summaryrefslogtreecommitdiffstats
path: root/svx/source
diff options
context:
space:
mode:
Diffstat (limited to 'svx/source')
-rw-r--r--svx/source/accessibility/AccessibleControlShape.cxx879
-rw-r--r--svx/source/accessibility/AccessibleEmptyEditSource.cxx329
-rw-r--r--svx/source/accessibility/AccessibleEmptyEditSource.hxx93
-rw-r--r--svx/source/accessibility/AccessibleFrameSelector.cxx380
-rw-r--r--svx/source/accessibility/AccessibleGraphicShape.cxx144
-rw-r--r--svx/source/accessibility/AccessibleOLEShape.cxx172
-rw-r--r--svx/source/accessibility/AccessibleShape.cxx1314
-rw-r--r--svx/source/accessibility/AccessibleShapeInfo.cxx63
-rw-r--r--svx/source/accessibility/AccessibleShapeTreeInfo.cxx117
-rw-r--r--svx/source/accessibility/AccessibleTextEventQueue.cxx92
-rw-r--r--svx/source/accessibility/AccessibleTextEventQueue.hxx90
-rw-r--r--svx/source/accessibility/AccessibleTextHelper.cxx1779
-rw-r--r--svx/source/accessibility/ChildrenManager.cxx115
-rw-r--r--svx/source/accessibility/ChildrenManagerImpl.cxx1051
-rw-r--r--svx/source/accessibility/ChildrenManagerImpl.hxx494
-rw-r--r--svx/source/accessibility/DescriptionGenerator.cxx187
-rw-r--r--svx/source/accessibility/GraphCtlAccessibleContext.cxx772
-rw-r--r--svx/source/accessibility/ShapeTypeHandler.cxx306
-rw-r--r--svx/source/accessibility/SvxShapeTypes.cxx165
-rw-r--r--svx/source/accessibility/charmapacc.cxx584
-rw-r--r--svx/source/accessibility/lookupcolorname.cxx121
-rw-r--r--svx/source/accessibility/lookupcolorname.hxx57
-rw-r--r--svx/source/accessibility/svxpixelctlaccessiblecontext.cxx468
-rw-r--r--svx/source/accessibility/svxrectctaccessiblecontext.cxx647
-rw-r--r--svx/source/core/extedit.cxx204
-rw-r--r--svx/source/core/graphichelper.cxx485
-rw-r--r--svx/source/customshapes/EnhancedCustomShape2d.cxx3053
-rw-r--r--svx/source/customshapes/EnhancedCustomShape3d.cxx1037
-rw-r--r--svx/source/customshapes/EnhancedCustomShape3d.hxx37
-rw-r--r--svx/source/customshapes/EnhancedCustomShapeEngine.cxx488
-rw-r--r--svx/source/customshapes/EnhancedCustomShapeFontWork.cxx956
-rw-r--r--svx/source/customshapes/EnhancedCustomShapeFontWork.hxx42
-rw-r--r--svx/source/customshapes/EnhancedCustomShapeFunctionParser.cxx1164
-rw-r--r--svx/source/customshapes/EnhancedCustomShapeGeometry.cxx8567
-rw-r--r--svx/source/customshapes/EnhancedCustomShapeHandle.cxx91
-rw-r--r--svx/source/customshapes/EnhancedCustomShapeHandle.hxx57
-rw-r--r--svx/source/customshapes/EnhancedCustomShapeTypeNames.cxx548
-rw-r--r--svx/source/diagram/IDiagramHelper.cxx430
-rw-r--r--svx/source/diagram/datamodel.cxx501
-rw-r--r--svx/source/dialog/AccessibilityCheckDialog.cxx59
-rw-r--r--svx/source/dialog/ClassificationCommon.cxx125
-rw-r--r--svx/source/dialog/ClassificationDialog.cxx693
-rw-r--r--svx/source/dialog/ClassificationEditView.cxx82
-rw-r--r--svx/source/dialog/ClassificationEditView.hxx54
-rw-r--r--svx/source/dialog/FileExportedDialog.cxx42
-rw-r--r--svx/source/dialog/GenericCheckDialog.cxx70
-rw-r--r--svx/source/dialog/SafeModeDialog.cxx307
-rw-r--r--svx/source/dialog/SafeModeDialog.hxx67
-rw-r--r--svx/source/dialog/SafeModeUI.cxx79
-rw-r--r--svx/source/dialog/SpellDialogChildWindow.cxx90
-rw-r--r--svx/source/dialog/SvxNumOptionsTabPageHelper.cxx88
-rw-r--r--svx/source/dialog/_bmpmask.cxx1057
-rw-r--r--svx/source/dialog/_contdlg.cxx675
-rw-r--r--svx/source/dialog/charmap.cxx1938
-rw-r--r--svx/source/dialog/compressgraphicdialog.cxx455
-rw-r--r--svx/source/dialog/connctrl.cxx310
-rw-r--r--svx/source/dialog/contimp.hxx132
-rw-r--r--svx/source/dialog/contwnd.cxx271
-rw-r--r--svx/source/dialog/contwnd.hxx70
-rw-r--r--svx/source/dialog/crashreportdlg.cxx111
-rw-r--r--svx/source/dialog/crashreportdlg.hxx39
-rw-r--r--svx/source/dialog/crashreportui.cxx81
-rw-r--r--svx/source/dialog/ctredlin.cxx1000
-rw-r--r--svx/source/dialog/databaseregistrationui.cxx45
-rw-r--r--svx/source/dialog/dialcontrol.cxx479
-rw-r--r--svx/source/dialog/dialmgr.cxx26
-rw-r--r--svx/source/dialog/dlgctl3d.cxx1173
-rw-r--r--svx/source/dialog/dlgctrl.cxx1479
-rw-r--r--svx/source/dialog/dlgunit.hxx55
-rw-r--r--svx/source/dialog/dlgutil.cxx74
-rw-r--r--svx/source/dialog/docrecovery.cxx1104
-rw-r--r--svx/source/dialog/fntctrl.cxx1089
-rw-r--r--svx/source/dialog/fontwork.cxx794
-rw-r--r--svx/source/dialog/framelink.cxx320
-rw-r--r--svx/source/dialog/framelinkarray.cxx1530
-rw-r--r--svx/source/dialog/frmdirlbox.cxx32
-rw-r--r--svx/source/dialog/frmsel.cxx1303
-rw-r--r--svx/source/dialog/graphctl.cxx850
-rw-r--r--svx/source/dialog/grfflt.cxx294
-rw-r--r--svx/source/dialog/hdft.cxx1052
-rw-r--r--svx/source/dialog/hexcolorcontrol.cxx111
-rw-r--r--svx/source/dialog/hyperdlg.cxx81
-rw-r--r--svx/source/dialog/imapdlg.cxx727
-rw-r--r--svx/source/dialog/imapimp.hxx51
-rw-r--r--svx/source/dialog/imapwnd.cxx743
-rw-r--r--svx/source/dialog/imapwnd.hxx141
-rw-r--r--svx/source/dialog/langbox.cxx502
-rw-r--r--svx/source/dialog/linkwarn.cxx61
-rw-r--r--svx/source/dialog/measctrl.cxx159
-rw-r--r--svx/source/dialog/optgrid.cxx281
-rw-r--r--svx/source/dialog/page.hrc104
-rw-r--r--svx/source/dialog/pagectrl.cxx396
-rw-r--r--svx/source/dialog/pagenumberlistbox.cxx52
-rw-r--r--svx/source/dialog/papersizelistbox.cxx74
-rw-r--r--svx/source/dialog/paraprev.cxx214
-rw-r--r--svx/source/dialog/passwd.cxx91
-rw-r--r--svx/source/dialog/relfld.cxx103
-rw-r--r--svx/source/dialog/rlrcitem.cxx148
-rw-r--r--svx/source/dialog/rlrcitem.hxx42
-rw-r--r--svx/source/dialog/rubydialog.cxx878
-rw-r--r--svx/source/dialog/rulritem.cxx732
-rw-r--r--svx/source/dialog/samecontentlistbox.cxx39
-rw-r--r--svx/source/dialog/searchcharmap.cxx441
-rw-r--r--svx/source/dialog/signaturelinehelper.cxx165
-rw-r--r--svx/source/dialog/spacinglistbox.cxx76
-rw-r--r--svx/source/dialog/srchctrl.cxx71
-rw-r--r--svx/source/dialog/srchctrl.hxx39
-rw-r--r--svx/source/dialog/srchdlg.cxx2466
-rw-r--r--svx/source/dialog/strarray.cxx88
-rw-r--r--svx/source/dialog/svxbmpnumvalueset.cxx533
-rw-r--r--svx/source/dialog/svxdlg.cxx29
-rw-r--r--svx/source/dialog/svxgraphicitem.cxx39
-rw-r--r--svx/source/dialog/svxruler.cxx3577
-rw-r--r--svx/source/dialog/swframeexample.cxx714
-rw-r--r--svx/source/dialog/swframeposstrings.cxx38
-rw-r--r--svx/source/dialog/txencbox.cxx267
-rw-r--r--svx/source/dialog/txenctab.cxx49
-rw-r--r--svx/source/dialog/weldeditview.cxx1682
-rw-r--r--svx/source/engine3d/camera3d.cxx180
-rw-r--r--svx/source/engine3d/cube3d.cxx153
-rw-r--r--svx/source/engine3d/deflt3d.cxx52
-rw-r--r--svx/source/engine3d/dragmt3d.cxx732
-rw-r--r--svx/source/engine3d/e3dsceneupdater.cxx109
-rw-r--r--svx/source/engine3d/e3dundo.cxx91
-rw-r--r--svx/source/engine3d/extrud3d.cxx219
-rw-r--r--svx/source/engine3d/float3d.cxx2944
-rw-r--r--svx/source/engine3d/helperhittest3d.cxx277
-rw-r--r--svx/source/engine3d/helperminimaldepth3d.cxx201
-rw-r--r--svx/source/engine3d/helperminimaldepth3d.hxx45
-rw-r--r--svx/source/engine3d/lathe3d.cxx201
-rw-r--r--svx/source/engine3d/obj3d.cxx617
-rw-r--r--svx/source/engine3d/objfac3d.cxx71
-rw-r--r--svx/source/engine3d/polygn3d.cxx239
-rw-r--r--svx/source/engine3d/scene3d.cxx898
-rw-r--r--svx/source/engine3d/sphere3d.cxx148
-rw-r--r--svx/source/engine3d/svx3ditems.cxx277
-rw-r--r--svx/source/engine3d/view3d.cxx1593
-rw-r--r--svx/source/engine3d/view3d1.cxx177
-rw-r--r--svx/source/engine3d/viewpt3d2.cxx153
-rw-r--r--svx/source/fmcomp/dbaexchange.cxx630
-rw-r--r--svx/source/fmcomp/dbaobjectex.cxx140
-rw-r--r--svx/source/fmcomp/fmgridcl.cxx2096
-rw-r--r--svx/source/fmcomp/fmgridif.cxx2808
-rw-r--r--svx/source/fmcomp/gridcell.cxx4615
-rw-r--r--svx/source/fmcomp/gridcols.cxx103
-rw-r--r--svx/source/fmcomp/gridctrl.cxx3396
-rw-r--r--svx/source/fmcomp/xmlexchg.cxx61
-rw-r--r--svx/source/form/ParseContext.cxx198
-rw-r--r--svx/source/form/dataaccessdescriptor.cxx356
-rw-r--r--svx/source/form/databaselocationinput.cxx246
-rw-r--r--svx/source/form/datalistener.cxx91
-rw-r--r--svx/source/form/datanavi.cxx3090
-rw-r--r--svx/source/form/dbcharsethelper.cxx48
-rw-r--r--svx/source/form/delayedevent.cxx48
-rw-r--r--svx/source/form/filtnav.cxx1850
-rw-r--r--svx/source/form/fmPropBrw.cxx581
-rw-r--r--svx/source/form/fmcontrolbordermanager.cxx425
-rw-r--r--svx/source/form/fmcontrollayout.cxx312
-rw-r--r--svx/source/form/fmdmod.cxx92
-rw-r--r--svx/source/form/fmdocumentclassification.cxx197
-rw-r--r--svx/source/form/fmdpage.cxx115
-rw-r--r--svx/source/form/fmexch.cxx347
-rw-r--r--svx/source/form/fmexpl.cxx527
-rw-r--r--svx/source/form/fmmodel.cxx209
-rw-r--r--svx/source/form/fmobj.cxx653
-rw-r--r--svx/source/form/fmobjfac.cxx231
-rw-r--r--svx/source/form/fmpage.cxx172
-rw-r--r--svx/source/form/fmpgeimp.cxx713
-rw-r--r--svx/source/form/fmscriptingenv.cxx928
-rw-r--r--svx/source/form/fmservs.cxx74
-rw-r--r--svx/source/form/fmshell.cxx1417
-rw-r--r--svx/source/form/fmshimp.cxx3960
-rw-r--r--svx/source/form/fmsrccfg.cxx286
-rw-r--r--svx/source/form/fmsrcimp.cxx1058
-rw-r--r--svx/source/form/fmtextcontroldialogs.cxx78
-rw-r--r--svx/source/form/fmtextcontrolfeature.cxx118
-rw-r--r--svx/source/form/fmtextcontrolshell.cxx1315
-rw-r--r--svx/source/form/fmtools.cxx371
-rw-r--r--svx/source/form/fmundo.cxx1263
-rw-r--r--svx/source/form/fmview.cxx599
-rw-r--r--svx/source/form/fmvwimp.cxx1918
-rw-r--r--svx/source/form/formcontrolfactory.cxx702
-rw-r--r--svx/source/form/formcontroller.cxx4168
-rw-r--r--svx/source/form/formcontrolling.cxx497
-rw-r--r--svx/source/form/formdispatchinterceptor.cxx176
-rw-r--r--svx/source/form/formfeaturedispatcher.cxx194
-rw-r--r--svx/source/form/formtoolbars.cxx93
-rw-r--r--svx/source/form/labelitemwindow.cxx45
-rw-r--r--svx/source/form/legacyformcontroller.cxx201
-rw-r--r--svx/source/form/navigatortree.cxx2044
-rw-r--r--svx/source/form/navigatortreemodel.cxx907
-rw-r--r--svx/source/form/sdbdatacolumn.cxx55
-rw-r--r--svx/source/form/sqlparserclient.cxx50
-rw-r--r--svx/source/form/tabwin.cxx304
-rw-r--r--svx/source/form/tbxform.cxx208
-rw-r--r--svx/source/form/typemap.cxx52
-rw-r--r--svx/source/form/xfm_addcondition.cxx157
-rw-r--r--svx/source/gallery2/GalleryControl.cxx47
-rw-r--r--svx/source/gallery2/codec.cxx148
-rw-r--r--svx/source/gallery2/codec.hxx40
-rw-r--r--svx/source/gallery2/galbrws1.cxx485
-rw-r--r--svx/source/gallery2/galbrws1.hxx91
-rw-r--r--svx/source/gallery2/galbrws2.cxx1242
-rw-r--r--svx/source/gallery2/galctrl.cxx408
-rw-r--r--svx/source/gallery2/galexpl.cxx299
-rw-r--r--svx/source/gallery2/galini.cxx95
-rw-r--r--svx/source/gallery2/gallery1.cxx724
-rw-r--r--svx/source/gallery2/gallerybinaryengine.cxx810
-rw-r--r--svx/source/gallery2/gallerybinaryengineentry.cxx180
-rw-r--r--svx/source/gallery2/gallerybinarystoragelocations.cxx75
-rw-r--r--svx/source/gallery2/gallerydrawmodel.hxx39
-rw-r--r--svx/source/gallery2/galleryfilestorage.cxx24
-rw-r--r--svx/source/gallery2/galleryfilestorageentry.cxx24
-rw-r--r--svx/source/gallery2/galleryobjectbinarystorage.cxx25
-rw-r--r--svx/source/gallery2/galleryobjectcollection.cxx60
-rw-r--r--svx/source/gallery2/galleryobjectstorage.cxx24
-rw-r--r--svx/source/gallery2/galleryobjectxmlstorage.cxx25
-rw-r--r--svx/source/gallery2/gallerystoragelocations.cxx24
-rw-r--r--svx/source/gallery2/galmisc.cxx563
-rw-r--r--svx/source/gallery2/galobj.cxx493
-rw-r--r--svx/source/gallery2/galtheme.cxx780
-rw-r--r--svx/source/gengal/gengal.cxx326
-rwxr-xr-xsvx/source/gengal/gengal.sh95
-rw-r--r--svx/source/inc/AccessibleFrameSelector.hxx137
-rw-r--r--svx/source/inc/DefaultShapesPanel.hxx82
-rw-r--r--svx/source/inc/GraphCtlAccessibleContext.hxx190
-rw-r--r--svx/source/inc/ShapesUtil.hxx39
-rw-r--r--svx/source/inc/StylesPreviewToolBoxControl.hxx70
-rw-r--r--svx/source/inc/StylesPreviewWindow.hxx146
-rw-r--r--svx/source/inc/cell.hxx220
-rw-r--r--svx/source/inc/celltypes.hxx95
-rw-r--r--svx/source/inc/charmapacc.hxx222
-rw-r--r--svx/source/inc/clonelist.hxx47
-rw-r--r--svx/source/inc/datalistener.hxx72
-rw-r--r--svx/source/inc/datanavi.hxx580
-rw-r--r--svx/source/inc/delayedevent.hxx81
-rw-r--r--svx/source/inc/docrecovery.hxx503
-rw-r--r--svx/source/inc/filtnav.hxx339
-rw-r--r--svx/source/inc/findtextfield.hxx70
-rw-r--r--svx/source/inc/fmPropBrw.hxx104
-rw-r--r--svx/source/inc/fmcontrolbordermanager.hxx230
-rw-r--r--svx/source/inc/fmcontrollayout.hxx62
-rw-r--r--svx/source/inc/fmdocumentclassification.hxx71
-rw-r--r--svx/source/inc/fmexch.hxx243
-rw-r--r--svx/source/inc/fmexpl.hxx551
-rw-r--r--svx/source/inc/fmobj.hxx116
-rw-r--r--svx/source/inc/fmpgeimp.hxx144
-rw-r--r--svx/source/inc/fmprop.hxx153
-rw-r--r--svx/source/inc/fmscriptingenv.hxx97
-rw-r--r--svx/source/inc/fmservs.hxx110
-rw-r--r--svx/source/inc/fmshimp.hxx562
-rw-r--r--svx/source/inc/fmslotinvalidator.hxx32
-rw-r--r--svx/source/inc/fmtextcontroldialogs.hxx50
-rw-r--r--svx/source/inc/fmtextcontrolfeature.hxx92
-rw-r--r--svx/source/inc/fmtextcontrolshell.hxx201
-rw-r--r--svx/source/inc/fmundo.hxx199
-rw-r--r--svx/source/inc/fmurl.hxx55
-rw-r--r--svx/source/inc/fmvwimp.hxx302
-rw-r--r--svx/source/inc/formcontrolfactory.hxx107
-rw-r--r--svx/source/inc/formcontroller.hxx570
-rw-r--r--svx/source/inc/formcontrolling.hxx213
-rw-r--r--svx/source/inc/formdispatchinterceptor.hxx107
-rw-r--r--svx/source/inc/formfeaturedispatcher.hxx111
-rw-r--r--svx/source/inc/formtoolbars.hxx62
-rw-r--r--svx/source/inc/frmselimpl.hxx285
-rw-r--r--svx/source/inc/gridcell.hxx1067
-rw-r--r--svx/source/inc/gridcols.hxx54
-rw-r--r--svx/source/inc/sdbdatacolumn.hxx59
-rw-r--r--svx/source/inc/sqlparserclient.hxx70
-rw-r--r--svx/source/inc/svdobjplusdata.hxx46
-rw-r--r--svx/source/inc/svdobjuserdatalist.hxx37
-rw-r--r--svx/source/inc/svdoutlinercache.hxx51
-rw-r--r--svx/source/inc/svdpdf.hxx142
-rw-r--r--svx/source/inc/svxpixelctlaccessiblecontext.hxx154
-rw-r--r--svx/source/inc/svxrectctaccessiblecontext.hxx235
-rw-r--r--svx/source/inc/tablemodel.hxx209
-rw-r--r--svx/source/inc/tabwin.hxx92
-rw-r--r--svx/source/inc/tbxform.hxx105
-rw-r--r--svx/source/inc/treevisitor.hxx104
-rw-r--r--svx/source/inc/xfm_addcondition.hxx76
-rw-r--r--svx/source/inc/xmlxtexp.hxx63
-rw-r--r--svx/source/inc/xmlxtimp.hxx55
-rw-r--r--svx/source/items/SmartTagItem.cxx96
-rw-r--r--svx/source/items/algitem.cxx315
-rw-r--r--svx/source/items/autoformathelper.cxx393
-rw-r--r--svx/source/items/chrtitem.cxx178
-rw-r--r--svx/source/items/clipfmtitem.cxx151
-rw-r--r--svx/source/items/customshapeitem.cxx347
-rw-r--r--svx/source/items/drawitem.cxx349
-rw-r--r--svx/source/items/e3ditem.cxx100
-rw-r--r--svx/source/items/galleryitem.cxx140
-rw-r--r--svx/source/items/grfitem.cxx121
-rw-r--r--svx/source/items/hlnkitem.cxx184
-rw-r--r--svx/source/items/legacyitem.cxx105
-rw-r--r--svx/source/items/numfmtsh.cxx1593
-rw-r--r--svx/source/items/numinf.cxx131
-rw-r--r--svx/source/items/ofaitem.cxx56
-rw-r--r--svx/source/items/pageitem.cxx289
-rw-r--r--svx/source/items/postattr.cxx154
-rw-r--r--svx/source/items/rotmodit.cxx123
-rw-r--r--svx/source/items/svxerr.cxx36
-rw-r--r--svx/source/items/viewlayoutitem.cxx170
-rw-r--r--svx/source/items/zoomslideritem.cxx219
-rw-r--r--svx/source/mnuctrls/clipboardctl.cxx119
-rw-r--r--svx/source/mnuctrls/smarttagmenu.cxx248
-rw-r--r--svx/source/sdr/animation/animationstate.cxx130
-rw-r--r--svx/source/sdr/animation/objectanimator.cxx35
-rw-r--r--svx/source/sdr/animation/scheduler.cxx173
-rw-r--r--svx/source/sdr/attribute/sdrallfillattributeshelper.cxx244
-rw-r--r--svx/source/sdr/attribute/sdreffectstextattribute.cxx76
-rw-r--r--svx/source/sdr/attribute/sdrfilltextattribute.cxx65
-rw-r--r--svx/source/sdr/attribute/sdrformtextattribute.cxx372
-rw-r--r--svx/source/sdr/attribute/sdrformtextoutlineattribute.cxx141
-rw-r--r--svx/source/sdr/attribute/sdrlineeffectstextattribute.cxx75
-rw-r--r--svx/source/sdr/attribute/sdrlinefilleffectstextattribute.cxx77
-rw-r--r--svx/source/sdr/attribute/sdrtextattribute.cxx422
-rw-r--r--svx/source/sdr/contact/displayinfo.cxx87
-rw-r--r--svx/source/sdr/contact/objectcontact.cxx217
-rw-r--r--svx/source/sdr/contact/objectcontactofobjlistpainter.cxx198
-rw-r--r--svx/source/sdr/contact/objectcontactofpageview.cxx451
-rw-r--r--svx/source/sdr/contact/sdrmediawindow.cxx174
-rw-r--r--svx/source/sdr/contact/sdrmediawindow.hxx60
-rw-r--r--svx/source/sdr/contact/viewcontact.cxx295
-rw-r--r--svx/source/sdr/contact/viewcontactofe3d.cxx195
-rw-r--r--svx/source/sdr/contact/viewcontactofe3dcube.cxx88
-rw-r--r--svx/source/sdr/contact/viewcontactofe3dextrude.cxx83
-rw-r--r--svx/source/sdr/contact/viewcontactofe3dlathe.cxx96
-rw-r--r--svx/source/sdr/contact/viewcontactofe3dpolygon.cxx169
-rw-r--r--svx/source/sdr/contact/viewcontactofe3dscene.cxx446
-rw-r--r--svx/source/sdr/contact/viewcontactofe3dsphere.cxx80
-rw-r--r--svx/source/sdr/contact/viewcontactofgraphic.cxx391
-rw-r--r--svx/source/sdr/contact/viewcontactofgroup.cxx78
-rw-r--r--svx/source/sdr/contact/viewcontactofmasterpagedescriptor.cxx103
-rw-r--r--svx/source/sdr/contact/viewcontactofpageobj.cxx79
-rw-r--r--svx/source/sdr/contact/viewcontactofsdrcaptionobj.cxx190
-rw-r--r--svx/source/sdr/contact/viewcontactofsdrcircobj.cxx102
-rw-r--r--svx/source/sdr/contact/viewcontactofsdredgeobj.cxx66
-rw-r--r--svx/source/sdr/contact/viewcontactofsdrmeasureobj.cxx127
-rw-r--r--svx/source/sdr/contact/viewcontactofsdrmediaobj.cxx129
-rw-r--r--svx/source/sdr/contact/viewcontactofsdrobj.cxx179
-rw-r--r--svx/source/sdr/contact/viewcontactofsdrobjcustomshape.cxx238
-rw-r--r--svx/source/sdr/contact/viewcontactofsdrole2obj.cxx179
-rw-r--r--svx/source/sdr/contact/viewcontactofsdrpage.cxx598
-rw-r--r--svx/source/sdr/contact/viewcontactofsdrpathobj.cxx210
-rw-r--r--svx/source/sdr/contact/viewcontactofsdrrectobj.cxx88
-rw-r--r--svx/source/sdr/contact/viewcontactoftextobj.cxx33
-rw-r--r--svx/source/sdr/contact/viewcontactofunocontrol.cxx133
-rw-r--r--svx/source/sdr/contact/viewcontactofvirtobj.cxx100
-rw-r--r--svx/source/sdr/contact/viewobjectcontact.cxx570
-rw-r--r--svx/source/sdr/contact/viewobjectcontactofe3d.cxx72
-rw-r--r--svx/source/sdr/contact/viewobjectcontactofe3dscene.cxx137
-rw-r--r--svx/source/sdr/contact/viewobjectcontactofgraphic.cxx57
-rw-r--r--svx/source/sdr/contact/viewobjectcontactofgroup.cxx87
-rw-r--r--svx/source/sdr/contact/viewobjectcontactofmasterpagedescriptor.cxx132
-rw-r--r--svx/source/sdr/contact/viewobjectcontactofpageobj.cxx315
-rw-r--r--svx/source/sdr/contact/viewobjectcontactofsdrmediaobj.cxx175
-rw-r--r--svx/source/sdr/contact/viewobjectcontactofsdrobj.cxx193
-rw-r--r--svx/source/sdr/contact/viewobjectcontactofsdrole2obj.cxx147
-rw-r--r--svx/source/sdr/contact/viewobjectcontactofsdrpage.cxx579
-rw-r--r--svx/source/sdr/contact/viewobjectcontactofunocontrol.cxx1777
-rw-r--r--svx/source/sdr/contact/viewobjectcontactredirector.cxx39
-rw-r--r--svx/source/sdr/misc/ImageMapInfo.cxx121
-rw-r--r--svx/source/sdr/overlay/overlayanimatedbitmapex.cxx113
-rw-r--r--svx/source/sdr/overlay/overlaybitmapex.cxx72
-rw-r--r--svx/source/sdr/overlay/overlaycrosshair.cxx67
-rw-r--r--svx/source/sdr/overlay/overlayhandle.cxx55
-rw-r--r--svx/source/sdr/overlay/overlayhelpline.cxx74
-rw-r--r--svx/source/sdr/overlay/overlayline.cxx74
-rw-r--r--svx/source/sdr/overlay/overlaymanager.cxx346
-rw-r--r--svx/source/sdr/overlay/overlaymanagerbuffered.cxx441
-rw-r--r--svx/source/sdr/overlay/overlayobject.cxx228
-rw-r--r--svx/source/sdr/overlay/overlayobjectcell.cxx81
-rw-r--r--svx/source/sdr/overlay/overlayobjectlist.cxx144
-rw-r--r--svx/source/sdr/overlay/overlaypolypolygon.cxx128
-rw-r--r--svx/source/sdr/overlay/overlayprimitive2dsequenceobject.cxx42
-rw-r--r--svx/source/sdr/overlay/overlayrectangle.cxx113
-rw-r--r--svx/source/sdr/overlay/overlayrollingrectangle.cxx117
-rw-r--r--svx/source/sdr/overlay/overlayselection.cxx218
-rw-r--r--svx/source/sdr/overlay/overlaytools.cxx601
-rw-r--r--svx/source/sdr/overlay/overlaytriangle.cxx61
-rw-r--r--svx/source/sdr/primitive2d/primitivefactory2d.cxx98
-rw-r--r--svx/source/sdr/primitive2d/sdrattributecreator.cxx1151
-rw-r--r--svx/source/sdr/primitive2d/sdrcaptionprimitive2d.cxx159
-rw-r--r--svx/source/sdr/primitive2d/sdrconnectorprimitive2d.cxx107
-rw-r--r--svx/source/sdr/primitive2d/sdrcustomshapeprimitive2d.cxx130
-rw-r--r--svx/source/sdr/primitive2d/sdrdecompositiontools.cxx884
-rw-r--r--svx/source/sdr/primitive2d/sdrellipseprimitive2d.cxx266
-rw-r--r--svx/source/sdr/primitive2d/sdrframeborderprimitive2d.cxx928
-rw-r--r--svx/source/sdr/primitive2d/sdrgrafprimitive2d.cxx167
-rw-r--r--svx/source/sdr/primitive2d/sdrmeasureprimitive2d.cxx498
-rw-r--r--svx/source/sdr/primitive2d/sdrole2primitive2d.cxx176
-rw-r--r--svx/source/sdr/primitive2d/sdrolecontentprimitive2d.cxx179
-rw-r--r--svx/source/sdr/primitive2d/sdrpathprimitive2d.cxx156
-rw-r--r--svx/source/sdr/primitive2d/sdrprimitivetools.cxx64
-rw-r--r--svx/source/sdr/primitive2d/sdrrectangleprimitive2d.cxx152
-rw-r--r--svx/source/sdr/primitive2d/sdrtextprimitive2d.cxx545
-rw-r--r--svx/source/sdr/primitive3d/sdrattributecreator3d.cxx145
-rw-r--r--svx/source/sdr/properties/attributeproperties.cxx541
-rw-r--r--svx/source/sdr/properties/captionproperties.cxx101
-rw-r--r--svx/source/sdr/properties/circleproperties.cxx125
-rw-r--r--svx/source/sdr/properties/connectorproperties.cxx89
-rw-r--r--svx/source/sdr/properties/customshapeproperties.cxx230
-rw-r--r--svx/source/sdr/properties/defaultproperties.cxx251
-rw-r--r--svx/source/sdr/properties/e3dcompoundproperties.cxx144
-rw-r--r--svx/source/sdr/properties/e3dextrudeproperties.cxx74
-rw-r--r--svx/source/sdr/properties/e3dlatheproperties.cxx84
-rw-r--r--svx/source/sdr/properties/e3dproperties.cxx75
-rw-r--r--svx/source/sdr/properties/e3dsceneproperties.cxx296
-rw-r--r--svx/source/sdr/properties/e3dsphereproperties.cxx69
-rw-r--r--svx/source/sdr/properties/emptyproperties.cxx89
-rw-r--r--svx/source/sdr/properties/graphicproperties.cxx150
-rw-r--r--svx/source/sdr/properties/groupproperties.cxx240
-rw-r--r--svx/source/sdr/properties/itemsettools.cxx87
-rw-r--r--svx/source/sdr/properties/measureproperties.cxx130
-rw-r--r--svx/source/sdr/properties/oleproperties.cxx73
-rw-r--r--svx/source/sdr/properties/pageproperties.cxx106
-rw-r--r--svx/source/sdr/properties/properties.cxx178
-rw-r--r--svx/source/sdr/properties/rectangleproperties.cxx69
-rw-r--r--svx/source/sdr/properties/textproperties.cxx631
-rw-r--r--svx/source/sidebar/ContextChangeEventMultiplexer.cxx90
-rw-r--r--svx/source/sidebar/EmptyPanel.cxx34
-rw-r--r--svx/source/sidebar/EmptyPanel.hxx43
-rw-r--r--svx/source/sidebar/PanelFactory.cxx220
-rw-r--r--svx/source/sidebar/SelectionAnalyzer.cxx479
-rw-r--r--svx/source/sidebar/SelectionChangeHandler.cxx100
-rw-r--r--svx/source/sidebar/area/AreaPropertyPanel.cxx158
-rw-r--r--svx/source/sidebar/area/AreaPropertyPanel.hxx94
-rw-r--r--svx/source/sidebar/area/AreaPropertyPanelBase.cxx1371
-rw-r--r--svx/source/sidebar/area/AreaTransparencyGradientPopup.cxx179
-rw-r--r--svx/source/sidebar/effect/EffectPropertyPanel.cxx178
-rw-r--r--svx/source/sidebar/effect/EffectPropertyPanel.hxx63
-rw-r--r--svx/source/sidebar/fontwork/FontworkPropertyPanel.cxx59
-rw-r--r--svx/source/sidebar/fontwork/FontworkPropertyPanel.hxx48
-rw-r--r--svx/source/sidebar/graphic/GraphicPropertyPanel.cxx246
-rw-r--r--svx/source/sidebar/graphic/GraphicPropertyPanel.hxx89
-rw-r--r--svx/source/sidebar/inspector/InspectorTextPanel.cxx173
-rw-r--r--svx/source/sidebar/line/LinePropertyPanel.cxx165
-rw-r--r--svx/source/sidebar/line/LinePropertyPanel.hxx96
-rw-r--r--svx/source/sidebar/line/LinePropertyPanelBase.cxx470
-rw-r--r--svx/source/sidebar/line/LineWidthPopup.cxx223
-rw-r--r--svx/source/sidebar/line/LineWidthValueSet.cxx172
-rw-r--r--svx/source/sidebar/line/LineWidthValueSet.hxx55
-rw-r--r--svx/source/sidebar/lists/ListsPropertyPanel.cxx61
-rw-r--r--svx/source/sidebar/lists/ListsPropertyPanel.hxx50
-rw-r--r--svx/source/sidebar/media/MediaPlaybackPanel.cxx175
-rw-r--r--svx/source/sidebar/media/MediaPlaybackPanel.hxx80
-rw-r--r--svx/source/sidebar/nbdtmg.cxx905
-rw-r--r--svx/source/sidebar/nbdtmgfact.cxx42
-rw-r--r--svx/source/sidebar/paragraph/ParaLineSpacingControl.cxx439
-rw-r--r--svx/source/sidebar/paragraph/ParaLineSpacingControl.hxx80
-rw-r--r--svx/source/sidebar/paragraph/ParaLineSpacingPopup.cxx99
-rw-r--r--svx/source/sidebar/paragraph/ParaPropertyPanel.cxx491
-rw-r--r--svx/source/sidebar/paragraph/ParaPropertyPanel.hxx135
-rw-r--r--svx/source/sidebar/paragraph/ParaSpacingControl.cxx257
-rw-r--r--svx/source/sidebar/paragraph/ParaSpacingWindow.cxx341
-rw-r--r--svx/source/sidebar/paragraph/ParaSpacingWindow.hxx110
-rw-r--r--svx/source/sidebar/possize/PosSizePropertyPanel.cxx1079
-rw-r--r--svx/source/sidebar/possize/PosSizePropertyPanel.hxx193
-rw-r--r--svx/source/sidebar/shadow/ShadowPropertyPanel.cxx362
-rw-r--r--svx/source/sidebar/shadow/ShadowPropertyPanel.hxx89
-rw-r--r--svx/source/sidebar/shapes/DefaultShapesPanel.cxx155
-rw-r--r--svx/source/sidebar/shapes/ShapesUtil.cxx212
-rw-r--r--svx/source/sidebar/styles/StylesPropertyPanel.cxx50
-rw-r--r--svx/source/sidebar/styles/StylesPropertyPanel.hxx38
-rw-r--r--svx/source/sidebar/text/TextCharacterSpacingControl.cxx224
-rw-r--r--svx/source/sidebar/text/TextCharacterSpacingControl.hxx69
-rw-r--r--svx/source/sidebar/text/TextCharacterSpacingPopup.cxx82
-rw-r--r--svx/source/sidebar/text/TextPropertyPanel.cxx155
-rw-r--r--svx/source/sidebar/text/TextPropertyPanel.hxx78
-rw-r--r--svx/source/sidebar/text/TextUnderlineControl.cxx141
-rw-r--r--svx/source/sidebar/text/TextUnderlineControl.hxx60
-rw-r--r--svx/source/sidebar/text/TextUnderlinePopup.cxx82
-rw-r--r--svx/source/sidebar/textcolumns/TextColumnsPropertyPanel.cxx118
-rw-r--r--svx/source/sidebar/textcolumns/TextColumnsPropertyPanel.hxx48
-rw-r--r--svx/source/sidebar/tools/ValueSetWithTextControl.cxx121
-rw-r--r--svx/source/smarttags/SmartTagMgr.cxx527
-rw-r--r--svx/source/stbctrls/insctrl.cxx83
-rw-r--r--svx/source/stbctrls/modctrl.cxx159
-rw-r--r--svx/source/stbctrls/modctrl_internal.hxx30
-rw-r--r--svx/source/stbctrls/pszctrl.cxx486
-rw-r--r--svx/source/stbctrls/selctrl.cxx195
-rw-r--r--svx/source/stbctrls/stbctrls.h35
-rw-r--r--svx/source/stbctrls/xmlsecctrl.cxx162
-rw-r--r--svx/source/stbctrls/zoomctrl.cxx229
-rw-r--r--svx/source/stbctrls/zoomsliderctrl.cxx392
-rw-r--r--svx/source/styles/ColorSets.cxx375
-rw-r--r--svx/source/styles/CommonStyleManager.cxx26
-rw-r--r--svx/source/styles/CommonStylePreviewRenderer.cxx233
-rw-r--r--svx/source/svdraw/ActionDescriptionProvider.cxx66
-rw-r--r--svx/source/svdraw/MediaShellHelpers.cxx108
-rw-r--r--svx/source/svdraw/charthelper.cxx135
-rw-r--r--svx/source/svdraw/clonelist.cxx127
-rw-r--r--svx/source/svdraw/gradtrns.cxx527
-rw-r--r--svx/source/svdraw/gradtrns.hxx55
-rw-r--r--svx/source/svdraw/polypolygoneditor.cxx182
-rw-r--r--svx/source/svdraw/presetooxhandleadjustmentrelations.cxx343
-rw-r--r--svx/source/svdraw/presetooxhandleadjustmentrelations.hxx34
-rw-r--r--svx/source/svdraw/sdrhittesthelper.cxx176
-rw-r--r--svx/source/svdraw/sdrmasterpagedescriptor.cxx104
-rw-r--r--svx/source/svdraw/sdrpagewindow.cxx531
-rw-r--r--svx/source/svdraw/sdrpaintwindow.cxx346
-rw-r--r--svx/source/svdraw/sdrundomanager.cxx184
-rw-r--r--svx/source/svdraw/selectioncontroller.cxx124
-rw-r--r--svx/source/svdraw/svdattr.cxx2042
-rw-r--r--svx/source/svdraw/svdcrtv.cxx916
-rw-r--r--svx/source/svdraw/svddrag.cxx129
-rw-r--r--svx/source/svdraw/svddrgm1.hxx232
-rw-r--r--svx/source/svdraw/svddrgmt.cxx3860
-rw-r--r--svx/source/svdraw/svddrgv.cxx919
-rw-r--r--svx/source/svdraw/svdedtv.cxx1103
-rw-r--r--svx/source/svdraw/svdedtv1.cxx2019
-rw-r--r--svx/source/svdraw/svdedtv2.cxx2250
-rw-r--r--svx/source/svdraw/svdedxv.cxx2871
-rw-r--r--svx/source/svdraw/svdetc.cxx726
-rw-r--r--svx/source/svdraw/svdfmtf.cxx1641
-rw-r--r--svx/source/svdraw/svdfmtf.hxx173
-rw-r--r--svx/source/svdraw/svdglev.cxx400
-rw-r--r--svx/source/svdraw/svdglue.cxx394
-rw-r--r--svx/source/svdraw/svdhdl.cxx2675
-rw-r--r--svx/source/svdraw/svdhlpln.cxx109
-rw-r--r--svx/source/svdraw/svditer.cxx140
-rw-r--r--svx/source/svdraw/svdlayer.cxx361
-rw-r--r--svx/source/svdraw/svdmark.cxx791
-rw-r--r--svx/source/svdraw/svdmodel.cxx1941
-rw-r--r--svx/source/svdraw/svdmrkv.cxx2733
-rw-r--r--svx/source/svdraw/svdmrkv1.cxx547
-rw-r--r--svx/source/svdraw/svdoashp.cxx3245
-rw-r--r--svx/source/svdraw/svdoattr.cxx103
-rw-r--r--svx/source/svdraw/svdobj.cxx3375
-rw-r--r--svx/source/svdraw/svdobjplusdata.cxx62
-rw-r--r--svx/source/svdraw/svdobjuserdatalist.cxx36
-rw-r--r--svx/source/svdraw/svdocapt.cxx756
-rw-r--r--svx/source/svdraw/svdocirc.cxx1154
-rw-r--r--svx/source/svdraw/svdoedge.cxx2647
-rw-r--r--svx/source/svdraw/svdograf.cxx1274
-rw-r--r--svx/source/svdraw/svdogrp.cxx835
-rw-r--r--svx/source/svdraw/svdomeas.cxx1429
-rw-r--r--svx/source/svdraw/svdomedia.cxx457
-rw-r--r--svx/source/svdraw/svdoole2.cxx1990
-rw-r--r--svx/source/svdraw/svdopage.cxx175
-rw-r--r--svx/source/svdraw/svdopath.cxx2994
-rw-r--r--svx/source/svdraw/svdorect.cxx567
-rw-r--r--svx/source/svdraw/svdotext.cxx2174
-rw-r--r--svx/source/svdraw/svdotextdecomposition.cxx1698
-rw-r--r--svx/source/svdraw/svdotextpathdecomposition.cxx748
-rw-r--r--svx/source/svdraw/svdotxat.cxx457
-rw-r--r--svx/source/svdraw/svdotxdr.cxx248
-rw-r--r--svx/source/svdraw/svdotxed.cxx360
-rw-r--r--svx/source/svdraw/svdotxfl.cxx28
-rw-r--r--svx/source/svdraw/svdotxln.cxx277
-rw-r--r--svx/source/svdraw/svdotxtr.cxx496
-rw-r--r--svx/source/svdraw/svdouno.cxx502
-rw-r--r--svx/source/svdraw/svdoutl.cxx107
-rw-r--r--svx/source/svdraw/svdoutlinercache.cxx95
-rw-r--r--svx/source/svdraw/svdovirt.cxx567
-rw-r--r--svx/source/svdraw/svdpage.cxx1895
-rw-r--r--svx/source/svdraw/svdpagv.cxx894
-rw-r--r--svx/source/svdraw/svdpdf.cxx1052
-rw-r--r--svx/source/svdraw/svdpntv.cxx1206
-rw-r--r--svx/source/svdraw/svdpoev.cxx652
-rw-r--r--svx/source/svdraw/svdsnpv.cxx633
-rw-r--r--svx/source/svdraw/svdtext.cxx154
-rw-r--r--svx/source/svdraw/svdtrans.cxx867
-rw-r--r--svx/source/svdraw/svdundo.cxx1834
-rw-r--r--svx/source/svdraw/svdview.cxx1544
-rw-r--r--svx/source/svdraw/svdviter.cxx164
-rw-r--r--svx/source/svdraw/svdxcgv.cxx784
-rw-r--r--svx/source/svdraw/textchain.cxx128
-rw-r--r--svx/source/svdraw/textchaincursor.cxx203
-rw-r--r--svx/source/svdraw/textchainflow.cxx314
-rw-r--r--svx/source/table/accessiblecell.cxx600
-rw-r--r--svx/source/table/accessiblecell.hxx132
-rw-r--r--svx/source/table/accessibletableshape.cxx1324
-rw-r--r--svx/source/table/cell.cxx1712
-rw-r--r--svx/source/table/cellcursor.cxx546
-rw-r--r--svx/source/table/cellcursor.hxx71
-rw-r--r--svx/source/table/cellrange.cxx116
-rw-r--r--svx/source/table/cellrange.hxx61
-rw-r--r--svx/source/table/propertyset.cxx206
-rw-r--r--svx/source/table/propertyset.hxx96
-rw-r--r--svx/source/table/svdotable.cxx2534
-rw-r--r--svx/source/table/tablecolumn.cxx289
-rw-r--r--svx/source/table/tablecolumn.hxx83
-rw-r--r--svx/source/table/tablecolumns.cxx121
-rw-r--r--svx/source/table/tablecolumns.hxx61
-rw-r--r--svx/source/table/tablecontroller.cxx3363
-rw-r--r--svx/source/table/tabledesign.cxx705
-rw-r--r--svx/source/table/tablehandles.cxx313
-rw-r--r--svx/source/table/tablehandles.hxx89
-rw-r--r--svx/source/table/tablelayouter.cxx1311
-rw-r--r--svx/source/table/tablelayouter.hxx172
-rw-r--r--svx/source/table/tablemodel.cxx1127
-rw-r--r--svx/source/table/tablerow.cxx351
-rw-r--r--svx/source/table/tablerow.hxx84
-rw-r--r--svx/source/table/tablerows.cxx114
-rw-r--r--svx/source/table/tablerows.hxx59
-rw-r--r--svx/source/table/tablertfexporter.cxx235
-rw-r--r--svx/source/table/tablertfimporter.cxx503
-rw-r--r--svx/source/table/tableundo.cxx516
-rw-r--r--svx/source/table/tableundo.hxx258
-rw-r--r--svx/source/table/viewcontactoftableobj.cxx547
-rw-r--r--svx/source/table/viewcontactoftableobj.hxx45
-rw-r--r--svx/source/tbxctrls/Palette.cxx377
-rw-r--r--svx/source/tbxctrls/PaletteManager.cxx442
-rw-r--r--svx/source/tbxctrls/StylesPreviewToolBoxControl.cxx189
-rw-r--r--svx/source/tbxctrls/StylesPreviewWindow.cxx520
-rw-r--r--svx/source/tbxctrls/SvxColorChildWindow.cxx43
-rw-r--r--svx/source/tbxctrls/SvxColorValueSet.cxx166
-rw-r--r--svx/source/tbxctrls/SvxPresetListBox.cxx112
-rw-r--r--svx/source/tbxctrls/bulletsnumbering.cxx242
-rw-r--r--svx/source/tbxctrls/colrctrl.cxx423
-rw-r--r--svx/source/tbxctrls/extrusioncontrols.cxx968
-rw-r--r--svx/source/tbxctrls/extrusioncontrols.hxx224
-rw-r--r--svx/source/tbxctrls/fillctrl.cxx1087
-rw-r--r--svx/source/tbxctrls/fontworkgallery.cxx799
-rw-r--r--svx/source/tbxctrls/formatpaintbrushctrl.cxx105
-rw-r--r--svx/source/tbxctrls/grafctrl.cxx961
-rw-r--r--svx/source/tbxctrls/itemwin.cxx347
-rw-r--r--svx/source/tbxctrls/layctrl.cxx798
-rw-r--r--svx/source/tbxctrls/lboxctrl.cxx347
-rw-r--r--svx/source/tbxctrls/linectrl.cxx650
-rw-r--r--svx/source/tbxctrls/linemetricbox.hxx56
-rw-r--r--svx/source/tbxctrls/linewidthctrl.cxx101
-rw-r--r--svx/source/tbxctrls/tbcontrl.cxx4186
-rw-r--r--svx/source/tbxctrls/tbunocontroller.cxx538
-rw-r--r--svx/source/tbxctrls/tbunosearchcontrollers.cxx1662
-rw-r--r--svx/source/tbxctrls/tbxcolor.cxx95
-rw-r--r--svx/source/tbxctrls/tbxcolorupdate.cxx355
-rw-r--r--svx/source/tbxctrls/tbxdrctl.cxx99
-rw-r--r--svx/source/tbxctrls/verttexttbxctrl.cxx162
-rw-r--r--svx/source/toolbars/extrusionbar.cxx1338
-rw-r--r--svx/source/toolbars/fontworkbar.cxx566
-rw-r--r--svx/source/uitest/sdrobject.cxx206
-rw-r--r--svx/source/uitest/uiobject.cxx104
-rw-r--r--svx/source/unodialogs/textconversiondlgs/chinese_dictionarydialog.cxx676
-rw-r--r--svx/source/unodialogs/textconversiondlgs/chinese_dictionarydialog.hxx168
-rw-r--r--svx/source/unodialogs/textconversiondlgs/chinese_translation_unodialog.cxx227
-rw-r--r--svx/source/unodialogs/textconversiondlgs/chinese_translation_unodialog.hxx119
-rw-r--r--svx/source/unodialogs/textconversiondlgs/chinese_translationdialog.cxx97
-rw-r--r--svx/source/unodialogs/textconversiondlgs/chinese_translationdialog.hxx51
-rw-r--r--svx/source/unodraw/SvxXTextColumns.cxx338
-rw-r--r--svx/source/unodraw/UnoGraphicExporter.cxx1318
-rw-r--r--svx/source/unodraw/UnoGraphicExporter.hxx33
-rw-r--r--svx/source/unodraw/UnoNameItemTable.cxx282
-rw-r--r--svx/source/unodraw/UnoNameItemTable.hxx94
-rw-r--r--svx/source/unodraw/UnoNamespaceMap.cxx275
-rw-r--r--svx/source/unodraw/XPropertyTable.cxx663
-rw-r--r--svx/source/unodraw/gluepts.cxx513
-rw-r--r--svx/source/unodraw/gluepts.hxx37
-rw-r--r--svx/source/unodraw/recoveryui.cxx331
-rw-r--r--svx/source/unodraw/shapeimpl.hxx96
-rw-r--r--svx/source/unodraw/shapepropertynotifier.cxx143
-rw-r--r--svx/source/unodraw/tableshape.cxx178
-rw-r--r--svx/source/unodraw/unobrushitemhelper.cxx342
-rw-r--r--svx/source/unodraw/unobtabl.cxx102
-rw-r--r--svx/source/unodraw/unoctabl.cxx181
-rw-r--r--svx/source/unodraw/unodtabl.cxx83
-rw-r--r--svx/source/unodraw/unogtabl.cxx79
-rw-r--r--svx/source/unodraw/unohtabl.cxx86
-rw-r--r--svx/source/unodraw/unomlstr.cxx60
-rw-r--r--svx/source/unodraw/unomod.cxx670
-rw-r--r--svx/source/unodraw/unomtabl.cxx423
-rw-r--r--svx/source/unodraw/unopage.cxx898
-rw-r--r--svx/source/unodraw/unopool.cxx375
-rw-r--r--svx/source/unodraw/unoprov.cxx2107
-rw-r--r--svx/source/unodraw/unoshap2.cxx1798
-rw-r--r--svx/source/unodraw/unoshap3.cxx1052
-rw-r--r--svx/source/unodraw/unoshap4.cxx1115
-rw-r--r--svx/source/unodraw/unoshape.cxx4042
-rw-r--r--svx/source/unodraw/unoshcol.cxx252
-rw-r--r--svx/source/unodraw/unoshtxt.cxx1009
-rw-r--r--svx/source/unodraw/unottabl.cxx85
-rw-r--r--svx/source/unogallery/unogalitem.cxx368
-rw-r--r--svx/source/unogallery/unogalitem.hxx104
-rw-r--r--svx/source/unogallery/unogaltheme.cxx362
-rw-r--r--svx/source/unogallery/unogaltheme.hxx100
-rw-r--r--svx/source/unogallery/unogalthemeprovider.cxx247
-rw-r--r--svx/source/xml/xmleohlp.cxx717
-rw-r--r--svx/source/xml/xmlexport.cxx239
-rw-r--r--svx/source/xml/xmlgrhlp.cxx1190
-rw-r--r--svx/source/xml/xmlxtexp.cxx485
-rw-r--r--svx/source/xml/xmlxtimp.cxx461
-rw-r--r--svx/source/xoutdev/XPropertyEntry.cxx29
-rw-r--r--svx/source/xoutdev/_xoutbmp.cxx389
-rw-r--r--svx/source/xoutdev/_xpoly.cxx947
-rw-r--r--svx/source/xoutdev/xattr.cxx3118
-rw-r--r--svx/source/xoutdev/xattr2.cxx731
-rw-r--r--svx/source/xoutdev/xattrbmp.cxx342
-rw-r--r--svx/source/xoutdev/xpool.cxx219
-rw-r--r--svx/source/xoutdev/xtabbtmp.cxx114
-rw-r--r--svx/source/xoutdev/xtabcolr.cxx172
-rw-r--r--svx/source/xoutdev/xtabdash.cxx220
-rw-r--r--svx/source/xoutdev/xtabgrdt.cxx216
-rw-r--r--svx/source/xoutdev/xtabhtch.cxx195
-rw-r--r--svx/source/xoutdev/xtable.cxx392
-rw-r--r--svx/source/xoutdev/xtablend.cxx163
-rw-r--r--svx/source/xoutdev/xtabptrn.cxx149
697 files changed, 323862 insertions, 0 deletions
diff --git a/svx/source/accessibility/AccessibleControlShape.cxx b/svx/source/accessibility/AccessibleControlShape.cxx
new file mode 100644
index 000000000..45b7edd75
--- /dev/null
+++ b/svx/source/accessibility/AccessibleControlShape.cxx
@@ -0,0 +1,879 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * 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/AccessibleControlShape.hxx>
+#include <svx/AccessibleShapeInfo.hxx>
+#include <DescriptionGenerator.hxx>
+#include <com/sun/star/awt/XWindow.hpp>
+#include <com/sun/star/beans/XPropertySet.hpp>
+#include <com/sun/star/drawing/XControlShape.hpp>
+#include <com/sun/star/accessibility/AccessibleRelationType.hpp>
+#include <com/sun/star/accessibility/AccessibleRole.hpp>
+#include <com/sun/star/accessibility/AccessibleEventId.hpp>
+#include <com/sun/star/accessibility/AccessibleStateType.hpp>
+#include <com/sun/star/lang/IndexOutOfBoundsException.hpp>
+#include <com/sun/star/reflection/ProxyFactory.hpp>
+#include <com/sun/star/util/XModeChangeBroadcaster.hpp>
+#include <com/sun/star/container/XContainer.hpp>
+#include <comphelper/processfactory.hxx>
+#include <comphelper/property.hxx>
+#include <unotools/accessiblestatesethelper.hxx>
+#include <unotools/accessiblerelationsethelper.hxx>
+#include <svx/IAccessibleParent.hxx>
+#include <svx/svdouno.hxx>
+#include <svx/ShapeTypeHandler.hxx>
+#include <svx/SvxShapeTypes.hxx>
+#include <comphelper/accessiblewrapper.hxx>
+#include <svx/svdview.hxx>
+#include <svx/svdpagv.hxx>
+#include <svx/strings.hrc>
+#include <vcl/svapp.hxx>
+#include <vcl/window.hxx>
+#include <sal/log.hxx>
+#include <tools/debug.hxx>
+#include <tools/diagnose_ex.h>
+
+using namespace ::accessibility;
+using namespace ::com::sun::star::accessibility;
+using namespace ::com::sun::star::uno;
+using namespace ::com::sun::star::awt;
+using namespace ::com::sun::star::beans;
+using namespace ::com::sun::star::util;
+using namespace ::com::sun::star::lang;
+using namespace ::com::sun::star::reflection;
+using namespace ::com::sun::star::drawing;
+using namespace ::com::sun::star::container;
+
+namespace
+{
+ OUString lcl_getNamePropertyName( )
+ {
+ return "Name";
+ }
+ OUString lcl_getDescPropertyName( )
+ {
+ return "HelpText";
+ }
+ OUString lcl_getLabelPropertyName( )
+ {
+ return "Label";
+ }
+ OUString lcl_getLabelControlPropertyName( )
+ {
+ return "LabelControl";
+ }
+ // return the property which should be used as AccessibleName
+ OUString lcl_getPreferredAccNameProperty( const Reference< XPropertySetInfo >& _rxPSI )
+ {
+ if ( _rxPSI.is() && _rxPSI->hasPropertyByName( lcl_getLabelPropertyName() ) )
+ return lcl_getLabelPropertyName();
+ else
+ return lcl_getNamePropertyName();
+ }
+
+ // determines whether or not a state which belongs to the inner context needs to be forwarded to the "composed"
+ // context
+ bool isComposedState( const sal_Int16 _nState )
+ {
+ return ( ( AccessibleStateType::INVALID != _nState )
+ && ( AccessibleStateType::DEFUNC != _nState )
+ && ( AccessibleStateType::ICONIFIED != _nState )
+ && ( AccessibleStateType::RESIZABLE != _nState )
+ && ( AccessibleStateType::SELECTABLE != _nState )
+ && ( AccessibleStateType::SHOWING != _nState )
+ && ( AccessibleStateType::MANAGES_DESCENDANTS != _nState )
+ && ( AccessibleStateType::VISIBLE != _nState )
+ );
+ }
+
+ /// determines whether the given control is in alive mode
+ bool isAliveMode( const Reference< XControl >& _rxControl )
+ {
+ OSL_PRECOND( _rxControl.is(), "AccessibleControlShape::isAliveMode: invalid control" );
+ return _rxControl.is() && !_rxControl->isDesignMode();
+ }
+}
+
+AccessibleControlShape::AccessibleControlShape (
+ const AccessibleShapeInfo& rShapeInfo,
+ const AccessibleShapeTreeInfo& rShapeTreeInfo)
+ : AccessibleShape (rShapeInfo, rShapeTreeInfo)
+ , m_bListeningForName( false )
+ , m_bListeningForDesc( false )
+ , m_bMultiplexingStates( false )
+ , m_bDisposeNativeContext( false )
+ , m_bWaitingForControl( false )
+{
+ m_pChildManager = new comphelper::OWrappedAccessibleChildrenManager( comphelper::getProcessComponentContext() );
+
+ osl_atomic_increment( &m_refCount );
+ {
+ m_pChildManager->setOwningAccessible( this );
+ }
+ osl_atomic_decrement( &m_refCount );
+}
+
+AccessibleControlShape::~AccessibleControlShape()
+{
+ m_pChildManager.clear();
+
+ if ( m_xControlContextProxy.is() )
+ m_xControlContextProxy->setDelegator( nullptr );
+ m_xControlContextProxy.clear();
+ m_xControlContextTypeAccess.clear();
+ m_xControlContextComponent.clear();
+ // this should remove the _only_ three "real" reference (means not delegated to
+ // ourself) to this proxy, and thus delete it
+}
+
+namespace {
+ Reference< XContainer > lcl_getControlContainer( const OutputDevice* _pWin, const SdrView* _pView )
+ {
+ Reference< XContainer > xReturn;
+ DBG_ASSERT( _pView, "lcl_getControlContainer: invalid view!" );
+ if ( _pView && _pView->GetSdrPageView())
+ {
+ xReturn.set(_pView->GetSdrPageView()->GetControlContainer( *_pWin ), css::uno::UNO_QUERY);
+ }
+ return xReturn;
+ }
+}
+
+void AccessibleControlShape::Init()
+{
+ AccessibleShape::Init();
+
+ OSL_ENSURE( !m_xControlContextProxy.is(), "AccessibleControlShape::Init: already initialized!" );
+ try
+ {
+ // What we need to do here is merge the functionality of the AccessibleContext of our UNO control
+ // with our own AccessibleContext-related functionality.
+
+ // The problem is that we do not know the interfaces our "inner" context supports - this may be any
+ // XAccessibleXXX interface (or even any other) which makes sense for it.
+
+ // In theory, we could implement all possible interfaces ourself, and re-route all functionality to
+ // the inner context (except those we implement ourself, like XAccessibleComponent). But this is in no
+ // way future-proof - as soon as an inner context appears which implements an additional interface,
+ // we would need to adjust our implementation to support this new interface, too. Bad idea.
+
+ // The usual solution for such a problem is aggregation. Aggregation means using UNO's own mechanism
+ // for merging an inner with an outer component, and get a component which behaves as it is exactly one.
+ // This is what XAggregation is for. Unfortunately, aggregation requires _exact_ control over the ref count
+ // of the inner object, which we do not have at all.
+ // Bad, too.
+
+ // But there is a solution: com.sun.star.reflection.ProxyFactory. This service is able to create a proxy
+ // for any component, which supports _exactly_ the same interfaces as the component. In addition, it can
+ // be aggregated, as by definition the proxy's ref count is exactly 1 when returned from the factory.
+ // Sounds better. Though this yields the problem of slightly degraded performance, it's the only solution
+ // I'm aware of at the moment...
+
+ // get the control which belongs to our model (relative to our view)
+ const vcl::Window* pViewWindow = maShapeTreeInfo.GetWindow();
+ SdrUnoObj* pUnoObjectImpl = dynamic_cast<SdrUnoObj*>(SdrObject::getSdrObjectFromXShape(mxShape));
+ SdrView* pView = maShapeTreeInfo.GetSdrView();
+ OSL_ENSURE( pView && pViewWindow && pUnoObjectImpl, "AccessibleControlShape::Init: no view, or no view window, no SdrUnoObj!" );
+
+ if ( pView && pViewWindow && pUnoObjectImpl )
+ {
+ // get the context of the control - it will be our "inner" context
+ m_xUnoControl = pUnoObjectImpl->GetUnoControl( *pView, *pViewWindow->GetOutDev() );
+
+ if ( !m_xUnoControl.is() )
+ {
+ // the control has not yet been created. Though speaking strictly, it is a bug that
+ // our instance here is created without an existing control (because an AccessibleControlShape
+ // is a representation of a view object, and can only live if the view it should represent
+ // is complete, which implies a living control), it's by far the easiest and most riskless way
+ // to fix this here in this class.
+ // Okay, we will add as listener to the control container where we expect our control to appear.
+ OSL_ENSURE( !m_bWaitingForControl, "AccessibleControlShape::Init: already waiting for the control!" );
+
+ Reference< XContainer > xControlContainer = lcl_getControlContainer( pViewWindow->GetOutDev(), maShapeTreeInfo.GetSdrView() );
+ OSL_ENSURE( xControlContainer.is(), "AccessibleControlShape::Init: unable to find my ControlContainer!" );
+ if ( xControlContainer.is() )
+ {
+ xControlContainer->addContainerListener( this );
+ m_bWaitingForControl = true;
+ }
+ }
+ else
+ {
+ Reference< XModeChangeBroadcaster > xControlModes( m_xUnoControl, UNO_QUERY );
+ Reference< XAccessible > xControlAccessible( xControlModes, UNO_QUERY );
+ Reference< XAccessibleContext > xNativeControlContext;
+ if ( xControlAccessible.is() )
+ xNativeControlContext = xControlAccessible->getAccessibleContext();
+ OSL_ENSURE( xNativeControlContext.is(), "AccessibleControlShape::Init: no AccessibleContext for the control!" );
+ m_aControlContext = WeakReference< XAccessibleContext >( xNativeControlContext );
+
+ // add as listener to the context - we want to multiplex some states
+ if ( isAliveMode( m_xUnoControl ) && xNativeControlContext.is() )
+ { // (but only in alive mode)
+ startStateMultiplexing( );
+ }
+
+ // now that we have all information about our control, do some adjustments
+ adjustAccessibleRole();
+ initializeComposedState();
+
+ // some initialization for our child manager, which is used in alive mode only
+ if ( isAliveMode( m_xUnoControl ) )
+ {
+ Reference< XAccessibleStateSet > xStates( getAccessibleStateSet( ) );
+ OSL_ENSURE( xStates.is(), "AccessibleControlShape::AccessibleControlShape: no inner state set!" );
+ m_pChildManager->setTransientChildren( !xStates.is() || xStates->contains( AccessibleStateType::MANAGES_DESCENDANTS ) );
+ }
+
+ // finally, aggregate a proxy for the control context
+ // first a factory for the proxy
+ Reference< XProxyFactory > xFactory = ProxyFactory::create( comphelper::getProcessComponentContext() );
+ // then the proxy itself
+ if ( xNativeControlContext.is() )
+ {
+ m_xControlContextProxy = xFactory->createProxy( xNativeControlContext );
+ m_xControlContextTypeAccess.set( xNativeControlContext, UNO_QUERY_THROW );
+ m_xControlContextComponent.set( xNativeControlContext, UNO_QUERY_THROW );
+
+ // aggregate the proxy
+ osl_atomic_increment( &m_refCount );
+ if ( m_xControlContextProxy.is() )
+ {
+ // At this point in time, the proxy has a ref count of exactly one - in m_xControlContextProxy.
+ // Remember to _not_ reset this member unless the delegator of the proxy has been reset, too!
+ m_xControlContextProxy->setDelegator( *this );
+ }
+ osl_atomic_decrement( &m_refCount );
+
+ m_bDisposeNativeContext = true;
+
+ // Finally, we need to add ourself as mode listener to the control. In case the mode switches,
+ // we need to dispose ourself.
+ xControlModes->addModeChangeListener( this );
+ }
+ }
+ }
+ }
+ catch( const Exception& )
+ {
+ OSL_FAIL( "AccessibleControlShape::Init: could not \"aggregate\" the controls XAccessibleContext!" );
+ }
+}
+
+void SAL_CALL AccessibleControlShape::grabFocus()
+{
+ if ( !m_xUnoControl.is() || !isAliveMode( m_xUnoControl ) )
+ {
+ // in design mode, we simply forward the request to the base class
+ AccessibleShape::grabFocus();
+ }
+ else
+ {
+ Reference< XWindow > xWindow( m_xUnoControl, UNO_QUERY );
+ OSL_ENSURE( xWindow.is(), "AccessibleControlShape::grabFocus: invalid control!" );
+ if ( xWindow.is() )
+ xWindow->setFocus();
+ }
+}
+
+OUString SAL_CALL AccessibleControlShape::getImplementationName()
+{
+ return "com.sun.star.comp.accessibility.AccessibleControlShape";
+}
+
+OUString AccessibleControlShape::CreateAccessibleBaseName()
+{
+ OUString sName;
+
+ ShapeTypeId nShapeType = ShapeTypeHandler::Instance().GetTypeId (mxShape);
+ switch (nShapeType)
+ {
+ case DRAWING_CONTROL:
+ sName = "ControlShape";
+ break;
+ default:
+ sName = "UnknownAccessibleControlShape";
+ if (mxShape.is())
+ sName += ": " + mxShape->getShapeType();
+ }
+
+ return sName;
+}
+
+OUString
+ AccessibleControlShape::CreateAccessibleDescription()
+{
+ DescriptionGenerator aDG (mxShape);
+ ShapeTypeId nShapeType = ShapeTypeHandler::Instance().GetTypeId (mxShape);
+ switch (nShapeType)
+ {
+ case DRAWING_CONTROL:
+ {
+ // check if we can obtain the "Desc" property from the model
+ OUString sDesc( getControlModelStringProperty( lcl_getDescPropertyName() ) );
+ if ( sDesc.isEmpty() )
+ { // no -> use the default
+ aDG.Initialize (STR_ObjNameSingulUno);
+ aDG.AddProperty ("ControlBackground", DescriptionGenerator::PropertyType::Color);
+ aDG.AddProperty ( "ControlBorder", DescriptionGenerator::PropertyType::Integer);
+ }
+ // ensure that we are listening to the Name property
+ m_bListeningForDesc = ensureListeningState( m_bListeningForDesc, true, lcl_getDescPropertyName() );
+ }
+ break;
+
+ default:
+ aDG.Initialize (u"Unknown accessible control shape");
+ if (mxShape.is())
+ {
+ aDG.AppendString (u"service name=");
+ aDG.AppendString (mxShape->getShapeType());
+ }
+ }
+
+ return aDG();
+}
+
+IMPLEMENT_FORWARD_REFCOUNT( AccessibleControlShape, AccessibleShape )
+IMPLEMENT_GET_IMPLEMENTATION_ID( AccessibleControlShape )
+
+void SAL_CALL AccessibleControlShape::propertyChange( const PropertyChangeEvent& _rEvent )
+{
+ ::osl::MutexGuard aGuard( m_aMutex );
+
+ // check if it is the name or the description
+ if ( _rEvent.PropertyName == lcl_getNamePropertyName()
+ || _rEvent.PropertyName == lcl_getLabelPropertyName() )
+ {
+ SetAccessibleName(
+ CreateAccessibleName(),
+ AccessibleContextBase::AutomaticallyCreated);
+ }
+ else if ( _rEvent.PropertyName == lcl_getDescPropertyName() )
+ {
+ SetAccessibleDescription(
+ CreateAccessibleDescription(),
+ AccessibleContextBase::AutomaticallyCreated);
+ }
+#if OSL_DEBUG_LEVEL > 0
+ else
+ {
+ OSL_FAIL( "AccessibleControlShape::propertyChange: where did this come from?" );
+ }
+#endif
+}
+
+Any SAL_CALL AccessibleControlShape::queryInterface( const Type& _rType )
+{
+ Any aReturn = AccessibleShape::queryInterface( _rType );
+ if ( !aReturn.hasValue() )
+ {
+ aReturn = AccessibleControlShape_Base::queryInterface( _rType );
+ if ( !aReturn.hasValue() && m_xControlContextProxy.is() )
+ aReturn = m_xControlContextProxy->queryAggregation( _rType );
+ }
+ return aReturn;
+}
+
+Sequence< Type > SAL_CALL AccessibleControlShape::getTypes()
+{
+ Sequence< Type > aShapeTypes = AccessibleShape::getTypes();
+ Sequence< Type > aOwnTypes = AccessibleControlShape_Base::getTypes();
+
+ Sequence< Type > aAggregateTypes;
+ if ( m_xControlContextTypeAccess.is() )
+ aAggregateTypes = m_xControlContextTypeAccess->getTypes();
+
+ // remove duplicates
+ return comphelper::combineSequences(comphelper::concatSequences( aShapeTypes, aOwnTypes), aAggregateTypes );
+}
+
+void SAL_CALL AccessibleControlShape::notifyEvent( const AccessibleEventObject& _rEvent )
+{
+ if ( AccessibleEventId::STATE_CHANGED == _rEvent.EventId )
+ {
+ // multiplex this change
+ sal_Int16 nLostState( 0 ), nGainedState( 0 );
+ _rEvent.OldValue >>= nLostState;
+ _rEvent.NewValue >>= nGainedState;
+
+ // don't multiplex states which the inner context is not responsible for
+ if ( isComposedState( nLostState ) )
+ AccessibleShape::ResetState( nLostState );
+
+ if ( isComposedState( nGainedState ) )
+ AccessibleShape::SetState( nGainedState );
+ }
+ else
+ {
+ AccessibleEventObject aTranslatedEvent( _rEvent );
+
+ {
+ ::osl::MutexGuard aGuard( m_aMutex );
+
+ // let the child manager translate the event
+ aTranslatedEvent.Source = *this;
+ m_pChildManager->translateAccessibleEvent( _rEvent, aTranslatedEvent );
+
+ // see if any of these notifications affect our child manager
+ m_pChildManager->handleChildNotification( _rEvent );
+ }
+
+ FireEvent( aTranslatedEvent );
+ }
+}
+
+void SAL_CALL AccessibleControlShape::modeChanged(const ModeChangeEvent& rSource)
+{
+ // did it come from our inner context (the real one, not it's proxy!)?
+ SAL_INFO("sw.uno", "AccessibleControlShape::modeChanged");
+ Reference<XControl> xSource(rSource.Source, UNO_QUERY); // for faster compare
+ if(xSource.get() != m_xUnoControl.get())
+ {
+ SAL_WARN("sw.uno", "AccessibleControlShape::modeChanged: where did this come from?");
+ return;
+ }
+ SolarMutexGuard g;
+ // If our "pseudo-aggregated" inner context does not live anymore,
+ // we don't want to live, too. This is accomplished by asking our
+ // parent to replace this object with a new one. Disposing this
+ // object and sending notifications about the replacement are in
+ // the responsibility of our parent.
+ const bool bReplaced = mpParent->ReplaceChild(this, mxShape, 0, maShapeTreeInfo);
+ SAL_WARN_IF(!bReplaced, "sw.uno", "AccessibleControlShape::modeChanged: replacing ourselves away did fail");
+}
+
+void SAL_CALL AccessibleControlShape::disposing (const EventObject& _rSource)
+{
+ AccessibleShape::disposing( _rSource );
+}
+
+bool AccessibleControlShape::ensureListeningState(
+ const bool _bCurrentlyListening, const bool _bNeedNewListening,
+ const OUString& _rPropertyName )
+{
+ if ( ( _bCurrentlyListening == _bNeedNewListening ) || !ensureControlModelAccess() )
+ // nothing to do
+ return _bCurrentlyListening;
+
+ try
+ {
+ if ( !m_xModelPropsMeta.is() || m_xModelPropsMeta->hasPropertyByName( _rPropertyName ) )
+ {
+ // add or revoke as listener
+ if ( _bNeedNewListening )
+ m_xControlModel->addPropertyChangeListener( _rPropertyName, static_cast< XPropertyChangeListener* >( this ) );
+ else
+ m_xControlModel->removePropertyChangeListener( _rPropertyName, static_cast< XPropertyChangeListener* >( this ) );
+ }
+ else
+ OSL_FAIL( "AccessibleControlShape::ensureListeningState: this property does not exist at this model!" );
+ }
+ catch( const Exception& )
+ {
+ OSL_FAIL( "AccessibleControlShape::ensureListeningState: could not change the listening state!" );
+ }
+
+ return _bNeedNewListening;
+}
+
+sal_Int32 SAL_CALL AccessibleControlShape::getAccessibleChildCount( )
+{
+ if ( !m_xUnoControl.is() )
+ return 0;
+ else if ( !isAliveMode( m_xUnoControl ) )
+ // no special action required when in design mode
+ return AccessibleShape::getAccessibleChildCount( );
+ else
+ {
+ // in alive mode, we have the full control over our children - they are determined by the children
+ // of the context of our UNO control
+ Reference< XAccessibleContext > xControlContext( m_aControlContext );
+ OSL_ENSURE( xControlContext.is(), "AccessibleControlShape::getAccessibleChildCount: control context already dead! How this!" );
+ return xControlContext.is() ? xControlContext->getAccessibleChildCount() : 0;
+ }
+}
+
+Reference< XAccessible > SAL_CALL AccessibleControlShape::getAccessibleChild( sal_Int32 i )
+{
+ Reference< XAccessible > xChild;
+ if ( !m_xUnoControl.is() )
+ {
+ throw IndexOutOfBoundsException();
+ }
+ if ( !isAliveMode( m_xUnoControl ) )
+ {
+ // no special action required when in design mode - let the base class handle this
+ xChild = AccessibleShape::getAccessibleChild( i );
+ }
+ else
+ {
+ // in alive mode, we have the full control over our children - they are determined by the children
+ // of the context of our UNO control
+
+ Reference< XAccessibleContext > xControlContext( m_aControlContext );
+ OSL_ENSURE( xControlContext.is(), "AccessibleControlShape::getAccessibleChildCount: control context already dead! How this!" );
+ if ( xControlContext.is() )
+ {
+ Reference< XAccessible > xInnerChild( xControlContext->getAccessibleChild( i ) );
+ OSL_ENSURE( xInnerChild.is(), "AccessibleControlShape::getAccessibleChild: control context returned nonsense!" );
+ if ( xInnerChild.is() )
+ {
+ // we need to wrap this inner child into an own implementation
+ xChild = m_pChildManager->getAccessibleWrapperFor( xInnerChild );
+ }
+ }
+ }
+
+#if OSL_DEBUG_LEVEL > 0
+ sal_Int32 nChildIndex = -1;
+ Reference< XAccessibleContext > xContext;
+ if ( xChild.is() )
+ xContext = xChild->getAccessibleContext( );
+ if ( xContext.is() )
+ nChildIndex = xContext->getAccessibleIndexInParent( );
+ SAL_WARN_IF( nChildIndex != i, "svx", "AccessibleControlShape::getAccessibleChild: index mismatch,"
+ " nChildIndex=" << nChildIndex << " vs i=" << i );
+#endif
+ return xChild;
+}
+
+Reference< XAccessibleRelationSet > SAL_CALL AccessibleControlShape::getAccessibleRelationSet( )
+{
+ rtl::Reference<utl::AccessibleRelationSetHelper> pRelationSetHelper = new utl::AccessibleRelationSetHelper;
+ ensureControlModelAccess();
+ AccessibleControlShape* pCtlAccShape = GetLabeledByControlShape();
+ if(pCtlAccShape)
+ {
+ Reference < XAccessible > xAcc (pCtlAccShape->getAccessibleContext(), UNO_QUERY);
+
+ css::uno::Sequence< css::uno::Reference< css::uno::XInterface > > aSequence { xAcc };
+ if( getAccessibleRole() == AccessibleRole::RADIO_BUTTON )
+ {
+ pRelationSetHelper->AddRelation( AccessibleRelation( AccessibleRelationType::MEMBER_OF, aSequence ) );
+ }
+ else
+ {
+ pRelationSetHelper->AddRelation( AccessibleRelation( AccessibleRelationType::LABELED_BY, aSequence ) );
+ }
+ }
+ return pRelationSetHelper;
+}
+
+OUString AccessibleControlShape::CreateAccessibleName()
+{
+ ensureControlModelAccess();
+
+ OUString sName;
+ sal_Int16 aAccessibleRole = getAccessibleRole();
+ if ( aAccessibleRole != AccessibleRole::SHAPE
+ && aAccessibleRole != AccessibleRole::RADIO_BUTTON )
+ {
+ AccessibleControlShape* pCtlAccShape = GetLabeledByControlShape();
+ if(pCtlAccShape)
+ {
+ sName = pCtlAccShape->CreateAccessibleName();
+ }
+ }
+
+ if (sName.isEmpty())
+ {
+ // check if we can obtain the "Name" resp. "Label" property from the model
+ const OUString& rAccNameProperty = lcl_getPreferredAccNameProperty( m_xModelPropsMeta );
+ sName = getControlModelStringProperty( rAccNameProperty );
+ if ( !sName.getLength() )
+ { // no -> use the default
+ sName = AccessibleShape::CreateAccessibleName();
+ }
+ }
+
+ // now that somebody first asked us for our name, ensure that we are listening to name changes on the model
+ m_bListeningForName = ensureListeningState( m_bListeningForName, true, lcl_getPreferredAccNameProperty( m_xModelPropsMeta ) );
+
+ return sName;
+}
+
+void SAL_CALL AccessibleControlShape::disposing()
+{
+ // ensure we're not listening
+ m_bListeningForName = ensureListeningState( m_bListeningForName, false, lcl_getPreferredAccNameProperty( m_xModelPropsMeta ) );
+ m_bListeningForDesc = ensureListeningState( m_bListeningForDesc, false, lcl_getDescPropertyName() );
+
+ if ( m_bMultiplexingStates )
+ stopStateMultiplexing( );
+
+ // dispose the child cache/map
+ m_pChildManager->dispose();
+
+ // release the model
+ m_xControlModel.clear();
+ m_xModelPropsMeta.clear();
+ m_aControlContext = WeakReference< XAccessibleContext >();
+
+ // stop listening at the control container (should never be necessary here, but who knows...)
+ if ( m_bWaitingForControl )
+ {
+ OSL_FAIL( "AccessibleControlShape::disposing: this should never happen!" );
+ Reference< XContainer > xContainer = lcl_getControlContainer( maShapeTreeInfo.GetWindow()->GetOutDev(), maShapeTreeInfo.GetSdrView() );
+ if ( xContainer.is() )
+ {
+ m_bWaitingForControl = false;
+ xContainer->removeContainerListener( this );
+ }
+ }
+
+ // forward the disposal to our inner context
+ if ( m_bDisposeNativeContext )
+ {
+ // don't listen for mode changes anymore
+ Reference< XModeChangeBroadcaster > xControlModes( m_xUnoControl, UNO_QUERY );
+ OSL_ENSURE( xControlModes.is(), "AccessibleControlShape::disposing: don't have a mode broadcaster anymore!" );
+ if ( xControlModes.is() )
+ xControlModes->removeModeChangeListener( this );
+
+ if ( m_xControlContextComponent.is() )
+ m_xControlContextComponent->dispose();
+ // do _not_ clear m_xControlContextProxy! This has to be done in the dtor for correct ref-count handling
+
+ // no need to dispose the proxy/inner context anymore
+ m_bDisposeNativeContext = false;
+ }
+
+ m_xUnoControl.clear();
+
+ // let the base do its stuff
+ AccessibleShape::disposing();
+}
+
+bool AccessibleControlShape::ensureControlModelAccess()
+{
+ if ( m_xControlModel.is() )
+ return true;
+
+ try
+ {
+ Reference< XControlShape > xShape( mxShape, UNO_QUERY );
+ if ( xShape.is() )
+ m_xControlModel.set(xShape->getControl(), css::uno::UNO_QUERY);
+
+ if ( m_xControlModel.is() )
+ m_xModelPropsMeta = m_xControlModel->getPropertySetInfo();
+ }
+ catch( const Exception& )
+ {
+ TOOLS_WARN_EXCEPTION( "svx", "AccessibleControlShape::ensureControlModelAccess" );
+ }
+
+ return m_xControlModel.is();
+}
+
+void AccessibleControlShape::startStateMultiplexing()
+{
+ OSL_PRECOND( !m_bMultiplexingStates, "AccessibleControlShape::startStateMultiplexing: already multiplexing!" );
+
+#if OSL_DEBUG_LEVEL > 0
+ // we should have a control, and it should be in alive mode
+ OSL_PRECOND( isAliveMode( m_xUnoControl ),
+ "AccessibleControlShape::startStateMultiplexing: should be done in alive mode only!" );
+#endif
+ // we should have the native context of the control
+ Reference< XAccessibleEventBroadcaster > xBroadcaster( m_aControlContext.get(), UNO_QUERY );
+ OSL_ENSURE( xBroadcaster.is(), "AccessibleControlShape::startStateMultiplexing: no AccessibleEventBroadcaster on the native context!" );
+
+ if ( xBroadcaster.is() )
+ {
+ xBroadcaster->addAccessibleEventListener( this );
+ m_bMultiplexingStates = true;
+ }
+}
+
+void AccessibleControlShape::stopStateMultiplexing()
+{
+ OSL_PRECOND( m_bMultiplexingStates, "AccessibleControlShape::stopStateMultiplexing: not multiplexing!" );
+
+ // we should have the native context of the control
+ Reference< XAccessibleEventBroadcaster > xBroadcaster( m_aControlContext.get(), UNO_QUERY );
+ OSL_ENSURE( xBroadcaster.is(), "AccessibleControlShape::stopStateMultiplexing: no AccessibleEventBroadcaster on the native context!" );
+
+ if ( xBroadcaster.is() )
+ {
+ xBroadcaster->removeAccessibleEventListener( this );
+ m_bMultiplexingStates = false;
+ }
+}
+
+OUString AccessibleControlShape::getControlModelStringProperty( const OUString& _rPropertyName ) const
+{
+ OUString sReturn;
+ try
+ {
+ if ( const_cast< AccessibleControlShape* >( this )->ensureControlModelAccess() )
+ {
+ if ( !m_xModelPropsMeta.is() || m_xModelPropsMeta->hasPropertyByName( _rPropertyName ) )
+ // ask only if a) the control does not have a PropertySetInfo object or b) it has, and the
+ // property in question is available
+ m_xControlModel->getPropertyValue( _rPropertyName ) >>= sReturn;
+ }
+ }
+ catch( const Exception& )
+ {
+ TOOLS_WARN_EXCEPTION( "svx", "OAccessibleControlContext::getModelStringProperty" );
+ }
+ return sReturn;
+}
+
+void AccessibleControlShape::adjustAccessibleRole( )
+{
+ // if we're in design mode, we are a simple SHAPE, in alive mode, we use the role of our inner context
+ if ( !isAliveMode( m_xUnoControl ) )
+ return;
+
+ // we're in alive mode -> determine the role of the inner context
+ Reference< XAccessibleContext > xNativeContext( m_aControlContext );
+ OSL_PRECOND( xNativeContext.is(), "AccessibleControlShape::adjustAccessibleRole: no inner context!" );
+ if ( xNativeContext.is() )
+ SetAccessibleRole( xNativeContext->getAccessibleRole( ) );
+}
+
+#ifdef DBG_UTIL
+
+bool AccessibleControlShape::SetState( sal_Int16 _nState )
+{
+ OSL_ENSURE( !isAliveMode( m_xUnoControl ) || !isComposedState( _nState ),
+ "AccessibleControlShape::SetState: a state which should be determined by the control context is set from outside!" );
+ return AccessibleShape::SetState( _nState );
+}
+#endif // DBG_UTIL
+
+void AccessibleControlShape::initializeComposedState()
+{
+ if ( !isAliveMode( m_xUnoControl ) )
+ // no action necessary for design mode
+ return;
+
+ // get our own state set implementation
+ ::utl::AccessibleStateSetHelper* pComposedStates = mxStateSet.get();
+ OSL_PRECOND( pComposedStates,
+ "AccessibleControlShape::initializeComposedState: no composed set!" );
+
+ // we need to reset some states of the composed set, because they either do not apply
+ // for controls in alive mode, or are in the responsibility of the UNO-control, anyway
+ pComposedStates->RemoveState( AccessibleStateType::ENABLED ); // this is controlled by the UNO-control
+ pComposedStates->RemoveState( AccessibleStateType::SENSITIVE ); // this is controlled by the UNO-control
+ pComposedStates->RemoveState( AccessibleStateType::FOCUSABLE ); // this is controlled by the UNO-control
+ pComposedStates->RemoveState( AccessibleStateType::SELECTABLE ); // this does not hold for an alive UNO-control
+#if OSL_DEBUG_LEVEL > 0
+ // now, only states which are not in the responsibility of the UNO control should be part of this state set
+ {
+ const Sequence< sal_Int16 > aInitStates = pComposedStates->getStates();
+ for ( sal_Int16 state : aInitStates )
+ OSL_ENSURE( !isComposedState( state ),
+ "AccessibleControlShape::initializeComposedState: invalid initial composed state (should be controlled by the UNO-control)!" );
+ }
+#endif
+
+ // get my inner context
+ Reference< XAccessibleContext > xInnerContext( m_aControlContext );
+ OSL_PRECOND( xInnerContext.is(), "AccessibleControlShape::initializeComposedState: no inner context!" );
+ if ( !xInnerContext.is() )
+ return;
+
+ // get all states of the inner context
+ Reference< XAccessibleStateSet > xInnerStates( xInnerContext->getAccessibleStateSet() );
+ OSL_ENSURE( xInnerStates.is(), "AccessibleControlShape::initializeComposedState: no inner states!" );
+ Sequence< sal_Int16 > aInnerStates;
+ if ( xInnerStates.is() )
+ aInnerStates = xInnerStates->getStates();
+
+ // look which one are to be propagated to the composed context
+ for ( const sal_Int16 nState : std::as_const(aInnerStates) )
+ {
+ if ( isComposedState( nState ) && !pComposedStates->contains( nState ) )
+ {
+ pComposedStates->AddState( nState );
+ }
+ }
+}
+
+void SAL_CALL AccessibleControlShape::elementInserted( const css::container::ContainerEvent& _rEvent )
+{
+ Reference< XContainer > xContainer( _rEvent.Source, UNO_QUERY );
+ Reference< XControl > xControl( _rEvent.Element, UNO_QUERY );
+
+ OSL_ENSURE( xContainer.is() && xControl.is(),
+ "AccessibleControlShape::elementInserted: invalid event description!" );
+
+ if ( !xControl.is() )
+ return;
+
+ ensureControlModelAccess();
+
+ Reference< XInterface > xNewNormalized( xControl->getModel(), UNO_QUERY );
+ Reference< XInterface > xMyModelNormalized( m_xControlModel, UNO_QUERY );
+ if ( !(xNewNormalized && xMyModelNormalized) )
+ return;
+
+ // now finally the control for the model we're responsible for has been inserted into the container
+ Reference< XInterface > xKeepAlive( *this );
+
+ // first, we're not interested in any more container events
+ if ( xContainer.is() )
+ {
+ xContainer->removeContainerListener( this );
+ m_bWaitingForControl = false;
+ }
+
+ // second, we need to replace ourself with a new version, which now can be based on the
+ // control
+ OSL_VERIFY( mpParent->ReplaceChild ( this, mxShape, 0, maShapeTreeInfo ) );
+}
+
+void SAL_CALL AccessibleControlShape::elementRemoved( const css::container::ContainerEvent& )
+{
+ // not interested in
+}
+
+void SAL_CALL AccessibleControlShape::elementReplaced( const css::container::ContainerEvent& )
+{
+ // not interested in
+}
+
+AccessibleControlShape* AccessibleControlShape::GetLabeledByControlShape( )
+{
+ if(m_xControlModel.is())
+ {
+ const OUString& rAccLabelControlProperty = lcl_getLabelControlPropertyName();
+ Any sCtlLabelBy;
+ // get the "label by" property value of the control
+ if (::comphelper::hasProperty(rAccLabelControlProperty, m_xControlModel))
+ {
+ sCtlLabelBy = m_xControlModel->getPropertyValue(rAccLabelControlProperty);
+ if( sCtlLabelBy.hasValue() )
+ {
+ Reference< XPropertySet > xAsSet (sCtlLabelBy, UNO_QUERY);
+ AccessibleControlShape* pCtlAccShape = mpParent->GetAccControlShapeFromModel(xAsSet.get());
+ return pCtlAccShape;
+ }
+ }
+ }
+ return nullptr;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/accessibility/AccessibleEmptyEditSource.cxx b/svx/source/accessibility/AccessibleEmptyEditSource.cxx
new file mode 100644
index 000000000..e1426d239
--- /dev/null
+++ b/svx/source/accessibility/AccessibleEmptyEditSource.cxx
@@ -0,0 +1,329 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+
+// Global header
+
+
+#include <memory>
+#include <svl/itemset.hxx>
+#include <editeng/editdata.hxx>
+#include <editeng/outliner.hxx>
+#include <svx/svdmodel.hxx>
+#include <svx/svdobj.hxx>
+#include <svx/svdpool.hxx>
+
+
+// Project-local header
+
+
+#include "AccessibleEmptyEditSource.hxx"
+#include <svx/unoshtxt.hxx>
+
+namespace accessibility
+{
+ namespace {
+
+ /** This class simply wraps a SvxTextEditSource, forwarding all
+ methods except the GetBroadcaster() call
+ */
+ class AccessibleProxyEditSource_Impl : public SvxEditSource
+ {
+ public:
+ /** Construct AccessibleEmptyEditSource_Impl
+
+ @param rBrdCast
+
+ Proxy broadcaster to allow seamless flipping of edit source implementations. ProxyEditSource and EmptyEditSource
+ */
+ AccessibleProxyEditSource_Impl( SdrObject& rObj,
+ SdrView& rView,
+ const OutputDevice& rViewWindow );
+
+ // from the SvxEditSource interface
+ SvxTextForwarder* GetTextForwarder() override;
+ SvxViewForwarder* GetViewForwarder() override;
+ SvxEditViewForwarder* GetEditViewForwarder( bool bCreate = false ) override;
+
+ std::unique_ptr<SvxEditSource> Clone() const override;
+
+ void UpdateData() override;
+
+ SfxBroadcaster& GetBroadcaster() const override;
+
+ private:
+ SvxTextEditSource maEditSource;
+
+ };
+
+ /** Dummy class, faking exactly one empty paragraph for EditEngine accessibility
+ */
+ class AccessibleEmptyEditSource_Impl : public SvxEditSource, public SvxViewForwarder, public SvxTextForwarder, public SfxBroadcaster
+ {
+ public:
+
+ AccessibleEmptyEditSource_Impl() {}
+
+ // SvxEditSource
+ SvxTextForwarder* GetTextForwarder() override { return this; }
+ SvxViewForwarder* GetViewForwarder() override { return this; }
+ std::unique_ptr<SvxEditSource> Clone() const override { return nullptr; }
+ void UpdateData() override {}
+ SfxBroadcaster& GetBroadcaster() const override { return *const_cast<AccessibleEmptyEditSource_Impl*>(this); }
+
+ // SvxTextForwarder
+ sal_Int32 GetParagraphCount() const override { return 1; }
+ sal_Int32 GetTextLen( sal_Int32 /*nParagraph*/ ) const override { return 0; }
+ OUString GetText( const ESelection& /*rSel*/ ) const override { return OUString(); }
+ SfxItemSet GetAttribs( const ESelection& /*rSel*/, EditEngineAttribs /*nOnlyHardAttrib*/ = EditEngineAttribs::All ) const override
+ {
+ // AW: Very dangerous: The former implementation used a SfxItemPool created on the
+ // fly which of course was deleted again ASAP. Thus, the returned SfxItemSet was using
+ // a deleted Pool by design.
+ return SfxItemSet(SdrObject::GetGlobalDrawObjectItemPool());
+ }
+ SfxItemSet GetParaAttribs( sal_Int32 /*nPara*/ ) const override { return GetAttribs(ESelection()); }
+ void SetParaAttribs( sal_Int32 /*nPara*/, const SfxItemSet& /*rSet*/ ) override {}
+ void RemoveAttribs( const ESelection& /*rSelection*/ ) override {}
+ void GetPortions( sal_Int32 /*nPara*/, std::vector<sal_Int32>& /*rList*/ ) const override {}
+
+ SfxItemState GetItemState( const ESelection& /*rSel*/, sal_uInt16 /*nWhich*/ ) const override { return SfxItemState::UNKNOWN; }
+ SfxItemState GetItemState( sal_Int32 /*nPara*/, sal_uInt16 /*nWhich*/ ) const override { return SfxItemState::UNKNOWN; }
+
+ SfxItemPool* GetPool() const override { return nullptr; }
+
+ void QuickInsertText( const OUString& /*rText*/, const ESelection& /*rSel*/ ) override {}
+ void QuickInsertField( const SvxFieldItem& /*rFld*/, const ESelection& /*rSel*/ ) override {}
+ void QuickSetAttribs( const SfxItemSet& /*rSet*/, const ESelection& /*rSel*/ ) override {}
+ void QuickInsertLineBreak( const ESelection& /*rSel*/ ) override {}
+
+ const SfxItemSet * GetEmptyItemSetPtr() override { return nullptr; }
+
+ void AppendParagraph() override {}
+ sal_Int32 AppendTextPortion( sal_Int32 /*nPara*/, const OUString & /*rText*/, const SfxItemSet & /*rSet*/ ) override { return 0; }
+
+ //XTextCopy
+ void CopyText(const SvxTextForwarder& ) override {}
+
+ OUString CalcFieldValue( const SvxFieldItem& /*rField*/, sal_Int32 /*nPara*/, sal_Int32 /*nPos*/, std::optional<Color>& /*rpTxtColor*/, std::optional<Color>& /*rpFldColor*/ ) override
+ {
+ return OUString();
+ }
+ void FieldClicked( const SvxFieldItem& ) override {}
+
+ bool IsValid() const override { return true; }
+
+ LanguageType GetLanguage( sal_Int32, sal_Int32 ) const override { return LANGUAGE_DONTKNOW; }
+ sal_Int32 GetFieldCount( sal_Int32 ) const override { return 0; }
+ EFieldInfo GetFieldInfo( sal_Int32, sal_uInt16 ) const override { return EFieldInfo(); }
+ EBulletInfo GetBulletInfo( sal_Int32 ) const override { return EBulletInfo(); }
+ tools::Rectangle GetCharBounds( sal_Int32, sal_Int32 ) const override { return tools::Rectangle(); }
+ tools::Rectangle GetParaBounds( sal_Int32 ) const override { return tools::Rectangle(); }
+ MapMode GetMapMode() const override { return MapMode(); }
+ OutputDevice* GetRefDevice() const override { return nullptr; }
+ bool GetIndexAtPoint( const Point&, sal_Int32&, sal_Int32& ) const override { return false; }
+ bool GetWordIndices( sal_Int32, sal_Int32, sal_Int32&, sal_Int32& ) const override { return false; }
+ bool GetAttributeRun( sal_Int32&, sal_Int32&, sal_Int32, sal_Int32, bool ) const override { return false; }
+ sal_Int32 GetLineCount( sal_Int32 nPara ) const override { return nPara == 0 ? 1 : 0; }
+ sal_Int32 GetLineLen( sal_Int32, sal_Int32 ) const override { return 0; }
+ void GetLineBoundaries( /*out*/sal_Int32 & rStart, /*out*/sal_Int32 & rEnd, sal_Int32 /*nParagraph*/, sal_Int32 /*nLine*/ ) const override { rStart = rEnd = 0; }
+ sal_Int32 GetLineNumberAtIndex( sal_Int32 /*nPara*/, sal_Int32 /*nIndex*/ ) const override { return 0; }
+
+ // the following two methods would, strictly speaking, require
+ // a switch to a real EditSource, too. Fortunately, the
+ // AccessibleEditableTextPara implementation currently always
+ // calls GetEditViewForwarder(true) before doing
+ // changes. Thus, we rely on this behaviour here (problem
+ // when that changes: via accessibility API, it would no
+ // longer be possible to enter text in previously empty
+ // shapes).
+ bool Delete( const ESelection& ) override { return false; }
+ bool InsertText( const OUString&, const ESelection& ) override { return false; }
+ bool QuickFormatDoc( bool ) override { return true; }
+ sal_Int16 GetDepth( sal_Int32 ) const override { return -1; }
+ bool SetDepth( sal_Int32, sal_Int16 ) override { return true; }
+
+ Point LogicToPixel( const Point& rPoint, const MapMode& /*rMapMode*/ ) const override { return rPoint; }
+ Point PixelToLogic( const Point& rPoint, const MapMode& /*rMapMode*/ ) const override { return rPoint; }
+
+ };
+
+ }
+
+ // Implementing AccessibleProxyEditSource_Impl
+
+
+ AccessibleProxyEditSource_Impl::AccessibleProxyEditSource_Impl( SdrObject& rObj,
+ SdrView& rView,
+ const OutputDevice& rViewWindow ) :
+ maEditSource( rObj, nullptr, rView, rViewWindow )
+ {
+ }
+
+ SvxTextForwarder* AccessibleProxyEditSource_Impl::GetTextForwarder()
+ {
+ return maEditSource.GetTextForwarder();
+ }
+
+ SvxViewForwarder* AccessibleProxyEditSource_Impl::GetViewForwarder()
+ {
+ return maEditSource.GetViewForwarder();
+ }
+
+ SvxEditViewForwarder* AccessibleProxyEditSource_Impl::GetEditViewForwarder( bool bCreate )
+ {
+ return maEditSource.GetEditViewForwarder( bCreate );
+ }
+
+ std::unique_ptr<SvxEditSource> AccessibleProxyEditSource_Impl::Clone() const
+ {
+ return maEditSource.Clone();
+ }
+
+ void AccessibleProxyEditSource_Impl::UpdateData()
+ {
+ maEditSource.UpdateData();
+ }
+
+ SfxBroadcaster& AccessibleProxyEditSource_Impl::GetBroadcaster() const
+ {
+ return maEditSource.GetBroadcaster();
+ }
+
+
+ // Implementing AccessibleEmptyEditSource
+
+
+ AccessibleEmptyEditSource::AccessibleEmptyEditSource( SdrObject& rObj,
+ SdrView& rView,
+ const OutputDevice& rViewWindow ) :
+ mpEditSource( new AccessibleEmptyEditSource_Impl() ),
+ mrObj(rObj),
+ mrView(rView),
+ mrViewWindow(rViewWindow),
+ mbEditSourceEmpty( true )
+ {
+ StartListening( mrObj.getSdrModelFromSdrObject() );
+ }
+
+ AccessibleEmptyEditSource::~AccessibleEmptyEditSource()
+ {
+ if( !mbEditSourceEmpty )
+ {
+ // deregister as listener
+ if (mpEditSource)
+ EndListening( mpEditSource->GetBroadcaster() );
+ }
+ else
+ {
+ EndListening( mrObj.getSdrModelFromSdrObject() );
+ }
+ }
+
+ SvxTextForwarder* AccessibleEmptyEditSource::GetTextForwarder()
+ {
+ if (!mpEditSource)
+ return nullptr;
+
+ return mpEditSource->GetTextForwarder();
+ }
+
+ SvxViewForwarder* AccessibleEmptyEditSource::GetViewForwarder()
+ {
+ if (!mpEditSource)
+ return nullptr;
+
+ return mpEditSource->GetViewForwarder();
+ }
+
+ void AccessibleEmptyEditSource::Switch2ProxyEditSource()
+ {
+ // deregister EmptyEditSource model listener
+ EndListening( mrObj.getSdrModelFromSdrObject() );
+
+ ::std::unique_ptr< SvxEditSource > pProxySource( new AccessibleProxyEditSource_Impl(mrObj, mrView, mrViewWindow) );
+ mpEditSource.swap(pProxySource);
+
+ // register as listener
+ StartListening( mpEditSource->GetBroadcaster() );
+
+ // we've irrevocably a full EditSource now.
+ mbEditSourceEmpty = false;
+ }
+
+ SvxEditViewForwarder* AccessibleEmptyEditSource::GetEditViewForwarder( bool bCreate )
+ {
+ if (!mpEditSource)
+ return nullptr;
+
+ // switch edit source, if not yet done
+ if( mbEditSourceEmpty && bCreate )
+ Switch2ProxyEditSource();
+
+ return mpEditSource->GetEditViewForwarder( bCreate );
+ }
+
+ std::unique_ptr<SvxEditSource> AccessibleEmptyEditSource::Clone() const
+ {
+ if (!mpEditSource)
+ return nullptr;
+
+ return mpEditSource->Clone();
+ }
+
+ void AccessibleEmptyEditSource::UpdateData()
+ {
+ if (mpEditSource)
+ mpEditSource->UpdateData();
+ }
+
+ SfxBroadcaster& AccessibleEmptyEditSource::GetBroadcaster() const
+ {
+ return *const_cast<AccessibleEmptyEditSource*>(this);
+ }
+
+ void AccessibleEmptyEditSource::Notify( SfxBroadcaster& /*rBC*/, const SfxHint& rHint )
+ {
+ const SdrHint* pSdrHint = ( rHint.GetId() == SfxHintId::ThisIsAnSdrHint ? static_cast<const SdrHint*>(&rHint) : nullptr );
+
+ if( pSdrHint && pSdrHint->GetKind() == SdrHintKind::BeginEdit &&
+ &mrObj == pSdrHint->GetObject() && mpEditSource )
+ {
+ // switch edit source, if not yet done. This is necessary
+ // to become a full-fledged EditSource the first time a
+ // user start entering text in a previously empty object.
+ if( mbEditSourceEmpty )
+ Switch2ProxyEditSource();
+ }
+ else if (pSdrHint && pSdrHint->GetObject()!=nullptr)
+ {
+ // When the SdrObject just got a para outliner object then
+ // switch the edit source.
+ if (pSdrHint->GetObject()->GetOutlinerParaObject() != nullptr)
+ Switch2ProxyEditSource();
+ }
+
+ // forward messages
+ Broadcast( rHint );
+ }
+
+} // end of namespace accessibility
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/accessibility/AccessibleEmptyEditSource.hxx b/svx/source/accessibility/AccessibleEmptyEditSource.hxx
new file mode 100644
index 000000000..6cfceeda4
--- /dev/null
+++ b/svx/source/accessibility/AccessibleEmptyEditSource.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_SVX_SOURCE_ACCESSIBILITY_ACCESSIBLEEMPTYEDITSOURCE_HXX
+#define INCLUDED_SVX_SOURCE_ACCESSIBILITY_ACCESSIBLEEMPTYEDITSOURCE_HXX
+
+#include <svl/SfxBroadcaster.hxx>
+#include <svl/lstner.hxx>
+
+#include <memory>
+#include <editeng/unoedsrc.hxx>
+
+class SdrObject;
+class SdrView;
+class OutputDevice;
+
+namespace accessibility
+{
+ /** Proxy edit source for shapes without text
+
+ Extracted from old SvxDummyEditSource
+ */
+ class AccessibleEmptyEditSource : public SvxEditSource, public SfxListener, public SfxBroadcaster
+ {
+ public:
+ /** Create proxy edit source for shapes without text
+
+ Since the views don't broadcast their dying, make sure that
+ this object gets destroyed if the view becomes invalid
+
+ The window is necessary, since our views can display on multiple windows
+
+ Make sure you only create such an object if the shape _really_
+ does not contain text.
+ */
+ AccessibleEmptyEditSource( SdrObject& rObj, SdrView& rView, const OutputDevice& rViewWindow );
+ virtual ~AccessibleEmptyEditSource() override;
+
+ // from the SvxEditSource interface
+ SvxTextForwarder* GetTextForwarder() override;
+ SvxViewForwarder* GetViewForwarder() override;
+
+ std::unique_ptr<SvxEditSource> Clone() const override;
+
+ // this method internally switches from empty to proxy mode,
+ // creating an SvxTextEditSource for the functionality.
+ SvxEditViewForwarder* GetEditViewForwarder( bool bCreate = false ) override;
+
+ void UpdateData() override;
+ SfxBroadcaster& GetBroadcaster() const override;
+
+ // from the SfxListener interface
+ void Notify( SfxBroadcaster& rBC, const SfxHint& rHint ) override;
+
+ private:
+ void Switch2ProxyEditSource();
+
+ /** Pointer to edit source implementation. This is switched on
+ a GetEditViewForwarder( true ) call, to actually create a
+ SvxTextEditSource.
+
+ @dyn
+ */
+ std::unique_ptr< SvxEditSource > mpEditSource;
+
+ SdrObject& mrObj;
+ SdrView& mrView;
+ const OutputDevice& mrViewWindow;
+
+ bool mbEditSourceEmpty;
+ };
+
+} // namespace accessibility
+
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/accessibility/AccessibleFrameSelector.cxx b/svx/source/accessibility/AccessibleFrameSelector.cxx
new file mode 100644
index 000000000..3c3007fc0
--- /dev/null
+++ b/svx/source/accessibility/AccessibleFrameSelector.cxx
@@ -0,0 +1,380 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <AccessibleFrameSelector.hxx>
+#include <com/sun/star/accessibility/AccessibleStateType.hpp>
+#include <com/sun/star/accessibility/AccessibleRole.hpp>
+#include <unotools/accessiblestatesethelper.hxx>
+#include <unotools/accessiblerelationsethelper.hxx>
+#include <vcl/svapp.hxx>
+#include <vcl/settings.hxx>
+#include <svx/frmsel.hxx>
+#include <svx/dialmgr.hxx>
+
+#include <frmsel.hrc>
+
+namespace svx::a11y {
+
+using ::com::sun::star::uno::Any;
+using ::com::sun::star::uno::Reference;
+using ::com::sun::star::uno::Sequence;
+using ::com::sun::star::uno::RuntimeException;
+
+using namespace ::com::sun::star::accessibility;
+
+
+AccFrameSelector::AccFrameSelector(FrameSelector& rFrameSel)
+ : mpFrameSel(&rFrameSel)
+{
+}
+
+AccFrameSelector::~AccFrameSelector()
+{
+}
+
+IMPLEMENT_FORWARD_XINTERFACE2( AccFrameSelector, OAccessibleComponentHelper, OAccessibleHelper_Base )
+IMPLEMENT_FORWARD_XTYPEPROVIDER2( AccFrameSelector, OAccessibleComponentHelper, OAccessibleHelper_Base )
+
+Reference< XAccessibleContext > AccFrameSelector::getAccessibleContext( )
+{
+ return this;
+}
+
+sal_Int32 AccFrameSelector::getAccessibleChildCount( )
+{
+ SolarMutexGuard aGuard;
+ IsValid();
+ return mpFrameSel->GetEnabledBorderCount();
+}
+
+Reference< XAccessible > AccFrameSelector::getAccessibleChild( sal_Int32 i )
+{
+ SolarMutexGuard aGuard;
+ IsValid();
+ Reference< XAccessible > xRet = mpFrameSel->GetChildAccessible( i );
+ if( !xRet.is() )
+ throw RuntimeException();
+ return xRet;
+}
+
+Reference< XAccessible > AccFrameSelector::getAccessibleParent( )
+{
+ SolarMutexGuard aGuard;
+ IsValid();
+ Reference< XAccessible > xRet = mpFrameSel->getAccessibleParent();
+ return xRet;
+}
+
+sal_Int16 AccFrameSelector::getAccessibleRole( )
+{
+ return AccessibleRole::OPTION_PANE;
+}
+
+OUString AccFrameSelector::getAccessibleDescription( )
+{
+ SolarMutexGuard aGuard;
+ IsValid();
+ return SvxResId(RID_SVXSTR_FRMSEL_DESCRIPTIONS[0].first);
+}
+
+OUString AccFrameSelector::getAccessibleName( )
+{
+ SolarMutexGuard aGuard;
+ IsValid();
+ return SvxResId(RID_SVXSTR_FRMSEL_TEXTS[0].first);
+}
+
+Reference< XAccessibleRelationSet > AccFrameSelector::getAccessibleRelationSet( )
+{
+ SolarMutexGuard aGuard;
+ IsValid();
+ return mpFrameSel->get_accessible_relation_set();
+}
+
+Reference< XAccessibleStateSet > AccFrameSelector::getAccessibleStateSet( )
+{
+ SolarMutexGuard aGuard;
+ rtl::Reference<utl::AccessibleStateSetHelper> pStateSetHelper = new utl::AccessibleStateSetHelper;
+
+ if(!mpFrameSel)
+ pStateSetHelper->AddState(AccessibleStateType::DEFUNC);
+ else
+ {
+ const sal_Int16 aStandardStates[] =
+ {
+ AccessibleStateType::EDITABLE,
+ AccessibleStateType::FOCUSABLE,
+ AccessibleStateType::MULTI_SELECTABLE,
+ AccessibleStateType::SELECTABLE,
+ AccessibleStateType::SHOWING,
+ AccessibleStateType::VISIBLE,
+ AccessibleStateType::OPAQUE,
+ 0};
+ sal_Int16 nState = 0;
+ while(aStandardStates[nState])
+ {
+ pStateSetHelper->AddState(aStandardStates[nState++]);
+ }
+ if(mpFrameSel->IsEnabled())
+ {
+ pStateSetHelper->AddState(AccessibleStateType::ENABLED);
+ pStateSetHelper->AddState(AccessibleStateType::SENSITIVE);
+ }
+
+ if (mpFrameSel->HasFocus())
+ {
+ pStateSetHelper->AddState(AccessibleStateType::ACTIVE);
+ pStateSetHelper->AddState(AccessibleStateType::FOCUSED);
+ pStateSetHelper->AddState(AccessibleStateType::SELECTED);
+ }
+ }
+ return pStateSetHelper;
+}
+
+Reference< XAccessible > AccFrameSelector::getAccessibleAtPoint(
+ const css::awt::Point& aPt )
+{
+ SolarMutexGuard aGuard;
+ IsValid();
+ //aPt is relative to the frame selector
+ return mpFrameSel->GetChildAccessible( Point( aPt.X, aPt.Y ) );
+}
+
+void AccFrameSelector::grabFocus( )
+{
+ SolarMutexGuard aGuard;
+ IsValid();
+ mpFrameSel->GrabFocus();
+}
+
+sal_Int32 AccFrameSelector::getForeground( )
+{
+ SolarMutexGuard aGuard;
+
+ //see FrameSelector::Paint
+ const StyleSettings& rStyles = Application::GetSettings().GetStyleSettings();
+ return sal_Int32(rStyles.GetLabelTextColor());
+}
+
+sal_Int32 AccFrameSelector::getBackground( )
+{
+ SolarMutexGuard aGuard;
+
+ //see FrameSelector::Paint
+ const StyleSettings& rStyles = Application::GetSettings().GetStyleSettings();
+ return sal_Int32(rStyles.GetDialogColor());
+}
+
+css::awt::Rectangle AccFrameSelector::implGetBounds()
+{
+ SolarMutexGuard aGuard;
+ IsValid();
+
+ css::awt::Rectangle aRet;
+
+ const Point aOutPos;
+ Size aOutSize(mpFrameSel->GetOutputSizePixel());
+
+ aRet.X = aOutPos.X();
+ aRet.Y = aOutPos.Y();
+ aRet.Width = aOutSize.Width();
+ aRet.Height = aOutSize.Height();
+
+ return aRet;
+}
+
+void AccFrameSelector::IsValid()
+{
+ if(!mpFrameSel)
+ throw RuntimeException();
+}
+
+void AccFrameSelector::Invalidate()
+{
+ mpFrameSel = nullptr;
+}
+
+AccFrameSelectorChild::AccFrameSelectorChild(FrameSelector& rFrameSel, FrameBorderType eBorder)
+ : mpFrameSel(&rFrameSel)
+ , meBorder(eBorder)
+{
+}
+
+AccFrameSelectorChild::~AccFrameSelectorChild()
+{
+}
+
+IMPLEMENT_FORWARD_XINTERFACE2( AccFrameSelectorChild, OAccessibleComponentHelper, OAccessibleHelper_Base )
+IMPLEMENT_FORWARD_XTYPEPROVIDER2( AccFrameSelectorChild, OAccessibleComponentHelper, OAccessibleHelper_Base )
+
+Reference< XAccessibleContext > AccFrameSelectorChild::getAccessibleContext( )
+{
+ return this;
+}
+
+sal_Int32 AccFrameSelectorChild::getAccessibleChildCount( )
+{
+ SolarMutexGuard aGuard;
+ IsValid();
+ return 0;
+}
+
+Reference< XAccessible > AccFrameSelectorChild::getAccessibleChild( sal_Int32 )
+{
+ throw RuntimeException();
+}
+
+Reference< XAccessible > AccFrameSelectorChild::getAccessibleParent( )
+{
+ SolarMutexGuard aGuard;
+ IsValid();
+ Reference< XAccessible > xRet = mpFrameSel->CreateAccessible();
+ return xRet;
+}
+
+sal_Int16 AccFrameSelectorChild::getAccessibleRole( )
+{
+ return AccessibleRole::CHECK_BOX;
+}
+
+OUString AccFrameSelectorChild::getAccessibleDescription( )
+{
+ SolarMutexGuard aGuard;
+ IsValid();
+ return SvxResId(RID_SVXSTR_FRMSEL_DESCRIPTIONS[static_cast<sal_uInt32>(meBorder)].first);
+}
+
+OUString AccFrameSelectorChild::getAccessibleName( )
+{
+ SolarMutexGuard aGuard;
+ IsValid();
+ return SvxResId(RID_SVXSTR_FRMSEL_TEXTS[static_cast<sal_uInt32>(meBorder)].first);
+}
+
+Reference< XAccessibleRelationSet > AccFrameSelectorChild::getAccessibleRelationSet( )
+{
+ SolarMutexGuard aGuard;
+ IsValid();
+ Reference< XAccessibleRelationSet > xRet = new utl::AccessibleRelationSetHelper;
+ return xRet;
+}
+
+Reference< XAccessibleStateSet > AccFrameSelectorChild::getAccessibleStateSet( )
+{
+ SolarMutexGuard aGuard;
+ rtl::Reference<utl::AccessibleStateSetHelper> pStateSetHelper = new utl::AccessibleStateSetHelper;
+
+ if(!mpFrameSel)
+ pStateSetHelper->AddState(AccessibleStateType::DEFUNC);
+ else
+ {
+ const sal_Int16 aStandardStates[] =
+ {
+ AccessibleStateType::EDITABLE,
+ AccessibleStateType::FOCUSABLE,
+ AccessibleStateType::MULTI_SELECTABLE,
+ AccessibleStateType::SELECTABLE,
+ AccessibleStateType::SHOWING,
+ AccessibleStateType::VISIBLE,
+ AccessibleStateType::OPAQUE,
+ 0};
+ sal_Int16 nState = 0;
+ while(aStandardStates[nState])
+ {
+ pStateSetHelper->AddState(aStandardStates[nState++]);
+ }
+ if(mpFrameSel->IsEnabled())
+ {
+ pStateSetHelper->AddState(AccessibleStateType::ENABLED);
+ pStateSetHelper->AddState(AccessibleStateType::SENSITIVE);
+ }
+
+ if (mpFrameSel->HasFocus() && mpFrameSel->IsBorderSelected(meBorder))
+ {
+ pStateSetHelper->AddState(AccessibleStateType::ACTIVE);
+ pStateSetHelper->AddState(AccessibleStateType::FOCUSED);
+ pStateSetHelper->AddState(AccessibleStateType::SELECTED);
+ }
+ }
+ return pStateSetHelper;
+}
+
+Reference< XAccessible > AccFrameSelectorChild::getAccessibleAtPoint(
+ const css::awt::Point& aPt )
+{
+ SolarMutexGuard aGuard;
+ IsValid();
+ //aPt is relative to the frame selector
+ return mpFrameSel->GetChildAccessible( Point( aPt.X, aPt.Y ) );
+}
+
+css::awt::Rectangle AccFrameSelectorChild::implGetBounds( )
+{
+ SolarMutexGuard aGuard;
+ IsValid();
+ const tools::Rectangle aSpot = mpFrameSel->GetClickBoundRect( meBorder );
+ Point aPos = aSpot.TopLeft();
+ Size aSz = aSpot.GetSize();
+ css::awt::Rectangle aRet;
+ aRet.X = aPos.X();
+ aRet.Y = aPos.Y();
+ aRet.Width = aSz.Width();
+ aRet.Height = aSz.Height();
+ return aRet;
+}
+
+void AccFrameSelectorChild::grabFocus( )
+{
+ SolarMutexGuard aGuard;
+ IsValid();
+ mpFrameSel->GrabFocus();
+}
+
+sal_Int32 AccFrameSelectorChild::getForeground( )
+{
+ SolarMutexGuard aGuard;
+
+ //see FrameSelector::Paint
+ const StyleSettings& rStyles = Application::GetSettings().GetStyleSettings();
+ return sal_Int32(rStyles.GetLabelTextColor());
+}
+
+sal_Int32 AccFrameSelectorChild::getBackground( )
+{
+ SolarMutexGuard aGuard;
+
+ //see FrameSelector::Paint
+ const StyleSettings& rStyles = Application::GetSettings().GetStyleSettings();
+ return sal_Int32(rStyles.GetDialogColor());
+}
+
+void AccFrameSelectorChild::IsValid()
+{
+ if(!mpFrameSel)
+ throw RuntimeException();
+}
+
+void AccFrameSelectorChild::Invalidate()
+{
+ mpFrameSel = nullptr;
+}
+
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/accessibility/AccessibleGraphicShape.cxx b/svx/source/accessibility/AccessibleGraphicShape.cxx
new file mode 100644
index 000000000..d94ce4886
--- /dev/null
+++ b/svx/source/accessibility/AccessibleGraphicShape.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 <svx/AccessibleGraphicShape.hxx>
+
+#include <svx/ShapeTypeHandler.hxx>
+#include <svx/SvxShapeTypes.hxx>
+#include <svx/svdobj.hxx>
+
+#include <com/sun/star/drawing/XShape.hpp>
+#include <com/sun/star/drawing/XShapeDescriptor.hpp>
+#include <comphelper/sequence.hxx>
+#include <cppuhelper/queryinterface.hxx>
+
+using namespace ::accessibility;
+using namespace ::com::sun::star;
+using namespace ::com::sun::star::accessibility;
+
+// internal
+AccessibleGraphicShape::AccessibleGraphicShape (
+ const AccessibleShapeInfo& rShapeInfo,
+ const AccessibleShapeTreeInfo& rShapeTreeInfo)
+ : AccessibleShape (rShapeInfo, rShapeTreeInfo)
+{
+}
+
+
+AccessibleGraphicShape::~AccessibleGraphicShape()
+{
+}
+
+// XAccessibleImage
+OUString SAL_CALL AccessibleGraphicShape::getAccessibleImageDescription()
+{
+ if (m_pShape)
+ return m_pShape->GetTitle();
+ return AccessibleShape::getAccessibleDescription ();
+}
+
+
+sal_Int32 SAL_CALL AccessibleGraphicShape::getAccessibleImageHeight()
+{
+ return AccessibleShape::getSize().Height;
+}
+
+
+sal_Int32 SAL_CALL AccessibleGraphicShape::getAccessibleImageWidth()
+{
+ return AccessibleShape::getSize().Width;
+}
+
+// XInterface
+css::uno::Any SAL_CALL
+ AccessibleGraphicShape::queryInterface (const css::uno::Type & rType)
+{
+ css::uno::Any aReturn = AccessibleShape::queryInterface (rType);
+ if ( ! aReturn.hasValue())
+ aReturn = ::cppu::queryInterface (rType,
+ static_cast<XAccessibleImage*>(this));
+ return aReturn;
+}
+
+
+void SAL_CALL
+ AccessibleGraphicShape::acquire()
+ noexcept
+{
+ AccessibleShape::acquire ();
+}
+
+
+void SAL_CALL
+ AccessibleGraphicShape::release()
+ noexcept
+{
+ AccessibleShape::release ();
+}
+
+// XServiceInfo
+OUString SAL_CALL
+ AccessibleGraphicShape::getImplementationName()
+{
+ return "AccessibleGraphicShape";
+}
+
+
+css::uno::Sequence< OUString> SAL_CALL
+ AccessibleGraphicShape::getSupportedServiceNames()
+{
+ ThrowIfDisposed ();
+ const css::uno::Sequence<OUString> vals { "com.sun.star.drawing.AccessibleGraphicShape" };
+ return comphelper::concatSequences(AccessibleShape::getSupportedServiceNames(), vals);
+}
+
+// XTypeProvider
+uno::Sequence<uno::Type> SAL_CALL
+ AccessibleGraphicShape::getTypes()
+{
+ // Get list of types from the context base implementation...
+ return comphelper::concatSequences(AccessibleShape::getTypes(),
+ uno::Sequence { cppu::UnoType<XAccessibleImage>::get() });
+}
+
+
+/// Create the base name of this object, i.e. the name without appended number.
+OUString
+ AccessibleGraphicShape::CreateAccessibleBaseName()
+{
+ OUString sName;
+
+ ShapeTypeId nShapeType = ShapeTypeHandler::Instance().GetTypeId (mxShape);
+ switch (nShapeType)
+ {
+ case DRAWING_GRAPHIC_OBJECT:
+ sName = "GraphicObjectShape";
+ break;
+
+ default:
+ sName = "UnknownAccessibleGraphicShape";
+ uno::Reference<drawing::XShapeDescriptor> xDescriptor (mxShape);
+ if (xDescriptor.is())
+ sName += ": " + xDescriptor->getShapeType();
+ }
+
+ return sName;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/accessibility/AccessibleOLEShape.cxx b/svx/source/accessibility/AccessibleOLEShape.cxx
new file mode 100644
index 000000000..9275ad180
--- /dev/null
+++ b/svx/source/accessibility/AccessibleOLEShape.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 <svx/AccessibleOLEShape.hxx>
+
+#include <svx/ShapeTypeHandler.hxx>
+#include <svx/SvxShapeTypes.hxx>
+#include <svx/svdoole2.hxx>
+
+#include <comphelper/sequence.hxx>
+#include <cppuhelper/queryinterface.hxx>
+
+#include <com/sun/star/lang/IndexOutOfBoundsException.hpp>
+#include <com/sun/star/drawing/XShape.hpp>
+#include <com/sun/star/drawing/XShapeDescriptor.hpp>
+
+using namespace ::accessibility;
+using namespace ::com::sun::star;
+using namespace ::com::sun::star::uno;
+using namespace ::com::sun::star::accessibility;
+
+// internal
+AccessibleOLEShape::AccessibleOLEShape (
+ const AccessibleShapeInfo& rShapeInfo,
+ const AccessibleShapeTreeInfo& rShapeTreeInfo)
+ : AccessibleShape (rShapeInfo, rShapeTreeInfo)
+{
+}
+
+
+AccessibleOLEShape::~AccessibleOLEShape()
+{
+}
+
+// XAccessibleAction
+sal_Int32 SAL_CALL AccessibleOLEShape::getAccessibleActionCount()
+{
+ return 0;
+}
+
+
+sal_Bool SAL_CALL AccessibleOLEShape::doAccessibleAction (sal_Int32 /*nIndex*/)
+{
+ throw lang::IndexOutOfBoundsException();
+}
+
+
+OUString SAL_CALL AccessibleOLEShape::getAccessibleActionDescription (sal_Int32 /*nIndex*/)
+{
+ throw lang::IndexOutOfBoundsException();
+}
+
+
+Reference<XAccessibleKeyBinding> SAL_CALL AccessibleOLEShape::getAccessibleActionKeyBinding (sal_Int32 /*nIndex*/)
+{
+ throw lang::IndexOutOfBoundsException();
+}
+
+// XInterface
+css::uno::Any SAL_CALL
+ AccessibleOLEShape::queryInterface (const css::uno::Type & rType)
+{
+ css::uno::Any aReturn = AccessibleShape::queryInterface (rType);
+ if ( ! aReturn.hasValue())
+ aReturn = ::cppu::queryInterface (rType,
+ static_cast<XAccessibleAction*>(this));
+ return aReturn;
+}
+
+
+void SAL_CALL
+ AccessibleOLEShape::acquire()
+ noexcept
+{
+ AccessibleShape::acquire ();
+}
+
+
+void SAL_CALL
+ AccessibleOLEShape::release()
+ noexcept
+{
+ AccessibleShape::release ();
+}
+
+// XServiceInfo
+OUString SAL_CALL
+ AccessibleOLEShape::getImplementationName()
+{
+ return "AccessibleOLEShape";
+}
+
+
+css::uno::Sequence< OUString> SAL_CALL
+ AccessibleOLEShape::getSupportedServiceNames()
+{
+ ThrowIfDisposed();
+ const css::uno::Sequence<OUString> vals { "com.sun.star.drawing.AccessibleOLEShape" };
+ return comphelper::concatSequences(AccessibleShape::getSupportedServiceNames(), vals);
+}
+
+// XTypeProvider
+uno::Sequence<uno::Type> SAL_CALL AccessibleOLEShape::getTypes()
+{
+ // Get list of types from the context base implementation...
+ return comphelper::concatSequences(AccessibleShape::getTypes(),
+ uno::Sequence { cppu::UnoType<XAccessibleAction>::get() } );
+}
+
+// XAccessibleExtendedAttributes
+uno::Any SAL_CALL AccessibleOLEShape::getExtendedAttributes()
+{
+ uno::Any strRet;
+ OUString style;
+ if( m_pShape )
+ {
+ style = "style:" + static_cast<SdrOle2Obj*>(m_pShape)->GetStyleString();
+ }
+ style += ";";
+ strRet <<= style;
+ return strRet;
+}
+
+/// Set this object's name if is different to the current name.
+OUString
+ AccessibleOLEShape::CreateAccessibleBaseName()
+{
+ OUString sName;
+
+ ShapeTypeId nShapeType = ShapeTypeHandler::Instance().GetTypeId (mxShape);
+ switch (nShapeType)
+ {
+ case DRAWING_APPLET:
+ sName = "AppletOLEShape";
+ break;
+ case DRAWING_FRAME:
+ sName = "FrameOLEShape";
+ break;
+ case DRAWING_OLE:
+ sName = "OLEShape";
+ break;
+ case DRAWING_PLUGIN:
+ sName = "PluginOLEShape";
+ break;
+
+ default:
+ sName = "UnknownAccessibleOLEShape";
+ uno::Reference<drawing::XShapeDescriptor> xDescriptor (mxShape);
+ if (xDescriptor.is())
+ sName += ": " + xDescriptor->getShapeType();
+ }
+
+ return sName;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/accessibility/AccessibleShape.cxx b/svx/source/accessibility/AccessibleShape.cxx
new file mode 100644
index 000000000..03996fe7f
--- /dev/null
+++ b/svx/source/accessibility/AccessibleShape.cxx
@@ -0,0 +1,1314 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * 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/AccessibleShape.hxx>
+#include <svx/AccessibleShapeInfo.hxx>
+#include <com/sun/star/accessibility/AccessibleRole.hpp>
+#include <com/sun/star/accessibility/AccessibleStateType.hpp>
+#include <com/sun/star/accessibility/AccessibleRelationType.hpp>
+#include <com/sun/star/accessibility/AccessibleEventId.hpp>
+#include <com/sun/star/beans/XPropertySet.hpp>
+#include <com/sun/star/drawing/XShapes.hpp>
+#include <com/sun/star/document/XShapeEventBroadcaster.hpp>
+#include <com/sun/star/lang/IndexOutOfBoundsException.hpp>
+#include <com/sun/star/drawing/FillStyle.hpp>
+#include <com/sun/star/text/XText.hpp>
+#include <sal/log.hxx>
+#include <editeng/unoedsrc.hxx>
+#include <svx/AccessibleTextHelper.hxx>
+#include <svx/ChildrenManager.hxx>
+#include <svx/IAccessibleParent.hxx>
+#include <svx/IAccessibleViewForwarder.hxx>
+#include <svx/unoshtxt.hxx>
+#include <svx/svdobj.hxx>
+#include <svx/unoapi.hxx>
+#include <svx/svdpage.hxx>
+#include <svx/ShapeTypeHandler.hxx>
+#include <svx/SvxShapeTypes.hxx>
+
+#include <vcl/svapp.hxx>
+#include <vcl/window.hxx>
+#include <unotools/accessiblestatesethelper.hxx>
+#include <unotools/accessiblerelationsethelper.hxx>
+#include <svx/svdview.hxx>
+#include <tools/diagnose_ex.h>
+#include <cppuhelper/queryinterface.hxx>
+#include <comphelper/sequence.hxx>
+#include "AccessibleEmptyEditSource.hxx"
+
+#include <algorithm>
+#include <memory>
+
+using namespace ::com::sun::star;
+using namespace ::com::sun::star::accessibility;
+using ::com::sun::star::uno::Reference;
+using ::com::sun::star::lang::IndexOutOfBoundsException;
+using ::com::sun::star::uno::RuntimeException;
+
+namespace accessibility {
+
+namespace {
+
+OUString GetOptionalProperty (
+ const Reference<beans::XPropertySet>& rxSet,
+ const OUString& rsPropertyName)
+{
+ OUString sValue;
+
+ if (rxSet.is())
+ {
+ const Reference<beans::XPropertySetInfo> xInfo (rxSet->getPropertySetInfo());
+ if ( ! xInfo.is() || xInfo->hasPropertyByName(rsPropertyName))
+ {
+ try
+ {
+ rxSet->getPropertyValue(rsPropertyName) >>= sValue;
+ }
+ catch (beans::UnknownPropertyException&)
+ {
+ // This exception should only be thrown when the property
+ // does not exits (of course) and the XPropertySetInfo is
+ // not available.
+ }
+ }
+ }
+ return sValue;
+}
+
+} // end of anonymous namespace
+
+// internal
+AccessibleShape::AccessibleShape (
+ const AccessibleShapeInfo& rShapeInfo,
+ const AccessibleShapeTreeInfo& rShapeTreeInfo)
+ : AccessibleContextBase (rShapeInfo.mxParent,AccessibleRole::SHAPE),
+ mxShape (rShapeInfo.mxShape),
+ maShapeTreeInfo (rShapeTreeInfo),
+ m_nIndexInParent(-1),
+ mpParent (rShapeInfo.mpChildrenManager)
+{
+ m_pShape = SdrObject::getSdrObjectFromXShape(mxShape);
+ UpdateNameAndDescription();
+}
+
+AccessibleShape::~AccessibleShape()
+{
+ mpChildrenManager.reset();
+ mpText.reset();
+ SAL_INFO("svx", "~AccessibleShape");
+
+ // Unregistering from the various broadcasters should be unnecessary
+ // since this destructor would not have been called if one of the
+ // broadcasters would still hold a strong reference to this object.
+}
+
+void AccessibleShape::Init()
+{
+ // Update the OPAQUE and SELECTED shape.
+ UpdateStates ();
+
+ // Create a children manager when this shape has children of its own.
+ Reference<drawing::XShapes> xShapes (mxShape, uno::UNO_QUERY);
+ if (xShapes.is() && xShapes->getCount() > 0)
+ mpChildrenManager.reset( new ChildrenManager (
+ this, xShapes, maShapeTreeInfo, *this) );
+ if (mpChildrenManager != nullptr)
+ mpChildrenManager->Update();
+
+ // Register at model as document::XEventListener.
+ if (mxShape.is() && maShapeTreeInfo.GetModelBroadcaster().is())
+ maShapeTreeInfo.GetModelBroadcaster()->addShapeEventListener(mxShape,
+ static_cast<document::XShapeEventListener*>(this));
+
+ // Beware! Here we leave the paths of the UNO API and descend into the
+ // depths of the core. Necessary for making the edit engine
+ // accessible.
+ Reference<text::XText> xText (mxShape, uno::UNO_QUERY);
+ if (!xText.is())
+ return;
+
+ SdrView* pView = maShapeTreeInfo.GetSdrView ();
+ const vcl::Window* pWindow = maShapeTreeInfo.GetWindow ();
+ if (!(pView != nullptr && pWindow != nullptr && mxShape.is()))
+ return;
+
+ // #107948# Determine whether shape text is empty
+ SdrObject* pSdrObject = SdrObject::getSdrObjectFromXShape(mxShape);
+ if( !pSdrObject )
+ return;
+
+ SdrTextObj* pTextObj = dynamic_cast<SdrTextObj*>( pSdrObject );
+ const bool hasOutlinerParaObject = (pTextObj && pTextObj->CanCreateEditOutlinerParaObject()) || (pSdrObject->GetOutlinerParaObject() != nullptr);
+
+ // create AccessibleTextHelper to handle this shape's text
+ if( !hasOutlinerParaObject )
+ {
+ // empty text -> use proxy edit source to delay creation of EditEngine
+ mpText.reset( new AccessibleTextHelper( std::make_unique<AccessibleEmptyEditSource >(*pSdrObject, *pView, *pWindow->GetOutDev()) ) );
+ }
+ else
+ {
+ // non-empty text -> use full-fledged edit source right away
+ mpText.reset( new AccessibleTextHelper( std::make_unique<SvxTextEditSource >(*pSdrObject, nullptr, *pView, *pWindow->GetOutDev()) ) );
+ }
+ if( pWindow->HasFocus() )
+ mpText->SetFocus();
+
+ mpText->SetEventSource(this);
+}
+
+
+void AccessibleShape::UpdateStates()
+{
+ if (mxStateSet == nullptr)
+ return;
+
+ // Set the opaque state for certain shape types when their fill style is
+ // solid.
+ bool bShapeIsOpaque = false;
+ switch (ShapeTypeHandler::Instance().GetTypeId (mxShape))
+ {
+ case DRAWING_PAGE:
+ case DRAWING_RECTANGLE:
+ case DRAWING_TEXT:
+ {
+ uno::Reference<beans::XPropertySet> xSet (mxShape, uno::UNO_QUERY);
+ if (xSet.is())
+ {
+ try
+ {
+ drawing::FillStyle aFillStyle;
+ bShapeIsOpaque = ( xSet->getPropertyValue ("FillStyle") >>= aFillStyle)
+ && aFillStyle == drawing::FillStyle_SOLID;
+ }
+ catch (css::beans::UnknownPropertyException&)
+ {
+ // Ignore.
+ }
+ }
+ }
+ }
+ if (bShapeIsOpaque)
+ mxStateSet->AddState (AccessibleStateType::OPAQUE);
+ else
+ mxStateSet->RemoveState (AccessibleStateType::OPAQUE);
+
+ // Set the selected state.
+ bool bShapeIsSelected = false;
+ // XXX fix_me this has to be done with an extra interface later on
+ if ( m_pShape && maShapeTreeInfo.GetSdrView() )
+ {
+ bShapeIsSelected = maShapeTreeInfo.GetSdrView()->IsObjMarked(m_pShape);
+ }
+
+ if (bShapeIsSelected)
+ mxStateSet->AddState (AccessibleStateType::SELECTED);
+ else
+ mxStateSet->RemoveState (AccessibleStateType::SELECTED);
+}
+
+OUString AccessibleShape::GetStyle() const
+{
+ return ShapeTypeHandler::CreateAccessibleBaseName( mxShape );
+}
+
+bool AccessibleShape::SetState (sal_Int16 aState)
+{
+ bool bStateHasChanged = false;
+
+ if (aState == AccessibleStateType::FOCUSED && mpText != nullptr)
+ {
+ // Offer FOCUSED state to edit engine and detect whether the state
+ // changes.
+ bool bIsFocused = mpText->HaveFocus ();
+ mpText->SetFocus();
+ bStateHasChanged = (bIsFocused != mpText->HaveFocus ());
+ }
+ else
+ bStateHasChanged = AccessibleContextBase::SetState (aState);
+
+ return bStateHasChanged;
+}
+
+
+bool AccessibleShape::ResetState (sal_Int16 aState)
+{
+ bool bStateHasChanged = false;
+
+ if (aState == AccessibleStateType::FOCUSED && mpText != nullptr)
+ {
+ // Try to remove FOCUSED state from the edit engine and detect
+ // whether the state changes.
+ bool bIsFocused = mpText->HaveFocus ();
+ mpText->SetFocus (false);
+ bStateHasChanged = (bIsFocused != mpText->HaveFocus ());
+ }
+ else
+ bStateHasChanged = AccessibleContextBase::ResetState (aState);
+
+ return bStateHasChanged;
+}
+
+
+bool AccessibleShape::GetState (sal_Int16 aState)
+{
+ if (aState == AccessibleStateType::FOCUSED && mpText != nullptr)
+ {
+ // Just delegate the call to the edit engine. The state is not
+ // merged into the state set.
+ return mpText->HaveFocus();
+ }
+ else
+ return AccessibleContextBase::GetState (aState);
+}
+
+// OverWrite the parent's getAccessibleName method
+OUString SAL_CALL AccessibleShape::getAccessibleName()
+{
+ ThrowIfDisposed ();
+ if (m_pShape && !m_pShape->GetTitle().isEmpty())
+ return CreateAccessibleName() + " " + m_pShape->GetTitle();
+ else
+ return CreateAccessibleName();
+}
+
+OUString SAL_CALL AccessibleShape::getAccessibleDescription()
+{
+ ThrowIfDisposed ();
+ if( m_pShape && !m_pShape->GetDescription().isEmpty())
+ return m_pShape->GetDescription() ;
+ else
+ return " ";
+}
+
+// XAccessibleContext
+/** The children of this shape come from two sources: The children from
+ group or scene shapes and the paragraphs of text.
+*/
+sal_Int32 SAL_CALL
+ AccessibleShape::getAccessibleChildCount ()
+{
+ if (IsDisposed())
+ {
+ return 0;
+ }
+
+ sal_Int32 nChildCount = 0;
+
+ // Add the number of shapes that are children of this shape.
+ if (mpChildrenManager != nullptr)
+ nChildCount += mpChildrenManager->GetChildCount ();
+ // Add the number text paragraphs.
+ if (mpText != nullptr)
+ nChildCount += mpText->GetChildCount ();
+
+ return nChildCount;
+}
+
+
+/** Forward the request to the shape. Return the requested shape or throw
+ an exception for a wrong index.
+*/
+uno::Reference<XAccessible> SAL_CALL
+ AccessibleShape::getAccessibleChild (sal_Int32 nIndex)
+{
+ ThrowIfDisposed ();
+
+ uno::Reference<XAccessible> xChild;
+
+ // Depending on the index decide whether to delegate this call to the
+ // children manager or the edit engine.
+ if ((mpChildrenManager != nullptr)
+ && (nIndex < mpChildrenManager->GetChildCount()))
+ {
+ xChild = mpChildrenManager->GetChild (nIndex);
+ }
+ else if (mpText != nullptr)
+ {
+ sal_Int32 nI = nIndex;
+ if (mpChildrenManager != nullptr)
+ nI -= mpChildrenManager->GetChildCount();
+ xChild = mpText->GetChild (nI);
+ }
+ else
+ throw lang::IndexOutOfBoundsException (
+ "shape has no child with index " + OUString::number(nIndex),
+ static_cast<uno::XWeak*>(this));
+
+ return xChild;
+}
+
+uno::Reference<XAccessibleRelationSet> SAL_CALL
+ AccessibleShape::getAccessibleRelationSet()
+{
+ ::osl::MutexGuard aGuard (m_aMutex);
+ if (mpParent == nullptr)
+ return uno::Reference<XAccessibleRelationSet>();
+
+ rtl::Reference<::utl::AccessibleRelationSetHelper> pRelationSet = new utl::AccessibleRelationSetHelper;
+
+ //this mxshape is the captioned shape
+ uno::Sequence< uno::Reference< uno::XInterface > > aSequence { mpParent->GetAccessibleCaption(mxShape) };
+ if(aSequence[0])
+ {
+ pRelationSet->AddRelation(
+ AccessibleRelation( AccessibleRelationType::DESCRIBED_BY, aSequence ) );
+ }
+ return pRelationSet;
+}
+
+/** Return a copy of the state set.
+ Possible states are:
+ ENABLED
+ SHOWING
+ VISIBLE
+*/
+uno::Reference<XAccessibleStateSet> SAL_CALL
+ AccessibleShape::getAccessibleStateSet()
+{
+ ::osl::MutexGuard aGuard (m_aMutex);
+
+ if (IsDisposed())
+ {
+ // Return a minimal state set that only contains the DEFUNC state.
+ return AccessibleContextBase::getAccessibleStateSet ();
+ }
+
+ ::utl::AccessibleStateSetHelper* pStateSet = mxStateSet.get();
+
+ if (!pStateSet)
+ return Reference<XAccessibleStateSet>();
+
+ // Merge current FOCUSED state from edit engine.
+ if (mpText)
+ {
+ if (mpText->HaveFocus())
+ pStateSet->AddState (AccessibleStateType::FOCUSED);
+ else
+ pStateSet->RemoveState (AccessibleStateType::FOCUSED);
+ }
+ //Just when the document is not read-only,set states EDITABLE,RESIZABLE,MOVEABLE
+ css::uno::Reference<XAccessible> xTempAcc = getAccessibleParent();
+ if( xTempAcc.is() )
+ {
+ css::uno::Reference<XAccessibleContext>
+ xTempAccContext = xTempAcc->getAccessibleContext();
+ if( xTempAccContext.is() )
+ {
+ css::uno::Reference<XAccessibleStateSet> rState =
+ xTempAccContext->getAccessibleStateSet();
+ if (rState.is())
+ {
+ const css::uno::Sequence<short> aStates = rState->getStates();
+ if (std::find(aStates.begin(), aStates.end(), AccessibleStateType::EDITABLE) != aStates.end())
+ {
+ pStateSet->AddState (AccessibleStateType::EDITABLE);
+ pStateSet->AddState (AccessibleStateType::RESIZABLE);
+ pStateSet->AddState (AccessibleStateType::MOVEABLE);
+ }
+ }
+ }
+ }
+
+ // Create a copy of the state set that may be modified by the
+ // caller without affecting the current state set.
+ Reference<XAccessibleStateSet> xStateSet(new ::utl::AccessibleStateSetHelper(*pStateSet));
+
+ if (mpParent && mpParent->IsDocumentSelAll())
+ {
+ ::utl::AccessibleStateSetHelper* pCopyStateSet =
+ static_cast<::utl::AccessibleStateSetHelper*>(xStateSet.get());
+ pCopyStateSet->AddState (AccessibleStateType::SELECTED);
+ }
+
+ return xStateSet;
+}
+
+// XAccessibleComponent
+/** The implementation below is at the moment straightforward. It iterates
+ over all children (and thereby instances all children which have not
+ been already instantiated) until a child covering the specified point is
+ found.
+ This leaves room for improvement. For instance, first iterate only over
+ the already instantiated children and only if no match is found
+ instantiate the remaining ones.
+*/
+uno::Reference<XAccessible > SAL_CALL
+ AccessibleShape::getAccessibleAtPoint (
+ const awt::Point& aPoint)
+{
+ ::osl::MutexGuard aGuard (m_aMutex);
+
+ sal_Int32 nChildCount = getAccessibleChildCount ();
+ for (sal_Int32 i=0; i<nChildCount; ++i)
+ {
+ Reference<XAccessible> xChild (getAccessibleChild (i));
+ if (xChild.is())
+ {
+ Reference<XAccessibleComponent> xChildComponent (
+ xChild->getAccessibleContext(), uno::UNO_QUERY);
+ if (xChildComponent.is())
+ {
+ awt::Rectangle aBBox (xChildComponent->getBounds());
+ if ( (aPoint.X >= aBBox.X)
+ && (aPoint.Y >= aBBox.Y)
+ && (aPoint.X < aBBox.X+aBBox.Width)
+ && (aPoint.Y < aBBox.Y+aBBox.Height) )
+ return xChild;
+ }
+ }
+ }
+
+ // Have not found a child under the given point. Returning empty
+ // reference to indicate this.
+ return uno::Reference<XAccessible>();
+}
+
+
+awt::Rectangle SAL_CALL AccessibleShape::getBounds()
+{
+ SolarMutexGuard aSolarGuard;
+ ::osl::MutexGuard aGuard (m_aMutex);
+
+ ThrowIfDisposed ();
+ awt::Rectangle aBoundingBox;
+ if ( mxShape.is() )
+ {
+
+ static constexpr OUStringLiteral sBoundRectName = u"BoundRect";
+ static constexpr OUStringLiteral sAnchorPositionName = u"AnchorPosition";
+
+ // Get the shape's bounding box in internal coordinates (in 100th of
+ // mm). Use the property BoundRect. Only if that is not supported ask
+ // the shape for its position and size directly.
+ Reference<beans::XPropertySet> xSet (mxShape, uno::UNO_QUERY);
+ Reference<beans::XPropertySetInfo> xSetInfo;
+ bool bFoundBoundRect = false;
+ if (xSet.is())
+ {
+ xSetInfo = xSet->getPropertySetInfo ();
+ if (xSetInfo.is())
+ {
+ if (xSetInfo->hasPropertyByName (sBoundRectName))
+ {
+ try
+ {
+ uno::Any aValue = xSet->getPropertyValue (sBoundRectName);
+ aValue >>= aBoundingBox;
+ bFoundBoundRect = true;
+ }
+ catch (beans::UnknownPropertyException const&)
+ {
+ // Handled below (bFoundBoundRect stays false).
+ }
+ }
+ else
+ SAL_WARN("svx", "no property BoundRect");
+ }
+ }
+
+ // Fallback when there is no BoundRect Property.
+ if ( ! bFoundBoundRect )
+ {
+ awt::Point aPosition (mxShape->getPosition());
+ awt::Size aSize (mxShape->getSize());
+ aBoundingBox = awt::Rectangle (
+ aPosition.X, aPosition.Y,
+ aSize.Width, aSize.Height);
+
+ // While BoundRects have absolute positions, the position returned
+ // by XPosition::getPosition is relative. Get the anchor position
+ // (usually not (0,0) for Writer shapes).
+ if (xSetInfo.is())
+ {
+ if (xSetInfo->hasPropertyByName (sAnchorPositionName))
+ {
+ uno::Any aPos = xSet->getPropertyValue (sAnchorPositionName);
+ awt::Point aAnchorPosition;
+ aPos >>= aAnchorPosition;
+ aBoundingBox.X += aAnchorPosition.X;
+ aBoundingBox.Y += aAnchorPosition.Y;
+ }
+ }
+ }
+
+ // Transform coordinates from internal to pixel.
+ if (maShapeTreeInfo.GetViewForwarder() == nullptr)
+ throw uno::RuntimeException (
+ "AccessibleShape has no valid view forwarder",
+ static_cast<uno::XWeak*>(this));
+ ::Size aPixelSize = maShapeTreeInfo.GetViewForwarder()->LogicToPixel (
+ ::Size (aBoundingBox.Width, aBoundingBox.Height));
+ ::Point aPixelPosition = maShapeTreeInfo.GetViewForwarder()->LogicToPixel (
+ ::Point (aBoundingBox.X, aBoundingBox.Y));
+
+ // Clip the shape's bounding box with the bounding box of its parent.
+ Reference<XAccessibleComponent> xParentComponent (
+ getAccessibleParent(), uno::UNO_QUERY);
+ if (xParentComponent.is())
+ {
+ // Make the coordinates relative to the parent.
+ awt::Point aParentLocation (xParentComponent->getLocationOnScreen());
+ int x = aPixelPosition.getX() - aParentLocation.X;
+ int y = aPixelPosition.getY() - aParentLocation.Y;
+
+ // Clip with parent (with coordinates relative to itself).
+ ::tools::Rectangle aBBox (
+ x, y, x + aPixelSize.getWidth(), y + aPixelSize.getHeight());
+ awt::Size aParentSize (xParentComponent->getSize());
+ ::tools::Rectangle aParentBBox (0,0, aParentSize.Width, aParentSize.Height);
+ aBBox = aBBox.GetIntersection (aParentBBox);
+ aBoundingBox = awt::Rectangle (
+ aBBox.Left(),
+ aBBox.Top(),
+ aBBox.getWidth(),
+ aBBox.getHeight());
+ }
+ else
+ {
+ SAL_INFO("svx", "parent does not support component");
+ aBoundingBox = awt::Rectangle (
+ aPixelPosition.getX(), aPixelPosition.getY(),
+ aPixelSize.getWidth(), aPixelSize.getHeight());
+ }
+ }
+
+ return aBoundingBox;
+}
+
+
+awt::Point SAL_CALL AccessibleShape::getLocation()
+{
+ ThrowIfDisposed ();
+ awt::Rectangle aBoundingBox (getBounds());
+ return awt::Point (aBoundingBox.X, aBoundingBox.Y);
+}
+
+
+awt::Point SAL_CALL AccessibleShape::getLocationOnScreen()
+{
+ ThrowIfDisposed ();
+
+ // Get relative position...
+ awt::Point aLocation (getLocation ());
+
+ // ... and add absolute position of the parent.
+ uno::Reference<XAccessibleComponent> xParentComponent (
+ getAccessibleParent(), uno::UNO_QUERY);
+ if (xParentComponent.is())
+ {
+ awt::Point aParentLocation (xParentComponent->getLocationOnScreen());
+ aLocation.X += aParentLocation.X;
+ aLocation.Y += aParentLocation.Y;
+ }
+ else
+ SAL_WARN("svx", "parent does not support XAccessibleComponent");
+ return aLocation;
+}
+
+
+awt::Size SAL_CALL AccessibleShape::getSize()
+{
+ ThrowIfDisposed ();
+ awt::Rectangle aBoundingBox (getBounds());
+ return awt::Size (aBoundingBox.Width, aBoundingBox.Height);
+}
+
+
+sal_Int32 SAL_CALL AccessibleShape::getForeground()
+{
+ ThrowIfDisposed ();
+ sal_Int32 nColor (0x0ffffffL);
+
+ try
+ {
+ uno::Reference<beans::XPropertySet> aSet (mxShape, uno::UNO_QUERY);
+ if (aSet.is())
+ {
+ uno::Any aColor;
+ aColor = aSet->getPropertyValue ("LineColor");
+ aColor >>= nColor;
+ }
+ }
+ catch (const css::beans::UnknownPropertyException &)
+ {
+ // Ignore exception and return default color.
+ }
+ return nColor;
+}
+
+
+sal_Int32 SAL_CALL AccessibleShape::getBackground()
+{
+ ThrowIfDisposed ();
+ Color nColor;
+
+ try
+ {
+ uno::Reference<beans::XPropertySet> aSet (mxShape, uno::UNO_QUERY);
+ if (aSet.is())
+ {
+ uno::Any aColor;
+ aColor = aSet->getPropertyValue ("FillColor");
+ aColor >>= nColor;
+ aColor = aSet->getPropertyValue ("FillTransparence");
+ short nTrans=0;
+ aColor >>= nTrans;
+ Color crBk(nColor);
+ if (nTrans == 0 )
+ {
+ crBk.SetAlpha(0);
+ }
+ else
+ {
+ nTrans = short(256 - nTrans / 100. * 256);
+ crBk.SetAlpha(255 - sal_uInt8(nTrans));
+ }
+ nColor = crBk;
+ }
+ }
+ catch (const css::beans::UnknownPropertyException &)
+ {
+ // Ignore exception and return default color.
+ }
+ return sal_Int32(nColor);
+}
+
+// XAccessibleEventBroadcaster
+void SAL_CALL AccessibleShape::addAccessibleEventListener (
+ const Reference<XAccessibleEventListener >& rxListener)
+{
+ if (rBHelper.bDisposed || rBHelper.bInDispose)
+ {
+ uno::Reference<uno::XInterface> xThis (
+ static_cast<lang::XComponent *>(this), uno::UNO_QUERY);
+ rxListener->disposing (lang::EventObject (xThis));
+ }
+ else
+ {
+ AccessibleContextBase::addAccessibleEventListener (rxListener);
+ if (mpText != nullptr)
+ mpText->AddEventListener (rxListener);
+ }
+}
+
+
+void SAL_CALL AccessibleShape::removeAccessibleEventListener (
+ const Reference<XAccessibleEventListener >& rxListener)
+{
+ AccessibleContextBase::removeAccessibleEventListener (rxListener);
+ if (mpText != nullptr)
+ mpText->RemoveEventListener (rxListener);
+}
+
+// XInterface
+css::uno::Any SAL_CALL
+ AccessibleShape::queryInterface (const css::uno::Type & rType)
+{
+ css::uno::Any aReturn = AccessibleContextBase::queryInterface (rType);
+ if ( ! aReturn.hasValue())
+ aReturn = ::cppu::queryInterface (rType,
+ static_cast<XAccessibleComponent*>(this),
+ static_cast<XAccessibleExtendedComponent*>(this),
+ static_cast< css::accessibility::XAccessibleSelection* >(this),
+ static_cast< css::accessibility::XAccessibleExtendedAttributes* >(this),
+ static_cast<document::XShapeEventListener*>(this),
+ static_cast<lang::XUnoTunnel*>(this),
+ static_cast<XAccessibleGroupPosition*>(this),
+ static_cast<XAccessibleHypertext*>(this)
+ );
+ return aReturn;
+}
+
+
+void SAL_CALL
+ AccessibleShape::acquire()
+ noexcept
+{
+ AccessibleContextBase::acquire ();
+}
+
+
+void SAL_CALL
+ AccessibleShape::release()
+ noexcept
+{
+ AccessibleContextBase::release ();
+}
+
+// XAccessibleSelection
+void SAL_CALL AccessibleShape::selectAccessibleChild( sal_Int32 )
+{
+}
+
+
+sal_Bool SAL_CALL AccessibleShape::isAccessibleChildSelected( sal_Int32 nChildIndex )
+{
+ uno::Reference<XAccessible> xAcc = getAccessibleChild( nChildIndex );
+ uno::Reference<XAccessibleContext> xContext;
+ if( xAcc.is() )
+ {
+ xContext = xAcc->getAccessibleContext();
+ }
+
+ if( xContext.is() )
+ {
+ if( xContext->getAccessibleRole() == AccessibleRole::PARAGRAPH )
+ {
+ uno::Reference< css::accessibility::XAccessibleText >
+ xText(xAcc, uno::UNO_QUERY);
+ if( xText.is() )
+ {
+ if( xText->getSelectionStart() >= 0 ) return true;
+ }
+ }
+ else if( xContext->getAccessibleRole() == AccessibleRole::SHAPE )
+ {
+ Reference< XAccessibleStateSet > pRState = xContext->getAccessibleStateSet();
+ if( !pRState.is() )
+ return false;
+
+ const uno::Sequence<short> aStates = pRState->getStates();
+ return std::find(aStates.begin(), aStates.end(), AccessibleStateType::SELECTED) != aStates.end();
+ }
+ }
+
+ return false;
+}
+
+
+void SAL_CALL AccessibleShape::clearAccessibleSelection( )
+{
+}
+
+
+void SAL_CALL AccessibleShape::selectAllAccessibleChildren( )
+{
+}
+
+
+sal_Int32 SAL_CALL AccessibleShape::getSelectedAccessibleChildCount()
+{
+ sal_Int32 nCount = 0;
+ sal_Int32 TotalCount = getAccessibleChildCount();
+ for( sal_Int32 i = 0; i < TotalCount; i++ )
+ if( isAccessibleChildSelected(i) ) nCount++;
+
+ return nCount;
+}
+
+
+Reference<XAccessible> SAL_CALL AccessibleShape::getSelectedAccessibleChild( sal_Int32 nSelectedChildIndex )
+{
+ if ( nSelectedChildIndex > getSelectedAccessibleChildCount() )
+ throw IndexOutOfBoundsException();
+ sal_Int32 i1, i2;
+ for( i1 = 0, i2 = 0; i1 < getAccessibleChildCount(); i1++ )
+ if( isAccessibleChildSelected(i1) )
+ {
+ if( i2 == nSelectedChildIndex )
+ return getAccessibleChild( i1 );
+ i2++;
+ }
+ return Reference<XAccessible>();
+}
+
+
+void SAL_CALL AccessibleShape::deselectAccessibleChild( sal_Int32 )
+{
+
+}
+
+// XAccessibleExtendedAttributes
+uno::Any SAL_CALL AccessibleShape::getExtendedAttributes()
+{
+ uno::Any strRet;
+ OUString style;
+ if( getAccessibleRole() != AccessibleRole::SHAPE ) return strRet;
+ if( m_pShape )
+ {
+ style = "style:" + GetStyle();
+ }
+ style += ";";
+ strRet <<= style;
+ return strRet;
+}
+
+// XServiceInfo
+OUString SAL_CALL
+ AccessibleShape::getImplementationName()
+{
+ return "AccessibleShape";
+}
+
+
+uno::Sequence<OUString> SAL_CALL
+ AccessibleShape::getSupportedServiceNames()
+{
+ ThrowIfDisposed ();
+ const css::uno::Sequence<OUString> vals { "com.sun.star.drawing.AccessibleShape" };
+ return comphelper::concatSequences(AccessibleContextBase::getSupportedServiceNames(), vals);
+}
+
+// XTypeProvider
+uno::Sequence<uno::Type> SAL_CALL
+ AccessibleShape::getTypes()
+{
+ ThrowIfDisposed ();
+ // Get list of types from the context base implementation, ...
+ uno::Sequence<uno::Type> aTypeList (AccessibleContextBase::getTypes());
+ // ... get list of types from component base implementation, ...
+ uno::Sequence<uno::Type> aComponentTypeList (AccessibleComponentBase::getTypes());
+ // ... define local types
+ uno::Sequence<uno::Type> localTypesList = {
+ cppu::UnoType<lang::XEventListener>::get(),
+ cppu::UnoType<document::XEventListener>::get(),
+ cppu::UnoType<lang::XUnoTunnel>::get()
+ };
+
+ return comphelper::concatSequences(aTypeList, aComponentTypeList, localTypesList);
+}
+
+// lang::XEventListener
+/** Disposing calls are accepted only from the model: Just reset the
+ reference to the model in the shape tree info. Otherwise this object
+ remains functional.
+*/
+void AccessibleShape::disposing (const lang::EventObject& aEvent)
+{
+ SolarMutexGuard aSolarGuard;
+ ::osl::MutexGuard aGuard (m_aMutex);
+
+ try
+ {
+ if (aEvent.Source == maShapeTreeInfo.GetModelBroadcaster())
+ {
+ // Remove reference to model broadcaster to allow it to pass
+ // away.
+ maShapeTreeInfo.SetModelBroadcaster(nullptr);
+ }
+
+ }
+ catch (uno::RuntimeException const&)
+ {
+ TOOLS_WARN_EXCEPTION("svx", "caught exception while disposing");
+ }
+}
+
+// document::XShapeEventListener
+void SAL_CALL
+ AccessibleShape::notifyShapeEvent (const document::EventObject& rEventObject)
+{
+ if (rEventObject.EventName != "ShapeModified")
+ return;
+
+ //Need to update text children when receiving ShapeModified hint when exiting edit mode for text box
+ if (mpText)
+ mpText->UpdateChildren();
+
+
+ // Some property of a shape has been modified. Send an event
+ // that indicates a change of the visible data to all listeners.
+ CommitChange (
+ AccessibleEventId::VISIBLE_DATA_CHANGED,
+ uno::Any(),
+ uno::Any());
+
+ // Name and Description may have changed. Update the local
+ // values accordingly.
+ UpdateNameAndDescription();
+}
+
+// lang::XUnoTunnel
+UNO3_GETIMPLEMENTATION_IMPL(AccessibleShape)
+
+// IAccessibleViewForwarderListener
+void AccessibleShape::ViewForwarderChanged()
+{
+ // Inform all listeners that the graphical representation (i.e. size
+ // and/or position) of the shape has changed.
+ CommitChange (AccessibleEventId::VISIBLE_DATA_CHANGED,
+ uno::Any(),
+ uno::Any());
+
+ // Tell children manager of the modified view forwarder.
+ if (mpChildrenManager != nullptr)
+ mpChildrenManager->ViewForwarderChanged();
+
+ // update our children that our screen position might have changed
+ if( mpText )
+ mpText->UpdateChildren();
+}
+
+// protected internal
+// Set this object's name if is different to the current name.
+OUString AccessibleShape::CreateAccessibleBaseName()
+{
+ return ShapeTypeHandler::CreateAccessibleBaseName( mxShape );
+}
+
+
+OUString AccessibleShape::CreateAccessibleName()
+{
+ return GetFullAccessibleName(this);
+}
+
+OUString AccessibleShape::GetFullAccessibleName (AccessibleShape *shape)
+{
+ OUString sName (shape->CreateAccessibleBaseName());
+ // Append the shape's index to the name to disambiguate between shapes
+ // of the same type. If such an index where not given to the
+ // constructor then use the z-order instead. If even that does not exist
+ // we throw an exception.
+ OUString nameStr;
+ if (shape->m_pShape)
+ nameStr = shape->m_pShape->GetName();
+ if (nameStr.isEmpty())
+ {
+ sName += " ";
+ }
+ else
+ {
+ sName = nameStr;
+ }
+
+ //If the new produced name if not the same with last,notify name changed
+ //Event
+ if (aAccName != sName && !aAccName.isEmpty())
+ {
+ uno::Any aOldValue, aNewValue;
+ aOldValue <<= aAccName;
+ aNewValue <<= sName;
+ CommitChange(
+ AccessibleEventId::NAME_CHANGED,
+ aNewValue,
+ aOldValue);
+ }
+ aAccName = sName;
+ return sName;
+}
+
+// protected
+void AccessibleShape::disposing()
+{
+ SolarMutexGuard aSolarGuard;
+ ::osl::MutexGuard aGuard (m_aMutex);
+
+ // Make sure to send an event that this object loses the focus in the
+ // case that it has the focus.
+ ::utl::AccessibleStateSetHelper* pStateSet = mxStateSet.get();
+ if (pStateSet != nullptr)
+ pStateSet->RemoveState (AccessibleStateType::FOCUSED);
+
+ // Unregister from model.
+ if (mxShape.is() && maShapeTreeInfo.GetModelBroadcaster().is())
+ maShapeTreeInfo.GetModelBroadcaster()->removeShapeEventListener(mxShape,
+ static_cast<document::XShapeEventListener*>(this));
+
+ // Release the child containers.
+ if (mpChildrenManager != nullptr)
+ {
+ mpChildrenManager.reset();
+ }
+ if (mpText != nullptr)
+ {
+ mpText->Dispose();
+ mpText.reset();
+ }
+
+ // Cleanup. Remove references to objects to allow them to be
+ // destroyed.
+ mxShape = nullptr;
+ maShapeTreeInfo.dispose();
+
+ // Call base classes.
+ AccessibleContextBase::dispose ();
+}
+
+sal_Int32 SAL_CALL
+ AccessibleShape::getAccessibleIndexInParent()
+{
+ ThrowIfDisposed ();
+ // Use a simple but slow solution for now. Optimize later.
+
+ sal_Int32 nIndex = m_nIndexInParent;
+ if ( -1 == nIndex )
+ nIndex = AccessibleContextBase::getAccessibleIndexInParent();
+ return nIndex;
+}
+
+
+void AccessibleShape::UpdateNameAndDescription()
+{
+ // Ignore missing title, name, or description. There are fallbacks for
+ // them.
+ try
+ {
+ Reference<beans::XPropertySet> xSet (mxShape, uno::UNO_QUERY_THROW);
+
+ // Get the accessible name.
+ OUString sString = GetOptionalProperty(xSet, "Title");
+ if (!sString.isEmpty())
+ {
+ SetAccessibleName(sString, AccessibleContextBase::FromShape);
+ }
+ else
+ {
+ sString = GetOptionalProperty(xSet, "Name");
+ if (!sString.isEmpty())
+ SetAccessibleName(sString, AccessibleContextBase::FromShape);
+ }
+
+ // Get the accessible description.
+ sString = GetOptionalProperty(xSet, "Description");
+ if (!sString.isEmpty())
+ SetAccessibleDescription(sString, AccessibleContextBase::FromShape);
+ }
+ catch (uno::RuntimeException&)
+ {
+ }
+}
+
+// Return this object's role.
+sal_Int16 SAL_CALL AccessibleShape::getAccessibleRole()
+{
+ sal_Int16 nAccessibleRole = AccessibleRole::SHAPE ;
+ switch (ShapeTypeHandler::Instance().GetTypeId (mxShape))
+ {
+ case DRAWING_GRAPHIC_OBJECT:
+ nAccessibleRole = AccessibleRole::GRAPHIC ; break;
+ case DRAWING_OLE:
+ nAccessibleRole = AccessibleRole::EMBEDDED_OBJECT ; break;
+
+ default:
+ nAccessibleRole = AccessibleContextBase::getAccessibleRole();
+ break;
+ }
+
+ return nAccessibleRole;
+}
+
+namespace {
+
+//sort the drawing objects from up to down, from left to right
+struct XShapePosCompareHelper
+{
+ bool operator() ( const uno::Reference<drawing::XShape>& xshape1,
+ const uno::Reference<drawing::XShape>& xshape2 ) const
+ {
+ SdrObject* pObj1 = SdrObject::getSdrObjectFromXShape(xshape1);
+ SdrObject* pObj2 = SdrObject::getSdrObjectFromXShape(xshape2);
+ if(pObj1 && pObj2)
+ return pObj1->GetOrdNum() < pObj2->GetOrdNum();
+ else
+ return false;
+ }
+};
+
+}
+//end of group position
+
+// XAccessibleGroupPosition
+uno::Sequence< sal_Int32 > SAL_CALL
+AccessibleShape::getGroupPosition( const uno::Any& )
+{
+ // we will return the:
+ // [0] group level
+ // [1] similar items counts in the group
+ // [2] the position of the object in the group
+ uno::Sequence< sal_Int32 > aRet{ 0, 0, 0 };
+
+ css::uno::Reference<XAccessible> xParent = getAccessibleParent();
+ if (!xParent.is())
+ {
+ return aRet;
+ }
+ SdrObject *pObj = SdrObject::getSdrObjectFromXShape(mxShape);
+
+
+ if(pObj == nullptr )
+ {
+ return aRet;
+ }
+
+ // Compute object's group level.
+ sal_Int32 nGroupLevel = 0;
+ SdrObject * pUper = pObj->getParentSdrObjectFromSdrObject();
+ while( pUper )
+ {
+ ++nGroupLevel;
+ pUper = pUper->getParentSdrObjectFromSdrObject();
+ }
+
+ css::uno::Reference<XAccessibleContext> xParentContext = xParent->getAccessibleContext();
+ if( xParentContext->getAccessibleRole() == AccessibleRole::DOCUMENT ||
+ xParentContext->getAccessibleRole() == AccessibleRole::DOCUMENT_PRESENTATION ||
+ xParentContext->getAccessibleRole() == AccessibleRole::DOCUMENT_SPREADSHEET ||
+ xParentContext->getAccessibleRole() == AccessibleRole::DOCUMENT_TEXT )//Document
+ {
+ Reference< XAccessibleGroupPosition > xGroupPosition( xParent,uno::UNO_QUERY );
+ if ( xGroupPosition.is() )
+ {
+ aRet = xGroupPosition->getGroupPosition( uno::Any( getAccessibleContext() ) );
+ }
+ return aRet;
+ }
+ if (xParentContext->getAccessibleRole() != AccessibleRole::SHAPE)
+ {
+ return aRet;
+ }
+
+ SdrObjList *pGrpList = nullptr;
+ if( pObj->getParentSdrObjectFromSdrObject() )
+ pGrpList = pObj->getParentSdrObjectFromSdrObject()->GetSubList();
+ else
+ return aRet;
+
+ std::vector< uno::Reference<drawing::XShape> > vXShapes;
+ if (pGrpList)
+ {
+ const size_t nObj = pGrpList->GetObjCount();
+ for(size_t i = 0 ; i < nObj ; ++i)
+ {
+ SdrObject *pSubObj = pGrpList->GetObj(i);
+ if (pSubObj &&
+ xParentContext->getAccessibleChild(i)->getAccessibleContext()->getAccessibleRole() != AccessibleRole::GROUP_BOX)
+ {
+ vXShapes.push_back( GetXShapeForSdrObject(pSubObj) );
+ }
+ }
+ }
+
+ std::sort( vXShapes.begin(), vXShapes.end(), XShapePosCompareHelper() );
+
+ //get the index of the selected object in the group
+ //we start counting position from 1
+ sal_Int32 nPos = 1;
+ for ( const auto& rpShape : vXShapes )
+ {
+ if ( rpShape.get() == mxShape.get() )
+ {
+ sal_Int32* pArray = aRet.getArray();
+ pArray[0] = nGroupLevel;
+ pArray[1] = vXShapes.size();
+ pArray[2] = nPos;
+ break;
+ }
+ nPos++;
+ }
+
+ return aRet;
+}
+
+OUString AccessibleShape::getObjectLink( const uno::Any& )
+{
+ OUString aRet;
+
+ SdrObject *pObj = SdrObject::getSdrObjectFromXShape(mxShape);
+ if(pObj == nullptr )
+ {
+ return aRet;
+ }
+ if (maShapeTreeInfo.GetDocumentWindow().is())
+ {
+ Reference< XAccessibleGroupPosition > xGroupPosition( maShapeTreeInfo.GetDocumentWindow(), uno::UNO_QUERY );
+ if (xGroupPosition.is())
+ {
+ aRet = xGroupPosition->getObjectLink( uno::Any( getAccessibleContext() ) );
+ }
+ }
+ return aRet;
+}
+
+// XAccessibleHypertext
+sal_Int32 SAL_CALL AccessibleShape::getHyperLinkCount()
+{
+ // MT: Introduced with IA2 CWS, but SvxAccessibleHyperlink was redundant to svx::AccessibleHyperlink which we introduced meanwhile.
+ // Code need to be adapted...
+ return 0;
+
+ /*
+ SvxAccessibleHyperlink* pLink = new SvxAccessibleHyperlink(m_pShape,this);
+ if (pLink->IsValidHyperlink())
+ return 1;
+ else
+ return 0;
+ */
+}
+uno::Reference< XAccessibleHyperlink > SAL_CALL
+ AccessibleShape::getHyperLink( sal_Int32 )
+{
+ uno::Reference< XAccessibleHyperlink > xRet;
+ // MT: Introduced with IA2 CWS, but SvxAccessibleHyperlink was redundant to svx::AccessibleHyperlink which we introduced meanwhile.
+ // Code need to be adapted...
+ /*
+ SvxAccessibleHyperlink* pLink = new SvxAccessibleHyperlink(m_pShape,this);
+ if (pLink->IsValidHyperlink())
+ xRet = pLink;
+ if( !xRet.is() )
+ throw css::lang::IndexOutOfBoundsException();
+ */
+ return xRet;
+}
+sal_Int32 SAL_CALL AccessibleShape::getHyperLinkIndex( sal_Int32 )
+{
+ return 0;
+}
+// XAccessibleText
+sal_Int32 SAL_CALL AccessibleShape::getCaretPosition( ){return 0;}
+sal_Bool SAL_CALL AccessibleShape::setCaretPosition( sal_Int32 ){return false;}
+sal_Unicode SAL_CALL AccessibleShape::getCharacter( sal_Int32 ){return 0;}
+css::uno::Sequence< css::beans::PropertyValue > SAL_CALL AccessibleShape::getCharacterAttributes( sal_Int32, const css::uno::Sequence< OUString >& )
+{
+ uno::Sequence< css::beans::PropertyValue > aValues(0);
+ return aValues;
+}
+css::awt::Rectangle SAL_CALL AccessibleShape::getCharacterBounds( sal_Int32 )
+{
+ return css::awt::Rectangle(0, 0, 0, 0 );
+}
+sal_Int32 SAL_CALL AccessibleShape::getCharacterCount( ){return 0;}
+sal_Int32 SAL_CALL AccessibleShape::getIndexAtPoint( const css::awt::Point& ){return 0;}
+OUString SAL_CALL AccessibleShape::getSelectedText( ){return OUString();}
+sal_Int32 SAL_CALL AccessibleShape::getSelectionStart( ){return 0;}
+sal_Int32 SAL_CALL AccessibleShape::getSelectionEnd( ){return 0;}
+sal_Bool SAL_CALL AccessibleShape::setSelection( sal_Int32, sal_Int32 ){return true;}
+OUString SAL_CALL AccessibleShape::getText( ){return OUString();}
+OUString SAL_CALL AccessibleShape::getTextRange( sal_Int32, sal_Int32 ){return OUString();}
+css::accessibility::TextSegment SAL_CALL AccessibleShape::getTextAtIndex( sal_Int32, sal_Int16 )
+{
+ css::accessibility::TextSegment aResult;
+ return aResult;
+}
+css::accessibility::TextSegment SAL_CALL AccessibleShape::getTextBeforeIndex( sal_Int32, sal_Int16 )
+{
+ css::accessibility::TextSegment aResult;
+ return aResult;
+}
+css::accessibility::TextSegment SAL_CALL AccessibleShape::getTextBehindIndex( sal_Int32, sal_Int16 )
+{
+ css::accessibility::TextSegment aResult;
+ return aResult;
+}
+sal_Bool SAL_CALL AccessibleShape::copyText( sal_Int32, sal_Int32 ){return true;}
+sal_Bool SAL_CALL AccessibleShape::scrollSubstringTo( sal_Int32, sal_Int32, AccessibleScrollType ){return false;}
+
+} // end of namespace accessibility
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/accessibility/AccessibleShapeInfo.cxx b/svx/source/accessibility/AccessibleShapeInfo.cxx
new file mode 100644
index 000000000..7a70ca4d7
--- /dev/null
+++ b/svx/source/accessibility/AccessibleShapeInfo.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 <svx/AccessibleShapeInfo.hxx>
+
+
+namespace accessibility {
+
+AccessibleShapeInfo::AccessibleShapeInfo (
+ const css::uno::Reference<css::drawing::XShape>& rxShape,
+ const css::uno::Reference<css::accessibility::XAccessible>& rxParent,
+ IAccessibleParent* pChildrenManager)
+ : mxShape (rxShape),
+ mxParent (rxParent),
+ mpChildrenManager (pChildrenManager)
+{
+ // empty.
+}
+
+
+AccessibleShapeInfo::AccessibleShapeInfo (
+ const css::uno::Reference<css::drawing::XShape>& rxShape,
+ const css::uno::Reference<css::accessibility::XAccessible>& rxParent)
+ : mxShape (rxShape),
+ mxParent (rxParent),
+ mpChildrenManager (nullptr)
+{
+ // empty.
+}
+
+AccessibleShapeInfo::AccessibleShapeInfo (const AccessibleShapeInfo &rOther)
+ : mxShape (rOther.mxShape),
+ mxParent (rOther.mxParent),
+ mpChildrenManager (rOther.mpChildrenManager)
+{
+ // empty.
+}
+
+
+AccessibleShapeInfo::~AccessibleShapeInfo()
+{
+ // empty.
+}
+
+} // end of namespace accessibility.
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/accessibility/AccessibleShapeTreeInfo.cxx b/svx/source/accessibility/AccessibleShapeTreeInfo.cxx
new file mode 100644
index 000000000..df6136f94
--- /dev/null
+++ b/svx/source/accessibility/AccessibleShapeTreeInfo.cxx
@@ -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 .
+ */
+
+
+#include <svx/AccessibleShapeTreeInfo.hxx>
+#include <vcl/svapp.hxx>
+#include <vcl/window.hxx>
+
+using namespace ::com::sun::star;
+using namespace ::com::sun::star::accessibility;
+using ::com::sun::star::uno::Reference;
+
+namespace accessibility {
+
+AccessibleShapeTreeInfo::AccessibleShapeTreeInfo()
+ : mpView (nullptr),
+ mpWindow (nullptr),
+ mpViewForwarder (nullptr)
+{
+ // Empty.
+}
+
+
+AccessibleShapeTreeInfo::AccessibleShapeTreeInfo (const AccessibleShapeTreeInfo& rInfo)
+ : mxDocumentWindow (rInfo.mxDocumentWindow),
+ mxModelBroadcaster (rInfo.mxModelBroadcaster),
+ mpView (rInfo.mpView),
+ mxController (rInfo.mxController),
+ mpWindow (rInfo.mpWindow),
+ mpViewForwarder (rInfo.mpViewForwarder)
+{
+ // Empty.
+}
+
+void AccessibleShapeTreeInfo::dispose()
+{
+ mxDocumentWindow.clear();
+ mxModelBroadcaster.clear();
+ mpView = nullptr;
+ mxController.clear();
+ mpWindow.reset();
+ mpViewForwarder = nullptr;
+}
+
+AccessibleShapeTreeInfo& AccessibleShapeTreeInfo::operator= (const AccessibleShapeTreeInfo& rInfo)
+{
+ if ( this != &rInfo )
+ {
+ mxDocumentWindow = rInfo.mxDocumentWindow;
+ mxModelBroadcaster = rInfo.mxModelBroadcaster;
+ mpView = rInfo.mpView;
+ mxController = rInfo.mxController;
+ mpWindow = rInfo.mpWindow;
+ mpViewForwarder = rInfo.mpViewForwarder;
+ }
+ return *this;
+}
+
+AccessibleShapeTreeInfo::~AccessibleShapeTreeInfo()
+{
+ SolarMutexGuard g;
+ mpWindow.reset();
+}
+
+void AccessibleShapeTreeInfo::SetDocumentWindow (
+ const Reference<XAccessibleComponent>& rxDocumentWindow)
+{
+ if (mxDocumentWindow != rxDocumentWindow)
+ mxDocumentWindow = rxDocumentWindow;
+}
+
+void AccessibleShapeTreeInfo::SetModelBroadcaster (
+ const Reference<document::XShapeEventBroadcaster>& rxModelBroadcaster)
+{
+ mxModelBroadcaster = rxModelBroadcaster;
+}
+
+void AccessibleShapeTreeInfo::SetSdrView (SdrView* pView)
+{
+ mpView = pView;
+}
+
+void AccessibleShapeTreeInfo::SetController (
+ const Reference<frame::XController>& rxController)
+{
+ mxController = rxController;
+}
+
+void AccessibleShapeTreeInfo::SetWindow(vcl::Window* pDevice)
+{
+ mpWindow = pDevice;
+}
+
+void AccessibleShapeTreeInfo::SetViewForwarder (const IAccessibleViewForwarder* pViewForwarder)
+{
+ mpViewForwarder = pViewForwarder;
+}
+
+} // end of namespace accessibility
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/accessibility/AccessibleTextEventQueue.cxx b/svx/source/accessibility/AccessibleTextEventQueue.cxx
new file mode 100644
index 000000000..a39123c45
--- /dev/null
+++ b/svx/source/accessibility/AccessibleTextEventQueue.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 <memory>
+#include "AccessibleTextEventQueue.hxx"
+
+#include <editeng/unoedhlp.hxx>
+#include <svx/svdmodel.hxx>
+#include <svx/svdpntv.hxx>
+
+namespace accessibility
+{
+
+
+ // EventQueue implementation
+
+
+ AccessibleTextEventQueue::AccessibleTextEventQueue()
+ {
+ }
+
+ AccessibleTextEventQueue::~AccessibleTextEventQueue()
+ {
+ Clear();
+ }
+
+ void AccessibleTextEventQueue::Append( const SdrHint& rHint )
+ {
+ // only enqueue the events we actually care about in
+ // AccessibleTextHelper_Impl::ProcessQueue(), because
+ // the cost of some events adds up.
+ auto eKind = rHint.GetKind();
+ if (eKind == SdrHintKind::BeginEdit
+ || eKind == SdrHintKind::EndEdit)
+ maEventQueue.push_back( new SdrHint( rHint ) );
+ }
+
+ void AccessibleTextEventQueue::Append( const TextHint& rHint )
+ {
+ maEventQueue.push_back( new TextHint( rHint ) );
+ }
+
+ void AccessibleTextEventQueue::Append( const SvxViewChangedHint& rHint )
+ {
+ maEventQueue.push_back( new SvxViewChangedHint( rHint ) );
+ }
+
+ void AccessibleTextEventQueue::Append( const SvxEditSourceHint& rHint )
+ {
+ maEventQueue.push_back( new SvxEditSourceHint( rHint ) );
+ }
+
+ ::std::unique_ptr< SfxHint > AccessibleTextEventQueue::PopFront()
+ {
+ ::std::unique_ptr< SfxHint > aRes( *(maEventQueue.begin()) );
+ maEventQueue.pop_front();
+ return aRes;
+ }
+
+ bool AccessibleTextEventQueue::IsEmpty() const
+ {
+ return maEventQueue.empty();
+ }
+
+ void AccessibleTextEventQueue::Clear()
+ {
+ // clear queue
+ for( auto p : maEventQueue)
+ delete p;
+ maEventQueue.clear();
+ }
+
+} // end of namespace accessibility
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/accessibility/AccessibleTextEventQueue.hxx b/svx/source/accessibility/AccessibleTextEventQueue.hxx
new file mode 100644
index 000000000..23dbf9faa
--- /dev/null
+++ b/svx/source/accessibility/AccessibleTextEventQueue.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_SVX_SOURCE_ACCESSIBILITY_ACCESSIBLETEXTEVENTQUEUE_HXX
+#define INCLUDED_SVX_SOURCE_ACCESSIBILITY_ACCESSIBLETEXTEVENTQUEUE_HXX
+
+#include <memory>
+#include <deque>
+#include <algorithm>
+
+class SfxHint;
+class SdrHint;
+class TextHint;
+class SvxViewChangedHint;
+class SvxEditSourceHint;
+
+namespace accessibility
+{
+ /** This class handles the notification events for the
+ AccessibleTextHelper class.
+
+ For various reasons, we cannot process EditEngine events as
+ they arrive, but have to queue and handle them in a batch.
+ */
+ class AccessibleTextEventQueue
+ {
+ public:
+ typedef ::std::deque< SfxHint* > EventQueue;
+
+ AccessibleTextEventQueue();
+ ~AccessibleTextEventQueue();
+
+ /// Append event to end of queue
+ void Append( const SdrHint& rHint );
+ /// Append event to end of queue
+ void Append( const TextHint& rHint );
+ /// Append event to end of queue
+ void Append( const SvxViewChangedHint& rHint );
+ /// Append event to end of queue
+ void Append( const SvxEditSourceHint& rHint );
+
+ /** Pop first queue element
+
+ return first queue element, ownership transfers to caller
+ */
+ ::std::unique_ptr< SfxHint > PopFront();
+
+ /** Apply functor to every queue member
+
+ @param rFunctor
+ Functor to apply. Functor receives queue element as
+ parameter: void func( const SfxHint* );
+ */
+ template < typename Functor > void ForEach( Functor& rFunctor ) const
+ {
+ // #109864# Make sure results are put back into rFunctor
+ rFunctor = ::std::for_each( maEventQueue.begin(), maEventQueue.end(), rFunctor );
+ }
+
+ /// Query whether queue is empty
+ bool IsEmpty() const;
+
+ /// Clear event queue
+ void Clear();
+
+ private:
+ EventQueue maEventQueue;
+ };
+
+} // end of namespace accessibility
+
+#endif // INCLUDED_SVX_SOURCE_ACCESSIBILITY_ACCESSIBLETEXTEVENTQUEUE_HXX
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/accessibility/AccessibleTextHelper.cxx b/svx/source/accessibility/AccessibleTextHelper.cxx
new file mode 100644
index 000000000..809bd25d3
--- /dev/null
+++ b/svx/source/accessibility/AccessibleTextHelper.cxx
@@ -0,0 +1,1779 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <sal/config.h>
+
+#include <cstdlib>
+#include <memory>
+#include <mutex>
+#include <utility>
+#include <algorithm>
+#include <sal/log.hxx>
+#include <com/sun/star/uno/Any.hxx>
+#include <com/sun/star/uno/Reference.hxx>
+#include <com/sun/star/awt/Point.hpp>
+#include <com/sun/star/awt/Rectangle.hpp>
+#include <com/sun/star/lang/IndexOutOfBoundsException.hpp>
+#include <com/sun/star/accessibility/AccessibleEventId.hpp>
+#include <com/sun/star/accessibility/XAccessible.hpp>
+#include <com/sun/star/accessibility/XAccessibleContext.hpp>
+#include <com/sun/star/accessibility/XAccessibleComponent.hpp>
+#include <com/sun/star/accessibility/AccessibleStateType.hpp>
+#include <comphelper/accessibleeventnotifier.hxx>
+#include <vcl/svapp.hxx>
+#include <vcl/textdata.hxx>
+#include <vcl/unohelp.hxx>
+
+
+// Project-local header
+
+
+#include "AccessibleTextEventQueue.hxx"
+#include <svx/AccessibleTextHelper.hxx>
+
+#include <editeng/unoedhlp.hxx>
+#include <editeng/unoedprx.hxx>
+#include <editeng/AccessibleParaManager.hxx>
+#include <editeng/AccessibleEditableTextPara.hxx>
+#include <svx/svdmodel.hxx>
+#include <svx/svdpntv.hxx>
+#include <cell.hxx>
+#include "../table/accessiblecell.hxx"
+#include <editeng/editdata.hxx>
+#include <tools/debug.hxx>
+#include <tools/diagnose_ex.h>
+
+using namespace ::com::sun::star;
+using namespace ::com::sun::star::accessibility;
+
+namespace accessibility
+{
+
+// AccessibleTextHelper_Impl declaration
+
+ template < typename first_type, typename second_type >
+ static ::std::pair< first_type, second_type > makeSortedPair( first_type first,
+ second_type second )
+ {
+ if( first > second )
+ return ::std::make_pair( second, first );
+ else
+ return ::std::make_pair( first, second );
+ }
+
+ class AccessibleTextHelper_Impl : public SfxListener
+ {
+ public:
+ typedef ::std::vector< sal_Int16 > VectorOfStates;
+
+ // receive pointer to our frontend class and view window
+ AccessibleTextHelper_Impl();
+ virtual ~AccessibleTextHelper_Impl() override;
+
+ // XAccessibleContext child handling methods
+ sal_Int32 getAccessibleChildCount() const;
+ uno::Reference< XAccessible > getAccessibleChild( sal_Int32 i );
+
+ // XAccessibleEventBroadcaster child related methods
+ void addAccessibleEventListener( const uno::Reference< XAccessibleEventListener >& xListener );
+ void removeAccessibleEventListener( const uno::Reference< XAccessibleEventListener >& xListener );
+
+ // XAccessibleComponent child related methods
+ uno::Reference< XAccessible > getAccessibleAtPoint( const awt::Point& aPoint );
+
+ SvxEditSourceAdapter& GetEditSource() const;
+
+ void SetEditSource( ::std::unique_ptr< SvxEditSource > && pEditSource );
+
+ void SetEventSource( const uno::Reference< XAccessible >& rInterface )
+ {
+ mxFrontEnd = rInterface;
+ }
+
+ void SetOffset( const Point& );
+ Point GetOffset() const
+ {
+ std::scoped_lock aGuard( maMutex ); Point aPoint( maOffset );
+ return aPoint;
+ }
+
+ void SetStartIndex( sal_Int32 nOffset );
+ sal_Int32 GetStartIndex() const
+ {
+ // Strictly correct only with locked solar mutex, // but
+ // here we rely on the fact that sal_Int32 access is
+ // atomic
+ return mnStartIndex;
+ }
+
+ void SetAdditionalChildStates( VectorOfStates&& rChildStates );
+
+ void Dispose();
+
+ // do NOT hold object mutex when calling this! Danger of deadlock
+ void FireEvent( const sal_Int16 nEventId, const uno::Any& rNewValue = uno::Any(), const uno::Any& rOldValue = uno::Any() ) const;
+ void FireEvent( const AccessibleEventObject& rEvent ) const;
+
+ void SetFocus( bool bHaveFocus );
+ bool HaveFocus() const
+ {
+ // No locking of solar mutex here, since we rely on the fact
+ // that sal_Bool access is atomic
+ return mbThisHasFocus;
+ }
+ void SetChildFocus( sal_Int32 nChild, bool bHaveFocus );
+ void SetShapeFocus( bool bHaveFocus );
+ void ChangeChildFocus( sal_Int32 nNewChild );
+
+#ifdef DBG_UTIL
+ void CheckInvariants() const;
+#endif
+
+ // checks all children for visibility, throws away invisible ones
+ void UpdateVisibleChildren( bool bBroadcastEvents=true );
+
+ // check all children for changes in position and size
+ void UpdateBoundRect();
+
+ // calls SetSelection on the forwarder and updates maLastSelection
+ // cache.
+ void UpdateSelection();
+
+ private:
+
+ // Process event queue
+ void ProcessQueue();
+
+ // syntactic sugar for FireEvent
+ void GotPropertyEvent( const uno::Any& rNewValue, const sal_Int16 nEventId ) const { FireEvent( nEventId, rNewValue ); }
+
+ // shutdown usage of current edit source on myself and the children.
+ void ShutdownEditSource();
+
+ void ParagraphsMoved( sal_Int32 nFirst, sal_Int32 nMiddle, sal_Int32 nLast );
+
+ virtual void Notify( SfxBroadcaster& rBC, const SfxHint& rHint ) override;
+
+ comphelper::AccessibleEventNotifier::TClientId getNotifierClientId() const { return mnNotifierClientId; }
+
+ // lock solar mutex before
+ SvxTextForwarder& GetTextForwarder() const;
+ // lock solar mutex before
+ SvxViewForwarder& GetViewForwarder() const;
+ // lock solar mutex before
+ SvxEditViewForwarder& GetEditViewForwarder() const;
+
+ // are we in edit mode?
+ bool IsActive() const;
+
+ // our frontend class (the one implementing the actual
+ // interface). That's not necessarily the one containing the impl
+ // pointer!
+ uno::Reference< XAccessible > mxFrontEnd;
+
+ // a wrapper for the text forwarders (guarded by solar mutex)
+ mutable SvxEditSourceAdapter maEditSource;
+
+ // store last selection (to correctly report selection changes, guarded by solar mutex)
+ ESelection maLastSelection;
+
+ // cache range of visible children (guarded by solar mutex)
+ sal_Int32 mnFirstVisibleChild;
+ sal_Int32 mnLastVisibleChild;
+
+ // offset to add to all our children (unguarded, relying on
+ // the fact that sal_Int32 access is atomic)
+ sal_Int32 mnStartIndex;
+
+ // the object handling our children (guarded by solar mutex)
+ ::accessibility::AccessibleParaManager maParaManager;
+
+ // Queued events from Notify() (guarded by solar mutex)
+ AccessibleTextEventQueue maEventQueue;
+
+ // spin lock to prevent notify in notify (guarded by solar mutex)
+ bool mbInNotify;
+
+ // whether the object or its children has the focus set (guarded by solar mutex)
+ bool mbGroupHasFocus;
+
+ // whether we (this object) has the focus set (guarded by solar mutex)
+ bool mbThisHasFocus;
+
+ mutable std::mutex maMutex;
+
+ /// our current offset to the containing shape/cell (guarded by maMutex)
+ Point maOffset;
+
+ /// client Id from AccessibleEventNotifier
+ comphelper::AccessibleEventNotifier::TClientId mnNotifierClientId;
+ static constexpr comphelper::AccessibleEventNotifier::TClientId snNotifierClientRevoked
+ = std::numeric_limits<comphelper::AccessibleEventNotifier::TClientId>::max();
+ };
+
+ AccessibleTextHelper_Impl::AccessibleTextHelper_Impl() :
+ maLastSelection( EE_PARA_NOT_FOUND,EE_INDEX_NOT_FOUND,EE_PARA_NOT_FOUND,EE_INDEX_NOT_FOUND ),
+ mnFirstVisibleChild( -1 ),
+ mnLastVisibleChild( -2 ),
+ mnStartIndex( 0 ),
+ mbInNotify( false ),
+ mbGroupHasFocus( false ),
+ mbThisHasFocus( false ),
+ maOffset(0,0),
+ // well, that's strictly exception safe, though not really
+ // robust. We rely on the fact that this member is constructed
+ // last, and that the constructor body is empty, thus no
+ // chance for exceptions once the Id is fetched. Nevertheless,
+ // normally should employ RAII here...
+ mnNotifierClientId(::comphelper::AccessibleEventNotifier::registerClient())
+ {
+ SAL_INFO("svx", "received ID: " << mnNotifierClientId );
+ }
+
+ AccessibleTextHelper_Impl::~AccessibleTextHelper_Impl()
+ {
+ SolarMutexGuard aGuard;
+
+ try
+ {
+ // call Dispose here, too, since we've some resources not
+ // automatically freed otherwise
+ Dispose();
+ }
+ catch( const uno::Exception& ) {}
+ }
+
+ SvxTextForwarder& AccessibleTextHelper_Impl::GetTextForwarder() const
+ {
+ if( !maEditSource.IsValid() )
+ throw uno::RuntimeException("Unknown edit source", mxFrontEnd);
+
+ SvxTextForwarder* pTextForwarder = maEditSource.GetTextForwarder();
+
+ if( !pTextForwarder )
+ throw uno::RuntimeException("Unable to fetch text forwarder, model might be dead", mxFrontEnd);
+
+ if( !pTextForwarder->IsValid() )
+ throw uno::RuntimeException("Text forwarder is invalid, model might be dead", mxFrontEnd);
+
+ return *pTextForwarder;
+ }
+
+ SvxViewForwarder& AccessibleTextHelper_Impl::GetViewForwarder() const
+ {
+ if( !maEditSource.IsValid() )
+ throw uno::RuntimeException("Unknown edit source", mxFrontEnd);
+
+ SvxViewForwarder* pViewForwarder = maEditSource.GetViewForwarder();
+
+ if( !pViewForwarder )
+ throw uno::RuntimeException("Unable to fetch view forwarder, model might be dead", mxFrontEnd);
+
+ if( !pViewForwarder->IsValid() )
+ throw uno::RuntimeException("View forwarder is invalid, model might be dead", mxFrontEnd);
+
+ return *pViewForwarder;
+ }
+
+ SvxEditViewForwarder& AccessibleTextHelper_Impl::GetEditViewForwarder() const
+ {
+ if( !maEditSource.IsValid() )
+ throw uno::RuntimeException("Unknown edit source", mxFrontEnd);
+
+ SvxEditViewForwarder* pViewForwarder = maEditSource.GetEditViewForwarder();
+
+ if( !pViewForwarder )
+ {
+ throw uno::RuntimeException("No edit view forwarder, object not in edit mode", mxFrontEnd);
+ }
+
+ if( !pViewForwarder->IsValid() )
+ {
+ throw uno::RuntimeException("View forwarder is invalid, object not in edit mode", mxFrontEnd);
+ }
+
+ return *pViewForwarder;
+ }
+
+ SvxEditSourceAdapter& AccessibleTextHelper_Impl::GetEditSource() const
+ {
+ if( !maEditSource.IsValid() )
+ throw uno::RuntimeException("AccessibleTextHelper_Impl::GetEditSource: no edit source", mxFrontEnd );
+ return maEditSource;
+ }
+
+ namespace {
+
+ // functor for sending child events (no stand-alone function, they are maybe not inlined)
+ class AccessibleTextHelper_OffsetChildIndex
+ {
+ public:
+ explicit AccessibleTextHelper_OffsetChildIndex( sal_Int32 nDifference ) : mnDifference(nDifference) {}
+ void operator()( ::accessibility::AccessibleEditableTextPara& rPara )
+ {
+ rPara.SetIndexInParent( rPara.GetIndexInParent() + mnDifference );
+ }
+
+ private:
+ const sal_Int32 mnDifference;
+ };
+
+ }
+
+ void AccessibleTextHelper_Impl::SetStartIndex( sal_Int32 nOffset )
+ {
+ sal_Int32 nOldOffset( mnStartIndex );
+
+ mnStartIndex = nOffset;
+
+ if( nOldOffset != nOffset )
+ {
+ // update children
+ AccessibleTextHelper_OffsetChildIndex aFunctor( nOffset - nOldOffset );
+
+ ::std::for_each( maParaManager.begin(), maParaManager.end(),
+ AccessibleParaManager::WeakChildAdapter< AccessibleTextHelper_OffsetChildIndex > (aFunctor) );
+ }
+ }
+
+ void AccessibleTextHelper_Impl::SetAdditionalChildStates( VectorOfStates&& rChildStates )
+ {
+ maParaManager.SetAdditionalChildStates( std::move(rChildStates) );
+ }
+
+ void AccessibleTextHelper_Impl::SetChildFocus( sal_Int32 nChild, bool bHaveFocus )
+ {
+ if( bHaveFocus )
+ {
+ if( mbThisHasFocus )
+ SetShapeFocus( false );
+
+ maParaManager.SetFocus( nChild );
+
+ // we just received the focus, also send caret event then
+ UpdateSelection();
+
+ SAL_INFO("svx", "Paragraph " << nChild << " received focus");
+ }
+ else
+ {
+ maParaManager.SetFocus( -1 );
+
+ SAL_INFO("svx", "Paragraph " << nChild << " lost focus");
+
+ if( mbGroupHasFocus )
+ SetShapeFocus( true );
+ }
+ }
+
+ void AccessibleTextHelper_Impl::ChangeChildFocus( sal_Int32 nNewChild )
+ {
+ if( mbThisHasFocus )
+ SetShapeFocus( false );
+
+ mbGroupHasFocus = true;
+ maParaManager.SetFocus( nNewChild );
+
+ SAL_INFO("svx", "Paragraph " << nNewChild << " received focus");
+ }
+
+ void AccessibleTextHelper_Impl::SetShapeFocus( bool bHaveFocus )
+ {
+ bool bOldFocus( mbThisHasFocus );
+
+ mbThisHasFocus = bHaveFocus;
+
+ if( bOldFocus == bHaveFocus )
+ return;
+
+ if( bHaveFocus )
+ {
+ if( mxFrontEnd.is() )
+ {
+ AccessibleCell* pAccessibleCell = dynamic_cast< AccessibleCell* > ( mxFrontEnd.get() );
+ if ( !pAccessibleCell )
+ GotPropertyEvent( uno::Any(AccessibleStateType::FOCUSED), AccessibleEventId::STATE_CHANGED );
+ else // the focus event on cell should be fired on table directly
+ {
+ AccessibleTableShape* pAccTable = pAccessibleCell->GetParentTable();
+ if (pAccTable)
+ pAccTable->SetStateDirectly(AccessibleStateType::FOCUSED);
+ }
+ }
+ SAL_INFO("svx", "Parent object received focus" );
+ }
+ else
+ {
+ // The focus state should be reset directly on table.
+ //LostPropertyEvent( uno::makeAny(AccessibleStateType::FOCUSED), AccessibleEventId::STATE_CHANGED );
+ if( mxFrontEnd.is() )
+ {
+ AccessibleCell* pAccessibleCell = dynamic_cast< AccessibleCell* > ( mxFrontEnd.get() );
+ if ( !pAccessibleCell )
+ FireEvent( AccessibleEventId::STATE_CHANGED, uno::Any(), uno::Any(AccessibleStateType::FOCUSED) );
+ else
+ {
+ AccessibleTableShape* pAccTable = pAccessibleCell->GetParentTable();
+ if (pAccTable)
+ pAccTable->ResetStateDirectly(AccessibleStateType::FOCUSED);
+ }
+ }
+ SAL_INFO("svx", "Parent object lost focus" );
+ }
+ }
+
+ void AccessibleTextHelper_Impl::SetFocus( bool bHaveFocus )
+ {
+ bool bOldFocus( mbGroupHasFocus );
+
+ mbGroupHasFocus = bHaveFocus;
+
+ if( IsActive() )
+ {
+ try
+ {
+ // find the one with the cursor and get/set focus accordingly
+ ESelection aSelection;
+ if( GetEditViewForwarder().GetSelection( aSelection ) )
+ SetChildFocus( aSelection.nEndPara, bHaveFocus );
+ }
+ catch( const uno::Exception& ) {}
+ }
+ else if( bOldFocus != bHaveFocus )
+ {
+ SetShapeFocus( bHaveFocus );
+ }
+
+ SAL_INFO("svx", "focus changed, Object " << this << ", state: " << (bHaveFocus ? "focused" : "not focused") );
+ }
+
+ bool AccessibleTextHelper_Impl::IsActive() const
+ {
+ try
+ {
+ SvxEditSource& rEditSource = GetEditSource();
+ SvxEditViewForwarder* pViewForwarder = rEditSource.GetEditViewForwarder();
+
+ if( !pViewForwarder )
+ return false;
+
+ if( mxFrontEnd.is() )
+ {
+ AccessibleCell* pAccessibleCell = dynamic_cast< AccessibleCell* > ( mxFrontEnd.get() );
+ if ( pAccessibleCell )
+ {
+ sdr::table::CellRef xCell = pAccessibleCell->getCellRef();
+ if ( xCell.is() )
+ return xCell->IsActiveCell();
+ }
+ }
+ return pViewForwarder->IsValid();
+ }
+ catch( const uno::RuntimeException& )
+ {
+ return false;
+ }
+ }
+
+ void AccessibleTextHelper_Impl::UpdateSelection()
+ {
+ try
+ {
+ ESelection aSelection;
+ if( GetEditViewForwarder().GetSelection( aSelection ) )
+ {
+ if( maLastSelection != aSelection &&
+ aSelection.nEndPara < maParaManager.GetNum() )
+ {
+ // #103998# Not that important, changed from assertion to trace
+ if( mbThisHasFocus )
+ {
+ SAL_INFO("svx", "Parent has focus!");
+ }
+
+ sal_Int32 nMaxValidParaIndex( GetTextForwarder().GetParagraphCount() - 1 );
+
+ // notify all affected paragraphs (TODO: may be suboptimal,
+ // since some paragraphs might stay selected)
+ if( maLastSelection.nStartPara != EE_PARA_NOT_FOUND )
+ {
+ // Did the caret move from one paragraph to another?
+ // #100530# no caret events if not focused.
+ if( mbGroupHasFocus &&
+ maLastSelection.nEndPara != aSelection.nEndPara )
+ {
+ if( maLastSelection.nEndPara < maParaManager.GetNum() )
+ {
+ maParaManager.FireEvent( ::std::min( maLastSelection.nEndPara, nMaxValidParaIndex ),
+ ::std::min( maLastSelection.nEndPara, nMaxValidParaIndex )+1,
+ AccessibleEventId::CARET_CHANGED,
+ uno::Any(static_cast<sal_Int32>(-1)),
+ uno::Any(maLastSelection.nEndPos) );
+ }
+
+ ChangeChildFocus( aSelection.nEndPara );
+
+ SAL_INFO(
+ "svx",
+ "focus changed, Object: " << this
+ << ", Paragraph: " << aSelection.nEndPara
+ << ", Last paragraph: "
+ << maLastSelection.nEndPara);
+ }
+ }
+
+ // #100530# no caret events if not focused.
+ if( mbGroupHasFocus )
+ {
+ uno::Any aOldCursor;
+
+ // #i13705# The old cursor can only contain valid
+ // values if it's the same paragraph!
+ if( maLastSelection.nStartPara != EE_PARA_NOT_FOUND &&
+ maLastSelection.nEndPara == aSelection.nEndPara )
+ {
+ aOldCursor <<= maLastSelection.nEndPos;
+ }
+ else
+ {
+ aOldCursor <<= static_cast<sal_Int32>(-1);
+ }
+
+ maParaManager.FireEvent( aSelection.nEndPara,
+ aSelection.nEndPara+1,
+ AccessibleEventId::CARET_CHANGED,
+ uno::Any(aSelection.nEndPos),
+ aOldCursor );
+ }
+
+ SAL_INFO(
+ "svx",
+ "caret changed, Object: " << this << ", New pos: "
+ << aSelection.nEndPos << ", Old pos: "
+ << maLastSelection.nEndPos << ", New para: "
+ << aSelection.nEndPara << ", Old para: "
+ << maLastSelection.nEndPara);
+
+ // #108947# Sort new range before calling FireEvent
+ ::std::pair<sal_Int32, sal_Int32> sortedSelection(
+ makeSortedPair(::std::min( aSelection.nStartPara, nMaxValidParaIndex ),
+ ::std::min( aSelection.nEndPara, nMaxValidParaIndex ) ) );
+
+ // #108947# Sort last range before calling FireEvent
+ ::std::pair<sal_Int32, sal_Int32> sortedLastSelection(
+ makeSortedPair(::std::min( maLastSelection.nStartPara, nMaxValidParaIndex ),
+ ::std::min( maLastSelection.nEndPara, nMaxValidParaIndex ) ) );
+
+ // event TEXT_SELECTION_CHANGED has to be submitted. (#i27299#)
+ const sal_Int16 nTextSelChgEventId =
+ AccessibleEventId::TEXT_SELECTION_CHANGED;
+ // #107037# notify selection change
+ if( maLastSelection.nStartPara == EE_PARA_NOT_FOUND )
+ {
+ // last selection is undefined
+ // use method <ESelection::HasRange()> (#i27299#)
+ if ( aSelection.HasRange() )
+ {
+ // selection was undefined, now is on
+ maParaManager.FireEvent( sortedSelection.first,
+ sortedSelection.second+1,
+ nTextSelChgEventId );
+ }
+ }
+ else
+ {
+ // last selection is valid
+ // use method <ESelection::HasRange()> (#i27299#)
+ if ( maLastSelection.HasRange() &&
+ !aSelection.HasRange() )
+ {
+ // selection was on, now is empty
+ maParaManager.FireEvent( sortedLastSelection.first,
+ sortedLastSelection.second+1,
+ nTextSelChgEventId );
+ }
+ // use method <ESelection::HasRange()> (#i27299#)
+ else if( !maLastSelection.HasRange() &&
+ aSelection.HasRange() )
+ {
+ // selection was empty, now is on
+ maParaManager.FireEvent( sortedSelection.first,
+ sortedSelection.second+1,
+ nTextSelChgEventId );
+ }
+ // no event TEXT_SELECTION_CHANGED event, if new and
+ // last selection are empty. (#i27299#)
+ else if ( maLastSelection.HasRange() &&
+ aSelection.HasRange() )
+ {
+ // use sorted last and new selection
+ ESelection aTmpLastSel( maLastSelection );
+ aTmpLastSel.Adjust();
+ ESelection aTmpSel( aSelection );
+ aTmpSel.Adjust();
+ // first submit event for new and changed selection
+ sal_Int32 nPara = aTmpSel.nStartPara;
+ for ( ; nPara <= aTmpSel.nEndPara; ++nPara )
+ {
+ if ( nPara < aTmpLastSel.nStartPara ||
+ nPara > aTmpLastSel.nEndPara )
+ {
+ // new selection on paragraph <nPara>
+ maParaManager.FireEvent( nPara,
+ nTextSelChgEventId );
+ }
+ else
+ {
+ // check for changed selection on paragraph <nPara>
+ const sal_Int32 nParaStartPos =
+ nPara == aTmpSel.nStartPara
+ ? aTmpSel.nStartPos : 0;
+ const sal_Int32 nParaEndPos =
+ nPara == aTmpSel.nEndPara
+ ? aTmpSel.nEndPos : -1;
+ const sal_Int32 nLastParaStartPos =
+ nPara == aTmpLastSel.nStartPara
+ ? aTmpLastSel.nStartPos : 0;
+ const sal_Int32 nLastParaEndPos =
+ nPara == aTmpLastSel.nEndPara
+ ? aTmpLastSel.nEndPos : -1;
+ if ( nParaStartPos != nLastParaStartPos ||
+ nParaEndPos != nLastParaEndPos )
+ {
+ maParaManager.FireEvent(
+ nPara, nTextSelChgEventId );
+ }
+ }
+ }
+ // second submit event for 'old' selections
+ nPara = aTmpLastSel.nStartPara;
+ for ( ; nPara <= aTmpLastSel.nEndPara; ++nPara )
+ {
+ if ( nPara < aTmpSel.nStartPara ||
+ nPara > aTmpSel.nEndPara )
+ {
+ maParaManager.FireEvent( nPara,
+ nTextSelChgEventId );
+ }
+ }
+ }
+ }
+
+ maLastSelection = aSelection;
+ }
+ }
+ }
+ // no selection? no update actions
+ catch( const uno::RuntimeException& ) {}
+ }
+
+ void AccessibleTextHelper_Impl::ShutdownEditSource()
+ {
+ // This should only be called with solar mutex locked, i.e. from the main office thread
+
+ // This here is somewhat clumsy: As soon as our children have
+ // a NULL EditSource (maParaManager.SetEditSource()), they
+ // enter the disposed state and cannot be reanimated. Thus, it
+ // is unavoidable and a hard requirement to let go and create
+ // from scratch each and every child.
+
+ // invalidate children
+ maParaManager.Dispose();
+ maParaManager.SetNum(0);
+
+ // lost all children
+ if( mxFrontEnd.is() )
+ FireEvent(AccessibleEventId::INVALIDATE_ALL_CHILDREN);
+
+ // quit listen on stale edit source
+ if( maEditSource.IsValid() )
+ EndListening( maEditSource.GetBroadcaster() );
+
+ maEditSource.SetEditSource( ::std::unique_ptr< SvxEditSource >() );
+ }
+
+ void AccessibleTextHelper_Impl::SetEditSource( ::std::unique_ptr< SvxEditSource > && pEditSource )
+ {
+ // This should only be called with solar mutex locked, i.e. from the main office thread
+
+ // shutdown old edit source
+ ShutdownEditSource();
+
+ // set new edit source
+ maEditSource.SetEditSource( std::move(pEditSource) );
+
+ // init child vector to the current child count
+ if( maEditSource.IsValid() )
+ {
+ maParaManager.SetNum( GetTextForwarder().GetParagraphCount() );
+
+ // listen on new edit source
+ StartListening( maEditSource.GetBroadcaster() );
+
+ UpdateVisibleChildren();
+ }
+ }
+
+ void AccessibleTextHelper_Impl::SetOffset( const Point& rPoint )
+ {
+ // guard against non-atomic access to maOffset data structure
+ {
+ std::scoped_lock aGuard( maMutex );
+ maOffset = rPoint;
+ }
+
+ maParaManager.SetEEOffset( rPoint );
+
+ // in all cases, check visibility afterwards.
+ UpdateVisibleChildren();
+ UpdateBoundRect();
+ }
+
+ void AccessibleTextHelper_Impl::UpdateVisibleChildren( bool bBroadcastEvents )
+ {
+ try
+ {
+ SvxTextForwarder& rCacheTF = GetTextForwarder();
+ sal_Int32 nParas=rCacheTF.GetParagraphCount();
+
+ // GetTextForwarder might have replaced everything, update
+ // paragraph count in case it's outdated
+ maParaManager.SetNum( nParas );
+
+ mnFirstVisibleChild = -1;
+ mnLastVisibleChild = -2;
+
+ for( sal_Int32 nCurrPara=0; nCurrPara<nParas; ++nCurrPara )
+ {
+ if (nCurrPara == 0)
+ mnFirstVisibleChild = nCurrPara;
+ mnLastVisibleChild = nCurrPara;
+ if (mxFrontEnd.is() && bBroadcastEvents)
+ {
+ // child not yet created?
+ if (!maParaManager.HasCreatedChild(nCurrPara))
+ {
+ GotPropertyEvent( uno::Any( maParaManager.CreateChild( nCurrPara - mnFirstVisibleChild,
+ mxFrontEnd, GetEditSource(), nCurrPara ).first ),
+ AccessibleEventId::CHILD );
+ }
+ }
+ }
+ }
+ catch( const uno::Exception& )
+ {
+ OSL_FAIL("AccessibleTextHelper_Impl::UpdateVisibleChildren error while determining visible children");
+
+ // something failed - currently no children
+ mnFirstVisibleChild = -1;
+ mnLastVisibleChild = -2;
+ maParaManager.SetNum(0);
+
+ // lost all children
+ if( bBroadcastEvents )
+ FireEvent(AccessibleEventId::INVALIDATE_ALL_CHILDREN);
+ }
+ }
+
+ void AccessibleTextHelper_Impl::UpdateBoundRect()
+ {
+ // send BOUNDRECT_CHANGED to affected children
+ for(auto it = maParaManager.begin(); it != maParaManager.end(); ++it)
+ {
+ ::accessibility::AccessibleParaManager::WeakChild& rChild = *it;
+ // retrieve hard reference from weak one
+ auto aHardRef( rChild.first.get() );
+
+ if( aHardRef.is() )
+ {
+ awt::Rectangle aNewRect = aHardRef->getBounds();
+ const awt::Rectangle& aOldRect = rChild.second;
+
+ if( aNewRect.X != aOldRect.X ||
+ aNewRect.Y != aOldRect.Y ||
+ aNewRect.Width != aOldRect.Width ||
+ aNewRect.Height != aOldRect.Height )
+ {
+ // visible data changed
+ aHardRef->FireEvent( AccessibleEventId::BOUNDRECT_CHANGED );
+
+ // update internal bounds
+ rChild = ::accessibility::AccessibleParaManager::WeakChild( rChild.first, aNewRect );
+ }
+ }
+ }
+ }
+
+#ifdef DBG_UTIL
+ void AccessibleTextHelper_Impl::CheckInvariants() const
+ {
+ if( mnFirstVisibleChild >= 0 &&
+ mnFirstVisibleChild > mnLastVisibleChild )
+ {
+ OSL_FAIL( "AccessibleTextHelper: range invalid" );
+ }
+ }
+#endif
+
+ namespace {
+
+ // functor for sending child events (no stand-alone function, they are maybe not inlined)
+ class AccessibleTextHelper_LostChildEvent
+ {
+ public:
+ explicit AccessibleTextHelper_LostChildEvent( AccessibleTextHelper_Impl& rImpl ) : mrImpl(rImpl) {}
+ void operator()( const ::accessibility::AccessibleParaManager::WeakChild& rPara )
+ {
+ // retrieve hard reference from weak one
+ auto aHardRef( rPara.first.get() );
+
+ if( aHardRef.is() )
+ mrImpl.FireEvent(AccessibleEventId::CHILD, uno::Any(), uno::Any(css::uno::Reference<css::accessibility::XAccessible>(aHardRef)) );
+ }
+
+ private:
+ AccessibleTextHelper_Impl& mrImpl;
+ };
+
+ }
+
+ void AccessibleTextHelper_Impl::ParagraphsMoved( sal_Int32 nFirst, sal_Int32 nMiddle, sal_Int32 nLast )
+ {
+ const sal_Int32 nParas = GetTextForwarder().GetParagraphCount();
+
+ /* rotate paragraphs
+ * =================
+ *
+ * Three cases:
+ *
+ * 1.
+ * ... nParagraph ... nParam1 ... nParam2 ...
+ * |______________[xxxxxxxxxxx]
+ * becomes
+ * [xxxxxxxxxxx]|______________
+ *
+ * tail is 0
+ *
+ * 2.
+ * ... nParam1 ... nParagraph ... nParam2 ...
+ * [xxxxxxxxxxx|xxxxxxxxxxxxxx]____________
+ * becomes
+ * ____________[xxxxxxxxxxx|xxxxxxxxxxxxxx]
+ *
+ * tail is nParagraph - nParam1
+ *
+ * 3.
+ * ... nParam1 ... nParam2 ... nParagraph ...
+ * [xxxxxxxxxxx]___________|____________
+ * becomes
+ * ___________|____________[xxxxxxxxxxx]
+ *
+ * tail is nParam2 - nParam1
+ */
+
+ // sort nParagraph, nParam1 and nParam2 in ascending order, calc range
+ if( nMiddle < nFirst )
+ {
+ ::std::swap(nFirst, nMiddle);
+ }
+ else if( nMiddle < nLast )
+ {
+ nLast = nLast + nMiddle - nFirst;
+ }
+ else
+ {
+ ::std::swap(nMiddle, nLast);
+ nLast = nLast + nMiddle - nFirst;
+ }
+
+ if( !(nFirst < nParas && nMiddle < nParas && nLast < nParas) )
+ return;
+
+ // since we have no "paragraph index
+ // changed" event on UAA, remove
+ // [first,last] and insert again later (in
+ // UpdateVisibleChildren)
+
+ // maParaManager.Rotate( nFirst, nMiddle, nLast );
+
+ // send CHILD_EVENT to affected children
+ ::accessibility::AccessibleParaManager::VectorOfChildren::const_iterator begin = maParaManager.begin();
+ ::accessibility::AccessibleParaManager::VectorOfChildren::const_iterator end = begin;
+
+ ::std::advance( begin, nFirst );
+ ::std::advance( end, nLast+1 );
+
+ // TODO: maybe optimize here in the following way. If the
+ // number of removed children exceeds a certain threshold,
+ // use InvalidateFlags::Children
+ AccessibleTextHelper_LostChildEvent aFunctor( *this );
+
+ ::std::for_each( begin, end, aFunctor );
+
+ maParaManager.Release(nFirst, nLast+1);
+ // should be no need for UpdateBoundRect, since all affected children are cleared.
+ }
+
+ namespace {
+
+ // functor for sending child events (no stand-alone function, they are maybe not inlined)
+ class AccessibleTextHelper_ChildrenTextChanged
+ {
+ public:
+ void operator()( ::accessibility::AccessibleEditableTextPara& rPara )
+ {
+ rPara.TextChanged();
+ }
+ };
+
+ /** functor processing queue events
+
+ Reacts on SfxHintId::TextParaInserted/REMOVED events and stores
+ their content
+ */
+ class AccessibleTextHelper_QueueFunctor
+ {
+ public:
+ AccessibleTextHelper_QueueFunctor() :
+ mnParasChanged( 0 ),
+ mnParaIndex(-1),
+ mnHintId(SfxHintId::NONE)
+ {}
+ void operator()( const SfxHint* pEvent )
+ {
+ if( !pEvent || mnParasChanged == -1 )
+ return;
+
+ // determine hint type
+ const TextHint* pTextHint = dynamic_cast<const TextHint*>( pEvent );
+ const SvxEditSourceHint* pEditSourceHint = dynamic_cast<const SvxEditSourceHint*>( pEvent );
+
+ if( !(!pEditSourceHint && pTextHint &&
+ (pTextHint->GetId() == SfxHintId::TextParaInserted ||
+ pTextHint->GetId() == SfxHintId::TextParaRemoved )) )
+ return;
+
+ if( pTextHint->GetValue() == EE_PARA_ALL )
+ {
+ mnParasChanged = -1;
+ }
+ else
+ {
+ mnHintId = pTextHint->GetId();
+ mnParaIndex = pTextHint->GetValue();
+ ++mnParasChanged;
+ }
+ }
+
+ /** Query number of paragraphs changed during queue processing.
+
+ @return number of changed paragraphs, -1 for
+ "every paragraph changed"
+ */
+ sal_Int32 GetNumberOfParasChanged() const { return mnParasChanged; }
+ /** Query index of last added/removed paragraph
+
+ @return index of lastly added paragraphs, -1 for none
+ added so far.
+ */
+ sal_Int32 GetParaIndex() const { return mnParaIndex; }
+ /** Query hint id of last interesting event
+
+ @return hint id of last interesting event (REMOVED/INSERTED).
+ */
+ SfxHintId GetHintId() const { return mnHintId; }
+
+ private:
+ /** number of paragraphs changed during queue processing. -1 for
+ "every paragraph changed"
+ */
+ sal_Int32 mnParasChanged;
+ /// index of paragraph added/removed last
+ sal_Int32 mnParaIndex;
+ /// TextHint ID (removed/inserted) of last interesting event
+ SfxHintId mnHintId;
+ };
+
+ }
+
+ void AccessibleTextHelper_Impl::ProcessQueue()
+ {
+ // inspect queue for paragraph insert/remove events. If there
+ // is exactly _one_ of those in the queue, and the number of
+ // paragraphs has changed by exactly one, use that event to
+ // determine a priori which paragraph was added/removed. This
+ // is necessary, since I must sync right here with the
+ // EditEngine state (number of paragraphs etc.), since I'm
+ // potentially sending listener events right away.
+ AccessibleTextHelper_QueueFunctor aFunctor;
+ maEventQueue.ForEach( aFunctor );
+
+ const sal_Int32 nNewParas( GetTextForwarder().GetParagraphCount() );
+ const sal_Int32 nCurrParas( maParaManager.GetNum() );
+
+ // whether every paragraph already is updated (no need to
+ // repeat that later on, e.g. for PARA_MOVED events)
+ bool bEverythingUpdated( false );
+
+ if( std::abs( nNewParas - nCurrParas ) == 1 &&
+ aFunctor.GetNumberOfParasChanged() == 1 )
+ {
+ // #103483# Exactly one paragraph added/removed. This is
+ // the normal case, optimize event handling here.
+
+ if( aFunctor.GetHintId() == SfxHintId::TextParaInserted )
+ {
+ // update num of paras
+ maParaManager.SetNum( nNewParas );
+
+ // release everything from the insertion position until the end
+ maParaManager.Release(aFunctor.GetParaIndex(), nCurrParas);
+
+ // TODO: Clarify whether this behaviour _really_ saves
+ // anybody anything!
+ // update children, _don't_ broadcast
+ UpdateVisibleChildren( false );
+ UpdateBoundRect();
+
+ // send insert event
+ // #109864# Enforce creation of this paragraph
+ try
+ {
+ GotPropertyEvent( uno::Any( getAccessibleChild( aFunctor.GetParaIndex() -
+ mnFirstVisibleChild + GetStartIndex() ) ),
+ AccessibleEventId::CHILD );
+ }
+ catch( const uno::Exception& )
+ {
+ OSL_FAIL("AccessibleTextHelper_Impl::ProcessQueue: could not create new paragraph");
+ }
+ }
+ else if( aFunctor.GetHintId() == SfxHintId::TextParaRemoved )
+ {
+ ::accessibility::AccessibleParaManager::VectorOfChildren::const_iterator begin = maParaManager.begin();
+ ::std::advance( begin, aFunctor.GetParaIndex() );
+ ::accessibility::AccessibleParaManager::VectorOfChildren::const_iterator end = begin;
+ ::std::advance( end, 1 );
+
+ // #i61812# remember para to be removed for later notification
+ // AFTER the new state is applied (that after the para got removed)
+ ::uno::Reference< XAccessible > xPara(begin->first.get());
+
+ // release everything from the remove position until the end
+ maParaManager.Release(aFunctor.GetParaIndex(), nCurrParas);
+
+ // update num of paras
+ maParaManager.SetNum( nNewParas );
+
+ // TODO: Clarify whether this behaviour _really_ saves
+ // anybody anything!
+ // update children, _don't_ broadcast
+ UpdateVisibleChildren( false );
+ UpdateBoundRect();
+
+ // #i61812# notification for removed para
+ if (xPara.is())
+ FireEvent(AccessibleEventId::CHILD, uno::Any(), uno::Any( xPara) );
+ }
+#ifdef DBG_UTIL
+ else
+ OSL_FAIL("AccessibleTextHelper_Impl::ProcessQueue() invalid hint id");
+#endif
+ }
+ else if( nNewParas != nCurrParas )
+ {
+ // release all paras
+ maParaManager.Release(0, nCurrParas);
+
+ // update num of paras
+ maParaManager.SetNum( nNewParas );
+
+ // #109864# create from scratch, don't broadcast
+ UpdateVisibleChildren( false );
+ UpdateBoundRect();
+
+ // number of paragraphs somehow changed - but we have no
+ // chance determining how. Thus, throw away everything and
+ // create from scratch.
+ // (child events should be broadcast after the changes are done...)
+ FireEvent(AccessibleEventId::INVALIDATE_ALL_CHILDREN);
+
+ // no need for further updates later on
+ bEverythingUpdated = true;
+ }
+
+ bool bUpdatedBoundRectAndVisibleChildren(false);
+
+ while( !maEventQueue.IsEmpty() )
+ {
+ ::std::unique_ptr< SfxHint > pHint( maEventQueue.PopFront() );
+ if (pHint)
+ {
+ const SfxHint& rHint = *pHint;
+
+ // Note, if you add events here, you need to update the AccessibleTextEventQueue::Append
+ // code, because only the events we process here, are actually queued there.
+
+ try
+ {
+
+ if (rHint.GetId() == SfxHintId::ThisIsAnSdrHint)
+ {
+ const SdrHint* pSdrHint = static_cast< const SdrHint* >( &rHint );
+
+ switch( pSdrHint->GetKind() )
+ {
+ case SdrHintKind::BeginEdit:
+ {
+ if(!IsActive())
+ {
+ break;
+ }
+ // change children state
+ maParaManager.SetActive();
+
+ // per definition, edit mode text has the focus
+ SetFocus( true );
+ break;
+ }
+
+ case SdrHintKind::EndEdit:
+ {
+ // focused child now loses focus
+ ESelection aSelection;
+ if( GetEditViewForwarder().GetSelection( aSelection ) )
+ SetChildFocus( aSelection.nEndPara, false );
+
+ // change children state
+ maParaManager.SetActive( false );
+
+ maLastSelection = ESelection( EE_PARA_NOT_FOUND, EE_INDEX_NOT_FOUND,
+ EE_PARA_NOT_FOUND, EE_INDEX_NOT_FOUND);
+ break;
+ }
+ default:
+ break;
+ }
+ }
+ else if( const SvxEditSourceHint* pEditSourceHint = dynamic_cast<const SvxEditSourceHint*>( &rHint ) )
+ {
+ switch( pEditSourceHint->GetId() )
+ {
+ case SfxHintId::EditSourceParasMoved:
+ {
+ DBG_ASSERT( pEditSourceHint->GetStartValue() < GetTextForwarder().GetParagraphCount() &&
+ pEditSourceHint->GetEndValue() < GetTextForwarder().GetParagraphCount(),
+ "AccessibleTextHelper_Impl::NotifyHdl: Invalid notification");
+
+ if( !bEverythingUpdated )
+ {
+ ParagraphsMoved(pEditSourceHint->GetStartValue(),
+ pEditSourceHint->GetValue(),
+ pEditSourceHint->GetEndValue());
+
+ // in all cases, check visibility afterwards.
+ UpdateVisibleChildren();
+ }
+ break;
+ }
+
+ case SfxHintId::EditSourceSelectionChanged:
+ // notify listeners
+ try
+ {
+ UpdateSelection();
+ }
+ // maybe we're not in edit mode (this is not an error)
+ catch( const uno::Exception& ) {}
+ break;
+ default: break;
+ }
+ }
+ else if( const TextHint* pTextHint = dynamic_cast<const TextHint*>( &rHint ) )
+ {
+ const sal_Int32 nParas = GetTextForwarder().GetParagraphCount();
+
+ switch( pTextHint->GetId() )
+ {
+ case SfxHintId::TextModified:
+ {
+ // notify listeners
+ sal_Int32 nPara( pTextHint->GetValue() );
+
+ // #108900# Delegate change event to children
+ AccessibleTextHelper_ChildrenTextChanged aNotifyChildrenFunctor;
+
+ if( nPara == EE_PARA_ALL )
+ {
+ // #108900# Call every child
+ ::std::for_each( maParaManager.begin(), maParaManager.end(),
+ AccessibleParaManager::WeakChildAdapter< AccessibleTextHelper_ChildrenTextChanged > (aNotifyChildrenFunctor) );
+ }
+ else
+ if( nPara < nParas )
+ {
+ // #108900# Call child at index nPara
+ ::std::for_each( maParaManager.begin()+nPara, maParaManager.begin()+nPara+1,
+ AccessibleParaManager::WeakChildAdapter< AccessibleTextHelper_ChildrenTextChanged > (aNotifyChildrenFunctor) );
+ }
+ break;
+ }
+
+ case SfxHintId::TextParaInserted:
+ // already happened above
+ break;
+
+ case SfxHintId::TextParaRemoved:
+ // already happened above
+ break;
+
+ case SfxHintId::TextHeightChanged:
+ // visibility changed, done below
+ break;
+
+ case SfxHintId::TextViewScrolled:
+ // visibility changed, done below
+ break;
+ default: break;
+ }
+
+ // in all cases, check visibility afterwards.
+ if (!bUpdatedBoundRectAndVisibleChildren)
+ {
+ UpdateVisibleChildren();
+ UpdateBoundRect();
+ bUpdatedBoundRectAndVisibleChildren = true;
+ }
+ }
+ else if ( dynamic_cast<const SvxViewChangedHint*>( &rHint ) )
+ {
+ // just check visibility
+ if (!bUpdatedBoundRectAndVisibleChildren)
+ {
+ UpdateVisibleChildren();
+ UpdateBoundRect();
+ bUpdatedBoundRectAndVisibleChildren = true;
+ }
+ }
+ // it's VITAL to keep the SfxSimpleHint last! It's the base of some classes above!
+ else if( rHint.GetId() == SfxHintId::Dying)
+ {
+ // edit source is dying under us, become defunc then
+ try
+ {
+ // make edit source inaccessible
+ // Note: cannot destroy it here, since we're called from there!
+ ShutdownEditSource();
+ }
+ catch( const uno::Exception& ) {}
+ }
+ }
+ catch( const uno::Exception& )
+ {
+ DBG_UNHANDLED_EXCEPTION("svx");
+ }
+ }
+ }
+ }
+
+ void AccessibleTextHelper_Impl::Notify( SfxBroadcaster& /*rBC*/, const SfxHint& rHint )
+ {
+ // precondition: solar mutex locked
+ DBG_TESTSOLARMUTEX();
+
+ // precondition: not in a recursion
+ if( mbInNotify )
+ return;
+
+ mbInNotify = true;
+
+ try
+ {
+ // Process notification event, arranged in order of likelihood of
+ // occurrence to avoid unnecessary dynamic_cast. Note that
+ // SvxEditSourceHint is derived from TextHint, so has to be checked
+ // before that.
+ if (rHint.GetId() == SfxHintId::ThisIsAnSdrHint)
+ {
+ const SdrHint* pSdrHint = static_cast< const SdrHint* >( &rHint );
+ // process drawing layer events right away, if not
+ // within an open EE notification frame. Otherwise,
+ // event processing would be delayed until next EE
+ // notification sequence.
+ maEventQueue.Append( *pSdrHint );
+ }
+ else if( const SvxViewChangedHint* pViewHint = dynamic_cast<const SvxViewChangedHint*>( &rHint ) )
+ {
+ // process visibility right away, if not within an
+ // open EE notification frame. Otherwise, event
+ // processing would be delayed until next EE
+ // notification sequence.
+ maEventQueue.Append( *pViewHint );
+ }
+ else if( const SvxEditSourceHint* pEditSourceHint = dynamic_cast<const SvxEditSourceHint*>( &rHint ) )
+ {
+ // EditEngine should emit TEXT_SELECTION_CHANGED events (#i27299#)
+ maEventQueue.Append( *pEditSourceHint );
+ }
+ else if( const TextHint* pTextHint = dynamic_cast<const TextHint*>( &rHint ) )
+ {
+ // EditEngine should emit TEXT_SELECTION_CHANGED events (#i27299#)
+ if(pTextHint->GetId() == SfxHintId::TextProcessNotifications)
+ ProcessQueue();
+ else
+ maEventQueue.Append( *pTextHint );
+ }
+ // it's VITAL to keep the SfxHint last! It's the base of the classes above!
+ else if( rHint.GetId() == SfxHintId::Dying )
+ {
+ // handle this event _at once_, because after that, objects are invalid
+ // edit source is dying under us, become defunc then
+ maEventQueue.Clear();
+ try
+ {
+ // make edit source inaccessible
+ // Note: cannot destroy it here, since we're called from there!
+ ShutdownEditSource();
+ }
+ catch( const uno::Exception& ) {}
+ }
+ }
+ catch( const uno::Exception& )
+ {
+ DBG_UNHANDLED_EXCEPTION("svx");
+ mbInNotify = false;
+ }
+
+ mbInNotify = false;
+ }
+
+ void AccessibleTextHelper_Impl::Dispose()
+ {
+ if( getNotifierClientId() != snNotifierClientRevoked)
+ {
+ try
+ {
+ // #106234# Unregister from EventNotifier
+ ::comphelper::AccessibleEventNotifier::revokeClient( getNotifierClientId() );
+ SAL_INFO("svx", "disposed ID: " << mnNotifierClientId );
+ }
+ catch( const uno::Exception& ) {}
+
+ mnNotifierClientId = snNotifierClientRevoked;
+ }
+
+ try
+ {
+ // dispose children
+ maParaManager.Dispose();
+ }
+ catch( const uno::Exception& ) {}
+
+ // quit listen on stale edit source
+ if( maEditSource.IsValid() )
+ EndListening( maEditSource.GetBroadcaster() );
+
+ // clear references
+ maEditSource.SetEditSource( ::std::unique_ptr< SvxEditSource >() );
+ mxFrontEnd = nullptr;
+ }
+
+ void AccessibleTextHelper_Impl::FireEvent( const sal_Int16 nEventId, const uno::Any& rNewValue, const uno::Any& rOldValue ) const
+ {
+ // -- object locked --
+ AccessibleEventObject aEvent;
+ {
+ std::scoped_lock aGuard(maMutex);
+
+ DBG_ASSERT(mxFrontEnd.is(), "AccessibleTextHelper::FireEvent: no event source set");
+
+ if (mxFrontEnd.is())
+ aEvent = AccessibleEventObject(mxFrontEnd->getAccessibleContext(), nEventId,
+ rNewValue, rOldValue);
+ else
+ aEvent = AccessibleEventObject(uno::Reference<uno::XInterface>(), nEventId,
+ rNewValue, rOldValue);
+
+ // no locking necessary, FireEvent internally copies listeners
+ // if someone removes/adds in between Further locking,
+ // actually, might lead to deadlocks, since we're calling out
+ // of this object
+ }
+ // -- until here --
+
+ FireEvent(aEvent);
+ }
+
+ void AccessibleTextHelper_Impl::FireEvent( const AccessibleEventObject& rEvent ) const
+ {
+ // #106234# Delegate to EventNotifier
+ if (getNotifierClientId() != snNotifierClientRevoked)
+ ::comphelper::AccessibleEventNotifier::addEvent( getNotifierClientId(), rEvent );
+ }
+
+ // XAccessibleContext
+ sal_Int32 AccessibleTextHelper_Impl::getAccessibleChildCount() const
+ {
+ return mnLastVisibleChild - mnFirstVisibleChild + 1;
+ }
+
+ uno::Reference< XAccessible > AccessibleTextHelper_Impl::getAccessibleChild( sal_Int32 i )
+ {
+ i -= GetStartIndex();
+
+ if( 0 > i || i >= getAccessibleChildCount() ||
+ GetTextForwarder().GetParagraphCount() <= i )
+ {
+ throw lang::IndexOutOfBoundsException("Invalid child index", mxFrontEnd);
+ }
+
+ DBG_ASSERT(mxFrontEnd.is(), "AccessibleTextHelper_Impl::UpdateVisibleChildren: no frontend set");
+
+ if( mxFrontEnd.is() )
+ return maParaManager.CreateChild( i, mxFrontEnd, GetEditSource(), mnFirstVisibleChild + i ).first;
+ else
+ return nullptr;
+ }
+
+ void AccessibleTextHelper_Impl::addAccessibleEventListener( const uno::Reference< XAccessibleEventListener >& xListener )
+ {
+ if( getNotifierClientId() != snNotifierClientRevoked )
+ ::comphelper::AccessibleEventNotifier::addEventListener( getNotifierClientId(), xListener );
+ }
+
+ void AccessibleTextHelper_Impl::removeAccessibleEventListener( const uno::Reference< XAccessibleEventListener >& xListener )
+ {
+ if( getNotifierClientId() == snNotifierClientRevoked )
+ return;
+
+ const sal_Int32 nListenerCount = ::comphelper::AccessibleEventNotifier::removeEventListener( getNotifierClientId(), 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::TClientId nId( getNotifierClientId() );
+ mnNotifierClientId = snNotifierClientRevoked;
+ ::comphelper::AccessibleEventNotifier::revokeClient( nId );
+ }
+ }
+
+ uno::Reference< XAccessible > AccessibleTextHelper_Impl::getAccessibleAtPoint( const awt::Point& _aPoint )
+ {
+ // make given position relative
+ if( !mxFrontEnd.is() )
+ throw uno::RuntimeException("AccessibleTextHelper_Impl::getAccessibleAt: frontend invalid", mxFrontEnd );
+
+ uno::Reference< XAccessibleContext > xFrontEndContext = mxFrontEnd->getAccessibleContext();
+
+ if( !xFrontEndContext.is() )
+ throw uno::RuntimeException("AccessibleTextHelper_Impl::getAccessibleAt: frontend invalid", mxFrontEnd );
+
+ uno::Reference< XAccessibleComponent > xFrontEndComponent( xFrontEndContext, uno::UNO_QUERY_THROW );
+
+ // #103862# No longer need to make given position relative
+ Point aPoint( _aPoint.X, _aPoint.Y );
+
+ // respect EditEngine offset to surrounding shape/cell
+ aPoint -= GetOffset();
+
+ // convert to EditEngine coordinate system
+ SvxTextForwarder& rCacheTF = GetTextForwarder();
+ Point aLogPoint( GetViewForwarder().PixelToLogic( aPoint, rCacheTF.GetMapMode() ) );
+
+ // iterate over all visible children (including those not yet created)
+ sal_Int32 nChild;
+ for( nChild=mnFirstVisibleChild; nChild <= mnLastVisibleChild; ++nChild )
+ {
+ DBG_ASSERT(nChild >= 0,
+ "AccessibleTextHelper_Impl::getAccessibleAt: index value overflow");
+
+ tools::Rectangle aParaBounds( rCacheTF.GetParaBounds( nChild ) );
+
+ if( aParaBounds.Contains( aLogPoint ) )
+ return getAccessibleChild( nChild - mnFirstVisibleChild + GetStartIndex() );
+ }
+
+ // found none
+ return nullptr;
+ }
+
+
+ // AccessibleTextHelper implementation (simply forwards to impl)
+
+ AccessibleTextHelper::AccessibleTextHelper( ::std::unique_ptr< SvxEditSource > && pEditSource ) :
+ mpImpl( new AccessibleTextHelper_Impl() )
+ {
+ SolarMutexGuard aGuard;
+
+ SetEditSource( std::move(pEditSource) );
+ }
+
+ AccessibleTextHelper::~AccessibleTextHelper()
+ {
+ }
+
+ const SvxEditSource& AccessibleTextHelper::GetEditSource() const
+ {
+#ifdef DBG_UTIL
+ mpImpl->CheckInvariants();
+
+ const SvxEditSource& aEditSource = mpImpl->GetEditSource();
+
+ mpImpl->CheckInvariants();
+
+ return aEditSource;
+#else
+ return mpImpl->GetEditSource();
+#endif
+ }
+
+ void AccessibleTextHelper::SetEditSource( ::std::unique_ptr< SvxEditSource > && pEditSource )
+ {
+#ifdef DBG_UTIL
+ // precondition: solar mutex locked
+ DBG_TESTSOLARMUTEX();
+
+ mpImpl->CheckInvariants();
+#endif
+
+ mpImpl->SetEditSource( std::move(pEditSource) );
+
+#ifdef DBG_UTIL
+ mpImpl->CheckInvariants();
+#endif
+ }
+
+ void AccessibleTextHelper::SetEventSource( const uno::Reference< XAccessible >& rInterface )
+ {
+#ifdef DBG_UTIL
+ mpImpl->CheckInvariants();
+#endif
+
+ mpImpl->SetEventSource( rInterface );
+
+#ifdef DBG_UTIL
+ mpImpl->CheckInvariants();
+#endif
+ }
+
+ void AccessibleTextHelper::SetFocus( bool bHaveFocus )
+ {
+#ifdef DBG_UTIL
+ // precondition: solar mutex locked
+ DBG_TESTSOLARMUTEX();
+
+ mpImpl->CheckInvariants();
+#endif
+
+ mpImpl->SetFocus( bHaveFocus );
+
+#ifdef DBG_UTIL
+ mpImpl->CheckInvariants();
+#endif
+ }
+
+ bool AccessibleTextHelper::HaveFocus()
+ {
+#ifdef DBG_UTIL
+ mpImpl->CheckInvariants();
+
+ bool bRet( mpImpl->HaveFocus() );
+
+ mpImpl->CheckInvariants();
+
+ return bRet;
+#else
+ return mpImpl->HaveFocus();
+#endif
+ }
+
+ void AccessibleTextHelper::SetOffset( const Point& rPoint )
+ {
+#ifdef DBG_UTIL
+ // precondition: solar mutex locked
+ DBG_TESTSOLARMUTEX();
+
+ mpImpl->CheckInvariants();
+#endif
+
+ mpImpl->SetOffset( rPoint );
+
+#ifdef DBG_UTIL
+ mpImpl->CheckInvariants();
+#endif
+ }
+
+ void AccessibleTextHelper::SetStartIndex( sal_Int32 nOffset )
+ {
+#ifdef DBG_UTIL
+ // precondition: solar mutex locked
+ DBG_TESTSOLARMUTEX();
+
+ mpImpl->CheckInvariants();
+#endif
+
+ mpImpl->SetStartIndex( nOffset );
+
+#ifdef DBG_UTIL
+ mpImpl->CheckInvariants();
+#endif
+ }
+
+ sal_Int32 AccessibleTextHelper::GetStartIndex() const
+ {
+#ifdef DBG_UTIL
+ mpImpl->CheckInvariants();
+
+ sal_Int32 nOffset = mpImpl->GetStartIndex();
+
+ mpImpl->CheckInvariants();
+
+ return nOffset;
+#else
+ return mpImpl->GetStartIndex();
+#endif
+ }
+
+ void AccessibleTextHelper::SetAdditionalChildStates( VectorOfStates&& rChildStates )
+ {
+ mpImpl->SetAdditionalChildStates( std::move(rChildStates) );
+ }
+
+ void AccessibleTextHelper::UpdateChildren()
+ {
+#ifdef DBG_UTIL
+ // precondition: solar mutex locked
+ DBG_TESTSOLARMUTEX();
+
+ mpImpl->CheckInvariants();
+#endif
+
+ mpImpl->UpdateVisibleChildren();
+ mpImpl->UpdateBoundRect();
+
+ mpImpl->UpdateSelection();
+
+#ifdef DBG_UTIL
+ mpImpl->CheckInvariants();
+#endif
+ }
+
+ void AccessibleTextHelper::Dispose()
+ {
+ // As Dispose calls ShutdownEditSource, which in turn
+ // deregisters as listener on the edit source, have to lock
+ // here
+ SolarMutexGuard aGuard;
+
+#ifdef DBG_UTIL
+ mpImpl->CheckInvariants();
+#endif
+
+ mpImpl->Dispose();
+
+#ifdef DBG_UTIL
+ mpImpl->CheckInvariants();
+#endif
+ }
+
+ // XAccessibleContext
+ sal_Int32 AccessibleTextHelper::GetChildCount() const
+ {
+ SolarMutexGuard aGuard;
+
+#ifdef DBG_UTIL
+ mpImpl->CheckInvariants();
+
+ sal_Int32 nRet = mpImpl->getAccessibleChildCount();
+
+ mpImpl->CheckInvariants();
+
+ return nRet;
+#else
+ return mpImpl->getAccessibleChildCount();
+#endif
+ }
+
+ uno::Reference< XAccessible > AccessibleTextHelper::GetChild( sal_Int32 i )
+ {
+ SolarMutexGuard aGuard;
+
+#ifdef DBG_UTIL
+ mpImpl->CheckInvariants();
+
+ uno::Reference< XAccessible > xRet = mpImpl->getAccessibleChild( i );
+
+ mpImpl->CheckInvariants();
+
+ return xRet;
+#else
+ return mpImpl->getAccessibleChild( i );
+#endif
+ }
+
+ void AccessibleTextHelper::AddEventListener( const uno::Reference< XAccessibleEventListener >& xListener )
+ {
+#ifdef DBG_UTIL
+ mpImpl->CheckInvariants();
+
+ mpImpl->addAccessibleEventListener( xListener );
+
+ mpImpl->CheckInvariants();
+#else
+ mpImpl->addAccessibleEventListener( xListener );
+#endif
+ }
+
+ void AccessibleTextHelper::RemoveEventListener( const uno::Reference< XAccessibleEventListener >& xListener )
+ {
+#ifdef DBG_UTIL
+ mpImpl->CheckInvariants();
+
+ mpImpl->removeAccessibleEventListener( xListener );
+
+ mpImpl->CheckInvariants();
+#else
+ mpImpl->removeAccessibleEventListener( xListener );
+#endif
+ }
+
+ // XAccessibleComponent
+ uno::Reference< XAccessible > AccessibleTextHelper::GetAt( const awt::Point& aPoint )
+ {
+ SolarMutexGuard aGuard;
+
+#ifdef DBG_UTIL
+ mpImpl->CheckInvariants();
+
+ uno::Reference< XAccessible > xChild = mpImpl->getAccessibleAtPoint( aPoint );
+
+ mpImpl->CheckInvariants();
+
+ return xChild;
+#else
+ return mpImpl->getAccessibleAtPoint( aPoint );
+#endif
+ }
+
+} // end of namespace accessibility
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/accessibility/ChildrenManager.cxx b/svx/source/accessibility/ChildrenManager.cxx
new file mode 100644
index 000000000..71983f46e
--- /dev/null
+++ b/svx/source/accessibility/ChildrenManager.cxx
@@ -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 .
+ */
+
+#include <svx/ChildrenManager.hxx>
+#include "ChildrenManagerImpl.hxx"
+#include <sal/log.hxx>
+
+using namespace ::com::sun::star;
+using namespace ::com::sun::star::accessibility;
+using ::com::sun::star::uno::Reference;
+
+namespace accessibility {
+
+// AccessibleChildrenManager
+ChildrenManager::ChildrenManager (
+ const css::uno::Reference<XAccessible>& rxParent,
+ const css::uno::Reference<drawing::XShapes>& rxShapeList,
+ const AccessibleShapeTreeInfo& rShapeTreeInfo,
+ AccessibleContextBase& rContext)
+ : mpImpl(
+ new ChildrenManagerImpl(
+ rxParent, rxShapeList, rShapeTreeInfo, rContext))
+{
+ mpImpl->Init ();
+}
+
+
+ChildrenManager::~ChildrenManager()
+{
+ mpImpl->dispose();
+
+ // empty
+ SAL_INFO("svx", "~ChildrenManager");
+}
+
+tools::Long ChildrenManager::GetChildCount() const noexcept
+{
+ return mpImpl->GetChildCount();
+}
+
+css::uno::Reference<XAccessible> ChildrenManager::GetChild (tools::Long nIndex)
+{
+ return mpImpl->GetChild (nIndex);
+}
+
+css::uno::Reference<css::drawing::XShape> ChildrenManager::GetChildShape(tools::Long nIndex)
+{
+ return mpImpl->GetChildShape(nIndex);
+}
+
+void ChildrenManager::Update (bool bCreateNewObjectsOnDemand)
+{
+ mpImpl->Update (bCreateNewObjectsOnDemand);
+}
+
+void ChildrenManager::SetShapeList (const css::uno::Reference<css::drawing::XShapes>& xShapeList)
+{
+ mpImpl->SetShapeList (xShapeList);
+}
+
+void ChildrenManager::AddAccessibleShape (css::uno::Reference<css::accessibility::XAccessible> const & shape)
+{
+ mpImpl->AddAccessibleShape (shape);
+}
+
+void ChildrenManager::ClearAccessibleShapeList()
+{
+ mpImpl->ClearAccessibleShapeList ();
+}
+
+void ChildrenManager::SetInfo (AccessibleShapeTreeInfo const & rShapeTreeInfo)
+{
+ mpImpl->SetInfo (rShapeTreeInfo);
+}
+
+void ChildrenManager::UpdateSelection()
+{
+ mpImpl->UpdateSelection ();
+}
+
+bool ChildrenManager::HasFocus() const
+{
+ return mpImpl->HasFocus ();
+}
+
+void ChildrenManager::RemoveFocus()
+{
+ mpImpl->RemoveFocus ();
+}
+
+// IAccessibleViewForwarderListener
+void ChildrenManager::ViewForwarderChanged()
+{
+ mpImpl->ViewForwarderChanged();
+}
+
+} // end of namespace accessibility
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/accessibility/ChildrenManagerImpl.cxx b/svx/source/accessibility/ChildrenManagerImpl.cxx
new file mode 100644
index 000000000..b557e00a9
--- /dev/null
+++ b/svx/source/accessibility/ChildrenManagerImpl.cxx
@@ -0,0 +1,1051 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * 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 "ChildrenManagerImpl.hxx"
+#include <svx/ShapeTypeHandler.hxx>
+#include <svx/AccessibleControlShape.hxx>
+#include <svx/AccessibleShapeInfo.hxx>
+#include <svx/IAccessibleViewForwarder.hxx>
+#include <vcl/svapp.hxx>
+#include <com/sun/star/accessibility/AccessibleRole.hpp>
+#include <com/sun/star/accessibility/AccessibleStateType.hpp>
+#include <com/sun/star/accessibility/AccessibleEventId.hpp>
+#include <com/sun/star/beans/XPropertySet.hpp>
+#include <com/sun/star/document/XShapeEventBroadcaster.hpp>
+#include <com/sun/star/frame/XController.hpp>
+#include <com/sun/star/lang/IndexOutOfBoundsException.hpp>
+#include <com/sun/star/view/XSelectionSupplier.hpp>
+#include <com/sun/star/container/XChild.hpp>
+#include <comphelper/types.hxx>
+#include <o3tl/safeint.hxx>
+#include <o3tl/sorted_vector.hxx>
+#include <rtl/ustring.hxx>
+#include <tools/debug.hxx>
+#include <svx/SvxShapeTypes.hxx>
+#include <vcl/window.hxx>
+
+using namespace ::com::sun::star;
+using namespace ::com::sun::star::accessibility;
+using ::com::sun::star::uno::Reference;
+
+namespace accessibility {
+
+namespace
+{
+void adjustIndexInParentOfShapes(ChildDescriptorListType& _rList)
+{
+ sal_Int32 i=0;
+ for (auto& rItem : _rList)
+ {
+ rItem.setIndexAtAccessibleShape(i);
+ ++i;
+ }
+}
+}
+
+// AccessibleChildrenManager
+ChildrenManagerImpl::ChildrenManagerImpl (
+ const uno::Reference<XAccessible>& rxParent,
+ const uno::Reference<drawing::XShapes>& rxShapeList,
+ const AccessibleShapeTreeInfo& rShapeTreeInfo,
+ AccessibleContextBase& rContext)
+ : mxShapeList (rxShapeList),
+ mxParent (rxParent),
+ maShapeTreeInfo (rShapeTreeInfo),
+ mrContext (rContext),
+ mpFocusedShape(nullptr)
+{
+}
+
+
+ChildrenManagerImpl::~ChildrenManagerImpl()
+{
+ DBG_ASSERT (m_bDisposed, "~AccessibleDrawDocumentView: object has not been disposed");
+}
+
+
+void ChildrenManagerImpl::Init()
+{
+ // Register as view::XSelectionChangeListener.
+ Reference<frame::XController> xController(maShapeTreeInfo.GetController());
+ Reference<view::XSelectionSupplier> xSelectionSupplier (
+ xController, uno::UNO_QUERY);
+ if (xSelectionSupplier.is())
+ {
+ xController->addEventListener(
+ static_cast<document::XEventListener*>(this));
+
+ xSelectionSupplier->addSelectionChangeListener (
+ static_cast<view::XSelectionChangeListener*>(this));
+ }
+
+ // Register at model as document::XEventListener.
+ if (maShapeTreeInfo.GetModelBroadcaster().is())
+ maShapeTreeInfo.GetModelBroadcaster()->addEventListener (
+ static_cast<document::XEventListener*>(this));
+}
+
+
+tools::Long ChildrenManagerImpl::GetChildCount() const noexcept
+{
+ return maVisibleChildren.size();
+}
+
+
+css::uno::Reference<css::drawing::XShape> ChildrenManagerImpl::GetChildShape(tools::Long nIndex)
+{
+ uno::Reference<XAccessible> xAcc = GetChild(nIndex);
+ auto I = std::find_if(maVisibleChildren.begin(), maVisibleChildren.end(),
+ [&xAcc](const ChildDescriptor& rChild) { return rChild.mxAccessibleShape == xAcc; });
+ if (I != maVisibleChildren.end())
+ return I->mxShape;
+ return uno::Reference< drawing::XShape > ();
+}
+
+/** Return the requested accessible child object. Create it if it is not
+ yet in the cache.
+*/
+uno::Reference<XAccessible>
+ ChildrenManagerImpl::GetChild (tools::Long nIndex)
+{
+ // Check whether the given index is valid.
+ if (nIndex < 0 || o3tl::make_unsigned(nIndex) >= maVisibleChildren.size())
+ throw lang::IndexOutOfBoundsException (
+ "no accessible child with index " + OUString::number(nIndex),
+ mxParent);
+
+ return GetChild (maVisibleChildren[nIndex],nIndex);
+}
+
+
+/** Return the requested accessible child object. Create it if it is not
+ yet in the cache.
+*/
+uno::Reference<XAccessible>
+ ChildrenManagerImpl::GetChild (ChildDescriptor& rChildDescriptor,sal_Int32 _nIndex)
+{
+ if ( ! rChildDescriptor.mxAccessibleShape.is())
+ {
+ SolarMutexGuard g;
+ // Make sure that the requested accessible object has not been
+ // created while locking the global mutex.
+ if ( ! rChildDescriptor.mxAccessibleShape.is())
+ {
+ AccessibleShapeInfo aShapeInfo(
+ rChildDescriptor.mxShape,
+ mxParent,
+ this);
+ // Create accessible object that corresponds to the descriptor's
+ // shape.
+ rtl::Reference<AccessibleShape> pShape(
+ ShapeTypeHandler::Instance().CreateAccessibleObject (
+ aShapeInfo,
+ maShapeTreeInfo));
+ rChildDescriptor.mxAccessibleShape = pShape;
+ if ( pShape.is() )
+ {
+ pShape->Init();
+ pShape->setIndexInParent(_nIndex);
+ }
+ }
+ }
+
+ return rChildDescriptor.mxAccessibleShape;
+}
+
+
+/** Find all shapes among the specified shapes that lie fully or partially
+ inside the visible area. Put those shapes into the cleared cache. The
+ corresponding accessible objects will be created on demand.
+
+ At the moment, first all accessible objects are removed from the cache
+ and the appropriate listeners are informed of this. Next, the list is
+ created again. This should be optimized in the future to not remove and
+ create objects that will be in the list before and after the update
+ method.
+*/
+void ChildrenManagerImpl::Update (bool bCreateNewObjectsOnDemand)
+{
+ if (maShapeTreeInfo.GetViewForwarder() == nullptr)
+ return;
+ tools::Rectangle aVisibleArea = maShapeTreeInfo.GetViewForwarder()->GetVisibleArea();
+
+ // 1. Create a local list of visible shapes.
+ ChildDescriptorListType aChildList;
+ CreateListOfVisibleShapes (aChildList);
+
+ // 2. Merge the information that is already known about the visible
+ // shapes from the current list into the new list.
+ MergeAccessibilityInformation (aChildList);
+
+ // 3. Replace the current list of visible shapes with the new one. Do
+ // the same with the visible area.
+ {
+ SolarMutexGuard g;
+ adjustIndexInParentOfShapes(aChildList);
+
+ // Use swap to copy the contents of the new list in constant time.
+ maVisibleChildren.swap (aChildList);
+
+ // aChildList now contains all the old children, while maVisibleChildren
+ // contains all the current children
+
+ // 4. Find all shapes in the old list that are not in the current list,
+ // send appropriate events and remove the accessible shape.
+
+ // Do this *after* we have set our new list of children, because
+ // removing a child may cause
+
+ // ChildDescriptor::disposeAccessibleObject -->
+ // AccessibleContextBase::CommitChange -->
+ // AtkListener::notifyEvent ->
+ // AtkListener::handleChildRemoved ->
+ // AtkListener::updateChildList
+ // AccessibleDrawDocumentView::getAccessibleChildCount ->
+ // ChildrenManagerImpl::GetChildCount ->
+ // maVisibleChildren.size()
+
+ // to be fired, and so the operations will take place on
+ // the list we are trying to replace
+
+ RemoveNonVisibleChildren (maVisibleChildren, aChildList);
+
+ aChildList.clear();
+
+ maVisibleArea = aVisibleArea;
+ }
+
+ // 5. If the visible area has changed then send events that signal a
+ // change of their bounding boxes for all shapes that are members of
+ // both the current and the new list of visible shapes.
+ if (maVisibleArea != aVisibleArea)
+ SendVisibleAreaEvents (maVisibleChildren);
+
+ // 6. If children have to be created immediately and not on demand then
+ // create the missing accessible objects now.
+ if (bCreateNewObjectsOnDemand)
+ return;
+
+ //operate on a copy of the list and restore it afterwards to guard
+ //against the pathological case where maVisibleChildren gets modified
+ //by other calls to this object while CreateAccessibilityObjects
+ //executes which can happen when java is disabled and the "enable-java"
+ //dialog appears during the instantiation of the linguistic components
+ //triggered by the creation of shapes belonging to the a11y objects
+ //
+ //i.e. launch start-center, launch impress with java disabled and
+ //a java-using linguistic component installed
+ maVisibleChildren.swap(aChildList);
+ CreateAccessibilityObjects(aChildList);
+ maVisibleChildren.swap(aChildList);
+}
+
+void ChildrenManagerImpl::CreateListOfVisibleShapes (
+ ChildDescriptorListType& raDescriptorList)
+{
+ SolarMutexGuard g;
+
+ OSL_ASSERT (maShapeTreeInfo.GetViewForwarder() != nullptr);
+
+ tools::Rectangle aVisibleArea = maShapeTreeInfo.GetViewForwarder()->GetVisibleArea();
+
+ // Add the visible shapes for which the accessible objects already exist.
+ for (const auto& rpShape : maAccessibleShapes)
+ {
+ if (rpShape.is())
+ {
+ uno::Reference<XAccessibleComponent> xComponent (
+ rpShape->getAccessibleContext(), uno::UNO_QUERY);
+ if (xComponent.is())
+ {
+ // The bounding box of the object already is clipped to the
+ // visible area. The object is therefore visible if the
+ // bounding box has non-zero extensions.
+ awt::Rectangle aPixelBBox (xComponent->getBounds());
+ if ((aPixelBBox.Width > 0) && (aPixelBBox.Height > 0))
+ raDescriptorList.emplace_back(rpShape);
+ }
+ }
+ }
+
+ // Add the visible shapes for which only the XShapes exist.
+ if (!mxShapeList.is() || !mxShapeList->hasElements())
+ return;
+
+ sal_Int32 nShapeCount = mxShapeList->getCount();
+ raDescriptorList.reserve( nShapeCount );
+ awt::Point aPos;
+ awt::Size aSize;
+ tools::Rectangle aBoundingBox;
+ uno::Reference<drawing::XShape> xShape;
+ for (sal_Int32 i=0; i<nShapeCount; ++i)
+ {
+ mxShapeList->getByIndex(i) >>= xShape;
+ aPos = xShape->getPosition();
+ aSize = xShape->getSize();
+
+ aBoundingBox.SetLeft( aPos.X );
+ aBoundingBox.SetTop( aPos.Y );
+ aBoundingBox.SetRight( aPos.X + aSize.Width );
+ aBoundingBox.SetBottom( aPos.Y + aSize.Height );
+
+ // Insert shape if it is visible, i.e. its bounding box overlaps
+ // the visible area.
+ if ( aBoundingBox.Overlaps(aVisibleArea) )
+ raDescriptorList.emplace_back(xShape);
+ }
+}
+
+void ChildrenManagerImpl::RemoveNonVisibleChildren (
+ const ChildDescriptorListType& rNewChildList,
+ ChildDescriptorListType& rOldChildList)
+{
+ // Iterate over list of formerly visible children and remove those that
+ // are not visible anymore, i.e. member of the new list of visible
+ // children.
+ for (auto& rChild : rOldChildList)
+ {
+ if (::std::find(rNewChildList.begin(), rNewChildList.end(), rChild) == rNewChildList.end())
+ {
+ // The child is disposed when there is a UNO shape from which
+ // the accessible shape can be created when the shape becomes
+ // visible again. When there is no such UNO shape then simply
+ // reset the descriptor but keep the accessibility object.
+ if (rChild.mxShape.is())
+ {
+ UnregisterAsDisposeListener (rChild.mxShape);
+ rChild.disposeAccessibleObject (mrContext);
+ }
+ else
+ {
+ AccessibleShape* pAccessibleShape = rChild.GetAccessibleShape();
+ pAccessibleShape->ResetState (AccessibleStateType::VISIBLE);
+ rChild.mxAccessibleShape = nullptr;
+ }
+ }
+ }
+}
+
+void ChildrenManagerImpl::MergeAccessibilityInformation (
+ ChildDescriptorListType& raNewChildList)
+{
+ ChildDescriptorListType::const_iterator aStartVisibleChildren = maVisibleChildren.begin();
+ ChildDescriptorListType::const_iterator aEndVisibleChildren = maVisibleChildren.end();
+
+ for (auto& rChild : raNewChildList)
+ {
+ ChildDescriptorListType::const_iterator aOldChildDescriptor =
+ std::find(aStartVisibleChildren, aEndVisibleChildren, rChild);
+
+ // Copy accessible shape if that exists in the old descriptor.
+ if (aOldChildDescriptor != aEndVisibleChildren &&
+ aOldChildDescriptor->mxAccessibleShape.is())
+ {
+ rChild.mxAccessibleShape = aOldChildDescriptor->mxAccessibleShape;
+ rChild.mbCreateEventPending = false;
+ }
+ else
+ RegisterAsDisposeListener (rChild.mxShape);
+ }
+}
+
+void ChildrenManagerImpl::SendVisibleAreaEvents (
+ ChildDescriptorListType& raNewChildList)
+{
+ for (const auto& rChild : raNewChildList)
+ {
+ // Tell shape of changed visible area. To do this, fake a
+ // change of the view forwarder. (Actually we usually get here
+ // as a result of a change of the view forwarder).
+ AccessibleShape* pShape = rChild.GetAccessibleShape ();
+ if (pShape != nullptr)
+ pShape->ViewForwarderChanged();
+ }
+}
+
+
+void ChildrenManagerImpl::CreateAccessibilityObjects (
+ ChildDescriptorListType& raNewChildList)
+{
+ sal_Int32 nPos = 0;
+ for ( auto& rChild : raNewChildList)
+ {
+ // Create the associated accessible object when the flag says so and
+ // it does not yet exist.
+ if ( ! rChild.mxAccessibleShape.is() )
+ GetChild (rChild, nPos);
+ if (rChild.mxAccessibleShape.is() && rChild.mbCreateEventPending)
+ {
+ rChild.mbCreateEventPending = false;
+ mrContext.CommitChange (
+ AccessibleEventId::CHILD,
+ uno::Any(rChild.mxAccessibleShape),
+ uno::Any());
+ }
+ ++nPos;
+ }
+}
+
+
+void ChildrenManagerImpl::AddShape (const Reference<drawing::XShape>& rxShape)
+{
+ if (!rxShape.is())
+ return;
+
+ SolarMutexClearableGuard aGuard;
+
+ // Test visibility of the shape.
+ tools::Rectangle aVisibleArea = maShapeTreeInfo.GetViewForwarder()->GetVisibleArea();
+ awt::Point aPos = rxShape->getPosition();
+ awt::Size aSize = rxShape->getSize();
+
+ tools::Rectangle aBoundingBox (
+ aPos.X,
+ aPos.Y,
+ aPos.X + aSize.Width,
+ aPos.Y + aSize.Height);
+
+ // Add the shape only when it belongs to the list of shapes stored
+ // in mxShapeList (which is either a page or a group shape).
+ Reference<container::XChild> xChild (rxShape, uno::UNO_QUERY);
+ if (!xChild.is())
+ return;
+
+ Reference<drawing::XShapes> xParent (xChild->getParent(), uno::UNO_QUERY);
+ if (xParent != mxShapeList)
+ return;
+
+ if (!aBoundingBox.Overlaps(aVisibleArea))
+ return;
+
+ // Add shape to list of visible shapes.
+ maVisibleChildren.emplace_back(rxShape);
+
+ // Create accessibility object.
+ ChildDescriptor& rDescriptor = maVisibleChildren.back();
+ GetChild (rDescriptor, maVisibleChildren.size()-1);
+
+ // Inform listeners about new child.
+ uno::Any aNewShape;
+ aNewShape <<= rDescriptor.mxAccessibleShape;
+ aGuard.clear();
+ mrContext.CommitChange (
+ AccessibleEventId::CHILD,
+ aNewShape,
+ uno::Any());
+ RegisterAsDisposeListener(rxShape);
+}
+
+
+void ChildrenManagerImpl::RemoveShape (const Reference<drawing::XShape>& rxShape)
+{
+ if (!rxShape.is())
+ return;
+
+ SolarMutexGuard g;
+
+ // Search shape in list of visible children.
+ ChildDescriptorListType::iterator I (
+ ::std::find (maVisibleChildren.begin(), maVisibleChildren.end(),
+ ChildDescriptor (rxShape)));
+ if (I == maVisibleChildren.end())
+ return;
+
+ // Remove descriptor from that list.
+ Reference<XAccessible> xHoldAlive(I->mxAccessibleShape);
+
+ UnregisterAsDisposeListener (I->mxShape);
+ // Dispose the accessible object.
+ I->disposeAccessibleObject (mrContext);
+
+ // Now we can safely remove the child descriptor and thus
+ // invalidate the iterator.
+ maVisibleChildren.erase (I);
+
+ adjustIndexInParentOfShapes(maVisibleChildren);
+}
+
+
+void ChildrenManagerImpl::SetShapeList (const css::uno::Reference<css::drawing::XShapes>& xShapeList)
+{
+ mxShapeList = xShapeList;
+}
+
+
+void ChildrenManagerImpl::AddAccessibleShape (css::uno::Reference<css::accessibility::XAccessible> const & shape)
+{
+ assert(shape.is());
+ maAccessibleShapes.push_back (shape);
+}
+
+
+void ChildrenManagerImpl::ClearAccessibleShapeList()
+{
+ // Copy the list of (visible) shapes to local lists and clear the
+ // originals.
+ ChildDescriptorListType aLocalVisibleChildren;
+ aLocalVisibleChildren.swap(maVisibleChildren);
+ AccessibleShapeList aLocalAccessibleShapes;
+ aLocalAccessibleShapes.swap(maAccessibleShapes);
+
+ // Tell the listeners that all children are gone.
+ mrContext.CommitChange (
+ AccessibleEventId::INVALIDATE_ALL_CHILDREN,
+ uno::Any(),
+ uno::Any());
+
+ // Now the objects in the local lists can be safely disposed without
+ // having problems with callers that want to update their child lists.
+
+ // Clear the list of visible accessible objects. Objects not created on
+ // demand for XShapes are treated below.
+ for (auto& rChild : aLocalVisibleChildren)
+ if ( rChild.mxAccessibleShape.is() && rChild.mxShape.is() )
+ {
+ ::comphelper::disposeComponent(rChild.mxAccessibleShape);
+ rChild.mxAccessibleShape = nullptr;
+ }
+
+ // Dispose all objects in the accessible shape list.
+ for (auto& rpShape : aLocalAccessibleShapes)
+ if (rpShape.is())
+ {
+ // Dispose the object.
+ ::comphelper::disposeComponent(rpShape);
+ rpShape = nullptr;
+ }
+}
+
+
+/** If the broadcasters change at which this object is registered then
+ unregister at old and register at new broadcasters.
+*/
+void ChildrenManagerImpl::SetInfo (const AccessibleShapeTreeInfo& rShapeTreeInfo)
+{
+ // Remember the current broadcasters and exchange the shape tree info.
+ Reference<document::XEventBroadcaster> xCurrentBroadcaster;
+ Reference<frame::XController> xCurrentController;
+ Reference<view::XSelectionSupplier> xCurrentSelectionSupplier;
+ {
+ SolarMutexGuard g;
+ xCurrentBroadcaster = maShapeTreeInfo.GetModelBroadcaster();
+ xCurrentController = maShapeTreeInfo.GetController();
+ xCurrentSelectionSupplier.set( xCurrentController, uno::UNO_QUERY);
+ maShapeTreeInfo = rShapeTreeInfo;
+ }
+
+ // Move registration to new model.
+ if (maShapeTreeInfo.GetModelBroadcaster() != xCurrentBroadcaster)
+ {
+ // Register at new broadcaster.
+ if (maShapeTreeInfo.GetModelBroadcaster().is())
+ maShapeTreeInfo.GetModelBroadcaster()->addEventListener (
+ static_cast<document::XEventListener*>(this));
+
+ // Unregister at old broadcaster.
+ if (xCurrentBroadcaster.is())
+ xCurrentBroadcaster->removeEventListener (
+ static_cast<document::XEventListener*>(this));
+ }
+
+ // Move registration to new selection supplier.
+ Reference<frame::XController> xNewController(maShapeTreeInfo.GetController());
+ Reference<view::XSelectionSupplier> xNewSelectionSupplier (
+ xNewController, uno::UNO_QUERY);
+ if (xNewSelectionSupplier == xCurrentSelectionSupplier)
+ return;
+
+ // Register at new broadcaster.
+ if (xNewSelectionSupplier.is())
+ {
+ xNewController->addEventListener(
+ static_cast<document::XEventListener*>(this));
+
+ xNewSelectionSupplier->addSelectionChangeListener (
+ static_cast<view::XSelectionChangeListener*>(this));
+ }
+
+ // Unregister at old broadcaster.
+ if (xCurrentSelectionSupplier.is())
+ {
+ xCurrentSelectionSupplier->removeSelectionChangeListener (
+ static_cast<view::XSelectionChangeListener*>(this));
+
+ xCurrentController->removeEventListener(
+ static_cast<document::XEventListener*>(this));
+ }
+}
+
+// lang::XEventListener
+void SAL_CALL
+ ChildrenManagerImpl::disposing (const lang::EventObject& rEventObject)
+{
+ if (rEventObject.Source == maShapeTreeInfo.GetModelBroadcaster()
+ || rEventObject.Source == maShapeTreeInfo.GetController())
+ {
+ impl_dispose();
+ }
+
+ // Handle disposing UNO shapes.
+ else
+ {
+ Reference<drawing::XShape> xShape (rEventObject.Source, uno::UNO_QUERY);
+
+ // Find the descriptor for the given shape.
+ ChildDescriptorListType::iterator I (
+ ::std::find (maVisibleChildren.begin(), maVisibleChildren.end(),
+ ChildDescriptor (xShape)));
+ if (I != maVisibleChildren.end())
+ {
+ // Clear the descriptor.
+ I->disposeAccessibleObject (mrContext);
+ I->mxShape = nullptr;
+ }
+ }
+}
+
+// document::XEventListener
+/** Listen for new and removed shapes.
+*/
+void SAL_CALL
+ ChildrenManagerImpl::notifyEvent (
+ const document::EventObject& rEventObject)
+{
+ if (rEventObject.EventName == "ShapeInserted")
+ AddShape (Reference<drawing::XShape>(rEventObject.Source, uno::UNO_QUERY));
+ else if (rEventObject.EventName == "ShapeRemoved")
+ RemoveShape (Reference<drawing::XShape>(rEventObject.Source, uno::UNO_QUERY));
+ // else ignore unknown event.
+}
+
+// view::XSelectionChangeListener
+void SAL_CALL
+ ChildrenManagerImpl::selectionChanged (const lang::EventObject& /*rEvent*/)
+{
+ UpdateSelection ();
+}
+
+
+void ChildrenManagerImpl::impl_dispose()
+{
+ Reference<frame::XController> xController(maShapeTreeInfo.GetController());
+ // Remove from broadcasters.
+ try
+ {
+ Reference<view::XSelectionSupplier> xSelectionSupplier (
+ xController, uno::UNO_QUERY);
+ if (xSelectionSupplier.is())
+ {
+ xSelectionSupplier->removeSelectionChangeListener (
+ static_cast<view::XSelectionChangeListener*>(this));
+ }
+ }
+ catch( uno::RuntimeException&)
+ {}
+
+ try
+ {
+ if (xController.is())
+ xController->removeEventListener(
+ static_cast<document::XEventListener*>(this));
+ }
+ catch( uno::RuntimeException&)
+ {}
+
+ maShapeTreeInfo.SetController (nullptr);
+
+ try
+ {
+ // Remove from broadcaster.
+ if (maShapeTreeInfo.GetModelBroadcaster().is())
+ maShapeTreeInfo.GetModelBroadcaster()->removeEventListener (
+ static_cast<document::XEventListener*>(this));
+ maShapeTreeInfo.SetModelBroadcaster (nullptr);
+ }
+ catch( uno::RuntimeException& )
+ {}
+
+ ClearAccessibleShapeList ();
+ SetShapeList (nullptr);
+}
+
+
+void ChildrenManagerImpl::disposing(std::unique_lock<std::mutex>&)
+{
+ impl_dispose();
+}
+
+// IAccessibleViewForwarderListener
+void ChildrenManagerImpl::ViewForwarderChanged()
+{
+ Update(false);
+}
+
+// IAccessibleParent
+bool ChildrenManagerImpl::ReplaceChild (
+ AccessibleShape* pCurrentChild,
+ const css::uno::Reference< css::drawing::XShape >& _rxShape,
+ const tools::Long /*_nIndex*/,
+ const AccessibleShapeTreeInfo& _rShapeTreeInfo)
+{
+ // Iterate over the visible children. If one of them has an already
+ // created accessible object that matches pCurrentChild then replace
+ // it. Otherwise the child to replace is either not in the list or has
+ // not ye been created (and is therefore not in the list, too) and a
+ // replacement is not necessary.
+ auto I = std::find_if(maVisibleChildren.begin(), maVisibleChildren.end(),
+ [&pCurrentChild](const ChildDescriptor& rChild) { return rChild.GetAccessibleShape() == pCurrentChild; });
+
+ if (I != maVisibleChildren.end())
+ {
+ // Dispose the current child and send an event about its deletion.
+ pCurrentChild->dispose();
+ mrContext.CommitChange (
+ AccessibleEventId::CHILD,
+ uno::Any(),
+ uno::Any (I->mxAccessibleShape));
+
+ // Replace with replacement and send an event about existence
+ // of the new child.
+ AccessibleShapeInfo aShapeInfo( _rxShape, pCurrentChild->getAccessibleParent(), this );
+ // create the new child
+ rtl::Reference<AccessibleShape> pNewChild(ShapeTypeHandler::Instance().CreateAccessibleObject (
+ aShapeInfo,
+ _rShapeTreeInfo
+ ));
+ if ( pNewChild.is() )
+ pNewChild->Init();
+
+ I->mxAccessibleShape = pNewChild.get();
+ mrContext.CommitChange (
+ AccessibleEventId::CHILD,
+ uno::Any (I->mxAccessibleShape),
+ uno::Any());
+
+ return true;
+ }
+
+ // When not found among the visible children we have to search the list
+ // of accessible shapes. This is not yet implemented.
+ return false;
+}
+
+// Add the impl method for IAccessibleParent interface
+AccessibleControlShape * ChildrenManagerImpl::GetAccControlShapeFromModel(css::beans::XPropertySet* pSet)
+{
+ sal_Int32 count = GetChildCount();
+ for (sal_Int32 index=0;index<count;index++)
+ {
+ AccessibleShape* pAccShape = maVisibleChildren[index].GetAccessibleShape();
+ if (pAccShape && ::accessibility::ShapeTypeHandler::Instance().GetTypeId(pAccShape->GetXShape()) == DRAWING_CONTROL)
+ {
+ auto* pCtlAccShape = static_cast<::accessibility::AccessibleControlShape*>(pAccShape);
+ if (pCtlAccShape->GetControlModel() == pSet)
+ return pCtlAccShape;
+ }
+ }
+ return nullptr;
+}
+uno::Reference<XAccessible>
+ ChildrenManagerImpl::GetAccessibleCaption (const uno::Reference<drawing::XShape>& xShape)
+{
+ auto I = std::find_if(maVisibleChildren.begin(), maVisibleChildren.end(),
+ [&xShape](const ChildDescriptor& rChild) { return rChild.mxShape.get() == xShape.get(); });
+ if (I != maVisibleChildren.end())
+ return I->mxAccessibleShape;
+ return uno::Reference<XAccessible> ();
+}
+
+/** Update the <const>SELECTED</const> and the <const>FOCUSED</const> state
+ of all visible children. Maybe this should be changed to all children.
+
+ Iterate over all descriptors of visible accessible shapes and look them
+ up in the selection.
+
+ If there is no valid controller then all shapes are deselected and
+ unfocused. If the controller's frame is not active then all shapes are
+ unfocused.
+*/
+void ChildrenManagerImpl::UpdateSelection()
+{
+ // Remember the current and new focused shape.
+ AccessibleShape* pCurrentlyFocusedShape = nullptr;
+ AccessibleShape* pNewFocusedShape = nullptr;
+ typedef std::pair< AccessibleShape* , sal_Bool > PAIR_SHAPE;//sal_Bool Selected,UnSelected.
+ typedef std::vector< PAIR_SHAPE > VEC_SHAPE;
+ VEC_SHAPE vecSelect;
+ int nAddSelect=0;
+ bool bHasSelectedShape=false;
+ if (!maVisibleChildren.empty())
+ {
+ Reference<frame::XController> xController(maShapeTreeInfo.GetController());
+ Reference<view::XSelectionSupplier> xSelectionSupplier (
+ xController, uno::UNO_QUERY);
+
+ // Try to cast the selection both to a multi selection and to a single
+ // selection.
+ Reference<container::XIndexAccess> xSelectedShapeAccess;
+ Reference<drawing::XShape> xSelectedShape;
+ if (xSelectionSupplier.is())
+ {
+ xSelectedShapeAccess.set( xSelectionSupplier->getSelection(), uno::UNO_QUERY);
+ xSelectedShape.set( xSelectionSupplier->getSelection(), uno::UNO_QUERY);
+ }
+
+ // tdf#139220 to quickly find if a given drawing::XShape is selected
+ o3tl::sorted_vector<css::uno::Reference<css::drawing::XShape>> aSortedSelectedShapes;
+ if (!xSelectedShape.is() && xSelectedShapeAccess.is())
+ {
+ sal_Int32 nCount = xSelectedShapeAccess->getCount();
+ aSortedSelectedShapes.reserve(nCount);
+ for (sal_Int32 i = 0; i < nCount; ++i)
+ {
+ css::uno::Reference<css::drawing::XShape> xShape(xSelectedShapeAccess->getByIndex(i), uno::UNO_QUERY);
+ aSortedSelectedShapes.insert(xShape);
+ }
+ }
+
+ for (const auto& rChild : maVisibleChildren)
+ {
+ AccessibleShape* pAccessibleShape = rChild.GetAccessibleShape();
+ if (rChild.mxAccessibleShape.is() && rChild.mxShape.is() && pAccessibleShape!=nullptr)
+ {
+ short nRole = pAccessibleShape->getAccessibleRole();
+ bool bDrawShape = (
+ nRole == AccessibleRole::GRAPHIC ||
+ nRole == AccessibleRole::EMBEDDED_OBJECT ||
+ nRole == AccessibleRole::SHAPE ||
+ nRole == AccessibleRole::IMAGE_MAP ||
+ nRole == AccessibleRole::TABLE_CELL ||
+ nRole == AccessibleRole::TABLE );
+ bool bShapeIsSelected = false;
+
+ // Look up the shape in the (single or multi-) selection.
+ if (xSelectedShape.is())
+ {
+ if (rChild.mxShape == xSelectedShape)
+ {
+ bShapeIsSelected = true;
+ pNewFocusedShape = pAccessibleShape;
+ }
+ }
+ else if (!aSortedSelectedShapes.empty())
+ {
+ if (aSortedSelectedShapes.find(rChild.mxShape) != aSortedSelectedShapes.end())
+ {
+ bShapeIsSelected = true;
+ // In a multi-selection no shape has the focus.
+ if (aSortedSelectedShapes.size() == 1)
+ pNewFocusedShape = pAccessibleShape;
+ }
+ }
+
+ // Set or reset the SELECTED state.
+ if (bShapeIsSelected)
+ {
+ if (pAccessibleShape->SetState (AccessibleStateType::SELECTED))
+ {
+ if (bDrawShape)
+ {
+ vecSelect.emplace_back(pAccessibleShape,true);
+ ++nAddSelect;
+ }
+ }
+ else
+ {//Selected not change,has selected shape before
+ bHasSelectedShape=true;
+ }
+ }
+ else
+ //pAccessibleShape->ResetState (AccessibleStateType::SELECTED);
+ {
+ if(pAccessibleShape->ResetState (AccessibleStateType::SELECTED))
+ {
+ if(bDrawShape)
+ {
+ vecSelect.emplace_back(pAccessibleShape,false);
+ }
+ }
+ }
+ // Does the shape have the current selection?
+ if (pAccessibleShape->GetState (AccessibleStateType::FOCUSED))
+ pCurrentlyFocusedShape = pAccessibleShape;
+ }
+ }
+ }
+
+ vcl::Window *pParentWindow = maShapeTreeInfo.GetWindow();
+ bool bShapeActive= false;
+ // For table cell, the table's parent must be checked to make sure it has focus.
+ if (pParentWindow)
+ {
+ vcl::Window *pPWindow = pParentWindow->GetParent();
+ if (pParentWindow->HasFocus() || (pPWindow && pPWindow->HasFocus()))
+ bShapeActive =true;
+ }
+ // Move focus from current to newly focused shape.
+ if (pCurrentlyFocusedShape != pNewFocusedShape)
+ {
+ if (pCurrentlyFocusedShape != nullptr)
+ pCurrentlyFocusedShape->ResetState (AccessibleStateType::FOCUSED);
+ if (pNewFocusedShape != nullptr && bShapeActive)
+ pNewFocusedShape->SetState (AccessibleStateType::FOCUSED);
+ }
+
+ if (nAddSelect >= 10 )//fire selection within
+ {
+ mrContext.CommitChange(AccessibleEventId::SELECTION_CHANGED_WITHIN,uno::Any(),uno::Any());
+ nAddSelect =0 ;//not fire selection event
+ }
+ for (VEC_SHAPE::reverse_iterator vi = vecSelect.rbegin(), aEndVecSelect = vecSelect.rend(); vi != aEndVecSelect ;++vi)
+ {
+ PAIR_SHAPE &pairShape= *vi;
+ Reference< XAccessible > xShape(pairShape.first);
+ uno::Any anyShape;
+ anyShape <<= xShape;
+
+ if (pairShape.second)//Selection add
+ {
+ if (bHasSelectedShape)
+ {
+ if ( nAddSelect > 0 )
+ {
+ mrContext.CommitChange(AccessibleEventId::SELECTION_CHANGED_ADD,anyShape,uno::Any());
+ }
+ }
+ else
+ {
+ //if has not selected shape ,first selected shape is fire selection event;
+ if (nAddSelect > 0 )
+ {
+ mrContext.CommitChange(AccessibleEventId::SELECTION_CHANGED,anyShape,uno::Any());
+ }
+ if (nAddSelect > 1 )//check other selected shape fire selection add event
+ {
+ bHasSelectedShape=true;
+ }
+ }
+ }
+ else //selection remove
+ {
+ mrContext.CommitChange(AccessibleEventId::SELECTION_CHANGED_REMOVE,anyShape,uno::Any());
+ }
+ }
+
+ // Remember whether there is a shape that now has the focus.
+ mpFocusedShape = pNewFocusedShape;
+}
+
+
+bool ChildrenManagerImpl::HasFocus() const
+{
+ return mpFocusedShape != nullptr;
+}
+
+
+void ChildrenManagerImpl::RemoveFocus()
+{
+ if (mpFocusedShape != nullptr)
+ {
+ mpFocusedShape->ResetState (AccessibleStateType::FOCUSED);
+ mpFocusedShape = nullptr;
+ }
+}
+
+
+void ChildrenManagerImpl::RegisterAsDisposeListener (
+ const Reference<drawing::XShape>& xShape)
+{
+ Reference<lang::XComponent> xComponent (xShape, uno::UNO_QUERY);
+ if (xComponent.is())
+ xComponent->addEventListener (
+ static_cast<document::XEventListener*>(this));
+}
+
+
+void ChildrenManagerImpl::UnregisterAsDisposeListener (
+ const Reference<drawing::XShape>& xShape)
+{
+ Reference<lang::XComponent> xComponent (xShape, uno::UNO_QUERY);
+ if (xComponent.is())
+ xComponent->removeEventListener (
+ static_cast<document::XEventListener*>(this));
+}
+
+// AccessibleChildDescriptor
+ChildDescriptor::ChildDescriptor (const Reference<drawing::XShape>& xShape)
+ : mxShape (xShape),
+ mbCreateEventPending (true)
+{
+ // Empty.
+}
+
+
+ChildDescriptor::ChildDescriptor (const Reference<XAccessible>& rxAccessibleShape)
+ : mxAccessibleShape (rxAccessibleShape),
+ mbCreateEventPending (true)
+{
+ // Make sure that the accessible object has the <const>VISIBLE</const>
+ // state set.
+ AccessibleShape* pAccessibleShape = GetAccessibleShape();
+ pAccessibleShape->SetState (AccessibleStateType::VISIBLE);
+}
+
+AccessibleShape* ChildDescriptor::GetAccessibleShape() const
+{
+ return static_cast<AccessibleShape*> (mxAccessibleShape.get());
+}
+
+void ChildDescriptor::setIndexAtAccessibleShape(sal_Int32 _nIndex)
+{
+ AccessibleShape* pShape = GetAccessibleShape();
+ if ( pShape )
+ pShape->setIndexInParent(_nIndex);
+}
+
+
+void ChildDescriptor::disposeAccessibleObject (AccessibleContextBase& rParent)
+{
+ if (!mxAccessibleShape.is())
+ return;
+
+ // Send event that the shape has been removed.
+ uno::Any aOldValue;
+ aOldValue <<= mxAccessibleShape;
+ rParent.CommitChange (
+ AccessibleEventId::CHILD,
+ uno::Any(),
+ aOldValue);
+
+ // Dispose and remove the object.
+ Reference<lang::XComponent> xComponent (mxAccessibleShape, uno::UNO_QUERY);
+ if (xComponent.is())
+ xComponent->dispose ();
+
+ mxAccessibleShape = nullptr;
+}
+
+
+} // end of namespace accessibility
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/accessibility/ChildrenManagerImpl.hxx b/svx/source/accessibility/ChildrenManagerImpl.hxx
new file mode 100644
index 000000000..f4105c878
--- /dev/null
+++ b/svx/source/accessibility/ChildrenManagerImpl.hxx
@@ -0,0 +1,494 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#ifndef INCLUDED_SVX_SOURCE_ACCESSIBILITY_CHILDRENMANAGERIMPL_HXX
+#define INCLUDED_SVX_SOURCE_ACCESSIBILITY_CHILDRENMANAGERIMPL_HXX
+
+#include <svx/IAccessibleViewForwarderListener.hxx>
+#include <svx/IAccessibleParent.hxx>
+#include <svx/AccessibleShapeTreeInfo.hxx>
+#include <editeng/AccessibleContextBase.hxx>
+#include <comphelper/compbase.hxx>
+#include <tools/gen.hxx>
+#include <vector>
+#include <com/sun/star/drawing/XShape.hpp>
+#include <com/sun/star/drawing/XShapes.hpp>
+#include <com/sun/star/document/XEventListener.hpp>
+#include <com/sun/star/view/XSelectionChangeListener.hpp>
+#include <com/sun/star/accessibility/XAccessible.hpp>
+
+namespace accessibility {
+
+class AccessibleShape;
+
+class ChildDescriptor; // See below for declaration.
+typedef ::std::vector<ChildDescriptor> ChildDescriptorListType;
+
+// Re-using MutexOwner class defined in AccessibleContextBase.hxx
+
+/** This class contains the actual implementation of the children manager.
+
+ <p>It maintains a set of visible accessible shapes in
+ <member>maVisibleChildren</member>. The objects in this list stem from
+ two sources. The first is a list of UNO shapes like the list of shapes
+ in a draw page. A reference to this list is held in
+ <member>maShapeList</member>. Accessible objects for these shapes are
+ created on demand. The list can be replaced by calls to the
+ <member>SetShapeList</member> method. The second source is a list of
+ already accessible objects. It can be modified by calls to the
+ <member>AddAccessibleShape</member> and
+ <member>ClearAccessibleShapeList</member> methods.</p>
+
+ <p>Each call of the <member>Update</member> method leads to a
+ re-calculation of the visible shapes which then can be queried with the
+ <member>GetChildCount</member> and <member>GetChild</member> methods.
+ Events are sent informing all listeners about the removed shapes which are
+ not visible anymore and about the added shapes.</p>
+
+ <p> The visible area which is used to determine the visibility of the
+ shapes is taken from the view forwarder. Thus, to signal a change of
+ the visible area call <member>ViewForwarderChanged</member>.</p>
+
+ <p>The children manager adds itself as disposing() listener at every UNO
+ shape it creates an accessible object for so that when the UNO shape
+ passes away it can dispose() the associated accessible object.</p>
+
+ @see ChildrenManager
+*/
+class ChildrenManagerImpl final
+ : public comphelper::WeakComponentImplHelper<
+ css::document::XEventListener,
+ css::view::XSelectionChangeListener>,
+ public IAccessibleViewForwarderListener,
+ public IAccessibleParent
+{
+public:
+ /** Create a children manager, which manages the children of the given
+ parent. The parent is used for creating accessible objects. The
+ list of shapes for which to create those objects is not derived from
+ the parent and has to be provided separately by calling one of the
+ update methods.
+ @param rxParent
+ The parent of the accessible objects which will be created
+ on demand at some point of time in the future.
+ @param rxShapeList
+ List of UNO shapes to manage.
+ @param rShapeTreeInfo
+ Bundle of information passed down the shape tree.
+ @param rContext
+ An accessible context object that is called for firing events
+ for new and deleted children, i.e. that holds a list of
+ listeners to be informed.
+ */
+ ChildrenManagerImpl (const css::uno::Reference<css::accessibility::XAccessible>& rxParent,
+ const css::uno::Reference<css::drawing::XShapes>& rxShapeList,
+ const AccessibleShapeTreeInfo& rShapeTreeInfo,
+ AccessibleContextBase& rContext);
+
+ /** If there still are managed children these are disposed and
+ released.
+ */
+ virtual ~ChildrenManagerImpl() override;
+
+ /** Do that part of the initialization that you can not or should not do
+ in the constructor like registering at broadcasters.
+ */
+ void Init();
+
+ /** Return the number of currently visible accessible children.
+ @return
+ If there are no children a 0 is returned.
+ */
+ tools::Long GetChildCount() const noexcept;
+
+ /// @throws css::uno::RuntimeException
+ /// @throws css::lang::IndexOutOfBoundsException
+ css::uno::Reference<css::drawing::XShape> GetChildShape(tools::Long nIndex);
+ /** Return the requested accessible child or throw and
+ IndexOutOfBoundsException if the given index is invalid.
+ @param nIndex
+ Index of the requested child. Call getChildCount for obtaining
+ the number of children.
+ @return
+ In case of a valid index this method returns a reference to the
+ requested accessible child. This reference is empty if it has
+ not been possible to create the accessible object of the
+ corresponding shape.
+ @throws
+ Throws an IndexOutOfBoundsException if the index is not valid.
+ */
+ css::uno::Reference<css::accessibility::XAccessible>
+ GetChild (tools::Long nIndex);
+
+ /** Return the requested accessible child.
+ @param aChildDescriptor
+ This object contains references to the original shape and its
+ associated accessible object.
+ @param _nIndex
+ The index which will be used in getAccessibleIndexInParent of the accessible shape.
+ @return
+ Returns a reference to the requested accessible child. This
+ reference is empty if it has not been possible to create the
+ accessible object of the corresponding shape.
+ @throws css::uno::RuntimeException
+ */
+ css::uno::Reference<css::accessibility::XAccessible>
+ GetChild (ChildDescriptor& aChildDescriptor,sal_Int32 _nIndex);
+
+ /** Update the child manager. Take care of a modified set of children
+ and modified visible area. This method can optimize the update
+ process with respect separate updates of a modified children list
+ and visible area.
+ @param bCreateNewObjectsOnDemand
+ If </true> then accessible objects associated with the visible
+ shapes are created only when asked for. No event is sent on
+ creation. If </false> then the accessible objects are created
+ before this method returns and events are sent to inform the
+ listeners of the new object.
+ */
+ void Update (bool bCreateNewObjectsOnDemand);
+
+ /** Set the list of UNO shapes to the given list. This removes the old
+ list and does not add to it. The list of accessible shapes that is
+ build up by calls to <member>AddAccessibleShape</member> is not
+ modified. Neither is the list of visible children. Accessible
+ objects are created on demand.
+ @param xShapeList
+ The list of UNO shapes that replaces the old list.
+ */
+ void SetShapeList (const css::uno::Reference<css::drawing::XShapes>& xShapeList);
+
+ /** Add an accessible shape. This does not modify the list of UNO shapes
+ or the list of visible shapes. Accessible shapes are, at the
+ moment, not tested against the visible area but are always appended
+ to the list of visible children.
+ @param shape
+ The new shape that is added to the list of accessible shapes; must
+ be non-null.
+ */
+ void AddAccessibleShape (css::uno::Reference<css::accessibility::XAccessible> const & shape);
+
+ /** Clear the lists of accessible shapes and that of visible accessible
+ shapes. The list of UNO shapes is not modified.
+ */
+ void ClearAccessibleShapeList();
+
+ /** Set a new event shape tree info. Call this method to inform the
+ children manager of a change of the info bundle.
+ @param rShapeTreeInfo
+ The new info that replaces the current one.
+ */
+ void SetInfo (const AccessibleShapeTreeInfo& rShapeTreeInfo);
+
+ /** Update the SELECTED and FOCUSED states of all visible children
+ according to the given selection. This includes setting
+ <em>and</em> resetting the states.
+ */
+ void UpdateSelection();
+
+ /** Return whether one of the shapes managed by this object has
+ currently the focus.
+ @return
+ Returns <true/> when there is a shape that has the focus and
+ <false/> when there is no such shape.
+ */
+ bool HasFocus() const;
+
+ /** When there is a shape that currently has the focus,
+ i.e. <member>HasFocus()</member> returns <true/> then remove the
+ focus from that shape. Otherwise nothing changes.
+ */
+ void RemoveFocus();
+
+ // lang::XEventListener
+ virtual void SAL_CALL
+ disposing (const css::lang::EventObject& rEventObject) override;
+
+ // document::XEventListener
+ virtual void SAL_CALL
+ notifyEvent (const css::document::EventObject& rEventObject) override;
+
+ // view::XSelectionChangeListener
+ virtual void SAL_CALL
+ selectionChanged (const css::lang::EventObject& rEvent) override;
+
+ // IAccessibleViewForwarderListener
+ /** Informs this children manager and its children about a change of one
+ (or more) aspect of the view forwarder.
+ @param aChangeType
+ A change type of <const>VISIBLE_AREA</const> leads to a call to
+ the <member>Update</member> which creates accessible objects of
+ new shapes immediately. Other change types are passed to the
+ visible accessible children without calling
+ <member>Update</member>.
+ @param pViewForwarder
+ The modified view forwarder. Use this one from now on.
+ */
+ virtual void ViewForwarderChanged() override;
+
+ // IAccessibleParent
+ /** Replace the specified child with a replacement.
+ @param pCurrentChild
+ This child is to be replaced.
+ @param pReplacement
+ The replacement for the current child.
+ @return
+ The returned value indicates whether the replacement has been
+ finished successfully.
+ */
+ virtual bool ReplaceChild (
+ AccessibleShape* pCurrentChild,
+ const css::uno::Reference< css::drawing::XShape >& _rxShape,
+ const tools::Long _nIndex,
+ const AccessibleShapeTreeInfo& _rShapeTreeInfo
+ ) override;
+
+ // Add the impl method for IAccessibleParent interface
+ virtual AccessibleControlShape* GetAccControlShapeFromModel
+ (css::beans::XPropertySet* pSet) override;
+ virtual css::uno::Reference<css::accessibility::XAccessible>
+ GetAccessibleCaption (const css::uno::Reference<css::drawing::XShape>& xShape) override;
+
+private:
+ /** This list holds the descriptors of all currently visible shapes and
+ associated accessible object.
+
+ <p>With the descriptors it maintains a mapping of shapes to
+ accessible objects. It acts as a cache in that accessible objects
+ are only created on demand and released with every update (where the
+ latter may be optimized by the update methods).<p>
+
+ <p>The list is realized as a vector because it remains unchanged
+ between updates (i.e. complete rebuilds of the list) and allows a
+ fast (constant time) access to its elements for given indices.</p>
+ */
+ ChildDescriptorListType maVisibleChildren;
+
+ /** The original list of UNO shapes. The visible shapes are inserted
+ into the list of visible children
+ <member>maVisibleChildren</member>.
+ */
+ css::uno::Reference<css::drawing::XShapes> mxShapeList;
+
+ /** This list of additional accessible shapes that can or shall not be
+ created by the shape factory.
+ */
+ typedef std::vector< css::uno::Reference< css::accessibility::XAccessible> > AccessibleShapeList;
+ AccessibleShapeList maAccessibleShapes;
+
+ /** Rectangle that describes the visible area in which a shape has to lie
+ at least partly, to be accessible through this class. Used to
+ detect changes of the visible area after changes of the view forwarder.
+ */
+ tools::Rectangle maVisibleArea;
+
+ /** The parent of the shapes. It is used for creating accessible
+ objects for given shapes.
+ */
+ css::uno::Reference<css::accessibility::XAccessible> mxParent;
+
+ /** Bundle of information passed down the shape tree.
+ */
+ AccessibleShapeTreeInfo maShapeTreeInfo;
+
+ /** Reference to an accessible context object that is used to inform its
+ listeners of new and removed children.
+ */
+ AccessibleContextBase& mrContext;
+
+ /** This method is called from the component helper base class while
+ disposing.
+ */
+ virtual void disposing(std::unique_lock<std::mutex>&) override;
+
+ void impl_dispose();
+
+ ChildrenManagerImpl (const ChildrenManagerImpl&) = delete;
+ ChildrenManagerImpl& operator= (const ChildrenManagerImpl&) = delete;
+
+ /** This member points to the currently focused shape. It is NULL when
+ there is no focused shape.
+ */
+ AccessibleShape* mpFocusedShape;
+
+ /** Three helper functions for the <member>Update</member> method.
+ */
+
+ /** Create a list of visible shapes from the list of UNO shapes
+ <member>maShapeList</member> and the list of accessible objects.
+ @param raChildList
+ For every visible shape from the two sources mentioned above one
+ descriptor is added to this list.
+ */
+ void CreateListOfVisibleShapes (ChildDescriptorListType& raChildList);
+
+ /** From the old list of (former) visible shapes remove those that
+ are not member of the new list. Send appropriate events for every
+ such shape.
+ @param raNewChildList
+ The new list of visible children against which the old one
+ is compared.
+ @param raOldChildList
+ The old list of visible children against which the new one
+ is compared.
+ */
+ void RemoveNonVisibleChildren (
+ const ChildDescriptorListType& raNewChildList,
+ ChildDescriptorListType& raOldChildList);
+
+ /** Merge the information that is already known about the visible shapes
+ from the current list into the new list.
+ @param raChildList
+ Information is merged from the current list of visible children
+ to this list.
+ */
+ void MergeAccessibilityInformation (ChildDescriptorListType& raChildList);
+
+ /** If the visible area has changed then send events that signal a
+ change of their bounding boxes for all shapes that are members of
+ both the current and the new list of visible shapes.
+ @param raChildList
+ Events are sent to all entries of this list that already contain
+ an accessible object.
+ */
+ static void SendVisibleAreaEvents (ChildDescriptorListType& raChildList);
+
+ /** If children have to be created immediately and not on demand the
+ create the missing accessible objects now.
+ @param raDescriptorList
+ Create an accessible object for every member of this list where
+ that object does not already exist.
+ */
+ void CreateAccessibilityObjects (ChildDescriptorListType& raChildList);
+
+ /** Add a single shape. Update all relevant data structures
+ accordingly. Use this method instead of <member>Update()</member>
+ when only a single shape has been added.
+ */
+ void AddShape (const css::uno::Reference<css::drawing::XShape>& xShape);
+
+ /** Remove a single shape. Update all relevant data structures
+ accordingly. Use this method instead of <member>Update()</member>
+ when only a single shape has been removed.
+ */
+ void RemoveShape (const css::uno::Reference<css::drawing::XShape>& xShape);
+
+ /** Add the children manager as dispose listener at the given shape so
+ that the associated accessible object can be disposed when the shape
+ is disposed.
+ @param xShape
+ Register at this shape as dispose listener.
+ */
+ void RegisterAsDisposeListener (const css::uno::Reference<css::drawing::XShape>& xShape);
+
+ /** Remove the children manager as dispose listener at the given shape
+ @param xShape
+ Unregister at this shape as dispose listener.
+ */
+ void UnregisterAsDisposeListener (const css::uno::Reference<css::drawing::XShape>& xShape);
+};
+
+
+/** A child descriptor holds a reference to a UNO shape and the
+ corresponding accessible object. There are two use cases:
+ <ol><li>The accessible object is only created on demand and is then
+ initially empty.</li>
+ <li>There is no UNO shape. The accessible object is given as argument
+ to the constructor.</li>
+ </ol>
+ In both cases the child descriptor assumes ownership over the accessible
+ object.
+*/
+class ChildDescriptor
+{
+public:
+ /** Reference to a (partially) visible shape.
+ */
+ css::uno::Reference<css::drawing::XShape> mxShape;
+
+ /** The corresponding accessible object. This reference is initially
+ empty and only replaced by a reference to a new object when that is
+ requested from the outside.
+ */
+ css::uno::Reference<css::accessibility::XAccessible> mxAccessibleShape;
+
+ /** Return a pointer to the implementation object of the accessible
+ shape of this descriptor.
+ @return
+ The result is NULL if either the UNO reference to the accessible
+ shape is empty or it can not be transformed into a pointer to
+ the desired class.
+ */
+ AccessibleShape* GetAccessibleShape() const;
+
+ /** set the index _nIndex at the accessible shape
+ @param _nIndex
+ The new index in parent.
+ */
+ void setIndexAtAccessibleShape(sal_Int32 _nIndex);
+
+ /** This flag is set during the visibility calculation and indicates
+ that at one time in this process an event is sent that informs the
+ listeners of the creation of a new accessible object. This flags is
+ not reset afterwards. Don't use it unless you know exactly what you
+ are doing.
+ */
+ bool mbCreateEventPending;
+
+ /** Create a new descriptor for the specified shape with empty reference
+ to accessible object.
+ */
+ explicit ChildDescriptor (const css::uno::Reference<css::drawing::XShape>& xShape);
+
+ /** Create a new descriptor for the specified shape with empty reference
+ to the original shape.
+ */
+ explicit ChildDescriptor (const css::uno::Reference<css::accessibility::XAccessible>& rxAccessibleShape);
+
+ /** Dispose the accessible object of this descriptor. If that object
+ does not exist then do nothing.
+ @param rParent
+ The parent of the accessible object to dispose. A child event
+ is sent in its name.
+ */
+ void disposeAccessibleObject (AccessibleContextBase& rParent);
+
+ /** Compare two child descriptors. Take into account that a child
+ descriptor may be based on a UNO shape or, already, on an accessible
+ shape.
+ */
+ bool operator == (const ChildDescriptor& aDescriptor) const
+ {
+ return (
+ this == &aDescriptor ||
+ (
+ (mxShape.get() == aDescriptor.mxShape.get() ) &&
+ (mxShape.is() || mxAccessibleShape.get() == aDescriptor.mxAccessibleShape.get())
+ )
+ );
+ }
+
+};
+
+
+} // end of namespace accessibility
+
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/accessibility/DescriptionGenerator.cxx b/svx/source/accessibility/DescriptionGenerator.cxx
new file mode 100644
index 000000000..fe4109149
--- /dev/null
+++ b/svx/source/accessibility/DescriptionGenerator.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 <DescriptionGenerator.hxx>
+#include <com/sun/star/beans/PropertyState.hpp>
+#include <com/sun/star/beans/XPropertySet.hpp>
+#include <com/sun/star/beans/XPropertyState.hpp>
+#include <com/sun/star/container/XNamed.hpp>
+#include <com/sun/star/drawing/XShape.hpp>
+#include <vcl/svapp.hxx>
+
+// Includes for string resources.
+#include <svx/strings.hrc>
+#include <svx/dialmgr.hxx>
+
+#include "lookupcolorname.hxx"
+
+using namespace ::com::sun::star;
+
+namespace accessibility
+{
+DescriptionGenerator::DescriptionGenerator(const uno::Reference<drawing::XShape>& xShape)
+ : mxShape(xShape)
+ , mxSet(mxShape, uno::UNO_QUERY)
+ , mbIsFirstProperty(true)
+{
+}
+
+DescriptionGenerator::~DescriptionGenerator() {}
+
+void DescriptionGenerator::Initialize(TranslateId pResourceId)
+{
+ // Get the string from the resource for the specified id.
+ OUString sPrefix;
+ {
+ SolarMutexGuard aGuard;
+ sPrefix = SvxResId(pResourceId);
+ }
+
+ // Forward the call with the resulting string.
+ Initialize(sPrefix);
+}
+
+void DescriptionGenerator::Initialize(std::u16string_view sPrefix)
+{
+ msDescription = sPrefix;
+ if (!mxSet.is())
+ return;
+
+ {
+ SolarMutexGuard aGuard;
+
+ msDescription.append(' ');
+ msDescription.append(SvxResId(RID_SVXSTR_A11Y_WITH));
+ msDescription.append(' ');
+
+ msDescription.append(SvxResId(RID_SVXSTR_A11Y_STYLE));
+ msDescription.append('=');
+ }
+
+ try
+ {
+ if (mxSet.is())
+ {
+ uno::Any aValue = mxSet->getPropertyValue("Style");
+ uno::Reference<container::XNamed> xStyle(aValue, uno::UNO_QUERY);
+ if (xStyle.is())
+ msDescription.append(xStyle->getName());
+ }
+ else
+ msDescription.append("<no style>");
+ }
+ catch (const css::beans::UnknownPropertyException&)
+ {
+ msDescription.append("<unknown>");
+ }
+}
+
+OUString DescriptionGenerator::operator()()
+{
+ msDescription.append('.');
+ return msDescription.makeStringAndClear();
+}
+
+void DescriptionGenerator::AddProperty(const OUString& sPropertyName, PropertyType aType)
+{
+ uno::Reference<beans::XPropertyState> xState(mxShape, uno::UNO_QUERY);
+ if (!xState.is()
+ || xState->getPropertyState(sPropertyName) == beans::PropertyState_DEFAULT_VALUE)
+ return;
+
+ if (!mxSet.is())
+ return;
+
+ // Append a separator from previous Properties.
+ if (!mbIsFirstProperty)
+ msDescription.append(',');
+ else
+ {
+ SolarMutexGuard aGuard;
+
+ msDescription.append(' ');
+ msDescription.append(SvxResId(RID_SVXSTR_A11Y_AND));
+ msDescription.append(' ');
+ mbIsFirstProperty = false;
+ }
+
+ // Delegate to type specific property handling.
+ switch (aType)
+ {
+ case PropertyType::Color:
+ AddColor(sPropertyName);
+ break;
+ case PropertyType::Integer:
+ AddInteger(sPropertyName);
+ break;
+ }
+}
+
+void DescriptionGenerator::AppendString(std::u16string_view sString)
+{
+ msDescription.append(sString);
+}
+
+/** Search for the given color in the global color table. If found append
+ its name to the description. Otherwise append its RGB tuple.
+*/
+void DescriptionGenerator::AddColor(const OUString& sPropertyName)
+{
+ msDescription.append('=');
+
+ try
+ {
+ tools::Long nValue(0);
+ if (mxSet.is())
+ {
+ uno::Any aValue = mxSet->getPropertyValue(sPropertyName);
+ aValue >>= nValue;
+ }
+
+ msDescription.append(lookUpColorName(nValue));
+ }
+ catch (const css::beans::UnknownPropertyException&)
+ {
+ msDescription.append("<unknown>");
+ }
+}
+
+void DescriptionGenerator::AddInteger(const OUString& sPropertyName)
+{
+ msDescription.append('=');
+
+ try
+ {
+ if (mxSet.is())
+ {
+ uno::Any aValue = mxSet->getPropertyValue(sPropertyName);
+ tools::Long nValue = 0;
+ aValue >>= nValue;
+ msDescription.append(nValue);
+ }
+ }
+ catch (const css::beans::UnknownPropertyException&)
+ {
+ msDescription.append("<unknown>");
+ }
+}
+
+} // end of namespace accessibility
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/accessibility/GraphCtlAccessibleContext.cxx b/svx/source/accessibility/GraphCtlAccessibleContext.cxx
new file mode 100644
index 000000000..531b75eb6
--- /dev/null
+++ b/svx/source/accessibility/GraphCtlAccessibleContext.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 <com/sun/star/accessibility/AccessibleRole.hpp>
+#include <com/sun/star/accessibility/AccessibleEventId.hpp>
+#include <com/sun/star/accessibility/AccessibleStateType.hpp>
+#include <com/sun/star/accessibility/IllegalAccessibleComponentStateException.hpp>
+#include <com/sun/star/lang/DisposedException.hpp>
+#include <com/sun/star/lang/IndexOutOfBoundsException.hpp>
+#include <unotools/accessiblestatesethelper.hxx>
+#include <cppuhelper/supportsservice.hxx>
+#include <vcl/svapp.hxx>
+#include <vcl/settings.hxx>
+#include <o3tl/safeint.hxx>
+#include <osl/mutex.hxx>
+#include <tools/gen.hxx>
+#include <svtools/colorcfg.hxx>
+#include <comphelper/accessibleeventnotifier.hxx>
+#include <svx/sdrpaintwindow.hxx>
+
+#include <svx/ShapeTypeHandler.hxx>
+#include <svx/AccessibleShapeInfo.hxx>
+#include <GraphCtlAccessibleContext.hxx>
+#include <svx/graphctl.hxx>
+#include <svx/strings.hrc>
+#include <svx/svdpage.hxx>
+#include <svx/dialmgr.hxx>
+#include <svx/sdrhittesthelper.hxx>
+
+// namespaces
+using namespace ::cppu;
+using namespace ::osl;
+using namespace ::accessibility;
+using namespace ::com::sun::star;
+using namespace ::com::sun::star::uno;
+using namespace ::com::sun::star::drawing;
+using namespace ::com::sun::star::lang;
+using namespace ::com::sun::star::accessibility;
+
+// internal
+/** initialize this component and set default values */
+SvxGraphCtrlAccessibleContext::SvxGraphCtrlAccessibleContext(
+ GraphCtrl& rRepr ) :
+
+ SvxGraphCtrlAccessibleContext_Base( m_aMutex ),
+ mpControl( &rRepr ),
+ mpModel (nullptr),
+ mpPage (nullptr),
+ mpView (nullptr),
+ mnClientId( 0 ),
+ mbDisposed( false )
+{
+ if (mpControl != nullptr)
+ {
+ mpModel = mpControl->GetSdrModel();
+ if (mpModel != nullptr)
+ mpPage = mpModel->GetPage( 0 );
+ mpView = mpControl->GetSdrView();
+
+ if( mpModel == nullptr || mpPage == nullptr || mpView == nullptr )
+ {
+ mbDisposed = true;
+ // Set all the pointers to NULL just in case they are used as
+ // a disposed flag.
+ mpModel = nullptr;
+ mpPage = nullptr;
+ mpView = nullptr;
+ }
+ }
+
+ {
+ ::SolarMutexGuard aSolarGuard;
+ msName = SvxResId( RID_SVXSTR_GRAPHCTRL_ACC_NAME );
+ msDescription = SvxResId( RID_SVXSTR_GRAPHCTRL_ACC_DESCRIPTION );
+ }
+
+ maTreeInfo.SetSdrView( mpView );
+ maTreeInfo.SetWindow(mpControl->GetDrawingArea()->get_ref_device().GetOwnerWindow());
+ maTreeInfo.SetViewForwarder( this );
+}
+
+
+/** on destruction, this component is disposed and all dispose listeners
+ are called, except if this component was already disposed */
+SvxGraphCtrlAccessibleContext::~SvxGraphCtrlAccessibleContext()
+{
+ disposing();
+}
+
+
+/** returns the XAccessible interface for a given SdrObject.
+ Multiple calls for the same SdrObject return the same XAccessible.
+*/
+Reference< XAccessible > SvxGraphCtrlAccessibleContext::getAccessible( const SdrObject* pObj )
+{
+ Reference<XAccessible> xAccessibleShape;
+
+ if( pObj )
+ {
+ // see if we already created an XAccessible for the given SdrObject
+ ShapesMapType::const_iterator iter = mxShapes.find( pObj );
+
+ if( iter != mxShapes.end() )
+ {
+ // if we already have one, return it
+ xAccessibleShape = (*iter).second.get();
+ }
+ else
+ {
+ // create a new one and remember in our internal map
+ Reference< XShape > xShape( Reference< XShape >::query( const_cast<SdrObject*>(pObj)->getUnoShape() ) );
+
+ css::uno::Reference<css::accessibility::XAccessible> xParent(getAccessibleParent());
+ AccessibleShapeInfo aShapeInfo (xShape,xParent);
+ // Create accessible object that corresponds to the descriptor's shape.
+ rtl::Reference<AccessibleShape> pAcc(ShapeTypeHandler::Instance().CreateAccessibleObject(
+ aShapeInfo, maTreeInfo));
+ xAccessibleShape = pAcc.get();
+ if (pAcc.is())
+ {
+ pAcc->Init ();
+ }
+ mxShapes[pObj] = pAcc;
+
+ // Create event and inform listeners of the object creation.
+ CommitChange( AccessibleEventId::CHILD, Any( xAccessibleShape ), Any( Reference<XAccessible>() ) );
+ }
+ }
+
+ return xAccessibleShape;
+}
+
+// XAccessible
+Reference< XAccessibleContext > SAL_CALL SvxGraphCtrlAccessibleContext::getAccessibleContext()
+{
+ return this;
+}
+
+// XAccessibleComponent
+sal_Bool SAL_CALL SvxGraphCtrlAccessibleContext::containsPoint( const awt::Point& rPoint )
+{
+ // no guard -> done in getSize()
+ awt::Size aSize (getSize());
+ return (rPoint.X >= 0)
+ && (rPoint.X < aSize.Width)
+ && (rPoint.Y >= 0)
+ && (rPoint.Y < aSize.Height);
+}
+
+
+Reference< XAccessible > SAL_CALL SvxGraphCtrlAccessibleContext::getAccessibleAtPoint( const awt::Point& rPoint )
+{
+ ::osl::MutexGuard aGuard( m_aMutex );
+
+ Reference< XAccessible > xAccessible;
+
+ if( !mpControl )
+ {
+ throw DisposedException();
+ }
+
+ Point aPnt( rPoint.X, rPoint.Y );
+ aPnt = mpControl->GetDrawingArea()->get_ref_device().PixelToLogic(aPnt);
+
+ SdrObject* pObj = nullptr;
+
+ if(mpView && mpView->GetSdrPageView())
+ {
+ pObj = SdrObjListPrimitiveHit(*mpPage, aPnt, 1, *mpView->GetSdrPageView(), nullptr, false);
+ }
+
+ if( pObj )
+ xAccessible = getAccessible( pObj );
+
+ return xAccessible;
+}
+
+awt::Rectangle SAL_CALL SvxGraphCtrlAccessibleContext::getBounds()
+{
+ const SolarMutexGuard aSolarGuard;
+
+ if (nullptr == mpControl)
+ throw DisposedException();
+
+ const Point aOutPos;
+ const Size aOutSize( mpControl->GetOutputSizePixel() );
+ awt::Rectangle aRet;
+
+ aRet.X = aOutPos.X();
+ aRet.Y = aOutPos.Y();
+ aRet.Width = aOutSize.Width();
+ aRet.Height = aOutSize.Height();
+
+ return aRet;
+}
+
+awt::Point SAL_CALL SvxGraphCtrlAccessibleContext::getLocation()
+{
+ const SolarMutexGuard aSolarGuard;
+
+ if (nullptr == mpControl)
+ throw DisposedException();
+
+ const awt::Rectangle aRect( getBounds() );
+ awt::Point aRet;
+
+ aRet.X = aRect.X;
+ aRet.Y = aRect.Y;
+
+ return aRet;
+}
+
+awt::Point SAL_CALL SvxGraphCtrlAccessibleContext::getLocationOnScreen()
+{
+ const SolarMutexGuard aSolarGuard;
+
+ if (nullptr == mpControl)
+ throw DisposedException();
+
+ awt::Point aScreenLoc(0, 0);
+
+ auto xParent(getAccessibleParent());
+ if (xParent)
+ {
+ css::uno::Reference<css::accessibility::XAccessibleContext> xParentContext(xParent->getAccessibleContext());
+ css::uno::Reference<css::accessibility::XAccessibleComponent> xParentComponent(xParentContext, css::uno::UNO_QUERY);
+ OSL_ENSURE( xParentComponent.is(), "ValueSetAcc::getLocationOnScreen: no parent component!" );
+ if ( xParentComponent.is() )
+ {
+ awt::Point aParentScreenLoc( xParentComponent->getLocationOnScreen() );
+ awt::Point aOwnRelativeLoc( getLocation() );
+ aScreenLoc.X = aParentScreenLoc.X + aOwnRelativeLoc.X;
+ aScreenLoc.Y = aParentScreenLoc.Y + aOwnRelativeLoc.Y;
+ }
+ }
+
+ return aScreenLoc;
+}
+
+awt::Size SAL_CALL SvxGraphCtrlAccessibleContext::getSize()
+{
+ const SolarMutexGuard aSolarGuard;
+
+ if (nullptr == mpControl)
+ throw DisposedException();
+
+ const awt::Rectangle aRect( getBounds() );
+ awt::Size aRet;
+
+ aRet.Width = aRect.Width;
+ aRet.Height = aRect.Height;
+
+ return aRet;
+}
+
+// XAccessibleContext
+sal_Int32 SAL_CALL SvxGraphCtrlAccessibleContext::getAccessibleChildCount()
+{
+ ::SolarMutexGuard aGuard;
+
+ if( nullptr == mpPage )
+ throw DisposedException();
+
+ return mpPage->GetObjCount();
+}
+
+
+/** returns the SdrObject at index nIndex from the model of this graph */
+SdrObject* SvxGraphCtrlAccessibleContext::getSdrObject( sal_Int32 nIndex )
+{
+ ::SolarMutexGuard aGuard;
+
+ if( nullptr == mpPage )
+ throw DisposedException();
+
+ if( (nIndex < 0) || ( o3tl::make_unsigned(nIndex) >= mpPage->GetObjCount() ) )
+ throw lang::IndexOutOfBoundsException();
+
+ return mpPage->GetObj( nIndex );
+}
+
+
+/** sends an AccessibleEventObject to all added XAccessibleEventListeners */
+void SvxGraphCtrlAccessibleContext::CommitChange (
+ sal_Int16 nEventId,
+ const uno::Any& rNewValue,
+ const uno::Any& rOldValue)
+{
+ AccessibleEventObject aEvent (
+ static_cast<uno::XWeak*>(this),
+ nEventId,
+ rNewValue,
+ rOldValue);
+
+ if (mnClientId)
+ comphelper::AccessibleEventNotifier::addEvent( mnClientId, aEvent );
+}
+
+Reference< XAccessible > SAL_CALL SvxGraphCtrlAccessibleContext::getAccessibleChild( sal_Int32 nIndex )
+{
+ ::SolarMutexGuard aGuard;
+
+ return getAccessible( getSdrObject( nIndex ) );
+}
+
+Reference< XAccessible > SAL_CALL SvxGraphCtrlAccessibleContext::getAccessibleParent()
+{
+ ::SolarMutexGuard aGuard;
+
+ if( nullptr == mpControl )
+ throw DisposedException();
+
+ return mpControl->GetDrawingArea()->get_accessible_parent();
+}
+
+sal_Int32 SAL_CALL SvxGraphCtrlAccessibleContext::getAccessibleIndexInParent()
+{
+ ::SolarMutexGuard aGuard;
+ // Use a simple but slow solution for now. Optimize later.
+
+ // Iterate over all the parent's children and search for this object.
+ css::uno::Reference<css::accessibility::XAccessible> xParent(getAccessibleParent());
+ if (xParent.is())
+ {
+ Reference< XAccessibleContext > xParentContext( xParent->getAccessibleContext() );
+ if( xParentContext.is() )
+ {
+ sal_Int32 nChildCount = xParentContext->getAccessibleChildCount();
+ for( sal_Int32 i = 0 ; i < nChildCount ; ++i )
+ {
+ Reference< XAccessible > xChild( xParentContext->getAccessibleChild( i ) );
+ if( xChild.is() )
+ {
+ Reference< XAccessibleContext > xChildContext = xChild->getAccessibleContext();
+ if( xChildContext == static_cast<XAccessibleContext*>(this) )
+ return i;
+ }
+ }
+ }
+ }
+
+ // Return -1 to indicate that this object's parent does not know about the
+ // object.
+ return -1;
+}
+
+
+sal_Int16 SAL_CALL SvxGraphCtrlAccessibleContext::getAccessibleRole()
+{
+ return AccessibleRole::PANEL;
+}
+
+
+OUString SAL_CALL SvxGraphCtrlAccessibleContext::getAccessibleDescription()
+{
+ ::SolarMutexGuard aGuard;
+ return msDescription;
+}
+
+
+OUString SAL_CALL SvxGraphCtrlAccessibleContext::getAccessibleName()
+{
+ ::SolarMutexGuard aGuard;
+ return msName;
+}
+
+
+/** Return empty reference to indicate that the relation set is not
+ supported.
+*/
+Reference< XAccessibleRelationSet > SAL_CALL SvxGraphCtrlAccessibleContext::getAccessibleRelationSet()
+{
+ return Reference< XAccessibleRelationSet >();
+}
+
+
+Reference< XAccessibleStateSet > SAL_CALL SvxGraphCtrlAccessibleContext::getAccessibleStateSet()
+{
+ ::SolarMutexGuard aGuard;
+
+ rtl::Reference<utl::AccessibleStateSetHelper> pStateSetHelper = new utl::AccessibleStateSetHelper;
+
+ if ( rBHelper.bDisposed || mbDisposed )
+ {
+ pStateSetHelper->AddState( AccessibleStateType::DEFUNC );
+ }
+ else
+ {
+ pStateSetHelper->AddState( AccessibleStateType::FOCUSABLE );
+ if( mpControl->HasFocus() )
+ pStateSetHelper->AddState( AccessibleStateType::FOCUSED );
+ pStateSetHelper->AddState( AccessibleStateType::OPAQUE );
+ pStateSetHelper->AddState( AccessibleStateType::SHOWING );
+ pStateSetHelper->AddState( AccessibleStateType::VISIBLE );
+ }
+
+ return pStateSetHelper;
+}
+
+
+lang::Locale SAL_CALL SvxGraphCtrlAccessibleContext::getLocale()
+{
+ ::SolarMutexGuard aGuard;
+
+ css::uno::Reference<css::accessibility::XAccessible> xParent(getAccessibleParent());
+ if (xParent.is())
+ {
+ Reference< XAccessibleContext > xParentContext( xParent->getAccessibleContext() );
+ if( xParentContext.is() )
+ return xParentContext->getLocale();
+ }
+
+ // No parent. Therefore throw exception to indicate this cluelessness.
+ throw IllegalAccessibleComponentStateException();
+}
+
+// XAccessibleEventListener
+void SAL_CALL SvxGraphCtrlAccessibleContext::addAccessibleEventListener( const Reference< XAccessibleEventListener >& xListener )
+{
+ if (xListener.is())
+ {
+ ::SolarMutexGuard aGuard;
+ if (!mnClientId)
+ mnClientId = comphelper::AccessibleEventNotifier::registerClient( );
+ comphelper::AccessibleEventNotifier::addEventListener( mnClientId, xListener );
+ }
+}
+
+
+void SAL_CALL SvxGraphCtrlAccessibleContext::removeAccessibleEventListener( const Reference< XAccessibleEventListener >& xListener )
+{
+ if (!xListener.is())
+ return;
+
+ ::SolarMutexGuard aGuard;
+
+ sal_Int32 nListenerCount = comphelper::AccessibleEventNotifier::removeEventListener( mnClientId, 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( mnClientId );
+ mnClientId = 0;
+ }
+}
+
+void SAL_CALL SvxGraphCtrlAccessibleContext::grabFocus()
+{
+ ::SolarMutexGuard aGuard;
+
+ if( nullptr == mpControl )
+ throw DisposedException();
+
+ mpControl->GrabFocus();
+}
+
+sal_Int32 SAL_CALL SvxGraphCtrlAccessibleContext::getForeground()
+{
+ svtools::ColorConfig aColorConfig;
+ Color nColor = aColorConfig.GetColorValue( svtools::FONTCOLOR ).nColor;
+ return static_cast<sal_Int32>(nColor);
+}
+
+sal_Int32 SAL_CALL SvxGraphCtrlAccessibleContext::getBackground()
+{
+ Color nColor = Application::GetSettings().GetStyleSettings().GetWindowColor();
+ return static_cast<sal_Int32>(nColor);
+}
+
+// XServiceInfo
+OUString SAL_CALL SvxGraphCtrlAccessibleContext::getImplementationName()
+{
+ return "com.sun.star.comp.ui.SvxGraphCtrlAccessibleContext";
+}
+
+sal_Bool SAL_CALL SvxGraphCtrlAccessibleContext::supportsService( const OUString& sServiceName )
+{
+ return cppu::supportsService(this, sServiceName);
+}
+
+Sequence< OUString > SAL_CALL SvxGraphCtrlAccessibleContext::getSupportedServiceNames()
+{
+ return { "com.sun.star.accessibility.Accessible",
+ "com.sun.star.accessibility.AccessibleContext",
+ "com.sun.star.drawing.AccessibleGraphControl" };
+}
+
+// XTypeProvider
+Sequence<sal_Int8> SAL_CALL SvxGraphCtrlAccessibleContext::getImplementationId()
+{
+ return css::uno::Sequence<sal_Int8>();
+}
+
+// XServiceName
+OUString SvxGraphCtrlAccessibleContext::getServiceName()
+{
+ return "com.sun.star.accessibility.AccessibleContext";
+}
+
+// XAccessibleSelection
+void SAL_CALL SvxGraphCtrlAccessibleContext::selectAccessibleChild( sal_Int32 nIndex )
+{
+ ::SolarMutexGuard aGuard;
+
+ if( nullptr == mpView )
+ throw DisposedException();
+
+ SdrObject* pObj = getSdrObject( nIndex );
+
+ if( pObj )
+ mpView->MarkObj( pObj, mpView->GetSdrPageView());
+}
+
+
+sal_Bool SAL_CALL SvxGraphCtrlAccessibleContext::isAccessibleChildSelected( sal_Int32 nIndex )
+{
+ ::SolarMutexGuard aGuard;
+
+ if( nullptr == mpView )
+ throw DisposedException();
+
+ return mpView->IsObjMarked( getSdrObject( nIndex ) );
+}
+
+
+void SAL_CALL SvxGraphCtrlAccessibleContext::clearAccessibleSelection()
+{
+ ::SolarMutexGuard aGuard;
+
+ if( nullptr == mpView )
+ throw DisposedException();
+
+ mpView->UnmarkAllObj();
+}
+
+
+void SAL_CALL SvxGraphCtrlAccessibleContext::selectAllAccessibleChildren()
+{
+ ::SolarMutexGuard aGuard;
+
+ if( nullptr == mpView )
+ throw DisposedException();
+
+ mpView->MarkAllObj();
+}
+
+
+sal_Int32 SAL_CALL SvxGraphCtrlAccessibleContext::getSelectedAccessibleChildCount()
+{
+ ::SolarMutexGuard aGuard;
+
+ if( nullptr == mpView )
+ throw DisposedException();
+
+ const SdrMarkList& rList = mpView->GetMarkedObjectList();
+ return static_cast<sal_Int32>(rList.GetMarkCount());
+}
+
+
+Reference< XAccessible > SAL_CALL SvxGraphCtrlAccessibleContext::getSelectedAccessibleChild( sal_Int32 nIndex )
+{
+ ::SolarMutexGuard aGuard;
+
+ checkChildIndexOnSelection( nIndex );
+
+ Reference< XAccessible > xAccessible;
+
+ const SdrMarkList& rList = mpView->GetMarkedObjectList();
+ SdrObject* pObj = rList.GetMark(static_cast<size_t>(nIndex))->GetMarkedSdrObj();
+ if( pObj )
+ xAccessible = getAccessible( pObj );
+
+ return xAccessible;
+}
+
+
+void SAL_CALL SvxGraphCtrlAccessibleContext::deselectAccessibleChild( sal_Int32 nIndex )
+{
+ ::SolarMutexGuard aGuard;
+
+ checkChildIndexOnSelection( nIndex );
+
+ if( !mpView )
+ return;
+
+ const SdrMarkList& rList = mpView->GetMarkedObjectList();
+
+ SdrObject* pObj = getSdrObject( nIndex );
+ if( !pObj )
+ return;
+
+ SdrMarkList aRefList( rList );
+
+ SdrPageView* pPV = mpView->GetSdrPageView();
+ mpView->UnmarkAllObj( pPV );
+
+ const size_t nCount = aRefList.GetMarkCount();
+ for( size_t nMark = 0; nMark < nCount; ++nMark )
+ {
+ if( aRefList.GetMark(nMark)->GetMarkedSdrObj() != pObj )
+ mpView->MarkObj( aRefList.GetMark(nMark)->GetMarkedSdrObj(), pPV );
+ }
+}
+
+// internals
+void SvxGraphCtrlAccessibleContext::checkChildIndexOnSelection( tools::Long nIndex )
+{
+ if( nIndex < 0 || nIndex >= getSelectedAccessibleChildCount() )
+ throw lang::IndexOutOfBoundsException();
+}
+
+
+/** Replace the model, page, and view pointers by the ones provided
+ (explicitly and implicitly).
+*/
+void SvxGraphCtrlAccessibleContext::setModelAndView (
+ SdrModel* pModel,
+ SdrView* pView)
+{
+ ::SolarMutexGuard aGuard;
+
+ mpModel = pModel;
+ if (mpModel != nullptr)
+ mpPage = mpModel->GetPage( 0 );
+ mpView = pView;
+
+ if (mpModel == nullptr || mpPage == nullptr || mpView == nullptr)
+ {
+ mbDisposed = true;
+
+ // Set all the pointers to NULL just in case they are used as
+ // a disposed flag.
+ mpModel = nullptr;
+ mpPage = nullptr;
+ mpView = nullptr;
+ }
+
+ maTreeInfo.SetSdrView (mpView);
+}
+
+
+void SAL_CALL SvxGraphCtrlAccessibleContext::disposing()
+{
+ ::SolarMutexGuard aGuard;
+
+ if( mbDisposed )
+ return;
+
+ mbDisposed = true;
+
+ mpControl = nullptr; // object dies with representation
+ mpView = nullptr;
+ mpPage = nullptr;
+
+ {
+ for (const auto& rEntry : mxShapes)
+ {
+ rtl::Reference<XAccessible> pAcc(rEntry.second);
+ Reference< XComponent > xComp( pAcc.get(), UNO_QUERY );
+ if( xComp.is() )
+ xComp->dispose();
+ }
+
+ mxShapes.clear();
+ }
+
+ // Send a disposing to all listeners.
+ if ( mnClientId )
+ {
+ comphelper::AccessibleEventNotifier::revokeClientNotifyDisposing( mnClientId, *this );
+ mnClientId = 0;
+ }
+}
+
+void SvxGraphCtrlAccessibleContext::Notify( SfxBroadcaster& /*rBC*/, const SfxHint& rHint )
+{
+ if (rHint.GetId() == SfxHintId::ThisIsAnSdrHint)
+ {
+ const SdrHint* pSdrHint = static_cast<const SdrHint*>( &rHint );
+ switch( pSdrHint->GetKind() )
+ {
+ case SdrHintKind::ObjectChange:
+ {
+ ShapesMapType::iterator iter = mxShapes.find( pSdrHint->GetObject() );
+
+ if( iter != mxShapes.end() )
+ {
+ // if we already have one, return it
+ rtl::Reference<AccessibleShape> pShape((*iter).second);
+
+ if( pShape.is() )
+ pShape->CommitChange( AccessibleEventId::VISIBLE_DATA_CHANGED, uno::Any(), uno::Any() );
+ }
+ }
+ break;
+
+ case SdrHintKind::ObjectInserted:
+ CommitChange( AccessibleEventId::CHILD, Any( getAccessible( pSdrHint->GetObject() ) ) , uno::Any());
+ break;
+ case SdrHintKind::ObjectRemoved:
+ CommitChange( AccessibleEventId::CHILD, uno::Any(), Any( getAccessible( pSdrHint->GetObject() ) ) );
+ break;
+ case SdrHintKind::ModelCleared:
+ dispose();
+ break;
+ default:
+ break;
+ }
+ }
+ else
+ {
+ // Has our SdDrawDocument just died?
+ if(rHint.GetId() == SfxHintId::Dying)
+ {
+ dispose();
+ }
+ }
+}
+
+// IAccessibleViewforwarder
+tools::Rectangle SvxGraphCtrlAccessibleContext::GetVisibleArea() const
+{
+ tools::Rectangle aVisArea;
+
+ if( mpView && mpView->PaintWindowCount())
+ {
+ SdrPaintWindow* pPaintWindow = mpView->GetPaintWindow(0);
+ aVisArea = pPaintWindow->GetVisibleArea();
+ }
+
+ return aVisArea;
+}
+
+Point SvxGraphCtrlAccessibleContext::LogicToPixel (const Point& rPoint) const
+{
+ if( mpControl )
+ {
+ return mpControl->GetDrawingArea()->get_ref_device().LogicToPixel (rPoint) + mpControl->GetPositionInDialog();
+ }
+ else
+ {
+ return rPoint;
+ }
+}
+
+Size SvxGraphCtrlAccessibleContext::LogicToPixel (const Size& rSize) const
+{
+ if( mpControl )
+ return mpControl->GetDrawingArea()->get_ref_device().LogicToPixel(rSize);
+ else
+ return rSize;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/accessibility/ShapeTypeHandler.cxx b/svx/source/accessibility/ShapeTypeHandler.cxx
new file mode 100644
index 000000000..1b169c761
--- /dev/null
+++ b/svx/source/accessibility/ShapeTypeHandler.cxx
@@ -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 .
+ */
+
+
+#include <svx/ShapeTypeHandler.hxx>
+#include <svx/SvxShapeTypes.hxx>
+#include <svx/AccessibleShapeInfo.hxx>
+#include <vcl/svapp.hxx>
+#include <svx/AccessibleShape.hxx>
+#include <svx/dialmgr.hxx>
+
+#include <svx/svdoashp.hxx>
+
+#include <svx/strings.hrc>
+
+using namespace ::com::sun::star;
+using namespace ::com::sun::star::accessibility;
+
+namespace accessibility {
+
+// Pointer to the shape type handler singleton.
+ShapeTypeHandler* ShapeTypeHandler::instance = nullptr;
+
+
+// Create an empty reference to an accessible object.
+static rtl::Reference<AccessibleShape>
+ CreateEmptyShapeReference (
+ const AccessibleShapeInfo& /*rShapeInfo*/,
+ const AccessibleShapeTreeInfo& /*rShapeTreeInfo*/,
+ ShapeTypeId /*nId*/)
+{
+ return nullptr;
+}
+
+
+ShapeTypeHandler& ShapeTypeHandler::Instance()
+{
+ // Using double check pattern to make sure that exactly one instance of
+ // the shape type handler is instantiated.
+ if (instance == nullptr)
+ {
+ SolarMutexGuard aGuard;
+ if (instance == nullptr)
+ {
+ // Create the single instance of the shape type handler.
+ instance = new ShapeTypeHandler;
+
+ // Register the basic SVX shape types.
+ RegisterDrawShapeTypes ();
+ }
+ }
+
+ return *instance;
+}
+
+
+/** The given service name is first transformed into a slot id that
+ identifies the place of the type descriptor. From that descriptor the
+ shape type id is returned.
+*/
+ShapeTypeId ShapeTypeHandler::GetTypeId (const OUString& aServiceName) const
+{
+ tServiceNameToSlotId::const_iterator I (maServiceNameToSlotId.find (aServiceName));
+ if (I != maServiceNameToSlotId.end())
+ {
+ return maShapeTypeDescriptorList[I->second].mnShapeTypeId;
+ }
+ else
+ return -1;
+}
+
+
+/** Extract the specified shape's service name and forward the request to
+ the appropriate method.
+*/
+ShapeTypeId ShapeTypeHandler::GetTypeId (const uno::Reference<drawing::XShape>& rxShape) const
+{
+ if (rxShape.is())
+ return GetTypeId (rxShape->getShapeType());
+ else
+ return -1;
+}
+
+
+/** This factory method determines the type descriptor for the type of the
+ given shape, then calls the descriptor's create function, and finally
+ initializes the new object.
+*/
+rtl::Reference<AccessibleShape>
+ ShapeTypeHandler::CreateAccessibleObject (
+ const AccessibleShapeInfo& rShapeInfo,
+ const AccessibleShapeTreeInfo& rShapeTreeInfo) const
+{
+ ShapeTypeId nSlotId (GetSlotId (rShapeInfo.mxShape));
+ rtl::Reference<AccessibleShape> pShape(
+ maShapeTypeDescriptorList[nSlotId].maCreateFunction (
+ rShapeInfo,
+ rShapeTreeInfo,
+ maShapeTypeDescriptorList[nSlotId].mnShapeTypeId));
+ return pShape;
+}
+
+
+/** Create the single instance of this class and initialize its list of
+ type descriptors with an entry of an unknown type.
+*/
+ShapeTypeHandler::ShapeTypeHandler()
+ : maShapeTypeDescriptorList (1)
+{
+ // Make sure that at least the UNKNOWN entry is present.
+ // Resize the list, if necessary, so that the new type can be inserted.
+ maShapeTypeDescriptorList[0].mnShapeTypeId = UNKNOWN_SHAPE_TYPE;
+ maShapeTypeDescriptorList[0].msServiceName = "UNKNOWN_SHAPE_TYPE";
+ maShapeTypeDescriptorList[0].maCreateFunction = CreateEmptyShapeReference;
+ maServiceNameToSlotId[maShapeTypeDescriptorList[0].msServiceName] = 0;
+}
+
+
+ShapeTypeHandler::~ShapeTypeHandler()
+{
+ // Because this class is a singleton and the only instance, whose
+ // destructor has just been called, is pointed to from instance,
+ // we reset the static variable instance, so that further calls to
+ // getInstance do not return an undefined object but create a new
+ // singleton.
+ instance = nullptr;
+}
+
+
+void ShapeTypeHandler::AddShapeTypeList (int nDescriptorCount,
+ ShapeTypeDescriptor const aDescriptorList[])
+{
+ SolarMutexGuard aGuard;
+
+ // Determine first id of new type descriptor(s).
+ int nFirstId = maShapeTypeDescriptorList.size();
+
+ // Resize the list, if necessary, so that the types can be inserted.
+ maShapeTypeDescriptorList.resize (nFirstId + nDescriptorCount);
+
+ for (int i=0; i<nDescriptorCount; i++)
+ {
+ // Fill Type descriptor.
+ maShapeTypeDescriptorList[nFirstId+i].mnShapeTypeId = aDescriptorList[i].mnShapeTypeId;
+ maShapeTypeDescriptorList[nFirstId+i].msServiceName = aDescriptorList[i].msServiceName;
+ maShapeTypeDescriptorList[nFirstId+i].maCreateFunction = aDescriptorList[i].maCreateFunction;
+
+ // Update inverse mapping from service name to the descriptor's position.
+ maServiceNameToSlotId[aDescriptorList[i].msServiceName] = nFirstId+i;
+ }
+}
+
+
+tools::Long ShapeTypeHandler::GetSlotId (const OUString& aServiceName) const
+{
+ tServiceNameToSlotId::const_iterator I (maServiceNameToSlotId.find (aServiceName));
+ if (I != maServiceNameToSlotId.end())
+ return I->second;
+ else
+ return 0;
+}
+
+
+// Extract the given shape's service name and forward request to appropriate
+// method.
+tools::Long ShapeTypeHandler::GetSlotId (const uno::Reference<drawing::XShape>& rxShape) const
+{
+ if (rxShape.is())
+ return GetSlotId (rxShape->getShapeType());
+ else
+ return 0;
+}
+
+/// get the accessible base name for an object
+OUString ShapeTypeHandler::CreateAccessibleBaseName (const uno::Reference<drawing::XShape>& rxShape)
+{
+ TranslateId pResourceId;
+ OUString sName;
+
+ switch (ShapeTypeHandler::Instance().GetTypeId (rxShape))
+ {
+ // case DRAWING_3D_POLYGON: was removed in original code in
+ // AccessibleShape::CreateAccessibleBaseName. See issue 11190 for details.
+ // Id can be removed from SvxShapeTypes.hxx as well.
+ case DRAWING_3D_CUBE:
+ pResourceId = STR_ObjNameSingulCube3d;
+ break;
+ case DRAWING_3D_EXTRUDE:
+ pResourceId = STR_ObjNameSingulExtrude3d;
+ break;
+ case DRAWING_3D_LATHE:
+ pResourceId = STR_ObjNameSingulLathe3d;
+ break;
+ case DRAWING_3D_SCENE:
+ pResourceId = STR_ObjNameSingulScene3d;
+ break;
+ case DRAWING_3D_SPHERE:
+ pResourceId = STR_ObjNameSingulSphere3d;
+ break;
+ case DRAWING_CAPTION:
+ pResourceId = STR_ObjNameSingulCAPTION;
+ break;
+ case DRAWING_CLOSED_BEZIER:
+ pResourceId = STR_ObjNameSingulPATHFILL;
+ break;
+ case DRAWING_CLOSED_FREEHAND:
+ pResourceId = STR_ObjNameSingulFREEFILL;
+ break;
+ case DRAWING_CONNECTOR:
+ pResourceId = STR_ObjNameSingulEDGE;
+ break;
+ case DRAWING_CONTROL:
+ pResourceId = STR_ObjNameSingulUno;
+ break;
+ case DRAWING_ELLIPSE:
+ pResourceId = STR_ObjNameSingulCIRCE;
+ break;
+ case DRAWING_GROUP:
+ pResourceId = STR_ObjNameSingulGRUP;
+ break;
+ case DRAWING_LINE:
+ pResourceId = STR_ObjNameSingulLINE;
+ break;
+ case DRAWING_MEASURE:
+ pResourceId = STR_ObjNameSingulMEASURE;
+ break;
+ case DRAWING_OPEN_BEZIER:
+ pResourceId = STR_ObjNameSingulPATHLINE;
+ break;
+ case DRAWING_OPEN_FREEHAND:
+ pResourceId = STR_ObjNameSingulFREELINE;
+ break;
+ case DRAWING_PAGE:
+ pResourceId = STR_ObjNameSingulPAGE;
+ break;
+ case DRAWING_POLY_LINE:
+ pResourceId = STR_ObjNameSingulPLIN;
+ break;
+ case DRAWING_POLY_LINE_PATH:
+ pResourceId = STR_ObjNameSingulPLIN;
+ break;
+ case DRAWING_POLY_POLYGON:
+ pResourceId = STR_ObjNameSingulPOLY;
+ break;
+ case DRAWING_POLY_POLYGON_PATH:
+ pResourceId = STR_ObjNameSingulPOLY;
+ break;
+ case DRAWING_RECTANGLE:
+ pResourceId = STR_ObjNameSingulRECT;
+ break;
+ case DRAWING_CUSTOM:
+ pResourceId = STR_ObjNameSingulCUSTOMSHAPE;
+
+ if (SdrObject* pSdrObject = SdrObject::getSdrObjectFromXShape(rxShape))
+ {
+ if (auto pCustomShape = dynamic_cast<SdrObjCustomShape*>(pSdrObject))
+ {
+ if (pCustomShape->IsTextPath())
+ pResourceId = STR_ObjNameSingulFONTWORK;
+ else
+ {
+ pResourceId = {};
+ sName = pCustomShape->GetCustomShapeName();
+ }
+ }
+ }
+ break;
+ case DRAWING_TEXT:
+ pResourceId = STR_ObjNameSingulTEXT;
+ break;
+ default:
+ pResourceId = {};
+ sName = "UnknownAccessibleShape";
+ if (rxShape.is())
+ sName += ": " + rxShape->getShapeType();
+ break;
+ }
+
+ if (pResourceId)
+ {
+ SolarMutexGuard aGuard;
+ sName = SvxResId(pResourceId);
+ }
+
+ return sName;
+}
+
+} // end of namespace accessibility
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/accessibility/SvxShapeTypes.cxx b/svx/source/accessibility/SvxShapeTypes.cxx
new file mode 100644
index 000000000..b02a153a4
--- /dev/null
+++ b/svx/source/accessibility/SvxShapeTypes.cxx
@@ -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 .
+ */
+
+
+#include <svx/SvxShapeTypes.hxx>
+#include <svx/AccessibleShape.hxx>
+#include <svx/AccessibleGraphicShape.hxx>
+#include <svx/AccessibleOLEShape.hxx>
+#include <svx/AccessibleControlShape.hxx>
+#include <svx/ShapeTypeHandler.hxx>
+#include <AccessibleTableShape.hxx>
+
+namespace accessibility {
+
+static rtl::Reference<AccessibleShape> CreateSvxAccessibleShape (
+ const AccessibleShapeInfo& rShapeInfo,
+ const AccessibleShapeTreeInfo& rShapeTreeInfo,
+ ShapeTypeId nId)
+{
+ switch (nId)
+ {
+ case DRAWING_3D_CUBE:
+ case DRAWING_3D_EXTRUDE:
+ case DRAWING_3D_LATHE:
+ case DRAWING_3D_SCENE:
+ case DRAWING_3D_SPHERE:
+ case DRAWING_CAPTION:
+ case DRAWING_CLOSED_BEZIER:
+ case DRAWING_CLOSED_FREEHAND:
+ case DRAWING_CONNECTOR:
+ case DRAWING_ELLIPSE:
+ case DRAWING_GROUP:
+ case DRAWING_LINE:
+ case DRAWING_MEASURE:
+ case DRAWING_OPEN_BEZIER:
+ case DRAWING_OPEN_FREEHAND:
+ case DRAWING_PAGE:
+ case DRAWING_POLY_POLYGON:
+ case DRAWING_POLY_LINE:
+ case DRAWING_POLY_POLYGON_PATH:
+ case DRAWING_POLY_LINE_PATH:
+ case DRAWING_RECTANGLE:
+ case DRAWING_TEXT:
+ // Default accessibility shape for
+ // css::drawing::CustomShape (#i37790#)
+ case DRAWING_CUSTOM:
+ // Default accessibility shape for
+ // css::drawing::MediaShape (#i85429#)
+ case DRAWING_MEDIA:
+ return new AccessibleShape (rShapeInfo, rShapeTreeInfo);
+
+ case DRAWING_CONTROL:
+ return new AccessibleControlShape (rShapeInfo, rShapeTreeInfo);
+
+ case DRAWING_GRAPHIC_OBJECT:
+ return new AccessibleGraphicShape (rShapeInfo, rShapeTreeInfo);
+
+ case DRAWING_APPLET:
+ case DRAWING_FRAME:
+ case DRAWING_OLE:
+ case DRAWING_PLUGIN:
+ return new AccessibleOLEShape (rShapeInfo, rShapeTreeInfo);
+
+ case DRAWING_TABLE:
+ return new AccessibleTableShape( rShapeInfo, rShapeTreeInfo );
+
+ default:
+ return nullptr;
+ }
+}
+
+void RegisterDrawShapeTypes()
+{
+ /** List of shape type descriptors corresponding to the
+ <type>SvxShapeTypes</type> enum.
+ */
+ static ShapeTypeDescriptor const aSvxShapeTypeList[] = {
+ ShapeTypeDescriptor ( DRAWING_TEXT, "com.sun.star.drawing.TextShape",
+ CreateSvxAccessibleShape),
+ ShapeTypeDescriptor (DRAWING_RECTANGLE, "com.sun.star.drawing.RectangleShape",
+ CreateSvxAccessibleShape),
+ ShapeTypeDescriptor ( DRAWING_ELLIPSE, "com.sun.star.drawing.EllipseShape",
+ CreateSvxAccessibleShape ),
+ ShapeTypeDescriptor ( DRAWING_CONTROL, "com.sun.star.drawing.ControlShape",
+ CreateSvxAccessibleShape ),
+ ShapeTypeDescriptor ( DRAWING_CONNECTOR, "com.sun.star.drawing.ConnectorShape",
+ CreateSvxAccessibleShape ),
+ ShapeTypeDescriptor ( DRAWING_MEASURE, "com.sun.star.drawing.MeasureShape",
+ CreateSvxAccessibleShape ),
+ ShapeTypeDescriptor ( DRAWING_LINE, "com.sun.star.drawing.LineShape",
+ CreateSvxAccessibleShape ),
+ ShapeTypeDescriptor ( DRAWING_POLY_POLYGON, "com.sun.star.drawing.PolyPolygonShape",
+ CreateSvxAccessibleShape ),
+ ShapeTypeDescriptor ( DRAWING_POLY_LINE, "com.sun.star.drawing.PolyLineShape",
+ CreateSvxAccessibleShape ),
+ ShapeTypeDescriptor ( DRAWING_OPEN_BEZIER, "com.sun.star.drawing.OpenBezierShape",
+ CreateSvxAccessibleShape ),
+ ShapeTypeDescriptor ( DRAWING_CLOSED_BEZIER, "com.sun.star.drawing.ClosedBezierShape",
+ CreateSvxAccessibleShape ),
+ ShapeTypeDescriptor ( DRAWING_OPEN_FREEHAND, "com.sun.star.drawing.OpenFreeHandShape",
+ CreateSvxAccessibleShape ),
+ ShapeTypeDescriptor ( DRAWING_CLOSED_FREEHAND, "com.sun.star.drawing.ClosedFreeHandShape",
+ CreateSvxAccessibleShape ),
+ ShapeTypeDescriptor ( DRAWING_POLY_POLYGON_PATH, "com.sun.star.drawing.PolyPolygonPathShape",
+ CreateSvxAccessibleShape ),
+ ShapeTypeDescriptor ( DRAWING_POLY_LINE_PATH, "com.sun.star.drawing.PolyLinePathShape",
+ CreateSvxAccessibleShape ),
+ ShapeTypeDescriptor ( DRAWING_GRAPHIC_OBJECT, "com.sun.star.drawing.GraphicObjectShape",
+ CreateSvxAccessibleShape ),
+ ShapeTypeDescriptor ( DRAWING_GROUP, "com.sun.star.drawing.GroupShape",
+ CreateSvxAccessibleShape ),
+ ShapeTypeDescriptor ( DRAWING_OLE, "com.sun.star.drawing.OLE2Shape",
+ CreateSvxAccessibleShape ),
+ ShapeTypeDescriptor ( DRAWING_PAGE, "com.sun.star.drawing.PageShape",
+ CreateSvxAccessibleShape ),
+ ShapeTypeDescriptor ( DRAWING_CAPTION, "com.sun.star.drawing.CaptionShape",
+ CreateSvxAccessibleShape ),
+ ShapeTypeDescriptor ( DRAWING_FRAME, "com.sun.star.drawing.FrameShape",
+ CreateSvxAccessibleShape ),
+ ShapeTypeDescriptor ( DRAWING_PLUGIN, "com.sun.star.drawing.PluginShape",
+ CreateSvxAccessibleShape ),
+ ShapeTypeDescriptor ( DRAWING_APPLET, "com.sun.star.drawing.AppletShape",
+ CreateSvxAccessibleShape ),
+ ShapeTypeDescriptor ( DRAWING_3D_SCENE, "com.sun.star.drawing.Shape3DSceneObject",
+ CreateSvxAccessibleShape ),
+ ShapeTypeDescriptor ( DRAWING_3D_CUBE, "com.sun.star.drawing.Shape3DCubeObject",
+ CreateSvxAccessibleShape ),
+ ShapeTypeDescriptor ( DRAWING_3D_SPHERE, "com.sun.star.drawing.Shape3DSphereObject",
+ CreateSvxAccessibleShape ),
+ ShapeTypeDescriptor ( DRAWING_3D_LATHE, "com.sun.star.drawing.Shape3DLatheObject",
+ CreateSvxAccessibleShape ),
+ ShapeTypeDescriptor ( DRAWING_3D_EXTRUDE, "com.sun.star.drawing.Shape3DExtrudeObject",
+ CreateSvxAccessibleShape ),
+ ShapeTypeDescriptor ( DRAWING_CUSTOM, "com.sun.star.drawing.CustomShape",
+ CreateSvxAccessibleShape ),
+ ShapeTypeDescriptor ( DRAWING_TABLE, "com.sun.star.drawing.TableShape",
+ CreateSvxAccessibleShape ),
+ ShapeTypeDescriptor ( DRAWING_MEDIA, "com.sun.star.drawing.MediaShape",
+ CreateSvxAccessibleShape ),
+
+ };
+
+ // Crash while inserting callout with activated accessibility (#i37790#)
+ ShapeTypeHandler::Instance().AddShapeTypeList ( DRAWING_END, aSvxShapeTypeList);
+}
+
+} // end of namespace accessibility
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/accessibility/charmapacc.cxx b/svx/source/accessibility/charmapacc.cxx
new file mode 100644
index 000000000..0de91ccee
--- /dev/null
+++ b/svx/source/accessibility/charmapacc.cxx
@@ -0,0 +1,584 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * 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/accessiblestatesethelper.hxx>
+#include <vcl/settings.hxx>
+#include <vcl/svapp.hxx>
+#include <stdio.h>
+#include <svx/charmap.hxx>
+#include <charmapacc.hxx>
+#include <com/sun/star/accessibility/AccessibleRole.hpp>
+#include <com/sun/star/accessibility/AccessibleStateType.hpp>
+#include <com/sun/star/lang/IndexOutOfBoundsException.hpp>
+#include <o3tl/temporary.hxx>
+#include <osl/interlck.h>
+#include <svx/dialmgr.hxx>
+#include <svx/strings.hrc>
+#include <comphelper/types.hxx>
+
+namespace svx
+{
+ using namespace comphelper;
+ using namespace ::com::sun::star;
+ using namespace ::com::sun::star::uno;
+ using namespace ::com::sun::star::lang;
+ using namespace ::com::sun::star::accessibility;
+
+SvxShowCharSetItem::SvxShowCharSetItem( SvxShowCharSet& rParent,SvxShowCharSetAcc* _pParent,sal_uInt16 _nPos ) :
+ mrParent( rParent )
+ ,mnId( _nPos )
+ ,m_pParent(_pParent)
+{
+}
+
+SvxShowCharSetItem::~SvxShowCharSetItem()
+{
+ if ( m_xItem.is() )
+ {
+ m_xItem->ParentDestroyed();
+ m_xItem.clear();
+ }
+}
+
+uno::Reference< css::accessibility::XAccessible > SvxShowCharSetItem::GetAccessible()
+{
+ if( !m_xItem.is() )
+ {
+ m_xItem = new SvxShowCharSetItemAcc( this );
+ }
+
+ return m_xItem;
+}
+
+SvxShowCharSetAcc::SvxShowCharSetAcc(SvxShowCharSet* pParent)
+ : m_pParent(pParent)
+{
+ osl_atomic_increment(&m_refCount);
+ {
+ lateInit(this);
+ }
+ osl_atomic_decrement(&m_refCount);
+}
+
+SvxShowCharSetAcc::~SvxShowCharSetAcc()
+{
+ ensureDisposed();
+}
+
+void SAL_CALL SvxShowCharSetAcc::disposing()
+{
+ OAccessibleSelectionHelper::disposing();
+ for (auto& rChild : m_aChildren)
+ ::comphelper::disposeComponent(rChild);
+
+ m_aChildren.clear();
+ m_pParent = nullptr;
+}
+
+
+IMPLEMENT_FORWARD_XINTERFACE2( SvxShowCharSetAcc, OAccessibleSelectionHelper, OAccessibleHelper_Base )
+IMPLEMENT_FORWARD_XTYPEPROVIDER2( SvxShowCharSetAcc, OAccessibleSelectionHelper, OAccessibleHelper_Base )
+
+bool SvxShowCharSetAcc::implIsSelected( sal_Int32 nAccessibleChildIndex )
+{
+ return m_pParent && m_pParent->IsSelected(
+ sal::static_int_cast<sal_uInt16>(nAccessibleChildIndex));
+}
+
+ // select the specified child => watch for special ChildIndexes (ACCESSIBLE_SELECTION_CHILD_xxx)
+void SvxShowCharSetAcc::implSelect(sal_Int32 nAccessibleChildIndex, bool bSelect)
+{
+ if ( m_pParent )
+ {
+ if ( bSelect )
+ m_pParent->SelectIndex(nAccessibleChildIndex, true);
+ else
+ m_pParent->DeSelect();
+ }
+}
+
+css::awt::Rectangle SvxShowCharSetAcc::implGetBounds()
+{
+ awt::Rectangle aRet;
+
+ if (m_pParent)
+ {
+ const Point aOutPos;//( m_pParent->GetPosPixel() );
+ Size aOutSize( m_pParent->GetOutputSizePixel());
+
+ aRet.X = aOutPos.X();
+ aRet.Y = aOutPos.Y();
+ aRet.Width = aOutSize.Width();
+ aRet.Height = aOutSize.Height();
+ }
+
+ return aRet;
+}
+
+sal_Int32 SAL_CALL SvxShowCharSetAcc::getAccessibleChildCount()
+{
+ OExternalLockGuard aGuard( this );
+
+ return m_pParent->getMaxCharCount();
+}
+
+uno::Reference< css::accessibility::XAccessible > SAL_CALL SvxShowCharSetAcc::getAccessibleChild( sal_Int32 i )
+{
+ OExternalLockGuard aGuard( this );
+
+ uno::Reference< css::accessibility::XAccessible > xRet;
+ SvxShowCharSetItem* pItem = m_pParent->ImplGetItem( static_cast< sal_uInt16 >( i ) );
+
+ if( !pItem )
+ throw lang::IndexOutOfBoundsException();
+
+ pItem->m_pParent = this;
+ xRet = pItem->GetAccessible();
+ m_aChildren.push_back(xRet);
+
+ return xRet;
+}
+
+uno::Reference< css::accessibility::XAccessible > SAL_CALL SvxShowCharSetAcc::getAccessibleParent()
+{
+ OExternalLockGuard aGuard( this );
+
+ if (m_pParent)
+ return m_pParent->getAccessibleParent();
+ return uno::Reference<css::accessibility::XAccessible>();
+}
+
+sal_Int16 SAL_CALL SvxShowCharSetAcc::getAccessibleRole()
+{
+ return css::accessibility::AccessibleRole::TABLE;
+}
+
+OUString SAL_CALL SvxShowCharSetAcc::getAccessibleDescription()
+{
+ OExternalLockGuard aGuard( this );
+ return SvxResId( RID_SVXSTR_CHARACTER_SELECTION );
+}
+
+
+OUString SAL_CALL SvxShowCharSetAcc::getAccessibleName()
+{
+ OExternalLockGuard aGuard( this );
+
+ return SvxResId( RID_SVXSTR_CHAR_SEL_DESC );
+}
+
+
+uno::Reference< css::accessibility::XAccessibleRelationSet > SAL_CALL SvxShowCharSetAcc::getAccessibleRelationSet()
+{
+ return uno::Reference< css::accessibility::XAccessibleRelationSet >();
+}
+
+
+uno::Reference< css::accessibility::XAccessibleStateSet > SAL_CALL SvxShowCharSetAcc::getAccessibleStateSet()
+{
+ OExternalLockGuard aGuard( this );
+
+ rtl::Reference<::utl::AccessibleStateSetHelper> pStateSet = new ::utl::AccessibleStateSetHelper;
+
+ if (m_pParent)
+ {
+ // SELECTABLE
+ pStateSet->AddState( AccessibleStateType::FOCUSABLE );
+ if (m_pParent->HasFocus())
+ {
+ pStateSet->AddState( AccessibleStateType::FOCUSED );
+ pStateSet->AddState( AccessibleStateType::ACTIVE );
+ }
+ if (m_pParent->IsEnabled())
+ {
+ pStateSet->AddState( AccessibleStateType::ENABLED );
+ pStateSet->AddState( AccessibleStateType::SENSITIVE );
+ }
+ if (m_pParent->IsVisible())
+ pStateSet->AddState( AccessibleStateType::VISIBLE );
+
+ pStateSet->AddState( AccessibleStateType::MANAGES_DESCENDANTS );
+ }
+
+ return pStateSet;
+}
+
+
+uno::Reference< css::accessibility::XAccessible > SAL_CALL SvxShowCharSetAcc::getAccessibleAtPoint( const awt::Point& aPoint )
+{
+ OExternalLockGuard aGuard( this );
+
+ uno::Reference< css::accessibility::XAccessible > xRet;
+ const sal_uInt16 nItemId = sal::static_int_cast<sal_uInt16>(
+ m_pParent->PixelToMapIndex( Point( aPoint.X, aPoint.Y ) ));
+
+ if( sal_uInt16(-1) != nItemId )
+ {
+ SvxShowCharSetItem* pItem = m_pParent->ImplGetItem( nItemId );
+ xRet = pItem->GetAccessible();
+ }
+ return xRet;
+}
+
+void SAL_CALL SvxShowCharSetAcc::grabFocus()
+{
+ OExternalLockGuard aGuard( this );
+
+ m_pParent->GrabFocus();
+}
+
+sal_Int32 SAL_CALL SvxShowCharSetAcc::getAccessibleRowCount( )
+{
+ return ((getAccessibleChildCount()-1) / COLUMN_COUNT) + 1;
+}
+
+sal_Int32 SAL_CALL SvxShowCharSetAcc::getAccessibleColumnCount( )
+{
+ return COLUMN_COUNT;
+}
+
+OUString SAL_CALL SvxShowCharSetAcc::getAccessibleRowDescription( sal_Int32 /*nRow*/ )
+{
+ return OUString();
+}
+
+OUString SAL_CALL SvxShowCharSetAcc::getAccessibleColumnDescription( sal_Int32 /*nColumn*/ )
+{
+ return OUString();
+}
+
+sal_Int32 SAL_CALL SvxShowCharSetAcc::getAccessibleRowExtentAt( sal_Int32 /*nRow*/, sal_Int32 /*nColumn*/ )
+{
+ return 1;
+}
+
+sal_Int32 SAL_CALL SvxShowCharSetAcc::getAccessibleColumnExtentAt( sal_Int32 /*nRow*/, sal_Int32 /*nColumn*/ )
+{
+ return 1;
+}
+
+Reference< XAccessibleTable > SAL_CALL SvxShowCharSetAcc::getAccessibleRowHeaders( )
+{
+ return Reference< XAccessibleTable >();
+}
+
+Reference< XAccessibleTable > SAL_CALL SvxShowCharSetAcc::getAccessibleColumnHeaders( )
+{
+ return Reference< XAccessibleTable >();
+}
+
+Sequence< sal_Int32 > SAL_CALL SvxShowCharSetAcc::getSelectedAccessibleRows( )
+{
+ OExternalLockGuard aGuard( this );
+
+ return { SvxShowCharSet::GetRowPos(m_pParent->GetSelectIndexId()) };
+}
+
+Sequence< sal_Int32 > SAL_CALL SvxShowCharSetAcc::getSelectedAccessibleColumns( )
+{
+ OExternalLockGuard aGuard( this );
+
+ return { SvxShowCharSet::GetColumnPos(m_pParent->GetSelectIndexId()) };
+}
+
+sal_Bool SAL_CALL SvxShowCharSetAcc::isAccessibleRowSelected( sal_Int32 nRow )
+{
+ OExternalLockGuard aGuard( this );
+
+ return SvxShowCharSet::GetRowPos(m_pParent->GetSelectIndexId()) == nRow;
+}
+
+sal_Bool SAL_CALL SvxShowCharSetAcc::isAccessibleColumnSelected( sal_Int32 nColumn )
+{
+ OExternalLockGuard aGuard( this );
+ ensureAlive();
+ return SvxShowCharSet::GetColumnPos(m_pParent->GetSelectIndexId()) == nColumn;
+}
+
+Reference< XAccessible > SAL_CALL SvxShowCharSetAcc::getAccessibleCellAt( sal_Int32 nRow, sal_Int32 nColumn )
+{
+ OExternalLockGuard aGuard( this );
+
+ svx::SvxShowCharSetItem* pItem = m_pParent->ImplGetItem(
+ sal::static_int_cast<sal_uInt16>(getAccessibleIndex(nRow,nColumn) ));
+ if ( !pItem )
+ throw IndexOutOfBoundsException();
+ return pItem->GetAccessible();
+}
+
+Reference< XAccessible > SAL_CALL SvxShowCharSetAcc::getAccessibleCaption( )
+{
+ return Reference< XAccessible >();
+}
+
+Reference< XAccessible > SAL_CALL SvxShowCharSetAcc::getAccessibleSummary( )
+{
+ return Reference< XAccessible >();
+}
+
+sal_Bool SAL_CALL SvxShowCharSetAcc::isAccessibleSelected( sal_Int32 nRow, sal_Int32 nColumn )
+{
+ OExternalLockGuard aGuard( this );
+
+ return m_pParent->GetSelectIndexId() == getAccessibleIndex(nRow,nColumn);
+}
+
+sal_Int32 SAL_CALL SvxShowCharSetAcc::getAccessibleIndex( sal_Int32 nRow, sal_Int32 nColumn )
+{
+ return (nRow*COLUMN_COUNT) + nColumn;
+}
+
+sal_Int32 SAL_CALL SvxShowCharSetAcc::getAccessibleRow( sal_Int32 nChildIndex )
+{
+ OExternalLockGuard aGuard( this );
+
+ return SvxShowCharSet::GetRowPos(sal::static_int_cast<sal_uInt16>(nChildIndex));
+}
+
+sal_Int32 SAL_CALL SvxShowCharSetAcc::getAccessibleColumn( sal_Int32 nChildIndex )
+{
+ OExternalLockGuard aGuard( this );
+
+ return SvxShowCharSet::GetColumnPos(sal::static_int_cast<sal_uInt16>(nChildIndex));
+}
+
+
+SvxShowCharSetItemAcc::SvxShowCharSetItemAcc( SvxShowCharSetItem* pParent ) : mpParent( pParent )
+{
+ OSL_ENSURE(pParent,"NO parent supplied!");
+ osl_atomic_increment(&m_refCount);
+ { // #b6211265 #
+ lateInit(this);
+ }
+ osl_atomic_decrement(&m_refCount);
+}
+
+
+SvxShowCharSetItemAcc::~SvxShowCharSetItemAcc()
+{
+ ensureDisposed();
+}
+
+IMPLEMENT_FORWARD_XINTERFACE2( SvxShowCharSetItemAcc, OAccessibleComponentHelper, OAccessibleHelper_Base_3 )
+IMPLEMENT_FORWARD_XTYPEPROVIDER2( SvxShowCharSetItemAcc, OAccessibleComponentHelper, OAccessibleHelper_Base_3 )
+
+void SvxShowCharSetItemAcc::ParentDestroyed()
+{
+ const ::osl::MutexGuard aGuard( GetMutex() );
+ mpParent = nullptr;
+}
+
+sal_Int32 SAL_CALL SvxShowCharSetItemAcc::getAccessibleChildCount()
+{
+ return 0;
+}
+
+
+uno::Reference< css::accessibility::XAccessible > SAL_CALL SvxShowCharSetItemAcc::getAccessibleChild( sal_Int32 /*i*/ )
+{
+ throw lang::IndexOutOfBoundsException();
+}
+
+
+uno::Reference< css::accessibility::XAccessible > SAL_CALL SvxShowCharSetItemAcc::getAccessibleParent()
+{
+ OExternalLockGuard aGuard( this );
+
+ return mpParent->m_pParent;
+}
+
+
+sal_Int16 SAL_CALL SvxShowCharSetItemAcc::getAccessibleRole()
+{
+ return css::accessibility::AccessibleRole::TABLE_CELL;
+}
+
+
+OUString SAL_CALL SvxShowCharSetItemAcc::getAccessibleDescription()
+{
+ OExternalLockGuard aGuard( this );
+
+ OUString sDescription;
+
+ const OUString aCharStr( mpParent->maText);
+ const sal_UCS4 c = aCharStr.iterateCodePoints( &o3tl::temporary(sal_Int32(0)) );
+ const int tmp_len = (c < 0x10000) ? 4 : 6;
+ char buf[16] = "0x0000";
+ sal_UCS4 c_Shifted = c;
+ for( int i = 0; i < tmp_len; ++i )
+ {
+ char h = static_cast<char>(c_Shifted & 0x0F);
+ buf[tmp_len+1-i] = (h > 9) ? (h - 10 + 'A') : (h + '0');
+ c_Shifted >>= 4;
+ }
+ if( c < 256 )
+ snprintf( buf+6, 10, " (%" SAL_PRIuUINT32 ")", c );
+
+ sDescription = SvxResId( RID_SVXSTR_CHARACTER_CODE )
+ + " "
+ + OUString(buf, strlen(buf), RTL_TEXTENCODING_ASCII_US);
+
+ return sDescription;
+}
+
+
+OUString SAL_CALL SvxShowCharSetItemAcc::getAccessibleName()
+{
+ OExternalLockGuard aGuard( this );
+
+ OUString aRet;
+
+ if( mpParent )
+ {
+ aRet = mpParent->maText;
+
+ if (aRet.isEmpty())
+ aRet = getAccessibleDescription();
+ }
+
+ return aRet;
+}
+
+
+uno::Reference< css::accessibility::XAccessibleRelationSet > SAL_CALL SvxShowCharSetItemAcc::getAccessibleRelationSet()
+{
+ return uno::Reference< css::accessibility::XAccessibleRelationSet >();
+}
+
+
+uno::Reference< css::accessibility::XAccessibleStateSet > SAL_CALL SvxShowCharSetItemAcc::getAccessibleStateSet()
+{
+ OExternalLockGuard aGuard( this );
+
+ rtl::Reference<::utl::AccessibleStateSetHelper> pStateSet = new ::utl::AccessibleStateSetHelper;
+
+ if( mpParent )
+ {
+ if (mpParent->mrParent.IsEnabled())
+ {
+ pStateSet->AddState( css::accessibility::AccessibleStateType::ENABLED );
+ // SELECTABLE
+ pStateSet->AddState( css::accessibility::AccessibleStateType::SELECTABLE );
+ pStateSet->AddState( css::accessibility::AccessibleStateType::FOCUSABLE );
+ }
+
+ // SELECTED
+ if( mpParent->mrParent.GetSelectIndexId() == mpParent->mnId )
+ {
+ pStateSet->AddState( css::accessibility::AccessibleStateType::SELECTED );
+ pStateSet->AddState( css::accessibility::AccessibleStateType::FOCUSED );
+ }
+ if ( mpParent->mnId >= mpParent->mrParent.FirstInView() && mpParent->mnId <= mpParent->mrParent.LastInView() )
+ {
+ pStateSet->AddState( AccessibleStateType::VISIBLE );
+ pStateSet->AddState( AccessibleStateType::SHOWING );
+ }
+ pStateSet->AddState( AccessibleStateType::TRANSIENT );
+ }
+
+ return pStateSet;
+}
+
+
+sal_Int32 SvxShowCharSetItemAcc::getAccessibleActionCount()
+{
+ return 1;
+}
+
+
+sal_Bool SvxShowCharSetItemAcc::doAccessibleAction ( sal_Int32 nIndex )
+{
+ OExternalLockGuard aGuard( this );
+
+ if( nIndex == 0 )
+ {
+ mpParent->mrParent.OutputIndex( mpParent->mnId );
+ return true;
+ }
+ throw IndexOutOfBoundsException();
+}
+
+
+OUString SvxShowCharSetItemAcc::getAccessibleActionDescription ( sal_Int32 nIndex )
+{
+ if( nIndex == 0 )
+ return "press";
+ throw IndexOutOfBoundsException();
+}
+
+
+Reference< css::accessibility::XAccessibleKeyBinding > SvxShowCharSetItemAcc::getAccessibleActionKeyBinding( sal_Int32 nIndex )
+{
+ if( nIndex == 0 )
+ return Reference< css::accessibility::XAccessibleKeyBinding >();
+ throw IndexOutOfBoundsException();
+}
+
+
+void SAL_CALL SvxShowCharSetItemAcc::grabFocus()
+{
+ // nothing to do
+}
+
+awt::Rectangle SvxShowCharSetItemAcc::implGetBounds( )
+{
+ awt::Rectangle aRet;
+
+ if( mpParent )
+ {
+ tools::Rectangle aRect( mpParent->maRect );
+ tools::Rectangle aParentRect(Point(), mpParent->mrParent.GetOutputSizePixel());
+
+ aRect.Intersection( aParentRect );
+
+ aRet.X = aRect.Left();
+ aRet.Y = aRect.Top();
+ aRet.Width = aRect.GetWidth();
+ aRet.Height = aRect.GetHeight();
+ }
+
+ return aRet;
+}
+
+uno::Reference< css::accessibility::XAccessible > SAL_CALL SvxShowCharSetItemAcc::getAccessibleAtPoint( const awt::Point& /*aPoint*/ )
+{
+ return uno::Reference< css::accessibility::XAccessible >();
+}
+
+sal_Int32 SAL_CALL SvxShowCharSetAcc::getForeground( )
+{
+ OExternalLockGuard aGuard( this );
+
+ //see SvxShowCharSet::InitSettings
+ const StyleSettings& rStyleSettings = Application::GetSettings().GetStyleSettings();
+ return static_cast<sal_Int32>(rStyleSettings.GetDialogTextColor());
+}
+
+sal_Int32 SAL_CALL SvxShowCharSetAcc::getBackground( )
+{
+ OExternalLockGuard aGuard( this );
+
+ //see SvxShowCharSet::InitSettings
+ const StyleSettings& rStyleSettings = Application::GetSettings().GetStyleSettings();
+ return static_cast<sal_Int32>(rStyleSettings.GetWindowColor());
+}
+
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/accessibility/lookupcolorname.cxx b/svx/source/accessibility/lookupcolorname.cxx
new file mode 100644
index 000000000..373dae469
--- /dev/null
+++ b/svx/source/accessibility/lookupcolorname.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 <sal/config.h>
+
+#include <com/sun/star/container/XNameAccess.hpp>
+#include <com/sun/star/container/XNameContainer.hpp>
+#include <com/sun/star/drawing/ColorTable.hpp>
+#include <com/sun/star/uno/Any.hxx>
+#include <com/sun/star/uno/Reference.hxx>
+#include <com/sun/star/uno/RuntimeException.hpp>
+#include <com/sun/star/uno/Sequence.hxx>
+#include <comphelper/processfactory.hxx>
+#include <rtl/ustring.hxx>
+#include <vcl/svapp.hxx>
+
+#include "lookupcolorname.hxx"
+#include <unordered_map>
+
+namespace
+{
+class ColorNameMap
+{
+public:
+ ColorNameMap();
+ ColorNameMap(const ColorNameMap&) = delete;
+ ColorNameMap& operator=(const ColorNameMap&) = delete;
+
+ OUString lookUp(tools::Long color) const;
+
+private:
+ typedef std::unordered_map<tools::Long, OUString> Map;
+
+ Map map_;
+};
+
+ColorNameMap::ColorNameMap()
+{
+ css::uno::Sequence<OUString> aNames;
+ css::uno::Reference<css::container::XNameAccess> xNA;
+
+ try
+ {
+ // Create color table in which to look up the given color.
+ css::uno::Reference<css::container::XNameContainer> xColorTable
+ = css::drawing::ColorTable::create(comphelper::getProcessComponentContext());
+
+ // Get list of color names in order to iterate over the color table.
+
+ // Lock the solar mutex here as workaround for missing lock in
+ // called function.
+ SolarMutexGuard aGuard;
+ xNA = xColorTable;
+ aNames = xColorTable->getElementNames();
+ }
+ catch (css::uno::RuntimeException const&)
+ {
+ // When an exception occurred then we have an empty name sequence
+ // and the loop below is not entered.
+ }
+
+ // Fill the map to convert from numerical color values to names.
+ if (!xNA.is())
+ return;
+
+ for (const auto& rName : std::as_const(aNames))
+ {
+ // Get the numerical value for the i-th color name.
+ try
+ {
+ css::uno::Any aColor = xNA->getByName(rName);
+ tools::Long nColor = 0;
+ aColor >>= nColor;
+ map_[nColor] = rName;
+ }
+ catch (css::uno::RuntimeException const&)
+ {
+ // Ignore the exception: the color who lead to the exception
+ // is not included into the map.
+ }
+ }
+}
+
+OUString ColorNameMap::lookUp(tools::Long color) const
+{
+ Map::const_iterator i(map_.find(color));
+ if (i != map_.end())
+ {
+ return i->second;
+ }
+ // Did not find the given color; return its RGB tuple representation:
+ return "#" + OUString::number(color, 16);
+}
+}
+
+namespace accessibility
+{
+OUString lookUpColorName(tools::Long color)
+{
+ static ColorNameMap theColorNameMap;
+ return theColorNameMap.lookUp(color);
+}
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/accessibility/lookupcolorname.hxx b/svx/source/accessibility/lookupcolorname.hxx
new file mode 100644
index 000000000..0d752c0fa
--- /dev/null
+++ b/svx/source/accessibility/lookupcolorname.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_SVX_SOURCE_ACCESSIBILITY_LOOKUPCOLORNAME_HXX
+#define INCLUDED_SVX_SOURCE_ACCESSIBILITY_LOOKUPCOLORNAME_HXX
+
+#include <sal/config.h>
+
+#include <rtl/ustring.hxx>
+#include <tools/long.hxx>
+
+namespace accessibility
+{
+/** This is a color name lookup targeted to be used by the accessibility
+ <type>DescriptionGenerator</type> class. It encapsulates a
+ <type>com.sun.star.drawing.ColorTable</type> and provides an inverse look
+ up of color names for given numerical color descriptions (the RGB values
+ encoded as an integer).
+
+ <p>The implementation uses as singleton so that the
+ <type>com.sun.star.drawing.ColorTable</type> object needs to be created
+ only once. That singleton instance for now lives until the application
+ terminates. However, the color table from which it takes its values may
+ change during this time. Reacting to these changes remains a task for the
+ future.</p>
+
+ @param nColor
+ This integer is the sum of the 8 Bit red value shifted left 16 Bits, the
+ green value shifted left 8 Bits, and the unshifted blue value.
+
+ @return
+ The returned string is either the color name of the specified color or,
+ when no name exists, a string of the form "#RRGGBB" with two hexadecimal
+ digits for each color component.
+*/
+OUString lookUpColorName(tools::Long color);
+}
+
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/accessibility/svxpixelctlaccessiblecontext.cxx b/svx/source/accessibility/svxpixelctlaccessiblecontext.cxx
new file mode 100644
index 000000000..4873b3cfe
--- /dev/null
+++ b/svx/source/accessibility/svxpixelctlaccessiblecontext.cxx
@@ -0,0 +1,468 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <com/sun/star/accessibility/AccessibleRole.hpp>
+#include <com/sun/star/accessibility/AccessibleEventId.hpp>
+#include <unotools/accessiblestatesethelper.hxx>
+#include <com/sun/star/accessibility/AccessibleStateType.hpp>
+#include <com/sun/star/lang/IndexOutOfBoundsException.hpp>
+#include <toolkit/helper/convert.hxx>
+#include <vcl/svapp.hxx>
+#include <vcl/settings.hxx>
+#include <osl/mutex.hxx>
+#include <tools/debug.hxx>
+#include <tools/gen.hxx>
+
+#include <svx/dlgctrl.hxx>
+
+#include <svxpixelctlaccessiblecontext.hxx>
+
+using namespace ::cppu;
+using namespace ::osl;
+using namespace ::com::sun::star;
+using namespace ::com::sun::star::uno;
+using namespace ::com::sun::star::accessibility;
+
+SvxPixelCtlAccessible::SvxPixelCtlAccessible(SvxPixelCtl* pControl)
+ : mpPixelCtl(pControl)
+{
+}
+
+SvxPixelCtlAccessible::~SvxPixelCtlAccessible()
+{
+ ensureDisposed();
+}
+
+IMPLEMENT_FORWARD_XINTERFACE2( SvxPixelCtlAccessible, OAccessibleSelectionHelper, OAccessibleHelper_Base )
+IMPLEMENT_FORWARD_XTYPEPROVIDER2( SvxPixelCtlAccessible, OAccessibleSelectionHelper, OAccessibleHelper_Base )
+
+uno::Reference< XAccessibleContext > SvxPixelCtlAccessible::getAccessibleContext( )
+{
+ return this;
+}
+
+sal_Int32 SvxPixelCtlAccessible::getAccessibleChildCount( )
+{
+ ::osl::MutexGuard aGuard( m_aMutex );
+ return SvxPixelCtl::GetSquares();
+}
+uno::Reference< XAccessible > SvxPixelCtlAccessible::getAccessibleChild( sal_Int32 i )
+{
+ ::osl::MutexGuard aGuard( m_aMutex );
+ if ( i < 0 || i >= getAccessibleChildCount())
+ throw lang::IndexOutOfBoundsException();
+ Reference< XAccessible > xChild;
+ if (mpPixelCtl)
+ xChild = CreateChild(i, mpPixelCtl->IndexToPoint(i));
+ return xChild;
+}
+
+uno::Reference< XAccessible > SvxPixelCtlAccessible::getAccessibleParent( )
+{
+ ::osl::MutexGuard aGuard( m_aMutex );
+ if (mpPixelCtl)
+ return mpPixelCtl->getAccessibleParent();
+ return uno::Reference<css::accessibility::XAccessible>();
+}
+
+sal_Int16 SvxPixelCtlAccessible::getAccessibleRole( )
+{
+ return AccessibleRole::LIST;
+}
+
+OUString SvxPixelCtlAccessible::getAccessibleDescription( )
+{
+
+ ::osl::MutexGuard aGuard( m_aMutex );
+ return mpPixelCtl ? mpPixelCtl->GetAccessibleDescription() : "";
+}
+
+OUString SvxPixelCtlAccessible::getAccessibleName( )
+{
+ ::osl::MutexGuard aGuard( m_aMutex );
+ return mpPixelCtl ? mpPixelCtl->GetAccessibleName() : "";
+}
+
+Reference< XAccessibleRelationSet > SAL_CALL SvxPixelCtlAccessible::getAccessibleRelationSet()
+{
+ if (mpPixelCtl)
+ return mpPixelCtl->get_accessible_relation_set();
+ return uno::Reference<css::accessibility::XAccessibleRelationSet>();
+}
+
+uno::Reference< XAccessibleStateSet > SvxPixelCtlAccessible::getAccessibleStateSet( )
+{
+ ::osl::MutexGuard aGuard( m_aMutex );
+ rtl::Reference<utl::AccessibleStateSetHelper> pStateSetHelper = new utl::AccessibleStateSetHelper;
+
+ if (mpPixelCtl)
+ {
+ const sal_Int16 aStandardStates[] =
+ {
+ AccessibleStateType::FOCUSABLE,
+ AccessibleStateType::SELECTABLE,
+ AccessibleStateType::SHOWING,
+ AccessibleStateType::VISIBLE,
+ AccessibleStateType::OPAQUE,
+ 0
+ };
+
+ sal_Int16 nState = 0;
+ while (aStandardStates[nState])
+ {
+ pStateSetHelper->AddState(aStandardStates[nState++]);
+ }
+ if (mpPixelCtl->IsEnabled())
+ pStateSetHelper->AddState(AccessibleStateType::ENABLED);
+ if (mpPixelCtl->HasFocus())
+ pStateSetHelper->AddState(AccessibleStateType::FOCUSED);
+ pStateSetHelper->AddState(AccessibleStateType::MANAGES_DESCENDANTS);
+ }
+
+ return pStateSetHelper;
+}
+
+uno::Reference<XAccessible > SAL_CALL SvxPixelCtlAccessible::getAccessibleAtPoint (
+ const awt::Point& rPoint)
+{
+ ::osl::MutexGuard aGuard( m_aMutex );
+
+ Reference< XAccessible > xRet;
+
+ if (mpPixelCtl)
+ {
+ tools::Long nIndex = mpPixelCtl->PointToIndex(Point(rPoint.X, rPoint.Y));
+ xRet = CreateChild(nIndex, mpPixelCtl->IndexToPoint(nIndex));
+ }
+
+ return xRet;
+}
+
+awt::Rectangle SvxPixelCtlAccessible::implGetBounds()
+{
+ ::osl::MutexGuard aGuard( m_aMutex );
+
+ awt::Rectangle aRet;
+
+ if (mpPixelCtl)
+ {
+ const Point aOutPos;
+ Size aOutSize(mpPixelCtl->GetOutputSizePixel());
+
+ aRet.X = aOutPos.X();
+ aRet.Y = aOutPos.Y();
+ aRet.Width = aOutSize.Width();
+ aRet.Height = aOutSize.Height();
+ }
+
+ return aRet;
+}
+
+void SvxPixelCtlAccessible::grabFocus( )
+{
+ ::osl::MutexGuard aGuard( m_aMutex );
+ if (mpPixelCtl)
+ mpPixelCtl->GrabFocus();
+}
+
+sal_Int32 SvxPixelCtlAccessible::getForeground( )
+{
+ ::osl::MutexGuard aGuard( m_aMutex );
+
+ //see SvxPixelCtl::Paint
+ const StyleSettings& rStyles = Application::GetSettings().GetStyleSettings();
+ return sal_Int32(rStyles.GetLabelTextColor());
+}
+
+sal_Int32 SvxPixelCtlAccessible::getBackground( )
+{
+ ::osl::MutexGuard aGuard( m_aMutex );
+
+ //see SvxPixelCtl::Paint
+ const StyleSettings& rStyles = Application::GetSettings().GetStyleSettings();
+ return sal_Int32(rStyles.GetDialogColor());
+}
+
+void SvxPixelCtlAccessible::implSelect(sal_Int32 nChildIndex, bool bSelect)
+{
+ ::osl::MutexGuard aGuard( m_aMutex );
+
+ if ( nChildIndex < 0 || nChildIndex >= getAccessibleChildCount())
+ throw lang::IndexOutOfBoundsException();
+
+ if (!mpPixelCtl)
+ return;
+
+ tools::Long nIndex = mpPixelCtl->ShowPosition(mpPixelCtl->IndexToPoint(nChildIndex));
+ NotifyChild(nIndex, bSelect, false);
+}
+
+bool SvxPixelCtlAccessible::implIsSelected(sal_Int32 nChildIndex)
+{
+ ::osl::MutexGuard aGuard( m_aMutex );
+
+ if (!mpPixelCtl)
+ return false;
+
+ return mpPixelCtl->GetFocusPosIndex() == nChildIndex;
+}
+
+void SAL_CALL SvxPixelCtlAccessible::disposing()
+{
+ ::osl::MutexGuard aGuard(m_aMutex);
+ OAccessibleSelectionHelper::disposing();
+ m_xCurChild.clear();
+ mpPixelCtl = nullptr;
+}
+
+void SvxPixelCtlAccessible::NotifyChild(tools::Long nIndex,bool bSelect ,bool bCheck)
+{
+ DBG_ASSERT( !(!bSelect && !bCheck),"" );//non is false
+
+ SvxPixelCtlAccessibleChild *pChild= nullptr;
+
+ if (m_xCurChild.is())
+ {
+ pChild= static_cast<SvxPixelCtlAccessibleChild*>(m_xCurChild.get());
+ DBG_ASSERT(pChild,"Child Must be Valid");
+ if (pChild->getAccessibleIndexInParent() == nIndex )
+ {
+ if (bSelect)
+ {
+ pChild->SelectChild(true);
+ }
+ if (bCheck)
+ {
+ pChild->ChangePixelColorOrBG(mpPixelCtl->GetBitmapPixel(sal_uInt16(nIndex)) != 0);
+ pChild->CheckChild();
+ }
+ return ;
+ }
+ }
+ uno::Reference <XAccessible> xNewChild =CreateChild(nIndex, mpPixelCtl->IndexToPoint(nIndex));
+ SvxPixelCtlAccessibleChild *pNewChild= static_cast<SvxPixelCtlAccessibleChild*>(xNewChild.get());
+ DBG_ASSERT(pNewChild,"Child Must be Valid");
+
+ Any aNewValue,aOldValue;
+ aNewValue<<= xNewChild;
+ NotifyAccessibleEvent(AccessibleEventId::ACTIVE_DESCENDANT_CHANGED, aOldValue, aNewValue);
+
+ if (bSelect)
+ {
+ if (pChild)
+ {
+ pChild->SelectChild(false);
+ }
+ pNewChild->SelectChild(true);
+ }
+ if (bCheck)
+ {
+ pNewChild->CheckChild();
+ }
+ m_xCurChild= xNewChild;
+}
+
+uno::Reference<XAccessible> SvxPixelCtlAccessible::CreateChild (tools::Long nIndex,Point mPoint)
+{
+ bool bPixelColorOrBG = mpPixelCtl->GetBitmapPixel(sal_uInt16(nIndex)) != 0;
+ Size size(mpPixelCtl->GetWidth() / SvxPixelCtl::GetLineCount(), mpPixelCtl->GetHeight() / SvxPixelCtl::GetLineCount());
+ uno::Reference<XAccessible> xChild = new SvxPixelCtlAccessibleChild(*mpPixelCtl,
+ bPixelColorOrBG,
+ tools::Rectangle(mPoint,size),
+ this,
+ nIndex);
+
+ return xChild;
+}
+
+void SvxPixelCtlAccessibleChild::CheckChild()
+{
+ Any aChecked;
+ aChecked <<= AccessibleStateType::CHECKED;
+
+ if (m_bPixelColorOrBG)//Current Child State
+ {
+ NotifyAccessibleEvent(AccessibleEventId::STATE_CHANGED, Any(), aChecked);
+ }
+ else
+ {
+ NotifyAccessibleEvent(AccessibleEventId::STATE_CHANGED, aChecked, Any());
+ }
+}
+
+void SvxPixelCtlAccessibleChild::SelectChild( bool bSelect)
+{
+ Any aSelected;
+ aSelected <<= AccessibleStateType::SELECTED;
+
+ if (bSelect)
+ {
+ NotifyAccessibleEvent(AccessibleEventId::STATE_CHANGED, Any(), aSelected);
+ }
+ else
+ {
+ NotifyAccessibleEvent(AccessibleEventId::STATE_CHANGED, aSelected, Any());
+ }
+}
+
+SvxPixelCtlAccessibleChild::SvxPixelCtlAccessibleChild( SvxPixelCtl& rWindow, bool bPixelColorOrBG,
+ const tools::Rectangle& rBoundingBox, const rtl::Reference<SvxPixelCtlAccessible>& rxParent,
+ tools::Long nIndexInParent)
+ : mrParentWindow( rWindow )
+ , mxParent(rxParent)
+ , m_bPixelColorOrBG(bPixelColorOrBG)
+ , maBoundingBox( rBoundingBox )
+ , mnIndexInParent( nIndexInParent )
+{
+}
+
+SvxPixelCtlAccessibleChild::~SvxPixelCtlAccessibleChild()
+{
+ ensureDisposed();
+}
+
+IMPLEMENT_FORWARD_XINTERFACE2( SvxPixelCtlAccessibleChild, OAccessibleComponentHelper, OAccessibleHelper_Base )
+IMPLEMENT_FORWARD_XTYPEPROVIDER2( SvxPixelCtlAccessibleChild, OAccessibleComponentHelper, OAccessibleHelper_Base )
+
+// XAccessible
+uno::Reference< XAccessibleContext> SAL_CALL SvxPixelCtlAccessibleChild::getAccessibleContext()
+{
+ return this;
+}
+
+uno::Reference< XAccessible > SAL_CALL SvxPixelCtlAccessibleChild::getAccessibleAtPoint( const awt::Point& )
+{
+ return uno::Reference< XAccessible >();
+}
+
+void SAL_CALL SvxPixelCtlAccessibleChild::grabFocus()
+{
+}
+
+sal_Int32 SvxPixelCtlAccessibleChild::getForeground()
+{
+ ::osl::MutexGuard aGuard( m_aMutex );
+ return mxParent.is() ? mxParent->getForeground() : -1;
+}
+
+sal_Int32 SvxPixelCtlAccessibleChild::getBackground()
+{
+ ::osl::MutexGuard aGuard( m_aMutex );
+ return mxParent.is() ? mxParent->getBackground() : -1;
+}
+
+// XAccessibleContext
+sal_Int32 SAL_CALL SvxPixelCtlAccessibleChild::getAccessibleChildCount()
+{
+ return 0;
+}
+
+uno::Reference< XAccessible > SAL_CALL SvxPixelCtlAccessibleChild::getAccessibleChild( sal_Int32 )
+{
+ throw lang::IndexOutOfBoundsException();
+}
+
+uno::Reference< XAccessible > SAL_CALL SvxPixelCtlAccessibleChild::getAccessibleParent()
+{
+ return mxParent;
+}
+
+sal_Int16 SAL_CALL SvxPixelCtlAccessibleChild::getAccessibleRole()
+{
+ return AccessibleRole::CHECK_BOX;
+}
+
+OUString SAL_CALL SvxPixelCtlAccessibleChild::getAccessibleDescription()
+{
+ ::osl::MutexGuard aGuard( m_aMutex );
+
+ return GetName();
+}
+
+OUString SAL_CALL SvxPixelCtlAccessibleChild::getAccessibleName()
+{
+ ::osl::MutexGuard aGuard( m_aMutex );
+ return GetName();
+}
+
+/** Return empty uno::Reference to indicate that the relation set is not
+ supported.
+*/
+uno::Reference<XAccessibleRelationSet> SAL_CALL SvxPixelCtlAccessibleChild::getAccessibleRelationSet()
+{
+ return uno::Reference< XAccessibleRelationSet >();
+}
+
+uno::Reference< XAccessibleStateSet > SAL_CALL SvxPixelCtlAccessibleChild::getAccessibleStateSet()
+{
+ ::osl::MutexGuard aGuard( m_aMutex );
+ rtl::Reference<utl::AccessibleStateSetHelper> pStateSetHelper = new utl::AccessibleStateSetHelper;
+
+ if (!rBHelper.bDisposed)
+ {
+
+ pStateSetHelper->AddState( AccessibleStateType::TRANSIENT );
+ pStateSetHelper->AddState( AccessibleStateType::ENABLED );
+ pStateSetHelper->AddState( AccessibleStateType::OPAQUE );
+ pStateSetHelper->AddState( AccessibleStateType::SELECTABLE );
+ pStateSetHelper->AddState( AccessibleStateType::SHOWING );
+ pStateSetHelper->AddState( AccessibleStateType::VISIBLE );
+
+ tools::Long nIndex = mrParentWindow.GetFocusPosIndex();
+ if ( nIndex == mnIndexInParent)
+ {
+ pStateSetHelper->AddState( AccessibleStateType::SELECTED );
+ }
+ if (mrParentWindow.GetBitmapPixel(sal_uInt16(mnIndexInParent)))
+ {
+ pStateSetHelper->AddState( AccessibleStateType::CHECKED );
+ }
+ }
+ else
+ pStateSetHelper->AddState( AccessibleStateType::DEFUNC );
+
+ return pStateSetHelper;
+}
+
+void SAL_CALL SvxPixelCtlAccessibleChild::disposing()
+{
+ OAccessibleComponentHelper::disposing();
+ mxParent.clear();
+}
+
+awt::Rectangle SvxPixelCtlAccessibleChild::implGetBounds()
+{
+ // no guard necessary, because no one changes maBoundingBox after creating it
+ return AWTRectangle(maBoundingBox);
+}
+
+OUString SvxPixelCtlAccessibleChild::GetName() const
+{
+ sal_Int32 nXIndex = mnIndexInParent % SvxPixelCtl::GetLineCount();
+ sal_Int32 nYIndex = mnIndexInParent / SvxPixelCtl::GetLineCount();
+
+ OUString str = "("
+ + OUString::number(nXIndex)
+ + ","
+ + OUString::number(nYIndex)
+ + ")";
+ return str;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/accessibility/svxrectctaccessiblecontext.cxx b/svx/source/accessibility/svxrectctaccessiblecontext.cxx
new file mode 100644
index 000000000..bca421c29
--- /dev/null
+++ b/svx/source/accessibility/svxrectctaccessiblecontext.cxx
@@ -0,0 +1,647 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <svxrectctaccessiblecontext.hxx>
+#include <com/sun/star/accessibility/AccessibleRole.hpp>
+#include <com/sun/star/accessibility/AccessibleEventId.hpp>
+#include <unotools/accessiblestatesethelper.hxx>
+#include <com/sun/star/accessibility/AccessibleStateType.hpp>
+#include <com/sun/star/lang/IndexOutOfBoundsException.hpp>
+#include <toolkit/helper/convert.hxx>
+#include <vcl/svapp.hxx>
+#include <osl/mutex.hxx>
+#include <tools/debug.hxx>
+#include <tools/gen.hxx>
+#include <sal/log.hxx>
+#include <vcl/settings.hxx>
+#include <svx/strings.hrc>
+#include <svx/dlgctrl.hxx>
+#include <svx/dialmgr.hxx>
+#include <com/sun/star/accessibility/AccessibleRelationType.hpp>
+#include <unotools/accessiblerelationsethelper.hxx>
+
+using namespace ::cppu;
+using namespace ::osl;
+using namespace ::com::sun::star;
+using namespace ::com::sun::star::uno;
+using namespace ::com::sun::star::accessibility;
+
+using namespace ::com::sun::star::lang;
+
+#define MAX_NUM_OF_CHILDREN 9
+#define NOCHILDSELECTED -1
+
+// internal
+namespace
+{
+ struct ChildIndexToPointData
+ {
+ TranslateId pResIdName;
+ TranslateId pResIdDescr;
+ RectPoint ePoint;
+ };
+}
+
+
+static const ChildIndexToPointData* IndexToPoint( tools::Long nIndex )
+{
+ DBG_ASSERT( nIndex < 9 && nIndex >= 0, "-IndexToPoint(): invalid child index! You have been warned..." );
+
+ // corners are counted from left to right and top to bottom
+ static const ChildIndexToPointData pCornerData[] =
+ { // index
+ { RID_SVXSTR_RECTCTL_ACC_CHLD_LT, RID_SVXSTR_RECTCTL_ACC_CHLD_LT, RectPoint::LT }, // 0
+ { RID_SVXSTR_RECTCTL_ACC_CHLD_MT, RID_SVXSTR_RECTCTL_ACC_CHLD_MT, RectPoint::MT }, // 1
+ { RID_SVXSTR_RECTCTL_ACC_CHLD_RT, RID_SVXSTR_RECTCTL_ACC_CHLD_RT, RectPoint::RT }, // 2
+ { RID_SVXSTR_RECTCTL_ACC_CHLD_LM, RID_SVXSTR_RECTCTL_ACC_CHLD_LM, RectPoint::LM }, // 3
+ { RID_SVXSTR_RECTCTL_ACC_CHLD_MM, RID_SVXSTR_RECTCTL_ACC_CHLD_MM, RectPoint::MM }, // 4
+ { RID_SVXSTR_RECTCTL_ACC_CHLD_RM, RID_SVXSTR_RECTCTL_ACC_CHLD_RM, RectPoint::RM }, // 5
+ { RID_SVXSTR_RECTCTL_ACC_CHLD_LB, RID_SVXSTR_RECTCTL_ACC_CHLD_LB, RectPoint::LB }, // 6
+ { RID_SVXSTR_RECTCTL_ACC_CHLD_MB, RID_SVXSTR_RECTCTL_ACC_CHLD_MB, RectPoint::MB }, // 7
+ { RID_SVXSTR_RECTCTL_ACC_CHLD_RB, RID_SVXSTR_RECTCTL_ACC_CHLD_RB, RectPoint::RB } // 8
+ };
+
+ return pCornerData + nIndex;
+}
+
+
+static tools::Long PointToIndex( RectPoint ePoint )
+{
+ tools::Long nRet( static_cast<tools::Long>(ePoint) );
+ // corner control
+ // corners are counted from left to right and top to bottom
+ DBG_ASSERT( int(RectPoint::LT) == 0 && int(RectPoint::MT) == 1 && int(RectPoint::RT) == 2 && int(RectPoint::LM) == 3 && int(RectPoint::MM) == 4 && int(RectPoint::RM) == 5 &&
+ int(RectPoint::LB) == 6 && int(RectPoint::MB) == 7 && int(RectPoint::RB) == 8, "*PointToIndex(): unexpected enum value!" );
+
+ nRet = static_cast<tools::Long>(ePoint);
+
+ return nRet;
+}
+
+SvxRectCtlAccessibleContext::SvxRectCtlAccessibleContext(SvxRectCtl* pRepr)
+ : mpRepr(pRepr)
+ , mnSelectedChild(NOCHILDSELECTED)
+{
+ {
+ ::SolarMutexGuard aSolarGuard;
+ msName = SvxResId( RID_SVXSTR_RECTCTL_ACC_CORN_NAME );
+ msDescription = SvxResId( RID_SVXSTR_RECTCTL_ACC_CORN_DESCR );
+ }
+
+ mvChildren.resize(MAX_NUM_OF_CHILDREN);
+}
+
+SvxRectCtlAccessibleContext::~SvxRectCtlAccessibleContext()
+{
+ ensureDisposed();
+}
+
+IMPLEMENT_FORWARD_XINTERFACE2( SvxRectCtlAccessibleContext, OAccessibleSelectionHelper, OAccessibleHelper_Base )
+IMPLEMENT_FORWARD_XTYPEPROVIDER2( SvxRectCtlAccessibleContext, OAccessibleSelectionHelper, OAccessibleHelper_Base )
+
+Reference< XAccessible > SAL_CALL SvxRectCtlAccessibleContext::getAccessibleAtPoint( const awt::Point& rPoint )
+{
+ ::osl::MutexGuard aGuard( m_aMutex );
+
+ Reference< XAccessible > xRet;
+
+ tools::Long nChild = mpRepr ? PointToIndex(mpRepr->GetApproxRPFromPixPt(rPoint)) : NOCHILDSELECTED;
+
+ if (nChild != NOCHILDSELECTED)
+ xRet = getAccessibleChild( nChild );
+
+ return xRet;
+}
+
+// XAccessibleContext
+sal_Int32 SAL_CALL SvxRectCtlAccessibleContext::getAccessibleChildCount()
+{
+ ::osl::MutexGuard aGuard( m_aMutex );
+
+ return SvxRectCtl::NO_CHILDREN;
+}
+
+Reference< XAccessible > SAL_CALL SvxRectCtlAccessibleContext::getAccessibleChild( sal_Int32 nIndex )
+{
+ checkChildIndex( nIndex );
+
+ Reference< XAccessible > xChild(mvChildren[ nIndex ]);
+ if( !xChild.is() )
+ {
+ ::SolarMutexGuard aSolarGuard;
+
+ ::osl::MutexGuard aGuard( m_aMutex );
+
+ xChild = mvChildren[ nIndex ].get();
+
+ if (!xChild.is() && mpRepr)
+ {
+ const ChildIndexToPointData* p = IndexToPoint( nIndex );
+ OUString aName(SvxResId(p->pResIdName));
+ OUString aDescr(SvxResId(p->pResIdDescr));
+
+ tools::Rectangle aFocusRect( mpRepr->CalculateFocusRectangle( p->ePoint ) );
+
+ rtl::Reference<SvxRectCtlChildAccessibleContext> pChild = new SvxRectCtlChildAccessibleContext(this, aName,
+ aDescr, aFocusRect, nIndex );
+ mvChildren[ nIndex ] = pChild;
+ xChild = pChild;
+
+ // set actual state
+ if( mnSelectedChild == nIndex )
+ pChild->setStateChecked( true );
+ }
+ }
+
+ return xChild;
+}
+
+Reference< XAccessible > SAL_CALL SvxRectCtlAccessibleContext::getAccessibleParent()
+{
+ ::osl::MutexGuard aGuard( m_aMutex );
+ if (mpRepr)
+ return mpRepr->getAccessibleParent();
+ return uno::Reference<css::accessibility::XAccessible>();
+}
+
+sal_Int16 SAL_CALL SvxRectCtlAccessibleContext::getAccessibleRole()
+{
+ return AccessibleRole::PANEL;
+}
+
+OUString SAL_CALL SvxRectCtlAccessibleContext::getAccessibleDescription()
+{
+ ::osl::MutexGuard aGuard( m_aMutex );
+ return msDescription + " Please use arrow key to selection.";
+}
+
+OUString SAL_CALL SvxRectCtlAccessibleContext::getAccessibleName()
+{
+ ::osl::MutexGuard aGuard( m_aMutex );
+ return msName;
+}
+
+/** Return empty reference to indicate that the relation set is not
+ supported.
+*/
+Reference< XAccessibleRelationSet > SAL_CALL SvxRectCtlAccessibleContext::getAccessibleRelationSet()
+{
+ ::osl::MutexGuard aGuard( m_aMutex );
+ if (mpRepr)
+ return mpRepr->get_accessible_relation_set();
+ return uno::Reference<css::accessibility::XAccessibleRelationSet>();
+}
+
+Reference< XAccessibleStateSet > SAL_CALL SvxRectCtlAccessibleContext::getAccessibleStateSet()
+{
+ ::osl::MutexGuard aGuard( m_aMutex );
+ rtl::Reference<utl::AccessibleStateSetHelper> pStateSetHelper = new utl::AccessibleStateSetHelper;
+
+ if (mpRepr)
+ {
+ pStateSetHelper->AddState( AccessibleStateType::ENABLED );
+ pStateSetHelper->AddState( AccessibleStateType::FOCUSABLE );
+ if( mpRepr->HasFocus() )
+ pStateSetHelper->AddState( AccessibleStateType::FOCUSED );
+ pStateSetHelper->AddState( AccessibleStateType::OPAQUE );
+
+ pStateSetHelper->AddState( AccessibleStateType::SHOWING );
+
+ if( mpRepr->IsVisible() )
+ pStateSetHelper->AddState( AccessibleStateType::VISIBLE );
+ }
+ else
+ pStateSetHelper->AddState( AccessibleStateType::DEFUNC );
+
+ return pStateSetHelper;
+}
+
+void SAL_CALL SvxRectCtlAccessibleContext::grabFocus()
+{
+ ::SolarMutexGuard aSolarGuard;
+ ::osl::MutexGuard aGuard( m_aMutex );
+
+ if (mpRepr)
+ mpRepr->GrabFocus();
+}
+
+sal_Int32 SvxRectCtlAccessibleContext::getForeground()
+{
+ ::SolarMutexGuard aSolarGuard;
+ ::osl::MutexGuard aGuard( m_aMutex );
+
+ //see SvxRectCtl::Paint
+ const StyleSettings& rStyles = Application::GetSettings().GetStyleSettings();
+ return sal_Int32(rStyles.GetLabelTextColor());
+}
+
+sal_Int32 SvxRectCtlAccessibleContext::getBackground( )
+{
+ ::SolarMutexGuard aSolarGuard;
+ ::osl::MutexGuard aGuard( m_aMutex );
+
+ //see SvxRectCtl::Paint
+ const StyleSettings& rStyles = Application::GetSettings().GetStyleSettings();
+ return sal_Int32(rStyles.GetDialogColor());
+}
+
+// XAccessibleSelection
+void SvxRectCtlAccessibleContext::implSelect(sal_Int32 nIndex, bool bSelect)
+{
+ ::SolarMutexGuard aSolarGuard;
+
+ ::osl::MutexGuard aGuard( m_aMutex );
+
+ checkChildIndex( nIndex );
+
+ const ChildIndexToPointData* pData = IndexToPoint( nIndex );
+
+ DBG_ASSERT(pData, "SvxRectCtlAccessibleContext::selectAccessibleChild(): this is an impossible state! Or at least should be...");
+
+ if (mpRepr)
+ {
+ if (bSelect)
+ {
+ // this does all what is needed, including the change of the child's state!
+ mpRepr->SetActualRP( pData->ePoint );
+ }
+ else
+ {
+ SAL_WARN( "svx", "SvxRectCtlAccessibleContext::clearAccessibleSelection() is not possible!" );
+ }
+ }
+}
+
+bool SvxRectCtlAccessibleContext::implIsSelected( sal_Int32 nIndex )
+{
+ ::osl::MutexGuard aGuard( m_aMutex );
+
+ checkChildIndex( nIndex );
+
+ return nIndex == mnSelectedChild;
+}
+
+// internals
+void SvxRectCtlAccessibleContext::checkChildIndex( tools::Long nIndex )
+{
+ if( nIndex < 0 || nIndex >= getAccessibleChildCount() )
+ throw lang::IndexOutOfBoundsException();
+}
+
+void SvxRectCtlAccessibleContext::FireChildFocus( RectPoint eButton )
+{
+ ::osl::MutexGuard aGuard( m_aMutex );
+ tools::Long nNew = PointToIndex( eButton );
+ tools::Long nNumOfChildren = getAccessibleChildCount();
+ if( nNew < nNumOfChildren )
+ {
+ // select new child
+ mnSelectedChild = nNew;
+ if( nNew != NOCHILDSELECTED )
+ {
+ if( mvChildren[ nNew ].is() )
+ mvChildren[ nNew ]->FireFocusEvent();
+ }
+ else
+ {
+ Any aOld;
+ Any aNew;
+ aNew <<= AccessibleStateType::FOCUSED;
+ NotifyAccessibleEvent(AccessibleEventId::STATE_CHANGED, aOld, aNew);
+ }
+ }
+ else
+ mnSelectedChild = NOCHILDSELECTED;
+}
+
+void SvxRectCtlAccessibleContext::selectChild( tools::Long nNew )
+{
+ ::osl::MutexGuard aGuard( m_aMutex );
+ if( nNew == mnSelectedChild )
+ return;
+
+ tools::Long nNumOfChildren = getAccessibleChildCount();
+ if( nNew < nNumOfChildren )
+ { // valid index
+ if( mnSelectedChild != NOCHILDSELECTED )
+ { // deselect old selected child if one is selected
+ SvxRectCtlChildAccessibleContext* pChild = mvChildren[ mnSelectedChild ].get();
+ if( pChild )
+ pChild->setStateChecked( false );
+ }
+
+ // select new child
+ mnSelectedChild = nNew;
+
+ if( nNew != NOCHILDSELECTED )
+ {
+ if( mvChildren[ nNew ].is() )
+ mvChildren[ nNew ]->setStateChecked( true );
+ }
+ }
+ else
+ mnSelectedChild = NOCHILDSELECTED;
+}
+
+void SvxRectCtlAccessibleContext::selectChild(RectPoint eButton )
+{
+ // no guard -> is done in next selectChild
+ selectChild(PointToIndex( eButton ));
+}
+
+void SAL_CALL SvxRectCtlAccessibleContext::disposing()
+{
+ ::osl::MutexGuard aGuard(m_aMutex);
+ OAccessibleSelectionHelper::disposing();
+ for (auto & rxChild : mvChildren)
+ {
+ if( rxChild.is() )
+ rxChild->dispose();
+ }
+ mvChildren.clear();
+ mpRepr = nullptr;
+}
+
+awt::Rectangle SvxRectCtlAccessibleContext::implGetBounds()
+{
+ ::SolarMutexGuard aSolarGuard;
+ ::osl::MutexGuard aGuard( m_aMutex );
+
+ awt::Rectangle aRet;
+
+ if (mpRepr)
+ {
+ const Point aOutPos;
+ Size aOutSize(mpRepr->GetOutputSizePixel());
+
+ aRet.X = aOutPos.X();
+ aRet.Y = aOutPos.Y();
+ aRet.Width = aOutSize.Width();
+ aRet.Height = aOutSize.Height();
+ }
+
+ return aRet;
+}
+
+SvxRectCtlChildAccessibleContext::SvxRectCtlChildAccessibleContext(
+ const Reference<XAccessible>& rxParent,
+ const OUString& rName,
+ const OUString& rDescription,
+ const tools::Rectangle& rBoundingBox,
+ tools::Long nIndexInParent )
+ : msDescription( rDescription )
+ , msName( rName )
+ , mxParent(rxParent)
+ , maBoundingBox( rBoundingBox )
+ , mnIndexInParent( nIndexInParent )
+ , mbIsChecked( false )
+{
+}
+
+SvxRectCtlChildAccessibleContext::~SvxRectCtlChildAccessibleContext()
+{
+ ensureDisposed();
+}
+
+Reference< XAccessible > SAL_CALL SvxRectCtlChildAccessibleContext::getAccessibleAtPoint( const awt::Point& /*rPoint*/ )
+{
+ return Reference< XAccessible >();
+}
+
+void SAL_CALL SvxRectCtlChildAccessibleContext::grabFocus()
+{
+}
+
+sal_Int32 SvxRectCtlChildAccessibleContext::getForeground( )
+{
+ ::SolarMutexGuard aSolarGuard;
+ ::osl::MutexGuard aGuard( m_aMutex );
+
+ //see SvxRectCtl::Paint
+ const StyleSettings& rStyles = Application::GetSettings().GetStyleSettings();
+ return sal_Int32(rStyles.GetLabelTextColor());
+}
+
+sal_Int32 SvxRectCtlChildAccessibleContext::getBackground( )
+{
+ ::SolarMutexGuard aSolarGuard;
+ ::osl::MutexGuard aGuard( m_aMutex );
+
+ //see SvxRectCtl::Paint
+ const StyleSettings& rStyles = Application::GetSettings().GetStyleSettings();
+ return sal_Int32(rStyles.GetDialogColor());
+}
+
+// XAccessibleContext
+sal_Int32 SAL_CALL SvxRectCtlChildAccessibleContext::getAccessibleChildCount()
+{
+ return 0;
+}
+
+Reference< XAccessible > SAL_CALL SvxRectCtlChildAccessibleContext::getAccessibleChild( sal_Int32 /*nIndex*/ )
+{
+ throw lang::IndexOutOfBoundsException();
+}
+
+Reference< XAccessible > SAL_CALL SvxRectCtlChildAccessibleContext::getAccessibleParent()
+{
+ return mxParent;
+}
+
+sal_Int16 SAL_CALL SvxRectCtlChildAccessibleContext::getAccessibleRole()
+{
+ return AccessibleRole::RADIO_BUTTON;
+}
+
+OUString SAL_CALL SvxRectCtlChildAccessibleContext::getAccessibleDescription()
+{
+ ::osl::MutexGuard aGuard( m_aMutex );
+ return msDescription;
+}
+
+OUString SAL_CALL SvxRectCtlChildAccessibleContext::getAccessibleName()
+{
+ ::osl::MutexGuard aGuard( m_aMutex );
+ return msName;
+}
+
+/** Return empty reference to indicate that the relation set is not
+ supported.
+*/
+Reference<XAccessibleRelationSet> SAL_CALL SvxRectCtlChildAccessibleContext::getAccessibleRelationSet()
+{
+ rtl::Reference<utl::AccessibleRelationSetHelper> pRelationSetHelper = new utl::AccessibleRelationSetHelper;
+ if( mxParent.is() )
+ {
+ uno::Sequence< uno::Reference< uno::XInterface > > aSequence { mxParent };
+ pRelationSetHelper->AddRelation( css::accessibility::AccessibleRelation( css::accessibility::AccessibleRelationType::MEMBER_OF, aSequence ) );
+ }
+
+ return pRelationSetHelper;
+}
+
+Reference< XAccessibleStateSet > SAL_CALL SvxRectCtlChildAccessibleContext::getAccessibleStateSet()
+{
+ ::osl::MutexGuard aGuard( m_aMutex );
+ rtl::Reference<utl::AccessibleStateSetHelper> pStateSetHelper = new utl::AccessibleStateSetHelper;
+
+ if (!rBHelper.bDisposed)
+ {
+ if( mbIsChecked )
+ {
+ pStateSetHelper->AddState( AccessibleStateType::CHECKED );
+ }
+
+ pStateSetHelper->AddState( AccessibleStateType::ENABLED );
+ pStateSetHelper->AddState( AccessibleStateType::SENSITIVE );
+ pStateSetHelper->AddState( AccessibleStateType::OPAQUE );
+ pStateSetHelper->AddState( AccessibleStateType::SELECTABLE );
+ pStateSetHelper->AddState( AccessibleStateType::SHOWING );
+ pStateSetHelper->AddState( AccessibleStateType::VISIBLE );
+ }
+ else
+ pStateSetHelper->AddState( AccessibleStateType::DEFUNC );
+
+ return pStateSetHelper;
+}
+
+// XAccessibleValue
+Any SAL_CALL SvxRectCtlChildAccessibleContext::getCurrentValue()
+{
+ Any aRet;
+ aRet <<= ( mbIsChecked? 1.0 : 0.0 );
+ return aRet;
+}
+
+sal_Bool SAL_CALL SvxRectCtlChildAccessibleContext::setCurrentValue( const Any& /*aNumber*/ )
+{
+ return false;
+}
+
+Any SAL_CALL SvxRectCtlChildAccessibleContext::getMaximumValue()
+{
+ Any aRet;
+ aRet <<= 1.0;
+ return aRet;
+}
+
+Any SAL_CALL SvxRectCtlChildAccessibleContext::getMinimumValue()
+{
+ Any aRet;
+ aRet <<= 0.0;
+ return aRet;
+}
+
+Any SAL_CALL SvxRectCtlChildAccessibleContext::getMinimumIncrement()
+{
+ Any aRet;
+ aRet <<= 1.0;
+ return aRet;
+}
+
+
+// XAccessibleAction
+
+
+sal_Int32 SvxRectCtlChildAccessibleContext::getAccessibleActionCount( )
+{
+ return 1;
+}
+
+
+sal_Bool SvxRectCtlChildAccessibleContext::doAccessibleAction ( sal_Int32 nIndex )
+{
+ ::osl::MutexGuard aGuard( m_aMutex );
+
+ if ( nIndex < 0 || nIndex >= getAccessibleActionCount() )
+ throw IndexOutOfBoundsException();
+
+ Reference<XAccessibleSelection> xSelection( mxParent, UNO_QUERY);
+
+ xSelection->selectAccessibleChild(mnIndexInParent);
+
+ return true;
+}
+
+
+OUString SvxRectCtlChildAccessibleContext::getAccessibleActionDescription ( sal_Int32 nIndex )
+{
+ ::osl::MutexGuard aGuard( m_aMutex );
+
+ if ( nIndex < 0 || nIndex >= getAccessibleActionCount() )
+ throw IndexOutOfBoundsException();
+
+ return "select";
+}
+
+
+Reference< XAccessibleKeyBinding > SvxRectCtlChildAccessibleContext::getAccessibleActionKeyBinding( sal_Int32 nIndex )
+{
+ ::osl::MutexGuard aGuard( m_aMutex );
+
+ if ( nIndex < 0 || nIndex >= getAccessibleActionCount() )
+ throw IndexOutOfBoundsException();
+
+ return Reference< XAccessibleKeyBinding >();
+}
+
+void SAL_CALL SvxRectCtlChildAccessibleContext::disposing()
+{
+ OAccessibleComponentHelper::disposing();
+ mxParent.clear();
+}
+
+awt::Rectangle SvxRectCtlChildAccessibleContext::implGetBounds( )
+{
+ // no guard necessary, because no one changes maBoundingBox after creating it
+ return AWTRectangle(maBoundingBox);
+}
+
+void SvxRectCtlChildAccessibleContext::setStateChecked( bool bChecked )
+{
+ if( mbIsChecked == bChecked )
+ return;
+
+ mbIsChecked = bChecked;
+
+ Any aOld;
+ Any aNew;
+ Any& rMod = bChecked? aNew : aOld;
+
+ //Send the STATE_CHANGED(Focused) event to accessible
+ rMod <<= AccessibleStateType::FOCUSED;
+ NotifyAccessibleEvent(AccessibleEventId::STATE_CHANGED, aOld, aNew);
+
+ rMod <<= AccessibleStateType::CHECKED;
+
+ NotifyAccessibleEvent(AccessibleEventId::STATE_CHANGED, aOld, aNew);
+}
+
+void SvxRectCtlChildAccessibleContext::FireFocusEvent()
+{
+ Any aOld;
+ Any aNew;
+ aNew <<= AccessibleStateType::FOCUSED;
+ NotifyAccessibleEvent(AccessibleEventId::STATE_CHANGED, aOld, aNew);
+}
+
+IMPLEMENT_FORWARD_XINTERFACE2( SvxRectCtlChildAccessibleContext, OAccessibleComponentHelper, OAccessibleHelper_Base_3 )
+IMPLEMENT_FORWARD_XTYPEPROVIDER2( SvxRectCtlChildAccessibleContext, OAccessibleComponentHelper, OAccessibleHelper_Base_3 )
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/core/extedit.cxx b/svx/source/core/extedit.cxx
new file mode 100644
index 000000000..3225e47f0
--- /dev/null
+++ b/svx/source/core/extedit.cxx
@@ -0,0 +1,204 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+#include <svx/extedit.hxx>
+
+#include <vcl/graph.hxx>
+#include <vcl/GraphicObject.hxx>
+#include <vcl/cvtgrf.hxx>
+#include <vcl/graphicfilter.hxx>
+#include <svx/xoutbmp.hxx>
+#include <svx/graphichelper.hxx>
+#include <svx/svdpagv.hxx>
+#include <svx/svdograf.hxx>
+#include <svx/fmview.hxx>
+#include <salhelper/thread.hxx>
+#include <sal/log.hxx>
+#include <osl/file.hxx>
+#include <svtools/filechangedchecker.hxx>
+#include <tools/diagnose_ex.h>
+#include <unotools/ucbstreamhelper.hxx>
+#include <comphelper/processfactory.hxx>
+
+#include <memory>
+
+#include <com/sun/star/system/SystemShellExecute.hpp>
+#include <com/sun/star/system/SystemShellExecuteFlags.hpp>
+
+using namespace css::uno;
+using namespace css::system;
+
+ExternalToolEdit::ExternalToolEdit()
+{
+}
+
+ExternalToolEdit::~ExternalToolEdit()
+{
+}
+
+void ExternalToolEdit::HandleCloseEvent(ExternalToolEdit* pData)
+{
+ Graphic newGraphic;
+
+ //import the temp file image stream into the newGraphic
+ std::unique_ptr<SvStream> pStream(utl::UcbStreamHelper::CreateStream(pData->m_aFileName, StreamMode::READ));
+ if(pStream)
+ {
+ GraphicConverter::Import(*pStream, newGraphic);
+
+ // Now update the Graphic in the shell by re-reading from the newGraphic
+ pData->Update( newGraphic );
+ }
+}
+
+void ExternalToolEdit::StartListeningEvent()
+{
+ //Start an event listener implemented via VCL timeout
+ assert(!m_pChecker);
+ m_pChecker.reset(new FileChangedChecker(
+ m_aFileName, [this] () { return HandleCloseEvent(this); }));
+}
+
+namespace {
+
+// self-destructing thread to make shell execute async
+class ExternalToolEditThread
+ : public ::salhelper::Thread
+{
+private:
+ OUString const m_aFileName;
+
+ virtual void execute() override;
+
+public:
+ explicit ExternalToolEditThread(OUString const& rFileName)
+ : ::salhelper::Thread("ExternalToolEdit")
+ , m_aFileName(rFileName)
+ {}
+};
+
+}
+
+void ExternalToolEditThread::execute()
+{
+ try
+ {
+ Reference<XSystemShellExecute> const xSystemShellExecute(
+ SystemShellExecute::create( ::comphelper::getProcessComponentContext()));
+ xSystemShellExecute->execute(m_aFileName, OUString(),
+ SystemShellExecuteFlags::URIS_ONLY);
+ }
+ catch (Exception const&)
+ {
+ TOOLS_WARN_EXCEPTION("svx", "ExternalToolEditThread");
+ }
+}
+
+void ExternalToolEdit::Edit(GraphicObject const*const pGraphicObject)
+{
+ //Get the graphic from the GraphicObject
+ const Graphic& aGraphic = pGraphicObject->GetGraphic();
+
+ //get the Preferred File Extension for this graphic
+ OUString fExtension;
+ GraphicHelper::GetPreferredExtension(fExtension, aGraphic);
+
+ //Create the temp File
+ OUString aTempFileBase;
+ OUString aTempFileName;
+
+ osl::FileBase::RC rc =
+ osl::FileBase::createTempFile(nullptr, nullptr, &aTempFileBase);
+ if (osl::FileBase::E_None != rc)
+ {
+ SAL_WARN("svx", "ExternalToolEdit::Edit: cannot create temp file");
+ return;
+ }
+
+ // Move it to a file name with image extension properly set
+ aTempFileName = aTempFileBase + "." + fExtension;
+ // FIXME: this is pretty stupid, need a better osl temp file API
+ rc = osl::File::move(aTempFileBase, aTempFileName);
+ if (osl::FileBase::E_None != rc)
+ {
+ SAL_WARN("svx", "ExternalToolEdit::Edit: cannot move temp file");
+ return;
+ }
+
+ //Write Graphic to the Temp File
+ GraphicFilter& rGraphicFilter = GraphicFilter::GetGraphicFilter();
+ sal_uInt16 nFilter(rGraphicFilter.GetExportFormatNumberForShortName(fExtension));
+
+ OUString aFilter(rGraphicFilter.GetExportFormatShortName(nFilter));
+
+ // Write the Graphic to the file now
+ XOutBitmap::WriteGraphic(aGraphic, aTempFileName, aFilter, XOutFlags::UseNativeIfPossible | XOutFlags::DontExpandFilename);
+
+ // There is a possibility that sPath extension might have been changed if the
+ // provided extension is not writable
+ m_aFileName = aTempFileName;
+
+ //Create a thread
+
+ rtl::Reference<ExternalToolEditThread> const pThread(
+ new ExternalToolEditThread(m_aFileName));
+ pThread->launch();
+
+ StartListeningEvent();
+}
+
+SdrExternalToolEdit::SdrExternalToolEdit(
+ FmFormView* pView,
+ SdrObject* pObj)
+: m_pView(pView)
+ ,m_pObj(pObj)
+{
+ assert(m_pObj && m_pView);
+ StartListening(m_pObj->getSdrModelFromSdrObject());
+}
+
+
+void SdrExternalToolEdit::Notify(SfxBroadcaster & rBC, SfxHint const& rHint)
+{
+ if (rHint.GetId() != SfxHintId::ThisIsAnSdrHint)
+ return;
+ SdrHint const*const pSdrHint(static_cast<SdrHint const*>(&rHint));
+ if (SdrHintKind::ModelCleared == pSdrHint->GetKind()
+ || (pSdrHint->GetObject() == m_pObj
+ && SdrHintKind::ObjectRemoved == pSdrHint->GetKind()))
+ {
+ m_pView = nullptr;
+ m_pObj = nullptr;
+ m_pChecker.reset(); // avoid modifying deleted object
+ EndListening(rBC);
+ }
+}
+
+void SdrExternalToolEdit::Update(Graphic & rGraphic)
+{
+ assert(m_pObj && m_pView); // timer should be deleted by Notify() too
+ SdrPageView *const pPageView = m_pView->GetSdrPageView();
+ if (!pPageView)
+ return;
+
+ SdrGrafObj *const pNewObj(static_cast<SdrGrafObj*>(m_pObj->CloneSdrObject(m_pObj->getSdrModelFromSdrObject())));
+ assert(pNewObj);
+ OUString const description =
+ m_pView->GetDescriptionOfMarkedObjects() + " External Edit";
+ m_pView->BegUndo(description);
+ pNewObj->SetGraphicObject(rGraphic);
+ // set to new object before ReplaceObjectAtView() so that Notify() will
+ // not delete the running timer and crash
+ SdrObject *const pOldObj = m_pObj;
+ m_pObj = pNewObj;
+ m_pView->ReplaceObjectAtView(pOldObj, *pPageView, pNewObj);
+ m_pView->EndUndo();
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/core/graphichelper.cxx b/svx/source/core/graphichelper.cxx
new file mode 100644
index 000000000..853be47d3
--- /dev/null
+++ b/svx/source/core/graphichelper.cxx
@@ -0,0 +1,485 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * 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/graphicfilter.hxx>
+#include <sfx2/docfile.hxx>
+#include <sfx2/filedlghelper.hxx>
+#include <svx/xoutbmp.hxx>
+#include <svx/dialmgr.hxx>
+#include <svx/graphichelper.hxx>
+#include <svx/strings.hrc>
+#include <tools/diagnose_ex.h>
+#include <vcl/svapp.hxx>
+#include <vcl/weld.hxx>
+
+#include <comphelper/processfactory.hxx>
+#include <comphelper/propertyvalue.hxx>
+
+#include <com/sun/star/beans/XPropertySet.hpp>
+#include <com/sun/star/beans/PropertyValue.hpp>
+#include <com/sun/star/container/NoSuchElementException.hpp>
+#include <com/sun/star/document/XExporter.hpp>
+#include <com/sun/star/drawing/GraphicExportFilter.hpp>
+#include <com/sun/star/drawing/XShape.hpp>
+#include <com/sun/star/lang/XMultiServiceFactory.hpp>
+#include <com/sun/star/lang/XComponent.hpp>
+#include <com/sun/star/io/XInputStream.hpp>
+#include <com/sun/star/ucb/SimpleFileAccess.hpp>
+#include <com/sun/star/ui/dialogs/XFilePicker3.hpp>
+#include <com/sun/star/ui/dialogs/TemplateDescription.hpp>
+#include <com/sun/star/beans/XPropertyAccess.hpp>
+#include <com/sun/star/task/ErrorCodeIOException.hpp>
+#include <com/sun/star/graphic/XGraphic.hpp>
+#include <com/sun/star/drawing/ShapeCollection.hpp>
+
+#include <map>
+
+#include <unotools/streamwrap.hxx>
+
+using namespace css::uno;
+using namespace css::lang;
+using namespace css::graphic;
+using namespace css::ucb;
+using namespace css::beans;
+using namespace css::io;
+using namespace css::document;
+using namespace css::ui::dialogs;
+using namespace css::container;
+using namespace com::sun::star::task;
+
+using namespace sfx2;
+
+namespace drawing = com::sun::star::drawing;
+
+void GraphicHelper::GetPreferredExtension( OUString& rExtension, const Graphic& rGraphic )
+{
+ OUString aExtension = "png";
+ auto const & rVectorGraphicDataPtr(rGraphic.getVectorGraphicData());
+
+ if (rVectorGraphicDataPtr && !rVectorGraphicDataPtr->getBinaryDataContainer().isEmpty())
+ {
+ switch (rVectorGraphicDataPtr->getType())
+ {
+ case VectorGraphicDataType::Wmf:
+ aExtension = "wmf";
+ break;
+ case VectorGraphicDataType::Emf:
+ aExtension = "emf";
+ break;
+ default: // case VectorGraphicDataType::Svg:
+ aExtension = "svg";
+ break;
+ }
+
+ rExtension = aExtension;
+ return;
+ }
+
+ switch( rGraphic.GetGfxLink().GetType() )
+ {
+ case GfxLinkType::NativeGif:
+ aExtension = "gif";
+ break;
+ case GfxLinkType::NativeTif:
+ aExtension = "tif";
+ break;
+ case GfxLinkType::NativeWmf:
+ aExtension = "wmf";
+ break;
+ case GfxLinkType::NativeMet:
+ aExtension = "met";
+ break;
+ case GfxLinkType::NativePct:
+ aExtension = "pct";
+ break;
+ case GfxLinkType::NativeJpg:
+ aExtension = "jpg";
+ break;
+ case GfxLinkType::NativeBmp:
+ aExtension = "bmp";
+ break;
+ case GfxLinkType::NativeSvg:
+ aExtension = "svg";
+ break;
+ case GfxLinkType::NativePdf:
+ aExtension = "pdf";
+ break;
+ case GfxLinkType::NativeWebp:
+ aExtension = "webp";
+ break;
+ default:
+ break;
+ }
+ rExtension = aExtension;
+}
+
+OUString GraphicHelper::GetImageType(const Graphic& rGraphic)
+{
+ OUString aGraphicTypeString = SvxResId(STR_IMAGE_UNKNOWN);
+ auto pGfxLink = rGraphic.GetSharedGfxLink();
+ if (pGfxLink)
+ {
+ switch (pGfxLink->GetType())
+ {
+ case GfxLinkType::NativeGif:
+ aGraphicTypeString = SvxResId(STR_IMAGE_GIF);
+ break;
+ case GfxLinkType::NativeJpg:
+ aGraphicTypeString = SvxResId(STR_IMAGE_JPEG);
+ break;
+ case GfxLinkType::NativePng:
+ aGraphicTypeString = SvxResId(STR_IMAGE_PNG);
+ break;
+ case GfxLinkType::NativeTif:
+ aGraphicTypeString = SvxResId(STR_IMAGE_TIFF);
+ break;
+ case GfxLinkType::NativeWmf:
+ aGraphicTypeString = SvxResId(STR_IMAGE_WMF);
+ break;
+ case GfxLinkType::NativeMet:
+ aGraphicTypeString = SvxResId(STR_IMAGE_MET);
+ break;
+ case GfxLinkType::NativePct:
+ aGraphicTypeString = SvxResId(STR_IMAGE_PCT);
+ break;
+ case GfxLinkType::NativeSvg:
+ aGraphicTypeString = SvxResId(STR_IMAGE_SVG);
+ break;
+ case GfxLinkType::NativeBmp:
+ aGraphicTypeString = SvxResId(STR_IMAGE_BMP);
+ break;
+ case GfxLinkType::NativeWebp:
+ aGraphicTypeString = SvxResId(STR_IMAGE_WEBP);
+ break;
+ default:
+ break;
+ }
+ }
+ return aGraphicTypeString;
+}
+namespace {
+
+
+bool lcl_ExecuteFilterDialog( const Sequence< PropertyValue >& rPropsForDialog,
+ Sequence< PropertyValue >& rFilterData )
+{
+ bool bStatus = false;
+ try
+ {
+ Reference< XExecutableDialog > xFilterDialog(
+ comphelper::getProcessServiceFactory()->createInstance( "com.sun.star.svtools.SvFilterOptionsDialog" ), UNO_QUERY );
+ Reference< XPropertyAccess > xFilterProperties( xFilterDialog, UNO_QUERY );
+
+ if( xFilterDialog.is() && xFilterProperties.is() )
+ {
+ xFilterProperties->setPropertyValues( rPropsForDialog );
+ if( xFilterDialog->execute() )
+ {
+ bStatus = true;
+ const Sequence< PropertyValue > aPropsFromDialog = xFilterProperties->getPropertyValues();
+ for ( const auto& rProp : aPropsFromDialog )
+ {
+ if (rProp.Name == "FilterData")
+ {
+ rProp.Value >>= rFilterData;
+ }
+ }
+ }
+ }
+ }
+ catch( const NoSuchElementException& e )
+ {
+ // the filter name is unknown
+ throw ErrorCodeIOException(
+ ("lcl_ExecuteFilterDialog: NoSuchElementException"
+ " \"" + e.Message + "\": ERRCODE_IO_ABORT"),
+ Reference< XInterface >(), sal_uInt32(ERRCODE_IO_INVALIDPARAMETER));
+ }
+ catch( const ErrorCodeIOException& )
+ {
+ throw;
+ }
+ catch( const Exception& )
+ {
+ TOOLS_WARN_EXCEPTION("sfx.doc", "ignoring");
+ }
+
+ return bStatus;
+}
+} // anonymous ns
+
+OUString GraphicHelper::ExportGraphic(weld::Window* pParent, const Graphic& rGraphic, const OUString& rGraphicName)
+{
+ FileDialogHelper aDialogHelper(TemplateDescription::FILESAVE_AUTOEXTENSION, FileDialogFlags::NONE, pParent);
+ Reference < XFilePicker3 > xFilePicker = aDialogHelper.GetFilePicker();
+
+ // fish out the graphic's name
+ aDialogHelper.SetContext(FileDialogHelper::ExportImage);
+ aDialogHelper.SetTitle( SvxResId(RID_SVXSTR_EXPORT_GRAPHIC_TITLE));
+ INetURLObject aURL;
+ aURL.SetSmartURL( rGraphicName );
+ aDialogHelper.SetFileName(aURL.GetLastName());
+
+ GraphicFilter& rGraphicFilter = GraphicFilter::GetGraphicFilter();
+ const sal_uInt16 nCount = rGraphicFilter.GetExportFormatCount();
+
+ OUString aExtension(aURL.GetFileExtension());
+ if( aExtension.isEmpty() )
+ {
+ GetPreferredExtension( aExtension, rGraphic );
+ }
+
+ aExtension = aExtension.toAsciiLowerCase();
+ sal_uInt16 nDefaultFilter = USHRT_MAX;
+
+ for ( sal_uInt16 i = 0; i < nCount; i++ )
+ {
+ xFilePicker->appendFilter( rGraphicFilter.GetExportFormatName( i ), rGraphicFilter.GetExportWildcard( i ) );
+ OUString aFormatShortName = rGraphicFilter.GetExportFormatShortName( i );
+ if ( aFormatShortName.equalsIgnoreAsciiCase( aExtension ) )
+ {
+ nDefaultFilter = i;
+ }
+ }
+ if ( USHRT_MAX == nDefaultFilter )
+ {
+ // "wrong" extension?
+ GetPreferredExtension( aExtension, rGraphic );
+ for ( sal_uInt16 i = 0; i < nCount; ++i )
+ if ( aExtension == rGraphicFilter.GetExportFormatShortName( i ).toAsciiLowerCase() )
+ {
+ nDefaultFilter = i;
+ break;
+ }
+ }
+
+ if( USHRT_MAX != nDefaultFilter )
+ {
+ xFilePicker->setCurrentFilter( rGraphicFilter.GetExportFormatName( nDefaultFilter ) ) ;
+
+ if( aDialogHelper.Execute() == ERRCODE_NONE )
+ {
+ OUString sPath( xFilePicker->getFiles().getConstArray()[0] );
+ if( !rGraphicName.isEmpty() &&
+ nDefaultFilter == rGraphicFilter.GetExportFormatNumber( xFilePicker->getCurrentFilter()))
+ {
+ // try to save the original graphic
+ SfxMedium aIn( rGraphicName, StreamMode::READ | StreamMode::NOCREATE );
+ if( aIn.GetInStream() && !aIn.GetInStream()->GetError() )
+ {
+ SfxMedium aOut( sPath, StreamMode::WRITE | StreamMode::SHARE_DENYNONE);
+ if( aOut.GetOutStream() && !aOut.GetOutStream()->GetError())
+ {
+ aOut.GetOutStream()->WriteStream( *aIn.GetInStream() );
+ if ( ERRCODE_NONE == aIn.GetError() )
+ {
+ aOut.Close();
+ aOut.Commit();
+ if ( ERRCODE_NONE == aOut.GetError() )
+ return sPath;
+ }
+ }
+ }
+ }
+
+ sal_uInt16 nFilter;
+ if ( !xFilePicker->getCurrentFilter().isEmpty() && rGraphicFilter.GetExportFormatCount() )
+ {
+ nFilter = rGraphicFilter.GetExportFormatNumber( xFilePicker->getCurrentFilter() );
+ }
+ else
+ {
+ nFilter = GRFILTER_FORMAT_DONTKNOW;
+ }
+ OUString aFilter( rGraphicFilter.GetExportFormatShortName( nFilter ) );
+
+ if ( rGraphic.GetType() == GraphicType::Bitmap )
+ {
+ Graphic aGraphic = rGraphic;
+ Reference<XGraphic> xGraphic = aGraphic.GetXGraphic();
+
+ OUString aExportFilter = rGraphicFilter.GetExportInternalFilterName(nFilter);
+
+ Sequence< PropertyValue > aPropsForDialog{
+ comphelper::makePropertyValue("Graphic", xGraphic),
+ comphelper::makePropertyValue("FilterName", aExportFilter)
+ };
+
+ Sequence< PropertyValue > aFilterData;
+ bool bStatus = lcl_ExecuteFilterDialog(aPropsForDialog, aFilterData);
+ if (bStatus)
+ {
+ sal_Int32 nWidth = 0;
+ sal_Int32 nHeight = 0;
+
+ for (const auto& rProp : std::as_const(aFilterData))
+ {
+ if (rProp.Name == "PixelWidth")
+ {
+ rProp.Value >>= nWidth;
+ }
+ else if (rProp.Name == "PixelHeight")
+ {
+ rProp.Value >>= nHeight;
+ }
+ }
+
+ // scaling must performed here because png/jpg writer s
+ // do not take care of that.
+ Size aSizePixel( aGraphic.GetSizePixel() );
+ if( nWidth && nHeight &&
+ ( ( nWidth != aSizePixel.Width() ) ||
+ ( nHeight != aSizePixel.Height() ) ) )
+ {
+ BitmapEx aBmpEx( aGraphic.GetBitmapEx() );
+ // export: use highest quality
+ aBmpEx.Scale( Size( nWidth, nHeight ), BmpScaleFlag::Lanczos );
+ aGraphic = aBmpEx;
+ }
+
+ XOutBitmap::WriteGraphic( aGraphic, sPath, aFilter,
+ XOutFlags::DontExpandFilename |
+ XOutFlags::DontAddExtension |
+ XOutFlags::UseNativeIfPossible,
+ nullptr, &aFilterData );
+ return sPath;
+ }
+ }
+ else
+ {
+ XOutBitmap::WriteGraphic( rGraphic, sPath, aFilter,
+ XOutFlags::DontExpandFilename |
+ XOutFlags::DontAddExtension |
+ XOutFlags::UseNativeIfPossible );
+ }
+ }
+ }
+ return OUString();
+}
+
+void GraphicHelper::SaveShapeAsGraphicToPath(
+ const css::uno::Reference<css::lang::XComponent>& xComponent,
+ const css::uno::Reference<css::drawing::XShape>& xShape, const OUString& aExportMimeType,
+ const OUString& sPath)
+{
+ Reference<XComponentContext> xContext(::comphelper::getProcessComponentContext());
+ Reference<XInputStream> xGraphStream;
+
+ if (xGraphStream.is())
+ {
+ Reference<XSimpleFileAccess3> xFileAccess = SimpleFileAccess::create(xContext);
+ xFileAccess->writeFile(sPath, xGraphStream);
+ }
+ else if (xComponent.is() && aExportMimeType == "application/pdf")
+ {
+ css::uno::Reference<css::lang::XMultiServiceFactory> xMSF(xContext->getServiceManager(),
+ css::uno::UNO_QUERY);
+ css::uno::Reference<css::document::XExporter> xExporter(
+ xMSF->createInstance("com.sun.star.comp.PDF.PDFFilter"), css::uno::UNO_QUERY);
+ xExporter->setSourceDocument(xComponent);
+
+ css::uno::Reference<css::drawing::XShapes> xShapes
+ = css::drawing::ShapeCollection::create(comphelper::getProcessComponentContext());
+ xShapes->add(xShape);
+ css::uno::Sequence<PropertyValue> aFilterData{
+ comphelper::makePropertyValue("Selection", xShapes),
+ };
+ SvFileStream aStream(sPath, StreamMode::READWRITE | StreamMode::TRUNC);
+ css::uno::Reference<css::io::XOutputStream> xStream(new utl::OStreamWrapper(aStream));
+ css::uno::Sequence<PropertyValue> aDescriptor{
+ comphelper::makePropertyValue("FilterData", aFilterData),
+ comphelper::makePropertyValue("OutputStream", xStream)
+ };
+ css::uno::Reference<css::document::XFilter> xFilter(xExporter, css::uno::UNO_QUERY);
+ xFilter->filter(aDescriptor);
+ }
+ else
+ {
+ Reference<css::drawing::XGraphicExportFilter> xGraphicExporter
+ = css::drawing::GraphicExportFilter::create(xContext);
+
+ Sequence<PropertyValue> aDescriptor{ comphelper::makePropertyValue("MediaType",
+ aExportMimeType),
+ comphelper::makePropertyValue("URL", sPath) };
+
+ Reference<XComponent> xSourceDocument(xShape, UNO_QUERY_THROW);
+ xGraphicExporter->setSourceDocument(xSourceDocument);
+ xGraphicExporter->filter(aDescriptor);
+ }
+}
+
+void GraphicHelper::SaveShapeAsGraphic(weld::Window* pParent,
+ const css::uno::Reference<css::lang::XComponent>& xComponent,
+ const Reference<drawing::XShape>& xShape)
+{
+ try
+ {
+ Reference< XPropertySet > xShapeSet( xShape, UNO_QUERY_THROW );
+
+ FileDialogHelper aDialogHelper(TemplateDescription::FILESAVE_AUTOEXTENSION, FileDialogFlags::NONE, pParent);
+ Reference < XFilePicker3 > xFilePicker = aDialogHelper.GetFilePicker();
+ aDialogHelper.SetContext(FileDialogHelper::ExportImage);
+ aDialogHelper.SetTitle( SvxResId(RID_SVXSTR_SAVEAS_IMAGE) );
+
+ // populate filter dialog filter list and select default filter to match graphic mime type
+
+ GraphicFilter& rGraphicFilter = GraphicFilter::GetGraphicFilter();
+ static const OUStringLiteral aDefaultMimeType(u"image/png");
+ OUString aDefaultFormatName;
+ sal_uInt16 nCount = rGraphicFilter.GetExportFormatCount();
+
+ std::map< OUString, OUString > aMimeTypeMap;
+
+ for ( sal_uInt16 i = 0; i < nCount; i++ )
+ {
+ const OUString aExportFormatName( rGraphicFilter.GetExportFormatName( i ) );
+ const OUString aFilterMimeType( rGraphicFilter.GetExportFormatMediaType( i ) );
+ xFilePicker->appendFilter( aExportFormatName, rGraphicFilter.GetExportWildcard( i ) );
+ aMimeTypeMap[ aExportFormatName ] = aFilterMimeType;
+ if( aDefaultMimeType == aFilterMimeType )
+ aDefaultFormatName = aExportFormatName;
+ }
+
+ if( !aDefaultFormatName.isEmpty() )
+ xFilePicker->setCurrentFilter( aDefaultFormatName );
+
+ // execute dialog
+
+ if( aDialogHelper.Execute() == ERRCODE_NONE )
+ {
+ OUString sPath( xFilePicker->getFiles().getConstArray()[0] );
+ OUString aExportMimeType( aMimeTypeMap[xFilePicker->getCurrentFilter()] );
+
+ GraphicHelper::SaveShapeAsGraphicToPath(xComponent, xShape, aExportMimeType, sPath);
+ }
+ }
+ catch( Exception& )
+ {
+ }
+}
+
+short GraphicHelper::HasToSaveTransformedImage(weld::Widget* pWin)
+{
+ OUString aMsg(SvxResId(RID_SVXSTR_SAVE_MODIFIED_IMAGE));
+ std::unique_ptr<weld::MessageDialog> xBox(Application::CreateMessageDialog(pWin,
+ VclMessageType::Question, VclButtonsType::YesNo, aMsg));
+ return xBox->run();
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/customshapes/EnhancedCustomShape2d.cxx b/svx/source/customshapes/EnhancedCustomShape2d.cxx
new file mode 100644
index 000000000..87c081628
--- /dev/null
+++ b/svx/source/customshapes/EnhancedCustomShape2d.cxx
@@ -0,0 +1,3053 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * 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/string_view.hxx>
+#include <svx/EnhancedCustomShape2d.hxx>
+#include <svx/EnhancedCustomShapeGeometry.hxx>
+#include <svx/EnhancedCustomShapeTypeNames.hxx>
+#include <svx/svdoashp.hxx>
+#include <svx/svdtrans.hxx>
+#include <svx/svdogrp.hxx>
+#include <svx/svdopath.hxx>
+#include <svx/svdorect.hxx>
+#include <svx/svdpage.hxx>
+#include <svx/xflclit.hxx>
+#include <svx/xfillit0.hxx>
+#include <svx/xlineit0.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/xflgrit.hxx>
+#include <svx/xflhtit.hxx>
+#include <svx/xbtmpit.hxx>
+#include <svx/xgrad.hxx>
+#include <svx/xhatch.hxx>
+#include <svx/sdshitm.hxx>
+#include <com/sun/star/awt/Size.hpp>
+#include <com/sun/star/drawing/EnhancedCustomShapeParameterType.hpp>
+#include <com/sun/star/drawing/EnhancedCustomShapeSegmentCommand.hpp>
+#include <com/sun/star/drawing/EnhancedCustomShapeAdjustmentValue.hpp>
+#include <com/sun/star/drawing/EnhancedCustomShapeSegment.hpp>
+#include <com/sun/star/drawing/EnhancedCustomShapeTextFrame.hpp>
+#include <basegfx/numeric/ftools.hxx>
+#include <basegfx/color/bcolortools.hxx>
+#include <basegfx/polygon/b2dpolygon.hxx>
+#include <basegfx/polygon/b2dpolygontools.hxx>
+#include <basegfx/matrix/b2dhommatrixtools.hxx>
+#include <sal/log.hxx>
+
+#include <algorithm>
+#include <cstdlib>
+#include <string_view>
+#include <unordered_set>
+
+using namespace ::com::sun::star;
+using namespace ::com::sun::star::uno;
+using namespace ::com::sun::star::drawing;
+using namespace ::com::sun::star::drawing::EnhancedCustomShapeSegmentCommand;
+
+void EnhancedCustomShape2d::SetEnhancedCustomShapeParameter( EnhancedCustomShapeParameter& rParameter, const sal_Int32 nValue )
+{
+ sal_uInt32 nDat = static_cast<sal_uInt32>(nValue);
+ sal_Int32 nNewValue = nValue;
+
+ // check if this is a special point
+ if ( ( nDat >> 16 ) == 0x8000 )
+ {
+ nNewValue = static_cast<sal_uInt16>(nDat);
+ rParameter.Type = EnhancedCustomShapeParameterType::EQUATION;
+ }
+ else
+ rParameter.Type = EnhancedCustomShapeParameterType::NORMAL;
+ rParameter.Value <<= nNewValue;
+}
+
+OUString EnhancedCustomShape2d::GetEquation( const sal_uInt16 nFlags, sal_Int32 nP1, sal_Int32 nP2, sal_Int32 nP3 )
+{
+ OUString aEquation;
+ bool b1Special = ( nFlags & 0x2000 ) != 0;
+ bool b2Special = ( nFlags & 0x4000 ) != 0;
+ bool b3Special = ( nFlags & 0x8000 ) != 0;
+ switch( nFlags & 0xff )
+ {
+ case 0 :
+ case 14 :
+ {
+ sal_Int32 nOptimize = 0;
+ if ( nP1 )
+ nOptimize |= 1;
+ if ( nP2 )
+ nOptimize |= 2;
+ if ( b1Special )
+ nOptimize |= 4;
+ if ( b2Special )
+ nOptimize |= 8;
+ switch( nOptimize )
+ {
+ case 0 :
+ break;
+ case 1 :
+ case 4 :
+ case 5 :
+ EnhancedCustomShape2d::AppendEnhancedCustomShapeEquationParameter( aEquation, nP1, b1Special );
+ break;
+ case 2 :
+ case 8 :
+ case 10:
+ EnhancedCustomShape2d::AppendEnhancedCustomShapeEquationParameter( aEquation, nP2, b2Special );
+ break;
+ default :
+ {
+ EnhancedCustomShape2d::AppendEnhancedCustomShapeEquationParameter( aEquation, nP1, b1Special );
+ aEquation += "+";
+ EnhancedCustomShape2d::AppendEnhancedCustomShapeEquationParameter( aEquation, nP2, b2Special );
+ }
+ break;
+ }
+ if ( b3Special || nP3 )
+ {
+ aEquation += "-";
+ EnhancedCustomShape2d::AppendEnhancedCustomShapeEquationParameter( aEquation, nP3, b3Special );
+ }
+ }
+ break;
+ case 1 :
+ {
+ EnhancedCustomShape2d::AppendEnhancedCustomShapeEquationParameter( aEquation, nP1, b1Special );
+ if ( b2Special || ( nP2 != 1 ) )
+ {
+ aEquation += "*";
+ EnhancedCustomShape2d::AppendEnhancedCustomShapeEquationParameter( aEquation, nP2, b2Special );
+ }
+ if ( b3Special || ( ( nP3 != 1 ) && ( nP3 != 0 ) ) )
+ {
+ aEquation += "/";
+ EnhancedCustomShape2d::AppendEnhancedCustomShapeEquationParameter( aEquation, nP3, b3Special );
+ }
+ }
+ break;
+ case 2 :
+ {
+ aEquation += "(";
+ EnhancedCustomShape2d::AppendEnhancedCustomShapeEquationParameter( aEquation, nP1, b1Special );
+ aEquation += "+";
+ EnhancedCustomShape2d::AppendEnhancedCustomShapeEquationParameter( aEquation, nP2, b2Special );
+ aEquation += ")/2";
+ }
+ break;
+ case 3 :
+ {
+ aEquation += "abs(";
+ EnhancedCustomShape2d::AppendEnhancedCustomShapeEquationParameter( aEquation, nP1, b1Special );
+ aEquation += ")";
+ }
+ break;
+ case 4 :
+ {
+ aEquation += "min(";
+ EnhancedCustomShape2d::AppendEnhancedCustomShapeEquationParameter( aEquation, nP1, b1Special );
+ aEquation += ",";
+ EnhancedCustomShape2d::AppendEnhancedCustomShapeEquationParameter( aEquation, nP2, b2Special );
+ aEquation += ")";
+ }
+ break;
+ case 5 :
+ {
+ aEquation += "max(";
+ EnhancedCustomShape2d::AppendEnhancedCustomShapeEquationParameter( aEquation, nP1, b1Special );
+ aEquation += ",";
+ EnhancedCustomShape2d::AppendEnhancedCustomShapeEquationParameter( aEquation, nP2, b2Special );
+ aEquation += ")";
+ }
+ break;
+ case 6 :
+ {
+ aEquation += "if(";
+ EnhancedCustomShape2d::AppendEnhancedCustomShapeEquationParameter( aEquation, nP1, b1Special );
+ aEquation += ",";
+ EnhancedCustomShape2d::AppendEnhancedCustomShapeEquationParameter( aEquation, nP2, b2Special );
+ aEquation += ",";
+ EnhancedCustomShape2d::AppendEnhancedCustomShapeEquationParameter( aEquation, nP3, b3Special );
+ aEquation += ")";
+ }
+ break;
+ case 7 :
+ {
+ aEquation += "sqrt(";
+ EnhancedCustomShape2d::AppendEnhancedCustomShapeEquationParameter( aEquation, nP1, b1Special );
+ aEquation += "*";
+ EnhancedCustomShape2d::AppendEnhancedCustomShapeEquationParameter( aEquation, nP1, b1Special );
+ aEquation += "+";
+ EnhancedCustomShape2d::AppendEnhancedCustomShapeEquationParameter( aEquation, nP2, b2Special );
+ aEquation += "*";
+ EnhancedCustomShape2d::AppendEnhancedCustomShapeEquationParameter( aEquation, nP2, b2Special );
+ aEquation += "+";
+ EnhancedCustomShape2d::AppendEnhancedCustomShapeEquationParameter( aEquation, nP3, b3Special );
+ aEquation += "*";
+ EnhancedCustomShape2d::AppendEnhancedCustomShapeEquationParameter( aEquation, nP3, b3Special );
+ aEquation += ")";
+ }
+ break;
+ case 8 :
+ {
+ aEquation += "atan2(";
+ EnhancedCustomShape2d::AppendEnhancedCustomShapeEquationParameter( aEquation, nP2, b2Special );
+ aEquation += ",";
+ EnhancedCustomShape2d::AppendEnhancedCustomShapeEquationParameter( aEquation, nP1, b1Special );
+ aEquation += ")/(pi/180)";
+ }
+ break;
+ case 9 :
+ {
+ EnhancedCustomShape2d::AppendEnhancedCustomShapeEquationParameter( aEquation, nP1, b1Special );
+ aEquation += "*sin(";
+ EnhancedCustomShape2d::AppendEnhancedCustomShapeEquationParameter( aEquation, nP2, b2Special );
+ aEquation += "*(pi/180))";
+ }
+ break;
+ case 10 :
+ {
+ EnhancedCustomShape2d::AppendEnhancedCustomShapeEquationParameter( aEquation, nP1, b1Special );
+ aEquation += "*cos(";
+ EnhancedCustomShape2d::AppendEnhancedCustomShapeEquationParameter( aEquation, nP2, b2Special );
+ aEquation += "*(pi/180))";
+ }
+ break;
+ case 11 :
+ {
+ EnhancedCustomShape2d::AppendEnhancedCustomShapeEquationParameter( aEquation, nP1, b1Special );
+ aEquation += "*cos(atan2(";
+ EnhancedCustomShape2d::AppendEnhancedCustomShapeEquationParameter( aEquation, nP3, b3Special );
+ aEquation += ",";
+ EnhancedCustomShape2d::AppendEnhancedCustomShapeEquationParameter( aEquation, nP2, b2Special );
+ aEquation += "))";
+ }
+ break;
+ case 12 :
+ {
+ EnhancedCustomShape2d::AppendEnhancedCustomShapeEquationParameter( aEquation, nP1, b1Special );
+ aEquation += "*sin(atan2(";
+ EnhancedCustomShape2d::AppendEnhancedCustomShapeEquationParameter( aEquation, nP3, b3Special );
+ aEquation += ",";
+ EnhancedCustomShape2d::AppendEnhancedCustomShapeEquationParameter( aEquation, nP2, b2Special );
+ aEquation += "))";
+ }
+ break;
+ case 13 :
+ {
+ aEquation += "sqrt(";
+ EnhancedCustomShape2d::AppendEnhancedCustomShapeEquationParameter( aEquation, nP1, b1Special );
+ aEquation += ")";
+ }
+ break;
+ case 15 :
+ {
+ EnhancedCustomShape2d::AppendEnhancedCustomShapeEquationParameter( aEquation, nP3, b3Special );
+ aEquation += "*sqrt(1-(";
+ EnhancedCustomShape2d::AppendEnhancedCustomShapeEquationParameter( aEquation, nP1, b1Special );
+ aEquation += "/";
+ EnhancedCustomShape2d::AppendEnhancedCustomShapeEquationParameter( aEquation, nP2, b2Special );
+ aEquation += ")"
+ "*(";
+ EnhancedCustomShape2d::AppendEnhancedCustomShapeEquationParameter( aEquation, nP1, b1Special );
+ aEquation += "/";
+ EnhancedCustomShape2d::AppendEnhancedCustomShapeEquationParameter( aEquation, nP2, b2Special );
+ aEquation += "))";
+ }
+ break;
+ case 16 :
+ {
+ EnhancedCustomShape2d::AppendEnhancedCustomShapeEquationParameter( aEquation, nP1, b1Special );
+ aEquation += "*tan(";
+ EnhancedCustomShape2d::AppendEnhancedCustomShapeEquationParameter( aEquation, nP2, b2Special );
+ aEquation += ")";
+ }
+ break;
+ case 0x80 :
+ {
+ aEquation += "sqrt(";
+ EnhancedCustomShape2d::AppendEnhancedCustomShapeEquationParameter( aEquation, nP3, b3Special );
+ aEquation += "*";
+ EnhancedCustomShape2d::AppendEnhancedCustomShapeEquationParameter( aEquation, nP3, b3Special );
+ aEquation += "-";
+ EnhancedCustomShape2d::AppendEnhancedCustomShapeEquationParameter( aEquation, nP1, b1Special );
+ aEquation += "*";
+ EnhancedCustomShape2d::AppendEnhancedCustomShapeEquationParameter( aEquation, nP1, b1Special );
+ aEquation += ")";
+ }
+ break;
+ case 0x81 :
+ {
+ aEquation += "(cos(";
+ EnhancedCustomShape2d::AppendEnhancedCustomShapeEquationParameter( aEquation, nP3, b3Special );
+ aEquation += "*(pi/180))*(";
+ EnhancedCustomShape2d::AppendEnhancedCustomShapeEquationParameter( aEquation, nP1, b1Special );
+ aEquation += "-10800)+sin(";
+ EnhancedCustomShape2d::AppendEnhancedCustomShapeEquationParameter( aEquation, nP3, b3Special );
+ aEquation += "*(pi/180))*(";
+ EnhancedCustomShape2d::AppendEnhancedCustomShapeEquationParameter( aEquation, nP2, b2Special );
+ aEquation += "-10800))+10800";
+ }
+ break;
+ case 0x82 :
+ {
+ aEquation += "-(sin(";
+ EnhancedCustomShape2d::AppendEnhancedCustomShapeEquationParameter( aEquation, nP3, b3Special );
+ aEquation += "*(pi/180))*(";
+ EnhancedCustomShape2d::AppendEnhancedCustomShapeEquationParameter( aEquation, nP1, b1Special );
+ aEquation += "-10800)-cos(";
+ EnhancedCustomShape2d::AppendEnhancedCustomShapeEquationParameter( aEquation, nP3, b3Special );
+ aEquation += "*(pi/180))*(";
+ EnhancedCustomShape2d::AppendEnhancedCustomShapeEquationParameter( aEquation, nP2, b2Special );
+ aEquation += "-10800))+10800";
+ }
+ break;
+ }
+ return aEquation;
+}
+
+void EnhancedCustomShape2d::AppendEnhancedCustomShapeEquationParameter( OUString& rParameter, const sal_Int32 nPara, const bool bIsSpecialValue )
+{
+ if ( bIsSpecialValue )
+ {
+ if ( nPara & 0x400 )
+ {
+ rParameter += "?";
+ rParameter += OUString::number( nPara & 0xff );
+ rParameter += " ";
+ }
+ else
+ {
+ switch( nPara )
+ {
+ case DFF_Prop_adjustValue :
+ case DFF_Prop_adjust2Value :
+ case DFF_Prop_adjust3Value :
+ case DFF_Prop_adjust4Value :
+ case DFF_Prop_adjust5Value :
+ case DFF_Prop_adjust6Value :
+ case DFF_Prop_adjust7Value :
+ case DFF_Prop_adjust8Value :
+ case DFF_Prop_adjust9Value :
+ case DFF_Prop_adjust10Value :
+ {
+ rParameter += "$";
+ rParameter += OUString::number( nPara - DFF_Prop_adjustValue );
+ rParameter += " ";
+ }
+ break;
+ case DFF_Prop_geoLeft :
+ {
+ rParameter += "left";
+ }
+ break;
+ case DFF_Prop_geoTop :
+ {
+ rParameter += "top";
+ }
+ break;
+ case DFF_Prop_geoRight :
+ {
+ rParameter += "right";
+ }
+ break;
+ case DFF_Prop_geoBottom :
+ {
+ rParameter += "bottom";
+ }
+ break;
+ }
+ }
+ }
+ else
+ {
+ rParameter += OUString::number( nPara );
+ }
+}
+
+void EnhancedCustomShape2d::SetEnhancedCustomShapeHandleParameter( EnhancedCustomShapeParameter& rParameter, const sal_Int32 nPara, const bool bIsSpecialValue, bool bHorz )
+{
+ sal_Int32 nValue = 0;
+ if ( bIsSpecialValue )
+ {
+ if ( ( nPara >= 0x100 ) && ( nPara <= 0x107 ) )
+ {
+ nValue = nPara & 0xff;
+ rParameter.Type = EnhancedCustomShapeParameterType::ADJUSTMENT;
+ }
+ else if ( ( nPara >= 3 ) && ( nPara <= 0x82 ) )
+ {
+ nValue = nPara - 3;
+ rParameter.Type = EnhancedCustomShapeParameterType::EQUATION;
+ }
+ else if ( nPara == 0 )
+ {
+ nValue = 0;
+ if ( bHorz )
+ rParameter.Type = EnhancedCustomShapeParameterType::LEFT;
+ else
+ rParameter.Type = EnhancedCustomShapeParameterType::TOP;
+ }
+ else if ( nPara == 1 )
+ {
+ nValue = 0;
+ if ( bHorz )
+ rParameter.Type = EnhancedCustomShapeParameterType::RIGHT;
+ else
+ rParameter.Type = EnhancedCustomShapeParameterType::BOTTOM;
+ }
+ else if ( nPara == 2 ) // means to be centered, but should not be
+ { // used in our implementation
+ nValue = 5600;
+ rParameter.Type = EnhancedCustomShapeParameterType::NORMAL;
+ }
+ else
+ {
+ nValue = nPara;
+ rParameter.Type = EnhancedCustomShapeParameterType::NORMAL;
+ }
+ }
+ else
+ {
+ nValue = nPara;
+ rParameter.Type = EnhancedCustomShapeParameterType::NORMAL;
+ }
+ rParameter.Value <<= nValue;
+}
+
+bool EnhancedCustomShape2d::ConvertSequenceToEnhancedCustomShape2dHandle(
+ const css::beans::PropertyValues& rHandleProperties,
+ EnhancedCustomShape2d::Handle& rDestinationHandle )
+{
+ bool bRetValue = false;
+ if ( rHandleProperties.hasElements() )
+ {
+ rDestinationHandle.nFlags = HandleFlags::NONE;
+ for ( const css::beans::PropertyValue& rPropVal : rHandleProperties )
+ {
+ if ( rPropVal.Name == "Position" )
+ {
+ if ( rPropVal.Value >>= rDestinationHandle.aPosition )
+ bRetValue = true;
+ }
+ else if ( rPropVal.Name == "MirroredX" )
+ {
+ bool bMirroredX;
+ if ( rPropVal.Value >>= bMirroredX )
+ {
+ if ( bMirroredX )
+ rDestinationHandle.nFlags |= HandleFlags::MIRRORED_X;
+ }
+ }
+ else if ( rPropVal.Name == "MirroredY" )
+ {
+ bool bMirroredY;
+ if ( rPropVal.Value >>= bMirroredY )
+ {
+ if ( bMirroredY )
+ rDestinationHandle.nFlags |= HandleFlags::MIRRORED_Y;
+ }
+ }
+ else if ( rPropVal.Name == "Switched" )
+ {
+ bool bSwitched;
+ if ( rPropVal.Value >>= bSwitched )
+ {
+ if ( bSwitched )
+ rDestinationHandle.nFlags |= HandleFlags::SWITCHED;
+ }
+ }
+ else if ( rPropVal.Name == "Polar" )
+ {
+ if ( rPropVal.Value >>= rDestinationHandle.aPolar )
+ rDestinationHandle.nFlags |= HandleFlags::POLAR;
+ }
+ else if ( rPropVal.Name == "RefX" )
+ {
+ if ( rPropVal.Value >>= rDestinationHandle.nRefX )
+ rDestinationHandle.nFlags |= HandleFlags::REFX;
+ }
+ else if ( rPropVal.Name == "RefY" )
+ {
+ if ( rPropVal.Value >>= rDestinationHandle.nRefY )
+ rDestinationHandle.nFlags |= HandleFlags::REFY;
+ }
+ else if ( rPropVal.Name == "RefAngle" )
+ {
+ if ( rPropVal.Value >>= rDestinationHandle.nRefAngle )
+ rDestinationHandle.nFlags |= HandleFlags::REFANGLE;
+ }
+ else if ( rPropVal.Name == "RefR" )
+ {
+ if ( rPropVal.Value >>= rDestinationHandle.nRefR )
+ rDestinationHandle.nFlags |= HandleFlags::REFR;
+ }
+ else if ( rPropVal.Name == "RadiusRangeMinimum" )
+ {
+ if ( rPropVal.Value >>= rDestinationHandle.aRadiusRangeMinimum )
+ rDestinationHandle.nFlags |= HandleFlags::RADIUS_RANGE_MINIMUM;
+ }
+ else if ( rPropVal.Name == "RadiusRangeMaximum" )
+ {
+ if ( rPropVal.Value >>= rDestinationHandle.aRadiusRangeMaximum )
+ rDestinationHandle.nFlags |= HandleFlags::RADIUS_RANGE_MAXIMUM;
+ }
+ else if ( rPropVal.Name == "RangeXMinimum" )
+ {
+ if ( rPropVal.Value >>= rDestinationHandle.aXRangeMinimum )
+ rDestinationHandle.nFlags |= HandleFlags::RANGE_X_MINIMUM;
+ }
+ else if ( rPropVal.Name == "RangeXMaximum" )
+ {
+ if ( rPropVal.Value >>= rDestinationHandle.aXRangeMaximum )
+ rDestinationHandle.nFlags |= HandleFlags::RANGE_X_MAXIMUM;
+ }
+ else if ( rPropVal.Name == "RangeYMinimum" )
+ {
+ if ( rPropVal.Value >>= rDestinationHandle.aYRangeMinimum )
+ rDestinationHandle.nFlags |= HandleFlags::RANGE_Y_MINIMUM;
+ }
+ else if ( rPropVal.Name == "RangeYMaximum" )
+ {
+ if ( rPropVal.Value >>= rDestinationHandle.aYRangeMaximum )
+ rDestinationHandle.nFlags |= HandleFlags::RANGE_Y_MAXIMUM;
+ }
+ }
+ }
+ return bRetValue;
+}
+
+void EnhancedCustomShape2d::ApplyShapeAttributes( const SdrCustomShapeGeometryItem& rGeometryItem )
+{
+ // AdjustmentValues
+ static constexpr OUStringLiteral sAdjustmentValues( u"AdjustmentValues" );
+ const Any* pAny = const_cast<SdrCustomShapeGeometryItem&>(rGeometryItem).GetPropertyValueByName( sAdjustmentValues );
+ if ( pAny )
+ *pAny >>= seqAdjustmentValues;
+
+
+ // Coordsize
+ static constexpr OUStringLiteral sViewBox( u"ViewBox" );
+ const Any* pViewBox = const_cast<SdrCustomShapeGeometryItem&>(rGeometryItem).GetPropertyValueByName( sViewBox );
+ css::awt::Rectangle aViewBox;
+ if ( pViewBox && (*pViewBox >>= aViewBox ) )
+ {
+ nCoordLeft = aViewBox.X;
+ nCoordTop = aViewBox.Y;
+ nCoordWidthG = std::abs( aViewBox.Width );
+ nCoordHeightG = std::abs( aViewBox.Height);
+ }
+ static constexpr OUStringLiteral sPath( u"Path" );
+ static constexpr OUStringLiteral sCoordinates( u"Coordinates" );
+ static constexpr OUStringLiteral sGluePoints( u"GluePoints" );
+ static constexpr OUStringLiteral sSegments( u"Segments" );
+ static constexpr OUStringLiteral sSubViewSize( u"SubViewSize" );
+ static constexpr OUStringLiteral sStretchX( u"StretchX" );
+ static constexpr OUStringLiteral sStretchY( u"StretchY" );
+ static constexpr OUStringLiteral sTextFrames( u"TextFrames" );
+ static constexpr OUStringLiteral sEquations( u"Equations" );
+ static constexpr OUStringLiteral sHandles( u"Handles" );
+
+
+ // Path/Coordinates
+ pAny = const_cast<SdrCustomShapeGeometryItem&>(rGeometryItem).GetPropertyValueByName( sPath, sCoordinates );
+ if ( pAny )
+ *pAny >>= seqCoordinates;
+
+
+ // Path/GluePoints
+ pAny = const_cast<SdrCustomShapeGeometryItem&>(rGeometryItem).GetPropertyValueByName( sPath, sGluePoints );
+ if ( pAny )
+ *pAny >>= seqGluePoints;
+
+
+ // Path/Segments
+ pAny = const_cast<SdrCustomShapeGeometryItem&>(rGeometryItem).GetPropertyValueByName( sPath, sSegments );
+ if ( pAny )
+ *pAny >>= seqSegments;
+
+
+ // Path/SubViewSize
+ pAny = const_cast<SdrCustomShapeGeometryItem&>(rGeometryItem).GetPropertyValueByName( sPath, sSubViewSize );
+ if ( pAny )
+ *pAny >>= seqSubViewSize;
+
+
+ // Path/StretchX
+ pAny = const_cast<SdrCustomShapeGeometryItem&>(rGeometryItem).GetPropertyValueByName( sPath, sStretchX );
+ if ( pAny )
+ {
+ sal_Int32 nStretchX = 0;
+ if ( *pAny >>= nStretchX )
+ nXRef = nStretchX;
+ }
+
+
+ // Path/StretchY
+ pAny = const_cast<SdrCustomShapeGeometryItem&>(rGeometryItem).GetPropertyValueByName( sPath, sStretchY );
+ if ( pAny )
+ {
+ sal_Int32 nStretchY = 0;
+ if ( *pAny >>= nStretchY )
+ nYRef = nStretchY;
+ }
+
+
+ // Path/TextFrames
+ pAny = const_cast<SdrCustomShapeGeometryItem&>(rGeometryItem).GetPropertyValueByName( sPath, sTextFrames );
+ if ( pAny )
+ *pAny >>= seqTextFrames;
+
+
+ // Equations
+ pAny = const_cast<SdrCustomShapeGeometryItem&>(rGeometryItem).GetPropertyValueByName( sEquations );
+ if ( pAny )
+ *pAny >>= seqEquations;
+
+
+ // Handles
+ pAny = const_cast<SdrCustomShapeGeometryItem&>(rGeometryItem).GetPropertyValueByName( sHandles );
+ if ( pAny )
+ *pAny >>= seqHandles;
+}
+
+EnhancedCustomShape2d::~EnhancedCustomShape2d()
+{
+}
+
+void EnhancedCustomShape2d::SetPathSize( sal_Int32 nIndex )
+{
+ sal_Int32 nWidth = 0;
+ sal_Int32 nHeight = 0;
+
+ if ( seqSubViewSize.hasElements() && nIndex < seqSubViewSize.getLength() ) {
+ nWidth = seqSubViewSize[ nIndex ].Width;
+ nHeight = seqSubViewSize[ nIndex ].Height;
+ SAL_INFO(
+ "svx",
+ "set subpath " << nIndex << " size: " << nWidth << " x "
+ << nHeight);
+ }
+
+ if ( nWidth && nHeight ) {
+ nCoordWidth = nWidth;
+ nCoordHeight = nHeight;
+ } else {
+ nCoordWidth = nCoordWidthG;
+ nCoordHeight = nCoordHeightG;
+ }
+
+ fXScale = nCoordWidth == 0 ? 0.0 : static_cast<double>(aLogicRect.GetWidth()) / static_cast<double>(nCoordWidth);
+ fYScale = nCoordHeight == 0 ? 0.0 : static_cast<double>(aLogicRect.GetHeight()) / static_cast<double>(nCoordHeight);
+ if ( bOOXMLShape )
+ {
+ SAL_INFO(
+ "svx",
+ "ooxml shape, path width: " << nCoordWidth << " height: "
+ << nCoordHeight);
+
+ // Try to set up scale separately, if given only width or height
+ // This is possible case in OOXML when only width or height is non-zero
+ if ( nCoordWidth == 0 )
+ {
+ if ( nWidth )
+ fXScale = static_cast<double>(aLogicRect.GetWidth()) / static_cast<double>(nWidth);
+ else
+ fXScale = 1.0;
+ }
+ if ( nCoordHeight == 0 )
+ {
+ if ( nHeight )
+ fYScale = static_cast<double>(aLogicRect.GetHeight()) / static_cast<double>(nHeight);
+ else
+ fYScale = 1.0;
+ }
+ }
+ if ( static_cast<sal_uInt32>(nXRef) != 0x80000000 && aLogicRect.GetHeight() )
+ {
+ fXRatio = static_cast<double>(aLogicRect.GetWidth()) / static_cast<double>(aLogicRect.GetHeight());
+ if ( fXRatio > 1 )
+ fXScale /= fXRatio;
+ else
+ fXRatio = 1.0;
+ }
+ else
+ fXRatio = 1.0;
+ if ( static_cast<sal_uInt32>(nYRef) != 0x80000000 && aLogicRect.GetWidth() )
+ {
+ fYRatio = static_cast<double>(aLogicRect.GetHeight()) / static_cast<double>(aLogicRect.GetWidth());
+ if ( fYRatio > 1 )
+ fYScale /= fYRatio;
+ else
+ fYRatio = 1.0;
+ }
+ else
+ fYRatio = 1.0;
+}
+
+EnhancedCustomShape2d::EnhancedCustomShape2d(SdrObjCustomShape& rSdrObjCustomShape)
+: SfxItemSet ( rSdrObjCustomShape.GetMergedItemSet() ),
+ mrSdrObjCustomShape ( rSdrObjCustomShape ),
+ eSpType ( mso_sptNil ),
+ nCoordLeft ( 0 ),
+ nCoordTop ( 0 ),
+ nCoordWidthG ( 21600 ),
+ nCoordHeightG ( 21600 ),
+ bOOXMLShape ( false ),
+ nXRef ( 0x80000000 ),
+ nYRef ( 0x80000000 ),
+ nColorData ( 0 ),
+ bFilled ( rSdrObjCustomShape.GetMergedItem( XATTR_FILLSTYLE ).GetValue() != drawing::FillStyle_NONE ),
+ bStroked ( rSdrObjCustomShape.GetMergedItem( XATTR_LINESTYLE ).GetValue() != drawing::LineStyle_NONE ),
+ bFlipH ( false ),
+ bFlipV ( false )
+{
+ // bTextFlow needs to be set before clearing the TextDirection Item
+
+ ClearItem( SDRATTR_TEXTDIRECTION ); //SJ: vertical writing is not required, by removing this item no outliner is created
+
+ // #i105323# For 2D AutoShapes, the shadow attribute does not need to be applied to any
+ // of the constructed helper SdrObjects. This would lead to problems since the shadow
+ // of one helper object would fall on one helper object behind it (e.g. with the
+ // eyes of the smiley shape). This is not wanted; instead a single shadow 'behind'
+ // the AutoShape visualisation is wanted. This is done with primitive functionality
+ // now in SdrCustomShapePrimitive2D::create2DDecomposition, but only for 2D objects
+ // (see there and in EnhancedCustomShape3d::Create3DObject to read more).
+ // This exception may be removed later when AutoShapes will create primitives directly.
+ // So, currently remove the ShadowAttribute from the ItemSet to not apply it to any
+ // 2D helper shape.
+ ClearItem(SDRATTR_SHADOW);
+
+ Point aP( mrSdrObjCustomShape.GetSnapRect().Center() );
+ Size aS( mrSdrObjCustomShape.GetLogicRect().GetSize() );
+ aP.AdjustX( -(aS.Width() / 2) );
+ aP.AdjustY( -(aS.Height() / 2) );
+ aLogicRect = tools::Rectangle( aP, aS );
+
+ OUString sShapeType;
+ const SdrCustomShapeGeometryItem& rGeometryItem(mrSdrObjCustomShape.GetMergedItem( SDRATTR_CUSTOMSHAPE_GEOMETRY ));
+ static constexpr OUStringLiteral sType = u"Type";
+ const Any* pAny = rGeometryItem.GetPropertyValueByName( sType );
+ if ( pAny ) {
+ *pAny >>= sShapeType;
+ bOOXMLShape = sShapeType.startsWith("ooxml-");
+ SAL_INFO("svx", "shape type: " << sShapeType << " " << bOOXMLShape);
+ }
+ eSpType = EnhancedCustomShapeTypeNames::Get( sShapeType );
+
+ static constexpr OUStringLiteral sMirroredX = u"MirroredX";
+ static constexpr OUStringLiteral sMirroredY = u"MirroredY";
+ pAny = rGeometryItem.GetPropertyValueByName( sMirroredX );
+ if ( pAny )
+ *pAny >>= bFlipH;
+ pAny = rGeometryItem.GetPropertyValueByName( sMirroredY );
+ if ( pAny )
+ *pAny >>= bFlipV;
+
+ nRotateAngle = Degree100(static_cast<sal_Int32>(mrSdrObjCustomShape.GetObjectRotation() * 100.0));
+
+ /*const sal_Int32* pDefData =*/ ApplyShapeAttributes( rGeometryItem );
+ SetPathSize();
+
+ switch( eSpType )
+ {
+ case mso_sptCan : nColorData = 0x20400000; break;
+ case mso_sptCube : nColorData = 0x302e0000; break;
+ case mso_sptActionButtonBlank : nColorData = 0x502ce400; break;
+ case mso_sptActionButtonHome : nColorData = 0x702ce4ce; break;
+ case mso_sptActionButtonHelp : nColorData = 0x602ce4c0; break;
+ case mso_sptActionButtonInformation : nColorData = 0x702ce4c5; break;
+ case mso_sptActionButtonBackPrevious : nColorData = 0x602ce4c0; break;
+ case mso_sptActionButtonForwardNext : nColorData = 0x602ce4c0; break;
+ case mso_sptActionButtonBeginning : nColorData = 0x602ce4c0; break;
+ case mso_sptActionButtonEnd : nColorData = 0x602ce4c0; break;
+ case mso_sptActionButtonReturn : nColorData = 0x602ce4c0; break;
+ case mso_sptActionButtonDocument : nColorData = 0x702ce4ec; break;
+ case mso_sptActionButtonSound : nColorData = 0x602ce4c0; break;
+ case mso_sptActionButtonMovie : nColorData = 0x602ce4c0; break;
+ case mso_sptBevel : nColorData = 0x502ce400; break;
+ case mso_sptFoldedCorner : nColorData = 0x20e00000; break;
+ case mso_sptSmileyFace : nColorData = 0x20e00000; break;
+ case mso_sptNil :
+ {
+ // Because calculation method has changed in #i102797 original color encoding for
+ // Octagon Bevel and Diamond Bevel can no longer be used. We keep the color coding
+ // only for self-created shapes, as authors may have already considered the change.
+ // We use ColorData compatible to OOXML.
+ if (sShapeType == "col-60da8460") // Octagon Bevel
+ {
+ nColorData = 0x60ecc240;
+ }
+ else if (sShapeType == "col-502ad400") // Diamond Bevel
+ {
+ nColorData = 0x502ce400;
+ }
+ else if (sShapeType.getLength() > 4 && sShapeType.match( "col-" ))
+ {
+ nColorData = o3tl::toUInt32(sShapeType.subView( 4 ), 16);
+ }
+ }
+ break;
+ case mso_sptCurvedLeftArrow :
+ case mso_sptCurvedRightArrow :
+ case mso_sptCurvedUpArrow :
+ case mso_sptCurvedDownArrow : nColorData = 0x20d00000; break;
+ case mso_sptRibbon2 : nColorData = 0x30ee0000; break;
+ case mso_sptRibbon : nColorData = 0x30ee0000; break;
+
+ case mso_sptEllipseRibbon2 : nColorData = 0x30ee0000; break;
+ case mso_sptEllipseRibbon : nColorData = 0x30ee0000; break;
+
+ case mso_sptVerticalScroll : nColorData = 0x30ee0000; break;
+ case mso_sptHorizontalScroll : nColorData = 0x30ee0000; break;
+ default:
+ break;
+ }
+
+ sal_Int32 nLength = seqEquations.getLength();
+
+ if ( !nLength )
+ return;
+
+ vNodesSharedPtr.resize( nLength );
+ vEquationResults.resize( nLength );
+ for ( sal_Int32 i = 0; i < nLength; i++ )
+ {
+ vEquationResults[ i ].bReady = false;
+ try
+ {
+ vNodesSharedPtr[ i ] = EnhancedCustomShape::FunctionParser::parseFunction( seqEquations[ i ], *this );
+ }
+ catch ( EnhancedCustomShape::ParseError& )
+ {
+ SAL_INFO(
+ "svx",
+ "error: equation number: " << i << ", parser failed ("
+ << seqEquations[i] << ")");
+ }
+ }
+}
+
+using EnhancedCustomShape::ExpressionFunct;
+
+double EnhancedCustomShape2d::GetEnumFunc( const ExpressionFunct eFunc ) const
+{
+ double fRet = 0.0;
+ switch( eFunc )
+ {
+ case ExpressionFunct::EnumPi : fRet = M_PI; break;
+ case ExpressionFunct::EnumLeft : fRet = static_cast<double>(nCoordLeft); break;
+ case ExpressionFunct::EnumTop : fRet = static_cast<double>(nCoordTop); break;
+ case ExpressionFunct::EnumRight : fRet = (static_cast<double>(nCoordLeft) + static_cast<double>(nCoordWidth)) * fXRatio; break;
+ case ExpressionFunct::EnumBottom : fRet = (static_cast<double>(nCoordTop) + static_cast<double>(nCoordHeight)) * fYRatio; break;
+ case ExpressionFunct::EnumXStretch : fRet = nXRef; break;
+ case ExpressionFunct::EnumYStretch : fRet = nYRef; break;
+ case ExpressionFunct::EnumHasStroke : fRet = bStroked ? 1.0 : 0.0; break;
+ case ExpressionFunct::EnumHasFill : fRet = bFilled ? 1.0 : 0.0; break;
+ case ExpressionFunct::EnumWidth : fRet = nCoordWidth; break;
+ case ExpressionFunct::EnumHeight : fRet = nCoordHeight; break;
+ case ExpressionFunct::EnumLogWidth : fRet = aLogicRect.GetWidth(); break;
+ case ExpressionFunct::EnumLogHeight : fRet = aLogicRect.GetHeight(); break;
+ default: break;
+ }
+ return fRet;
+}
+double EnhancedCustomShape2d::GetAdjustValueAsDouble( const sal_Int32 nIndex ) const
+{
+ double fNumber = 0.0;
+ if ( nIndex < seqAdjustmentValues.getLength() )
+ {
+ if ( seqAdjustmentValues[ nIndex ].Value.getValueTypeClass() == TypeClass_DOUBLE )
+ seqAdjustmentValues[ nIndex ].Value >>= fNumber;
+ else
+ {
+ sal_Int32 nNumber = 0;
+ seqAdjustmentValues[ nIndex ].Value >>= nNumber;
+ fNumber = static_cast<double>(nNumber);
+ }
+ }
+ return fNumber;
+}
+double EnhancedCustomShape2d::GetEquationValueAsDouble( const sal_Int32 nIndex ) const
+{
+ double fNumber = 0.0;
+ static sal_uInt32 nLevel = 0;
+ if ( nIndex < static_cast<sal_Int32>(vNodesSharedPtr.size()) )
+ {
+ if ( vNodesSharedPtr[ nIndex ] ) {
+ nLevel ++;
+ try
+ {
+ if ( vEquationResults[ nIndex ].bReady )
+ fNumber = vEquationResults[ nIndex ].fValue;
+ else {
+ // cast to non const, so that we can optimize by caching
+ // equation results, without changing all the const in the stack
+ struct EquationResult &aResult = const_cast<EnhancedCustomShape2d*>(this)->vEquationResults[ nIndex ];
+
+ fNumber = aResult.fValue = (*vNodesSharedPtr[ nIndex ])();
+ aResult.bReady = true;
+
+ SAL_INFO("svx", "equation " << nLevel << " (level: " << seqEquations[nIndex] << "): "
+ << fNumber << " --> " << 180.0*fNumber/10800000.0);
+ }
+ if ( !std::isfinite( fNumber ) )
+ fNumber = 0.0;
+ }
+ catch ( ... )
+ {
+ SAL_WARN("svx", "EnhancedCustomShape2d::GetEquationValueAsDouble failed");
+ }
+ nLevel --;
+ }
+ SAL_INFO(
+ "svx",
+ "?" << nIndex << " --> " << fNumber << " (angle: "
+ << 180.0*fNumber/10800000.0 << ")");
+ }
+
+ return fNumber;
+}
+
+bool EnhancedCustomShape2d::SetAdjustValueAsDouble( const double& rValue, const sal_Int32 nIndex )
+{
+ bool bRetValue = false;
+ if ( nIndex < seqAdjustmentValues.getLength() )
+ {
+ // updating our local adjustment sequence
+ auto pseqAdjustmentValues = seqAdjustmentValues.getArray();
+ pseqAdjustmentValues[ nIndex ].Value <<= rValue;
+ pseqAdjustmentValues[ nIndex ].State = css::beans::PropertyState_DIRECT_VALUE;
+ bRetValue = true;
+ }
+ return bRetValue;
+}
+
+basegfx::B2DPoint EnhancedCustomShape2d::GetPointAsB2DPoint( const css::drawing::EnhancedCustomShapeParameterPair& rPair,
+ const bool bScale, const bool bReplaceGeoSize ) const
+{
+ double fValX, fValY;
+ // width
+ GetParameter(fValX, rPair.First, bReplaceGeoSize, false);
+ fValX -= nCoordLeft;
+ if (bScale)
+ {
+ fValX *= fXScale;
+ }
+ // height
+ GetParameter(fValY, rPair.Second, false, bReplaceGeoSize);
+ fValY -= nCoordTop;
+ if (bScale)
+ {
+ fValY *= fYScale;
+ }
+ return basegfx::B2DPoint(fValX,fValY);
+}
+
+Point EnhancedCustomShape2d::GetPoint( const css::drawing::EnhancedCustomShapeParameterPair& rPair,
+ const bool bScale, const bool bReplaceGeoSize ) const
+{
+ basegfx::B2DPoint aPoint(GetPointAsB2DPoint(rPair, bScale, bReplaceGeoSize));
+ return Point(static_cast<tools::Long>(aPoint.getX()), static_cast<tools::Long>(aPoint.getY()));
+}
+
+void EnhancedCustomShape2d::GetParameter( double& rRetValue, const EnhancedCustomShapeParameter& rParameter,
+ const bool bReplaceGeoWidth, const bool bReplaceGeoHeight ) const
+{
+ rRetValue = 0.0;
+ switch ( rParameter.Type )
+ {
+ case EnhancedCustomShapeParameterType::ADJUSTMENT :
+ {
+ sal_Int32 nAdjustmentIndex = 0;
+ if ( rParameter.Value >>= nAdjustmentIndex )
+ {
+ rRetValue = GetAdjustValueAsDouble( nAdjustmentIndex );
+ }
+ }
+ break;
+ case EnhancedCustomShapeParameterType::EQUATION :
+ {
+ sal_Int32 nEquationIndex = 0;
+ if ( rParameter.Value >>= nEquationIndex )
+ {
+ rRetValue = GetEquationValueAsDouble( nEquationIndex );
+ }
+ }
+ break;
+ case EnhancedCustomShapeParameterType::NORMAL :
+ {
+ if ( rParameter.Value.getValueTypeClass() == TypeClass_DOUBLE )
+ {
+ double fValue(0.0);
+ if ( rParameter.Value >>= fValue )
+ {
+ rRetValue = fValue;
+ }
+ }
+ else
+ {
+ sal_Int32 nValue = 0;
+ if ( rParameter.Value >>= nValue )
+ {
+ rRetValue = nValue;
+ if ( bReplaceGeoWidth && ( nValue == nCoordWidth ) )
+ rRetValue *= fXRatio;
+ else if ( bReplaceGeoHeight && ( nValue == nCoordHeight ) )
+ rRetValue *= fYRatio;
+ }
+ }
+ }
+ break;
+ case EnhancedCustomShapeParameterType::LEFT :
+ {
+ rRetValue = 0.0;
+ }
+ break;
+ case EnhancedCustomShapeParameterType::TOP :
+ {
+ rRetValue = 0.0;
+ }
+ break;
+ case EnhancedCustomShapeParameterType::RIGHT :
+ {
+ rRetValue = nCoordWidth;
+ }
+ break;
+ case EnhancedCustomShapeParameterType::BOTTOM :
+ {
+ rRetValue = nCoordHeight;
+ }
+ break;
+ }
+}
+
+// nLumDat 28-31 = number of luminance entries in nLumDat
+// nLumDat 27-24 = nLumDatEntry 0
+// nLumDat 23-20 = nLumDatEntry 1 ...
+// each 4bit entry is to be interpreted as a 10 percent signed luminance changing
+sal_Int32 EnhancedCustomShape2d::GetLuminanceChange( sal_uInt32 nIndex ) const
+{
+ const sal_uInt32 nCount = nColorData >> 28;
+ if ( !nCount )
+ return 0;
+
+ if ( nIndex >= nCount )
+ nIndex = nCount - 1;
+
+ const sal_Int32 nLumDat = nColorData << ( ( 1 + nIndex ) << 2 );
+ return ( nLumDat >> 28 ) * 10;
+}
+
+Color EnhancedCustomShape2d::GetColorData( const Color& rFillColor, sal_uInt32 nIndex, double dBrightness ) const
+{
+ if ( bOOXMLShape || ( mso_sptMin == eSpType /* ODF "non-primitive" */ ) )
+ { //do LibreOffice way, using dBrightness
+ if ( dBrightness == 0.0)
+ {
+ return rFillColor;
+ }
+ else
+ {
+ if (dBrightness >=0.0)
+ { //lighten, blending with white
+ return Color( static_cast<sal_uInt8>(static_cast< sal_Int32 >( std::clamp(rFillColor.GetRed() * (1.0-dBrightness) + dBrightness * 255.0, 0.0, 255.0) )),
+ static_cast<sal_uInt8>(static_cast< sal_Int32 >( std::clamp(rFillColor.GetGreen() * (1.0-dBrightness) + dBrightness * 255.0, 0.0, 255.0) )),
+ static_cast<sal_uInt8>(static_cast< sal_Int32 >( std::clamp(rFillColor.GetBlue() * (1.0-dBrightness) + dBrightness * 255.0, 0.0, 255.0) )) );
+ }
+ else
+ { //darken (indicated by negative sign), blending with black
+ return Color( static_cast<sal_uInt8>(static_cast< sal_Int32 >( std::clamp(rFillColor.GetRed() * (1.0+dBrightness), 0.0, 255.0) )),
+ static_cast<sal_uInt8>(static_cast< sal_Int32 >( std::clamp(rFillColor.GetGreen() * (1.0+dBrightness), 0.0, 255.0) )),
+ static_cast<sal_uInt8>(static_cast< sal_Int32 >( std::clamp(rFillColor.GetBlue() * (1.0+dBrightness), 0.0, 255.0) )) );
+ }
+ }
+ }
+ else
+ { //do OpenOffice way, using nColorData
+ const sal_Int32 nLuminance = GetLuminanceChange(nIndex);
+ if( !nLuminance )
+ return rFillColor;
+
+ basegfx::BColor aHSVColor=
+ basegfx::utils::rgb2hsv(
+ basegfx::BColor(rFillColor.GetRed()/255.0,
+ rFillColor.GetGreen()/255.0,
+ rFillColor.GetBlue()/255.0));
+
+ if( nLuminance > 0 )
+ {
+ aHSVColor.setGreen(
+ aHSVColor.getGreen() * (1.0-nLuminance/100.0));
+ aHSVColor.setBlue(
+ nLuminance/100.0 +
+ (1.0-nLuminance/100.0)*aHSVColor.getBlue());
+ }
+ else if( nLuminance < 0 )
+ {
+ aHSVColor.setBlue(
+ (1.0+nLuminance/100.0)*aHSVColor.getBlue());
+ }
+
+ aHSVColor = basegfx::utils::hsv2rgb(aHSVColor);
+ return Color( static_cast<sal_uInt8>(static_cast< sal_Int32 >( std::clamp(aHSVColor.getRed(),0.0,1.0) * 255.0 + 0.5 )),
+ static_cast<sal_uInt8>(static_cast< sal_Int32 >( std::clamp(aHSVColor.getGreen(),0.0,1.0) * 255.0 + 0.5 )),
+ static_cast<sal_uInt8>(static_cast< sal_Int32 >( std::clamp(aHSVColor.getBlue(),0.0,1.0) * 255.0 + 0.5 )) );
+ }
+}
+
+tools::Rectangle EnhancedCustomShape2d::GetTextRect() const
+{
+ if ( !seqTextFrames.hasElements() )
+ return aLogicRect;
+ sal_Int32 nIndex = 0;
+ Point aTopLeft( GetPoint( seqTextFrames[ nIndex ].TopLeft, !bOOXMLShape, true ) );
+ Point aBottomRight( GetPoint( seqTextFrames[ nIndex ].BottomRight, !bOOXMLShape, true ) );
+ tools::Rectangle aRect( aTopLeft, aBottomRight );
+ if ( bFlipH )
+ {
+ aRect.SetLeft(aLogicRect.GetWidth() - 1 - aBottomRight.X());
+ aRect.SetRight( aLogicRect.GetWidth() - 1 - aTopLeft.X());
+ }
+ if ( bFlipV )
+ {
+ aRect.SetTop(aLogicRect.GetHeight() - 1 - aBottomRight.Y());
+ aRect.SetBottom(aLogicRect.GetHeight() - 1 - aTopLeft.Y());
+ }
+ SAL_INFO("svx", aRect.GetWidth() << " x " << aRect.GetHeight());
+ if( aRect.GetWidth() <= 1 || aRect.GetHeight() <= 1 )
+ return aLogicRect;
+ aRect.Move( aLogicRect.Left(), aLogicRect.Top() );
+ aRect.Justify();
+ return aRect;
+}
+
+sal_uInt32 EnhancedCustomShape2d::GetHdlCount() const
+{
+ return seqHandles.getLength();
+}
+
+bool EnhancedCustomShape2d::GetHandlePosition( const sal_uInt32 nIndex, Point& rReturnPosition ) const
+{
+ bool bRetValue = false;
+ if ( nIndex < GetHdlCount() )
+ {
+ Handle aHandle;
+ if ( ConvertSequenceToEnhancedCustomShape2dHandle( seqHandles[ nIndex ], aHandle ) )
+ {
+ if ( aHandle.nFlags & HandleFlags::POLAR )
+ {
+ Point aReferencePoint( GetPoint( aHandle.aPolar ) );
+
+ double fAngle;
+ double fRadius;
+ GetParameter( fRadius, aHandle.aPosition.First, false, false );
+ GetParameter( fAngle, aHandle.aPosition.Second, false, false );
+
+ double a = basegfx::deg2rad(360.0 - fAngle);
+ double dx = fRadius * fXScale;
+ double fX = dx * cos( a );
+ double fY =-dx * sin( a );
+ rReturnPosition =
+ Point(
+ FRound( fX + aReferencePoint.X() ),
+ basegfx::fTools::equalZero(fXScale) ? aReferencePoint.Y() :
+ FRound( ( fY * fYScale ) / fXScale + aReferencePoint.Y() ) );
+ }
+ else
+ {
+ if ( aHandle.nFlags & HandleFlags::SWITCHED )
+ {
+ if ( aLogicRect.GetHeight() > aLogicRect.GetWidth() )
+ {
+ css::drawing::EnhancedCustomShapeParameter aFirst = aHandle.aPosition.First;
+ css::drawing::EnhancedCustomShapeParameter aSecond = aHandle.aPosition.Second;
+ aHandle.aPosition.First = aSecond;
+ aHandle.aPosition.Second = aFirst;
+ }
+ }
+ if (bOOXMLShape)
+ rReturnPosition = GetPoint(aHandle.aPosition, false /*bScale*/);
+ else
+ rReturnPosition = GetPoint(aHandle.aPosition, true /*bScale*/);
+ }
+ const GeoStat aGeoStat(mrSdrObjCustomShape.GetGeoStat());
+ if ( aGeoStat.nShearAngle )
+ {
+ double nTan = aGeoStat.mfTanShearAngle;
+ if (bFlipV != bFlipH)
+ nTan = -nTan;
+ ShearPoint( rReturnPosition, Point( aLogicRect.GetWidth() / 2, aLogicRect.GetHeight() / 2 ), nTan );
+ }
+ if ( nRotateAngle )
+ {
+ double a = toRadians(nRotateAngle);
+ RotatePoint( rReturnPosition, Point( aLogicRect.GetWidth() / 2, aLogicRect.GetHeight() / 2 ), sin( a ), cos( a ) );
+ }
+ if ( bFlipH )
+ rReturnPosition.setX( aLogicRect.GetWidth() - rReturnPosition.X() );
+ if ( bFlipV )
+ rReturnPosition.setY( aLogicRect.GetHeight() - rReturnPosition.Y() );
+ rReturnPosition.Move( aLogicRect.Left(), aLogicRect.Top() );
+ bRetValue = true;
+ }
+ }
+ return bRetValue;
+}
+
+static double lcl_getXAdjustmentValue(std::u16string_view rShapeType, const sal_uInt32 nHandleIndex,
+ const double fX, const double fW, const double fH)
+{
+ // degenerated shapes are not worth to calculate special case for each shape type
+ if (fW <= 0.0 || fH <= 0.0)
+ return 50000;
+
+ // pattern (w - x) / ss * 100000 or (r - x) / ss * 100000
+ if ((rShapeType == u"ooxml-bentArrow" && nHandleIndex == 2) || (rShapeType == u"ooxml-chevron")
+ || (rShapeType == u"ooxml-curvedRightArrow") || (rShapeType == u"ooxml-foldedCorner")
+ || (rShapeType == u"ooxml-homePlate") || (rShapeType == u"ooxml-notchedRightArrow")
+ || (rShapeType == u"ooxml-nonIsoscelesTrapezoid" && nHandleIndex == 1)
+ || (rShapeType == u"ooxml-rightArrow")
+ || (rShapeType == u"ooxml-rightArrowCallout" && nHandleIndex == 2)
+ || (rShapeType == u"ooxml-round1Rect")
+ || (rShapeType == u"ooxml-round2DiagRect" && nHandleIndex == 1)
+ || (rShapeType == u"ooxml-round2SameRect" && nHandleIndex == 0)
+ || (rShapeType == u"ooxml-snip1Rect")
+ || (rShapeType == u"ooxml-snip2DiagRect" && nHandleIndex == 1)
+ || (rShapeType == u"ooxml-snip2SameRect" && nHandleIndex == 0)
+ || (rShapeType == u"ooxml-snipRoundRect" && nHandleIndex == 1)
+ || (rShapeType == u"ooxml-swooshArrow") || (rShapeType == u"ooxml-stripedRightArrow"))
+ return (fW - fX) / std::min(fW, fH) * 100000.0;
+
+ // pattern x / ss * 100000 or (x - l) / ss * 100000
+ if ((rShapeType == u"ooxml-bentArrow" && nHandleIndex == 0)
+ || (rShapeType == u"ooxml-bentArrow" && nHandleIndex == 3)
+ || (rShapeType == u"ooxml-corner")
+ || (rShapeType == u"ooxml-curvedDownArrow") || (rShapeType == u"ooxml-curvedLeftArrow")
+ || (rShapeType == u"ooxml-curvedUpArrow") || (rShapeType == u"ooxml-leftArrow")
+ || (rShapeType == u"ooxml-leftArrowCallout" && nHandleIndex == 2)
+ || (rShapeType == u"ooxml-leftRightArrow")
+ || (rShapeType == u"ooxml-leftRightArrowCallout" && nHandleIndex == 2)
+ || (rShapeType == u"ooxml-leftRightRibbon")
+ || (rShapeType == u"ooxml-nonIsoscelesTrapezoid" && nHandleIndex == 0)
+ || (rShapeType == u"ooxml-parallelogram")
+ || (rShapeType == u"ooxml-round2DiagRect" && nHandleIndex == 0)
+ || (rShapeType == u"ooxml-round2SameRect" && nHandleIndex == 1)
+ || (rShapeType == u"ooxml-roundRect")
+ || (rShapeType == u"ooxml-snip2DiagRect" && nHandleIndex == 0)
+ || (rShapeType == u"ooxml-snip2SameRect" && nHandleIndex == 1)
+ || (rShapeType == u"ooxml-snipRoundRect" && nHandleIndex == 0)
+ || (rShapeType == u"ooxml-uturnArrow" && nHandleIndex == 0)
+ || (rShapeType == u"ooxml-uturnArrow" && nHandleIndex == 3))
+ return fX / std::min(fW, fH) * 100000.0;
+
+ // pattern (hc - x) / ss * 200000
+ if ((rShapeType == u"ooxml-downArrowCallout" && nHandleIndex == 0)
+ || (rShapeType == u"ooxml-leftRightUpArrow" && nHandleIndex == 0)
+ || (rShapeType == u"ooxml-quadArrow" && nHandleIndex == 0)
+ || (rShapeType == u"ooxml-quadArrowCallout" && nHandleIndex == 0)
+ || (rShapeType == u"ooxml-upArrowCallout" && nHandleIndex == 0)
+ || (rShapeType == u"ooxml-upDownArrowCallout" && nHandleIndex == 0))
+ return (fW / 2.0 - fX) / std::min(fW, fH) * 200000.0;
+
+ // pattern (hc - x) / ss * 100000
+ if ((rShapeType == u"ooxml-downArrowCallout" && nHandleIndex == 1)
+ || (rShapeType == u"ooxml-leftRightUpArrow" && nHandleIndex == 1)
+ || (rShapeType == u"ooxml-quadArrow" && nHandleIndex == 1)
+ || (rShapeType == u"ooxml-quadArrowCallout" && nHandleIndex == 1)
+ || (rShapeType == u"ooxml-upArrowCallout" && nHandleIndex == 1)
+ || (rShapeType == u"ooxml-upDownArrowCallout" && nHandleIndex == 1))
+ return (fW / 2.0 - fX) / std::min(fW, fH) * 100000.0;
+
+ // pattern (w - x) / ss * 50000 or (r - x) / ss * 50000
+ if ((rShapeType == u"ooxml-bentUpArrow") || (rShapeType == u"ooxml-leftUpArrow")
+ || (rShapeType == u"ooxml-uturnArrow" && nHandleIndex == 1))
+ return (fW - fX) / std::min(fW, fH) * 50000.0;
+
+ // pattern x / ss * 200000
+ if (rShapeType == u"ooxml-nonIsoscelesTrapezoid" && nHandleIndex == 0)
+ return fX / std::min(fW, fH) * 200000.0;
+
+ // pattern (hc - x) / w * 200000
+ if ((rShapeType == u"ooxml-downArrow" && nHandleIndex == 0)
+ || (rShapeType == u"ooxml-ellipseRibbon") || (rShapeType == u"ooxml-ellipseRibbon2")
+ || (rShapeType == u"ooxml-leftRightArrowCallout" && nHandleIndex == 3)
+ || (rShapeType == u"ooxml-ribbon") || (rShapeType == u"ooxml-ribbon2")
+ || (rShapeType == u"ooxml-upArrow" && nHandleIndex == 0)
+ || (rShapeType == u"ooxml-upDownArrow" && nHandleIndex == 0))
+ return (fW / 2.0 - fX) / fW * 200000.0;
+
+ // pattern (x - hc) / w * 100000
+ if ((rShapeType == u"ooxml-cloudCallout") || (rShapeType == u"ooxml-doubleWave")
+ || (rShapeType == u"ooxml-wave") || (rShapeType == u"ooxml-wedgeEllipseCallout")
+ || (rShapeType == u"ooxml-wedgeRectCallout")
+ || (rShapeType == u"ooxml-wedgeRoundRectCallout"))
+ return (fX - fW / 2.0) / fW * 100000.0;
+
+ // pattern (x - hc) / w * 200000
+ if (rShapeType == u"ooxml-teardrop")
+ return (fX - fW / 2.0) / fW * 200000.0;
+
+ // pattern (w - x) / w * 100000 or (r - x) / w * 100000
+ if (rShapeType == u"ooxml-leftArrowCallout" && nHandleIndex == 3)
+ return (fW - fX) / fW * 100000.0;
+
+ // pattern (hc - x) / h * 100000
+ if (rShapeType == u"ooxml-mathDivide")
+ return (fW / 2.0 - fX) / fH * 100000.0;
+
+ // pattern x / w * 100000, simple scaling
+ if (o3tl::starts_with(rShapeType, u"ooxml-"))
+ return fX / fW * 100000.0;
+
+ return fX; // method is unknown
+}
+
+static double lcl_getYAdjustmentValue(std::u16string_view rShapeType, const sal_uInt32 nHandleIndex,
+ const double fY, const double fW, const double fH)
+{
+ // degenerated shapes are not worth to calculate a special case for each shape type
+ if (fW <= 0.0 || fH <= 0.0)
+ return 50000;
+
+ // pattern (vc - y) / ss * 100000
+ if ((rShapeType == u"ooxml-leftArrowCallout" && nHandleIndex == 1)
+ || (rShapeType == u"ooxml-leftRightArrowCallout" && nHandleIndex == 1)
+ || (rShapeType == u"ooxml-rightArrowCallout" && nHandleIndex == 1))
+ return (fH / 2.0 - fY) / std::min(fW, fH) * 100000.0;
+
+ // pattern (vc - y) / ss * 200000
+ if ((rShapeType == u"ooxml-curvedLeftArrow") || (rShapeType == u"ooxml-curvedRightArrow")
+ || (rShapeType == u"ooxml-leftArrowCallout" && nHandleIndex == 0)
+ || (rShapeType == u"ooxml-leftRightArrowCallout" && nHandleIndex == 0)
+ || (rShapeType == u"ooxml-mathPlus")
+ || (rShapeType == u"ooxml-rightArrowCallout" && nHandleIndex == 0))
+ return (fH / 2.0 - fY) / std::min(fW, fH) * 200000.0;
+
+ // pattern (h - y) / ss * 100000 or (b - y) / ss * 100000
+ if ((rShapeType == u"ooxml-bentUpArrow" && nHandleIndex == 0) || (rShapeType == u"ooxml-corner")
+ || (rShapeType == u"ooxml-curvedDownArrow") || (rShapeType == u"ooxml-downArrow")
+ || (rShapeType == u"ooxml-downArrowCallout" && nHandleIndex == 2)
+ || (rShapeType == u"ooxml-uturnArrow" && nHandleIndex == 2))
+ return (fH - fY) / std::min(fW, fH) * 100000.0;
+
+ // pattern (h - y) / ss * 200000 or (b - y) / ss * 200000
+ if (rShapeType == u"ooxml-leftUpArrow" && nHandleIndex == 0) // - adj2 * 2 outside
+ return (fH - fY) / std::min(fW, fH) * 200000.0;
+
+ // pattern y / ss * 100000 or (y - t) / ss * 100000
+ if ((rShapeType == u"ooxml-bentUpArrow" && nHandleIndex == 2)
+ || (rShapeType == u"ooxml-bracePair") || (rShapeType == u"ooxml-bracketPair")
+ || (rShapeType == u"ooxml-can") || (rShapeType == u"ooxml-cube")
+ || (rShapeType == u"ooxml-curvedUpArrow") || (rShapeType == u"ooxml-halfFrame")
+ || (rShapeType == u"ooxml-leftBrace" && nHandleIndex == 0)
+ || (rShapeType == u"ooxml-leftBracket") || (rShapeType == u"ooxml-leftRightUpArrow")
+ || (rShapeType == u"ooxml-leftUpArrow" && nHandleIndex == 2)
+ || (rShapeType == u"ooxml-mathMultiply") || (rShapeType == u"ooxml-quadArrow")
+ || (rShapeType == u"ooxml-quadArrowCallout" && nHandleIndex == 2)
+ || (rShapeType == u"ooxml-rightBrace" && nHandleIndex == 0)
+ || (rShapeType == u"ooxml-rightBracket") || (rShapeType == u"ooxml-upArrow")
+ || (rShapeType == u"ooxml-upArrowCallout" && nHandleIndex == 2)
+ || (rShapeType == u"ooxml-upDownArrow")
+ || (rShapeType == u"ooxml-upDownArrowCallout" && nHandleIndex == 2)
+ || (rShapeType == u"ooxml-verticalScroll"))
+ return fY / std::min(fW, fH) * 100000.0;
+
+ // pattern y / ss * 50000
+ if (rShapeType == u"ooxml-bentArrow")
+ return fY / std::min(fW, fH) * 50000.0;
+
+ // pattern (vc - y) / h * 100000
+ if ((rShapeType == u"ooxml-mathDivide" && nHandleIndex == 1) // -adj1 / 2 - adj3 outside
+ || (rShapeType == u"ooxml-mathEqual" && nHandleIndex == 0) // -adj2 / 2 outside
+ || (rShapeType == u"ooxml-mathNotEqual" && nHandleIndex == 0) // -adj3 / 2 outside
+ || (rShapeType == u"ooxml-star4") || (rShapeType == u"ooxml-star6")
+ || (rShapeType == u"ooxml-star8") || (rShapeType == u"ooxml-star10")
+ || (rShapeType == u"ooxml-star12") || (rShapeType == u"ooxml-star16")
+ || (rShapeType == u"ooxml-star24") || (rShapeType == u"ooxml-star32"))
+ return (fH / 2.0 - fY) / fH * 100000.0;
+
+ // pattern (vc - y) / h * 200000
+ if ((rShapeType == u"ooxml-leftArrow") || (rShapeType == u"ooxml-leftRightArrow")
+ || (rShapeType == u"ooxml-mathDivide" && nHandleIndex == 0)
+ || (rShapeType == u"ooxml-mathEqual" && nHandleIndex == 1)
+ || (rShapeType == u"ooxml-mathMinus") || (rShapeType == u"ooxml-notchedRightArrow")
+ || (rShapeType == u"ooxml-mathNotEqual" && nHandleIndex == 2)
+ || (rShapeType == u"ooxml-quadArrowCallout" && nHandleIndex == 3)
+ || (rShapeType == u"ooxml-rightArrow") || (rShapeType == u"ooxml-stripedRightArrow")
+ || (rShapeType == u"ooxml-upDownArrowCallout" && nHandleIndex == 3))
+ return (fH / 2.0 - fY) / fH * 200000.0;
+
+ // pattern (y - vc) / h * 100000
+ if ((rShapeType == u"ooxml-cloudCallout") || (rShapeType == u"ooxml-wedgeEllipseCallout")
+ || (rShapeType == u"ooxml-wedgeRectCallout")
+ || (rShapeType == u"ooxml-wedgeRoundRectCallout"))
+ return (fY - fH / 2.0) / fH * 100000.0;
+
+ // pattern (h - y) / h * 100000 or (b - y) / h * 100000
+ if ((rShapeType == u"ooxml-ellipseRibbon" && nHandleIndex == 2)
+ || (rShapeType == u"ooxml-ellipseRibbon2" && nHandleIndex == 0)
+ || (rShapeType == u"ooxml-ribbon2")
+ || (rShapeType == u"ooxml-upArrowCallout" && nHandleIndex == 3))
+ return (fH - fY) / fH * 100000.0;
+
+ // special pattern smiley
+ if (rShapeType == u"ooxml-smileyFace")
+ return (fY - fH * 16515.0 / 21600.0) / fH * 100000.0;
+
+ // special pattern for star with odd number of tips, because center of star not center of shape
+ if (rShapeType == u"ooxml-star5")
+ return (fH / 2.0 - fY * 100000.0 / 110557.0) / fH * 100000.0;
+ if (rShapeType == u"ooxml-star7")
+ return (fH / 2.0 - fY * 100000.0 / 105210.0) / fH * 100000.0;
+
+ // special pattern swooshArrow
+ if (rShapeType == u"ooxml-swooshArrow")
+ return (fY - std::min(fW, fH) / 8.0) / fH * 100000.0;
+
+ // special pattern leftRightRibbon
+ if (rShapeType == u"ooxml-leftRightRibbon")
+ return fY / fH * 200000 - 100000;
+
+ // pattern y / h * 100000, simple scaling
+ if (o3tl::starts_with(rShapeType, u"ooxml-"))
+ return fY / fH * 100000.0;
+
+ return fY; // method is unknown
+}
+
+static double lcl_getAngleInOOXMLUnit(double fDY, double fDX)
+{
+ if (fDX != 0.0 || fDY != 0.0)
+ {
+ double fAngleRad(atan2(fDY, fDX));
+ double fAngle = basegfx::rad2deg(fAngleRad);
+ // atan2 returns angle in ]-pi; pi], OOXML preset shapes use [0;360[.
+ if (fAngle < 0.0)
+ fAngle += 360.0;
+ // OOXML uses angle unit 1/60000 degree.
+ fAngle *= 60000.0;
+ return fAngle;
+ }
+ return 0.0; // no angle defined for origin in polar coordinate system
+}
+
+static double lcl_getRadiusDistance(double fWR, double fHR, double fX, double fY)
+{
+ // Get D so, that point (fX|fY) is on the ellipse, that has width fWR-D and
+ // height fHR-D and center in origin.
+ // Get solution of ellipse equation (fX/(fWR-D))^2 + (fY/(fHR-D)^2 = 1 by solving
+ // fX^2*(fHR-D)^2 + fY^2*(fWR-D)^2 - (fWR-D)^2 * (fHR-D)^2 = 0 with Newton-method.
+ if (fX == 0.0)
+ return std::min(fHR - fY, fWR);
+ else if (fY == 0.0)
+ return std::min(fWR - fX, fHR);
+
+ double fD = std::min(fWR, fHR) - std::hypot(fX, fY); // iteration start value
+ sal_uInt8 nIter(0);
+ bool bFound(false);
+ do
+ {
+ ++nIter;
+ const double fOldD(fD);
+ const double fWRmD(fWR - fD);
+ const double fHRmD(fHR - fD);
+ double fNumerator
+ = fX * fX * fHRmD * fHRmD + fY * fY * fWRmD * fWRmD - fWRmD * fWRmD * fHRmD * fHRmD;
+ double fDenominator
+ = 2.0 * (fHRmD * (fWRmD * fWRmD - fX * fX) + fWRmD * (fHRmD * fHRmD - fY * fY));
+ if (fDenominator != 0.0)
+ {
+ fD = fD - fNumerator / fDenominator;
+ bFound = fabs(fOldD - fD) < 1.0E-12;
+ }
+ else
+ fD = fD * 0.9; // new start value
+ } while (nIter < 50 && !bFound);
+ return fD;
+}
+
+bool EnhancedCustomShape2d::SetHandleControllerPosition( const sal_uInt32 nIndex, const css::awt::Point& rPosition )
+{
+ // The method name is misleading. Essentially it calculates the adjustment values from a given
+ // handle position.
+
+ // For ooxml-foo shapes, the way to calculate the adjustment value from the handle position depends on
+ // the type of the shape, therefore need 'Type'.
+ OUString sShapeType("non-primitive"); // default for ODF
+ const SdrCustomShapeGeometryItem& rGeometryItem(mrSdrObjCustomShape.GetMergedItem( SDRATTR_CUSTOMSHAPE_GEOMETRY ));
+ const Any* pAny = rGeometryItem.GetPropertyValueByName("Type");
+ if (pAny)
+ *pAny >>= sShapeType;
+
+ bool bRetValue = false;
+ if ( nIndex < GetHdlCount() )
+ {
+ Handle aHandle;
+ if ( ConvertSequenceToEnhancedCustomShape2dHandle( seqHandles[ nIndex ], aHandle ) )
+ {
+ Point aP( rPosition.X, rPosition.Y );
+ // apply the negative object rotation to the controller position
+
+ aP.Move( -aLogicRect.Left(), -aLogicRect.Top() );
+ if ( bFlipH )
+ aP.setX( aLogicRect.GetWidth() - aP.X() );
+ if ( bFlipV )
+ aP.setY( aLogicRect.GetHeight() - aP.Y() );
+ if ( nRotateAngle )
+ {
+ double a = -toRadians(nRotateAngle);
+ RotatePoint( aP, Point( aLogicRect.GetWidth() / 2, aLogicRect.GetHeight() / 2 ), sin( a ), cos( a ) );
+ }
+ const GeoStat aGeoStat(mrSdrObjCustomShape.GetGeoStat());
+ if ( aGeoStat.nShearAngle )
+ {
+ double nTan = -aGeoStat.mfTanShearAngle;
+ if (bFlipV != bFlipH)
+ nTan = -nTan;
+ ShearPoint( aP, Point( aLogicRect.GetWidth() / 2, aLogicRect.GetHeight() / 2 ), nTan );
+ }
+
+ double fPos1 = aP.X(); //( bFlipH ) ? aLogicRect.GetWidth() - aP.X() : aP.X();
+ double fPos2 = aP.Y(); //( bFlipV ) ? aLogicRect.GetHeight() -aP.Y() : aP.Y();
+ fPos1 = !basegfx::fTools::equalZero(fXScale) ? (fPos1 / fXScale) : SAL_MAX_INT32;
+ fPos2 = !basegfx::fTools::equalZero(fYScale) ? (fPos2 / fYScale) : SAL_MAX_INT32;
+ // revert -nCoordLeft and -nCoordTop aus GetPoint()
+ fPos1 += nCoordLeft;
+ fPos2 += nCoordTop;
+
+ // Used for scaling the adjustment values based on handle positions
+ double fWidth;
+ double fHeight;
+
+ if ( nCoordWidth || nCoordHeight )
+ {
+ fWidth = nCoordWidth;
+ fHeight = nCoordHeight;
+ }
+ else
+ {
+ fWidth = aLogicRect.GetWidth();
+ fHeight = aLogicRect.GetHeight();
+ }
+
+ if ( aHandle.nFlags & HandleFlags::SWITCHED )
+ {
+ if ( aLogicRect.GetHeight() > aLogicRect.GetWidth() )
+ {
+ double fX = fPos1;
+ double fY = fPos2;
+ double fTmp = fWidth;
+ fPos1 = fY;
+ fPos2 = fX;
+ fHeight = fWidth;
+ fWidth = fTmp;
+ }
+ }
+
+ sal_Int32 nFirstAdjustmentValue = -1, nSecondAdjustmentValue = -1;
+
+ // ODF shapes are expected to use a direct binding between position and adjustment
+ // values. OOXML preset shapes use known formulas. These are calculated backward to
+ // get the adjustment values. So far we do not have a general method to calculate
+ // the adjustment values for any shape from the handle position.
+ if ( aHandle.aPosition.First.Type == EnhancedCustomShapeParameterType::ADJUSTMENT )
+ aHandle.aPosition.First.Value >>= nFirstAdjustmentValue;
+ if ( aHandle.aPosition.Second.Type == EnhancedCustomShapeParameterType::ADJUSTMENT )
+ aHandle.aPosition.Second.Value>>= nSecondAdjustmentValue;
+
+ if ( aHandle.nFlags & ( HandleFlags::POLAR | HandleFlags::REFR | HandleFlags::REFANGLE))
+ { // Polar-Handle
+
+ if (aHandle.nFlags & HandleFlags::REFR)
+ nFirstAdjustmentValue = aHandle.nRefR;
+ if (aHandle.nFlags & HandleFlags::REFANGLE)
+ nSecondAdjustmentValue = aHandle.nRefAngle;
+
+ double fAngle(0.0);
+ double fRadius(0.0);
+ // 'then' treats only shapes of type "ooxml-foo", fontwork shapes have been mapped
+ // to MS binary import and will be treated in 'else'.
+ if (bOOXMLShape)
+ {
+ // DrawingML polar handles set REFR or REFANGLE instead of POLAR
+ // use the shape center instead.
+ double fDX = fPos1 - fWidth / 2.0;
+ double fDY = fPos2 - fHeight / 2.0;
+
+ // There exists no common pattern. 'radius' or 'angle' might have special meaning.
+ if (sShapeType == "ooxml-blockArc" && nIndex == 1)
+ {
+ // usual angle, special radius
+ fAngle = lcl_getAngleInOOXMLUnit(fDY, fDX);
+ // The value connected to REFR is the _difference_ between the outer
+ // ellipse given by shape width and height and the inner ellipse through
+ // the handle position.
+ double fRadiusDifference
+ = lcl_getRadiusDistance(fWidth / 2.0, fHeight / 2.0, fDX, fDY);
+ double fss(std::min(fWidth, fHeight));
+ if (fss != 0)
+ fRadius = fRadiusDifference * 100000.0 / fss;
+ }
+ else if (sShapeType == "ooxml-donut" || sShapeType == "ooxml-noSmoking")
+ {
+ // no angle adjustment, radius bound to x-coordinate of handle
+ double fss(std::min(fWidth, fHeight));
+ if (fss != 0.0)
+ fRadius = fPos1 * 100000.0 / fss;
+ }
+ else if ((sShapeType == "ooxml-circularArrow"
+ || sShapeType == "ooxml-leftRightCircularArrow"
+ || sShapeType == "ooxml-leftCircularArrow")
+ && nIndex == 0)
+ {
+ // The value adj2 is the increase compared to the angle in adj3
+ double fHandleAngle = lcl_getAngleInOOXMLUnit(fDY, fDX);
+ if (sShapeType == "ooxml-leftCircularArrow")
+ fAngle = GetAdjustValueAsDouble(2) - fHandleAngle;
+ else
+ fAngle = fHandleAngle - GetAdjustValueAsDouble(2);
+ if (fAngle < 0.0) // 0deg to 360deg cut
+ fAngle += 21600000.0;
+ // no REFR
+ }
+ else if ((sShapeType == "ooxml-circularArrow"
+ || sShapeType == "ooxml-leftCircularArrow"
+ || sShapeType == "ooxml-leftRightCircularArrow")
+ && nIndex == 2)
+ {
+ // The value adj1 connected to REFR is the thickness of the arc. The adjustvalue adj5
+ // has the _difference_ between the outer ellipse given by shape width and height
+ // and the middle ellipse of the arc. The handle is on the outer side of the
+ // arc. So we calculate the difference between the ellipse through the handle
+ // and the outer ellipse and subtract then.
+ double fRadiusDifferenceHandle
+ = lcl_getRadiusDistance(fWidth / 2.0, fHeight / 2.0, fDX, fDY);
+ double fadj5(GetAdjustValueAsDouble(4));
+ double fss(std::min(fWidth, fHeight));
+ if (fss != 0.0)
+ {
+ fadj5 = fadj5 * fss / 100000.0;
+ fRadius = 2.0 * (fadj5 - fRadiusDifferenceHandle);
+ fRadius = fRadius * 100000.0 / fss;
+ }
+ // ToDo: Get angle adj3 exact. Use approximation for now
+ fAngle = lcl_getAngleInOOXMLUnit(fDY, fDX);
+ }
+ else if ((sShapeType == "ooxml-circularArrow"
+ || sShapeType == "ooxml-leftCircularArrow"
+ || sShapeType == "ooxml-leftRightCircularArrow")
+ && nIndex == 3)
+ {
+ // ToDo: Getting handle position from adjustment value adj5 is complex.
+ // Analytical or numerical solution for backward calculation is missing.
+ // Approximation for now, using a line from center through handle position.
+ double fAngleRad(0.0);
+ if (fDX != 0.0 || fDY != 0.0)
+ fAngleRad = atan2(fDY, fDX);
+ double fHelpX = cos(fAngleRad) * fHeight / 2.0;
+ double fHelpY = sin(fAngleRad) * fWidth / 2.0;
+ if (fHelpX != 0.0 || fHelpY != 0.0)
+ {
+ double fHelpAngle = atan2(fHelpY, fHelpX);
+ double fOuterX = fWidth / 2.0 * cos(fHelpAngle);
+ double fOuterY = fHeight / 2.0 * sin(fHelpAngle);
+ double fOuterRadius = std::hypot(fOuterX, fOuterY);
+ double fHandleRadius = std::hypot(fDX, fDY);
+ fRadius = (fOuterRadius - fHandleRadius) / 2.0;
+ double fss(std::min(fWidth, fHeight));
+ if (fss != 0.0)
+ fRadius = fRadius * 100000.0 / fss;
+ }
+ // no REFANGLE
+ }
+ else if (sShapeType == "ooxml-mathNotEqual" && nIndex == 1)
+ {
+ double fadj1(GetAdjustValueAsDouble(0));
+ double fadj3(GetAdjustValueAsDouble(2));
+ fadj1 = fadj1 * fHeight / 100000.0;
+ fadj3 = fadj3 * fHeight / 100000.0;
+ double fDYRefHorizBar = fDY + fadj1 + fadj3;
+ if (fDX != 0.0 || fDYRefHorizBar != 0.0)
+ {
+ double fRawAngleDeg = basegfx::rad2deg(atan2(fDYRefHorizBar, fDX));
+ fAngle = (fRawAngleDeg + 180.0) * 60000.0;
+ }
+ // no REFR
+ }
+ else
+ {
+ // no special meaning of radius or angle, suitable for "ooxml-arc",
+ // "ooxml-chord", "ooxml-pie" and circular arrows value adj4.
+ fAngle = lcl_getAngleInOOXMLUnit(fDY, fDX);
+ fRadius = std::hypot(fDX, fDY);
+ double fss(std::min(fWidth, fHeight));
+ if (fss != 0.0)
+ fRadius = fRadius * 100000.0 / fss;
+ }
+ }
+ else // e.g. shapes from ODF, MS binary import or shape type "fontwork-foo"
+ {
+ double fXRef, fYRef;
+ if (aHandle.nFlags & HandleFlags::POLAR)
+ {
+ GetParameter(fXRef, aHandle.aPolar.First, false, false);
+ GetParameter(fYRef, aHandle.aPolar.Second, false, false);
+ }
+ else
+ {
+ fXRef = fWidth / 2.0;
+ fYRef = fHeight / 2.0;
+ }
+ const double fDX = fPos1 - fXRef;
+ const double fDY = fPos2 - fYRef;
+ // ToDo: MS binary uses fixed-point number for the angle. Make sure conversion
+ // to double is done in import and export.
+ // ToDo: Angle unit is degree, but range ]-180;180] or [0;360[? Assume ]-180;180].
+ if (fDX != 0.0 || fDY != 0.0)
+ {
+ fRadius = std::hypot(fDX, fDY);
+ fAngle = basegfx::rad2deg(atan2(fDY, fDX));
+ }
+ }
+
+ // All formats can restrict the radius to a range
+ if ( aHandle.nFlags & HandleFlags::RADIUS_RANGE_MINIMUM )
+ {
+ double fMin;
+ GetParameter( fMin, aHandle.aRadiusRangeMinimum, false, false );
+ if ( fRadius < fMin )
+ fRadius = fMin;
+ }
+ if ( aHandle.nFlags & HandleFlags::RADIUS_RANGE_MAXIMUM )
+ {
+ double fMax;
+ GetParameter( fMax, aHandle.aRadiusRangeMaximum, false, false );
+ if ( fRadius > fMax )
+ fRadius = fMax;
+ }
+
+ if ( nFirstAdjustmentValue >= 0 )
+ SetAdjustValueAsDouble( fRadius, nFirstAdjustmentValue );
+ if ( nSecondAdjustmentValue >= 0 )
+ SetAdjustValueAsDouble( fAngle, nSecondAdjustmentValue );
+ }
+ else // XY-Handle
+ {
+ // Calculating the adjustment values follows in most cases some patterns, which only
+ // need width and height of the shape and handle position. These patterns are calculated
+ // in the static, local methods. More complex calculations or additional steps are
+ // done here.
+ // Values for corner cases like 'root(negative)' or 'div zero' are meaningless dummies.
+ // Identifiers often refer to guide names in OOXML shape definitions.
+ double fAdjustX = fPos1;
+ double fAdjustY = fPos2;
+ if (aHandle.nFlags & HandleFlags::REFX)
+ {
+ nFirstAdjustmentValue = aHandle.nRefX;
+ if ((sShapeType == "ooxml-gear6") || (sShapeType == "ooxml-gear9"))
+ {
+ // special, needs angle calculations
+ double fss(std::min(fWidth, fHeight));
+ double fadj1(GetAdjustValueAsDouble(0)); // from point D6 or D9
+ double fth(fadj1 * fss / 100000.0); // radius difference
+ double frw(fWidth / 2.0 - fth); // inner ellipse
+ double frh(fHeight / 2.0 - fth);
+ double fDX(fPos1 - fWidth / 2.0);
+ double fDY(fPos2 - fHeight / 2.0);
+ double fbA(-1.7); // effective angle for point A6 or A9, dummy value
+ if (fDX != 0.0 || fDY != 0.0)
+ fbA = atan2(fDY, fDX);
+ double faA(fbA); // corresponding circle angle, dummy value
+ double ftmpX(frh * cos(fbA));
+ double ftmpY(frw * sin(fbA));
+ if (ftmpX != 0.0 || ftmpY != 0.0)
+ faA = atan2(ftmpY, ftmpX); // range ]-pi..pi], here -pi < faA < -pi/2
+ // screen 270 deg = mathematic coordinate system -pi/2
+ double fha(-M_PI_2 - faA); // positive circle angle difference to 270 deg
+ if (abs(fha) == M_PI_2) // should not happen, but ensure no tan(90deg)
+ fha = 0.12; // dummy value
+ double flFD(2 * std::min(frw, frh) * tan(fha) - fth);
+ if (fss != 0.0)
+ fAdjustX = flFD / fss * 100000.0;
+ }
+ else
+ {
+ fAdjustX
+ = lcl_getXAdjustmentValue(sShapeType, nIndex, fPos1, fWidth, fHeight);
+ if ((sShapeType == "ooxml-curvedDownArrow")
+ || (sShapeType == "ooxml-curvedUpArrow"))
+ {
+ double fss(std::min(fWidth, fHeight));
+ if (fss != 0.0)
+ {
+ double fadj3(GetAdjustValueAsDouble(2));
+ double fHScaled(100000.0 * fHeight / fss);
+ double fRadicand(fHScaled * fHScaled - fadj3 * fadj3);
+ double fSqrt = fRadicand >= 0.0 ? sqrt(fRadicand) : 0.0;
+ double fPart(200000.0 * fWidth / fss * (fSqrt + fHScaled));
+ fAdjustX = fPart - 4.0 * fHScaled * fAdjustX;
+ if (nIndex == 0)
+ {
+ // calculate adj1
+ double fadj2(GetAdjustValueAsDouble(1));
+ fAdjustX = fAdjustX - fadj2 * (fSqrt + fHScaled);
+ double fDenominator(fSqrt - 3.0 * fHScaled);
+ fAdjustX /= fDenominator != 0.0 ? fDenominator : 1.0;
+ }
+ else
+ {
+ // nIndex == 1, calculate adj2
+ double fadj1(GetAdjustValueAsDouble(0));
+ fAdjustX = fAdjustX - fadj1 * (fSqrt - fHScaled);
+ double fDenominator(fSqrt + 3.0 * fHScaled);
+ fAdjustX /= fDenominator != 0.0 ? fDenominator : 1.0;
+ }
+ }
+ }
+ }
+ }
+
+ if (aHandle.nFlags & HandleFlags::REFY)
+ {
+ nSecondAdjustmentValue = aHandle.nRefY;
+ if ((sShapeType == "ooxml-gear6") || (sShapeType == "ooxml-gear9"))
+ {
+ // special, acts more like a polar handle radius
+ double fDX = fPos1 - fWidth / 2.0;
+ double fDY = fPos2 - fHeight / 2.0;
+ double fRadiusDifference
+ = lcl_getRadiusDistance(fWidth / 2.0, fHeight / 2.0, fDX, fDY);
+ double fss(std::min(fWidth, fHeight));
+ if (fss != 0)
+ fAdjustY = fRadiusDifference / fss * 100000.0;
+ }
+ else
+ {
+ fAdjustY
+ = lcl_getYAdjustmentValue(sShapeType, nIndex, fPos2, fWidth, fHeight);
+ if (sShapeType == "ooxml-mathDivide" && nIndex == 1)
+ fAdjustY = fAdjustY - GetAdjustValueAsDouble(0) / 2.0
+ - GetAdjustValueAsDouble(2);
+ else if (sShapeType == "ooxml-mathEqual" && nIndex == 0)
+ fAdjustY -= GetAdjustValueAsDouble(1) / 2.0;
+ else if (sShapeType == "ooxml-mathNotEqual" && nIndex == 0)
+ fAdjustY -= GetAdjustValueAsDouble(2) / 2.0;
+ else if (sShapeType == "ooxml-leftUpArrow" && nIndex == 0)
+ fAdjustY -= GetAdjustValueAsDouble(1) * 2.0;
+ else if ((sShapeType == "ooxml-curvedRightArrow")
+ || (sShapeType == "ooxml-curvedLeftArrow"))
+ {
+ double fss(std::min(fWidth, fHeight));
+ if (fss != 0.0)
+ {
+ double fadj3(GetAdjustValueAsDouble(2));
+ double fWScaled(100000.0 * fWidth / fss);
+ double fRadicand(fWScaled * fWScaled - fadj3 * fadj3);
+ double fSqrt = fRadicand >= 0.0 ? sqrt(fRadicand) : 0.0;
+ if (nIndex == 0)
+ {
+ // calculate adj1
+ double fadj2(GetAdjustValueAsDouble(1));
+ fAdjustY = fWScaled * (2.0 * fAdjustY - fadj2);
+ fAdjustY += (200000.0 / fss * fHeight - fadj2) * fSqrt;
+ double fDenominator(fSqrt + fWScaled);
+ fAdjustY /= fDenominator != 0.0 ? fDenominator : 1.0;
+ }
+ else
+ {
+ // nIndex == 1, calculate adj2
+ double fadj1(GetAdjustValueAsDouble(0));
+ fAdjustY = fWScaled * (2.0 * fAdjustY + fadj1);
+ fAdjustY += (200000.0 / fss * fHeight - fadj1) * fSqrt;
+ double fDenominator(fSqrt + 3.0 * fWScaled);
+ fAdjustY /= fDenominator != 0.0 ? fDenominator : 1.0;
+ }
+ }
+ }
+ else if (sShapeType == "ooxml-uturnArrow" && nIndex == 2)
+ {
+ double fss(std::min(fWidth, fHeight));
+ if (fss != 0.0)
+ {
+ double fadj5(GetAdjustValueAsDouble(4));
+ fAdjustY += fHeight / fss * (fadj5 - 100000.0);
+ }
+ }
+ else if (sShapeType == "ooxml-leftRightRibbon")
+ {
+ if (nIndex == 0)
+ fAdjustY = GetAdjustValueAsDouble(2) - fAdjustY;
+ else // nIndex == 2
+ fAdjustY = GetAdjustValueAsDouble(0) + fAdjustY;
+ }
+ }
+ }
+
+ if ( nFirstAdjustmentValue >= 0 )
+ {
+ if ( aHandle.nFlags & HandleFlags::RANGE_X_MINIMUM ) // check if horizontal handle needs to be within a range
+ {
+ double fXMin;
+ GetParameter( fXMin, aHandle.aXRangeMinimum, false, false );
+ if (fAdjustX < fXMin)
+ fAdjustX = fXMin;
+ }
+ if ( aHandle.nFlags & HandleFlags::RANGE_X_MAXIMUM ) // check if horizontal handle needs to be within a range
+ {
+ double fXMax;
+ GetParameter( fXMax, aHandle.aXRangeMaximum, false, false );
+ if (fAdjustX > fXMax)
+ fAdjustX = fXMax;
+ }
+ SetAdjustValueAsDouble(fAdjustX, nFirstAdjustmentValue);
+ }
+ if ( nSecondAdjustmentValue >= 0 )
+ {
+ if ( aHandle.nFlags & HandleFlags::RANGE_Y_MINIMUM ) // check if vertical handle needs to be within a range
+ {
+ double fYMin;
+ GetParameter( fYMin, aHandle.aYRangeMinimum, false, false );
+ if (fAdjustY < fYMin)
+ fAdjustY = fYMin;
+ }
+ if ( aHandle.nFlags & HandleFlags::RANGE_Y_MAXIMUM ) // check if vertical handle needs to be within a range
+ {
+ double fYMax;
+ GetParameter( fYMax, aHandle.aYRangeMaximum, false, false );
+ if (fAdjustY > fYMax)
+ fAdjustY = fYMax;
+ }
+ SetAdjustValueAsDouble(fAdjustY, nSecondAdjustmentValue);
+ }
+ }
+ // and writing them back into the GeometryItem
+ SdrCustomShapeGeometryItem aGeometryItem(mrSdrObjCustomShape.GetMergedItem( SDRATTR_CUSTOMSHAPE_GEOMETRY ));
+ css::beans::PropertyValue aPropVal;
+ aPropVal.Name = "AdjustmentValues";
+ aPropVal.Value <<= seqAdjustmentValues;
+ aGeometryItem.SetPropertyValue( aPropVal );
+ mrSdrObjCustomShape.SetMergedItem( aGeometryItem );
+ bRetValue = true;
+ }
+ }
+ return bRetValue;
+}
+
+void EnhancedCustomShape2d::SwapStartAndEndArrow( SdrObject* pObj ) //#108274
+{
+ XLineStartItem aLineStart;
+ aLineStart.SetLineStartValue(pObj->GetMergedItem( XATTR_LINEEND ).GetLineEndValue());
+ XLineStartWidthItem aLineStartWidth(pObj->GetMergedItem( XATTR_LINEENDWIDTH ).GetValue());
+ XLineStartCenterItem aLineStartCenter(pObj->GetMergedItem( XATTR_LINEENDCENTER ).GetValue());
+
+ XLineEndItem aLineEnd;
+ aLineEnd.SetLineEndValue(pObj->GetMergedItem( XATTR_LINESTART ).GetLineStartValue());
+ XLineEndWidthItem aLineEndWidth(pObj->GetMergedItem( XATTR_LINESTARTWIDTH ).GetValue());
+ XLineEndCenterItem aLineEndCenter(pObj->GetMergedItem( XATTR_LINESTARTCENTER ).GetValue());
+
+ pObj->SetMergedItem( aLineStart );
+ pObj->SetMergedItem( aLineStartWidth );
+ pObj->SetMergedItem( aLineStartCenter );
+ pObj->SetMergedItem( aLineEnd );
+ pObj->SetMergedItem( aLineEndWidth );
+ pObj->SetMergedItem( aLineEndCenter );
+}
+
+static basegfx::B2DPolygon CreateArc( const tools::Rectangle& rRect, const Point& rStart, const Point& rEnd, const bool bClockwise )
+{
+ tools::Rectangle aRect( rRect );
+ Point aStart( rStart );
+ Point aEnd( rEnd );
+
+ sal_Int32 bSwapStartEndAngle = 0;
+
+ if ( aRect.Left() > aRect.Right() )
+ bSwapStartEndAngle ^= 0x01;
+ if ( aRect.Top() > aRect.Bottom() )
+ bSwapStartEndAngle ^= 0x11;
+ if ( bSwapStartEndAngle )
+ {
+ aRect.Justify();
+ if ( bSwapStartEndAngle & 1 )
+ {
+ Point aTmp( aStart );
+ aStart = aEnd;
+ aEnd = aTmp;
+ }
+ }
+
+ tools::Polygon aTempPoly( aRect, aStart, aEnd, PolyStyle::Arc );
+ basegfx::B2DPolygon aRetval;
+
+ if ( bClockwise )
+ {
+ for ( sal_uInt16 j = aTempPoly.GetSize(); j--; )
+ {
+ aRetval.append(basegfx::B2DPoint(aTempPoly[ j ].X(), aTempPoly[ j ].Y()));
+ }
+ }
+ else
+ {
+ for ( sal_uInt16 j = 0; j < aTempPoly.GetSize(); j++ )
+ {
+ aRetval.append(basegfx::B2DPoint(aTempPoly[ j ].X(), aTempPoly[ j ].Y()));
+ }
+ }
+
+ return aRetval;
+}
+
+static double lcl_getNormalizedCircleAngleRad(const double fWR, const double fHR, const double fEllipseAngleDeg)
+{
+ double fRet(0.0);
+ double fEAngleDeg(fmod(fEllipseAngleDeg, 360.0));
+ if (fEAngleDeg < 0.0)
+ fEAngleDeg += 360.0;
+ if (fEAngleDeg == 0.0 || fEAngleDeg == 90.0 || fEAngleDeg == 180.0 || fEAngleDeg == 270.0)
+ return basegfx::deg2rad(fEAngleDeg);
+ const double fX(fHR * cos(basegfx::deg2rad(fEAngleDeg)));
+ const double fY(fWR * sin(basegfx::deg2rad(fEAngleDeg)));
+ if (fX != 0.0 || fY != 0.0)
+ {
+ fRet = atan2(fY, fX);
+ if (fRet < 0.0)
+ fRet += 2 * M_PI;
+ }
+ return fRet;
+}
+
+static double lcl_getNormalizedAngleRad(const double fCircleAngleDeg)
+{
+ double fRet(fmod(fCircleAngleDeg, 360.0));
+ if (fRet < 0.0)
+ fRet += 360.0;
+ return basegfx::deg2rad(fRet);
+}
+
+void EnhancedCustomShape2d::CreateSubPath(
+ sal_Int32& rSrcPt,
+ sal_Int32& rSegmentInd,
+ std::vector< std::pair< SdrPathObjUniquePtr, double> >& rObjectList,
+ const bool bLineGeometryNeededOnly,
+ const bool bSortFilledObjectsToBack,
+ sal_Int32 nIndex)
+{
+ bool bNoFill = false;
+ bool bNoStroke = false;
+ double dBrightness = 0.0; //no blending
+
+ basegfx::B2DPolyPolygon aNewB2DPolyPolygon;
+ basegfx::B2DPolygon aNewB2DPolygon;
+
+ SetPathSize( nIndex );
+
+ sal_Int32 nSegInfoSize = seqSegments.getLength();
+ if ( !nSegInfoSize )
+ {
+ for ( const EnhancedCustomShapeParameterPair& rCoordinate : std::as_const(seqCoordinates) )
+ {
+ const Point aTempPoint(GetPoint( rCoordinate, true, true ));
+ aNewB2DPolygon.append(basegfx::B2DPoint(aTempPoint.X(), aTempPoint.Y()));
+ }
+
+ aNewB2DPolygon.setClosed(true);
+ }
+ else
+ {
+ sal_Int32 nCoordSize = seqCoordinates.getLength();
+ for ( ;rSegmentInd < nSegInfoSize; )
+ {
+ sal_Int16 nCommand = seqSegments[ rSegmentInd ].Command;
+ sal_Int16 nPntCount= seqSegments[ rSegmentInd++ ].Count;
+
+ switch ( nCommand )
+ {
+ case NOFILL :
+ bNoFill = true;
+ break;
+ case NOSTROKE :
+ bNoStroke = true;
+ break;
+ case DARKEN :
+ dBrightness = -0.4; //use sign to distinguish DARKEN from LIGHTEN
+ break;
+ case DARKENLESS :
+ dBrightness = -0.2;
+ break;
+ case LIGHTEN :
+ dBrightness = 0.4;
+ break;
+ case LIGHTENLESS :
+ dBrightness = 0.2;
+ break;
+ case MOVETO :
+ {
+ if(aNewB2DPolygon.count() > 1)
+ {
+ // #i76201# Add conversion to closed polygon when first and last points are equal
+ basegfx::utils::checkClosed(aNewB2DPolygon);
+ aNewB2DPolyPolygon.append(aNewB2DPolygon);
+ }
+
+ aNewB2DPolygon.clear();
+
+ if ( rSrcPt < nCoordSize )
+ {
+ const Point aTempPoint(GetPoint( seqCoordinates[ rSrcPt++ ], true, true ));
+ SAL_INFO(
+ "svx",
+ "moveTo: " << aTempPoint.X() << ","
+ << aTempPoint.Y());
+ aNewB2DPolygon.append(basegfx::B2DPoint(aTempPoint.X(), aTempPoint.Y()));
+ }
+ }
+ break;
+ case ENDSUBPATH :
+ break;
+ case CLOSESUBPATH :
+ {
+ if(aNewB2DPolygon.count())
+ {
+ if(aNewB2DPolygon.count() > 1)
+ {
+ aNewB2DPolygon.setClosed(true);
+ aNewB2DPolyPolygon.append(aNewB2DPolygon);
+ }
+
+ aNewB2DPolygon.clear();
+ }
+ }
+ break;
+ case CURVETO :
+ {
+ for ( sal_uInt16 i = 0; ( i < nPntCount ) && ( ( rSrcPt + 2 ) < nCoordSize ); i++ )
+ {
+ const Point aControlA(GetPoint( seqCoordinates[ rSrcPt++ ], true, true ));
+ const Point aControlB(GetPoint( seqCoordinates[ rSrcPt++ ], true, true ));
+ const Point aEnd(GetPoint( seqCoordinates[ rSrcPt++ ], true, true ));
+
+ DBG_ASSERT(aNewB2DPolygon.count(), "EnhancedCustomShape2d::CreateSubPath: Error in adding control point (!)");
+ aNewB2DPolygon.appendBezierSegment(
+ basegfx::B2DPoint(aControlA.X(), aControlA.Y()),
+ basegfx::B2DPoint(aControlB.X(), aControlB.Y()),
+ basegfx::B2DPoint(aEnd.X(), aEnd.Y()));
+ }
+ }
+ break;
+
+ case ANGLEELLIPSE: // command U
+ case ANGLEELLIPSETO: // command T
+ {
+ // Some shapes will need special handling, decide on property 'Type'.
+ OUString sShpType;
+ SdrCustomShapeGeometryItem& rGeometryItem = const_cast<SdrCustomShapeGeometryItem&>(mrSdrObjCustomShape.GetMergedItem(SDRATTR_CUSTOMSHAPE_GEOMETRY));
+ Any* pAny = rGeometryItem.GetPropertyValueByName("Type");
+ if (pAny)
+ *pAny >>= sShpType;
+ // User defined shapes in MS binary format, which contain command U or T after import
+ // in LibreOffice, starts with "mso".
+ const bool bIsFromBinaryImport(sShpType.startsWith("mso"));
+ // The only own or imported preset shapes with U command are those listed below.
+ // Command T is not used in preset shapes.
+ const std::unordered_set<OUString> aPresetShapesWithU =
+ { "ellipse", "ring", "smiley", "sun", "forbidden", "flowchart-connector",
+ "flowchart-summing-junction", "flowchart-or", "cloud-callout"};
+ std::unordered_set<OUString>::const_iterator aIter = aPresetShapesWithU.find(sShpType);
+ const bool bIsPresetShapeWithU(aIter != aPresetShapesWithU.end());
+
+ for (sal_uInt16 i = 0; (i < nPntCount) && ((rSrcPt + 2) < nCoordSize); i++)
+ {
+ // ANGLEELLIPSE is the same as ANGLEELLIPSETO, only that it
+ // makes an implicit MOVETO. That ends the previous subpath.
+ if (ANGLEELLIPSE == nCommand)
+ {
+ if (aNewB2DPolygon.count() > 1)
+ {
+ // #i76201# Add conversion to closed polygon when first and last points are equal
+ basegfx::utils::checkClosed(aNewB2DPolygon);
+ aNewB2DPolyPolygon.append(aNewB2DPolygon);
+ }
+ aNewB2DPolygon.clear();
+ }
+
+ // Read all parameters, but do not finally handle them.
+ basegfx::B2DPoint aCenter(GetPointAsB2DPoint(seqCoordinates[ rSrcPt ], true, true));
+ double fWR; // horizontal ellipse radius
+ double fHR; // vertical ellipse radius
+ GetParameter(fWR, seqCoordinates[rSrcPt + 1].First, true, false);
+ GetParameter(fHR, seqCoordinates[rSrcPt + 1].Second, false, true);
+ double fStartAngle;
+ GetParameter(fStartAngle, seqCoordinates[rSrcPt + 2].First, false, false);
+ double fEndAngle;
+ GetParameter(fEndAngle, seqCoordinates[rSrcPt + 2].Second, false, false);
+ // Increasing here allows flat case differentiation tree by using 'continue'.
+ rSrcPt += 3;
+
+ double fScaledWR(fWR * fXScale);
+ double fScaledHR(fHR * fYScale);
+ if (fScaledWR == 0.0 && fScaledHR == 0.0)
+ {
+ // degenerated ellipse, add center point
+ aNewB2DPolygon.append(aCenter);
+ continue;
+ }
+
+ if (bIsFromBinaryImport)
+ {
+ // If a shape comes from MS binary ('escher') import, the angles are in degrees*2^16
+ // and the second angle is not an end angle, but a swing angle.
+ // MS Word shows this behavior: 0deg right, 90deg top, 180deg left and 270deg
+ // bottom. Third and forth parameter are horizontal and vertical radius, not width
+ // and height as noted in VML spec. A positive swing angle goes counter-clock
+ // wise (in user view). The swing angle might go several times around in case
+ // abs(swing angle) >= 360deg. Stroke accumulates, so that e.g. dash-dot might fill the
+ // gaps of previous turn. Fill does not accumulate but uses even-odd rule, semi-transparent
+ // fill does not become darker. The start and end points of the arc are calculated by
+ // using the angles on a circle and then scaling the circle to the ellipse. Caution, that
+ // is different from angle handling in ARCANGLETO and ODF.
+ // The following implementation generates such rendering. It is only for rendering legacy
+ // MS shapes and independent of the meaning of commands U and T in ODF specification.
+
+ // The WordArt shape 'RingOutside' has already angles in degree, all other need
+ // conversion from fixed-point number.
+ double fSwingAngle = fEndAngle;
+ if (sShpType != "mso-spt143")
+ {
+ fStartAngle /= 65536.0;
+ fSwingAngle = fEndAngle / 65536.0;
+ }
+ // Convert orientation
+ fStartAngle = -fStartAngle;
+ fSwingAngle = -fSwingAngle;
+
+ fEndAngle = fStartAngle + fSwingAngle;
+ if (fSwingAngle < 0.0)
+ std::swap(fStartAngle, fEndAngle);
+ double fFrom(fStartAngle);
+ double fTo(fFrom + 180.0);
+ basegfx::B2DPolygon aTempB2DPolygon;
+ double fS; // fFrom in radians in [0..2Pi[
+ double fE; // fTo or fEndAngle in radians in [0..2PI[
+ while (fTo < fEndAngle)
+ {
+ fS = lcl_getNormalizedAngleRad(fFrom);
+ fE = lcl_getNormalizedAngleRad(fTo);
+ aTempB2DPolygon.append(basegfx::utils::createPolygonFromEllipseSegment(aCenter, fScaledWR, fScaledHR, fS,fE));
+ fFrom = fTo;
+ fTo += 180.0;
+ }
+ fS = lcl_getNormalizedAngleRad(fFrom);
+ fE = lcl_getNormalizedAngleRad(fEndAngle);
+ aTempB2DPolygon.append(basegfx::utils::createPolygonFromEllipseSegment(aCenter, fScaledWR, fScaledHR,fS, fE));
+ if (fSwingAngle < 0)
+ aTempB2DPolygon.flip();
+ aNewB2DPolygon.append(aTempB2DPolygon);
+ continue;
+ }
+
+ // The not yet handled shapes are own preset shapes, or preset shapes from MS binary import, or user
+ // defined shapes, or foreign shapes. Shapes from OOXML import do not use ANGLEELLIPSE or
+ // ANGLEELLIPSETO, but use ARCANGLETO.
+ if (bIsPresetShapeWithU)
+ {
+ // Besides "cloud-callout" all preset shapes have angle values '0 360'.
+ // The imported "cloud-callout" has angle values '0 360' too, only our own "cloud-callout"
+ // has values '0 23592960'. But that is fixedfloat and means 360*2^16. Thus all these shapes
+ // have a full ellipse with start at 0deg.
+ aNewB2DPolygon.append(basegfx::utils::createPolygonFromEllipse(aCenter, fScaledWR, fScaledHR));
+ continue;
+ }
+
+ // In all other cases, full ODF conform handling is necessary. ODF rules:
+ // Third and forth parameter are horizontal and vertical radius.
+ // An angle determines the start or end point of the segment by intersection of the second angle
+ // leg with the ellipse. The first angle leg is always the positive x-axis. For the position
+ // of the intersection points the angle is used modulo 360deg in range [0deg..360deg[.
+ // The position of range [0deg..360deg[ is the same as in command ARCANGLETO, with 0deg right,
+ // 90deg bottom, 180deg left and 270deg top. Only if abs(end angle - start angle) == 360 deg,
+ // a full ellipse is drawn. The segment is always drawn clock wise (in user view) from start
+ // point to end point. The end point of the segment becomes the new "current" point.
+
+ if (fabs(fabs(fEndAngle - fStartAngle) - 360.0) < 1.0E-15)
+ {
+ // draw full ellipse
+ // Because createPolygonFromEllipseSegment cannot create full ellipse and
+ // createPolygonFromEllipse has no varying starts, we use two half ellipses.
+ const double fS(lcl_getNormalizedCircleAngleRad(fWR, fHR, fStartAngle));
+ const double fH(lcl_getNormalizedCircleAngleRad(fWR, fHR, fStartAngle + 180.0));
+ const double fE(lcl_getNormalizedCircleAngleRad(fWR, fHR, fEndAngle));
+ aNewB2DPolygon.append(basegfx::utils::createPolygonFromEllipseSegment(aCenter, fScaledWR, fScaledHR, fS, fH));
+ aNewB2DPolygon.append(basegfx::utils::createPolygonFromEllipseSegment(aCenter, fScaledWR, fScaledHR, fH, fE));
+ continue;
+ }
+
+ // remaining cases with central segment angle < 360
+ double fS(lcl_getNormalizedCircleAngleRad(fWR, fHR, fStartAngle));
+ double fE(lcl_getNormalizedCircleAngleRad(fWR, fHR, fEndAngle));
+ aNewB2DPolygon.append(basegfx::utils::createPolygonFromEllipseSegment(aCenter, fScaledWR, fScaledHR, fS, fE));
+ } // end for
+ } // end case
+ break;
+
+ case QUADRATICCURVETO :
+ {
+ for ( sal_Int32 i(0); ( i < nPntCount ) && ( rSrcPt + 1 < nCoordSize ); i++ )
+ {
+ DBG_ASSERT(aNewB2DPolygon.count(), "EnhancedCustomShape2d::CreateSubPath: Error no previous point for Q (!)");
+ if (aNewB2DPolygon.count() > 0)
+ {
+ const basegfx::B2DPoint aPreviousEndPoint(aNewB2DPolygon.getB2DPoint(aNewB2DPolygon.count()-1));
+ const basegfx::B2DPoint aControlQ(GetPointAsB2DPoint( seqCoordinates[ rSrcPt++ ], true, true ));
+ const basegfx::B2DPoint aEnd(GetPointAsB2DPoint( seqCoordinates[ rSrcPt++ ], true, true ));
+ const basegfx::B2DPoint aControlA((aPreviousEndPoint + (aControlQ * 2)) / 3);
+ const basegfx::B2DPoint aControlB(((aControlQ * 2) + aEnd) / 3);
+ aNewB2DPolygon.appendBezierSegment(aControlA, aControlB, aEnd);
+ }
+ else // no previous point; ill structured path, but try to draw as much as possible
+ {
+ rSrcPt++; // skip control point
+ const basegfx::B2DPoint aEnd(GetPointAsB2DPoint( seqCoordinates[ rSrcPt++ ], true, true ));
+ aNewB2DPolygon.append(aEnd);
+ }
+ }
+ }
+ break;
+
+ case LINETO :
+ {
+ for ( sal_Int32 i(0); ( i < nPntCount ) && ( rSrcPt < nCoordSize ); i++ )
+ {
+ const Point aTempPoint(GetPoint( seqCoordinates[ rSrcPt++ ], true, true ));
+ SAL_INFO(
+ "svx",
+ "lineTo: " << aTempPoint.X() << ","
+ << aTempPoint.Y());
+ aNewB2DPolygon.append(basegfx::B2DPoint(aTempPoint.X(), aTempPoint.Y()));
+ }
+ }
+ break;
+
+ case ARC :
+ case CLOCKWISEARC :
+ case ARCTO :
+ case CLOCKWISEARCTO :
+ {
+ bool bClockwise = ( nCommand == CLOCKWISEARC ) || ( nCommand == CLOCKWISEARCTO );
+ bool bImplicitMoveTo = (nCommand == ARC) || (nCommand == CLOCKWISEARC);
+ sal_uInt32 nXor = bClockwise ? 3 : 2;
+ for ( sal_uInt16 i = 0; ( i < nPntCount ) && ( ( rSrcPt + 3 ) < nCoordSize ); i++ )
+ {
+ if (bImplicitMoveTo)
+ {
+ if (aNewB2DPolygon.count() > 1)
+ {
+ // #i76201# Add conversion to closed polygon when first and last
+ // points are equal
+ basegfx::utils::checkClosed(aNewB2DPolygon);
+ aNewB2DPolyPolygon.append(aNewB2DPolygon);
+ }
+ aNewB2DPolygon.clear();
+ }
+ tools::Rectangle aRect = tools::Rectangle::Justify( GetPoint( seqCoordinates[ rSrcPt ], true, true ), GetPoint( seqCoordinates[ rSrcPt + 1 ], true, true ) );
+ if ( aRect.GetWidth() && aRect.GetHeight() )
+ {
+ Point aStart( GetPoint( seqCoordinates[ static_cast<sal_uInt16>( rSrcPt + nXor ) ], true, true ) );
+ Point aEnd( GetPoint( seqCoordinates[ static_cast<sal_uInt16>( rSrcPt + ( nXor ^ 1 ) ) ], true, true ) );
+ aNewB2DPolygon.append(CreateArc( aRect, aStart, aEnd, bClockwise));
+ }
+ rSrcPt += 4;
+ }
+ }
+ break;
+
+ case ARCANGLETO :
+ {
+ double fWR, fHR; // in Shape coordinate system
+ double fStartAngle, fSwingAngle; // in deg
+
+ for ( sal_uInt16 i = 0; ( i < nPntCount ) && ( rSrcPt + 1 < nCoordSize ); i++ )
+ {
+ basegfx::B2DPoint aTempPair;
+ aTempPair = GetPointAsB2DPoint(seqCoordinates[static_cast<sal_uInt16>(rSrcPt)], false /*bScale*/, false /*bReplaceGeoSize*/);
+ fWR = aTempPair.getX();
+ fHR = aTempPair.getY();
+ aTempPair = GetPointAsB2DPoint(seqCoordinates[static_cast<sal_uInt16>(rSrcPt + 1)], false /*bScale*/, false /*bReplaceGeoSize*/);
+ fStartAngle = aTempPair.getX();
+ fSwingAngle = aTempPair.getY();
+
+ // tdf#122323 MS Office clamps the swing angle to [-360,360]. Such restriction
+ // is neither in OOXML nor in ODF. Nevertheless, to be compatible we do it for
+ // "ooxml-foo" shapes. Those shapes have their origin in MS Office.
+ if (bOOXMLShape)
+ {
+ fSwingAngle = std::clamp(fSwingAngle, -360.0, 360.0);
+ }
+
+ SAL_INFO("svx", "ARCANGLETO scale: " << fWR << "x" << fHR << " angles: " << fStartAngle << "," << fSwingAngle);
+
+ if (aNewB2DPolygon.count() > 0) // otherwise no "current point"
+ {
+ // use similar methods as in command U
+ basegfx::B2DPolygon aTempB2DPolygon;
+
+ if (fWR == 0.0 && fHR == 0.0)
+ {
+ // degenerated ellipse, add this one point
+ aTempB2DPolygon.append(basegfx::B2DPoint(0.0, 0.0));
+ }
+ else
+ {
+ double fEndAngle = fStartAngle + fSwingAngle;
+ // Generate arc with ellipse left|top = 0|0.
+ basegfx::B2DPoint aCenter(fWR, fHR);
+ if (fSwingAngle < 0.0)
+ std::swap(fStartAngle, fEndAngle);
+ double fS; // fFrom in radians in [0..2Pi[
+ double fE; // fTo or fEndAngle in radians in [0..2PI[
+ double fFrom(fStartAngle);
+ // createPolygonFromEllipseSegment expects angles in [0..2PI[.
+ if (fSwingAngle >= 360.0 || fSwingAngle <= -360.0)
+ {
+ double fTo(fFrom + 180.0);
+ while (fTo < fEndAngle)
+ {
+ fS = lcl_getNormalizedCircleAngleRad(fWR, fHR, fFrom);
+ fE = lcl_getNormalizedCircleAngleRad(fWR, fHR, fTo);
+ aTempB2DPolygon.append(basegfx::utils::createPolygonFromEllipseSegment(aCenter, fWR, fHR, fS,fE));
+ fFrom = fTo;
+ fTo += 180.0;
+ }
+ }
+ fS = lcl_getNormalizedCircleAngleRad(fWR, fHR, fFrom);
+ fE = lcl_getNormalizedCircleAngleRad(fWR, fHR, fEndAngle);
+ aTempB2DPolygon.append(basegfx::utils::createPolygonFromEllipseSegment(aCenter, fWR, fHR,fS, fE));
+ if (fSwingAngle < 0)
+ aTempB2DPolygon.flip();
+ aTempB2DPolygon.removeDoublePoints();
+ }
+ // Scale arc to 1/100mm
+ basegfx::B2DHomMatrix aMatrix = basegfx::utils::createScaleB2DHomMatrix(fXScale, fYScale);
+ aTempB2DPolygon.transform(aMatrix);
+
+ // Now that we have the arc, move it to the "current point".
+ basegfx::B2DPoint aCurrentPointB2D( aNewB2DPolygon.getB2DPoint(aNewB2DPolygon.count() - 1 ) );
+ const double fDx(aCurrentPointB2D.getX() - aTempB2DPolygon.getB2DPoint(0).getX());
+ const double fDy(aCurrentPointB2D.getY() - aTempB2DPolygon.getB2DPoint(0).getY());
+ aMatrix = basegfx::utils::createTranslateB2DHomMatrix(fDx, fDy);
+ aTempB2DPolygon.transform(aMatrix);
+ aNewB2DPolygon.append(aTempB2DPolygon);
+ }
+
+ rSrcPt += 2;
+ }
+ }
+ break;
+
+ case ELLIPTICALQUADRANTX :
+ case ELLIPTICALQUADRANTY :
+ {
+ if (nPntCount && (rSrcPt < nCoordSize))
+ {
+ // The arc starts at the previous point and ends at the point given in the parameter.
+ basegfx::B2DPoint aStart;
+ basegfx::B2DPoint aEnd;
+ sal_uInt16 i = 0;
+ if (rSrcPt)
+ {
+ aStart = GetPointAsB2DPoint(seqCoordinates[rSrcPt - 1], true, true);
+ }
+ else
+ { // no previous point, path is ill-structured. But we want to show as much as possible.
+ // Thus make a moveTo to the point given as parameter and continue from there.
+ aStart = GetPointAsB2DPoint(seqCoordinates[static_cast<sal_uInt16>(rSrcPt)], true, true);
+ aNewB2DPolygon.append(aStart);
+ rSrcPt++;
+ i++;
+ }
+ // If there are several points, then the direction changes with every point.
+ bool bIsXDirection(nCommand == ELLIPTICALQUADRANTX);
+ basegfx::B2DPolygon aArc;
+ for ( ; ( i < nPntCount ) && ( rSrcPt < nCoordSize ); i++ )
+ {
+ aEnd = GetPointAsB2DPoint(seqCoordinates[rSrcPt], true, true);
+ basegfx::B2DPoint aCenter;
+ double fRadiusX = fabs(aEnd.getX() - aStart.getX());
+ double fRadiusY = fabs(aEnd.getY() - aStart.getY());
+ if (bIsXDirection)
+ {
+ aCenter = basegfx::B2DPoint(aStart.getX(),aEnd.getY());
+ if (aEnd.getX()<aStart.getX())
+ {
+ if (aEnd.getY()<aStart.getY()) // left, up
+ {
+ aArc = basegfx::utils::createPolygonFromEllipseSegment(aCenter, fRadiusX, fRadiusY, M_PI_2, M_PI);
+ }
+ else // left, down
+ {
+ aArc = basegfx::utils::createPolygonFromEllipseSegment(aCenter, fRadiusX, fRadiusY, M_PI, 1.5*M_PI);
+ aArc.flip();
+ }
+ }
+ else // aEnd.getX()>=aStart.getX()
+ {
+ if (aEnd.getY()<aStart.getY()) // right, up
+ {
+ aArc = basegfx::utils::createPolygonFromEllipseSegment(aCenter, fRadiusX, fRadiusY, 0.0, M_PI_2);
+ aArc.flip();
+ }
+ else // right, down
+ {
+ aArc = basegfx::utils::createPolygonFromEllipseSegment(aCenter, fRadiusX, fRadiusY, 1.5*M_PI, 2*M_PI);
+ }
+ }
+ }
+ else // y-direction
+ {
+ aCenter = basegfx::B2DPoint(aEnd.getX(),aStart.getY());
+ if (aEnd.getX()<aStart.getX())
+ {
+ if (aEnd.getY()<aStart.getY()) // up, left
+ {
+ aArc = basegfx::utils::createPolygonFromEllipseSegment(aCenter, fRadiusX, fRadiusY, 1.5*M_PI, 2*M_PI);
+ aArc.flip();
+ }
+ else // down, left
+ {
+ aArc = basegfx::utils::createPolygonFromEllipseSegment(aCenter, fRadiusX, fRadiusY, 0.0, M_PI_2);
+ }
+ }
+ else // aEnd.getX()>=aStart.getX()
+ {
+ if (aEnd.getY()<aStart.getY()) // up, right
+ {
+ aArc = basegfx::utils::createPolygonFromEllipseSegment(aCenter, fRadiusX, fRadiusY, M_PI, 1.5*M_PI);
+ }
+ else // down, right
+ {
+ aArc = basegfx::utils::createPolygonFromEllipseSegment(aCenter, fRadiusX, fRadiusY, M_PI_2, M_PI);
+ aArc.flip();
+ }
+ }
+ }
+ aNewB2DPolygon.append(aArc);
+ rSrcPt++;
+ bIsXDirection = !bIsXDirection;
+ aStart = aEnd;
+ }
+ }
+ // else error in path syntax, do nothing
+ }
+ break;
+
+#ifdef DBG_CUSTOMSHAPE
+ case UNKNOWN :
+ default :
+ {
+ SAL_WARN( "svx", "CustomShapes::unknown PolyFlagValue :" << nCommand );
+ }
+ break;
+#endif
+ }
+ if ( nCommand == ENDSUBPATH )
+ break;
+ }
+ }
+ if ( rSegmentInd == nSegInfoSize )
+ rSegmentInd++;
+
+ if(aNewB2DPolygon.count() > 1)
+ {
+ // #i76201# Add conversion to closed polygon when first and last points are equal
+ basegfx::utils::checkClosed(aNewB2DPolygon);
+ aNewB2DPolyPolygon.append(aNewB2DPolygon);
+ }
+
+ if(!aNewB2DPolyPolygon.count())
+ return;
+
+ // #i37011#
+ bool bForceCreateTwoObjects(false);
+
+ if(!bSortFilledObjectsToBack && !aNewB2DPolyPolygon.isClosed() && !bNoStroke)
+ {
+ bForceCreateTwoObjects = true;
+ }
+
+ if(bLineGeometryNeededOnly)
+ {
+ bForceCreateTwoObjects = true;
+ bNoFill = true;
+ bNoStroke = false;
+ }
+
+ if(bForceCreateTwoObjects || bSortFilledObjectsToBack)
+ {
+ if(bFilled && !bNoFill)
+ {
+ basegfx::B2DPolyPolygon aClosedPolyPolygon(aNewB2DPolyPolygon);
+ aClosedPolyPolygon.setClosed(true);
+ SdrPathObjUniquePtr pFill(new SdrPathObj(
+ mrSdrObjCustomShape.getSdrModelFromSdrObject(),
+ SdrObjKind::Polygon,
+ aClosedPolyPolygon));
+ SfxItemSet aTempSet(*this);
+ aTempSet.Put(makeSdrShadowItem(false));
+ aTempSet.Put(XLineStyleItem(drawing::LineStyle_NONE));
+ pFill->SetMergedItemSet(aTempSet);
+ rObjectList.push_back(std::pair< SdrPathObjUniquePtr, double >(std::move(pFill), dBrightness));
+ }
+
+ if(!bNoStroke)
+ {
+ // there is no reason to use OBJ_PLIN here when the polygon is actually closed,
+ // the non-fill is defined by XFILL_NONE. Since SdrPathObj::ImpForceKind() needs
+ // to correct the polygon (here: open it) using the type, the last edge may get lost.
+ // Thus, use a type that fits the polygon
+ SdrPathObjUniquePtr pStroke(new SdrPathObj(
+ mrSdrObjCustomShape.getSdrModelFromSdrObject(),
+ aNewB2DPolyPolygon.isClosed() ? SdrObjKind::Polygon : SdrObjKind::PolyLine,
+ aNewB2DPolyPolygon));
+ SfxItemSet aTempSet(*this);
+ aTempSet.Put(makeSdrShadowItem(false));
+ aTempSet.Put(XFillStyleItem(drawing::FillStyle_NONE));
+ pStroke->SetMergedItemSet(aTempSet);
+ rObjectList.push_back(std::pair< SdrPathObjUniquePtr, double >(std::move(pStroke), dBrightness));
+ }
+ }
+ else
+ {
+ SdrPathObjUniquePtr pObj;
+ SfxItemSet aTempSet(*this);
+ aTempSet.Put(makeSdrShadowItem(false));
+
+ if(bNoFill)
+ {
+ // see comment above about OBJ_PLIN
+ pObj.reset(new SdrPathObj(
+ mrSdrObjCustomShape.getSdrModelFromSdrObject(),
+ aNewB2DPolyPolygon.isClosed() ? SdrObjKind::Polygon : SdrObjKind::PolyLine,
+ aNewB2DPolyPolygon));
+ aTempSet.Put(XFillStyleItem(drawing::FillStyle_NONE));
+ }
+ else
+ {
+ aNewB2DPolyPolygon.setClosed(true);
+ pObj.reset(new SdrPathObj(
+ mrSdrObjCustomShape.getSdrModelFromSdrObject(),
+ SdrObjKind::Polygon,
+ aNewB2DPolyPolygon));
+ }
+
+ if(bNoStroke)
+ {
+ aTempSet.Put(XLineStyleItem(drawing::LineStyle_NONE));
+ }
+
+ pObj->SetMergedItemSet(aTempSet);
+ rObjectList.push_back(std::pair< SdrPathObjUniquePtr, double >(std::move(pObj), dBrightness));
+ }
+}
+
+static void CorrectCalloutArrows(
+ MSO_SPT eSpType,
+ sal_uInt32 nLineObjectCount,
+ std::vector< std::pair< SdrPathObjUniquePtr, double> >& vObjectList )
+{
+ bool bAccent = false;
+ switch( eSpType )
+ {
+ case mso_sptCallout1 :
+ case mso_sptBorderCallout1 :
+ case mso_sptCallout90 :
+ case mso_sptBorderCallout90 :
+ default:
+ break;
+
+ case mso_sptAccentCallout1 :
+ case mso_sptAccentBorderCallout1 :
+ case mso_sptAccentCallout90 :
+ case mso_sptAccentBorderCallout90 :
+ {
+ sal_uInt32 nLine = 0;
+
+ for ( const std::pair< SdrPathObjUniquePtr, double >& rCandidate : vObjectList )
+ {
+ SdrPathObj* pObj(rCandidate.first.get());
+
+ if(pObj->IsLine())
+ {
+ nLine++;
+ if ( nLine == nLineObjectCount )
+ {
+ pObj->ClearMergedItem( XATTR_LINESTART );
+ pObj->ClearMergedItem( XATTR_LINEEND );
+ }
+ }
+ }
+ }
+ break;
+
+ // switch start & end
+ case mso_sptAccentCallout2 :
+ case mso_sptAccentBorderCallout2 :
+ bAccent = true;
+ [[fallthrough]];
+ case mso_sptCallout2 :
+ case mso_sptBorderCallout2 :
+ {
+ sal_uInt32 nLine = 0;
+
+ for ( const std::pair< SdrPathObjUniquePtr, double >& rCandidate : vObjectList )
+ {
+ SdrPathObj* pObj(rCandidate.first.get());
+
+ if(pObj->IsLine())
+ {
+ nLine++;
+ if ( nLine == 1 )
+ pObj->ClearMergedItem( XATTR_LINEEND );
+ else if ( ( bAccent && ( nLine == nLineObjectCount - 1 ) ) || ( !bAccent && ( nLine == nLineObjectCount ) ) )
+ pObj->ClearMergedItem( XATTR_LINESTART );
+ else
+ {
+ pObj->ClearMergedItem( XATTR_LINESTART );
+ pObj->ClearMergedItem( XATTR_LINEEND );
+ }
+ }
+ }
+ }
+ break;
+
+ case mso_sptAccentCallout3 :
+ case mso_sptAccentBorderCallout3 :
+ case mso_sptCallout3 :
+ case mso_sptBorderCallout3 :
+ {
+ sal_uInt32 nLine = 0;
+
+ for ( const std::pair< SdrPathObjUniquePtr, double >& rCandidate : vObjectList )
+ {
+ SdrPathObj* pObj(rCandidate.first.get());
+
+ if(pObj->IsLine())
+ {
+ if ( nLine )
+ {
+ pObj->ClearMergedItem( XATTR_LINESTART );
+ pObj->ClearMergedItem( XATTR_LINEEND );
+ }
+ else
+ EnhancedCustomShape2d::SwapStartAndEndArrow( pObj );
+
+ nLine++;
+ }
+ }
+ }
+ break;
+ }
+}
+
+void EnhancedCustomShape2d::AdaptObjColor(
+ SdrPathObj& rObj,
+ double dBrightness,
+ const SfxItemSet& rCustomShapeSet,
+ sal_uInt32& nColorIndex,
+ sal_uInt32 nColorCount)
+{
+ if ( rObj.IsLine() )
+ return;
+
+ const drawing::FillStyle eFillStyle = rObj.GetMergedItem(XATTR_FILLSTYLE).GetValue();
+ if (eFillStyle == drawing::FillStyle_NONE)
+ return;
+
+ switch( eFillStyle )
+ {
+ default:
+ case drawing::FillStyle_SOLID:
+ {
+ if ( nColorCount || 0.0 != dBrightness )
+ {
+ Color aFillColor = GetColorData(
+ rCustomShapeSet.Get( XATTR_FILLCOLOR ).GetColorValue(),
+ std::min(nColorIndex, nColorCount-1),
+ dBrightness );
+ rObj.SetMergedItem( XFillColorItem( "", aFillColor ) );
+ }
+ break;
+ }
+ case drawing::FillStyle_GRADIENT:
+ {
+ XGradient aXGradient(rObj.GetMergedItem(XATTR_FILLGRADIENT).GetGradientValue());
+
+ if ( nColorCount || 0.0 != dBrightness )
+ {
+ aXGradient.SetStartColor(
+ GetColorData(
+ aXGradient.GetStartColor(),
+ std::min(nColorIndex, nColorCount-1),
+ dBrightness ));
+ aXGradient.SetEndColor(
+ GetColorData(
+ aXGradient.GetEndColor(),
+ std::min(nColorIndex, nColorCount-1),
+ dBrightness ));
+ }
+
+ rObj.SetMergedItem( XFillGradientItem( "", aXGradient ) );
+ break;
+ }
+ case drawing::FillStyle_HATCH:
+ {
+ XHatch aXHatch(rObj.GetMergedItem(XATTR_FILLHATCH).GetHatchValue());
+
+ if ( nColorCount || 0.0 != dBrightness )
+ {
+ aXHatch.SetColor(
+ GetColorData(
+ aXHatch.GetColor(),
+ std::min(nColorIndex, nColorCount-1),
+ dBrightness ));
+ }
+
+ rObj.SetMergedItem( XFillHatchItem( "", aXHatch ) );
+ break;
+ }
+ case drawing::FillStyle_BITMAP:
+ {
+ if ( nColorCount || 0.0 != dBrightness )
+ {
+ BitmapEx aBitmap(rObj.GetMergedItem(XATTR_FILLBITMAP).GetGraphicObject().GetGraphic().GetBitmapEx());
+
+ short nLuminancePercent = static_cast< short > ( GetLuminanceChange(
+ std::min(nColorIndex, nColorCount-1)));
+ aBitmap.Adjust( nLuminancePercent, 0, 0, 0, 0 );
+
+ rObj.SetMergedItem(XFillBitmapItem(OUString(), Graphic(aBitmap)));
+ }
+
+ break;
+ }
+ }
+
+ if ( nColorIndex < nColorCount )
+ nColorIndex++;
+}
+
+SdrObjectUniquePtr EnhancedCustomShape2d::CreatePathObj( bool bLineGeometryNeededOnly )
+{
+ if ( !seqCoordinates.hasElements() )
+ {
+ return nullptr;
+ }
+
+ std::vector< std::pair< SdrPathObjUniquePtr, double > > vObjectList;
+ const bool bSortFilledObjectsToBack(SortFilledObjectsToBackByDefault(eSpType));
+ sal_Int32 nSubPathIndex(0);
+ sal_Int32 nSrcPt(0);
+ sal_Int32 nSegmentInd(0);
+ SdrObjectUniquePtr pRet;
+
+ while( nSegmentInd <= seqSegments.getLength() )
+ {
+ CreateSubPath(
+ nSrcPt,
+ nSegmentInd,
+ vObjectList,
+ bLineGeometryNeededOnly,
+ bSortFilledObjectsToBack,
+ nSubPathIndex);
+ nSubPathIndex++;
+ }
+
+ if ( !vObjectList.empty() )
+ {
+ const SfxItemSet& rCustomShapeSet(mrSdrObjCustomShape.GetMergedItemSet());
+ const sal_uInt32 nColorCount(nColorData >> 28);
+ sal_uInt32 nColorIndex(0);
+
+ // #i37011# remove invisible objects
+ std::vector< std::pair< SdrPathObjUniquePtr, double> > vNewList;
+
+ for ( std::pair< SdrPathObjUniquePtr, double >& rCandidate : vObjectList )
+ {
+ SdrPathObj* pObj(rCandidate.first.get());
+ const drawing::LineStyle eLineStyle(pObj->GetMergedItem(XATTR_LINESTYLE).GetValue());
+ const drawing::FillStyle eFillStyle(pObj->GetMergedItem(XATTR_FILLSTYLE).GetValue());
+ const auto pText = pObj->getActiveText();
+
+ // #i40600# if bLineGeometryNeededOnly is set, linestyle does not matter
+ if(pText || bLineGeometryNeededOnly || (drawing::LineStyle_NONE != eLineStyle) || (drawing::FillStyle_NONE != eFillStyle))
+ vNewList.push_back(std::move(rCandidate));
+ }
+
+ vObjectList = std::move(vNewList);
+
+ if(1 == vObjectList.size())
+ {
+ // a single object, correct some values
+ AdaptObjColor(
+ *vObjectList.begin()->first,
+ vObjectList.begin()->second,
+ rCustomShapeSet,
+ nColorIndex,
+ nColorCount);
+ }
+ else
+ {
+ sal_Int32 nLineObjectCount(0);
+
+ // correct some values and collect content data
+ for ( const std::pair< SdrPathObjUniquePtr, double >& rCandidate : vObjectList )
+ {
+ SdrPathObj* pObj(rCandidate.first.get());
+
+ if(pObj->IsLine())
+ {
+ nLineObjectCount++;
+ }
+ else
+ {
+ AdaptObjColor(
+ *pObj,
+ rCandidate.second,
+ rCustomShapeSet,
+ nColorIndex,
+ nColorCount);
+
+ // OperationSmiley: when we have access to the SdrObjCustomShape and the
+ // CustomShape is built with more than a single filled Geometry, use it
+ // to define that all helper geometries defined here (SdrObjects currently)
+ // will use the same FillGeometryDefinition (from the referenced SdrObjCustomShape).
+ // This will all same-filled objects look like filled smoothly with the same style.
+ pObj->setFillGeometryDefiningShape(&mrSdrObjCustomShape);
+ }
+ }
+
+ // #i88870# correct line arrows for callouts
+ if ( nLineObjectCount )
+ {
+ CorrectCalloutArrows(
+ eSpType,
+ nLineObjectCount,
+ vObjectList);
+ }
+
+ // sort objects so that filled ones are in front. Necessary
+ // for some strange objects
+ if(bSortFilledObjectsToBack)
+ {
+ std::vector< std::pair< SdrPathObjUniquePtr, double> > vTempList;
+ vTempList.reserve(vObjectList.size());
+
+ for ( std::pair< SdrPathObjUniquePtr, double >& rCandidate : vObjectList )
+ {
+ SdrPathObj* pObj(rCandidate.first.get());
+ if ( !pObj->IsLine() )
+ vTempList.push_back(std::move(rCandidate));
+ }
+
+ for ( std::pair< SdrPathObjUniquePtr, double >& rCandidate : vObjectList )
+ {
+ if ( rCandidate.first )
+ vTempList.push_back(std::move(rCandidate));
+ }
+
+ vObjectList = std::move(vTempList);
+ }
+ }
+ }
+
+ // #i37011#
+ if(!vObjectList.empty())
+ {
+ // copy remaining objects to pRet
+ if(vObjectList.size() > 1)
+ {
+ pRet.reset(new SdrObjGroup(mrSdrObjCustomShape.getSdrModelFromSdrObject()));
+
+ for ( std::pair< SdrPathObjUniquePtr, double >& rCandidate : vObjectList )
+ {
+ pRet->GetSubList()->NbcInsertObject(rCandidate.first.release());
+ }
+ }
+ else if(1 == vObjectList.size())
+ {
+ pRet.reset(vObjectList.begin()->first.release());
+ }
+
+ if(pRet)
+ {
+ // move to target position
+ tools::Rectangle aCurRect(pRet->GetSnapRect());
+ aCurRect.Move(aLogicRect.Left(), aLogicRect.Top());
+ pRet->NbcSetSnapRect(aCurRect);
+ }
+ }
+
+ return pRet;
+}
+
+SdrObjectUniquePtr EnhancedCustomShape2d::CreateObject( bool bLineGeometryNeededOnly )
+{
+ SdrObjectUniquePtr pRet;
+
+ if ( eSpType == mso_sptRectangle )
+ {
+ pRet.reset(new SdrRectObj(mrSdrObjCustomShape.getSdrModelFromSdrObject(), aLogicRect));
+ pRet->SetMergedItemSet( *this );
+ }
+ if ( !pRet )
+ pRet = CreatePathObj( bLineGeometryNeededOnly );
+
+ return pRet;
+}
+
+void EnhancedCustomShape2d::ApplyGluePoints( SdrObject* pObj )
+{
+ if ( !pObj )
+ return;
+
+ for ( const auto& rGluePoint : std::as_const(seqGluePoints) )
+ {
+ SdrGluePoint aGluePoint;
+
+ aGluePoint.SetPos( GetPoint( rGluePoint, true, true ) );
+ aGluePoint.SetPercent( false );
+ aGluePoint.SetAlign( SdrAlign::VERT_TOP | SdrAlign::HORZ_LEFT );
+ aGluePoint.SetEscDir( SdrEscapeDirection::SMART );
+ SdrGluePointList* pList = pObj->ForceGluePointList();
+ if( pList )
+ /* sal_uInt16 nId = */ pList->Insert( aGluePoint );
+ }
+}
+
+SdrObjectUniquePtr EnhancedCustomShape2d::CreateLineGeometry()
+{
+ return CreateObject( true );
+}
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/customshapes/EnhancedCustomShape3d.cxx b/svx/source/customshapes/EnhancedCustomShape3d.cxx
new file mode 100644
index 000000000..00a59ebf5
--- /dev/null
+++ b/svx/source/customshapes/EnhancedCustomShape3d.cxx
@@ -0,0 +1,1037 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include "EnhancedCustomShape3d.hxx"
+#include <o3tl/unit_conversion.hxx>
+#include <svx/deflt3d.hxx>
+#include <svx/svdmodel.hxx>
+#include <tools/poly.hxx>
+#include <svx/svditer.hxx>
+#include <svx/svdobj.hxx>
+#include <svx/svdoashp.hxx>
+#include <svl/itemset.hxx>
+#include <svx/xfillit0.hxx>
+#include <svx/xlineit0.hxx>
+#include <svx/xsflclit.hxx>
+#include <svx/xbtmpit.hxx>
+#include <svx/xflclit.hxx>
+#include <svx/svdopath.hxx>
+#include <svx/svddef.hxx>
+#include <svx/svx3ditems.hxx>
+#include <extrud3d.hxx>
+#include <svx/xflbmtit.hxx>
+#include <svx/xlnclit.hxx>
+#include <svx/sdasitm.hxx>
+#include <svx/scene3d.hxx>
+#include <com/sun/star/drawing/Position3D.hpp>
+#include <com/sun/star/drawing/Direction3D.hpp>
+#include <com/sun/star/drawing/NormalsKind.hpp>
+#include <com/sun/star/drawing/ShadeMode.hpp>
+#include <svx/sdr/properties/properties.hxx>
+#include <com/sun/star/drawing/EnhancedCustomShapeParameterPair.hpp>
+#include <com/sun/star/drawing/EnhancedCustomShapeMetalType.hpp>
+#include <com/sun/star/drawing/ProjectionMode.hpp>
+#include <basegfx/polygon/b2dpolypolygontools.hxx>
+#include <basegfx/polygon/b3dpolygon.hxx>
+#include <basegfx/range/b2drange.hxx>
+#include <sdr/primitive2d/sdrattributecreator.hxx>
+#include <drawinglayer/attribute/sdrlineattribute.hxx>
+#include <drawinglayer/attribute/sdrlinestartendattribute.hxx>
+#include <svx/xlnwtit.hxx>
+#include <svx/xlntrit.hxx>
+#include <svx/xfltrit.hxx>
+#include <basegfx/color/bcolor.hxx>
+
+using namespace com::sun::star;
+using namespace com::sun::star::uno;
+
+namespace {
+
+void GetOrigin( const SdrCustomShapeGeometryItem& rItem, double& rOriginX, double& rOriginY )
+{
+ css::drawing::EnhancedCustomShapeParameterPair aOriginParaPair;
+ const Any* pAny = rItem.GetPropertyValueByName( "Extrusion", "Origin" );
+ if ( ! ( pAny && ( *pAny >>= aOriginParaPair ) && ( aOriginParaPair.First.Value >>= rOriginX ) && ( aOriginParaPair.Second.Value >>= rOriginY ) ) )
+ {
+ rOriginX = 0.50;
+ rOriginY =-0.50;
+ }
+}
+
+void GetRotateAngle( const SdrCustomShapeGeometryItem& rItem, double& rAngleX, double& rAngleY )
+{
+ css::drawing::EnhancedCustomShapeParameterPair aRotateAngleParaPair;
+ const Any* pAny = rItem.GetPropertyValueByName( "Extrusion", "RotateAngle" );
+ if ( ! ( pAny && ( *pAny >>= aRotateAngleParaPair ) && ( aRotateAngleParaPair.First.Value >>= rAngleX ) && ( aRotateAngleParaPair.Second.Value >>= rAngleY ) ) )
+ {
+ rAngleX = 0.0;
+ rAngleY = 0.0;
+ }
+ rAngleX = basegfx::deg2rad(rAngleX);
+ rAngleY = basegfx::deg2rad(rAngleY);
+}
+
+void GetSkew( const SdrCustomShapeGeometryItem& rItem, double& rSkewAmount, double& rSkewAngle )
+{
+ css::drawing::EnhancedCustomShapeParameterPair aSkewParaPair;
+ const Any* pAny = rItem.GetPropertyValueByName( "Extrusion", "Skew" );
+ if ( ! ( pAny && ( *pAny >>= aSkewParaPair ) && ( aSkewParaPair.First.Value >>= rSkewAmount ) && ( aSkewParaPair.Second.Value >>= rSkewAngle ) ) )
+ {
+ rSkewAmount = 50;
+ // ODF default is 45, but older ODF documents expect -135 as default. For intermediate
+ // solution see tdf#141301 and tdf#141127.
+ // MS Office default -135 is set in msdffimp.cxx to make import independent from setting here.
+ rSkewAngle = -135;
+ }
+ rSkewAngle = basegfx::deg2rad(rSkewAngle);
+}
+
+void GetExtrusionDepth( const SdrCustomShapeGeometryItem& rItem, const double* pMap, double& rBackwardDepth, double& rForwardDepth )
+{
+ css::drawing::EnhancedCustomShapeParameterPair aDepthParaPair;
+ double fDepth = 0, fFraction = 0;
+ const Any* pAny = rItem.GetPropertyValueByName( "Extrusion", "Depth" );
+ if ( pAny && ( *pAny >>= aDepthParaPair ) && ( aDepthParaPair.First.Value >>= fDepth ) && ( aDepthParaPair.Second.Value >>= fFraction ) )
+ {
+ rForwardDepth = fDepth * fFraction;
+ rBackwardDepth = fDepth - rForwardDepth;
+ }
+ else
+ {
+ rBackwardDepth = 1270;
+ rForwardDepth = 0;
+ }
+ if ( pMap )
+ {
+ double fMap = *pMap;
+ rBackwardDepth *= fMap;
+ rForwardDepth *= fMap;
+ }
+}
+
+double GetDouble( const SdrCustomShapeGeometryItem& rItem, const OUString& rPropertyName, double fDefault )
+{
+ double fRetValue = fDefault;
+ const Any* pAny = rItem.GetPropertyValueByName( "Extrusion", rPropertyName );
+ if ( pAny )
+ *pAny >>= fRetValue;
+ return fRetValue;
+}
+
+drawing::ShadeMode GetShadeMode( const SdrCustomShapeGeometryItem& rItem, const drawing::ShadeMode eDefault )
+{
+ drawing::ShadeMode eRet( eDefault );
+ const Any* pAny = rItem.GetPropertyValueByName( "Extrusion", "ShadeMode" );
+ if ( pAny )
+ *pAny >>= eRet;
+ return eRet;
+}
+
+bool GetBool( const SdrCustomShapeGeometryItem& rItem, const OUString& rPropertyName, const bool bDefault )
+{
+ bool bRetValue = bDefault;
+ const Any* pAny = rItem.GetPropertyValueByName( "Extrusion", rPropertyName );
+ if ( pAny )
+ *pAny >>= bRetValue;
+ return bRetValue;
+}
+
+drawing::Position3D GetPosition3D( const SdrCustomShapeGeometryItem& rItem, const OUString& rPropertyName,
+ const drawing::Position3D& rDefault, const double* pMap )
+{
+ drawing::Position3D aRetValue( rDefault );
+ const Any* pAny = rItem.GetPropertyValueByName( "Extrusion", rPropertyName );
+ if ( pAny )
+ *pAny >>= aRetValue;
+ if ( pMap )
+ {
+ aRetValue.PositionX *= *pMap;
+ aRetValue.PositionY *= *pMap;
+ aRetValue.PositionZ *= *pMap;
+ }
+ return aRetValue;
+}
+
+drawing::Direction3D GetDirection3D( const SdrCustomShapeGeometryItem& rItem, const OUString& rPropertyName, const drawing::Direction3D& rDefault )
+{
+ drawing::Direction3D aRetValue( rDefault );
+ const Any* pAny = rItem.GetPropertyValueByName( "Extrusion", rPropertyName );
+ if ( pAny )
+ *pAny >>= aRetValue;
+ return aRetValue;
+}
+
+sal_Int16 GetMetalType(const SdrCustomShapeGeometryItem& rItem, const sal_Int16 eDefault)
+{
+ sal_Int16 aRetValue(eDefault);
+ const Any* pAny = rItem.GetPropertyValueByName("Extrusion", "MetalType");
+ if (pAny)
+ *pAny >>= aRetValue;
+ return aRetValue;
+}
+
+// Calculates the light directions for the additional lights, which are used to emulate soft
+// lights of MS Office. Method needs to be documented in the Wiki
+// https://wiki.documentfoundation.org/Development/ODF_Implementer_Notes in part
+// List_of_LibreOffice_ODF_implementation-defined_items
+// The method expects vector rLight to be normalized and results normalized vectors.
+void lcl_SoftLightsDirection(const basegfx::B3DVector& rLight, basegfx::B3DVector& rSoftUp,
+ basegfx::B3DVector& rSoftDown, basegfx::B3DVector& rSoftRight,
+ basegfx::B3DVector& rSoftLeft)
+{
+ constexpr double fAngle = basegfx::deg2rad(60); // angle between regular light and soft light
+
+ // We first create directions around (0|0|1) and then rotate them to the light position.
+ rSoftUp = basegfx::B3DVector(0.0, sin(fAngle), cos(fAngle));
+ rSoftDown = basegfx::B3DVector(0.0, -sin(fAngle), cos(fAngle));
+ rSoftRight = basegfx::B3DVector(sin(fAngle), 0.0, cos(fAngle));
+ rSoftLeft = basegfx::B3DVector(-sin(fAngle), 0.0, cos(fAngle));
+
+ basegfx::B3DHomMatrix aRotateMat;
+ aRotateMat.rotate(0.0, 0.0, M_PI_4);
+ if (rLight.getX() == 0.0 && rLight.getZ() == 0.0)
+ {
+ // Special case with light from top or bottom
+ if (rLight.getY() >= 0.0)
+ aRotateMat.rotate(-M_PI_2, 0.0, 0.0);
+ else
+ aRotateMat.rotate(M_PI_2, 0.0, 0.0);
+ }
+ else
+ {
+ // Azimuth from z-axis to x-axis. (0|0|1) to (1|0|0) is 90deg.
+ double fAzimuth = atan2(rLight.getX(), rLight.getZ());
+ // Elevation from xz-plane to y-axis. (0|0|1) to (0|1|0) is 90deg.
+ double fElevation = atan2(rLight.getY(), std::hypot(rLight.getX(), rLight.getZ()));
+ aRotateMat.rotate(-fElevation, fAzimuth, 0.0);
+ }
+
+ rSoftUp = aRotateMat * rSoftUp;
+ rSoftDown = aRotateMat * rSoftDown;
+ rSoftRight = aRotateMat * rSoftRight;
+ rSoftLeft = aRotateMat * rSoftLeft;
+}
+}
+
+SdrObject* EnhancedCustomShape3d::Create3DObject(
+ const SdrObject* pShape2d,
+ const SdrObjCustomShape& rSdrObjCustomShape)
+{
+ SdrObject* pRet(nullptr);
+ const SdrCustomShapeGeometryItem& rGeometryItem(rSdrObjCustomShape.GetMergedItem(SDRATTR_CUSTOMSHAPE_GEOMETRY));
+ double fMap(1.0), *pMap = nullptr;
+ Fraction aFraction( rSdrObjCustomShape.getSdrModelFromSdrObject().GetScaleFraction() );
+
+ if ( aFraction.GetNumerator() != 1 || aFraction.GetDenominator() != 1 )
+ {
+ fMap *= double(aFraction);
+ pMap = &fMap;
+ }
+
+ if ( rSdrObjCustomShape.getSdrModelFromSdrObject().GetScaleUnit() != MapUnit::Map100thMM )
+ {
+ DBG_ASSERT( rSdrObjCustomShape.getSdrModelFromSdrObject().GetScaleUnit() == MapUnit::MapTwip, "EnhancedCustomShape3d::Current MapMode is Unsupported" );
+ // But we could use MapToO3tlUnit from <tools/UnitConversion> ... ?
+ fMap *= o3tl::convert(1.0, o3tl::Length::mm100, o3tl::Length::twip);
+ pMap = &fMap;
+ }
+
+ if ( GetBool( rGeometryItem, "Extrusion", false ) )
+ {
+ bool bIsMirroredX(rSdrObjCustomShape.IsMirroredX());
+ bool bIsMirroredY(rSdrObjCustomShape.IsMirroredY());
+ tools::Rectangle aSnapRect(rSdrObjCustomShape.GetLogicRect());
+ Degree100 nObjectRotation(rSdrObjCustomShape.GetRotateAngle());
+ if ( nObjectRotation )
+ {
+ double a = toRadians(36000_deg100 - nObjectRotation);
+ tools::Long dx = aSnapRect.Right() - aSnapRect.Left();
+ tools::Long dy = aSnapRect.Bottom()- aSnapRect.Top();
+ Point aP( aSnapRect.TopLeft() );
+ RotatePoint( aP, rSdrObjCustomShape.GetSnapRect().Center(), sin( a ), cos( a ) );
+ aSnapRect.SetLeft( aP.X() );
+ aSnapRect.SetTop( aP.Y() );
+ aSnapRect.SetRight( aSnapRect.Left() + dx );
+ aSnapRect.SetBottom( aSnapRect.Top() + dy );
+ }
+ Point aCenter( aSnapRect.Center() );
+
+ SfxItemSet aSet( rSdrObjCustomShape.GetMergedItemSet() );
+
+ //SJ: vertical writing is not required, by removing this item no outliner is created
+ aSet.ClearItem( SDRATTR_TEXTDIRECTION );
+
+ // #i105323# For 3D AutoShapes, the shadow attribute has to be applied to each
+ // created visualisation helper model shape individually. The shadow itself
+ // will then be rendered from the 3D renderer correctly for the whole 3D scene
+ // (and thus behind all objects of which the visualisation may be built). So,
+ // do NOT remove it from the ItemSet here.
+ // aSet.ClearItem(SDRATTR_SHADOW);
+
+ std::vector< E3dCompoundObject* > aPlaceholderObjectList;
+
+ double fExtrusionBackward, fExtrusionForward;
+ GetExtrusionDepth( rGeometryItem, pMap, fExtrusionBackward, fExtrusionForward );
+ double fDepth = fExtrusionBackward + fExtrusionForward;
+ if ( fDepth < 1.0 )
+ fDepth = 1.0;
+
+ drawing::ProjectionMode eProjectionMode( drawing::ProjectionMode_PARALLEL );
+ const Any* pAny = rGeometryItem.GetPropertyValueByName( "Extrusion", "ProjectionMode" );
+ if ( pAny )
+ *pAny >>= eProjectionMode;
+ // pShape2d Convert in scenes which include 3D Objects
+ E3dDefaultAttributes a3DDefaultAttr;
+ a3DDefaultAttr.SetDefaultLatheCharacterMode( true );
+ a3DDefaultAttr.SetDefaultExtrudeCharacterMode( true );
+
+ E3dScene* pScene = new E3dScene(rSdrObjCustomShape.getSdrModelFromSdrObject());
+
+ bool bSceneHasObjects ( false );
+ bool bUseTwoFillStyles( false );
+
+ drawing::ShadeMode eShadeMode( GetShadeMode( rGeometryItem, drawing::ShadeMode_FLAT ) );
+ bool bUseExtrusionColor = GetBool( rGeometryItem, "Color", false );
+
+ drawing::FillStyle eFillStyle( aSet.Get(XATTR_FILLSTYLE).GetValue() );
+ pScene->GetProperties().SetObjectItem( Svx3DShadeModeItem(static_cast<sal_uInt16>(eShadeMode)));
+ aSet.Put( makeSvx3DPercentDiagonalItem( 0 ) );
+ aSet.Put( Svx3DTextureModeItem( 1 ) );
+ // SPECIFIC needed for ShadeMode_SMOOTH and ShadeMode_PHONG, otherwise FLAT is faster.
+ if (eShadeMode == drawing::ShadeMode_SMOOTH || eShadeMode == drawing::ShadeMode_PHONG)
+ aSet.Put( Svx3DNormalsKindItem(static_cast<sal_uInt16>(drawing::NormalsKind_SPECIFIC)));
+ else
+ aSet.Put( Svx3DNormalsKindItem(static_cast<sal_uInt16>(drawing::NormalsKind_FLAT)));
+
+ if ( eShadeMode == drawing::ShadeMode_DRAFT )
+ {
+ aSet.Put( XLineStyleItem( drawing::LineStyle_SOLID ) );
+ aSet.Put( XFillStyleItem ( drawing::FillStyle_NONE ) );
+ aSet.Put( makeSvx3DDoubleSidedItem( true ) );
+ }
+ else
+ {
+ aSet.Put( XLineStyleItem( drawing::LineStyle_NONE ) );
+ if ( eFillStyle == drawing::FillStyle_NONE )
+ aSet.Put( XFillStyleItem( drawing::FillStyle_SOLID ) );
+ else if ( ( eFillStyle == drawing::FillStyle_BITMAP ) || ( eFillStyle == drawing::FillStyle_GRADIENT ) || bUseExtrusionColor )
+ bUseTwoFillStyles = true;
+
+ // If shapes are mirrored once (mirroring two times correct geometry again)
+ // double-sided at the object and two-sided-lighting at the scene need to be set.
+
+ // #i122777# Also use double sided for two fill styles since there several 3d objects get
+ // created with a depth of 0; one of them is the backside which needs double-sided to
+ // get visible
+ if(bUseTwoFillStyles || (bIsMirroredX && !bIsMirroredY) || (!bIsMirroredX && bIsMirroredY))
+ {
+ aSet.Put( makeSvx3DDoubleSidedItem( true ) );
+ pScene->GetProperties().SetObjectItem( makeSvx3DTwoSidedLightingItem( true ) );
+ }
+ }
+
+ tools::Rectangle aBoundRect2d;
+ basegfx::B2DPolyPolygon aTotalPolyPoly;
+ SdrObjListIter aIter( *pShape2d, SdrIterMode::DeepNoGroups );
+ const bool bMultipleSubObjects(aIter.Count() > 1);
+
+ while( aIter.IsMore() )
+ {
+ const SdrObject* pNext = aIter.Next();
+ bool bIsPlaceholderObject = (pNext->GetMergedItem( XATTR_FILLSTYLE ).GetValue() == drawing::FillStyle_NONE )
+ && (pNext->GetMergedItem( XATTR_LINESTYLE ).GetValue() == drawing::LineStyle_NONE );
+ basegfx::B2DPolyPolygon aPolyPoly;
+ SfxItemSet aLocalSet(aSet);
+ drawing::FillStyle aLocalFillStyle(eFillStyle);
+
+ if ( auto pPathObj = dynamic_cast<const SdrPathObj*>(pNext) )
+ {
+ const SfxItemSet& rSet = pNext->GetMergedItemSet();
+ bool bNeedToConvertToContour(false);
+
+ // do conversion only for single line objects; for all others a fill and a
+ // line object get created. When we have fill, we want no line. That line has
+ // always been there, but since it was never converted to contour, it kept
+ // invisible (all this 'hidden' logic should be migrated to primitives).
+ if(!bMultipleSubObjects)
+ {
+ const drawing::FillStyle eStyle(rSet.Get(XATTR_FILLSTYLE).GetValue());
+
+ if(drawing::FillStyle_NONE == eStyle)
+ {
+ const drawinglayer::attribute::SdrLineAttribute aLine(
+ drawinglayer::primitive2d::createNewSdrLineAttribute(rSet));
+
+ bNeedToConvertToContour = (0.0 < aLine.getWidth() || 0.0 != aLine.getFullDotDashLen());
+
+ if(!bNeedToConvertToContour && !aLine.isDefault())
+ {
+ const drawinglayer::attribute::SdrLineStartEndAttribute aLineStartEnd(
+ drawinglayer::primitive2d::createNewSdrLineStartEndAttribute(rSet, aLine.getWidth()));
+
+ if((aLineStartEnd.getStartWidth() && aLineStartEnd.isStartActive())
+ || (aLineStartEnd.getEndWidth() && aLineStartEnd.isEndActive()))
+ {
+ bNeedToConvertToContour = true;
+ }
+ }
+ }
+ }
+
+ if(bNeedToConvertToContour)
+ {
+ SdrObject* pNewObj = pNext->ConvertToContourObj(const_cast< SdrObject* >(pNext));
+ SdrPathObj* pNewPathObj = dynamic_cast< SdrPathObj* >(pNewObj);
+
+ if(pNewPathObj)
+ {
+ aPolyPoly = pNewPathObj->GetPathPoly();
+
+ if(aPolyPoly.isClosed())
+ {
+ // correct item properties from line to fill style
+ if(eShadeMode == drawing::ShadeMode_DRAFT)
+ {
+ // for draft, create wireframe with fixed line width
+ aLocalSet.Put(XLineStyleItem(drawing::LineStyle_SOLID));
+ aLocalSet.Put(XLineWidthItem(40));
+ aLocalFillStyle = drawing::FillStyle_NONE;
+ }
+ else
+ {
+ // switch from line to fill, copy line attr to fill attr (color, transparence)
+ aLocalSet.Put(XLineWidthItem(0));
+ aLocalSet.Put(XLineStyleItem(drawing::LineStyle_NONE));
+ aLocalSet.Put(XFillColorItem(OUString(), aLocalSet.Get(XATTR_LINECOLOR).GetColorValue()));
+ aLocalSet.Put(XFillStyleItem(drawing::FillStyle_SOLID));
+ aLocalSet.Put(XFillTransparenceItem(aLocalSet.Get(XATTR_LINETRANSPARENCE).GetValue()));
+ aLocalFillStyle = drawing::FillStyle_SOLID;
+ }
+ }
+ else
+ {
+ // correct item properties to hairlines
+ aLocalSet.Put(XLineWidthItem(0));
+ aLocalSet.Put(XLineStyleItem(drawing::LineStyle_SOLID));
+ }
+ }
+
+ SdrObject::Free(pNewObj);
+ }
+ else
+ {
+ aPolyPoly = pPathObj->GetPathPoly();
+ }
+ }
+ else
+ {
+ SdrObjectUniquePtr pNewObj = pNext->ConvertToPolyObj( false, false );
+ SdrPathObj* pPath = dynamic_cast<SdrPathObj*>( pNewObj.get() );
+ if ( pPath )
+ aPolyPoly = pPath->GetPathPoly();
+ }
+
+ if( aPolyPoly.count() )
+ {
+ if(aPolyPoly.areControlPointsUsed())
+ {
+ aPolyPoly = basegfx::utils::adaptiveSubdivideByAngle(aPolyPoly);
+ }
+
+ const basegfx::B2DRange aTempRange(basegfx::utils::getRange(aPolyPoly));
+ const tools::Rectangle aBoundRect(basegfx::fround(aTempRange.getMinX()), basegfx::fround(aTempRange.getMinY()), basegfx::fround(aTempRange.getMaxX()), basegfx::fround(aTempRange.getMaxY()));
+ aTotalPolyPoly.append(aPolyPoly);
+ aBoundRect2d.Union( aBoundRect );
+
+ // #i122777# depth 0 is okay for planes when using double-sided
+ E3dCompoundObject* p3DObj = new E3dExtrudeObj(
+ rSdrObjCustomShape.getSdrModelFromSdrObject(),
+ a3DDefaultAttr,
+ aPolyPoly,
+ bUseTwoFillStyles ? 0 : fDepth );
+
+ p3DObj->NbcSetLayer( pShape2d->GetLayer() );
+ p3DObj->SetMergedItemSet( aLocalSet );
+
+ if ( bIsPlaceholderObject )
+ aPlaceholderObjectList.push_back( p3DObj );
+ else if ( bUseTwoFillStyles )
+ {
+ BitmapEx aFillBmp;
+ bool bFillBmpTile = p3DObj->GetMergedItem( XATTR_FILLBMP_TILE ).GetValue();
+ if ( bFillBmpTile )
+ {
+ const XFillBitmapItem& rBmpItm = p3DObj->GetMergedItem(XATTR_FILLBITMAP);
+ aFillBmp = rBmpItm.GetGraphicObject().GetGraphic().GetBitmapEx();
+
+ // #i122777# old adaptation of FillStyle bitmap size to 5-times the original size; this is not needed
+ // anymore and was used in old times to male the fill look better when converting to 3D. Removed
+ // from regular 3D objects for some time, also needs to be removed from CustomShapes
+
+ //Size aLogicalSize = aFillBmp.GetPrefSize();
+ //if ( aFillBmp.GetPrefMapMode() == MapUnit::MapPixel )
+ // aLogicalSize = Application::GetDefaultDevice()->PixelToLogic( aLogicalSize, MapUnit::Map100thMM );
+ //else
+ // aLogicalSize = OutputDevice::LogicToLogic( aLogicalSize, aFillBmp.GetPrefMapMode(), MapUnit::Map100thMM );
+ //aLogicalSize.Width() *= 5; ;// :-( nice scaling, look at engine3d/obj3d.cxx
+ //aLogicalSize.Height() *= 5;
+ //aFillBmp.SetPrefSize( aLogicalSize );
+ //aFillBmp.SetPrefMapMode( MapUnit::Map100thMM );
+ //p3DObj->SetMergedItem(XFillBitmapItem(String(), Graphic(aFillBmp)));
+ }
+ else
+ {
+ if ( aSnapRect != aBoundRect && aSnapRect.GetWidth() > 0 && aSnapRect.GetHeight() > 0)
+ {
+ const XFillBitmapItem& rBmpItm = p3DObj->GetMergedItem(XATTR_FILLBITMAP);
+ aFillBmp = rBmpItm.GetGraphicObject().GetGraphic().GetBitmapEx();
+ Size aBmpSize( aFillBmp.GetSizePixel() );
+ double fXScale = static_cast<double>(aBoundRect.GetWidth()) / static_cast<double>(aSnapRect.GetWidth());
+ double fYScale = static_cast<double>(aBoundRect.GetHeight()) / static_cast<double>(aSnapRect.GetHeight());
+
+ Point aPt( static_cast<sal_Int32>( static_cast<double>( aBoundRect.Left() - aSnapRect.Left() )* static_cast<double>(aBmpSize.Width()) / static_cast<double>(aSnapRect.GetWidth()) ),
+ static_cast<sal_Int32>( static_cast<double>( aBoundRect.Top() - aSnapRect.Top() ) * static_cast<double>(aBmpSize.Height()) / static_cast<double>(aSnapRect.GetHeight()) ) );
+ Size aSize( static_cast<sal_Int32>( aBmpSize.Width() * fXScale ),
+ static_cast<sal_Int32>( aBmpSize.Height() * fYScale ) );
+ tools::Rectangle aCropRect( aPt, aSize );
+ aFillBmp.Crop( aCropRect );
+ p3DObj->SetMergedItem(XFillBitmapItem(OUString(), Graphic(aFillBmp)));
+ }
+ }
+ pScene->InsertObject( p3DObj );
+ p3DObj = new E3dExtrudeObj(
+ rSdrObjCustomShape.getSdrModelFromSdrObject(),
+ a3DDefaultAttr,
+ aPolyPoly,
+ fDepth);
+ p3DObj->NbcSetLayer( pShape2d->GetLayer() );
+ p3DObj->SetMergedItemSet( aLocalSet );
+ if ( bUseExtrusionColor )
+ p3DObj->SetMergedItem( XFillColorItem( "", rSdrObjCustomShape.GetMergedItem( XATTR_SECONDARYFILLCOLOR ).GetColorValue() ) );
+ p3DObj->SetMergedItem( XFillStyleItem( drawing::FillStyle_SOLID ) );
+ p3DObj->SetMergedItem( Svx3DCloseFrontItem( false ) );
+ p3DObj->SetMergedItem( Svx3DCloseBackItem( false ) );
+ pScene->InsertObject( p3DObj );
+
+ // #i122777# depth 0 is okay for planes when using double-sided
+ p3DObj = new E3dExtrudeObj(
+ rSdrObjCustomShape.getSdrModelFromSdrObject(),
+ a3DDefaultAttr,
+ aPolyPoly,
+ 0);
+
+ p3DObj->NbcSetLayer( pShape2d->GetLayer() );
+ p3DObj->SetMergedItemSet( aLocalSet );
+
+ basegfx::B3DHomMatrix aFrontTransform( p3DObj->GetTransform() );
+ aFrontTransform.translate( 0.0, 0.0, fDepth );
+ p3DObj->NbcSetTransform( aFrontTransform );
+
+ if ( ( aLocalFillStyle == drawing::FillStyle_BITMAP ) && !aFillBmp.IsEmpty() )
+ {
+ p3DObj->SetMergedItem(XFillBitmapItem(OUString(), Graphic(aFillBmp)));
+ }
+ }
+ else if ( aLocalFillStyle == drawing::FillStyle_NONE )
+ {
+ const XLineColorItem& rLineColor = p3DObj->GetMergedItem( XATTR_LINECOLOR );
+ p3DObj->SetMergedItem( XFillColorItem( "", rLineColor.GetColorValue() ) );
+ p3DObj->SetMergedItem( makeSvx3DDoubleSidedItem( true ) );
+ p3DObj->SetMergedItem( Svx3DCloseFrontItem( false ) );
+ p3DObj->SetMergedItem( Svx3DCloseBackItem( false ) );
+ }
+ pScene->InsertObject( p3DObj );
+ bSceneHasObjects = true;
+ }
+ }
+
+ if ( bSceneHasObjects ) // is the SdrObject properly converted
+ {
+ // then we can change the return value
+ pRet = pScene;
+
+ // Camera settings, Perspective ...
+ Camera3D rCamera = pScene->GetCamera();
+ pScene->NbcSetSnapRect( aSnapRect );
+
+ // InitScene replacement
+ double fW = aBoundRect2d.getWidth();
+ double fH = aBoundRect2d.getHeight();
+ rCamera.SetAutoAdjustProjection( false );
+ rCamera.SetViewWindow( -fW / 2, - fH / 2, fW, fH);
+ basegfx::B3DPoint aLookAt( 0.0, 0.0, 0.0 );
+ basegfx::B3DPoint aCamPos( 0.0, 0.0, 100.0 );
+ rCamera.SetPosAndLookAt( aCamPos, aLookAt );
+ rCamera.SetFocalLength( 1.0 );
+ ProjectionType eProjectionType( eProjectionMode == drawing::ProjectionMode_PARALLEL ? ProjectionType::Parallel : ProjectionType::Perspective );
+ rCamera.SetProjection( eProjectionType );
+ pScene->SetCamera( rCamera );
+ pScene->SetBoundAndSnapRectsDirty();
+
+ basegfx::B3DHomMatrix aNewTransform( pScene->GetTransform() );
+ basegfx::B2DHomMatrix aPolyPolyTransform;
+ // Apply flip and z-rotation to scene transformation (y up). At same time transform
+ // aTotalPolyPoly (y down) which will be used for 2D boundRect of shape having 2D
+ // transformations applied.
+
+ // API values use shape center as origin. Move scene so, that shape center is origin.
+ aNewTransform.translate( -aCenter.X(), aCenter.Y(), -fExtrusionBackward);
+ aPolyPolyTransform.translate(-aCenter.X(), -aCenter.Y());
+
+ double fZRotate(basegfx::deg2rad(rSdrObjCustomShape.GetObjectRotation()));
+ if ( fZRotate != 0.0 )
+ {
+ aNewTransform.rotate( 0.0, 0.0, fZRotate );
+ aPolyPolyTransform.rotate(-fZRotate);
+ }
+ if ( bIsMirroredX )
+ {
+ aNewTransform.scale( -1.0, 1, 1 );
+ aPolyPolyTransform.scale(-1.0, 1);
+ }
+ if ( bIsMirroredY )
+ {
+ aNewTransform.scale( 1, -1.0, 1 );
+ aPolyPolyTransform.scale(1, -1.0);
+ }
+ aPolyPolyTransform.translate(aCenter.X(), aCenter.Y());
+ aTotalPolyPoly.transform(aPolyPolyTransform);
+
+ // x- and y-rotation have an own rotation center. x- and y-value of rotation center are
+ // fractions of shape size, z-value is in Hmm in property. Shape center is (0 0 0).
+ // Values in property are in custom shape extrusion space with y-axis down.
+ double fXRotate, fYRotate;
+ GetRotateAngle( rGeometryItem, fXRotate, fYRotate );
+ drawing::Direction3D aRotationCenterDefault( 0, 0, 0 );
+ drawing::Direction3D aRotationCenter( GetDirection3D( rGeometryItem, "RotationCenter", aRotationCenterDefault ) );
+ aRotationCenter.DirectionX *= aSnapRect.getWidth();
+ aRotationCenter.DirectionY *= aSnapRect.getHeight();
+ if (pMap)
+ {
+ aRotationCenter.DirectionZ *= *pMap;
+ }
+ aNewTransform.translate( -aRotationCenter.DirectionX, aRotationCenter.DirectionY, -aRotationCenter.DirectionZ );
+ if( fYRotate != 0.0 )
+ aNewTransform.rotate( 0.0, -fYRotate, 0.0 );
+ if( fXRotate != 0.0 )
+ aNewTransform.rotate( -fXRotate, 0.0, 0.0 );
+ aNewTransform.translate(aRotationCenter.DirectionX, -aRotationCenter.DirectionY, aRotationCenter.DirectionZ);
+
+ // oblique parallel projection is done by shearing the object, not by moving the camera
+ if (eProjectionMode == drawing::ProjectionMode_PARALLEL)
+ {
+ double fSkew, fAlpha;
+ GetSkew( rGeometryItem, fSkew, fAlpha );
+ if ( fSkew != 0.0 )
+ {
+ double fInvTanBeta( fSkew / 100.0 );
+ if(fInvTanBeta)
+ {
+ aNewTransform.shearXY(
+ fInvTanBeta * cos(fAlpha),
+ fInvTanBeta * sin(fAlpha));
+ }
+ }
+ }
+
+ pScene->NbcSetTransform( aNewTransform );
+
+ // These values are used later again, so declare them outside the if-statement. They will
+ // contain the absolute values of ViewPoint in 3D scene coordinate system, y-axis up.
+ double fViewPointX = 0; // dummy values
+ double fViewPointY = 0;
+ double fViewPointZ = 25000;
+ if (eProjectionMode == drawing::ProjectionMode_PERSPECTIVE)
+ {
+ double fOriginX, fOriginY;
+ // Calculate BoundRect of shape, including flip and z-rotation, from aTotalPolyPoly.
+ tools::Rectangle aBoundAfter2DTransform; // aBoundAfter2DTransform has y-axis down.
+ basegfx::B2DRange aTotalPolyPolyRange(aTotalPolyPoly.getB2DRange());
+ aBoundAfter2DTransform.SetLeft(aTotalPolyPolyRange.getMinX());
+ aBoundAfter2DTransform.SetTop(aTotalPolyPolyRange.getMinY());
+ aBoundAfter2DTransform.SetRight(aTotalPolyPolyRange.getMaxX());
+ aBoundAfter2DTransform.SetBottom(aTotalPolyPolyRange.getMaxY());
+
+ // Property "Origin" in API is relative to bounding box of shape after 2D
+ // transformations. Range is [-0.5;0.5] with center of bounding box as 0.
+ // Resolve "Origin" fractions to length
+ GetOrigin( rGeometryItem, fOriginX, fOriginY );
+ fOriginX *= aBoundAfter2DTransform.GetWidth();
+ fOriginY *= aBoundAfter2DTransform.GetHeight();
+ // Resolve length to absolute value for 3D
+ fOriginX += aBoundAfter2DTransform.Center().X();
+ fOriginY += aBoundAfter2DTransform.Center().Y();
+ fOriginY = - fOriginY;
+ // Scene is translated so that shape center is origin of coordinate system.
+ // Translate point "Origin" too.
+ fOriginX -= aCenter.X();
+ fOriginY -= -aCenter.Y();
+ // API ViewPoint values are relative to point "Origin" and have y-axis down.
+ // ToDo: These default ViewPoint values are used as default by MS Office. But ODF
+ // default is (3500, -3500, 25000), details in tdf#146192.
+ drawing::Position3D aViewPointDefault( 3472, -3472, 25000 );
+ drawing::Position3D aViewPoint( GetPosition3D( rGeometryItem, "ViewPoint", aViewPointDefault, pMap ) );
+ fViewPointX = aViewPoint.PositionX + fOriginX;
+ fViewPointY = - aViewPoint.PositionY + fOriginY;
+ fViewPointZ = aViewPoint.PositionZ;
+ }
+
+ // now set correct camera position
+ if (eProjectionMode == drawing::ProjectionMode_PARALLEL)
+ {
+ basegfx::B3DPoint _aLookAt( 0.0, 0.0, 0.0 );
+ basegfx::B3DPoint _aNewCamPos( 0.0, 0.0, 25000.0 );
+ rCamera.SetPosAndLookAt( _aNewCamPos, _aLookAt );
+ pScene->SetCamera( rCamera );
+ }
+ else
+ {
+ basegfx::B3DPoint _aLookAt(fViewPointX, fViewPointY, 0.0);
+ basegfx::B3DPoint aNewCamPos(fViewPointX, fViewPointY, fViewPointZ);
+ rCamera.SetPosAndLookAt( aNewCamPos, _aLookAt );
+ pScene->SetCamera( rCamera );
+ }
+
+ // NbcSetTransform has not updated the scene 2D rectangles.
+ // Idea: Get a bound volume as polygon from bound rectangle of shape without 2D
+ // transformations. Calculate its projection to the XY-plane. Then calculate the bounding
+ // rectangle of the projection and convert this rectangle back to absolute 2D coordinates.
+ // Set that as 2D rectangle of the scene.
+ const tools::Polygon aPolygon(aBoundRect2d); // y-up
+ basegfx::B3DPolygon aPolygonBoundVolume; // y-down, scene coordinates
+ for (sal_uInt16 i = 0; i < 4; i++ )
+ {
+ aPolygonBoundVolume.append(basegfx::B3DPoint(aPolygon[i].X(), -aPolygon[i].Y(), 0));
+ }
+ for (sal_uInt16 i = 0; i < 4; i++ )
+ {
+ aPolygonBoundVolume.append(basegfx::B3DPoint(aPolygon[i].X(), -aPolygon[i].Y(), fDepth));
+ }
+ aPolygonBoundVolume.transform(aNewTransform);
+
+ // projection
+ tools::Polygon a2DProjectionResult(8); // in fact 3D points with z=0
+ for (sal_uInt16 i = 0; i < 8; i++ )
+ {
+ const basegfx::B3DPoint aPoint3D(aPolygonBoundVolume.getB3DPoint(i));
+
+ if (eProjectionMode == drawing::ProjectionMode_PARALLEL)
+ {
+ a2DProjectionResult[i].setX(aPoint3D.getX());
+ a2DProjectionResult[i].setY(aPoint3D.getY());
+ }
+ else
+ {
+ // skip point if line from viewpoint to point is parallel to xy-plane
+ if (double fDiv = aPoint3D.getZ() - fViewPointZ; fDiv != 0.0)
+ {
+ double f = (- fViewPointZ) / fDiv;
+ double fX = (aPoint3D.getX() - fViewPointX) * f + fViewPointX;
+ double fY = (aPoint3D.getY() - fViewPointY) * f + fViewPointY;;
+ a2DProjectionResult[i].setX(static_cast<sal_Int32>(fX));
+ a2DProjectionResult[i].setY(static_cast<sal_Int32>(fY));
+ }
+ }
+ }
+ // Convert to y-axis down
+ for (sal_uInt16 i = 0; i < 8; i++ )
+ {
+ a2DProjectionResult[i].setY(- a2DProjectionResult[i].Y());
+ }
+ // Shift back to shape center
+ a2DProjectionResult.Translate(aCenter);
+
+ pScene->SetLogicRect(a2DProjectionResult.GetBoundRect());
+
+
+ // light and material
+
+ // "LightFace" has nothing corresponding in 3D rendering engine.
+ /* bool bLightFace = */ GetBool(rGeometryItem, "LightFace", true); // default in ODF
+
+ // Light directions
+
+ drawing::Direction3D aFirstLightDirectionDefault(50000.0, 0.0, 10000.0);
+ drawing::Direction3D aFirstLightDirection(GetDirection3D( rGeometryItem, "FirstLightDirection", aFirstLightDirectionDefault));
+ if (aFirstLightDirection.DirectionX == 0.0 && aFirstLightDirection.DirectionY == 0.0
+ && aFirstLightDirection.DirectionZ == 0.0)
+ aFirstLightDirection.DirectionZ = 1.0;
+ basegfx::B3DVector aLight1Vector(aFirstLightDirection.DirectionX, -aFirstLightDirection.DirectionY, aFirstLightDirection.DirectionZ);
+ aLight1Vector.normalize();
+
+ drawing::Direction3D aSecondLightDirectionDefault(-50000.0, 0.0, 10000.0);
+ drawing::Direction3D aSecondLightDirection(GetDirection3D( rGeometryItem, "SecondLightDirection", aSecondLightDirectionDefault));
+ if (aSecondLightDirection.DirectionX == 0.0 && aSecondLightDirection.DirectionY == 0.0
+ && aSecondLightDirection.DirectionZ == 0.0)
+ aSecondLightDirection.DirectionZ = 1.0;
+ basegfx::B3DVector aLight2Vector(aSecondLightDirection.DirectionX, -aSecondLightDirection.DirectionY, aSecondLightDirection.DirectionZ);
+ aLight2Vector.normalize();
+
+ // Light Intensity
+
+ // For "FirstLight" the 3D-Scene light "1" is regularly used. In case of surface "Matte"
+ // the light 4 is used instead. For "SecondLight" the 3D-Scene light "2" is regularly used.
+ // In case first or second light is not harsh, the lights 5 to 8 are used in addition
+ // to get a soft light appearance.
+ // The 3D-Scene light "3" is currently not used.
+
+ // ODF default 66%. MS Office default 38000/65536=0.579 is set in import filter.
+ double fLight1Intensity = GetDouble(rGeometryItem, "FirstLightLevel", 66) / 100.0;
+ // ODF and MS Office have both default 'true'.
+ bool bFirstLightHarsh = GetBool(rGeometryItem, "FirstLightHarsh", true);
+ // ODF default 66%. MS Office default 38000/65536=0.579 is set in import filter
+ double fLight2Intensity = GetDouble(rGeometryItem, "SecondLightLevel", 66) / 100.0;
+ // ODF has default 'true'. MS Office default 'false' is set in import.
+ bool bSecondLightHarsh = GetBool(rGeometryItem, "SecondLightHarsh", true);
+
+ // ODF default 33%. MS Office default 20000/65536=0.305 is set in import filter.
+ double fAmbientIntensity = GetDouble(rGeometryItem, "Brightness", 33) / 100.0;
+
+ double fLight1IntensityForSpecular(fLight1Intensity); // remember original value
+ if (!bFirstLightHarsh || !bSecondLightHarsh) // might need softing lights
+ {
+ bool bNeedSoftLights(false); // catch case of lights with zero intensity.
+ basegfx::B3DVector aLight5Vector;
+ basegfx::B3DVector aLight6Vector;
+ basegfx::B3DVector aLight7Vector;
+ basegfx::B3DVector aLight8Vector;
+ // The needed light intensities depend on the angle between regular light and
+ // additional lights, currently for 60deg.
+ Color aHoriSoftLightColor;
+ Color aVertSoftLightColor;
+
+ if (!bSecondLightHarsh && fLight2Intensity > 0.0
+ && (bFirstLightHarsh || fLight1Intensity == 0.0)) // only second light soft
+ {
+ // That is default for shapes generated in the UI, for LO and MS Office as well.
+ bNeedSoftLights = true;
+ double fLight2SoftIntensity = fLight2Intensity * 0.40;
+ aHoriSoftLightColor = Color(basegfx::BColor(fLight2SoftIntensity).clamp());
+ aVertSoftLightColor = aHoriSoftLightColor;
+ fLight2Intensity *= 0.2;
+
+ lcl_SoftLightsDirection(aLight2Vector, aLight5Vector, aLight6Vector,
+ aLight7Vector, aLight8Vector);
+ }
+ else if (!bFirstLightHarsh && fLight1Intensity > 0.0
+ && (bSecondLightHarsh || fLight2Intensity == 0.0)) // only first light soft
+ {
+ bNeedSoftLights = true;
+ double fLight1SoftIntensity = fLight1Intensity * 0.40;
+ aHoriSoftLightColor = Color(basegfx::BColor(fLight1SoftIntensity).clamp());
+ aVertSoftLightColor = aHoriSoftLightColor;
+ fLight1Intensity *= 0.2;
+
+ lcl_SoftLightsDirection(aLight1Vector, aLight5Vector, aLight6Vector,
+ aLight7Vector, aLight8Vector);
+ }
+ else if (!bFirstLightHarsh && fLight1Intensity > 0.0 && !bSecondLightHarsh
+ && fLight2Intensity > 0.0) // both lights soft
+ {
+ bNeedSoftLights = true;
+ // We do not hat enough lights. We use two soft lights for FirstLight and two for
+ // SecondLight and double intensity.
+ double fLight1SoftIntensity = fLight1Intensity * 0.8;
+ fLight1Intensity *= 0.4;
+ aHoriSoftLightColor = Color(basegfx::BColor(fLight1SoftIntensity).clamp());
+ basegfx::B3DVector aDummy1, aDummy2;
+ lcl_SoftLightsDirection(aLight1Vector, aDummy1, aDummy2, aLight7Vector,
+ aLight8Vector);
+
+ double fLight2SoftIntensity = fLight2Intensity * 0.8;
+ aVertSoftLightColor = Color(basegfx::BColor(fLight2SoftIntensity).clamp());
+ fLight2Intensity *= 0.4;
+ lcl_SoftLightsDirection(aLight2Vector, aLight5Vector, aLight6Vector, aDummy1,
+ aDummy2);
+ }
+
+ if (bNeedSoftLights)
+ {
+ pScene->GetProperties().SetObjectItem(
+ makeSvx3DLightDirection5Item(aLight5Vector));
+ pScene->GetProperties().SetObjectItem(
+ makeSvx3DLightcolor5Item(aVertSoftLightColor));
+ pScene->GetProperties().SetObjectItem(makeSvx3DLightOnOff5Item(true));
+ pScene->GetProperties().SetObjectItem(
+ makeSvx3DLightDirection6Item(aLight6Vector));
+ pScene->GetProperties().SetObjectItem(
+ makeSvx3DLightcolor6Item(aVertSoftLightColor));
+ pScene->GetProperties().SetObjectItem(makeSvx3DLightOnOff6Item(true));
+ pScene->GetProperties().SetObjectItem(
+ makeSvx3DLightDirection7Item(aLight7Vector));
+ pScene->GetProperties().SetObjectItem(
+ makeSvx3DLightcolor7Item(aHoriSoftLightColor));
+ pScene->GetProperties().SetObjectItem(makeSvx3DLightOnOff7Item(true));
+ pScene->GetProperties().SetObjectItem(
+ makeSvx3DLightDirection8Item(aLight8Vector));
+ pScene->GetProperties().SetObjectItem(
+ makeSvx3DLightcolor8Item(aHoriSoftLightColor));
+ pScene->GetProperties().SetObjectItem(makeSvx3DLightOnOff8Item(true));
+ }
+ }
+
+ // ToDo: MSO seems to add half of the surplus to ambient color. ODF restricts value to <1.
+ if (fLight1Intensity > 1.0)
+ {
+ fAmbientIntensity += (fLight1Intensity - 1.0) / 2.0;
+ }
+
+ // ToDo: How to handle fAmbientIntensity larger 1.0 ? Perhaps lighten object color?
+
+ // Now set the regularly 3D-scene light attributes.
+ Color aAmbientColor(basegfx::BColor(fAmbientIntensity).clamp());
+ pScene->GetProperties().SetObjectItem(makeSvx3DAmbientcolorItem(aAmbientColor));
+
+ pScene->GetProperties().SetObjectItem(makeSvx3DLightDirection1Item(aLight1Vector));
+ pScene->GetProperties().SetObjectItem(makeSvx3DLightOnOff1Item(fLight1Intensity > 0.0));
+ Color aLight1Color(basegfx::BColor(fLight1Intensity).clamp());
+ pScene->GetProperties().SetObjectItem(makeSvx3DLightcolor1Item(aLight1Color));
+
+ pScene->GetProperties().SetObjectItem(makeSvx3DLightDirection2Item(aLight2Vector));
+ pScene->GetProperties().SetObjectItem(makeSvx3DLightOnOff2Item(fLight2Intensity > 0.0));
+ Color aLight2Color(basegfx::BColor(fLight2Intensity).clamp());
+ pScene->GetProperties().SetObjectItem(makeSvx3DLightcolor2Item(aLight2Color));
+
+ // Object reactions on light
+ // Diffusion, Specular-Color and -Intensity are object properties, not scene properties.
+ // Surface flag "Metal" is an object property too.
+
+ // Property "Diffusion" would correspond to style attribute "drd3:diffuse-color".
+ // But that is not implemented. We cannot ignore the attribute because MS Office sets
+ // attribute c3DDiffuseAmt to 43712 (Type Fixed 16.16, approx 66,9%) instead of MSO
+ // default 65536 (100%), if the user sets surface 'Metal' in the UI of MS Office.
+ // We will change the material color of the 3D object as ersatz.
+ // ODF data type is percent with default 0%. MSO default is set in import filter.
+ double fDiffusion = GetDouble(rGeometryItem, "Diffusion", 0.0) / 100.0;
+
+ // ODF standard specifies for value true: "the specular color for the shading of an
+ // extruded shape is gray (red, green and blue values of 200) instead of white and 15% is
+ // added to the specularity."
+ // Neither 'specularity' nor 'specular color' is clearly defined in the standard. ODF term
+ // 'specularity' seems to correspond to UI field 'Specular Intensity' for 3D scenes.
+ // MS Office uses current material color in case 'Metal' is set. To detect, whether
+ // rendering similar to MS Office has to be used the property 'MetalType' is used. It is
+ // set on import and in the extrusion bar.
+ bool bMetal = GetBool(rGeometryItem, "Metal", false);
+ sal_Int16 eMetalType(
+ GetMetalType(rGeometryItem, drawing::EnhancedCustomShapeMetalType::MetalODF));
+ bool bMetalMSCompatible
+ = eMetalType == drawing::EnhancedCustomShapeMetalType::MetalMSCompatible;
+
+ // Property "Specularity" corresponds to 3D object style attribute dr3d:specular-color.
+ double fSpecularity = GetDouble(rGeometryItem, "Specularity", 0) / 100.0;
+
+ if (bMetal && !bMetalMSCompatible)
+ {
+ fSpecularity *= 200.0 / 255.0;
+ }
+
+ // MS Office seems to render as if 'Specular Color' = Specularity * Light1Intensity.
+ double fShadingFactor = fLight1IntensityForSpecular * fSpecularity;
+ Color aSpecularCol(basegfx::BColor(fShadingFactor).clamp());
+ // In case of bMetalMSCompatible the color will be recalculated in the below loop.
+
+ // Shininess ODF default 50 (unit %). MS Office default 5, import filter makes *10.
+ // Shininess corresponds to "Specular Intensity" with the nonlinear relationship
+ // "Specular Intensity" = 2^c3DShininess = 2^("Shininess" / 10)
+ double fShininess = GetDouble(rGeometryItem, "Shininess", 50) / 10.0;
+ fShininess = std::clamp<double>(pow(2, fShininess), 0.0, 100.0);
+ sal_uInt16 nIntensity = static_cast<sal_uInt16>(basegfx::fround(fShininess));
+ if (bMetal && !bMetalMSCompatible)
+ {
+ nIntensity += 15; // as specified in ODF
+ nIntensity = std::clamp<sal_uInt16>(nIntensity, 0, 100);
+ }
+
+ SdrObjListIter aSceneIter(*pScene, SdrIterMode::DeepNoGroups);
+ while (aSceneIter.IsMore())
+ {
+ const SdrObject* pNext = aSceneIter.Next();
+
+ // Change material color as ersatz for missing style attribute "drd3:diffuse-color".
+ // For this ersatz we exclude case fDiffusion == 0.0, because for older documents this
+ // attribute is not written out to draw:extrusion-diffusion and ODF default 0 would
+ // produce black objects.
+ const Color& rMatColor
+ = pNext->GetProperties().GetItem(XATTR_FILLCOLOR).GetColorValue();
+ Color aOldMatColor(rMatColor);
+ if (basegfx::fTools::more(fDiffusion, 0.0)
+ && !basegfx::fTools::equal(fDiffusion, 1.0))
+ {
+ // Occurs e.g. with MS surface preset 'Metal'.
+ sal_uInt16 nHue;
+ sal_uInt16 nSaturation;
+ sal_uInt16 nBrightness;
+ rMatColor.RGBtoHSB(nHue, nSaturation, nBrightness);
+ nBrightness
+ = static_cast<sal_uInt16>(static_cast<double>(nBrightness) * fDiffusion);
+ nBrightness = std::clamp<sal_uInt16>(nBrightness, 0, 100);
+ Color aNewMatColor = Color::HSBtoRGB(nHue, nSaturation, nBrightness);
+ pNext->GetProperties().SetObjectItem(XFillColorItem("", aNewMatColor));
+ }
+
+ // Using material color instead of gray in case of MS Office compatible rendering.
+ if (bMetal && bMetalMSCompatible)
+ {
+ sal_uInt16 nHue;
+ sal_uInt16 nSaturation;
+ sal_uInt16 nBrightness;
+ aOldMatColor.RGBtoHSB(nHue, nSaturation, nBrightness);
+ nBrightness = static_cast<sal_uInt16>(static_cast<double>(nBrightness)
+ * fShadingFactor);
+ nBrightness = std::clamp<sal_uInt16>(nBrightness, 0, 100);
+ aSpecularCol = Color::HSBtoRGB(nHue, nSaturation, nBrightness);
+ }
+
+ pNext->GetProperties().SetObjectItem(makeSvx3DMaterialSpecularItem(aSpecularCol));
+ pNext->GetProperties().SetObjectItem(
+ makeSvx3DMaterialSpecularIntensityItem(nIntensity));
+ }
+
+ // fSpecularity = 0 is used to indicate surface preset "Matte".
+ if (basegfx::fTools::equalZero(fSpecularity))
+ {
+ // First light in LO 3D engine is always specular, all other lights are never specular.
+ // We copy light1 values to light4 and use it instead of light1 in the 3D scene.
+ pScene->GetProperties().SetObjectItem(makeSvx3DLightOnOff1Item(false));
+ pScene->GetProperties().SetObjectItem(makeSvx3DLightOnOff4Item(true));
+ pScene->GetProperties().SetObjectItem(makeSvx3DLightcolor4Item(aLight1Color));
+ pScene->GetProperties().SetObjectItem(makeSvx3DLightDirection4Item(aLight1Vector));
+ }
+
+ // removing placeholder objects
+ for (E3dCompoundObject* pTemp : aPlaceholderObjectList)
+ {
+ pScene->RemoveObject( pTemp->GetOrdNum() );
+ // always use SdrObject::Free(...) for SdrObjects (!)
+ SdrObject* pTemp2(pTemp);
+ SdrObject::Free(pTemp2);
+ }
+ }
+ else
+ {
+ // always use SdrObject::Free(...) for SdrObjects (!)
+ SdrObject* pTemp(pScene);
+ SdrObject::Free(pTemp);
+ }
+ }
+ return pRet;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/customshapes/EnhancedCustomShape3d.hxx b/svx/source/customshapes/EnhancedCustomShape3d.hxx
new file mode 100644
index 000000000..77e7d350f
--- /dev/null
+++ b/svx/source/customshapes/EnhancedCustomShape3d.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_SVX_SOURCE_CUSTOMSHAPES_ENHANCEDCUSTOMSHAPE3D_HXX
+#define INCLUDED_SVX_SOURCE_CUSTOMSHAPES_ENHANCEDCUSTOMSHAPE3D_HXX
+
+class SdrObject;
+class SdrObjCustomShape;
+
+class EnhancedCustomShape3d final
+{
+
+public:
+ static SdrObject* Create3DObject(
+ const SdrObject* pShape2d,
+ const SdrObjCustomShape& rSdrObjCustomShape);
+};
+
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/customshapes/EnhancedCustomShapeEngine.cxx b/svx/source/customshapes/EnhancedCustomShapeEngine.cxx
new file mode 100644
index 000000000..ae919904a
--- /dev/null
+++ b/svx/source/customshapes/EnhancedCustomShapeEngine.cxx
@@ -0,0 +1,488 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * 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/Reference.h>
+#include <com/sun/star/uno/XComponentContext.hpp>
+#include <com/sun/star/awt/Rectangle.hpp>
+#include <com/sun/star/beans/PropertyValue.hpp>
+#include <com/sun/star/drawing/PolyPolygonBezierCoords.hpp>
+#include <com/sun/star/lang/XInitialization.hpp>
+#include <com/sun/star/lang/XServiceInfo.hpp>
+#include <com/sun/star/drawing/XCustomShapeEngine.hpp>
+#include <svx/EnhancedCustomShape2d.hxx>
+#include "EnhancedCustomShape3d.hxx"
+#include "EnhancedCustomShapeFontWork.hxx"
+#include "EnhancedCustomShapeHandle.hxx"
+#include <svx/unoshape.hxx>
+#include <svx/unopage.hxx>
+#include <svx/svdobj.hxx>
+#include <svx/svdoashp.hxx>
+#include <svx/svdogrp.hxx>
+#include <editeng/outlobj.hxx>
+#include <svl/itemset.hxx>
+#include <svx/svdopath.hxx>
+#include <svx/svdpage.hxx>
+#include <svx/svditer.hxx>
+#include <svx/xfillit0.hxx>
+#include <svx/xlineit0.hxx>
+#include <basegfx/polygon/b2dpolypolygontools.hxx>
+#include <com/sun/star/document/XActionLockable.hpp>
+#include <cppuhelper/implbase.hxx>
+#include <cppuhelper/supportsservice.hxx>
+
+using namespace css;
+using namespace css::uno;
+
+class SdrObject;
+class SdrObjCustomShape;
+
+namespace {
+
+class EnhancedCustomShapeEngine : public cppu::WeakImplHelper
+<
+ css::lang::XInitialization,
+ css::lang::XServiceInfo,
+ css::drawing::XCustomShapeEngine
+>
+{
+ css::uno::Reference< css::drawing::XShape > mxShape;
+ bool mbForceGroupWithText;
+
+ std::unique_ptr<SdrObject, SdrObjectFreeOp> ImplForceGroupWithText(
+ const SdrObjCustomShape& rSdrObjCustomShape,
+ std::unique_ptr<SdrObject, SdrObjectFreeOp> pRenderedShape);
+
+public:
+ EnhancedCustomShapeEngine();
+
+ // XInterface
+ virtual void SAL_CALL acquire() noexcept override;
+ virtual void SAL_CALL release() noexcept 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& rServiceName ) override;
+ virtual css::uno::Sequence< OUString > SAL_CALL getSupportedServiceNames() override;
+
+ // XCustomShapeEngine
+ virtual css::uno::Reference< css::drawing::XShape > SAL_CALL render() override;
+ virtual css::awt::Rectangle SAL_CALL getTextBounds() override;
+ virtual css::drawing::PolyPolygonBezierCoords SAL_CALL getLineGeometry() override;
+ virtual css::uno::Sequence< css::uno::Reference< css::drawing::XCustomShapeHandle > > SAL_CALL getInteraction() override;
+};
+
+EnhancedCustomShapeEngine::EnhancedCustomShapeEngine() :
+ mbForceGroupWithText ( false )
+{
+}
+
+// XInterface
+void SAL_CALL EnhancedCustomShapeEngine::acquire() noexcept
+{
+ OWeakObject::acquire();
+}
+void SAL_CALL EnhancedCustomShapeEngine::release() noexcept
+{
+ OWeakObject::release();
+}
+
+// XInitialization
+void SAL_CALL EnhancedCustomShapeEngine::initialize( const Sequence< Any >& aArguments )
+{
+ Sequence< beans::PropertyValue > aParameter;
+ for ( const auto& rArgument : aArguments )
+ {
+ if ( rArgument >>= aParameter )
+ break;
+ }
+ for ( const beans::PropertyValue& rProp : std::as_const(aParameter) )
+ {
+ if ( rProp.Name == "CustomShape" )
+ rProp.Value >>= mxShape;
+ else if ( rProp.Name == "ForceGroupWithText" )
+ rProp.Value >>= mbForceGroupWithText;
+ }
+}
+
+// XServiceInfo
+OUString SAL_CALL EnhancedCustomShapeEngine::getImplementationName()
+{
+ return "com.sun.star.drawing.EnhancedCustomShapeEngine";
+}
+sal_Bool SAL_CALL EnhancedCustomShapeEngine::supportsService( const OUString& rServiceName )
+{
+ return cppu::supportsService(this, rServiceName);
+}
+Sequence< OUString > SAL_CALL EnhancedCustomShapeEngine::getSupportedServiceNames()
+{
+ return { "com.sun.star.drawing.CustomShapeEngine" };
+}
+
+// XCustomShapeEngine
+std::unique_ptr<SdrObject, SdrObjectFreeOp> EnhancedCustomShapeEngine::ImplForceGroupWithText(
+ const SdrObjCustomShape& rSdrObjCustomShape,
+ std::unique_ptr<SdrObject, SdrObjectFreeOp> pRenderedShape)
+{
+ const bool bHasText(rSdrObjCustomShape.HasText());
+
+ if ( pRenderedShape || bHasText )
+ {
+ // applying shadow
+ const SdrObject* pShadowGeometry(rSdrObjCustomShape.GetSdrObjectShadowFromCustomShape());
+
+ if ( pShadowGeometry )
+ {
+ if ( pRenderedShape )
+ {
+ if ( dynamic_cast<const SdrObjGroup*>( pRenderedShape.get() ) == nullptr )
+ {
+ auto pTmp = std::move(pRenderedShape);
+ pRenderedShape.reset(new SdrObjGroup(rSdrObjCustomShape.getSdrModelFromSdrObject()));
+ static_cast<SdrObjGroup*>(pRenderedShape.get())->GetSubList()->NbcInsertObject( pTmp.release() );
+ }
+
+ static_cast<SdrObjGroup*>(pRenderedShape.get())->GetSubList()->NbcInsertObject(
+ pShadowGeometry->CloneSdrObject(pShadowGeometry->getSdrModelFromSdrObject()),
+ 0);
+ }
+ else
+ {
+ pRenderedShape.reset( pShadowGeometry->CloneSdrObject(pShadowGeometry->getSdrModelFromSdrObject()) );
+ }
+ }
+
+ // apply text
+ if ( bHasText )
+ {
+ // #i37011# also create a text object and add at rPos + 1
+ std::unique_ptr<SdrObject, SdrObjectFreeOp> pTextObj( SdrObjFactory::MakeNewObject(
+ rSdrObjCustomShape.getSdrModelFromSdrObject(),
+ rSdrObjCustomShape.GetObjInventor(),
+ SdrObjKind::Text) );
+
+ // Copy text content
+ OutlinerParaObject* pParaObj(rSdrObjCustomShape.GetOutlinerParaObject());
+
+ if( pParaObj )
+ pTextObj->NbcSetOutlinerParaObject( *pParaObj );
+
+ // copy all attributes
+ SfxItemSet aTargetItemSet(rSdrObjCustomShape.GetMergedItemSet());
+
+ // clear fill and line style
+ aTargetItemSet.Put(XLineStyleItem(drawing::LineStyle_NONE));
+ aTargetItemSet.Put(XFillStyleItem(drawing::FillStyle_NONE));
+
+ // get the text bounds and set at text object
+ tools::Rectangle aTextBounds(rSdrObjCustomShape.GetSnapRect());
+ auto pSdrObjCustomShape = dynamic_cast< SdrObjCustomShape* >(SdrObject::getSdrObjectFromXShape(mxShape));
+
+ if(pSdrObjCustomShape)
+ {
+ EnhancedCustomShape2d aCustomShape2d(*pSdrObjCustomShape);
+ aTextBounds = aCustomShape2d.GetTextRect();
+ }
+
+ pTextObj->SetSnapRect( aTextBounds );
+
+ // if rotated, copy GeoStat, too.
+ const GeoStat& rSourceGeo(rSdrObjCustomShape.GetGeoStat());
+ if ( rSourceGeo.nRotationAngle )
+ {
+ pTextObj->NbcRotate(
+ rSdrObjCustomShape.GetSnapRect().Center(),
+ rSourceGeo.nRotationAngle,
+ rSourceGeo.mfSinRotationAngle,
+ rSourceGeo.mfCosRotationAngle);
+ }
+
+ // set modified ItemSet at text object
+ pTextObj->SetMergedItemSet(aTargetItemSet);
+
+ if ( pRenderedShape )
+ {
+ if ( dynamic_cast<const SdrObjGroup*>( pRenderedShape.get() ) == nullptr )
+ {
+ auto pTmp = std::move(pRenderedShape);
+ pRenderedShape.reset(new SdrObjGroup(rSdrObjCustomShape.getSdrModelFromSdrObject()));
+ static_cast<SdrObjGroup*>(pRenderedShape.get())->GetSubList()->NbcInsertObject( pTmp.release() );
+ }
+ static_cast<SdrObjGroup*>(pRenderedShape.get())->GetSubList()->NbcInsertObject( pTextObj.release() );
+ }
+ else
+ pRenderedShape = std::move(pTextObj);
+ }
+
+ // force group
+ if ( pRenderedShape )
+ {
+ if ( dynamic_cast<const SdrObjGroup*>( pRenderedShape.get() ) == nullptr )
+ {
+ auto pTmp = std::move(pRenderedShape);
+ pRenderedShape.reset(new SdrObjGroup(rSdrObjCustomShape.getSdrModelFromSdrObject()));
+ static_cast<SdrObjGroup*>(pRenderedShape.get())->GetSubList()->NbcInsertObject( pTmp.release() );
+ }
+ }
+ }
+
+ return pRenderedShape;
+}
+
+void SetTemporary( uno::Reference< drawing::XShape > const & xShape )
+{
+ if ( xShape.is() )
+ {
+ SvxShape* pShape = comphelper::getFromUnoTunnel<SvxShape>( xShape );
+ if ( pShape )
+ pShape->TakeSdrObjectOwnership();
+ }
+}
+
+Reference< drawing::XShape > SAL_CALL EnhancedCustomShapeEngine::render()
+{
+ SdrObjCustomShape* pSdrObjCustomShape = dynamic_cast< SdrObjCustomShape* >(SdrObject::getSdrObjectFromXShape(mxShape));
+
+ if(!pSdrObjCustomShape)
+ {
+ return Reference< drawing::XShape >();
+ }
+
+ // retrieving the TextPath property to check if feature is enabled
+ const SdrCustomShapeGeometryItem& rGeometryItem(pSdrObjCustomShape->GetMergedItem( SDRATTR_CUSTOMSHAPE_GEOMETRY ));
+ bool bTextPathOn = false;
+ const uno::Any* pAny = rGeometryItem.GetPropertyValueByName( "TextPath", "TextPath" );
+ if ( pAny )
+ *pAny >>= bTextPathOn;
+
+ EnhancedCustomShape2d aCustomShape2d(*pSdrObjCustomShape);
+ Degree100 nRotateAngle = aCustomShape2d.GetRotateAngle();
+
+ bool bFlipV = aCustomShape2d.IsFlipVert();
+ bool bFlipH = aCustomShape2d.IsFlipHorz();
+ bool bLineGeometryNeededOnly = bTextPathOn;
+
+ std::unique_ptr<SdrObject, SdrObjectFreeOp> xRenderedShape(aCustomShape2d.CreateObject(bLineGeometryNeededOnly));
+ if (xRenderedShape)
+ {
+ if ( bTextPathOn )
+ {
+ std::unique_ptr<SdrObject, SdrObjectFreeOp> xRenderedFontWork(
+ EnhancedCustomShapeFontWork::CreateFontWork(
+ xRenderedShape.get(),
+ *pSdrObjCustomShape));
+
+ if (xRenderedFontWork)
+ {
+ xRenderedShape = std::move(xRenderedFontWork);
+ }
+ }
+ std::unique_ptr<SdrObject, SdrObjectFreeOp> xRenderedShape3d(EnhancedCustomShape3d::Create3DObject(xRenderedShape.get(), *pSdrObjCustomShape));
+ if (xRenderedShape3d)
+ {
+ bFlipV = bFlipH = false;
+ nRotateAngle = 0_deg100;
+ xRenderedShape = std::move(xRenderedShape3d);
+ }
+
+ tools::Rectangle aRect(pSdrObjCustomShape->GetSnapRect());
+ const GeoStat& rGeoStat(pSdrObjCustomShape->GetGeoStat());
+
+ if ( rGeoStat.nShearAngle )
+ {
+ Degree100 nShearAngle = rGeoStat.nShearAngle;
+ double nTan = rGeoStat.mfTanShearAngle;
+ if (bFlipV != bFlipH)
+ {
+ nShearAngle = -nShearAngle;
+ nTan = -nTan;
+ }
+
+ xRenderedShape->Shear(pSdrObjCustomShape->GetSnapRect().Center(), nShearAngle, nTan, false);
+ }
+ if(nRotateAngle )
+ xRenderedShape->NbcRotate(pSdrObjCustomShape->GetSnapRect().Center(), nRotateAngle);
+ if ( bFlipV )
+ {
+ Point aLeft( aRect.Left(), ( aRect.Top() + aRect.Bottom() ) >> 1 );
+ Point aRight( aLeft.X() + 1000, aLeft.Y() );
+ xRenderedShape->NbcMirror( aLeft, aRight );
+ }
+ if ( bFlipH )
+ {
+ Point aTop( ( aRect.Left() + aRect.Right() ) >> 1, aRect.Top() );
+ Point aBottom( aTop.X(), aTop.Y() + 1000 );
+ xRenderedShape->NbcMirror( aTop, aBottom );
+ }
+
+ xRenderedShape->NbcSetStyleSheet(pSdrObjCustomShape->GetStyleSheet(), true);
+ xRenderedShape->RecalcSnapRect();
+ }
+
+ if ( mbForceGroupWithText )
+ {
+ xRenderedShape = ImplForceGroupWithText(
+ *pSdrObjCustomShape,
+ std::move(xRenderedShape));
+ }
+
+ Reference< drawing::XShape > xShape;
+
+ if (xRenderedShape)
+ {
+ aCustomShape2d.ApplyGluePoints(xRenderedShape.get());
+ SdrObject* pRenderedShape = xRenderedShape.release();
+ xShape = SvxDrawPage::CreateShapeByTypeAndInventor( pRenderedShape->GetObjIdentifier(),
+ pRenderedShape->GetObjInventor(), pRenderedShape );
+ }
+
+ SetTemporary( xShape );
+
+ return xShape;
+}
+
+awt::Rectangle SAL_CALL EnhancedCustomShapeEngine::getTextBounds()
+{
+ awt::Rectangle aTextRect;
+ if (SdrObjCustomShape* pSdrObjCustomShape = dynamic_cast< SdrObjCustomShape* >(SdrObject::getSdrObjectFromXShape(mxShape)))
+ {
+ uno::Reference< document::XActionLockable > xLockable( mxShape, uno::UNO_QUERY );
+
+ if(xLockable.is() && !xLockable->isActionLocked())
+ {
+ EnhancedCustomShape2d aCustomShape2d(*pSdrObjCustomShape);
+ tools::Rectangle aRect( aCustomShape2d.GetTextRect() );
+ aTextRect.X = aRect.Left();
+ aTextRect.Y = aRect.Top();
+ aTextRect.Width = aRect.GetWidth();
+ aTextRect.Height = aRect.GetHeight();
+ }
+ }
+
+ return aTextRect;
+}
+
+drawing::PolyPolygonBezierCoords SAL_CALL EnhancedCustomShapeEngine::getLineGeometry()
+{
+ drawing::PolyPolygonBezierCoords aPolyPolygonBezierCoords;
+ SdrObjCustomShape* pSdrObjCustomShape = dynamic_cast< SdrObjCustomShape* >(SdrObject::getSdrObjectFromXShape(mxShape));
+
+ if(pSdrObjCustomShape)
+ {
+ EnhancedCustomShape2d aCustomShape2d(*pSdrObjCustomShape);
+ SdrObjectUniquePtr pObj = aCustomShape2d.CreateLineGeometry();
+
+ if ( pObj )
+ {
+ tools::Rectangle aRect(pSdrObjCustomShape->GetSnapRect());
+ bool bFlipV = aCustomShape2d.IsFlipVert();
+ bool bFlipH = aCustomShape2d.IsFlipHorz();
+ const GeoStat& rGeoStat(pSdrObjCustomShape->GetGeoStat());
+
+ if ( rGeoStat.nShearAngle )
+ {
+ Degree100 nShearAngle = rGeoStat.nShearAngle;
+ double nTan = rGeoStat.mfTanShearAngle;
+ if (bFlipV != bFlipH)
+ {
+ nShearAngle = -nShearAngle;
+ nTan = -nTan;
+ }
+ pObj->Shear( aRect.Center(), nShearAngle, nTan, false);
+ }
+ Degree100 nRotateAngle = aCustomShape2d.GetRotateAngle();
+ if( nRotateAngle )
+ pObj->NbcRotate( aRect.Center(), nRotateAngle );
+ if ( bFlipH )
+ {
+ Point aTop( ( aRect.Left() + aRect.Right() ) >> 1, aRect.Top() );
+ Point aBottom( aTop.X(), aTop.Y() + 1000 );
+ pObj->NbcMirror( aTop, aBottom );
+ }
+ if ( bFlipV )
+ {
+ Point aLeft( aRect.Left(), ( aRect.Top() + aRect.Bottom() ) >> 1 );
+ Point aRight( aLeft.X() + 1000, aLeft.Y() );
+ pObj->NbcMirror( aLeft, aRight );
+ }
+
+ basegfx::B2DPolyPolygon aPolyPolygon;
+ SdrObjListIter aIter( *pObj, SdrIterMode::DeepWithGroups );
+
+ while ( aIter.IsMore() )
+ {
+ basegfx::B2DPolyPolygon aPP;
+ const SdrObject* pNext = aIter.Next();
+
+ if ( auto pPathObj = dynamic_cast<const SdrPathObj*>(pNext) )
+ {
+ aPP = pPathObj->GetPathPoly();
+ }
+ else
+ {
+ SdrObjectUniquePtr pNewObj = pNext->ConvertToPolyObj( false, false );
+ SdrPathObj* pPath = dynamic_cast<SdrPathObj*>( pNewObj.get() );
+ if ( pPath )
+ aPP = pPath->GetPathPoly();
+ }
+
+ if ( aPP.count() )
+ aPolyPolygon.append(aPP);
+ }
+ pObj.reset();
+ basegfx::utils::B2DPolyPolygonToUnoPolyPolygonBezierCoords( aPolyPolygon,
+ aPolyPolygonBezierCoords );
+ }
+ }
+
+ return aPolyPolygonBezierCoords;
+}
+
+Sequence< Reference< drawing::XCustomShapeHandle > > SAL_CALL EnhancedCustomShapeEngine::getInteraction()
+{
+ sal_uInt32 i, nHdlCount = 0;
+ SdrObjCustomShape* pSdrObjCustomShape = dynamic_cast< SdrObjCustomShape* >(SdrObject::getSdrObjectFromXShape(mxShape));
+
+ if(pSdrObjCustomShape)
+ {
+ EnhancedCustomShape2d aCustomShape2d(*pSdrObjCustomShape);
+ nHdlCount = aCustomShape2d.GetHdlCount();
+ }
+
+ Sequence< Reference< drawing::XCustomShapeHandle > > aSeq( nHdlCount );
+ auto aSeqRange = asNonConstRange(aSeq);
+
+ for ( i = 0; i < nHdlCount; i++ )
+ aSeqRange[ i ] = new EnhancedCustomShapeHandle( mxShape, i );
+ return aSeq;
+}
+
+}
+
+extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface *
+com_sun_star_drawing_EnhancedCustomShapeEngine_get_implementation(
+ css::uno::XComponentContext *,
+ css::uno::Sequence<css::uno::Any> const &)
+{
+ return cppu::acquire(new EnhancedCustomShapeEngine);
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/customshapes/EnhancedCustomShapeFontWork.cxx b/svx/source/customshapes/EnhancedCustomShapeFontWork.cxx
new file mode 100644
index 000000000..0043b69cd
--- /dev/null
+++ b/svx/source/customshapes/EnhancedCustomShapeFontWork.cxx
@@ -0,0 +1,956 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include "EnhancedCustomShapeFontWork.hxx"
+#include <svl/itemset.hxx>
+#include <svx/svddef.hxx>
+#include <svx/svdopath.hxx>
+#include <vcl/metric.hxx>
+#include <svx/sdasitm.hxx>
+#include <svx/sdtfsitm.hxx>
+#include <vcl/virdev.hxx>
+#include <svx/svditer.hxx>
+#include <editeng/eeitem.hxx>
+#include <editeng/frmdiritem.hxx>
+#include <editeng/fontitem.hxx>
+#include <editeng/postitem.hxx>
+#include <editeng/wghtitem.hxx>
+#include <editeng/fhgtitem.hxx>
+#include <editeng/charscaleitem.hxx>
+#include <svx/svdoashp.hxx>
+#include <svx/sdshitm.hxx>
+#include <editeng/outlobj.hxx>
+#include <editeng/editobj.hxx>
+#include <o3tl/numeric.hxx>
+#include <vector>
+#include <numeric>
+#include <algorithm>
+#include <comphelper/processfactory.hxx>
+#include <com/sun/star/i18n/BreakIterator.hpp>
+#include <com/sun/star/i18n/ScriptType.hpp>
+#include <basegfx/polygon/b2dpolypolygontools.hxx>
+#include <basegfx/polygon/b2dpolygontools.hxx>
+#include <sal/log.hxx>
+#include <rtl/math.hxx>
+
+using namespace com::sun::star;
+using namespace com::sun::star::uno;
+
+namespace {
+
+struct FWCharacterData // representing a single character
+{
+ std::vector< tools::PolyPolygon > vOutlines;
+ tools::Rectangle aBoundRect;
+};
+struct FWParagraphData // representing a single paragraph
+{
+ OUString aString;
+ std::vector< FWCharacterData > vCharacters;
+ tools::Rectangle aBoundRect;
+ SvxFrameDirection nFrameDirection;
+};
+struct FWTextArea // representing multiple concluding paragraphs
+{
+ std::vector< FWParagraphData > vParagraphs;
+ tools::Rectangle aBoundRect;
+};
+struct FWData // representing the whole text
+{
+ std::vector< FWTextArea > vTextAreas;
+ double fHorizontalTextScaling;
+ double fVerticalTextScaling;
+ sal_uInt32 nMaxParagraphsPerTextArea;
+ sal_Int32 nSingleLineHeight;
+ bool bSingleLineMode;
+ bool bScaleX;
+};
+
+}
+
+static bool InitializeFontWorkData(
+ const SdrObjCustomShape& rSdrObjCustomShape,
+ const sal_uInt16 nOutlinesCount2d,
+ FWData& rFWData)
+{
+ bool bNoErr = false;
+ bool bSingleLineMode = false;
+ sal_uInt16 nTextAreaCount = nOutlinesCount2d;
+ if ( nOutlinesCount2d & 1 )
+ bSingleLineMode = true;
+ else
+ nTextAreaCount >>= 1;
+
+ const SdrCustomShapeGeometryItem& rGeometryItem( rSdrObjCustomShape.GetMergedItem( SDRATTR_CUSTOMSHAPE_GEOMETRY ) );
+ const css::uno::Any* pAny = rGeometryItem.GetPropertyValueByName( "TextPath", "ScaleX" );
+ if (pAny)
+ *pAny >>= rFWData.bScaleX;
+ else
+ rFWData.bScaleX = false;
+
+ if ( nTextAreaCount )
+ {
+ rFWData.bSingleLineMode = bSingleLineMode;
+
+ // setting the strings
+ OutlinerParaObject* pParaObj(rSdrObjCustomShape.GetOutlinerParaObject());
+
+ if ( pParaObj )
+ {
+ const EditTextObject& rTextObj = pParaObj->GetTextObject();
+ sal_Int32 nParagraphsLeft = rTextObj.GetParagraphCount();
+
+ rFWData.nMaxParagraphsPerTextArea = ( ( nParagraphsLeft - 1 ) / nTextAreaCount ) + 1;
+ sal_Int32 j = 0;
+ while( nParagraphsLeft && nTextAreaCount )
+ {
+ FWTextArea aTextArea;
+ sal_Int32 i, nParagraphs = ( ( nParagraphsLeft - 1 ) / nTextAreaCount ) + 1;
+ for ( i = 0; i < nParagraphs; ++i, ++j )
+ {
+ FWParagraphData aParagraphData;
+ aParagraphData.aString = rTextObj.GetText( j );
+
+ const SfxItemSet& rParaSet = rTextObj.GetParaAttribs( j ); // retrieving some paragraph attributes
+ aParagraphData.nFrameDirection = rParaSet.Get( EE_PARA_WRITINGDIR ).GetValue();
+ aTextArea.vParagraphs.push_back( aParagraphData );
+ }
+ rFWData.vTextAreas.push_back( aTextArea );
+ nParagraphsLeft -= nParagraphs;
+ nTextAreaCount--;
+ }
+ bNoErr = true;
+ }
+ }
+ return bNoErr;
+}
+
+static double GetLength( const tools::Polygon& rPolygon )
+{
+ double fLength = 0;
+ if ( rPolygon.GetSize() > 1 )
+ {
+ sal_uInt16 nCount = rPolygon.GetSize();
+ while( --nCount )
+ fLength += rPolygon.CalcDistance( nCount, nCount - 1 );
+ }
+ return fLength;
+}
+
+
+/* CalculateHorizontalScalingFactor returns the horizontal scaling factor for
+the whole text object, so that each text will match its corresponding 2d Outline */
+static void CalculateHorizontalScalingFactor(
+ const SdrObjCustomShape& rSdrObjCustomShape,
+ FWData& rFWData,
+ const tools::PolyPolygon& rOutline2d)
+{
+ double fScalingFactor = 1.0;
+ rFWData.fVerticalTextScaling = 1.0;
+
+ sal_uInt16 i = 0;
+ bool bSingleLineMode = false;
+ sal_uInt16 nOutlinesCount2d = rOutline2d.Count();
+
+ vcl::Font aFont;
+ const SvxFontItem& rFontItem( rSdrObjCustomShape.GetMergedItem( EE_CHAR_FONTINFO ) );
+ const SvxFontHeightItem& rFontHeight( rSdrObjCustomShape.GetMergedItem( EE_CHAR_FONTHEIGHT ) );
+ sal_Int32 nFontSize = rFontHeight.GetHeight();
+
+ SAL_WARN_IF(nFontSize > SAL_MAX_INT16, "svx", "CalculateHorizontalScalingFactor suspiciously large font height: " << nFontSize);
+
+ if (rFWData.bScaleX)
+ aFont.SetFontHeight( nFontSize );
+ else
+ aFont.SetFontHeight( rSdrObjCustomShape.GetLogicRect().GetHeight() / rFWData.nMaxParagraphsPerTextArea );
+
+ aFont.SetAlignment( ALIGN_TOP );
+ aFont.SetFamilyName( rFontItem.GetFamilyName() );
+ aFont.SetFamily( rFontItem.GetFamily() );
+ aFont.SetStyleName( rFontItem.GetStyleName() );
+ const SvxPostureItem& rPostureItem = rSdrObjCustomShape.GetMergedItem( EE_CHAR_ITALIC );
+ aFont.SetItalic( rPostureItem.GetPosture() );
+
+ const SvxWeightItem& rWeightItem = rSdrObjCustomShape.GetMergedItem( EE_CHAR_WEIGHT );
+ aFont.SetWeight( rWeightItem.GetWeight() );
+ aFont.SetOrientation( 0_deg10 );
+ // initializing virtual device
+
+ ScopedVclPtrInstance< VirtualDevice > pVirDev(DeviceFormat::DEFAULT);
+ pVirDev->SetMapMode(MapMode(MapUnit::Map100thMM));
+ pVirDev->SetFont( aFont );
+
+ if ( nOutlinesCount2d & 1 )
+ bSingleLineMode = true;
+
+ // In case of rFWData.bScaleX == true it loops with reduced font size until the current run
+ // results in a fScalingFactor >=1.0. The fact, that case rFWData.bScaleX == true keeps font
+ // size if possible, is not done here with scaling factor 1 but is done in method
+ // FitTextOutlinesToShapeOutlines()
+ do
+ {
+ i = 0;
+ bool bScalingFactorDefined = false; // New calculation for each font size
+ for( const auto& rTextArea : rFWData.vTextAreas )
+ {
+ // calculating the width of the corresponding 2d text area
+ double fWidth = GetLength( rOutline2d.GetObject( i++ ) );
+ if ( !bSingleLineMode )
+ {
+ fWidth += GetLength( rOutline2d.GetObject( i++ ) );
+ fWidth /= 2.0;
+ }
+
+ for( const auto& rParagraph : rTextArea.vParagraphs )
+ {
+ double fTextWidth = pVirDev->GetTextWidth( rParagraph.aString );
+ if ( fTextWidth > 0.0 )
+ {
+ double fScale = fWidth / fTextWidth;
+ if ( !bScalingFactorDefined )
+ {
+ fScalingFactor = fScale;
+ bScalingFactorDefined = true;
+ }
+ else if (fScale < fScalingFactor)
+ {
+ fScalingFactor = fScale;
+ }
+ }
+ }
+ }
+
+ if (fScalingFactor < 1.0)
+ {
+ // if we have a very large font that will require scaling down to a very small value then
+ // skip directly to a small font size
+ if (nFontSize > 128)
+ {
+ double nEstimatedFinalFontSize = nFontSize * fScalingFactor;
+ double nOnePercentFontSize = nFontSize / 100.0;
+ if (nEstimatedFinalFontSize < nOnePercentFontSize)
+ {
+ nFontSize = std::max<int>(16, std::ceil(5 * nEstimatedFinalFontSize));
+ SAL_WARN("svx", "CalculateHorizontalScalingFactor skipping direct to: " << nFontSize << " from " << rFontHeight.GetHeight());
+ }
+ }
+ nFontSize--;
+ aFont.SetFontHeight( nFontSize );
+ pVirDev->SetFont( aFont );
+ }
+ }
+ while (rFWData.bScaleX && fScalingFactor < 1.0 && nFontSize > 1 );
+
+ if (nFontSize > 1)
+ rFWData.fVerticalTextScaling = static_cast<double>(nFontSize) / rFontHeight.GetHeight();
+
+ rFWData.fHorizontalTextScaling = fScalingFactor;
+}
+
+static void GetTextAreaOutline(
+ const FWData& rFWData,
+ const SdrObjCustomShape& rSdrObjCustomShape,
+ FWTextArea& rTextArea,
+ bool bSameLetterHeights)
+{
+ bool bIsVertical(rSdrObjCustomShape.IsVerticalWriting());
+ sal_Int32 nVerticalOffset = rFWData.nMaxParagraphsPerTextArea > rTextArea.vParagraphs.size()
+ ? rFWData.nSingleLineHeight / 2 : 0;
+
+ for( auto& rParagraph : rTextArea.vParagraphs )
+ {
+ const OUString& rText = rParagraph.aString;
+ if ( !rText.isEmpty() )
+ {
+ // generating vcl/font
+ sal_uInt16 nScriptType = i18n::ScriptType::LATIN;
+ Reference< i18n::XBreakIterator > xBI( EnhancedCustomShapeFontWork::GetBreakIterator() );
+ if ( xBI.is() )
+ {
+ nScriptType = xBI->getScriptType( rText, 0 );
+ if( i18n::ScriptType::WEAK == nScriptType )
+ {
+ sal_Int32 nChg = xBI->endOfScript( rText, 0, nScriptType );
+ if (nChg < rText.getLength() && nChg >= 0)
+ nScriptType = xBI->getScriptType( rText, nChg );
+ else
+ nScriptType = i18n::ScriptType::LATIN;
+ }
+ }
+ sal_uInt16 nFntItm = EE_CHAR_FONTINFO;
+ if ( nScriptType == i18n::ScriptType::COMPLEX )
+ nFntItm = EE_CHAR_FONTINFO_CTL;
+ else if ( nScriptType == i18n::ScriptType::ASIAN )
+ nFntItm = EE_CHAR_FONTINFO_CJK;
+ const SvxFontItem& rFontItem = static_cast<const SvxFontItem&>(rSdrObjCustomShape.GetMergedItem( nFntItm ));
+ vcl::Font aFont;
+
+ aFont.SetFontHeight( rFWData.nSingleLineHeight );
+
+ aFont.SetAlignment( ALIGN_TOP );
+
+ aFont.SetFamilyName( rFontItem.GetFamilyName() );
+ aFont.SetFamily( rFontItem.GetFamily() );
+ aFont.SetStyleName( rFontItem.GetStyleName() );
+ aFont.SetOrientation( 0_deg10 );
+
+ const SvxPostureItem& rPostureItem = rSdrObjCustomShape.GetMergedItem( EE_CHAR_ITALIC );
+ aFont.SetItalic( rPostureItem.GetPosture() );
+
+ const SvxWeightItem& rWeightItem = rSdrObjCustomShape.GetMergedItem( EE_CHAR_WEIGHT );
+ aFont.SetWeight( rWeightItem.GetWeight() );
+
+ // initializing virtual device
+ ScopedVclPtrInstance< VirtualDevice > pVirDev(DeviceFormat::DEFAULT);
+ pVirDev->SetMapMode(MapMode(MapUnit::Map100thMM));
+ pVirDev->SetFont( aFont );
+ pVirDev->EnableRTL();
+ if ( rParagraph.nFrameDirection == SvxFrameDirection::Horizontal_RL_TB )
+ pVirDev->SetLayoutMode( vcl::text::ComplexTextLayoutFlags::BiDiRtl );
+
+ const SvxCharScaleWidthItem& rCharScaleWidthItem = rSdrObjCustomShape.GetMergedItem( EE_CHAR_FONTWIDTH );
+ sal_uInt16 nCharScaleWidth = rCharScaleWidthItem.GetValue();
+ sal_Int32 nWidth = 0;
+
+ // VERTICAL
+ if ( bIsVertical )
+ {
+ // vertical _> each single character needs to be rotated by 90
+ sal_Int32 i;
+ sal_Int32 nHeight = 0;
+ tools::Rectangle aSingleCharacterUnion;
+ for ( i = 0; i < rText.getLength(); i++ )
+ {
+ FWCharacterData aCharacterData;
+ OUString aCharText( rText[ i ] );
+ if ( pVirDev->GetTextOutlines( aCharacterData.vOutlines, aCharText, 0, 0, -1, nWidth, {} ) )
+ {
+ sal_Int32 nTextWidth = pVirDev->GetTextWidth( aCharText);
+ if ( aCharacterData.vOutlines.empty() )
+ {
+ nHeight += rFWData.nSingleLineHeight;
+ }
+ else
+ {
+ for ( auto& rOutline : aCharacterData.vOutlines )
+ {
+ // rotating
+ rOutline.Rotate( Point( nTextWidth / 2, rFWData.nSingleLineHeight / 2 ), 900_deg10 );
+ aCharacterData.aBoundRect.Union( rOutline.GetBoundRect() );
+ }
+ for ( auto& rOutline : aCharacterData.vOutlines )
+ {
+ sal_Int32 nM = - aCharacterData.aBoundRect.Left() + nHeight;
+ rOutline.Move( nM, 0 );
+ aCharacterData.aBoundRect.Move( nM, 0 );
+ }
+ nHeight += aCharacterData.aBoundRect.GetWidth() + ( rFWData.nSingleLineHeight / 5 );
+ aSingleCharacterUnion.Union( aCharacterData.aBoundRect );
+ }
+ }
+ rParagraph.vCharacters.push_back( aCharacterData );
+ }
+ for ( auto& rCharacter : rParagraph.vCharacters )
+ {
+ for ( auto& rOutline : rCharacter.vOutlines )
+ {
+ rOutline.Move( ( aSingleCharacterUnion.GetWidth() - rCharacter.aBoundRect.GetWidth() ) / 2, 0 );
+ }
+ }
+ }
+ else
+ {
+ std::vector<sal_Int32> aDXArry;
+ if ( ( nCharScaleWidth != 100 ) && nCharScaleWidth )
+ { // applying character spacing
+ pVirDev->GetTextArray( rText, &aDXArry);
+ FontMetric aFontMetric( pVirDev->GetFontMetric() );
+ aFont.SetAverageFontWidth( static_cast<sal_Int32>( static_cast<double>(aFontMetric.GetAverageFontWidth()) * ( double(100) / static_cast<double>(nCharScaleWidth) ) ) );
+ pVirDev->SetFont( aFont );
+ }
+ FWCharacterData aCharacterData;
+ if ( pVirDev->GetTextOutlines( aCharacterData.vOutlines, rText, 0, 0, -1, nWidth, aDXArry ) )
+ {
+ rParagraph.vCharacters.push_back( aCharacterData );
+ }
+ else
+ {
+ // GetTextOutlines failed what usually means that it is
+ // not implemented. To make FontWork not fail (it is
+ // dependent of graphic content to get a Range) create
+ // a rectangle substitution for now
+ pVirDev->GetTextArray( rText, &aDXArry);
+ aCharacterData.vOutlines.clear();
+
+ if(aDXArry.size())
+ {
+ for(size_t a(0); a < aDXArry.size(); a++)
+ {
+ const basegfx::B2DPolygon aPolygon(
+ basegfx::utils::createPolygonFromRect(
+ basegfx::B2DRange(
+ 0 == a ? 0 : aDXArry[a - 1],
+ 0,
+ aDXArry[a],
+ aFont.GetFontHeight()
+ )));
+ aCharacterData.vOutlines.push_back(tools::PolyPolygon(tools::Polygon(aPolygon)));
+ }
+ }
+ else
+ {
+ const basegfx::B2DPolygon aPolygon(
+ basegfx::utils::createPolygonFromRect(
+ basegfx::B2DRange(
+ 0,
+ 0,
+ aDXArry.empty() ? 10 : aDXArry.back(),
+ aFont.GetFontHeight()
+ )));
+ aCharacterData.vOutlines.push_back(tools::PolyPolygon(tools::Polygon(aPolygon)));
+ }
+
+
+ rParagraph.vCharacters.push_back( aCharacterData );
+ }
+ }
+
+ // vertical alignment
+ for ( auto& rCharacter : rParagraph.vCharacters )
+ {
+ for( tools::PolyPolygon& rPolyPoly : rCharacter.vOutlines )
+ {
+ if ( nVerticalOffset )
+ rPolyPoly.Move( 0, nVerticalOffset );
+
+ // retrieving the boundrect for the paragraph
+ tools::Rectangle aBoundRect( rPolyPoly.GetBoundRect() );
+ rParagraph.aBoundRect.Union( aBoundRect );
+ }
+ }
+ }
+ // updating the boundrect for the text area by merging the current paragraph boundrect
+ if ( rParagraph.aBoundRect.IsEmpty() )
+ {
+ if ( rTextArea.aBoundRect.IsEmpty() )
+ rTextArea.aBoundRect = tools::Rectangle( Point( 0, 0 ), Size( 1, rFWData.nSingleLineHeight ) );
+ else
+ rTextArea.aBoundRect.AdjustBottom(rFWData.nSingleLineHeight );
+ }
+ else
+ {
+ tools::Rectangle& rParagraphBoundRect = rParagraph.aBoundRect;
+ rTextArea.aBoundRect.Union( rParagraphBoundRect );
+
+ if ( bSameLetterHeights )
+ {
+ for ( auto& rCharacter : rParagraph.vCharacters )
+ {
+ for( auto& rOutline : rCharacter.vOutlines )
+ {
+ tools::Rectangle aPolyPolyBoundRect( rOutline.GetBoundRect() );
+ if (aPolyPolyBoundRect.GetHeight() != rParagraphBoundRect.GetHeight() && aPolyPolyBoundRect.GetHeight())
+ rOutline.Scale( 1.0, static_cast<double>(rParagraphBoundRect.GetHeight()) / aPolyPolyBoundRect.GetHeight() );
+ aPolyPolyBoundRect = rOutline.GetBoundRect();
+ sal_Int32 nMove = aPolyPolyBoundRect.Top() - rParagraphBoundRect.Top();
+ if ( nMove )
+ rOutline.Move( 0, -nMove );
+ }
+ }
+ }
+ }
+ if ( bIsVertical )
+ nVerticalOffset -= rFWData.nSingleLineHeight;
+ else
+ nVerticalOffset += rFWData.nSingleLineHeight;
+ }
+}
+
+static bool GetFontWorkOutline(
+ FWData& rFWData,
+ const SdrObjCustomShape& rSdrObjCustomShape)
+{
+ SdrTextHorzAdjust eHorzAdjust(rSdrObjCustomShape.GetMergedItem( SDRATTR_TEXT_HORZADJUST ).GetValue());
+ drawing::TextFitToSizeType const eFTS(rSdrObjCustomShape.GetMergedItem( SDRATTR_TEXT_FITTOSIZE ).GetValue());
+
+ bool bSameLetterHeights = false;
+ const SdrCustomShapeGeometryItem& rGeometryItem(rSdrObjCustomShape.GetMergedItem( SDRATTR_CUSTOMSHAPE_GEOMETRY ));
+ const css::uno::Any* pAny = rGeometryItem.GetPropertyValueByName( "TextPath", "SameLetterHeights" );
+ if ( pAny )
+ *pAny >>= bSameLetterHeights;
+
+ const SvxFontHeightItem& rFontHeight( rSdrObjCustomShape.GetMergedItem( EE_CHAR_FONTHEIGHT ) );
+ if (rFWData.bScaleX)
+ rFWData.nSingleLineHeight = rFWData.fVerticalTextScaling * rFontHeight.GetHeight();
+ else
+ rFWData.nSingleLineHeight = static_cast<sal_Int32>( ( static_cast<double>( rSdrObjCustomShape.GetLogicRect().GetHeight() )
+ / rFWData.nMaxParagraphsPerTextArea ) * rFWData.fHorizontalTextScaling );
+
+ if (rFWData.nSingleLineHeight == SAL_MIN_INT32)
+ return false;
+
+ for ( auto& rTextArea : rFWData.vTextAreas )
+ {
+ GetTextAreaOutline(
+ rFWData,
+ rSdrObjCustomShape,
+ rTextArea,
+ bSameLetterHeights);
+
+ if (eFTS == drawing::TextFitToSizeType_ALLLINES ||
+ // tdf#97630 interpret PROPORTIONAL same as ALLLINES so we don't
+ // need another ODF attribute!
+ eFTS == drawing::TextFitToSizeType_PROPORTIONAL)
+ {
+ for ( auto& rParagraph : rTextArea.vParagraphs )
+ {
+ sal_Int32 nParaWidth = rParagraph.aBoundRect.GetWidth();
+ if ( nParaWidth )
+ {
+ double fScale = static_cast<double>(rTextArea.aBoundRect.GetWidth()) / nParaWidth;
+
+ for ( auto& rCharacter : rParagraph.vCharacters )
+ {
+ for( auto& rOutline : rCharacter.vOutlines )
+ {
+ rOutline.Scale( fScale, 1.0 );
+ }
+ }
+ }
+ }
+ }
+ else if (rFWData.bScaleX)
+ {
+ const SdrTextVertAdjust nVertJustify = rSdrObjCustomShape.GetMergedItem( SDRATTR_TEXT_VERTADJUST ).GetValue();
+ double fFactor = nVertJustify == SdrTextVertAdjust::SDRTEXTVERTADJUST_BOTTOM ? -0.5 : ( nVertJustify == SdrTextVertAdjust::SDRTEXTVERTADJUST_TOP ? 0.5 : 0 );
+
+ for ( auto& rParagraph : rTextArea.vParagraphs )
+ {
+ sal_Int32 nHorzDiff = 0;
+ sal_Int32 nVertDiff = static_cast<double>( rFWData.nSingleLineHeight ) * fFactor * ( rTextArea.vParagraphs.size() - 1 );
+
+ if ( eHorzAdjust == SDRTEXTHORZADJUST_CENTER )
+ nHorzDiff = ( rFWData.fHorizontalTextScaling * rTextArea.aBoundRect.GetWidth() - rParagraph.aBoundRect.GetWidth() ) / 2;
+ else if ( eHorzAdjust == SDRTEXTHORZADJUST_RIGHT )
+ nHorzDiff = ( rFWData.fHorizontalTextScaling * rTextArea.aBoundRect.GetWidth() - rParagraph.aBoundRect.GetWidth() );
+
+ if (nHorzDiff || nVertDiff)
+ {
+ for ( auto& rCharacter : rParagraph.vCharacters )
+ {
+ for( auto& rOutline : rCharacter.vOutlines )
+ {
+ rOutline.Move( nHorzDiff, nVertDiff );
+ }
+ }
+ }
+ }
+ }
+ else
+ {
+ switch( eHorzAdjust )
+ {
+ case SDRTEXTHORZADJUST_RIGHT :
+ case SDRTEXTHORZADJUST_CENTER:
+ {
+ for ( auto& rParagraph : rTextArea.vParagraphs )
+ {
+ sal_Int32 nHorzDiff = 0;
+ if ( eHorzAdjust == SDRTEXTHORZADJUST_CENTER )
+ nHorzDiff = ( rTextArea.aBoundRect.GetWidth() - rParagraph.aBoundRect.GetWidth() ) / 2;
+ else if ( eHorzAdjust == SDRTEXTHORZADJUST_RIGHT )
+ nHorzDiff = ( rTextArea.aBoundRect.GetWidth() - rParagraph.aBoundRect.GetWidth() );
+ if ( nHorzDiff )
+ {
+ for ( auto& rCharacter : rParagraph.vCharacters )
+ {
+ for( auto& rOutline : rCharacter.vOutlines )
+ {
+ rOutline.Move( nHorzDiff, 0 );
+ }
+ }
+ }
+ }
+ }
+ break;
+ default:
+ case SDRTEXTHORZADJUST_BLOCK : break; // don't know
+ case SDRTEXTHORZADJUST_LEFT : break; // already left aligned -> nothing to do
+ }
+ }
+ }
+
+ return true;
+}
+
+static basegfx::B2DPolyPolygon GetOutlinesFromShape2d( const SdrObject* pShape2d )
+{
+ basegfx::B2DPolyPolygon aOutlines2d;
+
+ SdrObjListIter aObjListIter( *pShape2d, SdrIterMode::DeepWithGroups );
+ while( aObjListIter.IsMore() )
+ {
+ SdrObject* pPartObj = aObjListIter.Next();
+ if ( auto pPathObj = dynamic_cast<const SdrPathObj*>( pPartObj))
+ {
+ basegfx::B2DPolyPolygon aCandidate(pPathObj->GetPathPoly());
+ if(aCandidate.areControlPointsUsed())
+ {
+ aCandidate = basegfx::utils::adaptiveSubdivideByAngle(aCandidate);
+ }
+ aOutlines2d.append(aCandidate);
+ }
+ }
+
+ return aOutlines2d;
+}
+
+static void CalcDistances( const tools::Polygon& rPoly, std::vector< double >& rDistances )
+{
+ sal_uInt16 i, nCount = rPoly.GetSize();
+ if ( nCount <= 1 )
+ return;
+
+ for ( i = 0; i < nCount; i++ )
+ {
+ double fDistance = i ? rPoly.CalcDistance( i, i - 1 ) : 0.0;
+ rDistances.push_back( fDistance );
+ }
+ std::partial_sum( rDistances.begin(), rDistances.end(), rDistances.begin() );
+ double fLength = rDistances[ rDistances.size() - 1 ];
+ if ( fLength > 0.0 )
+ {
+ for ( auto& rDistance : rDistances )
+ rDistance /= fLength;
+ }
+}
+
+static void InsertMissingOutlinePoints( const std::vector< double >& rDistances,
+ const tools::Rectangle& rTextAreaBoundRect, tools::Polygon& rPoly )
+{
+ sal_uInt16 nSize = rPoly.GetSize();
+ if (nSize == 0)
+ return;
+
+ tools::Long nTextWidth = rTextAreaBoundRect.GetWidth();
+
+ if (nTextWidth == 0)
+ throw o3tl::divide_by_zero();
+
+ double fLastDistance = 0.0;
+ for (sal_uInt16 i = 0; i < nSize; ++i)
+ {
+ Point& rPoint = rPoly[ i ];
+ double fDistance = static_cast<double>( rPoint.X() - rTextAreaBoundRect.Left() ) / static_cast<double>(nTextWidth);
+ if ( i )
+ {
+ if ( fDistance > fLastDistance )
+ {
+ std::vector< double >::const_iterator aIter = std::upper_bound( rDistances.begin(), rDistances.end(), fLastDistance );
+ if ( aIter != rDistances.end() && ( *aIter > fLastDistance ) && ( *aIter < fDistance ) )
+ {
+ Point& rPt0 = rPoly[ i - 1 ];
+ sal_Int32 fX = rPoint.X() - rPt0.X();
+ sal_Int32 fY = rPoint.Y() - rPt0.Y();
+ double fd = ( 1.0 / ( fDistance - fLastDistance ) ) * ( *aIter - fLastDistance );
+ rPoly.Insert( i, Point( static_cast<sal_Int32>( rPt0.X() + fX * fd ), static_cast<sal_Int32>( rPt0.Y() + fY * fd ) ) );
+ fDistance = *aIter;
+ }
+ }
+ else if ( fDistance < fLastDistance )
+ {
+ std::vector< double >::const_iterator aIter = std::lower_bound( rDistances.begin(), rDistances.end(), fLastDistance );
+ if ( aIter != rDistances.begin() )
+ {
+ --aIter;
+ if ( ( *aIter > fDistance ) && ( *aIter < fLastDistance ) )
+ {
+ Point& rPt0 = rPoly[ i - 1 ];
+ sal_Int32 fX = rPoint.X() - rPt0.X();
+ sal_Int32 fY = rPoint.Y() - rPt0.Y();
+ double fd = ( 1.0 / ( fDistance - fLastDistance ) ) * ( *aIter - fLastDistance );
+ rPoly.Insert( i, Point( static_cast<sal_Int32>( rPt0.X() + fX * fd ), static_cast<sal_Int32>( rPt0.Y() + fY * fd ) ) );
+ fDistance = *aIter;
+ }
+ }
+ }
+ }
+ fLastDistance = fDistance;
+ }
+}
+
+static void GetPoint( const tools::Polygon& rPoly, const std::vector< double >& rDistances, const double& fX, double& fx1, double& fy1 )
+{
+ fy1 = fx1 = 0.0;
+ if ( rPoly.GetSize() <= 1 )
+ return;
+
+ std::vector< double >::const_iterator aIter = std::lower_bound( rDistances.begin(), rDistances.end(), fX );
+ sal_uInt16 nIdx = sal::static_int_cast<sal_uInt16>( std::distance( rDistances.begin(), aIter ) );
+ if ( aIter == rDistances.end() )
+ nIdx--;
+ const Point& rPt = rPoly[ nIdx ];
+ fx1 = rPt.X();
+ fy1 = rPt.Y();
+ if ( !nIdx || ( aIter == rDistances.end() ) || rtl::math::approxEqual( *aIter, fX ) )
+ return;
+
+ nIdx = sal::static_int_cast<sal_uInt16>( std::distance( rDistances.begin(), aIter ) );
+ double fDist0 = *( aIter - 1 );
+ double fd = ( 1.0 / ( *aIter - fDist0 ) ) * ( fX - fDist0 );
+ const Point& rPt2 = rPoly[ nIdx - 1 ];
+ double fWidth = rPt.X() - rPt2.X();
+ double fHeight= rPt.Y() - rPt2.Y();
+ fWidth *= fd;
+ fHeight*= fd;
+ fx1 = rPt2.X() + fWidth;
+ fy1 = rPt2.Y() + fHeight;
+}
+
+static void FitTextOutlinesToShapeOutlines( const tools::PolyPolygon& aOutlines2d, FWData& rFWData )
+{
+ sal_uInt16 nOutline2dIdx = 0;
+ for( auto& rTextArea : rFWData.vTextAreas )
+ {
+ tools::Rectangle rTextAreaBoundRect = rTextArea.aBoundRect;
+ sal_Int32 nLeft = rTextAreaBoundRect.Left();
+ sal_Int32 nTop = rTextAreaBoundRect.Top();
+ sal_Int32 nWidth = rTextAreaBoundRect.GetWidth();
+ sal_Int32 nHeight= rTextAreaBoundRect.GetHeight();
+
+ if (rFWData.bScaleX)
+ {
+ nWidth *= rFWData.fHorizontalTextScaling;
+ }
+
+ if ( rFWData.bSingleLineMode && nHeight && nWidth )
+ {
+ if ( nOutline2dIdx >= aOutlines2d.Count() )
+ break;
+ const tools::Polygon& rOutlinePoly( aOutlines2d[ nOutline2dIdx++ ] );
+ const sal_uInt16 nPointCount = rOutlinePoly.GetSize();
+ if ( nPointCount > 1 )
+ {
+ std::vector< double > vDistances;
+ vDistances.reserve( nPointCount );
+ CalcDistances( rOutlinePoly, vDistances );
+ if ( !vDistances.empty() )
+ {
+ for( auto& rParagraph : rTextArea.vParagraphs )
+ {
+ for ( auto& rCharacter : rParagraph.vCharacters )
+ {
+ for( tools::PolyPolygon& rPolyPoly : rCharacter.vOutlines )
+ {
+ tools::Rectangle aBoundRect( rPolyPoly.GetBoundRect() );
+ double fx1 = aBoundRect.Left() - nLeft;
+ double fx2 = aBoundRect.Right() - nLeft;
+ double fy1, fy2;
+ double fM1 = fx1 / static_cast<double>(nWidth);
+ double fM2 = fx2 / static_cast<double>(nWidth);
+
+ GetPoint( rOutlinePoly, vDistances, fM1, fx1, fy1 );
+ GetPoint( rOutlinePoly, vDistances, fM2, fx2, fy2 );
+
+ double fvx = fy2 - fy1;
+ double fvy = - ( fx2 - fx1 );
+ fx1 = fx1 + ( ( fx2 - fx1 ) * 0.5 );
+ fy1 = fy1 + ( ( fy2 - fy1 ) * 0.5 );
+
+ double fAngle = atan2( -fvx, -fvy );
+ double fL = hypot( fvx, fvy );
+ if (fL == 0.0)
+ {
+ SAL_WARN("svx", "FitTextOutlinesToShapeOutlines div-by-zero, abandon fit");
+ break;
+ }
+ fvx = fvx / fL;
+ fvy = fvy / fL;
+ fL = rTextArea.aBoundRect.GetHeight() / 2.0 + rTextArea.aBoundRect.Top() - rParagraph.aBoundRect.Center().Y();
+ fvx *= fL;
+ fvy *= fL;
+ rPolyPoly.Rotate( Point( aBoundRect.Center().X(), rParagraph.aBoundRect.Center().Y() ), sin( fAngle ), cos( fAngle ) );
+ rPolyPoly.Move( static_cast<sal_Int32>( ( fx1 + fvx )- aBoundRect.Center().X() ), static_cast<sal_Int32>( ( fy1 + fvy ) - rParagraph.aBoundRect.Center().Y() ) );
+ }
+ }
+ }
+ }
+ }
+ }
+ else
+ {
+ if ( ( nOutline2dIdx + 1 ) >= aOutlines2d.Count() )
+ break;
+ const tools::Polygon& rOutlinePoly( aOutlines2d[ nOutline2dIdx++ ] );
+ const tools::Polygon& rOutlinePoly2( aOutlines2d[ nOutline2dIdx++ ] );
+ const sal_uInt16 nPointCount = rOutlinePoly.GetSize();
+ const sal_uInt16 nPointCount2 = rOutlinePoly2.GetSize();
+ if ( ( nPointCount > 1 ) && ( nPointCount2 > 1 ) )
+ {
+ std::vector< double > vDistances;
+ vDistances.reserve( nPointCount );
+ std::vector< double > vDistances2;
+ vDistances2.reserve( nPointCount2 );
+ CalcDistances( rOutlinePoly, vDistances );
+ CalcDistances( rOutlinePoly2, vDistances2 );
+ for( auto& rParagraph : rTextArea.vParagraphs )
+ {
+ for ( auto& rCharacter : rParagraph.vCharacters )
+ {
+ for( tools::PolyPolygon& rPolyPoly : rCharacter.vOutlines )
+ {
+ sal_uInt16 i, nPolyCount = rPolyPoly.Count();
+ for ( i = 0; i < nPolyCount; i++ )
+ {
+ // #i35928#
+ basegfx::B2DPolygon aCandidate(rPolyPoly[ i ].getB2DPolygon());
+
+ if(aCandidate.areControlPointsUsed())
+ {
+ aCandidate = basegfx::utils::adaptiveSubdivideByAngle(aCandidate);
+ }
+
+ // create local polygon copy to work on
+ tools::Polygon aLocalPoly(aCandidate);
+
+ InsertMissingOutlinePoints( vDistances, rTextAreaBoundRect, aLocalPoly );
+ InsertMissingOutlinePoints( vDistances2, rTextAreaBoundRect, aLocalPoly );
+
+ sal_uInt16 _nPointCount = aLocalPoly.GetSize();
+ if (_nPointCount)
+ {
+ if (!nWidth || !nHeight)
+ throw o3tl::divide_by_zero();
+ for (sal_uInt16 j = 0; j < _nPointCount; ++j)
+ {
+ Point& rPoint = aLocalPoly[ j ];
+ rPoint.AdjustX( -nLeft );
+ rPoint.AdjustY( -nTop );
+ double fX = static_cast<double>(rPoint.X()) / static_cast<double>(nWidth);
+ double fY = static_cast<double>(rPoint.Y()) / static_cast<double>(nHeight);
+
+ double fx1, fy1, fx2, fy2;
+ GetPoint( rOutlinePoly, vDistances, fX, fx1, fy1 );
+ GetPoint( rOutlinePoly2, vDistances2, fX, fx2, fy2 );
+ double fWidth = fx2 - fx1;
+ double fHeight= fy2 - fy1;
+ rPoint.setX( static_cast<sal_Int32>( fx1 + fWidth * fY ) );
+ rPoint.setY( static_cast<sal_Int32>( fy1 + fHeight* fY ) );
+ }
+ }
+
+ // write back polygon
+ rPolyPoly[ i ] = aLocalPoly;
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+}
+
+static SdrObject* CreateSdrObjectFromParagraphOutlines(
+ const FWData& rFWData,
+ const SdrObjCustomShape& rSdrObjCustomShape)
+{
+ SdrObject* pRet = nullptr;
+ basegfx::B2DPolyPolygon aPolyPoly;
+ if ( !rFWData.vTextAreas.empty() )
+ {
+ for ( const auto& rTextArea : rFWData.vTextAreas )
+ {
+ for ( const auto& rParagraph : rTextArea.vParagraphs )
+ {
+ for ( const auto& rCharacter : rParagraph.vCharacters )
+ {
+ for( const auto& rOutline : rCharacter.vOutlines )
+ {
+ aPolyPoly.append( rOutline.getB2DPolyPolygon() );
+ }
+ }
+ }
+ }
+
+ pRet = new SdrPathObj(
+ rSdrObjCustomShape.getSdrModelFromSdrObject(),
+ SdrObjKind::Polygon,
+ aPolyPoly);
+
+ SfxItemSet aSet(rSdrObjCustomShape.GetMergedItemSet());
+ aSet.ClearItem( SDRATTR_TEXTDIRECTION ); //SJ: vertical writing is not required, by removing this item no outliner is created
+ aSet.Put(makeSdrShadowItem(false)); // #i37011# NO shadow for FontWork geometry
+ pRet->SetMergedItemSet( aSet ); // * otherwise we would crash, because the outliner tries to create a Paraobject, but there is no model
+ }
+
+ return pRet;
+}
+
+Reference < i18n::XBreakIterator > EnhancedCustomShapeFontWork::mxBreakIterator;
+
+Reference < i18n::XBreakIterator > const & EnhancedCustomShapeFontWork::GetBreakIterator()
+{
+ if ( !mxBreakIterator.is() )
+ {
+ Reference< uno::XComponentContext > xContext = ::comphelper::getProcessComponentContext();
+ mxBreakIterator = i18n::BreakIterator::create(xContext);
+ }
+ return mxBreakIterator;
+}
+
+SdrObject* EnhancedCustomShapeFontWork::CreateFontWork(
+ const SdrObject* pShape2d,
+ const SdrObjCustomShape& rSdrObjCustomShape)
+{
+ SdrObject* pRet = nullptr;
+
+ tools::PolyPolygon aOutlines2d( GetOutlinesFromShape2d( pShape2d ) );
+ sal_uInt16 nOutlinesCount2d = aOutlines2d.Count();
+ if ( nOutlinesCount2d )
+ {
+ FWData aFWData;
+
+ if(InitializeFontWorkData(rSdrObjCustomShape, nOutlinesCount2d, aFWData))
+ {
+ /* retrieves the horizontal scaling factor that has to be used
+ to fit each paragraph text into its corresponding 2d outline */
+ CalculateHorizontalScalingFactor(
+ rSdrObjCustomShape,
+ aFWData,
+ aOutlines2d);
+
+ /* retrieving the Outlines for the each Paragraph. */
+ if(!GetFontWorkOutline(
+ aFWData,
+ rSdrObjCustomShape))
+ {
+ return nullptr;
+ }
+
+ FitTextOutlinesToShapeOutlines( aOutlines2d, aFWData );
+
+ pRet = CreateSdrObjectFromParagraphOutlines(
+ aFWData,
+ rSdrObjCustomShape);
+ }
+ }
+ return pRet;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/customshapes/EnhancedCustomShapeFontWork.hxx b/svx/source/customshapes/EnhancedCustomShapeFontWork.hxx
new file mode 100644
index 000000000..335701be4
--- /dev/null
+++ b/svx/source/customshapes/EnhancedCustomShapeFontWork.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_SVX_SOURCE_CUSTOMSHAPES_ENHANCEDCUSTOMSHAPEFONTWORK_HXX
+#define INCLUDED_SVX_SOURCE_CUSTOMSHAPES_ENHANCEDCUSTOMSHAPEFONTWORK_HXX
+
+#include <com/sun/star/i18n/XBreakIterator.hpp>
+
+class SdrObject;
+class SdrObjCustomShape;
+
+class EnhancedCustomShapeFontWork
+{
+ static css::uno::Reference < css::i18n::XBreakIterator > mxBreakIterator;
+
+ public:
+
+ static css::uno::Reference < css::i18n::XBreakIterator > const & GetBreakIterator();
+ static SdrObject* CreateFontWork(
+ const SdrObject* pShape2d,
+ const SdrObjCustomShape& rSdrObjCustomShape);
+};
+
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/customshapes/EnhancedCustomShapeFunctionParser.cxx b/svx/source/customshapes/EnhancedCustomShapeFunctionParser.cxx
new file mode 100644
index 000000000..f7aa223f4
--- /dev/null
+++ b/svx/source/customshapes/EnhancedCustomShapeFunctionParser.cxx
@@ -0,0 +1,1164 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * 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 <svx/EnhancedCustomShape2d.hxx>
+#include <rtl/ustring.hxx>
+#include <sal/log.hxx>
+#include <tools/fract.hxx>
+
+#include <com/sun/star/drawing/EnhancedCustomShapeParameterType.hpp>
+
+// Makes parser a static resource,
+// we're synchronized externally.
+// But watch out, the parser might have
+// state not visible to this code!
+
+#define BOOST_SPIRIT_SINGLE_GRAMMAR_INSTANCE
+
+#if OSL_DEBUG_LEVEL >= 2 && defined(DBG_UTIL)
+#define BOOST_SPIRIT_DEBUG
+#endif
+#include <boost/spirit/include/classic_core.hpp>
+
+#include <functional>
+#include <algorithm>
+#include <stack>
+
+#include <math.h>
+using namespace EnhancedCustomShape;
+using namespace com::sun::star;
+using namespace com::sun::star::drawing;
+
+void EnhancedCustomShape::FillEquationParameter( const EnhancedCustomShapeParameter& rSource, const sal_Int32 nDestPara, EnhancedCustomShapeEquation& rDest )
+{
+ sal_Int32 nValue = 0;
+ if ( rSource.Value.getValueTypeClass() == uno::TypeClass_DOUBLE )
+ {
+ double fValue(0.0);
+ if ( rSource.Value >>= fValue )
+ nValue = static_cast<sal_Int32>(fValue);
+ }
+ else
+ rSource.Value >>= nValue;
+
+ switch( rSource.Type )
+ {
+ case css::drawing::EnhancedCustomShapeParameterType::EQUATION :
+ {
+ if ( nValue & 0x40000000 )
+ {
+ nValue ^= 0x40000000;
+ rDest.nOperation |= 0x20000000 << nDestPara; // the bit is indicating that this value has to be adjusted later
+ }
+ nValue |= 0x400;
+ }
+ break;
+ case css::drawing::EnhancedCustomShapeParameterType::ADJUSTMENT : nValue += DFF_Prop_adjustValue; break;
+ case css::drawing::EnhancedCustomShapeParameterType::BOTTOM : nValue = DFF_Prop_geoBottom; break;
+ case css::drawing::EnhancedCustomShapeParameterType::RIGHT : nValue = DFF_Prop_geoRight; break;
+ case css::drawing::EnhancedCustomShapeParameterType::TOP : nValue = DFF_Prop_geoTop; break;
+ case css::drawing::EnhancedCustomShapeParameterType::LEFT : nValue = DFF_Prop_geoLeft; break;
+ }
+ if ( rSource.Type != css::drawing::EnhancedCustomShapeParameterType::NORMAL )
+ rDest.nOperation |= ( 0x2000 << nDestPara );
+ rDest.nPara[ nDestPara ] = nValue;
+}
+
+ExpressionNode::~ExpressionNode()
+{}
+
+namespace
+{
+
+
+// EXPRESSION NODES
+
+
+class ConstantValueExpression : public ExpressionNode
+{
+ double maValue;
+
+public:
+
+ explicit ConstantValueExpression( double rValue ) :
+ maValue( rValue )
+ {
+ }
+ virtual double operator()() const override
+ {
+ return maValue;
+ }
+ virtual bool isConstant() const override
+ {
+ return true;
+ }
+ virtual ExpressionFunct getType() const override
+ {
+ return ExpressionFunct::Const;
+ }
+ virtual EnhancedCustomShapeParameter fillNode( std::vector< EnhancedCustomShapeEquation >& rEquations, ExpressionNode* /* pOptionalArg */, sal_uInt32 /* nFlags */ ) override
+ {
+ EnhancedCustomShapeParameter aRet;
+ Fraction aFract( maValue );
+ if ( aFract.GetDenominator() == 1 )
+ {
+ aRet.Type = EnhancedCustomShapeParameterType::NORMAL;
+ aRet.Value <<= aFract.GetNumerator();
+ }
+ else
+ {
+ EnhancedCustomShapeEquation aEquation;
+ aEquation.nOperation = 1;
+ aEquation.nPara[ 0 ] = 1;
+ aEquation.nPara[ 1 ] = static_cast<sal_Int16>(aFract.GetNumerator());
+ aEquation.nPara[ 2 ] = static_cast<sal_Int16>(aFract.GetDenominator());
+ aRet.Type = EnhancedCustomShapeParameterType::EQUATION;
+ aRet.Value <<= static_cast<sal_Int32>(rEquations.size());
+ rEquations.push_back( aEquation );
+ }
+ return aRet;
+ }
+};
+
+class AdjustmentExpression : public ExpressionNode
+{
+ sal_Int32 mnIndex;
+ const EnhancedCustomShape2d& mrCustoShape;
+
+public:
+
+ AdjustmentExpression( const EnhancedCustomShape2d& rCustoShape, sal_Int32 nIndex )
+ : mnIndex ( nIndex )
+ , mrCustoShape( rCustoShape )
+
+ {
+ }
+ virtual double operator()() const override
+ {
+ SAL_INFO(
+ "svx",
+ "$" << mnIndex << " --> "
+ << mrCustoShape.GetAdjustValueAsDouble(mnIndex) << " (angle: "
+ << 180.0*mrCustoShape.GetAdjustValueAsDouble(mnIndex)/10800000.0
+ << ")");
+ return mrCustoShape.GetAdjustValueAsDouble( mnIndex );
+ }
+ virtual bool isConstant() const override
+ {
+ return false;
+ }
+ virtual ExpressionFunct getType() const override
+ {
+ return ExpressionFunct::EnumAdjustment;
+ }
+ virtual EnhancedCustomShapeParameter fillNode( std::vector< EnhancedCustomShapeEquation >& /*rEquations*/, ExpressionNode* /*pOptionalArg*/, sal_uInt32 /*nFlags*/ ) override
+ {
+ EnhancedCustomShapeParameter aRet;
+ aRet.Type = EnhancedCustomShapeParameterType::ADJUSTMENT;
+ aRet.Value <<= mnIndex;
+ return aRet;
+ }
+};
+
+class EquationExpression : public ExpressionNode
+{
+ const sal_Int32 mnIndex;
+ const EnhancedCustomShape2d& mrCustoShape;
+ mutable bool mbGettingValueGuard;
+
+public:
+
+ EquationExpression( const EnhancedCustomShape2d& rCustoShape, sal_Int32 nIndex )
+ : mnIndex ( nIndex )
+ , mrCustoShape( rCustoShape )
+ , mbGettingValueGuard(false)
+ {
+ }
+ virtual double operator()() const override
+ {
+ if (mbGettingValueGuard)
+ throw ParseError("Loop in Expression");
+ mbGettingValueGuard = true;
+ double fRet = mrCustoShape.GetEquationValueAsDouble(mnIndex);
+ mbGettingValueGuard = false;
+ return fRet;
+ }
+ virtual bool isConstant() const override
+ {
+ return false;
+ }
+ virtual ExpressionFunct getType() const override
+ {
+ return ExpressionFunct::EnumEquation;
+ }
+ virtual EnhancedCustomShapeParameter fillNode( std::vector< EnhancedCustomShapeEquation >& /*rEquations*/, ExpressionNode* /*pOptionalArg*/, sal_uInt32 /*nFlags*/ ) override
+ {
+ EnhancedCustomShapeParameter aRet;
+ aRet.Type = EnhancedCustomShapeParameterType::EQUATION;
+ aRet.Value <<= mnIndex | 0x40000000; // the bit is indicating that this equation needs to be adjusted later
+ return aRet;
+ }
+};
+
+class EnumValueExpression : public ExpressionNode
+{
+ const ExpressionFunct meFunct;
+ const EnhancedCustomShape2d& mrCustoShape;
+
+public:
+
+ EnumValueExpression( const EnhancedCustomShape2d& rCustoShape, const ExpressionFunct eFunct )
+ : meFunct ( eFunct )
+ , mrCustoShape ( rCustoShape )
+ {
+ }
+ virtual double operator()() const override
+ {
+ SAL_INFO("svx", meFunct << " --> " << mrCustoShape.GetEnumFunc(meFunct) << "(angle: " <<
+ 180.0 * mrCustoShape.GetEnumFunc(meFunct) / 10800000.0 << ")");
+
+ return mrCustoShape.GetEnumFunc( meFunct );
+ }
+ virtual bool isConstant() const override
+ {
+ return false;
+ }
+ virtual ExpressionFunct getType() const override
+ {
+ return meFunct;
+ }
+ virtual EnhancedCustomShapeParameter fillNode( std::vector< EnhancedCustomShapeEquation >& rEquations, ExpressionNode* /*pOptionalArg*/, sal_uInt32 nFlags ) override
+ {
+ EnhancedCustomShapeParameter aRet;
+
+ aRet.Value <<= sal_Int32(1);
+
+ switch( meFunct )
+ {
+ case ExpressionFunct::EnumWidth : // TODO: do not use this as constant value
+ case ExpressionFunct::EnumHeight :
+ case ExpressionFunct::EnumLogWidth :
+ case ExpressionFunct::EnumLogHeight :
+ case ExpressionFunct::EnumPi :
+ {
+ ConstantValueExpression aConstantValue( mrCustoShape.GetEnumFunc( meFunct ) );
+ aRet = aConstantValue.fillNode( rEquations, nullptr, nFlags );
+ }
+ break;
+ case ExpressionFunct::EnumLeft : aRet.Type = EnhancedCustomShapeParameterType::LEFT; break;
+ case ExpressionFunct::EnumTop : aRet.Type = EnhancedCustomShapeParameterType::TOP; break;
+ case ExpressionFunct::EnumRight : aRet.Type = EnhancedCustomShapeParameterType::RIGHT; break;
+ case ExpressionFunct::EnumBottom : aRet.Type = EnhancedCustomShapeParameterType::BOTTOM; break;
+
+ // not implemented so far
+ case ExpressionFunct::EnumXStretch :
+ case ExpressionFunct::EnumYStretch :
+ case ExpressionFunct::EnumHasStroke :
+ case ExpressionFunct::EnumHasFill : aRet.Type = EnhancedCustomShapeParameterType::NORMAL; break;
+
+ default:
+ break;
+ }
+ return aRet;
+ }
+};
+
+/** ExpressionNode implementation for unary
+ function over one ExpressionNode
+ */
+class UnaryFunctionExpression : public ExpressionNode
+{
+ const ExpressionFunct meFunct;
+ std::shared_ptr<ExpressionNode> mpArg;
+
+public:
+ UnaryFunctionExpression( const ExpressionFunct eFunct, const std::shared_ptr<ExpressionNode>& rArg ) :
+ meFunct( eFunct ),
+ mpArg( rArg )
+ {
+ }
+ static double getValue( const ExpressionFunct eFunct, const std::shared_ptr<ExpressionNode>& rArg )
+ {
+ double fRet = 0;
+ switch( eFunct )
+ {
+ case ExpressionFunct::UnaryAbs : fRet = fabs( (*rArg)() ); break;
+ case ExpressionFunct::UnarySqrt: fRet = sqrt( (*rArg)() ); break;
+ case ExpressionFunct::UnarySin : fRet = sin( (*rArg)() ); break;
+ case ExpressionFunct::UnaryCos : fRet = cos( (*rArg)() ); break;
+ case ExpressionFunct::UnaryTan : fRet = tan( (*rArg)() ); break;
+ case ExpressionFunct::UnaryAtan: fRet = atan( (*rArg)() ); break;
+ case ExpressionFunct::UnaryNeg : fRet = ::std::negate<double>()( (*rArg)() ); break;
+ default:
+ break;
+ }
+ return fRet;
+ }
+ virtual double operator()() const override
+ {
+ return getValue( meFunct, mpArg );
+ }
+ virtual bool isConstant() const override
+ {
+ return mpArg->isConstant();
+ }
+ virtual ExpressionFunct getType() const override
+ {
+ return meFunct;
+ }
+ virtual EnhancedCustomShapeParameter fillNode( std::vector< EnhancedCustomShapeEquation >& rEquations, ExpressionNode* pOptionalArg, sal_uInt32 nFlags ) override
+ {
+ EnhancedCustomShapeParameter aRet;
+ switch( meFunct )
+ {
+ case ExpressionFunct::UnaryAbs :
+ {
+ EnhancedCustomShapeEquation aEquation;
+ aEquation.nOperation |= 3;
+ FillEquationParameter( mpArg->fillNode( rEquations, nullptr, nFlags ), 0, aEquation );
+ aRet.Type = EnhancedCustomShapeParameterType::EQUATION;
+ aRet.Value <<= static_cast<sal_Int32>(rEquations.size());
+ rEquations.push_back( aEquation );
+ }
+ break;
+ case ExpressionFunct::UnarySqrt:
+ {
+ EnhancedCustomShapeEquation aEquation;
+ aEquation.nOperation |= 13;
+ FillEquationParameter( mpArg->fillNode( rEquations, nullptr, nFlags ), 0, aEquation );
+ aRet.Type = EnhancedCustomShapeParameterType::EQUATION;
+ aRet.Value <<= static_cast<sal_Int32>(rEquations.size());
+ rEquations.push_back( aEquation );
+ }
+ break;
+ case ExpressionFunct::UnarySin :
+ {
+ EnhancedCustomShapeEquation aEquation;
+ aEquation.nOperation |= 9;
+ if ( pOptionalArg )
+ FillEquationParameter( pOptionalArg->fillNode( rEquations, nullptr, nFlags ), 0, aEquation );
+ else
+ aEquation.nPara[ 0 ] = 1;
+
+ EnhancedCustomShapeParameter aSource( mpArg->fillNode( rEquations, nullptr, nFlags | EXPRESSION_FLAG_SUMANGLE_MODE ) );
+ if ( aSource.Type == EnhancedCustomShapeParameterType::NORMAL )
+ { // sumangle needed :-(
+ EnhancedCustomShapeEquation _aEquation;
+ _aEquation.nOperation |= 0xe; // sumangle
+ FillEquationParameter( aSource, 1, _aEquation );
+ aSource.Type = EnhancedCustomShapeParameterType::EQUATION;
+ aSource.Value <<= static_cast<sal_Int32>(rEquations.size());
+ rEquations.push_back( _aEquation );
+ }
+ FillEquationParameter( aSource, 1, aEquation );
+ aRet.Type = EnhancedCustomShapeParameterType::EQUATION;
+ aRet.Value <<= static_cast<sal_Int32>(rEquations.size());
+ rEquations.push_back( aEquation );
+ }
+ break;
+ case ExpressionFunct::UnaryCos :
+ {
+ EnhancedCustomShapeEquation aEquation;
+ aEquation.nOperation |= 10;
+ if ( pOptionalArg )
+ FillEquationParameter( pOptionalArg->fillNode( rEquations, nullptr, nFlags ), 0, aEquation );
+ else
+ aEquation.nPara[ 0 ] = 1;
+
+ EnhancedCustomShapeParameter aSource( mpArg->fillNode( rEquations, nullptr, nFlags | EXPRESSION_FLAG_SUMANGLE_MODE ) );
+ if ( aSource.Type == EnhancedCustomShapeParameterType::NORMAL )
+ { // sumangle needed :-(
+ EnhancedCustomShapeEquation aTmpEquation;
+ aTmpEquation.nOperation |= 0xe; // sumangle
+ FillEquationParameter( aSource, 1, aTmpEquation );
+ aSource.Type = EnhancedCustomShapeParameterType::EQUATION;
+ aSource.Value <<= static_cast<sal_Int32>(rEquations.size());
+ rEquations.push_back( aTmpEquation );
+ }
+ FillEquationParameter( aSource, 1, aEquation );
+ aRet.Type = EnhancedCustomShapeParameterType::EQUATION;
+ aRet.Value <<= static_cast<sal_Int32>(rEquations.size());
+ rEquations.push_back( aEquation );
+ }
+ break;
+ case ExpressionFunct::UnaryTan :
+ {
+ EnhancedCustomShapeEquation aEquation;
+ aEquation.nOperation |= 16;
+ if ( pOptionalArg )
+ FillEquationParameter( pOptionalArg->fillNode( rEquations, nullptr, nFlags ), 0, aEquation );
+ else
+ aEquation.nPara[ 0 ] = 1;
+
+ EnhancedCustomShapeParameter aSource( mpArg->fillNode( rEquations, nullptr, nFlags | EXPRESSION_FLAG_SUMANGLE_MODE ) );
+ if ( aSource.Type == EnhancedCustomShapeParameterType::NORMAL )
+ { // sumangle needed :-(
+ EnhancedCustomShapeEquation aTmpEquation;
+ aTmpEquation.nOperation |= 0xe; // sumangle
+ FillEquationParameter( aSource, 1, aTmpEquation );
+ aSource.Type = EnhancedCustomShapeParameterType::EQUATION;
+ aSource.Value <<= static_cast<sal_Int32>(rEquations.size());
+ rEquations.push_back( aTmpEquation );
+ }
+ FillEquationParameter( aSource, 1, aEquation );
+ aRet.Type = EnhancedCustomShapeParameterType::EQUATION;
+ aRet.Value <<= static_cast<sal_Int32>(rEquations.size());
+ rEquations.push_back( aEquation );
+ }
+ break;
+ case ExpressionFunct::UnaryAtan:
+ {
+// TODO:
+ aRet.Type = EnhancedCustomShapeParameterType::NORMAL;
+ }
+ break;
+ case ExpressionFunct::UnaryNeg:
+ {
+ EnhancedCustomShapeEquation aEquation;
+ aEquation.nOperation |= 1;
+ aEquation.nPara[ 1 ] = -1;
+ aEquation.nPara[ 2 ] = 1;
+ FillEquationParameter( mpArg->fillNode( rEquations, nullptr, nFlags ), 0, aEquation );
+ aRet.Type = EnhancedCustomShapeParameterType::EQUATION;
+ aRet.Value <<= static_cast<sal_Int32>(rEquations.size());
+ rEquations.push_back( aEquation );
+ }
+ break;
+ default:
+ break;
+ }
+ return aRet;
+ }
+};
+
+/** ExpressionNode implementation for unary
+ function over two ExpressionNodes
+ */
+class BinaryFunctionExpression : public ExpressionNode
+{
+ const ExpressionFunct meFunct;
+ std::shared_ptr<ExpressionNode> mpFirstArg;
+ std::shared_ptr<ExpressionNode> mpSecondArg;
+
+public:
+
+ BinaryFunctionExpression( const ExpressionFunct eFunct, const std::shared_ptr<ExpressionNode>& rFirstArg, const std::shared_ptr<ExpressionNode>& rSecondArg ) :
+ meFunct( eFunct ),
+ mpFirstArg( rFirstArg ),
+ mpSecondArg( rSecondArg )
+ {
+ }
+#if defined(__clang__) || (defined (__GNUC__) && __GNUC__ >= 8)
+ //GetEquationValueAsDouble calls isFinite on the result
+ __attribute__((no_sanitize("float-divide-by-zero")))
+#endif
+ static double getValue( const ExpressionFunct eFunct, const std::shared_ptr<ExpressionNode>& rFirstArg, const std::shared_ptr<ExpressionNode>& rSecondArg )
+ {
+ double fRet = 0;
+ switch( eFunct )
+ {
+ case ExpressionFunct::BinaryPlus : fRet = (*rFirstArg)() + (*rSecondArg)(); break;
+ case ExpressionFunct::BinaryMinus: fRet = (*rFirstArg)() - (*rSecondArg)(); break;
+ case ExpressionFunct::BinaryMul : fRet = (*rFirstArg)() * (*rSecondArg)(); break;
+ case ExpressionFunct::BinaryDiv : fRet = (*rFirstArg)() / (*rSecondArg)(); break;
+ case ExpressionFunct::BinaryMin : fRet = ::std::min( (*rFirstArg)(), (*rSecondArg)() ); break;
+ case ExpressionFunct::BinaryMax : fRet = ::std::max( (*rFirstArg)(), (*rSecondArg)() ); break;
+ case ExpressionFunct::BinaryAtan2: fRet = atan2( (*rFirstArg)(), (*rSecondArg)() ); break;
+ default:
+ break;
+ }
+ return fRet;
+ }
+ virtual double operator()() const override
+ {
+ return getValue( meFunct, mpFirstArg, mpSecondArg );
+ }
+ virtual bool isConstant() const override
+ {
+ return mpFirstArg->isConstant() && mpSecondArg->isConstant();
+ }
+ virtual ExpressionFunct getType() const override
+ {
+ return meFunct;
+ }
+ virtual EnhancedCustomShapeParameter fillNode( std::vector< EnhancedCustomShapeEquation >& rEquations, ExpressionNode* /*pOptionalArg*/, sal_uInt32 nFlags ) override
+ {
+ EnhancedCustomShapeParameter aRet;
+ switch( meFunct )
+ {
+ case ExpressionFunct::BinaryPlus :
+ {
+ if ( nFlags & EXPRESSION_FLAG_SUMANGLE_MODE )
+ {
+ if ( mpFirstArg->getType() == ExpressionFunct::EnumAdjustment )
+ {
+ EnhancedCustomShapeEquation aEquation;
+ aEquation.nOperation |= 0xe; // sumangle
+ FillEquationParameter( mpFirstArg->fillNode( rEquations, nullptr, nFlags ), 0, aEquation );
+ FillEquationParameter( mpSecondArg->fillNode( rEquations, nullptr, nFlags ), 1, aEquation );
+ aRet.Type = EnhancedCustomShapeParameterType::EQUATION;
+ aRet.Value <<= static_cast<sal_Int32>(rEquations.size());
+ rEquations.push_back( aEquation );
+ }
+ else if ( mpSecondArg->getType() == ExpressionFunct::EnumAdjustment )
+ {
+ EnhancedCustomShapeEquation aEquation;
+ aEquation.nOperation |= 0xe; // sumangle
+ FillEquationParameter( mpSecondArg->fillNode( rEquations, nullptr, nFlags ), 0, aEquation );
+ FillEquationParameter( mpFirstArg->fillNode( rEquations, nullptr, nFlags ), 1, aEquation );
+ aRet.Type = EnhancedCustomShapeParameterType::EQUATION;
+ aRet.Value <<= static_cast<sal_Int32>(rEquations.size());
+ rEquations.push_back( aEquation );
+ }
+ else
+ {
+ EnhancedCustomShapeEquation aSumangle1;
+ aSumangle1.nOperation |= 0xe; // sumangle
+ FillEquationParameter( mpFirstArg->fillNode( rEquations, nullptr, nFlags &~EXPRESSION_FLAG_SUMANGLE_MODE ), 1, aSumangle1 );
+ aRet.Type = EnhancedCustomShapeParameterType::EQUATION;
+ aRet.Value <<= static_cast<sal_Int32>(rEquations.size());
+ rEquations.push_back( aSumangle1 );
+
+ EnhancedCustomShapeEquation aSumangle2;
+ aSumangle2.nOperation |= 0xe; // sumangle
+ FillEquationParameter( mpSecondArg->fillNode( rEquations, nullptr, nFlags &~EXPRESSION_FLAG_SUMANGLE_MODE ), 1, aSumangle2 );
+ aRet.Type = EnhancedCustomShapeParameterType::EQUATION;
+ aRet.Value <<= static_cast<sal_Int32>(rEquations.size());
+ rEquations.push_back( aSumangle2 );
+
+ EnhancedCustomShapeEquation aEquation;
+ aEquation.nOperation |= 0;
+ aEquation.nPara[ 0 ] = ( rEquations.size() - 2 ) | 0x400;
+ aEquation.nPara[ 1 ] = ( rEquations.size() - 1 ) | 0x400;
+ aRet.Type = EnhancedCustomShapeParameterType::EQUATION;
+ aRet.Value <<= static_cast<sal_Int32>(rEquations.size());
+ rEquations.push_back( aEquation );
+ }
+ }
+ else
+ {
+ bool bFirstIsEmpty = mpFirstArg->isConstant() && ( (*mpFirstArg)() == 0 );
+ bool bSecondIsEmpty = mpSecondArg->isConstant() && ( (*mpSecondArg)() == 0 );
+
+ if ( bFirstIsEmpty )
+ aRet = mpSecondArg->fillNode( rEquations, nullptr, nFlags );
+ else if ( bSecondIsEmpty )
+ aRet = mpFirstArg->fillNode( rEquations, nullptr, nFlags );
+ else
+ {
+ EnhancedCustomShapeEquation aEquation;
+ aEquation.nOperation |= 0;
+ FillEquationParameter( mpFirstArg->fillNode( rEquations, nullptr, nFlags ), 0, aEquation );
+ FillEquationParameter( mpSecondArg->fillNode( rEquations, nullptr, nFlags ), 1, aEquation );
+ aRet.Type = EnhancedCustomShapeParameterType::EQUATION;
+ aRet.Value <<= static_cast<sal_Int32>(rEquations.size());
+ rEquations.push_back( aEquation );
+ }
+ }
+ }
+ break;
+ case ExpressionFunct::BinaryMinus:
+ {
+ EnhancedCustomShapeEquation aEquation;
+ aEquation.nOperation |= 0;
+ FillEquationParameter( mpFirstArg->fillNode( rEquations, nullptr, nFlags ), 0, aEquation );
+ FillEquationParameter( mpSecondArg->fillNode( rEquations, nullptr, nFlags ), 2, aEquation );
+ aRet.Type = EnhancedCustomShapeParameterType::EQUATION;
+ aRet.Value <<= static_cast<sal_Int32>(rEquations.size());
+ rEquations.push_back( aEquation );
+ }
+ break;
+ case ExpressionFunct::BinaryMul :
+ {
+ // in the dest. format the cos function is using integer as result :-(
+ // so we can't use the generic algorithm
+ if ( ( mpFirstArg->getType() == ExpressionFunct::UnarySin ) || ( mpFirstArg->getType() == ExpressionFunct::UnaryCos ) || ( mpFirstArg->getType() == ExpressionFunct::UnaryTan ) )
+ aRet = mpFirstArg->fillNode( rEquations, mpSecondArg.get(), nFlags );
+ else if ( ( mpSecondArg->getType() == ExpressionFunct::UnarySin ) || ( mpSecondArg->getType() == ExpressionFunct::UnaryCos ) || ( mpSecondArg->getType() == ExpressionFunct::UnaryTan ) )
+ aRet = mpSecondArg->fillNode( rEquations, mpFirstArg.get(), nFlags );
+ else
+ {
+ if ( mpFirstArg->isConstant() && (*mpFirstArg)() == 1 )
+ aRet = mpSecondArg->fillNode( rEquations, nullptr, nFlags );
+ else if ( mpSecondArg->isConstant() && (*mpSecondArg)() == 1 )
+ aRet = mpFirstArg->fillNode( rEquations, nullptr, nFlags );
+ else if ( ( mpFirstArg->getType() == ExpressionFunct::BinaryDiv ) // don't care of (pi/180)
+ && ( static_cast<BinaryFunctionExpression*>(mpFirstArg.get())->mpFirstArg->getType() == ExpressionFunct::EnumPi )
+ && ( static_cast<BinaryFunctionExpression*>(mpFirstArg.get())->mpSecondArg->getType() == ExpressionFunct::Const ) )
+ {
+ aRet = mpSecondArg->fillNode( rEquations, nullptr, nFlags );
+ }
+ else if ( ( mpSecondArg->getType() == ExpressionFunct::BinaryDiv ) // don't care of (pi/180)
+ && ( static_cast<BinaryFunctionExpression*>(mpSecondArg.get())->mpFirstArg->getType() == ExpressionFunct::EnumPi )
+ && ( static_cast<BinaryFunctionExpression*>(mpSecondArg.get())->mpSecondArg->getType() == ExpressionFunct::Const ) )
+ {
+ aRet = mpFirstArg->fillNode( rEquations, nullptr, nFlags );
+ }
+ else
+ {
+ EnhancedCustomShapeEquation aEquation;
+ aEquation.nOperation |= 1;
+ FillEquationParameter( mpFirstArg->fillNode( rEquations, nullptr, nFlags ), 0, aEquation );
+ FillEquationParameter( mpSecondArg->fillNode( rEquations, nullptr, nFlags ), 1, aEquation );
+ aEquation.nPara[ 2 ] = 1;
+ aRet.Type = EnhancedCustomShapeParameterType::EQUATION;
+ aRet.Value <<= static_cast<sal_Int32>(rEquations.size());
+ rEquations.push_back( aEquation );
+ }
+ }
+ }
+ break;
+ case ExpressionFunct::BinaryDiv :
+ {
+ EnhancedCustomShapeEquation aEquation;
+ aEquation.nOperation |= 1;
+ FillEquationParameter( mpFirstArg->fillNode( rEquations, nullptr, nFlags ), 0, aEquation );
+ aEquation.nPara[ 1 ] = 1;
+ FillEquationParameter( mpSecondArg->fillNode( rEquations, nullptr, nFlags ), 2, aEquation );
+ aRet.Type = EnhancedCustomShapeParameterType::EQUATION;
+ aRet.Value <<= static_cast<sal_Int32>(rEquations.size());
+ rEquations.push_back( aEquation );
+ }
+ break;
+ case ExpressionFunct::BinaryMin :
+ {
+ EnhancedCustomShapeEquation aEquation;
+ aEquation.nOperation |= 4;
+ FillEquationParameter( mpFirstArg->fillNode( rEquations, nullptr, nFlags ), 0, aEquation );
+ FillEquationParameter( mpSecondArg->fillNode( rEquations, nullptr, nFlags ), 1, aEquation );
+ aRet.Type = EnhancedCustomShapeParameterType::EQUATION;
+ aRet.Value <<= static_cast<sal_Int32>(rEquations.size());
+ rEquations.push_back( aEquation );
+ }
+ break;
+ case ExpressionFunct::BinaryMax :
+ {
+ EnhancedCustomShapeEquation aEquation;
+ aEquation.nOperation |= 5;
+ FillEquationParameter( mpFirstArg->fillNode( rEquations, nullptr, nFlags ), 0, aEquation );
+ FillEquationParameter( mpSecondArg->fillNode( rEquations, nullptr, nFlags ), 1, aEquation );
+ aRet.Type = EnhancedCustomShapeParameterType::EQUATION;
+ aRet.Value <<= static_cast<sal_Int32>(rEquations.size());
+ rEquations.push_back( aEquation );
+ }
+ break;
+ case ExpressionFunct::BinaryAtan2:
+ {
+ EnhancedCustomShapeEquation aEquation;
+ aEquation.nOperation |= 8;
+ FillEquationParameter( mpSecondArg->fillNode( rEquations, nullptr, nFlags ), 0, aEquation );
+ FillEquationParameter( mpFirstArg->fillNode( rEquations, nullptr, nFlags ), 1, aEquation );
+ aRet.Type = EnhancedCustomShapeParameterType::EQUATION;
+ aRet.Value <<= static_cast<sal_Int32>(rEquations.size());
+ rEquations.push_back( aEquation );
+ }
+ break;
+ default:
+ break;
+ }
+ return aRet;
+ }
+};
+
+class IfExpression : public ExpressionNode
+{
+ std::shared_ptr<ExpressionNode> mpFirstArg;
+ std::shared_ptr<ExpressionNode> mpSecondArg;
+ std::shared_ptr<ExpressionNode> mpThirdArg;
+
+public:
+
+ IfExpression( const std::shared_ptr<ExpressionNode>& rFirstArg,
+ const std::shared_ptr<ExpressionNode>& rSecondArg,
+ const std::shared_ptr<ExpressionNode>& rThirdArg ) :
+ mpFirstArg( rFirstArg ),
+ mpSecondArg( rSecondArg ),
+ mpThirdArg( rThirdArg )
+ {
+ }
+ virtual bool isConstant() const override
+ {
+ return
+ mpFirstArg->isConstant() &&
+ mpSecondArg->isConstant() &&
+ mpThirdArg->isConstant();
+ }
+ virtual double operator()() const override
+ {
+ return (*mpFirstArg)() > 0 ? (*mpSecondArg)() : (*mpThirdArg)();
+ }
+ virtual ExpressionFunct getType() const override
+ {
+ return ExpressionFunct::TernaryIf;
+ }
+ virtual EnhancedCustomShapeParameter fillNode( std::vector< EnhancedCustomShapeEquation >& rEquations, ExpressionNode* /*pOptionalArg*/, sal_uInt32 nFlags ) override
+ {
+ EnhancedCustomShapeParameter aRet;
+ aRet.Type = EnhancedCustomShapeParameterType::EQUATION;
+ aRet.Value <<= static_cast<sal_Int32>(rEquations.size());
+ {
+ EnhancedCustomShapeEquation aEquation;
+ aEquation.nOperation |= 6;
+ FillEquationParameter( mpFirstArg->fillNode( rEquations, nullptr, nFlags ), 0, aEquation );
+ FillEquationParameter( mpSecondArg->fillNode( rEquations, nullptr, nFlags ), 1, aEquation );
+ FillEquationParameter( mpThirdArg->fillNode( rEquations, nullptr, nFlags ), 2, aEquation );
+ rEquations.push_back( aEquation );
+ }
+ return aRet;
+ }
+};
+
+
+// FUNCTION PARSER
+
+
+typedef const char* StringIteratorT;
+
+struct ParserContext
+{
+ typedef ::std::stack< std::shared_ptr<ExpressionNode> > OperandStack;
+
+ // stores a stack of not-yet-evaluated operands. This is used
+ // by the operators (i.e. '+', '*', 'sin' etc.) to pop their
+ // arguments from. If all arguments to an operator are constant,
+ // the operator pushes a precalculated result on the stack, and
+ // a composite ExpressionNode otherwise.
+ OperandStack maOperandStack;
+
+ const EnhancedCustomShape2d* mpCustoShape;
+
+};
+
+typedef std::shared_ptr< ParserContext > ParserContextSharedPtr;
+
+/** Generate parse-dependent-but-then-constant value
+ */
+class DoubleConstantFunctor
+{
+ ParserContextSharedPtr mxContext;
+
+public:
+ explicit DoubleConstantFunctor( const ParserContextSharedPtr& rContext ) :
+ mxContext( rContext )
+ {
+ }
+ void operator()( double n ) const
+ {
+ mxContext->maOperandStack.push( std::make_shared<ConstantValueExpression>( n ) );
+ }
+};
+
+class EnumFunctor
+{
+ const ExpressionFunct meFunct;
+ ParserContextSharedPtr mxContext;
+
+public:
+
+ EnumFunctor( const ExpressionFunct eFunct, const ParserContextSharedPtr& rContext )
+ : meFunct( eFunct )
+ , mxContext( rContext )
+ {
+ }
+ void operator()( StringIteratorT rFirst, StringIteratorT rSecond ) const
+ {
+ /*double nVal = mnValue;*/
+ switch( meFunct )
+ {
+ case ExpressionFunct::EnumAdjustment :
+ {
+ OUString aVal( rFirst + 1, rSecond - rFirst, RTL_TEXTENCODING_UTF8 );
+ mxContext->maOperandStack.push( std::make_shared<AdjustmentExpression>( *mxContext->mpCustoShape, aVal.toInt32() ) );
+ }
+ break;
+ case ExpressionFunct::EnumEquation :
+ {
+ OUString aVal( rFirst + 1, rSecond - rFirst, RTL_TEXTENCODING_UTF8 );
+ mxContext->maOperandStack.push( std::make_shared<EquationExpression>( *mxContext->mpCustoShape, aVal.toInt32() ) );
+ }
+ break;
+ default:
+ mxContext->maOperandStack.push( std::make_shared<EnumValueExpression>( *mxContext->mpCustoShape, meFunct ) );
+ }
+ }
+};
+
+class UnaryFunctionFunctor
+{
+ const ExpressionFunct meFunct;
+ ParserContextSharedPtr mxContext;
+
+public:
+
+ UnaryFunctionFunctor( const ExpressionFunct eFunct, const ParserContextSharedPtr& rContext ) :
+ meFunct( eFunct ),
+ mxContext( rContext )
+ {
+ }
+ void operator()( StringIteratorT, StringIteratorT ) const
+ {
+ ParserContext::OperandStack& rNodeStack( mxContext->maOperandStack );
+
+ if( rNodeStack.empty() )
+ throw ParseError( "Not enough arguments for unary operator" );
+
+ // retrieve arguments
+ std::shared_ptr<ExpressionNode> pArg( std::move(rNodeStack.top()) );
+ rNodeStack.pop();
+
+ if( pArg->isConstant() ) // check for constness
+ rNodeStack.push( std::make_shared<ConstantValueExpression>( UnaryFunctionExpression::getValue( meFunct, pArg ) ) );
+ else // push complex node, that calcs the value on demand
+ rNodeStack.push( std::make_shared<UnaryFunctionExpression>( meFunct, pArg ) );
+ }
+};
+
+/** Implements a binary function over two ExpressionNodes
+
+ @tpl Generator
+ Generator functor, to generate an ExpressionNode of
+ appropriate type
+
+ */
+class BinaryFunctionFunctor
+{
+ const ExpressionFunct meFunct;
+ ParserContextSharedPtr mxContext;
+
+public:
+
+ BinaryFunctionFunctor( const ExpressionFunct eFunct, const ParserContextSharedPtr& rContext ) :
+ meFunct( eFunct ),
+ mxContext( rContext )
+ {
+ }
+
+ void operator()( StringIteratorT, StringIteratorT ) const
+ {
+ ParserContext::OperandStack& rNodeStack( mxContext->maOperandStack );
+
+ if( rNodeStack.size() < 2 )
+ throw ParseError( "Not enough arguments for binary operator" );
+
+ // retrieve arguments
+ std::shared_ptr<ExpressionNode> pSecondArg( std::move(rNodeStack.top()) );
+ rNodeStack.pop();
+ std::shared_ptr<ExpressionNode> pFirstArg( std::move(rNodeStack.top()) );
+ rNodeStack.pop();
+
+ assert(pSecondArg && pFirstArg && "count of arg checked before we get here");
+
+ // create combined ExpressionNode
+ auto pNode = std::make_shared<BinaryFunctionExpression>( meFunct, pFirstArg, pSecondArg );
+ // check for constness
+ if( pFirstArg->isConstant() && pSecondArg->isConstant() ) // call the operator() at pNode, store result in constant value ExpressionNode.
+ rNodeStack.push( std::make_shared<ConstantValueExpression>( (*pNode)() ) );
+ else // push complex node, that calcs the value on demand
+ rNodeStack.push( pNode );
+ }
+};
+
+class IfFunctor
+{
+ ParserContextSharedPtr mxContext;
+
+public:
+
+ explicit IfFunctor( const ParserContextSharedPtr& rContext ) :
+ mxContext( rContext )
+ {
+ }
+ void operator()( StringIteratorT, StringIteratorT ) const
+ {
+ ParserContext::OperandStack& rNodeStack( mxContext->maOperandStack );
+
+ if( rNodeStack.size() < 3 )
+ throw ParseError( "Not enough arguments for ternary operator" );
+
+ // retrieve arguments
+ std::shared_ptr<ExpressionNode> pThirdArg( std::move(rNodeStack.top()) );
+ rNodeStack.pop();
+ std::shared_ptr<ExpressionNode> pSecondArg( std::move(rNodeStack.top()) );
+ rNodeStack.pop();
+ std::shared_ptr<ExpressionNode> pFirstArg( std::move(rNodeStack.top()) );
+ rNodeStack.pop();
+
+ assert(pThirdArg && pSecondArg && pFirstArg);
+
+ // create combined ExpressionNode
+ auto pNode = std::make_shared<IfExpression>( pFirstArg, pSecondArg, pThirdArg );
+ // check for constness
+ if( pFirstArg->isConstant() && pSecondArg->isConstant() && pThirdArg->isConstant() )
+ rNodeStack.push( std::make_shared<ConstantValueExpression>( (*pNode)() ) ); // call the operator() at pNode, store result in constant value ExpressionNode.
+ else
+ rNodeStack.push( pNode ); // push complex node, that calcs the value on demand
+ }
+};
+
+// Workaround for MSVC compiler anomaly (stack trashing)
+
+// The default ureal_parser_policies implementation of parse_exp
+// triggers a really weird error in MSVC7 (Version 13.00.9466), in
+// that the real_parser_impl::parse_main() call of parse_exp()
+// overwrites the frame pointer _on the stack_ (EBP of the calling
+// function gets overwritten while lying on the stack).
+
+// For the time being, our parser thus can only read the 1.0E10
+// notation, not the 1.0e10 one.
+
+// TODO(F1): Also handle the 1.0e10 case here.
+template< typename T > struct custom_real_parser_policies : public ::boost::spirit::classic::ureal_parser_policies<T>
+{
+ template< typename ScannerT >
+ static typename ::boost::spirit::classic::parser_result< ::boost::spirit::classic::chlit<>, ScannerT >::type
+ parse_exp(ScannerT& scan)
+ {
+ // as_lower_d somehow breaks MSVC7
+ return ::boost::spirit::classic::ch_p('E').parse(scan);
+ }
+};
+
+/* This class implements the following grammar (more or
+ less literally written down below, only slightly
+ obfuscated by the parser actions):
+
+ identifier = '$'|'pi'|'e'|'X'|'Y'|'Width'|'Height'
+
+ function = 'abs'|'sqrt'|'sin'|'cos'|'tan'|'atan'|'acos'|'asin'|'exp'|'log'
+
+ basic_expression =
+ number |
+ identifier |
+ function '(' additive_expression ')' |
+ '(' additive_expression ')'
+
+ unary_expression =
+ '-' basic_expression |
+ basic_expression
+
+ multiplicative_expression =
+ unary_expression ( ( '*' unary_expression )* |
+ ( '/' unary_expression )* )
+
+ additive_expression =
+ multiplicative_expression ( ( '+' multiplicative_expression )* |
+ ( '-' multiplicative_expression )* )
+
+ */
+
+class ExpressionGrammar : public ::boost::spirit::classic::grammar< ExpressionGrammar >
+{
+public:
+ /** Create an arithmetic expression grammar
+
+ @param rParserContext
+ Contains context info for the parser
+ */
+ explicit ExpressionGrammar( const ParserContextSharedPtr& rParserContext ) :
+ mpParserContext( rParserContext )
+ {
+ }
+
+ template< typename ScannerT > class definition
+ {
+ public:
+ // grammar definition
+ explicit definition( const ExpressionGrammar& self )
+ {
+ using ::boost::spirit::classic::str_p;
+ using ::boost::spirit::classic::range_p;
+ using ::boost::spirit::classic::lexeme_d;
+ using ::boost::spirit::classic::real_parser;
+
+ identifier =
+ str_p( "pi" )[ EnumFunctor(ExpressionFunct::EnumPi, self.getContext() ) ]
+ | str_p( "left" )[ EnumFunctor(ExpressionFunct::EnumLeft, self.getContext() ) ]
+ | str_p( "top" )[ EnumFunctor(ExpressionFunct::EnumTop, self.getContext() ) ]
+ | str_p( "right" )[ EnumFunctor(ExpressionFunct::EnumRight, self.getContext() ) ]
+ | str_p( "bottom" )[ EnumFunctor(ExpressionFunct::EnumBottom, self.getContext() ) ]
+ | str_p( "xstretch" )[ EnumFunctor(ExpressionFunct::EnumXStretch, self.getContext() ) ]
+ | str_p( "ystretch" )[ EnumFunctor(ExpressionFunct::EnumYStretch, self.getContext() ) ]
+ | str_p( "hasstroke" )[ EnumFunctor(ExpressionFunct::EnumHasStroke, self.getContext() ) ]
+ | str_p( "hasfill" )[ EnumFunctor(ExpressionFunct::EnumHasFill, self.getContext() ) ]
+ | str_p( "width" )[ EnumFunctor(ExpressionFunct::EnumWidth, self.getContext() ) ]
+ | str_p( "height" )[ EnumFunctor(ExpressionFunct::EnumHeight, self.getContext() ) ]
+ | str_p( "logwidth" )[ EnumFunctor(ExpressionFunct::EnumLogWidth, self.getContext() ) ]
+ | str_p( "logheight" )[ EnumFunctor(ExpressionFunct::EnumLogHeight, self.getContext() ) ]
+ ;
+
+ unaryFunction =
+ (str_p( "abs" ) >> '(' >> additiveExpression >> ')' )[ UnaryFunctionFunctor( ExpressionFunct::UnaryAbs, self.getContext()) ]
+ | (str_p( "sqrt" ) >> '(' >> additiveExpression >> ')' )[ UnaryFunctionFunctor( ExpressionFunct::UnarySqrt, self.getContext()) ]
+ | (str_p( "sin" ) >> '(' >> additiveExpression >> ')' )[ UnaryFunctionFunctor( ExpressionFunct::UnarySin, self.getContext()) ]
+ | (str_p( "cos" ) >> '(' >> additiveExpression >> ')' )[ UnaryFunctionFunctor( ExpressionFunct::UnaryCos, self.getContext()) ]
+ | (str_p( "tan" ) >> '(' >> additiveExpression >> ')' )[ UnaryFunctionFunctor( ExpressionFunct::UnaryTan, self.getContext()) ]
+ | (str_p( "atan" ) >> '(' >> additiveExpression >> ')' )[ UnaryFunctionFunctor( ExpressionFunct::UnaryAtan, self.getContext()) ]
+ ;
+
+ binaryFunction =
+ (str_p( "min" ) >> '(' >> additiveExpression >> ',' >> additiveExpression >> ')' )[ BinaryFunctionFunctor( ExpressionFunct::BinaryMin, self.getContext()) ]
+ | (str_p( "max" ) >> '(' >> additiveExpression >> ',' >> additiveExpression >> ')' )[ BinaryFunctionFunctor( ExpressionFunct::BinaryMax, self.getContext()) ]
+ | (str_p( "atan2") >> '(' >> additiveExpression >> ',' >> additiveExpression >> ')' )[ BinaryFunctionFunctor( ExpressionFunct::BinaryAtan2,self.getContext()) ]
+ ;
+
+ ternaryFunction =
+ (str_p( "if" ) >> '(' >> additiveExpression >> ',' >> additiveExpression >> ',' >> additiveExpression >> ')' )[ IfFunctor( self.getContext() ) ]
+ ;
+
+ funcRef_decl =
+ lexeme_d[ +( range_p('a','z') | range_p('A','Z') | range_p('0','9') ) ];
+
+ functionReference =
+ (str_p( "?" ) >> funcRef_decl )[ EnumFunctor( ExpressionFunct::EnumEquation, self.getContext() ) ];
+
+ modRef_decl =
+ lexeme_d[ +( range_p('0','9') ) ];
+
+ modifierReference =
+ (str_p( "$" ) >> modRef_decl )[ EnumFunctor( ExpressionFunct::EnumAdjustment, self.getContext() ) ];
+
+ basicExpression =
+ real_parser<double, custom_real_parser_policies<double> >()[ DoubleConstantFunctor(self.getContext()) ]
+ | identifier
+ | functionReference
+ | modifierReference
+ | unaryFunction
+ | binaryFunction
+ | ternaryFunction
+ | '(' >> additiveExpression >> ')'
+ ;
+
+ unaryExpression =
+ ('-' >> basicExpression)[ UnaryFunctionFunctor( ExpressionFunct::UnaryNeg, self.getContext()) ]
+ | basicExpression
+ ;
+
+ multiplicativeExpression =
+ unaryExpression
+ >> *( ('*' >> unaryExpression)[ BinaryFunctionFunctor( ExpressionFunct::BinaryMul, self.getContext()) ]
+ | ('/' >> unaryExpression)[ BinaryFunctionFunctor( ExpressionFunct::BinaryDiv, self.getContext()) ]
+ )
+ ;
+
+ additiveExpression =
+ multiplicativeExpression
+ >> *( ('+' >> multiplicativeExpression)[ BinaryFunctionFunctor( ExpressionFunct::BinaryPlus, self.getContext()) ]
+ | ('-' >> multiplicativeExpression)[ BinaryFunctionFunctor( ExpressionFunct::BinaryMinus, self.getContext()) ]
+ )
+ ;
+
+ BOOST_SPIRIT_DEBUG_RULE(additiveExpression);
+ BOOST_SPIRIT_DEBUG_RULE(multiplicativeExpression);
+ BOOST_SPIRIT_DEBUG_RULE(unaryExpression);
+ BOOST_SPIRIT_DEBUG_RULE(basicExpression);
+ BOOST_SPIRIT_DEBUG_RULE(unaryFunction);
+ BOOST_SPIRIT_DEBUG_RULE(binaryFunction);
+ BOOST_SPIRIT_DEBUG_RULE(ternaryFunction);
+ BOOST_SPIRIT_DEBUG_RULE(identifier);
+ }
+
+ const ::boost::spirit::classic::rule< ScannerT >& start() const
+ {
+ return additiveExpression;
+ }
+
+ private:
+ // the constituents of the Spirit arithmetic expression grammar.
+ // For the sake of readability, without 'ma' prefix.
+ ::boost::spirit::classic::rule< ScannerT > additiveExpression;
+ ::boost::spirit::classic::rule< ScannerT > multiplicativeExpression;
+ ::boost::spirit::classic::rule< ScannerT > unaryExpression;
+ ::boost::spirit::classic::rule< ScannerT > basicExpression;
+ ::boost::spirit::classic::rule< ScannerT > unaryFunction;
+ ::boost::spirit::classic::rule< ScannerT > binaryFunction;
+ ::boost::spirit::classic::rule< ScannerT > ternaryFunction;
+ ::boost::spirit::classic::rule< ScannerT > funcRef_decl;
+ ::boost::spirit::classic::rule< ScannerT > functionReference;
+ ::boost::spirit::classic::rule< ScannerT > modRef_decl;
+ ::boost::spirit::classic::rule< ScannerT > modifierReference;
+ ::boost::spirit::classic::rule< ScannerT > identifier;
+ };
+
+ const ParserContextSharedPtr& getContext() const
+ {
+ return mpParserContext;
+ }
+
+private:
+ ParserContextSharedPtr mpParserContext; // might get modified during parsing
+};
+
+const ParserContextSharedPtr& getParserContext()
+{
+ static ParserContextSharedPtr lcl_parserContext = std::make_shared<ParserContext>();
+
+ // clear node stack (since we reuse the static object, that's
+ // the whole point here)
+ while( !lcl_parserContext->maOperandStack.empty() )
+ lcl_parserContext->maOperandStack.pop();
+
+ return lcl_parserContext;
+}
+
+}
+
+namespace EnhancedCustomShape {
+
+
+std::shared_ptr<ExpressionNode> const & FunctionParser::parseFunction( std::u16string_view rFunction, const EnhancedCustomShape2d& rCustoShape )
+{
+ // TODO(Q1): Check if a combination of the RTL_UNICODETOTEXT_FLAGS_*
+ // gives better conversion robustness here (we might want to map space
+ // etc. to ASCII space here)
+ const OString& rAsciiFunction(
+ OUStringToOString( rFunction, RTL_TEXTENCODING_ASCII_US ) );
+
+ StringIteratorT aStart( rAsciiFunction.getStr() );
+ StringIteratorT aEnd( rAsciiFunction.getStr()+rAsciiFunction.getLength() );
+
+ // static parser context, because the actual
+ // Spirit parser is also a static object
+ ParserContextSharedPtr pContext = getParserContext();
+ pContext->mpCustoShape = &rCustoShape;
+
+ ExpressionGrammar aExpressionGrammer( pContext );
+ const ::boost::spirit::classic::parse_info<StringIteratorT> aParseInfo(
+ ::boost::spirit::classic::parse( aStart,
+ aEnd,
+ aExpressionGrammer >> ::boost::spirit::classic::end_p,
+ ::boost::spirit::classic::space_p ) );
+
+ // input fully congested by the parser?
+ if( !aParseInfo.full )
+ throw ParseError( "EnhancedCustomShapeFunctionParser::parseFunction(): string not fully parseable" );
+
+ // parser's state stack now must contain exactly _one_ ExpressionNode,
+ // which represents our formula.
+ if( pContext->maOperandStack.size() != 1 )
+ throw ParseError( "EnhancedCustomShapeFunctionParser::parseFunction(): incomplete or empty expression" );
+
+
+ return pContext->maOperandStack.top();
+}
+
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/customshapes/EnhancedCustomShapeGeometry.cxx b/svx/source/customshapes/EnhancedCustomShapeGeometry.cxx
new file mode 100644
index 000000000..0f999ed42
--- /dev/null
+++ b/svx/source/customshapes/EnhancedCustomShapeGeometry.cxx
@@ -0,0 +1,8567 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * 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 <limits>
+
+#include <svx/EnhancedCustomShapeGeometry.hxx>
+#include <com/sun/star/drawing/EnhancedCustomShapeGluePointType.hpp>
+
+const sal_Int32 MIN_INT32 = std::numeric_limits<sal_Int32>::min();
+
+const sal_Int32 mso_sptDefault1400[] =
+{
+ 1, 1400
+};
+const sal_Int32 mso_sptDefault1800[] =
+{
+ 1, 1800
+};
+const sal_Int32 mso_sptDefault2500[] =
+{
+ 1, 2500
+};
+const sal_Int32 mso_sptDefault2700[] =
+{
+ 1, 2700
+};
+const sal_Int32 mso_sptDefault3600[] =
+{
+ 1, 3600
+};
+const sal_Int32 mso_sptDefault3700[] =
+{
+ 1, 3700
+};
+const sal_Int32 mso_sptDefault5400[] =
+{
+ 1, 5400
+};
+const sal_Int32 mso_sptDefault7200[] =
+{
+ 1, 7200
+};
+const sal_Int32 mso_sptDefault8100[] =
+{
+ 1, 8100
+};
+const sal_Int32 mso_sptDefault9600[] =
+{
+ 1, 9600
+};
+const sal_Int32 mso_sptDefault10800[] =
+{
+ 1, 10800
+};
+const sal_Int32 mso_sptDefault12000[] =
+{
+ 1, 12000
+};
+const sal_Int32 mso_sptDefault13500[] =
+{
+ 1, 13500
+};
+const sal_Int32 mso_sptDefault16200[] =
+{
+ 1, 16200
+};
+const sal_Int32 mso_sptDefault16200and5400[] =
+{
+ 2, 16200, 5400
+};
+
+const SvxMSDffVertPair mso_sptArcVert[] =
+{
+ { 0, 0 }, { 21600, 21600 }, { 3 MSO_I, 1 MSO_I }, { 7 MSO_I, 5 MSO_I }, { 10800, 10800 },
+ { 0, 0 }, { 21600, 21600 }, { 3 MSO_I, 1 MSO_I }, { 7 MSO_I, 5 MSO_I }
+};
+const sal_uInt16 mso_sptArcSegm[] =
+{
+ 0xa604, 0xab00, 0x0001, 0x6001, 0x8000,
+ 0xa604, 0xaa00, 0x8000
+};
+const SvxMSDffCalculationData mso_sptArcCalc[] =
+{
+ { 0x4009, { 10800, DFF_Prop_adjustValue, 0 } },
+ { 0x2000, { 0x400, 10800, 0 } },
+ { 0x400a, { 10800, DFF_Prop_adjustValue, 0 } },
+ { 0x2000, { 0x402, 10800, 0 } },
+ { 0x4009, { 10800, DFF_Prop_adjust2Value, 0 } },
+ { 0x2000, { 0x404, 10800, 0 } },
+ { 0x400a, { 10800, DFF_Prop_adjust2Value, 0 } },
+ { 0x2000, { 0x406, 10800, 0 } }
+};
+const sal_Int32 mso_sptArcDefault[] =
+{
+ 2, 270, 0
+};
+const SvxMSDffVertPair mso_sptStandardGluePoints[] =
+{
+ { 10800, 0 }, { 0, 10800 }, { 10800, 21600 }, { 21600, 10800 }
+};
+const SvxMSDffHandle mso_sptArcHandle[] =
+{
+ { SvxMSDffHandleFlags::POLAR | SvxMSDffHandleFlags::RADIUS_RANGE,
+ 10800, 0x100, 10800, 10800, 10800, 10800, MIN_INT32, 0x7fffffff },
+ { SvxMSDffHandleFlags::POLAR | SvxMSDffHandleFlags::RADIUS_RANGE,
+ 10800, 0x101, 10800, 10800, 10800, 10800, MIN_INT32, 0x7fffffff }
+};
+const mso_CustomShape msoArc =
+{
+ const_cast<SvxMSDffVertPair*>(mso_sptArcVert), SAL_N_ELEMENTS( mso_sptArcVert ),
+ const_cast<sal_uInt16*>(mso_sptArcSegm), sizeof( mso_sptArcSegm ) >> 1,
+ const_cast<SvxMSDffCalculationData*>(mso_sptArcCalc), SAL_N_ELEMENTS( mso_sptArcCalc ),
+ const_cast<sal_Int32*>(mso_sptArcDefault),
+ nullptr, 0,
+ 21600, 21600,
+ MIN_INT32, MIN_INT32,
+ nullptr, 0,
+ const_cast<SvxMSDffHandle*>(mso_sptArcHandle), SAL_N_ELEMENTS( mso_sptArcHandle ) // handles
+};
+
+const SvxMSDffVertPair mso_sptTextSimpleVert[] =
+{
+ { 0, 0 }, { 21600, 0 }, { 21600, 21600 }, { 0, 21600 }, { 0, 0 }
+};
+const mso_CustomShape msoTextSimple =
+{
+ const_cast<SvxMSDffVertPair*>(mso_sptTextSimpleVert), SAL_N_ELEMENTS( mso_sptTextSimpleVert ),
+ nullptr, 0,
+ nullptr, 0,
+ nullptr,
+ nullptr, 0,
+ 21600, 21600,
+ MIN_INT32, MIN_INT32,
+ nullptr, 0,
+ nullptr, 0 // handles
+};
+
+const SvxMSDffVertPair mso_sptRectangleVert[] =
+{
+ { 0, 0 }, { 21600, 0 }, { 21600, 21600 }, { 0, 21600 }, { 0, 0 }
+};
+const mso_CustomShape msoRectangle =
+{
+ const_cast<SvxMSDffVertPair*>(mso_sptRectangleVert), SAL_N_ELEMENTS( mso_sptRectangleVert ),
+ nullptr, 0,
+ nullptr, 0,
+ nullptr,
+ nullptr, 0,
+ 21600, 21600,
+ MIN_INT32, MIN_INT32,
+ nullptr, 0,
+ nullptr, 0 // handles
+};
+
+const SvxMSDffVertPair mso_sptRoundRectangleVert[] = // adj value 0 -> 10800
+{
+ { 7 MSO_I, 0 }, { 0, 8 MSO_I }, { 0, 9 MSO_I }, { 7 MSO_I, 21600 },
+ { 10 MSO_I, 21600 }, { 21600, 9 MSO_I }, { 21600, 8 MSO_I }, { 10 MSO_I, 0 }
+};
+const sal_uInt16 mso_sptRoundRectangleSegm[] =
+{
+ 0x4000, 0xa701, 0x0001, 0xa801, 0x0001, 0xa701, 0x0001, 0xa801, 0x6000, 0x8000
+};
+const SvxMSDffCalculationData mso_sptRoundRectangleCalc[] =
+{
+ { 0x000e, { 0, 45, 0 } },
+ { 0x6009, { DFF_Prop_adjustValue, 0x400, 0 } },
+ { 0x2001, { 0x401, 3163, 7636 } },
+ { 0x6000, { DFF_Prop_geoLeft, 0x402, 0 } },
+ { 0x6000, { DFF_Prop_geoTop, 0x402, 0 } },
+ { 0xa000, { DFF_Prop_geoRight, 0, 0x402 } },
+ { 0xa000, { DFF_Prop_geoBottom, 0, 0x402 } },
+ { 0x6000, { DFF_Prop_geoLeft, DFF_Prop_adjustValue, 0 } },
+ { 0x6000, { DFF_Prop_geoTop, DFF_Prop_adjustValue, 0 } },
+ { 0xa000, { DFF_Prop_geoBottom, 0, DFF_Prop_adjustValue } },
+ { 0xa000, { DFF_Prop_geoRight, 0, DFF_Prop_adjustValue } }
+};
+const SvxMSDffTextRectangles mso_sptRoundRectangleTextRect[] =
+{
+ { { 3 MSO_I, 4 MSO_I }, { 5 MSO_I, 6 MSO_I } }
+};
+const SvxMSDffHandle mso_sptRoundRectangleHandle[] =
+{
+ { SvxMSDffHandleFlags::RANGE | SvxMSDffHandleFlags::SWITCHED,
+ 0x100, 0, 10800, 10800, 0, 10800, MIN_INT32, 0x7fffffff }
+};
+const mso_CustomShape msoRoundRectangle =
+{
+ const_cast<SvxMSDffVertPair*>(mso_sptRoundRectangleVert), SAL_N_ELEMENTS( mso_sptRoundRectangleVert ),
+ const_cast<sal_uInt16*>(mso_sptRoundRectangleSegm), sizeof( mso_sptRoundRectangleSegm ) >> 1,
+ const_cast<SvxMSDffCalculationData*>(mso_sptRoundRectangleCalc), SAL_N_ELEMENTS( mso_sptRoundRectangleCalc ),
+ const_cast<sal_Int32*>(mso_sptDefault3600),
+ const_cast<SvxMSDffTextRectangles*>(mso_sptRoundRectangleTextRect), SAL_N_ELEMENTS( mso_sptRoundRectangleTextRect ),
+ 21600, 21600,
+ 10800, 10800,
+ nullptr, 0,
+ const_cast<SvxMSDffHandle*>(mso_sptRoundRectangleHandle), SAL_N_ELEMENTS( mso_sptRoundRectangleHandle ) // handles
+};
+
+const SvxMSDffVertPair mso_sptRightTriangleVert[] =
+{
+ { 0, 0 }, { 21600, 21600 }, { 0, 21600 }, { 0, 0 }
+};
+const SvxMSDffTextRectangles mso_sptRightTriangleTextRect[] =
+{
+ { { 1900, 12700 }, { 12700, 19700 } }
+};
+const SvxMSDffVertPair mso_sptRightTriangleGluePoints[] =
+{
+ { 0, 0 }, { 0, 10800 }, { 0, 21600 }, { 10800, 21600 }, { 21600, 21600 }, { 10800, 10800 }
+};
+const mso_CustomShape msoRightTriangle =
+{
+ const_cast<SvxMSDffVertPair*>(mso_sptRightTriangleVert), SAL_N_ELEMENTS( mso_sptRightTriangleVert ),
+ nullptr, 0,
+ nullptr, 0,
+ nullptr,
+ const_cast<SvxMSDffTextRectangles*>(mso_sptRightTriangleTextRect), SAL_N_ELEMENTS( mso_sptRightTriangleTextRect ),
+ 21600, 21600,
+ MIN_INT32, MIN_INT32,
+ const_cast<SvxMSDffVertPair*>(mso_sptRightTriangleGluePoints), SAL_N_ELEMENTS( mso_sptRightTriangleGluePoints ),
+ nullptr, 0 // handles
+};
+
+const SvxMSDffVertPair mso_sptEllipseVert[] =
+{
+ { 10800, 10800 }, { 10800, 10800 }, { 0, 360 }
+};
+const sal_uInt16 mso_sptEllipseSegm[] =
+{
+ 0xa203, 0x6000, 0x8000
+};
+const SvxMSDffTextRectangles mso_sptEllipseTextRect[] =
+{
+ { { 3163, 3163 }, { 18437, 18437 } }
+};
+const SvxMSDffVertPair mso_sptEllipseGluePoints[] =
+{
+ { 10800, 0 }, { 3163, 3163 }, { 0, 10800 }, { 3163, 18437 }, { 10800, 21600 }, { 18437, 18437 }, { 21600, 10800 }, { 18437, 3163 }
+};
+const mso_CustomShape msoEllipse =
+{
+ const_cast<SvxMSDffVertPair*>(mso_sptEllipseVert), SAL_N_ELEMENTS( mso_sptEllipseVert ),
+ const_cast<sal_uInt16*>(mso_sptEllipseSegm), sizeof( mso_sptEllipseSegm ) >> 1,
+ nullptr, 0,
+ nullptr,
+ const_cast<SvxMSDffTextRectangles*>(mso_sptEllipseTextRect), SAL_N_ELEMENTS( mso_sptEllipseTextRect ),
+ 21600, 21600,
+ MIN_INT32, MIN_INT32,
+ const_cast<SvxMSDffVertPair*>(mso_sptEllipseGluePoints), SAL_N_ELEMENTS( mso_sptEllipseGluePoints ),
+ nullptr, 0 // handles
+};
+
+const SvxMSDffVertPair mso_sptParallelogramVert[] = // adjustment1 : 0 - 21600
+{
+ { 0 MSO_I, 0 }, { 21600, 0 }, { 1 MSO_I, 21600 }, { 0, 21600 }
+};
+const sal_uInt16 mso_sptParallelogramSegm[] =
+{
+ 0x4000, 0x0003, 0x6001, 0x8000
+};
+const SvxMSDffCalculationData mso_sptParallelogramCalc[] =
+{
+ { 0x4000, { 0, DFF_Prop_adjustValue, 0 } },
+ { 0x8000, { 0, 21600, DFF_Prop_adjustValue } },
+ { 0x2001, { DFF_Prop_adjustValue, 10, 24 } },
+ { 0x2000, { 0x0402, 1750, 0 } },
+ { 0x8000, { 21600, 0, 0x0403 } },
+ { 0x2001, { 0x400, 1, 2 } },
+ { 0x4000, { 10800, 0x405, 0 } },
+ { 0x2000, { 0x400, 0, 10800 } },
+ { 0x6006, { 0x407, 0x40d, 0 } },
+ { 0x8000, { 10800, 0, 0x405 } },
+ { 0x6006, { 0x407, 0x40c, 21600 } },
+ { 0x8000, { 21600, 0, 0x405 } },
+ { 0x8001, { 21600, 10800, 0x400 } },
+ { 0x8000, { 21600, 0, 0x40c } }
+};
+const SvxMSDffTextRectangles mso_sptParallelogramTextRect[] =
+{
+ { { 3 MSO_I, 3 MSO_I }, { 4 MSO_I, 4 MSO_I } }
+};
+const SvxMSDffVertPair mso_sptParallelogramGluePoints[] =
+{
+ { 6 MSO_I, 0 }, { 10800, 8 MSO_I }, { 11 MSO_I, 10800 }, { 9 MSO_I, 21600 }, { 10800, 10 MSO_I }, { 5 MSO_I, 10800 }
+};
+const SvxMSDffHandle mso_sptParallelogramHandle[] =
+{
+ { SvxMSDffHandleFlags::RANGE,
+ 0x100, 0, 10800, 10800, 0, 21600, MIN_INT32, 0x7fffffff }
+};
+const mso_CustomShape msoParallelogram =
+{
+ const_cast<SvxMSDffVertPair*>(mso_sptParallelogramVert), SAL_N_ELEMENTS( mso_sptParallelogramVert ),
+ const_cast<sal_uInt16*>(mso_sptParallelogramSegm), sizeof( mso_sptParallelogramSegm ) >> 1,
+ const_cast<SvxMSDffCalculationData*>(mso_sptParallelogramCalc), SAL_N_ELEMENTS( mso_sptParallelogramCalc ),
+ const_cast<sal_Int32*>(mso_sptDefault5400),
+ const_cast<SvxMSDffTextRectangles*>(mso_sptParallelogramTextRect), SAL_N_ELEMENTS( mso_sptParallelogramTextRect ),
+ 21600, 21600,
+ MIN_INT32, MIN_INT32,
+ const_cast<SvxMSDffVertPair*>(mso_sptParallelogramGluePoints), SAL_N_ELEMENTS( mso_sptParallelogramGluePoints ),
+ const_cast<SvxMSDffHandle*>(mso_sptParallelogramHandle), SAL_N_ELEMENTS( mso_sptParallelogramHandle ) // handles
+};
+
+const SvxMSDffVertPair mso_sptDiamondVert[] =
+{
+ { 10800, 0 }, { 21600, 10800 }, { 10800, 21600 }, { 0, 10800 }, { 10800, 0 }
+};
+const SvxMSDffTextRectangles mso_sptDiamondTextRect[] =
+{
+ { { 5400, 5400 }, { 16200, 16200 } }
+};
+const mso_CustomShape msoDiamond =
+{
+ const_cast<SvxMSDffVertPair*>(mso_sptDiamondVert), SAL_N_ELEMENTS( mso_sptDiamondVert ),
+ nullptr, 0,
+ nullptr, 0,
+ nullptr,
+ const_cast<SvxMSDffTextRectangles*>(mso_sptDiamondTextRect), SAL_N_ELEMENTS( mso_sptDiamondTextRect ),
+ 21600, 21600,
+ MIN_INT32, MIN_INT32,
+ const_cast<SvxMSDffVertPair*>(mso_sptStandardGluePoints), SAL_N_ELEMENTS( mso_sptStandardGluePoints ),
+ nullptr, 0 // handles
+};
+
+const SvxMSDffVertPair mso_sptTrapezoidVert[] = // adjustment1 : 0 - 10800
+{
+ { 0, 0 }, { 21600, 0 }, {0 MSO_I, 21600 }, { 1 MSO_I, 21600 }
+};
+const sal_uInt16 mso_sptTrapezoidSegm[] =
+{
+ 0x4000, 0x0003, 0x6001, 0x8000
+};
+const SvxMSDffCalculationData mso_sptTrapezoidCalc[] =
+{
+ { 0x8000, { 21600, 0, DFF_Prop_adjustValue } },
+ { 0x2000, { DFF_Prop_adjustValue, 0, 0 } },
+ { 0x2001, { DFF_Prop_adjustValue, 10, 18 } },
+ { 0x2000, { 0x0402, 1750, 0 } },
+ { 0x8000, { 21600, 0, 0x403 } },
+ { 0x2001, { DFF_Prop_adjustValue, 1, 2 } },
+ { 0x8000, { 21600, 0, 0x405 } }
+};
+const SvxMSDffTextRectangles mso_sptTrapezoidTextRect[] =
+{
+ { { 3 MSO_I, 3 MSO_I }, { 4 MSO_I, 4 MSO_I } }
+};
+const SvxMSDffVertPair mso_sptTrapezoidGluePoints[] =
+{
+ { 6 MSO_I, 10800 }, { 10800, 21600 }, { 5 MSO_I, 10800 }, { 10800, 0 }
+};
+const SvxMSDffHandle mso_sptTrapezoidHandle[] =
+{
+ { SvxMSDffHandleFlags::RANGE,
+ 0x100, 1, 10800, 10800, 0, 10800, MIN_INT32, 0x7fffffff }
+};
+const mso_CustomShape msoTrapezoid =
+{
+ const_cast<SvxMSDffVertPair*>(mso_sptTrapezoidVert), SAL_N_ELEMENTS( mso_sptTrapezoidVert ),
+ const_cast<sal_uInt16*>(mso_sptTrapezoidSegm), sizeof( mso_sptTrapezoidSegm ) >> 1,
+ const_cast<SvxMSDffCalculationData*>(mso_sptTrapezoidCalc), SAL_N_ELEMENTS( mso_sptTrapezoidCalc ),
+ const_cast<sal_Int32*>(mso_sptDefault5400),
+ const_cast<SvxMSDffTextRectangles*>(mso_sptTrapezoidTextRect), SAL_N_ELEMENTS( mso_sptTrapezoidTextRect ),
+ 21600, 21600,
+ MIN_INT32, MIN_INT32,
+ const_cast<SvxMSDffVertPair*>(mso_sptTrapezoidGluePoints), SAL_N_ELEMENTS( mso_sptTrapezoidGluePoints ),
+ const_cast<SvxMSDffHandle*>(mso_sptTrapezoidHandle), SAL_N_ELEMENTS( mso_sptTrapezoidHandle ) // handles
+};
+
+/*
+
+ The side of the enclosing square for the regular (all sides the
+ same, all angles the same) octagon described below is 21600. Let's
+ call that 'a'.
+
+ The "adjustment1" is the horizontal (or vertical) distance from a
+ side of the square to the nearest vertex. Let's call that 'd'.
+
+ Let's call the side of the regular octagon 'b'.
+
+ We know a. We want d. d=(a-b)/2
+
+ Pythagoras says that b^2 = 2d^2
+
+ Solving for b, we get b = (sqrt(2)-1)a
+
+
+ !------------a=21600-------!
+
+ !--d--!
+ x--------------x
+ / \
+ / \
+ / \
+ / \
+ / \
+ x x
+ ! !
+ ! !
+ ! !
+ ! !
+ ! !
+ ! !
+ x x
+ \ /
+ \ /
+ \ /
+ \ /
+ \ /
+ x--------------x
+
+*/
+
+const SvxMSDffVertPair mso_sptOctagonVert[] = // adjustment1 : 0 - 10800
+{
+ { 0 MSO_I, 0 }, { 2 MSO_I, 0 }, { 21600, 1 MSO_I }, { 21600, 3 MSO_I },
+ { 2 MSO_I, 21600 }, { 0 MSO_I, 21600 }, { 0, 3 MSO_I }, { 0, 1 MSO_I }
+};
+const sal_uInt16 mso_sptOctagonSegm[] =
+{
+ 0x4000, 0x0007, 0x6001, 0x8000
+};
+const SvxMSDffCalculationData mso_sptOctagonCalc[] =
+{
+ { 0x6000, { DFF_Prop_geoLeft, DFF_Prop_adjustValue, 0 } },
+ { 0x6000, { DFF_Prop_geoTop, DFF_Prop_adjustValue, 0 } },
+ { 0xa000, { DFF_Prop_geoRight, 0, DFF_Prop_adjustValue } },
+ { 0xa000, { DFF_Prop_geoBottom, 0, DFF_Prop_adjustValue } },
+ { 0x2001, { DFF_Prop_adjustValue, 1, 2 } },
+ { 0x6000, { DFF_Prop_geoLeft, 0x404, 0 } },
+ { 0x6000, { DFF_Prop_geoTop, 0x404, 0 } },
+ { 0xa000, { DFF_Prop_geoRight, 0, 0x404 } },
+ { 0xa000, { DFF_Prop_geoBottom, 0, 0x404 } }
+};
+const sal_Int32 mso_sptOctagonDefault[] =
+{
+ 1, static_cast<sal_Int32>((21600 - ((M_SQRT2-1)*21600)) / 2)
+};
+const SvxMSDffTextRectangles mso_sptOctagonTextRect[] =
+{
+ { { 5 MSO_I, 6 MSO_I }, { 7 MSO_I, 8 MSO_I } }
+};
+const SvxMSDffHandle mso_sptOctagonHandle[] =
+{
+ { SvxMSDffHandleFlags::RANGE,
+ 0x100, 0, 10800, 10800, 0, 10800, MIN_INT32, 0x7fffffff }
+};
+const mso_CustomShape msoOctagon =
+{
+ const_cast<SvxMSDffVertPair*>(mso_sptOctagonVert), SAL_N_ELEMENTS( mso_sptOctagonVert ),
+ const_cast<sal_uInt16*>(mso_sptOctagonSegm), sizeof( mso_sptOctagonSegm ) >> 1,
+ const_cast<SvxMSDffCalculationData*>(mso_sptOctagonCalc), SAL_N_ELEMENTS( mso_sptOctagonCalc ),
+ const_cast<sal_Int32*>(mso_sptOctagonDefault),
+ const_cast<SvxMSDffTextRectangles*>(mso_sptOctagonTextRect), SAL_N_ELEMENTS( mso_sptOctagonTextRect ),
+ 21600, 21600,
+ 10800, 10800,
+ const_cast<SvxMSDffVertPair*>(mso_sptStandardGluePoints), SAL_N_ELEMENTS( mso_sptStandardGluePoints ),
+ const_cast<SvxMSDffHandle*>(mso_sptOctagonHandle), SAL_N_ELEMENTS( mso_sptOctagonHandle ) // handles
+};
+
+const SvxMSDffVertPair mso_sptIsocelesTriangleVert[] = // adjustment1 : 0 - 21600
+{
+ { 0 MSO_I, 0 }, { 21600, 21600 }, { 0, 21600 }
+};
+const sal_uInt16 mso_sptIsocelesTriangleSegm[] =
+{
+ 0x4000, 0x0002, 0x6001, 0x8000
+};
+const SvxMSDffCalculationData mso_sptIsocelesTriangleCalc[] =
+{
+ { 0x4000, { 0, DFF_Prop_adjustValue, 0 } },
+ { 0x2001, { DFF_Prop_adjustValue, 1, 2 } },
+ { 0x2000, { 0x401, 10800, 0 } },
+ { 0x2001, { DFF_Prop_adjustValue, 2, 3 } },
+ { 0x2000, { 0x403, 7200, 0 } },
+ { 0x8000, { 21600, 0, 0x400 } },
+ { 0x2001, { 0x405, 1, 2 } },
+ { 0x8000, { 21600, 0, 0x406 } }
+};
+const SvxMSDffTextRectangles mso_sptIsocelesTriangleTextRect[] =
+{
+ { { 1 MSO_I, 10800 }, { 2 MSO_I, 18000 } },
+ { { 3 MSO_I, 7200 }, { 4 MSO_I, 21600 } }
+};
+const SvxMSDffVertPair mso_sptIsocelesTriangleGluePoints[] =
+{
+ { 0 MSO_I, 0 }, { 1 MSO_I, 10800 }, { 0, 21600 }, { 10800, 21600 }, { 21600, 21600 }, { 7 MSO_I, 10800 }
+};
+const SvxMSDffHandle mso_sptIsocelesTriangleHandle[] =
+{
+ { SvxMSDffHandleFlags::RANGE,
+ 0x100, 0, 10800, 10800, 0, 21600, MIN_INT32, 0x7fffffff }
+};
+const mso_CustomShape msoIsocelesTriangle =
+{
+ const_cast<SvxMSDffVertPair*>(mso_sptIsocelesTriangleVert), SAL_N_ELEMENTS( mso_sptIsocelesTriangleVert ),
+ const_cast<sal_uInt16*>(mso_sptIsocelesTriangleSegm), sizeof( mso_sptIsocelesTriangleSegm ) >> 1,
+ const_cast<SvxMSDffCalculationData*>(mso_sptIsocelesTriangleCalc), SAL_N_ELEMENTS( mso_sptIsocelesTriangleCalc ),
+ const_cast<sal_Int32*>(mso_sptDefault10800),
+ const_cast<SvxMSDffTextRectangles*>(mso_sptIsocelesTriangleTextRect), SAL_N_ELEMENTS( mso_sptIsocelesTriangleTextRect ),
+ 21600, 21600,
+ MIN_INT32, MIN_INT32,
+ const_cast<SvxMSDffVertPair*>(mso_sptIsocelesTriangleGluePoints), SAL_N_ELEMENTS( mso_sptIsocelesTriangleGluePoints ),
+ const_cast<SvxMSDffHandle*>(mso_sptIsocelesTriangleHandle), SAL_N_ELEMENTS( mso_sptIsocelesTriangleHandle ) // handles
+};
+
+const SvxMSDffVertPair mso_sptHexagonVert[] = // adjustment1 : 0 - 10800
+{
+ { 0 MSO_I, 0 }, { 1 MSO_I, 0 }, { 21600, 10800 }, { 1 MSO_I, 21600 },
+ { 0 MSO_I, 21600 }, { 0, 10800 }
+};
+const sal_uInt16 mso_sptHexagonSegm[] =
+{
+ 0x4000, 0x0005, 0x6001, 0x8000
+};
+const SvxMSDffCalculationData mso_sptHexagonCalc[] =
+{
+ { 0x2000, { DFF_Prop_adjustValue, 0, 0 } },
+ { 0x8000, { 21600, 0, DFF_Prop_adjustValue } },
+ { 0x2001, { DFF_Prop_adjustValue, 100, 234 } },
+ { 0x2000, { 0x402, 1700, 0 } },
+ { 0x8000, { 21600, 0, 0x403 } }
+};
+const SvxMSDffTextRectangles mso_sptHexagonTextRect[] =
+{
+ { { 3 MSO_I, 3 MSO_I }, { 4 MSO_I, 4 MSO_I } }
+};
+const SvxMSDffHandle mso_sptHexagonHandle[] =
+{
+ { SvxMSDffHandleFlags::RANGE,
+ 0x100, 0, 10800, 10800, 0, 10800, MIN_INT32, 0x7fffffff }
+};
+const mso_CustomShape msoHexagon =
+{
+ const_cast<SvxMSDffVertPair*>(mso_sptHexagonVert), SAL_N_ELEMENTS( mso_sptHexagonVert ),
+ const_cast<sal_uInt16*>(mso_sptHexagonSegm), sizeof( mso_sptHexagonSegm ) >> 1,
+ const_cast<SvxMSDffCalculationData*>(mso_sptHexagonCalc), SAL_N_ELEMENTS( mso_sptHexagonCalc ),
+ const_cast<sal_Int32*>(mso_sptDefault5400),
+ const_cast<SvxMSDffTextRectangles*>(mso_sptHexagonTextRect), SAL_N_ELEMENTS( mso_sptHexagonTextRect ),
+ 21600, 21600,
+ MIN_INT32, MIN_INT32,
+ const_cast<SvxMSDffVertPair*>(mso_sptStandardGluePoints), SAL_N_ELEMENTS( mso_sptStandardGluePoints ),
+ const_cast<SvxMSDffHandle*>(mso_sptHexagonHandle), SAL_N_ELEMENTS( mso_sptHexagonHandle ) // handles
+};
+
+const SvxMSDffVertPair mso_sptPentagonVert[] =
+{
+ { 10800, 0 }, { 0, 8260 }, { 4230, 21600 }, { 17370, 21600 },
+ { 21600, 8260 }, { 10800, 0 }
+};
+const SvxMSDffTextRectangles mso_sptPentagonTextRect[] =
+{
+ { { 4230, 5080 }, { 17370, 21600 } }
+};
+const SvxMSDffVertPair mso_sptPentagonGluePoints[] =
+{
+ { 10800, 0 }, { 0, 8260 }, { 4230, 21600 }, { 10800, 21600 },
+ { 17370, 21600 }, { 21600, 8260 }
+};
+const mso_CustomShape msoPentagon =
+{
+ const_cast<SvxMSDffVertPair*>(mso_sptPentagonVert), SAL_N_ELEMENTS( mso_sptPentagonVert ),
+ nullptr, 0,
+ nullptr, 0,
+ nullptr,
+ const_cast<SvxMSDffTextRectangles*>(mso_sptPentagonTextRect), SAL_N_ELEMENTS( mso_sptPentagonTextRect ),
+ 21600, 21600,
+ MIN_INT32, MIN_INT32,
+ const_cast<SvxMSDffVertPair*>(mso_sptPentagonGluePoints), SAL_N_ELEMENTS( mso_sptPentagonGluePoints ),
+ nullptr, 0 // handles
+};
+
+const SvxMSDffVertPair mso_sptPlusVert[] = // adjustment1 : 0 - 10800
+{
+ { 1 MSO_I, 0 }, { 2 MSO_I, 0 }, { 2 MSO_I, 1 MSO_I }, { 21600, 1 MSO_I },
+ { 21600, 3 MSO_I }, { 2 MSO_I, 3 MSO_I }, { 2 MSO_I, 21600 }, { 1 MSO_I, 21600 },
+ { 1 MSO_I, 3 MSO_I }, { 0, 3 MSO_I }, { 0, 1 MSO_I }, { 1 MSO_I, 1 MSO_I }, { 1 MSO_I, 0 }
+};
+const SvxMSDffCalculationData mso_sptPlusCalc[] =
+{
+ { 0x2001, { DFF_Prop_adjustValue, 10799, 10800 } },
+ { 0x2000, { 0x400, 0, 0 } },
+ { 0xa000, { DFF_Prop_geoRight, 0, 0x400 } },
+ { 0xa000, { DFF_Prop_geoBottom, 0, 0x400 } }
+};
+const SvxMSDffTextRectangles mso_sptPlusTextRect[] =
+{
+ { { 1 MSO_I, 1 MSO_I }, { 2 MSO_I, 3 MSO_I } }
+};
+const SvxMSDffHandle mso_sptPlusHandle[] =
+{
+ { SvxMSDffHandleFlags::RANGE | SvxMSDffHandleFlags::SWITCHED,
+ 0x100, 0, 10800, 10800, 0, 10800, MIN_INT32, 0x7fffffff }
+};
+const mso_CustomShape msoPlus =
+{
+ const_cast<SvxMSDffVertPair*>(mso_sptPlusVert), SAL_N_ELEMENTS( mso_sptPlusVert ),
+ nullptr, 0,
+ const_cast<SvxMSDffCalculationData*>(mso_sptPlusCalc), SAL_N_ELEMENTS( mso_sptPlusCalc ),
+ const_cast<sal_Int32*>(mso_sptDefault5400),
+ const_cast<SvxMSDffTextRectangles*>(mso_sptPlusTextRect), SAL_N_ELEMENTS( mso_sptPlusTextRect ),
+ 21600, 21600,
+ 10800, 10800,
+ const_cast<SvxMSDffVertPair*>(mso_sptStandardGluePoints), SAL_N_ELEMENTS( mso_sptStandardGluePoints ),
+ const_cast<SvxMSDffHandle*>(mso_sptPlusHandle), SAL_N_ELEMENTS( mso_sptPlusHandle ) // handles
+};
+
+const SvxMSDffVertPair mso_sptCanVert[] = // adjustment1 : 0 - 10800
+{
+ { 44, 0 },
+ { 20, 0 }, { 0, 2 MSO_I }, { 0, 0 MSO_I }, // ccp
+ { 0, 3 MSO_I }, // p
+ { 0, 4 MSO_I }, { 20, 21600 }, { 44, 21600 }, // ccp
+ { 68, 21600 }, { 88, 4 MSO_I }, { 88, 3 MSO_I }, // ccp
+ { 88, 0 MSO_I }, // p
+ { 88, 2 MSO_I }, { 68, 0 }, { 44, 0 }, // ccp
+ { 44, 0 }, // p
+ { 20, 0 }, { 0, 2 MSO_I }, { 0, 0 MSO_I }, // ccp
+ { 0, 5 MSO_I }, { 20, 6 MSO_I }, { 44, 6 MSO_I }, // ccp
+ { 68, 6 MSO_I },{ 88, 5 MSO_I }, { 88, 0 MSO_I }, // ccp
+ { 88, 2 MSO_I },{ 68, 0 }, { 44, 0 } // ccp
+};
+const sal_uInt16 mso_sptCanSegm[] =
+{
+ 0x4000, 0x2001, 0x0001, 0x2002, 0x0001, 0x2001, 0x6001, 0x8000,
+ 0x4000, 0x2004, 0x6001, 0x8000
+};
+const SvxMSDffCalculationData mso_sptCanCalc[] =
+{
+ { 0x2001, { DFF_Prop_adjustValue, 2, 4 } }, // 1/4
+ { 0x2001, { 0x0400, 6, 11 } },
+ { 0xa000, { 0x0400, 0, 0x0401 } },
+ { 0x8000, { 21600, 0, 0x0400 } },
+ { 0x6000, { 0x0403, 0x0401, 0 } },
+ { 0x6000, { 0x0400, 0x0401, 0 } },
+ { 0x2001, { DFF_Prop_adjustValue, 2, 2 } },
+ { 0x0000, { 44, 0, 0 } }
+};
+const SvxMSDffTextRectangles mso_sptCanTextRect[] =
+{
+ { { 0, 6 MSO_I }, { 88, 3 MSO_I } }
+};
+const SvxMSDffVertPair mso_sptCanGluePoints[] =
+{
+ { 44, 6 MSO_I }, { 44, 0 }, { 0, 10800 }, { 44, 21600 }, { 88, 10800 }
+};
+const SvxMSDffHandle mso_sptCanHandle[] =
+{
+ { SvxMSDffHandleFlags::RANGE,
+ 7 + 3, 0x100, 44, 10800, MIN_INT32, 0x7fffffff, 0, 10800 }
+};
+const mso_CustomShape msoCan =
+{
+ const_cast<SvxMSDffVertPair*>(mso_sptCanVert), SAL_N_ELEMENTS( mso_sptCanVert ),
+ const_cast<sal_uInt16*>(mso_sptCanSegm), sizeof( mso_sptCanSegm ) >> 1,
+ const_cast<SvxMSDffCalculationData*>(mso_sptCanCalc), SAL_N_ELEMENTS( mso_sptCanCalc ),
+ const_cast<sal_Int32*>(mso_sptDefault5400),
+ const_cast<SvxMSDffTextRectangles*>(mso_sptCanTextRect), SAL_N_ELEMENTS( mso_sptCanTextRect ),
+ 88, 21600,
+ MIN_INT32, MIN_INT32,
+ const_cast<SvxMSDffVertPair*>(mso_sptCanGluePoints), SAL_N_ELEMENTS( mso_sptCanGluePoints ),
+ const_cast<SvxMSDffHandle*>(mso_sptCanHandle), SAL_N_ELEMENTS( mso_sptCanHandle ) // handles
+};
+
+const SvxMSDffVertPair mso_sptArrowVert[] = // adjustment1: x 0 - 21600
+{ // adjustment2: y 0 - 10800
+ { 0, 0 MSO_I }, { 1 MSO_I, 0 MSO_I }, { 1 MSO_I, 0 }, { 21600, 10800 },
+ { 1 MSO_I, 21600 }, { 1 MSO_I, 2 MSO_I }, { 0, 2 MSO_I }
+};
+const sal_uInt16 mso_sptArrowSegm[] =
+{
+ 0x4000, 0x0006, 0x6001, 0x8000
+};
+const SvxMSDffCalculationData mso_sptArrowCalc[] =
+{
+ { 0x2000, { DFF_Prop_adjust2Value, 0, 0 } },
+ { 0x2000, { DFF_Prop_adjustValue, 0, 0 } },
+ { 0x8000, { 21600, 0, DFF_Prop_adjust2Value } },
+ { 0x8000, { 21600, 0, 0x401 } },
+ { 0x6001, { 0x403, 0x400, 10800 } },
+ { 0x6000, { 0x401, 0x404, 0 } },
+ { 0x6001, { 0x401, 0x400, 10800 } },
+ { 0xa000, { 0x401, 0, 0x406 } }
+};
+const SvxMSDffTextRectangles mso_sptArrowTextRect[] =
+{
+ { { 0, 0 MSO_I }, { 5 MSO_I, 2 MSO_I } }
+};
+const SvxMSDffHandle mso_sptArrowHandle[] =
+{
+ { SvxMSDffHandleFlags::RANGE,
+ 0x100, 0x101, 10800, 10800, 0, 21600, 0, 10800 }
+};
+const mso_CustomShape msoArrow =
+{
+ const_cast<SvxMSDffVertPair*>(mso_sptArrowVert), SAL_N_ELEMENTS( mso_sptArrowVert ),
+ const_cast<sal_uInt16*>(mso_sptArrowSegm), sizeof( mso_sptArrowSegm ) >> 1,
+ const_cast<SvxMSDffCalculationData*>(mso_sptArrowCalc), SAL_N_ELEMENTS( mso_sptArrowCalc ),
+ const_cast<sal_Int32*>(mso_sptDefault16200and5400),
+ const_cast<SvxMSDffTextRectangles*>(mso_sptArrowTextRect), SAL_N_ELEMENTS( mso_sptArrowTextRect ),
+ 21600, 21600,
+ MIN_INT32, MIN_INT32,
+ nullptr, 0,
+ const_cast<SvxMSDffHandle*>(mso_sptArrowHandle), SAL_N_ELEMENTS( mso_sptArrowHandle ) // handles
+};
+
+const SvxMSDffVertPair mso_sptLeftArrowVert[] = // adjustment1: x 0 - 21600
+{ // adjustment2: y 0 - 10800
+ { 21600, 0 MSO_I }, { 1 MSO_I, 0 MSO_I }, { 1 MSO_I, 0 }, { 0, 10800 },
+ { 1 MSO_I, 21600 }, { 1 MSO_I, 2 MSO_I }, { 21600, 2 MSO_I }
+};
+const sal_uInt16 mso_sptLeftArrowSegm[] =
+{
+ 0x4000, 0x0006, 0x6001, 0x8000
+};
+const sal_Int32 mso_sptLeftArrowDefault[] =
+{
+ 2, 5400, 5400
+};
+const SvxMSDffTextRectangles mso_sptLeftArrowTextRect[] =
+{
+ { { 7 MSO_I, 0 MSO_I }, { 21600, 2 MSO_I } }
+};
+const SvxMSDffHandle mso_sptLeftArrowHandle[] =
+{
+ { SvxMSDffHandleFlags::RANGE,
+ 0x100, 0x101, 10800, 10800, 0, 21600, 0, 10800 }
+};
+const mso_CustomShape msoLeftArrow =
+{
+ const_cast<SvxMSDffVertPair*>(mso_sptLeftArrowVert), SAL_N_ELEMENTS( mso_sptLeftArrowVert ),
+ const_cast<sal_uInt16*>(mso_sptLeftArrowSegm), sizeof( mso_sptLeftArrowSegm ) >> 1,
+ const_cast<SvxMSDffCalculationData*>(mso_sptArrowCalc), SAL_N_ELEMENTS( mso_sptArrowCalc ),
+ const_cast<sal_Int32*>(mso_sptLeftArrowDefault),
+ const_cast<SvxMSDffTextRectangles*>(mso_sptLeftArrowTextRect), SAL_N_ELEMENTS( mso_sptLeftArrowTextRect ),
+ 21600, 21600,
+ MIN_INT32, MIN_INT32,
+ nullptr, 0,
+ const_cast<SvxMSDffHandle*>(mso_sptLeftArrowHandle), SAL_N_ELEMENTS( mso_sptLeftArrowHandle ) // handles
+};
+
+const SvxMSDffVertPair mso_sptDownArrowVert[] = // adjustment1: x 0 - 21600
+{ // adjustment2: y 0 - 10800
+ { 0 MSO_I, 0 }, { 0 MSO_I, 1 MSO_I }, { 0, 1 MSO_I }, { 10800, 21600 },
+ { 21600, 1 MSO_I }, { 2 MSO_I, 1 MSO_I }, { 2 MSO_I, 0 }
+};
+const sal_uInt16 mso_sptDownArrowSegm[] =
+{
+ 0x4000, 0x0006, 0x6001, 0x8000
+};
+const SvxMSDffTextRectangles mso_sptDownArrowTextRect[] =
+{
+ { { 0 MSO_I, 0 }, { 2 MSO_I, 5 MSO_I } }
+};
+const SvxMSDffHandle mso_sptDownArrowHandle[] =
+{
+ { SvxMSDffHandleFlags::RANGE,
+ 0x101, 0x100, 10800, 10800, 0, 10800, 0, 21600 }
+};
+const mso_CustomShape msoDownArrow =
+{
+ const_cast<SvxMSDffVertPair*>(mso_sptDownArrowVert), SAL_N_ELEMENTS( mso_sptDownArrowVert ),
+ const_cast<sal_uInt16*>(mso_sptDownArrowSegm), sizeof( mso_sptDownArrowSegm ) >> 1,
+ const_cast<SvxMSDffCalculationData*>(mso_sptArrowCalc), SAL_N_ELEMENTS( mso_sptArrowCalc ),
+ const_cast<sal_Int32*>(mso_sptDefault16200and5400),
+ const_cast<SvxMSDffTextRectangles*>(mso_sptDownArrowTextRect), SAL_N_ELEMENTS( mso_sptDownArrowTextRect ),
+ 21600, 21600,
+ MIN_INT32, MIN_INT32,
+ nullptr, 0,
+ const_cast<SvxMSDffHandle*>(mso_sptDownArrowHandle), SAL_N_ELEMENTS( mso_sptDownArrowHandle ) // handles
+};
+
+const SvxMSDffVertPair mso_sptUpArrowVert[] = // adjustment1: x 0 - 21600
+{ // adjustment2: y 0 - 10800
+ { 0 MSO_I, 21600 }, { 0 MSO_I, 1 MSO_I }, { 0, 1 MSO_I }, { 10800, 0 },
+ { 21600, 1 MSO_I }, { 2 MSO_I, 1 MSO_I }, { 2 MSO_I, 21600 }
+};
+const sal_uInt16 mso_sptUpArrowSegm[] =
+{
+ 0x4000, 0x0006, 0x6001, 0x8000
+};
+const sal_Int32 mso_sptUpArrowDefault[] =
+{
+ 2, 5400, 5400
+};
+const SvxMSDffTextRectangles mso_sptUpArrowTextRect[] =
+{
+ { { 0 MSO_I, 7 MSO_I }, { 2 MSO_I, 21600 } }
+};
+const SvxMSDffHandle mso_sptUpArrowHandle[] =
+{
+ { SvxMSDffHandleFlags::RANGE,
+ 0x101, 0x100, 10800, 10800, 0, 10800, 0, 21600 }
+};
+const mso_CustomShape msoUpArrow =
+{
+ const_cast<SvxMSDffVertPair*>(mso_sptUpArrowVert), SAL_N_ELEMENTS( mso_sptUpArrowVert ),
+ const_cast<sal_uInt16*>(mso_sptUpArrowSegm), sizeof( mso_sptUpArrowSegm ) >> 1,
+ const_cast<SvxMSDffCalculationData*>(mso_sptArrowCalc), SAL_N_ELEMENTS( mso_sptArrowCalc ),
+ const_cast<sal_Int32*>(mso_sptUpArrowDefault),
+ const_cast<SvxMSDffTextRectangles*>(mso_sptUpArrowTextRect), SAL_N_ELEMENTS( mso_sptUpArrowTextRect ),
+ 21600, 21600,
+ MIN_INT32, MIN_INT32,
+ nullptr, 0,
+ const_cast<SvxMSDffHandle*>(mso_sptUpArrowHandle), SAL_N_ELEMENTS( mso_sptUpArrowHandle ) // handles
+};
+
+const SvxMSDffVertPair mso_sptLeftRightArrowVert[] = // adjustment1: x 0 - 10800
+{ // adjustment2: y 0 - 10800
+ { 0, 10800 }, { 0 MSO_I, 0 }, { 0 MSO_I, 1 MSO_I }, { 2 MSO_I, 1 MSO_I },
+ { 2 MSO_I, 0 }, { 21600, 10800 }, { 2 MSO_I, 21600 }, { 2 MSO_I, 3 MSO_I },
+ { 0 MSO_I, 3 MSO_I }, { 0 MSO_I, 21600 }
+};
+const sal_uInt16 mso_sptLeftRightArrowSegm[] =
+{
+ 0x4000, 0x0009, 0x6001, 0x8000
+};
+const SvxMSDffCalculationData mso_sptDoubleArrowCalc[] =
+{
+ { 0x2000, { DFF_Prop_adjustValue, 0, 0 } },
+ { 0x2000, { DFF_Prop_adjust2Value, 0, 0 } },
+ { 0x8000, { 21600, 0, DFF_Prop_adjustValue } },
+ { 0x8000, { 21600, 0, DFF_Prop_adjust2Value } },
+ { 0x8000, { 10800, 0, DFF_Prop_adjust2Value } },
+ { 0x6001, { DFF_Prop_adjustValue, 0x404, 10800 } },
+ { 0x8000, { 21600, 0, 0x405 } },
+ { 0x8000, { 10800, 0, DFF_Prop_adjustValue } },
+ { 0x6001, { DFF_Prop_adjust2Value, 0x407, 10800 } },
+ { 0x8000, { 21600, 0, 0x408 } }
+};
+const sal_Int32 mso_sptLeftRightArrowDefault[] =
+{
+ 2, 4300, 5400
+};
+const SvxMSDffTextRectangles mso_sptLeftRightArrowTextRect[] =
+{
+ { { 5 MSO_I, 1 MSO_I }, { 6 MSO_I, 3 MSO_I } }
+};
+const SvxMSDffHandle mso_sptLeftRightArrowHandle[] =
+{
+ { SvxMSDffHandleFlags::RANGE,
+ 0x100, 0x101, 10800, 10800, 0, 10800, 0, 10800 }
+};
+const mso_CustomShape msoLeftRightArrow =
+{
+ const_cast<SvxMSDffVertPair*>(mso_sptLeftRightArrowVert), SAL_N_ELEMENTS( mso_sptLeftRightArrowVert ),
+ const_cast<sal_uInt16*>(mso_sptLeftRightArrowSegm), sizeof( mso_sptLeftRightArrowSegm ) >> 1,
+ const_cast<SvxMSDffCalculationData*>(mso_sptDoubleArrowCalc), SAL_N_ELEMENTS( mso_sptDoubleArrowCalc ),
+ const_cast<sal_Int32*>(mso_sptLeftRightArrowDefault),
+ const_cast<SvxMSDffTextRectangles*>(mso_sptLeftRightArrowTextRect), SAL_N_ELEMENTS( mso_sptLeftRightArrowTextRect ),
+ 21600, 21600,
+ MIN_INT32, MIN_INT32,
+ nullptr, 0,
+ const_cast<SvxMSDffHandle*>(mso_sptLeftRightArrowHandle), SAL_N_ELEMENTS( mso_sptLeftRightArrowHandle ) // handles
+};
+
+const SvxMSDffVertPair mso_sptUpDownArrowVert[] = // adjustment1: x 0 - 10800
+{ // adjustment2: y 0 - 10800
+ { 0, 1 MSO_I }, { 10800, 0 }, { 21600, 1 MSO_I }, { 2 MSO_I, 1 MSO_I },
+ { 2 MSO_I, 3 MSO_I }, { 21600, 3 MSO_I }, { 10800, 21600 }, { 0, 3 MSO_I },
+ { 0 MSO_I, 3 MSO_I }, { 0 MSO_I, 1 MSO_I }
+};
+const sal_uInt16 mso_sptUpDownArrowSegm[] =
+{
+ 0x4000, 0x0009, 0x6001, 0x8000
+};
+const sal_Int32 mso_sptUpDownArrowDefault[] =
+{
+ 2, 5400, 4300
+};
+const SvxMSDffTextRectangles mso_sptUpDownArrowTextRect[] =
+{
+ { { 0 MSO_I, 8 MSO_I }, { 2 MSO_I, 9 MSO_I } }
+};
+const SvxMSDffHandle mso_sptUpDownArrowHandle[] =
+{
+ { SvxMSDffHandleFlags::RANGE,
+ 0x100, 0x101, 10800, 10800, 0, 10800, 0, 10800 }
+};
+const mso_CustomShape msoUpDownArrow =
+{
+ const_cast<SvxMSDffVertPair*>(mso_sptUpDownArrowVert), SAL_N_ELEMENTS( mso_sptUpDownArrowVert ),
+ const_cast<sal_uInt16*>(mso_sptUpDownArrowSegm), sizeof( mso_sptUpDownArrowSegm ) >> 1,
+ const_cast<SvxMSDffCalculationData*>(mso_sptDoubleArrowCalc), SAL_N_ELEMENTS( mso_sptDoubleArrowCalc ),
+ const_cast<sal_Int32*>(mso_sptUpDownArrowDefault),
+ const_cast<SvxMSDffTextRectangles*>(mso_sptUpDownArrowTextRect), SAL_N_ELEMENTS( mso_sptUpDownArrowTextRect ),
+ 21600, 21600,
+ MIN_INT32, MIN_INT32,
+ nullptr, 0,
+ const_cast<SvxMSDffHandle*>(mso_sptUpDownArrowHandle), SAL_N_ELEMENTS( mso_sptUpDownArrowHandle ) // handles
+};
+
+const SvxMSDffVertPair mso_sptQuadArrowVert[] = // adjustment1: x 0 - 10800, adjustment2: x 0 - 10800
+{ // adjustment3: y 0 - 10800
+ { 0, 10800 }, { 0 MSO_I, 1 MSO_I }, { 0 MSO_I, 2 MSO_I }, { 2 MSO_I, 2 MSO_I },
+ { 2 MSO_I, 0 MSO_I }, { 1 MSO_I, 0 MSO_I }, { 10800, 0 }, { 3 MSO_I, 0 MSO_I },
+ { 4 MSO_I, 0 MSO_I }, { 4 MSO_I, 2 MSO_I }, { 5 MSO_I, 2 MSO_I }, { 5 MSO_I, 1 MSO_I },
+ { 21600, 10800 }, { 5 MSO_I, 3 MSO_I }, { 5 MSO_I, 4 MSO_I }, { 4 MSO_I, 4 MSO_I },
+ { 4 MSO_I, 5 MSO_I }, { 3 MSO_I, 5 MSO_I }, { 10800, 21600 }, { 1 MSO_I, 5 MSO_I },
+ { 2 MSO_I, 5 MSO_I }, { 2 MSO_I, 4 MSO_I }, { 0 MSO_I, 4 MSO_I }, { 0 MSO_I, 3 MSO_I }
+};
+const sal_uInt16 mso_sptQuadArrowSegm[] =
+{
+ 0x4000, 0x0017, 0x6001, 0x8000
+};
+const SvxMSDffCalculationData mso_sptQuadArrowCalc[] =
+{
+ { 0x2000, { DFF_Prop_adjust3Value, 0, 0 } },
+ { 0x2000, { DFF_Prop_adjustValue, 0, 0 } },
+ { 0x2000, { DFF_Prop_adjust2Value, 0, 0 } },
+ { 0x8000, { 21600, 0, DFF_Prop_adjustValue } },
+ { 0x8000, { 21600, 0, DFF_Prop_adjust2Value } },
+ { 0x8000, { 21600, 0, DFF_Prop_adjust3Value } }
+};
+const sal_Int32 mso_sptQuadArrowDefault[] =
+{
+ 3, 6500, 8600, 4300
+};
+const SvxMSDffTextRectangles mso_sptQuadArrowTextRect[] = // todo
+{
+ { { 0, 0 }, { 21600, 21600 } }
+};
+const SvxMSDffHandle mso_sptQuadArrowHandle[] =
+{
+ { SvxMSDffHandleFlags::RANGE | SvxMSDffHandleFlags::RANGE_Y_MAX_IS_SPECIAL | SvxMSDffHandleFlags::RANGE_X_MIN_IS_SPECIAL,
+ 0x101, 0x102, 10800, 10800, 0x100, 10800, 0, 0x100 },
+ { SvxMSDffHandleFlags::RANGE | SvxMSDffHandleFlags::RANGE_X_MIN_IS_SPECIAL | SvxMSDffHandleFlags::RANGE_X_MAX_IS_SPECIAL,
+ 0x100, 0, 10800, 10800, 0x102, 0x101, MIN_INT32, 0x7fffffff }
+};
+const mso_CustomShape msoQuadArrow =
+{
+ const_cast<SvxMSDffVertPair*>(mso_sptQuadArrowVert), SAL_N_ELEMENTS( mso_sptQuadArrowVert ),
+ const_cast<sal_uInt16*>(mso_sptQuadArrowSegm), sizeof( mso_sptQuadArrowSegm ) >> 1,
+ const_cast<SvxMSDffCalculationData*>(mso_sptQuadArrowCalc), SAL_N_ELEMENTS( mso_sptQuadArrowCalc ),
+ const_cast<sal_Int32*>(mso_sptQuadArrowDefault),
+ const_cast<SvxMSDffTextRectangles*>(mso_sptQuadArrowTextRect), SAL_N_ELEMENTS( mso_sptQuadArrowTextRect ),
+ 21600, 21600,
+ MIN_INT32, MIN_INT32,
+ nullptr, 0,
+ const_cast<SvxMSDffHandle*>(mso_sptQuadArrowHandle), SAL_N_ELEMENTS( mso_sptQuadArrowHandle ) // handles
+};
+
+const SvxMSDffVertPair mso_sptLeftRightUpArrowVert[] = // adjustment1: x 0 - 10800, adjustment2: x 0 - 10800
+{ // adjustment3: y 0 - 21600
+ { 10800, 0 }, { 3 MSO_I, 2 MSO_I }, { 4 MSO_I, 2 MSO_I }, { 4 MSO_I, 1 MSO_I },
+ { 5 MSO_I, 1 MSO_I }, { 5 MSO_I, 0 MSO_I }, { 21600, 10800 }, { 5 MSO_I, 3 MSO_I },
+ { 5 MSO_I, 4 MSO_I }, { 2 MSO_I, 4 MSO_I }, { 2 MSO_I, 3 MSO_I }, { 0, 10800 },
+ { 2 MSO_I, 0 MSO_I }, { 2 MSO_I, 1 MSO_I }, { 1 MSO_I, 1 MSO_I }, { 1 MSO_I, 2 MSO_I },
+ { 0 MSO_I, 2 MSO_I }
+};
+const sal_uInt16 mso_sptLeftRightUpArrowSegm[] =
+{
+ 0x4000, 0x0010, 0x6001, 0x8000
+};
+const SvxMSDffCalculationData mso_sptLeftRightUpArrowCalc[] =
+{
+ { 0x2000, { DFF_Prop_adjustValue, 0, 0 } }, // 0
+ { 0x2000, { DFF_Prop_adjust2Value, 0, 0 } }, // 1
+ { 0x6001, { 0x0403, DFF_Prop_adjust3Value, 21600 } }, // 2
+ { 0x8000, { 21600, 0, DFF_Prop_adjustValue } }, // 3
+ { 0x8000, { 21600, 0, DFF_Prop_adjust2Value } }, // 4
+ { 0x8000, { 21600, 0, 0x0402 } } // 5
+};
+const sal_Int32 mso_sptLeftRightUpArrowDefault[] =
+{
+ 3, 6500, 8600, 6200
+};
+const SvxMSDffTextRectangles mso_sptLeftRightUpArrowTextRect[] = // todo
+{
+ { { 0, 0 }, { 21600, 21600 } }
+};
+const SvxMSDffHandle mso_sptLeftRightUpArrowHandle[] =
+{
+ { SvxMSDffHandleFlags::RANGE | SvxMSDffHandleFlags::RANGE_Y_MAX_IS_SPECIAL | SvxMSDffHandleFlags::RANGE_X_MIN_IS_SPECIAL,
+ 0x101, 0x102, 10800, 10800, 0x100, 10800, 0, 0x100 },
+ { SvxMSDffHandleFlags::RANGE | SvxMSDffHandleFlags::RANGE_X_MIN_IS_SPECIAL | SvxMSDffHandleFlags::RANGE_X_MAX_IS_SPECIAL,
+ 0x100, 0, 10800, 10800, 0x102, 0x101, MIN_INT32, 0x7fffffff }
+};
+const mso_CustomShape msoLeftRightUpArrow =
+{
+ const_cast<SvxMSDffVertPair*>(mso_sptLeftRightUpArrowVert), SAL_N_ELEMENTS( mso_sptLeftRightUpArrowVert ),
+ const_cast<sal_uInt16*>(mso_sptLeftRightUpArrowSegm), sizeof( mso_sptLeftRightUpArrowSegm ) >> 1,
+ const_cast<SvxMSDffCalculationData*>(mso_sptLeftRightUpArrowCalc), SAL_N_ELEMENTS( mso_sptLeftRightUpArrowCalc ),
+ const_cast<sal_Int32*>(mso_sptLeftRightUpArrowDefault),
+ const_cast<SvxMSDffTextRectangles*>(mso_sptLeftRightUpArrowTextRect), SAL_N_ELEMENTS( mso_sptLeftRightUpArrowTextRect ),
+ 21600, 21600,
+ MIN_INT32, MIN_INT32,
+ nullptr, 0,
+ const_cast<SvxMSDffHandle*>(mso_sptLeftRightUpArrowHandle), SAL_N_ELEMENTS( mso_sptLeftRightUpArrowHandle ) // handles
+};
+
+const SvxMSDffVertPair mso_sptBentArrowVert[] = // adjustment1 : x 12427 - 21600
+{ // adjustment2 : y 0 - 6079
+ { 0, 21600 }, { 0, 12160 }, { 12427, 1 MSO_I }, { 0 MSO_I, 1 MSO_I },
+ { 0 MSO_I, 0 }, { 21600, 6079 }, { 0 MSO_I, 12158 }, { 0 MSO_I, 2 MSO_I },
+ { 12427, 2 MSO_I }, { 4 MSO_I, 12160 }, { 4 MSO_I, 21600 }
+};
+const sal_uInt16 mso_sptBentArrowSegm[] =
+{
+ 0x4000, 0x0001, 0xa801, 0x0006, 0xa701, 0x0001, 0x6001, 0x8000
+};
+const SvxMSDffCalculationData mso_sptBentArrowCalc[] =
+{
+ { 0x2000, { DFF_Prop_adjustValue, 0, 0 } },
+ { 0x2000, { DFF_Prop_adjust2Value, 0, 0 } },
+ { 0x8000, { 12158, 0, DFF_Prop_adjust2Value } },
+ { 0x8000, { 6079, 0, DFF_Prop_adjust2Value } },
+ { 0x2001, { 0x0403, 2, 1 } }
+};
+const sal_Int32 mso_sptBentArrowDefault[] =
+{
+ 2, 15100, 2900
+};
+const SvxMSDffTextRectangles mso_sptBentArrowTextRect[] = // todo
+{
+ { { 0, 0 }, { 21600, 21600 } }
+};
+const SvxMSDffHandle mso_sptBentArrowHandle[] =
+{
+ { SvxMSDffHandleFlags::RANGE,
+ 0x100, 0x101, 10800, 10800, 12427, 21600, 0, 6079 }
+};
+const mso_CustomShape msoBentArrow =
+{
+ const_cast<SvxMSDffVertPair*>(mso_sptBentArrowVert), SAL_N_ELEMENTS( mso_sptBentArrowVert ),
+ const_cast<sal_uInt16*>(mso_sptBentArrowSegm), sizeof( mso_sptBentArrowSegm ) >> 1,
+ const_cast<SvxMSDffCalculationData*>(mso_sptBentArrowCalc), SAL_N_ELEMENTS( mso_sptBentArrowCalc ),
+ const_cast<sal_Int32*>(mso_sptBentArrowDefault),
+ const_cast<SvxMSDffTextRectangles*>(mso_sptBentArrowTextRect), SAL_N_ELEMENTS( mso_sptBentArrowTextRect ),
+ 21600, 21600,
+ MIN_INT32, MIN_INT32,
+ nullptr, 0,
+ const_cast<SvxMSDffHandle*>(mso_sptBentArrowHandle), SAL_N_ELEMENTS( mso_sptBentArrowHandle ) // handles
+};
+
+const SvxMSDffVertPair mso_sptUturnArrowVert[] =
+{
+ { 0, 21600 }, { 0, 8550 }, // pp
+ { 0, 3540 }, { 4370, 0 }, { 9270, 0 }, // ccp
+ { 13890, 0 }, { 18570, 3230 }, { 18600, 8300 }, // ccp
+ { 21600, 8300 }, { 15680, 14260 }, { 9700, 8300 }, { 12500, 8300 }, // pppp
+ { 12320, 6380 }, { 10870, 5850 }, { 9320, 5850 }, // ccp
+ { 7770, 5850 }, { 6040, 6410 }, { 6110, 8520 }, // ccp
+ { 6110, 21600 }
+};
+const sal_uInt16 mso_sptUturnArrowSegm[] =
+{
+ 0x4000, 0x0001, 0x2002, 0x0004, 0x2002, 0x0001, 0x6000, 0x8000
+};
+const SvxMSDffTextRectangles mso_sptUturnArrowTextRect[] =
+{
+ { { 0, 8280 }, { 6110, 21600 } }
+};
+const mso_CustomShape msoUturnArrow =
+{
+ const_cast<SvxMSDffVertPair*>(mso_sptUturnArrowVert), SAL_N_ELEMENTS( mso_sptUturnArrowVert ),
+ const_cast<sal_uInt16*>(mso_sptUturnArrowSegm), sizeof( mso_sptUturnArrowSegm ) >> 1,
+ nullptr, 0,
+ nullptr,
+ const_cast<SvxMSDffTextRectangles*>(mso_sptUturnArrowTextRect), SAL_N_ELEMENTS( mso_sptUturnArrowTextRect ),
+ 21600, 21600,
+ MIN_INT32, MIN_INT32,
+ nullptr, 0,
+ nullptr, 0 // handles
+};
+
+const SvxMSDffVertPair mso_sptLeftUpArrowVert[] = // adjustment1 : x 0 - 21600, adjustment2 : x 0 - 21600
+{ // adjustment3 : y 0 - 21600
+ { 0, 5 MSO_I }, { 2 MSO_I, 0 MSO_I }, { 2 MSO_I, 7 MSO_I }, { 7 MSO_I, 7 MSO_I },
+ { 7 MSO_I, 2 MSO_I }, { 0 MSO_I, 2 MSO_I }, { 5 MSO_I, 0 }, { 21600, 2 MSO_I },
+ { 1 MSO_I, 2 MSO_I }, { 1 MSO_I, 1 MSO_I }, { 2 MSO_I, 1 MSO_I }, { 2 MSO_I, 21600 }
+};
+const sal_uInt16 mso_sptLeftUpArrowSegm[] =
+{
+ 0x4000, 0x000b, 0x6001, 0x8000
+};
+const SvxMSDffCalculationData mso_sptLeftUpArrowCalc[] =
+{
+ { 0x2000, { DFF_Prop_adjustValue, 0, 0 } }, // 0
+ { 0x2000, { DFF_Prop_adjust2Value, 0, 0 } }, // 1
+ { 0x2000, { DFF_Prop_adjust3Value, 0, 0 } }, // 2
+ { 0x8000, { 21600, 0, DFF_Prop_adjustValue } }, // 3
+ { 0x2001, { 0x0403, 1, 2 } }, // 4
+ { 0x6000, { DFF_Prop_adjustValue, 0x0404, 0 } }, // 5
+ { 0x8000, { 21600, 0, DFF_Prop_adjust2Value } }, // 6
+ { 0x6000, { DFF_Prop_adjustValue, 0x0406, 0 } }, // 7
+ { 0x8000, { 21600, 0, 0x406 } }, // 8
+ { 0xa000, { 0x408, 0, 0x406 } } // 9
+};
+const sal_Int32 mso_sptLeftUpArrowDefault[] =
+{
+ 3, 9340, 18500, 6200
+};
+const SvxMSDffTextRectangles mso_sptLeftUpArrowTextRect[] =
+{
+ { { 2 MSO_I, 7 MSO_I }, { 1 MSO_I, 1 MSO_I } },
+ { { 7 MSO_I, 2 MSO_I }, { 1 MSO_I, 1 MSO_I } }
+};
+const SvxMSDffHandle mso_sptLeftUpArrowHandle[] =
+{
+ { SvxMSDffHandleFlags::RANGE | SvxMSDffHandleFlags::RANGE_Y_MAX_IS_SPECIAL | SvxMSDffHandleFlags::RANGE_X_MIN_IS_SPECIAL,
+ 0x101, 0x102, 10800, 10800, 3 + 5, 21600, 0, 0x100 },
+ { SvxMSDffHandleFlags::RANGE | SvxMSDffHandleFlags::RANGE_X_MIN_IS_SPECIAL | SvxMSDffHandleFlags::RANGE_X_MAX_IS_SPECIAL,
+ 0x100, 0, 10800, 10800, 0x102, 3 + 9, MIN_INT32, 0x7fffffff }
+};
+const mso_CustomShape msoLeftUpArrow =
+{
+ const_cast<SvxMSDffVertPair*>(mso_sptLeftUpArrowVert), SAL_N_ELEMENTS( mso_sptLeftUpArrowVert ),
+ const_cast<sal_uInt16*>(mso_sptLeftUpArrowSegm), sizeof( mso_sptLeftUpArrowSegm ) >> 1,
+ const_cast<SvxMSDffCalculationData*>(mso_sptLeftUpArrowCalc), SAL_N_ELEMENTS( mso_sptLeftUpArrowCalc ),
+ const_cast<sal_Int32*>(mso_sptLeftUpArrowDefault),
+ const_cast<SvxMSDffTextRectangles*>(mso_sptLeftUpArrowTextRect), SAL_N_ELEMENTS( mso_sptLeftUpArrowTextRect ),
+ 21600, 21600,
+ MIN_INT32, MIN_INT32,
+ nullptr, 0,
+ const_cast<SvxMSDffHandle*>(mso_sptLeftUpArrowHandle), SAL_N_ELEMENTS( mso_sptLeftUpArrowHandle ) // handles
+};
+
+const SvxMSDffVertPair mso_sptBentUpArrowVert[] = // adjustment1 : x 0 - 21600, adjustment2 : x 0 - 21600
+{ // adjustment3 : y 0 - 21600
+ { 0, 8 MSO_I }, { 7 MSO_I, 8 MSO_I }, { 7 MSO_I, 2 MSO_I }, { 0 MSO_I, 2 MSO_I },
+ { 5 MSO_I, 0 }, { 21600, 2 MSO_I }, { 1 MSO_I, 2 MSO_I }, { 1 MSO_I, 21600 },
+ { 0, 21600 }
+};
+const sal_uInt16 mso_sptBentUpArrowSegm[] =
+{
+ 0x4000, 0x0008, 0x6001, 0x8000
+};
+const SvxMSDffCalculationData mso_sptBentUpArrowCalc[] =
+{
+ { 0x2000, { DFF_Prop_adjustValue, 0, 0 } }, // 0
+ { 0x2000, { DFF_Prop_adjust2Value, 0, 0 } }, // 1
+ { 0x2000, { DFF_Prop_adjust3Value, 0, 0 } }, // 2
+ { 0x8000, { 21600, 0, DFF_Prop_adjustValue } }, // 3
+ { 0x2001, { 0x0403, 1, 2 } }, // 4
+ { 0x6000, { DFF_Prop_adjustValue, 0x0404, 0 } }, // 5
+ { 0x8000, { 21600, 0, DFF_Prop_adjust2Value } }, // 6
+ { 0x6000, { DFF_Prop_adjustValue, 0x0406, 0 } }, // 7
+ { 0x6000, { 0x0407, 0x0406, 0 } }, // 8
+ { 0x8000, { 21600, 0, 0x406 } }, // 9
+ { 0xa000, { 0x409, 0, 0x406 } } // a
+};
+const sal_Int32 mso_sptBentUpArrowDefault[] =
+{
+ 3, 9340, 18500, 7200
+};
+const SvxMSDffTextRectangles mso_sptBentUpArrowTextRect[] =
+{
+ { { 2 MSO_I, 7 MSO_I }, { 1 MSO_I, 1 MSO_I } },
+ { { 7 MSO_I, 2 MSO_I }, { 1 MSO_I, 1 MSO_I } }
+};
+const SvxMSDffHandle mso_sptBentUpArrowHandle[] =
+{
+ { SvxMSDffHandleFlags::RANGE | SvxMSDffHandleFlags::RANGE_Y_MAX_IS_SPECIAL | SvxMSDffHandleFlags::RANGE_X_MIN_IS_SPECIAL,
+ 0x101, 0x102, 10800, 10800, 3 + 5, 21600, 0, 0x100 },
+ { SvxMSDffHandleFlags::RANGE | SvxMSDffHandleFlags::RANGE_X_MIN_IS_SPECIAL | SvxMSDffHandleFlags::RANGE_X_MAX_IS_SPECIAL,
+ 0x100, 0, 10800, 10800, 0x102, 3 + 10, MIN_INT32, 0x7fffffff }
+};
+const mso_CustomShape msoBentUpArrow =
+{
+ const_cast<SvxMSDffVertPair*>(mso_sptBentUpArrowVert), SAL_N_ELEMENTS( mso_sptBentUpArrowVert ),
+ const_cast<sal_uInt16*>(mso_sptBentUpArrowSegm), sizeof( mso_sptBentUpArrowSegm ) >> 1,
+ const_cast<SvxMSDffCalculationData*>(mso_sptBentUpArrowCalc), SAL_N_ELEMENTS( mso_sptBentUpArrowCalc ),
+ const_cast<sal_Int32*>(mso_sptBentUpArrowDefault),
+ const_cast<SvxMSDffTextRectangles*>(mso_sptBentUpArrowTextRect), SAL_N_ELEMENTS( mso_sptBentUpArrowTextRect ),
+ 21600, 21600,
+ MIN_INT32, MIN_INT32,
+ nullptr, 0,
+ const_cast<SvxMSDffHandle*>(mso_sptBentUpArrowHandle), SAL_N_ELEMENTS( mso_sptBentUpArrowHandle )
+};
+
+const SvxMSDffVertPair mso_sptCurvedRightVert[] =
+{
+ { 0, 0 }, { 23 MSO_I, 3 MSO_I }, { 22 MSO_I, 0 }, { 0, 4 MSO_I }, { 0, 15 MSO_I }, { 23 MSO_I, 1 MSO_I }, { 0, 7 MSO_I }, { 2 MSO_I, 13 MSO_I },
+ { 2 MSO_I, 14 MSO_I }, { 22 MSO_I, 8 MSO_I }, { 2 MSO_I, 12 MSO_I },
+ { 0, 0 }, { 23 MSO_I, 3 MSO_I }, { 2 MSO_I, 11 MSO_I }, { 26 MSO_I, 17 MSO_I }, { 0, 15 MSO_I }, { 23 MSO_I, 1 MSO_I }, { 26 MSO_I, 17 MSO_I }, { 22 MSO_I, 15 MSO_I },
+ { 0, 0 }, { 23 MSO_I, 3 MSO_I }, { 22 MSO_I, 0 }, { 0, 4 MSO_I },
+ { 0, 0 }, { 23 MSO_I, 3 MSO_I }, { 0, 4 MSO_I }, { 26 MSO_I, 17 MSO_I },
+ { 0, 15 MSO_I }, { 23 MSO_I, 1 MSO_I }, { 26 MSO_I, 17 MSO_I }, { 22 MSO_I, 15 MSO_I }
+};
+const sal_uInt16 mso_sptCurvedRightSegm[] =
+{
+ 0xa404,
+ 0xa304,
+ 0x0003,
+ 0xa508,
+ 0x6000,
+ 0x8000,
+ 0xa404,
+ 0xa304,
+ 0xa504,
+ 0x6000,
+ 0x8000
+};
+const SvxMSDffCalculationData mso_sptCurvedRightCalc[] =
+{
+ { 0x2000, { DFF_Prop_adjustValue, 0, 0 } },
+ { 0x2000, { DFF_Prop_adjust2Value, 0, 0 } },
+ { 0x2000, { DFF_Prop_adjust3Value, 0, 0 } },
+ { 0xa000, { DFF_Prop_adjustValue, 21600, DFF_Prop_adjust2Value } },
+ { 0x2001, { 0x403, 1, 2 } },
+ { 0x6000, { DFF_Prop_adjust2Value, DFF_Prop_adjust2Value, 21600 } },
+ { 0xe000, { 0x405, DFF_Prop_adjust2Value, DFF_Prop_adjustValue } },
+ { 0x2001, { 0x406, 1, 2 } },
+ { 0x4002, { 21600, DFF_Prop_adjustValue, 0 } },
+ { 0x8000, { 21600, 0, DFF_Prop_adjust3Value } },
+ { 0xa00f, { 0x409, 21600, 0x404 } },
+ { 0x6000, { 0x404, 0x40a, 0 } },
+ { 0x6000, { 0x40b, DFF_Prop_adjust2Value, 21600 } },
+ { 0x6000, { 0x407, 0x40a, 0 } },
+ { 0xa000, { 0x40c, 21600, DFF_Prop_adjustValue } },
+ { 0xa000, { 0x405, 0, DFF_Prop_adjustValue } },
+ { 0x2001, { 0x40f, 1, 2 } },
+ { 0x6002, { 0x404, 0x407, 0 } },
+ { 0x6000, { DFF_Prop_adjustValue, DFF_Prop_adjust2Value, 21600 } },
+ { 0x2001, { 0x412, 1, 2 } },
+ { 0xa000, { 0x411, 0, 0x413 } },
+ { 0x0000, { 21600, 0, 0 } },
+ { 0x0000, { 21600, 0, 0 } },
+ { 0x0001, { 21600, 2, 1 } },
+ { 0xa000, { 0x411, 0, 0x404 } },
+ { 0x600f, { 0x418, 0x404, 21600 } },
+ { 0x8000, { 21600, 0, 0x419 } },
+ { 0x2000, { 0x408, 128, 0 } },
+ { 0x2001, { 0x405, 1, 2 } },
+ { 0x2000, { 0x405, 0, 128 } },
+ { 0xe000, { DFF_Prop_adjustValue, 0x411, 0x40c } },
+ { 0x600f, { 0x414, 0x404, 21600 } },
+ { 0x8000, { 21600, 0, DFF_Prop_adjustValue } },
+ { 0x2001, { 0x420, 1, 2 } },
+ { 0x0001, { 21600, 21600, 1 } },
+ { 0x6001, { 0x409, 0x409, 1 } },
+ { 0xa000, { 0x422, 0, 0x423 } },
+ { 0x200d, { 0x424, 0, 0 } },
+ { 0x2000, { 0x425, 21600, 0 } },
+ { 0x8001, { 21600, 21600, 0x426 } },
+ { 0x2000, { 0x427, 64, 0 } },
+ { 0x2001, { DFF_Prop_adjustValue, 1, 2 } },
+ { 0x600f, { 0x421, 0x429, 21600 } },
+ { 0x8000, { 21600, 0, 0x42a } },
+ { 0x2000, { 0x42b, 64, 0 } },
+ { 0x2001, { 0x404, 1, 2 } },
+ { 0xa000, { DFF_Prop_adjust2Value, 0, 0x42d } },
+ { 0x0001, { 21600, 2195, 16384 } },
+ { 0x0001, { 21600, 14189, 16384 } }
+};
+const sal_Int32 mso_sptCurvedRightDefault[] =
+{
+ 3, 12960, 19440, 14400
+};
+const SvxMSDffTextRectangles mso_sptCurvedRightTextRect[] =
+{
+ { { 47 MSO_I, 45 MSO_I }, { 48 MSO_I, 46 MSO_I } }
+};
+const SvxMSDffVertPair mso_sptCurvedRightGluePoints[] =
+{
+ { 0, 17 MSO_I }, { 2 MSO_I, 14 MSO_I }, { 22 MSO_I, 8 MSO_I }, { 2 MSO_I, 12 MSO_I }, { 22 MSO_I, 16 MSO_I }
+};
+const SvxMSDffHandle mso_sptCurvedRightHandles[] =
+{
+ { SvxMSDffHandleFlags::RANGE | SvxMSDffHandleFlags::RANGE_Y_MIN_IS_SPECIAL | SvxMSDffHandleFlags::RANGE_Y_MAX_IS_SPECIAL,
+ 21600, 0x100, 10800, 10800, 0, 10800, 3 + 40, 3 + 29 },
+ { SvxMSDffHandleFlags::RANGE | SvxMSDffHandleFlags::RANGE_Y_MIN_IS_SPECIAL | SvxMSDffHandleFlags::RANGE_Y_MAX_IS_SPECIAL,
+ 21600, 0x101, 10800, 10800, 0, 10800, 3 + 27, 3 + 21 },
+ { SvxMSDffHandleFlags::RANGE | SvxMSDffHandleFlags::RANGE_X_MIN_IS_SPECIAL | SvxMSDffHandleFlags::RANGE_X_MAX_IS_SPECIAL,
+ 0x102, 21600, 10800, 10800, 3 + 44, 3 + 22, 3375, 21600 }
+};
+const mso_CustomShape msoCurvedRightArrow =
+{
+ const_cast<SvxMSDffVertPair*>(mso_sptCurvedRightVert), SAL_N_ELEMENTS( mso_sptCurvedRightVert ),
+ const_cast<sal_uInt16*>(mso_sptCurvedRightSegm), sizeof( mso_sptCurvedRightSegm ) >> 1,
+ const_cast<SvxMSDffCalculationData*>(mso_sptCurvedRightCalc), SAL_N_ELEMENTS( mso_sptCurvedRightCalc ),
+ const_cast<sal_Int32*>(mso_sptCurvedRightDefault),
+ const_cast<SvxMSDffTextRectangles*>(mso_sptCurvedRightTextRect), SAL_N_ELEMENTS( mso_sptCurvedRightTextRect ),
+ 21600, 21600,
+ MIN_INT32, MIN_INT32,
+ const_cast<SvxMSDffVertPair*>(mso_sptCurvedRightGluePoints), SAL_N_ELEMENTS( mso_sptCurvedRightGluePoints ),
+ const_cast<SvxMSDffHandle*>(mso_sptCurvedRightHandles), SAL_N_ELEMENTS( mso_sptCurvedRightHandles )
+};
+
+const SvxMSDffVertPair mso_sptCurvedDownVert[] =
+{
+ { 0, 0 }, { 3 MSO_I, 23 MSO_I }, { 0, 22 MSO_I }, { 4 MSO_I, 0 },
+ { 15 MSO_I, 0 }, { 1 MSO_I, 23 MSO_I }, { 7 MSO_I, 0 }, { 13 MSO_I, 2 MSO_I },
+ { 14 MSO_I, 2 MSO_I }, { 8 MSO_I, 22 MSO_I }, { 12 MSO_I, 2 MSO_I },
+ { 0, 0 }, { 3 MSO_I, 23 MSO_I }, { 11 MSO_I, 2 MSO_I }, { 17 MSO_I, 26 MSO_I }, { 15 MSO_I, 0 },
+ { 1 MSO_I, 23 MSO_I }, { 17 MSO_I, 26 MSO_I }, { 15 MSO_I, 22 MSO_I },
+ { 0, 0 }, { 3 MSO_I, 23 MSO_I }, { 0, 22 MSO_I }, { 4 MSO_I, 0 },
+ { 0, 0 }, { 3 MSO_I, 23 MSO_I }, { 4 MSO_I, 0 }, { 17 MSO_I, 26 MSO_I },
+ { 15 MSO_I, 0 }, { 1 MSO_I, 23 MSO_I }, { 17 MSO_I, 26 MSO_I }, { 15 MSO_I, 22 MSO_I }
+};
+const sal_uInt16 mso_sptCurvedDownSegm[] =
+{
+ 0xa604,
+ 0xa504,
+ 0x0003,
+ 0xa308,
+ 0x6000,
+ 0x8000,
+ 0xa604,
+ 0xa504,
+ 0xa304,
+ 0x6000,
+ 0x8000
+};
+const SvxMSDffTextRectangles mso_sptCurvedDownTextRect[] =
+{
+ { { 45 MSO_I, 47 MSO_I }, { 46 MSO_I, 48 MSO_I } }
+};
+const SvxMSDffVertPair mso_sptCurvedDownGluePoints[] =
+{
+ { 17 MSO_I, 0 }, { 16 MSO_I, 22 MSO_I }, { 12 MSO_I, 2 MSO_I }, { 8 MSO_I, 22 MSO_I }, { 14 MSO_I, 2 MSO_I }
+};
+const SvxMSDffHandle mso_sptCurvedDownHandles[] =
+{
+ { SvxMSDffHandleFlags::RANGE | SvxMSDffHandleFlags::RANGE_X_MIN_IS_SPECIAL | SvxMSDffHandleFlags::RANGE_X_MAX_IS_SPECIAL,
+ 0x100, 21600, 10800, 10800, 3 + 40, 3 + 29, 0, 10800 },
+ { SvxMSDffHandleFlags::RANGE | SvxMSDffHandleFlags::RANGE_X_MIN_IS_SPECIAL | SvxMSDffHandleFlags::RANGE_X_MAX_IS_SPECIAL,
+ 0x101, 21600, 10800, 10800, 3 + 27, 3 + 21, 0, 10800 },
+ { SvxMSDffHandleFlags::RANGE | SvxMSDffHandleFlags::RANGE_Y_MIN_IS_SPECIAL | SvxMSDffHandleFlags::RANGE_Y_MAX_IS_SPECIAL,
+ 21600, 0x102, 10800, 10800, 3375, 21600, 3 + 44, 3 + 22 }
+};
+
+const mso_CustomShape msoCurvedDownArrow =
+{
+ const_cast<SvxMSDffVertPair*>(mso_sptCurvedDownVert), SAL_N_ELEMENTS( mso_sptCurvedDownVert ),
+ const_cast<sal_uInt16*>(mso_sptCurvedDownSegm), sizeof( mso_sptCurvedDownSegm ) >> 1,
+ const_cast<SvxMSDffCalculationData*>(mso_sptCurvedRightCalc), SAL_N_ELEMENTS( mso_sptCurvedRightCalc ),
+ const_cast<sal_Int32*>(mso_sptCurvedRightDefault),
+ const_cast<SvxMSDffTextRectangles*>(mso_sptCurvedDownTextRect), SAL_N_ELEMENTS( mso_sptCurvedDownTextRect ),
+ 21600, 21600,
+ MIN_INT32, MIN_INT32,
+ const_cast<SvxMSDffVertPair*>(mso_sptCurvedDownGluePoints), SAL_N_ELEMENTS( mso_sptCurvedDownGluePoints ),
+ const_cast<SvxMSDffHandle*>(mso_sptCurvedDownHandles), SAL_N_ELEMENTS( mso_sptCurvedDownHandles )
+};
+
+const SvxMSDffVertPair mso_sptCurvedUpVert[] =
+{
+ { 0, 22 MSO_I }, { 3 MSO_I, 21 MSO_I }, { 0, 0 }, { 4 MSO_I, 21 MSO_I },
+ { 14 MSO_I, 22 MSO_I }, { 1 MSO_I, 21 MSO_I }, { 7 MSO_I, 21 MSO_I },
+ { 12 MSO_I, 2 MSO_I }, { 13 MSO_I, 2 MSO_I }, { 8 MSO_I, 0 }, { 11 MSO_I, 2 MSO_I },
+ { 0, 22 MSO_I }, { 3 MSO_I, 21 MSO_I }, { 10 MSO_I, 2 MSO_I }, { 16 MSO_I, 24 MSO_I },
+ { 14 MSO_I, 22 MSO_I }, { 1 MSO_I, 21 MSO_I }, { 16 MSO_I, 24 MSO_I }, { 14 MSO_I, 0 },
+ { 0, 22 MSO_I }, { 3 MSO_I, 21 MSO_I }, { 0, 0 }, { 4 MSO_I, 21 MSO_I },
+ { 14 MSO_I, 22 MSO_I }, { 1 MSO_I, 21 MSO_I }, { 7 MSO_I, 21 MSO_I }, { 16 MSO_I, 24 MSO_I },
+ { 14 MSO_I, 22 MSO_I }, { 1 MSO_I, 21 MSO_I }, { 16 MSO_I, 24 MSO_I }, { 14 MSO_I, 0 }
+};
+const sal_uInt16 mso_sptCurvedUpSegm[] =
+{
+ 0xa404,
+ 0xa304,
+ 0x0003,
+ 0xa508,
+ 0x6000,
+ 0x8000,
+ 0xa404,
+ 0xa508,
+ 0xa504,
+ 0x6000,
+ 0x8000
+};
+const SvxMSDffCalculationData mso_sptCurvedUpCalc[] =
+{
+ { 0x2000, { DFF_Prop_adjustValue, 0, 0 } },
+ { 0x2000, { DFF_Prop_adjust2Value, 0, 0 } },
+ { 0x2000, { DFF_Prop_adjust3Value, 0, 0 } },
+ { 0xa000, { DFF_Prop_adjustValue, 21600, DFF_Prop_adjust2Value } },
+ { 0x2001, { 0x403, 1, 2 } },
+ { 0x6000, { DFF_Prop_adjust2Value, DFF_Prop_adjust2Value, 21600 } },
+ { 0xe000, { 0x405, DFF_Prop_adjust2Value, DFF_Prop_adjustValue } },
+ { 0x2001, { 0x406, 1, 2 } },
+ { 0x4002, { 21600, DFF_Prop_adjustValue, 0 } },
+ { 0xa00f, { DFF_Prop_adjust3Value, 21600, 0x404 } },
+ { 0x6000, { 0x404, 0x409, 0 } },
+ { 0x6000, { 0x40a, DFF_Prop_adjust2Value, 21600 } },
+ { 0x6000, { 0x407, 0x409, 0 } },
+ { 0xa000, { 0x40b, 21600, DFF_Prop_adjustValue } },
+ { 0xa000, { 0x405, 0, DFF_Prop_adjustValue } },
+ { 0x2001, { 0x40e, 1, 2 } },
+ { 0x6002, { 0x404, 0x407, 0 } },
+ { 0x6000, { DFF_Prop_adjustValue, DFF_Prop_adjust2Value, 21600 } },
+ { 0x2001, { 0x411, 1, 2 } },
+ { 0xa000, { 0x410, 0, 0x412 } },
+ { 0x0000, { 21600, 0, 0 } },
+ { 0x0000, { 21600, 0, 0 } },
+ { 0x0000, { 0, 0, 21600 } },
+ { 0xa000, { 0x410, 0, 0x404 } },
+ { 0x600f, { 0x417, 0x404, 21600 } },
+ { 0x2000, { 0x408, 128, 0 } },
+ { 0x2001, { 0x405, 1, 2 } },
+ { 0x2000, { 0x405, 0, 128 } },
+ { 0xe000, { DFF_Prop_adjustValue, 0x410, 0x40b } },
+ { 0x8000, { 21600, 0, DFF_Prop_adjustValue } },
+ { 0x2001, { 0x41d, 1, 2 } },
+ { 0x0001, { 21600, 21600, 1 } },
+ { 0x6001, { DFF_Prop_adjust3Value, DFF_Prop_adjust3Value, 1 } },
+ { 0xa000, { 0x41f, 0, 0x420 } },
+ { 0x200d, { 0x421, 0, 0 } },
+ { 0x2000, { 0x422, 21600, 0 } },
+ { 0x8001, { 21600, 21600, 0x423 } },
+ { 0x2000, { 0x424, 64, 0 } },
+ { 0x2001, { DFF_Prop_adjustValue, 1, 2 } },
+ { 0x600f, { 0x41e, 0x426, 21600 } },
+ { 0x2000, { 0x427, 0, 64 } },
+ { 0x2001, { 0x404, 1, 2 } },
+ { 0xa000, { DFF_Prop_adjust2Value, 0, 0x429 } },
+ { 0x0001, { 21600, 2195, 16384 } },
+ { 0x0001, { 21600, 14189, 16384 } }
+};
+const sal_Int32 mso_sptCurvedUpDefault[] =
+{
+ 3, 12960, 19440, 7200
+};
+const SvxMSDffTextRectangles mso_sptCurvedUpTextRect[] =
+{
+ { { 41 MSO_I, 43 MSO_I }, { 42 MSO_I, 44 MSO_I } }
+};
+const SvxMSDffVertPair mso_sptCurvedUpGluePoints[] =
+{
+ { 8 MSO_I, 0 }, { 11 MSO_I, 2 MSO_I }, { 15 MSO_I, 0 }, { 16 MSO_I, 21 MSO_I }, { 13 MSO_I, 2 MSO_I }
+};
+const SvxMSDffHandle mso_sptCurvedUpHandles[] =
+{
+ { SvxMSDffHandleFlags::RANGE | SvxMSDffHandleFlags::RANGE_X_MIN_IS_SPECIAL | SvxMSDffHandleFlags::RANGE_X_MAX_IS_SPECIAL,
+ 0x100, 0, 10800, 10800, 3 + 37, 3 + 27, 0, 10800 },
+ { SvxMSDffHandleFlags::RANGE | SvxMSDffHandleFlags::RANGE_X_MIN_IS_SPECIAL | SvxMSDffHandleFlags::RANGE_X_MAX_IS_SPECIAL,
+ 0x101, 0, 10800, 10800, 3 + 25, 3 + 20, 0, 10800 },
+ { SvxMSDffHandleFlags::RANGE | SvxMSDffHandleFlags::RANGE_Y_MAX_IS_SPECIAL,
+ 21600, 0x102, 10800, 10800, 3375, 21600, 0, 3 + 40 }
+};
+const mso_CustomShape msoCurvedUpArrow =
+{
+ const_cast<SvxMSDffVertPair*>(mso_sptCurvedUpVert), SAL_N_ELEMENTS( mso_sptCurvedUpVert ),
+ const_cast<sal_uInt16*>(mso_sptCurvedUpSegm), sizeof( mso_sptCurvedUpSegm ) >> 1,
+ const_cast<SvxMSDffCalculationData*>(mso_sptCurvedUpCalc), SAL_N_ELEMENTS( mso_sptCurvedUpCalc ),
+ const_cast<sal_Int32*>(mso_sptCurvedUpDefault),
+ const_cast<SvxMSDffTextRectangles*>(mso_sptCurvedUpTextRect), SAL_N_ELEMENTS( mso_sptCurvedUpTextRect ),
+ 21600, 21600,
+ MIN_INT32, MIN_INT32,
+ const_cast<SvxMSDffVertPair*>(mso_sptCurvedUpGluePoints), SAL_N_ELEMENTS( mso_sptCurvedUpGluePoints ),
+ const_cast<SvxMSDffHandle*>(mso_sptCurvedUpHandles), SAL_N_ELEMENTS( mso_sptCurvedUpHandles )
+};
+
+const SvxMSDffVertPair mso_sptCurvedLeftVert[] =
+{
+ { 22 MSO_I, 0 }, { 21 MSO_I, 3 MSO_I }, { 0, 0 }, { 21 MSO_I, 4 MSO_I },
+ { 22 MSO_I, 14 MSO_I }, { 21 MSO_I, 1 MSO_I }, { 21 MSO_I, 7 MSO_I }, { 2 MSO_I, 12 MSO_I },
+ { 2 MSO_I, 13 MSO_I }, { 0, 8 MSO_I }, { 2 MSO_I, 11 MSO_I },
+ { 22 MSO_I, 0 }, { 21 MSO_I, 3 MSO_I }, { 2 MSO_I, 10 MSO_I }, { 24 MSO_I, 16 MSO_I },
+ { 22 MSO_I, 14 MSO_I }, { 21 MSO_I, 1 MSO_I }, { 24 MSO_I, 16 MSO_I }, { 0, 14 MSO_I },
+ { 22 MSO_I, 0 }, { 21 MSO_I, 3 MSO_I }, { 0, 0 }, { 21 MSO_I, 4 MSO_I },
+ { 22 MSO_I, 14 MSO_I }, { 21 MSO_I, 1 MSO_I }, { 21 MSO_I, 7 MSO_I }, { 24 MSO_I, 16 MSO_I },
+ { 22 MSO_I, 14 MSO_I }, { 21 MSO_I, 1 MSO_I }, { 24 MSO_I, 16 MSO_I }, { 0, 14 MSO_I }
+};
+const sal_uInt16 mso_sptCurvedLeftSegm[] =
+{
+ 0xa604,
+ 0xa504,
+ 0x0003,
+ 0xa308,
+ 0x6000,
+ 0x8000,
+ 0xa604,
+ 0xa308,
+ 0x6000,
+ 0x8000
+};
+const SvxMSDffTextRectangles mso_sptCurvedLeftTextRect[] =
+{
+ { { 43 MSO_I, 41 MSO_I }, { 44 MSO_I, 42 MSO_I } }
+};
+const SvxMSDffVertPair mso_sptCurvedLeftGluePoints[] =
+{
+ { 0, 15 MSO_I }, { 2 MSO_I, 11 MSO_I }, { 0, 8 MSO_I }, { 2 MSO_I, 13 MSO_I }, { 21 MSO_I, 16 MSO_I }
+};
+const SvxMSDffHandle mso_sptCurvedLeftHandles[] =
+{
+ { SvxMSDffHandleFlags::RANGE | SvxMSDffHandleFlags::RANGE_Y_MIN_IS_SPECIAL | SvxMSDffHandleFlags::RANGE_Y_MAX_IS_SPECIAL,
+ 0, 0x100, 10800, 10800, 0, 10800, 3 + 37, 3 + 27 },
+ { SvxMSDffHandleFlags::RANGE | SvxMSDffHandleFlags::RANGE_Y_MIN_IS_SPECIAL | SvxMSDffHandleFlags::RANGE_Y_MAX_IS_SPECIAL,
+ 0, 0x101, 10800, 10800, 0, 10800, 3 + 25, 3 + 20 },
+ { SvxMSDffHandleFlags::RANGE | SvxMSDffHandleFlags::RANGE_X_MAX_IS_SPECIAL,
+ 0x102, 21600, 10800, 10800, 0, 3 + 40, 3375, 21600 }
+};
+const mso_CustomShape msoCurvedLeftArrow =
+{
+ const_cast<SvxMSDffVertPair*>(mso_sptCurvedLeftVert), SAL_N_ELEMENTS( mso_sptCurvedLeftVert ),
+ const_cast<sal_uInt16*>(mso_sptCurvedLeftSegm), sizeof( mso_sptCurvedLeftSegm ) >> 1,
+ const_cast<SvxMSDffCalculationData*>(mso_sptCurvedUpCalc), SAL_N_ELEMENTS( mso_sptCurvedUpCalc ),
+ const_cast<sal_Int32*>(mso_sptCurvedUpDefault),
+ const_cast<SvxMSDffTextRectangles*>(mso_sptCurvedLeftTextRect), SAL_N_ELEMENTS( mso_sptCurvedLeftTextRect ),
+ 21600, 21600,
+ MIN_INT32, MIN_INT32,
+ const_cast<SvxMSDffVertPair*>(mso_sptCurvedLeftGluePoints), SAL_N_ELEMENTS( mso_sptCurvedLeftGluePoints ),
+ const_cast<SvxMSDffHandle*>(mso_sptCurvedLeftHandles), SAL_N_ELEMENTS( mso_sptCurvedLeftHandles )
+};
+
+const SvxMSDffVertPair mso_sptStripedRightArrowVert[] = // adjustment1 : x 3375 - 21600
+{ // adjustment2 : y 0 - 10800
+ { 3375, 0 MSO_I }, { 1 MSO_I, 0 MSO_I }, { 1 MSO_I, 0 }, { 21600, 10800 },
+ { 1 MSO_I, 21600 }, { 1 MSO_I, 2 MSO_I }, { 3375, 2 MSO_I }, { 0, 0 MSO_I },
+ { 675, 0 MSO_I }, { 675, 2 MSO_I }, { 0, 2 MSO_I }, { 1350, 0 MSO_I },
+ { 2700, 0 MSO_I }, { 2700, 2 MSO_I }, { 1350, 2 MSO_I }
+};
+const sal_uInt16 mso_sptStripedRightArrowSegm[] =
+{
+ 0x4000, 0x0006, 0x6001, 0x8000,
+ 0x4000, 0x0003, 0x6001, 0x8000,
+ 0x4000, 0x0003, 0x6001, 0x8000
+};
+const SvxMSDffCalculationData mso_sptStripedRightArrowCalc[] =
+{
+ { 0x2000, { DFF_Prop_adjust2Value, 0, 0 } },
+ { 0x2000, { DFF_Prop_adjustValue, 0, 0 } },
+ { 0x8000, { 21600, 0, DFF_Prop_adjust2Value } },
+ { 0x8000, { 21600, 0, 0x401 } },
+ { 0x6001, { 0x403, 0x400, 10800 } },
+ { 0x6000, { 0x401, 0x404, 0 } }
+};
+const SvxMSDffTextRectangles mso_sptStripedRightArrowTextRect[] =
+{
+ { { 3375, 0 MSO_I }, { 5 MSO_I, 2 MSO_I } }
+};
+const SvxMSDffHandle mso_sptStripedRightArrowHandle[] =
+{
+ { SvxMSDffHandleFlags::RANGE,
+ 0x100, 0x101, 10800, 10800, 3375, 21600, 0, 10800 }
+};
+const mso_CustomShape msoStripedRightArrow =
+{
+ const_cast<SvxMSDffVertPair*>(mso_sptStripedRightArrowVert), SAL_N_ELEMENTS( mso_sptStripedRightArrowVert ),
+ const_cast<sal_uInt16*>(mso_sptStripedRightArrowSegm), sizeof( mso_sptStripedRightArrowSegm ) >> 1,
+ const_cast<SvxMSDffCalculationData*>(mso_sptStripedRightArrowCalc), SAL_N_ELEMENTS( mso_sptStripedRightArrowCalc ),
+ const_cast<sal_Int32*>(mso_sptDefault16200and5400),
+ const_cast<SvxMSDffTextRectangles*>(mso_sptStripedRightArrowTextRect), SAL_N_ELEMENTS( mso_sptStripedRightArrowTextRect ),
+ 21600, 21600,
+ MIN_INT32, MIN_INT32,
+ nullptr, 0,
+ const_cast<SvxMSDffHandle*>(mso_sptStripedRightArrowHandle), SAL_N_ELEMENTS( mso_sptStripedRightArrowHandle )
+};
+
+const SvxMSDffVertPair mso_sptNotchedRightArrowVert[] = // adjustment1 : x 0 - 21600 (default 16200)
+{ // adjustment2 : y 0 - 10800 (default 5400)
+ { 0, 1 MSO_I }, { 0 MSO_I, 1 MSO_I }, { 0 MSO_I, 0 }, { 21600, 10800 },
+ { 0 MSO_I, 21600 }, { 0 MSO_I, 2 MSO_I }, { 0, 2 MSO_I }, { 5 MSO_I, 10800 }, { 0, 1 MSO_I }
+};
+const SvxMSDffCalculationData mso_sptNotchedRightArrowCalc[] =
+{
+ { 0x2000, { DFF_Prop_adjustValue, 0, 0 } },
+ { 0x2000, { DFF_Prop_adjust2Value, 0, 0 } },
+ { 0x8000, { 21600, 0, DFF_Prop_adjust2Value } },
+ { 0x8000, { 21600, 0, DFF_Prop_adjustValue } },
+ { 0x8000, { 10800, 0, DFF_Prop_adjust2Value } },
+ { 0x6001, { 0x403, 0x404, 10800 } },
+ { 0x8000, { 21600, 0, 0x405 }}
+};
+const SvxMSDffTextRectangles mso_sptNotchedRightArrowTextRect[] = // todo
+{
+ { { 5 MSO_I, 1 MSO_I }, { 6 MSO_I, 2 MSO_I } }
+};
+const SvxMSDffHandle mso_sptNotchedRightArrowHandle[] =
+{
+ { SvxMSDffHandleFlags::RANGE,
+ 0x100, 0x101, 10800, 10800, 0, 21600, 0, 10800 }
+};
+const mso_CustomShape msoNotchedRightArrow =
+{
+ const_cast<SvxMSDffVertPair*>(mso_sptNotchedRightArrowVert), SAL_N_ELEMENTS( mso_sptNotchedRightArrowVert ),
+ nullptr, 0,
+ const_cast<SvxMSDffCalculationData*>(mso_sptNotchedRightArrowCalc), SAL_N_ELEMENTS( mso_sptNotchedRightArrowCalc ),
+ const_cast<sal_Int32*>(mso_sptDefault16200and5400),
+ const_cast<SvxMSDffTextRectangles*>(mso_sptNotchedRightArrowTextRect), SAL_N_ELEMENTS( mso_sptNotchedRightArrowTextRect ),
+ 21600, 21600,
+ MIN_INT32, MIN_INT32,
+ nullptr, 0,
+ const_cast<SvxMSDffHandle*>(mso_sptNotchedRightArrowHandle), SAL_N_ELEMENTS( mso_sptNotchedRightArrowHandle )
+};
+
+const SvxMSDffVertPair mso_sptHomePlateVert[] = // adjustment1 : x 0 - 21600
+{
+ { 0, 0 }, { 0 MSO_I, 0 }, { 21600, 10800 }, { 0 MSO_I, 21600 },
+ { 0, 21600 }
+};
+const sal_uInt16 mso_sptHomePlateSegm[] =
+{
+ 0x4000, 0x0004, 0x6001, 0x8000
+};
+const SvxMSDffCalculationData mso_sptHomePlateCalc[] =
+{
+ { 0x2000, { DFF_Prop_adjustValue, 0, 0 } }
+};
+const sal_Int32 mso_sptHomePlateDefault[] =
+{
+ 1, 16200
+};
+const SvxMSDffTextRectangles mso_sptHomePlateTextRect[] = // todo
+{
+ { { 0, 0 }, { 21600, 21600 } }
+};
+const SvxMSDffHandle mso_sptHomePlateHandle[] =
+{
+ { SvxMSDffHandleFlags::RANGE,
+ 0x100, 0, 10800, 10800, 0, 21600, MIN_INT32, 0x7fffffff }
+};
+const mso_CustomShape msoHomePlate =
+{
+ const_cast<SvxMSDffVertPair*>(mso_sptHomePlateVert), SAL_N_ELEMENTS( mso_sptHomePlateVert ),
+ const_cast<sal_uInt16*>(mso_sptHomePlateSegm), sizeof( mso_sptHomePlateSegm ) >> 1,
+ const_cast<SvxMSDffCalculationData*>(mso_sptHomePlateCalc), SAL_N_ELEMENTS( mso_sptHomePlateCalc ),
+ const_cast<sal_Int32*>(mso_sptHomePlateDefault),
+ const_cast<SvxMSDffTextRectangles*>(mso_sptHomePlateTextRect), SAL_N_ELEMENTS( mso_sptHomePlateTextRect ),
+ 21600, 21600,
+ MIN_INT32, MIN_INT32,
+ nullptr, 0,
+ const_cast<SvxMSDffHandle*>(mso_sptHomePlateHandle), SAL_N_ELEMENTS( mso_sptHomePlateHandle )
+};
+
+const SvxMSDffVertPair mso_sptChevronVert[] = // adjustment1 : x 0 - 21600
+{
+ { 0, 0 }, { 0 MSO_I, 0 }, { 21600, 10800 }, { 0 MSO_I, 21600 },
+ { 0, 21600 }, { 1 MSO_I, 10800 }
+};
+const sal_uInt16 mso_sptChevronSegm[] =
+{
+ 0x4000, 0x0005, 0x6001, 0x8000
+};
+const SvxMSDffCalculationData mso_sptChevronCalc[] =
+{
+ { 0x2000, { DFF_Prop_adjustValue, 0, 0 } },
+ { 0x8000, { 21600, 0, 0x0400 } }
+};
+const sal_Int32 mso_sptChevronDefault[] =
+{
+ 1, 16200
+};
+const SvxMSDffTextRectangles mso_sptChevronTextRect[] = // todo
+{
+ { { 0, 0 }, { 21600, 21600 } }
+};
+const SvxMSDffHandle mso_sptChevronHandle[] =
+{
+ { SvxMSDffHandleFlags::RANGE,
+ 0x100, 0, 10800, 10800, 0, 21600, MIN_INT32, 0x7fffffff }
+};
+const mso_CustomShape msoChevron =
+{
+ const_cast<SvxMSDffVertPair*>(mso_sptChevronVert), SAL_N_ELEMENTS( mso_sptChevronVert ),
+ const_cast<sal_uInt16*>(mso_sptChevronSegm), sizeof( mso_sptChevronSegm ) >> 1,
+ const_cast<SvxMSDffCalculationData*>(mso_sptChevronCalc), SAL_N_ELEMENTS( mso_sptChevronCalc ),
+ const_cast<sal_Int32*>(mso_sptChevronDefault),
+ const_cast<SvxMSDffTextRectangles*>(mso_sptChevronTextRect), SAL_N_ELEMENTS( mso_sptChevronTextRect ),
+ 21600, 21600,
+ MIN_INT32, MIN_INT32,
+ nullptr, 0,
+ const_cast<SvxMSDffHandle*>(mso_sptChevronHandle), SAL_N_ELEMENTS( mso_sptChevronHandle )
+};
+
+const SvxMSDffVertPair mso_sptRightArrowCalloutVert[] = // adjustment1 : x 0 - 21000
+{ // adjustment2 : y 0 - 10800
+ { 0, 0 }, { 0 MSO_I, 0 }, { 0 MSO_I, 3 MSO_I }, { 2 MSO_I, 3 MSO_I },
+ { 2 MSO_I, 1 MSO_I }, { 21600, 10800 }, { 2 MSO_I, 4 MSO_I }, { 2 MSO_I, 5 MSO_I },
+ { 0 MSO_I, 5 MSO_I }, { 0 MSO_I, 21600 }, { 0, 21600 }
+};
+const sal_uInt16 mso_sptRightArrowCalloutSegm[] =
+{
+ 0x4000, 0x000a, 0x6001, 0x8000
+};
+const SvxMSDffCalculationData mso_sptRightArrowCalloutCalc[] =
+{
+ { 0x2000, { DFF_Prop_adjustValue, 0, 0 } },
+ { 0x2000, { DFF_Prop_adjust2Value, 0, 0 } },
+ { 0x2000, { DFF_Prop_adjust3Value, 0, 0 } },
+ { 0x2000, { DFF_Prop_adjust4Value, 0, 0 } },
+ { 0x8000, { 21600, 0, 0x0401 } },
+ { 0x8000, { 21600, 0, 0x0403 } }
+};
+const sal_Int32 mso_sptRightArrowCalloutDefault[] =
+{
+ 4, 14400, 5400, 18000, 8100
+};
+const SvxMSDffTextRectangles mso_sptRightArrowCalloutTextRect[] =
+{
+ { { 0, 0 }, { 0 MSO_I, 21600 } }
+};
+const SvxMSDffHandle mso_sptRightArrowCalloutHandle[] =
+{
+ { SvxMSDffHandleFlags::RANGE | SvxMSDffHandleFlags::RANGE_X_MAX_IS_SPECIAL,
+ 0x100, 0, 10800, 10800, 0, 0x102, MIN_INT32, 0x7fffffff },
+ { SvxMSDffHandleFlags::RANGE | SvxMSDffHandleFlags::RANGE_X_MIN_IS_SPECIAL | SvxMSDffHandleFlags::RANGE_Y_MIN_IS_SPECIAL,
+ 0x102, 0x103, 10800, 10800, 0x100, 21600, 0x101, 10800 },
+ { SvxMSDffHandleFlags::RANGE | SvxMSDffHandleFlags::RANGE_Y_MAX_IS_SPECIAL,
+ 1, 0x101, 10800, 10800, MIN_INT32, 0x7fffffff, 0, 0x103 }
+};
+const mso_CustomShape msoRightArrowCallout =
+{
+ const_cast<SvxMSDffVertPair*>(mso_sptRightArrowCalloutVert), SAL_N_ELEMENTS( mso_sptRightArrowCalloutVert ),
+ const_cast<sal_uInt16*>(mso_sptRightArrowCalloutSegm), sizeof( mso_sptRightArrowCalloutSegm ) >> 1,
+ const_cast<SvxMSDffCalculationData*>(mso_sptRightArrowCalloutCalc), SAL_N_ELEMENTS( mso_sptRightArrowCalloutCalc ),
+ const_cast<sal_Int32*>(mso_sptRightArrowCalloutDefault),
+ const_cast<SvxMSDffTextRectangles*>(mso_sptRightArrowCalloutTextRect), SAL_N_ELEMENTS( mso_sptRightArrowCalloutTextRect ),
+ 21600, 21600,
+ MIN_INT32, MIN_INT32,
+ nullptr, 0,
+ const_cast<SvxMSDffHandle*>(mso_sptRightArrowCalloutHandle), SAL_N_ELEMENTS( mso_sptRightArrowCalloutHandle )
+};
+
+const SvxMSDffVertPair mso_sptLeftArrowCalloutVert[] = // adjustment1 : x 0 - 21600, adjustment2 : y 0 - 10800
+{ // adjustment3 : x 0 - 21600, adjustment4 : y 0 - 10800
+ { 0 MSO_I, 0 }, { 21600, 0 }, { 21600, 21600 }, { 0 MSO_I, 21600 },
+ { 0 MSO_I, 5 MSO_I }, { 2 MSO_I, 5 MSO_I }, { 2 MSO_I, 4 MSO_I }, { 0, 10800 },
+ { 2 MSO_I, 1 MSO_I }, { 2 MSO_I, 3 MSO_I }, { 0 MSO_I, 3 MSO_I }
+};
+const sal_uInt16 mso_sptLeftArrowCalloutSegm[] =
+{
+ 0x4000, 0x000a, 0x6001, 0x8000
+};
+const SvxMSDffCalculationData mso_sptLeftArrowCalloutCalc[] =
+{
+ { 0x2000, { DFF_Prop_adjustValue, 0, 0 } },
+ { 0x2000, { DFF_Prop_adjust2Value, 0, 0 } },
+ { 0x2000, { DFF_Prop_adjust3Value, 0, 0 } },
+ { 0x2000, { DFF_Prop_adjust4Value, 0, 0 } },
+ { 0x8000, { 21600, 0, 0x0401 } },
+ { 0x8000, { 21600, 0, 0x0403 } }
+};
+const sal_Int32 mso_sptLeftArrowCalloutDefault[] =
+{
+ 4, 7200, 5400, 3600, 8100
+};
+const SvxMSDffTextRectangles mso_sptLeftArrowCalloutTextRect[] =
+{
+ { { 0 MSO_I, 0 }, { 21600, 21600 } }
+};
+const SvxMSDffHandle mso_sptLeftArrowCalloutHandle[] =
+{
+ { SvxMSDffHandleFlags::RANGE | SvxMSDffHandleFlags::RANGE_X_MIN_IS_SPECIAL,
+ 0x100, 0, 10800, 10800, 0x102, 21600, MIN_INT32, 0x7fffffff },
+ { SvxMSDffHandleFlags::RANGE | SvxMSDffHandleFlags::RANGE_X_MAX_IS_SPECIAL | SvxMSDffHandleFlags::RANGE_Y_MIN_IS_SPECIAL,
+ 0x102, 0x103, 10800, 10800, 0, 0x100, 0x101, 10800 },
+ { SvxMSDffHandleFlags::RANGE | SvxMSDffHandleFlags::RANGE_Y_MAX_IS_SPECIAL,
+ 0, 0x101, 10800, 10800, MIN_INT32, 0x7fffffff, 0, 0x103 }
+};
+const mso_CustomShape msoLeftArrowCallout =
+{
+ const_cast<SvxMSDffVertPair*>(mso_sptLeftArrowCalloutVert), SAL_N_ELEMENTS( mso_sptLeftArrowCalloutVert ),
+ const_cast<sal_uInt16*>(mso_sptLeftArrowCalloutSegm), sizeof( mso_sptLeftArrowCalloutSegm ) >> 1,
+ const_cast<SvxMSDffCalculationData*>(mso_sptLeftArrowCalloutCalc), SAL_N_ELEMENTS( mso_sptLeftArrowCalloutCalc ),
+ const_cast<sal_Int32*>(mso_sptLeftArrowCalloutDefault),
+ const_cast<SvxMSDffTextRectangles*>(mso_sptLeftArrowCalloutTextRect), SAL_N_ELEMENTS( mso_sptLeftArrowCalloutTextRect ),
+ 21600, 21600,
+ MIN_INT32, MIN_INT32,
+ nullptr, 0,
+ const_cast<SvxMSDffHandle*>(mso_sptLeftArrowCalloutHandle), SAL_N_ELEMENTS( mso_sptLeftArrowCalloutHandle )
+};
+
+const SvxMSDffVertPair mso_sptUpArrowCalloutVert[] =
+{
+ { 21600, 0 MSO_I }, { 21600, 21600 }, { 0, 21600 }, { 0, 0 MSO_I },
+ { 3 MSO_I, 0 MSO_I }, { 3 MSO_I, 2 MSO_I }, { 1 MSO_I, 2 MSO_I }, { 10800, 0 },
+ { 4 MSO_I, 2 MSO_I }, { 5 MSO_I, 2 MSO_I }, { 5 MSO_I, 0 MSO_I }
+};
+const sal_uInt16 mso_sptUpArrowCalloutSegm[] =
+{
+ 0x4000, 0x000a, 0x6001, 0x8000
+};
+const SvxMSDffCalculationData mso_sptUpArrowCalloutCalc[] =
+{
+ { 0x2000, { DFF_Prop_adjustValue, 0, 0 } },
+ { 0x2000, { DFF_Prop_adjust2Value, 0, 0 } },
+ { 0x2000, { DFF_Prop_adjust3Value, 0, 0 } },
+ { 0x2000, { DFF_Prop_adjust4Value, 0, 0 } },
+ { 0x8000, { 21600, 0, 0x0401 } },
+ { 0x8000, { 21600, 0, 0x0403 } }
+};
+const sal_Int32 mso_sptUpArrowCalloutDefault[] =
+{
+ 4, 7200, 5400, 3600, 8100
+};
+const SvxMSDffTextRectangles mso_sptUpArrowCalloutTextRect[] =
+{
+ { { 0, 0 MSO_I }, { 21600, 21600 } }
+};
+const SvxMSDffHandle mso_sptUpArrowCalloutHandle[] =
+{
+ { SvxMSDffHandleFlags::RANGE | SvxMSDffHandleFlags::RANGE_Y_MIN_IS_SPECIAL,
+ 0, 0x100, 10800, 10800, MIN_INT32, 0x7fffffff, 0x102, 21600 },
+ { SvxMSDffHandleFlags::RANGE | SvxMSDffHandleFlags::RANGE_X_MIN_IS_SPECIAL | SvxMSDffHandleFlags::RANGE_Y_MAX_IS_SPECIAL,
+ 0x103, 0x102, 10800, 10800, 0x101, 10800, 0, 0x100 },
+ { SvxMSDffHandleFlags::RANGE | SvxMSDffHandleFlags::RANGE_X_MAX_IS_SPECIAL,
+ 0x101, 0, 10800, 10800, 0, 0x103, MIN_INT32, 0x7fffffff }
+};
+const mso_CustomShape msoUpArrowCallout =
+{
+ const_cast<SvxMSDffVertPair*>(mso_sptUpArrowCalloutVert), SAL_N_ELEMENTS( mso_sptUpArrowCalloutVert ),
+ const_cast<sal_uInt16*>(mso_sptUpArrowCalloutSegm), sizeof( mso_sptUpArrowCalloutSegm ) >> 1,
+ const_cast<SvxMSDffCalculationData*>(mso_sptUpArrowCalloutCalc), SAL_N_ELEMENTS( mso_sptUpArrowCalloutCalc ),
+ const_cast<sal_Int32*>(mso_sptUpArrowCalloutDefault),
+ const_cast<SvxMSDffTextRectangles*>(mso_sptUpArrowCalloutTextRect), SAL_N_ELEMENTS( mso_sptUpArrowCalloutTextRect ),
+ 21600, 21600,
+ MIN_INT32, MIN_INT32,
+ nullptr, 0,
+ const_cast<SvxMSDffHandle*>(mso_sptUpArrowCalloutHandle), SAL_N_ELEMENTS( mso_sptUpArrowCalloutHandle )
+};
+
+const SvxMSDffVertPair mso_sptDownArrowCalloutVert[] =
+{
+ { 0, 0 MSO_I }, { 0, 0 }, { 21600, 0 }, { 21600, 0 MSO_I },
+ { 5 MSO_I, 0 MSO_I }, { 5 MSO_I, 2 MSO_I }, { 4 MSO_I, 2 MSO_I }, { 10800, 21600 },
+ { 1 MSO_I, 2 MSO_I }, { 3 MSO_I, 2 MSO_I }, { 3 MSO_I, 0 MSO_I }
+};
+const sal_uInt16 mso_sptDownArrowCalloutSegm[] =
+{
+ 0x4000, 0x000a, 0x6001, 0x8000
+};
+const SvxMSDffCalculationData mso_sptDownArrowCalloutCalc[] =
+{
+ { 0x2000, { DFF_Prop_adjustValue, 0, 0 } },
+ { 0x2000, { DFF_Prop_adjust2Value, 0, 0 } },
+ { 0x2000, { DFF_Prop_adjust3Value, 0, 0 } },
+ { 0x2000, { DFF_Prop_adjust4Value, 0, 0 } },
+ { 0x8000, { 21600, 0, 0x0401 } },
+ { 0x8000, { 21600, 0, 0x0403 } }
+};
+const sal_Int32 mso_sptDownArrowCalloutDefault[] =
+{
+ 4, 14400, 5400, 18000, 8100
+};
+const SvxMSDffTextRectangles mso_sptDownArrowCalloutTextRect[] =
+{
+ { { 0, 0 }, { 21600, 0 MSO_I } }
+};
+const SvxMSDffHandle mso_sptDownArrowCalloutHandle[] =
+{
+ { SvxMSDffHandleFlags::RANGE | SvxMSDffHandleFlags::RANGE_Y_MAX_IS_SPECIAL,
+ 0, 0x100, 10800, 10800, MIN_INT32, 0x7fffffff, 0, 0x102 },
+ { SvxMSDffHandleFlags::RANGE | SvxMSDffHandleFlags::RANGE_X_MIN_IS_SPECIAL | SvxMSDffHandleFlags::RANGE_Y_MIN_IS_SPECIAL,
+ 0x103, 0x102, 10800, 10800, 0x101, 10800, 0x100, 21600 },
+ { SvxMSDffHandleFlags::RANGE | SvxMSDffHandleFlags::RANGE_X_MAX_IS_SPECIAL,
+ 0x101, 1, 10800, 10800, 0, 0x103, MIN_INT32, 0x7fffffff }
+};
+const mso_CustomShape msoDownArrowCallout =
+{
+ const_cast<SvxMSDffVertPair*>(mso_sptDownArrowCalloutVert), SAL_N_ELEMENTS( mso_sptDownArrowCalloutVert ),
+ const_cast<sal_uInt16*>(mso_sptDownArrowCalloutSegm), sizeof( mso_sptDownArrowCalloutSegm ) >> 1,
+ const_cast<SvxMSDffCalculationData*>(mso_sptDownArrowCalloutCalc), SAL_N_ELEMENTS( mso_sptDownArrowCalloutCalc ),
+ const_cast<sal_Int32*>(mso_sptDownArrowCalloutDefault),
+ const_cast<SvxMSDffTextRectangles*>(mso_sptDownArrowCalloutTextRect), SAL_N_ELEMENTS( mso_sptDownArrowCalloutTextRect ),
+ 21600, 21600,
+ MIN_INT32, MIN_INT32,
+ nullptr, 0,
+ const_cast<SvxMSDffHandle*>(mso_sptDownArrowCalloutHandle), SAL_N_ELEMENTS( mso_sptDownArrowCalloutHandle )
+};
+
+const SvxMSDffVertPair mso_sptLeftRightArrowCalloutVert[] =
+{
+ { 0 MSO_I, 0 }, { 4 MSO_I, 0 }, { 4 MSO_I, 3 MSO_I }, { 6 MSO_I, 3 MSO_I },
+ { 6 MSO_I, 1 MSO_I }, { 21600, 10800 }, { 6 MSO_I, 5 MSO_I }, { 6 MSO_I, 7 MSO_I },
+ { 4 MSO_I, 7 MSO_I }, { 4 MSO_I, 21600 }, { 0 MSO_I, 21600 }, { 0 MSO_I, 7 MSO_I },
+ { 2 MSO_I, 7 MSO_I }, { 2 MSO_I, 5 MSO_I }, { 0, 10800 }, { 2 MSO_I, 1 MSO_I },
+ { 2 MSO_I, 3 MSO_I }, { 0 MSO_I, 3 MSO_I }
+};
+const sal_uInt16 mso_sptLeftRightArrowCalloutSegm[] =
+{
+ 0x4000, 0x0011, 0x6001, 0x8000
+};
+const SvxMSDffCalculationData mso_sptLeftRightArrowCalloutCalc[] =
+{
+ { 0x2000, { DFF_Prop_adjustValue, 0, 0 } },
+ { 0x2000, { DFF_Prop_adjust2Value, 0, 0 } },
+ { 0x2000, { DFF_Prop_adjust3Value, 0, 0 } },
+ { 0x2000, { DFF_Prop_adjust4Value, 0, 0 } },
+ { 0x8000, { 21600, 0, 0x0400 } },
+ { 0x8000, { 21600, 0, 0x0401 } },
+ { 0x8000, { 21600, 0, 0x0402 } },
+ { 0x8000, { 21600, 0, 0x0403 } }
+};
+const sal_Int32 mso_sptLeftRightArrowCalloutDefault[] =
+{
+ 4, 5400, 5500, 2700, 8100
+};
+const SvxMSDffTextRectangles mso_sptLeftRightArrowCalloutTextRect[] =
+{
+ { { 0 MSO_I, 0 }, { 4 MSO_I, 21600 } }
+};
+const SvxMSDffHandle mso_sptLeftRightArrowCalloutHandle[] =
+{
+ { SvxMSDffHandleFlags::RANGE | SvxMSDffHandleFlags::RANGE_X_MIN_IS_SPECIAL,
+ 0x100, 0, 10800, 10800, 0x102, 10800, MIN_INT32, 0x7fffffff },
+ { SvxMSDffHandleFlags::RANGE | SvxMSDffHandleFlags::RANGE_X_MAX_IS_SPECIAL | SvxMSDffHandleFlags::RANGE_Y_MIN_IS_SPECIAL,
+ 0x102, 0x103, 10800, 10800, 0, 0x100, 0x101, 10800 },
+ { SvxMSDffHandleFlags::RANGE | SvxMSDffHandleFlags::RANGE_Y_MAX_IS_SPECIAL,
+ 0, 0x101, 10800, 10800, MIN_INT32, 0x7fffffff, 0, 0x103 }
+};
+const mso_CustomShape msoLeftRightArrowCallout =
+{
+ const_cast<SvxMSDffVertPair*>(mso_sptLeftRightArrowCalloutVert), SAL_N_ELEMENTS( mso_sptLeftRightArrowCalloutVert ),
+ const_cast<sal_uInt16*>(mso_sptLeftRightArrowCalloutSegm), sizeof( mso_sptLeftRightArrowCalloutSegm ) >> 1,
+ const_cast<SvxMSDffCalculationData*>(mso_sptLeftRightArrowCalloutCalc), SAL_N_ELEMENTS( mso_sptLeftRightArrowCalloutCalc ),
+ const_cast<sal_Int32*>(mso_sptLeftRightArrowCalloutDefault),
+ const_cast<SvxMSDffTextRectangles*>(mso_sptLeftRightArrowCalloutTextRect), SAL_N_ELEMENTS( mso_sptLeftRightArrowCalloutTextRect ),
+ 21600, 21600,
+ MIN_INT32, MIN_INT32,
+ nullptr, 0,
+ const_cast<SvxMSDffHandle*>(mso_sptLeftRightArrowCalloutHandle), SAL_N_ELEMENTS( mso_sptLeftRightArrowCalloutHandle )
+};
+
+const SvxMSDffVertPair mso_sptUpDownArrowCalloutVert[] =
+{
+ { 0, 0 MSO_I }, { 0, 4 MSO_I }, { 3 MSO_I, 4 MSO_I }, { 3 MSO_I, 6 MSO_I },
+ { 1 MSO_I, 6 MSO_I }, { 10800, 21600 }, { 5 MSO_I, 6 MSO_I }, { 7 MSO_I, 6 MSO_I },
+ { 7 MSO_I, 4 MSO_I }, { 21600, 4 MSO_I }, { 21600, 0 MSO_I }, { 7 MSO_I, 0 MSO_I },
+ { 7 MSO_I, 2 MSO_I }, { 5 MSO_I, 2 MSO_I }, { 10800, 0 }, { 1 MSO_I, 2 MSO_I },
+ { 3 MSO_I, 2 MSO_I }, { 3 MSO_I, 0 MSO_I }
+};
+const sal_uInt16 mso_sptUpDownArrowCalloutSegm[] =
+{
+ 0x4000, 0x0011, 0x6001, 0x8000
+};
+const SvxMSDffCalculationData mso_sptUpDownArrowCalloutCalc[] =
+{
+ { 0x2000, { DFF_Prop_adjustValue, 0, 0 } },
+ { 0x2000, { DFF_Prop_adjust2Value, 0, 0 } },
+ { 0x2000, { DFF_Prop_adjust3Value, 0, 0 } },
+ { 0x2000, { DFF_Prop_adjust4Value, 0, 0 } },
+ { 0x8000, { 21600, 0, 0x0400 } },
+ { 0x8000, { 21600, 0, 0x0401 } },
+ { 0x8000, { 21600, 0, 0x0402 } },
+ { 0x8000, { 21600, 0, 0x0403 } }
+};
+const sal_Int32 mso_sptUpDownArrowCalloutDefault[] =
+{
+ 4, 5400, 5500, 2700, 8100
+};
+const SvxMSDffTextRectangles mso_sptUpDownArrowCalloutTextRect[] =
+{
+ { { 0, 0 MSO_I }, { 21600, 4 MSO_I } }
+};
+const SvxMSDffHandle mso_sptUpDownArrowCalloutHandle[] =
+{
+ { SvxMSDffHandleFlags::RANGE | SvxMSDffHandleFlags::RANGE_Y_MIN_IS_SPECIAL,
+ 0, 0x100, 10800, 10800, MIN_INT32, 0x7fffffff, 0x102, 10800 },
+ { SvxMSDffHandleFlags::RANGE | SvxMSDffHandleFlags::RANGE_X_MIN_IS_SPECIAL | SvxMSDffHandleFlags::RANGE_Y_MAX_IS_SPECIAL,
+ 0x103, 0x102, 10800, 10800, 0x101, 10800, 0, 0x100 },
+ { SvxMSDffHandleFlags::RANGE | SvxMSDffHandleFlags::RANGE_X_MAX_IS_SPECIAL,
+ 0x101, 0, 10800, 10800, 0, 0x103, MIN_INT32, 0x7fffffff }
+};
+const mso_CustomShape msoUpDownArrowCallout =
+{
+ const_cast<SvxMSDffVertPair*>(mso_sptUpDownArrowCalloutVert), SAL_N_ELEMENTS( mso_sptUpDownArrowCalloutVert ),
+ const_cast<sal_uInt16*>(mso_sptUpDownArrowCalloutSegm), sizeof( mso_sptUpDownArrowCalloutSegm ) >> 1,
+ const_cast<SvxMSDffCalculationData*>(mso_sptUpDownArrowCalloutCalc), SAL_N_ELEMENTS( mso_sptUpDownArrowCalloutCalc ),
+ const_cast<sal_Int32*>(mso_sptUpDownArrowCalloutDefault),
+ const_cast<SvxMSDffTextRectangles*>(mso_sptUpDownArrowCalloutTextRect), SAL_N_ELEMENTS( mso_sptUpDownArrowCalloutTextRect ),
+ 21600, 21600,
+ MIN_INT32, MIN_INT32,
+ nullptr, 0,
+ const_cast<SvxMSDffHandle*>(mso_sptUpDownArrowCalloutHandle), SAL_N_ELEMENTS( mso_sptUpDownArrowCalloutHandle )
+};
+
+const SvxMSDffVertPair mso_sptQuadArrowCalloutVert[] =
+{
+ { 0 MSO_I, 0 MSO_I }, { 3 MSO_I, 0 MSO_I }, { 3 MSO_I, 2 MSO_I }, { 1 MSO_I, 2 MSO_I },
+ { 10800, 0 }, { 5 MSO_I, 2 MSO_I }, { 7 MSO_I, 2 MSO_I }, { 7 MSO_I, 0 MSO_I },
+ { 4 MSO_I, 0 MSO_I }, { 4 MSO_I, 3 MSO_I }, { 6 MSO_I, 3 MSO_I }, { 6 MSO_I, 1 MSO_I },
+ { 21600, 10800 }, { 6 MSO_I, 5 MSO_I }, { 6 MSO_I, 7 MSO_I }, { 4 MSO_I, 7 MSO_I },
+ { 4 MSO_I, 4 MSO_I }, { 7 MSO_I, 4 MSO_I }, { 7 MSO_I, 6 MSO_I }, { 5 MSO_I, 6 MSO_I },
+ { 10800, 21600 }, { 1 MSO_I, 6 MSO_I }, { 3 MSO_I, 6 MSO_I }, { 3 MSO_I, 4 MSO_I },
+ { 0 MSO_I, 4 MSO_I }, { 0 MSO_I, 7 MSO_I }, { 2 MSO_I, 7 MSO_I }, { 2 MSO_I, 5 MSO_I },
+ { 0, 10800 }, { 2 MSO_I, 1 MSO_I }, { 2 MSO_I, 3 MSO_I }, { 0 MSO_I, 3 MSO_I }
+};
+const sal_uInt16 mso_sptQuadArrowCalloutSegm[] =
+{
+ 0x4000, 0x001f, 0x6001, 0x8000
+};
+const SvxMSDffCalculationData mso_sptQuadArrowCalloutCalc[] =
+{
+ { 0x2000, { DFF_Prop_adjustValue, 0, 0 } },
+ { 0x2000, { DFF_Prop_adjust2Value, 0, 0 } },
+ { 0x2000, { DFF_Prop_adjust3Value, 0, 0 } },
+ { 0x2000, { DFF_Prop_adjust4Value, 0, 0 } },
+ { 0x8000, { 21600, 0, 0x0400 } },
+ { 0x8000, { 21600, 0, 0x0401 } },
+ { 0x8000, { 21600, 0, 0x0402 } },
+ { 0x8000, { 21600, 0, 0x0403 } }
+};
+const sal_Int32 mso_sptQuadArrowCalloutDefault[] =
+{
+ 4, 5400, 8100, 2700, 9400
+};
+const SvxMSDffTextRectangles mso_sptQuadArrowCalloutTextRect[] =
+{
+ { { 0 MSO_I, 0 MSO_I }, { 4 MSO_I, 4 MSO_I } }
+};
+const SvxMSDffHandle mso_sptQuadArrowCalloutHandle[] =
+{
+ { SvxMSDffHandleFlags::RANGE | SvxMSDffHandleFlags::RANGE_Y_MIN_IS_SPECIAL | SvxMSDffHandleFlags::RANGE_Y_MAX_IS_SPECIAL,
+ 0, 0x100, 10800, 10800, MIN_INT32, 0x7fffffff, 0x102, 0x101 },
+ { SvxMSDffHandleFlags::RANGE | SvxMSDffHandleFlags::RANGE_X_MIN_IS_SPECIAL,
+ 0x101, 0, 10800, 10800, 0x100, 10800, MIN_INT32, 0x7fffffff },
+ { SvxMSDffHandleFlags::RANGE | SvxMSDffHandleFlags::RANGE_X_MIN_IS_SPECIAL | SvxMSDffHandleFlags::RANGE_Y_MAX_IS_SPECIAL,
+ 0x103, 0x102, 10800, 10800, 0x101, 10800, 0, 0x100 }
+};
+const mso_CustomShape msoQuadArrowCallout =
+{
+ const_cast<SvxMSDffVertPair*>(mso_sptQuadArrowCalloutVert), SAL_N_ELEMENTS( mso_sptQuadArrowCalloutVert ),
+ const_cast<sal_uInt16*>(mso_sptQuadArrowCalloutSegm), sizeof( mso_sptQuadArrowCalloutSegm ) >> 1,
+ const_cast<SvxMSDffCalculationData*>(mso_sptQuadArrowCalloutCalc), SAL_N_ELEMENTS( mso_sptQuadArrowCalloutCalc ),
+ const_cast<sal_Int32*>(mso_sptQuadArrowCalloutDefault),
+ const_cast<SvxMSDffTextRectangles*>(mso_sptQuadArrowCalloutTextRect), SAL_N_ELEMENTS( mso_sptQuadArrowCalloutTextRect ),
+ 21600, 21600,
+ MIN_INT32, MIN_INT32,
+ nullptr, 0,
+ const_cast<SvxMSDffHandle*>(mso_sptQuadArrowCalloutHandle), SAL_N_ELEMENTS( mso_sptQuadArrowCalloutHandle )
+};
+
+const SvxMSDffVertPair mso_sptCircularArrowVert[] =
+{
+ { 0x03 MSO_I, 0x03 MSO_I },
+ { 0x14 MSO_I, 0x14 MSO_I },
+ { 0x13 MSO_I, 0x12 MSO_I },
+ { 0x11 MSO_I, 0x10 MSO_I },
+ { 0, 0 }, { 21600, 21600 },
+ { 0x09 MSO_I, 0x08 MSO_I },
+ { 0x0B MSO_I, 0x0A MSO_I },
+ { 0x18 MSO_I, 0x17 MSO_I },
+ { 0x2F MSO_I, 0x2E MSO_I },
+ { 0x1D MSO_I, 0x1C MSO_I }
+};
+const sal_uInt16 mso_sptCircularArrowSegm[] =
+{
+ 0xa404, 0xa504, 0x0003, 0x6001, 0x8000
+};
+const SvxMSDffCalculationData mso_sptCircularArrowCalc[] =
+{
+ { 0x2000, { 0x0147, 0x0000, 0x0000 } },
+ { 0x2000, { 0x0148, 0x0000, 0x0000 } },
+ { 0x2000, { 0x0149, 0x0000, 0x0000 } },
+ { 0x4000, { 0x2A30, 0x0149, 0x0000 } },
+ { 0x4009, { 0x2A30, 0x0147, 0x0000 } },
+ { 0x400A, { 0x2A30, 0x0147, 0x0000 } },
+ { 0x4009, { 0x2A30, 0x0148, 0x0000 } },
+ { 0x400A, { 0x2A30, 0x0148, 0x0000 } },
+ { 0x2000, { 0x0404, 0x2A30, 0x0000 } },
+ { 0x2000, { 0x0405, 0x2A30, 0x0000 } },
+ { 0x2000, { 0x0406, 0x2A30, 0x0000 } },
+ { 0x2000, { 0x0407, 0x2A30, 0x0000 } },
+ { 0x6009, { 0x0403, 0x0147, 0x0000 } },
+ { 0x600A, { 0x0403, 0x0147, 0x0000 } },
+ { 0x6009, { 0x0403, 0x0148, 0x0000 } },
+ { 0x600A, { 0x0403, 0x0148, 0x0000 } },
+ { 0x2000, { 0x040C, 0x2A30, 0x0000 } },
+ { 0x2000, { 0x040D, 0x2A30, 0x0000 } },
+ { 0x2000, { 0x040E, 0x2A30, 0x0000 } },
+ { 0x2000, { 0x040F, 0x2A30, 0x0000 } },
+ { 0x8000, { 0x5460, 0x0000, 0x0403 } },
+ { 0x4009, { 0x34BC, 0x0148, 0x0000 } },
+ { 0x400A, { 0x34BC, 0x0148, 0x0000 } },
+ { 0x2000, { 0x0415, 0x2A30, 0x0000 } },
+ { 0x2000, { 0x0416, 0x2A30, 0x0000 } },
+ { 0x2000, { 0x0149, 0x0000, 0x0A8C } },
+ { 0x6009, { 0x0419, 0x0148, 0x0000 } },
+ { 0x600A, { 0x0419, 0x0148, 0x0000 } },
+ { 0x2000, { 0x041A, 0x2A30, 0x0000 } },
+ { 0x2000, { 0x041B, 0x2A30, 0x0000 } },
+ { 0xA000, { 0x041D, 0x0000, 0x0418 } },
+ { 0xA000, { 0x041D, 0x0000, 0x0418 } },
+ { 0x6001, { 0x041E, 0x041F, 0x0001 } },
+ { 0xA000, { 0x041C, 0x0000, 0x0417 } },
+ { 0xA000, { 0x041C, 0x0000, 0x0417 } },
+ { 0x6001, { 0x0421, 0x0422, 0x0001 } },
+ { 0x6000, { 0x0420, 0x0423, 0x0000 } },
+ { 0x200D, { 0x0424, 0x0000, 0x0000 } },
+ { 0x200E, { 0x0148, 0x002D, 0x0000 } },
+ { 0x6009, { 0x0425, 0x0426, 0x0000 } },
+ { 0x200E, { 0x0148, 0x002D, 0x0000 } },
+ { 0x600A, { 0x0425, 0x0428, 0x0000 } },
+ { 0x000E, { 0x0000, 0x002D, 0x0000 } },
+ { 0x6009, { 0x0427, 0x042A, 0x0000 } },
+ { 0x000E, { 0x0000, 0x002D, 0x0000 } },
+ { 0x6009, { 0x0429, 0x042C, 0x0000 } },
+ { 0x6000, { 0x041C, 0x042B, 0x0000 } },
+ { 0x6000, { 0x041D, 0x042D, 0x0000 } }
+};
+const sal_Int32 mso_sptCircularArrowDefault[] =
+{
+ 3, 180, 0, 5500
+};
+const SvxMSDffTextRectangles mso_sptCircularArrowTextRect[] = // todo
+{
+ { { 0, 0 }, { 21600, 21600 } }
+};
+const SvxMSDffHandle mso_sptCircularArrowHandle[] =
+{
+ { SvxMSDffHandleFlags::POLAR | SvxMSDffHandleFlags::RADIUS_RANGE,
+ 10800, 0x100, 10800, 10800, 10800, 10800, -0x7f4c0000, 0x00b40000 },
+ { SvxMSDffHandleFlags::POLAR | SvxMSDffHandleFlags::RADIUS_RANGE,
+ 0x102, 0x101, 10800, 10800, 0, 10800, -0x7f4c0000, 0x00b40000 }
+};
+const mso_CustomShape msoCircularArrow =
+{
+ const_cast<SvxMSDffVertPair*>(mso_sptCircularArrowVert), SAL_N_ELEMENTS( mso_sptCircularArrowVert ),
+ const_cast<sal_uInt16*>(mso_sptCircularArrowSegm), sizeof( mso_sptCircularArrowSegm ) >> 1,
+ const_cast<SvxMSDffCalculationData*>(mso_sptCircularArrowCalc), SAL_N_ELEMENTS( mso_sptCircularArrowCalc ),
+ const_cast<sal_Int32*>(mso_sptCircularArrowDefault),
+ const_cast<SvxMSDffTextRectangles*>(mso_sptCircularArrowTextRect), SAL_N_ELEMENTS( mso_sptCircularArrowTextRect ),
+ 21600, 21600,
+ MIN_INT32, MIN_INT32,
+ nullptr, 0,
+ const_cast<SvxMSDffHandle*>(mso_sptCircularArrowHandle), SAL_N_ELEMENTS( mso_sptCircularArrowHandle ) // handles
+};
+
+const SvxMSDffVertPair mso_sptCubeVert[] =
+{
+ { 0, 12 MSO_I }, { 0, 1 MSO_I }, { 2 MSO_I, 0 }, { 11 MSO_I, 0 }, { 11 MSO_I, 3 MSO_I }, { 4 MSO_I, 12 MSO_I },
+ { 0, 1 MSO_I }, { 2 MSO_I, 0 }, { 11 MSO_I, 0 }, { 4 MSO_I, 1 MSO_I },
+ { 4 MSO_I, 12 MSO_I }, { 4 MSO_I, 1 MSO_I }, { 11 MSO_I, 0 }, { 11 MSO_I, 3 MSO_I }
+};
+const sal_uInt16 mso_sptCubeSegm[] =
+{
+ 0x4000, 0x0005, 0x6001, 0x8000,
+ 0x4000, 0x0003, 0x6001, 0x8000,
+ 0x4000, 0x0003, 0x6001, 0x8000
+};
+const SvxMSDffCalculationData mso_sptCubeCalc[] =
+{
+ { 0x2000, { DFF_Prop_adjustValue, 0, 0 } },
+ { 0x6000, { DFF_Prop_geoTop, 0x400, 0 } },
+ { 0x6000, { DFF_Prop_geoLeft, 0x400, 0 } },
+ { 0xa000, { DFF_Prop_geoBottom, 0, 0x400 } },
+ { 0xa000, { DFF_Prop_geoRight, 0, 0x400 } },
+ { 0xa000, { DFF_Prop_geoRight, 0, 0x402 } }, // 5
+ { 0x2001, { 0x405, 1, 2 } }, // 6
+ { 0x6000, { 0x402, 0x406, 0 } }, // 7
+ { 0xa000, { DFF_Prop_geoBottom, 0, 0x401 } }, // 8
+ { 0x2001, { 0x408, 1, 2 } }, // 9
+ { 0x6000, { 0x401, 0x409, 0 } }, // 10
+ { 0x2000, { DFF_Prop_geoRight, 0, 0 } }, // 11
+ { 0x2000, { DFF_Prop_geoBottom, 0, 0 } } // 12
+};
+const SvxMSDffTextRectangles mso_sptCubeTextRect[] =
+{
+ { { 0, 1 MSO_I }, { 4 MSO_I, 12 MSO_I } }
+};
+const SvxMSDffHandle mso_sptCubeHandle[] =
+{
+ { SvxMSDffHandleFlags::RANGE | SvxMSDffHandleFlags::SWITCHED,
+ 0, 0x100, 10800, 10800, MIN_INT32, 0x7fffffff, 0, 21600 }
+};
+const SvxMSDffVertPair mso_sptCubeGluePoints[] =
+{
+ { 7 MSO_I, 0 }, { 6 MSO_I, 1 MSO_I }, { 0, 10 MSO_I }, { 6 MSO_I, 21600 }, { 4 MSO_I, 10 MSO_I }, { 21600, 9 MSO_I }
+};
+const mso_CustomShape msoCube =
+{
+ const_cast<SvxMSDffVertPair*>(mso_sptCubeVert), SAL_N_ELEMENTS( mso_sptCubeVert ),
+ const_cast<sal_uInt16*>(mso_sptCubeSegm), sizeof( mso_sptCubeSegm ) >> 1,
+ const_cast<SvxMSDffCalculationData*>(mso_sptCubeCalc), SAL_N_ELEMENTS( mso_sptCubeCalc ),
+ const_cast<sal_Int32*>(mso_sptDefault5400),
+ const_cast<SvxMSDffTextRectangles*>(mso_sptCubeTextRect), SAL_N_ELEMENTS( mso_sptCubeTextRect ),
+ 21600, 21600,
+ 10800, 10800,
+ const_cast<SvxMSDffVertPair*>(mso_sptCubeGluePoints), SAL_N_ELEMENTS( mso_sptCubeGluePoints ),
+ const_cast<SvxMSDffHandle*>(mso_sptCubeHandle), SAL_N_ELEMENTS( mso_sptCubeHandle )
+};
+
+const SvxMSDffVertPair mso_sptBevelVert[] =
+{
+ { 0, 0 }, { 21600, 0 }, { 21600, 21600 }, { 0, 21600 },
+ { 0, 0 }, { 21600, 0 }, { 1 MSO_I, 0 MSO_I }, { 0 MSO_I, 0 MSO_I },
+ { 21600, 0 }, { 21600, 21600 }, { 1 MSO_I, 2 MSO_I }, { 1 MSO_I, 0 MSO_I },
+ { 21600, 21600 }, { 0, 21600 }, { 0 MSO_I, 2 MSO_I }, { 1 MSO_I, 2 MSO_I },
+ { 0, 21600 }, { 0, 0 }, { 0 MSO_I, 0 MSO_I }, { 0 MSO_I, 2 MSO_I }
+};
+const sal_uInt16 mso_sptBevelSegm[] =
+{
+ 0x4000, 0x0003, 0x6001, 0x8000,
+ 0x4000, 0x0003, 0x6001, 0x8000,
+ 0x4000, 0x0003, 0x6001, 0x8000,
+ 0x4000, 0x0003, 0x6001, 0x8000,
+ 0x4000, 0x0003, 0x6001, 0x8000
+};
+const SvxMSDffCalculationData mso_sptBevelCalc[] =
+{
+ { 0x2001, { DFF_Prop_adjustValue, 21599, 21600 } },
+ { 0xa000, { DFF_Prop_geoRight, 0, 0x400 } },
+ { 0xa000, { DFF_Prop_geoBottom, 0, 0x400 } }
+};
+
+const SvxMSDffTextRectangles mso_sptBevelTextRect[] =
+{
+ { { 0 MSO_I, 0 MSO_I }, { 1 MSO_I, 2 MSO_I } }
+};
+const SvxMSDffHandle mso_sptBevelHandle[] =
+{
+ { SvxMSDffHandleFlags::RANGE | SvxMSDffHandleFlags::SWITCHED,
+ 0x100, 0, 10800, 10800, 0, 10800, MIN_INT32, 0x7fffffff }
+};
+const mso_CustomShape msoBevel =
+{
+ const_cast<SvxMSDffVertPair*>(mso_sptBevelVert), SAL_N_ELEMENTS( mso_sptBevelVert ),
+ const_cast<sal_uInt16*>(mso_sptBevelSegm), sizeof( mso_sptBevelSegm ) >> 1,
+ const_cast<SvxMSDffCalculationData*>(mso_sptBevelCalc), SAL_N_ELEMENTS( mso_sptBevelCalc ),
+ const_cast<sal_Int32*>(mso_sptDefault2700),
+ const_cast<SvxMSDffTextRectangles*>(mso_sptBevelTextRect), SAL_N_ELEMENTS( mso_sptBevelTextRect ),
+ 21600, 21600,
+ 10800, 10800,
+ nullptr, 0,
+ const_cast<SvxMSDffHandle*>(mso_sptBevelHandle), SAL_N_ELEMENTS( mso_sptBevelHandle )
+};
+
+const SvxMSDffVertPair mso_sptFoldedCornerVert[] = // adjustment1 : x 10800 - 21600
+{
+ { 0, 0 }, { 21600, 0 }, { 21600, 0 MSO_I }, { 0 MSO_I, 21600 },
+ { 0, 21600 }, { 0 MSO_I, 21600 }, { 3 MSO_I, 0 MSO_I }, { 8 MSO_I, 9 MSO_I },
+ { 10 MSO_I, 11 MSO_I }, { 21600, 0 MSO_I }
+};
+const sal_uInt16 mso_sptFoldedCornerSegm[] =
+{
+ 0x4000, 0x0004, 0x6001, 0x8000,
+ 0x4000, 0x0001, 0x2001, 0x6001, 0x8000
+};
+const SvxMSDffCalculationData mso_sptFoldedCornerCalc[] =
+{
+ { 0x2000, { DFF_Prop_adjustValue, 0, 0 } },
+ { 0x8000, { 21600, 0, 0x400 } },
+ { 0x2001, { 0x0401, 8000, 10800 } },
+ { 0x8000, { 21600, 0, 0x0402 } },
+ { 0x2001, { 0x0401, 1, 2 } },
+ { 0x2001, { 0x0401, 1, 4 } },
+ { 0x2001, { 0x0401, 1, 7 } },
+ { 0x2001, { 0x0401, 1, 16 } },
+ { 0x6000, { 0x0403, 0x405, 0 } },
+ { 0x6000, { 0x0400, 0x406, 0 } },
+ { 0x8000, { 21600, 0, 0x404 } },
+ { 0x6000, { 0x400, 0x407, 0 } }
+};
+const sal_Int32 mso_sptFoldedCornerDefault[] =
+{
+ 1, 18900
+};
+const SvxMSDffTextRectangles mso_sptFoldedCornerTextRect[] =
+{
+ { { 0, 0 }, { 21600, 11 MSO_I } }
+};
+const SvxMSDffHandle mso_sptFoldedCornerHandle[] =
+{
+ { SvxMSDffHandleFlags::RANGE,
+ 0x100, 1, 10800, 10800, 10800, 21600, MIN_INT32, 0x7fffffff }
+};
+const mso_CustomShape msoFoldedCorner =
+{
+ const_cast<SvxMSDffVertPair*>(mso_sptFoldedCornerVert), SAL_N_ELEMENTS( mso_sptFoldedCornerVert ),
+ const_cast<sal_uInt16*>(mso_sptFoldedCornerSegm), sizeof( mso_sptFoldedCornerSegm ) >> 1,
+ const_cast<SvxMSDffCalculationData*>(mso_sptFoldedCornerCalc), SAL_N_ELEMENTS( mso_sptFoldedCornerCalc ),
+ const_cast<sal_Int32*>(mso_sptFoldedCornerDefault),
+ const_cast<SvxMSDffTextRectangles*>(mso_sptFoldedCornerTextRect), SAL_N_ELEMENTS( mso_sptFoldedCornerTextRect ),
+ 21600, 21600,
+ MIN_INT32, MIN_INT32,
+ const_cast<SvxMSDffVertPair*>(mso_sptStandardGluePoints), SAL_N_ELEMENTS( mso_sptStandardGluePoints ),
+ const_cast<SvxMSDffHandle*>(mso_sptFoldedCornerHandle), SAL_N_ELEMENTS( mso_sptFoldedCornerHandle )
+};
+
+const SvxMSDffVertPair mso_sptActionButtonBlankVert[] =
+{
+ { 0, 0 }, { 21600, 0 }, { 21600, 21600 }, { 0, 21600 },
+ { 0, 0 }, { 21600, 0 }, { 1 MSO_I, 0 MSO_I }, { 0 MSO_I, 0 MSO_I },
+ { 21600, 0 }, { 21600, 21600 }, { 1 MSO_I, 2 MSO_I }, { 1 MSO_I, 0 MSO_I },
+ { 21600, 21600 }, { 0, 21600 }, { 0 MSO_I, 2 MSO_I }, { 1 MSO_I, 2 MSO_I },
+ { 0, 21600 }, { 0, 0 }, { 0 MSO_I, 0 MSO_I }, { 0 MSO_I, 2 MSO_I }
+};
+const sal_uInt16 mso_sptActionButtonBlankSegm[] =
+{
+ 0x4000, 0x0003, 0x6001, 0x8000,
+ 0x4000, 0x0003, 0x6001, 0x8000,
+ 0x4000, 0x0003, 0x6001, 0x8000,
+ 0x4000, 0x0003, 0x6001, 0x8000,
+ 0x4000, 0x0003, 0x6001, 0x8000
+};
+const SvxMSDffCalculationData mso_sptActionButtonBlankCalc[] =
+{
+ { 0x2001, { DFF_Prop_adjustValue, 21599, 21600 } },
+ { 0xa000, { DFF_Prop_geoRight, 0, 0x400 } },
+ { 0xa000, { DFF_Prop_geoBottom, 0, 0x400 } }
+};
+const SvxMSDffTextRectangles mso_sptActionButtonBlankTextRect[] =
+{
+ { { 0 MSO_I, 0 MSO_I }, { 1 MSO_I, 2 MSO_I } }
+};
+const SvxMSDffHandle mso_sptButtonHandle[] =
+{
+ { SvxMSDffHandleFlags::RANGE | SvxMSDffHandleFlags::SWITCHED,
+ 0x100, 0, 10800, 10800, 0, 5400, MIN_INT32, 0x7fffffff }
+};
+const mso_CustomShape msoActionButtonBlank =
+{
+ const_cast<SvxMSDffVertPair*>(mso_sptActionButtonBlankVert), SAL_N_ELEMENTS( mso_sptActionButtonBlankVert ),
+ const_cast<sal_uInt16*>(mso_sptActionButtonBlankSegm), sizeof( mso_sptActionButtonBlankSegm ) >> 1,
+ const_cast<SvxMSDffCalculationData*>(mso_sptActionButtonBlankCalc), SAL_N_ELEMENTS( mso_sptActionButtonBlankCalc ),
+ const_cast<sal_Int32*>(mso_sptDefault1400),
+ const_cast<SvxMSDffTextRectangles*>(mso_sptActionButtonBlankTextRect), SAL_N_ELEMENTS( mso_sptActionButtonBlankTextRect ),
+ 21600, 21600,
+ 10800, 10800,
+ nullptr, 0,
+ const_cast<SvxMSDffHandle*>(mso_sptButtonHandle), SAL_N_ELEMENTS( mso_sptButtonHandle )
+};
+
+const SvxMSDffTextRectangles mso_sptActionButtonTextRect[] =
+{
+ { { 1 MSO_I, 2 MSO_I }, { 3 MSO_I, 4 MSO_I } }
+};
+const SvxMSDffVertPair mso_sptActionButtonHomeVert[] =
+{
+ { 0, 0 }, { 21600, 0 }, { 21600, 21600 }, { 0, 21600 },
+ { 0, 0 }, { 21600, 0 }, { 3 MSO_I, 2 MSO_I }, { 1 MSO_I, 2 MSO_I },
+ { 21600, 0 }, { 21600, 21600 }, { 3 MSO_I, 4 MSO_I }, { 3 MSO_I, 2 MSO_I },
+ { 21600, 21600 }, { 0, 21600 }, { 1 MSO_I, 4 MSO_I }, { 3 MSO_I, 4 MSO_I },
+ { 0, 21600 }, { 0, 0 }, { 1 MSO_I, 2 MSO_I }, { 1 MSO_I, 4 MSO_I },
+
+ { 7 MSO_I, 0xa MSO_I }, { 0xc MSO_I, 0xe MSO_I }, { 0xc MSO_I, 0x10 MSO_I }, { 0x12 MSO_I, 0x10 MSO_I },
+ { 0x12 MSO_I, 0x14 MSO_I }, { 0x16 MSO_I, 8 MSO_I }, { 0x18 MSO_I, 8 MSO_I }, { 0x18 MSO_I, 0x1a MSO_I },
+ { 0x1c MSO_I, 0x1a MSO_I }, { 0x1c MSO_I, 8 MSO_I }, { 0x1e MSO_I, 8 MSO_I },
+
+ { 0xc MSO_I, 0xe MSO_I }, { 0xc MSO_I, 0x10 MSO_I }, { 0x12 MSO_I, 0x10 MSO_I },{ 0x12 MSO_I, 0x14 MSO_I },
+
+ { 0x20 MSO_I, 0x24 MSO_I }, { 0x22 MSO_I, 0x24 MSO_I }, { 0x22 MSO_I, 0x1a MSO_I }, { 0x18 MSO_I, 0x1a MSO_I },
+ { 0x18 MSO_I, 8 MSO_I }, { 0x1c MSO_I, 8 MSO_I }, { 0x1c MSO_I, 0x1a MSO_I }, { 0x20 MSO_I, 0x1a MSO_I }
+
+};
+const sal_uInt16 mso_sptActionButtonHomeSegm[] =
+{
+ 0x4000, 0x0003, 0x6001, 0x8000,
+ 0x4000, 0x0003, 0x6001, 0x8000,
+ 0x4000, 0x0003, 0x6001, 0x8000,
+ 0x4000, 0x0003, 0x6001, 0x8000,
+ 0x4000, 0x0003, 0x6001, 0x8000,
+ 0x4000, 0x000a, 0x6001, 0x8000,
+ 0x4000, 0x0003, 0x6001, 0x8000,
+ 0x4000, 0x0007, 0x6001, 0x8000
+};
+const SvxMSDffCalculationData mso_sptActionButtonHomeCalc[] = // adj value 0 - 5400
+{
+ { 0x2000, { DFF_Prop_adjustValue, 0, 0 } },
+ { 0x6000, { DFF_Prop_geoLeft, DFF_Prop_adjustValue, 0 } },
+ { 0x6000, { DFF_Prop_geoTop, DFF_Prop_adjustValue, 0 } },
+ { 0xa000, { DFF_Prop_geoRight, 0, DFF_Prop_adjustValue } },
+ { 0xa000, { DFF_Prop_geoBottom, 0, DFF_Prop_adjustValue } },
+ { 0x8000, { 10800, 0, DFF_Prop_adjustValue } },
+ { 0x2001, { 0x0405, 1, 10800 } }, // scaling 6
+ { 0x2001, { DFF_Prop_geoRight, 1, 2 } }, // lr center 7
+ { 0x2001, { DFF_Prop_geoBottom, 1, 2 } }, // ul center 8
+
+ { 0x4001, { -8000, 0x0406, 1 } }, // 9
+ { 0x6000, { 0x0409, 0x0408, 0 } }, // a
+ { 0x4001, { 2960, 0x0406, 1 } }, // b
+ { 0x6000, { 0x040b, 0x0407, 0 } }, // c
+ { 0x4001, { -5000, 0x0406, 1 } }, // d
+ { 0x6000, { 0x040d, 0x0408, 0 } }, // e
+ { 0x4001, { -7000, 0x0406, 1 } }, // f
+ { 0x6000, { 0x040f, 0x0408, 0 } }, // 10
+ { 0x4001, { 5000, 0x0406, 1 } }, // 11
+ { 0x6000, { 0x0411, 0x0407, 0 } }, // 12
+ { 0x4001, { -2960, 0x0406, 1 } }, // 13
+ { 0x6000, { 0x0413, 0x0408, 0 } }, // 14
+ { 0x4001, { 8000, 0x0406, 1 } }, // 15
+ { 0x6000, { 0x0415,0x0407, 0 } }, // 16
+ { 0x4001, { 6100, 0x0406, 1 } }, // 17
+ { 0x6000, { 0x0417,0x0407, 0 } }, // 18
+ { 0x4001, { 8260, 0x0406, 1 } }, // 19
+ { 0x6000, { 0x0419, 0x0408, 0 } }, // 1a
+ { 0x4001, { -6100, 0x0406, 1 } }, // 1b
+ { 0x6000, { 0x041b, 0x0407, 0 } }, // 1c
+ { 0x4001, { -8000, 0x0406, 1 } }, // 1d
+ { 0x6000, { 0x041d, 0x0407, 0 } }, // 1e
+ { 0x4001, { -1060, 0x0406, 1 } }, // 1f
+ { 0x6000, { 0x041f, 0x0407, 0 } }, // 20
+ { 0x4001, { 1060, 0x0406, 1 } }, // 21
+ { 0x6000, { 0x0421, 0x0407, 0 } }, // 22
+ { 0x4001, { 4020, 0x0406, 1 } }, // 23
+ { 0x6000, { 0x0423, 0x0408, 0 } } // 24
+
+};
+const mso_CustomShape msoActionButtonHome =
+{
+ const_cast<SvxMSDffVertPair*>(mso_sptActionButtonHomeVert), SAL_N_ELEMENTS( mso_sptActionButtonHomeVert ),
+ const_cast<sal_uInt16*>(mso_sptActionButtonHomeSegm), sizeof( mso_sptActionButtonHomeSegm ) >> 1,
+ const_cast<SvxMSDffCalculationData*>(mso_sptActionButtonHomeCalc), SAL_N_ELEMENTS( mso_sptActionButtonHomeCalc ),
+ const_cast<sal_Int32*>(mso_sptDefault1400),
+ const_cast<SvxMSDffTextRectangles*>(mso_sptActionButtonTextRect), SAL_N_ELEMENTS( mso_sptActionButtonTextRect ),
+ 21600, 21600,
+ 10800, 10800,
+ nullptr, 0,
+ const_cast<SvxMSDffHandle*>(mso_sptButtonHandle), SAL_N_ELEMENTS( mso_sptButtonHandle )
+};
+
+const SvxMSDffVertPair mso_sptActionButtonHelpVert[] =
+{
+ { 0, 0 }, { 21600, 0 }, { 21600, 21600 }, { 0, 21600 },
+ { 0, 0 }, { 21600, 0 }, { 3 MSO_I, 2 MSO_I }, { 1 MSO_I, 2 MSO_I },
+ { 21600, 0 }, { 21600, 21600 }, { 3 MSO_I, 4 MSO_I }, { 3 MSO_I, 2 MSO_I },
+ { 21600, 21600 }, { 0, 21600 }, { 1 MSO_I, 4 MSO_I }, { 3 MSO_I, 4 MSO_I },
+ { 0, 21600 }, { 0, 0 }, { 1 MSO_I, 2 MSO_I }, { 1 MSO_I,4 MSO_I },
+ { 7 MSO_I, 0xc MSO_I }, { 0xa MSO_I, 0x3e MSO_I }, { 7 MSO_I, 0x10 MSO_I }, { 0xe MSO_I, 0x3e MSO_I }, { 7 MSO_I, 0xc MSO_I },
+ { 0x12 MSO_I, 0x14 MSO_I }, { 0x12 MSO_I, 0x16 MSO_I }, // pp
+ { 0x12 MSO_I, 0x18 MSO_I }, { 0x1a MSO_I, 8 MSO_I }, { 0x1c MSO_I, 8 MSO_I }, // ccp
+ { 0x1e MSO_I, 8 MSO_I }, { 0x20 MSO_I, 0x22 MSO_I }, { 0x20 MSO_I, 0x24 MSO_I }, // ccp
+ { 0x20 MSO_I, 0x26 MSO_I }, { 0x28 MSO_I, 0x2a MSO_I }, { 7 MSO_I, 0x2a MSO_I }, // ccp
+ { 0x2c MSO_I, 0x2a MSO_I }, { 0x2e MSO_I, 0x26 MSO_I }, { 0x2e MSO_I, 0x24 MSO_I }, // ccp
+ { 0x30 MSO_I, 0x24 MSO_I }, { 0x30 MSO_I, 0x32 MSO_I }, { 0x34 MSO_I, 0x36 MSO_I }, // ccp
+ { 7 MSO_I, 0x36 MSO_I }, // p
+ { 0x12 MSO_I, 0x36 MSO_I }, { 0x1c MSO_I, 0x32 MSO_I }, { 0x1c MSO_I, 0x24 MSO_I }, // ccp
+ { 0x1c MSO_I, 0x38 MSO_I }, { 0x3a MSO_I, 0x3c MSO_I }, { 0x12 MSO_I, 0x3c MSO_I }, // ccp
+ { 7 MSO_I, 0x3c MSO_I }, { 0x34 MSO_I, 8 MSO_I }, { 0x34 MSO_I, 0x16 MSO_I }, // ccp
+ { 0x34 MSO_I, 0x14 MSO_I }
+};
+const sal_uInt16 mso_sptActionButtonHelpSegm[] =
+{
+ 0x4000, 0x0003, 0x6001, 0x8000,
+ 0x4000, 0x0003, 0x6001, 0x8000,
+ 0x4000, 0x0003, 0x6001, 0x8000,
+ 0x4000, 0x0003, 0x6001, 0x8000,
+ 0x4000, 0x0003, 0x6001, 0x8000,
+ 0x4000, 0xa704, 0x6000, 0x8000,
+ 0x4000, 0x0001, 0x2004, 0x0001, 0x2004, 0x0001, 0x6001, 0x8000
+};
+const SvxMSDffCalculationData mso_sptActionButtonHelpCalc[] = // adj value 0 - 5400
+{
+ { 0x2000, { DFF_Prop_adjustValue, 0, 0 } },
+ { 0x6000, { DFF_Prop_geoLeft, DFF_Prop_adjustValue, 0 } },
+ { 0x6000, { DFF_Prop_geoTop, DFF_Prop_adjustValue, 0 } },
+ { 0xa000, { DFF_Prop_geoRight, 0, DFF_Prop_adjustValue } },
+ { 0xa000, { DFF_Prop_geoBottom, 0, DFF_Prop_adjustValue } },
+ { 0x8000, { 10800, 0, DFF_Prop_adjustValue } },
+ { 0x2001, { 0x0405, 1, 10800 } }, // scaling 6
+ { 0x2001, { DFF_Prop_geoRight, 1, 2 } }, // lr center 7
+ { 0x2001, { DFF_Prop_geoBottom, 1, 2 } }, // ul center 8
+
+ { 0x4001, { -1690, 0x0406, 1 } }, // 9
+ { 0x6000, { 0x0409, 0x0407, 0 } }, // a
+ { 0x4001, { 4600, 0x0406, 1 } }, // b
+ { 0x6000, { 0x040b, 0x0408, 0 } }, // c
+ { 0x4001, { 1690, 0x0406, 1 } }, // d
+ { 0x6000, { 0x040d, 0x0407, 0 } }, // e
+ { 0x4001, { 7980, 0x0406, 1 } }, // f
+ { 0x6000, { 0x040f, 0x0408, 0 } }, // 10
+ { 0x4001, { 1270, 0x0406, 1 } }, // 11
+ { 0x6000, { 0x0411, 0x0407, 0 } }, // 12
+ { 0x4001, { 4000, 0x0406, 1 } }, // 13
+ { 0x6000, { 0x0413, 0x0408, 0 } }, // 14
+ { 0x4001, { 1750, 0x0406, 1 } }, // 15
+ { 0x6000, { 0x0415, 0x0408, 0 } }, // 16
+ { 0x4001, { 800, 0x0406, 1 } }, // 17
+ { 0x6000, { 0x0417, 0x0408, 0 } }, // 18
+ { 0x4001, { 1650, 0x0406, 1 } }, // 19
+ { 0x6000, { 0x0419, 0x0407, 0 } }, // 1a
+ { 0x4001, { 2340, 0x0406, 1 } }, // 1b
+ { 0x6000, { 0x041b, 0x0407, 0 } }, // 1c
+ { 0x4001, { 3640, 0x0406, 1 } }, // 1d
+ { 0x6000, { 0x041d, 0x0407, 0 } }, // 1e
+ { 0x4001, { 4670, 0x0406, 1 } }, // 1f
+ { 0x6000, { 0x041f, 0x0407, 0 } }, // 20
+ { 0x4001, { -1570, 0x0406, 1 } }, // 21
+ { 0x6000, { 0x0421, 0x0408, 0 } }, // 22
+ { 0x4001, { -3390, 0x0406, 1 } }, // 23
+ { 0x6000, { 0x0423, 0x0408, 0 } }, // 24
+ { 0x4001, { -6050, 0x0406, 1 } }, // 25
+ { 0x6000, { 0x0425, 0x0408, 0 } }, // 26
+ { 0x4001, { 2540, 0x0406, 1 } }, // 27
+ { 0x6000, { 0x0427, 0x0407, 0 } }, // 28
+ { 0x4001, { -8050, 0x0406, 1 } }, // 29
+ { 0x6000, { 0x0429, 0x0408, 0 } }, // 2a
+ { 0x4001, { -2540, 0x0406, 1 } }, // 2b
+ { 0x6000, { 0x042b, 0x0407, 0 } }, // 2c
+ { 0x4001, { -4460, 0x0406, 1 } }, // 2d
+ { 0x6000, { 0x042d, 0x0407, 0 } }, // 2e
+ { 0x4001, { -2330, 0x0406, 1 } }, // 2f
+ { 0x6000, { 0x042f, 0x0407, 0 } }, // 30
+ { 0x4001, { -4700, 0x0406, 1 } }, // 31
+ { 0x6000, { 0x0431, 0x0408, 0 } }, // 32
+ { 0x4001, { -1270, 0x0406, 1 } }, // 33
+ { 0x6000, { 0x0433, 0x0407, 0 } }, // 34
+ { 0x4001, { -5720, 0x0406, 1 } }, // 35
+ { 0x6000, { 0x0435, 0x0408, 0 } }, // 36
+ { 0x4001, { -2540, 0x0406, 1 } }, // 37
+ { 0x6000, { 0x0437, 0x0408, 0 } }, // 38
+ { 0x4001, { 1800, 0x0406, 1 } }, // 39
+ { 0x6000, { 0x0439, 0x0407, 0 } }, // 3a
+ { 0x4001, { -1700, 0x0406, 1 } }, // 3b
+ { 0x6000, { 0x043b, 0x0408, 0 } }, // 3c
+ { 0x4001, { 6290, 0x0406, 1 } }, // 3d
+ { 0x6000, { 0x043d, 0x0408, 0 } } // 3e
+};
+const mso_CustomShape msoActionButtonHelp =
+{
+ const_cast<SvxMSDffVertPair*>(mso_sptActionButtonHelpVert), SAL_N_ELEMENTS( mso_sptActionButtonHelpVert ),
+ const_cast<sal_uInt16*>(mso_sptActionButtonHelpSegm), sizeof( mso_sptActionButtonHelpSegm ) >> 1,
+ const_cast<SvxMSDffCalculationData*>(mso_sptActionButtonHelpCalc), SAL_N_ELEMENTS( mso_sptActionButtonHelpCalc ),
+ const_cast<sal_Int32*>(mso_sptDefault1400),
+ const_cast<SvxMSDffTextRectangles*>(mso_sptActionButtonTextRect), SAL_N_ELEMENTS( mso_sptActionButtonTextRect ),
+ 21600, 21600,
+ 10800, 10800,
+ nullptr, 0,
+ const_cast<SvxMSDffHandle*>(mso_sptButtonHandle), SAL_N_ELEMENTS( mso_sptButtonHandle )
+};
+
+const SvxMSDffVertPair mso_sptActionButtonInformationVert[] =
+{
+ { 0, 0 }, { 21600, 0 }, { 21600, 21600 }, { 0, 21600 },
+ { 0, 0 }, { 21600, 0 }, { 3 MSO_I, 2 MSO_I }, { 1 MSO_I, 2 MSO_I },
+ { 21600, 0 }, { 21600, 21600 }, { 3 MSO_I, 4 MSO_I }, { 3 MSO_I, 2 MSO_I },
+ { 21600, 21600 }, { 0, 21600 }, { 1 MSO_I, 4 MSO_I }, { 3 MSO_I, 4 MSO_I },
+ { 0, 21600 }, { 0, 0 }, { 1 MSO_I, 2 MSO_I }, { 1 MSO_I, 4 MSO_I },
+ { 0x7 MSO_I, 0xc MSO_I }, { 0xa MSO_I, 0x8 MSO_I }, { 0x7 MSO_I, 0x10 MSO_I }, { 0xe MSO_I, 8 MSO_I }, { 0x7 MSO_I, 0xc MSO_I },
+ { 0x7 MSO_I, 0x14 MSO_I }, { 0x12 MSO_I, 0x2a MSO_I }, { 0x7 MSO_I, 0x18 MSO_I }, { 0x16 MSO_I, 0x2a MSO_I }, { 0x7 MSO_I, 0x14 MSO_I },
+ { 0x1a MSO_I, 0x1c MSO_I }, { 0x1e MSO_I, 0x1c MSO_I }, { 0x1e MSO_I, 0x20 MSO_I }, { 0x22 MSO_I, 0x20 MSO_I },
+ { 0x22 MSO_I, 0x24 MSO_I }, { 0x1a MSO_I, 0x24 MSO_I }, { 0x1a MSO_I, 0x20 MSO_I }, { 0x26 MSO_I, 0x20 MSO_I },
+ { 0x26 MSO_I, 0x28 MSO_I }, { 0x1a MSO_I, 0x28 MSO_I }
+};
+const sal_uInt16 mso_sptActionButtonInformationSegm[] =
+{
+ 0x4000, 0x0003, 0x6001, 0x8000,
+ 0x4000, 0x0003, 0x6001, 0x8000,
+ 0x4000, 0x0003, 0x6001, 0x8000,
+ 0x4000, 0x0003, 0x6001, 0x8000,
+ 0x4000, 0x0003, 0x6001, 0x8000,
+ 0x4000, 0xa704, 0x6000, 0x8000,
+ 0x4000, 0xa704, 0x6000, 0x8000,
+ 0x4000, 0x0009, 0x6001, 0x8000
+};
+const SvxMSDffCalculationData mso_sptActionButtonInformationCalc[] = // adj value 0 - 5400
+{
+ { 0x2000, { DFF_Prop_adjustValue, 0, 0 } },
+ { 0x6000, { DFF_Prop_geoLeft, DFF_Prop_adjustValue, 0 } },
+ { 0x6000, { DFF_Prop_geoTop, DFF_Prop_adjustValue, 0 } },
+ { 0xa000, { DFF_Prop_geoRight, 0, DFF_Prop_adjustValue } },
+ { 0xa000, { DFF_Prop_geoBottom, 0, DFF_Prop_adjustValue } },
+ { 0x8000, { 10800, 0, DFF_Prop_adjustValue } },
+ { 0x2001, { 0x0405, 1, 10800 } }, // scaling 6
+ { 0x2001, { DFF_Prop_geoRight, 1, 2 } }, // lr center 7
+ { 0x2001, { DFF_Prop_geoBottom, 1, 2 } }, // ul center 8
+
+ { 0x4001, { -8050, 0x0406, 1 } }, // 9
+ { 0x6000, { 0x0409, 0x0407, 0 } }, // a
+ { 0x4001, { -8050, 0x0406, 1 } }, // b
+ { 0x6000, { 0x040b, 0x0408, 0 } }, // c
+ { 0x4001, { 8050, 0x0406, 1 } }, // d
+ { 0x6000, { 0x040d, 0x0407, 0 } }, // e
+ { 0x4001, { 8050, 0x0406, 1 } }, // f
+ { 0x6000, { 0x040f, 0x0408, 0 } }, // 10
+
+ { 0x4001, { -2060, 0x0406, 1 } }, // 11
+ { 0x6000, { 0x0411, 0x0407, 0 } }, // 12
+ { 0x4001, { -7620, 0x0406, 1 } }, // 13
+ { 0x6000, { 0x0413, 0x0408, 0 } }, // 14
+ { 0x4001, { 2060, 0x0406, 1 } }, // 15
+ { 0x6000, { 0x0415, 0x0407, 0 } }, // 16
+ { 0x4001, { -3500, 0x0406, 1 } }, // 17
+ { 0x6000, { 0x0417, 0x0408, 0 } }, // 18
+
+ { 0x4001, { -2960, 0x0406, 1 } }, // 19
+ { 0x6000, { 0x0419, 0x0407, 0 } }, // 1a
+ { 0x4001, { -2960, 0x0406, 1 } }, // 1b
+ { 0x6000, { 0x041b, 0x0408, 0 } }, // 1c
+ { 0x4001, { 1480, 0x0406, 1 } }, // 1d
+ { 0x6000, { 0x041d, 0x0407, 0 } }, // 1e
+ { 0x4001, { 5080, 0x0406, 1 } }, // 1f
+ { 0x6000, { 0x041f, 0x0408, 0 } }, // 20
+ { 0x4001, { 2960, 0x0406, 1 } }, // 21
+ { 0x6000, { 0x0421, 0x0407, 0 } }, // 22
+ { 0x4001, { 6140, 0x0406, 1 } }, // 23
+ { 0x6000, { 0x0423, 0x0408, 0 } }, // 24
+ { 0x4001, { -1480, 0x0406, 1 } }, // 25
+ { 0x6000, { 0x0425, 0x0407, 0 } }, // 26
+ { 0x4001, { -1920, 0x0406, 1 } }, // 27
+ { 0x6000, { 0x0427, 0x0408, 0 } }, // 28
+
+ { 0x4001, { -5560, 0x0406, 1 } }, // 29
+ { 0x6000, { 0x0429, 0x0408, 0 } }, // 2a
+
+};
+const mso_CustomShape msoActionButtonInformation =
+{
+ const_cast<SvxMSDffVertPair*>(mso_sptActionButtonInformationVert), SAL_N_ELEMENTS( mso_sptActionButtonInformationVert ),
+ const_cast<sal_uInt16*>(mso_sptActionButtonInformationSegm), sizeof( mso_sptActionButtonInformationSegm ) >> 1,
+ const_cast<SvxMSDffCalculationData*>(mso_sptActionButtonInformationCalc), SAL_N_ELEMENTS( mso_sptActionButtonInformationCalc ),
+ const_cast<sal_Int32*>(mso_sptDefault1400),
+ const_cast<SvxMSDffTextRectangles*>(mso_sptActionButtonTextRect), SAL_N_ELEMENTS( mso_sptActionButtonTextRect ),
+ 21600, 21600,
+ 10800, 10800,
+ nullptr, 0,
+ const_cast<SvxMSDffHandle*>(mso_sptButtonHandle), SAL_N_ELEMENTS( mso_sptButtonHandle )
+};
+
+const SvxMSDffVertPair mso_sptActionButtonBackPreviousVert[] =
+{
+ { 0, 0 }, { 21600, 0 }, { 21600, 21600 }, { 0, 21600 },
+ { 0, 0 }, { 21600, 0 }, { 3 MSO_I, 2 MSO_I }, { 1 MSO_I, 2 MSO_I },
+ { 21600, 0 }, { 21600, 21600 }, { 3 MSO_I, 4 MSO_I }, { 3 MSO_I, 2 MSO_I },
+ { 21600, 21600 }, { 0, 21600 }, { 1 MSO_I, 4 MSO_I }, { 3 MSO_I,4 MSO_I },
+ { 0, 21600 }, { 0, 0 }, { 1 MSO_I, 2 MSO_I }, { 1 MSO_I, 4 MSO_I },
+
+ { 0xa MSO_I, 8 MSO_I }, { 0xe MSO_I, 0xc MSO_I }, { 0xe MSO_I, 0x10 MSO_I }
+};
+const sal_uInt16 mso_sptActionButtonForwardBackSegm[] =
+{
+ 0x4000, 0x0003, 0x6001, 0x8000,
+ 0x4000, 0x0003, 0x6001, 0x8000,
+ 0x4000, 0x0003, 0x6001, 0x8000,
+ 0x4000, 0x0003, 0x6001, 0x8000,
+ 0x4000, 0x0003, 0x6001, 0x8000,
+ 0x4000, 0x0002, 0x6001, 0x8000
+};
+const SvxMSDffCalculationData mso_sptActionButtonForwardBackCalc[] = // adj value 0 - 5400
+{
+ { 0x2000, { DFF_Prop_adjustValue, 0, 0 } },
+ { 0x6000, { DFF_Prop_geoLeft, DFF_Prop_adjustValue, 0 } },
+ { 0x6000, { DFF_Prop_geoTop, DFF_Prop_adjustValue, 0 } },
+ { 0xa000, { DFF_Prop_geoRight, 0, DFF_Prop_adjustValue } },
+ { 0xa000, { DFF_Prop_geoBottom, 0, DFF_Prop_adjustValue } },
+ { 0x8000, { 10800, 0, DFF_Prop_adjustValue } },
+ { 0x2001, { 0x0405, 1, 10800 } }, // scaling 6
+ { 0x2001, { DFF_Prop_geoRight, 1, 2 } }, // lr center 7
+ { 0x2001, { DFF_Prop_geoBottom, 1, 2 } }, // ul center 8
+
+ { 0x4001, { -8050, 0x0406, 1 } }, // 9
+ { 0x6000, { 0x0409, 0x0407, 0 } }, // a
+ { 0x4001, { -8050, 0x0406, 1 } }, // b
+ { 0x6000, { 0x040b, 0x0408, 0 } }, // c
+ { 0x4001, { 8050, 0x0406, 1 } }, // d
+ { 0x6000, { 0x040d, 0x0407, 0 } }, // e
+ { 0x4001, { 8050, 0x0406, 1 } }, // f
+ { 0x6000, { 0x040f, 0x0408, 0 } } // 10
+};
+const mso_CustomShape msoActionButtonBackPrevious =
+{
+ const_cast<SvxMSDffVertPair*>(mso_sptActionButtonBackPreviousVert), SAL_N_ELEMENTS( mso_sptActionButtonBackPreviousVert ),
+ const_cast<sal_uInt16*>(mso_sptActionButtonForwardBackSegm), sizeof( mso_sptActionButtonForwardBackSegm ) >> 1,
+ const_cast<SvxMSDffCalculationData*>(mso_sptActionButtonForwardBackCalc), SAL_N_ELEMENTS( mso_sptActionButtonForwardBackCalc ),
+ const_cast<sal_Int32*>(mso_sptDefault1400),
+ const_cast<SvxMSDffTextRectangles*>(mso_sptActionButtonTextRect), SAL_N_ELEMENTS( mso_sptActionButtonTextRect ),
+ 21600, 21600,
+ 10800, 10800,
+ nullptr, 0,
+ const_cast<SvxMSDffHandle*>(mso_sptButtonHandle), SAL_N_ELEMENTS( mso_sptButtonHandle )
+};
+
+const SvxMSDffVertPair mso_sptActionButtonForwardNextVert[] =
+{
+ { 0, 0 }, { 21600, 0 }, { 21600, 21600 }, { 0, 21600 },
+ { 0, 0 }, { 21600, 0 }, { 3 MSO_I, 2 MSO_I }, { 1 MSO_I, 2 MSO_I },
+ { 21600, 0 }, { 21600, 21600 }, { 3 MSO_I, 4 MSO_I }, { 3 MSO_I, 2 MSO_I },
+ { 21600, 21600 }, { 0, 21600 }, { 1 MSO_I, 4 MSO_I }, { 3 MSO_I, 4 MSO_I },
+ { 0, 21600 }, { 0, 0 }, { 1 MSO_I, 2 MSO_I }, { 1 MSO_I, 4 MSO_I },
+
+ { 0xa MSO_I, 0xc MSO_I }, { 0xe MSO_I, 8 MSO_I }, { 0xa MSO_I, 0x10 MSO_I }
+};
+const mso_CustomShape msoActionButtonForwardNext =
+{
+ const_cast<SvxMSDffVertPair*>(mso_sptActionButtonForwardNextVert), SAL_N_ELEMENTS( mso_sptActionButtonForwardNextVert ),
+ const_cast<sal_uInt16*>(mso_sptActionButtonForwardBackSegm), sizeof( mso_sptActionButtonForwardBackSegm ) >> 1,
+ const_cast<SvxMSDffCalculationData*>(mso_sptActionButtonForwardBackCalc), SAL_N_ELEMENTS( mso_sptActionButtonForwardBackCalc ),
+ const_cast<sal_Int32*>(mso_sptDefault1400),
+ const_cast<SvxMSDffTextRectangles*>(mso_sptActionButtonTextRect), SAL_N_ELEMENTS( mso_sptActionButtonTextRect ),
+ 21600, 21600,
+ 10800, 10800,
+ nullptr, 0,
+ const_cast<SvxMSDffHandle*>(mso_sptButtonHandle), SAL_N_ELEMENTS( mso_sptButtonHandle )
+};
+
+const SvxMSDffVertPair mso_sptActionButtonBeginningVert[] =
+{
+ { 0, 0 }, { 21600, 0 }, { 21600, 21600 }, { 0, 21600 },
+ { 0, 0 }, { 21600, 0 }, { 3 MSO_I, 2 MSO_I }, { 1 MSO_I, 2 MSO_I },
+ { 21600, 0 }, { 21600, 21600 }, { 3 MSO_I, 4 MSO_I }, { 3 MSO_I, 2 MSO_I },
+ { 21600, 21600 }, { 0, 21600 }, { 1 MSO_I, 4 MSO_I }, { 3 MSO_I, 4 MSO_I },
+ { 0, 21600 }, { 0, 0 }, { 1 MSO_I, 2 MSO_I }, { 1 MSO_I, 4 MSO_I },
+
+ { 0xa MSO_I, 8 MSO_I }, { 0xe MSO_I, 0xc MSO_I }, { 0xe MSO_I, 0x10 MSO_I }, { 0x12 MSO_I, 0xc MSO_I },
+ { 0x14 MSO_I, 0xc MSO_I }, { 0x14 MSO_I, 0x10 MSO_I }, { 0x12 MSO_I, 0x10 MSO_I }
+};
+const sal_uInt16 mso_sptActionButtonBeginningEndSegm[] =
+{
+ 0x4000, 0x0003, 0x6001, 0x8000,
+ 0x4000, 0x0003, 0x6001, 0x8000,
+ 0x4000, 0x0003, 0x6001, 0x8000,
+ 0x4000, 0x0003, 0x6001, 0x8000,
+ 0x4000, 0x0003, 0x6001, 0x8000,
+
+ 0x4000, 0x0002, 0x6001, 0x8000,
+ 0x4000, 0x0003, 0x6001, 0x8000
+};
+const SvxMSDffCalculationData mso_sptActionButtonBeginningEndCalc[] = // adj value 0 - 5400
+{
+ { 0x2000, { DFF_Prop_adjustValue, 0, 0 } },
+ { 0x6000, { DFF_Prop_geoLeft, DFF_Prop_adjustValue, 0 } },
+ { 0x6000, { DFF_Prop_geoTop, DFF_Prop_adjustValue, 0 } },
+ { 0xa000, { DFF_Prop_geoRight, 0, DFF_Prop_adjustValue } },
+ { 0xa000, { DFF_Prop_geoBottom, 0, DFF_Prop_adjustValue } },
+ { 0x8000, { 10800, 0, DFF_Prop_adjustValue } },
+ { 0x2001, { 0x0405, 1, 10800 } }, // scaling 6
+ { 0x2001, { DFF_Prop_geoRight, 1, 2 } }, // lr center 7
+ { 0x2001, { DFF_Prop_geoBottom, 1, 2 } }, // ul center 8
+
+ { 0x4001, { -4020, 0x0406, 1 } }, // 9
+ { 0x6000, { 0x0409, 0x0407, 0 } }, // a
+ { 0x4001, { -8050, 0x0406, 1 } }, // b
+ { 0x6000, { 0x040b, 0x0408, 0 } }, // c
+ { 0x4001, { 8050, 0x0406, 1 } }, // d
+ { 0x6000, { 0x040d, 0x0407, 0 } }, // e
+ { 0x4001, { 8050, 0x0406, 1 } }, // f
+ { 0x6000, { 0x040f, 0x0408, 0 } }, // 10
+
+ { 0x4001, { -8050, 0x0406, 1 } }, // 11
+ { 0x6000, { 0x0411, 0x0407, 0 } }, // 12
+ { 0x4001, { -6140, 0x0406, 1 } }, // 13
+ { 0x6000, { 0x0413, 0x0407, 0 } }, // 14
+
+
+ { 0x4001, { 4020, 0x0406, 1 } }, // 15
+ { 0x6000, { 0x0415, 0x0407, 0 } }, // 16
+ { 0x4001, { 6140, 0x0406, 1 } }, // 17
+ { 0x6000, { 0x0417, 0x0407, 0 } } // 18
+};
+const mso_CustomShape msoActionButtonBeginning =
+{
+ const_cast<SvxMSDffVertPair*>(mso_sptActionButtonBeginningVert), SAL_N_ELEMENTS( mso_sptActionButtonBeginningVert ),
+ const_cast<sal_uInt16*>(mso_sptActionButtonBeginningEndSegm), sizeof( mso_sptActionButtonBeginningEndSegm ) >> 1,
+ const_cast<SvxMSDffCalculationData*>(mso_sptActionButtonBeginningEndCalc), SAL_N_ELEMENTS( mso_sptActionButtonBeginningEndCalc ),
+ const_cast<sal_Int32*>(mso_sptDefault1400),
+ const_cast<SvxMSDffTextRectangles*>(mso_sptActionButtonTextRect), SAL_N_ELEMENTS( mso_sptActionButtonTextRect ),
+ 21600, 21600,
+ 10800, 10800,
+ nullptr, 0,
+ const_cast<SvxMSDffHandle*>(mso_sptButtonHandle), SAL_N_ELEMENTS( mso_sptButtonHandle )
+};
+
+const SvxMSDffVertPair mso_sptActionButtonEndVert[] =
+{
+ { 0, 0 }, { 21600, 0 }, { 21600, 21600 }, { 0, 21600 },
+ { 0, 0 }, { 21600, 0 }, { 3 MSO_I, 2 MSO_I }, { 1 MSO_I, 2 MSO_I },
+ { 21600, 0 }, { 21600, 21600 }, { 3 MSO_I, 4 MSO_I }, { 3 MSO_I, 2 MSO_I },
+ { 21600, 21600 }, { 0, 21600 }, { 1 MSO_I, 4 MSO_I }, { 3 MSO_I, 4 MSO_I },
+ { 0, 21600 }, { 0, 0 }, { 1 MSO_I, 2 MSO_I }, { 1 MSO_I, 4 MSO_I },
+
+ { 0x16 MSO_I, 8 MSO_I }, { 0x12 MSO_I, 0x10 MSO_I }, { 0x12 MSO_I, 0xc MSO_I },
+
+ { 0x18 MSO_I, 0xc MSO_I }, { 0x18 MSO_I, 0x10 MSO_I }, { 0xe MSO_I, 0x10 MSO_I }, { 0xe MSO_I, 0xc MSO_I }
+};
+const mso_CustomShape msoActionButtonEnd =
+{
+ const_cast<SvxMSDffVertPair*>(mso_sptActionButtonEndVert), SAL_N_ELEMENTS( mso_sptActionButtonEndVert ),
+ const_cast<sal_uInt16*>(mso_sptActionButtonBeginningEndSegm), sizeof( mso_sptActionButtonBeginningEndSegm ) >> 1,
+ const_cast<SvxMSDffCalculationData*>(mso_sptActionButtonBeginningEndCalc), SAL_N_ELEMENTS( mso_sptActionButtonBeginningEndCalc ),
+ const_cast<sal_Int32*>(mso_sptDefault1400),
+ const_cast<SvxMSDffTextRectangles*>(mso_sptActionButtonTextRect), SAL_N_ELEMENTS( mso_sptActionButtonTextRect ),
+ 21600, 21600,
+ 10800, 10800,
+ nullptr, 0,
+ const_cast<SvxMSDffHandle*>(mso_sptButtonHandle), SAL_N_ELEMENTS( mso_sptButtonHandle )
+};
+
+const SvxMSDffVertPair mso_sptActionButtonReturnVert[] =
+{
+ { 0, 0 }, { 21600, 0 }, { 21600, 21600 }, { 0, 21600 },
+ { 0, 0 }, { 21600, 0 }, { 3 MSO_I, 2 MSO_I }, { 1 MSO_I, 2 MSO_I },
+ { 21600, 0 }, { 21600, 21600 }, { 3 MSO_I, 4 MSO_I }, { 3 MSO_I, 2 MSO_I },
+ { 21600, 21600 }, { 0, 21600 }, { 1 MSO_I, 4 MSO_I }, { 3 MSO_I, 4 MSO_I },
+ { 0, 21600 }, { 0, 0 }, { 1 MSO_I, 2 MSO_I }, { 1 MSO_I, 4 MSO_I },
+
+ { 0xa MSO_I, 0xc MSO_I }, { 0xe MSO_I, 0xc MSO_I }, { 0xe MSO_I, 0x10 MSO_I }, // ppp
+ { 0xe MSO_I, 0x12 MSO_I }, { 0x14 MSO_I, 0x16 MSO_I }, { 0x18 MSO_I, 0x16 MSO_I }, // ccp
+ { 7 MSO_I, 0x16 MSO_I }, // p
+ { 0x1a MSO_I, 0x16 MSO_I }, { 0x1c MSO_I, 0x12 MSO_I }, { 0x1c MSO_I, 0x10 MSO_I }, // ccp
+ { 0x1c MSO_I, 0xc MSO_I }, { 7 MSO_I, 0xc MSO_I }, { 0x1e MSO_I, 0x20 MSO_I }, { 0x22 MSO_I, 0xc MSO_I },// pppp
+ { 0x24 MSO_I, 0xc MSO_I }, { 0x24 MSO_I, 0x10 MSO_I }, // pp
+ { 0x24 MSO_I, 0x26 MSO_I }, { 0x28 MSO_I, 0x2a MSO_I }, { 7 MSO_I, 0x2a MSO_I }, // ccp
+ { 0x18 MSO_I, 0x2a MSO_I }, // p
+ { 0x2c MSO_I, 0x2a MSO_I }, { 0xa MSO_I, 0x26 MSO_I }, { 0xa MSO_I, 0x10 MSO_I } // ccp
+};
+const sal_uInt16 mso_sptActionButtonReturnSegm[] =
+{
+ 0x4000, 0x0003, 0x6001, 0x8000,
+ 0x4000, 0x0003, 0x6001, 0x8000,
+ 0x4000, 0x0003, 0x6001, 0x8000,
+ 0x4000, 0x0003, 0x6001, 0x8000,
+ 0x4000, 0x0003, 0x6001, 0x8000,
+ 0x4000, 0x0002, 0x2001, 0x0001, 0x2001, 0x0006,0x2001, 0x0001, 0x2001, 0x6001, 0x8000
+};
+const SvxMSDffCalculationData mso_sptActionButtonReturnCalc[] = // adj value 0 - 5400
+{
+ { 0x2000, { DFF_Prop_adjustValue, 0, 0 } },
+ { 0x6000, { DFF_Prop_geoLeft, DFF_Prop_adjustValue, 0 } },
+ { 0x6000, { DFF_Prop_geoTop, DFF_Prop_adjustValue, 0 } },
+ { 0xa000, { DFF_Prop_geoRight, 0, DFF_Prop_adjustValue } },
+ { 0xa000, { DFF_Prop_geoBottom, 0, DFF_Prop_adjustValue } },
+ { 0x8000, { 10800, 0, DFF_Prop_adjustValue } },
+ { 0x2001, { 0x0405, 1, 10800 } }, // scaling 6
+ { 0x2001, { DFF_Prop_geoRight, 1, 2 } }, // lr center 7
+ { 0x2001, { DFF_Prop_geoBottom, 1, 2 } }, // ul center 8
+
+ { 0x4001, { -8050, 0x0406, 1 } }, // 9
+ { 0x6000, { 0x0409, 0x0407, 0 } }, // a
+ { 0x4001, { -3800, 0x0406, 1 } }, // b
+ { 0x6000, { 0x040b, 0x0408, 0 } }, // c
+ { 0x4001, { -4020, 0x0406, 1 } }, // d
+ { 0x6000, { 0x040d, 0x0407, 0 } }, // e
+ { 0x4001, { 2330, 0x0406, 1 } }, // f
+ { 0x6000, { 0x040f, 0x0408, 0 } }, // 10
+ { 0x4001, { 3390, 0x0406, 1 } }, // 11
+ { 0x6000, { 0x0411, 0x0408, 0 } }, // 12
+ { 0x4001, { -3100, 0x0406, 1 } }, // 13
+ { 0x6000, { 0x0413, 0x0407, 0 } }, // 14
+ { 0x4001, { 4230, 0x0406, 1 } }, // 15
+ { 0x6000, { 0x0415, 0x0408, 0 } }, // 16
+ { 0x4001, { -1910, 0x0406, 1 } }, // 17
+ { 0x6000, { 0x0417, 0x0407, 0 } }, // 18
+ { 0x4001, { 1190, 0x0406, 1 } }, // 19
+ { 0x6000, { 0x0419, 0x0407, 0 } }, // 1a
+ { 0x4001, { 2110, 0x0406, 1 } }, // 1b
+ { 0x6000, { 0x041b, 0x0407, 0 } }, // 1c
+ { 0x4001, { 4030, 0x0406, 1 } }, // 1d
+ { 0x6000, { 0x041d, 0x0407, 0 } }, // 1e
+ { 0x4001, { -7830, 0x0406, 1 } }, // 1f
+ { 0x6000, { 0x041f, 0x0408, 0 } }, // 20
+ { 0x4001, { 8250, 0x0406, 1 } }, // 21
+ { 0x6000, { 0x0421, 0x0407, 0 } }, // 22
+ { 0x4001, { 6140, 0x0406, 1 } }, // 23
+ { 0x6000, { 0x0423, 0x0407, 0 } }, // 24
+ { 0x4001, { 5510, 0x0406, 1 } }, // 25
+ { 0x6000, { 0x0425, 0x0408, 0 } }, // 26
+ { 0x4001, { 3180, 0x0406, 1 } }, // 27
+ { 0x6000, { 0x0427, 0x0407, 0 } }, // 28
+ { 0x4001, { 8450, 0x0406, 1 } }, // 29
+ { 0x6000, { 0x0429, 0x0408, 0 } }, // 2a
+ { 0x4001, { -5090, 0x0406, 1 } }, // 2b
+ { 0x6000, { 0x042b, 0x0407, 0 } } // 2c
+};
+const mso_CustomShape msoActionButtonReturn =
+{
+ const_cast<SvxMSDffVertPair*>(mso_sptActionButtonReturnVert), SAL_N_ELEMENTS( mso_sptActionButtonReturnVert ),
+ const_cast<sal_uInt16*>(mso_sptActionButtonReturnSegm), sizeof( mso_sptActionButtonReturnSegm ) >> 1,
+ const_cast<SvxMSDffCalculationData*>(mso_sptActionButtonReturnCalc), SAL_N_ELEMENTS( mso_sptActionButtonReturnCalc ),
+ const_cast<sal_Int32*>(mso_sptDefault1400),
+ const_cast<SvxMSDffTextRectangles*>(mso_sptActionButtonTextRect), SAL_N_ELEMENTS( mso_sptActionButtonTextRect ),
+ 21600, 21600,
+ 10800, 10800,
+ nullptr, 0,
+ const_cast<SvxMSDffHandle*>(mso_sptButtonHandle), SAL_N_ELEMENTS( mso_sptButtonHandle )
+};
+
+const SvxMSDffVertPair mso_sptActionButtonDocumentVert[] =
+{
+ { 0, 0 }, { 21600, 0 }, { 21600, 21600 }, { 0, 21600 },
+ { 0, 0 }, { 21600, 0 }, { 3 MSO_I, 2 MSO_I }, { 1 MSO_I, 2 MSO_I },
+ { 21600, 0 }, { 21600, 21600 }, { 3 MSO_I, 4 MSO_I }, { 3 MSO_I, 2 MSO_I },
+ { 21600, 21600 }, { 0, 21600 }, { 1 MSO_I, 4 MSO_I }, { 3 MSO_I, 4 MSO_I },
+ { 0, 21600 }, { 0, 0 }, { 1 MSO_I, 2 MSO_I }, { 1 MSO_I, 4 MSO_I },
+
+ { 0xa MSO_I, 0xc MSO_I }, { 0xe MSO_I, 0xc MSO_I }, { 0x10 MSO_I, 0x12 MSO_I }, { 0x10 MSO_I, 0x14 MSO_I },
+ { 0xa MSO_I, 0x14 MSO_I }, { 0xe MSO_I, 0xc MSO_I }, { 0x10 MSO_I, 0x12 MSO_I }, { 0xe MSO_I, 0x12 MSO_I }
+};
+const sal_uInt16 mso_sptActionButtonDocumentSegm[] =
+{
+ 0x4000, 0x0003, 0x6001, 0x8000,
+ 0x4000, 0x0003, 0x6001, 0x8000,
+ 0x4000, 0x0003, 0x6001, 0x8000,
+ 0x4000, 0x0003, 0x6001, 0x8000,
+ 0x4000, 0x0003, 0x6001, 0x8000,
+
+ 0x4000, 0x0004, 0x6001, 0x8000,
+ 0x4000, 0x0002, 0x6001, 0x8000
+};
+const SvxMSDffCalculationData mso_sptActionButtonDocumentCalc[] = // adj value 0 - 5400
+{
+ { 0x2000, { DFF_Prop_adjustValue, 0, 0 } },
+ { 0x6000, { DFF_Prop_geoLeft, DFF_Prop_adjustValue, 0 } },
+ { 0x6000, { DFF_Prop_geoTop, DFF_Prop_adjustValue, 0 } },
+ { 0xa000, { DFF_Prop_geoRight, 0, DFF_Prop_adjustValue } },
+ { 0xa000, { DFF_Prop_geoBottom, 0, DFF_Prop_adjustValue } },
+ { 0x8000, { 10800, 0, DFF_Prop_adjustValue } },
+ { 0x2001, { 0x0405, 1, 10800 } }, // scaling 6
+ { 0x2001, { DFF_Prop_geoRight, 1, 2 } }, // lr center 7
+ { 0x2001, { DFF_Prop_geoBottom, 1, 2 } }, // ul center 8
+
+ { 0x4001, { -6350, 0x0406, 1 } }, // 9
+ { 0x6000, { 0x0409, 0x0407, 0 } }, // a
+ { 0x4001, { -7830, 0x0406, 1 } }, // b
+ { 0x6000, { 0x040b, 0x0408, 0 } }, // c
+ { 0x4001, { 1690, 0x0406, 1 } }, // d
+ { 0x6000, { 0x040d, 0x0407, 0 } }, // e
+ { 0x4001, { 6350, 0x0406, 1 } }, // f
+ { 0x6000, { 0x040f, 0x0407, 0 } }, // 10
+ { 0x4001, { -3810, 0x0406, 1 } }, // 11
+ { 0x6000, { 0x0411, 0x0408, 0 } }, // 12
+ { 0x4001, { 7830, 0x0406, 1 } }, // 13
+ { 0x6000, { 0x0413, 0x0408, 0 } } // 14
+};
+const mso_CustomShape msoActionButtonDocument =
+{
+ const_cast<SvxMSDffVertPair*>(mso_sptActionButtonDocumentVert), SAL_N_ELEMENTS( mso_sptActionButtonDocumentVert ),
+ const_cast<sal_uInt16*>(mso_sptActionButtonDocumentSegm), sizeof( mso_sptActionButtonDocumentSegm ) >> 1,
+ const_cast<SvxMSDffCalculationData*>(mso_sptActionButtonDocumentCalc), SAL_N_ELEMENTS( mso_sptActionButtonDocumentCalc ),
+ const_cast<sal_Int32*>(mso_sptDefault1400),
+ const_cast<SvxMSDffTextRectangles*>(mso_sptActionButtonTextRect), SAL_N_ELEMENTS( mso_sptActionButtonTextRect ),
+ 21600, 21600,
+ 10800, 10800,
+ nullptr, 0,
+ const_cast<SvxMSDffHandle*>(mso_sptButtonHandle), SAL_N_ELEMENTS( mso_sptButtonHandle )
+};
+
+const SvxMSDffVertPair mso_sptActionButtonSoundVert[] =
+{
+ { 0, 0 }, { 21600, 0 }, { 21600, 21600 }, { 0, 21600 },
+ { 0, 0 }, { 21600, 0 }, { 3 MSO_I, 2 MSO_I }, { 1 MSO_I, 2 MSO_I },
+ { 21600, 0 }, { 21600, 21600 }, { 3 MSO_I, 4 MSO_I }, { 3 MSO_I, 2 MSO_I },
+ { 21600, 21600 }, { 0, 21600 }, { 1 MSO_I, 4 MSO_I }, { 3 MSO_I, 4 MSO_I },
+ { 0, 21600 }, { 0, 0 }, { 1 MSO_I, 2 MSO_I }, { 1 MSO_I, 4 MSO_I },
+
+ { 0xa MSO_I, 0xc MSO_I }, { 0xe MSO_I, 0xc MSO_I }, { 0x10 MSO_I, 0x12 MSO_I }, { 0x10 MSO_I, 0x14 MSO_I },
+ { 0xe MSO_I, 0x16 MSO_I }, { 0xa MSO_I, 0x16 MSO_I }, { 0x18 MSO_I, 8 MSO_I }, { 0x1a MSO_I, 8 MSO_I },
+
+ { 0x18 MSO_I, 0xc MSO_I }, { 0x1a MSO_I, 0x1c MSO_I },
+
+ { 0x18 MSO_I, 0x16 MSO_I }, { 0x1a MSO_I, 0x1e MSO_I }
+};
+const sal_uInt16 mso_sptActionButtonSoundSegm[] =
+{
+ 0x4000, 0x0003, 0x6001, 0x8000,
+ 0x4000, 0x0003, 0x6001, 0x8000,
+ 0x4000, 0x0003, 0x6001, 0x8000,
+ 0x4000, 0x0003, 0x6001, 0x8000,
+ 0x4000, 0x0003, 0x6001, 0x8000,
+
+ 0x4000, 0x0005, 0x6001, 0x8000,
+ 0x4000, 0x0001, 0x8000,
+ 0x4000, 0x0001, 0x8000,
+ 0x4000, 0x0001, 0x8000
+};
+const SvxMSDffCalculationData mso_sptActionButtonSoundCalc[] = // adj value 0 - 5400
+{
+ { 0x2000, { DFF_Prop_adjustValue, 0, 0 } },
+ { 0x6000, { DFF_Prop_geoLeft, DFF_Prop_adjustValue, 0 } },
+ { 0x6000, { DFF_Prop_geoTop, DFF_Prop_adjustValue, 0 } },
+ { 0xa000, { DFF_Prop_geoRight, 0, DFF_Prop_adjustValue } },
+ { 0xa000, { DFF_Prop_geoBottom, 0, DFF_Prop_adjustValue } },
+ { 0x8000, { 10800, 0, DFF_Prop_adjustValue } },
+ { 0x2001, { 0x0405, 1, 10800 } }, // scaling 6
+ { 0x2001, { DFF_Prop_geoRight, 1, 2 } }, // lr center 7
+ { 0x2001, { DFF_Prop_geoBottom, 1, 2 } }, // ul center 8
+
+ { 0x4001, { -8050, 0x0406, 1 } }, // 9
+ { 0x6000, { 0x0409, 0x0407, 0 } }, // a
+ { 0x4001, { -2750, 0x0406, 1 } }, // b
+ { 0x6000, { 0x040b, 0x0408, 0 } }, // c
+ { 0x4001, { -2960, 0x0406, 1 } }, // d
+ { 0x6000, { 0x040d, 0x0407, 0 } }, // e
+ { 0x4001, { 2120, 0x0406, 1 } }, // f
+ { 0x6000, { 0x040f, 0x0407, 0 } }, // 10
+ { 0x4001, { -8050, 0x0406, 1 } }, // 11
+ { 0x6000, { 0x0411, 0x0408, 0 } }, // 12
+ { 0x4001, { 8050, 0x0406, 1 } }, // 13
+ { 0x6000, { 0x0413, 0x0408, 0 } }, // 14
+ { 0x4001, { 2750, 0x0406, 1 } }, // 15
+ { 0x6000, { 0x0415, 0x0408, 0 } }, // 16
+ { 0x4001, { 4020, 0x0406, 1 } }, // 17
+ { 0x6000, { 0x0417, 0x0407, 0 } }, // 18
+ { 0x4001, { 8050, 0x0406, 1 } }, // 19
+ { 0x6000, { 0x0419, 0x0407, 0 } }, // 1a
+ { 0x4001, { -5930, 0x0406, 1 } }, // 1b
+ { 0x6000, { 0x041b, 0x0408, 0 } }, // 1c
+ { 0x4001, { 5930, 0x0406, 1 } }, // 1d
+ { 0x6000, { 0x041d, 0x0408, 0 } } // 1e
+};
+const mso_CustomShape msoActionButtonSound =
+{
+ const_cast<SvxMSDffVertPair*>(mso_sptActionButtonSoundVert), SAL_N_ELEMENTS( mso_sptActionButtonSoundVert ),
+ const_cast<sal_uInt16*>(mso_sptActionButtonSoundSegm), sizeof( mso_sptActionButtonSoundSegm ) >> 1,
+ const_cast<SvxMSDffCalculationData*>(mso_sptActionButtonSoundCalc), SAL_N_ELEMENTS( mso_sptActionButtonSoundCalc ),
+ const_cast<sal_Int32*>(mso_sptDefault1400),
+ const_cast<SvxMSDffTextRectangles*>(mso_sptActionButtonTextRect), SAL_N_ELEMENTS( mso_sptActionButtonTextRect ),
+ 21600, 21600,
+ 10800, 10800,
+ nullptr, 0,
+ const_cast<SvxMSDffHandle*>(mso_sptButtonHandle), SAL_N_ELEMENTS( mso_sptButtonHandle )
+};
+
+const SvxMSDffVertPair mso_sptActionButtonMovieVert[] =
+{
+ { 0, 0 }, { 21600, 0 }, { 21600, 21600 }, { 0, 21600 },
+ { 0, 0 }, { 21600, 0 }, { 3 MSO_I, 2 MSO_I }, { 1 MSO_I, 2 MSO_I },
+ { 21600, 0 }, { 21600, 21600 }, { 3 MSO_I, 4 MSO_I }, { 3 MSO_I, 2 MSO_I },
+ { 21600, 21600 }, { 0, 21600 }, { 1 MSO_I, 4 MSO_I }, { 3 MSO_I, 4 MSO_I },
+ { 0, 21600 }, { 0, 0 }, { 1 MSO_I, 2 MSO_I }, { 1 MSO_I, 4 MSO_I },
+
+ { 0xa MSO_I, 0xc MSO_I }, { 0xe MSO_I, 0xc MSO_I }, { 0x10 MSO_I, 0x12 MSO_I }, { 0x14 MSO_I, 0x12 MSO_I },
+ { 0x16 MSO_I, 0x18 MSO_I }, { 0x16 MSO_I, 0x1a MSO_I }, { 0x1c MSO_I, 0x1a MSO_I }, { 0x1e MSO_I, 0x18 MSO_I },
+ { 0x20 MSO_I, 0x18 MSO_I }, { 0x20 MSO_I, 0x22 MSO_I }, { 0x1e MSO_I, 0x22 MSO_I }, { 0x1c MSO_I, 0x24 MSO_I },
+ { 0x16 MSO_I, 0x24 MSO_I }, { 0x16 MSO_I, 0x26 MSO_I }, { 0x2a MSO_I, 0x26 MSO_I }, { 0x2a MSO_I, 0x28 MSO_I },
+ { 0x10 MSO_I, 0x28 MSO_I }, { 0xe MSO_I, 0x2c MSO_I }, { 0xa MSO_I, 0x2c MSO_I }
+};
+const sal_uInt16 mso_sptActionButtonMovieSegm[] =
+{
+ 0x4000, 0x0003, 0x6001, 0x8000,
+ 0x4000, 0x0003, 0x6001, 0x8000,
+ 0x4000, 0x0003, 0x6001, 0x8000,
+ 0x4000, 0x0003, 0x6001, 0x8000,
+ 0x4000, 0x0003, 0x6001, 0x8000,
+ 0x4000, 0x0012, 0x6001, 0x8000
+};
+const SvxMSDffCalculationData mso_sptActionButtonMovieCalc[] = // adj value 0 - 5400
+{
+ { 0x2000, { DFF_Prop_adjustValue, 0, 0 } },
+ { 0x6000, { DFF_Prop_geoLeft, DFF_Prop_adjustValue, 0 } },
+ { 0x6000, { DFF_Prop_geoTop, DFF_Prop_adjustValue, 0 } },
+ { 0xa000, { DFF_Prop_geoRight, 0, DFF_Prop_adjustValue } },
+ { 0xa000, { DFF_Prop_geoBottom, 0, DFF_Prop_adjustValue } },
+ { 0x8000, { 10800, 0, DFF_Prop_adjustValue } },
+ { 0x2001, { 0x0405, 1, 10800 } }, // scaling 6
+ { 0x2001, { DFF_Prop_geoRight, 1, 2 } }, // lr center 7
+ { 0x2001, { DFF_Prop_geoBottom, 1, 2 } }, // ul center 8
+
+ { 0x4001, { -8050, 0x0406, 1 } }, // 9
+ { 0x6000, { 0x0409, 0x0407, 0 } }, // a
+ { 0x4001, { -4020, 0x0406, 1 } }, // b
+ { 0x6000, { 0x040b, 0x0408, 0 } }, // c
+ { 0x4001, { -7000, 0x0406, 1 } }, // d
+ { 0x6000, { 0x040d, 0x0407, 0 } }, // e
+ { 0x4001, { -6560, 0x0406, 1 } }, // f
+ { 0x6000, { 0x040f, 0x0407, 0 } }, // 10
+ { 0x4001, { -3600, 0x0406, 1 } }, // 11
+ { 0x6000, { 0x0411, 0x0408, 0 } }, // 12
+ { 0x4001, { 4020, 0x0406, 1 } }, // 13
+ { 0x6000, { 0x0413, 0x0407, 0 } }, // 14
+ { 0x4001, { 4660, 0x0406, 1 } }, // 15
+ { 0x6000, { 0x0415, 0x0407, 0 } }, // 16
+ { 0x4001, { -2960, 0x0406, 1 } }, // 17
+ { 0x6000, { 0x0417, 0x0408, 0 } }, // 18
+ { 0x4001, { -2330, 0x0406, 1 } }, // 19
+ { 0x6000, { 0x0419, 0x0408, 0 } }, // 1a
+ { 0x4001, { 6780, 0x0406, 1 } }, // 1b
+ { 0x6000, { 0x041b, 0x0407, 0 } }, // 1c
+ { 0x4001, { 7200, 0x0406, 1 } }, // 1d
+ { 0x6000, { 0x041d, 0x0407, 0 } }, // 1e
+ { 0x4001, { 8050, 0x0406, 1 } }, // 1f
+ { 0x6000, { 0x041f, 0x0407, 0 } }, // 20
+ { 0x4001, { 2960, 0x0406, 1 } }, // 21
+ { 0x6000, { 0x0421, 0x0408, 0 } }, // 22
+ { 0x4001, { 2330, 0x0406, 1 } }, // 23
+ { 0x6000, { 0x0423, 0x0408, 0 } }, // 24
+ { 0x4001, { 3800, 0x0406, 1 } }, // 25
+ { 0x6000, { 0x0425, 0x0408, 0 } }, // 26
+ { 0x4001, { -1060, 0x0406, 1 } }, // 27
+ { 0x6000, { 0x0427, 0x0408, 0 } }, // 28
+ { 0x4001, { -6350, 0x0406, 1 } }, // 29
+ { 0x6000, { 0x0429, 0x0407, 0 } }, // 2a
+ { 0x4001, { -640, 0x0406, 1 } }, // 2b
+ { 0x6000, { 0x042b, 0x0408, 0 } } // 2c
+};
+const mso_CustomShape msoActionButtonMovie =
+{
+ const_cast<SvxMSDffVertPair*>(mso_sptActionButtonMovieVert), SAL_N_ELEMENTS( mso_sptActionButtonMovieVert ),
+ const_cast<sal_uInt16*>(mso_sptActionButtonMovieSegm), sizeof( mso_sptActionButtonMovieSegm ) >> 1,
+ const_cast<SvxMSDffCalculationData*>(mso_sptActionButtonMovieCalc), SAL_N_ELEMENTS( mso_sptActionButtonMovieCalc ),
+ const_cast<sal_Int32*>(mso_sptDefault1400),
+ const_cast<SvxMSDffTextRectangles*>(mso_sptActionButtonTextRect), SAL_N_ELEMENTS( mso_sptActionButtonTextRect ),
+ 21600, 21600,
+ 10800, 10800,
+ nullptr, 0,
+ const_cast<SvxMSDffHandle*>(mso_sptButtonHandle), SAL_N_ELEMENTS( mso_sptButtonHandle )
+};
+
+const SvxMSDffVertPair mso_sptSmileyFaceVert[] = // adj value 15510 - 17520
+{
+ { 10800, 10800 }, { 10800, 10800 }, { 0, 360 },
+ { 7305, 7515 }, { 1000, 1865 }, { 0, 360 },
+ { 14295, 7515 }, { 1000, 1865 }, { 0, 360 },
+ { 4870, 1 MSO_I }, { 8680, 2 MSO_I }, { 12920, 2 MSO_I }, { 16730, 1 MSO_I }
+};
+const sal_uInt16 mso_sptSmileyFaceSegm[] =
+{
+ 0xa203, 0x6000, 0x8000,
+ 0xa203, 0x6000, 0x8000,
+ 0xa203, 0x6000, 0x8000,
+ 0x4000, 0x2001, 0xaa00, 0x8000
+};
+const SvxMSDffCalculationData mso_sptSmileyFaceCalc[] =
+{
+ { 0x2000, { DFF_Prop_adjustValue, 0, 14510 } },
+ { 0x8000, { 18520, 0, 0x400 } },
+ { 0x4000, { 14510, 0x400, 0 } }
+};
+
+const sal_Int32 mso_sptSmileyFaceDefault[] =
+{
+ 1, 18520
+};
+const SvxMSDffHandle mso_sptSmileyHandle[] =
+{
+ { SvxMSDffHandleFlags::RANGE,
+ 10800, 0x100, 10800, 10800, MIN_INT32, 0x7fffffff, 14510, 18520 }
+};
+const mso_CustomShape msoSmileyFace =
+{
+ const_cast<SvxMSDffVertPair*>(mso_sptSmileyFaceVert), SAL_N_ELEMENTS( mso_sptSmileyFaceVert ),
+ const_cast<sal_uInt16*>(mso_sptSmileyFaceSegm), sizeof( mso_sptSmileyFaceSegm ) >> 1,
+ const_cast<SvxMSDffCalculationData*>(mso_sptSmileyFaceCalc), SAL_N_ELEMENTS( mso_sptSmileyFaceCalc ),
+ const_cast<sal_Int32*>(mso_sptSmileyFaceDefault),
+ const_cast<SvxMSDffTextRectangles*>(mso_sptEllipseTextRect), SAL_N_ELEMENTS( mso_sptEllipseTextRect ),
+ 21600, 21600,
+ MIN_INT32, MIN_INT32,
+ const_cast<SvxMSDffVertPair*>(mso_sptEllipseGluePoints), SAL_N_ELEMENTS( mso_sptEllipseGluePoints ),
+ const_cast<SvxMSDffHandle*>(mso_sptSmileyHandle), SAL_N_ELEMENTS( mso_sptSmileyHandle ) // handles
+};
+
+const SvxMSDffVertPair mso_sptDonutVert[] = // adj value 0 - 10800
+{
+ { 10800, 10800 }, { 10800, 10800 }, { 0, 360 },
+ { 10800, 10800 }, { 1 MSO_I, 1 MSO_I }, { 0, 360 }
+};
+const sal_uInt16 mso_sptDonutSegm[] =
+{
+ 0xa203, 0x6000, 0xa203, 0x8000
+};
+const SvxMSDffCalculationData mso_sptDonutCalc[] =
+{
+ { 0x2000, { DFF_Prop_adjustValue, 0, 0 } },
+ { 0x8000, { 10800, 0, DFF_Prop_adjustValue } }
+};
+const SvxMSDffHandle mso_sptDonutHandle[] =
+{
+ { SvxMSDffHandleFlags::RANGE,
+ 0x100, 10800, 10800, 10800, 0, 10800, MIN_INT32, 0x7fffffff }
+};
+const mso_CustomShape msoDonut =
+{
+ const_cast<SvxMSDffVertPair*>(mso_sptDonutVert), SAL_N_ELEMENTS( mso_sptDonutVert ),
+ const_cast<sal_uInt16*>(mso_sptDonutSegm), sizeof( mso_sptDonutSegm ) >> 1,
+ const_cast<SvxMSDffCalculationData*>(mso_sptDonutCalc), SAL_N_ELEMENTS( mso_sptDonutCalc ),
+ const_cast<sal_Int32*>(mso_sptDefault5400),
+ const_cast<SvxMSDffTextRectangles*>(mso_sptEllipseTextRect), SAL_N_ELEMENTS( mso_sptEllipseTextRect ),
+ 21600, 21600,
+ MIN_INT32, MIN_INT32,
+ const_cast<SvxMSDffVertPair*>(mso_sptEllipseGluePoints), SAL_N_ELEMENTS( mso_sptEllipseGluePoints ),
+ const_cast<SvxMSDffHandle*>(mso_sptDonutHandle), SAL_N_ELEMENTS( mso_sptDonutHandle ) // handles
+};
+
+const SvxMSDffVertPair mso_sptNoSmokingVert[] = // adj value 0 - 7200
+{
+ { 10800, 10800 }, { 10800, 10800 }, { 0, 360 },
+ { 0 MSO_I, 0 MSO_I }, { 1 MSO_I, 1 MSO_I },
+ { 9 MSO_I, 0xa MSO_I }, { 0xb MSO_I, 0xc MSO_I }, { 0 MSO_I, 0 MSO_I }, { 1 MSO_I, 1 MSO_I },
+ { 0xd MSO_I, 0xe MSO_I }, { 0xf MSO_I, 0x10 MSO_I }
+};
+const sal_uInt16 mso_sptNoSmokingSegm[] =
+{
+ 0xa203, 0x6000, 0xa404, 0x6000, 0xa404, 0x6000, 0x8000
+};
+const SvxMSDffCalculationData mso_sptNoSmokingCalc[] =
+{
+ { 0x2000, { DFF_Prop_adjustValue, 0, 0 } }, // 0
+ { 0x8000, { 21600, 0, DFF_Prop_adjustValue } }, // 1
+ { 0x8000, { 10800, 0, DFF_Prop_adjustValue } }, // 2
+ { 0x2001, { DFF_Prop_adjustValue, 1, 2 } }, // 3
+ { 0xa080, { 0x403, 0, 0x402 } }, // 4
+ { 0x8000, { 10800, 0, 0x403 } }, // 5 x1
+ { 0x4000, { 10800, 0x403, 0 } }, // 6 x2
+ { 0x8000, { 10800, 0, 0x404 } }, // 7 y1
+ { 0x4000, { 10800, 0x404, 0 } }, // 8 y2
+ { 0x6081, { 0x405, 0x407, 45 } }, // 9
+ { 0x6082, { 0x405, 0x407, 45 } }, // a
+ { 0x6081, { 0x405, 0x408, 45 } }, // b
+ { 0x6082, { 0x405, 0x408, 45 } }, // c
+ { 0x6081, { 0x406, 0x408, 45 } }, // d
+ { 0x6082, { 0x406, 0x408, 45 } }, // e
+ { 0x6081, { 0x406, 0x407, 45 } }, // f
+ { 0x6082, { 0x406, 0x407, 45 } } // 10
+};
+const SvxMSDffHandle mso_sptNoSmokingHandle[] =
+{
+ { SvxMSDffHandleFlags::RANGE,
+ 0x100, 10800, 10800, 10800, 0, 7200, MIN_INT32, 0x7fffffff }
+};
+const mso_CustomShape msoNoSmoking =
+{
+ const_cast<SvxMSDffVertPair*>(mso_sptNoSmokingVert), SAL_N_ELEMENTS( mso_sptNoSmokingVert ),
+ const_cast<sal_uInt16*>(mso_sptNoSmokingSegm), sizeof( mso_sptNoSmokingSegm ) >> 1,
+ const_cast<SvxMSDffCalculationData*>(mso_sptNoSmokingCalc), SAL_N_ELEMENTS( mso_sptNoSmokingCalc ),
+ const_cast<sal_Int32*>(mso_sptDefault2700),
+ const_cast<SvxMSDffTextRectangles*>(mso_sptEllipseTextRect), SAL_N_ELEMENTS( mso_sptEllipseTextRect ),
+ 21600, 21600,
+ MIN_INT32, MIN_INT32,
+ const_cast<SvxMSDffVertPair*>(mso_sptEllipseGluePoints), SAL_N_ELEMENTS( mso_sptEllipseGluePoints ),
+ const_cast<SvxMSDffHandle*>(mso_sptNoSmokingHandle), SAL_N_ELEMENTS( mso_sptNoSmokingHandle ) // handles
+};
+
+const SvxMSDffVertPair mso_sptBlockArcVert[] = // adj value 0 (degrees)
+{ // adj value 1: 0 -> 10800;
+ { 0, 0 }, { 21600, 21600 }, { 4 MSO_I, 3 MSO_I }, { 2 MSO_I, 3 MSO_I },
+ { 5 MSO_I, 5 MSO_I }, { 6 MSO_I, 6 MSO_I }, { 2 MSO_I, 3 MSO_I }, { 4 MSO_I, 3 MSO_I }
+};
+const sal_uInt16 mso_sptBlockArcSegm[] =
+{
+ 0xA404, 0xa504, 0x6001, 0x8000
+};
+const sal_Int32 mso_sptBlockArcDefault[] =
+{
+ 2, 180, 5400
+};
+const SvxMSDffCalculationData mso_sptBlockArcCalc[] =
+{
+ { 0x400a, { 10800, DFF_Prop_adjustValue, 0 } },
+ { 0x4009, { 10800, DFF_Prop_adjustValue, 0 } },
+ { 0x2000, { 0x400, 10800, 0 } },
+ { 0x2000, { 0x401, 10800, 0 } },
+ { 0x8000, { 21600, 0, 0x402 } },
+ { 0x8000, { 10800, 0, DFF_Prop_adjust2Value } },
+ { 0x4000, { 10800, DFF_Prop_adjust2Value, 0 } },
+ { 0x600a, { 0x405, DFF_Prop_adjustValue, 0 } },
+ { 0x6009, { 0x405, DFF_Prop_adjustValue, 0 } }
+};
+const SvxMSDffHandle mso_sptBlockArcHandle[] =
+{
+ { SvxMSDffHandleFlags::POLAR | SvxMSDffHandleFlags::RADIUS_RANGE,
+ 0x101, 0x100, 10800, 10800, 0, 10800, MIN_INT32, 0x7fffffff }
+};
+const mso_CustomShape msoBlockArc =
+{
+ const_cast<SvxMSDffVertPair*>(mso_sptBlockArcVert), SAL_N_ELEMENTS( mso_sptBlockArcVert ),
+ const_cast<sal_uInt16*>(mso_sptBlockArcSegm), sizeof( mso_sptBlockArcSegm ) >> 1,
+ const_cast<SvxMSDffCalculationData*>(mso_sptBlockArcCalc), SAL_N_ELEMENTS( mso_sptBlockArcCalc ),
+ const_cast<sal_Int32*>(mso_sptBlockArcDefault),
+ nullptr, 0,
+ 21600, 21600,
+ MIN_INT32, MIN_INT32,
+ nullptr, 0,
+ const_cast<SvxMSDffHandle*>(mso_sptBlockArcHandle), SAL_N_ELEMENTS( mso_sptBlockArcHandle ) // handles
+};
+
+// aware : control points are always part of the bounding box
+const SvxMSDffVertPair mso_sptHeartVert[] =
+{
+ { 10800, 21599 }, { 321, 6886 }, { 70, 6036 }, // ppp
+ { -9, 5766 }, { -1, 5474 }, { 2, 5192 }, // ccp
+ { 6, 4918 }, { 43, 4641 }, { 101, 4370 }, // ccp
+ { 159, 4103 }, { 245, 3837 }, { 353, 3582 }, // ccp
+ { 460, 3326 }, { 591, 3077 }, { 741, 2839 }, // ccp
+ { 892, 2598 }, { 1066, 2369 }, { 1253, 2155 }, // ccp
+ { 1443, 1938 }, { 1651, 1732 }, { 1874, 1543 }, // ccp
+ { 2097, 1351 }, { 2337, 1174 }, { 2587, 1014 }, // ccp
+ { 2839, 854 }, { 3106, 708 }, { 3380, 584 }, // ccp
+ { 3656, 459 }, { 3945, 350 }, { 4237, 264 }, // ccp
+ { 4533, 176 }, { 4838, 108 }, { 5144, 66 }, // ccp
+ { 5454, 22 }, { 5771, 1 }, { 6086, 3 }, // ccp
+ { 6407, 7 }, { 6731, 35 }, { 7048, 89 }, // ccp
+ { 7374, 144 }, { 7700, 226 }, { 8015, 335 }, // ccp
+ { 8344, 447 }, { 8667, 590 }, { 8972, 756 }, // ccp
+ { 9297, 932 }, { 9613, 1135 }, { 9907, 1363 }, // ccp
+ { 10224, 1609 }, { 10504, 1900 }, { 10802, 2169 }, // ccp
+ { 11697, 1363 }, // p
+ { 11971, 1116 }, { 12304, 934 }, { 12630, 756 }, // ccp
+ { 12935, 590 }, { 13528, 450 }, { 13589, 335 }, // ccp
+ { 13901, 226 }, { 14227, 144 }, { 14556, 89 }, // ccp
+ { 14872, 35 }, { 15195, 7 }, { 15517, 3 }, // ccp
+ { 15830, 0 }, { 16147, 22 }, { 16458, 66 }, // ccp
+ { 16764, 109 }, { 17068, 177 }, { 17365, 264 }, // ccp
+ { 17658, 349 }, { 17946, 458 }, { 18222, 584 }, // ccp
+ { 18496, 708 }, { 18762, 854 }, { 19015, 1014 }, // ccp
+ { 19264, 1172 }, { 19504, 1349 }, { 19730, 1543 }, // ccp
+ { 19950, 1731 }, { 20158, 1937 }, { 20350, 2155 }, // ccp
+ { 20536, 2369 }, { 20710, 2598 }, { 20861, 2839 }, // ccp
+ { 21010, 3074 }, { 21143, 3323 }, { 21251, 3582 }, // ccp
+ { 21357, 3835 }, { 21443, 4099 }, { 21502, 4370 }, // ccp
+ { 21561, 4639 }, { 21595, 4916 }, { 21600, 5192 }, // ccp
+ { 21606, 5474 }, { 21584, 5760 }, { 21532, 6036 }, // ccp
+ { 21478, 6326 }, { 21366, 6603 }, { 21282, 6887 }, // ccp
+ { 10802, 21602 } // p
+};
+const sal_uInt16 mso_sptHeartSegm[] =
+{
+ 0x4000, 0x0002, 0x2010, 0x0001, 0x2010, 0x0001, 0x6001, 0x8000
+};
+const SvxMSDffTextRectangles mso_sptHeartTextRect[] =
+{
+ { { 5080, 2540 }, { 16520, 13550 } }
+};
+const SvxMSDffVertPair mso_sptHeartGluePoints[] =
+{
+ { 10800, 2180 }, { 3090, 10800 }, { 10800, 21600 }, { 18490, 10800 }
+};
+const mso_CustomShape msoHeart =
+{
+ const_cast<SvxMSDffVertPair*>(mso_sptHeartVert), SAL_N_ELEMENTS( mso_sptHeartVert ),
+ const_cast<sal_uInt16*>(mso_sptHeartSegm), sizeof( mso_sptHeartSegm ) >> 1,
+ nullptr, 0,
+ nullptr,
+ const_cast<SvxMSDffTextRectangles*>(mso_sptHeartTextRect), SAL_N_ELEMENTS( mso_sptHeartTextRect ),
+ 21615, 21602,
+ MIN_INT32, MIN_INT32,
+ const_cast<SvxMSDffVertPair*>(mso_sptHeartGluePoints), SAL_N_ELEMENTS( mso_sptHeartGluePoints ),
+ nullptr, 0 // handles
+};
+
+const SvxMSDffVertPair mso_sptLightningBoldVert[] =
+{
+ { 8458, 0 }, { 0, 3923 }, { 7564, 8416 }, { 4993, 9720 },
+ { 12197, 13904 }, { 9987, 14934 }, { 21600, 21600 }, { 14768, 12911 },
+ { 16558, 12016 }, { 11030, 6840 }, { 12831, 6120 }, { 8458, 0 }
+};
+const SvxMSDffTextRectangles mso_sptLightningBoldTextRect[] =
+{
+ { { 8680, 7410 }, { 13970, 14190 } }
+};
+const SvxMSDffVertPair mso_sptLightningBoldGluePoints[] =
+{
+ { 8458, 0 }, { 0, 3923 }, { 4993, 9720 }, { 9987, 14934 }, { 21600, 21600 },
+ { 16558, 12016 }, { 12831, 6120 }
+};
+const mso_CustomShape msoLightningBold =
+{
+ const_cast<SvxMSDffVertPair*>(mso_sptLightningBoldVert), SAL_N_ELEMENTS( mso_sptLightningBoldVert ),
+ nullptr, 0,
+ nullptr, 0,
+ nullptr,
+ const_cast<SvxMSDffTextRectangles*>(mso_sptLightningBoldTextRect), SAL_N_ELEMENTS( mso_sptLightningBoldTextRect ),
+ 21600, 21600,
+ MIN_INT32, MIN_INT32,
+ const_cast<SvxMSDffVertPair*>(mso_sptLightningBoldGluePoints), SAL_N_ELEMENTS( mso_sptLightningBoldGluePoints ),
+ nullptr, 0 // handles
+};
+
+const SvxMSDffVertPair mso_sptSunVert[] = // adj value 2700 -> 10125
+{
+ { 0, 10800 }, { 4 MSO_I, 8 MSO_I }, { 4 MSO_I, 9 MSO_I },
+ { 0x0a MSO_I, 0x0b MSO_I }, { 0x0c MSO_I, 0x0d MSO_I }, { 0x0e MSO_I, 0x0f MSO_I },
+ { 0x10 MSO_I, 0x11 MSO_I }, { 0x12 MSO_I, 0x13 MSO_I }, { 0x14 MSO_I, 0x15 MSO_I },
+ { 0x16 MSO_I, 0x17 MSO_I }, { 0x18 MSO_I, 0x19 MSO_I }, { 0x1a MSO_I, 0x1b MSO_I },
+ { 0x1c MSO_I, 0x1d MSO_I }, { 0x1e MSO_I, 0x1f MSO_I }, { 0x20 MSO_I, 0x21 MSO_I },
+ { 0x22 MSO_I, 0x23 MSO_I }, { 0x24 MSO_I, 0x25 MSO_I }, { 0x26 MSO_I, 0x27 MSO_I },
+ { 0x28 MSO_I, 0x29 MSO_I }, { 0x2a MSO_I, 0x2b MSO_I }, { 0x2c MSO_I, 0x2d MSO_I },
+ { 0x2e MSO_I, 0x2f MSO_I }, { 0x30 MSO_I, 0x31 MSO_I }, { 0x32 MSO_I, 0x33 MSO_I },
+ { 10800, 10800 }, { 0x36 MSO_I, 0x36 MSO_I }, { 0, 360 }
+};
+const sal_uInt16 mso_sptSunSegm[] =
+{
+ 0x4000, 0x0002, 0x6001, 0x8000,
+ 0x4000, 0x0002, 0x6001, 0x8000,
+ 0x4000, 0x0002, 0x6001, 0x8000,
+ 0x4000, 0x0002, 0x6001, 0x8000,
+ 0x4000, 0x0002, 0x6001, 0x8000,
+ 0x4000, 0x0002, 0x6001, 0x8000,
+ 0x4000, 0x0002, 0x6001, 0x8000,
+ 0x4000, 0x0002, 0x6001, 0x8000,
+ 0xa203, 0x6000, 0x8000
+};
+const SvxMSDffCalculationData mso_sptSunCalc[] =
+{
+ { 0x2000, { DFF_Prop_adjustValue, 0, 0 } },
+ { 0x8000, { 21600, 0, DFF_Prop_adjustValue } },
+ { 0x2000, { DFF_Prop_adjustValue, 0, 2700 } },
+ { 0x2001, { 0x402, 5080, 7425 } },
+ { 0x2000, { 0x403, 2540, 0 } },
+ { 0x8000, { 10125, 0, DFF_Prop_adjustValue } },
+ { 0x2001, { 0x405, 2120, 7425 } },
+ { 0x2000, { 0x406, 210, 0 } },
+ { 0x4000, { 10800, 0x407, 0 } }, // y1 (0x8)
+ { 0x8000, { 10800, 0, 0x407 } }, // y2 (0x9)
+ { 0x0081, { 0, 10800, 45 } }, // 0xa
+ { 0x0082, { 0, 10800, 45 } }, // 0xb
+ { 0x6081, { 0x404, 0x408, 45 } }, // 0xc
+ { 0x6082, { 0x404, 0x408, 45 } }, // 0xd
+ { 0x6081, { 0x404, 0x409, 45 } }, // 0xe
+ { 0x6082, { 0x404, 0x409, 45 } }, // 0xf
+ { 0x0081, { 0, 10800, 90 } }, // 0x10
+ { 0x0082, { 0, 10800, 90 } }, // 0x11
+ { 0x6081, { 0x404, 0x408, 90 } }, // 0x12
+ { 0x6082, { 0x404, 0x408, 90 } }, // 0x13
+ { 0x6081, { 0x404, 0x409, 90 } }, // 0x14
+ { 0x6082, { 0x404, 0x409, 90 } }, // 0x15
+ { 0x0081, { 0, 10800, 135 } }, // 0x16
+ { 0x0082, { 0, 10800, 135 } }, // 0x17
+ { 0x6081, { 0x404, 0x408, 135 } }, // 0x18
+ { 0x6082, { 0x404, 0x408, 135 } }, // 0x19
+ { 0x6081, { 0x404, 0x409, 135 } }, // 0x1a
+ { 0x6082, { 0x404, 0x409, 135 } }, // 0x1b
+ { 0x0081, { 0, 10800, 180 } }, // 0x1c
+ { 0x0082, { 0, 10800, 180 } }, // 0x1d
+ { 0x6081, { 0x404, 0x408, 180 } }, // 0x1e
+ { 0x6082, { 0x404, 0x408, 180 } }, // 0x1f
+ { 0x6081, { 0x404, 0x409, 180 } }, // 0x20
+ { 0x6082, { 0x404, 0x409, 180 } }, // 0x21
+ { 0x0081, { 0, 10800, 225 } }, // 0x22
+ { 0x0082, { 0, 10800, 225 } }, // 0x23
+ { 0x6081, { 0x404, 0x408, 225 } }, // 0x24
+ { 0x6082, { 0x404, 0x408, 225 } }, // 0x25
+ { 0x6081, { 0x404, 0x409, 225 } }, // 0x26
+ { 0x6082, { 0x404, 0x409, 225 } }, // 0x27
+ { 0x0081, { 0, 10800, 270 } }, // 0x28
+ { 0x0082, { 0, 10800, 270 } }, // 0x29
+ { 0x6081, { 0x404, 0x408, 270 } }, // 0x2a
+ { 0x6082, { 0x404, 0x408, 270 } }, // 0x2b
+ { 0x6081, { 0x404, 0x409, 270 } }, // 0x2c
+ { 0x6082, { 0x404, 0x409, 270 } }, // 0x2d
+ { 0x0081, { 0, 10800, 315 } }, // 0x2e
+ { 0x0082, { 0, 10800, 315 } }, // 0x2f
+ { 0x6081, { 0x404, 0x408, 315 } }, // 0x30
+ { 0x6082, { 0x404, 0x408, 315 } }, // 0x31
+ { 0x6081, { 0x404, 0x409, 315 } }, // 0x32
+ { 0x6082, { 0x404, 0x409, 315 } }, // 0x33
+ { 0x2081, { DFF_Prop_adjustValue, 10800, 45 } }, // 0x34 ( textbox )
+ { 0x2081, { DFF_Prop_adjustValue, 10800, 225 } }, // 0x35
+ { 0x8000, { 10800, 0, DFF_Prop_adjustValue } }
+};
+const SvxMSDffTextRectangles mso_sptSunTextRect[] =
+{
+ { { 0x34 MSO_I, 0x34 MSO_I }, { 0x35 MSO_I, 0x35 MSO_I } }
+};
+const SvxMSDffHandle mso_sptSunHandle[] =
+{
+ { SvxMSDffHandleFlags::RANGE,
+ 0x100, 10800, 10800, 10800, 2700, 10125, MIN_INT32, 0x7fffffff }
+};
+const mso_CustomShape msoSun =
+{
+ const_cast<SvxMSDffVertPair*>(mso_sptSunVert), SAL_N_ELEMENTS( mso_sptSunVert ),
+ const_cast<sal_uInt16*>(mso_sptSunSegm), sizeof( mso_sptSunSegm ) >> 1,
+ const_cast<SvxMSDffCalculationData*>(mso_sptSunCalc), SAL_N_ELEMENTS( mso_sptSunCalc ),
+ const_cast<sal_Int32*>(mso_sptDefault5400),
+ const_cast<SvxMSDffTextRectangles*>(mso_sptSunTextRect), SAL_N_ELEMENTS( mso_sptSunTextRect ),
+ 21600, 21600,
+ MIN_INT32, MIN_INT32,
+ const_cast<SvxMSDffVertPair*>(mso_sptStandardGluePoints), SAL_N_ELEMENTS( mso_sptStandardGluePoints ),
+ const_cast<SvxMSDffHandle*>(mso_sptSunHandle), SAL_N_ELEMENTS( mso_sptSunHandle ) // handles
+};
+
+const SvxMSDffVertPair mso_sptMoonVert[] = // adj value 0 -> 18900
+{
+ { 21600, 0 },
+ { 3 MSO_I, 4 MSO_I }, { 0 MSO_I, 5080 }, { 0 MSO_I, 10800 }, // ccp
+ { 0 MSO_I, 16520 }, { 3 MSO_I, 5 MSO_I }, { 21600, 21600 }, // ccp
+ { 9740, 21600 }, { 0, 16730 }, { 0, 10800 }, // ccp
+ { 0, 4870 }, { 9740, 0 }, { 21600, 0 } // ccp
+};
+const sal_uInt16 mso_sptMoonSegm[] =
+{
+ 0x4000, 0x2004, 0x6000, 0x8000
+};
+const SvxMSDffCalculationData mso_sptMoonCalc[] =
+{
+ { 0x2000, { DFF_Prop_adjustValue, 0, 0 } },
+ { 0x8000, { 21600, 0, DFF_Prop_adjustValue } },
+ { 0x2001, { 0x401, 1, 2 } },
+ { 0x6000, { 0x402, DFF_Prop_adjustValue, 0 } },
+ { 0x2001, { DFF_Prop_adjustValue, 1794, 10000 } },
+ { 0x8000, { 21600, 0, 0x0404 } },
+ { 0x2001, { DFF_Prop_adjustValue, 400, 18900 } },
+ { 0x8081, { 0, 10800, 0x406 } },
+ { 0x8082, { 0, 10800, 0x406 } },
+ { 0x6000, { 0x407, 0x407, 0 } },
+ { 0x8000, { 21600, 0, 0x408 } }
+};
+const SvxMSDffTextRectangles mso_sptMoonTextRect[] =
+{
+ { { 9 MSO_I, 8 MSO_I }, { 0 MSO_I, 0xa MSO_I } }
+};
+const SvxMSDffVertPair mso_sptMoonGluePoints[] =
+{
+ { 21600, 0 }, { 0, 10800 }, { 21600, 21600 }, { 0 MSO_I, 10800 }
+};
+const SvxMSDffHandle mso_sptMoonHandle[] =
+{
+ { SvxMSDffHandleFlags::RANGE,
+ 0x100, 10800, 10800, 10800, 0, 18900, MIN_INT32, 0x7fffffff }
+};
+const mso_CustomShape msoMoon =
+{
+ const_cast<SvxMSDffVertPair*>(mso_sptMoonVert), SAL_N_ELEMENTS( mso_sptMoonVert ),
+ const_cast<sal_uInt16*>(mso_sptMoonSegm), sizeof( mso_sptMoonSegm ) >> 1,
+ const_cast<SvxMSDffCalculationData*>(mso_sptMoonCalc), SAL_N_ELEMENTS( mso_sptMoonCalc ),
+ const_cast<sal_Int32*>(mso_sptDefault10800),
+ const_cast<SvxMSDffTextRectangles*>(mso_sptMoonTextRect), SAL_N_ELEMENTS( mso_sptMoonTextRect ),
+ 21600, 21600,
+ MIN_INT32, MIN_INT32,
+ const_cast<SvxMSDffVertPair*>(mso_sptMoonGluePoints), SAL_N_ELEMENTS( mso_sptMoonGluePoints ),
+ const_cast<SvxMSDffHandle*>(mso_sptMoonHandle), SAL_N_ELEMENTS( mso_sptMoonHandle ) // handles
+};
+
+const SvxMSDffVertPair mso_sptBracketPairVert[] = // adj value 0 -> 10800
+{
+ { 0 MSO_I, 0 }, { 0, 1 MSO_I }, // left top alignment
+ { 0, 2 MSO_I }, { 0 MSO_I, 21600 }, // left bottom "
+ { 3 MSO_I, 21600 }, { 21600, 2 MSO_I }, // right bottom "
+ { 21600, 1 MSO_I }, { 3 MSO_I, 0 }, // right top "
+ { 0 MSO_I, 0 }, { 0, 1 MSO_I }, // filling area
+ { 0, 2 MSO_I }, { 0 MSO_I, 21600 },
+ { 3 MSO_I, 21600 }, { 21600, 2 MSO_I },
+ { 21600, 1 MSO_I }, { 3 MSO_I, 0 }
+};
+const sal_uInt16 mso_sptBracketPairSegm[] =
+{
+ 0x4000, 0xa701, 0x0001, 0xa801, 0xaa00, 0x8000,
+ 0x4000, 0xa701, 0x0001, 0xa801, 0xaa00, 0x8000,
+ 0x4000, 0xa701, 0x0001, 0xa801, 0x0001, // filling area
+ 0xa701, 0x0001, 0xa801, 0x6000, 0xab00, 0x8000
+};
+const SvxMSDffCalculationData mso_sptBracketPairCalc[] =
+{
+ { 0x6000, { DFF_Prop_geoLeft, DFF_Prop_adjustValue, 0 } },
+ { 0x6000, { DFF_Prop_geoTop, DFF_Prop_adjustValue, 0 } },
+ { 0xa000, { DFF_Prop_geoBottom, 0, DFF_Prop_adjustValue } },
+ { 0xa000, { DFF_Prop_geoRight, 0, DFF_Prop_adjustValue } },
+ { 0x2082, { DFF_Prop_adjustValue, 0, 45 } },
+ { 0x2000, { 0x404, 0, 10800 } },
+ { 0x8000, { 0, 0, DFF_Prop_adjustValue } },
+ { 0xa000, { 0x406, 0, 0x405 } },
+ { 0xa000, { DFF_Prop_geoLeft, 0, 0x407 } },
+ { 0xa000, { DFF_Prop_geoTop, 0, 0x407 } },
+ { 0x6000, { DFF_Prop_geoRight, 0x407, 0 } },
+ { 0x6000, { DFF_Prop_geoBottom, 0x407, 0 } },
+ { 0xa000, { DFF_Prop_geoLeft, 0, 0x405 } },
+ { 0xa000, { DFF_Prop_geoTop, 0, 0x405 } },
+ { 0x6000, { DFF_Prop_geoRight, 0x405, 0 } },
+ { 0x6000, { DFF_Prop_geoBottom, 0x405, 0 } }
+};
+const SvxMSDffTextRectangles mso_sptBracketPairTextRect[] =
+{
+ { { 8 MSO_I, 9 MSO_I }, { 0xa MSO_I, 0xb MSO_I } }
+};
+const SvxMSDffHandle mso_sptBracketPairHandle[] =
+{
+ { SvxMSDffHandleFlags::RANGE | SvxMSDffHandleFlags::SWITCHED,
+ 0x100, 0, 10800, 10800, 0, 10800, MIN_INT32, 0x7fffffff }
+};
+const mso_CustomShape msoBracketPair =
+{
+ const_cast<SvxMSDffVertPair*>(mso_sptBracketPairVert), SAL_N_ELEMENTS( mso_sptBracketPairVert ),
+ const_cast<sal_uInt16*>(mso_sptBracketPairSegm), sizeof( mso_sptBracketPairSegm ) >> 1,
+ const_cast<SvxMSDffCalculationData*>(mso_sptBracketPairCalc), SAL_N_ELEMENTS( mso_sptBracketPairCalc ),
+ const_cast<sal_Int32*>(mso_sptDefault3700),
+ const_cast<SvxMSDffTextRectangles*>(mso_sptBracketPairTextRect), SAL_N_ELEMENTS( mso_sptBracketPairTextRect ),
+ 21600, 21600,
+ 10800, MIN_INT32,
+ const_cast<SvxMSDffVertPair*>(mso_sptStandardGluePoints), SAL_N_ELEMENTS( mso_sptStandardGluePoints ),
+ const_cast<SvxMSDffHandle*>(mso_sptBracketPairHandle), SAL_N_ELEMENTS( mso_sptBracketPairHandle ) // handles
+};
+
+const sal_uInt16 mso_sptPlaqueSegm[] =
+{
+ 0x4000, 0xa801, 0x0001, 0xa701, 0x0001, 0xa801, 0x0001, 0xa701, 0x6000, 0x8000
+};
+const SvxMSDffTextRectangles mso_sptPlaqueTextRect[] =
+{
+ { { 0xc MSO_I, 0xd MSO_I }, { 0xe MSO_I, 0xf MSO_I } }
+};
+const SvxMSDffHandle mso_sptPlaqueHandle[] =
+{
+ { SvxMSDffHandleFlags::RANGE | SvxMSDffHandleFlags::SWITCHED,
+ 0x100, 0, 10800, 10800, 0, 10800, MIN_INT32, 0x7fffffff }
+};
+const mso_CustomShape msoPlaque =
+{
+ const_cast<SvxMSDffVertPair*>(mso_sptBracketPairVert), SAL_N_ELEMENTS( mso_sptBracketPairVert ),
+ const_cast<sal_uInt16*>(mso_sptPlaqueSegm), sizeof( mso_sptPlaqueSegm ) >> 1,
+ const_cast<SvxMSDffCalculationData*>(mso_sptBracketPairCalc), SAL_N_ELEMENTS( mso_sptBracketPairCalc ),
+ const_cast<sal_Int32*>(mso_sptDefault3600),
+ const_cast<SvxMSDffTextRectangles*>(mso_sptPlaqueTextRect), SAL_N_ELEMENTS( mso_sptPlaqueTextRect ),
+ 21600, 21600,
+ 10800, 10800,
+ const_cast<SvxMSDffVertPair*>(mso_sptStandardGluePoints), SAL_N_ELEMENTS( mso_sptStandardGluePoints ),
+ const_cast<SvxMSDffHandle*>(mso_sptPlaqueHandle), SAL_N_ELEMENTS( mso_sptPlaqueHandle ) // handles
+};
+
+const SvxMSDffVertPair mso_sptBracePairVert[] = // adj value 0 -> 5400
+{
+ { 4 MSO_I, 0 }, { 0 MSO_I, 1 MSO_I }, { 0 MSO_I, 6 MSO_I }, { 0 ,10800 }, // left bracket
+ { 0 MSO_I, 7 MSO_I }, { 0 MSO_I, 2 MSO_I }, { 4 MSO_I, 21600 },
+ { 8 MSO_I, 21600 }, { 3 MSO_I, 2 MSO_I }, { 3 MSO_I, 7 MSO_I }, { 21600, 10800 }, // right bracket
+ { 3 MSO_I, 6 MSO_I }, { 3 MSO_I, 1 MSO_I }, { 8 MSO_I, 0 },
+ { 4 MSO_I, 0 }, { 0 MSO_I, 1 MSO_I }, { 0 MSO_I, 6 MSO_I }, { 0 ,10800 }, // filling area
+ { 0 MSO_I, 7 MSO_I }, { 0 MSO_I, 2 MSO_I }, { 4 MSO_I, 21600 },
+ { 8 MSO_I, 21600 }, { 3 MSO_I, 2 MSO_I }, { 3 MSO_I, 7 MSO_I }, { 21600, 10800 },
+ { 3 MSO_I, 6 MSO_I }, { 3 MSO_I, 1 MSO_I }, { 8 MSO_I, 0 }
+};
+const sal_uInt16 mso_sptBracePairSegm[] =
+{
+ 0x4000, 0xa701, 0x0001, 0xa801, 0xa701, 0x0001, 0xa801, 0xaa00, 0x8000,
+ 0x4000, 0xa701, 0x0001, 0xa801, 0xa701, 0x0001, 0xa801, 0xaa00, 0x8000,
+ 0x4000, 0xa701, 0x0001, 0xa801, 0xa701, 0x0001, 0xa801, 0x0001, // filling area
+ 0xa701, 0x0001, 0xa801, 0xa701, 0x0001, 0xa801, 0x6000, 0xab00, 0x8000
+};
+const SvxMSDffCalculationData mso_sptBracePairCalc[] =
+{
+ { 0x6000, { DFF_Prop_geoLeft, DFF_Prop_adjustValue, 0 } },
+ { 0x6000, { DFF_Prop_geoTop, DFF_Prop_adjustValue, 0 } },
+ { 0xa000, { DFF_Prop_geoBottom, 0, DFF_Prop_adjustValue } },
+ { 0xa000, { DFF_Prop_geoRight, 0, DFF_Prop_adjustValue } },
+ { 0x2001, { 0x400, 2, 1 } }, // 4
+ { 0x2001, { DFF_Prop_adjustValue, 2, 1 } }, // 5
+ { 0x8000, { 10800, 0, DFF_Prop_adjustValue } }, // 6
+ { 0x8000, { 21600, 0, 0x406 } }, // 7
+ { 0xa000, { DFF_Prop_geoRight, 0, 0x405 } }, // 8
+ { 0x2001, { DFF_Prop_adjustValue, 1, 3 } }, // 9
+ { 0x6000, { 0x409, DFF_Prop_adjustValue, 0 } }, // xa
+ { 0x6000, { DFF_Prop_geoLeft, 0x40a, 0 } }, // xb
+ { 0x6000, { DFF_Prop_geoTop, 0x409, 0 } }, // xc
+ { 0xa000, { DFF_Prop_geoRight, 0, 0x40a } }, // xd
+ { 0xa000, { DFF_Prop_geoBottom, 0, 0x409 } } // xe
+};
+const SvxMSDffTextRectangles mso_sptBracePairTextRect[] =
+{
+ { { 0xb MSO_I, 0xc MSO_I }, { 0xd MSO_I, 0xe MSO_I } }
+};
+const SvxMSDffHandle mso_sptBracePairHandle[] =
+{
+ { SvxMSDffHandleFlags::RANGE | SvxMSDffHandleFlags::SWITCHED,
+ 0, 0x100, 10800, 10800, MIN_INT32, 0x7fffffff, 0, 5400 }
+};
+const mso_CustomShape msoBracePair =
+{
+ const_cast<SvxMSDffVertPair*>(mso_sptBracePairVert), SAL_N_ELEMENTS( mso_sptBracePairVert ),
+ const_cast<sal_uInt16*>(mso_sptBracePairSegm), sizeof( mso_sptBracePairSegm ) >> 1,
+ const_cast<SvxMSDffCalculationData*>(mso_sptBracePairCalc), SAL_N_ELEMENTS( mso_sptBracePairCalc ),
+ const_cast<sal_Int32*>(mso_sptDefault1800),
+ const_cast<SvxMSDffTextRectangles*>(mso_sptBracePairTextRect), SAL_N_ELEMENTS( mso_sptBracePairTextRect ),
+ 21600, 21600,
+ 10800, MIN_INT32,
+ const_cast<SvxMSDffVertPair*>(mso_sptStandardGluePoints), SAL_N_ELEMENTS( mso_sptStandardGluePoints ),
+ const_cast<SvxMSDffHandle*>(mso_sptBracePairHandle), SAL_N_ELEMENTS( mso_sptBracePairHandle ) // handles
+};
+
+const SvxMSDffCalculationData mso_sptBracketCalc[] =
+{
+ { 0x2001, { DFF_Prop_adjustValue, 1, 2 } },
+ { 0x6000, { DFF_Prop_geoTop, DFF_Prop_adjustValue, 0 } },
+ { 0xa000, { DFF_Prop_geoBottom, 0, DFF_Prop_adjustValue } },
+ { 0x6000, { DFF_Prop_geoTop, 0x400, 0 } },
+ { 0xa000, { DFF_Prop_geoBottom, 0, 0x400 } }
+};
+const sal_uInt16 mso_sptBracketSegm[] =
+{
+ 0x4000, 0x2001, 0x0001, 0x2001, 0x8000
+};
+const SvxMSDffVertPair mso_sptLeftBracketVert[] = // adj value 0 -> 10800
+{
+ { 21600, 0 }, { 10800, 0 }, { 0, 3 MSO_I }, { 0, 1 MSO_I },
+ { 0, 2 MSO_I }, { 0, 4 MSO_I }, { 10800, 21600 }, { 21600, 21600 }
+};
+const SvxMSDffTextRectangles mso_sptLeftBracketTextRect[] =
+{
+ { { 6350, 3 MSO_I }, { 21600, 4 MSO_I } }
+};
+const SvxMSDffVertPair mso_sptLeftBracketGluePoints[] =
+{
+ { 21600, 0 }, { 0, 10800 }, { 21600, 21600 }
+};
+const SvxMSDffHandle mso_sptLeftBracketHandle[] =
+{
+ { SvxMSDffHandleFlags::RANGE,
+ 0, 0x100, 10800, 10800, MIN_INT32, 0x7fffffff, 0, 10800 }
+};
+const mso_CustomShape msoLeftBracket =
+{
+ const_cast<SvxMSDffVertPair*>(mso_sptLeftBracketVert), SAL_N_ELEMENTS( mso_sptLeftBracketVert ),
+ const_cast<sal_uInt16*>(mso_sptBracketSegm), sizeof( mso_sptBracketSegm ) >> 1,
+ const_cast<SvxMSDffCalculationData*>(mso_sptBracketCalc), SAL_N_ELEMENTS( mso_sptBracketCalc ),
+ const_cast<sal_Int32*>(mso_sptDefault1800),
+ const_cast<SvxMSDffTextRectangles*>(mso_sptLeftBracketTextRect), SAL_N_ELEMENTS( mso_sptLeftBracketTextRect ),
+ 21600, 21600,
+ MIN_INT32, MIN_INT32,
+ const_cast<SvxMSDffVertPair*>(mso_sptLeftBracketGluePoints), SAL_N_ELEMENTS( mso_sptLeftBracketGluePoints ),
+ const_cast<SvxMSDffHandle*>(mso_sptLeftBracketHandle), SAL_N_ELEMENTS( mso_sptLeftBracketHandle ) // handles
+};
+const SvxMSDffVertPair mso_sptRightBracketVert[] = // adj value 0 -> 10800
+{
+ { 0, 0 }, { 10800, 0 }, { 21600, 3 MSO_I }, { 21600, 1 MSO_I },
+ { 21600, 2 MSO_I }, { 21600, 4 MSO_I }, { 10800, 21600 }, { 0, 21600 }
+};
+const SvxMSDffTextRectangles mso_sptRightBracketTextRect[] =
+{
+ { { 0, 3 MSO_I }, { 15150, 4 MSO_I } }
+};
+const SvxMSDffVertPair mso_sptRightBracketGluePoints[] =
+{
+ { 0, 0 }, { 0, 21600 }, { 21600, 10800 }
+};
+const SvxMSDffHandle mso_sptRightBracketHandle[] =
+{
+ { SvxMSDffHandleFlags::RANGE,
+ 1, 0x100, 10800, 10800, MIN_INT32, 0x7fffffff, 0, 10800 }
+};
+const mso_CustomShape msoRightBracket =
+{
+ const_cast<SvxMSDffVertPair*>(mso_sptRightBracketVert), SAL_N_ELEMENTS( mso_sptRightBracketVert ),
+ const_cast<sal_uInt16*>(mso_sptBracketSegm), sizeof( mso_sptBracketSegm ) >> 1,
+ const_cast<SvxMSDffCalculationData*>(mso_sptBracketCalc), SAL_N_ELEMENTS( mso_sptBracketCalc ),
+ const_cast<sal_Int32*>(mso_sptDefault1800),
+ const_cast<SvxMSDffTextRectangles*>(mso_sptRightBracketTextRect), SAL_N_ELEMENTS( mso_sptRightBracketTextRect ),
+ 21600, 21600,
+ MIN_INT32, MIN_INT32,
+ const_cast<SvxMSDffVertPair*>(mso_sptRightBracketGluePoints), SAL_N_ELEMENTS( mso_sptRightBracketGluePoints ),
+ const_cast<SvxMSDffHandle*>(mso_sptRightBracketHandle), SAL_N_ELEMENTS( mso_sptRightBracketHandle ) // handles
+};
+
+const SvxMSDffCalculationData mso_sptBraceCalc[] =
+{
+ { 0x2001, { DFF_Prop_adjustValue, 1, 2 } },
+ { 0x2000, { DFF_Prop_adjustValue, 0, 0 } },
+ { 0xa000, { 0x404, 0, DFF_Prop_adjustValue } },
+ { 0xa000, { 0x404, 0, 0x400 } },
+ { 0x2000, { DFF_Prop_adjust2Value, 0, 0 } },
+ { 0x6000, { 0x404, 0x400, 0 } },
+ { 0x6000, { 0x404, DFF_Prop_adjustValue, 0 } },
+ { 0x8000, { 21600, 0, DFF_Prop_adjustValue } },
+ { 0x8000, { 21600, 0, 0x400 } },
+ { 0x2001, { DFF_Prop_adjustValue, 10000, 31953 } },
+ { 0x8000, { 21600, 0, 0x409 } }
+};
+const sal_uInt16 mso_sptBraceSegm[] =
+{
+ 0x4000, 0x2001, 0x0001, 0x2002, 0x0001, 0x2001, 0x8000
+};
+const sal_Int32 mso_sptBraceDefault[] =
+{
+ 2, 1800, 10800
+};
+const SvxMSDffVertPair mso_sptLeftBraceVert[] =
+{
+ { 21600, 0 }, // p
+ { 16200, 0 }, { 10800, 0 MSO_I }, { 10800, 1 MSO_I }, // ccp
+ { 10800, 2 MSO_I }, // p
+ { 10800, 3 MSO_I }, { 5400, 4 MSO_I }, { 0, 4 MSO_I }, // ccp
+ { 5400, 4 MSO_I }, { 10800, 5 MSO_I }, { 10800, 6 MSO_I }, // ccp
+ { 10800, 7 MSO_I }, // p
+ { 10800, 8 MSO_I }, { 16200, 21600 }, { 21600, 21600 } // ccp
+};
+const SvxMSDffTextRectangles mso_sptLeftBraceTextRect[] =
+{
+ { { 13800, 9 MSO_I }, { 21600, 10 MSO_I } }
+};
+const SvxMSDffHandle mso_sptLeftBraceHandle[] =
+{
+ { SvxMSDffHandleFlags::RANGE,
+ 10800, 0x100, 10800, 10800, MIN_INT32, 0x7fffffff, 0, 5400 },
+ { SvxMSDffHandleFlags::RANGE,
+ 0, 0x101, 10800, 10800, MIN_INT32, 0x7fffffff, 0, 21600 }
+};
+const mso_CustomShape msoLeftBrace = // adj value0 0 -> 5400
+{ // adj value1 0 -> 21600
+ const_cast<SvxMSDffVertPair*>(mso_sptLeftBraceVert), SAL_N_ELEMENTS( mso_sptLeftBraceVert ),
+ const_cast<sal_uInt16*>(mso_sptBraceSegm), sizeof( mso_sptBraceSegm ) >> 1,
+ const_cast<SvxMSDffCalculationData*>(mso_sptBraceCalc), SAL_N_ELEMENTS( mso_sptBraceCalc ),
+ const_cast<sal_Int32*>(mso_sptBraceDefault),
+ const_cast<SvxMSDffTextRectangles*>(mso_sptLeftBraceTextRect), SAL_N_ELEMENTS( mso_sptLeftBraceTextRect ),
+ 21600, 21600,
+ MIN_INT32, MIN_INT32,
+ const_cast<SvxMSDffVertPair*>(mso_sptLeftBracketGluePoints), SAL_N_ELEMENTS( mso_sptLeftBracketGluePoints ),
+ const_cast<SvxMSDffHandle*>(mso_sptLeftBraceHandle), SAL_N_ELEMENTS( mso_sptLeftBraceHandle ) // handles
+};
+const SvxMSDffVertPair mso_sptRightBraceVert[] =
+{
+ { 0, 0 }, // p
+ { 5400, 0 }, { 10800, 0 MSO_I }, { 10800, 1 MSO_I }, // ccp
+ { 10800, 2 MSO_I }, // p
+ { 10800, 3 MSO_I }, { 16200, 4 MSO_I }, { 21600, 4 MSO_I }, // ccp
+ { 16200, 4 MSO_I }, { 10800, 5 MSO_I }, { 10800, 6 MSO_I }, // ccp
+ { 10800, 7 MSO_I }, // p
+ { 10800, 8 MSO_I }, { 5400, 21600 }, { 0, 21600 } // ccp
+};
+const SvxMSDffTextRectangles mso_sptRightBraceTextRect[] =
+{
+ { { 0, 9 MSO_I }, { 7800, 10 MSO_I } }
+};
+const SvxMSDffHandle mso_sptRightBraceHandle[] =
+{
+ { SvxMSDffHandleFlags::RANGE,
+ 10800, 0x100, 10800, 10800, MIN_INT32, 0x7fffffff, 0, 5400 },
+ { SvxMSDffHandleFlags::RANGE,
+ 1, 0x101, 10800, 10800, MIN_INT32, 0x7fffffff, 0, 21600 }
+};
+const mso_CustomShape msoRightBrace = // adj value0 0 -> 5400
+{ // adj value1 0 -> 21600
+ const_cast<SvxMSDffVertPair*>(mso_sptRightBraceVert), SAL_N_ELEMENTS( mso_sptRightBraceVert ),
+ const_cast<sal_uInt16*>(mso_sptBraceSegm), sizeof( mso_sptBraceSegm ) >> 1,
+ const_cast<SvxMSDffCalculationData*>(mso_sptBraceCalc), SAL_N_ELEMENTS( mso_sptBraceCalc ),
+ const_cast<sal_Int32*>(mso_sptBraceDefault),
+ const_cast<SvxMSDffTextRectangles*>(mso_sptRightBraceTextRect), SAL_N_ELEMENTS( mso_sptRightBraceTextRect ),
+ 21600, 21600,
+ MIN_INT32, MIN_INT32,
+ const_cast<SvxMSDffVertPair*>(mso_sptRightBracketGluePoints), SAL_N_ELEMENTS( mso_sptRightBracketGluePoints ),
+ const_cast<SvxMSDffHandle*>(mso_sptRightBraceHandle), SAL_N_ELEMENTS( mso_sptRightBraceHandle ) // handles
+};
+
+const SvxMSDffVertPair mso_sptIrregularSeal1Vert[] =
+{
+ { 10901, 5905 }, { 8458, 2399 }, { 7417, 6425 }, { 476, 2399 },
+ { 4732, 7722 }, { 106, 8718 }, { 3828, 11880 }, { 243, 14689 },
+ { 5772, 14041 }, { 4868, 17719 }, { 7819, 15730 }, { 8590, 21600 },
+ { 10637, 15038 }, { 13349, 19840 }, { 14125, 14561 }, { 18248, 18195 },
+ { 16938, 13044 }, { 21600, 13393 }, { 17710, 10579 }, { 21198, 8242 },
+ { 16806, 7417 }, { 18482, 4560 }, { 14257, 5429 }, { 14623, 106 }, { 10901, 5905 }
+};
+const SvxMSDffTextRectangles mso_sptIrregularSeal1TextRect[] =
+{
+ { { 4680, 6570 }, { 16140, 13280 } }
+};
+const SvxMSDffVertPair mso_sptIrregularSeal1GluePoints[] =
+{
+ { 14623, 106 }, { 106, 8718 }, { 8590, 21600 }, { 21600, 13393 }
+};
+const mso_CustomShape msoIrregularSeal1 =
+{
+ const_cast<SvxMSDffVertPair*>(mso_sptIrregularSeal1Vert), SAL_N_ELEMENTS( mso_sptIrregularSeal1Vert ),
+ nullptr, 0,
+ nullptr, 0,
+ nullptr,
+ const_cast<SvxMSDffTextRectangles*>(mso_sptIrregularSeal1TextRect), SAL_N_ELEMENTS( mso_sptIrregularSeal1TextRect ),
+ 21600, 21600,
+ MIN_INT32, MIN_INT32,
+ const_cast<SvxMSDffVertPair*>(mso_sptIrregularSeal1GluePoints), SAL_N_ELEMENTS( mso_sptIrregularSeal1GluePoints ),
+ nullptr, 0 // handles
+};
+
+const SvxMSDffVertPair mso_sptIrregularSeal2Vert[] =
+{
+ { 11464, 4340 }, { 9722, 1887 }, { 8548, 6383 }, { 4503, 3626 },
+ { 5373, 7816 }, { 1174, 8270 }, { 3934, 11592 }, { 0, 12875 },
+ { 3329, 15372 }, { 1283, 17824 }, { 4804, 18239 }, { 4918, 21600 },
+ { 7525, 18125 }, { 8698, 19712 }, { 9871, 17371 }, { 11614, 18844 },
+ { 12178, 15937 }, { 14943, 17371 }, { 14640, 14348 }, { 18878, 15632 },
+ { 16382, 12311 }, { 18270, 11292 }, { 16986, 9404 }, { 21600, 6646 },
+ { 16382, 6533 }, { 18005, 3172 }, { 14524, 5778 }, { 14789, 0 },
+ { 11464, 4340 }
+};
+const SvxMSDffTextRectangles mso_sptIrregularSeal2TextRect[] =
+{
+ { { 5400, 6570 }, { 14160, 15290 } }
+};
+const SvxMSDffVertPair mso_sptIrregularSeal2GluePoints[] =
+{
+ { 9722, 1887 }, { 0, 12875 }, { 11614, 18844 }, { 21600, 6646 }
+};
+const mso_CustomShape msoIrregularSeal2 =
+{
+ const_cast<SvxMSDffVertPair*>(mso_sptIrregularSeal2Vert), SAL_N_ELEMENTS( mso_sptIrregularSeal2Vert ),
+ nullptr, 0,
+ nullptr, 0,
+ nullptr,
+ const_cast<SvxMSDffTextRectangles*>(mso_sptIrregularSeal2TextRect), SAL_N_ELEMENTS( mso_sptIrregularSeal2TextRect ),
+ 21600, 21600,
+ MIN_INT32, MIN_INT32,
+ const_cast<SvxMSDffVertPair*>(mso_sptIrregularSeal2GluePoints), SAL_N_ELEMENTS( mso_sptIrregularSeal2GluePoints ),
+ nullptr, 0 // handles
+};
+
+const SvxMSDffVertPair mso_sptSeal4Vert[] = // adjustment1 : 0 - 10800
+{
+ { 0, 10800 }, { 4 MSO_I, 4 MSO_I }, { 10800, 0 }, { 3 MSO_I, 4 MSO_I },
+ { 21600, 10800 }, { 3 MSO_I, 3 MSO_I }, { 10800, 21600 }, { 4 MSO_I, 3 MSO_I },
+ { 0, 10800 }
+};
+const SvxMSDffCalculationData mso_sptSeal4Calc[] =
+{
+ { 0x0000, { 7600, 0, 0 } },
+ { 0x6001, { 0x400, DFF_Prop_adjustValue, 10800 } },
+ { 0xa000, { 0x400, 0, 0x401 } },
+ { 0x4000, { 10800, 0x402, 0 } },
+ { 0x8000, { 10800, 0, 0x402 } }
+};
+const SvxMSDffTextRectangles mso_sptSeal4TextRect[] =
+{
+ { { 4 MSO_I, 4 MSO_I }, { 3 MSO_I, 3 MSO_I } }
+};
+const SvxMSDffHandle mso_sptSealHandle[] =
+{
+ { SvxMSDffHandleFlags::RANGE,
+ 0x100, 10800, 10800, 10800, 0, 10800, MIN_INT32, 0x7fffffff }
+};
+const mso_CustomShape msoSeal4 =
+{
+ const_cast<SvxMSDffVertPair*>(mso_sptSeal4Vert), SAL_N_ELEMENTS( mso_sptSeal4Vert ),
+ nullptr, 0,
+ const_cast<SvxMSDffCalculationData*>(mso_sptSeal4Calc), SAL_N_ELEMENTS( mso_sptSeal4Calc ),
+ const_cast<sal_Int32*>(mso_sptDefault8100),
+ const_cast<SvxMSDffTextRectangles*>(mso_sptSeal4TextRect), SAL_N_ELEMENTS( mso_sptSeal4TextRect ),
+ 21600, 21600,
+ MIN_INT32, MIN_INT32,
+ nullptr, 0,
+ const_cast<SvxMSDffHandle*>(mso_sptSealHandle), SAL_N_ELEMENTS( mso_sptSealHandle ) // handles
+};
+
+const SvxMSDffVertPair mso_sptStarVert[] =
+{
+ { 10797, 0 }, { 8278, 8256 }, { 0, 8256 }, { 6722, 13405 },
+ { 4198, 21600 }, { 10797, 16580 }, { 17401, 21600 }, { 14878, 13405 },
+ { 21600, 8256 }, { 13321, 8256 }, { 10797, 0 }
+};
+const SvxMSDffTextRectangles mso_sptStarTextRect[] =
+{
+ { { 6722, 8256 }, { 14878, 15460 } }
+};
+const mso_CustomShape msoStar =
+{
+ const_cast<SvxMSDffVertPair*>(mso_sptStarVert), SAL_N_ELEMENTS( mso_sptStarVert ),
+ nullptr, 0,
+ nullptr, 0,
+ nullptr,
+ const_cast<SvxMSDffTextRectangles*>(mso_sptStarTextRect), SAL_N_ELEMENTS( mso_sptStarTextRect ),
+ 21600, 21600,
+ MIN_INT32, MIN_INT32,
+ nullptr, 0,
+ nullptr, 0 // handles
+};
+
+const SvxMSDffCalculationData mso_sptSeal24Calc[] =
+{
+ { 0x2000, { DFF_Prop_adjustValue, 0, 0 } }, // 0x00
+ { 0x2081, { 0x400, 10800, 315 } }, // 0x01 ( textframe )
+ { 0x2082, { 0x400, 10800, 315 } }, // 0x02
+ { 0x2081, { 0x400, 10800, 135 } }, // 0x03
+ { 0x2082, { 0x400, 10800, 135 } }, // 0x04
+ { 0x0081, { 0, 10800, 0 } },
+ { 0x0082, { 0, 10800, 0 } },
+ { 0x2081, { 0x400, 10800, 7 } },
+ { 0x2082, { 0x400, 10800, 7 } },
+ { 0x0081, { 0, 10800, 15 } },
+ { 0x0082, { 0, 10800, 15 } },
+ { 0x2081, { 0x400, 10800, 22 } },
+ { 0x2082, { 0x400, 10800, 22 } },
+ { 0x0081, { 0, 10800, 30 } },
+ { 0x0082, { 0, 10800, 30 } },
+ { 0x2081, { 0x400, 10800, 37 } },
+ { 0x2082, { 0x400, 10800, 37 } },
+ { 0x0081, { 0, 10800, 45 } },
+ { 0x0082, { 0, 10800, 45 } },
+ { 0x2081, { 0x400, 10800, 52 } },
+ { 0x2082, { 0x400, 10800, 52 } },
+ { 0x0081, { 0, 10800, 60 } },
+ { 0x0082, { 0, 10800, 60 } },
+ { 0x2081, { 0x400, 10800, 67 } },
+ { 0x2082, { 0x400, 10800, 67 } },
+ { 0x0081, { 0, 10800, 75 } },
+ { 0x0082, { 0, 10800, 75 } },
+ { 0x2081, { 0x400, 10800, 82 } },
+ { 0x2082, { 0x400, 10800, 82 } },
+ { 0x0081, { 0, 10800, 90 } },
+ { 0x0082, { 0, 10800, 90 } },
+ { 0x2081, { 0x400, 10800, 97 } },
+ { 0x2082, { 0x400, 10800, 97 } },
+ { 0x0081, { 0, 10800, 105 } },
+ { 0x0082, { 0, 10800, 105 } },
+ { 0x2081, { 0x400, 10800, 112 } },
+ { 0x2082, { 0x400, 10800, 112 } },
+ { 0x0081, { 0, 10800, 120 } },
+ { 0x0082, { 0, 10800, 120 } },
+ { 0x2081, { 0x400, 10800, 127 } },
+ { 0x2082, { 0x400, 10800, 127 } },
+ { 0x0081, { 0, 10800, 135 } },
+ { 0x0082, { 0, 10800, 135 } },
+ { 0x2081, { 0x400, 10800, 142 } },
+ { 0x2082, { 0x400, 10800, 142 } },
+ { 0x0081, { 0, 10800, 150 } },
+ { 0x0082, { 0, 10800, 150 } },
+ { 0x2081, { 0x400, 10800, 157 } },
+ { 0x2082, { 0x400, 10800, 157 } },
+ { 0x0081, { 0, 10800, 165 } },
+ { 0x0082, { 0, 10800, 165 } },
+ { 0x2081, { 0x400, 10800, 172 } },
+ { 0x2082, { 0x400, 10800, 172 } },
+ { 0x0081, { 0, 10800, 180 } },
+ { 0x0082, { 0, 10800, 180 } },
+ { 0x2081, { 0x400, 10800, 187 } },
+ { 0x2082, { 0x400, 10800, 187 } },
+ { 0x0081, { 0, 10800, 195 } },
+ { 0x0082, { 0, 10800, 195 } },
+ { 0x2081, { 0x400, 10800, 202 } },
+ { 0x2082, { 0x400, 10800, 202 } },
+ { 0x0081, { 0, 10800, 210 } },
+ { 0x0082, { 0, 10800, 210 } },
+ { 0x2081, { 0x400, 10800, 217 } },
+ { 0x2082, { 0x400, 10800, 217 } },
+ { 0x0081, { 0, 10800, 225 } },
+ { 0x0082, { 0, 10800, 225 } },
+ { 0x2081, { 0x400, 10800, 232 } },
+ { 0x2082, { 0x400, 10800, 232 } },
+ { 0x0081, { 0, 10800, 240 } },
+ { 0x0082, { 0, 10800, 240 } },
+ { 0x2081, { 0x400, 10800, 247 } },
+ { 0x2082, { 0x400, 10800, 247 } },
+ { 0x0081, { 0, 10800, 255 } },
+ { 0x0082, { 0, 10800, 255 } },
+ { 0x2081, { 0x400, 10800, 262 } },
+ { 0x2082, { 0x400, 10800, 262 } },
+ { 0x0081, { 0, 10800, 270 } },
+ { 0x0082, { 0, 10800, 270 } },
+ { 0x2081, { 0x400, 10800, 277 } },
+ { 0x2082, { 0x400, 10800, 277 } },
+ { 0x0081, { 0, 10800, 285 } },
+ { 0x0082, { 0, 10800, 285 } },
+ { 0x2081, { 0x400, 10800, 292 } },
+ { 0x2082, { 0x400, 10800, 292 } },
+ { 0x0081, { 0, 10800, 300 } },
+ { 0x0082, { 0, 10800, 300 } },
+ { 0x2081, { 0x400, 10800, 307 } },
+ { 0x2082, { 0x400, 10800, 307 } },
+ { 0x0081, { 0, 10800, 315 } },
+ { 0x0082, { 0, 10800, 315 } },
+ { 0x2081, { 0x400, 10800, 322 } },
+ { 0x2082, { 0x400, 10800, 322 } },
+ { 0x0081, { 0, 10800, 330 } },
+ { 0x0082, { 0, 10800, 330 } },
+ { 0x2081, { 0x400, 10800, 337 } },
+ { 0x2082, { 0x400, 10800, 337 } },
+ { 0x0081, { 0, 10800, 345 } },
+ { 0x0082, { 0, 10800, 345 } },
+ { 0x2081, { 0x400, 10800, 352 } },
+ { 0x2082, { 0x400, 10800, 352 } }
+};
+const SvxMSDffVertPair mso_sptSeal8Vert[] = // adj value 0 -> 10800
+{
+ { 5 MSO_I, 6 MSO_I }, { 11 MSO_I, 12 MSO_I }, { 17 MSO_I, 18 MSO_I }, { 23 MSO_I, 24 MSO_I },
+ { 29 MSO_I, 30 MSO_I }, { 35 MSO_I, 36 MSO_I }, { 41 MSO_I, 42 MSO_I }, { 47 MSO_I, 48 MSO_I },
+ { 53 MSO_I, 54 MSO_I }, { 59 MSO_I, 60 MSO_I }, { 65 MSO_I, 66 MSO_I }, { 71 MSO_I, 72 MSO_I },
+ { 77 MSO_I, 78 MSO_I }, { 83 MSO_I, 84 MSO_I }, { 89 MSO_I, 90 MSO_I }, { 95 MSO_I, 96 MSO_I },
+ { 5 MSO_I, 6 MSO_I }
+};
+const SvxMSDffTextRectangles mso_sptSealTextRect[] =
+{
+ { { 1 MSO_I, 2 MSO_I }, { 3 MSO_I, 4 MSO_I } }
+};
+const mso_CustomShape msoSeal8 =
+{
+ const_cast<SvxMSDffVertPair*>(mso_sptSeal8Vert), SAL_N_ELEMENTS( mso_sptSeal8Vert ),
+ nullptr, 0,
+ const_cast<SvxMSDffCalculationData*>(mso_sptSeal24Calc), SAL_N_ELEMENTS( mso_sptSeal24Calc ),
+ const_cast<sal_Int32*>(mso_sptDefault2500),
+ const_cast<SvxMSDffTextRectangles*>(mso_sptSealTextRect), SAL_N_ELEMENTS( mso_sptSealTextRect ),
+ 21600, 21600,
+ MIN_INT32, MIN_INT32,
+ nullptr, 0,
+ const_cast<SvxMSDffHandle*>(mso_sptSealHandle), SAL_N_ELEMENTS( mso_sptSealHandle ) // handles
+};
+const SvxMSDffVertPair mso_sptSeal16Vert[] = // adj value 0 -> 10800
+{
+ { 0x05 MSO_I, 0x06 MSO_I }, { 0x07 MSO_I, 0x08 MSO_I }, { 0x09 MSO_I, 0x0a MSO_I }, { 0x0b MSO_I, 0x0c MSO_I },
+ { 0x0d MSO_I, 0x0e MSO_I }, { 0x0f MSO_I, 0x10 MSO_I }, { 0x11 MSO_I, 0x12 MSO_I }, { 0x13 MSO_I, 0x14 MSO_I },
+ { 0x15 MSO_I, 0x16 MSO_I }, { 0x17 MSO_I, 0x18 MSO_I }, { 0x19 MSO_I, 0x1a MSO_I }, { 0x1b MSO_I, 0x1c MSO_I },
+ { 0x1d MSO_I, 0x1e MSO_I }, { 0x1f MSO_I, 0x20 MSO_I }, { 0x21 MSO_I, 0x22 MSO_I }, { 0x23 MSO_I, 0x24 MSO_I },
+ { 0x25 MSO_I, 0x26 MSO_I }, { 0x27 MSO_I, 0x28 MSO_I }, { 0x29 MSO_I, 0x2a MSO_I }, { 0x2b MSO_I, 0x2c MSO_I },
+ { 0x2d MSO_I, 0x2e MSO_I }, { 0x2f MSO_I, 0x30 MSO_I }, { 0x31 MSO_I, 0x32 MSO_I }, { 0x33 MSO_I, 0x34 MSO_I },
+ { 0x35 MSO_I, 0x36 MSO_I }, { 0x37 MSO_I, 0x38 MSO_I }, { 0x39 MSO_I, 0x3a MSO_I }, { 0x3b MSO_I, 0x3c MSO_I },
+ { 0x3d MSO_I, 0x3e MSO_I }, { 0x3f MSO_I, 0x40 MSO_I }, { 0x41 MSO_I, 0x42 MSO_I }, { 0x43 MSO_I, 0x44 MSO_I },
+ { 0x05 MSO_I, 0x06 MSO_I }
+};
+const SvxMSDffCalculationData mso_sptSeal16Calc[] =
+{
+ { 0x2000, { DFF_Prop_adjustValue, 0, 0 } }, // 0x00
+ { 0x2081, { 0x400, 10800, 315 } }, // 0x01 ( textframe )
+ { 0x2082, { 0x400, 10800, 315 } }, // 0x02
+ { 0x2081, { 0x400, 10800, 135 } }, // 0x03
+ { 0x2082, { 0x400, 10800, 135 } }, // 0x04
+ { 0x0081, { 0, 10800, 0 } },
+ { 0x0082, { 0, 10800, 0 } },
+ { 0x2081, { 0x400, 10800, 11 } },
+ { 0x2082, { 0x400, 10800, 11 } },
+ { 0x0081, { 0, 10800, 22 } },
+ { 0x0082, { 0, 10800, 22 } },
+ { 0x2081, { 0x400, 10800, 33 } },
+ { 0x2082, { 0x400, 10800, 33 } },
+ { 0x0081, { 0, 10800, 45 } },
+ { 0x0082, { 0, 10800, 45 } },
+ { 0x2081, { 0x400, 10800, 56 } },
+ { 0x2082, { 0x400, 10800, 56 } },
+ { 0x0081, { 0, 10800, 67 } },
+ { 0x0082, { 0, 10800, 67 } },
+ { 0x2081, { 0x400, 10800, 78 } },
+ { 0x2082, { 0x400, 10800, 78 } },
+ { 0x0081, { 0, 10800, 90 } },
+ { 0x0082, { 0, 10800, 90 } },
+ { 0x2081, { 0x400, 10800, 101 } },
+ { 0x2082, { 0x400, 10800, 101 } },
+ { 0x0081, { 0, 10800, 112 } },
+ { 0x0082, { 0, 10800, 112 } },
+ { 0x2081, { 0x400, 10800, 123 } },
+ { 0x2082, { 0x400, 10800, 123 } },
+ { 0x0081, { 0, 10800, 135 } },
+ { 0x0082, { 0, 10800, 135 } },
+ { 0x2081, { 0x400, 10800, 146 } },
+ { 0x2082, { 0x400, 10800, 146 } },
+ { 0x0081, { 0, 10800, 157 } },
+ { 0x0082, { 0, 10800, 157 } },
+ { 0x2081, { 0x400, 10800, 168 } },
+ { 0x2082, { 0x400, 10800, 168 } },
+ { 0x0081, { 0, 10800, 180 } },
+ { 0x0082, { 0, 10800, 180 } },
+ { 0x2081, { 0x400, 10800, 191 } },
+ { 0x2082, { 0x400, 10800, 191 } },
+ { 0x0081, { 0, 10800, 202 } },
+ { 0x0082, { 0, 10800, 202 } },
+ { 0x2081, { 0x400, 10800, 213 } },
+ { 0x2082, { 0x400, 10800, 213 } },
+ { 0x0081, { 0, 10800, 225 } },
+ { 0x0082, { 0, 10800, 225 } },
+ { 0x2081, { 0x400, 10800, 236 } },
+ { 0x2082, { 0x400, 10800, 236 } },
+ { 0x0081, { 0, 10800, 247 } },
+ { 0x0082, { 0, 10800, 247 } },
+ { 0x2081, { 0x400, 10800, 258 } },
+ { 0x2082, { 0x400, 10800, 258 } },
+ { 0x0081, { 0, 10800, 270 } },
+ { 0x0082, { 0, 10800, 270 } },
+ { 0x2081, { 0x400, 10800, 281 } },
+ { 0x2082, { 0x400, 10800, 281 } },
+ { 0x0081, { 0, 10800, 292 } },
+ { 0x0082, { 0, 10800, 292 } },
+ { 0x2081, { 0x400, 10800, 303 } },
+ { 0x2082, { 0x400, 10800, 303 } },
+ { 0x0081, { 0, 10800, 315 } },
+ { 0x0082, { 0, 10800, 315 } },
+ { 0x2081, { 0x400, 10800, 326 } },
+ { 0x2082, { 0x400, 10800, 326 } },
+ { 0x0081, { 0, 10800, 337 } },
+ { 0x0082, { 0, 10800, 337 } },
+ { 0x2081, { 0x400, 10800, 348 } },
+ { 0x2082, { 0x400, 10800, 348 } }
+};
+const mso_CustomShape msoSeal16 =
+{
+ const_cast<SvxMSDffVertPair*>(mso_sptSeal16Vert), SAL_N_ELEMENTS( mso_sptSeal16Vert ),
+ nullptr, 0,
+ const_cast<SvxMSDffCalculationData*>(mso_sptSeal16Calc), SAL_N_ELEMENTS( mso_sptSeal16Calc ),
+ const_cast<sal_Int32*>(mso_sptDefault2500),
+ const_cast<SvxMSDffTextRectangles*>(mso_sptSealTextRect), SAL_N_ELEMENTS( mso_sptSealTextRect ),
+ 21600, 21600,
+ MIN_INT32, MIN_INT32,
+ nullptr, 0,
+ const_cast<SvxMSDffHandle*>(mso_sptSealHandle), SAL_N_ELEMENTS( mso_sptSealHandle ) // handles
+};
+const SvxMSDffVertPair mso_sptSeal24Vert[] =
+{
+ { 0x05 MSO_I, 0x06 MSO_I }, { 0x07 MSO_I, 0x08 MSO_I }, { 0x09 MSO_I, 0x0a MSO_I }, { 0x0b MSO_I, 0x0c MSO_I },
+ { 0x0d MSO_I, 0x0e MSO_I }, { 0x0f MSO_I, 0x10 MSO_I }, { 0x11 MSO_I, 0x12 MSO_I }, { 0x13 MSO_I, 0x14 MSO_I },
+ { 0x15 MSO_I, 0x16 MSO_I }, { 0x17 MSO_I, 0x18 MSO_I }, { 0x19 MSO_I, 0x1a MSO_I }, { 0x1b MSO_I, 0x1c MSO_I },
+ { 0x1d MSO_I, 0x1e MSO_I }, { 0x1f MSO_I, 0x20 MSO_I }, { 0x21 MSO_I, 0x22 MSO_I }, { 0x23 MSO_I, 0x24 MSO_I },
+ { 0x25 MSO_I, 0x26 MSO_I }, { 0x27 MSO_I, 0x28 MSO_I }, { 0x29 MSO_I, 0x2a MSO_I }, { 0x2b MSO_I, 0x2c MSO_I },
+ { 0x2d MSO_I, 0x2e MSO_I }, { 0x2f MSO_I, 0x30 MSO_I }, { 0x31 MSO_I, 0x32 MSO_I }, { 0x33 MSO_I, 0x34 MSO_I },
+ { 0x35 MSO_I, 0x36 MSO_I }, { 0x37 MSO_I, 0x38 MSO_I }, { 0x39 MSO_I, 0x3a MSO_I }, { 0x3b MSO_I, 0x3c MSO_I },
+ { 0x3d MSO_I, 0x3e MSO_I }, { 0x3f MSO_I, 0x40 MSO_I }, { 0x41 MSO_I, 0x42 MSO_I }, { 0x43 MSO_I, 0x44 MSO_I },
+ { 0x45 MSO_I, 0x46 MSO_I }, { 0x47 MSO_I, 0x48 MSO_I }, { 0x49 MSO_I, 0x4a MSO_I }, { 0x4b MSO_I, 0x4c MSO_I },
+ { 0x4d MSO_I, 0x4e MSO_I }, { 0x4f MSO_I, 0x50 MSO_I }, { 0x51 MSO_I, 0x52 MSO_I }, { 0x53 MSO_I, 0x54 MSO_I },
+ { 0x55 MSO_I, 0x56 MSO_I }, { 0x57 MSO_I, 0x58 MSO_I }, { 0x59 MSO_I, 0x5a MSO_I }, { 0x5b MSO_I, 0x5c MSO_I },
+ { 0x5d MSO_I, 0x5e MSO_I }, { 0x5f MSO_I, 0x60 MSO_I }, { 0x61 MSO_I, 0x62 MSO_I }, { 0x63 MSO_I, 0x64 MSO_I },
+ { 0x05 MSO_I, 0x06 MSO_I }
+};
+const mso_CustomShape msoSeal24 =
+{
+ const_cast<SvxMSDffVertPair*>(mso_sptSeal24Vert), SAL_N_ELEMENTS( mso_sptSeal24Vert ),
+ nullptr, 0,
+ const_cast<SvxMSDffCalculationData*>(mso_sptSeal24Calc), SAL_N_ELEMENTS( mso_sptSeal24Calc ),
+ const_cast<sal_Int32*>(mso_sptDefault2500),
+ const_cast<SvxMSDffTextRectangles*>(mso_sptSealTextRect), SAL_N_ELEMENTS( mso_sptSealTextRect ),
+ 21600, 21600,
+ MIN_INT32, MIN_INT32,
+ nullptr, 0,
+ const_cast<SvxMSDffHandle*>(mso_sptSealHandle), SAL_N_ELEMENTS( mso_sptSealHandle ) // handles
+};
+const SvxMSDffCalculationData mso_sptSeal32Calc[] =
+{
+ { 0x2000, { DFF_Prop_adjustValue, 0, 0 } }, // 0x00
+ { 0x2081, { 0x400, 10800, 315 } }, // 0x01 ( textframe )
+ { 0x2082, { 0x400, 10800, 315 } }, // 0x02
+ { 0x2081, { 0x400, 10800, 135 } }, // 0x03
+ { 0x2082, { 0x400, 10800, 135 } }, // 0x04
+ { 0x0081, { 0, 10800, 0 } },
+ { 0x0082, { 0, 10800, 0 } },
+ { 0x2081, { 0x400, 10800, 5 } },
+ { 0x2082, { 0x400, 10800, 5 } },
+ { 0x0081, { 0, 10800, 11 } },
+ { 0x0082, { 0, 10800, 11 } },
+ { 0x2081, { 0x400, 10800, 16 } },
+ { 0x2082, { 0x400, 10800, 16 } },
+ { 0x0081, { 0, 10800, 22 } },
+ { 0x0082, { 0, 10800, 22 } },
+ { 0x2081, { 0x400, 10800, 28 } },
+ { 0x2082, { 0x400, 10800, 28 } },
+ { 0x0081, { 0, 10800, 33 } },
+ { 0x0082, { 0, 10800, 33 } },
+ { 0x2081, { 0x400, 10800, 39 } },
+ { 0x2082, { 0x400, 10800, 39 } },
+ { 0x0081, { 0, 10800, 45 } },
+ { 0x0082, { 0, 10800, 45 } },
+ { 0x2081, { 0x400, 10800, 50 } },
+ { 0x2082, { 0x400, 10800, 50 } },
+ { 0x0081, { 0, 10800, 56 } },
+ { 0x0082, { 0, 10800, 56 } },
+ { 0x2081, { 0x400, 10800, 61 } },
+ { 0x2082, { 0x400, 10800, 61 } },
+ { 0x0081, { 0, 10800, 67 } },
+ { 0x0082, { 0, 10800, 67 } },
+ { 0x2081, { 0x400, 10800, 73 } },
+ { 0x2082, { 0x400, 10800, 73 } },
+ { 0x0081, { 0, 10800, 78 } },
+ { 0x0082, { 0, 10800, 78 } },
+ { 0x2081, { 0x400, 10800, 84 } },
+ { 0x2082, { 0x400, 10800, 84 } },
+ { 0x0081, { 0, 10800, 90 } },
+ { 0x0082, { 0, 10800, 90 } },
+ { 0x2081, { 0x400, 10800, 95 } },
+ { 0x2082, { 0x400, 10800, 95 } },
+ { 0x0081, { 0, 10800, 101 } },
+ { 0x0082, { 0, 10800, 101 } },
+ { 0x2081, { 0x400, 10800, 106 } },
+ { 0x2082, { 0x400, 10800, 106 } },
+ { 0x0081, { 0, 10800, 112 } },
+ { 0x0082, { 0, 10800, 112 } },
+ { 0x2081, { 0x400, 10800, 118 } },
+ { 0x2082, { 0x400, 10800, 118 } },
+ { 0x0081, { 0, 10800, 123 } },
+ { 0x0082, { 0, 10800, 123 } },
+ { 0x2081, { 0x400, 10800, 129 } },
+ { 0x2082, { 0x400, 10800, 129 } },
+ { 0x0081, { 0, 10800, 135 } },
+ { 0x0082, { 0, 10800, 135 } },
+ { 0x2081, { 0x400, 10800, 140 } },
+ { 0x2082, { 0x400, 10800, 140 } },
+ { 0x0081, { 0, 10800, 146 } },
+ { 0x0082, { 0, 10800, 146 } },
+ { 0x2081, { 0x400, 10800, 151 } },
+ { 0x2082, { 0x400, 10800, 151 } },
+ { 0x0081, { 0, 10800, 157 } },
+ { 0x0082, { 0, 10800, 157 } },
+ { 0x2081, { 0x400, 10800, 163 } },
+ { 0x2082, { 0x400, 10800, 163 } },
+ { 0x0081, { 0, 10800, 168 } },
+ { 0x0082, { 0, 10800, 168 } },
+ { 0x2081, { 0x400, 10800, 174 } },
+ { 0x2082, { 0x400, 10800, 174 } },
+ { 0x0081, { 0, 10800, 180 } },
+ { 0x0082, { 0, 10800, 180 } },
+ { 0x2081, { 0x400, 10800, 185 } },
+ { 0x2082, { 0x400, 10800, 185 } },
+ { 0x0081, { 0, 10800, 191 } },
+ { 0x0082, { 0, 10800, 191 } },
+ { 0x2081, { 0x400, 10800, 196 } },
+ { 0x2082, { 0x400, 10800, 196 } },
+ { 0x0081, { 0, 10800, 202 } },
+ { 0x0082, { 0, 10800, 202 } },
+ { 0x2081, { 0x400, 10800, 208 } },
+ { 0x2082, { 0x400, 10800, 208 } },
+ { 0x0081, { 0, 10800, 213 } },
+ { 0x0082, { 0, 10800, 213 } },
+ { 0x2081, { 0x400, 10800, 219 } },
+ { 0x2082, { 0x400, 10800, 219 } },
+ { 0x0081, { 0, 10800, 225 } },
+ { 0x0082, { 0, 10800, 225 } },
+ { 0x2081, { 0x400, 10800, 230 } },
+ { 0x2082, { 0x400, 10800, 230 } },
+ { 0x0081, { 0, 10800, 236 } },
+ { 0x0082, { 0, 10800, 236 } },
+ { 0x2081, { 0x400, 10800, 241 } },
+ { 0x2082, { 0x400, 10800, 241 } },
+ { 0x0081, { 0, 10800, 247 } },
+ { 0x0082, { 0, 10800, 247 } },
+ { 0x2081, { 0x400, 10800, 253 } },
+ { 0x2082, { 0x400, 10800, 253 } },
+ { 0x0081, { 0, 10800, 258 } },
+ { 0x0082, { 0, 10800, 258 } },
+ { 0x2081, { 0x400, 10800, 264 } },
+ { 0x2082, { 0x400, 10800, 264 } },
+ { 0x0081, { 0, 10800, 270 } },
+ { 0x0082, { 0, 10800, 270 } },
+ { 0x2081, { 0x400, 10800, 275 } },
+ { 0x2082, { 0x400, 10800, 275 } },
+ { 0x0081, { 0, 10800, 281 } },
+ { 0x0082, { 0, 10800, 281 } },
+ { 0x2081, { 0x400, 10800, 286 } },
+ { 0x2082, { 0x400, 10800, 286 } },
+ { 0x0081, { 0, 10800, 292 } },
+ { 0x0082, { 0, 10800, 292 } },
+ { 0x2081, { 0x400, 10800, 298 } },
+ { 0x2082, { 0x400, 10800, 298 } },
+ { 0x0081, { 0, 10800, 303 } },
+ { 0x0082, { 0, 10800, 303 } },
+ { 0x2081, { 0x400, 10800, 309 } },
+ { 0x2082, { 0x400, 10800, 309 } },
+ { 0x0081, { 0, 10800, 315 } },
+ { 0x0082, { 0, 10800, 315 } },
+ { 0x2081, { 0x400, 10800, 320 } },
+ { 0x2082, { 0x400, 10800, 320 } },
+ { 0x0081, { 0, 10800, 326 } },
+ { 0x0082, { 0, 10800, 326 } },
+ { 0x2081, { 0x400, 10800, 331 } },
+ { 0x2082, { 0x400, 10800, 331 } },
+ { 0x0081, { 0, 10800, 337 } },
+ { 0x0082, { 0, 10800, 337 } },
+ { 0x2081, { 0x400, 10800, 343 } },
+ { 0x2082, { 0x400, 10800, 343 } },
+ { 0x0081, { 0, 10800, 348 } },
+ { 0x0082, { 0, 10800, 348 } },
+ { 0x2081, { 0x400, 10800, 354 } },
+ { 0x2082, { 0x400, 10800, 354 } }
+};
+const SvxMSDffVertPair mso_sptSeal32Vert[] =
+{
+ { 0x05 MSO_I, 0x06 MSO_I }, { 0x07 MSO_I, 0x08 MSO_I }, { 0x09 MSO_I, 0x0a MSO_I }, { 0x0b MSO_I, 0x0c MSO_I },
+ { 0x0d MSO_I, 0x0e MSO_I }, { 0x0f MSO_I, 0x10 MSO_I }, { 0x11 MSO_I, 0x12 MSO_I }, { 0x13 MSO_I, 0x14 MSO_I },
+ { 0x15 MSO_I, 0x16 MSO_I }, { 0x17 MSO_I, 0x18 MSO_I }, { 0x19 MSO_I, 0x1a MSO_I }, { 0x1b MSO_I, 0x1c MSO_I },
+ { 0x1d MSO_I, 0x1e MSO_I }, { 0x1f MSO_I, 0x20 MSO_I }, { 0x21 MSO_I, 0x22 MSO_I }, { 0x23 MSO_I, 0x24 MSO_I },
+ { 0x25 MSO_I, 0x26 MSO_I }, { 0x27 MSO_I, 0x28 MSO_I }, { 0x29 MSO_I, 0x2a MSO_I }, { 0x2b MSO_I, 0x2c MSO_I },
+ { 0x2d MSO_I, 0x2e MSO_I }, { 0x2f MSO_I, 0x30 MSO_I }, { 0x31 MSO_I, 0x32 MSO_I }, { 0x33 MSO_I, 0x34 MSO_I },
+ { 0x35 MSO_I, 0x36 MSO_I }, { 0x37 MSO_I, 0x38 MSO_I }, { 0x39 MSO_I, 0x3a MSO_I }, { 0x3b MSO_I, 0x3c MSO_I },
+ { 0x3d MSO_I, 0x3e MSO_I }, { 0x3f MSO_I, 0x40 MSO_I }, { 0x41 MSO_I, 0x42 MSO_I }, { 0x43 MSO_I, 0x44 MSO_I },
+ { 0x45 MSO_I, 0x46 MSO_I }, { 0x47 MSO_I, 0x48 MSO_I }, { 0x49 MSO_I, 0x4a MSO_I }, { 0x4b MSO_I, 0x4c MSO_I },
+ { 0x4d MSO_I, 0x4e MSO_I }, { 0x4f MSO_I, 0x50 MSO_I }, { 0x51 MSO_I, 0x52 MSO_I }, { 0x53 MSO_I, 0x54 MSO_I },
+ { 0x55 MSO_I, 0x56 MSO_I }, { 0x57 MSO_I, 0x58 MSO_I }, { 0x59 MSO_I, 0x5a MSO_I }, { 0x5b MSO_I, 0x5c MSO_I },
+ { 0x5d MSO_I, 0x5e MSO_I }, { 0x5f MSO_I, 0x60 MSO_I }, { 0x61 MSO_I, 0x62 MSO_I }, { 0x63 MSO_I, 0x64 MSO_I },
+ { 0x65 MSO_I, 0x66 MSO_I }, { 0x67 MSO_I, 0x68 MSO_I }, { 0x69 MSO_I, 0x6a MSO_I }, { 0x6b MSO_I, 0x6c MSO_I },
+ { 0x6d MSO_I, 0x6e MSO_I }, { 0x6f MSO_I, 0x70 MSO_I }, { 0x71 MSO_I, 0x72 MSO_I }, { 0x73 MSO_I, 0x74 MSO_I },
+ { 0x75 MSO_I, 0x76 MSO_I }, { 0x77 MSO_I, 0x78 MSO_I }, { 0x79 MSO_I, 0x7a MSO_I }, { 0x7b MSO_I, 0x7c MSO_I },
+ { 0x7d MSO_I, 0x7e MSO_I }, { 0x7f MSO_I, 0x80 MSO_I }, { 0x81 MSO_I, 0x82 MSO_I }, { 0x83 MSO_I, 0x84 MSO_I },
+ { 0x05 MSO_I, 0x06 MSO_I }
+};
+const mso_CustomShape msoSeal32 =
+{
+ const_cast<SvxMSDffVertPair*>(mso_sptSeal32Vert), SAL_N_ELEMENTS( mso_sptSeal32Vert ),
+ nullptr, 0,
+ const_cast<SvxMSDffCalculationData*>(mso_sptSeal32Calc), SAL_N_ELEMENTS( mso_sptSeal32Calc ),
+ const_cast<sal_Int32*>(mso_sptDefault2500),
+ const_cast<SvxMSDffTextRectangles*>(mso_sptSealTextRect), SAL_N_ELEMENTS( mso_sptSealTextRect ),
+ 21600, 21600,
+ MIN_INT32, MIN_INT32,
+ nullptr, 0,
+ const_cast<SvxMSDffHandle*>(mso_sptSealHandle), SAL_N_ELEMENTS( mso_sptSealHandle )
+};
+
+const SvxMSDffVertPair mso_sptRibbon2Vert[] = // adjustment1 : x 2700 - 8100 def 5400
+{ // adjustment2 : y 14400 - 21600 def 18900
+ { 12 MSO_I, 1 MSO_I }, { 12 MSO_I, 13 MSO_I }, // pp
+ { 12 MSO_I, 14 MSO_I }, { 15 MSO_I, 21600 }, { 16 MSO_I, 21600 }, // ccp
+ { 0, 21600 }, { 2750, 7 MSO_I }, { 0, 2 MSO_I }, { 0 MSO_I, 2 MSO_I }, // pppp
+ { 0 MSO_I, 4 MSO_I }, // p
+ { 0 MSO_I, 5 MSO_I }, { 10 MSO_I, 0 }, { 11 MSO_I, 0 }, // ccp
+ { 17 MSO_I, 0 }, // p
+ { 18 MSO_I, 0 }, { 19 MSO_I, 5 MSO_I }, { 19 MSO_I, 4 MSO_I }, // ccp
+ { 19 MSO_I, 2 MSO_I }, { 21600, 2 MSO_I }, { 18850, 7 MSO_I }, { 21600, 21600 }, // pppp
+ { 20 MSO_I, 21600 }, // p
+ { 21 MSO_I, 21600 }, { 22 MSO_I, 14 MSO_I }, { 22 MSO_I, 13 MSO_I }, // ccp
+ { 22 MSO_I, 1 MSO_I }, { 12 MSO_I, 1 MSO_I }, { 12 MSO_I, 13 MSO_I }, // ppp
+ { 12 MSO_I, 23 MSO_I }, { 15 MSO_I, 24 MSO_I }, { 16 MSO_I, 24 MSO_I }, // ccp
+ { 11 MSO_I, 24 MSO_I }, // p
+ { 10 MSO_I, 24 MSO_I }, { 0 MSO_I, 26 MSO_I }, { 0 MSO_I, 25 MSO_I }, // ccp
+ { 0 MSO_I, 27 MSO_I }, { 10 MSO_I, 1 MSO_I }, { 11 MSO_I, 1 MSO_I }, // ccp
+
+ { 22 MSO_I, 1 MSO_I }, { 22 MSO_I, 13 MSO_I }, // pp
+ { 22 MSO_I, 23 MSO_I }, { 21 MSO_I, 24 MSO_I }, { 20 MSO_I, 24 MSO_I }, // ccp
+ { 17 MSO_I, 24 MSO_I }, // p
+ { 18 MSO_I, 24 MSO_I }, { 19 MSO_I, 26 MSO_I }, { 19 MSO_I, 25 MSO_I }, // ccp
+ { 19 MSO_I, 27 MSO_I }, { 18 MSO_I, 1 MSO_I }, { 17 MSO_I, 1 MSO_I }, // ccp
+
+ { 0 MSO_I, 25 MSO_I }, { 0 MSO_I, 2 MSO_I }, // pp
+
+ { 19 MSO_I, 25 MSO_I }, { 19 MSO_I, 2 MSO_I } // pp
+};
+const sal_uInt16 mso_sptRibbon2Segm[] =
+{
+ 0x4000, 0x0001, 0x2001, 0x0005, 0x2001, 0x0001, 0x2001, 0x0005, 0x2001, 0x0001, 0x6001, 0x8000,
+ 0x4000, 0x0001, 0x2001, 0x0001, 0x2002, 0x6001, 0x8000,
+ 0x4000, 0x0001, 0x2001, 0x0001, 0x2002, 0x6001, 0x8000,
+ 0x4000, 0x0001, 0x8000,
+ 0x4000, 0x0001, 0x8000
+};
+const SvxMSDffCalculationData mso_sptRibbon2Calc[] =
+{
+ { 0x2000, { DFF_Prop_adjustValue, 0, 0 } }, // 00
+ { 0x2000, { DFF_Prop_adjust2Value, 0, 0 } }, // 01
+ { 0x8000, { 21600, 0, 0x401 } }, // 02
+ { 0x2001, { 0x402, 1, 2 } }, // 03
+ { 0x2001, { 0x403, 1, 2 } }, // 04
+ { 0x2001, { 0x404, 1, 2 } }, // 05
+ { 0x2001, { 0x401, 1, 2 } }, // 06
+ { 0x8000, { 21600, 0, 0x406 } }, // 07
+ { 0x0000, { 420, 0, 0 } }, // 08
+ { 0x2001, { 0x408, 2, 1 } }, // 09
+ { 0x6000, { 0x400, 0x408, 0 } }, // 10
+ { 0x6000, { 0x400, 0x409, 0 } }, // 11
+ { 0x2000, { 0x400, 2700, 0 } }, // 12
+ { 0x8000, { 21600, 0, 0x404 } }, // 13
+ { 0x8000, { 21600, 0, 0x405 } }, // 14
+ { 0xa000, { 0x40c, 0, 0x408 } }, // 15
+ { 0xa000, { 0x40c, 0, 0x409 } }, // 16
+
+ { 0x8000, { 21600, 0, 0x40b } }, // 17
+ { 0x8000, { 21600, 0, 0x40a } }, // 18
+ { 0x8000, { 21600, 0, 0x400 } }, // 19
+ { 0x8000, { 21600, 0, 0x410 } }, // 20
+ { 0x8000, { 21600, 0, 0x40f } }, // 21
+ { 0x8000, { 21600, 0, 0x40c } }, // 22
+
+ { 0xa000, { 0x40d, 0, 0x405 } }, // 23
+ { 0x6000, { 0x401, 0x403, 0 } }, // 24
+ { 0x6000, { 0x401, 0x404, 0 } }, // 25
+ { 0x6000, { 0x419, 0x405, 0 } }, // 26
+ { 0xa000, { 0x419, 0, 0x405 } } // 27
+};
+const sal_Int32 mso_sptRibbon2Default[] =
+{
+ 2, 5400, 18900
+};
+const SvxMSDffTextRectangles mso_sptRibbon2TextRect[] =
+{
+ { { 0 MSO_I, 0 }, { 19 MSO_I, 1 MSO_I } }
+};
+const SvxMSDffHandle mso_sptRibbon2Handle[] =
+{
+ { SvxMSDffHandleFlags::RANGE,
+ 0x100, 0, 10800, 10800, 2700, 8100, MIN_INT32, 0x7fffffff },
+ { SvxMSDffHandleFlags::RANGE,
+ 10800, 0x101, 10800, 10800, MIN_INT32, 0x7fffffff, 14400, 21600 }
+};
+const mso_CustomShape msoRibbon2 =
+{
+ const_cast<SvxMSDffVertPair*>(mso_sptRibbon2Vert), SAL_N_ELEMENTS( mso_sptRibbon2Vert ),
+ const_cast<sal_uInt16*>(mso_sptRibbon2Segm), sizeof( mso_sptRibbon2Segm ) >> 1,
+ const_cast<SvxMSDffCalculationData*>(mso_sptRibbon2Calc), SAL_N_ELEMENTS( mso_sptRibbon2Calc ),
+ const_cast<sal_Int32*>(mso_sptRibbon2Default),
+ const_cast<SvxMSDffTextRectangles*>(mso_sptRibbon2TextRect), SAL_N_ELEMENTS( mso_sptRibbon2TextRect ),
+ 21600, 21600,
+ MIN_INT32, MIN_INT32,
+ nullptr, 0,
+ const_cast<SvxMSDffHandle*>(mso_sptRibbon2Handle), SAL_N_ELEMENTS( mso_sptRibbon2Handle )
+};
+
+const SvxMSDffVertPair mso_sptRibbonVert[] =
+{
+ { 0, 0 }, { 3 MSO_I, 0 },
+ { 4 MSO_I, 11 MSO_I }, { 4 MSO_I, 10 MSO_I }, { 5 MSO_I, 10 MSO_I }, { 5 MSO_I, 11 MSO_I },
+ { 6 MSO_I, 0 }, { 21600, 0 }, { 18 MSO_I, 14 MSO_I }, { 21600, 15 MSO_I }, { 9 MSO_I, 15 MSO_I }, { 9 MSO_I, 16 MSO_I }, { 8 MSO_I, 21600 }, { 1 MSO_I, 21600 },
+ { 0 MSO_I, 16 MSO_I }, { 0 MSO_I, 15 MSO_I }, { 0, 15 MSO_I }, { 2700, 14 MSO_I },
+
+ { 4 MSO_I, 11 MSO_I },
+ { 3 MSO_I, 12 MSO_I }, { 1 MSO_I, 12 MSO_I },
+ { 0 MSO_I, 13 MSO_I }, { 1 MSO_I, 10 MSO_I }, { 4 MSO_I, 10 MSO_I },
+ { 5 MSO_I, 11 MSO_I },
+ { 6 MSO_I, 12 MSO_I }, { 8 MSO_I, 12 MSO_I },
+ { 9 MSO_I, 13 MSO_I }, { 8 MSO_I, 10 MSO_I }, { 5 MSO_I, 10 MSO_I },
+ { 0 MSO_I, 13 MSO_I },
+ { 0 MSO_I, 15 MSO_I },
+ { 9 MSO_I, 13 MSO_I },
+ { 9 MSO_I, 15 MSO_I }
+};
+const sal_uInt16 mso_sptRibbonSegm[] =
+{
+ 0x4000, 0x0001, 0xa701, 0x0003, 0xa801, 0x0005, 0xa801, 0x0001, 0xa701, 0x0003, 0x6000, 0x8000,
+ 0x4000, 0xaa00, 0xa801, 0x0001, 0xa702, 0x0001, 0x8000,
+ 0x4000, 0xaa00, 0xa801, 0x0001, 0xa702, 0x0001, 0x8000,
+ 0x4000, 0xaa00, 0x0001, 0x8000,
+ 0x4000, 0xaa00, 0x0001, 0x8000
+};
+const SvxMSDffCalculationData mso_sptRibbonCalc[] =
+{
+ { 0x2000, { DFF_Prop_adjustValue, 0, 0 } }, // 00
+ { 0x2000, { 0x400, 675, 0 } }, // 01
+ { 0x2000, { 0x401, 675, 0 } }, // 02
+ { 0x2000, { 0x402, 675, 0 } }, // 03
+ { 0x2000, { 0x403, 675, 0 } }, // 04
+ { 0x8000, { 21600, 0, 0x404 } }, // 05
+ { 0x8000, { 21600, 0, 0x403 } }, // 06
+ { 0x8000, { 21600, 0, 0x402 } }, // 07
+ { 0x8000, { 21600, 0, 0x401 } }, // 08
+ { 0x8000, { 21600, 0, 0x400 } }, // 09
+ { 0x2000, { DFF_Prop_adjust2Value, 0, 0 } }, // 10
+ { 0x2001, { 0x40a, 1, 4 } }, // 11
+ { 0x2001, { 0x40b, 2, 1 } }, // 12
+ { 0x2001, { 0x40b, 3, 1 } }, // 13
+ { 0x8000, { 10800, 0, 0x40c } }, // 14
+ { 0x8000, { 21600, 0, 0x40a } }, // 15
+ { 0x8000, { 21600, 0, 0x40b } }, // 16
+ { 0x0001, { 21600, 1, 2 } }, // 17
+ { 0x0000, { 21600, 0, 2700 } }, // 18
+ { 0x2000, { 0x411, 0, 2700 } } // 19
+};
+const sal_Int32 mso_sptRibbonDefault[] =
+{
+ 2, 5400, 2700
+};
+const SvxMSDffTextRectangles mso_sptRibbonTextRect[] =
+{
+ { { 0 MSO_I, 10 MSO_I }, { 9 MSO_I, 21600 } }
+};
+const SvxMSDffVertPair mso_sptRibbonGluePoints[] =
+{
+ { 17 MSO_I, 10 MSO_I }, { 2700, 14 MSO_I }, { 17 MSO_I, 21600 }, { 18 MSO_I, 14 MSO_I }
+};
+const SvxMSDffHandle mso_sptRibbonHandle[] =
+{
+ { SvxMSDffHandleFlags::RANGE,
+ 0x100, 0, 10800, 10800, 2700, 8100, MIN_INT32, 0x7fffffff },
+ { SvxMSDffHandleFlags::RANGE,
+ 10800, 0x101, 10800, 10800, MIN_INT32, 0x7fffffff, 0, 7200 }
+};
+const mso_CustomShape msoRibbon =
+{
+ const_cast<SvxMSDffVertPair*>(mso_sptRibbonVert), SAL_N_ELEMENTS( mso_sptRibbonVert ),
+ const_cast<sal_uInt16*>(mso_sptRibbonSegm), sizeof( mso_sptRibbonSegm ) >> 1,
+ const_cast<SvxMSDffCalculationData*>(mso_sptRibbonCalc), SAL_N_ELEMENTS( mso_sptRibbonCalc ),
+ const_cast<sal_Int32*>(mso_sptRibbonDefault),
+ const_cast<SvxMSDffTextRectangles*>(mso_sptRibbonTextRect), SAL_N_ELEMENTS( mso_sptRibbonTextRect ),
+ 21600, 21600,
+ MIN_INT32, MIN_INT32,
+ const_cast<SvxMSDffVertPair*>(mso_sptRibbonGluePoints), SAL_N_ELEMENTS( mso_sptRibbonGluePoints ),
+ const_cast<SvxMSDffHandle*>(mso_sptRibbonHandle), SAL_N_ELEMENTS( mso_sptRibbonHandle )
+};
+//msosptEllipseRibbon
+//svg path = ar@9@38@8@37,0@27@0@26@9@13@8@4@0@25@22@25@9@38@8@37@22@26@3@27l@7@40@3,wa@9@35@8@10@3,0@21@33@9@36@8@1@21@31@20@31@9@35@8@10@20@33,,l@5@40xewr@9@36@8@1@20@31@0@32nfl@20@33ear@9@36@8@1@21@31@22@32nfl@21@33em@0@26nfl@0@32em@22@26nfl@22@32e
+//odp path = A ?f9 ?f38 ?f8 ?f37 0 ?f27 ?f0 ?f26 ?f9 ?f13 ?f8 ?f4 ?f0 ?f25 ?f22 ?f25 ?f9 ?f38 ?f8 ?f37 ?f22 ?f26 ?f3 ?f27 L ?f7 ?f40 ?f3 0 W ?f9 ?f35 ?f8 ?f10 ?f3 0 ?f21 ?f33 ?f9 ?f36 ?f8 ?f1 ?f21 ?f31 ?f20 ?f31 ?f9 ?f35 ?f8 ?f10 ?f20 ?f33 0 0 L ?f5 ?f40 Z N W ?f9 ?f36 ?f8 ?f1 ?f20 ?f31 ?f0 ?f32 F L ?f20 ?f33 N A ?f9 ?f36 ?f8 ?f1 ?f21 ?f31 ?f22 ?f32 F L ?f21 ?f33 N M ?f0 ?f26 F L ?f0 ?f32 N M ?f22 ?f26 F L ?f22 ?f32 N
+const SvxMSDffVertPair mso_sptEllipseRibbonVert[] =
+{
+ { 9 MSO_I , 38 MSO_I },
+ { 8 MSO_I , 37 MSO_I },
+ { 0 , 27 MSO_I },
+ { 0 MSO_I , 26 MSO_I },
+ { 9 MSO_I , 13 MSO_I },
+ { 8 MSO_I , 4 MSO_I },
+ { 0 MSO_I , 25 MSO_I },
+ { 22 MSO_I , 25 MSO_I },
+ { 9 MSO_I , 38 MSO_I },
+ { 8 MSO_I , 37 MSO_I },
+ { 22 MSO_I , 26 MSO_I },
+ { 3 MSO_I , 27 MSO_I },
+ { 7 MSO_I , 40 MSO_I },
+ { 3 MSO_I , 0 },
+ { 9 MSO_I , 35 MSO_I },
+ { 8 MSO_I , 10 MSO_I },
+ { 3 MSO_I , 0 },
+ { 21 MSO_I , 33 MSO_I },
+ { 9 MSO_I , 36 MSO_I },
+ { 8 MSO_I , 1 MSO_I },
+ { 21 MSO_I , 31 MSO_I },
+ { 20 MSO_I , 31 MSO_I },
+ { 9 MSO_I , 35 MSO_I },
+ { 8 MSO_I , 10 MSO_I },
+ { 20 MSO_I , 33 MSO_I },
+ { 0 , 0 },
+ { 5 MSO_I , 40 MSO_I },
+ { 9 MSO_I , 36 MSO_I },
+ { 8 MSO_I , 1 MSO_I },
+ { 20 MSO_I , 31 MSO_I },
+ { 0 MSO_I , 32 MSO_I },
+ { 20 MSO_I , 33 MSO_I },
+ { 9 MSO_I , 36 MSO_I },
+ { 8 MSO_I , 1 MSO_I },
+ { 21 MSO_I , 31 MSO_I },
+ { 22 MSO_I , 32 MSO_I },
+ { 21 MSO_I , 33 MSO_I },
+ { 0 MSO_I , 26 MSO_I },
+ { 0 MSO_I , 32 MSO_I },
+ { 22 MSO_I , 26 MSO_I },
+ { 22 MSO_I , 32 MSO_I }
+};
+
+const sal_uInt16 mso_sptEllipseRibbonSegm[] =
+{
+ 0xa30c /*ar*/,0x0002/*l*/,0xa50c/*wa*/,0x0001/*l*/,
+ 0x6001/*x*/, 0x8000/*e*/,0xa504/*wr*/,0xaa00/*nf*/,
+ 0x0001/*l*/, 0x8000/*e*/,0xa304/*ar*/,0xaa00/*nf*/,
+ 0x0001/*l*/, 0x8000/*e*/,0x4000/*m*/,0xaa00/*nf*/,
+ 0x0001/*l*/, 0x8000/*e*/,0x4000/*m*/,0xaa00/*nf*/,
+ 0x0001/*l*/, 0x8000/*e*/
+};
+
+const SvxMSDffCalculationData mso_sptEllipseRibbonCalc[] =
+{
+ { 0x2000 , { DFF_Prop_adjustValue , 0 , 0 } }, //val #0
+ { 0x2000 , { DFF_Prop_adjust2Value , 0 , 0 } }, //val #1
+ { 0x2000 , { DFF_Prop_adjust3Value , 0 , 0 } }, //val #2
+ { 0x2000 , { DFF_Prop_geoRight , 0 , 0 } }, //val width
+ { 0x2000 , { DFF_Prop_geoBottom , 0 , 0 } }, //val height
+ { 0x2001 , { DFF_Prop_geoRight , 1 , 8 } }, //prod width 1 8
+ { 0x2001 , { DFF_Prop_geoRight , 1 , 2 } }, //prod width 1 2
+ { 0x2001 , { DFF_Prop_geoRight , 7 , 8 } }, //prod width 7 8
+ { 0x2001 , { DFF_Prop_geoRight , 3 , 2 } }, //prod width 3 2
+ { 0x8000 , { 0 , 0 , 0x406 } }, //sum 0 0 @6
+ { 0xa000 , { DFF_Prop_geoBottom , 0 , DFF_Prop_adjust3Value } }, //sum height 0 #2
+ { 0x2001 , { 0x40a , 30573 , 4096 } }, //prod @10 30573 4096
+ { 0x2001 , { 0x40b , 2 , 1 } }, //prod @11 2 1
+ { 0xa000 , { DFF_Prop_geoBottom , 0 , 0x40c } }, //sum height 0 @12
+ { 0x6000 , { 0x40b , DFF_Prop_adjust3Value , 0 } }, //sum @11 #2 0
+ { 0xe000 , { 0x40b , DFF_Prop_geoBottom , DFF_Prop_adjust2Value } }, //sum @11 height #1
+ { 0xa000 , { DFF_Prop_geoBottom , 0 , DFF_Prop_adjust2Value } }, //sum height 0 #1
+ { 0x2001 , { 0x410 , 1 , 2 } }, //prod @16 1 2
+ { 0x6000 , { 0x40b , 0x411 , 0 } }, //sum @11 @17 0
+ { 0xe000 , { 0x40e , DFF_Prop_adjust2Value , DFF_Prop_geoBottom } }, //sum @14 #1 height
+ { 0x6000 , { DFF_Prop_adjustValue , 0x405 , 0 } }, //sum #0 @5 0
+ { 0xa000 , { DFF_Prop_geoRight , 0 , 0x414 } }, //sum width 0 @20
+ { 0xa000 , { DFF_Prop_geoRight , 0 , DFF_Prop_adjustValue } }, //sum width 0 #0
+ { 0xa000 , { 0x406 , 0 , DFF_Prop_adjustValue } }, //sum @6 0 #0
+ { 0xe00f , { 0x417 , DFF_Prop_geoRight , 0x40b } }, //ellipse @23 width @11
+ { 0xe000 , { 0x418 , DFF_Prop_geoBottom , 0x40b } }, //sum @24 height @11
+ { 0xe000 , { 0x419 , 0x40b , 0x413 } }, //sum @25 @11 @19
+ { 0xe000 , { DFF_Prop_adjust3Value , 0x40b , 0x413 } }, //sum #2 @11 @19
+ { 0x2001 , { 0x40b , 2391 , 32768 } }, //prod @11 2391 32768
+ { 0xa000 , { 0x406 , 0 , 0x414 } }, //sum @6 0 @20
+ { 0xe00f , { 0x41d , DFF_Prop_geoRight , 0x40b } }, //ellipse @29 width @11
+ { 0xe000 , { DFF_Prop_adjust2Value , 0x41e , 0x40b } }, //sum #1 @30 @11
+ { 0xe000 , { 0x419 , DFF_Prop_adjust2Value , DFF_Prop_geoBottom } }, //sum @25 #1 height
+ { 0xe000 , { DFF_Prop_geoBottom , 0x41e , 0x40e } }, //sum height @30 @14
+ { 0x6000 , { 0x40b , 0x40e , 0 } }, //sum @11 @14 0
+ { 0xa000 , { DFF_Prop_geoBottom , 0 , 0x422 } }, //sum height 0 @34
+ { 0xe000 , { 0x423 , 0x413 , 0x40b } }, //sum @35 @19 @11
+ { 0xe000 , { 0x40a , 0x40f , 0x40b } }, //sum @10 @15 @11
+ { 0xe000 , { 0x423 , 0x40f , 0x40b } }, //sum @35 @15 @11
+ { 0xe000 , { 0x41c , 0x40e , 0x412 } }, //sum @28 @14 @18
+ { 0xa000 , { DFF_Prop_geoBottom , 0 , 0x427 } }, //sum height 0 @39
+ { 0xa000 , { 0x413 , 0 , 0x412 } }, //sum @19 0 @18
+ { 0x2001 , { 0x429 , 2 , 3 } }, //prod @41 2 3
+ { 0xa000 , { DFF_Prop_adjust2Value , 0 , 0x42a } }, //sum #1 0 @42
+ { 0xa000 , { DFF_Prop_adjust3Value , 0 , 0x42a } }, //sum #2 0 @42
+ { 0x2004 , { 0x42c , 20925 , 0 } }, //min @44 20925
+ { 0x2001 , { DFF_Prop_geoRight , 3 , 8 } }, //prod width 3 8
+ { 0x2000 , { 0x42e , 0 , 4 } }, //sum @46 0 4
+};
+
+const SvxMSDffTextRectangles mso_sptEllipseRibbonTextRect[] =
+{//@0,@1,@22,@25
+ { { 0 MSO_I, 1 MSO_I }, { 22 MSO_I, 25 MSO_I } }
+};
+
+const sal_Int32 mso_sptEllipseRibbonDefault[] =
+{
+ 3,5400,5400,18900
+};
+
+const SvxMSDffHandle mso_sptEllipseRibbonHandle[] =
+{
+//position="#0,bottomRight" xrange="@5,@47
+//position="center,#1" yrange="@10,@43
+//position="topLeft,#2" yrange="@27,@45
+ { SvxMSDffHandleFlags::RANGE | SvxMSDffHandleFlags::RANGE_X_MAX_IS_SPECIAL| SvxMSDffHandleFlags::RANGE_X_MIN_IS_SPECIAL,
+ 0x100, 21600, 0, 0, 0x8/*5+3*/, 0x32/*47+3*/, MIN_INT32, 0x7fffffff },
+ { SvxMSDffHandleFlags::RANGE | SvxMSDffHandleFlags::RANGE_Y_MAX_IS_SPECIAL | SvxMSDffHandleFlags::RANGE_Y_MIN_IS_SPECIAL,
+ 10800, 0x101, 0, 0, MIN_INT32, 0x7fffffff,0xd/*10+3*/, 0x2e /*43+3*/ },
+ { SvxMSDffHandleFlags::RANGE | SvxMSDffHandleFlags::RANGE_Y_MAX_IS_SPECIAL | SvxMSDffHandleFlags::RANGE_Y_MIN_IS_SPECIAL,
+ 0, 0x102, 0, 0, MIN_INT32, 0x7fffffff,0x1e/*27+3*/, 0x30 /*45+3*/ }
+};
+
+const mso_CustomShape msosptEllipseRibbon =
+{
+ const_cast<SvxMSDffVertPair*>(mso_sptEllipseRibbonVert), SAL_N_ELEMENTS( mso_sptEllipseRibbonVert ),
+ const_cast<sal_uInt16*>(mso_sptEllipseRibbonSegm), sizeof( mso_sptEllipseRibbonSegm ) >> 1,
+ const_cast<SvxMSDffCalculationData*>(mso_sptEllipseRibbonCalc), SAL_N_ELEMENTS( mso_sptEllipseRibbonCalc ),
+ const_cast<sal_Int32*>(mso_sptEllipseRibbonDefault),
+ const_cast<SvxMSDffTextRectangles*>(mso_sptEllipseRibbonTextRect), SAL_N_ELEMENTS( mso_sptEllipseRibbonTextRect ),
+ 21600, 21600,
+ MIN_INT32, MIN_INT32,
+ nullptr, 0,
+ const_cast<SvxMSDffHandle*>(mso_sptEllipseRibbonHandle), SAL_N_ELEMENTS( mso_sptEllipseRibbonHandle )
+};
+
+//msosptEllipseRibbon2
+//svg path = wr@9@34@8@35,0@24@0@23@9,0@8@11@0@22@19@22@9@34@8@35@19@23@3@24l@7@36@3@4at@9@31@8@32@3@4@18@30@9@1@8@33@18@28@17@28@9@31@8@32@17@30,0@4l@5@36xear@9@1@8@33@17@28@0@29nfl@17@30ewr@9@1@8@33@18@28@19@29nfl@18@30em@0@23nfl@0@29em@19@23nfl@19@29e
+const SvxMSDffVertPair mso_sptEllipseRibbon2Vert[] =
+{
+ { 9 MSO_I , 34 MSO_I },
+ { 8 MSO_I , 35 MSO_I },
+ { 0 , 24 MSO_I },
+ { 0 MSO_I , 23 MSO_I },
+ { 9 MSO_I , 0 },
+ { 8 MSO_I , 11 MSO_I },
+ { 0 MSO_I , 22 MSO_I },
+ { 19 MSO_I , 22 MSO_I },
+ { 9 MSO_I , 34 MSO_I },
+ { 8 MSO_I , 35 MSO_I },
+ { 19 MSO_I , 23 MSO_I },
+ { 3 MSO_I , 24 MSO_I },
+ { 7 MSO_I , 36 MSO_I },
+ { 3 MSO_I , 4 MSO_I },
+ { 9 MSO_I , 31 MSO_I },
+ { 8 MSO_I , 32 MSO_I },
+ { 3 MSO_I , 4 MSO_I },
+ { 18 MSO_I , 30 MSO_I },
+ { 9 MSO_I , 1 MSO_I },
+ { 8 MSO_I , 33 MSO_I },
+ { 18 MSO_I , 28 MSO_I },
+ { 17 MSO_I , 28 MSO_I },
+ { 9 MSO_I , 31 MSO_I },
+ { 8 MSO_I , 32 MSO_I },
+ { 17 MSO_I , 30 MSO_I },
+ { 0 , 4l MSO_I },
+ { 5 MSO_I , 36 MSO_I },
+ { 9 MSO_I , 1 MSO_I },
+ { 8 MSO_I , 33 MSO_I },
+ { 17 MSO_I , 28 MSO_I },
+ { 0 MSO_I , 29 MSO_I },
+ { 17 MSO_I , 30 MSO_I },
+ { 9 MSO_I , 1 MSO_I },
+ { 8 MSO_I , 33 MSO_I },
+ { 18 MSO_I , 28 MSO_I },
+ { 19 MSO_I , 29 MSO_I },
+ { 18 MSO_I , 30 MSO_I },
+ { 0 MSO_I , 23 MSO_I },
+ { 0 MSO_I , 29 MSO_I },
+ { 19 MSO_I , 23 MSO_I },
+ { 19 MSO_I , 29 MSO_I }
+};
+const sal_uInt16 mso_sptEllipseRibbon2Segm[] =
+{
+ 0xa50c/*wr*/,0x0002/*l*/,0xa30c/*at*/,0x0001/*l*/,
+ 0x6001/*x*/, 0x8000/*e*/,0xa304/*ar*/,0xaa00/*nf*/,
+ 0x0001/*l*/, 0x8000/*e*/,0xa504/*wr*/,0xaa00/*nf*/,
+ 0x0001/*l*/, 0x8000/*e*/,0x4000/*m*/,0xaa00/*nf*/,
+ 0x0001/*l*/, 0x8000/*e*/,0x4000/*m*/,0xaa00/*nf*/,
+ 0x0001/*l*/, 0x8000/*e*/
+};
+
+const SvxMSDffCalculationData mso_sptEllipseRibbon2Calc[] =
+{
+ { 0x2000 , { DFF_Prop_adjustValue , 0 , 0 } }, //val #0
+ { 0x2000 , { DFF_Prop_adjust2Value , 0 , 0 } }, //val #1
+ { 0x2000 , { DFF_Prop_adjust3Value , 0 , 0 } }, //val #2
+ { 0x2000 , { DFF_Prop_geoRight , 0 , 0 } }, //val width
+ { 0x2000 , { DFF_Prop_geoBottom , 0 , 0 } }, //val height
+ { 0x2001 , { DFF_Prop_geoRight , 1 , 8 } }, //prod width 1 8
+ { 0x2001 , { DFF_Prop_geoRight , 1 , 2 } }, //prod width 1 2
+ { 0x2001 , { DFF_Prop_geoRight , 7 , 8 } }, //prod width 7 8
+ { 0x2001 , { DFF_Prop_geoRight , 3 , 2 } }, //prod width 3 2
+ { 0x8000 , { 0 , 0 , 0x406 } }, //sum 0 0 @6
+ { 0x2001 , { DFF_Prop_adjust3Value , 30573 , 4096 } }, //prod #2 30573 4096
+ { 0x2001 , { 0x40a , 2 , 1 } }, //prod @10 2 1
+ { 0xe000 , { 0x40a , DFF_Prop_geoBottom , DFF_Prop_adjust3Value } }, //sum @10 height #2
+ { 0x6000 , { 0x40a , DFF_Prop_adjust2Value , 0 } }, //sum @10 #1 0
+ { 0x2001 , { DFF_Prop_adjust2Value , 1 , 2 } }, //prod #1 1 2
+ { 0x6000 , { 0x40a , 0x40e , 0 } }, //sum @10 @14 0
+ { 0xa000 , { 0x40c , 0 , DFF_Prop_adjust2Value } }, //sum @12 0 #1
+ { 0x6000 , { DFF_Prop_adjustValue , 0x405 , 0 } }, //sum #0 @5 0
+ { 0xa000 , { DFF_Prop_geoRight , 0 , 0x411 } }, //sum width 0 @17
+ { 0xa000 , { DFF_Prop_geoRight , 0 , DFF_Prop_adjustValue } }, //sum width 0 #0
+ { 0xa000 , { 0x406 , 0 , DFF_Prop_adjustValue } }, //sum @6 0 #0
+ { 0xe00f , { 0x414 , DFF_Prop_geoRight , 0x40a } }, //ellipse @20 width @10
+ { 0xa000 , { 0x40a , 0 , 0x415 } }, //sum @10 0 @21
+ { 0xe000 , { 0x416 , 0x410 , 0x40a } }, //sum @22 @16 @10
+ { 0xe000 , { DFF_Prop_adjust3Value , 0x410 , 0x40a } }, //sum #2 @16 @10
+ { 0x2001 , { 0x40a , 2391 , 32768 } }, //prod @10 2391 32768
+ { 0xa000 , { 0x406 , 0 , 0x411 } }, //sum @6 0 @17
+ { 0xe00f , { 0x41a , DFF_Prop_geoRight , 0x40a } }, //ellipse @26 width @10
+ { 0xe000 , { 0x40a , DFF_Prop_adjust2Value , 0x41b } }, //sum @10 #1 @27
+ { 0x6000 , { 0x416 , DFF_Prop_adjust2Value , 0 } }, //sum @22 #1 0
+ { 0xa000 , { 0x40c , 0 , 0x41b } }, //sum @12 0 @27
+ { 0xa000 , { DFF_Prop_geoBottom , 0 , DFF_Prop_adjust3Value } }, //sum height 0 #2
+ { 0x6000 , { 0x40a , 0x40c , 0 } }, //sum @10 @12 0
+ { 0xe000 , { 0x420 , 0x40a , 0x410 } }, //sum @32 @10 @16
+ { 0xe000 , { 0x41f , 0x40a , 0x40d } }, //sum @31 @10 @13
+ { 0xe000 , { 0x420 , 0x40a , 0x40d } }, //sum @32 @10 @13
+ { 0xe000 , { 0x419 , 0x40c , 0x40f } }, //sum @25 @12 @15
+ { 0xa000 , { 0x410 , 0 , 0x40f } }, //sum @16 0 @15
+ { 0x2001 , { 0x425 , 2 , 3 } }, //prod @37 2 3
+ { 0x6000 , { 0x401 , 0x426 , 0 } }, //sum @1 @38 0
+ { 0x6000 , { DFF_Prop_adjust3Value , 0x426 , 0 } }, //sum #2 @38 0
+ { 0x2005 , { 0x428 , 675 , 0 } }, //max @40 675
+ { 0x2001 , { DFF_Prop_geoRight , 3 , 8 } }, //prod width 3 8
+ { 0x2000 , { 0x42a , 0 , 4 } } //sum @42 0 4
+};
+
+const SvxMSDffTextRectangles mso_sptEllipseRibbon2TextRect[] =
+{//@0,@22,@19,@1
+ { { 0 MSO_I, 22 MSO_I }, { 19 MSO_I, 1 MSO_I } }
+};
+
+const sal_Int32 mso_sptEllipseRibbon2Default[] =
+{
+ 3,5400,16200,2700
+};
+
+const SvxMSDffHandle mso_sptEllipseRibbon2Handle[] =
+{
+//position="#0,topLeft" xrange="@5,@43
+//position="center,#1" yrange="@39,@31
+//position="topLeft,#2" yrange="@41,@24
+ { SvxMSDffHandleFlags::RANGE | SvxMSDffHandleFlags::RANGE_X_MAX_IS_SPECIAL| SvxMSDffHandleFlags::RANGE_X_MIN_IS_SPECIAL,
+ 0x100, 0, 0, 0, 0x8/*5+3*/, 0x2e/*43+3*/, MIN_INT32, 0x7fffffff },
+ { SvxMSDffHandleFlags::RANGE | SvxMSDffHandleFlags::RANGE_Y_MAX_IS_SPECIAL | SvxMSDffHandleFlags::RANGE_Y_MIN_IS_SPECIAL,
+ 10800, 0x101, 0, 0, MIN_INT32, 0x7fffffff,0x2a/*39+3*/, 0x22 /*31+3*/ },
+ { SvxMSDffHandleFlags::RANGE | SvxMSDffHandleFlags::RANGE_Y_MAX_IS_SPECIAL | SvxMSDffHandleFlags::RANGE_Y_MIN_IS_SPECIAL,
+ 0, 0x102, 0, 0, MIN_INT32, 0x7fffffff,0x2c/*41+3*/, 0x1b /*24+3*/ }
+};
+
+const mso_CustomShape msosptEllipseRibbon2 =
+{
+ const_cast<SvxMSDffVertPair*>(mso_sptEllipseRibbon2Vert), SAL_N_ELEMENTS( mso_sptEllipseRibbon2Vert ),
+ const_cast<sal_uInt16*>(mso_sptEllipseRibbon2Segm), sizeof( mso_sptEllipseRibbon2Segm ) >> 1,
+ const_cast<SvxMSDffCalculationData*>(mso_sptEllipseRibbon2Calc), SAL_N_ELEMENTS( mso_sptEllipseRibbon2Calc ),
+ const_cast<sal_Int32*>(mso_sptEllipseRibbon2Default),
+ const_cast<SvxMSDffTextRectangles*>(mso_sptEllipseRibbon2TextRect), SAL_N_ELEMENTS( mso_sptEllipseRibbon2TextRect ),
+ 21600, 21600,
+ MIN_INT32, MIN_INT32,
+ nullptr, 0,
+ const_cast<SvxMSDffHandle*>(mso_sptEllipseRibbon2Handle), SAL_N_ELEMENTS( mso_sptEllipseRibbon2Handle )
+};
+// End
+const SvxMSDffVertPair mso_sptVerticalScrollVert[] = // adjustment1 : 0 - 5400
+{
+ { 1 MSO_I, 21600 }, { 0, 11 MSO_I }, { 1 MSO_I, 12 MSO_I }, { 0 MSO_I, 12 MSO_I },
+ { 0 MSO_I, 1 MSO_I }, { 4 MSO_I, 0 }, { 2 MSO_I, 0 }, { 21600, 1 MSO_I },
+ { 2 MSO_I, 0 MSO_I }, { 3 MSO_I, 0 MSO_I }, { 3 MSO_I, 11 MSO_I }, { 5 MSO_I, 21600 },
+
+ { 6 MSO_I, 1 MSO_I }, { 4 MSO_I, 0 MSO_I }, { 8 MSO_I, 9 MSO_I }, { 4 MSO_I, 1 MSO_I },
+
+ { 0 MSO_I, 11 MSO_I }, { 1 MSO_I, 21600 }, { 0, 11 MSO_I }, { 1 MSO_I, 12 MSO_I },
+ { 9 MSO_I, 10 MSO_I }, { 1 MSO_I, 11 MSO_I },
+
+ { 4 MSO_I, 0 }, { 6 MSO_I, 1 MSO_I },
+
+ { 0 MSO_I, 12 MSO_I }, { 0 MSO_I, 11 MSO_I },
+
+ { 4 MSO_I, 0 MSO_I },
+ { 2 MSO_I, 0 MSO_I }
+};
+const sal_uInt16 mso_sptVerticalScrollSegm[] =
+{
+ 0x4000, 0xa702, 0x0002, 0xa801, 0x0001, 0xa702, 0x0002, 0xa801, 0x6001, 0x8000,
+ 0x4000, 0xa801, 0xa702, 0x6000, 0x8000,
+ 0x4000, 0xa803, 0xa702, 0x6001, 0x8000,
+ 0x4000, 0xa701, 0x8000,
+ 0x4000, 0x0001, 0x8000,
+ 0x4000, 0x0001, 0x8000
+};
+const SvxMSDffCalculationData mso_sptScrollCalc[] =
+{
+ { 0x2000, { DFF_Prop_adjustValue, 0, 0 } },
+ { 0x2001, { 0x400, 1, 2 } },
+ { 0xa000, { DFF_Prop_geoRight, 0, 0x401 } },
+ { 0xa000, { DFF_Prop_geoRight, 0, 0x400 } },
+ { 0x6000, { 0x400, 0x401, 0 } },
+ { 0xa000, { DFF_Prop_geoRight, 0, 0x404 } },
+ { 0x2001, { 0x400, 2, 1 } },
+ { 0x2001, { 0x401, 1, 2 } },
+ { 0x6000, { 0x400, 0x407, 0 } },
+ { 0x6000, { 0x401, 0x407, 0 } },
+ { 0xa000, { DFF_Prop_geoBottom, 0, 0x409 } },
+ { 0xa000, { DFF_Prop_geoBottom, 0, 0x401 } },
+ { 0xa000, { DFF_Prop_geoBottom, 0, 0x400 } },
+ { 0xa000, { DFF_Prop_geoBottom, 0, 0x404 } }
+};
+const SvxMSDffTextRectangles mso_sptScrollTextRect[] =
+{
+ { { 0 MSO_I, 0 MSO_I }, { 3 MSO_I, 12 MSO_I } }
+};
+const SvxMSDffHandle mso_sptVerticalScrollHandle[] =
+{
+ { SvxMSDffHandleFlags::RANGE,
+ 0, 0x100, 10800, 10800, MIN_INT32, 0x7fffffff, 0, 5400 }
+};
+const mso_CustomShape msoVerticalScroll =
+{
+ const_cast<SvxMSDffVertPair*>(mso_sptVerticalScrollVert), SAL_N_ELEMENTS( mso_sptVerticalScrollVert ),
+ const_cast<sal_uInt16*>(mso_sptVerticalScrollSegm), sizeof( mso_sptVerticalScrollSegm ) >> 1,
+ const_cast<SvxMSDffCalculationData*>(mso_sptScrollCalc), SAL_N_ELEMENTS( mso_sptScrollCalc ),
+ const_cast<sal_Int32*>(mso_sptDefault2700),
+ const_cast<SvxMSDffTextRectangles*>(mso_sptScrollTextRect), SAL_N_ELEMENTS( mso_sptScrollTextRect ),
+ 21600, 21600,
+ 11000, 10800,
+ nullptr, 0,
+ const_cast<SvxMSDffHandle*>(mso_sptVerticalScrollHandle), SAL_N_ELEMENTS( mso_sptVerticalScrollHandle )
+};
+const SvxMSDffVertPair mso_sptHorizontalScrollVert[] = // adjustment1 : 0 - 5400
+{
+ { 0, 4 MSO_I }, { 1 MSO_I, 0 MSO_I }, { 3 MSO_I, 0 MSO_I }, { 3 MSO_I, 1 MSO_I },
+ { 2 MSO_I, 0 }, { 21600, 1 MSO_I }, { 21600, 13 MSO_I }, { 2 MSO_I, 12 MSO_I },
+ { 0 MSO_I, 12 MSO_I }, { 0 MSO_I, 11 MSO_I }, { 1 MSO_I, 21600 }, { 0, 11 MSO_I },
+
+ { 1 MSO_I, 4 MSO_I }, { 9 MSO_I, 8 MSO_I }, { 0 MSO_I, 4 MSO_I }, { 1 MSO_I, 6 MSO_I },
+
+ { 2 MSO_I, 1 MSO_I }, { 3 MSO_I, 9 MSO_I }, { 3 MSO_I, 1 MSO_I }, { 2 MSO_I, 0 },
+ { 21600, 1 MSO_I }, { 2 MSO_I, 0 MSO_I },
+
+ { 1 MSO_I, 6 MSO_I },
+ { 0, 4 MSO_I },
+
+ { 2 MSO_I, 0 MSO_I },
+ { 3 MSO_I, 0 MSO_I },
+
+ { 0 MSO_I, 4 MSO_I },
+ { 0 MSO_I, 11 MSO_I }
+};
+const sal_uInt16 mso_sptHorizontalScrollSegm[] =
+{
+ 0x4000, 0xa801, 0x0002, 0xa802, 0x0001, 0xa801, 0x0002, 0xa802, 0x6001, 0x8000,
+ 0x4000, 0xa803, 0x6000, 0x8000,
+ 0x4000, 0xa803, 0xa702, 0x6000, 0x8000,
+ 0x4000, 0xa701, 0x8000,
+ 0x4000, 0x0001, 0x8000,
+ 0x4000, 0x0001, 0x8000
+};
+const SvxMSDffHandle mso_sptHorizontalScrollHandle[] =
+{
+ { SvxMSDffHandleFlags::RANGE,
+ 0x100, 0, 10800, 10800, 0, 5400, MIN_INT32, 0x7fffffff }
+};
+const mso_CustomShape msoHorizontalScroll =
+{
+ const_cast<SvxMSDffVertPair*>(mso_sptHorizontalScrollVert), SAL_N_ELEMENTS( mso_sptHorizontalScrollVert ),
+ const_cast<sal_uInt16*>(mso_sptHorizontalScrollSegm), sizeof( mso_sptHorizontalScrollSegm ) >> 1,
+ const_cast<SvxMSDffCalculationData*>(mso_sptScrollCalc), SAL_N_ELEMENTS( mso_sptScrollCalc ),
+ const_cast<sal_Int32*>(mso_sptDefault2700),
+ const_cast<SvxMSDffTextRectangles*>(mso_sptScrollTextRect), SAL_N_ELEMENTS( mso_sptScrollTextRect ),
+ 21600, 21600,
+ 10800, 11000,
+ nullptr, 0,
+ const_cast<SvxMSDffHandle*>(mso_sptHorizontalScrollHandle), SAL_N_ELEMENTS( mso_sptHorizontalScrollHandle )
+};
+
+const SvxMSDffVertPair mso_sptFlowChartProcessVert[] =
+{
+ { 0, 0 }, { 21600, 0 }, { 21600, 21600 }, { 0, 21600 }, { 0, 0 }
+};
+const mso_CustomShape msoFlowChartProcess =
+{
+ const_cast<SvxMSDffVertPair*>(mso_sptFlowChartProcessVert), SAL_N_ELEMENTS( mso_sptFlowChartProcessVert ),
+ nullptr, 0,
+ nullptr, 0,
+ nullptr,
+ nullptr, 0,
+ 21600, 21600,
+ MIN_INT32, MIN_INT32,
+ const_cast<SvxMSDffVertPair*>(mso_sptStandardGluePoints), SAL_N_ELEMENTS( mso_sptStandardGluePoints ),
+ nullptr, 0 // handles
+};
+
+const SvxMSDffVertPair mso_sptFlowChartAlternateProcessVert[] =
+{
+ { 0, 2 MSO_I }, { 0 MSO_I, 0 }, { 1 MSO_I, 0 }, { 21600, 2 MSO_I },
+ { 21600, 3 MSO_I }, { 1 MSO_I, 21600 }, { 0 MSO_I, 21600 }, { 0, 3 MSO_I }
+};
+const sal_uInt16 mso_sptFlowChartAlternateProcessSegm[] =
+{
+ 0x4000, 0xa801, 0x0001, 0xa701, 0x0001, 0xa801, 0x0001, 0xa701, 0x6000, 0x8000
+};
+const SvxMSDffCalculationData mso_sptFlowChartAlternateProcessCalc[] =
+{
+ { 0x2000, { DFF_Prop_geoLeft, 2540, 0 } },
+ { 0x2000, { DFF_Prop_geoRight, 0, 2540 } },
+ { 0x2000, { DFF_Prop_geoTop, 2540, 0 } },
+ { 0x2000, { DFF_Prop_geoBottom, 0, 2540 } },
+ { 0x2000, { DFF_Prop_geoLeft, 800, 0 } },
+ { 0x2000, { DFF_Prop_geoRight, 0, 800 } },
+ { 0x2000, { DFF_Prop_geoTop, 800, 0 } },
+ { 0x2000, { DFF_Prop_geoBottom,0, 800 } }
+};
+const SvxMSDffTextRectangles mso_sptFlowChartAlternateProcessTextRect[] =
+{
+ { { 4 MSO_I, 6 MSO_I }, { 5 MSO_I, 7 MSO_I } }
+};
+const mso_CustomShape msoFlowChartAlternateProcess =
+{
+ const_cast<SvxMSDffVertPair*>(mso_sptFlowChartAlternateProcessVert), SAL_N_ELEMENTS( mso_sptFlowChartAlternateProcessVert ),
+ const_cast<sal_uInt16*>(mso_sptFlowChartAlternateProcessSegm), sizeof( mso_sptFlowChartAlternateProcessSegm ) >> 1,
+ const_cast<SvxMSDffCalculationData*>(mso_sptFlowChartAlternateProcessCalc), SAL_N_ELEMENTS( mso_sptFlowChartAlternateProcessCalc ),
+ nullptr,
+ const_cast<SvxMSDffTextRectangles*>(mso_sptFlowChartAlternateProcessTextRect), SAL_N_ELEMENTS( mso_sptFlowChartAlternateProcessTextRect ),
+ 21600, 21600,
+ MIN_INT32, MIN_INT32,
+ const_cast<SvxMSDffVertPair*>(mso_sptStandardGluePoints), SAL_N_ELEMENTS( mso_sptStandardGluePoints ),
+ nullptr, 0 // handles
+};
+
+const SvxMSDffVertPair mso_sptFlowChartDecisionVert[] =
+{
+ { 0, 10800 }, { 10800, 0 }, { 21600, 10800 }, { 10800, 21600 }, { 0, 10800 }
+};
+const SvxMSDffTextRectangles mso_sptFlowChartDecisionTextRect[] =
+{
+ { { 5400, 5400 }, { 16200, 16200 } }
+};
+const mso_CustomShape msoFlowChartDecision =
+{
+ const_cast<SvxMSDffVertPair*>(mso_sptFlowChartDecisionVert), SAL_N_ELEMENTS( mso_sptFlowChartDecisionVert ),
+ nullptr, 0,
+ nullptr, 0,
+ nullptr,
+ const_cast<SvxMSDffTextRectangles*>(mso_sptFlowChartDecisionTextRect), SAL_N_ELEMENTS( mso_sptFlowChartDecisionTextRect ),
+ 21600, 21600,
+ MIN_INT32, MIN_INT32,
+ const_cast<SvxMSDffVertPair*>(mso_sptStandardGluePoints), SAL_N_ELEMENTS( mso_sptStandardGluePoints ),
+ nullptr, 0 // handles
+};
+
+const SvxMSDffVertPair mso_sptFlowChartInputOutputVert[] =
+{
+ { 4230, 0 }, { 21600, 0 }, { 17370, 21600 }, { 0, 21600 }, { 4230, 0 }
+};
+const SvxMSDffTextRectangles mso_sptFlowChartInputOutputTextRect[] =
+{
+ { { 4230, 0 }, { 17370, 21600 } }
+};
+const SvxMSDffVertPair mso_sptFlowChartInputOutputGluePoints[] =
+{
+ { 12960, 0 }, { 10800, 0 }, { 2160, 10800 }, { 8600, 21600 }, { 10800, 21600 }, { 19400, 10800 }
+};
+const mso_CustomShape msoFlowChartInputOutput =
+{
+ const_cast<SvxMSDffVertPair*>(mso_sptFlowChartInputOutputVert), SAL_N_ELEMENTS( mso_sptFlowChartInputOutputVert ),
+ nullptr, 0,
+ nullptr, 0,
+ nullptr,
+ const_cast<SvxMSDffTextRectangles*>(mso_sptFlowChartInputOutputTextRect), SAL_N_ELEMENTS( mso_sptFlowChartInputOutputTextRect ),
+ 21600, 21600,
+ MIN_INT32, MIN_INT32,
+ const_cast<SvxMSDffVertPair*>(mso_sptFlowChartInputOutputGluePoints), SAL_N_ELEMENTS( mso_sptFlowChartInputOutputGluePoints ),
+ nullptr, 0 // handles
+};
+
+const SvxMSDffVertPair mso_sptFlowChartPredefinedProcessVert[] =
+{
+ { 0, 0 }, { 21600, 0 }, { 21600, 21600 }, { 0, 21600 },
+
+ { 2540, 0 }, { 2540, 21600 },
+
+ { 21600 - 2540, 0 }, { 21600 - 2540, 21600 }
+};
+const sal_uInt16 mso_sptFlowChartPredefinedProcessSegm[] =
+{
+ 0x4000, 0x0003, 0x6000, 0x8000,
+ 0x4000, 0x0001, 0x8000,
+ 0x4000, 0x0001, 0x8000
+};
+const SvxMSDffTextRectangles mso_sptFlowChartPredefinedProcessTextRect[] =
+{
+ { { 2540, 0 }, { 21600 - 2540, 21600 } }
+};
+const mso_CustomShape msoFlowChartPredefinedProcess =
+{
+ const_cast<SvxMSDffVertPair*>(mso_sptFlowChartPredefinedProcessVert), SAL_N_ELEMENTS( mso_sptFlowChartPredefinedProcessVert ),
+ const_cast<sal_uInt16*>(mso_sptFlowChartPredefinedProcessSegm), sizeof( mso_sptFlowChartPredefinedProcessSegm ) >> 1,
+ nullptr, 0,
+ nullptr,
+ const_cast<SvxMSDffTextRectangles*>(mso_sptFlowChartPredefinedProcessTextRect), SAL_N_ELEMENTS( mso_sptFlowChartPredefinedProcessTextRect ),
+ 21600, 21600,
+ MIN_INT32, MIN_INT32,
+ nullptr, 0,
+ nullptr, 0 // handles
+};
+
+const SvxMSDffVertPair mso_sptFlowChartInternalStorageVert[] =
+{
+ { 0, 0 }, { 21600, 0 }, { 21600, 21600 }, { 0, 21600 },
+
+ { 4230, 0 }, { 4230, 21600 },
+
+ { 0, 4230 }, { 21600, 4230 }
+};
+const sal_uInt16 mso_sptFlowChartInternalStorageSegm[] =
+{
+ 0x4000, 0x0003, 0x6000, 0x8000,
+ 0x4000, 0x0001, 0x8000,
+ 0x4000, 0x0001, 0x8000
+};
+const SvxMSDffTextRectangles mso_sptFlowChartInternalStorageTextRect[] =
+{
+ { { 4230, 4230 }, { 21600, 21600 } }
+};
+const mso_CustomShape msoFlowChartInternalStorage =
+{
+ const_cast<SvxMSDffVertPair*>(mso_sptFlowChartInternalStorageVert), SAL_N_ELEMENTS( mso_sptFlowChartInternalStorageVert ),
+ const_cast<sal_uInt16*>(mso_sptFlowChartInternalStorageSegm), sizeof( mso_sptFlowChartInternalStorageSegm ) >> 1,
+ nullptr, 0,
+ nullptr,
+ const_cast<SvxMSDffTextRectangles*>(mso_sptFlowChartInternalStorageTextRect), SAL_N_ELEMENTS( mso_sptFlowChartInternalStorageTextRect ),
+ 21600, 21600,
+ MIN_INT32, MIN_INT32,
+ nullptr, 0,
+ nullptr, 0 // handles
+};
+
+const SvxMSDffVertPair mso_sptFlowChartDocumentVert[] =
+{
+ { 0, 0 }, { 21600, 0 }, { 21600, 17360 },
+ { 13050, 17220 }, { 13340, 20770 }, { 5620, 21600 }, // ccp
+ { 2860, 21100 }, { 1850, 20700 }, { 0, 20120 } // ccp
+};
+const sal_uInt16 mso_sptFlowChartDocumentSegm[] =
+{
+ 0x4000, 0x0002, 0x2002, 0x6000, 0x8000
+};
+const SvxMSDffTextRectangles mso_sptFlowChartDocumentTextRect[] =
+{
+ { { 0, 0 }, { 21600, 17360 } }
+};
+const SvxMSDffVertPair mso_sptFlowChartDocumentGluePoints[] =
+{
+ { 10800, 0 }, { 0, 10800 }, { 10800, 20320 }, { 21600, 10800 }
+};
+const mso_CustomShape msoFlowChartDocument =
+{
+ const_cast<SvxMSDffVertPair*>(mso_sptFlowChartDocumentVert), SAL_N_ELEMENTS( mso_sptFlowChartDocumentVert ),
+ const_cast<sal_uInt16*>(mso_sptFlowChartDocumentSegm), sizeof( mso_sptFlowChartDocumentSegm ) >> 1,
+ nullptr, 0,
+ nullptr,
+ const_cast<SvxMSDffTextRectangles*>(mso_sptFlowChartDocumentTextRect), SAL_N_ELEMENTS( mso_sptFlowChartDocumentTextRect ),
+ 21600, 21600,
+ MIN_INT32, MIN_INT32,
+ const_cast<SvxMSDffVertPair*>(mso_sptFlowChartDocumentGluePoints), SAL_N_ELEMENTS( mso_sptFlowChartDocumentGluePoints ),
+ nullptr, 0 // handles
+};
+
+const SvxMSDffVertPair mso_sptFlowChartMultidocumentVert[] =
+{
+ { 0, 3600 }, { 1500, 3600 }, { 1500, 1800 }, { 3000, 1800 },
+ { 3000, 0 }, { 21600, 0 }, { 21600, 14409 }, { 21600 - 1500, 14409 },
+ { 21600 - 1500, 14409 + 1800 }, { 21600 - 3000, 14409 + 1800 }, { 21600 - 3000, 14409 + 3600 },
+ { 11610, 14293 + 3600 }, { 11472, 17239 + 3600 }, { 4833, 17928 + 3600 }, // ccp
+ { 2450, 17513 + 3600 }, { 1591, 17181 + 3600 }, { 0, 16700 + 3600 }, // ccp
+
+ { 1500, 3600 }, { 21600 - 3000, 3600 }, { 21600 - 3000, 14409 + 1800 },
+
+ { 3000, 1800 }, { 21600 - 1500, 1800 }, { 21600 - 1500, 14409 }
+};
+const sal_uInt16 mso_sptFlowChartMultidocumentSegm[] =
+{
+ 0x4000, 0x000a, 0x2002, 0x6000, 0x8000,
+ 0x4000, 0xaa00, 0x0002, 0x8000, // NO FILL
+ 0x4000, 0xaa00, 0x0002, 0x8000 // NO FILL
+};
+const SvxMSDffTextRectangles mso_sptFlowChartMultidocumentTextRect[] =
+{
+ { { 0, 3600 }, { 21600 - 3000, 14409 + 3600 } }
+};
+const SvxMSDffVertPair mso_sptFlowChartMultidocumentGluePoints[] =
+{
+ { 10800, 0 }, { 0, 10800 }, { 10800, 19890 }, { 21600, 10800 }
+};
+const mso_CustomShape msoFlowChartMultidocument =
+{
+ const_cast<SvxMSDffVertPair*>(mso_sptFlowChartMultidocumentVert), SAL_N_ELEMENTS( mso_sptFlowChartMultidocumentVert ),
+ const_cast<sal_uInt16*>(mso_sptFlowChartMultidocumentSegm), sizeof( mso_sptFlowChartMultidocumentSegm ) >> 1,
+ nullptr, 0,
+ nullptr,
+ const_cast<SvxMSDffTextRectangles*>(mso_sptFlowChartMultidocumentTextRect), SAL_N_ELEMENTS( mso_sptFlowChartMultidocumentTextRect ),
+ 21600, 21600,
+ MIN_INT32, MIN_INT32,
+ const_cast<SvxMSDffVertPair*>(mso_sptFlowChartMultidocumentGluePoints), SAL_N_ELEMENTS( mso_sptFlowChartMultidocumentGluePoints ),
+ nullptr, 0 // handles
+};
+
+const SvxMSDffVertPair mso_sptFlowChartTerminatorVert[] =
+{
+ { 3470, 21600 }, { 0, 10800 }, { 3470, 0 }, { 18130, 0 },
+ { 21600, 10800 }, { 18130, 21600 }
+};
+const sal_uInt16 mso_sptFlowChartTerminatorSegm[] =
+{
+ 0x4000, 0xa702, 0x0001, 0xa702, 0x6000, 0x8000
+};
+const SvxMSDffTextRectangles mso_sptFlowChartTerminatorTextRect[] =
+{
+ { { 1060, 3180 }, { 20540, 18420 } }
+};
+const mso_CustomShape msoFlowChartTerminator =
+{
+ const_cast<SvxMSDffVertPair*>(mso_sptFlowChartTerminatorVert), SAL_N_ELEMENTS( mso_sptFlowChartTerminatorVert ),
+ const_cast<sal_uInt16*>(mso_sptFlowChartTerminatorSegm), sizeof( mso_sptFlowChartTerminatorSegm ) >> 1,
+ nullptr, 0,
+ nullptr,
+ const_cast<SvxMSDffTextRectangles*>(mso_sptFlowChartTerminatorTextRect), SAL_N_ELEMENTS( mso_sptFlowChartTerminatorTextRect ),
+ 21600, 21600,
+ MIN_INT32, MIN_INT32,
+ const_cast<SvxMSDffVertPair*>(mso_sptStandardGluePoints), SAL_N_ELEMENTS( mso_sptStandardGluePoints ),
+ nullptr, 0 // handles
+};
+
+const SvxMSDffVertPair mso_sptFlowChartPreparationVert[] =
+{
+ { 4350, 0 }, { 17250, 0 }, { 21600, 10800 }, { 17250, 21600 },
+ { 4350, 21600 }, { 0, 10800 }, { 4350, 0 }
+};
+const SvxMSDffTextRectangles mso_sptFlowChartPreparationTextRect[] =
+{
+ { { 4350, 0 }, { 17250, 21600 } }
+};
+const mso_CustomShape msoFlowChartPreparation =
+{
+ const_cast<SvxMSDffVertPair*>(mso_sptFlowChartPreparationVert), SAL_N_ELEMENTS( mso_sptFlowChartPreparationVert ),
+ nullptr, 0,
+ nullptr, 0,
+ nullptr,
+ const_cast<SvxMSDffTextRectangles*>(mso_sptFlowChartPreparationTextRect), SAL_N_ELEMENTS( mso_sptFlowChartPreparationTextRect ),
+ 21600, 21600,
+ MIN_INT32, MIN_INT32,
+ const_cast<SvxMSDffVertPair*>(mso_sptStandardGluePoints), SAL_N_ELEMENTS( mso_sptStandardGluePoints ),
+ nullptr, 0 // handles
+};
+
+const SvxMSDffVertPair mso_sptFlowChartManualInputVert[] =
+{
+ { 0, 4300 }, { 21600, 0 }, { 21600, 21600 }, { 0, 21600 }, { 0, 4300 }
+};
+const SvxMSDffTextRectangles mso_sptFlowChartManualInputTextRect[] =
+{
+ { { 0, 4300 }, { 21600, 21600 } }
+};
+const SvxMSDffVertPair mso_sptFlowChartManualInputGluePoints[] =
+{
+ { 10800, 2150 }, { 0, 10800 }, { 10800, 19890 }, { 21600, 10800 }
+};
+const mso_CustomShape msoFlowChartManualInput =
+{
+ const_cast<SvxMSDffVertPair*>(mso_sptFlowChartManualInputVert), SAL_N_ELEMENTS( mso_sptFlowChartManualInputVert ),
+ nullptr, 0,
+ nullptr, 0,
+ nullptr,
+ const_cast<SvxMSDffTextRectangles*>(mso_sptFlowChartManualInputTextRect), SAL_N_ELEMENTS( mso_sptFlowChartManualInputTextRect ),
+ 21600, 21600,
+ MIN_INT32, MIN_INT32,
+ const_cast<SvxMSDffVertPair*>(mso_sptFlowChartManualInputGluePoints), SAL_N_ELEMENTS( mso_sptFlowChartManualInputGluePoints ),
+ nullptr, 0 // handles
+};
+
+const SvxMSDffVertPair mso_sptFlowChartManualOperationVert[] =
+{
+ { 0, 0 }, { 21600, 0 }, { 17250, 21600 }, { 4350, 21600 }, { 0, 0 }
+};
+const SvxMSDffTextRectangles mso_sptFlowChartManualOperationTextRect[] =
+{
+ { { 4350, 0 }, { 17250, 21600 } }
+};
+const SvxMSDffVertPair mso_sptFlowChartManualOperationGluePoints[] =
+{
+ { 10800, 0 }, { 2160, 10800 }, { 10800, 21600 }, { 19440, 10800 }
+};
+const mso_CustomShape msoFlowChartManualOperation =
+{
+ const_cast<SvxMSDffVertPair*>(mso_sptFlowChartManualOperationVert), SAL_N_ELEMENTS( mso_sptFlowChartManualOperationVert ),
+ nullptr, 0,
+ nullptr, 0,
+ nullptr,
+ const_cast<SvxMSDffTextRectangles*>(mso_sptFlowChartManualOperationTextRect), SAL_N_ELEMENTS( mso_sptFlowChartManualOperationTextRect ),
+ 21600, 21600,
+ MIN_INT32, MIN_INT32,
+ const_cast<SvxMSDffVertPair*>(mso_sptFlowChartManualOperationGluePoints), SAL_N_ELEMENTS( mso_sptFlowChartManualOperationGluePoints ),
+ nullptr, 0 // handles
+};
+
+const SvxMSDffVertPair mso_sptFlowChartConnectorVert[] =
+{
+ { 10800, 10800 }, { 10800, 10800 }, { 0, 360 }
+};
+const sal_uInt16 mso_sptFlowChartConnectorSegm[] =
+{
+ 0xa203, 0x6000, 0x8000
+};
+const SvxMSDffTextRectangles mso_sptFlowChartConnectorTextRect[] =
+{
+ { { 3180, 3180 }, { 18420, 18420 } }
+};
+const mso_CustomShape msoFlowChartConnector =
+{
+ const_cast<SvxMSDffVertPair*>(mso_sptFlowChartConnectorVert), SAL_N_ELEMENTS( mso_sptFlowChartConnectorVert ),
+ const_cast<sal_uInt16*>(mso_sptFlowChartConnectorSegm), sizeof( mso_sptFlowChartConnectorSegm ) >> 1,
+ nullptr, 0,
+ nullptr,
+ const_cast<SvxMSDffTextRectangles*>(mso_sptFlowChartConnectorTextRect), SAL_N_ELEMENTS( mso_sptFlowChartConnectorTextRect ),
+ 21600, 21600,
+ MIN_INT32, MIN_INT32,
+ const_cast<SvxMSDffVertPair*>(mso_sptEllipseGluePoints), SAL_N_ELEMENTS( mso_sptEllipseGluePoints ),
+ nullptr, 0 // handles
+};
+
+const SvxMSDffVertPair mso_sptFlowChartOffpageConnectorVert[] =
+{
+ { 0, 0 }, { 21600, 0 }, { 21600, 17150 }, { 10800, 21600 },
+ { 0, 17150 }, { 0, 0 }
+};
+const SvxMSDffTextRectangles mso_sptFlowChartOffpageConnectorTextRect[] =
+{
+ { { 0, 0 }, { 21600, 17150 } }
+};
+const mso_CustomShape msoFlowChartOffpageConnector =
+{
+ const_cast<SvxMSDffVertPair*>(mso_sptFlowChartOffpageConnectorVert), SAL_N_ELEMENTS( mso_sptFlowChartOffpageConnectorVert ),
+ nullptr, 0,
+ nullptr, 0,
+ nullptr,
+ const_cast<SvxMSDffTextRectangles*>(mso_sptFlowChartOffpageConnectorTextRect), SAL_N_ELEMENTS( mso_sptFlowChartOffpageConnectorTextRect ),
+ 21600, 21600,
+ MIN_INT32, MIN_INT32,
+ const_cast<SvxMSDffVertPair*>(mso_sptStandardGluePoints), SAL_N_ELEMENTS( mso_sptStandardGluePoints ),
+ nullptr, 0 // handles
+};
+
+const SvxMSDffVertPair mso_sptFlowChartPunchedCardVert[] =
+{
+ { 4300, 0 }, { 21600, 0 }, { 21600, 21600 }, { 0, 21600 },
+ { 0, 4300 }, { 4300, 0 }
+};
+const SvxMSDffTextRectangles mso_sptFlowChartPunchedCardTextRect[] =
+{
+ { { 0, 4300 }, { 21600, 21600 } }
+};
+const mso_CustomShape msoFlowChartPunchedCard =
+{
+ const_cast<SvxMSDffVertPair*>(mso_sptFlowChartPunchedCardVert), SAL_N_ELEMENTS( mso_sptFlowChartPunchedCardVert ),
+ nullptr, 0,
+ nullptr, 0,
+ nullptr,
+ const_cast<SvxMSDffTextRectangles*>(mso_sptFlowChartPunchedCardTextRect), SAL_N_ELEMENTS( mso_sptFlowChartPunchedCardTextRect ),
+ 21600, 21600,
+ MIN_INT32, MIN_INT32,
+ const_cast<SvxMSDffVertPair*>(mso_sptStandardGluePoints), SAL_N_ELEMENTS( mso_sptStandardGluePoints ),
+ nullptr, 0 // handles
+};
+
+const SvxMSDffVertPair mso_sptFlowChartPunchedTapeVert[] =
+{
+ { 0, 2230 }, // p
+ { 820, 3990 }, { 3410, 3980 }, { 5370, 4360 }, // ccp
+ { 7430, 4030 }, { 10110, 3890 }, { 10690, 2270 }, // ccp
+ { 11440, 300 }, { 14200, 160 }, { 16150, 0 }, // ccp
+ { 18670, 170 }, { 20690, 390 }, { 21600, 2230 }, // ccp
+ { 21600, 19420 }, // p
+ { 20640, 17510 }, { 18320, 17490 }, { 16140, 17240 }, // ccp
+ { 14710, 17370 }, { 11310, 17510 }, { 10770, 19430 }, // ccp
+ { 10150, 21150 }, { 7380, 21290 }, { 5290, 21600 }, // ccp
+ { 3220, 21250 }, { 610, 21130 }, { 0, 19420 } // ccp
+};
+const sal_uInt16 mso_sptFlowChartPunchedTapeSegm[] =
+{
+ 0x4000, 0x2004, 0x0001, 0x2004, 0x6000, 0x8000
+};
+const SvxMSDffTextRectangles mso_sptFlowChartPunchedTapeTextRect[] =
+{
+ { { 0, 4360 }, { 21600, 17240 } }
+};
+const SvxMSDffVertPair mso_sptFlowChartPunchedTapeGluePoints[] =
+{
+ { 10800, 2020 }, { 0, 10800 }, { 10800, 19320 }, { 21600, 10800 }
+};
+const mso_CustomShape msoFlowChartPunchedTape =
+{
+ const_cast<SvxMSDffVertPair*>(mso_sptFlowChartPunchedTapeVert), SAL_N_ELEMENTS( mso_sptFlowChartPunchedTapeVert ),
+ const_cast<sal_uInt16*>(mso_sptFlowChartPunchedTapeSegm), sizeof( mso_sptFlowChartPunchedTapeSegm ) >> 1,
+ nullptr, 0,
+ nullptr,
+ const_cast<SvxMSDffTextRectangles*>(mso_sptFlowChartPunchedTapeTextRect), SAL_N_ELEMENTS( mso_sptFlowChartPunchedTapeTextRect ),
+ 21600, 21600,
+ MIN_INT32, MIN_INT32,
+ const_cast<SvxMSDffVertPair*>(mso_sptFlowChartPunchedTapeGluePoints), SAL_N_ELEMENTS( mso_sptFlowChartPunchedTapeGluePoints ),
+ nullptr, 0 // handles
+};
+
+const SvxMSDffVertPair mso_sptFlowChartSummingJunctionVert[] =
+{
+ { 10800, 10800 }, { 10800, 10800 }, { 0, 360 },
+
+ { 3100, 3100 },
+ { 18500, 18500 },
+
+ { 3100, 18500 },
+ { 18500, 3100 }
+};
+const sal_uInt16 mso_sptFlowChartSummingJunctionSegm[] =
+{
+ 0xa203, 0x6000, 0x8000,
+ 0x4000, 0x0001, 0x8000,
+ 0x4000, 0x0001, 0x8000
+};
+const SvxMSDffTextRectangles mso_sptFlowChartSummingJunctionTextRect[] =
+{
+ { { 3100, 3100 }, { 18500, 18500 } }
+};
+const mso_CustomShape msoFlowChartSummingJunction =
+{
+ const_cast<SvxMSDffVertPair*>(mso_sptFlowChartSummingJunctionVert), SAL_N_ELEMENTS( mso_sptFlowChartSummingJunctionVert ),
+ const_cast<sal_uInt16*>(mso_sptFlowChartSummingJunctionSegm), sizeof( mso_sptFlowChartSummingJunctionSegm ) >> 1,
+ nullptr, 0,
+ nullptr,
+ const_cast<SvxMSDffTextRectangles*>(mso_sptFlowChartSummingJunctionTextRect), SAL_N_ELEMENTS( mso_sptFlowChartSummingJunctionTextRect ),
+ 21600, 21600,
+ MIN_INT32, MIN_INT32,
+ const_cast<SvxMSDffVertPair*>(mso_sptEllipseGluePoints), SAL_N_ELEMENTS( mso_sptEllipseGluePoints ),
+ nullptr, 0 // handles
+};
+
+const SvxMSDffVertPair mso_sptFlowChartOrVert[] =
+{
+ { 10800, 10800 }, { 10800, 10800 }, { 0, 360 },
+
+ { 0, 10800 }, { 21600, 10800 },
+
+ { 10800, 0 }, { 10800, 21600 }
+};
+const sal_uInt16 mso_sptFlowChartOrSegm[] =
+{
+ 0xa203, 0x6000, 0x8000,
+ 0x4000, 0x0001, 0x8000,
+ 0x4000, 0x0001, 0x8000
+};
+const SvxMSDffTextRectangles mso_sptFlowChartOrTextRect[] =
+{
+ { { 3100, 3100 }, { 18500, 18500 } }
+};
+const mso_CustomShape msoFlowChartOr =
+{
+ const_cast<SvxMSDffVertPair*>(mso_sptFlowChartOrVert), SAL_N_ELEMENTS( mso_sptFlowChartOrVert ),
+ const_cast<sal_uInt16*>(mso_sptFlowChartOrSegm), sizeof( mso_sptFlowChartOrSegm ) >> 1,
+ nullptr, 0,
+ nullptr,
+ const_cast<SvxMSDffTextRectangles*>(mso_sptFlowChartOrTextRect), SAL_N_ELEMENTS( mso_sptFlowChartOrTextRect ),
+ 21600, 21600,
+ MIN_INT32, MIN_INT32,
+ const_cast<SvxMSDffVertPair*>(mso_sptEllipseGluePoints), SAL_N_ELEMENTS( mso_sptEllipseGluePoints ),
+ nullptr, 0 // handles
+};
+
+const SvxMSDffVertPair mso_sptFlowChartCollateVert[] =
+{
+ { 0, 0 }, { 21600, 21600 }, { 0, 21600 }, { 21600, 0 }, { 0, 0 }
+};
+const SvxMSDffTextRectangles mso_sptFlowChartCollateTextRect[] =
+{
+ { { 5400, 5400 }, { 16200, 16200 } }
+};
+const SvxMSDffVertPair mso_sptFlowChartCollateGluePoints[] =
+{
+ { 10800, 0 }, { 10800, 10800 }, { 10800, 21600 }
+};
+const mso_CustomShape msoFlowChartCollate =
+{
+ const_cast<SvxMSDffVertPair*>(mso_sptFlowChartCollateVert), SAL_N_ELEMENTS( mso_sptFlowChartCollateVert ),
+ nullptr, 0,
+ nullptr, 0,
+ nullptr,
+ const_cast<SvxMSDffTextRectangles*>(mso_sptFlowChartCollateTextRect), SAL_N_ELEMENTS( mso_sptFlowChartCollateTextRect ),
+ 21600, 21600,
+ MIN_INT32, MIN_INT32,
+ const_cast<SvxMSDffVertPair*>(mso_sptFlowChartCollateGluePoints), SAL_N_ELEMENTS( mso_sptFlowChartCollateGluePoints ),
+ nullptr, 0 // handles
+};
+
+const SvxMSDffVertPair mso_sptFlowChartSortVert[] =
+{
+ { 0, 10800 }, { 10800, 0 }, { 21600, 10800 }, { 10800, 21600 },
+
+ { 0, 10800 }, { 21600, 10800 }
+};
+const sal_uInt16 mso_sptFlowChartSortSegm[] =
+{
+ 0x4000, 0x0003, 0x6000, 0x8000,
+ 0x4000, 0x0001, 0x8000
+};
+const SvxMSDffTextRectangles mso_sptFlowChartSortTextRect[] =
+{
+ { { 5400, 5400 }, { 16200, 16200 } }
+};
+const mso_CustomShape msoFlowChartSort =
+{
+ const_cast<SvxMSDffVertPair*>(mso_sptFlowChartSortVert), SAL_N_ELEMENTS( mso_sptFlowChartSortVert ),
+ const_cast<sal_uInt16*>(mso_sptFlowChartSortSegm), sizeof( mso_sptFlowChartSortSegm ) >> 1,
+ nullptr, 0,
+ nullptr,
+ const_cast<SvxMSDffTextRectangles*>(mso_sptFlowChartSortTextRect), SAL_N_ELEMENTS( mso_sptFlowChartSortTextRect ),
+ 21600, 21600,
+ MIN_INT32, MIN_INT32,
+ nullptr, 0,
+ nullptr, 0 // handles
+};
+
+const SvxMSDffVertPair mso_sptFlowChartExtractVert[] =
+{
+ { 10800, 0 }, { 21600, 21600 }, { 0, 21600 }, { 10800, 0 }
+};
+const SvxMSDffTextRectangles mso_sptFlowChartExtractTextRect[] =
+{
+ { { 5400, 10800 }, { 16200, 21600 } }
+};
+const SvxMSDffVertPair mso_sptFlowChartExtractGluePoints[] =
+{
+ { 10800, 0 }, { 5400, 10800 }, { 10800, 21600 }, { 16200, 10800 }
+};
+const mso_CustomShape msoFlowChartExtract =
+{
+ const_cast<SvxMSDffVertPair*>(mso_sptFlowChartExtractVert), SAL_N_ELEMENTS( mso_sptFlowChartExtractVert ),
+ nullptr, 0,
+ nullptr, 0,
+ nullptr,
+ const_cast<SvxMSDffTextRectangles*>(mso_sptFlowChartExtractTextRect), SAL_N_ELEMENTS( mso_sptFlowChartExtractTextRect ),
+ 21600, 21600,
+ MIN_INT32, MIN_INT32,
+ const_cast<SvxMSDffVertPair*>(mso_sptFlowChartExtractGluePoints), SAL_N_ELEMENTS( mso_sptFlowChartExtractGluePoints ),
+ nullptr, 0 // handles
+};
+
+const SvxMSDffVertPair mso_sptFlowChartMergeVert[] =
+{
+ { 0, 0 }, { 21600, 0 }, { 10800, 21600 }, { 0, 0 }
+};
+const SvxMSDffTextRectangles mso_sptFlowChartMergeTextRect[] =
+{
+ { { 5400, 0 }, { 16200, 10800 } }
+};
+const mso_CustomShape msoFlowChartMerge =
+{
+ const_cast<SvxMSDffVertPair*>(mso_sptFlowChartMergeVert), SAL_N_ELEMENTS( mso_sptFlowChartMergeVert ),
+ nullptr, 0,
+ nullptr, 0,
+ nullptr,
+ const_cast<SvxMSDffTextRectangles*>(mso_sptFlowChartMergeTextRect), SAL_N_ELEMENTS( mso_sptFlowChartMergeTextRect ),
+ 21600, 21600,
+ MIN_INT32, MIN_INT32,
+ const_cast<SvxMSDffVertPair*>(mso_sptFlowChartExtractGluePoints), SAL_N_ELEMENTS( mso_sptFlowChartExtractGluePoints ),
+ nullptr, 0 // handles
+};
+
+const SvxMSDffVertPair mso_sptFlowChartOnlineStorageVert[] =
+{
+ { 3600, 21600 }, { 0, 10800 }, { 3600, 0 }, { 21600, 0 },
+ { 18000, 10800 }, { 21600, 21600 }
+};
+const sal_uInt16 mso_sptFlowChartOnlineStorageSegm[] =
+{
+ 0x4000, 0xa702, 0x0001, 0xa702, 0x6000, 0x8000
+};
+const SvxMSDffTextRectangles mso_sptFlowChartOnlineStorageTextRect[] =
+{
+ { { 3600, 0 }, { 18000, 21600 } }
+};
+const SvxMSDffVertPair mso_sptFlowChartOnlineStorageGluePoints[] =
+{
+ { 10800, 0 }, { 0, 10800 }, { 10800, 21600 }, { 18000, 10800 }
+};
+const mso_CustomShape msoFlowChartOnlineStorage =
+{
+ const_cast<SvxMSDffVertPair*>(mso_sptFlowChartOnlineStorageVert), SAL_N_ELEMENTS( mso_sptFlowChartOnlineStorageVert ),
+ const_cast<sal_uInt16*>(mso_sptFlowChartOnlineStorageSegm), sizeof( mso_sptFlowChartOnlineStorageSegm ) >> 1,
+ nullptr, 0,
+ nullptr,
+ const_cast<SvxMSDffTextRectangles*>(mso_sptFlowChartOnlineStorageTextRect), SAL_N_ELEMENTS( mso_sptFlowChartOnlineStorageTextRect ),
+ 21600, 21600,
+ MIN_INT32, MIN_INT32,
+ const_cast<SvxMSDffVertPair*>(mso_sptFlowChartOnlineStorageGluePoints), SAL_N_ELEMENTS( mso_sptFlowChartOnlineStorageGluePoints ),
+ nullptr, 0 // handles
+};
+
+const SvxMSDffVertPair mso_sptFlowChartDelayVert[] =
+{
+ { 10800, 0 }, { 21600, 10800 }, { 10800, 21600 }, { 0, 21600 },
+ { 0, 0 }
+};
+const sal_uInt16 mso_sptFlowChartDelaySegm[] =
+{
+ 0x4000, 0xa702, 0x0002, 0x6000, 0x8000
+};
+const SvxMSDffTextRectangles mso_sptFlowChartDelayTextRect[] =
+{
+ { { 0, 3100 }, { 18500, 18500 } }
+};
+const mso_CustomShape msoFlowChartDelay =
+{
+ const_cast<SvxMSDffVertPair*>(mso_sptFlowChartDelayVert), SAL_N_ELEMENTS( mso_sptFlowChartDelayVert ),
+ const_cast<sal_uInt16*>(mso_sptFlowChartDelaySegm), sizeof( mso_sptFlowChartDelaySegm ) >> 1,
+ nullptr, 0,
+ nullptr,
+ const_cast<SvxMSDffTextRectangles*>(mso_sptFlowChartDelayTextRect), SAL_N_ELEMENTS( mso_sptFlowChartDelayTextRect ),
+ 21600, 21600,
+ MIN_INT32, MIN_INT32,
+ const_cast<SvxMSDffVertPair*>(mso_sptStandardGluePoints), SAL_N_ELEMENTS( mso_sptStandardGluePoints ),
+ nullptr, 0 // handles
+};
+
+const SvxMSDffVertPair mso_sptFlowChartMagneticTapeVert[] =
+{
+ { 20980, 18150 }, { 20980, 21600 }, { 10670, 21600 },
+ { 4770, 21540 }, { 0, 16720 }, { 0, 10800 }, // ccp
+ { 0, 4840 }, { 4840, 0 }, { 10800, 0 }, // ccp
+ { 16740, 0 }, { 21600, 4840 }, { 21600, 10800 }, // ccp
+ { 21600, 13520 }, { 20550, 16160 }, { 18670, 18170 } // ccp
+};
+const sal_uInt16 mso_sptFlowChartMagneticTapeSegm[] =
+{
+ 0x4000, 0x0002, 0x2004, 0x6000, 0x8000
+};
+const SvxMSDffTextRectangles mso_sptFlowChartMagneticTapeTextRect[] =
+{
+ { { 3100, 3100 }, { 18500, 18500 } }
+};
+const mso_CustomShape msoFlowChartMagneticTape =
+{
+ const_cast<SvxMSDffVertPair*>(mso_sptFlowChartMagneticTapeVert), SAL_N_ELEMENTS( mso_sptFlowChartMagneticTapeVert ),
+ const_cast<sal_uInt16*>(mso_sptFlowChartMagneticTapeSegm), sizeof( mso_sptFlowChartMagneticTapeSegm ) >> 1,
+ nullptr, 0,
+ nullptr,
+ const_cast<SvxMSDffTextRectangles*>(mso_sptFlowChartMagneticTapeTextRect), SAL_N_ELEMENTS( mso_sptFlowChartMagneticTapeTextRect ),
+ 21600, 21600,
+ MIN_INT32, MIN_INT32,
+ const_cast<SvxMSDffVertPair*>(mso_sptStandardGluePoints), SAL_N_ELEMENTS( mso_sptStandardGluePoints ),
+ nullptr, 0 // handles
+};
+
+const SvxMSDffVertPair mso_sptFlowChartMagneticDiskVert[] =
+{
+ { 0, 3400 }, { 10800, 0 }, { 21600, 3400 }, { 21600, 18200 },
+ { 10800, 21600 }, { 0, 18200 },
+
+ { 0, 3400 }, { 10800, 6800 }, { 21600, 3400 }
+};
+const sal_uInt16 mso_sptFlowChartMagneticDiskSegm[] =
+{
+ 0x4000, 0xa802, 0x0001, 0xa802, 0x6000, 0x8000,
+ 0x4000, 0xa802, 0x8000
+};
+const SvxMSDffTextRectangles mso_sptFlowChartMagneticDiskTextRect[] =
+{
+ { { 0, 6800 }, { 21600, 18200 } }
+};
+const SvxMSDffVertPair mso_sptFlowChartMagneticDiskGluePoints[] =
+{
+ { 10800, 6800 }, { 10800, 0 }, { 0, 10800 }, { 10800, 21600 }, { 21600, 10800 }
+};
+const mso_CustomShape msoFlowChartMagneticDisk =
+{
+ const_cast<SvxMSDffVertPair*>(mso_sptFlowChartMagneticDiskVert), SAL_N_ELEMENTS( mso_sptFlowChartMagneticDiskVert ),
+ const_cast<sal_uInt16*>(mso_sptFlowChartMagneticDiskSegm), sizeof( mso_sptFlowChartMagneticDiskSegm ) >> 1,
+ nullptr, 0,
+ nullptr,
+ const_cast<SvxMSDffTextRectangles*>(mso_sptFlowChartMagneticDiskTextRect), SAL_N_ELEMENTS( mso_sptFlowChartMagneticDiskTextRect ),
+ 21600, 21600,
+ MIN_INT32, MIN_INT32,
+ const_cast<SvxMSDffVertPair*>(mso_sptFlowChartMagneticDiskGluePoints), SAL_N_ELEMENTS( mso_sptFlowChartMagneticDiskGluePoints ),
+ nullptr, 0 // handles
+};
+
+const SvxMSDffVertPair mso_sptFlowChartMagneticDrumVert[] =
+{
+ { 18200, 0 }, { 21600, 10800 }, { 18200, 21600 }, { 3400, 21600 },
+ { 0, 10800 }, { 3400, 0 },
+
+ { 18200, 0 }, { 14800, 10800 }, { 18200, 21600 }
+};
+const sal_uInt16 mso_sptFlowChartMagneticDrumSegm[] =
+{
+ 0x4000, 0xa702, 0x0001, 0xa702, 0x6000, 0x8000,
+ 0x4000, 0xa702, 0x8000
+};
+const SvxMSDffTextRectangles mso_sptFlowChartMagneticDrumTextRect[] =
+{
+ { { 3400, 0 }, { 14800, 21600 } }
+};
+const SvxMSDffVertPair mso_sptFlowChartMagneticDrumGluePoints[] =
+{
+ { 10800, 0 }, { 0, 10800 }, { 10800, 21600 }, { 14800, 10800 }, { 21600, 10800 }
+};
+const mso_CustomShape msoFlowChartMagneticDrum =
+{
+ const_cast<SvxMSDffVertPair*>(mso_sptFlowChartMagneticDrumVert), SAL_N_ELEMENTS( mso_sptFlowChartMagneticDrumVert ),
+ const_cast<sal_uInt16*>(mso_sptFlowChartMagneticDrumSegm), sizeof( mso_sptFlowChartMagneticDrumSegm ) >> 1,
+ nullptr, 0,
+ nullptr,
+ const_cast<SvxMSDffTextRectangles*>(mso_sptFlowChartMagneticDrumTextRect), SAL_N_ELEMENTS( mso_sptFlowChartMagneticDrumTextRect ),
+ 21600, 21600,
+ MIN_INT32, MIN_INT32,
+ const_cast<SvxMSDffVertPair*>(mso_sptFlowChartMagneticDrumGluePoints), SAL_N_ELEMENTS( mso_sptFlowChartMagneticDrumGluePoints ),
+ nullptr, 0 // handles
+};
+
+const SvxMSDffVertPair mso_sptFlowChartDisplayVert[] =
+{
+ { 3600, 0 }, { 17800, 0 }, { 21600, 10800 }, { 17800, 21600 },
+ { 3600, 21600 }, { 0, 10800 }
+};
+const sal_uInt16 mso_sptFlowChartDisplaySegm[] =
+{
+ 0x4000, 0x0001, 0xa702, 0x0002, 0x6000, 0x8000
+};
+const SvxMSDffTextRectangles mso_sptFlowChartDisplayTextRect[] =
+{
+ { { 3600, 0 }, { 17800, 21600 } }
+};
+const mso_CustomShape msoFlowChartDisplay =
+{
+ const_cast<SvxMSDffVertPair*>(mso_sptFlowChartDisplayVert), SAL_N_ELEMENTS( mso_sptFlowChartDisplayVert ),
+ const_cast<sal_uInt16*>(mso_sptFlowChartDisplaySegm), sizeof( mso_sptFlowChartDisplaySegm ) >> 1,
+ nullptr, 0,
+ nullptr,
+ const_cast<SvxMSDffTextRectangles*>(mso_sptFlowChartDisplayTextRect), SAL_N_ELEMENTS( mso_sptFlowChartDisplayTextRect ),
+ 21600, 21600,
+ MIN_INT32, MIN_INT32,
+ const_cast<SvxMSDffVertPair*>(mso_sptStandardGluePoints), SAL_N_ELEMENTS( mso_sptStandardGluePoints ),
+ nullptr, 0 // handles
+};
+
+const SvxMSDffVertPair mso_sptWedgeRectCalloutVert[] =
+{
+ { 0, 0 },
+ { 0, 3590 }, { 2 MSO_I, 3 MSO_I }, { 0, 8970 },
+ { 0, 12630 },{ 4 MSO_I, 5 MSO_I }, { 0, 18010 },
+ { 0, 21600 },
+ { 3590, 21600 }, { 6 MSO_I, 7 MSO_I }, { 8970, 21600 },
+ { 12630, 21600 }, { 8 MSO_I, 9 MSO_I }, { 18010, 21600 },
+ { 21600, 21600 },
+ { 21600, 18010 }, { 10 MSO_I, 11 MSO_I }, { 21600, 12630 },
+ { 21600, 8970 }, { 12 MSO_I, 13 MSO_I }, { 21600, 3590 },
+ { 21600, 0 },
+ { 18010, 0 }, { 14 MSO_I, 15 MSO_I }, { 12630, 0 },
+ { 8970, 0 }, { 16 MSO_I, 17 MSO_I }, { 3590, 0 },
+ { 0, 0 }
+};
+const SvxMSDffCalculationData mso_sptWedgeRectCalloutCalc[] =
+{
+ { 0x2000, { DFF_Prop_adjustValue, 0, 10800 } }, //0x400
+ { 0x2000, { DFF_Prop_adjust2Value, 0,10800 } },
+ { 0x6006, { 0x412, DFF_Prop_adjustValue, 0 } }, //0x402
+ { 0x6006, { 0x412, DFF_Prop_adjust2Value, 6280 } },
+ { 0x6006, { 0x417, DFF_Prop_adjustValue, 0 } }, //0x404
+ { 0x6006, { 0x417, DFF_Prop_adjust2Value, 15320 } },
+ { 0x6006, { 0x41a, DFF_Prop_adjustValue, 6280 } }, //0x406
+ { 0x6006, { 0x41a, DFF_Prop_adjust2Value, 21600 } },
+ { 0x6006, { 0x41d, DFF_Prop_adjustValue, 15320 } }, //0x408
+ { 0x6006, { 0x41d, DFF_Prop_adjust2Value, 21600 } },
+ { 0x6006, { 0x420, DFF_Prop_adjustValue, 21600 } }, //0x40a
+ { 0x6006, { 0x420, DFF_Prop_adjust2Value, 15320 } },
+ { 0x6006, { 0x422, DFF_Prop_adjustValue, 21600 } }, //0x40c
+ { 0x6006, { 0x422, DFF_Prop_adjust2Value, 6280 } },
+ { 0x6006, { 0x424, DFF_Prop_adjustValue, 15320 } }, //0x40e
+ { 0x6006, { 0x424, DFF_Prop_adjust2Value, 0 } },
+ { 0x6006, { 0x426, DFF_Prop_adjustValue, 6280 } }, //0x410
+ { 0x6006, { 0x426, DFF_Prop_adjust2Value, 0 } },
+ { 0xa006, { DFF_Prop_adjustValue, -1, 0x413 } }, //0x412
+ { 0xa006, { 0x401, -1, 0x416 } },
+ { 0x2003, { 0x400, 0, 0 } }, //0x414
+ { 0x2003, { 0x401, 0, 0 } },
+ { 0xa000, { 0x414, 0, 0x415 } }, //0x416
+ { 0xa006, { DFF_Prop_adjustValue, -1, 0x418 } },
+ { 0x6006, { 0x401, 0x416, -1 } }, //0x418
+ { 0x2000, { DFF_Prop_adjust2Value, 0, 21600 } },
+ { 0x6006, { 0x419, 0x41b, -1 } }, //0x41a
+ { 0xa006, { 0x400, -1, 0x41c } },
+ { 0xa000, { 0x415, 0, 0x414 } }, //0x41c
+ { 0x6006, { 0x419, 0x41e, -1 } },
+ { 0x6006, { 0x400, 0x41c, -1 } }, //0x41e
+ { 0x2000, { DFF_Prop_adjustValue, 0, 21600 } },
+ { 0x6006, { 0x41f, 0x421, -1 } }, //0x420
+ { 0x6006, { 0x401, 0x416, -1 } },
+ { 0x6006, { 0x41f, 0x423, -1 } }, //0x422
+ { 0xa006, { 0x401, -1, 0x416 } },
+ { 0xa006, { DFF_Prop_adjust2Value, -1, 0x425 } }, //0x424
+ { 0x6006, { 0x400, 0x41c, -1 } },
+ { 0xa006, { DFF_Prop_adjust2Value, -1, 0x427 } }, //0x426
+ { 0xa006, { 0x400, -1, 0x41c } },
+ { 0x2000, { DFF_Prop_adjustValue, 0, 0 } }, //0x428
+ { 0x2000, { DFF_Prop_adjust2Value, 0, 0 } }
+};
+const sal_Int32 mso_sptWedgeRectCalloutDefault[] =
+{
+ 2, 1400, 25920
+};
+const SvxMSDffTextRectangles mso_sptWedgeRectCalloutTextRect[] =
+{
+ { { 0, 0 }, { 21600, 21600 } }
+};
+const SvxMSDffVertPair mso_sptWedgeRectCalloutGluePoints[] =
+{
+ { 10800, 0 }, { 0, 10800 }, { 10800, 21600 }, { 21600, 10800 }, { 40 MSO_I, 41 MSO_I }
+};
+const SvxMSDffHandle mso_sptCalloutHandle[] =
+{
+ {
+ SvxMSDffHandleFlags::NONE,
+ 0x100, 0x101, 10800, 10800, MIN_INT32, 0x7fffffff, MIN_INT32, 0x7fffffff
+ }
+};
+const mso_CustomShape msoWedgeRectCallout =
+{
+ const_cast<SvxMSDffVertPair*>(mso_sptWedgeRectCalloutVert), SAL_N_ELEMENTS( mso_sptWedgeRectCalloutVert ),
+ nullptr, 0,
+ const_cast<SvxMSDffCalculationData*>(mso_sptWedgeRectCalloutCalc), SAL_N_ELEMENTS( mso_sptWedgeRectCalloutCalc ),
+ const_cast<sal_Int32*>(mso_sptWedgeRectCalloutDefault),
+ const_cast<SvxMSDffTextRectangles*>(mso_sptWedgeRectCalloutTextRect), SAL_N_ELEMENTS( mso_sptWedgeRectCalloutTextRect ),
+ 21600, 21600,
+ MIN_INT32, MIN_INT32,
+ const_cast<SvxMSDffVertPair*>(mso_sptWedgeRectCalloutGluePoints), SAL_N_ELEMENTS( mso_sptWedgeRectCalloutGluePoints ),
+ const_cast<SvxMSDffHandle*>(mso_sptCalloutHandle), SAL_N_ELEMENTS( mso_sptCalloutHandle ) // handles
+};
+const SvxMSDffVertPair mso_sptWedgeRRectCalloutVert[] =
+{
+ { 3590, 0 },
+ { 0, 3590 },
+ { 2 MSO_I, 3 MSO_I }, { 0, 8970 },
+ { 0, 12630 },{ 4 MSO_I, 5 MSO_I }, { 0, 18010 },
+ { 3590, 21600 },
+ { 6 MSO_I, 7 MSO_I }, { 8970, 21600 },
+ { 12630, 21600 }, { 8 MSO_I, 9 MSO_I }, { 18010, 21600 },
+ { 21600, 18010 },
+ { 10 MSO_I, 11 MSO_I }, { 21600, 12630 },
+ { 21600, 8970 }, { 12 MSO_I, 13 MSO_I }, { 21600, 3590 },
+ { 18010, 0 },
+ { 14 MSO_I, 15 MSO_I }, { 12630, 0 },
+ { 8970, 0 }, { 16 MSO_I, 17 MSO_I }
+};
+const sal_uInt16 mso_sptWedgeRRectCalloutSegm[] =
+{
+ 0x4000, 0xa701, 0x0005, 0xa801, 0x0005, 0xa701, 0x0005, 0xa801, 0x0004, 0x6001, 0x8000
+};
+const SvxMSDffTextRectangles mso_sptWedgeRRectCalloutTextRect[] =
+{
+ { { 800, 800 }, { 20800, 20800 } }
+};
+const mso_CustomShape msoWedgeRRectCallout =
+{
+ const_cast<SvxMSDffVertPair*>(mso_sptWedgeRRectCalloutVert), SAL_N_ELEMENTS( mso_sptWedgeRRectCalloutVert ),
+ const_cast<sal_uInt16*>(mso_sptWedgeRRectCalloutSegm), sizeof( mso_sptWedgeRRectCalloutSegm ) >> 1,
+ const_cast<SvxMSDffCalculationData*>(mso_sptWedgeRectCalloutCalc), SAL_N_ELEMENTS( mso_sptWedgeRectCalloutCalc ),
+ const_cast<sal_Int32*>(mso_sptWedgeRectCalloutDefault),
+ const_cast<SvxMSDffTextRectangles*>(mso_sptWedgeRRectCalloutTextRect), SAL_N_ELEMENTS( mso_sptWedgeRRectCalloutTextRect ),
+ 21600, 21600,
+ MIN_INT32, MIN_INT32,
+ nullptr, 0,
+ const_cast<SvxMSDffHandle*>(mso_sptCalloutHandle), SAL_N_ELEMENTS( mso_sptCalloutHandle ) // handles
+};
+const SvxMSDffVertPair mso_sptBalloonVert[] =
+{
+ { 3590, 0 },
+ { 0, 3590 },
+ { 0, 14460 },
+ { 3590, 18050 },
+ { 40 MSO_I, 21600 }, { 5420, 18050 },
+ { 18010, 18050 },
+ { 21600, 14460 },
+ { 21600, 3590 },
+ { 18010, 0 }
+};
+const sal_uInt16 mso_sptBalloonSegm[] =
+{
+ 0x4000, 0xa701, 0x0001, 0xa801, 0x0003, 0xa701, 0x0001, 0xa801, 0x6001, 0x8000
+};
+const SvxMSDffHandle mso_sptBalloonHandle[] =
+{
+ {
+ SvxMSDffHandleFlags::RANGE,
+ 0x100, 1, 10800, 10800, 0, 8990, MIN_INT32, 0x7fffffff
+ }
+};
+const SvxMSDffTextRectangles mso_sptBalloonTextRect[] =
+{
+ { { 800, 800 }, { 20800, 17250 } }
+};
+const mso_CustomShape msoBalloon =
+{
+ const_cast<SvxMSDffVertPair*>(mso_sptBalloonVert), SAL_N_ELEMENTS( mso_sptBalloonVert ),
+ const_cast<sal_uInt16*>(mso_sptBalloonSegm), sizeof( mso_sptBalloonSegm ) >> 1,
+ const_cast<SvxMSDffCalculationData*>(mso_sptWedgeRectCalloutCalc), SAL_N_ELEMENTS( mso_sptWedgeRectCalloutCalc ),
+ const_cast<sal_Int32*>(mso_sptWedgeRectCalloutDefault),
+ const_cast<SvxMSDffTextRectangles*>(mso_sptBalloonTextRect), SAL_N_ELEMENTS( mso_sptBalloonTextRect ),
+ 21600, 21600,
+ MIN_INT32, MIN_INT32,
+ nullptr, 0,
+ const_cast<SvxMSDffHandle*>(mso_sptBalloonHandle), SAL_N_ELEMENTS( mso_sptBalloonHandle ) // handles
+};
+const SvxMSDffVertPair mso_sptWedgeEllipseCalloutVert[] =
+{
+ { 0, 0 }, { 21600, 21600 }, { 0x16 MSO_I, 0x17 MSO_I }, { 0x12 MSO_I, 0x13 MSO_I }, { 0xe MSO_I, 0xf MSO_I }
+};
+const sal_uInt16 mso_sptWedgeEllipseCalloutSegm[] =
+{
+ 0xa504, 0x0001, 0x6001, 0x8000
+};
+const SvxMSDffCalculationData mso_sptWedgeEllipseCalloutCalc[] =
+{
+ { 0x2000, { DFF_Prop_adjustValue, 0, 10800 } }, // 00 rad x
+ { 0x2000, { DFF_Prop_adjust2Value, 0, 10800 } }, // 01 rad y
+ { 0x6001, { 0x400, 0x400, 1 } }, // 02 rad x^2
+ { 0x6001, { 0x401, 0x401, 1 } }, // 03 rad y^2
+ { 0x6000, { 0x402, 0x403, 0 } }, // 04
+ { 0x200d, { 0x404, 0, 0 } }, // 05
+ { 0x2000, { 0x405, 0, 10800 } }, // 06 > 0 ? spur needs to be drawn : 10800
+ { 0x6008, { 0x400, 0x401, 0 } }, // 07 atan2 -> angle
+ { 0x2000, { 0x407, 0, 10 } }, // 08
+ { 0x2000, { 0x407, 10, 0 } }, // 09
+ { 0x400a, { 10800, 0x407, 0 } }, // 0a
+ { 0x4009, { 10800, 0x407, 0 } }, // 0b
+ { 0x2000, { 0x40a, 10800, 0 } }, // 0c
+ { 0x2000, { 0x40b, 10800, 0 } }, // 0d
+ { 0xe006, { 0x406, DFF_Prop_adjustValue, 0x40c } }, // 0e
+ { 0xe006, { 0x406, DFF_Prop_adjust2Value, 0x40d } },// 0f
+ { 0x400a, { 10800, 0x408, 0 } }, // 10
+ { 0x4009, { 10800, 0x408, 0 } }, // 11
+ { 0x2000, { 0x410, 10800, 0 } }, // 12
+ { 0x2000, { 0x411, 10800, 0 } }, // 13
+ { 0x400a, { 10800, 0x409, 0 } }, // 14
+ { 0x4009, { 10800, 0x409, 0 } }, // 15
+ { 0x2000, { 0x414, 10800, 0 } }, // 16
+ { 0x2000, { 0x415, 10800, 0 } } // 17
+};
+const sal_Int32 mso_sptWedgeEllipseCalloutDefault[] =
+{
+ 2, 1350, 25920
+};
+const SvxMSDffVertPair mso_sptWedgeEllipseCalloutGluePoints[] =
+{
+ { 10800, 0 }, { 3160, 3160 }, { 0, 10800 }, { 3160, 18440 }, { 10800, 21600 }, { 18440, 18440 }, { 21600, 10800 }, { 18440, 3160 }, { 0xe MSO_I, 0xf MSO_I }
+};
+const SvxMSDffTextRectangles mso_sptWedgeEllipseCalloutTextRect[] =
+{
+ { { 3200, 3200 }, { 18400, 18400 } }
+};
+const mso_CustomShape msoWedgeEllipseCallout =
+{
+ const_cast<SvxMSDffVertPair*>(mso_sptWedgeEllipseCalloutVert), SAL_N_ELEMENTS( mso_sptWedgeEllipseCalloutVert ),
+ const_cast<sal_uInt16*>(mso_sptWedgeEllipseCalloutSegm), sizeof( mso_sptWedgeEllipseCalloutSegm ) >> 1,
+ const_cast<SvxMSDffCalculationData*>(mso_sptWedgeEllipseCalloutCalc), SAL_N_ELEMENTS( mso_sptWedgeEllipseCalloutCalc ),
+ const_cast<sal_Int32*>(mso_sptWedgeEllipseCalloutDefault),
+ const_cast<SvxMSDffTextRectangles*>(mso_sptWedgeEllipseCalloutTextRect), SAL_N_ELEMENTS( mso_sptWedgeEllipseCalloutTextRect ),
+ 21600, 21600,
+ MIN_INT32, MIN_INT32,
+ const_cast<SvxMSDffVertPair*>(mso_sptWedgeEllipseCalloutGluePoints), SAL_N_ELEMENTS( mso_sptWedgeEllipseCalloutGluePoints ),
+ const_cast<SvxMSDffHandle*>(mso_sptCalloutHandle), SAL_N_ELEMENTS( mso_sptCalloutHandle ) // handles
+};
+
+const SvxMSDffVertPair mso_sptCloudCalloutVert[] =
+{
+ { 1930,7160 }, // p
+ { 1530,4490 }, { 3400,1970 }, { 5270,1970 }, // ccp
+ { 5860,1950 }, { 6470,2210 }, { 6970,2600 }, // ccp
+ { 7450,1390 }, { 8340,650 }, { 9340,650 }, // ccp
+ { 10004,690 }, { 10710,1050 }, { 11210,1700 }, // ccp
+ { 11570,630 }, { 12330,0 }, { 13150,0 }, // ccp
+ { 13840,0 }, { 14470,460 }, { 14870,1160 }, // ccp
+ { 15330,440 }, { 16020,0 }, { 16740,0 }, // ccp
+ { 17910,0 }, { 18900,1130 }, { 19110,2710 }, // ccp
+ { 20240,3150 }, { 21060,4580 }, { 21060,6220 }, // ccp
+ { 21060,6720 }, { 21000,7200 }, { 20830,7660 }, // ccp
+ { 21310,8460 }, { 21600,9450 }, { 21600,10460 }, // ccp
+ { 21600,12750 }, { 20310,14680 }, { 18650,15010 }, // ccp
+ { 18650,17200 }, { 17370,18920 }, { 15770,18920 }, // ccp
+ { 15220,18920 }, { 14700,18710 }, { 14240,18310 }, // ccp
+ { 13820,20240 }, { 12490,21600 }, { 11000,21600 }, // ccp
+ { 9890,21600 }, { 8840,20790 }, { 8210,19510 }, // ccp
+ { 7620,20000 }, { 7930,20290 }, { 6240,20290 }, // ccp
+ { 4850,20290 }, { 3570,19280 }, { 2900,17640 }, // ccp
+ { 1300,17600 }, { 480,16300 }, { 480,14660 }, // ccp
+ { 480,13900 }, { 690,13210 }, { 1070,12640 }, // ccp
+ { 380,12160 }, { 0,11210 }, { 0,10120 }, // ccp
+ { 0,8590 }, { 840,7330 }, { 1930,7160 }, // ccp
+
+ { 1930, 7160 }, { 1950, 7410 }, { 2040, 7690 }, { 2090, 7920 }, // pccp
+ { 6970, 2600 }, { 7200, 2790 }, { 7480, 3050 }, { 7670, 3310 }, // pccp
+ { 11210, 1700 }, { 11130, 1910 }, { 11080, 2160 }, { 11030, 2400 }, // pccp
+ { 14870, 1160 }, { 14720, 1400 }, { 14640, 1720 }, { 14540, 2010 }, // pccp
+ { 19110, 2710 }, { 19130, 2890 }, { 19230, 3290 }, { 19190, 3380 }, // pccp
+ { 20830, 7660 }, { 20660, 8170 }, { 20430, 8620 }, { 20110, 8990 }, // pccp
+ { 18660, 15010 }, { 18740, 14200 }, { 18280, 12200 }, { 17000, 11450 }, // pccp
+ { 14240, 18310 }, { 14320, 17980 }, { 14350, 17680 }, { 14370, 17360 }, // pccp
+ { 8220, 19510 }, { 8060, 19250 }, { 7960, 18950 }, { 7860, 18640 }, // pccp
+ { 2900, 17640 }, { 3090, 17600 }, { 3280, 17540 }, { 3460, 17450 }, // pccp
+ { 1070, 12640 }, { 1400, 12900 }, { 1780, 13130 }, { 2330, 13040 }, // pccp
+
+ { 0x11 MSO_I, 0x12 MSO_I }, { 1800, 1800 }, { 0, 360 }, // circ1
+ { 0x13 MSO_I, 0x14 MSO_I }, { 1200, 1200 }, { 0, 360 }, // circ2
+ { 0xd MSO_I, 0xe MSO_I }, { 700, 700 }, { 0, 360 } // circ3
+};
+const sal_uInt16 mso_sptCloudCalloutSegm[] =
+{
+ 0x4000, 0x2016, 0x6001, 0x8000,
+ 0x4000, 0x2001, 0xaa00, 0x8000,
+ 0x4000, 0x2001, 0xaa00, 0x8000,
+ 0x4000, 0x2001, 0xaa00, 0x8000,
+ 0x4000, 0x2001, 0xaa00, 0x8000,
+ 0x4000, 0x2001, 0xaa00, 0x8000,
+ 0x4000, 0x2001, 0xaa00, 0x8000,
+ 0x4000, 0x2001, 0xaa00, 0x8000,
+ 0x4000, 0x2001, 0xaa00, 0x8000,
+ 0x4000, 0x2001, 0xaa00, 0x8000,
+ 0x4000, 0x2001, 0xaa00, 0x8000,
+ 0x4000, 0x2001, 0xaa00, 0x8000,
+ 0xa203, 0x6001, 0x8000,
+ 0xa203, 0x6001, 0x8000,
+ 0xa203, 0x6001, 0x8000
+};
+const SvxMSDffCalculationData mso_sptCloudCalloutCalc[] =
+{
+ { 0x2000, { DFF_Prop_adjustValue, 0, 10800 } },
+ { 0x2000, { DFF_Prop_adjust2Value, 0, 10800 } },
+ { 0x6008, { 0x400, 0x401, 0 } },
+ { 0x400a, { 10800, 0x402, 0 } }, // 3
+ { 0x4009, { 10800, 0x402, 0 } }, // 4
+ { 0x2000, { 0x403, 10800, 0 } }, // 5
+ { 0x2000, { 0x404, 10800, 0 } }, // 6
+ { 0xa000, { DFF_Prop_adjustValue, 0, 0x405 } }, // 7
+ { 0xa000, { DFF_Prop_adjust2Value,0, 0x406 } }, // 8
+ { 0x2001, { 0x407, 1, 3 } }, // 9
+ { 0x2001, { 0x408, 1, 3 } }, // 0xa
+ { 0x2001, { 0x407, 2, 3 } }, // 0xb
+ { 0x2001, { 0x408, 2, 3 } }, // 0xc
+ { 0x2000, { DFF_Prop_adjustValue, 0, 0 } }, // 0xd
+ { 0x2000, { DFF_Prop_adjust2Value, 0, 0 } }, // 0xe
+ { 0x2001, { 0x403, 1, 10800 / 900 } }, // 0xf taking half x distance of the radius from the first bobble
+ { 0x2001, { 0x404, 1, 10800 / 900 } }, // 0x10
+ { 0xe000, { 0x409, 0x405, 0x40f } }, // 0x11
+ { 0xe000, { 0x40a, 0x406, 0x410 } }, // 0x12
+ { 0x6000, { 0x40b, 0x405, 0 } }, // 0x13
+ { 0x6000, { 0x40c, 0x406, 0 } } // 0x14
+};
+const sal_Int32 mso_sptCloudCalloutDefault[] =
+{
+ 2, 1350, 25920
+};
+const SvxMSDffTextRectangles mso_sptCloudCalloutTextRect[] =
+{
+ { { 3000, 3320 }, { 17110, 17330 } }
+};
+const mso_CustomShape msoCloudCallout =
+{
+ const_cast<SvxMSDffVertPair*>(mso_sptCloudCalloutVert), SAL_N_ELEMENTS( mso_sptCloudCalloutVert ),
+ const_cast<sal_uInt16*>(mso_sptCloudCalloutSegm), sizeof( mso_sptCloudCalloutSegm ) >> 1,
+ const_cast<SvxMSDffCalculationData*>(mso_sptCloudCalloutCalc), SAL_N_ELEMENTS( mso_sptCloudCalloutCalc ),
+ const_cast<sal_Int32*>(mso_sptCloudCalloutDefault),
+ const_cast<SvxMSDffTextRectangles*>(mso_sptCloudCalloutTextRect), SAL_N_ELEMENTS( mso_sptCloudCalloutTextRect ),
+ 21600, 21600,
+ MIN_INT32, MIN_INT32,
+ nullptr, 0,
+ const_cast<SvxMSDffHandle*>(mso_sptCalloutHandle), SAL_N_ELEMENTS( mso_sptCalloutHandle ) // handles
+};
+
+const SvxMSDffVertPair mso_sptWaveVert[] = // adjustment1 : 0 - 4460
+{ // adjustment2 : 8640 - 12960
+ { 7 MSO_I, 0 MSO_I }, { 15 MSO_I, 9 MSO_I }, { 16 MSO_I, 10 MSO_I }, { 12 MSO_I, 0 MSO_I },
+ { 24 MSO_I, 1 MSO_I }, { 25 MSO_I, 26 MSO_I }, { 27 MSO_I, 28 MSO_I }, { 29 MSO_I, 1 MSO_I }
+};
+const SvxMSDffCalculationData mso_sptWaveCalc[] =
+{
+ { 0x2000, { DFF_Prop_adjustValue, 0, 0 } }, //400 (vert.adj)
+ { 0x8000, { 21600, 0, 0x400 } }, //401
+ { 0x2000, { DFF_Prop_adjust2Value, 0, 0 } },//402 (horz.adj)
+ { 0x2000, { 0x402, 0, 10800 } }, //403 -2160 -> 2160 (horz.adj)
+ { 0x2001, { 0x403, 2, 1 } }, //404 -4320 -> 4320 (horz.adj)
+ { 0x2003, { 0x404, 0, 0 } }, //405 abs( 0x404 ) (horz.adj)
+ { 0x8000, { 4320, 0, 0x405 } }, //406
+ { 0xa006, { 0x403, 0, 0x405 } }, //407
+ { 0x4001, { 15800, 0x400, 4460 } }, //408 0 -> 15800 (vert.adj)
+ { 0xa000, { 0x400, 0, 0x408 } }, //409
+ { 0x6000, { 0x400, 0x408, 0 } }, //40a
+ { 0x8000, { 21600, 0, 0x404 } }, //40b
+ { 0x6006, { 0x403, 0x40b, 21600 } }, //40c
+ { 0xa000, { 0x40c, 0, 0x407 } }, //40d width between p0 and p1
+ { 0x2001, { 0x405, 1, 2 } }, //40e
+ { 0xa000, { 0x407, 7200, 0x40e } }, //40f
+ { 0x6000, { 0x40c, 0x40e, 7200 } }, //410
+ { 0x2001, { 0x40d, 1, 2 } }, //411 1/2 width
+ { 0x6000, { 0x407, 0x411, 0 } }, //412 top center glue xpos
+ { 0x8000, { 21600, 0, 0x412 } }, //413 bottom center glue xpos
+ { 0x2001, { 0x405, 1, 2 } }, //414 left glue x pos
+ { 0x8000, { 21600, 0, 0x414 } }, //415 right glue x pos
+ { 0x2001, { 0x400, 2, 1 } }, //416 y1 (textbox)
+ { 0x8000, { 21600, 0, 0x416 } }, //417 y2 (textbox)
+
+ { 0x8000, { 21600, 0, 0x407 } }, //418 p2
+
+ { 0x8000, { 21600, 0, 0x40f } }, //419 c
+ { 0x6000, { 0x401, 0x408, 0 } }, //41a
+
+ { 0x8000, { 21600, 0, 0x410 } }, //41b c
+ { 0xa000, { 0x401, 0, 0x408 } }, //41c
+
+ { 0x8000, { 21600, 0, 0x40c } } //41d p3
+};
+const SvxMSDffVertPair mso_sptWaveGluePoints[] =
+{
+ { 0x12 MSO_I, 0 MSO_I }, { 0x14 MSO_I, 10800 }, { 0x13 MSO_I, 1 MSO_I }, { 0x15 MSO_I, 10800 }
+};
+const sal_uInt16 mso_sptWaveSegm[] =
+{
+ 0x4000, 0x2001, 0x0001, 0x2001, 0x6000, 0x8000
+};
+const SvxMSDffHandle mso_sptWaveHandle[] =
+{
+ { SvxMSDffHandleFlags::RANGE,
+ 0, 0x100, 10800, 10800, MIN_INT32, 0x7fffffff, 0, 4460 },
+ { SvxMSDffHandleFlags::RANGE,
+ 0x101, 21600, 10800, 10800, 8640, 12960, MIN_INT32, 0x7fffffff }
+};
+const sal_Int32 mso_sptWaveDefault[] =
+{
+ 2, 1400, 10800
+};
+const SvxMSDffTextRectangles mso_sptWaveTextRect[] =
+{
+ { { 5 MSO_I, 22 MSO_I }, { 11 MSO_I, 23 MSO_I } }
+};
+const mso_CustomShape msoWave =
+{
+ const_cast<SvxMSDffVertPair*>(mso_sptWaveVert), SAL_N_ELEMENTS( mso_sptWaveVert ),
+ const_cast<sal_uInt16*>(mso_sptWaveSegm), sizeof( mso_sptWaveSegm ) >> 1,
+ const_cast<SvxMSDffCalculationData*>(mso_sptWaveCalc), SAL_N_ELEMENTS( mso_sptWaveCalc ),
+ const_cast<sal_Int32*>(mso_sptWaveDefault),
+ const_cast<SvxMSDffTextRectangles*>(mso_sptWaveTextRect), SAL_N_ELEMENTS( mso_sptWaveTextRect ),
+ 21600, 21600,
+ MIN_INT32, MIN_INT32,
+ const_cast<SvxMSDffVertPair*>(mso_sptWaveGluePoints), SAL_N_ELEMENTS( mso_sptWaveGluePoints ),
+ const_cast<SvxMSDffHandle*>(mso_sptWaveHandle), SAL_N_ELEMENTS( mso_sptWaveHandle )
+};
+
+const SvxMSDffVertPair mso_sptDoubleWaveVert[] = // adjustment1 : 0 - 2230
+{ // adjustment2 : 8640 - 12960
+ { 7 MSO_I, 0 MSO_I }, { 15 MSO_I, 9 MSO_I }, { 0x1f MSO_I, 10 MSO_I }, { 0x12 MSO_I, 0 MSO_I }, { 0x1e MSO_I, 9 MSO_I }, { 16 MSO_I, 10 MSO_I }, { 12 MSO_I, 0 MSO_I },
+ { 24 MSO_I, 1 MSO_I }, { 25 MSO_I, 26 MSO_I }, { 0x20 MSO_I, 28 MSO_I }, { 0x13 MSO_I, 1 MSO_I }, { 0x21 MSO_I, 26 MSO_I }, { 27 MSO_I, 28 MSO_I }, { 29 MSO_I, 1 MSO_I }
+};
+const SvxMSDffCalculationData mso_sptDoubleWaveCalc[] =
+{
+ { 0x2000, { DFF_Prop_adjustValue, 0, 0 } }, //400 (vert.adj)
+ { 0x8000, { 21600, 0, 0x400 } }, //401
+ { 0x2000, { DFF_Prop_adjust2Value, 0, 0 } },//402 (horz.adj)
+ { 0x2000, { 0x402, 0, 10800 } }, //403 -2160 -> 2160 (horz.adj)
+ { 0x2001, { 0x403, 2, 1 } }, //404 -4320 -> 4320 (horz.adj)
+ { 0x2003, { 0x404, 0, 0 } }, //405 abs( 0x404 ) (horz.adj)
+ { 0x8000, { 4320, 0, 0x405 } }, //406 -> not used
+ { 0xa006, { 0x403, 0, 0x405 } }, //407
+ { 0x4001, { 7900, 0x400, 2230 } }, //408 0 -> 7900 (vert.adj)
+ { 0xa000, { 0x400, 0, 0x408 } }, //409
+ { 0x6000, { 0x400, 0x408, 0 } }, //40a
+ { 0x8000, { 21600, 0, 0x404 } }, //40b
+ { 0x6006, { 0x403, 0x40b, 21600 } }, //40c
+ { 0xa000, { 0x40c, 0, 0x407 } }, //40d width between p0 and p1
+ { 0x2001, { 0x405, 1, 2 } }, //40e
+ { 0xa000, { 0x407, 3600, 0x40e } }, //40f
+ { 0x6000, { 0x40c, 0x40e, 3600 } }, //410
+ { 0x2001, { 0x40d, 1, 2 } }, //411 1/2 width
+ { 0x6000, { 0x407, 0x411, 0 } }, //412 top center glue xpos
+ { 0x8000, { 21600, 0, 0x412 } }, //413 bottom center glue xpos
+ { 0x2001, { 0x405, 1, 2 } }, //414 left glue x pos
+ { 0x8000, { 21600, 0, 0x414 } }, //415 right glue x pos
+ { 0x2001, { 0x400, 2, 1 } }, //416 y1 (textbox)
+ { 0x8000, { 21600, 0, 0x416 } }, //417 y2 (textbox)
+
+ { 0x8000, { 21600, 0, 0x407 } }, //418 p2
+
+ { 0x8000, { 21600, 0, 0x40f } }, //419 c
+ { 0x6000, { 0x401, 0x408, 0 } }, //41a
+
+ { 0x8000, { 21600, 0, 0x410 } }, //41b c
+ { 0xa000, { 0x401, 0, 0x408 } }, //41c
+
+ { 0x8000, { 21600, 0, 0x40c } }, //41d p3
+ { 0xa000, { 0x412, 3600, 0x40e } }, //41e
+ { 0x6000, { 0x412, 0x40e, 3600 } }, //41f
+ { 0xa000, { 0x413, 3600, 0x40e } }, //420
+ { 0x6000, { 0x413, 0x40e, 3600 } } //421
+};
+const SvxMSDffVertPair mso_sptDoubleWaveGluePoints[] =
+{
+ { 0x12 MSO_I, 0 MSO_I }, { 0x14 MSO_I, 10800 }, { 0x13 MSO_I, 1 MSO_I }, { 0x15 MSO_I, 10800 }
+};
+const sal_uInt16 mso_sptDoubleWaveSegm[] =
+{
+ 0x4000, 0x2002, 0x0001, 0x2002, 0x6000, 0x8000
+};
+const SvxMSDffHandle mso_sptDoubleWaveHandle[] =
+{
+ { SvxMSDffHandleFlags::RANGE,
+ 0, 0x100, 10800, 10800, MIN_INT32, 0x7fffffff, 0, 2230 },
+ { SvxMSDffHandleFlags::RANGE,
+ 0x101, 21600, 10800, 10800, 8640, 12960, MIN_INT32, 0x7fffffff }
+};
+const sal_Int32 mso_sptDoubleWaveDefault[] =
+{
+ 2, 1400, 10800
+};
+const SvxMSDffTextRectangles mso_sptDoubleWaveTextRect[] =
+{
+ { { 5 MSO_I, 22 MSO_I }, { 11 MSO_I, 23 MSO_I } }
+};
+const mso_CustomShape msoDoubleWave =
+{
+ const_cast<SvxMSDffVertPair*>(mso_sptDoubleWaveVert), SAL_N_ELEMENTS( mso_sptDoubleWaveVert ),
+ const_cast<sal_uInt16*>(mso_sptDoubleWaveSegm), sizeof( mso_sptDoubleWaveSegm ) >> 1,
+ const_cast<SvxMSDffCalculationData*>(mso_sptDoubleWaveCalc), SAL_N_ELEMENTS( mso_sptDoubleWaveCalc ),
+ const_cast<sal_Int32*>(mso_sptDoubleWaveDefault),
+ const_cast<SvxMSDffTextRectangles*>(mso_sptDoubleWaveTextRect), SAL_N_ELEMENTS( mso_sptDoubleWaveTextRect ),
+ 21600, 21600,
+ MIN_INT32, MIN_INT32,
+ const_cast<SvxMSDffVertPair*>(mso_sptDoubleWaveGluePoints), SAL_N_ELEMENTS( mso_sptDoubleWaveGluePoints ),
+ const_cast<SvxMSDffHandle*>(mso_sptDoubleWaveHandle), SAL_N_ELEMENTS( mso_sptDoubleWaveHandle )
+};
+
+// for each shapetype a bit of 1 is indicating that the shape is NOT filled by default
+const sal_uInt16 mso_DefaultFillingTable[] =
+{
+ 0x0000, 0x0018, 0x01ff, 0x0000, 0x0c00, 0x01e0, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0600, 0x0000, 0x0000, 0x0000, 0x0000
+};
+bool IsCustomShapeFilledByDefault( MSO_SPT eSpType )
+{
+ bool bIsFilledByDefault = true;
+ sal_uInt32 i = static_cast<sal_uInt32>(eSpType);
+ if ( i < 0x100 )
+ bIsFilledByDefault = ( mso_DefaultFillingTable[ i >> 4 ] & ( 1 << ( i & 0xf ) ) ) == 0;
+ return bIsFilledByDefault;
+}
+sal_Int16 GetCustomShapeConnectionTypeDefault( MSO_SPT eSpType )
+{
+ sal_Int16 nGluePointType = css::drawing::EnhancedCustomShapeGluePointType::SEGMENTS;
+ const mso_CustomShape* pDefCustomShape = GetCustomShapeContent( eSpType );
+ if ( pDefCustomShape && pDefCustomShape->nGluePoints )
+ nGluePointType = css::drawing::EnhancedCustomShapeGluePointType::CUSTOM;
+ else
+ {
+ switch( eSpType )
+ {
+ case mso_sptRectangle :
+ case mso_sptRoundRectangle :
+ case mso_sptPictureFrame :
+ case mso_sptFlowChartProcess :
+ case mso_sptFlowChartPredefinedProcess :
+ case mso_sptFlowChartInternalStorage :
+ case mso_sptTextPlainText :
+ case mso_sptTextBox :
+ case mso_sptVerticalScroll :
+ case mso_sptHorizontalScroll :
+ nGluePointType = css::drawing::EnhancedCustomShapeGluePointType::RECT;
+ break;
+ default: break;
+ }
+ }
+ return nGluePointType;
+}
+
+// for each shapetype a bit of 1 is indicating that the shape is NOT stroked by default
+// #i28269#
+const sal_uInt16 mso_DefaultStrokingTable[] =
+{
+ 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0800, 0x0000, 0x0000, 0x0000, // #i28269# Added shape 75 (mso_sptPictureFrame)
+ 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000
+};
+// #i28269#
+bool IsCustomShapeStrokedByDefault( MSO_SPT eSpType )
+{
+ bool bIsStrokedByDefault = true;
+ sal_uInt32 i = static_cast<sal_uInt32>(eSpType);
+ if ( i < 0x100 )
+ bIsStrokedByDefault = ( mso_DefaultStrokingTable[ i >> 4 ] & ( 1 << ( i & 0xf ) ) ) == 0;
+ return bIsStrokedByDefault;
+}
+
+const sal_uInt16 msoSortFilledObjectsToBackTable[] =
+{
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0010, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000
+};
+bool SortFilledObjectsToBackByDefault( MSO_SPT eSpType )
+{
+ bool bSortFilledObjectsToBackByDefault = true;
+ sal_uInt32 i = static_cast<sal_uInt32>(eSpType);
+ if ( i < 0x100 )
+ bSortFilledObjectsToBackByDefault = ( msoSortFilledObjectsToBackTable[ i >> 4 ] & ( 1 << ( i & 0xf ) ) ) != 0;
+ return bSortFilledObjectsToBackByDefault;
+}
+
+const SvxMSDffTextRectangles mso_sptFontWorkTextRect[] =
+{
+ { { 0, 0 }, { 21600, 21600 } }
+};
+
+const SvxMSDffVertPair mso_sptTextPlainTextVert[] =
+{
+ { 3 MSO_I, 0 }, { 5 MSO_I, 0 }, { 6 MSO_I, 21600 }, { 7 MSO_I, 21600 }
+};
+const SvxMSDffCalculationData mso_sptTextPlainTextCalc[] = // adjustment1 : 6629 - 14971
+{
+ { 0x2000, { DFF_Prop_adjustValue, 0, 10800 } },
+ { 0x2001, { 0x400, 2, 1 } },
+ { 0x2003, { 0x401, 0, 0 } },
+ { 0xa006, { 0x401, 0, 0x402 } }, // x1(3)
+ { 0x8000, { 21600, 0, 0x402 } },
+ { 0x6006, { 0x401, 0x404, 21600 } }, // x2(5)
+ { 0x6006, { 0x401, 0x402, 0 } }, // x2
+ { 0xa006, { 0x401, 21600, 0x404 } } // x3(7)
+};
+const sal_uInt16 mso_sptTextPlainTextSegm[] =
+{
+ 0x4000, 0x0001, 0x8000,
+ 0x4000, 0x0001, 0x8000
+};
+const SvxMSDffHandle mso_sptTextPlainTextHandle[] =
+{
+ { SvxMSDffHandleFlags::RANGE,
+ 0x100, 21600, 10800, 10800, 6629, 14971, MIN_INT32, 0x7fffffff }
+};
+const mso_CustomShape msoTextPlainText =
+{
+ const_cast<SvxMSDffVertPair*>(mso_sptTextPlainTextVert), SAL_N_ELEMENTS( mso_sptTextPlainTextVert ),
+ const_cast<sal_uInt16*>(mso_sptTextPlainTextSegm), sizeof( mso_sptTextPlainTextSegm ) >> 1,
+ const_cast<SvxMSDffCalculationData*>(mso_sptTextPlainTextCalc), SAL_N_ELEMENTS( mso_sptTextPlainTextCalc ),
+ const_cast<sal_Int32*>(mso_sptDefault10800),
+ const_cast<SvxMSDffTextRectangles*>(mso_sptFontWorkTextRect), SAL_N_ELEMENTS( mso_sptFontWorkTextRect ),
+ 21600, 21600,
+ MIN_INT32, MIN_INT32,
+ nullptr, 0,
+ const_cast<SvxMSDffHandle*>(mso_sptTextPlainTextHandle), SAL_N_ELEMENTS( mso_sptTextPlainTextHandle )
+};
+
+const SvxMSDffVertPair mso_sptTextStopVert[] =
+{
+ { 0, 0 MSO_I }, { 7200, 0 }, { 14400, 0 }, { 21600, 0 MSO_I },
+ { 0, 1 MSO_I }, { 7200, 21600 }, { 14400, 21600 }, { 21600, 1 MSO_I }
+};
+const SvxMSDffCalculationData mso_sptTextStopCalc[] = // adjustment1 : 3080 - 10800
+{
+ { 0x2000, { DFF_Prop_adjustValue, 0, 0 } },
+ { 0x8000, { 21600, 0, DFF_Prop_adjustValue } }
+};
+const sal_uInt16 mso_sptTextStopSegm[] =
+{
+ 0x4000, 0x0003, 0x8000,
+ 0x4000, 0x0003, 0x8000
+};
+const sal_Int32 mso_sptTextStopDefault[] =
+{
+ 1, 2700
+};
+const SvxMSDffHandle mso_sptTextStopHandle[] =
+{
+ { SvxMSDffHandleFlags::RANGE,
+ 0, 0x100, 10800, 10800, MIN_INT32, 0x7fffffff, 3080, 10800 }
+};
+const mso_CustomShape msoTextStop =
+{
+ const_cast<SvxMSDffVertPair*>(mso_sptTextStopVert), SAL_N_ELEMENTS( mso_sptTextStopVert ),
+ const_cast<sal_uInt16*>(mso_sptTextStopSegm), sizeof( mso_sptTextStopSegm ) >> 1,
+ const_cast<SvxMSDffCalculationData*>(mso_sptTextStopCalc), SAL_N_ELEMENTS( mso_sptTextStopCalc ),
+ const_cast<sal_Int32*>(mso_sptTextStopDefault),
+ const_cast<SvxMSDffTextRectangles*>(mso_sptFontWorkTextRect), SAL_N_ELEMENTS( mso_sptFontWorkTextRect ),
+ 21600, 21600,
+ MIN_INT32, MIN_INT32,
+ nullptr, 0,
+ const_cast<SvxMSDffHandle*>(mso_sptTextStopHandle), SAL_N_ELEMENTS( mso_sptTextStopHandle )
+};
+
+const SvxMSDffVertPair mso_sptTextTriangleVert[] =
+{
+ { 0, 0 MSO_I }, { 10800, 0 }, { 21600, 0 MSO_I }, { 0, 21600 }, { 21600, 21600 }
+};
+const SvxMSDffCalculationData mso_sptTextTriangleCalc[] = // adjustment1 : 6629 - 14971
+{
+ { 0x2000, { DFF_Prop_adjustValue, 0, 0 } }
+};
+const sal_uInt16 mso_sptTextTriangleSegm[] =
+{
+ 0x4000, 0x0002, 0x8000,
+ 0x4000, 0x0001, 0x8000
+};
+const SvxMSDffHandle mso_sptTextTriangleHandle[] =
+{
+ { SvxMSDffHandleFlags::RANGE,
+ 0, 0x100, 10800, 10800, MIN_INT32, 0x7fffffff, 0, 21600 }
+};
+const mso_CustomShape msoTextTriangle =
+{
+ const_cast<SvxMSDffVertPair*>(mso_sptTextTriangleVert), SAL_N_ELEMENTS( mso_sptTextTriangleVert ),
+ const_cast<sal_uInt16*>(mso_sptTextTriangleSegm), sizeof( mso_sptTextTriangleSegm ) >> 1,
+ const_cast<SvxMSDffCalculationData*>(mso_sptTextTriangleCalc), SAL_N_ELEMENTS( mso_sptTextTriangleCalc ),
+ const_cast<sal_Int32*>(mso_sptDefault10800),
+ const_cast<SvxMSDffTextRectangles*>(mso_sptFontWorkTextRect), SAL_N_ELEMENTS( mso_sptFontWorkTextRect ),
+ 21600, 21600,
+ MIN_INT32, MIN_INT32,
+ nullptr, 0,
+ const_cast<SvxMSDffHandle*>(mso_sptTextTriangleHandle), SAL_N_ELEMENTS( mso_sptTextTriangleHandle )
+};
+const SvxMSDffVertPair mso_sptTextTriangleInvertedVert[] =
+{
+ { 0, 0 }, { 21600, 0 }, { 0, 0 MSO_I }, { 10800, 21600 }, { 21600, 0 MSO_I }
+};
+const sal_uInt16 mso_sptTextTriangleInvertedSegm[] =
+{
+ 0x4000, 0x0001, 0x8000,
+ 0x4000, 0x0002, 0x8000
+};
+const mso_CustomShape msoTextTriangleInverted =
+{
+ const_cast<SvxMSDffVertPair*>(mso_sptTextTriangleInvertedVert), SAL_N_ELEMENTS( mso_sptTextTriangleInvertedVert ),
+ const_cast<sal_uInt16*>(mso_sptTextTriangleInvertedSegm), sizeof( mso_sptTextTriangleInvertedSegm ) >> 1,
+ const_cast<SvxMSDffCalculationData*>(mso_sptTextTriangleCalc), SAL_N_ELEMENTS( mso_sptTextTriangleCalc ),
+ const_cast<sal_Int32*>(mso_sptDefault10800),
+ const_cast<SvxMSDffTextRectangles*>(mso_sptFontWorkTextRect), SAL_N_ELEMENTS( mso_sptFontWorkTextRect ),
+ 21600, 21600,
+ MIN_INT32, MIN_INT32,
+ nullptr, 0,
+ const_cast<SvxMSDffHandle*>(mso_sptTextTriangleHandle), SAL_N_ELEMENTS( mso_sptTextTriangleHandle )
+};
+
+const SvxMSDffVertPair mso_sptTextChevronVert[] =
+{
+ { 0, 0 MSO_I }, { 10800, 0 }, { 21600, 0 MSO_I }, { 0, 21600 }, { 10800, 1 MSO_I }, { 21600, 21600 }
+};
+const SvxMSDffCalculationData mso_sptTextChevronCalc[] = // adjustment1 : 6629 - 14971
+{
+ { 0x2000, { DFF_Prop_adjustValue, 0, 0 } },
+ { 0x8000, { 21600, 0, DFF_Prop_adjustValue } }
+};
+const sal_uInt16 mso_sptTextChevronSegm[] =
+{
+ 0x4000, 0x0002, 0x8000,
+ 0x4000, 0x0002, 0x8000
+};
+const SvxMSDffHandle mso_sptTextChevronHandle[] =
+{
+ { SvxMSDffHandleFlags::RANGE,
+ 0, 0x100, 10800, 10800, MIN_INT32, 0x7fffffff, 0, 10800 }
+};
+const mso_CustomShape msoTextChevron =
+{
+ const_cast<SvxMSDffVertPair*>(mso_sptTextChevronVert), SAL_N_ELEMENTS( mso_sptTextChevronVert ),
+ const_cast<sal_uInt16*>(mso_sptTextChevronSegm), sizeof( mso_sptTextChevronSegm ) >> 1,
+ const_cast<SvxMSDffCalculationData*>(mso_sptTextChevronCalc), SAL_N_ELEMENTS( mso_sptTextChevronCalc ),
+ const_cast<sal_Int32*>(mso_sptDefault5400),
+ const_cast<SvxMSDffTextRectangles*>(mso_sptFontWorkTextRect), SAL_N_ELEMENTS( mso_sptFontWorkTextRect ),
+ 21600, 21600,
+ MIN_INT32, MIN_INT32,
+ nullptr, 0,
+ const_cast<SvxMSDffHandle*>(mso_sptTextChevronHandle), SAL_N_ELEMENTS( mso_sptTextChevronHandle )
+};
+
+const SvxMSDffVertPair mso_sptTextChevronInvertedVert[] =
+{
+ { 0, 0 }, { 10800, 1 MSO_I }, { 21600, 0 }, { 0, 0 MSO_I }, { 10800, 21600 }, { 21600, 0 MSO_I }
+};
+const SvxMSDffCalculationData mso_sptTextChevronInvertedCalc[] = // adjustment1 : 6629 - 14971
+{
+ { 0x2000, { DFF_Prop_adjustValue, 0, 0 } },
+ { 0x8000, { 21600, 0, DFF_Prop_adjustValue } }
+};
+const sal_uInt16 mso_sptTextChevronInvertedSegm[] =
+{
+ 0x4000, 0x0002, 0x8000,
+ 0x4000, 0x0002, 0x8000
+};
+const SvxMSDffHandle mso_sptTextChevronInvertedHandle[] =
+{
+ { SvxMSDffHandleFlags::RANGE,
+ 0, 0x100, 10800, 10800, MIN_INT32, 0x7fffffff, 10800, 21600 }
+};
+const mso_CustomShape msoTextChevronInverted =
+{
+ const_cast<SvxMSDffVertPair*>(mso_sptTextChevronInvertedVert), SAL_N_ELEMENTS( mso_sptTextChevronInvertedVert ),
+ const_cast<sal_uInt16*>(mso_sptTextChevronInvertedSegm), sizeof( mso_sptTextChevronInvertedSegm ) >> 1,
+ const_cast<SvxMSDffCalculationData*>(mso_sptTextChevronInvertedCalc), SAL_N_ELEMENTS( mso_sptTextChevronInvertedCalc ),
+ const_cast<sal_Int32*>(mso_sptDefault16200),
+ const_cast<SvxMSDffTextRectangles*>(mso_sptFontWorkTextRect), SAL_N_ELEMENTS( mso_sptFontWorkTextRect ),
+ 21600, 21600,
+ MIN_INT32, MIN_INT32,
+ nullptr, 0,
+ const_cast<SvxMSDffHandle*>(mso_sptTextChevronInvertedHandle), SAL_N_ELEMENTS( mso_sptTextChevronInvertedHandle )
+};
+//V 0 0 21600 ?f2 0 ?f0 21600 ?f0
+//W 0 0 21600 ?f2 21600 ?f0 0 ?f0 N
+//V 0 ?f3 21600 21600 0 ?f1 21600 ?f1
+//W 0 ?f3 21600 21600 21600 ?f1 0 ?f1 N
+//mso_sptTextRingInside
+const SvxMSDffVertPair mso_sptTextRingInsideVert[] =
+{
+ { 0, 0 }, { 21600, 2 MSO_I }, { 0, 0 MSO_I },{ 21600, 0 MSO_I },//V
+ { 0, 0 }, { 21600, 2 MSO_I }, { 21600, 0 MSO_I },{ 0, 0 MSO_I },//W
+ { 0, 3 MSO_I }, { 21600, 21600 }, { 0, 1 MSO_I },{ 21600, 1 MSO_I },//V
+ { 0, 3 MSO_I }, { 21600, 21600 }, { 21600, 1 MSO_I },{ 0, 1 MSO_I }//W
+};
+const SvxMSDffCalculationData mso_sptTextRingInsideCalc[] = // adjustment1 : 6629 - 14971
+{
+ { 0x2001, { DFF_Prop_adjustValue, 1, 2 } },
+ { 0x8000, { 21600, 0, 0x400 } },
+ { 0x2000, { DFF_Prop_adjustValue, 0, 0 } }, //$0
+ { 0x8000, { 21600, 0, DFF_Prop_adjustValue } }//21600-$0
+};
+const sal_uInt16 mso_sptTextRingInsideSegm[] =
+{
+ 0xa604, 0xa504,0x8000,
+ 0xa604, 0xa504,0x8000
+};
+const SvxMSDffHandle mso_sptTextRingInsideHandle[] =
+{
+ { SvxMSDffHandleFlags::RANGE| SvxMSDffHandleFlags::RANGE_Y_MAX_IS_SPECIAL | SvxMSDffHandleFlags::RANGE_Y_MIN_IS_SPECIAL,
+ 10800, 0x100, 10800, 10800, MIN_INT32, 0x7fffffff, 10800, 21600 }
+};
+const mso_CustomShape msoTextRingInside =
+{
+ const_cast<SvxMSDffVertPair*>(mso_sptTextRingInsideVert), SAL_N_ELEMENTS( mso_sptTextRingInsideVert ),
+ const_cast<sal_uInt16*>(mso_sptTextRingInsideSegm), sizeof( mso_sptTextRingInsideSegm ) >> 1,
+ const_cast<SvxMSDffCalculationData*>(mso_sptTextRingInsideCalc), SAL_N_ELEMENTS( mso_sptTextRingInsideCalc ),
+ const_cast<sal_Int32*>(mso_sptDefault13500),
+ const_cast<SvxMSDffTextRectangles*>(mso_sptFontWorkTextRect), SAL_N_ELEMENTS( mso_sptFontWorkTextRect ),
+ 21600, 21600,
+ MIN_INT32, MIN_INT32,
+ nullptr, 0,
+ const_cast<SvxMSDffHandle*>(mso_sptTextRingInsideHandle), SAL_N_ELEMENTS( mso_sptTextRingInsideHandle )
+};
+//mso_sptTextRingOutside
+//path = U 10800 ?f0 10800 ?f2 180 539 N U 10800 ?f1 10800 ?f2 180 539 N
+// MSO binary format has swing angle, not end angle, therefore 359 instead of 539.
+const SvxMSDffVertPair mso_sptTextRingOutsideVert[] =
+{
+ { 10800, 0 MSO_I }, { 10800, 0 MSO_I }, { 180, 359 },
+ { 10800, 1 MSO_I }, { 10800, 0 MSO_I }, { 180, 359 }
+};
+const SvxMSDffCalculationData mso_sptTextRingOutsideCalc[] = // adjustment1 : 6629 - 14971
+{
+ { 0x2001, { DFF_Prop_adjustValue, 1, 2 } },
+ { 0x8000, { 21600, 0, 0x400 } }
+};
+const sal_uInt16 mso_sptTextRingOutsideSegm[] =
+{
+ 0xA203, 0x8000,
+ 0xA203, 0x8000
+};
+const SvxMSDffHandle mso_sptTextRingOutsideHandle[] =
+{
+ { SvxMSDffHandleFlags::RANGE,
+ 10800, 0x100, 10800, 10800, MIN_INT32, 0x7fffffff, 10800, 21600 }
+};
+const mso_CustomShape msoTextRingOutside =
+{
+ const_cast<SvxMSDffVertPair*>(mso_sptTextRingOutsideVert), SAL_N_ELEMENTS( mso_sptTextRingOutsideVert ),
+ const_cast<sal_uInt16*>(mso_sptTextRingOutsideSegm), sizeof( mso_sptTextRingOutsideSegm ) >> 1,
+ const_cast<SvxMSDffCalculationData*>(mso_sptTextRingOutsideCalc), SAL_N_ELEMENTS( mso_sptTextRingOutsideCalc ),
+ const_cast<sal_Int32*>(mso_sptDefault13500),
+ const_cast<SvxMSDffTextRectangles*>(mso_sptFontWorkTextRect), SAL_N_ELEMENTS( mso_sptFontWorkTextRect ),
+ 21600, 21600,
+ MIN_INT32, MIN_INT32,
+ nullptr, 0,
+ const_cast<SvxMSDffHandle*>(mso_sptTextRingOutsideHandle), SAL_N_ELEMENTS( mso_sptTextRingOutsideHandle )
+};
+
+const SvxMSDffVertPair mso_sptTextFadeRightVert[] =
+{
+ { 0, 0 }, { 21600, 0 MSO_I }, { 0, 21600 }, { 21600, 1 MSO_I }
+};
+const SvxMSDffCalculationData mso_sptTextFadeCalc[] =
+{
+ { 0x2000, { DFF_Prop_adjustValue, 0, 0 } },
+ { 0x8000, { 21600, 0, DFF_Prop_adjustValue } }
+};
+const sal_uInt16 mso_sptTextFadeSegm[] =
+{
+ 0x4000, 0x0001, 0x8000,
+ 0x4000, 0x0001, 0x8000
+};
+const SvxMSDffHandle mso_sptTextFadeRightHandle[] =
+{
+ { SvxMSDffHandleFlags::RANGE,
+ 21600, 0x100, 10800, 10800, MIN_INT32, 0x7fffffff, 0, 10800 }
+};
+const mso_CustomShape msoTextFadeRight =
+{
+ const_cast<SvxMSDffVertPair*>(mso_sptTextFadeRightVert), SAL_N_ELEMENTS( mso_sptTextFadeRightVert ),
+ const_cast<sal_uInt16*>(mso_sptTextFadeSegm), sizeof( mso_sptTextFadeSegm ) >> 1,
+ const_cast<SvxMSDffCalculationData*>(mso_sptTextFadeCalc), SAL_N_ELEMENTS( mso_sptTextFadeCalc ),
+ const_cast<sal_Int32*>(mso_sptDefault7200),
+ const_cast<SvxMSDffTextRectangles*>(mso_sptFontWorkTextRect), SAL_N_ELEMENTS( mso_sptFontWorkTextRect ),
+ 21600, 21600,
+ MIN_INT32, MIN_INT32,
+ nullptr, 0,
+ const_cast<SvxMSDffHandle*>(mso_sptTextFadeRightHandle), SAL_N_ELEMENTS( mso_sptTextFadeRightHandle )
+};
+
+const SvxMSDffVertPair mso_sptTextFadeLeftVert[] =
+{
+ { 0, 0 MSO_I }, { 21600, 0 }, { 0, 1 MSO_I }, { 21600, 21600 }
+};
+const SvxMSDffHandle mso_sptTextFadeLeftHandle[] =
+{
+ { SvxMSDffHandleFlags::RANGE,
+ 0, 0x100, 10800, 10800, MIN_INT32, 0x7fffffff, 0, 10800 }
+};
+const mso_CustomShape msoTextFadeLeft =
+{
+ const_cast<SvxMSDffVertPair*>(mso_sptTextFadeLeftVert), SAL_N_ELEMENTS( mso_sptTextFadeLeftVert ),
+ const_cast<sal_uInt16*>(mso_sptTextFadeSegm), sizeof( mso_sptTextFadeSegm ) >> 1,
+ const_cast<SvxMSDffCalculationData*>(mso_sptTextFadeCalc), SAL_N_ELEMENTS( mso_sptTextFadeCalc ),
+ const_cast<sal_Int32*>(mso_sptDefault7200),
+ const_cast<SvxMSDffTextRectangles*>(mso_sptFontWorkTextRect), SAL_N_ELEMENTS( mso_sptFontWorkTextRect ),
+ 21600, 21600,
+ MIN_INT32, MIN_INT32,
+ nullptr, 0,
+ const_cast<SvxMSDffHandle*>(mso_sptTextFadeLeftHandle), SAL_N_ELEMENTS( mso_sptTextFadeLeftHandle )
+};
+
+const SvxMSDffVertPair mso_sptTextFadeUpVert[] =
+{
+ { 0 MSO_I, 0 }, { 1 MSO_I, 0 }, { 0, 21600 }, { 21600, 21600 }
+};
+const SvxMSDffHandle mso_sptTextFadeUpHandle[] =
+{
+ { SvxMSDffHandleFlags::RANGE,
+ 0x100, 0, 10800, 10800, 0, 10800, MIN_INT32, 0x7fffffff }
+};
+const mso_CustomShape msoTextFadeUp =
+{
+ const_cast<SvxMSDffVertPair*>(mso_sptTextFadeUpVert), SAL_N_ELEMENTS( mso_sptTextFadeUpVert ),
+ const_cast<sal_uInt16*>(mso_sptTextFadeSegm), sizeof( mso_sptTextFadeSegm ) >> 1,
+ const_cast<SvxMSDffCalculationData*>(mso_sptTextFadeCalc), SAL_N_ELEMENTS( mso_sptTextFadeCalc ),
+ const_cast<sal_Int32*>(mso_sptDefault7200),
+ const_cast<SvxMSDffTextRectangles*>(mso_sptFontWorkTextRect), SAL_N_ELEMENTS( mso_sptFontWorkTextRect ),
+ 21600, 21600,
+ MIN_INT32, MIN_INT32,
+ nullptr, 0,
+ const_cast<SvxMSDffHandle*>(mso_sptTextFadeUpHandle), SAL_N_ELEMENTS( mso_sptTextFadeUpHandle )
+};
+
+const SvxMSDffVertPair mso_sptTextFadeDownVert[] =
+{
+ { 0, 0 }, { 21600, 0 }, { 0 MSO_I, 21600 }, { 1 MSO_I, 21600 }
+};
+const SvxMSDffHandle mso_sptTextFadeDownHandle[] =
+{
+ { SvxMSDffHandleFlags::RANGE,
+ 0x100, 21600, 10800, 10800, 0, 10800, MIN_INT32, 0x7fffffff }
+};
+const mso_CustomShape msoTextFadeDown =
+{
+ const_cast<SvxMSDffVertPair*>(mso_sptTextFadeDownVert), SAL_N_ELEMENTS( mso_sptTextFadeDownVert ),
+ const_cast<sal_uInt16*>(mso_sptTextFadeSegm), sizeof( mso_sptTextFadeSegm ) >> 1,
+ const_cast<SvxMSDffCalculationData*>(mso_sptTextFadeCalc), SAL_N_ELEMENTS( mso_sptTextFadeCalc ),
+ const_cast<sal_Int32*>(mso_sptDefault7200),
+ const_cast<SvxMSDffTextRectangles*>(mso_sptFontWorkTextRect), SAL_N_ELEMENTS( mso_sptFontWorkTextRect ),
+ 21600, 21600,
+ MIN_INT32, MIN_INT32,
+ nullptr, 0,
+ const_cast<SvxMSDffHandle*>(mso_sptTextFadeDownHandle), SAL_N_ELEMENTS( mso_sptTextFadeDownHandle )
+};
+
+const SvxMSDffVertPair mso_sptTextSlantUpVert[] =
+{
+ { 0, 0 MSO_I }, { 21600, 0 }, { 0, 21600 }, { 21600, 1 MSO_I }
+};
+const SvxMSDffHandle mso_sptTextSlantUpHandle[] =
+{
+ { SvxMSDffHandleFlags::RANGE,
+ 0, 0x100, 10800, 10800, MIN_INT32, 0x7fffffff, 0, 15400 }
+};
+const mso_CustomShape msoTextSlantUp =
+{
+ const_cast<SvxMSDffVertPair*>(mso_sptTextSlantUpVert), SAL_N_ELEMENTS( mso_sptTextSlantUpVert ),
+ const_cast<sal_uInt16*>(mso_sptTextFadeSegm), sizeof( mso_sptTextFadeSegm ) >> 1,
+ const_cast<SvxMSDffCalculationData*>(mso_sptTextFadeCalc), SAL_N_ELEMENTS( mso_sptTextFadeCalc ),
+ const_cast<sal_Int32*>(mso_sptDefault12000),
+ const_cast<SvxMSDffTextRectangles*>(mso_sptFontWorkTextRect), SAL_N_ELEMENTS( mso_sptFontWorkTextRect ),
+ 21600, 21600,
+ MIN_INT32, MIN_INT32,
+ nullptr, 0,
+ const_cast<SvxMSDffHandle*>(mso_sptTextSlantUpHandle), SAL_N_ELEMENTS( mso_sptTextSlantUpHandle )
+};
+
+const SvxMSDffVertPair mso_sptTextSlantDownVert[] =
+{
+ { 0, 0 }, { 21600, 1 MSO_I }, { 0, 0 MSO_I }, { 21600, 21600 }
+};
+const SvxMSDffHandle mso_sptTextSlantDownHandle[] =
+{
+ { SvxMSDffHandleFlags::RANGE,
+ 0, 0x100, 10800, 10800, MIN_INT32, 0x7fffffff, 6200, 21600 }
+};
+const mso_CustomShape msoTextSlantDown =
+{
+ const_cast<SvxMSDffVertPair*>(mso_sptTextSlantDownVert), SAL_N_ELEMENTS( mso_sptTextSlantDownVert ),
+ const_cast<sal_uInt16*>(mso_sptTextFadeSegm), sizeof( mso_sptTextFadeSegm ) >> 1,
+ const_cast<SvxMSDffCalculationData*>(mso_sptTextFadeCalc), SAL_N_ELEMENTS( mso_sptTextFadeCalc ),
+ const_cast<sal_Int32*>(mso_sptDefault12000),
+ const_cast<SvxMSDffTextRectangles*>(mso_sptFontWorkTextRect), SAL_N_ELEMENTS( mso_sptFontWorkTextRect ),
+ 21600, 21600,
+ MIN_INT32, MIN_INT32,
+ nullptr, 0,
+ const_cast<SvxMSDffHandle*>(mso_sptTextSlantDownHandle), SAL_N_ELEMENTS( mso_sptTextSlantDownHandle )
+};
+
+const SvxMSDffVertPair mso_sptTextCascadeUpVert[] =
+{
+ { 0, 2 MSO_I }, { 21600, 0 }, { 0, 21600 }, { 21600, 0 MSO_I }
+};
+const SvxMSDffCalculationData mso_sptTextCascadeCalc[] =
+{
+ { 0x2000, { DFF_Prop_adjustValue, 0, 0 } },
+ { 0x8000, { 21600, 0, DFF_Prop_adjustValue } },
+ { 0x2001, { 0x401, 1, 4 } }
+};
+const SvxMSDffHandle mso_sptTextCascadeUpHandle[] =
+{
+ { SvxMSDffHandleFlags::RANGE,
+ 21600, 0x100, 10800, 10800, MIN_INT32, 0x7fffffff, 6200, 21600 }
+};
+const mso_CustomShape msoTextCascadeUp =
+{
+ const_cast<SvxMSDffVertPair*>(mso_sptTextCascadeUpVert), SAL_N_ELEMENTS( mso_sptTextCascadeUpVert ),
+ const_cast<sal_uInt16*>(mso_sptTextFadeSegm), sizeof( mso_sptTextFadeSegm ) >> 1,
+ const_cast<SvxMSDffCalculationData*>(mso_sptTextCascadeCalc), SAL_N_ELEMENTS( mso_sptTextCascadeCalc ),
+ const_cast<sal_Int32*>(mso_sptDefault9600),
+ const_cast<SvxMSDffTextRectangles*>(mso_sptFontWorkTextRect), SAL_N_ELEMENTS( mso_sptFontWorkTextRect ),
+ 21600, 21600,
+ MIN_INT32, MIN_INT32,
+ nullptr, 0,
+ const_cast<SvxMSDffHandle*>(mso_sptTextCascadeUpHandle), SAL_N_ELEMENTS( mso_sptTextCascadeUpHandle )
+};
+
+const SvxMSDffVertPair mso_sptTextCascadeDownVert[] =
+{
+ { 0, 0 }, { 21600, 2 MSO_I }, { 0, 0 MSO_I }, { 21600, 21600 }
+};
+const SvxMSDffHandle mso_sptTextCascadeDownHandle[] =
+{
+ { SvxMSDffHandleFlags::RANGE,
+ 0, 0x100, 10800, 10800, MIN_INT32, 0x7fffffff, 6200, 21600 }
+};
+const mso_CustomShape msoTextCascadeDown =
+{
+ const_cast<SvxMSDffVertPair*>(mso_sptTextCascadeDownVert), SAL_N_ELEMENTS( mso_sptTextCascadeDownVert ),
+ const_cast<sal_uInt16*>(mso_sptTextFadeSegm), sizeof( mso_sptTextFadeSegm ) >> 1,
+ const_cast<SvxMSDffCalculationData*>(mso_sptTextCascadeCalc), SAL_N_ELEMENTS( mso_sptTextCascadeCalc ),
+ const_cast<sal_Int32*>(mso_sptDefault9600),
+ const_cast<SvxMSDffTextRectangles*>(mso_sptFontWorkTextRect), SAL_N_ELEMENTS( mso_sptFontWorkTextRect ),
+ 21600, 21600,
+ MIN_INT32, MIN_INT32,
+ nullptr, 0,
+ const_cast<SvxMSDffHandle*>(mso_sptTextCascadeDownHandle), SAL_N_ELEMENTS( mso_sptTextCascadeDownHandle )
+};
+
+const SvxMSDffVertPair mso_sptTextArchUpCurveVert[] =
+{
+ { 0, 0 }, { 21600, 21600 }, { 2 MSO_I, 3 MSO_I }, { 4 MSO_I, 3 MSO_I }
+};
+const SvxMSDffCalculationData mso_sptTextArchCurveCalc[] =
+{
+ { 0x400a, { 10800, DFF_Prop_adjustValue, 0 } },
+ { 0x4009, { 10800, DFF_Prop_adjustValue, 0 } },
+ { 0x2000, { 0x400, 10800, 0 } },
+ { 0x2000, { 0x401, 10800, 0 } },
+ { 0x8000, { 21600, 0, 0x402 } }
+};
+const sal_uInt16 mso_sptTextArchUpCurveSegm[] =
+{
+ 0xA504, 0x8000 // clockwise arc
+};
+const SvxMSDffHandle mso_sptTextArchUpCurveHandle[] =
+{
+ { SvxMSDffHandleFlags::POLAR,
+ 10800, 0x100, 10800, 10800, 0, 10800, MIN_INT32, 0x7fffffff }
+};
+const sal_Int32 mso_sptTextArchUpCurveDefault[] =
+{
+ 1, 180
+};
+const mso_CustomShape msoTextArchUpCurve =
+{
+ const_cast<SvxMSDffVertPair*>(mso_sptTextArchUpCurveVert), SAL_N_ELEMENTS( mso_sptTextArchUpCurveVert ),
+ const_cast<sal_uInt16*>(mso_sptTextArchUpCurveSegm), sizeof( mso_sptTextArchUpCurveSegm ) >> 1,
+ const_cast<SvxMSDffCalculationData*>(mso_sptTextArchCurveCalc), SAL_N_ELEMENTS( mso_sptTextArchCurveCalc ),
+ const_cast<sal_Int32*>(mso_sptTextArchUpCurveDefault),
+ const_cast<SvxMSDffTextRectangles*>(mso_sptFontWorkTextRect), SAL_N_ELEMENTS( mso_sptFontWorkTextRect ),
+ 21600, 21600,
+ MIN_INT32, MIN_INT32,
+ nullptr, 0,
+ const_cast<SvxMSDffHandle*>(mso_sptTextArchUpCurveHandle), SAL_N_ELEMENTS( mso_sptTextArchUpCurveHandle )
+};
+
+const SvxMSDffVertPair mso_sptTextArchDownCurveVert[] =
+{
+ { 0, 0 }, { 21600, 21600 }, { 4 MSO_I, 3 MSO_I }, { 2 MSO_I, 3 MSO_I }
+};
+const sal_uInt16 mso_sptTextArchDownCurveSegm[] =
+{
+ 0xA304, 0x8000 // counter clockwise arc to
+};
+const SvxMSDffHandle mso_sptTextArchDownCurveHandle[] =
+{
+ { SvxMSDffHandleFlags::POLAR,
+ 10800, 0x100, 10800, 10800, 0, 10800, MIN_INT32, 0x7fffffff }
+};
+const sal_Int32 mso_sptTextArchDownCurveDefault[] =
+{
+ 1, 0
+};
+const mso_CustomShape msoTextArchDownCurve =
+{
+ const_cast<SvxMSDffVertPair*>(mso_sptTextArchDownCurveVert), SAL_N_ELEMENTS( mso_sptTextArchDownCurveVert ),
+ const_cast<sal_uInt16*>(mso_sptTextArchDownCurveSegm), sizeof( mso_sptTextArchDownCurveSegm ) >> 1,
+ const_cast<SvxMSDffCalculationData*>(mso_sptTextArchCurveCalc), SAL_N_ELEMENTS( mso_sptTextArchCurveCalc ),
+ const_cast<sal_Int32*>(mso_sptTextArchDownCurveDefault),
+ const_cast<SvxMSDffTextRectangles*>(mso_sptFontWorkTextRect), SAL_N_ELEMENTS( mso_sptFontWorkTextRect ),
+ 21600, 21600,
+ MIN_INT32, MIN_INT32,
+ nullptr, 0,
+ const_cast<SvxMSDffHandle*>(mso_sptTextArchDownCurveHandle), SAL_N_ELEMENTS( mso_sptTextArchDownCurveHandle )
+};
+
+const SvxMSDffVertPair mso_sptTextCircleCurveVert[] =
+{
+ { 0, 0 }, { 21600, 21600 }, { 2 MSO_I, 3 MSO_I }, { 2 MSO_I, 4 MSO_I }
+};
+const SvxMSDffCalculationData mso_sptTextCircleCurveCalc[] =
+{
+ { 0x400a, { 10800, DFF_Prop_adjustValue, 0 } },
+ { 0x4009, { 10800, DFF_Prop_adjustValue, 0 } },
+ { 0x2000, { 0x400, 10800, 0 } },
+ { 0x2000, { 0x401, 10800, 0 } },
+ { 0x8000, { 21600, 0, 0x403 } }
+};
+const sal_uInt16 mso_sptTextCircleCurveSegm[] =
+{
+ 0xA504, 0x8000 // clockwise arc to
+};
+const SvxMSDffHandle mso_sptTextCircleCurveHandle[] =
+{
+ { SvxMSDffHandleFlags::POLAR,
+ 10800, 0x100, 10800, 10800, 0, 10800, MIN_INT32, 0x7fffffff }
+};
+const sal_Int32 mso_sptTextCircleCurveDefault[] =
+{
+ 1, -179
+};
+const mso_CustomShape msoTextCircleCurve =
+{
+ const_cast<SvxMSDffVertPair*>(mso_sptTextCircleCurveVert), SAL_N_ELEMENTS( mso_sptTextCircleCurveVert ),
+ const_cast<sal_uInt16*>(mso_sptTextCircleCurveSegm), sizeof( mso_sptTextCircleCurveSegm ) >> 1,
+ const_cast<SvxMSDffCalculationData*>(mso_sptTextCircleCurveCalc), SAL_N_ELEMENTS( mso_sptTextCircleCurveCalc ),
+ const_cast<sal_Int32*>(mso_sptTextCircleCurveDefault),
+ const_cast<SvxMSDffTextRectangles*>(mso_sptFontWorkTextRect), SAL_N_ELEMENTS( mso_sptFontWorkTextRect ),
+ 21600, 21600,
+ MIN_INT32, MIN_INT32,
+ nullptr, 0,
+ const_cast<SvxMSDffHandle*>(mso_sptTextCircleCurveHandle), SAL_N_ELEMENTS( mso_sptTextCircleCurveHandle )
+};
+
+const SvxMSDffVertPair mso_sptTextButtonCurveVert[] =
+{
+ { 0, 0 }, { 21600, 21600 }, { 2 MSO_I, 3 MSO_I }, { 4 MSO_I, 3 MSO_I },
+ { 0, 10800 }, { 21600, 10800 },
+ { 0, 0 }, { 21600, 21600 }, { 2 MSO_I, 5 MSO_I }, { 4 MSO_I, 5 MSO_I }
+};
+const SvxMSDffCalculationData mso_sptTextButtonCurveCalc[] =
+{
+ { 0x400a, { 10800, DFF_Prop_adjustValue, 0 } },
+ { 0x4009, { 10800, DFF_Prop_adjustValue, 0 } },
+ { 0x2000, { 0x400, 10800, 0 } },
+ { 0x2000, { 0x401, 10800, 0 } },
+ { 0x8000, { 21600, 0, 0x402 } },
+ { 0x8000, { 21600, 0, 0x403 } }
+};
+const sal_uInt16 mso_sptTextButtonCurveSegm[] =
+{
+ 0xA504, 0x8000, // clockwise arc
+ 0x4000, 0x0001, 0x8000,
+ 0xA304, 0x8000 // counter clockwise
+};
+const SvxMSDffHandle mso_sptTextButtonCurveHandle[] =
+{
+ { SvxMSDffHandleFlags::POLAR,
+ 10800, 0x100, 10800, 10800, 0, 10800, MIN_INT32, 0x7fffffff }
+};
+const sal_Int32 mso_sptTextButtonCurveDefault[] =
+{
+ 1, 180
+};
+const mso_CustomShape msoTextButtonCurve =
+{
+ const_cast<SvxMSDffVertPair*>(mso_sptTextButtonCurveVert), SAL_N_ELEMENTS( mso_sptTextButtonCurveVert ),
+ const_cast<sal_uInt16*>(mso_sptTextButtonCurveSegm), sizeof( mso_sptTextButtonCurveSegm ) >> 1,
+ const_cast<SvxMSDffCalculationData*>(mso_sptTextButtonCurveCalc), SAL_N_ELEMENTS( mso_sptTextButtonCurveCalc ),
+ const_cast<sal_Int32*>(mso_sptTextButtonCurveDefault),
+ const_cast<SvxMSDffTextRectangles*>(mso_sptFontWorkTextRect), SAL_N_ELEMENTS( mso_sptFontWorkTextRect ),
+ 21600, 21600,
+ MIN_INT32, MIN_INT32,
+ nullptr, 0,
+ const_cast<SvxMSDffHandle*>(mso_sptTextButtonCurveHandle), SAL_N_ELEMENTS( mso_sptTextButtonCurveHandle )
+};
+
+const SvxMSDffVertPair mso_sptTextArchUpPourVert[] =
+{
+ { 0, 0 }, { 21600, 21600 }, { 2 MSO_I, 3 MSO_I }, { 4 MSO_I, 3 MSO_I },
+ { 5 MSO_I, 5 MSO_I }, { 11 MSO_I, 11 MSO_I }, { 8 MSO_I, 9 MSO_I }, { 0xa MSO_I, 9 MSO_I }
+};
+const SvxMSDffCalculationData mso_sptTextArchPourCalc[] =
+{
+ { 0x400a, { 10800, DFF_Prop_adjustValue, 0 } },
+ { 0x4009, { 10800, DFF_Prop_adjustValue, 0 } },
+ { 0x2000, { 0x400, 10800, 0 } },
+ { 0x2000, { 0x401, 10800, 0 } },
+ { 0x8000, { 21600, 0, 0x402 } },
+ { 0x8000, { 10800, 0, DFF_Prop_adjust2Value } },
+ { 0x600a, { 0x405, DFF_Prop_adjustValue, 0 } }, // 6
+ { 0x6009, { 0x405, DFF_Prop_adjustValue, 0 } },
+ { 0x2000, { 0x406, 10800, 0 } }, // 8
+ { 0x2000, { 0x407, 10800, 0 } },
+ { 0x8000, { 21600, 0, 0x408 } }, // 10
+ { 0x8000, { 21600, 0, 0x405 } }
+};
+const sal_uInt16 mso_sptTextArchUpPourSegm[] =
+{
+ 0xA504, 0x8000, 0xA504, 0x8000
+};
+const SvxMSDffHandle mso_sptTextArchPourHandle[] =
+{
+ { SvxMSDffHandleFlags::POLAR | SvxMSDffHandleFlags::RADIUS_RANGE,
+ 0x101, 0x100, 10800, 10800, 0, 10800, MIN_INT32, 0x7fffffff }
+};
+const sal_Int32 mso_sptTextArchUpPourDefault[] =
+{
+ 2, 180, 5400
+};
+const mso_CustomShape msoTextArchUpPour =
+{
+ const_cast<SvxMSDffVertPair*>(mso_sptTextArchUpPourVert), SAL_N_ELEMENTS( mso_sptTextArchUpPourVert ),
+ const_cast<sal_uInt16*>(mso_sptTextArchUpPourSegm), sizeof( mso_sptTextArchUpPourSegm ) >> 1,
+ const_cast<SvxMSDffCalculationData*>(mso_sptTextArchPourCalc), SAL_N_ELEMENTS( mso_sptTextArchPourCalc ),
+ const_cast<sal_Int32*>(mso_sptTextArchUpPourDefault),
+ const_cast<SvxMSDffTextRectangles*>(mso_sptFontWorkTextRect), SAL_N_ELEMENTS( mso_sptFontWorkTextRect ),
+ 21600, 21600,
+ MIN_INT32, MIN_INT32,
+ nullptr, 0,
+ const_cast<SvxMSDffHandle*>(mso_sptTextArchPourHandle), SAL_N_ELEMENTS( mso_sptTextArchPourHandle )
+};
+
+const SvxMSDffVertPair mso_sptTextArchDownPourVert[] =
+{
+ { 5 MSO_I, 5 MSO_I }, { 11 MSO_I, 11 MSO_I }, { 0xa MSO_I, 9 MSO_I }, { 8 MSO_I, 9 MSO_I },
+ { 0, 0 }, { 21600, 21600 }, { 4 MSO_I, 3 MSO_I }, { 2 MSO_I, 3 MSO_I }
+};
+const sal_uInt16 mso_sptTextArchDownPourSegm[] =
+{
+ 0xA304, 0x8000, 0xA304, 0x8000
+};
+const sal_Int32 mso_sptTextArchDownPourDefault[] =
+{
+ 2, 0, 5400
+};
+const mso_CustomShape msoTextArchDownPour =
+{
+ const_cast<SvxMSDffVertPair*>(mso_sptTextArchDownPourVert), SAL_N_ELEMENTS( mso_sptTextArchDownPourVert ),
+ const_cast<sal_uInt16*>(mso_sptTextArchDownPourSegm), sizeof( mso_sptTextArchDownPourSegm ) >> 1,
+ const_cast<SvxMSDffCalculationData*>(mso_sptTextArchPourCalc), SAL_N_ELEMENTS( mso_sptTextArchPourCalc ),
+ const_cast<sal_Int32*>(mso_sptTextArchDownPourDefault),
+ const_cast<SvxMSDffTextRectangles*>(mso_sptFontWorkTextRect), SAL_N_ELEMENTS( mso_sptFontWorkTextRect ),
+ 21600, 21600,
+ MIN_INT32, MIN_INT32,
+ nullptr, 0,
+ const_cast<SvxMSDffHandle*>(mso_sptTextArchPourHandle), SAL_N_ELEMENTS( mso_sptTextArchPourHandle )
+};
+
+const SvxMSDffVertPair mso_sptTextCirclePourVert[] =
+{
+ { 0, 0 }, { 21600, 21600 }, { 2 MSO_I, 3 MSO_I }, { 2 MSO_I, 4 MSO_I },
+ { 5 MSO_I, 5 MSO_I }, { 11 MSO_I, 11 MSO_I }, { 8 MSO_I, 9 MSO_I }, { 8 MSO_I, 0xa MSO_I }
+};
+const SvxMSDffCalculationData mso_sptTextCirclePourCalc[] =
+{
+ { 0x400a, { 10800, DFF_Prop_adjustValue, 0 } },
+ { 0x4009, { 10800, DFF_Prop_adjustValue, 0 } },
+ { 0x2000, { 0x400, 10800, 0 } },
+ { 0x2000, { 0x401, 10800, 0 } },
+ { 0x8000, { 21600, 0, 0x403 } },
+ { 0x8000, { 10800, 0, DFF_Prop_adjust2Value } },
+ { 0x600a, { 0x405, DFF_Prop_adjustValue, 0 } }, // 6
+ { 0x6009, { 0x405, DFF_Prop_adjustValue, 0 } },
+ { 0x2000, { 0x406, 10800, 0 } }, // 8
+ { 0x2000, { 0x407, 10800, 0 } },
+ { 0x8000, { 21600, 0, 0x409 } }, // 10
+ { 0x8000, { 21600, 0, 0x405 } },
+ { 0x000, { 21600, 0, 0 } }
+};
+const sal_uInt16 mso_sptTextCirclePourSegm[] =
+{
+ 0xA504, 0x8000, 0xA504, 0x8000
+};
+const SvxMSDffHandle mso_sptTextCirclePourHandle[] =
+{
+ { SvxMSDffHandleFlags::POLAR | SvxMSDffHandleFlags::RADIUS_RANGE,
+ 0x101, 0x100, 10800, 10800, 0, 10800, MIN_INT32, 0x7fffffff }
+};
+const sal_Int32 mso_sptTextCirclePourDefault[] =
+{
+ 2, -179, 5400
+};
+const mso_CustomShape msoTextCirclePour =
+{
+ const_cast<SvxMSDffVertPair*>(mso_sptTextCirclePourVert), SAL_N_ELEMENTS( mso_sptTextCirclePourVert ),
+ const_cast<sal_uInt16*>(mso_sptTextCirclePourSegm), sizeof( mso_sptTextCirclePourSegm ) >> 1,
+ const_cast<SvxMSDffCalculationData*>(mso_sptTextCirclePourCalc), SAL_N_ELEMENTS( mso_sptTextCirclePourCalc ),
+ const_cast<sal_Int32*>(mso_sptTextCirclePourDefault),
+ const_cast<SvxMSDffTextRectangles*>(mso_sptFontWorkTextRect), SAL_N_ELEMENTS( mso_sptFontWorkTextRect ),
+ 21600, 21600,
+ MIN_INT32, MIN_INT32,
+ nullptr, 0,
+ const_cast<SvxMSDffHandle*>(mso_sptTextCirclePourHandle), SAL_N_ELEMENTS( mso_sptTextCirclePourHandle )
+};
+
+const SvxMSDffVertPair mso_sptTextButtonPourVert[] =
+{
+ { 0, 0 }, { 21600, 21600 }, { 2 MSO_I, 3 MSO_I }, { 4 MSO_I, 3 MSO_I },
+ { 6 MSO_I, 6 MSO_I }, { 7 MSO_I, 7 MSO_I }, { 10 MSO_I, 11 MSO_I }, { 12 MSO_I, 11 MSO_I },
+ { 0x16 MSO_I, 16 MSO_I }, { 0x15 MSO_I, 16 MSO_I },
+ { 0x16 MSO_I, 15 MSO_I }, { 0x15 MSO_I, 15 MSO_I },
+ { 6 MSO_I, 6 MSO_I }, { 7 MSO_I, 7 MSO_I }, { 10 MSO_I, 13 MSO_I }, { 12 MSO_I, 13 MSO_I },
+ { 0, 0 }, { 21600, 21600 }, { 2 MSO_I, 5 MSO_I }, { 4 MSO_I, 5 MSO_I }
+};
+const SvxMSDffCalculationData mso_sptTextButtonPourCalc[] =
+{
+ { 0x400a, { 10800, DFF_Prop_adjustValue, 0 } }, // 0x00
+ { 0x4009, { 10800, DFF_Prop_adjustValue, 0 } }, // 0x01
+ { 0x2000, { 0x400, 10800, 0 } }, // 0x02
+ { 0x2000, { 0x401, 10800, 0 } }, // 0x03
+ { 0x8000, { 21600, 0, 0x402 } }, // 0x04
+ { 0x8000, { 21600, 0, 0x403 } }, // 0x05
+
+ { 0x8000, { 10800, 0, DFF_Prop_adjust2Value } }, // 0x06
+ { 0x8000, { 21600, 0, 0x406 } }, // 0x07
+
+ { 0x600a, { DFF_Prop_adjust2Value, DFF_Prop_adjustValue, 0 } }, // 0x08
+ { 0x6009, { DFF_Prop_adjust2Value, DFF_Prop_adjustValue, 0 } }, // 0x09
+ { 0x2000, { 0x408, 10800, 0 } }, // 0x0a
+ { 0x2000, { 0x409, 10800, 0 } }, // 0x0b
+ { 0x8000, { 21600, 0, 0x40a } }, // 0x0c
+ { 0x8000, { 21600, 0, 0x40b } }, // 0x0d
+ { 0x2001, { 0x406, 1, 2 } }, // 0x0e
+ { 0x4000, { 10800, 0x40e, 0 } }, // 0x0f
+ { 0x8000, { 10800, 0, 0x40e } }, // 0x10
+ { 0x6001, { 0x40e, 0x40e, 1 } }, // 0x11
+ { 0x6001, { DFF_Prop_adjust2Value, DFF_Prop_adjust2Value, 1 } }, // 0x12
+ { 0xA000, { 0x412, 0, 0x411 } }, // 0x13
+ { 0x200d, { 0x413, 0, 0 } }, // 0x14
+ { 0x4000, { 10800, 0x414, 0 } }, // 0x15
+ { 0x8000, { 10800, 0, 0x414 } } // 0x16
+};
+const sal_uInt16 mso_sptTextButtonPourSegm[] =
+{
+ 0xA504, 0x8000, // clockwise arc
+ 0xA504, 0x8000, // clockwise arc
+ 0x4000, 0x0001, 0x8000,
+ 0x4000, 0x0001, 0x8000,
+ 0xA304, 0x8000, // counter clockwise
+ 0xA304, 0x8000 // counter clockwise
+};
+const SvxMSDffHandle mso_sptTextButtonPourHandle[] =
+{
+ { SvxMSDffHandleFlags::POLAR | SvxMSDffHandleFlags::RADIUS_RANGE,
+ 0x101, 0x100, 10800, 10800, 4320, 10800, MIN_INT32, 0x7fffffff }
+};
+const sal_Int32 mso_sptTextButtonPourDefault[] =
+{
+ 2, 180, 5400
+};
+const mso_CustomShape msoTextButtonPour =
+{
+ const_cast<SvxMSDffVertPair*>(mso_sptTextButtonPourVert), SAL_N_ELEMENTS( mso_sptTextButtonPourVert ),
+ const_cast<sal_uInt16*>(mso_sptTextButtonPourSegm), sizeof( mso_sptTextButtonPourSegm ) >> 1,
+ const_cast<SvxMSDffCalculationData*>(mso_sptTextButtonPourCalc), SAL_N_ELEMENTS( mso_sptTextButtonPourCalc ),
+ const_cast<sal_Int32*>(mso_sptTextButtonPourDefault),
+ const_cast<SvxMSDffTextRectangles*>(mso_sptFontWorkTextRect), SAL_N_ELEMENTS( mso_sptFontWorkTextRect ),
+ 21600, 21600,
+ MIN_INT32, MIN_INT32,
+ nullptr, 0,
+ const_cast<SvxMSDffHandle*>(mso_sptTextButtonPourHandle), SAL_N_ELEMENTS( mso_sptTextButtonPourHandle )
+};
+
+const SvxMSDffVertPair mso_sptTextCurveUpVert[] =
+{
+ { 0, 0 MSO_I }, { 4900, 1 MSO_I /*12170->0 14250 ->0*/ }, { 11640, 2 MSO_I /*12170->0 12800 ->0*/ }, { 21600, 0 },
+ { 0, 4 MSO_I /*12170->0 17220 ->21600*/ }, { 3700, 21600 }, { 8500, 21600 }, { 10100, 21600 }, { 14110, 21600 }, { 15910, 21600 }, { 21600, 4 MSO_I /*12170->0 17220 ->21600*/ }
+};
+const SvxMSDffCalculationData mso_sptTextCurveUpCalc[] =
+{
+ { 0x2000, { DFF_Prop_adjustValue, 0, 0 } }, // 400
+ { 0x4001, { 14250, 0x400, 12170 } }, // 401
+ { 0x4001, { 12800, 0x400, 12170 } }, // 402
+ { 0x4001, { 6380, 0x400, 12170 } }, // 403
+ { 0x8000, { 21600, 0, 0x403 } } // 404
+};
+const sal_uInt16 mso_sptTextCurveUpSegm[] =
+{
+ 0x4000, 0x2001, 0x8000,
+ 0x4000, 0x2002, 0x8000
+};
+const SvxMSDffHandle mso_sptTextCurveUpHandle[] =
+{
+ { SvxMSDffHandleFlags::RANGE,
+ 0, 0x100, 10800, 10800, MIN_INT32, 0x7fffffff, 0, 12170 }
+};
+const sal_Int32 mso_sptTextCurveUpDefault[] =
+{
+ 1, 9900
+};
+const mso_CustomShape msoTextCurveUp =
+{
+ const_cast<SvxMSDffVertPair*>(mso_sptTextCurveUpVert), SAL_N_ELEMENTS( mso_sptTextCurveUpVert ),
+ const_cast<sal_uInt16*>(mso_sptTextCurveUpSegm), sizeof( mso_sptTextCurveUpSegm ) >> 1,
+ const_cast<SvxMSDffCalculationData*>(mso_sptTextCurveUpCalc), SAL_N_ELEMENTS( mso_sptTextCurveUpCalc ),
+ const_cast<sal_Int32*>(mso_sptTextCurveUpDefault),
+ const_cast<SvxMSDffTextRectangles*>(mso_sptFontWorkTextRect), SAL_N_ELEMENTS( mso_sptFontWorkTextRect ),
+ 21600, 21600,
+ MIN_INT32, MIN_INT32,
+ nullptr, 0,
+ const_cast<SvxMSDffHandle*>(mso_sptTextCurveUpHandle), SAL_N_ELEMENTS( mso_sptTextCurveUpHandle )
+};
+
+const SvxMSDffVertPair mso_sptTextCurveDownVert[] =
+{
+// { 0, 0 MSO_I }, { 4900, 1 MSO_I /*12170->0 14250 ->0*/ }, { 11640, 2 MSO_I /*12170->0 12800 ->0*/ }, { 21600, 0 },
+ { 0, 0 }, { 9960, 2 MSO_I }, { 16700, 1 MSO_I }, { 21600, 0 MSO_I },
+
+// { 0, 4 MSO_I /*12170->0 17220 ->21600*/ }, { 3700, 21600 }, { 8500, 21600 }, { 10100, 21600 }, { 14110, 21600 }, { 15910, 21600 }, { 21600, 4 MSO_I /*12170->0 17220 ->21600*/ }
+ { 0, 4 MSO_I }, { 5690, 21600 }, { 7490, 21600 }, { 11500, 21600 }, { 13100, 21600 }, { 17900, 21600 }, { 21600, 4 MSO_I }
+};
+const SvxMSDffHandle mso_sptTextCurveDownHandle[] =
+{
+ { SvxMSDffHandleFlags::RANGE,
+ 21600, 0x100, 10800, 10800, MIN_INT32, 0x7fffffff, 0, 12170 }
+};
+const mso_CustomShape msoTextCurveDown =
+{
+ const_cast<SvxMSDffVertPair*>(mso_sptTextCurveDownVert), SAL_N_ELEMENTS( mso_sptTextCurveDownVert ),
+ const_cast<sal_uInt16*>(mso_sptTextCurveUpSegm), sizeof( mso_sptTextCurveUpSegm ) >> 1,
+ const_cast<SvxMSDffCalculationData*>(mso_sptTextCurveUpCalc), SAL_N_ELEMENTS( mso_sptTextCurveUpCalc ),
+ const_cast<sal_Int32*>(mso_sptTextCurveUpDefault),
+ const_cast<SvxMSDffTextRectangles*>(mso_sptFontWorkTextRect), SAL_N_ELEMENTS( mso_sptFontWorkTextRect ),
+ 21600, 21600,
+ MIN_INT32, MIN_INT32,
+ nullptr, 0,
+ const_cast<SvxMSDffHandle*>(mso_sptTextCurveDownHandle), SAL_N_ELEMENTS( mso_sptTextCurveDownHandle )
+};
+
+const SvxMSDffVertPair mso_sptTextCanUpVert[] =
+{
+ { 0, 1 MSO_I }, { 900, 0 }, { 7100, 0 }, { 10800, 0 }, { 14500, 0 }, { 20700, 0 }, { 21600, 1 MSO_I },
+ { 0, 21600 }, { 900, 4 MSO_I }, { 7100, 0 MSO_I }, { 10800, 0 MSO_I }, { 14500, 0 MSO_I }, { 20700, 4 MSO_I }, { 21600, 21600 }
+};
+const SvxMSDffCalculationData mso_sptTextCanUpCalc[] =
+{
+ { 0x2000, { DFF_Prop_adjustValue, 0, 0 } }, // 400
+ { 0x8000, { 21600, 0, DFF_Prop_adjustValue } }, // 401
+ { 0x2000, { DFF_Prop_adjustValue, 0, 14400 } }, // 402
+ { 0x4001, { 5470, 0x402, 7200 } }, // 403
+ { 0x4000, { 16130, 0x403, 0 } } // 404
+};
+const sal_uInt16 mso_sptTextCanUpSegm[] =
+{
+ 0x4000, 0x2002, 0x8000,
+ 0x4000, 0x2002, 0x8000
+};
+const SvxMSDffHandle mso_sptTextCanUpHandle[] =
+{
+ { SvxMSDffHandleFlags::RANGE,
+ 10800, 0x100, 10800, 10800, MIN_INT32, 0x7fffffff, 14400, 21600 }
+};
+const sal_Int32 mso_sptTextCanUpDefault[] =
+{
+ 1, 18500
+};
+const mso_CustomShape msoTextCanUp =
+{
+ const_cast<SvxMSDffVertPair*>(mso_sptTextCanUpVert), SAL_N_ELEMENTS( mso_sptTextCanUpVert ),
+ const_cast<sal_uInt16*>(mso_sptTextCanUpSegm), sizeof( mso_sptTextCanUpSegm ) >> 1,
+ const_cast<SvxMSDffCalculationData*>(mso_sptTextCanUpCalc), SAL_N_ELEMENTS( mso_sptTextCanUpCalc ),
+ const_cast<sal_Int32*>(mso_sptTextCanUpDefault),
+ const_cast<SvxMSDffTextRectangles*>(mso_sptFontWorkTextRect), SAL_N_ELEMENTS( mso_sptFontWorkTextRect ),
+ 21600, 21600,
+ MIN_INT32, MIN_INT32,
+ nullptr, 0,
+ const_cast<SvxMSDffHandle*>(mso_sptTextCanUpHandle), SAL_N_ELEMENTS( mso_sptTextCanUpHandle )
+};
+
+const SvxMSDffVertPair mso_sptTextCanDownVert[] =
+{
+ { 0, 0 }, { 900, 2 MSO_I }, { 7100, 0 MSO_I }, { 10800, 0 MSO_I }, { 14500, 0 MSO_I }, { 20700, 2 MSO_I }, { 21600, 0 },
+ { 0, 1 MSO_I }, { 900, 21600 }, { 7100, 21600 }, { 10800, 21600 }, { 14500, 21600 }, { 20700, 21600 }, { 21600, 1 MSO_I }
+};
+const SvxMSDffCalculationData mso_sptTextCanDownCalc[] =
+{
+ { 0x2000, { DFF_Prop_adjustValue, 0, 0 } }, // 400
+ { 0x8000, { 21600, 0, DFF_Prop_adjustValue } }, // 401
+ { 0x4001, { 5470, 0x400, 7200 } } // 402
+};
+const SvxMSDffHandle mso_sptTextCanDownHandle[] =
+{
+ { SvxMSDffHandleFlags::RANGE,
+ 10800, 0x100, 10800, 10800, MIN_INT32, 0x7fffffff, 0, 7200 }
+};
+const sal_Int32 mso_sptTextCanDownDefault[] =
+{
+ 1, 3100
+};
+const mso_CustomShape msoTextCanDown =
+{
+ const_cast<SvxMSDffVertPair*>(mso_sptTextCanDownVert), SAL_N_ELEMENTS( mso_sptTextCanDownVert ),
+ const_cast<sal_uInt16*>(mso_sptTextCanUpSegm), sizeof( mso_sptTextCanUpSegm ) >> 1,
+ const_cast<SvxMSDffCalculationData*>(mso_sptTextCanDownCalc), SAL_N_ELEMENTS( mso_sptTextCanDownCalc ),
+ const_cast<sal_Int32*>(mso_sptTextCanDownDefault),
+ const_cast<SvxMSDffTextRectangles*>(mso_sptFontWorkTextRect), SAL_N_ELEMENTS( mso_sptFontWorkTextRect ),
+ 21600, 21600,
+ MIN_INT32, MIN_INT32,
+ nullptr, 0,
+ const_cast<SvxMSDffHandle*>(mso_sptTextCanDownHandle), SAL_N_ELEMENTS( mso_sptTextCanDownHandle )
+};
+
+const SvxMSDffVertPair mso_sptTextInflateVert[] =
+{
+ { 0, 0 MSO_I }, { 4100, 1 MSO_I }, { 7300, 0 }, { 10800, 0 }, { 14300, 0 }, { 17500, 1 MSO_I }, { 21600, 0 MSO_I },
+ { 0, 2 MSO_I }, { 4100, 3 MSO_I }, { 7300, 21600 }, { 10800, 21600 }, { 14300, 21600 }, { 17500, 3 MSO_I }, { 21600, 2 MSO_I }
+};
+const SvxMSDffCalculationData mso_sptTextInflateCalc[] =
+{
+ { 0x2000, { DFF_Prop_adjustValue, 0, 0 } }, // 400
+ { 0x4001, { 1530, 0x400, 4650 } }, // 401
+ { 0x8000, { 21600, 0, 0x400 } }, // 402
+ { 0x8000, { 21600, 0, 0x401 } } // 403
+};
+const SvxMSDffHandle mso_sptTextInflateHandle[] =
+{
+ { SvxMSDffHandleFlags::RANGE,
+ 0, 0x100, 10800, 10800, MIN_INT32, 0x7fffffff, 0, 4650 }
+};
+const sal_Int32 mso_sptTextInflateDefault[] =
+{
+ 1, 2950
+};
+const mso_CustomShape msoTextInflate =
+{
+ const_cast<SvxMSDffVertPair*>(mso_sptTextInflateVert), SAL_N_ELEMENTS( mso_sptTextInflateVert ),
+ const_cast<sal_uInt16*>(mso_sptTextCanUpSegm), sizeof( mso_sptTextCanUpSegm ) >> 1,
+ const_cast<SvxMSDffCalculationData*>(mso_sptTextInflateCalc), SAL_N_ELEMENTS( mso_sptTextInflateCalc ),
+ const_cast<sal_Int32*>(mso_sptTextInflateDefault),
+ const_cast<SvxMSDffTextRectangles*>(mso_sptFontWorkTextRect), SAL_N_ELEMENTS( mso_sptFontWorkTextRect ),
+ 21600, 21600,
+ MIN_INT32, MIN_INT32,
+ nullptr, 0,
+ const_cast<SvxMSDffHandle*>(mso_sptTextInflateHandle), SAL_N_ELEMENTS( mso_sptTextInflateHandle )
+};
+
+const SvxMSDffVertPair mso_sptTextDeflateVert[] =
+{
+ { 0, 0 }, { 3500, 1 MSO_I }, { 7100, 0 MSO_I }, { 10800, 0 MSO_I }, { 14500, 0 MSO_I }, { 18100, 1 MSO_I }, { 21600, 0 },
+ { 0, 21600 }, { 3500, 3 MSO_I }, { 7100, 2 MSO_I }, { 10800, 2 MSO_I }, { 14500, 2 MSO_I }, { 18100, 3 MSO_I }, { 21600, 21600 }
+};
+const SvxMSDffCalculationData mso_sptTextDeflateCalc[] =
+{
+ { 0x2000, { DFF_Prop_adjustValue, 0, 0 } }, // 400
+ { 0x2001, { 0x400, 5320, 7100 } }, // 401
+ { 0x8000, { 21600, 0, 0x400 } }, // 402
+ { 0x8000, { 21600, 0, 0x401 } } // 403
+};
+const SvxMSDffHandle mso_sptTextDeflateHandle[] =
+{
+ { SvxMSDffHandleFlags::RANGE,
+ 10800, 0x100, 10800, 10800, MIN_INT32, 0x7fffffff, 0, 8100 }
+};
+const mso_CustomShape msoTextDeflate =
+{
+ const_cast<SvxMSDffVertPair*>(mso_sptTextDeflateVert), SAL_N_ELEMENTS( mso_sptTextDeflateVert ),
+ const_cast<sal_uInt16*>(mso_sptTextCanUpSegm), sizeof( mso_sptTextCanUpSegm ) >> 1,
+ const_cast<SvxMSDffCalculationData*>(mso_sptTextDeflateCalc), SAL_N_ELEMENTS( mso_sptTextDeflateCalc ),
+ const_cast<sal_Int32*>(mso_sptDefault8100),
+ const_cast<SvxMSDffTextRectangles*>(mso_sptFontWorkTextRect), SAL_N_ELEMENTS( mso_sptFontWorkTextRect ),
+ 21600, 21600,
+ MIN_INT32, MIN_INT32,
+ nullptr, 0,
+ const_cast<SvxMSDffHandle*>(mso_sptTextDeflateHandle), SAL_N_ELEMENTS( mso_sptTextDeflateHandle )
+};
+
+const SvxMSDffVertPair mso_sptTextInflateBottomVert[] =
+{
+ { 0, 0 }, { 21600, 0 },
+ { 0, 0 MSO_I }, { 3500, 3 MSO_I }, { 7300, 21600 }, { 10800, 21600 }, { 14300, 21600 }, { 18100, 3 MSO_I }, { 21600, 0 MSO_I }
+};
+const SvxMSDffCalculationData mso_sptTextInflateBottomCalc[] =
+{
+ { 0x2000, { DFF_Prop_adjustValue, 0, 0 } }, // 400
+ { 0x2000, { 0x400, 0, 11150 } }, // 401 0->10450
+ { 0x2001, { 0x401, 3900, 10450 } }, // 402
+ { 0x2000, { 0x402, 17700, 0 } } // 403
+};
+const sal_uInt16 mso_sptTextInflateBottomSegm[] =
+{
+ 0x4000, 0x0001, 0x8000,
+ 0x4000, 0x2002, 0x8000
+};
+const SvxMSDffHandle mso_sptTextInflateBottomHandle[] =
+{
+ { SvxMSDffHandleFlags::RANGE,
+ 0, 0x100, 10800, 10800, MIN_INT32, 0x7fffffff, 11150, 21600 }
+};
+const sal_Int32 mso_sptTextInflateBottomDefault[] =
+{
+ 1, 14700
+};
+const mso_CustomShape msoTextInflateBottom =
+{
+ const_cast<SvxMSDffVertPair*>(mso_sptTextInflateBottomVert), SAL_N_ELEMENTS( mso_sptTextInflateBottomVert ),
+ const_cast<sal_uInt16*>(mso_sptTextInflateBottomSegm), sizeof( mso_sptTextInflateBottomSegm ) >> 1,
+ const_cast<SvxMSDffCalculationData*>(mso_sptTextInflateBottomCalc), SAL_N_ELEMENTS( mso_sptTextInflateBottomCalc ),
+ const_cast<sal_Int32*>(mso_sptTextInflateBottomDefault),
+ const_cast<SvxMSDffTextRectangles*>(mso_sptFontWorkTextRect), SAL_N_ELEMENTS( mso_sptFontWorkTextRect ),
+ 21600, 21600,
+ MIN_INT32, MIN_INT32,
+ nullptr, 0,
+ const_cast<SvxMSDffHandle*>(mso_sptTextInflateBottomHandle), SAL_N_ELEMENTS( mso_sptTextInflateBottomHandle )
+};
+
+const SvxMSDffVertPair mso_sptTextDeflateBottomVert[] =
+{
+ { 0, 0 }, { 21600, 0 },
+ { 0, 21600 }, { 2900, 3 MSO_I }, { 7200, 0 MSO_I }, { 10800, 0 MSO_I }, { 14400, 0 MSO_I }, { 18700, 3 MSO_I }, { 21600, 21600 }
+};
+const SvxMSDffCalculationData mso_sptTextDeflateBottomCalc[] =
+{
+ { 0x2000, { DFF_Prop_adjustValue, 0, 0 } }, // 400
+ { 0x2000, { 0x400, 0, 1350 } }, // 401 0->20250
+ { 0x2001, { 0x401, 12070, 20250 } }, // 402
+ { 0x2000, { 0x402, 9530, 0 } } // 403
+};
+const sal_uInt16 mso_sptTextDeflateBottomSegm[] =
+{
+ 0x4000, 0x0001, 0x8000,
+ 0x4000, 0x2002, 0x8000
+};
+const SvxMSDffHandle mso_sptTextDeflateBottomHandle[] =
+{
+ { SvxMSDffHandleFlags::RANGE,
+ 10800, 0x100, 10800, 10800, MIN_INT32, 0x7fffffff, 1350, 21600 }
+};
+const sal_Int32 mso_sptTextDeflateBottomDefault[] =
+{
+ 1, 11500
+};
+const mso_CustomShape msoTextDeflateBottom =
+{
+ const_cast<SvxMSDffVertPair*>(mso_sptTextDeflateBottomVert), SAL_N_ELEMENTS( mso_sptTextDeflateBottomVert ),
+ const_cast<sal_uInt16*>(mso_sptTextDeflateBottomSegm), sizeof( mso_sptTextDeflateBottomSegm ) >> 1,
+ const_cast<SvxMSDffCalculationData*>(mso_sptTextDeflateBottomCalc), SAL_N_ELEMENTS( mso_sptTextDeflateBottomCalc ),
+ const_cast<sal_Int32*>(mso_sptTextDeflateBottomDefault),
+ const_cast<SvxMSDffTextRectangles*>(mso_sptFontWorkTextRect), SAL_N_ELEMENTS( mso_sptFontWorkTextRect ),
+ 21600, 21600,
+ MIN_INT32, MIN_INT32,
+ nullptr, 0,
+ const_cast<SvxMSDffHandle*>(mso_sptTextDeflateBottomHandle), SAL_N_ELEMENTS( mso_sptTextDeflateBottomHandle )
+};
+
+const SvxMSDffVertPair mso_sptTextInflateTopVert[] =
+{
+ { 0, 0 MSO_I }, { 3500, 1 MSO_I }, { 7300, 0 }, { 10800, 0 }, { 14300, 0 }, { 18100, 1 MSO_I }, { 21600, 0 MSO_I },
+ { 0, 21600 }, { 21600, 21600 }
+};
+const SvxMSDffCalculationData mso_sptTextInflateTopCalc[] =
+{
+ { 0x2000, { DFF_Prop_adjustValue, 0, 0 } }, // 400
+ { 0x2001, { 0x400, 3900, 10450 } } // 401
+};
+const sal_uInt16 mso_sptTextInflateTopSegm[] =
+{
+ 0x4000, 0x2002, 0x8000,
+ 0x4000, 0x0001, 0x8000
+};
+const SvxMSDffHandle mso_sptTextInflateTopHandle[] =
+{
+ { SvxMSDffHandleFlags::RANGE,
+ 0, 0x100, 10800, 10800, MIN_INT32, 0x7fffffff, 0, 10450 }
+};
+const sal_Int32 mso_sptTextInflateTopDefault[] =
+{
+ 1, 6900
+};
+const mso_CustomShape msoTextInflateTop =
+{
+ const_cast<SvxMSDffVertPair*>(mso_sptTextInflateTopVert), SAL_N_ELEMENTS( mso_sptTextInflateTopVert ),
+ const_cast<sal_uInt16*>(mso_sptTextInflateTopSegm), sizeof( mso_sptTextInflateTopSegm ) >> 1,
+ const_cast<SvxMSDffCalculationData*>(mso_sptTextInflateTopCalc), SAL_N_ELEMENTS( mso_sptTextInflateTopCalc ),
+ const_cast<sal_Int32*>(mso_sptTextInflateTopDefault),
+ const_cast<SvxMSDffTextRectangles*>(mso_sptFontWorkTextRect), SAL_N_ELEMENTS( mso_sptFontWorkTextRect ),
+ 21600, 21600,
+ MIN_INT32, MIN_INT32,
+ nullptr, 0,
+ const_cast<SvxMSDffHandle*>(mso_sptTextInflateTopHandle), SAL_N_ELEMENTS( mso_sptTextInflateTopHandle )
+};
+
+const SvxMSDffVertPair mso_sptTextDeflateTopVert[] =
+{
+ { 0, 0 }, { 2900, 1 MSO_I }, { 7200, 0 MSO_I }, { 10800, 0 MSO_I }, { 14400, 0 MSO_I }, { 18700, 1 MSO_I }, { 21600, 0 },
+ { 0, 21600 }, { 21600, 21600 }
+};
+const SvxMSDffCalculationData mso_sptTextDeflateTopCalc[] =
+{
+ { 0x2000, { DFF_Prop_adjustValue, 0, 0 } }, // 400
+ { 0x2001, { 0x400, 12070, 20250 } } // 402
+};
+const sal_uInt16 mso_sptTextDeflateTopSegm[] =
+{
+ 0x4000, 0x2002, 0x8000,
+ 0x4000, 0x0001, 0x8000
+};
+const SvxMSDffHandle mso_sptTextDeflateTopHandle[] =
+{
+ { SvxMSDffHandleFlags::RANGE,
+ 10800, 0x100, 10800, 10800, MIN_INT32, 0x7fffffff, 0, 20250 }
+};
+const sal_Int32 mso_sptTextDeflateTopDefault[] =
+{
+ 1, 10100
+};
+const mso_CustomShape msoTextDeflateTop =
+{
+ const_cast<SvxMSDffVertPair*>(mso_sptTextDeflateTopVert), SAL_N_ELEMENTS( mso_sptTextDeflateTopVert ),
+ const_cast<sal_uInt16*>(mso_sptTextDeflateTopSegm), sizeof( mso_sptTextDeflateTopSegm ) >> 1,
+ const_cast<SvxMSDffCalculationData*>(mso_sptTextDeflateTopCalc), SAL_N_ELEMENTS( mso_sptTextDeflateTopCalc ),
+ const_cast<sal_Int32*>(mso_sptTextDeflateTopDefault),
+ const_cast<SvxMSDffTextRectangles*>(mso_sptFontWorkTextRect), SAL_N_ELEMENTS( mso_sptFontWorkTextRect ),
+ 21600, 21600,
+ MIN_INT32, MIN_INT32,
+ nullptr, 0,
+ const_cast<SvxMSDffHandle*>(mso_sptTextDeflateTopHandle), SAL_N_ELEMENTS( mso_sptTextDeflateTopHandle )
+};
+
+const SvxMSDffVertPair mso_sptTextDeflateInflateVert[] =
+{
+ { 0, 0 }, { 21600, 0 },
+ { 0, 10100 }, { 3300, 3 MSO_I }, { 7100, 5 MSO_I }, { 10800, 5 MSO_I }, { 14500, 5 MSO_I }, { 18300, 3 MSO_I }, { 21600, 10100 },
+ { 0, 11500 }, { 3300, 4 MSO_I }, { 7100, 6 MSO_I }, { 10800, 6 MSO_I }, { 14500, 6 MSO_I }, { 18300, 4 MSO_I }, { 21600, 11500 },
+ { 0, 21600 }, { 21600, 21600 }
+};
+const SvxMSDffCalculationData mso_sptTextDeflateInflateCalc[] =
+{
+ { 0x2000, { DFF_Prop_adjustValue, 0, 0 } }, // 400
+ { 0x8000, { 10800, 0, 0x400 } }, // 401
+ { 0x2001, { 0x401, 5770, 9500 } }, // 402
+ { 0x8000, { 10100, 0, 0x402 } }, // 403
+ { 0x8000, { 11500, 0, 0x402 } }, // 404
+ { 0x2000, { 0x400, 0, 700 } }, // 405
+ { 0x2000, { 0x400, 700, 0 } } // 406
+};
+const sal_uInt16 mso_sptTextDeflateInflateSegm[] =
+{
+ 0x4000, 0x0001, 0x8000,
+ 0x4000, 0x2002, 0x8000,
+ 0x4000, 0x2002, 0x8000,
+ 0x4000, 0x0001, 0x8000
+};
+const SvxMSDffHandle mso_sptTextDeflateInflateHandle[] =
+{
+ { SvxMSDffHandleFlags::RANGE,
+ 10800, 0x100, 10800, 10800, MIN_INT32, 0x7fffffff, 1300, 20300 }
+};
+const sal_Int32 mso_sptTextDeflateInflateDefault[] =
+{
+ 1, 6500
+};
+const mso_CustomShape msoTextDeflateInflate =
+{
+ const_cast<SvxMSDffVertPair*>(mso_sptTextDeflateInflateVert), SAL_N_ELEMENTS( mso_sptTextDeflateInflateVert ),
+ const_cast<sal_uInt16*>(mso_sptTextDeflateInflateSegm), sizeof( mso_sptTextDeflateInflateSegm ) >> 1,
+ const_cast<SvxMSDffCalculationData*>(mso_sptTextDeflateInflateCalc), SAL_N_ELEMENTS( mso_sptTextDeflateInflateCalc ),
+ const_cast<sal_Int32*>(mso_sptTextDeflateInflateDefault),
+ const_cast<SvxMSDffTextRectangles*>(mso_sptFontWorkTextRect), SAL_N_ELEMENTS( mso_sptFontWorkTextRect ),
+ 21600, 21600,
+ MIN_INT32, MIN_INT32,
+ nullptr, 0,
+ const_cast<SvxMSDffHandle*>(mso_sptTextDeflateInflateHandle), SAL_N_ELEMENTS( mso_sptTextDeflateInflateHandle )
+};
+
+const SvxMSDffVertPair mso_sptTextDeflateInflateDeflateVert[] =
+{
+ { 0, 0 }, { 21600, 0 },
+ { 0, 6600 }, { 3600, 3 MSO_I }, { 7250, 4 MSO_I }, { 10800, 4 MSO_I }, { 14350, 4 MSO_I }, { 18000, 3 MSO_I }, { 21600, 6600 },
+ { 0, 7500 }, { 3600, 5 MSO_I }, { 7250, 6 MSO_I }, { 10800, 6 MSO_I }, { 14350, 6 MSO_I }, { 18000, 5 MSO_I }, { 21600, 7500 },
+ { 0, 14100 }, { 3600, 9 MSO_I }, { 7250, 10 MSO_I }, { 10800, 10 MSO_I }, { 14350, 10 MSO_I }, { 18000, 9 MSO_I }, { 21600, 14100 },
+ { 0, 15000 }, { 3600, 7 MSO_I }, { 7250, 8 MSO_I }, { 10800, 8 MSO_I }, { 14350, 8 MSO_I }, { 18000, 7 MSO_I }, { 21600, 15000 },
+ { 0, 21600 }, { 21600, 21600 }
+};
+const SvxMSDffCalculationData mso_sptTextDeflateInflateDeflateCalc[] =
+{
+ { 0x2000, { DFF_Prop_adjustValue, 0, 850 } }, // 400
+ { 0x2001, { 0x400, 6120, 8700 } },
+ { 0x2000, { 0x401, 0, 4280 } },
+ { 0x4000, { 6600, 0x402, 0 } },
+ { 0x2000, { DFF_Prop_adjustValue, 0, 450 } }, // 404
+ { 0x2000, { 0x403, 900, 0 } }, // 405
+ { 0x2000, { 0x404, 900, 0 } }, // 406
+ { 0x8000, { 21600, 0, 0x403 } }, // 407
+ { 0x8000, { 21600, 0, 0x404 } }, // 408
+ { 0x8000, { 21600, 0, 0x405 } }, // 409
+ { 0x8000, { 21600, 0, 0x406 } } // 410
+};
+const sal_uInt16 mso_sptTextDeflateInflateDeflateSegm[] =
+{
+ 0x4000, 0x0001, 0x8000,
+ 0x4000, 0x2002, 0x8000,
+ 0x4000, 0x2002, 0x8000,
+ 0x4000, 0x2002, 0x8000,
+ 0x4000, 0x2002, 0x8000,
+ 0x4000, 0x0001, 0x8000
+};
+const SvxMSDffHandle mso_sptTextDeflateInflateDeflateHandle[] =
+{
+ { SvxMSDffHandleFlags::RANGE,
+ 10800, 0x100, 10800, 10800, MIN_INT32, 0x7fffffff, 850, 9550 }
+};
+const sal_Int32 mso_sptTextDeflateInflateDeflateDefault[] =
+{
+ 1, 6050
+};
+const mso_CustomShape msoTextDeflateInflateDeflate =
+{
+ const_cast<SvxMSDffVertPair*>(mso_sptTextDeflateInflateDeflateVert), SAL_N_ELEMENTS( mso_sptTextDeflateInflateDeflateVert ),
+ const_cast<sal_uInt16*>(mso_sptTextDeflateInflateDeflateSegm), sizeof( mso_sptTextDeflateInflateDeflateSegm ) >> 1,
+ const_cast<SvxMSDffCalculationData*>(mso_sptTextDeflateInflateDeflateCalc), SAL_N_ELEMENTS( mso_sptTextDeflateInflateDeflateCalc ),
+ const_cast<sal_Int32*>(mso_sptTextDeflateInflateDeflateDefault),
+ const_cast<SvxMSDffTextRectangles*>(mso_sptFontWorkTextRect), SAL_N_ELEMENTS( mso_sptFontWorkTextRect ),
+ 21600, 21600,
+ MIN_INT32, MIN_INT32,
+ nullptr, 0,
+ const_cast<SvxMSDffHandle*>(mso_sptTextDeflateInflateDeflateHandle), SAL_N_ELEMENTS( mso_sptTextDeflateInflateDeflateHandle )
+};
+
+const SvxMSDffVertPair mso_sptTextWave1Vert[] = // adjustment1 : 0 - 4459
+{ // adjustment2 : 8640 - 12960
+ { 7 MSO_I, 0 MSO_I }, { 15 MSO_I, 9 MSO_I }, { 16 MSO_I, 10 MSO_I }, { 12 MSO_I, 0 MSO_I },
+ { 29 MSO_I, 1 MSO_I }, { 27 MSO_I, 28 MSO_I }, { 25 MSO_I, 26 MSO_I }, { 24 MSO_I, 1 MSO_I }
+};
+const sal_uInt16 mso_sptTextWave1Segm[] =
+{
+ 0x4000, 0x2001, 0x8000,
+ 0x4000, 0x2001, 0x8000
+};
+const mso_CustomShape msoTextWave1 =
+{
+ const_cast<SvxMSDffVertPair*>(mso_sptTextWave1Vert), SAL_N_ELEMENTS( mso_sptTextWave1Vert ),
+ const_cast<sal_uInt16*>(mso_sptTextWave1Segm), sizeof( mso_sptTextWave1Segm ) >> 1,
+ const_cast<SvxMSDffCalculationData*>(mso_sptWaveCalc), SAL_N_ELEMENTS( mso_sptWaveCalc ),
+ const_cast<sal_Int32*>(mso_sptWaveDefault),
+ const_cast<SvxMSDffTextRectangles*>(mso_sptFontWorkTextRect), SAL_N_ELEMENTS( mso_sptFontWorkTextRect ),
+ 21600, 21600,
+ MIN_INT32, MIN_INT32,
+ const_cast<SvxMSDffVertPair*>(mso_sptWaveGluePoints), SAL_N_ELEMENTS( mso_sptWaveGluePoints ),
+ const_cast<SvxMSDffHandle*>(mso_sptWaveHandle), SAL_N_ELEMENTS( mso_sptWaveHandle )
+};
+
+const SvxMSDffVertPair mso_sptTextWave2Vert[] = // adjustment1 : 0 - 4459
+{ // adjustment2 : 8640 - 12960
+ { 7 MSO_I, 0 MSO_I }, { 15 MSO_I, 10 MSO_I }, { 16 MSO_I, 9 MSO_I }, { 12 MSO_I, 0 MSO_I },
+ { 29 MSO_I, 1 MSO_I }, { 27 MSO_I, 26 MSO_I }, { 25 MSO_I, 28 MSO_I }, { 24 MSO_I, 1 MSO_I }
+};
+const mso_CustomShape msoTextWave2 =
+{
+ const_cast<SvxMSDffVertPair*>(mso_sptTextWave2Vert), SAL_N_ELEMENTS( mso_sptTextWave2Vert ),
+ const_cast<sal_uInt16*>(mso_sptTextWave1Segm), sizeof( mso_sptTextWave1Segm ) >> 1,
+ const_cast<SvxMSDffCalculationData*>(mso_sptWaveCalc), SAL_N_ELEMENTS( mso_sptWaveCalc ),
+ const_cast<sal_Int32*>(mso_sptWaveDefault),
+ const_cast<SvxMSDffTextRectangles*>(mso_sptFontWorkTextRect), SAL_N_ELEMENTS( mso_sptFontWorkTextRect ),
+ 21600, 21600,
+ MIN_INT32, MIN_INT32,
+ const_cast<SvxMSDffVertPair*>(mso_sptWaveGluePoints), SAL_N_ELEMENTS( mso_sptWaveGluePoints ),
+ const_cast<SvxMSDffHandle*>(mso_sptWaveHandle), SAL_N_ELEMENTS( mso_sptWaveHandle )
+};
+
+const SvxMSDffVertPair mso_sptTextWave3Vert[] = // adjustment1 : 0 - 2230
+{ // adjustment2 : 8640 - 12960
+ { 7 MSO_I, 0 MSO_I }, { 15 MSO_I, 9 MSO_I }, { 0x1f MSO_I, 10 MSO_I }, { 0x12 MSO_I, 0 MSO_I }, { 0x1e MSO_I, 9 MSO_I }, { 16 MSO_I, 10 MSO_I }, { 12 MSO_I, 0 MSO_I },
+ { 29 MSO_I, 1 MSO_I }, { 27 MSO_I, 28 MSO_I }, { 0x21 MSO_I, 26 MSO_I }, { 0x13 MSO_I, 1 MSO_I }, { 0x20 MSO_I, 28 MSO_I }, { 25 MSO_I, 26 MSO_I }, { 24 MSO_I, 1 MSO_I }
+};
+const sal_uInt16 mso_sptTextWave3Segm[] =
+{
+ 0x4000, 0x2002, 0x8000,
+ 0x4000, 0x2002, 0x8000
+};
+const mso_CustomShape msoTextWave3 =
+{
+ const_cast<SvxMSDffVertPair*>(mso_sptTextWave3Vert), SAL_N_ELEMENTS( mso_sptTextWave3Vert ),
+ const_cast<sal_uInt16*>(mso_sptTextWave3Segm), sizeof( mso_sptTextWave3Segm ) >> 1,
+ const_cast<SvxMSDffCalculationData*>(mso_sptDoubleWaveCalc), SAL_N_ELEMENTS( mso_sptDoubleWaveCalc ),
+ const_cast<sal_Int32*>(mso_sptDoubleWaveDefault),
+ const_cast<SvxMSDffTextRectangles*>(mso_sptDoubleWaveTextRect), SAL_N_ELEMENTS( mso_sptDoubleWaveTextRect ),
+ 21600, 21600,
+ MIN_INT32, MIN_INT32,
+ const_cast<SvxMSDffVertPair*>(mso_sptDoubleWaveGluePoints), SAL_N_ELEMENTS( mso_sptDoubleWaveGluePoints ),
+ const_cast<SvxMSDffHandle*>(mso_sptDoubleWaveHandle), SAL_N_ELEMENTS( mso_sptDoubleWaveHandle )
+};
+
+const SvxMSDffVertPair mso_sptTextWave4Vert[] = // adjustment1 : 0 - 2230
+{ // adjustment2 : 8640 - 12960
+ { 7 MSO_I, 0 MSO_I }, { 15 MSO_I, 10 MSO_I }, { 0x1f MSO_I, 9 MSO_I }, { 0x12 MSO_I, 0 MSO_I }, { 0x1e MSO_I, 10 MSO_I }, { 16 MSO_I, 9 MSO_I }, { 12 MSO_I, 0 MSO_I },
+ { 29 MSO_I, 1 MSO_I }, { 27 MSO_I, 26 MSO_I }, { 0x21 MSO_I, 28 MSO_I }, { 0x13 MSO_I, 1 MSO_I }, { 0x20 MSO_I, 26 MSO_I }, { 25 MSO_I, 28 MSO_I }, { 24 MSO_I, 1 MSO_I }
+};
+const mso_CustomShape msoTextWave4 =
+{
+ const_cast<SvxMSDffVertPair*>(mso_sptTextWave4Vert), SAL_N_ELEMENTS( mso_sptTextWave4Vert ),
+ const_cast<sal_uInt16*>(mso_sptTextWave3Segm), sizeof( mso_sptTextWave3Segm ) >> 1,
+ const_cast<SvxMSDffCalculationData*>(mso_sptDoubleWaveCalc), SAL_N_ELEMENTS( mso_sptDoubleWaveCalc ),
+ const_cast<sal_Int32*>(mso_sptDoubleWaveDefault),
+ const_cast<SvxMSDffTextRectangles*>(mso_sptDoubleWaveTextRect), SAL_N_ELEMENTS( mso_sptDoubleWaveTextRect ),
+ 21600, 21600,
+ MIN_INT32, MIN_INT32,
+ const_cast<SvxMSDffVertPair*>(mso_sptDoubleWaveGluePoints), SAL_N_ELEMENTS( mso_sptDoubleWaveGluePoints ),
+ const_cast<SvxMSDffHandle*>(mso_sptDoubleWaveHandle), SAL_N_ELEMENTS( mso_sptDoubleWaveHandle )
+};
+
+const sal_Int32 mso_sptCalloutDefault1[] =
+{
+ 4, -1800, 24500, -1800, 4000
+};
+const sal_Int32 mso_sptCalloutDefault2[] =
+{
+ 4, -8300, 24500, -1800, 4000
+};
+const sal_Int32 mso_sptCalloutDefault3[] =
+{
+ 6, -10000, 24500, -3600, 4000, -1800, 4000
+};
+const sal_Int32 mso_sptCalloutDefault4[] =
+{
+ 8, 23400, 24500, 25200, 21600, 25200, 4000, 23400, 4000
+};
+const SvxMSDffVertPair mso_sptCalloutVert1[] =
+{
+ { 0, 0 }, { 21600, 0 }, { 21600, 21600 }, { 0, 21600 }, { 0 MSO_I, 1 MSO_I }, { 2 MSO_I, 3 MSO_I }
+};
+const SvxMSDffHandle mso_sptCalloutHandle1[] =
+{
+ { SvxMSDffHandleFlags::NONE,
+ 0x100, 0x101, 10800, 10800, MIN_INT32, 0x7fffffff, MIN_INT32, 0x7fffffff },
+ { SvxMSDffHandleFlags::NONE,
+ 0x102, 0x103, 10800, 10800, MIN_INT32, 0x7fffffff, MIN_INT32, 0x7fffffff }
+};
+const sal_uInt16 mso_sptCalloutSegm1a[] =
+{
+ 0x4000, 0x0003, 0x6000, 0x8000,
+ 0x4000, 0x0001, 0x8000
+};
+const sal_uInt16 mso_sptCalloutSegm1b[] =
+{
+ 0x4000, 0xab00, 0x0003, 0x6000, 0x8000, // NO STROKE
+ 0x4000, 0x0001, 0x8000
+};
+const SvxMSDffVertPair mso_sptCallout1Vert[] =
+{
+ { 0, 0 }, { 21600, 0 }, { 21600, 21600 }, { 0, 21600 }, { 0 MSO_I, 1 MSO_I }, { 2 MSO_I, 3 MSO_I }, { 2 MSO_I, 0 }, { 2 MSO_I, 21600 }
+};
+const sal_uInt16 mso_sptCallout1Segm1a[] =
+{
+ 0x4000, 0x0003, 0x6000, 0x8000,
+ 0x4000, 0x0001, 0x8000,
+ 0x4000, 0x0001, 0x8000
+};
+const sal_uInt16 mso_sptCallout1Segm1b[] =
+{
+ 0x4000, 0xab00, 0x0003, 0x6000, 0x8000, // NO STROKE
+ 0x4000, 0x0001, 0x8000,
+ 0x4000, 0x0001, 0x8000
+};
+const SvxMSDffVertPair mso_sptCallout2Verta[] =
+{
+ { 0, 0 }, { 21600, 0 }, { 21600, 21600 }, { 0, 21600 }, { 0 MSO_I, 1 MSO_I }, { 2 MSO_I, 3 MSO_I }, { 2 MSO_I, 3 MSO_I }, { 4 MSO_I, 5 MSO_I }
+};
+const SvxMSDffVertPair mso_sptCallout2Vertb[] =
+{
+ { 0, 0 }, { 21600, 0 }, { 21600, 21600 }, { 0, 21600 }, { 0 MSO_I, 1 MSO_I }, { 2 MSO_I, 3 MSO_I }, { 2 MSO_I, 3 MSO_I }, { 4 MSO_I, 5 MSO_I }, { 4 MSO_I, 0 }, { 4 MSO_I, 21600 }
+};
+const SvxMSDffHandle mso_sptCalloutHandle2[] =
+{
+ { SvxMSDffHandleFlags::NONE,
+ 0x100, 0x101, 10800, 10800, MIN_INT32, 0x7fffffff, MIN_INT32, 0x7fffffff },
+ { SvxMSDffHandleFlags::NONE,
+ 0x102, 0x103, 10800, 10800, MIN_INT32, 0x7fffffff, MIN_INT32, 0x7fffffff },
+ { SvxMSDffHandleFlags::NONE,
+ 0x104, 0x105, 10800, 10800, MIN_INT32, 0x7fffffff, MIN_INT32, 0x7fffffff }
+};
+const sal_uInt16 mso_sptCallout2Segm1a[] =
+{
+ 0x4000, 0x0003, 0x6000, 0x8000,
+ 0x4000, 0x0001, 0x8000,
+ 0x4000, 0x0001, 0x8000,
+ 0x4000, 0x0001, 0x8000
+};
+const sal_uInt16 mso_sptCallout2Segm1b[] =
+{
+ 0x4000, 0xab00, 0x0003, 0x6000, 0x8000, // NO STROKE
+ 0x4000, 0x0001, 0x8000,
+ 0x4000, 0x0001, 0x8000,
+ 0x4000, 0x0001, 0x8000
+};
+const sal_uInt16 mso_sptCallout2Segm1c[] =
+{
+ 0x4000, 0x0003, 0x6000, 0x8000,
+ 0x4000, 0x0001, 0x8000,
+ 0x4000, 0x0001, 0x8000,
+ 0x4000, 0x0001, 0x8000,
+ 0x4000, 0x0001, 0x8000
+};
+const sal_uInt16 mso_sptCallout2Segm1d[] =
+{
+ 0x4000, 0xab00, 0x0003, 0x6000, 0x8000, // NO STROKE
+ 0x4000, 0x0001, 0x8000,
+ 0x4000, 0x0001, 0x8000,
+ 0x4000, 0x0001, 0x8000,
+ 0x4000, 0x0001, 0x8000
+};
+const SvxMSDffVertPair mso_sptCallout3Verta[] =
+{
+ { 0, 0 }, { 21600, 0 }, { 21600, 21600 }, { 0, 21600 }, { 6 MSO_I, 7 MSO_I }, { 4 MSO_I, 5 MSO_I }, { 2 MSO_I, 3 MSO_I }, { 0 MSO_I, 1 MSO_I }
+};
+const SvxMSDffVertPair mso_sptCallout3Vertb[] =
+{
+ { 0, 0 }, { 21600, 0 }, { 21600, 21600 }, { 0, 21600 }, { 6 MSO_I, 7 MSO_I }, { 4 MSO_I, 5 MSO_I }, { 2 MSO_I, 3 MSO_I }, { 0 MSO_I, 1 MSO_I }, { 6 MSO_I, 0 }, { 6 MSO_I, 21600 }
+};
+const SvxMSDffHandle mso_sptCalloutHandle3[] =
+{
+ { SvxMSDffHandleFlags::NONE,
+ 0x100, 0x101, 10800, 10800, MIN_INT32, 0x7fffffff, MIN_INT32, 0x7fffffff },
+ { SvxMSDffHandleFlags::NONE,
+ 0x102, 0x103, 10800, 10800, MIN_INT32, 0x7fffffff, MIN_INT32, 0x7fffffff },
+ { SvxMSDffHandleFlags::NONE,
+ 0x104, 0x105, 10800, 10800, MIN_INT32, 0x7fffffff, MIN_INT32, 0x7fffffff },
+ { SvxMSDffHandleFlags::NONE,
+ 0x106, 0x107, 10800, 10800, MIN_INT32, 0x7fffffff, MIN_INT32, 0x7fffffff }
+};
+const sal_uInt16 mso_sptCallout3Segm1a[] =
+{
+ 0x4000, 0x0003, 0x6000, 0x8000,
+ 0x4000, 0xaa00, 0x0003, 0x8000 // NO_FILL
+};
+const sal_uInt16 mso_sptCallout3Segm1b[] =
+{
+ 0x4000, 0xab00, 0x0003, 0x6000, 0x8000, // NO STROKE
+ 0x4000, 0xaa00, 0x0003, 0x8000 // NO FILL
+};
+const sal_uInt16 mso_sptCallout3Segm1c[] =
+{
+ 0x4000, 0x0003, 0x6000, 0x8000,
+ 0x4000, 0xaa00, 0x0003, 0x8000, // NO FILL
+ 0x4000, 0x0001, 0x8000
+};
+const sal_uInt16 mso_sptCallout3Segm1d[] =
+{
+ 0x4000, 0xab00, 0x0003, 0x6000, 0x8000, // NO STROKE
+ 0x4000, 0xaa00, 0x0003, 0x8000, // NO FILL
+ 0x4000, 0x0001, 0x8000
+};
+
+const SvxMSDffCalculationData mso_sptCalloutCalc[] =
+{
+ { 0x2000, { DFF_Prop_adjustValue, 0, 0 } },
+ { 0x2000, { DFF_Prop_adjust2Value, 0, 0 } },
+ { 0x2000, { DFF_Prop_adjust3Value, 0, 0 } },
+ { 0x2000, { DFF_Prop_adjust4Value, 0, 0 } },
+ { 0x2000, { DFF_Prop_adjust5Value, 0, 0 } },
+ { 0x2000, { DFF_Prop_adjust6Value, 0, 0 } },
+ { 0x2000, { DFF_Prop_adjust7Value, 0, 0 } },
+ { 0x2000, { DFF_Prop_adjust8Value, 0, 0 } }
+};
+
+const mso_CustomShape msoCallout90 =
+{
+ const_cast<SvxMSDffVertPair*>(mso_sptCalloutVert1), SAL_N_ELEMENTS( mso_sptCalloutVert1 ),
+ const_cast<sal_uInt16*>(mso_sptCalloutSegm1b), sizeof( mso_sptCalloutSegm1b ) >> 1,
+ const_cast<SvxMSDffCalculationData*>(mso_sptCalloutCalc), SAL_N_ELEMENTS( mso_sptCalloutCalc ),
+ const_cast<sal_Int32*>(mso_sptCalloutDefault1),
+ nullptr, 0,
+ 21600, 21600,
+ MIN_INT32, MIN_INT32,
+ nullptr, 0,
+ const_cast<SvxMSDffHandle*>(mso_sptCalloutHandle1), SAL_N_ELEMENTS( mso_sptCalloutHandle1 )
+};
+const mso_CustomShape msoCallout1 =
+{
+ const_cast<SvxMSDffVertPair*>(mso_sptCalloutVert1), SAL_N_ELEMENTS( mso_sptCalloutVert1 ),
+ const_cast<sal_uInt16*>(mso_sptCalloutSegm1b), sizeof( mso_sptCalloutSegm1b ) >> 1,
+ const_cast<SvxMSDffCalculationData*>(mso_sptCalloutCalc), SAL_N_ELEMENTS( mso_sptCalloutCalc ),
+ const_cast<sal_Int32*>(mso_sptCalloutDefault2),
+ nullptr, 0,
+ 21600, 21600,
+ MIN_INT32, MIN_INT32,
+ nullptr, 0,
+ const_cast<SvxMSDffHandle*>(mso_sptCalloutHandle1), SAL_N_ELEMENTS( mso_sptCalloutHandle1 )
+};
+const mso_CustomShape msoCallout2 =
+{
+ const_cast<SvxMSDffVertPair*>(mso_sptCallout2Verta), SAL_N_ELEMENTS( mso_sptCallout2Verta ),
+ const_cast<sal_uInt16*>(mso_sptCallout2Segm1b), sizeof( mso_sptCallout2Segm1b ) >> 1,
+ const_cast<SvxMSDffCalculationData*>(mso_sptCalloutCalc), SAL_N_ELEMENTS( mso_sptCalloutCalc ),
+ const_cast<sal_Int32*>(mso_sptCalloutDefault3),
+ nullptr, 0,
+ 21600, 21600,
+ MIN_INT32, MIN_INT32,
+ nullptr, 0,
+ const_cast<SvxMSDffHandle*>(mso_sptCalloutHandle2), SAL_N_ELEMENTS( mso_sptCalloutHandle2 )
+};
+const mso_CustomShape msoCallout3 =
+{
+ const_cast<SvxMSDffVertPair*>(mso_sptCallout3Verta), SAL_N_ELEMENTS( mso_sptCallout3Verta ),
+ const_cast<sal_uInt16*>(mso_sptCallout3Segm1b), sizeof( mso_sptCallout3Segm1b ) >> 1,
+ const_cast<SvxMSDffCalculationData*>(mso_sptCalloutCalc), SAL_N_ELEMENTS( mso_sptCalloutCalc ),
+ const_cast<sal_Int32*>(mso_sptCalloutDefault4),
+ nullptr, 0,
+ 21600, 21600,
+ MIN_INT32, MIN_INT32,
+ nullptr, 0,
+ const_cast<SvxMSDffHandle*>(mso_sptCalloutHandle3), SAL_N_ELEMENTS( mso_sptCalloutHandle3 )
+};
+const mso_CustomShape msoAccentCallout90 =
+{
+ const_cast<SvxMSDffVertPair*>(mso_sptCalloutVert1), SAL_N_ELEMENTS( mso_sptCalloutVert1 ),
+ const_cast<sal_uInt16*>(mso_sptCalloutSegm1b), sizeof( mso_sptCalloutSegm1b ) >> 1,
+ const_cast<SvxMSDffCalculationData*>(mso_sptCalloutCalc), SAL_N_ELEMENTS( mso_sptCalloutCalc ),
+ const_cast<sal_Int32*>(mso_sptCalloutDefault1),
+ nullptr, 0,
+ 21600, 21600,
+ MIN_INT32, MIN_INT32,
+ nullptr, 0,
+ const_cast<SvxMSDffHandle*>(mso_sptCalloutHandle1), SAL_N_ELEMENTS( mso_sptCalloutHandle1 )
+};
+const mso_CustomShape msoAccentCallout1 =
+{
+ const_cast<SvxMSDffVertPair*>(mso_sptCallout1Vert), SAL_N_ELEMENTS( mso_sptCallout1Vert ),
+ const_cast<sal_uInt16*>(mso_sptCallout1Segm1b), sizeof( mso_sptCallout1Segm1b ) >> 1,
+ const_cast<SvxMSDffCalculationData*>(mso_sptCalloutCalc), SAL_N_ELEMENTS( mso_sptCalloutCalc ),
+ const_cast<sal_Int32*>(mso_sptCalloutDefault2),
+ nullptr, 0,
+ 21600, 21600,
+ MIN_INT32, MIN_INT32,
+ nullptr, 0,
+ const_cast<SvxMSDffHandle*>(mso_sptCalloutHandle1), SAL_N_ELEMENTS( mso_sptCalloutHandle1 )
+};
+const mso_CustomShape msoAccentCallout2 =
+{
+ const_cast<SvxMSDffVertPair*>(mso_sptCallout2Vertb), SAL_N_ELEMENTS( mso_sptCallout2Vertb ),
+ const_cast<sal_uInt16*>(mso_sptCallout2Segm1d), sizeof( mso_sptCallout2Segm1d ) >> 1,
+ const_cast<SvxMSDffCalculationData*>(mso_sptCalloutCalc), SAL_N_ELEMENTS( mso_sptCalloutCalc ),
+ const_cast<sal_Int32*>(mso_sptCalloutDefault3),
+ nullptr, 0,
+ 21600, 21600,
+ MIN_INT32, MIN_INT32,
+ nullptr, 0,
+ const_cast<SvxMSDffHandle*>(mso_sptCalloutHandle2), SAL_N_ELEMENTS( mso_sptCalloutHandle2 )
+};
+const mso_CustomShape msoAccentCallout3 =
+{
+ const_cast<SvxMSDffVertPair*>(mso_sptCallout3Vertb), SAL_N_ELEMENTS( mso_sptCallout3Vertb ),
+ const_cast<sal_uInt16*>(mso_sptCallout3Segm1d), sizeof( mso_sptCallout3Segm1d ) >> 1,
+ const_cast<SvxMSDffCalculationData*>(mso_sptCalloutCalc), SAL_N_ELEMENTS( mso_sptCalloutCalc ),
+ const_cast<sal_Int32*>(mso_sptCalloutDefault4),
+ nullptr, 0,
+ 21600, 21600,
+ MIN_INT32, MIN_INT32,
+ nullptr, 0,
+ const_cast<SvxMSDffHandle*>(mso_sptCalloutHandle3), SAL_N_ELEMENTS( mso_sptCalloutHandle3 )
+};
+const mso_CustomShape msoBorderCallout90 =
+{
+ const_cast<SvxMSDffVertPair*>(mso_sptCalloutVert1), SAL_N_ELEMENTS( mso_sptCalloutVert1 ),
+ const_cast<sal_uInt16*>(mso_sptCalloutSegm1a), sizeof( mso_sptCalloutSegm1a ) >> 1,
+ const_cast<SvxMSDffCalculationData*>(mso_sptCalloutCalc), SAL_N_ELEMENTS( mso_sptCalloutCalc ),
+ const_cast<sal_Int32*>(mso_sptCalloutDefault1),
+ nullptr, 0,
+ 21600, 21600,
+ MIN_INT32, MIN_INT32,
+ nullptr, 0,
+ const_cast<SvxMSDffHandle*>(mso_sptCalloutHandle1), SAL_N_ELEMENTS( mso_sptCalloutHandle1 )
+};
+const mso_CustomShape msoBorderCallout1 =
+{
+ const_cast<SvxMSDffVertPair*>(mso_sptCalloutVert1), SAL_N_ELEMENTS( mso_sptCalloutVert1 ),
+ const_cast<sal_uInt16*>(mso_sptCalloutSegm1a), sizeof( mso_sptCalloutSegm1a ) >> 1,
+ const_cast<SvxMSDffCalculationData*>(mso_sptCalloutCalc), SAL_N_ELEMENTS( mso_sptCalloutCalc ),
+ const_cast<sal_Int32*>(mso_sptCalloutDefault2),
+ nullptr, 0,
+ 21600, 21600,
+ MIN_INT32, MIN_INT32,
+ nullptr, 0,
+ const_cast<SvxMSDffHandle*>(mso_sptCalloutHandle1), SAL_N_ELEMENTS( mso_sptCalloutHandle1 )
+};
+const mso_CustomShape msoBorderCallout2 =
+{
+ const_cast<SvxMSDffVertPair*>(mso_sptCallout2Verta), SAL_N_ELEMENTS( mso_sptCallout2Verta ),
+ const_cast<sal_uInt16*>(mso_sptCallout2Segm1a), sizeof( mso_sptCallout2Segm1a ) >> 1,
+ const_cast<SvxMSDffCalculationData*>(mso_sptCalloutCalc), SAL_N_ELEMENTS( mso_sptCalloutCalc ),
+ const_cast<sal_Int32*>(mso_sptCalloutDefault3),
+ nullptr, 0,
+ 21600, 21600,
+ MIN_INT32, MIN_INT32,
+ nullptr, 0,
+ const_cast<SvxMSDffHandle*>(mso_sptCalloutHandle2), SAL_N_ELEMENTS( mso_sptCalloutHandle2 )
+};
+const mso_CustomShape msoBorderCallout3 =
+{
+ const_cast<SvxMSDffVertPair*>(mso_sptCallout3Verta), SAL_N_ELEMENTS( mso_sptCallout3Verta ),
+ const_cast<sal_uInt16*>(mso_sptCallout3Segm1a), sizeof( mso_sptCallout3Segm1a ) >> 1,
+ const_cast<SvxMSDffCalculationData*>(mso_sptCalloutCalc), SAL_N_ELEMENTS( mso_sptCalloutCalc ),
+ const_cast<sal_Int32*>(mso_sptCalloutDefault4),
+ nullptr, 0,
+ 21600, 21600,
+ MIN_INT32, MIN_INT32,
+ nullptr, 0,
+ const_cast<SvxMSDffHandle*>(mso_sptCalloutHandle3), SAL_N_ELEMENTS( mso_sptCalloutHandle3 )
+};
+const mso_CustomShape msoAccentBorderCallout90 =
+{
+ const_cast<SvxMSDffVertPair*>(mso_sptCalloutVert1), SAL_N_ELEMENTS( mso_sptCalloutVert1 ),
+ const_cast<sal_uInt16*>(mso_sptCalloutSegm1a), sizeof( mso_sptCalloutSegm1a ) >> 1,
+ const_cast<SvxMSDffCalculationData*>(mso_sptCalloutCalc), SAL_N_ELEMENTS( mso_sptCalloutCalc ),
+ const_cast<sal_Int32*>(mso_sptCalloutDefault1),
+ nullptr, 0,
+ 21600, 21600,
+ MIN_INT32, MIN_INT32,
+ nullptr, 0,
+ const_cast<SvxMSDffHandle*>(mso_sptCalloutHandle1), SAL_N_ELEMENTS( mso_sptCalloutHandle1 )
+};
+const mso_CustomShape msoAccentBorderCallout1 =
+{
+ const_cast<SvxMSDffVertPair*>(mso_sptCallout1Vert), SAL_N_ELEMENTS( mso_sptCallout1Vert ),
+ const_cast<sal_uInt16*>(mso_sptCallout1Segm1a), sizeof( mso_sptCallout1Segm1a ) >> 1,
+ const_cast<SvxMSDffCalculationData*>(mso_sptCalloutCalc), SAL_N_ELEMENTS( mso_sptCalloutCalc ),
+ const_cast<sal_Int32*>(mso_sptCalloutDefault2),
+ nullptr, 0,
+ 21600, 21600,
+ MIN_INT32, MIN_INT32,
+ nullptr, 0,
+ const_cast<SvxMSDffHandle*>(mso_sptCalloutHandle1), SAL_N_ELEMENTS( mso_sptCalloutHandle1 )
+};
+const mso_CustomShape msoAccentBorderCallout2 =
+{
+ const_cast<SvxMSDffVertPair*>(mso_sptCallout2Vertb), SAL_N_ELEMENTS( mso_sptCallout2Vertb ),
+ const_cast<sal_uInt16*>(mso_sptCallout2Segm1c), sizeof( mso_sptCallout2Segm1c ) >> 1,
+ const_cast<SvxMSDffCalculationData*>(mso_sptCalloutCalc), SAL_N_ELEMENTS( mso_sptCalloutCalc ),
+ const_cast<sal_Int32*>(mso_sptCalloutDefault3),
+ nullptr, 0,
+ 21600, 21600,
+ MIN_INT32, MIN_INT32,
+ nullptr, 0,
+ const_cast<SvxMSDffHandle*>(mso_sptCalloutHandle2), SAL_N_ELEMENTS( mso_sptCalloutHandle2 )
+};
+const mso_CustomShape msoAccentBorderCallout3 =
+{
+ const_cast<SvxMSDffVertPair*>(mso_sptCallout3Vertb), SAL_N_ELEMENTS( mso_sptCallout3Vertb ),
+ const_cast<sal_uInt16*>(mso_sptCallout3Segm1c), sizeof( mso_sptCallout3Segm1c ) >> 1,
+ const_cast<SvxMSDffCalculationData*>(mso_sptCalloutCalc), SAL_N_ELEMENTS( mso_sptCalloutCalc ),
+ const_cast<sal_Int32*>(mso_sptCalloutDefault4),
+ nullptr, 0,
+ 21600, 21600,
+ MIN_INT32, MIN_INT32,
+ nullptr, 0,
+ const_cast<SvxMSDffHandle*>(mso_sptCalloutHandle3), SAL_N_ELEMENTS( mso_sptCalloutHandle3 )
+};
+
+const SvxMSDffVertPair mso_sptStraightConnector1Vert[] =
+{
+ { 0, 0 }, { 21600, 21600 }
+};
+const sal_uInt16 mso_sptStraightConnector1Segm[] =
+{
+ 0x4000, 0x0001, 0x8000
+};
+const mso_CustomShape msoStraightConnector1 =
+{
+ const_cast<SvxMSDffVertPair*>(mso_sptStraightConnector1Vert), SAL_N_ELEMENTS( mso_sptStraightConnector1Vert ),
+ const_cast<sal_uInt16*>(mso_sptStraightConnector1Segm), sizeof( mso_sptStraightConnector1Segm ) >> 1,
+ nullptr, 0,
+ nullptr,
+ nullptr, 0,
+ 21600, 21600,
+ MIN_INT32, MIN_INT32,
+ nullptr, 0,
+ nullptr, 0
+};
+
+const SvxMSDffVertPair mso_sptBentConnector2Vert[] =
+{
+ { 0, 0 }, { 21600, 0 }, { 21600, 21600 }
+};
+const sal_uInt16 mso_sptBentConnector2Segm[] =
+{
+ 0x4000, 0x0002, 0x8000
+};
+const mso_CustomShape msoBentConnector2 =
+{
+ const_cast<SvxMSDffVertPair*>(mso_sptBentConnector2Vert), SAL_N_ELEMENTS( mso_sptBentConnector2Vert ),
+ const_cast<sal_uInt16*>(mso_sptBentConnector2Segm), sizeof( mso_sptBentConnector2Segm ) >> 1,
+ nullptr, 0,
+ nullptr,
+ nullptr, 0,
+ 21600, 21600,
+ MIN_INT32, MIN_INT32,
+ nullptr, 0,
+ nullptr, 0
+};
+
+const SvxMSDffVertPair mso_sptBentConnector3Vert[] =
+{
+ { 0, 0 }, { 0 MSO_I, 0 }, { 0 MSO_I, 21600 }, { 21600, 21600 }
+};
+const sal_uInt16 mso_sptBentConnector3Segm[] =
+{
+ 0x4000, 0x0003, 0x8000
+};
+const SvxMSDffCalculationData mso_sptBentConnector3Calc[] =
+{
+ { 0x2000, { DFF_Prop_adjustValue, 0, 0 } }
+};
+const sal_Int32 mso_sptBentConnector3Default[] =
+{
+ 1, 10800
+};
+const SvxMSDffHandle mso_sptBentConnector3Handle[] =
+{
+ { SvxMSDffHandleFlags::RANGE,
+ 0x100, 10800, 10800, 10800, MIN_INT32, 0x7fffffff, MIN_INT32, 0x7fffffff }
+};
+const mso_CustomShape msoBentConnector3 =
+{
+ const_cast<SvxMSDffVertPair*>(mso_sptBentConnector3Vert), SAL_N_ELEMENTS( mso_sptBentConnector3Vert ),
+ const_cast<sal_uInt16*>(mso_sptBentConnector3Segm), sizeof( mso_sptBentConnector3Segm ) >> 1,
+ const_cast<SvxMSDffCalculationData*>(mso_sptBentConnector3Calc), SAL_N_ELEMENTS( mso_sptBentConnector3Calc ),
+ const_cast<sal_Int32*>(mso_sptBentConnector3Default),
+ nullptr, 0,
+ 21600, 21600,
+ MIN_INT32, MIN_INT32,
+ nullptr, 0,
+ const_cast<SvxMSDffHandle*>(mso_sptBentConnector3Handle), SAL_N_ELEMENTS( mso_sptBentConnector3Handle )
+};
+
+const SvxMSDffVertPair mso_sptBentConnector4Vert[] =
+{
+ { 0, 0 }, { 0 MSO_I, 0 }, { 0 MSO_I, 1 MSO_I }, { 21600, 1 MSO_I }, { 21600, 21600 }
+};
+const sal_uInt16 mso_sptBentConnector4Segm[] =
+{
+ 0x4000, 0x0004, 0x8000
+};
+const SvxMSDffCalculationData mso_sptBentConnector4Calc[] =
+{
+ { 0x2000, { DFF_Prop_adjustValue, 0, 0 } },
+ { 0x2000, { DFF_Prop_adjust2Value, 0, 0 } },
+ { 0x2000, { 0x400, 21600, 0 } },
+ { 0x2001, { 0x402, 1, 2 } },
+ { 0x2001, { 0x401, 1, 2 } }
+};
+const sal_Int32 mso_sptBentConnector4Default[] =
+{
+ 2, 10800, 10800
+};
+const SvxMSDffHandle mso_sptBentConnector4Handle[] =
+{
+ { SvxMSDffHandleFlags::RANGE | SvxMSDffHandleFlags::RANGE_Y_MIN_IS_SPECIAL | SvxMSDffHandleFlags::RANGE_Y_MAX_IS_SPECIAL,
+ 0x100, 4 + 3, 10800, 10800, MIN_INT32, 0x7fffffff, 4 + 3, 4 + 3 },
+ { SvxMSDffHandleFlags::RANGE | SvxMSDffHandleFlags::RANGE_X_MIN_IS_SPECIAL | SvxMSDffHandleFlags::RANGE_X_MAX_IS_SPECIAL,
+ 3 + 3, 0x101, 10800, 10800, 3 + 3, 3 + 3, MIN_INT32, 0x7fffffff }
+};
+const mso_CustomShape msoBentConnector4 =
+{
+ const_cast<SvxMSDffVertPair*>(mso_sptBentConnector4Vert), SAL_N_ELEMENTS( mso_sptBentConnector4Vert ),
+ const_cast<sal_uInt16*>(mso_sptBentConnector4Segm), sizeof( mso_sptBentConnector4Segm ) >> 1,
+ const_cast<SvxMSDffCalculationData*>(mso_sptBentConnector4Calc), SAL_N_ELEMENTS( mso_sptBentConnector4Calc ),
+ const_cast<sal_Int32*>(mso_sptBentConnector4Default),
+ nullptr, 0,
+ 21600, 21600,
+ MIN_INT32, MIN_INT32,
+ nullptr, 0,
+ const_cast<SvxMSDffHandle*>(mso_sptBentConnector4Handle), SAL_N_ELEMENTS( mso_sptBentConnector4Handle )
+};
+
+const SvxMSDffVertPair mso_sptBentConnector5Vert[] =
+{
+ { 0, 0 }, { 0 MSO_I, 0 }, { 0 MSO_I, 4 MSO_I }, { 1 MSO_I, 4 MSO_I }, { 1 MSO_I, 21600 }, { 21600, 21600 }
+};
+const sal_uInt16 mso_sptBentConnector5Segm[] =
+{
+ 0x4000, 0x0005, 0x8000
+};
+const SvxMSDffCalculationData mso_sptBentConnector5Calc[] =
+{
+ { 0x2000, { DFF_Prop_adjustValue, 0, 0 } },
+ { 0x2000, { DFF_Prop_adjust3Value, 0, 0 } },
+ { 0x6000, { 0x400, 0x401, 0 } },
+ { 0x2001, { 0x402, 1, 2 } },
+ { 0x2000, { DFF_Prop_adjust2Value, 0, 0 } },
+ { 0x2001, { 0x404, 1, 2 } },
+ { 0x4000, { 21600, 0x404, 0 } },
+ { 0x2001, { 0x406, 1, 2 } }
+};
+const sal_Int32 mso_sptBentConnector5Default[] =
+{
+ 3, 10800, 10800, 10800
+};
+const SvxMSDffHandle mso_sptBentConnector5Handle[] =
+{
+ { SvxMSDffHandleFlags::RANGE | SvxMSDffHandleFlags::RANGE_Y_MIN_IS_SPECIAL | SvxMSDffHandleFlags::RANGE_Y_MAX_IS_SPECIAL,
+ 0x100, 5 + 3, 10800, 10800, MIN_INT32, 0x7fffffff, 5 + 3, 5 + 3 },
+ { SvxMSDffHandleFlags::RANGE | SvxMSDffHandleFlags::RANGE_X_MIN_IS_SPECIAL | SvxMSDffHandleFlags::RANGE_X_MAX_IS_SPECIAL,
+ 3 + 3, 0x101, 10800, 10800, 3 + 3, 3 + 3, MIN_INT32, 0x7fffffff },
+ { SvxMSDffHandleFlags::RANGE | SvxMSDffHandleFlags::RANGE_Y_MIN_IS_SPECIAL | SvxMSDffHandleFlags::RANGE_Y_MAX_IS_SPECIAL,
+ 0x102, 7 + 3, 10800, 10800, MIN_INT32, 0x7fffffff, 7 + 3, 7 + 3 }
+};
+const mso_CustomShape msoBentConnector5 =
+{
+ const_cast<SvxMSDffVertPair*>(mso_sptBentConnector5Vert), SAL_N_ELEMENTS( mso_sptBentConnector5Vert ),
+ const_cast<sal_uInt16*>(mso_sptBentConnector5Segm), sizeof( mso_sptBentConnector5Segm ) >> 1,
+ const_cast<SvxMSDffCalculationData*>(mso_sptBentConnector5Calc), SAL_N_ELEMENTS( mso_sptBentConnector5Calc ),
+ const_cast<sal_Int32*>(mso_sptBentConnector5Default),
+ nullptr, 0,
+ 21600, 21600,
+ MIN_INT32, MIN_INT32,
+ nullptr, 0,
+ const_cast<SvxMSDffHandle*>(mso_sptBentConnector5Handle), SAL_N_ELEMENTS( mso_sptBentConnector5Handle )
+};
+
+const SvxMSDffVertPair mso_sptCurvedConnector2Vert[] =
+{
+ { 0, 0 }, { 10800, 0 }, { 21600, 10800 }, { 21600, 21600 }
+};
+const sal_uInt16 mso_sptCurvedConnector2Segm[] =
+{
+ 0x4000, 0x2001, 0x8000
+};
+const mso_CustomShape msoCurvedConnector2 =
+{
+ const_cast<SvxMSDffVertPair*>(mso_sptCurvedConnector2Vert), SAL_N_ELEMENTS( mso_sptCurvedConnector2Vert ),
+ const_cast<sal_uInt16*>(mso_sptCurvedConnector2Segm), sizeof( mso_sptCurvedConnector2Segm ) >> 1,
+ nullptr, 0,
+ nullptr,
+ nullptr, 0,
+ 21600, 21600,
+ MIN_INT32, MIN_INT32,
+ nullptr, 0,
+ nullptr, 0
+};
+
+const SvxMSDffVertPair mso_sptCurvedConnector3Vert[] =
+{
+ { 0, 0 }, { 1 MSO_I, 0 }, { 0 MSO_I, 5400 }, { 0 MSO_I, 10800 }, { 0 MSO_I, 16200 }, { 3 MSO_I, 21600 }, { 21600, 21600 }
+};
+const sal_uInt16 mso_sptCurvedConnector3Segm[] =
+{
+ 0x4000, 0x2002, 0x8000
+};
+const SvxMSDffCalculationData mso_sptCurvedConnector3Calc[] =
+{
+ { 0x2000, { DFF_Prop_adjustValue, 0, 0 } },
+ { 0x2001, { 0x400, 1, 2 } },
+ { 0x2000, { 0x400, 21600, 0 } },
+ { 0x2001, { 0x402, 1, 2 } }
+};
+const sal_Int32 mso_sptCurvedConnector3Default[] =
+{
+ 1, 10800
+};
+const SvxMSDffHandle mso_sptCurvedConnector3Handle[] =
+{
+ { SvxMSDffHandleFlags::RANGE,
+ 0x100, 10800, 10800, 10800, MIN_INT32, 0x7fffffff, MIN_INT32, 0x7fffffff }
+};
+const mso_CustomShape msoCurvedConnector3 =
+{
+ const_cast<SvxMSDffVertPair*>(mso_sptCurvedConnector3Vert), SAL_N_ELEMENTS( mso_sptCurvedConnector3Vert ),
+ const_cast<sal_uInt16*>(mso_sptCurvedConnector3Segm), sizeof( mso_sptCurvedConnector3Segm ) >> 1,
+ const_cast<SvxMSDffCalculationData*>(mso_sptCurvedConnector3Calc), SAL_N_ELEMENTS( mso_sptCurvedConnector3Calc ),
+ const_cast<sal_Int32*>(mso_sptCurvedConnector3Default),
+ nullptr, 0,
+ 21600, 21600,
+ MIN_INT32, MIN_INT32,
+ nullptr, 0,
+ const_cast<SvxMSDffHandle*>(mso_sptCurvedConnector3Handle), SAL_N_ELEMENTS( mso_sptCurvedConnector3Handle )
+};
+
+const SvxMSDffVertPair mso_sptCurvedConnector4Vert[] =
+{
+ { 0, 0 }, { 1 MSO_I, 0 }, { 0 MSO_I, 10 MSO_I }, { 0 MSO_I, 9 MSO_I },
+ { 0 MSO_I, 12 MSO_I }, { 5 MSO_I, 8 MSO_I }, { 3 MSO_I, 8 MSO_I },
+ { 7 MSO_I, 8 MSO_I }, { 21600, 14 MSO_I }, { 21600, 21600 }
+
+};
+const sal_uInt16 mso_sptCurvedConnector4Segm[] =
+{
+ 0x4000, 0x2003, 0x8000
+};
+const SvxMSDffCalculationData mso_sptCurvedConnector4Calc[] =
+{
+ { 0x2000, { DFF_Prop_adjustValue, 0, 0 } },
+ { 0x2001, { 0x400, 1, 2 } },
+ { 0x4000, { 21600, 0x400, 0 } },
+ { 0x2001, { 0x402, 1, 2 } },
+ { 0x6000, { 0x400, 0x403, 0 } },
+ { 0x2001, { 0x404, 1, 2 } },
+ { 0x2000, { 0x403, 21600, 0 } },
+ { 0x2001, { 0x406, 1, 2 } },
+ { 0x2000, { DFF_Prop_adjust2Value, 0, 0 } },
+ { 0x2001, { DFF_Prop_adjust2Value, 1, 2 } },
+ { 0x2001, { DFF_Prop_adjust2Value, 1, 4 } },
+ { 0x6000, { 0x408, 0x409, 0 } },
+ { 0x2001, { 0x40b, 1, 2 } },
+ { 0x2000, { 0x408, 21600, 0 } },
+ { 0x2001, { 0x40d, 1, 2 } }
+};
+const sal_Int32 mso_sptCurvedConnector4Default[] =
+{
+ 2, 10800, 10800
+};
+const SvxMSDffHandle mso_sptCurvedConnector4Handle[] =
+{
+ { SvxMSDffHandleFlags::RANGE | SvxMSDffHandleFlags::RANGE_Y_MIN_IS_SPECIAL | SvxMSDffHandleFlags::RANGE_Y_MAX_IS_SPECIAL,
+ 0x100, 9 + 3, 10800, 10800, MIN_INT32, 0x7fffffff, 9 + 3, 9 + 3 },
+ { SvxMSDffHandleFlags::RANGE | SvxMSDffHandleFlags::RANGE_X_MIN_IS_SPECIAL | SvxMSDffHandleFlags::RANGE_X_MAX_IS_SPECIAL,
+ 3 + 3, 0x101, 10800, 10800, 3 + 3, 3 + 3, MIN_INT32, 0x7fffffff }
+};
+const mso_CustomShape msoCurvedConnector4 =
+{
+ const_cast<SvxMSDffVertPair*>(mso_sptCurvedConnector4Vert), SAL_N_ELEMENTS( mso_sptCurvedConnector4Vert ),
+ const_cast<sal_uInt16*>(mso_sptCurvedConnector4Segm), sizeof( mso_sptCurvedConnector4Segm ) >> 1,
+ const_cast<SvxMSDffCalculationData*>(mso_sptCurvedConnector4Calc), SAL_N_ELEMENTS( mso_sptCurvedConnector4Calc ),
+ const_cast<sal_Int32*>(mso_sptCurvedConnector4Default),
+ nullptr, 0,
+ 21600, 21600,
+ MIN_INT32, MIN_INT32,
+ nullptr, 0,
+ const_cast<SvxMSDffHandle*>(mso_sptCurvedConnector4Handle), SAL_N_ELEMENTS( mso_sptCurvedConnector4Handle )
+};
+
+const SvxMSDffVertPair mso_sptCurvedConnector5Vert[] =
+{
+ { 0, 0 },
+ { 21 MSO_I, 0 }, { 0 MSO_I, 12 MSO_I }, { 0 MSO_I, 11 MSO_I },
+ { 0 MSO_I, 14 MSO_I }, { 6 MSO_I, 4 MSO_I }, { 3 MSO_I, 4 MSO_I },
+ { 8 MSO_I, 4 MSO_I }, { 1 MSO_I, 18 MSO_I }, { 1 MSO_I, 16 MSO_I },
+ { 1 MSO_I, 20 MSO_I }, { 10 MSO_I, 21600 }, { 21600, 21600 }
+};
+const sal_uInt16 mso_sptCurvedConnector5Segm[] =
+{
+ 0x4000, 0x2004, 0x8000
+};
+const SvxMSDffCalculationData mso_sptCurvedConnector5Calc[] =
+{
+ { 0x2000, { DFF_Prop_adjustValue, 0, 0 } },
+ { 0x2000, { DFF_Prop_adjust3Value, 0, 0 } },
+ { 0x6000, { 0x400, 0x401, 0 } },
+ { 0x2001, { 0x402, 1, 2 } },
+ { 0x2000, { DFF_Prop_adjust2Value, 0, 0 } },
+ { 0x6000, { 0x400, 0x403, 0 } },
+ { 0x2001, { 0x405, 1, 2 } },
+ { 0x6000, { 0x401, 0x403, 0 } },
+ { 0x2001, { 0x407, 1, 2 } },
+ { 0x2000, { 0x401, 21600, 0 } },
+ { 0x2001, { 0x409, 1, 2 } },
+ { 0x2001, { 0x404, 1, 2 } },
+ { 0x2001, { 0x40b, 1, 2 } },
+ { 0x6000, { 0x404, 0x40b, 0 } },
+ { 0x2001, { 0x40d, 1, 2 } },
+ { 0x2000, { 0x404, 21600, 0 } },
+ { 0x2001, { 0x40f, 1, 2 } },
+ { 0x6000, { 0x404, 0x410, 0 } },
+ { 0x2001, { 0x411, 1, 2 } },
+ { 0x2000, { 0x410, 21600, 0 } },
+ { 0x2001, { 0x413, 1, 2 } },
+ { 0x2001, { 0x400, 1, 2 } }
+};
+const sal_Int32 mso_sptCurvedConnector5Default[] =
+{
+ 3, 10800, 10800, 10800
+};
+const SvxMSDffHandle mso_sptCurvedConnector5Handle[] =
+{
+ { SvxMSDffHandleFlags::RANGE | SvxMSDffHandleFlags::RANGE_Y_MIN_IS_SPECIAL | SvxMSDffHandleFlags::RANGE_Y_MAX_IS_SPECIAL,
+ 0x100, 11 + 3, 10800, 10800, MIN_INT32, 0x7fffffff, 11 + 3, 11 + 3 },
+ { SvxMSDffHandleFlags::RANGE | SvxMSDffHandleFlags::RANGE_X_MIN_IS_SPECIAL | SvxMSDffHandleFlags::RANGE_X_MAX_IS_SPECIAL,
+ 3 + 3, 0x101, 10800, 10800, 3 + 3, 3 + 3, MIN_INT32, 0x7fffffff },
+ { SvxMSDffHandleFlags::RANGE | SvxMSDffHandleFlags::RANGE_Y_MIN_IS_SPECIAL | SvxMSDffHandleFlags::RANGE_Y_MAX_IS_SPECIAL,
+ 0x102, 16 + 3, 10800, 10800, MIN_INT32, 0x7fffffff, 16 + 3, 16 + 3 }
+};
+const mso_CustomShape msoCurvedConnector5 =
+{
+ const_cast<SvxMSDffVertPair*>(mso_sptCurvedConnector5Vert), SAL_N_ELEMENTS( mso_sptCurvedConnector5Vert ),
+ const_cast<sal_uInt16*>(mso_sptCurvedConnector5Segm), sizeof( mso_sptCurvedConnector5Segm ) >> 1,
+ const_cast<SvxMSDffCalculationData*>(mso_sptCurvedConnector5Calc), SAL_N_ELEMENTS( mso_sptCurvedConnector5Calc ),
+ const_cast<sal_Int32*>(mso_sptCurvedConnector5Default),
+ nullptr, 0,
+ 21600, 21600,
+ MIN_INT32, MIN_INT32,
+ nullptr, 0,
+ const_cast<SvxMSDffHandle*>(mso_sptCurvedConnector5Handle), SAL_N_ELEMENTS( mso_sptCurvedConnector5Handle )
+};
+
+/////////////////////////////teardrop
+const SvxMSDffVertPair mso_sptTearDropVert[] =
+{
+ { 10800, 0 },
+ { 0, 10800 }, // X
+ { 10800, 21600 }, // Y
+ { 21600, 10800 }, // X
+ { 21600, 10800 }, { 21600, 3 MSO_I }, { 0 MSO_I, 1 MSO_I }, // C
+ { 0 MSO_I, 1 MSO_I }, { 4 MSO_I, 0 }, { 10800, 0 }
+};
+
+// the last number (0x***n) : 0 = sum, 1 = prod, 2 = mid, 3 = abs, 4 = min, 5 = max, 6 = if, 13 = sqrt, 15 = eclipse ...
+// the first number(0xn***) : 2/4/8 the first/second/third value is not directly value
+const SvxMSDffCalculationData mso_sptTearDropCalc[] =
+{
+ { 0x2000 , { DFF_Prop_adjustValue , 0 , 0 } }, // 0 adjust value #0
+ { 0x8000 , { 21600 , 0 , 0x0400 } }, // 1 21600 - @0 y0
+ { 0x8000 , { 32400 , 0 , 0x0400 } }, // 2 (32400 - @0)
+ { 0x2001 , { 0x0402 , 1 , 2 } }, // 3 (32400 - @0)/2 y1
+ { 0x2002 , { 0x0400 , 10800 , 0 } }, // 4 (@0+10800)/2 x2
+};
+
+//m, qx, qy, qx,C,C
+//the last number(0x***n) : repeat number of this current Segm
+const sal_uInt16 mso_sptTearDropSegm[] =
+{
+ 0x4000, 0xa701, 0xa801, 0xa701, 0x2002, 0x6000, 0x8000
+};
+
+const SvxMSDffTextRectangles mso_sptTearDropTextRect[] =
+{
+ { { 2863, 2863 }, { 18737, 18737 } }
+};
+
+//the range of adjust values
+const SvxMSDffHandle mso_sptTearDropHandle[] =
+{
+ //position="$0,0" xrange="10800,32400"
+ { SvxMSDffHandleFlags::RANGE | SvxMSDffHandleFlags::RANGE_X_MAX_IS_SPECIAL| SvxMSDffHandleFlags::RANGE_X_MIN_IS_SPECIAL,
+ 0x100, 0, 10800, 10800, 10800, 32400, MIN_INT32, 0x7fffffff }
+};
+
+//the number of adjust values, the default values
+const sal_Int32 mso_sptTearDropDefault[] =
+{
+ 1, 21600
+};
+
+const mso_CustomShape msoTearDrop =
+{
+ const_cast<SvxMSDffVertPair*>(mso_sptTearDropVert), SAL_N_ELEMENTS( mso_sptTearDropVert ),
+ const_cast<sal_uInt16*>(mso_sptTearDropSegm), sizeof( mso_sptTearDropSegm ) >> 1,
+ const_cast<SvxMSDffCalculationData*>(mso_sptTearDropCalc), SAL_N_ELEMENTS(mso_sptTearDropCalc),
+ const_cast<sal_Int32*>(mso_sptTearDropDefault),
+ const_cast<SvxMSDffTextRectangles*>(mso_sptTearDropTextRect), SAL_N_ELEMENTS( mso_sptTearDropTextRect ),
+ 21600, 21600,
+ MIN_INT32, MIN_INT32,
+ nullptr, 0,
+ const_cast<SvxMSDffHandle*>(mso_sptTearDropHandle), SAL_N_ELEMENTS(mso_sptTearDropHandle) // handles
+};
+
+
+const mso_CustomShape* GetCustomShapeContent( MSO_SPT eSpType )
+{
+ const mso_CustomShape* pCustomShape = nullptr;
+ switch( eSpType )
+ {
+ case mso_sptArc : pCustomShape = &msoArc; break;
+ case mso_sptLine: pCustomShape = &msoStraightConnector1; break;
+ case mso_sptRectangle : pCustomShape = &msoRectangle; break;
+ case mso_sptParallelogram : pCustomShape = &msoParallelogram; break;
+ case mso_sptTrapezoid : pCustomShape = &msoTrapezoid; break;
+ case mso_sptDiamond : pCustomShape = &msoDiamond; break;
+ case mso_sptRoundRectangle : pCustomShape = &msoRoundRectangle; break;
+ case mso_sptOctagon : pCustomShape = &msoOctagon; break;
+ case mso_sptIsocelesTriangle : pCustomShape = &msoIsocelesTriangle; break;
+ case mso_sptRightTriangle : pCustomShape = &msoRightTriangle; break;
+ case mso_sptEllipse : pCustomShape = &msoEllipse; break;
+ case mso_sptHexagon : pCustomShape = &msoHexagon; break;
+ case mso_sptPlus : pCustomShape = &msoPlus; break;
+ case mso_sptPentagon : pCustomShape = &msoPentagon; break;
+ case mso_sptCan : pCustomShape = &msoCan; break;
+ case mso_sptCube : pCustomShape = &msoCube; break;
+ case mso_sptBalloon : pCustomShape = &msoBalloon; break;
+ case mso_sptActionButtonBlank : pCustomShape = &msoActionButtonBlank; break;
+ case mso_sptActionButtonHome : pCustomShape = &msoActionButtonHome; break;
+ case mso_sptActionButtonHelp : pCustomShape = &msoActionButtonHelp; break;
+ case mso_sptActionButtonInformation : pCustomShape = &msoActionButtonInformation; break;
+ case mso_sptActionButtonBackPrevious : pCustomShape = &msoActionButtonBackPrevious; break;
+ case mso_sptActionButtonForwardNext : pCustomShape = &msoActionButtonForwardNext; break;
+ case mso_sptActionButtonBeginning : pCustomShape = &msoActionButtonBeginning; break;
+ case mso_sptActionButtonEnd : pCustomShape = &msoActionButtonEnd; break;
+ case mso_sptActionButtonReturn : pCustomShape = &msoActionButtonReturn; break;
+ case mso_sptActionButtonDocument : pCustomShape = &msoActionButtonDocument; break;
+ case mso_sptActionButtonSound : pCustomShape = &msoActionButtonSound; break;
+ case mso_sptActionButtonMovie : pCustomShape = &msoActionButtonMovie; break;
+ case mso_sptBevel : pCustomShape = &msoBevel; break;
+ case mso_sptFoldedCorner : pCustomShape = &msoFoldedCorner; break;
+ case mso_sptSmileyFace : pCustomShape = &msoSmileyFace; break;
+ case mso_sptDonut : pCustomShape = &msoDonut; break;
+ case mso_sptNoSmoking : pCustomShape = &msoNoSmoking; break;
+ case mso_sptBlockArc : pCustomShape = &msoBlockArc; break;
+ case mso_sptHeart : pCustomShape = &msoHeart; break;
+ case mso_sptLightningBolt : pCustomShape = &msoLightningBold; break;
+ case mso_sptSun : pCustomShape = &msoSun; break;
+ case mso_sptMoon : pCustomShape = &msoMoon; break;
+ case mso_sptBracketPair : pCustomShape = &msoBracketPair; break;
+ case mso_sptBracePair : pCustomShape = &msoBracePair; break;
+ case mso_sptPlaque : pCustomShape = &msoPlaque; break;
+ case mso_sptLeftBracket : pCustomShape = &msoLeftBracket; break;
+ case mso_sptRightBracket : pCustomShape = &msoRightBracket; break;
+ case mso_sptLeftBrace : pCustomShape = &msoLeftBrace; break;
+ case mso_sptRightBrace : pCustomShape = &msoRightBrace; break;
+ case mso_sptArrow : pCustomShape = &msoArrow; break;
+ case mso_sptUpArrow : pCustomShape = &msoUpArrow; break;
+ case mso_sptDownArrow : pCustomShape = &msoDownArrow; break;
+ case mso_sptLeftArrow : pCustomShape = &msoLeftArrow; break;
+ case mso_sptLeftRightArrow : pCustomShape = &msoLeftRightArrow; break;
+ case mso_sptUpDownArrow : pCustomShape = &msoUpDownArrow; break;
+ case mso_sptQuadArrow : pCustomShape = &msoQuadArrow; break;
+ case mso_sptLeftRightUpArrow : pCustomShape = &msoLeftRightUpArrow; break;
+ case mso_sptBentArrow : pCustomShape = &msoBentArrow; break;
+ case mso_sptUturnArrow : pCustomShape = &msoUturnArrow; break;
+ case mso_sptLeftUpArrow : pCustomShape = &msoLeftUpArrow; break;
+ case mso_sptBentUpArrow : pCustomShape = &msoBentUpArrow; break;
+ case mso_sptCurvedRightArrow : pCustomShape = &msoCurvedRightArrow; break;
+ case mso_sptCurvedLeftArrow : pCustomShape = &msoCurvedLeftArrow; break;
+ case mso_sptCurvedUpArrow : pCustomShape = &msoCurvedUpArrow; break;
+ case mso_sptCurvedDownArrow : pCustomShape = &msoCurvedDownArrow; break;
+ case mso_sptStripedRightArrow : pCustomShape = &msoStripedRightArrow; break;
+ case mso_sptNotchedRightArrow : pCustomShape = &msoNotchedRightArrow; break;
+ case mso_sptHomePlate : pCustomShape = &msoHomePlate; break;
+ case mso_sptChevron : pCustomShape = &msoChevron; break;
+ case mso_sptRightArrowCallout : pCustomShape = &msoRightArrowCallout; break;
+ case mso_sptLeftArrowCallout : pCustomShape = &msoLeftArrowCallout; break;
+ case mso_sptUpArrowCallout : pCustomShape = &msoUpArrowCallout; break;
+ case mso_sptDownArrowCallout : pCustomShape = &msoDownArrowCallout; break;
+ case mso_sptLeftRightArrowCallout : pCustomShape = &msoLeftRightArrowCallout; break;
+ case mso_sptUpDownArrowCallout : pCustomShape = &msoUpDownArrowCallout; break;
+ case mso_sptQuadArrowCallout : pCustomShape = &msoQuadArrowCallout; break;
+ case mso_sptCircularArrow : pCustomShape = &msoCircularArrow; break;
+ case mso_sptIrregularSeal1 : pCustomShape = &msoIrregularSeal1; break;
+ case mso_sptIrregularSeal2 : pCustomShape = &msoIrregularSeal2; break;
+ case mso_sptSeal4 : pCustomShape = &msoSeal4; break;
+ case mso_sptStar : pCustomShape = &msoStar; break;
+ case mso_sptSeal8 : pCustomShape = &msoSeal8; break;
+ case mso_sptSeal :
+ case mso_sptSeal16 : pCustomShape = &msoSeal16; break;
+ case mso_sptSeal24 : pCustomShape = &msoSeal24; break;
+ case mso_sptSeal32 : pCustomShape = &msoSeal32; break;
+ case mso_sptRibbon2 : pCustomShape = &msoRibbon2; break;
+ case mso_sptRibbon : pCustomShape = &msoRibbon; break;
+ case mso_sptEllipseRibbon2 : pCustomShape = &msosptEllipseRibbon2; break; // SJ: TODO
+ case mso_sptEllipseRibbon : pCustomShape = &msosptEllipseRibbon; break; // SJ: TODO
+ case mso_sptVerticalScroll : pCustomShape = &msoVerticalScroll; break;
+ case mso_sptHorizontalScroll : pCustomShape = &msoHorizontalScroll; break;
+ case mso_sptFlowChartProcess : pCustomShape = &msoFlowChartProcess; break;
+ case mso_sptFlowChartAlternateProcess : pCustomShape = &msoFlowChartAlternateProcess; break;
+ case mso_sptFlowChartDecision : pCustomShape = &msoFlowChartDecision; break;
+ case mso_sptFlowChartInputOutput : pCustomShape = &msoFlowChartInputOutput; break;
+ case mso_sptFlowChartPredefinedProcess :pCustomShape = &msoFlowChartPredefinedProcess; break;
+ case mso_sptFlowChartInternalStorage : pCustomShape = &msoFlowChartInternalStorage; break;
+ case mso_sptFlowChartDocument : pCustomShape = &msoFlowChartDocument; break;
+ case mso_sptFlowChartMultidocument : pCustomShape = &msoFlowChartMultidocument; break;
+ case mso_sptFlowChartTerminator : pCustomShape = &msoFlowChartTerminator; break;
+ case mso_sptFlowChartPreparation : pCustomShape = &msoFlowChartPreparation; break;
+ case mso_sptFlowChartManualInput : pCustomShape = &msoFlowChartManualInput; break;
+ case mso_sptFlowChartManualOperation : pCustomShape = &msoFlowChartManualOperation; break;
+ case mso_sptFlowChartConnector : pCustomShape = &msoFlowChartConnector; break;
+ case mso_sptFlowChartOffpageConnector : pCustomShape = &msoFlowChartOffpageConnector; break;
+ case mso_sptFlowChartPunchedCard : pCustomShape = &msoFlowChartPunchedCard; break;
+ case mso_sptFlowChartPunchedTape : pCustomShape = &msoFlowChartPunchedTape; break;
+ case mso_sptFlowChartSummingJunction : pCustomShape = &msoFlowChartSummingJunction; break;
+ case mso_sptFlowChartOr : pCustomShape = &msoFlowChartOr; break;
+ case mso_sptFlowChartCollate : pCustomShape = &msoFlowChartCollate; break;
+ case mso_sptFlowChartSort : pCustomShape = &msoFlowChartSort; break;
+ case mso_sptFlowChartExtract : pCustomShape = &msoFlowChartExtract; break;
+ case mso_sptFlowChartMerge : pCustomShape = &msoFlowChartMerge; break;
+ case mso_sptFlowChartOnlineStorage : pCustomShape = &msoFlowChartOnlineStorage; break;
+ case mso_sptFlowChartDelay : pCustomShape = &msoFlowChartDelay; break;
+ case mso_sptFlowChartMagneticTape : pCustomShape = &msoFlowChartMagneticTape; break;
+ case mso_sptFlowChartMagneticDisk : pCustomShape = &msoFlowChartMagneticDisk; break;
+ case mso_sptFlowChartMagneticDrum : pCustomShape = &msoFlowChartMagneticDrum; break;
+ case mso_sptFlowChartDisplay : pCustomShape = &msoFlowChartDisplay; break;
+ case mso_sptWedgeRectCallout : pCustomShape = &msoWedgeRectCallout; break;
+ case mso_sptWedgeRRectCallout : pCustomShape = &msoWedgeRRectCallout; break;
+ case mso_sptWedgeEllipseCallout : pCustomShape = &msoWedgeEllipseCallout; break;
+ case mso_sptCloudCallout : pCustomShape = &msoCloudCallout; break;
+ case mso_sptWave : pCustomShape = &msoWave; break;
+ case mso_sptDoubleWave : pCustomShape = &msoDoubleWave; break;
+
+ // callout
+ case mso_sptCallout1 : pCustomShape = &msoCallout1; break;
+ case mso_sptCallout2 : pCustomShape = &msoCallout2; break;
+ case mso_sptCallout3 : pCustomShape = &msoCallout3; break;
+ case mso_sptAccentCallout1 : pCustomShape = &msoAccentCallout1; break;
+ case mso_sptAccentCallout2 : pCustomShape = &msoAccentCallout2; break;
+ case mso_sptAccentCallout3 : pCustomShape = &msoAccentCallout3; break;
+ case mso_sptBorderCallout1 : pCustomShape = &msoBorderCallout1; break;
+ case mso_sptBorderCallout2 : pCustomShape = &msoBorderCallout2; break;
+ case mso_sptBorderCallout3 : pCustomShape = &msoBorderCallout3; break;
+ case mso_sptAccentBorderCallout1 : pCustomShape = &msoAccentBorderCallout1; break;
+ case mso_sptAccentBorderCallout2 : pCustomShape = &msoAccentBorderCallout2; break;
+ case mso_sptAccentBorderCallout3 : pCustomShape = &msoAccentBorderCallout3; break;
+ case mso_sptCallout90 : pCustomShape = &msoCallout90; break;
+ case mso_sptAccentCallout90 : pCustomShape = &msoAccentCallout90; break;
+ case mso_sptBorderCallout90 : pCustomShape = &msoBorderCallout90; break;
+ case mso_sptAccentBorderCallout90 : pCustomShape = &msoAccentBorderCallout90; break;
+
+ // connectors
+ case mso_sptStraightConnector1 : pCustomShape = &msoStraightConnector1; break;
+ case mso_sptBentConnector2 : pCustomShape = &msoBentConnector2; break;
+ case mso_sptBentConnector3 : pCustomShape = &msoBentConnector3; break;
+ case mso_sptBentConnector4 : pCustomShape = &msoBentConnector4; break;
+ case mso_sptBentConnector5 : pCustomShape = &msoBentConnector5; break;
+ case mso_sptCurvedConnector2 : pCustomShape = &msoCurvedConnector2; break;
+ case mso_sptCurvedConnector3 : pCustomShape = &msoCurvedConnector3; break;
+ case mso_sptCurvedConnector4 : pCustomShape = &msoCurvedConnector4; break;
+ case mso_sptCurvedConnector5 : pCustomShape = &msoCurvedConnector5; break;
+
+ // Don't know, simply mapping to TextSimple
+ case mso_sptTextOnRing :
+ case mso_sptTextOnCurve :
+ case mso_sptTextRing :
+ case mso_sptTextWave :
+ case mso_sptTextCurve :
+ case mso_sptTextHexagon :
+ case mso_sptTextOctagon :
+ case mso_sptTextBox : pCustomShape = &msoTextSimple; break;
+
+ // FontWork
+ case mso_sptTextSimple :
+ case mso_sptTextPlainText : pCustomShape = &msoTextPlainText; break;
+ case mso_sptTextStop : pCustomShape = &msoTextStop; break;
+ case mso_sptTextTriangle : pCustomShape = &msoTextTriangle; break;
+ case mso_sptTextTriangleInverted : pCustomShape = &msoTextTriangleInverted; break;
+ case mso_sptTextChevron : pCustomShape = &msoTextChevron; break;
+ case mso_sptTextChevronInverted : pCustomShape = &msoTextChevronInverted; break;
+ case mso_sptTextRingInside : pCustomShape = &msoTextRingInside; break; // SJ: TODO->the orientation of the ellipse needs to be changed
+ case mso_sptTextRingOutside : pCustomShape = &msoTextRingOutside; break;
+ case mso_sptTextFadeRight : pCustomShape = &msoTextFadeRight; break;
+ case mso_sptTextFadeLeft : pCustomShape = &msoTextFadeLeft; break;
+ case mso_sptTextFadeUp : pCustomShape = &msoTextFadeUp; break;
+ case mso_sptTextFadeDown : pCustomShape = &msoTextFadeDown; break;
+ case mso_sptTextSlantUp : pCustomShape = &msoTextSlantUp; break;
+ case mso_sptTextSlantDown : pCustomShape = &msoTextSlantDown; break;
+ case mso_sptTextCascadeUp : pCustomShape = &msoTextCascadeUp; break;
+ case mso_sptTextCascadeDown : pCustomShape = &msoTextCascadeDown; break;
+ case mso_sptTextArchUpCurve : pCustomShape = &msoTextArchUpCurve; break;
+ case mso_sptTextArchDownCurve : pCustomShape = &msoTextArchDownCurve; break;
+ case mso_sptTextCircleCurve : pCustomShape = &msoTextCircleCurve; break;
+ case mso_sptTextButtonCurve : pCustomShape = &msoTextButtonCurve; break;
+ case mso_sptTextArchUpPour : pCustomShape = &msoTextArchUpPour; break;
+ case mso_sptTextArchDownPour : pCustomShape = &msoTextArchDownPour; break;
+ case mso_sptTextCirclePour : pCustomShape = &msoTextCirclePour; break;
+ case mso_sptTextButtonPour : pCustomShape = &msoTextButtonPour; break;
+ case mso_sptTextCurveUp : pCustomShape = &msoTextCurveUp; break;
+ case mso_sptTextCurveDown : pCustomShape = &msoTextCurveDown; break;
+ case mso_sptTextCanUp : pCustomShape = &msoTextCanUp; break;
+ case mso_sptTextCanDown : pCustomShape = &msoTextCanDown; break;
+ case mso_sptTextInflate : pCustomShape = &msoTextInflate; break;
+ case mso_sptTextDeflate : pCustomShape = &msoTextDeflate; break;
+ case mso_sptTextInflateBottom : pCustomShape = &msoTextInflateBottom; break;
+ case mso_sptTextDeflateBottom : pCustomShape = &msoTextDeflateBottom; break;
+ case mso_sptTextInflateTop : pCustomShape = &msoTextInflateTop; break;
+ case mso_sptTextDeflateTop : pCustomShape = &msoTextDeflateTop; break;
+ case mso_sptTextDeflateInflate : pCustomShape = &msoTextDeflateInflate; break;
+ case mso_sptTextDeflateInflateDeflate : pCustomShape = &msoTextDeflateInflateDeflate; break;
+ case mso_sptTextWave1 : pCustomShape = &msoTextWave1; break;
+ case mso_sptTextWave2 : pCustomShape = &msoTextWave2; break;
+ case mso_sptTextWave3 : pCustomShape = &msoTextWave3; break;
+ case mso_sptTextWave4 : pCustomShape = &msoTextWave4; break;
+ case mso_sptTearDrop : pCustomShape = &msoTearDrop; break;
+ default :
+ break;
+ }
+ return pCustomShape;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/customshapes/EnhancedCustomShapeHandle.cxx b/svx/source/customshapes/EnhancedCustomShapeHandle.cxx
new file mode 100644
index 000000000..8dce09c1c
--- /dev/null
+++ b/svx/source/customshapes/EnhancedCustomShapeHandle.cxx
@@ -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 .
+ */
+
+#include "EnhancedCustomShapeHandle.hxx"
+#include <svx/EnhancedCustomShape2d.hxx>
+#include <svx/svdoashp.hxx>
+
+
+EnhancedCustomShapeHandle::EnhancedCustomShapeHandle( css::uno::Reference< css::drawing::XShape > const & xCustomShape, sal_uInt32 nIndex ) :
+ mnIndex ( nIndex ),
+ mxCustomShape ( xCustomShape )
+{
+}
+
+
+EnhancedCustomShapeHandle::~EnhancedCustomShapeHandle()
+{
+}
+
+
+void SAL_CALL EnhancedCustomShapeHandle::acquire() noexcept
+{
+ OWeakObject::acquire();
+}
+
+
+void SAL_CALL EnhancedCustomShapeHandle::release() noexcept
+{
+ OWeakObject::release();
+}
+
+// XCustomShapeHandle
+css::awt::Point SAL_CALL EnhancedCustomShapeHandle::getPosition()
+{
+ auto pSdrObjCustomShape = dynamic_cast< SdrObjCustomShape* >(SdrObject::getSdrObjectFromXShape(mxCustomShape));
+
+ if(!pSdrObjCustomShape)
+ {
+ throw css::uno::RuntimeException();
+ }
+
+ Point aPosition;
+ EnhancedCustomShape2d aCustomShape2d(*pSdrObjCustomShape);
+
+ if(!aCustomShape2d.GetHandlePosition(mnIndex, aPosition))
+ {
+ throw css::uno::RuntimeException();
+ }
+
+ return css::awt::Point( aPosition.X(), aPosition.Y() );
+}
+
+void SAL_CALL EnhancedCustomShapeHandle::setControllerPosition( const css::awt::Point& aPnt )
+{
+ auto pSdrObjCustomShape = dynamic_cast< SdrObjCustomShape* >(SdrObject::getSdrObjectFromXShape(mxCustomShape));
+
+ if(!pSdrObjCustomShape)
+ {
+ throw css::uno::RuntimeException();
+ }
+
+ EnhancedCustomShape2d aCustomShape2d(*pSdrObjCustomShape);
+
+ if(!aCustomShape2d.SetHandleControllerPosition(mnIndex, aPnt))
+ {
+ throw css::uno::RuntimeException();
+ }
+}
+
+// XInitialization
+void SAL_CALL EnhancedCustomShapeHandle::initialize( const css::uno::Sequence< css::uno::Any >& /* aArguments */ )
+{
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/customshapes/EnhancedCustomShapeHandle.hxx b/svx/source/customshapes/EnhancedCustomShapeHandle.hxx
new file mode 100644
index 000000000..b56909420
--- /dev/null
+++ b/svx/source/customshapes/EnhancedCustomShapeHandle.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_SVX_SOURCE_CUSTOMSHAPES_ENHANCEDCUSTOMSHAPEHANDLE_HXX
+#define INCLUDED_SVX_SOURCE_CUSTOMSHAPES_ENHANCEDCUSTOMSHAPEHANDLE_HXX
+
+#include <cppuhelper/implbase.hxx>
+#include <com/sun/star/lang/XInitialization.hpp>
+#include <com/sun/star/drawing/XShape.hpp>
+#include <com/sun/star/drawing/XCustomShapeHandle.hpp>
+#include <com/sun/star/awt/Point.hpp>
+
+class EnhancedCustomShapeHandle : public cppu::WeakImplHelper
+<
+ css::drawing::XCustomShapeHandle,
+ css::lang::XInitialization
+>
+{
+ sal_uInt32 mnIndex;
+ css::uno::Reference< css::drawing::XShape > mxCustomShape;
+
+public:
+
+ EnhancedCustomShapeHandle( css::uno::Reference< css::drawing::XShape > const & xCustomShape, sal_uInt32 nIndex );
+ virtual ~EnhancedCustomShapeHandle() override;
+
+ // XInterface
+ virtual void SAL_CALL acquire() noexcept override;
+ virtual void SAL_CALL release() noexcept override;
+
+ // XCustomShapeHandle
+ virtual css::awt::Point SAL_CALL getPosition() override;
+ virtual void SAL_CALL setControllerPosition( const css::awt::Point& ) override;
+
+ // XInitialization
+ virtual void SAL_CALL initialize( const css::uno::Sequence< css::uno::Any >& aArguments ) override;
+};
+
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/customshapes/EnhancedCustomShapeTypeNames.cxx b/svx/source/customshapes/EnhancedCustomShapeTypeNames.cxx
new file mode 100644
index 000000000..70c15ca66
--- /dev/null
+++ b/svx/source/customshapes/EnhancedCustomShapeTypeNames.cxx
@@ -0,0 +1,548 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * 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/EnhancedCustomShapeTypeNames.hxx>
+#include <unordered_map>
+
+namespace {
+
+struct NameTypeTable
+{
+ const char* pS;
+ MSO_SPT pE;
+};
+
+}
+
+const NameTypeTable pNameTypeTableArray[] =
+{
+ { "non-primitive", mso_sptMin },
+ { "rectangle", mso_sptRectangle },
+ { "round-rectangle", mso_sptRoundRectangle },
+ { "ellipse", mso_sptEllipse },
+ { "diamond", mso_sptDiamond },
+ { "isosceles-triangle", mso_sptIsocelesTriangle },
+ { "right-triangle", mso_sptRightTriangle },
+ { "parallelogram", mso_sptParallelogram },
+ { "trapezoid", mso_sptTrapezoid },
+ { "hexagon", mso_sptHexagon },
+ { "octagon", mso_sptOctagon },
+ { "cross", mso_sptPlus },
+ { "star5", mso_sptStar },
+ { "right-arrow", mso_sptArrow },
+ { "mso-spt14", mso_sptThickArrow },
+ { "pentagon-right", mso_sptHomePlate },
+ { "cube", mso_sptCube },
+ { "mso-spt17", mso_sptBalloon },
+ { "mso-spt18", mso_sptSeal },
+ { "mso-spt19", mso_sptArc },
+ { "mso-spt20", mso_sptLine },
+ { "mso-spt21", mso_sptPlaque },
+ { "can", mso_sptCan },
+ { "ring", mso_sptDonut },
+ { "mso-spt24", mso_sptTextSimple },
+ { "mso-spt25", mso_sptTextOctagon },
+ { "mso-spt26", mso_sptTextHexagon },
+ { "mso-spt27", mso_sptTextCurve },
+ { "mso-spt28", mso_sptTextWave },
+ { "mso-spt29", mso_sptTextRing },
+ { "mso-spt30", mso_sptTextOnCurve },
+ { "mso-spt31", mso_sptTextOnRing },
+ { "mso-spt32", mso_sptStraightConnector1 },
+ { "mso-spt33", mso_sptBentConnector2 },
+ { "mso-spt34", mso_sptBentConnector3 },
+ { "mso-spt35", mso_sptBentConnector4 },
+ { "mso-spt36", mso_sptBentConnector5 },
+ { "mso-spt37", mso_sptCurvedConnector2 },
+ { "mso-spt38", mso_sptCurvedConnector3 },
+ { "mso-spt39", mso_sptCurvedConnector4 },
+ { "mso-spt40", mso_sptCurvedConnector5 },
+ { "mso-spt41", mso_sptCallout1 },
+ { "mso-spt42", mso_sptCallout2 },
+ { "mso-spt43", mso_sptCallout3 },
+ { "mso-spt44", mso_sptAccentCallout1 },
+ { "mso-spt45", mso_sptAccentCallout2 },
+ { "mso-spt46", mso_sptAccentCallout3 },
+ { "line-callout-1", mso_sptBorderCallout1 },
+ { "line-callout-2", mso_sptBorderCallout2 },
+ { "mso-spt49", mso_sptBorderCallout3 },
+ { "mso-spt50", mso_sptAccentBorderCallout1 },
+ { "mso-spt51", mso_sptAccentBorderCallout2 },
+ { "mso-spt52", mso_sptAccentBorderCallout3 },
+ { "mso-spt53", mso_sptRibbon },
+ { "mso-spt54", mso_sptRibbon2 },
+ { "chevron", mso_sptChevron },
+ { "pentagon", mso_sptPentagon },
+ { "forbidden", mso_sptNoSmoking },
+ { "star8", mso_sptSeal8 },
+ { "mso-spt59", mso_sptSeal16 },
+ { "mso-spt60", mso_sptSeal32 },
+ { "rectangular-callout", mso_sptWedgeRectCallout },
+ { "round-rectangular-callout", mso_sptWedgeRRectCallout },
+ { "round-callout", mso_sptWedgeEllipseCallout },
+ { "mso-spt64", mso_sptWave },
+ { "paper", mso_sptFoldedCorner },
+ { "left-arrow", mso_sptLeftArrow },
+ { "down-arrow", mso_sptDownArrow },
+ { "up-arrow", mso_sptUpArrow },
+ { "left-right-arrow", mso_sptLeftRightArrow },
+ { "up-down-arrow", mso_sptUpDownArrow },
+ { "mso-spt71", mso_sptIrregularSeal1 },
+ { "bang", mso_sptIrregularSeal2 },
+ { "lightning", mso_sptLightningBolt },
+ { "heart", mso_sptHeart },
+ { "mso-spt75", mso_sptPictureFrame },
+ { "quad-arrow", mso_sptQuadArrow },
+ { "left-arrow-callout", mso_sptLeftArrowCallout },
+ { "right-arrow-callout", mso_sptRightArrowCallout },
+ { "up-arrow-callout", mso_sptUpArrowCallout },
+ { "down-arrow-callout", mso_sptDownArrowCallout },
+ { "left-right-arrow-callout", mso_sptLeftRightArrowCallout },
+ { "up-down-arrow-callout", mso_sptUpDownArrowCallout },
+ { "quad-arrow-callout", mso_sptQuadArrowCallout },
+ { "quad-bevel", mso_sptBevel },
+ { "left-bracket", mso_sptLeftBracket },
+ { "right-bracket", mso_sptRightBracket },
+ { "left-brace", mso_sptLeftBrace },
+ { "right-brace", mso_sptRightBrace },
+ { "mso-spt89", mso_sptLeftUpArrow },
+ { "mso-spt90", mso_sptBentUpArrow },
+ { "mso-spt91", mso_sptBentArrow },
+ { "star24", mso_sptSeal24 },
+ { "striped-right-arrow", mso_sptStripedRightArrow },
+ { "notched-right-arrow", mso_sptNotchedRightArrow },
+ { "block-arc", mso_sptBlockArc },
+ { "smiley", mso_sptSmileyFace },
+ { "vertical-scroll", mso_sptVerticalScroll },
+ { "horizontal-scroll", mso_sptHorizontalScroll },
+ { "circular-arrow", mso_sptCircularArrow },
+ { "mso-spt100", mso_sptNotchedCircularArrow },
+ { "mso-spt101", mso_sptUturnArrow },
+ { "mso-spt102", mso_sptCurvedRightArrow },
+ { "mso-spt103", mso_sptCurvedLeftArrow },
+ { "mso-spt104", mso_sptCurvedUpArrow },
+ { "mso-spt105", mso_sptCurvedDownArrow },
+ { "cloud-callout", mso_sptCloudCallout },
+ { "mso-spt107", mso_sptEllipseRibbon },
+ { "mso-spt108", mso_sptEllipseRibbon2 },
+ { "flowchart-process", mso_sptFlowChartProcess },
+ { "flowchart-decision", mso_sptFlowChartDecision },
+ { "flowchart-data", mso_sptFlowChartInputOutput },
+ { "flowchart-predefined-process", mso_sptFlowChartPredefinedProcess },
+ { "flowchart-internal-storage", mso_sptFlowChartInternalStorage },
+ { "flowchart-document", mso_sptFlowChartDocument },
+ { "flowchart-multidocument", mso_sptFlowChartMultidocument },
+ { "flowchart-terminator", mso_sptFlowChartTerminator },
+ { "flowchart-preparation", mso_sptFlowChartPreparation },
+ { "flowchart-manual-input", mso_sptFlowChartManualInput },
+ { "flowchart-manual-operation", mso_sptFlowChartManualOperation },
+ { "flowchart-connector", mso_sptFlowChartConnector },
+ { "flowchart-card", mso_sptFlowChartPunchedCard },
+ { "flowchart-punched-tape", mso_sptFlowChartPunchedTape },
+ { "flowchart-summing-junction", mso_sptFlowChartSummingJunction },
+ { "flowchart-or", mso_sptFlowChartOr },
+ { "flowchart-collate", mso_sptFlowChartCollate },
+ { "flowchart-sort", mso_sptFlowChartSort },
+ { "flowchart-extract", mso_sptFlowChartExtract },
+ { "flowchart-merge", mso_sptFlowChartMerge },
+ { "mso-spt129", mso_sptFlowChartOfflineStorage },
+ { "flowchart-stored-data", mso_sptFlowChartOnlineStorage },
+ { "flowchart-sequential-access", mso_sptFlowChartMagneticTape },
+ { "flowchart-magnetic-disk", mso_sptFlowChartMagneticDisk },
+ { "flowchart-direct-access-storage", mso_sptFlowChartMagneticDrum },
+ { "flowchart-display", mso_sptFlowChartDisplay },
+ { "flowchart-delay", mso_sptFlowChartDelay },
+ { "fontwork-plain-text", mso_sptTextPlainText },
+ { "fontwork-stop", mso_sptTextStop },
+ { "fontwork-triangle-up", mso_sptTextTriangle },
+ { "fontwork-triangle-down", mso_sptTextTriangleInverted },
+ { "fontwork-chevron-up", mso_sptTextChevron },
+ { "fontwork-chevron-down", mso_sptTextChevronInverted },
+ { "mso-spt142", mso_sptTextRingInside },
+ { "mso-spt143", mso_sptTextRingOutside },
+ { "fontwork-arch-up-curve", mso_sptTextArchUpCurve },
+ { "fontwork-arch-down-curve", mso_sptTextArchDownCurve },
+ { "fontwork-circle-curve", mso_sptTextCircleCurve },
+ { "fontwork-open-circle-curve", mso_sptTextButtonCurve },
+ { "fontwork-arch-up-pour", mso_sptTextArchUpPour },
+ { "fontwork-arch-down-pour", mso_sptTextArchDownPour },
+ { "fontwork-circle-pour", mso_sptTextCirclePour },
+ { "fontwork-open-circle-pour", mso_sptTextButtonPour },
+ { "fontwork-curve-up", mso_sptTextCurveUp },
+ { "fontwork-curve-down", mso_sptTextCurveDown },
+ { "fontwork-fade-up-and-right", mso_sptTextCascadeUp },
+ { "fontwork-fade-up-and-left", mso_sptTextCascadeDown },
+ { "fontwork-wave", mso_sptTextWave1 },
+ { "mso-spt157", mso_sptTextWave2 },
+ { "mso-spt158", mso_sptTextWave3 },
+ { "mso-spt159", mso_sptTextWave4 },
+ { "fontwork-inflate", mso_sptTextInflate },
+ { "mso-spt161", mso_sptTextDeflate },
+ { "mso-spt162", mso_sptTextInflateBottom },
+ { "mso-spt163", mso_sptTextDeflateBottom },
+ { "mso-spt164", mso_sptTextInflateTop },
+ { "mso-spt165", mso_sptTextDeflateTop },
+ { "mso-spt166", mso_sptTextDeflateInflate },
+ { "mso-spt167", mso_sptTextDeflateInflateDeflate },
+ { "fontwork-fade-right", mso_sptTextFadeRight },
+ { "fontwork-fade-left", mso_sptTextFadeLeft },
+ { "fontwork-fade-up", mso_sptTextFadeUp },
+ { "fontwork-fade-down", mso_sptTextFadeDown },
+ { "fontwork-slant-up", mso_sptTextSlantUp },
+ { "fontwork-slant-down", mso_sptTextSlantDown },
+ { "mso-spt174", mso_sptTextCanUp },
+ { "mso-spt175", mso_sptTextCanDown },
+ { "flowchart-alternate-process", mso_sptFlowChartAlternateProcess },
+ { "flowchart-off-page-connector", mso_sptFlowChartOffpageConnector },
+ { "mso-spt178", mso_sptCallout90 },
+ { "mso-spt179", mso_sptAccentCallout90 },
+ { "mso-spt180", mso_sptBorderCallout90 },
+ { "line-callout-3", mso_sptAccentBorderCallout90 },
+ { "mso-spt182", mso_sptLeftRightUpArrow },
+ { "sun", mso_sptSun },
+ { "moon", mso_sptMoon },
+ { "bracket-pair", mso_sptBracketPair },
+ { "brace-pair", mso_sptBracePair },
+ { "star4", mso_sptSeal4 },
+ { "mso-spt188", mso_sptDoubleWave },
+ { "mso-spt189", mso_sptActionButtonBlank },
+ { "mso-spt190", mso_sptActionButtonHome },
+ { "mso-spt191", mso_sptActionButtonHelp },
+ { "mso-spt192", mso_sptActionButtonInformation },
+ { "mso-spt193", mso_sptActionButtonForwardNext },
+ { "mso-spt194", mso_sptActionButtonBackPrevious },
+ { "mso-spt195", mso_sptActionButtonEnd },
+ { "mso-spt196", mso_sptActionButtonBeginning },
+ { "mso-spt197", mso_sptActionButtonReturn },
+ { "mso-spt198", mso_sptActionButtonDocument },
+ { "mso-spt199", mso_sptActionButtonSound },
+ { "mso-spt200", mso_sptActionButtonMovie },
+ { "mso-spt201", mso_sptHostControl },
+ { "mso-spt202", mso_sptTextBox },
+ { "teardrop", mso_sptTearDrop },
+ { "ooxml-rect", mso_sptRectangle }
+};
+
+ // gallery: quadrat
+ // gallery: round-quadrat
+ // gallery: circle
+ // gallery: circle-pie
+ // gallery: frame
+ // gallery: flower
+ // gallery: cloud
+ // gallery: puzzle
+ // gallery: octagon-bevel
+ // gallery: diamond-bevel
+ // gallery: up-right-arrow
+ // gallery: up-right-down-arrow
+ // gallery: corner-right-arrow
+ // gallery: split-arrow
+ // gallery: up-right-arrow-callout
+ // gallery: split-round-arrow
+ // gallery: s-sharped-arrow
+ // Gallery: star6
+ // Gallery: star12
+ // Gallery: concave-star6
+ // Gallery: signet
+ // Gallery: doorplate
+ // gallery: fontwork-arch-left-curve
+ // gallery: fontwork-arch-right-curve
+ // gallery: fontwork-arch-left-pour
+ // gallery: fontwork-arch-right-pour
+
+
+typedef std::unordered_map< OUString, MSO_SPT> TypeNameHashMap;
+
+static const TypeNameHashMap& GetTypeNameHashMap()
+{
+ static TypeNameHashMap aMap = []()
+ {
+ TypeNameHashMap map;
+ for (auto const & i : pNameTypeTableArray)
+ map[OUString::createFromAscii(i.pS)] = i.pE;
+ return map;
+ }();
+ return aMap;
+}
+
+
+MSO_SPT EnhancedCustomShapeTypeNames::Get( const OUString& rShapeType )
+{
+ const TypeNameHashMap & rTypeMap = GetTypeNameHashMap();
+ MSO_SPT eRetValue = mso_sptNil;
+ auto aHashIter = rTypeMap.find( rShapeType );
+ if ( aHashIter != rTypeMap.end() )
+ eRetValue = (*aHashIter).second;
+ return eRetValue;
+}
+
+OUString EnhancedCustomShapeTypeNames::Get( const MSO_SPT eShapeType )
+{
+ return (eShapeType <= mso_sptTextBox && eShapeType >= mso_sptMin)
+ ? OUString::createFromAscii( pNameTypeTableArray[ eShapeType ].pS )
+ : OUString();
+}
+
+namespace {
+
+struct ACCNameTypeTable
+{
+ const char* pS;
+ const char* pE;
+};
+
+}
+
+const ACCNameTypeTable pACCNameTypeTableArray[] =
+{
+ { "non-primitive", "Non Primitive Shape" },
+ { "rectangle", "Rectangle" },
+ { "round-rectangle", "Rounded Rectangle" },
+ { "ellipse", "Ellipse" },
+ { "diamond", "Diamond" },
+ { "isosceles-triangle", "Triangle" },
+ { "right-triangle", "Right Triangle" },
+ { "parallelogram", "Parallelogram" },
+ { "trapezoid", "Trapezoid" },
+ { "hexagon", "Hexagon" },
+ { "octagon", "Octagon" },
+ { "cross", "Cross" },
+ { "star5", "5-Point Star" },
+ { "right-arrow", "Right Arrow" },
+ //{ "mso-spt14", mso_sptThickArrow },
+ { "pentagon-right", "Pentagon" },
+ { "cube", "Cube" },
+ { "mso-spt21", "Doorplate" },
+ /*{ "mso-spt17", mso_sptBalloon },
+ { "mso-spt18", mso_sptSeal },
+ { "mso-spt19", mso_sptArc },
+ { "mso-spt20", mso_sptLine },
+ { "mso-spt21", mso_sptPlaque },
+ { "can", mso_sptCan },*/
+ { "ring", "Ring" },
+ /*{ "mso-spt24", mso_sptTextSimple },
+ { "mso-spt25", mso_sptTextOctagon },
+ { "mso-spt26", mso_sptTextHexagon },
+ { "mso-spt27", mso_sptTextCurve },
+ { "mso-spt28", mso_sptTextWave },
+ { "mso-spt29", mso_sptTextRing },
+ { "mso-spt30", mso_sptTextOnCurve },
+ { "mso-spt31", mso_sptTextOnRing },
+ { "mso-spt32", mso_sptStraightConnector1 },
+ { "mso-spt33", mso_sptBentConnector2 },
+ { "mso-spt34", mso_sptBentConnector3 },
+ { "mso-spt35", mso_sptBentConnector4 },
+ { "mso-spt36", mso_sptBentConnector5 },
+ { "mso-spt37", mso_sptCurvedConnector2 },
+ { "mso-spt38", mso_sptCurvedConnector3 },
+ { "mso-spt39", mso_sptCurvedConnector4 },
+ { "mso-spt40", mso_sptCurvedConnector5 },
+ { "mso-spt41", mso_sptCallout1 },
+ { "mso-spt42", mso_sptCallout2 },
+ { "mso-spt43", mso_sptCallout3 },
+ { "mso-spt44", mso_sptAccentCallout1 },
+ { "mso-spt45", mso_sptAccentCallout2 },
+ { "mso-spt46", mso_sptAccentCallout3 },*/
+ { "line-callout-1", "Line Callout 1" },
+ { "line-callout-2", "Line Callout 2" },
+ /*{ "mso-spt49", mso_sptBorderCallout3 },
+ { "mso-spt50", mso_sptAccentBorderCallout1 },
+ { "mso-spt51", mso_sptAccentBorderCallout2 },
+ { "mso-spt52", mso_sptAccentBorderCallout3 },
+ { "mso-spt53", mso_sptRibbon },
+ { "mso-spt54", mso_sptRibbon2 },*/
+ { "chevron", "Chevron" },
+ { "pentagon", "Regular Pentagon" },
+ { "forbidden", "'No' Symbol" },
+ { "star8", "8-Point Star" },
+ /*{ "mso-spt59", mso_sptSeal16 },
+ { "mso-spt60", mso_sptSeal32 },*/
+ { "rectangular-callout", "Rectangular Callout" },
+ { "round-rectangular-callout", "Rounded Rectangular Callout" },
+ { "round-callout", "Round Callout" },
+ //{ "mso-spt64", mso_sptWave },
+ { "paper", "Folded Corner" },
+ { "left-arrow", "Left Arrow" },
+ { "down-arrow", "Down Arrow" },
+ { "up-arrow", "Up Arrow" },
+ { "left-right-arrow", "Left and Right Arrow" },
+ { "up-down-arrow", "Up and Down Arrow" },
+ //{ "mso-spt71", mso_sptIrregularSeal1 },
+ { "bang", "Explosion" },
+ { "lightning", "Lighting Bolt" },
+ { "heart", "Heart" },
+ //{ "mso-spt75", mso_sptPictureFrame },
+ { "quad-arrow", "4-Way Arrow" },
+ { "left-arrow-callout", "Left Arrow Callout" },
+ { "right-arrow-callout", "Right Arrow Callout" },
+ { "up-arrow-callout", "Up Arrow Callout" },
+ { "down-arrow-callout", "Down Arrow Callout" },
+ { "left-right-arrow-callout", "Left and Right Arrow Callout" },
+ { "up-down-arrow-callout", "Up and Down Arrow Callout" },
+ { "quad-arrow-callout", "4-Way Arrow Callout" },
+ { "quad-bevel", "Square Bevel" },
+ { "left-bracket", "Left Bracket" },
+ { "right-bracket", "Right Bracket" },
+ { "left-brace", "Left Brace" },
+ { "right-brace", "Right Brace" },
+ { "mso-spt89", "Up and Left Arrow" },
+ //{ "mso-spt90", mso_sptBentUpArrow },
+ //{ "mso-spt91", mso_sptBentArrow },
+ { "star24", "24-Point Star" },
+ { "striped-right-arrow", "Striped Right Arrow" },
+ { "notched-right-arrow", "Notched Right Arrow" },
+ { "block-arc", "Block Arc" },
+ { "smiley", "Smile Face" },
+ { "vertical-scroll", "Vertical Scroll" },
+ { "horizontal-scroll", "Horizontal Scroll" },
+ { "circular-arrow", "Circular Arrow" },
+ { "mso-spt100", "Notched Circular Arrow" },
+ /*
+ { "mso-spt101", mso_sptUturnArrow },
+ { "mso-spt102", mso_sptCurvedRightArrow },
+ { "mso-spt103", mso_sptCurvedLeftArrow },
+ { "mso-spt104", mso_sptCurvedUpArrow },
+ { "mso-spt105", mso_sptCurvedDownArrow },*/
+ { "cloud-callout", "Cloud Callout" },
+ /*{ "mso-spt107", mso_sptEllipseRibbon },
+ { "mso-spt108", mso_sptEllipseRibbon2 },*/
+ { "flowchart-process", "Flowchart:Process" },
+ { "flowchart-decision", "Flowchart:Decision" },
+ { "flowchart-data", "Flowchart:Data" },
+ { "flowchart-predefined-process", "Flowchart:Predefined Process" },
+ { "flowchart-internal-storage", "Flowchart:Internal Storage" },
+ { "flowchart-document", "Flowchart:Document" },
+ { "flowchart-multidocument", "Flowchart:Multidocument" },
+ { "flowchart-terminator", "Flowchart:Terminator" },
+ { "flowchart-preparation", "Flowchart:Preparation" },
+ { "flowchart-manual-input", "Flowchart:Manual Input" },
+ { "flowchart-manual-operation", "Flowchart:Manual Operation" },
+ { "flowchart-connector", "Flowchart:Connector" },
+ { "flowchart-card", "Flowchart:Card" },
+ { "flowchart-punched-tape", "Flowchart:Punched Tape" },
+ { "flowchart-summing-junction", "Flowchart:Summing Junction" },
+ { "flowchart-or", "Flowchart:Or" },
+ { "flowchart-collate", "Flowchart:Collate" },
+ { "flowchart-sort", "Flowchart:Sort" },
+ { "flowchart-extract", "Flowchart:Extract" },
+ { "flowchart-merge", "Flowchart:Merge" },
+ //{ "mso-spt129", mso_sptFlowChartOfflineStorage },
+ { "flowchart-stored-data", "Flowchart:Stored Data" },
+ { "flowchart-sequential-access", "drawingbar.fc.25=Flowchart:Sequential Access" },
+ { "flowchart-magnetic-disk", "Flowchart:Magnetic Disk" },
+ { "flowchart-direct-access-storage", "Flowchart:Direct Access Storage" },
+ { "flowchart-display", "Flowchart:Display" },
+ { "flowchart-delay", "Flowchart:Delay" },
+ /*{ "fontwork-plain-text", mso_sptTextPlainText },
+ { "fontwork-stop", mso_sptTextStop },
+ { "fontwork-triangle-up", mso_sptTextTriangle },
+ { "fontwork-triangle-down", mso_sptTextTriangleInverted },
+ { "fontwork-chevron-up", mso_sptTextChevron },
+ { "fontwork-chevron-down", mso_sptTextChevronInverted },
+ { "mso-spt142", mso_sptTextRingInside },
+ { "mso-spt143", mso_sptTextRingOutside },
+ { "fontwork-arch-up-curve", mso_sptTextArchUpCurve },
+ { "fontwork-arch-down-curve", mso_sptTextArchDownCurve },
+ { "fontwork-circle-curve", mso_sptTextCircleCurve },
+ { "fontwork-open-circle-curve", mso_sptTextButtonCurve },
+ { "fontwork-arch-up-pour", mso_sptTextArchUpPour },
+ { "fontwork-arch-down-pour", mso_sptTextArchDownPour },
+ { "fontwork-circle-pour", mso_sptTextCirclePour },
+ { "fontwork-open-circle-pour", mso_sptTextButtonPour },
+ { "fontwork-curve-up", mso_sptTextCurveUp },
+ { "fontwork-curve-down", mso_sptTextCurveDown },
+ { "fontwork-fade-up-and-right", mso_sptTextCascadeUp },
+ { "fontwork-fade-up-and-left", mso_sptTextCascadeDown },
+ { "fontwork-wave", mso_sptTextWave1 },
+ { "mso-spt157", mso_sptTextWave2 },
+ { "mso-spt158", mso_sptTextWave3 },
+ { "mso-spt159", mso_sptTextWave4 },
+ { "fontwork-inflate", mso_sptTextInflate },
+ { "mso-spt161", mso_sptTextDeflate },
+ { "mso-spt162", mso_sptTextInflateBottom },
+ { "mso-spt163", mso_sptTextDeflateBottom },
+ { "mso-spt164", mso_sptTextInflateTop },
+ { "mso-spt165", mso_sptTextDeflateTop },
+ { "mso-spt166", mso_sptTextDeflateInflate },
+ { "mso-spt167", mso_sptTextDeflateInflateDeflate },
+ { "fontwork-fade-right", mso_sptTextFadeRight },
+ { "fontwork-fade-left", mso_sptTextFadeLeft },
+ { "fontwork-fade-up", mso_sptTextFadeUp },
+ { "fontwork-fade-down", mso_sptTextFadeDown },
+ { "fontwork-slant-up", mso_sptTextSlantUp },
+ { "fontwork-slant-down", mso_sptTextSlantDown },
+ { "mso-spt174", mso_sptTextCanUp },
+ { "mso-spt175", mso_sptTextCanDown },*/
+ { "flowchart-alternate-process", "Flowchart:Alternate Process " },
+ { "flowchart-off-page-connector", "Flowchart:Off-page Connector" },
+ /*{ "mso-spt178", mso_sptCallout90 },
+ { "mso-spt179", mso_sptAccentCallout90 },
+ { "mso-spt180", mso_sptBorderCallout90 },*/
+ { "line-callout-3", "Line Callout 3" },
+ //{ "mso-spt182", mso_sptLeftRightUpArrow },
+ { "sun", "Sun" },
+ { "moon", "Moon" },
+ { "bracket-pair", "Double Bracket" },
+ { "brace-pair", "Double Brace" },
+ { "star4", "4-Point Star" },
+ /*{ "mso-spt188", mso_sptDoubleWave },
+ { "mso-spt189", mso_sptActionButtonBlank },
+ { "mso-spt190", mso_sptActionButtonHome },
+ { "mso-spt191", mso_sptActionButtonHelp },
+ { "mso-spt192", mso_sptActionButtonInformation },
+ { "mso-spt193", mso_sptActionButtonForwardNext },
+ { "mso-spt194", mso_sptActionButtonBackPrevious },
+ { "mso-spt195", mso_sptActionButtonEnd },
+ { "mso-spt196", mso_sptActionButtonBeginning },
+ { "mso-spt197", mso_sptActionButtonReturn },
+ { "mso-spt198", mso_sptActionButtonDocument },
+ { "mso-spt199", mso_sptActionButtonSound },
+ { "mso-spt200", mso_sptActionButtonMovie },
+ { "mso-spt201", mso_sptHostControl },
+ { "mso-spt202", mso_sptTextBox },*/
+ { "frame", "Frame" },
+ { "col-60da8460", "Octagon Bevel" },
+ { "col-502ad400", "Diamond Bevel" }
+};
+
+typedef std::unordered_map<OUString, OUString> TypeACCNameHashMap;
+
+static const TypeACCNameHashMap& GetACCHashMap()
+{
+ static TypeACCNameHashMap aMap = []()
+ {
+ TypeACCNameHashMap map;
+ for (auto const & i : pACCNameTypeTableArray)
+ map[OUString::createFromAscii(i.pS)] = OUString::createFromAscii(i.pE);
+ return map;
+ }();
+ return aMap;
+}
+
+const OUString & EnhancedCustomShapeTypeNames::GetAccName( const OUString& rShapeType )
+{
+ static const OUString EMPTY;
+ const TypeACCNameHashMap& rACCMap = GetACCHashMap();
+ auto aHashIter = rACCMap.find( rShapeType );
+ if ( aHashIter != rACCMap.end() )
+ return aHashIter->second;
+ return EMPTY;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/diagram/IDiagramHelper.cxx b/svx/source/diagram/IDiagramHelper.cxx
new file mode 100644
index 000000000..1803a3d2b
--- /dev/null
+++ b/svx/source/diagram/IDiagramHelper.cxx
@@ -0,0 +1,430 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * 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/diagram/IDiagramHelper.hxx>
+#include <svx/svdogrp.hxx>
+#include <svx/svdhdl.hxx>
+#include <svx/svdmrkv.hxx>
+#include <svx/svdpagv.hxx>
+#include <svx/sdrpagewindow.hxx>
+#include <svx/sdrpaintwindow.hxx>
+#include <basegfx/polygon/b2dpolygontools.hxx>
+#include <svx/sdr/primitive2d/svx_primitivetypes2d.hxx>
+#include <drawinglayer/primitive2d/primitivetools2d.hxx>
+#include <svx/sdr/overlay/overlaymanager.hxx>
+#include <drawinglayer/attribute/lineattribute.hxx>
+#include <drawinglayer/primitive2d/PolyPolygonStrokePrimitive2D.hxx>
+#include <drawinglayer/primitive2d/PolygonStrokePrimitive2D.hxx>
+#include <drawinglayer/primitive2d/PolyPolygonColorPrimitive2D.hxx>
+#include <comphelper/dispatchcommand.hxx>
+#include <drawinglayer/primitive2d/textlayoutdevice.hxx>
+#include <officecfg/Office/Common.hxx>
+#include <svx/strings.hrc>
+#include <svx/dialmgr.hxx>
+
+namespace {
+
+// helper to create the geometry for a rounded polygon, maybe
+// containing a Lap positioned inside top-left for some text
+basegfx::B2DPolygon createRoundedPolygon(
+ const basegfx::B2DRange& rRange,
+ double fDistance,
+ bool bCreateLap,
+ double fTextWidth)
+{
+ basegfx::B2DPolygon aRetval;
+
+ // TopLeft rounded edge
+ aRetval.append(
+ basegfx::utils::createPolygonFromEllipseSegment(
+ basegfx::B2DPoint(rRange.getMinX(), rRange.getMinY()),
+ fDistance,
+ fDistance,
+ M_PI * 1.0,
+ M_PI * 1.5));
+
+ // create Lap topLeft inside
+ if(bCreateLap)
+ {
+ const double fLapLeft(rRange.getMinX() + fDistance);
+ double fLapRight(rRange.getMinX() + (rRange.getWidth() * 0.5) - fDistance);
+ const double fLapTop(rRange.getMinY() - fDistance);
+ const double fLapBottom(fLapTop + (fDistance * 2.0));
+ const double fExtendedTextWidth(fTextWidth + (fDistance * 3.0));
+
+ if(0.0 != fExtendedTextWidth && fLapLeft + fExtendedTextWidth < fLapRight)
+ {
+ fLapRight = fLapLeft + fExtendedTextWidth;
+ }
+
+ aRetval.append(basegfx::B2DPoint(fLapLeft, fLapTop));
+ aRetval.append(basegfx::B2DPoint(fLapLeft + (fDistance * 0.5), fLapBottom));
+ aRetval.append(basegfx::B2DPoint(fLapRight - (fDistance * 0.5), fLapBottom));
+ aRetval.append(basegfx::B2DPoint(fLapRight, fLapTop));
+ }
+
+ // TopRight rounded edge
+ aRetval.append(
+ basegfx::utils::createPolygonFromEllipseSegment(
+ basegfx::B2DPoint(rRange.getMaxX(), rRange.getMinY()),
+ fDistance,
+ fDistance,
+ M_PI * 1.5,
+ M_PI * 0.0));
+
+ // BottomRight rounded edge
+ aRetval.append(
+ basegfx::utils::createPolygonFromEllipseSegment(
+ basegfx::B2DPoint(rRange.getMaxX(), rRange.getMaxY()),
+ fDistance,
+ fDistance,
+ M_PI * 0.0,
+ M_PI * 0.5));
+
+ // BottomLeft rounded edge
+ aRetval.append(
+ basegfx::utils::createPolygonFromEllipseSegment(
+ basegfx::B2DPoint(rRange.getMinX(), rRange.getMaxY()),
+ fDistance,
+ fDistance,
+ M_PI * 0.5,
+ M_PI * 1.0));
+
+ aRetval.setClosed(true);
+
+ return aRetval;
+}
+
+// helper primitive to create/show the overlay geometry for a DynamicDiagram
+class OverlayDiagramPrimitive final : public drawinglayer::primitive2d::DiscreteMetricDependentPrimitive2D
+{
+private:
+ basegfx::B2DHomMatrix maTransformation; // object dimensions
+ double mfDiscreteDistance; // distance from object in pixels
+ double mfDiscreteGap; // gap/width of visualization in pixels
+ Color maColor; // base color (made lighter/darker as needed, should be system selection color)
+
+ virtual void create2DDecomposition(
+ drawinglayer::primitive2d::Primitive2DContainer& rContainer,
+ const drawinglayer::geometry::ViewInformation2D& rViewInformation) const override;
+
+public:
+ OverlayDiagramPrimitive(
+ const basegfx::B2DHomMatrix& rTransformation,
+ double fDiscreteDistance,
+ double fDiscreteGap,
+ Color const & rColor);
+
+ virtual sal_uInt32 getPrimitive2DID() const override;
+};
+
+void OverlayDiagramPrimitive::create2DDecomposition(
+ drawinglayer::primitive2d::Primitive2DContainer& rContainer,
+ const drawinglayer::geometry::ViewInformation2D& /*rViewInformation*/) const
+{
+ // get the dimensions. Do *not* take rotation/shear into account,
+ // this is intended to be a pure expanded/frame visualization as
+ // needed in UI for simplified visualization
+ basegfx::B2DRange aRange(0.0, 0.0, 1.0, 1.0);
+ aRange.transform(maTransformation);
+
+ basegfx::B2DPolyPolygon aPolyPolygon;
+ const double fInnerDistance(mfDiscreteDistance * getDiscreteUnit());
+ const double fOuterDistance((mfDiscreteDistance + mfDiscreteGap) * getDiscreteUnit());
+ bool bCreateLap(true);
+ basegfx::B2DPolyPolygon aTextAsPolyPolygon;
+ double fTextWidth(0.0);
+
+ // initially try to create lap
+ if(bCreateLap)
+ {
+ // take a resource text (for now existing one that fits)
+ const OUString aName(SvxResId(RID_STR_DATANAV_EDIT_ELEMENT));
+ drawinglayer::primitive2d::TextLayouterDevice aTextLayouter;
+ basegfx::B2DPolyPolygonVector aTarget;
+ std::vector<double> aDXArray;
+
+ // to simplify things for now, do not create a TextSimplePortionPrimitive2D
+ // and needed FontAttribute, just get the TextOutlines as geometry
+ aTextLayouter.getTextOutlines(
+ aTarget,
+ aName,
+ 0,
+ aName.getLength(),
+ aDXArray);
+
+ // put into one PolyPolygon (also simplification - overlapping chars
+ // may create XOR gaps, so these exist for a reason, but low probability)
+ for (auto const& elem : aTarget)
+ {
+ aTextAsPolyPolygon.append(elem);
+ }
+
+ // get text dimensions & transform to destination
+ const basegfx::B2DRange aTextRange(aTextAsPolyPolygon.getB2DRange());
+ basegfx::B2DHomMatrix aTextTransform;
+
+ aTextTransform.translate(aTextRange.getMinX(), aTextRange.getMinY());
+ const double fTargetTextHeight((mfDiscreteDistance + mfDiscreteGap - 2.0) * getDiscreteUnit());
+ const double fTextScale(fTargetTextHeight / aTextRange.getHeight());
+ aTextTransform.scale(fTextScale, fTextScale);
+ aTextTransform.translate(
+ aRange.getMinX() + (fInnerDistance * 2.0),
+ aRange.getMinY() + fTargetTextHeight + (fOuterDistance - fInnerDistance) - (2.0 * getDiscreteUnit()));
+ aTextAsPolyPolygon.transform(aTextTransform);
+
+ // check text size/position
+ fTextWidth = aTextRange.getWidth() * fTextScale;
+ const double fLapLeft(aRange.getMinX() + fInnerDistance);
+ const double fLapRight(aRange.getMinX() + (aRange.getWidth() * 0.5) - fInnerDistance);
+
+ // if text is too big, do not create a Lap at all
+ // to avoid trouble. It is expected that the user keeps
+ // the object he works with big enough to do useful actions
+ if(fTextWidth + (4.0 * getDiscreteUnit()) > fLapRight - fLapLeft)
+ bCreateLap = false;
+ }
+
+ // create outer polygon
+ aPolyPolygon.append(
+ createRoundedPolygon(
+ aRange,
+ fOuterDistance,
+ false,
+ 0.0));
+
+ // create inner polygon, maybe with Lap
+ aPolyPolygon.append(
+ createRoundedPolygon(
+ aRange,
+ fInnerDistance,
+ bCreateLap,
+ fTextWidth));
+
+ Color aFillColor(maColor);
+ Color aLineColor(maColor);
+
+ aFillColor.IncreaseLuminance(10);
+ aLineColor.DecreaseLuminance(30);
+
+ const drawinglayer::attribute::LineAttribute aLineAttribute(
+ aLineColor.getBColor(),
+ 1.0 * getDiscreteUnit());
+
+ // filled polygon as BG (may get transparence for better look ?)
+ rContainer.push_back(
+ new drawinglayer::primitive2d::PolyPolygonColorPrimitive2D(
+ aPolyPolygon,
+ aFillColor.getBColor()));
+
+ // outline polygon for visibility (may be accentuated shaded
+ // top/left, would require alternative creation)
+ rContainer.push_back(
+ new drawinglayer::primitive2d::PolyPolygonStrokePrimitive2D(
+ aPolyPolygon,
+ aLineAttribute));
+
+ // top-left line pattern (as grep-here-sign to signal
+ // that this construct may be also dragged by the user)
+ const double fLapLeft(aRange.getMinX() + fInnerDistance);
+ const double fLapRight(aRange.getMinX() + (aRange.getWidth() * 0.5) - fInnerDistance);
+ const double fLapUp(aRange.getMinY() - ((mfDiscreteDistance + mfDiscreteDistance * 0.666) * getDiscreteUnit()));
+ const double fLapDown(aRange.getMinY() - ((mfDiscreteDistance + mfDiscreteDistance * 0.333) * getDiscreteUnit()));
+ basegfx::B2DPolygon aPolygonLapUp;
+ aPolygonLapUp.append(basegfx::B2DPoint(fLapLeft, fLapUp));
+ aPolygonLapUp.append(basegfx::B2DPoint(fLapRight, fLapUp));
+ basegfx::B2DPolygon aPolygonLapDown;
+ aPolygonLapDown.append(basegfx::B2DPoint(fLapLeft, fLapDown));
+ aPolygonLapDown.append(basegfx::B2DPoint(fLapRight, fLapDown));
+ drawinglayer::attribute::StrokeAttribute aStrokeAttribute({ 2.0 * getDiscreteUnit(), 2.0 * getDiscreteUnit() });
+
+ rContainer.push_back(
+ new drawinglayer::primitive2d::PolygonStrokePrimitive2D(
+ aPolygonLapUp,
+ aLineAttribute,
+ aStrokeAttribute));
+
+ rContainer.push_back(
+ new drawinglayer::primitive2d::PolygonStrokePrimitive2D(
+ aPolygonLapDown,
+ aLineAttribute,
+ aStrokeAttribute));
+
+ // add text last. May use darker text color, go for same color
+ // as accentuation line for now
+ if(bCreateLap && 0 != aTextAsPolyPolygon.count())
+ {
+ rContainer.push_back(
+ new drawinglayer::primitive2d::PolyPolygonColorPrimitive2D(
+ aTextAsPolyPolygon,
+ aLineColor.getBColor()));
+ }
+}
+
+OverlayDiagramPrimitive::OverlayDiagramPrimitive(
+ const basegfx::B2DHomMatrix& rTransformation,
+ double fDiscreteDistance,
+ double fDiscreteGap,
+ Color const & rColor)
+: drawinglayer::primitive2d::DiscreteMetricDependentPrimitive2D()
+, maTransformation(rTransformation)
+, mfDiscreteDistance(fDiscreteDistance)
+, mfDiscreteGap(fDiscreteGap)
+, maColor(rColor)
+{
+}
+
+sal_uInt32 OverlayDiagramPrimitive::getPrimitive2DID() const
+{
+ return PRIMITIVE2D_ID_OVERLAYDIAGRAMPRIMITIVE2D;
+}
+
+// helper object for DiagramOverlay
+class OverlayDiagramFrame final : public sdr::overlay::OverlayObject
+{
+private:
+ basegfx::B2DHomMatrix maTransformation; // object dimensions
+ Color maColor; // base color
+
+ virtual drawinglayer::primitive2d::Primitive2DContainer createOverlayObjectPrimitive2DSequence() override;
+
+public:
+ explicit OverlayDiagramFrame(
+ const basegfx::B2DHomMatrix& rTransformation,
+ Color const & rColor);
+};
+
+OverlayDiagramFrame::OverlayDiagramFrame(
+ const basegfx::B2DHomMatrix& rTransformation,
+ const Color& rColor)
+: sdr::overlay::OverlayObject(rColor)
+, maTransformation(rTransformation)
+, maColor(rColor)
+{
+}
+
+drawinglayer::primitive2d::Primitive2DContainer OverlayDiagramFrame::createOverlayObjectPrimitive2DSequence()
+{
+ drawinglayer::primitive2d::Primitive2DContainer aReturnContainer;
+
+ if ( !officecfg::Office::Common::Misc::ExperimentalMode::get() )
+ return aReturnContainer;
+
+ if (getOverlayManager())
+ {
+ aReturnContainer = drawinglayer::primitive2d::Primitive2DContainer {
+ new OverlayDiagramPrimitive(
+ maTransformation,
+ 8.0, // distance from geometry in pixels
+ 8.0, // gap/width of visualization in pixels
+ maColor) };
+ }
+
+ return aReturnContainer;
+}
+
+} // end of anonymous namespace
+
+namespace svx { namespace diagram {
+
+void DiagramFrameHdl::clicked(const Point& /*rPnt*/)
+{
+ // this may check for a direct hit at the text later
+ // and only then take action. That would require
+ // to evaluate & keep that (maybe during creation).
+ // For now, just trigger to open the Dialog
+ comphelper::dispatchCommand(".uno:EditDiagram", {});
+}
+
+void DiagramFrameHdl::CreateB2dIAObject()
+{
+ // first throw away old one
+ GetRidOfIAObject();
+
+ SdrMarkView* pView = pHdlList->GetView();
+
+ if(!pView || pView->areMarkHandlesHidden())
+ return;
+
+ SdrPageView* pPageView = pView->GetSdrPageView();
+
+ if(!pPageView)
+ return;
+
+ for(sal_uInt32 b(0); b < pPageView->PageWindowCount(); b++)
+ {
+ const SdrPageWindow& rPageWindow = *pPageView->GetPageWindow(b);
+
+ if(rPageWindow.GetPaintWindow().OutputToWindow())
+ {
+ const rtl::Reference< sdr::overlay::OverlayManager >& xManager = rPageWindow.GetOverlayManager();
+ if (xManager.is())
+ {
+ OutputDevice& rOutDev(rPageWindow.GetPaintWindow().GetOutputDevice());
+ const StyleSettings& rStyles(rOutDev.GetSettings().GetStyleSettings());
+ Color aFillColor(rStyles.GetHighlightColor());
+ std::unique_ptr<sdr::overlay::OverlayObject> pNewOverlayObject(
+ new OverlayDiagramFrame(
+ maTransformation,
+ aFillColor));
+
+ // OVERLAYMANAGER
+ insertNewlyCreatedOverlayObjectForSdrHdl(
+ std::move(pNewOverlayObject),
+ rPageWindow.GetObjectContact(),
+ *xManager);
+ }
+ }
+ }
+}
+
+DiagramFrameHdl::DiagramFrameHdl(const basegfx::B2DHomMatrix& rTransformation)
+: SdrHdl(Point(), SdrHdlKind::Move)
+, maTransformation(rTransformation)
+{
+}
+
+IDiagramHelper::IDiagramHelper()
+: mbUseDiagramThemeData(false)
+, mbUseDiagramModelData(true)
+, mbForceThemePtrRecreation(false)
+{
+}
+
+IDiagramHelper::~IDiagramHelper() {}
+
+void IDiagramHelper::anchorToSdrObjGroup(SdrObjGroup& rTarget)
+{
+ rTarget.mp_DiagramHelper.reset(this);
+}
+
+void IDiagramHelper::AddAdditionalVisualization(const SdrObjGroup& rTarget, SdrHdlList& rHdlList)
+{
+ // create an extra frame visualization here
+ basegfx::B2DHomMatrix aTransformation;
+ basegfx::B2DPolyPolygon aPolyPolygon;
+ rTarget.TRGetBaseGeometry(aTransformation, aPolyPolygon);
+
+ std::unique_ptr<SdrHdl> pHdl(new DiagramFrameHdl(aTransformation));
+ rHdlList.AddHdl(std::move(pHdl));
+}
+
+}} // end of namespace
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/diagram/datamodel.cxx b/svx/source/diagram/datamodel.cxx
new file mode 100644
index 000000000..e45fe7fb4
--- /dev/null
+++ b/svx/source/diagram/datamodel.cxx
@@ -0,0 +1,501 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <unordered_set>
+#include <algorithm>
+#include <fstream>
+
+#include <svx/diagram/datamodel.hxx>
+#include <comphelper/xmltools.hxx>
+#include <sal/log.hxx>
+
+namespace svx::diagram {
+
+Connection::Connection()
+: mnXMLType( XML_none )
+, mnSourceOrder( 0 )
+, mnDestOrder( 0 )
+{
+}
+
+Point::Point()
+: msTextBody(std::make_shared< TextBody >())
+, msPointStylePtr(std::make_shared< PointStyle >())
+, mnXMLType(XML_none)
+, mnMaxChildren(-1)
+, mnPreferredChildren(-1)
+, mnDirection(XML_norm)
+, mnResizeHandles(XML_rel)
+, mnCustomAngle(-1)
+, mnPercentageNeighbourWidth(-1)
+, mnPercentageNeighbourHeight(-1)
+, mnPercentageOwnWidth(-1)
+, mnPercentageOwnHeight(-1)
+, mnIncludeAngleScale(-1)
+, mnRadiusScale(-1)
+, mnWidthScale(-1)
+, mnHeightScale(-1)
+, mnWidthOverride(-1)
+, mnHeightOverride(-1)
+, mnLayoutStyleCount(-1)
+, mnLayoutStyleIndex(-1)
+, mbOrgChartEnabled(false)
+, mbBulletEnabled(false)
+, mbCoherent3DOffset(false)
+, mbCustomHorizontalFlip(false)
+, mbCustomVerticalFlip(false)
+, mbCustomText(false)
+, mbIsPlaceholder(false)
+{
+}
+
+DiagramData::DiagramData()
+{
+}
+
+DiagramData::~DiagramData()
+{
+}
+
+const Point* DiagramData::getRootPoint() const
+{
+ for (const auto & aCurrPoint : maPoints)
+ if (aCurrPoint.mnXMLType == TypeConstant::XML_doc)
+ return &aCurrPoint;
+
+ SAL_WARN("svx.diagram", "No root point");
+ return nullptr;
+}
+
+OUString DiagramData::getString() const
+{
+ OUStringBuffer aBuf;
+ const Point* pPoint = getRootPoint();
+ getChildrenString(aBuf, pPoint, 0);
+ return aBuf.makeStringAndClear();
+}
+
+bool DiagramData::removeNode(const OUString& rNodeId)
+{
+ // check if it doesn't have children
+ for (const auto& aCxn : maConnections)
+ if (aCxn.mnXMLType == TypeConstant::XML_parOf && aCxn.msSourceId == rNodeId)
+ {
+ SAL_WARN("svx.diagram", "Node has children - can't be removed");
+ return false;
+ }
+
+ Connection aParCxn;
+ for (const auto& aCxn : maConnections)
+ if (aCxn.mnXMLType == TypeConstant::XML_parOf && aCxn.msDestId == rNodeId)
+ aParCxn = aCxn;
+
+ std::unordered_set<OUString> aIdsToRemove;
+ aIdsToRemove.insert(rNodeId);
+ if (!aParCxn.msParTransId.isEmpty())
+ aIdsToRemove.insert(aParCxn.msParTransId);
+ if (!aParCxn.msSibTransId.isEmpty())
+ aIdsToRemove.insert(aParCxn.msSibTransId);
+
+ for (const Point& rPoint : maPoints)
+ if (aIdsToRemove.count(rPoint.msPresentationAssociationId))
+ aIdsToRemove.insert(rPoint.msModelId);
+
+ // insert also transition nodes
+ for (const auto& aCxn : maConnections)
+ if (aIdsToRemove.count(aCxn.msSourceId) || aIdsToRemove.count(aCxn.msDestId))
+ if (!aCxn.msPresId.isEmpty())
+ aIdsToRemove.insert(aCxn.msPresId);
+
+ // remove connections
+ maConnections.erase(std::remove_if(maConnections.begin(), maConnections.end(),
+ [aIdsToRemove](const Connection& rCxn) {
+ return aIdsToRemove.count(rCxn.msSourceId) || aIdsToRemove.count(rCxn.msDestId);
+ }),
+ maConnections.end());
+
+ // remove data and presentation nodes
+ maPoints.erase(std::remove_if(maPoints.begin(), maPoints.end(),
+ [aIdsToRemove](const Point& rPoint) {
+ return aIdsToRemove.count(rPoint.msModelId);
+ }),
+ maPoints.end());
+
+ // TODO: fix source/dest order
+ return true;
+}
+
+DiagramDataState::DiagramDataState(const Connections& rConnections, const Points& rPoints)
+: maConnections(rConnections)
+, maPoints(rPoints)
+{
+}
+
+DiagramDataStatePtr DiagramData::extractDiagramDataState() const
+{
+ // Just copy all Connections && Points. The shared_ptr data in
+ // Point-entries is no problem, it just continues exiting shared
+ return std::make_shared< DiagramDataState >(maConnections, maPoints);
+}
+
+void DiagramData::applyDiagramDataState(const DiagramDataStatePtr& rState)
+{
+ if(rState)
+ {
+ maConnections = rState->getConnections();
+ maPoints = rState->getPoints();
+
+ // Reset temporary buffered ModelData association lists & rebuild them
+ // and the Diagram DataModel. Do that here *immediately* to prevent
+ // re-usage of potentially invalid Connection/Point objects
+ buildDiagramDataModel(true);
+ }
+}
+
+void DiagramData::getChildrenString(
+ OUStringBuffer& rBuf,
+ const svx::diagram::Point* pPoint,
+ sal_Int32 nLevel) const
+{
+ if (!pPoint)
+ return;
+
+ if (nLevel > 0)
+ {
+ for (sal_Int32 i = 0; i < nLevel-1; i++)
+ rBuf.append('\t');
+ rBuf.append('+');
+ rBuf.append(' ');
+ rBuf.append(pPoint->msTextBody->msText);
+ rBuf.append('\n');
+ }
+
+ std::vector< const svx::diagram::Point* > aChildren;
+ for (const auto& rCxn : maConnections)
+ if (rCxn.mnXMLType == TypeConstant::XML_parOf && rCxn.msSourceId == pPoint->msModelId)
+ {
+ if (rCxn.mnSourceOrder >= static_cast<sal_Int32>(aChildren.size()))
+ aChildren.resize(rCxn.mnSourceOrder + 1);
+ const auto pChild = maPointNameMap.find(rCxn.msDestId);
+ if (pChild != maPointNameMap.end())
+ aChildren[rCxn.mnSourceOrder] = pChild->second;
+ }
+
+ for (auto pChild : aChildren)
+ getChildrenString(rBuf, pChild, nLevel + 1);
+}
+
+std::vector<std::pair<OUString, OUString>> DiagramData::getChildren(const OUString& rParentId) const
+{
+ const OUString sModelId = rParentId.isEmpty() ? getRootPoint()->msModelId : rParentId;
+ std::vector<std::pair<OUString, OUString>> aChildren;
+ for (const auto& rCxn : maConnections)
+ if (rCxn.mnXMLType == TypeConstant::XML_parOf && rCxn.msSourceId == sModelId)
+ {
+ if (rCxn.mnSourceOrder >= static_cast<sal_Int32>(aChildren.size()))
+ aChildren.resize(rCxn.mnSourceOrder + 1);
+ const auto pChild = maPointNameMap.find(rCxn.msDestId);
+ if (pChild != maPointNameMap.end())
+ {
+ aChildren[rCxn.mnSourceOrder] = std::make_pair(
+ pChild->second->msModelId,
+ pChild->second->msTextBody->msText);
+ }
+ }
+
+ // HACK: empty items shouldn't appear there
+ aChildren.erase(std::remove_if(aChildren.begin(), aChildren.end(),
+ [](const std::pair<OUString, OUString>& aItem) { return aItem.first.isEmpty(); }),
+ aChildren.end());
+
+ return aChildren;
+}
+
+OUString DiagramData::addNode(const OUString& rText)
+{
+ const svx::diagram::Point& rDataRoot = *getRootPoint();
+ OUString sPresRoot;
+ for (const auto& aCxn : maConnections)
+ if (aCxn.mnXMLType == TypeConstant::XML_presOf && aCxn.msSourceId == rDataRoot.msModelId)
+ sPresRoot = aCxn.msDestId;
+
+ if (sPresRoot.isEmpty())
+ return OUString();
+
+ OUString sNewNodeId = OStringToOUString(comphelper::xml::generateGUIDString(), RTL_TEXTENCODING_UTF8);
+
+ svx::diagram::Point aDataPoint;
+ aDataPoint.mnXMLType = TypeConstant::XML_node;
+ aDataPoint.msModelId = sNewNodeId;
+ aDataPoint.msTextBody->msText = rText;
+
+ OUString sDataSibling;
+ for (const auto& aCxn : maConnections)
+ if (aCxn.mnXMLType == TypeConstant::XML_parOf && aCxn.msSourceId == rDataRoot.msModelId)
+ sDataSibling = aCxn.msDestId;
+
+ OUString sPresSibling;
+ for (const auto& aCxn : maConnections)
+ if (aCxn.mnXMLType == TypeConstant::XML_presOf && aCxn.msSourceId == sDataSibling)
+ sPresSibling = aCxn.msDestId;
+
+ svx::diagram::Point aPresPoint;
+ aPresPoint.mnXMLType = TypeConstant::XML_pres;
+ aPresPoint.msModelId = OStringToOUString(comphelper::xml::generateGUIDString(), RTL_TEXTENCODING_UTF8);
+
+ aPresPoint.msPresentationAssociationId = aDataPoint.msModelId;
+ if (!sPresSibling.isEmpty())
+ {
+ // no idea where to get these values from, so copy from previous sibling
+ const svx::diagram::Point* pSiblingPoint = maPointNameMap[sPresSibling];
+ aPresPoint.msPresentationLayoutName = pSiblingPoint->msPresentationLayoutName;
+ aPresPoint.msPresentationLayoutStyleLabel = pSiblingPoint->msPresentationLayoutStyleLabel;
+ aPresPoint.mnLayoutStyleIndex = pSiblingPoint->mnLayoutStyleIndex;
+ aPresPoint.mnLayoutStyleCount = pSiblingPoint->mnLayoutStyleCount;
+ }
+
+ addConnection(svx::diagram::TypeConstant::XML_parOf, rDataRoot.msModelId, aDataPoint.msModelId);
+ addConnection(svx::diagram::TypeConstant::XML_presParOf, sPresRoot, aPresPoint.msModelId);
+ addConnection(svx::diagram::TypeConstant::XML_presOf, aDataPoint.msModelId, aPresPoint.msModelId);
+
+ // adding at the end, so that references are not invalidated in between
+ maPoints.push_back(aDataPoint);
+ maPoints.push_back(aPresPoint);
+
+ return sNewNodeId;
+}
+
+void DiagramData::addConnection(svx::diagram::TypeConstant nType, const OUString& sSourceId, const OUString& sDestId)
+{
+ sal_Int32 nMaxOrd = -1;
+ for (const auto& aCxn : maConnections)
+ if (aCxn.mnXMLType == nType && aCxn.msSourceId == sSourceId)
+ nMaxOrd = std::max(nMaxOrd, aCxn.mnSourceOrder);
+
+ svx::diagram::Connection& rCxn = maConnections.emplace_back();
+ rCxn.mnXMLType = nType;
+ rCxn.msSourceId = sSourceId;
+ rCxn.msDestId = sDestId;
+ rCxn.mnSourceOrder = nMaxOrd + 1;
+}
+
+// #define DEBUG_OOX_DIAGRAM
+#ifdef DEBUG_OOX_DIAGRAM
+OString normalizeDotName( const OUString& rStr )
+{
+ OUStringBuffer aBuf;
+ aBuf.append('N');
+
+ const sal_Int32 nLen(rStr.getLength());
+ sal_Int32 nCurrIndex(0);
+ while( nCurrIndex < nLen )
+ {
+ const sal_Int32 aChar=rStr.iterateCodePoints(&nCurrIndex);
+ if( aChar != '-' && aChar != '{' && aChar != '}' )
+ aBuf.append((sal_Unicode)aChar);
+ }
+
+ return OUStringToOString(aBuf.makeStringAndClear(),
+ RTL_TEXTENCODING_UTF8);
+}
+#endif
+
+static sal_Int32 calcDepth( std::u16string_view rNodeName,
+ const svx::diagram::Connections& rCnx )
+{
+ // find length of longest path in 'isChild' graph, ending with rNodeName
+ for (auto const& elem : rCnx)
+ {
+ if( !elem.msParTransId.isEmpty() &&
+ !elem.msSibTransId.isEmpty() &&
+ !elem.msSourceId.isEmpty() &&
+ !elem.msDestId.isEmpty() &&
+ elem.mnXMLType == TypeConstant::XML_parOf &&
+ rNodeName == elem.msDestId )
+ {
+ return calcDepth(elem.msSourceId, rCnx) + 1;
+ }
+ }
+
+ return 0;
+}
+
+void DiagramData::buildDiagramDataModel(bool /*bClearOoxShapes*/)
+{
+ // build name-object maps
+ maPointNameMap.clear();
+ maPointsPresNameMap.clear();
+ maConnectionNameMap.clear();
+ maPresOfNameMap.clear();
+ msBackgroundShapeModelID.clear();
+
+#ifdef DEBUG_OOX_DIAGRAM
+ std::ofstream output("tree.dot");
+
+ output << "digraph datatree {" << std::endl;
+#endif
+ svx::diagram::Points& rPoints = getPoints();
+ for (auto & point : rPoints)
+ {
+#ifdef DEBUG_OOX_DIAGRAM
+ output << "\t"
+ << normalizeDotName(point.msModelId).getStr()
+ << "[";
+
+ if( !point.msPresentationLayoutName.isEmpty() )
+ output << "label=\""
+ << OUStringToOString(
+ point.msPresentationLayoutName,
+ RTL_TEXTENCODING_UTF8).getStr() << "\", ";
+ else
+ output << "label=\""
+ << OUStringToOString(
+ point.msModelId,
+ RTL_TEXTENCODING_UTF8).getStr() << "\", ";
+
+ switch( point.mnXMLType )
+ {
+ case TypeConstant::XML_doc: output << "style=filled, color=red"; break;
+ case TypeConstant::XML_asst: output << "style=filled, color=green"; break;
+ default:
+ case TypeConstant::XML_node: output << "style=filled, color=blue"; break;
+ case TypeConstant::XML_pres: output << "style=filled, color=yellow"; break;
+ case TypeConstant::XML_parTrans: output << "color=grey"; break;
+ case TypeConstant::XML_sibTrans: output << " "; break;
+ }
+
+ output << "];" << std::endl;
+#endif
+
+ // does currpoint have any text set?
+ if(!point.msTextBody->msText.isEmpty())
+ {
+#ifdef DEBUG_OOX_DIAGRAM
+ static sal_Int32 nCount=0;
+ output << "\t"
+ << "textNode" << nCount
+ << " ["
+ << "label=\""
+ << OUStringToOString(
+ point.msTextBody->msText,
+ RTL_TEXTENCODING_UTF8).getStr()
+ << "\"" << "];" << std::endl;
+ output << "\t"
+ << normalizeDotName(point.msModelId).getStr()
+ << " -> "
+ << "textNode" << nCount++
+ << ";" << std::endl;
+#endif
+ }
+
+ const bool bInserted1 = getPointNameMap().insert(
+ std::make_pair(point.msModelId,&point)).second;
+
+ SAL_WARN_IF(!bInserted1, "oox.drawingml", "DiagramData::build(): non-unique point model id");
+
+ if( !point.msPresentationLayoutName.isEmpty() )
+ {
+ DiagramData::PointsNameMap::value_type::second_type& rVec=
+ getPointsPresNameMap()[point.msPresentationLayoutName];
+ rVec.push_back(&point);
+ }
+ }
+
+ const svx::diagram::Connections& rConnections = getConnections();
+ for (auto const& connection : rConnections)
+ {
+#ifdef DEBUG_OOX_DIAGRAM
+ if( !connection.msParTransId.isEmpty() ||
+ !connection.msSibTransId.isEmpty() )
+ {
+ if( !connection.msSourceId.isEmpty() ||
+ !connection.msDestId.isEmpty() )
+ {
+ output << "\t"
+ << normalizeDotName(connection.msSourceId).getStr()
+ << " -> "
+ << normalizeDotName(connection.msParTransId).getStr()
+ << " -> "
+ << normalizeDotName(connection.msSibTransId).getStr()
+ << " -> "
+ << normalizeDotName(connection.msDestId).getStr()
+ << " [style=dotted,"
+ << ((connection.mnXMLType == TypeConstant::XML_presOf) ? " color=red, " : ((connection.mnXMLType == TypeConstant::XML_presParOf) ? " color=green, " : " "))
+ << "label=\""
+ << OUStringToOString(connection.msModelId,
+ RTL_TEXTENCODING_UTF8 ).getStr()
+ << "\"];" << std::endl;
+ }
+ else
+ {
+ output << "\t"
+ << normalizeDotName(connection.msParTransId).getStr()
+ << " -> "
+ << normalizeDotName(connection.msSibTransId).getStr()
+ << " ["
+ << ((connection.mnXMLType == TypeConstant::XML_presOf) ? " color=red, " : ((connection.mnXMLType == TypeConstant::XML_presParOf) ? " color=green, " : " "))
+ << "label=\""
+ << OUStringToOString(connection.msModelId,
+ RTL_TEXTENCODING_UTF8 ).getStr()
+ << "\"];" << std::endl;
+ }
+ }
+ else if( !connection.msSourceId.isEmpty() ||
+ !connection.msDestId.isEmpty() )
+ output << "\t"
+ << normalizeDotName(connection.msSourceId).getStr()
+ << " -> "
+ << normalizeDotName(connection.msDestId).getStr()
+ << " [label=\""
+ << OUStringToOString(connection.msModelId,
+ RTL_TEXTENCODING_UTF8 ).getStr()
+ << ((connection.mnXMLType == TypeConstant::XML_presOf) ? "\", color=red]" : ((connection.mnXMLType == TypeConstant::XML_presParOf) ? "\", color=green]" : "\"]"))
+ << ";" << std::endl;
+#endif
+
+ const bool bInserted1 = maConnectionNameMap.insert(
+ std::make_pair(connection.msModelId,&connection)).second;
+
+ SAL_WARN_IF(!bInserted1, "oox.drawingml", "DiagramData::build(): non-unique connection model id");
+
+ if( connection.mnXMLType == TypeConstant::XML_presOf )
+ {
+ DiagramData::StringMap::value_type::second_type& rVec = getPresOfNameMap()[connection.msDestId];
+ rVec[connection.mnDestOrder] = { connection.msSourceId, sal_Int32(0) };
+ }
+ }
+
+ // assign outline levels
+ DiagramData::StringMap& rStringMap = getPresOfNameMap();
+ for (auto & elemPresOf : rStringMap)
+ {
+ for (auto & elem : elemPresOf.second)
+ {
+ const sal_Int32 nDepth = calcDepth(elem.second.msSourceId, getConnections());
+ elem.second.mnDepth = nDepth != 0 ? nDepth : -1;
+ }
+ }
+#ifdef DEBUG_OOX_DIAGRAM
+ output << "}" << std::endl;
+#endif
+}
+
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/dialog/AccessibilityCheckDialog.cxx b/svx/source/dialog/AccessibilityCheckDialog.cxx
new file mode 100644
index 000000000..8fb0c43ce
--- /dev/null
+++ b/svx/source/dialog/AccessibilityCheckDialog.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/.
+ *
+ */
+
+#include <svx/AccessibilityCheckDialog.hxx>
+
+#include <sfx2/AccessibilityIssue.hxx>
+#include <vcl/svapp.hxx>
+
+namespace svx
+{
+AccessibilityCheckEntry::AccessibilityCheckEntry(
+ weld::Container* pParent, std::shared_ptr<sfx::AccessibilityIssue> const& rAccessibilityIssue)
+ : m_xBuilder(Application::CreateBuilder(pParent, "svx/ui/accessibilitycheckentry.ui"))
+ , m_xContainer(m_xBuilder->weld_container("accessibilityCheckEntryBox"))
+ , m_xLabel(m_xBuilder->weld_label("accessibilityCheckEntryLabel"))
+ , m_xGotoButton(m_xBuilder->weld_button("accessibilityCheckEntryGotoButton"))
+ , m_pAccessibilityIssue(rAccessibilityIssue)
+{
+ m_xLabel->set_label(m_pAccessibilityIssue->m_aIssueText);
+ m_xGotoButton->set_visible(m_pAccessibilityIssue->canGotoIssue());
+ m_xGotoButton->connect_clicked(LINK(this, AccessibilityCheckEntry, GotoButtonClicked));
+ m_xContainer->show();
+}
+
+IMPL_LINK_NOARG(AccessibilityCheckEntry, GotoButtonClicked, weld::Button&, void)
+{
+ m_pAccessibilityIssue->gotoIssue();
+}
+
+AccessibilityCheckDialog::AccessibilityCheckDialog(
+ weld::Window* pParent, sfx::AccessibilityIssueCollection const& rIssueCollection)
+ : GenericDialogController(pParent, "svx/ui/accessibilitycheckdialog.ui",
+ "AccessibilityCheckDialog")
+ , m_aIssueCollection(rIssueCollection)
+ , m_xAccessibilityCheckBox(m_xBuilder->weld_box("accessibilityCheckBox"))
+{
+ sal_Int32 i = 0;
+
+ for (std::shared_ptr<sfx::AccessibilityIssue> const& pIssue : m_aIssueCollection.getIssues())
+ {
+ auto xEntry
+ = std::make_unique<AccessibilityCheckEntry>(m_xAccessibilityCheckBox.get(), pIssue);
+ m_xAccessibilityCheckBox->reorder_child(xEntry->get_widget(), i++);
+ m_aAccessibilityCheckEntries.push_back(std::move(xEntry));
+ }
+}
+
+AccessibilityCheckDialog::~AccessibilityCheckDialog() {}
+
+} // end svx namespace
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/dialog/ClassificationCommon.cxx b/svx/source/dialog/ClassificationCommon.cxx
new file mode 100644
index 000000000..885dff644
--- /dev/null
+++ b/svx/source/dialog/ClassificationCommon.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/.
+ *
+ */
+
+#include <svx/ClassificationCommon.hxx>
+#include <svx/ClassificationField.hxx>
+#include <rtl/ustrbuf.hxx>
+
+#include <com/sun/star/beans/PropertyAttribute.hpp>
+#include <com/sun/star/beans/XPropertySet.hpp>
+#include <com/sun/star/beans/XPropertyContainer.hpp>
+
+using namespace css;
+
+namespace svx::classification
+{
+OUString convertClassificationResultToString(std::vector<svx::ClassificationResult> const& rResults)
+{
+ OUStringBuffer sRepresentation;
+
+ for (svx::ClassificationResult const& rResult : rResults)
+ {
+ switch (rResult.meType)
+ {
+ case svx::ClassificationType::CATEGORY:
+ case svx::ClassificationType::INTELLECTUAL_PROPERTY_PART:
+ case svx::ClassificationType::MARKING:
+ case svx::ClassificationType::TEXT:
+ sRepresentation.append(rResult.msName);
+ break;
+
+ case svx::ClassificationType::PARAGRAPH:
+ sRepresentation.append(" ");
+ break;
+ }
+ }
+ return sRepresentation.makeStringAndClear();
+}
+
+OUString getProperty(uno::Reference<beans::XPropertyContainer> const& rxPropertyContainer,
+ OUString const& rName)
+{
+ try
+ {
+ uno::Reference<beans::XPropertySet> xPropertySet(rxPropertyContainer, uno::UNO_QUERY);
+ return xPropertySet->getPropertyValue(rName).get<OUString>();
+ }
+ catch (const css::uno::Exception&)
+ {
+ }
+
+ return OUString();
+}
+
+bool containsProperty(uno::Sequence<beans::Property> const& rProperties, std::u16string_view rName)
+{
+ return std::any_of(rProperties.begin(), rProperties.end(),
+ [&](const beans::Property& rProperty) { return rProperty.Name == rName; });
+}
+
+void removeAllProperties(uno::Reference<beans::XPropertyContainer> const& rxPropertyContainer)
+{
+ uno::Reference<beans::XPropertySet> xPropertySet(rxPropertyContainer, uno::UNO_QUERY);
+ const uno::Sequence<beans::Property> aProperties
+ = xPropertySet->getPropertySetInfo()->getProperties();
+
+ for (const beans::Property& rProperty : aProperties)
+ {
+ rxPropertyContainer->removeProperty(rProperty.Name);
+ }
+}
+
+bool addOrInsertDocumentProperty(
+ uno::Reference<beans::XPropertyContainer> const& rxPropertyContainer, OUString const& rsKey,
+ OUString const& rsValue)
+{
+ uno::Reference<beans::XPropertySet> xPropertySet(rxPropertyContainer, uno::UNO_QUERY);
+
+ try
+ {
+ if (containsProperty(xPropertySet->getPropertySetInfo()->getProperties(), rsKey))
+ xPropertySet->setPropertyValue(rsKey, uno::Any(rsValue));
+ else
+ rxPropertyContainer->addProperty(rsKey, beans::PropertyAttribute::REMOVABLE,
+ uno::Any(rsValue));
+ }
+ catch (const uno::Exception& /*rException*/)
+ {
+ return false;
+ }
+ return true;
+}
+
+void insertFullTextualRepresentationAsDocumentProperty(
+ uno::Reference<beans::XPropertyContainer> const& rxPropertyContainer,
+ sfx::ClassificationKeyCreator const& rKeyCreator,
+ std::vector<svx::ClassificationResult> const& rResults)
+{
+ OUString sString = convertClassificationResultToString(rResults);
+ addOrInsertDocumentProperty(rxPropertyContainer, rKeyCreator.makeFullTextualRepresentationKey(),
+ sString);
+}
+
+void insertCreationOrigin(uno::Reference<beans::XPropertyContainer> const& rxPropertyContainer,
+ sfx::ClassificationKeyCreator const& rKeyCreator,
+ sfx::ClassificationCreationOrigin eOrigin)
+{
+ // Nothing to do if origin is "NONE"
+ if (eOrigin == sfx::ClassificationCreationOrigin::NONE)
+ return;
+
+ OUString sValue = (eOrigin == sfx::ClassificationCreationOrigin::BAF_POLICY)
+ ? OUString("BAF_POLICY")
+ : OUString("MANUAL");
+ addOrInsertDocumentProperty(rxPropertyContainer, rKeyCreator.makeCreationOriginKey(), sValue);
+}
+} // end svx::classification namespace
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/dialog/ClassificationDialog.cxx b/svx/source/dialog/ClassificationDialog.cxx
new file mode 100644
index 000000000..f0dc743f6
--- /dev/null
+++ b/svx/source/dialog/ClassificationDialog.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/.
+ *
+ */
+
+#include <svx/ClassificationDialog.hxx>
+#include <svx/ClassificationCommon.hxx>
+
+#include <editeng/flditem.hxx>
+#include <editeng/eeitem.hxx>
+#include <editeng/section.hxx>
+#include <editeng/editobj.hxx>
+#include <editeng/wghtitem.hxx>
+#include <svl/itemset.hxx>
+#include <osl/file.hxx>
+#include <rtl/bootstrap.hxx>
+#include <config_folders.h>
+#include <tools/stream.hxx>
+#include <tools/XmlWriter.hxx>
+#include <tools/XmlWalker.hxx>
+#include <vcl/customweld.hxx>
+#include <vcl/event.hxx>
+#include <vcl/svapp.hxx>
+#include <sfx2/objsh.hxx>
+
+#include <officecfg/Office/Common.hxx>
+
+#include "ClassificationEditView.hxx"
+
+namespace svx {
+
+IMPL_STATIC_LINK(ClassificationDialog, KeyInput, const KeyEvent&, rKeyEvent, bool)
+{
+ bool bTextIsFreeForm = officecfg::Office::Common::Classification::IntellectualPropertyTextInputIsFreeForm::get();
+
+ if (!bTextIsFreeForm)
+ {
+ // Ignore key combination with modifier keys
+ if (rKeyEvent.GetKeyCode().IsMod3()
+ || rKeyEvent.GetKeyCode().IsMod2()
+ || rKeyEvent.GetKeyCode().IsMod1())
+ {
+ return true;
+ }
+
+ switch (rKeyEvent.GetKeyCode().GetCode())
+ {
+ // Allowed characters
+ case KEY_BACKSPACE:
+ case KEY_DELETE:
+ case KEY_DIVIDE:
+ case KEY_SEMICOLON:
+ case KEY_SPACE:
+ return false;
+ // Anything else is ignored
+ default:
+ return true;
+ }
+ }
+
+ return false;
+}
+
+namespace {
+
+constexpr size_t RECENTLY_USED_LIMIT = 5;
+
+constexpr OUStringLiteral constRecentlyUsedFileName(u"recentlyUsed.xml");
+
+OUString lcl_getClassificationUserPath()
+{
+ OUString sPath("${$BRAND_BASE_DIR/" LIBO_ETC_FOLDER "/" SAL_CONFIGFILE("bootstrap") ":UserInstallation}/user/classification/");
+ rtl::Bootstrap::expandMacros(sPath);
+ return sPath;
+}
+
+const SvxFieldItem* findField(editeng::Section const & rSection)
+{
+ for (SfxPoolItem const * pPool : rSection.maAttributes)
+ {
+ if (pPool->Which() == EE_FEATURE_FIELD)
+ return static_cast<const SvxFieldItem*>(pPool);
+ }
+ return nullptr;
+}
+
+bool fileExists(OUString const & sFilename)
+{
+ osl::File aFile(sFilename);
+ osl::FileBase::RC eRC = aFile.open(osl_File_OpenFlag_Read);
+ return osl::FileBase::E_None == eRC;
+}
+
+bool stringToClassificationType(std::string_view rsType, svx::ClassificationType & reType)
+{
+ if (rsType == "CATEGORY")
+ reType = svx::ClassificationType::CATEGORY;
+ else if (rsType == "INTELLECTUAL_PROPERTY_PART")
+ reType = svx::ClassificationType::INTELLECTUAL_PROPERTY_PART;
+ else if (rsType == "MARKING")
+ reType = svx::ClassificationType::MARKING;
+ else if (rsType == "PARAGRAPH")
+ reType = svx::ClassificationType::PARAGRAPH;
+ else if (rsType == "TEXT")
+ reType = svx::ClassificationType::TEXT;
+ else
+ return false;
+ return true;
+}
+
+OUString classificationTypeToString(svx::ClassificationType const & reType)
+{
+ switch(reType)
+ {
+ case svx::ClassificationType::CATEGORY:
+ return "CATEGORY"; break;
+ case svx::ClassificationType::MARKING:
+ return "MARKING"; break;
+ case svx::ClassificationType::TEXT:
+ return "TEXT"; break;
+ case svx::ClassificationType::INTELLECTUAL_PROPERTY_PART:
+ return "INTELLECTUAL_PROPERTY_PART"; break;
+ case svx::ClassificationType::PARAGRAPH:
+ return "PARAGRAPH"; break;
+ }
+ return OUString();
+}
+
+void writeResultToXml(tools::XmlWriter & rXmlWriter,
+ std::vector<ClassificationResult> const & rResultCollection)
+{
+ for (ClassificationResult const & rResult : rResultCollection)
+ {
+ rXmlWriter.startElement("element");
+ OUString sType = classificationTypeToString(rResult.meType);
+ rXmlWriter.attribute("type", sType);
+ rXmlWriter.startElement("string");
+ rXmlWriter.content(rResult.msName);
+ rXmlWriter.endElement();
+ rXmlWriter.startElement("abbreviatedString");
+ rXmlWriter.content(rResult.msAbbreviatedName);
+ rXmlWriter.endElement();
+ rXmlWriter.startElement("identifier");
+ rXmlWriter.content(rResult.msIdentifier);
+ rXmlWriter.endElement();
+ rXmlWriter.endElement();
+ }
+}
+
+} // end anonymous namespace
+
+ClassificationDialog::ClassificationDialog(weld::Window* pParent,const css::uno::Reference<css::document::XDocumentProperties>& rDocProps,
+ const bool bPerParagraph, const std::function<void()>& rParagraphSignHandler)
+ : GenericDialogController(pParent, "svx/ui/classificationdialog.ui", "AdvancedDocumentClassificationDialog")
+ , maHelper(rDocProps)
+ , maInternationalHelper(rDocProps, /*bUseLocalizedPolicy*/ false)
+ , m_bPerParagraph(bPerParagraph)
+ , m_aParagraphSignHandler(rParagraphSignHandler)
+ , m_nCurrentSelectedCategory(-1)
+ , m_xOkButton(m_xBuilder->weld_button("ok"))
+ , m_xSignButton(m_xBuilder->weld_button("signButton"))
+ , m_xToolBox(m_xBuilder->weld_toggle_button("toolbox"))
+ , m_xRecentlyUsedListBox(m_xBuilder->weld_combo_box("recentlyUsedCB"))
+ , m_xClassificationListBox(m_xBuilder->weld_combo_box("classificationCB"))
+ , m_xInternationalClassificationListBox(m_xBuilder->weld_combo_box("internationalClassificationCB"))
+ , m_xMarkingLabel(m_xBuilder->weld_label("markingLabel"))
+ , m_xMarkingListBox(m_xBuilder->weld_tree_view("markingLB"))
+ , m_xIntellectualPropertyPartListBox(m_xBuilder->weld_tree_view("intellectualPropertyPartLB"))
+ , m_xIntellectualPropertyPartNumberListBox(m_xBuilder->weld_tree_view("intellectualPropertyPartNumberLB"))
+ , m_xIntellectualPropertyPartAddButton(m_xBuilder->weld_button("intellectualPropertyPartAddButton"))
+ , m_xIntellectualPropertyPartEdit(m_xBuilder->weld_entry("intellectualPropertyPartEntry"))
+ , m_xIntellectualPropertyExpander(m_xBuilder->weld_expander("intellectualPropertyExpander"))
+ , m_xEditWindow(new ClassificationEditView)
+ , m_xEditWindowWeld(new weld::CustomWeld(*m_xBuilder, "classificationEditWindow", *m_xEditWindow))
+{
+ m_xOkButton->connect_clicked(LINK(this, ClassificationDialog, OkHdl));
+ m_xSignButton->connect_clicked(LINK(this, ClassificationDialog, ButtonClicked));
+ m_xSignButton->set_visible(m_bPerParagraph);
+
+ m_xIntellectualPropertyPartEdit->connect_key_press(LINK(this, ClassificationDialog, KeyInput));
+
+ // no need for BOLD if we do paragraph classification
+ if (m_bPerParagraph)
+ {
+ m_xToolBox->hide();
+ }
+ else
+ {
+ m_xToolBox->connect_toggled(LINK(this, ClassificationDialog, SelectToolboxHdl));
+ }
+
+ m_xIntellectualPropertyPartAddButton->connect_clicked(LINK(this, ClassificationDialog, ButtonClicked));
+
+ m_xClassificationListBox->set_size_request(m_xClassificationListBox->get_approximate_digit_width() * 20, -1);
+ m_xClassificationListBox->connect_changed(LINK(this, ClassificationDialog, SelectClassificationHdl));
+ for (const OUString& rName : maHelper.GetBACNames())
+ m_xClassificationListBox->append_text(rName);
+
+ m_xInternationalClassificationListBox->set_size_request(m_xInternationalClassificationListBox->get_approximate_digit_width() * 20, -1);
+ m_xInternationalClassificationListBox->connect_changed(LINK(this, ClassificationDialog, SelectClassificationHdl));
+ for (const OUString& rName : maInternationalHelper.GetBACNames())
+ m_xInternationalClassificationListBox->append_text(rName);
+
+ if (!maHelper.GetMarkings().empty())
+ {
+ m_xMarkingListBox->set_size_request(m_xMarkingListBox->get_approximate_digit_width() * 10,
+ m_xMarkingListBox->get_height_rows(4));
+ m_xMarkingListBox->connect_row_activated(LINK(this, ClassificationDialog, SelectMarkingHdl));
+
+ for (const OUString& rName : maHelper.GetMarkings())
+ m_xMarkingListBox->append_text(rName);
+ }
+ else
+ {
+ m_xMarkingListBox->hide();
+ m_xMarkingLabel->hide();
+ }
+
+ m_xIntellectualPropertyPartNumberListBox->set_size_request(m_xIntellectualPropertyPartNumberListBox->get_approximate_digit_width() * 10,
+ m_xIntellectualPropertyPartNumberListBox->get_height_rows(5));
+ m_xIntellectualPropertyPartNumberListBox->connect_row_activated(LINK(this, ClassificationDialog, SelectIPPartNumbersHdl));
+ for (const OUString& rName : maHelper.GetIntellectualPropertyPartNumbers())
+ m_xIntellectualPropertyPartNumberListBox->append_text(rName);
+
+ m_xIntellectualPropertyPartNumberListBox->set_size_request(m_xIntellectualPropertyPartNumberListBox->get_approximate_digit_width() * 20,
+ m_xIntellectualPropertyPartListBox->get_height_rows(5));
+ m_xIntellectualPropertyPartListBox->connect_row_activated(LINK(this, ClassificationDialog, SelectIPPartHdl));
+ for (const OUString& rName : maHelper.GetIntellectualPropertyParts())
+ m_xIntellectualPropertyPartListBox->append_text(rName);
+
+ m_xRecentlyUsedListBox->set_size_request(m_xRecentlyUsedListBox->get_approximate_digit_width() * 5, -1);
+ m_xRecentlyUsedListBox->connect_changed(LINK(this, ClassificationDialog, SelectRecentlyUsedHdl));
+
+ m_xIntellectualPropertyExpander->connect_expanded(LINK(this, ClassificationDialog, ExpandedHdl));
+ if (officecfg::Office::Common::Classification::IntellectualPropertySectionExpanded::get())
+ m_nAsyncExpandEvent = Application::PostUserEvent(LINK(this, ClassificationDialog, OnAsyncExpandHdl));
+ else
+ m_nAsyncExpandEvent = nullptr;
+
+ m_xEditWindow->SetModifyHdl(LINK(this, ClassificationDialog, EditWindowModifiedHdl));
+
+ readRecentlyUsed();
+ toggleWidgetsDependingOnCategory();
+
+ int nNumber = 1;
+ if (m_aRecentlyUsedValuesCollection.empty())
+ {
+ m_xRecentlyUsedListBox->set_sensitive(false);
+ }
+ else
+ {
+ for (std::vector<ClassificationResult> const & rResults : m_aRecentlyUsedValuesCollection)
+ {
+ OUString rContentRepresentation = svx::classification::convertClassificationResultToString(rResults);
+ OUString rDescription = OUString::number(nNumber) + ": " + rContentRepresentation;
+ nNumber++;
+
+ m_xRecentlyUsedListBox->append_text(rDescription);
+ }
+ }
+}
+
+//do it async so gtk has a chance to shrink it to best size, otherwise its larger than min
+IMPL_LINK_NOARG(ClassificationDialog, OnAsyncExpandHdl, void*, void)
+{
+ m_nAsyncExpandEvent = nullptr;
+ m_xIntellectualPropertyExpander->set_expanded(true);
+}
+
+ClassificationDialog::~ClassificationDialog()
+{
+ if (m_nAsyncExpandEvent)
+ Application::RemoveUserEvent(m_nAsyncExpandEvent);
+}
+
+void ClassificationDialog::insertCategoryField(sal_Int32 nID)
+{
+ const OUString aFullString = maHelper.GetBACNames()[nID];
+ const OUString aAbbreviatedString = maHelper.GetAbbreviatedBACNames()[nID];
+ const OUString aIdentifierString = maHelper.GetBACIdentifiers()[nID];
+ insertField(ClassificationType::CATEGORY, aAbbreviatedString, aFullString, aIdentifierString);
+}
+
+void ClassificationDialog::insertField(ClassificationType eType, OUString const & rString, OUString const & rFullString, OUString const & rIdentifier)
+{
+ ClassificationField aField(eType, rString, rFullString, rIdentifier);
+ m_xEditWindow->InsertField(SvxFieldItem(aField, EE_FEATURE_FIELD));
+}
+
+void ClassificationDialog::setupValues(std::vector<ClassificationResult> && rInput)
+{
+ m_aInitialValues = std::move(rInput);
+ readIn(m_aInitialValues);
+}
+
+void ClassificationDialog::readRecentlyUsed()
+{
+ OUString sPath = lcl_getClassificationUserPath();
+ OUString sFilePath(sPath + constRecentlyUsedFileName);
+
+ if (!fileExists(sFilePath))
+ return;
+
+ SvFileStream aFileStream(sFilePath, StreamMode::READ);
+ tools::XmlWalker aWalker;
+ if (!aWalker.open(&aFileStream))
+ return;
+
+ if (aWalker.name() != "recentlyUsedClassifications")
+ return;
+
+ aWalker.children();
+ while (aWalker.isValid())
+ {
+ if (aWalker.name() == "elementGroup")
+ {
+ std::vector<ClassificationResult> aResults;
+
+ aWalker.children();
+
+ while (aWalker.isValid())
+ {
+ if (aWalker.name() == "element")
+ {
+ svx::ClassificationType eType = svx::ClassificationType::TEXT;
+ OUString sString;
+ OUString sAbbreviatedString;
+ OUString sIdentifier;
+
+ // Convert string to classification type, but continue only if
+ // conversion was successful.
+ if (stringToClassificationType(aWalker.attribute("type"), eType))
+ {
+ aWalker.children();
+
+ while (aWalker.isValid())
+ {
+ if (aWalker.name() == "string")
+ {
+ sString = OStringToOUString(aWalker.content(), RTL_TEXTENCODING_UTF8);
+ }
+ else if (aWalker.name() == "abbreviatedString")
+ {
+ sAbbreviatedString = OStringToOUString(aWalker.content(), RTL_TEXTENCODING_UTF8);
+ }
+ else if (aWalker.name() == "identifier")
+ {
+ sIdentifier = OStringToOUString(aWalker.content(), RTL_TEXTENCODING_UTF8);
+ }
+ aWalker.next();
+ }
+ aWalker.parent();
+
+ aResults.push_back({ eType, sString, sAbbreviatedString, sIdentifier });
+ }
+ }
+ aWalker.next();
+ }
+ aWalker.parent();
+ m_aRecentlyUsedValuesCollection.push_back(aResults);
+ }
+ aWalker.next();
+ }
+ aWalker.parent();
+}
+
+void ClassificationDialog::writeRecentlyUsed()
+{
+ OUString sPath = lcl_getClassificationUserPath();
+ osl::Directory::createPath(sPath);
+ OUString sFilePath(sPath + constRecentlyUsedFileName);
+
+ std::unique_ptr<SvStream> pStream;
+ pStream.reset(new SvFileStream(sFilePath, StreamMode::STD_READWRITE | StreamMode::TRUNC));
+
+ tools::XmlWriter aXmlWriter(pStream.get());
+
+ if (!aXmlWriter.startDocument())
+ return;
+
+ aXmlWriter.startElement("recentlyUsedClassifications");
+
+ aXmlWriter.startElement("elementGroup");
+
+ writeResultToXml(aXmlWriter, getResult());
+
+ aXmlWriter.endElement();
+
+ if (m_aRecentlyUsedValuesCollection.size() >= RECENTLY_USED_LIMIT)
+ m_aRecentlyUsedValuesCollection.pop_back();
+
+ for (std::vector<ClassificationResult> const & rResultCollection : m_aRecentlyUsedValuesCollection)
+ {
+ aXmlWriter.startElement("elementGroup");
+
+ writeResultToXml(aXmlWriter, rResultCollection);
+
+ aXmlWriter.endElement();
+ }
+
+ aXmlWriter.endElement();
+
+ aXmlWriter.endDocument();
+}
+
+void ClassificationDialog::readIn(std::vector<ClassificationResult> const & rInput)
+{
+ sal_Int32 nParagraph = -1;
+
+ for (ClassificationResult const & rClassificationResult : rInput)
+ {
+
+ switch (rClassificationResult.meType)
+ {
+ case svx::ClassificationType::TEXT:
+ {
+ m_xEditWindow->getEditView().InsertText(rClassificationResult.msName);
+ }
+ break;
+
+ case svx::ClassificationType::CATEGORY:
+ {
+ OUString sName;
+ if (rClassificationResult.msName.isEmpty())
+ sName = maHelper.GetBACNameForIdentifier(rClassificationResult.msIdentifier);
+ else
+ sName = rClassificationResult.msName;
+
+ OUString sAbbreviatedName = rClassificationResult.msAbbreviatedName;
+ if (sAbbreviatedName.isEmpty())
+ sAbbreviatedName = maHelper.GetAbbreviatedBACName(sName);
+
+ m_xClassificationListBox->set_active_text(sName);
+ m_nCurrentSelectedCategory = m_xClassificationListBox->get_active();
+ m_xInternationalClassificationListBox->set_active(m_xClassificationListBox->get_active());
+
+ insertField(rClassificationResult.meType, sAbbreviatedName, sName, rClassificationResult.msIdentifier);
+ }
+ break;
+
+ case svx::ClassificationType::MARKING:
+ {
+ m_xMarkingListBox->select_text(rClassificationResult.msName);
+ insertField(rClassificationResult.meType, rClassificationResult.msName, rClassificationResult.msName, rClassificationResult.msIdentifier);
+ }
+ break;
+
+ case svx::ClassificationType::INTELLECTUAL_PROPERTY_PART:
+ {
+ insertField(rClassificationResult.meType, rClassificationResult.msName, rClassificationResult.msName, rClassificationResult.msIdentifier);
+ }
+ break;
+
+ case svx::ClassificationType::PARAGRAPH:
+ {
+ nParagraph++;
+
+ if (nParagraph != 0)
+ m_xEditWindow->getEditView().InsertParaBreak();
+
+ // Set paragraph font weight
+ FontWeight eWeight = (rClassificationResult.msName == "BOLD") ? WEIGHT_BOLD : WEIGHT_NORMAL;
+
+ ClassificationEditEngine& rEdEngine = m_xEditWindow->getEditEngine();
+ SfxItemSet aSet(rEdEngine.GetParaAttribs(nParagraph));
+ aSet.Put(SvxWeightItem(eWeight, EE_CHAR_WEIGHT));
+ rEdEngine.SetParaAttribs(nParagraph, aSet);
+ }
+ break;
+
+ default:
+ break;
+ }
+ }
+ toggleWidgetsDependingOnCategory();
+}
+
+void ClassificationDialog::toggleWidgetsDependingOnCategory()
+{
+ const EditEngine& rEditEngine = m_xEditWindow->getEditEngine();
+
+ for (sal_Int32 nParagraph = 0; nParagraph < rEditEngine.GetParagraphCount(); ++nParagraph)
+ {
+ sal_uInt16 nFieldCount = rEditEngine.GetFieldCount(nParagraph);
+ for (sal_uInt16 nField = 0; nField < nFieldCount; ++nField)
+ {
+ EFieldInfo aFieldInfo = rEditEngine.GetFieldInfo(nParagraph, nField);
+ if (aFieldInfo.pFieldItem)
+ {
+ const ClassificationField* pClassificationField = dynamic_cast<const ClassificationField*>(aFieldInfo.pFieldItem->GetField());
+ if (pClassificationField && pClassificationField->meType == ClassificationType::CATEGORY)
+ {
+ m_xOkButton->set_sensitive(true);
+ return;
+ }
+ }
+ }
+ }
+
+ // Category field in the text edit has been deleted, so reset the list boxes
+ m_xOkButton->set_sensitive(false);
+ m_xClassificationListBox->set_active(-1);
+ m_xInternationalClassificationListBox->set_active(-1);
+}
+
+std::vector<ClassificationResult> ClassificationDialog::getResult()
+{
+ std::vector<ClassificationResult> aClassificationResults;
+
+ ClassificationEditEngine& rEdEngine = m_xEditWindow->getEditEngine();
+ std::unique_ptr<EditTextObject> pEditText(rEdEngine.CreateTextObject());
+
+ sal_Int32 nCurrentParagraph = -1;
+
+ std::vector<editeng::Section> aSections;
+ pEditText->GetAllSections(aSections);
+ for (editeng::Section const & rSection : aSections)
+ {
+ while (nCurrentParagraph < rSection.mnParagraph)
+ {
+ nCurrentParagraph++;
+
+ // Get Weight of current paragraph
+ FontWeight eFontWeight = WEIGHT_NORMAL;
+ SfxItemSet aItemSet(rEdEngine.GetParaAttribs(nCurrentParagraph));
+ if (const SfxPoolItem* pItem = aItemSet.GetItem(EE_CHAR_WEIGHT, false))
+ {
+ const SvxWeightItem* pWeightItem = dynamic_cast<const SvxWeightItem*>(pItem);
+ if (pWeightItem && pWeightItem->GetWeight() == WEIGHT_BOLD)
+ eFontWeight = WEIGHT_BOLD;
+ }
+ // Font weight to string
+ OUString sWeightProperty = "NORMAL";
+ if (eFontWeight == WEIGHT_BOLD)
+ sWeightProperty = "BOLD";
+ // Insert into collection
+ OUString sBlank;
+ aClassificationResults.push_back({ ClassificationType::PARAGRAPH, sWeightProperty, sBlank, sBlank });
+ }
+
+ const SvxFieldItem* pFieldItem = findField(rSection);
+
+ ESelection aSelection(rSection.mnParagraph, rSection.mnStart, rSection.mnParagraph, rSection.mnEnd);
+ const OUString sDisplayString = rEdEngine.GetText(aSelection);
+ if (!sDisplayString.isEmpty())
+ {
+ const ClassificationField* pClassificationField = pFieldItem ? dynamic_cast<const ClassificationField*>(pFieldItem->GetField()) : nullptr;
+
+ if (pClassificationField)
+ {
+ aClassificationResults.push_back({ pClassificationField->meType, pClassificationField->msFullClassName,
+ pClassificationField->msDescription, pClassificationField->msIdentifier });
+ }
+ else
+ {
+ aClassificationResults.push_back({ ClassificationType::TEXT, sDisplayString, sDisplayString, OUString() });
+ }
+ }
+ }
+
+ return aClassificationResults;
+}
+
+IMPL_LINK(ClassificationDialog, SelectClassificationHdl, weld::ComboBox&, rBox, void)
+{
+ const sal_Int32 nSelected = rBox.get_active();
+ if (nSelected < 0 || m_nCurrentSelectedCategory == nSelected)
+ return;
+
+ std::unique_ptr<EditTextObject> pEditText(m_xEditWindow->getEditEngine().CreateTextObject());
+ std::vector<editeng::Section> aSections;
+ pEditText->GetAllSections(aSections);
+
+ // if we are replacing an existing field
+ bool bReplaceExisting = false;
+ // selection of the existing field, which will be replaced
+ ESelection aExistingFieldSelection;
+
+ for (editeng::Section const & rSection : aSections)
+ {
+ const SvxFieldItem* pFieldItem = findField(rSection);
+ if (pFieldItem)
+ {
+ const ClassificationField* pClassificationField = dynamic_cast<const ClassificationField*>(pFieldItem->GetField());
+ if (pClassificationField && pClassificationField->meType == ClassificationType::CATEGORY)
+ {
+ aExistingFieldSelection = ESelection(rSection.mnParagraph, rSection.mnStart,
+ rSection.mnParagraph, rSection.mnEnd);
+ bReplaceExisting = true;
+ }
+ }
+ }
+
+ if (bReplaceExisting)
+ m_xEditWindow->getEditView().SetSelection(aExistingFieldSelection);
+
+ insertCategoryField(nSelected);
+
+ // Change category to the new selection
+ m_xInternationalClassificationListBox->set_active(nSelected);
+ m_xClassificationListBox->set_active(nSelected);
+ m_nCurrentSelectedCategory = nSelected;
+}
+
+IMPL_LINK(ClassificationDialog, SelectMarkingHdl, weld::TreeView&, rBox, bool)
+{
+ sal_Int32 nSelected = rBox.get_selected_index();
+ if (nSelected >= 0)
+ {
+ const OUString aString = maHelper.GetMarkings()[nSelected];
+ insertField(ClassificationType::MARKING, aString, aString);
+ }
+ return true;
+}
+
+IMPL_LINK(ClassificationDialog, SelectIPPartNumbersHdl, weld::TreeView&, rBox, bool)
+{
+ sal_Int32 nSelected = rBox.get_selected_index();
+ if (nSelected >= 0)
+ {
+ OUString sString = maHelper.GetIntellectualPropertyPartNumbers()[nSelected];
+ m_xIntellectualPropertyPartEdit->replace_selection(sString);
+ m_xIntellectualPropertyPartEdit->grab_focus();
+ }
+ return true;
+}
+
+IMPL_LINK(ClassificationDialog, SelectRecentlyUsedHdl, weld::ComboBox&, rBox, void)
+{
+ sal_Int32 nSelected = rBox.get_active();
+ if (nSelected >= 0)
+ {
+ m_xEditWindow->getEditEngine().Clear();
+ readIn(m_aRecentlyUsedValuesCollection[nSelected]);
+ }
+}
+
+IMPL_LINK(ClassificationDialog, SelectIPPartHdl, weld::TreeView&, rBox, bool)
+{
+ const sal_Int32 nSelected = rBox.get_selected_index();
+ if (nSelected >= 0)
+ {
+ const OUString sString = maHelper.GetIntellectualPropertyParts()[nSelected];
+ m_xIntellectualPropertyPartEdit->replace_selection(sString);
+ m_xIntellectualPropertyPartEdit->grab_focus();
+ }
+ return true;
+}
+
+IMPL_LINK(ClassificationDialog, ButtonClicked, weld::Button&, rButton, void)
+{
+ if (&rButton == m_xSignButton.get())
+ {
+ m_aParagraphSignHandler();
+ }
+ else if (&rButton == m_xIntellectualPropertyPartAddButton.get())
+ {
+ const OUString sString = m_xIntellectualPropertyPartEdit->get_text();
+ insertField(ClassificationType::INTELLECTUAL_PROPERTY_PART, sString, sString);
+ }
+}
+
+IMPL_LINK_NOARG(ClassificationDialog, OkHdl, weld::Button&, void)
+{
+ writeRecentlyUsed();
+ m_xDialog->response(RET_OK);
+}
+
+IMPL_LINK_NOARG(ClassificationDialog, SelectToolboxHdl, weld::Toggleable&, void)
+{
+ m_xEditWindow->InvertSelectionWeight();
+}
+
+IMPL_LINK_NOARG(ClassificationDialog, EditWindowModifiedHdl, LinkParamNone*, void)
+{
+ toggleWidgetsDependingOnCategory();
+}
+
+IMPL_STATIC_LINK(ClassificationDialog, ExpandedHdl, weld::Expander&, rExpander, void)
+{
+ std::shared_ptr<comphelper::ConfigurationChanges> aConfigurationChanges(comphelper::ConfigurationChanges::create());
+ officecfg::Office::Common::Classification::IntellectualPropertySectionExpanded::set(rExpander.get_expanded(), aConfigurationChanges);
+ aConfigurationChanges->commit();
+}
+
+} // end svx
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/dialog/ClassificationEditView.cxx b/svx/source/dialog/ClassificationEditView.cxx
new file mode 100644
index 000000000..0b6ef3705
--- /dev/null
+++ b/svx/source/dialog/ClassificationEditView.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 <svx/ClassificationField.hxx>
+
+#include <svl/itempool.hxx>
+#include <svl/itemset.hxx>
+#include <editeng/wghtitem.hxx>
+#include <editeng/eeitem.hxx>
+
+#include "ClassificationEditView.hxx"
+
+namespace svx {
+
+ClassificationEditEngine::ClassificationEditEngine(SfxItemPool* pItemPool)
+ : EditEngine(pItemPool)
+{}
+
+OUString ClassificationEditEngine::CalcFieldValue(const SvxFieldItem& rField, sal_Int32 /*nPara*/,
+ sal_Int32 /*nPos*/, std::optional<Color>& /*rTxtColor*/, std::optional<Color>& /*rFldColor*/)
+{
+ OUString aString;
+ const ClassificationField* pClassificationField = dynamic_cast<const ClassificationField*>(rField.GetField());
+ if (pClassificationField)
+ aString = pClassificationField->msDescription;
+ else
+ aString = "Unknown";
+ return aString;
+}
+
+ClassificationEditView::ClassificationEditView()
+{
+}
+
+void ClassificationEditView::makeEditEngine()
+{
+ m_xEditEngine.reset(new ClassificationEditEngine(EditEngine::CreatePool().get()));
+}
+
+ClassificationEditView::~ClassificationEditView()
+{
+}
+
+void ClassificationEditView::InsertField(const SvxFieldItem& rFieldItem)
+{
+ m_xEditView->InsertField(rFieldItem);
+ m_xEditView->Invalidate();
+}
+
+void ClassificationEditView::InvertSelectionWeight()
+{
+ ESelection aSelection = m_xEditView->GetSelection();
+
+ for (sal_Int32 nParagraph = aSelection.nStartPara; nParagraph <= aSelection.nEndPara; ++nParagraph)
+ {
+ FontWeight eFontWeight = WEIGHT_BOLD;
+
+ SfxItemSet aSet(m_xEditEngine->GetParaAttribs(nParagraph));
+ if (const SfxPoolItem* pItem = aSet.GetItem(EE_CHAR_WEIGHT, false))
+ {
+ const SvxWeightItem* pWeightItem = dynamic_cast<const SvxWeightItem*>(pItem);
+ if (pWeightItem && pWeightItem->GetWeight() == WEIGHT_BOLD)
+ eFontWeight = WEIGHT_NORMAL;
+ }
+ SvxWeightItem aWeight(eFontWeight, EE_CHAR_WEIGHT);
+ aSet.Put(aWeight);
+ m_xEditEngine->SetParaAttribs(nParagraph, aSet);
+ }
+
+ m_xEditView->Invalidate();
+}
+
+} // end sfx2
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/dialog/ClassificationEditView.hxx b/svx/source/dialog/ClassificationEditView.hxx
new file mode 100644
index 000000000..b65e95399
--- /dev/null
+++ b/svx/source/dialog/ClassificationEditView.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/.
+ *
+ */
+
+#ifndef INCLUDED_SVX_CLASSIFICATIONEDITVIEW_HXX
+#define INCLUDED_SVX_CLASSIFICATIONEDITVIEW_HXX
+
+#include <sal/config.h>
+#include <svx/weldeditview.hxx>
+
+namespace svx {
+
+class ClassificationEditEngine final : public EditEngine
+{
+public:
+ ClassificationEditEngine(SfxItemPool* pItemPool);
+
+ virtual OUString CalcFieldValue(const SvxFieldItem& rField, sal_Int32 nPara, sal_Int32 nPos, std::optional<Color>& rTxtColor, std::optional<Color>& rFldColor) override;
+};
+
+class ClassificationEditView final : public WeldEditView
+{
+public:
+ ClassificationEditView();
+ virtual ~ClassificationEditView() override;
+
+ virtual void makeEditEngine() override;
+
+ void InsertField(const SvxFieldItem& rField);
+
+ void InvertSelectionWeight();
+
+ ClassificationEditEngine& getEditEngine()
+ {
+ return *static_cast<ClassificationEditEngine*>(m_xEditEngine.get());
+ }
+
+ EditView& getEditView()
+ {
+ return *m_xEditView;
+ }
+};
+
+} // end svx namespace
+
+#endif // INCLUDED_SVX_CLASSIFICATIONEDITVIEW_HXX
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/dialog/FileExportedDialog.cxx b/svx/source/dialog/FileExportedDialog.cxx
new file mode 100644
index 000000000..e352a0621
--- /dev/null
+++ b/svx/source/dialog/FileExportedDialog.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/.
+ */
+
+#include <svx/FileExportedDialog.hxx>
+
+#include <tools/diagnose_ex.h>
+#include <comphelper/backupfilehelper.hxx>
+#include <comphelper/processfactory.hxx>
+#include <com/sun/star/system/XSystemShellExecute.hpp>
+#include <com/sun/star/system/SystemShellExecuteFlags.hpp>
+#include <com/sun/star/system/SystemShellExecute.hpp>
+
+FileExportedDialog::FileExportedDialog(weld::Window* pParent, OUString atitle)
+ : GenericDialogController(pParent, "svx/ui/fileexporteddialog.ui", "FileExportedDialog")
+ , m_xFileLabel(m_xBuilder->weld_label("Filelabel"))
+ , m_xButton(m_xBuilder->weld_button("ok"))
+{
+ m_xFileLabel->set_label(atitle);
+ m_xButton->connect_clicked(LINK(this, FileExportedDialog, OpenHdl));
+}
+
+IMPL_LINK_NOARG(FileExportedDialog, OpenHdl, weld::Button&, void)
+{
+ const OUString uri(comphelper::BackupFileHelper::getUserProfileURL());
+ css::uno::Reference<css::system::XSystemShellExecute> exec(
+ css::system::SystemShellExecute::create(comphelper::getProcessComponentContext()));
+ try
+ {
+ exec->execute(uri, OUString(), css::system::SystemShellExecuteFlags::URIS_ONLY);
+ }
+ catch (const css::uno::Exception&)
+ {
+ TOOLS_WARN_EXCEPTION("svx.dialog", "opening <" << uri << "> failed:");
+ }
+ m_xDialog->response(RET_OK);
+} \ No newline at end of file
diff --git a/svx/source/dialog/GenericCheckDialog.cxx b/svx/source/dialog/GenericCheckDialog.cxx
new file mode 100644
index 000000000..09fc3d678
--- /dev/null
+++ b/svx/source/dialog/GenericCheckDialog.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/.
+ *
+ */
+
+#include <svx/GenericCheckDialog.hxx>
+#include <vcl/svapp.hxx>
+
+namespace svx
+{
+GenericCheckEntry::GenericCheckEntry(weld::Container* pParent,
+ std::unique_ptr<CheckData>& pCheckData)
+ : m_xBuilder(Application::CreateBuilder(pParent, "svx/ui/genericcheckentry.ui"))
+ , m_xContainer(m_xBuilder->weld_container("checkEntryBox"))
+ , m_xLabel(m_xBuilder->weld_label("label"))
+ , m_xMarkButton(m_xBuilder->weld_button("markButton"))
+ , m_xPropertiesButton(m_xBuilder->weld_button("propertiesButton"))
+ , m_pCheckData(pCheckData)
+{
+ m_xLabel->set_label(m_pCheckData->getText());
+ m_xMarkButton->set_visible(m_pCheckData->canMarkObject());
+ m_xMarkButton->connect_clicked(LINK(this, GenericCheckEntry, MarkButtonClicked));
+ m_xPropertiesButton->set_visible(m_pCheckData->hasProperties());
+ m_xPropertiesButton->connect_clicked(LINK(this, GenericCheckEntry, PropertiesButtonClicked));
+
+ m_xContainer->show();
+}
+
+IMPL_LINK_NOARG(GenericCheckEntry, MarkButtonClicked, weld::Button&, void)
+{
+ m_pCheckData->markObject();
+}
+
+IMPL_LINK_NOARG(GenericCheckEntry, PropertiesButtonClicked, weld::Button&, void)
+{
+ m_pCheckData->runProperties();
+}
+
+GenericCheckDialog::GenericCheckDialog(weld::Window* pParent,
+ CheckDataCollection& rCheckDataCollection)
+ : GenericDialogController(pParent, "svx/ui/genericcheckdialog.ui", "GenericCheckDialog")
+ , m_rCheckDataCollection(rCheckDataCollection)
+ , m_xCheckBox(m_xBuilder->weld_box("checkBox"))
+{
+ set_title(m_rCheckDataCollection.getTitle());
+}
+
+GenericCheckDialog::~GenericCheckDialog() {}
+
+short GenericCheckDialog::run()
+{
+ sal_Int32 i = 0;
+
+ for (std::unique_ptr<CheckData>& pCheckData : m_rCheckDataCollection.getCollection())
+ {
+ auto xEntry = std::make_unique<GenericCheckEntry>(m_xCheckBox.get(), pCheckData);
+ m_xCheckBox->reorder_child(xEntry->get_widget(), i++);
+ m_aCheckEntries.push_back(std::move(xEntry));
+ }
+ return GenericDialogController::run();
+}
+
+} // end svx namespace
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/dialog/SafeModeDialog.cxx b/svx/source/dialog/SafeModeDialog.cxx
new file mode 100644
index 000000000..beb530030
--- /dev/null
+++ b/svx/source/dialog/SafeModeDialog.cxx
@@ -0,0 +1,307 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+#include "SafeModeDialog.hxx"
+
+#include <osl/file.hxx>
+#include <sfx2/safemode.hxx>
+#include <vcl/svapp.hxx>
+#include <vcl/weld.hxx>
+#include <comphelper/processfactory.hxx>
+#include <unotools/ZipPackageHelper.hxx>
+#include <unotools/configmgr.hxx>
+#include <svx/dialmgr.hxx>
+#include <svx/strings.hrc>
+#include <svx/FileExportedDialog.hxx>
+
+#include <com/sun/star/task/OfficeRestartManager.hpp>
+#include <com/sun/star/task/XInteractionHandler.hpp>
+
+using namespace css;
+
+SafeModeDialog::SafeModeDialog(weld::Window* pParent)
+ : GenericDialogController(pParent, "svx/ui/safemodedialog.ui", "SafeModeDialog")
+ , mxBtnContinue(m_xBuilder->weld_button("btn_continue"))
+ , mxBtnRestart(m_xBuilder->weld_button("btn_restart"))
+ , mxBtnApply(m_xBuilder->weld_button("btn_apply"))
+ , mxBoxRestore(m_xBuilder->weld_container("group_restore"))
+ , mxBoxConfigure(m_xBuilder->weld_container("group_configure"))
+ , mxBoxDeinstall(m_xBuilder->weld_container("group_deinstall"))
+ , mxBoxReset(m_xBuilder->weld_container("group_reset"))
+ , mxRadioRestore(m_xBuilder->weld_radio_button("radio_restore"))
+ , mxRadioConfigure(m_xBuilder->weld_radio_button("radio_configure"))
+ , mxRadioExtensions(m_xBuilder->weld_radio_button("radio_extensions"))
+ , mxRadioReset(m_xBuilder->weld_radio_button("radio_reset"))
+ , mxCBCheckProfilesafeConfig(m_xBuilder->weld_check_button("check_profilesafe_config"))
+ , mxCBCheckProfilesafeExtensions(m_xBuilder->weld_check_button("check_profilesafe_extensions"))
+ , mxCBDisableAllExtensions(m_xBuilder->weld_check_button("check_disable_all_extensions"))
+ , mxCBDeinstallUserExtensions(m_xBuilder->weld_check_button("check_deinstall_user_extensions"))
+ , mxCBResetSharedExtensions(m_xBuilder->weld_check_button("check_reset_shared_extensions"))
+ , mxCBResetBundledExtensions(m_xBuilder->weld_check_button("check_reset_bundled_extensions"))
+ , mxCBDisableHWAcceleration(m_xBuilder->weld_check_button("check_disable_hw_acceleration"))
+ , mxCBResetCustomizations(m_xBuilder->weld_check_button("check_reset_customizations"))
+ , mxCBResetWholeUserProfile(m_xBuilder->weld_check_button("check_reset_whole_userprofile"))
+ , mxBugLink(m_xBuilder->weld_link_button("linkbutton_bugs"))
+ , mxUserProfileLink(m_xBuilder->weld_link_button("linkbutton_profile"))
+ , mxBtnCreateZip(m_xBuilder->weld_button("btn_create_zip"))
+ , mxExpander(m_xBuilder->weld_expander("expander"))
+{
+ m_xDialog->set_centered_on_parent(false);
+ mxRadioRestore->connect_toggled(LINK(this, SafeModeDialog, RadioBtnHdl));
+ mxRadioConfigure->connect_toggled(LINK(this, SafeModeDialog, RadioBtnHdl));
+ mxRadioExtensions->connect_toggled(LINK(this, SafeModeDialog, RadioBtnHdl));
+ mxRadioReset->connect_toggled(LINK(this, SafeModeDialog, RadioBtnHdl));
+
+ mxBtnContinue->connect_clicked(LINK(this, SafeModeDialog, DialogBtnHdl));
+ mxBtnRestart->connect_clicked(LINK(this, SafeModeDialog, DialogBtnHdl));
+ mxBtnApply->connect_clicked(LINK(this, SafeModeDialog, DialogBtnHdl));
+
+ mxCBCheckProfilesafeConfig->connect_toggled(LINK(this, SafeModeDialog, CheckBoxHdl));
+ mxCBCheckProfilesafeExtensions->connect_toggled(LINK(this, SafeModeDialog, CheckBoxHdl));
+ mxCBDisableAllExtensions->connect_toggled(LINK(this, SafeModeDialog, CheckBoxHdl));
+ mxCBDeinstallUserExtensions->connect_toggled(LINK(this, SafeModeDialog, CheckBoxHdl));
+ mxCBResetSharedExtensions->connect_toggled(LINK(this, SafeModeDialog, CheckBoxHdl));
+ mxCBResetBundledExtensions->connect_toggled(LINK(this, SafeModeDialog, CheckBoxHdl));
+ mxCBDisableHWAcceleration->connect_toggled(LINK(this, SafeModeDialog, CheckBoxHdl));
+ mxCBResetCustomizations->connect_toggled(LINK(this, SafeModeDialog, CheckBoxHdl));
+ mxCBResetWholeUserProfile->connect_toggled(LINK(this, SafeModeDialog, CheckBoxHdl));
+
+ mxBtnCreateZip->connect_clicked(LINK(this, SafeModeDialog, CreateZipBtnHdl));
+
+ // Disable restart btn until some checkbox is active
+ mxBtnApply->set_sensitive(false);
+
+ // Check the first radio button and call its handler,
+ // it'll disable the relevant parts
+ mxRadioRestore->set_active(true);
+ RadioBtnHdl(*mxRadioRestore);
+
+ // Set URL for help button (module=safemode)
+ OUString sURL("http://hub.libreoffice.org/send-feedback/?LOversion=" + utl::ConfigManager::getAboutBoxProductVersion() +
+ "&LOlocale=" + utl::ConfigManager::getUILocale() + "&LOmodule=safemode");
+ mxBugLink->set_uri(sURL);
+
+ mxUserProfileLink->set_uri(comphelper::BackupFileHelper::getUserProfileURL());
+}
+
+SafeModeDialog::~SafeModeDialog()
+{
+}
+
+void SafeModeDialog::enableDisableWidgets()
+{
+ mxCBCheckProfilesafeConfig->set_sensitive(maBackupFileHelper.isPopPossible());
+ mxCBCheckProfilesafeExtensions->set_sensitive(maBackupFileHelper.isPopPossibleExtensionInfo());
+ mxCBDisableAllExtensions->set_sensitive(comphelper::BackupFileHelper::isTryDisableAllExtensionsPossible());
+ mxCBDeinstallUserExtensions->set_sensitive(comphelper::BackupFileHelper::isTryDeinstallUserExtensionsPossible());
+ mxCBResetSharedExtensions->set_sensitive(comphelper::BackupFileHelper::isTryResetSharedExtensionsPossible());
+ mxCBResetBundledExtensions->set_sensitive(comphelper::BackupFileHelper::isTryResetBundledExtensionsPossible());
+ mxCBResetCustomizations->set_sensitive(comphelper::BackupFileHelper::isTryResetCustomizationsPossible());
+
+ // no disable of mxCBResetWholeUserProfile, always possible (as last choice)
+}
+
+short SafeModeDialog::run()
+{
+ short nRet = weld::GenericDialogController::run();
+ // Remove the safe mode flag before exiting this dialog
+ sfx2::SafeMode::removeFlag();
+ return nRet;
+}
+
+void SafeModeDialog::applyChanges()
+{
+ // Restore
+ if (mxRadioRestore->get_active())
+ {
+ if (mxCBCheckProfilesafeConfig->get_active())
+ {
+ // reset UserConfiguration to last known working state
+ // ProfileSafeMode/BackupFileHelper
+ maBackupFileHelper.tryPop();
+ }
+
+ if (mxCBCheckProfilesafeExtensions->get_active())
+ {
+ // reset State of installed Extensions to last known working state
+ // ProfileSafeMode/BackupFileHelper
+ maBackupFileHelper.tryPopExtensionInfo();
+ }
+ }
+
+ // Configure
+ if (mxRadioConfigure->get_active())
+ {
+ if (mxCBDisableAllExtensions->get_active())
+ {
+ // Disable all extensions
+ comphelper::BackupFileHelper::tryDisableAllExtensions();
+ }
+
+ if (mxCBDisableHWAcceleration->get_active())
+ {
+ comphelper::BackupFileHelper::tryDisableHWAcceleration();
+ }
+ }
+
+ // Deinstall
+ if (mxRadioExtensions->get_active())
+ {
+ if (mxCBDeinstallUserExtensions->get_active())
+ {
+ // Deinstall all User Extensions (installed for User only)
+ comphelper::BackupFileHelper::tryDeinstallUserExtensions();
+ }
+
+ if (mxCBResetSharedExtensions->get_active())
+ {
+ // Reset shared Extensions
+ comphelper::BackupFileHelper::tryResetSharedExtensions();
+ }
+ if (mxCBResetBundledExtensions->get_active())
+ {
+ // Reset bundled Extensions
+ comphelper::BackupFileHelper::tryResetBundledExtensions();
+ }
+ }
+
+ // Reset
+ if (mxRadioReset->get_active())
+ {
+ if (mxCBResetCustomizations->get_active())
+ {
+ // Reset customizations (Settings and UserInterface modifications)
+ comphelper::BackupFileHelper::tryResetCustomizations();
+ }
+
+ if (mxCBResetWholeUserProfile->get_active())
+ {
+ // Reset the whole UserProfile
+ comphelper::BackupFileHelper::tryResetUserProfile();
+ }
+ }
+
+ // finally, restart
+ css::task::OfficeRestartManager::get(comphelper::getProcessComponentContext())->requestRestart(
+ css::uno::Reference< css::task::XInteractionHandler >());
+}
+
+IMPL_LINK(SafeModeDialog, RadioBtnHdl, weld::Toggleable&, rButton, void)
+{
+ if (!rButton.get_active())
+ return;
+ if (mxRadioRestore->get_active())
+ {
+ // Enable the currently selected box
+ mxBoxRestore->set_sensitive(true);
+ // Make sure only possible choices are active
+ enableDisableWidgets();
+ // Disable the unselected boxes
+ mxBoxReset->set_sensitive(false);
+ mxBoxConfigure->set_sensitive(false);
+ mxBoxDeinstall->set_sensitive(false);
+ }
+ else if (mxRadioConfigure->get_active())
+ {
+ // Enable the currently selected box
+ mxBoxConfigure->set_sensitive(true);
+ // Make sure only possible choices are active
+ enableDisableWidgets();
+ // Disable the unselected boxes
+ mxBoxRestore->set_sensitive(false);
+ mxBoxReset->set_sensitive(false);
+ mxBoxDeinstall->set_sensitive(false);
+
+ }
+ else if (mxRadioExtensions->get_active())
+ {
+ // Enable the currently selected box
+ mxBoxDeinstall->set_sensitive(true);
+ // Make sure only possible choices are active
+ enableDisableWidgets();
+ // Disable the unselected boxes
+ mxBoxRestore->set_sensitive(false);
+ mxBoxConfigure->set_sensitive(false);
+ mxBoxReset->set_sensitive(false);
+ }
+ else if (mxRadioReset->get_active())
+ {
+ // Enable the currently selected box
+ mxBoxReset->set_sensitive(true);
+ // Make sure only possible choices are active
+ enableDisableWidgets();
+ // Disable the unselected boxes
+ mxBoxConfigure->set_sensitive(false);
+ mxBoxRestore->set_sensitive(false);
+ mxBoxDeinstall->set_sensitive(false);
+ }
+}
+
+IMPL_LINK(SafeModeDialog, DialogBtnHdl, weld::Button&, rBtn, void)
+{
+ if (&rBtn == mxBtnContinue.get())
+ {
+ m_xDialog->response(RET_CLOSE);
+ }
+ else if (&rBtn == mxBtnRestart.get())
+ {
+ sfx2::SafeMode::putRestartFlag();
+ m_xDialog->response(RET_CLOSE);
+ uno::Reference< uno::XComponentContext > xContext = comphelper::getProcessComponentContext();
+ css::task::OfficeRestartManager::get(xContext)->requestRestart(
+ css::uno::Reference< css::task::XInteractionHandler >());
+ }
+ else if (&rBtn == mxBtnApply.get())
+ {
+ sfx2::SafeMode::putRestartFlag();
+ m_xDialog->response(RET_CLOSE);
+ applyChanges();
+ }
+}
+
+IMPL_LINK(SafeModeDialog, CreateZipBtnHdl, weld::Button&, /*rBtn*/, void)
+{
+ const OUString zipFileURL(comphelper::BackupFileHelper::getUserProfileURL() + "/libreoffice-profile.zip");
+ osl::File::remove(zipFileURL); // Remove previous exports
+ try
+ {
+ utl::ZipPackageHelper aZipHelper(comphelper::getProcessComponentContext(), zipFileURL);
+ aZipHelper.addFolderWithContent(aZipHelper.getRootFolder(), comphelper::BackupFileHelper::getUserProfileWorkURL());
+ aZipHelper.savePackage();
+ }
+ catch (const uno::Exception &)
+ {
+ std::unique_ptr<weld::MessageDialog> xBox(Application::CreateMessageDialog(m_xDialog.get(),
+ VclMessageType::Warning, VclButtonsType::Ok,
+ SvxResId(RID_SVXSTR_SAFEMODE_ZIP_FAILURE)));
+ xBox->run();
+ return;
+ }
+
+ FileExportedDialog aDialog(m_xDialog.get(),"Your user profile has been exported as 'libreoffice-profile.zip'.");
+ aDialog.run();
+}
+
+IMPL_LINK(SafeModeDialog, CheckBoxHdl, weld::Toggleable&, /*pCheckBox*/, void)
+{
+ const bool bEnable(
+ mxCBCheckProfilesafeConfig->get_active() ||
+ mxCBCheckProfilesafeExtensions->get_active() ||
+ mxCBDisableAllExtensions->get_active() ||
+ mxCBDeinstallUserExtensions->get_active() ||
+ mxCBResetSharedExtensions->get_active() ||
+ mxCBResetBundledExtensions->get_active() ||
+ mxCBDisableHWAcceleration->get_active() ||
+ mxCBResetCustomizations->get_active() ||
+ mxCBResetWholeUserProfile->get_active());
+
+ mxBtnApply->set_sensitive(bEnable);
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/dialog/SafeModeDialog.hxx b/svx/source/dialog/SafeModeDialog.hxx
new file mode 100644
index 000000000..0f90c1e29
--- /dev/null
+++ b/svx/source/dialog/SafeModeDialog.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_SVX_SOURCE_DIALOG_SAFEMODEDIALOG_HXX
+#define INCLUDED_SVX_SOURCE_DIALOG_SAFEMODEDIALOG_HXX
+
+#include <comphelper/backupfilehelper.hxx>
+#include <vcl/weld.hxx>
+
+class SafeModeDialog : public weld::GenericDialogController
+{
+public:
+ explicit SafeModeDialog(weld::Window* pParent);
+ virtual short run() override;
+ virtual ~SafeModeDialog() override;
+
+private:
+ std::unique_ptr<weld::Button> mxBtnContinue;
+ std::unique_ptr<weld::Button> mxBtnRestart;
+ std::unique_ptr<weld::Button> mxBtnApply;
+
+ std::unique_ptr<weld::Container> mxBoxRestore;
+ std::unique_ptr<weld::Container> mxBoxConfigure;
+ std::unique_ptr<weld::Container> mxBoxDeinstall;
+ std::unique_ptr<weld::Container> mxBoxReset;
+
+ std::unique_ptr<weld::RadioButton> mxRadioRestore;
+ std::unique_ptr<weld::RadioButton> mxRadioConfigure;
+ std::unique_ptr<weld::RadioButton> mxRadioExtensions;
+ std::unique_ptr<weld::RadioButton> mxRadioReset;
+
+ std::unique_ptr<weld::CheckButton> mxCBCheckProfilesafeConfig;
+ std::unique_ptr<weld::CheckButton> mxCBCheckProfilesafeExtensions;
+ std::unique_ptr<weld::CheckButton> mxCBDisableAllExtensions;
+ std::unique_ptr<weld::CheckButton> mxCBDeinstallUserExtensions;
+ std::unique_ptr<weld::CheckButton> mxCBResetSharedExtensions;
+ std::unique_ptr<weld::CheckButton> mxCBResetBundledExtensions;
+ std::unique_ptr<weld::CheckButton> mxCBDisableHWAcceleration;
+ std::unique_ptr<weld::CheckButton> mxCBResetCustomizations;
+ std::unique_ptr<weld::CheckButton> mxCBResetWholeUserProfile;
+
+ std::unique_ptr<weld::LinkButton> mxBugLink;
+ std::unique_ptr<weld::LinkButton> mxUserProfileLink;
+ std::unique_ptr<weld::Button> mxBtnCreateZip;
+ std::unique_ptr<weld::Expander> mxExpander;
+
+ // local BackupFileHelper for handling possible restores
+ comphelper::BackupFileHelper maBackupFileHelper;
+
+ void enableDisableWidgets();
+ void applyChanges();
+
+ DECL_LINK(RadioBtnHdl, weld::Toggleable&, void);
+ DECL_LINK(CheckBoxHdl, weld::Toggleable&, void);
+ DECL_LINK(CreateZipBtnHdl, weld::Button&, void);
+ DECL_LINK(DialogBtnHdl, weld::Button&, void);
+};
+
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/dialog/SafeModeUI.cxx b/svx/source/dialog/SafeModeUI.cxx
new file mode 100644
index 000000000..4e32669f0
--- /dev/null
+++ b/svx/source/dialog/SafeModeUI.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/.
+ */
+
+#include <cppuhelper/implbase.hxx>
+#include <com/sun/star/frame/XSynchronousDispatch.hpp>
+#include <com/sun/star/lang/XServiceInfo.hpp>
+
+#include <cppuhelper/supportsservice.hxx>
+
+#include <vcl/svapp.hxx>
+
+#include "SafeModeDialog.hxx"
+
+namespace {
+
+class SafeModeUI : public ::cppu::WeakImplHelper< css::lang::XServiceInfo,
+ css::frame::XSynchronousDispatch > // => XDispatch!
+{
+public:
+ SafeModeUI();
+
+ // css.lang.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;
+
+
+ virtual css::uno::Any SAL_CALL dispatchWithReturnValue(const css::util::URL& aURL,
+ const css::uno::Sequence< css::beans::PropertyValue >& lArguments ) override;
+};
+
+SafeModeUI::SafeModeUI()
+{
+}
+
+OUString SAL_CALL SafeModeUI::getImplementationName()
+{
+ return "com.sun.star.comp.svx.SafeModeUI";
+}
+
+sal_Bool SAL_CALL SafeModeUI::supportsService(const OUString& sServiceName)
+{
+ return cppu::supportsService(this, sServiceName);
+}
+
+css::uno::Sequence< OUString > SAL_CALL SafeModeUI::getSupportedServiceNames()
+{
+ return { "com.sun.star.dialog.SafeModeUI" };
+}
+
+css::uno::Any SAL_CALL SafeModeUI::dispatchWithReturnValue(const css::util::URL&,
+ const css::uno::Sequence< css::beans::PropertyValue >& )
+{
+ SolarMutexGuard aGuard;
+ css::uno::Any aRet;
+ SafeModeDialog aDialog(Application::GetDefDialogParent());
+ aDialog.run();
+ return aRet;
+}
+
+}
+
+extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface *
+com_sun_star_comp_svx_SafeModeUI_get_implementation(
+ css::uno::XComponentContext * /*context*/,
+ css::uno::Sequence<css::uno::Any> const &)
+{
+ return cppu::acquire(new SafeModeUI);
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/dialog/SpellDialogChildWindow.cxx b/svx/source/dialog/SpellDialogChildWindow.cxx
new file mode 100644
index 000000000..ae82769df
--- /dev/null
+++ b/svx/source/dialog/SpellDialogChildWindow.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 <svx/SpellDialogChildWindow.hxx>
+
+#include <svx/svxdlg.hxx>
+#include <osl/diagnose.h>
+
+namespace svx {
+
+
+SpellDialogChildWindow::SpellDialogChildWindow (
+ vcl::Window* _pParent,
+ sal_uInt16 nId,
+ SfxBindings* pBindings)
+ : SfxChildWindow (_pParent, nId)
+{
+ SvxAbstractDialogFactory* pFact = SvxAbstractDialogFactory::Create();
+ m_xAbstractSpellDialog = pFact->CreateSvxSpellDialog(_pParent->GetFrameWeld(),
+ pBindings,
+ this );
+ SetController(m_xAbstractSpellDialog->GetController());
+ SetHideNotDelete(true);
+}
+
+SpellDialogChildWindow::~SpellDialogChildWindow()
+{
+ m_xAbstractSpellDialog.disposeAndClear();
+}
+
+SfxBindings& SpellDialogChildWindow::GetBindings() const
+{
+ assert(m_xAbstractSpellDialog);
+ return m_xAbstractSpellDialog->GetBindings();
+}
+
+void SpellDialogChildWindow::InvalidateSpellDialog()
+{
+ OSL_ASSERT (m_xAbstractSpellDialog);
+ if (m_xAbstractSpellDialog)
+ m_xAbstractSpellDialog->InvalidateDialog();
+}
+
+bool SpellDialogChildWindow::HasAutoCorrection()
+{
+ return false;
+}
+
+void SpellDialogChildWindow::AddAutoCorrection(
+ const OUString& /*rOld*/,
+ const OUString& /*rNew*/,
+ LanguageType /*eLanguage*/)
+{
+ OSL_FAIL("AutoCorrection should have been overridden - if available");
+}
+
+bool SpellDialogChildWindow::HasGrammarChecking()
+{
+ return false;
+}
+
+bool SpellDialogChildWindow::IsGrammarChecking()
+{
+ OSL_FAIL("Grammar checking should have been overridden - if available");
+ return false;
+}
+
+void SpellDialogChildWindow::SetGrammarChecking(bool )
+{
+ OSL_FAIL("Grammar checking should have been overridden - if available");
+}
+} // end of namespace ::svx
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/dialog/SvxNumOptionsTabPageHelper.cxx b/svx/source/dialog/SvxNumOptionsTabPageHelper.cxx
new file mode 100644
index 000000000..0236c8835
--- /dev/null
+++ b/svx/source/dialog/SvxNumOptionsTabPageHelper.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 <svx/SvxNumOptionsTabPageHelper.hxx>
+#include <com/sun/star/style/NumberingType.hpp>
+#include <com/sun/star/text/DefaultNumberingProvider.hpp>
+#include <com/sun/star/text/XNumberingTypeInfo.hpp>
+#include <comphelper/processfactory.hxx>
+
+using namespace css;
+using namespace css::uno;
+using namespace css::text;
+using namespace css::style;
+
+Reference<XDefaultNumberingProvider> SvxNumOptionsTabPageHelper::GetNumberingProvider()
+{
+ Reference<XComponentContext> xContext(::comphelper::getProcessComponentContext());
+ Reference<XDefaultNumberingProvider> xRet = text::DefaultNumberingProvider::create(xContext);
+ return xRet;
+}
+
+void SvxNumOptionsTabPageHelper::GetI18nNumbering(weld::ComboBox& rFmtLB, sal_uInt16 nDoNotRemove)
+{
+ Reference<XDefaultNumberingProvider> xDefNum = GetNumberingProvider();
+ Reference<XNumberingTypeInfo> xInfo(xDefNum, UNO_QUERY);
+
+ // Extended numbering schemes present in the resource but not offered by
+ // the i18n framework per configuration must be removed from the listbox.
+ // Do not remove a special entry matching nDoNotRemove.
+ const sal_uInt16 nDontRemove = SAL_MAX_UINT16;
+ ::std::vector<sal_uInt16> aRemove(rFmtLB.get_count(), nDontRemove);
+ for (size_t i = 0; i < aRemove.size(); ++i)
+ {
+ sal_uInt16 nEntryData = rFmtLB.get_id(i).toInt32();
+ if (nEntryData > NumberingType::CHARS_LOWER_LETTER_N && nEntryData != nDoNotRemove)
+ aRemove[i] = nEntryData;
+ }
+ if (xInfo.is())
+ {
+ const Sequence<sal_Int16> aTypes = xInfo->getSupportedNumberingTypes();
+ for (const sal_Int16 nCurrent : aTypes)
+ {
+ if (nCurrent > NumberingType::CHARS_LOWER_LETTER_N)
+ {
+ bool bInsert = true;
+ for (int nEntry = 0; nEntry < rFmtLB.get_count(); ++nEntry)
+ {
+ sal_uInt16 nEntryData = rFmtLB.get_id(nEntry).toInt32();
+ if (nEntryData == static_cast<sal_uInt16>(nCurrent))
+ {
+ bInsert = false;
+ aRemove[nEntry] = nDontRemove;
+ break;
+ }
+ }
+ if (bInsert)
+ {
+ OUString aIdent = xInfo->getNumberingIdentifier(nCurrent);
+ rFmtLB.append(OUString::number(nCurrent), aIdent);
+ }
+ }
+ }
+ }
+ for (unsigned short i : aRemove)
+ {
+ if (i == nDontRemove)
+ continue;
+ rFmtLB.remove_id(OUString::number(i));
+ }
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/dialog/_bmpmask.cxx b/svx/source/dialog/_bmpmask.cxx
new file mode 100644
index 000000000..2be9a7c8d
--- /dev/null
+++ b/svx/source/dialog/_bmpmask.cxx
@@ -0,0 +1,1057 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * 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/metaact.hxx>
+#include <vcl/virdev.hxx>
+#include <svtools/valueset.hxx>
+#include <svl/eitem.hxx>
+#include <sfx2/dispatch.hxx>
+#include <svtools/colrdlg.hxx>
+
+#include <svx/colorbox.hxx>
+#include <svx/dialmgr.hxx>
+#include <svx/bmpmask.hxx>
+#include <svx/strings.hrc>
+#include <svx/svxids.hrc>
+#include <memory>
+#include <helpids.h>
+
+#define OWN_CALLMODE SfxCallMode::ASYNCHRON | SfxCallMode::RECORD
+
+
+#define TEST_COLS() \
+{ \
+ nR = aCol.GetRed(); nG = aCol.GetGreen(); nB = aCol.GetBlue(); \
+ for( i = 0; i < nCount; i++ ) \
+ { \
+ if ( ( pMinR[i] <= nR ) && ( pMaxR[i] >= nR ) && \
+ ( pMinG[i] <= nG ) && ( pMaxG[i] >= nG ) && \
+ ( pMinB[i] <= nB ) && ( pMaxB[i] >= nB ) ) \
+ { \
+ aCol = pDstCols[i]; bReplace = true; break; \
+ } \
+ } \
+}
+
+SFX_IMPL_DOCKINGWINDOW_WITHID( SvxBmpMaskChildWindow, SID_BMPMASK )
+
+class BmpColorWindow : public weld::CustomWidgetController
+{
+ Color aColor;
+
+
+public:
+ explicit BmpColorWindow()
+ : aColor( COL_WHITE )
+ {
+ }
+
+ void SetColor( const Color& rColor )
+ {
+ aColor = rColor;
+ Invalidate();
+ }
+
+ virtual void Paint( vcl::RenderContext& rRenderContext, const tools::Rectangle& rRect ) override;
+
+ virtual void SetDrawingArea(weld::DrawingArea* pArea) override
+ {
+ Size aSize(pArea->get_ref_device().LogicToPixel(Size(43, 14), MapMode(MapUnit::MapAppFont)));
+ CustomWidgetController::SetDrawingArea(pArea);
+ pArea->set_size_request(aSize.Width(), aSize.Height());
+ SetOutputSizePixel(aSize);
+ }
+};
+
+class MaskSet : public ValueSet
+{
+ VclPtr<SvxBmpMask> pSvxBmpMask;
+
+public:
+ MaskSet(SvxBmpMask* pMask);
+ virtual void Select() override;
+ virtual bool KeyInput( const KeyEvent& rKEvt ) override;
+ virtual void GetFocus() override;
+ virtual void SetDrawingArea(weld::DrawingArea* pArea) override
+ {
+ Size aSize(pArea->get_ref_device().LogicToPixel(Size(24, 12), MapMode(MapUnit::MapAppFont)));
+ ValueSet::SetDrawingArea(pArea);
+ pArea->set_size_request(aSize.Width(), aSize.Height());
+ SetOutputSizePixel(aSize);
+ SetHelpId(HID_BMPMASK_CTL_QCOL_1);
+ }
+ void onEditColor();
+};
+
+MaskSet::MaskSet(SvxBmpMask* pMask)
+ : ValueSet(nullptr)
+ , pSvxBmpMask(pMask)
+{
+}
+
+void MaskSet::Select()
+{
+ ValueSet::Select();
+
+ pSvxBmpMask->onSelect( this );
+}
+
+void MaskSet::GetFocus()
+{
+ ValueSet::GetFocus();
+ SelectItem( 1 );
+ pSvxBmpMask->onSelect( this );
+}
+
+bool MaskSet::KeyInput( const KeyEvent& rKEvt )
+{
+ bool bRet = false;
+
+ vcl::KeyCode aCode = rKEvt.GetKeyCode();
+
+ // if the key has a modifier we don't care
+ if( aCode.GetModifier() )
+ {
+ bRet = ValueSet::KeyInput( rKEvt );
+ }
+ else
+ {
+ // check for keys that interests us
+ switch ( aCode.GetCode() )
+ {
+ case KEY_SPACE:
+ onEditColor();
+ bRet = true;
+ break;
+ default:
+ bRet = ValueSet::KeyInput( rKEvt );
+ }
+ }
+ return bRet;
+}
+
+void MaskSet::onEditColor()
+{
+ SvColorDialog aColorDlg;
+
+ aColorDlg.SetColor(GetItemColor(1));
+
+ if (aColorDlg.Execute(pSvxBmpMask->GetFrameWeld()))
+ SetItemColor(1, aColorDlg.GetColor());
+}
+
+class MaskData
+{
+ VclPtr<SvxBmpMask> pMask;
+ bool bIsReady;
+ bool bExecState;
+ SfxBindings& rBindings;
+
+public:
+ MaskData( SvxBmpMask* pBmpMask, SfxBindings& rBind );
+
+ bool IsCbxReady() const { return bIsReady; }
+ void SetExecState( bool bState ) { bExecState = bState; }
+ bool IsExecReady() const { return bExecState; }
+
+ DECL_LINK( PipetteHdl, const OString&, void );
+ DECL_LINK( CbxHdl, weld::Toggleable&, void);
+ DECL_LINK( CbxTransHdl, weld::Toggleable&, void );
+ DECL_LINK( FocusLbHdl, weld::Widget&, void );
+ DECL_LINK(ExecHdl, weld::Button&, void);
+};
+
+
+MaskData::MaskData( SvxBmpMask* pBmpMask, SfxBindings& rBind ) :
+
+ pMask ( pBmpMask ),
+ bIsReady ( false ),
+ bExecState ( false ),
+ rBindings ( rBind )
+
+{
+}
+
+IMPL_LINK( MaskData, PipetteHdl, const OString&, rId, void )
+{
+ SfxBoolItem aBItem( SID_BMPMASK_PIPETTE,
+ pMask->m_xTbxPipette->get_item_active(rId) );
+
+ rBindings.GetDispatcher()->ExecuteList(SID_BMPMASK_PIPETTE, OWN_CALLMODE,
+ { &aBItem });
+}
+
+IMPL_LINK( MaskData, CbxHdl, weld::Toggleable&, rCbx, void )
+{
+ bIsReady = pMask->m_xCbx1->get_active() || pMask->m_xCbx2->get_active() ||
+ pMask->m_xCbx3->get_active() || pMask->m_xCbx4->get_active();
+
+ if ( bIsReady && IsExecReady() )
+ pMask->m_xBtnExec->set_sensitive(true);
+ else
+ pMask->m_xBtnExec->set_sensitive(false);
+
+ // When a checkbox is checked, the pipette is enabled
+ if ( !rCbx.get_active() )
+ return;
+
+ MaskSet* pSet = nullptr;
+
+ if (&rCbx == pMask->m_xCbx1.get())
+ pSet = pMask->m_xQSet1.get();
+ else if (&rCbx == pMask->m_xCbx2.get())
+ pSet = pMask->m_xQSet2.get();
+ else if (&rCbx == pMask->m_xCbx3.get())
+ pSet = pMask->m_xQSet3.get();
+ else // if ( &rCbx == pMask->m_xCbx4 )
+ pSet = pMask->m_xQSet4.get();
+
+ pSet->SelectItem( 1 );
+ pSet->Select();
+
+ pMask->m_xTbxPipette->set_item_active("pipette", true);
+ PipetteHdl("pipette");
+}
+
+IMPL_LINK( MaskData, CbxTransHdl, weld::Toggleable&, rCbx, void )
+{
+ bIsReady = rCbx.get_active();
+ if ( bIsReady )
+ {
+ pMask->m_xQSet1->Disable();
+ pMask->m_xQSet2->Disable();
+ pMask->m_xQSet3->Disable();
+ pMask->m_xQSet4->Disable();
+ pMask->m_xCtlPipette->Disable();
+ pMask->m_xCbx1->set_sensitive(false);
+ pMask->m_xSp1->set_sensitive(false);
+ pMask->m_xCbx2->set_sensitive(false);
+ pMask->m_xSp2->set_sensitive(false);
+ pMask->m_xCbx3->set_sensitive(false);
+ pMask->m_xSp3->set_sensitive(false);
+ pMask->m_xCbx4->set_sensitive(false);
+ pMask->m_xSp4->set_sensitive(false);
+ pMask->m_xTbxPipette->set_sensitive(false);
+
+ pMask->m_xLbColor1->set_sensitive(false);
+ pMask->m_xLbColor2->set_sensitive(false);
+ pMask->m_xLbColor3->set_sensitive(false);
+ pMask->m_xLbColor4->set_sensitive(false);
+ pMask->m_xLbColorTrans->set_sensitive(true);
+ }
+ else
+ {
+ pMask->m_xQSet1->Enable();
+ pMask->m_xQSet2->Enable();
+ pMask->m_xQSet3->Enable();
+ pMask->m_xQSet4->Enable();
+ pMask->m_xCtlPipette->Enable();
+ pMask->m_xCbx1->set_sensitive(true);
+ pMask->m_xSp1->set_sensitive(true);
+ pMask->m_xCbx2->set_sensitive(true);
+ pMask->m_xSp2->set_sensitive(true);
+ pMask->m_xCbx3->set_sensitive(true);
+ pMask->m_xSp3->set_sensitive(true);
+ pMask->m_xCbx4->set_sensitive(true);
+ pMask->m_xSp4->set_sensitive(true);
+ pMask->m_xTbxPipette->set_sensitive(true);
+
+ pMask->m_xLbColor1->set_sensitive(true);
+ pMask->m_xLbColor2->set_sensitive(true);
+ pMask->m_xLbColor3->set_sensitive(true);
+ pMask->m_xLbColor4->set_sensitive(true);
+ pMask->m_xLbColorTrans->set_sensitive(false);
+
+ bIsReady = pMask->m_xCbx1->get_active() || pMask->m_xCbx2->get_active() ||
+ pMask->m_xCbx3->get_active() || pMask->m_xCbx4->get_active();
+ }
+
+ if ( bIsReady && IsExecReady() )
+ pMask->m_xBtnExec->set_sensitive(true);
+ else
+ pMask->m_xBtnExec->set_sensitive(false);
+}
+
+IMPL_LINK( MaskData, FocusLbHdl, weld::Widget&, rLb, void )
+{
+ pMask->m_xQSet1->SelectItem( &rLb == &pMask->m_xLbColor1->get_widget() ? 1 : 0 /* , false */ );
+ pMask->m_xQSet2->SelectItem( &rLb == &pMask->m_xLbColor2->get_widget() ? 1 : 0 /* , false */ );
+ pMask->m_xQSet3->SelectItem( &rLb == &pMask->m_xLbColor3->get_widget() ? 1 : 0 /* , false */ );
+ pMask->m_xQSet4->SelectItem( &rLb == &pMask->m_xLbColor4->get_widget() ? 1 : 0 /* , false */ );
+}
+
+IMPL_LINK_NOARG(MaskData, ExecHdl, weld::Button&, void)
+{
+ SfxBoolItem aBItem( SID_BMPMASK_EXEC, true );
+ rBindings.GetDispatcher()->ExecuteList(SID_BMPMASK_EXEC, OWN_CALLMODE,
+ { &aBItem });
+}
+
+void BmpColorWindow::Paint( vcl::RenderContext& rRenderContext, const tools::Rectangle& /*Rect*/)
+{
+ rRenderContext.Push(vcl::PushFlags::LINECOLOR | vcl::PushFlags::FILLCOLOR);
+ rRenderContext.SetLineColor(aColor);
+ rRenderContext.SetFillColor(aColor);
+ rRenderContext.DrawRect(tools::Rectangle(Point(), GetOutputSizePixel()));
+ rRenderContext.Pop();
+}
+
+SvxBmpMaskSelectItem::SvxBmpMaskSelectItem( SvxBmpMask& rMask,
+ SfxBindings& rBindings ) :
+ SfxControllerItem ( SID_BMPMASK_EXEC, rBindings ),
+ rBmpMask ( rMask)
+{
+}
+
+void SvxBmpMaskSelectItem::StateChangedAtToolBoxControl( sal_uInt16 nSID, SfxItemState /*eState*/,
+ const SfxPoolItem* pItem )
+{
+ if ( ( nSID == SID_BMPMASK_EXEC ) && pItem )
+ {
+ const SfxBoolItem* pStateItem = dynamic_cast<const SfxBoolItem*>( pItem );
+ assert(pStateItem); // SfxBoolItem expected
+ if (pStateItem)
+ rBmpMask.SetExecState( pStateItem->GetValue() );
+ }
+}
+
+SvxBmpMaskChildWindow::SvxBmpMaskChildWindow(vcl::Window* pParent_, sal_uInt16 nId,
+ SfxBindings* pBindings,
+ SfxChildWinInfo* pInfo)
+ : SfxChildWindow(pParent_, nId)
+{
+ VclPtr<SvxBmpMask> pDlg = VclPtr<SvxBmpMask>::Create(pBindings, this, pParent_);
+
+ SetWindow( pDlg );
+
+ pDlg->Initialize( pInfo );
+}
+
+SvxBmpMask::SvxBmpMask(SfxBindings *pBindinx, SfxChildWindow *pCW, vcl::Window* pParent)
+ : SfxDockingWindow(pBindinx, pCW, pParent, "DockingColorReplace",
+ "svx/ui/dockingcolorreplace.ui")
+ , m_xTbxPipette(m_xBuilder->weld_toolbar("toolbar"))
+ , m_xCtlPipette(new BmpColorWindow)
+ , m_xCtlPipetteWin(new weld::CustomWeld(*m_xBuilder, "toolcolor", *m_xCtlPipette))
+ , m_xBtnExec(m_xBuilder->weld_button("replace"))
+ , m_xCbx1(m_xBuilder->weld_check_button("cbx1"))
+ , m_xQSet1(new MaskSet(this))
+ , m_xQSetWin1(new weld::CustomWeld(*m_xBuilder, "qset1", *m_xQSet1))
+ , m_xSp1(m_xBuilder->weld_metric_spin_button("tol1", FieldUnit::PERCENT))
+ , m_xLbColor1(new ColorListBox(m_xBuilder->weld_menu_button("color1"), [this]{ return GetFrameWeld(); }))
+ , m_xCbx2(m_xBuilder->weld_check_button("cbx2"))
+ , m_xQSet2(new MaskSet(this))
+ , m_xQSetWin2(new weld::CustomWeld(*m_xBuilder, "qset2", *m_xQSet2))
+ , m_xSp2(m_xBuilder->weld_metric_spin_button("tol2", FieldUnit::PERCENT))
+ , m_xLbColor2(new ColorListBox(m_xBuilder->weld_menu_button("color2"), [this]{ return GetFrameWeld(); }))
+ , m_xCbx3(m_xBuilder->weld_check_button("cbx3"))
+ , m_xQSet3(new MaskSet(this))
+ , m_xQSetWin3(new weld::CustomWeld(*m_xBuilder, "qset3", *m_xQSet3))
+ , m_xSp3(m_xBuilder->weld_metric_spin_button("tol3", FieldUnit::PERCENT))
+ , m_xLbColor3(new ColorListBox(m_xBuilder->weld_menu_button("color3"), [this]{ return GetFrameWeld(); }))
+ , m_xCbx4(m_xBuilder->weld_check_button("cbx4"))
+ , m_xQSet4(new MaskSet(this))
+ , m_xQSetWin4(new weld::CustomWeld(*m_xBuilder, "qset4", *m_xQSet4))
+ , m_xSp4(m_xBuilder->weld_metric_spin_button("tol4", FieldUnit::PERCENT))
+ , m_xLbColor4(new ColorListBox(m_xBuilder->weld_menu_button("color4"), [this]{ return GetFrameWeld(); }))
+ , m_xCbxTrans(m_xBuilder->weld_check_button("cbx5"))
+ , m_xLbColorTrans(new ColorListBox(m_xBuilder->weld_menu_button("color5"), [this]{ return GetFrameWeld(); }))
+ , m_xData(new MaskData(this, *pBindinx))
+ , aPipetteColor(COL_WHITE)
+ , aSelItem(*this, *pBindinx)
+{
+ SetText(SvxResId(RID_SVXDLG_BMPMASK_STR_TITLE));
+
+ m_xLbColor1->SetSlotId(SID_BMPMASK_COLOR);
+ m_xLbColor2->SetSlotId(SID_BMPMASK_COLOR);
+ m_xLbColor3->SetSlotId(SID_BMPMASK_COLOR);
+ m_xLbColor4->SetSlotId(SID_BMPMASK_COLOR);
+
+ m_xLbColorTrans->SelectEntry(COL_BLACK);
+ m_xLbColor1->SelectEntry(COL_TRANSPARENT);
+ m_xLbColor2->SelectEntry(COL_TRANSPARENT);
+ m_xLbColor3->SelectEntry(COL_TRANSPARENT);
+ m_xLbColor4->SelectEntry(COL_TRANSPARENT);
+
+ m_xTbxPipette->connect_clicked( LINK( m_xData.get(), MaskData, PipetteHdl ) );
+ m_xBtnExec->connect_clicked( LINK( m_xData.get(), MaskData, ExecHdl ) );
+
+ m_xCbx1->connect_toggled( LINK( m_xData.get(), MaskData, CbxHdl ) );
+ m_xCbx2->connect_toggled( LINK( m_xData.get(), MaskData, CbxHdl ) );
+ m_xCbx3->connect_toggled( LINK( m_xData.get(), MaskData, CbxHdl ) );
+ m_xCbx4->connect_toggled( LINK( m_xData.get(), MaskData, CbxHdl ) );
+ m_xCbxTrans->connect_toggled( LINK( m_xData.get(), MaskData, CbxTransHdl ) );
+
+ SetAccessibleNames ();
+
+ m_xLbColor1->connect_focus_in( LINK( m_xData.get(), MaskData, FocusLbHdl ) );
+ m_xLbColor2->connect_focus_in( LINK( m_xData.get(), MaskData, FocusLbHdl ) );
+ m_xLbColor3->connect_focus_in( LINK( m_xData.get(), MaskData, FocusLbHdl ) );
+ m_xLbColor4->connect_focus_in( LINK( m_xData.get(), MaskData, FocusLbHdl ) );
+ m_xLbColorTrans->set_sensitive(false);
+
+ OUString sColorPalette (SvxResId( RID_SVXDLG_BMPMASK_STR_PALETTE));
+ OUString sColorPaletteN;
+
+ m_xQSet1->SetStyle( m_xQSet1->GetStyle() | WB_DOUBLEBORDER | WB_ITEMBORDER );
+ m_xQSet1->SetColCount();
+ m_xQSet1->SetLineCount( 1 );
+ sColorPaletteN = sColorPalette + " 1";
+ m_xQSet1->InsertItem( 1, aPipetteColor, sColorPaletteN);
+ m_xQSet1->SelectItem( 1 );
+
+ m_xQSet2->SetStyle( m_xQSet2->GetStyle() | WB_DOUBLEBORDER | WB_ITEMBORDER );
+ m_xQSet2->SetColCount();
+ m_xQSet2->SetLineCount( 1 );
+ sColorPaletteN = sColorPalette + " 2";
+ m_xQSet2->InsertItem( 1, aPipetteColor, sColorPaletteN);
+ m_xQSet2->SelectItem( 0 );
+
+ m_xQSet3->SetStyle( m_xQSet3->GetStyle() | WB_DOUBLEBORDER | WB_ITEMBORDER );
+ m_xQSet3->SetColCount();
+ m_xQSet3->SetLineCount( 1 );
+ sColorPaletteN = sColorPalette + " 3";
+ m_xQSet3->InsertItem( 1, aPipetteColor, sColorPaletteN);
+ m_xQSet3->SelectItem( 0 );
+
+ m_xQSet4->SetStyle( m_xQSet4->GetStyle() | WB_DOUBLEBORDER | WB_ITEMBORDER );
+ m_xQSet4->SetColCount();
+ m_xQSet4->SetLineCount( 1 );
+ sColorPaletteN = sColorPalette + " 4";
+ m_xQSet4->InsertItem( 1, aPipetteColor, sColorPaletteN);
+ m_xQSet4->SelectItem( 0 );
+
+ m_xQSet1->Show();
+ m_xQSet2->Show();
+ m_xQSet3->Show();
+ m_xQSet4->Show();
+}
+
+SvxBmpMask::~SvxBmpMask()
+{
+ disposeOnce();
+}
+
+void SvxBmpMask::dispose()
+{
+ m_xQSetWin1.reset();
+ m_xQSet1.reset();
+ m_xQSetWin2.reset();
+ m_xQSet2.reset();
+ m_xQSetWin3.reset();
+ m_xQSet3.reset();
+ m_xQSetWin4.reset();
+ m_xQSet4.reset();
+ m_xCtlPipetteWin.reset();
+ m_xCtlPipette.reset();
+ m_xData.reset();
+ m_xTbxPipette.reset();
+ m_xBtnExec.reset();
+ m_xCbx1.reset();
+ m_xSp1.reset();
+ m_xLbColor1.reset();
+ m_xCbx2.reset();
+ m_xSp2.reset();
+ m_xLbColor2.reset();
+ m_xCbx3.reset();
+ m_xSp3.reset();
+ m_xLbColor3.reset();
+ m_xCbx4.reset();
+ m_xSp4.reset();
+ m_xLbColor4.reset();
+ m_xCbxTrans.reset();
+ m_xLbColorTrans.reset();
+ aSelItem.dispose();
+ SfxDockingWindow::dispose();
+}
+
+/** is called by a MaskSet when it is selected */
+void SvxBmpMask::onSelect( const MaskSet* pSet )
+{
+ // now deselect all other value sets
+ if( pSet != m_xQSet1.get() )
+ m_xQSet1->SelectItem( 0 );
+
+ if( pSet != m_xQSet2.get() )
+ m_xQSet2->SelectItem( 0 );
+
+ if( pSet != m_xQSet3.get() )
+ m_xQSet3->SelectItem( 0 );
+
+ if( pSet != m_xQSet4.get() )
+ m_xQSet4->SelectItem( 0 );
+}
+
+bool SvxBmpMask::Close()
+{
+ SfxBoolItem aItem2( SID_BMPMASK_PIPETTE, false );
+ GetBindings().GetDispatcher()->ExecuteList(SID_BMPMASK_PIPETTE,
+ OWN_CALLMODE, { &aItem2 });
+
+ return SfxDockingWindow::Close();
+}
+
+void SvxBmpMask::SetColor( const Color& rColor )
+{
+ aPipetteColor = rColor;
+ m_xCtlPipette->SetColor( aPipetteColor );
+}
+
+void SvxBmpMask::PipetteClicked()
+{
+ if( m_xQSet1->GetSelectedItemId() == 1 )
+ {
+ m_xCbx1->set_active(true);
+ m_xData->CbxHdl(*m_xCbx1);
+ m_xQSet1->SetItemColor( 1, aPipetteColor );
+ m_xQSet1->SetFormat();
+ }
+ else if( m_xQSet2->GetSelectedItemId() == 1 )
+ {
+ m_xCbx2->set_active(true);
+ m_xData->CbxHdl(*m_xCbx2);
+ m_xQSet2->SetItemColor( 1, aPipetteColor );
+ m_xQSet2->SetFormat();
+ }
+ else if( m_xQSet3->GetSelectedItemId() == 1 )
+ {
+ m_xCbx3->set_active(true);
+ m_xData->CbxHdl(*m_xCbx3);
+ m_xQSet3->SetItemColor( 1, aPipetteColor );
+ m_xQSet3->SetFormat();
+ }
+ else if( m_xQSet4->GetSelectedItemId() == 1 )
+ {
+ m_xCbx4->set_active(true);
+ m_xData->CbxHdl(*m_xCbx4);
+ m_xQSet4->SetItemColor( 1, aPipetteColor );
+ m_xQSet4->SetFormat();
+ }
+
+ m_xTbxPipette->set_item_active("pipette", false);
+ m_xData->PipetteHdl("pipette");
+}
+
+void SvxBmpMask::SetExecState( bool bEnable )
+{
+ m_xData->SetExecState( bEnable );
+
+ if ( m_xData->IsExecReady() && m_xData->IsCbxReady() )
+ m_xBtnExec->set_sensitive(true);
+ else
+ m_xBtnExec->set_sensitive(false);
+}
+
+
+sal_uInt16 SvxBmpMask::InitColorArrays( Color* pSrcCols, Color* pDstCols, sal_uInt8* pTols )
+{
+ sal_uInt16 nCount = 0;
+
+ if ( m_xCbx1->get_active() )
+ {
+ pSrcCols[nCount] = m_xQSet1->GetItemColor( 1 );
+ pDstCols[nCount] = m_xLbColor1->GetSelectEntryColor();
+ pTols[nCount++] = static_cast<sal_uInt8>(m_xSp1->get_value(FieldUnit::PERCENT));
+ }
+
+ if ( m_xCbx2->get_active() )
+ {
+ pSrcCols[nCount] = m_xQSet2->GetItemColor( 1 );
+ pDstCols[nCount] = m_xLbColor2->GetSelectEntryColor();
+ pTols[nCount++] = static_cast<sal_uInt8>(m_xSp2->get_value(FieldUnit::PERCENT));
+ }
+
+ if ( m_xCbx3->get_active() )
+ {
+ pSrcCols[nCount] = m_xQSet3->GetItemColor( 1 );
+ pDstCols[nCount] = m_xLbColor3->GetSelectEntryColor();
+ pTols[nCount++] = static_cast<sal_uInt8>(m_xSp3->get_value(FieldUnit::PERCENT));
+ }
+
+ if ( m_xCbx4->get_active() )
+ {
+ pSrcCols[nCount] = m_xQSet4->GetItemColor( 1 );
+ pDstCols[nCount] = m_xLbColor4->GetSelectEntryColor();
+ pTols[nCount++] = static_cast<sal_uInt8>(m_xSp4->get_value(FieldUnit::PERCENT));
+ }
+
+ return nCount;
+}
+
+void SvxBmpMask::ImpMask( BitmapEx& rBitmap )
+{
+ Color pSrcCols[4];
+ Color pDstCols[4];
+ sal_uInt8 pTols[4];
+ const sal_uInt16 nCount = InitColorArrays( pSrcCols, pDstCols, pTols );
+
+ EnterWait();
+ rBitmap.Replace( pSrcCols, pDstCols, nCount, pTols );
+ LeaveWait();
+}
+
+BitmapEx SvxBmpMask::ImpMaskTransparent( const BitmapEx& rBitmapEx, const Color& rColor, const sal_uInt8 nTol )
+{
+ EnterWait();
+
+ BitmapEx aBmpEx;
+ Bitmap aMask( rBitmapEx.GetBitmap().CreateMask( rColor, nTol ) );
+
+ if( rBitmapEx.IsAlpha() )
+ aMask.CombineOr( rBitmapEx.GetAlpha() );
+
+ aBmpEx = BitmapEx( rBitmapEx.GetBitmap(), aMask );
+ LeaveWait();
+
+ return aBmpEx;
+}
+
+
+Animation SvxBmpMask::ImpMask( const Animation& rAnimation )
+{
+ Animation aAnimation( rAnimation );
+ Color pSrcCols[4];
+ Color pDstCols[4];
+ sal_uInt8 pTols[4];
+ InitColorArrays( pSrcCols, pDstCols, pTols );
+ sal_uInt16 nAnimationCount = aAnimation.Count();
+
+ for( sal_uInt16 i = 0; i < nAnimationCount; i++ )
+ {
+ AnimationBitmap aAnimationBitmap( aAnimation.Get( i ) );
+ aAnimationBitmap.maBitmapEx = Mask(aAnimationBitmap.maBitmapEx).GetBitmapEx();
+ aAnimation.Replace(aAnimationBitmap, i);
+ }
+
+ return aAnimation;
+}
+
+
+GDIMetaFile SvxBmpMask::ImpMask( const GDIMetaFile& rMtf )
+{
+ GDIMetaFile aMtf;
+ Color pSrcCols[4];
+ Color pDstCols[4];
+ sal_uInt8 pTols[4];
+ sal_uInt16 nCount = InitColorArrays( pSrcCols, pDstCols, pTols );
+
+ // If no color is selected, we copy only the Mtf
+ if( !nCount )
+ aMtf = rMtf;
+ else
+ {
+ bool pTrans[4];
+ Color aCol;
+ tools::Long nR;
+ tools::Long nG;
+ tools::Long nB;
+ std::unique_ptr<tools::Long[]> pMinR(new tools::Long[nCount]);
+ std::unique_ptr<tools::Long[]> pMaxR(new tools::Long[nCount]);
+ std::unique_ptr<tools::Long[]> pMinG(new tools::Long[nCount]);
+ std::unique_ptr<tools::Long[]> pMaxG(new tools::Long[nCount]);
+ std::unique_ptr<tools::Long[]> pMinB(new tools::Long[nCount]);
+ std::unique_ptr<tools::Long[]> pMaxB(new tools::Long[nCount]);
+ sal_uInt16 i;
+
+ aMtf.SetPrefSize( rMtf.GetPrefSize() );
+ aMtf.SetPrefMapMode( rMtf.GetPrefMapMode() );
+
+ // Prepare Color comparison array
+ for( i = 0; i < nCount; i++ )
+ {
+ tools::Long nTol = ( pTols[i] * 255 ) / 100;
+
+ tools::Long nVal = static_cast<tools::Long>(pSrcCols[i].GetRed());
+ pMinR[i] = std::max( nVal - nTol, tools::Long(0) );
+ pMaxR[i] = std::min( nVal + nTol, tools::Long(255) );
+
+ nVal = static_cast<tools::Long>(pSrcCols[i].GetGreen());
+ pMinG[i] = std::max( nVal - nTol, tools::Long(0) );
+ pMaxG[i] = std::min( nVal + nTol, tools::Long(255) );
+
+ nVal = static_cast<tools::Long>(pSrcCols[i].GetBlue());
+ pMinB[i] = std::max( nVal - nTol, tools::Long(0) );
+ pMaxB[i] = std::min( nVal + nTol, tools::Long(255) );
+
+ pTrans[ i ] = (pDstCols[ i ] == COL_TRANSPARENT);
+ }
+
+ // Investigate actions and if necessary replace colors
+ for( size_t nAct = 0, nActCount = rMtf.GetActionSize(); nAct < nActCount; nAct++ )
+ {
+ MetaAction* pAction = rMtf.GetAction( nAct );
+
+ bool bReplace = false;
+
+ switch( pAction->GetType() )
+ {
+ case MetaActionType::PIXEL:
+ {
+ MetaPixelAction* pAct = static_cast<MetaPixelAction*>(pAction);
+
+ aCol = pAct->GetColor();
+ TEST_COLS();
+
+ if( bReplace )
+ pAct = new MetaPixelAction( pAct->GetPoint(), aCol );
+
+ aMtf.AddAction( pAct );
+ }
+ break;
+
+ case MetaActionType::LINECOLOR:
+ {
+ MetaLineColorAction* pAct = static_cast<MetaLineColorAction*>(pAction);
+
+ aCol = pAct->GetColor();
+ TEST_COLS();
+
+ if( bReplace )
+ pAct = new MetaLineColorAction( aCol, !pTrans[ i ] );
+
+ aMtf.AddAction( pAct );
+ }
+ break;
+
+ case MetaActionType::FILLCOLOR:
+ {
+ MetaFillColorAction* pAct = static_cast<MetaFillColorAction*>(pAction);
+
+ aCol = pAct->GetColor();
+ TEST_COLS();
+
+ if( bReplace )
+ pAct = new MetaFillColorAction( aCol, !pTrans[ i ] );
+
+ aMtf.AddAction( pAct );
+ }
+ break;
+
+ case MetaActionType::TEXTCOLOR:
+ {
+ MetaTextColorAction* pAct = static_cast<MetaTextColorAction*>(pAction);
+
+ aCol = pAct->GetColor();
+ TEST_COLS();
+
+ if( bReplace )
+ pAct = new MetaTextColorAction( aCol );
+
+ aMtf.AddAction( pAct );
+ }
+ break;
+
+ case MetaActionType::TEXTFILLCOLOR:
+ {
+ MetaTextFillColorAction* pAct = static_cast<MetaTextFillColorAction*>(pAction);
+
+ aCol = pAct->GetColor();
+ TEST_COLS();
+
+ if( bReplace )
+ pAct = new MetaTextFillColorAction( aCol, !pTrans[ i ] );
+
+ aMtf.AddAction( pAct );
+ }
+ break;
+
+ case MetaActionType::FONT:
+ {
+ MetaFontAction* pAct = static_cast<MetaFontAction*>(pAction);
+ vcl::Font aFont( pAct->GetFont() );
+
+ aCol = aFont.GetColor();
+ TEST_COLS();
+
+ if( bReplace )
+ {
+ aFont.SetColor( aCol );
+ pAct = new MetaFontAction( aFont );
+ }
+
+ aMtf.AddAction( pAct );
+ }
+ break;
+
+ case MetaActionType::WALLPAPER:
+ {
+ MetaWallpaperAction* pAct = static_cast<MetaWallpaperAction*>(pAction);
+ Wallpaper aWall( pAct->GetWallpaper() );
+
+ aCol = aWall.GetColor();
+ TEST_COLS();
+
+ if( bReplace )
+ {
+ aWall.SetColor( aCol );
+ pAct = new MetaWallpaperAction( pAct->GetRect(), aWall );
+ }
+
+ aMtf.AddAction( pAct );
+ }
+ break;
+
+ case MetaActionType::BMP:
+ {
+ MetaBmpAction* pAct = static_cast<MetaBmpAction*>(pAction);
+ const Bitmap aBmp( Mask(BitmapEx(pAct->GetBitmap())).GetBitmapEx().GetBitmap() );
+
+ pAct = new MetaBmpAction( pAct->GetPoint(), aBmp );
+ aMtf.AddAction( pAct );
+ }
+ break;
+
+ case MetaActionType::BMPSCALE:
+ {
+ MetaBmpScaleAction* pAct = static_cast<MetaBmpScaleAction*>(pAction);
+ const Bitmap aBmp( Mask(BitmapEx(pAct->GetBitmap())).GetBitmapEx().GetBitmap() );
+
+ pAct = new MetaBmpScaleAction( pAct->GetPoint(), pAct->GetSize(), aBmp );
+ aMtf.AddAction( pAct );
+ }
+ break;
+
+ case MetaActionType::BMPSCALEPART:
+ {
+ MetaBmpScalePartAction* pAct = static_cast<MetaBmpScalePartAction*>(pAction);
+ const Bitmap aBmp( Mask(BitmapEx(pAct->GetBitmap())).GetBitmapEx().GetBitmap() );
+
+ pAct = new MetaBmpScalePartAction( pAct->GetDestPoint(), pAct->GetDestSize(),
+ pAct->GetSrcPoint(), pAct->GetSrcSize(), aBmp );
+ aMtf.AddAction( pAct );
+ }
+ break;
+
+ case MetaActionType::BMPEX:
+ {
+ MetaBmpExAction* pAct = static_cast<MetaBmpExAction*>(pAction);
+ const BitmapEx aBmpEx( Mask( pAct->GetBitmapEx() ).GetBitmapEx() );
+
+ pAct = new MetaBmpExAction( pAct->GetPoint(), aBmpEx );
+ aMtf.AddAction( pAct );
+ }
+ break;
+
+ case MetaActionType::BMPEXSCALE:
+ {
+ MetaBmpExScaleAction* pAct = static_cast<MetaBmpExScaleAction*>(pAction);
+ const BitmapEx aBmpEx( Mask( pAct->GetBitmapEx() ).GetBitmapEx() );
+
+ pAct = new MetaBmpExScaleAction( pAct->GetPoint(), pAct->GetSize(), aBmpEx );
+ aMtf.AddAction( pAct );
+ }
+ break;
+
+ case MetaActionType::BMPEXSCALEPART:
+ {
+ MetaBmpExScalePartAction* pAct = static_cast<MetaBmpExScalePartAction*>(pAction);
+ const BitmapEx aBmpEx( Mask( pAct->GetBitmapEx() ).GetBitmapEx() );
+
+ pAct = new MetaBmpExScalePartAction( pAct->GetDestPoint(), pAct->GetDestSize(),
+ pAct->GetSrcPoint(), pAct->GetSrcSize(), aBmpEx );
+ aMtf.AddAction( pAct );
+ }
+ break;
+
+ default:
+ {
+ aMtf.AddAction( pAction );
+ }
+ break;
+ }
+ }
+ }
+
+ LeaveWait();
+
+ return aMtf;
+}
+
+
+Animation SvxBmpMask::ImpReplaceTransparency( const Animation& rAnim, const Color& rColor )
+{
+ Animation aAnimation( rAnim );
+ sal_uInt16 nAnimationCount = aAnimation.Count();
+
+ for( sal_uInt16 i = 0; i < nAnimationCount; i++ )
+ {
+ AnimationBitmap aAnimationBitmap(aAnimation.Get(i));
+ aAnimationBitmap.maBitmapEx.ReplaceTransparency(rColor);
+ aAnimation.Replace(aAnimationBitmap, i);
+ }
+
+ return aAnimation;
+}
+
+
+GDIMetaFile SvxBmpMask::ImpReplaceTransparency( const GDIMetaFile& rMtf, const Color& rColor )
+{
+ ScopedVclPtrInstance< VirtualDevice > pVDev;
+ GDIMetaFile aMtf;
+ const MapMode& rPrefMap = rMtf.GetPrefMapMode();
+ const Size& rPrefSize = rMtf.GetPrefSize();
+ const size_t nActionCount = rMtf.GetActionSize();
+
+ pVDev->EnableOutput( false );
+ aMtf.Record( pVDev );
+ aMtf.SetPrefSize( rPrefSize );
+ aMtf.SetPrefMapMode( rPrefMap );
+ pVDev->SetLineColor( rColor );
+ pVDev->SetFillColor( rColor );
+
+ // retrieve one action at the time; first
+ // set the whole area to the replacement color.
+ pVDev->DrawRect( tools::Rectangle( rPrefMap.GetOrigin(), rPrefSize ) );
+ for ( size_t i = 0; i < nActionCount; i++ )
+ {
+ MetaAction* pAct = rMtf.GetAction( i );
+ aMtf.AddAction( pAct );
+ }
+
+ aMtf.Stop();
+ aMtf.WindStart();
+
+ return aMtf;
+}
+
+GDIMetaFile SvxBmpMask::GetMetaFile(const Graphic& rGraphic)
+{
+ // Replace transparency?
+ if (m_xCbxTrans->get_active())
+ return ImpReplaceTransparency(rGraphic.GetGDIMetaFile(), m_xLbColorTrans->GetSelectEntryColor());
+ return ImpMask(rGraphic.GetGDIMetaFile());
+}
+
+Graphic SvxBmpMask::Mask( const Graphic& rGraphic )
+{
+ Graphic aGraphic( rGraphic );
+ const Color aReplColor( m_xLbColorTrans->GetSelectEntryColor() );
+
+ switch( rGraphic.GetType() )
+ {
+ case GraphicType::Bitmap:
+ {
+ if( rGraphic.IsAnimated() )
+ {
+ // Replace transparency?
+ if ( m_xCbxTrans->get_active() )
+ aGraphic = ImpReplaceTransparency( rGraphic.GetAnimation(), aReplColor );
+ else
+ aGraphic = ImpMask( rGraphic.GetAnimation() );
+ }
+ else
+ {
+ // Replace transparency?
+ if( m_xCbxTrans->get_active() )
+ {
+ BitmapEx aBmpEx = aGraphic.GetBitmapEx();
+ aBmpEx.ReplaceTransparency(aReplColor);
+ aGraphic = aBmpEx;
+ }
+ else
+ {
+ Color pSrcCols[4];
+ Color pDstCols[4];
+ sal_uInt8 pTols[4];
+ sal_uInt16 nCount = InitColorArrays( pSrcCols, pDstCols, pTols );
+
+ if( nCount )
+ {
+ // first set all transparent colors
+ for( sal_uInt16 i = 0; i < nCount; i++ )
+ {
+ // Do we have a transparent color?
+ if (pDstCols[i] == COL_TRANSPARENT)
+ {
+ BitmapEx aBmpEx( ImpMaskTransparent( aGraphic.GetBitmapEx(),
+ pSrcCols[ i ], pTols[ i ] ) );
+ const Size aSize( aBmpEx.GetSizePixel() );
+
+ if( aSize.Width() && aSize.Height() )
+ aGraphic = aBmpEx;
+ }
+ }
+
+ // now replace it again with the normal colors
+ BitmapEx aBitmapEx( aGraphic.GetBitmapEx() );
+ if ( aBitmapEx.GetSizePixel().Width() && aBitmapEx.GetSizePixel().Height() )
+ {
+ ImpMask( aBitmapEx );
+ if ( aGraphic.IsTransparent() )
+ aGraphic = Graphic( BitmapEx( aBitmapEx.GetBitmap(), aBitmapEx.GetAlpha() ) );
+ else
+ aGraphic = aBitmapEx;
+ }
+ }
+ }
+ }
+ }
+ break;
+
+ case GraphicType::GdiMetafile:
+ {
+ GDIMetaFile aMtf(GetMetaFile(rGraphic));
+ Size aSize( aMtf.GetPrefSize() );
+ if ( aSize.Width() && aSize.Height() )
+ aGraphic = Graphic( aMtf );
+ else
+ aGraphic = rGraphic;
+ }
+ break;
+
+ default:
+ aGraphic = rGraphic;
+ break;
+ }
+
+ if( aGraphic != rGraphic )
+ {
+ aGraphic.SetPrefSize( rGraphic.GetPrefSize() );
+ aGraphic.SetPrefMapMode( rGraphic.GetPrefMapMode() );
+ }
+
+ return aGraphic;
+}
+
+bool SvxBmpMask::IsEyedropping() const
+{
+ return m_xTbxPipette->get_item_active("pipette");
+}
+
+/** Set an accessible name for the source color check boxes. Without this
+ the lengthy description is read.
+*/
+void SvxBmpMask::SetAccessibleNames()
+{
+ // set the accessible name for valueset
+ OUString sColorPalette (SvxResId( RID_SVXDLG_BMPMASK_STR_PALETTE));
+ OUString sColorPaletteN;
+
+ sColorPaletteN = sColorPalette + " 1";
+ m_xQSet1->SetText (sColorPaletteN);
+ sColorPaletteN = sColorPalette + " 2";
+ m_xQSet2->SetText (sColorPaletteN);
+ sColorPaletteN = sColorPalette + " 3";
+ m_xQSet3->SetText (sColorPaletteN);
+ sColorPaletteN = sColorPalette + " 4";
+ m_xQSet4->SetText (sColorPaletteN);
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/dialog/_contdlg.cxx b/svx/source/dialog/_contdlg.cxx
new file mode 100644
index 000000000..fbd6f40bc
--- /dev/null
+++ b/svx/source/dialog/_contdlg.cxx
@@ -0,0 +1,675 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <sal/config.h>
+
+#include <tools/helpers.hxx>
+#include <svl/eitem.hxx>
+#include <sfx2/ctrlitem.hxx>
+#include <sfx2/dispatch.hxx>
+#include <sfx2/module.hxx>
+#include <unotools/localedatawrapper.hxx>
+
+#include <svx/svxids.hrc>
+#include <svx/contdlg.hxx>
+#include "contimp.hxx"
+#include "contwnd.hxx"
+#include <svx/svdopath.hxx>
+#include <vcl/svapp.hxx>
+#include <vcl/settings.hxx>
+#include <vcl/virdev.hxx>
+#include "dlgunit.hxx"
+#include <vcl/weld.hxx>
+
+SFX_IMPL_MODELESSDIALOGCONTOLLER_WITHID(SvxContourDlgChildWindow, SID_CONTOUR_DLG);
+
+SvxContourDlgItem::SvxContourDlgItem( SvxSuperContourDlg& rContourDlg, SfxBindings& rBindings ) :
+ SfxControllerItem ( SID_CONTOUR_EXEC, rBindings ),
+ rDlg ( rContourDlg )
+{
+}
+
+void SvxContourDlgItem::StateChangedAtToolBoxControl( sal_uInt16 nSID, SfxItemState /*eState*/, const SfxPoolItem* pItem )
+{
+ if ( pItem && ( SID_CONTOUR_EXEC == nSID ) )
+ {
+ const SfxBoolItem* pStateItem = dynamic_cast<const SfxBoolItem*>( pItem );
+ assert(pStateItem); //SfxBoolItem expected
+ if (pStateItem)
+ rDlg.SetExecState(!pStateItem->GetValue());
+ }
+}
+
+SvxContourDlgChildWindow::SvxContourDlgChildWindow(vcl::Window* _pParent, sal_uInt16 nId,
+ SfxBindings* pBindings, SfxChildWinInfo const * pInfo)
+ : SfxChildWindow( _pParent, nId )
+{
+ SetController(std::make_shared<SvxContourDlg>(pBindings, this, _pParent->GetFrameWeld()));
+ SvxContourDlg* pDlg = static_cast<SvxContourDlg*>(GetController().get());
+ pDlg->Initialize( pInfo );
+}
+
+SvxContourDlg::SvxContourDlg(SfxBindings* _pBindings, SfxChildWindow* pCW,
+ weld::Window* _pParent)
+ : SfxModelessDialogController(_pBindings, pCW, _pParent, "svx/ui/floatingcontour.ui", "FloatingContour")
+ , m_xImpl(std::make_unique<SvxSuperContourDlg>(*m_xBuilder, *m_xDialog, _pBindings))
+{
+}
+
+SvxContourDlg::~SvxContourDlg()
+{
+}
+
+tools::PolyPolygon SvxContourDlg::CreateAutoContour( const Graphic& rGraphic,
+ const tools::Rectangle* pRect )
+{
+ Bitmap aBmp;
+ bool bContourEdgeDetect = false;
+
+ if ( rGraphic.GetType() == GraphicType::Bitmap )
+ {
+ if( rGraphic.IsAnimated() )
+ {
+ ScopedVclPtrInstance< VirtualDevice > pVDev;
+ MapMode aTransMap;
+ const Animation aAnim( rGraphic.GetAnimation() );
+ const Size& rSizePix = aAnim.GetDisplaySizePixel();
+ const sal_uInt16 nCount = aAnim.Count();
+
+ if ( pVDev->SetOutputSizePixel( rSizePix ) )
+ {
+ pVDev->SetLineColor( COL_BLACK );
+ pVDev->SetFillColor( COL_BLACK );
+
+ for( sal_uInt16 i = 0; i < nCount; i++ )
+ {
+ const AnimationBitmap& rStepBmp = aAnim.Get( i );
+
+ // Push Polygon output to the right place; this is the
+ // offset of the sub-image within the total animation
+ aTransMap.SetOrigin( Point( rStepBmp.maPositionPixel.X(), rStepBmp.maPositionPixel.Y() ) );
+ pVDev->SetMapMode( aTransMap );
+ pVDev->DrawPolyPolygon( CreateAutoContour( rStepBmp.maBitmapEx, pRect ) );
+ }
+
+ aTransMap.SetOrigin( Point() );
+ pVDev->SetMapMode( aTransMap );
+ aBmp = pVDev->GetBitmap( Point(), rSizePix );
+ aBmp.Convert( BmpConversion::N1BitThreshold );
+ }
+ }
+ else if( rGraphic.IsTransparent() )
+ aBmp = rGraphic.GetBitmapEx().GetAlpha();
+ else
+ {
+ aBmp = rGraphic.GetBitmapEx().GetBitmap();
+ bContourEdgeDetect = true;
+ }
+ }
+ else if( rGraphic.GetType() != GraphicType::NONE )
+ {
+ const Graphic aTmpGrf( rGraphic.GetGDIMetaFile().GetMonochromeMtf( COL_BLACK ) );
+ ScopedVclPtrInstance< VirtualDevice > pVDev;
+ Size aSizePix( pVDev->LogicToPixel( aTmpGrf.GetPrefSize(), aTmpGrf.GetPrefMapMode() ) );
+
+ if( aSizePix.Width() && aSizePix.Height() && ( aSizePix.Width() > 512 || aSizePix.Height() > 512 ) )
+ {
+ double fWH = static_cast<double>(aSizePix.Width()) / aSizePix.Height();
+
+ if( fWH <= 1.0 )
+ {
+ aSizePix.setHeight(512);
+ aSizePix.setWidth( FRound( ( aSizePix.Height() ) * fWH ) );
+ }
+ else
+ {
+ aSizePix.setWidth(512);
+ aSizePix.setHeight( FRound( ( aSizePix.Width() ) / fWH ) );
+ }
+ }
+
+ if( pVDev->SetOutputSizePixel( aSizePix ) )
+ {
+ const Point aPt;
+ aTmpGrf.Draw(*pVDev, aPt, aSizePix);
+ aBmp = pVDev->GetBitmap( aPt, aSizePix );
+ }
+
+ bContourEdgeDetect = true;
+ }
+
+ aBmp.SetPrefSize( rGraphic.GetPrefSize() );
+ aBmp.SetPrefMapMode( rGraphic.GetPrefMapMode() );
+
+ return tools::PolyPolygon( BitmapEx(aBmp).GetContour( bContourEdgeDetect, pRect ) );
+}
+
+// Loop through to super class, no virtual Methods to not become incompatible
+// due to IF changes
+
+const Graphic& SvxContourDlg::GetGraphic() const
+{
+ return m_xImpl->GetGraphic();
+}
+
+bool SvxContourDlg::IsGraphicChanged() const
+{
+ return m_xImpl->IsGraphicChanged();
+}
+
+tools::PolyPolygon SvxContourDlg::GetPolyPolygon()
+{
+ return m_xImpl->GetPolyPolygon();
+}
+
+const void* SvxContourDlg::GetEditingObject() const
+{
+ return m_xImpl->GetEditingObject();
+}
+
+void SvxContourDlg::Update( const Graphic& rGraphic, bool bGraphicLinked,
+ const tools::PolyPolygon* pPolyPoly, void* pEditingObj )
+{
+ m_xImpl->UpdateGraphic( rGraphic, bGraphicLinked, pPolyPoly, pEditingObj );
+}
+
+SvxSuperContourDlg::SvxSuperContourDlg(weld::Builder& rBuilder,
+ weld::Dialog& rDialog, SfxBindings* pBindings)
+ : aUpdateIdle( "SvxSuperContourDlg UpdateIdle" )
+ , aCreateIdle( "SvxSuperContourDlg CreateIdle" )
+ , mpBindings(pBindings)
+ , pUpdateEditingObject( nullptr )
+ , pCheckObj( nullptr )
+ , aContourItem( *this, *pBindings )
+ , mnGrfChanged( 0 )
+ , bExecState( false )
+ , bUpdateGraphicLinked( false )
+ , bGraphicLinked( false )
+ , m_rDialog(rDialog)
+ , m_xContourWnd(new ContourWindow(&rDialog))
+ , m_xStbStatusColor(new StatusColor(*m_xContourWnd))
+ , m_xTbx1(rBuilder.weld_toolbar("toolbar"))
+ , m_xMtfTolerance(rBuilder.weld_metric_spin_button("spinbutton", FieldUnit::PERCENT))
+ , m_xStbStatus2(rBuilder.weld_label("statuspos"))
+ , m_xStbStatus3(rBuilder.weld_label("statussize"))
+ , m_xCancelBtn(rBuilder.weld_button("cancel"))
+ , m_xStbStatusColorWeld(new weld::CustomWeld(rBuilder, "statuscolor", *m_xStbStatusColor))
+ , m_xContourWndWeld(new weld::CustomWeld(rBuilder, "container", *m_xContourWnd))
+{
+ m_xCancelBtn->connect_clicked(LINK(this, SvxSuperContourDlg, CancelHdl));
+
+ m_xContourWnd->SetMousePosLink( LINK( this, SvxSuperContourDlg, MousePosHdl ) );
+ m_xContourWnd->SetGraphSizeLink( LINK( this, SvxSuperContourDlg, GraphSizeHdl ) );
+ m_xContourWnd->SetUpdateLink( LINK( this, SvxSuperContourDlg, StateHdl ) );
+ m_xContourWnd->SetPipetteHdl( LINK( this, SvxSuperContourDlg, PipetteHdl ) );
+ m_xContourWnd->SetPipetteClickHdl( LINK( this, SvxSuperContourDlg, PipetteClickHdl ) );
+ m_xContourWnd->SetWorkplaceClickHdl( LINK( this, SvxSuperContourDlg, WorkplaceClickHdl ) );
+
+ m_xTbx1->connect_clicked( LINK( this, SvxSuperContourDlg, Tbx1ClickHdl ) );
+
+ m_xMtfTolerance->set_value(10, FieldUnit::PERCENT);
+
+ aUpdateIdle.SetInvokeHandler( LINK( this, SvxSuperContourDlg, UpdateHdl ) );
+
+ aCreateIdle.SetPriority( TaskPriority::RESIZE );
+ aCreateIdle.SetInvokeHandler( LINK( this, SvxSuperContourDlg, CreateHdl ) );
+}
+
+SvxSuperContourDlg::~SvxSuperContourDlg()
+{
+ m_xContourWnd->SetUpdateLink( Link<GraphCtrl*,void>() );
+ m_xContourWnd.reset();
+}
+
+IMPL_LINK_NOARG(SvxSuperContourDlg, CancelHdl, weld::Button&, void)
+{
+ bool bRet = true;
+
+ if (m_xTbx1->get_item_sensitive("TBI_APPLY"))
+ {
+ std::unique_ptr<weld::Builder> xBuilder(Application::CreateBuilder(&m_rDialog, "svx/ui/querysavecontchangesdialog.ui"));
+ std::unique_ptr<weld::MessageDialog> xQBox(xBuilder->weld_message_dialog("QuerySaveContourChangesDialog"));
+ const short nRet = xQBox->run();
+
+ if ( nRet == RET_YES )
+ {
+ SfxBoolItem aBoolItem( SID_CONTOUR_EXEC, true );
+ GetBindings().GetDispatcher()->ExecuteList(
+ SID_CONTOUR_EXEC, SfxCallMode::SYNCHRON | SfxCallMode::RECORD,
+ { &aBoolItem });
+ }
+ else if ( nRet == RET_CANCEL )
+ bRet = false;
+ }
+
+ if (bRet)
+ m_rDialog.response(RET_CANCEL);
+}
+
+// Enabled or disabled all Controls
+
+void SvxSuperContourDlg::SetExecState( bool bEnable )
+{
+ bExecState = bEnable;
+}
+
+void SvxSuperContourDlg::SetGraphic( const Graphic& rGraphic )
+{
+ aUndoGraphic = aRedoGraphic = Graphic();
+ aGraphic = rGraphic;
+ mnGrfChanged = 0;
+ m_xContourWnd->SetGraphic( aGraphic );
+}
+
+void SvxSuperContourDlg::SetPolyPolygon( const tools::PolyPolygon& rPolyPoly )
+{
+ DBG_ASSERT( m_xContourWnd->GetGraphic().GetType() != GraphicType::NONE, "Graphic must've been set first!" );
+
+ tools::PolyPolygon aPolyPoly( rPolyPoly );
+ const MapMode aMap100( MapUnit::Map100thMM );
+ const MapMode aGrfMap( aGraphic.GetPrefMapMode() );
+ OutputDevice* pOutDev = Application::GetDefaultDevice();
+ bool bPixelMap = aGrfMap.GetMapUnit() == MapUnit::MapPixel;
+
+ for ( sal_uInt16 j = 0, nPolyCount = aPolyPoly.Count(); j < nPolyCount; j++ )
+ {
+ tools::Polygon& rPoly = aPolyPoly[ j ];
+
+ for ( sal_uInt16 i = 0, nCount = rPoly.GetSize(); i < nCount; i++ )
+ {
+ Point& rPt = rPoly[ i ];
+
+ if ( !bPixelMap )
+ rPt = pOutDev->LogicToPixel( rPt, aGrfMap );
+
+ rPt = pOutDev->PixelToLogic( rPt, aMap100 );
+ }
+ }
+
+ m_xContourWnd->SetPolyPolygon( aPolyPoly );
+ m_xContourWnd->GetSdrModel()->SetChanged();
+}
+
+tools::PolyPolygon SvxSuperContourDlg::GetPolyPolygon()
+{
+ tools::PolyPolygon aRetPolyPoly( m_xContourWnd->GetPolyPolygon() );
+
+ const MapMode aMap100( MapUnit::Map100thMM );
+ const MapMode aGrfMap( aGraphic.GetPrefMapMode() );
+ OutputDevice* pOutDev = Application::GetDefaultDevice();
+ bool bPixelMap = aGrfMap.GetMapUnit() == MapUnit::MapPixel;
+
+ for ( sal_uInt16 j = 0, nPolyCount = aRetPolyPoly.Count(); j < nPolyCount; j++ )
+ {
+ tools::Polygon& rPoly = aRetPolyPoly[ j ];
+
+ for ( sal_uInt16 i = 0, nCount = rPoly.GetSize(); i < nCount; i++ )
+ {
+ Point& rPt = rPoly[ i ];
+
+ rPt = pOutDev->LogicToPixel( rPt, aMap100 );
+
+ if ( !bPixelMap )
+ rPt = pOutDev->PixelToLogic( rPt, aGrfMap );
+ }
+ }
+
+ return aRetPolyPoly;
+}
+
+void SvxSuperContourDlg::UpdateGraphic( const Graphic& rGraphic, bool _bGraphicLinked,
+ const tools::PolyPolygon* pPolyPoly, void* pEditingObj )
+{
+ aUpdateGraphic = rGraphic;
+ bUpdateGraphicLinked = _bGraphicLinked;
+ pUpdateEditingObject = pEditingObj;
+
+ if ( pPolyPoly )
+ aUpdatePolyPoly = *pPolyPoly;
+ else
+ aUpdatePolyPoly = tools::PolyPolygon();
+
+ aUpdateIdle.Start();
+}
+
+// Click handler for ToolBox
+
+IMPL_LINK(SvxSuperContourDlg, Tbx1ClickHdl, const OString&, rId, void)
+{
+ if (rId == "TBI_APPLY")
+ {
+ SfxBoolItem aBoolItem( SID_CONTOUR_EXEC, true );
+ GetBindings().GetDispatcher()->ExecuteList(
+ SID_CONTOUR_EXEC, SfxCallMode::ASYNCHRON | SfxCallMode::RECORD,
+ { &aBoolItem });
+ }
+ else if (rId == "TBI_WORKPLACE")
+ {
+ if (m_xTbx1->get_item_active("TBI_WORKPLACE"))
+ {
+ std::unique_ptr<weld::Builder> xBuilder(Application::CreateBuilder(&m_rDialog, "svx/ui/querydeletecontourdialog.ui"));
+ std::unique_ptr<weld::MessageDialog> xQBox(xBuilder->weld_message_dialog("QueryDeleteContourDialog"));
+
+ if (!m_xContourWnd->IsContourChanged() || (xQBox->run() == RET_YES))
+ m_xContourWnd->SetWorkplaceMode( true );
+ else
+ m_xTbx1->set_item_active("TBI_WORKPLACE", false);
+ }
+ else
+ m_xContourWnd->SetWorkplaceMode( false );
+ }
+ else if (rId == "TBI_SELECT")
+ {
+ SetActiveTool(rId);
+ m_xContourWnd->SetEditMode( true );
+ }
+ else if (rId == "TBI_RECT")
+ {
+ SetActiveTool(rId);
+ m_xContourWnd->SetObjKind( SdrObjKind::Rectangle );
+ }
+ else if (rId == "TBI_CIRCLE")
+ {
+ SetActiveTool(rId);
+ m_xContourWnd->SetObjKind( SdrObjKind::CircleOrEllipse );
+ }
+ else if (rId == "TBI_POLY")
+ {
+ SetActiveTool(rId);
+ m_xContourWnd->SetObjKind( SdrObjKind::Polygon );
+ }
+ else if (rId == "TBI_POLYEDIT")
+ {
+ m_xContourWnd->SetPolyEditMode(m_xTbx1->get_item_active("TBI_POLYEDIT") ? SID_BEZIER_MOVE : 0);
+ }
+ else if (rId == "TBI_POLYMOVE")
+ {
+ SetActivePoly(rId);
+ m_xContourWnd->SetPolyEditMode( SID_BEZIER_MOVE );
+ }
+ else if (rId == "TBI_POLYINSERT")
+ {
+ SetActivePoly(rId);
+ m_xContourWnd->SetPolyEditMode( SID_BEZIER_INSERT );
+ }
+ else if (rId == "TBI_POLYDELETE")
+ {
+ m_xContourWnd->GetSdrView()->DeleteMarkedPoints();
+ }
+ else if (rId == "TBI_UNDO")
+ {
+ mnGrfChanged = mnGrfChanged ? mnGrfChanged - 1 : 0;
+ aRedoGraphic = aGraphic;
+ aGraphic = aUndoGraphic;
+ aUndoGraphic = Graphic();
+ m_xContourWnd->SetGraphic( aGraphic, false );
+ }
+ else if (rId == "TBI_REDO")
+ {
+ mnGrfChanged++;
+ aUndoGraphic = aGraphic;
+ aGraphic = aRedoGraphic;
+ aRedoGraphic = Graphic();
+ m_xContourWnd->SetGraphic( aGraphic, false );
+ }
+ else if (rId == "TBI_AUTOCONTOUR")
+ {
+ aCreateIdle.Start();
+ }
+ else if (rId == "TBI_PIPETTE")
+ {
+ bool bPipette = m_xTbx1->get_item_active("TBI_PIPETTE");
+
+ if ( !bPipette )
+ m_xStbStatusColor->Invalidate();
+ else if ( bGraphicLinked )
+ {
+ std::unique_ptr<weld::Builder> xBuilder(Application::CreateBuilder(&m_rDialog, "svx/ui/queryunlinkgraphicsdialog.ui"));
+ std::unique_ptr<weld::MessageDialog> xQBox(xBuilder->weld_message_dialog("QueryUnlinkGraphicsDialog"));
+
+ if (xQBox->run() != RET_YES)
+ {
+ bPipette = false;
+ m_xTbx1->set_item_active("TBI_PIPETTE", bPipette);
+ m_xStbStatusColor->Invalidate();
+ }
+ }
+
+ m_xContourWnd->SetPipetteMode( bPipette );
+ }
+ m_xContourWnd->QueueIdleUpdate();
+}
+
+void SvxSuperContourDlg::SetActiveTool(std::string_view rId)
+{
+ m_xTbx1->set_item_active("TBI_SELECT", rId == "TBI_SELECT");
+ m_xTbx1->set_item_active("TBI_RECT", rId == "TBI_RECT");
+ m_xTbx1->set_item_active("TBI_CIRCLE", rId == "TBI_CIRCLE");
+ m_xTbx1->set_item_active("TBI_POLY", rId == "TBI_POLY");
+}
+
+void SvxSuperContourDlg::SetActivePoly(std::string_view rId)
+{
+ m_xTbx1->set_item_active("TBI_POLYMOVE", rId == "TBI_POLYMOVE");
+ m_xTbx1->set_item_active("TBI_POLYINSERT", rId == "TBI_POLYINSERT");
+}
+
+IMPL_LINK( SvxSuperContourDlg, MousePosHdl, GraphCtrl*, pWnd, void )
+{
+ OUString aStr;
+ const FieldUnit eFieldUnit = GetBindings().GetDispatcher()->GetModule()->GetFieldUnit();
+ const Point& rMousePos = pWnd->GetMousePos();
+ const LocaleDataWrapper& rLocaleWrapper( Application::GetSettings().GetLocaleDataWrapper() );
+ const sal_Unicode cSep = rLocaleWrapper.getNumDecimalSep()[0];
+
+ aStr = GetUnitString( rMousePos.X(), eFieldUnit, cSep )
+ + " / "
+ + GetUnitString( rMousePos.Y(), eFieldUnit, cSep );
+
+ m_xStbStatus2->set_label( aStr );
+}
+
+IMPL_LINK( SvxSuperContourDlg, GraphSizeHdl, GraphCtrl*, pWnd, void )
+{
+ OUString aStr;
+ const FieldUnit eFieldUnit = GetBindings().GetDispatcher()->GetModule()->GetFieldUnit();
+ const Size& rSize = pWnd->GetGraphicSize();
+ const LocaleDataWrapper& rLocaleWrapper( Application::GetSettings().GetLocaleDataWrapper() );
+ const sal_Unicode cSep = rLocaleWrapper.getNumDecimalSep()[0];
+
+ aStr = GetUnitString( rSize.Width(), eFieldUnit, cSep )
+ + " x "
+ + GetUnitString( rSize.Height(), eFieldUnit, cSep );
+
+ m_xStbStatus3->set_label( aStr );
+}
+
+IMPL_LINK_NOARG(SvxSuperContourDlg, UpdateHdl, Timer *, void)
+{
+ aUpdateIdle.Stop();
+
+ if ( pUpdateEditingObject != pCheckObj )
+ {
+ if( !GetEditingObject() )
+ m_xContourWnd->GrabFocus();
+
+ SetGraphic( aUpdateGraphic );
+ SetPolyPolygon( aUpdatePolyPoly );
+ pCheckObj = pUpdateEditingObject;
+ bGraphicLinked = bUpdateGraphicLinked;
+
+ aUpdateGraphic = Graphic();
+ aUpdatePolyPoly = tools::PolyPolygon();
+ bUpdateGraphicLinked = false;
+
+ m_xContourWnd->GetSdrModel()->SetChanged( false );
+ }
+
+ GetBindings().Invalidate( SID_CONTOUR_EXEC );
+ m_xContourWnd->QueueIdleUpdate();
+}
+
+IMPL_LINK_NOARG(SvxSuperContourDlg, CreateHdl, Timer *, void)
+{
+ aCreateIdle.Stop();
+
+ const tools::Rectangle aWorkRect = m_xContourWnd->GetDrawingArea()->get_ref_device().LogicToPixel(
+ m_xContourWnd->GetWorkRect(), MapMode( MapUnit::Map100thMM));
+
+ const Graphic& rGraphic = m_xContourWnd->GetGraphic();
+ const bool bValid = aWorkRect.Left() != aWorkRect.Right() && aWorkRect.Top() != aWorkRect.Bottom();
+
+ weld::WaitObject aWaitObj(&m_rDialog);
+ SetPolyPolygon( SvxContourDlg::CreateAutoContour( rGraphic, bValid ? &aWorkRect : nullptr ) );
+}
+
+IMPL_LINK( SvxSuperContourDlg, StateHdl, GraphCtrl*, pWnd, void )
+{
+ const SdrObject* pObj = pWnd->GetSelectedSdrObject();
+ const SdrView* pView = pWnd->GetSdrView();
+ const bool bPolyEdit = ( pObj != nullptr ) && dynamic_cast<const SdrPathObj*>( pObj) != nullptr;
+ const bool bDrawEnabled = !(bPolyEdit && m_xTbx1->get_item_active("TBI_POLYEDIT"));
+ const bool bPipette = m_xTbx1->get_item_active("TBI_PIPETTE");
+ const bool bWorkplace = m_xTbx1->get_item_active("TBI_WORKPLACE");
+ const bool bDontHide = !( bPipette || bWorkplace );
+ const bool bBitmap = pWnd->GetGraphic().GetType() == GraphicType::Bitmap;
+
+ m_xTbx1->set_item_sensitive("TBI_APPLY", bDontHide && bExecState && pWnd->IsChanged());
+
+ m_xTbx1->set_item_sensitive("TBI_WORKPLACE", !bPipette && bDrawEnabled);
+
+ m_xTbx1->set_item_sensitive("TBI_SELECT", bDontHide && bDrawEnabled);
+ m_xTbx1->set_item_sensitive("TBI_RECT", bDontHide && bDrawEnabled);
+ m_xTbx1->set_item_sensitive("TBI_CIRCLE", bDontHide && bDrawEnabled);
+ m_xTbx1->set_item_sensitive("TBI_POLY", bDontHide && bDrawEnabled);
+
+ m_xTbx1->set_item_sensitive("TBI_POLYEDIT", bDontHide && bPolyEdit);
+ m_xTbx1->set_item_sensitive("TBI_POLYMOVE", bDontHide && !bDrawEnabled);
+ m_xTbx1->set_item_sensitive("TBI_POLYINSERT", bDontHide && !bDrawEnabled);
+ m_xTbx1->set_item_sensitive("TBI_POLYDELETE", bDontHide && !bDrawEnabled && pView->IsDeleteMarkedPointsPossible());
+
+ m_xTbx1->set_item_sensitive("TBI_AUTOCONTOUR", bDontHide && bDrawEnabled);
+ m_xTbx1->set_item_sensitive("TBI_PIPETTE", !bWorkplace && bDrawEnabled && bBitmap);
+
+ m_xTbx1->set_item_sensitive("TBI_UNDO", bDontHide && aUndoGraphic.GetType() != GraphicType::NONE);
+ m_xTbx1->set_item_sensitive("TBI_REDO", bDontHide && aRedoGraphic.GetType() != GraphicType::NONE);
+
+ if ( bPolyEdit )
+ {
+ switch( pWnd->GetPolyEditMode() )
+ {
+ case SID_BEZIER_MOVE:
+ SetActivePoly("TBI_POLYMOVE");
+ break;
+ case SID_BEZIER_INSERT:
+ SetActivePoly("TBI_POLYINSERT");
+ break;
+ default:
+ break;
+ }
+ }
+ else
+ {
+ m_xTbx1->set_item_active("TBI_POLYEDIT", false);
+ SetActivePoly("TBI_POLYMOVE");
+ pWnd->SetPolyEditMode( 0 );
+ }
+}
+
+IMPL_LINK_NOARG(SvxSuperContourDlg, PipetteHdl, ContourWindow&, void)
+{
+ m_xStbStatusColor->Invalidate();
+}
+
+void StatusColor::Paint(vcl::RenderContext& rDevice, const tools::Rectangle&)
+{
+ const Color& rOldLineColor = rDevice.GetLineColor();
+ const Color& rOldFillColor = rDevice.GetFillColor();
+
+ tools::Rectangle aRect(Point(), GetOutputSizePixel());
+ const Color& rColor = m_rWnd.GetPipetteColor();
+
+ rDevice.SetLineColor(rColor);
+ rDevice.SetFillColor(rColor);
+
+ aRect.AdjustLeft(4 );
+ aRect.AdjustTop(4 );
+ aRect.AdjustRight( -4 );
+ aRect.AdjustBottom( -4 );
+
+ rDevice.DrawRect( aRect );
+
+ rDevice.SetLineColor(rOldLineColor);
+ rDevice.SetFillColor(rOldFillColor);
+}
+
+IMPL_LINK( SvxSuperContourDlg, PipetteClickHdl, ContourWindow&, rWnd, void )
+{
+ if ( rWnd.IsClickValid() )
+ {
+ const Color& rColor = rWnd.GetPipetteColor();
+
+ weld::WaitObject aWaitObj(&m_rDialog);
+
+ if( aGraphic.GetType() == GraphicType::Bitmap )
+ {
+ const tools::Long nTol = static_cast<tools::Long>(m_xMtfTolerance->get_value(FieldUnit::PERCENT) * 255 / 100);
+
+ Bitmap aMask = aGraphic.GetBitmapEx().GetBitmap().CreateMask( rColor, nTol );
+
+ if( aGraphic.IsTransparent() )
+ aMask.CombineOr( aGraphic.GetBitmapEx().GetAlpha() );
+
+ if( !aMask.IsEmpty() )
+ {
+ std::unique_ptr<weld::Builder> xBuilder(Application::CreateBuilder(&m_rDialog, "svx/ui/querynewcontourdialog.ui"));
+ std::unique_ptr<weld::MessageDialog> xQBox(xBuilder->weld_message_dialog("QueryNewContourDialog"));
+
+ bool bNewContour;
+
+ aRedoGraphic = Graphic();
+ aUndoGraphic = aGraphic;
+ Bitmap aBmp = aGraphic.GetBitmapEx().GetBitmap();
+ aGraphic = Graphic( BitmapEx( aBmp, aMask ) );
+ mnGrfChanged++;
+
+ bNewContour = (xQBox->run() == RET_YES);
+ rWnd.SetGraphic( aGraphic, bNewContour );
+
+ if( bNewContour )
+ aCreateIdle.Start();
+ }
+ }
+ }
+
+ m_xTbx1->set_item_active("TBI_PIPETTE", false);
+ rWnd.SetPipetteMode( false );
+ m_xStbStatusColor->Invalidate();
+}
+
+IMPL_LINK( SvxSuperContourDlg, WorkplaceClickHdl, ContourWindow&, rWnd, void )
+{
+ m_xTbx1->set_item_active("TBI_WORKPLACE", false);
+ m_xTbx1->set_item_active("TBI_SELECT", true);
+ rWnd.SetWorkplaceMode( false );
+
+ m_xContourWnd->QueueIdleUpdate();
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/dialog/charmap.cxx b/svx/source/dialog/charmap.cxx
new file mode 100644
index 000000000..4d8dd07b5
--- /dev/null
+++ b/svx/source/dialog/charmap.cxx
@@ -0,0 +1,1938 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <config_wasm_strip.h>
+
+#include <vcl/event.hxx>
+#include <vcl/fontcharmap.hxx>
+#include <vcl/svapp.hxx>
+#include <vcl/settings.hxx>
+#include <vcl/virdev.hxx>
+
+#include <svx/ucsubset.hxx>
+
+
+#include <svx/strings.hrc>
+
+#include <svx/charmap.hxx>
+#include <svx/dialmgr.hxx>
+
+#include <charmapacc.hxx>
+#include <uiobject.hxx>
+
+#include <com/sun/star/accessibility/AccessibleEventId.hpp>
+#include <com/sun/star/accessibility/AccessibleStateType.hpp>
+#include <com/sun/star/datatransfer/clipboard/XFlushableClipboard.hpp>
+#include <com/sun/star/datatransfer/clipboard/SystemClipboard.hpp>
+#include <officecfg/Office/Common.hxx>
+#include <comphelper/processfactory.hxx>
+#include <unicode/uchar.h>
+#include <vcl/textview.hxx>
+#include <rtl/ustrbuf.hxx>
+
+using namespace ::com::sun::star::accessibility;
+using namespace ::com::sun::star::uno;
+using namespace ::com::sun::star;
+
+sal_uInt32& SvxShowCharSet::getSelectedChar()
+{
+ static sal_uInt32 cSelectedChar = ' '; // keeps selected character over app lifetime
+ return cSelectedChar;
+}
+
+FactoryFunction SvxShowCharSet::GetUITestFactory() const
+{
+ return SvxShowCharSetUIObject::create;
+}
+
+SvxShowCharSet::SvxShowCharSet(std::unique_ptr<weld::ScrolledWindow> pScrolledWindow, const VclPtr<VirtualDevice>& rVirDev)
+ : mxVirDev(rVirDev)
+ , mxScrollArea(std::move(pScrolledWindow))
+ , nX(0)
+ , nY(0)
+ , maFontSize(0, 0)
+ , maPosition(0,0)
+ , mbRecalculateFont(true)
+ , mbUpdateForeground(true)
+ , mbUpdateBackground(true)
+{
+ init();
+}
+
+void SvxShowCharSet::SetDrawingArea(weld::DrawingArea* pDrawingArea)
+{
+ CustomWidgetController::SetDrawingArea(pDrawingArea);
+
+ Size aSize(COLUMN_COUNT * pDrawingArea->get_approximate_digit_width() * 5.25,
+ ROW_COUNT * pDrawingArea->get_text_height() * 2);
+
+ nX = aSize.Width() / COLUMN_COUNT;
+ nY = aSize.Height() / ROW_COUNT;
+
+ // tdf#121232 set a size request that will result in a 0 m_nXGap by default
+ mxScrollArea->set_size_request(COLUMN_COUNT * nX + mxScrollArea->get_scroll_thickness() + 2,
+ ROW_COUNT * nY);
+}
+
+void SvxShowCharSet::init()
+{
+ nSelectedIndex = -1; // TODO: move into init list when it is no longer static
+ m_nXGap = 0;
+ m_nYGap = 0;
+
+ mxScrollArea->connect_vadjustment_changed(LINK(this, SvxShowCharSet, VscrollHdl));
+ getFavCharacterList();
+ // other settings depend on selected font => see RecalculateFont
+
+ bDrag = false;
+}
+
+void SvxShowCharSet::Resize()
+{
+ mbRecalculateFont = true;
+}
+
+void SvxShowCharSet::GetFocus()
+{
+ SelectIndex(nSelectedIndex, true);
+}
+
+void SvxShowCharSet::LoseFocus()
+{
+ SelectIndex(nSelectedIndex);
+}
+
+bool SvxShowCharSet::MouseButtonDown(const MouseEvent& rMEvt)
+{
+ if ( rMEvt.IsLeft() )
+ {
+ if ( rMEvt.GetClicks() == 1 )
+ {
+ GrabFocus();
+ bDrag = true;
+ CaptureMouse();
+
+ int nIndex = PixelToMapIndex( rMEvt.GetPosPixel() );
+ // Fire the focus event
+ SelectIndex( nIndex, true);
+ }
+
+ if ( !(rMEvt.GetClicks() % 2) )
+ aDoubleClkHdl.Call( this );
+ }
+
+ if (rMEvt.IsRight())
+ {
+ Point aPosition (rMEvt.GetPosPixel());
+ maPosition = aPosition;
+ int nIndex = PixelToMapIndex( rMEvt.GetPosPixel() );
+ // Fire the focus event
+ SelectIndex( nIndex, true);
+ createContextMenu();
+ }
+
+ return true;
+}
+
+bool SvxShowCharSet::MouseButtonUp(const MouseEvent& rMEvt)
+{
+ if ( bDrag && rMEvt.IsLeft() )
+ {
+ // released mouse over character map
+ if ( tools::Rectangle(Point(), GetOutputSizePixel()).Contains(rMEvt.GetPosPixel()))
+ aSelectHdl.Call( this );
+ ReleaseMouse();
+ bDrag = false;
+ }
+
+ return true;
+}
+
+bool SvxShowCharSet::MouseMove(const MouseEvent& rMEvt)
+{
+ if ( rMEvt.IsLeft() && bDrag )
+ {
+ Point aPos = rMEvt.GetPosPixel();
+ Size aSize = GetOutputSizePixel();
+
+ if ( aPos.X() < 0 )
+ aPos.setX( 0 );
+ else if ( aPos.X() > aSize.Width()-5 )
+ aPos.setX( aSize.Width()-5 );
+ if ( aPos.Y() < 0 )
+ aPos.setY( 0 );
+ else if ( aPos.Y() > aSize.Height()-5 )
+ aPos.setY( aSize.Height()-5 );
+
+ int nIndex = PixelToMapIndex( aPos );
+ // Fire the focus event.
+ SelectIndex( nIndex, true );
+ }
+
+ return true;
+}
+
+sal_uInt16 SvxShowCharSet::GetRowPos(sal_uInt16 _nPos)
+{
+ return _nPos / COLUMN_COUNT ;
+}
+
+void SvxShowCharSet::getFavCharacterList()
+{
+ maFavCharList.clear();
+ maFavCharFontList.clear();
+ //retrieve recent character list
+ css::uno::Sequence< OUString > rFavCharList( officecfg::Office::Common::FavoriteCharacters::FavoriteCharacterList::get() );
+ comphelper::sequenceToContainer(maFavCharList, rFavCharList);
+
+ //retrieve recent character font list
+ css::uno::Sequence< OUString > rFavCharFontList( officecfg::Office::Common::FavoriteCharacters::FavoriteCharacterFontList::get() );
+ comphelper::sequenceToContainer(maFavCharFontList, rFavCharFontList);
+}
+
+bool SvxShowCharSet::isFavChar(const OUString& sTitle, const OUString& rFont)
+{
+ auto isFavCharTitleExists = std::any_of(maFavCharList.begin(),
+ maFavCharList.end(),
+ [sTitle] (const OUString & a) { return a == sTitle; });
+
+ auto isFavCharFontExists = std::any_of(maFavCharFontList.begin(),
+ maFavCharFontList.end(),
+ [rFont] (const OUString & a) { return a == rFont; });
+
+ // if Fav char to be added is already in list, return true
+ return isFavCharTitleExists && isFavCharFontExists;
+}
+
+void SvxShowCharSet::createContextMenu()
+{
+ std::unique_ptr<weld::Builder> xBuilder(Application::CreateBuilder(GetDrawingArea(), "svx/ui/charsetmenu.ui"));
+ std::unique_ptr<weld::Menu> xItemMenu(xBuilder->weld_menu("charsetmenu"));
+
+ sal_UCS4 cChar = GetSelectCharacter();
+ OUString aOUStr( &cChar, 1 );
+ if (isFavChar(aOUStr, mxVirDev->GetFont().GetFamilyName()) || maFavCharList.size() >= 16)
+ xItemMenu->set_visible("add", false);
+ else
+ xItemMenu->set_visible("remove", false);
+
+ ContextMenuSelect(xItemMenu->popup_at_rect(GetDrawingArea(), tools::Rectangle(maPosition, Size(1,1))));
+ GrabFocus();
+ Invalidate();
+}
+
+void SvxShowCharSet::ContextMenuSelect(std::string_view rIdent)
+{
+ sal_UCS4 cChar = GetSelectCharacter();
+ OUString aOUStr(&cChar, 1);
+
+ if (rIdent == "insert")
+ aDoubleClkHdl.Call(this);
+ else if (rIdent == "add" || rIdent == "remove")
+ {
+ updateFavCharacterList(aOUStr, mxVirDev->GetFont().GetFamilyName());
+ aFavClickHdl.Call(this);
+ }
+ else if (rIdent == "copy")
+ CopyToClipboard(aOUStr);
+}
+
+void SvxShowCharSet::CopyToClipboard(const OUString& rOUStr)
+{
+ css::uno::Reference<css::datatransfer::clipboard::XClipboard> xClipboard =
+ css::datatransfer::clipboard::SystemClipboard::create(comphelper::getProcessComponentContext());
+
+ if (!xClipboard.is())
+ return;
+
+ rtl::Reference<TETextDataObject> pDataObj = new TETextDataObject(rOUStr);
+
+ try
+ {
+ xClipboard->setContents( pDataObj, nullptr );
+
+ css::uno::Reference<css::datatransfer::clipboard::XFlushableClipboard> xFlushableClipboard(xClipboard, css::uno::UNO_QUERY);
+ if( xFlushableClipboard.is() )
+ xFlushableClipboard->flushClipboard();
+ }
+ catch( const css::uno::Exception& )
+ {
+ }
+}
+
+void SvxShowCharSet::updateFavCharacterList(const OUString& sTitle, const OUString& rFont)
+{
+ if(isFavChar(sTitle, rFont))
+ {
+ auto itChar = std::find(maFavCharList.begin(), maFavCharList.end(), sTitle);
+ auto itChar2 = std::find(maFavCharFontList.begin(), maFavCharFontList.end(), rFont);
+
+ // if Fav char to be added is already in list, remove it
+ if( itChar != maFavCharList.end() && itChar2 != maFavCharFontList.end() )
+ {
+ maFavCharList.erase( itChar );
+ maFavCharFontList.erase( itChar2);
+ }
+
+ css::uno::Sequence< OUString > aFavCharList(maFavCharList.size());
+ auto aFavCharListRange = asNonConstRange(aFavCharList);
+ css::uno::Sequence< OUString > aFavCharFontList(maFavCharFontList.size());
+ auto aFavCharFontListRange = asNonConstRange(aFavCharFontList);
+
+ for (size_t i = 0; i < maFavCharList.size(); ++i)
+ {
+ aFavCharListRange[i] = maFavCharList[i];
+ aFavCharFontListRange[i] = maFavCharFontList[i];
+ }
+
+ std::shared_ptr<comphelper::ConfigurationChanges> batch(comphelper::ConfigurationChanges::create());
+ officecfg::Office::Common::FavoriteCharacters::FavoriteCharacterList::set(aFavCharList, batch);
+ officecfg::Office::Common::FavoriteCharacters::FavoriteCharacterFontList::set(aFavCharFontList, batch);
+ batch->commit();
+ return;
+ }
+
+ auto itChar = std::find(maFavCharList.begin(), maFavCharList.end(), sTitle);
+ auto itChar2 = std::find(maFavCharFontList.begin(), maFavCharFontList.end(), rFont);
+
+ // if Fav char to be added is already in list, remove it
+ if( itChar != maFavCharList.end() && itChar2 != maFavCharFontList.end() )
+ {
+ maFavCharList.erase( itChar );
+ maFavCharFontList.erase( itChar2);
+ }
+
+ if (maFavCharList.size() == 16)
+ {
+ maFavCharList.pop_back();
+ maFavCharFontList.pop_back();
+ }
+
+ maFavCharList.push_back(sTitle);
+ maFavCharFontList.push_back(rFont);
+
+ css::uno::Sequence< OUString > aFavCharList(maFavCharList.size());
+ auto aFavCharListRange = asNonConstRange(aFavCharList);
+ css::uno::Sequence< OUString > aFavCharFontList(maFavCharFontList.size());
+ auto aFavCharFontListRange = asNonConstRange(aFavCharFontList);
+
+ for (size_t i = 0; i < maFavCharList.size(); ++i)
+ {
+ aFavCharListRange[i] = maFavCharList[i];
+ aFavCharFontListRange[i] = maFavCharFontList[i];
+ }
+
+ std::shared_ptr<comphelper::ConfigurationChanges> batch(comphelper::ConfigurationChanges::create());
+ officecfg::Office::Common::FavoriteCharacters::FavoriteCharacterList::set(aFavCharList, batch);
+ officecfg::Office::Common::FavoriteCharacters::FavoriteCharacterFontList::set(aFavCharFontList, batch);
+ batch->commit();
+}
+
+sal_uInt16 SvxShowCharSet::GetColumnPos(sal_uInt16 _nPos)
+{
+ return _nPos % COLUMN_COUNT ;
+}
+
+int SvxShowCharSet::FirstInView() const
+{
+ return mxScrollArea->vadjustment_get_value() * COLUMN_COUNT;
+}
+
+int SvxShowCharSet::LastInView() const
+{
+ sal_uInt32 nIndex = FirstInView();
+ nIndex += ROW_COUNT * COLUMN_COUNT - 1;
+ sal_uInt32 nCompare = mxFontCharMap->GetCharCount() - 1;
+ if (nIndex > nCompare)
+ nIndex = nCompare;
+ return nIndex;
+}
+
+Point SvxShowCharSet::MapIndexToPixel( int nIndex ) const
+{
+ const int nBase = FirstInView();
+ int x = ((nIndex - nBase) % COLUMN_COUNT) * nX;
+ int y = ((nIndex - nBase) / COLUMN_COUNT) * nY;
+ return Point( x + m_nXGap, y + m_nYGap );
+}
+
+
+int SvxShowCharSet::PixelToMapIndex( const Point& point) const
+{
+ int nBase = FirstInView();
+ assert(nX != 0);
+ int x = nX == 0 ? 0 : (point.X() - m_nXGap)/nX;
+ assert(nY != 0);
+ int y = nY == 0 ? 0 : (point.Y() - m_nYGap)/nY;
+ return (nBase + x + y * COLUMN_COUNT);
+}
+
+bool SvxShowCharSet::KeyInput(const KeyEvent& rKEvt)
+{
+ vcl::KeyCode aCode = rKEvt.GetKeyCode();
+
+ if (aCode.GetModifier())
+ return false;
+
+ bool bRet = true;
+
+ int tmpSelected = nSelectedIndex;
+
+ switch (aCode.GetCode())
+ {
+ case KEY_SPACE:
+ aSelectHdl.Call( this );
+ break;
+ case KEY_LEFT:
+ --tmpSelected;
+ break;
+ case KEY_RIGHT:
+ ++tmpSelected;
+ break;
+ case KEY_UP:
+ tmpSelected -= COLUMN_COUNT;
+ break;
+ case KEY_DOWN:
+ tmpSelected += COLUMN_COUNT;
+ break;
+ case KEY_PAGEUP:
+ tmpSelected -= ROW_COUNT * COLUMN_COUNT;
+ break;
+ case KEY_PAGEDOWN:
+ tmpSelected += ROW_COUNT * COLUMN_COUNT;
+ break;
+ case KEY_HOME:
+ tmpSelected = 0;
+ break;
+ case KEY_END:
+ tmpSelected = mxFontCharMap->GetCharCount() - 1;
+ break;
+ case KEY_TAB: // some fonts have a character at these unicode control codes
+ case KEY_ESCAPE:
+ case KEY_RETURN:
+ tmpSelected = - 1; // mark as invalid
+ bRet = false;
+ break;
+ default:
+ {
+ sal_UCS4 cChar = rKEvt.GetCharCode();
+ sal_UCS4 cNext = mxFontCharMap->GetNextChar(cChar - 1);
+ tmpSelected = mxFontCharMap->GetIndexFromChar(cNext);
+ if (tmpSelected < 0 || (cChar != cNext))
+ {
+ tmpSelected = - 1; // mark as invalid
+ bRet = false;
+ }
+ break;
+ }
+ }
+
+ if ( tmpSelected >= 0 )
+ {
+ SelectIndex( tmpSelected, true );
+ aPreSelectHdl.Call( this );
+ }
+
+ return bRet;
+}
+
+void SvxShowCharSet::Paint(vcl::RenderContext& rRenderContext, const tools::Rectangle&)
+{
+ InitSettings(rRenderContext);
+ RecalculateFont(rRenderContext);
+ DrawChars_Impl(rRenderContext, FirstInView(), LastInView());
+}
+
+void SvxShowCharSet::SetFont( const vcl::Font& rFont )
+{
+ maFont = rFont;
+ mbRecalculateFont = true;
+ Invalidate();
+}
+
+void SvxShowCharSet::DeSelect()
+{
+ Invalidate();
+}
+
+// stretch a grid rectangle if it's at the edge to fill unused space
+tools::Rectangle SvxShowCharSet::getGridRectangle(const Point &rPointUL, const Size &rOutputSize) const
+{
+ tools::Long x = rPointUL.X() - 1;
+ tools::Long y = rPointUL.Y() - 1;
+ Point aPointUL(x+1, y+1);
+ Size aGridSize(nX-1, nY-1);
+
+ tools::Long nXDistFromLeft = x - m_nXGap;
+ if (nXDistFromLeft <= 1)
+ {
+ aPointUL.setX( 1 );
+ aGridSize.AdjustWidth(m_nXGap + nXDistFromLeft );
+ }
+ tools::Long nXDistFromRight = rOutputSize.Width() - m_nXGap - nX - x;
+ if (nXDistFromRight <= 1)
+ aGridSize.AdjustWidth(m_nXGap + nXDistFromRight );
+
+ tools::Long nXDistFromTop = y - m_nYGap;
+ if (nXDistFromTop <= 1)
+ {
+ aPointUL.setY( 1 );
+ aGridSize.AdjustHeight(m_nYGap + nXDistFromTop );
+ }
+ tools::Long nXDistFromBottom = rOutputSize.Height() - m_nYGap - nY - y;
+ if (nXDistFromBottom <= 1)
+ aGridSize.AdjustHeight(m_nYGap + nXDistFromBottom );
+
+ return tools::Rectangle(aPointUL, aGridSize);
+}
+
+void SvxShowCharSet::DrawChars_Impl(vcl::RenderContext& rRenderContext, int n1, int n2)
+{
+ if (n1 > LastInView() || n2 < FirstInView())
+ return;
+
+ Size aOutputSize(GetOutputSizePixel());
+
+ const StyleSettings& rStyleSettings = Application::GetSettings().GetStyleSettings();
+ const Color aWindowTextColor(rStyleSettings.GetFieldTextColor());
+ Color aHighlightColor(rStyleSettings.GetHighlightColor());
+ Color aHighlightTextColor(rStyleSettings.GetHighlightTextColor());
+ Color aFaceColor(rStyleSettings.GetFaceColor());
+ Color aLightColor(rStyleSettings.GetLightColor());
+ Color aShadowColor(rStyleSettings.GetShadowColor());
+
+ int i;
+ rRenderContext.SetLineColor(aShadowColor);
+ for (i = 1; i < COLUMN_COUNT; ++i)
+ {
+ rRenderContext.DrawLine(Point(nX * i + m_nXGap, 0),
+ Point(nX * i + m_nXGap, aOutputSize.Height()));
+ }
+ for (i = 1; i < ROW_COUNT; ++i)
+ {
+ rRenderContext.DrawLine(Point(0, nY * i + m_nYGap),
+ Point(aOutputSize.Width(), nY * i + m_nYGap));
+ }
+
+ int nTextHeight = rRenderContext.GetTextHeight();
+ tools::Rectangle aBoundRect;
+ for (i = n1; i <= n2; ++i)
+ {
+ Point pix = MapIndexToPixel(i);
+ int x = pix.X();
+ int y = pix.Y();
+
+ sal_UCS4 nChar = mxFontCharMap->GetCharFromIndex(i);
+ OUString aCharStr(&nChar, 1);
+ int nTextWidth = rRenderContext.GetTextWidth(aCharStr);
+ int tx = x + (nX - nTextWidth + 1) / 2;
+ int ty = y + (nY - nTextHeight + 1) / 2;
+ Point aPointTxTy(tx, ty);
+
+ // adjust position before it gets out of bounds
+ if (rRenderContext.GetTextBoundRect(aBoundRect, aCharStr) && !aBoundRect.IsEmpty())
+ {
+ // zero advance width => use ink width to center glyph
+ if (!nTextWidth)
+ {
+ aPointTxTy.setX( x - aBoundRect.Left() + (nX - aBoundRect.GetWidth() + 1) / 2 );
+ }
+
+ aBoundRect += aPointTxTy;
+
+ // shift back vertically if needed
+ int nYLDelta = aBoundRect.Top() - y;
+ int nYHDelta = (y + nY) - aBoundRect.Bottom();
+ if (nYLDelta <= 0)
+ aPointTxTy.AdjustY( -(nYLDelta - 1) );
+ else if (nYHDelta <= 0)
+ aPointTxTy.AdjustY(nYHDelta - 1 );
+
+ // shift back horizontally if needed
+ int nXLDelta = aBoundRect.Left() - x;
+ int nXHDelta = (x + nX) - aBoundRect.Right();
+ if (nXLDelta <= 0)
+ aPointTxTy.AdjustX( -(nXLDelta - 1) );
+ else if (nXHDelta <= 0)
+ aPointTxTy.AdjustX(nXHDelta - 1 );
+ }
+
+ // tdf#109214 - highlight the favorite characters
+ if (isFavChar(aCharStr, mxVirDev->GetFont().GetFamilyName()))
+ {
+ const Color aLineCol = rRenderContext.GetLineColor();
+ rRenderContext.SetLineColor(aHighlightColor);
+ rRenderContext.SetFillColor(COL_TRANSPARENT);
+ // Outer border
+ rRenderContext.DrawRect(tools::Rectangle(Point(x - 1, y - 1), Size(nX + 3, nY + 3)));
+ // Inner border
+ rRenderContext.DrawRect(tools::Rectangle(Point(x, y), Size(nX + 1, nY + 1)));
+ rRenderContext.SetLineColor(aLineCol);
+ }
+
+ Color aTextCol = rRenderContext.GetTextColor();
+ if (i != nSelectedIndex)
+ {
+ rRenderContext.SetTextColor(aWindowTextColor);
+ rRenderContext.DrawText(aPointTxTy, aCharStr);
+ }
+ else
+ {
+ Color aLineCol = rRenderContext.GetLineColor();
+ Color aFillCol = rRenderContext.GetFillColor();
+ rRenderContext.SetLineColor();
+ Point aPointUL(x + 1, y + 1);
+ if (HasFocus())
+ {
+ rRenderContext.SetFillColor(aHighlightColor);
+ rRenderContext.DrawRect(getGridRectangle(aPointUL, aOutputSize));
+
+ rRenderContext.SetTextColor(aHighlightTextColor);
+ rRenderContext.DrawText(aPointTxTy, aCharStr);
+ }
+ else
+ {
+ rRenderContext.SetFillColor(aFaceColor);
+ rRenderContext.DrawRect(getGridRectangle(aPointUL, aOutputSize));
+
+ rRenderContext.SetLineColor(aLightColor);
+ rRenderContext.DrawLine(aPointUL, Point(x + nX - 1, y + 1));
+ rRenderContext.DrawLine(aPointUL, Point(x + 1, y + nY - 1));
+
+ rRenderContext.SetLineColor(aShadowColor);
+ rRenderContext.DrawLine(Point(x + 1, y + nY - 1), Point(x + nX - 1, y + nY - 1));
+ rRenderContext.DrawLine(Point(x + nX - 1, y + nY - 1), Point(x + nX - 1, y + 1));
+
+ rRenderContext.DrawText(aPointTxTy, aCharStr);
+ }
+ rRenderContext.SetLineColor(aLineCol);
+ rRenderContext.SetFillColor(aFillCol);
+ }
+ rRenderContext.SetTextColor(aTextCol);
+ }
+}
+
+
+void SvxShowCharSet::InitSettings(vcl::RenderContext& rRenderContext)
+{
+ const StyleSettings& rStyleSettings = rRenderContext.GetSettings().GetStyleSettings();
+
+ if (mbUpdateForeground)
+ {
+ rRenderContext.SetTextColor(rStyleSettings.GetDialogTextColor());
+ mbUpdateForeground = false;
+ }
+
+ if (mbUpdateBackground)
+ {
+ rRenderContext.SetBackground(rStyleSettings.GetWindowColor());
+ rRenderContext.Erase();
+ mbUpdateBackground = false;
+ }
+
+ vcl::Font aFont(maFont);
+ aFont.SetWeight(WEIGHT_LIGHT);
+ aFont.SetAlignment(ALIGN_TOP);
+ aFont.SetFontSize(maFontSize);
+ aFont.SetTransparent(true);
+ rRenderContext.SetFont(aFont);
+}
+
+sal_UCS4 SvxShowCharSet::GetSelectCharacter() const
+{
+ if( nSelectedIndex >= 0 )
+ getSelectedChar() = mxFontCharMap->GetCharFromIndex( nSelectedIndex );
+ return getSelectedChar();
+}
+
+
+void SvxShowCharSet::RecalculateFont(vcl::RenderContext& rRenderContext)
+{
+ if (!mbRecalculateFont)
+ return;
+
+ // save last selected unicode
+ if (nSelectedIndex >= 0)
+ getSelectedChar() = mxFontCharMap->GetCharFromIndex(nSelectedIndex);
+
+ Size aSize(GetOutputSizePixel());
+
+ vcl::Font aFont = maFont;
+ aFont.SetWeight(WEIGHT_LIGHT);
+ aFont.SetAlignment(ALIGN_TOP);
+ int nFontHeight = (aSize.Height() - 5) * 2 / (3 * ROW_COUNT);
+ maFontSize = rRenderContext.PixelToLogic(Size(0, nFontHeight));
+ aFont.SetFontSize(maFontSize);
+ aFont.SetTransparent(true);
+ rRenderContext.SetFont(aFont);
+ rRenderContext.GetFontCharMap(mxFontCharMap);
+ getFavCharacterList();
+
+ nX = aSize.Width() / COLUMN_COUNT;
+ nY = aSize.Height() / ROW_COUNT;
+
+ const int nLastRow = (mxFontCharMap->GetCharCount() - 1 + COLUMN_COUNT) / COLUMN_COUNT;
+ mxScrollArea->vadjustment_configure(mxScrollArea->vadjustment_get_value(), 0, nLastRow, 1, ROW_COUNT - 1, ROW_COUNT);
+
+ // restore last selected unicode
+ int nMapIndex = mxFontCharMap->GetIndexFromChar(getSelectedChar());
+ if (nMapIndex != nSelectedIndex)
+ SelectIndex(nMapIndex);
+
+ // rearrange CharSet element in sync with nX- and nY-multiples
+ Size aDrawSize(nX * COLUMN_COUNT, nY * ROW_COUNT);
+ m_nXGap = (aSize.Width() - aDrawSize.Width()) / 2;
+ m_nYGap = (aSize.Height() - aDrawSize.Height()) / 2;
+
+ mbRecalculateFont = false;
+}
+
+void SvxShowCharSet::SelectIndex(int nNewIndex, bool bFocus)
+{
+ if (!mxFontCharMap.is())
+ RecalculateFont(*mxVirDev);
+
+ if( nNewIndex < 0 )
+ {
+ // need to scroll see closest unicode
+ sal_uInt32 cPrev = mxFontCharMap->GetPrevChar( getSelectedChar() );
+ int nMapIndex = mxFontCharMap->GetIndexFromChar( cPrev );
+ int nNewPos = nMapIndex / COLUMN_COUNT;
+ mxScrollArea->vadjustment_set_value(nNewPos);
+ nSelectedIndex = bFocus ? nMapIndex+1 : -1;
+ Invalidate();
+ }
+ else if( nNewIndex < FirstInView() )
+ {
+ // need to scroll up to see selected item
+ int nOldPos = mxScrollArea->vadjustment_get_value();
+ int nDelta = (FirstInView() - nNewIndex + COLUMN_COUNT-1) / COLUMN_COUNT;
+ mxScrollArea->vadjustment_set_value(nOldPos - nDelta);
+ nSelectedIndex = nNewIndex;
+ Invalidate();
+ }
+ else if( nNewIndex > LastInView() )
+ {
+ // need to scroll down to see selected item
+ int nOldPos = mxScrollArea->vadjustment_get_value();
+ int nDelta = (nNewIndex - LastInView() + COLUMN_COUNT) / COLUMN_COUNT;
+ mxScrollArea->vadjustment_set_value(nOldPos + nDelta);
+ if( nNewIndex < mxFontCharMap->GetCharCount() )
+ {
+ nSelectedIndex = nNewIndex;
+ Invalidate();
+ }
+ else if (nOldPos != mxScrollArea->vadjustment_get_value())
+ {
+ Invalidate();
+ }
+ }
+ else
+ {
+ nSelectedIndex = nNewIndex;
+ Invalidate();
+ }
+
+ if( nSelectedIndex >= 0 )
+ {
+ getSelectedChar() = mxFontCharMap->GetCharFromIndex( nSelectedIndex );
+#if !ENABLE_WASM_STRIP_ACCESSIBILITY
+ if( m_xAccessible.is() )
+ {
+ svx::SvxShowCharSetItem* pItem = ImplGetItem(nSelectedIndex);
+ // Don't fire the focus event.
+ if ( bFocus )
+ m_xAccessible->fireEvent( AccessibleEventId::ACTIVE_DESCENDANT_CHANGED, Any(), Any(pItem->GetAccessible()) ); // this call assures that m_pItem is set
+ else
+ m_xAccessible->fireEvent( AccessibleEventId::ACTIVE_DESCENDANT_CHANGED_NOFOCUS, Any(), Any(pItem->GetAccessible()) ); // this call assures that m_pItem is set
+
+ assert(pItem->m_xItem.is() && "No accessible created!");
+ Any aOldAny, aNewAny;
+ aNewAny <<= AccessibleStateType::FOCUSED;
+ // Don't fire the focus event.
+ if ( bFocus )
+ pItem->m_xItem->fireEvent( AccessibleEventId::STATE_CHANGED, aOldAny, aNewAny );
+
+ aNewAny <<= AccessibleStateType::SELECTED;
+ pItem->m_xItem->fireEvent( AccessibleEventId::STATE_CHANGED, aOldAny, aNewAny );
+ }
+#endif
+ }
+ aHighHdl.Call( this );
+}
+
+void SvxShowCharSet::OutputIndex( int nNewIndex )
+{
+ SelectIndex( nNewIndex, true );
+ aSelectHdl.Call( this );
+}
+
+
+void SvxShowCharSet::SelectCharacter( sal_UCS4 cNew )
+{
+ if ( !mxFontCharMap.is() )
+ RecalculateFont(*mxVirDev);
+
+ // get next available char of current font
+ sal_UCS4 cNext = mxFontCharMap->GetNextChar( (cNew > 0) ? cNew - 1 : cNew );
+
+ int nMapIndex = mxFontCharMap->GetIndexFromChar( cNext );
+ SelectIndex( nMapIndex );
+ // move selected item to top row if not in focus
+ mxScrollArea->vadjustment_set_value(nMapIndex / COLUMN_COUNT);
+ Invalidate();
+}
+
+IMPL_LINK_NOARG(SvxShowCharSet, VscrollHdl, weld::ScrolledWindow&, void)
+{
+ if( nSelectedIndex < FirstInView() )
+ {
+ SelectIndex( FirstInView() + (nSelectedIndex % COLUMN_COUNT) );
+ }
+ else if( nSelectedIndex > LastInView() )
+ {
+#if !ENABLE_WASM_STRIP_ACCESSIBILITY
+ if( m_xAccessible.is() )
+ {
+ css::uno::Any aOldAny, aNewAny;
+ int nLast = LastInView();
+ for ( ; nLast != nSelectedIndex; ++nLast)
+ {
+ aOldAny <<= ImplGetItem(nLast)->GetAccessible();
+ m_xAccessible ->fireEvent( AccessibleEventId::CHILD, aOldAny, aNewAny );
+ }
+ }
+#endif
+ SelectIndex( (LastInView() - COLUMN_COUNT + 1) + (nSelectedIndex % COLUMN_COUNT) );
+ }
+
+ Invalidate();
+}
+
+SvxShowCharSet::~SvxShowCharSet()
+{
+#if !ENABLE_WASM_STRIP_ACCESSIBILITY
+ if (m_xAccessible.is())
+ {
+ m_aItems.clear();
+ m_xAccessible->clearCharSetControl();
+ m_xAccessible.clear();
+ }
+#endif
+}
+
+css::uno::Reference< XAccessible > SvxShowCharSet::CreateAccessible()
+{
+#if !ENABLE_WASM_STRIP_ACCESSIBILITY
+ OSL_ENSURE(!m_xAccessible.is(),"Accessible already created!");
+ m_xAccessible = new svx::SvxShowCharSetAcc(this);
+#endif
+ return m_xAccessible;
+}
+
+svx::SvxShowCharSetItem* SvxShowCharSet::ImplGetItem( int _nPos )
+{
+ ItemsMap::iterator aFind = m_aItems.find(_nPos);
+ if ( aFind == m_aItems.end() )
+ {
+#if !ENABLE_WASM_STRIP_ACCESSIBILITY
+ OSL_ENSURE(m_xAccessible.is(), "Who wants to create a child of my table without a parent?");
+#endif
+ auto xItem = std::make_shared<svx::SvxShowCharSetItem>(*this,
+ m_xAccessible.get(), sal::static_int_cast< sal_uInt16 >(_nPos));
+ aFind = m_aItems.emplace(_nPos, xItem).first;
+ OUStringBuffer buf;
+ buf.appendUtf32( mxFontCharMap->GetCharFromIndex( _nPos ) );
+ aFind->second->maText = buf.makeStringAndClear();
+ Point pix = MapIndexToPixel( _nPos );
+ aFind->second->maRect = tools::Rectangle( Point( pix.X() + 1, pix.Y() + 1 ), Size(nX-1,nY-1) );
+ }
+
+ return aFind->second.get();
+}
+
+sal_Int32 SvxShowCharSet::getMaxCharCount() const
+{
+ return mxFontCharMap->GetCharCount();
+}
+
+FontCharMapRef const & SvxShowCharSet::GetFontCharMap()
+{
+ RecalculateFont(*mxVirDev);
+ return mxFontCharMap;
+}
+
+// TODO: should be moved into Font Attributes stuff
+// we let it mature here though because it is currently the only use
+
+SubsetMap::SubsetMap( const FontCharMapRef& rxFontCharMap )
+{
+ InitList();
+ ApplyCharMap(rxFontCharMap);
+}
+
+const SubsetVec& SubsetMap::GetSubsetMap() const
+{
+ return maSubsets;
+}
+
+const Subset* SubsetMap::GetSubsetByUnicode( sal_UCS4 cChar ) const
+{
+ for (auto const& subset : maSubsets)
+ if( (subset.GetRangeMin() <= cChar) && (cChar <= subset.GetRangeMax()) )
+ return &subset;
+ return nullptr;
+}
+
+inline Subset::Subset(sal_UCS4 nMin, sal_UCS4 nMax, const OUString& rName)
+: mnRangeMin(nMin), mnRangeMax(nMax), maRangeName(rName)
+{
+}
+
+void SubsetMap::InitList()
+{
+ static SubsetVec s_aAllSubsets = []()
+ {
+ SubsetVec aAllSubsets;
+ //I wish icu had a way to give me the block ranges
+ for (int i = UBLOCK_BASIC_LATIN; i < UBLOCK_COUNT; ++i)
+ {
+ UBlockCode eBlock = static_cast<UBlockCode>(i);
+ switch (eBlock)
+ {
+ case UBLOCK_NO_BLOCK:
+ case UBLOCK_INVALID_CODE:
+ case UBLOCK_COUNT:
+ case UBLOCK_HIGH_SURROGATES:
+ case UBLOCK_HIGH_PRIVATE_USE_SURROGATES:
+ case UBLOCK_LOW_SURROGATES:
+ break;
+ case UBLOCK_BASIC_LATIN:
+ aAllSubsets.emplace_back( 0x0000, 0x007F, SvxResId(RID_SUBSETSTR_BASIC_LATIN) );
+ break;
+ case UBLOCK_LATIN_1_SUPPLEMENT:
+ aAllSubsets.emplace_back( 0x0080, 0x00FF, SvxResId(RID_SUBSETSTR_LATIN_1) );
+ break;
+ case UBLOCK_LATIN_EXTENDED_A:
+ aAllSubsets.emplace_back( 0x0100, 0x017F, SvxResId(RID_SUBSETSTR_LATIN_EXTENDED_A) );
+ break;
+ case UBLOCK_LATIN_EXTENDED_B:
+ aAllSubsets.emplace_back( 0x0180, 0x024F, SvxResId(RID_SUBSETSTR_LATIN_EXTENDED_B) );
+ break;
+ case UBLOCK_IPA_EXTENSIONS:
+ aAllSubsets.emplace_back( 0x0250, 0x02AF, SvxResId(RID_SUBSETSTR_IPA_EXTENSIONS) );
+ break;
+ case UBLOCK_SPACING_MODIFIER_LETTERS:
+ aAllSubsets.emplace_back( 0x02B0, 0x02FF, SvxResId(RID_SUBSETSTR_SPACING_MODIFIERS) );
+ break;
+ case UBLOCK_COMBINING_DIACRITICAL_MARKS:
+ aAllSubsets.emplace_back( 0x0300, 0x036F, SvxResId(RID_SUBSETSTR_COMB_DIACRITICAL) );
+ break;
+ case UBLOCK_GREEK:
+ aAllSubsets.emplace_back( 0x0370, 0x03FF, SvxResId(RID_SUBSETSTR_BASIC_GREEK) );
+ break;
+ case UBLOCK_CYRILLIC:
+ aAllSubsets.emplace_back( 0x0400, 0x04FF, SvxResId(RID_SUBSETSTR_CYRILLIC) );
+ break;
+ case UBLOCK_ARMENIAN:
+ aAllSubsets.emplace_back( 0x0530, 0x058F, SvxResId(RID_SUBSETSTR_ARMENIAN) );
+ break;
+ case UBLOCK_HEBREW:
+ aAllSubsets.emplace_back( 0x0590, 0x05FF, SvxResId(RID_SUBSETSTR_BASIC_HEBREW) );
+ break;
+ case UBLOCK_ARABIC:
+ aAllSubsets.emplace_back( 0x0600, 0x065F, SvxResId(RID_SUBSETSTR_BASIC_ARABIC) );
+ break;
+ case UBLOCK_SYRIAC:
+ aAllSubsets.emplace_back( 0x0700, 0x074F, SvxResId(RID_SUBSETSTR_SYRIAC) );
+ break;
+ case UBLOCK_THAANA:
+ aAllSubsets.emplace_back( 0x0780, 0x07BF, SvxResId(RID_SUBSETSTR_THAANA) );
+ break;
+ case UBLOCK_DEVANAGARI:
+ aAllSubsets.emplace_back( 0x0900, 0x097F, SvxResId(RID_SUBSETSTR_DEVANAGARI) );
+ break;
+ case UBLOCK_BENGALI:
+ aAllSubsets.emplace_back( 0x0980, 0x09FF, SvxResId(RID_SUBSETSTR_BENGALI) );
+ break;
+ case UBLOCK_GURMUKHI:
+ aAllSubsets.emplace_back( 0x0A00, 0x0A7F, SvxResId(RID_SUBSETSTR_GURMUKHI) );
+ break;
+ case UBLOCK_GUJARATI:
+ aAllSubsets.emplace_back( 0x0A80, 0x0AFF, SvxResId(RID_SUBSETSTR_GUJARATI) );
+ break;
+ case UBLOCK_ORIYA:
+ aAllSubsets.emplace_back( 0x0B00, 0x0B7F, SvxResId(RID_SUBSETSTR_ODIA) );
+ break;
+ case UBLOCK_TAMIL:
+ aAllSubsets.emplace_back( 0x0B80, 0x0BFF, SvxResId(RID_SUBSETSTR_TAMIL) );
+ break;
+ case UBLOCK_TELUGU:
+ aAllSubsets.emplace_back( 0x0C00, 0x0C7F, SvxResId(RID_SUBSETSTR_TELUGU) );
+ break;
+ case UBLOCK_KANNADA:
+ aAllSubsets.emplace_back( 0x0C80, 0x0CFF, SvxResId(RID_SUBSETSTR_KANNADA) );
+ break;
+ case UBLOCK_MALAYALAM:
+ aAllSubsets.emplace_back( 0x0D00, 0x0D7F, SvxResId(RID_SUBSETSTR_MALAYALAM) );
+ break;
+ case UBLOCK_SINHALA:
+ aAllSubsets.emplace_back( 0x0D80, 0x0DFF, SvxResId(RID_SUBSETSTR_SINHALA) );
+ break;
+ case UBLOCK_THAI:
+ aAllSubsets.emplace_back( 0x0E00, 0x0E7F, SvxResId(RID_SUBSETSTR_THAI) );
+ break;
+ case UBLOCK_LAO:
+ aAllSubsets.emplace_back( 0x0E80, 0x0EFF, SvxResId(RID_SUBSETSTR_LAO) );
+ break;
+ case UBLOCK_TIBETAN:
+ aAllSubsets.emplace_back( 0x0F00, 0x0FBF, SvxResId(RID_SUBSETSTR_TIBETAN) );
+ break;
+ case UBLOCK_MYANMAR:
+ aAllSubsets.emplace_back( 0x1000, 0x109F, SvxResId(RID_SUBSETSTR_MYANMAR) );
+ break;
+ case UBLOCK_GEORGIAN:
+ aAllSubsets.emplace_back( 0x10A0, 0x10FF, SvxResId(RID_SUBSETSTR_BASIC_GEORGIAN) );
+ break;
+ case UBLOCK_HANGUL_JAMO:
+ aAllSubsets.emplace_back( 0x1100, 0x11FF, SvxResId(RID_SUBSETSTR_HANGUL_JAMO) );
+ break;
+ case UBLOCK_ETHIOPIC:
+ aAllSubsets.emplace_back( 0x1200, 0x137F, SvxResId(RID_SUBSETSTR_ETHIOPIC) );
+ break;
+ case UBLOCK_CHEROKEE:
+ aAllSubsets.emplace_back( 0x13A0, 0x13FF, SvxResId(RID_SUBSETSTR_CHEROKEE) );
+ break;
+ case UBLOCK_UNIFIED_CANADIAN_ABORIGINAL_SYLLABICS:
+ aAllSubsets.emplace_back( 0x1400, 0x167F, SvxResId(RID_SUBSETSTR_CANADIAN_ABORIGINAL) );
+ break;
+ case UBLOCK_OGHAM:
+ aAllSubsets.emplace_back( 0x1680, 0x169F, SvxResId(RID_SUBSETSTR_OGHAM) );
+ break;
+ case UBLOCK_RUNIC:
+ aAllSubsets.emplace_back( 0x16A0, 0x16F0, SvxResId(RID_SUBSETSTR_RUNIC) );
+ break;
+ case UBLOCK_KHMER:
+ aAllSubsets.emplace_back( 0x1780, 0x17FF, SvxResId(RID_SUBSETSTR_KHMER) );
+ break;
+ case UBLOCK_MONGOLIAN:
+ aAllSubsets.emplace_back( 0x1800, 0x18AF, SvxResId(RID_SUBSETSTR_MONGOLIAN) );
+ break;
+ case UBLOCK_LATIN_EXTENDED_ADDITIONAL:
+ aAllSubsets.emplace_back( 0x1E00, 0x1EFF, SvxResId(RID_SUBSETSTR_LATIN_EXTENDED_ADDS) );
+ break;
+ case UBLOCK_GREEK_EXTENDED:
+ aAllSubsets.emplace_back( 0x1F00, 0x1FFF, SvxResId(RID_SUBSETSTR_GREEK_EXTENDED) );
+ break;
+ case UBLOCK_GENERAL_PUNCTUATION:
+ aAllSubsets.emplace_back( 0x2000, 0x206F, SvxResId(RID_SUBSETSTR_GENERAL_PUNCTUATION) );
+ break;
+ case UBLOCK_SUPERSCRIPTS_AND_SUBSCRIPTS:
+ aAllSubsets.emplace_back( 0x2070, 0x209F, SvxResId(RID_SUBSETSTR_SUB_SUPER_SCRIPTS) );
+ break;
+ case UBLOCK_CURRENCY_SYMBOLS:
+ aAllSubsets.emplace_back( 0x20A0, 0x20CF, SvxResId(RID_SUBSETSTR_CURRENCY_SYMBOLS) );
+ break;
+ case UBLOCK_COMBINING_MARKS_FOR_SYMBOLS:
+ aAllSubsets.emplace_back( 0x20D0, 0x20FF, SvxResId(RID_SUBSETSTR_COMB_DIACRITIC_SYMS) );
+ break;
+ case UBLOCK_LETTERLIKE_SYMBOLS:
+ aAllSubsets.emplace_back( 0x2100, 0x214F, SvxResId(RID_SUBSETSTR_LETTERLIKE_SYMBOLS) );
+ break;
+ case UBLOCK_NUMBER_FORMS:
+ aAllSubsets.emplace_back( 0x2150, 0x218F, SvxResId(RID_SUBSETSTR_NUMBER_FORMS) );
+ break;
+ case UBLOCK_ARROWS:
+ aAllSubsets.emplace_back( 0x2190, 0x21FF, SvxResId(RID_SUBSETSTR_ARROWS) );
+ break;
+ case UBLOCK_MATHEMATICAL_OPERATORS:
+ aAllSubsets.emplace_back( 0x2200, 0x22FF, SvxResId(RID_SUBSETSTR_MATH_OPERATORS) );
+ break;
+ case UBLOCK_MISCELLANEOUS_TECHNICAL:
+ aAllSubsets.emplace_back( 0x2300, 0x23FF, SvxResId(RID_SUBSETSTR_MISC_TECHNICAL) );
+ break;
+ case UBLOCK_CONTROL_PICTURES:
+ aAllSubsets.emplace_back( 0x2400, 0x243F, SvxResId(RID_SUBSETSTR_CONTROL_PICTURES) );
+ break;
+ case UBLOCK_OPTICAL_CHARACTER_RECOGNITION:
+ aAllSubsets.emplace_back( 0x2440, 0x245F, SvxResId(RID_SUBSETSTR_OPTICAL_CHAR_REC) );
+ break;
+ case UBLOCK_ENCLOSED_ALPHANUMERICS:
+ aAllSubsets.emplace_back( 0x2460, 0x24FF, SvxResId(RID_SUBSETSTR_ENCLOSED_ALPHANUM) );
+ break;
+ case UBLOCK_BOX_DRAWING:
+ aAllSubsets.emplace_back( 0x2500, 0x257F, SvxResId(RID_SUBSETSTR_BOX_DRAWING) );
+ break;
+ case UBLOCK_BLOCK_ELEMENTS:
+ aAllSubsets.emplace_back( 0x2580, 0x259F, SvxResId(RID_SUBSETSTR_BLOCK_ELEMENTS) );
+ break;
+ case UBLOCK_GEOMETRIC_SHAPES:
+ aAllSubsets.emplace_back( 0x25A0, 0x25FF, SvxResId(RID_SUBSETSTR_GEOMETRIC_SHAPES) );
+ break;
+ case UBLOCK_MISCELLANEOUS_SYMBOLS:
+ aAllSubsets.emplace_back( 0x2600, 0x26FF, SvxResId(RID_SUBSETSTR_MISC_DINGBATS) );
+ break;
+ case UBLOCK_DINGBATS:
+ aAllSubsets.emplace_back( 0x2700, 0x27BF, SvxResId(RID_SUBSETSTR_DINGBATS) );
+ break;
+ case UBLOCK_BRAILLE_PATTERNS:
+ aAllSubsets.emplace_back( 0x2800, 0x28FF, SvxResId(RID_SUBSETSTR_BRAILLE_PATTERNS) );
+ break;
+ case UBLOCK_CJK_RADICALS_SUPPLEMENT:
+ aAllSubsets.emplace_back( 0x2E80, 0x2EFF, SvxResId(RID_SUBSETSTR_CJK_RADICAL_SUPPL) );
+ break;
+ case UBLOCK_KANGXI_RADICALS:
+ aAllSubsets.emplace_back( 0x2F00, 0x2FDF, SvxResId(RID_SUBSETSTR_KANGXI_RADICALS) );
+ break;
+ case UBLOCK_IDEOGRAPHIC_DESCRIPTION_CHARACTERS:
+ aAllSubsets.emplace_back( 0x2FF0, 0x2FFF, SvxResId(RID_SUBSETSTR_IDEO_DESC_CHARS) );
+ break;
+ case UBLOCK_CJK_SYMBOLS_AND_PUNCTUATION:
+ aAllSubsets.emplace_back( 0x3000, 0x303F, SvxResId(RID_SUBSETSTR_CJK_SYMS_PUNCTUATION) );
+ break;
+ case UBLOCK_HIRAGANA:
+ aAllSubsets.emplace_back( 0x3040, 0x309F, SvxResId(RID_SUBSETSTR_HIRAGANA) );
+ break;
+ case UBLOCK_KATAKANA:
+ aAllSubsets.emplace_back( 0x30A0, 0x30FF, SvxResId(RID_SUBSETSTR_KATAKANA) );
+ break;
+ case UBLOCK_BOPOMOFO:
+ aAllSubsets.emplace_back( 0x3100, 0x312F, SvxResId(RID_SUBSETSTR_BOPOMOFO) );
+ break;
+ case UBLOCK_HANGUL_COMPATIBILITY_JAMO:
+ aAllSubsets.emplace_back( 0x3130, 0x318F, SvxResId(RID_SUBSETSTR_HANGUL_COMPAT_JAMO) );
+ break;
+ case UBLOCK_KANBUN:
+ aAllSubsets.emplace_back( 0x3190, 0x319F, SvxResId(RID_SUBSETSTR_KANBUN) );
+ break;
+ case UBLOCK_BOPOMOFO_EXTENDED:
+ aAllSubsets.emplace_back( 0x31A0, 0x31BF, SvxResId(RID_SUBSETSTR_BOPOMOFO_EXTENDED) );
+ break;
+ case UBLOCK_ENCLOSED_CJK_LETTERS_AND_MONTHS:
+ aAllSubsets.emplace_back( 0x3200, 0x32FF, SvxResId(RID_SUBSETSTR_ENCLOSED_CJK_LETTERS) );
+ break;
+ case UBLOCK_CJK_COMPATIBILITY:
+ aAllSubsets.emplace_back( 0x3300, 0x33FF, SvxResId(RID_SUBSETSTR_CJK_COMPATIBILITY) );
+ break;
+ case UBLOCK_CJK_UNIFIED_IDEOGRAPHS_EXTENSION_A:
+ aAllSubsets.emplace_back( 0x3400, 0x4DBF, SvxResId(RID_SUBSETSTR_CJK_EXT_A_UNIFIED_IDGRAPH) );
+ break;
+ case UBLOCK_CJK_UNIFIED_IDEOGRAPHS:
+ aAllSubsets.emplace_back( 0x4E00, 0x9FA5, SvxResId(RID_SUBSETSTR_CJK_UNIFIED_IDGRAPH) );
+ break;
+ case UBLOCK_YI_SYLLABLES:
+ aAllSubsets.emplace_back( 0xA000, 0xA48F, SvxResId(RID_SUBSETSTR_YI_SYLLABLES) );
+ break;
+ case UBLOCK_YI_RADICALS:
+ aAllSubsets.emplace_back( 0xA490, 0xA4CF, SvxResId(RID_SUBSETSTR_YI_RADICALS) );
+ break;
+ case UBLOCK_HANGUL_SYLLABLES:
+ aAllSubsets.emplace_back( 0xAC00, 0xD7AF, SvxResId(RID_SUBSETSTR_HANGUL) );
+ break;
+ case UBLOCK_PRIVATE_USE_AREA:
+ aAllSubsets.emplace_back( 0xE000, 0xF8FF, SvxResId(RID_SUBSETSTR_PRIVATE_USE_AREA) );
+ break;
+ case UBLOCK_CJK_COMPATIBILITY_IDEOGRAPHS:
+ aAllSubsets.emplace_back( 0xF900, 0xFAFF, SvxResId(RID_SUBSETSTR_CJK_COMPAT_IDGRAPHS) );
+ break;
+ case UBLOCK_ALPHABETIC_PRESENTATION_FORMS:
+ aAllSubsets.emplace_back( 0xFB00, 0xFB4F, SvxResId(RID_SUBSETSTR_ALPHA_PRESENTATION) );
+ break;
+ case UBLOCK_ARABIC_PRESENTATION_FORMS_A:
+ aAllSubsets.emplace_back( 0xFB50, 0xFDFF, SvxResId(RID_SUBSETSTR_ARABIC_PRESENT_A) );
+ break;
+ case UBLOCK_COMBINING_HALF_MARKS:
+ aAllSubsets.emplace_back( 0xFE20, 0xFE2F, SvxResId(RID_SUBSETSTR_COMBINING_HALF_MARKS) );
+ break;
+ case UBLOCK_CJK_COMPATIBILITY_FORMS:
+ aAllSubsets.emplace_back( 0xFE30, 0xFE4F, SvxResId(RID_SUBSETSTR_CJK_COMPAT_FORMS) );
+ break;
+ case UBLOCK_SMALL_FORM_VARIANTS:
+ aAllSubsets.emplace_back( 0xFE50, 0xFE6F, SvxResId(RID_SUBSETSTR_SMALL_FORM_VARIANTS) );
+ break;
+ case UBLOCK_ARABIC_PRESENTATION_FORMS_B:
+ aAllSubsets.emplace_back( 0xFE70, 0xFEFF, SvxResId(RID_SUBSETSTR_ARABIC_PRESENT_B) );
+ break;
+ case UBLOCK_SPECIALS:
+ aAllSubsets.emplace_back( 0xFFF0, 0xFFFF, SvxResId(RID_SUBSETSTR_SPECIALS) );
+ break;
+ case UBLOCK_HALFWIDTH_AND_FULLWIDTH_FORMS:
+ aAllSubsets.emplace_back( 0xFF00, 0xFFEF, SvxResId(RID_SUBSETSTR_HALFW_FULLW_FORMS) );
+ break;
+ case UBLOCK_OLD_ITALIC:
+ aAllSubsets.emplace_back( 0x10300, 0x1032F, SvxResId(RID_SUBSETSTR_OLD_ITALIC) );
+ break;
+ case UBLOCK_GOTHIC:
+ aAllSubsets.emplace_back( 0x10330, 0x1034F, SvxResId(RID_SUBSETSTR_GOTHIC) );
+ break;
+ case UBLOCK_DESERET:
+ aAllSubsets.emplace_back( 0x10400, 0x1044F, SvxResId(RID_SUBSETSTR_DESERET) );
+ break;
+ case UBLOCK_BYZANTINE_MUSICAL_SYMBOLS:
+ aAllSubsets.emplace_back( 0x1D000, 0x1D0FF, SvxResId(RID_SUBSETSTR_BYZANTINE_MUSICAL_SYMBOLS) );
+ break;
+ case UBLOCK_MUSICAL_SYMBOLS:
+ aAllSubsets.emplace_back( 0x1D100, 0x1D1FF, SvxResId(RID_SUBSETSTR_MUSICAL_SYMBOLS) );
+ break;
+ case UBLOCK_MATHEMATICAL_ALPHANUMERIC_SYMBOLS:
+ aAllSubsets.emplace_back( 0x1D400, 0x1D7FF, SvxResId(RID_SUBSETSTR_MATHEMATICAL_ALPHANUMERIC_SYMBOLS) );
+ break;
+ case UBLOCK_CJK_UNIFIED_IDEOGRAPHS_EXTENSION_B:
+ aAllSubsets.emplace_back( 0x20000, 0x2A6DF, SvxResId(RID_SUBSETSTR_CJK_UNIFIED_IDEOGRAPHS_EXTENSION_B) );
+ break;
+ case UBLOCK_CJK_COMPATIBILITY_IDEOGRAPHS_SUPPLEMENT:
+ aAllSubsets.emplace_back( 0x2F800, 0x2FA1F, SvxResId(RID_SUBSETSTR_CJK_COMPATIBILITY_IDEOGRAPHS_SUPPLEMENT) );
+ break;
+ case UBLOCK_TAGS:
+ aAllSubsets.emplace_back( 0xE0000, 0xE007F, SvxResId(RID_SUBSETSTR_TAGS) );
+ break;
+ case UBLOCK_CYRILLIC_SUPPLEMENTARY:
+ aAllSubsets.emplace_back( 0x0500, 0x052F, SvxResId(RID_SUBSETSTR_CYRILLIC_SUPPLEMENTARY) );
+ break;
+ case UBLOCK_TAGALOG:
+ aAllSubsets.emplace_back( 0x1700, 0x171F, SvxResId(RID_SUBSETSTR_TAGALOG) );
+ break;
+ case UBLOCK_HANUNOO:
+ aAllSubsets.emplace_back( 0x1720, 0x173F, SvxResId(RID_SUBSETSTR_HANUNOO) );
+ break;
+ case UBLOCK_BUHID:
+ aAllSubsets.emplace_back( 0x1740, 0x175F, SvxResId(RID_SUBSETSTR_BUHID) );
+ break;
+ case UBLOCK_TAGBANWA:
+ aAllSubsets.emplace_back( 0x1760, 0x177F, SvxResId(RID_SUBSETSTR_TAGBANWA) );
+ break;
+ case UBLOCK_MISCELLANEOUS_MATHEMATICAL_SYMBOLS_A:
+ aAllSubsets.emplace_back( 0x27C0, 0x27EF, SvxResId(RID_SUBSETSTR_MISC_MATH_SYMS_A) );
+ break;
+ case UBLOCK_SUPPLEMENTAL_ARROWS_A:
+ aAllSubsets.emplace_back( 0x27F0, 0x27FF, SvxResId(RID_SUBSETSTR_SUPPL_ARROWS_A) );
+ break;
+ case UBLOCK_SUPPLEMENTAL_ARROWS_B:
+ aAllSubsets.emplace_back( 0x2900, 0x297F, SvxResId(RID_SUBSETSTR_SUPPL_ARROWS_B) );
+ break;
+ case UBLOCK_MISCELLANEOUS_MATHEMATICAL_SYMBOLS_B:
+ aAllSubsets.emplace_back( 0x2980, 0x29FF, SvxResId(RID_SUBSETSTR_MISC_MATH_SYMS_B) );
+ break;
+ case UBLOCK_SUPPLEMENTAL_MATHEMATICAL_OPERATORS:
+ aAllSubsets.emplace_back( 0x2A00, 0x2AFF, SvxResId(RID_SUBSETSTR_MISC_MATH_SYMS_B) );
+ break;
+ case UBLOCK_KATAKANA_PHONETIC_EXTENSIONS:
+ aAllSubsets.emplace_back( 0x31F0, 0x31FF, SvxResId(RID_SUBSETSTR_KATAKANA_PHONETIC) );
+ break;
+ case UBLOCK_VARIATION_SELECTORS:
+ aAllSubsets.emplace_back( 0xFE00, 0xFE0F, SvxResId(RID_SUBSETSTR_VARIATION_SELECTORS) );
+ break;
+ case UBLOCK_SUPPLEMENTARY_PRIVATE_USE_AREA_A:
+ aAllSubsets.emplace_back( 0xF0000, 0xFFFFF, SvxResId(RID_SUBSETSTR_SUPPLEMENTARY_PRIVATE_USE_AREA_A) );
+ break;
+ case UBLOCK_SUPPLEMENTARY_PRIVATE_USE_AREA_B:
+ aAllSubsets.emplace_back( 0x100000, 0x10FFFF, SvxResId(RID_SUBSETSTR_SUPPLEMENTARY_PRIVATE_USE_AREA_B) );
+ break;
+ case UBLOCK_LIMBU:
+ aAllSubsets.emplace_back( 0x1900, 0x194F, SvxResId(RID_SUBSETSTR_LIMBU) );
+ break;
+ case UBLOCK_TAI_LE:
+ aAllSubsets.emplace_back( 0x1950, 0x197F, SvxResId(RID_SUBSETSTR_TAI_LE) );
+ break;
+ case UBLOCK_KHMER_SYMBOLS:
+ aAllSubsets.emplace_back( 0x19E0, 0x19FF, SvxResId(RID_SUBSETSTR_KHMER_SYMBOLS) );
+ break;
+ case UBLOCK_PHONETIC_EXTENSIONS:
+ aAllSubsets.emplace_back( 0x1D00, 0x1D7F, SvxResId(RID_SUBSETSTR_PHONETIC_EXTENSIONS) );
+ break;
+ case UBLOCK_MISCELLANEOUS_SYMBOLS_AND_ARROWS:
+ aAllSubsets.emplace_back( 0x2B00, 0x2BFF, SvxResId(RID_SUBSETSTR_MISCELLANEOUS_SYMBOLS_AND_ARROWS) );
+ break;
+ case UBLOCK_YIJING_HEXAGRAM_SYMBOLS:
+ aAllSubsets.emplace_back( 0x4DC0, 0x4DFF, SvxResId(RID_SUBSETSTR_YIJING_HEXAGRAM_SYMBOLS) );
+ break;
+ case UBLOCK_LINEAR_B_SYLLABARY:
+ aAllSubsets.emplace_back( 0x10000, 0x1007F, SvxResId(RID_SUBSETSTR_LINEAR_B_SYLLABARY) );
+ break;
+ case UBLOCK_LINEAR_B_IDEOGRAMS:
+ aAllSubsets.emplace_back( 0x10080, 0x100FF, SvxResId(RID_SUBSETSTR_LINEAR_B_IDEOGRAMS) );
+ break;
+ case UBLOCK_AEGEAN_NUMBERS:
+ aAllSubsets.emplace_back( 0x10100, 0x1013F, SvxResId(RID_SUBSETSTR_AEGEAN_NUMBERS) );
+ break;
+ case UBLOCK_UGARITIC:
+ aAllSubsets.emplace_back( 0x10380, 0x1039F, SvxResId(RID_SUBSETSTR_UGARITIC) );
+ break;
+ case UBLOCK_SHAVIAN:
+ aAllSubsets.emplace_back( 0x10450, 0x1047F, SvxResId(RID_SUBSETSTR_SHAVIAN) );
+ break;
+ case UBLOCK_OSMANYA:
+ aAllSubsets.emplace_back( 0x10480, 0x104AF, SvxResId(RID_SUBSETSTR_OSMANYA) );
+ break;
+ case UBLOCK_CYPRIOT_SYLLABARY:
+ aAllSubsets.emplace_back( 0x10800, 0x1083F, SvxResId(RID_SUBSETSTR_CYPRIOT_SYLLABARY) );
+ break;
+ case UBLOCK_TAI_XUAN_JING_SYMBOLS:
+ aAllSubsets.emplace_back( 0x1D300, 0x1D35F, SvxResId(RID_SUBSETSTR_TAI_XUAN_JING_SYMBOLS) );
+ break;
+ case UBLOCK_VARIATION_SELECTORS_SUPPLEMENT:
+ aAllSubsets.emplace_back( 0xE0100, 0xE01EF, SvxResId(RID_SUBSETSTR_VARIATION_SELECTORS_SUPPLEMENT) );
+ break;
+ case UBLOCK_ANCIENT_GREEK_MUSICAL_NOTATION:
+ aAllSubsets.emplace_back(0x1D200, 0x1D24F, SvxResId(RID_SUBSETSTR_ANCIENT_GREEK_MUSICAL_NOTATION) );
+ break;
+ case UBLOCK_ANCIENT_GREEK_NUMBERS:
+ aAllSubsets.emplace_back(0x10140, 0x1018F , SvxResId(RID_SUBSETSTR_ANCIENT_GREEK_NUMBERS) );
+ break;
+ case UBLOCK_ARABIC_SUPPLEMENT:
+ aAllSubsets.emplace_back(0x0750, 0x077F , SvxResId(RID_SUBSETSTR_ARABIC_SUPPLEMENT) );
+ break;
+ case UBLOCK_BUGINESE:
+ aAllSubsets.emplace_back(0x1A00, 0x1A1F , SvxResId(RID_SUBSETSTR_BUGINESE) );
+ break;
+ case UBLOCK_CJK_STROKES:
+ aAllSubsets.emplace_back( 0x31C0, 0x31EF, SvxResId(RID_SUBSETSTR_CJK_STROKES) );
+ break;
+ case UBLOCK_COMBINING_DIACRITICAL_MARKS_SUPPLEMENT:
+ aAllSubsets.emplace_back( 0x1DC0, 0x1DFF , SvxResId(RID_SUBSETSTR_COMBINING_DIACRITICAL_MARKS_SUPPLEMENT) );
+ break;
+ case UBLOCK_COPTIC:
+ aAllSubsets.emplace_back( 0x2C80, 0x2CFF , SvxResId(RID_SUBSETSTR_COPTIC) );
+ break;
+ case UBLOCK_ETHIOPIC_EXTENDED:
+ aAllSubsets.emplace_back( 0x2D80, 0x2DDF , SvxResId(RID_SUBSETSTR_ETHIOPIC_EXTENDED) );
+ break;
+ case UBLOCK_ETHIOPIC_SUPPLEMENT:
+ aAllSubsets.emplace_back( 0x1380, 0x139F, SvxResId(RID_SUBSETSTR_ETHIOPIC_SUPPLEMENT) );
+ break;
+ case UBLOCK_GEORGIAN_SUPPLEMENT:
+ aAllSubsets.emplace_back( 0x2D00, 0x2D2F, SvxResId(RID_SUBSETSTR_GEORGIAN_SUPPLEMENT) );
+ break;
+ case UBLOCK_GLAGOLITIC:
+ aAllSubsets.emplace_back( 0x2C00, 0x2C5F, SvxResId(RID_SUBSETSTR_GLAGOLITIC) );
+ break;
+ case UBLOCK_KHAROSHTHI:
+ aAllSubsets.emplace_back( 0x10A00, 0x10A5F, SvxResId(RID_SUBSETSTR_KHAROSHTHI) );
+ break;
+ case UBLOCK_MODIFIER_TONE_LETTERS:
+ aAllSubsets.emplace_back( 0xA700, 0xA71F, SvxResId(RID_SUBSETSTR_MODIFIER_TONE_LETTERS) );
+ break;
+ case UBLOCK_NEW_TAI_LUE:
+ aAllSubsets.emplace_back( 0x1980, 0x19DF, SvxResId(RID_SUBSETSTR_NEW_TAI_LUE) );
+ break;
+ case UBLOCK_OLD_PERSIAN:
+ aAllSubsets.emplace_back( 0x103A0, 0x103DF, SvxResId(RID_SUBSETSTR_OLD_PERSIAN) );
+ break;
+ case UBLOCK_PHONETIC_EXTENSIONS_SUPPLEMENT:
+ aAllSubsets.emplace_back( 0x1D80, 0x1DBF, SvxResId(RID_SUBSETSTR_PHONETIC_EXTENSIONS_SUPPLEMENT) );
+ break;
+ case UBLOCK_SUPPLEMENTAL_PUNCTUATION:
+ aAllSubsets.emplace_back( 0x2E00, 0x2E7F, SvxResId(RID_SUBSETSTR_SUPPLEMENTAL_PUNCTUATION) );
+ break;
+ case UBLOCK_SYLOTI_NAGRI:
+ aAllSubsets.emplace_back( 0xA800, 0xA82F, SvxResId(RID_SUBSETSTR_SYLOTI_NAGRI) );
+ break;
+ case UBLOCK_TIFINAGH:
+ aAllSubsets.emplace_back( 0x2D30, 0x2D7F, SvxResId(RID_SUBSETSTR_TIFINAGH) );
+ break;
+ case UBLOCK_VERTICAL_FORMS:
+ aAllSubsets.emplace_back( 0xFE10, 0xFE1F, SvxResId(RID_SUBSETSTR_VERTICAL_FORMS) );
+ break;
+ case UBLOCK_NKO:
+ aAllSubsets.emplace_back( 0x07C0, 0x07FF, SvxResId(RID_SUBSETSTR_NKO) );
+ break;
+ case UBLOCK_BALINESE:
+ aAllSubsets.emplace_back( 0x1B00, 0x1B7F, SvxResId(RID_SUBSETSTR_BALINESE) );
+ break;
+ case UBLOCK_LATIN_EXTENDED_C:
+ aAllSubsets.emplace_back( 0x2C60, 0x2C7F, SvxResId(RID_SUBSETSTR_LATIN_EXTENDED_C) );
+ break;
+ case UBLOCK_LATIN_EXTENDED_D:
+ aAllSubsets.emplace_back( 0xA720, 0xA7FF, SvxResId(RID_SUBSETSTR_LATIN_EXTENDED_D) );
+ break;
+ case UBLOCK_PHAGS_PA:
+ aAllSubsets.emplace_back( 0xA840, 0xA87F, SvxResId(RID_SUBSETSTR_PHAGS_PA) );
+ break;
+ case UBLOCK_PHOENICIAN:
+ aAllSubsets.emplace_back( 0x10900, 0x1091F, SvxResId(RID_SUBSETSTR_PHOENICIAN) );
+ break;
+ case UBLOCK_CUNEIFORM:
+ aAllSubsets.emplace_back( 0x12000, 0x123FF, SvxResId(RID_SUBSETSTR_CUNEIFORM) );
+ break;
+ case UBLOCK_CUNEIFORM_NUMBERS_AND_PUNCTUATION:
+ aAllSubsets.emplace_back( 0x12400, 0x1247F, SvxResId(RID_SUBSETSTR_CUNEIFORM_NUMBERS_AND_PUNCTUATION) );
+ break;
+ case UBLOCK_COUNTING_ROD_NUMERALS:
+ aAllSubsets.emplace_back( 0x1D360, 0x1D37F, SvxResId(RID_SUBSETSTR_COUNTING_ROD_NUMERALS) );
+ break;
+ case UBLOCK_SUNDANESE:
+ aAllSubsets.emplace_back( 0x1B80, 0x1BBF, SvxResId(RID_SUBSETSTR_SUNDANESE) );
+ break;
+ case UBLOCK_LEPCHA:
+ aAllSubsets.emplace_back( 0x1C00, 0x1C4F, SvxResId(RID_SUBSETSTR_LEPCHA) );
+ break;
+ case UBLOCK_OL_CHIKI:
+ aAllSubsets.emplace_back( 0x1C50, 0x1C7F, SvxResId(RID_SUBSETSTR_OL_CHIKI) );
+ break;
+ case UBLOCK_CYRILLIC_EXTENDED_A:
+ aAllSubsets.emplace_back( 0x2DE0, 0x2DFF, SvxResId(RID_SUBSETSTR_CYRILLIC_EXTENDED_A) );
+ break;
+ case UBLOCK_VAI:
+ aAllSubsets.emplace_back( 0xA500, 0xA63F, SvxResId(RID_SUBSETSTR_VAI) );
+ break;
+ case UBLOCK_CYRILLIC_EXTENDED_B:
+ aAllSubsets.emplace_back( 0xA640, 0xA69F, SvxResId(RID_SUBSETSTR_CYRILLIC_EXTENDED_B) );
+ break;
+ case UBLOCK_SAURASHTRA:
+ aAllSubsets.emplace_back( 0xA880, 0xA8DF, SvxResId(RID_SUBSETSTR_SAURASHTRA) );
+ break;
+ case UBLOCK_KAYAH_LI:
+ aAllSubsets.emplace_back( 0xA900, 0xA92F, SvxResId(RID_SUBSETSTR_KAYAH_LI) );
+ break;
+ case UBLOCK_REJANG:
+ aAllSubsets.emplace_back( 0xA930, 0xA95F, SvxResId(RID_SUBSETSTR_REJANG) );
+ break;
+ case UBLOCK_CHAM:
+ aAllSubsets.emplace_back( 0xAA00, 0xAA5F, SvxResId(RID_SUBSETSTR_CHAM) );
+ break;
+ case UBLOCK_ANCIENT_SYMBOLS:
+ aAllSubsets.emplace_back( 0x10190, 0x101CF, SvxResId(RID_SUBSETSTR_ANCIENT_SYMBOLS) );
+ break;
+ case UBLOCK_PHAISTOS_DISC:
+ aAllSubsets.emplace_back( 0x101D0, 0x101FF, SvxResId(RID_SUBSETSTR_PHAISTOS_DISC) );
+ break;
+ case UBLOCK_LYCIAN:
+ aAllSubsets.emplace_back( 0x10280, 0x1029F, SvxResId(RID_SUBSETSTR_LYCIAN) );
+ break;
+ case UBLOCK_CARIAN:
+ aAllSubsets.emplace_back( 0x102A0, 0x102DF, SvxResId(RID_SUBSETSTR_CARIAN) );
+ break;
+ case UBLOCK_LYDIAN:
+ aAllSubsets.emplace_back( 0x10920, 0x1093F, SvxResId(RID_SUBSETSTR_LYDIAN) );
+ break;
+ case UBLOCK_MAHJONG_TILES:
+ aAllSubsets.emplace_back( 0x1F000, 0x1F02F, SvxResId(RID_SUBSETSTR_MAHJONG_TILES) );
+ break;
+ case UBLOCK_DOMINO_TILES:
+ aAllSubsets.emplace_back( 0x1F030, 0x1F09F, SvxResId(RID_SUBSETSTR_DOMINO_TILES) );
+ break;
+ case UBLOCK_SAMARITAN:
+ aAllSubsets.emplace_back( 0x0800, 0x083F, SvxResId(RID_SUBSETSTR_SAMARITAN) );
+ break;
+ case UBLOCK_UNIFIED_CANADIAN_ABORIGINAL_SYLLABICS_EXTENDED:
+ aAllSubsets.emplace_back( 0x18B0, 0x18FF, SvxResId(RID_SUBSETSTR_UNIFIED_CANADIAN_ABORIGINAL_SYLLABICS_EXTENDED) );
+ break;
+ case UBLOCK_TAI_THAM:
+ aAllSubsets.emplace_back( 0x1A20, 0x1AAF, SvxResId(RID_SUBSETSTR_TAI_THAM) );
+ break;
+ case UBLOCK_VEDIC_EXTENSIONS:
+ aAllSubsets.emplace_back( 0x1CD0, 0x1CFF, SvxResId(RID_SUBSETSTR_VEDIC_EXTENSIONS) );
+ break;
+ case UBLOCK_LISU:
+ aAllSubsets.emplace_back( 0xA4D0, 0xA4FF, SvxResId(RID_SUBSETSTR_LISU) );
+ break;
+ case UBLOCK_BAMUM:
+ aAllSubsets.emplace_back( 0xA6A0, 0xA6FF, SvxResId(RID_SUBSETSTR_BAMUM) );
+ break;
+ case UBLOCK_COMMON_INDIC_NUMBER_FORMS:
+ aAllSubsets.emplace_back( 0xA830, 0xA83F, SvxResId(RID_SUBSETSTR_COMMON_INDIC_NUMBER_FORMS) );
+ break;
+ case UBLOCK_DEVANAGARI_EXTENDED:
+ aAllSubsets.emplace_back( 0xA8E0, 0xA8FF, SvxResId(RID_SUBSETSTR_DEVANAGARI_EXTENDED) );
+ break;
+ case UBLOCK_HANGUL_JAMO_EXTENDED_A:
+ aAllSubsets.emplace_back( 0xA960, 0xA97F, SvxResId(RID_SUBSETSTR_HANGUL_JAMO_EXTENDED_A) );
+ break;
+ case UBLOCK_JAVANESE:
+ aAllSubsets.emplace_back( 0xA980, 0xA9DF, SvxResId(RID_SUBSETSTR_JAVANESE) );
+ break;
+ case UBLOCK_MYANMAR_EXTENDED_A:
+ aAllSubsets.emplace_back( 0xAA60, 0xAA7F, SvxResId(RID_SUBSETSTR_MYANMAR_EXTENDED_A) );
+ break;
+ case UBLOCK_TAI_VIET:
+ aAllSubsets.emplace_back( 0xAA80, 0xAADF, SvxResId(RID_SUBSETSTR_TAI_VIET) );
+ break;
+ case UBLOCK_MEETEI_MAYEK:
+ aAllSubsets.emplace_back( 0xABC0, 0xABFF, SvxResId(RID_SUBSETSTR_MEETEI_MAYEK) );
+ break;
+ case UBLOCK_HANGUL_JAMO_EXTENDED_B:
+ aAllSubsets.emplace_back( 0xD7B0, 0xD7FF, SvxResId(RID_SUBSETSTR_HANGUL_JAMO_EXTENDED_B) );
+ break;
+ case UBLOCK_IMPERIAL_ARAMAIC:
+ aAllSubsets.emplace_back( 0x10840, 0x1085F, SvxResId(RID_SUBSETSTR_IMPERIAL_ARAMAIC) );
+ break;
+ case UBLOCK_OLD_SOUTH_ARABIAN:
+ aAllSubsets.emplace_back( 0x10A60, 0x10A7F, SvxResId(RID_SUBSETSTR_OLD_SOUTH_ARABIAN) );
+ break;
+ case UBLOCK_AVESTAN:
+ aAllSubsets.emplace_back( 0x10B00, 0x10B3F, SvxResId(RID_SUBSETSTR_AVESTAN) );
+ break;
+ case UBLOCK_INSCRIPTIONAL_PARTHIAN:
+ aAllSubsets.emplace_back( 0x10B40, 0x10B5F, SvxResId(RID_SUBSETSTR_INSCRIPTIONAL_PARTHIAN) );
+ break;
+ case UBLOCK_INSCRIPTIONAL_PAHLAVI:
+ aAllSubsets.emplace_back( 0x10B60, 0x10B7F, SvxResId(RID_SUBSETSTR_INSCRIPTIONAL_PAHLAVI) );
+ break;
+ case UBLOCK_OLD_TURKIC:
+ aAllSubsets.emplace_back( 0x10C00, 0x10C4F, SvxResId(RID_SUBSETSTR_OLD_TURKIC) );
+ break;
+ case UBLOCK_RUMI_NUMERAL_SYMBOLS:
+ aAllSubsets.emplace_back( 0x10E60, 0x10E7F, SvxResId(RID_SUBSETSTR_RUMI_NUMERAL_SYMBOLS) );
+ break;
+ case UBLOCK_KAITHI:
+ aAllSubsets.emplace_back( 0x11080, 0x110CF, SvxResId(RID_SUBSETSTR_KAITHI) );
+ break;
+ case UBLOCK_EGYPTIAN_HIEROGLYPHS:
+ aAllSubsets.emplace_back( 0x13000, 0x1342F, SvxResId(RID_SUBSETSTR_EGYPTIAN_HIEROGLYPHS) );
+ break;
+ case UBLOCK_ENCLOSED_ALPHANUMERIC_SUPPLEMENT:
+ aAllSubsets.emplace_back( 0x1F100, 0x1F1FF, SvxResId(RID_SUBSETSTR_ENCLOSED_ALPHANUMERIC_SUPPLEMENT) );
+ break;
+ case UBLOCK_ENCLOSED_IDEOGRAPHIC_SUPPLEMENT:
+ aAllSubsets.emplace_back( 0x1F200, 0x1F2FF, SvxResId(RID_SUBSETSTR_ENCLOSED_IDEOGRAPHIC_SUPPLEMENT) );
+ break;
+ case UBLOCK_CJK_UNIFIED_IDEOGRAPHS_EXTENSION_C:
+ aAllSubsets.emplace_back( 0x2A700, 0x2B73F, SvxResId(RID_SUBSETSTR_CJK_UNIFIED_IDEOGRAPHS_EXTENSION_C) );
+ break;
+ case UBLOCK_MANDAIC:
+ aAllSubsets.emplace_back( 0x0840, 0x085F, SvxResId(RID_SUBSETSTR_MANDAIC) );
+ break;
+ case UBLOCK_BATAK:
+ aAllSubsets.emplace_back( 0x1BC0, 0x1BFF, SvxResId(RID_SUBSETSTR_BATAK) );
+ break;
+ case UBLOCK_ETHIOPIC_EXTENDED_A:
+ aAllSubsets.emplace_back( 0xAB00, 0xAB2F, SvxResId(RID_SUBSETSTR_ETHIOPIC_EXTENDED_A) );
+ break;
+ case UBLOCK_BRAHMI:
+ aAllSubsets.emplace_back( 0x11000, 0x1107F, SvxResId(RID_SUBSETSTR_BRAHMI) );
+ break;
+ case UBLOCK_BAMUM_SUPPLEMENT:
+ aAllSubsets.emplace_back( 0x16800, 0x16A3F, SvxResId(RID_SUBSETSTR_BAMUM_SUPPLEMENT) );
+ break;
+ case UBLOCK_KANA_SUPPLEMENT:
+ aAllSubsets.emplace_back( 0x1B000, 0x1B0FF, SvxResId(RID_SUBSETSTR_KANA_SUPPLEMENT) );
+ break;
+ case UBLOCK_PLAYING_CARDS:
+ aAllSubsets.emplace_back( 0x1F0A0, 0x1F0FF, SvxResId(RID_SUBSETSTR_PLAYING_CARDS) );
+ break;
+ case UBLOCK_MISCELLANEOUS_SYMBOLS_AND_PICTOGRAPHS:
+ aAllSubsets.emplace_back( 0x1F300, 0x1F5FF, SvxResId(RID_SUBSETSTR_MISCELLANEOUS_SYMBOLS_AND_PICTOGRAPHS) );
+ break;
+ case UBLOCK_EMOTICONS:
+ aAllSubsets.emplace_back( 0x1F600, 0x1F64F, SvxResId(RID_SUBSETSTR_EMOTICONS) );
+ break;
+ case UBLOCK_TRANSPORT_AND_MAP_SYMBOLS:
+ aAllSubsets.emplace_back( 0x1F680, 0x1F6FF, SvxResId(RID_SUBSETSTR_TRANSPORT_AND_MAP_SYMBOLS) );
+ break;
+ case UBLOCK_ALCHEMICAL_SYMBOLS:
+ aAllSubsets.emplace_back( 0x1F700, 0x1F77F, SvxResId(RID_SUBSETSTR_ALCHEMICAL_SYMBOLS) );
+ break;
+ case UBLOCK_CJK_UNIFIED_IDEOGRAPHS_EXTENSION_D:
+ aAllSubsets.emplace_back( 0x2B740, 0x2B81F, SvxResId(RID_SUBSETSTR_CJK_UNIFIED_IDEOGRAPHS_EXTENSION_D) );
+ break;
+// Note ICU version 49 (NOT 4.9), so the MAJOR_NUM is two digits.
+#if U_ICU_VERSION_MAJOR_NUM >= 49
+ case UBLOCK_ARABIC_EXTENDED_A:
+ aAllSubsets.emplace_back( 0x08A0, 0x08FF, SvxResId(RID_SUBSETSTR_ARABIC_EXTENDED_A) );
+ break;
+ case UBLOCK_ARABIC_MATHEMATICAL_ALPHABETIC_SYMBOLS:
+ aAllSubsets.emplace_back( 0x1EE00, 0x1EEFF, SvxResId(RID_SUBSETSTR_ARABIC_MATHEMATICAL_ALPHABETIC_SYMBOLS) );
+ break;
+ case UBLOCK_CHAKMA:
+ aAllSubsets.emplace_back( 0x11100, 0x1114F, SvxResId(RID_SUBSETSTR_CHAKMA) );
+ break;
+ case UBLOCK_MEETEI_MAYEK_EXTENSIONS:
+ aAllSubsets.emplace_back( 0xAAE0, 0xAAFF, SvxResId(RID_SUBSETSTR_MEETEI_MAYEK_EXTENSIONS) );
+ break;
+ case UBLOCK_MEROITIC_CURSIVE:
+ aAllSubsets.emplace_back( 0x109A0, 0x109FF, SvxResId(RID_SUBSETSTR_MEROITIC_CURSIVE) );
+ break;
+ case UBLOCK_MEROITIC_HIEROGLYPHS:
+ aAllSubsets.emplace_back( 0x10980, 0x1099F, SvxResId(RID_SUBSETSTR_MEROITIC_HIEROGLYPHS) );
+ break;
+ case UBLOCK_MIAO:
+ aAllSubsets.emplace_back( 0x16F00, 0x16F9F, SvxResId(RID_SUBSETSTR_MIAO) );
+ break;
+ case UBLOCK_SHARADA:
+ aAllSubsets.emplace_back( 0x11180, 0x111DF, SvxResId(RID_SUBSETSTR_SHARADA) );
+ break;
+ case UBLOCK_SORA_SOMPENG:
+ aAllSubsets.emplace_back( 0x110D0, 0x110FF, SvxResId(RID_SUBSETSTR_SORA_SOMPENG) );
+ break;
+ case UBLOCK_SUNDANESE_SUPPLEMENT:
+ aAllSubsets.emplace_back( 0x1CC0, 0x1CCF, SvxResId(RID_SUBSETSTR_SUNDANESE_SUPPLEMENT) );
+ break;
+ case UBLOCK_TAKRI:
+ aAllSubsets.emplace_back( 0x11680, 0x116CF, SvxResId(RID_SUBSETSTR_TAKRI) );
+ break;
+#endif
+#if U_ICU_VERSION_MAJOR_NUM >= 54
+ case UBLOCK_BASSA_VAH:
+ aAllSubsets.emplace_back( 0x16AD0, 0x16AFF, SvxResId(RID_SUBSETSTR_BASSA_VAH) );
+ break;
+ case UBLOCK_CAUCASIAN_ALBANIAN:
+ aAllSubsets.emplace_back( 0x10530, 0x1056F, SvxResId(RID_SUBSETSTR_CAUCASIAN_ALBANIAN) );
+ break;
+ case UBLOCK_COPTIC_EPACT_NUMBERS:
+ aAllSubsets.emplace_back( 0x102E0, 0x102FF, SvxResId(RID_SUBSETSTR_COPTIC_EPACT_NUMBERS) );
+ break;
+ case UBLOCK_COMBINING_DIACRITICAL_MARKS_EXTENDED:
+ aAllSubsets.emplace_back( 0x1AB0, 0x1AFF, SvxResId(RID_SUBSETSTR_COMBINING_DIACRITICAL_MARKS_EXTENDED) );
+ break;
+ case UBLOCK_DUPLOYAN:
+ aAllSubsets.emplace_back( 0x1BC00, 0x1BC9F, SvxResId(RID_SUBSETSTR_DUPLOYAN) );
+ break;
+ case UBLOCK_ELBASAN:
+ aAllSubsets.emplace_back( 0x10500, 0x1052F, SvxResId(RID_SUBSETSTR_ELBASAN) );
+ break;
+ case UBLOCK_GEOMETRIC_SHAPES_EXTENDED:
+ aAllSubsets.emplace_back( 0x1F780, 0x1F7FF, SvxResId(RID_SUBSETSTR_GEOMETRIC_SHAPES_EXTENDED) );
+ break;
+ case UBLOCK_GRANTHA:
+ aAllSubsets.emplace_back( 0x11300, 0x1137F, SvxResId(RID_SUBSETSTR_GRANTHA) );
+ break;
+ case UBLOCK_KHOJKI:
+ aAllSubsets.emplace_back( 0x11200, 0x1124F, SvxResId(RID_SUBSETSTR_KHOJKI) );
+ break;
+ case UBLOCK_KHUDAWADI:
+ aAllSubsets.emplace_back( 0x112B0, 0x112FF, SvxResId(RID_SUBSETSTR_KHUDAWADI) );
+ break;
+ case UBLOCK_LATIN_EXTENDED_E:
+ aAllSubsets.emplace_back( 0xAB30, 0xAB6F, SvxResId(RID_SUBSETSTR_LATIN_EXTENDED_E) );
+ break;
+ case UBLOCK_LINEAR_A:
+ aAllSubsets.emplace_back( 0x10600, 0x1077F, SvxResId(RID_SUBSETSTR_LINEAR_A) );
+ break;
+ case UBLOCK_MAHAJANI:
+ aAllSubsets.emplace_back( 0x11150, 0x1117F, SvxResId(RID_SUBSETSTR_MAHAJANI) );
+ break;
+ case UBLOCK_MANICHAEAN:
+ aAllSubsets.emplace_back( 0x10AC0, 0x10AFF, SvxResId(RID_SUBSETSTR_MANICHAEAN) );
+ break;
+ case UBLOCK_MENDE_KIKAKUI:
+ aAllSubsets.emplace_back( 0x1E800, 0x1E8DF, SvxResId(RID_SUBSETSTR_MENDE_KIKAKUI) );
+ break;
+ case UBLOCK_MODI:
+ aAllSubsets.emplace_back( 0x11600, 0x1165F, SvxResId(RID_SUBSETSTR_MODI) );
+ break;
+ case UBLOCK_MRO:
+ aAllSubsets.emplace_back( 0x16A40, 0x16A6F, SvxResId(RID_SUBSETSTR_MRO) );
+ break;
+ case UBLOCK_MYANMAR_EXTENDED_B:
+ aAllSubsets.emplace_back( 0xA9E0, 0xA9FF, SvxResId(RID_SUBSETSTR_MYANMAR_EXTENDED_B) );
+ break;
+ case UBLOCK_NABATAEAN:
+ aAllSubsets.emplace_back( 0x10880, 0x108AF, SvxResId(RID_SUBSETSTR_NABATAEAN) );
+ break;
+ case UBLOCK_OLD_NORTH_ARABIAN:
+ aAllSubsets.emplace_back( 0x10A80, 0x10A9F, SvxResId(RID_SUBSETSTR_OLD_NORTH_ARABIAN) );
+ break;
+ case UBLOCK_OLD_PERMIC:
+ aAllSubsets.emplace_back( 0x10350, 0x1037F, SvxResId(RID_SUBSETSTR_OLD_PERMIC) );
+ break;
+ case UBLOCK_ORNAMENTAL_DINGBATS:
+ aAllSubsets.emplace_back( 0x1F650, 0x1F67F, SvxResId(RID_SUBSETSTR_ORNAMENTAL_DINGBATS) );
+ break;
+ case UBLOCK_PAHAWH_HMONG:
+ aAllSubsets.emplace_back( 0x16B00, 0x16B8F, SvxResId(RID_SUBSETSTR_PAHAWH_HMONG) );
+ break;
+ case UBLOCK_PALMYRENE:
+ aAllSubsets.emplace_back( 0x10860, 0x1087F, SvxResId(RID_SUBSETSTR_PALMYRENE) );
+ break;
+ case UBLOCK_PAU_CIN_HAU:
+ aAllSubsets.emplace_back( 0x11AC0, 0x11AFF, SvxResId(RID_SUBSETSTR_PAU_CIN_HAU) );
+ break;
+ case UBLOCK_PSALTER_PAHLAVI:
+ aAllSubsets.emplace_back( 0x10B80, 0x10BAF, SvxResId(RID_SUBSETSTR_PSALTER_PAHLAVI) );
+ break;
+ case UBLOCK_SHORTHAND_FORMAT_CONTROLS:
+ aAllSubsets.emplace_back( 0x1BCA0, 0x1BCAF, SvxResId(RID_SUBSETSTR_SHORTHAND_FORMAT_CONTROLS) );
+ break;
+ case UBLOCK_SIDDHAM:
+ aAllSubsets.emplace_back( 0x11580, 0x115FF, SvxResId(RID_SUBSETSTR_SIDDHAM) );
+ break;
+ case UBLOCK_SINHALA_ARCHAIC_NUMBERS:
+ aAllSubsets.emplace_back( 0x111E0, 0x111FF, SvxResId(RID_SUBSETSTR_SINHALA_ARCHAIC_NUMBERS) );
+ break;
+ case UBLOCK_SUPPLEMENTAL_ARROWS_C:
+ aAllSubsets.emplace_back( 0x1F800, 0x1F8FF, SvxResId(RID_SUBSETSTR_SUPPLEMENTAL_ARROWS_C) );
+ break;
+ case UBLOCK_TIRHUTA:
+ aAllSubsets.emplace_back( 0x11480, 0x114DF, SvxResId(RID_SUBSETSTR_TIRHUTA) );
+ break;
+ case UBLOCK_WARANG_CITI:
+ aAllSubsets.emplace_back( 0x118A0, 0x118FF, SvxResId(RID_SUBSETSTR_WARANG_CITI) );
+ break;
+#endif
+#if U_ICU_VERSION_MAJOR_NUM >= 56
+ case UBLOCK_AHOM:
+ aAllSubsets.emplace_back( 0x11700, 0x1173F, SvxResId(RID_SUBSETSTR_AHOM) );
+ break;
+ case UBLOCK_ANATOLIAN_HIEROGLYPHS:
+ aAllSubsets.emplace_back( 0x14400, 0x1467F, SvxResId(RID_SUBSETSTR_ANATOLIAN_HIEROGLYPHS) );
+ break;
+ case UBLOCK_CHEROKEE_SUPPLEMENT:
+ aAllSubsets.emplace_back( 0xAB70, 0xABBF, SvxResId(RID_SUBSETSTR_CHEROKEE_SUPPLEMENT) );
+ break;
+ case UBLOCK_CJK_UNIFIED_IDEOGRAPHS_EXTENSION_E:
+ aAllSubsets.emplace_back( 0x2B820, 0x2CEAF, SvxResId(RID_SUBSETSTR_CJK_UNIFIED_IDEOGRAPHS_EXTENSION_E) );
+ break;
+ case UBLOCK_EARLY_DYNASTIC_CUNEIFORM:
+ aAllSubsets.emplace_back( 0x12480, 0x1254F, SvxResId(RID_SUBSETSTR_EARLY_DYNASTIC_CUNEIFORM) );
+ break;
+ case UBLOCK_HATRAN:
+ aAllSubsets.emplace_back( 0x108E0, 0x108FF, SvxResId(RID_SUBSETSTR_HATRAN) );
+ break;
+ case UBLOCK_MULTANI:
+ aAllSubsets.emplace_back( 0x11280, 0x112AF, SvxResId(RID_SUBSETSTR_MULTANI) );
+ break;
+ case UBLOCK_OLD_HUNGARIAN:
+ aAllSubsets.emplace_back( 0x10C80, 0x10CFF, SvxResId(RID_SUBSETSTR_OLD_HUNGARIAN) );
+ break;
+ case UBLOCK_SUPPLEMENTAL_SYMBOLS_AND_PICTOGRAPHS:
+ aAllSubsets.emplace_back( 0x1F900, 0x1F9FF, SvxResId(RID_SUBSETSTR_SUPPLEMENTAL_SYMBOLS_AND_PICTOGRAPHS) );
+ break;
+ case UBLOCK_SUTTON_SIGNWRITING:
+ aAllSubsets.emplace_back( 0x1D800, 0x1DAAF, SvxResId(RID_SUBSETSTR_SUTTON_SIGNWRITING) );
+ break;
+#endif
+#if (U_ICU_VERSION_MAJOR_NUM >= 58)
+ case UBLOCK_ADLAM:
+ aAllSubsets.emplace_back( 0x1E900, 0x1E95F, SvxResId(RID_SUBSETSTR_ADLAM) );
+ break;
+ case UBLOCK_BHAIKSUKI:
+ aAllSubsets.emplace_back( 0x11C00, 0x11C6F, SvxResId(RID_SUBSETSTR_BHAIKSUKI) );
+ break;
+ case UBLOCK_CYRILLIC_EXTENDED_C:
+ aAllSubsets.emplace_back( 0x1C80, 0x1C8F, SvxResId(RID_SUBSETSTR_CYRILLIC_EXTENDED_C) );
+ break;
+ case UBLOCK_GLAGOLITIC_SUPPLEMENT:
+ aAllSubsets.emplace_back( 0x1E000, 0x1E02F, SvxResId(RID_SUBSETSTR_GLAGOLITIC_SUPPLEMENT) );
+ break;
+ case UBLOCK_IDEOGRAPHIC_SYMBOLS_AND_PUNCTUATION:
+ aAllSubsets.emplace_back( 0x16FE0, 0x16FFF, SvxResId(RID_SUBSETSTR_IDEOGRAPHIC_SYMBOLS_AND_PUNCTUATION) );
+ break;
+ case UBLOCK_MARCHEN:
+ aAllSubsets.emplace_back( 0x11C70, 0x11CBF, SvxResId(RID_SUBSETSTR_MARCHEN) );
+ break;
+ case UBLOCK_MONGOLIAN_SUPPLEMENT:
+ aAllSubsets.emplace_back( 0x11660, 0x1167F, SvxResId(RID_SUBSETSTR_MONGOLIAN_SUPPLEMENT) );
+ break;
+ case UBLOCK_NEWA:
+ aAllSubsets.emplace_back( 0x11400, 0x1147F, SvxResId(RID_SUBSETSTR_NEWA) );
+ break;
+ case UBLOCK_OSAGE:
+ aAllSubsets.emplace_back( 0x104B0, 0x104FF, SvxResId(RID_SUBSETSTR_OSAGE) );
+ break;
+ case UBLOCK_TANGUT:
+ aAllSubsets.emplace_back( 0x17000, 0x187FF, SvxResId(RID_SUBSETSTR_TANGUT) );
+ break;
+ case UBLOCK_TANGUT_COMPONENTS:
+ aAllSubsets.emplace_back( 0x18800, 0x18AFF, SvxResId(RID_SUBSETSTR_TANGUT_COMPONENTS) );
+ break;
+#endif
+#if (U_ICU_VERSION_MAJOR_NUM >= 60)
+ case UBLOCK_CJK_UNIFIED_IDEOGRAPHS_EXTENSION_F:
+ aAllSubsets.emplace_back( 0x2CEB0, 0x2EBE0, SvxResId(RID_SUBSETSTR_CJK_UNIFIED_IDEOGRAPHS_EXTENSION_F) );
+ break;
+ case UBLOCK_KANA_EXTENDED_A:
+ aAllSubsets.emplace_back( 0x1B100, 0x1B12F, SvxResId(RID_SUBSETSTR_KANA_EXTENDED_A) );
+ break;
+ case UBLOCK_MASARAM_GONDI:
+ aAllSubsets.emplace_back( 0x11D00, 0x11D5F, SvxResId(RID_SUBSETSTR_MASARAM_GONDI) );
+ break;
+ case UBLOCK_NUSHU:
+ aAllSubsets.emplace_back( 0x1B170, 0x1B2FF, SvxResId(RID_SUBSETSTR_NUSHU) );
+ break;
+ case UBLOCK_SOYOMBO:
+ aAllSubsets.emplace_back( 0x11A50, 0x11AAF, SvxResId(RID_SUBSETSTR_SOYOMBO) );
+ break;
+ case UBLOCK_SYRIAC_SUPPLEMENT:
+ aAllSubsets.emplace_back( 0x0860, 0x086f, SvxResId(RID_SUBSETSTR_SYRIAC_SUPPLEMENT) );
+ break;
+ case UBLOCK_ZANABAZAR_SQUARE:
+ aAllSubsets.emplace_back( 0x11A00, 0x11A4F, SvxResId(RID_SUBSETSTR_ZANABAZAR_SQUARE) );
+ break;
+#endif
+#if (U_ICU_VERSION_MAJOR_NUM >= 62)
+ case UBLOCK_CHESS_SYMBOLS:
+ aAllSubsets.emplace_back( 0x1FA00, 0x1FA6F, SvxResId(RID_SUBSETSTR_CHESS_SYMBOLS) );
+ break;
+ case UBLOCK_DOGRA:
+ aAllSubsets.emplace_back( 0x11800, 0x1184F, SvxResId(RID_SUBSETSTR_DOGRA) );
+ break;
+ case UBLOCK_GEORGIAN_EXTENDED:
+ aAllSubsets.emplace_back( 0x1C90, 0x1CBF, SvxResId(RID_SUBSETSTR_GEORGIAN_EXTENDED) );
+ break;
+ case UBLOCK_GUNJALA_GONDI:
+ aAllSubsets.emplace_back( 0x11D60, 0x11DAF, SvxResId(RID_SUBSETSTR_GUNJALA_GONDI) );
+ break;
+ case UBLOCK_HANIFI_ROHINGYA:
+ aAllSubsets.emplace_back( 0x10D00, 0x10D3F, SvxResId(RID_SUBSETSTR_HANIFI_ROHINGYA) );
+ break;
+ case UBLOCK_INDIC_SIYAQ_NUMBERS:
+ aAllSubsets.emplace_back( 0x1EC70, 0x1ECBF, SvxResId(RID_SUBSETSTR_INDIC_SIYAQ_NUMBERS) );
+ break;
+ case UBLOCK_MAKASAR:
+ aAllSubsets.emplace_back( 0x11EE0, 0x11EFF, SvxResId(RID_SUBSETSTR_MAKASAR) );
+ break;
+ case UBLOCK_MAYAN_NUMERALS:
+ aAllSubsets.emplace_back( 0x1D2E0, 0x1D2FF, SvxResId(RID_SUBSETSTR_MAYAN_NUMERALS) );
+ break;
+ case UBLOCK_MEDEFAIDRIN:
+ aAllSubsets.emplace_back( 0x16E40, 0x16E9F, SvxResId(RID_SUBSETSTR_MEDEFAIDRIN) );
+ break;
+ case UBLOCK_OLD_SOGDIAN:
+ aAllSubsets.emplace_back( 0x10F00, 0x10F2F, SvxResId(RID_SUBSETSTR_OLD_SOGDIAN) );
+ break;
+ case UBLOCK_SOGDIAN:
+ aAllSubsets.emplace_back( 0x10F30, 0x10F6F, SvxResId(RID_SUBSETSTR_SOGDIAN) );
+ break;
+#endif
+#if (U_ICU_VERSION_MAJOR_NUM >= 64)
+ case UBLOCK_EGYPTIAN_HIEROGLYPH_FORMAT_CONTROLS:
+ aAllSubsets.emplace_back( 0x13430, 0x1343F, SvxResId(RID_SUBSETSTR_EGYPTIAN_HIEROGLYPH_FORMAT_CONTROLS) );
+ break;
+ case UBLOCK_ELYMAIC:
+ aAllSubsets.emplace_back( 0x10FE0, 0x10FFF, SvxResId(RID_SUBSETSTR_ELYMAIC) );
+ break;
+ case UBLOCK_NANDINAGARI:
+ aAllSubsets.emplace_back( 0x119A0, 0x119FF, SvxResId(RID_SUBSETSTR_NANDINAGARI) );
+ break;
+ case UBLOCK_NYIAKENG_PUACHUE_HMONG:
+ aAllSubsets.emplace_back( 0x1E100, 0x1E14F, SvxResId(RID_SUBSETSTR_NYIAKENG_PUACHUE_HMONG) );
+ break;
+ case UBLOCK_OTTOMAN_SIYAQ_NUMBERS:
+ aAllSubsets.emplace_back( 0x1ED00, 0x1ED4F, SvxResId(RID_SUBSETSTR_OTTOMAN_SIYAQ_NUMBERS) );
+ break;
+ case UBLOCK_SMALL_KANA_EXTENSION:
+ aAllSubsets.emplace_back( 0x1B130, 0x1B16F, SvxResId(RID_SUBSETSTR_SMALL_KANA_EXTENSION) );
+ break;
+ case UBLOCK_SYMBOLS_AND_PICTOGRAPHS_EXTENDED_A:
+ aAllSubsets.emplace_back( 0x1FA70, 0x1FAFF, SvxResId(RID_SUBSETSTR_SYMBOLS_AND_PICTOGRAPHS_EXTENDED_A) );
+ break;
+ case UBLOCK_TAMIL_SUPPLEMENT:
+ aAllSubsets.emplace_back( 0x11FC0, 0x11FFF, SvxResId(RID_SUBSETSTR_TAMIL_SUPPLEMENT) );
+ break;
+ case UBLOCK_WANCHO:
+ aAllSubsets.emplace_back( 0x1E2C0, 0x1E2FF, SvxResId(RID_SUBSETSTR_WANCHO) );
+ break;
+#endif
+#if (U_ICU_VERSION_MAJOR_NUM >= 66)
+ case UBLOCK_CHORASMIAN:
+ aAllSubsets.emplace_back( 0x10FB0, 0x10FDF, SvxResId(RID_SUBSETSTR_CHORASMIAN) );
+ break;
+ case UBLOCK_CJK_UNIFIED_IDEOGRAPHS_EXTENSION_G:
+ aAllSubsets.emplace_back( 0x30000, 0x3134F, SvxResId(RID_SUBSETSTR_CJK_UNIFIED_IDEOGRAPHS_EXTENSION_G) );
+ break;
+ case UBLOCK_DIVES_AKURU:
+ aAllSubsets.emplace_back( 0x11900, 0x1195F, SvxResId(RID_SUBSETSTR_DIVES_AKURU) );
+ break;
+ case UBLOCK_KHITAN_SMALL_SCRIPT:
+ aAllSubsets.emplace_back( 0x18B00, 0x18CFF, SvxResId(RID_SUBSETSTR_KHITAN_SMALL_SCRIPT) );
+ break;
+ case UBLOCK_LISU_SUPPLEMENT:
+ aAllSubsets.emplace_back( 0x11FB0, 0x11FBF, SvxResId(RID_SUBSETSTR_LISU_SUPPLEMENT) );
+ break;
+ case UBLOCK_SYMBOLS_FOR_LEGACY_COMPUTING:
+ aAllSubsets.emplace_back( 0x1FB00, 0x1FBFF, SvxResId(RID_SUBSETSTR_SYMBOLS_FOR_LEGACY_COMPUTING) );
+ break;
+ case UBLOCK_TANGUT_SUPPLEMENT:
+ aAllSubsets.emplace_back( 0x18D00, 0x18D7F, SvxResId(RID_SUBSETSTR_TANGUT_SUPPLEMENT) );
+ break;
+ case UBLOCK_YEZIDI:
+ aAllSubsets.emplace_back( 0x10E80, 0x10EBF, SvxResId(RID_SUBSETSTR_YEZIDI) );
+ break;
+#endif
+#if (U_ICU_VERSION_MAJOR_NUM >= 70)
+ case UBLOCK_ARABIC_EXTENDED_B:
+ aAllSubsets.emplace_back( 0x0870, 0x089F, SvxResId(RID_SUBSETSTR_ARABIC_EXTENDED_B) );
+ break;
+ case UBLOCK_CYPRO_MINOAN:
+ aAllSubsets.emplace_back( 0x12F90, 0x12FFF, SvxResId(RID_SUBSETSTR_CYPRO_MINOAN) );
+ break;
+ case UBLOCK_ETHIOPIC_EXTENDED_B:
+ aAllSubsets.emplace_back( 0x1E7E0, 0x1E7FF, SvxResId(RID_SUBSETSTR_ETHIOPIC_EXTENDED_B) );
+ break;
+ case UBLOCK_KANA_EXTENDED_B:
+ aAllSubsets.emplace_back( 0x1AFF0, 0x1AFFF, SvxResId(RID_SUBSETSTR_KANA_EXTENDED_B) );
+ break;
+ case UBLOCK_LATIN_EXTENDED_F:
+ aAllSubsets.emplace_back( 0x10780, 0x107BF, SvxResId(RID_SUBSETSTR_LATIN_EXTENDED_F) );
+ break;
+ case UBLOCK_LATIN_EXTENDED_G:
+ aAllSubsets.emplace_back( 0x1DF00, 0x1DFFF, SvxResId(RID_SUBSETSTR_LATIN_EXTENDED_G) );
+ break;
+ case UBLOCK_OLD_UYGHUR:
+ aAllSubsets.emplace_back( 0x10F70, 0x10FAF, SvxResId(RID_SUBSETSTR_OLD_UYGHUR) );
+ break;
+ case UBLOCK_TANGSA:
+ aAllSubsets.emplace_back( 0x16A70, 0x16ACF, SvxResId(RID_SUBSETSTR_TANGSA) );
+ break;
+ case UBLOCK_TOTO:
+ aAllSubsets.emplace_back( 0x1E290, 0x1E2BF, SvxResId(RID_SUBSETSTR_TOTO) );
+ break;
+ case UBLOCK_UNIFIED_CANADIAN_ABORIGINAL_SYLLABICS_EXTENDED_A:
+ aAllSubsets.emplace_back( 0x11AB0, 0x11ABF, SvxResId(RID_SUBSETSTR_UNIFIED_CANADIAN_ABORIGINAL_SYLLABICS_EXTENDED_A) );
+ break;
+ case UBLOCK_VITHKUQI:
+ aAllSubsets.emplace_back( 0x10570, 0x105BF, SvxResId(RID_SUBSETSTR_VITHKUQI) );
+ break;
+ case UBLOCK_ZNAMENNY_MUSICAL_NOTATION:
+ aAllSubsets.emplace_back( 0x1CF00, 0x1CFCF, SvxResId(RID_SUBSETSTR_ZNAMENNY_MUSICAL_NOTATION) );
+ break;
+#endif
+
+ }
+
+#if OSL_DEBUG_LEVEL > 0 && !defined NDEBUG
+ if (eBlock != UBLOCK_NO_BLOCK &&
+ eBlock != UBLOCK_INVALID_CODE &&
+ eBlock != UBLOCK_COUNT &&
+ eBlock != UBLOCK_HIGH_SURROGATES &&
+ eBlock != UBLOCK_HIGH_PRIVATE_USE_SURROGATES &&
+ eBlock != UBLOCK_LOW_SURROGATES)
+
+ {
+ UBlockCode eBlockStart = ublock_getCode(aAllSubsets.back().GetRangeMin());
+ UBlockCode eBlockEnd = ublock_getCode(aAllSubsets.back().GetRangeMax());
+ assert(eBlockStart == eBlockEnd && eBlockStart == eBlock);
+ }
+#endif
+ }
+
+ std::stable_sort(aAllSubsets.begin(), aAllSubsets.end());
+ return aAllSubsets;
+ }();
+
+ maSubsets = s_aAllSubsets;
+}
+
+void SubsetMap::ApplyCharMap( const FontCharMapRef& rxFontCharMap )
+{
+ if( !rxFontCharMap.is() )
+ return;
+
+ // remove subsets that are not matched in any range
+ maSubsets.erase(std::remove_if(maSubsets.begin(), maSubsets.end(),
+ [&rxFontCharMap](const Subset& rSubset) {
+ sal_uInt32 cMin = rSubset.GetRangeMin();
+ sal_uInt32 cMax = rSubset.GetRangeMax();
+ int nCount = rxFontCharMap->CountCharsInRange( cMin, cMax );
+ return nCount <= 0;
+ }),
+ maSubsets.end());
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/dialog/compressgraphicdialog.cxx b/svx/source/dialog/compressgraphicdialog.cxx
new file mode 100644
index 000000000..7e80b623e
--- /dev/null
+++ b/svx/source/dialog/compressgraphicdialog.cxx
@@ -0,0 +1,455 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include "dlgunit.hxx"
+#include <vcl/fieldvalues.hxx>
+#include <vcl/graph.hxx>
+#include <vcl/graphicfilter.hxx>
+#include <vcl/virdev.hxx>
+#include <vcl/svapp.hxx>
+#include <vcl/settings.hxx>
+#include <vcl/weld.hxx>
+#include <svx/strings.hrc>
+#include <svx/svdograf.hxx>
+#include <svx/sdgcpitm.hxx>
+#include <svx/dialmgr.hxx>
+#include <svx/graphichelper.hxx>
+#include <svx/compressgraphicdialog.hxx>
+#include <sfx2/dispatch.hxx>
+#include <sfx2/module.hxx>
+#include <comphelper/fileformat.h>
+#include <comphelper/propertyvalue.hxx>
+#include <com/sun/star/uno/Sequence.hxx>
+#include <tools/stream.hxx>
+#include <unotools/localedatawrapper.hxx>
+
+// tdf#146929 - remember user settings within the current session
+// memp is filled in dtor and restored after initialization
+namespace
+{
+ struct memParam {
+ bool ReduceResolutionCB = false;
+ int MFNewWidth = 1;
+ int MFNewHeight = 1;
+ bool LosslessRB = true;
+ bool JpegCompRB = false;
+ int CompressionMF = 6;
+ int QualityMF = 80;
+ int InterpolationCombo = 3;
+ };
+ memParam memp;
+}
+
+using namespace com::sun::star::uno;
+using namespace com::sun::star::beans;
+
+CompressGraphicsDialog::CompressGraphicsDialog( weld::Window* pParent, SdrGrafObj* pGraphicObj, SfxBindings& rBindings ) :
+ GenericDialogController( pParent, "svx/ui/compressgraphicdialog.ui", "CompressGraphicDialog" ),
+ m_xGraphicObj ( pGraphicObj ),
+ m_aGraphic ( pGraphicObj->GetGraphicObject().GetGraphic() ),
+ m_aViewSize100mm ( pGraphicObj->GetLogicRect().GetSize() ),
+ m_rBindings ( rBindings ),
+ m_dResolution ( 300 )
+{
+ const SdrGrafCropItem& rCrop = m_xGraphicObj->GetMergedItem(SDRATTR_GRAFCROP);
+ m_aCropRectangle = tools::Rectangle(rCrop.GetLeft(), rCrop.GetTop(), rCrop.GetRight(), rCrop.GetBottom());
+
+ Initialize();
+ recallParameter();
+}
+
+CompressGraphicsDialog::CompressGraphicsDialog( weld::Window* pParent, Graphic const & rGraphic, Size rViewSize100mm, tools::Rectangle const & rCropRectangle, SfxBindings& rBindings ) :
+ GenericDialogController( pParent, "svx/ui/compressgraphicdialog.ui", "CompressGraphicDialog" ),
+ m_xGraphicObj ( nullptr ),
+ m_aGraphic ( rGraphic ),
+ m_aViewSize100mm ( rViewSize100mm ),
+ m_aCropRectangle ( rCropRectangle ),
+ m_rBindings ( rBindings ),
+ m_dResolution ( 300 )
+{
+ Initialize();
+ recallParameter();
+}
+
+CompressGraphicsDialog::~CompressGraphicsDialog()
+{
+}
+
+void CompressGraphicsDialog::recallParameter()
+{
+ m_xReduceResolutionCB->set_active( memp.ReduceResolutionCB );
+ if (memp.ReduceResolutionCB && (memp.MFNewWidth > 1))
+ m_xMFNewWidth->set_value( memp.MFNewWidth );
+ if (memp.ReduceResolutionCB && (memp.MFNewHeight > 1))
+ m_xMFNewHeight->set_value( memp.MFNewHeight );
+
+ m_xLosslessRB->set_active( memp.LosslessRB );
+ m_xJpegCompRB->set_active( memp.JpegCompRB );
+ m_xCompressionMF->set_value( memp.CompressionMF );
+ m_xCompressionSlider->set_value( memp.CompressionMF );
+ m_xQualityMF->set_value( memp.QualityMF );
+ m_xQualitySlider->set_value( memp.QualityMF );
+
+ m_xInterpolationCombo->set_active( memp.InterpolationCombo );
+}
+
+void CompressGraphicsDialog::Initialize()
+{
+ m_xLabelGraphicType = m_xBuilder->weld_label("label-graphic-type");
+ m_xFixedText2 = m_xBuilder->weld_label("label-original-size");
+ m_xFixedText3 = m_xBuilder->weld_label("label-view-size");
+ m_xFixedText5 = m_xBuilder->weld_label("label-image-capacity");
+ m_xFixedText6 = m_xBuilder->weld_label("label-new-capacity");
+ m_xJpegCompRB = m_xBuilder->weld_radio_button("radio-jpeg");
+ m_xCompressionMF = m_xBuilder->weld_spin_button("spin-compression");
+ m_xCompressionSlider = m_xBuilder->weld_scale("scale-compression");
+ m_xLosslessRB = m_xBuilder->weld_radio_button("radio-lossless");
+ m_xQualityMF = m_xBuilder->weld_spin_button("spin-quality");
+ m_xQualitySlider = m_xBuilder->weld_scale("scale-quality");
+ m_xReduceResolutionCB = m_xBuilder->weld_check_button("checkbox-reduce-resolution");
+ m_xMFNewWidth = m_xBuilder->weld_spin_button("spin-new-width");
+ m_xMFNewHeight = m_xBuilder->weld_spin_button("spin-new-height");
+ m_xResolutionLB = m_xBuilder->weld_combo_box("combo-resolution");
+ m_xBtnCalculate = m_xBuilder->weld_button("calculate");
+ m_xInterpolationCombo = m_xBuilder->weld_combo_box("interpolation-method-combo");
+ m_xBtnOkay = m_xBuilder->weld_button("ok");
+
+ m_xInterpolationCombo->set_active_text("Lanczos");
+
+ m_xInterpolationCombo->connect_changed(LINK(this, CompressGraphicsDialog, NewInterpolationModifiedHdl));
+
+ m_xMFNewWidth->connect_changed( LINK( this, CompressGraphicsDialog, NewWidthModifiedHdl ));
+ m_xMFNewHeight->connect_changed( LINK( this, CompressGraphicsDialog, NewHeightModifiedHdl ));
+
+ m_xResolutionLB->connect_changed( LINK( this, CompressGraphicsDialog, ResolutionModifiedHdl ));
+ m_xBtnCalculate->connect_clicked( LINK( this, CompressGraphicsDialog, CalculateClickHdl ) );
+
+ m_xLosslessRB->connect_toggled( LINK( this, CompressGraphicsDialog, ToggleCompressionRB ) );
+ m_xJpegCompRB->connect_toggled( LINK( this, CompressGraphicsDialog, ToggleCompressionRB ) );
+
+ m_xReduceResolutionCB->connect_toggled( LINK( this, CompressGraphicsDialog, ToggleReduceResolutionRB ) );
+
+ m_xQualitySlider->connect_value_changed( LINK( this, CompressGraphicsDialog, SlideHdl ));
+ m_xCompressionSlider->connect_value_changed( LINK( this, CompressGraphicsDialog, SlideHdl ));
+ m_xQualityMF->connect_changed( LINK( this, CompressGraphicsDialog, NewQualityModifiedHdl ));
+ m_xCompressionMF->connect_changed( LINK( this, CompressGraphicsDialog, NewCompressionModifiedHdl ));
+
+ m_xJpegCompRB->set_active(true);
+ m_xReduceResolutionCB->set_active(true);
+
+ m_xBtnOkay->connect_clicked( LINK( this, CompressGraphicsDialog, OkayClickHdl ) );
+ UpdateNewWidthMF();
+ UpdateNewHeightMF();
+ UpdateResolutionLB();
+ Update();
+}
+
+void CompressGraphicsDialog::Update()
+{
+ auto pGfxLink = m_aGraphic.GetSharedGfxLink();
+
+ m_xLabelGraphicType->set_label(GraphicHelper::GetImageType(m_aGraphic));
+
+ const FieldUnit eFieldUnit = m_rBindings.GetDispatcher()->GetModule()->GetFieldUnit();
+ const LocaleDataWrapper& rLocaleWrapper( Application::GetSettings().GetLocaleDataWrapper() );
+ sal_Unicode cSeparator = rLocaleWrapper.getNumDecimalSep()[0];
+
+ ScopedVclPtrInstance<VirtualDevice> pDummyVDev;
+ pDummyVDev->EnableOutput( false );
+ pDummyVDev->SetMapMode( m_aGraphic.GetPrefMapMode() );
+
+ Size aPixelSize = m_aGraphic.GetSizePixel();
+ Size aOriginalSize100mm(pDummyVDev->PixelToLogic(m_aGraphic.GetSizePixel(), MapMode(MapUnit::Map100thMM)));
+
+ OUString aBitmapSizeString = SvxResId(STR_IMAGE_ORIGINAL_SIZE);
+ OUString aWidthString = GetUnitString( aOriginalSize100mm.Width(), eFieldUnit, cSeparator );
+ OUString aHeightString = GetUnitString( aOriginalSize100mm.Height(), eFieldUnit, cSeparator );
+ aBitmapSizeString = aBitmapSizeString.replaceAll("$(WIDTH)", aWidthString);
+ aBitmapSizeString = aBitmapSizeString.replaceAll("$(HEIGHT)", aHeightString);
+ aBitmapSizeString = aBitmapSizeString.replaceAll("$(WIDTH_IN_PX)", OUString::number(aPixelSize.Width()));
+ aBitmapSizeString = aBitmapSizeString.replaceAll("$(HEIGHT_IN_PX)", OUString::number(aPixelSize.Height()));
+ m_xFixedText2->set_label(aBitmapSizeString);
+
+ int aValX = static_cast<int>(aPixelSize.Width() / GetViewWidthInch());
+
+ OUString aViewSizeString = SvxResId(STR_IMAGE_VIEW_SIZE);
+
+ aWidthString = GetUnitString( m_aViewSize100mm.Width(), eFieldUnit, cSeparator );
+ aHeightString = GetUnitString( m_aViewSize100mm.Height(), eFieldUnit, cSeparator );
+ aViewSizeString = aViewSizeString.replaceAll("$(WIDTH)", aWidthString);
+ aViewSizeString = aViewSizeString.replaceAll("$(HEIGHT)", aHeightString);
+ aViewSizeString = aViewSizeString.replaceAll("$(DPI)", OUString::number(aValX));
+ m_xFixedText3->set_label(aViewSizeString);
+
+ m_aNativeSize = pGfxLink ? pGfxLink->GetDataSize() : 0;
+
+ OUString aNativeSizeString = SvxResId(STR_IMAGE_CAPACITY);
+ aNativeSizeString = aNativeSizeString.replaceAll("$(CAPACITY)", OUString::number( m_aNativeSize / 1024 ));
+ m_xFixedText5->set_label(aNativeSizeString);
+
+ m_xFixedText6->set_label("??");
+}
+
+void CompressGraphicsDialog::UpdateNewWidthMF()
+{
+ int nPixelX = static_cast<sal_Int32>( GetViewWidthInch() * m_dResolution );
+ m_xMFNewWidth->set_value(nPixelX);
+}
+
+void CompressGraphicsDialog::UpdateNewHeightMF()
+{
+ int nPixelY = static_cast<sal_Int32>( GetViewHeightInch() * m_dResolution );
+ m_xMFNewHeight->set_value(nPixelY);
+}
+
+void CompressGraphicsDialog::UpdateResolutionLB()
+{
+ m_xResolutionLB->set_entry_text( OUString::number( static_cast<sal_Int32>(m_dResolution) ) );
+}
+
+double CompressGraphicsDialog::GetViewWidthInch() const
+{
+ return static_cast<double>(vcl::ConvertValue(m_aViewSize100mm.Width(), 2, MapUnit::Map100thMM, FieldUnit::INCH)) / 100.0;
+}
+
+double CompressGraphicsDialog::GetViewHeightInch() const
+{
+ return static_cast<double>(vcl::ConvertValue(m_aViewSize100mm.Height(), 2, MapUnit::Map100thMM, FieldUnit::INCH)) / 100.0;
+}
+
+BmpScaleFlag CompressGraphicsDialog::GetSelectedInterpolationType() const
+{
+ OUString aSelectionText = m_xInterpolationCombo->get_active_text();
+
+ if( aSelectionText == "Lanczos" ) {
+ return BmpScaleFlag::Lanczos;
+ } else if( aSelectionText == "Bilinear" ) {
+ return BmpScaleFlag::BiLinear;
+ } else if( aSelectionText == "Bicubic" ) {
+ return BmpScaleFlag::BiCubic;
+ } else if ( aSelectionText == "None" ) {
+ return BmpScaleFlag::Fast;
+ }
+ return BmpScaleFlag::BestQuality;
+}
+
+void CompressGraphicsDialog::Compress(SvStream& aStream)
+{
+ BitmapEx aBitmap = m_aGraphic.GetBitmapEx();
+ if ( m_xReduceResolutionCB->get_active() )
+ {
+ tools::Long nPixelX = static_cast<tools::Long>( GetViewWidthInch() * m_dResolution );
+ tools::Long nPixelY = static_cast<tools::Long>( GetViewHeightInch() * m_dResolution );
+
+ aBitmap.Scale( Size( nPixelX, nPixelY ), GetSelectedInterpolationType() );
+ }
+ Graphic aScaledGraphic( aBitmap );
+ GraphicFilter& rFilter = GraphicFilter::GetGraphicFilter();
+
+ Sequence< PropertyValue > aFilterData{
+ comphelper::makePropertyValue("Interlaced", sal_Int32(0)),
+ comphelper::makePropertyValue("Compression", static_cast<sal_Int32>(m_xCompressionMF->get_value())),
+ comphelper::makePropertyValue("Quality", static_cast<sal_Int32>(m_xQualityMF->get_value()))
+ };
+
+ OUString aGraphicFormatName = m_xLosslessRB->get_active() ? OUString( "png" ) : OUString( "jpg" );
+
+ sal_uInt16 nFilterFormat = rFilter.GetExportFormatNumberForShortName( aGraphicFormatName );
+ rFilter.ExportGraphic( aScaledGraphic, u"none", aStream, nFilterFormat, &aFilterData );
+}
+
+IMPL_LINK_NOARG( CompressGraphicsDialog, OkayClickHdl, weld::Button&, void )
+{
+ memp.ReduceResolutionCB = m_xReduceResolutionCB->get_active();
+ memp.MFNewWidth = m_xMFNewWidth->get_value();
+ memp.MFNewHeight = m_xMFNewHeight->get_value();
+ memp.LosslessRB = m_xLosslessRB->get_active();
+ memp.JpegCompRB = m_xJpegCompRB->get_active();
+ memp.CompressionMF = m_xCompressionMF->get_value();
+ memp.QualityMF = m_xQualityMF->get_value();
+ memp.InterpolationCombo = m_xInterpolationCombo->get_active();
+ CompressGraphicsDialog::response(RET_OK);
+}
+
+IMPL_LINK_NOARG( CompressGraphicsDialog, NewWidthModifiedHdl, weld::Entry&, void )
+{
+ m_dResolution = m_xMFNewWidth->get_value() / GetViewWidthInch();
+
+ UpdateNewHeightMF();
+ UpdateResolutionLB();
+ Update();
+}
+
+IMPL_LINK( CompressGraphicsDialog, SlideHdl, weld::Scale&, rScale, void )
+{
+ if (&rScale == m_xQualitySlider.get())
+ m_xQualityMF->set_value(m_xQualitySlider->get_value());
+ else
+ m_xCompressionMF->set_value(m_xCompressionSlider->get_value());
+ Update();
+}
+
+IMPL_LINK_NOARG( CompressGraphicsDialog, NewInterpolationModifiedHdl, weld::ComboBox&, void )
+{
+ Update();
+}
+
+IMPL_LINK_NOARG( CompressGraphicsDialog, NewQualityModifiedHdl, weld::Entry&, void )
+{
+ m_xQualitySlider->set_value(m_xQualityMF->get_value());
+ Update();
+}
+
+IMPL_LINK_NOARG( CompressGraphicsDialog, NewCompressionModifiedHdl, weld::Entry&, void )
+{
+ m_xCompressionSlider->set_value(m_xCompressionMF->get_value());
+ Update();
+}
+
+IMPL_LINK_NOARG( CompressGraphicsDialog, NewHeightModifiedHdl, weld::Entry&, void )
+{
+ m_dResolution = m_xMFNewHeight->get_value() / GetViewHeightInch();
+
+ UpdateNewWidthMF();
+ UpdateResolutionLB();
+ Update();
+}
+
+IMPL_LINK_NOARG( CompressGraphicsDialog, ResolutionModifiedHdl, weld::ComboBox&, void )
+{
+ m_dResolution = static_cast<double>(m_xResolutionLB->get_active_text().toInt32());
+
+ UpdateNewWidthMF();
+ UpdateNewHeightMF();
+ Update();
+}
+
+IMPL_LINK_NOARG( CompressGraphicsDialog, ToggleCompressionRB, weld::Toggleable&, void )
+{
+ bool choice = m_xLosslessRB->get_active();
+ m_xCompressionMF->set_sensitive(choice);
+ m_xCompressionSlider->set_sensitive(choice);
+ m_xQualityMF->set_sensitive(!choice);
+ m_xQualitySlider->set_sensitive(!choice);
+ Update();
+}
+
+IMPL_LINK_NOARG( CompressGraphicsDialog, ToggleReduceResolutionRB, weld::Toggleable&, void )
+{
+ bool choice = m_xReduceResolutionCB->get_active();
+ m_xMFNewWidth->set_sensitive(choice);
+ m_xMFNewHeight->set_sensitive(choice);
+ m_xResolutionLB->set_sensitive(choice);
+ m_xInterpolationCombo->set_sensitive(choice);
+ Update();
+}
+
+IMPL_LINK_NOARG( CompressGraphicsDialog, CalculateClickHdl, weld::Button&, void )
+{
+ sal_Int32 aSize = 0;
+
+ if ( m_dResolution > 0.0 )
+ {
+ SvMemoryStream aMemStream;
+ aMemStream.SetVersion( SOFFICE_FILEFORMAT_CURRENT );
+ Compress( aMemStream );
+ aSize = aMemStream.TellEnd();
+ }
+
+ if ( aSize > 0 )
+ {
+ OUString aSizeAsString = OUString::number(aSize / 1024);
+
+ OUString aReductionSizeAsString;
+ if (m_aNativeSize > 0 )
+ aReductionSizeAsString = OUString::number( static_cast<sal_Int32>((m_aNativeSize - aSize) * 100.0 / m_aNativeSize) );
+ else
+ aReductionSizeAsString = "0";
+
+ OUString aNewSizeString = SvxResId(STR_IMAGE_CAPACITY_WITH_REDUCTION);
+ aNewSizeString = aNewSizeString.replaceAll("$(CAPACITY)", aSizeAsString);
+ aNewSizeString = aNewSizeString.replaceAll("$(REDUCTION)", aReductionSizeAsString);
+ m_xFixedText6->set_label(aNewSizeString);
+ }
+}
+
+tools::Rectangle CompressGraphicsDialog::GetScaledCropRectangle() const
+{
+ if ( m_xReduceResolutionCB->get_active() )
+ {
+ tools::Long nPixelX = static_cast<tools::Long>( GetViewWidthInch() * m_dResolution );
+ tools::Long nPixelY = static_cast<tools::Long>( GetViewHeightInch() * m_dResolution );
+ Size aSize = m_aGraphic.GetBitmapEx().GetSizePixel();
+ double aScaleX = nPixelX / static_cast<double>(aSize.Width());
+ double aScaleY = nPixelY / static_cast<double>(aSize.Height());
+
+ return tools::Rectangle(
+ m_aCropRectangle.Left() * aScaleX,
+ m_aCropRectangle.Top() * aScaleY,
+ m_aCropRectangle.Right() * aScaleX,
+ m_aCropRectangle.Bottom()* aScaleY);
+ }
+ else
+ {
+ return m_aCropRectangle;
+ }
+}
+
+Graphic CompressGraphicsDialog::GetCompressedGraphic()
+{
+ if ( m_dResolution > 0.0 )
+ {
+ SvMemoryStream aMemStream;
+ aMemStream.SetVersion( SOFFICE_FILEFORMAT_CURRENT );
+ Compress( aMemStream );
+ aMemStream.Seek( STREAM_SEEK_TO_BEGIN );
+ Graphic aResultGraphic;
+ GraphicFilter& rFilter = GraphicFilter::GetGraphicFilter();
+ rFilter.ImportGraphic( aResultGraphic, u"import", aMemStream );
+
+ return aResultGraphic;
+ }
+ return Graphic();
+}
+
+SdrGrafObj* CompressGraphicsDialog::GetCompressedSdrGrafObj()
+{
+ if ( m_dResolution > 0.0 )
+ {
+ SdrGrafObj* pNewObject(m_xGraphicObj->CloneSdrObject(m_xGraphicObj->getSdrModelFromSdrObject()));
+
+ if ( m_xReduceResolutionCB->get_active() )
+ {
+ tools::Rectangle aScaledCropedRectangle = GetScaledCropRectangle();
+ SdrGrafCropItem aNewCrop(
+ aScaledCropedRectangle.Left(),
+ aScaledCropedRectangle.Top(),
+ aScaledCropedRectangle.Right(),
+ aScaledCropedRectangle.Bottom());
+
+ pNewObject->SetMergedItem(aNewCrop);
+ }
+ pNewObject->SetGraphic( GetCompressedGraphic() );
+
+ return pNewObject;
+ }
+ return nullptr;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/dialog/connctrl.cxx b/svx/source/dialog/connctrl.cxx
new file mode 100644
index 000000000..0249b5dd9
--- /dev/null
+++ b/svx/source/dialog/connctrl.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 <vcl/svapp.hxx>
+
+#include <svx/connctrl.hxx>
+#include <svx/dlgutil.hxx>
+
+#include <svx/sdr/contact/displayinfo.hxx>
+#include <sdr/contact/objectcontactofobjlistpainter.hxx>
+#include <svx/svdmark.hxx>
+#include <svx/svdoedge.hxx>
+#include <svx/svdpage.hxx>
+#include <svx/svdview.hxx>
+#include <svx/sxelditm.hxx>
+
+#include <vcl/settings.hxx>
+#include <memory>
+
+SvxXConnectionPreview::SvxXConnectionPreview()
+ : pEdgeObj(nullptr)
+ , pView(nullptr)
+{
+ SetMapMode(MapMode(MapUnit::Map100thMM));
+}
+
+void SvxXConnectionPreview::SetDrawingArea(weld::DrawingArea* pDrawingArea)
+{
+ weld::CustomWidgetController::SetDrawingArea(pDrawingArea);
+ Size aSize(pDrawingArea->get_ref_device().LogicToPixel(Size(118 , 121), MapMode(MapUnit::MapAppFont)));
+ pDrawingArea->set_size_request(aSize.Width(), aSize.Height());
+ SetOutputSizePixel(aSize);
+}
+
+SvxXConnectionPreview::~SvxXConnectionPreview()
+{
+}
+
+void SvxXConnectionPreview::Resize()
+{
+ AdaptSize();
+
+ Invalidate();
+}
+
+void SvxXConnectionPreview::AdaptSize()
+{
+ // Adapt size
+ if( !mxSdrPage )
+ return;
+
+ SetMapMode(MapMode(MapUnit::Map100thMM));
+
+ OutputDevice* pOD = pView->GetFirstOutputDevice(); // GetWin( 0 );
+ tools::Rectangle aRect = mxSdrPage->GetAllObjBoundRect();
+
+ MapMode aMapMode = GetMapMode();
+ aMapMode.SetMapUnit( pOD->GetMapMode().GetMapUnit() );
+ SetMapMode( aMapMode );
+
+ MapMode aDisplayMap( aMapMode );
+ Point aNewPos;
+ Size aNewSize;
+ const Size aWinSize = GetDrawingArea()->get_ref_device().PixelToLogic(GetOutputSizePixel(), aDisplayMap);
+ const tools::Long nWidth = aWinSize.Width();
+ const tools::Long nHeight = aWinSize.Height();
+ if (aRect.GetHeight() == 0)
+ return;
+ double fRectWH = static_cast<double>(aRect.GetWidth()) / aRect.GetHeight();
+ if (nHeight == 0)
+ return;
+ double fWinWH = static_cast<double>(nWidth) / nHeight;
+
+ // Adapt bitmap to Thumb size (not here!)
+ if ( fRectWH < fWinWH)
+ {
+ aNewSize.setWidth( static_cast<tools::Long>( static_cast<double>(nHeight) * fRectWH ) );
+ aNewSize.setHeight( nHeight );
+ }
+ else
+ {
+ aNewSize.setWidth( nWidth );
+ aNewSize.setHeight( static_cast<tools::Long>( static_cast<double>(nWidth) / fRectWH ) );
+ }
+
+ Fraction aFrac1( aWinSize.Width(), aRect.GetWidth() );
+ Fraction aFrac2( aWinSize.Height(), aRect.GetHeight() );
+ Fraction aMinFrac( aFrac1 <= aFrac2 ? aFrac1 : aFrac2 );
+
+ // Implement MapMode
+ aDisplayMap.SetScaleX( aMinFrac );
+ aDisplayMap.SetScaleY( aMinFrac );
+
+ // Centering
+ aNewPos.setX( ( nWidth - aNewSize.Width() ) >> 1 );
+ aNewPos.setY( ( nHeight - aNewSize.Height() ) >> 1 );
+
+ aDisplayMap.SetOrigin(OutputDevice::LogicToLogic(aNewPos, aMapMode, aDisplayMap));
+ SetMapMode( aDisplayMap );
+
+ // Origin
+ aNewPos = aDisplayMap.GetOrigin();
+ aNewPos -= Point( aRect.Left(), aRect.Top() );
+ aDisplayMap.SetOrigin( aNewPos );
+ SetMapMode( aDisplayMap );
+
+ MouseEvent aMEvt( Point(), 1, MouseEventModifiers::NONE, MOUSE_RIGHT );
+ MouseButtonDown( aMEvt );
+}
+
+void SvxXConnectionPreview::Construct()
+{
+ DBG_ASSERT( pView, "No valid view is passed on! ");
+
+ const SdrMarkList& rMarkList = pView->GetMarkedObjectList();
+ const size_t nMarkCount = rMarkList.GetMarkCount();
+
+ if( nMarkCount >= 1 )
+ {
+ bool bFound = false;
+
+ for( size_t i = 0; i < nMarkCount && !bFound; ++i )
+ {
+ const SdrObject* pObj = rMarkList.GetMark( i )->GetMarkedSdrObj();
+ SdrInventor nInv = pObj->GetObjInventor();
+ SdrObjKind nId = pObj->GetObjIdentifier();
+ if( nInv == SdrInventor::Default && nId == SdrObjKind::Edge )
+ {
+ bFound = true;
+
+ // potential memory leak here (!). Create SdrObjList only when there is
+ // not yet one.
+ if(!mxSdrPage)
+ {
+ mxSdrPage = new SdrPage(
+ pView->getSdrModelFromSdrView(),
+ false);
+ }
+
+ const SdrEdgeObj* pTmpEdgeObj = static_cast<const SdrEdgeObj*>(pObj);
+ pEdgeObj = pTmpEdgeObj->CloneSdrObject(mxSdrPage->getSdrModelFromSdrPage());
+
+ SdrObjConnection& rConn1 = pEdgeObj->GetConnection( true );
+ SdrObjConnection& rConn2 = pEdgeObj->GetConnection( false );
+
+ rConn1 = pTmpEdgeObj->GetConnection( true );
+ rConn2 = pTmpEdgeObj->GetConnection( false );
+
+ SdrObject* pTmpObj1 = pTmpEdgeObj->GetConnectedNode( true );
+ SdrObject* pTmpObj2 = pTmpEdgeObj->GetConnectedNode( false );
+
+ if( pTmpObj1 )
+ {
+ SdrObject* pObj1 = pTmpObj1->CloneSdrObject(mxSdrPage->getSdrModelFromSdrPage());
+ mxSdrPage->InsertObject( pObj1 );
+ pEdgeObj->ConnectToNode( true, pObj1 );
+ }
+
+ if( pTmpObj2 )
+ {
+ SdrObject* pObj2 = pTmpObj2->CloneSdrObject(mxSdrPage->getSdrModelFromSdrPage());
+ mxSdrPage->InsertObject( pObj2 );
+ pEdgeObj->ConnectToNode( false, pObj2 );
+ }
+
+ mxSdrPage->InsertObject( pEdgeObj );
+ }
+ }
+ }
+
+ if( !pEdgeObj )
+ {
+ pEdgeObj = new SdrEdgeObj(pView->getSdrModelFromSdrView());
+ }
+
+ AdaptSize();
+}
+
+void SvxXConnectionPreview::Paint(vcl::RenderContext& rRenderContext, const tools::Rectangle&)
+{
+ rRenderContext.Push(vcl::PushFlags::ALL);
+
+ rRenderContext.SetMapMode(GetMapMode());
+
+ const StyleSettings& rStyles = Application::GetSettings().GetStyleSettings();
+ rRenderContext.SetDrawMode(rStyles.GetHighContrastMode() ? OUTPUT_DRAWMODE_CONTRAST : OUTPUT_DRAWMODE_COLOR);
+ rRenderContext.SetBackground(Wallpaper(rStyles.GetFieldColor()));
+
+ if (mxSdrPage)
+ {
+ // This will not work anymore. To not start at Adam and Eve, i will
+ // ATM not try to change all this stuff to really using an own model
+ // and a view. I will just try to provide a mechanism to paint such
+ // objects without own model and without a page/view with the new
+ // mechanism.
+
+ // New stuff: Use an ObjectContactOfObjListPainter.
+ sdr::contact::SdrObjectVector aObjectVector;
+
+ for (size_t a = 0; a < mxSdrPage->GetObjCount(); ++a)
+ {
+ SdrObject* pObject = mxSdrPage->GetObj(a);
+ DBG_ASSERT(pObject,
+ "SvxXConnectionPreview::Paint: Corrupt ObjectList (!)");
+ aObjectVector.push_back(pObject);
+ }
+
+ sdr::contact::ObjectContactOfObjListPainter aPainter(rRenderContext, std::move(aObjectVector), nullptr);
+ sdr::contact::DisplayInfo aDisplayInfo;
+
+ // do processing
+ aPainter.ProcessDisplay(aDisplayInfo);
+ }
+
+ rRenderContext.Pop();
+}
+
+void SvxXConnectionPreview::SetAttributes( const SfxItemSet& rInAttrs )
+{
+ pEdgeObj->SetMergedItemSetAndBroadcast(rInAttrs);
+
+ Invalidate();
+}
+
+// Get number of lines which are offset based on the preview object
+
+sal_uInt16 SvxXConnectionPreview::GetLineDeltaCount() const
+{
+ const SfxItemSet& rSet = pEdgeObj->GetMergedItemSet();
+ sal_uInt16 nCount(0);
+
+ if(SfxItemState::DONTCARE != rSet.GetItemState(SDRATTR_EDGELINEDELTACOUNT))
+ nCount = rSet.Get(SDRATTR_EDGELINEDELTACOUNT).GetValue();
+
+ return nCount;
+}
+
+bool SvxXConnectionPreview::MouseButtonDown( const MouseEvent& rMEvt )
+{
+ bool bZoomIn = rMEvt.IsLeft() && !rMEvt.IsShift();
+ bool bZoomOut = rMEvt.IsRight() || rMEvt.IsShift();
+ bool bCtrl = rMEvt.IsMod1();
+
+ if( bZoomIn || bZoomOut )
+ {
+ MapMode aMapMode = GetMapMode();
+ Fraction aXFrac = aMapMode.GetScaleX();
+ Fraction aYFrac = aMapMode.GetScaleY();
+ std::unique_ptr<Fraction> pMultFrac;
+
+ if( bZoomIn )
+ {
+ if( bCtrl )
+ pMultFrac.reset(new Fraction( 3, 2 ));
+ else
+ pMultFrac.reset(new Fraction( 11, 10 ));
+ }
+ else
+ {
+ if( bCtrl )
+ pMultFrac.reset(new Fraction( 2, 3 ));
+ else
+ pMultFrac.reset(new Fraction( 10, 11 ));
+ }
+
+ aXFrac *= *pMultFrac;
+ aYFrac *= *pMultFrac;
+ if( static_cast<double>(aXFrac) > 0.001 && static_cast<double>(aXFrac) < 1000.0 &&
+ static_cast<double>(aYFrac) > 0.001 && static_cast<double>(aYFrac) < 1000.0 )
+ {
+ aMapMode.SetScaleX( aXFrac );
+ aMapMode.SetScaleY( aYFrac );
+ SetMapMode( aMapMode );
+
+ Size aOutSize(GetOutputSizePixel());
+ aOutSize = GetDrawingArea()->get_ref_device().PixelToLogic(aOutSize);
+
+ Point aPt( aMapMode.GetOrigin() );
+ tools::Long nX = static_cast<tools::Long>( ( static_cast<double>(aOutSize.Width()) - ( static_cast<double>(aOutSize.Width()) * static_cast<double>(*pMultFrac) ) ) / 2.0 + 0.5 );
+ tools::Long nY = static_cast<tools::Long>( ( static_cast<double>(aOutSize.Height()) - ( static_cast<double>(aOutSize.Height()) * static_cast<double>(*pMultFrac) ) ) / 2.0 + 0.5 );
+ aPt.AdjustX(nX );
+ aPt.AdjustY(nY );
+
+ aMapMode.SetOrigin( aPt );
+ SetMapMode( aMapMode );
+
+ Invalidate();
+ }
+ }
+
+ return true;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/dialog/contimp.hxx b/svx/source/dialog/contimp.hxx
new file mode 100644
index 000000000..1c7e10959
--- /dev/null
+++ b/svx/source/dialog/contimp.hxx
@@ -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 .
+ */
+#ifndef INCLUDED_SVX_SOURCE_DIALOG_CONTIMP_HXX
+#define INCLUDED_SVX_SOURCE_DIALOG_CONTIMP_HXX
+
+#include <sfx2/ctrlitem.hxx>
+#include "contwnd.hxx"
+#include <vcl/idle.hxx>
+
+class SvxSuperContourDlg;
+
+class SvxContourDlgItem : public SfxControllerItem
+{
+ SvxSuperContourDlg& rDlg;
+
+protected:
+
+ virtual void StateChangedAtToolBoxControl( sal_uInt16 nSID, SfxItemState eState, const SfxPoolItem* pState ) override;
+
+public:
+
+ SvxContourDlgItem( SvxSuperContourDlg& rDlg, SfxBindings& rBindings );
+};
+
+class ContourWindow;
+
+class StatusColor : public weld::CustomWidgetController
+{
+private:
+ ContourWindow& m_rWnd;
+public:
+ StatusColor(ContourWindow& rWnd)
+ : m_rWnd(rWnd)
+ {
+ }
+ virtual void Paint(vcl::RenderContext& rRenderContext, const tools::Rectangle& rRect) override;
+ virtual void SetDrawingArea(weld::DrawingArea* pDrawingArea) override
+ {
+ weld::CustomWidgetController::SetDrawingArea(pDrawingArea);
+ Size aSize(pDrawingArea->get_approximate_digit_width() * 3,
+ pDrawingArea->get_text_height());
+ pDrawingArea->set_size_request(aSize.Width(), aSize.Height());
+ SetOutputSizePixel(aSize);
+ }
+};
+
+class SvxSuperContourDlg
+{
+ Graphic aGraphic;
+ Graphic aUndoGraphic;
+ Graphic aRedoGraphic;
+ Graphic aUpdateGraphic;
+ tools::PolyPolygon aUpdatePolyPoly;
+ Idle aUpdateIdle;
+ Idle aCreateIdle;
+ SfxBindings* mpBindings;
+ void* pUpdateEditingObject;
+ void* pCheckObj;
+ SvxContourDlgItem aContourItem;
+ sal_Int32 mnGrfChanged;
+ bool bExecState;
+ bool bUpdateGraphicLinked;
+ bool bGraphicLinked;
+
+ weld::Dialog& m_rDialog;
+ std::unique_ptr<ContourWindow> m_xContourWnd;
+ std::unique_ptr<StatusColor> m_xStbStatusColor;
+ std::unique_ptr<weld::Toolbar> m_xTbx1;
+ std::unique_ptr<weld::MetricSpinButton> m_xMtfTolerance;
+ std::unique_ptr<weld::Label> m_xStbStatus2;
+ std::unique_ptr<weld::Label> m_xStbStatus3;
+ std::unique_ptr<weld::Button> m_xCancelBtn;
+ std::unique_ptr<weld::CustomWeld> m_xStbStatusColorWeld;
+ std::unique_ptr<weld::CustomWeld> m_xContourWndWeld;
+
+ DECL_LINK( Tbx1ClickHdl, const OString&, void );
+ DECL_LINK( MousePosHdl, GraphCtrl*, void );
+ DECL_LINK( GraphSizeHdl, GraphCtrl*, void );
+ DECL_LINK( UpdateHdl, Timer *, void );
+ DECL_LINK( CreateHdl, Timer *, void );
+ DECL_LINK( StateHdl, GraphCtrl*, void );
+ DECL_LINK( PipetteHdl, ContourWindow&, void );
+ DECL_LINK( PipetteClickHdl, ContourWindow&, void );
+ DECL_LINK( WorkplaceClickHdl, ContourWindow&, void );
+ DECL_LINK( CancelHdl, weld::Button&, void );
+
+ void SetActiveTool(std::string_view rId);
+ void SetActivePoly(std::string_view rId);
+
+ SfxBindings& GetBindings() { return *mpBindings; }
+
+public:
+
+ SvxSuperContourDlg(weld::Builder& rBuilder, weld::Dialog& rDialog, SfxBindings* pBindings);
+ ~SvxSuperContourDlg();
+
+ void SetExecState( bool bEnable );
+
+ void SetGraphic( const Graphic& rGraphic );
+ const Graphic& GetGraphic() const { return aGraphic; }
+ bool IsGraphicChanged() const { return mnGrfChanged > 0; }
+
+ void SetPolyPolygon( const tools::PolyPolygon& rPolyPoly );
+ tools::PolyPolygon GetPolyPolygon();
+
+ const void* GetEditingObject() const { return pCheckObj; }
+
+ void UpdateGraphic( const Graphic& rGraphic, bool bGraphicLinked,
+ const tools::PolyPolygon* pPolyPoly,
+ void* pEditingObj );
+};
+
+
+#endif // INCLUDED_SVX_SOURCE_DIALOG_CONTIMP_HXX
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/dialog/contwnd.cxx b/svx/source/dialog/contwnd.cxx
new file mode 100644
index 000000000..48ace0ac7
--- /dev/null
+++ b/svx/source/dialog/contwnd.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 "contwnd.hxx"
+#include <svx/svdpage.hxx>
+#include <svx/svdopath.hxx>
+#include <svx/xfillit0.hxx>
+#include <svx/xfltrit.hxx>
+#include <svx/xflclit.hxx>
+#include <basegfx/polygon/b2dpolypolygontools.hxx>
+#include <svx/sdrpaintwindow.hxx>
+#include <vcl/ptrstyle.hxx>
+
+using namespace css;
+
+#define TRANSCOL COL_WHITE
+
+ContourWindow::ContourWindow(weld::Dialog* pDialog)
+ : GraphCtrl(pDialog)
+ , aWorkRect(0, 0, 0, 0)
+ , bPipetteMode(false)
+ , bWorkplaceMode(false)
+ , bClickValid(false)
+{
+}
+
+void ContourWindow::SetPolyPolygon(const tools::PolyPolygon& rPolyPoly)
+{
+ SdrPage* pPage = pModel->GetPage(0);
+ const sal_uInt16 nPolyCount = rPolyPoly.Count();
+
+ // First delete all drawing objects
+ aPolyPoly = rPolyPoly;
+
+ // To avoid to have destroyed objects which are still selected, it is necessary to deselect
+ // them first (!)
+ pView->UnmarkAllObj();
+
+ // clear SdrObjects with broadcasting
+ pPage->ClearSdrObjList();
+
+ for (sal_uInt16 i = 0; i < nPolyCount; i++)
+ {
+ basegfx::B2DPolyPolygon aPolyPolygon;
+ aPolyPolygon.append(aPolyPoly[ i ].getB2DPolygon());
+ SdrPathObj* pPathObj = new SdrPathObj(
+ *pModel,
+ SdrObjKind::PathFill,
+ aPolyPolygon);
+
+ SfxItemSet aSet(pModel->GetItemPool());
+
+ aSet.Put(XFillStyleItem(drawing::FillStyle_SOLID));
+ aSet.Put(XFillColorItem("", TRANSCOL));
+ aSet.Put(XFillTransparenceItem(50) );
+
+ pPathObj->SetMergedItemSetAndBroadcast(aSet);
+
+ pPage->InsertObject( pPathObj );
+ }
+
+ if (nPolyCount)
+ {
+ pView->MarkAll();
+ pView->CombineMarkedObjects(false);
+ }
+
+ pModel->SetChanged(false);
+}
+
+const tools::PolyPolygon& ContourWindow::GetPolyPolygon()
+{
+ if ( pModel->IsChanged() )
+ {
+ SdrPage* pPage = pModel->GetPage( 0 );
+
+ aPolyPoly = tools::PolyPolygon();
+
+ if ( pPage && pPage->GetObjCount() )
+ {
+ SdrPathObj* pPathObj = static_cast<SdrPathObj*>(pPage->GetObj(0));
+ // Not sure if subdivision is needed for ContourWindow, but maybe it cannot handle
+ // curves at all. Keeping subdivision here for security
+ const basegfx::B2DPolyPolygon aB2DPolyPolygon(basegfx::utils::adaptiveSubdivideByAngle(pPathObj->GetPathPoly()));
+ aPolyPoly = tools::PolyPolygon(aB2DPolyPolygon);
+ }
+
+ pModel->SetChanged( false );
+ }
+
+ return aPolyPoly;
+}
+
+void ContourWindow::InitSdrModel()
+{
+ GraphCtrl::InitSdrModel();
+
+ SfxItemSet aSet( pModel->GetItemPool() );
+
+ aSet.Put( XFillColorItem( "", TRANSCOL ) );
+ aSet.Put( XFillTransparenceItem( 50 ) );
+ pView->SetAttributes( aSet );
+ pView->SetFrameDragSingles();
+}
+
+void ContourWindow::SdrObjCreated( const SdrObject& )
+{
+ pView->MarkAll();
+ pView->CombineMarkedObjects( false );
+}
+
+bool ContourWindow::IsContourChanged() const
+{
+ SdrPage* pPage = pModel->GetPage( 0 );
+ bool bRet = false;
+
+ if ( pPage && pPage->GetObjCount() )
+ bRet = static_cast<SdrPathObj*>( pPage->GetObj( 0 ) )->GetPathPoly().count() && pModel->IsChanged();
+
+ return bRet;
+}
+
+bool ContourWindow::MouseButtonDown( const MouseEvent& rMEvt )
+{
+ if ( bWorkplaceMode )
+ {
+ const Point aLogPt(GetDrawingArea()->get_ref_device().PixelToLogic(rMEvt.GetPosPixel()));
+
+ SetPolyPolygon( tools::PolyPolygon() );
+ aWorkRect = tools::Rectangle( aLogPt, aLogPt );
+ Invalidate(tools::Rectangle(Point(), GetGraphicSize()));
+ SetEditMode( true );
+ }
+
+ if (!bPipetteMode)
+ return GraphCtrl::MouseButtonDown( rMEvt );
+
+ return true;
+}
+
+bool ContourWindow::MouseMove( const MouseEvent& rMEvt )
+{
+ bClickValid = false;
+
+ if ( bPipetteMode )
+ {
+ const Point aLogPt( GetDrawingArea()->get_ref_device().PixelToLogic( rMEvt.GetPosPixel() ) );
+
+ aPipetteColor = GetDrawingArea()->get_ref_device().GetPixel( aLogPt );
+ weld::CustomWidgetController::MouseMove( rMEvt );
+
+ if ( aPipetteLink.IsSet() && tools::Rectangle( Point(), GetGraphicSize() ).Contains( aLogPt ) )
+ {
+ SetPointer( PointerStyle::RefHand );
+ aPipetteLink.Call( *this );
+ }
+
+ return true;
+ }
+
+ return GraphCtrl::MouseMove( rMEvt );
+}
+
+bool ContourWindow::MouseButtonUp(const MouseEvent& rMEvt)
+{
+ const tools::Rectangle aGraphRect( Point(), GetGraphicSize() );
+ const Point aLogPt( GetDrawingArea()->get_ref_device().PixelToLogic( rMEvt.GetPosPixel() ) );
+
+ bClickValid = aGraphRect.Contains( aLogPt );
+ ReleaseMouse();
+
+ if ( bPipetteMode )
+ {
+ weld::CustomWidgetController::MouseButtonUp( rMEvt );
+
+ aPipetteClickLink.Call( *this );
+
+ return true;
+ }
+ else if ( bWorkplaceMode )
+ {
+ GraphCtrl::MouseButtonUp( rMEvt );
+
+ aWorkRect.SetRight( aLogPt.X() );
+ aWorkRect.SetBottom( aLogPt.Y() );
+ aWorkRect.Intersection( aGraphRect );
+ aWorkRect.Justify();
+
+ if ( aWorkRect.Left() != aWorkRect.Right() && aWorkRect.Top() != aWorkRect.Bottom() )
+ {
+ tools::PolyPolygon _aPolyPoly( GetPolyPolygon() );
+
+ _aPolyPoly.Clip( aWorkRect );
+ SetPolyPolygon( _aPolyPoly );
+ pView->SetWorkArea( aWorkRect );
+ }
+ else
+ pView->SetWorkArea( aGraphRect );
+
+ Invalidate( aGraphRect );
+
+ aWorkplaceClickLink.Call( *this );
+
+ return false;
+ }
+
+ return GraphCtrl::MouseButtonUp( rMEvt );
+}
+
+void ContourWindow::Paint(vcl::RenderContext& rRenderContext, const tools::Rectangle& rRect)
+{
+ // #i75482#
+ // encapsulate the redraw using Begin/End and use the returned
+ // data to get the target output device (e.g. when pre-rendering)
+ SdrPaintWindow* pPaintWindow = pView->BeginCompleteRedraw(&rRenderContext);
+ pPaintWindow->SetOutputToWindow(true);
+ OutputDevice& rTarget = pPaintWindow->GetTargetOutputDevice();
+
+ const Graphic& rGraphic = GetGraphic();
+ rTarget.Push(vcl::PushFlags::LINECOLOR |vcl::PushFlags::FILLCOLOR);
+ rTarget.SetLineColor(COL_BLACK);
+ rTarget.SetFillColor(COL_WHITE);
+ rTarget.DrawRect( tools::Rectangle( Point(), GetGraphicSize() ) );
+ rTarget.Pop();
+
+ if (rGraphic.GetType() != GraphicType::NONE)
+ rGraphic.Draw(rTarget, Point(), GetGraphicSize());
+
+ if (aWorkRect.Left() != aWorkRect.Right() && aWorkRect.Top() != aWorkRect.Bottom())
+ {
+ tools::PolyPolygon _aPolyPoly(2);
+ rTarget.Push(vcl::PushFlags::FILLCOLOR);
+ _aPolyPoly.Insert(tools::Rectangle(Point(), GetGraphicSize()));
+ _aPolyPoly.Insert(aWorkRect);
+ rTarget.SetFillColor(COL_LIGHTRED);
+ rTarget.DrawTransparent(_aPolyPoly, 50);
+ rTarget.Pop();
+ }
+
+ // #i75482#
+ const vcl::Region aRepaintRegion(rRect);
+ pView->DoCompleteRedraw(*pPaintWindow, aRepaintRegion);
+ pView->EndCompleteRedraw(*pPaintWindow, true);
+}
+
+void ContourWindow::SetDrawingArea(weld::DrawingArea* pDrawingArea)
+{
+ GraphCtrl::SetDrawingArea(pDrawingArea);
+ Size aSize(pDrawingArea->get_ref_device().LogicToPixel(Size(270, 170), MapMode(MapUnit::MapAppFont)));
+ pDrawingArea->set_size_request(aSize.Width(), aSize.Height());
+ SetOutputSizePixel(aSize);
+ SetSdrMode(true);
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/dialog/contwnd.hxx b/svx/source/dialog/contwnd.hxx
new file mode 100644
index 000000000..1bad623c0
--- /dev/null
+++ b/svx/source/dialog/contwnd.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_SVX_SOURCE_DIALOG_CONTWND_HXX
+#define INCLUDED_SVX_SOURCE_DIALOG_CONTWND_HXX
+
+#include <tools/poly.hxx>
+#include <svx/graphctl.hxx>
+
+class ContourWindow final : public GraphCtrl
+{
+ tools::PolyPolygon aPolyPoly;
+ Color aPipetteColor;
+ tools::Rectangle aWorkRect;
+ Link<ContourWindow&,void> aPipetteLink;
+ Link<ContourWindow&,void> aPipetteClickLink;
+ Link<ContourWindow&,void> aWorkplaceClickLink;
+ bool bPipetteMode;
+ bool bWorkplaceMode;
+ bool bClickValid;
+
+ virtual bool MouseButtonDown(const MouseEvent& rMEvt) override;
+ virtual bool MouseMove(const MouseEvent& rMEvt) override;
+ virtual bool MouseButtonUp(const MouseEvent& rMEvt) override;
+ virtual void SdrObjCreated( const SdrObject& rObj ) override;
+ virtual void InitSdrModel() override;
+ virtual void Paint( vcl::RenderContext& rRenderContext, const tools::Rectangle& rRect ) override;
+ virtual void SetDrawingArea(weld::DrawingArea* pDrawingArea) override;
+
+public:
+
+ ContourWindow(weld::Dialog* pDialog);
+
+ void SetPolyPolygon( const tools::PolyPolygon& rPolyPoly );
+ const tools::PolyPolygon& GetPolyPolygon();
+
+ void SetPipetteMode( const bool bPipette ) { bPipetteMode = bPipette; }
+ const Color& GetPipetteColor() const { return aPipetteColor; }
+
+ bool IsClickValid() const { return bClickValid; }
+ bool IsContourChanged() const;
+
+ void SetWorkplaceMode( const bool bWorkplace ) { bWorkplaceMode = bWorkplace; }
+ const tools::Rectangle& GetWorkRect() const { return aWorkRect; }
+
+ void SetPipetteHdl( const Link<ContourWindow&,void>& rLink ) { aPipetteLink = rLink; }
+ void SetPipetteClickHdl( const Link<ContourWindow&,void>& rLink ) { aPipetteClickLink = rLink; }
+ void SetWorkplaceClickHdl( const Link<ContourWindow&,void>& rLink ) { aWorkplaceClickLink = rLink; }
+};
+
+
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/dialog/crashreportdlg.cxx b/svx/source/dialog/crashreportdlg.cxx
new file mode 100644
index 000000000..33c879866
--- /dev/null
+++ b/svx/source/dialog/crashreportdlg.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/.
+ */
+
+#include "crashreportdlg.hxx"
+
+#include <desktop/crashreport.hxx>
+#include <sfx2/safemode.hxx>
+#include <comphelper/processfactory.hxx>
+#include <i18nlangtag/languagetag.hxx>
+#include <unotools/configmgr.hxx>
+#include <officecfg/Office/Common.hxx>
+
+#include <com/sun/star/task/OfficeRestartManager.hpp>
+#include <com/sun/star/task/XInteractionHandler.hpp>
+
+CrashReportDialog::CrashReportDialog(weld::Window* pParent)
+ : GenericDialogController(pParent, "svx/ui/crashreportdlg.ui", "CrashReportDialog")
+ , mxBtnSend(m_xBuilder->weld_button("btn_send"))
+ , mxBtnCancel(m_xBuilder->weld_button("btn_cancel"))
+ , mxBtnClose(m_xBuilder->weld_button("btn_close"))
+ , mxEditPreUpload(m_xBuilder->weld_label("ed_pre"))
+ , mxEditPostUpload(m_xBuilder->weld_text_view("ed_post"))
+ , mxFtBugReport(m_xBuilder->weld_text_view("ed_bugreport"))
+ , mxCBSafeMode(m_xBuilder->weld_check_button("check_safemode"))
+ , mxPrivacyPolicyButton(m_xBuilder->weld_link_button("btnPrivacyPolicy"))
+{
+ maSuccessMsg = mxEditPostUpload->get_text();
+
+ auto nWidth = mxEditPreUpload->get_preferred_size().Width();
+ nWidth = std::max(nWidth, mxCBSafeMode->get_size_request().Width());
+ mxEditPreUpload->set_size_request(nWidth, -1);
+ mxCBSafeMode->set_size_request(nWidth, -1);
+
+ mxBtnSend->connect_clicked(LINK(this, CrashReportDialog, BtnHdl));
+ mxBtnCancel->connect_clicked(LINK(this, CrashReportDialog, BtnHdl));
+ mxBtnClose->connect_clicked(LINK(this, CrashReportDialog, BtnHdl));
+
+ mxPrivacyPolicyButton->set_uri(
+ officecfg::Office::Common::Menus::PrivacyPolicyURL::get()
+ + "?type=crashreport&LOvers=" + utl::ConfigManager::getProductVersion()
+ + "&LOlocale=" + LanguageTag(utl::ConfigManager::getUILocale()).getBcp47());
+}
+
+CrashReportDialog::~CrashReportDialog() {}
+
+short CrashReportDialog::run()
+{
+ short nRet = GenericDialogController::run();
+
+ // Check whether to go to safe mode
+ if (mxCBSafeMode->get_active())
+ {
+ sfx2::SafeMode::putFlag();
+ css::task::OfficeRestartManager::get(comphelper::getProcessComponentContext())
+ ->requestRestart(css::uno::Reference<css::task::XInteractionHandler>());
+ }
+ return nRet;
+}
+
+IMPL_LINK(CrashReportDialog, BtnHdl, weld::Button&, rBtn, void)
+{
+ if (&rBtn == mxBtnSend.get())
+ {
+ std::string response;
+ bool bSuccess = CrashReporter::readSendConfig(response);
+
+ OUString aCrashID = OUString::createFromAscii(response.c_str());
+
+ if (bSuccess)
+ {
+ OUString aProcessedMessage
+ = maSuccessMsg.replaceAll("%CRASHID", aCrashID.replaceAll("Crash-ID=", ""));
+
+ // vclbuilder seems to replace _ with ~ even in text
+ mxEditPostUpload->set_text(aProcessedMessage.replaceAll("~", "_"));
+ }
+ else
+ {
+ mxEditPostUpload->set_text(aCrashID);
+ }
+
+ mxBtnClose->show();
+ mxFtBugReport->show();
+ mxEditPostUpload->show();
+ mxBtnSend->set_sensitive(false);
+ mxBtnCancel->set_sensitive(false);
+ mxBtnClose->grab_focus();
+
+ mxEditPreUpload->hide();
+ mxBtnSend->hide();
+ mxBtnCancel->hide();
+
+ m_xDialog->resize_to_request();
+ }
+ else if (&rBtn == mxBtnCancel.get())
+ {
+ m_xDialog->response(RET_CLOSE);
+ }
+ else if (&rBtn == mxBtnClose.get())
+ {
+ m_xDialog->response(RET_CLOSE);
+ }
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/dialog/crashreportdlg.hxx b/svx/source/dialog/crashreportdlg.hxx
new file mode 100644
index 000000000..868f86c58
--- /dev/null
+++ b/svx/source/dialog/crashreportdlg.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_SVX_SOURCE_DIALOG_CRASHREPORTDLG_HXX
+#define INCLUDED_SVX_SOURCE_DIALOG_CRASHREPORTDLG_HXX
+
+#include <vcl/weld.hxx>
+
+class CrashReportDialog : public weld::GenericDialogController
+{
+public:
+ explicit CrashReportDialog(weld::Window* pParent);
+ virtual short run() override;
+ virtual ~CrashReportDialog() override;
+
+private:
+ std::unique_ptr<weld::Button> mxBtnSend;
+ std::unique_ptr<weld::Button> mxBtnCancel;
+ std::unique_ptr<weld::Button> mxBtnClose;
+ std::unique_ptr<weld::Label> mxEditPreUpload;
+ std::unique_ptr<weld::TextView> mxEditPostUpload;
+ std::unique_ptr<weld::TextView> mxFtBugReport;
+ std::unique_ptr<weld::CheckButton> mxCBSafeMode;
+ std::unique_ptr<weld::LinkButton> mxPrivacyPolicyButton;
+
+ OUString maSuccessMsg;
+
+ DECL_LINK(BtnHdl, weld::Button&, void);
+};
+
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/dialog/crashreportui.cxx b/svx/source/dialog/crashreportui.cxx
new file mode 100644
index 000000000..5384e3c18
--- /dev/null
+++ b/svx/source/dialog/crashreportui.cxx
@@ -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/.
+ */
+
+#include <cppuhelper/implbase.hxx>
+#include <com/sun/star/frame/XSynchronousDispatch.hpp>
+#include <com/sun/star/lang/XServiceInfo.hpp>
+
+#include <cppuhelper/supportsservice.hxx>
+
+#include <vcl/svapp.hxx>
+
+#include "crashreportdlg.hxx"
+
+namespace {
+
+class CrashReportUI : public ::cppu::WeakImplHelper< css::lang::XServiceInfo ,
+ css::frame::XSynchronousDispatch > // => XDispatch!
+{
+public:
+ explicit CrashReportUI();
+
+ // css.lang.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;
+
+
+ virtual css::uno::Any SAL_CALL dispatchWithReturnValue(const css::util::URL& aURL,
+ const css::uno::Sequence< css::beans::PropertyValue >& lArguments ) override;
+};
+
+CrashReportUI::CrashReportUI()
+{
+
+}
+
+OUString SAL_CALL CrashReportUI::getImplementationName()
+{
+ return "com.sun.star.comp.svx.CrashReportUI";
+}
+
+sal_Bool SAL_CALL CrashReportUI::supportsService(const OUString& sServiceName)
+{
+ return cppu::supportsService(this, sServiceName);
+}
+
+css::uno::Sequence< OUString > SAL_CALL CrashReportUI::getSupportedServiceNames()
+{
+ return { "com.sun.star.dialog.CrashReportUI" };
+}
+
+css::uno::Any SAL_CALL CrashReportUI::dispatchWithReturnValue(const css::util::URL&,
+ const css::uno::Sequence< css::beans::PropertyValue >& )
+{
+ SolarMutexGuard aGuard;
+ css::uno::Any aRet;
+ CrashReportDialog aDialog(nullptr);
+ aDialog.run();
+ return aRet;
+}
+
+}
+
+extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface *
+com_sun_star_comp_svx_CrashReportUI_get_implementation(
+ css::uno::XComponentContext * /*context*/,
+ css::uno::Sequence<css::uno::Any> const &)
+{
+ return cppu::acquire(new CrashReportUI());
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/dialog/ctredlin.cxx b/svx/source/dialog/ctredlin.cxx
new file mode 100644
index 000000000..3a0f1e32e
--- /dev/null
+++ b/svx/source/dialog/ctredlin.cxx
@@ -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 .
+ */
+
+#include <comphelper/processfactory.hxx>
+#include <comphelper/string.hxx>
+#include <i18nlangtag/languagetag.hxx>
+#include <o3tl/safeint.hxx>
+#include <vcl/svapp.hxx>
+#include <vcl/settings.hxx>
+#include <vcl/weldutils.hxx>
+#include <svtools/ctrlbox.hxx>
+#include <unotools/textsearch.hxx>
+
+#include <helpids.h>
+
+#include <svx/ctredlin.hxx>
+
+#define WRITER_DATE 2
+#define CALC_DATE 3
+
+RedlinData::RedlinData()
+ : aDateTime(DateTime::EMPTY)
+ , pData(nullptr)
+ , eType(RedlineType::Any)
+ , bDisabled(false)
+{
+}
+
+RedlinData::~RedlinData()
+{
+}
+
+SvxRedlinTable::SvxRedlinTable(std::unique_ptr<weld::TreeView> xWriterControl,
+ std::unique_ptr<weld::TreeView> xCalcControl)
+ : xSorter(new comphelper::string::NaturalStringSorter(::comphelper::getProcessComponentContext(),
+ Application::GetSettings().GetUILanguageTag().getLocale()))
+ , xWriterTreeView(std::move(xWriterControl))
+ , xCalcTreeView(std::move(xCalcControl))
+ , pTreeView(nullptr)
+ , nDatePos(WRITER_DATE)
+ , bAuthor(false)
+ , bDate(false)
+ , bComment(false)
+ , bSorted(false)
+ , nDaTiMode(SvxRedlinDateMode::BEFORE)
+ , aDaTiFirst( DateTime::EMPTY )
+ , aDaTiLast( DateTime::EMPTY )
+ , aDaTiFilterFirst( DateTime::EMPTY )
+ , aDaTiFilterLast( DateTime::EMPTY )
+{
+ if (xWriterTreeView)
+ {
+ xWriterTreeView->connect_column_clicked(LINK(this, SvxRedlinTable, HeaderBarClick));
+ xWriterTreeView->set_sort_func([this](const weld::TreeIter& rLeft, const weld::TreeIter& rRight){
+ return ColCompare(rLeft, rRight);
+ });
+ pTreeView = xWriterTreeView.get();
+ }
+ if (xCalcTreeView)
+ {
+ xCalcTreeView->connect_column_clicked(LINK(this, SvxRedlinTable, HeaderBarClick));
+ xCalcTreeView->set_sort_func([this](const weld::TreeIter& rLeft, const weld::TreeIter& rRight){
+ return ColCompare(rLeft, rRight);
+ });
+ pTreeView = xCalcTreeView.get();
+ }
+}
+
+SvxRedlinTable::~SvxRedlinTable()
+{
+}
+
+IMPL_LINK(SvxRedlinTable, HeaderBarClick, int, nColumn, void)
+{
+ if (!bSorted)
+ {
+ pTreeView->make_sorted();
+ bSorted = true;
+ }
+
+ bool bSortAtoZ = pTreeView->get_sort_order();
+
+ //set new arrow positions in headerbar
+ if (nColumn == pTreeView->get_sort_column())
+ {
+ bSortAtoZ = !bSortAtoZ;
+ pTreeView->set_sort_order(bSortAtoZ);
+ }
+ else
+ {
+ int nOldSortColumn = pTreeView->get_sort_column();
+ if (nOldSortColumn != -1)
+ pTreeView->set_sort_indicator(TRISTATE_INDET, nOldSortColumn);
+ pTreeView->set_sort_column(nColumn);
+ }
+
+ if (nColumn != -1)
+ {
+ //sort lists
+ pTreeView->set_sort_indicator(bSortAtoZ ? TRISTATE_TRUE : TRISTATE_FALSE, nColumn);
+ }
+}
+
+int SvxRedlinTable::ColCompare(const weld::TreeIter& rLeft, const weld::TreeIter& rRight)
+{
+ sal_Int32 nCompare = 0;
+
+ int nSortCol = pTreeView->get_sort_column();
+
+ if (pTreeView == xWriterTreeView.get() && nSortCol == 0)
+ {
+ RedlinData *pLeftData = weld::fromId<RedlinData*>(pTreeView->get_id(rLeft));
+ RedlinData *pRightData = weld::fromId<RedlinData*>(pTreeView->get_id(rRight));
+
+ if (pLeftData && pRightData)
+ {
+ if (pLeftData->eType < pRightData->eType)
+ nCompare = -1;
+ else if (pLeftData->eType > pRightData->eType)
+ nCompare = 1;
+ return nCompare;
+ }
+ }
+
+ if (nSortCol == nDatePos)
+ {
+ RedlinData *pLeftData = weld::fromId<RedlinData*>(pTreeView->get_id(rLeft));
+ RedlinData *pRightData = weld::fromId<RedlinData*>(pTreeView->get_id(rRight));
+
+ if (pLeftData && pRightData)
+ {
+ if (pLeftData->aDateTime < pRightData->aDateTime)
+ nCompare = -1;
+ else if (pLeftData->aDateTime > pRightData->aDateTime)
+ nCompare = 1;
+ return nCompare;
+ }
+ }
+
+ return xSorter->compare(pTreeView->get_text(rLeft, nSortCol),
+ pTreeView->get_text(rRight, nSortCol));
+}
+
+void SvxRedlinTable::UpdateFilterTest()
+{
+ Date aDateMax( Date::SYSTEM );
+ aDateMax.AddYears(100);
+ Date aDateMin(1,1,1989);
+ tools::Time aTMin(0);
+ tools::Time aTMax(23,59,59);
+
+ DateTime aDTMin(aDateMin);
+ DateTime aDTMax(aDateMax);
+
+ switch(nDaTiMode)
+ {
+ case SvxRedlinDateMode::BEFORE:
+ aDaTiFilterFirst=aDTMin;
+ aDaTiFilterLast=aDaTiFirst;
+ break;
+ case SvxRedlinDateMode::SAVE:
+ case SvxRedlinDateMode::SINCE:
+ aDaTiFilterFirst=aDaTiFirst;
+ aDaTiFilterLast=aDTMax;
+ break;
+ case SvxRedlinDateMode::EQUAL:
+ aDaTiFilterFirst=aDaTiFirst;
+ aDaTiFilterLast=aDaTiFirst;
+ aDaTiFilterFirst.SetTime(aTMin.GetTime());
+ aDaTiFilterLast.SetTime(aTMax.GetTime());
+ break;
+ case SvxRedlinDateMode::NOTEQUAL:
+ aDaTiFilterFirst=aDaTiFirst;
+ aDaTiFilterLast=aDaTiFirst;
+ aDaTiFilterFirst.SetTime(aTMin.GetTime());
+ aDaTiFilterLast.SetTime(aTMax.GetTime());
+ break;
+ case SvxRedlinDateMode::BETWEEN:
+ aDaTiFilterFirst=aDaTiFirst;
+ aDaTiFilterLast=aDaTiLast;
+ break;
+ case SvxRedlinDateMode::NONE:
+ break;
+ }
+}
+
+void SvxRedlinTable::SetFilterDate(bool bFlag)
+{
+ bDate=bFlag;
+}
+
+void SvxRedlinTable::SetDateTimeMode(SvxRedlinDateMode nMode)
+{
+ nDaTiMode=nMode;
+}
+
+void SvxRedlinTable::SetFirstDate(const Date& aDate)
+{
+ aDaTiFirst.SetDate(aDate.GetDate());
+}
+
+void SvxRedlinTable::SetLastDate(const Date& aDate)
+{
+ aDaTiLast.SetDate(aDate.GetDate());
+}
+
+void SvxRedlinTable::SetFirstTime(const tools::Time& aTime)
+{
+ aDaTiFirst.SetTime(aTime.GetTime());
+}
+
+void SvxRedlinTable::SetLastTime(const tools::Time& aTime)
+{
+ aDaTiLast.SetTime(aTime.GetTime());
+}
+
+void SvxRedlinTable::SetFilterAuthor(bool bFlag)
+{
+ bAuthor=bFlag;
+}
+
+void SvxRedlinTable::SetAuthor(const OUString &aString)
+{
+ aAuthor=aString;
+}
+
+void SvxRedlinTable::SetFilterComment(bool bFlag)
+{
+ bComment=bFlag;
+}
+
+void SvxRedlinTable::SetCommentParams( const utl::SearchParam* pSearchPara )
+{
+ if(pSearchPara!=nullptr)
+ {
+ pCommentSearcher.reset(new utl::TextSearch(*pSearchPara, LANGUAGE_SYSTEM ));
+ }
+}
+
+bool SvxRedlinTable::IsValidEntry(std::u16string_view rAuthorStr,
+ const DateTime &rDateTime,
+ const OUString &rCommentStr)
+{
+ return IsValidEntry(rAuthorStr, rDateTime) && IsValidComment(rCommentStr);
+}
+
+bool SvxRedlinTable::IsValidEntry(std::u16string_view rAuthorStr, const DateTime &rDateTime)
+{
+ if (bAuthor && aAuthor!=rAuthorStr)
+ return false;
+
+ if (!bDate)
+ return true;
+
+ const bool bRes = rDateTime.IsBetween(aDaTiFilterFirst, aDaTiFilterLast);
+ return nDaTiMode!=SvxRedlinDateMode::NOTEQUAL ? bRes : !bRes;
+}
+
+bool SvxRedlinTable::IsValidComment(const OUString &rCommentStr)
+{
+ if (!bComment)
+ return true;
+
+ sal_Int32 nStartPos = 0;
+ sal_Int32 nEndPos = rCommentStr.getLength();
+ return pCommentSearcher->SearchForward( rCommentStr, &nStartPos, &nEndPos);
+}
+
+SvxTPage::~SvxTPage()
+{
+}
+
+void SvxTPage::ActivatePage()
+{
+}
+
+SvxTPView::SvxTPView(weld::Container* pParent)
+ : SvxTPage(pParent, "svx/ui/redlineviewpage.ui", "RedlineViewPage")
+ , bEnableAccept(true)
+ , bEnableAcceptAll(true)
+ , bEnableReject(true)
+ , bEnableRejectAll(true)
+ , bEnableUndo(true)
+ , bEnableClearFormat(false)
+ , bEnableClearFormatAll(false)
+ , m_xAccept(m_xBuilder->weld_button("accept"))
+ , m_xReject(m_xBuilder->weld_button("reject"))
+ , m_xAcceptAll(m_xBuilder->weld_button("acceptall"))
+ , m_xRejectAll(m_xBuilder->weld_button("rejectall"))
+ , m_xUndo(m_xBuilder->weld_button("undo"))
+ , m_xViewData(new SvxRedlinTable(m_xBuilder->weld_tree_view("writerchanges"),
+ m_xBuilder->weld_tree_view("calcchanges")))
+{
+ Link<weld::Button&,void> aLink=LINK( this, SvxTPView, PbClickHdl);
+
+ m_xAccept->connect_clicked(aLink);
+ m_xAcceptAll->connect_clicked(aLink);
+ m_xReject->connect_clicked(aLink);
+ m_xRejectAll->connect_clicked(aLink);
+ m_xUndo->connect_clicked(aLink);
+}
+
+void SvxTPView::ActivatePage()
+{
+ m_xAccept->set_sensitive(bEnableAccept);
+ m_xReject->set_sensitive(bEnableReject);
+ m_xAcceptAll->set_sensitive(bEnableAcceptAll);
+ m_xRejectAll->set_sensitive(bEnableRejectAll);
+ m_xUndo->set_sensitive(bEnableUndo);
+}
+
+void SvxTPView::DeactivatePage()
+{
+ m_xAccept->set_sensitive(false);
+ m_xReject->set_sensitive(false);
+ m_xAcceptAll->set_sensitive(false);
+ m_xRejectAll->set_sensitive(false);
+ m_xUndo->set_sensitive(false);
+}
+
+SvxTPView::~SvxTPView()
+{
+}
+
+void SvxRedlinTable::SetWriterView()
+{
+ nDatePos = WRITER_DATE;
+ if (xCalcTreeView)
+ xCalcTreeView->hide();
+ xWriterTreeView->show();
+ pTreeView = xWriterTreeView.get();
+
+ auto nDigitWidth = pTreeView->get_approximate_digit_width();
+ std::vector<int> aWidths
+ {
+ o3tl::narrowing<int>(nDigitWidth * 10),
+ o3tl::narrowing<int>(nDigitWidth * 20),
+ o3tl::narrowing<int>(nDigitWidth * 20)
+ };
+ pTreeView->set_column_fixed_widths(aWidths);
+}
+
+void SvxRedlinTable::SetCalcView()
+{
+ nDatePos = CALC_DATE;
+ if (xWriterTreeView)
+ xWriterTreeView->hide();
+ xCalcTreeView->show();
+ pTreeView = xCalcTreeView.get();
+
+ auto nDigitWidth = pTreeView->get_approximate_digit_width();
+ std::vector<int> aWidths
+ {
+ o3tl::narrowing<int>(nDigitWidth * 20),
+ o3tl::narrowing<int>(nDigitWidth * 20),
+ o3tl::narrowing<int>(nDigitWidth * 20),
+ o3tl::narrowing<int>(nDigitWidth * 20)
+ };
+ pTreeView->set_column_fixed_widths(aWidths);
+}
+
+void SvxTPView::EnableAccept(bool bFlag)
+{
+ bEnableAccept = bFlag;
+ m_xAccept->set_sensitive(bFlag);
+}
+
+void SvxTPView::EnableAcceptAll(bool bFlag)
+{
+ bEnableAcceptAll = bFlag;
+ m_xAcceptAll->set_sensitive(bFlag);
+}
+
+void SvxTPView::EnableReject(bool bFlag)
+{
+ bEnableReject = bFlag;
+ m_xReject->set_sensitive(bFlag);
+}
+
+void SvxTPView::EnableRejectAll(bool bFlag)
+{
+ bEnableRejectAll = bFlag;
+ m_xRejectAll->set_sensitive(bFlag);
+}
+
+void SvxTPView::EnableClearFormat(bool bFlag)
+{
+ if (bEnableClearFormat == bFlag)
+ return;
+ bEnableClearFormat = bFlag;
+}
+
+void SvxTPView::EnableClearFormatAll(bool bFlag)
+{
+ if (bEnableClearFormatAll == bFlag)
+ return;
+ bEnableClearFormatAll = bFlag;
+}
+
+void SvxTPView::ShowUndo()
+{
+ m_xUndo->show();
+}
+
+void SvxTPView::EnableUndo(bool bFlag)
+{
+ bEnableUndo = bFlag;
+ m_xUndo->set_sensitive(bFlag);
+}
+
+IMPL_LINK( SvxTPView, PbClickHdl, weld::Button&, rPushB, void)
+{
+ if (&rPushB == m_xAccept.get())
+ {
+ AcceptClickLk.Call(this);
+ }
+ else if (&rPushB == m_xAcceptAll.get())
+ {
+ AcceptAllClickLk.Call(this);
+ }
+ else if (&rPushB == m_xReject.get())
+ {
+ RejectClickLk.Call(this);
+ }
+ else if (&rPushB == m_xRejectAll.get())
+ {
+ RejectAllClickLk.Call(this);
+ }
+ else if (&rPushB == m_xUndo.get())
+ {
+ UndoClickLk.Call(this);
+ }
+}
+
+SvxTPage::SvxTPage(weld::Container* pParent, const OUString& rUIXMLDescription, const OString& rID)
+ : m_xBuilder(Application::CreateBuilder(pParent, rUIXMLDescription))
+ , m_xContainer(m_xBuilder->weld_container(rID))
+{
+}
+
+SvxTPFilter::SvxTPFilter(weld::Container* pParent)
+ : SvxTPage(pParent, "svx/ui/redlinefilterpage.ui", "RedlineFilterPage")
+ , bModified(false)
+ , m_pRedlinTable(nullptr)
+ , m_xCbDate(m_xBuilder->weld_check_button("date"))
+ , m_xLbDate(m_xBuilder->weld_combo_box("datecond"))
+ , m_xDfDate(new SvtCalendarBox(m_xBuilder->weld_menu_button("startdate")))
+ , m_xTfDate(m_xBuilder->weld_formatted_spin_button("starttime"))
+ , m_xTfDateFormatter(new weld::TimeFormatter(*m_xTfDate))
+ , m_xIbClock(m_xBuilder->weld_button("startclock"))
+ , m_xFtDate2(m_xBuilder->weld_label("and"))
+ , m_xDfDate2(new SvtCalendarBox(m_xBuilder->weld_menu_button("enddate")))
+ , m_xTfDate2(m_xBuilder->weld_formatted_spin_button("endtime"))
+ , m_xTfDate2Formatter(new weld::TimeFormatter(*m_xTfDate2))
+ , m_xIbClock2(m_xBuilder->weld_button("endclock"))
+ , m_xCbAuthor(m_xBuilder->weld_check_button("author"))
+ , m_xLbAuthor(m_xBuilder->weld_combo_box("authorlist"))
+ , m_xCbRange(m_xBuilder->weld_check_button("range"))
+ , m_xEdRange(m_xBuilder->weld_entry("rangeedit"))
+ , m_xBtnRange(m_xBuilder->weld_button("dotdotdot"))
+ , m_xCbAction(m_xBuilder->weld_check_button("action"))
+ , m_xLbAction(m_xBuilder->weld_combo_box("actionlist"))
+ , m_xCbComment(m_xBuilder->weld_check_button("comment"))
+ , m_xEdComment(m_xBuilder->weld_entry("commentedit"))
+{
+ m_xTfDateFormatter->EnableEmptyField(false);
+ m_xTfDate2Formatter->EnableEmptyField(false);
+
+ m_xLbDate->set_active(0);
+ m_xLbDate->connect_changed( LINK( this, SvxTPFilter, SelDateHdl ) );
+ m_xIbClock->connect_clicked( LINK( this, SvxTPFilter, TimeHdl) );
+ m_xIbClock2->connect_clicked( LINK( this, SvxTPFilter,TimeHdl) );
+ m_xBtnRange->connect_clicked( LINK( this, SvxTPFilter, RefHandle));
+
+ Link<weld::Toggleable&,void> aLink=LINK( this, SvxTPFilter, RowEnableHdl) ;
+ m_xCbDate->connect_toggled(aLink);
+ m_xCbAuthor->connect_toggled(aLink);
+ m_xCbRange->connect_toggled(aLink);
+ m_xCbAction->connect_toggled(aLink);
+ m_xCbComment->connect_toggled(aLink);
+
+ Link<SvtCalendarBox&,void> a2Link=LINK(this, SvxTPFilter, ModifyDate);
+ m_xDfDate->connect_activated(a2Link);
+ m_xDfDate2->connect_activated(a2Link);
+
+ Link<weld::FormattedSpinButton&,void> a3Link=LINK(this, SvxTPFilter, ModifyTime);
+ m_xTfDate->connect_value_changed(a3Link);
+ m_xTfDate2->connect_value_changed(a3Link);
+
+ Link<weld::Entry&,void> a4Link=LINK( this, SvxTPFilter, ModifyHdl);
+ m_xEdRange->connect_changed(a4Link);
+ m_xEdComment->connect_changed(a4Link);
+ m_xLbAction->connect_changed(LINK( this, SvxTPFilter, ModifyListBoxHdl));
+ m_xLbAuthor->connect_changed(LINK( this, SvxTPFilter, ModifyListBoxHdl));
+
+ RowEnableHdl(*m_xCbDate);
+ RowEnableHdl(*m_xCbAuthor);
+ RowEnableHdl(*m_xCbRange);
+ RowEnableHdl(*m_xCbAction);
+ RowEnableHdl(*m_xCbComment);
+
+ DateTime aDateTime(DateTime::SYSTEM);
+ SetFirstDate(aDateTime);
+ SetLastDate(aDateTime);
+ SetFirstTime(aDateTime);
+ SetLastTime(aDateTime);
+ HideRange();
+ ShowAction();
+ bModified=false;
+}
+
+SvxTPFilter::~SvxTPFilter()
+{
+}
+
+void SvxTPFilter::SetRedlinTable(SvxRedlinTable* pTable)
+{
+ m_pRedlinTable = pTable;
+}
+
+void SvxTPFilter::EnableDateLine1(bool bFlag)
+{
+ if(bFlag && m_xCbDate->get_active())
+ {
+ m_xDfDate->set_sensitive(true);
+ m_xTfDate->set_sensitive(true);
+ m_xIbClock->set_sensitive(true);
+ }
+ else
+ {
+ m_xDfDate->set_sensitive(false);
+ m_xTfDate->set_sensitive(false);
+ m_xIbClock->set_sensitive(false);
+ }
+}
+void SvxTPFilter::EnableDateLine2(bool bFlag)
+{
+ if(bFlag && m_xCbDate->get_active())
+ {
+ m_xFtDate2->set_sensitive(true);
+ m_xDfDate2->set_sensitive(true);
+ m_xTfDate2->set_sensitive(true);
+ m_xIbClock2->set_sensitive(true);
+ }
+ else
+ {
+ m_xFtDate2->set_sensitive(false);
+ m_xDfDate2->set_sensitive(false);
+ m_xDfDate2->set_label(OUString());
+ m_xTfDate2->set_sensitive(false);
+ m_xTfDate2->set_text(OUString());
+ m_xIbClock2->set_sensitive(false);
+ }
+}
+
+Date SvxTPFilter::GetFirstDate() const
+{
+ return m_xDfDate->get_date();
+}
+
+void SvxTPFilter::SetFirstDate(const Date &aDate)
+{
+ m_xDfDate->set_date(aDate);
+}
+
+tools::Time SvxTPFilter::GetFirstTime() const
+{
+ return m_xTfDateFormatter->GetTime();
+}
+
+void SvxTPFilter::SetFirstTime(const tools::Time &aTime)
+{
+ m_xTfDateFormatter->SetTime(aTime);
+}
+
+Date SvxTPFilter::GetLastDate() const
+{
+ return m_xDfDate2->get_date();
+}
+
+void SvxTPFilter::SetLastDate(const Date &aDate)
+{
+ m_xDfDate2->set_date(aDate);
+}
+
+tools::Time SvxTPFilter::GetLastTime() const
+{
+ return m_xTfDate2Formatter->GetTime();
+}
+
+void SvxTPFilter::SetLastTime(const tools::Time &aTime)
+{
+ m_xTfDate2Formatter->SetTime(aTime);
+}
+
+void SvxTPFilter::SetDateMode(sal_uInt16 nMode)
+{
+ m_xLbDate->set_active(nMode);
+ SelDateHdl(*m_xLbDate);
+}
+
+SvxRedlinDateMode SvxTPFilter::GetDateMode() const
+{
+ return static_cast<SvxRedlinDateMode>(m_xLbDate->get_active());
+}
+void SvxTPFilter::ClearAuthors()
+{
+ m_xLbAuthor->clear();
+}
+
+void SvxTPFilter::InsertAuthor( const OUString& rString)
+{
+ m_xLbAuthor->append_text(rString);
+}
+
+OUString SvxTPFilter::GetSelectedAuthor() const
+{
+ return m_xLbAuthor->get_active_text();
+}
+
+void SvxTPFilter::SelectedAuthorPos(sal_Int32 nPos)
+{
+ m_xLbAuthor->set_active(nPos);
+}
+
+sal_Int32 SvxTPFilter::SelectAuthor(const OUString& aString)
+{
+ m_xLbAuthor->set_active_text(aString);
+ return m_xLbAuthor->get_active();
+}
+
+void SvxTPFilter::SetRange(const OUString& rString)
+{
+ m_xEdRange->set_text(rString);
+}
+
+OUString SvxTPFilter::GetRange() const
+{
+ return m_xEdRange->get_text();
+}
+
+void SvxTPFilter::SetFocusToRange()
+{
+ m_xEdRange->grab_focus();
+}
+
+void SvxTPFilter::HideRange(bool bHide)
+{
+ if (bHide)
+ {
+ m_xCbRange->hide();
+ m_xEdRange->hide();
+ m_xBtnRange->hide();
+ }
+ else
+ {
+ ShowAction(false);
+ m_xCbRange->show();
+ m_xEdRange->show();
+ m_xBtnRange->show();
+ }
+}
+
+void SvxTPFilter::SetComment(const OUString &rComment)
+{
+ m_xEdComment->set_text(rComment);
+}
+
+OUString SvxTPFilter::GetComment()const
+{
+ return m_xEdComment->get_text();
+}
+
+bool SvxTPFilter::IsDate() const
+{
+ return m_xCbDate->get_active();
+}
+
+bool SvxTPFilter::IsAuthor() const
+{
+ return m_xCbAuthor->get_active();
+}
+
+bool SvxTPFilter::IsRange() const
+{
+ return m_xCbRange->get_active();
+}
+
+bool SvxTPFilter::IsAction() const
+{
+ return m_xCbAction->get_active();
+}
+
+bool SvxTPFilter::IsComment() const
+{
+ return m_xCbComment->get_active();
+}
+
+void SvxTPFilter::CheckDate(bool bFlag)
+{
+ m_xCbDate->set_active(bFlag);
+ RowEnableHdl(*m_xCbDate);
+ bModified=false;
+}
+
+void SvxTPFilter::CheckAuthor(bool bFlag)
+{
+ m_xCbAuthor->set_active(bFlag);
+ RowEnableHdl(*m_xCbAuthor);
+ bModified=false;
+}
+
+void SvxTPFilter::CheckRange(bool bFlag)
+{
+ m_xCbRange->set_active(bFlag);
+ RowEnableHdl(*m_xCbRange);
+ bModified=false;
+}
+
+void SvxTPFilter::CheckAction(bool bFlag)
+{
+ m_xCbAction->set_active(bFlag);
+ RowEnableHdl(*m_xCbAction);
+ bModified=false;
+}
+
+void SvxTPFilter::CheckComment(bool bFlag)
+{
+ m_xCbComment->set_active(bFlag);
+ RowEnableHdl(*m_xCbComment);
+ bModified=false;
+}
+
+void SvxTPFilter::ShowAction(bool bShow)
+{
+ if(!bShow)
+ {
+ m_xCbAction->hide();
+ m_xLbAction->hide();
+ }
+ else
+ {
+ HideRange();
+ m_xCbAction->show();
+ m_xLbAction->show();
+ }
+}
+
+IMPL_LINK_NOARG(SvxTPFilter, SelDateHdl, weld::ComboBox&, void)
+{
+ SvxRedlinDateMode nKind = static_cast<SvxRedlinDateMode>(m_xLbDate->get_active());
+ switch(nKind)
+ {
+ case SvxRedlinDateMode::BEFORE:
+ EnableDateLine1(true);
+ EnableDateLine2(false);
+ break;
+ case SvxRedlinDateMode::SINCE:
+ EnableDateLine1(true);
+ EnableDateLine2(false);
+ break;
+ case SvxRedlinDateMode::EQUAL:
+ EnableDateLine1(true);
+ m_xTfDate->set_sensitive(false);
+ m_xTfDate->set_text(OUString());
+ EnableDateLine2(false);
+ break;
+ case SvxRedlinDateMode::NOTEQUAL:
+ EnableDateLine1(true);
+ m_xTfDate->set_sensitive(false);
+ m_xTfDate->set_text(OUString());
+ EnableDateLine2(false);
+ break;
+ case SvxRedlinDateMode::BETWEEN:
+ EnableDateLine1(true);
+ EnableDateLine2(true);
+ break;
+ case SvxRedlinDateMode::SAVE:
+ EnableDateLine1(false);
+ EnableDateLine2(false);
+ break;
+ case SvxRedlinDateMode::NONE:
+ break;
+ }
+ bModified = true;
+}
+
+IMPL_LINK(SvxTPFilter, RowEnableHdl, weld::Toggleable&, rCB, void)
+{
+ if (&rCB == m_xCbDate.get())
+ {
+ m_xLbDate->set_sensitive(m_xCbDate->get_active());
+ EnableDateLine1(false);
+ EnableDateLine2(false);
+ if(m_xCbDate->get_active()) SelDateHdl(*m_xLbDate);
+ }
+ else if (&rCB == m_xCbAuthor.get())
+ {
+ m_xLbAuthor->set_sensitive(m_xCbAuthor->get_active());
+ }
+ else if (&rCB == m_xCbRange.get())
+ {
+ m_xEdRange->set_sensitive(m_xCbRange->get_active());
+ m_xBtnRange->set_sensitive(m_xCbRange->get_active());
+ }
+ else if (&rCB == m_xCbAction.get())
+ {
+ m_xLbAction->set_sensitive(m_xCbAction->get_active());
+ }
+ else if (&rCB == m_xCbComment.get())
+ {
+ m_xEdComment->set_sensitive(m_xCbComment->get_active());
+ }
+ bModified = true;
+}
+
+IMPL_LINK(SvxTPFilter, TimeHdl, weld::Button&, rIB, void)
+{
+ DateTime aDateTime( DateTime::SYSTEM );
+ if (&rIB == m_xIbClock.get())
+ {
+ SetFirstDate(aDateTime);
+ SetFirstTime(aDateTime);
+ }
+ else if (&rIB == m_xIbClock2.get())
+ {
+ SetLastDate(aDateTime);
+ SetLastTime(aDateTime);
+ }
+ bModified=true;
+}
+
+IMPL_LINK_NOARG(SvxTPFilter, ModifyHdl, weld::Entry&, void)
+{
+ bModified=true;
+}
+
+IMPL_LINK_NOARG(SvxTPFilter, ModifyListBoxHdl, weld::ComboBox&, void)
+{
+ bModified=true;
+}
+
+void SvxTPFilter::DeactivatePage()
+{
+ if(bModified)
+ {
+ if (m_pRedlinTable)
+ {
+ m_pRedlinTable->SetFilterDate(IsDate());
+ m_pRedlinTable->SetDateTimeMode(GetDateMode());
+ m_pRedlinTable->SetFirstDate(GetFirstDate());
+ m_pRedlinTable->SetLastDate(GetLastDate());
+ m_pRedlinTable->SetFirstTime(GetFirstTime());
+ m_pRedlinTable->SetLastTime(GetLastTime());
+ m_pRedlinTable->SetFilterAuthor(IsAuthor());
+ m_pRedlinTable->SetAuthor(GetSelectedAuthor());
+
+ m_pRedlinTable->SetFilterComment(IsComment());
+
+ utl::SearchParam aSearchParam( m_xEdComment->get_text(),
+ utl::SearchParam::SearchType::Regexp,false );
+
+ m_pRedlinTable->SetCommentParams(&aSearchParam);
+
+ m_pRedlinTable->UpdateFilterTest();
+ }
+
+ aReadyLink.Call(this);
+ }
+ bModified=false;
+}
+
+void SvxTPFilter::Enable(bool bEnable)
+{
+ m_xContainer->set_sensitive(bEnable);
+ if (m_xCbDate->get_sensitive())
+ {
+ RowEnableHdl(*m_xCbDate);
+ RowEnableHdl(*m_xCbAuthor);
+ RowEnableHdl(*m_xCbRange);
+ RowEnableHdl(*m_xCbComment);
+ }
+}
+
+IMPL_LINK(SvxTPFilter, ModifyDate, SvtCalendarBox&, rTF, void)
+{
+ Date aDate( Date::SYSTEM );
+ if (m_xDfDate.get() == &rTF)
+ {
+ if (m_xDfDate->get_label().isEmpty())
+ m_xDfDate->set_date(aDate);
+
+ if(m_pRedlinTable!=nullptr)
+ m_pRedlinTable->SetFirstDate(m_xDfDate->get_date());
+ }
+ else if (m_xDfDate2.get() == &rTF)
+ {
+ if (m_xDfDate2->get_label().isEmpty())
+ m_xDfDate2->set_date(aDate);
+
+ if (m_pRedlinTable)
+ m_pRedlinTable->SetLastDate(m_xDfDate2->get_date());
+ }
+ bModified=true;
+}
+
+IMPL_LINK(SvxTPFilter, ModifyTime, weld::FormattedSpinButton&, rTF, void)
+{
+ tools::Time aTime(0);
+ if (m_xTfDate.get() == &rTF)
+ {
+ if (m_xTfDate->get_text().isEmpty())
+ SetFirstTime(aTime);
+
+ if (m_pRedlinTable!=nullptr)
+ m_pRedlinTable->SetFirstTime(GetFirstTime());
+ }
+ else if (m_xTfDate2.get() == &rTF)
+ {
+ if (m_xTfDate2->get_text().isEmpty())
+ SetLastTime(aTime);
+
+ if (m_pRedlinTable!=nullptr)
+ m_pRedlinTable->SetLastTime(GetLastTime());
+
+ }
+ bModified=true;
+}
+
+IMPL_LINK_NOARG(SvxTPFilter, RefHandle, weld::Button&, void)
+{
+ aRefLink.Call(this);
+}
+
+SvxAcceptChgCtr::SvxAcceptChgCtr(weld::Container* pParent)
+ : m_xBuilder(Application::CreateBuilder(pParent, "svx/ui/redlinecontrol.ui"))
+ , m_xTabCtrl(m_xBuilder->weld_notebook("tabcontrol"))
+{
+ m_xTabCtrl->connect_enter_page(LINK(this, SvxAcceptChgCtr, ActivatePageHdl));
+ m_xTabCtrl->connect_leave_page(LINK(this, SvxAcceptChgCtr, DeactivatePageHdl));
+
+ m_xTPFilter.reset(new SvxTPFilter(m_xTabCtrl->get_page("filter")));
+ m_xTPView.reset(new SvxTPView(m_xTabCtrl->get_page("view")));
+ m_xTPFilter->SetRedlinTable(m_xTPView->GetTableControl());
+ m_xTabCtrl->set_current_page("view");
+ m_xTabCtrl->set_help_id(HID_REDLINE_CTRL_VIEW);
+ m_xTabCtrl->show();
+}
+
+SvxAcceptChgCtr::~SvxAcceptChgCtr()
+{
+ m_xTPFilter.reset();
+ m_xTPView.reset();
+}
+
+void SvxAcceptChgCtr::ShowFilterPage()
+{
+ m_xTabCtrl->set_current_page("filter");
+}
+
+IMPL_LINK(SvxAcceptChgCtr, ActivatePageHdl, const OString&, rPage, void)
+{
+ if (rPage == "filter")
+ {
+ m_xTPFilter->ActivatePage();
+ m_xTabCtrl->set_help_id(HID_REDLINE_CTRL_FILTER);
+ }
+ else if (rPage == "view")
+ {
+ m_xTPView->ActivatePage();
+ m_xTabCtrl->set_help_id(HID_REDLINE_CTRL_VIEW);
+ }
+}
+
+IMPL_LINK(SvxAcceptChgCtr, DeactivatePageHdl, const OString&, rPage, bool)
+{
+ if (rPage == "filter")
+ m_xTPFilter->DeactivatePage();
+ else if (rPage == "view")
+ m_xTPView->DeactivatePage();
+ return true;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/dialog/databaseregistrationui.cxx b/svx/source/dialog/databaseregistrationui.cxx
new file mode 100644
index 000000000..aa62b5b38
--- /dev/null
+++ b/svx/source/dialog/databaseregistrationui.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/databaseregistrationui.hxx>
+
+#include <svx/svxdlg.hxx>
+#include <svx/dialogs.hrc>
+
+#include <sfx2/app.hxx>
+#include <svl/itemset.hxx>
+
+namespace svx
+{
+ sal_uInt16 administrateDatabaseRegistration(weld::Window* parentWindow)
+ {
+ sal_uInt16 nResult = RET_CANCEL;
+
+ SfxItemSetFixed<SID_SB_DB_REGISTER, SID_SB_DB_REGISTER> aRegistrationItems( SfxGetpApp()->GetPool() );
+
+ SvxAbstractDialogFactory* pDialogFactory = SvxAbstractDialogFactory::Create();
+ ScopedVclPtr<SfxAbstractDialog> pDialog(pDialogFactory->CreateSfxDialog(parentWindow, aRegistrationItems, nullptr, RID_SFXPAGE_DBREGISTER));
+ nResult = pDialog->Execute();
+
+ return nResult;
+ }
+
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/dialog/dialcontrol.cxx b/svx/source/dialog/dialcontrol.cxx
new file mode 100644
index 000000000..e6c7c8e43
--- /dev/null
+++ b/svx/source/dialog/dialcontrol.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 <svx/dialcontrol.hxx>
+#include <svx/svdtrans.hxx>
+#include <cmath>
+#include <vcl/virdev.hxx>
+#include <vcl/svapp.hxx>
+#include <vcl/bitmapex.hxx>
+#include <vcl/event.hxx>
+#include <vcl/settings.hxx>
+#include <i18nlangtag/languagetag.hxx>
+
+namespace svx {
+
+const tools::Long DIAL_OUTER_WIDTH = 8;
+
+DialControlBmp::DialControlBmp(OutputDevice& rReference)
+ : VirtualDevice(rReference, DeviceFormat::DEFAULT, DeviceFormat::DEFAULT)
+ , mbEnabled(true)
+ , mrParent(rReference)
+ , mnCenterX(0)
+ , mnCenterY(0)
+{
+ EnableRTL(false);
+}
+
+void DialControlBmp::InitBitmap(const vcl::Font& rFont)
+{
+ Init();
+ SetFont(rFont);
+}
+
+void DialControlBmp::CopyBackground( const DialControlBmp& rSrc )
+{
+ Init();
+ SetSize(rSrc.maRect.GetSize());
+ mbEnabled = rSrc.mbEnabled;
+ Point aPos;
+ DrawBitmapEx( aPos, rSrc.GetBitmapEx( aPos, maRect.GetSize() ) );
+}
+
+void DialControlBmp::DrawBackground( const Size& rSize, bool bEnabled )
+{
+ Init();
+ SetSize(rSize);
+ mbEnabled = bEnabled;
+ DrawBackground();
+}
+
+void DialControlBmp::DrawElements( const OUString& rText, Degree100 nAngle )
+{
+ double fAngle = toRadians(nAngle);
+ double fSin = sin( fAngle );
+ double fCos = cos( fAngle );
+ double fWidth = GetTextWidth( rText ) / 2.0;
+ double fHeight = GetTextHeight() / 2.0;
+
+ if ( !rText.isEmpty() )
+ {
+ // rotated text
+ vcl::Font aFont( GetFont() );
+ aFont.SetColor( GetTextColor() );
+ aFont.SetOrientation( to<Degree10>(nAngle) ); // Font uses 1/10 degrees
+ aFont.SetWeight( WEIGHT_BOLD );
+ SetFont( aFont );
+
+ tools::Long nX = static_cast< tools::Long >( mnCenterX - fWidth * fCos - fHeight * fSin );
+ tools::Long nY = static_cast< tools::Long >( mnCenterY + fWidth * fSin - fHeight * fCos );
+ tools::Rectangle aRect( nX, nY, 2 * mnCenterX - nX, 2 * mnCenterY - nY );
+ DrawText( aRect, rText, mbEnabled ? DrawTextFlags::NONE : DrawTextFlags::Disable );
+ }
+ else
+ {
+ // only a line
+ const sal_Int32 nDx (fCos * (maRect.GetWidth()-4) / 2);
+ const sal_Int32 nDy (-fSin * (maRect.GetHeight()-4) / 2);
+ Point pt1( maRect.Center() );
+ Point pt2( pt1.X() + nDx, pt1.Y() + nDy);
+
+ SetLineColor( GetTextColor() );
+ DrawLine( pt1, pt2 );
+ }
+
+ // *** drag button ***
+
+ bool bMain = (nAngle % 4500_deg100) != 0_deg100;
+ SetLineColor( GetButtonLineColor() );
+ SetFillColor( GetButtonFillColor( bMain ) );
+
+ tools::Long nX = mnCenterX - static_cast< tools::Long >( (DIAL_OUTER_WIDTH / 2 - mnCenterX) * fCos );
+ tools::Long nY = mnCenterY - static_cast< tools::Long >( (mnCenterY - DIAL_OUTER_WIDTH / 2) * fSin );
+ tools::Long nSize = bMain ? (DIAL_OUTER_WIDTH / 4) : (DIAL_OUTER_WIDTH / 2 - 1);
+ DrawEllipse( tools::Rectangle( nX - nSize, nY - nSize, nX + nSize, nY + nSize ) );
+}
+
+Color DialControlBmp::GetBackgroundColor() const
+{
+ return GetSettings().GetStyleSettings().GetDialogColor();
+}
+
+const Color& DialControlBmp::GetTextColor() const
+{
+ return GetSettings().GetStyleSettings().GetLabelTextColor();
+}
+
+const Color& DialControlBmp::GetScaleLineColor() const
+{
+ const StyleSettings& rSett = GetSettings().GetStyleSettings();
+ return mbEnabled ? rSett.GetButtonTextColor() : rSett.GetDisableColor();
+}
+
+const Color& DialControlBmp::GetButtonLineColor() const
+{
+ const StyleSettings& rSett = GetSettings().GetStyleSettings();
+ return mbEnabled ? rSett.GetButtonTextColor() : rSett.GetDisableColor();
+}
+
+const Color& DialControlBmp::GetButtonFillColor( bool bMain ) const
+{
+ const StyleSettings& rSett = GetSettings().GetStyleSettings();
+ return mbEnabled ? (bMain ? rSett.GetMenuColor() : rSett.GetHighlightColor()) : rSett.GetDisableColor();
+}
+
+void DialControlBmp::Init()
+{
+ SetSettings(mrParent.GetSettings());
+ SetBackground(GetBackgroundColor());
+}
+
+void DialControlBmp::SetSize( const Size& rSize )
+{
+ maRect.SetPos( Point( 0, 0 ) );
+ maRect.SetSize( rSize );
+ mnCenterX = rSize.Width() / 2;
+ mnCenterY = rSize.Height() / 2;
+ SetOutputSize( rSize );
+}
+
+void DialControlBmp::DrawBackground()
+{
+ // *** background with 3D effect ***
+
+ SetLineColor();
+ SetFillColor();
+ Erase();
+
+ EnableRTL(); // draw 3D effect in correct direction
+
+ sal_uInt8 nDiff = mbEnabled ? 0x18 : 0x10;
+ Color aColor;
+
+ aColor = GetBackgroundColor();
+ SetFillColor( aColor );
+ DrawPie( maRect, maRect.TopRight(), maRect.TopCenter() );
+ DrawPie( maRect, maRect.BottomLeft(), maRect.BottomCenter() );
+
+ aColor.DecreaseLuminance( nDiff );
+ SetFillColor( aColor );
+ DrawPie( maRect, maRect.BottomCenter(), maRect.TopRight() );
+
+ aColor.DecreaseLuminance( nDiff );
+ SetFillColor( aColor );
+ DrawPie( maRect, maRect.BottomRight(), maRect.RightCenter() );
+
+ aColor = GetBackgroundColor();
+ aColor.IncreaseLuminance( nDiff );
+ SetFillColor( aColor );
+ DrawPie( maRect, maRect.TopCenter(), maRect.BottomLeft() );
+
+ aColor.IncreaseLuminance( nDiff );
+ SetFillColor( aColor );
+ DrawPie( maRect, maRect.TopLeft(), maRect.LeftCenter() );
+
+ EnableRTL( false );
+
+ // *** calibration ***
+
+ Point aStartPos( mnCenterX, mnCenterY );
+ Color aFullColor( GetScaleLineColor() );
+ Color aLightColor( GetBackgroundColor() );
+ aLightColor.Merge( aFullColor, 128 );
+
+ for( int nAngle = 0; nAngle < 360; nAngle += 15 )
+ {
+ SetLineColor( (nAngle % 45) ? aLightColor : aFullColor );
+ double fAngle = basegfx::deg2rad(nAngle);
+ tools::Long nX = static_cast< tools::Long >( -mnCenterX * cos( fAngle ) );
+ tools::Long nY = static_cast< tools::Long >( mnCenterY * sin( fAngle ) );
+ DrawLine( aStartPos, Point( mnCenterX - nX, mnCenterY - nY ) );
+ }
+
+ // *** clear inner area ***
+
+ SetLineColor();
+ SetFillColor( GetBackgroundColor() );
+ tools::Rectangle aEllipseRect = maRect;
+ aEllipseRect.shrink(DIAL_OUTER_WIDTH);
+ DrawEllipse( aEllipseRect );
+}
+
+DialControl::DialControl_Impl::DialControl_Impl(OutputDevice& rReference) :
+ mxBmpEnabled(VclPtr<DialControlBmp>::Create(rReference)),
+ mxBmpDisabled(VclPtr<DialControlBmp>::Create(rReference)),
+ mxBmpBuffered(VclPtr<DialControlBmp>::Create(rReference)),
+ mpLinkField( nullptr ),
+ mnLinkedFieldValueMultiplyer( 0 ),
+ mnAngle( 0 ),
+ mnInitialAngle( 0 ),
+ mnOldAngle( 0 ),
+ mnCenterX( 0 ),
+ mnCenterY( 0 ),
+ mbNoRot( false )
+{
+}
+
+void DialControl::DialControl_Impl::Init( const Size& rWinSize, const vcl::Font& rWinFont )
+{
+ maWinFont = rWinFont;
+ maWinFont.SetTransparent(true);
+ mxBmpBuffered->InitBitmap(maWinFont);
+ SetSize(rWinSize);
+}
+
+void DialControl::DialControl_Impl::SetSize( const Size& rWinSize )
+{
+ // make the control squared, and adjusted so that we have a well-defined
+ // center ["(x - 1) | 1" creates odd value <= x]
+ tools::Long nMin = (std::min(rWinSize.Width(), rWinSize.Height()) - 1) | 1;
+
+ maWinSize = Size( nMin, nMin );
+
+ mnCenterX = maWinSize.Width() / 2;
+ mnCenterY = maWinSize.Height() / 2;
+
+ mxBmpEnabled->DrawBackground( maWinSize, true );
+ mxBmpDisabled->DrawBackground( maWinSize, false );
+ mxBmpBuffered->SetSize( maWinSize );
+}
+
+void DialControl::SetDrawingArea(weld::DrawingArea* pDrawingArea)
+{
+ CustomWidgetController::SetDrawingArea(pDrawingArea);
+ //use same logic as DialControl_Impl::SetSize
+ int nDim = (std::min<int>(pDrawingArea->get_approximate_digit_width() * 12,
+ pDrawingArea->get_text_height() * 6) - 1) | 1;
+ Size aSize(nDim, nDim);
+ pDrawingArea->set_size_request(aSize.Width(), aSize.Height());
+ mpImpl.reset(new DialControl_Impl(pDrawingArea->get_ref_device()));
+ //set size and use that
+ Init(aSize);
+}
+
+void DialControl::Resize()
+{
+ mpImpl->SetSize(GetOutputSizePixel());
+ InvalidateControl();
+}
+
+void DialControl::Paint(vcl::RenderContext& rRenderContext, const tools::Rectangle&)
+{
+ Point aPos;
+ rRenderContext.DrawBitmapEx(aPos, mpImpl->mxBmpBuffered->GetBitmapEx(aPos, mpImpl->maWinSize));
+}
+
+void DialControl::StyleUpdated()
+{
+ CustomWidgetController::StyleUpdated();
+ Init( mpImpl->maWinSize, mpImpl->maWinFont );
+ InvalidateControl();
+}
+
+bool DialControl::MouseButtonDown(const MouseEvent& rMEvt)
+{
+ if( rMEvt.IsLeft() )
+ {
+ GrabFocus();
+ CaptureMouse();
+ mpImpl->mnOldAngle = mpImpl->mnAngle;
+ HandleMouseEvent( rMEvt.GetPosPixel(), true );
+ }
+ return true;
+}
+
+bool DialControl::MouseMove( const MouseEvent& rMEvt )
+{
+ if( IsMouseCaptured() && rMEvt.IsLeft() )
+ HandleMouseEvent( rMEvt.GetPosPixel(), false );
+ return true;
+}
+
+bool DialControl::MouseButtonUp(const MouseEvent&)
+{
+ if( IsMouseCaptured() )
+ {
+ ReleaseMouse();
+ if( mpImpl->mpLinkField )
+ mpImpl->mpLinkField->grab_focus();
+ }
+ return true;
+}
+
+bool DialControl::KeyInput( const KeyEvent& rKEvt )
+{
+ const vcl::KeyCode& rKCode = rKEvt.GetKeyCode();
+ if( !rKCode.GetModifier() && (rKCode.GetCode() == KEY_ESCAPE) )
+ {
+ HandleEscapeEvent();
+ return true;
+ }
+ return CustomWidgetController::KeyInput(rKEvt);
+}
+
+void DialControl::LoseFocus()
+{
+ // release captured mouse
+ HandleEscapeEvent();
+}
+
+bool DialControl::HasRotation() const
+{
+ return !mpImpl->mbNoRot;
+}
+
+void DialControl::SetNoRotation()
+{
+ if( !mpImpl->mbNoRot )
+ {
+ mpImpl->mbNoRot = true;
+ InvalidateControl();
+ if (mpImpl->mpLinkField)
+ mpImpl->mpLinkField->set_text("");
+ }
+}
+
+Degree100 DialControl::GetRotation() const
+{
+ return mpImpl->mnAngle;
+}
+
+void DialControl::SetRotation(Degree100 nAngle)
+{
+ SetRotation(nAngle, false);
+}
+
+void DialControl::SetLinkedField(weld::MetricSpinButton* pField, sal_Int32 nDecimalPlaces)
+{
+ mpImpl->mnLinkedFieldValueMultiplyer = 100 / std::pow(10.0, double(nDecimalPlaces));
+
+ // remove modify handler from old linked field
+ if( mpImpl->mpLinkField )
+ {
+ weld::MetricSpinButton& rField = *mpImpl->mpLinkField;
+ rField.connect_value_changed(Link<weld::MetricSpinButton&,void>());
+ }
+ // remember the new linked field
+ mpImpl->mpLinkField = pField;
+ // set modify handler at new linked field
+ if( mpImpl->mpLinkField )
+ {
+ weld::MetricSpinButton& rField = *mpImpl->mpLinkField;
+ rField.connect_value_changed(LINK(this, DialControl, LinkedFieldModifyHdl));
+ }
+}
+
+IMPL_LINK_NOARG(DialControl, LinkedFieldModifyHdl, weld::MetricSpinButton&, void)
+{
+ SetRotation(Degree100(mpImpl->mpLinkField->get_value(FieldUnit::DEGREE) * mpImpl->mnLinkedFieldValueMultiplyer), true);
+}
+
+void DialControl::SaveValue()
+{
+ mpImpl->mnInitialAngle = mpImpl->mnAngle;
+}
+
+bool DialControl::IsValueModified() const
+{
+ return mpImpl->mnInitialAngle != mpImpl->mnAngle;
+}
+
+void DialControl::Init( const Size& rWinSize, const vcl::Font& rWinFont )
+{
+ mpImpl->Init( rWinSize, rWinFont );
+ EnableRTL( false ); // don't mirror mouse handling
+ SetOutputSizePixel( mpImpl->maWinSize );
+}
+
+void DialControl::Init( const Size& rWinSize )
+{
+ //hidpi TODO: GetDefaultFont() picks a font size too small, so fix it here.
+ vcl::Font aDefaultSize = Application::GetSettings().GetStyleSettings().GetLabelFont();
+
+ vcl::Font aFont( OutputDevice::GetDefaultFont(
+ DefaultFontType::UI_SANS, Application::GetSettings().GetUILanguageTag().getLanguageType(), GetDefaultFontFlags::OnlyOne ) );
+
+ aFont.SetFontHeight(aDefaultSize.GetFontHeight());
+ Init( rWinSize, aFont );
+}
+
+void DialControl::InvalidateControl()
+{
+ mpImpl->mxBmpBuffered->CopyBackground( IsEnabled() ? *mpImpl->mxBmpEnabled : *mpImpl->mxBmpDisabled );
+ if( !mpImpl->mbNoRot )
+ mpImpl->mxBmpBuffered->DrawElements(GetText(), mpImpl->mnAngle);
+ Invalidate();
+}
+
+void DialControl::SetRotation(Degree100 nAngle, bool bBroadcast)
+{
+ bool bOldSel = mpImpl->mbNoRot;
+ mpImpl->mbNoRot = false;
+
+ nAngle = NormAngle36000(nAngle);
+
+ if (!bOldSel || (mpImpl->mnAngle != nAngle))
+ {
+ mpImpl->mnAngle = nAngle;
+ InvalidateControl();
+ if( mpImpl->mpLinkField )
+ mpImpl->mpLinkField->set_value(GetRotation().get() / mpImpl->mnLinkedFieldValueMultiplyer, FieldUnit::DEGREE);
+ if( bBroadcast )
+ mpImpl->maModifyHdl.Call(*this);
+ }
+}
+
+void DialControl::SetModifyHdl( const Link<DialControl&,void>& rLink )
+{
+ mpImpl->maModifyHdl = rLink;
+}
+
+void DialControl::HandleMouseEvent( const Point& rPos, bool bInitial )
+{
+ tools::Long nX = rPos.X() - mpImpl->mnCenterX;
+ tools::Long nY = mpImpl->mnCenterY - rPos.Y();
+ double fH = sqrt( static_cast< double >( nX ) * nX + static_cast< double >( nY ) * nY );
+ if( fH != 0.0 )
+ {
+ double fAngle = acos( nX / fH );
+ sal_Int32 nAngle = basegfx::rad2deg<100>(fAngle);
+ if( nY < 0 )
+ nAngle = 36000 - nAngle;
+ if( bInitial ) // round to entire 15 degrees
+ nAngle = ((nAngle + 750) / 1500) * 1500;
+ // Round up to 1 degree
+ nAngle = (((nAngle + 50) / 100) * 100) % 36000;
+ SetRotation(Degree100(nAngle), true);
+ }
+}
+
+void DialControl::HandleEscapeEvent()
+{
+ if( IsMouseCaptured() )
+ {
+ ReleaseMouse();
+ SetRotation(mpImpl->mnOldAngle, true);
+ if( mpImpl->mpLinkField )
+ mpImpl->mpLinkField->grab_focus();
+ }
+}
+
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/dialog/dialmgr.cxx b/svx/source/dialog/dialmgr.cxx
new file mode 100644
index 000000000..9fdd50dda
--- /dev/null
+++ b/svx/source/dialog/dialmgr.cxx
@@ -0,0 +1,26 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * 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/dialmgr.hxx>
+
+std::locale SvxResLocale() { return Translate::Create("svx"); }
+
+OUString SvxResId(TranslateId aId) { return Translate::get(aId, SvxResLocale()); }
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/dialog/dlgctl3d.cxx b/svx/source/dialog/dlgctl3d.cxx
new file mode 100644
index 000000000..792485a06
--- /dev/null
+++ b/svx/source/dialog/dlgctl3d.cxx
@@ -0,0 +1,1173 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * 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/dlgctl3d.hxx>
+#include <svx/strings.hrc>
+#include <svx/view3d.hxx>
+#include <svx/fmmodel.hxx>
+#include <svl/itempool.hxx>
+#include <svx/fmpage.hxx>
+#include <svx/sphere3d.hxx>
+#include <svx/cube3d.hxx>
+#include <svx/scene3d.hxx>
+#include <vcl/svapp.hxx>
+#include <svx/helperhittest3d.hxx>
+#include <basegfx/polygon/b2dpolygontools.hxx>
+#include <polygn3d.hxx>
+#include <svx/xfillit0.hxx>
+#include <svx/xflclit.hxx>
+#include <svx/xlineit0.hxx>
+#include <svx/xlnclit.hxx>
+#include <svx/xlnwtit.hxx>
+#include <helpids.h>
+#include <svx/dialmgr.hxx>
+#include <tools/helpers.hxx>
+#include <vcl/settings.hxx>
+
+using namespace com::sun::star;
+
+Svx3DPreviewControl::Svx3DPreviewControl()
+ : mpScene(nullptr)
+ , mp3DObj(nullptr)
+ , mnObjectType(SvxPreviewObjectType::SPHERE)
+{
+}
+
+void Svx3DPreviewControl::SetDrawingArea(weld::DrawingArea* pDrawingArea)
+{
+ Size aSize(pDrawingArea->get_ref_device().LogicToPixel(Size(80, 100), MapMode(MapUnit::MapAppFont)));
+ pDrawingArea->set_size_request(aSize.Width(), aSize.Height());
+ CustomWidgetController::SetDrawingArea(pDrawingArea);
+ SetOutputSizePixel(aSize);
+
+ Construct();
+}
+
+Svx3DPreviewControl::~Svx3DPreviewControl()
+{
+ mxFmPage.clear();
+ mp3DView.reset();
+ mpModel.reset();
+}
+
+void Svx3DPreviewControl::Construct()
+{
+ // Do never mirror the preview window. This explicitly includes right
+ // to left writing environments.
+ EnableRTL (false);
+ OutputDevice& rDevice = GetDrawingArea()->get_ref_device();
+ rDevice.SetMapMode(MapMode(MapUnit::Map100thMM));
+
+ // Model
+ mpModel.reset(new FmFormModel());
+ mpModel->GetItemPool().FreezeIdRanges();
+
+ // Page
+ mxFmPage = new FmFormPage( *mpModel );
+ mpModel->InsertPage( mxFmPage.get(), 0 );
+
+ // 3D View
+ mp3DView.reset(new E3dView(*mpModel, &rDevice));
+ mp3DView->SetBufferedOutputAllowed(true);
+ mp3DView->SetBufferedOverlayAllowed(true);
+
+ // 3D Scene
+ mpScene = new E3dScene(*mpModel);
+
+ // initially create object
+ SetObjectType(SvxPreviewObjectType::SPHERE);
+
+ // camera and perspective
+ Camera3D rCamera = mpScene->GetCamera();
+ const basegfx::B3DRange& rVolume = mpScene->GetBoundVolume();
+ double fW = rVolume.getWidth();
+ double fH = rVolume.getHeight();
+ double fCamZ = rVolume.getMaxZ() + ((fW + fH) / 2.0);
+
+ rCamera.SetAutoAdjustProjection(false);
+ rCamera.SetViewWindow(- fW / 2, - fH / 2, fW, fH);
+ basegfx::B3DPoint aLookAt;
+ double fDefaultCamPosZ = mp3DView->GetDefaultCamPosZ();
+ basegfx::B3DPoint aCamPos(0.0, 0.0, fCamZ < fDefaultCamPosZ ? fDefaultCamPosZ : fCamZ);
+ rCamera.SetPosAndLookAt(aCamPos, aLookAt);
+ double fDefaultCamFocal = mp3DView->GetDefaultCamFocal();
+ rCamera.SetFocalLength(fDefaultCamFocal);
+
+ mpScene->SetCamera( rCamera );
+ mxFmPage->InsertObject( mpScene );
+
+ basegfx::B3DHomMatrix aRotation;
+ aRotation.rotate(basegfx::deg2rad( 25 ), 0.0, 0.0);
+ aRotation.rotate(0.0, basegfx::deg2rad( 40 ), 0.0);
+ mpScene->SetTransform(aRotation * mpScene->GetTransform());
+
+ // invalidate SnapRects of objects
+ mpScene->SetBoundAndSnapRectsDirty();
+
+ SfxItemSetFixed<XATTR_LINESTYLE, XATTR_LINESTYLE,
+ XATTR_FILL_FIRST, XATTR_FILLBITMAP> aSet( mpModel->GetItemPool() );
+ aSet.Put( XLineStyleItem( drawing::LineStyle_NONE ) );
+ aSet.Put( XFillStyleItem( drawing::FillStyle_SOLID ) );
+ aSet.Put( XFillColorItem( "", COL_WHITE ) );
+
+ mpScene->SetMergedItemSet(aSet);
+
+ // PageView
+ SdrPageView* pPageView = mp3DView->ShowSdrPage( mxFmPage.get() );
+ mp3DView->hideMarkHandles();
+
+ // mark scene
+ mp3DView->MarkObj( mpScene, pPageView );
+}
+
+void Svx3DPreviewControl::Resize()
+{
+ // size of page
+ Size aSize(GetOutputSizePixel());
+ aSize = GetDrawingArea()->get_ref_device().PixelToLogic(aSize);
+ mxFmPage->SetSize(aSize);
+
+ // set size
+ Size aObjSize( aSize.Width()*5/6, aSize.Height()*5/6 );
+ Point aObjPoint( (aSize.Width() - aObjSize.Width()) / 2,
+ (aSize.Height() - aObjSize.Height()) / 2);
+ tools::Rectangle aRect( aObjPoint, aObjSize);
+ mpScene->SetSnapRect( aRect );
+}
+
+void Svx3DPreviewControl::Paint(vcl::RenderContext& rRenderContext, const tools::Rectangle& rRect)
+{
+ mp3DView->CompleteRedraw(&rRenderContext, vcl::Region(rRect));
+}
+
+bool Svx3DPreviewControl::MouseButtonDown(const MouseEvent& rMEvt)
+{
+ if (rMEvt.IsShift() && rMEvt.IsMod1())
+ {
+ if(SvxPreviewObjectType::SPHERE == GetObjectType())
+ {
+ SetObjectType(SvxPreviewObjectType::CUBE);
+ }
+ else
+ {
+ SetObjectType(SvxPreviewObjectType::SPHERE);
+ }
+ }
+ return false;
+}
+
+void Svx3DPreviewControl::SetObjectType(SvxPreviewObjectType nType)
+{
+ if(mnObjectType == nType && mp3DObj)
+ return;
+
+ SfxItemSetFixed<SDRATTR_START, SDRATTR_END> aSet(mpModel->GetItemPool());
+ mnObjectType = nType;
+
+ if( mp3DObj )
+ {
+ aSet.Put(mp3DObj->GetMergedItemSet());
+ mpScene->RemoveObject( mp3DObj->GetOrdNum() );
+ // always use SdrObject::Free(...) for SdrObjects (!)
+ SdrObject* pTemp(mp3DObj);
+ SdrObject::Free(pTemp);
+ }
+
+ switch( nType )
+ {
+ case SvxPreviewObjectType::SPHERE:
+ {
+ mp3DObj = new E3dSphereObj(
+ *mpModel,
+ mp3DView->Get3DDefaultAttributes(),
+ basegfx::B3DPoint( 0, 0, 0 ),
+ basegfx::B3DVector( 5000, 5000, 5000 ));
+ }
+ break;
+
+ case SvxPreviewObjectType::CUBE:
+ {
+ mp3DObj = new E3dCubeObj(
+ *mpModel,
+ mp3DView->Get3DDefaultAttributes(),
+ basegfx::B3DPoint( -2500, -2500, -2500 ),
+ basegfx::B3DVector( 5000, 5000, 5000 ));
+ }
+ break;
+ }
+
+ if (mp3DObj)
+ {
+ mpScene->InsertObject( mp3DObj );
+ mp3DObj->SetMergedItemSet(aSet);
+ }
+
+ Invalidate();
+}
+
+SfxItemSet const & Svx3DPreviewControl::Get3DAttributes() const
+{
+ return mp3DObj->GetMergedItemSet();
+}
+
+void Svx3DPreviewControl::Set3DAttributes( const SfxItemSet& rAttr )
+{
+ mp3DObj->SetMergedItemSet(rAttr, true);
+ Resize();
+ Invalidate();
+}
+
+#define RADIUS_LAMP_PREVIEW_SIZE (4500.0)
+#define RADIUS_LAMP_SMALL (600.0)
+#define RADIUS_LAMP_BIG (1000.0)
+#define NO_LIGHT_SELECTED (0xffffffff)
+#define MAX_NUMBER_LIGHTS (8)
+
+const sal_Int32 g_nInteractionStartDistance = 5 * 5 * 2;
+
+Svx3DLightControl::Svx3DLightControl()
+: maSelectedLight(NO_LIGHT_SELECTED),
+ mpExpansionObject(nullptr),
+ mpLampBottomObject(nullptr),
+ mpLampShaftObject(nullptr),
+ maLightObjects(MAX_NUMBER_LIGHTS, nullptr),
+ mfRotateX(-20.0),
+ mfRotateY(45.0),
+ mfRotateZ(0.0),
+ mfSaveActionStartHor(0.0),
+ mfSaveActionStartVer(0.0),
+ mfSaveActionStartRotZ(0.0),
+ mbMouseMoved(false),
+ mbMouseCaptured(false),
+ mbGeometrySelected(false)
+{
+}
+
+void Svx3DLightControl::SetDrawingArea(weld::DrawingArea* pDrawingArea)
+{
+ Svx3DPreviewControl::SetDrawingArea(pDrawingArea);
+ Construct2();
+}
+
+void Svx3DLightControl::Construct2()
+{
+ {
+ // hide all page stuff, use control background (normally gray)
+ const Color aDialogColor(Application::GetSettings().GetStyleSettings().GetDialogColor());
+ mp3DView->SetPageVisible(false);
+ mp3DView->SetApplicationBackgroundColor(aDialogColor);
+ mp3DView->SetApplicationDocumentColor(aDialogColor);
+ }
+
+ {
+ // create invisible expansion object
+ const double fMaxExpansion(RADIUS_LAMP_BIG + RADIUS_LAMP_PREVIEW_SIZE);
+ mpExpansionObject = new E3dCubeObj(
+ *mpModel,
+ mp3DView->Get3DDefaultAttributes(),
+ basegfx::B3DPoint(-fMaxExpansion, -fMaxExpansion, -fMaxExpansion),
+ basegfx::B3DVector(2.0 * fMaxExpansion, 2.0 * fMaxExpansion, 2.0 * fMaxExpansion));
+ mpScene->InsertObject( mpExpansionObject );
+ SfxItemSet aSet(mpModel->GetItemPool());
+ aSet.Put( XLineStyleItem( drawing::LineStyle_NONE ) );
+ aSet.Put( XFillStyleItem( drawing::FillStyle_NONE ) );
+ mpExpansionObject->SetMergedItemSet(aSet);
+ }
+
+ {
+ // create lamp control object (Yellow lined object)
+ // base circle
+ const basegfx::B2DPolygon a2DCircle(basegfx::utils::createPolygonFromCircle(basegfx::B2DPoint(0.0, 0.0), RADIUS_LAMP_PREVIEW_SIZE));
+ basegfx::B3DPolygon a3DCircle(basegfx::utils::createB3DPolygonFromB2DPolygon(a2DCircle));
+ basegfx::B3DHomMatrix aTransform;
+
+ aTransform.rotate(M_PI_2, 0.0, 0.0);
+ aTransform.translate(0.0, -RADIUS_LAMP_PREVIEW_SIZE, 0.0);
+ a3DCircle.transform(aTransform);
+
+ // create object for it
+ mpLampBottomObject = new E3dPolygonObj(
+ *mpModel,
+ basegfx::B3DPolyPolygon(a3DCircle));
+ mpScene->InsertObject( mpLampBottomObject );
+
+ // half circle with stand
+ basegfx::B2DPolygon a2DHalfCircle;
+ a2DHalfCircle.append(basegfx::B2DPoint(RADIUS_LAMP_PREVIEW_SIZE, 0.0));
+ a2DHalfCircle.append(basegfx::B2DPoint(RADIUS_LAMP_PREVIEW_SIZE, -RADIUS_LAMP_PREVIEW_SIZE));
+ a2DHalfCircle.append(basegfx::utils::createPolygonFromEllipseSegment(
+ basegfx::B2DPoint(0.0, 0.0), RADIUS_LAMP_PREVIEW_SIZE, RADIUS_LAMP_PREVIEW_SIZE, 2 * M_PI - M_PI_2, M_PI_2));
+ basegfx::B3DPolygon a3DHalfCircle(basegfx::utils::createB3DPolygonFromB2DPolygon(a2DHalfCircle));
+
+ // create object for it
+ mpLampShaftObject = new E3dPolygonObj(
+ *mpModel,
+ basegfx::B3DPolyPolygon(a3DHalfCircle));
+ mpScene->InsertObject( mpLampShaftObject );
+
+ // initially invisible
+ SfxItemSet aSet(mpModel->GetItemPool());
+ aSet.Put( XLineStyleItem( drawing::LineStyle_NONE ) );
+ aSet.Put( XFillStyleItem( drawing::FillStyle_NONE ) );
+
+ mpLampBottomObject->SetMergedItemSet(aSet);
+ mpLampShaftObject->SetMergedItemSet(aSet);
+ }
+
+ {
+ // change camera settings
+ Camera3D rCamera = mpScene->GetCamera();
+ const basegfx::B3DRange& rVolume = mpScene->GetBoundVolume();
+ double fW = rVolume.getWidth();
+ double fH = rVolume.getHeight();
+ double fCamZ = rVolume.getMaxZ() + ((fW + fH) / 2.0);
+
+ rCamera.SetAutoAdjustProjection(false);
+ rCamera.SetViewWindow(- fW / 2, - fH / 2, fW, fH);
+ basegfx::B3DPoint aLookAt;
+ double fDefaultCamPosZ = mp3DView->GetDefaultCamPosZ();
+ basegfx::B3DPoint aCamPos(0.0, 0.0, fCamZ < fDefaultCamPosZ ? fDefaultCamPosZ : fCamZ);
+ rCamera.SetPosAndLookAt(aCamPos, aLookAt);
+ double fDefaultCamFocal = mp3DView->GetDefaultCamFocal();
+ rCamera.SetFocalLength(fDefaultCamFocal);
+
+ mpScene->SetCamera( rCamera );
+
+ basegfx::B3DHomMatrix aNeutral;
+ mpScene->SetTransform(aNeutral);
+ }
+
+ // invalidate SnapRects of objects
+ mpScene->SetBoundAndSnapRectsDirty();
+}
+
+void Svx3DLightControl::ConstructLightObjects()
+{
+ for(sal_uInt32 a(0); a < MAX_NUMBER_LIGHTS; a++)
+ {
+ // get rid of possible existing light object
+ if(maLightObjects[a])
+ {
+ mpScene->RemoveObject(maLightObjects[a]->GetOrdNum());
+ // always use SdrObject::Free(...) for SdrObjects (!)
+ SdrObject* pTemp(maLightObjects[a]);
+ SdrObject::Free(pTemp);
+ maLightObjects[a] = nullptr;
+ }
+
+ if(GetLightOnOff(a))
+ {
+ const bool bIsSelectedLight(a == maSelectedLight);
+ basegfx::B3DVector aDirection(GetLightDirection(a));
+ aDirection.normalize();
+ aDirection *= RADIUS_LAMP_PREVIEW_SIZE;
+
+ const double fLampSize(bIsSelectedLight ? RADIUS_LAMP_BIG : RADIUS_LAMP_SMALL);
+ E3dObject* pNewLight = new E3dSphereObj(
+ *mpModel,
+ mp3DView->Get3DDefaultAttributes(),
+ basegfx::B3DPoint( 0, 0, 0 ),
+ basegfx::B3DVector( fLampSize, fLampSize, fLampSize));
+ mpScene->InsertObject(pNewLight);
+
+ basegfx::B3DHomMatrix aTransform;
+ aTransform.translate(aDirection.getX(), aDirection.getY(), aDirection.getZ());
+ pNewLight->SetTransform(aTransform);
+
+ SfxItemSet aSet(mpModel->GetItemPool());
+ aSet.Put( XLineStyleItem( drawing::LineStyle_NONE ) );
+ aSet.Put( XFillStyleItem( drawing::FillStyle_SOLID ) );
+ aSet.Put( XFillColorItem(OUString(), GetLightColor(a)));
+ pNewLight->SetMergedItemSet(aSet);
+
+ maLightObjects[a] = pNewLight;
+ }
+ }
+}
+
+void Svx3DLightControl::AdaptToSelectedLight()
+{
+ if(NO_LIGHT_SELECTED == maSelectedLight)
+ {
+ // make mpLampBottomObject/mpLampShaftObject invisible
+ SfxItemSet aSet(mpModel->GetItemPool());
+ aSet.Put( XLineStyleItem( drawing::LineStyle_NONE ) );
+ aSet.Put( XFillStyleItem( drawing::FillStyle_NONE ) );
+ mpLampBottomObject->SetMergedItemSet(aSet);
+ mpLampShaftObject->SetMergedItemSet(aSet);
+ }
+ else
+ {
+ basegfx::B3DVector aDirection(GetLightDirection(maSelectedLight));
+ aDirection.normalize();
+
+ // make mpLampBottomObject/mpLampShaftObject visible (yellow hairline)
+ SfxItemSet aSet(mpModel->GetItemPool());
+ aSet.Put( XLineStyleItem( drawing::LineStyle_SOLID ) );
+ aSet.Put( XLineColorItem(OUString(), COL_YELLOW));
+ aSet.Put( XLineWidthItem(0));
+ aSet.Put( XFillStyleItem( drawing::FillStyle_NONE ) );
+ mpLampBottomObject->SetMergedItemSet(aSet);
+ mpLampShaftObject->SetMergedItemSet(aSet);
+
+ // adapt transformation of mpLampShaftObject
+ basegfx::B3DHomMatrix aTransform;
+ double fRotateY(0.0);
+
+ if(!basegfx::fTools::equalZero(aDirection.getZ()) || !basegfx::fTools::equalZero(aDirection.getX()))
+ {
+ fRotateY = atan2(-aDirection.getZ(), aDirection.getX());
+ }
+
+ aTransform.rotate(0.0, fRotateY, 0.0);
+ mpLampShaftObject->SetTransform(aTransform);
+
+ // adapt transformation of selected light
+ E3dObject* pSelectedLight = maLightObjects[sal_Int32(maSelectedLight)];
+
+ if(pSelectedLight)
+ {
+ aTransform.identity();
+ aTransform.translate(
+ aDirection.getX() * RADIUS_LAMP_PREVIEW_SIZE,
+ aDirection.getY() * RADIUS_LAMP_PREVIEW_SIZE,
+ aDirection.getZ() * RADIUS_LAMP_PREVIEW_SIZE);
+ pSelectedLight->SetTransform(aTransform);
+ }
+ }
+}
+
+void Svx3DLightControl::TrySelection(Point aPosPixel)
+{
+ if(!mpScene)
+ return;
+
+ const Point aPosLogic(GetDrawingArea()->get_ref_device().PixelToLogic(aPosPixel));
+ const basegfx::B2DPoint aPoint(aPosLogic.X(), aPosLogic.Y());
+ std::vector< const E3dCompoundObject* > aResult;
+ getAllHit3DObjectsSortedFrontToBack(aPoint, *mpScene, aResult);
+
+ if(aResult.empty())
+ return;
+
+ // exclude expansion object which will be part of
+ // the hits. It's invisible, but for HitTest, it's included
+ const E3dCompoundObject* pResult = nullptr;
+
+ for(auto const & b: aResult)
+ {
+ if(b && b != mpExpansionObject)
+ {
+ pResult = b;
+ break;
+ }
+ }
+
+ if(pResult == mp3DObj)
+ {
+ if(!mbGeometrySelected)
+ {
+ mbGeometrySelected = true;
+ maSelectedLight = NO_LIGHT_SELECTED;
+ ConstructLightObjects();
+ AdaptToSelectedLight();
+ Invalidate();
+
+ if(maSelectionChangeCallback.IsSet())
+ {
+ maSelectionChangeCallback.Call(this);
+ }
+ }
+ }
+ else
+ {
+ sal_uInt32 aNewSelectedLight(NO_LIGHT_SELECTED);
+
+ for(sal_uInt32 a(0); a < MAX_NUMBER_LIGHTS; a++)
+ {
+ if(maLightObjects[a] && maLightObjects[a] == pResult)
+ {
+ aNewSelectedLight = a;
+ }
+ }
+
+ if(aNewSelectedLight != maSelectedLight)
+ {
+ SelectLight(aNewSelectedLight);
+
+ if(maSelectionChangeCallback.IsSet())
+ {
+ maSelectionChangeCallback.Call(this);
+ }
+ }
+ }
+}
+
+void Svx3DLightControl::Paint(vcl::RenderContext& rRenderContext, const tools::Rectangle& rRect)
+{
+ Svx3DPreviewControl::Paint(rRenderContext, rRect);
+}
+
+tools::Rectangle Svx3DLightControl::GetFocusRect()
+{
+ if (!HasFocus())
+ return tools::Rectangle();
+ Size aFocusSize = GetOutputSizePixel();
+ aFocusSize.AdjustWidth( -4 );
+ aFocusSize.AdjustHeight( -4 );
+ return tools::Rectangle(Point(2, 2), aFocusSize);
+}
+
+bool Svx3DLightControl::MouseButtonDown( const MouseEvent& rMEvt )
+{
+ bool bCallParent(true);
+
+ // switch state
+ if(rMEvt.IsLeft())
+ {
+ if(IsSelectionValid() || mbGeometrySelected)
+ {
+ mbMouseMoved = false;
+ bCallParent = false;
+ maActionStartPoint = rMEvt.GetPosPixel();
+ CaptureMouse();
+ mbMouseCaptured = true;
+ }
+ else
+ {
+ // Single click without moving much trying to do a selection
+ TrySelection(rMEvt.GetPosPixel());
+ bCallParent = false;
+ }
+ }
+
+ // call parent
+ if (bCallParent)
+ return Svx3DPreviewControl::MouseButtonDown(rMEvt);
+ return true;
+}
+
+bool Svx3DLightControl::MouseMove(const MouseEvent& rMEvt)
+{
+ if (!mbMouseCaptured)
+ return false;
+
+ Point aDeltaPos = rMEvt.GetPosPixel() - maActionStartPoint;
+
+ if(!mbMouseMoved)
+ {
+ if(sal_Int32(aDeltaPos.X() * aDeltaPos.X() + aDeltaPos.Y() * aDeltaPos.Y()) > g_nInteractionStartDistance)
+ {
+ if(mbGeometrySelected)
+ {
+ GetRotation(mfSaveActionStartVer, mfSaveActionStartHor, mfSaveActionStartRotZ);
+ }
+ else
+ {
+ // interaction start, save values
+ GetPosition(mfSaveActionStartHor, mfSaveActionStartVer);
+ }
+
+ mbMouseMoved = true;
+ }
+ }
+
+ if(mbMouseMoved)
+ {
+ if(mbGeometrySelected)
+ {
+ double fNewRotX = mfSaveActionStartVer - basegfx::deg2rad(aDeltaPos.Y());
+ double fNewRotY = mfSaveActionStartHor + basegfx::deg2rad(aDeltaPos.X());
+
+ // cut horizontal
+ while(fNewRotY < 0.0)
+ {
+ fNewRotY += 2 * M_PI;
+ }
+
+ while(fNewRotY >= 2 * M_PI)
+ {
+ fNewRotY -= 2 * M_PI;
+ }
+
+ // cut vertical
+ if(fNewRotX < -M_PI_2)
+ {
+ fNewRotX = -M_PI_2;
+ }
+
+ if(fNewRotX > M_PI_2)
+ {
+ fNewRotX = M_PI_2;
+ }
+
+ SetRotation(fNewRotX, fNewRotY, mfSaveActionStartRotZ);
+
+ if(maChangeCallback.IsSet())
+ {
+ maChangeCallback.Call(this);
+ }
+ }
+ else
+ {
+ // interaction in progress
+ double fNewPosHor = mfSaveActionStartHor + static_cast<double>(aDeltaPos.X());
+ double fNewPosVer = mfSaveActionStartVer - static_cast<double>(aDeltaPos.Y());
+
+ // cut horizontal
+ fNewPosHor = NormAngle360(fNewPosHor);
+
+ // cut vertical
+ if(fNewPosVer < -90.0)
+ {
+ fNewPosVer = -90.0;
+ }
+
+ if(fNewPosVer > 90.0)
+ {
+ fNewPosVer = 90.0;
+ }
+
+ SetPosition(fNewPosHor, fNewPosVer);
+
+ if(maChangeCallback.IsSet())
+ {
+ maChangeCallback.Call(this);
+ }
+ }
+ }
+ return true;
+}
+
+bool Svx3DLightControl::MouseButtonUp(const MouseEvent& rMEvt)
+{
+ if (!mbMouseCaptured)
+ return false;
+ ReleaseMouse();
+ mbMouseCaptured = false;
+
+ if (mbMouseMoved)
+ {
+ // was change interactively
+ }
+ else
+ {
+ // simple click without much movement, try selection
+ TrySelection(rMEvt.GetPosPixel());
+ }
+
+ return true;
+}
+
+void Svx3DLightControl::Resize()
+{
+ // set size of page
+ const Size aSize(GetDrawingArea()->get_ref_device().PixelToLogic(GetOutputSizePixel()));
+ mxFmPage->SetSize(aSize);
+
+ // set position and size of scene
+ mpScene->SetSnapRect(tools::Rectangle(Point(0, 0), aSize));
+}
+
+void Svx3DLightControl::SetObjectType(SvxPreviewObjectType nType)
+{
+ // call parent
+ Svx3DPreviewControl::SetObjectType(nType);
+
+ // apply object rotation
+ if(mp3DObj)
+ {
+ basegfx::B3DHomMatrix aObjectRotation;
+ aObjectRotation.rotate(mfRotateX, mfRotateY, mfRotateZ);
+ mp3DObj->SetTransform(aObjectRotation);
+ }
+}
+
+bool Svx3DLightControl::IsSelectionValid()
+{
+ return (NO_LIGHT_SELECTED != maSelectedLight) && GetLightOnOff(maSelectedLight);
+}
+
+void Svx3DLightControl::GetPosition(double& rHor, double& rVer)
+{
+ if(IsSelectionValid())
+ {
+ basegfx::B3DVector aDirection(GetLightDirection(maSelectedLight));
+ aDirection.normalize();
+ rHor = basegfx::rad2deg(atan2(-aDirection.getX(), -aDirection.getZ()) + M_PI); // 0..360.0
+ rVer = basegfx::rad2deg(atan2(aDirection.getY(), aDirection.getXZLength())); // -90.0..90.0
+ }
+ if(IsGeometrySelected())
+ {
+ rHor = basegfx::rad2deg(mfRotateY); // 0..360.0
+ rVer = basegfx::rad2deg(mfRotateX); // -90.0..90.0
+ }
+}
+
+void Svx3DLightControl::SetPosition(double fHor, double fVer)
+{
+ if(IsSelectionValid())
+ {
+ // set selected light's direction
+ fHor = basegfx::deg2rad(fHor) - M_PI; // -PI..PI
+ fVer = basegfx::deg2rad(fVer); // -PI2..PI2
+ basegfx::B3DVector aDirection(cos(fVer) * -sin(fHor), sin(fVer), cos(fVer) * -cos(fHor));
+ aDirection.normalize();
+
+ if(!aDirection.equal(GetLightDirection(maSelectedLight)))
+ {
+ // set changed light direction at SdrScene
+ SfxItemSet aSet(mpModel->GetItemPool());
+
+ switch(maSelectedLight)
+ {
+ case 0: aSet.Put(makeSvx3DLightDirection1Item(aDirection)); break;
+ case 1: aSet.Put(makeSvx3DLightDirection2Item(aDirection)); break;
+ case 2: aSet.Put(makeSvx3DLightDirection3Item(aDirection)); break;
+ case 3: aSet.Put(makeSvx3DLightDirection4Item(aDirection)); break;
+ case 4: aSet.Put(makeSvx3DLightDirection5Item(aDirection)); break;
+ case 5: aSet.Put(makeSvx3DLightDirection6Item(aDirection)); break;
+ case 6: aSet.Put(makeSvx3DLightDirection7Item(aDirection)); break;
+ default:
+ case 7: aSet.Put(makeSvx3DLightDirection8Item(aDirection)); break;
+ }
+
+ mpScene->SetMergedItemSet(aSet);
+
+ // correct 3D light's and LampFrame's geometries
+ AdaptToSelectedLight();
+ Invalidate();
+ }
+ }
+ if(!IsGeometrySelected())
+ return;
+
+ if(mfRotateX == fVer && mfRotateY == fHor)
+ return;
+
+ mfRotateX = basegfx::deg2rad(fVer);
+ mfRotateY = basegfx::deg2rad(fHor);
+
+ if(mp3DObj)
+ {
+ basegfx::B3DHomMatrix aObjectRotation;
+ aObjectRotation.rotate(mfRotateX, mfRotateY, mfRotateZ);
+ mp3DObj->SetTransform(aObjectRotation);
+
+ Invalidate();
+ }
+}
+
+void Svx3DLightControl::SetRotation(double fRotX, double fRotY, double fRotZ)
+{
+ if(!IsGeometrySelected())
+ return;
+
+ if(fRotX == mfRotateX && fRotY == mfRotateY && fRotZ == mfRotateZ)
+ return;
+
+ mfRotateX = fRotX;
+ mfRotateY = fRotY;
+ mfRotateZ = fRotZ;
+
+ if(mp3DObj)
+ {
+ basegfx::B3DHomMatrix aObjectRotation;
+ aObjectRotation.rotate(mfRotateX, mfRotateY, mfRotateZ);
+ mp3DObj->SetTransform(aObjectRotation);
+
+ Invalidate();
+ }
+}
+
+void Svx3DLightControl::GetRotation(double& rRotX, double& rRotY, double& rRotZ)
+{
+ rRotX = mfRotateX;
+ rRotY = mfRotateY;
+ rRotZ = mfRotateZ;
+}
+
+void Svx3DLightControl::Set3DAttributes( const SfxItemSet& rAttr )
+{
+ // call parent
+ Svx3DPreviewControl::Set3DAttributes(rAttr);
+
+ if(maSelectedLight != NO_LIGHT_SELECTED && !GetLightOnOff(maSelectedLight))
+ {
+ // selected light is no more active, select new one
+ maSelectedLight = NO_LIGHT_SELECTED;
+ }
+
+ // local updates
+ ConstructLightObjects();
+ AdaptToSelectedLight();
+ Invalidate();
+}
+
+void Svx3DLightControl::SelectLight(sal_uInt32 nLightNumber)
+{
+ if(nLightNumber > 7)
+ {
+ nLightNumber = NO_LIGHT_SELECTED;
+ }
+
+ if(NO_LIGHT_SELECTED != nLightNumber)
+ {
+ if(!GetLightOnOff(nLightNumber))
+ {
+ nLightNumber = NO_LIGHT_SELECTED;
+ }
+ }
+
+ if(nLightNumber != maSelectedLight)
+ {
+ maSelectedLight = nLightNumber;
+ mbGeometrySelected = false;
+ ConstructLightObjects();
+ AdaptToSelectedLight();
+ Invalidate();
+ }
+}
+
+bool Svx3DLightControl::GetLightOnOff(sal_uInt32 nNum) const
+{
+ if(nNum <= 7)
+ {
+ const SfxItemSet aLightItemSet(Get3DAttributes());
+
+ switch(nNum)
+ {
+ case 0 : return aLightItemSet.Get(SDRATTR_3DSCENE_LIGHTON_1).GetValue();
+ case 1 : return aLightItemSet.Get(SDRATTR_3DSCENE_LIGHTON_2).GetValue();
+ case 2 : return aLightItemSet.Get(SDRATTR_3DSCENE_LIGHTON_3).GetValue();
+ case 3 : return aLightItemSet.Get(SDRATTR_3DSCENE_LIGHTON_4).GetValue();
+ case 4 : return aLightItemSet.Get(SDRATTR_3DSCENE_LIGHTON_5).GetValue();
+ case 5 : return aLightItemSet.Get(SDRATTR_3DSCENE_LIGHTON_6).GetValue();
+ case 6 : return aLightItemSet.Get(SDRATTR_3DSCENE_LIGHTON_7).GetValue();
+ case 7 : return aLightItemSet.Get(SDRATTR_3DSCENE_LIGHTON_8).GetValue();
+ }
+ }
+
+ return false;
+}
+
+Color Svx3DLightControl::GetLightColor(sal_uInt32 nNum) const
+{
+ if(nNum <= 7)
+ {
+ const SfxItemSet aLightItemSet(Get3DAttributes());
+
+ switch(nNum)
+ {
+ case 0 : return aLightItemSet.Get(SDRATTR_3DSCENE_LIGHTCOLOR_1).GetValue();
+ case 1 : return aLightItemSet.Get(SDRATTR_3DSCENE_LIGHTCOLOR_2).GetValue();
+ case 2 : return aLightItemSet.Get(SDRATTR_3DSCENE_LIGHTCOLOR_3).GetValue();
+ case 3 : return aLightItemSet.Get(SDRATTR_3DSCENE_LIGHTCOLOR_4).GetValue();
+ case 4 : return aLightItemSet.Get(SDRATTR_3DSCENE_LIGHTCOLOR_5).GetValue();
+ case 5 : return aLightItemSet.Get(SDRATTR_3DSCENE_LIGHTCOLOR_6).GetValue();
+ case 6 : return aLightItemSet.Get(SDRATTR_3DSCENE_LIGHTCOLOR_7).GetValue();
+ case 7 : return aLightItemSet.Get(SDRATTR_3DSCENE_LIGHTCOLOR_8).GetValue();
+ }
+ }
+
+ return COL_BLACK;
+}
+
+basegfx::B3DVector Svx3DLightControl::GetLightDirection(sal_uInt32 nNum) const
+{
+ if(nNum <= 7)
+ {
+ const SfxItemSet aLightItemSet(Get3DAttributes());
+
+ switch(nNum)
+ {
+ case 0 : return aLightItemSet.Get(SDRATTR_3DSCENE_LIGHTDIRECTION_1).GetValue();
+ case 1 : return aLightItemSet.Get(SDRATTR_3DSCENE_LIGHTDIRECTION_2).GetValue();
+ case 2 : return aLightItemSet.Get(SDRATTR_3DSCENE_LIGHTDIRECTION_3).GetValue();
+ case 3 : return aLightItemSet.Get(SDRATTR_3DSCENE_LIGHTDIRECTION_4).GetValue();
+ case 4 : return aLightItemSet.Get(SDRATTR_3DSCENE_LIGHTDIRECTION_5).GetValue();
+ case 5 : return aLightItemSet.Get(SDRATTR_3DSCENE_LIGHTDIRECTION_6).GetValue();
+ case 6 : return aLightItemSet.Get(SDRATTR_3DSCENE_LIGHTDIRECTION_7).GetValue();
+ case 7 : return aLightItemSet.Get(SDRATTR_3DSCENE_LIGHTDIRECTION_8).GetValue();
+ }
+ }
+
+ return basegfx::B3DVector();
+}
+
+SvxLightCtl3D::SvxLightCtl3D(Svx3DLightControl& rLightControl, weld::Scale& rHori,
+ weld::Scale& rVert, weld::Button& rSwitcher)
+ : mrLightControl(rLightControl)
+ , mrHorScroller(rHori)
+ , mrVerScroller(rVert)
+ , mrSwitcher(rSwitcher)
+{
+ // init members
+ Init();
+}
+
+void SvxLightCtl3D::Init()
+{
+ Size aSize(mrLightControl.GetDrawingArea()->get_ref_device().LogicToPixel(Size(80, 100), MapMode(MapUnit::MapAppFont)));
+ mrLightControl.set_size_request(aSize.Width(), aSize.Height());
+
+ // #i58240# set HelpIDs for scrollbars and switcher
+ mrHorScroller.set_help_id(HID_CTRL3D_HSCROLL);
+ mrVerScroller.set_help_id(HID_CTRL3D_VSCROLL);
+ mrSwitcher.set_help_id(HID_CTRL3D_SWITCHER);
+ mrSwitcher.set_accessible_name(SvxResId(STR_SWITCH));
+
+ // Light preview
+ mrLightControl.Show();
+ mrLightControl.SetChangeCallback( LINK(this, SvxLightCtl3D, InternalInteractiveChange) );
+ mrLightControl.SetSelectionChangeCallback( LINK(this, SvxLightCtl3D, InternalSelectionChange) );
+
+ // Horiz Scrollbar
+ mrHorScroller.show();
+ mrHorScroller.set_range(0, 36000);
+ mrHorScroller.connect_value_changed( LINK(this, SvxLightCtl3D, ScrollBarMove) );
+
+ // Vert Scrollbar
+ mrVerScroller.show();
+ mrVerScroller.set_range(0, 18000);
+ mrVerScroller.connect_value_changed( LINK(this, SvxLightCtl3D, ScrollBarMove) );
+
+ // Switch Button
+ mrSwitcher.show();
+ mrSwitcher.connect_clicked( LINK(this, SvxLightCtl3D, ButtonPress) );
+
+ weld::DrawingArea* pArea = mrLightControl.GetDrawingArea();
+ pArea->connect_key_press(Link<const KeyEvent&, bool>()); //acknowledge we first remove the old one
+ pArea->connect_key_press(LINK(this, SvxLightCtl3D, KeyInput));
+
+ pArea->connect_focus_in(Link<weld::Widget&, void>()); //acknowledge we first remove the old one
+ pArea->connect_focus_in(LINK(this, SvxLightCtl3D, FocusIn));
+
+ // check selection
+ CheckSelection();
+}
+
+void SvxLightCtl3D::CheckSelection()
+{
+ const bool bSelectionValid(mrLightControl.IsSelectionValid() || mrLightControl.IsGeometrySelected());
+ mrHorScroller.set_sensitive(bSelectionValid);
+ mrVerScroller.set_sensitive(bSelectionValid);
+
+ if (bSelectionValid)
+ {
+ double fHor(0.0), fVer(0.0);
+ mrLightControl.GetPosition(fHor, fVer);
+ mrHorScroller.set_value( sal_Int32(fHor * 100.0) );
+ mrVerScroller.set_value( 18000 - sal_Int32((fVer + 90.0) * 100.0) );
+ }
+}
+
+void SvxLightCtl3D::move( double fDeltaHor, double fDeltaVer )
+{
+ double fHor(0.0), fVer(0.0);
+
+ mrLightControl.GetPosition(fHor, fVer);
+ fHor += fDeltaHor;
+ fVer += fDeltaVer;
+
+ if( fVer > 90.0 )
+ return;
+
+ if ( fVer < -90.0 )
+ return;
+
+ mrLightControl.SetPosition(fHor, fVer);
+ mrHorScroller.set_value( sal_Int32(fHor * 100.0) );
+ mrVerScroller.set_value( 18000 - sal_Int32((fVer + 90.0) * 100.0) );
+
+ if(maUserInteractiveChangeCallback.IsSet())
+ {
+ maUserInteractiveChangeCallback.Call(this);
+ }
+}
+
+IMPL_LINK(SvxLightCtl3D, KeyInput, const KeyEvent&, rKEvt, bool)
+{
+ const vcl::KeyCode aCode(rKEvt.GetKeyCode());
+
+ if (aCode.GetModifier())
+ return false;
+
+ bool bHandled = true;
+
+ switch ( aCode.GetCode() )
+ {
+ case KEY_SPACE:
+ {
+ break;
+ }
+ case KEY_LEFT:
+ {
+ move( -4.0, 0.0 ); // #i58242# changed move direction in X
+ break;
+ }
+ case KEY_RIGHT:
+ {
+ move( 4.0, 0.0 ); // #i58242# changed move direction in X
+ break;
+ }
+ case KEY_UP:
+ {
+ move( 0.0, 4.0 );
+ break;
+ }
+ case KEY_DOWN:
+ {
+ move( 0.0, -4.0 );
+ break;
+ }
+ case KEY_PAGEUP:
+ {
+ sal_Int32 nLight(mrLightControl.GetSelectedLight() - 1);
+
+ while((nLight >= 0) && !mrLightControl.GetLightOnOff(nLight))
+ {
+ nLight--;
+ }
+
+ if(nLight < 0)
+ {
+ nLight = 7;
+
+ while((nLight >= 0) && !mrLightControl.GetLightOnOff(nLight))
+ {
+ nLight--;
+ }
+ }
+
+ if(nLight >= 0)
+ {
+ mrLightControl.SelectLight(nLight);
+ CheckSelection();
+
+ if(maUserSelectionChangeCallback.IsSet())
+ {
+ maUserSelectionChangeCallback.Call(this);
+ }
+ }
+
+ break;
+ }
+ case KEY_PAGEDOWN:
+ {
+ sal_Int32 nLight(mrLightControl.GetSelectedLight() - 1);
+
+ while(nLight <= 7 && !mrLightControl.GetLightOnOff(nLight))
+ {
+ nLight++;
+ }
+
+ if(nLight > 7)
+ {
+ nLight = 0;
+
+ while(nLight <= 7 && !mrLightControl.GetLightOnOff(nLight))
+ {
+ nLight++;
+ }
+ }
+
+ if(nLight <= 7)
+ {
+ mrLightControl.SelectLight(nLight);
+ CheckSelection();
+
+ if(maUserSelectionChangeCallback.IsSet())
+ {
+ maUserSelectionChangeCallback.Call(this);
+ }
+ }
+
+ break;
+ }
+ default:
+ {
+ bHandled = false;
+ break;
+ }
+ }
+ return bHandled;
+}
+
+IMPL_LINK_NOARG(SvxLightCtl3D, FocusIn, weld::Widget&, void)
+{
+ if (mrLightControl.IsEnabled())
+ {
+ CheckSelection();
+ }
+}
+
+IMPL_LINK_NOARG(SvxLightCtl3D, ScrollBarMove, weld::Scale&, void)
+{
+ const sal_Int32 nHor(mrHorScroller.get_value());
+ const sal_Int32 nVer(mrVerScroller.get_value());
+
+ mrLightControl.SetPosition(
+ static_cast<double>(nHor) / 100.0,
+ static_cast<double>((18000 - nVer) - 9000) / 100.0);
+
+ if (maUserInteractiveChangeCallback.IsSet())
+ {
+ maUserInteractiveChangeCallback.Call(this);
+ }
+}
+
+IMPL_LINK_NOARG(SvxLightCtl3D, ButtonPress, weld::Button&, void)
+{
+ if(SvxPreviewObjectType::SPHERE == GetSvx3DLightControl().GetObjectType())
+ {
+ GetSvx3DLightControl().SetObjectType(SvxPreviewObjectType::CUBE);
+ }
+ else
+ {
+ GetSvx3DLightControl().SetObjectType(SvxPreviewObjectType::SPHERE);
+ }
+}
+
+IMPL_LINK_NOARG(SvxLightCtl3D, InternalInteractiveChange, Svx3DLightControl*, void)
+{
+ double fHor(0.0), fVer(0.0);
+
+ mrLightControl.GetPosition(fHor, fVer);
+ mrHorScroller.set_value( sal_Int32(fHor * 100.0) );
+ mrVerScroller.set_value( 18000 - sal_Int32((fVer + 90.0) * 100.0) );
+
+ if(maUserInteractiveChangeCallback.IsSet())
+ {
+ maUserInteractiveChangeCallback.Call(this);
+ }
+}
+
+IMPL_LINK_NOARG(SvxLightCtl3D, InternalSelectionChange, Svx3DLightControl*, void)
+{
+ CheckSelection();
+
+ if(maUserSelectionChangeCallback.IsSet())
+ {
+ maUserSelectionChangeCallback.Call(this);
+ }
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/dialog/dlgctrl.cxx b/svx/source/dialog/dlgctrl.cxx
new file mode 100644
index 000000000..35737285a
--- /dev/null
+++ b/svx/source/dialog/dlgctrl.cxx
@@ -0,0 +1,1479 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <config_wasm_strip.h>
+
+#include <vcl/svapp.hxx>
+#include <vcl/settings.hxx>
+#include <vcl/virdev.hxx>
+#include <vcl/event.hxx>
+#include <sfx2/dialoghelper.hxx>
+#include <sfx2/weldutils.hxx>
+#include <svx/relfld.hxx>
+#include <svx/xlineit0.hxx>
+#include <svx/xtable.hxx>
+#include <bitmaps.hlst>
+#include <svx/dlgctrl.hxx>
+#include <tools/debug.hxx>
+#include <svxpixelctlaccessiblecontext.hxx>
+#include <svtools/colorcfg.hxx>
+#include <svxrectctaccessiblecontext.hxx>
+#include <basegfx/point/b2dpoint.hxx>
+#include <basegfx/polygon/b2dpolygon.hxx>
+#include <svx/svdorect.hxx>
+#include <svx/svdmodel.hxx>
+#include <svx/svdopath.hxx>
+#include <sdr/contact/objectcontactofobjlistpainter.hxx>
+#include <svx/sdr/contact/displayinfo.hxx>
+#include <vcl/BitmapTools.hxx>
+
+#define OUTPUT_DRAWMODE_COLOR (DrawModeFlags::Default)
+#define OUTPUT_DRAWMODE_CONTRAST (DrawModeFlags::SettingsLine | DrawModeFlags::SettingsFill | DrawModeFlags::SettingsText | DrawModeFlags::SettingsGradient)
+
+using namespace ::com::sun::star;
+using namespace ::com::sun::star::uno;
+using namespace ::com::sun::star::lang;
+using namespace ::com::sun::star::accessibility;
+
+// Control for display and selection of the corner points and
+// mid point of an object
+
+BitmapEx& SvxRectCtl::GetRectBitmap()
+{
+ if( !pBitmap )
+ InitRectBitmap();
+
+ return *pBitmap;
+}
+
+SvxRectCtl::SvxRectCtl(SvxTabPage* pPage)
+ : m_pPage(pPage)
+ , nBorderWidth(Application::GetDefaultDevice()->LogicToPixel(Size(200, 0), MapMode(MapUnit::Map100thMM)).Width())
+ , eRP(RectPoint::MM)
+ , eDefRP(RectPoint::MM)
+ , m_nState(CTL_STATE::NONE)
+ , mbCompleteDisable(false)
+{
+}
+
+void SvxRectCtl::SetDrawingArea(weld::DrawingArea* pDrawingArea)
+{
+ CustomWidgetController::SetDrawingArea(pDrawingArea);
+ Size aSize(pDrawingArea->get_approximate_digit_width() * 25,
+ pDrawingArea->get_text_height() * 5);
+ pDrawingArea->set_size_request(aSize.Width(), aSize.Height());
+ Resize_Impl(aSize);
+}
+
+void SvxRectCtl::SetControlSettings(RectPoint eRpt, sal_uInt16 nBorder)
+{
+ nBorderWidth = Application::GetDefaultDevice()->LogicToPixel(Size(nBorder, 0), MapMode(MapUnit::Map100thMM)).Width();
+ eDefRP = eRpt;
+ Resize();
+}
+
+SvxRectCtl::~SvxRectCtl()
+{
+ pBitmap.reset();
+#if !ENABLE_WASM_STRIP_ACCESSIBILITY
+ pAccContext.clear();
+#endif
+}
+
+void SvxRectCtl::Resize()
+{
+ Resize_Impl(GetOutputSizePixel());
+}
+
+void SvxRectCtl::Resize_Impl(const Size &rSize)
+{
+ aPtLT = Point( 0 + nBorderWidth, 0 + nBorderWidth );
+ aPtMT = Point( rSize.Width() / 2, 0 + nBorderWidth );
+ aPtRT = Point( rSize.Width() - nBorderWidth, 0 + nBorderWidth );
+
+ aPtLM = Point( 0 + nBorderWidth, rSize.Height() / 2 );
+ aPtMM = Point( rSize.Width() / 2, rSize.Height() / 2 );
+ aPtRM = Point( rSize.Width() - nBorderWidth, rSize.Height() / 2 );
+
+ aPtLB = Point( 0 + nBorderWidth, rSize.Height() - nBorderWidth );
+ aPtMB = Point( rSize.Width() / 2, rSize.Height() - nBorderWidth );
+ aPtRB = Point( rSize.Width() - nBorderWidth, rSize.Height() - nBorderWidth );
+
+ Reset();
+ StyleUpdated();
+}
+
+void SvxRectCtl::InitRectBitmap()
+{
+ pBitmap.reset();
+
+ const StyleSettings& rStyles = Application::GetSettings().GetStyleSettings();
+ svtools::ColorConfig aColorConfig;
+
+ pBitmap.reset(new BitmapEx(RID_SVXCTRL_RECTBTNS));
+
+ // set bitmap-colors
+ Color aColorAry1[7];
+ Color aColorAry2[7];
+ aColorAry1[0] = Color( 0xC0, 0xC0, 0xC0 ); // light-gray
+ aColorAry1[1] = Color( 0xFF, 0xFF, 0x00 ); // yellow
+ aColorAry1[2] = Color( 0xFF, 0xFF, 0xFF ); // white
+ aColorAry1[3] = Color( 0x80, 0x80, 0x80 ); // dark-gray
+ aColorAry1[4] = Color( 0x00, 0x00, 0x00 ); // black
+ aColorAry1[5] = Color( 0x00, 0xFF, 0x00 ); // green
+ aColorAry1[6] = Color( 0x00, 0x00, 0xFF ); // blue
+ aColorAry2[0] = rStyles.GetDialogColor(); // background
+ aColorAry2[1] = rStyles.GetWindowColor();
+ aColorAry2[2] = rStyles.GetLightColor();
+ aColorAry2[3] = rStyles.GetShadowColor();
+ aColorAry2[4] = rStyles.GetDarkShadowColor();
+ aColorAry2[5] = aColorConfig.GetColorValue( svtools::FONTCOLOR ).nColor;
+ aColorAry2[6] = rStyles.GetDialogColor();
+
+#ifdef DBG_UTIL
+ static bool bModify = false;
+ bool& rModify = bModify;
+ if( rModify )
+ {
+ static int n = 0;
+ static sal_uInt8 r = 0xFF;
+ static sal_uInt8 g = 0x00;
+ static sal_uInt8 b = 0xFF;
+ int& rn = n;
+ sal_uInt8& rr = r;
+ sal_uInt8& rg = g;
+ sal_uInt8& rb = b;
+ aColorAry2[ rn ] = Color( rr, rg, rb );
+ }
+#endif
+
+ pBitmap->Replace( aColorAry1, aColorAry2, 7 );
+}
+
+void SvxRectCtl::StyleUpdated()
+{
+ pBitmap.reset(); // forces new creating of bitmap
+ CustomWidgetController::StyleUpdated();
+}
+
+void SvxRectCtl::InitSettings(vcl::RenderContext& rRenderContext)
+{
+ svtools::ColorConfig aColorConfig;
+ Color aTextColor(aColorConfig.GetColorValue(svtools::FONTCOLOR).nColor);
+ rRenderContext.SetTextColor(aTextColor);
+ const StyleSettings& rStyleSettings = Application::GetSettings().GetStyleSettings();
+ rRenderContext.SetBackground(rStyleSettings.GetWindowColor());
+}
+
+// The clicked rectangle (3 x 3) is determined and the parent (dialog)
+// is notified that the item was changed
+bool SvxRectCtl::MouseButtonDown(const MouseEvent& rMEvt)
+{
+ // CompletelyDisabled() added to have a disabled state for SvxRectCtl
+ if(!IsCompletelyDisabled())
+ {
+ aPtNew = GetApproxLogPtFromPixPt( rMEvt.GetPosPixel() );
+ eRP = GetRPFromPoint( aPtNew );
+ SetActualRP( eRP );
+
+ if (m_pPage)
+ m_pPage->PointChanged(GetDrawingArea(), eRP);
+ }
+ return true;
+}
+
+bool SvxRectCtl::KeyInput(const KeyEvent& rKeyEvt)
+{
+ // CompletelyDisabled() added to have a disabled state for SvxRectCtl
+ if (IsCompletelyDisabled())
+ return false;
+
+ RectPoint eNewRP = eRP;
+
+ switch( rKeyEvt.GetKeyCode().GetCode() )
+ {
+ case KEY_DOWN:
+ {
+ if( !(m_nState & CTL_STATE::NOVERT) )
+ switch( eNewRP )
+ {
+ case RectPoint::LT: eNewRP = RectPoint::LM; break;
+ case RectPoint::MT: eNewRP = RectPoint::MM; break;
+ case RectPoint::RT: eNewRP = RectPoint::RM; break;
+ case RectPoint::LM: eNewRP = RectPoint::LB; break;
+ case RectPoint::MM: eNewRP = RectPoint::MB; break;
+ case RectPoint::RM: eNewRP = RectPoint::RB; break;
+ default: ; //prevent warning
+ }
+ }
+ break;
+ case KEY_UP:
+ {
+ if( !(m_nState & CTL_STATE::NOVERT) )
+ switch( eNewRP )
+ {
+ case RectPoint::LM: eNewRP = RectPoint::LT; break;
+ case RectPoint::MM: eNewRP = RectPoint::MT; break;
+ case RectPoint::RM: eNewRP = RectPoint::RT; break;
+ case RectPoint::LB: eNewRP = RectPoint::LM; break;
+ case RectPoint::MB: eNewRP = RectPoint::MM; break;
+ case RectPoint::RB: eNewRP = RectPoint::RM; break;
+ default: ; //prevent warning
+ }
+ }
+ break;
+ case KEY_LEFT:
+ {
+ if( !(m_nState & CTL_STATE::NOHORZ) )
+ switch( eNewRP )
+ {
+ case RectPoint::MT: eNewRP = RectPoint::LT; break;
+ case RectPoint::RT: eNewRP = RectPoint::MT; break;
+ case RectPoint::MM: eNewRP = RectPoint::LM; break;
+ case RectPoint::RM: eNewRP = RectPoint::MM; break;
+ case RectPoint::MB: eNewRP = RectPoint::LB; break;
+ case RectPoint::RB: eNewRP = RectPoint::MB; break;
+ default: ; //prevent warning
+ }
+ }
+ break;
+ case KEY_RIGHT:
+ {
+ if( !(m_nState & CTL_STATE::NOHORZ) )
+ switch( eNewRP )
+ {
+ case RectPoint::LT: eNewRP = RectPoint::MT; break;
+ case RectPoint::MT: eNewRP = RectPoint::RT; break;
+ case RectPoint::LM: eNewRP = RectPoint::MM; break;
+ case RectPoint::MM: eNewRP = RectPoint::RM; break;
+ case RectPoint::LB: eNewRP = RectPoint::MB; break;
+ case RectPoint::MB: eNewRP = RectPoint::RB; break;
+ default: ; //prevent warning
+ }
+ }
+ break;
+ default:
+ return false;
+ }
+ if( eNewRP != eRP )
+ {
+ SetActualRP( eNewRP );
+
+ if (m_pPage)
+ m_pPage->PointChanged(GetDrawingArea(), eRP);
+ }
+ return true;
+}
+
+// the control (rectangle with 9 circles)
+void SvxRectCtl::Paint(vcl::RenderContext& rRenderContext, const tools::Rectangle&)
+{
+ InitSettings(rRenderContext);
+
+ Point aPtDiff(1, 1);
+
+ const StyleSettings& rStyles = Application::GetSettings().GetStyleSettings();
+
+ rRenderContext.SetLineColor(rStyles.GetDialogColor());
+ rRenderContext.SetFillColor(rStyles.GetDialogColor());
+ rRenderContext.DrawRect(tools::Rectangle(Point(0,0), rRenderContext.GetOutputSize()));
+
+ if (IsEnabled())
+ rRenderContext.SetLineColor(rStyles.GetLabelTextColor());
+ else
+ rRenderContext.SetLineColor(rStyles.GetShadowColor());
+
+ rRenderContext.SetFillColor();
+
+ if (!IsEnabled())
+ {
+ Color aOldCol = rRenderContext.GetLineColor();
+ rRenderContext.SetLineColor(rStyles.GetLightColor());
+ rRenderContext.DrawRect(tools::Rectangle(aPtLT + aPtDiff, aPtRB + aPtDiff));
+ rRenderContext.SetLineColor(aOldCol);
+ }
+ rRenderContext.DrawRect(tools::Rectangle(aPtLT, aPtRB));
+
+ rRenderContext.SetFillColor(rRenderContext.GetBackground().GetColor());
+
+ Size aBtnSize(11, 11);
+ Size aDstBtnSize(aBtnSize);
+ Point aToCenter(aDstBtnSize.Width() >> 1, aDstBtnSize.Height() >> 1);
+ Point aBtnPnt1(IsEnabled() ? 0 : 22, 0);
+ Point aBtnPnt2(11, 0);
+ Point aBtnPnt3(22, 0);
+
+ bool bNoHorz = bool(m_nState & CTL_STATE::NOHORZ);
+ bool bNoVert = bool(m_nState & CTL_STATE::NOVERT);
+
+ BitmapEx& rBitmap = GetRectBitmap();
+
+ // CompletelyDisabled() added to have a disabled state for SvxRectCtl
+ if (IsCompletelyDisabled())
+ {
+ rRenderContext.DrawBitmapEx(aPtLT - aToCenter, aDstBtnSize, aBtnPnt3, aBtnSize, rBitmap);
+ rRenderContext.DrawBitmapEx(aPtMT - aToCenter, aDstBtnSize, aBtnPnt3, aBtnSize, rBitmap);
+ rRenderContext.DrawBitmapEx(aPtRT - aToCenter, aDstBtnSize, aBtnPnt3, aBtnSize, rBitmap);
+ rRenderContext.DrawBitmapEx(aPtLM - aToCenter, aDstBtnSize, aBtnPnt3, aBtnSize, rBitmap);
+ rRenderContext.DrawBitmapEx(aPtMM - aToCenter, aDstBtnSize, aBtnPnt3, aBtnSize, rBitmap);
+ rRenderContext.DrawBitmapEx(aPtRM - aToCenter, aDstBtnSize, aBtnPnt3, aBtnSize, rBitmap);
+ rRenderContext.DrawBitmapEx(aPtLB - aToCenter, aDstBtnSize, aBtnPnt3, aBtnSize, rBitmap);
+ rRenderContext.DrawBitmapEx(aPtMB - aToCenter, aDstBtnSize, aBtnPnt3, aBtnSize, rBitmap);
+ rRenderContext.DrawBitmapEx(aPtRB - aToCenter, aDstBtnSize, aBtnPnt3, aBtnSize, rBitmap);
+ }
+ else
+ {
+ rRenderContext.DrawBitmapEx(aPtLT - aToCenter, aDstBtnSize, (bNoHorz || bNoVert)?aBtnPnt3:aBtnPnt1, aBtnSize, rBitmap);
+ rRenderContext.DrawBitmapEx(aPtMT - aToCenter, aDstBtnSize, bNoVert?aBtnPnt3:aBtnPnt1, aBtnSize, rBitmap);
+ rRenderContext.DrawBitmapEx(aPtRT - aToCenter, aDstBtnSize, (bNoHorz || bNoVert)?aBtnPnt3:aBtnPnt1, aBtnSize, rBitmap);
+ rRenderContext.DrawBitmapEx(aPtLM - aToCenter, aDstBtnSize, bNoHorz?aBtnPnt3:aBtnPnt1, aBtnSize, rBitmap);
+
+ // Center for rectangle and line
+ rRenderContext.DrawBitmapEx(aPtMM - aToCenter, aDstBtnSize, aBtnPnt1, aBtnSize, rBitmap);
+
+ rRenderContext.DrawBitmapEx(aPtRM - aToCenter, aDstBtnSize, bNoHorz?aBtnPnt3:aBtnPnt1, aBtnSize, rBitmap);
+ rRenderContext.DrawBitmapEx(aPtLB - aToCenter, aDstBtnSize, (bNoHorz || bNoVert)?aBtnPnt3:aBtnPnt1, aBtnSize, rBitmap);
+ rRenderContext.DrawBitmapEx(aPtMB - aToCenter, aDstBtnSize, bNoVert?aBtnPnt3:aBtnPnt1, aBtnSize, rBitmap);
+ rRenderContext.DrawBitmapEx(aPtRB - aToCenter, aDstBtnSize, (bNoHorz || bNoVert)?aBtnPnt3:aBtnPnt1, aBtnSize, rBitmap);
+ }
+
+ // draw active button, avoid center pos for angle
+ // CompletelyDisabled() added to have a disabled state for SvxRectCtl
+ if (!IsCompletelyDisabled())
+ {
+ if (IsEnabled())
+ {
+ Point aCenterPt(aPtNew);
+ aCenterPt -= aToCenter;
+
+ rRenderContext.DrawBitmapEx(aCenterPt, aDstBtnSize, aBtnPnt2, aBtnSize, rBitmap);
+ }
+ }
+}
+
+tools::Rectangle SvxRectCtl::GetFocusRect()
+{
+ tools::Rectangle aRet;
+ if (HasFocus())
+ aRet = CalculateFocusRectangle();
+ return aRet;
+}
+
+// Convert RectPoint Point
+
+const Point& SvxRectCtl::GetPointFromRP( RectPoint _eRP) const
+{
+ switch( _eRP )
+ {
+ case RectPoint::LT: return aPtLT;
+ case RectPoint::MT: return aPtMT;
+ case RectPoint::RT: return aPtRT;
+ case RectPoint::LM: return aPtLM;
+ case RectPoint::MM: return aPtMM;
+ case RectPoint::RM: return aPtRM;
+ case RectPoint::LB: return aPtLB;
+ case RectPoint::MB: return aPtMB;
+ case RectPoint::RB: return aPtRB;
+ }
+ return aPtMM; // default
+}
+
+Point SvxRectCtl::SetActualRPWithoutInvalidate( RectPoint eNewRP )
+{
+ Point aPtLast = aPtNew;
+ aPtNew = GetPointFromRP( eNewRP );
+
+ if( m_nState & CTL_STATE::NOHORZ )
+ aPtNew.setX( aPtMM.X() );
+
+ if( m_nState & CTL_STATE::NOVERT )
+ aPtNew.setY( aPtMM.Y() );
+
+ // fdo#74751 this fix reverse base point on RTL UI.
+ bool bRTL = AllSettings::GetLayoutRTL();
+ eNewRP = GetRPFromPoint( aPtNew, bRTL );
+
+ eDefRP = eNewRP;
+ eRP = eNewRP;
+
+ return aPtLast;
+}
+
+void SvxRectCtl::GetFocus()
+{
+ Invalidate();
+
+#if !ENABLE_WASM_STRIP_ACCESSIBILITY
+ // Send accessibility event.
+ if (pAccContext.is())
+ {
+ pAccContext->FireChildFocus(GetActualRP());
+ }
+#endif
+}
+
+void SvxRectCtl::LoseFocus()
+{
+ Invalidate();
+}
+
+Point SvxRectCtl::GetApproxLogPtFromPixPt( const Point& rPt ) const
+{
+ Point aPt = rPt;
+ tools::Long x;
+ tools::Long y;
+
+ Size aSize(GetOutputSizePixel());
+
+ if( !( m_nState & CTL_STATE::NOHORZ ) )
+ {
+ if( aPt.X() < aSize.Width() / 3 )
+ x = aPtLT.X();
+ else if( aPt.X() < aSize.Width() * 2 / 3 )
+ x = aPtMM.X();
+ else
+ x = aPtRB.X();
+ }
+ else
+ x = aPtMM.X();
+
+ if( !( m_nState & CTL_STATE::NOVERT ) )
+ {
+ if( aPt.Y() < aSize.Height() / 3 )
+ y = aPtLT.Y();
+ else if( aPt.Y() < aSize.Height() * 2 / 3 )
+ y = aPtMM.Y();
+ else
+ y = aPtRB.Y();
+ }
+ else
+ y = aPtMM.Y();
+
+ return Point( x, y );
+}
+
+
+// Converts Point in RectPoint
+
+RectPoint SvxRectCtl::GetRPFromPoint( Point aPt, bool bRTL ) const
+{
+ RectPoint rPoint = RectPoint::MM; // default
+
+ if (aPt == aPtLT) rPoint = bRTL ? RectPoint::RT : RectPoint::LT;
+ else if( aPt == aPtMT) rPoint = RectPoint::MT;
+ else if( aPt == aPtRT) rPoint = bRTL ? RectPoint::LT : RectPoint::RT;
+ else if( aPt == aPtLM) rPoint = bRTL ? RectPoint::RM : RectPoint::LM;
+ else if( aPt == aPtRM) rPoint = bRTL ? RectPoint::LM : RectPoint::RM;
+ else if( aPt == aPtLB) rPoint = bRTL ? RectPoint::RB : RectPoint::LB;
+ else if( aPt == aPtMB) rPoint = RectPoint::MB;
+ else if( aPt == aPtRB) rPoint = bRTL ? RectPoint::LB : RectPoint::RB;
+
+ return rPoint;
+}
+
+// Resets to the original state of the control
+
+void SvxRectCtl::Reset()
+{
+ aPtNew = GetPointFromRP( eDefRP );
+ eRP = eDefRP;
+ Invalidate();
+}
+
+// Returns the currently selected RectPoint
+
+
+void SvxRectCtl::SetActualRP( RectPoint eNewRP )
+{
+ SetActualRPWithoutInvalidate(eNewRP);
+
+ Invalidate();
+
+#if !ENABLE_WASM_STRIP_ACCESSIBILITY
+ // notify accessibility object about change
+ if (pAccContext.is())
+ pAccContext->selectChild( eNewRP /* MT, bFireFocus */ );
+#endif
+}
+
+void SvxRectCtl::SetState( CTL_STATE nState )
+{
+ m_nState = nState;
+
+ Point aPtLast( GetPointFromRP( eRP ) );
+ Point _aPtNew( aPtLast );
+
+ if( m_nState & CTL_STATE::NOHORZ )
+ _aPtNew.setX( aPtMM.X() );
+
+ if( m_nState & CTL_STATE::NOVERT)
+ _aPtNew.setY( aPtMM.Y() );
+
+ eRP = GetRPFromPoint( _aPtNew );
+ Invalidate();
+
+ if (m_pPage)
+ m_pPage->PointChanged(GetDrawingArea(), eRP);
+}
+
+tools::Rectangle SvxRectCtl::CalculateFocusRectangle() const
+{
+ Size aDstBtnSize(15, 15);
+ return tools::Rectangle( aPtNew - Point( aDstBtnSize.Width() >> 1, aDstBtnSize.Height() >> 1 ), aDstBtnSize );
+}
+
+tools::Rectangle SvxRectCtl::CalculateFocusRectangle( RectPoint eRectPoint ) const
+{
+ tools::Rectangle aRet;
+ RectPoint eOldRectPoint = GetActualRP();
+
+ if( eOldRectPoint == eRectPoint )
+ aRet = CalculateFocusRectangle();
+ else
+ {
+ SvxRectCtl* pThis = const_cast<SvxRectCtl*>(this);
+
+ pThis->SetActualRPWithoutInvalidate( eRectPoint ); // no invalidation because it's only temporary!
+ aRet = CalculateFocusRectangle();
+
+ pThis->SetActualRPWithoutInvalidate( eOldRectPoint ); // no invalidation because nothing has changed!
+ }
+
+ return aRet;
+}
+
+Reference< XAccessible > SvxRectCtl::CreateAccessible()
+{
+#if !ENABLE_WASM_STRIP_ACCESSIBILITY
+ pAccContext = new SvxRectCtlAccessibleContext(this);
+#endif
+ return pAccContext;
+}
+
+RectPoint SvxRectCtl::GetApproxRPFromPixPt( const css::awt::Point& r ) const
+{
+ return GetRPFromPoint( GetApproxLogPtFromPixPt( Point( r.X, r.Y ) ) );
+}
+
+// CompletelyDisabled() added to have a disabled state for SvxRectCtl
+void SvxRectCtl::DoCompletelyDisable(bool bNew)
+{
+ mbCompleteDisable = bNew;
+ Invalidate();
+}
+
+// Control for editing bitmaps
+
+css::uno::Reference< css::accessibility::XAccessible > SvxPixelCtl::CreateAccessible()
+{
+#if !ENABLE_WASM_STRIP_ACCESSIBILITY
+ if (!m_xAccess.is())
+ m_xAccess = new SvxPixelCtlAccessible(this);
+#endif
+ return m_xAccess;
+}
+
+tools::Long SvxPixelCtl::PointToIndex(const Point &aPt) const
+{
+ tools::Long nX = aPt.X() * nLines / aRectSize.Width();
+ tools::Long nY = aPt.Y() * nLines / aRectSize.Height();
+
+ return nX + nY * nLines ;
+}
+
+Point SvxPixelCtl::IndexToPoint(tools::Long nIndex) const
+{
+ DBG_ASSERT(nIndex >= 0 && nIndex < nSquares ," Check Index");
+
+ sal_Int32 nXIndex = nIndex % nLines;
+ sal_Int32 nYIndex = nIndex / nLines;
+
+ Point aPtTl;
+ aPtTl.setY( aRectSize.Height() * nYIndex / nLines + 1 );
+ aPtTl.setX( aRectSize.Width() * nXIndex / nLines + 1 );
+
+ return aPtTl;
+}
+
+tools::Long SvxPixelCtl::GetFocusPosIndex() const
+{
+ return aFocusPosition.getX() + aFocusPosition.getY() * nLines ;
+}
+
+tools::Long SvxPixelCtl::ShowPosition( const Point &rPt)
+{
+ sal_Int32 nX = rPt.X() * nLines / aRectSize.Width();
+ sal_Int32 nY = rPt.Y() * nLines / aRectSize.Height();
+
+ ChangePixel( nX + nY * nLines );
+
+ //Solution:Set new focus position and repaint
+ aFocusPosition.setX(nX);
+ aFocusPosition.setY(nY);
+ Invalidate(tools::Rectangle(Point(0,0),aRectSize));
+
+ if (m_pPage)
+ m_pPage->PointChanged(GetDrawingArea(), RectPoint::MM ); // RectPoint is dummy
+
+ return GetFocusPosIndex();
+
+}
+
+SvxPixelCtl::SvxPixelCtl(SvxTabPage* pPage)
+ : m_pPage(pPage)
+ , bPaintable(true)
+ , aFocusPosition(0,0)
+{
+ maPixelData.fill(0);
+}
+
+void SvxPixelCtl::Resize()
+{
+ CustomWidgetController::Resize();
+ aRectSize = GetOutputSizePixel();
+}
+
+void SvxPixelCtl::SetDrawingArea(weld::DrawingArea* pDrawingArea)
+{
+ CustomWidgetController::SetDrawingArea(pDrawingArea);
+ pDrawingArea->set_size_request(pDrawingArea->get_approximate_digit_width() * 25,
+ pDrawingArea->get_text_height() * 10);
+}
+
+SvxPixelCtl::~SvxPixelCtl()
+{
+}
+
+// Changes the foreground or Background color
+
+void SvxPixelCtl::ChangePixel( sal_uInt16 nPixel )
+{
+ if( maPixelData[nPixel] == 0 )
+ maPixelData[nPixel] = 1; // could be extended to more colors
+ else
+ maPixelData[nPixel] = 0;
+}
+
+// The clicked rectangle is identified, to change its color
+
+bool SvxPixelCtl::MouseButtonDown( const MouseEvent& rMEvt )
+{
+ if (!aRectSize.Width() || !aRectSize.Height())
+ return true;
+
+ //Grab focus when click in window
+ if (!HasFocus())
+ {
+ GrabFocus();
+ }
+
+ tools::Long nIndex = ShowPosition(rMEvt.GetPosPixel());
+
+#if !ENABLE_WASM_STRIP_ACCESSIBILITY
+ if(m_xAccess.is())
+ {
+ m_xAccess->NotifyChild(nIndex,true, true);
+ }
+#else
+ (void)nIndex;
+#endif
+
+ return true;
+}
+
+tools::Rectangle SvxPixelCtl::GetFocusRect()
+{
+ tools::Rectangle aRet;
+ //Draw visual focus when has focus
+ if (HasFocus())
+ aRet = implCalFocusRect(aFocusPosition);
+ return aRet;
+}
+
+// Draws the Control (Rectangle with nine circles)
+void SvxPixelCtl::Paint( vcl::RenderContext& rRenderContext, const tools::Rectangle& )
+{
+ if (!aRectSize.Width() || !aRectSize.Height())
+ return;
+
+ sal_uInt16 i, j, nTmp;
+ Point aPtTl, aPtBr;
+
+ if (bPaintable)
+ {
+ // Draw lines
+ rRenderContext.SetLineColor(Color());
+ for (i = 1; i < nLines; i++)
+ {
+ // horizontal
+ nTmp = static_cast<sal_uInt16>(aRectSize.Height() * i / nLines);
+ rRenderContext.DrawLine(Point(0, nTmp), Point(aRectSize.Width(), nTmp));
+ // vertically
+ nTmp = static_cast<sal_uInt16>( aRectSize.Width() * i / nLines );
+ rRenderContext.DrawLine(Point(nTmp, 0), Point(nTmp, aRectSize.Height()));
+ }
+
+ //Draw Rectangles (squares)
+ rRenderContext.SetLineColor();
+ sal_uInt16 nLastPixel = maPixelData[0] ? 0 : 1;
+
+ for (i = 0; i < nLines; i++)
+ {
+ aPtTl.setY( aRectSize.Height() * i / nLines + 1 );
+ aPtBr.setY( aRectSize.Height() * (i + 1) / nLines - 1 );
+
+ for (j = 0; j < nLines; j++)
+ {
+ aPtTl.setX( aRectSize.Width() * j / nLines + 1 );
+ aPtBr.setX( aRectSize.Width() * (j + 1) / nLines - 1 );
+
+ if (maPixelData[i * nLines + j] != nLastPixel)
+ {
+ nLastPixel = maPixelData[i * nLines + j];
+ // Change color: 0 -> Background color
+ rRenderContext.SetFillColor(nLastPixel ? aPixelColor : aBackgroundColor);
+ }
+ rRenderContext.DrawRect(tools::Rectangle(aPtTl, aPtBr));
+ }
+ }
+ }
+ else
+ {
+ rRenderContext.SetBackground(Wallpaper(COL_LIGHTGRAY));
+ rRenderContext.SetLineColor(COL_LIGHTRED);
+ rRenderContext.DrawLine(Point(0, 0), Point(aRectSize.Width(), aRectSize.Height()));
+ rRenderContext.DrawLine(Point(0, aRectSize.Height()), Point(aRectSize.Width(), 0));
+ }
+}
+
+//Calculate visual focus rectangle via focus position
+tools::Rectangle SvxPixelCtl::implCalFocusRect( const Point& aPosition )
+{
+ tools::Long nLeft,nTop,nRight,nBottom;
+ tools::Long i,j;
+ i = aPosition.Y();
+ j = aPosition.X();
+ nLeft = aRectSize.Width() * j / nLines + 1;
+ nRight = aRectSize.Width() * (j + 1) / nLines - 1;
+ nTop = aRectSize.Height() * i / nLines + 1;
+ nBottom = aRectSize.Height() * (i + 1) / nLines - 1;
+ return tools::Rectangle(nLeft,nTop,nRight,nBottom);
+}
+
+//Solution:Keyboard function
+bool SvxPixelCtl::KeyInput( const KeyEvent& rKEvt )
+{
+ vcl::KeyCode aKeyCode = rKEvt.GetKeyCode();
+ sal_uInt16 nCode = aKeyCode.GetCode();
+ bool bIsMod = aKeyCode.IsShift() || aKeyCode.IsMod1() || aKeyCode.IsMod2();
+
+ if( !bIsMod )
+ {
+ Point aRepaintPoint( aRectSize.Width() *( aFocusPosition.getX() - 1)/ nLines - 1,
+ aRectSize.Height() *( aFocusPosition.getY() - 1)/ nLines -1
+ );
+ Size aRepaintSize( aRectSize.Width() *3/ nLines + 2,aRectSize.Height() *3/ nLines + 2);
+ tools::Rectangle aRepaintRect( aRepaintPoint, aRepaintSize );
+ bool bFocusPosChanged=false;
+ switch(nCode)
+ {
+ case KEY_LEFT:
+ if(aFocusPosition.getX() >= 1)
+ {
+ aFocusPosition.setX( aFocusPosition.getX() - 1 );
+ Invalidate(aRepaintRect);
+ bFocusPosChanged=true;
+ }
+ break;
+ case KEY_RIGHT:
+ if( aFocusPosition.getX() < (nLines - 1) )
+ {
+ aFocusPosition.setX( aFocusPosition.getX() + 1 );
+ Invalidate(aRepaintRect);
+ bFocusPosChanged=true;
+ }
+ break;
+ case KEY_UP:
+ if(aFocusPosition.getY() >= 1)
+ {
+ aFocusPosition.setY( aFocusPosition.getY() - 1 );
+ Invalidate(aRepaintRect);
+ bFocusPosChanged=true;
+ }
+ break;
+ case KEY_DOWN:
+ if( aFocusPosition.getY() < ( nLines - 1 ) )
+ {
+ aFocusPosition.setY( aFocusPosition.getY() + 1 );
+ Invalidate(aRepaintRect);
+ bFocusPosChanged=true;
+ }
+ break;
+ case KEY_SPACE:
+ ChangePixel( sal_uInt16(aFocusPosition.getX() + aFocusPosition.getY() * nLines) );
+ Invalidate( implCalFocusRect(aFocusPosition) );
+ break;
+ default:
+ return CustomWidgetController::KeyInput( rKEvt );
+ }
+#if !ENABLE_WASM_STRIP_ACCESSIBILITY
+ if(m_xAccess.is())
+ {
+ tools::Long nIndex = GetFocusPosIndex();
+ switch(nCode)
+ {
+ case KEY_LEFT:
+ case KEY_RIGHT:
+ case KEY_UP:
+ case KEY_DOWN:
+ if (bFocusPosChanged)
+ {
+ m_xAccess->NotifyChild(nIndex,false,false);
+ }
+ break;
+ case KEY_SPACE:
+ m_xAccess->NotifyChild(nIndex,false,true);
+ break;
+ default:
+ break;
+ }
+ }
+#else
+ (void)bFocusPosChanged;
+#endif
+ return true;
+ }
+ else
+ {
+ return CustomWidgetController::KeyInput( rKEvt );
+ }
+}
+
+//Draw focus when get focus
+void SvxPixelCtl::GetFocus()
+{
+ Invalidate(implCalFocusRect(aFocusPosition));
+
+#if !ENABLE_WASM_STRIP_ACCESSIBILITY
+ if (m_xAccess.is())
+ {
+ m_xAccess->NotifyChild(GetFocusPosIndex(),true,false);
+ }
+#endif
+}
+
+void SvxPixelCtl::LoseFocus()
+{
+ Invalidate();
+}
+
+void SvxPixelCtl::SetXBitmap(const BitmapEx& rBitmapEx)
+{
+ if (vcl::bitmap::isHistorical8x8(rBitmapEx, aBackgroundColor, aPixelColor))
+ {
+ for (sal_uInt16 i = 0; i < nSquares; i++)
+ {
+ Color aColor = rBitmapEx.GetPixelColor(i%8, i/8);
+ maPixelData[i] = (aColor == aBackgroundColor) ? 0 : 1;
+ }
+ }
+}
+
+// Returns a specific pixel
+
+sal_uInt8 SvxPixelCtl::GetBitmapPixel( const sal_uInt16 nPixel ) const
+{
+ return maPixelData[nPixel];
+}
+
+// Resets to the original state of the control
+
+void SvxPixelCtl::Reset()
+{
+ // clear pixel area
+ maPixelData.fill(0);
+ Invalidate();
+}
+
+SvxLineLB::SvxLineLB(std::unique_ptr<weld::ComboBox> pControl)
+ : m_xControl(std::move(pControl))
+ , mbAddStandardFields(true)
+{
+}
+
+void SvxLineLB::setAddStandardFields(bool bNew)
+{
+ if(getAddStandardFields() != bNew)
+ {
+ mbAddStandardFields = bNew;
+ }
+}
+
+// Fills the listbox (provisional) with strings
+
+void SvxLineLB::Fill( const XDashListRef &pList )
+{
+ m_xControl->clear();
+
+ if( !pList.is() )
+ return;
+
+ ScopedVclPtrInstance< VirtualDevice > pVD;
+
+ if(getAddStandardFields())
+ {
+ // entry for 'none'
+ m_xControl->append_text(pList->GetStringForUiNoLine());
+
+ // entry for solid line
+ const BitmapEx aBitmap = pList->GetBitmapForUISolidLine();
+ const Size aBmpSize(aBitmap.GetSizePixel());
+ pVD->SetOutputSizePixel(aBmpSize, false);
+ pVD->DrawBitmapEx(Point(), aBitmap);
+ m_xControl->append("", pList->GetStringForUiSolidLine(), *pVD);
+ }
+
+ // entries for dashed lines
+
+ tools::Long nCount = pList->Count();
+ m_xControl->freeze();
+
+ for( tools::Long i = 0; i < nCount; i++ )
+ {
+ const XDashEntry* pEntry = pList->GetDash(i);
+ const BitmapEx aBitmap = pList->GetUiBitmap( i );
+ if( !aBitmap.IsEmpty() )
+ {
+ const Size aBmpSize(aBitmap.GetSizePixel());
+ pVD->SetOutputSizePixel(aBmpSize, false);
+ pVD->DrawBitmapEx(Point(), aBitmap);
+ m_xControl->append("", pEntry->GetName(), *pVD);
+ }
+ else
+ {
+ m_xControl->append_text(pEntry->GetName());
+ }
+ }
+
+ m_xControl->thaw();
+}
+
+void SvxLineLB::Append( const XDashEntry& rEntry, const BitmapEx& rBitmap )
+{
+ if (!rBitmap.IsEmpty())
+ {
+ ScopedVclPtrInstance< VirtualDevice > pVD;
+
+ const Size aBmpSize(rBitmap.GetSizePixel());
+ pVD->SetOutputSizePixel(aBmpSize, false);
+ pVD->DrawBitmapEx(Point(), rBitmap);
+ m_xControl->append("", rEntry.GetName(), *pVD);
+ }
+ else
+ {
+ m_xControl->append_text(rEntry.GetName());
+ }
+}
+
+void SvxLineLB::Modify(const XDashEntry& rEntry, sal_Int32 nPos, const BitmapEx& rBitmap)
+{
+ m_xControl->remove(nPos);
+
+ if (!rBitmap.IsEmpty())
+ {
+ ScopedVclPtrInstance< VirtualDevice > pVD;
+
+ const Size aBmpSize(rBitmap.GetSizePixel());
+ pVD->SetOutputSizePixel(aBmpSize, false);
+ pVD->DrawBitmapEx(Point(), rBitmap);
+ m_xControl->insert(nPos, rEntry.GetName(), nullptr, nullptr, pVD);
+ }
+ else
+ {
+ m_xControl->insert_text(nPos, rEntry.GetName());
+ }
+}
+
+SvxLineEndLB::SvxLineEndLB(std::unique_ptr<weld::ComboBox> pControl)
+ : m_xControl(std::move(pControl))
+{
+}
+
+void SvxLineEndLB::Fill( const XLineEndListRef &pList, bool bStart )
+{
+ if( !pList.is() )
+ return;
+
+ tools::Long nCount = pList->Count();
+ ScopedVclPtrInstance< VirtualDevice > pVD;
+ m_xControl->freeze();
+
+ for( tools::Long i = 0; i < nCount; i++ )
+ {
+ const XLineEndEntry* pEntry = pList->GetLineEnd(i);
+ const BitmapEx aBitmap = pList->GetUiBitmap( i );
+ if( !aBitmap.IsEmpty() )
+ {
+ const Size aBmpSize(aBitmap.GetSizePixel());
+ pVD->SetOutputSizePixel(Size(aBmpSize.Width() / 2, aBmpSize.Height()), false);
+ pVD->DrawBitmapEx(bStart ? Point() : Point(-aBmpSize.Width() / 2, 0), aBitmap);
+ m_xControl->append("", pEntry->GetName(), *pVD);
+ }
+ else
+ m_xControl->append_text(pEntry->GetName());
+ }
+
+ m_xControl->thaw();
+}
+
+void SvxLineEndLB::Append( const XLineEndEntry& rEntry, const BitmapEx& rBitmap )
+{
+ if(!rBitmap.IsEmpty())
+ {
+ ScopedVclPtrInstance< VirtualDevice > pVD;
+
+ const Size aBmpSize(rBitmap.GetSizePixel());
+ pVD->SetOutputSizePixel(Size(aBmpSize.Width() / 2, aBmpSize.Height()), false);
+ pVD->DrawBitmapEx(Point(-aBmpSize.Width() / 2, 0), rBitmap);
+ m_xControl->append("", rEntry.GetName(), *pVD);
+ }
+ else
+ {
+ m_xControl->append_text(rEntry.GetName());
+ }
+}
+
+void SvxLineEndLB::Modify( const XLineEndEntry& rEntry, sal_Int32 nPos, const BitmapEx& rBitmap )
+{
+ m_xControl->remove(nPos);
+
+ if(!rBitmap.IsEmpty())
+ {
+ ScopedVclPtrInstance< VirtualDevice > pVD;
+
+ const Size aBmpSize(rBitmap.GetSizePixel());
+ pVD->SetOutputSizePixel(Size(aBmpSize.Width() / 2, aBmpSize.Height()), false);
+ pVD->DrawBitmapEx(Point(-aBmpSize.Width() / 2, 0), rBitmap);
+ m_xControl->insert(nPos, rEntry.GetName(), nullptr, nullptr, pVD);
+ }
+ else
+ {
+ m_xControl->insert_text(nPos, rEntry.GetName());
+ }
+}
+
+void SvxXLinePreview::Resize()
+{
+ SvxPreviewBase::Resize();
+
+ const Size aOutputSize(GetOutputSize());
+ const sal_Int32 nDistance(500);
+ const sal_Int32 nAvailableLength(aOutputSize.Width() - (4 * nDistance));
+
+ // create DrawObjectA
+ const sal_Int32 aYPosA(aOutputSize.Height() / 2);
+ const basegfx::B2DPoint aPointA1( nDistance, aYPosA);
+ const basegfx::B2DPoint aPointA2( aPointA1.getX() + ((nAvailableLength * 14) / 20), aYPosA );
+ basegfx::B2DPolygon aPolygonA;
+ aPolygonA.append(aPointA1);
+ aPolygonA.append(aPointA2);
+ mpLineObjA->SetPathPoly(basegfx::B2DPolyPolygon(aPolygonA));
+
+ // create DrawObjectB
+ const sal_Int32 aYPosB1((aOutputSize.Height() * 3) / 4);
+ const sal_Int32 aYPosB2((aOutputSize.Height() * 1) / 4);
+ const basegfx::B2DPoint aPointB1( aPointA2.getX() + nDistance, aYPosB1);
+ const basegfx::B2DPoint aPointB2( aPointB1.getX() + ((nAvailableLength * 2) / 20), aYPosB2 );
+ const basegfx::B2DPoint aPointB3( aPointB2.getX() + ((nAvailableLength * 2) / 20), aYPosB1 );
+ basegfx::B2DPolygon aPolygonB;
+ aPolygonB.append(aPointB1);
+ aPolygonB.append(aPointB2);
+ aPolygonB.append(aPointB3);
+ mpLineObjB->SetPathPoly(basegfx::B2DPolyPolygon(aPolygonB));
+
+ // create DrawObjectC
+ basegfx::B2DPolygon aPolygonC;
+ const basegfx::B2DPoint aPointC1( aPointB3.getX() + nDistance, aYPosB1);
+ const basegfx::B2DPoint aPointC2( aPointC1.getX() + ((nAvailableLength * 1) / 20), aYPosB2 );
+ const basegfx::B2DPoint aPointC3( aPointC2.getX() + ((nAvailableLength * 1) / 20), aYPosB1 );
+ aPolygonC.append(aPointC1);
+ aPolygonC.append(aPointC2);
+ aPolygonC.append(aPointC3);
+ mpLineObjC->SetPathPoly(basegfx::B2DPolyPolygon(aPolygonC));
+}
+
+SvxXLinePreview::SvxXLinePreview()
+ : mpLineObjA(nullptr)
+ , mpLineObjB(nullptr)
+ , mpLineObjC(nullptr)
+ , mpGraphic(nullptr)
+ , mbWithSymbol(false)
+{
+}
+
+void SvxXLinePreview::SetDrawingArea(weld::DrawingArea* pDrawingArea)
+{
+ SvxPreviewBase::SetDrawingArea(pDrawingArea);
+
+ mpLineObjA = new SdrPathObj(getModel(), SdrObjKind::Line);
+ mpLineObjB = new SdrPathObj(getModel(), SdrObjKind::PolyLine);
+ mpLineObjC = new SdrPathObj(getModel(), SdrObjKind::PolyLine);
+
+ Resize();
+ Invalidate();
+}
+
+SvxXLinePreview::~SvxXLinePreview()
+{
+ SdrObject *pFoo = mpLineObjA;
+ SdrObject::Free( pFoo );
+ pFoo = mpLineObjB;
+ SdrObject::Free( pFoo );
+ pFoo = mpLineObjC;
+ SdrObject::Free( pFoo );
+}
+
+void SvxXLinePreview::SetSymbol(Graphic* p,const Size& s)
+{
+ mpGraphic = p;
+ maSymbolSize = s;
+}
+
+void SvxXLinePreview::ResizeSymbol(const Size& s)
+{
+ if ( s != maSymbolSize )
+ {
+ maSymbolSize = s;
+ Invalidate();
+ }
+}
+
+void SvxXLinePreview::SetLineAttributes(const SfxItemSet& rItemSet)
+{
+ // Set ItemSet at objects
+ mpLineObjA->SetMergedItemSet(rItemSet);
+
+ // At line joints, do not use arrows
+ SfxItemSet aTempSet(rItemSet);
+ aTempSet.ClearItem(XATTR_LINESTART);
+ aTempSet.ClearItem(XATTR_LINEEND);
+
+ mpLineObjB->SetMergedItemSet(aTempSet);
+ mpLineObjC->SetMergedItemSet(aTempSet);
+}
+
+void SvxXLinePreview::Paint(vcl::RenderContext& rRenderContext, const tools::Rectangle&)
+{
+ LocalPrePaint(rRenderContext);
+
+ // paint objects to buffer device
+ sdr::contact::SdrObjectVector aObjectVector;
+ aObjectVector.push_back(mpLineObjA);
+ aObjectVector.push_back(mpLineObjB);
+ aObjectVector.push_back(mpLineObjC);
+
+ sdr::contact::ObjectContactOfObjListPainter aPainter(getBufferDevice(), std::move(aObjectVector), nullptr);
+ sdr::contact::DisplayInfo aDisplayInfo;
+
+ // do processing
+ aPainter.ProcessDisplay(aDisplayInfo);
+
+ if ( mbWithSymbol && mpGraphic )
+ {
+ const Size aOutputSize(GetOutputSize());
+ Point aPos( aOutputSize.Width() / 3, aOutputSize.Height() / 2 );
+ aPos.AdjustX( -(maSymbolSize.Width() / 2) );
+ aPos.AdjustY( -(maSymbolSize.Height() / 2) );
+ mpGraphic->Draw(getBufferDevice(), aPos, maSymbolSize);
+ }
+
+ LocalPostPaint(rRenderContext);
+}
+
+SvxXShadowPreview::SvxXShadowPreview()
+ : mpRectangleObject(nullptr)
+ , mpRectangleShadow(nullptr)
+{
+}
+
+void SvxXShadowPreview::SetDrawingArea(weld::DrawingArea* pDrawingArea)
+{
+ SvxPreviewBase::SetDrawingArea(pDrawingArea);
+ InitSettings();
+
+ // prepare size
+ Size aSize = GetPreviewSize().GetSize();
+ aSize.setWidth( aSize.Width() / 3 );
+ aSize.setHeight( aSize.Height() / 3 );
+
+ // create RectangleObject
+ const tools::Rectangle aObjectSize( Point( aSize.Width(), aSize.Height() ), aSize );
+ mpRectangleObject = new SdrRectObj(
+ getModel(),
+ aObjectSize);
+
+ // create ShadowObject
+ const tools::Rectangle aShadowSize( Point( aSize.Width(), aSize.Height() ), aSize );
+ mpRectangleShadow = new SdrRectObj(
+ getModel(),
+ aShadowSize);
+}
+
+SvxXShadowPreview::~SvxXShadowPreview()
+{
+ SdrObject::Free(mpRectangleObject);
+ SdrObject::Free(mpRectangleShadow);
+}
+
+void SvxXShadowPreview::SetRectangleAttributes(const SfxItemSet& rItemSet)
+{
+ mpRectangleObject->SetMergedItemSet(rItemSet, true);
+ mpRectangleObject->SetMergedItem(XLineStyleItem(drawing::LineStyle_NONE));
+}
+
+void SvxXShadowPreview::SetShadowAttributes(const SfxItemSet& rItemSet)
+{
+ mpRectangleShadow->SetMergedItemSet(rItemSet, true);
+ mpRectangleShadow->SetMergedItem(XLineStyleItem(drawing::LineStyle_NONE));
+}
+
+void SvxXShadowPreview::SetShadowPosition(const Point& rPos)
+{
+ maShadowOffset = rPos;
+}
+
+void SvxXShadowPreview::Paint(vcl::RenderContext& rRenderContext, const tools::Rectangle&)
+{
+ rRenderContext.Push(vcl::PushFlags::MAPMODE);
+ rRenderContext.SetMapMode(MapMode(MapUnit::Map100thMM));
+
+ LocalPrePaint(rRenderContext);
+
+ // prepare size
+ Size aSize = rRenderContext.GetOutputSize();
+ aSize.setWidth( aSize.Width() / 3 );
+ aSize.setHeight( aSize.Height() / 3 );
+
+ tools::Rectangle aObjectRect(Point(aSize.Width(), aSize.Height()), aSize);
+ mpRectangleObject->SetSnapRect(aObjectRect);
+ aObjectRect.Move(maShadowOffset.X(), maShadowOffset.Y());
+ mpRectangleShadow->SetSnapRect(aObjectRect);
+
+ sdr::contact::SdrObjectVector aObjectVector;
+
+ aObjectVector.push_back(mpRectangleShadow);
+ aObjectVector.push_back(mpRectangleObject);
+
+ sdr::contact::ObjectContactOfObjListPainter aPainter(getBufferDevice(), std::move(aObjectVector), nullptr);
+ sdr::contact::DisplayInfo aDisplayInfo;
+
+ aPainter.ProcessDisplay(aDisplayInfo);
+
+ LocalPostPaint(rRenderContext);
+
+ rRenderContext.Pop();
+}
+
+void SvxPreviewBase::InitSettings()
+{
+ const StyleSettings& rStyleSettings = Application::GetSettings().GetStyleSettings();
+
+ svtools::ColorConfig aColorConfig;
+ Color aTextColor(aColorConfig.GetColorValue(svtools::FONTCOLOR).nColor);
+ getBufferDevice().SetTextColor(aTextColor);
+
+ getBufferDevice().SetBackground(rStyleSettings.GetWindowColor());
+
+ getBufferDevice().SetDrawMode(rStyleSettings.GetHighContrastMode() ? OUTPUT_DRAWMODE_CONTRAST : OUTPUT_DRAWMODE_COLOR);
+
+ Invalidate();
+}
+
+SvxPreviewBase::SvxPreviewBase()
+ : mpModel(new SdrModel(nullptr, nullptr, true))
+{
+ // init model
+ mpModel->GetItemPool().FreezeIdRanges();
+}
+
+void SvxPreviewBase::SetDrawingArea(weld::DrawingArea* pDrawingArea)
+{
+ CustomWidgetController::SetDrawingArea(pDrawingArea);
+ Size aSize(getPreviewStripSize(pDrawingArea->get_ref_device()));
+ pDrawingArea->set_size_request(aSize.Width(), aSize.Height());
+ SetOutputSizePixel(aSize);
+
+ mpBufferDevice = VclPtr<VirtualDevice>::Create(pDrawingArea->get_ref_device());
+ mpBufferDevice->SetMapMode(MapMode(MapUnit::Map100thMM));
+}
+
+SvxPreviewBase::~SvxPreviewBase()
+{
+ mpModel.reset();
+ mpBufferDevice.disposeAndClear();
+}
+
+void SvxPreviewBase::LocalPrePaint(vcl::RenderContext const & rRenderContext)
+{
+ // init BufferDevice
+ if (mpBufferDevice->GetOutputSizePixel() != GetOutputSizePixel())
+ mpBufferDevice->SetOutputSizePixel(GetOutputSizePixel());
+ mpBufferDevice->SetAntialiasing(rRenderContext.GetAntialiasing());
+
+ const StyleSettings& rStyleSettings = Application::GetSettings().GetStyleSettings();
+
+ if (rStyleSettings.GetPreviewUsesCheckeredBackground())
+ {
+ const Point aNull(0, 0);
+ static const sal_uInt32 nLen(8);
+ static const Color aW(COL_WHITE);
+ static const Color aG(0xef, 0xef, 0xef);
+ const bool bWasEnabled(mpBufferDevice->IsMapModeEnabled());
+
+ mpBufferDevice->EnableMapMode(false);
+ mpBufferDevice->DrawCheckered(aNull, mpBufferDevice->GetOutputSizePixel(), nLen, aW, aG);
+ mpBufferDevice->EnableMapMode(bWasEnabled);
+ }
+ else
+ {
+ mpBufferDevice->Erase();
+ }
+}
+
+void SvxPreviewBase::LocalPostPaint(vcl::RenderContext& rRenderContext)
+{
+ // copy to front (in pixel mode)
+ const bool bWasEnabledSrc(mpBufferDevice->IsMapModeEnabled());
+ const bool bWasEnabledDst(rRenderContext.IsMapModeEnabled());
+ const Point aEmptyPoint;
+
+ mpBufferDevice->EnableMapMode(false);
+ rRenderContext.EnableMapMode(false);
+
+ rRenderContext.DrawOutDev(aEmptyPoint, GetOutputSizePixel(),
+ aEmptyPoint, GetOutputSizePixel(),
+ *mpBufferDevice);
+
+ mpBufferDevice->EnableMapMode(bWasEnabledSrc);
+ rRenderContext.EnableMapMode(bWasEnabledDst);
+}
+
+void SvxPreviewBase::StyleUpdated()
+{
+ InitSettings();
+ CustomWidgetController::StyleUpdated();
+}
+
+SvxXRectPreview::SvxXRectPreview()
+ : mpRectangleObject(nullptr)
+{
+}
+
+tools::Rectangle SvxPreviewBase::GetPreviewSize() const
+{
+ tools::Rectangle aObjectSize(Point(), getBufferDevice().PixelToLogic(GetOutputSizePixel()));
+ return aObjectSize;
+}
+
+void SvxXRectPreview::SetDrawingArea(weld::DrawingArea* pDrawingArea)
+{
+ SvxPreviewBase::SetDrawingArea(pDrawingArea);
+ InitSettings();
+
+ // create RectangleObject
+ mpRectangleObject = new SdrRectObj(getModel(), GetPreviewSize());
+}
+
+void SvxXRectPreview::Resize()
+{
+ SdrObject *pOrigObject = mpRectangleObject;
+ if (pOrigObject)
+ {
+ mpRectangleObject = new SdrRectObj(getModel(), GetPreviewSize());
+ SetAttributes(pOrigObject->GetMergedItemSet());
+ SdrObject::Free(pOrigObject);
+ }
+ SvxPreviewBase::Resize();
+}
+
+SvxXRectPreview::~SvxXRectPreview()
+{
+ SdrObject::Free(mpRectangleObject);
+}
+
+void SvxXRectPreview::SetAttributes(const SfxItemSet& rItemSet)
+{
+ mpRectangleObject->SetMergedItemSet(rItemSet, true);
+ mpRectangleObject->SetMergedItem(XLineStyleItem(drawing::LineStyle_NONE));
+}
+
+void SvxXRectPreview::Paint(vcl::RenderContext& rRenderContext, const tools::Rectangle&)
+{
+ rRenderContext.Push(vcl::PushFlags::MAPMODE);
+ rRenderContext.SetMapMode(MapMode(MapUnit::Map100thMM));
+ LocalPrePaint(rRenderContext);
+
+ sdr::contact::SdrObjectVector aObjectVector;
+
+ aObjectVector.push_back(mpRectangleObject);
+
+ sdr::contact::ObjectContactOfObjListPainter aPainter(getBufferDevice(), std::move(aObjectVector), nullptr);
+ sdr::contact::DisplayInfo aDisplayInfo;
+
+ aPainter.ProcessDisplay(aDisplayInfo);
+
+ LocalPostPaint(rRenderContext);
+ rRenderContext.Pop();
+}
+
+void limitWidthForSidebar(weld::SpinButton& rSpinButton)
+{
+ // space is limited in the sidebar, so limit MetricSpinButtons to a width of 4 digits
+ const int nMaxDigits = 4;
+ rSpinButton.set_width_chars(std::min(rSpinButton.get_width_chars(), nMaxDigits));
+}
+
+void limitWidthForSidebar(SvxRelativeField& rMetricSpinButton)
+{
+ weld::SpinButton& rSpinButton = rMetricSpinButton.get_widget();
+ limitWidthForSidebar(rSpinButton);
+}
+
+void padWidthForSidebar(weld::Toolbar& rToolbar, const css::uno::Reference<css::frame::XFrame>& rFrame)
+{
+ static int nColumnWidth = -1;
+ static vcl::ImageType eSize;
+ if (nColumnWidth != -1 && eSize != rToolbar.get_icon_size())
+ nColumnWidth = -1;
+ if (nColumnWidth == -1)
+ {
+ // use the, filled-in by dispatcher, width of measurewidth as the width
+ // of a "standard" column in a two column panel
+ std::unique_ptr<weld::Builder> xBuilder(Application::CreateBuilder(&rToolbar, "svx/ui/measurewidthbar.ui"));
+ std::unique_ptr<weld::Toolbar> xToolbar1(xBuilder->weld_toolbar("measurewidth1"));
+ ToolbarUnoDispatcher aDispatcher1(*xToolbar1, *xBuilder, rFrame);
+ std::unique_ptr<weld::Toolbar> xToolbar2(xBuilder->weld_toolbar("measurewidth2"));
+ ToolbarUnoDispatcher aDispatcher2(*xToolbar2, *xBuilder, rFrame);
+ nColumnWidth = std::max(xToolbar1->get_preferred_size().Width(), xToolbar2->get_preferred_size().Width());
+ eSize = rToolbar.get_icon_size();
+ }
+ rToolbar.set_size_request(nColumnWidth, -1);
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/dialog/dlgunit.hxx b/svx/source/dialog/dlgunit.hxx
new file mode 100644
index 000000000..bb8ac83d2
--- /dev/null
+++ b/svx/source/dialog/dlgunit.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_SVX_SOURCE_DIALOG_DLGUNIT_HXX
+#define INCLUDED_SVX_SOURCE_DIALOG_DLGUNIT_HXX
+
+#include <rtl/ustrbuf.hxx>
+#include <svx/svdtrans.hxx>
+#include <vcl/fieldvalues.hxx>
+
+inline OUString GetUnitString(tools::Long nVal_100, FieldUnit eFieldUnit, sal_Unicode cSep)
+{
+ OUStringBuffer aVal
+ = OUString::number(vcl::ConvertValue(nVal_100, 2, MapUnit::Map100thMM, eFieldUnit));
+
+ while (aVal.getLength() < 3)
+ aVal.insert(0, "0");
+
+ aVal.insert(aVal.getLength() - 2, cSep);
+ OUString aSuffix = SdrFormatter::GetUnitStr(eFieldUnit);
+ if (eFieldUnit != FieldUnit::NONE && eFieldUnit != FieldUnit::DEGREE
+ && eFieldUnit != FieldUnit::INCH)
+ aVal.append(" ");
+ if (eFieldUnit == FieldUnit::INCH)
+ {
+ OUString sDoublePrime = u"\u2033";
+ if (aSuffix != "\"" && aSuffix != sDoublePrime)
+ aVal.append(" ");
+ else
+ aSuffix = sDoublePrime;
+ }
+ aVal.append(aSuffix);
+
+ return aVal.makeStringAndClear();
+}
+
+#endif // INCLUDED_SVX_SOURCE_DIALOG_DLGUNIT_HXX
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/dialog/dlgutil.cxx b/svx/source/dialog/dlgutil.cxx
new file mode 100644
index 000000000..a33e1ca94
--- /dev/null
+++ b/svx/source/dialog/dlgutil.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 <svx/dlgutil.hxx>
+#include <svl/itemset.hxx>
+#include <sfx2/sfxsids.hrc>
+#include <sfx2/module.hxx>
+#include <svl/intitem.hxx>
+#include <svl/eitem.hxx>
+#include <sfx2/viewfrm.hxx>
+#include <sfx2/objsh.hxx>
+#include <sal/log.hxx>
+
+
+FieldUnit GetModuleFieldUnit( const SfxItemSet& rSet )
+{
+ if (const SfxUInt16Item* pItem = rSet.GetItemIfSet(SID_ATTR_METRIC, false))
+ return static_cast<FieldUnit>(pItem->GetValue());
+
+ return SfxModule::GetCurrentFieldUnit();
+}
+
+bool GetApplyCharUnit( const SfxItemSet& rSet )
+{
+ bool bUseCharUnit = false;
+ if ( const SfxBoolItem* pItem = rSet.GetItemIfSet( SID_ATTR_APPLYCHARUNIT, false ) )
+ bUseCharUnit = pItem->GetValue();
+ else
+ {
+ // FIXME - this might be wrong, cf. the DEV300 changes in GetModuleFieldUnit()
+ SfxViewFrame* pFrame = SfxViewFrame::Current();
+ SfxObjectShell* pSh = nullptr;
+ if ( pFrame )
+ pSh = pFrame->GetObjectShell();
+ if ( pSh ) // the object shell is not always available during reload
+ {
+ SfxModule* pModule = pSh->GetModule();
+ if ( pModule )
+ {
+ pItem = pModule->GetItem( SID_ATTR_APPLYCHARUNIT );
+ if ( pItem )
+ bUseCharUnit = pItem->GetValue();
+ }
+ else
+ {
+ SAL_WARN( "svx.dialog", "GetApplyCharUnit(): no module found" );
+ }
+ }
+ }
+ return bUseCharUnit;
+}
+
+FieldUnit GetModuleFieldUnit()
+{
+ return SfxModule::GetCurrentFieldUnit();
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/dialog/docrecovery.cxx b/svx/source/dialog/docrecovery.cxx
new file mode 100644
index 000000000..181d27b4f
--- /dev/null
+++ b/svx/source/dialog/docrecovery.cxx
@@ -0,0 +1,1104 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * 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/dialmgr.hxx>
+#include <svx/strings.hrc>
+#include <bitmaps.hlst>
+#include <docrecovery.hxx>
+
+#include <comphelper/propertyvalue.hxx>
+#include <comphelper/sequenceashashmap.hxx>
+#include <comphelper/string.hxx>
+#include <o3tl/safeint.hxx>
+#include <svtools/imagemgr.hxx>
+#include <sfx2/filedlghelper.hxx>
+#include <tools/urlobj.hxx>
+#include <vcl/weld.hxx>
+#include <vcl/svapp.hxx>
+
+#include <com/sun/star/util/URL.hpp>
+#include <com/sun/star/util/XURLTransformer.hpp>
+#include <com/sun/star/frame/theAutoRecovery.hpp>
+#include <com/sun/star/ui/dialogs/XFolderPicker2.hpp>
+#include <com/sun/star/ui/dialogs/ExecutableDialogResults.hpp>
+#include <com/sun/star/util/URLTransformer.hpp>
+#include <osl/file.hxx>
+#include <unotools/pathoptions.hxx>
+
+namespace svx::DocRecovery
+{
+
+using namespace ::osl;
+
+RecoveryCore::RecoveryCore(const css::uno::Reference< css::uno::XComponentContext >& rxContext,
+ bool bUsedForSaving)
+ : m_xContext ( rxContext )
+ , m_pListener ( nullptr )
+ , m_bListenForSaving(bUsedForSaving)
+{
+ impl_startListening();
+}
+
+
+RecoveryCore::~RecoveryCore()
+{
+ impl_stopListening();
+}
+
+
+const css::uno::Reference< css::uno::XComponentContext >& RecoveryCore::getComponentContext() const
+{
+ return m_xContext;
+}
+
+
+TURLList& RecoveryCore::getURLListAccess()
+{
+ return m_lURLs;
+}
+
+
+bool RecoveryCore::isBrokenTempEntry(const TURLInfo& rInfo)
+{
+ if (rInfo.TempURL.isEmpty())
+ return false;
+
+ // Note: If the original files was recovery ... but a temp file
+ // exists ... an error inside the temp file exists!
+ if (
+ (rInfo.RecoveryState != E_RECOVERY_FAILED ) &&
+ (rInfo.RecoveryState != E_ORIGINAL_DOCUMENT_RECOVERED)
+ )
+ return false;
+
+ return true;
+}
+
+
+void RecoveryCore::saveBrokenTempEntries(const OUString& rPath)
+{
+ if (rPath.isEmpty())
+ return;
+
+ if (!m_xRealCore.is())
+ return;
+
+ // prepare all needed parameters for the following dispatch() request.
+ css::util::URL aCopyURL = impl_getParsedURL(RECOVERY_CMD_DO_ENTRY_BACKUP);
+ css::uno::Sequence< css::beans::PropertyValue > lCopyArgs(3);
+ auto plCopyArgs = lCopyArgs.getArray();
+ plCopyArgs[0].Name = PROP_DISPATCHASYNCHRON;
+ plCopyArgs[0].Value <<= false;
+ plCopyArgs[1].Name = PROP_SAVEPATH;
+ plCopyArgs[1].Value <<= rPath;
+ plCopyArgs[2].Name = PROP_ENTRYID;
+ // lCopyArgs[2].Value will be changed during next loop...
+
+ // work on a copied list only...
+ // Reason: We will get notifications from the core for every
+ // changed or removed element. And that will change our m_lURLs list.
+ // That's not a good idea, if we use a stl iterator inbetween .-)
+ TURLList lURLs = m_lURLs;
+ for (const TURLInfo& rInfo : lURLs)
+ {
+ if (!RecoveryCore::isBrokenTempEntry(rInfo))
+ continue;
+
+ plCopyArgs[2].Value <<= rInfo.ID;
+ m_xRealCore->dispatch(aCopyURL, lCopyArgs);
+ }
+}
+
+
+void RecoveryCore::saveAllTempEntries(const OUString& rPath)
+{
+ if (rPath.isEmpty())
+ return;
+
+ if (!m_xRealCore.is())
+ return;
+
+ // prepare all needed parameters for the following dispatch() request.
+ css::util::URL aCopyURL = impl_getParsedURL(RECOVERY_CMD_DO_ENTRY_BACKUP);
+ css::uno::Sequence< css::beans::PropertyValue > lCopyArgs(3);
+ auto plCopyArgs = lCopyArgs.getArray();
+ plCopyArgs[0].Name = PROP_DISPATCHASYNCHRON;
+ plCopyArgs[0].Value <<= false;
+ plCopyArgs[1].Name = PROP_SAVEPATH;
+ plCopyArgs[1].Value <<= rPath;
+ plCopyArgs[2].Name = PROP_ENTRYID;
+ // lCopyArgs[2].Value will be changed during next loop ...
+
+ // work on a copied list only ...
+ // Reason: We will get notifications from the core for every
+ // changed or removed element. And that will change our m_lURLs list.
+ // That's not a good idea, if we use a stl iterator inbetween .-)
+ TURLList lURLs = m_lURLs;
+ for (const TURLInfo& rInfo : lURLs)
+ {
+ if (rInfo.TempURL.isEmpty())
+ continue;
+
+ plCopyArgs[2].Value <<= rInfo.ID;
+ m_xRealCore->dispatch(aCopyURL, lCopyArgs);
+ }
+}
+
+
+void RecoveryCore::forgetBrokenTempEntries()
+{
+ if (!m_xRealCore.is())
+ return;
+
+ css::util::URL aRemoveURL = impl_getParsedURL(RECOVERY_CMD_DO_ENTRY_CLEANUP);
+ css::uno::Sequence< css::beans::PropertyValue > lRemoveArgs(2);
+ auto plRemoveArgs = lRemoveArgs.getArray();
+ plRemoveArgs[0].Name = PROP_DISPATCHASYNCHRON;
+ plRemoveArgs[0].Value <<= false;
+ plRemoveArgs[1].Name = PROP_ENTRYID;
+ // lRemoveArgs[1].Value will be changed during next loop ...
+
+ // work on a copied list only ...
+ // Reason: We will get notifications from the core for every
+ // changed or removed element. And that will change our m_lURLs list.
+ // That's not a good idea, if we use a stl iterator inbetween .-)
+ TURLList lURLs = m_lURLs;
+ for (const TURLInfo& rInfo : lURLs)
+ {
+ if (!RecoveryCore::isBrokenTempEntry(rInfo))
+ continue;
+
+ plRemoveArgs[1].Value <<= rInfo.ID;
+ m_xRealCore->dispatch(aRemoveURL, lRemoveArgs);
+ }
+}
+
+
+void RecoveryCore::forgetAllRecoveryEntries()
+{
+ if (!m_xRealCore.is())
+ return;
+
+ css::util::URL aRemoveURL = impl_getParsedURL(RECOVERY_CMD_DO_ENTRY_CLEANUP);
+ css::uno::Sequence< css::beans::PropertyValue > lRemoveArgs(2);
+ auto plRemoveArgs = lRemoveArgs.getArray();
+ plRemoveArgs[0].Name = PROP_DISPATCHASYNCHRON;
+ plRemoveArgs[0].Value <<= false;
+ plRemoveArgs[1].Name = PROP_ENTRYID;
+ // lRemoveArgs[1].Value will be changed during next loop ...
+
+ // work on a copied list only ...
+ // Reason: We will get notifications from the core for every
+ // changed or removed element. And that will change our m_lURLs list.
+ // That's not a good idea, if we use a stl iterator inbetween .-)
+ TURLList lURLs = m_lURLs;
+ for (const TURLInfo& rInfo : lURLs)
+ {
+ plRemoveArgs[1].Value <<= rInfo.ID;
+ m_xRealCore->dispatch(aRemoveURL, lRemoveArgs);
+ }
+}
+
+
+void RecoveryCore::forgetBrokenRecoveryEntries()
+{
+ if (!m_xRealCore.is())
+ return;
+
+ css::util::URL aRemoveURL = impl_getParsedURL(RECOVERY_CMD_DO_ENTRY_CLEANUP);
+ css::uno::Sequence< css::beans::PropertyValue > lRemoveArgs(2);
+ auto plRemoveArgs = lRemoveArgs.getArray();
+ plRemoveArgs[0].Name = PROP_DISPATCHASYNCHRON;
+ plRemoveArgs[0].Value <<= false;
+ plRemoveArgs[1].Name = PROP_ENTRYID;
+ // lRemoveArgs[1].Value will be changed during next loop ...
+
+ // work on a copied list only ...
+ // Reason: We will get notifications from the core for every
+ // changed or removed element. And that will change our m_lURLs list.
+ // That's not a good idea, if we use a stl iterator inbetween .-)
+ TURLList lURLs = m_lURLs;
+ for (const TURLInfo& rInfo : lURLs)
+ {
+ if (!RecoveryCore::isBrokenTempEntry(rInfo))
+ continue;
+
+ plRemoveArgs[1].Value <<= rInfo.ID;
+ m_xRealCore->dispatch(aRemoveURL, lRemoveArgs);
+ }
+}
+
+
+void RecoveryCore::setProgressHandler(const css::uno::Reference< css::task::XStatusIndicator >& xProgress)
+{
+ m_xProgress = xProgress;
+}
+
+
+void RecoveryCore::setUpdateListener(IRecoveryUpdateListener* pListener)
+{
+ m_pListener = pListener;
+}
+
+
+void RecoveryCore::doEmergencySavePrepare()
+{
+ if (!m_xRealCore.is())
+ return;
+
+ css::util::URL aURL = impl_getParsedURL(RECOVERY_CMD_DO_PREPARE_EMERGENCY_SAVE);
+
+ css::uno::Sequence< css::beans::PropertyValue > lArgs{ comphelper::makePropertyValue(
+ PROP_DISPATCHASYNCHRON, false) };
+
+ m_xRealCore->dispatch(aURL, lArgs);
+}
+
+
+void RecoveryCore::doEmergencySave()
+{
+ if (!m_xRealCore.is())
+ return;
+
+ css::util::URL aURL = impl_getParsedURL(RECOVERY_CMD_DO_EMERGENCY_SAVE);
+
+ css::uno::Sequence< css::beans::PropertyValue > lArgs{
+ comphelper::makePropertyValue(PROP_STATUSINDICATOR, m_xProgress),
+ comphelper::makePropertyValue(PROP_DISPATCHASYNCHRON, true)
+ };
+
+ m_xRealCore->dispatch(aURL, lArgs);
+}
+
+
+void RecoveryCore::doRecovery()
+{
+ if (!m_xRealCore.is())
+ return;
+
+ css::util::URL aURL = impl_getParsedURL(RECOVERY_CMD_DO_RECOVERY);
+
+ css::uno::Sequence< css::beans::PropertyValue > lArgs{
+ comphelper::makePropertyValue(PROP_STATUSINDICATOR, m_xProgress),
+ comphelper::makePropertyValue(PROP_DISPATCHASYNCHRON, true)
+ };
+
+ m_xRealCore->dispatch(aURL, lArgs);
+}
+
+
+ERecoveryState RecoveryCore::mapDocState2RecoverState(EDocStates eDocState)
+{
+ // ???
+ ERecoveryState eRecState = E_NOT_RECOVERED_YET;
+
+ /* Attention:
+ Some of the following states can occur at the
+ same time. So we have to check for the "worst case" first!
+
+ DAMAGED -> INCOMPLETE -> HANDLED
+ */
+
+ // running ...
+ if (
+ (eDocState & EDocStates::TryLoadBackup ) ||
+ (eDocState & EDocStates::TryLoadOriginal)
+ )
+ eRecState = E_RECOVERY_IS_IN_PROGRESS;
+ // red
+ else if (eDocState & EDocStates::Damaged)
+ eRecState = E_RECOVERY_FAILED;
+ // yellow
+ else if (eDocState & EDocStates::Incomplete)
+ eRecState = E_ORIGINAL_DOCUMENT_RECOVERED;
+ // green
+ else if (eDocState & EDocStates::Succeeded)
+ eRecState = E_SUCCESSFULLY_RECOVERED;
+
+ return eRecState;
+}
+
+
+void SAL_CALL RecoveryCore::statusChanged(const css::frame::FeatureStateEvent& aEvent)
+{
+ // a) special notification about start/stop async dispatch!
+ // FeatureDescriptor = "start" || "stop"
+ if (aEvent.FeatureDescriptor == RECOVERY_OPERATIONSTATE_START)
+ {
+ return;
+ }
+
+ if (aEvent.FeatureDescriptor == RECOVERY_OPERATIONSTATE_STOP)
+ {
+ if (m_pListener)
+ m_pListener->end();
+ return;
+ }
+
+ // b) normal notification about changed items
+ // FeatureDescriptor = "Update"
+ // State = List of information [seq< NamedValue >]
+ if (aEvent.FeatureDescriptor != RECOVERY_OPERATIONSTATE_UPDATE)
+ return;
+
+ ::comphelper::SequenceAsHashMap lInfo(aEvent.State);
+ TURLInfo aNew;
+
+ aNew.ID = lInfo.getUnpackedValueOrDefault(STATEPROP_ID , sal_Int32(0) );
+ aNew.DocState = static_cast<EDocStates>(lInfo.getUnpackedValueOrDefault(STATEPROP_STATE , sal_Int32(0) ));
+ aNew.OrgURL = lInfo.getUnpackedValueOrDefault(STATEPROP_ORGURL , OUString());
+ aNew.TempURL = lInfo.getUnpackedValueOrDefault(STATEPROP_TEMPURL , OUString());
+ aNew.FactoryURL = lInfo.getUnpackedValueOrDefault(STATEPROP_FACTORYURL , OUString());
+ aNew.TemplateURL = lInfo.getUnpackedValueOrDefault(STATEPROP_TEMPLATEURL, OUString());
+ aNew.DisplayName = lInfo.getUnpackedValueOrDefault(STATEPROP_TITLE , OUString());
+ aNew.Module = lInfo.getUnpackedValueOrDefault(STATEPROP_MODULE , OUString());
+
+ if (aNew.OrgURL.isEmpty()) {
+ // If there is no file URL, the window title is used for the display name.
+ // Remove any unwanted elements such as " - LibreOffice Writer".
+ sal_Int32 i = aNew.DisplayName.indexOf(" - ");
+ if (i > 0)
+ aNew.DisplayName = aNew.DisplayName.copy(0, i);
+ } else {
+ // If there is a file URL, parse out the filename part as the display name.
+ INetURLObject aOrgURL(aNew.OrgURL);
+ aNew.DisplayName = aOrgURL.getName(INetURLObject::LAST_SEGMENT, true,
+ INetURLObject::DecodeMechanism::WithCharset);
+ }
+
+ // search for already existing items and update her nState value ...
+ for (TURLInfo& aOld : m_lURLs)
+ {
+ if (aOld.ID == aNew.ID)
+ {
+ // change existing
+ aOld.DocState = aNew.DocState;
+ aOld.RecoveryState = RecoveryCore::mapDocState2RecoverState(aOld.DocState);
+ if (m_pListener)
+ {
+ m_pListener->updateItems();
+ m_pListener->stepNext(&aOld);
+ }
+ return;
+ }
+ }
+
+ // append as new one
+ // TODO think about matching Module name to a corresponding icon
+ OUString sURL = aNew.OrgURL;
+ if (sURL.isEmpty())
+ sURL = aNew.FactoryURL;
+ if (sURL.isEmpty())
+ sURL = aNew.TempURL;
+ if (sURL.isEmpty())
+ sURL = aNew.TemplateURL;
+ INetURLObject aURL(sURL);
+ aNew.StandardImageId = SvFileInformationManager::GetFileImageId(aURL);
+
+ /* set the right UI state for this item to NOT_RECOVERED_YET... because nDocState shows the state of
+ the last emergency save operation before and is interesting for the used recovery core service only...
+ for now! But if there is a further notification for this item (see lines above!) we must
+ map the doc state to an UI state. */
+ aNew.RecoveryState = E_NOT_RECOVERED_YET;
+
+ m_lURLs.push_back(aNew);
+
+ if (m_pListener)
+ m_pListener->updateItems();
+}
+
+
+void SAL_CALL RecoveryCore::disposing(const css::lang::EventObject& /*aEvent*/)
+{
+ m_xRealCore.clear();
+}
+
+
+void RecoveryCore::impl_startListening()
+{
+ // listening already initialized ?
+ if (m_xRealCore.is())
+ return;
+ m_xRealCore = css::frame::theAutoRecovery::get(m_xContext);
+
+ css::util::URL aURL;
+ if (m_bListenForSaving)
+ aURL.Complete = RECOVERY_CMD_DO_EMERGENCY_SAVE;
+ else
+ aURL.Complete = RECOVERY_CMD_DO_RECOVERY;
+ css::uno::Reference< css::util::XURLTransformer > xParser(css::util::URLTransformer::create(m_xContext));
+ xParser->parseStrict(aURL);
+
+ /* Note: addStatusListener() call us synchronous back ... so we
+ will get the complete list of currently open documents! */
+ m_xRealCore->addStatusListener(static_cast< css::frame::XStatusListener* >(this), aURL);
+}
+
+
+void RecoveryCore::impl_stopListening()
+{
+ // Ignore it, if this instance doesn't listen currently
+ if (!m_xRealCore.is())
+ return;
+
+ css::util::URL aURL;
+ if (m_bListenForSaving)
+ aURL.Complete = RECOVERY_CMD_DO_EMERGENCY_SAVE;
+ else
+ aURL.Complete = RECOVERY_CMD_DO_RECOVERY;
+ css::uno::Reference< css::util::XURLTransformer > xParser(css::util::URLTransformer::create(m_xContext));
+ xParser->parseStrict(aURL);
+
+ m_xRealCore->removeStatusListener(static_cast< css::frame::XStatusListener* >(this), aURL);
+ m_xRealCore.clear();
+}
+
+
+css::util::URL RecoveryCore::impl_getParsedURL(const OUString& sURL)
+{
+ css::util::URL aURL;
+ aURL.Complete = sURL;
+
+ css::uno::Reference< css::util::XURLTransformer > xParser(css::util::URLTransformer::create(m_xContext));
+ xParser->parseStrict(aURL);
+
+ return aURL;
+}
+
+PluginProgress::PluginProgress(weld::ProgressBar* pProgressBar)
+ : m_pProgressBar(pProgressBar)
+ , m_nRange(100)
+{
+}
+
+PluginProgress::~PluginProgress()
+{
+}
+
+void SAL_CALL PluginProgress::dispose()
+{
+ m_pProgressBar = nullptr;
+}
+
+void SAL_CALL PluginProgress::addEventListener(const css::uno::Reference< css::lang::XEventListener >& )
+{
+}
+
+void SAL_CALL PluginProgress::removeEventListener( const css::uno::Reference< css::lang::XEventListener >& )
+{
+}
+
+void SAL_CALL PluginProgress::start(const OUString&, sal_Int32 nRange)
+{
+ m_nRange = nRange;
+ if (m_pProgressBar)
+ m_pProgressBar->set_percentage(0);
+}
+
+void SAL_CALL PluginProgress::end()
+{
+ if (m_pProgressBar)
+ m_pProgressBar->set_percentage(m_nRange);
+}
+
+void SAL_CALL PluginProgress::setText(const OUString& rText)
+{
+ if (m_pProgressBar)
+ m_pProgressBar->set_text(rText);
+}
+
+void SAL_CALL PluginProgress::setValue(sal_Int32 nValue)
+{
+ if (m_pProgressBar)
+ m_pProgressBar->set_percentage((nValue * 100) / m_nRange);
+}
+
+void SAL_CALL PluginProgress::reset()
+{
+ if (m_pProgressBar)
+ m_pProgressBar->set_percentage(0);
+}
+
+SaveDialog::SaveDialog(weld::Window* pParent, RecoveryCore* pCore)
+ : GenericDialogController(pParent, "svx/ui/docrecoverysavedialog.ui", "DocRecoverySaveDialog")
+ , m_pCore(pCore)
+ , m_xFileListLB(m_xBuilder->weld_tree_view("filelist"))
+ , m_xOkBtn(m_xBuilder->weld_button("ok"))
+{
+ m_xFileListLB->set_size_request(-1, m_xFileListLB->get_height_rows(10));
+
+ // Prepare the office for the following crash save step.
+ // E.g. hide all open windows so the user can't influence our
+ // operation .-)
+ m_pCore->doEmergencySavePrepare();
+
+ m_xOkBtn->connect_clicked(LINK(this, SaveDialog, OKButtonHdl));
+
+ // fill listbox with current open documents
+
+ TURLList& rURLs = m_pCore->getURLListAccess();
+
+ for (const TURLInfo& rInfo : rURLs)
+ {
+ m_xFileListLB->append("", rInfo.DisplayName, rInfo.StandardImageId);
+ }
+}
+
+SaveDialog::~SaveDialog()
+{
+}
+
+IMPL_LINK_NOARG(SaveDialog, OKButtonHdl, weld::Button&, void)
+{
+ // start crash-save with progress
+ std::unique_ptr<SaveProgressDialog> xProgress(new SaveProgressDialog(m_xDialog.get(), m_pCore));
+ short nResult = xProgress->run();
+ xProgress.reset();
+
+ // if "CANCEL" => return "CANCEL"
+ // if "OK" => "AUTOLUNCH" always !
+ if (nResult == DLG_RET_OK)
+ nResult = DLG_RET_OK_AUTOLUNCH;
+
+ m_xDialog->response(nResult);
+}
+
+SaveProgressDialog::SaveProgressDialog(weld::Window* pParent, RecoveryCore* pCore)
+ : GenericDialogController(pParent, "svx/ui/docrecoveryprogressdialog.ui", "DocRecoveryProgressDialog")
+ , m_pCore(pCore)
+ , m_xProgressBar(m_xBuilder->weld_progress_bar("progress"))
+{
+ m_xProgressBar->set_size_request(m_xProgressBar->get_approximate_digit_width() * 50, -1);
+ m_xProgress = new PluginProgress(m_xProgressBar.get());
+}
+
+SaveProgressDialog::~SaveProgressDialog()
+{
+ css::uno::Reference<css::lang::XComponent> xComp(m_xProgress, css::uno::UNO_QUERY);
+ if (xComp)
+ xComp->dispose();
+}
+
+short SaveProgressDialog::run()
+{
+ ::SolarMutexGuard aLock;
+
+ m_pCore->setProgressHandler(m_xProgress);
+ m_pCore->setUpdateListener(this);
+ m_pCore->doEmergencySave();
+ short nRet = DialogController::run();
+ m_pCore->setUpdateListener(nullptr);
+ return nRet;
+}
+
+void SaveProgressDialog::updateItems()
+{
+}
+
+void SaveProgressDialog::stepNext(TURLInfo* )
+{
+ /* TODO
+
+ if m_pCore would have a member m_mCurrentItem, you could see,
+ who is current, who is next ... You can show this information
+ in progress report FixText
+ */
+}
+
+void SaveProgressDialog::end()
+{
+ m_xDialog->response(DLG_RET_OK);
+}
+
+static short impl_askUserForWizardCancel(weld::Widget* pParent, TranslateId pRes)
+{
+ std::unique_ptr<weld::MessageDialog> xQuery(Application::CreateMessageDialog(pParent,
+ VclMessageType::Question, VclButtonsType::YesNo, SvxResId(pRes)));
+ if (xQuery->run() == RET_YES)
+ return DLG_RET_OK;
+ else
+ return DLG_RET_CANCEL;
+}
+
+RecoveryDialog::RecoveryDialog(weld::Window* pParent, RecoveryCore* pCore)
+ : GenericDialogController(pParent, "svx/ui/docrecoveryrecoverdialog.ui", "DocRecoveryRecoverDialog")
+ , m_aTitleRecoveryInProgress(SvxResId(RID_SVXSTR_RECOVERY_INPROGRESS))
+ , m_aRecoveryOnlyFinish (SvxResId(RID_SVXSTR_RECOVERYONLY_FINISH))
+ , m_aRecoveryOnlyFinishDescr(SvxResId(RID_SVXSTR_RECOVERYONLY_FINISH_DESCR))
+ , m_pCore(pCore)
+ , m_eRecoveryState(RecoveryDialog::E_RECOVERY_PREPARED)
+ , m_bWaitForCore(false)
+ , m_bWasRecoveryStarted(false)
+ , m_aSuccessRecovStr(SvxResId(RID_SVXSTR_SUCCESSRECOV))
+ , m_aOrigDocRecovStr(SvxResId(RID_SVXSTR_ORIGDOCRECOV))
+ , m_aRecovFailedStr(SvxResId(RID_SVXSTR_RECOVFAILED))
+ , m_aRecovInProgrStr(SvxResId(RID_SVXSTR_RECOVINPROGR))
+ , m_aNotRecovYetStr(SvxResId(RID_SVXSTR_NOTRECOVYET))
+ , m_xDescrFT(m_xBuilder->weld_label("desc"))
+ , m_xProgressBar(m_xBuilder->weld_progress_bar("progress"))
+ , m_xFileListLB(m_xBuilder->weld_tree_view("filelist"))
+ , m_xNextBtn(m_xBuilder->weld_button("next"))
+ , m_xCancelBtn(m_xBuilder->weld_button("cancel"))
+{
+ const auto nWidth = m_xFileListLB->get_approximate_digit_width() * 70;
+ m_xFileListLB->set_size_request(nWidth, m_xFileListLB->get_height_rows(10));
+ m_xProgressBar->set_size_request(m_xProgressBar->get_approximate_digit_width() * 50, -1);
+ m_xProgress = new PluginProgress(m_xProgressBar.get());
+
+ std::vector<int> aWidths
+ {
+ o3tl::narrowing<int>(m_xFileListLB->get_checkbox_column_width()),
+ o3tl::narrowing<int>(60 * nWidth / 100),
+ o3tl::narrowing<int>(m_xFileListLB->get_checkbox_column_width())
+ };
+ m_xFileListLB->set_column_fixed_widths(aWidths);
+
+ m_xNextBtn->set_sensitive(true);
+ m_xNextBtn->connect_clicked( LINK( this, RecoveryDialog, NextButtonHdl ) );
+ m_xCancelBtn->connect_clicked( LINK( this, RecoveryDialog, CancelButtonHdl ) );
+
+ // fill list box first time
+ TURLList& rURLList = m_pCore->getURLListAccess();
+ for (size_t i = 0, nCount = rURLList.size(); i < nCount; ++i)
+ {
+ const TURLInfo& rInfo = rURLList[i];
+ m_xFileListLB->append();
+ m_xFileListLB->set_id(i, weld::toId(&rInfo));
+ m_xFileListLB->set_image(i, rInfo.StandardImageId, 0);
+ m_xFileListLB->set_text(i, rInfo.DisplayName, 1);
+ m_xFileListLB->set_image(i, impl_getStatusImage(rInfo), 2);
+ m_xFileListLB->set_text(i, impl_getStatusString(rInfo), 3);
+ }
+
+ // mark first item
+ if (m_xFileListLB->n_children())
+ m_xFileListLB->set_cursor(0);
+}
+
+RecoveryDialog::~RecoveryDialog()
+{
+ css::uno::Reference<css::lang::XComponent> xComp(m_xProgress, css::uno::UNO_QUERY);
+ if (xComp)
+ xComp->dispose();
+}
+
+short RecoveryDialog::execute()
+{
+ ::SolarMutexGuard aSolarLock;
+
+ switch (m_eRecoveryState)
+ {
+ case RecoveryDialog::E_RECOVERY_IN_PROGRESS :
+ {
+ // user decided to start recovery ...
+ m_bWasRecoveryStarted = true;
+ // do it asynchronous (to allow repaints)
+ // and wait for this asynchronous operation.
+ m_xDescrFT->set_label( m_aTitleRecoveryInProgress );
+ m_xNextBtn->set_sensitive(false);
+ m_xCancelBtn->set_sensitive(false);
+ m_pCore->setProgressHandler(m_xProgress);
+ m_pCore->setUpdateListener(this);
+ m_pCore->doRecovery();
+
+ m_bWaitForCore = true;
+ while(m_bWaitForCore && !Application::IsQuit())
+ Application::Yield();
+
+ m_pCore->setUpdateListener(nullptr);
+ m_eRecoveryState = RecoveryDialog::E_RECOVERY_CORE_DONE;
+ return execute();
+ }
+
+ case RecoveryDialog::E_RECOVERY_CORE_DONE :
+ {
+ // the core finished it's task.
+ // let the user decide the next step.
+ m_xDescrFT->set_label(m_aRecoveryOnlyFinishDescr);
+ m_xNextBtn->set_label(m_aRecoveryOnlyFinish);
+ m_xNextBtn->set_sensitive(true);
+ m_xCancelBtn->set_sensitive(false);
+ return 0;
+ }
+
+ case RecoveryDialog::E_RECOVERY_DONE :
+ {
+ // All documents were recovered.
+ // User decided to step to the "next" wizard page.
+ // Do it ... but check first, if there exist some
+ // failed recovery documents. They must be saved to
+ // a user selected directory.
+ short nRet = DLG_RET_UNKNOWN;
+ BrokenRecoveryDialog aBrokenRecoveryDialog(m_xDialog.get(), m_pCore, !m_bWasRecoveryStarted);
+ OUString sSaveDir = aBrokenRecoveryDialog.getSaveDirURL(); // get the default dir
+ if (aBrokenRecoveryDialog.isExecutionNeeded())
+ {
+ nRet = aBrokenRecoveryDialog.run();
+ sSaveDir = aBrokenRecoveryDialog.getSaveDirURL();
+ }
+
+ switch(nRet)
+ {
+ // no broken temp files exists
+ // step to the next wizard page
+ case DLG_RET_UNKNOWN :
+ {
+ m_eRecoveryState = RecoveryDialog::E_RECOVERY_HANDLED;
+ return DLG_RET_OK;
+ }
+
+ // user decided to save the broken temp files
+ // do and forget it
+ // step to the next wizard page
+ case DLG_RET_OK :
+ {
+ m_pCore->saveBrokenTempEntries(sSaveDir);
+ m_pCore->forgetBrokenTempEntries();
+ m_eRecoveryState = RecoveryDialog::E_RECOVERY_HANDLED;
+ return DLG_RET_OK;
+ }
+
+ // user decided to ignore broken temp files.
+ // Ask it again ... may be this decision was wrong.
+ // Results:
+ // IGNORE => remove broken temp files
+ // => step to the next wizard page
+ // CANCEL => step back to the recovery page
+ case DLG_RET_CANCEL :
+ {
+ // TODO ask user ...
+ m_pCore->forgetBrokenTempEntries();
+ m_eRecoveryState = RecoveryDialog::E_RECOVERY_HANDLED;
+ return DLG_RET_OK;
+ }
+ }
+
+ m_eRecoveryState = RecoveryDialog::E_RECOVERY_HANDLED;
+ return DLG_RET_OK;
+ }
+
+ case RecoveryDialog::E_RECOVERY_CANCELED :
+ {
+ // "YES" => break recovery
+ // But there exist different states, where "cancel" can be called.
+ // Handle it different.
+ if (m_bWasRecoveryStarted)
+ m_eRecoveryState = RecoveryDialog::E_RECOVERY_CANCELED_AFTERWARDS;
+ else
+ m_eRecoveryState = RecoveryDialog::E_RECOVERY_CANCELED_BEFORE;
+ return execute();
+ }
+
+ case RecoveryDialog::E_RECOVERY_CANCELED_BEFORE :
+ case RecoveryDialog::E_RECOVERY_CANCELED_AFTERWARDS :
+ {
+ // We have to check if there exists some temp. files.
+ // They should be saved to a user defined location.
+ // If no temp files exists or user decided to ignore it ...
+ // we have to remove all recovery/session data anyway!
+ short nRet = DLG_RET_UNKNOWN;
+ BrokenRecoveryDialog aBrokenRecoveryDialog(m_xDialog.get(), m_pCore, !m_bWasRecoveryStarted);
+ OUString sSaveDir = aBrokenRecoveryDialog.getSaveDirURL(); // get the default save location
+
+ // dialog itself checks if there is a need to copy files for this mode.
+ // It uses the information m_bWasRecoveryStarted doing so.
+ if (aBrokenRecoveryDialog.isExecutionNeeded())
+ {
+ nRet = aBrokenRecoveryDialog.run();
+ sSaveDir = aBrokenRecoveryDialog.getSaveDirURL();
+ }
+
+ // Possible states:
+ // a) nRet == DLG_RET_UNKNOWN
+ // dialog was not shown ...
+ // because there exists no temp file for copy.
+ // => remove all recovery data
+ // b) nRet == DLG_RET_OK
+ // dialog was shown ...
+ // user decided to save temp files
+ // => save all OR broken temp files (depends from the time, where cancel was called)
+ // => remove all recovery data
+ // c) nRet == DLG_RET_CANCEL
+ // dialog was shown ...
+ // user decided to ignore temp files
+ // => remove all recovery data
+ // => a)/c) are the same ... b) has one additional operation
+
+ // b)
+ if (nRet == DLG_RET_OK)
+ {
+ if (m_bWasRecoveryStarted)
+ m_pCore->saveBrokenTempEntries(sSaveDir);
+ else
+ m_pCore->saveAllTempEntries(sSaveDir);
+ }
+
+ // a,b,c)
+ if (m_bWasRecoveryStarted)
+ m_pCore->forgetBrokenRecoveryEntries();
+ else
+ m_pCore->forgetAllRecoveryEntries();
+ m_eRecoveryState = RecoveryDialog::E_RECOVERY_HANDLED;
+
+ // THERE IS NO WAY BACK. see impl_askUserForWizardCancel()!
+ return DLG_RET_CANCEL;
+ }
+ }
+
+ // should never be reached .-)
+ OSL_FAIL("Should never be reached!");
+ return DLG_RET_OK;
+}
+
+void RecoveryDialog::updateItems()
+{
+ int c = m_xFileListLB->n_children();
+ for (int i = 0; i < c; ++i)
+ {
+ TURLInfo* pInfo = weld::fromId<TURLInfo*>(m_xFileListLB->get_id(i));
+ if ( !pInfo )
+ continue;
+
+ m_xFileListLB->set_image(i, impl_getStatusImage(*pInfo), 2);
+ OUString sStatus = impl_getStatusString( *pInfo );
+ if (!sStatus.isEmpty())
+ m_xFileListLB->set_text(i, sStatus, 3);
+ }
+}
+
+void RecoveryDialog::stepNext(TURLInfo* pItem)
+{
+ int c = m_xFileListLB->n_children();
+ for (int i=0; i < c; ++i)
+ {
+ TURLInfo* pInfo = weld::fromId<TURLInfo*>(m_xFileListLB->get_id(i));
+ if (pInfo->ID != pItem->ID)
+ continue;
+
+ m_xFileListLB->set_cursor(i);
+ m_xFileListLB->scroll_to_row(i);
+ break;
+ }
+}
+
+void RecoveryDialog::end()
+{
+ m_bWaitForCore = false;
+}
+
+IMPL_LINK_NOARG(RecoveryDialog, NextButtonHdl, weld::Button&, void)
+{
+ switch (m_eRecoveryState)
+ {
+ case RecoveryDialog::E_RECOVERY_PREPARED:
+ m_eRecoveryState = RecoveryDialog::E_RECOVERY_IN_PROGRESS;
+ execute();
+ break;
+ case RecoveryDialog::E_RECOVERY_CORE_DONE:
+ m_eRecoveryState = RecoveryDialog::E_RECOVERY_DONE;
+ execute();
+ break;
+ }
+
+ if (m_eRecoveryState == RecoveryDialog::E_RECOVERY_HANDLED)
+ {
+ m_xDialog->response(DLG_RET_OK);
+ }
+}
+
+IMPL_LINK_NOARG(RecoveryDialog, CancelButtonHdl, weld::Button&, void)
+{
+ switch (m_eRecoveryState)
+ {
+ case RecoveryDialog::E_RECOVERY_PREPARED:
+ if (impl_askUserForWizardCancel(m_xDialog.get(), RID_SVXSTR_QUERY_EXIT_RECOVERY) != DLG_RET_CANCEL)
+ {
+ m_eRecoveryState = RecoveryDialog::E_RECOVERY_CANCELED;
+ execute();
+ }
+ break;
+ case RecoveryDialog::E_RECOVERY_CORE_DONE:
+ m_eRecoveryState = RecoveryDialog::E_RECOVERY_CANCELED;
+ execute();
+ break;
+ }
+
+ if (m_eRecoveryState == RecoveryDialog::E_RECOVERY_HANDLED)
+ {
+ m_xDialog->response(RET_CANCEL);
+ }
+}
+
+OUString RecoveryDialog::impl_getStatusString( const TURLInfo& rInfo ) const
+{
+ OUString sStatus;
+ switch ( rInfo.RecoveryState )
+ {
+ case E_SUCCESSFULLY_RECOVERED :
+ sStatus = m_aSuccessRecovStr;
+ break;
+ case E_ORIGINAL_DOCUMENT_RECOVERED :
+ sStatus = m_aOrigDocRecovStr;
+ break;
+ case E_RECOVERY_FAILED :
+ sStatus = m_aRecovFailedStr;
+ break;
+ case E_RECOVERY_IS_IN_PROGRESS :
+ sStatus = m_aRecovInProgrStr;
+ break;
+ case E_NOT_RECOVERED_YET :
+ sStatus = m_aNotRecovYetStr;
+ break;
+ default:
+ break;
+ }
+ return sStatus;
+}
+
+OUString RecoveryDialog::impl_getStatusImage( const TURLInfo& rInfo )
+{
+ OUString sStatus;
+ switch ( rInfo.RecoveryState )
+ {
+ case E_SUCCESSFULLY_RECOVERED :
+ sStatus = RID_SVXBMP_GREENCHECK;
+ break;
+ case E_ORIGINAL_DOCUMENT_RECOVERED :
+ sStatus = RID_SVXBMP_YELLOWCHECK;
+ break;
+ case E_RECOVERY_FAILED :
+ sStatus = RID_SVXBMP_REDCROSS;
+ break;
+ default:
+ break;
+ }
+ return sStatus;
+}
+
+BrokenRecoveryDialog::BrokenRecoveryDialog(weld::Window* pParent,
+ RecoveryCore* pCore,
+ bool bBeforeRecovery)
+ : GenericDialogController(pParent, "svx/ui/docrecoverybrokendialog.ui", "DocRecoveryBrokenDialog")
+ , m_pCore(pCore)
+ , m_bBeforeRecovery(bBeforeRecovery)
+ , m_bExecutionNeeded(false)
+ , m_xFileListLB(m_xBuilder->weld_tree_view("filelist"))
+ , m_xSaveDirED(m_xBuilder->weld_entry("savedir"))
+ , m_xSaveDirBtn(m_xBuilder->weld_button("change"))
+ , m_xOkBtn(m_xBuilder->weld_button("ok"))
+ , m_xCancelBtn(m_xBuilder->weld_button("cancel"))
+{
+ m_xSaveDirBtn->connect_clicked( LINK( this, BrokenRecoveryDialog, SaveButtonHdl ) );
+ m_xOkBtn->connect_clicked( LINK( this, BrokenRecoveryDialog, OkButtonHdl ) );
+ m_xCancelBtn->connect_clicked( LINK( this, BrokenRecoveryDialog, CancelButtonHdl ) );
+
+ m_sSavePath = SvtPathOptions().GetWorkPath();
+ INetURLObject aObj( m_sSavePath );
+ OUString sPath;
+ osl::FileBase::getSystemPathFromFileURL(aObj.GetMainURL( INetURLObject::DecodeMechanism::NONE ), sPath);
+ m_xSaveDirED->set_text(sPath);
+
+ impl_refresh();
+}
+
+BrokenRecoveryDialog::~BrokenRecoveryDialog()
+{
+}
+
+void BrokenRecoveryDialog::impl_refresh()
+{
+ m_bExecutionNeeded = false;
+ TURLList& rURLList = m_pCore->getURLListAccess();
+ for (const TURLInfo& rInfo : rURLList)
+ {
+ if (m_bBeforeRecovery)
+ {
+ // "Cancel" before recovery ->
+ // search for any temp files!
+ if (rInfo.TempURL.isEmpty())
+ continue;
+ }
+ else
+ {
+ // "Cancel" after recovery ->
+ // search for broken temp files
+ if (!RecoveryCore::isBrokenTempEntry(rInfo))
+ continue;
+ }
+
+ m_bExecutionNeeded = true;
+
+ m_xFileListLB->append(weld::toId(&rInfo), rInfo.DisplayName, rInfo.StandardImageId);
+ }
+ m_sSavePath.clear();
+ m_xOkBtn->grab_focus();
+}
+
+bool BrokenRecoveryDialog::isExecutionNeeded() const
+{
+ return m_bExecutionNeeded;
+}
+
+const OUString& BrokenRecoveryDialog::getSaveDirURL() const
+{
+ return m_sSavePath;
+}
+
+IMPL_LINK_NOARG(BrokenRecoveryDialog, OkButtonHdl, weld::Button&, void)
+{
+ OUString sPhysicalPath = comphelper::string::strip(m_xSaveDirED->get_text(), ' ');
+ OUString sURL;
+ osl::FileBase::getFileURLFromSystemPath( sPhysicalPath, sURL );
+ m_sSavePath = sURL;
+ while (m_sSavePath.isEmpty())
+ impl_askForSavePath();
+
+ m_xDialog->response(DLG_RET_OK);
+}
+
+IMPL_LINK_NOARG(BrokenRecoveryDialog, CancelButtonHdl, weld::Button&, void)
+{
+ m_xDialog->response(RET_CANCEL);
+}
+
+IMPL_LINK_NOARG(BrokenRecoveryDialog, SaveButtonHdl, weld::Button&, void)
+{
+ impl_askForSavePath();
+}
+
+void BrokenRecoveryDialog::impl_askForSavePath()
+{
+ css::uno::Reference< css::ui::dialogs::XFolderPicker2 > xFolderPicker =
+ sfx2::createFolderPicker(m_pCore->getComponentContext(), m_xDialog.get());
+
+ INetURLObject aURL(m_sSavePath, INetProtocol::File);
+ xFolderPicker->setDisplayDirectory(aURL.GetMainURL(INetURLObject::DecodeMechanism::NONE));
+ short nRet = xFolderPicker->execute();
+ if (nRet == css::ui::dialogs::ExecutableDialogResults::OK)
+ {
+ m_sSavePath = xFolderPicker->getDirectory();
+ OUString sPath;
+ osl::FileBase::getSystemPathFromFileURL(m_sSavePath, sPath);
+ m_xSaveDirED->set_text(sPath);
+ }
+}
+
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/dialog/fntctrl.cxx b/svx/source/dialog/fntctrl.cxx
new file mode 100644
index 000000000..393fc4e68
--- /dev/null
+++ b/svx/source/dialog/fntctrl.cxx
@@ -0,0 +1,1089 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * 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/dialoghelper.hxx>
+#include <sfx2/viewsh.hxx>
+#include <sfx2/printer.hxx>
+#include <vcl/metric.hxx>
+#include <vcl/svapp.hxx>
+#include <vcl/settings.hxx>
+#include <unicode/uchar.h>
+#include <com/sun/star/uno/Reference.h>
+#include <com/sun/star/i18n/BreakIterator.hpp>
+#include <comphelper/processfactory.hxx>
+
+#include <com/sun/star/i18n/ScriptType.hpp>
+
+#include <vector>
+#include <deque>
+#include <optional>
+#include <svtools/colorcfg.hxx>
+#include <svtools/sampletext.hxx>
+
+#include <svx/fntctrl.hxx>
+#include <svx/svxids.hrc>
+
+// Item set includes
+#include <svl/itemset.hxx>
+#include <svl/itempool.hxx>
+#include <svl/stritem.hxx>
+#include <svl/cjkoptions.hxx>
+#include <svl/ctloptions.hxx>
+
+#include <editeng/colritem.hxx>
+#include <editeng/fontitem.hxx>
+#include <editeng/editids.hrc>
+#include <editeng/postitem.hxx>
+#include <editeng/udlnitem.hxx>
+#include <editeng/crossedoutitem.hxx>
+#include <editeng/contouritem.hxx>
+#include <editeng/wghtitem.hxx>
+#include <editeng/fhgtitem.hxx>
+#include <editeng/shdditem.hxx>
+#include <editeng/escapementitem.hxx>
+#include <editeng/wrlmitem.hxx>
+#include <editeng/cmapitem.hxx>
+#include <editeng/kernitem.hxx>
+#include <editeng/brushitem.hxx>
+#include <editeng/emphasismarkitem.hxx>
+#include <editeng/charreliefitem.hxx>
+#include <editeng/charscaleitem.hxx>
+#include <editeng/langitem.hxx>
+
+//TODO: remove this and calculate off the actual size of text, not
+//an arbitrary number of characters
+#define TEXT_WIDTH 80
+
+using namespace ::com::sun::star::uno;
+using namespace ::com::sun::star::lang;
+using ::com::sun::star::i18n::XBreakIterator;
+using ::com::sun::star::i18n::BreakIterator;
+
+
+// small helper functions to set fonts
+
+namespace
+{
+void scaleFontWidth(vcl::Font& rFont, vcl::RenderContext const & rRenderContext,tools::Long& n100PercentFont)
+{
+ rFont.SetAverageFontWidth(0);
+ n100PercentFont = rRenderContext.GetFontMetric(rFont).GetAverageFontWidth();
+}
+
+void initFont(vcl::Font& rFont)
+{
+ rFont.SetTransparent(true);
+ rFont.SetAlignment(ALIGN_BASELINE);
+}
+
+void setFontSize(vcl::Font& rFont)
+{
+ Size aSize(rFont.GetFontSize());
+ aSize.setHeight( (aSize.Height() * 3) / 5 );
+ aSize.setWidth( (aSize.Width() * 3) / 5 );
+ rFont.SetFontSize(aSize);
+}
+
+void calcFontHeightAnyAscent(vcl::RenderContext& rRenderContext, const vcl::Font& rFont, tools::Long& nHeight, tools::Long& nAscent)
+{
+ if (!nHeight)
+ {
+ rRenderContext.SetFont(rFont);
+ FontMetric aMetric(rRenderContext.GetFontMetric());
+ nHeight = aMetric.GetLineHeight();
+ nAscent = aMetric.GetAscent();
+ }
+}
+
+void setFont(const SvxFont& rNewFont, SvxFont& rImplFont)
+{
+ rImplFont = rNewFont;
+ rImplFont.SetTransparent(true);
+ rImplFont.SetAlignment(ALIGN_BASELINE);
+}
+
+/*
+ * removes line feeds and carriage returns from string
+ * returns if param is empty
+ */
+OUString removeCRLF(const OUString& rText)
+{
+ return rText.replace(0xa, ' ').replace(0xd, ' ').trim();
+}
+
+} // end anonymous namespace
+
+class FontPrevWin_Impl
+{
+ friend class SvxFontPrevWindow;
+
+ SvxFont maFont;
+ VclPtr<Printer> mpPrinter;
+ bool mbDelPrinter;
+
+ Reference <XBreakIterator> mxBreak;
+ std::vector<tools::Long> maTextWidth;
+ std::deque<sal_Int32> maScriptChg;
+ std::vector<sal_uInt16> maScriptType;
+ SvxFont maCJKFont;
+ SvxFont maCTLFont;
+ OUString maText;
+ OUString maScriptText;
+ std::optional<Color> mxColor;
+ std::optional<Color> mxBackColor;
+ std::optional<Color> mxTextLineColor;
+ std::optional<Color> mxOverlineColor;
+ tools::Long mnAscent;
+ sal_Unicode mcStartBracket;
+ sal_Unicode mcEndBracket;
+
+ tools::Long mn100PercentFontWidth; // initial -1 -> not set yet
+ tools::Long mn100PercentFontWidthCJK;
+ tools::Long mn100PercentFontWidthCTL;
+ sal_uInt16 mnFontWidthScale;
+
+ bool mbSelection : 1;
+ bool mbGetSelection : 1;
+ bool mbTwoLines : 1;
+ bool mbUseFontNameAsText : 1;
+ bool mbTextInited : 1;
+
+ bool m_bCJKEnabled;
+ bool m_bCTLEnabled;
+
+
+public:
+ FontPrevWin_Impl() :
+ mpPrinter(nullptr),
+ mbDelPrinter(false),
+ mnAscent(0),
+ mcStartBracket(0),
+ mcEndBracket(0),
+ mnFontWidthScale(100),
+ mbSelection(false),
+ mbGetSelection(false),
+ mbTwoLines(false),
+ mbUseFontNameAsText(false),
+ mbTextInited(false)
+ {
+ m_bCJKEnabled = SvtCJKOptions::IsAnyEnabled();
+ m_bCTLEnabled = SvtCTLOptions().IsCTLFontEnabled();
+ mxBackColor = svtools::ColorConfig().GetColorValue(svtools::DOCCOLOR).nColor;
+ Invalidate100PercentFontWidth();
+ }
+
+ ~FontPrevWin_Impl()
+ {
+ if (mbDelPrinter)
+ mpPrinter.disposeAndClear();
+ }
+
+ void CheckScript();
+ Size CalcTextSize(vcl::RenderContext& rRenderContext, OutputDevice const * pPrinter, const SvxFont& rFont);
+ void DrawPrev(vcl::RenderContext& rRenderContext, Printer* pPrinter, Point& rPt, const SvxFont& rFont);
+
+ bool SetFontWidthScale(sal_uInt16 nScaleInPercent);
+ inline void Invalidate100PercentFontWidth();
+ inline bool Is100PercentFontWidthValid() const;
+ void ScaleFontWidth(vcl::RenderContext const & rRenderContext);
+ // scales rNonCJKFont and aCJKFont depending on nFontWidthScale and
+ // sets the 100%-Font-Widths
+};
+
+inline void FontPrevWin_Impl::Invalidate100PercentFontWidth()
+{
+ mn100PercentFontWidth = mn100PercentFontWidthCJK = mn100PercentFontWidthCTL = -1;
+}
+
+inline bool FontPrevWin_Impl::Is100PercentFontWidthValid() const
+{
+ DBG_ASSERT( ( mn100PercentFontWidth == -1 && mn100PercentFontWidthCJK == -1 ) ||
+ ( mn100PercentFontWidth != -1 && mn100PercentFontWidthCJK != -1 ) ||
+ ( mn100PercentFontWidth == -1 && mn100PercentFontWidthCTL == -1 ) ||
+ ( mn100PercentFontWidth != -1 && mn100PercentFontWidthCTL != -1 ),
+ "*FontPrevWin_Impl::Is100PercentFontWidthValid(): 100PercentFontWidth's not synchronous" );
+ return mn100PercentFontWidth != -1;
+}
+
+/*
+ * evaluates the scripttypes of the actual string.
+ * Afterwards the positions of script change are notified in aScriptChg,
+ * the scripttypes in aScriptType.
+ * The aTextWidth array will be filled with zero.
+ */
+void FontPrevWin_Impl::CheckScript()
+{
+ assert(!maText.isEmpty()); // must have a preview text here!
+ if (maText == maScriptText)
+ {
+ return; // already initialized
+ }
+
+ maScriptText = maText;
+
+ maScriptChg.clear();
+ maScriptType.clear();
+ maTextWidth.clear();
+
+ if (!mxBreak.is())
+ {
+ Reference<XComponentContext> xContext = ::comphelper::getProcessComponentContext();
+ mxBreak = BreakIterator::create(xContext);
+ }
+
+ sal_uInt16 nScript = 0;
+ sal_Int32 nChg = 0;
+
+ while (nChg < maText.getLength())
+ {
+ nScript = mxBreak->getScriptType(maText, nChg);
+ nChg = mxBreak->endOfScript(maText, nChg, nScript);
+ if (nChg < maText.getLength() && nChg > 0 &&
+ (css::i18n::ScriptType::WEAK ==
+ mxBreak->getScriptType(maText, nChg - 1)))
+ {
+ int8_t nType = u_charType(maText[nChg]);
+ if (nType == U_NON_SPACING_MARK || nType == U_ENCLOSING_MARK ||
+ nType == U_COMBINING_SPACING_MARK)
+ {
+ maScriptChg.push_back(nChg - 1);
+ }
+ else
+ {
+ maScriptChg.push_back(nChg);
+ }
+ }
+ else
+ {
+ maScriptChg.push_back(nChg);
+ }
+ maScriptType.push_back(nScript);
+ maTextWidth.push_back(0);
+ }
+}
+
+/*
+ * Size FontPrevWin_Impl::CalcTextSize(..)
+ * fills the aTextWidth array with the text width of every part
+ * of the actual string without a script change inside.
+ * For Latin parts the given rFont will be used,
+ * for Asian parts the aCJKFont.
+ * The returned size contains the whole string.
+ * The member nAscent is calculated to the maximal ascent of all used fonts.
+ */
+
+Size FontPrevWin_Impl::CalcTextSize(vcl::RenderContext& rRenderContext, OutputDevice const * _pPrinter, const SvxFont& rInFont)
+{
+ sal_uInt16 nScript;
+ sal_uInt16 nIdx = 0;
+ sal_Int32 nStart = 0;
+ sal_Int32 nEnd;
+ size_t nCnt = maScriptChg.size();
+
+ if (nCnt)
+ {
+ nEnd = maScriptChg[nIdx];
+ nScript = maScriptType[nIdx];
+ }
+ else
+ {
+ nEnd = maText.getLength();
+ nScript = css::i18n::ScriptType::LATIN;
+ }
+ tools::Long nTxtWidth = 0;
+ tools::Long nCJKHeight = 0;
+ tools::Long nCTLHeight = 0;
+ tools::Long nHeight = 0;
+ mnAscent = 0;
+ tools::Long nCJKAscent = 0;
+ tools::Long nCTLAscent = 0;
+
+ do
+ {
+ const SvxFont& rFont = (nScript == css::i18n::ScriptType::ASIAN) ?
+ maCJKFont :
+ ((nScript == css::i18n::ScriptType::COMPLEX) ?
+ maCTLFont :
+ rInFont);
+ tools::Long nWidth = rFont.GetTextSize(*_pPrinter, maText, nStart, nEnd - nStart).Width();
+ if (nIdx >= maTextWidth.size())
+ break;
+
+ maTextWidth[nIdx++] = nWidth;
+ nTxtWidth += nWidth;
+
+ switch (nScript)
+ {
+ case css::i18n::ScriptType::ASIAN:
+ calcFontHeightAnyAscent(rRenderContext, maCJKFont, nCJKHeight, nCJKAscent);
+ break;
+ case css::i18n::ScriptType::COMPLEX:
+ calcFontHeightAnyAscent(rRenderContext, maCTLFont, nCTLHeight, nCTLAscent);
+ break;
+ default:
+ calcFontHeightAnyAscent(rRenderContext, rFont, nHeight, mnAscent);
+ }
+
+ if (nEnd < maText.getLength() && nIdx < nCnt)
+ {
+ nStart = nEnd;
+ nEnd = maScriptChg[nIdx];
+ nScript = maScriptType[nIdx];
+ }
+ else
+ break;
+ }
+ while(true);
+
+ nHeight -= mnAscent;
+ nCJKHeight -= nCJKAscent;
+ nCTLHeight -= nCTLAscent;
+
+ if (nHeight < nCJKHeight)
+ nHeight = nCJKHeight;
+
+ if (mnAscent < nCJKAscent)
+ mnAscent = nCJKAscent;
+
+ if (nHeight < nCTLHeight)
+ nHeight = nCTLHeight;
+
+ if (mnAscent < nCTLAscent)
+ mnAscent = nCTLAscent;
+
+ nHeight += mnAscent;
+
+ Size aTxtSize(nTxtWidth, nHeight);
+ return aTxtSize;
+}
+
+/*
+ * void FontPrevWin_Impl::DrawPrev(..)
+ * calls SvxFont::DrawPrev(..) for every part of the string without a script
+ * change inside, for Asian parts the aCJKFont will be used, otherwise the
+ * given rFont.
+ */
+
+void FontPrevWin_Impl::DrawPrev(vcl::RenderContext& rRenderContext, Printer* _pPrinter, Point &rPt, const SvxFont& rInFont)
+{
+ vcl::Font aOldFont = _pPrinter->GetFont();
+ sal_uInt16 nScript;
+ sal_uInt16 nIdx = 0;
+ sal_Int32 nStart = 0;
+ sal_Int32 nEnd;
+ size_t nCnt = maScriptChg.size();
+ if (nCnt)
+ {
+ nEnd = maScriptChg[nIdx];
+ nScript = maScriptType[nIdx];
+ }
+ else
+ {
+ nEnd = maText.getLength();
+ nScript = css::i18n::ScriptType::LATIN;
+ }
+ do
+ {
+ const SvxFont& rFont = (nScript == css::i18n::ScriptType::ASIAN)
+ ? maCJKFont
+ : ((nScript == css::i18n::ScriptType::COMPLEX)
+ ? maCTLFont
+ : rInFont);
+ _pPrinter->SetFont(rFont);
+
+ rFont.DrawPrev(&rRenderContext, _pPrinter, rPt, maText, nStart, nEnd - nStart);
+
+ rPt.AdjustX(maTextWidth[nIdx++] );
+ if (nEnd < maText.getLength() && nIdx < nCnt)
+ {
+ nStart = nEnd;
+ nEnd = maScriptChg[nIdx];
+ nScript = maScriptType[nIdx];
+ }
+ else
+ break;
+ }
+ while(true);
+ _pPrinter->SetFont(aOldFont);
+}
+
+
+bool FontPrevWin_Impl::SetFontWidthScale(sal_uInt16 nScale)
+{
+ if (mnFontWidthScale != nScale)
+ {
+ mnFontWidthScale = nScale;
+ return true;
+ }
+
+ return false;
+}
+
+void FontPrevWin_Impl::ScaleFontWidth(vcl::RenderContext const & rOutDev)
+{
+ if (!Is100PercentFontWidthValid())
+ {
+ scaleFontWidth(maFont, rOutDev, mn100PercentFontWidth);
+ scaleFontWidth(maCJKFont, rOutDev, mn100PercentFontWidthCJK);
+ scaleFontWidth(maCTLFont, rOutDev, mn100PercentFontWidthCTL);
+ }
+
+ maFont.SetAverageFontWidth(mn100PercentFontWidth * mnFontWidthScale / 100);
+ maCJKFont.SetAverageFontWidth(mn100PercentFontWidthCJK * mnFontWidthScale / 100);
+ maCTLFont.SetAverageFontWidth(mn100PercentFontWidthCTL * mnFontWidthScale / 100);
+}
+
+static bool GetWhich (const SfxItemSet& rSet, sal_uInt16 nSlot, sal_uInt16& rWhich)
+{
+ rWhich = rSet.GetPool()->GetWhich(nSlot);
+ return rSet.GetItemState(rWhich) >= SfxItemState::DEFAULT;
+}
+
+static void SetPrevFont(const SfxItemSet& rSet, sal_uInt16 nSlot, SvxFont& rFont)
+{
+ sal_uInt16 nWhich;
+ if (GetWhich(rSet, nSlot, nWhich))
+ {
+ const SvxFontItem& rFontItem = static_cast<const SvxFontItem&>(rSet.Get(nWhich));
+ rFont.SetFamily(rFontItem.GetFamily());
+ rFont.SetFamilyName(rFontItem.GetFamilyName());
+ rFont.SetPitch(rFontItem.GetPitch());
+ rFont.SetCharSet(rFontItem.GetCharSet());
+ rFont.SetStyleName(rFontItem.GetStyleName());
+ }
+}
+
+static void SetPrevFontStyle( const SfxItemSet& rSet, sal_uInt16 nPosture, sal_uInt16 nWeight, SvxFont& rFont )
+{
+ sal_uInt16 nWhich;
+ if( GetWhich( rSet, nPosture, nWhich ) )
+ {
+ const SvxPostureItem& rItem = static_cast<const SvxPostureItem&>( rSet.Get( nWhich ) );
+ rFont.SetItalic( rItem.GetValue() != ITALIC_NONE ? ITALIC_NORMAL : ITALIC_NONE );
+ }
+
+ if( GetWhich( rSet, nWeight, nWhich ) )
+ {
+ const SvxWeightItem& rItem = static_cast<const SvxWeightItem&>( rSet.Get( nWhich ) );
+ rFont.SetWeight( rItem.GetValue() != WEIGHT_NORMAL ? WEIGHT_BOLD : WEIGHT_NORMAL );
+ }
+}
+
+static void SetPrevFontEscapement(SvxFont& rFont, sal_uInt8 nProp, sal_uInt8 nEscProp, short nEsc)
+{
+ rFont.SetPropr(nProp);
+ rFont.SetProprRel(nEscProp);
+ rFont.SetEscapement(nEsc);
+}
+
+void SvxFontPrevWindow::ApplySettings(vcl::RenderContext& rRenderContext)
+{
+ rRenderContext.SetTextColor( svtools::ColorConfig().GetColorValue(svtools::FONTCOLOR).nColor );
+ rRenderContext.SetBackground( svtools::ColorConfig().GetColorValue(svtools::DOCCOLOR).nColor );
+}
+
+void SvxFontPrevWindow::SetDrawingArea(weld::DrawingArea* pDrawingArea)
+{
+ CustomWidgetController::SetDrawingArea(pDrawingArea);
+ Size aPrefSize(getPreviewStripSize(pDrawingArea->get_ref_device()));
+ pDrawingArea->set_size_request(aPrefSize.Width(), aPrefSize.Height());
+
+ pImpl.reset(new FontPrevWin_Impl);
+ SfxViewShell* pSh = SfxViewShell::Current();
+
+ if (pSh)
+ pImpl->mpPrinter = pSh->GetPrinter();
+
+ if (!pImpl->mpPrinter)
+ {
+ pImpl->mpPrinter = VclPtr<Printer>::Create();
+ pImpl->mbDelPrinter = true;
+ }
+ initFont(pImpl->maFont);
+ initFont(pImpl->maCJKFont);
+ initFont(pImpl->maCTLFont);
+
+ Invalidate();
+}
+
+SvxFontPrevWindow::SvxFontPrevWindow()
+{
+}
+
+SvxFontPrevWindow::~SvxFontPrevWindow()
+{
+}
+
+SvxFont& SvxFontPrevWindow::GetCTLFont()
+{
+ return pImpl->maCTLFont;
+}
+
+SvxFont& SvxFontPrevWindow::GetCJKFont()
+{
+ return pImpl->maCJKFont;
+}
+
+SvxFont& SvxFontPrevWindow::GetFont()
+{
+ pImpl->Invalidate100PercentFontWidth(); // because the user might change the size
+ return pImpl->maFont;
+}
+
+const SvxFont& SvxFontPrevWindow::GetFont() const
+{
+ return pImpl->maFont;
+}
+
+void SvxFontPrevWindow::SetPreviewText( const OUString& rString )
+{
+ pImpl->maText = rString;
+ pImpl->mbTextInited = true;
+}
+
+void SvxFontPrevWindow::SetFontNameAsPreviewText()
+{
+ pImpl->mbUseFontNameAsText = true;
+}
+
+void SvxFontPrevWindow::SetFont( const SvxFont& rNormalOutFont, const SvxFont& rCJKOutFont, const SvxFont& rCTLFont )
+{
+ setFont(rNormalOutFont, pImpl->maFont);
+ setFont(rCJKOutFont, pImpl->maCJKFont);
+ setFont(rCTLFont, pImpl->maCTLFont);
+
+ pImpl->Invalidate100PercentFontWidth();
+ Invalidate();
+}
+
+void SvxFontPrevWindow::SetColor(const Color &rColor)
+{
+ pImpl->mxColor = rColor;
+ Invalidate();
+}
+
+void SvxFontPrevWindow::ResetColor()
+{
+ pImpl->mxColor.reset();
+ Invalidate();
+}
+
+void SvxFontPrevWindow::SetTextLineColor(const Color &rColor)
+{
+ pImpl->mxTextLineColor = rColor;
+ Invalidate();
+}
+
+void SvxFontPrevWindow::SetOverlineColor(const Color &rColor)
+{
+ pImpl->mxOverlineColor = rColor;
+ Invalidate();
+}
+
+void SvxFontPrevWindow::Paint(vcl::RenderContext& rRenderContext, const tools::Rectangle&)
+{
+ rRenderContext.Push(vcl::PushFlags::ALL);
+ rRenderContext.SetMapMode(MapMode(MapUnit::MapTwip));
+
+ ApplySettings(rRenderContext);
+
+ Printer* pPrinter = pImpl->mpPrinter;
+ const SvxFont& rFont = pImpl->maFont;
+ const SvxFont& rCJKFont = pImpl->maCJKFont;
+ const SvxFont& rCTLFont = pImpl->maCTLFont;
+
+ if (!IsEnabled())
+ {
+ const StyleSettings& rStyleSettings = Application::GetSettings().GetStyleSettings();
+ const Size aLogSize(rRenderContext.GetOutputSize());
+
+ tools::Rectangle aRect(Point(0, 0), aLogSize);
+ rRenderContext.SetLineColor();
+ rRenderContext.SetFillColor(rStyleSettings.GetWindowColor());
+ rRenderContext.DrawRect(aRect);
+ }
+ else
+ {
+ if (!pImpl->mbSelection && !pImpl->mbTextInited)
+ {
+ using namespace css::i18n::ScriptType;
+
+ SfxViewShell* pSh = SfxViewShell::Current();
+
+ if (pSh && !pImpl->mbGetSelection && !pImpl->mbUseFontNameAsText)
+ {
+ pImpl->maText = removeCRLF(pSh->GetSelectionText(/*bCompleteWords*/false, /*bOnlyASample*/true));
+ pImpl->mbGetSelection = true;
+ pImpl->mbSelection = !(pImpl->maText.isEmpty());
+ }
+
+ if (!pImpl->mbSelection || pImpl->mbUseFontNameAsText)
+ {
+ //If we're showing multiple sample texts, then they're all
+ //sample texts. If only showing Latin, continue to use
+ //the fontname as the preview
+ if ((pImpl->m_bCJKEnabled) || (pImpl->m_bCTLEnabled))
+ pImpl->maText = makeRepresentativeTextForFont(LATIN, rFont);
+ else
+ pImpl->maText = rFont.GetFamilyName();
+
+ if (pImpl->m_bCJKEnabled)
+ {
+ if (!pImpl->maText.isEmpty())
+ pImpl->maText += " ";
+ pImpl->maText += makeRepresentativeTextForFont(ASIAN, rCJKFont);
+
+ }
+ if (pImpl->m_bCTLEnabled)
+ {
+ if (!pImpl->maText.isEmpty())
+ pImpl->maText += " ";
+ pImpl->maText += makeRepresentativeTextForFont(COMPLEX, rCTLFont);
+ }
+ }
+
+ if (pImpl->maText.isEmpty())
+ { // fdo#58427: still no text? let's try that one...
+ pImpl->maText = makeRepresentativeTextForFont(LATIN, rFont);
+ }
+
+ pImpl->maText = removeCRLF(pImpl->maText);
+
+ if (pImpl->maText.getLength() > (TEXT_WIDTH - 1))
+ {
+ const sal_Int32 nSpaceIdx = pImpl->maText.indexOf(" ", TEXT_WIDTH);
+ if (nSpaceIdx != -1)
+ pImpl->maText = pImpl->maText.copy(0, nSpaceIdx);
+ else
+ pImpl->maText = pImpl->maText.copy(0, (TEXT_WIDTH - 1));
+ }
+ }
+
+ // calculate text width scaling
+ pImpl->ScaleFontWidth(rRenderContext);
+
+ pImpl->CheckScript();
+ Size aTxtSize = pImpl->CalcTextSize(rRenderContext, pPrinter, rFont);
+
+ const Size aLogSize(rRenderContext.GetOutputSize());
+
+ tools::Long nX = aLogSize.Width() / 2 - aTxtSize.Width() / 2;
+ tools::Long nY = aLogSize.Height() / 2 - aTxtSize.Height() / 2;
+
+ if (nY + pImpl->mnAscent > aLogSize.Height())
+ nY = aLogSize.Height() - pImpl->mnAscent;
+
+ if (pImpl->mxBackColor)
+ {
+ tools::Rectangle aRect(Point(0, 0), aLogSize);
+ Color aLineCol = rRenderContext.GetLineColor();
+ Color aFillCol = rRenderContext.GetFillColor();
+ rRenderContext.SetLineColor();
+ rRenderContext.SetFillColor(*pImpl->mxBackColor);
+ rRenderContext.DrawRect(aRect);
+ rRenderContext.SetLineColor(aLineCol);
+ rRenderContext.SetFillColor(aFillCol);
+ }
+ if (pImpl->mxColor)
+ {
+ tools::Rectangle aRect(Point(nX, nY), aTxtSize);
+ Color aLineCol = rRenderContext.GetLineColor();
+ Color aFillCol = rRenderContext.GetFillColor();
+ rRenderContext.SetLineColor();
+ rRenderContext.SetFillColor(*pImpl->mxColor);
+ rRenderContext.DrawRect(aRect);
+ rRenderContext.SetLineColor(aLineCol);
+ rRenderContext.SetFillColor(aFillCol);
+ }
+
+ if (pImpl->mxTextLineColor)
+ {
+ rRenderContext.SetTextLineColor(*pImpl->mxTextLineColor);
+ }
+
+ if (pImpl->mxOverlineColor)
+ {
+ rRenderContext.SetOverlineColor(*pImpl->mxOverlineColor);
+ }
+
+ tools::Long nStdAscent = pImpl->mnAscent;
+ nY += nStdAscent;
+
+ if (IsTwoLines())
+ {
+ SvxFont aSmallFont(rFont);
+ Size aOldSize = pImpl->maCJKFont.GetFontSize();
+ setFontSize(aSmallFont);
+ setFontSize(pImpl->maCJKFont);
+
+ tools::Long nStartBracketWidth = 0;
+ tools::Long nEndBracketWidth = 0;
+ tools::Long nTextWidth = 0;
+ if (pImpl->mcStartBracket)
+ {
+ OUString sBracket(pImpl->mcStartBracket);
+ nStartBracketWidth = rFont.GetTextSize(*pPrinter, sBracket).Width();
+ }
+ if (pImpl->mcEndBracket)
+ {
+ OUString sBracket(pImpl->mcEndBracket);
+ nEndBracketWidth = rFont.GetTextSize(*pPrinter, sBracket).Width();
+ }
+ nTextWidth = pImpl->CalcTextSize(rRenderContext, pPrinter, aSmallFont).Width();
+ tools::Long nResultWidth = nStartBracketWidth;
+ nResultWidth += nEndBracketWidth;
+ nResultWidth += nTextWidth;
+
+ tools::Long _nX = (aLogSize.Width() - nResultWidth) / 2;
+ rRenderContext.DrawLine(Point(0, nY), Point(_nX, nY));
+ rRenderContext.DrawLine(Point(_nX + nResultWidth, nY), Point(aLogSize.Width(), nY));
+
+ tools::Long nSmallAscent = pImpl->mnAscent;
+ tools::Long nOffset = (nStdAscent - nSmallAscent) / 2;
+
+ if (pImpl->mcStartBracket)
+ {
+ OUString sBracket(pImpl->mcStartBracket);
+ rFont.DrawPrev(&rRenderContext, pPrinter, Point(_nX, nY - nOffset - 4), sBracket);
+ _nX += nStartBracketWidth;
+ }
+
+ Point aTmpPoint1(_nX, nY - nSmallAscent - 2);
+ Point aTmpPoint2(_nX, nY);
+ pImpl->DrawPrev(rRenderContext, pPrinter, aTmpPoint1, aSmallFont);
+ pImpl->DrawPrev(rRenderContext, pPrinter, aTmpPoint2, aSmallFont);
+
+ _nX += nTextWidth;
+ if (pImpl->mcEndBracket)
+ {
+ Point aTmpPoint( _nX + 1, nY - nOffset - 4);
+ OUString sBracket(pImpl->mcEndBracket);
+ rFont.DrawPrev(&rRenderContext, pPrinter, aTmpPoint, sBracket);
+ }
+ pImpl->maCJKFont.SetFontSize(aOldSize);
+ }
+ else
+ {
+
+ Color aLineCol = rRenderContext.GetLineColor();
+
+ rRenderContext.SetLineColor(rFont.GetColor());
+ rRenderContext.DrawLine(Point(0, nY), Point(nX, nY));
+ rRenderContext.DrawLine(Point(nX + aTxtSize.Width(), nY), Point(aLogSize.Width(), nY));
+ rRenderContext.SetLineColor(aLineCol);
+
+ Point aTmpPoint(nX, nY);
+ pImpl->DrawPrev(rRenderContext, pPrinter, aTmpPoint, rFont);
+ }
+ }
+ rRenderContext.Pop();
+}
+
+bool SvxFontPrevWindow::IsTwoLines() const
+{
+ return pImpl->mbTwoLines;
+}
+
+void SvxFontPrevWindow::SetTwoLines(bool bSet)
+{
+ pImpl->mbTwoLines = bSet;
+}
+
+void SvxFontPrevWindow::SetBrackets(sal_Unicode cStart, sal_Unicode cEnd)
+{
+ pImpl->mcStartBracket = cStart;
+ pImpl->mcEndBracket = cEnd;
+}
+
+void SvxFontPrevWindow::SetFontWidthScale( sal_uInt16 n )
+{
+ if (pImpl->SetFontWidthScale(n))
+ Invalidate();
+}
+
+void SvxFontPrevWindow::AutoCorrectFontColor()
+{
+ Color aColor(COL_AUTO);
+ if ( pImpl->mxBackColor ) aColor = *pImpl->mxBackColor;
+ const bool bIsDark(aColor.IsDark());
+
+ aColor = pImpl->maFont.GetColor();
+ if (aColor == COL_AUTO)
+ pImpl->maFont.SetColor( bIsDark ? COL_WHITE : COL_BLACK );
+ aColor = pImpl->maCJKFont.GetColor();
+ if (aColor == COL_AUTO)
+ pImpl->maCJKFont.SetColor( bIsDark ? COL_WHITE : COL_BLACK );
+ aColor = pImpl->maCTLFont.GetColor();
+ if (aColor == COL_AUTO)
+ pImpl->maCTLFont.SetColor( bIsDark ? COL_WHITE : COL_BLACK );
+}
+
+void SvxFontPrevWindow::SetFontSize( const SfxItemSet& rSet, sal_uInt16 nSlot, SvxFont& rFont )
+{
+ sal_uInt16 nWhich;
+ tools::Long nH;
+ if (GetWhich(rSet, nSlot, nWhich))
+ {
+ nH = OutputDevice::LogicToLogic(static_cast<const SvxFontHeightItem&>(rSet.Get(nWhich)).GetHeight(),
+ rSet.GetPool()->GetMetric(nWhich),
+ MapUnit::MapTwip);
+ }
+ else
+ nH = 240;// as default 12pt
+
+ rFont.SetFontSize(Size(0, nH));
+}
+
+void SvxFontPrevWindow::SetFontLang(const SfxItemSet& rSet, sal_uInt16 nSlot, SvxFont& rFont)
+{
+ sal_uInt16 nWhich;
+ LanguageType nLang;
+ if( GetWhich( rSet, nSlot, nWhich ) )
+ nLang = static_cast<const SvxLanguageItem&>(rSet.Get(nWhich)).GetLanguage();
+ else
+ nLang = LANGUAGE_NONE;
+ rFont.SetLanguage(nLang);
+}
+
+void SvxFontPrevWindow::SetFromItemSet(const SfxItemSet &rSet, bool bPreviewBackgroundToCharacter)
+{
+ sal_uInt16 nWhich;
+ SvxFont& rFont = GetFont();
+ SvxFont& rCJKFont = GetCJKFont();
+ SvxFont& rCTLFont = GetCTLFont();
+
+ // Preview string
+ if( GetWhich( rSet, SID_CHAR_DLG_PREVIEW_STRING, nWhich ) )
+ {
+ const SfxStringItem& rItem = static_cast<const SfxStringItem&>( rSet.Get( nWhich ) );
+ const OUString& aString = rItem.GetValue();
+ if( !aString.isEmpty() )
+ SetPreviewText( aString );
+ else
+ SetFontNameAsPreviewText();
+ }
+
+ // Underline
+ FontLineStyle eUnderline;
+ if( GetWhich( rSet, SID_ATTR_CHAR_UNDERLINE, nWhich ) )
+ {
+ const SvxUnderlineItem& rItem = static_cast<const SvxUnderlineItem&>( rSet.Get( nWhich ) );
+ eUnderline = rItem.GetValue();
+ }
+ else
+ eUnderline = LINESTYLE_NONE;
+
+ rFont.SetUnderline( eUnderline );
+ rCJKFont.SetUnderline( eUnderline );
+ rCTLFont.SetUnderline( eUnderline );
+
+ // Overline
+ FontLineStyle eOverline;
+ if( GetWhich( rSet, SID_ATTR_CHAR_OVERLINE, nWhich ) )
+ {
+ const SvxOverlineItem& rItem = static_cast<const SvxOverlineItem&>( rSet.Get( nWhich ) );
+ eOverline = rItem.GetValue();
+ }
+ else
+ eOverline = LINESTYLE_NONE;
+
+ rFont.SetOverline( eOverline );
+ rCJKFont.SetOverline( eOverline );
+ rCTLFont.SetOverline( eOverline );
+
+ // Strikeout
+ FontStrikeout eStrikeout;
+ if( GetWhich( rSet, SID_ATTR_CHAR_STRIKEOUT, nWhich ) )
+ {
+ const SvxCrossedOutItem& rItem = static_cast<const SvxCrossedOutItem&>( rSet.Get( nWhich ) );
+ eStrikeout = rItem.GetValue();
+ }
+ else
+ eStrikeout = STRIKEOUT_NONE;
+
+ rFont.SetStrikeout( eStrikeout );
+ rCJKFont.SetStrikeout( eStrikeout );
+ rCTLFont.SetStrikeout( eStrikeout );
+
+ // WordLineMode
+ if( GetWhich( rSet, SID_ATTR_CHAR_WORDLINEMODE, nWhich ) )
+ {
+ const SvxWordLineModeItem& rItem = static_cast<const SvxWordLineModeItem&>( rSet.Get( nWhich ) );
+ rFont.SetWordLineMode( rItem.GetValue() );
+ rCJKFont.SetWordLineMode( rItem.GetValue() );
+ rCTLFont.SetWordLineMode( rItem.GetValue() );
+ }
+
+ // Emphasis
+ if( GetWhich( rSet, SID_ATTR_CHAR_EMPHASISMARK, nWhich ) )
+ {
+ const SvxEmphasisMarkItem& rItem = static_cast<const SvxEmphasisMarkItem&>( rSet.Get( nWhich ) );
+ FontEmphasisMark eMark = rItem.GetEmphasisMark();
+ rFont.SetEmphasisMark( eMark );
+ rCJKFont.SetEmphasisMark( eMark );
+ rCTLFont.SetEmphasisMark( eMark );
+ }
+
+ // Relief
+ if( GetWhich( rSet, SID_ATTR_CHAR_RELIEF, nWhich ) )
+ {
+ const SvxCharReliefItem& rItem = static_cast<const SvxCharReliefItem&>( rSet.Get( nWhich ) );
+ FontRelief eFontRelief = rItem.GetValue();
+ rFont.SetRelief( eFontRelief );
+ rCJKFont.SetRelief( eFontRelief );
+ rCTLFont.SetRelief( eFontRelief );
+ }
+
+ // Effects
+ if( GetWhich( rSet, SID_ATTR_CHAR_CASEMAP, nWhich ) )
+ {
+ const SvxCaseMapItem& rItem = static_cast<const SvxCaseMapItem&>( rSet.Get( nWhich ) );
+ SvxCaseMap eCaseMap = rItem.GetValue();
+ rFont.SetCaseMap( eCaseMap );
+ rCJKFont.SetCaseMap( eCaseMap );
+ // #i78474# small caps do not exist in CTL fonts
+ rCTLFont.SetCaseMap( eCaseMap == SvxCaseMap::SmallCaps ? SvxCaseMap::NotMapped : eCaseMap );
+ }
+
+ // Outline
+ if( GetWhich( rSet, SID_ATTR_CHAR_CONTOUR, nWhich ) )
+ {
+ const SvxContourItem& rItem = static_cast<const SvxContourItem&>( rSet.Get( nWhich ) );
+ bool bOutline = rItem.GetValue();
+ rFont.SetOutline( bOutline );
+ rCJKFont.SetOutline( bOutline );
+ rCTLFont.SetOutline( bOutline );
+ }
+
+ // Shadow
+ if( GetWhich( rSet, SID_ATTR_CHAR_SHADOWED, nWhich ) )
+ {
+ const SvxShadowedItem& rItem = static_cast<const SvxShadowedItem&>( rSet.Get( nWhich ) );
+ bool bShadow = rItem.GetValue();
+ rFont.SetShadow( bShadow );
+ rCJKFont.SetShadow( bShadow );
+ rCTLFont.SetShadow( bShadow );
+ }
+
+ // Background
+ bool bTransparent;
+ if( GetWhich( rSet, bPreviewBackgroundToCharacter ? SID_ATTR_BRUSH : SID_ATTR_BRUSH_CHAR, nWhich ) )
+ {
+ const SvxBrushItem& rBrush = static_cast<const SvxBrushItem&>( rSet.Get( nWhich ) );
+ const Color& rColor = rBrush.GetColor();
+ bTransparent = rColor.IsTransparent();
+ rFont.SetFillColor( rColor );
+ rCJKFont.SetFillColor( rColor );
+ rCTLFont.SetFillColor( rColor );
+ }
+ else
+ bTransparent = true;
+
+ rFont.SetTransparent( bTransparent );
+ rCJKFont.SetTransparent( bTransparent );
+ rCTLFont.SetTransparent( bTransparent );
+
+ if( !bPreviewBackgroundToCharacter )
+ {
+ if( GetWhich( rSet, SID_ATTR_BRUSH, nWhich ) )
+ {
+ const SvxBrushItem& rBrush = static_cast<const SvxBrushItem&>( rSet.Get( nWhich ) );
+ if( GPOS_NONE == rBrush.GetGraphicPos() )
+ pImpl->mxBackColor = rBrush.GetColor();
+ }
+ }
+
+ // Font
+ SetPrevFont( rSet, SID_ATTR_CHAR_FONT, rFont );
+ SetPrevFont( rSet, SID_ATTR_CHAR_CJK_FONT, rCJKFont );
+ SetPrevFont( rSet, SID_ATTR_CHAR_CTL_FONT, rCTLFont );
+
+ // Style
+ SetPrevFontStyle( rSet, SID_ATTR_CHAR_POSTURE, SID_ATTR_CHAR_WEIGHT, rFont );
+ SetPrevFontStyle( rSet, SID_ATTR_CHAR_CJK_POSTURE, SID_ATTR_CHAR_CJK_WEIGHT, rCJKFont );
+ SetPrevFontStyle( rSet, SID_ATTR_CHAR_CTL_POSTURE, SID_ATTR_CHAR_CTL_WEIGHT, rCTLFont );
+
+ // Size
+ SetFontSize( rSet, SID_ATTR_CHAR_FONTHEIGHT, rFont );
+ SetFontSize( rSet, SID_ATTR_CHAR_CJK_FONTHEIGHT, rCJKFont );
+ SetFontSize( rSet, SID_ATTR_CHAR_CTL_FONTHEIGHT, rCTLFont );
+
+ // Language
+ SetFontLang( rSet, SID_ATTR_CHAR_LANGUAGE, rFont );
+ SetFontLang( rSet, SID_ATTR_CHAR_CJK_LANGUAGE, rCJKFont );
+ SetFontLang( rSet, SID_ATTR_CHAR_CTL_LANGUAGE, rCTLFont );
+
+ // Color
+ if( GetWhich( rSet, SID_ATTR_CHAR_COLOR, nWhich ) )
+ {
+ const SvxColorItem& rItem = static_cast<const SvxColorItem&>( rSet.Get( nWhich ) );
+ Color aCol( rItem.GetValue() );
+ rFont.SetColor( aCol );
+
+ rCJKFont.SetColor( aCol );
+ rCTLFont.SetColor( aCol );
+
+ AutoCorrectFontColor(); // handle color COL_AUTO
+ }
+
+ // Kerning
+ if( GetWhich( rSet, SID_ATTR_CHAR_KERNING, nWhich ) )
+ {
+ const SvxKerningItem& rItem = static_cast<const SvxKerningItem&>( rSet.Get( nWhich ) );
+ short nKern = static_cast<short>(OutputDevice::LogicToLogic(rItem.GetValue(), rSet.GetPool()->GetMetric(nWhich), MapUnit::MapTwip));
+ rFont.SetFixKerning( nKern );
+ rCJKFont.SetFixKerning( nKern );
+ rCTLFont.SetFixKerning( nKern );
+ }
+
+ // Escapement
+ const sal_uInt8 nProp = 100;
+ short nEsc;
+ sal_uInt8 nEscProp;
+ if( GetWhich( rSet, SID_ATTR_CHAR_ESCAPEMENT, nWhich ) )
+ {
+ const SvxEscapementItem& rItem = static_cast<const SvxEscapementItem&>( rSet.Get( nWhich ) );
+ nEsc = rItem.GetEsc();
+ nEscProp = rItem.GetProportionalHeight();
+
+ if( nEsc == DFLT_ESC_AUTO_SUPER )
+ nEsc = DFLT_ESC_SUPER;
+ else if( nEsc == DFLT_ESC_AUTO_SUB )
+ nEsc = DFLT_ESC_SUB;
+ }
+ else
+ {
+ nEsc = 0;
+ nEscProp = 100;
+ }
+ SetPrevFontEscapement( rFont, nProp, nEscProp, nEsc );
+ SetPrevFontEscapement( rCJKFont, nProp, nEscProp, nEsc );
+ SetPrevFontEscapement( rCTLFont, nProp, nEscProp, nEsc );
+
+ // Font width scale
+ if( GetWhich( rSet, SID_ATTR_CHAR_SCALEWIDTH, nWhich ) )
+ {
+ const SvxCharScaleWidthItem&rItem = static_cast<const SvxCharScaleWidthItem&>( rSet.Get( nWhich ) );
+ SetFontWidthScale( rItem.GetValue() );
+ }
+
+ Invalidate();
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/dialog/fontwork.cxx b/svx/source/dialog/fontwork.cxx
new file mode 100644
index 000000000..af1f25649
--- /dev/null
+++ b/svx/source/dialog/fontwork.cxx
@@ -0,0 +1,794 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * 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/module.hxx>
+#include <sfx2/dispatch.hxx>
+
+#include <svx/colorbox.hxx>
+#include <svx/dialmgr.hxx>
+#include <svx/strings.hrc>
+#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/xtextit0.hxx>
+
+#include <svtools/unitconv.hxx>
+#include <svx/svxids.hrc>
+#include <bitmaps.hlst>
+#include <svx/fontwork.hxx>
+
+#define WIDTH_CHARS 10
+
+SFX_IMPL_DOCKINGWINDOW_WITHID( SvxFontWorkChildWindow, SID_FONTWORK );
+
+// ControllerItem for Fontwork
+
+SvxFontWorkControllerItem::SvxFontWorkControllerItem
+(
+ sal_uInt16 _nId,
+ SvxFontWorkDialog& rDlg,
+ SfxBindings& rBindings
+) :
+
+ SfxControllerItem( _nId, rBindings ),
+
+ rFontWorkDlg( rDlg )
+{
+}
+
+// StateChanged method for FontWork items
+
+void SvxFontWorkControllerItem::StateChangedAtToolBoxControl( sal_uInt16 /*nSID*/, SfxItemState /*eState*/,
+ const SfxPoolItem* pItem )
+{
+ switch ( GetId() )
+ {
+ case SID_FORMTEXT_STYLE:
+ {
+ const XFormTextStyleItem* pStateItem =
+ dynamic_cast<const XFormTextStyleItem*>( pItem );
+ DBG_ASSERT(pStateItem || pItem == nullptr, "XFormTextStyleItem expected");
+ rFontWorkDlg.SetStyle_Impl(pStateItem);
+ break;
+ }
+ case SID_FORMTEXT_ADJUST:
+ {
+ const XFormTextAdjustItem* pStateItem =
+ dynamic_cast<const XFormTextAdjustItem*>( pItem );
+ DBG_ASSERT(pStateItem || pItem == nullptr, "XFormTextAdjustItem expected");
+ rFontWorkDlg.SetAdjust_Impl(pStateItem);
+ break;
+ }
+ case SID_FORMTEXT_DISTANCE:
+ {
+ const XFormTextDistanceItem* pStateItem =
+ dynamic_cast<const XFormTextDistanceItem*>( pItem );
+ DBG_ASSERT(pStateItem || pItem == nullptr, "XFormTextDistanceItem expected");
+ rFontWorkDlg.SetDistance_Impl(pStateItem);
+ break;
+ }
+ case SID_FORMTEXT_START:
+ {
+ const XFormTextStartItem* pStateItem =
+ dynamic_cast<const XFormTextStartItem*>( pItem );
+ DBG_ASSERT(pStateItem || pItem == nullptr, "XFormTextStartItem expected");
+ rFontWorkDlg.SetStart_Impl(pStateItem);
+ break;
+ }
+ case SID_FORMTEXT_MIRROR:
+ {
+ const XFormTextMirrorItem* pStateItem =
+ dynamic_cast<const XFormTextMirrorItem*>( pItem );
+ DBG_ASSERT(pStateItem || pItem == nullptr, "XFormTextMirrorItem expected");
+ rFontWorkDlg.SetMirror_Impl(pStateItem);
+ break;
+ }
+ case SID_FORMTEXT_HIDEFORM:
+ {
+ const XFormTextHideFormItem* pStateItem =
+ dynamic_cast<const XFormTextHideFormItem*>( pItem );
+ DBG_ASSERT(pStateItem || pItem == nullptr, "XFormTextHideFormItem expected");
+ rFontWorkDlg.SetShowForm_Impl(pStateItem);
+ break;
+ }
+ case SID_FORMTEXT_OUTLINE:
+ {
+ const XFormTextOutlineItem* pStateItem =
+ dynamic_cast<const XFormTextOutlineItem*>( pItem );
+ DBG_ASSERT(pStateItem || pItem == nullptr, "XFormTextOutlineItem expected");
+ rFontWorkDlg.SetOutline_Impl(pStateItem);
+ break;
+ }
+ case SID_FORMTEXT_SHADOW:
+ {
+ const XFormTextShadowItem* pStateItem =
+ dynamic_cast<const XFormTextShadowItem*>( pItem );
+ DBG_ASSERT(pStateItem || pItem == nullptr, "XFormTextShadowItem expected");
+ rFontWorkDlg.SetShadow_Impl(pStateItem);
+ break;
+ }
+ case SID_FORMTEXT_SHDWCOLOR:
+ {
+ const XFormTextShadowColorItem* pStateItem =
+ dynamic_cast<const XFormTextShadowColorItem*>( pItem );
+ DBG_ASSERT(pStateItem || pItem == nullptr, "XFormTextShadowColorItem expected");
+ rFontWorkDlg.SetShadowColor_Impl(pStateItem);
+ break;
+ }
+ case SID_FORMTEXT_SHDWXVAL:
+ {
+ const XFormTextShadowXValItem* pStateItem =
+ dynamic_cast<const XFormTextShadowXValItem*>( pItem );
+ DBG_ASSERT(pStateItem || pItem == nullptr, "XFormTextShadowXValItem expected");
+ rFontWorkDlg.SetShadowXVal_Impl(pStateItem);
+ break;
+ }
+ case SID_FORMTEXT_SHDWYVAL:
+ {
+ const XFormTextShadowYValItem* pStateItem =
+ dynamic_cast<const XFormTextShadowYValItem*>( pItem );
+ DBG_ASSERT(pStateItem || pItem == nullptr, "XFormTextShadowYValItem expected");
+ rFontWorkDlg.SetShadowYVal_Impl(pStateItem);
+ break;
+ }
+ }
+}
+
+// Derivation from SfxChildWindow as "containers" for Fontwork dialog
+
+SvxFontWorkChildWindow::SvxFontWorkChildWindow
+(
+ vcl::Window* _pParent,
+ sal_uInt16 nId,
+ SfxBindings* pBindings,
+ SfxChildWinInfo* pInfo
+) :
+ SfxChildWindow( _pParent, nId )
+{
+ VclPtrInstance<SvxFontWorkDialog> pDlg(pBindings, this, _pParent);
+ SetWindow(pDlg);
+
+ pDlg->Initialize( pInfo );
+}
+
+// Floating Window to the attribution of text effects
+SvxFontWorkDialog::SvxFontWorkDialog(SfxBindings *pBindinx,
+ SfxChildWindow *pCW,
+ vcl::Window* _pParent)
+ : SfxDockingWindow(pBindinx, pCW, _pParent, "DockingFontwork", "svx/ui/dockingfontwork.ui")
+ , rBindings(*pBindinx)
+ , aInputIdle("SvxFontWorkDialog Input")
+ , nSaveShadowX(0)
+ , nSaveShadowY(0)
+ , nSaveShadowAngle(450)
+ , nSaveShadowSize (100)
+ , m_xTbxStyle(m_xBuilder->weld_toolbar("style"))
+ , m_xTbxAdjust(m_xBuilder->weld_toolbar("adjust"))
+ , m_xMtrFldDistance(m_xBuilder->weld_metric_spin_button("distance", FieldUnit::CM))
+ , m_xMtrFldTextStart(m_xBuilder->weld_metric_spin_button("indent", FieldUnit::CM))
+ , m_xTbxShadow(m_xBuilder->weld_toolbar("shadow"))
+ , m_xFbShadowX(m_xBuilder->weld_image("shadowx"))
+ , m_xMtrFldShadowX(m_xBuilder->weld_metric_spin_button("distancex", FieldUnit::CM))
+ , m_xFbShadowY(m_xBuilder->weld_image("shadowy"))
+ , m_xMtrFldShadowY(m_xBuilder->weld_metric_spin_button("distancey", FieldUnit::CM))
+ , m_xShadowColorLB(new ColorListBox(m_xBuilder->weld_menu_button("color"), [this]{ return GetFrameWeld(); } ))
+{
+ SetText(SvxResId(RID_SVXSTR_FONTWORK));
+
+ ApplyImageList();
+
+ pCtrlItems[0] = new SvxFontWorkControllerItem(SID_FORMTEXT_STYLE, *this, rBindings);
+ pCtrlItems[1] = new SvxFontWorkControllerItem(SID_FORMTEXT_ADJUST, *this, rBindings);
+ pCtrlItems[2] = new SvxFontWorkControllerItem(SID_FORMTEXT_DISTANCE, *this, rBindings);
+ pCtrlItems[3] = new SvxFontWorkControllerItem(SID_FORMTEXT_START, *this, rBindings);
+ pCtrlItems[4] = new SvxFontWorkControllerItem(SID_FORMTEXT_MIRROR, *this, rBindings);
+ pCtrlItems[5] = new SvxFontWorkControllerItem(SID_FORMTEXT_HIDEFORM, *this, rBindings);
+ pCtrlItems[6] = new SvxFontWorkControllerItem(SID_FORMTEXT_OUTLINE, *this, rBindings);
+ pCtrlItems[7] = new SvxFontWorkControllerItem(SID_FORMTEXT_SHADOW, *this, rBindings);
+ pCtrlItems[8] = new SvxFontWorkControllerItem(SID_FORMTEXT_SHDWCOLOR, *this, rBindings);
+ pCtrlItems[9] = new SvxFontWorkControllerItem(SID_FORMTEXT_SHDWXVAL, *this, rBindings);
+ pCtrlItems[10] = new SvxFontWorkControllerItem(SID_FORMTEXT_SHDWYVAL, *this, rBindings);
+
+ m_xTbxStyle->connect_clicked(LINK(this, SvxFontWorkDialog, SelectStyleHdl_Impl));
+ m_xTbxAdjust->connect_clicked(LINK(this, SvxFontWorkDialog, SelectAdjustHdl_Impl));
+ m_xTbxShadow->connect_clicked(LINK(this, SvxFontWorkDialog, SelectShadowHdl_Impl));
+
+ Link<weld::MetricSpinButton&,void> aLink = LINK(this, SvxFontWorkDialog, ModifyInputHdl_Impl);
+ m_xMtrFldDistance->connect_value_changed( aLink );
+ m_xMtrFldTextStart->connect_value_changed( aLink );
+ m_xMtrFldShadowX->connect_value_changed( aLink );
+ m_xMtrFldShadowY->connect_value_changed( aLink );
+
+ // Set System metric
+ const FieldUnit eDlgUnit = rBindings.GetDispatcher()->GetModule()->GetFieldUnit();
+ SetFieldUnit(*m_xMtrFldDistance, eDlgUnit, true);
+ SetFieldUnit(*m_xMtrFldTextStart, eDlgUnit, true);
+ SetFieldUnit(*m_xMtrFldShadowX, eDlgUnit, true);
+ SetFieldUnit(*m_xMtrFldShadowY, eDlgUnit, true);
+ if( eDlgUnit == FieldUnit::MM )
+ {
+ m_xMtrFldDistance->set_increments(50, 500, FieldUnit::NONE);
+ m_xMtrFldTextStart->set_increments(50, 500, FieldUnit::NONE);
+ m_xMtrFldShadowX->set_increments(50, 500, FieldUnit::NONE);
+ m_xMtrFldShadowY->set_increments(50, 500, FieldUnit::NONE);
+ }
+ else
+ {
+ m_xMtrFldDistance->set_increments(10, 100, FieldUnit::NONE);
+ m_xMtrFldTextStart->set_increments(10, 100, FieldUnit::NONE);
+ m_xMtrFldShadowX->set_increments(10, 100, FieldUnit::NONE);
+ m_xMtrFldShadowY->set_increments(10, 100, FieldUnit::NONE);
+ }
+ m_xMtrFldShadowX->set_width_chars(WIDTH_CHARS);
+ m_xMtrFldShadowY->set_width_chars(WIDTH_CHARS);
+
+ m_xShadowColorLB->SetSelectHdl( LINK(this, SvxFontWorkDialog, ColorSelectHdl_Impl) );
+
+ aInputIdle.SetPriority(TaskPriority::LOWEST);
+ aInputIdle.SetInvokeHandler(LINK(this, SvxFontWorkDialog, InputTimeoutHdl_Impl));
+}
+
+SvxFontWorkDialog::~SvxFontWorkDialog()
+{
+ disposeOnce();
+}
+
+void SvxFontWorkDialog::dispose()
+{
+ for (SvxFontWorkControllerItem* pCtrlItem : pCtrlItems)
+ pCtrlItem->dispose();
+ m_xTbxStyle.reset();
+ m_xTbxAdjust.reset();
+ m_xMtrFldDistance.reset();
+ m_xMtrFldTextStart.reset();
+ m_xTbxShadow.reset();
+ m_xFbShadowX.reset();
+ m_xMtrFldShadowX.reset();
+ m_xFbShadowY.reset();
+ m_xMtrFldShadowY.reset();
+ m_xShadowColorLB.reset();
+ SfxDockingWindow::dispose();
+}
+
+SfxChildAlignment SvxFontWorkDialog::CheckAlignment( SfxChildAlignment eActAlign,
+ SfxChildAlignment eAlign )
+{
+ SfxChildAlignment eAlignment;
+
+ switch ( eAlign )
+ {
+ case SfxChildAlignment::TOP:
+ case SfxChildAlignment::HIGHESTTOP:
+ case SfxChildAlignment::LOWESTTOP:
+ case SfxChildAlignment::BOTTOM:
+ case SfxChildAlignment::LOWESTBOTTOM:
+ case SfxChildAlignment::HIGHESTBOTTOM:
+ {
+ eAlignment = eActAlign;
+ }
+ break;
+
+ case SfxChildAlignment::LEFT:
+ case SfxChildAlignment::RIGHT:
+ case SfxChildAlignment::FIRSTLEFT:
+ case SfxChildAlignment::LASTLEFT:
+ case SfxChildAlignment::FIRSTRIGHT:
+ case SfxChildAlignment::LASTRIGHT:
+ {
+ eAlignment = eAlign;
+ }
+ break;
+
+ default:
+ {
+ eAlignment = eAlign;
+ }
+ break;
+ }
+
+ return eAlignment;
+}
+
+// Set style buttons
+void SvxFontWorkDialog::SetStyle_Impl(const XFormTextStyleItem* pItem)
+{
+ if ( pItem )
+ {
+ OString sId = "off";
+
+ switch ( pItem->GetValue() )
+ {
+ case XFormTextStyle::Rotate : sId = "rotate"; break;
+ case XFormTextStyle::Upright: sId = "upright"; break;
+ case XFormTextStyle::SlantX : sId = "hori"; break;
+ case XFormTextStyle::SlantY : sId = "vert"; break;
+ default: ;//prevent warning
+ }
+ m_xTbxStyle->set_sensitive(true);
+
+ // Make sure that there is always exactly one checked toolbox item.
+ if ( pItem->GetValue() == XFormTextStyle::NONE )
+ {
+ m_xTbxStyle->set_item_active("rotate", false);
+ m_xTbxStyle->set_item_active("upright", false);
+ m_xTbxStyle->set_item_active("hori", false);
+ m_xTbxStyle->set_item_active("vert", false);
+
+ m_xTbxStyle->set_item_active("off", true);
+ }
+ else
+ {
+ m_xTbxStyle->set_item_active("off", false);
+ m_xTbxStyle->set_item_active(sId, true);
+ }
+
+ m_sLastStyleTbxId = sId;
+ }
+ else
+ m_xTbxStyle->set_sensitive(false);
+}
+
+// Set adjust buttons
+
+void SvxFontWorkDialog::SetAdjust_Impl(const XFormTextAdjustItem* pItem)
+{
+ if ( pItem )
+ {
+ OString sId;
+
+ m_xTbxAdjust->set_sensitive(true);
+ m_xMtrFldDistance->set_sensitive(true);
+
+ if ( pItem->GetValue() == XFormTextAdjust::Left || pItem->GetValue() == XFormTextAdjust::Right )
+ {
+ if (pItem->GetValue() == XFormTextAdjust::Left)
+ sId = "left";
+ else
+ sId = "right";
+ m_xMtrFldTextStart->set_sensitive(true);
+ }
+ else
+ {
+ if (pItem->GetValue() == XFormTextAdjust::Center)
+ sId = "center";
+ else
+ sId = "autosize";
+ m_xMtrFldTextStart->set_sensitive(false);
+ }
+
+ if (!m_xTbxAdjust->get_item_active(sId))
+ m_xTbxAdjust->set_item_active(sId, true);
+
+ m_sLastAdjustTbxId = sId;
+ }
+ else
+ {
+ m_xTbxAdjust->set_sensitive(false);
+ m_xMtrFldTextStart->set_sensitive(false);
+ m_xMtrFldDistance->set_sensitive(false);
+ }
+}
+
+// Enter Distance value in the edit field
+
+void SvxFontWorkDialog::SetDistance_Impl(const XFormTextDistanceItem* pItem)
+{
+ if (pItem && !m_xMtrFldDistance->has_focus())
+ {
+ SetMetricValue(*m_xMtrFldDistance, pItem->GetValue(), MapUnit::Map100thMM);
+ }
+}
+
+// Enter indent value in the edit field
+
+void SvxFontWorkDialog::SetStart_Impl(const XFormTextStartItem* pItem)
+{
+ if (pItem && !m_xMtrFldTextStart->has_focus())
+ {
+ SetMetricValue(*m_xMtrFldTextStart, pItem->GetValue(), MapUnit::Map100thMM);
+ }
+}
+
+// Set button for reversing the direction of text
+
+void SvxFontWorkDialog::SetMirror_Impl(const XFormTextMirrorItem* pItem)
+{
+ if ( pItem )
+ m_xTbxAdjust->set_item_active("orientation", pItem->GetValue());
+}
+
+// Set button for contour display
+
+void SvxFontWorkDialog::SetShowForm_Impl(const XFormTextHideFormItem* pItem)
+{
+ if ( pItem )
+ m_xTbxShadow->set_item_active("contour", !pItem->GetValue());
+}
+
+// Set button for text border
+
+void SvxFontWorkDialog::SetOutline_Impl(const XFormTextOutlineItem* pItem)
+{
+ if ( pItem )
+ m_xTbxShadow->set_item_active("textcontour", pItem->GetValue());
+}
+
+// Set shadow buttons
+
+void SvxFontWorkDialog::SetShadow_Impl(const XFormTextShadowItem* pItem,
+ bool bRestoreValues)
+{
+ if ( pItem )
+ {
+ OString sId;
+
+ m_xTbxShadow->set_sensitive(true);
+
+ if ( pItem->GetValue() == XFormTextShadow::NONE )
+ {
+ sId = "noshadow";
+ m_xFbShadowX->hide();
+ m_xFbShadowY->hide();
+ m_xMtrFldShadowX->set_sensitive(false);
+ m_xMtrFldShadowY->set_sensitive(false);
+ m_xShadowColorLB->set_sensitive(false);
+ }
+ else
+ {
+ m_xFbShadowX->show();
+ m_xFbShadowY->show();
+ m_xMtrFldShadowX->set_sensitive(true);
+ m_xMtrFldShadowY->set_sensitive(true);
+ m_xShadowColorLB->set_sensitive(true);
+
+ if ( pItem->GetValue() == XFormTextShadow::Normal )
+ {
+ sId = "vertical";
+ const FieldUnit eDlgUnit = rBindings.GetDispatcher()->GetModule()->GetFieldUnit();
+
+ m_xMtrFldShadowX->set_unit( eDlgUnit );
+ m_xMtrFldShadowX->set_digits(2);
+ m_xMtrFldShadowX->set_range(INT_MIN, INT_MAX, FieldUnit::NONE);
+ if( eDlgUnit == FieldUnit::MM )
+ m_xMtrFldShadowX->set_increments(50, 500, FieldUnit::NONE);
+ else
+ m_xMtrFldShadowX->set_increments(10, 100, FieldUnit::NONE);
+ m_xMtrFldShadowX->set_width_chars(WIDTH_CHARS);
+
+ m_xMtrFldShadowY->set_unit( eDlgUnit );
+ m_xMtrFldShadowY->set_digits(2);
+ m_xMtrFldShadowY->set_range(INT_MIN, INT_MAX, FieldUnit::NONE);
+ if( eDlgUnit == FieldUnit::MM )
+ m_xMtrFldShadowY->set_increments(50, 500, FieldUnit::NONE);
+ else
+ m_xMtrFldShadowY->set_increments(10, 100, FieldUnit::NONE);
+ m_xMtrFldShadowY->set_width_chars(WIDTH_CHARS);
+
+ if ( bRestoreValues )
+ {
+ SetMetricValue(*m_xMtrFldShadowX, nSaveShadowX, MapUnit::Map100thMM);
+ SetMetricValue(*m_xMtrFldShadowY, nSaveShadowY, MapUnit::Map100thMM);
+
+ XFormTextShadowXValItem aXItem( nSaveShadowX );
+ XFormTextShadowYValItem aYItem( nSaveShadowY );
+
+ GetBindings().GetDispatcher()->ExecuteList(
+ SID_FORMTEXT_SHDWXVAL, SfxCallMode::RECORD,
+ { &aXItem, &aYItem });
+ }
+ }
+ else
+ {
+ sId = "slant";
+
+ m_xMtrFldShadowX->set_unit(FieldUnit::DEGREE);
+ m_xMtrFldShadowX->set_digits(1);
+ m_xMtrFldShadowX->set_range(-1800, 1800, FieldUnit::NONE);
+ m_xMtrFldShadowX->set_increments(10, 100, FieldUnit::NONE);
+ m_xMtrFldShadowX->set_width_chars(WIDTH_CHARS);
+
+ m_xMtrFldShadowY->set_unit(FieldUnit::PERCENT);
+ m_xMtrFldShadowY->set_digits(0);
+ m_xMtrFldShadowY->set_range(-999, 999, FieldUnit::NONE);
+ m_xMtrFldShadowY->set_increments(10, 100, FieldUnit::NONE);
+ m_xMtrFldShadowY->set_width_chars(WIDTH_CHARS);
+
+ if ( bRestoreValues )
+ {
+ m_xMtrFldShadowX->set_value(nSaveShadowAngle, FieldUnit::NONE);
+ m_xMtrFldShadowY->set_value(nSaveShadowSize, FieldUnit::NONE);
+ XFormTextShadowXValItem aXItem(nSaveShadowAngle);
+ XFormTextShadowYValItem aYItem(nSaveShadowSize);
+ GetBindings().GetDispatcher()->ExecuteList(
+ SID_FORMTEXT_SHDWXVAL, SfxCallMode::RECORD,
+ { &aXItem, &aYItem });
+ }
+ }
+ }
+
+ if (!m_xTbxShadow->get_item_active(sId))
+ m_xTbxShadow->set_item_active(sId, true);
+ m_sLastShadowTbxId = sId;
+
+ ApplyImageList();
+ }
+ else
+ {
+ m_xTbxShadow->set_sensitive(false);
+ m_xMtrFldShadowX->set_sensitive(false);
+ m_xMtrFldShadowY->set_sensitive(false);
+ m_xShadowColorLB->set_sensitive(false);
+ }
+}
+
+// Insert shadow color in listbox
+
+void SvxFontWorkDialog::SetShadowColor_Impl(const XFormTextShadowColorItem* pItem)
+{
+ if ( pItem )
+ m_xShadowColorLB->SelectEntry(pItem->GetColorValue());
+}
+
+// Enter X-value for shadow in edit field
+void SvxFontWorkDialog::SetShadowXVal_Impl(const XFormTextShadowXValItem* pItem)
+{
+ if (!pItem || m_xMtrFldShadowX->has_focus())
+ return;
+
+ // #i19251#
+ // sal_Int32 nValue = pItem->GetValue();
+
+ // #i19251#
+ // The two involved fields/items are used double and contain/give different
+ // values regarding to the access method. Thus, here we need to separate the access
+ // methods regarding to the kind of value accessed.
+ if (m_xTbxShadow->get_item_active("slant"))
+ {
+ // #i19251#
+ // There is no value correction necessary at all, i think this
+ // was only tried to be done without understanding that the two
+ // involved fields/items are used double and contain/give different
+ // values regarding to the access method.
+ // nValue = nValue - ( int( float( nValue ) / 360.0 ) * 360 );
+ m_xMtrFldShadowX->set_value(pItem->GetValue(), FieldUnit::NONE);
+ }
+ else
+ {
+ SetMetricValue(*m_xMtrFldShadowX, pItem->GetValue(), MapUnit::Map100thMM);
+ }
+}
+
+// Enter Y-value for shadow in edit field
+void SvxFontWorkDialog::SetShadowYVal_Impl(const XFormTextShadowYValItem* pItem)
+{
+ if (!pItem || m_xMtrFldShadowY->has_focus())
+ return;
+
+ // #i19251#
+ // The two involved fields/items are used double and contain/give different
+ // values regarding to the access method. Thus, here we need to separate the access
+ // methods regarding to the kind of value accessed.
+ if (m_xTbxShadow->get_item_active("slant"))
+ {
+ m_xMtrFldShadowY->set_value(pItem->GetValue(), FieldUnit::NONE);
+ }
+ else
+ {
+ SetMetricValue(*m_xMtrFldShadowY, pItem->GetValue(), MapUnit::Map100thMM);
+ }
+}
+
+IMPL_LINK(SvxFontWorkDialog, SelectStyleHdl_Impl, const OString&, rId, void)
+{
+ // Execute this block when a different toolbox item has been clicked or
+ // when the off item has been clicked. The later is necessary to
+ // override the toolbox behaviour of unchecking the item after second
+ // click on it: One of the items has to be checked at all times (when
+ // enabled that is.)
+ if (rId != "off" && rId == m_sLastStyleTbxId)
+ return;
+
+ XFormTextStyle eStyle = XFormTextStyle::NONE;
+
+ if (rId == "rotate")
+ eStyle = XFormTextStyle::Rotate;
+ else if (rId == "upright")
+ eStyle = XFormTextStyle::Upright;
+ else if (rId == "hori")
+ eStyle = XFormTextStyle::SlantX;
+ else if (rId == "vert")
+ eStyle = XFormTextStyle::SlantY;
+
+ XFormTextStyleItem aItem( eStyle );
+ GetBindings().GetDispatcher()->ExecuteList(SID_FORMTEXT_STYLE,
+ SfxCallMode::RECORD, { &aItem });
+ SetStyle_Impl( &aItem );
+ m_sLastStyleTbxId = rId;
+}
+
+IMPL_LINK(SvxFontWorkDialog, SelectAdjustHdl_Impl, const OString&, rId, void)
+{
+ if (rId == "orientation")
+ {
+ XFormTextMirrorItem aItem(m_xTbxAdjust->get_item_active(rId));
+ GetBindings().GetDispatcher()->ExecuteList(SID_FORMTEXT_MIRROR,
+ SfxCallMode::SLOT, { &aItem });
+ }
+ else if (rId != m_sLastAdjustTbxId)
+ {
+ XFormTextAdjust eAdjust = XFormTextAdjust::AutoSize;
+
+ if (rId == "left")
+ eAdjust = XFormTextAdjust::Left;
+ else if (rId == "center")
+ eAdjust = XFormTextAdjust::Center;
+ else if (rId == "right")
+ eAdjust = XFormTextAdjust::Right;
+
+ XFormTextAdjustItem aItem(eAdjust);
+ GetBindings().GetDispatcher()->ExecuteList(SID_FORMTEXT_ADJUST,
+ SfxCallMode::RECORD, { &aItem });
+ SetAdjust_Impl(&aItem);
+ m_sLastAdjustTbxId = rId;
+ }
+}
+
+IMPL_LINK(SvxFontWorkDialog, SelectShadowHdl_Impl, const OString&, rId, void)
+{
+ if (rId == "contour")
+ {
+ XFormTextHideFormItem aItem(!m_xTbxShadow->get_item_active(rId));
+ GetBindings().GetDispatcher()->ExecuteList(SID_FORMTEXT_HIDEFORM,
+ SfxCallMode::RECORD, { &aItem });
+ }
+ else if (rId == "textcontour")
+ {
+ XFormTextOutlineItem aItem(m_xTbxShadow->get_item_active(rId));
+ GetBindings().GetDispatcher()->ExecuteList(SID_FORMTEXT_OUTLINE,
+ SfxCallMode::RECORD, { &aItem });
+ }
+ else if (rId != m_sLastShadowTbxId)
+ {
+ XFormTextShadow eShadow = XFormTextShadow::NONE;
+
+ if (m_sLastShadowTbxId == "vertical")
+ {
+ nSaveShadowX = GetCoreValue(*m_xMtrFldShadowX, MapUnit::Map100thMM);
+ nSaveShadowY = GetCoreValue(*m_xMtrFldShadowY, MapUnit::Map100thMM);
+ }
+ else if (m_sLastShadowTbxId == "slant")
+ {
+ nSaveShadowAngle = m_xMtrFldShadowX->get_value(FieldUnit::NONE);
+ nSaveShadowSize = m_xMtrFldShadowY->get_value(FieldUnit::NONE);
+ }
+ m_sLastShadowTbxId = rId;
+
+ if ( rId == "vertical") eShadow = XFormTextShadow::Normal;
+ else if (rId == "slant") eShadow = XFormTextShadow::Slant;
+
+ XFormTextShadowItem aItem(eShadow);
+ GetBindings().GetDispatcher()->ExecuteList(SID_FORMTEXT_SHADOW,
+ SfxCallMode::RECORD, { &aItem });
+ SetShadow_Impl(&aItem, true);
+ }
+}
+
+IMPL_LINK_NOARG(SvxFontWorkDialog, ModifyInputHdl_Impl, weld::MetricSpinButton&, void)
+{
+ aInputIdle.Start();
+}
+
+IMPL_LINK_NOARG(SvxFontWorkDialog, InputTimeoutHdl_Impl, Timer*, void)
+{
+ // Possibly set the Metric system again. This should be done with a
+ // listen, this is however not possible at the moment due to compatibility
+ // issues.
+ const FieldUnit eDlgUnit = rBindings.GetDispatcher()->GetModule()->GetFieldUnit();
+ if( eDlgUnit != m_xMtrFldDistance->get_unit() )
+ {
+ SetFieldUnit(*m_xMtrFldDistance, eDlgUnit, true);
+ SetFieldUnit(*m_xMtrFldTextStart, eDlgUnit, true);
+ if (eDlgUnit == FieldUnit::MM)
+ {
+ m_xMtrFldDistance->set_increments(50, 500, FieldUnit::NONE);
+ m_xMtrFldTextStart->set_increments(50, 500, FieldUnit::NONE);
+ }
+ else
+ {
+ m_xMtrFldDistance->set_increments(10, 100, FieldUnit::NONE);
+ m_xMtrFldTextStart->set_increments(10, 100, FieldUnit::NONE);
+ }
+ }
+ if( eDlgUnit != m_xMtrFldShadowX->get_unit() &&
+ m_xTbxShadow->get_item_active("vertical") )
+ {
+ SetFieldUnit(*m_xMtrFldShadowX, eDlgUnit, true);
+ SetFieldUnit(*m_xMtrFldShadowY, eDlgUnit, true);
+
+ if (eDlgUnit == FieldUnit::MM)
+ {
+ m_xMtrFldShadowX->set_increments(50, 500, FieldUnit::NONE);
+ m_xMtrFldShadowY->set_increments(50, 500, FieldUnit::NONE);
+ }
+ else
+ {
+ m_xMtrFldShadowX->set_increments(10, 100, FieldUnit::NONE);
+ m_xMtrFldShadowY->set_increments(10, 100, FieldUnit::NONE);
+ }
+ }
+
+ tools::Long nValue = GetCoreValue(*m_xMtrFldDistance, MapUnit::Map100thMM);
+ XFormTextDistanceItem aDistItem( nValue );
+ nValue = GetCoreValue(*m_xMtrFldTextStart, MapUnit::Map100thMM);
+ XFormTextStartItem aStartItem( nValue );
+
+ sal_Int32 nValueX(0);
+ sal_Int32 nValueY(0);
+
+ // #i19251#
+ // The two involved fields/items are used double and contain/give different
+ // values regarding to the access method. Thus, here we need to separate the access
+ // method regarding to the kind of value accessed.
+ if (m_sLastShadowTbxId == "vertical")
+ {
+ nValueX = GetCoreValue(*m_xMtrFldShadowX, MapUnit::Map100thMM);
+ nValueY = GetCoreValue(*m_xMtrFldShadowY, MapUnit::Map100thMM);
+ }
+ else if (m_sLastShadowTbxId == "slant")
+ {
+ nValueX = m_xMtrFldShadowX->get_value(FieldUnit::NONE);
+ nValueY = m_xMtrFldShadowY->get_value(FieldUnit::NONE);
+ }
+
+ XFormTextShadowXValItem aShadowXItem( nValueX );
+ XFormTextShadowYValItem aShadowYItem( nValueY );
+
+ // Slot-ID does not matter, the Exec method evaluates the entire item set
+ GetBindings().GetDispatcher()->ExecuteList(SID_FORMTEXT_DISTANCE,
+ SfxCallMode::RECORD,
+ { &aDistItem, &aStartItem, &aShadowXItem, &aShadowYItem });
+}
+
+IMPL_LINK_NOARG(SvxFontWorkDialog, ColorSelectHdl_Impl, ColorListBox&, void)
+{
+ XFormTextShadowColorItem aItem( "", m_xShadowColorLB->GetSelectEntryColor() );
+ GetBindings().GetDispatcher()->ExecuteList(SID_FORMTEXT_SHDWCOLOR,
+ SfxCallMode::RECORD, { &aItem });
+}
+
+void SvxFontWorkDialog::ApplyImageList()
+{
+ if (m_sLastShadowTbxId == "slant")
+ {
+ m_xFbShadowX->set_from_icon_name(RID_SVXBMP_SHADOW_ANGLE);
+ m_xFbShadowY->set_from_icon_name(RID_SVXBMP_SHADOW_SIZE);
+ }
+ else
+ {
+ m_xFbShadowX->set_from_icon_name(RID_SVXBMP_SHADOW_XDIST);
+ m_xFbShadowY->set_from_icon_name(RID_SVXBMP_SHADOW_YDIST);
+ }
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/dialog/framelink.cxx b/svx/source/dialog/framelink.cxx
new file mode 100644
index 000000000..77098bd3e
--- /dev/null
+++ b/svx/source/dialog/framelink.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 <sal/config.h>
+
+#include <rtl/math.hxx>
+#include <svx/framelink.hxx>
+
+#include <editeng/borderline.hxx>
+
+
+using namespace ::com::sun::star;
+using namespace editeng;
+
+namespace svx::frame
+{
+
+Style::Style( double nP, double nD, double nS, SvxBorderLineStyle nType, double fScale )
+{
+ Clear();
+ mnType = nType;
+ mfPatternScale = fScale;
+ Set( nP, nD, nS );
+}
+
+Style::Style( const Color& rColorPrim, const Color& rColorSecn, const Color& rColorGap, bool bUseGapColor, double nP, double nD, double nS, SvxBorderLineStyle nType, double fScale )
+{
+ Clear();
+ mnType = nType;
+ mfPatternScale = fScale;
+ Set( rColorPrim, rColorSecn, rColorGap, bUseGapColor, nP, nD, nS );
+}
+
+Style::Style( const editeng::SvxBorderLine* pBorder, double fScale )
+{
+ Clear();
+ if(nullptr != pBorder)
+ {
+ mfPatternScale = fScale;
+ Set( pBorder, fScale );
+ }
+}
+
+void Style::Clear()
+{
+ maColorPrim = Color();
+ maColorSecn = Color();
+ maColorGap = Color();
+ mbUseGapColor = false;
+ meRefMode = RefMode::Centered;
+ mfPrim = 0.0;
+ mfDist = 0.0;
+ mfSecn = 0.0;
+ mfPatternScale = 1.0;
+ mnType = SvxBorderLineStyle::SOLID;
+ mbWordTableCell = false;
+}
+
+void Style::Set( double nP, double nD, double nS )
+{
+ /* nP nD nS -> mfPrim mfDist mfSecn
+ --------------------------------------
+ any any 0 nP 0 0
+ 0 any >0 nS 0 0
+ >0 0 >0 nP 0 0
+ >0 >0 >0 nP nD nS
+ */
+ mfPrim = rtl::math::round(nP ? nP : nS, 2);
+ mfDist = rtl::math::round((nP && nS) ? nD : 0, 2);
+ mfSecn = rtl::math::round((nP && nD) ? nS : 0, 2);
+}
+
+void Style::Set( const Color& rColorPrim, const Color& rColorSecn, const Color& rColorGap, bool bUseGapColor, double nP, double nD, double nS )
+{
+ maColorPrim = rColorPrim;
+ maColorSecn = rColorSecn;
+ maColorGap = rColorGap;
+ mbUseGapColor = bUseGapColor;
+ Set( nP, nD, nS );
+}
+
+void Style::Set( const SvxBorderLine* pBorder, double fScale, sal_uInt16 nMaxWidth )
+{
+ if(nullptr == pBorder)
+ {
+ Clear();
+ return;
+ }
+
+ maColorPrim = pBorder->GetColorOut();
+ maColorSecn = pBorder->GetColorIn();
+ maColorGap = pBorder->GetColorGap();
+ mbUseGapColor = pBorder->HasGapColor();
+
+ const sal_uInt16 nPrim(pBorder->GetOutWidth());
+ const sal_uInt16 nDist(pBorder->GetDistance());
+ const sal_uInt16 nSecn(pBorder->GetInWidth());
+
+ mnType = pBorder->GetBorderLineStyle();
+ mfPatternScale = fScale;
+
+ if( !nSecn ) // no or single frame border
+ {
+ Set( std::min<double>(nPrim * fScale, nMaxWidth), 0, 0 );
+ }
+ else
+ {
+ Set(std::min<double>(nPrim * fScale, nMaxWidth), std::min<double>(nDist * fScale, nMaxWidth), std::min<double>(nSecn * fScale, nMaxWidth));
+ // Enlarge the style if distance is too small due to rounding losses.
+ double nPixWidth = std::min<double>((nPrim + nDist + nSecn) * fScale, nMaxWidth);
+
+ if( nPixWidth > GetWidth() )
+ {
+ mfDist = nPixWidth - mfPrim - mfSecn;
+ }
+
+ // Shrink the style if it is too thick for the control.
+ while( GetWidth() > nMaxWidth )
+ {
+ // First decrease space between lines.
+ if (mfDist)
+ {
+ --mfDist;
+ continue;
+ }
+
+ // Still too thick? Decrease the line widths.
+ if (mfPrim != 0.0 && rtl::math::approxEqual(mfPrim, mfSecn))
+ {
+ // Both lines equal - decrease both to keep symmetry.
+ --mfPrim;
+ --mfSecn;
+ continue;
+ }
+
+ // Decrease each line for itself
+ if (mfPrim)
+ --mfPrim;
+
+ if ((GetWidth() > nMaxWidth) && mfSecn != 0.0)
+ --mfSecn;
+ }
+ }
+}
+
+Style& Style::MirrorSelf()
+{
+ if (mfSecn)
+ {
+ std::swap( mfPrim, mfSecn );
+ // also need to swap colors
+ std::swap( maColorPrim, maColorSecn );
+ }
+
+ if( meRefMode != RefMode::Centered )
+ {
+ meRefMode = (meRefMode == RefMode::Begin) ? RefMode::End : RefMode::Begin;
+ }
+
+ return *this;
+}
+
+bool Style::operator==( const Style& rOther) const
+{
+ return (Prim() == rOther.Prim()
+ && Dist() == rOther.Dist()
+ && Secn() == rOther.Secn()
+ && GetColorPrim() == rOther.GetColorPrim()
+ && GetColorSecn() == rOther.GetColorSecn()
+ && GetColorGap() == rOther.GetColorGap()
+ && GetRefMode() == rOther.GetRefMode()
+ && UseGapColor() == rOther.UseGapColor()
+ && Type() == rOther.Type());
+}
+
+namespace
+{
+/**
+ * Gets the weight of rStyle, according to [MS-OI29500] v20171130, 2.1.168 Part 1 Section 17.4.66,
+ * tcBorders (Table Cell Borders).
+ */
+double GetWordTableCellBorderWeight(const Style& rStyle)
+{
+ double fWidth = rStyle.GetWidth();
+ int nBorderNumber = 0;
+
+ // See lcl_convertBorderStyleFromToken() in writerfilter/ and ConvertBorderStyleFromWord() in
+ // editeng/, this is the opposite of the combination of those functions.
+ switch (rStyle.Type())
+ {
+ case SvxBorderLineStyle::NONE:
+ return 0.0;
+ case SvxBorderLineStyle::DOTTED:
+ case SvxBorderLineStyle::DASHED:
+ return 1.0;
+ case SvxBorderLineStyle::SOLID:
+ // single = 1
+ // thick = 2
+ // wave = 20
+ nBorderNumber = 1;
+ break;
+ case SvxBorderLineStyle::DOUBLE:
+ case SvxBorderLineStyle::DOUBLE_THIN:
+ // double = 3
+ // triple = 10
+ // doubleWave = 21
+ // dashDotStroked = 23
+ nBorderNumber = 3;
+ break;
+ case SvxBorderLineStyle::DASH_DOT:
+ // dotDash = 8
+ nBorderNumber = 8;
+ break;
+ case SvxBorderLineStyle::DASH_DOT_DOT:
+ // dotDotDash = 9
+ nBorderNumber = 9;
+ break;
+ case SvxBorderLineStyle::THINTHICK_SMALLGAP:
+ // thinThickSmallGap = 11
+ nBorderNumber = 11;
+ break;
+ case SvxBorderLineStyle::THICKTHIN_SMALLGAP:
+ // thickThinSmallGap = 12
+ // thinThickThinSmallGap = 13
+ nBorderNumber = 12;
+ break;
+ case SvxBorderLineStyle::THINTHICK_MEDIUMGAP:
+ // thinThickMediumGap = 14
+ nBorderNumber = 14;
+ break;
+ case SvxBorderLineStyle::THICKTHIN_MEDIUMGAP:
+ // thickThinMediumGap = 15
+ // thinThickThinMediumGap = 16
+ nBorderNumber = 15;
+ break;
+ case SvxBorderLineStyle::THINTHICK_LARGEGAP:
+ // thinThickLargeGap = 17
+ nBorderNumber = 17;
+ break;
+ case SvxBorderLineStyle::THICKTHIN_LARGEGAP:
+ // thickThinLargeGap = 18
+ // thinThickThinLargeGap = 19
+ nBorderNumber = 18;
+ break;
+ case SvxBorderLineStyle::FINE_DASHED:
+ // dashSmallGap = 22
+ nBorderNumber = 22;
+ break;
+ case SvxBorderLineStyle::EMBOSSED:
+ // threeDEmboss = 24
+ nBorderNumber = 24;
+ break;
+ case SvxBorderLineStyle::ENGRAVED:
+ // threeDEngrave = 25
+ nBorderNumber = 25;
+ break;
+ case SvxBorderLineStyle::OUTSET:
+ // outset = 26
+ nBorderNumber = 25;
+ break;
+ case SvxBorderLineStyle::INSET:
+ // inset = 27
+ nBorderNumber = 27;
+ break;
+ }
+
+ return nBorderNumber * fWidth;
+}
+}
+
+bool Style::operator<( const Style& rOther) const
+{
+ if (mbWordTableCell)
+ {
+ // The below code would first compare based on the border width, Word compares based on its
+ // calculated weight, do that in the compat case.
+ double fLW = GetWordTableCellBorderWeight(*this);
+ double fRW = GetWordTableCellBorderWeight(rOther);
+ if (!rtl::math::approxEqual(fLW, fRW))
+ {
+ return fLW < fRW;
+ }
+ }
+
+ // different total widths -> this<rOther, if this is thinner
+ double nLW = GetWidth();
+ double nRW = rOther.GetWidth();
+ if( !rtl::math::approxEqual(nLW, nRW) ) return nLW < nRW;
+
+ // one line double, the other single -> this<rOther, if this is single
+ if( (Secn() == 0) != (rOther.Secn() == 0) ) return Secn() == 0;
+
+ // both lines double with different distances -> this<rOther, if distance of this greater
+ if( (Secn() && rOther.Secn()) && !rtl::math::approxEqual(Dist(), rOther.Dist()) ) return Dist() > rOther.Dist();
+
+ // both lines single and 1 unit thick, only one is dotted -> this<rOther, if this is dotted
+ if ((nLW == 1) && !Secn() && !rOther.Secn() && (Type() != rOther.Type())) return Type() > rOther.Type();
+
+ // seem to be equal
+ return false;
+}
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/dialog/framelinkarray.cxx b/svx/source/dialog/framelinkarray.cxx
new file mode 100644
index 000000000..aabce64ba
--- /dev/null
+++ b/svx/source/dialog/framelinkarray.cxx
@@ -0,0 +1,1530 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * 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/framelinkarray.hxx>
+
+#include <math.h>
+#include <vector>
+#include <set>
+#include <algorithm>
+#include <tools/debug.hxx>
+#include <tools/gen.hxx>
+#include <vcl/canvastools.hxx>
+#include <svx/sdr/primitive2d/sdrframeborderprimitive2d.hxx>
+#include <basegfx/matrix/b2dhommatrixtools.hxx>
+#include <basegfx/polygon/b2dpolygonclipper.hxx>
+
+//#define OPTICAL_CHECK_CLIPRANGE_FOR_MERGED_CELL
+#ifdef OPTICAL_CHECK_CLIPRANGE_FOR_MERGED_CELL
+#include <basegfx/polygon/b2dpolygontools.hxx>
+#include <drawinglayer/primitive2d/PolygonHairlinePrimitive2D.hxx>
+#endif
+
+namespace svx::frame {
+
+namespace {
+
+class Cell
+{
+private:
+ Style maLeft;
+ Style maRight;
+ Style maTop;
+ Style maBottom;
+ Style maTLBR;
+ Style maBLTR;
+
+ basegfx::B2DHomMatrix HelperCreateB2DHomMatrixFromB2DRange(
+ const basegfx::B2DRange& rRange ) const;
+
+public:
+ sal_Int32 mnAddLeft;
+ sal_Int32 mnAddRight;
+ sal_Int32 mnAddTop;
+ sal_Int32 mnAddBottom;
+
+ SvxRotateMode meRotMode;
+ double mfOrientation;
+
+ bool mbMergeOrig;
+ bool mbOverlapX;
+ bool mbOverlapY;
+
+public:
+ explicit Cell();
+
+ void SetStyleLeft(const Style& rStyle) { maLeft = rStyle; }
+ void SetStyleRight(const Style& rStyle) { maRight = rStyle; }
+ void SetStyleTop(const Style& rStyle) { maTop = rStyle; }
+ void SetStyleBottom(const Style& rStyle) { maBottom = rStyle; }
+ void SetStyleTLBR(const Style& rStyle) { maTLBR = rStyle; }
+ void SetStyleBLTR(const Style& rStyle) { maBLTR = rStyle; }
+
+ const Style& GetStyleLeft() const { return maLeft; }
+ const Style& GetStyleRight() const { return maRight; }
+ const Style& GetStyleTop() const { return maTop; }
+ const Style& GetStyleBottom() const { return maBottom; }
+ const Style& GetStyleTLBR() const { return maTLBR; }
+ const Style& GetStyleBLTR() const { return maBLTR; }
+
+ bool IsMerged() const { return mbMergeOrig || mbOverlapX || mbOverlapY; }
+ bool IsRotated() const { return mfOrientation != 0.0; }
+
+ void MirrorSelfX();
+
+ basegfx::B2DHomMatrix CreateCoordinateSystemSingleCell(
+ const Array& rArray, sal_Int32 nCol, sal_Int32 nRow ) const;
+ basegfx::B2DHomMatrix CreateCoordinateSystemMergedCell(
+ const Array& rArray, sal_Int32 nColLeft, sal_Int32 nRowTop, sal_Int32 nColRight, sal_Int32 nRowBottom ) const;
+};
+
+}
+
+typedef std::vector< Cell > CellVec;
+
+basegfx::B2DHomMatrix Cell::HelperCreateB2DHomMatrixFromB2DRange(
+ const basegfx::B2DRange& rRange ) const
+{
+ if( rRange.isEmpty() )
+ return basegfx::B2DHomMatrix();
+
+ basegfx::B2DPoint aOrigin(rRange.getMinimum());
+ basegfx::B2DVector aX(rRange.getWidth(), 0.0);
+ basegfx::B2DVector aY(0.0, rRange.getHeight());
+
+ if (IsRotated() && SvxRotateMode::SVX_ROTATE_MODE_STANDARD != meRotMode )
+ {
+ // when rotated, adapt values. Get Skew (cos/sin == 1/tan)
+ const double fSkew(aY.getY() * (cos(mfOrientation) / sin(mfOrientation)));
+
+ switch (meRotMode)
+ {
+ case SvxRotateMode::SVX_ROTATE_MODE_TOP:
+ // shear Y-Axis
+ aY.setX(-fSkew);
+ break;
+ case SvxRotateMode::SVX_ROTATE_MODE_CENTER:
+ // shear origin half, Y full
+ aOrigin.setX(aOrigin.getX() + (fSkew * 0.5));
+ aY.setX(-fSkew);
+ break;
+ case SvxRotateMode::SVX_ROTATE_MODE_BOTTOM:
+ // shear origin full, Y full
+ aOrigin.setX(aOrigin.getX() + fSkew);
+ aY.setX(-fSkew);
+ break;
+ default: // SvxRotateMode::SVX_ROTATE_MODE_STANDARD, already excluded above
+ break;
+ }
+ }
+
+ // use column vectors as coordinate axes, homogen column for translation
+ return basegfx::utils::createCoordinateSystemTransform( aOrigin, aX, aY );
+}
+
+basegfx::B2DHomMatrix Cell::CreateCoordinateSystemSingleCell(
+ const Array& rArray, sal_Int32 nCol, sal_Int32 nRow) const
+{
+ const Point aPoint( rArray.GetColPosition( nCol ), rArray.GetRowPosition( nRow ) );
+ const Size aSize( rArray.GetColWidth( nCol, nCol ) + 1, rArray.GetRowHeight( nRow, nRow ) + 1 );
+ const basegfx::B2DRange aRange( vcl::unotools::b2DRectangleFromRectangle( tools::Rectangle( aPoint, aSize ) ) );
+
+ return HelperCreateB2DHomMatrixFromB2DRange( aRange );
+}
+
+basegfx::B2DHomMatrix Cell::CreateCoordinateSystemMergedCell(
+ const Array& rArray, sal_Int32 nColLeft, sal_Int32 nRowTop, sal_Int32 nColRight, sal_Int32 nRowBottom) const
+{
+ basegfx::B2DRange aRange( rArray.GetB2DRange(
+ nColLeft, nRowTop, nColRight, nRowBottom ) );
+
+ // adjust rectangle for partly visible merged cells
+ if( IsMerged() )
+ {
+ // not *sure* what exactly this is good for,
+ // it is just a hard set extension at merged cells,
+ // probably *should* be included in the above extended
+ // GetColPosition/GetColWidth already. This might be
+ // added due to GetColPosition/GetColWidth not working
+ // correctly over PageChanges (if used), but not sure.
+ aRange.expand(
+ basegfx::B2DRange(
+ aRange.getMinX() - mnAddLeft,
+ aRange.getMinY() - mnAddTop,
+ aRange.getMaxX() + mnAddRight,
+ aRange.getMaxY() + mnAddBottom ) );
+ }
+
+ return HelperCreateB2DHomMatrixFromB2DRange( aRange );
+}
+
+Cell::Cell() :
+ mnAddLeft( 0 ),
+ mnAddRight( 0 ),
+ mnAddTop( 0 ),
+ mnAddBottom( 0 ),
+ meRotMode(SvxRotateMode::SVX_ROTATE_MODE_STANDARD ),
+ mfOrientation( 0.0 ),
+ mbMergeOrig( false ),
+ mbOverlapX( false ),
+ mbOverlapY( false )
+{
+}
+
+void Cell::MirrorSelfX()
+{
+ std::swap( maLeft, maRight );
+ std::swap( mnAddLeft, mnAddRight );
+ maLeft.MirrorSelf();
+ maRight.MirrorSelf();
+ mfOrientation = -mfOrientation;
+}
+
+
+static void lclRecalcCoordVec( std::vector<sal_Int32>& rCoords, const std::vector<sal_Int32>& rSizes )
+{
+ DBG_ASSERT( rCoords.size() == rSizes.size() + 1, "lclRecalcCoordVec - inconsistent vectors" );
+ auto aCIt = rCoords.begin();
+ for( const auto& rSize : rSizes )
+ {
+ *(aCIt + 1) = *aCIt + rSize;
+ ++aCIt;
+ }
+}
+
+static void lclSetMergedRange( CellVec& rCells, sal_Int32 nWidth, sal_Int32 nFirstCol, sal_Int32 nFirstRow, sal_Int32 nLastCol, sal_Int32 nLastRow )
+{
+ for( sal_Int32 nCol = nFirstCol; nCol <= nLastCol; ++nCol )
+ {
+ for( sal_Int32 nRow = nFirstRow; nRow <= nLastRow; ++nRow )
+ {
+ Cell& rCell = rCells[ nRow * nWidth + nCol ];
+ rCell.mbMergeOrig = false;
+ rCell.mbOverlapX = nCol > nFirstCol;
+ rCell.mbOverlapY = nRow > nFirstRow;
+ }
+ }
+ rCells[ nFirstRow * nWidth + nFirstCol ].mbMergeOrig = true;
+}
+
+
+const Style OBJ_STYLE_NONE;
+const Cell OBJ_CELL_NONE;
+
+struct ArrayImpl
+{
+ CellVec maCells;
+ std::vector<sal_Int32> maWidths;
+ std::vector<sal_Int32> maHeights;
+ mutable std::vector<sal_Int32> maXCoords;
+ mutable std::vector<sal_Int32> maYCoords;
+ sal_Int32 mnWidth;
+ sal_Int32 mnHeight;
+ sal_Int32 mnFirstClipCol;
+ sal_Int32 mnFirstClipRow;
+ sal_Int32 mnLastClipCol;
+ sal_Int32 mnLastClipRow;
+ mutable bool mbXCoordsDirty;
+ mutable bool mbYCoordsDirty;
+ bool mbMayHaveCellRotation;
+
+ explicit ArrayImpl( sal_Int32 nWidth, sal_Int32 nHeight );
+
+ bool IsValidPos( sal_Int32 nCol, sal_Int32 nRow ) const
+ { return (nCol < mnWidth) && (nRow < mnHeight); }
+ sal_Int32 GetIndex( sal_Int32 nCol, sal_Int32 nRow ) const
+ { return nRow * mnWidth + nCol; }
+
+ const Cell& GetCell( sal_Int32 nCol, sal_Int32 nRow ) const;
+ Cell& GetCellAcc( sal_Int32 nCol, sal_Int32 nRow );
+
+ sal_Int32 GetMergedFirstCol( sal_Int32 nCol, sal_Int32 nRow ) const;
+ sal_Int32 GetMergedFirstRow( sal_Int32 nCol, sal_Int32 nRow ) const;
+ sal_Int32 GetMergedLastCol( sal_Int32 nCol, sal_Int32 nRow ) const;
+ sal_Int32 GetMergedLastRow( sal_Int32 nCol, sal_Int32 nRow ) const;
+
+ const Cell& GetMergedOriginCell( sal_Int32 nCol, sal_Int32 nRow ) const;
+ const Cell& GetMergedLastCell( sal_Int32 nCol, sal_Int32 nRow ) const;
+
+ bool IsMergedOverlappedLeft( sal_Int32 nCol, sal_Int32 nRow ) const;
+ bool IsMergedOverlappedRight( sal_Int32 nCol, sal_Int32 nRow ) const;
+ bool IsMergedOverlappedTop( sal_Int32 nCol, sal_Int32 nRow ) const;
+ bool IsMergedOverlappedBottom( sal_Int32 nCol, sal_Int32 nRow ) const;
+
+ bool IsInClipRange( sal_Int32 nCol, sal_Int32 nRow ) const;
+ bool IsColInClipRange( sal_Int32 nCol ) const;
+ bool IsRowInClipRange( sal_Int32 nRow ) const;
+
+ bool OverlapsClipRange( sal_Int32 nFirstCol, sal_Int32 nFirstRow, sal_Int32 nLastCol, sal_Int32 nLastRow ) const;
+
+ sal_Int32 GetMirrorCol( sal_Int32 nCol ) const { return mnWidth - nCol - 1; }
+
+ sal_Int32 GetColPosition( sal_Int32 nCol ) const;
+ sal_Int32 GetRowPosition( sal_Int32 nRow ) const;
+
+ bool HasCellRotation() const;
+};
+
+ArrayImpl::ArrayImpl( sal_Int32 nWidth, sal_Int32 nHeight ) :
+ mnWidth( nWidth ),
+ mnHeight( nHeight ),
+ mnFirstClipCol( 0 ),
+ mnFirstClipRow( 0 ),
+ mnLastClipCol( nWidth - 1 ),
+ mnLastClipRow( nHeight - 1 ),
+ mbXCoordsDirty( false ),
+ mbYCoordsDirty( false ),
+ mbMayHaveCellRotation( false )
+{
+ // default-construct all vectors
+ maCells.resize( mnWidth * mnHeight );
+ maWidths.resize( mnWidth, 0 );
+ maHeights.resize( mnHeight, 0 );
+ maXCoords.resize( mnWidth + 1, 0 );
+ maYCoords.resize( mnHeight + 1, 0 );
+}
+
+const Cell& ArrayImpl::GetCell( sal_Int32 nCol, sal_Int32 nRow ) const
+{
+ return IsValidPos( nCol, nRow ) ? maCells[ GetIndex( nCol, nRow ) ] : OBJ_CELL_NONE;
+}
+
+Cell& ArrayImpl::GetCellAcc( sal_Int32 nCol, sal_Int32 nRow )
+{
+ static Cell aDummy;
+ return IsValidPos( nCol, nRow ) ? maCells[ GetIndex( nCol, nRow ) ] : aDummy;
+}
+
+sal_Int32 ArrayImpl::GetMergedFirstCol( sal_Int32 nCol, sal_Int32 nRow ) const
+{
+ sal_Int32 nFirstCol = nCol;
+ while( (nFirstCol > 0) && GetCell( nFirstCol, nRow ).mbOverlapX ) --nFirstCol;
+ return nFirstCol;
+}
+
+sal_Int32 ArrayImpl::GetMergedFirstRow( sal_Int32 nCol, sal_Int32 nRow ) const
+{
+ sal_Int32 nFirstRow = nRow;
+ while( (nFirstRow > 0) && GetCell( nCol, nFirstRow ).mbOverlapY ) --nFirstRow;
+ return nFirstRow;
+}
+
+sal_Int32 ArrayImpl::GetMergedLastCol( sal_Int32 nCol, sal_Int32 nRow ) const
+{
+ sal_Int32 nLastCol = nCol + 1;
+ while( (nLastCol < mnWidth) && GetCell( nLastCol, nRow ).mbOverlapX ) ++nLastCol;
+ return nLastCol - 1;
+}
+
+sal_Int32 ArrayImpl::GetMergedLastRow( sal_Int32 nCol, sal_Int32 nRow ) const
+{
+ sal_Int32 nLastRow = nRow + 1;
+ while( (nLastRow < mnHeight) && GetCell( nCol, nLastRow ).mbOverlapY ) ++nLastRow;
+ return nLastRow - 1;
+}
+
+const Cell& ArrayImpl::GetMergedOriginCell( sal_Int32 nCol, sal_Int32 nRow ) const
+{
+ return GetCell( GetMergedFirstCol( nCol, nRow ), GetMergedFirstRow( nCol, nRow ) );
+}
+
+const Cell& ArrayImpl::GetMergedLastCell( sal_Int32 nCol, sal_Int32 nRow ) const
+{
+ return GetCell( GetMergedLastCol( nCol, nRow ), GetMergedLastRow( nCol, nRow ) );
+}
+
+bool ArrayImpl::IsMergedOverlappedLeft( sal_Int32 nCol, sal_Int32 nRow ) const
+{
+ const Cell& rCell = GetCell( nCol, nRow );
+ return rCell.mbOverlapX || (rCell.mnAddLeft > 0);
+}
+
+bool ArrayImpl::IsMergedOverlappedRight( sal_Int32 nCol, sal_Int32 nRow ) const
+{
+ return GetCell( nCol + 1, nRow ).mbOverlapX || (GetCell( nCol, nRow ).mnAddRight > 0);
+}
+
+bool ArrayImpl::IsMergedOverlappedTop( sal_Int32 nCol, sal_Int32 nRow ) const
+{
+ const Cell& rCell = GetCell( nCol, nRow );
+ return rCell.mbOverlapY || (rCell.mnAddTop > 0);
+}
+
+bool ArrayImpl::IsMergedOverlappedBottom( sal_Int32 nCol, sal_Int32 nRow ) const
+{
+ return GetCell( nCol, nRow + 1 ).mbOverlapY || (GetCell( nCol, nRow ).mnAddBottom > 0);
+}
+
+bool ArrayImpl::IsColInClipRange( sal_Int32 nCol ) const
+{
+ return (mnFirstClipCol <= nCol) && (nCol <= mnLastClipCol);
+}
+
+bool ArrayImpl::IsRowInClipRange( sal_Int32 nRow ) const
+{
+ return (mnFirstClipRow <= nRow) && (nRow <= mnLastClipRow);
+}
+
+bool ArrayImpl::OverlapsClipRange( sal_Int32 nFirstCol, sal_Int32 nFirstRow, sal_Int32 nLastCol, sal_Int32 nLastRow ) const
+{
+ if(nLastCol < mnFirstClipCol)
+ return false;
+
+ if(nFirstCol > mnLastClipCol)
+ return false;
+
+ if(nLastRow < mnFirstClipRow)
+ return false;
+
+ if(nFirstRow > mnLastClipRow)
+ return false;
+
+ return true;
+}
+
+bool ArrayImpl::IsInClipRange( sal_Int32 nCol, sal_Int32 nRow ) const
+{
+ return IsColInClipRange( nCol ) && IsRowInClipRange( nRow );
+}
+
+sal_Int32 ArrayImpl::GetColPosition( sal_Int32 nCol ) const
+{
+ if( mbXCoordsDirty )
+ {
+ lclRecalcCoordVec( maXCoords, maWidths );
+ mbXCoordsDirty = false;
+ }
+ return maXCoords[ nCol ];
+}
+
+sal_Int32 ArrayImpl::GetRowPosition( sal_Int32 nRow ) const
+{
+ if( mbYCoordsDirty )
+ {
+ lclRecalcCoordVec( maYCoords, maHeights );
+ mbYCoordsDirty = false;
+ }
+ return maYCoords[ nRow ];
+}
+
+bool ArrayImpl::HasCellRotation() const
+{
+ // check cell array
+ for (const auto& aCell : maCells)
+ {
+ if (aCell.IsRotated())
+ {
+ return true;
+ }
+ }
+
+ return false;
+}
+
+namespace {
+
+class MergedCellIterator
+{
+public:
+ explicit MergedCellIterator( const Array& rArray, sal_Int32 nCol, sal_Int32 nRow );
+
+ bool Is() const { return (mnCol <= mnLastCol) && (mnRow <= mnLastRow); }
+ sal_Int32 Col() const { return mnCol; }
+ sal_Int32 Row() const { return mnRow; }
+
+ MergedCellIterator& operator++();
+
+private:
+ sal_Int32 mnFirstCol;
+ sal_Int32 mnFirstRow;
+ sal_Int32 mnLastCol;
+ sal_Int32 mnLastRow;
+ sal_Int32 mnCol;
+ sal_Int32 mnRow;
+};
+
+}
+
+MergedCellIterator::MergedCellIterator( const Array& rArray, sal_Int32 nCol, sal_Int32 nRow )
+{
+ DBG_ASSERT( rArray.IsMerged( nCol, nRow ), "svx::frame::MergedCellIterator::MergedCellIterator - not in merged range" );
+ rArray.GetMergedRange( mnFirstCol, mnFirstRow, mnLastCol, mnLastRow, nCol, nRow );
+ mnCol = mnFirstCol;
+ mnRow = mnFirstRow;
+}
+
+MergedCellIterator& MergedCellIterator::operator++()
+{
+ DBG_ASSERT( Is(), "svx::frame::MergedCellIterator::operator++() - already invalid" );
+ if( ++mnCol > mnLastCol )
+ {
+ mnCol = mnFirstCol;
+ ++mnRow;
+ }
+ return *this;
+}
+
+
+#define DBG_FRAME_CHECK( cond, funcname, error ) DBG_ASSERT( cond, "svx::frame::Array::" funcname " - " error )
+#define DBG_FRAME_CHECK_COL( col, funcname ) DBG_FRAME_CHECK( (col) < GetColCount(), funcname, "invalid column index" )
+#define DBG_FRAME_CHECK_ROW( row, funcname ) DBG_FRAME_CHECK( (row) < GetRowCount(), funcname, "invalid row index" )
+#define DBG_FRAME_CHECK_COLROW( col, row, funcname ) DBG_FRAME_CHECK( ((col) < GetColCount()) && ((row) < GetRowCount()), funcname, "invalid cell index" )
+#define DBG_FRAME_CHECK_COL_1( col, funcname ) DBG_FRAME_CHECK( (col) <= GetColCount(), funcname, "invalid column index" )
+#define DBG_FRAME_CHECK_ROW_1( row, funcname ) DBG_FRAME_CHECK( (row) <= GetRowCount(), funcname, "invalid row index" )
+
+
+#define CELL( col, row ) mxImpl->GetCell( col, row )
+#define CELLACC( col, row ) mxImpl->GetCellAcc( col, row )
+#define ORIGCELL( col, row ) mxImpl->GetMergedOriginCell( col, row )
+#define LASTCELL( col, row ) mxImpl->GetMergedLastCell( col, row )
+
+
+Array::Array()
+{
+ Initialize( 0, 0 );
+}
+
+Array::~Array()
+{
+}
+
+// array size and column/row indexes
+void Array::Initialize( sal_Int32 nWidth, sal_Int32 nHeight )
+{
+ mxImpl.reset( new ArrayImpl( nWidth, nHeight ) );
+}
+
+sal_Int32 Array::GetColCount() const
+{
+ return mxImpl->mnWidth;
+}
+
+sal_Int32 Array::GetRowCount() const
+{
+ return mxImpl->mnHeight;
+}
+
+sal_Int32 Array::GetCellCount() const
+{
+ return mxImpl->maCells.size();
+}
+
+sal_Int32 Array::GetCellIndex( sal_Int32 nCol, sal_Int32 nRow, bool bRTL ) const
+{
+ DBG_FRAME_CHECK_COLROW( nCol, nRow, "GetCellIndex" );
+ if (bRTL)
+ nCol = mxImpl->GetMirrorCol(nCol);
+ return mxImpl->GetIndex( nCol, nRow );
+}
+
+// cell border styles
+void Array::SetCellStyleLeft( sal_Int32 nCol, sal_Int32 nRow, const Style& rStyle )
+{
+ DBG_FRAME_CHECK_COLROW( nCol, nRow, "SetCellStyleLeft" );
+ CELLACC( nCol, nRow ).SetStyleLeft(rStyle);
+}
+
+void Array::SetCellStyleRight( sal_Int32 nCol, sal_Int32 nRow, const Style& rStyle )
+{
+ DBG_FRAME_CHECK_COLROW( nCol, nRow, "SetCellStyleRight" );
+ CELLACC( nCol, nRow ).SetStyleRight(rStyle);
+}
+
+void Array::SetCellStyleTop( sal_Int32 nCol, sal_Int32 nRow, const Style& rStyle )
+{
+ DBG_FRAME_CHECK_COLROW( nCol, nRow, "SetCellStyleTop" );
+ CELLACC( nCol, nRow ).SetStyleTop(rStyle);
+}
+
+void Array::SetCellStyleBottom( sal_Int32 nCol, sal_Int32 nRow, const Style& rStyle )
+{
+ DBG_FRAME_CHECK_COLROW( nCol, nRow, "SetCellStyleBottom" );
+ CELLACC( nCol, nRow ).SetStyleBottom(rStyle);
+}
+
+void Array::SetCellStyleTLBR( sal_Int32 nCol, sal_Int32 nRow, const Style& rStyle )
+{
+ DBG_FRAME_CHECK_COLROW( nCol, nRow, "SetCellStyleTLBR" );
+ CELLACC( nCol, nRow ).SetStyleTLBR(rStyle);
+}
+
+void Array::SetCellStyleBLTR( sal_Int32 nCol, sal_Int32 nRow, const Style& rStyle )
+{
+ DBG_FRAME_CHECK_COLROW( nCol, nRow, "SetCellStyleBLTR" );
+ CELLACC( nCol, nRow ).SetStyleBLTR(rStyle);
+}
+
+void Array::SetCellStyleDiag( sal_Int32 nCol, sal_Int32 nRow, const Style& rTLBR, const Style& rBLTR )
+{
+ DBG_FRAME_CHECK_COLROW( nCol, nRow, "SetCellStyleDiag" );
+ Cell& rCell = CELLACC( nCol, nRow );
+ rCell.SetStyleTLBR(rTLBR);
+ rCell.SetStyleBLTR(rBLTR);
+}
+
+void Array::SetColumnStyleLeft( sal_Int32 nCol, const Style& rStyle )
+{
+ DBG_FRAME_CHECK_COL( nCol, "SetColumnStyleLeft" );
+ for( sal_Int32 nRow = 0; nRow < mxImpl->mnHeight; ++nRow )
+ SetCellStyleLeft( nCol, nRow, rStyle );
+}
+
+void Array::SetColumnStyleRight( sal_Int32 nCol, const Style& rStyle )
+{
+ DBG_FRAME_CHECK_COL( nCol, "SetColumnStyleRight" );
+ for( sal_Int32 nRow = 0; nRow < mxImpl->mnHeight; ++nRow )
+ SetCellStyleRight( nCol, nRow, rStyle );
+}
+
+void Array::SetRowStyleTop( sal_Int32 nRow, const Style& rStyle )
+{
+ DBG_FRAME_CHECK_ROW( nRow, "SetRowStyleTop" );
+ for( sal_Int32 nCol = 0; nCol < mxImpl->mnWidth; ++nCol )
+ SetCellStyleTop( nCol, nRow, rStyle );
+}
+
+void Array::SetRowStyleBottom( sal_Int32 nRow, const Style& rStyle )
+{
+ DBG_FRAME_CHECK_ROW( nRow, "SetRowStyleBottom" );
+ for( sal_Int32 nCol = 0; nCol < mxImpl->mnWidth; ++nCol )
+ SetCellStyleBottom( nCol, nRow, rStyle );
+}
+
+void Array::SetCellRotation(sal_Int32 nCol, sal_Int32 nRow, SvxRotateMode eRotMode, double fOrientation)
+{
+ DBG_FRAME_CHECK_COLROW(nCol, nRow, "SetCellRotation");
+ Cell& rTarget = CELLACC(nCol, nRow);
+ rTarget.meRotMode = eRotMode;
+ rTarget.mfOrientation = fOrientation;
+
+ if (!mxImpl->mbMayHaveCellRotation)
+ {
+ // activate once when a cell gets actually rotated to allow fast
+ // answering HasCellRotation() calls
+ mxImpl->mbMayHaveCellRotation = rTarget.IsRotated();
+ }
+}
+
+bool Array::HasCellRotation() const
+{
+ if (!mxImpl->mbMayHaveCellRotation)
+ {
+ // never set, no need to check
+ return false;
+ }
+
+ return mxImpl->HasCellRotation();
+}
+
+const Style& Array::GetCellStyleLeft( sal_Int32 nCol, sal_Int32 nRow ) const
+{
+ // outside clipping rows or overlapped in merged cells: invisible
+ if( !mxImpl->IsRowInClipRange( nRow ) || mxImpl->IsMergedOverlappedLeft( nCol, nRow ) )
+ return OBJ_STYLE_NONE;
+ // left clipping border: always own left style
+ if( nCol == mxImpl->mnFirstClipCol )
+ return ORIGCELL( nCol, nRow ).GetStyleLeft();
+ // right clipping border: always right style of left neighbor cell
+ if( nCol == mxImpl->mnLastClipCol + 1 )
+ return ORIGCELL( nCol - 1, nRow ).GetStyleRight();
+ // outside clipping columns: invisible
+ if( !mxImpl->IsColInClipRange( nCol ) )
+ return OBJ_STYLE_NONE;
+ // inside clipping range: maximum of own left style and right style of left neighbor cell
+ return std::max( ORIGCELL( nCol, nRow ).GetStyleLeft(), ORIGCELL( nCol - 1, nRow ).GetStyleRight() );
+}
+
+const Style& Array::GetCellStyleRight( sal_Int32 nCol, sal_Int32 nRow ) const
+{
+ // outside clipping rows or overlapped in merged cells: invisible
+ if( !mxImpl->IsRowInClipRange( nRow ) || mxImpl->IsMergedOverlappedRight( nCol, nRow ) )
+ return OBJ_STYLE_NONE;
+ // left clipping border: always left style of right neighbor cell
+ if( nCol + 1 == mxImpl->mnFirstClipCol )
+ return ORIGCELL( nCol + 1, nRow ).GetStyleLeft();
+ // right clipping border: always own right style
+ if( nCol == mxImpl->mnLastClipCol )
+ return LASTCELL( nCol, nRow ).GetStyleRight();
+ // outside clipping columns: invisible
+ if( !mxImpl->IsColInClipRange( nCol ) )
+ return OBJ_STYLE_NONE;
+ // inside clipping range: maximum of own right style and left style of right neighbor cell
+ return std::max( ORIGCELL( nCol, nRow ).GetStyleRight(), ORIGCELL( nCol + 1, nRow ).GetStyleLeft() );
+}
+
+const Style& Array::GetCellStyleTop( sal_Int32 nCol, sal_Int32 nRow ) const
+{
+ // outside clipping columns or overlapped in merged cells: invisible
+ if( !mxImpl->IsColInClipRange( nCol ) || mxImpl->IsMergedOverlappedTop( nCol, nRow ) )
+ return OBJ_STYLE_NONE;
+ // top clipping border: always own top style
+ if( nRow == mxImpl->mnFirstClipRow )
+ return ORIGCELL( nCol, nRow ).GetStyleTop();
+ // bottom clipping border: always bottom style of top neighbor cell
+ if( nRow == mxImpl->mnLastClipRow + 1 )
+ return ORIGCELL( nCol, nRow - 1 ).GetStyleBottom();
+ // outside clipping rows: invisible
+ if( !mxImpl->IsRowInClipRange( nRow ) )
+ return OBJ_STYLE_NONE;
+ // inside clipping range: maximum of own top style and bottom style of top neighbor cell
+ return std::max( ORIGCELL( nCol, nRow ).GetStyleTop(), ORIGCELL( nCol, nRow - 1 ).GetStyleBottom() );
+}
+
+const Style& Array::GetCellStyleBottom( sal_Int32 nCol, sal_Int32 nRow ) const
+{
+ // outside clipping columns or overlapped in merged cells: invisible
+ if( !mxImpl->IsColInClipRange( nCol ) || mxImpl->IsMergedOverlappedBottom( nCol, nRow ) )
+ return OBJ_STYLE_NONE;
+ // top clipping border: always top style of bottom neighbor cell
+ if( nRow + 1 == mxImpl->mnFirstClipRow )
+ return ORIGCELL( nCol, nRow + 1 ).GetStyleTop();
+ // bottom clipping border: always own bottom style
+ if( nRow == mxImpl->mnLastClipRow )
+ return LASTCELL( nCol, nRow ).GetStyleBottom();
+ // outside clipping rows: invisible
+ if( !mxImpl->IsRowInClipRange( nRow ) )
+ return OBJ_STYLE_NONE;
+ // inside clipping range: maximum of own bottom style and top style of bottom neighbor cell
+ return std::max( ORIGCELL( nCol, nRow ).GetStyleBottom(), ORIGCELL( nCol, nRow + 1 ).GetStyleTop() );
+}
+
+const Style& Array::GetCellStyleTLBR( sal_Int32 nCol, sal_Int32 nRow ) const
+{
+ return CELL( nCol, nRow ).GetStyleTLBR();
+}
+
+const Style& Array::GetCellStyleBLTR( sal_Int32 nCol, sal_Int32 nRow ) const
+{
+ return CELL( nCol, nRow ).GetStyleBLTR();
+}
+
+const Style& Array::GetCellStyleTL( sal_Int32 nCol, sal_Int32 nRow ) const
+{
+ // not in clipping range: always invisible
+ if( !mxImpl->IsInClipRange( nCol, nRow ) )
+ return OBJ_STYLE_NONE;
+ // return style only for top-left cell
+ sal_Int32 nFirstCol = mxImpl->GetMergedFirstCol( nCol, nRow );
+ sal_Int32 nFirstRow = mxImpl->GetMergedFirstRow( nCol, nRow );
+ return ((nCol == nFirstCol) && (nRow == nFirstRow)) ?
+ CELL( nFirstCol, nFirstRow ).GetStyleTLBR() : OBJ_STYLE_NONE;
+}
+
+const Style& Array::GetCellStyleBR( sal_Int32 nCol, sal_Int32 nRow ) const
+{
+ // not in clipping range: always invisible
+ if( !mxImpl->IsInClipRange( nCol, nRow ) )
+ return OBJ_STYLE_NONE;
+ // return style only for bottom-right cell
+ sal_Int32 nLastCol = mxImpl->GetMergedLastCol( nCol, nRow );
+ sal_Int32 nLastRow = mxImpl->GetMergedLastRow( nCol, nRow );
+ return ((nCol == nLastCol) && (nRow == nLastRow)) ?
+ CELL( mxImpl->GetMergedFirstCol( nCol, nRow ), mxImpl->GetMergedFirstRow( nCol, nRow ) ).GetStyleTLBR() : OBJ_STYLE_NONE;
+}
+
+const Style& Array::GetCellStyleBL( sal_Int32 nCol, sal_Int32 nRow ) const
+{
+ // not in clipping range: always invisible
+ if( !mxImpl->IsInClipRange( nCol, nRow ) )
+ return OBJ_STYLE_NONE;
+ // return style only for bottom-left cell
+ sal_Int32 nFirstCol = mxImpl->GetMergedFirstCol( nCol, nRow );
+ sal_Int32 nLastRow = mxImpl->GetMergedLastRow( nCol, nRow );
+ return ((nCol == nFirstCol) && (nRow == nLastRow)) ?
+ CELL( nFirstCol, mxImpl->GetMergedFirstRow( nCol, nRow ) ).GetStyleBLTR() : OBJ_STYLE_NONE;
+}
+
+const Style& Array::GetCellStyleTR( sal_Int32 nCol, sal_Int32 nRow ) const
+{
+ // not in clipping range: always invisible
+ if( !mxImpl->IsInClipRange( nCol, nRow ) )
+ return OBJ_STYLE_NONE;
+ // return style only for top-right cell
+ sal_Int32 nFirstRow = mxImpl->GetMergedFirstRow( nCol, nRow );
+ sal_Int32 nLastCol = mxImpl->GetMergedLastCol( nCol, nRow );
+ return ((nCol == nLastCol) && (nRow == nFirstRow)) ?
+ CELL( mxImpl->GetMergedFirstCol( nCol, nRow ), nFirstRow ).GetStyleBLTR() : OBJ_STYLE_NONE;
+}
+
+// cell merging
+void Array::SetMergedRange( sal_Int32 nFirstCol, sal_Int32 nFirstRow, sal_Int32 nLastCol, sal_Int32 nLastRow )
+{
+ DBG_FRAME_CHECK_COLROW( nFirstCol, nFirstRow, "SetMergedRange" );
+ DBG_FRAME_CHECK_COLROW( nLastCol, nLastRow, "SetMergedRange" );
+#if OSL_DEBUG_LEVEL >= 2
+ {
+ bool bFound = false;
+ for( sal_Int32 nCurrCol = nFirstCol; !bFound && (nCurrCol <= nLastCol); ++nCurrCol )
+ for( sal_Int32 nCurrRow = nFirstRow; !bFound && (nCurrRow <= nLastRow); ++nCurrRow )
+ bFound = CELL( nCurrCol, nCurrRow ).IsMerged();
+ DBG_FRAME_CHECK( !bFound, "SetMergedRange", "overlapping merged ranges" );
+ }
+#endif
+ if( mxImpl->IsValidPos( nFirstCol, nFirstRow ) && mxImpl->IsValidPos( nLastCol, nLastRow ) )
+ lclSetMergedRange( mxImpl->maCells, mxImpl->mnWidth, nFirstCol, nFirstRow, nLastCol, nLastRow );
+}
+
+void Array::SetAddMergedLeftSize( sal_Int32 nCol, sal_Int32 nRow, sal_Int32 nAddSize )
+{
+ DBG_FRAME_CHECK_COLROW( nCol, nRow, "SetAddMergedLeftSize" );
+ DBG_FRAME_CHECK( mxImpl->GetMergedFirstCol( nCol, nRow ) == 0, "SetAddMergedLeftSize", "additional border inside array" );
+ for( MergedCellIterator aIt( *this, nCol, nRow ); aIt.Is(); ++aIt )
+ CELLACC( aIt.Col(), aIt.Row() ).mnAddLeft = nAddSize;
+}
+
+void Array::SetAddMergedRightSize( sal_Int32 nCol, sal_Int32 nRow, sal_Int32 nAddSize )
+{
+ DBG_FRAME_CHECK_COLROW( nCol, nRow, "SetAddMergedRightSize" );
+ DBG_FRAME_CHECK( mxImpl->GetMergedLastCol( nCol, nRow ) + 1 == mxImpl->mnWidth, "SetAddMergedRightSize", "additional border inside array" );
+ for( MergedCellIterator aIt( *this, nCol, nRow ); aIt.Is(); ++aIt )
+ CELLACC( aIt.Col(), aIt.Row() ).mnAddRight = nAddSize;
+}
+
+void Array::SetAddMergedTopSize( sal_Int32 nCol, sal_Int32 nRow, sal_Int32 nAddSize )
+{
+ DBG_FRAME_CHECK_COLROW( nCol, nRow, "SetAddMergedTopSize" );
+ DBG_FRAME_CHECK( mxImpl->GetMergedFirstRow( nCol, nRow ) == 0, "SetAddMergedTopSize", "additional border inside array" );
+ for( MergedCellIterator aIt( *this, nCol, nRow ); aIt.Is(); ++aIt )
+ CELLACC( aIt.Col(), aIt.Row() ).mnAddTop = nAddSize;
+}
+
+void Array::SetAddMergedBottomSize( sal_Int32 nCol, sal_Int32 nRow, sal_Int32 nAddSize )
+{
+ DBG_FRAME_CHECK_COLROW( nCol, nRow, "SetAddMergedBottomSize" );
+ DBG_FRAME_CHECK( mxImpl->GetMergedLastRow( nCol, nRow ) + 1 == mxImpl->mnHeight, "SetAddMergedBottomSize", "additional border inside array" );
+ for( MergedCellIterator aIt( *this, nCol, nRow ); aIt.Is(); ++aIt )
+ CELLACC( aIt.Col(), aIt.Row() ).mnAddBottom = nAddSize;
+}
+
+bool Array::IsMerged( sal_Int32 nCol, sal_Int32 nRow ) const
+{
+ DBG_FRAME_CHECK_COLROW( nCol, nRow, "IsMerged" );
+ return CELL( nCol, nRow ).IsMerged();
+}
+
+void Array::GetMergedOrigin( sal_Int32& rnFirstCol, sal_Int32& rnFirstRow, sal_Int32 nCol, sal_Int32 nRow ) const
+{
+ DBG_FRAME_CHECK_COLROW( nCol, nRow, "GetMergedOrigin" );
+ rnFirstCol = mxImpl->GetMergedFirstCol( nCol, nRow );
+ rnFirstRow = mxImpl->GetMergedFirstRow( nCol, nRow );
+}
+
+void Array::GetMergedRange( sal_Int32& rnFirstCol, sal_Int32& rnFirstRow,
+ sal_Int32& rnLastCol, sal_Int32& rnLastRow, sal_Int32 nCol, sal_Int32 nRow ) const
+{
+ GetMergedOrigin( rnFirstCol, rnFirstRow, nCol, nRow );
+ rnLastCol = mxImpl->GetMergedLastCol( nCol, nRow );
+ rnLastRow = mxImpl->GetMergedLastRow( nCol, nRow );
+}
+
+// clipping
+void Array::SetClipRange( sal_Int32 nFirstCol, sal_Int32 nFirstRow, sal_Int32 nLastCol, sal_Int32 nLastRow )
+{
+ DBG_FRAME_CHECK_COLROW( nFirstCol, nFirstRow, "SetClipRange" );
+ DBG_FRAME_CHECK_COLROW( nLastCol, nLastRow, "SetClipRange" );
+ mxImpl->mnFirstClipCol = nFirstCol;
+ mxImpl->mnFirstClipRow = nFirstRow;
+ mxImpl->mnLastClipCol = nLastCol;
+ mxImpl->mnLastClipRow = nLastRow;
+}
+
+// cell coordinates
+void Array::SetXOffset( sal_Int32 nXOffset )
+{
+ mxImpl->maXCoords[ 0 ] = nXOffset;
+ mxImpl->mbXCoordsDirty = true;
+}
+
+void Array::SetYOffset( sal_Int32 nYOffset )
+{
+ mxImpl->maYCoords[ 0 ] = nYOffset;
+ mxImpl->mbYCoordsDirty = true;
+}
+
+void Array::SetColWidth( sal_Int32 nCol, sal_Int32 nWidth )
+{
+ DBG_FRAME_CHECK_COL( nCol, "SetColWidth" );
+ mxImpl->maWidths[ nCol ] = nWidth;
+ mxImpl->mbXCoordsDirty = true;
+}
+
+void Array::SetRowHeight( sal_Int32 nRow, sal_Int32 nHeight )
+{
+ DBG_FRAME_CHECK_ROW( nRow, "SetRowHeight" );
+ mxImpl->maHeights[ nRow ] = nHeight;
+ mxImpl->mbYCoordsDirty = true;
+}
+
+void Array::SetAllColWidths( sal_Int32 nWidth )
+{
+ std::fill( mxImpl->maWidths.begin(), mxImpl->maWidths.end(), nWidth );
+ mxImpl->mbXCoordsDirty = true;
+}
+
+void Array::SetAllRowHeights( sal_Int32 nHeight )
+{
+ std::fill( mxImpl->maHeights.begin(), mxImpl->maHeights.end(), nHeight );
+ mxImpl->mbYCoordsDirty = true;
+}
+
+sal_Int32 Array::GetColPosition( sal_Int32 nCol ) const
+{
+ DBG_FRAME_CHECK_COL_1( nCol, "GetColPosition" );
+ return mxImpl->GetColPosition( nCol );
+}
+
+sal_Int32 Array::GetRowPosition( sal_Int32 nRow ) const
+{
+ DBG_FRAME_CHECK_ROW_1( nRow, "GetRowPosition" );
+ return mxImpl->GetRowPosition( nRow );
+}
+
+sal_Int32 Array::GetColWidth( sal_Int32 nFirstCol, sal_Int32 nLastCol ) const
+{
+ DBG_FRAME_CHECK_COL( nFirstCol, "GetColWidth" );
+ DBG_FRAME_CHECK_COL( nLastCol, "GetColWidth" );
+ return GetColPosition( nLastCol + 1 ) - GetColPosition( nFirstCol );
+}
+
+sal_Int32 Array::GetRowHeight( sal_Int32 nFirstRow, sal_Int32 nLastRow ) const
+{
+ DBG_FRAME_CHECK_ROW( nFirstRow, "GetRowHeight" );
+ DBG_FRAME_CHECK_ROW( nLastRow, "GetRowHeight" );
+ return GetRowPosition( nLastRow + 1 ) - GetRowPosition( nFirstRow );
+}
+
+sal_Int32 Array::GetWidth() const
+{
+ return GetColPosition( mxImpl->mnWidth ) - GetColPosition( 0 );
+}
+
+sal_Int32 Array::GetHeight() const
+{
+ return GetRowPosition( mxImpl->mnHeight ) - GetRowPosition( 0 );
+}
+
+basegfx::B2DRange Array::GetCellRange( sal_Int32 nCol, sal_Int32 nRow ) const
+{
+ // get the Range of the fully expanded cell (if merged)
+ const sal_Int32 nFirstCol(mxImpl->GetMergedFirstCol( nCol, nRow ));
+ const sal_Int32 nFirstRow(mxImpl->GetMergedFirstRow( nCol, nRow ));
+ const sal_Int32 nLastCol(mxImpl->GetMergedLastCol( nCol, nRow ));
+ const sal_Int32 nLastRow(mxImpl->GetMergedLastRow( nCol, nRow ));
+ const Point aPoint( GetColPosition( nFirstCol ), GetRowPosition( nFirstRow ) );
+ const Size aSize( GetColWidth( nFirstCol, nLastCol ) + 1, GetRowHeight( nFirstRow, nLastRow ) + 1 );
+ tools::Rectangle aRect(aPoint, aSize);
+
+ // adjust rectangle for partly visible merged cells
+ const Cell& rCell = CELL( nCol, nRow );
+
+ if( rCell.IsMerged() )
+ {
+ // not *sure* what exactly this is good for,
+ // it is just a hard set extension at merged cells,
+ // probably *should* be included in the above extended
+ // GetColPosition/GetColWidth already. This might be
+ // added due to GetColPosition/GetColWidth not working
+ // correctly over PageChanges (if used), but not sure.
+ aRect.AdjustLeft( -(rCell.mnAddLeft) );
+ aRect.AdjustRight(rCell.mnAddRight );
+ aRect.AdjustTop( -(rCell.mnAddTop) );
+ aRect.AdjustBottom(rCell.mnAddBottom );
+ }
+
+ return vcl::unotools::b2DRectangleFromRectangle(aRect);
+}
+
+// return output range of given row/col range in logical coordinates
+basegfx::B2DRange Array::GetB2DRange(sal_Int32 nFirstCol, sal_Int32 nFirstRow, sal_Int32 nLastCol, sal_Int32 nLastRow) const
+{
+ const Point aPoint( GetColPosition( nFirstCol ), GetRowPosition( nFirstRow ) );
+ const Size aSize( GetColWidth( nFirstCol, nLastCol ) + 1, GetRowHeight( nFirstRow, nLastRow ) + 1 );
+
+ return vcl::unotools::b2DRectangleFromRectangle(tools::Rectangle(aPoint, aSize));
+}
+
+// mirroring
+void Array::MirrorSelfX()
+{
+ CellVec aNewCells;
+ aNewCells.reserve( GetCellCount() );
+
+ sal_Int32 nCol, nRow;
+ for( nRow = 0; nRow < mxImpl->mnHeight; ++nRow )
+ {
+ for( nCol = 0; nCol < mxImpl->mnWidth; ++nCol )
+ {
+ aNewCells.push_back( CELL( mxImpl->GetMirrorCol( nCol ), nRow ) );
+ aNewCells.back().MirrorSelfX();
+ }
+ }
+ for( nRow = 0; nRow < mxImpl->mnHeight; ++nRow )
+ {
+ for( nCol = 0; nCol < mxImpl->mnWidth; ++nCol )
+ {
+ if( CELL( nCol, nRow ).mbMergeOrig )
+ {
+ sal_Int32 nLastCol = mxImpl->GetMergedLastCol( nCol, nRow );
+ sal_Int32 nLastRow = mxImpl->GetMergedLastRow( nCol, nRow );
+ lclSetMergedRange( aNewCells, mxImpl->mnWidth,
+ mxImpl->GetMirrorCol( nLastCol ), nRow,
+ mxImpl->GetMirrorCol( nCol ), nLastRow );
+ }
+ }
+ }
+ mxImpl->maCells.swap( aNewCells );
+
+ std::reverse( mxImpl->maWidths.begin(), mxImpl->maWidths.end() );
+ mxImpl->mbXCoordsDirty = true;
+}
+
+// drawing
+static void HelperCreateHorizontalEntry(
+ const Array& rArray,
+ const Style& rStyle,
+ sal_Int32 col,
+ sal_Int32 row,
+ const basegfx::B2DPoint& rOrigin,
+ const basegfx::B2DVector& rX,
+ const basegfx::B2DVector& rY,
+ drawinglayer::primitive2d::SdrFrameBorderDataVector& rData,
+ bool bUpper,
+ const Color* pForceColor)
+{
+ // prepare SdrFrameBorderData
+ rData.emplace_back(
+ bUpper ? rOrigin : basegfx::B2DPoint(rOrigin + rY),
+ rX,
+ rStyle,
+ pForceColor);
+ drawinglayer::primitive2d::SdrFrameBorderData& rInstance(rData.back());
+
+ // get involved styles at start
+ const Style& rStartFromTR(rArray.GetCellStyleBL( col, row - 1 ));
+ const Style& rStartLFromT(rArray.GetCellStyleLeft( col, row - 1 ));
+ const Style& rStartLFromL(rArray.GetCellStyleTop( col - 1, row ));
+ const Style& rStartLFromB(rArray.GetCellStyleLeft( col, row ));
+ const Style& rStartFromBR(rArray.GetCellStyleTL( col, row ));
+
+ rInstance.addSdrConnectStyleData(true, rStartFromTR, rX - rY, false);
+ rInstance.addSdrConnectStyleData(true, rStartLFromT, -rY, true);
+ rInstance.addSdrConnectStyleData(true, rStartLFromL, -rX, true);
+ rInstance.addSdrConnectStyleData(true, rStartLFromB, rY, false);
+ rInstance.addSdrConnectStyleData(true, rStartFromBR, rX + rY, false);
+
+ // get involved styles at end
+ const Style& rEndFromTL(rArray.GetCellStyleBR( col, row - 1 ));
+ const Style& rEndRFromT(rArray.GetCellStyleRight( col, row - 1 ));
+ const Style& rEndRFromR(rArray.GetCellStyleTop( col + 1, row ));
+ const Style& rEndRFromB(rArray.GetCellStyleRight( col, row ));
+ const Style& rEndFromBL(rArray.GetCellStyleTR( col, row ));
+
+ rInstance.addSdrConnectStyleData(false, rEndFromTL, -rX - rY, true);
+ rInstance.addSdrConnectStyleData(false, rEndRFromT, -rY, true);
+ rInstance.addSdrConnectStyleData(false, rEndRFromR, rX, false);
+ rInstance.addSdrConnectStyleData(false, rEndRFromB, rY, false);
+ rInstance.addSdrConnectStyleData(false, rEndFromBL, rY - rX, true);
+}
+
+static void HelperCreateVerticalEntry(
+ const Array& rArray,
+ const Style& rStyle,
+ sal_Int32 col,
+ sal_Int32 row,
+ const basegfx::B2DPoint& rOrigin,
+ const basegfx::B2DVector& rX,
+ const basegfx::B2DVector& rY,
+ drawinglayer::primitive2d::SdrFrameBorderDataVector& rData,
+ bool bLeft,
+ const Color* pForceColor)
+{
+ // prepare SdrFrameBorderData
+ rData.emplace_back(
+ bLeft ? rOrigin : basegfx::B2DPoint(rOrigin + rX),
+ rY,
+ rStyle,
+ pForceColor);
+ drawinglayer::primitive2d::SdrFrameBorderData& rInstance(rData.back());
+
+ // get involved styles at start
+ const Style& rStartFromBL(rArray.GetCellStyleTR( col - 1, row ));
+ const Style& rStartTFromL(rArray.GetCellStyleTop( col - 1, row ));
+ const Style& rStartTFromT(rArray.GetCellStyleLeft( col, row - 1 ));
+ const Style& rStartTFromR(rArray.GetCellStyleTop( col, row ));
+ const Style& rStartFromBR(rArray.GetCellStyleTL( col, row ));
+
+ rInstance.addSdrConnectStyleData(true, rStartFromBR, rX + rY, false);
+ rInstance.addSdrConnectStyleData(true, rStartTFromR, rX, false);
+ rInstance.addSdrConnectStyleData(true, rStartTFromT, -rY, true);
+ rInstance.addSdrConnectStyleData(true, rStartTFromL, -rX, true);
+ rInstance.addSdrConnectStyleData(true, rStartFromBL, rY - rX, true);
+
+ // get involved styles at end
+ const Style& rEndFromTL(rArray.GetCellStyleBR( col - 1, row ));
+ const Style& rEndBFromL(rArray.GetCellStyleBottom( col - 1, row ));
+ const Style& rEndBFromB(rArray.GetCellStyleLeft( col, row + 1 ));
+ const Style& rEndBFromR(rArray.GetCellStyleBottom( col, row ));
+ const Style& rEndFromTR(rArray.GetCellStyleBL( col, row ));
+
+ rInstance.addSdrConnectStyleData(false, rEndFromTR, rX - rY, false);
+ rInstance.addSdrConnectStyleData(false, rEndBFromR, rX, false);
+ rInstance.addSdrConnectStyleData(false, rEndBFromB, rY, false);
+ rInstance.addSdrConnectStyleData(false, rEndBFromL, -rX, true);
+ rInstance.addSdrConnectStyleData(false, rEndFromTL, -rY - rX, true);
+}
+
+static void HelperClipLine(
+ basegfx::B2DPoint& rStart,
+ basegfx::B2DVector& rDirection,
+ const basegfx::B2DRange& rClipRange)
+{
+ basegfx::B2DPolygon aLine({rStart, rStart + rDirection});
+ const basegfx::B2DPolyPolygon aResultPP(
+ basegfx::utils::clipPolygonOnRange(
+ aLine,
+ rClipRange,
+ true, // bInside
+ true)); // bStroke
+
+ if(aResultPP.count() > 0)
+ {
+ const basegfx::B2DPolygon aResultP(aResultPP.getB2DPolygon(0));
+
+ if(aResultP.count() > 0)
+ {
+ const basegfx::B2DPoint aResultStart(aResultP.getB2DPoint(0));
+ const basegfx::B2DPoint aResultEnd(aResultP.getB2DPoint(aResultP.count() - 1));
+
+ if(aResultStart != aResultEnd)
+ {
+ rStart = aResultStart;
+ rDirection = aResultEnd - aResultStart;
+ }
+ }
+ }
+}
+
+static void HelperCreateTLBREntry(
+ const Array& rArray,
+ const Style& rStyle,
+ drawinglayer::primitive2d::SdrFrameBorderDataVector& rData,
+ const basegfx::B2DPoint& rOrigin,
+ const basegfx::B2DVector& rX,
+ const basegfx::B2DVector& rY,
+ sal_Int32 nColLeft,
+ sal_Int32 nColRight,
+ sal_Int32 nRowTop,
+ sal_Int32 nRowBottom,
+ const Color* pForceColor,
+ const basegfx::B2DRange* pClipRange)
+{
+ if(rStyle.IsUsed())
+ {
+ /// prepare geometry line data
+ basegfx::B2DPoint aStart(rOrigin);
+ basegfx::B2DVector aDirection(rX + rY);
+
+ /// check if we need to clip geometry line data and do it
+ if(nullptr != pClipRange)
+ {
+ HelperClipLine(aStart, aDirection, *pClipRange);
+ }
+
+ /// top-left and bottom-right Style Tables
+ rData.emplace_back(
+ aStart,
+ aDirection,
+ rStyle,
+ pForceColor);
+ drawinglayer::primitive2d::SdrFrameBorderData& rInstance(rData.back());
+
+ /// Fill top-left Style Table
+ const Style& rTLFromRight(rArray.GetCellStyleTop(nColLeft, nRowTop));
+ const Style& rTLFromBottom(rArray.GetCellStyleLeft(nColLeft, nRowTop));
+
+ rInstance.addSdrConnectStyleData(true, rTLFromRight, rX, false);
+ rInstance.addSdrConnectStyleData(true, rTLFromBottom, rY, false);
+
+ /// Fill bottom-right Style Table
+ const Style& rBRFromBottom(rArray.GetCellStyleRight(nColRight, nRowBottom));
+ const Style& rBRFromLeft(rArray.GetCellStyleBottom(nColRight, nRowBottom));
+
+ rInstance.addSdrConnectStyleData(false, rBRFromBottom, -rY, true);
+ rInstance.addSdrConnectStyleData(false, rBRFromLeft, -rX, true);
+ }
+}
+
+static void HelperCreateBLTREntry(
+ const Array& rArray,
+ const Style& rStyle,
+ drawinglayer::primitive2d::SdrFrameBorderDataVector& rData,
+ const basegfx::B2DPoint& rOrigin,
+ const basegfx::B2DVector& rX,
+ const basegfx::B2DVector& rY,
+ sal_Int32 nColLeft,
+ sal_Int32 nColRight,
+ sal_Int32 nRowTop,
+ sal_Int32 nRowBottom,
+ const Color* pForceColor,
+ const basegfx::B2DRange* pClipRange)
+{
+ if(rStyle.IsUsed())
+ {
+ /// prepare geometry line data
+ basegfx::B2DPoint aStart(rOrigin + rY);
+ basegfx::B2DVector aDirection(rX - rY);
+
+ /// check if we need to clip geometry line data and do it
+ if(nullptr != pClipRange)
+ {
+ HelperClipLine(aStart, aDirection, *pClipRange);
+ }
+
+ /// bottom-left and top-right Style Tables
+ rData.emplace_back(
+ aStart,
+ aDirection,
+ rStyle,
+ pForceColor);
+ drawinglayer::primitive2d::SdrFrameBorderData& rInstance(rData.back());
+
+ /// Fill bottom-left Style Table
+ const Style& rBLFromTop(rArray.GetCellStyleLeft(nColLeft, nRowBottom));
+ const Style& rBLFromBottom(rArray.GetCellStyleBottom(nColLeft, nRowBottom));
+
+ rInstance.addSdrConnectStyleData(true, rBLFromTop, -rY, true);
+ rInstance.addSdrConnectStyleData(true, rBLFromBottom, rX, false);
+
+ /// Fill top-right Style Table
+ const Style& rTRFromLeft(rArray.GetCellStyleTop(nColRight, nRowTop));
+ const Style& rTRFromBottom(rArray.GetCellStyleRight(nColRight, nRowTop));
+
+ rInstance.addSdrConnectStyleData(false, rTRFromLeft, -rX, true);
+ rInstance.addSdrConnectStyleData(false, rTRFromBottom, rY, false);
+ }
+}
+
+drawinglayer::primitive2d::Primitive2DContainer Array::CreateB2DPrimitiveRange(
+ sal_Int32 nFirstCol, sal_Int32 nFirstRow, sal_Int32 nLastCol, sal_Int32 nLastRow,
+ const Color* pForceColor ) const
+{
+ DBG_FRAME_CHECK_COLROW( nFirstCol, nFirstRow, "CreateB2DPrimitiveRange" );
+ DBG_FRAME_CHECK_COLROW( nLastCol, nLastRow, "CreateB2DPrimitiveRange" );
+
+#ifdef OPTICAL_CHECK_CLIPRANGE_FOR_MERGED_CELL
+ std::vector<basegfx::B2DRange> aClipRanges;
+#endif
+
+ // It may be necessary to extend the loop ranges by one cell to the outside,
+ // when possible. This is needed e.g. when there is in Calc a Cell with an
+ // upper CellBorder using DoubleLine and that is right/left connected upwards
+ // to also DoubleLine. These upper DoubleLines will be extended to meet the
+ // lower of the upper CellBorder and thus have graphical parts that are
+ // displayed one cell below and right/left of the target cell - analog to
+ // other examples in all other directions.
+ // It would be possible to explicitly test this (if possible by indices at all)
+ // looping and testing the styles in the outer cells to detect this, but since
+ // for other usages (e.g. UI) usually nFirstRow==0 and nLastRow==GetRowCount()-1
+ // (and analog for Col) it is okay to just expand the range when available.
+ // Do *not* change nFirstRow/nLastRow due to these needed to the boolean tests
+ // below (!)
+ // Checked usages, this method is used in Calc EditView/Print/Export stuff and
+ // in UI (Dialog), not for Writer Tables and Draw/Impress tables. All usages
+ // seem okay with this change, so I will add it.
+ const sal_Int32 nStartRow(nFirstRow > 0 ? nFirstRow - 1 : nFirstRow);
+ const sal_Int32 nEndRow(nLastRow < GetRowCount() - 1 ? nLastRow + 1 : nLastRow);
+ const sal_Int32 nStartCol(nFirstCol > 0 ? nFirstCol - 1 : nFirstCol);
+ const sal_Int32 nEndCol(nLastCol < GetColCount() - 1 ? nLastCol + 1 : nLastCol);
+
+ // prepare SdrFrameBorderDataVector
+ std::shared_ptr<drawinglayer::primitive2d::SdrFrameBorderDataVector> aData(
+ std::make_shared<drawinglayer::primitive2d::SdrFrameBorderDataVector>());
+
+ // remember for which merged cells crossed lines were already created. To
+ // do so, hold the sal_Int32 cell index in a set for fast check
+ std::set< sal_Int32 > aMergedCells;
+
+ for (sal_Int32 nRow(nStartRow); nRow <= nEndRow; ++nRow)
+ {
+ for (sal_Int32 nCol(nStartCol); nCol <= nEndCol; ++nCol)
+ {
+ // get Cell and CoordinateSystem (*only* for this Cell, do *not* expand for
+ // merged cells (!)), check if used (non-empty vectors)
+ const Cell& rCell(CELL(nCol, nRow));
+ basegfx::B2DHomMatrix aCoordinateSystem(rCell.CreateCoordinateSystemSingleCell(*this, nCol, nRow));
+ basegfx::B2DVector aX(basegfx::utils::getColumn(aCoordinateSystem, 0));
+ basegfx::B2DVector aY(basegfx::utils::getColumn(aCoordinateSystem, 1));
+
+ // get needed local values
+ basegfx::B2DPoint aOrigin(basegfx::utils::getColumn(aCoordinateSystem, 2));
+ const bool bOverlapX(rCell.mbOverlapX);
+ const bool bFirstCol(nCol == nFirstCol);
+
+ // handle rotation: If cell is rotated, handle lower/right edge inside
+ // this local geometry due to the created CoordinateSystem already representing
+ // the needed transformations.
+ const bool bRotated(rCell.IsRotated());
+
+ // Additionally avoid double-handling by suppressing handling when self not rotated,
+ // but above/left is rotated and thus already handled. Two directly connected
+ // rotated will paint/create both edges, they might be rotated differently.
+ const bool bSuppressLeft(!bRotated && nCol > nFirstCol && CELL(nCol - 1, nRow).IsRotated());
+ const bool bSuppressAbove(!bRotated && nRow > nFirstRow && CELL(nCol, nRow - 1).IsRotated());
+
+ if(!aX.equalZero() && !aY.equalZero())
+ {
+ // additionally needed local values
+ const bool bOverlapY(rCell.mbOverlapY);
+ const bool bLastCol(nCol == nLastCol);
+ const bool bFirstRow(nRow == nFirstRow);
+ const bool bLastRow(nRow == nLastRow);
+
+ // create upper line for this Cell
+ if ((!bOverlapY // true for first line in merged cells or cells
+ || bFirstRow) // true for non_Calc usages of this tooling
+ && !bSuppressAbove) // true when above is not rotated, so edge is already handled (see bRotated)
+ {
+ // get CellStyle - method will take care to get the correct one, e.g.
+ // for merged cells (it uses ORIGCELL that works with topLeft's of these)
+ const Style& rTop(GetCellStyleTop(nCol, nRow));
+
+ if(rTop.IsUsed())
+ {
+ HelperCreateHorizontalEntry(*this, rTop, nCol, nRow, aOrigin, aX, aY, *aData, true, pForceColor);
+ }
+ }
+
+ // create lower line for this Cell
+ if (bLastRow // true for non_Calc usages of this tooling
+ || bRotated) // true if cell is rotated, handle lower edge in local geometry
+ {
+ const Style& rBottom(GetCellStyleBottom(nCol, nRow));
+
+ if(rBottom.IsUsed())
+ {
+ HelperCreateHorizontalEntry(*this, rBottom, nCol, nRow + 1, aOrigin, aX, aY, *aData, false, pForceColor);
+ }
+ }
+
+ // create left line for this Cell
+ if ((!bOverlapX // true for first column in merged cells or cells
+ || bFirstCol) // true for non_Calc usages of this tooling
+ && !bSuppressLeft) // true when left is not rotated, so edge is already handled (see bRotated)
+ {
+ const Style& rLeft(GetCellStyleLeft(nCol, nRow));
+
+ if(rLeft.IsUsed())
+ {
+ HelperCreateVerticalEntry(*this, rLeft, nCol, nRow, aOrigin, aX, aY, *aData, true, pForceColor);
+ }
+ }
+
+ // create right line for this Cell
+ if (bLastCol // true for non_Calc usages of this tooling
+ || bRotated) // true if cell is rotated, handle right edge in local geometry
+ {
+ const Style& rRight(GetCellStyleRight(nCol, nRow));
+
+ if(rRight.IsUsed())
+ {
+ HelperCreateVerticalEntry(*this, rRight, nCol + 1, nRow, aOrigin, aX, aY, *aData, false, pForceColor);
+ }
+ }
+
+ // tdf#126269 check for crossed lines, these need special treatment, especially
+ // for merged cells (see comments in task). Separate treatment of merged and
+ // non-merged cells to allow better handling of both types
+ if(rCell.IsMerged())
+ {
+ // first check if this merged cell was already handled. To do so,
+ // calculate and use the index of the TopLeft cell
+ sal_Int32 nColLeft(nCol), nRowTop(nRow), nColRight(nCol), nRowBottom(nRow);
+ GetMergedRange(nColLeft, nRowTop, nColRight, nRowBottom, nCol, nRow);
+ const sal_Int32 nIndexOfMergedCell(mxImpl->GetIndex(nColLeft, nRowTop));
+
+ if(aMergedCells.end() == aMergedCells.find(nIndexOfMergedCell))
+ {
+ // not found, so not yet handled. Add now to mark as handled
+ aMergedCells.insert(nIndexOfMergedCell);
+
+ // Get and check if diagonal styles are used
+ // Note: For GetCellStyleBLTR below I tried to use nRowBottom
+ // as Y-value what seemed more logical, but that
+ // is wrong. Despite defining a line starting at
+ // bottom-left, the Style is defined in the cell at top-left
+ const Style& rTLBR(GetCellStyleTLBR(nColLeft, nRowTop));
+ const Style& rBLTR(GetCellStyleBLTR(nColLeft, nRowTop));
+
+ if(rTLBR.IsUsed() || rBLTR.IsUsed())
+ {
+ // test if merged cell overlaps ClipRange at all (needs visualization)
+ if(mxImpl->OverlapsClipRange(nColLeft, nRowTop, nColRight, nRowBottom))
+ {
+ // when merged, get extended coordinate system and derived values
+ // for the full range of this merged cell. Only work with rMergedCell
+ // (which is the top-left single cell of the merged cell) from here on
+ const Cell& rMergedCell(CELL(nColLeft, nRowTop));
+ aCoordinateSystem = rMergedCell.CreateCoordinateSystemMergedCell(
+ *this, nColLeft, nRowTop, nColRight, nRowBottom);
+ aX = basegfx::utils::getColumn(aCoordinateSystem, 0);
+ aY = basegfx::utils::getColumn(aCoordinateSystem, 1);
+ aOrigin = basegfx::utils::getColumn(aCoordinateSystem, 2);
+
+ // check if clip is needed
+ basegfx::B2DRange aClipRange;
+
+ // first use row/col ClipTest for raw check
+ bool bNeedToClip(
+ !mxImpl->IsColInClipRange(nColLeft) ||
+ !mxImpl->IsRowInClipRange(nRowTop) ||
+ !mxImpl->IsColInClipRange(nColRight) ||
+ !mxImpl->IsRowInClipRange(nRowBottom));
+
+ if(bNeedToClip)
+ {
+ // now get ClipRange and CellRange in logical coordinates
+ aClipRange = GetB2DRange(
+ mxImpl->mnFirstClipCol, mxImpl->mnFirstClipRow,
+ mxImpl->mnLastClipCol, mxImpl->mnLastClipRow);
+
+ basegfx::B2DRange aCellRange(
+ GetB2DRange(
+ nColLeft, nRowTop,
+ nColRight, nRowBottom));
+
+ // intersect these to get the target ClipRange, ensure
+ // that clip is needed
+ aClipRange.intersect(aCellRange);
+ bNeedToClip = !aClipRange.isEmpty();
+
+#ifdef OPTICAL_CHECK_CLIPRANGE_FOR_MERGED_CELL
+ aClipRanges.push_back(aClipRange);
+#endif
+ }
+
+ // create top-left to bottom-right geometry
+ HelperCreateTLBREntry(*this, rTLBR, *aData, aOrigin, aX, aY,
+ nColLeft, nRowTop, nColRight, nRowBottom, pForceColor,
+ bNeedToClip ? &aClipRange : nullptr);
+
+ // create bottom-left to top-right geometry
+ HelperCreateBLTREntry(*this, rBLTR, *aData, aOrigin, aX, aY,
+ nColLeft, nRowTop, nColRight, nRowBottom, pForceColor,
+ bNeedToClip ? &aClipRange : nullptr);
+ }
+ }
+ }
+ }
+ else
+ {
+ // must be in clipping range: else not visible. This
+ // already clips completely for non-merged cells
+ if( mxImpl->IsInClipRange( nCol, nRow ) )
+ {
+ // get and check if diagonal styles are used
+ const Style& rTLBR(GetCellStyleTLBR(nCol, nRow));
+ const Style& rBLTR(GetCellStyleBLTR(nCol, nRow));
+
+ if(rTLBR.IsUsed() || rBLTR.IsUsed())
+ {
+ HelperCreateTLBREntry(*this, rTLBR, *aData, aOrigin, aX, aY,
+ nCol, nRow, nCol, nRow, pForceColor, nullptr);
+
+ HelperCreateBLTREntry(*this, rBLTR, *aData, aOrigin, aX, aY,
+ nCol, nRow, nCol, nRow, pForceColor, nullptr);
+ }
+ }
+ }
+ }
+ else if(!aY.equalZero())
+ {
+ // cell has height, but no width. Create left vertical line for this Cell
+ if ((!bOverlapX // true for first column in merged cells or cells
+ || bFirstCol) // true for non_Calc usages of this tooling
+ && !bSuppressLeft) // true when left is not rotated, so edge is already handled (see bRotated)
+ {
+ const Style& rLeft(GetCellStyleLeft(nCol, nRow));
+
+ if (rLeft.IsUsed())
+ {
+ HelperCreateVerticalEntry(*this, rLeft, nCol, nRow, aOrigin, aX, aY, *aData, true, pForceColor);
+ }
+ }
+ }
+ else
+ {
+ // Cell has *no* size, thus no visualization
+ }
+ }
+ }
+
+ // create instance of SdrFrameBorderPrimitive2D if
+ // SdrFrameBorderDataVector is used
+ drawinglayer::primitive2d::Primitive2DContainer aSequence;
+
+ if(!aData->empty())
+ {
+ aSequence.append(
+ drawinglayer::primitive2d::Primitive2DReference(
+ new drawinglayer::primitive2d::SdrFrameBorderPrimitive2D(
+ aData,
+ true))); // force visualization to minimal one discrete unit (pixel)
+ }
+
+#ifdef OPTICAL_CHECK_CLIPRANGE_FOR_MERGED_CELL
+ for(auto const& rClipRange : aClipRanges)
+ {
+ // draw ClipRange in yellow to allow simple interactive optical control in office
+ aSequence.append(
+ drawinglayer::primitive2d::Primitive2DReference(
+ new drawinglayer::primitive2d::PolygonHairlinePrimitive2D(
+ basegfx::utils::createPolygonFromRect(rClipRange),
+ basegfx::BColor(1.0, 1.0, 0.0))));
+ }
+#endif
+
+ return aSequence;
+}
+
+drawinglayer::primitive2d::Primitive2DContainer Array::CreateB2DPrimitiveArray() const
+{
+ drawinglayer::primitive2d::Primitive2DContainer aPrimitives;
+
+ if (mxImpl->mnWidth && mxImpl->mnHeight)
+ {
+ aPrimitives = CreateB2DPrimitiveRange(0, 0, mxImpl->mnWidth - 1, mxImpl->mnHeight - 1, nullptr);
+ }
+
+ return aPrimitives;
+}
+
+#undef ORIGCELL
+#undef LASTCELL
+#undef CELLACC
+#undef CELL
+#undef DBG_FRAME_CHECK_ROW_1
+#undef DBG_FRAME_CHECK_COL_1
+#undef DBG_FRAME_CHECK_COLROW
+#undef DBG_FRAME_CHECK_ROW
+#undef DBG_FRAME_CHECK_COL
+#undef DBG_FRAME_CHECK
+
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/dialog/frmdirlbox.cxx b/svx/source/dialog/frmdirlbox.cxx
new file mode 100644
index 000000000..375a41867
--- /dev/null
+++ b/svx/source/dialog/frmdirlbox.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 <svx/frmdirlbox.hxx>
+
+namespace svx
+{
+FrameDirectionListBox::FrameDirectionListBox(std::unique_ptr<weld::ComboBox> pControl)
+ : m_xControl(std::move(pControl))
+{
+}
+
+FrameDirectionListBox::~FrameDirectionListBox() {}
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/dialog/frmsel.cxx b/svx/source/dialog/frmsel.cxx
new file mode 100644
index 000000000..fed0d59d0
--- /dev/null
+++ b/svx/source/dialog/frmsel.cxx
@@ -0,0 +1,1303 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <config_wasm_strip.h>
+
+#include <sal/config.h>
+
+#include <o3tl/safeint.hxx>
+#include <svx/frmsel.hxx>
+#include <vcl/event.hxx>
+#include <sal/log.hxx>
+#include <tools/debug.hxx>
+#include <svtools/colorcfg.hxx>
+
+#include <algorithm>
+#include <math.h>
+
+#include <frmselimpl.hxx>
+#include <AccessibleFrameSelector.hxx>
+#include <com/sun/star/accessibility/AccessibleEventId.hpp>
+#include <com/sun/star/accessibility/AccessibleStateType.hpp>
+#include <vcl/settings.hxx>
+#include <vcl/svapp.hxx>
+#include <drawinglayer/processor2d/processor2dtools.hxx>
+#include <drawinglayer/processor2d/baseprocessor2d.hxx>
+
+#include <bitmaps.hlst>
+
+using namespace ::com::sun::star;
+using namespace ::editeng;
+
+namespace svx {
+
+using ::com::sun::star::uno::Reference;
+using ::com::sun::star::uno::Any;
+using ::com::sun::star::accessibility::XAccessible;
+using namespace ::com::sun::star::accessibility;
+
+// global functions from framebordertype.hxx
+
+FrameBorderType GetFrameBorderTypeFromIndex( size_t nIndex )
+{
+ DBG_ASSERT( nIndex < o3tl::make_unsigned(FRAMEBORDERTYPE_COUNT),
+ "svx::GetFrameBorderTypeFromIndex - invalid index" );
+ return static_cast< FrameBorderType >( nIndex + 1 );
+}
+
+size_t GetIndexFromFrameBorderType( FrameBorderType eBorder )
+{
+ DBG_ASSERT( eBorder != FrameBorderType::NONE,
+ "svx::GetIndexFromFrameBorderType - invalid frame border type" );
+ return static_cast< size_t >( eBorder ) - 1;
+}
+
+namespace
+{
+
+/** Space between outer control border and any graphical element of the control. */
+const tools::Long FRAMESEL_GEOM_OUTER = 2;
+
+/** Space between arrows and usable inner area. */
+const tools::Long FRAMESEL_GEOM_INNER = 3;
+
+/** Maximum width to draw a frame border style. */
+const tools::Long FRAMESEL_GEOM_WIDTH = 9;
+
+/** Additional margin for click area of outer lines. */
+const tools::Long FRAMESEL_GEOM_ADD_CLICK_OUTER = 5;
+
+/** Additional margin for click area of inner lines. */
+const tools::Long FRAMESEL_GEOM_ADD_CLICK_INNER = 2;
+
+
+/** Returns the corresponding flag for a frame border. */
+FrameSelFlags lclGetFlagFromType( FrameBorderType eBorder )
+{
+ switch( eBorder )
+ {
+ case FrameBorderType::Left: return FrameSelFlags::Left;
+ case FrameBorderType::Right: return FrameSelFlags::Right;
+ case FrameBorderType::Top: return FrameSelFlags::Top;
+ case FrameBorderType::Bottom: return FrameSelFlags::Bottom;
+ case FrameBorderType::Horizontal: return FrameSelFlags::InnerHorizontal;
+ case FrameBorderType::Vertical: return FrameSelFlags::InnerVertical;
+ case FrameBorderType::TLBR: return FrameSelFlags::DiagonalTLBR;
+ case FrameBorderType::BLTR: return FrameSelFlags::DiagonalBLTR;
+ case FrameBorderType::NONE : break;
+ }
+ return FrameSelFlags::NONE;
+}
+
+/** Merges the rSource polypolygon into the rDest polypolygon. */
+void lclPolyPolyUnion( tools::PolyPolygon& rDest, const tools::PolyPolygon& rSource )
+{
+ const tools::PolyPolygon aTmp( rDest );
+ aTmp.GetUnion( rSource, rDest );
+}
+
+} // namespace
+
+FrameBorder::FrameBorder( FrameBorderType eType ) :
+ meType( eType ),
+ meState( FrameBorderState::Hide ),
+ meKeyLeft( FrameBorderType::NONE ),
+ meKeyRight( FrameBorderType::NONE ),
+ meKeyTop( FrameBorderType::NONE ),
+ meKeyBottom( FrameBorderType::NONE ),
+ mbEnabled( false ),
+ mbSelected( false )
+{
+}
+
+void FrameBorder::Enable( FrameSelFlags nFlags )
+{
+ mbEnabled = bool(nFlags & lclGetFlagFromType( meType ));
+ if( !mbEnabled )
+ SetState( FrameBorderState::Hide );
+}
+
+void FrameBorder::SetCoreStyle( const SvxBorderLine* pStyle )
+{
+ if( pStyle )
+ maCoreStyle = *pStyle;
+ else
+ maCoreStyle = SvxBorderLine();
+
+ // from twips to points
+ maUIStyle.Set( &maCoreStyle, FrameBorder::GetDefaultPatternScale(), FRAMESEL_GEOM_WIDTH );
+ meState = maUIStyle.IsUsed() ? FrameBorderState::Show : FrameBorderState::Hide;
+}
+
+void FrameBorder::SetState( FrameBorderState eState )
+{
+ meState = eState;
+ switch( meState )
+ {
+ case FrameBorderState::Show:
+ SAL_WARN( "svx.dialog", "svx::FrameBorder::SetState - use SetCoreStyle to make border visible" );
+ break;
+ case FrameBorderState::Hide:
+ maCoreStyle = SvxBorderLine();
+ maUIStyle.Clear();
+ break;
+ case FrameBorderState::DontCare:
+ maCoreStyle = SvxBorderLine();
+ maUIStyle = frame::Style(3, 0, 0, SvxBorderLineStyle::SOLID, FrameBorder::GetDefaultPatternScale()); //OBJ_FRAMESTYLE_DONTCARE
+ break;
+ }
+}
+
+void FrameBorder::AddFocusPolygon( const tools::Polygon& rFocus )
+{
+ lclPolyPolyUnion( maFocusArea, rFocus );
+}
+
+void FrameBorder::MergeFocusToPolyPolygon( tools::PolyPolygon& rPPoly ) const
+{
+ lclPolyPolyUnion( rPPoly, maFocusArea );
+}
+
+void FrameBorder::AddClickRect( const tools::Rectangle& rRect )
+{
+ lclPolyPolyUnion( maClickArea, tools::Polygon( rRect ) );
+}
+
+bool FrameBorder::ContainsClickPoint( const Point& rPos ) const
+{
+ return vcl::Region( maClickArea ).Contains( rPos );
+}
+
+tools::Rectangle FrameBorder::GetClickBoundRect() const
+{
+ return maClickArea.GetBoundRect();
+}
+
+void FrameBorder::SetKeyboardNeighbors(
+ FrameBorderType eLeft, FrameBorderType eRight, FrameBorderType eTop, FrameBorderType eBottom )
+{
+ meKeyLeft = eLeft;
+ meKeyRight = eRight;
+ meKeyTop = eTop;
+ meKeyBottom = eBottom;
+}
+
+FrameBorderType FrameBorder::GetKeyboardNeighbor( sal_uInt16 nKeyCode ) const
+{
+ FrameBorderType eBorder = FrameBorderType::NONE;
+ switch( nKeyCode )
+ {
+ case KEY_LEFT: eBorder = meKeyLeft; break;
+ case KEY_RIGHT: eBorder = meKeyRight; break;
+ case KEY_UP: eBorder = meKeyTop; break;
+ case KEY_DOWN: eBorder = meKeyBottom; break;
+ default: SAL_WARN( "svx.dialog", "svx::FrameBorder::GetKeyboardNeighbor - unknown key code" );
+ }
+ return eBorder;
+}
+
+FrameSelectorImpl::FrameSelectorImpl( FrameSelector& rFrameSel ) :
+ mrFrameSel( rFrameSel ),
+ mpVirDev( VclPtr<VirtualDevice>::Create() ),
+ maLeft( FrameBorderType::Left ),
+ maRight( FrameBorderType::Right ),
+ maTop( FrameBorderType::Top ),
+ maBottom( FrameBorderType::Bottom ),
+ maHor( FrameBorderType::Horizontal ),
+ maVer( FrameBorderType::Vertical ),
+ maTLBR( FrameBorderType::TLBR ),
+ maBLTR( FrameBorderType::BLTR ),
+ mnFlags( FrameSelFlags::Outer ),
+ mnCtrlSize( 0 ),
+ mnArrowSize( 0 ),
+ mnLine1( 0 ),
+ mnLine2( 0 ),
+ mnLine3( 0 ),
+ mnFocusOffs( 0 ),
+ mbHor( false ),
+ mbVer( false ),
+ mbTLBR( false ),
+ mbBLTR( false ),
+ mbFullRepaint( true ),
+ mbAutoSelect( true ),
+ mbHCMode( false )
+#if !ENABLE_WASM_STRIP_ACCESSIBILITY
+ ,maChildVec( 8 )
+#endif
+{
+ maAllBorders.resize( FRAMEBORDERTYPE_COUNT, nullptr );
+ maAllBorders[ GetIndexFromFrameBorderType( FrameBorderType::Left ) ] = &maLeft;
+ maAllBorders[ GetIndexFromFrameBorderType( FrameBorderType::Right ) ] = &maRight;
+ maAllBorders[ GetIndexFromFrameBorderType( FrameBorderType::Top ) ] = &maTop;
+ maAllBorders[ GetIndexFromFrameBorderType( FrameBorderType::Bottom ) ] = &maBottom;
+ maAllBorders[ GetIndexFromFrameBorderType( FrameBorderType::Horizontal ) ] = &maHor;
+ maAllBorders[ GetIndexFromFrameBorderType( FrameBorderType::Vertical ) ] = &maVer;
+ maAllBorders[ GetIndexFromFrameBorderType( FrameBorderType::TLBR ) ] = &maTLBR;
+ maAllBorders[ GetIndexFromFrameBorderType( FrameBorderType::BLTR ) ] = &maBLTR;
+#if OSL_DEBUG_LEVEL >= 2
+ {
+ bool bOk = true;
+ for( FrameBorderCIter aIt( maAllBorders ); bOk && aIt.Is(); bOk = (*aIt != 0), ++aIt );
+ DBG_ASSERT( bOk, "svx::FrameSelectorImpl::FrameSelectorImpl - missing entry in maAllBorders" );
+ }
+#endif
+ // left neighbor right neighbor upper neighbor lower neighbor
+ maLeft.SetKeyboardNeighbors( FrameBorderType::NONE, FrameBorderType::TLBR, FrameBorderType::Top, FrameBorderType::Bottom );
+ maRight.SetKeyboardNeighbors( FrameBorderType::BLTR, FrameBorderType::NONE, FrameBorderType::Top, FrameBorderType::Bottom );
+ maTop.SetKeyboardNeighbors( FrameBorderType::Left, FrameBorderType::Right, FrameBorderType::NONE, FrameBorderType::TLBR );
+ maBottom.SetKeyboardNeighbors( FrameBorderType::Left, FrameBorderType::Right, FrameBorderType::BLTR, FrameBorderType::NONE );
+ maHor.SetKeyboardNeighbors( FrameBorderType::Left, FrameBorderType::Right, FrameBorderType::TLBR, FrameBorderType::BLTR );
+ maVer.SetKeyboardNeighbors( FrameBorderType::TLBR, FrameBorderType::BLTR, FrameBorderType::Top, FrameBorderType::Bottom );
+ maTLBR.SetKeyboardNeighbors( FrameBorderType::Left, FrameBorderType::Vertical, FrameBorderType::Top, FrameBorderType::Horizontal );
+ maBLTR.SetKeyboardNeighbors( FrameBorderType::Vertical, FrameBorderType::Right, FrameBorderType::Horizontal, FrameBorderType::Bottom );
+
+ Initialize(mnFlags);
+}
+
+FrameSelectorImpl::~FrameSelectorImpl()
+{
+#if !ENABLE_WASM_STRIP_ACCESSIBILITY
+ for( auto& rpChild : maChildVec )
+ if( rpChild.is() )
+ rpChild->Invalidate();
+#endif
+}
+
+// initialization
+void FrameSelectorImpl::Initialize( FrameSelFlags nFlags )
+{
+ mnFlags = nFlags;
+
+ maEnabBorders.clear();
+ for( FrameBorderIter aIt( maAllBorders ); aIt.Is(); ++aIt )
+ {
+ (*aIt)->Enable( mnFlags );
+ if( (*aIt)->IsEnabled() )
+ maEnabBorders.push_back( *aIt );
+ }
+ mbHor = maHor.IsEnabled();
+ mbVer = maVer.IsEnabled();
+ mbTLBR = maTLBR.IsEnabled();
+ mbBLTR = maBLTR.IsEnabled();
+
+ InitVirtualDevice();
+}
+
+void FrameSelectorImpl::InitColors()
+{
+ const StyleSettings& rSettings = Application::GetSettings().GetStyleSettings();
+ svtools::ColorConfig aColorConfig;
+ maBackCol = aColorConfig.GetColorValue(svtools::DOCCOLOR).nColor;
+ mbHCMode = rSettings.GetHighContrastMode();
+ maArrowCol = aColorConfig.GetColorValue(svtools::DOCBOUNDARIES).nColor;
+ maMarkCol = aColorConfig.GetColorValue(svtools::TABLEBOUNDARIES).nColor;
+ maHCLineCol = COL_BLACK;
+}
+
+constexpr rtl::OUStringConstExpr aImageIds[] =
+{
+ RID_SVXBMP_FRMSEL_ARROW1,
+ RID_SVXBMP_FRMSEL_ARROW2,
+ RID_SVXBMP_FRMSEL_ARROW3,
+ RID_SVXBMP_FRMSEL_ARROW4,
+ RID_SVXBMP_FRMSEL_ARROW5,
+ RID_SVXBMP_FRMSEL_ARROW6,
+ RID_SVXBMP_FRMSEL_ARROW7,
+ RID_SVXBMP_FRMSEL_ARROW8,
+ RID_SVXBMP_FRMSEL_ARROW9,
+ RID_SVXBMP_FRMSEL_ARROW10,
+ RID_SVXBMP_FRMSEL_ARROW11,
+ RID_SVXBMP_FRMSEL_ARROW12,
+ RID_SVXBMP_FRMSEL_ARROW13,
+ RID_SVXBMP_FRMSEL_ARROW14,
+ RID_SVXBMP_FRMSEL_ARROW15,
+ RID_SVXBMP_FRMSEL_ARROW16
+};
+
+void FrameSelectorImpl::InitArrowImageList()
+{
+ maArrows.clear();
+
+ /* Build the arrow images bitmap with current colors. */
+ Color pColorAry1[3];
+ Color pColorAry2[3];
+ pColorAry1[0] = Color( 0, 0, 0 );
+ pColorAry2[0] = maArrowCol; // black -> arrow color
+ pColorAry1[1] = Color( 0, 255, 0 );
+ pColorAry2[1] = maMarkCol; // green -> marker color
+ pColorAry1[2] = Color( 255, 0, 255 );
+ pColorAry2[2] = maBackCol; // magenta -> background
+
+ assert(SAL_N_ELEMENTS(aImageIds) == 16);
+ for (size_t i = 0; i < SAL_N_ELEMENTS(aImageIds); ++i)
+ {
+ BitmapEx aBmpEx { OUString(aImageIds[i]) };
+ aBmpEx.Replace(pColorAry1, pColorAry2, 3);
+ maArrows.emplace_back(aBmpEx);
+ }
+ assert(maArrows.size() == 16);
+
+ mnArrowSize = maArrows[0].GetSizePixel().Height();
+}
+
+void FrameSelectorImpl::InitGlobalGeometry()
+{
+ Size aCtrlSize(mrFrameSel.GetOutputSizePixel());
+ /* nMinSize is the lower of width and height (control will always be squarish).
+ FRAMESEL_GEOM_OUTER is the minimal distance between inner control border
+ and any element. */
+ tools::Long nMinSize = std::min( aCtrlSize.Width(), aCtrlSize.Height() ) - 2 * FRAMESEL_GEOM_OUTER;
+ /* nFixedSize is the size all existing elements need in one direction:
+ the diag. arrow, space betw. arrow and frame border, outer frame border,
+ inner frame border, other outer frame border, space betw. frame border
+ and arrow, the other arrow. */
+ tools::Long nFixedSize = 2 * mnArrowSize + 2 * FRAMESEL_GEOM_INNER + 3 * FRAMESEL_GEOM_WIDTH;
+ /* nBetwBordersSize contains the size between an outer and inner frame border (made odd). */
+ tools::Long nBetwBordersSize = (((nMinSize - nFixedSize) / 2) - 1) | 1;
+
+ /* The final size of the usable area. At least do not get negative */
+ mnCtrlSize = 2 * nBetwBordersSize + nFixedSize;
+ mnCtrlSize = std::max(mnCtrlSize, static_cast<tools::Long>(0));
+ mpVirDev->SetOutputSizePixel( Size( mnCtrlSize, mnCtrlSize ) );
+
+ /* Center the virtual device in the control. */
+ maVirDevPos = Point( (aCtrlSize.Width() - mnCtrlSize) / 2, (aCtrlSize.Height() - mnCtrlSize) / 2 );
+}
+
+void FrameSelectorImpl::InitBorderGeometry()
+{
+ size_t nCol, nCols, nRow, nRows;
+
+ // Global border geometry values
+ /* mnLine* is the middle point inside a frame border (i.e. mnLine1 is mid X inside left border). */
+ mnLine1 = mnArrowSize + FRAMESEL_GEOM_INNER + FRAMESEL_GEOM_WIDTH / 2;
+ mnLine2 = mnCtrlSize / 2;
+ mnLine3 = 2 * mnLine2 - mnLine1;
+
+ // Frame helper array
+ maArray.Initialize( mbVer ? 2 : 1, mbHor ? 2 : 1 );
+
+ maArray.SetXOffset( mnLine1 );
+ maArray.SetAllColWidths( (mbVer ? mnLine2 : mnLine3) - mnLine1 );
+
+ maArray.SetYOffset( mnLine1 );
+ maArray.SetAllRowHeights( (mbHor ? mnLine2 : mnLine3) - mnLine1 );
+
+ // Focus polygons
+ /* Width for focus rectangles from center of frame borders. */
+ mnFocusOffs = FRAMESEL_GEOM_WIDTH / 2 + 1;
+
+ maLeft.ClearFocusArea();
+ maVer.ClearFocusArea();
+ maRight.ClearFocusArea();
+ maTop.ClearFocusArea();
+ maHor.ClearFocusArea();
+ maBottom.ClearFocusArea();
+
+ maLeft.AddFocusPolygon( tools::Rectangle( mnLine1 - mnFocusOffs, mnLine1 - mnFocusOffs, mnLine1 + mnFocusOffs, mnLine3 + mnFocusOffs ) );
+ maVer.AddFocusPolygon( tools::Rectangle( mnLine2 - mnFocusOffs, mnLine1 - mnFocusOffs, mnLine2 + mnFocusOffs, mnLine3 + mnFocusOffs ) );
+ maRight.AddFocusPolygon( tools::Rectangle( mnLine3 - mnFocusOffs, mnLine1 - mnFocusOffs, mnLine3 + mnFocusOffs, mnLine3 + mnFocusOffs ) );
+ maTop.AddFocusPolygon( tools::Rectangle( mnLine1 - mnFocusOffs, mnLine1 - mnFocusOffs, mnLine3 + mnFocusOffs, mnLine1 + mnFocusOffs ) );
+ maHor.AddFocusPolygon( tools::Rectangle( mnLine1 - mnFocusOffs, mnLine2 - mnFocusOffs, mnLine3 + mnFocusOffs, mnLine2 + mnFocusOffs ) );
+ maBottom.AddFocusPolygon( tools::Rectangle( mnLine1 - mnFocusOffs, mnLine3 - mnFocusOffs, mnLine3 + mnFocusOffs, mnLine3 + mnFocusOffs ) );
+
+ maTLBR.ClearFocusArea();
+ maBLTR.ClearFocusArea();
+
+ for( nCol = 0, nCols = maArray.GetColCount(); nCol < nCols; ++nCol )
+ {
+ for( nRow = 0, nRows = maArray.GetRowCount(); nRow < nRows; ++nRow )
+ {
+ const basegfx::B2DRange aCellRange(maArray.GetCellRange( nCol, nRow ));
+ const tools::Rectangle aRect(
+ basegfx::fround(aCellRange.getMinX()), basegfx::fround(aCellRange.getMinY()),
+ basegfx::fround(aCellRange.getMaxX()), basegfx::fround(aCellRange.getMaxY()));
+ const double fHorDiagAngle(atan2(fabs(aCellRange.getHeight()), fabs(aCellRange.getWidth())));
+ const double fVerDiagAngle(fHorDiagAngle > 0.0 ? M_PI_2 - fHorDiagAngle : 0.0);
+ const tools::Long nDiagFocusOffsX(basegfx::fround(-mnFocusOffs / tan(fHorDiagAngle) + mnFocusOffs / sin(fHorDiagAngle)));
+ const tools::Long nDiagFocusOffsY(basegfx::fround(-mnFocusOffs / tan(fVerDiagAngle) + mnFocusOffs / sin(fVerDiagAngle)));
+
+ std::vector< Point > aFocusVec;
+ aFocusVec.emplace_back( aRect.Left() - mnFocusOffs, aRect.Top() + nDiagFocusOffsY );
+ aFocusVec.emplace_back( aRect.Left() - mnFocusOffs, aRect.Top() - mnFocusOffs );
+ aFocusVec.emplace_back( aRect.Left() + nDiagFocusOffsX, aRect.Top() - mnFocusOffs );
+ aFocusVec.emplace_back( aRect.Right() + mnFocusOffs, aRect.Bottom() - nDiagFocusOffsY );
+ aFocusVec.emplace_back( aRect.Right() + mnFocusOffs, aRect.Bottom() + mnFocusOffs );
+ aFocusVec.emplace_back( aRect.Right() - nDiagFocusOffsX, aRect.Bottom() + mnFocusOffs );
+ maTLBR.AddFocusPolygon( tools::Polygon( static_cast< sal_uInt16 >( aFocusVec.size() ), aFocusVec.data() ) );
+
+ aFocusVec.clear();
+ aFocusVec.emplace_back( aRect.Right() + mnFocusOffs, aRect.Top() + nDiagFocusOffsY );
+ aFocusVec.emplace_back( aRect.Right() + mnFocusOffs, aRect.Top() - mnFocusOffs );
+ aFocusVec.emplace_back( aRect.Right() - nDiagFocusOffsX, aRect.Top() - mnFocusOffs );
+ aFocusVec.emplace_back( aRect.Left() - mnFocusOffs, aRect.Bottom() - nDiagFocusOffsY );
+ aFocusVec.emplace_back( aRect.Left() - mnFocusOffs, aRect.Bottom() + mnFocusOffs );
+ aFocusVec.emplace_back( aRect.Left() + nDiagFocusOffsX, aRect.Bottom() + mnFocusOffs );
+ maBLTR.AddFocusPolygon( tools::Polygon( static_cast< sal_uInt16 >( aFocusVec.size() ), aFocusVec.data() ) );
+ }
+ }
+
+ // Click areas
+ for( FrameBorderIter aIt( maAllBorders ); aIt.Is(); ++aIt )
+ (*aIt)->ClearClickArea();
+
+ /* Additional space for click area: is added to the space available to draw
+ the frame borders. For instance left frame border:
+ - To left, top, and bottom always big additional space (outer area).
+ - To right: Dependent on existence of inner vertical frame border
+ (if enabled, use less space).
+ */
+ tools::Long nClO = FRAMESEL_GEOM_WIDTH / 2 + FRAMESEL_GEOM_ADD_CLICK_OUTER;
+ tools::Long nClI = (mbTLBR && mbBLTR) ? (FRAMESEL_GEOM_WIDTH / 2 + FRAMESEL_GEOM_ADD_CLICK_INNER) : nClO;
+ tools::Long nClH = mbHor ? nClI : nClO; // additional space dependent of horizontal inner border
+ tools::Long nClV = mbVer ? nClI : nClO; // additional space dependent of vertical inner border
+
+ maLeft.AddClickRect( tools::Rectangle( mnLine1 - nClO, mnLine1 - nClO, mnLine1 + nClV, mnLine3 + nClO ) );
+ maVer.AddClickRect( tools::Rectangle( mnLine2 - nClI, mnLine1 - nClO, mnLine2 + nClI, mnLine3 + nClO ) );
+ maRight.AddClickRect( tools::Rectangle( mnLine3 - nClV, mnLine1 - nClO, mnLine3 + nClO, mnLine3 + nClO ) );
+ maTop.AddClickRect( tools::Rectangle( mnLine1 - nClO, mnLine1 - nClO, mnLine3 + nClO, mnLine1 + nClH ) );
+ maHor.AddClickRect( tools::Rectangle( mnLine1 - nClO, mnLine2 - nClI, mnLine3 + nClO, mnLine2 + nClI ) );
+ maBottom.AddClickRect( tools::Rectangle( mnLine1 - nClO, mnLine3 - nClH, mnLine3 + nClO, mnLine3 + nClO ) );
+
+ /* Diagonal frame borders use the remaining space between outer and inner frame borders. */
+ if( !(mbTLBR || mbBLTR) )
+ return;
+
+ for( nCol = 0, nCols = maArray.GetColCount(); nCol < nCols; ++nCol )
+ {
+ for( nRow = 0, nRows = maArray.GetRowCount(); nRow < nRows; ++nRow )
+ {
+ // the usable area between horizontal/vertical frame borders of current quadrant
+ const basegfx::B2DRange aCellRange(maArray.GetCellRange( nCol, nRow ));
+ const tools::Rectangle aRect(
+ basegfx::fround(aCellRange.getMinX()) + nClV + 1, basegfx::fround(aCellRange.getMinY()) + nClH + 1,
+ basegfx::fround(aCellRange.getMaxX()) - nClV + 1, basegfx::fround(aCellRange.getMaxY()) - nClH + 1);
+
+ /* Both diagonal frame borders enabled. */
+ if( mbTLBR && mbBLTR )
+ {
+ // single areas
+ Point aMid( aRect.Center() );
+ maTLBR.AddClickRect( tools::Rectangle( aRect.TopLeft(), aMid ) );
+ maTLBR.AddClickRect( tools::Rectangle( aMid + Point( 1, 1 ), aRect.BottomRight() ) );
+ maBLTR.AddClickRect( tools::Rectangle( aRect.Left(), aMid.Y() + 1, aMid.X(), aRect.Bottom() ) );
+ maBLTR.AddClickRect( tools::Rectangle( aMid.X() + 1, aRect.Top(), aRect.Right(), aMid.Y() ) );
+ // centered rectangle for both frame borders
+ tools::Rectangle aMidRect( aRect.TopLeft(), Size( aRect.GetWidth() / 3, aRect.GetHeight() / 3 ) );
+ aMidRect.Move( (aRect.GetWidth() - aMidRect.GetWidth()) / 2, (aRect.GetHeight() - aMidRect.GetHeight()) / 2 );
+ maTLBR.AddClickRect( aMidRect );
+ maBLTR.AddClickRect( aMidRect );
+ }
+ /* One of the diagonal frame borders enabled - use entire rectangle. */
+ else if( mbTLBR && !mbBLTR ) // top-left to bottom-right only
+ maTLBR.AddClickRect( aRect );
+ else if( !mbTLBR && mbBLTR ) // bottom-left to top-right only
+ maBLTR.AddClickRect( aRect );
+ }
+ }
+}
+
+void FrameSelectorImpl::InitVirtualDevice()
+{
+ // initialize resources
+ InitColors();
+ InitArrowImageList();
+
+ sizeChanged();
+}
+
+void FrameSelectorImpl::sizeChanged()
+{
+ // initialize geometry
+ InitGlobalGeometry();
+ InitBorderGeometry();
+
+ DoInvalidate( true );
+}
+
+// frame border access
+const FrameBorder& FrameSelectorImpl::GetBorder( FrameBorderType eBorder ) const
+{
+ size_t nIndex = GetIndexFromFrameBorderType( eBorder );
+ if( nIndex < maAllBorders.size() )
+ return *maAllBorders[ nIndex ];
+ SAL_WARN( "svx.dialog", "svx::FrameSelectorImpl::GetBorder - unknown border type" );
+ return maTop;
+}
+
+FrameBorder& FrameSelectorImpl::GetBorderAccess( FrameBorderType eBorder )
+{
+ return const_cast< FrameBorder& >( GetBorder( eBorder ) );
+}
+
+// drawing
+void FrameSelectorImpl::DrawBackground()
+{
+ // clear the area
+ mpVirDev->SetLineColor();
+ mpVirDev->SetFillColor( maBackCol );
+ mpVirDev->DrawRect( tools::Rectangle( Point( 0, 0 ), mpVirDev->GetOutputSizePixel() ) );
+
+ // draw the inner gray (or whatever color) rectangle
+ mpVirDev->SetLineColor();
+ mpVirDev->SetFillColor( maMarkCol );
+ mpVirDev->DrawRect( tools::Rectangle(
+ mnLine1 - mnFocusOffs, mnLine1 - mnFocusOffs, mnLine3 + mnFocusOffs, mnLine3 + mnFocusOffs ) );
+
+ // draw the white space for enabled frame borders
+ tools::PolyPolygon aPPoly;
+ for( FrameBorderCIter aIt( maEnabBorders ); aIt.Is(); ++aIt )
+ (*aIt)->MergeFocusToPolyPolygon( aPPoly );
+ aPPoly.Optimize( PolyOptimizeFlags::CLOSE );
+ mpVirDev->SetLineColor( maBackCol );
+ mpVirDev->SetFillColor( maBackCol );
+ mpVirDev->DrawPolyPolygon( aPPoly );
+}
+
+void FrameSelectorImpl::DrawArrows( const FrameBorder& rBorder )
+{
+ DBG_ASSERT( rBorder.IsEnabled(), "svx::FrameSelectorImpl::DrawArrows - access to disabled border" );
+
+ tools::Long nLinePos = 0;
+ switch( rBorder.GetType() )
+ {
+ case FrameBorderType::Left:
+ case FrameBorderType::Top: nLinePos = mnLine1; break;
+ case FrameBorderType::Vertical:
+ case FrameBorderType::Horizontal: nLinePos = mnLine2; break;
+ case FrameBorderType::Right:
+ case FrameBorderType::Bottom: nLinePos = mnLine3; break;
+ default: ; //prevent warning
+ }
+ nLinePos -= mnArrowSize / 2;
+
+ tools::Long nTLPos = 0;
+ tools::Long nBRPos = mnCtrlSize - mnArrowSize;
+ Point aPos1, aPos2;
+ int nImgIndex1 = -1, nImgIndex2 = -1;
+ switch( rBorder.GetType() )
+ {
+ case FrameBorderType::Left:
+ case FrameBorderType::Right:
+ case FrameBorderType::Vertical:
+ aPos1 = Point( nLinePos, nTLPos ); nImgIndex1 = 0;
+ aPos2 = Point( nLinePos, nBRPos ); nImgIndex2 = 1;
+ break;
+
+ case FrameBorderType::Top:
+ case FrameBorderType::Bottom:
+ case FrameBorderType::Horizontal:
+ aPos1 = Point( nTLPos, nLinePos ); nImgIndex1 = 2;
+ aPos2 = Point( nBRPos, nLinePos ); nImgIndex2 = 3;
+ break;
+
+ case FrameBorderType::TLBR:
+ aPos1 = Point( nTLPos, nTLPos ); nImgIndex1 = 4;
+ aPos2 = Point( nBRPos, nBRPos ); nImgIndex2 = 5;
+ break;
+ case FrameBorderType::BLTR:
+ aPos1 = Point( nTLPos, nBRPos ); nImgIndex1 = 6;
+ aPos2 = Point( nBRPos, nTLPos ); nImgIndex2 = 7;
+ break;
+ default: ; //prevent warning
+ }
+
+ // Arrow or marker? Do not draw arrows into disabled control.
+ sal_uInt16 nSelectAdd = (mrFrameSel.IsEnabled() && rBorder.IsSelected()) ? 0 : 8;
+ if (nImgIndex1 >= 0)
+ mpVirDev->DrawImage(aPos1, maArrows[nImgIndex1 + nSelectAdd]);
+ if (nImgIndex2 >= 0)
+ mpVirDev->DrawImage(aPos2, maArrows[nImgIndex2 + nSelectAdd]);
+}
+
+Color FrameSelectorImpl::GetDrawLineColor( const Color& rColor ) const
+{
+ Color aColor( mbHCMode ? maHCLineCol : rColor );
+ if( aColor == maBackCol )
+ aColor.Invert();
+ return aColor;
+}
+
+void FrameSelectorImpl::DrawAllFrameBorders()
+{
+ // Translate core colors to current UI colors (regards current background and HC mode).
+ for( FrameBorderIter aIt( maEnabBorders ); aIt.Is(); ++aIt )
+ {
+ Color aCoreColorPrim = ((*aIt)->GetState() == FrameBorderState::DontCare) ? maMarkCol : (*aIt)->GetCoreStyle().GetColorOut();
+ Color aCoreColorSecn = ((*aIt)->GetState() == FrameBorderState::DontCare) ? maMarkCol : (*aIt)->GetCoreStyle().GetColorIn();
+ (*aIt)->SetUIColorPrim( GetDrawLineColor( aCoreColorPrim ) );
+ (*aIt)->SetUIColorSecn( GetDrawLineColor( aCoreColorSecn ) );
+ }
+
+ // Copy all frame border styles to the helper array
+ maArray.SetColumnStyleLeft( 0, maLeft.GetUIStyle() );
+ if( mbVer ) maArray.SetColumnStyleLeft( 1, maVer.GetUIStyle() );
+
+ // Invert the style for the right line
+ const frame::Style rRightStyle = maRight.GetUIStyle( );
+ frame::Style rInvertedRight( rRightStyle.GetColorPrim(),
+ rRightStyle.GetColorSecn(), rRightStyle.GetColorGap(),
+ rRightStyle.UseGapColor(),
+ rRightStyle.Secn(), rRightStyle.Dist(), rRightStyle.Prim( ),
+ rRightStyle.Type( ), rRightStyle.PatternScale() );
+ maArray.SetColumnStyleRight( mbVer ? 1 : 0, rInvertedRight );
+
+ maArray.SetRowStyleTop( 0, maTop.GetUIStyle() );
+ if( mbHor )
+ {
+ // Invert the style for the hor line to match the real borders
+ const frame::Style rHorStyle = maHor.GetUIStyle();
+ frame::Style rInvertedHor( rHorStyle.GetColorPrim(),
+ rHorStyle.GetColorSecn(), rHorStyle.GetColorGap(),
+ rHorStyle.UseGapColor(),
+ rHorStyle.Secn(), rHorStyle.Dist(), rHorStyle.Prim( ),
+ rHorStyle.Type(), rHorStyle.PatternScale() );
+ maArray.SetRowStyleTop( 1, rInvertedHor );
+ }
+
+ // Invert the style for the bottom line
+ const frame::Style rBottomStyle = maBottom.GetUIStyle( );
+ frame::Style rInvertedBottom( rBottomStyle.GetColorPrim(),
+ rBottomStyle.GetColorSecn(), rBottomStyle.GetColorGap(),
+ rBottomStyle.UseGapColor(),
+ rBottomStyle.Secn(), rBottomStyle.Dist(), rBottomStyle.Prim( ),
+ rBottomStyle.Type(), rBottomStyle.PatternScale() );
+ maArray.SetRowStyleBottom( mbHor ? 1 : 0, rInvertedBottom );
+
+ for( sal_Int32 nCol = 0; nCol < maArray.GetColCount(); ++nCol )
+ for( sal_Int32 nRow = 0; nRow < maArray.GetRowCount(); ++nRow )
+ maArray.SetCellStyleDiag( nCol, nRow, maTLBR.GetUIStyle(), maBLTR.GetUIStyle() );
+
+ // This is used in the dialog/control for 'Border' attributes. When using
+ // the original paint below instead of primitives, the advantage currently
+ // is the correct visualization of diagonal line(s) including overlaying,
+ // but the rest is bad. Since the edit views use primitives and the preview
+ // should be 'real' I opt for also changing this to primitives. I will
+ // keep the old solution and add a switch (above) based on a static bool so
+ // that interested people may test this out in the debugger.
+ // This is one more hint to enhance the primitive visualization further to
+ // support diagonals better - that's the way to go.
+ const drawinglayer::geometry::ViewInformation2D aNewViewInformation2D;
+ std::unique_ptr<drawinglayer::processor2d::BaseProcessor2D> pProcessor2D(
+ drawinglayer::processor2d::createPixelProcessor2DFromOutputDevice(
+ *mpVirDev,
+ aNewViewInformation2D));
+
+ pProcessor2D->process(maArray.CreateB2DPrimitiveArray());
+ pProcessor2D.reset();
+}
+
+void FrameSelectorImpl::DrawVirtualDevice()
+{
+ DrawBackground();
+ for(FrameBorderCIter aIt(maEnabBorders); aIt.Is(); ++aIt)
+ DrawArrows(**aIt);
+ DrawAllFrameBorders();
+ mbFullRepaint = false;
+}
+
+void FrameSelectorImpl::CopyVirDevToControl(vcl::RenderContext& rRenderContext)
+{
+ if (mbFullRepaint)
+ DrawVirtualDevice();
+ rRenderContext.DrawBitmapEx(maVirDevPos, mpVirDev->GetBitmapEx(Point(0, 0), mpVirDev->GetOutputSizePixel()));
+}
+
+void FrameSelectorImpl::DrawAllTrackingRects(vcl::RenderContext& rRenderContext)
+{
+ tools::PolyPolygon aPPoly;
+ if (mrFrameSel.IsAnyBorderSelected())
+ {
+ for(SelFrameBorderCIter aIt( maEnabBorders ); aIt.Is(); ++aIt)
+ (*aIt)->MergeFocusToPolyPolygon(aPPoly);
+ aPPoly.Move(maVirDevPos.X(), maVirDevPos.Y());
+ }
+ else
+ // no frame border selected -> draw tracking rectangle around entire control
+ aPPoly.Insert( tools::Polygon(tools::Rectangle(maVirDevPos, mpVirDev->GetOutputSizePixel())));
+
+ aPPoly.Optimize(PolyOptimizeFlags::CLOSE);
+
+ for(sal_uInt16 nIdx = 0, nCount = aPPoly.Count(); nIdx < nCount; ++nIdx)
+ rRenderContext.Invert(aPPoly.GetObject(nIdx), InvertFlags::TrackFrame);
+}
+
+Point FrameSelectorImpl::GetDevPosFromMousePos( const Point& rMousePos ) const
+{
+ return rMousePos - maVirDevPos;
+}
+
+void FrameSelectorImpl::DoInvalidate( bool bFullRepaint )
+{
+ mbFullRepaint |= bFullRepaint;
+ mrFrameSel.Invalidate();
+}
+
+// frame border state and style
+void FrameSelectorImpl::SetBorderState( FrameBorder& rBorder, FrameBorderState eState )
+{
+ DBG_ASSERT( rBorder.IsEnabled(), "svx::FrameSelectorImpl::SetBorderState - access to disabled border" );
+ Any aOld;
+ Any aNew;
+ Any& rMod = eState == FrameBorderState::Show ? aNew : aOld;
+ rMod <<= AccessibleStateType::CHECKED;
+
+#if !ENABLE_WASM_STRIP_ACCESSIBILITY
+ rtl::Reference< a11y::AccFrameSelectorChild > xRet;
+ size_t nVecIdx = static_cast< size_t >( rBorder.GetType() );
+ if( GetBorder(rBorder.GetType()).IsEnabled() && (1 <= nVecIdx) && (nVecIdx <= maChildVec.size()) )
+ xRet = maChildVec[ --nVecIdx ].get();
+#endif
+
+ if( eState == FrameBorderState::Show )
+ SetBorderCoreStyle( rBorder, &maCurrStyle );
+ else
+ rBorder.SetState( eState );
+
+#if !ENABLE_WASM_STRIP_ACCESSIBILITY
+ if (xRet.is())
+ xRet->NotifyAccessibleEvent( AccessibleEventId::STATE_CHANGED, aOld, aNew );
+#endif
+
+ DoInvalidate( true );
+}
+
+void FrameSelectorImpl::SetBorderCoreStyle( FrameBorder& rBorder, const SvxBorderLine* pStyle )
+{
+ DBG_ASSERT( rBorder.IsEnabled(), "svx::FrameSelectorImpl::SetBorderCoreStyle - access to disabled border" );
+ rBorder.SetCoreStyle( pStyle );
+ DoInvalidate( true );
+}
+
+void FrameSelectorImpl::ToggleBorderState( FrameBorder& rBorder )
+{
+ bool bDontCare = mrFrameSel.SupportsDontCareState();
+ switch( rBorder.GetState() )
+ {
+ // same order as tristate check box: visible -> don't care -> hidden
+ case FrameBorderState::Show:
+ SetBorderState( rBorder, bDontCare ? FrameBorderState::DontCare : FrameBorderState::Hide );
+ break;
+ case FrameBorderState::Hide:
+ SetBorderState( rBorder, FrameBorderState::Show );
+ break;
+ case FrameBorderState::DontCare:
+ SetBorderState( rBorder, FrameBorderState::Hide );
+ break;
+ }
+}
+
+// frame border selection
+void FrameSelectorImpl::SelectBorder( FrameBorder& rBorder, bool bSelect )
+{
+ DBG_ASSERT( rBorder.IsEnabled(), "svx::FrameSelectorImpl::SelectBorder - access to disabled border" );
+ rBorder.Select( bSelect );
+ DrawArrows( rBorder );
+ DoInvalidate( false );
+}
+
+void FrameSelectorImpl::SilentGrabFocus()
+{
+ bool bOldAuto = mbAutoSelect;
+ mbAutoSelect = false;
+ mrFrameSel.GrabFocus();
+ mbAutoSelect = bOldAuto;
+}
+
+bool FrameSelectorImpl::SelectedBordersEqual() const
+{
+ bool bEqual = true;
+ SelFrameBorderCIter aIt( maEnabBorders );
+ if( aIt.Is() )
+ {
+ const SvxBorderLine& rFirstStyle = (*aIt)->GetCoreStyle();
+ for( ++aIt; bEqual && aIt.Is(); ++aIt )
+ bEqual = ((*aIt)->GetCoreStyle() == rFirstStyle);
+ }
+ return bEqual;
+}
+
+FrameSelector::FrameSelector()
+{
+}
+
+void FrameSelector::SetDrawingArea(weld::DrawingArea* pDrawingArea)
+{
+ CustomWidgetController::SetDrawingArea(pDrawingArea);
+ mxImpl.reset( new FrameSelectorImpl( *this ) );
+ Size aPrefSize = pDrawingArea->get_ref_device().LogicToPixel(Size(61, 65), MapMode(MapUnit::MapAppFont));
+ pDrawingArea->set_size_request(aPrefSize.Width(), aPrefSize.Height());
+ EnableRTL( false ); // #107808# don't mirror the mouse handling
+}
+
+FrameSelector::~FrameSelector()
+{
+#if !ENABLE_WASM_STRIP_ACCESSIBILITY
+ if( mxAccess.is() )
+ mxAccess->Invalidate();
+#endif
+}
+
+void FrameSelector::Initialize( FrameSelFlags nFlags )
+{
+ mxImpl->Initialize( nFlags );
+ Show();
+}
+
+// enabled frame borders
+bool FrameSelector::IsBorderEnabled( FrameBorderType eBorder ) const
+{
+ return mxImpl->GetBorder( eBorder ).IsEnabled();
+}
+
+sal_Int32 FrameSelector::GetEnabledBorderCount() const
+{
+ return static_cast< sal_Int32 >( mxImpl->maEnabBorders.size() );
+}
+
+FrameBorderType FrameSelector::GetEnabledBorderType( sal_Int32 nIndex ) const
+{
+ FrameBorderType eBorder = FrameBorderType::NONE;
+ if( nIndex >= 0 )
+ {
+ size_t nVecIdx = static_cast< size_t >( nIndex );
+ if( nVecIdx < mxImpl->maEnabBorders.size() )
+ eBorder = mxImpl->maEnabBorders[ nVecIdx ]->GetType();
+ }
+ return eBorder;
+}
+
+// frame border state and style
+bool FrameSelector::SupportsDontCareState() const
+{
+ return bool(mxImpl->mnFlags & FrameSelFlags::DontCare);
+}
+
+FrameBorderState FrameSelector::GetFrameBorderState( FrameBorderType eBorder ) const
+{
+ return mxImpl->GetBorder( eBorder ).GetState();
+}
+
+const SvxBorderLine* FrameSelector::GetFrameBorderStyle( FrameBorderType eBorder ) const
+{
+ const SvxBorderLine& rStyle = mxImpl->GetBorder( eBorder ).GetCoreStyle();
+ // rest of the world uses null pointer for invisible frame border
+ return rStyle.GetOutWidth() ? &rStyle : nullptr;
+}
+
+void FrameSelector::ShowBorder( FrameBorderType eBorder, const SvxBorderLine* pStyle )
+{
+ mxImpl->SetBorderCoreStyle( mxImpl->GetBorderAccess( eBorder ), pStyle );
+}
+
+void FrameSelector::SetBorderDontCare( FrameBorderType eBorder )
+{
+ mxImpl->SetBorderState( mxImpl->GetBorderAccess( eBorder ), FrameBorderState::DontCare );
+}
+
+bool FrameSelector::IsAnyBorderVisible() const
+{
+ bool bIsSet = false;
+ for( FrameBorderCIter aIt( mxImpl->maEnabBorders ); !bIsSet && aIt.Is(); ++aIt )
+ bIsSet = ((*aIt)->GetState() == FrameBorderState::Show);
+ return bIsSet;
+}
+
+void FrameSelector::HideAllBorders()
+{
+ for( FrameBorderIter aIt( mxImpl->maEnabBorders ); aIt.Is(); ++aIt )
+ mxImpl->SetBorderState( **aIt, FrameBorderState::Hide );
+}
+
+bool FrameSelector::GetVisibleWidth( tools::Long& rnWidth, SvxBorderLineStyle& rnStyle ) const
+{
+ VisFrameBorderCIter aIt( mxImpl->maEnabBorders );
+ if( !aIt.Is() )
+ return false;
+
+ const SvxBorderLine& rStyle = (*aIt)->GetCoreStyle();
+ bool bFound = true;
+ for( ++aIt; bFound && aIt.Is(); ++aIt )
+ {
+ bFound =
+ (rStyle.GetWidth() == (*aIt)->GetCoreStyle().GetWidth()) &&
+ (rStyle.GetBorderLineStyle() ==
+ (*aIt)->GetCoreStyle().GetBorderLineStyle());
+ }
+
+ if( bFound )
+ {
+ rnWidth = rStyle.GetWidth();
+ rnStyle = rStyle.GetBorderLineStyle();
+ }
+ return bFound;
+}
+
+bool FrameSelector::GetVisibleColor( Color& rColor ) const
+{
+ VisFrameBorderCIter aIt( mxImpl->maEnabBorders );
+ if( !aIt.Is() )
+ return false;
+
+ const SvxBorderLine& rStyle = (*aIt)->GetCoreStyle();
+ bool bFound = true;
+ for( ++aIt; bFound && aIt.Is(); ++aIt )
+ bFound = (rStyle.GetColor() == (*aIt)->GetCoreStyle().GetColor());
+
+ if( bFound )
+ rColor = rStyle.GetColor();
+ return bFound;
+}
+
+// frame border selection
+const Link<LinkParamNone*,void>& FrameSelector::GetSelectHdl() const
+{
+ return mxImpl->maSelectHdl;
+}
+
+void FrameSelector::SetSelectHdl( const Link<LinkParamNone*,void>& rHdl )
+{
+ mxImpl->maSelectHdl = rHdl;
+}
+
+bool FrameSelector::IsBorderSelected( FrameBorderType eBorder ) const
+{
+ return mxImpl->GetBorder( eBorder ).IsSelected();
+}
+
+void FrameSelector::SelectBorder( FrameBorderType eBorder )
+{
+ mxImpl->SelectBorder( mxImpl->GetBorderAccess( eBorder ), true/*bSelect*/ );
+#if !ENABLE_WASM_STRIP_ACCESSIBILITY
+ // MT: bFireFox as API parameter is ugly...
+ // if (bFocus)
+ {
+ rtl::Reference< a11y::AccFrameSelectorChild > xRet = GetChildAccessible(eBorder);
+ if (xRet.is())
+ {
+ Any aOldValue, aNewValue;
+ aNewValue <<= AccessibleStateType::FOCUSED;
+ xRet->NotifyAccessibleEvent( AccessibleEventId::STATE_CHANGED, aOldValue, aNewValue );
+ }
+ }
+#endif
+}
+
+bool FrameSelector::IsAnyBorderSelected() const
+{
+ // Construct an iterator for selected borders. If it is valid, there is a selected border.
+ return SelFrameBorderCIter( mxImpl->maEnabBorders ).Is();
+}
+
+void FrameSelector::SelectAllBorders( bool bSelect )
+{
+ for( FrameBorderIter aIt( mxImpl->maEnabBorders ); aIt.Is(); ++aIt )
+ mxImpl->SelectBorder( **aIt, bSelect );
+}
+
+void FrameSelector::SelectAllVisibleBorders()
+{
+ for( VisFrameBorderIter aIt( mxImpl->maEnabBorders ); aIt.Is(); ++aIt )
+ mxImpl->SelectBorder( **aIt, true/*bSelect*/ );
+}
+
+void FrameSelector::SetStyleToSelection( tools::Long nWidth, SvxBorderLineStyle nStyle )
+{
+ mxImpl->maCurrStyle.SetBorderLineStyle( nStyle );
+ mxImpl->maCurrStyle.SetWidth( nWidth );
+ for( SelFrameBorderIter aIt( mxImpl->maEnabBorders ); aIt.Is(); ++aIt )
+ mxImpl->SetBorderState( **aIt, FrameBorderState::Show );
+}
+
+void FrameSelector::SetColorToSelection( const Color& rColor )
+{
+ mxImpl->maCurrStyle.SetColor( rColor );
+ for( SelFrameBorderIter aIt( mxImpl->maEnabBorders ); aIt.Is(); ++aIt )
+ mxImpl->SetBorderState( **aIt, FrameBorderState::Show );
+}
+
+SvxBorderLineStyle FrameSelector::getCurrentStyleLineStyle() const
+{
+ return mxImpl->maCurrStyle.GetBorderLineStyle();
+}
+
+// accessibility
+Reference< XAccessible > FrameSelector::CreateAccessible()
+{
+#if !ENABLE_WASM_STRIP_ACCESSIBILITY
+ if( !mxAccess.is() )
+ mxAccess = new a11y::AccFrameSelector(*this);
+#endif
+ return mxAccess;
+}
+
+rtl::Reference< a11y::AccFrameSelectorChild > FrameSelector::GetChildAccessible( FrameBorderType eBorder )
+{
+ rtl::Reference< a11y::AccFrameSelectorChild > xRet;
+ size_t nVecIdx = static_cast< size_t >( eBorder );
+ if( IsBorderEnabled( eBorder ) && (1 <= nVecIdx) && (nVecIdx <= mxImpl->maChildVec.size()) )
+ {
+ --nVecIdx;
+ if( !mxImpl->maChildVec[ nVecIdx ].is() )
+ mxImpl->maChildVec[ nVecIdx ] = new a11y::AccFrameSelectorChild( *this, eBorder );
+ xRet = mxImpl->maChildVec[ nVecIdx ].get();
+ }
+ return xRet;
+}
+
+Reference< XAccessible > FrameSelector::GetChildAccessible( sal_Int32 nIndex )
+{
+ return GetChildAccessible( GetEnabledBorderType( nIndex ) );
+}
+
+Reference< XAccessible > FrameSelector::GetChildAccessible( const Point& rPos )
+{
+ Reference< XAccessible > xRet;
+ for( FrameBorderCIter aIt( mxImpl->maEnabBorders ); !xRet.is() && aIt.Is(); ++aIt )
+ if( (*aIt)->ContainsClickPoint( rPos ) )
+ xRet = GetChildAccessible( (*aIt)->GetType() ).get();
+ return xRet;
+}
+
+tools::Rectangle FrameSelector::GetClickBoundRect( FrameBorderType eBorder ) const
+{
+ tools::Rectangle aRect;
+ const FrameBorder& rBorder = mxImpl->GetBorder( eBorder );
+ if( rBorder.IsEnabled() )
+ aRect = rBorder.GetClickBoundRect();
+ return aRect;
+}
+
+// virtual functions from base class
+void FrameSelector::Paint(vcl::RenderContext& rRenderContext, const tools::Rectangle&)
+{
+ mxImpl->CopyVirDevToControl(rRenderContext);
+ if (HasFocus())
+ mxImpl->DrawAllTrackingRects(rRenderContext);
+}
+
+bool FrameSelector::MouseButtonDown( const MouseEvent& rMEvt )
+{
+ /* Mouse handling:
+ * Click on an unselected frame border:
+ Set current style/color, make frame border visible, deselect all
+ other frame borders.
+ * Click on a selected frame border:
+ Toggle state of the frame border (visible -> don't care -> hidden),
+ deselect all other frame borders.
+ * SHIFT+Click or CTRL+Click on an unselected frame border:
+ Extend selection, set current style/color to all selected frame
+ borders independent of the state/style/color of the borders.
+ * SHIFT+Click or CTRL+Click on a selected frame border:
+ If all frame borders have same style/color, toggle state of all
+ borders (see above), otherwise set current style/color to all
+ borders.
+ * Click on unused area: Do not modify selection and selected frame
+ borders.
+ */
+
+ // #107394# do not auto-select a frame border
+ mxImpl->SilentGrabFocus();
+
+ if( rMEvt.IsLeft() )
+ {
+ Point aPos( mxImpl->GetDevPosFromMousePos( rMEvt.GetPosPixel() ) );
+ FrameBorderPtrVec aDeselectBorders;
+
+ bool bAnyClicked = false; // Any frame border clicked?
+ bool bNewSelected = false; // Any unselected frame border selected?
+
+ /* If frame borders are set to "don't care" and the control does not
+ support this state, hide them on first mouse click.
+ DR 2004-01-30: Why are the borders set to "don't care" then?!? */
+ bool bHideDontCare = !SupportsDontCareState();
+
+ for( FrameBorderIter aIt( mxImpl->maEnabBorders ); aIt.Is(); ++aIt )
+ {
+ if( (*aIt)->ContainsClickPoint( aPos ) )
+ {
+ // frame border is clicked
+ bAnyClicked = true;
+ if( !(*aIt)->IsSelected() )
+ {
+ bNewSelected = true;
+ //mxImpl->SelectBorder( **aIt, true );
+ SelectBorder((**aIt).GetType());
+ }
+ }
+ else
+ {
+ // hide a "don't care" frame border only if it is not clicked
+ if( bHideDontCare && ((*aIt)->GetState() == FrameBorderState::DontCare) )
+ mxImpl->SetBorderState( **aIt, FrameBorderState::Hide );
+
+ // deselect frame borders not clicked (if SHIFT or CTRL are not pressed)
+ if( !rMEvt.IsShift() && !rMEvt.IsMod1() )
+ aDeselectBorders.push_back( *aIt );
+ }
+ }
+
+ if( bAnyClicked )
+ {
+ // any valid frame border clicked? -> deselect other frame borders
+ for( FrameBorderIter aIt( aDeselectBorders ); aIt.Is(); ++aIt )
+ mxImpl->SelectBorder( **aIt, false );
+
+ if( bNewSelected || !mxImpl->SelectedBordersEqual() )
+ {
+ // new frame border selected, selection extended, or selected borders different? -> show
+ for( SelFrameBorderIter aIt( mxImpl->maEnabBorders ); aIt.Is(); ++aIt )
+ // SetBorderState() sets current style and color to the frame border
+ mxImpl->SetBorderState( **aIt, FrameBorderState::Show );
+ }
+ else
+ {
+ // all selected frame borders are equal -> toggle state
+ for( SelFrameBorderIter aIt( mxImpl->maEnabBorders ); aIt.Is(); ++aIt )
+ mxImpl->ToggleBorderState( **aIt );
+ }
+
+ GetSelectHdl().Call( nullptr );
+ }
+ }
+
+ return true;
+}
+
+bool FrameSelector::KeyInput( const KeyEvent& rKEvt )
+{
+ bool bHandled = false;
+ vcl::KeyCode aKeyCode = rKEvt.GetKeyCode();
+ if( !aKeyCode.GetModifier() )
+ {
+ sal_uInt16 nCode = aKeyCode.GetCode();
+ switch( nCode )
+ {
+ case KEY_SPACE:
+ {
+ for( SelFrameBorderIter aIt( mxImpl->maEnabBorders ); aIt.Is(); ++aIt )
+ mxImpl->ToggleBorderState( **aIt );
+ bHandled = true;
+ }
+ break;
+
+ case KEY_UP:
+ case KEY_DOWN:
+ case KEY_LEFT:
+ case KEY_RIGHT:
+ {
+ if( !mxImpl->maEnabBorders.empty() )
+ {
+ // start from first selected frame border
+ SelFrameBorderCIter aIt( mxImpl->maEnabBorders );
+ FrameBorderType eBorder = aIt.Is() ? (*aIt)->GetType() : mxImpl->maEnabBorders.front()->GetType();
+
+ // search for next enabled frame border
+ do
+ {
+ eBorder = mxImpl->GetBorder( eBorder ).GetKeyboardNeighbor( nCode );
+ }
+ while( (eBorder != FrameBorderType::NONE) && !IsBorderEnabled( eBorder ) );
+
+ // select the frame border
+ if( eBorder != FrameBorderType::NONE )
+ {
+ DeselectAllBorders();
+ SelectBorder( eBorder );
+ }
+ bHandled = true;
+ }
+ }
+ break;
+ }
+ }
+ if (bHandled)
+ return true;
+ return CustomWidgetController::KeyInput(rKEvt);
+}
+
+void FrameSelector::GetFocus()
+{
+ // auto-selection of a frame border, if focus reaches control, and nothing is selected
+ if( mxImpl->mbAutoSelect && !IsAnyBorderSelected() && !mxImpl->maEnabBorders.empty() )
+ mxImpl->SelectBorder( *mxImpl->maEnabBorders.front(), true );
+
+ mxImpl->DoInvalidate( false );
+ if (IsAnyBorderSelected())
+ {
+ FrameBorderType borderType = FrameBorderType::NONE;
+ if (mxImpl->maLeft.IsSelected())
+ borderType = FrameBorderType::Left;
+ else if (mxImpl->maRight.IsSelected())
+ borderType = FrameBorderType::Right;
+ else if (mxImpl->maTop.IsSelected())
+ borderType = FrameBorderType::Top;
+ else if (mxImpl->maBottom.IsSelected())
+ borderType = FrameBorderType::Bottom;
+ else if (mxImpl->maHor.IsSelected())
+ borderType = FrameBorderType::Horizontal;
+ else if (mxImpl->maVer.IsSelected())
+ borderType = FrameBorderType::Vertical;
+ else if (mxImpl->maTLBR.IsSelected())
+ borderType = FrameBorderType::TLBR;
+ else if (mxImpl->maBLTR.IsSelected())
+ borderType = FrameBorderType::BLTR;
+ SelectBorder(borderType);
+ }
+ for( SelFrameBorderIter aIt( mxImpl->maEnabBorders ); aIt.Is(); ++aIt )
+ mxImpl->SetBorderState( **aIt, FrameBorderState::Show );
+ CustomWidgetController::GetFocus();
+}
+
+void FrameSelector::LoseFocus()
+{
+ mxImpl->DoInvalidate( false );
+ CustomWidgetController::LoseFocus();
+}
+
+void FrameSelector::StyleUpdated()
+{
+ mxImpl->InitVirtualDevice();
+ CustomWidgetController::StyleUpdated();
+}
+
+void FrameSelector::Resize()
+{
+ CustomWidgetController::Resize();
+ mxImpl->sizeChanged();
+}
+
+template< typename Cont, typename Iter, typename Pred >
+FrameBorderIterBase< Cont, Iter, Pred >::FrameBorderIterBase( container_type& rCont ) :
+ maIt( rCont.begin() ),
+ maEnd( rCont.end() )
+{
+ while( Is() && !maPred( *maIt ) ) ++maIt;
+}
+
+template< typename Cont, typename Iter, typename Pred >
+FrameBorderIterBase< Cont, Iter, Pred >& FrameBorderIterBase< Cont, Iter, Pred >::operator++()
+{
+ do { ++maIt; } while( Is() && !maPred( *maIt ) );
+ return *this;
+}
+
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/dialog/graphctl.cxx b/svx/source/dialog/graphctl.cxx
new file mode 100644
index 000000000..136eae27b
--- /dev/null
+++ b/svx/source/dialog/graphctl.cxx
@@ -0,0 +1,850 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <config_wasm_strip.h>
+
+#include <svl/itempool.hxx>
+#include <vcl/settings.hxx>
+#include <vcl/ptrstyle.hxx>
+#include <vcl/svapp.hxx>
+
+#include <svx/graphctl.hxx>
+#include <svx/sdr/overlay/overlaymanager.hxx>
+#include <GraphCtlAccessibleContext.hxx>
+#include <svx/svxids.hrc>
+#include <svx/svdpage.hxx>
+#include <svx/sdrpaintwindow.hxx>
+
+void GraphCtrlUserCall::Changed( const SdrObject& rObj, SdrUserCallType eType, const tools::Rectangle& /*rOldBoundRect*/ )
+{
+ switch( eType )
+ {
+ case SdrUserCallType::MoveOnly:
+ case SdrUserCallType::Resize:
+ rWin.SdrObjChanged( rObj );
+ break;
+
+ case SdrUserCallType::Inserted:
+ rWin.SdrObjCreated( rObj );
+ break;
+
+ default:
+ break;
+ }
+ rWin.QueueIdleUpdate();
+}
+
+GraphCtrl::GraphCtrl(weld::Dialog* pDialog)
+ : aUpdateIdle("svx GraphCtrl Update")
+ , aMap100(MapUnit::Map100thMM)
+ , eObjKind(SdrObjKind::NONE)
+ , nPolyEdit(0)
+ , bEditMode(false)
+ , mbSdrMode(false)
+ , mbInIdleUpdate(false)
+ , mpDialog(pDialog)
+{
+ pUserCall.reset(new GraphCtrlUserCall( *this ));
+ aUpdateIdle.SetPriority( TaskPriority::LOWEST );
+ aUpdateIdle.SetInvokeHandler( LINK( this, GraphCtrl, UpdateHdl ) );
+ aUpdateIdle.Start();
+}
+
+void GraphCtrl::SetDrawingArea(weld::DrawingArea* pDrawingArea)
+{
+ weld::CustomWidgetController::SetDrawingArea(pDrawingArea);
+ EnableRTL(false);
+}
+
+GraphCtrl::~GraphCtrl()
+{
+ aUpdateIdle.Stop();
+
+#if !ENABLE_WASM_STRIP_ACCESSIBILITY
+ if( mpAccContext.is() )
+ {
+ mpAccContext->disposing();
+ mpAccContext.clear();
+ }
+#endif
+
+ pView.reset();
+ pModel.reset();
+ pUserCall.reset();
+}
+
+void GraphCtrl::SetSdrMode(bool bSdrMode)
+{
+ mbSdrMode = bSdrMode;
+
+ const StyleSettings& rStyleSettings = Application::GetSettings().GetStyleSettings();
+ OutputDevice& rDevice = GetDrawingArea()->get_ref_device();
+ rDevice.SetBackground( Wallpaper( rStyleSettings.GetWindowColor() ) );
+ xVD->SetBackground( Wallpaper( rStyleSettings.GetWindowColor() ) );
+ rDevice.SetMapMode( aMap100 );
+ xVD->SetMapMode( aMap100 );
+
+ pView.reset();
+ pModel.reset();
+
+ if ( mbSdrMode )
+ InitSdrModel();
+
+ QueueIdleUpdate();
+}
+
+void GraphCtrl::InitSdrModel()
+{
+ SolarMutexGuard aGuard;
+
+ rtl::Reference<SdrPage> pPage;
+
+ // destroy old junk
+ pView.reset();
+ pModel.reset();
+
+ // Creating a Model
+ pModel.reset(new SdrModel(nullptr, nullptr, true));
+ pModel->GetItemPool().FreezeIdRanges();
+ pModel->SetScaleUnit( aMap100.GetMapUnit() );
+ pModel->SetScaleFraction( Fraction( 1, 1 ) );
+ pModel->SetDefaultFontHeight( 500 );
+
+ pPage = new SdrPage( *pModel );
+
+ pPage->SetSize( aGraphSize );
+ pPage->SetBorder( 0, 0, 0, 0 );
+ pModel->InsertPage( pPage.get() );
+ pModel->SetChanged( false );
+
+ // Creating a View
+ pView.reset(new GraphCtrlView(*pModel, this));
+ pView->SetWorkArea( tools::Rectangle( Point(), aGraphSize ) );
+ pView->EnableExtendedMouseEventDispatcher( true );
+ pView->ShowSdrPage(pView->GetModel()->GetPage(0));
+ pView->SetFrameDragSingles();
+ pView->SetMarkedPointsSmooth( SdrPathSmoothKind::Symmetric );
+ pView->SetEditMode();
+
+ // #i72889# set needed flags
+ pView->SetPagePaintingAllowed(false);
+ pView->SetBufferedOutputAllowed(true);
+ pView->SetBufferedOverlayAllowed(true);
+
+#if !ENABLE_WASM_STRIP_ACCESSIBILITY
+ // Tell the accessibility object about the changes.
+ if (mpAccContext.is())
+ mpAccContext->setModelAndView (pModel.get(), pView.get());
+#endif
+}
+
+void GraphCtrl::SetGraphic( const Graphic& rGraphic, bool bNewModel )
+{
+ aGraphic = rGraphic;
+ xVD->SetOutputSizePixel(Size(0, 0)); //force redraw
+
+ if ( aGraphic.GetPrefMapMode().GetMapUnit() == MapUnit::MapPixel )
+ aGraphSize = Application::GetDefaultDevice()->PixelToLogic( aGraphic.GetPrefSize(), aMap100 );
+ else
+ aGraphSize = OutputDevice::LogicToLogic( aGraphic.GetPrefSize(), aGraphic.GetPrefMapMode(), aMap100 );
+
+ if ( mbSdrMode && bNewModel )
+ InitSdrModel();
+
+ aGraphSizeLink.Call( this );
+
+ Resize();
+
+ Invalidate();
+ QueueIdleUpdate();
+}
+
+void GraphCtrl::GraphicToVD()
+{
+ OutputDevice& rDevice = GetDrawingArea()->get_ref_device();
+ xVD->SetOutputSizePixel(GetOutputSizePixel());
+ xVD->SetBackground(rDevice.GetBackground());
+ xVD->Erase();
+ const bool bGraphicValid(GraphicType::NONE != aGraphic.GetType());
+ if (bGraphicValid)
+ aGraphic.Draw(*xVD, Point(), aGraphSize);
+}
+
+void GraphCtrl::Resize()
+{
+ weld::CustomWidgetController::Resize();
+
+ if (aGraphSize.Width() && aGraphSize.Height())
+ {
+ MapMode aDisplayMap( aMap100 );
+ Point aNewPos;
+ Size aNewSize;
+ OutputDevice& rDevice = GetDrawingArea()->get_ref_device();
+ const Size aWinSize = rDevice.PixelToLogic( GetOutputSizePixel(), aDisplayMap );
+ const tools::Long nWidth = aWinSize.Width();
+ const tools::Long nHeight = aWinSize.Height();
+ double fGrfWH = static_cast<double>(aGraphSize.Width()) / aGraphSize.Height();
+ double fWinWH = static_cast<double>(nWidth) / nHeight;
+
+ // Adapt Bitmap to Thumb size
+ if ( fGrfWH < fWinWH)
+ {
+ aNewSize.setWidth( static_cast<tools::Long>( static_cast<double>(nHeight) * fGrfWH ) );
+ aNewSize.setHeight( nHeight );
+ }
+ else
+ {
+ aNewSize.setWidth( nWidth );
+ aNewSize.setHeight( static_cast<tools::Long>( static_cast<double>(nWidth) / fGrfWH ) );
+ }
+
+ aNewPos.setX( ( nWidth - aNewSize.Width() ) >> 1 );
+ aNewPos.setY( ( nHeight - aNewSize.Height() ) >> 1 );
+
+ // Implementing MapMode for Engine
+ aDisplayMap.SetScaleX( Fraction( aNewSize.Width(), aGraphSize.Width() ) );
+ aDisplayMap.SetScaleY( Fraction( aNewSize.Height(), aGraphSize.Height() ) );
+
+ aDisplayMap.SetOrigin( OutputDevice::LogicToLogic( aNewPos, aMap100, aDisplayMap ) );
+ rDevice.SetMapMode( aDisplayMap );
+ xVD->SetMapMode( aDisplayMap );
+ }
+
+ Invalidate();
+}
+
+void GraphCtrl::Paint(vcl::RenderContext& rRenderContext, const tools::Rectangle& rRect)
+{
+ // #i72889# used split repaint to be able to paint an own background
+ // even to the buffered view
+ const bool bGraphicValid(GraphicType::NONE != aGraphic.GetType());
+
+ if (GetOutputSizePixel() != xVD->GetOutputSizePixel())
+ GraphicToVD();
+
+ if (mbSdrMode)
+ {
+ SdrPaintWindow* pPaintWindow = pView->BeginCompleteRedraw(&rRenderContext);
+ pPaintWindow->SetOutputToWindow(true);
+
+ if (bGraphicValid)
+ {
+ vcl::RenderContext& rTarget = pPaintWindow->GetTargetOutputDevice();
+
+ OutputDevice& rDevice = GetDrawingArea()->get_ref_device();
+ rTarget.SetBackground(rDevice.GetBackground());
+ rTarget.Erase();
+
+ rTarget.DrawOutDev(Point(), xVD->GetOutputSize(), Point(), xVD->GetOutputSize(), *xVD);
+ }
+
+ const vcl::Region aRepaintRegion(rRect);
+ pView->DoCompleteRedraw(*pPaintWindow, aRepaintRegion);
+ pView->EndCompleteRedraw(*pPaintWindow, true);
+ }
+ else
+ {
+ // #i73381# in non-SdrMode, paint to local directly
+ rRenderContext.DrawOutDev(rRect.TopLeft(), rRect.GetSize(),
+ rRect.TopLeft(), rRect.GetSize(),
+ *xVD);
+ }
+}
+
+void GraphCtrl::SdrObjChanged( const SdrObject& )
+{
+ QueueIdleUpdate();
+}
+
+void GraphCtrl::SdrObjCreated( const SdrObject& )
+{
+ QueueIdleUpdate();
+}
+
+void GraphCtrl::MarkListHasChanged()
+{
+ QueueIdleUpdate();
+}
+
+bool GraphCtrl::KeyInput( const KeyEvent& rKEvt )
+{
+ vcl::KeyCode aCode( rKEvt.GetKeyCode() );
+ bool bProc = false;
+
+ OutputDevice& rDevice = GetDrawingArea()->get_ref_device();
+
+ switch ( aCode.GetCode() )
+ {
+ case KEY_DELETE:
+ case KEY_BACKSPACE:
+ {
+ if ( mbSdrMode )
+ {
+ pView->DeleteMarked();
+ bProc = true;
+ }
+ }
+ break;
+
+ case KEY_ESCAPE:
+ {
+ if ( mbSdrMode )
+ {
+ if ( pView->IsAction() )
+ {
+ pView->BrkAction();
+ bProc = true;
+ }
+ else if ( pView->AreObjectsMarked() )
+ {
+ pView->UnmarkAllObj();
+ bProc = true;
+ }
+ }
+ }
+ break;
+
+ case KEY_F11:
+ case KEY_TAB:
+ {
+ if( mbSdrMode )
+ {
+ if( !aCode.IsMod1() && !aCode.IsMod2() )
+ {
+ bool bForward = !aCode.IsShift();
+ // select next object
+ if ( ! pView->MarkNextObj( bForward ))
+ {
+ // At first or last object. Cycle to the other end
+ // of the list.
+ pView->UnmarkAllObj();
+ pView->MarkNextObj (bForward);
+ }
+ bProc = true;
+ }
+ else if(aCode.IsMod1())
+ {
+ // select next handle
+ const SdrHdlList& rHdlList = pView->GetHdlList();
+ bool bForward(!aCode.IsShift());
+
+ const_cast<SdrHdlList&>(rHdlList).TravelFocusHdl(bForward);
+
+ bProc = true;
+ }
+ }
+ }
+ break;
+
+ case KEY_END:
+ {
+
+ if ( aCode.IsMod1() )
+ {
+ // mark last object
+ pView->UnmarkAllObj();
+ pView->MarkNextObj();
+
+ bProc = true;
+ }
+ }
+ break;
+
+ case KEY_HOME:
+ {
+ if ( aCode.IsMod1() )
+ {
+ pView->UnmarkAllObj();
+ pView->MarkNextObj(true);
+
+ bProc = true;
+ }
+ }
+ break;
+
+ case KEY_UP:
+ case KEY_DOWN:
+ case KEY_LEFT:
+ case KEY_RIGHT:
+ {
+ tools::Long nX = 0;
+ tools::Long nY = 0;
+
+ if (aCode.GetCode() == KEY_UP)
+ {
+ // Scroll up
+ nX = 0;
+ nY =-1;
+ }
+ else if (aCode.GetCode() == KEY_DOWN)
+ {
+ // Scroll down
+ nX = 0;
+ nY = 1;
+ }
+ else if (aCode.GetCode() == KEY_LEFT)
+ {
+ // Scroll left
+ nX =-1;
+ nY = 0;
+ }
+ else if (aCode.GetCode() == KEY_RIGHT)
+ {
+ // Scroll right
+ nX = 1;
+ nY = 0;
+ }
+
+ if (pView->AreObjectsMarked() && !aCode.IsMod1() )
+ {
+ if(aCode.IsMod2())
+ {
+ // move in 1 pixel distance
+ Size aLogicSizeOnePixel = rDevice.PixelToLogic(Size(1,1));
+ nX *= aLogicSizeOnePixel.Width();
+ nY *= aLogicSizeOnePixel.Height();
+ }
+ else
+ {
+ // old, fixed move distance
+ nX *= 100;
+ nY *= 100;
+ }
+
+ // II
+ const SdrHdlList& rHdlList = pView->GetHdlList();
+ SdrHdl* pHdl = rHdlList.GetFocusHdl();
+
+ if(nullptr == pHdl)
+ {
+ // restrict movement to WorkArea
+ const tools::Rectangle& rWorkArea = pView->GetWorkArea();
+
+ if(!rWorkArea.IsEmpty())
+ {
+ tools::Rectangle aMarkRect(pView->GetMarkedObjRect());
+ aMarkRect.Move(nX, nY);
+
+ if(!aMarkRect.Contains(rWorkArea))
+ {
+ if(aMarkRect.Left() < rWorkArea.Left())
+ {
+ nX += rWorkArea.Left() - aMarkRect.Left();
+ }
+
+ if(aMarkRect.Right() > rWorkArea.Right())
+ {
+ nX -= aMarkRect.Right() - rWorkArea.Right();
+ }
+
+ if(aMarkRect.Top() < rWorkArea.Top())
+ {
+ nY += rWorkArea.Top() - aMarkRect.Top();
+ }
+
+ if(aMarkRect.Bottom() > rWorkArea.Bottom())
+ {
+ nY -= aMarkRect.Bottom() - rWorkArea.Bottom();
+ }
+ }
+ }
+
+ // no handle selected
+ if(0 != nX || 0 != nY)
+ {
+ pView->MoveAllMarked(Size(nX, nY));
+ }
+ }
+ else
+ {
+ // move handle with index nHandleIndex
+ if (nX || nY)
+ {
+ // now move the Handle (nX, nY)
+ Point aStartPoint(pHdl->GetPos());
+ Point aEndPoint(pHdl->GetPos() + Point(nX, nY));
+ const SdrDragStat& rDragStat = pView->GetDragStat();
+
+ // start dragging
+ pView->BegDragObj(aStartPoint, nullptr, pHdl, 0);
+
+ if(pView->IsDragObj())
+ {
+ bool bWasNoSnap = rDragStat.IsNoSnap();
+ bool bWasSnapEnabled = pView->IsSnapEnabled();
+
+ // switch snapping off
+ if(!bWasNoSnap)
+ const_cast<SdrDragStat&>(rDragStat).SetNoSnap();
+ if(bWasSnapEnabled)
+ pView->SetSnapEnabled(false);
+
+ pView->MovAction(aEndPoint);
+ pView->EndDragObj();
+
+ // restore snap
+ if(!bWasNoSnap)
+ const_cast<SdrDragStat&>(rDragStat).SetNoSnap(bWasNoSnap);
+ if(bWasSnapEnabled)
+ pView->SetSnapEnabled(bWasSnapEnabled);
+ }
+ }
+ }
+
+ bProc = true;
+ }
+ }
+ break;
+
+ case KEY_SPACE:
+ {
+ const SdrHdlList& rHdlList = pView->GetHdlList();
+ SdrHdl* pHdl = rHdlList.GetFocusHdl();
+
+ if(pHdl)
+ {
+ if(pHdl->GetKind() == SdrHdlKind::Poly)
+ {
+ // rescue ID of point with focus
+ sal_uInt32 nPol(pHdl->GetPolyNum());
+ sal_uInt32 nPnt(pHdl->GetPointNum());
+
+ if(pView->IsPointMarked(*pHdl))
+ {
+ if(rKEvt.GetKeyCode().IsShift())
+ {
+ pView->UnmarkPoint(*pHdl);
+ }
+ }
+ else
+ {
+ if(!rKEvt.GetKeyCode().IsShift())
+ {
+ pView->UnmarkAllPoints();
+ }
+
+ pView->MarkPoint(*pHdl);
+ }
+
+ if(nullptr == rHdlList.GetFocusHdl())
+ {
+ // restore point with focus
+ SdrHdl* pNewOne = nullptr;
+
+ for(size_t a = 0; !pNewOne && a < rHdlList.GetHdlCount(); ++a)
+ {
+ SdrHdl* pAct = rHdlList.GetHdl(a);
+
+ if(pAct
+ && pAct->GetKind() == SdrHdlKind::Poly
+ && pAct->GetPolyNum() == nPol
+ && pAct->GetPointNum() == nPnt)
+ {
+ pNewOne = pAct;
+ }
+ }
+
+ if(pNewOne)
+ {
+ const_cast<SdrHdlList&>(rHdlList).SetFocusHdl(pNewOne);
+ }
+ }
+
+ bProc = true;
+ }
+ }
+ }
+ break;
+
+ default:
+ break;
+ }
+
+ if (bProc)
+ ReleaseMouse();
+
+ QueueIdleUpdate();
+
+ return bProc;
+}
+
+bool GraphCtrl::MouseButtonDown( const MouseEvent& rMEvt )
+{
+ if ( mbSdrMode && ( rMEvt.GetClicks() < 2 ) )
+ {
+ OutputDevice& rDevice = GetDrawingArea()->get_ref_device();
+
+ const Point aLogPt( rDevice.PixelToLogic( rMEvt.GetPosPixel() ) );
+
+ if ( !tools::Rectangle( Point(), aGraphSize ).Contains( aLogPt ) && !pView->IsEditMode() )
+ weld::CustomWidgetController::MouseButtonDown( rMEvt );
+ else
+ {
+ // Get Focus for key inputs
+ GrabFocus();
+
+ if ( nPolyEdit )
+ {
+ SdrViewEvent aVEvt;
+ SdrHitKind eHit = pView->PickAnything( rMEvt, SdrMouseEventKind::BUTTONDOWN, aVEvt );
+
+ if ( nPolyEdit == SID_BEZIER_INSERT && eHit == SdrHitKind::MarkedObject )
+ pView->BegInsObjPoint( aLogPt, rMEvt.IsMod1());
+ else
+ pView->MouseButtonDown( rMEvt, &rDevice );
+ }
+ else
+ pView->MouseButtonDown( rMEvt, &rDevice );
+ }
+
+ SdrObject* pCreateObj = pView->GetCreateObj();
+
+ // We want to realize the insert
+ if ( pCreateObj && !pCreateObj->GetUserCall() )
+ pCreateObj->SetUserCall( pUserCall.get() );
+
+ SetPointer( pView->GetPreferredPointer( aLogPt, &rDevice ) );
+ }
+ else
+ weld::CustomWidgetController::MouseButtonDown( rMEvt );
+
+ QueueIdleUpdate();
+
+ return false;
+}
+
+bool GraphCtrl::MouseMove(const MouseEvent& rMEvt)
+{
+ OutputDevice& rDevice = GetDrawingArea()->get_ref_device();
+ const Point aLogPos( rDevice.PixelToLogic( rMEvt.GetPosPixel() ) );
+
+ if ( mbSdrMode )
+ {
+ pView->MouseMove( rMEvt, &rDevice );
+
+ if( ( SID_BEZIER_INSERT == nPolyEdit ) &&
+ !pView->PickHandle( aLogPos ) &&
+ !pView->IsInsObjPoint() )
+ {
+ SetPointer( PointerStyle::Cross );
+ }
+ else
+ SetPointer( pView->GetPreferredPointer( aLogPos, &rDevice ) );
+ }
+ else
+ weld::CustomWidgetController::MouseButtonUp( rMEvt );
+
+ if ( aMousePosLink.IsSet() )
+ {
+ if ( tools::Rectangle( Point(), aGraphSize ).Contains( aLogPos ) )
+ aMousePos = aLogPos;
+ else
+ aMousePos = Point();
+
+ aMousePosLink.Call( this );
+ }
+
+ QueueIdleUpdate();
+
+ return false;
+}
+
+bool GraphCtrl::MouseButtonUp(const MouseEvent& rMEvt)
+{
+ if ( mbSdrMode )
+ {
+ OutputDevice& rDevice = GetDrawingArea()->get_ref_device();
+
+ if ( pView->IsInsObjPoint() )
+ pView->EndInsObjPoint( SdrCreateCmd::ForceEnd );
+ else
+ pView->MouseButtonUp( rMEvt, &rDevice );
+
+ ReleaseMouse();
+ SetPointer( pView->GetPreferredPointer( rDevice.PixelToLogic( rMEvt.GetPosPixel() ), &rDevice ) );
+ }
+ else
+ weld::CustomWidgetController::MouseButtonUp( rMEvt );
+
+ QueueIdleUpdate();
+
+ return false;
+}
+
+SdrObject* GraphCtrl::GetSelectedSdrObject() const
+{
+ SdrObject* pSdrObj = nullptr;
+
+ if ( mbSdrMode )
+ {
+ const SdrMarkList& rMarkList = pView->GetMarkedObjectList();
+
+ if ( rMarkList.GetMarkCount() == 1 )
+ pSdrObj = rMarkList.GetMark( 0 )->GetMarkedSdrObj();
+ }
+
+ return pSdrObj;
+}
+
+void GraphCtrl::SetEditMode( const bool _bEditMode )
+{
+ if ( mbSdrMode )
+ {
+ bEditMode = _bEditMode;
+ pView->SetEditMode( bEditMode );
+ eObjKind = SdrObjKind::NONE;
+ pView->SetCurrentObj(eObjKind);
+ }
+ else
+ bEditMode = false;
+
+ QueueIdleUpdate();
+}
+
+void GraphCtrl::SetPolyEditMode( const sal_uInt16 _nPolyEdit )
+{
+ if ( mbSdrMode && ( _nPolyEdit != nPolyEdit ) )
+ {
+ nPolyEdit = _nPolyEdit;
+ pView->SetFrameDragSingles( nPolyEdit == 0 );
+ }
+ else
+ nPolyEdit = 0;
+
+ QueueIdleUpdate();
+}
+
+void GraphCtrl::SetObjKind( const SdrObjKind _eObjKind )
+{
+ if ( mbSdrMode )
+ {
+ bEditMode = false;
+ pView->SetEditMode( bEditMode );
+ eObjKind = _eObjKind;
+ pView->SetCurrentObj(eObjKind);
+ }
+ else
+ eObjKind = SdrObjKind::NONE;
+
+ QueueIdleUpdate();
+}
+
+IMPL_LINK_NOARG(GraphCtrl, UpdateHdl, Timer *, void)
+{
+ mbInIdleUpdate = true;
+ aUpdateLink.Call( this );
+ mbInIdleUpdate = false;
+}
+
+void GraphCtrl::QueueIdleUpdate()
+{
+ if (!mbInIdleUpdate)
+ aUpdateIdle.Start();
+}
+
+namespace
+{
+ class WeldOverlayManager final : public sdr::overlay::OverlayManager
+ {
+ weld::CustomWidgetController& m_rGraphCtrl;
+
+ public:
+ WeldOverlayManager(weld::CustomWidgetController& rGraphCtrl, OutputDevice& rDevice)
+ : OverlayManager(rDevice)
+ , m_rGraphCtrl(rGraphCtrl)
+ {
+ }
+
+ // invalidate the given range at local OutputDevice
+ virtual void invalidateRange(const basegfx::B2DRange& rRange) override
+ {
+ tools::Rectangle aInvalidateRectangle(RangeToInvalidateRectangle(rRange));
+ m_rGraphCtrl.Invalidate(aInvalidateRectangle);
+ }
+ };
+}
+
+rtl::Reference<sdr::overlay::OverlayManager> GraphCtrlView::CreateOverlayManager(OutputDevice& rDevice) const
+{
+ assert(&rDevice == &rGraphCtrl.GetDrawingArea()->get_ref_device());
+ if (rDevice.GetOutDevType() == OUTDEV_VIRDEV)
+ {
+ rtl::Reference<sdr::overlay::OverlayManager> xOverlayManager(new WeldOverlayManager(rGraphCtrl, rDevice));
+ InitOverlayManager(xOverlayManager);
+ return xOverlayManager;
+ }
+ return SdrView::CreateOverlayManager(rDevice);
+}
+
+void GraphCtrlView::InvalidateOneWin(OutputDevice& rDevice)
+{
+ assert(&rDevice == &rGraphCtrl.GetDrawingArea()->get_ref_device());
+ if (rDevice.GetOutDevType() == OUTDEV_VIRDEV)
+ {
+ rGraphCtrl.Invalidate();
+ return;
+ }
+ SdrView::InvalidateOneWin(rDevice);
+}
+
+void GraphCtrlView::InvalidateOneWin(OutputDevice& rDevice, const tools::Rectangle& rArea)
+{
+ assert(&rDevice == &rGraphCtrl.GetDrawingArea()->get_ref_device());
+ if (rDevice.GetOutDevType() == OUTDEV_VIRDEV)
+ {
+ rGraphCtrl.Invalidate(rArea);
+ return;
+ }
+ SdrView::InvalidateOneWin(rDevice, rArea);
+}
+
+GraphCtrlView::~GraphCtrlView()
+{
+ // turn SetOutputToWindow back off again before
+ // turning back into our baseclass during dtoring
+ const sal_uInt32 nWindowCount(PaintWindowCount());
+ for (sal_uInt32 nWinNum(0); nWinNum < nWindowCount; nWinNum++)
+ {
+ SdrPaintWindow* pPaintWindow = GetPaintWindow(nWinNum);
+ pPaintWindow->SetOutputToWindow(false);
+ }
+}
+
+Point GraphCtrl::GetPositionInDialog() const
+{
+ int x, y, width, height;
+ if (GetDrawingArea()->get_extents_relative_to(*mpDialog, x, y, width, height))
+ return Point(x, y);
+ return Point();
+}
+
+css::uno::Reference< css::accessibility::XAccessible > GraphCtrl::CreateAccessible()
+{
+#if !ENABLE_WASM_STRIP_ACCESSIBILITY
+ if(mpAccContext == nullptr )
+ {
+ // Disable accessibility if no model/view data available
+ if (pView && pModel)
+ mpAccContext = new SvxGraphCtrlAccessibleContext(*this);
+ }
+#endif
+ return mpAccContext;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/dialog/grfflt.cxx b/svx/source/dialog/grfflt.cxx
new file mode 100644
index 000000000..81b928900
--- /dev/null
+++ b/svx/source/dialog/grfflt.cxx
@@ -0,0 +1,294 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <vcl/BitmapSharpenFilter.hxx>
+#include <vcl/BitmapMedianFilter.hxx>
+#include <vcl/BitmapSobelGreyFilter.hxx>
+#include <vcl/BitmapPopArtFilter.hxx>
+
+#include <sfx2/viewfrm.hxx>
+#include <sfx2/viewsh.hxx>
+#include <sfx2/objsh.hxx>
+#include <sfx2/request.hxx>
+
+#include <osl/diagnose.h>
+#include <svx/grfflt.hxx>
+#include <svx/svxids.hrc>
+#include <svx/svxdlg.hxx>
+
+
+SvxGraphicFilterResult SvxGraphicFilter::ExecuteGrfFilterSlot( SfxRequest const & rReq, GraphicObject& rFilterObject )
+{
+ const Graphic& rGraphic = rFilterObject.GetGraphic();
+ SvxGraphicFilterResult nRet = SvxGraphicFilterResult::UnsupportedGraphicType;
+
+ if( rGraphic.GetType() == GraphicType::Bitmap )
+ {
+ SfxViewFrame* pViewFrame = SfxViewFrame::Current();
+ SfxObjectShell* pShell = pViewFrame ? pViewFrame->GetObjectShell() : nullptr;
+ weld::Window* pFrameWeld = (pViewFrame && pViewFrame->GetViewShell()) ? pViewFrame->GetViewShell()->GetFrameWeld() : nullptr;
+ Graphic aGraphic;
+
+ switch( rReq.GetSlot() )
+ {
+ case SID_GRFFILTER_INVERT:
+ {
+ if( pShell )
+ pShell->SetWaitCursor( true );
+
+ if( rGraphic.IsAnimated() )
+ {
+ Animation aAnimation( rGraphic.GetAnimation() );
+
+ if( aAnimation.Invert() )
+ aGraphic = aAnimation;
+ }
+ else
+ {
+ BitmapEx aBmpEx( rGraphic.GetBitmapEx() );
+
+ if( aBmpEx.Invert() )
+ aGraphic = aBmpEx;
+ }
+
+ if( pShell )
+ pShell->SetWaitCursor( false );
+ }
+ break;
+
+ case SID_GRFFILTER_SMOOTH:
+ {
+ SvxAbstractDialogFactory* pFact = SvxAbstractDialogFactory::Create();
+ ScopedVclPtr<AbstractGraphicFilterDialog> aDlg(pFact->CreateGraphicFilterSmooth(pFrameWeld, rGraphic, 0.7));
+ if( aDlg->Execute() == RET_OK )
+ aGraphic = aDlg->GetFilteredGraphic( rGraphic, 1.0, 1.0 );
+ }
+ break;
+
+ case SID_GRFFILTER_SHARPEN:
+ {
+ if( pShell )
+ pShell->SetWaitCursor( true );
+
+ if( rGraphic.IsAnimated() )
+ {
+ Animation aAnimation( rGraphic.GetAnimation() );
+
+ if (BitmapFilter::Filter(aAnimation, BitmapSharpenFilter()))
+ aGraphic = aAnimation;
+ }
+ else
+ {
+ BitmapEx aBmpEx( rGraphic.GetBitmapEx() );
+
+ if (BitmapFilter::Filter(aBmpEx, BitmapSharpenFilter()))
+ aGraphic = aBmpEx;
+ }
+
+ if( pShell )
+ pShell->SetWaitCursor( false );
+ }
+ break;
+
+ case SID_GRFFILTER_REMOVENOISE:
+ {
+ if( pShell )
+ pShell->SetWaitCursor( true );
+
+ if( rGraphic.IsAnimated() )
+ {
+ Animation aAnimation( rGraphic.GetAnimation() );
+
+ if (BitmapFilter::Filter(aAnimation, BitmapMedianFilter()))
+ aGraphic = aAnimation;
+ }
+ else
+ {
+ BitmapEx aBmpEx( rGraphic.GetBitmapEx() );
+
+ if (BitmapFilter::Filter(aBmpEx, BitmapMedianFilter()))
+ aGraphic = aBmpEx;
+ }
+
+ if( pShell )
+ pShell->SetWaitCursor( false );
+ }
+ break;
+
+ case SID_GRFFILTER_SOBEL:
+ {
+ if( pShell )
+ pShell->SetWaitCursor( true );
+
+ if( rGraphic.IsAnimated() )
+ {
+ Animation aAnimation( rGraphic.GetAnimation() );
+
+ if (BitmapFilter::Filter(aAnimation, BitmapSobelGreyFilter()))
+ aGraphic = aAnimation;
+ }
+ else
+ {
+ BitmapEx aBmpEx( rGraphic.GetBitmapEx() );
+
+ if (BitmapFilter::Filter(aBmpEx, BitmapSobelGreyFilter()))
+ aGraphic = aBmpEx;
+ }
+
+ if( pShell )
+ pShell->SetWaitCursor( false );
+ }
+ break;
+
+ case SID_GRFFILTER_MOSAIC:
+ {
+ SvxAbstractDialogFactory* pFact = SvxAbstractDialogFactory::Create();
+ ScopedVclPtr<AbstractGraphicFilterDialog> aDlg(pFact->CreateGraphicFilterMosaic(pFrameWeld, rGraphic));
+ if( aDlg->Execute() == RET_OK )
+ aGraphic = aDlg->GetFilteredGraphic( rGraphic, 1.0, 1.0 );
+ }
+ break;
+
+ case SID_GRFFILTER_EMBOSS:
+ {
+ SvxAbstractDialogFactory* pFact = SvxAbstractDialogFactory::Create();
+ ScopedVclPtr<AbstractGraphicFilterDialog> aDlg(pFact->CreateGraphicFilterEmboss(pFrameWeld, rGraphic));
+ if( aDlg->Execute() == RET_OK )
+ aGraphic = aDlg->GetFilteredGraphic( rGraphic, 1.0, 1.0 );
+ }
+ break;
+
+ case SID_GRFFILTER_POSTER:
+ {
+ SvxAbstractDialogFactory* pFact = SvxAbstractDialogFactory::Create();
+ ScopedVclPtr<AbstractGraphicFilterDialog> aDlg(pFact->CreateGraphicFilterPoster(pFrameWeld, rGraphic));
+ if( aDlg->Execute() == RET_OK )
+ aGraphic = aDlg->GetFilteredGraphic( rGraphic, 1.0, 1.0 );
+ }
+ break;
+
+ case SID_GRFFILTER_POPART:
+ {
+ if( pShell )
+ pShell->SetWaitCursor( true );
+
+ if( rGraphic.IsAnimated() )
+ {
+ Animation aAnimation( rGraphic.GetAnimation() );
+
+ if (BitmapFilter::Filter(aAnimation, BitmapPopArtFilter()))
+ aGraphic = aAnimation;
+ }
+ else
+ {
+ BitmapEx aBmpEx( rGraphic.GetBitmapEx() );
+
+ if (BitmapFilter::Filter(aBmpEx, BitmapPopArtFilter()))
+ aGraphic = aBmpEx;
+ }
+
+ if( pShell )
+ pShell->SetWaitCursor( false );
+ }
+ break;
+
+ case SID_GRFFILTER_SEPIA:
+ {
+ SvxAbstractDialogFactory* pFact = SvxAbstractDialogFactory::Create();
+ ScopedVclPtr<AbstractGraphicFilterDialog> aDlg(pFact->CreateGraphicFilterSepia(pFrameWeld, rGraphic));
+ if( aDlg->Execute() == RET_OK )
+ aGraphic = aDlg->GetFilteredGraphic( rGraphic, 1.0, 1.0 );
+ }
+ break;
+
+ case SID_GRFFILTER_SOLARIZE:
+ {
+ SvxAbstractDialogFactory* pFact = SvxAbstractDialogFactory::Create();
+ ScopedVclPtr<AbstractGraphicFilterDialog> aDlg(pFact->CreateGraphicFilterSolarize(pFrameWeld, rGraphic));
+ if( aDlg->Execute() == RET_OK )
+ aGraphic = aDlg->GetFilteredGraphic( rGraphic, 1.0, 1.0 );
+ }
+ break;
+
+ case SID_GRFFILTER :
+ {
+ // do nothing; no error
+ nRet = SvxGraphicFilterResult::NONE;
+ break;
+ }
+
+ default:
+ {
+ OSL_FAIL( "SvxGraphicFilter: selected filter slot not yet implemented" );
+ nRet = SvxGraphicFilterResult::UnsupportedSlot;
+ }
+ break;
+ }
+
+ if( aGraphic.GetType() != GraphicType::NONE )
+ {
+ rFilterObject.SetGraphic( aGraphic );
+ nRet = SvxGraphicFilterResult::NONE;
+ }
+ }
+
+ return nRet;
+}
+
+
+void SvxGraphicFilter::DisableGraphicFilterSlots( SfxItemSet& rSet )
+{
+ if( SfxItemState::DEFAULT <= rSet.GetItemState( SID_GRFFILTER ) )
+ rSet.DisableItem( SID_GRFFILTER );
+
+ if( SfxItemState::DEFAULT <= rSet.GetItemState( SID_GRFFILTER_INVERT ) )
+ rSet.DisableItem( SID_GRFFILTER_INVERT );
+
+ if( SfxItemState::DEFAULT <= rSet.GetItemState( SID_GRFFILTER_SMOOTH ) )
+ rSet.DisableItem( SID_GRFFILTER_SMOOTH );
+
+ if( SfxItemState::DEFAULT <= rSet.GetItemState( SID_GRFFILTER_SHARPEN ) )
+ rSet.DisableItem( SID_GRFFILTER_SHARPEN );
+
+ if( SfxItemState::DEFAULT <= rSet.GetItemState( SID_GRFFILTER_REMOVENOISE ) )
+ rSet.DisableItem( SID_GRFFILTER_REMOVENOISE );
+
+ if( SfxItemState::DEFAULT <= rSet.GetItemState( SID_GRFFILTER_SOBEL ) )
+ rSet.DisableItem( SID_GRFFILTER_SOBEL );
+
+ if( SfxItemState::DEFAULT <= rSet.GetItemState( SID_GRFFILTER_MOSAIC ) )
+ rSet.DisableItem( SID_GRFFILTER_MOSAIC );
+
+ if( SfxItemState::DEFAULT <= rSet.GetItemState( SID_GRFFILTER_EMBOSS ) )
+ rSet.DisableItem( SID_GRFFILTER_EMBOSS );
+
+ if( SfxItemState::DEFAULT <= rSet.GetItemState( SID_GRFFILTER_POSTER ) )
+ rSet.DisableItem( SID_GRFFILTER_POSTER );
+
+ if( SfxItemState::DEFAULT <= rSet.GetItemState( SID_GRFFILTER_POPART ) )
+ rSet.DisableItem( SID_GRFFILTER_POPART );
+
+ if( SfxItemState::DEFAULT <= rSet.GetItemState( SID_GRFFILTER_SEPIA ) )
+ rSet.DisableItem( SID_GRFFILTER_SEPIA );
+
+ if( SfxItemState::DEFAULT <= rSet.GetItemState( SID_GRFFILTER_SOLARIZE ) )
+ rSet.DisableItem( SID_GRFFILTER_SOLARIZE );
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/dialog/hdft.cxx b/svx/source/dialog/hdft.cxx
new file mode 100644
index 000000000..9f0985ad1
--- /dev/null
+++ b/svx/source/dialog/hdft.cxx
@@ -0,0 +1,1052 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * 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/unit_conversion.hxx>
+#include <svl/itemiter.hxx>
+#include <sfx2/objsh.hxx>
+#include <svx/svxids.hrc>
+
+#include <svl/intitem.hxx>
+#include <svtools/unitconv.hxx>
+
+#include <svx/hdft.hxx>
+#include <svx/pageitem.hxx>
+
+#include <svx/dlgutil.hxx>
+#include <sfx2/htmlmode.hxx>
+#include <osl/diagnose.h>
+
+#include <editeng/brushitem.hxx>
+#include <editeng/lrspitem.hxx>
+#include <editeng/ulspitem.hxx>
+#include <editeng/sizeitem.hxx>
+#include <editeng/boxitem.hxx>
+
+#include <svx/svxdlg.hxx>
+#include <memory>
+
+#include <svx/xdef.hxx>
+#include <svx/xfillit0.hxx>
+#include <svx/unobrushitemhelper.hxx>
+
+using namespace com::sun::star;
+
+// Word 97 incompatibility (#i19922#)
+// #i19922# - tdf#126051 see cui/source/tabpages/page.cxx and sw/source/uibase/sidebar/PageMarginControl.hxx
+constexpr tools::Long MINBODY = o3tl::toTwips(1, o3tl::Length::mm); // 1mm in twips rounded
+
+// default distance to Header or footer
+const tools::Long DEF_DIST_WRITER = 500; // 5mm (Writer)
+const tools::Long DEF_DIST_CALC = 250; // 2.5mm (Calc)
+
+const WhichRangesContainer SvxHFPage::pRanges(svl::Items<
+ // Support DrawingLayer FillStyles (no real call to below GetRanges()
+ // detected, still do the complete transition)
+ XATTR_FILL_FIRST, XATTR_FILL_LAST,
+
+ SID_ATTR_BRUSH, SID_ATTR_BRUSH,
+ SID_ATTR_BORDER_INNER, SID_ATTR_BORDER_INNER,
+ SID_ATTR_BORDER_OUTER, SID_ATTR_BORDER_OUTER,
+ SID_ATTR_BORDER_SHADOW, SID_ATTR_BORDER_SHADOW,
+ SID_ATTR_LRSPACE, SID_ATTR_LRSPACE,
+ SID_ATTR_ULSPACE, SID_ATTR_ULSPACE,
+ SID_ATTR_PAGE_SIZE, SID_ATTR_PAGE_SIZE,
+ SID_ATTR_PAGE_HEADERSET, SID_ATTR_PAGE_HEADERSET,
+ SID_ATTR_PAGE_FOOTERSET, SID_ATTR_PAGE_FOOTERSET,
+ SID_ATTR_PAGE_ON, SID_ATTR_PAGE_ON,
+ SID_ATTR_PAGE_DYNAMIC, SID_ATTR_PAGE_DYNAMIC,
+ SID_ATTR_PAGE_SHARED, SID_ATTR_PAGE_SHARED,
+ SID_ATTR_HDFT_DYNAMIC_SPACING, SID_ATTR_HDFT_DYNAMIC_SPACING,
+ SID_ATTR_PAGE_SHARED_FIRST, SID_ATTR_PAGE_SHARED_FIRST
+>);
+
+namespace svx {
+
+ bool ShowBorderBackgroundDlg(weld::Window* pParent, SfxItemSet* pBBSet)
+ {
+ bool bRes = false;
+ SvxAbstractDialogFactory* pFact = SvxAbstractDialogFactory::Create();
+ ScopedVclPtr<SfxAbstractTabDialog> pDlg(pFact->CreateSvxBorderBackgroundDlg(pParent, *pBBSet, true /*bEnableDrawingLayerFillStyles*/));
+ if ( pDlg->Execute() == RET_OK && pDlg->GetOutputItemSet() )
+ {
+ SfxItemIter aIter( *pDlg->GetOutputItemSet() );
+
+ for (const SfxPoolItem* pItem = aIter.GetCurItem(); pItem; pItem = aIter.NextItem())
+ {
+ if ( !IsInvalidItem( pItem ) )
+ pBBSet->Put( *pItem );
+ }
+ bRes = true;
+ }
+ return bRes;
+ }
+}
+
+std::unique_ptr<SfxTabPage> SvxHeaderPage::Create( weld::Container* pPage, weld::DialogController* pController, const SfxItemSet* rSet )
+{
+ return std::make_unique<SvxHeaderPage>( pPage, pController, *rSet );
+}
+
+std::unique_ptr<SfxTabPage> SvxFooterPage::Create( weld::Container* pPage, weld::DialogController* pController, const SfxItemSet* rSet )
+{
+ return std::make_unique<SvxFooterPage>( pPage, pController, *rSet );
+}
+
+SvxHeaderPage::SvxHeaderPage(weld::Container* pPage, weld::DialogController* pController, const SfxItemSet& rAttr)
+ : SvxHFPage( pPage, pController, rAttr, SID_ATTR_PAGE_HEADERSET )
+{
+}
+
+SvxFooterPage::SvxFooterPage(weld::Container* pPage, weld::DialogController* pController, const SfxItemSet& rAttr)
+ : SvxHFPage( pPage, pController, rAttr, SID_ATTR_PAGE_FOOTERSET )
+{
+}
+
+SvxHFPage::SvxHFPage(weld::Container* pPage, weld::DialogController* pController, const SfxItemSet& rSet, sal_uInt16 nSetId)
+ : SfxTabPage(pPage, pController, "svx/ui/headfootformatpage.ui", "HFFormatPage", &rSet)
+ , nId(nSetId)
+ , mbDisableQueryBox(false)
+ , mbEnableDrawingLayerFillStyles(false)
+ , m_xCntSharedBox(m_xBuilder->weld_check_button("checkSameLR"))
+ , m_xCntSharedFirstBox(m_xBuilder->weld_check_button("checkSameFP"))
+ , m_xLMLbl(m_xBuilder->weld_label("labelLeftMarg"))
+ , m_xLMEdit(m_xBuilder->weld_metric_spin_button("spinMargLeft", FieldUnit::CM))
+ , m_xRMLbl(m_xBuilder->weld_label("labelRightMarg"))
+ , m_xRMEdit(m_xBuilder->weld_metric_spin_button("spinMargRight", FieldUnit::CM))
+ , m_xDistFT(m_xBuilder->weld_label("labelSpacing"))
+ , m_xDistEdit(m_xBuilder->weld_metric_spin_button("spinSpacing", FieldUnit::CM))
+ , m_xDynSpacingCB(m_xBuilder->weld_check_button("checkDynSpacing"))
+ , m_xHeightFT(m_xBuilder->weld_label("labelHeight"))
+ , m_xHeightEdit(m_xBuilder->weld_metric_spin_button("spinHeight", FieldUnit::CM))
+ , m_xHeightDynBtn(m_xBuilder->weld_check_button("checkAutofit"))
+ , m_xBackgroundBtn(m_xBuilder->weld_button("buttonMore"))
+ , m_xBspWin(new weld::CustomWeld(*m_xBuilder, "drawingareaPageHF", m_aBspWin))
+{
+ //swap header <-> footer in UI
+ if (nId == SID_ATTR_PAGE_FOOTERSET)
+ {
+ m_xContainer->set_help_id("svx/ui/headfootformatpage/FFormatPage");
+ m_xPageLbl = m_xBuilder->weld_label("labelFooterFormat");
+ m_xTurnOnBox = m_xBuilder->weld_check_button("checkFooterOn");
+
+ /* Set custom HIDs for the Footer help page (shared/01/05040400.xhp)
+ otherwise it would display the same extended help
+ on both the Header and Footer tabs */
+ m_xCntSharedBox->set_help_id( "SVX_HID_FOOTER_CHECKSAMELR" );
+ m_xCntSharedFirstBox->set_help_id( "SVX_HID_FOOTER_CHECKSAMEFP" );
+ m_xLMEdit->set_help_id( "SVX_HID_FOOTER_SPINMARGLEFT" );
+ m_xRMEdit->set_help_id( "SVX_HID_FOOTER_SPINMARGRIGHT" );
+ m_xDistEdit->set_help_id( "SVX_HID_FOOTER_SPINSPACING" );
+ m_xDynSpacingCB->set_help_id( "SVX_HID_FOOTER_CHECKDYNSPACING" );
+ m_xHeightEdit->set_help_id( "SVX_HID_FOOTER_SPINHEIGHT" );
+ m_xHeightDynBtn->set_help_id( "SVX_HID_FOOTER_CHECKAUTOFIT" );
+ m_xBackgroundBtn->set_help_id( "SVX_HID_FOOTER_BUTTONMORE" );
+ }
+ else //Header
+ {
+ m_xContainer->set_help_id("svx/ui/headfootformatpage/HFormatPage");
+ m_xPageLbl = m_xBuilder->weld_label("labelHeaderFormat");
+ m_xTurnOnBox = m_xBuilder->weld_check_button("checkHeaderOn");
+ }
+ m_xTurnOnBox->show();
+ m_xPageLbl->show();
+
+ InitHandler();
+ m_aBspWin.EnableRTL(false);
+
+ // This Page needs ExchangeSupport
+ SetExchangeSupport();
+
+ // Set metrics
+ FieldUnit eFUnit = GetModuleFieldUnit( rSet );
+ SetFieldUnit( *m_xDistEdit, eFUnit );
+ SetFieldUnit( *m_xHeightEdit, eFUnit );
+ SetFieldUnit( *m_xLMEdit, eFUnit );
+ SetFieldUnit( *m_xRMEdit, eFUnit );
+}
+
+SvxHFPage::~SvxHFPage()
+{
+}
+
+bool SvxHFPage::FillItemSet( SfxItemSet* rSet )
+{
+ const sal_uInt16 nWSize = GetWhich(SID_ATTR_PAGE_SIZE);
+ const sal_uInt16 nWLRSpace = GetWhich(SID_ATTR_LRSPACE);
+ const sal_uInt16 nWULSpace = GetWhich(SID_ATTR_ULSPACE);
+ const sal_uInt16 nWOn = GetWhich(SID_ATTR_PAGE_ON);
+ const sal_uInt16 nWDynamic = GetWhich(SID_ATTR_PAGE_DYNAMIC);
+ const sal_uInt16 nWDynSpacing = GetWhich(SID_ATTR_HDFT_DYNAMIC_SPACING);
+ const sal_uInt16 nWShared = GetWhich(SID_ATTR_PAGE_SHARED);
+ const sal_uInt16 nWSharedFirst = GetWhich( SID_ATTR_PAGE_SHARED_FIRST );
+ const sal_uInt16 nWBrush = GetWhich(SID_ATTR_BRUSH);
+ const sal_uInt16 nWBox = GetWhich(SID_ATTR_BORDER_OUTER);
+ const sal_uInt16 nWBoxInfo = GetWhich(SID_ATTR_BORDER_INNER);
+ const sal_uInt16 nWShadow = GetWhich(SID_ATTR_BORDER_SHADOW);
+
+ const SfxItemSet& rOldSet = GetItemSet();
+ SfxItemPool* pPool = rOldSet.GetPool();
+ DBG_ASSERT(pPool,"no pool :-(");
+ MapUnit eUnit = pPool->GetMetric(nWSize);
+ // take over DrawingLayer FillStyles
+ SfxItemSetFixed<XATTR_FILL_FIRST, XATTR_FILL_LAST> aSet(*pPool);
+ // Keep it valid
+ aSet.MergeRange(nWSize, nWSize);
+ aSet.MergeRange(nWLRSpace, nWLRSpace);
+ aSet.MergeRange(nWULSpace, nWULSpace);
+ aSet.MergeRange(nWOn, nWOn);
+ aSet.MergeRange(nWDynamic, nWDynamic);
+ aSet.MergeRange(nWShared, nWShared);
+ aSet.MergeRange(nWSharedFirst, nWSharedFirst);
+ aSet.MergeRange(nWBrush, nWBrush);
+ aSet.MergeRange(nWBoxInfo, nWBoxInfo);
+ aSet.MergeRange(nWBox, nWBox);
+ aSet.MergeRange(nWShadow, nWShadow);
+ aSet.MergeRange(nWDynSpacing, nWDynSpacing);
+
+ if(mbEnableDrawingLayerFillStyles)
+ {
+ // When using the XATTR_FILLSTYLE DrawingLayer FillStyle definition
+ // extra action has to be done here since the pool default is drawing::FillStyle_SOLID
+ // instead of drawing::FillStyle_NONE (to have the default blue fill color at start).
+ aSet.Put(XFillStyleItem(drawing::FillStyle_NONE));
+ }
+
+ aSet.Put( SfxBoolItem( nWOn, m_xTurnOnBox->get_active() ) );
+ aSet.Put( SfxBoolItem( nWDynamic, m_xHeightDynBtn->get_active() ) );
+ aSet.Put( SfxBoolItem( nWShared, m_xCntSharedBox->get_active() ) );
+ if(m_xCntSharedFirstBox->get_visible())
+ aSet.Put(SfxBoolItem(nWSharedFirst, m_xCntSharedFirstBox->get_active()));
+ if (m_xDynSpacingCB->get_visible() && SfxItemPool::IsWhich(nWDynSpacing))
+ {
+ std::unique_ptr<SfxBoolItem> pBoolItem(static_cast<SfxBoolItem*>(pPool->GetDefaultItem(nWDynSpacing).Clone()));
+ pBoolItem->SetValue(m_xDynSpacingCB->get_active());
+ aSet.Put(std::move(pBoolItem));
+ }
+
+ // Size
+ SvxSizeItem aSizeItem( static_cast<const SvxSizeItem&>(rOldSet.Get( nWSize )) );
+ Size aSize( aSizeItem.GetSize() );
+ tools::Long nDist = GetCoreValue( *m_xDistEdit, eUnit );
+ tools::Long nH = GetCoreValue( *m_xHeightEdit, eUnit );
+
+ nH += nDist; // add distance
+ aSize.setHeight( nH );
+ aSizeItem.SetSize( aSize );
+ aSet.Put( aSizeItem );
+
+ // Margins
+ SvxLRSpaceItem aLR( nWLRSpace );
+ aLR.SetLeft( static_cast<sal_uInt16>(GetCoreValue( *m_xLMEdit, eUnit )) );
+ aLR.SetRight( static_cast<sal_uInt16>(GetCoreValue( *m_xRMEdit, eUnit )) );
+ aSet.Put( aLR );
+
+ SvxULSpaceItem aUL( nWULSpace );
+ if ( nId == SID_ATTR_PAGE_HEADERSET )
+ aUL.SetLower( static_cast<sal_uInt16>(nDist) );
+ else
+ aUL.SetUpper( static_cast<sal_uInt16>(nDist) );
+ aSet.Put( aUL );
+
+ // Background and border?
+ if (pBBSet)
+ {
+ aSet.Put(*pBBSet);
+ }
+ else
+ {
+ const SfxPoolItem* pItem;
+
+ if(SfxItemState::SET == GetItemSet().GetItemState(GetWhich(nId), false, &pItem))
+ {
+ const SfxItemSet* _pSet = &(static_cast< const SvxSetItem* >(pItem)->GetItemSet());
+
+ if(_pSet->GetItemState(nWBrush) == SfxItemState::SET)
+ {
+ aSet.Put(_pSet->Get(nWBrush));
+ }
+
+ if(_pSet->GetItemState(nWBoxInfo) == SfxItemState::SET)
+ {
+ aSet.Put(_pSet->Get(nWBoxInfo));
+ }
+
+ if(_pSet->GetItemState(nWBox) == SfxItemState::SET)
+ {
+ aSet.Put(_pSet->Get(nWBox));
+ }
+
+ if(_pSet->GetItemState(nWShadow) == SfxItemState::SET)
+ {
+ aSet.Put(_pSet->Get(nWShadow));
+ }
+
+ // take care of [XATTR_XATTR_FILL_FIRST .. XATTR_FILL_LAST]
+ for(sal_uInt16 nFillStyleId(XATTR_FILL_FIRST); nFillStyleId <= XATTR_FILL_LAST; nFillStyleId++)
+ {
+ if(_pSet->GetItemState(nFillStyleId) == SfxItemState::SET)
+ {
+ aSet.Put(_pSet->Get(nFillStyleId));
+ }
+ }
+ }
+ }
+
+ // Flush the SetItem
+ SvxSetItem aSetItem( TypedWhichId<SvxSetItem>(GetWhich( nId )), aSet );
+ rSet->Put( aSetItem );
+
+ return true;
+}
+
+
+void SvxHFPage::Reset( const SfxItemSet* rSet )
+{
+ ActivatePage( *rSet );
+ ResetBackground_Impl( *rSet );
+
+ SfxItemPool* pPool = GetItemSet().GetPool();
+ DBG_ASSERT( pPool, "Where is the pool" );
+ MapUnit eUnit = pPool->GetMetric( GetWhich( SID_ATTR_PAGE_SIZE ) );
+
+ // Evaluate header-/footer- attributes
+ const SvxSetItem* pSetItem = nullptr;
+
+ if ( SfxItemState::SET == rSet->GetItemState( GetWhich(nId), false,
+ reinterpret_cast<const SfxPoolItem**>(&pSetItem) ) )
+ {
+ const SfxItemSet& rHeaderSet = pSetItem->GetItemSet();
+ const SfxBoolItem& rHeaderOn =
+ rHeaderSet.Get(GetWhich(SID_ATTR_PAGE_ON));
+
+ m_xTurnOnBox->set_active(rHeaderOn.GetValue());
+
+ if ( rHeaderOn.GetValue() )
+ {
+ const SfxBoolItem& rDynamic =
+ rHeaderSet.Get( GetWhich( SID_ATTR_PAGE_DYNAMIC ) );
+ const SfxBoolItem& rShared =
+ rHeaderSet.Get( GetWhich( SID_ATTR_PAGE_SHARED ) );
+ const SfxBoolItem* pSharedFirst = nullptr;
+ if (rHeaderSet.HasItem(GetWhich(SID_ATTR_PAGE_SHARED_FIRST)))
+ pSharedFirst = static_cast<const SfxBoolItem*>(&rHeaderSet.Get( GetWhich( SID_ATTR_PAGE_SHARED_FIRST ) ));
+ const SvxSizeItem& rSize =
+ rHeaderSet.Get( GetWhich( SID_ATTR_PAGE_SIZE ) );
+ const SvxULSpaceItem& rUL =
+ static_cast<const SvxULSpaceItem&>(rHeaderSet.Get( GetWhich( SID_ATTR_ULSPACE ) ));
+ const SvxLRSpaceItem& rLR =
+ static_cast<const SvxLRSpaceItem&>(rHeaderSet.Get( GetWhich( SID_ATTR_LRSPACE ) ));
+ if (m_xDynSpacingCB->get_visible())
+ {
+ const SfxBoolItem& rDynSpacing =
+ static_cast<const SfxBoolItem&>(rHeaderSet.Get(GetWhich(SID_ATTR_HDFT_DYNAMIC_SPACING)));
+ m_xDynSpacingCB->set_active(rDynSpacing.GetValue());
+ }
+
+
+ if ( nId == SID_ATTR_PAGE_HEADERSET )
+ { // Header
+ SetMetricValue( *m_xDistEdit, rUL.GetLower(), eUnit );
+ SetMetricValue( *m_xHeightEdit, rSize.GetSize().Height() - rUL.GetLower(), eUnit );
+ }
+ else
+ { // Footer
+ SetMetricValue( *m_xDistEdit, rUL.GetUpper(), eUnit );
+ SetMetricValue( *m_xHeightEdit, rSize.GetSize().Height() - rUL.GetUpper(), eUnit );
+ }
+
+ m_xHeightDynBtn->set_active(rDynamic.GetValue());
+ SetMetricValue( *m_xLMEdit, rLR.GetLeft(), eUnit );
+ SetMetricValue( *m_xRMEdit, rLR.GetRight(), eUnit );
+ m_xCntSharedBox->set_active(rShared.GetValue());
+ if (pSharedFirst)
+ m_xCntSharedFirstBox->set_active(pSharedFirst->GetValue());
+ }
+ else
+ pSetItem = nullptr;
+ }
+ else
+ {
+ bool bIsCalc = false;
+ const SfxPoolItem* pExt1 = GetItem(*rSet, SID_ATTR_PAGE_EXT1);
+ const SfxPoolItem* pExt2 = GetItem(*rSet, SID_ATTR_PAGE_EXT2);
+ if (dynamic_cast<const SfxBoolItem*>(pExt1) && dynamic_cast<const SfxBoolItem*>(pExt2) )
+ bIsCalc = true;
+
+ // defaults for distance and height
+ tools::Long nDefaultDist = bIsCalc ? DEF_DIST_CALC : DEF_DIST_WRITER;
+ SetMetricValue( *m_xDistEdit, nDefaultDist, MapUnit::Map100thMM );
+ SetMetricValue( *m_xHeightEdit, 500, MapUnit::Map100thMM );
+ }
+
+ if ( !pSetItem )
+ {
+ m_xTurnOnBox->set_active(false);
+ m_xHeightDynBtn->set_active(true);
+ m_xCntSharedBox->set_active(true);
+ m_xCntSharedFirstBox->set_active(true);
+ }
+
+ TurnOn(nullptr);
+
+ m_xTurnOnBox->save_state();
+ m_xDistEdit->save_value();
+ m_xHeightEdit->save_value();
+ m_xHeightDynBtn->save_state();
+ m_xLMEdit->save_value();
+ m_xRMEdit->save_value();
+ m_xCntSharedBox->save_state();
+ RangeHdl();
+
+ SfxObjectShell* pShell;
+ const SfxUInt16Item* pItem = rSet->GetItemIfSet(SID_HTML_MODE, false);
+ if(pItem ||
+ ( nullptr != (pShell = SfxObjectShell::Current()) &&
+ nullptr != (pItem = pShell->GetItem(SID_HTML_MODE))))
+ {
+ sal_uInt16 nHtmlMode = pItem->GetValue();
+ if (nHtmlMode & HTMLMODE_ON)
+ {
+ m_xCntSharedBox->hide();
+ m_xBackgroundBtn->hide();
+ }
+ }
+
+}
+
+void SvxHFPage::InitHandler()
+{
+ m_xTurnOnBox->connect_toggled(LINK(this, SvxHFPage, TurnOnHdl));
+ m_xDistEdit->connect_value_changed(LINK(this, SvxHFPage, ValueChangeHdl));
+ m_xHeightEdit->connect_value_changed(LINK(this,SvxHFPage,ValueChangeHdl));
+
+ m_xLMEdit->connect_value_changed(LINK(this, SvxHFPage, ValueChangeHdl));
+ m_xRMEdit->connect_value_changed(LINK(this, SvxHFPage, ValueChangeHdl));
+ m_xBackgroundBtn->connect_clicked(LINK(this,SvxHFPage, BackgroundHdl));
+}
+
+void SvxHFPage::TurnOn(const weld::Toggleable* pBox)
+{
+ if (m_xTurnOnBox->get_active())
+ {
+ m_xDistFT->set_sensitive(true);
+ m_xDistEdit->set_sensitive(true);
+ m_xDynSpacingCB->set_sensitive(true);
+ m_xHeightFT->set_sensitive(true);
+ m_xHeightEdit->set_sensitive(true);
+ m_xHeightDynBtn->set_sensitive(true);
+ m_xLMLbl->set_sensitive(true);
+ m_xLMEdit->set_sensitive(true);
+ m_xRMLbl->set_sensitive(true);
+ m_xRMEdit->set_sensitive(true);
+
+ SvxPageUsage nUsage = m_aBspWin.GetUsage();
+
+ if( nUsage == SvxPageUsage::Right || nUsage == SvxPageUsage::Left )
+ m_xCntSharedBox->set_sensitive(false);
+ else
+ {
+ m_xCntSharedBox->set_sensitive(true);
+ m_xCntSharedFirstBox->set_sensitive(true);
+ }
+ m_xBackgroundBtn->set_sensitive(true);
+ }
+ else
+ {
+ bool bDelete = true;
+
+ if (!mbDisableQueryBox && pBox && m_xTurnOnBox->get_saved_state() == TRISTATE_TRUE)
+ {
+ short nResult;
+ if (nId == SID_ATTR_PAGE_HEADERSET)
+ {
+ DeleteHeaderDialog aDlg(GetFrameWeld());
+ nResult = aDlg.run();
+ }
+ else
+ {
+ DeleteFooterDialog aDlg(GetFrameWeld());
+ nResult = aDlg.run();
+ }
+ bDelete = nResult == RET_YES;
+ }
+
+ if ( bDelete )
+ {
+ m_xDistFT->set_sensitive(false);
+ m_xDistEdit->set_sensitive(false);
+ m_xDynSpacingCB->set_sensitive(false);
+ m_xHeightFT->set_sensitive(false);
+ m_xHeightEdit->set_sensitive(false);
+ m_xHeightDynBtn->set_sensitive(false);
+
+ m_xLMLbl->set_sensitive(false);
+ m_xLMEdit->set_sensitive(false);
+ m_xRMLbl->set_sensitive(false);
+ m_xRMEdit->set_sensitive(false);
+
+ m_xCntSharedBox->set_sensitive(false);
+ m_xBackgroundBtn->set_sensitive(false);
+ m_xCntSharedFirstBox->set_sensitive(false);
+ }
+ else
+ m_xTurnOnBox->set_active(true);
+ }
+ UpdateExample();
+}
+
+IMPL_LINK(SvxHFPage, TurnOnHdl, weld::Toggleable&, rBox, void)
+{
+ TurnOn(&rBox);
+}
+
+IMPL_LINK_NOARG(SvxHFPage, BackgroundHdl, weld::Button&, void)
+{
+ if(!pBBSet)
+ {
+ // Use only the necessary items for border and background
+ const sal_uInt16 nOuter(GetWhich(SID_ATTR_BORDER_OUTER));
+ const sal_uInt16 nInner(GetWhich(SID_ATTR_BORDER_INNER, false));
+ const sal_uInt16 nShadow(GetWhich(SID_ATTR_BORDER_SHADOW));
+
+ if(mbEnableDrawingLayerFillStyles)
+ {
+ pBBSet.reset(new SfxItemSetFixed
+ <XATTR_FILL_FIRST, XATTR_FILL_LAST, // DrawingLayer FillStyle definitions
+ SID_COLOR_TABLE, SID_PATTERN_LIST> // XPropertyLists for Color, Gradient, Hatch and Graphic fills
+ (*GetItemSet().GetPool()));
+ // Keep it valid
+ pBBSet->MergeRange(nOuter, nOuter);
+ pBBSet->MergeRange(nInner, nInner);
+ pBBSet->MergeRange(nShadow, nShadow);
+
+ // copy items for XPropertyList entries from the DrawModel so that
+ // the Area TabPage can access them
+ static const sal_uInt16 nCopyFlags[] = {
+ SID_COLOR_TABLE,
+ SID_GRADIENT_LIST,
+ SID_HATCH_LIST,
+ SID_BITMAP_LIST,
+ SID_PATTERN_LIST,
+ 0
+ };
+
+ for(sal_uInt16 a(0); nCopyFlags[a]; a++)
+ {
+ const SfxPoolItem* pItem = GetItemSet().GetItem(nCopyFlags[a]);
+
+ if(pItem)
+ {
+ pBBSet->Put(*pItem);
+ }
+ else
+ {
+ OSL_ENSURE(false, "XPropertyList missing (!)");
+ }
+ }
+ }
+ else
+ {
+ const sal_uInt16 nBrush(GetWhich(SID_ATTR_BRUSH));
+
+ pBBSet.reset( new SfxItemSetFixed<XATTR_FILL_FIRST, XATTR_FILL_LAST>
+ (*GetItemSet().GetPool()) );
+ // Keep it valid
+ pBBSet->MergeRange(nBrush, nBrush);
+ pBBSet->MergeRange(nOuter, nOuter);
+ pBBSet->MergeRange(nInner, nInner);
+ pBBSet->MergeRange(nShadow, nShadow);
+ }
+
+ const SfxPoolItem* pItem;
+
+ if(SfxItemState::SET == GetItemSet().GetItemState(GetWhich(nId), false, &pItem))
+ {
+ // If a SfxItemSet from the SetItem for SID_ATTR_PAGE_HEADERSET or
+ // SID_ATTR_PAGE_FOOTERSET exists, use its content
+ pBBSet->Put(static_cast<const SvxSetItem*>(pItem)->GetItemSet());
+ }
+ else
+ {
+ if(mbEnableDrawingLayerFillStyles)
+ {
+ // The style for header/footer is not yet created, need to reset
+ // XFillStyleItem to drawing::FillStyle_NONE which is the same as in the style
+ // initialization. This needs to be done since the pool default for
+ // XFillStyleItem is drawing::FillStyle_SOLID
+ pBBSet->Put(XFillStyleItem(drawing::FillStyle_NONE));
+ }
+ }
+
+ if(SfxItemState::SET == GetItemSet().GetItemState(nInner, false, &pItem))
+ {
+ // The set InfoItem is always required
+ pBBSet->Put(*pItem);
+ }
+ }
+
+ SvxAbstractDialogFactory* pFact = SvxAbstractDialogFactory::Create();
+
+ VclPtr<SfxAbstractTabDialog> pDlg(pFact->CreateSvxBorderBackgroundDlg(
+ GetFrameWeld(),
+ *pBBSet,
+ mbEnableDrawingLayerFillStyles));
+
+ pDlg->StartExecuteAsync([pDlg, this](sal_Int32 nResult) {
+ if (nResult == RET_OK && pDlg->GetOutputItemSet())
+ {
+ SfxItemIter aIter(*pDlg->GetOutputItemSet());
+
+ for (const SfxPoolItem* pItem = aIter.GetCurItem(); pItem; pItem = aIter.NextItem())
+ {
+ if(!IsInvalidItem(pItem))
+ {
+ pBBSet->Put(*pItem);
+ }
+ }
+
+ {
+ drawinglayer::attribute::SdrAllFillAttributesHelperPtr aFillAttributes;
+
+ if (mbEnableDrawingLayerFillStyles)
+ {
+ // create FillAttributes directly from DrawingLayer FillStyle entries
+ aFillAttributes =
+ std::make_shared<drawinglayer::attribute::SdrAllFillAttributesHelper>(*pBBSet);
+ }
+ else
+ {
+ const sal_uInt16 nWhich = GetWhich(SID_ATTR_BRUSH);
+
+ if (pBBSet->GetItemState(nWhich) == SfxItemState::SET)
+ {
+ // create FillAttributes from SvxBrushItem
+ const SvxBrushItem& rItem
+ = static_cast<const SvxBrushItem&>(pBBSet->Get(nWhich));
+ SfxItemSetFixed<XATTR_FILL_FIRST, XATTR_FILL_LAST> aTempSet(*pBBSet->GetPool());
+
+ setSvxBrushItemAsFillAttributesToTargetSet(rItem, aTempSet);
+ aFillAttributes =
+ std::make_shared<drawinglayer::attribute::SdrAllFillAttributesHelper>(aTempSet);
+ }
+ }
+
+ if (SID_ATTR_PAGE_HEADERSET == nId)
+ {
+ //m_aBspWin.SetHdColor(rItem.GetColor());
+ m_aBspWin.setHeaderFillAttributes(aFillAttributes);
+ }
+ else
+ {
+ //m_aBspWin.SetFtColor(rItem.GetColor());
+ m_aBspWin.setFooterFillAttributes(aFillAttributes);
+ }
+ }
+ }
+ pDlg->disposeOnce();
+ });
+
+ UpdateExample();
+}
+
+void SvxHFPage::UpdateExample()
+{
+ if ( nId == SID_ATTR_PAGE_HEADERSET )
+ {
+ m_aBspWin.SetHeader( m_xTurnOnBox->get_active() );
+ m_aBspWin.SetHdHeight( GetCoreValue( *m_xHeightEdit, MapUnit::MapTwip ) );
+ m_aBspWin.SetHdDist( GetCoreValue( *m_xDistEdit, MapUnit::MapTwip ) );
+ m_aBspWin.SetHdLeft( GetCoreValue( *m_xLMEdit, MapUnit::MapTwip ) );
+ m_aBspWin.SetHdRight( GetCoreValue( *m_xRMEdit, MapUnit::MapTwip ) );
+ }
+ else
+ {
+ m_aBspWin.SetFooter( m_xTurnOnBox->get_active() );
+ m_aBspWin.SetFtHeight( GetCoreValue( *m_xHeightEdit, MapUnit::MapTwip ) );
+ m_aBspWin.SetFtDist( GetCoreValue( *m_xDistEdit, MapUnit::MapTwip ) );
+ m_aBspWin.SetFtLeft( GetCoreValue( *m_xLMEdit, MapUnit::MapTwip ) );
+ m_aBspWin.SetFtRight( GetCoreValue( *m_xRMEdit, MapUnit::MapTwip ) );
+ }
+ m_aBspWin.Invalidate();
+}
+
+void SvxHFPage::ResetBackground_Impl( const SfxItemSet& rSet )
+{
+ sal_uInt16 nWhich(GetWhich(SID_ATTR_PAGE_HEADERSET));
+
+ if (SfxItemState::SET == rSet.GetItemState(nWhich, false))
+ {
+ const SvxSetItem& rSetItem = static_cast< const SvxSetItem& >(rSet.Get(nWhich, false));
+ const SfxItemSet& rTmpSet = rSetItem.GetItemSet();
+ const SfxBoolItem& rOn = rTmpSet.Get(GetWhich(SID_ATTR_PAGE_ON));
+
+ if(rOn.GetValue())
+ {
+ drawinglayer::attribute::SdrAllFillAttributesHelperPtr aHeaderFillAttributes;
+
+ if(mbEnableDrawingLayerFillStyles)
+ {
+ // create FillAttributes directly from DrawingLayer FillStyle entries
+ aHeaderFillAttributes = std::make_shared<drawinglayer::attribute::SdrAllFillAttributesHelper>(rTmpSet);
+ }
+ else
+ {
+ nWhich = GetWhich(SID_ATTR_BRUSH);
+
+ if(SfxItemState::SET == rTmpSet.GetItemState(nWhich))
+ {
+ // create FillAttributes from SvxBrushItem
+ const SvxBrushItem& rItem = static_cast< const SvxBrushItem& >(rTmpSet.Get(nWhich));
+ SfxItemSetFixed<XATTR_FILL_FIRST, XATTR_FILL_LAST> aTempSet(*rTmpSet.GetPool());
+
+ setSvxBrushItemAsFillAttributesToTargetSet(rItem, aTempSet);
+ aHeaderFillAttributes = std::make_shared<drawinglayer::attribute::SdrAllFillAttributesHelper>(aTempSet);
+ }
+ }
+
+ m_aBspWin.setHeaderFillAttributes(aHeaderFillAttributes);
+ }
+ }
+
+ nWhich = GetWhich(SID_ATTR_PAGE_FOOTERSET);
+
+ if (SfxItemState::SET == rSet.GetItemState(nWhich, false))
+ {
+ const SvxSetItem& rSetItem = static_cast< const SvxSetItem& >(rSet.Get(nWhich, false));
+ const SfxItemSet& rTmpSet = rSetItem.GetItemSet();
+ const SfxBoolItem& rOn = rTmpSet.Get(GetWhich(SID_ATTR_PAGE_ON));
+
+ if(rOn.GetValue())
+ {
+ drawinglayer::attribute::SdrAllFillAttributesHelperPtr aFooterFillAttributes;
+
+ if(mbEnableDrawingLayerFillStyles)
+ {
+ // create FillAttributes directly from DrawingLayer FillStyle entries
+ aFooterFillAttributes = std::make_shared<drawinglayer::attribute::SdrAllFillAttributesHelper>(rTmpSet);
+ }
+ else
+ {
+ nWhich = GetWhich(SID_ATTR_BRUSH);
+
+ if(SfxItemState::SET == rTmpSet.GetItemState(nWhich))
+ {
+ // create FillAttributes from SvxBrushItem
+ const SvxBrushItem& rItem = static_cast< const SvxBrushItem& >(rTmpSet.Get(nWhich));
+ SfxItemSetFixed<XATTR_FILL_FIRST, XATTR_FILL_LAST> aTempSet(*rTmpSet.GetPool());
+
+ setSvxBrushItemAsFillAttributesToTargetSet(rItem, aTempSet);
+ aFooterFillAttributes = std::make_shared<drawinglayer::attribute::SdrAllFillAttributesHelper>(aTempSet);
+ }
+ }
+
+ m_aBspWin.setFooterFillAttributes(aFooterFillAttributes);
+ }
+ }
+
+ drawinglayer::attribute::SdrAllFillAttributesHelperPtr aPageFillAttributes;
+
+ if(mbEnableDrawingLayerFillStyles)
+ {
+ // create FillAttributes directly from DrawingLayer FillStyle entries
+ aPageFillAttributes = std::make_shared<drawinglayer::attribute::SdrAllFillAttributesHelper>(rSet);
+ }
+ else
+ {
+ nWhich = GetWhich(SID_ATTR_BRUSH);
+
+ if(rSet.GetItemState(nWhich) >= SfxItemState::DEFAULT)
+ {
+ // create FillAttributes from SvxBrushItem
+ const SvxBrushItem& rItem = static_cast< const SvxBrushItem& >(rSet.Get(nWhich));
+ SfxItemSetFixed<XATTR_FILL_FIRST, XATTR_FILL_LAST> aTempSet(*rSet.GetPool());
+
+ setSvxBrushItemAsFillAttributesToTargetSet(rItem, aTempSet);
+ aPageFillAttributes = std::make_shared<drawinglayer::attribute::SdrAllFillAttributesHelper>(aTempSet);
+ }
+ }
+
+ m_aBspWin.setPageFillAttributes(aPageFillAttributes);
+}
+
+void SvxHFPage::ActivatePage( const SfxItemSet& rSet )
+{
+ const SfxPoolItem* pItem = GetItem( rSet, SID_ATTR_LRSPACE );
+
+ if ( pItem )
+ {
+ // Set left and right margins
+ const SvxLRSpaceItem& rLRSpace = static_cast<const SvxLRSpaceItem&>(*pItem);
+
+ m_aBspWin.SetLeft( rLRSpace.GetLeft() );
+ m_aBspWin.SetRight( rLRSpace.GetRight() );
+ }
+ else
+ {
+ m_aBspWin.SetLeft( 0 );
+ m_aBspWin.SetRight( 0 );
+ }
+
+ pItem = GetItem( rSet, SID_ATTR_ULSPACE );
+
+ if ( pItem )
+ {
+ // Set top and bottom margins
+ const SvxULSpaceItem& rULSpace = static_cast<const SvxULSpaceItem&>(*pItem);
+
+ m_aBspWin.SetTop( rULSpace.GetUpper() );
+ m_aBspWin.SetBottom( rULSpace.GetLower() );
+ }
+ else
+ {
+ m_aBspWin.SetTop( 0 );
+ m_aBspWin.SetBottom( 0 );
+ }
+
+ SvxPageUsage nUsage = SvxPageUsage::All;
+ pItem = GetItem( rSet, SID_ATTR_PAGE );
+
+ if ( pItem )
+ nUsage = static_cast<const SvxPageItem*>(pItem)->GetPageUsage();
+
+ m_aBspWin.SetUsage( nUsage );
+
+ if ( SvxPageUsage::Right == nUsage || SvxPageUsage::Left == nUsage )
+ m_xCntSharedBox->set_sensitive(false);
+ else
+ {
+ m_xCntSharedBox->set_sensitive(true);
+ m_xCntSharedFirstBox->set_sensitive(true);
+ }
+ pItem = GetItem( rSet, SID_ATTR_PAGE_SIZE );
+
+ if ( pItem )
+ {
+ // Orientation and Size from the PageItem
+ const SvxSizeItem& rSize = static_cast<const SvxSizeItem&>(*pItem);
+ // if the size is already swapped (Landscape)
+ m_aBspWin.SetSize( rSize.GetSize() );
+ }
+
+ // Evaluate Header attribute
+ const SvxSetItem* pSetItem = nullptr;
+
+ if ( SfxItemState::SET == rSet.GetItemState( GetWhich( SID_ATTR_PAGE_HEADERSET ),
+ false,
+ reinterpret_cast<const SfxPoolItem**>(&pSetItem) ) )
+ {
+ const SfxItemSet& rHeaderSet = pSetItem->GetItemSet();
+ const SfxBoolItem& rHeaderOn =
+ rHeaderSet.Get( GetWhich( SID_ATTR_PAGE_ON ) );
+
+ if ( rHeaderOn.GetValue() )
+ {
+ const SvxSizeItem& rSize =
+ rHeaderSet.Get( GetWhich( SID_ATTR_PAGE_SIZE ) );
+ const SvxULSpaceItem& rUL = static_cast<const SvxULSpaceItem&>(
+ rHeaderSet.Get( GetWhich(SID_ATTR_ULSPACE ) ));
+ const SvxLRSpaceItem& rLR = static_cast<const SvxLRSpaceItem&>(
+ rHeaderSet.Get( GetWhich( SID_ATTR_LRSPACE ) ));
+ tools::Long nDist = rUL.GetLower();
+
+ m_aBspWin.SetHdHeight( rSize.GetSize().Height() - nDist );
+ m_aBspWin.SetHdDist( nDist );
+ m_aBspWin.SetHdLeft( rLR.GetLeft() );
+ m_aBspWin.SetHdRight( rLR.GetRight() );
+ m_aBspWin.SetHeader( true );
+ }
+ else
+ pSetItem = nullptr;
+ }
+
+ if ( !pSetItem )
+ {
+ m_aBspWin.SetHeader( false );
+
+ if ( SID_ATTR_PAGE_HEADERSET == nId )
+ {
+ m_xCntSharedBox->set_sensitive(false);
+ m_xCntSharedFirstBox->set_sensitive(false);
+ }
+ }
+ pSetItem = nullptr;
+
+ if ( SfxItemState::SET == rSet.GetItemState( GetWhich( SID_ATTR_PAGE_FOOTERSET ),
+ false,
+ reinterpret_cast<const SfxPoolItem**>(&pSetItem) ) )
+ {
+ const SfxItemSet& rFooterSet = pSetItem->GetItemSet();
+ const SfxBoolItem& rFooterOn =
+ rFooterSet.Get( GetWhich( SID_ATTR_PAGE_ON ) );
+
+ if ( rFooterOn.GetValue() )
+ {
+ const SvxSizeItem& rSize =
+ rFooterSet.Get( GetWhich( SID_ATTR_PAGE_SIZE ) );
+ const SvxULSpaceItem& rUL = static_cast<const SvxULSpaceItem&>(
+ rFooterSet.Get( GetWhich( SID_ATTR_ULSPACE ) ));
+ const SvxLRSpaceItem& rLR = static_cast<const SvxLRSpaceItem&>(
+ rFooterSet.Get( GetWhich( SID_ATTR_LRSPACE ) ));
+ tools::Long nDist = rUL.GetUpper();
+
+ m_aBspWin.SetFtHeight( rSize.GetSize().Height() - nDist );
+ m_aBspWin.SetFtDist( nDist );
+ m_aBspWin.SetFtLeft( rLR.GetLeft() );
+ m_aBspWin.SetFtRight( rLR.GetRight() );
+ m_aBspWin.SetFooter( true );
+ }
+ else
+ pSetItem = nullptr;
+ }
+
+ if ( !pSetItem )
+ {
+ m_aBspWin.SetFooter( false );
+
+ if ( SID_ATTR_PAGE_FOOTERSET == nId )
+ {
+ m_xCntSharedBox->set_sensitive(false);
+ m_xCntSharedFirstBox->set_sensitive(false);
+ }
+ }
+
+ pItem = GetItem( rSet, SID_ATTR_PAGE_EXT1 );
+
+ if ( auto pBoolItem = dynamic_cast<const SfxBoolItem*>( pItem) )
+ {
+ m_aBspWin.SetTable( true );
+ m_aBspWin.SetHorz( pBoolItem->GetValue() );
+ }
+
+ pItem = GetItem( rSet, SID_ATTR_PAGE_EXT2 );
+
+ if ( auto pBoolItem = dynamic_cast<const SfxBoolItem*>( pItem) )
+ {
+ m_aBspWin.SetTable( true );
+ m_aBspWin.SetVert( pBoolItem->GetValue() );
+ }
+ ResetBackground_Impl( rSet );
+ RangeHdl();
+}
+
+DeactivateRC SvxHFPage::DeactivatePage( SfxItemSet* _pSet )
+{
+ if ( _pSet )
+ FillItemSet( _pSet );
+ return DeactivateRC::LeavePage;
+}
+
+IMPL_LINK_NOARG(SvxHFPage, ValueChangeHdl, weld::MetricSpinButton&, void)
+{
+ UpdateExample();
+ RangeHdl();
+}
+
+void SvxHFPage::RangeHdl()
+{
+ tools::Long nHHeight = m_aBspWin.GetHdHeight();
+ tools::Long nHDist = m_aBspWin.GetHdDist();
+
+ tools::Long nFHeight = m_aBspWin.GetFtHeight();
+ tools::Long nFDist = m_aBspWin.GetFtDist();
+
+ tools::Long nHeight = std::max(tools::Long(MINBODY),
+ static_cast<tools::Long>(m_xHeightEdit->denormalize(m_xHeightEdit->get_value(FieldUnit::TWIP))));
+ tools::Long nDist = m_xTurnOnBox->get_active() ?
+ static_cast<tools::Long>(m_xDistEdit->denormalize(m_xDistEdit->get_value(FieldUnit::TWIP))) : 0;
+
+ tools::Long nMin;
+ tools::Long nMax;
+
+ if ( nId == SID_ATTR_PAGE_HEADERSET )
+ {
+ nHHeight = nHeight;
+ nHDist = nDist;
+ }
+ else
+ {
+ nFHeight = nHeight;
+ nFDist = nDist;
+ }
+
+ // Current values of the side edges
+ tools::Long nBT = m_aBspWin.GetTop();
+ tools::Long nBB = m_aBspWin.GetBottom();
+ tools::Long nBL = m_aBspWin.GetLeft();
+ tools::Long nBR = m_aBspWin.GetRight();
+
+ tools::Long nH = m_aBspWin.GetSize().Height();
+ tools::Long nW = m_aBspWin.GetSize().Width();
+
+ // Borders
+ if ( nId == SID_ATTR_PAGE_HEADERSET )
+ {
+ // Header
+ nMin = ( nH - nBB - nBT ) / 5; // 20%
+ nMax = std::max( nH - nMin - nHDist - nFDist - nFHeight - nBB - nBT,
+ nMin );
+ m_xHeightEdit->set_max(m_xHeightEdit->normalize(nMax), FieldUnit::TWIP);
+ nMin = ( nH - nBB - nBT ) / 5; // 20%
+ nDist = std::max( nH - nMin - nHHeight - nFDist - nFHeight - nBB - nBT,
+ tools::Long(0) );
+ m_xDistEdit->set_max(m_xDistEdit->normalize(nDist), FieldUnit::TWIP);
+ }
+ else
+ {
+ // Footer
+ nMin = ( nH - nBT - nBB ) / 5; // 20%
+ nMax = std::max( nH - nMin - nFDist - nHDist - nHHeight - nBT - nBB,
+ nMin );
+ m_xHeightEdit->set_max(m_xHeightEdit->normalize(nMax), FieldUnit::TWIP);
+ nMin = ( nH - nBT - nBB ) / 5; // 20%
+ nDist = std::max( nH - nMin - nFHeight - nHDist - nHHeight - nBT - nBB,
+ tools::Long(0) );
+ m_xDistEdit->set_max(m_xDistEdit->normalize(nDist), FieldUnit::TWIP);
+ }
+
+ // Limit Indentation
+ nMax = nW - nBL - nBR -
+ static_cast<tools::Long>(m_xRMEdit->denormalize(m_xRMEdit->get_value(FieldUnit::TWIP))) - MINBODY;
+ m_xLMEdit->set_max(m_xLMEdit->normalize(nMax), FieldUnit::TWIP);
+
+ nMax = nW - nBL - nBR -
+ static_cast<tools::Long>(m_xLMEdit->denormalize(m_xLMEdit->get_value(FieldUnit::TWIP))) - MINBODY;
+ m_xRMEdit->set_max(m_xLMEdit->normalize(nMax), FieldUnit::TWIP);
+}
+
+void SvxHFPage::EnableDynamicSpacing()
+{
+ m_xDynSpacingCB->show();
+}
+
+void SvxHFPage::PageCreated(const SfxAllItemSet &rSet)
+{
+ const SfxBoolItem* pSupportDrawingLayerFillStyleItem = rSet.GetItem<SfxBoolItem>(SID_DRAWINGLAYER_FILLSTYLES, false);
+
+ if (pSupportDrawingLayerFillStyleItem)
+ {
+ const bool bNew(pSupportDrawingLayerFillStyleItem->GetValue());
+
+ mbEnableDrawingLayerFillStyles = bNew;
+ }
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/dialog/hexcolorcontrol.cxx b/svx/source/dialog/hexcolorcontrol.cxx
new file mode 100644
index 000000000..fbf6b9ea6
--- /dev/null
+++ b/svx/source/dialog/hexcolorcontrol.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 <sax/tools/converter.hxx>
+#include <svx/hexcolorcontrol.hxx>
+#include <rtl/character.hxx>
+#include <vcl/svapp.hxx>
+#include <vcl/weld.hxx>
+
+namespace weld
+{
+HexColorControl::HexColorControl(std::unique_ptr<weld::Entry> pEntry)
+ : m_xEntry(std::move(pEntry))
+ , m_nAsyncModifyEvent(nullptr)
+{
+ m_xEntry->set_max_length(6);
+ m_xEntry->set_width_chars(6);
+ m_xEntry->connect_insert_text(LINK(this, HexColorControl, ImplProcessInputHdl));
+ m_xEntry->connect_changed(LINK(this, HexColorControl, ImplProcessModifyHdl));
+}
+
+HexColorControl::~HexColorControl()
+{
+ if (m_nAsyncModifyEvent)
+ Application::RemoveUserEvent(m_nAsyncModifyEvent);
+}
+
+IMPL_LINK_NOARG(HexColorControl, OnAsyncModifyHdl, void*, void)
+{
+ m_nAsyncModifyEvent = nullptr;
+ m_aModifyHdl.Call(*m_xEntry);
+}
+
+// tdf#123291 resend it async so it arrives after ImplProcessInputHdl has been
+// processed
+IMPL_LINK_NOARG(HexColorControl, ImplProcessModifyHdl, weld::Entry&, void)
+{
+ if (m_nAsyncModifyEvent)
+ Application::RemoveUserEvent(m_nAsyncModifyEvent);
+ m_nAsyncModifyEvent = Application::PostUserEvent(LINK(this, HexColorControl, OnAsyncModifyHdl));
+}
+
+void HexColorControl::SetColor(Color nColor)
+{
+ OUStringBuffer aBuffer;
+ sax::Converter::convertColor(aBuffer, nColor);
+ OUString sColor = aBuffer.makeStringAndClear().copy(1);
+ if (sColor == m_xEntry->get_text())
+ return;
+ int nStartPos, nEndPos;
+ m_xEntry->get_selection_bounds(nStartPos, nEndPos);
+ m_xEntry->set_text(sColor);
+ m_xEntry->select_region(nStartPos, nEndPos);
+}
+
+Color HexColorControl::GetColor() const
+{
+ sal_Int32 nColor = -1;
+
+ OUString aStr = "#" + m_xEntry->get_text();
+ sal_Int32 nLen = aStr.getLength();
+
+ if (nLen < 7)
+ {
+ static const char* const pNullStr = "000000";
+ aStr += OUString::createFromAscii(&pNullStr[nLen - 1]);
+ }
+
+ sax::Converter::convertColor(nColor, aStr);
+
+ m_xEntry->set_message_type(nColor != -1 ? weld::EntryMessageType::Normal
+ : weld::EntryMessageType::Error);
+
+ return Color(ColorTransparency, nColor);
+}
+
+IMPL_STATIC_LINK(HexColorControl, ImplProcessInputHdl, OUString&, rTest, bool)
+{
+ const sal_Unicode* pTest = rTest.getStr();
+ sal_Int32 nLen = rTest.getLength();
+
+ OUStringBuffer aFilter(nLen);
+ for (sal_Int32 i = 0; i < nLen; ++i)
+ {
+ if (rtl::isAsciiHexDigit(*pTest))
+ aFilter.append(*pTest);
+ ++pTest;
+ }
+
+ rTest = aFilter.makeStringAndClear();
+ return true;
+}
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/dialog/hyperdlg.cxx b/svx/source/dialog/hyperdlg.cxx
new file mode 100644
index 000000000..6d94d8add
--- /dev/null
+++ b/svx/source/dialog/hyperdlg.cxx
@@ -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 .
+ */
+
+#include <svx/hyperdlg.hxx>
+#include <svx/svxdlg.hxx>
+#include <sfx2/app.hxx>
+#include <sfx2/sfxsids.hrc>
+
+//# #
+//# Childwindow-Wrapper-Class #
+//# #
+SFX_IMPL_CHILDWINDOW_WITHID(SvxHlinkDlgWrapper, SID_HYPERLINK_DIALOG)
+
+SvxHlinkDlgWrapper::SvxHlinkDlgWrapper( vcl::Window* _pParent, sal_uInt16 nId,
+ SfxBindings* pBindings,
+ SfxChildWinInfo* pInfo ) :
+ SfxChildWindow( _pParent, nId ),
+
+ mpDlg( nullptr )
+
+{
+ SvxAbstractDialogFactory* pFact = SvxAbstractDialogFactory::Create();
+ mpDlg = pFact->CreateSvxHpLinkDlg(this, pBindings, _pParent->GetFrameWeld());
+ SetController( mpDlg->GetController() );
+ SetVisible_Impl(false);
+
+ if ( !pInfo->aSize.IsEmpty() )
+ {
+ weld::Window* pTopWindow = SfxGetpApp()->GetTopWindow();
+ if (pTopWindow)
+ {
+ weld::Dialog* pDialog = GetController()->getDialog();
+
+ Size aParentSize(pTopWindow->get_size());
+ Size aDlgSize(pDialog->get_size());
+
+ if( aParentSize.Width() < pInfo->aPos.X() )
+ pInfo->aPos.setX( aParentSize.Width()-aDlgSize.Width() < tools::Long(0.1*aParentSize.Width()) ?
+ tools::Long(0.1*aParentSize.Width()) : aParentSize.Width()-aDlgSize.Width() );
+ if( aParentSize.Height() < pInfo->aPos. Y() )
+ pInfo->aPos.setY( aParentSize.Height()-aDlgSize.Height() < tools::Long(0.1*aParentSize.Height()) ?
+ tools::Long(0.1*aParentSize.Height()) : aParentSize.Height()-aDlgSize.Height() );
+
+ pDialog->window_move(pInfo->aPos.X(), pInfo->aPos.Y());
+ }
+ }
+ SetHideNotDelete( true );
+}
+
+SfxChildWinInfo SvxHlinkDlgWrapper::GetInfo() const
+{
+ return SfxChildWindow::GetInfo();
+}
+
+bool SvxHlinkDlgWrapper::QueryClose()
+{
+ return !mpDlg || mpDlg->QueryClose();
+}
+
+SvxHlinkDlgWrapper::~SvxHlinkDlgWrapper()
+{
+ mpDlg.disposeAndClear();
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/dialog/imapdlg.cxx b/svx/source/dialog/imapdlg.cxx
new file mode 100644
index 000000000..2eda3129b
--- /dev/null
+++ b/svx/source/dialog/imapdlg.cxx
@@ -0,0 +1,727 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * 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 <tools/urlobj.hxx>
+#include <unotools/ucbstreamhelper.hxx>
+#include <svl/eitem.hxx>
+#include <sfx2/dispatch.hxx>
+#include <sfx2/module.hxx>
+#include <sfx2/filedlghelper.hxx>
+#include <com/sun/star/ui/dialogs/TemplateDescription.hpp>
+#include <svl/urihelper.hxx>
+#include <svtools/ehdl.hxx>
+#include <svtools/inettbc.hxx>
+#include <svtools/sfxecode.hxx>
+#include <sfx2/viewfrm.hxx>
+#include <sfx2/objsh.hxx>
+#include <sfx2/docfile.hxx>
+#include <unotools/localedatawrapper.hxx>
+#include <vcl/weld.hxx>
+#include <svx/imapdlg.hxx>
+#include <svx/dialmgr.hxx>
+#include <svx/strings.hrc>
+#include <svx/svxids.hrc>
+#include "imapwnd.hxx"
+#include "imapimp.hxx"
+#include <svx/svdopath.hxx>
+#include <vcl/svapp.hxx>
+#include <vcl/settings.hxx>
+#include <osl/diagnose.h>
+#include "dlgunit.hxx"
+#include <memory>
+
+constexpr OUStringLiteral SELF_TARGET = u"_self";
+constexpr OUStringLiteral IMAP_CERN_FILTER = u"MAP - CERN";
+constexpr OUStringLiteral IMAP_NCSA_FILTER = u"MAP - NCSA";
+constexpr OUStringLiteral IMAP_BINARY_FILTER = u"SIP - StarView ImageMap";
+constexpr OUStringLiteral IMAP_ALL_TYPE = u"*.*";
+constexpr OUStringLiteral IMAP_BINARY_TYPE = u"*.sip";
+constexpr OUStringLiteral IMAP_CERN_TYPE = u"*.map";
+constexpr OUStringLiteral IMAP_NCSA_TYPE = u"*.map";
+
+SFX_IMPL_MODELESSDIALOGCONTOLLER_WITHID( SvxIMapDlgChildWindow, SID_IMAP );
+
+// ControllerItem
+
+SvxIMapDlgItem::SvxIMapDlgItem( SvxIMapDlg& rIMapDlg, SfxBindings& rBindings ) :
+ SfxControllerItem ( SID_IMAP_EXEC, rBindings ),
+ rIMap ( rIMapDlg )
+{
+}
+
+void SvxIMapDlgItem::StateChangedAtToolBoxControl( sal_uInt16 nSID, SfxItemState /*eState*/,
+ const SfxPoolItem* pItem )
+{
+ if ( ( nSID == SID_IMAP_EXEC ) && pItem )
+ {
+ const SfxBoolItem* pStateItem = dynamic_cast<const SfxBoolItem*>( pItem );
+ assert(pStateItem); //SfxBoolItem expected
+ if (pStateItem)
+ {
+ // Disable Float if possible
+ rIMap.SetExecState( !pStateItem->GetValue() );
+ }
+ }
+}
+
+SvxIMapDlgChildWindow::SvxIMapDlgChildWindow(vcl::Window* _pParent, sal_uInt16 nId,
+ SfxBindings* pBindings,
+ SfxChildWinInfo const * pInfo)
+ : SfxChildWindow( _pParent, nId )
+{
+ SetController(std::make_shared<SvxIMapDlg>(pBindings, this, _pParent->GetFrameWeld()));
+ SvxIMapDlg* pDlg = static_cast<SvxIMapDlg*>(GetController().get());
+ pDlg->Initialize( pInfo );
+}
+
+void SvxIMapDlgChildWindow::UpdateIMapDlg( const Graphic& rGraphic, const ImageMap* pImageMap,
+ const TargetList* pTargetList, void* pEditingObj )
+{
+ SvxIMapDlg* pDlg = GetIMapDlg();
+ if (pDlg)
+ pDlg->UpdateLink(rGraphic, pImageMap, pTargetList, pEditingObj);
+}
+
+SvxIMapDlg::SvxIMapDlg(SfxBindings *_pBindings, SfxChildWindow *pCW, weld::Window* _pParent)
+ : SfxModelessDialogController(_pBindings, pCW, _pParent, "svx/ui/imapdialog.ui", "ImapDialog")
+ , pCheckObj(nullptr)
+ , aIMapItem(*this, *_pBindings)
+ , m_xIMapWnd(new IMapWindow(_pBindings->GetActiveFrame(), m_xDialog.get()))
+ , m_xTbxIMapDlg1(m_xBuilder->weld_toolbar("toolbar"))
+ , m_xFtURL(m_xBuilder->weld_label("urlft"))
+ , m_xURLBox(new SvtURLBox(m_xBuilder->weld_combo_box("url")))
+ , m_xFtText(m_xBuilder->weld_label("textft"))
+ , m_xEdtText(m_xBuilder->weld_entry("text"))
+ , m_xFtTarget(m_xBuilder->weld_label("targetft"))
+ , m_xCbbTarget(m_xBuilder->weld_combo_box("target"))
+ , m_xCancelBtn(m_xBuilder->weld_button("cancel"))
+ , m_xStbStatus1(m_xBuilder->weld_label("statusurl"))
+ , m_xStbStatus2(m_xBuilder->weld_label("statuspos"))
+ , m_xStbStatus3(m_xBuilder->weld_label("statussize"))
+ , m_xIMapWndWeld(new weld::CustomWeld(*m_xBuilder, "container", *m_xIMapWnd))
+
+{
+ m_xTbxIMapDlg1->insert_separator(4, "sep1");
+ m_xTbxIMapDlg1->insert_separator(10, "sep2");
+ m_xTbxIMapDlg1->insert_separator(15, "sep3");
+ m_xTbxIMapDlg1->insert_separator(18, "sel4");
+
+ //lock this down so it doesn't jump around in size
+ //as entries are added later on
+ TargetList aTmpList;
+ SfxFrame::GetDefaultTargetList(aTmpList);
+ for (const OUString & s : aTmpList)
+ m_xCbbTarget->append_text(s);
+ Size aPrefSize(m_xCbbTarget->get_preferred_size());
+ m_xCbbTarget->set_size_request(aPrefSize.Width(), -1);
+ m_xCbbTarget->clear();
+
+ m_xIMapWnd->Show();
+
+ pOwnData.reset(new IMapOwnData);
+
+ m_xIMapWnd->SetInfoLink( LINK( this, SvxIMapDlg, InfoHdl ) );
+ m_xIMapWnd->SetMousePosLink( LINK( this, SvxIMapDlg, MousePosHdl ) );
+ m_xIMapWnd->SetGraphSizeLink( LINK( this, SvxIMapDlg, GraphSizeHdl ) );
+ m_xIMapWnd->SetUpdateLink( LINK( this, SvxIMapDlg, StateHdl ) );
+
+ m_xURLBox->connect_changed( LINK( this, SvxIMapDlg, URLModifyHdl ) );
+ m_xURLBox->connect_focus_out( LINK( this, SvxIMapDlg, URLLoseFocusHdl ) );
+ m_xEdtText->connect_changed( LINK( this, SvxIMapDlg, EntryModifyHdl ) );
+ m_xCbbTarget->connect_focus_out( LINK( this, SvxIMapDlg, URLLoseFocusHdl ) );
+
+ m_xTbxIMapDlg1->connect_clicked( LINK( this, SvxIMapDlg, TbxClickHdl ) );
+ OString sSelect("TBI_SELECT");
+ m_xTbxIMapDlg1->set_item_active(sSelect, true);
+ TbxClickHdl(sSelect);
+
+ m_xStbStatus1->set_size_request(120, -1);
+ const int nWidth = m_xStbStatus1->get_pixel_size(" 9999,99 cm / 9999,99 cm ").Width();
+ m_xStbStatus2->set_size_request(nWidth, -1);
+ m_xStbStatus3->set_size_request(nWidth, -1);
+
+ m_xFtURL->set_sensitive(false);
+ m_xURLBox->set_sensitive(false);
+ m_xFtText->set_sensitive(false);
+ m_xEdtText->set_sensitive(false);
+ m_xFtTarget->set_sensitive(false);
+ m_xCbbTarget->set_sensitive(false);
+ pOwnData->bExecState = false;
+
+ pOwnData->aIdle.SetInvokeHandler( LINK( this, SvxIMapDlg, UpdateHdl ) );
+
+ m_xTbxIMapDlg1->set_item_sensitive("TBI_ACTIVE", false);
+ m_xTbxIMapDlg1->set_item_sensitive("TBI_MACRO", false );
+ m_xTbxIMapDlg1->set_item_sensitive("TBI_PROPERTY", false );
+
+ m_xCancelBtn->connect_clicked(LINK(this, SvxIMapDlg, CancelHdl));
+}
+
+SvxIMapDlg::~SvxIMapDlg()
+{
+ m_xIMapWnd->SetUpdateLink( Link<GraphCtrl*,void>() );
+ m_xIMapWnd.reset();
+}
+
+IMPL_LINK_NOARG(SvxIMapDlg, CancelHdl, weld::Button&, void)
+{
+ bool bRet = true;
+
+ if ( m_xTbxIMapDlg1->get_item_sensitive("TBI_APPLY") )
+ {
+ std::unique_ptr<weld::Builder> xBuilder(Application::CreateBuilder(m_xDialog.get(), "svx/ui/querymodifyimagemapchangesdialog.ui"));
+ std::unique_ptr<weld::MessageDialog> xQBox(xBuilder->weld_message_dialog("QueryModifyImageMapChangesDialog"));
+ const tools::Long nRet = xQBox->run();
+
+ if( nRet == RET_YES )
+ {
+ SfxBoolItem aBoolItem( SID_IMAP_EXEC, true );
+ GetBindings().GetDispatcher()->ExecuteList(SID_IMAP_EXEC,
+ SfxCallMode::SYNCHRON | SfxCallMode::RECORD,
+ { &aBoolItem });
+ }
+ else if( nRet == RET_CANCEL )
+ bRet = false;
+ }
+ else if( m_xIMapWnd->IsChanged() )
+ {
+ std::unique_ptr<weld::Builder> xBuilder(Application::CreateBuilder(m_xDialog.get(), "svx/ui/querysaveimagemapchangesdialog.ui"));
+ std::unique_ptr<weld::MessageDialog> xQBox(xBuilder->weld_message_dialog("QuerySaveImageMapChangesDialog"));
+ const tools::Long nRet = xQBox->run();
+
+ if( nRet == RET_YES )
+ bRet = DoSave();
+ else if( nRet == RET_CANCEL )
+ bRet = false;
+ }
+
+ if (bRet)
+ m_xDialog->response(RET_CANCEL);
+}
+
+// Enabled or disable all Controls
+
+void SvxIMapDlg::SetExecState( bool bEnable )
+{
+ pOwnData->bExecState = bEnable;
+}
+
+const ImageMap& SvxIMapDlg::GetImageMap() const
+{
+ return m_xIMapWnd->GetImageMap();
+}
+
+void SvxIMapDlg::SetTargetList( const TargetList& rTargetList )
+{
+ m_xIMapWnd->SetTargetList( rTargetList );
+
+ m_xCbbTarget->clear();
+
+ for (const OUString & s : rTargetList)
+ m_xCbbTarget->append_text(s);
+}
+
+void SvxIMapDlg::UpdateLink( const Graphic& rGraphic, const ImageMap* pImageMap,
+ const TargetList* pTargetList, void* pEditingObj )
+{
+ pOwnData->aUpdateGraphic = rGraphic;
+
+ if ( pImageMap )
+ pOwnData->aUpdateImageMap = *pImageMap;
+ else
+ pOwnData->aUpdateImageMap.ClearImageMap();
+
+ pOwnData->pUpdateEditingObject = pEditingObj;
+
+ // Delete UpdateTargetList, because this method can still be called several
+ // times before the update timer is turned on
+
+ // TargetList must be copied, since it is owned by the caller and can be
+ // deleted immediately after this call the copied list will be deleted
+ // again in the handler
+ if( pTargetList )
+ pOwnData->aUpdateTargetList = *pTargetList;
+ else
+ pOwnData->aUpdateTargetList.clear();
+
+ pOwnData->aIdle.Start();
+}
+
+
+// Click-handler for ToolBox
+
+IMPL_LINK(SvxIMapDlg, TbxClickHdl, const OString&, rNewItemId, void)
+{
+ if (rNewItemId == "TBI_APPLY")
+ {
+ URLLoseFocusHdl(*m_xCbbTarget);
+ SfxBoolItem aBoolItem( SID_IMAP_EXEC, true );
+ GetBindings().GetDispatcher()->ExecuteList(SID_IMAP_EXEC,
+ SfxCallMode::ASYNCHRON | SfxCallMode::RECORD,
+ { &aBoolItem });
+ }
+ else if (rNewItemId == "TBI_OPEN")
+ DoOpen();
+ else if (rNewItemId == "TBI_SAVEAS")
+ DoSave();
+ else if (rNewItemId == "TBI_CLOSE")
+ CancelHdl(*m_xCancelBtn);
+ else if (rNewItemId == "TBI_SELECT")
+ {
+ SetActiveTool( rNewItemId );
+ m_xIMapWnd->SetEditMode( true );
+ }
+ else if (rNewItemId == "TBI_RECT")
+ {
+ SetActiveTool( rNewItemId );
+ m_xIMapWnd->SetObjKind( SdrObjKind::Rectangle );
+ }
+ else if (rNewItemId == "TBI_CIRCLE")
+ {
+ SetActiveTool( rNewItemId );
+ m_xIMapWnd->SetObjKind( SdrObjKind::CircleOrEllipse );
+ }
+ else if (rNewItemId == "TBI_POLY")
+ {
+ SetActiveTool( rNewItemId );
+ m_xIMapWnd->SetObjKind( SdrObjKind::Polygon );
+ }
+ else if (rNewItemId == "TBI_FREEPOLY")
+ {
+ SetActiveTool( rNewItemId );
+ m_xIMapWnd->SetObjKind( SdrObjKind::FreehandFill );
+ }
+ else if (rNewItemId == "TBI_ACTIVE")
+ {
+ URLLoseFocusHdl(*m_xCbbTarget);
+ bool bNewState = !m_xTbxIMapDlg1->get_item_active(rNewItemId);
+ m_xTbxIMapDlg1->set_item_active(rNewItemId, bNewState);
+ m_xIMapWnd->SetCurrentObjState( !bNewState );
+ }
+ else if (rNewItemId == "TBI_MACRO")
+ m_xIMapWnd->DoMacroAssign();
+ else if (rNewItemId == "TBI_PROPERTY")
+ m_xIMapWnd->DoPropertyDialog();
+ else if (rNewItemId == "TBI_POLYEDIT")
+ {
+ SetActiveTool( rNewItemId );
+ m_xIMapWnd->SetPolyEditMode( m_xTbxIMapDlg1->get_item_active(rNewItemId) ? SID_BEZIER_MOVE : 0 );
+ }
+ else if (rNewItemId == "TBI_POLYMOVE")
+ {
+ SetActiveTool( rNewItemId );
+ m_xIMapWnd->SetPolyEditMode( SID_BEZIER_MOVE );
+ }
+ else if (rNewItemId == "TBI_POLYINSERT")
+ {
+ SetActiveTool( rNewItemId );
+ m_xIMapWnd->SetPolyEditMode( SID_BEZIER_INSERT );
+ }
+ else if (rNewItemId == "TBI_POLYDELETE")
+ {
+ SetActiveTool( rNewItemId );
+ m_xIMapWnd->GetSdrView()->DeleteMarkedPoints();
+ }
+ else if (rNewItemId == "TBI_UNDO")
+ {
+ URLLoseFocusHdl(*m_xCbbTarget);
+ m_xIMapWnd->GetSdrModel()->Undo();
+ }
+ else if (rNewItemId == "TBI_REDO")
+ {
+ URLLoseFocusHdl(*m_xCbbTarget);
+ m_xIMapWnd->GetSdrModel()->Redo();
+ }
+}
+
+void SvxIMapDlg::DoOpen()
+{
+ ::sfx2::FileDialogHelper aDlg(
+ css::ui::dialogs::TemplateDescription::FILEOPEN_SIMPLE,
+ FileDialogFlags::NONE, m_xDialog.get());
+
+ ImageMap aLoadIMap;
+ const OUString aFilter(SvxResId(RID_SVXSTR_IMAP_ALL_FILTER));
+
+ aDlg.AddFilter( aFilter, IMAP_ALL_TYPE );
+ aDlg.AddFilter( IMAP_CERN_FILTER, IMAP_CERN_TYPE );
+ aDlg.AddFilter( IMAP_NCSA_FILTER, IMAP_NCSA_TYPE );
+ aDlg.AddFilter( IMAP_BINARY_FILTER, IMAP_BINARY_TYPE );
+
+ aDlg.SetCurrentFilter( aFilter );
+ aDlg.SetContext(sfx2::FileDialogHelper::ImageMap);
+
+ if( aDlg.Execute() != ERRCODE_NONE )
+ return;
+
+ INetURLObject aURL( aDlg.GetPath() );
+ DBG_ASSERT( aURL.GetProtocol() != INetProtocol::NotValid, "invalid URL" );
+ std::unique_ptr<SvStream> pIStm(::utl::UcbStreamHelper::CreateStream( aURL.GetMainURL( INetURLObject::DecodeMechanism::NONE ), StreamMode::READ ));
+
+ if( pIStm )
+ {
+ aLoadIMap.Read( *pIStm, IMapFormat::Detect );
+
+ if( pIStm->GetError() )
+ {
+ SfxErrorContext eEC(ERRCTX_ERROR, m_xDialog.get());
+ ErrorHandler::HandleError( ERRCODE_IO_GENERAL );
+ }
+ else
+ m_xIMapWnd->SetImageMap( aLoadIMap );
+ }
+
+ m_xIMapWnd->Invalidate();
+}
+
+bool SvxIMapDlg::DoSave()
+{
+ ::sfx2::FileDialogHelper aDlg(
+ css::ui::dialogs::TemplateDescription::FILESAVE_SIMPLE,
+ FileDialogFlags::NONE, m_xDialog.get());
+
+ const OUString aBinFilter( IMAP_BINARY_FILTER );
+ const OUString aCERNFilter( IMAP_CERN_FILTER );
+ const OUString aNCSAFilter( IMAP_NCSA_FILTER );
+ SdrModel* pModel = m_xIMapWnd->GetSdrModel();
+ const bool bChanged = pModel->IsChanged();
+ bool bRet = false;
+
+ aDlg.AddFilter( aCERNFilter, IMAP_CERN_TYPE );
+ aDlg.AddFilter( aNCSAFilter, IMAP_NCSA_TYPE );
+ aDlg.AddFilter( aBinFilter, IMAP_BINARY_TYPE );
+
+ aDlg.SetCurrentFilter( aCERNFilter );
+ aDlg.SetContext(sfx2::FileDialogHelper::ImageMap);
+
+ if( aDlg.Execute() == ERRCODE_NONE )
+ {
+ const OUString aFilter( aDlg.GetCurrentFilter() );
+ OUString aExt;
+ IMapFormat nFormat;
+
+ if ( aFilter == aBinFilter )
+ {
+ nFormat = IMapFormat::Binary;
+ aExt = "sip";
+ }
+ else if ( aFilter == aCERNFilter )
+ {
+ nFormat = IMapFormat::CERN;
+ aExt = "map";
+ }
+ else if ( aFilter == aNCSAFilter )
+ {
+ nFormat = IMapFormat::NCSA;
+ aExt = "map";
+ }
+ else
+ {
+ return false;
+ }
+
+ INetURLObject aURL( aDlg.GetPath() );
+
+ if( aURL.GetProtocol() == INetProtocol::NotValid )
+ {
+ OSL_FAIL( "invalid URL" );
+ }
+ else
+ {
+ if( aURL.getExtension().isEmpty() )
+ aURL.setExtension( aExt );
+
+ std::unique_ptr<SvStream> pOStm(::utl::UcbStreamHelper::CreateStream( aURL.GetMainURL( INetURLObject::DecodeMechanism::NONE ), StreamMode::WRITE | StreamMode::TRUNC ));
+ if( pOStm )
+ {
+ m_xIMapWnd->GetImageMap().Write( *pOStm, nFormat );
+
+ if( pOStm->GetError() )
+ ErrorHandler::HandleError( ERRCODE_IO_GENERAL );
+
+ pOStm.reset();
+ pModel->SetChanged( bChanged );
+ bRet = true;
+ }
+ }
+ }
+
+ return bRet;
+}
+
+void SvxIMapDlg::SetActiveTool(std::string_view rId)
+{
+ m_xTbxIMapDlg1->set_item_active("TBI_SELECT", rId == "TBI_SELECT");
+ m_xTbxIMapDlg1->set_item_active("TBI_RECT", rId == "TBI_RECT");
+ m_xTbxIMapDlg1->set_item_active("TBI_CIRCLE", rId == "TBI_CIRCLE");
+ m_xTbxIMapDlg1->set_item_active("TBI_POLY", rId == "TBI_POLY");
+ m_xTbxIMapDlg1->set_item_active("TBI_FREEPOLY", rId == "TBI_FREEPOLY");
+
+ m_xTbxIMapDlg1->set_item_active("TBI_POLYINSERT", rId == "TBI_POLYINSERT");
+ m_xTbxIMapDlg1->set_item_active("TBI_POLYDELETE", false);
+
+ bool bMove = rId == "TBI_POLYMOVE"
+ || ( rId == "TBI_POLYEDIT"
+ && !m_xTbxIMapDlg1->get_item_active("TBI_POLYINSERT")
+ && !m_xTbxIMapDlg1->get_item_active("TBI_POLYDELETE") );
+
+ m_xTbxIMapDlg1->set_item_active("TBI_POLYMOVE", bMove );
+
+ bool bEditMode = ( rId == "TBI_POLYEDIT" )
+ || ( rId == "TBI_POLYMOVE")
+ || ( rId == "TBI_POLYINSERT")
+ || ( rId == "TBI_POLYDELETE" );
+
+ m_xTbxIMapDlg1->set_item_active("TBI_POLYEDIT", bEditMode);
+}
+
+IMPL_LINK( SvxIMapDlg, InfoHdl, IMapWindow&, rWnd, void )
+{
+ const NotifyInfo& rInfo = rWnd.GetInfo();
+
+ if ( rInfo.bNewObj )
+ {
+ if (!rInfo.aMarkURL.isEmpty() && ( m_xURLBox->find_text(rInfo.aMarkURL) == -1))
+ m_xURLBox->append_text(rInfo.aMarkURL);
+
+ m_xURLBox->set_entry_text(rInfo.aMarkURL);
+ m_xEdtText->set_text(rInfo.aMarkAltText);
+
+ if ( rInfo.aMarkTarget.isEmpty() )
+ m_xCbbTarget->set_entry_text( SELF_TARGET );
+ else
+ m_xCbbTarget->set_entry_text( rInfo.aMarkTarget );
+ }
+
+ if ( !rInfo.bOneMarked )
+ {
+ m_xTbxIMapDlg1->set_item_active("TBI_ACTIVE", false);
+ m_xTbxIMapDlg1->set_item_sensitive("TBI_ACTIVE", false);
+ m_xTbxIMapDlg1->set_item_sensitive("TBI_MACRO", false);
+ m_xTbxIMapDlg1->set_item_sensitive("TBI_PROPERTY", false);
+ m_xStbStatus1->set_label(OUString());
+
+ m_xFtURL->set_sensitive(false);
+ m_xURLBox->set_sensitive(false);
+ m_xFtText->set_sensitive(false);
+ m_xEdtText->set_sensitive(false);
+ m_xFtTarget->set_sensitive(false);
+ m_xCbbTarget->set_sensitive(false);
+
+ m_xURLBox->set_entry_text( "" );
+ m_xEdtText->set_text( "" );
+ }
+ else
+ {
+ m_xTbxIMapDlg1->set_item_sensitive("TBI_ACTIVE", true);
+ m_xTbxIMapDlg1->set_item_active("TBI_ACTIVE", !rInfo.bActivated );
+ m_xTbxIMapDlg1->set_item_sensitive("TBI_MACRO", true);
+ m_xTbxIMapDlg1->set_item_sensitive("TBI_PROPERTY", true);
+
+ m_xFtURL->set_sensitive(true);
+ m_xURLBox->set_sensitive(true);
+ m_xFtText->set_sensitive(true);
+ m_xEdtText->set_sensitive(true);
+ m_xFtTarget->set_sensitive(true);
+ m_xCbbTarget->set_sensitive(true);
+
+ m_xStbStatus1->set_label(rInfo.aMarkURL);
+
+ if ( m_xURLBox->get_active_text() != rInfo.aMarkURL )
+ m_xURLBox->set_entry_text( rInfo.aMarkURL );
+
+ if ( m_xEdtText->get_text() != rInfo.aMarkAltText )
+ m_xEdtText->set_text( rInfo.aMarkAltText );
+
+ if ( rInfo.aMarkTarget.isEmpty() )
+ m_xCbbTarget->set_entry_text( SELF_TARGET );
+ else
+ m_xCbbTarget->set_entry_text( rInfo.aMarkTarget );
+ }
+}
+
+IMPL_LINK( SvxIMapDlg, MousePosHdl, GraphCtrl*, pWnd, void )
+{
+ const FieldUnit eFieldUnit = GetBindings().GetDispatcher()->GetModule()->GetFieldUnit();
+ const Point& rMousePos = pWnd->GetMousePos();
+ const LocaleDataWrapper& rLocaleWrapper( Application::GetSettings().GetLocaleDataWrapper() );
+ const sal_Unicode cSep = rLocaleWrapper.getNumDecimalSep()[0];
+
+ OUString aStr = GetUnitString( rMousePos.X(), eFieldUnit, cSep ) +
+ " / " + GetUnitString( rMousePos.Y(), eFieldUnit, cSep );
+
+ m_xStbStatus2->set_label(aStr);
+}
+
+IMPL_LINK( SvxIMapDlg, GraphSizeHdl, GraphCtrl*, pWnd, void )
+{
+ const FieldUnit eFieldUnit = GetBindings().GetDispatcher()->GetModule()->GetFieldUnit();
+ const Size& rSize = pWnd->GetGraphicSize();
+ const LocaleDataWrapper& rLocaleWrapper( Application::GetSettings().GetLocaleDataWrapper() );
+ const sal_Unicode cSep = rLocaleWrapper.getNumDecimalSep()[0];
+
+ OUString aStr = GetUnitString( rSize.Width(), eFieldUnit, cSep ) +
+ " x " + GetUnitString( rSize.Height(), eFieldUnit, cSep );
+
+ m_xStbStatus3->set_label(aStr);
+}
+
+void SvxIMapDlg::URLModify()
+{
+ NotifyInfo aNewInfo;
+
+ aNewInfo.aMarkURL = m_xURLBox->get_active_text();
+ aNewInfo.aMarkAltText = m_xEdtText->get_text();
+ aNewInfo.aMarkTarget = m_xCbbTarget->get_active_text();
+
+ m_xIMapWnd->ReplaceActualIMapInfo( aNewInfo );
+}
+
+IMPL_LINK_NOARG(SvxIMapDlg, URLModifyHdl, weld::ComboBox&, void)
+{
+ URLModify();
+}
+
+IMPL_LINK_NOARG(SvxIMapDlg, EntryModifyHdl, weld::Entry&, void)
+{
+ URLModify();
+}
+
+IMPL_LINK_NOARG(SvxIMapDlg, URLLoseFocusHdl, weld::Widget&, void)
+{
+ NotifyInfo aNewInfo;
+ const OUString aURLText( m_xURLBox->get_active_text() );
+ const OUString aTargetText( m_xCbbTarget->get_active_text() );
+
+ if ( !aURLText.isEmpty() )
+ {
+ OUString aBase = GetBindings().GetDispatcher()->GetFrame()->GetObjectShell()->GetMedium()->GetBaseURL();
+ aNewInfo.aMarkURL = ::URIHelper::SmartRel2Abs( INetURLObject(aBase), aURLText, URIHelper::GetMaybeFileHdl(), true, false,
+ INetURLObject::EncodeMechanism::WasEncoded,
+ INetURLObject::DecodeMechanism::Unambiguous );
+ }
+ else
+ aNewInfo.aMarkURL = aURLText;
+
+ aNewInfo.aMarkAltText = m_xEdtText->get_text();
+
+ if ( aTargetText.isEmpty() )
+ aNewInfo.aMarkTarget = SELF_TARGET;
+ else
+ aNewInfo.aMarkTarget = aTargetText;
+
+ m_xIMapWnd->ReplaceActualIMapInfo( aNewInfo );
+}
+
+IMPL_LINK_NOARG(SvxIMapDlg, UpdateHdl, Timer *, void)
+{
+ pOwnData->aIdle.Stop();
+
+ if ( pOwnData->pUpdateEditingObject != pCheckObj )
+ {
+ if (m_xIMapWnd->IsChanged())
+ {
+ std::unique_ptr<weld::Builder> xBuilder(Application::CreateBuilder(m_xDialog.get(), "svx/ui/querysaveimagemapchangesdialog.ui"));
+ std::unique_ptr<weld::MessageDialog> xQBox(xBuilder->weld_message_dialog("QuerySaveImageMapChangesDialog"));
+ if (xQBox->run() == RET_YES)
+ {
+ DoSave();
+ }
+ }
+
+ m_xIMapWnd->SetGraphic( pOwnData->aUpdateGraphic );
+ m_xIMapWnd->SetImageMap( pOwnData->aUpdateImageMap );
+ SetTargetList( pOwnData->aUpdateTargetList );
+ pCheckObj = pOwnData->pUpdateEditingObject;
+
+ // After changes => default selection
+ m_xTbxIMapDlg1->set_item_active("TBI_SELECT", true);
+ m_xIMapWnd->SetEditMode( true );
+ }
+
+ // Delete the copied list again in the Update method
+ pOwnData->aUpdateTargetList.clear();
+
+ GetBindings().Invalidate( SID_IMAP_EXEC );
+ m_xIMapWnd->QueueIdleUpdate();
+}
+
+IMPL_LINK( SvxIMapDlg, StateHdl, GraphCtrl*, pWnd, void )
+{
+ const SdrObject* pObj = pWnd->GetSelectedSdrObject();
+ const SdrModel* pModel = pWnd->GetSdrModel();
+ const SdrView* pView = pWnd->GetSdrView();
+ const bool bPolyEdit = ( pObj != nullptr ) && dynamic_cast<const SdrPathObj*>( pObj) != nullptr;
+ const bool bDrawEnabled = !( bPolyEdit && m_xTbxIMapDlg1->get_item_active("TBI_POLYEDIT") );
+
+ m_xTbxIMapDlg1->set_item_sensitive("TBI_APPLY", pOwnData->bExecState && pWnd->IsChanged() );
+
+ m_xTbxIMapDlg1->set_item_sensitive("TBI_SELECT", bDrawEnabled);
+ m_xTbxIMapDlg1->set_item_sensitive("TBI_RECT", bDrawEnabled);
+ m_xTbxIMapDlg1->set_item_sensitive("TBI_CIRCLE", bDrawEnabled);
+ m_xTbxIMapDlg1->set_item_sensitive("TBI_POLY", bDrawEnabled);
+ m_xTbxIMapDlg1->set_item_sensitive("TBI_FREEPOLY", bDrawEnabled);
+
+ // BezierEditor State
+ m_xTbxIMapDlg1->set_item_sensitive( "TBI_POLYEDIT", bPolyEdit );
+ m_xTbxIMapDlg1->set_item_sensitive( "TBI_POLYMOVE", !bDrawEnabled );
+ m_xTbxIMapDlg1->set_item_sensitive( "TBI_POLYINSERT", !bDrawEnabled );
+ m_xTbxIMapDlg1->set_item_sensitive( "TBI_POLYDELETE", !bDrawEnabled && pView->IsDeleteMarkedPointsPossible() );
+
+ // Undo/Redo
+ m_xTbxIMapDlg1->set_item_sensitive( "TBI_UNDO", pModel->HasUndoActions() );
+ m_xTbxIMapDlg1->set_item_sensitive( "TBI_REDO", pModel->HasRedoActions() );
+
+ if ( bPolyEdit )
+ {
+ switch( pWnd->GetPolyEditMode() )
+ {
+ case SID_BEZIER_MOVE:
+ m_xTbxIMapDlg1->set_item_active("TBI_POLYMOVE", true);
+ m_xTbxIMapDlg1->set_item_active("TBI_POLYINSERT", false);
+ break;
+ case SID_BEZIER_INSERT:
+ m_xTbxIMapDlg1->set_item_active("TBI_POLYINSERT", true);
+ m_xTbxIMapDlg1->set_item_active("TBI_POLYMOVE", false);
+ break;
+ default:
+ break;
+ }
+ }
+ else
+ {
+ m_xTbxIMapDlg1->set_item_active( "TBI_POLYEDIT", false );
+ m_xTbxIMapDlg1->set_item_active( "TBI_POLYMOVE", true);
+ m_xTbxIMapDlg1->set_item_active( "TBI_POLYINSERT", false );
+ pWnd->SetPolyEditMode( 0 );
+ }
+
+ m_xIMapWnd->QueueIdleUpdate();
+}
+
+SvxIMapDlg* GetIMapDlg()
+{
+ SfxChildWindow* pWnd = nullptr;
+ if (SfxViewFrame::Current() && SfxViewFrame::Current()->HasChildWindow(SvxIMapDlgChildWindow::GetChildWindowId()))
+ pWnd = SfxViewFrame::Current()->GetChildWindow(SvxIMapDlgChildWindow::GetChildWindowId());
+ return pWnd ? static_cast<SvxIMapDlg*>(pWnd->GetController().get()) : nullptr;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/dialog/imapimp.hxx b/svx/source/dialog/imapimp.hxx
new file mode 100644
index 000000000..50862f625
--- /dev/null
+++ b/svx/source/dialog/imapimp.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_SVX_SOURCE_DIALOG_IMAPIMP_HXX
+#define INCLUDED_SVX_SOURCE_DIALOG_IMAPIMP_HXX
+
+#include <svx/imapdlg.hxx>
+#include <vcl/graph.hxx>
+#include <vcl/idle.hxx>
+#include <vcl/imap.hxx>
+
+class IMapOwnData
+{
+public:
+
+ Idle aIdle;
+ Graphic aUpdateGraphic;
+ ImageMap aUpdateImageMap;
+ TargetList aUpdateTargetList;
+ void* pUpdateEditingObject;
+ bool bExecState;
+
+ IMapOwnData()
+ : aIdle("svx IMapOwnData")
+ , pUpdateEditingObject(nullptr)
+ , bExecState(false)
+ {
+ }
+};
+
+
+#endif // _IMAPIMP_HXX
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/dialog/imapwnd.cxx b/svx/source/dialog/imapwnd.cxx
new file mode 100644
index 000000000..0e7b85131
--- /dev/null
+++ b/svx/source/dialog/imapwnd.cxx
@@ -0,0 +1,743 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * 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/commandevent.hxx>
+#include <vcl/imaprect.hxx>
+#include <vcl/imapcirc.hxx>
+#include <vcl/imappoly.hxx>
+#include <vcl/svapp.hxx>
+#include <svl/urlbmk.hxx>
+
+#include <svx/svxids.hrc>
+#include "imapwnd.hxx"
+#include <svx/svdpage.hxx>
+#include <svx/svdorect.hxx>
+#include <svx/svdocirc.hxx>
+#include <svx/svdopath.hxx>
+#include <svx/xfltrit.hxx>
+#include <svx/svdpagv.hxx>
+#include <svx/xfillit0.hxx>
+#include <svx/xflclit.hxx>
+#include <svx/xlnclit.hxx>
+
+#include <sfx2/evntconf.hxx>
+
+#include <sot/formats.hxx>
+
+#include <svx/svxdlg.hxx>
+#include <basegfx/polygon/b2dpolygon.hxx>
+#include <memory>
+
+using namespace com::sun::star;
+using ::com::sun::star::frame::XFrame;
+using ::com::sun::star::uno::Reference;
+
+#define TRANSCOL COL_WHITE
+
+IMapWindow::IMapWindow(const Reference< XFrame >& rxDocumentFrame, weld::Dialog* pDialog)
+ : GraphCtrl(pDialog)
+ , mxDocumentFrame(rxDocumentFrame)
+{
+ pIMapPool = new SfxItemPool( "IMapItemPool",
+ SID_ATTR_MACROITEM, SID_ATTR_MACROITEM, maItemInfos );
+ pIMapPool->FreezeIdRanges();
+}
+
+IMapWindow::~IMapWindow()
+{
+}
+
+void IMapWindow::SetDrawingArea(weld::DrawingArea* pDrawingArea)
+{
+ Size aSize(pDrawingArea->get_ref_device().LogicToPixel(Size(270, 170), MapMode(MapUnit::MapAppFont)));
+ pDrawingArea->set_size_request(aSize.Width(), aSize.Height());
+ SetOutputSizePixel(aSize);
+ weld::CustomWidgetController::SetDrawingArea(pDrawingArea);
+
+ SetSdrMode(true);
+
+ mxDropTargetHelper.reset(new IMapDropTargetHelper(*this));
+}
+
+void IMapWindow::SetImageMap( const ImageMap& rImageMap )
+{
+ ReplaceImageMap( rImageMap );
+}
+
+void IMapWindow::ReplaceImageMap( const ImageMap& rImageMap )
+{
+ SdrPage* pPage = nullptr;
+ aIMap = rImageMap;
+
+ if(GetSdrModel())
+ {
+ // try to access page
+ pPage = GetSdrModel()->GetPage(0);
+ }
+
+ if(pPage)
+ {
+ // clear SdrObjects with broadcasting
+ pPage->ClearSdrObjList();
+ }
+
+ if(GetSdrView())
+ {
+ // #i63762# reset selection at view
+ GetSdrView()->UnmarkAllObj();
+ }
+
+ // create new drawing objects
+ const sal_uInt16 nCount(rImageMap.GetIMapObjectCount());
+
+ for ( sal_uInt16 i(nCount); i > 0; i-- )
+ {
+ SdrObject* pNewObj = CreateObj( rImageMap.GetIMapObject( i - 1 ) );
+
+ if (pNewObj && pPage)
+ {
+ pPage->InsertObject( pNewObj );
+ }
+ }
+}
+
+void IMapWindow::ReplaceActualIMapInfo( const NotifyInfo& rNewInfo )
+{
+ const SdrObject* pSdrObj = GetSelectedSdrObject();
+
+ if ( pSdrObj )
+ {
+ IMapObject* pIMapObj = GetIMapObj( pSdrObj );
+ if (pIMapObj)
+ {
+ pIMapObj->SetURL( rNewInfo.aMarkURL );
+ pIMapObj->SetAltText( rNewInfo.aMarkAltText );
+ pIMapObj->SetTarget( rNewInfo.aMarkTarget );
+ pModel->SetChanged();
+ UpdateInfo( false );
+ }
+ }
+}
+
+const ImageMap& IMapWindow::GetImageMap()
+{
+ if ( pModel->IsChanged() )
+ {
+ SdrPage* pPage = pModel->GetPage( 0 );
+
+ if ( pPage )
+ {
+ const size_t nCount = pPage->GetObjCount();
+
+ aIMap.ClearImageMap();
+
+ for ( size_t i = nCount; i; )
+ {
+ --i;
+ aIMap.InsertIMapObject( *( static_cast<IMapUserData*>( pPage->GetObj( i )->GetUserData( 0 ) )->GetObject() ) );
+ }
+ }
+
+ pModel->SetChanged( false );
+ }
+
+ return aIMap;
+}
+
+void IMapWindow::SetTargetList( const TargetList& rTargetList )
+{
+ // Delete old List
+ // Fill with the provided list
+ aTargetList = rTargetList;
+
+ pModel->SetChanged( false );
+}
+
+SdrObject* IMapWindow::CreateObj( const IMapObject* pIMapObj )
+{
+ tools::Rectangle aClipRect( Point(), GetGraphicSize() );
+ SdrObject* pSdrObj = nullptr;
+ IMapObjectPtr pCloneIMapObj;
+
+ switch( pIMapObj->GetType() )
+ {
+ case IMapObjectType::Rectangle:
+ {
+ const IMapRectangleObject* pIMapRectObj = static_cast<const IMapRectangleObject*>(pIMapObj);
+ tools::Rectangle aDrawRect( pIMapRectObj->GetRectangle( false ) );
+
+ // clipped on CanvasPane
+ aDrawRect.Intersection( aClipRect );
+
+ pSdrObj = new SdrRectObj(*pModel, aDrawRect);
+ pCloneIMapObj.reset(static_cast<IMapObject*>(new IMapRectangleObject( *pIMapRectObj )));
+ }
+ break;
+
+ case IMapObjectType::Circle:
+ {
+ const IMapCircleObject* pIMapCircleObj = static_cast<const IMapCircleObject*>(pIMapObj);
+ const Point aCenter( pIMapCircleObj->GetCenter( false ) );
+ const tools::Long nRadius = pIMapCircleObj->GetRadius( false );
+ const Point aOffset( nRadius, nRadius );
+ tools::Rectangle aCircle( aCenter - aOffset, aCenter + aOffset );
+
+ // limited to CanvasPane
+ aCircle.Intersection( aClipRect );
+
+ pSdrObj = new SdrCircObj(
+ *pModel,
+ SdrCircKind::Full,
+ aCircle,
+ 0_deg100,
+ 36000_deg100);
+ pCloneIMapObj.reset(static_cast<IMapObject*>(new IMapCircleObject( *pIMapCircleObj )));
+ }
+ break;
+
+ case IMapObjectType::Polygon:
+ {
+ const IMapPolygonObject* pIMapPolyObj = static_cast<const IMapPolygonObject*>(pIMapObj);
+
+ // If it actually is an ellipse, then another ellipse is created again
+ if ( pIMapPolyObj->HasExtraEllipse() )
+ {
+ tools::Rectangle aDrawRect( pIMapPolyObj->GetExtraEllipse() );
+
+ // clipped on CanvasPane
+ aDrawRect.Intersection( aClipRect );
+
+ pSdrObj = new SdrCircObj(
+ *pModel,
+ SdrCircKind::Full,
+ aDrawRect,
+ 0_deg100,
+ 36000_deg100);
+ }
+ else
+ {
+ const tools::Polygon& rPoly = pIMapPolyObj->GetPolygon( false );
+ tools::Polygon aDrawPoly( rPoly );
+
+ // clipped on CanvasPane
+ aDrawPoly.Clip( aClipRect );
+
+ basegfx::B2DPolygon aPolygon;
+ aPolygon.append(aDrawPoly.getB2DPolygon());
+ pSdrObj = new SdrPathObj(
+ *pModel,
+ SdrObjKind::Polygon,
+ basegfx::B2DPolyPolygon(aPolygon));
+ }
+
+ pCloneIMapObj.reset(static_cast<IMapObject*>(new IMapPolygonObject( *pIMapPolyObj )));
+ }
+ break;
+
+ default:
+ break;
+ }
+
+ if ( pSdrObj )
+ {
+ SfxItemSet aSet( pModel->GetItemPool() );
+
+ aSet.Put( XFillStyleItem( drawing::FillStyle_SOLID ) );
+ aSet.Put( XFillColorItem( "", TRANSCOL ) );
+
+ if ( !pIMapObj->IsActive() )
+ {
+ aSet.Put( XFillTransparenceItem( 100 ) );
+ aSet.Put( XLineColorItem( "", COL_RED ) );
+ }
+ else
+ {
+ aSet.Put( XFillTransparenceItem( 50 ) );
+ aSet.Put( XLineColorItem( "", COL_BLACK ) );
+ }
+
+ pSdrObj->SetMergedItemSetAndBroadcast(aSet);
+
+ pSdrObj->AppendUserData( std::unique_ptr<SdrObjUserData>(new IMapUserData( pCloneIMapObj )) );
+ pSdrObj->SetUserCall( GetSdrUserCall() );
+ }
+
+ return pSdrObj;
+}
+
+void IMapWindow::InitSdrModel()
+{
+ GraphCtrl::InitSdrModel();
+
+ SfxItemSet aSet( pModel->GetItemPool() );
+
+ aSet.Put( XFillColorItem( "", TRANSCOL ) );
+ aSet.Put( XFillTransparenceItem( 50 ) );
+ pView->SetAttributes( aSet );
+ pView->SetFrameDragSingles();
+}
+
+void IMapWindow::SdrObjCreated( const SdrObject& rObj )
+{
+ switch( rObj.GetObjIdentifier() )
+ {
+ case SdrObjKind::Rectangle:
+ {
+ SdrRectObj* pRectObj = const_cast<SdrRectObj*>(static_cast<const SdrRectObj*>(&rObj));
+ auto pObj = std::make_shared<IMapRectangleObject>( pRectObj->GetLogicRect(), "", "", "", "", "", true, false );
+
+ pRectObj->AppendUserData( std::unique_ptr<SdrObjUserData>(new IMapUserData( pObj )) );
+ }
+ break;
+
+ case SdrObjKind::CircleOrEllipse:
+ {
+ SdrCircObj* pCircObj = const_cast<SdrCircObj*>( static_cast<const SdrCircObj*>(&rObj) );
+ SdrPathObj* pPathObj = static_cast<SdrPathObj*>( pCircObj->ConvertToPolyObj( false, false ).release() );
+ tools::Polygon aPoly(pPathObj->GetPathPoly().getB2DPolygon(0));
+
+ // always use SdrObject::Free(...) for SdrObjects (!)
+ SdrObject* pTemp(pPathObj);
+ SdrObject::Free(pTemp);
+
+ auto pObj = std::make_shared<IMapPolygonObject>( aPoly, "", "", "", "", "", true, false );
+ pObj->SetExtraEllipse( aPoly.GetBoundRect() );
+ pCircObj->AppendUserData( std::unique_ptr<SdrObjUserData>(new IMapUserData( pObj )) );
+ }
+ break;
+
+ case SdrObjKind::Polygon:
+ case SdrObjKind::FreehandFill:
+ case SdrObjKind::PathPoly:
+ case SdrObjKind::PathFill:
+ {
+ SdrPathObj* pPathObj = const_cast<SdrPathObj*>( static_cast<const SdrPathObj*>(&rObj) );
+ const basegfx::B2DPolyPolygon& rXPolyPoly = pPathObj->GetPathPoly();
+
+ if ( rXPolyPoly.count() )
+ {
+ tools::Polygon aPoly(rXPolyPoly.getB2DPolygon(0));
+ auto pObj = std::make_shared<IMapPolygonObject>( aPoly, "", "", "", "", "", true, false );
+ pPathObj->AppendUserData( std::unique_ptr<SdrObjUserData>(new IMapUserData( pObj )) );
+ }
+ }
+ break;
+
+ default:
+ break;
+ }
+}
+
+void IMapWindow::SdrObjChanged( const SdrObject& rObj )
+{
+ IMapUserData* pUserData = static_cast<IMapUserData*>( rObj.GetUserData( 0 ) );
+
+ if ( !pUserData )
+ return;
+
+ OUString aURL;
+ OUString aAltText;
+ OUString aDesc;
+ OUString aTarget;
+ IMapObjectPtr pIMapObj = pUserData->GetObject();
+ bool bActive = true;
+
+ if ( pIMapObj )
+ {
+ aURL = pIMapObj->GetURL();
+ aAltText = pIMapObj->GetAltText();
+ aDesc = pIMapObj->GetDesc();
+ aTarget = pIMapObj->GetTarget();
+ bActive = pIMapObj->IsActive();
+ }
+
+ switch( rObj.GetObjIdentifier() )
+ {
+ case SdrObjKind::Rectangle:
+ {
+ pUserData->ReplaceObject( std::make_shared<IMapRectangleObject>( static_cast<const SdrRectObj&>(rObj).GetLogicRect(),
+ aURL, aAltText, aDesc, aTarget, "", bActive, false ) );
+ }
+ break;
+
+ case SdrObjKind::CircleOrEllipse:
+ {
+ const SdrCircObj& rCircObj = static_cast<const SdrCircObj&>(rObj);
+ SdrPathObj* pPathObj = static_cast<SdrPathObj*>( rCircObj.ConvertToPolyObj( false, false ).release() );
+ tools::Polygon aPoly(pPathObj->GetPathPoly().getB2DPolygon(0));
+
+ auto pObj = std::make_shared<IMapPolygonObject>( aPoly, aURL, aAltText, aDesc, aTarget, "", bActive, false );
+ pObj->SetExtraEllipse( aPoly.GetBoundRect() );
+
+ // was only created by us temporarily
+ // always use SdrObject::Free(...) for SdrObjects (!)
+ SdrObject* pTemp(pPathObj);
+ SdrObject::Free(pTemp);
+
+ pUserData->ReplaceObject( pObj );
+ }
+ break;
+
+ case SdrObjKind::Polygon:
+ case SdrObjKind::FreehandFill:
+ case SdrObjKind::PathPoly:
+ case SdrObjKind::PathFill:
+ {
+ const SdrPathObj& rPathObj = static_cast<const SdrPathObj&>(rObj);
+ const basegfx::B2DPolyPolygon& rXPolyPoly = rPathObj.GetPathPoly();
+
+ if ( rXPolyPoly.count() )
+ {
+ tools::Polygon aPoly(rPathObj.GetPathPoly().getB2DPolygon(0));
+ auto pObj = std::make_shared<IMapPolygonObject>( aPoly, aURL, aAltText, aDesc, aTarget, "", bActive, false );
+ pUserData->ReplaceObject( pObj );
+ }
+ }
+ break;
+
+ default:
+ break;
+ }
+}
+
+bool IMapWindow::MouseButtonUp(const MouseEvent& rMEvt)
+{
+ bool bRet = GraphCtrl::MouseButtonUp( rMEvt );
+ UpdateInfo( true );
+ return bRet;
+}
+
+void IMapWindow::MarkListHasChanged()
+{
+ GraphCtrl::MarkListHasChanged();
+ UpdateInfo( false );
+}
+
+SdrObject* IMapWindow::GetHitSdrObj( const Point& rPosPixel ) const
+{
+ OutputDevice& rDevice = GetDrawingArea()->get_ref_device();
+
+ SdrObject* pObj = nullptr;
+ Point aPt = rDevice.PixelToLogic( rPosPixel );
+
+ if ( tools::Rectangle( Point(), GetGraphicSize() ).Contains( aPt ) )
+ {
+ SdrPage* pPage = pModel->GetPage( 0 );
+ if ( pPage )
+ {
+ for ( size_t i = pPage->GetObjCount(); i > 0; )
+ {
+ --i;
+ SdrObject* pTestObj = pPage->GetObj( i );
+ IMapObject* pIMapObj = GetIMapObj( pTestObj );
+
+ if ( pIMapObj && pIMapObj->IsHit( aPt ) )
+ {
+ pObj = pTestObj;
+ break;
+ }
+ }
+ }
+ }
+
+ return pObj;
+}
+
+IMapObject* IMapWindow::GetIMapObj( const SdrObject* pSdrObj )
+{
+ IMapObject* pIMapObj = nullptr;
+
+ if ( pSdrObj )
+ {
+ IMapUserData* pUserData = static_cast<IMapUserData*>( pSdrObj->GetUserData( 0 ) );
+
+ if ( pUserData )
+ pIMapObj = pUserData->GetObject().get();
+ }
+
+ return pIMapObj;
+}
+
+bool IMapWindow::Command(const CommandEvent& rCEvt)
+{
+ if ( rCEvt.GetCommand() == CommandEventId::ContextMenu )
+ {
+ std::unique_ptr<weld::Builder> xBuilder(Application::CreateBuilder(GetDrawingArea(), "svx/ui/imapmenu.ui"));
+ mxPopupMenu = xBuilder->weld_menu("menu");
+ const SdrMarkList& rMarkList = pView->GetMarkedObjectList();
+ const size_t nMarked = rMarkList.GetMarkCount();
+
+ mxPopupMenu->set_sensitive("url", false);
+ mxPopupMenu->set_sensitive("active", false);
+ mxPopupMenu->set_sensitive("macro", false);
+ mxPopupMenu->set_sensitive("selectall", pModel->GetPage(0)->GetObjCount() != pView->GetMarkedObjectCount());
+
+ if ( !nMarked )
+ {
+ mxPopupMenu->set_sensitive("arrange", false);
+ mxPopupMenu->set_sensitive("delete", false);
+ }
+ else
+ {
+ if ( nMarked == 1 )
+ {
+ SdrObject* pSdrObj = GetSelectedSdrObject();
+
+ mxPopupMenu->set_sensitive("url", true);
+ mxPopupMenu->set_sensitive("active", true);
+ mxPopupMenu->set_sensitive("macro", true);
+ mxPopupMenu->set_active("active", GetIMapObj(pSdrObj)->IsActive());
+ }
+
+ mxPopupMenu->set_sensitive("arrange", true);
+ mxPopupMenu->set_sensitive("delete", true);
+ }
+
+ MenuSelectHdl(mxPopupMenu->popup_at_rect(GetDrawingArea(), tools::Rectangle(rCEvt.GetMousePosPixel(), Size(1,1))));
+
+ mxPopupMenu.reset();
+
+ return true;
+ }
+ return CustomWidgetController::Command(rCEvt);
+}
+
+IMapDropTargetHelper::IMapDropTargetHelper(IMapWindow& rImapWindow)
+ : DropTargetHelper(rImapWindow.GetDrawingArea()->get_drop_target())
+ , m_rImapWindow(rImapWindow)
+{
+}
+
+sal_Int8 IMapDropTargetHelper::AcceptDrop( const AcceptDropEvent& rEvt )
+{
+ return m_rImapWindow.AcceptDrop(rEvt);
+}
+
+sal_Int8 IMapDropTargetHelper::ExecuteDrop( const ExecuteDropEvent& rEvt )
+{
+ return m_rImapWindow.ExecuteDrop(rEvt);
+}
+
+sal_Int8 IMapWindow::AcceptDrop( const AcceptDropEvent& rEvt )
+{
+ return( ( GetHitSdrObj( rEvt.maPosPixel ) != nullptr ) ? rEvt.mnAction : DND_ACTION_NONE );
+}
+
+sal_Int8 IMapWindow::ExecuteDrop( const ExecuteDropEvent& rEvt )
+{
+ sal_Int8 nRet = DND_ACTION_NONE;
+
+ if (mxDropTargetHelper->IsDropFormatSupported(SotClipboardFormatId::NETSCAPE_BOOKMARK))
+ {
+ INetBookmark aBookMark( "", "" );
+ SdrObject* pSdrObj = GetHitSdrObj( rEvt.maPosPixel );
+
+ if( pSdrObj && TransferableDataHelper( rEvt.maDropEvent.Transferable ).GetINetBookmark( SotClipboardFormatId::NETSCAPE_BOOKMARK, aBookMark ) )
+ {
+ IMapObject* pIMapObj = GetIMapObj( pSdrObj );
+
+ pIMapObj->SetURL( aBookMark.GetURL() );
+ pIMapObj->SetAltText( aBookMark.GetDescription() );
+ pModel->SetChanged();
+ pView->UnmarkAll();
+ pView->MarkObj( pSdrObj, pView->GetSdrPageView() );
+ UpdateInfo( true );
+ nRet = rEvt.mnAction;
+ }
+ }
+
+ return nRet;
+}
+
+OUString IMapWindow::RequestHelp(tools::Rectangle& rHelpArea)
+{
+ OutputDevice& rDevice = GetDrawingArea()->get_ref_device();
+
+ Point aPos = rDevice.PixelToLogic(rHelpArea.TopLeft());
+
+ SdrPageView* pPageView = nullptr;
+ SdrObject* pSdrObj = pView->PickObj(aPos, pView->getHitTolLog(), pPageView);
+ if (pSdrObj)
+ {
+ const IMapObject* pIMapObj = GetIMapObj( pSdrObj );
+ if ( pIMapObj )
+ {
+ OUString aStr = pIMapObj->GetURL();
+ if ( !aStr.isEmpty() )
+ {
+ rHelpArea = rDevice.LogicToPixel(tools::Rectangle( Point(), GetGraphicSize()));
+ return aStr;
+ }
+ }
+ }
+
+ return OUString();
+}
+
+void IMapWindow::SetCurrentObjState( bool bActive )
+{
+ SdrObject* pObj = GetSelectedSdrObject();
+
+ if ( !pObj )
+ return;
+
+ SfxItemSet aSet( pModel->GetItemPool() );
+
+ GetIMapObj( pObj )->SetActive( bActive );
+
+ aSet.Put( XFillColorItem( "", TRANSCOL ) );
+
+ if ( !bActive )
+ {
+ aSet.Put( XFillTransparenceItem( 100 ) );
+ aSet.Put( XLineColorItem( "", COL_RED ) );
+ }
+ else
+ {
+ aSet.Put( XFillTransparenceItem( 50 ) );
+ aSet.Put( XLineColorItem( "", COL_BLACK ) );
+ }
+
+ pView->SetAttributes( aSet );
+}
+
+void IMapWindow::UpdateInfo( bool bNewObj )
+{
+ if ( !aInfoLink.IsSet() )
+ return;
+
+ const SdrObject* pSdrObj = GetSelectedSdrObject();
+ const IMapObject* pIMapObj = pSdrObj ? GetIMapObj( pSdrObj ) : nullptr;
+
+ aInfo.bNewObj = bNewObj;
+
+ if ( pIMapObj )
+ {
+ aInfo.bOneMarked = true;
+ aInfo.aMarkURL = pIMapObj->GetURL();
+ aInfo.aMarkAltText = pIMapObj->GetAltText();
+ aInfo.aMarkTarget = pIMapObj->GetTarget();
+ aInfo.bActivated = pIMapObj->IsActive();
+ aInfoLink.Call( *this );
+ }
+ else
+ {
+ aInfo.aMarkURL.clear();
+ aInfo.aMarkAltText.clear();
+ aInfo.aMarkTarget.clear();
+ aInfo.bOneMarked = false;
+ aInfo.bActivated = false;
+ }
+
+ aInfoLink.Call( *this );
+}
+
+void IMapWindow::DoMacroAssign()
+{
+ SdrObject* pSdrObj = GetSelectedSdrObject();
+
+ if ( !pSdrObj )
+ return;
+
+ SfxItemSetFixed<SID_ATTR_MACROITEM, SID_ATTR_MACROITEM, SID_EVENTCONFIG, SID_EVENTCONFIG>
+ aSet(*pIMapPool);
+
+ SfxEventNamesItem aNamesItem(SID_EVENTCONFIG);
+ aNamesItem.AddEvent( "MouseOver", "", SvMacroItemId::OnMouseOver );
+ aNamesItem.AddEvent( "MouseOut", "", SvMacroItemId::OnMouseOut );
+ aSet.Put( aNamesItem );
+
+ SvxMacroItem aMacroItem(SID_ATTR_MACROITEM);
+ IMapObject* pIMapObj = GetIMapObj( pSdrObj );
+ aMacroItem.SetMacroTable( pIMapObj->GetMacroTable() );
+ aSet.Put( aMacroItem );
+
+ SvxAbstractDialogFactory* pFact = SvxAbstractDialogFactory::Create();
+ ScopedVclPtr<SfxAbstractDialog> pMacroDlg(pFact->CreateEventConfigDialog(GetDrawingArea(), aSet, mxDocumentFrame));
+
+ if ( pMacroDlg->Execute() == RET_OK )
+ {
+ const SfxItemSet* pOutSet = pMacroDlg->GetOutputItemSet();
+ pIMapObj->SetMacroTable( pOutSet->Get( SID_ATTR_MACROITEM ).GetMacroTable() );
+ pModel->SetChanged();
+ UpdateInfo( false );
+ }
+}
+
+void IMapWindow::DoPropertyDialog()
+{
+ SdrObject* pSdrObj = GetSelectedSdrObject();
+
+ if ( !pSdrObj )
+ return;
+
+ IMapObject* pIMapObj = GetIMapObj( pSdrObj );
+ SvxAbstractDialogFactory* pFact = SvxAbstractDialogFactory::Create();
+ ScopedVclPtr<AbstractURLDlg> aDlg(pFact->CreateURLDialog(GetDrawingArea(), pIMapObj->GetURL(), pIMapObj->GetAltText(), pIMapObj->GetDesc(),
+ pIMapObj->GetTarget(), pIMapObj->GetName(), aTargetList));
+ if ( aDlg->Execute() != RET_OK )
+ return;
+
+ const OUString aURLText( aDlg->GetURL() );
+
+ if ( !aURLText.isEmpty() )
+ {
+ INetURLObject aObj( aURLText, INetProtocol::File );
+ DBG_ASSERT( aObj.GetProtocol() != INetProtocol::NotValid, "Invalid URL" );
+ pIMapObj->SetURL( aObj.GetMainURL( INetURLObject::DecodeMechanism::NONE ) );
+ }
+ else
+ pIMapObj->SetURL( aURLText );
+
+ pIMapObj->SetAltText( aDlg->GetAltText() );
+ pIMapObj->SetDesc( aDlg->GetDesc() );
+ pIMapObj->SetTarget( aDlg->GetTarget() );
+ pIMapObj->SetName( aDlg->GetName() );
+ pModel->SetChanged();
+ UpdateInfo( true );
+}
+
+void IMapWindow::MenuSelectHdl(const OString& rId)
+{
+ if (rId == "url")
+ DoPropertyDialog();
+ else if (rId == "macro")
+ DoMacroAssign();
+ else if (rId == "active")
+ {
+ const bool bNewState = !mxPopupMenu->get_active(rId);
+ SetCurrentObjState(bNewState);
+ UpdateInfo( false );
+ }
+ else if (rId == "front")
+ pView->PutMarkedToTop();
+ else if (rId == "forward")
+ pView->MovMarkedToTop();
+ else if (rId == "backward")
+ pView->MovMarkedToBtm();
+ else if (rId == "back")
+ pView->PutMarkedToBtm();
+ else if (rId == "selectall")
+ pView->MarkAll();
+ else if (rId == "delete")
+ pView->DeleteMarked();
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/dialog/imapwnd.hxx b/svx/source/dialog/imapwnd.hxx
new file mode 100644
index 000000000..4ef2c9277
--- /dev/null
+++ b/svx/source/dialog/imapwnd.hxx
@@ -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 .
+ */
+#ifndef INCLUDED_SVX_SOURCE_DIALOG_IMAPWND_HXX
+#define INCLUDED_SVX_SOURCE_DIALOG_IMAPWND_HXX
+
+#include <vcl/imapobj.hxx>
+#include <vcl/transfer.hxx>
+#include <vcl/imap.hxx>
+#include <sfx2/frame.hxx>
+#include <svx/graphctl.hxx>
+#include <svl/itempool.hxx>
+
+#include <com/sun/star/frame/XFrame.hpp>
+
+struct NotifyInfo
+{
+ OUString aMarkURL;
+ OUString aMarkAltText;
+ OUString aMarkTarget;
+ bool bNewObj;
+ bool bOneMarked;
+ bool bActivated;
+};
+
+
+#define SVD_IMAP_USERDATA 0x0001
+
+typedef std::shared_ptr< IMapObject > IMapObjectPtr;
+
+class IMapUserData : public SdrObjUserData
+{
+ // #i98386# use std::shared_ptr here due to cloning possibilities
+ IMapObjectPtr mpObj;
+
+public:
+
+ explicit IMapUserData( const IMapObjectPtr& rIMapObj ) :
+ SdrObjUserData ( SdrInventor::IMap, SVD_IMAP_USERDATA ),
+ mpObj ( rIMapObj ) {}
+
+ IMapUserData( const IMapUserData& rIMapUserData ) :
+ SdrObjUserData ( SdrInventor::IMap, SVD_IMAP_USERDATA ),
+ mpObj ( rIMapUserData.mpObj ) {}
+
+ virtual std::unique_ptr<SdrObjUserData> Clone( SdrObject * ) const override { return std::unique_ptr<SdrObjUserData>(new IMapUserData( *this )); }
+
+ const IMapObjectPtr& GetObject() const { return mpObj; }
+ void ReplaceObject( const IMapObjectPtr& pNewIMapObject ) { mpObj = pNewIMapObject; }
+};
+
+class IMapWindow;
+
+class IMapDropTargetHelper final : public DropTargetHelper
+{
+ IMapWindow& m_rImapWindow;
+public:
+ IMapDropTargetHelper(IMapWindow& rImapWindow);
+
+ // DropTargetHelper
+ virtual sal_Int8 AcceptDrop( const AcceptDropEvent& rEvt ) override;
+ virtual sal_Int8 ExecuteDrop( const ExecuteDropEvent& rEvt ) override;
+};
+
+class IMapWindow final : public GraphCtrl
+{
+ NotifyInfo aInfo;
+ ImageMap aIMap;
+ TargetList aTargetList;
+ Link<IMapWindow&,void> aInfoLink;
+ rtl::Reference<SfxItemPool> pIMapPool;
+ SfxItemInfo maItemInfos[1] = {};
+ css::uno::Reference< css::frame::XFrame >
+ mxDocumentFrame;
+ std::unique_ptr<IMapDropTargetHelper> mxDropTargetHelper;
+ std::unique_ptr<weld::Menu> mxPopupMenu;
+
+ void MenuSelectHdl(const OString& rId);
+
+ // GraphCtrl
+ virtual bool MouseButtonUp(const MouseEvent& rMEvt) override;
+ virtual void SetDrawingArea(weld::DrawingArea* pDrawingArea) override;
+ virtual bool Command(const CommandEvent& rCEvt) override;
+ virtual OUString RequestHelp(tools::Rectangle& rHelpArea) override;
+ virtual void SdrObjCreated( const SdrObject& rObj ) override;
+ virtual void SdrObjChanged( const SdrObject& rObj ) override;
+ virtual void MarkListHasChanged() override;
+ virtual void InitSdrModel() override;
+
+ void ReplaceImageMap( const ImageMap& rNewImageMap );
+
+ SdrObject* CreateObj( const IMapObject* pIMapObj );
+ static IMapObject* GetIMapObj( const SdrObject* pSdrObj );
+ SdrObject* GetHitSdrObj( const Point& rPosPixel ) const;
+
+ void UpdateInfo( bool bNewObj );
+
+public:
+
+ IMapWindow(const css::uno::Reference< css::frame::XFrame >& rxDocumentFrame,
+ weld::Dialog* pDialog);
+ virtual ~IMapWindow() override;
+
+ sal_Int8 AcceptDrop( const AcceptDropEvent& rEvt );
+ sal_Int8 ExecuteDrop( const ExecuteDropEvent& rEvt );
+
+ void ReplaceActualIMapInfo( const NotifyInfo& rNewInfo );
+
+ void SetImageMap( const ImageMap& rImageMap );
+ const ImageMap& GetImageMap();
+
+ void SetCurrentObjState( bool bActive );
+ void DoMacroAssign();
+ void DoPropertyDialog();
+
+ void SetInfoLink( const Link<IMapWindow&,void>& rLink ) { aInfoLink = rLink; }
+
+ void SetTargetList( const TargetList& rTargetList );
+
+ const NotifyInfo& GetInfo() const { return aInfo; }
+};
+
+
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/dialog/langbox.cxx b/svx/source/dialog/langbox.cxx
new file mode 100644
index 000000000..43d123b1e
--- /dev/null
+++ b/svx/source/dialog/langbox.cxx
@@ -0,0 +1,502 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * 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/XAvailableLocales.hpp>
+#include <com/sun/star/linguistic2/XLinguServiceManager2.hpp>
+#include <com/sun/star/linguistic2/XSpellChecker1.hpp>
+#include <linguistic/misc.hxx>
+#include <rtl/ustring.hxx>
+#include <sal/log.hxx>
+#include <unotools/localedatawrapper.hxx>
+#include <tools/urlobj.hxx>
+#include <svtools/langtab.hxx>
+#include <i18nlangtag/mslangid.hxx>
+#include <i18nlangtag/lang.h>
+#include <editeng/unolingu.hxx>
+#include <svl/languageoptions.hxx>
+#include <svx/langbox.hxx>
+#include <svx/dialmgr.hxx>
+#include <svx/strings.hrc>
+#include <bitmaps.hlst>
+
+#include <comphelper/string.hxx>
+#include <comphelper/processfactory.hxx>
+#include <vcl/svapp.hxx>
+#include <vcl/settings.hxx>
+
+using namespace ::com::sun::star::util;
+using namespace ::com::sun::star::linguistic2;
+using namespace ::com::sun::star::uno;
+
+OUString GetDicInfoStr( std::u16string_view rName, const LanguageType nLang, bool bNeg )
+{
+ INetURLObject aURLObj;
+ aURLObj.SetSmartProtocol( INetProtocol::File );
+ aURLObj.SetSmartURL( rName, INetURLObject::EncodeMechanism::All );
+ OUString aTmp( aURLObj.GetBase() + " " );
+
+ if ( bNeg )
+ {
+ aTmp += " (-) ";
+ }
+
+ if ( LANGUAGE_NONE == nLang )
+ aTmp += SvxResId(RID_SVXSTR_LANGUAGE_ALL);
+ else
+ {
+ aTmp += "[" + SvtLanguageTable::GetLanguageString( nLang ) + "]";
+ }
+
+ return aTmp;
+}
+
+// misc local helper functions
+static std::vector< LanguageType > lcl_LocaleSeqToLangSeq( Sequence< css::lang::Locale > const &rSeq )
+{
+ sal_Int32 nCount = rSeq.getLength();
+
+ std::vector< LanguageType > aLangs;
+ aLangs.reserve(nCount);
+
+ std::transform(rSeq.begin(), rSeq.end(), std::back_inserter(aLangs),
+ [](const css::lang::Locale& rLocale) -> LanguageType {
+ return LanguageTag::convertToLanguageType(rLocale); });
+
+ return aLangs;
+}
+
+static bool lcl_SeqHasLang( const Sequence< sal_Int16 > & rLangSeq, sal_Int16 nLang )
+{
+ return rLangSeq.hasElements()
+ && std::find(rLangSeq.begin(), rLangSeq.end(), nLang) != rLangSeq.end();
+}
+
+namespace {
+
+bool lcl_isPrerequisite( LanguageType nLangType )
+{
+ return
+ nLangType != LANGUAGE_DONTKNOW &&
+ nLangType != LANGUAGE_SYSTEM &&
+ nLangType != LANGUAGE_NONE &&
+ nLangType != LANGUAGE_USER_KEYID &&
+ !MsLangId::isLegacy( nLangType) &&
+ MsLangId::getSubLanguage( nLangType);
+}
+
+bool lcl_isScriptTypeRequested( LanguageType nLangType, SvxLanguageListFlags nLangList )
+{
+ return
+ bool(nLangList & SvxLanguageListFlags::ALL) ||
+ (bool(nLangList & SvxLanguageListFlags::WESTERN) &&
+ (SvtLanguageOptions::GetScriptTypeOfLanguage(nLangType) == SvtScriptType::LATIN)) ||
+ (bool(nLangList & SvxLanguageListFlags::CTL) &&
+ (SvtLanguageOptions::GetScriptTypeOfLanguage(nLangType) == SvtScriptType::COMPLEX)) ||
+ (bool(nLangList & SvxLanguageListFlags::CJK) &&
+ (SvtLanguageOptions::GetScriptTypeOfLanguage(nLangType) == SvtScriptType::ASIAN));
+}
+
+}
+
+
+LanguageType SvxLanguageBox::get_active_id() const
+{
+ OUString sLang = m_xControl->get_active_id();
+ if (!sLang.isEmpty())
+ return LanguageType(sLang.toInt32());
+ else
+ return LANGUAGE_DONTKNOW;
+}
+
+int SvxLanguageBox::find_id(const LanguageType eLangType) const
+{
+ return m_xControl->find_id(OUString::number(static_cast<sal_uInt16>(eLangType)));
+}
+
+void SvxLanguageBox::set_id(int pos, const LanguageType eLangType)
+{
+ m_xControl->set_id(pos, OUString::number(static_cast<sal_uInt16>(eLangType)));
+}
+
+LanguageType SvxLanguageBox::get_id(int pos) const
+{
+ return LanguageType(m_xControl->get_id(pos).toInt32());
+}
+
+void SvxLanguageBox::remove_id(const LanguageType eLangType)
+{
+ m_xControl->remove_id(OUString::number(static_cast<sal_uInt16>(eLangType)));
+}
+
+void SvxLanguageBox::append(const LanguageType eLangType, const OUString& rStr)
+{
+ m_xControl->append(OUString::number(static_cast<sal_uInt16>(eLangType)), rStr);
+}
+
+void SvxLanguageBox::set_active_id(const LanguageType eLangType)
+{
+ // If the core uses a LangID of an imported MS document and wants to select
+ // a language that is replaced, we need to select the replacement instead.
+ LanguageType nLang = MsLangId::getReplacementForObsoleteLanguage( eLangType);
+
+ sal_Int32 nAt = ImplTypeToPos( nLang );
+
+ if (nAt == -1)
+ {
+ InsertLanguage( nLang ); // on-the-fly-ID
+ nAt = ImplTypeToPos( nLang );
+ }
+
+ if (nAt != -1)
+ m_xControl->set_active(nAt);
+}
+
+void SvxLanguageBox::AddLanguages(const std::vector< LanguageType >& rLanguageTypes,
+ SvxLanguageListFlags nLangList, std::vector<weld::ComboBoxEntry>& rEntries)
+{
+ for ( auto const & nLangType : rLanguageTypes )
+ {
+ if (lcl_isPrerequisite( nLangType ))
+ {
+ LanguageType nLang = MsLangId::getReplacementForObsoleteLanguage( nLangType );
+ if (lcl_isScriptTypeRequested( nLang, nLangList))
+ {
+ int nAt = ImplTypeToPos(nLang);
+ if (nAt != -1)
+ continue;
+ weld::ComboBoxEntry aNewEntry(BuildEntry(nLang));
+ if (aNewEntry.sString.isEmpty())
+ continue;
+ if (std::find_if(rEntries.begin(), rEntries.end(),
+ [=](const weld::ComboBoxEntry& rEntry){ return rEntry.sId == aNewEntry.sId; }) != rEntries.end())
+ continue;
+ rEntries.push_back(aNewEntry);
+ }
+ }
+ }
+}
+
+void SvxLanguageBox::SetLanguageList(SvxLanguageListFlags nLangList, bool bHasLangNone,
+ bool bLangNoneIsLangAll, bool bCheckSpellAvail,
+ bool bDefaultLangExist, LanguageType eDefaultLangType,
+ sal_Int16 nDefaultType)
+{
+ m_bHasLangNone = bHasLangNone;
+ m_bLangNoneIsLangAll = bLangNoneIsLangAll;
+ m_bWithCheckmark = bCheckSpellAvail;
+
+ if (SvxLanguageListFlags::EMPTY == nLangList)
+ {
+ m_xControl->clear();
+ return;
+ }
+
+ bool bAddAvailable = (!(nLangList & SvxLanguageListFlags::ONLY_KNOWN) &&
+ ((nLangList & SvxLanguageListFlags::ALL) ||
+ (nLangList & SvxLanguageListFlags::WESTERN) ||
+ (nLangList & SvxLanguageListFlags::CTL) ||
+ (nLangList & SvxLanguageListFlags::CJK)));
+ std::vector< LanguageType > aSpellAvailLang;
+ std::vector< LanguageType > aHyphAvailLang;
+ std::vector< LanguageType > aThesAvailLang;
+ Sequence< sal_Int16 > aSpellUsedLang;
+ Reference< XAvailableLocales > xAvail( LinguMgr::GetLngSvcMgr() );
+ if (xAvail.is())
+ {
+ Sequence< css::lang::Locale > aTmp;
+
+ if (bAddAvailable)
+ {
+ aTmp = xAvail->getAvailableLocales( SN_SPELLCHECKER );
+ aSpellAvailLang = lcl_LocaleSeqToLangSeq( aTmp );
+
+ aTmp = xAvail->getAvailableLocales( SN_HYPHENATOR );
+ aHyphAvailLang = lcl_LocaleSeqToLangSeq( aTmp );
+
+ aTmp = xAvail->getAvailableLocales( SN_THESAURUS );
+ aThesAvailLang = lcl_LocaleSeqToLangSeq( aTmp );
+ }
+ }
+ if (SvxLanguageListFlags::SPELL_USED & nLangList)
+ {
+ Reference< XSpellChecker1 > xTmp1 = LinguMgr::GetSpellChecker();
+ if (xTmp1.is())
+ aSpellUsedLang = xTmp1->getLanguages();
+ }
+
+ std::vector<LanguageType> aKnown;
+ sal_uInt32 nCount;
+ if ( nLangList & SvxLanguageListFlags::ONLY_KNOWN )
+ {
+ aKnown = LocaleDataWrapper::getInstalledLanguageTypes();
+ nCount = aKnown.size();
+ }
+ else
+ {
+ nCount = SvtLanguageTable::GetLanguageEntryCount();
+ }
+
+ std::vector<weld::ComboBoxEntry> aEntries;
+ for ( sal_uInt32 i = 0; i < nCount; i++ )
+ {
+ LanguageType nLangType;
+ if ( nLangList & SvxLanguageListFlags::ONLY_KNOWN )
+ nLangType = aKnown[i];
+ else
+ nLangType = SvtLanguageTable::GetLanguageTypeAtIndex( i );
+ if ( lcl_isPrerequisite( nLangType ) &&
+ (lcl_isScriptTypeRequested( nLangType, nLangList) ||
+ (bool(nLangList & SvxLanguageListFlags::FBD_CHARS) &&
+ MsLangId::hasForbiddenCharacters(nLangType)) ||
+ (bool(nLangList & SvxLanguageListFlags::SPELL_USED) &&
+ lcl_SeqHasLang(aSpellUsedLang, static_cast<sal_uInt16>(nLangType)))
+ ) )
+ {
+ aEntries.push_back(BuildEntry(nLangType));
+ if (aEntries.back().sString.isEmpty())
+ aEntries.pop_back();
+ }
+ }
+
+ if (bAddAvailable)
+ {
+ // Spell checkers, hyphenators and thesauri may add language tags
+ // unknown so far.
+ AddLanguages(aSpellAvailLang, nLangList, aEntries);
+ AddLanguages(aHyphAvailLang, nLangList, aEntries);
+ AddLanguages(aThesAvailLang, nLangList, aEntries);
+ }
+
+ std::sort(aEntries.begin(), aEntries.end(),
+ [](const weld::ComboBoxEntry e1, const weld::ComboBoxEntry e2) {
+ static const auto aSorter = comphelper::string::NaturalStringSorter(
+ ::comphelper::getProcessComponentContext(),
+ Application::GetSettings().GetLanguageTag().getLocale());
+ return aSorter.compare(e1.sString, e2.sString) < 0;
+ });
+
+ int nSeparatorPosition = 0;
+ if (bDefaultLangExist)
+ {
+ aEntries.insert(aEntries.begin(), BuildEntry(eDefaultLangType, nDefaultType));
+ nSeparatorPosition++;
+ }
+
+ if (bHasLangNone)
+ {
+ aEntries.insert(aEntries.begin(), BuildEntry(LANGUAGE_NONE));
+ nSeparatorPosition++;
+ }
+
+ m_xControl->insert_vector(aEntries, false);
+ if (nSeparatorPosition > 0)
+ m_xControl->insert_separator(nSeparatorPosition, "");
+}
+
+int SvxLanguageBox::ImplTypeToPos(LanguageType eType) const
+{
+ return m_xControl->find_id(OUString::number(static_cast<sal_uInt16>(eType)));
+}
+
+void SvxLanguageBox::InsertLanguage(const LanguageType nLangType, sal_Int16 nType)
+{
+ weld::ComboBoxEntry aEntry = BuildEntry(nLangType, nType);
+ if (aEntry.sString.isEmpty())
+ return;
+ if (aEntry.sImage.isEmpty())
+ m_xControl->append(aEntry.sId, aEntry.sString);
+ else
+ m_xControl->append(aEntry.sId, aEntry.sString, aEntry.sImage);
+}
+
+void SvxLanguageBox::InsertLanguage(const LanguageType nLangType)
+{
+ InsertLanguage(nLangType, css::i18n::ScriptType::WEAK);
+}
+
+weld::ComboBoxEntry SvxLanguageBox::BuildEntry(const LanguageType nLangType, sal_Int16 nType)
+{
+ LanguageType nLang = MsLangId::getReplacementForObsoleteLanguage(nLangType);
+ // For obsolete and to be replaced languages check whether an entry of the
+ // replacement already exists and if so don't add an entry with identical
+ // string as would be returned by SvtLanguageTable::GetString().
+ if (nLang != nLangType)
+ {
+ int nAt = ImplTypeToPos( nLang );
+ if (nAt != -1)
+ return weld::ComboBoxEntry("");
+ }
+
+ OUString aStrEntry = SvtLanguageTable::GetLanguageString( nLang );
+ if (LANGUAGE_NONE == nLang && m_bHasLangNone && m_bLangNoneIsLangAll)
+ aStrEntry = m_aAllString;
+
+ LanguageType nRealLang = nLang;
+ if (nRealLang == LANGUAGE_SYSTEM)
+ {
+ nRealLang = MsLangId::resolveSystemLanguageByScriptType(nRealLang, nType);
+ aStrEntry += " - " + SvtLanguageTable::GetLanguageString( nRealLang );
+ }
+ else if (nRealLang == LANGUAGE_USER_SYSTEM_CONFIG)
+ {
+ nRealLang = MsLangId::getSystemLanguage();
+ // Whatever we obtained, ensure a known supported locale.
+ nRealLang = LanguageTag(nRealLang).makeFallback().getLanguageType();
+ aStrEntry += " - " + SvtLanguageTable::GetLanguageString( nRealLang );
+ }
+
+ if (m_bWithCheckmark)
+ {
+ if (!m_xSpellUsedLang)
+ {
+ Reference<XSpellChecker1> xSpell = LinguMgr::GetSpellChecker();
+ if (xSpell.is())
+ m_xSpellUsedLang.reset(new Sequence<sal_Int16>(xSpell->getLanguages()));
+ }
+
+ bool bFound = m_xSpellUsedLang && lcl_SeqHasLang(*m_xSpellUsedLang, static_cast<sal_uInt16>(nRealLang));
+
+ return weld::ComboBoxEntry(aStrEntry, OUString::number(static_cast<sal_uInt16>(nLangType)), bFound ? OUString(RID_SVXBMP_CHECKED) : OUString(RID_SVXBMP_NOTCHECKED));
+ }
+ else
+ return weld::ComboBoxEntry(aStrEntry, OUString::number(static_cast<sal_uInt16>(nLangType)));
+}
+
+IMPL_LINK(SvxLanguageBox, ChangeHdl, weld::ComboBox&, rControl, void)
+{
+ if (rControl.has_entry())
+ {
+ EditedAndValid eOldState = m_eEditedAndValid;
+ OUString aStr(rControl.get_active_text());
+ if (aStr.isEmpty())
+ m_eEditedAndValid = EditedAndValid::Invalid;
+ else
+ {
+ const int nPos = rControl.find_text(aStr);
+ if (nPos != -1)
+ {
+ int nStartSelectPos, nEndSelectPos;
+ rControl.get_entry_selection_bounds(nStartSelectPos, nEndSelectPos);
+
+ // Select the corresponding listbox entry if not current. This
+ // invalidates the Edit Selection thus has to happen between
+ // obtaining the Selection and setting the new Selection.
+ int nSelPos = m_xControl->get_active();
+ bool bSetEditSelection;
+ if (nSelPos == nPos)
+ bSetEditSelection = false;
+ else
+ {
+ m_xControl->set_active(nPos);
+ bSetEditSelection = true;
+ }
+
+ // If typing into the Edit control led us here, advance start of a
+ // full selection by one so the next character will already
+ // continue the string instead of having to type the same character
+ // again to start a new string. The selection is in reverse
+ // when obtained from the Edit control.
+ if (nEndSelectPos == 0)
+ {
+ OUString aText(m_xControl->get_active_text());
+ if (nStartSelectPos == aText.getLength())
+ {
+ ++nEndSelectPos;
+ bSetEditSelection = true;
+ }
+ }
+
+ if (bSetEditSelection)
+ rControl.select_entry_region(nStartSelectPos, nEndSelectPos);
+
+ m_eEditedAndValid = EditedAndValid::No;
+ }
+ else
+ {
+ OUString aCanonicalized;
+ bool bValid = LanguageTag::isValidBcp47( aStr, &aCanonicalized, true);
+ m_eEditedAndValid = (bValid ? EditedAndValid::Valid : EditedAndValid::Invalid);
+ if (bValid && aCanonicalized != aStr)
+ {
+ m_xControl->set_entry_text(aCanonicalized);
+ const auto nCursorPos = aCanonicalized.getLength();
+ m_xControl->select_entry_region(nCursorPos, nCursorPos);
+ }
+ }
+ }
+ if (eOldState != m_eEditedAndValid)
+ {
+ if (m_eEditedAndValid == EditedAndValid::Invalid)
+ rControl.set_entry_message_type(weld::EntryMessageType::Error);
+ else
+ rControl.set_entry_message_type(weld::EntryMessageType::Normal);
+ }
+ }
+ m_aChangeHdl.Call(rControl);
+}
+
+SvxLanguageBox::SvxLanguageBox(std::unique_ptr<weld::ComboBox> pControl)
+ : m_xControl(std::move(pControl))
+ , m_aAllString(SvxResId(RID_SVXSTR_LANGUAGE_ALL))
+ , m_eSavedLanguage(LANGUAGE_DONTKNOW)
+ , m_eEditedAndValid(EditedAndValid::No)
+ , m_bHasLangNone(false)
+ , m_bLangNoneIsLangAll(false)
+ , m_bWithCheckmark(false)
+{
+ m_xControl->connect_changed(LINK(this, SvxLanguageBox, ChangeHdl));
+}
+
+sal_Int32 SvxLanguageBox::SaveEditedAsEntry()
+{
+ if (m_eEditedAndValid != EditedAndValid::Valid)
+ return -1;
+
+ LanguageTag aLanguageTag(m_xControl->get_active_text());
+ LanguageType nLang = aLanguageTag.getLanguageType();
+ if (nLang == LANGUAGE_DONTKNOW)
+ {
+ SAL_WARN( "svx.dialog", "SvxLanguageComboBox::SaveEditedAsEntry: unknown tag");
+ return -1;
+ }
+
+ int nPos = ImplTypeToPos( nLang);
+ if (nPos != -1)
+ return nPos; // Already present but with a different string.
+
+ if (SvtLanguageTable::HasLanguageType( nLang))
+ {
+ // In SvtLanguageTable but not in SvxLanguageComboBox. On purpose? This
+ // may be an entry with different settings or CTL instead of Western or
+ // ... all things we don't handle yet.
+ SAL_WARN( "svx.dialog", "SvxLanguageComboBox::SaveEditedAsEntry: already in SvtLanguageTable: " <<
+ SvtLanguageTable::GetLanguageString( nLang) << ", " << nLang);
+ }
+ else
+ {
+ // Add to both, SvtLanguageTable and SvxLanguageComboBox.
+ /* TODO: a descriptive user comment would be a nice to have here. */
+ SvtLanguageTable::AddLanguageTag( aLanguageTag );
+ }
+
+ InsertLanguage(nLang);
+ return ImplTypeToPos(nLang);
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/dialog/linkwarn.cxx b/svx/source/dialog/linkwarn.cxx
new file mode 100644
index 000000000..852c92af1
--- /dev/null
+++ b/svx/source/dialog/linkwarn.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 <osl/file.hxx>
+#include <svx/linkwarn.hxx>
+#include <officecfg/Office/Common.hxx>
+
+SvxLinkWarningDialog::SvxLinkWarningDialog(weld::Widget* pParent, const OUString& _rFileName)
+ : MessageDialogController(pParent, "svx/ui/linkwarndialog.ui", "LinkWarnDialog", "ask")
+ , m_xWarningOnBox(m_xBuilder->weld_check_button("ask"))
+{
+ // replace filename
+ OUString sInfoText = m_xDialog->get_primary_text();
+ OUString aPath;
+ if (osl::FileBase::E_None != osl::FileBase::getSystemPathFromFileURL(_rFileName, aPath))
+ aPath = _rFileName;
+ sInfoText = sInfoText.replaceAll("%FILENAME", aPath);
+ m_xDialog->set_primary_text(sInfoText);
+
+ // load state of "warning on" checkbox from misc options
+ m_xWarningOnBox->set_active(officecfg::Office::Common::Misc::ShowLinkWarningDialog::get());
+ m_xWarningOnBox->set_sensitive(
+ !officecfg::Office::Common::Misc::ShowLinkWarningDialog::isReadOnly());
+}
+
+SvxLinkWarningDialog::~SvxLinkWarningDialog()
+{
+ try
+ {
+ // save value of "warning off" checkbox, if necessary
+ bool bChecked = m_xWarningOnBox->get_active();
+ if (officecfg::Office::Common::Misc::ShowLinkWarningDialog::get() != bChecked)
+ {
+ std::shared_ptr<comphelper::ConfigurationChanges> xChanges(
+ comphelper::ConfigurationChanges::create());
+ officecfg::Office::Common::Misc::ShowLinkWarningDialog::set(bChecked, xChanges);
+ xChanges->commit();
+ }
+ }
+ catch (...)
+ {
+ }
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/dialog/measctrl.cxx b/svx/source/dialog/measctrl.cxx
new file mode 100644
index 000000000..ac1303db2
--- /dev/null
+++ b/svx/source/dialog/measctrl.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 <sfx2/dialoghelper.hxx>
+#include <svx/svdomeas.hxx>
+#include <svx/svdmodel.hxx>
+#include <svx/measctrl.hxx>
+#include <svx/dlgutil.hxx>
+#include <vcl/event.hxx>
+#include <vcl/settings.hxx>
+#include <vcl/svapp.hxx>
+#include <memory>
+
+SvxXMeasurePreview::SvxXMeasurePreview()
+ : m_aMapMode(MapUnit::Map100thMM)
+{
+ // Scale: 1:2
+ m_aMapMode.SetScaleX(Fraction(1, 2));
+ m_aMapMode.SetScaleY(Fraction(1, 2));
+}
+
+void SvxXMeasurePreview::SetDrawingArea(weld::DrawingArea* pDrawingArea)
+{
+ CustomWidgetController::SetDrawingArea(pDrawingArea);
+ Size aSize(getPreviewStripSize(pDrawingArea->get_ref_device()));
+ pDrawingArea->set_size_request(aSize.Width(), aSize.Height());
+
+ pModel.reset(new SdrModel(nullptr, nullptr, true));
+ pMeasureObj.reset(new SdrMeasureObj(*pModel, Point(), Point()));
+
+ ResizeImpl(aSize);
+ Invalidate();
+}
+
+void SvxXMeasurePreview::ResizeImpl(const Size& rSize)
+{
+ OutputDevice& rRefDevice = GetDrawingArea()->get_ref_device();
+ rRefDevice.Push(vcl::PushFlags::MAPMODE);
+
+ rRefDevice.SetMapMode(m_aMapMode);
+
+ Size aSize = rRefDevice.PixelToLogic(rSize);
+ Point aPt1(aSize.Width() / 5, static_cast<tools::Long>(aSize.Height() / 2));
+ pMeasureObj->SetPoint(aPt1, 0);
+ Point aPt2(aSize.Width() * 4 / 5, static_cast<tools::Long>(aSize.Height() / 2));
+ pMeasureObj->SetPoint(aPt2, 1);
+
+ rRefDevice.Pop();
+}
+
+void SvxXMeasurePreview::Resize()
+{
+ CustomWidgetController::Resize();
+ ResizeImpl(GetOutputSizePixel());
+ Invalidate();
+}
+
+SvxXMeasurePreview::~SvxXMeasurePreview() {}
+
+void SvxXMeasurePreview::Paint(vcl::RenderContext& rRenderContext, const tools::Rectangle&)
+{
+ rRenderContext.SetBackground(rRenderContext.GetSettings().GetStyleSettings().GetWindowColor());
+ rRenderContext.Erase();
+
+ rRenderContext.Push(vcl::PushFlags::MAPMODE);
+ rRenderContext.SetMapMode(m_aMapMode);
+
+ bool bHighContrast = Application::GetSettings().GetStyleSettings().GetHighContrastMode();
+ rRenderContext.SetDrawMode(bHighContrast ? OUTPUT_DRAWMODE_CONTRAST : OUTPUT_DRAWMODE_COLOR);
+ pMeasureObj->SingleObjectPainter(rRenderContext);
+
+ rRenderContext.Pop();
+}
+
+void SvxXMeasurePreview::SetAttributes(const SfxItemSet& rInAttrs)
+{
+ pMeasureObj->SetMergedItemSetAndBroadcast(rInAttrs);
+
+ Invalidate();
+}
+
+bool SvxXMeasurePreview::MouseButtonDown(const MouseEvent& rMEvt)
+{
+ bool bZoomIn = rMEvt.IsLeft() && !rMEvt.IsShift();
+ bool bZoomOut = rMEvt.IsRight() || rMEvt.IsShift();
+ bool bCtrl = rMEvt.IsMod1();
+
+ if (bZoomIn || bZoomOut)
+ {
+ Fraction aXFrac = m_aMapMode.GetScaleX();
+ Fraction aYFrac = m_aMapMode.GetScaleY();
+ std::unique_ptr<Fraction> pMultFrac;
+
+ if (bZoomIn)
+ {
+ if (bCtrl)
+ pMultFrac.reset(new Fraction(3, 2));
+ else
+ pMultFrac.reset(new Fraction(11, 10));
+ }
+ else
+ {
+ if (bCtrl)
+ pMultFrac.reset(new Fraction(2, 3));
+ else
+ pMultFrac.reset(new Fraction(10, 11));
+ }
+
+ aXFrac *= *pMultFrac;
+ aYFrac *= *pMultFrac;
+
+ if (double(aXFrac) > 0.001 && double(aXFrac) < 1000.0 && double(aYFrac) > 0.001
+ && double(aYFrac) < 1000.0)
+ {
+ m_aMapMode.SetScaleX(aXFrac);
+ m_aMapMode.SetScaleY(aYFrac);
+
+ OutputDevice& rRefDevice = GetDrawingArea()->get_ref_device();
+ rRefDevice.Push(vcl::PushFlags::MAPMODE);
+ rRefDevice.SetMapMode(m_aMapMode);
+ Size aOutSize(rRefDevice.PixelToLogic(GetOutputSizePixel()));
+ rRefDevice.Pop();
+
+ Point aPt(m_aMapMode.GetOrigin());
+ tools::Long nX = tools::Long(
+ (double(aOutSize.Width()) - (double(aOutSize.Width()) * double(*pMultFrac))) / 2.0
+ + 0.5);
+ tools::Long nY = tools::Long(
+ (double(aOutSize.Height()) - (double(aOutSize.Height()) * double(*pMultFrac))) / 2.0
+ + 0.5);
+ aPt.AdjustX(nX);
+ aPt.AdjustY(nY);
+
+ m_aMapMode.SetOrigin(aPt);
+
+ Invalidate();
+ }
+ }
+
+ return true;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/dialog/optgrid.cxx b/svx/source/dialog/optgrid.cxx
new file mode 100644
index 000000000..3cd12b48d
--- /dev/null
+++ b/svx/source/dialog/optgrid.cxx
@@ -0,0 +1,281 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * 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/intitem.hxx>
+#include <svtools/unitconv.hxx>
+
+#include <svx/svxids.hrc>
+#include <svx/optgrid.hxx>
+#include <svx/dlgutil.hxx>
+
+// local functions
+static void lcl_GetMinMax(weld::MetricSpinButton const& rField, sal_Int64& nMin, sal_Int64& nMax)
+{
+ rField.get_range(nMin, nMax, FieldUnit::TWIP);
+ nMin = rField.denormalize(nMin);
+ nMax = rField.denormalize(nMax);
+}
+
+static void lcl_SetMinMax(weld::MetricSpinButton& rField, sal_Int64 nMin, sal_Int64 nMax)
+{
+ rField.set_range(rField.normalize(nMin), rField.normalize(nMax), FieldUnit::TWIP);
+}
+
+SvxOptionsGrid::SvxOptionsGrid() :
+ nFldDrawX ( 100 ),
+ nFldDivisionX ( 0 ),
+ nFldDrawY ( 100 ),
+ nFldDivisionY ( 0 ),
+ nFldSnapX ( 100 ),
+ nFldSnapY ( 100 ),
+ bUseGridsnap ( false ),
+ bSynchronize ( true ),
+ bGridVisible ( false ),
+ bEqualGrid ( true )
+{
+}
+
+SvxGridItem* SvxGridItem::Clone( SfxItemPool* ) const
+{
+ return new SvxGridItem( *this );
+}
+
+bool SvxGridItem::operator==( const SfxPoolItem& rAttr ) const
+{
+ assert(SfxPoolItem::operator==(rAttr));
+
+ const SvxGridItem& rItem = static_cast<const SvxGridItem&>(rAttr);
+
+ return ( bUseGridsnap == rItem.bUseGridsnap &&
+ bSynchronize == rItem.bSynchronize &&
+ bGridVisible == rItem.bGridVisible &&
+ bEqualGrid == rItem.bEqualGrid &&
+ nFldDrawX == rItem.nFldDrawX &&
+ nFldDivisionX== rItem.nFldDivisionX&&
+ nFldDrawY == rItem.nFldDrawY &&
+ nFldDivisionY== rItem.nFldDivisionY&&
+ nFldSnapX == rItem.nFldSnapX &&
+ nFldSnapY == rItem.nFldSnapY );
+}
+
+bool SvxGridItem::GetPresentation
+(
+ SfxItemPresentation /*ePres*/,
+ MapUnit /*eCoreUnit*/,
+ MapUnit /*ePresUnit*/,
+ OUString& rText, const IntlWrapper&
+) const
+{
+ rText = "SvxGridItem";
+ return true;
+}
+
+// TabPage Screen Settings
+SvxGridTabPage::SvxGridTabPage(weld::Container* pPage, weld::DialogController* pController, const SfxItemSet& rCoreSet)
+ : SfxTabPage(pPage, pController, "svx/ui/optgridpage.ui", "OptGridPage", &rCoreSet)
+ , bAttrModified(false)
+ , m_xCbxUseGridsnap(m_xBuilder->weld_check_button("usegridsnap"))
+ , m_xCbxGridVisible(m_xBuilder->weld_check_button("gridvisible"))
+ , m_xMtrFldDrawX(m_xBuilder->weld_metric_spin_button("mtrflddrawx", FieldUnit::CM))
+ , m_xMtrFldDrawY(m_xBuilder->weld_metric_spin_button("mtrflddrawy", FieldUnit::CM))
+ , m_xNumFldDivisionX(m_xBuilder->weld_spin_button("numflddivisionx"))
+ , m_xNumFldDivisionY(m_xBuilder->weld_spin_button("numflddivisiony"))
+ , m_xCbxSynchronize(m_xBuilder->weld_check_button("synchronize"))
+ , m_xSnapFrames(m_xBuilder->weld_widget("snapframes"))
+ , m_xCbxSnapHelplines(m_xBuilder->weld_check_button("snaphelplines"))
+ , m_xCbxSnapBorder(m_xBuilder->weld_check_button("snapborder"))
+ , m_xCbxSnapFrame(m_xBuilder->weld_check_button("snapframe"))
+ , m_xCbxSnapPoints(m_xBuilder->weld_check_button("snappoints"))
+ , m_xMtrFldSnapArea(m_xBuilder->weld_metric_spin_button("mtrfldsnaparea", FieldUnit::PIXEL))
+ , m_xCbxOrtho(m_xBuilder->weld_check_button("ortho"))
+ , m_xCbxBigOrtho(m_xBuilder->weld_check_button("bigortho"))
+ , m_xCbxRotate(m_xBuilder->weld_check_button("rotate"))
+ , m_xMtrFldAngle(m_xBuilder->weld_metric_spin_button("mtrfldangle", FieldUnit::DEGREE))
+ , m_xMtrFldBezAngle(m_xBuilder->weld_metric_spin_button("mtrfldbezangle", FieldUnit::DEGREE))
+{
+ // This page requires exchange Support
+ SetExchangeSupport();
+
+ // Set Metrics
+ FieldUnit eFUnit = GetModuleFieldUnit( rCoreSet );
+ sal_Int64 nMin, nMax;
+
+ lcl_GetMinMax(*m_xMtrFldDrawX, nMin, nMax);
+ SetFieldUnit( *m_xMtrFldDrawX, eFUnit, true );
+ lcl_SetMinMax(*m_xMtrFldDrawX, nMin, nMax);
+
+ lcl_GetMinMax(*m_xMtrFldDrawY, nMin, nMax);
+ SetFieldUnit( *m_xMtrFldDrawY, eFUnit, true );
+ lcl_SetMinMax(*m_xMtrFldDrawY, nMin, nMax);
+
+
+ m_xCbxRotate->connect_toggled(LINK(this, SvxGridTabPage, ClickRotateHdl_Impl));
+ Link<weld::Toggleable&,void> aLink = LINK(this, SvxGridTabPage, ChangeGridsnapHdl_Impl);
+ m_xCbxUseGridsnap->connect_toggled(aLink);
+ m_xCbxSynchronize->connect_toggled(aLink);
+ m_xCbxGridVisible->connect_toggled(aLink);
+ m_xMtrFldDrawX->connect_value_changed(
+ LINK( this, SvxGridTabPage, ChangeDrawHdl_Impl ) );
+ m_xMtrFldDrawY->connect_value_changed(
+ LINK( this, SvxGridTabPage, ChangeDrawHdl_Impl ) );
+ m_xNumFldDivisionX->connect_value_changed(
+ LINK( this, SvxGridTabPage, ChangeDivisionHdl_Impl ) );
+ m_xNumFldDivisionY->connect_value_changed(
+ LINK( this, SvxGridTabPage, ChangeDivisionHdl_Impl ) );
+}
+
+SvxGridTabPage::~SvxGridTabPage()
+{
+}
+
+std::unique_ptr<SfxTabPage> SvxGridTabPage::Create(weld::Container* pPage, weld::DialogController* pController, const SfxItemSet& rAttrSet)
+{
+ return std::make_unique<SvxGridTabPage>(pPage, pController, rAttrSet);
+}
+
+bool SvxGridTabPage::FillItemSet( SfxItemSet* rCoreSet )
+{
+ if ( bAttrModified )
+ {
+ SvxGridItem aGridItem( SID_ATTR_GRID_OPTIONS );
+
+ aGridItem.bUseGridsnap = m_xCbxUseGridsnap->get_active();
+ aGridItem.bSynchronize = m_xCbxSynchronize->get_active();
+ aGridItem.bGridVisible = m_xCbxGridVisible->get_active();
+
+ MapUnit eUnit = rCoreSet->GetPool()->GetMetric( SID_ATTR_GRID_OPTIONS );
+ tools::Long nX = GetCoreValue( *m_xMtrFldDrawX, eUnit );
+ tools::Long nY = GetCoreValue( *m_xMtrFldDrawY, eUnit );
+
+ aGridItem.nFldDrawX = static_cast<sal_uInt32>(nX);
+ aGridItem.nFldDrawY = static_cast<sal_uInt32>(nY);
+ aGridItem.nFldDivisionX = static_cast<tools::Long>(m_xNumFldDivisionX->get_value() - 1);
+ aGridItem.nFldDivisionY = static_cast<tools::Long>(m_xNumFldDivisionY->get_value() - 1);
+
+ rCoreSet->Put( aGridItem );
+ }
+ return bAttrModified;
+}
+
+void SvxGridTabPage::Reset( const SfxItemSet* rSet )
+{
+ const SvxGridItem* pGridAttr = nullptr;
+
+ if( (pGridAttr = rSet->GetItemIfSet( SID_ATTR_GRID_OPTIONS , false )) )
+ {
+ m_xCbxUseGridsnap->set_active(pGridAttr->bUseGridsnap);
+ m_xCbxSynchronize->set_active(pGridAttr->bSynchronize);
+ m_xCbxGridVisible->set_active(pGridAttr->bGridVisible);
+
+ MapUnit eUnit = rSet->GetPool()->GetMetric( SID_ATTR_GRID_OPTIONS );
+ SetMetricValue( *m_xMtrFldDrawX , pGridAttr->nFldDrawX, eUnit );
+ SetMetricValue( *m_xMtrFldDrawY , pGridAttr->nFldDrawY, eUnit );
+
+ m_xNumFldDivisionX->set_value(pGridAttr->nFldDivisionX + 1);
+ m_xNumFldDivisionY->set_value(pGridAttr->nFldDivisionY + 1);
+ }
+
+ ChangeGridsnapHdl_Impl(*m_xCbxUseGridsnap);
+ bAttrModified = false;
+}
+
+void SvxGridTabPage::ActivatePage( const SfxItemSet& rSet )
+{
+ const SvxGridItem* pGridAttr = nullptr;
+ if( (pGridAttr = rSet.GetItemIfSet( SID_ATTR_GRID_OPTIONS , false )) )
+ {
+ m_xCbxUseGridsnap->set_active(pGridAttr->bUseGridsnap);
+
+ ChangeGridsnapHdl_Impl(*m_xCbxUseGridsnap);
+ }
+
+ // Metric Change if necessary (as TabPage is in the dialog, where the
+ // metric can be set
+ const SfxUInt16Item* pItem = rSet.GetItemIfSet( SID_ATTR_METRIC , false );
+ if( !pItem )
+ return;
+
+
+ FieldUnit eFUnit = static_cast<FieldUnit>(static_cast<tools::Long>(pItem->GetValue()));
+
+ if (eFUnit == m_xMtrFldDrawX->get_unit())
+ return;
+
+ // Set Metrics
+ sal_Int64 nMin, nMax;
+ int nVal = m_xMtrFldDrawX->denormalize(m_xMtrFldDrawX->get_value(FieldUnit::TWIP));
+
+ lcl_GetMinMax(*m_xMtrFldDrawX, nMin, nMax);
+ SetFieldUnit(*m_xMtrFldDrawX, eFUnit, true);
+ lcl_SetMinMax(*m_xMtrFldDrawX, nMin, nMax);
+
+ m_xMtrFldDrawX->set_value(m_xMtrFldDrawX->normalize(nVal), FieldUnit::TWIP);
+
+ nVal = m_xMtrFldDrawY->denormalize(m_xMtrFldDrawY->get_value(FieldUnit::TWIP));
+ lcl_GetMinMax(*m_xMtrFldDrawY, nMin, nMax);
+ SetFieldUnit(*m_xMtrFldDrawY, eFUnit, true);
+ lcl_SetMinMax(*m_xMtrFldDrawY, nMin, nMax);
+ m_xMtrFldDrawY->set_value(m_xMtrFldDrawY->normalize(nVal), FieldUnit::TWIP);
+}
+
+DeactivateRC SvxGridTabPage::DeactivatePage( SfxItemSet* _pSet )
+{
+ if ( _pSet )
+ FillItemSet( _pSet );
+ return DeactivateRC::LeavePage;
+}
+
+IMPL_LINK(SvxGridTabPage, ChangeDrawHdl_Impl, weld::MetricSpinButton&, rField, void)
+{
+ bAttrModified = true;
+ if (m_xCbxSynchronize->get_active())
+ {
+ if (&rField == m_xMtrFldDrawX.get())
+ m_xMtrFldDrawY->set_value(m_xMtrFldDrawX->get_value(FieldUnit::NONE), FieldUnit::NONE);
+ else
+ m_xMtrFldDrawX->set_value(m_xMtrFldDrawY->get_value(FieldUnit::NONE), FieldUnit::NONE);
+ }
+}
+
+IMPL_LINK_NOARG(SvxGridTabPage, ClickRotateHdl_Impl, weld::Toggleable&, void)
+{
+ if (m_xCbxRotate->get_active())
+ m_xMtrFldAngle->set_sensitive(true);
+ else
+ m_xMtrFldAngle->set_sensitive(false);
+}
+
+IMPL_LINK(SvxGridTabPage, ChangeDivisionHdl_Impl, weld::SpinButton&, rField, void)
+{
+ bAttrModified = true;
+ if (m_xCbxSynchronize->get_active())
+ {
+ if (m_xNumFldDivisionX.get() == &rField)
+ m_xNumFldDivisionY->set_value(m_xNumFldDivisionX->get_value());
+ else
+ m_xNumFldDivisionX->set_value(m_xNumFldDivisionY->get_value());
+ }
+}
+
+IMPL_LINK_NOARG(SvxGridTabPage, ChangeGridsnapHdl_Impl, weld::Toggleable&, void)
+{
+ bAttrModified = true;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/dialog/page.hrc b/svx/source/dialog/page.hrc
new file mode 100644
index 000000000..52e8a0dab
--- /dev/null
+++ b/svx/source/dialog/page.hrc
@@ -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_SVX_SOURCE_DIALOG_PAGE_HRC
+#define INCLUDED_SVX_SOURCE_DIALOG_PAGE_HRC
+
+#include <i18nutil/paper.hxx>
+#include <unotools/resmgr.hxx>
+#include <utility>
+
+#define NC_(Context, String) TranslateId(Context, reinterpret_cast<char const *>(u8##String))
+
+const std::pair<TranslateId, int> RID_SVXSTRARY_PAPERSIZE_STD[] =
+{
+ { NC_("RID_SVXSTRARY_PAPERSIZE_STD", "A6") , PAPER_A6 },
+ { NC_("RID_SVXSTRARY_PAPERSIZE_STD", "A5") , PAPER_A5 },
+ { NC_("RID_SVXSTRARY_PAPERSIZE_STD", "A4") , PAPER_A4 },
+ { NC_("RID_SVXSTRARY_PAPERSIZE_STD", "A3") , PAPER_A3 },
+ { NC_("RID_SVXSTRARY_PAPERSIZE_STD", "B6 (ISO)") , PAPER_B6_ISO },
+ { NC_("RID_SVXSTRARY_PAPERSIZE_STD", "B5 (ISO)") , PAPER_B5_ISO },
+ { NC_("RID_SVXSTRARY_PAPERSIZE_STD", "B4 (ISO)") , PAPER_B4_ISO },
+ { NC_("RID_SVXSTRARY_PAPERSIZE_STD", "Letter") , PAPER_LETTER },
+ { NC_("RID_SVXSTRARY_PAPERSIZE_STD", "Legal") , PAPER_LEGAL },
+ { NC_("RID_SVXSTRARY_PAPERSIZE_STD", "Long Bond") , PAPER_FANFOLD_LEGAL_DE },
+ { NC_("RID_SVXSTRARY_PAPERSIZE_STD", "Tabloid") , PAPER_TABLOID },
+ { NC_("RID_SVXSTRARY_PAPERSIZE_STD", "B6 (JIS)") , PAPER_B6_JIS },
+ { NC_("RID_SVXSTRARY_PAPERSIZE_STD", "B5 (JIS)") , PAPER_B5_JIS },
+ { NC_("RID_SVXSTRARY_PAPERSIZE_STD", "B4 (JIS)") , PAPER_B4_JIS },
+ { NC_("RID_SVXSTRARY_PAPERSIZE_STD", "16 Kai") , PAPER_KAI16},
+ { NC_("RID_SVXSTRARY_PAPERSIZE_STD", "32 Kai") , PAPER_KAI32},
+ { NC_("RID_SVXSTRARY_PAPERSIZE_STD", "Big 32 Kai") , PAPER_KAI32BIG},
+ { NC_("RID_SVXSTRARY_PAPERSIZE_STD", "User") , PAPER_USER },
+ { NC_("RID_SVXSTRARY_PAPERSIZE_STD", "DL Envelope") , PAPER_ENV_DL },
+ { NC_("RID_SVXSTRARY_PAPERSIZE_STD", "C6 Envelope") , PAPER_ENV_C6 },
+ { NC_("RID_SVXSTRARY_PAPERSIZE_STD", "C6/5 Envelope") , PAPER_ENV_C65 },
+ { NC_("RID_SVXSTRARY_PAPERSIZE_STD", "C5 Envelope") , PAPER_ENV_C5 },
+ { NC_("RID_SVXSTRARY_PAPERSIZE_STD", "C4 Envelope") , PAPER_ENV_C4 },
+ { NC_("RID_SVXSTRARY_PAPERSIZE_STD", "#6¾ Envelope") , PAPER_ENV_PERSONAL},
+ { NC_("RID_SVXSTRARY_PAPERSIZE_STD", "#7¾ (Monarch) Envelope") , PAPER_ENV_MONARCH},
+ { NC_("RID_SVXSTRARY_PAPERSIZE_STD", "#9 Envelope") , PAPER_ENV_9},
+ { NC_("RID_SVXSTRARY_PAPERSIZE_STD", "#10 Envelope") , PAPER_ENV_10},
+ { NC_("RID_SVXSTRARY_PAPERSIZE_STD", "#11 Envelope") , PAPER_ENV_11},
+ { NC_("RID_SVXSTRARY_PAPERSIZE_STD", "#12 Envelope") , PAPER_ENV_12},
+ { NC_("RID_SVXSTRARY_PAPERSIZE_STD", "Japanese Postcard") , PAPER_POSTCARD_JP}
+};
+
+const std::pair<TranslateId, int> RID_SVXSTRARY_PAPERSIZE_DRAW[] =
+{
+ { NC_("RID_SVXSTRARY_PAPERSIZE_DRAW", "A6") , PAPER_A6 },
+ { NC_("RID_SVXSTRARY_PAPERSIZE_DRAW", "A5") , PAPER_A5 },
+ { NC_("RID_SVXSTRARY_PAPERSIZE_DRAW", "A4") , PAPER_A4 },
+ { NC_("RID_SVXSTRARY_PAPERSIZE_DRAW", "A3") , PAPER_A3 },
+ { NC_("RID_SVXSTRARY_PAPERSIZE_DRAW", "A2") , PAPER_A2 },
+ { NC_("RID_SVXSTRARY_PAPERSIZE_DRAW", "A1") , PAPER_A1 },
+ { NC_("RID_SVXSTRARY_PAPERSIZE_DRAW", "A0") , PAPER_A0 },
+ { NC_("RID_SVXSTRARY_PAPERSIZE_DRAW", "B6 (ISO)") , PAPER_B6_ISO },
+ { NC_("RID_SVXSTRARY_PAPERSIZE_DRAW", "B5 (ISO)") , PAPER_B5_ISO },
+ { NC_("RID_SVXSTRARY_PAPERSIZE_DRAW", "B4 (ISO)") , PAPER_B4_ISO },
+ { NC_("RID_SVXSTRARY_PAPERSIZE_DRAW", "Letter") , PAPER_LETTER },
+ { NC_("RID_SVXSTRARY_PAPERSIZE_DRAW", "Legal") , PAPER_LEGAL },
+ { NC_("RID_SVXSTRARY_PAPERSIZE_DRAW", "Long Bond") , PAPER_FANFOLD_LEGAL_DE },
+ { NC_("RID_SVXSTRARY_PAPERSIZE_DRAW", "Tabloid") , PAPER_TABLOID },
+ { NC_("RID_SVXSTRARY_PAPERSIZE_DRAW", "B6 (JIS)") , PAPER_B6_JIS },
+ { NC_("RID_SVXSTRARY_PAPERSIZE_DRAW", "B5 (JIS)") , PAPER_B5_JIS },
+ { NC_("RID_SVXSTRARY_PAPERSIZE_DRAW", "B4 (JIS)") , PAPER_B4_JIS },
+ { NC_("RID_SVXSTRARY_PAPERSIZE_DRAW", "16 Kai") , PAPER_KAI16},
+ { NC_("RID_SVXSTRARY_PAPERSIZE_DRAW", "32 Kai") , PAPER_KAI32},
+ { NC_("RID_SVXSTRARY_PAPERSIZE_DRAW", "Big 32 Kai") , PAPER_KAI32BIG},
+ { NC_("RID_SVXSTRARY_PAPERSIZE_DRAW", "User") , PAPER_USER },
+ { NC_("RID_SVXSTRARY_PAPERSIZE_DRAW", "DL Envelope") , PAPER_ENV_DL },
+ { NC_("RID_SVXSTRARY_PAPERSIZE_DRAW", "C6 Envelope") , PAPER_ENV_C6 },
+ { NC_("RID_SVXSTRARY_PAPERSIZE_DRAW", "C6/5 Envelope") , PAPER_ENV_C65 },
+ { NC_("RID_SVXSTRARY_PAPERSIZE_DRAW", "C5 Envelope") , PAPER_ENV_C5 },
+ { NC_("RID_SVXSTRARY_PAPERSIZE_DRAW", "C4 Envelope") , PAPER_ENV_C4 },
+ { NC_("RID_SVXSTRARY_PAPERSIZE_DRAW", "Dia Slide") , PAPER_SLIDE_DIA },
+ { NC_("RID_SVXSTRARY_PAPERSIZE_DRAW", "Screen 4:3") , PAPER_SCREEN_4_3 },
+ { NC_("RID_SVXSTRARY_PAPERSIZE_DRAW", "Screen 16:9") , PAPER_SCREEN_16_9 },
+ { NC_("RID_SVXSTRARY_PAPERSIZE_DRAW", "Screen 16:10") , PAPER_SCREEN_16_10 },
+ { NC_("RID_SVXSTRARY_PAPERSIZE_DRAW", "Widescreen") , PAPER_WIDESCREEN },
+ { NC_("RID_SVXSTRARY_PAPERSIZE_DRAW", "On-screen Show (4:3)") , PAPER_ONSCREENSHOW_4_3 },
+ { NC_("RID_SVXSTRARY_PAPERSIZE_DRAW", "On-screen Show (16:9)") , PAPER_ONSCREENSHOW_16_9 },
+ { NC_("RID_SVXSTRARY_PAPERSIZE_DRAW", "On-screen Show (16:10)") , PAPER_ONSCREENSHOW_16_10 },
+ { NC_("RID_SVXSTRARY_PAPERSIZE_DRAW", "Japanese Postcard") , PAPER_POSTCARD_JP}
+};
+
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/dialog/pagectrl.cxx b/svx/source/dialog/pagectrl.cxx
new file mode 100644
index 000000000..a55f6a257
--- /dev/null
+++ b/svx/source/dialog/pagectrl.cxx
@@ -0,0 +1,396 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <memory>
+#include <editeng/frmdir.hxx>
+#include <vcl/canvastools.hxx>
+#include <vcl/outdev.hxx>
+#include <vcl/settings.hxx>
+#include <tools/fract.hxx>
+#include <svx/pageitem.hxx>
+#include <svx/pagectrl.hxx>
+#include <algorithm>
+#include <basegfx/matrix/b2dhommatrix.hxx>
+#include <drawinglayer/geometry/viewinformation2d.hxx>
+#include <drawinglayer/processor2d/baseprocessor2d.hxx>
+#include <drawinglayer/primitive2d/PolygonHairlinePrimitive2D.hxx>
+#include <drawinglayer/processor2d/processor2dtools.hxx>
+#include <basegfx/polygon/b2dpolygontools.hxx>
+
+#define CELL_WIDTH 1600L
+#define CELL_HEIGHT 800L
+
+SvxPageWindow::SvxPageWindow() :
+ nTop(0),
+ nBottom(0),
+ nLeft(0),
+ nRight(0),
+ bResetBackground(false),
+ bFrameDirection(false),
+ nFrameDirection(SvxFrameDirection::Horizontal_LR_TB),
+ nHdLeft(0),
+ nHdRight(0),
+ nHdDist(0),
+ nHdHeight(0),
+ nFtLeft(0),
+ nFtRight(0),
+ nFtDist(0),
+ nFtHeight(0),
+ bFooter(false),
+ bHeader(false),
+ bTable(false),
+ bHorz(false),
+ bVert(false),
+ eUsage(SvxPageUsage::All)
+{
+}
+
+SvxPageWindow::~SvxPageWindow()
+{
+}
+
+void SvxPageWindow::Paint(vcl::RenderContext& rRenderContext, const tools::Rectangle&)
+{
+ rRenderContext.Push(vcl::PushFlags::MAPMODE);
+ rRenderContext.SetMapMode(MapMode(MapUnit::MapTwip));
+
+ Fraction aXScale(aWinSize.Width(), std::max(aSize.Width() * 2 + aSize.Width() / 8, tools::Long(1)));
+ Fraction aYScale(aWinSize.Height(), std::max(aSize.Height(), tools::Long(1)));
+ MapMode aMapMode(rRenderContext.GetMapMode());
+
+ if(aYScale < aXScale)
+ {
+ aMapMode.SetScaleX(aYScale);
+ aMapMode.SetScaleY(aYScale);
+ }
+ else
+ {
+ aMapMode.SetScaleX(aXScale);
+ aMapMode.SetScaleY(aXScale);
+ }
+ rRenderContext.SetMapMode(aMapMode);
+ Size aSz(rRenderContext.PixelToLogic(GetOutputSizePixel()));
+ tools::Long nYPos = (aSz.Height() - aSize.Height()) / 2;
+
+ if (eUsage == SvxPageUsage::All)
+ {
+ // all pages are equal -> draw one page
+ if (aSize.Width() > aSize.Height())
+ {
+ // Draw Landscape page of the same size
+ Fraction aX = aMapMode.GetScaleX();
+ Fraction aY = aMapMode.GetScaleY();
+ Fraction a2(1.5);
+ aX *= a2;
+ aY *= a2;
+ aMapMode.SetScaleX(aX);
+ aMapMode.SetScaleY(aY);
+ rRenderContext.SetMapMode(aMapMode);
+ aSz = rRenderContext.PixelToLogic(GetOutputSizePixel());
+ nYPos = (aSz.Height() - aSize.Height()) / 2;
+ tools::Long nXPos = (aSz.Width() - aSize.Width()) / 2;
+ DrawPage(rRenderContext, Point(nXPos,nYPos),true,true);
+ }
+ else
+ // Portrait
+ DrawPage(rRenderContext, Point((aSz.Width() - aSize.Width()) / 2,nYPos),true,true);
+ }
+ else
+ {
+ // Left and right page are different -> draw two pages if possible
+ DrawPage(rRenderContext, Point(0, nYPos), false,
+ eUsage == SvxPageUsage::Left || eUsage == SvxPageUsage::All || eUsage == SvxPageUsage::Mirror);
+ DrawPage(rRenderContext, Point(aSize.Width() + aSize.Width() / 8, nYPos), true,
+ eUsage == SvxPageUsage::Right || eUsage == SvxPageUsage::All || eUsage == SvxPageUsage::Mirror);
+ }
+ rRenderContext.Pop();
+}
+
+void SvxPageWindow::DrawPage(vcl::RenderContext& rRenderContext, const Point& rOrg, const bool bSecond, const bool bEnabled)
+{
+ const StyleSettings& rStyleSettings = rRenderContext.GetSettings().GetStyleSettings();
+ const Color& rFieldColor = rStyleSettings.GetFieldColor();
+ const Color& rFieldTextColor = rStyleSettings.GetFieldTextColor();
+ const Color& rDisableColor = rStyleSettings.GetDisableColor();
+ const Color& rDlgColor = rStyleSettings.GetDialogColor();
+
+ // background
+ if (!bSecond || bResetBackground)
+ {
+ rRenderContext.SetLineColor(COL_TRANSPARENT);
+ rRenderContext.SetFillColor(rDlgColor);
+ Size winSize(rRenderContext.GetOutputSize());
+ rRenderContext.DrawRect(tools::Rectangle(Point(0,0), winSize));
+
+ if (bResetBackground)
+ bResetBackground = false;
+ }
+ rRenderContext.SetLineColor(rFieldTextColor);
+
+ // Shadow
+ Size aTempSize = aSize;
+
+ // Page
+ if (!bEnabled)
+ {
+ rRenderContext.SetFillColor(rDisableColor);
+ rRenderContext.DrawRect(tools::Rectangle(rOrg, aTempSize));
+ return;
+ }
+ rRenderContext.SetFillColor(rFieldColor);
+ rRenderContext.DrawRect(tools::Rectangle(rOrg, aTempSize));
+
+ tools::Long nL = nLeft;
+ tools::Long nR = nRight;
+
+ if (eUsage == SvxPageUsage::Mirror && !bSecond)
+ {
+ // turn for mirrored
+ nL = nRight;
+ nR = nLeft;
+ }
+
+ tools::Rectangle aRect;
+
+ aRect.SetLeft( rOrg.X() + nL );
+ aRect.SetRight( rOrg.X() + aTempSize.Width() - nR );
+ aRect.SetTop( rOrg.Y() + nTop );
+ aRect.SetBottom( rOrg.Y() + aTempSize.Height() - nBottom );
+
+ tools::Rectangle aHdRect(aRect);
+ tools::Rectangle aFtRect(aRect);
+
+ if (bHeader || bFooter)
+ {
+ // Header and/or footer used
+ const Color aLineColor(rRenderContext.GetLineColor());
+
+ // draw PageFill first and on the whole page, no outline
+ rRenderContext.SetLineColor();
+ drawFillAttributes(rRenderContext, maPageFillAttributes, aRect, aRect);
+ rRenderContext.SetLineColor(aLineColor);
+
+ if (bHeader)
+ {
+ // show headers if possible
+ aHdRect.AdjustLeft(nHdLeft );
+ aHdRect.AdjustRight( -nHdRight );
+ aHdRect.SetBottom( aRect.Top() + nHdHeight );
+ aRect.AdjustTop(nHdHeight + nHdDist );
+
+ // draw header over PageFill, plus outline
+ drawFillAttributes(rRenderContext, maHeaderFillAttributes, aHdRect, aHdRect);
+ }
+
+ if (bFooter)
+ {
+ // show footer if possible
+ aFtRect.AdjustLeft(nFtLeft );
+ aFtRect.AdjustRight( -nFtRight );
+ aFtRect.SetTop( aRect.Bottom() - nFtHeight );
+ aRect.AdjustBottom( -(nFtHeight + nFtDist) );
+
+ // draw footer over PageFill, plus outline
+ drawFillAttributes(rRenderContext, maFooterFillAttributes, aFtRect, aFtRect);
+ }
+
+ // draw page's reduced outline, only outline
+ drawFillAttributes(rRenderContext, drawinglayer::attribute::SdrAllFillAttributesHelperPtr(), aRect, aRect);
+ }
+ else
+ {
+ // draw PageFill and outline
+ drawFillAttributes(rRenderContext, maPageFillAttributes, aRect, aRect);
+ }
+
+ if (bFrameDirection && !bTable)
+ {
+ Point aPos;
+ vcl::Font aFont(rRenderContext.GetFont());
+ const Size aSaveSize = aFont.GetFontSize();
+ Size aDrawSize(0,aRect.GetHeight() / 6);
+ aFont.SetFontSize(aDrawSize);
+ rRenderContext.SetFont(aFont);
+ OUString sText("ABC");
+ Point aMove(1, rRenderContext.GetTextHeight());
+ sal_Unicode cArrow = 0x2193;
+ tools::Long nAWidth = rRenderContext.GetTextWidth(sText.copy(0,1));
+ switch (nFrameDirection)
+ {
+ case SvxFrameDirection::Horizontal_LR_TB:
+ aPos = aRect.TopLeft();
+ aPos.AdjustX(rRenderContext.PixelToLogic(Point(1,1)).X() );
+ aMove.setY( 0 );
+ cArrow = 0x2192;
+ break;
+ case SvxFrameDirection::Horizontal_RL_TB:
+ aPos = aRect.TopRight();
+ aPos.AdjustX( -nAWidth );
+ aMove.setY( 0 );
+ aMove.setX( aMove.X() * -1 );
+ cArrow = 0x2190;
+ break;
+ case SvxFrameDirection::Vertical_LR_TB:
+ aPos = aRect.TopLeft();
+ aPos.AdjustX(rRenderContext.PixelToLogic(Point(1,1)).X() );
+ aMove.setX( 0 );
+ break;
+ case SvxFrameDirection::Vertical_RL_TB:
+ aPos = aRect.TopRight();
+ aPos.AdjustX( -nAWidth );
+ aMove.setX( 0 );
+ break;
+ default: break;
+ }
+ sText += OUStringChar(cArrow);
+ for (sal_Int32 i = 0; i < sText.getLength(); i++)
+ {
+ OUString sDraw(sText.copy(i,1));
+ tools::Long nHDiff = 0;
+ tools::Long nCharWidth = rRenderContext.GetTextWidth(sDraw);
+ bool bHorizontal = 0 == aMove.Y();
+ if (!bHorizontal)
+ {
+ nHDiff = (nAWidth - nCharWidth) / 2;
+ aPos.AdjustX(nHDiff );
+ }
+ rRenderContext.DrawText(aPos,sDraw);
+ if (bHorizontal)
+ {
+ aPos.AdjustX(aMove.X() < 0 ? -nCharWidth : nCharWidth );
+ }
+ else
+ {
+ aPos.AdjustX( -nHDiff );
+ aPos.AdjustY(aMove.Y() );
+ }
+ }
+ aFont.SetFontSize(aSaveSize);
+ rRenderContext.SetFont(aFont);
+
+ }
+ if (!bTable)
+ return;
+
+ // Paint Table, if necessary center it
+ rRenderContext.SetLineColor(COL_LIGHTGRAY);
+
+ tools::Long nW = aRect.GetWidth();
+ tools::Long nH = aRect.GetHeight();
+ tools::Long const nTW = CELL_WIDTH * 3;
+ tools::Long const nTH = CELL_HEIGHT * 3;
+ tools::Long _nLeft = bHorz ? aRect.Left() + ((nW - nTW) / 2) : aRect.Left();
+ tools::Long _nTop = bVert ? aRect.Top() + ((nH - nTH) / 2) : aRect.Top();
+ tools::Rectangle aCellRect(Point(_nLeft, _nTop),Size(CELL_WIDTH, CELL_HEIGHT));
+
+ for (sal_uInt16 i = 0; i < 3; ++i)
+ {
+ aCellRect.SetLeft( _nLeft );
+ aCellRect.SetRight( _nLeft + CELL_WIDTH );
+ if(i > 0)
+ aCellRect.Move(0,CELL_HEIGHT);
+
+ for (sal_uInt16 j = 0; j < 3; ++j)
+ {
+ if (j > 0)
+ aCellRect.Move(CELL_WIDTH,0);
+ rRenderContext.DrawRect(aCellRect);
+ }
+ }
+}
+
+void SvxPageWindow::drawFillAttributes(vcl::RenderContext& rRenderContext,
+ const drawinglayer::attribute::SdrAllFillAttributesHelperPtr& rFillAttributes,
+ const tools::Rectangle& rPaintRange,
+ const tools::Rectangle& rDefineRange)
+{
+ const basegfx::B2DRange aPaintRange = vcl::unotools::b2DRectangleFromRectangle(rPaintRange);
+
+ if(aPaintRange.isEmpty() ||
+ basegfx::fTools::equalZero(aPaintRange.getWidth()) ||
+ basegfx::fTools::equalZero(aPaintRange.getHeight()))
+ return;
+
+ const basegfx::B2DRange aDefineRange = vcl::unotools::b2DRectangleFromRectangle(rDefineRange);
+
+ // prepare primitive sequence
+ drawinglayer::primitive2d::Primitive2DContainer aSequence;
+
+ // create fill geometry if there is something to fill
+ if (rFillAttributes && rFillAttributes->isUsed())
+ {
+ aSequence = rFillAttributes->getPrimitive2DSequence(aPaintRange, aDefineRange);
+ }
+
+ // create line geometry if a LineColor is set at the target device
+ if (rRenderContext.IsLineColor())
+ {
+ const drawinglayer::primitive2d::Primitive2DReference xOutline(
+ new drawinglayer::primitive2d::PolygonHairlinePrimitive2D(
+ basegfx::utils::createPolygonFromRect(aPaintRange), rRenderContext.GetLineColor().getBColor()));
+
+ aSequence.push_back(xOutline);
+ }
+
+ // draw that if we have something to draw
+ if (aSequence.empty())
+ return;
+
+ const drawinglayer::geometry::ViewInformation2D aViewInformation2D(
+ basegfx::B2DHomMatrix(), rRenderContext.GetViewTransformation(), aPaintRange, nullptr, 0.0);
+
+ std::unique_ptr<drawinglayer::processor2d::BaseProcessor2D> pProcessor(
+ drawinglayer::processor2d::createProcessor2DFromOutputDevice(rRenderContext, aViewInformation2D));
+ pProcessor->process(aSequence);
+}
+
+void SvxPageWindow::EnableFrameDirection(bool bEnable)
+{
+ bFrameDirection = bEnable;
+}
+
+void SvxPageWindow::SetFrameDirection(SvxFrameDirection nDirection)
+{
+ nFrameDirection = nDirection;
+}
+
+void SvxPageWindow::ResetBackground()
+{
+ bResetBackground = true;
+}
+
+void SvxPageWindow::SetDrawingArea(weld::DrawingArea* pDrawingArea)
+{
+ CustomWidgetController::SetDrawingArea(pDrawingArea);
+
+ OutputDevice& rRefDevice = pDrawingArea->get_ref_device();
+ // Count in Twips by default
+ rRefDevice.Push(vcl::PushFlags::MAPMODE);
+ rRefDevice.SetMapMode(MapMode(MapUnit::MapTwip));
+ aWinSize = rRefDevice.LogicToPixel(Size(75, 46), MapMode(MapUnit::MapAppFont));
+ pDrawingArea->set_size_request(aWinSize.Width(), aWinSize.Height());
+
+ aWinSize.AdjustHeight( -4 );
+ aWinSize.AdjustWidth( -4 );
+
+ aWinSize = rRefDevice.PixelToLogic(aWinSize);
+ rRefDevice.Pop();
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/dialog/pagenumberlistbox.cxx b/svx/source/dialog/pagenumberlistbox.cxx
new file mode 100644
index 000000000..076da12e1
--- /dev/null
+++ b/svx/source/dialog/pagenumberlistbox.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 <svx/dialmgr.hxx>
+#include <svx/pagenumberlistbox.hxx>
+#include <editeng/numitem.hxx>
+#include <com/sun/star/style/NumberingType.hpp>
+#include <numberingtype.hrc>
+
+SvxPageNumberListBox::SvxPageNumberListBox(std::unique_ptr<weld::ComboBox> pControl)
+ : m_xControl(std::move(pControl))
+{
+ m_xControl->set_size_request(150, -1);
+
+ for (size_t i = 0; i < SAL_N_ELEMENTS(RID_SVXSTRARY_NUMBERINGTYPE); ++i)
+ {
+ sal_uInt16 nData = RID_SVXSTRARY_NUMBERINGTYPE[i].second;
+ switch (nData)
+ {
+ // String list array is also used in Writer and contains strings
+ // for Bullet and Graphics, ignore those here.
+ case css::style::NumberingType::CHAR_SPECIAL:
+ case css::style::NumberingType::BITMAP:
+ case css::style::NumberingType::BITMAP | LINK_TOKEN:
+ break;
+ default:
+ {
+ OUString aStr = SvxResId(RID_SVXSTRARY_NUMBERINGTYPE[i].first);
+ m_xControl->append(OUString::number(nData), aStr);
+ break;
+ }
+ }
+ }
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/dialog/papersizelistbox.cxx b/svx/source/dialog/papersizelistbox.cxx
new file mode 100644
index 000000000..fc0211a8b
--- /dev/null
+++ b/svx/source/dialog/papersizelistbox.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 <svx/dialmgr.hxx>
+#include <svx/papersizelistbox.hxx>
+#include "page.hrc"
+
+SvxPaperSizeListBox::SvxPaperSizeListBox(std::unique_ptr<weld::ComboBox> pControl)
+ : m_xControl(std::move(pControl))
+{
+ m_xControl->set_size_request(150, -1);
+}
+
+void SvxPaperSizeListBox::FillPaperSizeEntries( PaperSizeApp eApp )
+{
+ const std::pair<TranslateId, int>* pPaperAry = eApp == PaperSizeApp::Std ?
+ RID_SVXSTRARY_PAPERSIZE_STD : RID_SVXSTRARY_PAPERSIZE_DRAW;
+ sal_uInt32 nCnt = eApp == PaperSizeApp::Std ?
+ SAL_N_ELEMENTS(RID_SVXSTRARY_PAPERSIZE_STD) : SAL_N_ELEMENTS(RID_SVXSTRARY_PAPERSIZE_DRAW);
+
+ for ( sal_uInt32 i = 0; i < nCnt; ++i )
+ {
+ OUString aStr = SvxResId(pPaperAry[i].first);
+ Paper eSize = static_cast<Paper>(pPaperAry[i].second);
+ m_xControl->append(OUString::number(static_cast<sal_Int32>(eSize)), aStr);
+ }
+}
+
+void SvxPaperSizeListBox::set_active_id( Paper ePreselectPaper )
+{
+ int nEntryCount = m_xControl->get_count();
+ int nSelPos = -1;
+ int nUserPos = -1;
+
+ for (int i = 0; i < nEntryCount; ++i)
+ {
+ Paper eTmp = static_cast<Paper>(m_xControl->get_id(i).toInt32());
+ if (eTmp == ePreselectPaper)
+ {
+ nSelPos = i;
+ break;
+ }
+
+ if (eTmp == PAPER_USER)
+ nUserPos = i;
+ }
+
+ // preselect current paper format - #115915#: ePaper might not be in aPaperSizeBox so use PAPER_USER instead
+ m_xControl->set_active((nSelPos != -1) ? nSelPos : nUserPos);
+}
+
+Paper SvxPaperSizeListBox::get_active_id() const
+{
+ return static_cast<Paper>(m_xControl->get_active_id().toInt32());
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
+
diff --git a/svx/source/dialog/paraprev.cxx b/svx/source/dialog/paraprev.cxx
new file mode 100644
index 000000000..27dccbfd9
--- /dev/null
+++ b/svx/source/dialog/paraprev.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/dialoghelper.hxx>
+#include <svx/paraprev.hxx>
+#include <vcl/outdev.hxx>
+#include <vcl/settings.hxx>
+
+SvxParaPrevWindow::SvxParaPrevWindow() :
+ nLeftMargin ( 0 ),
+ nRightMargin ( 0 ),
+ nFirstLineOffset ( 0 ),
+ nUpper ( 0 ),
+ nLower ( 0 ),
+ eAdjust ( SvxAdjust::Left ),
+ eLastLine ( SvxAdjust::Left ),
+ eLine ( SvxPrevLineSpace::N1 )
+{
+ aSize = Size(11905, 16837);
+}
+
+void SvxParaPrevWindow::SetDrawingArea(weld::DrawingArea* pDrawingArea)
+{
+ CustomWidgetController::SetDrawingArea(pDrawingArea);
+ Size aOptimalSize(getParagraphPreviewOptimalSize(pDrawingArea->get_ref_device()));
+ pDrawingArea->set_size_request(aOptimalSize.Width(), aOptimalSize.Height());
+}
+
+void SvxParaPrevWindow::Paint(vcl::RenderContext& rRenderContext, const tools::Rectangle&)
+{
+ DrawParagraph(rRenderContext);
+}
+
+#define DEF_MARGIN 120
+
+void SvxParaPrevWindow::DrawParagraph(vcl::RenderContext& rRenderContext)
+{
+ // Count in Twips by default
+ rRenderContext.Push(vcl::PushFlags::MAPMODE);
+ rRenderContext.SetMapMode(MapMode(MapUnit::MapTwip));
+
+ Size aWinSize(GetOutputSizePixel());
+ aWinSize = rRenderContext.PixelToLogic(aWinSize);
+ Size aTmp(1, 1);
+ aTmp = rRenderContext.PixelToLogic(aTmp);
+ aWinSize.AdjustWidth( -(aTmp.Width() /2) );
+ aWinSize.AdjustHeight( -(aTmp.Height() /2) );
+
+ const StyleSettings& rStyleSettings = rRenderContext.GetSettings().GetStyleSettings();
+ const Color& rWinColor = rStyleSettings.GetWindowColor();
+ Color aGrayColor(COL_LIGHTGRAY);
+
+ rRenderContext.SetFillColor(rWinColor);
+ rRenderContext.DrawRect(tools::Rectangle(Point(), aWinSize));
+
+ rRenderContext.SetLineColor();
+
+ tools::Long nH = aWinSize.Height() / 19;
+ Size aLineSiz(aWinSize.Width() - DEF_MARGIN, nH);
+ Size aSiz = aLineSiz;
+ Point aPnt;
+ aPnt.setX( DEF_MARGIN / 2 );
+ rRenderContext.SetFillColor(aGrayColor);
+
+ for (sal_uInt16 i = 0; i < 9; ++i)
+ {
+ if (i == 3)
+ {
+ rRenderContext.SetFillColor(COL_GRAY);
+ auto nTop = nUpper * aLineSiz.Height() / aSize.Height();
+ aPnt.AdjustY(nTop * 2 );
+ }
+
+ if (i == 6 )
+ rRenderContext.SetFillColor(aGrayColor);
+
+ if (3 <= i && 6 > i)
+ {
+ tools::Long nLeft = nLeftMargin * aLineSiz.Width() / aSize.Width();
+ tools::Long nFirst = nFirstLineOffset * aLineSiz.Width() / aSize.Width();
+ tools::Long nTmp = nLeft + nFirst;
+
+ if (i == 3)
+ {
+ aPnt.AdjustX(nTmp );
+ aSiz.AdjustWidth( -nTmp );
+ }
+ else
+ {
+ aPnt.AdjustX(nLeft );
+ aSiz.AdjustWidth( -nLeft );
+ }
+ tools::Long nRight = nRightMargin * aLineSiz.Width() / aSize.Width();
+ aSiz.AdjustWidth( -nRight );
+ }
+
+ if (4 == i || 5 == i || 6 == i)
+ {
+ switch (eLine)
+ {
+ case SvxPrevLineSpace::N1:
+ break;
+ case SvxPrevLineSpace::N115:
+ aPnt.AdjustY(nH / 6.67 ); // 1/.15 = 6.(6)
+ break;
+ case SvxPrevLineSpace::N15:
+ aPnt.AdjustY(nH / 2 );
+ break;
+ case SvxPrevLineSpace::N2:
+ aPnt.AdjustY(nH );
+ break;
+ case SvxPrevLineSpace::Prop:
+ case SvxPrevLineSpace::Min:
+ case SvxPrevLineSpace::Leading:
+ break;
+ }
+ }
+
+ aPnt.AdjustY(nH );
+
+ if (3 <= i && 5 >= i)
+ {
+ tools::Long nLW = tools::Long();
+ switch (i)
+ {
+ case 3:
+ nLW = aLineSiz.Width() * 8 / 10;
+ break;
+ case 4:
+ nLW = aLineSiz.Width() * 9 / 10;
+ break;
+ case 5:
+ nLW = aLineSiz.Width() / 2;
+ break;
+ }
+
+ if (nLW > aSiz.Width())
+ nLW = aSiz.Width();
+
+ switch (eAdjust)
+ {
+ case SvxAdjust::Left:
+ break;
+ case SvxAdjust::Right:
+ aPnt.AdjustX( aSiz.Width() - nLW );
+ break;
+ case SvxAdjust::Center:
+ aPnt.AdjustX(( aSiz.Width() - nLW ) / 2 );
+ break;
+ default: ; //prevent warning
+ }
+ if (SvxAdjust::Block == eAdjust)
+ {
+ if(5 == i)
+ {
+ switch( eLastLine )
+ {
+ case SvxAdjust::Left:
+ break;
+ case SvxAdjust::Right:
+ aPnt.AdjustX( aSiz.Width() - nLW );
+ break;
+ case SvxAdjust::Center:
+ aPnt.AdjustX(( aSiz.Width() - nLW ) / 2 );
+ break;
+ case SvxAdjust::Block:
+ nLW = aSiz.Width();
+ break;
+ default: ; //prevent warning
+ }
+ }
+ else
+ nLW = aSiz.Width();
+ }
+ aSiz.setWidth( nLW );
+ }
+
+ tools::Rectangle aRect(aPnt, aSiz);
+
+ rRenderContext.DrawRect( aRect );
+
+ if (5 == i)
+ {
+ auto nBottom = nLower * aLineSiz.Height() / aSize.Height();
+ aPnt.AdjustY(nBottom * 2 );
+ }
+
+ aPnt.AdjustY(nH );
+ // Reset, recalculate for each line
+ aPnt.setX( DEF_MARGIN / 2 );
+ aSiz = aLineSiz;
+ }
+ rRenderContext.Pop();
+}
+
+#undef DEF_MARGIN
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/dialog/passwd.cxx b/svx/source/dialog/passwd.cxx
new file mode 100644
index 000000000..cb7793c3d
--- /dev/null
+++ b/svx/source/dialog/passwd.cxx
@@ -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 .
+ */
+
+#include <vcl/svapp.hxx>
+#include <vcl/weld.hxx>
+#include <svx/passwd.hxx>
+#include <svx/dialmgr.hxx>
+#include <svx/strings.hrc>
+
+IMPL_LINK_NOARG(SvxPasswordDialog, ButtonHdl, weld::Button&, void)
+{
+ bool bOK = true;
+
+ if (m_xNewPasswdED->get_text() != m_xRepeatPasswdED->get_text())
+ {
+ std::unique_ptr<weld::MessageDialog> xBox(Application::CreateMessageDialog(m_xDialog.get(),
+ VclMessageType::Warning, VclButtonsType::Ok,
+ m_aRepeatPasswdErrStr));
+ xBox->run();
+ m_xNewPasswdED->set_text("");
+ m_xRepeatPasswdED->set_text("");
+ m_xNewPasswdED->grab_focus();
+ bOK = false;
+ }
+
+ if (bOK && m_aCheckPasswordHdl.IsSet() && !m_aCheckPasswordHdl.Call(this))
+ {
+ std::unique_ptr<weld::MessageDialog> xBox(Application::CreateMessageDialog(m_xDialog.get(),
+ VclMessageType::Warning, VclButtonsType::Ok,
+ m_aOldPasswdErrStr));
+ xBox->run();
+ m_xOldPasswdED->set_text("");
+ m_xOldPasswdED->grab_focus();
+ bOK = false;
+ }
+
+ if (bOK)
+ m_xDialog->response(RET_OK);
+}
+
+IMPL_LINK_NOARG(SvxPasswordDialog, EditModifyHdl, weld::Entry&, void)
+{
+ if (!m_xOKBtn->get_sensitive())
+ m_xOKBtn->set_sensitive(true);
+}
+
+SvxPasswordDialog::SvxPasswordDialog(weld::Window* pParent, bool bDisableOldPassword)
+ : SfxDialogController(pParent, "svx/ui/passwd.ui", "PasswordDialog")
+ , m_aOldPasswdErrStr(SvxResId(RID_SVXSTR_ERR_OLD_PASSWD))
+ , m_aRepeatPasswdErrStr(SvxResId(RID_SVXSTR_ERR_REPEAT_PASSWD ))
+ , m_xOldFL(m_xBuilder->weld_label("oldpass"))
+ , m_xOldPasswdFT(m_xBuilder->weld_label("oldpassL"))
+ , m_xOldPasswdED(m_xBuilder->weld_entry("oldpassEntry"))
+ , m_xNewPasswdED(m_xBuilder->weld_entry("newpassEntry"))
+ , m_xRepeatPasswdED(m_xBuilder->weld_entry("confirmpassEntry"))
+ , m_xOKBtn(m_xBuilder->weld_button("ok"))
+{
+ m_xOKBtn->connect_clicked(LINK(this, SvxPasswordDialog, ButtonHdl));
+ m_xRepeatPasswdED->connect_changed(LINK(this, SvxPasswordDialog, EditModifyHdl));
+ EditModifyHdl(*m_xRepeatPasswdED);
+
+ if (bDisableOldPassword)
+ {
+ m_xOldFL->set_sensitive(false);
+ m_xOldPasswdFT->set_sensitive(false);
+ m_xOldPasswdED->set_sensitive(false);
+ m_xNewPasswdED->grab_focus();
+ }
+}
+
+SvxPasswordDialog::~SvxPasswordDialog()
+{
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/dialog/relfld.cxx b/svx/source/dialog/relfld.cxx
new file mode 100644
index 000000000..3929e4fcf
--- /dev/null
+++ b/svx/source/dialog/relfld.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/relfld.hxx>
+
+SvxRelativeField::SvxRelativeField(std::unique_ptr<weld::MetricSpinButton> pControl)
+ : m_xSpinButton(std::move(pControl))
+ , nRelMin(0)
+ , nRelMax(0)
+ , bRelativeMode(false)
+ , bRelative(false)
+ , bNegativeEnabled(false)
+
+{
+ weld::SpinButton& rSpinButton = m_xSpinButton->get_widget();
+ rSpinButton.connect_changed(LINK(this, SvxRelativeField, ModifyHdl));
+}
+
+IMPL_LINK_NOARG(SvxRelativeField, ModifyHdl, weld::Entry&, void)
+{
+ if (!bRelativeMode)
+ return;
+
+ OUString aStr = m_xSpinButton->get_text();
+ bool bNewMode = bRelative;
+
+ if ( bRelative )
+ {
+ const sal_Unicode* pStr = aStr.getStr();
+
+ while ( *pStr )
+ {
+ if( ( ( *pStr < '0' ) || ( *pStr > '9' ) ) &&
+ ( *pStr != '%' ) )
+ {
+ bNewMode = false;
+ break;
+ }
+ pStr++;
+ }
+ }
+ else
+ {
+ if ( aStr.indexOf( "%" ) != -1 )
+ bNewMode = true;
+ }
+
+ if ( bNewMode != bRelative )
+ SetRelative( bNewMode );
+}
+
+void SvxRelativeField::EnableRelativeMode(sal_uInt16 nMin, sal_uInt16 nMax)
+{
+ bRelativeMode = true;
+ nRelMin = nMin;
+ nRelMax = nMax;
+ m_xSpinButton->set_unit(FieldUnit::CM);
+}
+
+void SvxRelativeField::SetRelative( bool bNewRelative )
+{
+ weld::SpinButton& rSpinButton = m_xSpinButton->get_widget();
+
+ int nStartPos, nEndPos;
+ rSpinButton.get_selection_bounds(nStartPos, nEndPos);
+ OUString aStr = rSpinButton.get_text();
+
+ if ( bNewRelative )
+ {
+ bRelative = true;
+ m_xSpinButton->set_digits(0);
+ m_xSpinButton->set_range(nRelMin, nRelMax, FieldUnit::NONE);
+ m_xSpinButton->set_unit(FieldUnit::PERCENT);
+ }
+ else
+ {
+ bRelative = false;
+ m_xSpinButton->set_digits(2);
+ m_xSpinButton->set_range(bNegativeEnabled ? -9999 : 0, 9999, FieldUnit::NONE);
+ m_xSpinButton->set_unit(FieldUnit::CM);
+ }
+
+ rSpinButton.set_text(aStr);
+ rSpinButton.select_region(nStartPos, nEndPos);
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/dialog/rlrcitem.cxx b/svx/source/dialog/rlrcitem.cxx
new file mode 100644
index 000000000..a73cd00bd
--- /dev/null
+++ b/svx/source/dialog/rlrcitem.cxx
@@ -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 .
+ */
+
+#include <svl/rectitem.hxx>
+#include <sal/log.hxx>
+#include <osl/diagnose.h>
+
+#include <svx/svxids.hrc>
+
+#include <svx/ruler.hxx>
+#include <editeng/lrspitem.hxx>
+#include <editeng/tstpitem.hxx>
+#include <editeng/protitem.hxx>
+#include "rlrcitem.hxx"
+#include <svx/rulritem.hxx>
+#include <svl/eitem.hxx>
+
+SvxRulerItem::SvxRulerItem(sal_uInt16 _nId, SvxRuler &rRul, SfxBindings &rBindings)
+: SfxControllerItem(_nId, rBindings),
+ rRuler(rRul)
+{
+}
+
+
+void SvxRulerItem::StateChangedAtToolBoxControl( sal_uInt16 nSID, SfxItemState eState,
+ const SfxPoolItem* pState)
+{
+ // SfxItemState::DONTCARE => pState == -1 => PTR_CAST buff
+ if ( eState != SfxItemState::DEFAULT )
+ pState = nullptr;
+
+ switch(nSID)
+ {
+ // Left / right margin
+ case SID_RULER_LR_MIN_MAX:
+ {
+ const SfxRectangleItem *pItem = dynamic_cast<const SfxRectangleItem*>( pState );
+ rRuler.UpdateFrameMinMax(pItem);
+ break;
+ }
+ case SID_ATTR_LONG_LRSPACE:
+ {
+ const SvxLongLRSpaceItem *pItem = dynamic_cast<const SvxLongLRSpaceItem*>( pState );
+ SAL_WARN_IF(pState != nullptr && pItem == nullptr, "svx.dialog", "SvxLRSpaceItem expected");
+ rRuler.UpdateFrame(pItem);
+ break;
+ }
+ case SID_ATTR_LONG_ULSPACE:
+ {
+ const SvxLongULSpaceItem *pItem = dynamic_cast<const SvxLongULSpaceItem*>( pState );
+ SAL_WARN_IF(pState != nullptr && pItem == nullptr, "svx.dialog", "SvxULSpaceItem expected");
+ rRuler.UpdateFrame(pItem);
+ break;
+ }
+ case SID_ATTR_TABSTOP_VERTICAL:
+ case SID_ATTR_TABSTOP:
+ {
+ const SvxTabStopItem *pItem = dynamic_cast<const SvxTabStopItem*>( pState );
+ SAL_WARN_IF(pState != nullptr && pItem == nullptr, "svx.dialog", "SvxTabStopItem expected");
+ rRuler.Update(pItem);
+ break;
+ }
+ case SID_ATTR_PARA_LRSPACE_VERTICAL:
+ case SID_ATTR_PARA_LRSPACE:
+ {
+ const SvxLRSpaceItem *pItem = dynamic_cast<const SvxLRSpaceItem*>( pState );
+ SAL_WARN_IF(pState != nullptr && pItem == nullptr, "svx.dialog", "SvxLRSpaceItem expected");
+ rRuler.UpdatePara(pItem);
+ break;
+ }
+ case SID_RULER_BORDERS_VERTICAL:
+ case SID_RULER_BORDERS:
+ case SID_RULER_ROWS:
+ case SID_RULER_ROWS_VERTICAL:
+ {
+ const SvxColumnItem *pItem = dynamic_cast<const SvxColumnItem*>( pState );
+ SAL_WARN_IF(pState != nullptr && pItem == nullptr, "svx.dialog", "SvxColumnItem expected");
+#ifdef DBG_UTIL
+ if(pItem)
+ {
+ if(pItem->IsConsistent())
+ rRuler.Update(pItem, nSID);
+ else
+ OSL_FAIL("Column item corrupted");
+ }
+ else
+ rRuler.Update(pItem, nSID);
+#else
+ rRuler.Update(pItem, nSID);
+#endif
+ break;
+ }
+ case SID_RULER_PAGE_POS:
+ { // Position page, page width
+ const SvxPagePosSizeItem *pItem = dynamic_cast<const SvxPagePosSizeItem*>( pState );
+ SAL_WARN_IF(pState != nullptr && pItem == nullptr, "svx.dialog", "SvxPagePosSizeItem expected");
+ rRuler.Update(pItem);
+ break;
+ }
+ case SID_RULER_OBJECT:
+ { // Object selection
+ const SvxObjectItem *pItem = dynamic_cast<const SvxObjectItem*>( pState );
+ SAL_WARN_IF(pState != nullptr && pItem == nullptr, "svx.dialog", "SvxObjectItem expected");
+ rRuler.Update(pItem);
+ break;
+ }
+ case SID_RULER_PROTECT:
+ {
+ const SvxProtectItem *pItem = dynamic_cast<const SvxProtectItem*>( pState );
+ SAL_WARN_IF(pState != nullptr && pItem == nullptr, "svx.dialog", "SvxProtectItem expected");
+ rRuler.Update(pItem);
+ break;
+ }
+ case SID_RULER_BORDER_DISTANCE:
+ {
+ const SvxLRSpaceItem *pItem = dynamic_cast<const SvxLRSpaceItem*>( pState );
+ SAL_WARN_IF(pState != nullptr && pItem == nullptr, "svx.dialog", "SvxLRSpaceItem expected");
+ rRuler.UpdateBorder(pItem);
+ }
+ break;
+ case SID_RULER_TEXT_RIGHT_TO_LEFT :
+ {
+ const SfxBoolItem *pItem = dynamic_cast<const SfxBoolItem*>( pState );
+ SAL_WARN_IF(pState != nullptr && pItem == nullptr, "svx.dialog", "SfxBoolItem expected");
+ rRuler.UpdateTextRTL(pItem);
+ }
+ break;
+ }
+}
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/dialog/rlrcitem.hxx b/svx/source/dialog/rlrcitem.hxx
new file mode 100644
index 000000000..0fde86f0c
--- /dev/null
+++ b/svx/source/dialog/rlrcitem.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_SVX_SOURCE_DIALOG_RLRCITEM_HXX
+#define INCLUDED_SVX_SOURCE_DIALOG_RLRCITEM_HXX
+
+#include <sfx2/ctrlitem.hxx>
+
+class SvxRuler;
+
+class SvxRulerItem : public SfxControllerItem
+{
+private:
+ SvxRuler& rRuler;
+
+protected:
+ virtual void StateChangedAtToolBoxControl( sal_uInt16,
+ SfxItemState, const SfxPoolItem* pState ) override;
+
+public:
+ SvxRulerItem( sal_uInt16 nId, SvxRuler&, SfxBindings& );
+};
+
+
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/dialog/rubydialog.cxx b/svx/source/dialog/rubydialog.cxx
new file mode 100644
index 000000000..c2d396901
--- /dev/null
+++ b/svx/source/dialog/rubydialog.cxx
@@ -0,0 +1,878 @@
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * 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 <tools/debug.hxx>
+#include <tools/diagnose_ex.h>
+
+#include <svx/rubydialog.hxx>
+#include <sfx2/dispatch.hxx>
+#include <sfx2/sfxsids.hrc>
+#include <sfx2/viewfrm.hxx>
+#include <sfx2/viewsh.hxx>
+#include <svl/eitem.hxx>
+#include <com/sun/star/frame/XController.hpp>
+#include <com/sun/star/style/XStyle.hpp>
+#include <com/sun/star/text/XRubySelection.hpp>
+#include <com/sun/star/beans/PropertyValues.hpp>
+#include <com/sun/star/beans/XPropertySet.hpp>
+#include <com/sun/star/beans/XPropertySetInfo.hpp>
+#include <com/sun/star/container/XNameContainer.hpp>
+#include <com/sun/star/style/XStyleFamiliesSupplier.hpp>
+#include <com/sun/star/text/RubyAdjust.hpp>
+#include <com/sun/star/view/XSelectionChangeListener.hpp>
+#include <com/sun/star/view/XSelectionSupplier.hpp>
+#include <cppuhelper/implbase.hxx>
+#include <svtools/colorcfg.hxx>
+#include <vcl/event.hxx>
+#include <vcl/settings.hxx>
+#include <vcl/svapp.hxx>
+
+using namespace css::uno;
+using namespace css::frame;
+using namespace css::text;
+using namespace css::beans;
+using namespace css::style;
+using namespace css::view;
+using namespace css::lang;
+using namespace css::container;
+
+SFX_IMPL_CHILDWINDOW(SvxRubyChildWindow, SID_RUBY_DIALOG);
+
+namespace
+{
+constexpr OUStringLiteral cRubyBaseText = u"RubyBaseText";
+constexpr OUStringLiteral cRubyText = u"RubyText";
+constexpr OUStringLiteral cRubyAdjust = u"RubyAdjust";
+constexpr OUStringLiteral cRubyPosition = u"RubyPosition";
+constexpr OUStringLiteral cRubyCharStyleName = u"RubyCharStyleName";
+
+} // end anonymous namespace
+
+SvxRubyChildWindow::SvxRubyChildWindow(vcl::Window* _pParent, sal_uInt16 nId,
+ SfxBindings* pBindings, SfxChildWinInfo const* pInfo)
+ : SfxChildWindow(_pParent, nId)
+{
+ auto xDlg = std::make_shared<SvxRubyDialog>(pBindings, this, _pParent->GetFrameWeld());
+ SetController(xDlg);
+ xDlg->Initialize(pInfo);
+}
+
+SfxChildWinInfo SvxRubyChildWindow::GetInfo() const { return SfxChildWindow::GetInfo(); }
+
+class SvxRubyData_Impl : public cppu::WeakImplHelper<css::view::XSelectionChangeListener>
+{
+ Reference<XModel> xModel;
+ Reference<XRubySelection> xSelection;
+ Sequence<PropertyValues> aRubyValues;
+ Reference<XController> xController;
+ bool bHasSelectionChanged;
+ bool bDisposing;
+
+public:
+ SvxRubyData_Impl();
+ virtual ~SvxRubyData_Impl() override;
+
+ void SetController(const Reference<XController>& xCtrl);
+ Reference<XModel> const& GetModel()
+ {
+ if (!xController.is())
+ xModel = nullptr;
+ else
+ xModel = xController->getModel();
+ return xModel;
+ }
+ bool HasSelectionChanged() const { return bHasSelectionChanged; }
+ bool IsDisposing() const { return bDisposing; }
+ Reference<XRubySelection> const& GetRubySelection()
+ {
+ xSelection.set(xController, UNO_QUERY);
+ return xSelection;
+ }
+ void UpdateRubyValues()
+ {
+ if (!xSelection.is())
+ aRubyValues.realloc(0);
+ else
+ aRubyValues = xSelection->getRubyList(false);
+ bHasSelectionChanged = false;
+ }
+ Sequence<PropertyValues>& GetRubyValues() { return aRubyValues; }
+ void AssertOneEntry();
+
+ virtual void SAL_CALL selectionChanged(const css::lang::EventObject& aEvent) override;
+ virtual void SAL_CALL disposing(const css::lang::EventObject& Source) override;
+};
+
+SvxRubyData_Impl::SvxRubyData_Impl()
+ : bHasSelectionChanged(false)
+ , bDisposing(false)
+{
+}
+
+SvxRubyData_Impl::~SvxRubyData_Impl() {}
+
+void SvxRubyData_Impl::SetController(const Reference<XController>& xCtrl)
+{
+ if (xCtrl.get() == xController.get())
+ return;
+
+ try
+ {
+ Reference<XSelectionSupplier> xSelSupp(xController, UNO_QUERY);
+ if (xSelSupp.is())
+ xSelSupp->removeSelectionChangeListener(this);
+
+ bHasSelectionChanged = true;
+ xController = xCtrl;
+ xSelSupp.set(xController, UNO_QUERY);
+ if (xSelSupp.is())
+ xSelSupp->addSelectionChangeListener(this);
+ }
+ catch (const Exception&)
+ {
+ }
+}
+
+void SvxRubyData_Impl::selectionChanged(const EventObject&) { bHasSelectionChanged = true; }
+
+void SvxRubyData_Impl::disposing(const EventObject&)
+{
+ try
+ {
+ Reference<XSelectionSupplier> xSelSupp(xController, UNO_QUERY);
+ if (xSelSupp.is())
+ xSelSupp->removeSelectionChangeListener(this);
+ }
+ catch (const Exception&)
+ {
+ }
+ xController = nullptr;
+ bDisposing = true;
+}
+
+void SvxRubyData_Impl::AssertOneEntry()
+{
+ //create one entry
+ if (!aRubyValues.hasElements())
+ {
+ aRubyValues.realloc(1);
+ Sequence<PropertyValue>& rValues = aRubyValues.getArray()[0];
+ rValues.realloc(5);
+ PropertyValue* pValues = rValues.getArray();
+ pValues[0].Name = cRubyBaseText;
+ pValues[1].Name = cRubyText;
+ pValues[2].Name = cRubyAdjust;
+ pValues[3].Name = cRubyPosition;
+ pValues[4].Name = cRubyCharStyleName;
+ }
+}
+
+SvxRubyDialog::SvxRubyDialog(SfxBindings* pBind, SfxChildWindow* pCW, weld::Window* pParent)
+ : SfxModelessDialogController(pBind, pCW, pParent, "svx/ui/asianphoneticguidedialog.ui",
+ "AsianPhoneticGuideDialog")
+ , nLastPos(0)
+ , nCurrentEdit(0)
+ , bModified(false)
+ , pBindings(pBind)
+ , m_pImpl(new SvxRubyData_Impl)
+ , m_xLeftFT(m_xBuilder->weld_label("basetextft"))
+ , m_xRightFT(m_xBuilder->weld_label("rubytextft"))
+ , m_xLeft1ED(m_xBuilder->weld_entry("Left1ED"))
+ , m_xRight1ED(m_xBuilder->weld_entry("Right1ED"))
+ , m_xLeft2ED(m_xBuilder->weld_entry("Left2ED"))
+ , m_xRight2ED(m_xBuilder->weld_entry("Right2ED"))
+ , m_xLeft3ED(m_xBuilder->weld_entry("Left3ED"))
+ , m_xRight3ED(m_xBuilder->weld_entry("Right3ED"))
+ , m_xLeft4ED(m_xBuilder->weld_entry("Left4ED"))
+ , m_xRight4ED(m_xBuilder->weld_entry("Right4ED"))
+ , m_xScrolledWindow(m_xBuilder->weld_scrolled_window("scrolledwindow", true))
+ , m_xAdjustLB(m_xBuilder->weld_combo_box("adjustlb"))
+ , m_xPositionLB(m_xBuilder->weld_combo_box("positionlb"))
+ , m_xCharStyleFT(m_xBuilder->weld_label("styleft"))
+ , m_xCharStyleLB(m_xBuilder->weld_combo_box("stylelb"))
+ , m_xStylistPB(m_xBuilder->weld_button("styles"))
+ , m_xApplyPB(m_xBuilder->weld_button("ok"))
+ , m_xClosePB(m_xBuilder->weld_button("close"))
+ , m_xContentArea(m_xDialog->weld_content_area())
+ , m_xGrid(m_xBuilder->weld_widget("grid"))
+ , m_xPreviewWin(new RubyPreview)
+ , m_xPreview(new weld::CustomWeld(*m_xBuilder, "preview", *m_xPreviewWin))
+{
+ m_xCharStyleLB->make_sorted();
+ m_xPreviewWin->setRubyDialog(this);
+ m_xScrolledWindow->set_size_request(-1, m_xGrid->get_preferred_size().Height());
+ m_xScrolledWindow->set_vpolicy(VclPolicyType::NEVER);
+
+ aEditArr[0] = m_xLeft1ED.get();
+ aEditArr[1] = m_xRight1ED.get();
+ aEditArr[2] = m_xLeft2ED.get();
+ aEditArr[3] = m_xRight2ED.get();
+ aEditArr[4] = m_xLeft3ED.get();
+ aEditArr[5] = m_xRight3ED.get();
+ aEditArr[6] = m_xLeft4ED.get();
+ aEditArr[7] = m_xRight4ED.get();
+
+ m_xApplyPB->connect_clicked(LINK(this, SvxRubyDialog, ApplyHdl_Impl));
+ m_xClosePB->connect_clicked(LINK(this, SvxRubyDialog, CloseHdl_Impl));
+ m_xStylistPB->connect_clicked(LINK(this, SvxRubyDialog, StylistHdl_Impl));
+ m_xAdjustLB->connect_changed(LINK(this, SvxRubyDialog, AdjustHdl_Impl));
+ m_xPositionLB->connect_changed(LINK(this, SvxRubyDialog, PositionHdl_Impl));
+ m_xCharStyleLB->connect_changed(LINK(this, SvxRubyDialog, CharStyleHdl_Impl));
+
+ Link<weld::ScrolledWindow&, void> aScrLk(LINK(this, SvxRubyDialog, ScrollHdl_Impl));
+ m_xScrolledWindow->connect_vadjustment_changed(aScrLk);
+
+ Link<weld::Entry&, void> aEditLk(LINK(this, SvxRubyDialog, EditModifyHdl_Impl));
+ Link<weld::Widget&, void> aFocusLk(LINK(this, SvxRubyDialog, EditFocusHdl_Impl));
+ Link<const KeyEvent&, bool> aKeyUpDownLk(LINK(this, SvxRubyDialog, KeyUpDownHdl_Impl));
+ Link<const KeyEvent&, bool> aKeyTabUpDownLk(LINK(this, SvxRubyDialog, KeyUpDownTabHdl_Impl));
+ for (sal_uInt16 i = 0; i < 8; i++)
+ {
+ aEditArr[i]->connect_changed(aEditLk);
+ aEditArr[i]->connect_focus_in(aFocusLk);
+ if (!i || 7 == i)
+ aEditArr[i]->connect_key_press(aKeyTabUpDownLk);
+ else
+ aEditArr[i]->connect_key_press(aKeyUpDownLk);
+ }
+}
+
+SvxRubyDialog::~SvxRubyDialog()
+{
+ ClearCharStyleList();
+ EventObject aEvent;
+ m_pImpl->disposing(aEvent);
+}
+
+void SvxRubyDialog::ClearCharStyleList() { m_xCharStyleLB->clear(); }
+
+void SvxRubyDialog::Close()
+{
+ if (IsClosing())
+ return;
+ SfxViewFrame* pViewFrame = SfxViewFrame::Current();
+ if (pViewFrame)
+ pViewFrame->ToggleChildWindow(SID_RUBY_DIALOG);
+}
+
+void SvxRubyDialog::Activate()
+{
+ SfxModelessDialogController::Activate();
+ if (m_pImpl->IsDisposing())
+ {
+ // tdf#141967/tdf#152495 if Activate is called during tear down bail early
+ return;
+ }
+
+ //get selection from current view frame
+ SfxViewFrame* pCurFrm = SfxViewFrame::Current();
+ Reference<XController> xCtrl(pCurFrm ? pCurFrm->GetFrame().GetController() : nullptr);
+ m_pImpl->SetController(xCtrl);
+ if (!m_pImpl->HasSelectionChanged())
+ return;
+
+ Reference<XRubySelection> xRubySel = m_pImpl->GetRubySelection();
+ m_pImpl->UpdateRubyValues();
+ EnableControls(xRubySel.is());
+ if (xRubySel.is())
+ {
+ Reference<XModel> xModel = m_pImpl->GetModel();
+ const OUString sCharStyleSelect = m_xCharStyleLB->get_active_text();
+ ClearCharStyleList();
+ Reference<XStyleFamiliesSupplier> xSupplier(xModel, UNO_QUERY);
+ if (xSupplier.is())
+ {
+ try
+ {
+ Reference<XNameAccess> xFam = xSupplier->getStyleFamilies();
+ Any aChar = xFam->getByName("CharacterStyles");
+ Reference<XNameContainer> xChar;
+ aChar >>= xChar;
+ Reference<XIndexAccess> xCharIdx(xChar, UNO_QUERY);
+ if (xCharIdx.is())
+ {
+ OUString sUIName("DisplayName");
+ for (sal_Int32 nStyle = 0; nStyle < xCharIdx->getCount(); nStyle++)
+ {
+ Any aStyle = xCharIdx->getByIndex(nStyle);
+ Reference<XStyle> xStyle;
+ aStyle >>= xStyle;
+ Reference<XPropertySet> xPrSet(xStyle, UNO_QUERY);
+ OUString sName, sCoreName;
+ if (xPrSet.is())
+ {
+ Reference<XPropertySetInfo> xInfo = xPrSet->getPropertySetInfo();
+ if (xInfo->hasPropertyByName(sUIName))
+ {
+ Any aName = xPrSet->getPropertyValue(sUIName);
+ aName >>= sName;
+ }
+ }
+ if (xStyle.is())
+ {
+ sCoreName = xStyle->getName();
+ if (sName.isEmpty())
+ sName = sCoreName;
+ }
+ if (!sName.isEmpty())
+ {
+ m_xCharStyleLB->append(sCoreName, sName);
+ }
+ }
+ }
+ }
+ catch (const Exception&)
+ {
+ TOOLS_WARN_EXCEPTION("svx.dialog", "exception in style access");
+ }
+ if (!sCharStyleSelect.isEmpty())
+ m_xCharStyleLB->set_active_text(sCharStyleSelect);
+ }
+ m_xCharStyleLB->set_sensitive(xSupplier.is());
+ m_xCharStyleFT->set_sensitive(xSupplier.is());
+ }
+ Update();
+ m_xPreviewWin->Invalidate();
+}
+
+void SvxRubyDialog::SetRubyText(sal_Int32 nPos, weld::Entry& rLeft, weld::Entry& rRight)
+{
+ OUString sLeft, sRight;
+ const Sequence<PropertyValues>& aRubyValues = m_pImpl->GetRubyValues();
+ bool bEnable = aRubyValues.getLength() > nPos;
+ if (bEnable)
+ {
+ const Sequence<PropertyValue> aProps = aRubyValues.getConstArray()[nPos];
+ for (const PropertyValue& rProp : aProps)
+ {
+ if (rProp.Name == cRubyBaseText)
+ rProp.Value >>= sLeft;
+ else if (rProp.Name == cRubyText)
+ rProp.Value >>= sRight;
+ }
+ }
+ else if (!nPos)
+ {
+ bEnable = true;
+ }
+ rLeft.set_sensitive(bEnable);
+ rRight.set_sensitive(bEnable);
+ rLeft.set_text(sLeft);
+ rRight.set_text(sRight);
+ rLeft.save_value();
+ rRight.save_value();
+}
+
+void SvxRubyDialog::GetRubyText()
+{
+ tools::Long nTempLastPos = GetLastPos();
+ Sequence<PropertyValues>& aRubyValues = m_pImpl->GetRubyValues();
+ auto aRubyValuesRange = asNonConstRange(aRubyValues);
+ for (int i = 0; i < 8; i += 2)
+ {
+ if (aEditArr[i]->get_sensitive()
+ && (aEditArr[i]->get_value_changed_from_saved()
+ || aEditArr[i + 1]->get_value_changed_from_saved()))
+ {
+ DBG_ASSERT(aRubyValues.getLength() > (i / 2 + nTempLastPos), "wrong index");
+ SetModified(true);
+ for (PropertyValue& propVal : asNonConstRange(aRubyValuesRange[i / 2 + nTempLastPos]))
+ {
+ if (propVal.Name == cRubyBaseText)
+ propVal.Value <<= aEditArr[i]->get_text();
+ else if (propVal.Name == cRubyText)
+ propVal.Value <<= aEditArr[i + 1]->get_text();
+ }
+ }
+ }
+}
+
+void SvxRubyDialog::Update()
+{
+ const Sequence<PropertyValues>& aRubyValues = m_pImpl->GetRubyValues();
+ sal_Int32 nLen = aRubyValues.getLength();
+ m_xScrolledWindow->vadjustment_configure(0, 0, !nLen ? 1 : nLen, 1, 4, 4);
+ if (nLen > 4)
+ m_xScrolledWindow->set_vpolicy(VclPolicyType::ALWAYS);
+ else
+ m_xScrolledWindow->set_vpolicy(VclPolicyType::NEVER);
+ SetLastPos(0);
+ SetModified(false);
+
+ sal_Int16 nAdjust = -1;
+ sal_Int16 nPosition = -1;
+ OUString sCharStyleName, sTmp;
+ bool bCharStyleEqual = true;
+ for (sal_Int32 nRuby = 0; nRuby < nLen; nRuby++)
+ {
+ const Sequence<PropertyValue>& rProps = aRubyValues.getConstArray()[nRuby];
+ for (const PropertyValue& rProp : rProps)
+ {
+ if (nAdjust > -2 && rProp.Name == cRubyAdjust)
+ {
+ sal_Int16 nTmp = sal_Int16();
+ rProp.Value >>= nTmp;
+ if (!nRuby)
+ nAdjust = nTmp;
+ else if (nAdjust != nTmp)
+ nAdjust = -2;
+ }
+ if (nPosition > -2 && rProp.Name == cRubyPosition)
+ {
+ sal_Int16 nTmp = sal_Int16();
+ rProp.Value >>= nTmp;
+ if (!nRuby)
+ nPosition = nTmp;
+ else if (nPosition != nTmp)
+ nPosition = -2;
+ }
+ if (bCharStyleEqual && rProp.Name == cRubyCharStyleName)
+ {
+ rProp.Value >>= sTmp;
+ if (!nRuby)
+ sCharStyleName = sTmp;
+ else if (sCharStyleName != sTmp)
+ bCharStyleEqual = false;
+ }
+ }
+ }
+ if (!nLen)
+ {
+ //enable selection if the ruby list is empty
+ nAdjust = 0;
+ nPosition = 0;
+ }
+ if (nAdjust > -1)
+ m_xAdjustLB->set_active(nAdjust);
+ else
+ m_xAdjustLB->set_active(-1);
+ if (nPosition > -1)
+ m_xPositionLB->set_active(nPosition);
+ if (!nLen || (bCharStyleEqual && sCharStyleName.isEmpty()))
+ sCharStyleName = "Rubies";
+ if (!sCharStyleName.isEmpty())
+ {
+ for (int i = 0, nEntryCount = m_xCharStyleLB->get_count(); i < nEntryCount; i++)
+ {
+ OUString sCoreName = m_xCharStyleLB->get_id(i);
+ if (sCharStyleName == sCoreName)
+ {
+ m_xCharStyleLB->set_active(i);
+ break;
+ }
+ }
+ }
+ else
+ m_xCharStyleLB->set_active(-1);
+
+ ScrollHdl_Impl(*m_xScrolledWindow);
+}
+
+void SvxRubyDialog::GetCurrentText(OUString& rBase, OUString& rRuby)
+{
+ rBase = aEditArr[nCurrentEdit * 2]->get_text();
+ rRuby = aEditArr[nCurrentEdit * 2 + 1]->get_text();
+}
+
+IMPL_LINK(SvxRubyDialog, ScrollHdl_Impl, weld::ScrolledWindow&, rScroll, void)
+{
+ int nPos = rScroll.vadjustment_get_value();
+ if (GetLastPos() != nPos)
+ {
+ GetRubyText();
+ }
+ SetRubyText(nPos++, *m_xLeft1ED, *m_xRight1ED);
+ SetRubyText(nPos++, *m_xLeft2ED, *m_xRight2ED);
+ SetRubyText(nPos++, *m_xLeft3ED, *m_xRight3ED);
+ SetRubyText(nPos, *m_xLeft4ED, *m_xRight4ED);
+ SetLastPos(nPos - 3);
+ m_xPreviewWin->Invalidate();
+}
+
+IMPL_LINK_NOARG(SvxRubyDialog, ApplyHdl_Impl, weld::Button&, void)
+{
+ const Sequence<PropertyValues>& aRubyValues = m_pImpl->GetRubyValues();
+ if (!aRubyValues.hasElements())
+ {
+ AssertOneEntry();
+ PositionHdl_Impl(*m_xPositionLB);
+ AdjustHdl_Impl(*m_xAdjustLB);
+ CharStyleHdl_Impl(*m_xCharStyleLB);
+ }
+ GetRubyText();
+ //reset all edit fields - SaveValue is called
+ ScrollHdl_Impl(*m_xScrolledWindow);
+
+ Reference<XRubySelection> xSelection = m_pImpl->GetRubySelection();
+ if (IsModified() && xSelection.is())
+ {
+ try
+ {
+ xSelection->setRubyList(aRubyValues, false);
+ }
+ catch (const Exception&)
+ {
+ TOOLS_WARN_EXCEPTION("svx.dialog", "");
+ }
+ }
+}
+
+IMPL_LINK_NOARG(SvxRubyDialog, CloseHdl_Impl, weld::Button&, void) { Close(); }
+
+IMPL_LINK_NOARG(SvxRubyDialog, StylistHdl_Impl, weld::Button&, void)
+{
+ std::unique_ptr<SfxBoolItem> pState;
+ SfxItemState eState = pBindings->QueryState(SID_STYLE_DESIGNER, pState);
+ if (eState <= SfxItemState::SET || !pState || !pState->GetValue())
+ {
+ pBindings->GetDispatcher()->Execute(SID_STYLE_DESIGNER,
+ SfxCallMode::ASYNCHRON | SfxCallMode::RECORD);
+ }
+}
+
+IMPL_LINK(SvxRubyDialog, AdjustHdl_Impl, weld::ComboBox&, rBox, void)
+{
+ AssertOneEntry();
+ sal_Int16 nAdjust = rBox.get_active();
+ for (PropertyValues& rProps : asNonConstRange(m_pImpl->GetRubyValues()))
+ {
+ for (PropertyValue& propVal : asNonConstRange(rProps))
+ {
+ if (propVal.Name == cRubyAdjust)
+ propVal.Value <<= nAdjust;
+ }
+ SetModified(true);
+ }
+ m_xPreviewWin->Invalidate();
+}
+
+IMPL_LINK(SvxRubyDialog, PositionHdl_Impl, weld::ComboBox&, rBox, void)
+{
+ AssertOneEntry();
+ sal_Int16 nPosition = rBox.get_active();
+ for (PropertyValues& rProps : asNonConstRange(m_pImpl->GetRubyValues()))
+ {
+ for (PropertyValue& propVal : asNonConstRange(rProps))
+ {
+ if (propVal.Name == cRubyPosition)
+ propVal.Value <<= nPosition;
+ }
+ SetModified(true);
+ }
+ m_xPreviewWin->Invalidate();
+}
+
+IMPL_LINK_NOARG(SvxRubyDialog, CharStyleHdl_Impl, weld::ComboBox&, void)
+{
+ AssertOneEntry();
+ OUString sStyleName;
+ if (m_xCharStyleLB->get_active() != -1)
+ sStyleName = m_xCharStyleLB->get_active_id();
+ for (PropertyValues& rProps : asNonConstRange(m_pImpl->GetRubyValues()))
+ {
+ for (PropertyValue& propVal : asNonConstRange(rProps))
+ {
+ if (propVal.Name == cRubyCharStyleName)
+ {
+ propVal.Value <<= sStyleName;
+ }
+ }
+ SetModified(true);
+ }
+}
+
+IMPL_LINK(SvxRubyDialog, EditFocusHdl_Impl, weld::Widget&, rEdit, void)
+{
+ for (sal_uInt16 i = 0; i < 8; i++)
+ {
+ if (&rEdit == aEditArr[i])
+ {
+ nCurrentEdit = i / 2;
+ break;
+ }
+ }
+ m_xPreviewWin->Invalidate();
+}
+
+IMPL_LINK(SvxRubyDialog, EditModifyHdl_Impl, weld::Entry&, rEdit, void)
+{
+ EditFocusHdl_Impl(rEdit);
+}
+
+bool SvxRubyDialog::EditScrollHdl_Impl(sal_Int32 nParam)
+{
+ bool bRet = false;
+ //scroll forward
+ if (nParam > 0 && (aEditArr[7]->has_focus() || aEditArr[6]->has_focus()))
+ {
+ if (m_xScrolledWindow->vadjustment_get_upper()
+ > m_xScrolledWindow->vadjustment_get_value()
+ + m_xScrolledWindow->vadjustment_get_page_size())
+ {
+ m_xScrolledWindow->vadjustment_set_value(m_xScrolledWindow->vadjustment_get_value()
+ + 1);
+ aEditArr[6]->grab_focus();
+ bRet = true;
+ }
+ }
+ //scroll backward
+ else if (m_xScrolledWindow->vadjustment_get_value()
+ && (aEditArr[0]->has_focus() || aEditArr[1]->has_focus()))
+ {
+ m_xScrolledWindow->vadjustment_set_value(m_xScrolledWindow->vadjustment_get_value() - 1);
+ aEditArr[1]->grab_focus();
+ bRet = true;
+ }
+ if (bRet)
+ ScrollHdl_Impl(*m_xScrolledWindow);
+ return bRet;
+}
+
+bool SvxRubyDialog::EditJumpHdl_Impl(sal_Int32 nParam)
+{
+ bool bHandled = false;
+ sal_uInt16 nIndex = USHRT_MAX;
+ for (sal_uInt16 i = 0; i < 8; i++)
+ {
+ if (aEditArr[i]->has_focus())
+ nIndex = i;
+ }
+ if (nIndex < 8)
+ {
+ if (nParam > 0)
+ {
+ if (nIndex < 6)
+ aEditArr[nIndex + 2]->grab_focus();
+ else if (EditScrollHdl_Impl(nParam))
+ aEditArr[nIndex]->grab_focus();
+ }
+ else
+ {
+ if (nIndex > 1)
+ aEditArr[nIndex - 2]->grab_focus();
+ else if (EditScrollHdl_Impl(nParam))
+ aEditArr[nIndex]->grab_focus();
+ }
+ bHandled = true;
+ }
+ return bHandled;
+}
+
+void SvxRubyDialog::AssertOneEntry() { m_pImpl->AssertOneEntry(); }
+
+void SvxRubyDialog::EnableControls(bool bEnable)
+{
+ m_xContentArea->set_sensitive(bEnable);
+ m_xApplyPB->set_sensitive(bEnable);
+}
+
+RubyPreview::RubyPreview()
+ : m_pParentDlg(nullptr)
+{
+}
+
+RubyPreview::~RubyPreview() {}
+
+void RubyPreview::Paint(vcl::RenderContext& rRenderContext, const tools::Rectangle& /*rRect*/)
+{
+ rRenderContext.Push(vcl::PushFlags::ALL);
+
+ rRenderContext.SetMapMode(MapMode(MapUnit::MapTwip));
+
+ Size aWinSize = rRenderContext.GetOutputSize();
+
+ const StyleSettings& rStyleSettings = Application::GetSettings().GetStyleSettings();
+ svtools::ColorConfig aColorConfig;
+
+ Color aNewTextColor(aColorConfig.GetColorValue(svtools::FONTCOLOR).nColor);
+ Color aNewFillColor(rStyleSettings.GetWindowColor());
+
+ vcl::Font aFont = rRenderContext.GetFont();
+ aFont.SetFontHeight(aWinSize.Height() / 4);
+ aFont.SetFillColor(aNewFillColor);
+ aFont.SetColor(aNewTextColor);
+ rRenderContext.SetFont(aFont);
+
+ tools::Rectangle aRect(Point(0, 0), aWinSize);
+ rRenderContext.SetLineColor();
+ rRenderContext.SetFillColor(aFont.GetFillColor());
+ rRenderContext.DrawRect(aRect);
+
+ OUString sBaseText, sRubyText;
+ m_pParentDlg->GetCurrentText(sBaseText, sRubyText);
+
+ tools::Long nTextHeight = rRenderContext.GetTextHeight();
+ tools::Long nBaseWidth = rRenderContext.GetTextWidth(sBaseText);
+
+ vcl::Font aRubyFont(aFont);
+ aRubyFont.SetFontHeight(aRubyFont.GetFontHeight() * 70 / 100);
+ rRenderContext.SetFont(aRubyFont);
+ tools::Long nRubyWidth = rRenderContext.GetTextWidth(sRubyText);
+ rRenderContext.SetFont(aFont);
+
+ RubyAdjust nAdjust = static_cast<RubyAdjust>(m_pParentDlg->m_xAdjustLB->get_active());
+ //use center if no adjustment is available
+ if (nAdjust > RubyAdjust_INDENT_BLOCK)
+ nAdjust = RubyAdjust_CENTER;
+
+ //which part is stretched ?
+ bool bRubyStretch = nBaseWidth >= nRubyWidth;
+
+ tools::Long nCenter = aWinSize.Width() / 2;
+ tools::Long nHalfWidth = std::max(nBaseWidth, nRubyWidth) / 2;
+ tools::Long nLeftStart = nCenter - nHalfWidth;
+ tools::Long nRightEnd = nCenter + nHalfWidth;
+
+ // Default values for TOP or no selection
+ tools::Long nYRuby = aWinSize.Height() / 4 - nTextHeight / 2;
+ tools::Long nYBase = aWinSize.Height() * 3 / 4 - nTextHeight / 2;
+
+ sal_Int16 nRubyPos = m_pParentDlg->m_xPositionLB->get_active();
+ if (nRubyPos == 1) // BOTTOM
+ {
+ tools::Long nTmp = nYRuby;
+ nYRuby = nYBase;
+ nYBase = nTmp;
+ }
+ else if (nRubyPos == 2) // RIGHT ( vertically )
+ {
+ // Align the ruby text and base text to the vertical center.
+ nYBase = (aWinSize.Height() - nTextHeight) / 2;
+ nYRuby = (aWinSize.Height() - nRubyWidth) / 2;
+
+ // Align the ruby text at the right side of the base text
+ nAdjust = RubyAdjust_RIGHT;
+ nHalfWidth = nBaseWidth / 2;
+ nLeftStart = nCenter - nHalfWidth;
+ nRightEnd = nCenter + nHalfWidth + nRubyWidth + nTextHeight;
+ // Render base text first, then render ruby text on the right.
+ bRubyStretch = true;
+
+ aRubyFont.SetVertical(true);
+ aRubyFont.SetOrientation(2700_deg10);
+ }
+
+ tools::Long nYOutput;
+ tools::Long nOutTextWidth;
+ OUString sOutputText;
+
+ if (bRubyStretch)
+ {
+ rRenderContext.DrawText(Point(nLeftStart, nYBase), sBaseText);
+ nYOutput = nYRuby;
+ sOutputText = sRubyText;
+ nOutTextWidth = nRubyWidth;
+ rRenderContext.SetFont(aRubyFont);
+ }
+ else
+ {
+ rRenderContext.SetFont(aRubyFont);
+ rRenderContext.DrawText(Point(nLeftStart, nYRuby), sRubyText);
+ nYOutput = nYBase;
+ sOutputText = sBaseText;
+ nOutTextWidth = nBaseWidth;
+ rRenderContext.SetFont(aFont);
+ }
+
+ switch (nAdjust)
+ {
+ case RubyAdjust_LEFT:
+ rRenderContext.DrawText(Point(nLeftStart, nYOutput), sOutputText);
+ break;
+ case RubyAdjust_RIGHT:
+ rRenderContext.DrawText(Point(nRightEnd - nOutTextWidth, nYOutput), sOutputText);
+ break;
+ case RubyAdjust_INDENT_BLOCK:
+ {
+ tools::Long nCharWidth = rRenderContext.GetTextWidth("X");
+ if (nOutTextWidth < (nRightEnd - nLeftStart - nCharWidth))
+ {
+ nCharWidth /= 2;
+ nLeftStart += nCharWidth;
+ nRightEnd -= nCharWidth;
+ }
+ [[fallthrough]];
+ }
+ case RubyAdjust_BLOCK:
+ {
+ if (sOutputText.getLength() > 1)
+ {
+ sal_Int32 nCount = sOutputText.getLength();
+ tools::Long nSpace
+ = ((nRightEnd - nLeftStart) - rRenderContext.GetTextWidth(sOutputText))
+ / (nCount - 1);
+ for (sal_Int32 i = 0; i < nCount; i++)
+ {
+ OUString sChar(sOutputText[i]);
+ rRenderContext.DrawText(Point(nLeftStart, nYOutput), sChar);
+ tools::Long nCharWidth = rRenderContext.GetTextWidth(sChar);
+ nLeftStart += nCharWidth + nSpace;
+ }
+ break;
+ }
+ [[fallthrough]];
+ }
+ case RubyAdjust_CENTER:
+ rRenderContext.DrawText(Point(nCenter - nOutTextWidth / 2, nYOutput), sOutputText);
+ break;
+ default:
+ break;
+ }
+ rRenderContext.Pop();
+}
+
+void RubyPreview::SetDrawingArea(weld::DrawingArea* pDrawingArea)
+{
+ pDrawingArea->set_size_request(pDrawingArea->get_approximate_digit_width() * 40,
+ pDrawingArea->get_text_height() * 7);
+ CustomWidgetController::SetDrawingArea(pDrawingArea);
+}
+
+IMPL_LINK(SvxRubyDialog, KeyUpDownHdl_Impl, const KeyEvent&, rKEvt, bool)
+{
+ bool bHandled = false;
+ const vcl::KeyCode& rKeyCode = rKEvt.GetKeyCode();
+ sal_uInt16 nCode = rKeyCode.GetCode();
+ if (KEY_UP == nCode || KEY_DOWN == nCode)
+ {
+ sal_Int32 nParam = KEY_UP == nCode ? -1 : 1;
+ bHandled = EditJumpHdl_Impl(nParam);
+ }
+ return bHandled;
+}
+
+IMPL_LINK(SvxRubyDialog, KeyUpDownTabHdl_Impl, const KeyEvent&, rKEvt, bool)
+{
+ bool bHandled = false;
+ const vcl::KeyCode& rKeyCode = rKEvt.GetKeyCode();
+ sal_uInt16 nMod = rKeyCode.GetModifier();
+ sal_uInt16 nCode = rKeyCode.GetCode();
+ if (nCode == KEY_TAB && (!nMod || KEY_SHIFT == nMod))
+ {
+ sal_Int32 nParam = KEY_SHIFT == nMod ? -1 : 1;
+ if (EditScrollHdl_Impl(nParam))
+ bHandled = true;
+ }
+ if (!bHandled)
+ bHandled = KeyUpDownHdl_Impl(rKEvt);
+ return bHandled;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/dialog/rulritem.cxx b/svx/source/dialog/rulritem.cxx
new file mode 100644
index 000000000..2e1dac984
--- /dev/null
+++ b/svx/source/dialog/rulritem.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 <svx/svxids.hrc>
+#include <svx/rulritem.hxx>
+#include <svx/unomid.hxx>
+#include <tools/debug.hxx>
+#include <tools/mapunit.hxx>
+#include <tools/UnitConversion.hxx>
+#include <osl/diagnose.h>
+#include <sal/log.hxx>
+#include <com/sun/star/awt/Rectangle.hpp>
+#include <com/sun/star/frame/status/LeftRightMargin.hpp>
+#include <com/sun/star/frame/status/UpperLowerMargin.hpp>
+
+SfxPoolItem* SvxPagePosSizeItem::CreateDefault() { return new SvxPagePosSizeItem; }
+SfxPoolItem* SvxLongLRSpaceItem::CreateDefault() { return new SvxLongLRSpaceItem; }
+SfxPoolItem* SvxLongULSpaceItem::CreateDefault() { return new SvxLongULSpaceItem; }
+SfxPoolItem* SvxColumnItem::CreateDefault() { return new SvxColumnItem; }
+SfxPoolItem* SvxObjectItem::CreateDefault() { SAL_WARN( "svx", "No SvxObjectItem factory available"); return nullptr; }
+
+/* SvxLongLRSpaceItem */
+
+bool SvxLongLRSpaceItem::operator==( const SfxPoolItem& rCmp) const
+{
+ return SfxPoolItem::operator==(rCmp) &&
+ mlLeft == static_cast<const SvxLongLRSpaceItem &>(rCmp).mlLeft &&
+ mlRight == static_cast<const SvxLongLRSpaceItem &>(rCmp).mlRight;
+}
+
+bool SvxLongLRSpaceItem::QueryValue( css::uno::Any& rVal, sal_uInt8 nMemberId ) const
+{
+ bool bConvert = 0!=(nMemberId&CONVERT_TWIPS);
+ nMemberId &= ~CONVERT_TWIPS;
+
+ sal_Int32 nVal;
+ switch( nMemberId )
+ {
+ case 0:
+ {
+ css::frame::status::LeftRightMargin aLeftRightMargin;
+ aLeftRightMargin.Left = bConvert ? convertTwipToMm100( mlLeft ) : mlLeft;
+ aLeftRightMargin.Right = bConvert ? convertTwipToMm100( mlRight ) : mlRight;
+ rVal <<= aLeftRightMargin;
+ return true;
+ }
+
+ case MID_LEFT:
+ nVal = mlLeft;
+ break;
+ case MID_RIGHT:
+ nVal = mlRight;
+ break;
+ default:
+ OSL_FAIL("Wrong MemberId!");
+ return false;
+ }
+
+ if ( bConvert )
+ nVal = convertTwipToMm100( nVal );
+
+ rVal <<= nVal;
+ return true;
+}
+
+bool SvxLongLRSpaceItem::PutValue( const css::uno::Any& rVal, sal_uInt8 nMemberId )
+{
+ bool bConvert = 0!=(nMemberId&CONVERT_TWIPS);
+ nMemberId &= ~CONVERT_TWIPS;
+
+ sal_Int32 nVal = 0;
+ if ( nMemberId == 0 )
+ {
+ css::frame::status::LeftRightMargin aLeftRightMargin;
+ if ( rVal >>= aLeftRightMargin )
+ {
+ mlLeft = bConvert ? o3tl::toTwips(aLeftRightMargin.Left, o3tl::Length::mm100) : aLeftRightMargin.Left;
+ mlRight = bConvert ? o3tl::toTwips(aLeftRightMargin.Right, o3tl::Length::mm100) : aLeftRightMargin.Right;
+ return true;
+ }
+ }
+ else if ( rVal >>= nVal )
+ {
+ if ( bConvert )
+ nVal = o3tl::toTwips(nVal, o3tl::Length::mm100);
+
+ switch( nMemberId )
+ {
+ case MID_LEFT:
+ mlLeft = nVal;
+ break;
+ case MID_RIGHT:
+ mlRight = nVal;
+ break;
+ default:
+ OSL_FAIL("Wrong MemberId!");
+ return false;
+ }
+
+ return true;
+ }
+
+ return false;
+}
+
+bool SvxLongLRSpaceItem::GetPresentation(
+ SfxItemPresentation /*ePres*/,
+ MapUnit /*eCoreUnit*/,
+ MapUnit /*ePresUnit*/,
+ OUString& /*rText*/,
+ const IntlWrapper& /*rWrapper*/) const
+{
+ return false;
+}
+
+SvxLongLRSpaceItem* SvxLongLRSpaceItem::Clone(SfxItemPool *) const
+{
+ return new SvxLongLRSpaceItem(*this);
+}
+
+SvxLongLRSpaceItem::SvxLongLRSpaceItem(tools::Long lLeft, tools::Long lRight, TypedWhichId<SvxLongLRSpaceItem> nId) :
+ SfxPoolItem (nId),
+ mlLeft (lLeft),
+ mlRight (lRight)
+{}
+
+SvxLongLRSpaceItem::SvxLongLRSpaceItem() :
+ SfxPoolItem (0),
+ mlLeft (0),
+ mlRight (0)
+{}
+
+void SvxLongLRSpaceItem::SetLeft(tools::Long lArgLeft)
+{
+ mlLeft = lArgLeft;
+}
+
+void SvxLongLRSpaceItem::SetRight(tools::Long lArgRight)
+{
+ mlRight = lArgRight;
+}
+
+/* SvxLongULSpaceItem */
+
+bool SvxLongULSpaceItem::operator==( const SfxPoolItem& rCmp) const
+{
+ return SfxPoolItem::operator==(rCmp) &&
+ mlLeft == static_cast<const SvxLongULSpaceItem&>(rCmp).mlLeft &&
+ mlRight == static_cast<const SvxLongULSpaceItem&>(rCmp).mlRight;
+}
+
+bool SvxLongULSpaceItem::QueryValue( css::uno::Any& rVal, sal_uInt8 nMemberId ) const
+{
+ bool bConvert = 0!=(nMemberId&CONVERT_TWIPS);
+ nMemberId &= ~CONVERT_TWIPS;
+
+ sal_Int32 nVal;
+ switch( nMemberId )
+ {
+ case 0:
+ {
+ css::frame::status::UpperLowerMargin aUpperLowerMargin;
+ aUpperLowerMargin.Upper = bConvert ? convertTwipToMm100( mlLeft ) : mlLeft;
+ aUpperLowerMargin.Lower = bConvert ? convertTwipToMm100( mlRight ) : mlRight;
+ rVal <<= aUpperLowerMargin;
+ return true;
+ }
+
+ case MID_UPPER:
+ nVal = mlLeft;
+ break;
+ case MID_LOWER:
+ nVal = mlRight;
+ break;
+ default: OSL_FAIL("Wrong MemberId!"); return false;
+ }
+
+ if ( bConvert )
+ nVal = convertTwipToMm100( nVal );
+
+ rVal <<= nVal;
+ return true;
+}
+
+bool SvxLongULSpaceItem::PutValue( const css::uno::Any& rVal, sal_uInt8 nMemberId )
+{
+ bool bConvert = 0!=(nMemberId&CONVERT_TWIPS);
+ nMemberId &= ~CONVERT_TWIPS;
+
+ sal_Int32 nVal = 0;
+ if ( nMemberId == 0 )
+ {
+ css::frame::status::UpperLowerMargin aUpperLowerMargin;
+ if ( rVal >>= aUpperLowerMargin )
+ {
+ mlLeft = bConvert ? o3tl::toTwips(aUpperLowerMargin.Upper, o3tl::Length::mm100) : aUpperLowerMargin.Upper;
+ mlRight = bConvert ? o3tl::toTwips(aUpperLowerMargin.Lower, o3tl::Length::mm100) : aUpperLowerMargin.Lower;
+ return true;
+ }
+ }
+ else if ( rVal >>= nVal )
+ {
+ if ( bConvert )
+ nVal = o3tl::toTwips(nVal, o3tl::Length::mm100);
+
+ switch( nMemberId )
+ {
+ case MID_UPPER:
+ mlLeft = nVal;
+ break;
+ case MID_LOWER:
+ mlRight = nVal;
+ break;
+ default:
+ OSL_FAIL("Wrong MemberId!");
+ return false;
+ }
+
+ return true;
+ }
+
+ return false;
+}
+
+bool SvxLongULSpaceItem::GetPresentation(
+ SfxItemPresentation /*ePres*/,
+ MapUnit /*eCoreUnit*/,
+ MapUnit /*ePresUnit*/,
+ OUString& /*rText*/,
+ const IntlWrapper& /*rWrapper*/ ) const
+{
+ return false;
+}
+
+SvxLongULSpaceItem* SvxLongULSpaceItem::Clone(SfxItemPool *) const
+{
+ return new SvxLongULSpaceItem(*this);
+}
+
+SvxLongULSpaceItem::SvxLongULSpaceItem(tools::Long lLeft, tools::Long lRight, TypedWhichId<SvxLongULSpaceItem> nId) :
+ SfxPoolItem (nId),
+ mlLeft (lLeft),
+ mlRight (lRight)
+{}
+
+SvxLongULSpaceItem::SvxLongULSpaceItem() :
+ SfxPoolItem (0),
+ mlLeft (0),
+ mlRight (0)
+{}
+
+
+void SvxLongULSpaceItem::SetUpper(tools::Long lArgLeft)
+{
+ mlLeft = lArgLeft;
+}
+
+void SvxLongULSpaceItem::SetLower(tools::Long lArgRight)
+{
+ mlRight = lArgRight;
+}
+
+/* SvxPagePosSizeItem */
+
+bool SvxPagePosSizeItem::operator==( const SfxPoolItem& rCmp) const
+{
+ return SfxPoolItem::operator==(rCmp) &&
+ aPos == static_cast<const SvxPagePosSizeItem &>(rCmp).aPos &&
+ lWidth == static_cast<const SvxPagePosSizeItem &>(rCmp).lWidth &&
+ lHeight == static_cast<const SvxPagePosSizeItem &>(rCmp).lHeight;
+}
+
+bool SvxPagePosSizeItem::QueryValue( css::uno::Any& rVal, sal_uInt8 nMemberId ) const
+{
+ nMemberId &= ~CONVERT_TWIPS;
+
+ sal_Int32 nVal;
+ switch ( nMemberId )
+ {
+ case 0 :
+ {
+ css::awt::Rectangle aPagePosSize;
+ aPagePosSize.X = aPos.X();
+ aPagePosSize.Y = aPos.Y();
+ aPagePosSize.Width = lWidth;
+ aPagePosSize.Height = lHeight;
+ rVal <<= aPagePosSize;
+ return true;
+ }
+
+ case MID_X: nVal = aPos.X(); break;
+ case MID_Y: nVal = aPos.Y(); break;
+ case MID_WIDTH: nVal = lWidth; break;
+ case MID_HEIGHT: nVal = lHeight; break;
+
+ default: OSL_FAIL("Wrong MemberId!"); return false;
+ }
+
+ rVal <<= nVal;
+ return true;
+}
+
+bool SvxPagePosSizeItem::PutValue( const css::uno::Any& rVal, sal_uInt8 nMemberId )
+{
+ nMemberId &= ~CONVERT_TWIPS;
+
+ sal_Int32 nVal = 0;
+ if ( nMemberId == 0 )
+ {
+ css::awt::Rectangle aPagePosSize;
+ if ( rVal >>= aPagePosSize )
+ {
+ aPos.setX( aPagePosSize.X );
+ aPos.setY( aPagePosSize.Y );
+ lWidth = aPagePosSize.Width;
+ lHeight = aPagePosSize.Height;
+ return true;
+ }
+ else
+ return false;
+ }
+ else if ( rVal >>= nVal )
+ {
+ switch ( nMemberId )
+ {
+ case MID_X: aPos.setX( nVal ); break;
+ case MID_Y: aPos.setY( nVal ); break;
+ case MID_WIDTH: lWidth = nVal; break;
+ case MID_HEIGHT: lHeight = nVal; break;
+
+ default: OSL_FAIL("Wrong MemberId!"); return false;
+ }
+
+ return true;
+ }
+
+ return false;
+}
+
+bool SvxPagePosSizeItem::GetPresentation(
+ SfxItemPresentation /*ePres*/,
+ MapUnit /*eCoreUnit*/,
+ MapUnit /*ePresUnit*/,
+ OUString& /*rText*/,
+ const IntlWrapper& /*rWrapper*/ ) const
+{
+ return false;
+}
+
+SvxPagePosSizeItem* SvxPagePosSizeItem::Clone(SfxItemPool *) const
+{
+ return new SvxPagePosSizeItem(*this);
+}
+
+SvxPagePosSizeItem::SvxPagePosSizeItem(const Point &rP, tools::Long lW, tools::Long lH) :
+ SfxPoolItem (SID_RULER_PAGE_POS),
+ aPos (rP),
+ lWidth (lW),
+ lHeight (lH)
+{}
+
+SvxPagePosSizeItem::SvxPagePosSizeItem() :
+ SfxPoolItem (0),
+ aPos (0, 0),
+ lWidth (0),
+ lHeight (0)
+{}
+
+/* SvxColumnItem */
+
+bool SvxColumnItem::operator==(const SfxPoolItem& rCmp) const
+{
+ if(!SfxPoolItem::operator==(rCmp) ||
+ nActColumn != static_cast<const SvxColumnItem&>(rCmp).nActColumn ||
+ nLeft != static_cast<const SvxColumnItem&>(rCmp).nLeft ||
+ nRight != static_cast<const SvxColumnItem&>(rCmp).nRight ||
+ bTable != static_cast<const SvxColumnItem&>(rCmp).bTable ||
+ Count() != static_cast<const SvxColumnItem&>(rCmp).Count())
+ return false;
+
+ const sal_uInt16 nCount = static_cast<const SvxColumnItem&>(rCmp).Count();
+ for(sal_uInt16 i = 0; i < nCount;++i)
+ {
+ if( (*this)[i] != static_cast<const SvxColumnItem&>(rCmp)[i] )
+ return false;
+ }
+ return true;
+}
+
+SvxColumnItem::SvxColumnItem( sal_uInt16 nAct ) :
+ SfxPoolItem (SID_RULER_BORDERS),
+ nLeft (0),
+ nRight (0),
+ nActColumn (nAct),
+ bTable (false),
+ bOrtho (true)
+
+{}
+
+SvxColumnItem::SvxColumnItem( sal_uInt16 nActCol, sal_uInt16 left, sal_uInt16 right ) :
+ SfxPoolItem (SID_RULER_BORDERS),
+ nLeft (left),
+ nRight (right),
+ nActColumn (nActCol),
+ bTable (true),
+ bOrtho (true)
+{}
+
+bool SvxColumnItem::GetPresentation(
+ SfxItemPresentation /*ePres*/,
+ MapUnit /*eCoreUnit*/,
+ MapUnit /*ePresUnit*/,
+ OUString& /*rText*/,
+ const IntlWrapper& /*rWrapper*/ ) const
+{
+ return false;
+}
+
+SvxColumnItem* SvxColumnItem::Clone(SfxItemPool* /*pPool*/) const
+{
+ return new SvxColumnItem(*this);
+}
+
+bool SvxColumnItem::CalcOrtho() const
+{
+ const sal_uInt16 nCount = Count();
+ DBG_ASSERT(nCount >= 2, "no columns");
+ if(nCount < 2)
+ return false;
+
+ tools::Long nColWidth = (*this)[0].GetWidth();
+ for(sal_uInt16 i = 1; i < nCount; ++i) {
+ if( (*this)[i].GetWidth() != nColWidth)
+ return false;
+ }
+ //!! Wide divider
+ return true;
+}
+
+bool SvxColumnItem::QueryValue( css::uno::Any& rVal, sal_uInt8 nMemberId ) const
+{
+ nMemberId &= ~CONVERT_TWIPS;
+ switch ( nMemberId )
+ {
+ case MID_COLUMNARRAY:
+ return false;
+ case MID_RIGHT:
+ rVal <<= nRight;
+ break;
+ case MID_LEFT:
+ rVal <<= nLeft;
+ break;
+ case MID_ORTHO:
+ rVal <<= bOrtho;
+ break;
+ case MID_ACTUAL:
+ rVal <<= static_cast<sal_Int32>(nActColumn);
+ break;
+ case MID_TABLE:
+ rVal <<= bTable;
+ break;
+ default:
+ // SfxDispatchController_Impl::StateChanged calls this with hardcoded 0 triggering this;
+ OSL_FAIL("Wrong MemberId!");
+ return false;
+ }
+
+ return true;
+}
+
+bool SvxColumnItem::PutValue( const css::uno::Any& rVal, sal_uInt8 nMemberId )
+{
+ nMemberId &= ~CONVERT_TWIPS;
+ sal_Int32 nVal = 0;
+ switch ( nMemberId )
+ {
+ case MID_COLUMNARRAY:
+ {
+ return false;
+ }
+ case MID_RIGHT:
+ rVal >>= nRight;
+ break;
+ case MID_LEFT:
+ rVal >>= nLeft;
+ break;
+ case MID_ORTHO:
+ rVal >>= nVal;
+ bOrtho = static_cast<bool>(nVal);
+ break;
+ case MID_ACTUAL:
+ rVal >>= nVal;
+ nActColumn = static_cast<sal_uInt16>(nVal);
+ break;
+ case MID_TABLE:
+ rVal >>= nVal;
+ bTable = static_cast<bool>(nVal);
+ break;
+ default:
+ OSL_FAIL("Wrong MemberId!");
+ return false;
+ }
+
+ return true;
+}
+
+sal_uInt16 SvxColumnItem::Count() const
+{
+ return aColumns.size();
+}
+
+SvxColumnDescription& SvxColumnItem::At(sal_uInt16 index)
+{
+ return aColumns[index];
+}
+
+SvxColumnDescription& SvxColumnItem::GetActiveColumnDescription()
+{
+ return aColumns[GetActColumn()];
+}
+
+SvxColumnDescription& SvxColumnItem::operator[](sal_uInt16 index)
+{
+ return aColumns[index];
+}
+
+const SvxColumnDescription& SvxColumnItem::operator[](sal_uInt16 index) const
+{
+ return aColumns[index];
+}
+
+void SvxColumnItem::Append(const SvxColumnDescription &rDesc)
+{
+ aColumns.push_back(rDesc);
+}
+
+void SvxColumnItem::SetLeft(tools::Long left)
+{
+ nLeft = left;
+}
+
+void SvxColumnItem::SetRight(tools::Long right)
+{
+ nRight = right;
+}
+
+
+bool SvxColumnItem::IsFirstAct() const
+{
+ return nActColumn == 0;
+}
+
+bool SvxColumnItem::IsLastAct() const
+{
+ return nActColumn == Count() - 1;
+}
+
+SvxColumnDescription::SvxColumnDescription(tools::Long start, tools::Long end, bool bVis) :
+ nStart (start),
+ nEnd (end),
+ bVisible (bVis),
+ nEndMin (0),
+ nEndMax (0)
+{}
+
+SvxColumnDescription::SvxColumnDescription(tools::Long start, tools::Long end, tools::Long endMin, tools::Long endMax, bool bVis) :
+ nStart (start),
+ nEnd (end),
+ bVisible (bVis),
+ // fdo#85858 hack: clamp these to smaller value to prevent overflow
+ nEndMin(std::min<tools::Long>(endMin, std::numeric_limits<unsigned short>::max())),
+ nEndMax(std::min<tools::Long>(endMax, std::numeric_limits<unsigned short>::max()))
+{}
+
+bool SvxColumnDescription::operator==(const SvxColumnDescription& rCmp) const
+{
+ return nStart == rCmp.nStart
+ && bVisible == rCmp.bVisible
+ && nEnd == rCmp.nEnd
+ && nEndMin == rCmp.nEndMin
+ && nEndMax == rCmp.nEndMax;
+}
+
+bool SvxColumnDescription::operator!=(const SvxColumnDescription& rCmp) const
+{
+ return !operator==(rCmp);
+}
+
+tools::Long SvxColumnDescription::GetWidth() const
+{
+ return nEnd - nStart;
+}
+
+/* SvxColumnItem */
+void SvxColumnItem::SetOrtho(bool bVal)
+{
+ bOrtho = bVal;
+}
+
+bool SvxColumnItem::IsConsistent() const
+{
+ return nActColumn < aColumns.size();
+}
+
+bool SvxObjectItem::operator==( const SfxPoolItem& rCmp ) const
+{
+ return SfxPoolItem::operator==(rCmp) &&
+ nStartX == static_cast<const SvxObjectItem&>(rCmp).nStartX &&
+ nEndX == static_cast<const SvxObjectItem&>(rCmp).nEndX &&
+ nStartY == static_cast<const SvxObjectItem&>(rCmp).nStartY &&
+ nEndY == static_cast<const SvxObjectItem&>(rCmp).nEndY &&
+ bLimits == static_cast<const SvxObjectItem&>(rCmp).bLimits;
+}
+
+bool SvxObjectItem::GetPresentation(
+ SfxItemPresentation /*ePres*/,
+ MapUnit /*eCoreUnit*/,
+ MapUnit /*ePresUnit*/,
+ OUString& /*rText*/,
+ const IntlWrapper& /*rWrapper*/ ) const
+{
+ return false;
+}
+
+SvxObjectItem* SvxObjectItem::Clone(SfxItemPool *) const
+{
+ return new SvxObjectItem(*this);
+}
+
+SvxObjectItem::SvxObjectItem( tools::Long nSX, tools::Long nEX,
+ tools::Long nSY, tools::Long nEY ) :
+ SfxPoolItem (SID_RULER_OBJECT),
+ nStartX (nSX),
+ nEndX (nEX),
+ nStartY (nSY),
+ nEndY (nEY),
+ bLimits (false)
+{}
+
+bool SvxObjectItem::QueryValue( css::uno::Any& rVal, sal_uInt8 nMemberId ) const
+{
+ nMemberId &= ~CONVERT_TWIPS;
+ switch (nMemberId)
+ {
+ case MID_START_X:
+ rVal <<= nStartX;
+ break;
+ case MID_START_Y:
+ rVal <<= nStartY;
+ break;
+ case MID_END_X:
+ rVal <<= nEndX;
+ break;
+ case MID_END_Y:
+ rVal <<= nEndY;
+ break;
+ case MID_LIMIT:
+ rVal <<= bLimits;
+ break;
+ default:
+ OSL_FAIL( "Wrong MemberId" );
+ return false;
+ }
+
+ return true;
+}
+
+bool SvxObjectItem::PutValue( const css::uno::Any& rVal, sal_uInt8 nMemberId )
+{
+ nMemberId &= ~CONVERT_TWIPS;
+ bool bRet = false;
+ switch (nMemberId)
+ {
+ case MID_START_X:
+ bRet = (rVal >>= nStartX);
+ break;
+ case MID_START_Y:
+ bRet = (rVal >>= nStartY);
+ break;
+ case MID_END_X:
+ bRet = (rVal >>= nEndX);
+ break;
+ case MID_END_Y:
+ bRet = (rVal >>= nEndY);
+ break;
+ case MID_LIMIT:
+ bRet = (rVal >>= bLimits);
+ break;
+ default: OSL_FAIL( "Wrong MemberId" );
+ }
+
+ return bRet;
+}
+
+
+void SvxObjectItem::SetStartX(tools::Long lValue)
+{
+ nStartX = lValue;
+}
+
+void SvxObjectItem::SetEndX(tools::Long lValue)
+{
+ nEndX = lValue;
+}
+
+void SvxObjectItem::SetStartY(tools::Long lValue)
+{
+ nStartY = lValue;
+}
+
+void SvxObjectItem::SetEndY(tools::Long lValue)
+{
+ nEndY = lValue;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/dialog/samecontentlistbox.cxx b/svx/source/dialog/samecontentlistbox.cxx
new file mode 100644
index 000000000..66c628a1e
--- /dev/null
+++ b/svx/source/dialog/samecontentlistbox.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 <svx/dialmgr.hxx>
+#include <svx/samecontentlistbox.hxx>
+#include <samecontent.hrc>
+
+namespace SameContentListBox
+{
+ void Fill(weld::ComboBox& rComboBox)
+ {
+ rComboBox.clear();
+ for (size_t i = 0; i < SAL_N_ELEMENTS(RID_SVXSTRARY_SAMECONTENT); ++i)
+ {
+ OUString aStr = SvxResId(RID_SVXSTRARY_SAMECONTENT[i].first);
+ sal_uInt32 nData = RID_SVXSTRARY_SAMECONTENT[i].second;
+ rComboBox.append(OUString::number(nData), aStr);
+ }
+ rComboBox.set_active(0);
+ rComboBox.set_size_request(150, -1);
+ }
+}
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/dialog/searchcharmap.cxx b/svx/source/dialog/searchcharmap.cxx
new file mode 100644
index 000000000..2d8eeaab8
--- /dev/null
+++ b/svx/source/dialog/searchcharmap.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 <config_wasm_strip.h>
+
+#include <vcl/event.hxx>
+#include <vcl/svapp.hxx>
+#include <vcl/settings.hxx>
+#include <vcl/virdev.hxx>
+
+#include <svx/ucsubset.hxx>
+#include <unordered_map>
+
+#include <svx/searchcharmap.hxx>
+
+#include <charmapacc.hxx>
+
+#include <rtl/ustrbuf.hxx>
+
+using namespace ::com::sun::star::accessibility;
+using namespace ::com::sun::star::uno;
+using namespace ::com::sun::star;
+
+
+SvxSearchCharSet::SvxSearchCharSet(std::unique_ptr<weld::ScrolledWindow> pScrolledWindow, const VclPtr<VirtualDevice>& rVirDev)
+ : SvxShowCharSet(std::move(pScrolledWindow), rVirDev)
+ , nCount(0)
+{
+}
+
+int SvxSearchCharSet::LastInView() const
+{
+ int nIndex = FirstInView();
+ nIndex += ROW_COUNT * COLUMN_COUNT - 1;
+ int nCompare = nCount - 1;
+ if (nIndex > nCompare)
+ nIndex = nCompare;
+ return nIndex;
+}
+
+bool SvxSearchCharSet::KeyInput(const KeyEvent& rKEvt)
+{
+ vcl::KeyCode aCode = rKEvt.GetKeyCode();
+
+ if (aCode.GetModifier())
+ return false;
+
+ int tmpSelected = nSelectedIndex;
+
+ bool bRet = true;
+
+ switch (aCode.GetCode())
+ {
+ case KEY_SPACE:
+ aSelectHdl.Call( this );
+ break;
+ case KEY_LEFT:
+ --tmpSelected;
+ break;
+ case KEY_RIGHT:
+ ++tmpSelected;
+ break;
+ case KEY_UP:
+ tmpSelected -= COLUMN_COUNT;
+ break;
+ case KEY_DOWN:
+ tmpSelected += COLUMN_COUNT;
+ break;
+ case KEY_PAGEUP:
+ tmpSelected -= ROW_COUNT * COLUMN_COUNT;
+ break;
+ case KEY_PAGEDOWN:
+ tmpSelected += ROW_COUNT * COLUMN_COUNT;
+ break;
+ case KEY_HOME:
+ tmpSelected = 0;
+ break;
+ case KEY_END:
+ tmpSelected = nCount - 1;
+ break;
+ case KEY_TAB: // some fonts have a character at these unicode control codes
+ case KEY_ESCAPE:
+ case KEY_RETURN:
+ bRet = false;
+ tmpSelected = - 1; // mark as invalid
+ break;
+ default:
+ tmpSelected = -1;
+ bRet = false;
+ break;
+ }
+
+ if ( tmpSelected >= 0 )
+ {
+ SelectIndex( tmpSelected, true );
+ aPreSelectHdl.Call( this );
+ }
+
+ return bRet;
+}
+
+void SvxSearchCharSet::SelectCharacter( const Subset* sub )
+{
+ if (!mxFontCharMap.is())
+ RecalculateFont(*mxVirDev);
+
+ // get next available char of current font
+ sal_UCS4 cChar = sub->GetRangeMin();
+ int nMapIndex = 0;
+
+ while(cChar <= sub->GetRangeMax() && nMapIndex == 0)
+ {
+ auto it = std::find_if(m_aItemList.begin(), m_aItemList.end(),
+ [&cChar](const std::pair<const sal_Int32, sal_UCS4>& rItem) { return rItem.second == cChar; });
+ if (it != m_aItemList.end())
+ nMapIndex = it->first;
+ cChar++;
+ }
+
+ if(nMapIndex == 0)
+ SelectIndex( 0 );
+ else
+ SelectIndex( nMapIndex );
+ aHighHdl.Call(this);
+ // move selected item to top row if not in focus
+ //TO.DO aVscrollSB->SetThumbPos( nMapIndex / COLUMN_COUNT );
+ Invalidate();
+}
+
+void SvxSearchCharSet::Paint(vcl::RenderContext& rRenderContext, const tools::Rectangle&)
+{
+ InitSettings(rRenderContext);
+ RecalculateFont(rRenderContext);
+ DrawChars_Impl(rRenderContext, FirstInView(), LastInView());
+}
+
+void SvxSearchCharSet::DrawChars_Impl(vcl::RenderContext& rRenderContext, int n1, int n2)
+{
+ if (n1 > LastInView() || n2 < FirstInView())
+ return;
+
+ Size aOutputSize(GetOutputSizePixel());
+
+ const StyleSettings& rStyleSettings = Application::GetSettings().GetStyleSettings();
+ const Color aWindowTextColor(rStyleSettings.GetFieldTextColor());
+ Color aHighlightColor(rStyleSettings.GetHighlightColor());
+ Color aHighlightTextColor(rStyleSettings.GetHighlightTextColor());
+ Color aFaceColor(rStyleSettings.GetFaceColor());
+ Color aLightColor(rStyleSettings.GetLightColor());
+ Color aShadowColor(rStyleSettings.GetShadowColor());
+
+ int i;
+ rRenderContext.SetLineColor(aShadowColor);
+ for (i = 1; i < COLUMN_COUNT; ++i)
+ {
+ rRenderContext.DrawLine(Point(nX * i + m_nXGap, 0),
+ Point(nX * i + m_nXGap, aOutputSize.Height()));
+ }
+ for (i = 1; i < ROW_COUNT; ++i)
+ {
+ rRenderContext.DrawLine(Point(0, nY * i + m_nYGap),
+ Point(aOutputSize.Width(), nY * i + m_nYGap));
+ }
+
+ int nTextHeight = rRenderContext.GetTextHeight();
+ tools::Rectangle aBoundRect;
+ for (i = n1; i <= n2; ++i)
+ {
+ Point pix = MapIndexToPixel(i);
+ int x = pix.X();
+ int y = pix.Y();
+
+ OUStringBuffer buf;
+ std::unordered_map<sal_Int32, sal_UCS4>::const_iterator got = m_aItemList.find (i);
+ sal_UCS4 sName;
+
+ if(got == m_aItemList.end())
+ continue;
+ else
+ sName = got->second;
+
+ buf.appendUtf32(sName);
+ OUString aCharStr(buf.makeStringAndClear());
+ int nTextWidth = rRenderContext.GetTextWidth(aCharStr);
+ int tx = x + (nX - nTextWidth + 1) / 2;
+ int ty = y + (nY - nTextHeight + 1) / 2;
+ Point aPointTxTy(tx, ty);
+
+ // adjust position before it gets out of bounds
+ if (rRenderContext.GetTextBoundRect(aBoundRect, aCharStr) && !aBoundRect.IsEmpty())
+ {
+ // zero advance width => use ink width to center glyph
+ if (!nTextWidth)
+ {
+ aPointTxTy.setX( x - aBoundRect.Left() + (nX - aBoundRect.GetWidth() + 1) / 2 );
+ }
+
+ aBoundRect += aPointTxTy;
+
+ // shift back vertically if needed
+ int nYLDelta = aBoundRect.Top() - y;
+ int nYHDelta = (y + nY) - aBoundRect.Bottom();
+ if (nYLDelta <= 0)
+ aPointTxTy.AdjustY( -(nYLDelta - 1) );
+ else if (nYHDelta <= 0)
+ aPointTxTy.AdjustY(nYHDelta - 1 );
+
+ // shift back horizontally if needed
+ int nXLDelta = aBoundRect.Left() - x;
+ int nXHDelta = (x + nX) - aBoundRect.Right();
+ if (nXLDelta <= 0)
+ aPointTxTy.AdjustX( -(nXLDelta - 1) );
+ else if (nXHDelta <= 0)
+ aPointTxTy.AdjustX(nXHDelta - 1 );
+ }
+
+ Color aTextCol = rRenderContext.GetTextColor();
+ if (i != nSelectedIndex)
+ {
+ rRenderContext.SetTextColor(aWindowTextColor);
+ rRenderContext.DrawText(aPointTxTy, aCharStr);
+ }
+ else
+ {
+ Color aLineCol = rRenderContext.GetLineColor();
+ Color aFillCol = rRenderContext.GetFillColor();
+ rRenderContext.SetLineColor();
+ Point aPointUL(x + 1, y + 1);
+ if (HasFocus())
+ {
+ rRenderContext.SetFillColor(aHighlightColor);
+ rRenderContext.DrawRect(getGridRectangle(aPointUL, aOutputSize));
+
+ rRenderContext.SetTextColor(aHighlightTextColor);
+ rRenderContext.DrawText(aPointTxTy, aCharStr);
+ }
+ else
+ {
+ rRenderContext.SetFillColor(aFaceColor);
+ rRenderContext.DrawRect(getGridRectangle(aPointUL, aOutputSize));
+
+ rRenderContext.SetLineColor(aLightColor);
+ rRenderContext.DrawLine(aPointUL, Point(x + nX - 1, y + 1));
+ rRenderContext.DrawLine(aPointUL, Point(x + 1, y + nY - 1));
+
+ rRenderContext.SetLineColor(aShadowColor);
+ rRenderContext.DrawLine(Point(x + 1, y + nY - 1), Point(x + nX - 1, y + nY - 1));
+ rRenderContext.DrawLine(Point(x + nX - 1, y + nY - 1), Point(x + nX - 1, y + 1));
+
+ rRenderContext.DrawText(aPointTxTy, aCharStr);
+ }
+ rRenderContext.SetLineColor(aLineCol);
+ rRenderContext.SetFillColor(aFillCol);
+ }
+ rRenderContext.SetTextColor(aTextCol);
+ }
+}
+
+sal_UCS4 SvxSearchCharSet::GetSelectCharacter() const
+{
+ if( nSelectedIndex >= 0 )
+ {
+ std::unordered_map<sal_Int32,sal_UCS4>::const_iterator got = m_aItemList.find (nSelectedIndex);
+
+ if(got == m_aItemList.end())
+ return 1;
+ else
+ return got->second;
+ }
+ return 1;
+}
+
+void SvxSearchCharSet::RecalculateFont(vcl::RenderContext& rRenderContext)
+{
+ if (!mbRecalculateFont)
+ return;
+
+ Size aSize(GetOutputSizePixel());
+
+ vcl::Font aFont = rRenderContext.GetFont();
+ aFont.SetWeight(WEIGHT_LIGHT);
+ aFont.SetAlignment(ALIGN_TOP);
+ int nFontHeight = (aSize.Height() - 5) * 2 / (3 * ROW_COUNT);
+ maFontSize = rRenderContext.PixelToLogic(Size(0, nFontHeight));
+ aFont.SetFontSize(maFontSize);
+ aFont.SetTransparent(true);
+ rRenderContext.SetFont(aFont);
+ rRenderContext.GetFontCharMap(mxFontCharMap);
+ getFavCharacterList();
+
+ nX = aSize.Width() / COLUMN_COUNT;
+ nY = aSize.Height() / ROW_COUNT;
+
+ UpdateScrollRange();
+
+ // rearrange CharSet element in sync with nX- and nY-multiples
+ Size aDrawSize(nX * COLUMN_COUNT, nY * ROW_COUNT);
+ m_nXGap = (aSize.Width() - aDrawSize.Width()) / 2;
+ m_nYGap = (aSize.Height() - aDrawSize.Height()) / 2;
+
+ mbRecalculateFont = false;
+}
+
+void SvxSearchCharSet::UpdateScrollRange()
+{
+ //scrollbar settings
+ int nLastRow = (nCount - 1 + COLUMN_COUNT) / COLUMN_COUNT;
+ mxScrollArea->vadjustment_configure(mxScrollArea->vadjustment_get_value(), 0, nLastRow, 1, ROW_COUNT - 1, ROW_COUNT);
+}
+
+void SvxSearchCharSet::SelectIndex(int nNewIndex, bool bFocus)
+{
+ if (!mxFontCharMap.is())
+ RecalculateFont(*mxVirDev);
+
+ if( nNewIndex < 0 )
+ {
+ mxScrollArea->vadjustment_set_value(0);
+ nSelectedIndex = bFocus ? 0 : -1;
+ Invalidate();
+ }
+ else if( nNewIndex < FirstInView() )
+ {
+ // need to scroll up to see selected item
+ int nOldPos = mxScrollArea->vadjustment_get_value();
+ int nDelta = (FirstInView() - nNewIndex + COLUMN_COUNT-1) / COLUMN_COUNT;
+ mxScrollArea->vadjustment_set_value(nOldPos - nDelta);
+ nSelectedIndex = nNewIndex;
+ Invalidate();
+ }
+ else if( nNewIndex > LastInView() )
+ {
+ // need to scroll down to see selected item
+ int nOldPos = mxScrollArea->vadjustment_get_value();
+ int nDelta = (nNewIndex - LastInView() + COLUMN_COUNT) / COLUMN_COUNT;
+ mxScrollArea->vadjustment_set_value(nOldPos + nDelta);
+
+ if( nNewIndex < nCount )
+ {
+ nSelectedIndex = nNewIndex;
+ Invalidate();
+ }
+ else if (nOldPos != mxScrollArea->vadjustment_get_value())
+ {
+ Invalidate();
+ }
+ }
+ else
+ {
+ nSelectedIndex = nNewIndex;
+ Invalidate();
+ }
+
+ if( nSelectedIndex >= 0 )
+ {
+#if 0
+ if( m_xAccessible.is() )
+ {
+ svx::SvxShowCharSetItem* pItem = ImplGetItem(nSelectedIndex);
+ // Don't fire the focus event.
+ if ( bFocus )
+ m_xAccessible->fireEvent( AccessibleEventId::ACTIVE_DESCENDANT_CHANGED, Any(), makeAny(pItem->GetAccessible()) ); // this call assures that m_pItem is set
+ else
+ m_xAccessible->fireEvent( AccessibleEventId::ACTIVE_DESCENDANT_CHANGED_NOFOCUS, Any(), makeAny(pItem->GetAccessible()) ); // this call assures that m_pItem is set
+
+ assert(pItem->m_xItem.is() && "No accessible created!");
+ Any aOldAny, aNewAny;
+ aNewAny <<= AccessibleStateType::FOCUSED;
+ // Don't fire the focus event.
+ if ( bFocus )
+ pItem->m_xItem->fireEvent( AccessibleEventId::STATE_CHANGED, aOldAny, aNewAny );
+
+ aNewAny <<= AccessibleStateType::SELECTED;
+ pItem->m_xItem->fireEvent( AccessibleEventId::STATE_CHANGED, aOldAny, aNewAny );
+ }
+#endif
+ }
+ aHighHdl.Call( this );
+}
+
+SvxSearchCharSet::~SvxSearchCharSet()
+{
+}
+
+svx::SvxShowCharSetItem* SvxSearchCharSet::ImplGetItem( int _nPos )
+{
+ ItemsMap::iterator aFind = m_aItems.find(_nPos);
+ if ( aFind == m_aItems.end() )
+ {
+#if !ENABLE_WASM_STRIP_ACCESSIBILITY
+ OSL_ENSURE(m_xAccessible.is(), "Who wants to create a child of my table without a parent?");
+#endif
+ auto xItem = std::make_shared<svx::SvxShowCharSetItem>(*this,
+ m_xAccessible.get(), sal::static_int_cast< sal_uInt16 >(_nPos));
+ aFind = m_aItems.emplace(_nPos, xItem).first;
+ OUStringBuffer buf;
+ std::unordered_map<sal_Int32,sal_UCS4>::const_iterator got = m_aItemList.find (_nPos);
+ if (got != m_aItemList.end())
+ buf.appendUtf32(got->second);
+ aFind->second->maText = buf.makeStringAndClear();
+ Point pix = MapIndexToPixel( _nPos );
+ aFind->second->maRect = tools::Rectangle( Point( pix.X() + 1, pix.Y() + 1 ), Size(nX-1,nY-1) );
+ }
+
+ return aFind->second.get();
+}
+
+sal_Int32 SvxSearchCharSet::getMaxCharCount() const
+{
+ return nCount;
+}
+
+void SvxSearchCharSet::ClearPreviousData()
+{
+ m_aItemList.clear();
+ nCount = 0;
+ Invalidate();
+}
+
+void SvxSearchCharSet::AppendCharToList(sal_UCS4 sChar)
+{
+ m_aItemList.insert(std::make_pair(nCount++, sChar));
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/dialog/signaturelinehelper.cxx b/svx/source/dialog/signaturelinehelper.cxx
new file mode 100644
index 000000000..2c055e84d
--- /dev/null
+++ b/svx/source/dialog/signaturelinehelper.cxx
@@ -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/.
+ */
+
+#include <svx/signaturelinehelper.hxx>
+
+#include <com/sun/star/drawing/XShape.hpp>
+#include <com/sun/star/graphic/GraphicProvider.hpp>
+#include <com/sun/star/security/DocumentDigitalSignatures.hpp>
+
+#include <comphelper/processfactory.hxx>
+#include <comphelper/propertyvalue.hxx>
+#include <comphelper/sequenceashashmap.hxx>
+#include <comphelper/storagehelper.hxx>
+#include <comphelper/xmlsechelper.hxx>
+#include <config_folders.h>
+#include <rtl/bootstrap.hxx>
+#include <sal/log.hxx>
+#include <sfx2/docfile.hxx>
+#include <sfx2/docfilt.hxx>
+#include <sfx2/objsh.hxx>
+#include <svx/dialmgr.hxx>
+#include <svx/strings.hrc>
+#include <svx/svdmark.hxx>
+#include <svx/svdview.hxx>
+#include <tools/stream.hxx>
+#include <unotools/localedatawrapper.hxx>
+#include <unotools/streamwrap.hxx>
+#include <unotools/syslocale.hxx>
+#include <vcl/weld.hxx>
+
+using namespace com::sun::star;
+
+namespace svx::SignatureLineHelper
+{
+OUString getSignatureImage(const OUString& rType)
+{
+ OUString aType = rType;
+ if (aType.isEmpty())
+ {
+ aType = "signature-line.svg";
+ }
+ OUString aPath("$BRAND_BASE_DIR/" LIBO_SHARE_FOLDER "/filter/" + aType);
+ rtl::Bootstrap::expandMacros(aPath);
+ SvFileStream aStream(aPath, StreamMode::READ);
+ if (aStream.GetError() != ERRCODE_NONE)
+ {
+ SAL_WARN("cui.dialogs", "failed to open " << aType);
+ }
+
+ OString const svg = read_uInt8s_ToOString(aStream, aStream.remainingSize());
+ return OUString::fromUtf8(svg);
+}
+
+uno::Reference<security::XCertificate> getSignatureCertificate(SfxObjectShell* pShell,
+ weld::Window* pParent)
+{
+ if (!pShell)
+ {
+ return {};
+ }
+
+ if (!pParent)
+ {
+ return {};
+ }
+
+ uno::Reference<security::XDocumentDigitalSignatures> xSigner;
+ if (pShell->GetMedium()->GetFilter()->IsAlienFormat())
+ {
+ xSigner = security::DocumentDigitalSignatures::createDefault(
+ comphelper::getProcessComponentContext());
+ }
+ else
+ {
+ OUString const aODFVersion(
+ comphelper::OStorageHelper::GetODFVersionFromStorage(pShell->GetStorage()));
+ xSigner = security::DocumentDigitalSignatures::createWithVersion(
+ comphelper::getProcessComponentContext(), aODFVersion);
+ }
+ xSigner->setParentWindow(pParent->GetXWindow());
+ OUString aDescription;
+ security::CertificateKind certificateKind = security::CertificateKind_NONE;
+ // When signing ooxml, we only want X.509 certificates
+ if (pShell->GetMedium()->GetFilter()->IsAlienFormat())
+ {
+ certificateKind = security::CertificateKind_X509;
+ }
+ uno::Reference<security::XCertificate> xSignCertificate
+ = xSigner->selectSigningCertificateWithType(certificateKind, aDescription);
+ return xSignCertificate;
+}
+
+OUString getSignerName(const css::uno::Reference<css::security::XCertificate>& xCertificate)
+{
+ return comphelper::xmlsec::GetContentPart(xCertificate->getSubjectName(),
+ xCertificate->getCertificateKind());
+}
+
+OUString getLocalizedDate()
+{
+ const SvtSysLocale aSysLocale;
+ const LocaleDataWrapper& rLocaleData = aSysLocale.GetLocaleData();
+ Date aDateTime(Date::SYSTEM);
+ return rLocaleData.getDate(aDateTime);
+}
+
+uno::Reference<graphic::XGraphic> importSVG(std::u16string_view rSVG)
+{
+ SvMemoryStream aSvgStream(4096, 4096);
+ aSvgStream.WriteOString(OUStringToOString(rSVG, RTL_TEXTENCODING_UTF8));
+ uno::Reference<io::XInputStream> xInputStream(new utl::OSeekableInputStreamWrapper(aSvgStream));
+ uno::Reference<uno::XComponentContext> xContext(comphelper::getProcessComponentContext());
+ uno::Reference<graphic::XGraphicProvider> xProvider
+ = graphic::GraphicProvider::create(xContext);
+
+ uno::Sequence<beans::PropertyValue> aMediaProperties{ comphelper::makePropertyValue(
+ "InputStream", xInputStream) };
+ uno::Reference<graphic::XGraphic> xGraphic(xProvider->queryGraphic(aMediaProperties));
+ return xGraphic;
+}
+
+void setShapeCertificate(const SdrView* pView,
+ const css::uno::Reference<css::security::XCertificate>& xCertificate)
+{
+ const SdrMarkList& rMarkList = pView->GetMarkedObjectList();
+ if (rMarkList.GetMarkCount() < 1)
+ {
+ return;
+ }
+
+ const SdrMark* pMark = rMarkList.GetMark(0);
+ SdrObject* pSignatureLine = pMark->GetMarkedSdrObj();
+ if (!pSignatureLine)
+ {
+ return;
+ }
+
+ // Remember the selected certificate.
+ uno::Reference<drawing::XShape> xShape = pSignatureLine->getUnoShape();
+ uno::Reference<beans::XPropertySet> xShapeProps(xShape, uno::UNO_QUERY);
+ comphelper::SequenceAsHashMap aMap(xShapeProps->getPropertyValue("InteropGrabBag"));
+ aMap["SignatureCertificate"] <<= xCertificate;
+ xShapeProps->setPropertyValue("InteropGrabBag", uno::Any(aMap.getAsConstPropertyValueList()));
+
+ // Read svg and replace placeholder texts.
+ OUString aSvgImage(svx::SignatureLineHelper::getSignatureImage("signature-line-draw.svg"));
+ aSvgImage = aSvgImage.replaceAll("[SIGNED_BY]", SvxResId(RID_SVXSTR_SIGNATURELINE_DSIGNED_BY));
+ OUString aSignerName = svx::SignatureLineHelper::getSignerName(xCertificate);
+ aSvgImage = aSvgImage.replaceAll("[SIGNER_NAME]", aSignerName);
+ OUString aDate = svx::SignatureLineHelper::getLocalizedDate();
+ aDate = SvxResId(RID_SVXSTR_SIGNATURELINE_DATE).replaceFirst("%1", aDate);
+ aSvgImage = aSvgImage.replaceAll("[DATE]", aDate);
+
+ uno::Reference<graphic::XGraphic> xGraphic = svx::SignatureLineHelper::importSVG(aSvgImage);
+ xShapeProps->setPropertyValue("Graphic", uno::Any(xGraphic));
+}
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/dialog/spacinglistbox.cxx b/svx/source/dialog/spacinglistbox.cxx
new file mode 100644
index 000000000..90cc689cc
--- /dev/null
+++ b/svx/source/dialog/spacinglistbox.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 <svx/dialmgr.hxx>
+#include <svx/spacinglistbox.hxx>
+#include <unotools/localedatawrapper.hxx>
+#include <vcl/settings.hxx>
+#include <vcl/svapp.hxx>
+#include <spacing.hrc>
+
+namespace SpacingListBox
+{
+ void Fill(SpacingType eType, weld::ComboBox& rComboBox)
+ {
+ auto nSelected = rComboBox.get_active();
+ if (nSelected == -1)
+ nSelected = 0;
+ rComboBox.clear();
+
+ const LocaleDataWrapper& rLocaleData = Application::GetSettings().GetLocaleDataWrapper();
+ OUString sSuffix;
+
+ const measurement* pResources;
+ switch (eType)
+ {
+ case SpacingType::SPACING_INCH:
+ pResources = RID_SVXSTRARY_SPACING_INCH;
+ sSuffix = weld::MetricSpinButton::MetricToString(FieldUnit::INCH);
+ break;
+ case SpacingType::MARGINS_INCH:
+ pResources = RID_SVXSTRARY_MARGINS_INCH;
+ sSuffix = weld::MetricSpinButton::MetricToString(FieldUnit::INCH);
+ break;
+ case SpacingType::SPACING_CM:
+ pResources = RID_SVXSTRARY_SPACING_CM;
+ sSuffix = " " + weld::MetricSpinButton::MetricToString(FieldUnit::CM);
+ break;
+ default:
+ case SpacingType::MARGINS_CM:
+ sSuffix = " " + weld::MetricSpinButton::MetricToString(FieldUnit::CM);
+ pResources = RID_SVXSTRARY_MARGINS_CM;
+ break;
+ }
+
+ while (pResources->key)
+ {
+ OUString sMeasurement = rLocaleData.getNum(pResources->human, 2, true, false) + sSuffix;
+ OUString aStr = SvxResId(pResources->key).replaceFirst("%1", sMeasurement);
+ sal_uInt32 nData = pResources->twips;
+ rComboBox.append(OUString::number(nData), aStr);
+ ++pResources;
+ }
+
+ rComboBox.set_active(nSelected);
+
+ rComboBox.set_size_request(150, -1);
+ }
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/dialog/srchctrl.cxx b/svx/source/dialog/srchctrl.cxx
new file mode 100644
index 000000000..51c8d0333
--- /dev/null
+++ b/svx/source/dialog/srchctrl.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 <svl/intitem.hxx>
+#include <sfx2/objsh.hxx>
+
+#include <svx/svxids.hrc>
+
+#include "srchctrl.hxx"
+#include <svx/srchdlg.hxx>
+#include <svl/srchitem.hxx>
+
+SvxSearchController::SvxSearchController
+(
+ sal_uInt16 _nId,
+ SfxBindings& rBind,
+ SvxSearchDialog& rDlg
+) :
+ SfxControllerItem( _nId, rBind ),
+
+ rSrchDlg( rDlg )
+{
+}
+
+
+void SvxSearchController::StateChangedAtToolBoxControl( sal_uInt16 nSID, SfxItemState eState,
+ const SfxPoolItem* pState )
+{
+ if ( SfxItemState::DEFAULT == eState )
+ {
+ if ( SID_STYLE_FAMILY1 <= nSID && nSID <= SID_STYLE_FAMILY4 )
+ {
+ SfxObjectShell* pShell = SfxObjectShell::Current();
+
+ if ( pShell && pShell->GetStyleSheetPool() )
+ rSrchDlg.TemplatesChanged_Impl( *pShell->GetStyleSheetPool() );
+ }
+ else if ( SID_SEARCH_OPTIONS == nSID )
+ {
+ DBG_ASSERT( dynamic_cast<const SfxUInt16Item* >(pState) != nullptr, "wrong item type" );
+ SearchOptionFlags nFlags = static_cast<SearchOptionFlags>(static_cast<const SfxUInt16Item*>(pState)->GetValue());
+ rSrchDlg.EnableControls_Impl( nFlags );
+ }
+ else if ( SID_SEARCH_ITEM == nSID )
+ {
+ DBG_ASSERT( dynamic_cast<const SvxSearchItem*>( pState) != nullptr, "wrong item type" );
+ rSrchDlg.SetItem_Impl( static_cast<const SvxSearchItem*>(pState) );
+ }
+ }
+ else if ( SID_SEARCH_OPTIONS == nSID || SID_SEARCH_ITEM == nSID )
+ rSrchDlg.EnableControls_Impl( SearchOptionFlags::NONE );
+}
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/dialog/srchctrl.hxx b/svx/source/dialog/srchctrl.hxx
new file mode 100644
index 000000000..042757537
--- /dev/null
+++ b/svx/source/dialog/srchctrl.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_SVX_SOURCE_DIALOG_SRCHCTRL_HXX
+#define INCLUDED_SVX_SOURCE_DIALOG_SRCHCTRL_HXX
+
+#include <sfx2/ctrlitem.hxx>
+class SvxSearchDialog;
+
+class SvxSearchController : public SfxControllerItem
+{
+ SvxSearchDialog& rSrchDlg;
+
+protected:
+ virtual void StateChangedAtToolBoxControl(sal_uInt16, SfxItemState,
+ const SfxPoolItem* pState) override;
+
+public:
+ SvxSearchController(sal_uInt16 nId, SfxBindings& rBnd, SvxSearchDialog& rDlg);
+};
+
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/dialog/srchdlg.cxx b/svx/source/dialog/srchdlg.cxx
new file mode 100644
index 000000000..b047ef27d
--- /dev/null
+++ b/svx/source/dialog/srchdlg.cxx
@@ -0,0 +1,2466 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * 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 <vcl/timer.hxx>
+#include <svl/slstitm.hxx>
+#include <svl/itemiter.hxx>
+#include <svl/style.hxx>
+#include <unotools/intlwrapper.hxx>
+#include <unotools/moduleoptions.hxx>
+#include <unotools/searchopt.hxx>
+#include <unotools/syslocale.hxx>
+#include <sfx2/dispatch.hxx>
+#include <sfx2/objsh.hxx>
+#include <sfx2/module.hxx>
+#include <sfx2/viewsh.hxx>
+#include <sfx2/basedlgs.hxx>
+#include <sfx2/viewfrm.hxx>
+#include <svl/cjkoptions.hxx>
+#include <svl/ctloptions.hxx>
+#include <com/sun/star/awt/XWindow.hpp>
+#include <com/sun/star/container/XNameAccess.hpp>
+#include <com/sun/star/frame/XDispatch.hpp>
+#include <com/sun/star/frame/XDispatchProvider.hpp>
+#include <com/sun/star/frame/XLayoutManager.hpp>
+#include <com/sun/star/beans/PropertyValue.hpp>
+#include <com/sun/star/beans/XPropertySet.hpp>
+#include <com/sun/star/configuration/theDefaultProvider.hpp>
+#include <com/sun/star/frame/ModuleManager.hpp>
+#include <com/sun/star/ui/XUIElement.hpp>
+#include <comphelper/processfactory.hxx>
+#include <comphelper/scopeguard.hxx>
+#include <svl/itempool.hxx>
+
+#include <sfx2/app.hxx>
+#include <toolkit/helper/vclunohelper.hxx>
+
+#include <svx/srchdlg.hxx>
+#include <svx/strarray.hxx>
+
+#include <svx/strings.hrc>
+#include <svx/svxids.hrc>
+
+#include <svl/srchitem.hxx>
+#include <svx/pageitem.hxx>
+#include "srchctrl.hxx"
+#include <svx/dialmgr.hxx>
+#include <editeng/brushitem.hxx>
+#include <tools/resary.hxx>
+#include <svx/svxdlg.hxx>
+#include <vcl/toolbox.hxx>
+#include <o3tl/typed_flags_set.hxx>
+#include <comphelper/lok.hxx>
+
+#include <cstdlib>
+#include <memory>
+
+#include <findtextfield.hxx>
+
+#include <svx/labelitemwindow.hxx>
+#include <svx/xdef.hxx>
+#include <officecfg/Office/Common.hxx>
+
+using namespace com::sun::star::i18n;
+using namespace com::sun::star::uno;
+using namespace com::sun::star::accessibility;
+using namespace com::sun::star;
+using namespace comphelper;
+
+
+#define IS_MOBILE (comphelper::LibreOfficeKit::isActive() && SfxViewShell::Current() && SfxViewShell::Current()->isLOKMobilePhone())
+
+enum class ModifyFlags {
+ NONE = 0x000000,
+ Search = 0x000001,
+ Replace = 0x000002,
+ Word = 0x000004,
+ Exact = 0x000008,
+ Backwards = 0x000010,
+ Selection = 0x000020,
+ Regexp = 0x000040,
+ Layout = 0x000080,
+ Similarity = 0x000100,
+ Formulas = 0x000200,
+ Values = 0x000400,
+ CalcNotes = 0x000800,
+ Rows = 0x001000,
+ Columns = 0x002000,
+ AllTables = 0x004000,
+ Notes = 0x008000,
+ Wildcard = 0x010000
+};
+namespace o3tl {
+ template<> struct typed_flags<ModifyFlags> : is_typed_flags<ModifyFlags, 0x01ffff> {};
+}
+
+namespace
+{
+ bool GetCheckBoxValue(const weld::CheckButton& rBox)
+ {
+ return rBox.get_sensitive() && rBox.get_active();
+ }
+
+ bool GetNegatedCheckBoxValue(const weld::CheckButton& rBox)
+ {
+ return rBox.get_sensitive() && !rBox.get_active();
+ }
+}
+
+struct SearchDlg_Impl
+{
+ bool bSaveToModule : 1,
+ bFocusOnSearch : 1;
+ WhichRangesContainer pRanges;
+ Timer aSelectionTimer { "svx SearchDlg_Impl aSelectionTimer" };
+
+ uno::Reference< frame::XDispatch > xCommand1Dispatch;
+ uno::Reference< frame::XDispatch > xCommand2Dispatch;
+ util::URL aCommand1URL;
+ util::URL aCommand2URL;
+
+ SearchDlg_Impl()
+ : bSaveToModule(true)
+ , bFocusOnSearch(true)
+ {
+ aCommand1URL.Complete = aCommand1URL.Main = "vnd.sun.search:SearchViaComponent1";
+ aCommand1URL.Protocol = "vnd.sun.search:";
+ aCommand1URL.Path = "SearchViaComponent1";
+ aCommand2URL.Complete = aCommand2URL.Main = "vnd.sun.search:SearchViaComponent2";
+ aCommand2URL.Protocol = "vnd.sun.search:";
+ aCommand2URL.Path = "SearchViaComponent2";
+ }
+};
+
+static void ListToStrArr_Impl(sal_uInt16 nId, std::vector<OUString>& rStrLst, weld::ComboBox& rCBox, sal_uInt16 nRememberSize)
+{
+ const SfxStringListItem* pSrchItem =
+ static_cast<const SfxStringListItem*>(SfxGetpApp()->GetItem( nId ));
+
+ if (!pSrchItem)
+ return;
+
+ std::vector<OUString> aLst = pSrchItem->GetList();
+
+ if (aLst.size() > nRememberSize)
+ aLst.resize(nRememberSize);
+
+ for (const OUString & s : aLst)
+ {
+ rStrLst.push_back(s);
+ rCBox.append_text(s);
+ }
+}
+
+static void StrArrToList_Impl( sal_uInt16 nId, const std::vector<OUString>& rStrLst )
+{
+ DBG_ASSERT( !rStrLst.empty(), "check in advance");
+ SfxGetpApp()->PutItem( SfxStringListItem( nId, &rStrLst ) );
+}
+
+SearchAttrItemList::SearchAttrItemList( SearchAttrItemList&& rList ) :
+ SrchAttrItemList(std::move(rList))
+{
+ for ( size_t i = 0; i < size(); ++i )
+ if ( !IsInvalidItem( (*this)[i].pItem ) )
+ (*this)[i].pItem = (*this)[i].pItem->Clone();
+}
+
+SearchAttrItemList::SearchAttrItemList( const SearchAttrItemList& rList ) :
+ SrchAttrItemList(rList)
+{
+ for ( size_t i = 0; i < size(); ++i )
+ if ( !IsInvalidItem( (*this)[i].pItem ) )
+ (*this)[i].pItem = (*this)[i].pItem->Clone();
+}
+
+SearchAttrItemList::~SearchAttrItemList()
+{
+ Clear();
+}
+
+void SearchAttrItemList::Put( const SfxItemSet& rSet )
+{
+ if ( !rSet.Count() )
+ return;
+
+ SfxItemPool* pPool = rSet.GetPool();
+ SfxItemIter aIter( rSet );
+ SearchAttrItem aItem;
+ const SfxPoolItem* pItem = aIter.GetCurItem();
+ sal_uInt16 nWhich;
+
+ do
+ {
+ // only test that it is available?
+ if( IsInvalidItem( pItem ) )
+ {
+ nWhich = rSet.GetWhichByPos( aIter.GetCurPos() );
+ aItem.pItem = const_cast<SfxPoolItem*>(pItem);
+ }
+ else
+ {
+ nWhich = pItem->Which();
+ aItem.pItem = pItem->Clone();
+ }
+
+ aItem.nSlot = pPool->GetSlotId( nWhich );
+ Insert( aItem );
+
+ pItem = aIter.NextItem();
+ } while (pItem);
+}
+
+
+SfxItemSet& SearchAttrItemList::Get( SfxItemSet& rSet )
+{
+ SfxItemPool* pPool = rSet.GetPool();
+
+ for ( size_t i = 0; i < size(); ++i )
+ if ( IsInvalidItem( (*this)[i].pItem ) )
+ rSet.InvalidateItem( pPool->GetWhich( (*this)[i].nSlot ) );
+ else
+ rSet.Put( *(*this)[i].pItem );
+ return rSet;
+}
+
+
+void SearchAttrItemList::Clear()
+{
+ for ( size_t i = 0; i < size(); ++i )
+ if ( !IsInvalidItem( (*this)[i].pItem ) )
+ delete (*this)[i].pItem;
+ SrchAttrItemList::clear();
+}
+
+
+// Deletes the pointer to the items
+void SearchAttrItemList::Remove(size_t nPos)
+{
+ size_t nLen = 1;
+ if ( nPos + nLen > size() )
+ nLen = size() - nPos;
+
+ for ( size_t i = nPos; i < nPos + nLen; ++i )
+ if ( !IsInvalidItem( (*this)[i].pItem ) )
+ delete (*this)[i].pItem;
+
+ SrchAttrItemList::erase( begin() + nPos, begin() + nPos + nLen );
+}
+
+SvxSearchDialog::SvxSearchDialog(weld::Window* pParent, SfxChildWindow* pChildWin, SfxBindings& rBind)
+ : SfxModelessDialogController(&rBind, pChildWin, pParent,
+ IS_MOBILE ? OUString("svx/ui/findreplacedialog-mobile.ui") : OUString("svx/ui/findreplacedialog.ui"),
+ "FindReplaceDialog")
+ , rBindings(rBind)
+ , m_aPresentIdle("Bring SvxSearchDialog to Foreground")
+ , bWriter(false)
+ , bSearch(true)
+ , bFormat(false)
+ , bReplaceBackwards(false)
+ , nOptions(SearchOptionFlags::ALL)
+ , bSet(false)
+ , bConstruct(true)
+ , nModifyFlag(ModifyFlags::NONE)
+ , pReplaceList(new SearchAttrItemList)
+ , nTransliterationFlags(TransliterationFlags::NONE)
+ , m_xSearchFrame(m_xBuilder->weld_frame("searchframe"))
+ , m_xSearchLB(m_xBuilder->weld_combo_box("searchterm"))
+ , m_xSearchTmplLB(m_xBuilder->weld_combo_box("searchlist"))
+ , m_xSearchAttrText(m_xBuilder->weld_label("searchdesc"))
+ , m_xSearchLabel(m_xBuilder->weld_label("searchlabel"))
+ , m_xReplaceFrame(m_xBuilder->weld_frame("replaceframe"))
+ , m_xReplaceLB(m_xBuilder->weld_combo_box("replaceterm"))
+ , m_xReplaceTmplLB(m_xBuilder->weld_combo_box("replacelist"))
+ , m_xReplaceAttrText(m_xBuilder->weld_label("replacedesc"))
+ , m_xSearchBtn(m_xBuilder->weld_button("search"))
+ , m_xBackSearchBtn(m_xBuilder->weld_button("backsearch"))
+ , m_xSearchAllBtn(m_xBuilder->weld_button("searchall"))
+ , m_xReplaceBtn(m_xBuilder->weld_button("replace"))
+ , m_xReplaceAllBtn(m_xBuilder->weld_button("replaceall"))
+ , m_xComponentFrame(m_xBuilder->weld_frame("componentframe"))
+ , m_xSearchComponent1PB(m_xBuilder->weld_button("component1"))
+ , m_xSearchComponent2PB(m_xBuilder->weld_button("component2"))
+ , m_xMatchCaseCB(m_xBuilder->weld_check_button("matchcase"))
+ , m_xSearchFormattedCB(m_xBuilder->weld_check_button("searchformatted"))
+ , m_xWordBtn(m_xBuilder->weld_check_button("wholewords"))
+ , m_xCloseBtn(m_xBuilder->weld_button("close"))
+ , m_xIncludeDiacritics(m_xBuilder->weld_check_button("includediacritics"))
+ , m_xIncludeKashida(m_xBuilder->weld_check_button("includekashida"))
+ , m_xOtherOptionsExpander(m_xBuilder->weld_expander("OptionsExpander"))
+ , m_xSelectionBtn(m_xBuilder->weld_check_button("selection"))
+ , m_xRegExpBtn(m_xBuilder->weld_check_button("regexp"))
+ , m_xWildcardBtn(m_xBuilder->weld_check_button("wildcard"))
+ , m_xSimilarityBox(m_xBuilder->weld_check_button("similarity"))
+ , m_xSimilarityBtn(m_xBuilder->weld_button("similaritybtn"))
+ , m_xLayoutBtn(m_xBuilder->weld_check_button("layout"))
+ , m_xNotesBtn(m_xBuilder->weld_check_button("notes"))
+ , m_xJapMatchFullHalfWidthCB(m_xBuilder->weld_check_button("matchcharwidth"))
+ , m_xJapOptionsCB(m_xBuilder->weld_check_button("soundslike"))
+ , m_xReplaceBackwardsCB(m_xBuilder->weld_check_button("replace_backwards"))
+ , m_xJapOptionsBtn(m_xBuilder->weld_button("soundslikebtn"))
+ , m_xAttributeBtn(m_xBuilder->weld_button("attributes"))
+ , m_xFormatBtn(m_xBuilder->weld_button("format"))
+ , m_xNoFormatBtn(m_xBuilder->weld_button("noformat"))
+ , m_xCalcGrid(m_xBuilder->weld_widget("calcgrid"))
+ , m_xCalcSearchInFT(m_xBuilder->weld_label("searchinlabel"))
+ , m_xCalcSearchInLB(m_xBuilder->weld_combo_box("calcsearchin"))
+ , m_xCalcSearchDirFT(m_xBuilder->weld_label("searchdir"))
+ , m_xRowsBtn(m_xBuilder->weld_radio_button("rows"))
+ , m_xColumnsBtn(m_xBuilder->weld_radio_button("cols"))
+ , m_xAllSheetsCB(m_xBuilder->weld_check_button("allsheets"))
+ , m_xCalcStrFT(m_xBuilder->weld_label("entirecells"))
+{
+ m_aPresentIdle.SetTimeout(50);
+ m_aPresentIdle.SetInvokeHandler(LINK(this, SvxSearchDialog, PresentTimeoutHdl_Impl));
+
+ m_xSearchTmplLB->make_sorted();
+ m_xSearchAttrText->hide();
+ m_xSearchLabel->show();
+
+ m_xReplaceTmplLB->make_sorted();
+ m_xReplaceAttrText->hide();
+
+ aCalcStr = m_xCalcStrFT->get_label();
+
+ // m_xSimilarityBtn->set_height_request(m_xSimilarityBox->get_preferred_size().Height());
+ // m_xJapOptionsBtn->set_height_request(m_xJapOptionsCB->get_preferred_size().Height());
+
+ //tdf#122322
+ nRememberSize = officecfg::Office::Common::Misc::FindReplaceRememberedSearches::get();
+ if (nRememberSize<1)
+ nRememberSize = 1; //0 crashes with no results found
+
+ auto nTermWidth = m_xSearchLB->get_approximate_digit_width() * 28;
+ m_xSearchLB->set_size_request(nTermWidth, -1);
+ m_xSearchTmplLB->set_size_request(nTermWidth, -1);
+ m_xReplaceLB->set_size_request(nTermWidth, -1);
+ m_xReplaceTmplLB->set_size_request(nTermWidth, -1);
+
+ Construct_Impl();
+}
+
+IMPL_LINK_NOARG(SvxSearchDialog, PresentTimeoutHdl_Impl, Timer*, void)
+{
+ getDialog()->present();
+}
+
+void SvxSearchDialog::Present()
+{
+ PresentTimeoutHdl_Impl(nullptr);
+ // tdf#133807 try again in a short timeout
+ m_aPresentIdle.Start();
+}
+
+void SvxSearchDialog::ChildWinDispose()
+{
+ rBindings.EnterRegistrations();
+ pSearchController.reset();
+ pOptionsController.reset();
+ pFamilyController.reset();
+ rBindings.LeaveRegistrations();
+ SfxModelessDialogController::ChildWinDispose();
+}
+
+SvxSearchDialog::~SvxSearchDialog()
+{
+ m_aPresentIdle.Stop();
+ pSearchItem.reset();
+ pImpl.reset();
+}
+
+void SvxSearchDialog::Construct_Impl()
+{
+ pImpl.reset( new SearchDlg_Impl() );
+ pImpl->aSelectionTimer.SetTimeout( 500 );
+ pImpl->aSelectionTimer.SetInvokeHandler(
+ LINK( this, SvxSearchDialog, TimeoutHdl_Impl ) );
+ EnableControls_Impl( SearchOptionFlags::NONE );
+
+ // Store old Text from m_xWordBtn
+ aCalcStr += "#";
+ aCalcStr += m_xWordBtn->get_label();
+
+ aLayoutStr = SvxResId( RID_SVXSTR_SEARCH_STYLES );
+ aLayoutWriterStr = SvxResId( RID_SVXSTR_WRITER_STYLES );
+ aLayoutCalcStr = SvxResId( RID_SVXSTR_CALC_STYLES );
+ aStylesStr = m_xLayoutBtn->get_label();
+
+ // Get stored search-strings from the application
+ ListToStrArr_Impl(SID_SEARCHDLG_SEARCHSTRINGS,
+ aSearchStrings, *m_xSearchLB, nRememberSize);
+ ListToStrArr_Impl(SID_SEARCHDLG_REPLACESTRINGS,
+ aReplaceStrings, *m_xReplaceLB, nRememberSize);
+
+ InitControls_Impl();
+
+ // Get attribute sets only once in constructor()
+ const SfxPoolItem* ppArgs[] = { pSearchItem.get(), nullptr };
+ const SvxSetItem* pSrchSetItem =
+ static_cast<const SvxSetItem*>( rBindings.GetDispatcher()->Execute( FID_SEARCH_SEARCHSET, SfxCallMode::SLOT, ppArgs ) );
+
+ if ( pSrchSetItem )
+ InitAttrList_Impl( &pSrchSetItem->GetItemSet(), nullptr );
+
+ const SvxSetItem* pReplSetItem =
+ static_cast<const SvxSetItem*>( rBindings.GetDispatcher()->Execute( FID_SEARCH_REPLACESET, SfxCallMode::SLOT, ppArgs ) );
+
+ if ( pReplSetItem )
+ InitAttrList_Impl( nullptr, &pReplSetItem->GetItemSet() );
+
+ // Create controller and update at once
+ rBindings.EnterRegistrations();
+ pSearchController.reset(
+ new SvxSearchController( SID_SEARCH_ITEM, rBindings, *this ) );
+ pOptionsController.reset(
+ new SvxSearchController( SID_SEARCH_OPTIONS, rBindings, *this ) );
+ rBindings.LeaveRegistrations();
+ rBindings.GetDispatcher()->Execute( FID_SEARCH_ON, SfxCallMode::SLOT, ppArgs );
+ pImpl->aSelectionTimer.Start();
+
+
+ if(!SvtCJKOptions::IsJapaneseFindEnabled())
+ {
+ m_xJapOptionsCB->set_active( false );
+ m_xJapOptionsCB->hide();
+ m_xJapOptionsBtn->hide();
+ }
+ if(!SvtCJKOptions::IsCJKFontEnabled())
+ {
+ m_xJapMatchFullHalfWidthCB->hide();
+ }
+ SvtCTLOptions aCTLOptions;
+ // Do not disable and hide the m_xIncludeDiacritics button.
+ // Include Diacritics == Not Ignore Diacritics => A does not match A-Umlaut (Diaeresis).
+ // Confusingly these have negated names (following the UI) but the actual
+ // transliteration is to *ignore* diacritics if "included" (sensitive) is
+ // _not_ checked.
+ if(!aCTLOptions.IsCTLFontEnabled())
+ {
+ m_xIncludeDiacritics->set_active( true );
+ m_xIncludeKashida->set_active( true );
+ m_xIncludeKashida->hide();
+ }
+ //component extension - show component search buttons if the commands
+ // vnd.sun.star::SearchViaComponent1 and 2 are supported
+ const uno::Reference< frame::XFrame >xFrame = rBindings.GetActiveFrame();
+ const uno::Reference< frame::XDispatchProvider > xDispatchProv(xFrame, uno::UNO_QUERY);
+
+ bool bSearchComponent1 = false;
+ bool bSearchComponent2 = false;
+ if(xDispatchProv.is())
+ {
+ OUString sTarget("_self");
+ pImpl->xCommand1Dispatch = xDispatchProv->queryDispatch(pImpl->aCommand1URL, sTarget, 0);
+ if (pImpl->xCommand1Dispatch.is())
+ bSearchComponent1 = true;
+ pImpl->xCommand2Dispatch = xDispatchProv->queryDispatch(pImpl->aCommand2URL, sTarget, 0);
+ if (pImpl->xCommand2Dispatch.is())
+ bSearchComponent2 = true;
+ }
+
+ if( !(bSearchComponent1 || bSearchComponent2) )
+ return;
+
+ try
+ {
+ uno::Reference< lang::XMultiServiceFactory > xConfigurationProvider =
+ configuration::theDefaultProvider::get( comphelper::getProcessComponentContext() );
+ uno::Sequence< uno::Any > aArgs {
+ Any(OUString( "/org.openoffice.Office.Common/SearchOptions/")) };
+
+ uno::Reference< uno::XInterface > xIFace = xConfigurationProvider->createInstanceWithArguments(
+ "com.sun.star.configuration.ConfigurationUpdateAccess",
+ aArgs);
+ uno::Reference< container::XNameAccess> xDirectAccess(xIFace, uno::UNO_QUERY);
+ if(xDirectAccess.is())
+ {
+ OUString sTemp;
+ uno::Any aRet = xDirectAccess->getByName("ComponentSearchGroupLabel");
+ aRet >>= sTemp;
+ m_xComponentFrame->set_label(sTemp);
+ aRet = xDirectAccess->getByName("ComponentSearchCommandLabel1");
+ aRet >>= sTemp;
+ m_xSearchComponent1PB->set_label( sTemp );
+ aRet = xDirectAccess->getByName("ComponentSearchCommandLabel2");
+ aRet >>= sTemp;
+ m_xSearchComponent2PB->set_label( sTemp );
+ }
+ }
+ catch(uno::Exception&){}
+
+ if(!m_xSearchComponent1PB->get_label().isEmpty() && bSearchComponent1 )
+ {
+ m_xComponentFrame->show();
+ m_xSearchComponent1PB->show();
+ }
+ if( !m_xSearchComponent2PB->get_label().isEmpty() )
+ {
+ m_xComponentFrame->show();
+ m_xSearchComponent2PB->show();
+ }
+}
+
+void SvxSearchDialog::Close()
+{
+ // remember strings
+ if (!aSearchStrings.empty())
+ StrArrToList_Impl( SID_SEARCHDLG_SEARCHSTRINGS, aSearchStrings );
+
+ if (!aReplaceStrings.empty())
+ StrArrToList_Impl( SID_SEARCHDLG_REPLACESTRINGS, aReplaceStrings );
+
+ // save settings to configuration
+ SvtSearchOptions aOpt;
+ aOpt.SetWholeWordsOnly ( m_xWordBtn->get_active() );
+ aOpt.SetBackwards ( m_xReplaceBackwardsCB->get_active() );
+ aOpt.SetUseRegularExpression ( m_xRegExpBtn->get_active() );
+ aOpt.SetUseWildcard ( m_xWildcardBtn->get_active() );
+ aOpt.SetSearchForStyles ( m_xLayoutBtn->get_active() );
+ aOpt.SetSimilaritySearch ( m_xSimilarityBox->get_active() );
+ aOpt.SetUseAsianOptions ( m_xJapOptionsCB->get_active() );
+ aOpt.SetNotes ( m_xNotesBtn->get_active() );
+ aOpt.SetIgnoreDiacritics_CTL ( !m_xIncludeDiacritics->get_active() );
+ aOpt.SetIgnoreKashida_CTL ( !m_xIncludeKashida->get_active() );
+ aOpt.SetSearchFormatted ( m_xSearchFormattedCB->get_active() );
+ aOpt.Commit();
+
+ if (IsClosing())
+ return;
+
+ const SfxPoolItem* ppArgs[] = { pSearchItem.get(), nullptr };
+ rBindings.GetDispatcher()->Execute( FID_SEARCH_OFF, SfxCallMode::SLOT, ppArgs );
+ rBindings.Invalidate(SID_SEARCH_DLG);
+
+ SfxViewFrame* pViewFrame = SfxViewFrame::Current();
+ if (pViewFrame)
+ pViewFrame->ToggleChildWindow(SID_SEARCH_DLG);
+}
+
+TransliterationFlags SvxSearchDialog::GetTransliterationFlags() const
+{
+ if (!m_xMatchCaseCB->get_active())
+ nTransliterationFlags |= TransliterationFlags::IGNORE_CASE;
+ else
+ nTransliterationFlags &= ~TransliterationFlags::IGNORE_CASE;
+ if ( !m_xJapMatchFullHalfWidthCB->get_active())
+ nTransliterationFlags |= TransliterationFlags::IGNORE_WIDTH;
+ else
+ nTransliterationFlags &= ~TransliterationFlags::IGNORE_WIDTH;
+ return nTransliterationFlags;
+}
+
+void SvxSearchDialog::SetSaveToModule(bool b)
+{
+ pImpl->bSaveToModule = b;
+}
+
+void SvxSearchDialog::SetSearchLabel(const OUString& rStr)
+{
+ m_xSearchLabel->set_label(rStr);
+
+ if (rStr == SvxResId(RID_SVXSTR_SEARCH_NOT_FOUND))
+ m_xSearchLB->set_entry_message_type(weld::EntryMessageType::Error);
+}
+
+void SvxSearchDialog::ApplyTransliterationFlags_Impl( TransliterationFlags nSettings )
+{
+ nTransliterationFlags = nSettings;
+ bool bVal(nSettings & TransliterationFlags::IGNORE_CASE);
+ m_xMatchCaseCB->set_active( !bVal );
+ bVal = bool(nSettings & TransliterationFlags::IGNORE_WIDTH);
+ m_xJapMatchFullHalfWidthCB->set_active( !bVal );
+}
+
+
+bool SvxSearchDialog::IsOtherOptionsExpanded() const
+{
+ return m_xReplaceBackwardsCB->get_active() ||
+ m_xSelectionBtn->get_active() ||
+ m_xRegExpBtn->get_active() ||
+ m_xLayoutBtn->get_active() ||
+ m_xSimilarityBox->get_active() ||
+ m_xJapMatchFullHalfWidthCB->get_active() ||
+ m_xJapOptionsCB->get_active() ||
+ m_xWildcardBtn->get_active() ||
+ m_xNotesBtn->get_active() ||
+ m_xIncludeKashida->get_active() ||
+ !m_xIncludeDiacritics->get_active();//tdf#138173
+}
+
+void SvxSearchDialog::Activate()
+{
+ // apply possible transliteration changes of the SvxSearchItem member
+ if (pSearchItem)
+ {
+ m_xMatchCaseCB->set_active( pSearchItem->GetExact() );
+ m_xJapMatchFullHalfWidthCB->set_active( !pSearchItem->IsMatchFullHalfWidthForms() );
+ }
+
+ SfxModelessDialogController::Activate();
+}
+
+void SvxSearchDialog::InitControls_Impl()
+{
+ // CaseSensitives AutoComplete
+ m_xSearchLB->set_entry_completion( true, true );
+ m_xSearchLB->show();
+ m_xReplaceLB->set_entry_completion( true, true );
+ m_xReplaceLB->show();
+
+ m_xFormatBtn->set_sensitive(false);
+ m_xAttributeBtn->set_sensitive(false);
+
+ m_xSearchLB->connect_changed( LINK( this, SvxSearchDialog, ModifyHdl_Impl ) );
+ m_xReplaceLB->connect_changed( LINK( this, SvxSearchDialog, ModifyHdl_Impl ) );
+
+ Link<weld::Widget&,void> aLink = LINK( this, SvxSearchDialog, FocusHdl_Impl );
+ m_xSearchLB->connect_focus_in( aLink );
+ m_xReplaceLB->connect_focus_in( aLink );
+
+ aLink = LINK( this, SvxSearchDialog, LoseFocusHdl_Impl );
+ m_xSearchLB->connect_focus_out( aLink );
+ m_xReplaceLB->connect_focus_out( aLink );
+
+ m_xSearchTmplLB->connect_focus_out( aLink );
+ m_xReplaceTmplLB->connect_focus_out( aLink );
+
+ Link<weld::Button&,void> aLink2 = LINK( this, SvxSearchDialog, CommandHdl_Impl );
+ m_xSearchBtn->connect_clicked( aLink2 );
+ m_xBackSearchBtn->connect_clicked( aLink2 );
+ m_xSearchAllBtn->connect_clicked( aLink2 );
+ m_xReplaceBtn->connect_clicked( aLink2 );
+ m_xReplaceAllBtn->connect_clicked( aLink2 );
+ m_xCloseBtn->connect_clicked( aLink2 );
+ m_xSimilarityBtn->connect_clicked( aLink2 );
+ m_xJapOptionsBtn->connect_clicked( aLink2 );
+ m_xSearchComponent1PB->connect_clicked( aLink2 );
+ m_xSearchComponent2PB->connect_clicked( aLink2 );
+
+ Link<weld::Toggleable&,void> aLink3 = LINK( this, SvxSearchDialog, FlagHdl_Impl );
+ m_xReplaceBackwardsCB->connect_toggled( aLink3 );
+ m_xWordBtn->connect_toggled( aLink3 );
+ m_xSelectionBtn->connect_toggled( aLink3 );
+ m_xMatchCaseCB->connect_toggled( aLink3 );
+ m_xRegExpBtn->connect_toggled( aLink3 );
+ m_xWildcardBtn->connect_toggled( aLink3 );
+ m_xNotesBtn->connect_toggled( aLink3 );
+ m_xSimilarityBox->connect_toggled( aLink3 );
+ m_xJapOptionsCB->connect_toggled( aLink3 );
+ m_xJapMatchFullHalfWidthCB->connect_toggled( aLink3 );
+ m_xIncludeDiacritics->connect_toggled( aLink3 );
+ m_xIncludeKashida->connect_toggled( aLink3 );
+ m_xLayoutBtn->connect_toggled( LINK( this, SvxSearchDialog, TemplateHdl_Impl ) );
+ m_xFormatBtn->connect_clicked( LINK( this, SvxSearchDialog, FormatHdl_Impl ) );
+ m_xNoFormatBtn->connect_clicked(
+ LINK( this, SvxSearchDialog, NoFormatHdl_Impl ) );
+ m_xAttributeBtn->connect_clicked(
+ LINK( this, SvxSearchDialog, AttributeHdl_Impl ) );
+}
+
+namespace
+{
+ SvtModuleOptions::EFactory getModule(SfxBindings const & rBindings)
+ {
+ SvtModuleOptions::EFactory eFactory(SvtModuleOptions::EFactory::UNKNOWN_FACTORY);
+ try
+ {
+ const uno::Reference< frame::XFrame > xFrame =
+ rBindings.GetActiveFrame();
+ uno::Reference< frame::XModuleManager2 > xModuleManager(
+ frame::ModuleManager::create(::comphelper::getProcessComponentContext()));
+
+ OUString aModuleIdentifier = xModuleManager->identify( xFrame );
+ eFactory = SvtModuleOptions::ClassifyFactoryByServiceName(aModuleIdentifier);
+ }
+ catch (const uno::Exception&)
+ {
+ }
+ return eFactory;
+ }
+}
+
+void SvxSearchDialog::ShowOptionalControls_Impl()
+{
+ DBG_ASSERT( pSearchItem, "no search item" );
+
+ SvtCTLOptions aCTLOptions;
+ SvtModuleOptions::EFactory eFactory = getModule(rBindings);
+ bool bDrawApp = eFactory == SvtModuleOptions::EFactory::DRAW;
+ bool bWriterApp =
+ eFactory == SvtModuleOptions::EFactory::WRITER ||
+ eFactory == SvtModuleOptions::EFactory::WRITERWEB ||
+ eFactory == SvtModuleOptions::EFactory::WRITERGLOBAL;
+ bool bCalcApp = eFactory == SvtModuleOptions::EFactory::CALC;
+
+ m_xLayoutBtn->set_visible(!bDrawApp);
+ m_xNotesBtn->set_visible(bWriterApp);
+ m_xRegExpBtn->set_visible(!bDrawApp);
+ m_xWildcardBtn->set_visible(bCalcApp); /* TODO:WILDCARD enable for other apps if hey handle it */
+ m_xReplaceBackwardsCB->show();
+ m_xSimilarityBox->show();
+ m_xSimilarityBtn->show();
+ m_xSelectionBtn->show();
+ m_xIncludeDiacritics->show();
+ m_xIncludeKashida->set_visible(aCTLOptions.IsCTLFontEnabled());
+ m_xJapMatchFullHalfWidthCB->set_visible(SvtCJKOptions::IsCJKFontEnabled());
+ m_xJapOptionsCB->set_visible(SvtCJKOptions::IsJapaneseFindEnabled());
+ m_xJapOptionsBtn->set_visible(SvtCJKOptions::IsJapaneseFindEnabled());
+
+ if (bWriter)
+ {
+ m_xAttributeBtn->show();
+ m_xFormatBtn->show();
+ m_xNoFormatBtn->show();
+ }
+
+ if (bCalcApp)
+ {
+ m_xCalcSearchInFT->show();
+ m_xCalcSearchInLB->show();
+ m_xCalcSearchDirFT->show();
+ m_xRowsBtn->show();
+ m_xColumnsBtn->show();
+ m_xAllSheetsCB->show();
+ m_xSearchFormattedCB->show();
+ }
+}
+
+
+namespace {
+
+class ToggleSaveToModule
+{
+public:
+ ToggleSaveToModule(SvxSearchDialog& rDialog, bool bValue) :
+ mrDialog(rDialog), mbValue(bValue)
+ {
+ mrDialog.SetSaveToModule(mbValue);
+ }
+
+ ~ToggleSaveToModule()
+ {
+ mrDialog.SetSaveToModule(!mbValue);
+ }
+private:
+ SvxSearchDialog& mrDialog;
+ bool mbValue;
+};
+
+}
+
+void SvxSearchDialog::Init_Impl( bool bSearchPattern )
+{
+ DBG_ASSERT( pSearchItem, "SearchItem == 0" );
+
+ // We don't want to save any intermediate state to the module while the
+ // dialog is being initialized.
+ ToggleSaveToModule aNoModuleSave(*this, false);
+ SvtSearchOptions aOpt;
+
+ bWriter = ( pSearchItem->GetAppFlag() == SvxSearchApp::WRITER );
+
+ if ( !( nModifyFlag & ModifyFlags::Word ) )
+ m_xWordBtn->set_active( pSearchItem->GetWordOnly() );
+ if ( !( nModifyFlag & ModifyFlags::Exact ) )
+ m_xMatchCaseCB->set_active( pSearchItem->GetExact() );
+ if ( !( nModifyFlag & ModifyFlags::Backwards ) )
+ m_xReplaceBackwardsCB->set_active( bReplaceBackwards ); //adjustment to replace backwards
+ if ( !( nModifyFlag & ModifyFlags::Notes ) )
+ m_xNotesBtn->set_active( pSearchItem->GetNotes() );
+ if ( !( nModifyFlag & ModifyFlags::Selection ) )
+ m_xSelectionBtn->set_active( pSearchItem->GetSelection() );
+ if ( !( nModifyFlag & ModifyFlags::Regexp ) )
+ m_xRegExpBtn->set_active( pSearchItem->GetRegExp() );
+ if ( !( nModifyFlag & ModifyFlags::Wildcard ) )
+ m_xWildcardBtn->set_active( pSearchItem->GetWildcard() );
+ if ( !( nModifyFlag & ModifyFlags::Layout ) )
+ m_xLayoutBtn->set_active( pSearchItem->GetPattern() );
+ if (m_xNotesBtn->get_active())
+ m_xLayoutBtn->set_sensitive(false);
+ m_xSimilarityBox->set_active( pSearchItem->IsLevenshtein() );
+ if ( m_xJapOptionsCB->get_visible() )
+ m_xJapOptionsCB->set_active( pSearchItem->IsUseAsianOptions() );
+ m_xIncludeDiacritics->set_active( !aOpt.IsIgnoreDiacritics_CTL() );
+ if ( m_xIncludeKashida->get_visible() )
+ m_xIncludeKashida->set_active( !aOpt.IsIgnoreKashida_CTL() );
+ ApplyTransliterationFlags_Impl( pSearchItem->GetTransliterationFlags() );
+
+ ShowOptionalControls_Impl();
+
+ if ( pSearchItem->GetAppFlag() == SvxSearchApp::CALC )
+ {
+ m_xCalcGrid->show();
+ m_xSearchFormattedCB->set_active( aOpt.IsSearchFormatted() );
+ Link<weld::Toggleable&,void> aLink = LINK( this, SvxSearchDialog, FlagHdl_Impl );
+ m_xCalcSearchInLB->connect_changed( LINK( this, SvxSearchDialog, LBSelectHdl_Impl ) );
+ m_xRowsBtn->connect_toggled( aLink );
+ m_xColumnsBtn->connect_toggled( aLink );
+ m_xAllSheetsCB->connect_toggled( aLink );
+ m_xSearchFormattedCB->connect_toggled( aLink );
+
+ ModifyFlags nModifyFlagCheck;
+ switch ( pSearchItem->GetCellType() )
+ {
+ case SvxSearchCellType::FORMULA:
+ nModifyFlagCheck = ModifyFlags::Formulas;
+ break;
+
+ case SvxSearchCellType::VALUE:
+ nModifyFlagCheck = ModifyFlags::Values;
+ break;
+
+ case SvxSearchCellType::NOTE:
+ nModifyFlagCheck = ModifyFlags::CalcNotes;
+ break;
+
+ default:
+ std::abort(); // cannot happen
+ }
+ if ( !(nModifyFlag & nModifyFlagCheck) )
+ m_xCalcSearchInLB->set_active( static_cast<sal_Int32>(pSearchItem->GetCellType()) );
+
+ m_xWordBtn->set_label( aCalcStr.getToken( 0, '#' ) );
+
+ if ( pSearchItem->GetRowDirection() &&
+ !( nModifyFlag & ModifyFlags::Rows ) )
+ m_xRowsBtn->set_active(true);
+ else if ( !pSearchItem->GetRowDirection() &&
+ !( nModifyFlag & ModifyFlags::Columns ) )
+ m_xColumnsBtn->set_active(true);
+
+ if ( !( nModifyFlag & ModifyFlags::AllTables ) )
+ m_xAllSheetsCB->set_active( pSearchItem->IsAllTables() );
+
+ // only look for formatting in Writer
+ m_xFormatBtn->hide();
+ m_xNoFormatBtn->hide();
+ m_xAttributeBtn->hide();
+ }
+ else
+ {
+ m_xSearchFormattedCB->hide();
+ m_xWordBtn->set_label( aCalcStr.getToken( 1, '#' ) );
+
+ if ( pSearchItem->GetAppFlag() == SvxSearchApp::DRAW )
+ {
+ m_xSearchAllBtn->hide();
+
+ m_xRegExpBtn->hide();
+ m_xWildcardBtn->hide();
+ m_xLayoutBtn->hide();
+
+ // only look for formatting in Writer
+ m_xFormatBtn->hide();
+ m_xNoFormatBtn->hide();
+ m_xAttributeBtn->hide();
+ }
+ else
+ {
+ m_xWildcardBtn->hide(); /* TODO:WILDCARD do not hide for other apps if they handle it */
+
+ if ( !pSearchList )
+ {
+ // Get attribute sets, if it not has been done already
+ const SfxPoolItem* ppArgs[] = { pSearchItem.get(), nullptr };
+ const SvxSetItem* pSrchSetItem =
+ static_cast<const SvxSetItem*>(rBindings.GetDispatcher()->Execute( FID_SEARCH_SEARCHSET, SfxCallMode::SLOT, ppArgs ));
+
+ if ( pSrchSetItem )
+ InitAttrList_Impl( &pSrchSetItem->GetItemSet(), nullptr );
+
+ const SvxSetItem* pReplSetItem =
+ static_cast<const SvxSetItem*>( rBindings.GetDispatcher()->Execute( FID_SEARCH_REPLACESET, SfxCallMode::SLOT, ppArgs ) );
+
+ if ( pReplSetItem )
+ InitAttrList_Impl( nullptr, &pReplSetItem->GetItemSet() );
+ }
+ }
+ }
+
+ // similarity search?
+ if ( !( nModifyFlag & ModifyFlags::Similarity ) )
+ m_xSimilarityBox->set_active( pSearchItem->IsLevenshtein() );
+ bSet = true;
+
+ FlagHdl_Impl(*m_xSimilarityBox);
+ FlagHdl_Impl(*m_xJapOptionsCB);
+
+ bool bDisableSearch = false;
+ SfxViewShell* pViewShell = SfxViewShell::Current();
+
+ if ( pViewShell )
+ {
+ bool bText = !bSearchPattern;
+
+ if ( pViewShell->HasSelection( bText ) )
+ EnableControl_Impl(*m_xSelectionBtn);
+ else
+ {
+ m_xSelectionBtn->set_active( false );
+ m_xSelectionBtn->set_sensitive(false);
+ }
+ }
+
+ // Pattern Search and there were no AttrSets given
+ if ( bSearchPattern )
+ {
+ SfxObjectShell* pShell = SfxObjectShell::Current();
+
+ if ( pShell && pShell->GetStyleSheetPool() )
+ {
+ // Templates designed
+ m_xSearchTmplLB->clear();
+ m_xReplaceTmplLB->clear();
+ SfxStyleSheetBasePool* pStylePool = pShell->GetStyleSheetPool();
+ SfxStyleSheetBase* pBase = pStylePool->First(pSearchItem->GetFamily());
+
+ while ( pBase )
+ {
+ if ( pBase->IsUsed() )
+ m_xSearchTmplLB->append_text( pBase->GetName() );
+ m_xReplaceTmplLB->append_text( pBase->GetName() );
+ pBase = pStylePool->Next();
+ }
+ m_xSearchTmplLB->set_active_text( pSearchItem->GetSearchString() );
+ m_xReplaceTmplLB->set_active_text( pSearchItem->GetReplaceString() );
+
+ }
+ m_xSearchTmplLB->show();
+
+ if ( bConstruct )
+ // Grab focus only after creating
+ m_xSearchTmplLB->grab_focus();
+ m_xReplaceTmplLB->show();
+ m_xSearchLB->hide();
+ m_xReplaceLB->hide();
+
+ m_xWordBtn->set_sensitive(false);
+ m_xRegExpBtn->set_sensitive(false);
+ m_xWildcardBtn->set_sensitive(false);
+ m_xMatchCaseCB->set_sensitive(false);
+
+ bDisableSearch = !m_xSearchTmplLB->get_count();
+ }
+ else
+ {
+ bool bSetSearch = !( nModifyFlag & ModifyFlags::Search );
+ bool bSetReplace = !( nModifyFlag & ModifyFlags::Replace );
+
+ if ( !(pSearchItem->GetSearchString().isEmpty()) && bSetSearch )
+ m_xSearchLB->set_entry_text( pSearchItem->GetSearchString() );
+ else if (!aSearchStrings.empty())
+ {
+ bool bAttributes =
+ ( ( pSearchList && pSearchList->Count() ) ||
+ ( pReplaceList && pReplaceList->Count() ) );
+
+ if ( bSetSearch && !bAttributes )
+ m_xSearchLB->set_entry_text(aSearchStrings[0]);
+
+ OUString aReplaceTxt = pSearchItem->GetReplaceString();
+
+ if (!aReplaceStrings.empty())
+ aReplaceTxt = aReplaceStrings[0];
+
+ if ( bSetReplace && !bAttributes )
+ m_xReplaceLB->set_entry_text( aReplaceTxt );
+ }
+ m_xSearchLB->show();
+
+ if ( bConstruct )
+ // Grab focus only after creating
+ m_xSearchLB->grab_focus();
+ m_xReplaceLB->show();
+ m_xSearchTmplLB->hide();
+ m_xReplaceTmplLB->hide();
+
+ EnableControl_Impl(*m_xRegExpBtn);
+ EnableControl_Impl(*m_xWildcardBtn);
+ EnableControl_Impl(*m_xMatchCaseCB);
+
+ if ( m_xRegExpBtn->get_active() )
+ m_xWordBtn->set_sensitive(false);
+ else
+ EnableControl_Impl(*m_xWordBtn);
+
+ bDisableSearch = m_xSearchLB->get_active_text().isEmpty() &&
+ m_xSearchAttrText->get_label().isEmpty();
+ }
+ FocusHdl_Impl(*m_xSearchLB);
+
+ if ( bDisableSearch )
+ {
+ m_xSearchBtn->set_sensitive(false);
+ m_xBackSearchBtn->set_sensitive(false);
+ m_xSearchAllBtn->set_sensitive(false);
+ m_xReplaceBtn->set_sensitive(false);
+ m_xReplaceAllBtn->set_sensitive(false);
+ m_xComponentFrame->set_sensitive(false);
+ }
+ else
+ {
+ EnableControl_Impl(*m_xSearchBtn);
+ EnableControl_Impl(*m_xBackSearchBtn);
+ EnableControl_Impl(*m_xReplaceBtn);
+ if (!bWriter || !m_xNotesBtn->get_active())
+ {
+ EnableControl_Impl(*m_xSearchAllBtn);
+ EnableControl_Impl(*m_xReplaceAllBtn);
+ }
+ if (bWriter && pSearchItem->GetNotes())
+ {
+ m_xSearchAllBtn->set_sensitive(false);
+ m_xReplaceAllBtn->set_sensitive(false);
+ }
+ }
+
+ if (!m_xSearchAttrText->get_label().isEmpty())
+ EnableControl_Impl(*m_xNoFormatBtn);
+ else
+ m_xNoFormatBtn->set_sensitive(false);
+
+ if ( !pSearchList )
+ {
+ m_xAttributeBtn->set_sensitive(false);
+ m_xFormatBtn->set_sensitive(false);
+ }
+
+ if ( m_xLayoutBtn->get_active() )
+ {
+ pImpl->bSaveToModule = false;
+ TemplateHdl_Impl(*m_xLayoutBtn);
+ pImpl->bSaveToModule = true;
+ }
+}
+
+
+void SvxSearchDialog::InitAttrList_Impl( const SfxItemSet* pSSet,
+ const SfxItemSet* pRSet )
+{
+ if ( !pSSet && !pRSet )
+ return;
+
+ if ( pImpl->pRanges.empty() && pSSet )
+ pImpl->pRanges = pSSet->GetRanges();
+
+ bool bSetOptimalLayoutSize = false;
+
+ // See to it that are the texts of the attributes are correct
+ OUString aDesc;
+
+ if ( pSSet )
+ {
+ pSearchList.reset(new SearchAttrItemList);
+
+ if ( pSSet->Count() )
+ {
+ pSearchList->Put( *pSSet );
+
+ m_xSearchAttrText->set_label( BuildAttrText_Impl( aDesc, true ) );
+
+ if ( !aDesc.isEmpty() )
+ {
+ if (!m_xSearchAttrText->get_visible())
+ {
+ m_xSearchAttrText->show();
+ bSetOptimalLayoutSize = true;
+ }
+ bFormat |= true;
+ }
+ }
+ }
+
+ if ( pRSet )
+ {
+ pReplaceList.reset(new SearchAttrItemList);
+
+ if ( pRSet->Count() )
+ {
+ pReplaceList->Put( *pRSet );
+
+ m_xReplaceAttrText->set_label( BuildAttrText_Impl( aDesc, false ) );
+
+ if ( !aDesc.isEmpty() )
+ {
+ if (!m_xReplaceAttrText->get_visible())
+ {
+ m_xReplaceAttrText->show();
+ bSetOptimalLayoutSize = true;
+ }
+ bFormat |= true;
+ }
+ }
+ }
+
+ if (bSetOptimalLayoutSize)
+ m_xDialog->resize_to_request();
+}
+
+IMPL_LINK( SvxSearchDialog, LBSelectHdl_Impl, weld::ComboBox&, rCtrl, void )
+{
+ ClickHdl_Impl(&rCtrl);
+}
+
+IMPL_LINK( SvxSearchDialog, FlagHdl_Impl, weld::Toggleable&, rCtrl, void )
+{
+ ClickHdl_Impl(&rCtrl);
+}
+
+void SvxSearchDialog::ClickHdl_Impl(const weld::Widget* pCtrl)
+{
+ if ( pCtrl && !bSet )
+ SetModifyFlag_Impl(pCtrl);
+ else
+ bSet = false;
+
+ if (pCtrl == m_xSimilarityBox.get())
+ {
+ bool bIsChecked = m_xSimilarityBox->get_active();
+
+ if ( bIsChecked )
+ {
+ m_xSimilarityBtn->set_sensitive(true);
+ m_xRegExpBtn->set_active( false );
+ m_xRegExpBtn->set_sensitive(false);
+ m_xWildcardBtn->set_active( false );
+ m_xWildcardBtn->set_sensitive(false);
+ EnableControl_Impl(*m_xWordBtn);
+
+ if ( m_xLayoutBtn->get_active() )
+ {
+ EnableControl_Impl(*m_xMatchCaseCB);
+ m_xLayoutBtn->set_active( false );
+ }
+ m_xRegExpBtn->set_sensitive(false);
+ m_xWildcardBtn->set_sensitive(false);
+ m_xLayoutBtn->set_sensitive(false);
+ m_xFormatBtn->set_sensitive(false);
+ m_xNoFormatBtn->set_sensitive(false);
+ m_xAttributeBtn->set_sensitive(false);
+ }
+ else
+ {
+ EnableControl_Impl(*m_xRegExpBtn);
+ EnableControl_Impl(*m_xWildcardBtn);
+ if (!m_xNotesBtn->get_active())
+ EnableControl_Impl(*m_xLayoutBtn);
+ EnableControl_Impl(*m_xFormatBtn);
+ EnableControl_Impl(*m_xAttributeBtn);
+ m_xSimilarityBtn->set_sensitive(false);
+ }
+ pSearchItem->SetLevenshtein( bIsChecked );
+ }
+ else if (pCtrl == m_xNotesBtn.get())
+ {
+ if (m_xNotesBtn->get_active())
+ {
+ m_xLayoutBtn->set_sensitive(false);
+ m_xSearchAllBtn->set_sensitive(false);
+ m_xReplaceAllBtn->set_sensitive(false);
+ }
+ else
+ {
+ EnableControl_Impl(*m_xLayoutBtn);
+ ModifyHdl_Impl(*m_xSearchLB);
+ }
+ }
+ else
+ {
+ if ( m_xLayoutBtn->get_active() && !bFormat )
+ {
+ m_xWordBtn->set_active( false );
+ m_xWordBtn->set_sensitive(false);
+ m_xRegExpBtn->set_active( false );
+ m_xRegExpBtn->set_sensitive(false);
+ m_xWildcardBtn->set_active( false );
+ m_xWildcardBtn->set_sensitive(false);
+ m_xMatchCaseCB->set_active( false );
+ m_xMatchCaseCB->set_sensitive(false);
+ m_xNotesBtn->set_sensitive(false);
+
+ if ( m_xSearchTmplLB->get_count() )
+ {
+ EnableControl_Impl(*m_xSearchBtn);
+ EnableControl_Impl(*m_xBackSearchBtn);
+ EnableControl_Impl(*m_xSearchAllBtn);
+ EnableControl_Impl(*m_xReplaceBtn);
+ EnableControl_Impl(*m_xReplaceAllBtn);
+ }
+ }
+ else
+ {
+ EnableControl_Impl(*m_xRegExpBtn);
+ EnableControl_Impl(*m_xWildcardBtn);
+ EnableControl_Impl(*m_xMatchCaseCB);
+ EnableControl_Impl(*m_xNotesBtn);
+
+ if ( m_xRegExpBtn->get_active() )
+ {
+ m_xWordBtn->set_active( false );
+ m_xWordBtn->set_sensitive(false);
+ m_xWildcardBtn->set_active( false );
+ m_xWildcardBtn->set_sensitive(false);
+ m_xSimilarityBox->set_active( false );
+ m_xSimilarityBox->set_sensitive(false);
+ m_xSimilarityBtn->set_sensitive(false);
+ }
+ else if ( m_xWildcardBtn->get_active() )
+ {
+ m_xRegExpBtn->set_active( false );
+ m_xRegExpBtn->set_sensitive(false);
+ m_xSimilarityBox->set_active( false );
+ m_xSimilarityBox->set_sensitive(false);
+ m_xSimilarityBtn->set_sensitive(false);
+ }
+ else
+ {
+ EnableControl_Impl(*m_xWordBtn);
+ EnableControl_Impl(*m_xSimilarityBox);
+ }
+
+ // Search-string in place? then enable Buttons
+ bSet = true;
+ ModifyHdl_Impl(*m_xSearchLB);
+ }
+ }
+
+ if (pCtrl == m_xAllSheetsCB.get())
+ {
+ bSet = true;
+ ModifyHdl_Impl(*m_xSearchLB);
+ }
+
+ if (pCtrl == m_xJapOptionsCB.get())
+ {
+ bool bEnableJapOpt = m_xJapOptionsCB->get_active();
+ m_xMatchCaseCB->set_sensitive(!bEnableJapOpt );
+ m_xJapMatchFullHalfWidthCB->set_sensitive(!bEnableJapOpt );
+ m_xJapOptionsBtn->set_sensitive( bEnableJapOpt );
+ }
+
+ if ( pImpl->bSaveToModule )
+ SaveToModule_Impl();
+}
+
+IMPL_LINK(SvxSearchDialog, CommandHdl_Impl, weld::Button&, rBtn, void)
+{
+ bool bInclusive = ( m_xLayoutBtn->get_label() == aLayoutStr );
+
+ if ( ( &rBtn == m_xSearchBtn.get() ) ||
+ (&rBtn == m_xBackSearchBtn.get()) ||
+ ( &rBtn == m_xSearchAllBtn.get() )||
+ ( &rBtn == m_xReplaceBtn.get() ) ||
+ ( &rBtn == m_xReplaceAllBtn.get() ) )
+ {
+ if ( m_xLayoutBtn->get_active() && !bInclusive )
+ {
+ pSearchItem->SetSearchString ( m_xSearchTmplLB->get_active_text() );
+ pSearchItem->SetReplaceString( m_xReplaceTmplLB->get_active_text() );
+ }
+ else
+ {
+ pSearchItem->SetSearchString ( m_xSearchLB->get_active_text() );
+ pSearchItem->SetReplaceString( m_xReplaceLB->get_active_text() );
+
+ if ( &rBtn == m_xReplaceBtn.get() )
+ Remember_Impl( m_xReplaceLB->get_active_text(), false );
+ else
+ {
+ Remember_Impl( m_xSearchLB->get_active_text(), true );
+
+ if ( &rBtn == m_xReplaceAllBtn.get() )
+ Remember_Impl( m_xReplaceLB->get_active_text(), false );
+ }
+ }
+
+ pSearchItem->SetRegExp( false );
+ pSearchItem->SetWildcard( false );
+ pSearchItem->SetLevenshtein( false );
+ if (GetCheckBoxValue(*m_xRegExpBtn))
+ pSearchItem->SetRegExp( true );
+ else if (GetCheckBoxValue(*m_xWildcardBtn))
+ pSearchItem->SetWildcard( true );
+ else if (GetCheckBoxValue(*m_xSimilarityBox))
+ pSearchItem->SetLevenshtein( true );
+
+ pSearchItem->SetWordOnly(GetCheckBoxValue(*m_xWordBtn));
+
+ bool bSetBackwards = false;
+ if( &rBtn == m_xBackSearchBtn.get())
+ {
+ bSetBackwards = true;
+ }
+ else if( &rBtn == m_xReplaceBtn.get())
+ {
+ bSetBackwards = GetCheckBoxValue(*m_xReplaceBackwardsCB);
+ bReplaceBackwards = GetCheckBoxValue(*m_xReplaceBackwardsCB);
+ }
+
+ pSearchItem->SetBackward(bSetBackwards);
+
+ pSearchItem->SetNotes(GetCheckBoxValue(*m_xNotesBtn));
+ pSearchItem->SetPattern(GetCheckBoxValue(*m_xLayoutBtn));
+ pSearchItem->SetSelection(GetCheckBoxValue(*m_xSelectionBtn));
+ pSearchItem->SetUseAsianOptions(GetCheckBoxValue(*m_xJapOptionsCB));
+ TransliterationFlags nFlags = GetTransliterationFlags();
+ if( !pSearchItem->IsUseAsianOptions())
+ nFlags &= TransliterationFlags::IGNORE_CASE |
+ TransliterationFlags::IGNORE_WIDTH;
+ if (GetNegatedCheckBoxValue(*m_xIncludeDiacritics))
+ nFlags |= TransliterationFlags::IGNORE_DIACRITICS_CTL;
+ if (GetNegatedCheckBoxValue(*m_xIncludeKashida))
+ nFlags |= TransliterationFlags::IGNORE_KASHIDA_CTL;
+ pSearchItem->SetTransliterationFlags( nFlags );
+
+ if ( !bWriter )
+ {
+ if ( m_xCalcSearchInLB->get_active() != -1)
+ pSearchItem->SetCellType( static_cast<SvxSearchCellType>(m_xCalcSearchInLB->get_active()) );
+
+ pSearchItem->SetRowDirection( m_xRowsBtn->get_active() );
+ pSearchItem->SetAllTables( m_xAllSheetsCB->get_active() );
+ pSearchItem->SetSearchFormatted( m_xSearchFormattedCB->get_active() );
+ }
+
+ if ((&rBtn == m_xSearchBtn.get()) || (&rBtn == m_xBackSearchBtn.get()))
+ pSearchItem->SetCommand( SvxSearchCmd::FIND );
+ else if ( &rBtn == m_xSearchAllBtn.get() )
+ pSearchItem->SetCommand( SvxSearchCmd::FIND_ALL );
+ else if ( &rBtn == m_xReplaceBtn.get() )
+ pSearchItem->SetCommand( SvxSearchCmd::REPLACE );
+ else if ( &rBtn == m_xReplaceAllBtn.get() )
+ pSearchItem->SetCommand( SvxSearchCmd::REPLACE_ALL );
+
+ // when looking for templates, delete format lists
+ if ( !bFormat && pSearchItem->GetPattern() )
+ {
+ if ( pSearchList )
+ pSearchList->Clear();
+
+ if ( pReplaceList )
+ pReplaceList->Clear();
+ }
+ nModifyFlag = ModifyFlags::NONE;
+ const SfxPoolItem* ppArgs[] = { pSearchItem.get(), nullptr };
+ rBindings.ExecuteSynchron( FID_SEARCH_NOW, ppArgs );
+ }
+ else if ( &rBtn == m_xCloseBtn.get() )
+ {
+ if ( !m_xLayoutBtn->get_active() || bInclusive )
+ {
+ OUString aStr( m_xSearchLB->get_active_text() );
+
+ if ( !aStr.isEmpty() )
+ Remember_Impl( aStr, true );
+ aStr = m_xReplaceLB->get_active_text();
+
+ if ( !aStr.isEmpty() )
+ Remember_Impl( aStr, false );
+ }
+ SaveToModule_Impl();
+ Close();
+ }
+ else if (&rBtn == m_xSimilarityBtn.get())
+ {
+ SvxAbstractDialogFactory* pFact = SvxAbstractDialogFactory::Create();
+ ScopedVclPtr<AbstractSvxSearchSimilarityDialog> pDlg(pFact->CreateSvxSearchSimilarityDialog(m_xDialog.get(),
+ pSearchItem->IsLEVRelaxed(),
+ pSearchItem->GetLEVOther(),
+ pSearchItem->GetLEVShorter(),
+ pSearchItem->GetLEVLonger() ));
+ if ( executeSubDialog(pDlg.get()) == RET_OK )
+ {
+ pSearchItem->SetLEVRelaxed( pDlg->IsRelaxed() );
+ pSearchItem->SetLEVOther( pDlg->GetOther() );
+ pSearchItem->SetLEVShorter( pDlg->GetShorter() );
+ pSearchItem->SetLEVLonger( pDlg->GetLonger() );
+ SaveToModule_Impl();
+ }
+ }
+ else if (&rBtn == m_xJapOptionsBtn.get())
+ {
+ SfxItemSet aSet( SfxGetpApp()->GetPool() );
+ pSearchItem->SetTransliterationFlags( GetTransliterationFlags() );
+ SvxAbstractDialogFactory* pFact = SvxAbstractDialogFactory::Create();
+ ScopedVclPtr<AbstractSvxJSearchOptionsDialog> aDlg(pFact->CreateSvxJSearchOptionsDialog(m_xDialog.get(), aSet,
+ pSearchItem->GetTransliterationFlags() ));
+ int nRet = executeSubDialog(aDlg.get());
+ if (RET_OK == nRet) //! true only if FillItemSet of SvxJSearchOptionsPage returns true
+ {
+ TransliterationFlags nFlags = aDlg->GetTransliterationFlags();
+ pSearchItem->SetTransliterationFlags( nFlags );
+ ApplyTransliterationFlags_Impl( nFlags );
+ }
+ }
+ else if (&rBtn == m_xSearchComponent1PB.get() || &rBtn == m_xSearchComponent2PB.get())
+ {
+ uno::Sequence < beans::PropertyValue > aArgs(2);
+ beans::PropertyValue* pArgs = aArgs.getArray();
+ pArgs[0].Name = "SearchString";
+ pArgs[0].Value <<= m_xSearchLB->get_active_text();
+ pArgs[1].Name = "ParentWindow";
+ pArgs[1].Value <<= m_xDialog->GetXWindow();
+ if (&rBtn == m_xSearchComponent1PB.get())
+ {
+ if ( pImpl->xCommand1Dispatch.is() )
+ pImpl->xCommand1Dispatch->dispatch(pImpl->aCommand1URL, aArgs);
+ }
+ else
+ {
+ if ( pImpl->xCommand2Dispatch.is() )
+ pImpl->xCommand2Dispatch->dispatch(pImpl->aCommand2URL, aArgs);
+ }
+ }
+}
+
+
+IMPL_LINK( SvxSearchDialog, ModifyHdl_Impl, weld::ComboBox&, rEd, void )
+{
+ if ( !bSet )
+ SetModifyFlag_Impl( &rEd );
+ else
+ bSet = false;
+
+ // Calc allows searching for empty cells.
+ bool bAllowEmptySearch = (pSearchItem->GetAppFlag() == SvxSearchApp::CALC);
+
+ if (&rEd != m_xSearchLB.get() && &rEd != m_xReplaceLB.get())
+ return;
+
+ sal_Int32 nSrchTxtLen = m_xSearchLB->get_active_text().getLength();
+ sal_Int32 nReplTxtLen = 0;
+ if (bAllowEmptySearch)
+ nReplTxtLen = m_xReplaceLB->get_active_text().getLength();
+ sal_Int32 nAttrTxtLen = m_xSearchAttrText->get_label().getLength();
+
+ if (nSrchTxtLen || nReplTxtLen || nAttrTxtLen)
+ {
+ EnableControl_Impl(*m_xSearchBtn);
+ EnableControl_Impl(*m_xBackSearchBtn);
+ EnableControl_Impl(*m_xReplaceBtn);
+ if (!bWriter || !m_xNotesBtn->get_active())
+ {
+ EnableControl_Impl(*m_xSearchAllBtn);
+ EnableControl_Impl(*m_xReplaceAllBtn);
+ }
+ }
+ else
+ {
+ m_xComponentFrame->set_sensitive(false);
+ m_xSearchBtn->set_sensitive(false);
+ m_xBackSearchBtn->set_sensitive(false);
+ m_xSearchAllBtn->set_sensitive(false);
+ m_xReplaceBtn->set_sensitive(false);
+ m_xReplaceAllBtn->set_sensitive(false);
+ }
+}
+
+IMPL_LINK_NOARG(SvxSearchDialog, TemplateHdl_Impl, weld::Toggleable&, void)
+{
+ if ( pImpl->bSaveToModule )
+ SaveToModule_Impl();
+
+ if ( bFormat )
+ return;
+ OUString sDesc;
+
+ if ( m_xLayoutBtn->get_active() )
+ {
+ if ( !pFamilyController )
+ {
+ sal_uInt16 nId = 0;
+
+ // Enable templates controller
+ switch ( pSearchItem->GetFamily() )
+ {
+ case SfxStyleFamily::Char:
+ nId = SID_STYLE_FAMILY1; break;
+
+ case SfxStyleFamily::Para:
+ nId = SID_STYLE_FAMILY2; break;
+
+ case SfxStyleFamily::Frame:
+ nId = SID_STYLE_FAMILY3; break;
+
+ case SfxStyleFamily::Page:
+ nId = SID_STYLE_FAMILY4; break;
+
+ case SfxStyleFamily::All:
+ break;
+
+ default:
+ OSL_FAIL( "StyleSheetFamily was changed?" );
+ }
+
+ rBindings.EnterRegistrations();
+ pFamilyController.reset(
+ new SvxSearchController( nId, rBindings, *this ) );
+ rBindings.LeaveRegistrations();
+ m_xSearchTmplLB->clear();
+ m_xReplaceTmplLB->clear();
+
+ m_xSearchTmplLB->show();
+ m_xReplaceTmplLB->show();
+ m_xSearchLB->hide();
+ m_xReplaceLB->hide();
+
+ m_xSearchAttrText->set_label( sDesc );
+ m_xReplaceAttrText->set_label( sDesc );
+
+ if(!sDesc.isEmpty())
+ {
+ if (!m_xReplaceAttrText->get_visible() || !m_xReplaceAttrText->get_visible())
+ {
+ m_xSearchAttrText->show();
+ m_xReplaceAttrText->show();
+ m_xDialog->resize_to_request();
+ }
+ }
+ }
+ m_xFormatBtn->set_sensitive(false);
+ m_xNoFormatBtn->set_sensitive(false);
+ m_xAttributeBtn->set_sensitive(false);
+ m_xSimilarityBox->set_sensitive(false);
+ m_xSimilarityBtn->set_sensitive(false);
+ }
+ else
+ {
+ // Disable templates controller
+ rBindings.EnterRegistrations();
+ pFamilyController.reset();
+ rBindings.LeaveRegistrations();
+
+ m_xSearchLB->show();
+ m_xReplaceLB->show();
+ m_xSearchTmplLB->hide();
+ m_xReplaceTmplLB->hide();
+
+ m_xSearchAttrText->set_label( BuildAttrText_Impl( sDesc, true ) );
+ m_xReplaceAttrText->set_label( BuildAttrText_Impl( sDesc, false ) );
+
+ if(!sDesc.isEmpty())
+ {
+ if (!m_xReplaceAttrText->get_visible() || !m_xReplaceAttrText->get_visible())
+ {
+ m_xSearchAttrText->show();
+ m_xReplaceAttrText->show();
+ m_xDialog->resize_to_request();
+ }
+ }
+
+ EnableControl_Impl(*m_xFormatBtn);
+ EnableControl_Impl(*m_xAttributeBtn);
+ EnableControl_Impl(*m_xSimilarityBox);
+
+ FocusHdl_Impl( bSearch ? *m_xSearchLB : *m_xReplaceLB );
+ }
+ bSet = true;
+ pImpl->bSaveToModule = false;
+ FlagHdl_Impl(*m_xLayoutBtn);
+ pImpl->bSaveToModule = true;
+}
+
+void SvxSearchDialog::Remember_Impl( const OUString &rStr, bool _bSearch )
+{
+ if ( rStr.isEmpty() )
+ return;
+
+ std::vector<OUString>* pArr = _bSearch ? &aSearchStrings : &aReplaceStrings;
+ weld::ComboBox* pListBox = _bSearch ? m_xSearchLB.get() : m_xReplaceLB.get();
+
+ // ignore identical strings
+ if (std::find(pArr->begin(), pArr->end(), rStr) != pArr->end())
+ return;
+
+ pArr->insert(pArr->begin(), rStr);
+ pListBox->insert_text(0, rStr);
+
+ // delete oldest entry at maximum occupancy (ListBox and Array)
+ size_t nArrSize = pArr->size();
+ if (nArrSize > nRememberSize)
+ {
+ pListBox->remove(nArrSize - 1);
+ pArr->erase(pArr->begin() + nArrSize - 1);
+ }
+}
+
+void SvxSearchDialog::TemplatesChanged_Impl( SfxStyleSheetBasePool& rPool )
+{
+ OUString aOldSrch( m_xSearchTmplLB->get_active_text() );
+ OUString aOldRepl( m_xReplaceTmplLB->get_active_text() );
+ m_xSearchTmplLB->clear();
+ m_xReplaceTmplLB->clear();
+ m_xSearchTmplLB->freeze();
+ m_xReplaceTmplLB->freeze();
+ SfxStyleSheetBase* pBase = rPool.First(pSearchItem->GetFamily());
+
+ while ( pBase )
+ {
+ if ( pBase->IsUsed() )
+ m_xSearchTmplLB->append_text( pBase->GetName() );
+ m_xReplaceTmplLB->append_text( pBase->GetName() );
+ pBase = rPool.Next();
+ }
+ m_xSearchTmplLB->thaw();
+ m_xReplaceTmplLB->thaw();
+ m_xSearchTmplLB->set_active(0);
+
+ if ( !aOldSrch.isEmpty() )
+ m_xSearchTmplLB->set_active_text( aOldSrch );
+ m_xReplaceTmplLB->set_active(0);
+
+ if ( !aOldRepl.isEmpty() )
+ m_xReplaceTmplLB->set_active_text( aOldRepl );
+
+ if ( m_xSearchTmplLB->get_count() )
+ {
+ EnableControl_Impl(*m_xSearchBtn);
+ EnableControl_Impl(*m_xBackSearchBtn);
+ EnableControl_Impl(*m_xSearchAllBtn);
+ EnableControl_Impl(*m_xReplaceBtn);
+ EnableControl_Impl(*m_xReplaceAllBtn);
+ }
+}
+
+
+void SvxSearchDialog::EnableControls_Impl( const SearchOptionFlags nFlags )
+{
+ if ( nFlags == nOptions )
+ return;
+ else
+ nOptions = nFlags;
+
+ bool bNoSearch = true;
+
+ bool bEnableSearch = bool( SearchOptionFlags::SEARCH & nOptions );
+ m_xSearchBtn->set_sensitive(bEnableSearch);
+ m_xBackSearchBtn->set_sensitive(bEnableSearch);
+
+ if( bEnableSearch )
+ bNoSearch = false;
+
+
+ if ( SearchOptionFlags::SEARCHALL & nOptions )
+ {
+ m_xSearchAllBtn->set_sensitive(true);
+ bNoSearch = false;
+ }
+ else
+ m_xSearchAllBtn->set_sensitive(false);
+ if ( SearchOptionFlags::REPLACE & nOptions )
+ {
+ m_xReplaceBtn->set_sensitive(true);
+ m_xReplaceFrame->set_sensitive(true);
+ m_xReplaceLB->set_sensitive(true);
+ m_xReplaceTmplLB->set_sensitive(true);
+ bNoSearch = false;
+ }
+ else
+ {
+ m_xReplaceBtn->set_sensitive(false);
+ m_xReplaceFrame->set_sensitive(false);
+ m_xReplaceLB->set_sensitive(false);
+ m_xReplaceTmplLB->set_sensitive(false);
+ }
+ if ( SearchOptionFlags::REPLACE_ALL & nOptions )
+ {
+ m_xReplaceAllBtn->set_sensitive(true);
+ bNoSearch = false;
+ }
+ else
+ m_xReplaceAllBtn->set_sensitive(false);
+ m_xComponentFrame->set_sensitive(!bNoSearch);
+ m_xSearchBtn->set_sensitive( !bNoSearch );
+ m_xBackSearchBtn->set_sensitive( !bNoSearch );
+ m_xSearchFrame->set_sensitive( !bNoSearch );
+ m_xSearchLB->set_sensitive( !bNoSearch );
+ m_xNotesBtn->set_sensitive(true);
+
+ if ( SearchOptionFlags::WHOLE_WORDS & nOptions )
+ m_xWordBtn->set_sensitive(true);
+ else
+ m_xWordBtn->set_sensitive(false);
+ if ( SearchOptionFlags::BACKWARDS & nOptions )
+ {
+ m_xBackSearchBtn->set_sensitive(true);
+ m_xReplaceBackwardsCB->set_sensitive(true);
+ }
+ else
+ {
+ m_xBackSearchBtn->set_sensitive(false);
+ m_xReplaceBackwardsCB->set_sensitive(false);
+ }
+ if ( SearchOptionFlags::REG_EXP & nOptions )
+ m_xRegExpBtn->set_sensitive(true);
+ else
+ m_xRegExpBtn->set_sensitive(false);
+ if ( SearchOptionFlags::WILDCARD & nOptions )
+ m_xWildcardBtn->set_sensitive(true);
+ else
+ m_xWildcardBtn->set_sensitive(false);
+ if ( SearchOptionFlags::EXACT & nOptions )
+ m_xMatchCaseCB->set_sensitive(true);
+ else
+ m_xMatchCaseCB->set_sensitive(false);
+ if ( SearchOptionFlags::SELECTION & nOptions )
+ m_xSelectionBtn->set_sensitive(true);
+ else
+ m_xSelectionBtn->set_sensitive(false);
+ if ( SearchOptionFlags::FAMILIES & nOptions )
+ m_xLayoutBtn->set_sensitive(true);
+ else
+ m_xLayoutBtn->set_sensitive(false);
+ if ( SearchOptionFlags::FORMAT & nOptions )
+ {
+ m_xAttributeBtn->set_sensitive(true);
+ m_xFormatBtn->set_sensitive(true);
+ m_xNoFormatBtn->set_sensitive(true);
+ }
+ else
+ {
+ m_xAttributeBtn->set_sensitive(false);
+ m_xFormatBtn->set_sensitive(false);
+ m_xNoFormatBtn->set_sensitive(false);
+ }
+
+ if ( SearchOptionFlags::SIMILARITY & nOptions )
+ {
+ m_xSimilarityBox->set_sensitive(true);
+ m_xSimilarityBtn->set_sensitive(true);
+ }
+ else
+ {
+ m_xSimilarityBox->set_sensitive(false);
+ m_xSimilarityBtn->set_sensitive(false);
+ }
+
+ if ( pSearchItem )
+ {
+ Init_Impl( pSearchItem->GetPattern() &&
+ ( !pSearchList || !pSearchList->Count() ) );
+ if (SvxSearchDialog::IsOtherOptionsExpanded())
+ m_xOtherOptionsExpander->set_expanded(true);
+ }
+}
+
+void SvxSearchDialog::EnableControl_Impl(const weld::Widget& rCtrl)
+{
+ if (m_xSearchBtn.get() == &rCtrl && ( SearchOptionFlags::SEARCH & nOptions ) )
+ {
+ m_xComponentFrame->set_sensitive(true);
+ m_xSearchBtn->set_sensitive(true);
+ return;
+ }
+ if ( m_xSearchAllBtn.get() == &rCtrl &&
+ ( SearchOptionFlags::SEARCHALL & nOptions ) )
+ {
+ m_xSearchAllBtn->set_sensitive(true);
+ return;
+ }
+ if ( m_xReplaceBtn.get() == &rCtrl && ( SearchOptionFlags::REPLACE & nOptions ) )
+ {
+ m_xReplaceBtn->set_sensitive(true);
+ return;
+ }
+ if ( m_xReplaceAllBtn.get() == &rCtrl &&
+ ( SearchOptionFlags::REPLACE_ALL & nOptions ) )
+ {
+ m_xReplaceAllBtn->set_sensitive(true);
+ return;
+ }
+ if ( m_xWordBtn.get() == &rCtrl && ( SearchOptionFlags::WHOLE_WORDS & nOptions ) )
+ {
+ m_xWordBtn->set_sensitive(true);
+ return;
+ }
+ if ( SearchOptionFlags::BACKWARDS & nOptions )
+ {
+ if( m_xBackSearchBtn.get() == &rCtrl )
+ {
+ m_xBackSearchBtn->set_sensitive(true);
+ return;
+ }
+ else if ( m_xReplaceBackwardsCB.get() == &rCtrl )
+ {
+ m_xReplaceBackwardsCB->set_sensitive(true);
+ return;
+ }
+ }
+ if (m_xNotesBtn.get() == &rCtrl)
+ {
+ m_xNotesBtn->set_sensitive(true);
+ return;
+ }
+ if ( m_xRegExpBtn.get() == &rCtrl && ( SearchOptionFlags::REG_EXP & nOptions )
+ && !m_xSimilarityBox->get_active() && !m_xWildcardBtn->get_active())
+ {
+ m_xRegExpBtn->set_sensitive(true);
+ return;
+ }
+ if ( m_xWildcardBtn.get() == &rCtrl && ( SearchOptionFlags::WILDCARD & nOptions )
+ && !m_xSimilarityBox->get_active() && !m_xRegExpBtn->get_active())
+ {
+ m_xWildcardBtn->set_sensitive(true);
+ return;
+ }
+ if ( m_xMatchCaseCB.get() == &rCtrl && ( SearchOptionFlags::EXACT & nOptions ) )
+ {
+ if (!m_xJapOptionsCB->get_active())
+ m_xMatchCaseCB->set_sensitive(true);
+ return;
+ }
+ if ( m_xSelectionBtn.get() == &rCtrl && ( SearchOptionFlags::SELECTION & nOptions ) )
+ {
+ m_xSelectionBtn->set_sensitive(true);
+ return;
+ }
+ if ( m_xLayoutBtn.get() == &rCtrl && ( SearchOptionFlags::FAMILIES & nOptions ) )
+ {
+ m_xLayoutBtn->set_sensitive(true);
+ return;
+ }
+ if ( m_xAttributeBtn.get() == &rCtrl
+ && ( SearchOptionFlags::FORMAT & nOptions )
+ && pSearchList )
+ {
+ m_xAttributeBtn->set_sensitive( pImpl->bFocusOnSearch );
+ }
+ if ( m_xFormatBtn.get() == &rCtrl && ( SearchOptionFlags::FORMAT & nOptions ) )
+ {
+ m_xFormatBtn->set_sensitive(true);
+ return;
+ }
+ if ( m_xNoFormatBtn.get() == &rCtrl && ( SearchOptionFlags::FORMAT & nOptions ) )
+ {
+ m_xNoFormatBtn->set_sensitive(true);
+ return;
+ }
+ if ( m_xSimilarityBox.get() == &rCtrl && ( SearchOptionFlags::SIMILARITY & nOptions )
+ && !m_xRegExpBtn->get_active() && !m_xWildcardBtn->get_active())
+ {
+ m_xSimilarityBox->set_sensitive(true);
+
+ if ( m_xSimilarityBox->get_active() )
+ m_xSimilarityBtn->set_sensitive(true);
+ }
+}
+
+void SvxSearchDialog::SetItem_Impl( const SvxSearchItem* pItem )
+{
+ //TODO: save pItem and process later if m_executingSubDialog?
+ if ( pItem && !m_executingSubDialog )
+ {
+ pSearchItem.reset(pItem->Clone());
+ Init_Impl( pSearchItem->GetPattern() &&
+ ( !pSearchList || !pSearchList->Count() ) );
+ }
+}
+
+IMPL_LINK(SvxSearchDialog, FocusHdl_Impl, weld::Widget&, rControl, void)
+{
+ sal_Int32 nTxtLen = m_xSearchAttrText->get_label().getLength();
+ weld::Widget* pCtrl = &rControl;
+ if (pCtrl == m_xSearchLB.get())
+ {
+ if (pCtrl->has_focus())
+ pImpl->bFocusOnSearch = true;
+ pCtrl = m_xSearchLB.get();
+ bSearch = true;
+
+ if( nTxtLen )
+ EnableControl_Impl(*m_xNoFormatBtn);
+ else
+ m_xNoFormatBtn->set_sensitive(false);
+ EnableControl_Impl(*m_xAttributeBtn);
+ }
+ else
+ {
+ pImpl->bFocusOnSearch = false;
+ pCtrl = m_xReplaceLB.get();
+ bSearch = false;
+
+ if (!m_xReplaceAttrText->get_label().isEmpty())
+ EnableControl_Impl(*m_xNoFormatBtn);
+ else
+ m_xNoFormatBtn->set_sensitive(false);
+ m_xAttributeBtn->set_sensitive(false);
+ }
+ bSet = true;
+
+ weld::ComboBox &rComboBox = dynamic_cast<weld::ComboBox&>(*pCtrl);
+ rComboBox.select_entry_region(0, -1);
+ ModifyHdl_Impl(rComboBox);
+
+ if (bFormat && nTxtLen)
+ m_xLayoutBtn->set_label(aLayoutStr);
+ else
+ {
+ SvtModuleOptions::EFactory eFactory = getModule(rBindings);
+ bool bWriterApp =
+ eFactory == SvtModuleOptions::EFactory::WRITER ||
+ eFactory == SvtModuleOptions::EFactory::WRITERWEB ||
+ eFactory == SvtModuleOptions::EFactory::WRITERGLOBAL;
+ bool bCalcApp = eFactory == SvtModuleOptions::EFactory::CALC;
+
+ if (bWriterApp)
+ m_xLayoutBtn->set_label(aLayoutWriterStr);
+ else
+ {
+ if (bCalcApp)
+ m_xLayoutBtn->set_label(aLayoutCalcStr);
+ else
+ m_xLayoutBtn->set_label(aStylesStr);
+ }
+ }
+}
+
+IMPL_LINK_NOARG(SvxSearchDialog, LoseFocusHdl_Impl, weld::Widget&, void)
+{
+ SaveToModule_Impl();
+}
+
+IMPL_LINK_NOARG(SvxSearchDialog, FormatHdl_Impl, weld::Button&, void)
+{
+ SfxObjectShell* pSh = SfxObjectShell::Current();
+
+ DBG_ASSERT( pSh, "no DocShell" );
+
+ if ( !pSh || pImpl->pRanges.empty() )
+ return;
+
+ SfxItemPool& rPool = pSh->GetPool();
+ SfxItemSet aSet(rPool, pImpl->pRanges);
+
+ aSet.MergeRange(SID_ATTR_PARA_MODEL, SID_ATTR_PARA_MODEL);
+
+ sal_uInt16 nBrushWhich = pSh->GetPool().GetWhich(SID_ATTR_BRUSH);
+ aSet.MergeRange(nBrushWhich, nBrushWhich);
+
+ aSet.MergeRange(XATTR_FILL_FIRST, XATTR_FILL_LAST);
+
+ OUString aTxt;
+
+ aSet.InvalidateAllItems();
+ aSet.Put(SvxBrushItem(nBrushWhich));
+
+ if ( bSearch )
+ {
+ aTxt = SvxResId( RID_SVXSTR_SEARCH );
+ pSearchList->Get( aSet );
+ }
+ else
+ {
+ aTxt = SvxResId( RID_SVXSTR_REPLACE );
+ pReplaceList->Get( aSet );
+ }
+ aSet.DisableItem(SID_ATTR_PARA_MODEL);
+ aSet.DisableItem(rPool.GetWhich(SID_ATTR_PARA_PAGEBREAK));
+ aSet.DisableItem(rPool.GetWhich(SID_ATTR_PARA_KEEP));
+
+
+ SvxAbstractDialogFactory* pFact = SvxAbstractDialogFactory::Create();
+ ScopedVclPtr<SfxAbstractTabDialog> pDlg(pFact->CreateTabItemDialog(m_xDialog.get(), aSet));
+ pDlg->SetText( aTxt );
+
+ if ( executeSubDialog(pDlg.get()) != RET_OK )
+ return;
+
+ DBG_ASSERT( pDlg->GetOutputItemSet(), "invalid Output-Set" );
+ SfxItemSet aOutSet( *pDlg->GetOutputItemSet() );
+
+ SearchAttrItemList* pList = bSearch ? pSearchList.get() : pReplaceList.get();
+
+ const SfxPoolItem* pItem;
+ for( sal_uInt16 n = 0; n < pList->Count(); ++n )
+ {
+ SearchAttrItem* pAItem = &pList->GetObject(n);
+ if( !IsInvalidItem( pAItem->pItem ) &&
+ SfxItemState::SET == aOutSet.GetItemState(
+ pAItem->pItem->Which(), false, &pItem ) )
+ {
+ delete pAItem->pItem;
+ pAItem->pItem = pItem->Clone();
+ aOutSet.ClearItem( pAItem->pItem->Which() );
+ }
+ }
+
+ if( aOutSet.Count() )
+ pList->Put( aOutSet );
+
+ PaintAttrText_Impl(); // Set AttributeText in GroupBox
+}
+
+IMPL_LINK_NOARG(SvxSearchDialog, NoFormatHdl_Impl, weld::Button&, void)
+{
+ SvtModuleOptions::EFactory eFactory = getModule(rBindings);
+ bool bWriterApp =
+ eFactory == SvtModuleOptions::EFactory::WRITER ||
+ eFactory == SvtModuleOptions::EFactory::WRITERWEB ||
+ eFactory == SvtModuleOptions::EFactory::WRITERGLOBAL;
+ bool bCalcApp = eFactory == SvtModuleOptions::EFactory::CALC;
+
+ if (bCalcApp)
+ m_xLayoutBtn->set_label( aLayoutCalcStr );
+ else
+ {
+ if (bWriterApp)
+ m_xLayoutBtn->set_label( aLayoutWriterStr);
+ else
+ m_xLayoutBtn->set_label( aStylesStr );
+ }
+
+ bFormat = false;
+ m_xLayoutBtn->set_active( false );
+
+ bool bSetOptimalLayoutSize = false;
+
+ if ( bSearch )
+ {
+ pSearchList->Clear();
+ m_xSearchAttrText->set_label( "" );
+ if (m_xSearchAttrText->get_visible())
+ {
+ m_xSearchAttrText->hide();
+ bSetOptimalLayoutSize = true;
+ }
+ }
+ else
+ {
+ pReplaceList->Clear();
+ m_xReplaceAttrText->set_label( "" );
+ if (m_xReplaceAttrText->get_visible())
+ {
+ m_xReplaceAttrText->hide();
+ bSetOptimalLayoutSize = true;
+ }
+ }
+
+ if (bSetOptimalLayoutSize)
+ m_xDialog->resize_to_request();
+
+ pImpl->bSaveToModule = false;
+ TemplateHdl_Impl(*m_xLayoutBtn);
+ pImpl->bSaveToModule = true;
+ m_xNoFormatBtn->set_sensitive(false);
+}
+
+IMPL_LINK_NOARG(SvxSearchDialog, AttributeHdl_Impl, weld::Button&, void)
+{
+ if ( !pSearchList || pImpl->pRanges.empty() )
+ return;
+
+ SvxAbstractDialogFactory* pFact = SvxAbstractDialogFactory::Create();
+ ScopedVclPtr<VclAbstractDialog> pDlg(pFact->CreateSvxSearchAttributeDialog(m_xDialog.get(), *pSearchList, pImpl->pRanges));
+ executeSubDialog(pDlg.get());
+ PaintAttrText_Impl();
+}
+
+IMPL_LINK( SvxSearchDialog, TimeoutHdl_Impl, Timer *, pTimer, void )
+{
+ SfxViewShell* pViewShell = SfxViewShell::Current();
+
+ if ( pViewShell )
+ {
+ if ( pViewShell->HasSelection( m_xSearchLB->get_visible() ) )
+ EnableControl_Impl(*m_xSelectionBtn);
+ else
+ {
+ m_xSelectionBtn->set_active( false );
+ m_xSelectionBtn->set_sensitive(false);
+ }
+ }
+
+ pTimer->Start();
+}
+
+OUString& SvxSearchDialog::BuildAttrText_Impl( OUString& rStr,
+ bool bSrchFlag ) const
+{
+ rStr.clear();
+
+ SfxObjectShell* pSh = SfxObjectShell::Current();
+ DBG_ASSERT( pSh, "no DocShell" );
+
+ if ( !pSh )
+ return rStr;
+
+ SfxItemPool& rPool = pSh->GetPool();
+ SearchAttrItemList* pList = bSrchFlag ? pSearchList.get() : pReplaceList.get();
+
+ if ( !pList )
+ return rStr;
+
+ // Metric query
+ MapUnit eMapUnit = MapUnit::MapCM;
+ FieldUnit eFieldUnit = pSh->GetModule()->GetFieldUnit();
+ switch ( eFieldUnit )
+ {
+ case FieldUnit::MM: eMapUnit = MapUnit::MapMM; break;
+ case FieldUnit::CM:
+ case FieldUnit::M:
+ case FieldUnit::KM: eMapUnit = MapUnit::MapCM; break;
+ case FieldUnit::TWIP: eMapUnit = MapUnit::MapTwip; break;
+ case FieldUnit::POINT:
+ case FieldUnit::PICA: eMapUnit = MapUnit::MapPoint; break;
+ case FieldUnit::INCH:
+ case FieldUnit::FOOT:
+ case FieldUnit::MILE: eMapUnit = MapUnit::MapInch; break;
+ case FieldUnit::MM_100TH: eMapUnit = MapUnit::Map100thMM; break;
+ default: ;//prevent warning
+ }
+
+ IntlWrapper aIntlWrapper(SvtSysLocale().GetUILanguageTag());
+ for ( sal_uInt16 i = 0; i < pList->Count(); ++i )
+ {
+ const SearchAttrItem& rItem = pList->GetObject(i);
+
+ if ( !rStr.isEmpty() )
+ rStr += ", ";
+
+ if ( !IsInvalidItem( rItem.pItem ) )
+ {
+ OUString aStr;
+ rPool.GetPresentation(*rItem.pItem, eMapUnit, aStr, aIntlWrapper);
+ if (aStr.isEmpty())
+ {
+ if (rStr.endsWith(", "))
+ rStr = rStr.copy(0, rStr.lastIndexOf(","));
+ }
+ else
+ rStr += aStr;
+ }
+ else if ( rItem.nSlot == SID_ATTR_BRUSH_CHAR )
+ {
+ // Special treatment for text background
+ rStr += SvxResId( RID_SVXITEMS_BRUSH_CHAR );
+ }
+ else
+ {
+ sal_uInt32 nId = SvxAttrNameTable::FindIndex(rItem.nSlot);
+ if ( RESARRAY_INDEX_NOTFOUND != nId )
+ rStr += SvxAttrNameTable::GetString(nId);
+ }
+ }
+ return rStr;
+}
+
+
+void SvxSearchDialog::PaintAttrText_Impl()
+{
+ OUString aDesc;
+ BuildAttrText_Impl( aDesc, bSearch );
+
+ if ( !bFormat && !aDesc.isEmpty() )
+ bFormat = true;
+
+ bool bSetOptimalLayoutSize = false;
+
+ if ( bSearch )
+ {
+ m_xSearchAttrText->set_label( aDesc );
+ if (!aDesc.isEmpty() && !m_xSearchAttrText->get_visible())
+ {
+ m_xSearchAttrText->show();
+ bSetOptimalLayoutSize = true;
+ }
+
+ FocusHdl_Impl(*m_xSearchLB);
+ }
+ else
+ {
+ m_xReplaceAttrText->set_label( aDesc );
+ if (!aDesc.isEmpty() && !m_xReplaceAttrText->get_visible())
+ {
+ m_xReplaceAttrText->show();
+ bSetOptimalLayoutSize = true;
+ }
+
+ FocusHdl_Impl(*m_xReplaceLB);
+ }
+
+ if (bSetOptimalLayoutSize)
+ m_xDialog->resize_to_request();
+}
+
+void SvxSearchDialog::SetModifyFlag_Impl( const weld::Widget* pCtrl )
+{
+ if (m_xSearchLB.get() == pCtrl)
+ {
+ nModifyFlag |= ModifyFlags::Search;
+ m_xSearchLB->set_entry_message_type(weld::EntryMessageType::Normal);
+ SvxSearchDialogWrapper::SetSearchLabel("");
+ }
+ else if ( m_xReplaceLB.get() == pCtrl )
+ nModifyFlag |= ModifyFlags::Replace;
+ else if ( m_xWordBtn.get() == pCtrl )
+ nModifyFlag |= ModifyFlags::Word;
+ else if ( m_xMatchCaseCB.get() == pCtrl )
+ nModifyFlag |= ModifyFlags::Exact;
+ else if ( m_xReplaceBackwardsCB.get() == pCtrl )
+ nModifyFlag |= ModifyFlags::Backwards;
+ else if ( m_xNotesBtn.get() == pCtrl )
+ nModifyFlag |= ModifyFlags::Notes;
+ else if ( m_xSelectionBtn.get() == pCtrl )
+ nModifyFlag |= ModifyFlags::Selection;
+ else if ( m_xRegExpBtn.get() == pCtrl )
+ nModifyFlag |= ModifyFlags::Regexp;
+ else if ( m_xWildcardBtn.get() == pCtrl )
+ nModifyFlag |= ModifyFlags::Wildcard;
+ else if ( m_xLayoutBtn.get() == pCtrl )
+ nModifyFlag |= ModifyFlags::Layout;
+ else if ( m_xSimilarityBox.get() == pCtrl )
+ nModifyFlag |= ModifyFlags::Similarity;
+ else if ( m_xCalcSearchInLB.get() == pCtrl )
+ {
+ nModifyFlag |= ModifyFlags::Formulas;
+ nModifyFlag |= ModifyFlags::Values;
+ nModifyFlag |= ModifyFlags::CalcNotes;
+ }
+ else if ( m_xRowsBtn.get() == pCtrl )
+ nModifyFlag |= ModifyFlags::Rows;
+ else if ( m_xColumnsBtn.get() == pCtrl )
+ nModifyFlag |= ModifyFlags::Columns;
+ else if ( m_xAllSheetsCB.get() == pCtrl )
+ nModifyFlag |= ModifyFlags::AllTables;
+}
+
+void SvxSearchDialog::SaveToModule_Impl()
+{
+ if ( !pSearchItem )
+ return;
+
+ if ( m_xLayoutBtn->get_active() )
+ {
+ pSearchItem->SetSearchString ( m_xSearchTmplLB->get_active_text() );
+ pSearchItem->SetReplaceString( m_xReplaceTmplLB->get_active_text() );
+ }
+ else
+ {
+ pSearchItem->SetSearchString ( m_xSearchLB->get_active_text() );
+ pSearchItem->SetReplaceString( m_xReplaceLB->get_active_text() );
+ Remember_Impl( m_xSearchLB->get_active_text(), true );
+ }
+
+ pSearchItem->SetRegExp( false );
+ pSearchItem->SetWildcard( false );
+ pSearchItem->SetLevenshtein( false );
+ if (GetCheckBoxValue(*m_xRegExpBtn))
+ pSearchItem->SetRegExp( true );
+ else if (GetCheckBoxValue(*m_xWildcardBtn))
+ pSearchItem->SetWildcard( true );
+ else if (GetCheckBoxValue(*m_xSimilarityBox))
+ pSearchItem->SetLevenshtein( true );
+
+ pSearchItem->SetWordOnly(GetCheckBoxValue(*m_xWordBtn));
+ pSearchItem->SetBackward(GetCheckBoxValue(*m_xReplaceBackwardsCB));
+ pSearchItem->SetNotes(GetCheckBoxValue(*m_xNotesBtn));
+ pSearchItem->SetPattern(GetCheckBoxValue(*m_xLayoutBtn));
+ pSearchItem->SetSelection(GetCheckBoxValue(*m_xSelectionBtn));
+ pSearchItem->SetUseAsianOptions(GetCheckBoxValue(*m_xJapOptionsCB));
+
+ SvtSearchOptions aOpt;
+ aOpt.SetIgnoreDiacritics_CTL(GetNegatedCheckBoxValue(*m_xIncludeDiacritics));
+ aOpt.SetIgnoreKashida_CTL(GetNegatedCheckBoxValue(*m_xIncludeKashida));
+ aOpt.Commit();
+
+ TransliterationFlags nFlags = GetTransliterationFlags();
+ if( !pSearchItem->IsUseAsianOptions())
+ nFlags &= TransliterationFlags::IGNORE_CASE |
+ TransliterationFlags::IGNORE_WIDTH;
+ if (GetNegatedCheckBoxValue(*m_xIncludeDiacritics))
+ nFlags |= TransliterationFlags::IGNORE_DIACRITICS_CTL;
+ if (GetNegatedCheckBoxValue(*m_xIncludeKashida))
+ nFlags |= TransliterationFlags::IGNORE_KASHIDA_CTL;
+ pSearchItem->SetTransliterationFlags( nFlags );
+
+ if ( !bWriter )
+ {
+ if (m_xCalcSearchInLB->get_active() != -1)
+ pSearchItem->SetCellType( static_cast<SvxSearchCellType>(m_xCalcSearchInLB->get_active()) );
+
+ pSearchItem->SetRowDirection( m_xRowsBtn->get_active() );
+ pSearchItem->SetAllTables( m_xAllSheetsCB->get_active() );
+ pSearchItem->SetSearchFormatted( m_xSearchFormattedCB->get_active() );
+ }
+
+ pSearchItem->SetCommand( SvxSearchCmd::FIND );
+ nModifyFlag = ModifyFlags::NONE;
+ const SfxPoolItem* ppArgs[] = { pSearchItem.get(), nullptr };
+ rBindings.GetDispatcher()->Execute( SID_SEARCH_ITEM, SfxCallMode::SLOT, ppArgs );
+}
+
+short SvxSearchDialog::executeSubDialog(VclAbstractDialog * dialog) {
+ assert(!m_executingSubDialog);
+ comphelper::ScopeGuard g([this] { m_executingSubDialog = false; });
+ m_executingSubDialog = true;
+ return dialog->Execute();
+}
+
+SFX_IMPL_CHILDWINDOW_WITHID(SvxSearchDialogWrapper, SID_SEARCH_DLG);
+
+
+SvxSearchDialogWrapper::SvxSearchDialogWrapper( vcl::Window* _pParent, sal_uInt16 nId,
+ SfxBindings* pBindings,
+ SfxChildWinInfo const * pInfo )
+ : SfxChildWindow( _pParent, nId )
+ , dialog(std::make_shared<SvxSearchDialog>(_pParent->GetFrameWeld(), this, *pBindings))
+{
+ SetController(dialog);
+ dialog->Initialize( pInfo );
+
+ pBindings->Update( SID_SEARCH_ITEM );
+ pBindings->Update( SID_SEARCH_OPTIONS );
+ pBindings->Update( SID_SEARCH_SEARCHSET );
+ pBindings->Update( SID_SEARCH_REPLACESET );
+ dialog->bConstruct = false;
+}
+
+SvxSearchDialogWrapper::~SvxSearchDialogWrapper ()
+{
+}
+
+
+SfxChildWinInfo SvxSearchDialogWrapper::GetInfo() const
+{
+ SfxChildWinInfo aInfo = SfxChildWindow::GetInfo();
+ aInfo.bVisible = false;
+ return aInfo;
+}
+
+static void lcl_SetSearchLabelWindow(const OUString& rStr, SfxViewFrame& rViewFrame)
+{
+ bool bNotFound = rStr == SvxResId(RID_SVXSTR_SEARCH_NOT_FOUND);
+
+ css::uno::Reference< css::beans::XPropertySet > xPropSet(
+ rViewFrame.GetFrame().GetFrameInterface(), css::uno::UNO_QUERY_THROW);
+ css::uno::Reference< css::frame::XLayoutManager > xLayoutManager;
+ xPropSet->getPropertyValue("LayoutManager") >>= xLayoutManager;
+ css::uno::Reference< css::ui::XUIElement > xUIElement =
+ xLayoutManager->getElement("private:resource/toolbar/findbar");
+ if (!xUIElement.is())
+ return;
+ css::uno::Reference< css::awt::XWindow > xWindow(
+ xUIElement->getRealInterface(), css::uno::UNO_QUERY_THROW);
+ VclPtr< ToolBox > pToolBox = static_cast<ToolBox*>( VCLUnoHelper::GetWindow(xWindow) );
+ for (ToolBox::ImplToolItems::size_type i = 0; pToolBox && i < pToolBox->GetItemCount(); ++i)
+ {
+ ToolBoxItemId id = pToolBox->GetItemId(i);
+ if (pToolBox->GetItemCommand(id) == ".uno:SearchLabel")
+ {
+ LabelItemWindow* pSearchLabel = dynamic_cast<LabelItemWindow*>(pToolBox->GetItemWindow(id));
+ assert(pSearchLabel);
+ pSearchLabel->set_label(rStr);
+ if (rStr.isEmpty())
+ pSearchLabel->SetSizePixel(Size(16, pSearchLabel->GetSizePixel().Height()));
+ else
+ pSearchLabel->SetOptimalSize();
+ }
+
+ if (pToolBox->GetItemCommand(id) == ".uno:FindText")
+ {
+ FindTextFieldControl* pFindText = dynamic_cast<FindTextFieldControl*>(pToolBox->GetItemWindow(id));
+ assert(pFindText);
+ if (bNotFound)
+ pFindText->set_entry_message_type(weld::EntryMessageType::Error);
+ else
+ pFindText->set_entry_message_type(weld::EntryMessageType::Normal);
+ }
+ }
+ xLayoutManager->doLayout();
+ pToolBox->Resize();
+}
+
+OUString SvxSearchDialogWrapper::GetSearchLabel()
+{
+ SfxViewFrame* pViewFrame = SfxViewFrame::Current();
+ if (!pViewFrame)
+ return OUString();
+
+ css::uno::Reference< css::beans::XPropertySet > xPropSet(
+ pViewFrame->GetFrame().GetFrameInterface(), css::uno::UNO_QUERY_THROW);
+ css::uno::Reference< css::frame::XLayoutManager > xLayoutManager;
+ xPropSet->getPropertyValue("LayoutManager") >>= xLayoutManager;
+ if (!xLayoutManager.is())
+ return OUString();
+ css::uno::Reference< css::ui::XUIElement > xUIElement =
+ xLayoutManager->getElement("private:resource/toolbar/findbar");
+ if (!xUIElement.is())
+ return OUString();
+ css::uno::Reference< css::awt::XWindow > xWindow(
+ xUIElement->getRealInterface(), css::uno::UNO_QUERY_THROW);
+ VclPtr< ToolBox > pToolBox = static_cast<ToolBox*>( VCLUnoHelper::GetWindow(xWindow) );
+ for (ToolBox::ImplToolItems::size_type i = 0; pToolBox && i < pToolBox->GetItemCount(); ++i)
+ {
+ ToolBoxItemId id = pToolBox->GetItemId(i);
+ if (pToolBox->GetItemCommand(id) == ".uno:SearchLabel")
+ {
+ LabelItemWindow* pSearchLabel = dynamic_cast<LabelItemWindow*>(pToolBox->GetItemWindow(id));
+ return pSearchLabel ? pSearchLabel->get_label() : OUString();
+ }
+ }
+ return OUString();
+}
+
+void SvxSearchDialogWrapper::SetSearchLabel(const SearchLabel& rSL)
+{
+ SfxViewFrame* pViewFrame = SfxViewFrame::Current();
+ if (!pViewFrame)
+ return;
+
+ OUString sStr;
+ if (rSL == SearchLabel::End)
+ sStr = SvxResId(RID_SVXSTR_SEARCH_END);
+ else if (rSL == SearchLabel::Start)
+ sStr = SvxResId(RID_SVXSTR_SEARCH_START);
+ else if (rSL == SearchLabel::EndWrapped)
+ sStr = SvxResId(RID_SVXSTR_SEARCH_END_WRAPPED);
+ else if (rSL == SearchLabel::StartWrapped)
+ sStr = SvxResId(RID_SVXSTR_SEARCH_START_WRAPPED);
+ else if (rSL == SearchLabel::EndSheet)
+ sStr = SvxResId(RID_SVXSTR_SEARCH_END_SHEET);
+ else if (rSL == SearchLabel::NotFound)
+ sStr = SvxResId(RID_SVXSTR_SEARCH_NOT_FOUND);
+ else if (rSL == SearchLabel::NavElementNotFound)
+ sStr = SvxResId(RID_SVXSTR_SEARCH_NAV_ELEMENT_NOT_FOUND);
+ else if (rSL == SearchLabel::ReminderEndWrapped)
+ sStr = SvxResId(RID_SVXSTR_SEARCH_REMINDER_END_WRAPPED);
+ else if (rSL == SearchLabel::ReminderStartWrapped)
+ sStr = SvxResId(RID_SVXSTR_SEARCH_REMINDER_START_WRAPPED);
+
+ lcl_SetSearchLabelWindow(sStr, *pViewFrame);
+
+ if (SvxSearchDialogWrapper *pWrp = static_cast<SvxSearchDialogWrapper*>( pViewFrame->
+ GetChildWindow( SvxSearchDialogWrapper::GetChildWindowId() )))
+ pWrp->getDialog()->SetSearchLabel(sStr);
+}
+
+void SvxSearchDialogWrapper::SetSearchLabel(const OUString& sStr)
+{
+ SfxViewFrame* pViewFrame = SfxViewFrame::Current();
+ if (!pViewFrame)
+ return;
+
+ lcl_SetSearchLabelWindow(sStr, *pViewFrame);
+ if (SvxSearchDialogWrapper *pWrp = static_cast<SvxSearchDialogWrapper*>( pViewFrame->
+ GetChildWindow( SvxSearchDialogWrapper::GetChildWindowId() )))
+ pWrp->getDialog()->SetSearchLabel(sStr);
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/dialog/strarray.cxx b/svx/source/dialog/strarray.cxx
new file mode 100644
index 000000000..335642452
--- /dev/null
+++ b/svx/source/dialog/strarray.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 <svx/dialmgr.hxx>
+#include <svx/strarray.hxx>
+#include <tools/resary.hxx>
+#include <svx/svxitems.hrc>
+#include <fieldunit.hrc>
+#include <numberingtype.hrc>
+
+sal_uInt32 SvxFieldUnitTable::Count() { return SAL_N_ELEMENTS(RID_SVXSTR_FIELDUNIT_TABLE); }
+
+OUString SvxFieldUnitTable::GetString(sal_uInt32 nPos)
+{
+ if (RESARRAY_INDEX_NOTFOUND != nPos && nPos < Count())
+ return SvxResId(RID_SVXSTR_FIELDUNIT_TABLE[nPos].first);
+ return OUString();
+}
+
+FieldUnit SvxFieldUnitTable::GetValue(sal_uInt32 nPos)
+{
+ if (RESARRAY_INDEX_NOTFOUND != nPos && nPos < Count())
+ return RID_SVXSTR_FIELDUNIT_TABLE[nPos].second;
+ return FieldUnit::NONE;
+}
+
+OUString SvxAttrNameTable::GetString(sal_uInt32 nPos)
+{
+ if (RESARRAY_INDEX_NOTFOUND != nPos && nPos < Count())
+ return SvxResId(RID_ATTR_NAMES[nPos].first);
+ return OUString();
+}
+
+sal_uInt32 SvxAttrNameTable::Count() { return SAL_N_ELEMENTS(RID_ATTR_NAMES); }
+
+sal_uInt32 SvxAttrNameTable::FindIndex(int nValue)
+{
+ for (size_t i = 0; i < SAL_N_ELEMENTS(RID_ATTR_NAMES); ++i)
+ {
+ if (nValue == RID_ATTR_NAMES[i].second)
+ return i;
+ }
+ return RESARRAY_INDEX_NOTFOUND;
+}
+
+OUString SvxNumberingTypeTable::GetString(sal_uInt32 nPos)
+{
+ if (RESARRAY_INDEX_NOTFOUND != nPos && nPos < Count())
+ return SvxResId(RID_SVXSTRARY_NUMBERINGTYPE[nPos].first);
+ return OUString();
+}
+
+sal_uInt32 SvxNumberingTypeTable::Count() { return SAL_N_ELEMENTS(RID_SVXSTRARY_NUMBERINGTYPE); }
+
+int SvxNumberingTypeTable::GetValue(sal_uInt32 nPos)
+{
+ if (RESARRAY_INDEX_NOTFOUND != nPos && nPos < Count())
+ return RID_SVXSTRARY_NUMBERINGTYPE[nPos].second;
+ return 0;
+}
+
+sal_uInt32 SvxNumberingTypeTable::FindIndex(int nValue)
+{
+ for (size_t i = 0; i < SAL_N_ELEMENTS(RID_SVXSTRARY_NUMBERINGTYPE); ++i)
+ {
+ if (nValue == RID_SVXSTRARY_NUMBERINGTYPE[i].second)
+ return i;
+ }
+ return RESARRAY_INDEX_NOTFOUND;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/dialog/svxbmpnumvalueset.cxx b/svx/source/dialog/svxbmpnumvalueset.cxx
new file mode 100644
index 000000000..50b9efb3c
--- /dev/null
+++ b/svx/source/dialog/svxbmpnumvalueset.cxx
@@ -0,0 +1,533 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * 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 <svx/dialmgr.hxx>
+#include <svx/strings.hrc>
+#include <tools/diagnose_ex.h>
+#include <i18nlangtag/mslangid.hxx>
+#include <svtools/valueset.hxx>
+#include <editeng/numitem.hxx>
+#include <svx/gallery.hxx>
+#include <vcl/event.hxx>
+#include <vcl/graph.hxx>
+#include <vcl/virdev.hxx>
+#include <svx/numvset.hxx>
+#include <com/sun/star/style/NumberingType.hpp>
+#include <com/sun/star/container/XIndexAccess.hpp>
+#include <com/sun/star/text/XNumberingFormatter.hpp>
+#include <com/sun/star/beans/PropertyValue.hpp>
+
+#include <algorithm>
+
+#include <uiobject.hxx>
+
+using namespace com::sun::star::uno;
+using namespace com::sun::star::beans;
+using namespace com::sun::star::lang;
+using namespace com::sun::star::text;
+using namespace com::sun::star::container;
+using namespace com::sun::star::style;
+
+
+// The selection of bullets from the star symbol
+const sal_Unicode aBulletTypes[] =
+{
+ 0x2022,
+ 0x25cf,
+ 0xe00c,
+ 0xe00a,
+ 0x2794,
+ 0x27a2,
+ 0x2717,
+ 0x2714
+};
+
+static vcl::Font& lcl_GetDefaultBulletFont()
+{
+ static vcl::Font aDefBulletFont = []()
+ {
+ static vcl::Font tmp("OpenSymbol", "", Size(0, 14));
+ tmp.SetCharSet( RTL_TEXTENCODING_SYMBOL );
+ tmp.SetFamily( FAMILY_DONTKNOW );
+ tmp.SetPitch( PITCH_DONTKNOW );
+ tmp.SetWeight( WEIGHT_DONTKNOW );
+ tmp.SetTransparent( true );
+ return tmp;
+ }();
+ return aDefBulletFont;
+}
+
+static void lcl_PaintLevel(OutputDevice* pVDev, sal_Int16 nNumberingType,
+ const OUString& rBulletChar, const OUString& rText, const OUString& rFontName,
+ Point& rLeft, vcl::Font& rRuleFont, const vcl::Font& rTextFont)
+{
+
+ if(NumberingType::CHAR_SPECIAL == nNumberingType )
+ {
+ rRuleFont.SetStyleName(rFontName);
+ pVDev->SetFont(rRuleFont);
+ pVDev->DrawText(rLeft, rBulletChar);
+ rLeft.AdjustX(pVDev->GetTextWidth(rBulletChar) );
+ }
+ else
+ {
+ pVDev->SetFont(rTextFont);
+ pVDev->DrawText(rLeft, rText);
+ rLeft.AdjustX(pVDev->GetTextWidth(rText) );
+ }
+}
+
+ const TranslateId RID_SVXSTR_BULLET_DESCRIPTIONS[] =
+{
+ RID_SVXSTR_BULLET_DESCRIPTION_0,
+ RID_SVXSTR_BULLET_DESCRIPTION_1,
+ RID_SVXSTR_BULLET_DESCRIPTION_2,
+ RID_SVXSTR_BULLET_DESCRIPTION_3,
+ RID_SVXSTR_BULLET_DESCRIPTION_4,
+ RID_SVXSTR_BULLET_DESCRIPTION_5,
+ RID_SVXSTR_BULLET_DESCRIPTION_6,
+ RID_SVXSTR_BULLET_DESCRIPTION_7
+};
+
+const TranslateId RID_SVXSTR_SINGLENUM_DESCRIPTIONS[] =
+{
+ RID_SVXSTR_SINGLENUM_DESCRIPTION_0,
+ RID_SVXSTR_SINGLENUM_DESCRIPTION_1,
+ RID_SVXSTR_SINGLENUM_DESCRIPTION_2,
+ RID_SVXSTR_SINGLENUM_DESCRIPTION_3,
+ RID_SVXSTR_SINGLENUM_DESCRIPTION_4,
+ RID_SVXSTR_SINGLENUM_DESCRIPTION_5,
+ RID_SVXSTR_SINGLENUM_DESCRIPTION_6,
+ RID_SVXSTR_SINGLENUM_DESCRIPTION_7
+};
+
+const TranslateId RID_SVXSTR_OUTLINENUM_DESCRIPTIONS[] =
+{
+ RID_SVXSTR_OUTLINENUM_DESCRIPTION_0,
+ RID_SVXSTR_OUTLINENUM_DESCRIPTION_1,
+ RID_SVXSTR_OUTLINENUM_DESCRIPTION_2,
+ RID_SVXSTR_OUTLINENUM_DESCRIPTION_3,
+ RID_SVXSTR_OUTLINENUM_DESCRIPTION_4,
+ RID_SVXSTR_OUTLINENUM_DESCRIPTION_5,
+ RID_SVXSTR_OUTLINENUM_DESCRIPTION_6,
+ RID_SVXSTR_OUTLINENUM_DESCRIPTION_7
+};
+
+void SvxNumValueSet::UserDraw( const UserDrawEvent& rUDEvt )
+{
+ static const sal_uInt16 aLinesArr[] =
+ {
+ 15, 10,
+ 20, 30,
+ 25, 50,
+ 30, 70,
+ 35, 90, // up to here line positions
+ 5, 10, // character positions
+ 10, 30,
+ 15, 50,
+ 20, 70,
+ 25, 90,
+ };
+
+ const Color aBackColor(COL_WHITE);
+ const Color aTextColor(COL_BLACK);
+
+ vcl::RenderContext* pDev = rUDEvt.GetRenderContext();
+ tools::Rectangle aRect = rUDEvt.GetRect();
+ sal_uInt16 nItemId = rUDEvt.GetItemId();
+
+ tools::Long nRectWidth = aRect.GetWidth();
+ tools::Long nRectHeight = aRect.GetHeight();
+ Size aRectSize(nRectWidth, aRect.GetHeight());
+ Point aBLPos = aRect.TopLeft();
+ vcl::Font aOldFont = pDev->GetFont();
+ Color aOldColor = pDev->GetLineColor();
+ pDev->SetLineColor(aTextColor);
+ vcl::Font aFont(OutputDevice::GetDefaultFont(
+ DefaultFontType::UI_SANS, MsLangId::getConfiguredSystemLanguage(), GetDefaultFontFlags::OnlyOne));
+
+ Size aSize = aFont.GetFontSize();
+
+ vcl::Font aRuleFont( lcl_GetDefaultBulletFont() );
+ aSize.setHeight( nRectHeight/6 );
+ aRuleFont.SetFontSize(aSize);
+ aRuleFont.SetColor(aTextColor);
+ aRuleFont.SetFillColor(aBackColor);
+ if(ePageType == NumberingPageType::BULLET)
+ aFont = aRuleFont;
+ else if(ePageType == NumberingPageType::OUTLINE)
+ {
+ aSize.setHeight( nRectHeight/8 );
+ }
+ aFont.SetColor(aTextColor);
+ aFont.SetFillColor(aBackColor);
+ aFont.SetFontSize( aSize );
+ pDev->SetFont(aFont);
+
+ if(!pVDev)
+ {
+ // The lines are only one time in the virtual device, only the outline
+ // page is currently done
+ pVDev = VclPtr<VirtualDevice>::Create(*pDev);
+ pVDev->SetMapMode(pDev->GetMapMode());
+ pVDev->EnableRTL( IsRTLEnabled() );
+ pVDev->SetOutputSize( aRectSize );
+ aOrgRect = aRect;
+ pVDev->SetFillColor( aBackColor );
+ pVDev->SetLineColor(COL_LIGHTGRAY);
+ // Draw line only once
+ if(ePageType != NumberingPageType::OUTLINE)
+ {
+ Point aStart(aBLPos.X() + nRectWidth *25 / 100,0);
+ Point aEnd(aBLPos.X() + nRectWidth * 9 / 10,0);
+ for( sal_uInt16 i = 11; i < 100; i += 33)
+ {
+ aStart.setY( aBLPos.Y() + nRectHeight * i / 100 );
+ aEnd.setY( aStart.Y() );
+ pVDev->DrawLine(aStart, aEnd);
+ aStart.setY( aBLPos.Y() + nRectHeight * (i + 11) / 100 );
+ aEnd.setY( aStart.Y() );
+ pVDev->DrawLine(aStart, aEnd);
+ }
+ }
+ }
+ pDev->DrawOutDev( aRect.TopLeft(), aRectSize,
+ aOrgRect.TopLeft(), aRectSize,
+ *pVDev );
+ // Now comes the text
+ static const OUStringLiteral sValue(u"Value");
+ if( NumberingPageType::SINGLENUM == ePageType ||
+ NumberingPageType::BULLET == ePageType )
+ {
+ Point aStart(aBLPos.X() + nRectWidth / 9,0);
+ for( sal_uInt16 i = 0; i < 3; i++ )
+ {
+ sal_uInt16 nY = 11 + i * 33;
+ aStart.setY( aBLPos.Y() + nRectHeight * nY / 100 );
+ OUString sText;
+ if(ePageType == NumberingPageType::BULLET)
+ {
+ sText = OUString( aBulletTypes[nItemId - 1] );
+ aStart.AdjustY( -(pDev->GetTextHeight()/2) );
+ aStart.setX( aBLPos.X() + 5 );
+ }
+ else
+ {
+ if(xFormatter.is() && aNumSettings.getLength() > nItemId - 1)
+ {
+ Sequence<PropertyValue> aLevel = aNumSettings.getConstArray()[nItemId - 1];
+ try
+ {
+ aLevel.realloc(aLevel.getLength() + 1);
+ PropertyValue& rValue = aLevel.getArray()[aLevel.getLength() - 1];
+ rValue.Name = sValue;
+ rValue.Value <<= static_cast<sal_Int32>(i + 1);
+ sText = xFormatter->makeNumberingString( aLevel, aLocale );
+ }
+ catch(Exception&)
+ {
+ TOOLS_WARN_EXCEPTION("svx.dialog", "");
+ }
+ }
+ // start just next to the left edge
+ aStart.setX( aBLPos.X() + 2 );
+ aStart.AdjustY( -(pDev->GetTextHeight()/2) );
+ }
+ pDev->DrawText(aStart, sText);
+ }
+ }
+ else if(NumberingPageType::OUTLINE == ePageType )
+ {
+ // Outline numbering has to be painted into the virtual device
+ // to get correct lines
+ // has to be made again
+ pVDev->SetLineColor(aBackColor);
+ pVDev->DrawRect(aOrgRect);
+ tools::Long nStartX = aOrgRect.Left();
+ tools::Long nStartY = aOrgRect.Top();
+
+ if(xFormatter.is() && aOutlineSettings.getLength() > nItemId - 1)
+ {
+ Reference<XIndexAccess> xLevel = aOutlineSettings.getArray()[nItemId - 1];
+ try
+ {
+ OUString sLevelTexts[5];
+ OUString sFontNames[5];
+ OUString sBulletChars[5];
+ sal_Int16 aNumberingTypes[5];
+ OUString sPrefixes[5];
+ OUString sSuffixes[5];
+ sal_Int16 aParentNumberings[5];
+
+ sal_Int32 nLevelCount = xLevel->getCount();
+ if(nLevelCount > 5)
+ nLevelCount = 5;
+ for( sal_Int32 i = 0; i < nLevelCount; i++)
+ {
+ tools::Long nTop = nStartY + nRectHeight * (aLinesArr[2 * i + 11])/100 ;
+ Point aLeft(nStartX + nRectWidth * (aLinesArr[2 * i + 10])/ 100, nTop );
+
+ Any aLevelAny = xLevel->getByIndex(i);
+ Sequence<PropertyValue> aLevel;
+ aLevelAny >>= aLevel;
+ aNumberingTypes[i] = 0;
+ aParentNumberings[i] = 0;
+ for(const PropertyValue& rProp : std::as_const(aLevel))
+ {
+ if ( rProp.Name == "NumberingType" )
+ rProp.Value >>= aNumberingTypes[i];
+ else if ( rProp.Name == "BulletFontName" )
+ rProp.Value >>= sFontNames[i];
+ else if ( rProp.Name == "BulletChar" )
+ rProp.Value >>= sBulletChars[i];
+ else if ( rProp.Name == "Prefix" )
+ rProp.Value >>= sPrefixes[i];
+ else if ( rProp.Name == "Suffix" )
+ rProp.Value >>= sSuffixes[i];
+ else if ( rProp.Name == "ParentNumbering" )
+ rProp.Value >>= aParentNumberings[i];
+ }
+ Sequence< PropertyValue > aProperties(2);
+ PropertyValue* pProperties = aProperties.getArray();
+ pProperties[0].Name = "NumberingType";
+ pProperties[0].Value <<= aNumberingTypes[i];
+ pProperties[1].Name = "Value";
+ pProperties[1].Value <<= sal_Int32(1);
+ try
+ {
+ sLevelTexts[i] = xFormatter->makeNumberingString( aProperties, aLocale );
+ }
+ catch(Exception&)
+ {
+ TOOLS_WARN_EXCEPTION("svx.dialog", "");
+ }
+
+ aLeft.AdjustY( -(pDev->GetTextHeight()/2) );
+ if(!sPrefixes[i].isEmpty() &&
+ sPrefixes[i] != " ")
+ {
+ pVDev->SetFont(aFont);
+ pVDev->DrawText(aLeft, sPrefixes[i]);
+ aLeft.AdjustX(pDev->GetTextWidth(sPrefixes[i]) );
+ }
+ if(aParentNumberings[i])
+ {
+ //insert old numberings here
+ sal_Int32 nStartLevel = std::min(static_cast<sal_Int32>(aParentNumberings[i]), i);
+ for(sal_Int32 nParentLevel = i - nStartLevel; nParentLevel < i; nParentLevel++)
+ {
+ OUString sTmp = sLevelTexts[nParentLevel] + ".";
+ lcl_PaintLevel(pVDev,
+ aNumberingTypes[nParentLevel],
+ sBulletChars[nParentLevel],
+ sTmp,
+ sFontNames[nParentLevel],
+ aLeft,
+ aRuleFont,
+ aFont);
+ }
+ }
+ lcl_PaintLevel(pVDev,
+ aNumberingTypes[i],
+ sBulletChars[i],
+ sLevelTexts[i],
+ sFontNames[i],
+ aLeft,
+ aRuleFont,
+ aFont);
+ if(!sSuffixes[i].isEmpty() &&
+ !sSuffixes[i].startsWith(" "))
+ {
+ pVDev->SetFont(aFont);
+ pVDev->DrawText(aLeft, sSuffixes[i]);
+ aLeft.AdjustX(pDev->GetTextWidth(sSuffixes[i]) );
+ }
+
+ tools::Long nLineTop = nStartY + nRectHeight * aLinesArr[2 * i + 1]/100 ;
+ Point aLineLeft(aLeft.X(), nLineTop );
+ Point aLineRight(nStartX + nRectWidth * 90 /100, nLineTop );
+ pVDev->SetLineColor(COL_LIGHTGRAY);
+ pVDev->DrawLine(aLineLeft, aLineRight);
+ }
+
+ }
+#ifdef DBG_UTIL
+ catch(Exception&)
+ {
+ static bool bAssert = false;
+ if(!bAssert)
+ {
+ TOOLS_WARN_EXCEPTION("svx.dialog", "");
+ bAssert = true;
+ }
+ }
+#else
+ catch(Exception&)
+ {
+ }
+#endif
+ }
+ pDev->DrawOutDev( aRect.TopLeft(), aRectSize,
+ aOrgRect.TopLeft(), aRectSize,
+ *pVDev );
+ }
+
+ pDev->SetFont(aOldFont);
+ pDev->SetLineColor(aOldColor);
+}
+
+SvxNumValueSet::SvxNumValueSet(std::unique_ptr<weld::ScrolledWindow> pScrolledWindow)
+ : ValueSet(std::move(pScrolledWindow))
+ , ePageType(NumberingPageType::BULLET)
+ , pVDev(nullptr)
+{
+}
+
+FactoryFunction SvxNumValueSet::GetUITestFactory() const
+{
+ return SvxNumValueSetUIObject::create;
+}
+
+void SvxNumValueSet::init(NumberingPageType eType)
+{
+ ePageType = eType;
+ pVDev = nullptr;
+
+ SetColCount( 4 );
+ SetLineCount( 2 );
+ SetStyle( GetStyle() | WB_ITEMBORDER | WB_DOUBLEBORDER );
+ if(NumberingPageType::BULLET == eType)
+ {
+ for ( sal_uInt16 i = 0; i < 8; i++ )
+ {
+ InsertItem( i + 1, i );
+ SetItemText(i + 1, SvxResId(RID_SVXSTR_BULLET_DESCRIPTIONS[i]));
+ }
+ }
+}
+
+SvxNumValueSet::~SvxNumValueSet()
+{
+}
+
+void SvxNumValueSet::SetNumberingSettings(
+ const Sequence<Sequence<PropertyValue> >& aNum,
+ Reference<XNumberingFormatter> const & xFormat,
+ const Locale& rLocale )
+{
+ aNumSettings = aNum;
+ xFormatter = xFormat;
+ aLocale = rLocale;
+ if(aNum.getLength() > 8)
+ SetStyle( GetStyle()|WB_VSCROLL);
+ for ( sal_Int32 i = 0; i < aNum.getLength(); i++ )
+ {
+ InsertItem( i + 1, i );
+ if( i < 8 )
+ SetItemText(i + 1, SvxResId(RID_SVXSTR_SINGLENUM_DESCRIPTIONS[i]));
+ }
+}
+
+void SvxNumValueSet::SetOutlineNumberingSettings(
+ Sequence<Reference<XIndexAccess> > const & rOutline,
+ Reference<XNumberingFormatter> const & xFormat,
+ const Locale& rLocale)
+{
+ aOutlineSettings = rOutline;
+ xFormatter = xFormat;
+ aLocale = rLocale;
+ if(aOutlineSettings.getLength() > 8)
+ SetStyle( GetStyle() | WB_VSCROLL );
+ for ( sal_Int32 i = 0; i < aOutlineSettings.getLength(); i++ )
+ {
+ InsertItem( i + 1, i );
+ if( i < 8 )
+ SetItemText(i + 1, SvxResId(RID_SVXSTR_OUTLINENUM_DESCRIPTIONS[i]));
+ }
+}
+
+SvxBmpNumValueSet::SvxBmpNumValueSet(std::unique_ptr<weld::ScrolledWindow> pScrolledWindow)
+ : SvxNumValueSet(std::move(pScrolledWindow))
+ , aFormatIdle("SvxBmpNumValueSet FormatIdle")
+ , bGrfNotFound(false)
+{
+}
+
+void SvxBmpNumValueSet::init()
+{
+ SvxNumValueSet::init(NumberingPageType::BITMAP);
+ bGrfNotFound = false;
+ GalleryExplorer::BeginLocking(GALLERY_THEME_BULLETS);
+ SetStyle( GetStyle() | WB_VSCROLL );
+ SetLineCount( 3 );
+ aFormatIdle.SetPriority(TaskPriority::LOWEST);
+ aFormatIdle.SetInvokeHandler(LINK(this, SvxBmpNumValueSet, FormatHdl_Impl));
+}
+
+
+SvxBmpNumValueSet::~SvxBmpNumValueSet()
+{
+ GalleryExplorer::EndLocking(GALLERY_THEME_BULLETS);
+ aFormatIdle.Stop();
+}
+
+void SvxBmpNumValueSet::UserDraw(const UserDrawEvent& rUDEvt)
+{
+ SvxNumValueSet::UserDraw(rUDEvt);
+
+ tools::Rectangle aRect = rUDEvt.GetRect();
+ vcl::RenderContext* pDev = rUDEvt.GetRenderContext();
+ sal_uInt16 nItemId = rUDEvt.GetItemId();
+ Point aBLPos = aRect.TopLeft();
+
+ tools::Long nRectHeight = aRect.GetHeight();
+ Size aSize(nRectHeight/8, nRectHeight/8);
+
+ Graphic aGraphic;
+ if(!GalleryExplorer::GetGraphicObj( GALLERY_THEME_BULLETS, nItemId - 1,
+ &aGraphic))
+ {
+ bGrfNotFound = true;
+ }
+ else
+ {
+ Point aPos(aBLPos.X() + 5, 0);
+ for( sal_uInt16 i = 0; i < 3; i++ )
+ {
+ sal_uInt16 nY = 11 + i * 33;
+ aPos.setY( aBLPos.Y() + nRectHeight * nY / 100 );
+ aGraphic.Draw(*pDev, aPos, aSize);
+ }
+ }
+}
+
+IMPL_LINK_NOARG(SvxBmpNumValueSet, FormatHdl_Impl, Timer *, void)
+{
+ // only when a graphics was not there, it needs to be formatted
+ if (bGrfNotFound)
+ {
+ SetFormat();
+ bGrfNotFound = false;
+ }
+ Invalidate();
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/dialog/svxdlg.cxx b/svx/source/dialog/svxdlg.cxx
new file mode 100644
index 000000000..c073fb241
--- /dev/null
+++ b/svx/source/dialog/svxdlg.cxx
@@ -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/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * 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/svxdlg.hxx>
+
+SvxAbstractDialogFactory* SvxAbstractDialogFactory::Create()
+{
+ return dynamic_cast<SvxAbstractDialogFactory*>(VclAbstractDialogFactory::Create());
+}
+
+SvxAbstractDialogFactory::~SvxAbstractDialogFactory() {}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/dialog/svxgraphicitem.cxx b/svx/source/dialog/svxgraphicitem.cxx
new file mode 100644
index 000000000..9550e79e4
--- /dev/null
+++ b/svx/source/dialog/svxgraphicitem.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 <svx/svxgraphicitem.hxx>
+#include <svx/svxids.hrc>
+
+SvxGraphicItem::SvxGraphicItem( const Graphic& rGraphic )
+ : SfxPoolItem( SID_GRAPHIC ), aGraphic( rGraphic )
+{
+
+}
+
+bool SvxGraphicItem::operator==( const SfxPoolItem& rItem) const
+{
+ return SfxPoolItem::operator==(rItem) && static_cast<const SvxGraphicItem&>(rItem).aGraphic == aGraphic;
+}
+
+SvxGraphicItem* SvxGraphicItem::Clone( SfxItemPool * ) const
+{
+ return new SvxGraphicItem( *this );
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/dialog/svxruler.cxx b/svx/source/dialog/svxruler.cxx
new file mode 100644
index 000000000..b3d5e293e
--- /dev/null
+++ b/svx/source/dialog/svxruler.cxx
@@ -0,0 +1,3577 @@
+/* -*- 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 <cstring>
+#include <climits>
+
+#include <vcl/commandevent.hxx>
+#include <vcl/event.hxx>
+#include <vcl/fieldvalues.hxx>
+#include <vcl/settings.hxx>
+#include <vcl/svapp.hxx>
+#include <vcl/virdev.hxx>
+#include <vcl/weldutils.hxx>
+#include <svl/eitem.hxx>
+#include <svl/rectitem.hxx>
+#include <svl/hint.hxx>
+#include <sfx2/dispatch.hxx>
+#include <svx/strings.hrc>
+#include <svx/svxids.hrc>
+#include <svx/dialmgr.hxx>
+#include <svx/ruler.hxx>
+#include <svx/rulritem.hxx>
+#include <editeng/editids.hrc>
+#include <editeng/tstpitem.hxx>
+#include <editeng/lrspitem.hxx>
+#include <editeng/protitem.hxx>
+#include <osl/diagnose.h>
+#include <rtl/math.hxx>
+#include <o3tl/string_view.hxx>
+
+#include "rlrcitem.hxx"
+#include <memory>
+
+#define CTRL_ITEM_COUNT 14
+#define GAP 10
+#define OBJECT_BORDER_COUNT 4
+#define TAB_GAP 1
+#define INDENT_GAP 2
+#define INDENT_FIRST_LINE 2
+#define INDENT_LEFT_MARGIN 3
+#define INDENT_RIGHT_MARGIN 4
+#define INDENT_COUNT 3 //without the first two old values
+
+struct SvxRuler_Impl {
+ std::unique_ptr<sal_uInt16[]> pPercBuf;
+ std::unique_ptr<sal_uInt16[]> pBlockBuf;
+ sal_uInt16 nPercSize;
+ tools::Long nTotalDist;
+ tools::Long lOldWinPos;
+ tools::Long lMaxLeftLogic;
+ tools::Long lMaxRightLogic;
+ tools::Long lLastLMargin;
+ tools::Long lLastRMargin;
+ std::unique_ptr<SvxProtectItem> aProtectItem;
+ std::unique_ptr<SfxBoolItem> pTextRTLItem;
+ sal_uInt16 nControllerItems;
+ sal_uInt16 nIdx;
+ sal_uInt16 nColLeftPix;
+ sal_uInt16 nColRightPix; // Pixel values for left / right edge
+ // For columns; buffered to prevent
+ // recalculation errors
+ // May be has to be widen for future values
+ bool bIsTableRows : 1; // mxColumnItem contains table rows instead of columns
+ //#i24363# tab stops relative to indent
+ bool bIsTabsRelativeToIndent : 1; // Tab stops relative to paragraph indent?
+ // false means relative to SvxRuler::GetLeftFrameMargin()
+
+ SvxRuler_Impl() :
+ nPercSize(0), nTotalDist(0),
+ lOldWinPos(0), lMaxLeftLogic(0), lMaxRightLogic(0),
+ lLastLMargin(0), lLastRMargin(0),
+ aProtectItem(std::make_unique<SvxProtectItem>(SID_RULER_PROTECT)),
+ nControllerItems(0), nIdx(0),
+ nColLeftPix(0), nColRightPix(0),
+ bIsTableRows(false),
+ bIsTabsRelativeToIndent(true)
+ {
+ }
+
+ void SetPercSize(sal_uInt16 nSize);
+
+};
+
+static RulerTabData ruler_tab_svx =
+{
+ 0, // DPIScaleFactor to be set
+ 7, // ruler_tab_width
+ 6, // ruler_tab_height
+ 0, // ruler_tab_height2
+ 0, // ruler_tab_width2
+ 0, // ruler_tab_cwidth
+ 0, // ruler_tab_cwidth2
+ 0, // ruler_tab_cwidth3
+ 0, // ruler_tab_cwidth4
+ 0, // ruler_tab_dheight
+ 0, // ruler_tab_dheight2
+ 0, // ruler_tab_dwidth
+ 0, // ruler_tab_dwidth2
+ 0, // ruler_tab_dwidth3
+ 0, // ruler_tab_dwidth4
+ 0 // ruler_tab_textoff
+};
+
+void SvxRuler_Impl::SetPercSize(sal_uInt16 nSize)
+{
+ if(nSize > nPercSize)
+ {
+ nPercSize = nSize;
+ pPercBuf.reset( new sal_uInt16[nPercSize] );
+ pBlockBuf.reset( new sal_uInt16[nPercSize] );
+ }
+ size_t nSize2 = sizeof(sal_uInt16) * nPercSize;
+ memset(pPercBuf.get(), 0, nSize2);
+ memset(pBlockBuf.get(), 0, nSize2);
+}
+
+// Constructor of the ruler
+
+// SID_ATTR_ULSPACE, SID_ATTR_LRSPACE
+// expects as parameter SvxULSpaceItem for page edge
+// (either left/right or top/bottom)
+// Ruler: SetMargin1, SetMargin2
+
+// SID_RULER_PAGE_POS
+// expects as parameter the initial value of the page and page width
+// Ruler: SetPagePos
+
+// SID_ATTR_TABSTOP
+// expects: SvxTabStopItem
+// Ruler: SetTabs
+
+// SID_ATTR_PARA_LRSPACE
+// left, right paragraph edge in H-ruler
+// Ruler: SetIndents
+
+// SID_RULER_BORDERS
+// Table borders, columns
+// expects: something like SwTabCols
+// Ruler: SetBorders
+
+constexpr tools::Long glMinFrame = 5; // minimal frame width in pixels
+
+SvxRuler::SvxRuler(
+ vcl::Window* pParent, // StarView Parent
+ vcl::Window* pWin, // Output window: is used for conversion
+ // logical units <-> pixels
+ SvxRulerSupportFlags flags, // Display flags, see ruler.hxx
+ SfxBindings &rBindings, // associated Bindings
+ WinBits nWinStyle) : // StarView WinBits
+ Ruler(pParent, nWinStyle),
+ pCtrlItems(CTRL_ITEM_COUNT),
+ pEditWin(pWin),
+ mxRulerImpl(new SvxRuler_Impl),
+ bAppSetNullOffset(false), // Is the 0-offset of the ruler set by the application?
+ lLogicNullOffset(0),
+ lAppNullOffset(LONG_MAX),
+ lInitialDragPos(0),
+ nFlags(flags),
+ nDragType(SvxRulerDragFlags::NONE),
+ nDefTabType(RULER_TAB_LEFT),
+ nTabCount(0),
+ nTabBufSize(0),
+ lDefTabDist(50),
+ lTabPos(-1),
+ mpBorders(1), // due to one column tables
+ pBindings(&rBindings),
+ nDragOffset(0),
+ nMaxLeft(0),
+ nMaxRight(0),
+ bValid(false),
+ bListening(false),
+ bActive(true),
+ mbCoarseSnapping(false),
+ mbSnapping(true)
+
+{
+ /* Constructor; Initialize data buffer; controller items are created */
+
+ rBindings.EnterRegistrations();
+
+ // Create Supported Items
+ sal_uInt16 i = 0;
+
+ // Page edges
+ pCtrlItems[i++].reset(new SvxRulerItem(SID_RULER_LR_MIN_MAX, *this, rBindings));
+ if((nWinStyle & WB_VSCROLL) == WB_VSCROLL)
+ {
+ bHorz = false;
+ pCtrlItems[i++].reset(new SvxRulerItem(SID_ATTR_LONG_ULSPACE, *this, rBindings));
+ }
+ else
+ {
+ bHorz = true;
+ pCtrlItems[i++].reset(new SvxRulerItem(SID_ATTR_LONG_LRSPACE, *this, rBindings));
+ }
+
+ // Page Position
+ pCtrlItems[i++].reset(new SvxRulerItem(SID_RULER_PAGE_POS, *this, rBindings));
+
+ if(nFlags & SvxRulerSupportFlags::TABS)
+ {
+ sal_uInt16 nTabStopId = bHorz ? SID_ATTR_TABSTOP : SID_ATTR_TABSTOP_VERTICAL;
+ pCtrlItems[i++].reset(new SvxRulerItem(nTabStopId, *this, rBindings));
+ SetExtraType(RulerExtra::Tab, nDefTabType);
+ }
+
+ if(nFlags & (SvxRulerSupportFlags::PARAGRAPH_MARGINS |SvxRulerSupportFlags::PARAGRAPH_MARGINS_VERTICAL))
+ {
+ if(bHorz)
+ pCtrlItems[i++].reset(new SvxRulerItem(SID_ATTR_PARA_LRSPACE, *this, rBindings));
+ else
+ pCtrlItems[i++].reset(new SvxRulerItem(SID_ATTR_PARA_LRSPACE_VERTICAL, *this, rBindings));
+
+ mpIndents.resize(5 + INDENT_GAP);
+
+ for(RulerIndent & rIndent : mpIndents)
+ {
+ rIndent.nPos = 0;
+ rIndent.nStyle = RulerIndentStyle::Top;
+ }
+
+ mpIndents[0].nStyle = RulerIndentStyle::Top;
+ mpIndents[1].nStyle = RulerIndentStyle::Top;
+ mpIndents[INDENT_FIRST_LINE].nStyle = RulerIndentStyle::Top;
+ mpIndents[INDENT_LEFT_MARGIN].nStyle = RulerIndentStyle::Bottom;
+ mpIndents[INDENT_RIGHT_MARGIN].nStyle = RulerIndentStyle::Bottom;
+ }
+
+ if( (nFlags & SvxRulerSupportFlags::BORDERS) == SvxRulerSupportFlags::BORDERS )
+ {
+ pCtrlItems[i++].reset(new SvxRulerItem(bHorz ? SID_RULER_BORDERS : SID_RULER_BORDERS_VERTICAL, *this, rBindings));
+ pCtrlItems[i++].reset(new SvxRulerItem(bHorz ? SID_RULER_ROWS : SID_RULER_ROWS_VERTICAL, *this, rBindings));
+ }
+
+ pCtrlItems[i++].reset(new SvxRulerItem(SID_RULER_TEXT_RIGHT_TO_LEFT, *this, rBindings));
+
+ if( (nFlags & SvxRulerSupportFlags::OBJECT) == SvxRulerSupportFlags::OBJECT )
+ {
+ pCtrlItems[i++].reset(new SvxRulerItem(SID_RULER_OBJECT, *this, rBindings));
+ mpObjectBorders.resize(OBJECT_BORDER_COUNT);
+ for(sal_uInt16 nBorder = 0; nBorder < OBJECT_BORDER_COUNT; ++nBorder)
+ {
+ mpObjectBorders[nBorder].nPos = 0;
+ mpObjectBorders[nBorder].nWidth = 0;
+ mpObjectBorders[nBorder].nStyle = RulerBorderStyle::Moveable;
+ }
+ }
+
+ pCtrlItems[i++].reset(new SvxRulerItem(SID_RULER_PROTECT, *this, rBindings));
+ pCtrlItems[i++].reset(new SvxRulerItem(SID_RULER_BORDER_DISTANCE, *this, rBindings));
+ mxRulerImpl->nControllerItems=i;
+
+ if( (nFlags & SvxRulerSupportFlags::SET_NULLOFFSET) == SvxRulerSupportFlags::SET_NULLOFFSET )
+ SetExtraType(RulerExtra::NullOffset);
+
+ rBindings.LeaveRegistrations();
+
+ ruler_tab_svx.DPIScaleFactor = pParent->GetDPIScaleFactor();
+ ruler_tab_svx.height *= ruler_tab_svx.DPIScaleFactor;
+ ruler_tab_svx.width *= ruler_tab_svx.DPIScaleFactor;
+
+}
+
+SvxRuler::~SvxRuler()
+{
+ disposeOnce();
+}
+
+void SvxRuler::dispose()
+{
+ /* Destructor ruler; release internal buffer */
+ if(bListening)
+ EndListening(*pBindings);
+
+ pBindings->EnterRegistrations();
+
+ pCtrlItems.clear();
+
+ pBindings->LeaveRegistrations();
+
+ pEditWin.clear();
+ Ruler::dispose();
+}
+
+tools::Long SvxRuler::MakePositionSticky(tools::Long aPosition, tools::Long aPointOfReference, bool aSnapToFrameMargin) const
+{
+ tools::Long aPointOfReferencePixel = ConvertHPosPixel(aPointOfReference);
+ tools::Long aLeftFramePosition = ConvertHPosPixel(GetLeftFrameMargin());
+ tools::Long aRightFramePosition = ConvertHPosPixel(GetRightFrameMargin());
+
+ double aTick = GetCurrentRulerUnit().nTick1;
+
+ if (mbCoarseSnapping)
+ aTick = GetCurrentRulerUnit().nTick2;
+
+ tools::Long aTickPixel = pEditWin->LogicToPixel(Size(aTick, 0), GetCurrentMapMode()).Width();
+
+ double aHalfTick = aTick / 2.0;
+ double aHalfTickPixel = aTickPixel / 2.0;
+
+ if (aSnapToFrameMargin)
+ {
+ if (aPosition > aLeftFramePosition - aHalfTickPixel && aPosition < aLeftFramePosition + aHalfTickPixel)
+ return aLeftFramePosition;
+
+ if (aPosition > aRightFramePosition - aHalfTickPixel && aPosition < aRightFramePosition + aHalfTickPixel)
+ return aRightFramePosition;
+ }
+
+ if (!mbSnapping)
+ return aPosition;
+
+ // Move "coordinate system" to frame position so ticks are calculated correctly
+ tools::Long aTranslatedPosition = aPosition - aPointOfReferencePixel;
+ // Convert position to current selected map mode
+ tools::Long aPositionLogic = pEditWin->PixelToLogic(Size(aTranslatedPosition, 0), GetCurrentMapMode()).Width();
+ // Normalize -- snap to nearest tick
+ aPositionLogic = rtl::math::round((aPositionLogic + aHalfTick) / aTick) * aTick;
+ // Convert back to pixels
+ aPosition = pEditWin->LogicToPixel(Size(aPositionLogic, 0), GetCurrentMapMode()).Width();
+ // Move "coordinate system" back to original position
+ return aPosition + aPointOfReferencePixel;
+}
+
+tools::Long SvxRuler::ConvertHPosPixel(tools::Long nVal) const
+{
+ return pEditWin->LogicToPixel(Size(nVal, 0)).Width();
+}
+
+tools::Long SvxRuler::ConvertVPosPixel(tools::Long nVal) const
+{
+ return pEditWin->LogicToPixel(Size(0, nVal)).Height();
+}
+
+tools::Long SvxRuler::ConvertHSizePixel(tools::Long nVal) const
+{
+ return pEditWin->LogicToPixel(Size(nVal, 0)).Width();
+}
+
+tools::Long SvxRuler::ConvertVSizePixel(tools::Long nVal) const
+{
+ return pEditWin->LogicToPixel(Size(0, nVal)).Height();
+}
+
+tools::Long SvxRuler::ConvertPosPixel(tools::Long nVal) const
+{
+ return bHorz ? ConvertHPosPixel(nVal): ConvertVPosPixel(nVal);
+}
+
+tools::Long SvxRuler::ConvertSizePixel(tools::Long nVal) const
+{
+ return bHorz? ConvertHSizePixel(nVal): ConvertVSizePixel(nVal);
+}
+
+inline tools::Long SvxRuler::ConvertHPosLogic(tools::Long nVal) const
+{
+ return pEditWin->PixelToLogic(Size(nVal, 0)).Width();
+}
+
+inline tools::Long SvxRuler::ConvertVPosLogic(tools::Long nVal) const
+{
+ return pEditWin->PixelToLogic(Size(0, nVal)).Height();
+}
+
+inline tools::Long SvxRuler::ConvertHSizeLogic(tools::Long nVal) const
+{
+ return pEditWin->PixelToLogic(Size(nVal, 0)).Width();
+}
+
+inline tools::Long SvxRuler::ConvertVSizeLogic(tools::Long nVal) const
+{
+ return pEditWin->PixelToLogic(Size(0, nVal)).Height();
+}
+
+inline tools::Long SvxRuler::ConvertPosLogic(tools::Long nVal) const
+{
+ return bHorz? ConvertHPosLogic(nVal): ConvertVPosLogic(nVal);
+}
+
+inline tools::Long SvxRuler::ConvertSizeLogic(tools::Long nVal) const
+{
+ return bHorz? ConvertHSizeLogic(nVal): ConvertVSizeLogic(nVal);
+}
+
+tools::Long SvxRuler::PixelHAdjust(tools::Long nVal, tools::Long nValOld) const
+{
+ if(ConvertHSizePixel(nVal) != ConvertHSizePixel(nValOld))
+ return nVal;
+ else
+ return nValOld;
+}
+
+tools::Long SvxRuler::PixelVAdjust(tools::Long nVal, tools::Long nValOld) const
+{
+ if(ConvertVSizePixel(nVal) != ConvertVSizePixel(nValOld))
+ return nVal;
+ else
+ return nValOld;
+}
+
+tools::Long SvxRuler::PixelAdjust(tools::Long nVal, tools::Long nValOld) const
+{
+ if(ConvertSizePixel(nVal) != ConvertSizePixel(nValOld))
+ return nVal;
+ else
+ return nValOld;
+}
+
+inline sal_uInt16 SvxRuler::GetObjectBordersOff(sal_uInt16 nIdx) const
+{
+ return bHorz ? nIdx : nIdx + 2;
+}
+
+/*
+ Update Upper Left edge.
+ Items are translated into the representation of the ruler.
+*/
+void SvxRuler::UpdateFrame()
+{
+ const RulerMarginStyle nMarginStyle =
+ ( mxRulerImpl->aProtectItem->IsSizeProtected() ||
+ mxRulerImpl->aProtectItem->IsPosProtected() ) ?
+ RulerMarginStyle::NONE : RulerMarginStyle::Sizeable;
+
+ if(mxLRSpaceItem && mxPagePosItem)
+ {
+ // if no initialization by default app behavior
+ const tools::Long nOld = lLogicNullOffset;
+ lLogicNullOffset = mxColumnItem ? mxColumnItem->GetLeft() : mxLRSpaceItem->GetLeft();
+
+ if(bAppSetNullOffset)
+ {
+ lAppNullOffset += lLogicNullOffset - nOld;
+ }
+
+ if(!bAppSetNullOffset || lAppNullOffset == LONG_MAX)
+ {
+ Ruler::SetNullOffset(ConvertHPosPixel(lLogicNullOffset));
+ SetMargin1(0, nMarginStyle);
+ lAppNullOffset = 0;
+ }
+ else
+ {
+ SetMargin1(ConvertHPosPixel(lAppNullOffset), nMarginStyle);
+ }
+
+ tools::Long lRight = 0;
+
+ // evaluate the table right edge of the table
+ if(mxColumnItem && mxColumnItem->IsTable())
+ lRight = mxColumnItem->GetRight();
+ else
+ lRight = mxLRSpaceItem->GetRight();
+
+ tools::Long aWidth = mxPagePosItem->GetWidth() - lRight - lLogicNullOffset + lAppNullOffset;
+ tools::Long aWidthPixel = ConvertHPosPixel(aWidth);
+
+ SetMargin2(aWidthPixel, nMarginStyle);
+ }
+ else if(mxULSpaceItem && mxPagePosItem)
+ {
+ // relative the upper edge of the surrounding frame
+ const tools::Long nOld = lLogicNullOffset;
+ lLogicNullOffset = mxColumnItem ? mxColumnItem->GetLeft() : mxULSpaceItem->GetUpper();
+
+ if(bAppSetNullOffset)
+ {
+ lAppNullOffset += lLogicNullOffset - nOld;
+ }
+
+ if(!bAppSetNullOffset || lAppNullOffset == LONG_MAX)
+ {
+ Ruler::SetNullOffset(ConvertVPosPixel(lLogicNullOffset));
+ lAppNullOffset = 0;
+ SetMargin1(0, nMarginStyle);
+ }
+ else
+ {
+ SetMargin1(ConvertVPosPixel(lAppNullOffset), nMarginStyle);
+ }
+
+ tools::Long lLower = mxColumnItem ? mxColumnItem->GetRight() : mxULSpaceItem->GetLower();
+ tools::Long nMargin2 = mxPagePosItem->GetHeight() - lLower - lLogicNullOffset + lAppNullOffset;
+ tools::Long nMargin2Pixel = ConvertVPosPixel(nMargin2);
+
+ SetMargin2(nMargin2Pixel, nMarginStyle);
+ }
+ else
+ {
+ // turns off the view
+ SetMargin1();
+ SetMargin2();
+ }
+
+ if (mxColumnItem)
+ {
+ mxRulerImpl->nColLeftPix = static_cast<sal_uInt16>(ConvertSizePixel(mxColumnItem->GetLeft()));
+ mxRulerImpl->nColRightPix = static_cast<sal_uInt16>(ConvertSizePixel(mxColumnItem->GetRight()));
+ }
+}
+
+void SvxRuler::MouseMove( const MouseEvent& rMEvt )
+{
+ if( bActive )
+ {
+ pBindings->Update( SID_RULER_LR_MIN_MAX );
+ pBindings->Update( SID_ATTR_LONG_ULSPACE );
+ pBindings->Update( SID_ATTR_LONG_LRSPACE );
+ pBindings->Update( SID_RULER_PAGE_POS );
+ pBindings->Update( bHorz ? SID_ATTR_TABSTOP : SID_ATTR_TABSTOP_VERTICAL);
+ pBindings->Update( bHorz ? SID_ATTR_PARA_LRSPACE : SID_ATTR_PARA_LRSPACE_VERTICAL);
+ pBindings->Update( bHorz ? SID_RULER_BORDERS : SID_RULER_BORDERS_VERTICAL);
+ pBindings->Update( bHorz ? SID_RULER_ROWS : SID_RULER_ROWS_VERTICAL);
+ pBindings->Update( SID_RULER_OBJECT );
+ pBindings->Update( SID_RULER_PROTECT );
+ }
+
+ Ruler::MouseMove( rMEvt );
+
+ RulerSelection aSelection = GetHoverSelection();
+
+ if (aSelection.eType == RulerType::DontKnow)
+ {
+ SetQuickHelpText("");
+ return;
+ }
+
+ RulerUnitData aUnitData = GetCurrentRulerUnit();
+ double aRoundingFactor = aUnitData.nTickUnit / aUnitData.nTick1;
+ sal_Int32 aNoDecimalPlaces = 1 + std::ceil(std::log10(aRoundingFactor));
+ OUString sUnit = OUString::createFromAscii(aUnitData.aUnitStr);
+
+ switch (aSelection.eType)
+ {
+ case RulerType::Indent:
+ {
+ if (!mxParaItem)
+ break;
+
+ tools::Long nIndex = aSelection.nAryPos + INDENT_GAP;
+
+ tools::Long nIndentValue = 0.0;
+ if (nIndex == INDENT_LEFT_MARGIN)
+ nIndentValue = mxParaItem->GetTextLeft();
+ else if (nIndex == INDENT_FIRST_LINE)
+ nIndentValue = mxParaItem->GetTextFirstLineOffset();
+ else if (nIndex == INDENT_RIGHT_MARGIN)
+ nIndentValue = mxParaItem->GetRight();
+
+ double fValue = OutputDevice::LogicToLogic(Size(nIndentValue, 0), pEditWin->GetMapMode(), GetCurrentMapMode()).Width();
+ fValue = rtl::math::round(fValue / aUnitData.nTickUnit, aNoDecimalPlaces);
+
+ SetQuickHelpText(OUString::number(fValue) + " " + sUnit);
+ break;
+ }
+ case RulerType::Border:
+ {
+ if (mxColumnItem == nullptr)
+ break;
+
+ SvxColumnItem& aColumnItem = *mxColumnItem;
+
+ if (aSelection.nAryPos + 1 >= aColumnItem.Count())
+ break;
+
+ double fStart = OutputDevice::LogicToLogic(Size(aColumnItem[aSelection.nAryPos].nEnd, 0), pEditWin->GetMapMode(), GetCurrentMapMode()).Width();
+ fStart = rtl::math::round(fStart / aUnitData.nTickUnit, aNoDecimalPlaces);
+ double fEnd = OutputDevice::LogicToLogic(Size(aColumnItem[aSelection.nAryPos + 1].nStart, 0), pEditWin->GetMapMode(), GetCurrentMapMode()).Width();
+ fEnd = rtl::math::round(fEnd / aUnitData.nTickUnit, aNoDecimalPlaces);
+
+ SetQuickHelpText(
+ OUString::number(fStart) + " " + sUnit + " - " +
+ OUString::number(fEnd) + " " + sUnit );
+ break;
+ }
+ case RulerType::Margin1:
+ {
+ tools::Long nLeft = 0.0;
+ if (mxLRSpaceItem)
+ nLeft = mxLRSpaceItem->GetLeft();
+ else if (mxULSpaceItem)
+ nLeft = mxULSpaceItem->GetUpper();
+ else
+ break;
+
+ double fValue = OutputDevice::LogicToLogic(Size(nLeft, 0), pEditWin->GetMapMode(), GetCurrentMapMode()).Width();
+ fValue = rtl::math::round(fValue / aUnitData.nTickUnit, aNoDecimalPlaces);
+ SetQuickHelpText(OUString::number(fValue) + " " + sUnit);
+
+ break;
+ }
+ case RulerType::Margin2:
+ {
+ tools::Long nRight = 0.0;
+ if (mxLRSpaceItem)
+ nRight = mxLRSpaceItem->GetRight();
+ else if (mxULSpaceItem)
+ nRight = mxULSpaceItem->GetLower();
+ else
+ break;
+
+ double fValue = OutputDevice::LogicToLogic(Size(nRight, 0), pEditWin->GetMapMode(), GetCurrentMapMode()).Width();
+ fValue = rtl::math::round(fValue / aUnitData.nTickUnit, aNoDecimalPlaces);
+ SetQuickHelpText(OUString::number(fValue) + " " + sUnit);
+
+ break;
+ }
+ default:
+ {
+ SetQuickHelpText("");
+ break;
+ }
+ }
+}
+
+void SvxRuler::StartListening_Impl()
+{
+ if(!bListening)
+ {
+ bValid = false;
+ StartListening(*pBindings);
+ bListening = true;
+ }
+}
+
+void SvxRuler::UpdateFrame(const SvxLongLRSpaceItem *pItem) // new value LRSpace
+{
+ /* Store new value LRSpace; delete old ones if possible */
+ if(bActive)
+ {
+ if(pItem)
+ mxLRSpaceItem.reset(new SvxLongLRSpaceItem(*pItem));
+ else
+ mxLRSpaceItem.reset();
+ StartListening_Impl();
+ }
+}
+
+void SvxRuler::UpdateFrameMinMax(const SfxRectangleItem *pItem) // value for MinMax
+{
+ /* Set new value for MinMax; delete old ones if possible */
+ if(bActive)
+ {
+ if(pItem)
+ mxMinMaxItem.reset(new SfxRectangleItem(*pItem));
+ else
+ mxMinMaxItem.reset();
+ }
+}
+
+
+void SvxRuler::UpdateFrame(const SvxLongULSpaceItem *pItem) // new value
+{
+ /* Update Right/bottom margin */
+ if(bActive && !bHorz)
+ {
+ if(pItem)
+ mxULSpaceItem.reset(new SvxLongULSpaceItem(*pItem));
+ else
+ mxULSpaceItem.reset();
+ StartListening_Impl();
+ }
+}
+
+void SvxRuler::Update( const SvxProtectItem* pItem )
+{
+ if( pItem )
+ mxRulerImpl->aProtectItem.reset(pItem->Clone());
+}
+
+void SvxRuler::UpdateTextRTL(const SfxBoolItem* pItem)
+{
+ if(bActive && bHorz)
+ {
+ mxRulerImpl->pTextRTLItem.reset();
+ if(pItem)
+ mxRulerImpl->pTextRTLItem.reset(new SfxBoolItem(*pItem));
+ SetTextRTL(mxRulerImpl->pTextRTLItem && mxRulerImpl->pTextRTLItem->GetValue());
+ StartListening_Impl();
+ }
+}
+
+void SvxRuler::Update(
+ const SvxColumnItem *pItem, // new value
+ sal_uInt16 nSID) //Slot Id to identify NULL items
+{
+ /* Set new value for column view */
+ if(!bActive)
+ return;
+
+ if(pItem)
+ {
+ mxColumnItem.reset(new SvxColumnItem(*pItem));
+ mxRulerImpl->bIsTableRows = (pItem->Which() == SID_RULER_ROWS || pItem->Which() == SID_RULER_ROWS_VERTICAL);
+ if(!bHorz && !mxRulerImpl->bIsTableRows)
+ mxColumnItem->SetWhich(SID_RULER_BORDERS_VERTICAL);
+ }
+ else if(mxColumnItem && mxColumnItem->Which() == nSID)
+ //there are two groups of column items table/frame columns and table rows
+ //both can occur in vertical or horizontal mode
+ //the horizontal ruler handles the SID_RULER_BORDERS and SID_RULER_ROWS_VERTICAL
+ //and the vertical handles SID_RULER_BORDERS_VERTICAL and SID_RULER_ROWS
+ //if mxColumnItem is already set with one of the ids then a NULL pItem argument
+ //must not delete it
+ {
+ mxColumnItem.reset();
+ mxRulerImpl->bIsTableRows = false;
+ }
+ StartListening_Impl();
+}
+
+
+void SvxRuler::UpdateColumns()
+{
+ /* Update column view */
+ if(mxColumnItem && mxColumnItem->Count() > 1)
+ {
+ mpBorders.resize(mxColumnItem->Count());
+
+ RulerBorderStyle nStyleFlags = RulerBorderStyle::Variable;
+
+ bool bProtectColumns =
+ mxRulerImpl->aProtectItem->IsSizeProtected() ||
+ mxRulerImpl->aProtectItem->IsPosProtected();
+
+ if( !bProtectColumns )
+ {
+ nStyleFlags |= RulerBorderStyle::Moveable;
+ if( !mxColumnItem->IsTable() )
+ nStyleFlags |= RulerBorderStyle::Sizeable;
+ }
+
+ sal_uInt16 nBorders = mxColumnItem->Count();
+
+ if(!mxRulerImpl->bIsTableRows)
+ --nBorders;
+
+ for(sal_uInt16 i = 0; i < nBorders; ++i)
+ {
+ mpBorders[i].nStyle = nStyleFlags;
+ if(!mxColumnItem->At(i).bVisible)
+ mpBorders[i].nStyle |= RulerBorderStyle::Invisible;
+
+ mpBorders[i].nPos = ConvertPosPixel(mxColumnItem->At(i).nEnd + lAppNullOffset);
+
+ if(mxColumnItem->Count() == i + 1)
+ {
+ //with table rows the end of the table is contained in the
+ //column item but it has no width!
+ mpBorders[i].nWidth = 0;
+ }
+ else
+ {
+ mpBorders[i].nWidth = ConvertSizePixel(mxColumnItem->At(i + 1).nStart - mxColumnItem->At(i).nEnd);
+ }
+ mpBorders[i].nMinPos = ConvertPosPixel(mxColumnItem->At(i).nEndMin + lAppNullOffset);
+ mpBorders[i].nMaxPos = ConvertPosPixel(mxColumnItem->At(i).nEndMax + lAppNullOffset);
+ }
+ SetBorders(mxColumnItem->Count() - 1, mpBorders.data());
+ }
+ else
+ {
+ SetBorders();
+ }
+}
+
+void SvxRuler::UpdateObject()
+{
+ /* Update view of object representation */
+ if (mxObjectItem)
+ {
+ DBG_ASSERT(!mpObjectBorders.empty(), "no Buffer");
+ // !! to the page margin
+ tools::Long nMargin = mxLRSpaceItem ? mxLRSpaceItem->GetLeft() : 0;
+ mpObjectBorders[0].nPos =
+ ConvertPosPixel(mxObjectItem->GetStartX() -
+ nMargin + lAppNullOffset);
+ mpObjectBorders[1].nPos =
+ ConvertPosPixel(mxObjectItem->GetEndX() - nMargin + lAppNullOffset);
+ nMargin = mxULSpaceItem ? mxULSpaceItem->GetUpper() : 0;
+ mpObjectBorders[2].nPos =
+ ConvertPosPixel(mxObjectItem->GetStartY() -
+ nMargin + lAppNullOffset);
+ mpObjectBorders[3].nPos =
+ ConvertPosPixel(mxObjectItem->GetEndY() - nMargin + lAppNullOffset);
+
+ const sal_uInt16 nOffset = GetObjectBordersOff(0);
+ SetBorders(2, mpObjectBorders.data() + nOffset);
+ }
+ else
+ {
+ SetBorders();
+ }
+}
+
+void SvxRuler::UpdatePara()
+{
+
+ /* Update the view for paragraph indents:
+ Left margin, first line indent, right margin paragraph update
+ mpIndents[0] = Buffer for old intent
+ mpIndents[1] = Buffer for old intent
+ mpIndents[INDENT_FIRST_LINE] = first line indent
+ mpIndents[INDENT_LEFT_MARGIN] = left margin
+ mpIndents[INDENT_RIGHT_MARGIN] = right margin
+ */
+
+ // Dependence on PagePosItem
+ if (mxParaItem && mxPagePosItem && !mxObjectItem)
+ {
+ bool bRTLText = mxRulerImpl->pTextRTLItem && mxRulerImpl->pTextRTLItem->GetValue();
+ // First-line indent is negative to the left paragraph margin
+ tools::Long nLeftFrameMargin = GetLeftFrameMargin();
+ tools::Long nRightFrameMargin = GetRightFrameMargin();
+ SetLeftFrameMargin(ConvertHPosPixel(nLeftFrameMargin));
+ SetRightFrameMargin(ConvertHPosPixel(nRightFrameMargin));
+
+ tools::Long leftMargin;
+ tools::Long leftFirstLine;
+ tools::Long rightMargin;
+
+ if(bRTLText)
+ {
+ leftMargin = nRightFrameMargin - mxParaItem->GetTextLeft() + lAppNullOffset;
+ leftFirstLine = leftMargin - mxParaItem->GetTextFirstLineOffset();
+ rightMargin = nLeftFrameMargin + mxParaItem->GetRight() + lAppNullOffset;
+ }
+ else
+ {
+ leftMargin = nLeftFrameMargin + mxParaItem->GetTextLeft() + lAppNullOffset;
+ leftFirstLine = leftMargin + mxParaItem->GetTextFirstLineOffset();
+ rightMargin = nRightFrameMargin - mxParaItem->GetRight() + lAppNullOffset;
+ }
+
+ mpIndents[INDENT_LEFT_MARGIN].nPos = ConvertHPosPixel(leftMargin);
+ mpIndents[INDENT_FIRST_LINE].nPos = ConvertHPosPixel(leftFirstLine);
+ mpIndents[INDENT_RIGHT_MARGIN].nPos = ConvertHPosPixel(rightMargin);
+
+ mpIndents[INDENT_FIRST_LINE].bInvisible = mxParaItem->IsAutoFirst();
+
+ SetIndents(INDENT_COUNT, mpIndents.data() + INDENT_GAP);
+ }
+ else
+ {
+ if(!mpIndents.empty())
+ {
+ mpIndents[INDENT_FIRST_LINE].nPos = 0;
+ mpIndents[INDENT_LEFT_MARGIN].nPos = 0;
+ mpIndents[INDENT_RIGHT_MARGIN].nPos = 0;
+ }
+ SetIndents(); // turn off
+ }
+}
+
+void SvxRuler::UpdatePara(const SvxLRSpaceItem *pItem) // new value of paragraph indents
+{
+ /* Store new value of paragraph indents */
+ if(bActive)
+ {
+ if(pItem)
+ mxParaItem.reset(new SvxLRSpaceItem(*pItem));
+ else
+ mxParaItem.reset();
+ StartListening_Impl();
+ }
+}
+
+void SvxRuler::UpdateBorder(const SvxLRSpaceItem * pItem)
+{
+ /* Border distance */
+ if(bActive)
+ {
+ if (pItem)
+ mxBorderItem.reset(new SvxLRSpaceItem(*pItem));
+ else
+ mxBorderItem.reset();
+
+ StartListening_Impl();
+ }
+}
+
+void SvxRuler::UpdatePage()
+{
+ /* Update view of position and width of page */
+ if (mxPagePosItem)
+ {
+ // all objects are automatically adjusted
+ if(bHorz)
+ {
+ SetPagePos(
+ pEditWin->LogicToPixel(mxPagePosItem->GetPos()).X(),
+ pEditWin->LogicToPixel(Size(mxPagePosItem->GetWidth(), 0)).
+ Width());
+ }
+ else
+ {
+ SetPagePos(
+ pEditWin->LogicToPixel(mxPagePosItem->GetPos()).Y(),
+ pEditWin->LogicToPixel(Size(0, mxPagePosItem->GetHeight())).
+ Height());
+ }
+ if(bAppSetNullOffset)
+ SetNullOffset(ConvertSizePixel(-lAppNullOffset + lLogicNullOffset));
+ }
+ else
+ {
+ SetPagePos();
+ }
+
+ tools::Long lPos = 0;
+ Point aOwnPos = GetPosPixel();
+ Point aEdtWinPos = pEditWin->GetPosPixel();
+ if( AllSettings::GetLayoutRTL() && bHorz )
+ {
+ //#i73321# in RTL the window and the ruler is not mirrored but the
+ // influence of the vertical ruler is inverted
+ Size aOwnSize = GetSizePixel();
+ Size aEdtWinSize = pEditWin->GetSizePixel();
+ lPos = aOwnSize.Width() - aEdtWinSize.Width();
+ lPos -= (aEdtWinPos - aOwnPos).X();
+ }
+ else
+ {
+ Point aPos(aEdtWinPos - aOwnPos);
+ lPos = bHorz ? aPos.X() : aPos.Y();
+ }
+
+ // Unfortunately, we get the offset of the edit window to the ruler never
+ // through a status message. So we set it ourselves if necessary.
+ if(lPos != mxRulerImpl->lOldWinPos)
+ {
+ mxRulerImpl->lOldWinPos=lPos;
+ SetWinPos(lPos);
+ }
+}
+
+void SvxRuler::Update(const SvxPagePosSizeItem *pItem) // new value of page attributes
+{
+ /* Store new value of page attributes */
+ if(bActive)
+ {
+ if(pItem)
+ mxPagePosItem.reset(new SvxPagePosSizeItem(*pItem));
+ else
+ mxPagePosItem.reset();
+ StartListening_Impl();
+ }
+}
+
+void SvxRuler::SetDefTabDist(tools::Long inDefTabDist) // New distance for DefaultTabs in App-Metrics
+{
+ if (lAppNullOffset == LONG_MAX)
+ UpdateFrame(); // hack: try to get lAppNullOffset initialized
+ /* New distance is set for DefaultTabs */
+ lDefTabDist = inDefTabDist;
+ if( !lDefTabDist )
+ lDefTabDist = 1;
+
+ UpdateTabs();
+}
+
+static sal_uInt16 ToSvTab_Impl(SvxTabAdjust eAdj)
+{
+ /* Internal conversion routine between SV-Tab.-Enum and Svx */
+ switch(eAdj) {
+ case SvxTabAdjust::Left: return RULER_TAB_LEFT;
+ case SvxTabAdjust::Right: return RULER_TAB_RIGHT;
+ case SvxTabAdjust::Decimal: return RULER_TAB_DECIMAL;
+ case SvxTabAdjust::Center: return RULER_TAB_CENTER;
+ case SvxTabAdjust::Default: return RULER_TAB_DEFAULT;
+ default: ; //prevent warning
+ }
+ return 0;
+}
+
+static SvxTabAdjust ToAttrTab_Impl(sal_uInt16 eAdj)
+{
+ switch(eAdj) {
+ case RULER_TAB_LEFT: return SvxTabAdjust::Left ;
+ case RULER_TAB_RIGHT: return SvxTabAdjust::Right ;
+ case RULER_TAB_DECIMAL: return SvxTabAdjust::Decimal ;
+ case RULER_TAB_CENTER: return SvxTabAdjust::Center ;
+ case RULER_TAB_DEFAULT: return SvxTabAdjust::Default ;
+ }
+ return SvxTabAdjust::Left;
+}
+
+void SvxRuler::UpdateTabs()
+{
+ if(IsDrag())
+ return;
+
+ if (mxPagePosItem && mxParaItem && mxTabStopItem && !mxObjectItem)
+ {
+ // buffer for DefaultTabStop
+ // Distance last Tab <-> Right paragraph margin / DefaultTabDist
+ bool bRTL = mxRulerImpl->pTextRTLItem && mxRulerImpl->pTextRTLItem->GetValue();
+
+ const tools::Long nLeftFrameMargin = GetLeftFrameMargin();
+ const tools::Long nRightFrameMargin = GetRightFrameMargin();
+
+ //#i24363# tab stops relative to indent
+ const tools::Long nParaItemTxtLeft = mxParaItem->GetTextLeft();
+
+ const tools::Long lParaIndent = nLeftFrameMargin + nParaItemTxtLeft;
+ const tools::Long lRightMargin = nRightFrameMargin - nParaItemTxtLeft;
+
+ const tools::Long lLastTab = mxTabStopItem->Count()
+ ? ConvertHPosPixel(mxTabStopItem->At(mxTabStopItem->Count() - 1).GetTabPos())
+ : 0;
+ const tools::Long lPosPixel = ConvertHPosPixel(lParaIndent) + lLastTab;
+ const tools::Long lRightIndent = ConvertHPosPixel(nRightFrameMargin - mxParaItem->GetRight());
+
+ tools::Long nDefTabDist = ConvertHPosPixel(lDefTabDist);
+
+ const sal_uInt16 nDefTabBuf = lPosPixel > lRightIndent || lLastTab > lRightIndent
+ ? 0
+ : static_cast<sal_uInt16>( (lRightIndent - lPosPixel) / nDefTabDist );
+
+ if(mxTabStopItem->Count() + TAB_GAP + nDefTabBuf > nTabBufSize)
+ {
+ // 10 (GAP) in stock
+ nTabBufSize = mxTabStopItem->Count() + TAB_GAP + nDefTabBuf + GAP;
+ mpTabs.resize(nTabBufSize);
+ }
+
+ nTabCount = 0;
+ sal_uInt16 j;
+
+ const tools::Long lParaIndentPix = ConvertSizePixel(lParaIndent);
+
+ tools::Long lTabStartLogic = (mxRulerImpl->bIsTabsRelativeToIndent ? lParaIndent : nLeftFrameMargin)
+ + lAppNullOffset;
+ if (bRTL)
+ {
+ lTabStartLogic = lParaIndent + lRightMargin - lTabStartLogic;
+ }
+ tools::Long lLastTabOffsetLogic = 0;
+ for(j = 0; j < mxTabStopItem->Count(); ++j)
+ {
+ const SvxTabStop* pTab = &mxTabStopItem->At(j);
+ lLastTabOffsetLogic = pTab->GetTabPos();
+ tools::Long lPos = lTabStartLogic + (bRTL ? -lLastTabOffsetLogic : lLastTabOffsetLogic);
+ mpTabs[nTabCount + TAB_GAP].nPos = ConvertHPosPixel(lPos);
+ mpTabs[nTabCount + TAB_GAP].nStyle = ToSvTab_Impl(pTab->GetAdjustment());
+ ++nTabCount;
+ }
+
+ // Adjust to previous-to-first default tab stop
+ lLastTabOffsetLogic -= lLastTabOffsetLogic % lDefTabDist;
+
+ // fill the rest with default Tabs
+ for (j = 0; j < nDefTabBuf; ++j)
+ {
+ //simply add the default distance to the last position
+ lLastTabOffsetLogic += lDefTabDist;
+ if (bRTL)
+ {
+ mpTabs[nTabCount + TAB_GAP].nPos =
+ ConvertHPosPixel(lTabStartLogic - lLastTabOffsetLogic);
+ if (mpTabs[nTabCount + TAB_GAP].nPos <= lParaIndentPix)
+ break;
+ }
+ else
+ {
+ mpTabs[nTabCount + TAB_GAP].nPos =
+ ConvertHPosPixel(lTabStartLogic + lLastTabOffsetLogic);
+ if (mpTabs[nTabCount + TAB_GAP].nPos >= lRightIndent)
+ break;
+ }
+
+ mpTabs[nTabCount + TAB_GAP].nStyle = RULER_TAB_DEFAULT;
+ ++nTabCount;
+ }
+ SetTabs(nTabCount, mpTabs.data() + TAB_GAP);
+ DBG_ASSERT(nTabCount + TAB_GAP <= nTabBufSize, "BufferSize too small");
+ }
+ else
+ {
+ SetTabs();
+ }
+}
+
+void SvxRuler::Update(const SvxTabStopItem *pItem) // new value for tabs
+{
+ /* Store new value for tabs; delete old ones if possible */
+ if(!bActive)
+ return;
+
+ if(pItem)
+ {
+ mxTabStopItem.reset(new SvxTabStopItem(*pItem));
+ if(!bHorz)
+ mxTabStopItem->SetWhich(SID_ATTR_TABSTOP_VERTICAL);
+ }
+ else
+ {
+ mxTabStopItem.reset();
+ }
+ StartListening_Impl();
+}
+
+void SvxRuler::Update(const SvxObjectItem *pItem) // new value for objects
+{
+ /* Store new value for objects */
+ if(bActive)
+ {
+ if(pItem)
+ mxObjectItem.reset(new SvxObjectItem(*pItem));
+ else
+ mxObjectItem.reset();
+ StartListening_Impl();
+ }
+}
+
+void SvxRuler::SetNullOffsetLogic(tools::Long lVal) // Setting of the logic NullOffsets
+{
+ lAppNullOffset = lLogicNullOffset - lVal;
+ bAppSetNullOffset = true;
+ Ruler::SetNullOffset(ConvertSizePixel(lVal));
+ Update();
+}
+
+void SvxRuler::Update()
+{
+ /* Perform update of view */
+ if(IsDrag())
+ return;
+
+ UpdatePage();
+ UpdateFrame();
+ if(nFlags & SvxRulerSupportFlags::OBJECT)
+ UpdateObject();
+ else
+ UpdateColumns();
+
+ if(nFlags & (SvxRulerSupportFlags::PARAGRAPH_MARGINS | SvxRulerSupportFlags::PARAGRAPH_MARGINS_VERTICAL))
+ UpdatePara();
+
+ if(nFlags & SvxRulerSupportFlags::TABS)
+ UpdateTabs();
+}
+
+tools::Long SvxRuler::GetPageWidth() const
+{
+ if (!mxPagePosItem)
+ return 0;
+ return bHorz ? mxPagePosItem->GetWidth() : mxPagePosItem->GetHeight();
+}
+
+inline tools::Long SvxRuler::GetFrameLeft() const
+{
+ /* Get Left margin in Pixels */
+ return bAppSetNullOffset ?
+ GetMargin1() + ConvertSizePixel(lLogicNullOffset) :
+ Ruler::GetNullOffset();
+}
+
+tools::Long SvxRuler::GetFirstLineIndent() const
+{
+ /* Get First-line indent in pixels */
+ return mxParaItem ? mpIndents[INDENT_FIRST_LINE].nPos : GetMargin1();
+}
+
+tools::Long SvxRuler::GetLeftIndent() const
+{
+ /* Get Left paragraph margin in Pixels */
+ return mxParaItem ? mpIndents[INDENT_LEFT_MARGIN].nPos : GetMargin1();
+}
+
+tools::Long SvxRuler::GetRightIndent() const
+{
+ /* Get Right paragraph margin in Pixels */
+ return mxParaItem ? mpIndents[INDENT_RIGHT_MARGIN].nPos : GetMargin2();
+}
+
+tools::Long SvxRuler::GetLogicRightIndent() const
+{
+ /* Get Right paragraph margin in Logic */
+ return mxParaItem ? GetRightFrameMargin() - mxParaItem->GetRight() : GetRightFrameMargin();
+}
+
+// Left margin in App values, is either the margin (= 0) or the left edge of
+// the column that is set in the column attribute as current column.
+tools::Long SvxRuler::GetLeftFrameMargin() const
+{
+ // #126721# for some unknown reason the current column is set to 0xffff
+ DBG_ASSERT(!mxColumnItem || mxColumnItem->GetActColumn() < mxColumnItem->Count(),
+ "issue #126721# - invalid current column!");
+ tools::Long nLeft = 0;
+ if (mxColumnItem &&
+ mxColumnItem->Count() &&
+ mxColumnItem->IsConsistent())
+ {
+ nLeft = mxColumnItem->GetActiveColumnDescription().nStart;
+ }
+
+ if (mxBorderItem && (!mxColumnItem || mxColumnItem->IsTable()))
+ nLeft += mxBorderItem->GetLeft();
+
+ return nLeft;
+}
+
+inline tools::Long SvxRuler::GetLeftMin() const
+{
+ DBG_ASSERT(mxMinMaxItem, "no MinMax value set");
+ if (mxMinMaxItem)
+ {
+ if (bHorz)
+ return mxMinMaxItem->GetValue().Left();
+ else
+ return mxMinMaxItem->GetValue().Top();
+ }
+ return 0;
+}
+
+inline tools::Long SvxRuler::GetRightMax() const
+{
+ DBG_ASSERT(mxMinMaxItem, "no MinMax value set");
+ if (mxMinMaxItem)
+ {
+ if (bHorz)
+ return mxMinMaxItem->GetValue().Right();
+ else
+ return mxMinMaxItem->GetValue().Bottom();
+ }
+ return 0;
+}
+
+
+tools::Long SvxRuler::GetRightFrameMargin() const
+{
+ /* Get right frame margin (in logical units) */
+ if (mxColumnItem)
+ {
+ if (!IsActLastColumn(true))
+ {
+ return mxColumnItem->At(GetActRightColumn(true)).nEnd;
+ }
+ }
+
+ tools::Long lResult = lLogicNullOffset;
+
+ // If possible deduct right table entry
+ if(mxColumnItem && mxColumnItem->IsTable())
+ lResult += mxColumnItem->GetRight();
+ else if(bHorz && mxLRSpaceItem)
+ lResult += mxLRSpaceItem->GetRight();
+ else if(!bHorz && mxULSpaceItem)
+ lResult += mxULSpaceItem->GetLower();
+
+ if (bHorz && mxBorderItem && (!mxColumnItem || mxColumnItem->IsTable()))
+ lResult += mxBorderItem->GetRight();
+
+ if(bHorz)
+ lResult = mxPagePosItem->GetWidth() - lResult;
+ else
+ lResult = mxPagePosItem->GetHeight() - lResult;
+
+ return lResult;
+}
+
+#define NEG_FLAG ( (nFlags & SvxRulerSupportFlags::NEGATIVE_MARGINS) == \
+ SvxRulerSupportFlags::NEGATIVE_MARGINS )
+#define TAB_FLAG ( mxColumnItem && mxColumnItem->IsTable() )
+
+tools::Long SvxRuler::GetCorrectedDragPos( bool bLeft, bool bRight )
+{
+ /*
+ Corrects the position within the calculated limits. The limit values are in
+ pixels relative to the page edge.
+ */
+
+ const tools::Long lNullPix = Ruler::GetNullOffset();
+ tools::Long lDragPos = GetDragPos() + lNullPix;
+ bool bHoriRows = bHorz && mxRulerImpl->bIsTableRows;
+ if((bLeft || bHoriRows) && lDragPos < nMaxLeft)
+ lDragPos = nMaxLeft;
+ else if((bRight||bHoriRows) && lDragPos > nMaxRight)
+ lDragPos = nMaxRight;
+ return lDragPos - lNullPix;
+}
+
+static void ModifyTabs_Impl( sal_uInt16 nCount, // Number of Tabs
+ RulerTab* pTabs, // Tab buffer
+ tools::Long lDiff) // difference to be added
+{
+ /* Helper function, move all the tabs by a fixed value */
+ if( pTabs )
+ {
+ for(sal_uInt16 i = 0; i < nCount; ++i)
+ {
+ pTabs[i].nPos += lDiff;
+ }
+ }
+}
+
+void SvxRuler::DragMargin1()
+{
+ /* Dragging the left edge of frame */
+ tools::Long aDragPosition = GetCorrectedDragPos( !TAB_FLAG || !NEG_FLAG );
+
+ aDragPosition = MakePositionSticky(aDragPosition, GetRightFrameMargin(), false);
+
+ // Check if position changed
+ if (aDragPosition == 0)
+ return;
+
+ DrawLine_Impl(lTabPos, ( TAB_FLAG && NEG_FLAG ) ? 3 : 7, bHorz);
+ if (mxColumnItem && (nDragType & SvxRulerDragFlags::OBJECT_SIZE_PROPORTIONAL))
+ DragBorders();
+ AdjustMargin1(aDragPosition);
+}
+
+void SvxRuler::AdjustMargin1(tools::Long lInputDiff)
+{
+ const tools::Long nOld = bAppSetNullOffset? GetMargin1(): GetNullOffset();
+ const tools::Long lDragPos = lInputDiff;
+
+ bool bProtectColumns =
+ mxRulerImpl->aProtectItem->IsSizeProtected() ||
+ mxRulerImpl->aProtectItem->IsPosProtected();
+
+ const RulerMarginStyle nMarginStyle =
+ bProtectColumns ? RulerMarginStyle::NONE : RulerMarginStyle::Sizeable;
+
+ if(!bAppSetNullOffset)
+ {
+ tools::Long lDiff = lDragPos;
+ SetNullOffset(nOld + lDiff);
+ if (!mxColumnItem || !(nDragType & SvxRulerDragFlags::OBJECT_SIZE_LINEAR))
+ {
+ SetMargin2( GetMargin2() - lDiff, nMarginStyle );
+
+ if (!mxColumnItem && !mxObjectItem && mxParaItem)
+ {
+ // Right indent of the old position
+ mpIndents[INDENT_RIGHT_MARGIN].nPos -= lDiff;
+ SetIndents(INDENT_COUNT, mpIndents.data() + INDENT_GAP);
+ }
+ if (mxObjectItem)
+ {
+ mpObjectBorders[GetObjectBordersOff(0)].nPos -= lDiff;
+ mpObjectBorders[GetObjectBordersOff(1)].nPos -= lDiff;
+ SetBorders(2, mpObjectBorders.data() + GetObjectBordersOff(0));
+ }
+ if (mxColumnItem)
+ {
+ for(sal_uInt16 i = 0; i < mxColumnItem->Count()-1; ++i)
+ mpBorders[i].nPos -= lDiff;
+ SetBorders(mxColumnItem->Count()-1, mpBorders.data());
+ if(mxColumnItem->IsFirstAct())
+ {
+ // Right indent of the old position
+ if (mxParaItem)
+ {
+ mpIndents[INDENT_RIGHT_MARGIN].nPos -= lDiff;
+ SetIndents(INDENT_COUNT, mpIndents.data() + INDENT_GAP);
+ }
+ }
+ else
+ {
+ if (mxParaItem)
+ {
+ mpIndents[INDENT_FIRST_LINE].nPos -= lDiff;
+ mpIndents[INDENT_LEFT_MARGIN].nPos -= lDiff;
+ mpIndents[INDENT_RIGHT_MARGIN].nPos -= lDiff;
+ SetIndents(INDENT_COUNT, mpIndents.data() + INDENT_GAP);
+ }
+ }
+ if(mxTabStopItem && (nDragType & SvxRulerDragFlags::OBJECT_SIZE_PROPORTIONAL)
+ &&!IsActFirstColumn())
+ {
+ ModifyTabs_Impl(nTabCount + TAB_GAP, mpTabs.data(), -lDiff);
+ SetTabs(nTabCount, mpTabs.data() + TAB_GAP);
+ }
+ }
+ }
+ }
+ else
+ {
+ tools::Long lDiff = lDragPos - nOld;
+ SetMargin1(nOld + lDiff, nMarginStyle);
+
+ if (!mxColumnItem
+ || !(nDragType
+ & (SvxRulerDragFlags::OBJECT_SIZE_LINEAR
+ | SvxRulerDragFlags::OBJECT_SIZE_PROPORTIONAL)))
+ {
+ if (!mxColumnItem && !mxObjectItem && mxParaItem)
+ {
+ // Left indent of the old position
+ mpIndents[INDENT_FIRST_LINE].nPos += lDiff;
+ mpIndents[INDENT_LEFT_MARGIN].nPos += lDiff;
+ SetIndents(INDENT_COUNT, mpIndents.data() + INDENT_GAP);
+ }
+
+ if (mxColumnItem)
+ {
+ for(sal_uInt16 i = 0; i < mxColumnItem->Count() - 1; ++i)
+ mpBorders[i].nPos += lDiff;
+ SetBorders(mxColumnItem->Count() - 1, mpBorders.data());
+ if (mxColumnItem->IsFirstAct())
+ {
+ // Left indent of the old position
+ if (mxParaItem)
+ {
+ mpIndents[INDENT_FIRST_LINE].nPos += lDiff;
+ mpIndents[INDENT_LEFT_MARGIN].nPos += lDiff;
+ SetIndents(INDENT_COUNT, mpIndents.data() + INDENT_GAP);
+ }
+ }
+ else
+ {
+ if (mxParaItem)
+ {
+ mpIndents[INDENT_FIRST_LINE].nPos += lDiff;
+ mpIndents[INDENT_LEFT_MARGIN].nPos += lDiff;
+ mpIndents[INDENT_RIGHT_MARGIN].nPos += lDiff;
+ SetIndents(INDENT_COUNT, mpIndents.data() + INDENT_GAP);
+ }
+ }
+ }
+ if (mxTabStopItem)
+ {
+ ModifyTabs_Impl(nTabCount + TAB_GAP, mpTabs.data(), lDiff);
+ SetTabs(nTabCount, mpTabs.data() + TAB_GAP);
+ }
+ }
+ }
+}
+
+void SvxRuler::DragMargin2()
+{
+ /* Dragging the right edge of frame */
+ tools::Long aDragPosition = GetCorrectedDragPos( true, !TAB_FLAG || !NEG_FLAG);
+ aDragPosition = MakePositionSticky(aDragPosition, GetLeftFrameMargin(), false);
+ tools::Long lDiff = aDragPosition - GetMargin2();
+
+ // Check if position changed
+ if (lDiff == 0)
+ return;
+
+ if( mxRulerImpl->bIsTableRows &&
+ !bHorz &&
+ mxColumnItem &&
+ (nDragType & SvxRulerDragFlags::OBJECT_SIZE_PROPORTIONAL))
+ {
+ DragBorders();
+ }
+
+ bool bProtectColumns =
+ mxRulerImpl->aProtectItem->IsSizeProtected() ||
+ mxRulerImpl->aProtectItem->IsPosProtected();
+
+ const RulerMarginStyle nMarginStyle = bProtectColumns ? RulerMarginStyle::NONE : RulerMarginStyle::Sizeable;
+
+ SetMargin2( aDragPosition, nMarginStyle );
+
+ // Right indent of the old position
+ if ((!mxColumnItem || IsActLastColumn()) && mxParaItem)
+ {
+ mpIndents[INDENT_FIRST_LINE].nPos += lDiff;
+ SetIndents(INDENT_COUNT, mpIndents.data() + INDENT_GAP);
+ }
+
+ DrawLine_Impl(lTabPos, ( TAB_FLAG && NEG_FLAG ) ? 5 : 7, bHorz);
+}
+
+void SvxRuler::DragIndents()
+{
+ /* Dragging the paragraph indents */
+ tools::Long aDragPosition = NEG_FLAG ? GetDragPos() : GetCorrectedDragPos();
+ const sal_uInt16 nIndex = GetDragAryPos() + INDENT_GAP;
+
+ bool bRTL = mxRulerImpl->pTextRTLItem && mxRulerImpl->pTextRTLItem->GetValue();
+
+ if(nIndex == INDENT_RIGHT_MARGIN)
+ aDragPosition = MakePositionSticky(aDragPosition, bRTL ? GetLeftFrameMargin() : GetRightFrameMargin());
+ else
+ aDragPosition = MakePositionSticky(aDragPosition, bRTL ? GetRightFrameMargin() : GetLeftFrameMargin());
+
+ const tools::Long lDiff = mpIndents[nIndex].nPos - aDragPosition;
+
+ // Check if position changed
+ if (lDiff == 0)
+ return;
+
+ if((nIndex == INDENT_FIRST_LINE || nIndex == INDENT_LEFT_MARGIN ) &&
+ !(nDragType & SvxRulerDragFlags::OBJECT_LEFT_INDENT_ONLY))
+ {
+ mpIndents[INDENT_FIRST_LINE].nPos -= lDiff;
+ }
+
+ mpIndents[nIndex].nPos = aDragPosition;
+
+ SetIndents(INDENT_COUNT, mpIndents.data() + INDENT_GAP);
+ DrawLine_Impl(lTabPos, 1, bHorz);
+}
+
+void SvxRuler::DrawLine_Impl(tools::Long& lTabPosition, int nNew, bool bHorizontal)
+{
+ /*
+ Output routine for the ledger line when moving tabs, tables and other
+ columns
+ */
+ if(bHorizontal)
+ {
+ const tools::Long nHeight = pEditWin->GetOutDev()->GetOutputSize().Height();
+ Point aZero = pEditWin->GetMapMode().GetOrigin();
+ if(lTabPosition != -1)
+ {
+ pEditWin->InvertTracking(
+ tools::Rectangle( Point(lTabPosition, -aZero.Y()),
+ Point(lTabPosition, -aZero.Y() + nHeight)),
+ ShowTrackFlags::Split | ShowTrackFlags::Clip );
+ }
+ if( nNew & 1 )
+ {
+ tools::Long nDrapPosition = GetCorrectedDragPos( ( nNew & 4 ) != 0, ( nNew & 2 ) != 0 );
+ nDrapPosition = MakePositionSticky(nDrapPosition, GetLeftFrameMargin());
+ lTabPosition = ConvertHSizeLogic( nDrapPosition + GetNullOffset() );
+ if (mxPagePosItem)
+ lTabPosition += mxPagePosItem->GetPos().X();
+ pEditWin->InvertTracking(
+ tools::Rectangle( Point(lTabPosition, -aZero.Y()),
+ Point(lTabPosition, -aZero.Y() + nHeight) ),
+ ShowTrackFlags::Clip | ShowTrackFlags::Split );
+ }
+ }
+ else
+ {
+ const tools::Long nWidth = pEditWin->GetOutDev()->GetOutputSize().Width();
+ Point aZero = pEditWin->GetMapMode().GetOrigin();
+ if(lTabPosition != -1)
+ {
+ pEditWin->InvertTracking(
+ tools::Rectangle( Point(-aZero.X(), lTabPosition),
+ Point(-aZero.X() + nWidth, lTabPosition)),
+ ShowTrackFlags::Split | ShowTrackFlags::Clip );
+ }
+
+ if(nNew & 1)
+ {
+ tools::Long nDrapPosition = GetCorrectedDragPos();
+ nDrapPosition = MakePositionSticky(nDrapPosition, GetLeftFrameMargin());
+ lTabPosition = ConvertVSizeLogic(nDrapPosition + GetNullOffset());
+ if (mxPagePosItem)
+ lTabPosition += mxPagePosItem->GetPos().Y();
+ pEditWin->InvertTracking(
+ tools::Rectangle( Point(-aZero.X(), lTabPosition),
+ Point(-aZero.X()+nWidth, lTabPosition)),
+ ShowTrackFlags::Clip | ShowTrackFlags::Split );
+ }
+ }
+}
+
+void SvxRuler::DragTabs()
+{
+ /* Dragging of Tabs */
+ tools::Long aDragPosition = GetCorrectedDragPos(true, false);
+ aDragPosition = MakePositionSticky(aDragPosition, GetLeftFrameMargin());
+
+ sal_uInt16 nIdx = GetDragAryPos() + TAB_GAP;
+ tools::Long nDiff = aDragPosition - mpTabs[nIdx].nPos;
+ if (nDiff == 0)
+ return;
+
+ DrawLine_Impl(lTabPos, 7, bHorz);
+
+ if(nDragType & SvxRulerDragFlags::OBJECT_SIZE_LINEAR)
+ {
+
+ for(sal_uInt16 i = nIdx; i < nTabCount; ++i)
+ {
+ mpTabs[i].nPos += nDiff;
+ // limit on maximum
+ if(mpTabs[i].nPos > GetMargin2())
+ mpTabs[nIdx].nStyle |= RULER_STYLE_INVISIBLE;
+ else
+ mpTabs[nIdx].nStyle &= ~RULER_STYLE_INVISIBLE;
+ }
+ }
+ else if(nDragType & SvxRulerDragFlags::OBJECT_SIZE_PROPORTIONAL)
+ {
+ mxRulerImpl->nTotalDist -= nDiff;
+ mpTabs[nIdx].nPos = aDragPosition;
+ for(sal_uInt16 i = nIdx+1; i < nTabCount; ++i)
+ {
+ if(mpTabs[i].nStyle & RULER_TAB_DEFAULT)
+ // can be canceled at the DefaultTabs
+ break;
+ tools::Long nDelta = mxRulerImpl->nTotalDist * mxRulerImpl->pPercBuf[i];
+ nDelta /= 1000;
+ mpTabs[i].nPos = mpTabs[nIdx].nPos + nDelta;
+ if(mpTabs[i].nPos + GetNullOffset() > nMaxRight)
+ mpTabs[i].nStyle |= RULER_STYLE_INVISIBLE;
+ else
+ mpTabs[i].nStyle &= ~RULER_STYLE_INVISIBLE;
+ }
+ }
+ else
+ {
+ mpTabs[nIdx].nPos = aDragPosition;
+ }
+
+ if(IsDragDelete())
+ mpTabs[nIdx].nStyle |= RULER_STYLE_INVISIBLE;
+ else
+ mpTabs[nIdx].nStyle &= ~RULER_STYLE_INVISIBLE;
+ SetTabs(nTabCount, mpTabs.data() + TAB_GAP);
+}
+
+void SvxRuler::SetActive(bool bOn)
+{
+ if(bOn)
+ {
+ Activate();
+ }
+ else
+ Deactivate();
+ if(bActive!=bOn)
+ {
+ pBindings->EnterRegistrations();
+ if(bOn)
+ for(sal_uInt16 i=0;i<mxRulerImpl->nControllerItems;i++)
+ pCtrlItems[i]->ReBind();
+ else
+ for(sal_uInt16 j=0;j<mxRulerImpl->nControllerItems;j++)
+ pCtrlItems[j]->UnBind();
+ pBindings->LeaveRegistrations();
+ }
+ bActive = bOn;
+}
+
+void SvxRuler::UpdateParaContents_Impl(
+ tools::Long lDifference,
+ UpdateType eType) // Art (all, left or right)
+{
+ /* Helper function; carry Tabs and Paragraph Margins */
+ switch(eType)
+ {
+ case UpdateType::MoveRight:
+ mpIndents[INDENT_RIGHT_MARGIN].nPos += lDifference;
+ break;
+ case UpdateType::MoveLeft:
+ {
+ mpIndents[INDENT_FIRST_LINE].nPos += lDifference;
+ mpIndents[INDENT_LEFT_MARGIN].nPos += lDifference;
+ if (!mpTabs.empty())
+ {
+ for(sal_uInt16 i = 0; i < nTabCount+TAB_GAP; ++i)
+ {
+ mpTabs[i].nPos += lDifference;
+ }
+ SetTabs(nTabCount, mpTabs.data() + TAB_GAP);
+ }
+ break;
+ }
+ }
+ SetIndents(INDENT_COUNT, mpIndents.data() + INDENT_GAP);
+}
+
+void SvxRuler::DragBorders()
+{
+ /* Dragging of Borders (Tables and other columns) */
+ bool bLeftIndentsCorrected = false;
+ bool bRightIndentsCorrected = false;
+ int nIndex;
+
+ if(GetDragType() == RulerType::Border)
+ {
+ DrawLine_Impl(lTabPos, 7, bHorz);
+ nIndex = GetDragAryPos();
+ }
+ else
+ {
+ nIndex = 0;
+ }
+
+ RulerDragSize nDragSize = GetDragSize();
+ tools::Long lDiff = 0;
+
+ // the drag position has to be corrected to be able to prevent borders from passing each other
+ tools::Long lPos = MakePositionSticky(GetCorrectedDragPos(), GetLeftFrameMargin());
+
+ switch(nDragSize)
+ {
+ case RulerDragSize::Move:
+ {
+ if(GetDragType() == RulerType::Border)
+ lDiff = lPos - nDragOffset - mpBorders[nIndex].nPos;
+ else
+ lDiff = GetDragType() == RulerType::Margin1 ? lPos - mxRulerImpl->lLastLMargin : lPos - mxRulerImpl->lLastRMargin;
+
+ if(nDragType & SvxRulerDragFlags::OBJECT_SIZE_LINEAR)
+ {
+ tools::Long nRight = GetMargin2() - glMinFrame; // Right limiters
+ for(int i = mpBorders.size() - 2; i >= nIndex; --i)
+ {
+ tools::Long l = mpBorders[i].nPos;
+ mpBorders[i].nPos += lDiff;
+ mpBorders[i].nPos = std::min(mpBorders[i].nPos, nRight - mpBorders[i].nWidth);
+ nRight = mpBorders[i].nPos - glMinFrame;
+ // RR update the column
+ if(i == GetActRightColumn())
+ {
+ UpdateParaContents_Impl(mpBorders[i].nPos - l, UpdateType::MoveRight);
+ bRightIndentsCorrected = true;
+ }
+ // LAR, EZE update the column
+ else if(i == GetActLeftColumn())
+ {
+ UpdateParaContents_Impl(mpBorders[i].nPos - l, UpdateType::MoveLeft);
+ bLeftIndentsCorrected = true;
+ }
+ }
+ }
+ else if(nDragType & SvxRulerDragFlags::OBJECT_SIZE_PROPORTIONAL)
+ {
+ int nLimit;
+ tools::Long lLeft;
+ int nStartLimit = mpBorders.size() - 2;
+ switch(GetDragType())
+ {
+ default: ;//prevent warning
+ OSL_FAIL("svx::SvxRuler::DragBorders(), unknown drag type!" );
+ [[fallthrough]];
+ case RulerType::Border:
+ if(mxRulerImpl->bIsTableRows)
+ {
+ mpBorders[nIndex].nPos += lDiff;
+ if(bHorz)
+ {
+ lLeft = mpBorders[nIndex].nPos;
+ mxRulerImpl->nTotalDist -= lDiff;
+ nLimit = nIndex + 1;
+ }
+ else
+ {
+ lLeft = 0;
+ nStartLimit = nIndex - 1;
+ mxRulerImpl->nTotalDist += lDiff;
+ nLimit = 0;
+ }
+ }
+ else
+ {
+ nLimit = nIndex + 1;
+ mpBorders[nIndex].nPos += lDiff;
+ lLeft = mpBorders[nIndex].nPos;
+ mxRulerImpl->nTotalDist -= lDiff;
+ }
+ break;
+ case RulerType::Margin1:
+ nLimit = 0;
+ lLeft = mxRulerImpl->lLastLMargin + lDiff;
+ mxRulerImpl->nTotalDist -= lDiff;
+ break;
+ case RulerType::Margin2:
+ nLimit = 0;
+ lLeft= 0;
+ nStartLimit = mpBorders.size() - 2;
+ mxRulerImpl->nTotalDist += lDiff;
+ break;
+ }
+
+ for(int i = nStartLimit; i >= nLimit; --i)
+ {
+
+ tools::Long l = mpBorders[i].nPos;
+ mpBorders[i].nPos =
+ lLeft +
+ (mxRulerImpl->nTotalDist * mxRulerImpl->pPercBuf[i]) / 1000 +
+ mxRulerImpl->pBlockBuf[i];
+
+ // RR update the column
+ if(!mxRulerImpl->bIsTableRows)
+ {
+ if(i == GetActRightColumn())
+ {
+ UpdateParaContents_Impl(mpBorders[i].nPos - l, UpdateType::MoveRight);
+ bRightIndentsCorrected = true;
+ }
+ // LAR, EZE update the column
+ else if(i == GetActLeftColumn())
+ {
+ UpdateParaContents_Impl(mpBorders[i].nPos - l, UpdateType::MoveLeft);
+ bLeftIndentsCorrected = true;
+ }
+ }
+ }
+ if(mxRulerImpl->bIsTableRows)
+ {
+ //in vertical tables the left borders have to be moved
+ if(bHorz)
+ {
+ for(int i = 0; i < nIndex; ++i)
+ mpBorders[i].nPos += lDiff;
+ AdjustMargin1(lDiff);
+ }
+ else
+ {
+ //otherwise the right borders are moved
+ for(int i = mxColumnItem->Count() - 1; i > nIndex; --i)
+ mpBorders[i].nPos += lDiff;
+ SetMargin2( GetMargin2() + lDiff, RulerMarginStyle::NONE );
+ }
+ }
+ }
+ else if(mxRulerImpl->bIsTableRows)
+ {
+ //moving rows: if a row is resized all following rows
+ //have to be moved by the same amount.
+ //This includes the left border when the table is not limited
+ //to a lower frame border.
+ int nLimit;
+ if(GetDragType()==RulerType::Border)
+ {
+ nLimit = nIndex + 1;
+ mpBorders[nIndex].nPos += lDiff;
+ }
+ else
+ {
+ nLimit=0;
+ }
+ //in vertical tables the left borders have to be moved
+ if(bHorz)
+ {
+ for(int i = 0; i < nIndex; ++i)
+ {
+ mpBorders[i].nPos += lDiff;
+ }
+ AdjustMargin1(lDiff);
+ }
+ else
+ {
+ //otherwise the right borders are moved
+ for(int i = mpBorders.size() - 2; i >= nLimit; --i)
+ {
+ mpBorders[i].nPos += lDiff;
+ }
+ SetMargin2( GetMargin2() + lDiff, RulerMarginStyle::NONE );
+ }
+ }
+ else
+ mpBorders[nIndex].nPos += lDiff;
+ break;
+ }
+ case RulerDragSize::N1:
+ {
+ lDiff = lPos - mpBorders[nIndex].nPos;
+ mpBorders[nIndex].nWidth += mpBorders[nIndex].nPos - lPos;
+ mpBorders[nIndex].nPos = lPos;
+ break;
+ }
+ case RulerDragSize::N2:
+ {
+ const tools::Long nOld = mpBorders[nIndex].nWidth;
+ mpBorders[nIndex].nWidth = lPos - mpBorders[nIndex].nPos;
+ lDiff = mpBorders[nIndex].nWidth - nOld;
+ break;
+ }
+ }
+ if(!bRightIndentsCorrected &&
+ GetActRightColumn() == nIndex &&
+ nDragSize != RulerDragSize::N2 &&
+ !mpIndents.empty() &&
+ !mxRulerImpl->bIsTableRows)
+ {
+ UpdateParaContents_Impl(lDiff, UpdateType::MoveRight);
+ }
+ else if(!bLeftIndentsCorrected &&
+ GetActLeftColumn() == nIndex &&
+ nDragSize != RulerDragSize::N1 &&
+ !mpIndents.empty())
+ {
+ UpdateParaContents_Impl(lDiff, UpdateType::MoveLeft);
+ }
+ SetBorders(mxColumnItem->Count() - 1, mpBorders.data());
+}
+
+void SvxRuler::DragObjectBorder()
+{
+ /* Dragging of object edges */
+ if(RulerDragSize::Move == GetDragSize())
+ {
+ const tools::Long lPosition = MakePositionSticky(GetCorrectedDragPos(), GetLeftFrameMargin());
+
+ const sal_uInt16 nIdx = GetDragAryPos();
+ mpObjectBorders[GetObjectBordersOff(nIdx)].nPos = lPosition;
+ SetBorders(2, mpObjectBorders.data() + GetObjectBordersOff(0));
+ DrawLine_Impl(lTabPos, 7, bHorz);
+
+ }
+}
+
+void SvxRuler::ApplyMargins()
+{
+ /* Applying margins; changed by dragging. */
+ const SfxPoolItem* pItem = nullptr;
+ sal_uInt16 nId = SID_ATTR_LONG_LRSPACE;
+
+ if(bHorz)
+ {
+ const tools::Long lOldNull = lLogicNullOffset;
+ if(mxRulerImpl->lMaxLeftLogic != -1 && nMaxLeft == GetMargin1() + Ruler::GetNullOffset())
+ {
+ lLogicNullOffset = mxRulerImpl->lMaxLeftLogic;
+ mxLRSpaceItem->SetLeft(lLogicNullOffset);
+ }
+ else
+ {
+ lLogicNullOffset = ConvertHPosLogic(GetFrameLeft()) - lAppNullOffset;
+ mxLRSpaceItem->SetLeft(PixelHAdjust(lLogicNullOffset, mxLRSpaceItem->GetLeft()));
+ }
+
+ if(bAppSetNullOffset)
+ {
+ lAppNullOffset += lLogicNullOffset - lOldNull;
+ }
+
+ tools::Long nRight;
+ if(mxRulerImpl->lMaxRightLogic != -1
+ && nMaxRight == GetMargin2() + Ruler::GetNullOffset())
+ {
+ nRight = GetPageWidth() - mxRulerImpl->lMaxRightLogic;
+ }
+ else
+ {
+ nRight = std::max(tools::Long(0),
+ mxPagePosItem->GetWidth() - mxLRSpaceItem->GetLeft() -
+ (ConvertHPosLogic(GetMargin2()) - lAppNullOffset));
+
+ nRight = PixelHAdjust( nRight, mxLRSpaceItem->GetRight());
+ }
+ mxLRSpaceItem->SetRight(nRight);
+
+ pItem = mxLRSpaceItem.get();
+
+#ifdef DEBUGLIN
+ Debug_Impl(pEditWin, *mxLRSpaceItem);
+#endif // DEBUGLIN
+
+ }
+ else
+ {
+ const tools::Long lOldNull = lLogicNullOffset;
+ lLogicNullOffset =
+ ConvertVPosLogic(GetFrameLeft()) -
+ lAppNullOffset;
+ mxULSpaceItem->SetUpper(
+ PixelVAdjust(lLogicNullOffset, mxULSpaceItem->GetUpper()));
+ if(bAppSetNullOffset)
+ {
+ lAppNullOffset += lLogicNullOffset - lOldNull;
+ }
+ mxULSpaceItem->SetLower(
+ PixelVAdjust(
+ std::max(tools::Long(0), mxPagePosItem->GetHeight() -
+ mxULSpaceItem->GetUpper() -
+ (ConvertVPosLogic(GetMargin2()) -
+ lAppNullOffset)), mxULSpaceItem->GetLower()));
+ pItem = mxULSpaceItem.get();
+ nId = SID_ATTR_LONG_ULSPACE;
+
+#ifdef DEBUGLIN
+ Debug_Impl(pEditWin,*mxULSpaceItem);
+#endif // DEBUGLIN
+
+ }
+ pBindings->GetDispatcher()->ExecuteList(nId, SfxCallMode::RECORD, { pItem });
+ if (mxTabStopItem)
+ UpdateTabs();
+}
+
+tools::Long SvxRuler::RoundToCurrentMapMode(tools::Long lValue) const
+{
+ RulerUnitData aUnitData = GetCurrentRulerUnit();
+ double aRoundingFactor = aUnitData.nTickUnit / aUnitData.nTick1;
+
+ tools::Long lNewValue = OutputDevice::LogicToLogic(Size(lValue, 0), pEditWin->GetMapMode(), GetCurrentMapMode()).Width();
+ lNewValue = (rtl::math::round(lNewValue / static_cast<double>(aUnitData.nTickUnit) * aRoundingFactor) / aRoundingFactor) * aUnitData.nTickUnit;
+ return OutputDevice::LogicToLogic(Size(lNewValue, 0), GetCurrentMapMode(), pEditWin->GetMapMode()).Width();
+}
+
+void SvxRuler::ApplyIndents()
+{
+ /* Applying paragraph settings; changed by dragging. */
+
+ tools::Long nLeftFrameMargin = GetLeftFrameMargin();
+
+ bool bRTL = mxRulerImpl->pTextRTLItem && mxRulerImpl->pTextRTLItem->GetValue();
+
+ tools::Long nNewTxtLeft;
+ tools::Long nNewFirstLineOffset;
+ tools::Long nNewRight;
+
+ tools::Long nFirstLine = ConvertPosLogic(mpIndents[INDENT_FIRST_LINE].nPos);
+ tools::Long nLeftMargin = ConvertPosLogic(mpIndents[INDENT_LEFT_MARGIN].nPos);
+ tools::Long nRightMargin = ConvertPosLogic(mpIndents[INDENT_RIGHT_MARGIN].nPos);
+
+ if(mxColumnItem && ((bRTL && !IsActLastColumn(true)) || (!bRTL && !IsActFirstColumn(true))))
+ {
+ if(bRTL)
+ {
+ tools::Long nRightColumn = GetActRightColumn(true);
+ tools::Long nRightBorder = ConvertPosLogic(mpBorders[nRightColumn].nPos);
+ nNewTxtLeft = nRightBorder - nLeftMargin - lAppNullOffset;
+ }
+ else
+ {
+ tools::Long nLeftColumn = GetActLeftColumn(true);
+ tools::Long nLeftBorder = ConvertPosLogic(mpBorders[nLeftColumn].nPos + mpBorders[nLeftColumn].nWidth);
+ nNewTxtLeft = nLeftMargin - nLeftBorder - lAppNullOffset;
+ }
+ }
+ else
+ {
+ if(bRTL)
+ {
+ tools::Long nRightBorder = ConvertPosLogic(GetMargin2());
+ nNewTxtLeft = nRightBorder - nLeftMargin - lAppNullOffset;
+ }
+ else
+ {
+ tools::Long nLeftBorder = ConvertPosLogic(GetMargin1());
+ nNewTxtLeft = nLeftBorder + nLeftMargin - nLeftFrameMargin - lAppNullOffset;
+ }
+ }
+
+ if(bRTL)
+ nNewFirstLineOffset = nLeftMargin - nFirstLine - lAppNullOffset;
+ else
+ nNewFirstLineOffset = nFirstLine - nLeftMargin - lAppNullOffset;
+
+ if(mxColumnItem && ((!bRTL && !IsActLastColumn(true)) || (bRTL && !IsActFirstColumn(true))))
+ {
+ if(bRTL)
+ {
+ tools::Long nLeftColumn = GetActLeftColumn(true);
+ tools::Long nLeftBorder = ConvertPosLogic(mpBorders[nLeftColumn].nPos + mpBorders[nLeftColumn].nWidth);
+ nNewRight = nRightMargin - nLeftBorder - lAppNullOffset;
+ }
+ else
+ {
+ tools::Long nRightColumn = GetActRightColumn(true);
+ tools::Long nRightBorder = ConvertPosLogic(mpBorders[nRightColumn].nPos);
+ nNewRight = nRightBorder - nRightMargin - lAppNullOffset;
+ }
+ }
+ else
+ {
+ if(bRTL)
+ {
+ tools::Long nLeftBorder = ConvertPosLogic(GetMargin1());
+ nNewRight = nLeftBorder + nRightMargin - nLeftFrameMargin - lAppNullOffset;
+ }
+ else
+ {
+ tools::Long nRightBorder = ConvertPosLogic(GetMargin2());
+ nNewRight = nRightBorder - nRightMargin - lAppNullOffset;
+ }
+ }
+
+ if (mbSnapping)
+ {
+ nNewTxtLeft = RoundToCurrentMapMode(nNewTxtLeft);
+ nNewFirstLineOffset = RoundToCurrentMapMode(nNewFirstLineOffset);
+ nNewRight = RoundToCurrentMapMode(nNewRight);
+ }
+
+ mxParaItem->SetTextFirstLineOffset(sal::static_int_cast<short>(nNewFirstLineOffset));
+ mxParaItem->SetTextLeft(nNewTxtLeft);
+ mxParaItem->SetRight(nNewRight);
+
+ sal_uInt16 nParagraphId = bHorz ? SID_ATTR_PARA_LRSPACE : SID_ATTR_PARA_LRSPACE_VERTICAL;
+ pBindings->GetDispatcher()->ExecuteList(nParagraphId, SfxCallMode::RECORD,
+ { mxParaItem.get() });
+ UpdateTabs();
+}
+
+void SvxRuler::ApplyTabs()
+{
+ /* Apply tab settings, changed by dragging. */
+ bool bRTL = mxRulerImpl->pTextRTLItem && mxRulerImpl->pTextRTLItem->GetValue();
+ const sal_uInt16 nCoreIdx = GetDragAryPos();
+ if(IsDragDelete())
+ {
+ mxTabStopItem->Remove(nCoreIdx);
+ }
+ else if(SvxRulerDragFlags::OBJECT_SIZE_LINEAR & nDragType ||
+ SvxRulerDragFlags::OBJECT_SIZE_PROPORTIONAL & nDragType)
+ {
+ SvxTabStopItem *pItem = new SvxTabStopItem(mxTabStopItem->Which());
+ //remove default tab stops
+ for ( sal_uInt16 i = 0; i < pItem->Count(); )
+ {
+ if ( SvxTabAdjust::Default == (*pItem)[i].GetAdjustment() )
+ {
+ pItem->Remove(i);
+ continue;
+ }
+ ++i;
+ }
+
+ sal_uInt16 j;
+ for(j = 0; j < nCoreIdx; ++j)
+ {
+ pItem->Insert(mxTabStopItem->At(j));
+ }
+ for(; j < mxTabStopItem->Count(); ++j)
+ {
+ SvxTabStop aTabStop = mxTabStopItem->At(j);
+ aTabStop.GetTabPos() = PixelHAdjust(
+ ConvertHPosLogic(
+ mpTabs[j + TAB_GAP].nPos - GetLeftIndent()) - lAppNullOffset,
+ aTabStop.GetTabPos());
+ pItem->Insert(aTabStop);
+ }
+ mxTabStopItem.reset(pItem);
+ }
+ else if( mxTabStopItem->Count() == 0 )
+ return;
+ else
+ {
+ SvxTabStop aTabStop = mxTabStopItem->At(nCoreIdx);
+ if( mxRulerImpl->lMaxRightLogic != -1 &&
+ mpTabs[nCoreIdx + TAB_GAP].nPos + Ruler::GetNullOffset() == nMaxRight )
+ {
+ // Set tab pos exactly at the right indent
+ tools::Long nTmpLeftIndentLogic
+ = lAppNullOffset + (bRTL ? GetRightFrameMargin() : GetLeftFrameMargin());
+ if (mxRulerImpl->bIsTabsRelativeToIndent && mxParaItem)
+ {
+ nTmpLeftIndentLogic += bRTL ? mxParaItem->GetRight() : mxParaItem->GetLeft();
+ }
+ aTabStop.GetTabPos()
+ = mxRulerImpl->lMaxRightLogic - lLogicNullOffset - nTmpLeftIndentLogic;
+ }
+ else
+ {
+ if(bRTL)
+ {
+ //#i24363# tab stops relative to indent
+ const tools::Long nTmpLeftIndent = mxRulerImpl->bIsTabsRelativeToIndent ?
+ GetLeftIndent() :
+ ConvertHPosPixel( GetRightFrameMargin() + lAppNullOffset );
+
+ tools::Long nNewPosition = ConvertHPosLogic(nTmpLeftIndent - mpTabs[nCoreIdx + TAB_GAP].nPos);
+ aTabStop.GetTabPos() = PixelHAdjust(nNewPosition - lAppNullOffset, aTabStop.GetTabPos());
+ }
+ else
+ {
+ //#i24363# tab stops relative to indent
+ const tools::Long nTmpLeftIndent = mxRulerImpl->bIsTabsRelativeToIndent ?
+ GetLeftIndent() :
+ ConvertHPosPixel( GetLeftFrameMargin() + lAppNullOffset );
+
+ tools::Long nNewPosition = ConvertHPosLogic(mpTabs[nCoreIdx + TAB_GAP].nPos - nTmpLeftIndent);
+ aTabStop.GetTabPos() = PixelHAdjust(nNewPosition - lAppNullOffset, aTabStop.GetTabPos());
+ }
+ }
+ mxTabStopItem->Remove(nCoreIdx);
+ mxTabStopItem->Insert(aTabStop);
+ }
+ sal_uInt16 nTabStopId = bHorz ? SID_ATTR_TABSTOP : SID_ATTR_TABSTOP_VERTICAL;
+ pBindings->GetDispatcher()->ExecuteList(nTabStopId, SfxCallMode::RECORD,
+ { mxTabStopItem.get() });
+ UpdateTabs();
+}
+
+void SvxRuler::ApplyBorders()
+{
+ /* Applying (table) column settings; changed by dragging. */
+ if(mxColumnItem->IsTable())
+ {
+ tools::Long lValue = GetFrameLeft();
+ if(lValue != mxRulerImpl->nColLeftPix)
+ {
+ tools::Long nLeft = PixelHAdjust(
+ ConvertHPosLogic(lValue) -
+ lAppNullOffset,
+ mxColumnItem->GetLeft());
+ mxColumnItem->SetLeft(nLeft);
+ }
+
+ lValue = GetMargin2();
+
+ if(lValue != mxRulerImpl->nColRightPix)
+ {
+ tools::Long nWidthOrHeight = bHorz ? mxPagePosItem->GetWidth() : mxPagePosItem->GetHeight();
+ tools::Long nRight = PixelHAdjust(
+ nWidthOrHeight -
+ mxColumnItem->GetLeft() -
+ ConvertHPosLogic(lValue) -
+ lAppNullOffset,
+ mxColumnItem->GetRight() );
+ mxColumnItem->SetRight(nRight);
+ }
+ }
+
+ for(sal_uInt16 i = 0; i < mxColumnItem->Count() - 1; ++i)
+ {
+ tools::Long& nEnd = mxColumnItem->At(i).nEnd;
+ nEnd = PixelHAdjust(
+ ConvertPosLogic(mpBorders[i].nPos),
+ mxColumnItem->At(i).nEnd);
+ tools::Long& nStart = mxColumnItem->At(i + 1).nStart;
+ nStart = PixelHAdjust(
+ ConvertSizeLogic(mpBorders[i].nPos +
+ mpBorders[i].nWidth) -
+ lAppNullOffset,
+ mxColumnItem->At(i + 1).nStart);
+ // It may be that, due to the PixelHAdjust readjustment to old values,
+ // the width becomes < 0. This we readjust.
+ if( nEnd > nStart )
+ nStart = nEnd;
+ }
+
+#ifdef DEBUGLIN
+ Debug_Impl(pEditWin,*mxColumnItem);
+#endif // DEBUGLIN
+
+ SfxBoolItem aFlag(SID_RULER_ACT_LINE_ONLY,
+ bool(nDragType & SvxRulerDragFlags::OBJECT_ACTLINE_ONLY));
+
+ sal_uInt16 nColId = mxRulerImpl->bIsTableRows ? (bHorz ? SID_RULER_ROWS : SID_RULER_ROWS_VERTICAL) :
+ (bHorz ? SID_RULER_BORDERS : SID_RULER_BORDERS_VERTICAL);
+
+ pBindings->GetDispatcher()->ExecuteList(nColId, SfxCallMode::RECORD,
+ { mxColumnItem.get(), &aFlag });
+}
+
+void SvxRuler::ApplyObject()
+{
+ /* Applying object settings, changed by dragging. */
+
+ // to the page margin
+ tools::Long nMargin = mxLRSpaceItem ? mxLRSpaceItem->GetLeft() : 0;
+ tools::Long nStartX = PixelAdjust(
+ ConvertPosLogic(mpObjectBorders[0].nPos) +
+ nMargin -
+ lAppNullOffset,
+ mxObjectItem->GetStartX());
+ mxObjectItem->SetStartX(nStartX);
+
+ tools::Long nEndX = PixelAdjust(
+ ConvertPosLogic(mpObjectBorders[1].nPos) +
+ nMargin -
+ lAppNullOffset,
+ mxObjectItem->GetEndX());
+ mxObjectItem->SetEndX(nEndX);
+
+ nMargin = mxULSpaceItem ? mxULSpaceItem->GetUpper() : 0;
+ tools::Long nStartY = PixelAdjust(
+ ConvertPosLogic(mpObjectBorders[2].nPos) +
+ nMargin -
+ lAppNullOffset,
+ mxObjectItem->GetStartY());
+ mxObjectItem->SetStartY(nStartY);
+
+ tools::Long nEndY = PixelAdjust(
+ ConvertPosLogic(mpObjectBorders[3].nPos) +
+ nMargin -
+ lAppNullOffset,
+ mxObjectItem->GetEndY());
+ mxObjectItem->SetEndY(nEndY);
+
+ pBindings->GetDispatcher()->ExecuteList(SID_RULER_OBJECT,
+ SfxCallMode::RECORD, { mxObjectItem.get() });
+}
+
+void SvxRuler::PrepareProportional_Impl(RulerType eType)
+{
+ /*
+ Preparation proportional dragging, and it is calculated based on the
+ proportional share of the total width in parts per thousand.
+ */
+ mxRulerImpl->nTotalDist = GetMargin2();
+ switch(eType)
+ {
+ case RulerType::Margin2:
+ case RulerType::Margin1:
+ case RulerType::Border:
+ {
+ DBG_ASSERT(mxColumnItem, "no ColumnItem");
+
+ mxRulerImpl->SetPercSize(mxColumnItem->Count());
+
+ tools::Long lPos;
+ tools::Long lWidth=0;
+ sal_uInt16 nStart;
+ sal_uInt16 nIdx=GetDragAryPos();
+ tools::Long lActWidth=0;
+ tools::Long lActBorderSum;
+ tools::Long lOrigLPos;
+
+ if(eType != RulerType::Border)
+ {
+ lOrigLPos = GetMargin1();
+ nStart = 0;
+ lActBorderSum = 0;
+ }
+ else
+ {
+ if(mxRulerImpl->bIsTableRows &&!bHorz)
+ {
+ lOrigLPos = GetMargin1();
+ nStart = 0;
+ }
+ else
+ {
+ lOrigLPos = mpBorders[nIdx].nPos + mpBorders[nIdx].nWidth;
+ nStart = 1;
+ }
+ lActBorderSum = mpBorders[nIdx].nWidth;
+ }
+
+ //in horizontal mode the percentage value has to be
+ //calculated on a "current change" position base
+ //because the height of the table changes while dragging
+ if(mxRulerImpl->bIsTableRows && RulerType::Border == eType)
+ {
+ sal_uInt16 nStartBorder;
+ sal_uInt16 nEndBorder;
+ if(bHorz)
+ {
+ nStartBorder = nIdx + 1;
+ nEndBorder = mxColumnItem->Count() - 1;
+ }
+ else
+ {
+ nStartBorder = 0;
+ nEndBorder = nIdx;
+ }
+
+ lWidth = mpBorders[nIdx].nPos;
+ if(bHorz)
+ lWidth = GetMargin2() - lWidth;
+ mxRulerImpl->nTotalDist = lWidth;
+ lPos = mpBorders[nIdx].nPos;
+
+ for(sal_uInt16 i = nStartBorder; i < nEndBorder; ++i)
+ {
+ if(bHorz)
+ {
+ lActWidth += mpBorders[i].nPos - lPos;
+ lPos = mpBorders[i].nPos + mpBorders[i].nWidth;
+ }
+ else
+ lActWidth = mpBorders[i].nPos;
+ mxRulerImpl->pPercBuf[i] = static_cast<sal_uInt16>((lActWidth * 1000)
+ / mxRulerImpl->nTotalDist);
+ mxRulerImpl->pBlockBuf[i] = static_cast<sal_uInt16>(lActBorderSum);
+ lActBorderSum += mpBorders[i].nWidth;
+ }
+ }
+ else
+ {
+ lPos = lOrigLPos;
+ for(sal_uInt16 ii = nStart; ii < mxColumnItem->Count() - 1; ++ii)
+ {
+ lWidth += mpBorders[ii].nPos - lPos;
+ lPos = mpBorders[ii].nPos + mpBorders[ii].nWidth;
+ }
+
+ lWidth += GetMargin2() - lPos;
+ mxRulerImpl->nTotalDist = lWidth;
+ lPos = lOrigLPos;
+
+ for(sal_uInt16 i = nStart; i < mxColumnItem->Count() - 1; ++i)
+ {
+ lActWidth += mpBorders[i].nPos - lPos;
+ lPos = mpBorders[i].nPos + mpBorders[i].nWidth;
+ mxRulerImpl->pPercBuf[i] = static_cast<sal_uInt16>((lActWidth * 1000)
+ / mxRulerImpl->nTotalDist);
+ mxRulerImpl->pBlockBuf[i] = static_cast<sal_uInt16>(lActBorderSum);
+ lActBorderSum += mpBorders[i].nWidth;
+ }
+ }
+ }
+ break;
+ case RulerType::Tab:
+ {
+ const sal_uInt16 nIdx = GetDragAryPos()+TAB_GAP;
+ mxRulerImpl->nTotalDist -= mpTabs[nIdx].nPos;
+ mxRulerImpl->SetPercSize(nTabCount);
+ for(sal_uInt16 n=0;n<=nIdx;mxRulerImpl->pPercBuf[n++]=0) ;
+ for(sal_uInt16 i = nIdx+1; i < nTabCount; ++i)
+ {
+ const tools::Long nDelta = mpTabs[i].nPos - mpTabs[nIdx].nPos;
+ mxRulerImpl->pPercBuf[i] = static_cast<sal_uInt16>((nDelta * 1000) / mxRulerImpl->nTotalDist);
+ }
+ break;
+ }
+ default: break;
+ }
+}
+
+void SvxRuler::EvalModifier()
+{
+ /*
+ Eval Drag Modifier
+ Shift: move linear
+ Control: move proportional
+ Shift + Control: Table: only current line
+ Alt: disable snapping
+ Alt + Shift: coarse snapping
+ */
+
+ sal_uInt16 nModifier = GetDragModifier();
+ if(mxRulerImpl->bIsTableRows)
+ {
+ //rows can only be moved in one way, additionally current column is possible
+ if(nModifier == KEY_SHIFT)
+ nModifier = 0;
+ }
+
+ switch(nModifier)
+ {
+ case KEY_SHIFT:
+ nDragType = SvxRulerDragFlags::OBJECT_SIZE_LINEAR;
+ break;
+ case KEY_MOD2 | KEY_SHIFT:
+ mbCoarseSnapping = true;
+ break;
+ case KEY_MOD2:
+ mbSnapping = false;
+ break;
+ case KEY_MOD1:
+ {
+ const RulerType eType = GetDragType();
+ nDragType = SvxRulerDragFlags::OBJECT_SIZE_PROPORTIONAL;
+ if( RulerType::Tab == eType ||
+ ( ( RulerType::Border == eType ||
+ RulerType::Margin1 == eType ||
+ RulerType::Margin2 == eType ) &&
+ mxColumnItem ) )
+ {
+ PrepareProportional_Impl(eType);
+ }
+ }
+ break;
+ case KEY_MOD1 | KEY_SHIFT:
+ if( GetDragType() != RulerType::Margin1 &&
+ GetDragType() != RulerType::Margin2 )
+ {
+ nDragType = SvxRulerDragFlags::OBJECT_ACTLINE_ONLY;
+ }
+ break;
+ }
+}
+
+void SvxRuler::Click()
+{
+ /* Override handler SV; sets Tab per dispatcher call */
+ Ruler::Click();
+ if( bActive )
+ {
+ pBindings->Update( SID_RULER_LR_MIN_MAX );
+ pBindings->Update( SID_ATTR_LONG_ULSPACE );
+ pBindings->Update( SID_ATTR_LONG_LRSPACE );
+ pBindings->Update( SID_RULER_PAGE_POS );
+ pBindings->Update( bHorz ? SID_ATTR_TABSTOP : SID_ATTR_TABSTOP_VERTICAL);
+ pBindings->Update( bHorz ? SID_ATTR_PARA_LRSPACE : SID_ATTR_PARA_LRSPACE_VERTICAL);
+ pBindings->Update( bHorz ? SID_RULER_BORDERS : SID_RULER_BORDERS_VERTICAL);
+ pBindings->Update( bHorz ? SID_RULER_ROWS : SID_RULER_ROWS_VERTICAL);
+ pBindings->Update( SID_RULER_OBJECT );
+ pBindings->Update( SID_RULER_PROTECT );
+ pBindings->Update( SID_ATTR_PARA_LRSPACE_VERTICAL );
+ }
+ bool bRTL = mxRulerImpl->pTextRTLItem && mxRulerImpl->pTextRTLItem->GetValue();
+ if(!(mxTabStopItem &&
+ (nFlags & SvxRulerSupportFlags::TABS) == SvxRulerSupportFlags::TABS))
+ return;
+
+ bool bContentProtected = mxRulerImpl->aProtectItem->IsContentProtected();
+ if( bContentProtected ) return;
+ const tools::Long lPos = GetClickPos();
+ if(!((bRTL && lPos < std::min(GetFirstLineIndent(), GetLeftIndent()) && lPos > GetRightIndent()) ||
+ (!bRTL && lPos > std::min(GetFirstLineIndent(), GetLeftIndent()) && lPos < GetRightIndent())))
+ return;
+
+ //convert position in left-to-right text
+ tools::Long nTabPos;
+//#i24363# tab stops relative to indent
+ if(bRTL)
+ nTabPos = ( mxRulerImpl->bIsTabsRelativeToIndent ?
+ GetLeftIndent() :
+ ConvertHPosPixel( GetRightFrameMargin() + lAppNullOffset ) ) -
+ lPos;
+ else
+ nTabPos = lPos -
+ ( mxRulerImpl->bIsTabsRelativeToIndent ?
+ GetLeftIndent() :
+ ConvertHPosPixel( GetLeftFrameMargin() + lAppNullOffset ));
+
+ SvxTabStop aTabStop(ConvertHPosLogic(nTabPos),
+ ToAttrTab_Impl(nDefTabType));
+ mxTabStopItem->Insert(aTabStop);
+ UpdateTabs();
+}
+
+void SvxRuler::CalcMinMax()
+{
+ /*
+ Calculates the limits for dragging; which are in pixels relative to the
+ page edge
+ */
+ bool bRTL = mxRulerImpl->pTextRTLItem && mxRulerImpl->pTextRTLItem->GetValue();
+ const tools::Long lNullPix = ConvertPosPixel(lLogicNullOffset);
+ mxRulerImpl->lMaxLeftLogic=mxRulerImpl->lMaxRightLogic=-1;
+ switch(GetDragType())
+ {
+ case RulerType::Margin1:
+ { // left edge of the surrounding Frame
+ // DragPos - NOf between left - right
+ mxRulerImpl->lMaxLeftLogic = GetLeftMin();
+ nMaxLeft=ConvertSizePixel(mxRulerImpl->lMaxLeftLogic);
+
+ if (!mxColumnItem || mxColumnItem->Count() == 1)
+ {
+ if(bRTL)
+ {
+ nMaxRight = lNullPix - GetRightIndent() +
+ std::max(GetFirstLineIndent(), GetLeftIndent()) -
+ glMinFrame;
+ }
+ else
+ {
+ nMaxRight = lNullPix + GetRightIndent() -
+ std::max(GetFirstLineIndent(), GetLeftIndent()) -
+ glMinFrame;
+ }
+ }
+ else if(mxRulerImpl->bIsTableRows)
+ {
+ //top border is not moveable when table rows are displayed
+ // protection of content means the margin is not moveable
+ if(bHorz && !mxRulerImpl->aProtectItem->IsContentProtected())
+ {
+ nMaxLeft = mpBorders[0].nMinPos + lNullPix;
+ if(nDragType & SvxRulerDragFlags::OBJECT_SIZE_PROPORTIONAL)
+ nMaxRight = GetRightIndent() + lNullPix -
+ (mxColumnItem->Count() - 1 ) * glMinFrame;
+ else
+ nMaxRight = mpBorders[0].nPos - glMinFrame + lNullPix;
+ }
+ else
+ nMaxLeft = nMaxRight = lNullPix;
+ }
+ else
+ {
+ if (nDragType & SvxRulerDragFlags::OBJECT_SIZE_PROPORTIONAL)
+ {
+ nMaxRight=lNullPix+CalcPropMaxRight();
+ }
+ else if (nDragType & SvxRulerDragFlags::OBJECT_SIZE_LINEAR)
+ {
+ nMaxRight = ConvertPosPixel(
+ GetPageWidth() - (
+ (mxColumnItem->IsTable() && mxLRSpaceItem)
+ ? mxLRSpaceItem->GetRight() : 0))
+ - GetMargin2() + GetMargin1();
+ }
+ else
+ {
+ nMaxRight = lNullPix - glMinFrame;
+ if (mxColumnItem->IsFirstAct())
+ {
+ if(bRTL)
+ {
+ nMaxRight += std::min(
+ mpBorders[0].nPos,
+ std::max(GetFirstLineIndent(), GetLeftIndent()) - GetRightIndent());
+ }
+ else
+ {
+ nMaxRight += std::min(
+ mpBorders[0].nPos, GetRightIndent() -
+ std::max(GetFirstLineIndent(), GetLeftIndent()));
+ }
+ }
+ else if ( mxColumnItem->Count() > 1 )
+ {
+ nMaxRight += mpBorders[0].nPos;
+ }
+ else
+ {
+ nMaxRight += GetRightIndent() - std::max(GetFirstLineIndent(), GetLeftIndent());
+ }
+ // Do not drag the left table edge over the edge of the page
+ if(mxLRSpaceItem && mxColumnItem->IsTable())
+ {
+ tools::Long nTmp=ConvertSizePixel(mxLRSpaceItem->GetLeft());
+ if(nTmp>nMaxLeft)
+ nMaxLeft=nTmp;
+ }
+ }
+ }
+ break;
+ }
+ case RulerType::Margin2:
+ { // right edge of the surrounding Frame
+ mxRulerImpl->lMaxRightLogic
+ = mxMinMaxItem ? GetPageWidth() - GetRightMax() : GetPageWidth();
+ nMaxRight = ConvertSizePixel(mxRulerImpl->lMaxRightLogic);
+
+ if (!mxColumnItem)
+ {
+ if(bRTL)
+ {
+ nMaxLeft = GetMargin2() + GetRightIndent() -
+ std::max(GetFirstLineIndent(),GetLeftIndent()) - GetMargin1()+
+ glMinFrame + lNullPix;
+ }
+ else
+ {
+ nMaxLeft = GetMargin2() - GetRightIndent() +
+ std::max(GetFirstLineIndent(),GetLeftIndent()) - GetMargin1()+
+ glMinFrame + lNullPix;
+ }
+ }
+ else if(mxRulerImpl->bIsTableRows)
+ {
+ // get the bottom move range from the last border position - only available for rows!
+ // protection of content means the margin is not moveable
+ if(bHorz || mxRulerImpl->aProtectItem->IsContentProtected())
+ {
+ nMaxLeft = nMaxRight = mpBorders[mxColumnItem->Count() - 1].nMaxPos + lNullPix;
+ }
+ else
+ {
+ if(nDragType & SvxRulerDragFlags::OBJECT_SIZE_PROPORTIONAL)
+ {
+ nMaxLeft = (mxColumnItem->Count()) * glMinFrame + lNullPix;
+ }
+ else
+ {
+ if(mxColumnItem->Count() > 1)
+ nMaxLeft = mpBorders[mxColumnItem->Count() - 2].nPos + glMinFrame + lNullPix;
+ else
+ nMaxLeft = glMinFrame + lNullPix;
+ }
+ if(mxColumnItem->Count() > 1)
+ nMaxRight = mpBorders[mxColumnItem->Count() - 2].nMaxPos + lNullPix;
+ else
+ nMaxRight -= GetRightIndent() - lNullPix;
+ }
+ }
+ else
+ {
+ nMaxLeft = glMinFrame + lNullPix;
+ if(IsActLastColumn() || mxColumnItem->Count() < 2 ) //If last active column
+ {
+ if(bRTL)
+ {
+ nMaxLeft = glMinFrame + lNullPix + GetMargin2() +
+ GetRightIndent() - std::max(GetFirstLineIndent(),
+ GetLeftIndent());
+ }
+ else
+ {
+ nMaxLeft = glMinFrame + lNullPix + GetMargin2() -
+ GetRightIndent() + std::max(GetFirstLineIndent(),
+ GetLeftIndent());
+ }
+ }
+ if( mxColumnItem->Count() >= 2 )
+ {
+ tools::Long nNewMaxLeft =
+ glMinFrame + lNullPix +
+ mpBorders[mxColumnItem->Count() - 2].nPos +
+ mpBorders[mxColumnItem->Count() - 2].nWidth;
+ nMaxLeft = std::max(nMaxLeft, nNewMaxLeft);
+ }
+
+ }
+ break;
+ }
+ case RulerType::Border:
+ { // Table, column (Modifier)
+ const sal_uInt16 nIdx = GetDragAryPos();
+ switch(GetDragSize())
+ {
+ case RulerDragSize::N1 :
+ {
+ nMaxRight = mpBorders[nIdx].nPos +
+ mpBorders[nIdx].nWidth + lNullPix;
+
+ if(0 == nIdx)
+ nMaxLeft = lNullPix;
+ else
+ nMaxLeft = mpBorders[nIdx - 1].nPos + mpBorders[nIdx - 1].nWidth + lNullPix;
+ if(nIdx == mxColumnItem->GetActColumn())
+ {
+ if(bRTL)
+ {
+ nMaxLeft += mpBorders[nIdx].nPos +
+ GetRightIndent() - std::max(GetFirstLineIndent(),
+ GetLeftIndent());
+ }
+ else
+ {
+ nMaxLeft += mpBorders[nIdx].nPos -
+ GetRightIndent() + std::max(GetFirstLineIndent(),
+ GetLeftIndent());
+ }
+ if(0 != nIdx)
+ nMaxLeft -= mpBorders[nIdx-1].nPos +
+ mpBorders[nIdx-1].nWidth;
+ }
+ nMaxLeft += glMinFrame;
+ nMaxLeft += nDragOffset;
+ break;
+ }
+ case RulerDragSize::Move:
+ {
+ if (mxColumnItem)
+ {
+ //nIdx contains the position of the currently moved item
+ //next visible separator on the left
+ sal_uInt16 nLeftCol=GetActLeftColumn(false, nIdx);
+ //next visible separator on the right
+ sal_uInt16 nRightCol=GetActRightColumn(false, nIdx);
+ //next separator on the left - regardless if visible or not
+ sal_uInt16 nActLeftCol=GetActLeftColumn();
+ //next separator on the right - regardless if visible or not
+ sal_uInt16 nActRightCol=GetActRightColumn();
+ if(mxColumnItem->IsTable())
+ {
+ if(nDragType & SvxRulerDragFlags::OBJECT_ACTLINE_ONLY)
+ {
+ //the current row/column should be modified only
+ //then the next/previous visible border position
+ //marks the min/max positions
+ nMaxLeft = nLeftCol == USHRT_MAX ?
+ 0 :
+ mpBorders[nLeftCol].nPos;
+ //rows can always be increased without a limit
+ if(mxRulerImpl->bIsTableRows)
+ nMaxRight = mpBorders[nIdx].nMaxPos;
+ else
+ nMaxRight = nRightCol == USHRT_MAX ?
+ GetMargin2():
+ mpBorders[nRightCol].nPos;
+ nMaxLeft += lNullPix;
+ nMaxRight += lNullPix;
+ }
+ else
+ {
+ if(SvxRulerDragFlags::OBJECT_SIZE_PROPORTIONAL & nDragType && !bHorz && mxRulerImpl->bIsTableRows)
+ nMaxLeft = (nIdx + 1) * glMinFrame + lNullPix;
+ else
+ nMaxLeft = mpBorders[nIdx].nMinPos + lNullPix;
+ if((SvxRulerDragFlags::OBJECT_SIZE_PROPORTIONAL & nDragType) ||
+ (SvxRulerDragFlags::OBJECT_SIZE_LINEAR & nDragType) )
+ {
+ if(mxRulerImpl->bIsTableRows)
+ {
+ if(bHorz)
+ nMaxRight = GetRightIndent() + lNullPix -
+ (mxColumnItem->Count() - nIdx - 1) * glMinFrame;
+ else
+ nMaxRight = mpBorders[nIdx].nMaxPos + lNullPix;
+ }
+ else
+ nMaxRight=lNullPix+CalcPropMaxRight(nIdx);
+ }
+ else
+ nMaxRight = mpBorders[nIdx].nMaxPos + lNullPix;
+ }
+ nMaxLeft += glMinFrame;
+ nMaxRight -= glMinFrame;
+
+ }
+ else
+ {
+ if(nLeftCol==USHRT_MAX)
+ nMaxLeft=lNullPix;
+ else
+ nMaxLeft = mpBorders[nLeftCol].nPos +
+ mpBorders[nLeftCol].nWidth + lNullPix;
+
+ if(nActRightCol == nIdx)
+ {
+ if(bRTL)
+ {
+ nMaxLeft += mpBorders[nIdx].nPos +
+ GetRightIndent() - std::max(GetFirstLineIndent(),
+ GetLeftIndent());
+ if(nActLeftCol!=USHRT_MAX)
+ nMaxLeft -= mpBorders[nActLeftCol].nPos +
+ mpBorders[nActLeftCol].nWidth;
+ }
+ else
+ {
+ nMaxLeft += mpBorders[nIdx].nPos -
+ GetRightIndent() + std::max(GetFirstLineIndent(),
+ GetLeftIndent());
+ if(nActLeftCol!=USHRT_MAX)
+ nMaxLeft -= mpBorders[nActLeftCol].nPos +
+ mpBorders[nActLeftCol].nWidth;
+ }
+ }
+ nMaxLeft += glMinFrame;
+ nMaxLeft += nDragOffset;
+
+ // nMaxRight
+ // linear / proportional move
+ if((SvxRulerDragFlags::OBJECT_SIZE_PROPORTIONAL & nDragType) ||
+ (SvxRulerDragFlags::OBJECT_SIZE_LINEAR & nDragType) )
+ {
+ nMaxRight=lNullPix+CalcPropMaxRight(nIdx);
+ }
+ else if(SvxRulerDragFlags::OBJECT_SIZE_LINEAR & nDragType)
+ {
+ nMaxRight = lNullPix + GetMargin2() - GetMargin1() +
+ (mpBorders.size() - nIdx - 1) * glMinFrame;
+ }
+ else
+ {
+ if(nRightCol==USHRT_MAX)
+ { // last column
+ nMaxRight = GetMargin2() + lNullPix;
+ if(IsActLastColumn())
+ {
+ if(bRTL)
+ {
+ nMaxRight -=
+ GetMargin2() + GetRightIndent() -
+ std::max(GetFirstLineIndent(),
+ GetLeftIndent());
+ }
+ else
+ {
+ nMaxRight -=
+ GetMargin2() - GetRightIndent() +
+ std::max(GetFirstLineIndent(),
+ GetLeftIndent());
+ }
+ nMaxRight += mpBorders[nIdx].nPos +
+ mpBorders[nIdx].nWidth;
+ }
+ }
+ else
+ {
+ nMaxRight = lNullPix + mpBorders[nRightCol].nPos;
+ sal_uInt16 nNotHiddenRightCol =
+ GetActRightColumn(true, nIdx);
+
+ if( nActLeftCol == nIdx )
+ {
+ tools::Long nBorder = nNotHiddenRightCol ==
+ USHRT_MAX ?
+ GetMargin2() :
+ mpBorders[nNotHiddenRightCol].nPos;
+ if(bRTL)
+ {
+ nMaxRight -= nBorder + GetRightIndent() -
+ std::max(GetFirstLineIndent(),
+ GetLeftIndent());
+ }
+ else
+ {
+ nMaxRight -= nBorder - GetRightIndent() +
+ std::max(GetFirstLineIndent(),
+ GetLeftIndent());
+ }
+ nMaxRight += mpBorders[nIdx].nPos +
+ mpBorders[nIdx].nWidth;
+ }
+ }
+ nMaxRight -= glMinFrame;
+ nMaxRight -= mpBorders[nIdx].nWidth;
+ }
+ }
+ }
+ // ObjectItem
+ else
+ {
+ nMaxLeft = LONG_MIN;
+ nMaxRight = LONG_MAX;
+ }
+ break;
+ }
+ case RulerDragSize::N2:
+ {
+ nMaxLeft = lNullPix + mpBorders[nIdx].nPos;
+ if(nIdx == mxColumnItem->Count()-2) { // last column
+ nMaxRight = GetMargin2() + lNullPix;
+ if(mxColumnItem->IsLastAct()) {
+ nMaxRight -=
+ GetMargin2() - GetRightIndent() +
+ std::max(GetFirstLineIndent(),
+ GetLeftIndent());
+ nMaxRight += mpBorders[nIdx].nPos +
+ mpBorders[nIdx].nWidth;
+ }
+ }
+ else {
+ nMaxRight = lNullPix + mpBorders[nIdx+1].nPos;
+ if(mxColumnItem->GetActColumn()-1 == nIdx) {
+ nMaxRight -= mpBorders[nIdx+1].nPos - GetRightIndent() +
+ std::max(GetFirstLineIndent(),
+ GetLeftIndent());
+ nMaxRight += mpBorders[nIdx].nPos +
+ mpBorders[nIdx].nWidth;
+ }
+ }
+ nMaxRight -= glMinFrame;
+ nMaxRight -= mpBorders[nIdx].nWidth;
+ break;
+ }
+ }
+ nMaxRight += nDragOffset;
+ break;
+ }
+ case RulerType::Indent:
+ {
+ const sal_uInt16 nIdx = GetDragAryPos();
+ switch(nIdx) {
+ case INDENT_FIRST_LINE - INDENT_GAP:
+ case INDENT_LEFT_MARGIN - INDENT_GAP:
+ {
+ if(bRTL)
+ {
+ nMaxLeft = lNullPix + GetRightIndent();
+
+ if(mxColumnItem && !mxColumnItem->IsFirstAct())
+ nMaxLeft += mpBorders[mxColumnItem->GetActColumn()-1].nPos +
+ mpBorders[mxColumnItem->GetActColumn()-1].nWidth;
+ nMaxRight = lNullPix + GetMargin2();
+
+ // Dragging along
+ if((INDENT_FIRST_LINE - INDENT_GAP) != nIdx &&
+ !(nDragType & SvxRulerDragFlags::OBJECT_LEFT_INDENT_ONLY))
+ {
+ if(GetLeftIndent() > GetFirstLineIndent())
+ nMaxLeft += GetLeftIndent() - GetFirstLineIndent();
+ else
+ nMaxRight -= GetFirstLineIndent() - GetLeftIndent();
+ }
+ }
+ else
+ {
+ nMaxLeft = lNullPix;
+
+ if(mxColumnItem && !mxColumnItem->IsFirstAct())
+ nMaxLeft += mpBorders[mxColumnItem->GetActColumn()-1].nPos +
+ mpBorders[mxColumnItem->GetActColumn()-1].nWidth;
+ nMaxRight = lNullPix + GetRightIndent() - glMinFrame;
+
+ // Dragging along
+ if((INDENT_FIRST_LINE - INDENT_GAP) != nIdx &&
+ !(nDragType & SvxRulerDragFlags::OBJECT_LEFT_INDENT_ONLY))
+ {
+ if(GetLeftIndent() > GetFirstLineIndent())
+ nMaxLeft += GetLeftIndent() - GetFirstLineIndent();
+ else
+ nMaxRight -= GetFirstLineIndent() - GetLeftIndent();
+ }
+ }
+ }
+ break;
+ case INDENT_RIGHT_MARGIN - INDENT_GAP:
+ {
+ if(bRTL)
+ {
+ nMaxLeft = lNullPix;
+ nMaxRight = lNullPix + std::min(GetFirstLineIndent(), GetLeftIndent()) - glMinFrame;
+ if (mxColumnItem)
+ {
+ sal_uInt16 nRightCol=GetActRightColumn( true );
+ if(!IsActLastColumn( true ))
+ nMaxRight += mpBorders[nRightCol].nPos;
+ else
+ nMaxRight += GetMargin2();
+ }
+ else
+ {
+ nMaxLeft += GetMargin1();
+ }
+ nMaxLeft += glMinFrame;
+ }
+ else
+ {
+ nMaxLeft = lNullPix +
+ std::max(GetFirstLineIndent(), GetLeftIndent());
+ nMaxRight = lNullPix;
+ if (mxColumnItem)
+ {
+ sal_uInt16 nRightCol=GetActRightColumn( true );
+ if(!IsActLastColumn( true ))
+ nMaxRight += mpBorders[nRightCol].nPos;
+ else
+ nMaxRight += GetMargin2();
+ }
+ else
+ nMaxRight += GetMargin2();
+ nMaxLeft += glMinFrame;
+ }
+ }
+ break;
+ }
+ break;
+ }
+ case RulerType::Tab: // Tabs (Modifier)
+ /* left = NOf + Max(LAR, EZ)
+ right = NOf + RAR */
+
+ if (bRTL)
+ nMaxLeft = lNullPix + GetRightIndent();
+ else
+ nMaxLeft = lNullPix + std::min(GetFirstLineIndent(), GetLeftIndent());
+
+ mxRulerImpl->lMaxRightLogic = GetLogicRightIndent() + lLogicNullOffset;
+ nMaxRight = ConvertSizePixel(mxRulerImpl->lMaxRightLogic);
+ break;
+ default: ; //prevent warning
+ }
+}
+
+bool SvxRuler::StartDrag()
+{
+ /*
+ Beginning of a drag operation (SV-handler) evaluates modifier and
+ calculated values
+
+ [Cross-reference]
+
+ <SvxRuler::EvalModifier()>
+ <SvxRuler::CalcMinMax()>
+ <SvxRuler::EndDrag()>
+ */
+ bool bContentProtected = mxRulerImpl->aProtectItem->IsContentProtected();
+
+ if(!bValid)
+ return false;
+
+ mxRulerImpl->lLastLMargin = GetMargin1();
+ mxRulerImpl->lLastRMargin = GetMargin2();
+
+ bool bOk = true;
+
+ lInitialDragPos = GetDragPos();
+ switch(GetDragType())
+ {
+ case RulerType::Margin1: // left edge of the surrounding Frame
+ case RulerType::Margin2: // right edge of the surrounding Frame
+ if((bHorz && mxLRSpaceItem) || (!bHorz && mxULSpaceItem))
+ {
+ if (!mxColumnItem)
+ EvalModifier();
+ else
+ nDragType = SvxRulerDragFlags::OBJECT;
+ }
+ else
+ {
+ bOk = false;
+ }
+ break;
+ case RulerType::Border: // Table, column (Modifier)
+ if (mxColumnItem)
+ {
+ nDragOffset = 0;
+ if (!mxColumnItem->IsTable())
+ nDragOffset = GetDragPos() - mpBorders[GetDragAryPos()].nPos;
+ EvalModifier();
+ }
+ else
+ nDragOffset = 0;
+ break;
+ case RulerType::Indent: // Paragraph indents (Modifier)
+ {
+ if( bContentProtected )
+ return false;
+ if(INDENT_LEFT_MARGIN == GetDragAryPos() + INDENT_GAP) { // Left paragraph indent
+ mpIndents[0] = mpIndents[INDENT_FIRST_LINE];
+ EvalModifier();
+ }
+ else
+ {
+ nDragType = SvxRulerDragFlags::OBJECT;
+ }
+ mpIndents[1] = mpIndents[GetDragAryPos() + INDENT_GAP];
+ break;
+ }
+ case RulerType::Tab: // Tabs (Modifier)
+ if( bContentProtected )
+ return false;
+ EvalModifier();
+ mpTabs[0] = mpTabs[GetDragAryPos() + 1];
+ mpTabs[0].nStyle |= RULER_STYLE_DONTKNOW;
+ break;
+ default:
+ nDragType = SvxRulerDragFlags::NONE;
+ }
+
+ if(bOk)
+ CalcMinMax();
+
+ return bOk;
+}
+
+void SvxRuler::Drag()
+{
+ /* SV-Draghandler */
+ if(IsDragCanceled())
+ {
+ Ruler::Drag();
+ return;
+ }
+ switch(GetDragType()) {
+ case RulerType::Margin1: // left edge of the surrounding Frame
+ DragMargin1();
+ mxRulerImpl->lLastLMargin = GetMargin1();
+ break;
+ case RulerType::Margin2: // right edge of the surrounding Frame
+ DragMargin2();
+ mxRulerImpl->lLastRMargin = GetMargin2();
+ break;
+ case RulerType::Indent: // Paragraph indents
+ DragIndents();
+ break;
+ case RulerType::Border: // Table, columns
+ if (mxColumnItem)
+ DragBorders();
+ else if (mxObjectItem)
+ DragObjectBorder();
+ break;
+ case RulerType::Tab: // Tabs
+ DragTabs();
+ break;
+ default:
+ break; //prevent warning
+ }
+ Ruler::Drag();
+}
+
+void SvxRuler::EndDrag()
+{
+ /*
+ SV-handler; is called when ending the dragging. Triggers the updating of data
+ on the application, by calling the respective Apply...() methods to send the
+ data to the application.
+ */
+ const bool bUndo = IsDragCanceled();
+ const tools::Long lPos = GetDragPos();
+ DrawLine_Impl(lTabPos, 6, bHorz);
+ lTabPos = -1;
+
+ if(!bUndo)
+ {
+ switch(GetDragType())
+ {
+ case RulerType::Margin1: // upper left edge of the surrounding Frame
+ case RulerType::Margin2: // lower right edge of the surrounding Frame
+ {
+ if (!mxColumnItem || !mxColumnItem->IsTable())
+ ApplyMargins();
+
+ if(mxColumnItem &&
+ (mxColumnItem->IsTable() ||
+ (nDragType & SvxRulerDragFlags::OBJECT_SIZE_PROPORTIONAL)))
+ ApplyBorders();
+
+ }
+ break;
+ case RulerType::Border: // Table, columns
+ if(lInitialDragPos != lPos ||
+ (mxRulerImpl->bIsTableRows && bHorz)) //special case - the null offset is changed here
+ {
+ if (mxColumnItem)
+ {
+ ApplyBorders();
+ if(bHorz)
+ UpdateTabs();
+ }
+ else if (mxObjectItem)
+ ApplyObject();
+ }
+ break;
+ case RulerType::Indent: // Paragraph indents
+ if(lInitialDragPos != lPos)
+ ApplyIndents();
+ SetIndents(INDENT_COUNT, mpIndents.data() + INDENT_GAP);
+ break;
+ case RulerType::Tab: // Tabs
+ {
+ ApplyTabs();
+ mpTabs[GetDragAryPos()].nStyle &= ~RULER_STYLE_INVISIBLE;
+ SetTabs(nTabCount, mpTabs.data() + TAB_GAP);
+ }
+ break;
+ default:
+ break; //prevent warning
+ }
+ }
+ nDragType = SvxRulerDragFlags::NONE;
+
+ mbCoarseSnapping = false;
+ mbSnapping = true;
+
+ Ruler::EndDrag();
+ if(bUndo)
+ {
+ for(sal_uInt16 i = 0; i < mxRulerImpl->nControllerItems; i++)
+ {
+ pCtrlItems[i]->ClearCache();
+ pCtrlItems[i]->GetBindings().Invalidate(pCtrlItems[i]->GetId());
+ }
+ }
+}
+
+void SvxRuler::ExtraDown()
+{
+ /* Override SV method, sets the new type for the Default tab. */
+
+ // Switch Tab Type
+ if(mxTabStopItem &&
+ (nFlags & SvxRulerSupportFlags::TABS) == SvxRulerSupportFlags::TABS)
+ {
+ ++nDefTabType;
+ if(RULER_TAB_DEFAULT == nDefTabType)
+ nDefTabType = RULER_TAB_LEFT;
+ SetExtraType(RulerExtra::Tab, nDefTabType);
+ }
+ Ruler::ExtraDown();
+}
+
+void SvxRuler::Notify(SfxBroadcaster&, const SfxHint& rHint)
+{
+ /*
+ Report through the bindings that the status update is completed. The ruler
+ updates its appearance and gets registered again in the bindings.
+ */
+
+ // start update
+ if (bActive && rHint.GetId() == SfxHintId::UpdateDone)
+ {
+ Update();
+ EndListening(*pBindings);
+ bValid = true;
+ bListening = false;
+ }
+}
+
+void SvxRuler::MenuSelect(std::string_view ident)
+{
+ if (ident.empty())
+ return;
+ /* Handler of the context menus for switching the unit of measurement */
+ SetUnit(vcl::EnglishStringToMetric(ident));
+}
+
+void SvxRuler::TabMenuSelect(std::string_view rIdent)
+{
+ if (rIdent.empty())
+ return;
+ sal_Int32 nId = o3tl::toInt32(rIdent);
+ /* Handler of the tab menu for setting the type */
+ if (mxTabStopItem && mxTabStopItem->Count() > mxRulerImpl->nIdx)
+ {
+ SvxTabStop aTabStop = mxTabStopItem->At(mxRulerImpl->nIdx);
+ aTabStop.GetAdjustment() = ToAttrTab_Impl(nId - 1);
+ mxTabStopItem->Remove(mxRulerImpl->nIdx);
+ mxTabStopItem->Insert(aTabStop);
+ sal_uInt16 nTabStopId = bHorz ? SID_ATTR_TABSTOP : SID_ATTR_TABSTOP_VERTICAL;
+ pBindings->GetDispatcher()->ExecuteList(nTabStopId,
+ SfxCallMode::RECORD, { mxTabStopItem.get() });
+ UpdateTabs();
+ mxRulerImpl->nIdx = 0;
+ }
+}
+
+const TranslateId RID_SVXSTR_RULER_TAB[] =
+{
+ RID_SVXSTR_RULER_TAB_LEFT,
+ RID_SVXSTR_RULER_TAB_RIGHT,
+ RID_SVXSTR_RULER_TAB_CENTER,
+ RID_SVXSTR_RULER_TAB_DECIMAL
+};
+
+void SvxRuler::Command( const CommandEvent& rCommandEvent )
+{
+ /* Mouse context menu for switching the unit of measurement */
+ if ( CommandEventId::ContextMenu == rCommandEvent.GetCommand() )
+ {
+ CancelDrag();
+
+ tools::Rectangle aRect(rCommandEvent.GetMousePosPixel(), Size(1, 1));
+ weld::Window* pPopupParent = weld::GetPopupParent(*this, aRect);
+ std::unique_ptr<weld::Builder> xBuilder(Application::CreateBuilder(pPopupParent, "svx/ui/rulermenu.ui"));
+ std::unique_ptr<weld::Menu> xMenu(xBuilder->weld_menu("menu"));
+
+ bool bRTL = mxRulerImpl->pTextRTLItem && mxRulerImpl->pTextRTLItem->GetValue();
+ if ( !mpTabs.empty() &&
+ RulerType::Tab ==
+ GetRulerType( rCommandEvent.GetMousePosPixel(), &mxRulerImpl->nIdx ) &&
+ mpTabs[mxRulerImpl->nIdx + TAB_GAP].nStyle < RULER_TAB_DEFAULT )
+ {
+ xMenu->clear();
+
+ const Size aSz(ruler_tab_svx.width + 2, ruler_tab_svx.height + 2);
+ const Point aPt(aSz.Width() / 2, aSz.Height() / 2);
+
+ for ( sal_uInt16 i = RULER_TAB_LEFT; i < RULER_TAB_DEFAULT; ++i )
+ {
+ ScopedVclPtr<VirtualDevice> xDev(pPopupParent->create_virtual_device());
+ xDev->SetOutputSize(aSz);
+
+ sal_uInt16 nStyle = bRTL ? i|RULER_TAB_RTL : i;
+ nStyle |= static_cast<sal_uInt16>(bHorz ? WB_HORZ : WB_VERT);
+
+ Color aFillColor(xDev->GetSettings().GetStyleSettings().GetShadowColor());
+ DrawTab(*xDev, aFillColor, aPt, nStyle);
+
+ OString sId(OString::number(i + 1));
+ xMenu->insert(-1, OUString::fromUtf8(sId), SvxResId(RID_SVXSTR_RULER_TAB[i]),
+ nullptr, xDev.get(), nullptr, TRISTATE_TRUE);
+ xMenu->set_active(sId, i == mpTabs[mxRulerImpl->nIdx + TAB_GAP].nStyle);
+ }
+ TabMenuSelect(xMenu->popup_at_rect(pPopupParent, aRect));
+ }
+ else
+ {
+ FieldUnit eUnit = GetUnit();
+ const int nCount = xMenu->n_children();
+
+ bool bReduceMetric = bool(nFlags & SvxRulerSupportFlags::REDUCED_METRIC);
+ for ( sal_uInt16 i = nCount; i; --i )
+ {
+ OString sIdent = xMenu->get_id(i - 1);
+ FieldUnit eMenuUnit = vcl::EnglishStringToMetric(sIdent);
+ xMenu->set_active(sIdent, eMenuUnit == eUnit);
+ if( bReduceMetric )
+ {
+ if (eMenuUnit == FieldUnit::M ||
+ eMenuUnit == FieldUnit::KM ||
+ eMenuUnit == FieldUnit::FOOT ||
+ eMenuUnit == FieldUnit::MILE)
+ {
+ xMenu->remove(sIdent);
+ }
+ else if (( eMenuUnit == FieldUnit::CHAR ) && !bHorz )
+ {
+ xMenu->remove(sIdent);
+ }
+ else if (( eMenuUnit == FieldUnit::LINE ) && bHorz )
+ {
+ xMenu->remove(sIdent);
+ }
+ }
+ }
+ MenuSelect(xMenu->popup_at_rect(pPopupParent, aRect));
+ }
+ }
+ else
+ {
+ Ruler::Command( rCommandEvent );
+ }
+}
+
+sal_uInt16 SvxRuler::GetActRightColumn(
+ bool bForceDontConsiderHidden,
+ sal_uInt16 nAct ) const
+{
+ if( nAct == USHRT_MAX )
+ nAct = mxColumnItem->GetActColumn();
+ else
+ nAct++; //To be able to pass on the ActDrag
+
+ bool bConsiderHidden = !bForceDontConsiderHidden &&
+ !(nDragType & SvxRulerDragFlags::OBJECT_ACTLINE_ONLY);
+
+ while( nAct < mxColumnItem->Count() - 1 )
+ {
+ if (mxColumnItem->At(nAct).bVisible || bConsiderHidden)
+ return nAct;
+ else
+ nAct++;
+ }
+ return USHRT_MAX;
+}
+
+sal_uInt16 SvxRuler::GetActLeftColumn(
+ bool bForceDontConsiderHidden,
+ sal_uInt16 nAct ) const
+{
+ if(nAct == USHRT_MAX)
+ nAct = mxColumnItem->GetActColumn();
+
+ sal_uInt16 nLeftOffset = 1;
+
+ bool bConsiderHidden = !bForceDontConsiderHidden &&
+ !(nDragType & SvxRulerDragFlags::OBJECT_ACTLINE_ONLY);
+
+ while(nAct >= nLeftOffset)
+ {
+ if (mxColumnItem->At(nAct - nLeftOffset).bVisible || bConsiderHidden)
+ return nAct - nLeftOffset;
+ else
+ nLeftOffset++;
+ }
+ return USHRT_MAX;
+}
+
+bool SvxRuler::IsActLastColumn(
+ bool bForceDontConsiderHidden,
+ sal_uInt16 nAct) const
+{
+ return GetActRightColumn(bForceDontConsiderHidden, nAct) == USHRT_MAX;
+}
+
+bool SvxRuler::IsActFirstColumn(
+ bool bForceDontConsiderHidden,
+ sal_uInt16 nAct) const
+{
+ return GetActLeftColumn(bForceDontConsiderHidden, nAct) == USHRT_MAX;
+}
+
+tools::Long SvxRuler::CalcPropMaxRight(sal_uInt16 nCol) const
+{
+
+ if(!(nDragType & SvxRulerDragFlags::OBJECT_SIZE_LINEAR))
+ {
+ // Remove the minimum width for all affected columns
+ // starting from the right edge
+ tools::Long _nMaxRight = GetMargin2() - GetMargin1();
+
+ tools::Long lFences = 0;
+ tools::Long lMinSpace = USHRT_MAX;
+ tools::Long lOldPos;
+ tools::Long lColumns = 0;
+
+ sal_uInt16 nStart;
+ if(!mxColumnItem->IsTable())
+ {
+ if(nCol == USHRT_MAX)
+ {
+ lOldPos = GetMargin1();
+ nStart = 0;
+ }
+ else
+ {
+ lOldPos = mpBorders[nCol].nPos + mpBorders[nCol].nWidth;
+ nStart = nCol + 1;
+ lFences = mpBorders[nCol].nWidth;
+ }
+
+ for(size_t i = nStart; i < mpBorders.size() - 1; ++i)
+ {
+ tools::Long lWidth = mpBorders[i].nPos - lOldPos;
+ lColumns += lWidth;
+ if(lWidth < lMinSpace)
+ lMinSpace = lWidth;
+ lOldPos = mpBorders[i].nPos + mpBorders[i].nWidth;
+ lFences += mpBorders[i].nWidth;
+ }
+ tools::Long lWidth = GetMargin2() - lOldPos;
+ lColumns += lWidth;
+ if(lWidth < lMinSpace)
+ lMinSpace = lWidth;
+ }
+ else
+ {
+ sal_uInt16 nActCol;
+ if(nCol == USHRT_MAX) //CalcMinMax for LeftMargin
+ {
+ lOldPos = GetMargin1();
+ }
+ else
+ {
+ lOldPos = mpBorders[nCol].nPos;
+ }
+ lColumns = GetMargin2()-lOldPos;
+ nActCol = nCol;
+ lFences = 0;
+ while(nActCol < mpBorders.size() || nActCol == USHRT_MAX)
+ {
+ sal_uInt16 nRight;
+ if(nActCol == USHRT_MAX)
+ {
+ nRight = 0;
+ while (!(*mxColumnItem)[nRight].bVisible)
+ {
+ nRight++;
+ }
+ }
+ else
+ {
+ nRight = GetActRightColumn(false, nActCol);
+ }
+
+ tools::Long lWidth;
+ if(nRight != USHRT_MAX)
+ {
+ lWidth = mpBorders[nRight].nPos - lOldPos;
+ lOldPos = mpBorders[nRight].nPos;
+ }
+ else
+ {
+ lWidth=GetMargin2() - lOldPos;
+ }
+ nActCol = nRight;
+ if(lWidth < lMinSpace)
+ lMinSpace = lWidth;
+ if(nActCol == USHRT_MAX)
+ break;
+ }
+ }
+
+ _nMaxRight -= static_cast<tools::Long>(lFences + glMinFrame / static_cast<float>(lMinSpace) * lColumns);
+ return _nMaxRight;
+ }
+ else
+ {
+ if(mxColumnItem->IsTable())
+ {
+ sal_uInt16 nVisCols = 0;
+ for(size_t i = GetActRightColumn(false, nCol); i < mpBorders.size();)
+ {
+ if ((*mxColumnItem)[i].bVisible)
+ nVisCols++;
+ i = GetActRightColumn(false, i);
+ }
+ return GetMargin2() - GetMargin1() - (nVisCols + 1) * glMinFrame;
+ }
+ else
+ {
+ tools::Long lWidth = 0;
+ for(size_t i = nCol; i < mpBorders.size() - 1; i++)
+ {
+ lWidth += glMinFrame + mpBorders[i].nWidth;
+ }
+ return GetMargin2() - GetMargin1() - lWidth;
+ }
+ }
+}
+
+// Tab stops relative to indent (#i24363#)
+void SvxRuler::SetTabsRelativeToIndent( bool bRel )
+{
+ mxRulerImpl->bIsTabsRelativeToIndent = bRel;
+}
+
+void SvxRuler::SetValues(RulerChangeType type, tools::Long diffValue)
+{
+ if (diffValue == 0)
+ return;
+
+ if (type == RulerChangeType::MARGIN1)
+ AdjustMargin1(diffValue);
+ else if (type == RulerChangeType::MARGIN2)
+ SetMargin2( GetMargin2() - diffValue);
+ ApplyMargins();
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab cinoptions=b1,g0,N-s cinkeys+=0=break: */
diff --git a/svx/source/dialog/swframeexample.cxx b/svx/source/dialog/swframeexample.cxx
new file mode 100644
index 000000000..a90780a5b
--- /dev/null
+++ b/svx/source/dialog/swframeexample.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 <vcl/metric.hxx>
+#include <vcl/outdev.hxx>
+#include <vcl/svapp.hxx>
+#include <vcl/settings.hxx>
+#include <svtools/colorcfg.hxx>
+#include <svx/swframeexample.hxx>
+#include <i18nlangtag/languagetag.hxx>
+#include <com/sun/star/text/HoriOrientation.hpp>
+#include <com/sun/star/text/VertOrientation.hpp>
+#include <com/sun/star/text/RelOrientation.hpp>
+
+using namespace ::com::sun::star::text;
+
+#define FLYINFLY_BORDER 3
+constexpr OUStringLiteral DEMOTEXT = u"Ij";
+
+namespace {
+
+void DrawRect_Impl(vcl::RenderContext& rRenderContext, const tools::Rectangle &rRect,
+ const Color &rFillColor, const Color &rLineColor)
+{
+ rRenderContext.SetFillColor(rFillColor);
+ rRenderContext.SetLineColor(rLineColor);
+ rRenderContext.DrawRect(rRect);
+}
+
+}
+
+SwFrameExample::SwFrameExample()
+ : nHAlign(HoriOrientation::CENTER)
+ , nHRel(RelOrientation::FRAME)
+ , nVAlign(VertOrientation::TOP)
+ , nVRel(RelOrientation::PRINT_AREA)
+ , nWrap(WrapTextMode_NONE)
+ , nAnchor(RndStdIds::FLY_AT_PAGE)
+ , bTrans(false)
+ , aRelPos(Point(0,0))
+{
+ InitColors_Impl();
+}
+
+void SwFrameExample::SetDrawingArea(weld::DrawingArea* pDrawingArea)
+{
+ CustomWidgetController::SetDrawingArea(pDrawingArea);
+ pDrawingArea->set_size_request(pDrawingArea->get_approximate_digit_width() * 16,
+ pDrawingArea->get_text_height() * 12);
+}
+
+void SwFrameExample::InitColors_Impl()
+{
+ const StyleSettings& rSettings = Application::GetSettings().GetStyleSettings();
+ m_aBgCol = rSettings.GetWindowColor();
+
+ bool bHC = rSettings.GetHighContrastMode();
+
+ m_aFrameColor = COL_LIGHTGREEN;
+ m_aAlignColor = COL_LIGHTRED;
+ m_aTransColor = COL_TRANSPARENT;
+
+ m_aTxtCol = bHC?
+ svtools::ColorConfig().GetColorValue(svtools::FONTCOLOR).nColor :
+ COL_GRAY;
+ m_aPrintAreaCol = bHC? m_aTxtCol : COL_GRAY;
+ m_aBorderCol = m_aTxtCol;
+ m_aBlankCol = bHC? m_aTxtCol : COL_LIGHTGRAY;
+ m_aBlankFrameCol = bHC? m_aTxtCol : COL_GRAY;
+}
+
+void SwFrameExample::StyleUpdated()
+{
+ InitColors_Impl();
+ CustomWidgetController::StyleUpdated();
+}
+
+void SwFrameExample::InitAllRects_Impl(vcl::RenderContext& rRenderContext)
+{
+ aPage.SetSize(GetOutputSizePixel());
+
+ sal_uInt32 nOutWPix = aPage.GetWidth();
+ sal_uInt32 nOutHPix = aPage.GetHeight();
+
+ // PrintArea
+ sal_uInt32 nLBorder;
+ sal_uInt32 nRBorder;
+ sal_uInt32 nTBorder;
+ sal_uInt32 nBBorder;
+
+ sal_uInt32 nLTxtBorder;
+ sal_uInt32 nRTxtBorder;
+ sal_uInt32 nTTxtBorder;
+ sal_uInt32 nBTxtBorder;
+
+ if (nAnchor != RndStdIds::FLY_AS_CHAR)
+ {
+ nLBorder = 14;
+ nRBorder = 10;
+ nTBorder = 10;
+ nBBorder = 15;
+
+ nLTxtBorder = 8;
+ nRTxtBorder = 4;
+ nTTxtBorder = 2;
+ nBTxtBorder = 2;
+ }
+ else
+ {
+ nLBorder = 2;
+ nRBorder = 2;
+ nTBorder = 2;
+ nBBorder = 2;
+
+ nLTxtBorder = 2;
+ nRTxtBorder = 2;
+ nTTxtBorder = 2;
+ nBTxtBorder = 2;
+ }
+ aPagePrtArea = tools::Rectangle(Point(nLBorder, nTBorder), Point((nOutWPix - 1) - nRBorder, (nOutHPix - 1) - nBBorder));
+
+ // Example text: Preparing for the text output
+ // A line of text
+ aTextLine = aPagePrtArea;
+ aTextLine.SetSize(Size(aTextLine.GetWidth(), 2));
+ aTextLine.AdjustLeft(nLTxtBorder );
+ aTextLine.AdjustRight( -sal_Int32(nRTxtBorder) );
+ aTextLine.Move(0, nTTxtBorder);
+
+ // Rectangle to edges including paragraph
+ sal_uInt16 nLines = static_cast<sal_uInt16>((aPagePrtArea.GetHeight() / 2 - nTTxtBorder - nBTxtBorder)
+ / (aTextLine.GetHeight() + 2));
+ aPara = aPagePrtArea;
+ aPara.SetSize(Size(aPara.GetWidth(),
+ (aTextLine.GetHeight() + 2) * nLines + nTTxtBorder + nBTxtBorder));
+
+ // Rectangle around paragraph without borders
+ aParaPrtArea = aPara;
+ aParaPrtArea.AdjustLeft(nLTxtBorder );
+ aParaPrtArea.AdjustRight( -sal_Int32(nRTxtBorder) );
+ aParaPrtArea.AdjustTop(nTTxtBorder );
+ aParaPrtArea.AdjustBottom( -sal_Int32(nBTxtBorder) );
+
+ if (nAnchor == RndStdIds::FLY_AS_CHAR || nAnchor == RndStdIds::FLY_AT_CHAR)
+ {
+ vcl::Font aFont = OutputDevice::GetDefaultFont(
+ DefaultFontType::LATIN_TEXT, Application::GetSettings().GetLanguageTag().getLanguageType(),
+ GetDefaultFontFlags::OnlyOne, &rRenderContext );
+ aFont.SetColor( m_aTxtCol );
+ aFont.SetFillColor( m_aBgCol );
+ aFont.SetWeight(WEIGHT_NORMAL);
+
+ if (nAnchor == RndStdIds::FLY_AS_CHAR)
+ {
+ aFont.SetFontSize(Size(0, aParaPrtArea.GetHeight() - 2));
+ rRenderContext.SetFont(aFont);
+ aParaPrtArea.SetSize(Size(rRenderContext.GetTextWidth(DEMOTEXT), rRenderContext.GetTextHeight()));
+ }
+ else
+ {
+ aFont.SetFontSize(Size(0, aParaPrtArea.GetHeight() / 2));
+ rRenderContext.SetFont(aFont);
+ aAutoCharFrame.SetSize(Size(rRenderContext.GetTextWidth(OUString('A')), GetTextHeight()));
+ aAutoCharFrame.SetPos(Point(aParaPrtArea.Left() + (aParaPrtArea.GetWidth() - aAutoCharFrame.GetWidth()) / 2,
+ aParaPrtArea.Top() + (aParaPrtArea.GetHeight() - aAutoCharFrame.GetHeight()) / 2));
+ }
+ }
+
+ // Inner Frame anchored at the Frame
+ aFrameAtFrame = aPara;
+ aFrameAtFrame.AdjustLeft(9 );
+ aFrameAtFrame.AdjustRight( -5 );
+ aFrameAtFrame.AdjustBottom(5 );
+ aFrameAtFrame.SetPos(Point(aFrameAtFrame.Left() + 2, (aPagePrtArea.Bottom() - aFrameAtFrame.GetHeight()) / 2 + 5));
+
+ // Size of the frame to be positioned
+ if (nAnchor != RndStdIds::FLY_AS_CHAR)
+ {
+ sal_uInt32 nLFBorder = nAnchor == RndStdIds::FLY_AT_PAGE ? nLBorder : nLTxtBorder;
+ sal_uInt32 nRFBorder = nAnchor == RndStdIds::FLY_AT_PAGE ? nRBorder : nRTxtBorder;
+
+ switch (nHRel)
+ {
+ case RelOrientation::PAGE_LEFT:
+ case RelOrientation::FRAME_LEFT:
+ aFrmSize = Size(nLFBorder - 4, (aTextLine.GetHeight() + 2) * 3);
+ break;
+
+ case RelOrientation::PAGE_RIGHT:
+ case RelOrientation::FRAME_RIGHT:
+ aFrmSize = Size(nRFBorder - 4, (aTextLine.GetHeight() + 2) * 3);
+ break;
+
+ default:
+ aFrmSize = Size(nLBorder - 3, (aTextLine.GetHeight() + 2) * 3);
+ break;
+ }
+ aFrmSize.setWidth( std::max(tools::Long(5), aFrmSize.Width()) );
+ aFrmSize.setHeight( std::max(tools::Long(5), aFrmSize.Height()) );
+ }
+ else
+ {
+ sal_uInt32 nFreeWidth = aPagePrtArea.GetWidth() - rRenderContext.GetTextWidth(DEMOTEXT);
+
+ aFrmSize = Size(nFreeWidth / 2, (aTextLine.GetHeight() + 2) * 3);
+ aDrawObj.SetSize(Size(std::max(tools::Long(5), tools::Long(nFreeWidth / 3)), std::max(tools::Long(5), aFrmSize.Height() * 3)));
+ aDrawObj.SetPos(Point(aParaPrtArea.Right() + 1, aParaPrtArea.Bottom() / 2));
+ aParaPrtArea.SetRight( aDrawObj.Right() );
+ }
+}
+
+void SwFrameExample::CalcBoundRect_Impl(const vcl::RenderContext& rRenderContext, tools::Rectangle &rRect)
+{
+ switch (nAnchor)
+ {
+ case RndStdIds::FLY_AT_PAGE:
+ {
+ switch (nHRel)
+ {
+ case RelOrientation::FRAME:
+ case RelOrientation::PAGE_FRAME:
+ rRect.SetLeft( aPage.Left() );
+ rRect.SetRight( aPage.Right() );
+ break;
+
+ case RelOrientation::PRINT_AREA:
+ case RelOrientation::PAGE_PRINT_AREA:
+ rRect.SetLeft( aPagePrtArea.Left() );
+ rRect.SetRight( aPagePrtArea.Right() );
+ break;
+
+ case RelOrientation::PAGE_LEFT:
+ rRect.SetLeft( aPage.Left() );
+ rRect.SetRight( aPagePrtArea.Left() );
+ break;
+
+ case RelOrientation::PAGE_RIGHT:
+ rRect.SetLeft( aPagePrtArea.Right() );
+ rRect.SetRight( aPage.Right() );
+ break;
+ }
+
+ switch (nVRel)
+ {
+ case RelOrientation::PRINT_AREA:
+ case RelOrientation::PAGE_PRINT_AREA:
+ rRect.SetTop( aPagePrtArea.Top() );
+ rRect.SetBottom( aPagePrtArea.Bottom() );
+ break;
+
+ case RelOrientation::FRAME:
+ case RelOrientation::PAGE_FRAME:
+ rRect.SetTop( aPage.Top() );
+ rRect.SetBottom( aPage.Bottom() );
+ break;
+ }
+ }
+ break;
+
+ case RndStdIds::FLY_AT_FLY:
+ {
+ switch (nHRel)
+ {
+ case RelOrientation::FRAME:
+ case RelOrientation::PAGE_FRAME:
+ rRect.SetLeft( aFrameAtFrame.Left() );
+ rRect.SetRight( aFrameAtFrame.Right() );
+ break;
+
+ case RelOrientation::PRINT_AREA:
+ case RelOrientation::PAGE_PRINT_AREA:
+ rRect.SetLeft( aFrameAtFrame.Left() + FLYINFLY_BORDER );
+ rRect.SetRight( aFrameAtFrame.Right() - FLYINFLY_BORDER );
+ break;
+
+ case RelOrientation::PAGE_LEFT:
+ rRect.SetLeft( aFrameAtFrame.Left() );
+ rRect.SetRight( aFrameAtFrame.Left() + FLYINFLY_BORDER );
+ break;
+
+ case RelOrientation::PAGE_RIGHT:
+ rRect.SetLeft( aFrameAtFrame.Right() );
+ rRect.SetRight( aFrameAtFrame.Right() - FLYINFLY_BORDER );
+ break;
+ }
+
+ switch (nVRel)
+ {
+ case RelOrientation::FRAME:
+ case RelOrientation::PAGE_FRAME:
+ rRect.SetTop( aFrameAtFrame.Top() );
+ rRect.SetBottom( aFrameAtFrame.Bottom() );
+ break;
+
+ case RelOrientation::PRINT_AREA:
+ case RelOrientation::PAGE_PRINT_AREA:
+ rRect.SetTop( aFrameAtFrame.Top() + FLYINFLY_BORDER );
+ rRect.SetBottom( aFrameAtFrame.Bottom() - FLYINFLY_BORDER );
+ break;
+ }
+ }
+ break;
+ case RndStdIds::FLY_AT_PARA:
+ case RndStdIds::FLY_AT_CHAR:
+ {
+ switch (nHRel)
+ {
+ case RelOrientation::FRAME:
+ rRect.SetLeft( aPara.Left() );
+ rRect.SetRight( aPara.Right() );
+ break;
+
+ case RelOrientation::PRINT_AREA:
+ rRect.SetLeft( aParaPrtArea.Left() );
+ rRect.SetRight( aParaPrtArea.Right() );
+ break;
+
+ case RelOrientation::PAGE_LEFT:
+ rRect.SetLeft( aPage.Left() );
+ rRect.SetRight( aPagePrtArea.Left() );
+ break;
+
+ case RelOrientation::PAGE_RIGHT:
+ rRect.SetLeft( aPagePrtArea.Right() );
+ rRect.SetRight( aPage.Right() );
+ break;
+
+ case RelOrientation::PAGE_FRAME:
+ rRect.SetLeft( aPage.Left() );
+ rRect.SetRight( aPage.Right() );
+ break;
+
+ case RelOrientation::PAGE_PRINT_AREA:
+ rRect.SetLeft( aPagePrtArea.Left() );
+ rRect.SetRight( aPagePrtArea.Right() );
+ break;
+
+ case RelOrientation::FRAME_LEFT:
+ rRect.SetLeft( aPara.Left() );
+ rRect.SetRight( aParaPrtArea.Left() );
+ break;
+
+ case RelOrientation::FRAME_RIGHT:
+ rRect.SetLeft( aParaPrtArea.Right() );
+ rRect.SetRight( aPara.Right() );
+ break;
+
+ case RelOrientation::CHAR:
+ rRect.SetLeft( aAutoCharFrame.Left() );
+ rRect.SetRight( aAutoCharFrame.Left() );
+ break;
+ }
+
+ switch (nVRel)
+ {
+ case RelOrientation::FRAME:
+ rRect.SetTop( aPara.Top() );
+ rRect.SetBottom( aPara.Bottom() );
+ break;
+
+ case RelOrientation::PRINT_AREA:
+ rRect.SetTop( aParaPrtArea.Top() );
+ rRect.SetBottom( aParaPrtArea.Bottom() );
+ break;
+
+ case RelOrientation::CHAR:
+ if (nVAlign != VertOrientation::NONE &&
+ nVAlign != VertOrientation::CHAR_BOTTOM)
+ rRect.SetTop( aAutoCharFrame.Top() );
+ else
+ rRect.SetTop( aAutoCharFrame.Bottom() );
+ rRect.SetBottom( aAutoCharFrame.Bottom() );
+ break;
+ // OD 12.11.2003 #i22341#
+ case RelOrientation::TEXT_LINE:
+ rRect.SetTop( aAutoCharFrame.Top() );
+ rRect.SetBottom( aAutoCharFrame.Top() );
+ break;
+ }
+ }
+ break;
+
+ case RndStdIds::FLY_AS_CHAR:
+ rRect.SetLeft( aParaPrtArea.Left() );
+ rRect.SetRight( aParaPrtArea.Right() );
+
+ switch (nVAlign)
+ {
+ case VertOrientation::NONE:
+ case VertOrientation::TOP:
+ case VertOrientation::CENTER:
+ case VertOrientation::BOTTOM:
+ {
+ FontMetric aMetric(rRenderContext.GetFontMetric());
+
+ rRect.SetTop( aParaPrtArea.Bottom() - aMetric.GetDescent() );
+ rRect.SetBottom( rRect.Top() );
+ }
+ break;
+
+ default:
+
+ case VertOrientation::LINE_TOP:
+ case VertOrientation::LINE_CENTER:
+ case VertOrientation::LINE_BOTTOM:
+ rRect.SetTop( aParaPrtArea.Top() );
+ rRect.SetBottom( aDrawObj.Bottom() );
+ break;
+
+ case VertOrientation::CHAR_TOP:
+ case VertOrientation::CHAR_CENTER:
+ case VertOrientation::CHAR_BOTTOM:
+ rRect.SetTop( aParaPrtArea.Top() );
+ rRect.SetBottom( aParaPrtArea.Bottom() );
+ break;
+ }
+ break;
+
+ default:
+ break;
+ }
+}
+
+tools::Rectangle SwFrameExample::DrawInnerFrame_Impl(vcl::RenderContext& rRenderContext, const tools::Rectangle &rRect,
+ const Color &rFillColor, const Color &rBorderColor)
+{
+ DrawRect_Impl(rRenderContext, rRect, rFillColor, rBorderColor);
+
+ // determine the area relative to which the positioning happens
+ tools::Rectangle aRect(rRect); // aPagePrtArea = Default
+ CalcBoundRect_Impl(rRenderContext, aRect);
+
+ if (nAnchor == RndStdIds::FLY_AT_FLY && &rRect == &aPagePrtArea)
+ {
+ // draw text paragraph
+ tools::Rectangle aTxt(aTextLine);
+ sal_Int32 nStep = aTxt.GetHeight() + 2;
+ sal_uInt16 nLines = static_cast<sal_uInt16>(aParaPrtArea.GetHeight() / (aTextLine.GetHeight() + 2));
+
+ for (sal_uInt16 i = 0; i < nLines; i++)
+ {
+ if (i == nLines - 1)
+ aTxt.SetSize(Size(aTxt.GetWidth() / 2, aTxt.GetHeight()));
+ DrawRect_Impl(rRenderContext, aTxt, m_aTxtCol, m_aTransColor);
+ aTxt.Move(0, nStep);
+ }
+ }
+
+ return aRect;
+}
+
+void SwFrameExample::Paint(vcl::RenderContext& rRenderContext, const tools::Rectangle&)
+{
+ rRenderContext.SetMapMode(MapMode(MapUnit::MapPixel));
+
+ InitAllRects_Impl(rRenderContext);
+
+ // Draw page
+ DrawRect_Impl(rRenderContext, aPage, m_aBgCol, m_aBorderCol);
+
+ // Draw PrintArea
+ tools::Rectangle aRect = DrawInnerFrame_Impl(rRenderContext, aPagePrtArea, m_aTransColor, m_aPrintAreaCol);
+
+ if (nAnchor == RndStdIds::FLY_AT_FLY)
+ aRect = DrawInnerFrame_Impl(rRenderContext, aFrameAtFrame, m_aBgCol, m_aBorderCol);
+
+ tools::Long lXPos = 0;
+ tools::Long lYPos = 0;
+
+ // Horizontal alignment
+ if (nAnchor != RndStdIds::FLY_AS_CHAR)
+ {
+ switch (nHAlign)
+ {
+ case HoriOrientation::RIGHT:
+ {
+ lXPos = aRect.Right() - aFrmSize.Width() + 1;
+ break;
+ }
+ case HoriOrientation::CENTER:
+ {
+ lXPos = aRect.Left() + (aRect.GetWidth() - aFrmSize.Width()) / 2;
+ break;
+ }
+ case HoriOrientation::NONE:
+ {
+ lXPos = aRect.Left() + aRelPos.X();
+ break;
+ }
+
+ default: // HoriOrientation::LEFT
+ lXPos = aRect.Left();
+ break;
+ }
+ }
+ else
+ {
+ lXPos = aRect.Right() + 2;
+ }
+
+ // Vertical Alignment
+ if (nAnchor != RndStdIds::FLY_AS_CHAR)
+ {
+ switch (nVAlign)
+ {
+ case VertOrientation::BOTTOM:
+ case VertOrientation::LINE_BOTTOM:
+ {
+ // #i22341#
+ if ( nVRel != RelOrientation::TEXT_LINE )
+ {
+ lYPos = aRect.Bottom() - aFrmSize.Height() + 1;
+ }
+ else
+ {
+ lYPos = aRect.Top();
+ }
+ break;
+ }
+ case VertOrientation::CENTER:
+ case VertOrientation::LINE_CENTER:
+ {
+ lYPos = aRect.Top() + (aRect.GetHeight() - aFrmSize.Height()) / 2;
+ break;
+ }
+ case VertOrientation::NONE:
+ {
+ // #i22341#
+ if ( nVRel != RelOrientation::CHAR && nVRel != RelOrientation::TEXT_LINE )
+ lYPos = aRect.Top() + aRelPos.Y();
+ else
+ lYPos = aRect.Top() - aRelPos.Y();
+ break;
+ }
+ default:
+ // #i22341#
+ if ( nVRel != RelOrientation::TEXT_LINE )
+ {
+ lYPos = aRect.Top();
+ }
+ else
+ {
+ lYPos = aRect.Bottom() - aFrmSize.Height() + 1;
+ }
+ break;
+ }
+ }
+ else
+ {
+ switch(nVAlign)
+ {
+ case VertOrientation::CENTER:
+ case VertOrientation::CHAR_CENTER:
+ case VertOrientation::LINE_CENTER:
+ lYPos = aRect.Top() + (aRect.GetHeight() - aFrmSize.Height()) / 2;
+ break;
+
+ case VertOrientation::TOP:
+ case VertOrientation::CHAR_BOTTOM:
+ case VertOrientation::LINE_BOTTOM:
+ lYPos = aRect.Bottom() - aFrmSize.Height() + 1;
+ break;
+
+ default:
+ lYPos = aRect.Top() - aRelPos.Y();
+ break;
+ }
+ }
+
+ tools::Rectangle aFrmRect(Point(lXPos, lYPos), aFrmSize);
+
+ tools::Rectangle* pOuterFrame = &aPage;
+
+ if (nAnchor == RndStdIds::FLY_AT_FLY)
+ pOuterFrame = &aFrameAtFrame;
+
+ if (aFrmRect.Left() < pOuterFrame->Left())
+ aFrmRect.Move(pOuterFrame->Left() - aFrmRect.Left(), 0);
+ if (aFrmRect.Right() > pOuterFrame->Right())
+ aFrmRect.Move(pOuterFrame->Right() - aFrmRect.Right(), 0);
+
+ if (aFrmRect.Top() < pOuterFrame->Top())
+ aFrmRect.Move(0, pOuterFrame->Top() - aFrmRect.Top());
+ if (aFrmRect.Bottom() > pOuterFrame->Bottom())
+ aFrmRect.Move(0, pOuterFrame->Bottom() - aFrmRect.Bottom());
+
+ // Draw Test paragraph
+ const tools::Long nTxtLineHeight = aTextLine.GetHeight();
+ tools::Rectangle aTxt(aTextLine);
+ sal_Int32 nStep;
+ sal_uInt16 nLines;
+
+ if (nAnchor == RndStdIds::FLY_AT_FLY)
+ {
+ aTxt.SetLeft( aFrameAtFrame.Left() + FLYINFLY_BORDER );
+ aTxt.SetRight( aFrameAtFrame.Right() - FLYINFLY_BORDER );
+ aTxt.SetTop( aFrameAtFrame.Top() + FLYINFLY_BORDER );
+ aTxt.SetBottom( aTxt.Top() + aTextLine.GetHeight() - 1 );
+
+ nStep = aTxt.GetHeight() + 2;
+ nLines = static_cast<sal_uInt16>(((aFrameAtFrame.GetHeight() - 2 * FLYINFLY_BORDER) * 2 / 3)
+ / (aTxt.GetHeight() + 2));
+ }
+ else
+ {
+ nStep = aTxt.GetHeight() + 2;
+ nLines = static_cast<sal_uInt16>(aParaPrtArea.GetHeight() / (aTextLine.GetHeight() + 2));
+ }
+
+ if (nAnchor != RndStdIds::FLY_AS_CHAR)
+ {
+ // Simulate text
+ const tools::Long nOldR = aTxt.Right();
+ const tools::Long nOldL = aTxt.Left();
+
+ // #i22341#
+ const bool bIgnoreWrap = nAnchor == RndStdIds::FLY_AT_CHAR &&
+ ( nHRel == RelOrientation::CHAR || nVRel == RelOrientation::CHAR ||
+ nVRel == RelOrientation::TEXT_LINE );
+
+ for (sal_uInt16 i = 0; i < nLines; ++i)
+ {
+ if (i == (nLines - 1))
+ aTxt.SetSize(Size(aTxt.GetWidth() / 2, aTxt.GetHeight()));
+
+ if (aTxt.Overlaps(aFrmRect) && nAnchor != RndStdIds::FLY_AS_CHAR && !bIgnoreWrap)
+ {
+ switch(nWrap)
+ {
+ case WrapTextMode_NONE:
+ aTxt.SetTop( aFrmRect.Bottom() + nTxtLineHeight );
+ aTxt.SetBottom( aTxt.Top() + nTxtLineHeight - 1 );
+ break;
+
+ case WrapTextMode_LEFT:
+ aTxt.SetRight( aFrmRect.Left() );
+ break;
+
+ case WrapTextMode_RIGHT:
+ aTxt.SetLeft( aFrmRect.Right() );
+ break;
+ default: break;
+ }
+ }
+ if (pOuterFrame->Contains(aTxt))
+ DrawRect_Impl(rRenderContext, aTxt, m_aTxtCol, m_aTransColor );
+
+ aTxt.Move(0, nStep);
+ aTxt.SetRight( nOldR );
+ aTxt.SetLeft( nOldL );
+ }
+ aTxt.Move(0, -nStep);
+
+ if (nAnchor != RndStdIds::FLY_AT_FLY && aTxt.Bottom() > aParaPrtArea.Bottom())
+ {
+ // Text has been replaced by frame, so adjust parameters height
+ sal_Int32 nDiff = aTxt.Bottom() - aParaPrtArea.Bottom();
+ aParaPrtArea.AdjustBottom(nDiff );
+ aPara.AdjustBottom(nDiff );
+
+ CalcBoundRect_Impl(rRenderContext, aRect);
+
+ aParaPrtArea.AdjustBottom( -nDiff );
+ aPara.AdjustBottom( -nDiff );
+ }
+ if (nAnchor == RndStdIds::FLY_AT_CHAR && bIgnoreWrap)
+ rRenderContext.DrawText(aAutoCharFrame, OUString('A'));
+ }
+ else
+ {
+ rRenderContext.DrawText(aParaPrtArea, OUString(DEMOTEXT));
+ DrawRect_Impl(rRenderContext, aDrawObj, m_aBlankCol, m_aBlankFrameCol );
+ }
+
+ // Draw rectangle on which the frame is aligned:
+ DrawRect_Impl(rRenderContext, aRect, m_aTransColor, m_aAlignColor);
+
+ // Frame View
+ bool bDontFill = (nAnchor == RndStdIds::FLY_AT_CHAR && aFrmRect.Overlaps(aAutoCharFrame)) || bTrans;
+ DrawRect_Impl(rRenderContext, aFrmRect, bDontFill? m_aTransColor : m_aBgCol, m_aFrameColor);
+}
+
+void SwFrameExample::SetRelPos(const Point& rP)
+{
+ aRelPos = rP;
+
+ if (aRelPos.X() > 0)
+ aRelPos.setX( 5 );
+ if (aRelPos.X() < 0)
+ aRelPos.setX( -5 );
+
+ if (aRelPos.Y() > 0)
+ aRelPos.setY( 5 );
+ if (aRelPos.Y() < 0)
+ aRelPos.setY( -5 );
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/dialog/swframeposstrings.cxx b/svx/source/dialog/swframeposstrings.cxx
new file mode 100644
index 000000000..29b9243f8
--- /dev/null
+++ b/svx/source/dialog/swframeposstrings.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 <sal/config.h>
+
+#include <cassert>
+
+#include <svx/swframeposstrings.hxx>
+#include <svx/dialmgr.hxx>
+#include <swframeposstrings.hrc>
+
+OUString SvxSwFramePosString::GetString(StringId eId)
+{
+ static_assert(
+ (SAL_N_ELEMENTS(RID_SVXSW_FRAMEPOSITIONS)
+ == SvxSwFramePosString::STR_MAX),
+ "RID_SVXSW_FRAMEPOSITIONS too small");
+ assert(eId >= 0 && eId < STR_MAX);
+ return SvxResId(RID_SVXSW_FRAMEPOSITIONS[eId]);
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/dialog/txencbox.cxx b/svx/source/dialog/txencbox.cxx
new file mode 100644
index 000000000..0aff6781d
--- /dev/null
+++ b/svx/source/dialog/txencbox.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 <config_features.h>
+
+#include <svx/txencbox.hxx>
+#include <svx/dialmgr.hxx>
+#include <svx/txenctab.hxx>
+#if HAVE_FEATURE_DBCONNECTIVITY
+#include <dbcharsethelper.hxx>
+#endif
+#include <unotools/syslocale.hxx>
+#include <rtl/tencinfo.h>
+#include <sal/log.hxx>
+#include <txenctab.hrc>
+
+namespace
+{
+ std::vector<rtl_TextEncoding> FillFromDbTextEncodingMap(bool bExcludeImportSubsets, sal_uInt32 nExcludeInfoFlags)
+ {
+ std::vector<rtl_TextEncoding> aRet;
+#if !HAVE_FEATURE_DBCONNECTIVITY
+ (void)bExcludeImportSubsets;
+ (void)nExcludeInfoFlags;
+#else
+ rtl_TextEncodingInfo aInfo;
+ aInfo.StructSize = sizeof(rtl_TextEncodingInfo);
+ ::std::vector< rtl_TextEncoding > aEncs;
+ sal_Int32 nCount = svxform::charset_helper::getSupportedTextEncodings( aEncs );
+ for ( sal_Int32 j=0; j<nCount; j++ )
+ {
+ bool bInsert = true;
+ rtl_TextEncoding nEnc = rtl_TextEncoding( aEncs[j] );
+ if ( nExcludeInfoFlags )
+ {
+ if ( !rtl_getTextEncodingInfo( nEnc, &aInfo ) )
+ bInsert = false;
+ else
+ {
+ if ( (aInfo.Flags & nExcludeInfoFlags) == 0 )
+ {
+ if ( (nExcludeInfoFlags & RTL_TEXTENCODING_INFO_UNICODE) &&
+ ((nEnc == RTL_TEXTENCODING_UCS2) ||
+ nEnc == RTL_TEXTENCODING_UCS4) )
+ bInsert = false; // InfoFlags don't work for Unicode :-(
+ }
+ else
+ bInsert = false;
+ }
+ }
+ if ( bInsert )
+ {
+ if ( bExcludeImportSubsets )
+ {
+ switch ( nEnc )
+ {
+ // subsets of RTL_TEXTENCODING_GB_18030
+ case RTL_TEXTENCODING_GB_2312 :
+ case RTL_TEXTENCODING_GBK :
+ case RTL_TEXTENCODING_MS_936 :
+ bInsert = false;
+ break;
+ }
+ }
+ // CharsetMap offers a RTL_TEXTENCODING_DONTKNOW for internal use,
+ // makes no sense here and would result in an empty string as list
+ // entry.
+ if ( bInsert && nEnc != RTL_TEXTENCODING_DONTKNOW )
+ aRet.push_back(nEnc);
+ }
+ }
+#endif
+ return aRet;
+ }
+}
+
+void SvxTextEncodingBox::FillFromDbTextEncodingMap(
+ bool bExcludeImportSubsets, sal_uInt32 nExcludeInfoFlags )
+{
+ m_xControl->freeze();
+ auto aEncs = ::FillFromDbTextEncodingMap(bExcludeImportSubsets, nExcludeInfoFlags);
+ for (auto nEnc : aEncs)
+ InsertTextEncoding(nEnc);
+ m_xControl->thaw();
+}
+
+void SvxTextEncodingTreeView::FillFromDbTextEncodingMap(
+ bool bExcludeImportSubsets, sal_uInt32 nExcludeInfoFlags )
+{
+ m_xControl->freeze();
+ auto aEncs = ::FillFromDbTextEncodingMap(bExcludeImportSubsets, nExcludeInfoFlags);
+ for (auto nEnc : aEncs)
+ InsertTextEncoding(nEnc);
+ m_xControl->thaw();
+}
+
+SvxTextEncodingBox::SvxTextEncodingBox(std::unique_ptr<weld::ComboBox> pControl)
+ : m_xControl(std::move(pControl))
+{
+ m_xControl->make_sorted();
+}
+
+SvxTextEncodingTreeView::SvxTextEncodingTreeView(std::unique_ptr<weld::TreeView> pControl)
+ : m_xControl(std::move(pControl))
+{
+ m_xControl->make_sorted();
+}
+
+SvxTextEncodingBox::~SvxTextEncodingBox()
+{
+}
+
+SvxTextEncodingTreeView::~SvxTextEncodingTreeView()
+{
+}
+
+namespace
+{
+ std::vector<int> FillFromTextEncodingTable(bool bExcludeImportSubsets, sal_uInt32 nExcludeInfoFlags, sal_uInt32 nButIncludeInfoFlags)
+ {
+ std::vector<int> aRet;
+
+ rtl_TextEncodingInfo aInfo;
+ aInfo.StructSize = sizeof(rtl_TextEncodingInfo);
+ const sal_uInt32 nCount = SAL_N_ELEMENTS(RID_SVXSTR_TEXTENCODING_TABLE);
+ for (sal_uInt32 j = 0; j < nCount; ++j)
+ {
+ bool bInsert = true;
+ rtl_TextEncoding nEnc = RID_SVXSTR_TEXTENCODING_TABLE[j].second;
+ if ( nExcludeInfoFlags )
+ {
+ if ( !rtl_getTextEncodingInfo( nEnc, &aInfo ) )
+ bInsert = false;
+ else
+ {
+ if ( (aInfo.Flags & nExcludeInfoFlags) == 0 )
+ {
+ if ( (nExcludeInfoFlags & RTL_TEXTENCODING_INFO_UNICODE) &&
+ ((nEnc == RTL_TEXTENCODING_UCS2) ||
+ nEnc == RTL_TEXTENCODING_UCS4) )
+ bInsert = false; // InfoFlags don't work for Unicode :-(
+ }
+ else if ( (aInfo.Flags & nButIncludeInfoFlags) == 0 )
+ bInsert = false;
+ }
+ }
+ if ( bExcludeImportSubsets )
+ {
+ switch ( nEnc )
+ {
+ // subsets of RTL_TEXTENCODING_GB_18030
+ case RTL_TEXTENCODING_GB_2312 :
+ case RTL_TEXTENCODING_GBK :
+ case RTL_TEXTENCODING_MS_936 :
+ bInsert = false;
+ break;
+ }
+ }
+ if ( bInsert )
+ aRet.push_back(j);
+ }
+ return aRet;
+ }
+}
+
+void SvxTextEncodingBox::FillFromTextEncodingTable(
+ bool bExcludeImportSubsets, sal_uInt32 nExcludeInfoFlags,
+ sal_uInt32 nButIncludeInfoFlags )
+{
+ std::vector<int> aRet(::FillFromTextEncodingTable(bExcludeImportSubsets, nExcludeInfoFlags, nButIncludeInfoFlags));
+ m_xControl->freeze();
+ for (auto j : aRet)
+ {
+ rtl_TextEncoding nEnc = RID_SVXSTR_TEXTENCODING_TABLE[j].second;
+ InsertTextEncoding(nEnc, SvxResId(RID_SVXSTR_TEXTENCODING_TABLE[j].first));
+ }
+ m_xControl->thaw();
+}
+
+void SvxTextEncodingTreeView::FillFromTextEncodingTable(
+ bool bExcludeImportSubsets, sal_uInt32 nExcludeInfoFlags )
+{
+ std::vector<int> aRet(::FillFromTextEncodingTable(bExcludeImportSubsets, nExcludeInfoFlags, /*nButIncludeInfoFlags*/0));
+ m_xControl->freeze();
+ for (auto j : aRet)
+ {
+ rtl_TextEncoding nEnc = RID_SVXSTR_TEXTENCODING_TABLE[j].second;
+ InsertTextEncoding(nEnc, SvxResId(RID_SVXSTR_TEXTENCODING_TABLE[j].first));
+ }
+ m_xControl->thaw();
+}
+
+void SvxTextEncodingBox::InsertTextEncoding( const rtl_TextEncoding nEnc,
+ const OUString& rEntry )
+{
+ m_xControl->append(OUString::number(nEnc), rEntry);
+}
+
+void SvxTextEncodingTreeView::InsertTextEncoding( const rtl_TextEncoding nEnc,
+ const OUString& rEntry )
+{
+ m_xControl->append(OUString::number(nEnc), rEntry);
+}
+
+void SvxTextEncodingBox::InsertTextEncoding( const rtl_TextEncoding nEnc )
+{
+ const OUString& rEntry = SvxTextEncodingTable::GetTextString(nEnc);
+ if (!rEntry.isEmpty())
+ InsertTextEncoding( nEnc, rEntry );
+ else
+ SAL_WARN( "svx.dialog", "SvxTextEncodingBox::InsertTextEncoding: no resource string for text encoding: " << static_cast<sal_Int32>( nEnc ) );
+}
+
+void SvxTextEncodingTreeView::InsertTextEncoding( const rtl_TextEncoding nEnc )
+{
+ const OUString& rEntry = SvxTextEncodingTable::GetTextString(nEnc);
+ if (!rEntry.isEmpty())
+ InsertTextEncoding( nEnc, rEntry );
+ else
+ SAL_WARN( "svx.dialog", "SvxTextEncodingTreeView::InsertTextEncoding: no resource string for text encoding: " << static_cast<sal_Int32>( nEnc ) );
+}
+
+rtl_TextEncoding SvxTextEncodingBox::GetSelectTextEncoding() const
+{
+ OUString sId(m_xControl->get_active_id());
+ if (!sId.isEmpty())
+ return rtl_TextEncoding(sId.toInt32());
+ else
+ return RTL_TEXTENCODING_DONTKNOW;
+}
+
+rtl_TextEncoding SvxTextEncodingTreeView::GetSelectTextEncoding() const
+{
+ OUString sId(m_xControl->get_selected_id());
+ if (!sId.isEmpty())
+ return rtl_TextEncoding(sId.toInt32());
+ else
+ return RTL_TEXTENCODING_DONTKNOW;
+}
+
+void SvxTextEncodingBox::SelectTextEncoding( const rtl_TextEncoding nEnc )
+{
+ m_xControl->set_active_id(OUString::number(nEnc));
+}
+
+void SvxTextEncodingTreeView::SelectTextEncoding( const rtl_TextEncoding nEnc )
+{
+ m_xControl->select_id(OUString::number(nEnc));
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/dialog/txenctab.cxx b/svx/source/dialog/txenctab.cxx
new file mode 100644
index 000000000..791b8f735
--- /dev/null
+++ b/svx/source/dialog/txenctab.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 <svx/dialmgr.hxx>
+#include <svx/txenctab.hxx>
+#include <txenctab.hrc>
+
+OUString SvxTextEncodingTable::GetTextString(const rtl_TextEncoding nEnc)
+{
+ const size_t nCount = SAL_N_ELEMENTS(RID_SVXSTR_TEXTENCODING_TABLE);
+
+ for (size_t i = 0; i < nCount; ++i)
+ {
+ if (RID_SVXSTR_TEXTENCODING_TABLE[i].second == nEnc)
+ return SvxResId(RID_SVXSTR_TEXTENCODING_TABLE[i].first);
+ }
+
+ return OUString();
+}
+
+rtl_TextEncoding SvxTextEncodingTable::GetTextEncoding(const OUString& rStr)
+{
+ const size_t nCount = SAL_N_ELEMENTS(RID_SVXSTR_TEXTENCODING_TABLE);
+
+ for (size_t i = 0; i < nCount; ++i)
+ {
+ if (SvxResId(RID_SVXSTR_TEXTENCODING_TABLE[i].first).equals(rStr))
+ return RID_SVXSTR_TEXTENCODING_TABLE[i].second;
+ }
+ return RTL_TEXTENCODING_DONTKNOW;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/dialog/weldeditview.cxx b/svx/source/dialog/weldeditview.cxx
new file mode 100644
index 000000000..dfae0f308
--- /dev/null
+++ b/svx/source/dialog/weldeditview.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 <config_wasm_strip.h>
+
+#include <basegfx/matrix/b2dhommatrix.hxx>
+#include <com/sun/star/accessibility/AccessibleRole.hpp>
+#include <com/sun/star/accessibility/AccessibleStateType.hpp>
+#include <com/sun/star/accessibility/XAccessible.hpp>
+#include <com/sun/star/accessibility/XAccessibleComponent.hpp>
+#include <com/sun/star/accessibility/XAccessibleContext.hpp>
+#include <com/sun/star/accessibility/XAccessibleEventBroadcaster.hpp>
+#include <com/sun/star/lang/IndexOutOfBoundsException.hpp>
+#include <com/sun/star/lang/XServiceInfo.hpp>
+#include <cppuhelper/supportsservice.hxx>
+#include <drawinglayer/processor2d/baseprocessor2d.hxx>
+#include <drawinglayer/processor2d/processor2dtools.hxx>
+#include <editeng/eeitem.hxx>
+#include <editeng/fhgtitem.hxx>
+#include <editeng/fontitem.hxx>
+#include <editeng/outliner.hxx>
+#include <editeng/unoedhlp.hxx>
+#include <editeng/unoedsrc.hxx>
+#include <i18nlangtag/languagetag.hxx>
+#include <osl/diagnose.h>
+#include <svl/itempool.hxx>
+#include <svl/itemset.hxx>
+#include <sal/log.hxx>
+#include <svx/sdr/overlay/overlayselection.hxx>
+#include <svtools/optionsdrawinglayer.hxx>
+#include <svx/AccessibleTextHelper.hxx>
+#include <svx/weldeditview.hxx>
+#include <tools/diagnose_ex.h>
+#include <unotools/accessiblestatesethelper.hxx>
+#include <vcl/canvastools.hxx>
+#include <vcl/cursor.hxx>
+#include <vcl/event.hxx>
+#include <vcl/ptrstyle.hxx>
+#include <vcl/settings.hxx>
+#include <vcl/svapp.hxx>
+#include <vcl/window.hxx>
+#include <vcl/uitest/uiobject.hxx>
+
+void WeldEditView::SetText(const OUString& rStr) { GetEditEngine()->SetText(rStr); }
+
+OUString WeldEditView::GetText() const { return GetEditEngine()->GetText(); }
+
+void WeldEditView::SetModifyHdl(const Link<LinkParamNone*, void>& rLink)
+{
+ GetEditEngine()->SetModifyHdl(rLink);
+}
+
+EditView* WeldEditView::GetEditView() const { return m_xEditView.get(); }
+
+EditEngine* WeldEditView::GetEditEngine() const { return m_xEditEngine.get(); }
+
+bool WeldEditView::HasSelection() const
+{
+ EditView* pEditView = GetEditView();
+ return pEditView && pEditView->HasSelection();
+}
+
+void WeldEditView::Delete()
+{
+ if (EditView* pEditView = GetEditView())
+ pEditView->DeleteSelected();
+}
+
+void WeldEditView::Cut()
+{
+ if (EditView* pEditView = GetEditView())
+ pEditView->Cut();
+}
+
+void WeldEditView::Copy()
+{
+ if (EditView* pEditView = GetEditView())
+ pEditView->Copy();
+}
+
+void WeldEditView::Paste()
+{
+ if (EditView* pEditView = GetEditView())
+ pEditView->Paste();
+}
+
+WeldEditView::WeldEditView()
+ : m_bAcceptsTab(false)
+{
+}
+
+// tdf#127033 want to use UI font so override makeEditEngine to enable that
+void WeldEditView::makeEditEngine()
+{
+ rtl::Reference<SfxItemPool> pItemPool = EditEngine::CreatePool();
+
+ vcl::Font aAppFont(Application::GetSettings().GetStyleSettings().GetAppFont());
+
+ pItemPool->SetPoolDefaultItem(SvxFontItem(aAppFont.GetFamilyType(), aAppFont.GetFamilyName(),
+ "", PITCH_DONTKNOW, RTL_TEXTENCODING_DONTKNOW,
+ EE_CHAR_FONTINFO));
+ pItemPool->SetPoolDefaultItem(SvxFontItem(aAppFont.GetFamilyType(), aAppFont.GetFamilyName(),
+ "", PITCH_DONTKNOW, RTL_TEXTENCODING_DONTKNOW,
+ EE_CHAR_FONTINFO_CJK));
+ pItemPool->SetPoolDefaultItem(SvxFontItem(aAppFont.GetFamilyType(), aAppFont.GetFamilyName(),
+ "", PITCH_DONTKNOW, RTL_TEXTENCODING_DONTKNOW,
+ EE_CHAR_FONTINFO_CTL));
+
+ pItemPool->SetPoolDefaultItem(
+ SvxFontHeightItem(aAppFont.GetFontHeight() * 20, 100, EE_CHAR_FONTHEIGHT));
+ pItemPool->SetPoolDefaultItem(
+ SvxFontHeightItem(aAppFont.GetFontHeight() * 20, 100, EE_CHAR_FONTHEIGHT_CJK));
+ pItemPool->SetPoolDefaultItem(
+ SvxFontHeightItem(aAppFont.GetFontHeight() * 20, 100, EE_CHAR_FONTHEIGHT_CTL));
+
+ m_xEditEngine.reset(new EditEngine(pItemPool.get()));
+}
+
+void WeldEditView::Resize()
+{
+ if (EditView* pEditView = GetEditView())
+ {
+ OutputDevice& rDevice = GetDrawingArea()->get_ref_device();
+ Size aOutputSize(rDevice.PixelToLogic(GetOutputSizePixel()));
+ // Resizes the edit engine to adjust to the size of the output area
+ pEditView->SetOutputArea(tools::Rectangle(Point(0, 0), aOutputSize));
+ GetEditEngine()->SetPaperSize(aOutputSize);
+ pEditView->ShowCursor();
+
+ const tools::Long nMaxVisAreaStart
+ = pEditView->GetEditEngine()->GetTextHeight() - aOutputSize.Height();
+ tools::Rectangle aVisArea(pEditView->GetVisArea());
+ if (aVisArea.Top() > nMaxVisAreaStart)
+ {
+ aVisArea.SetTop(std::max<tools::Long>(nMaxVisAreaStart, 0));
+ aVisArea.SetSize(aOutputSize);
+ pEditView->SetVisArea(aVisArea);
+ pEditView->ShowCursor();
+ }
+
+ EditViewScrollStateChange();
+ }
+ weld::CustomWidgetController::Resize();
+}
+
+void WeldEditView::Paint(vcl::RenderContext& rRenderContext, const tools::Rectangle& rRect)
+{
+ DoPaint(rRenderContext, rRect);
+}
+
+void WeldEditView::DoPaint(vcl::RenderContext& rRenderContext, const tools::Rectangle& rRect)
+{
+ rRenderContext.Push(vcl::PushFlags::ALL);
+ rRenderContext.SetClipRegion();
+
+ std::vector<tools::Rectangle> aLogicRects;
+
+ if (EditView* pEditView = GetEditView())
+ {
+ pEditView->Paint(rRect, &rRenderContext);
+
+ if (HasFocus())
+ {
+ pEditView->ShowCursor(false);
+ vcl::Cursor* pCursor = pEditView->GetCursor();
+ pCursor->DrawToDevice(rRenderContext);
+ }
+
+ // get logic selection
+ pEditView->GetSelectionRectangles(aLogicRects);
+ }
+
+ if (!aLogicRects.empty())
+ {
+ std::vector<basegfx::B2DRange> aLogicRanges;
+ aLogicRanges.reserve(aLogicRects.size());
+
+ tools::Long nMinX(LONG_MAX), nMaxX(0), nMinY(LONG_MAX), nMaxY(0);
+ for (const auto& aRect : aLogicRects)
+ {
+ nMinX = std::min(nMinX, aRect.Left());
+ nMinY = std::min(nMinY, aRect.Top());
+ nMaxX = std::max(nMaxX, aRect.Right());
+ nMaxY = std::max(nMaxY, aRect.Bottom());
+ }
+
+ const Size aLogicPixel(rRenderContext.PixelToLogic(Size(1, 1)));
+ for (const auto& aRect : aLogicRects)
+ {
+ // Extend each range by one pixel so multiple lines touch each
+ // other if adjacent, so the whole set is drawn with a single
+ // border around the lot. But keep the selection within the
+ // original max extents.
+ auto nTop = aRect.Top();
+ if (nTop > nMinY)
+ nTop -= aLogicPixel.Height();
+ auto nBottom = aRect.Bottom();
+ if (nBottom < nMaxY)
+ nBottom += aLogicPixel.Height();
+ auto nLeft = aRect.Left();
+ if (nLeft > nMinX)
+ nLeft -= aLogicPixel.Width();
+ auto nRight = aRect.Right();
+ if (nRight < nMaxX)
+ nRight += aLogicPixel.Width();
+
+ aLogicRanges.emplace_back(nLeft, nTop, nRight, nBottom);
+ }
+
+ // get the system's highlight color
+ const Color aHighlight(SvtOptionsDrawinglayer::getHilightColor());
+
+ sdr::overlay::OverlaySelection aCursorOverlay(sdr::overlay::OverlayType::Transparent,
+ aHighlight, std::move(aLogicRanges), true);
+
+ const drawinglayer::geometry::ViewInformation2D aViewInformation2D(
+ basegfx::B2DHomMatrix(), rRenderContext.GetViewTransformation(),
+ vcl::unotools::b2DRectangleFromRectangle(rRect), nullptr, 0.0);
+
+ std::unique_ptr<drawinglayer::processor2d::BaseProcessor2D> xProcessor(
+ drawinglayer::processor2d::createProcessor2DFromOutputDevice(rRenderContext,
+ aViewInformation2D));
+
+ xProcessor->process(aCursorOverlay.getOverlayObjectPrimitive2DSequence());
+ }
+
+ rRenderContext.Pop();
+}
+
+bool WeldEditView::MouseMove(const MouseEvent& rMEvt)
+{
+ EditView* pEditView = GetEditView();
+ return pEditView && pEditView->MouseMove(rMEvt);
+}
+
+bool WeldEditView::MouseButtonDown(const MouseEvent& rMEvt)
+{
+ if (!IsMouseCaptured())
+ CaptureMouse();
+
+ if (!HasFocus() && CanFocus())
+ GrabFocus();
+
+ EditView* pEditView = GetEditView();
+ return pEditView && pEditView->MouseButtonDown(rMEvt);
+}
+
+bool WeldEditView::MouseButtonUp(const MouseEvent& rMEvt)
+{
+ if (IsMouseCaptured())
+ ReleaseMouse();
+ EditView* pEditView = GetEditView();
+ return pEditView && pEditView->MouseButtonUp(rMEvt);
+}
+
+bool WeldEditView::KeyInput(const KeyEvent& rKEvt)
+{
+ EditView* pEditView = GetEditView();
+
+ sal_uInt16 nKey = rKEvt.GetKeyCode().GetCode();
+
+ if (nKey == KEY_TAB && !GetAcceptsTab())
+ {
+ return false;
+ }
+ else if (pEditView && !pEditView->PostKeyEvent(rKEvt))
+ {
+ if (rKEvt.GetKeyCode().IsMod1() && !rKEvt.GetKeyCode().IsMod2())
+ {
+ if (nKey == KEY_A)
+ {
+ EditEngine* pEditEngine = GetEditEngine();
+ sal_Int32 nPar = pEditEngine->GetParagraphCount();
+ if (nPar)
+ {
+ sal_Int32 nLen = pEditEngine->GetTextLen(nPar - 1);
+ pEditView->SetSelection(ESelection(0, 0, nPar - 1, nLen));
+ }
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ return true;
+}
+
+bool WeldEditView::Command(const CommandEvent& rCEvt)
+{
+ EditView* pEditView = GetEditView();
+ if (!pEditView)
+ return false;
+ return pEditView->Command(rCEvt);
+}
+
+Point WeldEditView::EditViewPointerPosPixel() const
+{
+ return GetDrawingArea()->get_pointer_position();
+}
+
+class WeldEditAccessible;
+
+namespace
+{
+class WeldViewForwarder : public SvxViewForwarder
+{
+ WeldEditAccessible& m_rEditAcc;
+
+ WeldViewForwarder(const WeldViewForwarder&) = delete;
+ WeldViewForwarder& operator=(const WeldViewForwarder&) = delete;
+
+public:
+ explicit WeldViewForwarder(WeldEditAccessible& rAcc)
+ : m_rEditAcc(rAcc)
+ {
+ }
+
+ virtual bool IsValid() const override;
+ virtual Point LogicToPixel(const Point& rPoint, const MapMode& rMapMode) const override;
+ virtual Point PixelToLogic(const Point& rPoint, const MapMode& rMapMode) const override;
+};
+}
+
+class WeldEditAccessible;
+
+namespace
+{
+class WeldEditSource;
+
+/* analog to SvxEditEngineForwarder */
+class WeldTextForwarder : public SvxTextForwarder
+{
+ WeldEditAccessible& m_rEditAcc;
+ WeldEditSource& m_rEditSource;
+
+ DECL_LINK(NotifyHdl, EENotify&, void);
+
+ WeldTextForwarder(const WeldTextForwarder&) = delete;
+ WeldTextForwarder& operator=(const WeldTextForwarder&) = delete;
+
+public:
+ WeldTextForwarder(WeldEditAccessible& rAcc, WeldEditSource& rSource);
+ virtual ~WeldTextForwarder() override;
+
+ virtual sal_Int32 GetParagraphCount() const override;
+ virtual sal_Int32 GetTextLen(sal_Int32 nParagraph) const override;
+ virtual OUString GetText(const ESelection& rSel) const override;
+ virtual SfxItemSet GetAttribs(const ESelection& rSel, EditEngineAttribs nOnlyHardAttrib
+ = EditEngineAttribs::All) const override;
+ virtual SfxItemSet GetParaAttribs(sal_Int32 nPara) const override;
+ virtual void SetParaAttribs(sal_Int32 nPara, const SfxItemSet& rSet) override;
+ virtual void RemoveAttribs(const ESelection& rSelection) override;
+ virtual void GetPortions(sal_Int32 nPara, std::vector<sal_Int32>& rList) const override;
+
+ virtual SfxItemState GetItemState(const ESelection& rSel, sal_uInt16 nWhich) const override;
+ virtual SfxItemState GetItemState(sal_Int32 nPara, sal_uInt16 nWhich) const override;
+
+ virtual void QuickInsertText(const OUString& rText, const ESelection& rSel) override;
+ virtual void QuickInsertField(const SvxFieldItem& rFld, const ESelection& rSel) override;
+ virtual void QuickSetAttribs(const SfxItemSet& rSet, const ESelection& rSel) override;
+ virtual void QuickInsertLineBreak(const ESelection& rSel) override;
+
+ virtual SfxItemPool* GetPool() const override;
+
+ virtual OUString CalcFieldValue(const SvxFieldItem& rField, sal_Int32 nPara, sal_Int32 nPos,
+ std::optional<Color>& rpTxtColor,
+ std::optional<Color>& rpFldColor) override;
+ virtual void FieldClicked(const SvxFieldItem&) override;
+ virtual bool IsValid() const override;
+
+ virtual LanguageType GetLanguage(sal_Int32, sal_Int32) const override;
+ virtual sal_Int32 GetFieldCount(sal_Int32 nPara) const override;
+ virtual EFieldInfo GetFieldInfo(sal_Int32 nPara, sal_uInt16 nField) const override;
+ virtual EBulletInfo GetBulletInfo(sal_Int32 nPara) const override;
+ virtual tools::Rectangle GetCharBounds(sal_Int32 nPara, sal_Int32 nIndex) const override;
+ virtual tools::Rectangle GetParaBounds(sal_Int32 nPara) const override;
+ virtual MapMode GetMapMode() const override;
+ virtual OutputDevice* GetRefDevice() const override;
+ virtual bool GetIndexAtPoint(const Point&, sal_Int32& nPara, sal_Int32& nIndex) const override;
+ virtual bool GetWordIndices(sal_Int32 nPara, sal_Int32 nIndex, sal_Int32& nStart,
+ sal_Int32& nEnd) const override;
+ virtual bool GetAttributeRun(sal_Int32& nStartIndex, sal_Int32& nEndIndex, sal_Int32 nPara,
+ sal_Int32 nIndex, bool bInCell = false) const override;
+ virtual sal_Int32 GetLineCount(sal_Int32 nPara) const override;
+ virtual sal_Int32 GetLineLen(sal_Int32 nPara, sal_Int32 nLine) const override;
+ virtual void GetLineBoundaries(/*out*/ sal_Int32& rStart, /*out*/ sal_Int32& rEnd,
+ sal_Int32 nParagraph, sal_Int32 nLine) const override;
+ virtual sal_Int32 GetLineNumberAtIndex(sal_Int32 nPara, sal_Int32 nLine) const override;
+ virtual bool Delete(const ESelection&) override;
+ virtual bool InsertText(const OUString&, const ESelection&) override;
+ virtual bool QuickFormatDoc(bool bFull = false) override;
+
+ virtual sal_Int16 GetDepth(sal_Int32 nPara) const override;
+ virtual bool SetDepth(sal_Int32 nPara, sal_Int16 nNewDepth) override;
+
+ virtual const SfxItemSet* GetEmptyItemSetPtr() override;
+ // implementation functions for XParagraphAppend and XTextPortionAppend
+ virtual void AppendParagraph() override;
+ virtual sal_Int32 AppendTextPortion(sal_Int32 nPara, const OUString& rText,
+ const SfxItemSet& rSet) override;
+
+ virtual void CopyText(const SvxTextForwarder& rSource) override;
+};
+
+/* analog to SvxEditEngineViewForwarder */
+class WeldEditViewForwarder : public SvxEditViewForwarder
+{
+ WeldEditAccessible& m_rEditAcc;
+
+ WeldEditViewForwarder(const WeldEditViewForwarder&) = delete;
+ WeldEditViewForwarder& operator=(const WeldEditViewForwarder&) = delete;
+
+public:
+ explicit WeldEditViewForwarder(WeldEditAccessible& rAcc);
+
+ virtual bool IsValid() const override;
+
+ virtual Point LogicToPixel(const Point& rPoint, const MapMode& rMapMode) const override;
+ virtual Point PixelToLogic(const Point& rPoint, const MapMode& rMapMode) const override;
+
+ virtual bool GetSelection(ESelection& rSelection) const override;
+ virtual bool SetSelection(const ESelection& rSelection) override;
+ virtual bool Copy() override;
+ virtual bool Cut() override;
+ virtual bool Paste() override;
+};
+
+class WeldEditSource : public SvxEditSource
+{
+ SfxBroadcaster m_aBroadCaster;
+ WeldViewForwarder m_aViewFwd;
+ WeldTextForwarder m_aTextFwd;
+ WeldEditViewForwarder m_aEditViewFwd;
+ WeldEditAccessible& m_rEditAcc;
+
+ WeldEditSource(const WeldEditSource& rSrc)
+ : SvxEditSource()
+ , m_aViewFwd(rSrc.m_rEditAcc)
+ , m_aTextFwd(rSrc.m_rEditAcc, *this)
+ , m_aEditViewFwd(rSrc.m_rEditAcc)
+ , m_rEditAcc(rSrc.m_rEditAcc)
+ {
+ }
+
+ WeldEditSource& operator=(const WeldEditSource&) = delete;
+
+public:
+ WeldEditSource(WeldEditAccessible& rAcc)
+ : m_aViewFwd(rAcc)
+ , m_aTextFwd(rAcc, *this)
+ , m_aEditViewFwd(rAcc)
+ , m_rEditAcc(rAcc)
+ {
+ }
+
+ virtual std::unique_ptr<SvxEditSource> Clone() const override
+ {
+ return std::unique_ptr<SvxEditSource>(new WeldEditSource(*this));
+ }
+
+ virtual SvxTextForwarder* GetTextForwarder() override { return &m_aTextFwd; }
+
+ virtual SvxViewForwarder* GetViewForwarder() override { return &m_aViewFwd; }
+
+ virtual SvxEditViewForwarder* GetEditViewForwarder(bool /*bCreate*/) override
+ {
+ return &m_aEditViewFwd;
+ }
+
+ virtual void UpdateData() override
+ {
+ // would possibly only by needed if the XText interface is implemented
+ // and its text needs to be updated.
+ }
+ virtual SfxBroadcaster& GetBroadcaster() const override
+ {
+ return const_cast<WeldEditSource*>(this)->m_aBroadCaster;
+ }
+};
+}
+
+typedef cppu::WeakImplHelper<css::lang::XServiceInfo, css::accessibility::XAccessible,
+ css::accessibility::XAccessibleComponent,
+ css::accessibility::XAccessibleContext,
+ css::accessibility::XAccessibleEventBroadcaster>
+ WeldEditAccessibleBaseClass;
+
+class WeldEditAccessible : public WeldEditAccessibleBaseClass
+{
+ weld::CustomWidgetController* m_pController;
+ EditEngine* m_pEditEngine;
+ EditView* m_pEditView;
+ std::unique_ptr<::accessibility::AccessibleTextHelper> m_xTextHelper;
+
+public:
+ WeldEditAccessible(weld::CustomWidgetController* pController)
+ : m_pController(pController)
+ , m_pEditEngine(nullptr)
+ , m_pEditView(nullptr)
+ {
+ }
+
+ ::accessibility::AccessibleTextHelper* GetTextHelper() { return m_xTextHelper.get(); }
+
+ void Init(EditEngine* pEditEngine, EditView* pEditView)
+ {
+ m_pEditEngine = pEditEngine;
+ m_pEditView = pEditView;
+ m_xTextHelper.reset(
+ new ::accessibility::AccessibleTextHelper(std::make_unique<WeldEditSource>(*this)));
+ m_xTextHelper->SetEventSource(this);
+ }
+
+ EditEngine* GetEditEngine() { return m_pEditEngine; }
+ EditView* GetEditView() { return m_pEditView; }
+
+ void ClearWin()
+ {
+ // remove handler before current object gets destroyed
+ // (avoid handler being called for already dead object)
+ m_pEditEngine->SetNotifyHdl(Link<EENotify&, void>());
+
+ m_pEditEngine = nullptr;
+ m_pEditView = nullptr;
+ m_pController = nullptr; // implicitly results in AccessibleStateType::DEFUNC set
+
+ //! make TextHelper implicitly release C++ references to some core objects
+ m_xTextHelper->SetEditSource(::std::unique_ptr<SvxEditSource>());
+
+ //! make TextHelper release references
+ //! (e.g. the one set by the 'SetEventSource' call)
+ m_xTextHelper->Dispose();
+ m_xTextHelper.reset();
+ }
+
+ // XAccessible
+ virtual css::uno::Reference<css::accessibility::XAccessibleContext>
+ SAL_CALL getAccessibleContext() override
+ {
+ return this;
+ }
+
+ // XAccessibleComponent
+ virtual sal_Bool SAL_CALL containsPoint(const css::awt::Point& rPoint) override
+ {
+ //! the arguments coordinates are relative to the current window !
+ //! Thus the top left-point is (0, 0)
+ SolarMutexGuard aGuard;
+ if (!m_pController)
+ throw css::uno::RuntimeException();
+
+ Size aSz(m_pController->GetOutputSizePixel());
+ return rPoint.X >= 0 && rPoint.Y >= 0 && rPoint.X < aSz.Width() && rPoint.Y < aSz.Height();
+ }
+
+ virtual css::uno::Reference<css::accessibility::XAccessible>
+ SAL_CALL getAccessibleAtPoint(const css::awt::Point& rPoint) override
+ {
+ SolarMutexGuard aGuard;
+ if (!m_xTextHelper)
+ throw css::uno::RuntimeException();
+
+ return m_xTextHelper->GetAt(rPoint);
+ }
+
+ virtual css::awt::Rectangle SAL_CALL getBounds() override
+ {
+ SolarMutexGuard aGuard;
+ if (!m_pController)
+ throw css::uno::RuntimeException();
+
+ const Point aOutPos;
+ const Size aOutSize(m_pController->GetOutputSizePixel());
+ css::awt::Rectangle aRet;
+
+ aRet.X = aOutPos.X();
+ aRet.Y = aOutPos.Y();
+ aRet.Width = aOutSize.Width();
+ aRet.Height = aOutSize.Height();
+
+ return aRet;
+ }
+
+ virtual css::awt::Point SAL_CALL getLocation() override
+ {
+ SolarMutexGuard aGuard;
+ if (!m_pController)
+ throw css::uno::RuntimeException();
+
+ const css::awt::Rectangle aRect(getBounds());
+ css::awt::Point aRet;
+
+ aRet.X = aRect.X;
+ aRet.Y = aRect.Y;
+
+ return aRet;
+ }
+
+ virtual css::awt::Point SAL_CALL getLocationOnScreen() override
+ {
+ SolarMutexGuard aGuard;
+ if (!m_pController)
+ throw css::uno::RuntimeException();
+
+ css::awt::Point aScreenLoc(0, 0);
+
+ if (weld::DrawingArea* pDrawingArea = m_pController->GetDrawingArea())
+ {
+ Point aPos = pDrawingArea->get_accessible_location_on_screen();
+ aScreenLoc.X = aPos.X();
+ aScreenLoc.Y = aPos.Y();
+ }
+
+ return aScreenLoc;
+ }
+
+ virtual css::awt::Size SAL_CALL getSize() override
+ {
+ SolarMutexGuard aGuard;
+ if (!m_pController)
+ throw css::uno::RuntimeException();
+
+ Size aSz(m_pController->GetOutputSizePixel());
+ return css::awt::Size(aSz.Width(), aSz.Height());
+ }
+
+ virtual void SAL_CALL grabFocus() override { m_pController->GrabFocus(); }
+
+ virtual sal_Int32 SAL_CALL getForeground() override
+ {
+ SolarMutexGuard aGuard;
+ if (!m_pController)
+ throw css::uno::RuntimeException();
+
+ Color nCol = m_pEditEngine->GetAutoColor();
+ return static_cast<sal_Int32>(nCol);
+ }
+
+ virtual sal_Int32 SAL_CALL getBackground() override
+ {
+ SolarMutexGuard aGuard;
+ if (!m_pController)
+ throw css::uno::RuntimeException();
+
+ Color nCol = m_pEditEngine->GetBackgroundColor();
+ return static_cast<sal_Int32>(nCol);
+ }
+
+ // XAccessibleContext
+ virtual sal_Int32 SAL_CALL getAccessibleChildCount() override
+ {
+ if (m_xTextHelper)
+ return m_xTextHelper->GetChildCount();
+ return 0;
+ }
+
+ virtual css::uno::Reference<css::accessibility::XAccessible>
+ SAL_CALL getAccessibleChild(sal_Int32 i) override
+ {
+ if (m_xTextHelper)
+ return m_xTextHelper->GetChild(i);
+ throw css::lang::IndexOutOfBoundsException(); // there is no child...
+ }
+
+ virtual css::uno::Reference<css::accessibility::XAccessible>
+ SAL_CALL getAccessibleParent() override
+ {
+ SolarMutexGuard aGuard;
+ if (!m_pController)
+ throw css::uno::RuntimeException();
+
+ return m_pController->GetDrawingArea()->get_accessible_parent();
+ }
+
+ virtual sal_Int32 SAL_CALL getAccessibleIndexInParent() override
+ {
+ SolarMutexGuard aGuard;
+ if (!m_pController)
+ throw css::uno::RuntimeException();
+
+ // -1 for child not found/no parent (according to specification)
+ sal_Int32 nRet = -1;
+
+ css::uno::Reference<css::accessibility::XAccessible> xParent(getAccessibleParent());
+ if (!xParent)
+ return nRet;
+
+ try
+ {
+ css::uno::Reference<css::accessibility::XAccessibleContext> xParentContext(
+ xParent->getAccessibleContext());
+
+ // iterate over parent's children and search for this object
+ if (xParentContext.is())
+ {
+ sal_Int32 nChildCount = xParentContext->getAccessibleChildCount();
+ for (sal_Int32 nChild = 0; (nChild < nChildCount) && (-1 == nRet); ++nChild)
+ {
+ css::uno::Reference<css::accessibility::XAccessible> xChild(
+ xParentContext->getAccessibleChild(nChild));
+ if (xChild.get() == this)
+ nRet = nChild;
+ }
+ }
+ }
+ catch (const css::uno::Exception&)
+ {
+ TOOLS_WARN_EXCEPTION("svx", "WeldEditAccessible::getAccessibleIndexInParent");
+ }
+
+ return nRet;
+ }
+
+ virtual sal_Int16 SAL_CALL getAccessibleRole() override
+ {
+ return css::accessibility::AccessibleRole::TEXT_FRAME;
+ }
+
+ virtual OUString SAL_CALL getAccessibleDescription() override
+ {
+ SolarMutexGuard aGuard;
+
+ OUString aRet;
+
+ if (m_pController)
+ {
+ aRet = m_pController->GetAccessibleDescription();
+ }
+
+ return aRet;
+ }
+
+ virtual OUString SAL_CALL getAccessibleName() override
+ {
+ SolarMutexGuard aGuard;
+
+ OUString aRet;
+
+ if (m_pController)
+ {
+ aRet = m_pController->GetAccessibleName();
+ }
+
+ return aRet;
+ }
+
+ virtual css::uno::Reference<css::accessibility::XAccessibleRelationSet>
+ SAL_CALL getAccessibleRelationSet() override
+ {
+ SolarMutexGuard aGuard;
+ if (!m_pController)
+ throw css::uno::RuntimeException();
+
+ return m_pController->GetDrawingArea()->get_accessible_relation_set();
+ }
+
+ virtual css::uno::Reference<css::accessibility::XAccessibleStateSet>
+ SAL_CALL getAccessibleStateSet() override
+ {
+ SolarMutexGuard aGuard;
+ rtl::Reference<::utl::AccessibleStateSetHelper> pStateSet
+ = new ::utl::AccessibleStateSetHelper;
+
+ if (!m_pController || !m_xTextHelper)
+ pStateSet->AddState(css::accessibility::AccessibleStateType::DEFUNC);
+ else
+ {
+ pStateSet->AddState(css::accessibility::AccessibleStateType::MULTI_LINE);
+ pStateSet->AddState(css::accessibility::AccessibleStateType::ENABLED);
+ pStateSet->AddState(css::accessibility::AccessibleStateType::EDITABLE);
+ pStateSet->AddState(css::accessibility::AccessibleStateType::FOCUSABLE);
+ pStateSet->AddState(css::accessibility::AccessibleStateType::SELECTABLE);
+ if (m_pController->HasFocus())
+ pStateSet->AddState(css::accessibility::AccessibleStateType::FOCUSED);
+ if (m_pController->IsActive())
+ pStateSet->AddState(css::accessibility::AccessibleStateType::ACTIVE);
+ if (m_pController->IsVisible())
+ pStateSet->AddState(css::accessibility::AccessibleStateType::SHOWING);
+ if (m_pController->IsReallyVisible())
+ pStateSet->AddState(css::accessibility::AccessibleStateType::VISIBLE);
+ if (COL_TRANSPARENT != m_pEditEngine->GetBackgroundColor())
+ pStateSet->AddState(css::accessibility::AccessibleStateType::OPAQUE);
+ }
+
+ return pStateSet;
+ }
+
+ virtual css::lang::Locale SAL_CALL getLocale() override
+ {
+ SolarMutexGuard aGuard;
+ return LanguageTag(m_pEditEngine->GetDefaultLanguage()).getLocale();
+ }
+
+ // XAccessibleEventBroadcaster
+ virtual void SAL_CALL addAccessibleEventListener(
+ const css::uno::Reference<css::accessibility::XAccessibleEventListener>& rListener) override
+ {
+ if (!m_xTextHelper) // not disposing (about to destroy view shell)
+ return;
+ m_xTextHelper->AddEventListener(rListener);
+ }
+
+ virtual void SAL_CALL removeAccessibleEventListener(
+ const css::uno::Reference<css::accessibility::XAccessibleEventListener>& rListener) override
+ {
+ if (!m_xTextHelper) // not disposing (about to destroy view shell)
+ return;
+ m_xTextHelper->RemoveEventListener(rListener);
+ }
+
+ virtual OUString SAL_CALL getImplementationName() override { return "WeldEditAccessible"; }
+
+ 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 { "css::accessibility::Accessible", "css::accessibility::AccessibleComponent",
+ "css::accessibility::AccessibleContext" };
+ }
+};
+
+css::uno::Reference<css::accessibility::XAccessible> WeldEditView::CreateAccessible()
+{
+#if !ENABLE_WASM_STRIP_ACCESSIBILITY
+ if (!m_xAccessible.is())
+ m_xAccessible.set(new WeldEditAccessible(this));
+#endif
+ return m_xAccessible;
+}
+
+WeldEditView::~WeldEditView()
+{
+#if !ENABLE_WASM_STRIP_ACCESSIBILITY
+ if (m_xAccessible.is())
+ {
+ m_xAccessible->ClearWin(); // make Accessible nonfunctional
+ m_xAccessible.clear();
+ }
+#endif
+}
+
+bool WeldViewForwarder::IsValid() const { return m_rEditAcc.GetEditView() != nullptr; }
+
+Point WeldViewForwarder::LogicToPixel(const Point& rPoint, const MapMode& rMapMode) const
+{
+ EditView* pEditView = m_rEditAcc.GetEditView();
+ if (!pEditView)
+ return Point();
+ OutputDevice& rOutDev = pEditView->GetOutputDevice();
+ MapMode aMapMode(rOutDev.GetMapMode());
+ Point aPoint(OutputDevice::LogicToLogic(rPoint, rMapMode, MapMode(aMapMode.GetMapUnit())));
+ aMapMode.SetOrigin(Point());
+ return rOutDev.LogicToPixel(aPoint, aMapMode);
+}
+
+Point WeldViewForwarder::PixelToLogic(const Point& rPoint, const MapMode& rMapMode) const
+{
+ EditView* pEditView = m_rEditAcc.GetEditView();
+ if (!pEditView)
+ return Point();
+ OutputDevice& rOutDev = pEditView->GetOutputDevice();
+ MapMode aMapMode(rOutDev.GetMapMode());
+ aMapMode.SetOrigin(Point());
+ Point aPoint(rOutDev.PixelToLogic(rPoint, aMapMode));
+ return OutputDevice::LogicToLogic(aPoint, MapMode(aMapMode.GetMapUnit()), rMapMode);
+}
+
+WeldTextForwarder::WeldTextForwarder(WeldEditAccessible& rAcc, WeldEditSource& rSource)
+ : m_rEditAcc(rAcc)
+ , m_rEditSource(rSource)
+{
+ EditEngine* pEditEngine = m_rEditAcc.GetEditEngine();
+ if (pEditEngine)
+ pEditEngine->SetNotifyHdl(LINK(this, WeldTextForwarder, NotifyHdl));
+}
+
+WeldTextForwarder::~WeldTextForwarder()
+{
+ EditEngine* pEditEngine = m_rEditAcc.GetEditEngine();
+ if (pEditEngine)
+ pEditEngine->SetNotifyHdl(Link<EENotify&, void>());
+}
+
+IMPL_LINK(WeldTextForwarder, NotifyHdl, EENotify&, rNotify, void)
+{
+ if (EditEngine* pEditEngine = m_rEditAcc.GetEditEngine())
+ {
+ if (rNotify.eNotificationType == EE_NOTIFY_PROCESSNOTIFICATIONS
+ && !pEditEngine->IsUpdateLayout())
+ {
+ // tdf#143088 an UpdateMode of false will just to on to cause
+ // AccessibleTextHelper_Impl::GetTextForwarder to throw an
+ // exception as a Frozen EditEngine is considered Invalid so return
+ // early instead
+ return;
+ }
+ }
+
+ ::std::unique_ptr<SfxHint> aHint = SvxEditSourceHelper::EENotification2Hint(&rNotify);
+ if (aHint)
+ m_rEditSource.GetBroadcaster().Broadcast(*aHint);
+}
+
+sal_Int32 WeldTextForwarder::GetParagraphCount() const
+{
+ EditEngine* pEditEngine = m_rEditAcc.GetEditEngine();
+ return pEditEngine ? pEditEngine->GetParagraphCount() : 0;
+}
+
+sal_Int32 WeldTextForwarder::GetTextLen(sal_Int32 nParagraph) const
+{
+ EditEngine* pEditEngine = m_rEditAcc.GetEditEngine();
+ return pEditEngine ? pEditEngine->GetTextLen(nParagraph) : 0;
+}
+
+OUString WeldTextForwarder::GetText(const ESelection& rSel) const
+{
+ EditEngine* pEditEngine = m_rEditAcc.GetEditEngine();
+ OUString aRet;
+ if (pEditEngine)
+ aRet = pEditEngine->GetText(rSel);
+ return convertLineEnd(aRet, GetSystemLineEnd());
+}
+
+SfxItemSet WeldTextForwarder::GetAttribs(const ESelection& rSel,
+ EditEngineAttribs nOnlyHardAttrib) const
+{
+ EditEngine* pEditEngine = m_rEditAcc.GetEditEngine();
+ assert(pEditEngine && "EditEngine missing");
+ if (rSel.nStartPara == rSel.nEndPara)
+ {
+ GetAttribsFlags nFlags = GetAttribsFlags::NONE;
+ switch (nOnlyHardAttrib)
+ {
+ case EditEngineAttribs::All:
+ nFlags = GetAttribsFlags::ALL;
+ break;
+ case EditEngineAttribs::OnlyHard:
+ nFlags = GetAttribsFlags::CHARATTRIBS;
+ break;
+ default:
+ SAL_WARN("svx", "unknown flags for WeldTextForwarder::GetAttribs");
+ }
+
+ return pEditEngine->GetAttribs(rSel.nStartPara, rSel.nStartPos, rSel.nEndPos, nFlags);
+ }
+ else
+ {
+ return pEditEngine->GetAttribs(rSel, nOnlyHardAttrib);
+ }
+}
+
+SfxItemSet WeldTextForwarder::GetParaAttribs(sal_Int32 nPara) const
+{
+ EditEngine* pEditEngine = m_rEditAcc.GetEditEngine();
+ assert(pEditEngine && "EditEngine missing");
+
+ SfxItemSet aSet(pEditEngine->GetParaAttribs(nPara));
+
+ sal_uInt16 nWhich = EE_PARA_START;
+ while (nWhich <= EE_PARA_END)
+ {
+ if (aSet.GetItemState(nWhich) != SfxItemState::SET)
+ {
+ if (pEditEngine->HasParaAttrib(nPara, nWhich))
+ aSet.Put(pEditEngine->GetParaAttrib(nPara, nWhich));
+ }
+ nWhich++;
+ }
+
+ return aSet;
+}
+
+void WeldTextForwarder::SetParaAttribs(sal_Int32 nPara, const SfxItemSet& rSet)
+{
+ EditEngine* pEditEngine = m_rEditAcc.GetEditEngine();
+ if (pEditEngine)
+ pEditEngine->SetParaAttribs(nPara, rSet);
+}
+
+SfxItemPool* WeldTextForwarder::GetPool() const
+{
+ EditEngine* pEditEngine = m_rEditAcc.GetEditEngine();
+ return pEditEngine ? pEditEngine->GetEmptyItemSet().GetPool() : nullptr;
+}
+
+void WeldTextForwarder::RemoveAttribs(const ESelection& rSelection)
+{
+ EditEngine* pEditEngine = m_rEditAcc.GetEditEngine();
+ if (pEditEngine)
+ pEditEngine->RemoveAttribs(rSelection, false /*bRemoveParaAttribs*/, 0);
+}
+
+void WeldTextForwarder::GetPortions(sal_Int32 nPara, std::vector<sal_Int32>& rList) const
+{
+ EditEngine* pEditEngine = m_rEditAcc.GetEditEngine();
+ if (pEditEngine)
+ pEditEngine->GetPortions(nPara, rList);
+}
+
+void WeldTextForwarder::QuickInsertText(const OUString& rText, const ESelection& rSel)
+{
+ EditEngine* pEditEngine = m_rEditAcc.GetEditEngine();
+ if (pEditEngine)
+ pEditEngine->QuickInsertText(rText, rSel);
+}
+
+void WeldTextForwarder::QuickInsertLineBreak(const ESelection& rSel)
+{
+ EditEngine* pEditEngine = m_rEditAcc.GetEditEngine();
+ if (pEditEngine)
+ pEditEngine->QuickInsertLineBreak(rSel);
+}
+
+void WeldTextForwarder::QuickInsertField(const SvxFieldItem& rFld, const ESelection& rSel)
+{
+ EditEngine* pEditEngine = m_rEditAcc.GetEditEngine();
+ if (pEditEngine)
+ pEditEngine->QuickInsertField(rFld, rSel);
+}
+
+void WeldTextForwarder::QuickSetAttribs(const SfxItemSet& rSet, const ESelection& rSel)
+{
+ EditEngine* pEditEngine = m_rEditAcc.GetEditEngine();
+ if (pEditEngine)
+ pEditEngine->QuickSetAttribs(rSet, rSel);
+}
+
+bool WeldTextForwarder::IsValid() const
+{
+ EditEngine* pEditEngine = m_rEditAcc.GetEditEngine();
+ // cannot reliably query EditEngine state
+ // while in the middle of an update
+ return pEditEngine && pEditEngine->IsUpdateLayout();
+}
+
+OUString WeldTextForwarder::CalcFieldValue(const SvxFieldItem& rField, sal_Int32 nPara,
+ sal_Int32 nPos, std::optional<Color>& rpTxtColor,
+ std::optional<Color>& rpFldColor)
+{
+ EditEngine* pEditEngine = m_rEditAcc.GetEditEngine();
+ return pEditEngine ? pEditEngine->CalcFieldValue(rField, nPara, nPos, rpTxtColor, rpFldColor)
+ : OUString();
+}
+
+void WeldTextForwarder::FieldClicked(const SvxFieldItem&) {}
+
+static SfxItemState GetSvxEditEngineItemState(EditEngine const& rEditEngine, const ESelection& rSel,
+ sal_uInt16 nWhich)
+{
+ std::vector<EECharAttrib> aAttribs;
+
+ const SfxPoolItem* pLastItem = nullptr;
+
+ SfxItemState eState = SfxItemState::DEFAULT;
+
+ // check all paragraphs inside the selection
+ for (sal_Int32 nPara = rSel.nStartPara; nPara <= rSel.nEndPara; nPara++)
+ {
+ SfxItemState eParaState = SfxItemState::DEFAULT;
+
+ // calculate start and endpos for this paragraph
+ sal_Int32 nPos = 0;
+ if (rSel.nStartPara == nPara)
+ nPos = rSel.nStartPos;
+
+ sal_Int32 nEndPos = rSel.nEndPos;
+ if (rSel.nEndPara != nPara)
+ nEndPos = rEditEngine.GetTextLen(nPara);
+
+ // get list of char attribs
+ rEditEngine.GetCharAttribs(nPara, aAttribs);
+
+ bool bEmpty = true; // we found no item inside the selection of this paragraph
+ bool bGaps = false; // we found items but there are gaps between them
+ sal_Int32 nLastEnd = nPos;
+
+ const SfxPoolItem* pParaItem = nullptr;
+
+ for (const auto& rAttrib : aAttribs)
+ {
+ OSL_ENSURE(rAttrib.pAttr, "GetCharAttribs gives corrupt data");
+
+ const bool bEmptyPortion = (rAttrib.nStart == rAttrib.nEnd);
+ if ((!bEmptyPortion && (rAttrib.nStart >= nEndPos))
+ || (bEmptyPortion && (rAttrib.nStart > nEndPos)))
+ break; // break if we are already behind our selection
+
+ if ((!bEmptyPortion && (rAttrib.nEnd <= nPos))
+ || (bEmptyPortion && (rAttrib.nEnd < nPos)))
+ continue; // or if the attribute ends before our selection
+
+ if (rAttrib.pAttr->Which() != nWhich)
+ continue; // skip if is not the searched item
+
+ // if we already found an item
+ if (pParaItem)
+ {
+ // ... and its different to this one than the state is don't care
+ if (*pParaItem != *(rAttrib.pAttr))
+ return SfxItemState::DONTCARE;
+ }
+ else
+ {
+ pParaItem = rAttrib.pAttr;
+ }
+
+ if (bEmpty)
+ bEmpty = false;
+
+ if (!bGaps && rAttrib.nStart > nLastEnd)
+ bGaps = true;
+
+ nLastEnd = rAttrib.nEnd;
+ }
+
+ if (!bEmpty && !bGaps && nLastEnd < (nEndPos - 1))
+ bGaps = true;
+ if (bEmpty)
+ eParaState = SfxItemState::DEFAULT;
+ else if (bGaps)
+ eParaState = SfxItemState::DONTCARE;
+ else
+ eParaState = SfxItemState::SET;
+
+ // if we already found an item check if we found the same
+ if (pLastItem)
+ {
+ if ((pParaItem == nullptr) || (*pLastItem != *pParaItem))
+ return SfxItemState::DONTCARE;
+ }
+ else
+ {
+ pLastItem = pParaItem;
+ eState = eParaState;
+ }
+ }
+
+ return eState;
+}
+
+SfxItemState WeldTextForwarder::GetItemState(const ESelection& rSel, sal_uInt16 nWhich) const
+{
+ SfxItemState nState = SfxItemState::DISABLED;
+ EditEngine* pEditEngine = m_rEditAcc.GetEditEngine();
+ if (pEditEngine)
+ nState = GetSvxEditEngineItemState(*pEditEngine, rSel, nWhich);
+ return nState;
+}
+
+SfxItemState WeldTextForwarder::GetItemState(sal_Int32 nPara, sal_uInt16 nWhich) const
+{
+ SfxItemState nState = SfxItemState::DISABLED;
+ EditEngine* pEditEngine = m_rEditAcc.GetEditEngine();
+ if (pEditEngine)
+ {
+ const SfxItemSet& rSet = pEditEngine->GetParaAttribs(nPara);
+ nState = rSet.GetItemState(nWhich);
+ }
+ return nState;
+}
+
+LanguageType WeldTextForwarder::GetLanguage(sal_Int32 nPara, sal_Int32 nIndex) const
+{
+ EditEngine* pEditEngine = m_rEditAcc.GetEditEngine();
+ return pEditEngine ? pEditEngine->GetLanguage(nPara, nIndex).nLang : LANGUAGE_NONE;
+}
+
+sal_Int32 WeldTextForwarder::GetFieldCount(sal_Int32 nPara) const
+{
+ EditEngine* pEditEngine = m_rEditAcc.GetEditEngine();
+ return pEditEngine ? pEditEngine->GetFieldCount(nPara) : 0;
+}
+
+EFieldInfo WeldTextForwarder::GetFieldInfo(sal_Int32 nPara, sal_uInt16 nField) const
+{
+ EditEngine* pEditEngine = m_rEditAcc.GetEditEngine();
+ return pEditEngine ? pEditEngine->GetFieldInfo(nPara, nField) : EFieldInfo();
+}
+
+EBulletInfo WeldTextForwarder::GetBulletInfo(sal_Int32 /*nPara*/) const { return EBulletInfo(); }
+
+tools::Rectangle WeldTextForwarder::GetCharBounds(sal_Int32 nPara, sal_Int32 nIndex) const
+{
+ tools::Rectangle aRect(0, 0, 0, 0);
+ EditEngine* pEditEngine = m_rEditAcc.GetEditEngine();
+
+ if (pEditEngine)
+ {
+ // Handle virtual position one-past-the end of the string
+ if (nIndex >= pEditEngine->GetTextLen(nPara))
+ {
+ if (nIndex)
+ aRect = pEditEngine->GetCharacterBounds(EPosition(nPara, nIndex - 1));
+
+ aRect.Move(aRect.Right() - aRect.Left(), 0);
+ aRect.SetSize(Size(1, pEditEngine->GetTextHeight()));
+ }
+ else
+ {
+ aRect = pEditEngine->GetCharacterBounds(EPosition(nPara, nIndex));
+ }
+ }
+ return aRect;
+}
+
+tools::Rectangle WeldTextForwarder::GetParaBounds(sal_Int32 nPara) const
+{
+ tools::Rectangle aRect(0, 0, 0, 0);
+ EditEngine* pEditEngine = m_rEditAcc.GetEditEngine();
+
+ if (pEditEngine)
+ {
+ const Point aPnt = pEditEngine->GetDocPosTopLeft(nPara);
+ const sal_Int32 nWidth = pEditEngine->CalcTextWidth();
+ const sal_Int32 nHeight = pEditEngine->GetTextHeight(nPara);
+ aRect = tools::Rectangle(aPnt.X(), aPnt.Y(), aPnt.X() + nWidth, aPnt.Y() + nHeight);
+ }
+
+ return aRect;
+}
+
+MapMode WeldTextForwarder::GetMapMode() const
+{
+ EditEngine* pEditEngine = m_rEditAcc.GetEditEngine();
+ return pEditEngine ? pEditEngine->GetRefMapMode() : MapMode(MapUnit::Map100thMM);
+}
+
+OutputDevice* WeldTextForwarder::GetRefDevice() const
+{
+ EditEngine* pEditEngine = m_rEditAcc.GetEditEngine();
+ return pEditEngine ? pEditEngine->GetRefDevice() : nullptr;
+}
+
+bool WeldTextForwarder::GetIndexAtPoint(const Point& rPos, sal_Int32& nPara,
+ sal_Int32& nIndex) const
+{
+ bool bRes = false;
+ EditEngine* pEditEngine = m_rEditAcc.GetEditEngine();
+ if (pEditEngine)
+ {
+ EPosition aDocPos = pEditEngine->FindDocPosition(rPos);
+ nPara = aDocPos.nPara;
+ nIndex = aDocPos.nIndex;
+ bRes = true;
+ }
+ return bRes;
+}
+
+bool WeldTextForwarder::GetWordIndices(sal_Int32 nPara, sal_Int32 nIndex, sal_Int32& nStart,
+ sal_Int32& nEnd) const
+{
+ bool bRes = false;
+ EditEngine* pEditEngine = m_rEditAcc.GetEditEngine();
+ if (pEditEngine)
+ {
+ ESelection aRes = pEditEngine->GetWord(ESelection(nPara, nIndex, nPara, nIndex),
+ css::i18n::WordType::DICTIONARY_WORD);
+
+ if (aRes.nStartPara == nPara && aRes.nStartPara == aRes.nEndPara)
+ {
+ nStart = aRes.nStartPos;
+ nEnd = aRes.nEndPos;
+
+ bRes = true;
+ }
+ }
+
+ return bRes;
+}
+
+bool WeldTextForwarder::GetAttributeRun(sal_Int32& nStartIndex, sal_Int32& nEndIndex,
+ sal_Int32 nPara, sal_Int32 nIndex, bool bInCell) const
+{
+ EditEngine* pEditEngine = m_rEditAcc.GetEditEngine();
+ if (!pEditEngine)
+ return false;
+ SvxEditSourceHelper::GetAttributeRun(nStartIndex, nEndIndex, *pEditEngine, nPara, nIndex,
+ bInCell);
+ return true;
+}
+
+sal_Int32 WeldTextForwarder::GetLineCount(sal_Int32 nPara) const
+{
+ EditEngine* pEditEngine = m_rEditAcc.GetEditEngine();
+ return pEditEngine ? pEditEngine->GetLineCount(nPara) : 0;
+}
+
+sal_Int32 WeldTextForwarder::GetLineLen(sal_Int32 nPara, sal_Int32 nLine) const
+{
+ EditEngine* pEditEngine = m_rEditAcc.GetEditEngine();
+ return pEditEngine ? pEditEngine->GetLineLen(nPara, nLine) : 0;
+}
+
+void WeldTextForwarder::GetLineBoundaries(/*out*/ sal_Int32& rStart, /*out*/ sal_Int32& rEnd,
+ sal_Int32 nPara, sal_Int32 nLine) const
+{
+ EditEngine* pEditEngine = m_rEditAcc.GetEditEngine();
+ if (pEditEngine)
+ pEditEngine->GetLineBoundaries(rStart, rEnd, nPara, nLine);
+ else
+ rStart = rEnd = 0;
+}
+
+sal_Int32 WeldTextForwarder::GetLineNumberAtIndex(sal_Int32 nPara, sal_Int32 nIndex) const
+{
+ EditEngine* pEditEngine = m_rEditAcc.GetEditEngine();
+ return pEditEngine ? pEditEngine->GetLineNumberAtIndex(nPara, nIndex) : 0;
+}
+
+bool WeldTextForwarder::QuickFormatDoc(bool /*bFull*/)
+{
+ bool bRes = false;
+ EditEngine* pEditEngine = m_rEditAcc.GetEditEngine();
+ if (pEditEngine)
+ {
+ pEditEngine->QuickFormatDoc();
+ bRes = true;
+ }
+ return bRes;
+}
+
+sal_Int16 WeldTextForwarder::GetDepth(sal_Int32 /*nPara*/) const
+{
+ // math has no outliner...
+ return -1;
+}
+
+bool WeldTextForwarder::SetDepth(sal_Int32 /*nPara*/, sal_Int16 nNewDepth)
+{
+ // math has no outliner...
+ return -1 == nNewDepth; // is it the value from 'GetDepth' ?
+}
+
+bool WeldTextForwarder::Delete(const ESelection& rSelection)
+{
+ bool bRes = false;
+ EditEngine* pEditEngine = m_rEditAcc.GetEditEngine();
+ if (pEditEngine)
+ {
+ pEditEngine->QuickDelete(rSelection);
+ pEditEngine->QuickFormatDoc();
+ bRes = true;
+ }
+ return bRes;
+}
+
+bool WeldTextForwarder::InsertText(const OUString& rStr, const ESelection& rSelection)
+{
+ bool bRes = false;
+ EditEngine* pEditEngine = m_rEditAcc.GetEditEngine();
+ if (pEditEngine)
+ {
+ pEditEngine->QuickInsertText(rStr, rSelection);
+ pEditEngine->QuickFormatDoc();
+ bRes = true;
+ }
+ return bRes;
+}
+
+const SfxItemSet* WeldTextForwarder::GetEmptyItemSetPtr()
+{
+ const SfxItemSet* pItemSet = nullptr;
+ EditEngine* pEditEngine = m_rEditAcc.GetEditEngine();
+ if (pEditEngine)
+ {
+ pItemSet = &pEditEngine->GetEmptyItemSet();
+ }
+ return pItemSet;
+}
+
+void WeldTextForwarder::AppendParagraph()
+{
+ // append an empty paragraph
+ EditEngine* pEditEngine = m_rEditAcc.GetEditEngine();
+ if (pEditEngine)
+ {
+ sal_Int32 nParaCount = pEditEngine->GetParagraphCount();
+ pEditEngine->InsertParagraph(nParaCount, OUString());
+ }
+}
+
+sal_Int32 WeldTextForwarder::AppendTextPortion(sal_Int32 nPara, const OUString& rText,
+ const SfxItemSet& rSet)
+{
+ sal_uInt16 nRes = 0;
+ EditEngine* pEditEngine = m_rEditAcc.GetEditEngine();
+ if (pEditEngine && nPara < pEditEngine->GetParagraphCount())
+ {
+ // append text
+ ESelection aSel(nPara, pEditEngine->GetTextLen(nPara));
+ pEditEngine->QuickInsertText(rText, aSel);
+
+ // set attributes for new appended text
+ nRes = aSel.nEndPos = pEditEngine->GetTextLen(nPara);
+ pEditEngine->QuickSetAttribs(rSet, aSel);
+ }
+ return nRes;
+}
+
+void WeldTextForwarder::CopyText(const SvxTextForwarder& rSource)
+{
+ const WeldTextForwarder* pSourceForwarder = dynamic_cast<const WeldTextForwarder*>(&rSource);
+ if (!pSourceForwarder)
+ return;
+ EditEngine* pSourceEditEngine = pSourceForwarder->m_rEditAcc.GetEditEngine();
+ EditEngine* pEditEngine = m_rEditAcc.GetEditEngine();
+ if (pEditEngine && pSourceEditEngine)
+ {
+ std::unique_ptr<EditTextObject> pNewTextObject = pSourceEditEngine->CreateTextObject();
+ pEditEngine->SetText(*pNewTextObject);
+ }
+}
+
+WeldEditViewForwarder::WeldEditViewForwarder(WeldEditAccessible& rAcc)
+ : m_rEditAcc(rAcc)
+{
+}
+
+bool WeldEditViewForwarder::IsValid() const { return m_rEditAcc.GetEditView() != nullptr; }
+
+Point WeldEditViewForwarder::LogicToPixel(const Point& rPoint, const MapMode& rMapMode) const
+{
+ EditView* pEditView = m_rEditAcc.GetEditView();
+ if (!pEditView)
+ return Point();
+ OutputDevice& rOutDev = pEditView->GetOutputDevice();
+ MapMode aMapMode(rOutDev.GetMapMode());
+ Point aPoint(OutputDevice::LogicToLogic(rPoint, rMapMode, MapMode(aMapMode.GetMapUnit())));
+ aMapMode.SetOrigin(Point());
+ return rOutDev.LogicToPixel(aPoint, aMapMode);
+}
+
+Point WeldEditViewForwarder::PixelToLogic(const Point& rPoint, const MapMode& rMapMode) const
+{
+ EditView* pEditView = m_rEditAcc.GetEditView();
+ if (!pEditView)
+ return Point();
+ OutputDevice& rOutDev = pEditView->GetOutputDevice();
+ MapMode aMapMode(rOutDev.GetMapMode());
+ aMapMode.SetOrigin(Point());
+ Point aPoint(rOutDev.PixelToLogic(rPoint, aMapMode));
+ return OutputDevice::LogicToLogic(aPoint, MapMode(aMapMode.GetMapUnit()), rMapMode);
+}
+
+bool WeldEditViewForwarder::GetSelection(ESelection& rSelection) const
+{
+ bool bRes = false;
+ EditView* pEditView = m_rEditAcc.GetEditView();
+ if (pEditView)
+ {
+ rSelection = pEditView->GetSelection();
+ bRes = true;
+ }
+ return bRes;
+}
+
+bool WeldEditViewForwarder::SetSelection(const ESelection& rSelection)
+{
+ bool bRes = false;
+ EditView* pEditView = m_rEditAcc.GetEditView();
+ if (pEditView)
+ {
+ pEditView->SetSelection(rSelection);
+ bRes = true;
+ }
+ return bRes;
+}
+
+bool WeldEditViewForwarder::Copy()
+{
+ bool bRes = false;
+ EditView* pEditView = m_rEditAcc.GetEditView();
+ if (pEditView)
+ {
+ pEditView->Copy();
+ bRes = true;
+ }
+ return bRes;
+}
+
+bool WeldEditViewForwarder::Cut()
+{
+ bool bRes = false;
+ EditView* pEditView = m_rEditAcc.GetEditView();
+ if (pEditView)
+ {
+ pEditView->Cut();
+ bRes = true;
+ }
+ return bRes;
+}
+
+bool WeldEditViewForwarder::Paste()
+{
+ bool bRes = false;
+ EditView* pEditView = m_rEditAcc.GetEditView();
+ if (pEditView)
+ {
+ pEditView->Paste();
+ bRes = true;
+ }
+ return bRes;
+}
+
+void WeldEditView::SetDrawingArea(weld::DrawingArea* pDrawingArea)
+{
+ Size aSize(pDrawingArea->get_size_request());
+ if (aSize.Width() == -1)
+ aSize.setWidth(500);
+ if (aSize.Height() == -1)
+ aSize.setHeight(100);
+ pDrawingArea->set_size_request(aSize.Width(), aSize.Height());
+
+ SetOutputSizePixel(aSize);
+
+ weld::CustomWidgetController::SetDrawingArea(pDrawingArea);
+
+ EnableRTL(false);
+
+ const StyleSettings& rStyleSettings = Application::GetSettings().GetStyleSettings();
+ Color aBgColor = rStyleSettings.GetWindowColor();
+
+ OutputDevice& rDevice = pDrawingArea->get_ref_device();
+
+ rDevice.SetMapMode(MapMode(MapUnit::MapTwip));
+ rDevice.SetBackground(aBgColor);
+
+ Size aOutputSize(rDevice.PixelToLogic(aSize));
+
+ makeEditEngine();
+ m_xEditEngine->SetPaperSize(aOutputSize);
+ m_xEditEngine->SetRefDevice(&rDevice);
+
+ m_xEditEngine->SetControlWord(m_xEditEngine->GetControlWord() | EEControlBits::MARKFIELDS);
+
+ m_xEditView.reset(new EditView(m_xEditEngine.get(), nullptr));
+ m_xEditView->setEditViewCallbacks(this);
+ m_xEditView->SetOutputArea(tools::Rectangle(Point(0, 0), aOutputSize));
+
+ m_xEditView->SetBackgroundColor(aBgColor);
+ m_xEditEngine->SetBackgroundColor(aBgColor);
+ m_xEditEngine->InsertView(m_xEditView.get());
+
+ pDrawingArea->set_cursor(PointerStyle::Text);
+
+#if !ENABLE_WASM_STRIP_ACCESSIBILITY
+ InitAccessible();
+#endif
+}
+
+#if !ENABLE_WASM_STRIP_ACCESSIBILITY
+void WeldEditView::InitAccessible()
+{
+ if (m_xAccessible.is())
+ m_xAccessible->Init(GetEditEngine(), GetEditView());
+}
+#endif
+
+int WeldEditView::GetSurroundingText(OUString& rSurrounding)
+{
+ EditView* pEditView = GetEditView();
+ if (!pEditView)
+ return -1;
+ rSurrounding = pEditView->GetSurroundingText();
+ return pEditView->GetSurroundingTextSelection().Min();
+}
+
+bool WeldEditView::DeleteSurroundingText(const Selection& rRange)
+{
+ EditView* pEditView = GetEditView();
+ if (!pEditView)
+ return false;
+ return pEditView->DeleteSurroundingText(rRange);
+}
+
+void WeldEditView::GetFocus()
+{
+ EditView* pEditView = GetEditView();
+ if (pEditView)
+ {
+ pEditView->ShowCursor(false);
+ Invalidate(); // redraw with cursor
+ }
+
+ weld::CustomWidgetController::GetFocus();
+
+#if !ENABLE_WASM_STRIP_ACCESSIBILITY
+ if (m_xAccessible.is())
+ {
+ // Note: will implicitly send the AccessibleStateType::FOCUSED event
+ ::accessibility::AccessibleTextHelper* pHelper = m_xAccessible->GetTextHelper();
+ if (pHelper)
+ pHelper->SetFocus();
+ }
+#endif
+}
+
+void WeldEditView::LoseFocus()
+{
+ weld::CustomWidgetController::LoseFocus();
+ Invalidate(); // redraw without cursor
+
+#if !ENABLE_WASM_STRIP_ACCESSIBILITY
+ if (m_xAccessible.is())
+ {
+ // Note: will implicitly send the AccessibleStateType::FOCUSED event
+ ::accessibility::AccessibleTextHelper* pHelper = m_xAccessible->GetTextHelper();
+ if (pHelper)
+ pHelper->SetFocus(false);
+ }
+#endif
+}
+
+bool WeldEditView::CanFocus() const { return true; }
+
+css::uno::Reference<css::datatransfer::dnd::XDropTarget> WeldEditView::GetDropTarget()
+{
+ if (!m_xDropTarget)
+ m_xDropTarget = weld::CustomWidgetController::GetDropTarget();
+ return m_xDropTarget;
+}
+
+css::uno::Reference<css::datatransfer::clipboard::XClipboard> WeldEditView::GetClipboard() const
+{
+ return weld::CustomWidgetController::GetClipboard();
+}
+
+namespace
+{
+class WeldEditViewUIObject final : public DrawingAreaUIObject
+{
+private:
+ WeldEditView* mpEditView;
+
+public:
+ WeldEditViewUIObject(const VclPtr<vcl::Window>& rDrawingArea)
+ : DrawingAreaUIObject(rDrawingArea)
+ , mpEditView(static_cast<WeldEditView*>(mpController))
+ {
+ }
+
+ static std::unique_ptr<UIObject> create(vcl::Window* pWindow)
+ {
+ return std::unique_ptr<UIObject>(new WeldEditViewUIObject(pWindow));
+ }
+
+ virtual StringMap get_state() override
+ {
+ StringMap aMap = WindowUIObject::get_state();
+ aMap["Text"] = mpEditView->GetText();
+ return aMap;
+ }
+
+private:
+ virtual OUString get_name() const override { return "WeldEditViewUIObject"; }
+};
+}
+
+FactoryFunction WeldEditView::GetUITestFactory() const { return WeldEditViewUIObject::create; }
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/engine3d/camera3d.cxx b/svx/source/engine3d/camera3d.cxx
new file mode 100644
index 000000000..30a1b09fb
--- /dev/null
+++ b/svx/source/engine3d/camera3d.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 <svx/camera3d.hxx>
+
+Camera3D::Camera3D(const basegfx::B3DPoint& rPos, const basegfx::B3DPoint& rLookAt,
+ double fFocalLen)
+ : fBankAngle(0)
+ , bAutoAdjustProjection(true)
+{
+ SetPosition(rPos);
+ SetLookAt(rLookAt);
+ SetFocalLength(fFocalLen);
+}
+
+Camera3D::Camera3D()
+ : fFocalLength(35.0)
+ , fBankAngle(0.0)
+ , bAutoAdjustProjection(false)
+{
+}
+
+// Set ViewWindow and adjust PRP
+
+void Camera3D::SetViewWindow(double fX, double fY, double fW, double fH)
+{
+ Viewport3D::SetViewWindow(fX, fY, fW, fH);
+ if (bAutoAdjustProjection)
+ SetFocalLength(fFocalLength);
+}
+
+void Camera3D::SetPosition(const basegfx::B3DPoint& rNewPos)
+{
+ if (rNewPos != aPosition)
+ {
+ aPosition = rNewPos;
+ SetVRP(aPosition);
+ SetVPN(aPosition - aLookAt);
+ SetBankAngle(fBankAngle);
+ }
+}
+
+void Camera3D::SetLookAt(const basegfx::B3DPoint& rNewLookAt)
+{
+ if (rNewLookAt != aLookAt)
+ {
+ aLookAt = rNewLookAt;
+ SetVPN(aPosition - aLookAt);
+ SetBankAngle(fBankAngle);
+ }
+}
+
+void Camera3D::SetPosAndLookAt(const basegfx::B3DPoint& rNewPos,
+ const basegfx::B3DPoint& rNewLookAt)
+{
+ if (rNewPos != aPosition || rNewLookAt != aLookAt)
+ {
+ aPosition = rNewPos;
+ aLookAt = rNewLookAt;
+
+ SetVRP(aPosition);
+ SetVPN(aPosition - aLookAt);
+ SetBankAngle(fBankAngle);
+ }
+}
+
+void Camera3D::SetBankAngle(double fAngle)
+{
+ basegfx::B3DVector aDiff(aPosition - aLookAt);
+ basegfx::B3DVector aPrj(aDiff);
+ fBankAngle = fAngle;
+
+ if (aDiff.getY() == 0)
+ {
+ aPrj.setY(-1.0);
+ }
+ else
+ { // aPrj = Projection from aDiff on the XZ-plane
+ aPrj.setY(0.0);
+
+ if (aDiff.getY() < 0.0)
+ {
+ aPrj = -aPrj;
+ }
+ }
+
+ // Calculate from aDiff to upwards pointing View-Up-Vector
+ // duplicated line is intentional!
+ aPrj = aPrj.getPerpendicular(aDiff);
+ aPrj = aPrj.getPerpendicular(aDiff);
+ aDiff.normalize();
+
+ // Rotate on Z axis, to rotate the BankAngle and back
+ basegfx::B3DHomMatrix aTf;
+ const double fV(sqrt(aDiff.getY() * aDiff.getY() + aDiff.getZ() * aDiff.getZ()));
+
+ if (fV != 0.0)
+ {
+ basegfx::B3DHomMatrix aTemp;
+ const double fSin(aDiff.getY() / fV);
+ const double fCos(aDiff.getZ() / fV);
+
+ aTemp.set(1, 1, fCos);
+ aTemp.set(2, 2, fCos);
+ aTemp.set(2, 1, fSin);
+ aTemp.set(1, 2, -fSin);
+
+ aTf *= aTemp;
+ }
+
+ {
+ basegfx::B3DHomMatrix aTemp;
+ const double fSin(-aDiff.getX());
+ const double fCos(fV);
+
+ aTemp.set(0, 0, fCos);
+ aTemp.set(2, 2, fCos);
+ aTemp.set(0, 2, fSin);
+ aTemp.set(2, 0, -fSin);
+
+ aTf *= aTemp;
+ }
+
+ aTf.rotate(0.0, 0.0, fBankAngle);
+
+ {
+ basegfx::B3DHomMatrix aTemp;
+ const double fSin(aDiff.getX());
+ const double fCos(fV);
+
+ aTemp.set(0, 0, fCos);
+ aTemp.set(2, 2, fCos);
+ aTemp.set(0, 2, fSin);
+ aTemp.set(2, 0, -fSin);
+
+ aTf *= aTemp;
+ }
+
+ if (fV != 0.0)
+ {
+ basegfx::B3DHomMatrix aTemp;
+ const double fSin(-aDiff.getY() / fV);
+ const double fCos(aDiff.getZ() / fV);
+
+ aTemp.set(1, 1, fCos);
+ aTemp.set(2, 2, fCos);
+ aTemp.set(2, 1, fSin);
+ aTemp.set(1, 2, -fSin);
+
+ aTf *= aTemp;
+ }
+
+ SetVUV(aTf * aPrj);
+}
+
+void Camera3D::SetFocalLength(double fLen)
+{
+ if (fLen < 5)
+ fLen = 5;
+ SetPRP(basegfx::B3DPoint(0.0, 0.0, fLen / 35.0 * aViewWin.W));
+ fFocalLength = fLen;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/engine3d/cube3d.cxx b/svx/source/engine3d/cube3d.cxx
new file mode 100644
index 000000000..020d4c85e
--- /dev/null
+++ b/svx/source/engine3d/cube3d.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 <svx/strings.hrc>
+#include <svx/deflt3d.hxx>
+#include <svx/dialmgr.hxx>
+#include <svx/cube3d.hxx>
+#include <svx/svdobjkind.hxx>
+#include <basegfx/point/b3dpoint.hxx>
+#include <sdr/contact/viewcontactofe3dcube.hxx>
+
+
+// DrawContact section
+
+std::unique_ptr<sdr::contact::ViewContact> E3dCubeObj::CreateObjectSpecificViewContact()
+{
+ return std::make_unique<sdr::contact::ViewContactOfE3dCube>(*this);
+}
+
+
+E3dCubeObj::E3dCubeObj(
+ SdrModel& rSdrModel,
+ const E3dDefaultAttributes& rDefault,
+ const basegfx::B3DPoint& aPos,
+ const basegfx::B3DVector& r3DSize)
+: E3dCompoundObject(rSdrModel)
+{
+ // Set Defaults
+ SetDefaultAttributes(rDefault);
+
+ // position centre or left, bottom, back (dependent on bPosIsCenter)
+ aCubePos = aPos;
+ aCubeSize = r3DSize;
+}
+
+E3dCubeObj::E3dCubeObj(SdrModel& rSdrModel)
+: E3dCompoundObject(rSdrModel)
+{
+ // Set Defaults
+ const E3dDefaultAttributes aDefault;
+
+ SetDefaultAttributes(aDefault);
+}
+
+E3dCubeObj::E3dCubeObj(SdrModel& rSdrModel, E3dCubeObj const & rSource)
+: E3dCompoundObject(rSdrModel, rSource)
+{
+ // Set Defaults
+ const E3dDefaultAttributes aDefault;
+
+ SetDefaultAttributes(aDefault);
+
+ aCubePos = rSource.aCubePos;
+ aCubeSize = rSource.aCubeSize;
+ bPosIsCenter = rSource.bPosIsCenter;
+}
+
+E3dCubeObj::~E3dCubeObj()
+{
+}
+
+void E3dCubeObj::SetDefaultAttributes(const E3dDefaultAttributes& rDefault)
+{
+ aCubePos = rDefault.GetDefaultCubePos();
+ aCubeSize = rDefault.GetDefaultCubeSize();
+ bPosIsCenter = rDefault.GetDefaultCubePosIsCenter();
+}
+
+SdrObjKind E3dCubeObj::GetObjIdentifier() const
+{
+ return SdrObjKind::E3D_Cube;
+}
+
+// Convert the object into a group object consisting of 6 polygons
+
+SdrObjectUniquePtr E3dCubeObj::DoConvertToPolyObj(bool /*bBezier*/, bool /*bAddText*/) const
+{
+ return nullptr;
+}
+
+E3dCubeObj* E3dCubeObj::CloneSdrObject(SdrModel& rTargetModel) const
+{
+ return new E3dCubeObj(rTargetModel, *this);
+}
+
+// Set local parameters with geometry re-creating
+
+void E3dCubeObj::SetCubePos(const basegfx::B3DPoint& rNew)
+{
+ if(aCubePos != rNew)
+ {
+ aCubePos = rNew;
+ ActionChanged();
+ }
+}
+
+void E3dCubeObj::SetCubeSize(const basegfx::B3DVector& rNew)
+{
+ if(aCubeSize != rNew)
+ {
+ aCubeSize = rNew;
+ ActionChanged();
+ }
+}
+
+void E3dCubeObj::SetPosIsCenter(bool bNew)
+{
+ if(bPosIsCenter != bNew)
+ {
+ bPosIsCenter = bNew;
+ ActionChanged();
+ }
+}
+
+// Get the name of the object (singular)
+
+OUString E3dCubeObj::TakeObjNameSingul() const
+{
+ OUString sName = SvxResId(STR_ObjNameSingulCube3d);
+
+ OUString aName(GetName());
+ if (!aName.isEmpty())
+ {
+ sName += " \'" + aName + "'";
+ }
+ return sName;
+}
+
+// Get the name of the object (plural)
+
+OUString E3dCubeObj::TakeObjNamePlural() const
+{
+ return SvxResId(STR_ObjNamePluralCube3d);
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/engine3d/deflt3d.cxx b/svx/source/engine3d/deflt3d.cxx
new file mode 100644
index 000000000..89342e676
--- /dev/null
+++ b/svx/source/engine3d/deflt3d.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 <svx/deflt3d.hxx>
+
+// Class to manage the 3D default attributes
+
+E3dDefaultAttributes::E3dDefaultAttributes() { Reset(); }
+
+void E3dDefaultAttributes::Reset()
+{
+ // Cube object
+ aDefaultCubePos = basegfx::B3DPoint(-500.0, -500.0, -500.0);
+ aDefaultCubeSize = basegfx::B3DVector(1000.0, 1000.0, 1000.0);
+ bDefaultCubePosIsCenter = false;
+
+ // Sphere object
+ aDefaultSphereCenter = basegfx::B3DPoint(0.0, 0.0, 0.0);
+ aDefaultSphereSize = basegfx::B3DPoint(1000.0, 1000.0, 1000.0);
+
+ // Lathe object
+ bDefaultLatheSmoothed = true;
+ bDefaultLatheSmoothFrontBack = false;
+ bDefaultLatheCharacterMode = false;
+ bDefaultLatheCloseFront = true;
+ bDefaultLatheCloseBack = true;
+
+ // Extrude object
+ bDefaultExtrudeSmoothed = true;
+ bDefaultExtrudeSmoothFrontBack = false;
+ bDefaultExtrudeCharacterMode = false;
+ bDefaultExtrudeCloseFront = true;
+ bDefaultExtrudeCloseBack = true;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/engine3d/dragmt3d.cxx b/svx/source/engine3d/dragmt3d.cxx
new file mode 100644
index 000000000..c27d95598
--- /dev/null
+++ b/svx/source/engine3d/dragmt3d.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 <dragmt3d.hxx>
+#include <o3tl/numeric.hxx>
+#include <svx/svdpagv.hxx>
+#include <svx/dialmgr.hxx>
+#include <svx/svddrgmt.hxx>
+#include <svx/svdtrans.hxx>
+#include <svx/obj3d.hxx>
+#include <svx/e3dundo.hxx>
+#include <svx/strings.hrc>
+#include <svx/sdr/overlay/overlaypolypolygon.hxx>
+#include <svx/sdr/overlay/overlaymanager.hxx>
+#include <basegfx/polygon/b2dpolypolygontools.hxx>
+#include <svx/sdr/contact/viewcontactofe3dscene.hxx>
+#include <drawinglayer/geometry/viewinformation3d.hxx>
+#include <svx/e3dsceneupdater.hxx>
+#include <vcl/ptrstyle.hxx>
+#include <comphelper/lok.hxx>
+
+
+E3dDragMethod::E3dDragMethod (
+ SdrDragView &_rView,
+ const SdrMarkList& rMark,
+ E3dDragConstraint eConstr,
+ bool bFull)
+: SdrDragMethod(_rView),
+ meConstraint(eConstr),
+ mbMoveFull(bFull),
+ mbMovedAtAll(false)
+{
+ // Create a unit for all the 3D objects present in the selection
+ const size_t nCnt(rMark.GetMarkCount());
+
+ if(mbMoveFull)
+ {
+ // for non-visible 3D objects fallback to wireframe interaction
+ for(size_t nObjs = 0; nObjs < nCnt; ++nObjs)
+ {
+ E3dObject* pE3dObj = dynamic_cast< E3dObject* >(rMark.GetMark(nObjs)->GetMarkedSdrObj());
+
+ if(pE3dObj)
+ {
+ if(!pE3dObj->HasFillStyle() && !pE3dObj->HasLineStyle())
+ {
+ mbMoveFull = false;
+ break;
+ }
+ }
+ }
+ }
+
+ for(size_t nObjs = 0; nObjs < nCnt; ++nObjs)
+ {
+ E3dObject* pE3dObj = dynamic_cast< E3dObject* >(rMark.GetMark(nObjs)->GetMarkedSdrObj());
+
+ if(pE3dObj)
+ {
+ // fill new interaction unit
+ E3dDragMethodUnit aNewUnit(*pE3dObj);
+
+ // get transformations
+ aNewUnit.maInitTransform = aNewUnit.maTransform = pE3dObj->GetTransform();
+
+ if(nullptr != pE3dObj->getParentE3dSceneFromE3dObject())
+ {
+ // get transform between object and world, normally scene transform
+ aNewUnit.maInvDisplayTransform = aNewUnit.maDisplayTransform = pE3dObj->getParentE3dSceneFromE3dObject()->GetFullTransform();
+ aNewUnit.maInvDisplayTransform.invert();
+ }
+
+ if(!mbMoveFull)
+ {
+ // create wireframe visualisation for parent coordinate system
+ aNewUnit.maWireframePoly.clear();
+ aNewUnit.maWireframePoly = pE3dObj->CreateWireframe();
+ aNewUnit.maWireframePoly.transform(aNewUnit.maTransform);
+ }
+
+ // Determine FullBound
+ maFullBound.Union(pE3dObj->GetSnapRect());
+
+ // Insert Unit
+ maGrp.push_back(aNewUnit);
+ }
+ }
+}
+
+OUString E3dDragMethod::GetSdrDragComment() const
+{
+ return OUString();
+}
+
+// Create the wireframe model for all actions
+
+bool E3dDragMethod::BeginSdrDrag()
+{
+ if(E3dDragConstraint::Z == meConstraint)
+ {
+ const sal_uInt32 nCnt(maGrp.size());
+ DragStat().SetRef1( maFullBound.Center() );
+
+ for(sal_uInt32 nOb(0); nOb < nCnt; nOb++)
+ {
+ E3dDragMethodUnit& rCandidate = maGrp[nOb];
+ rCandidate.mnStartAngle = GetAngle(DragStat().GetStart() - DragStat().GetRef1());
+ rCandidate.mnLastAngle = 0_deg100;
+ }
+ }
+ else
+ {
+ maLastPos = DragStat().GetStart();
+ }
+
+ if(!mbMoveFull)
+ {
+ Show();
+ }
+
+ return true;
+}
+
+bool E3dDragMethod::EndSdrDrag(bool /*bCopy*/)
+{
+ const sal_uInt32 nCnt(maGrp.size());
+
+ if(!mbMoveFull)
+ {
+ // Hide wireframe
+ Hide();
+ }
+
+ // Apply all transformations and create undo's
+ if(mbMovedAtAll)
+ {
+ const bool bUndo = getSdrDragView().IsUndoEnabled();
+ if( bUndo )
+ getSdrDragView().BegUndo(SvxResId(RID_SVX_3D_UNDO_ROTATE));
+ sal_uInt32 nOb(0);
+
+ for(nOb=0;nOb<nCnt;nOb++)
+ {
+ E3dDragMethodUnit& rCandidate = maGrp[nOb];
+ E3DModifySceneSnapRectUpdater aUpdater(&rCandidate.mr3DObj);
+ rCandidate.mr3DObj.SetTransform(rCandidate.maTransform);
+ if( bUndo )
+ {
+ getSdrDragView().AddUndo(
+ std::make_unique<E3dRotateUndoAction>(
+ rCandidate.mr3DObj,
+ rCandidate.maInitTransform,
+ rCandidate.maTransform));
+ }
+ }
+ if( bUndo )
+ getSdrDragView().EndUndo();
+ }
+
+ return true;
+}
+
+void E3dDragMethod::CancelSdrDrag()
+{
+ if(mbMoveFull)
+ {
+ if(mbMovedAtAll)
+ {
+ const sal_uInt32 nCnt(maGrp.size());
+
+ for(sal_uInt32 nOb(0); nOb < nCnt; nOb++)
+ {
+ // Restore transformation
+ E3dDragMethodUnit& rCandidate = maGrp[nOb];
+ E3DModifySceneSnapRectUpdater aUpdater(&rCandidate.mr3DObj);
+ rCandidate.mr3DObj.SetTransform(rCandidate.maInitTransform);
+ }
+ }
+ }
+ else
+ {
+ // Hide WireFrame
+ Hide();
+ }
+}
+
+// Common MoveSdrDrag()
+
+void E3dDragMethod::MoveSdrDrag(const Point& /*rPnt*/)
+{
+ mbMovedAtAll = true;
+}
+
+// Draw the wire frame model
+
+// for migration from XOR to overlay
+void E3dDragMethod::CreateOverlayGeometry(
+ sdr::overlay::OverlayManager& rOverlayManager,
+ const sdr::contact::ObjectContact& rObjectContact)
+{
+ // We do client-side object manipulation with the Kit API
+ if (comphelper::LibreOfficeKit::isActive())
+ return;
+
+ const sal_uInt32 nCnt(maGrp.size());
+ basegfx::B2DPolyPolygon aResult;
+
+ for(sal_uInt32 nOb(0); nOb < nCnt; nOb++)
+ {
+ E3dDragMethodUnit& rCandidate = maGrp[nOb];
+ SdrPageView* pPV = getSdrDragView().GetSdrPageView();
+
+ if(pPV && pPV->HasMarkedObjPageView())
+ {
+ const basegfx::B3DPolyPolygon aCandidate(rCandidate.maWireframePoly);
+ const sal_uInt32 nPlyCnt(aCandidate.count());
+
+ if(nPlyCnt)
+ {
+ const E3dScene* pScene(rCandidate.mr3DObj.getRootE3dSceneFromE3dObject());
+
+ if(nullptr != pScene)
+ {
+ const sdr::contact::ViewContactOfE3dScene& rVCScene = static_cast< sdr::contact::ViewContactOfE3dScene& >(pScene->GetViewContact());
+ const drawinglayer::geometry::ViewInformation3D& aViewInfo3D(rVCScene.getViewInformation3D());
+ const basegfx::B3DHomMatrix aWorldToView(aViewInfo3D.getDeviceToView() * aViewInfo3D.getProjection() * aViewInfo3D.getOrientation());
+ const basegfx::B3DHomMatrix aTransform(aWorldToView * rCandidate.maDisplayTransform);
+
+ // transform to relative scene coordinates
+ basegfx::B2DPolyPolygon aPolyPolygon(basegfx::utils::createB2DPolyPolygonFromB3DPolyPolygon(aCandidate, aTransform));
+
+ // transform to 2D view coordinates
+ aPolyPolygon.transform(rVCScene.getObjectTransformation());
+
+ aResult.append(aPolyPolygon);
+ }
+ }
+ }
+ }
+
+ if(aResult.count())
+ {
+ std::unique_ptr<sdr::overlay::OverlayPolyPolygonStripedAndFilled> pNew(
+ new sdr::overlay::OverlayPolyPolygonStripedAndFilled(
+ aResult));
+
+ insertNewlyCreatedOverlayObjectForSdrDragMethod(
+ std::move(pNew),
+ rObjectContact,
+ rOverlayManager);
+ }
+}
+
+
+E3dDragRotate::E3dDragRotate(SdrDragView &_rView,
+ const SdrMarkList& rMark,
+ E3dDragConstraint eConstr,
+ bool bFull)
+: E3dDragMethod(_rView, rMark, eConstr, bFull)
+{
+ // Get center of all selected objects in eye coordinates
+ const sal_uInt32 nCnt(maGrp.size());
+
+ if(!nCnt)
+ return;
+
+ const E3dScene* pScene(maGrp[0].mr3DObj.getRootE3dSceneFromE3dObject());
+
+ if(nullptr == pScene)
+ return;
+
+ const sdr::contact::ViewContactOfE3dScene& rVCScene = static_cast< sdr::contact::ViewContactOfE3dScene& >(pScene->GetViewContact());
+ const drawinglayer::geometry::ViewInformation3D& aViewInfo3D(rVCScene.getViewInformation3D());
+
+ for(sal_uInt32 nOb(0); nOb < nCnt; nOb++)
+ {
+ E3dDragMethodUnit& rCandidate = maGrp[nOb];
+ basegfx::B3DPoint aObjCenter = rCandidate.mr3DObj.GetBoundVolume().getCenter();
+ const basegfx::B3DHomMatrix aTransform(aViewInfo3D.getOrientation() * rCandidate.maDisplayTransform * rCandidate.maInitTransform);
+
+ aObjCenter = aTransform * aObjCenter;
+ maGlobalCenter += aObjCenter;
+ }
+
+ // Divide by the number
+ if(nCnt > 1)
+ {
+ maGlobalCenter /= static_cast<double>(nCnt);
+ }
+
+ // get rotate center and transform to 3D eye coordinates
+ basegfx::B2DPoint aRotCenter2D(Ref1().X(), Ref1().Y());
+
+ // from world to relative scene using inverse getObjectTransformation()
+ basegfx::B2DHomMatrix aInverseObjectTransform(rVCScene.getObjectTransformation());
+ aInverseObjectTransform.invert();
+ aRotCenter2D = aInverseObjectTransform * aRotCenter2D;
+
+ // from 3D view to 3D eye
+ basegfx::B3DPoint aRotCenter3D(aRotCenter2D.getX(), aRotCenter2D.getY(), 0.0);
+ basegfx::B3DHomMatrix aInverseViewToEye(aViewInfo3D.getDeviceToView() * aViewInfo3D.getProjection());
+ aInverseViewToEye.invert();
+ aRotCenter3D = aInverseViewToEye * aRotCenter3D;
+
+// Use X,Y of the RotCenter and depth of the common object centre
+// as rotation point in the space
+ maGlobalCenter.setX(aRotCenter3D.getX());
+ maGlobalCenter.setY(aRotCenter3D.getY());
+}
+
+
+//The object is moved, determine the angle
+
+void E3dDragRotate::MoveSdrDrag(const Point& rPnt)
+{
+ // call parent
+ E3dDragMethod::MoveSdrDrag(rPnt);
+
+ if(!DragStat().CheckMinMoved(rPnt))
+ return;
+
+ // Get modifier
+ sal_uInt16 nModifier = 0;
+ if(auto pDragView = dynamic_cast<const E3dView*>(&getSdrDragView()))
+ {
+ const MouseEvent& rLastMouse = pDragView->GetMouseEvent();
+ nModifier = rLastMouse.GetModifier();
+ }
+
+ // Rotate all objects
+ const sal_uInt32 nCnt(maGrp.size());
+
+ for(sal_uInt32 nOb(0); nOb < nCnt; nOb++)
+ {
+ // Determine rotation angle
+ double fWAngle, fHAngle;
+ E3dDragMethodUnit& rCandidate = maGrp[nOb];
+
+ if(E3dDragConstraint::Z == meConstraint)
+ {
+ Degree100 lastAngle = NormAngle36000(GetAngle(rPnt - DragStat().GetRef1()) -
+ rCandidate.mnStartAngle) - rCandidate.mnLastAngle;
+ rCandidate.mnLastAngle = lastAngle + rCandidate.mnLastAngle;
+ fWAngle = toDegrees(lastAngle);
+ fHAngle = 0.0;
+ }
+ else
+ {
+ if ((maFullBound.GetWidth() == 0) || (maFullBound.GetHeight() == 0))
+ throw o3tl::divide_by_zero();
+ fWAngle = 90.0 * static_cast<double>(rPnt.X() - maLastPos.X())
+ / static_cast<double>(maFullBound.GetWidth());
+ fHAngle = 90.0 * static_cast<double>(rPnt.Y() - maLastPos.Y())
+ / static_cast<double>(maFullBound.GetHeight());
+ }
+ tools::Long nSnap = 0;
+
+ if(!getSdrDragView().IsRotateAllowed())
+ nSnap = 90;
+
+ if(nSnap != 0)
+ {
+ fWAngle = static_cast<double>((static_cast<tools::Long>(fWAngle) + nSnap/2) / nSnap * nSnap);
+ fHAngle = static_cast<double>((static_cast<tools::Long>(fHAngle) + nSnap/2) / nSnap * nSnap);
+ }
+
+ // to radians
+ fWAngle = basegfx::deg2rad(fWAngle);
+ fHAngle = basegfx::deg2rad(fHAngle);
+
+ // Determine transformation
+ basegfx::B3DHomMatrix aRotMat;
+ if(E3dDragConstraint::Y & meConstraint)
+ {
+ if(nModifier & KEY_MOD2)
+ aRotMat.rotate(0.0, 0.0, fWAngle);
+ else
+ aRotMat.rotate(0.0, fWAngle, 0.0);
+ }
+ else if(E3dDragConstraint::Z & meConstraint)
+ {
+ if(nModifier & KEY_MOD2)
+ aRotMat.rotate(0.0, fWAngle, 0.0);
+ else
+ aRotMat.rotate(0.0, 0.0, fWAngle);
+ }
+ if(E3dDragConstraint::X & meConstraint)
+ {
+ aRotMat.rotate(fHAngle, 0.0, 0.0);
+ }
+
+ const E3dScene* pScene(rCandidate.mr3DObj.getRootE3dSceneFromE3dObject());
+
+ if(nullptr != pScene)
+ {
+ // Transformation in eye coordinates, there rotate then and back
+ const sdr::contact::ViewContactOfE3dScene& rVCScene = static_cast< sdr::contact::ViewContactOfE3dScene& >(pScene->GetViewContact());
+ const drawinglayer::geometry::ViewInformation3D& aViewInfo3D(rVCScene.getViewInformation3D());
+ basegfx::B3DHomMatrix aInverseOrientation(aViewInfo3D.getOrientation());
+ aInverseOrientation.invert();
+
+ basegfx::B3DHomMatrix aTransMat(rCandidate.maDisplayTransform);
+ aTransMat *= aViewInfo3D.getOrientation();
+ aTransMat.translate(-maGlobalCenter.getX(), -maGlobalCenter.getY(), -maGlobalCenter.getZ());
+ aTransMat *= aRotMat;
+ aTransMat.translate(maGlobalCenter.getX(), maGlobalCenter.getY(), maGlobalCenter.getZ());
+ aTransMat *= aInverseOrientation;
+ aTransMat *= rCandidate.maInvDisplayTransform;
+
+ // ...and apply
+ rCandidate.maTransform *= aTransMat;
+
+ if(mbMoveFull)
+ {
+ E3DModifySceneSnapRectUpdater aUpdater(&rCandidate.mr3DObj);
+ rCandidate.mr3DObj.SetTransform(rCandidate.maTransform);
+ }
+ else
+ {
+ Hide();
+ rCandidate.maWireframePoly.transform(aTransMat);
+ Show();
+ }
+ }
+ }
+ maLastPos = rPnt;
+ DragStat().NextMove(rPnt);
+}
+
+PointerStyle E3dDragRotate::GetSdrDragPointer() const
+{
+ return PointerStyle::Rotate;
+}
+
+// E3dDragMove. This drag method is only required for translations inside
+// 3D scenes. If a 3D-scene itself moved, then this drag method will drag
+// not be used.
+
+
+E3dDragMove::E3dDragMove(SdrDragView &_rView,
+ const SdrMarkList& rMark,
+ SdrHdlKind eDrgHdl,
+ E3dDragConstraint eConstr,
+ bool bFull)
+: E3dDragMethod(_rView, rMark, eConstr, bFull),
+ meWhatDragHdl(eDrgHdl)
+{
+ switch(meWhatDragHdl)
+ {
+ case SdrHdlKind::Left:
+ maScaleFixPos = maFullBound.RightCenter();
+ break;
+ case SdrHdlKind::Right:
+ maScaleFixPos = maFullBound.LeftCenter();
+ break;
+ case SdrHdlKind::Upper:
+ maScaleFixPos = maFullBound.BottomCenter();
+ break;
+ case SdrHdlKind::Lower:
+ maScaleFixPos = maFullBound.TopCenter();
+ break;
+ case SdrHdlKind::UpperLeft:
+ maScaleFixPos = maFullBound.BottomRight();
+ break;
+ case SdrHdlKind::UpperRight:
+ maScaleFixPos = maFullBound.BottomLeft();
+ break;
+ case SdrHdlKind::LowerLeft:
+ maScaleFixPos = maFullBound.TopRight();
+ break;
+ case SdrHdlKind::LowerRight:
+ maScaleFixPos = maFullBound.TopLeft();
+ break;
+ default:
+ // Moving the object, SdrHdlKind::Move
+ break;
+ }
+
+ // Override when IsResizeAtCenter()
+ if(getSdrDragView().IsResizeAtCenter())
+ {
+ meWhatDragHdl = SdrHdlKind::User;
+ maScaleFixPos = maFullBound.Center();
+ }
+}
+
+// The object is moved, determine the translations
+
+void E3dDragMove::MoveSdrDrag(const Point& rPnt)
+{
+ // call parent
+ E3dDragMethod::MoveSdrDrag(rPnt);
+
+ if(!DragStat().CheckMinMoved(rPnt))
+ return;
+
+ if(SdrHdlKind::Move == meWhatDragHdl)
+ {
+ // Translation
+ // Determine the motion vector
+ const sal_uInt32 nCnt(maGrp.size());
+
+ // Get modifier
+ sal_uInt16 nModifier(0);
+
+ if(auto pDragView = dynamic_cast<const E3dView*>(&getSdrDragView()))
+ {
+ const MouseEvent& rLastMouse = pDragView->GetMouseEvent();
+ nModifier = rLastMouse.GetModifier();
+ }
+
+ for(sal_uInt32 nOb(0); nOb < nCnt; nOb++)
+ {
+ E3dDragMethodUnit& rCandidate = maGrp[nOb];
+ const E3dScene* pScene(rCandidate.mr3DObj.getRootE3dSceneFromE3dObject());
+
+ if(nullptr != pScene)
+ {
+ const sdr::contact::ViewContactOfE3dScene& rVCScene = static_cast< sdr::contact::ViewContactOfE3dScene& >(pScene->GetViewContact());
+ const drawinglayer::geometry::ViewInformation3D& aViewInfo3D(rVCScene.getViewInformation3D());
+
+ // move coor from 2d world to 3d Eye
+ basegfx::B2DPoint aGlobalMoveHead2D(static_cast<double>(rPnt.X() - maLastPos.X()), static_cast<double>(rPnt.Y() - maLastPos.Y()));
+ basegfx::B2DPoint aGlobalMoveTail2D(0.0, 0.0);
+ basegfx::B2DHomMatrix aInverseSceneTransform(rVCScene.getObjectTransformation());
+
+ aInverseSceneTransform.invert();
+ aGlobalMoveHead2D = aInverseSceneTransform * aGlobalMoveHead2D;
+ aGlobalMoveTail2D = aInverseSceneTransform * aGlobalMoveTail2D;
+
+ basegfx::B3DPoint aMoveHead3D(aGlobalMoveHead2D.getX(), aGlobalMoveHead2D.getY(), 0.5);
+ basegfx::B3DPoint aMoveTail3D(aGlobalMoveTail2D.getX(), aGlobalMoveTail2D.getY(), 0.5);
+ basegfx::B3DHomMatrix aInverseViewToEye(aViewInfo3D.getDeviceToView() * aViewInfo3D.getProjection());
+ aInverseViewToEye.invert();
+
+ aMoveHead3D = aInverseViewToEye * aMoveHead3D;
+ aMoveTail3D = aInverseViewToEye * aMoveTail3D;
+
+ // eventually switch movement from XY to XZ plane
+ if(nModifier & KEY_MOD2)
+ {
+ double fZwi = aMoveHead3D.getY();
+ aMoveHead3D.setY(aMoveHead3D.getZ());
+ aMoveHead3D.setZ(fZwi);
+
+ fZwi = aMoveTail3D.getY();
+ aMoveTail3D.setY(aMoveTail3D.getZ());
+ aMoveTail3D.setZ(fZwi);
+ }
+
+ // Motion vector from eye coordinates to parent coordinates
+ basegfx::B3DHomMatrix aInverseOrientation(aViewInfo3D.getOrientation());
+ aInverseOrientation.invert();
+ basegfx::B3DHomMatrix aCompleteTrans(rCandidate.maInvDisplayTransform * aInverseOrientation);
+
+ aMoveHead3D = aCompleteTrans * aMoveHead3D;
+ aMoveTail3D = aCompleteTrans* aMoveTail3D;
+
+ // build transformation
+ basegfx::B3DHomMatrix aTransMat;
+ basegfx::B3DPoint aTranslate(aMoveHead3D - aMoveTail3D);
+ aTransMat.translate(aTranslate.getX(), aTranslate.getY(), aTranslate.getZ());
+
+ // ...and apply
+ rCandidate.maTransform *= aTransMat;
+
+ if(mbMoveFull)
+ {
+ E3DModifySceneSnapRectUpdater aUpdater(&rCandidate.mr3DObj);
+ rCandidate.mr3DObj.SetTransform(rCandidate.maTransform);
+ }
+ else
+ {
+ Hide();
+ rCandidate.maWireframePoly.transform(aTransMat);
+ Show();
+ }
+ }
+ }
+ }
+ else
+ {
+ // Scaling
+ // Determine scaling vector
+ Point aStartPos = DragStat().GetStart();
+ const sal_uInt32 nCnt(maGrp.size());
+
+ for(sal_uInt32 nOb(0); nOb < nCnt; nOb++)
+ {
+ E3dDragMethodUnit& rCandidate = maGrp[nOb];
+ const basegfx::B3DPoint aObjectCenter(rCandidate.mr3DObj.GetBoundVolume().getCenter());
+ const E3dScene* pScene(rCandidate.mr3DObj.getRootE3dSceneFromE3dObject());
+
+ if(nullptr != pScene)
+ {
+ // transform from 2D world view to 3D eye
+ const sdr::contact::ViewContactOfE3dScene& rVCScene = static_cast< sdr::contact::ViewContactOfE3dScene& >(pScene->GetViewContact());
+ const drawinglayer::geometry::ViewInformation3D& aViewInfo3D(rVCScene.getViewInformation3D());
+
+ basegfx::B2DPoint aGlobalScaleStart2D(static_cast<double>(aStartPos.X()), static_cast<double>(aStartPos.Y()));
+ basegfx::B2DPoint aGlobalScaleNext2D(static_cast<double>(rPnt.X()), static_cast<double>(rPnt.Y()));
+ basegfx::B2DPoint aGlobalScaleFixPos2D(static_cast<double>(maScaleFixPos.X()), static_cast<double>(maScaleFixPos.Y()));
+ basegfx::B2DHomMatrix aInverseSceneTransform(rVCScene.getObjectTransformation());
+
+ aInverseSceneTransform.invert();
+ aGlobalScaleStart2D = aInverseSceneTransform * aGlobalScaleStart2D;
+ aGlobalScaleNext2D = aInverseSceneTransform * aGlobalScaleNext2D;
+ aGlobalScaleFixPos2D = aInverseSceneTransform * aGlobalScaleFixPos2D;
+
+ basegfx::B3DPoint aGlobalScaleStart3D(aGlobalScaleStart2D.getX(), aGlobalScaleStart2D.getY(), aObjectCenter.getZ());
+ basegfx::B3DPoint aGlobalScaleNext3D(aGlobalScaleNext2D.getX(), aGlobalScaleNext2D.getY(), aObjectCenter.getZ());
+ basegfx::B3DPoint aGlobalScaleFixPos3D(aGlobalScaleFixPos2D.getX(), aGlobalScaleFixPos2D.getY(), aObjectCenter.getZ());
+ basegfx::B3DHomMatrix aInverseViewToEye(aViewInfo3D.getDeviceToView() * aViewInfo3D.getProjection());
+
+ aInverseViewToEye.invert();
+ basegfx::B3DPoint aScStart(aInverseViewToEye * aGlobalScaleStart3D);
+ basegfx::B3DPoint aScNext(aInverseViewToEye * aGlobalScaleNext3D);
+ basegfx::B3DPoint aScFixPos(aInverseViewToEye * aGlobalScaleFixPos3D);
+
+ // constraints?
+ switch(meWhatDragHdl)
+ {
+ case SdrHdlKind::Left:
+ case SdrHdlKind::Right:
+ // to constrain on X -> Y equal
+ aScNext.setY(aScFixPos.getY());
+ break;
+ case SdrHdlKind::Upper:
+ case SdrHdlKind::Lower:
+ // constrain to Y -> X equal
+ aScNext.setX(aScFixPos.getX());
+ break;
+ default:
+ break;
+ }
+
+ // get scale vector in eye coordinates
+ basegfx::B3DPoint aScaleVec(aScStart - aScFixPos);
+ aScaleVec.setZ(1.0);
+
+ if(aScaleVec.getX() != 0.0)
+ {
+ aScaleVec.setX((aScNext.getX() - aScFixPos.getX()) / aScaleVec.getX());
+ }
+ else
+ {
+ aScaleVec.setX(1.0);
+ }
+
+ if(aScaleVec.getY() != 0.0)
+ {
+ aScaleVec.setY((aScNext.getY() - aScFixPos.getY()) / aScaleVec.getY());
+ }
+ else
+ {
+ aScaleVec.setY(1.0);
+ }
+
+ // SHIFT-key used?
+ if(getSdrDragView().IsOrtho())
+ {
+ if(fabs(aScaleVec.getX()) > fabs(aScaleVec.getY()))
+ {
+ // X is biggest
+ aScaleVec.setY(aScaleVec.getX());
+ }
+ else
+ {
+ // Y is biggest
+ aScaleVec.setX(aScaleVec.getY());
+ }
+ }
+
+ // build transformation
+ basegfx::B3DHomMatrix aInverseOrientation(aViewInfo3D.getOrientation());
+ aInverseOrientation.invert();
+
+ basegfx::B3DHomMatrix aNewTrans = rCandidate.maInitTransform;
+ aNewTrans *= rCandidate.maDisplayTransform;
+ aNewTrans *= aViewInfo3D.getOrientation();
+ aNewTrans.translate(-aScFixPos.getX(), -aScFixPos.getY(), -aScFixPos.getZ());
+ aNewTrans.scale(aScaleVec.getX(), aScaleVec.getY(), aScaleVec.getZ());
+ aNewTrans.translate(aScFixPos.getX(), aScFixPos.getY(), aScFixPos.getZ());
+ aNewTrans *= aInverseOrientation;
+ aNewTrans *= rCandidate.maInvDisplayTransform;
+
+ // ...and apply
+ rCandidate.maTransform = aNewTrans;
+
+ if(mbMoveFull)
+ {
+ E3DModifySceneSnapRectUpdater aUpdater(&rCandidate.mr3DObj);
+ rCandidate.mr3DObj.SetTransform(rCandidate.maTransform);
+ }
+ else
+ {
+ Hide();
+ rCandidate.maWireframePoly.clear();
+ rCandidate.maWireframePoly = rCandidate.mr3DObj.CreateWireframe();
+ rCandidate.maWireframePoly.transform(rCandidate.maTransform);
+ Show();
+ }
+ }
+ }
+ }
+ maLastPos = rPnt;
+ DragStat().NextMove(rPnt);
+}
+
+PointerStyle E3dDragMove::GetSdrDragPointer() const
+{
+ return PointerStyle::Move;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/engine3d/e3dsceneupdater.cxx b/svx/source/engine3d/e3dsceneupdater.cxx
new file mode 100644
index 000000000..a72b4126d
--- /dev/null
+++ b/svx/source/engine3d/e3dsceneupdater.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 <svx/e3dsceneupdater.hxx>
+#include <drawinglayer/geometry/viewinformation3d.hxx>
+#include <svx/obj3d.hxx>
+#include <svx/scene3d.hxx>
+#include <svx/sdr/contact/viewcontactofe3dscene.hxx>
+
+
+E3DModifySceneSnapRectUpdater::E3DModifySceneSnapRectUpdater(const SdrObject* pObject)
+: mpScene(nullptr)
+{
+ // Secure old 3D transformation stack before modification
+ const E3dObject* pE3dObject = dynamic_cast< const E3dObject* >(pObject);
+ if(!pE3dObject)
+ return;
+
+ mpScene = pE3dObject->getRootE3dSceneFromE3dObject();
+
+ if(nullptr == mpScene || mpScene->getRootE3dSceneFromE3dObject() != mpScene)
+ return;
+
+ // if there is a scene and it's the outmost scene, get current 3D range
+ const sdr::contact::ViewContactOfE3dScene& rVCScene = static_cast< sdr::contact::ViewContactOfE3dScene& >(mpScene->GetViewContact());
+ const basegfx::B3DRange aAllContentRange(rVCScene.getAllContentRange3D());
+
+ if(aAllContentRange.isEmpty())
+ {
+ // no content, nothing to do
+ mpScene = nullptr;
+ }
+ else
+ {
+ // secure current 3D transformation stack
+ mpViewInformation3D = rVCScene.getViewInformation3D(aAllContentRange);
+ }
+}
+
+E3DModifySceneSnapRectUpdater::~E3DModifySceneSnapRectUpdater()
+{
+ if(!(mpScene && mpViewInformation3D))
+ return;
+
+ // after changing parts of the scene, use the secured last 3d transformation stack and the new content
+ // range to calculate a new, eventually expanded or shrunk, 2D geometry for the scene and apply it.
+ // Get new content range
+ const sdr::contact::ViewContactOfE3dScene& rVCScene = static_cast< sdr::contact::ViewContactOfE3dScene& >(mpScene->GetViewContact());
+ basegfx::B3DRange aAllContentRange(rVCScene.getAllContentRange3D());
+
+ // only change when there is still content; else let scene stay at old SnapRect
+ if(aAllContentRange.isEmpty())
+ return;
+
+ // check if object transform of scene has changed
+ if(mpViewInformation3D->getObjectTransformation() != mpScene->GetTransform())
+ {
+ // If Yes, it needs to be updated since it's - for historical reasons -
+ // part of the basic 3d transformation stack of the scene
+ mpViewInformation3D = drawinglayer::geometry::ViewInformation3D(
+ mpScene->GetTransform(), // replace object transformation with new local transform
+ mpViewInformation3D->getOrientation(),
+ mpViewInformation3D->getProjection(),
+ mpViewInformation3D->getDeviceToView(),
+ mpViewInformation3D->getViewTime(),
+ mpViewInformation3D->getExtendedInformationSequence());
+ }
+
+ // transform content range to scene-relative coordinates using old 3d transformation stack
+ aAllContentRange.transform(mpViewInformation3D->getObjectToView());
+
+ // build 2d relative content range
+ basegfx::B2DRange aSnapRange(
+ aAllContentRange.getMinX(), aAllContentRange.getMinY(),
+ aAllContentRange.getMaxX(), aAllContentRange.getMaxY());
+
+ // transform to 2D world coordinates using scene's 2D transformation
+ aSnapRange.transform(rVCScene.getObjectTransformation());
+
+ // snap to (old) integer
+ const tools::Rectangle aNewSnapRect(
+ sal_Int32(floor(aSnapRange.getMinX())), sal_Int32(floor(aSnapRange.getMinY())),
+ sal_Int32(ceil(aSnapRange.getMaxX())), sal_Int32(ceil(aSnapRange.getMaxY())));
+
+ // set as new SnapRect and invalidate bound volume
+ if(mpScene->GetSnapRect() != aNewSnapRect)
+ {
+ mpScene->SetSnapRect(aNewSnapRect);
+ mpScene->InvalidateBoundVolume();
+ }
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/engine3d/e3dundo.cxx b/svx/source/engine3d/e3dundo.cxx
new file mode 100644
index 000000000..b1d99ddfd
--- /dev/null
+++ b/svx/source/engine3d/e3dundo.cxx
@@ -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 .
+ */
+
+#include <svx/e3dundo.hxx>
+#include <svx/e3dsceneupdater.hxx>
+
+
+E3dUndoAction::~E3dUndoAction ()
+{
+}
+
+// Repeat does not exist
+
+bool E3dUndoAction::CanRepeat(SfxRepeatTarget&) const
+{
+ return false;
+}
+
+
+// Undo destructor for 3D-Rotation
+E3dRotateUndoAction::~E3dRotateUndoAction()
+{
+}
+
+// Undo for 3D-Rotation on the Rotation matrix
+void E3dRotateUndoAction::Undo()
+{
+ E3DModifySceneSnapRectUpdater aUpdater(&mrMy3DObj);
+ mrMy3DObj.SetTransform(maMyOldRotation);
+}
+
+// Redo for 3D-Rotation on the Rotation matrix
+void E3dRotateUndoAction::Redo()
+{
+ E3DModifySceneSnapRectUpdater aUpdater(&mrMy3DObj);
+ mrMy3DObj.SetTransform(maMyNewRotation);
+}
+
+E3dAttributesUndoAction::E3dAttributesUndoAction(
+ E3dObject& rInObject,
+ const SfxItemSet& rNewSet,
+ const SfxItemSet& rOldSet)
+: SdrUndoAction(rInObject.getSdrModelFromSdrObject())
+ ,mrObject(rInObject)
+ ,maNewSet(rNewSet)
+ ,maOldSet(rOldSet)
+{
+}
+
+E3dAttributesUndoAction::~E3dAttributesUndoAction()
+{
+}
+
+// Undo() implemented through Set3DAttributes() to only maintain the attributes
+// in one place
+
+void E3dAttributesUndoAction::Undo()
+{
+ E3DModifySceneSnapRectUpdater aUpdater(&mrObject);
+ mrObject.SetMergedItemSetAndBroadcast(maOldSet);
+}
+
+void E3dAttributesUndoAction::Redo()
+{
+ E3DModifySceneSnapRectUpdater aUpdater(&mrObject);
+ mrObject.SetMergedItemSetAndBroadcast(maNewSet);
+}
+
+// Multiple Undo is not possible
+bool E3dAttributesUndoAction::CanRepeat(SfxRepeatTarget& /*rView*/) const
+{
+ return false;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/engine3d/extrud3d.cxx b/svx/source/engine3d/extrud3d.cxx
new file mode 100644
index 000000000..a74479b60
--- /dev/null
+++ b/svx/source/engine3d/extrud3d.cxx
@@ -0,0 +1,219 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * 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/strings.hrc>
+#include <svx/deflt3d.hxx>
+#include <svx/dialmgr.hxx>
+#include <svx/svdobjkind.hxx>
+#include <extrud3d.hxx>
+
+#include <svx/svdopath.hxx>
+#include <svx/svdmodel.hxx>
+#include <svx/svx3ditems.hxx>
+#include <svx/xlineit0.hxx>
+#include <sdr/properties/e3dextrudeproperties.hxx>
+#include <sdr/contact/viewcontactofe3dextrude.hxx>
+#include <basegfx/matrix/b2dhommatrix.hxx>
+#include <basegfx/polygon/b2dpolypolygontools.hxx>
+#include <basegfx/polygon/b2dpolygontools.hxx>
+#include <basegfx/polygon/b3dpolypolygontools.hxx>
+
+
+// DrawContact section
+std::unique_ptr<sdr::contact::ViewContact> E3dExtrudeObj::CreateObjectSpecificViewContact()
+{
+ return std::make_unique<sdr::contact::ViewContactOfE3dExtrude>(*this);
+}
+
+std::unique_ptr<sdr::properties::BaseProperties> E3dExtrudeObj::CreateObjectSpecificProperties()
+{
+ return std::make_unique<sdr::properties::E3dExtrudeProperties>(*this);
+}
+
+// Constructor creates a two cover surface tools::PolyPolygon and (point-count 1) side
+// surfaces rectangles from the passed PolyPolygon
+E3dExtrudeObj::E3dExtrudeObj(
+ SdrModel& rSdrModel,
+ const E3dDefaultAttributes& rDefault,
+ const basegfx::B2DPolyPolygon& rPP,
+ double fDepth)
+: E3dCompoundObject(rSdrModel),
+ maExtrudePolygon(rPP)
+{
+ // since the old class PolyPolygon3D did mirror the given PolyPolygons in Y, do the same here
+ basegfx::B2DHomMatrix aMirrorY;
+ aMirrorY.scale(1.0, -1.0);
+ maExtrudePolygon.transform(aMirrorY);
+
+ // Set Defaults
+ SetDefaultAttributes(rDefault);
+
+ // set extrude depth
+ GetProperties().SetObjectItemDirect(makeSvx3DDepthItem(static_cast<sal_uInt32>(fDepth + 0.5)));
+}
+
+E3dExtrudeObj::E3dExtrudeObj(SdrModel& rSdrModel)
+: E3dCompoundObject(rSdrModel)
+{
+ // Set Defaults
+ const E3dDefaultAttributes aDefault;
+
+ SetDefaultAttributes(aDefault);
+}
+
+E3dExtrudeObj::E3dExtrudeObj(SdrModel& rSdrModel, E3dExtrudeObj const & rSource)
+: E3dCompoundObject(rSdrModel, rSource)
+{
+ // Set Defaults
+ const E3dDefaultAttributes aDefault;
+
+ SetDefaultAttributes(aDefault);
+
+ maExtrudePolygon = rSource.maExtrudePolygon;
+}
+
+E3dExtrudeObj::~E3dExtrudeObj()
+{
+}
+
+void E3dExtrudeObj::SetDefaultAttributes(const E3dDefaultAttributes& rDefault)
+{
+ GetProperties().SetObjectItemDirect(Svx3DSmoothNormalsItem(rDefault.GetDefaultExtrudeSmoothed()));
+ GetProperties().SetObjectItemDirect(Svx3DSmoothLidsItem(rDefault.GetDefaultExtrudeSmoothFrontBack()));
+ GetProperties().SetObjectItemDirect(Svx3DCharacterModeItem(rDefault.GetDefaultExtrudeCharacterMode()));
+ GetProperties().SetObjectItemDirect(Svx3DCloseFrontItem(rDefault.GetDefaultExtrudeCloseFront()));
+ GetProperties().SetObjectItemDirect(Svx3DCloseBackItem(rDefault.GetDefaultExtrudeCloseBack()));
+
+ // For extrudes use StdTexture in X and Y by default
+ GetProperties().SetObjectItemDirect(Svx3DTextureProjectionXItem(1));
+ GetProperties().SetObjectItemDirect(Svx3DTextureProjectionYItem(1));
+}
+
+SdrObjKind E3dExtrudeObj::GetObjIdentifier() const
+{
+ return SdrObjKind::E3D_Extrusion;
+}
+
+E3dExtrudeObj* E3dExtrudeObj::CloneSdrObject(SdrModel& rTargetModel) const
+{
+ return new E3dExtrudeObj(rTargetModel, *this);
+}
+
+// Set local parameters with geometry re-creating
+
+void E3dExtrudeObj::SetExtrudePolygon(const basegfx::B2DPolyPolygon &rNew)
+{
+ if(maExtrudePolygon != rNew)
+ {
+ maExtrudePolygon = rNew;
+ ActionChanged();
+ }
+}
+
+// Get the name of the object (singular)
+
+OUString E3dExtrudeObj::TakeObjNameSingul() const
+{
+ OUString sName(SvxResId(STR_ObjNameSingulExtrude3d));
+
+ OUString aName(GetName());
+ if (!aName.isEmpty())
+ {
+ sName += " '" + aName + "'";
+ }
+ return sName;
+}
+
+// Get the name of the object (plural)
+
+OUString E3dExtrudeObj::TakeObjNamePlural() const
+{
+ return SvxResId(STR_ObjNamePluralExtrude3d);
+}
+
+bool E3dExtrudeObj::IsBreakObjPossible()
+{
+ return true;
+}
+
+std::unique_ptr<SdrAttrObj,SdrObjectFreeOp> E3dExtrudeObj::GetBreakObj()
+{
+ basegfx::B3DPolyPolygon aFrontSide;
+ basegfx::B3DPolyPolygon aBackSide;
+
+ if(maExtrudePolygon.count())
+ {
+ basegfx::B2DPolyPolygon aTemp(maExtrudePolygon);
+ aTemp.removeDoublePoints();
+ aTemp = basegfx::utils::correctOrientations(aTemp);
+ const basegfx::B2VectorOrientation aOrient = basegfx::utils::getOrientation(aTemp.getB2DPolygon(0));
+
+ if(basegfx::B2VectorOrientation::Positive == aOrient)
+ {
+ aTemp.flip();
+ }
+
+ aFrontSide = basegfx::utils::createB3DPolyPolygonFromB2DPolyPolygon(aTemp);
+ }
+
+ if(aFrontSide.count())
+ {
+ aBackSide = aFrontSide;
+
+ if(GetExtrudeDepth())
+ {
+ basegfx::B3DHomMatrix aTransform;
+
+ if(100 != GetPercentBackScale())
+ {
+ // scale polygon from center
+ const double fScaleFactor(GetPercentBackScale() / 100.0);
+ const basegfx::B3DRange aPolyPolyRange(basegfx::utils::getRange(aBackSide));
+ const basegfx::B3DPoint aCenter(aPolyPolyRange.getCenter());
+
+ aTransform.translate(-aCenter.getX(), -aCenter.getY(), -aCenter.getZ());
+ aTransform.scale(fScaleFactor, fScaleFactor, fScaleFactor);
+ aTransform.translate(aCenter.getX(), aCenter.getY(), aCenter.getZ());
+ }
+
+ // translate by extrude depth
+ aTransform.translate(0.0, 0.0, static_cast<double>(GetExtrudeDepth()));
+
+ aBackSide.transform(aTransform);
+ }
+ }
+
+ if(aBackSide.count())
+ {
+ // create PathObj
+ basegfx::B2DPolyPolygon aPoly = TransformToScreenCoor(aBackSide);
+ std::unique_ptr<SdrPathObj,SdrObjectFreeOp> pPathObj(new SdrPathObj(getSdrModelFromSdrObject(), SdrObjKind::PolyLine, aPoly));
+
+ SfxItemSet aSet(GetObjectItemSet());
+ aSet.Put(XLineStyleItem(css::drawing::LineStyle_SOLID));
+ pPathObj->SetMergedItemSet(aSet);
+
+ return std::unique_ptr<SdrAttrObj,SdrObjectFreeOp>(pPathObj.release());
+ }
+
+ return nullptr;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/engine3d/float3d.cxx b/svx/source/engine3d/float3d.cxx
new file mode 100644
index 000000000..0c0b8eeb8
--- /dev/null
+++ b/svx/source/engine3d/float3d.cxx
@@ -0,0 +1,2944 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * 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 <sfx2/dispatch.hxx>
+#include <sfx2/module.hxx>
+#include <sfx2/viewfrm.hxx>
+#include <svl/eitem.hxx>
+#include <svl/intitem.hxx>
+#include <svl/itempool.hxx>
+#include <svtools/colrdlg.hxx>
+#include <svx/colorbox.hxx>
+#include <svx/f3dchild.hxx>
+#include <svx/xfillit0.hxx>
+#include <svx/xflclit.hxx>
+#include <svx/xlineit0.hxx>
+#include <svx/fmmodel.hxx>
+#include <svx/dlgutil.hxx>
+#include <svx/sdshitm.hxx>
+#include <svx/svx3ditems.hxx>
+
+#include <svx/dialmgr.hxx>
+#include <svx/viewpt3d.hxx>
+
+#include <svx/svxids.hrc>
+#include <svx/strings.hrc>
+
+#include <editeng/colritem.hxx>
+#include <osl/diagnose.h>
+#include <svx/e3ditem.hxx>
+#include <svl/whiter.hxx>
+#include <svtools/unitconv.hxx>
+
+#include <svx/float3d.hxx>
+
+#include <bitmaps.hlst>
+
+using namespace com::sun::star;
+
+SFX_IMPL_DOCKINGWINDOW_WITHID( Svx3DChildWindow, SID_3D_WIN )
+
+struct Svx3DWinImpl
+{
+ SfxItemPool* pPool;
+};
+
+namespace {
+ /** Get the dispatcher from the current view frame, or, if that is not
+ available, from the given bindings.
+ @param pBindings
+ May be NULL.
+ @returns NULL when both the current view frame is NULL and the given
+ bindings are NULL.
+ */
+ SfxDispatcher* LocalGetDispatcher (const SfxBindings* pBindings)
+ {
+ SfxDispatcher* pDispatcher = nullptr;
+
+ if (SfxViewFrame::Current() != nullptr)
+ pDispatcher = SfxViewFrame::Current()->GetDispatcher();
+ else if (pBindings != nullptr)
+ pDispatcher = pBindings->GetDispatcher();
+
+ return pDispatcher;
+ }
+}
+
+Svx3DWin::Svx3DWin(SfxBindings* pInBindings, SfxChildWindow *pCW, vcl::Window* pParent)
+ : SfxDockingWindow(pInBindings, pCW, pParent,
+ "Docking3DEffects", "svx/ui/docking3deffects.ui")
+
+ , m_xBtnGeo(m_xBuilder->weld_toggle_button("geometry"))
+ , m_xBtnRepresentation(m_xBuilder->weld_toggle_button("representation"))
+ , m_xBtnLight(m_xBuilder->weld_toggle_button("light"))
+ , m_xBtnTexture(m_xBuilder->weld_toggle_button("texture"))
+ , m_xBtnMaterial(m_xBuilder->weld_toggle_button("material"))
+ , m_xBtnUpdate(m_xBuilder->weld_toggle_button("update"))
+ , m_xBtnAssign(m_xBuilder->weld_button("assign"))
+
+ , m_xFLGeometrie(m_xBuilder->weld_container("geoframe"))
+ , m_xFtPercentDiagonal(m_xBuilder->weld_label("diagonalft"))
+ , m_xMtrPercentDiagonal(m_xBuilder->weld_metric_spin_button("diagonal", FieldUnit::PERCENT))
+ , m_xFtBackscale(m_xBuilder->weld_label("scaleddepthft"))
+ , m_xMtrBackscale(m_xBuilder->weld_metric_spin_button("scaleddepth", FieldUnit::PERCENT))
+ , m_xFtEndAngle(m_xBuilder->weld_label("angleft"))
+ , m_xMtrEndAngle(m_xBuilder->weld_metric_spin_button("angle", FieldUnit::DEGREE))
+ , m_xFtDepth(m_xBuilder->weld_label("depthft"))
+ , m_xMtrDepth(m_xBuilder->weld_metric_spin_button("depth", FieldUnit::CM))
+
+ , m_xFLSegments(m_xBuilder->weld_container("segmentsframe"))
+ , m_xNumHorizontal(m_xBuilder->weld_spin_button("hori"))
+ , m_xNumVertical(m_xBuilder->weld_spin_button("veri"))
+
+ , m_xFLNormals(m_xBuilder->weld_container("normals"))
+ , m_xBtnNormalsObj(m_xBuilder->weld_toggle_button("objspecific"))
+ , m_xBtnNormalsFlat(m_xBuilder->weld_toggle_button("flat"))
+ , m_xBtnNormalsSphere(m_xBuilder->weld_toggle_button("spherical"))
+ , m_xBtnNormalsInvert(m_xBuilder->weld_toggle_button("invertnormals"))
+ , m_xBtnTwoSidedLighting(m_xBuilder->weld_toggle_button("doublesidedillum"))
+ , m_xBtnDoubleSided(m_xBuilder->weld_toggle_button("doublesided"))
+
+ , m_xFLRepresentation(m_xBuilder->weld_container("shadingframe"))
+ , m_xLbShademode(m_xBuilder->weld_combo_box("mode"))
+
+ , m_xFLShadow(m_xBuilder->weld_container("shadowframe"))
+ , m_xBtnShadow3d(m_xBuilder->weld_toggle_button("shadow"))
+ , m_xFtSlant(m_xBuilder->weld_label("slantft"))
+ , m_xMtrSlant(m_xBuilder->weld_metric_spin_button("slant", FieldUnit::DEGREE))
+
+ , m_xFLCamera(m_xBuilder->weld_container("cameraframe"))
+ , m_xMtrDistance(m_xBuilder->weld_metric_spin_button("distance", FieldUnit::CM))
+ , m_xMtrFocalLength(m_xBuilder->weld_metric_spin_button("focal", FieldUnit::CM))
+
+ , m_xFLLight(m_xBuilder->weld_container("illumframe"))
+ , m_xBtnLight1(new LightButton(m_xBuilder->weld_toggle_button("light1")))
+ , m_xBtnLight2(new LightButton(m_xBuilder->weld_toggle_button("light2")))
+ , m_xBtnLight3(new LightButton(m_xBuilder->weld_toggle_button("light3")))
+ , m_xBtnLight4(new LightButton(m_xBuilder->weld_toggle_button("light4")))
+ , m_xBtnLight5(new LightButton(m_xBuilder->weld_toggle_button("light5")))
+ , m_xBtnLight6(new LightButton(m_xBuilder->weld_toggle_button("light6")))
+ , m_xBtnLight7(new LightButton(m_xBuilder->weld_toggle_button("light7")))
+ , m_xBtnLight8(new LightButton(m_xBuilder->weld_toggle_button("light8")))
+ , m_xLbLight1(new ColorListBox(m_xBuilder->weld_menu_button("lightcolor1"), [this]{ return GetFrameWeld(); }))
+ , m_xLbLight2(new ColorListBox(m_xBuilder->weld_menu_button("lightcolor2"), [this]{ return GetFrameWeld(); }))
+ , m_xLbLight3(new ColorListBox(m_xBuilder->weld_menu_button("lightcolor3"), [this]{ return GetFrameWeld(); }))
+ , m_xLbLight4(new ColorListBox(m_xBuilder->weld_menu_button("lightcolor4"), [this]{ return GetFrameWeld(); }))
+ , m_xLbLight5(new ColorListBox(m_xBuilder->weld_menu_button("lightcolor5"), [this]{ return GetFrameWeld(); }))
+ , m_xLbLight6(new ColorListBox(m_xBuilder->weld_menu_button("lightcolor6"), [this]{ return GetFrameWeld(); }))
+ , m_xLbLight7(new ColorListBox(m_xBuilder->weld_menu_button("lightcolor7"), [this]{ return GetFrameWeld(); }))
+ , m_xLbLight8(new ColorListBox(m_xBuilder->weld_menu_button("lightcolor8"), [this]{ return GetFrameWeld(); }))
+ , m_xBtnLightColor(m_xBuilder->weld_button("colorbutton1"))
+ , m_xLbAmbientlight(new ColorListBox(m_xBuilder->weld_menu_button("ambientcolor"), [this]{ return GetFrameWeld(); }))
+ , m_xBtnAmbientColor(m_xBuilder->weld_button("colorbutton2"))
+
+ , m_xFLTexture(m_xBuilder->weld_container("textureframe"))
+ , m_xBtnTexLuminance(m_xBuilder->weld_toggle_button("textype"))
+ , m_xBtnTexColor(m_xBuilder->weld_toggle_button("texcolor"))
+ , m_xBtnTexReplace(m_xBuilder->weld_toggle_button("texreplace"))
+ , m_xBtnTexModulate(m_xBuilder->weld_toggle_button("texmodulate"))
+ , m_xBtnTexObjectX(m_xBuilder->weld_toggle_button("texobjx"))
+ , m_xBtnTexParallelX(m_xBuilder->weld_toggle_button("texparallelx"))
+ , m_xBtnTexCircleX(m_xBuilder->weld_toggle_button("texcirclex"))
+ , m_xBtnTexObjectY(m_xBuilder->weld_toggle_button("texobjy"))
+ , m_xBtnTexParallelY(m_xBuilder->weld_toggle_button("texparallely"))
+ , m_xBtnTexCircleY(m_xBuilder->weld_toggle_button("texcircley"))
+ , m_xBtnTexFilter(m_xBuilder->weld_toggle_button("texfilter"))
+
+ , m_xFLMaterial(m_xBuilder->weld_container("materialframe"))
+ , m_xLbMatFavorites(m_xBuilder->weld_combo_box("favorites"))
+ , m_xLbMatColor(new ColorListBox(m_xBuilder->weld_menu_button("objcolor"), [this]{ return GetFrameWeld(); }))
+ , m_xBtnMatColor(m_xBuilder->weld_button("colorbutton3"))
+ , m_xLbMatEmission(new ColorListBox(m_xBuilder->weld_menu_button("illumcolor"), [this]{ return GetFrameWeld(); }))
+ , m_xBtnEmissionColor(m_xBuilder->weld_button("colorbutton4"))
+
+ , m_xFLMatSpecular(m_xBuilder->weld_container("specframe"))
+ , m_xLbMatSpecular(new ColorListBox(m_xBuilder->weld_menu_button("speccolor"), [this]{ return GetFrameWeld(); }))
+ , m_xBtnSpecularColor(m_xBuilder->weld_button("colorbutton5"))
+ , m_xMtrMatSpecularIntensity(m_xBuilder->weld_metric_spin_button("intensity", FieldUnit::PERCENT))
+
+ , m_xCtlPreview(new Svx3DPreviewControl)
+ , m_xCtlPreviewWin(new weld::CustomWeld(*m_xBuilder, "preview", *m_xCtlPreview))
+
+ , m_xLightPreviewGrid(m_xBuilder->weld_container("lightpreviewgrid"))
+ , m_xHoriScale(m_xBuilder->weld_scale("horiscale"))
+ , m_xVertScale(m_xBuilder->weld_scale("vertscale"))
+ , m_xBtn_Corner(m_xBuilder->weld_button("corner"))
+ , m_xLightPreview(new Svx3DLightControl)
+ , m_xCtlLightPreviewWin(new weld::CustomWeld(*m_xBuilder, "lightpreview", *m_xLightPreview))
+ , m_xCtlLightPreview(new SvxLightCtl3D(*m_xLightPreview, *m_xHoriScale, *m_xVertScale, *m_xBtn_Corner)) // TODO might be other body widget as arg 1
+
+ , m_xBtnConvertTo3D(m_xBuilder->weld_button("to3d"))
+ , m_xBtnLatheObject(m_xBuilder->weld_button("tolathe"))
+ , m_xBtnPerspective(m_xBuilder->weld_toggle_button("perspective"))
+
+ , bUpdate(false)
+ , eViewType(ViewType3D::Geo)
+ , pBindings(pInBindings)
+ , mpImpl(new Svx3DWinImpl)
+ , ePoolUnit(MapUnit::MapMM)
+{
+ SetText(SvxResId(RID_SVXDLG_FLOAT3D_STR_TITLE));
+
+ weld::DrawingArea* pDrawingArea = m_xCtlPreview->GetDrawingArea();
+ Size aSize(pDrawingArea->get_ref_device().LogicToPixel(Size(83, 76), MapMode(MapUnit::MapAppFont)));
+ pDrawingArea->set_size_request(aSize.Width(), aSize.Height());
+ m_xCtlPreview->SetOutputSizePixel(aSize);
+
+ m_xLightPreviewGrid->set_size_request(aSize.Width(), aSize.Height());
+ pDrawingArea = m_xLightPreview->GetDrawingArea();
+ pDrawingArea->set_size_request(42, 42); // small to fit to m_xLightPreviewGrid
+
+ mpImpl->pPool = nullptr;
+
+ // Set Metric
+ eFUnit = pInBindings->GetDispatcher()->GetModule()->GetFieldUnit();
+
+ m_xMtrDepth->set_unit( eFUnit );
+ m_xMtrDistance->set_unit( eFUnit );
+ m_xMtrFocalLength->set_unit( eFUnit );
+
+ pControllerItem.reset( new Svx3DCtrlItem(SID_3D_STATE, pBindings) );
+ pConvertTo3DItem.reset( new SvxConvertTo3DItem(SID_CONVERT_TO_3D, pBindings) );
+ pConvertTo3DLatheItem.reset( new SvxConvertTo3DItem(SID_CONVERT_TO_3D_LATHE_FAST, pBindings) );
+
+ m_xBtnAssign->connect_clicked( LINK( this, Svx3DWin, ClickAssignHdl ) );
+ m_xBtnUpdate->connect_toggled( LINK( this, Svx3DWin, ClickUpdateHdl ) );
+
+ Link<weld::Button&,void> aLink( LINK( this, Svx3DWin, ClickViewTypeHdl ) );
+ m_xBtnGeo->connect_clicked( aLink );
+ m_xBtnRepresentation->connect_clicked( aLink );
+ m_xBtnLight->connect_clicked( aLink );
+ m_xBtnTexture->connect_clicked( aLink );
+ m_xBtnMaterial->connect_clicked( aLink );
+
+ aLink = LINK( this, Svx3DWin, ClickHdl );
+ m_xBtnPerspective->connect_clicked( aLink );
+ m_xBtnConvertTo3D->connect_clicked( aLink );
+ m_xBtnLatheObject->connect_clicked( aLink );
+
+ // Geometry
+ m_xBtnNormalsObj->connect_clicked( aLink );
+ m_xBtnNormalsFlat->connect_clicked( aLink );
+ m_xBtnNormalsSphere->connect_clicked( aLink );
+ m_xBtnTwoSidedLighting->connect_clicked( aLink );
+ m_xBtnNormalsInvert->connect_clicked( aLink );
+ m_xBtnDoubleSided->connect_clicked( aLink );
+
+ // Representation
+ m_xBtnShadow3d->connect_clicked( aLink );
+
+ // Lighting
+ m_xBtnLight1->connect_clicked( aLink );
+ m_xBtnLight2->connect_clicked( aLink );
+ m_xBtnLight3->connect_clicked( aLink );
+ m_xBtnLight4->connect_clicked( aLink );
+ m_xBtnLight5->connect_clicked( aLink );
+ m_xBtnLight6->connect_clicked( aLink );
+ m_xBtnLight7->connect_clicked( aLink );
+ m_xBtnLight8->connect_clicked( aLink );
+
+ // Textures
+ m_xBtnTexLuminance->connect_clicked( aLink );
+ m_xBtnTexColor->connect_clicked( aLink );
+ m_xBtnTexReplace->connect_clicked( aLink );
+ m_xBtnTexModulate->connect_clicked( aLink );
+ m_xBtnTexParallelX->connect_clicked( aLink );
+ m_xBtnTexCircleX->connect_clicked( aLink );
+ m_xBtnTexObjectX->connect_clicked( aLink );
+ m_xBtnTexParallelY->connect_clicked( aLink );
+ m_xBtnTexCircleY->connect_clicked( aLink );
+ m_xBtnTexObjectY->connect_clicked( aLink );
+ m_xBtnTexFilter->connect_clicked( aLink );
+
+ // Material
+ aLink = LINK( this, Svx3DWin, ClickColorHdl );
+ m_xBtnLightColor->connect_clicked( aLink );
+ m_xBtnAmbientColor->connect_clicked( aLink );
+ m_xBtnMatColor->connect_clicked( aLink );
+ m_xBtnEmissionColor->connect_clicked( aLink );
+ m_xBtnSpecularColor->connect_clicked( aLink );
+
+
+ Link<weld::ComboBox&,void> aLink2 = LINK( this, Svx3DWin, SelectHdl );
+ Link<ColorListBox&,void> aLink4 = LINK( this, Svx3DWin, SelectColorHdl );
+ m_xLbMatFavorites->connect_changed( aLink2 );
+ m_xLbMatColor->SetSelectHdl( aLink4 );
+ m_xLbMatEmission->SetSelectHdl( aLink4 );
+ m_xLbMatSpecular->SetSelectHdl( aLink4 );
+ m_xLbLight1->SetSelectHdl( aLink4 );
+ m_xLbLight2->SetSelectHdl( aLink4 );
+ m_xLbLight3->SetSelectHdl( aLink4 );
+ m_xLbLight4->SetSelectHdl( aLink4 );
+ m_xLbLight5->SetSelectHdl( aLink4 );
+ m_xLbLight6->SetSelectHdl( aLink4 );
+ m_xLbLight7->SetSelectHdl( aLink4 );
+ m_xLbLight8->SetSelectHdl( aLink4 );
+ m_xLbAmbientlight->SetSelectHdl( aLink4 );
+ m_xLbShademode->connect_changed( aLink2 );
+
+ Link<weld::MetricSpinButton&,void> aLink3 = LINK( this, Svx3DWin, ModifyMetricHdl );
+ Link<weld::SpinButton&,void> aLink5 = LINK( this, Svx3DWin, ModifySpinHdl );
+ m_xMtrMatSpecularIntensity->connect_value_changed( aLink3 );
+ m_xNumHorizontal->connect_value_changed( aLink5 );
+ m_xNumVertical->connect_value_changed( aLink5 );
+ m_xMtrSlant->connect_value_changed( aLink3 );
+
+ // Preview callback
+ m_xCtlLightPreview->SetUserSelectionChangeCallback(LINK( this, Svx3DWin, ChangeSelectionCallbackHdl ));
+
+ aSize = GetOutputSizePixel();
+ SetMinOutputSizePixel( aSize );
+
+ Construct();
+
+ // Initiation of the initialization of the ColorLBs
+ SfxDispatcher* pDispatcher = LocalGetDispatcher(pBindings);
+ if (pDispatcher != nullptr)
+ {
+ SfxBoolItem aItem( SID_3D_INIT, true );
+ pDispatcher->ExecuteList(SID_3D_INIT,
+ SfxCallMode::ASYNCHRON | SfxCallMode::RECORD, { &aItem });
+ }
+
+ Reset();
+
+ //lock down the size of the initial largest default mode as the permanent size
+ aSize = get_preferred_size();
+ set_width_request(aSize.Width());
+ set_height_request(aSize.Height());
+}
+
+Svx3DWin::~Svx3DWin()
+{
+ disposeOnce();
+}
+
+void Svx3DWin::dispose()
+{
+ pModel.reset();
+
+ pControllerItem.reset();
+ pConvertTo3DItem.reset();
+ pConvertTo3DLatheItem.reset();
+
+ mpImpl.reset();
+
+ m_xBtnGeo.reset();
+ m_xBtnRepresentation.reset();
+ m_xBtnLight.reset();
+ m_xBtnTexture.reset();
+ m_xBtnMaterial.reset();
+ m_xBtnUpdate.reset();
+ m_xBtnAssign.reset();
+ m_xFLGeometrie.reset();
+ m_xFtPercentDiagonal.reset();
+ m_xMtrPercentDiagonal.reset();
+ m_xFtBackscale.reset();
+ m_xMtrBackscale.reset();
+ m_xFtEndAngle.reset();
+ m_xMtrEndAngle.reset();
+ m_xFtDepth.reset();
+ m_xMtrDepth.reset();
+ m_xFLSegments.reset();
+ m_xNumHorizontal.reset();
+ m_xNumVertical.reset();
+ m_xFLNormals.reset();
+ m_xBtnNormalsObj.reset();
+ m_xBtnNormalsFlat.reset();
+ m_xBtnNormalsSphere.reset();
+ m_xBtnNormalsInvert.reset();
+ m_xBtnTwoSidedLighting.reset();
+ m_xBtnDoubleSided.reset();
+ m_xFLRepresentation.reset();
+ m_xLbShademode.reset();
+ m_xFLShadow.reset();
+ m_xBtnShadow3d.reset();
+ m_xFtSlant.reset();
+ m_xMtrSlant.reset();
+ m_xFLCamera.reset();
+ m_xMtrDistance.reset();
+ m_xMtrFocalLength.reset();
+ m_xFLLight.reset();
+ m_xBtnLight1.reset();
+ m_xBtnLight2.reset();
+ m_xBtnLight3.reset();
+ m_xBtnLight4.reset();
+ m_xBtnLight5.reset();
+ m_xBtnLight6.reset();
+ m_xBtnLight7.reset();
+ m_xBtnLight8.reset();
+ m_xLbLight1.reset();
+ m_xLbLight2.reset();
+ m_xLbLight3.reset();
+ m_xLbLight4.reset();
+ m_xLbLight5.reset();
+ m_xLbLight6.reset();
+ m_xLbLight7.reset();
+ m_xLbLight8.reset();
+ m_xBtnLightColor.reset();
+ m_xLbAmbientlight.reset();
+ m_xBtnAmbientColor.reset();
+ m_xFLTexture.reset();
+ m_xBtnTexLuminance.reset();
+ m_xBtnTexColor.reset();
+ m_xBtnTexReplace.reset();
+ m_xBtnTexModulate.reset();
+ m_xBtnTexObjectX.reset();
+ m_xBtnTexParallelX.reset();
+ m_xBtnTexCircleX.reset();
+ m_xBtnTexObjectY.reset();
+ m_xBtnTexParallelY.reset();
+ m_xBtnTexCircleY.reset();
+ m_xBtnTexFilter.reset();
+ m_xFLMaterial.reset();
+ m_xLbMatFavorites.reset();
+ m_xLbMatColor.reset();
+ m_xBtnMatColor.reset();
+ m_xLbMatEmission.reset();
+ m_xBtnEmissionColor.reset();
+ m_xFLMatSpecular.reset();
+ m_xLbMatSpecular.reset();
+ m_xBtnSpecularColor.reset();
+ m_xMtrMatSpecularIntensity.reset();
+ m_xCtlPreviewWin.reset();
+ m_xCtlPreview.reset();
+
+ m_xCtlLightPreview.reset();
+ m_xCtlLightPreviewWin.reset();
+ m_xLightPreview.reset();
+ m_xBtn_Corner.reset();
+ m_xVertScale.reset();
+ m_xHoriScale.reset();
+ m_xLightPreviewGrid.reset();
+
+ m_xBtnConvertTo3D.reset();
+ m_xBtnLatheObject.reset();
+ m_xBtnPerspective.reset();
+
+ SfxDockingWindow::dispose();
+}
+
+void Svx3DWin::Construct()
+{
+ m_xBtnGeo->set_active(true);
+ ClickViewTypeHdl(*m_xBtnGeo);
+ m_xLightPreviewGrid->hide();
+}
+
+void Svx3DWin::Reset()
+{
+ // Various initializations, default is AllAttributes
+ m_xLbShademode->set_active( 0 );
+ m_xMtrMatSpecularIntensity->set_value( 50, FieldUnit::PERCENT );
+
+ m_xBtnLight1->set_active(true);
+ m_xBtnUpdate->set_active(true);
+ ClickUpdateHdl(*m_xBtnUpdate);
+
+ // Select nothing, to avoid errors when selecting the first
+ m_xCtlLightPreview->GetSvx3DLightControl().SelectLight(0);
+ m_xCtlLightPreview->CheckSelection();
+}
+
+void Svx3DWin::Update( SfxItemSet const & rAttrs )
+{
+ // remember 2d attributes
+ if(mpRemember2DAttributes)
+ mpRemember2DAttributes->ClearItem();
+ else
+ mpRemember2DAttributes = std::make_unique<SfxItemSetFixed
+ <SDRATTR_START, SDRATTR_SHADOW_LAST,
+ SDRATTR_3D_FIRST, SDRATTR_3D_LAST>>(*rAttrs.GetPool());
+
+ SfxWhichIter aIter(*mpRemember2DAttributes);
+ sal_uInt16 nWhich(aIter.FirstWhich());
+
+ while(nWhich)
+ {
+ SfxItemState eState = rAttrs.GetItemState(nWhich, false);
+ if(SfxItemState::DONTCARE == eState)
+ mpRemember2DAttributes->InvalidateItem(nWhich);
+ else if(SfxItemState::SET == eState)
+ mpRemember2DAttributes->Put(rAttrs.Get(nWhich, false));
+
+ nWhich = aIter.NextWhich();
+ }
+
+ // construct field values
+ const SfxPoolItem* pItem;
+
+ // Possible determine PoolUnit
+ if( !mpImpl->pPool )
+ {
+ mpImpl->pPool = rAttrs.GetPool();
+ DBG_ASSERT( mpImpl->pPool, "Where is the Pool? ");
+ ePoolUnit = mpImpl->pPool->GetMetric( SID_ATTR_LINE_WIDTH );
+ }
+ eFUnit = GetModuleFieldUnit( rAttrs );
+
+
+ // Segment Number Can be changed? and other states
+ SfxItemState eState = rAttrs.GetItemState( SID_ATTR_3D_INTERN, false, &pItem );
+ if( SfxItemState::SET == eState )
+ {
+ sal_uInt32 nState = static_cast<const SfxUInt32Item*>(pItem)->GetValue();
+ bool bExtrude = ( nState & 2 );
+ bool bSphere = ( nState & 4 );
+ bool bCube = ( nState & 8 );
+
+ bool bChart = ( nState & 32 ); // Chart
+
+ if( !bChart )
+ {
+ // For cube objects are no segments set
+ m_xFLSegments->set_sensitive(!bCube);
+
+ m_xFtPercentDiagonal->set_sensitive( !bCube && !bSphere );
+ m_xMtrPercentDiagonal->set_sensitive( !bCube && !bSphere );
+ m_xFtBackscale->set_sensitive( !bCube && !bSphere );
+ m_xMtrBackscale->set_sensitive( !bCube && !bSphere );
+ m_xFtDepth->set_sensitive( !bCube && !bSphere );
+ m_xMtrDepth->set_sensitive( !bCube && !bSphere );
+ if( bCube )
+ {
+ m_xNumHorizontal->set_text("");
+ m_xNumVertical->set_text("");
+ }
+ if( bCube || bSphere )
+ {
+ m_xMtrPercentDiagonal->set_text("");
+ m_xMtrBackscale->set_text("");
+ m_xMtrDepth->set_text("");
+ }
+
+ // There is a final angle only for Lathe objects.
+ m_xFtEndAngle->set_sensitive( !bExtrude && !bCube && !bSphere );
+ m_xMtrEndAngle->set_sensitive( !bExtrude && !bCube && !bSphere );
+ if( bExtrude || bCube || bSphere )
+ m_xMtrEndAngle->set_text("");
+ }
+ else
+ {
+ // Geometry
+ m_xNumHorizontal->set_text("");
+ m_xNumVertical->set_text("");
+ m_xFLSegments->set_sensitive( false );
+ m_xFtEndAngle->set_sensitive( false );
+ m_xMtrEndAngle->set_sensitive( false );
+ m_xMtrEndAngle->set_text("");
+ m_xFtDepth->set_sensitive( false );
+ m_xMtrDepth->set_sensitive( false );
+ m_xMtrDepth->set_text("");
+
+ // Representation
+ m_xFLShadow->set_sensitive(false);
+
+ m_xMtrDistance->set_text("");
+ m_xMtrFocalLength->set_text("");
+ m_xFLCamera->set_sensitive( false );
+
+ //Lower Range
+ m_xBtnConvertTo3D->set_sensitive( false );
+ m_xBtnLatheObject->set_sensitive( false );
+ }
+ }
+ // Bitmap fill ? -> Status
+ bool bBitmap(false);
+ eState = rAttrs.GetItemState(XATTR_FILLSTYLE);
+ if(eState != SfxItemState::DONTCARE)
+ {
+ drawing::FillStyle eXFS = rAttrs.Get(XATTR_FILLSTYLE).GetValue();
+ bBitmap = (eXFS == drawing::FillStyle_BITMAP || eXFS == drawing::FillStyle_GRADIENT || eXFS == drawing::FillStyle_HATCH);
+ }
+
+ m_xFLTexture->set_sensitive(bBitmap);
+
+ // Geometry
+ // Number of segments (horizontal)
+ if( m_xNumHorizontal->get_sensitive() )
+ {
+ eState = rAttrs.GetItemState(SDRATTR_3DOBJ_HORZ_SEGS);
+ if(eState != SfxItemState::DONTCARE)
+ {
+ sal_uInt32 nValue = rAttrs.Get(SDRATTR_3DOBJ_HORZ_SEGS).GetValue();
+ if (nValue != static_cast<sal_uInt32>(m_xNumHorizontal->get_value()))
+ {
+ m_xNumHorizontal->set_value( nValue );
+ bUpdate = true;
+ }
+ else if( m_xNumHorizontal->get_text().isEmpty() )
+ m_xNumHorizontal->set_value( nValue );
+ }
+ else
+ {
+ if( !m_xNumHorizontal->get_text().isEmpty() )
+ {
+ m_xNumHorizontal->set_text("");
+ bUpdate = true;
+ }
+ }
+ }
+
+ //Number of segments (vertical)
+ if( m_xNumVertical->get_sensitive() )
+ {
+ eState = rAttrs.GetItemState(SDRATTR_3DOBJ_VERT_SEGS);
+ if( eState != SfxItemState::DONTCARE )
+ {
+ sal_uInt32 nValue = rAttrs.Get(SDRATTR_3DOBJ_VERT_SEGS).GetValue();
+ if( nValue != static_cast<sal_uInt32>(m_xNumVertical->get_value()) )
+ {
+ m_xNumVertical->set_value( nValue );
+ bUpdate = true;
+ }
+ else if( m_xNumVertical->get_text().isEmpty() )
+ m_xNumVertical->set_value( nValue );
+ }
+ else
+ {
+ if( !m_xNumVertical->get_text().isEmpty() )
+ {
+ m_xNumVertical->set_text("");
+ bUpdate = true;
+ }
+ }
+ }
+
+ // Depth
+ if( m_xMtrDepth->get_sensitive() )
+ {
+ eState = rAttrs.GetItemState(SDRATTR_3DOBJ_DEPTH);
+ if( eState != SfxItemState::DONTCARE )
+ {
+ sal_uInt32 nValue = rAttrs.Get(SDRATTR_3DOBJ_DEPTH).GetValue();
+ sal_uInt32 nValue2 = GetCoreValue(*m_xMtrDepth, ePoolUnit);
+ if( nValue != nValue2 )
+ {
+ if( eFUnit != m_xMtrDepth->get_unit() )
+ SetFieldUnit(*m_xMtrDepth, eFUnit);
+
+ SetMetricValue(*m_xMtrDepth, nValue, ePoolUnit);
+ bUpdate = true;
+ }
+ else if( m_xMtrDepth->get_text().isEmpty() )
+ m_xMtrDepth->set_value(m_xMtrDepth->get_value(FieldUnit::NONE), FieldUnit::NONE);
+ }
+ else
+ {
+ if( !m_xMtrDepth->get_text().isEmpty() )
+ {
+ m_xMtrDepth->set_text("");
+ bUpdate = true;
+ }
+ }
+ }
+
+ // Double walled / Double sided
+ eState = rAttrs.GetItemState(SDRATTR_3DOBJ_DOUBLE_SIDED);
+ if( eState != SfxItemState::DONTCARE )
+ {
+ bool bValue = rAttrs.Get(SDRATTR_3DOBJ_DOUBLE_SIDED).GetValue();
+ if( bValue != m_xBtnDoubleSided->get_active() )
+ {
+ m_xBtnDoubleSided->set_active( bValue );
+ bUpdate = true;
+ }
+ else if( m_xBtnDoubleSided->get_state() == TRISTATE_INDET )
+ m_xBtnDoubleSided->set_active( bValue );
+ }
+ else
+ {
+ if( m_xBtnDoubleSided->get_state() != TRISTATE_INDET )
+ {
+ m_xBtnDoubleSided->set_state( TRISTATE_INDET );
+ bUpdate = true;
+ }
+ }
+
+ // Edge rounding
+ if( m_xMtrPercentDiagonal->get_sensitive() )
+ {
+ eState = rAttrs.GetItemState(SDRATTR_3DOBJ_PERCENT_DIAGONAL);
+ if( eState != SfxItemState::DONTCARE )
+ {
+ sal_uInt16 nValue = rAttrs.Get(SDRATTR_3DOBJ_PERCENT_DIAGONAL).GetValue();
+ if( nValue != m_xMtrPercentDiagonal->get_value(FieldUnit::PERCENT) )
+ {
+ m_xMtrPercentDiagonal->set_value(nValue, FieldUnit::PERCENT);
+ bUpdate = true;
+ }
+ else if( m_xMtrPercentDiagonal->get_text().isEmpty() )
+ m_xMtrPercentDiagonal->set_value(nValue, FieldUnit::PERCENT);
+ }
+ else
+ {
+ if( !m_xMtrPercentDiagonal->get_text().isEmpty() )
+ {
+ m_xMtrPercentDiagonal->set_text("");
+ bUpdate = true;
+ }
+ }
+ }
+
+ // Depth scaling
+ if( m_xMtrBackscale->get_sensitive() )
+ {
+ eState = rAttrs.GetItemState(SDRATTR_3DOBJ_BACKSCALE);
+ if( eState != SfxItemState::DONTCARE )
+ {
+ sal_uInt16 nValue = rAttrs.Get(SDRATTR_3DOBJ_BACKSCALE).GetValue();
+ if( nValue != m_xMtrBackscale->get_value(FieldUnit::PERCENT) )
+ {
+ m_xMtrBackscale->set_value(nValue, FieldUnit::PERCENT);
+ bUpdate = true;
+ }
+ else if( m_xMtrBackscale->get_text().isEmpty() )
+ m_xMtrBackscale->set_value(nValue, FieldUnit::PERCENT);
+ }
+ else
+ {
+ if( !m_xMtrBackscale->get_text().isEmpty() )
+ {
+ m_xMtrBackscale->set_text("");
+ bUpdate = true;
+ }
+ }
+ }
+
+ // End angle
+ if( m_xMtrEndAngle->get_sensitive() )
+ {
+ eState = rAttrs.GetItemState(SDRATTR_3DOBJ_END_ANGLE);
+ if( eState != SfxItemState::DONTCARE )
+ {
+ sal_Int32 nValue = rAttrs.Get(SDRATTR_3DOBJ_END_ANGLE).GetValue();
+ if( nValue != m_xMtrEndAngle->get_value(FieldUnit::DEGREE) )
+ {
+ m_xMtrEndAngle->set_value(nValue, FieldUnit::DEGREE);
+ bUpdate = true;
+ }
+ }
+ else
+ {
+ if( !m_xMtrEndAngle->get_text().isEmpty() )
+ {
+ m_xMtrEndAngle->set_text("");
+ bUpdate = true;
+ }
+ }
+ }
+
+ // Normal type
+ eState = rAttrs.GetItemState(SDRATTR_3DOBJ_NORMALS_KIND);
+ if( eState != SfxItemState::DONTCARE )
+ {
+ sal_uInt16 nValue = rAttrs.Get(SDRATTR_3DOBJ_NORMALS_KIND).GetValue();
+
+ if( ( !m_xBtnNormalsObj->get_active() && nValue == 0 ) ||
+ ( !m_xBtnNormalsFlat->get_active() && nValue == 1 ) ||
+ ( !m_xBtnNormalsSphere->get_active() && nValue == 2 ) )
+ {
+ m_xBtnNormalsObj->set_active( nValue == 0 );
+ m_xBtnNormalsFlat->set_active( nValue == 1 );
+ m_xBtnNormalsSphere->set_active( nValue == 2 );
+ bUpdate = true;
+ }
+ }
+ else
+ {
+ if( m_xBtnNormalsObj->get_active() ||
+ m_xBtnNormalsFlat->get_active() ||
+ m_xBtnNormalsSphere->get_active() )
+ {
+ m_xBtnNormalsObj->set_active( false );
+ m_xBtnNormalsFlat->set_active( false );
+ m_xBtnNormalsSphere->set_active( false );
+ bUpdate = true;
+ }
+ }
+
+ // Normal inverted
+ eState = rAttrs.GetItemState(SDRATTR_3DOBJ_NORMALS_INVERT);
+ if( eState != SfxItemState::DONTCARE )
+ {
+ bool bValue = rAttrs.Get(SDRATTR_3DOBJ_NORMALS_INVERT).GetValue();
+ if( bValue != m_xBtnNormalsInvert->get_active() )
+ {
+ m_xBtnNormalsInvert->set_active( bValue );
+ bUpdate = true;
+ }
+ else if( m_xBtnNormalsInvert->get_state() == TRISTATE_INDET )
+ m_xBtnNormalsInvert->set_active( bValue );
+ }
+ else
+ {
+ if( m_xBtnNormalsInvert->get_state() != TRISTATE_INDET )
+ {
+ m_xBtnNormalsInvert->set_state( TRISTATE_INDET );
+ bUpdate = true;
+ }
+ }
+
+ // 2-sided lighting
+ eState = rAttrs.GetItemState(SDRATTR_3DSCENE_TWO_SIDED_LIGHTING);
+ if( eState != SfxItemState::DONTCARE )
+ {
+ bool bValue = rAttrs.Get(SDRATTR_3DSCENE_TWO_SIDED_LIGHTING).GetValue();
+ if( bValue != m_xBtnTwoSidedLighting->get_active() )
+ {
+ m_xBtnTwoSidedLighting->set_active( bValue );
+ bUpdate = true;
+ }
+ else if( m_xBtnTwoSidedLighting->get_state() == TRISTATE_INDET )
+ m_xBtnTwoSidedLighting->set_active( bValue );
+ }
+ else
+ {
+ if( m_xBtnTwoSidedLighting->get_state() != TRISTATE_INDET )
+ {
+ m_xBtnTwoSidedLighting->set_state( TRISTATE_INDET );
+ bUpdate = true;
+ }
+ }
+
+ // Representation
+ // Shademode
+ eState = rAttrs.GetItemState(SDRATTR_3DSCENE_SHADE_MODE);
+ if( eState != SfxItemState::DONTCARE )
+ {
+ sal_uInt16 nValue = rAttrs.Get(SDRATTR_3DSCENE_SHADE_MODE).GetValue();
+ if( nValue != m_xLbShademode->get_active() )
+ {
+ m_xLbShademode->set_active( nValue );
+ bUpdate = true;
+ }
+ }
+ else
+ {
+ if( m_xLbShademode->get_active() != 0 )
+ {
+ m_xLbShademode->set_active(-1);
+ bUpdate = true;
+ }
+ }
+
+ // 3D-Shadow
+ eState = rAttrs.GetItemState(SDRATTR_3DOBJ_SHADOW_3D);
+ if( eState != SfxItemState::DONTCARE )
+ {
+ bool bValue = rAttrs.Get(SDRATTR_3DOBJ_SHADOW_3D).GetValue();
+ if( bValue != m_xBtnShadow3d->get_active() )
+ {
+ m_xBtnShadow3d->set_active( bValue );
+ m_xFtSlant->set_sensitive( bValue );
+ m_xMtrSlant->set_sensitive( bValue );
+ bUpdate = true;
+ }
+ else if( m_xBtnShadow3d->get_state() == TRISTATE_INDET )
+ m_xBtnShadow3d->set_active( bValue );
+ }
+ else
+ {
+ if( m_xBtnShadow3d->get_state() != TRISTATE_INDET )
+ {
+ m_xBtnShadow3d->set_state( TRISTATE_INDET );
+ bUpdate = true;
+ }
+ }
+
+ // Inclination (Shadow)
+ eState = rAttrs.GetItemState(SDRATTR_3DSCENE_SHADOW_SLANT);
+ if( eState != SfxItemState::DONTCARE )
+ {
+ sal_uInt16 nValue = rAttrs.Get(SDRATTR_3DSCENE_SHADOW_SLANT).GetValue();
+ if( nValue != m_xMtrSlant->get_value(FieldUnit::DEGREE) )
+ {
+ m_xMtrSlant->set_value(nValue, FieldUnit::DEGREE);
+ bUpdate = true;
+ }
+ }
+ else
+ {
+ if( !m_xMtrSlant->get_text().isEmpty() )
+ {
+ m_xMtrSlant->set_text("");
+ bUpdate = true;
+ }
+ }
+
+ // Distance
+ eState = rAttrs.GetItemState(SDRATTR_3DSCENE_DISTANCE);
+ if( eState != SfxItemState::DONTCARE )
+ {
+ sal_uInt32 nValue = rAttrs.Get(SDRATTR_3DSCENE_DISTANCE).GetValue();
+ sal_uInt32 nValue2 = GetCoreValue(*m_xMtrDistance, ePoolUnit);
+ if( nValue != nValue2 )
+ {
+ if( eFUnit != m_xMtrDistance->get_unit() )
+ SetFieldUnit(*m_xMtrDistance, eFUnit);
+
+ SetMetricValue(*m_xMtrDistance, nValue, ePoolUnit);
+ bUpdate = true;
+ }
+ }
+ else
+ {
+ if( !m_xMtrDepth->get_text().isEmpty() )
+ {
+ m_xMtrDepth->set_text("");
+ bUpdate = true;
+ }
+ }
+
+ // Focal length
+ eState = rAttrs.GetItemState(SDRATTR_3DSCENE_FOCAL_LENGTH);
+ if( eState != SfxItemState::DONTCARE )
+ {
+ sal_uInt32 nValue = rAttrs.Get(SDRATTR_3DSCENE_FOCAL_LENGTH).GetValue();
+ sal_uInt32 nValue2 = GetCoreValue(*m_xMtrFocalLength, ePoolUnit);
+ if( nValue != nValue2 )
+ {
+ if( eFUnit != m_xMtrFocalLength->get_unit() )
+ SetFieldUnit(*m_xMtrFocalLength, eFUnit);
+
+ SetMetricValue(*m_xMtrFocalLength, nValue, ePoolUnit);
+ bUpdate = true;
+ }
+ }
+ else
+ {
+ if( !m_xMtrFocalLength->get_text().isEmpty() )
+ {
+ m_xMtrFocalLength->set_text("");
+ bUpdate = true;
+ }
+ }
+
+// Lighting
+ Color aColor;
+ // Light 1 (Color)
+ eState = rAttrs.GetItemState(SDRATTR_3DSCENE_LIGHTCOLOR_1);
+ if( eState != SfxItemState::DONTCARE )
+ {
+ aColor = rAttrs.Get(SDRATTR_3DSCENE_LIGHTCOLOR_1).GetValue();
+ ColorListBox* pLb = m_xLbLight1.get();
+ if( aColor != pLb->GetSelectEntryColor() )
+ {
+ LBSelectColor( pLb, aColor );
+ bUpdate = true;
+ }
+ }
+ else
+ {
+ if (!m_xLbLight1->IsNoSelection())
+ {
+ m_xLbLight1->SetNoSelection();
+ bUpdate = true;
+ }
+ }
+ // Light 1 (on/off)
+ eState = rAttrs.GetItemState(SDRATTR_3DSCENE_LIGHTON_1);
+ if( eState != SfxItemState::DONTCARE )
+ {
+ bool bOn = rAttrs.Get(SDRATTR_3DSCENE_LIGHTON_1).GetValue();
+ if (bOn != m_xBtnLight1->isLightOn())
+ {
+ m_xBtnLight1->switchLightOn(bOn);
+ bUpdate = true;
+ }
+ if( m_xBtnLight1->get_state() == TRISTATE_INDET )
+ m_xBtnLight1->set_active( m_xBtnLight1->get_active() );
+ }
+ else
+ {
+ if( m_xBtnLight1->get_state() != TRISTATE_INDET )
+ {
+ m_xBtnLight1->set_state( TRISTATE_INDET );
+ bUpdate = true;
+ }
+ }
+ // Light 1 (direction)
+ eState = rAttrs.GetItemState(SDRATTR_3DSCENE_LIGHTDIRECTION_1);
+ if( eState != SfxItemState::DONTCARE )
+ {
+ bUpdate = true;
+ }
+
+ //Light 2 (color)
+ eState = rAttrs.GetItemState(SDRATTR_3DSCENE_LIGHTCOLOR_2);
+ if( eState != SfxItemState::DONTCARE )
+ {
+ aColor = rAttrs.Get(SDRATTR_3DSCENE_LIGHTCOLOR_2).GetValue();
+ ColorListBox* pLb = m_xLbLight2.get();
+ if( aColor != pLb->GetSelectEntryColor() )
+ {
+ LBSelectColor( pLb, aColor );
+ bUpdate = true;
+ }
+ }
+ else
+ {
+ if (!m_xLbLight2->IsNoSelection())
+ {
+ m_xLbLight2->SetNoSelection();
+ bUpdate = true;
+ }
+ }
+ // Light 2 (on/off)
+ eState = rAttrs.GetItemState(SDRATTR_3DSCENE_LIGHTON_2);
+ if( eState != SfxItemState::DONTCARE )
+ {
+ bool bOn = rAttrs.Get(SDRATTR_3DSCENE_LIGHTON_2).GetValue();
+ if (bOn != m_xBtnLight2->isLightOn())
+ {
+ m_xBtnLight2->switchLightOn(bOn);
+ bUpdate = true;
+ }
+ if( m_xBtnLight2->get_state() == TRISTATE_INDET )
+ m_xBtnLight2->set_active( m_xBtnLight2->get_active() );
+ }
+ else
+ {
+ if( m_xBtnLight2->get_state() != TRISTATE_INDET )
+ {
+ m_xBtnLight2->set_state( TRISTATE_INDET );
+ bUpdate = true;
+ }
+ }
+ //Light 2 (Direction)
+ eState = rAttrs.GetItemState(SDRATTR_3DSCENE_LIGHTDIRECTION_2);
+ if( eState != SfxItemState::DONTCARE )
+ {
+ bUpdate = true;
+ }
+
+ //Light 3 (color)
+ eState = rAttrs.GetItemState(SDRATTR_3DSCENE_LIGHTCOLOR_3);
+ if( eState != SfxItemState::DONTCARE )
+ {
+ aColor = rAttrs.Get(SDRATTR_3DSCENE_LIGHTCOLOR_3).GetValue();
+ ColorListBox* pLb = m_xLbLight3.get();
+ if( aColor != pLb->GetSelectEntryColor() )
+ {
+ LBSelectColor( pLb, aColor );
+ bUpdate = true;
+ }
+ }
+ else
+ {
+ if (!m_xLbLight3->IsNoSelection())
+ {
+ m_xLbLight3->SetNoSelection();
+ bUpdate = true;
+ }
+ }
+ // Light 3 (on/off)
+ eState = rAttrs.GetItemState(SDRATTR_3DSCENE_LIGHTON_3);
+ if( eState != SfxItemState::DONTCARE )
+ {
+ bool bOn = rAttrs.Get(SDRATTR_3DSCENE_LIGHTON_3).GetValue();
+ if (bOn != m_xBtnLight3->isLightOn())
+ {
+ m_xBtnLight3->switchLightOn(bOn);
+ bUpdate = true;
+ }
+ if( m_xBtnLight3->get_state() == TRISTATE_INDET )
+ m_xBtnLight3->set_active( m_xBtnLight3->get_active() );
+ }
+ else
+ {
+ if( m_xBtnLight3->get_state() != TRISTATE_INDET )
+ {
+ m_xBtnLight3->set_state( TRISTATE_INDET );
+ bUpdate = true;
+ }
+ }
+ // Light 3 (Direction)
+ eState = rAttrs.GetItemState(SDRATTR_3DSCENE_LIGHTDIRECTION_3);
+ if( eState != SfxItemState::DONTCARE )
+ {
+ bUpdate = true;
+ }
+
+ // Light 4 (Color)
+ eState = rAttrs.GetItemState(SDRATTR_3DSCENE_LIGHTCOLOR_4);
+ if( eState != SfxItemState::DONTCARE )
+ {
+ aColor = rAttrs.Get(SDRATTR_3DSCENE_LIGHTCOLOR_4).GetValue();
+ ColorListBox* pLb = m_xLbLight4.get();
+ if( aColor != pLb->GetSelectEntryColor() )
+ {
+ LBSelectColor( pLb, aColor );
+ bUpdate = true;
+ }
+ }
+ else
+ {
+ if (!m_xLbLight4->IsNoSelection())
+ {
+ m_xLbLight4->SetNoSelection();
+ bUpdate = true;
+ }
+ }
+ // Light 4 (on/off)
+ eState = rAttrs.GetItemState(SDRATTR_3DSCENE_LIGHTON_4);
+ if( eState != SfxItemState::DONTCARE )
+ {
+ bool bOn = rAttrs.Get(SDRATTR_3DSCENE_LIGHTON_4).GetValue();
+ if (bOn != m_xBtnLight4->isLightOn())
+ {
+ m_xBtnLight4->switchLightOn(bOn);
+ bUpdate = true;
+ }
+ if( m_xBtnLight4->get_state() == TRISTATE_INDET )
+ m_xBtnLight4->set_active( m_xBtnLight4->get_active() );
+ }
+ else
+ {
+ if( m_xBtnLight4->get_state() != TRISTATE_INDET )
+ {
+ m_xBtnLight4->set_state( TRISTATE_INDET );
+ bUpdate = true;
+ }
+ }
+ // Light 4 (direction)
+ eState = rAttrs.GetItemState(SDRATTR_3DSCENE_LIGHTDIRECTION_4);
+ if( eState != SfxItemState::DONTCARE )
+ {
+ bUpdate = true;
+ }
+
+ // Light 5 (color)
+ eState = rAttrs.GetItemState(SDRATTR_3DSCENE_LIGHTCOLOR_5);
+ if( eState != SfxItemState::DONTCARE )
+ {
+ aColor = rAttrs.Get(SDRATTR_3DSCENE_LIGHTCOLOR_5).GetValue();
+ ColorListBox* pLb = m_xLbLight5.get();
+ if( aColor != pLb->GetSelectEntryColor() )
+ {
+ LBSelectColor( pLb, aColor );
+ bUpdate = true;
+ }
+ }
+ else
+ {
+ if (!m_xLbLight5->IsNoSelection())
+ {
+ m_xLbLight5->SetNoSelection();
+ bUpdate = true;
+ }
+ }
+ // Light 5 (on/off)
+ eState = rAttrs.GetItemState(SDRATTR_3DSCENE_LIGHTON_5);
+ if( eState != SfxItemState::DONTCARE )
+ {
+ bool bOn = rAttrs.Get(SDRATTR_3DSCENE_LIGHTON_5).GetValue();
+ if (bOn != m_xBtnLight5->isLightOn())
+ {
+ m_xBtnLight5->switchLightOn(bOn);
+ bUpdate = true;
+ }
+ if( m_xBtnLight5->get_state() == TRISTATE_INDET )
+ m_xBtnLight5->set_active( m_xBtnLight5->get_active() );
+ }
+ else
+ {
+ if( m_xBtnLight5->get_state() != TRISTATE_INDET )
+ {
+ m_xBtnLight5->set_state( TRISTATE_INDET );
+ bUpdate = true;
+ }
+ }
+ // Light 5 (direction)
+ eState = rAttrs.GetItemState(SDRATTR_3DSCENE_LIGHTDIRECTION_5);
+ if( eState != SfxItemState::DONTCARE )
+ {
+ bUpdate = true;
+ }
+
+ // Light 6 (color)
+ eState = rAttrs.GetItemState(SDRATTR_3DSCENE_LIGHTCOLOR_6);
+ if( eState != SfxItemState::DONTCARE )
+ {
+ aColor = rAttrs.Get(SDRATTR_3DSCENE_LIGHTCOLOR_6).GetValue();
+ ColorListBox* pLb = m_xLbLight6.get();
+ if( aColor != pLb->GetSelectEntryColor() )
+ {
+ LBSelectColor( pLb, aColor );
+ bUpdate = true;
+ }
+ }
+ else
+ {
+ if (!m_xLbLight6->IsNoSelection())
+ {
+ m_xLbLight6->SetNoSelection();
+ bUpdate = true;
+ }
+ }
+ // Light 6 (on/off)
+ eState = rAttrs.GetItemState(SDRATTR_3DSCENE_LIGHTON_6);
+ if( eState != SfxItemState::DONTCARE )
+ {
+ bool bOn = rAttrs.Get(SDRATTR_3DSCENE_LIGHTON_6).GetValue();
+ if (bOn != m_xBtnLight6->isLightOn())
+ {
+ m_xBtnLight6->switchLightOn(bOn);
+ bUpdate = true;
+ }
+ if( m_xBtnLight6->get_state() == TRISTATE_INDET )
+ m_xBtnLight6->set_active( m_xBtnLight6->get_active() );
+ }
+ else
+ {
+ if( m_xBtnLight6->get_state() != TRISTATE_INDET )
+ {
+ m_xBtnLight6->set_state( TRISTATE_INDET );
+ bUpdate = true;
+ }
+ }
+ // Light 6 (direction)
+ eState = rAttrs.GetItemState(SDRATTR_3DSCENE_LIGHTDIRECTION_6);
+ if( eState != SfxItemState::DONTCARE )
+ {
+ bUpdate = true;
+ }
+
+ // Light 7 (color)
+ eState = rAttrs.GetItemState(SDRATTR_3DSCENE_LIGHTCOLOR_7);
+ if( eState != SfxItemState::DONTCARE )
+ {
+ aColor = rAttrs.Get(SDRATTR_3DSCENE_LIGHTCOLOR_7).GetValue();
+ ColorListBox* pLb = m_xLbLight7.get();
+ if( aColor != pLb->GetSelectEntryColor() )
+ {
+ LBSelectColor( pLb, aColor );
+ bUpdate = true;
+ }
+ }
+ else
+ {
+ if (!m_xLbLight7->IsNoSelection())
+ {
+ m_xLbLight7->SetNoSelection();
+ bUpdate = true;
+ }
+ }
+ // Light 7 (on/off)
+ eState = rAttrs.GetItemState(SDRATTR_3DSCENE_LIGHTON_7);
+ if( eState != SfxItemState::DONTCARE )
+ {
+ bool bOn = rAttrs.Get(SDRATTR_3DSCENE_LIGHTON_7).GetValue();
+ if (bOn != m_xBtnLight7->isLightOn())
+ {
+ m_xBtnLight7->switchLightOn(bOn);
+ bUpdate = true;
+ }
+ if( m_xBtnLight7->get_state() == TRISTATE_INDET )
+ m_xBtnLight7->set_active( m_xBtnLight7->get_active() );
+ }
+ else
+ {
+ if( m_xBtnLight7->get_state() != TRISTATE_INDET )
+ {
+ m_xBtnLight7->set_state( TRISTATE_INDET );
+ bUpdate = true;
+ }
+ }
+ // Light 7 (direction)
+ eState = rAttrs.GetItemState(SDRATTR_3DSCENE_LIGHTDIRECTION_7);
+ if( eState != SfxItemState::DONTCARE )
+ {
+ bUpdate = true;
+ }
+
+ // Light 8 (color)
+ eState = rAttrs.GetItemState(SDRATTR_3DSCENE_LIGHTCOLOR_8);
+ if( eState != SfxItemState::DONTCARE )
+ {
+ aColor = rAttrs.Get(SDRATTR_3DSCENE_LIGHTCOLOR_8).GetValue();
+ ColorListBox* pLb = m_xLbLight8.get();
+ if( aColor != pLb->GetSelectEntryColor() )
+ {
+ LBSelectColor( pLb, aColor );
+ bUpdate = true;
+ }
+ }
+ else
+ {
+ if (!m_xLbLight8->IsNoSelection())
+ {
+ m_xLbLight8->SetNoSelection();
+ bUpdate = true;
+ }
+ }
+ // Light 8 (on/off)
+ eState = rAttrs.GetItemState(SDRATTR_3DSCENE_LIGHTON_8);
+ if( eState != SfxItemState::DONTCARE )
+ {
+ bool bOn = rAttrs.Get(SDRATTR_3DSCENE_LIGHTON_8).GetValue();
+ if (bOn != m_xBtnLight8->isLightOn())
+ {
+ m_xBtnLight8->switchLightOn(bOn);
+ bUpdate = true;
+ }
+ if( m_xBtnLight8->get_state() == TRISTATE_INDET )
+ m_xBtnLight8->set_active( m_xBtnLight8->get_active() );
+ }
+ else
+ {
+ if( m_xBtnLight8->get_state() != TRISTATE_INDET )
+ {
+ m_xBtnLight8->set_state( TRISTATE_INDET );
+ bUpdate = true;
+ }
+ }
+ // Light 8 (direction)
+ eState = rAttrs.GetItemState(SDRATTR_3DSCENE_LIGHTDIRECTION_8);
+ if( eState != SfxItemState::DONTCARE )
+ {
+ bUpdate = true;
+ }
+
+ // Ambient light
+ eState = rAttrs.GetItemState(SDRATTR_3DSCENE_AMBIENTCOLOR);
+ if( eState != SfxItemState::DONTCARE )
+ {
+ aColor = rAttrs.Get(SDRATTR_3DSCENE_AMBIENTCOLOR).GetValue();
+ ColorListBox* pLb = m_xLbAmbientlight.get();
+ if( aColor != pLb->GetSelectEntryColor() )
+ {
+ LBSelectColor( pLb, aColor );
+ bUpdate = true;
+ }
+ }
+ else
+ {
+ if (!m_xLbAmbientlight->IsNoSelection())
+ {
+ m_xLbAmbientlight->SetNoSelection();
+ bUpdate = true;
+ }
+ }
+
+
+// Textures
+ // Art
+ if( bBitmap )
+ {
+ eState = rAttrs.GetItemState(SDRATTR_3DOBJ_TEXTURE_KIND);
+ if( eState != SfxItemState::DONTCARE )
+ {
+ sal_uInt16 nValue = rAttrs.Get(SDRATTR_3DOBJ_TEXTURE_KIND).GetValue();
+
+ if( ( !m_xBtnTexLuminance->get_active() && nValue == 1 ) ||
+ ( !m_xBtnTexColor->get_active() && nValue == 3 ) )
+ {
+ m_xBtnTexLuminance->set_active( nValue == 1 );
+ m_xBtnTexColor->set_active( nValue == 3 );
+ bUpdate = true;
+ }
+ }
+ else
+ {
+ if( m_xBtnTexLuminance->get_active() ||
+ m_xBtnTexColor->get_active() )
+ {
+ m_xBtnTexLuminance->set_active( false );
+ m_xBtnTexColor->set_active( false );
+ bUpdate = true;
+ }
+ }
+
+ // Mode
+ eState = rAttrs.GetItemState(SDRATTR_3DOBJ_TEXTURE_MODE);
+ if( eState != SfxItemState::DONTCARE )
+ {
+ sal_uInt16 nValue = rAttrs.Get(SDRATTR_3DOBJ_TEXTURE_MODE).GetValue();
+
+ if( ( !m_xBtnTexReplace->get_active() && nValue == 1 ) ||
+ ( !m_xBtnTexModulate->get_active() && nValue == 2 ) )
+ {
+ m_xBtnTexReplace->set_active( nValue == 1 );
+ m_xBtnTexModulate->set_active( nValue == 2 );
+ bUpdate = true;
+ }
+ }
+ else
+ {
+ if( m_xBtnTexReplace->get_active() ||
+ m_xBtnTexModulate->get_active() )
+ {
+ m_xBtnTexReplace->set_active( false );
+ m_xBtnTexModulate->set_active( false );
+ bUpdate = true;
+ }
+ }
+
+ // Projection X
+ eState = rAttrs.GetItemState(SDRATTR_3DOBJ_TEXTURE_PROJ_X);
+ if( eState != SfxItemState::DONTCARE )
+ {
+ sal_uInt16 nValue = rAttrs.Get(SDRATTR_3DOBJ_TEXTURE_PROJ_X).GetValue();
+
+ if( ( !m_xBtnTexObjectX->get_active() && nValue == 0 ) ||
+ ( !m_xBtnTexParallelX->get_active() && nValue == 1 ) ||
+ ( !m_xBtnTexCircleX->get_active() && nValue == 2 ) )
+ {
+ m_xBtnTexObjectX->set_active( nValue == 0 );
+ m_xBtnTexParallelX->set_active( nValue == 1 );
+ m_xBtnTexCircleX->set_active( nValue == 2 );
+ bUpdate = true;
+ }
+ }
+ else
+ {
+ if( m_xBtnTexObjectX->get_active() ||
+ m_xBtnTexParallelX->get_active() ||
+ m_xBtnTexCircleX->get_active() )
+ {
+ m_xBtnTexObjectX->set_active( false );
+ m_xBtnTexParallelX->set_active( false );
+ m_xBtnTexCircleX->set_active( false );
+ bUpdate = true;
+ }
+ }
+
+ // Projection Y
+ eState = rAttrs.GetItemState(SDRATTR_3DOBJ_TEXTURE_PROJ_Y);
+ if( eState != SfxItemState::DONTCARE )
+ {
+ sal_uInt16 nValue = rAttrs.Get(SDRATTR_3DOBJ_TEXTURE_PROJ_Y).GetValue();
+
+ if( ( !m_xBtnTexObjectY->get_active() && nValue == 0 ) ||
+ ( !m_xBtnTexParallelY->get_active() && nValue == 1 ) ||
+ ( !m_xBtnTexCircleY->get_active() && nValue == 2 ) )
+ {
+ m_xBtnTexObjectY->set_active( nValue == 0 );
+ m_xBtnTexParallelY->set_active( nValue == 1 );
+ m_xBtnTexCircleY->set_active( nValue == 2 );
+ bUpdate = true;
+ }
+ }
+ else
+ {
+ if( m_xBtnTexObjectY->get_active() ||
+ m_xBtnTexParallelY->get_active() ||
+ m_xBtnTexCircleY->get_active() )
+ {
+ m_xBtnTexObjectY->set_active( false );
+ m_xBtnTexParallelY->set_active( false );
+ m_xBtnTexCircleY->set_active( false );
+ bUpdate = true;
+ }
+ }
+
+ // Filter
+ eState = rAttrs.GetItemState(SDRATTR_3DOBJ_TEXTURE_FILTER);
+ if( eState != SfxItemState::DONTCARE )
+ {
+ bool bValue = rAttrs.Get(SDRATTR_3DOBJ_TEXTURE_FILTER).GetValue();
+ if( bValue != m_xBtnTexFilter->get_active() )
+ {
+ m_xBtnTexFilter->set_active( bValue );
+ bUpdate = true;
+ }
+ if( m_xBtnTexFilter->get_state() == TRISTATE_INDET )
+ m_xBtnTexFilter->set_active( bValue );
+ }
+ else
+ {
+ if( m_xBtnTexFilter->get_state() != TRISTATE_INDET )
+ {
+ m_xBtnTexFilter->set_state( TRISTATE_INDET );
+ bUpdate = true;
+ }
+ }
+ }
+
+
+ // Material Favorites
+ m_xLbMatFavorites->set_active( 0 );
+
+ // Object color
+ eState = rAttrs.GetItemState(XATTR_FILLCOLOR);
+ if( eState != SfxItemState::DONTCARE )
+ {
+ aColor = rAttrs.Get(XATTR_FILLCOLOR).GetColorValue();
+ ColorListBox* pLb = m_xLbMatColor.get();
+ if( aColor != pLb->GetSelectEntryColor() )
+ {
+ LBSelectColor( pLb, aColor );
+ bUpdate = true;
+ }
+ }
+ else
+ {
+ if (!m_xLbMatColor->IsNoSelection())
+ {
+ m_xLbMatColor->SetNoSelection();
+ bUpdate = true;
+ }
+ }
+
+ // Self-luminous color
+ eState = rAttrs.GetItemState(SDRATTR_3DOBJ_MAT_EMISSION);
+ if( eState != SfxItemState::DONTCARE )
+ {
+ aColor = rAttrs.Get(SDRATTR_3DOBJ_MAT_EMISSION).GetValue();
+ ColorListBox* pLb = m_xLbMatEmission.get();
+ if( aColor != pLb->GetSelectEntryColor() )
+ {
+ LBSelectColor( pLb, aColor );
+ bUpdate = true;
+ }
+ }
+ else
+ {
+ if (!m_xLbMatEmission->IsNoSelection())
+ {
+ m_xLbMatEmission->SetNoSelection();
+ bUpdate = true;
+ }
+ }
+
+ // Specular
+ eState = rAttrs.GetItemState(SDRATTR_3DOBJ_MAT_SPECULAR);
+ if( eState != SfxItemState::DONTCARE )
+ {
+ aColor = rAttrs.Get(SDRATTR_3DOBJ_MAT_SPECULAR).GetValue();
+ ColorListBox* pLb = m_xLbMatSpecular.get();
+ if( aColor != pLb->GetSelectEntryColor() )
+ {
+ LBSelectColor( pLb, aColor );
+ bUpdate = true;
+ }
+ }
+ else
+ {
+ if (!m_xLbMatSpecular->IsNoSelection())
+ {
+ m_xLbMatSpecular->SetNoSelection();
+ bUpdate = true;
+ }
+ }
+
+ // Specular Intensity
+ eState = rAttrs.GetItemState(SDRATTR_3DOBJ_MAT_SPECULAR_INTENSITY);
+ if( eState != SfxItemState::DONTCARE )
+ {
+ sal_uInt16 nValue = rAttrs.Get(SDRATTR_3DOBJ_MAT_SPECULAR_INTENSITY).GetValue();
+ if( nValue != m_xMtrMatSpecularIntensity->get_value(FieldUnit::PERCENT) )
+ {
+ m_xMtrMatSpecularIntensity->set_value(nValue, FieldUnit::PERCENT);
+ bUpdate = true;
+ }
+ }
+ else
+ {
+ if( !m_xMtrMatSpecularIntensity->get_text().isEmpty() )
+ {
+ m_xMtrMatSpecularIntensity->set_text("");
+ bUpdate = true;
+ }
+ }
+
+
+// Other
+ // Perspective
+ eState = rAttrs.GetItemState(SDRATTR_3DSCENE_PERSPECTIVE);
+ if( eState != SfxItemState::DONTCARE )
+ {
+ ProjectionType ePT = static_cast<ProjectionType>(rAttrs.Get(SDRATTR_3DSCENE_PERSPECTIVE).GetValue());
+ if( ( !m_xBtnPerspective->get_active() && ePT == ProjectionType::Perspective ) ||
+ ( m_xBtnPerspective->get_active() && ePT == ProjectionType::Parallel ) )
+ {
+ m_xBtnPerspective->set_active( ePT == ProjectionType::Perspective );
+ bUpdate = true;
+ }
+ if( m_xBtnPerspective->get_state() == TRISTATE_INDET )
+ m_xBtnPerspective->set_active( ePT == ProjectionType::Perspective );
+ }
+ else
+ {
+ if( m_xBtnPerspective->get_state() != TRISTATE_INDET )
+ {
+ m_xBtnPerspective->set_state( TRISTATE_INDET );
+ bUpdate = true;
+ }
+ }
+
+ if( !bUpdate )
+ {
+ // however the 2D attributes may be different. Compare these and decide
+
+ bUpdate = true;
+ }
+
+ // Update preview
+ SfxItemSet aSet(rAttrs);
+
+ // set LineStyle hard to drawing::LineStyle_NONE when it's not set so that
+ // the default (drawing::LineStyle_SOLID) is not used for 3d preview
+ if(SfxItemState::SET != aSet.GetItemState(XATTR_LINESTYLE, false))
+ aSet.Put(XLineStyleItem(drawing::LineStyle_NONE));
+
+ // set FillColor hard to WHITE when it's SfxItemState::DONTCARE so that
+ // the default (Blue7) is not used for 3d preview
+ if(SfxItemState::DONTCARE == aSet.GetItemState(XATTR_FILLCOLOR, false))
+ aSet.Put(XFillColorItem(OUString(), COL_WHITE));
+
+ m_xCtlPreview->Set3DAttributes(aSet);
+ m_xCtlLightPreview->GetSvx3DLightControl().Set3DAttributes(aSet);
+
+ // try to select light corresponding to active button
+ sal_uInt32 nNumber(0xffffffff);
+
+ if(m_xBtnLight1->get_active())
+ nNumber = 0;
+ else if(m_xBtnLight2->get_active())
+ nNumber = 1;
+ else if(m_xBtnLight3->get_active())
+ nNumber = 2;
+ else if(m_xBtnLight4->get_active())
+ nNumber = 3;
+ else if(m_xBtnLight5->get_active())
+ nNumber = 4;
+ else if(m_xBtnLight6->get_active())
+ nNumber = 5;
+ else if(m_xBtnLight7->get_active())
+ nNumber = 6;
+ else if(m_xBtnLight8->get_active())
+ nNumber = 7;
+
+ if(nNumber != 0xffffffff)
+ {
+ m_xCtlLightPreview->GetSvx3DLightControl().SelectLight(nNumber);
+ }
+
+ // handle state of converts possible
+ m_xBtnConvertTo3D->set_sensitive(pConvertTo3DItem->GetState());
+ m_xBtnLatheObject->set_sensitive(pConvertTo3DLatheItem->GetState());
+}
+
+
+void Svx3DWin::GetAttr( SfxItemSet& rAttrs )
+{
+ // get remembered 2d attributes from the dialog
+ if(mpRemember2DAttributes)
+ {
+ SfxWhichIter aIter(*mpRemember2DAttributes);
+ sal_uInt16 nWhich(aIter.FirstWhich());
+
+ while(nWhich)
+ {
+ SfxItemState eState = aIter.GetItemState(false);
+ if(SfxItemState::DONTCARE == eState)
+ rAttrs.InvalidateItem(nWhich);
+ else if(SfxItemState::SET == eState)
+ rAttrs.Put(mpRemember2DAttributes->Get(nWhich, false));
+
+ nWhich = aIter.NextWhich();
+ }
+ }
+
+//Others must stand as the front on all sides
+ // Perspective
+ if( m_xBtnPerspective->get_state() != TRISTATE_INDET )
+ {
+ ProjectionType nValue;
+ if( m_xBtnPerspective->get_active() )
+ nValue = ProjectionType::Perspective;
+ else
+ nValue = ProjectionType::Parallel;
+ rAttrs.Put(Svx3DPerspectiveItem(nValue));
+ }
+ else
+ rAttrs.InvalidateItem(SDRATTR_3DSCENE_PERSPECTIVE);
+
+// Geometry
+ // Possible determine PoolUnit (in this case this has not happened in Update() )
+ if( !mpImpl->pPool )
+ {
+ OSL_FAIL( "No Pool in GetAttr()! May be incompatible to drviewsi.cxx ?" );
+ mpImpl->pPool = rAttrs.GetPool();
+ DBG_ASSERT( mpImpl->pPool, "Where is the Pool?" );
+ ePoolUnit = mpImpl->pPool->GetMetric( SID_ATTR_LINE_WIDTH );
+
+ eFUnit = GetModuleFieldUnit( rAttrs );
+ }
+
+ // Number of segments (horizontal)
+ if( !m_xNumHorizontal->get_text().isEmpty() )
+ {
+ sal_uInt32 nValue = static_cast<sal_uInt32>(m_xNumHorizontal->get_value());
+ rAttrs.Put(makeSvx3DHorizontalSegmentsItem(nValue));
+ }
+ else
+ rAttrs.InvalidateItem(SDRATTR_3DOBJ_HORZ_SEGS);
+
+ // Number of segments (vertical)
+ if( !m_xNumVertical->get_text().isEmpty() )
+ {
+ sal_uInt32 nValue = static_cast<sal_uInt32>(m_xNumVertical->get_value());
+ rAttrs.Put(makeSvx3DVerticalSegmentsItem(nValue));
+ }
+ else
+ rAttrs.InvalidateItem(SDRATTR_3DOBJ_VERT_SEGS);
+
+ // Depth
+ if( !m_xMtrDepth->get_text().isEmpty() )
+ {
+ sal_uInt32 nValue = GetCoreValue(*m_xMtrDepth, ePoolUnit);
+ rAttrs.Put(makeSvx3DDepthItem(nValue));
+ }
+ else
+ rAttrs.InvalidateItem(SDRATTR_3DOBJ_DEPTH);
+
+ // Double-sided
+ TriState eState = m_xBtnDoubleSided->get_state();
+ if( eState != TRISTATE_INDET )
+ {
+ bool bValue = TRISTATE_TRUE == eState;
+ rAttrs.Put(makeSvx3DDoubleSidedItem(bValue));
+ }
+ else
+ rAttrs.InvalidateItem(SDRATTR_3DOBJ_DOUBLE_SIDED);
+
+ // Edge rounding
+ if( !m_xMtrPercentDiagonal->get_text().isEmpty() )
+ {
+ sal_uInt16 nValue = static_cast<sal_uInt16>(m_xMtrPercentDiagonal->get_value(FieldUnit::PERCENT));
+ rAttrs.Put(makeSvx3DPercentDiagonalItem(nValue));
+ }
+ else
+ rAttrs.InvalidateItem(SDRATTR_3DOBJ_PERCENT_DIAGONAL);
+
+ // Depth scale
+ if( !m_xMtrBackscale->get_text().isEmpty() )
+ {
+ sal_uInt16 nValue = static_cast<sal_uInt16>(m_xMtrBackscale->get_value(FieldUnit::PERCENT));
+ rAttrs.Put(makeSvx3DBackscaleItem(nValue));
+ }
+ else
+ rAttrs.InvalidateItem(SDRATTR_3DOBJ_BACKSCALE);
+
+ // End angle
+ if( !m_xMtrEndAngle->get_text().isEmpty() )
+ {
+ sal_uInt16 nValue = static_cast<sal_uInt16>(m_xMtrEndAngle->get_value(FieldUnit::DEGREE));
+ rAttrs.Put(makeSvx3DEndAngleItem(nValue));
+ }
+ else
+ rAttrs.InvalidateItem(SDRATTR_3DOBJ_END_ANGLE);
+
+ // Normal type
+ sal_uInt16 nValue = 99;
+ if( m_xBtnNormalsObj->get_active() )
+ nValue = 0;
+ else if( m_xBtnNormalsFlat->get_active() )
+ nValue = 1;
+ else if( m_xBtnNormalsSphere->get_active() )
+ nValue = 2;
+
+ if( nValue <= 2 )
+ rAttrs.Put(Svx3DNormalsKindItem(nValue));
+ else
+ rAttrs.InvalidateItem(SDRATTR_3DOBJ_NORMALS_KIND);
+
+ // Normal inverted
+ eState = m_xBtnNormalsInvert->get_state();
+ if( eState != TRISTATE_INDET )
+ {
+ bool bValue = TRISTATE_TRUE == eState;
+ rAttrs.Put(makeSvx3DNormalsInvertItem(bValue));
+ }
+ else
+ rAttrs.InvalidateItem(SDRATTR_3DOBJ_NORMALS_INVERT);
+
+ // 2-sided lighting
+ eState = m_xBtnTwoSidedLighting->get_state();
+ if( eState != TRISTATE_INDET )
+ {
+ bool bValue = TRISTATE_TRUE == eState;
+ rAttrs.Put(makeSvx3DTwoSidedLightingItem(bValue));
+ }
+ else
+ rAttrs.InvalidateItem(SDRATTR_3DSCENE_TWO_SIDED_LIGHTING);
+
+// Representation
+ // Shade mode
+ if( m_xLbShademode->get_active() != -1 )
+ {
+ nValue = m_xLbShademode->get_active();
+ rAttrs.Put(Svx3DShadeModeItem(nValue));
+ }
+ else
+ rAttrs.InvalidateItem(SDRATTR_3DSCENE_SHADE_MODE);
+
+ // 3D-Shadow
+ eState = m_xBtnShadow3d->get_state();
+ if( eState != TRISTATE_INDET )
+ {
+ bool bValue = TRISTATE_TRUE == eState;
+ rAttrs.Put(makeSvx3DShadow3DItem(bValue));
+ rAttrs.Put(makeSdrShadowItem(bValue));
+ }
+ else
+ {
+ rAttrs.InvalidateItem(SDRATTR_3DOBJ_SHADOW_3D);
+ rAttrs.InvalidateItem(SDRATTR_SHADOW);
+ }
+
+ // Slant (Shadow)
+ if( !m_xMtrSlant->get_text().isEmpty() )
+ {
+ sal_uInt16 nValue2 = static_cast<sal_uInt16>(m_xMtrSlant->get_value(FieldUnit::DEGREE));
+ rAttrs.Put(makeSvx3DShadowSlantItem(nValue2));
+ }
+ else
+ rAttrs.InvalidateItem(SDRATTR_3DSCENE_SHADOW_SLANT);
+
+ // Distance
+ if( !m_xMtrDistance->get_text().isEmpty() )
+ {
+ sal_uInt32 nValue2 = GetCoreValue(*m_xMtrDistance, ePoolUnit);
+ rAttrs.Put(makeSvx3DDistanceItem(nValue2));
+ }
+ else
+ rAttrs.InvalidateItem(SDRATTR_3DSCENE_DISTANCE);
+
+ // Focal length
+ if( !m_xMtrFocalLength->get_text().isEmpty() )
+ {
+ sal_uInt32 nValue2 = GetCoreValue(*m_xMtrFocalLength, ePoolUnit);
+ rAttrs.Put(makeSvx3DFocalLengthItem(nValue2));
+ }
+ else
+ rAttrs.InvalidateItem(SDRATTR_3DSCENE_FOCAL_LENGTH);
+
+ // Lighting
+ Color aColor;
+ const SfxItemSet aLightItemSet(m_xCtlLightPreview->GetSvx3DLightControl().Get3DAttributes());
+
+ // Light 1 color
+ if (!m_xLbLight1->IsNoSelection())
+ {
+ aColor = m_xLbLight1->GetSelectEntryColor();
+ rAttrs.Put(makeSvx3DLightcolor1Item(aColor));
+ }
+ else
+ rAttrs.InvalidateItem(SDRATTR_3DSCENE_LIGHTCOLOR_1);
+ // Light 1 (on/off)
+ eState = m_xBtnLight1->get_state();
+ if( eState != TRISTATE_INDET )
+ {
+ bool bValue = m_xBtnLight1->isLightOn();
+ rAttrs.Put(makeSvx3DLightOnOff1Item(bValue));
+
+ // Light 1 (direction)
+ if( bValue )
+ {
+ rAttrs.Put(aLightItemSet.Get(SDRATTR_3DSCENE_LIGHTDIRECTION_1));
+ }
+ }
+ else
+ rAttrs.InvalidateItem(SDRATTR_3DSCENE_LIGHTON_1);
+
+
+ // Light 2 color
+ if (!m_xLbLight2->IsNoSelection())
+ {
+ aColor = m_xLbLight2->GetSelectEntryColor();
+ rAttrs.Put(makeSvx3DLightcolor2Item(aColor));
+ }
+ else
+ rAttrs.InvalidateItem(SDRATTR_3DSCENE_LIGHTCOLOR_2);
+ // Light 2 (on/off)
+ eState = m_xBtnLight2->get_state();
+ if( eState != TRISTATE_INDET )
+ {
+ bool bValue = m_xBtnLight2->isLightOn();
+ rAttrs.Put(makeSvx3DLightOnOff2Item(bValue));
+
+ // Light 2 (direction)
+ if( bValue )
+ {
+ rAttrs.Put(aLightItemSet.Get(SDRATTR_3DSCENE_LIGHTDIRECTION_2));
+ }
+ }
+ else
+ rAttrs.InvalidateItem(SDRATTR_3DSCENE_LIGHTON_2);
+
+ // Light 3 color
+ if (!m_xLbLight3->IsNoSelection())
+ {
+ aColor = m_xLbLight3->GetSelectEntryColor();
+ rAttrs.Put(makeSvx3DLightcolor3Item(aColor));
+ }
+ else
+ rAttrs.InvalidateItem(SDRATTR_3DSCENE_LIGHTCOLOR_3);
+ // Light 3 (on/off)
+ eState = m_xBtnLight3->get_state();
+ if( eState != TRISTATE_INDET )
+ {
+ bool bValue = m_xBtnLight3->isLightOn();
+ rAttrs.Put(makeSvx3DLightOnOff3Item(bValue));
+
+ // Light 3 (direction)
+ if( bValue )
+ {
+ rAttrs.Put(aLightItemSet.Get(SDRATTR_3DSCENE_LIGHTDIRECTION_3));
+ }
+ }
+ else
+ rAttrs.InvalidateItem(SDRATTR_3DSCENE_LIGHTON_3);
+
+ // Light 4 color
+ if (!m_xLbLight4->IsNoSelection())
+ {
+ aColor = m_xLbLight4->GetSelectEntryColor();
+ rAttrs.Put(makeSvx3DLightcolor4Item(aColor));
+ }
+ else
+ rAttrs.InvalidateItem(SDRATTR_3DSCENE_LIGHTCOLOR_4);
+ // Light 4 (on/off)
+ eState = m_xBtnLight4->get_state();
+ if( eState != TRISTATE_INDET )
+ {
+ bool bValue = m_xBtnLight4->isLightOn();
+ rAttrs.Put(makeSvx3DLightOnOff4Item(bValue));
+
+ // Light 4 (direction)
+ if( bValue )
+ {
+ rAttrs.Put(aLightItemSet.Get(SDRATTR_3DSCENE_LIGHTDIRECTION_4));
+ }
+ }
+ else
+ rAttrs.InvalidateItem(SDRATTR_3DSCENE_LIGHTON_4);
+
+ // Light 5 color
+ if (!m_xLbLight5->IsNoSelection())
+ {
+ aColor = m_xLbLight5->GetSelectEntryColor();
+ rAttrs.Put(makeSvx3DLightcolor5Item(aColor));
+ }
+ else
+ rAttrs.InvalidateItem(SDRATTR_3DSCENE_LIGHTCOLOR_5);
+ // Light 5 (on/off)
+ eState = m_xBtnLight5->get_state();
+ if( eState != TRISTATE_INDET )
+ {
+ bool bValue = m_xBtnLight5->isLightOn();
+ rAttrs.Put(makeSvx3DLightOnOff5Item(bValue));
+
+ // Light 5 (direction)
+ if( bValue )
+ {
+ rAttrs.Put(aLightItemSet.Get(SDRATTR_3DSCENE_LIGHTDIRECTION_5));
+ }
+ }
+ else
+ rAttrs.InvalidateItem(SDRATTR_3DSCENE_LIGHTON_5);
+
+ // Light 6 color
+ if (!m_xLbLight6->IsNoSelection())
+ {
+ aColor = m_xLbLight6->GetSelectEntryColor();
+ rAttrs.Put(makeSvx3DLightcolor6Item(aColor));
+ }
+ else
+ rAttrs.InvalidateItem(SDRATTR_3DSCENE_LIGHTCOLOR_6);
+ // Light 6 (on/off)
+ eState = m_xBtnLight6->get_state();
+ if( eState != TRISTATE_INDET )
+ {
+ bool bValue = m_xBtnLight6->isLightOn();
+ rAttrs.Put(makeSvx3DLightOnOff6Item(bValue));
+
+ // Light 6 (direction)
+ if( bValue )
+ {
+ rAttrs.Put(aLightItemSet.Get(SDRATTR_3DSCENE_LIGHTDIRECTION_6));
+ }
+ }
+ else
+ rAttrs.InvalidateItem(SDRATTR_3DSCENE_LIGHTON_6);
+
+ // Light 7 color
+ if (!m_xLbLight7->IsNoSelection())
+ {
+ aColor = m_xLbLight7->GetSelectEntryColor();
+ rAttrs.Put(makeSvx3DLightcolor7Item(aColor));
+ }
+ else
+ rAttrs.InvalidateItem(SDRATTR_3DSCENE_LIGHTCOLOR_7);
+ // Light 7 (on/off)
+ eState = m_xBtnLight7->get_state();
+ if( eState != TRISTATE_INDET )
+ {
+ bool bValue = m_xBtnLight7->isLightOn();
+ rAttrs.Put(makeSvx3DLightOnOff7Item(bValue));
+
+ // Light 7 (direction)
+ if( bValue )
+ {
+ rAttrs.Put(aLightItemSet.Get(SDRATTR_3DSCENE_LIGHTDIRECTION_7));
+ }
+ }
+ else
+ rAttrs.InvalidateItem(SDRATTR_3DSCENE_LIGHTON_7);
+
+ // Light 8 color
+ if (!m_xLbLight8->IsNoSelection())
+ {
+ aColor = m_xLbLight8->GetSelectEntryColor();
+ rAttrs.Put(makeSvx3DLightcolor8Item(aColor));
+ }
+ else
+ rAttrs.InvalidateItem(SDRATTR_3DSCENE_LIGHTCOLOR_8);
+ // Light 8 (on/off)
+ eState = m_xBtnLight8->get_state();
+ if( eState != TRISTATE_INDET )
+ {
+ bool bValue = m_xBtnLight8->isLightOn();
+ rAttrs.Put(makeSvx3DLightOnOff8Item(bValue));
+
+ // Light 8 (direction)
+ if( bValue )
+ {
+ rAttrs.Put(aLightItemSet.Get(SDRATTR_3DSCENE_LIGHTDIRECTION_8));
+ }
+ }
+ else
+ rAttrs.InvalidateItem(SDRATTR_3DSCENE_LIGHTON_8);
+
+ // Ambient light
+ if (!m_xLbAmbientlight->IsNoSelection())
+ {
+ aColor = m_xLbAmbientlight->GetSelectEntryColor();
+ rAttrs.Put(makeSvx3DAmbientcolorItem(aColor));
+ }
+ else
+ rAttrs.InvalidateItem(SDRATTR_3DSCENE_AMBIENTCOLOR);
+
+// Textures
+ // Art
+ nValue = 99;
+ if( m_xBtnTexLuminance->get_active() )
+ nValue = 1;
+ else if( m_xBtnTexColor->get_active() )
+ nValue = 3;
+
+ if( nValue == 1 || nValue == 3 )
+ rAttrs.Put(Svx3DTextureKindItem(nValue));
+ else
+ rAttrs.InvalidateItem(SDRATTR_3DOBJ_TEXTURE_KIND);
+
+
+ // Mode
+ nValue = 99;
+ if( m_xBtnTexReplace->get_active() )
+ nValue = 1;
+ else if( m_xBtnTexModulate->get_active() )
+ nValue = 2;
+
+ if( nValue == 1 || nValue == 2 )
+ rAttrs.Put(Svx3DTextureModeItem(nValue));
+ else
+ rAttrs.InvalidateItem(SDRATTR_3DOBJ_TEXTURE_MODE);
+
+ // X projection
+ nValue = 99;
+ if( m_xBtnTexObjectX->get_active() )
+ nValue = 0;
+ else if( m_xBtnTexParallelX->get_active() )
+ nValue = 1;
+ else if( m_xBtnTexCircleX->get_active() )
+ nValue = 2;
+
+ if( nValue <= 2 )
+ rAttrs.Put(Svx3DTextureProjectionXItem(nValue));
+ else
+ rAttrs.InvalidateItem(SDRATTR_3DOBJ_TEXTURE_PROJ_X);
+
+ // Y projection
+ nValue = 99;
+ if( m_xBtnTexObjectY->get_active() )
+ nValue = 0;
+ else if( m_xBtnTexParallelY->get_active() )
+ nValue = 1;
+ else if( m_xBtnTexCircleY->get_active() )
+ nValue = 2;
+
+ if( nValue <= 2 )
+ rAttrs.Put(Svx3DTextureProjectionYItem(nValue));
+ else
+ rAttrs.InvalidateItem(SDRATTR_3DOBJ_TEXTURE_PROJ_Y);
+
+
+ // Filter
+ eState = m_xBtnTexFilter->get_state();
+ if( eState != TRISTATE_INDET )
+ {
+ bool bValue = TRISTATE_TRUE == eState;
+ rAttrs.Put(makeSvx3DTextureFilterItem(bValue));
+ }
+ else
+ rAttrs.InvalidateItem(SDRATTR_3DOBJ_TEXTURE_FILTER);
+
+
+// Material
+ // Object color
+ if (!m_xLbMatColor->IsNoSelection())
+ {
+ aColor = m_xLbMatColor->GetSelectEntryColor();
+ rAttrs.Put( XFillColorItem( "", aColor) );
+ }
+ else
+ {
+ rAttrs.InvalidateItem( XATTR_FILLCOLOR );
+ }
+
+ // luminous color
+ if (!m_xLbMatEmission->IsNoSelection())
+ {
+ aColor = m_xLbMatEmission->GetSelectEntryColor();
+ rAttrs.Put(makeSvx3DMaterialEmissionItem(aColor));
+ }
+ else
+ rAttrs.InvalidateItem(SDRATTR_3DOBJ_MAT_EMISSION);
+
+ // Specular
+ if (!m_xLbMatSpecular->IsNoSelection())
+ {
+ aColor = m_xLbMatSpecular->GetSelectEntryColor();
+ rAttrs.Put(makeSvx3DMaterialSpecularItem(aColor));
+ }
+ else
+ rAttrs.InvalidateItem(SDRATTR_3DOBJ_MAT_SPECULAR);
+
+ // Specular intensity
+ if( !m_xMtrMatSpecularIntensity->get_text().isEmpty() )
+ {
+ sal_uInt16 nValue2 = static_cast<sal_uInt16>(m_xMtrMatSpecularIntensity->get_value(FieldUnit::PERCENT));
+ rAttrs.Put(makeSvx3DMaterialSpecularIntensityItem(nValue2));
+ }
+ else
+ rAttrs.InvalidateItem(SDRATTR_3DOBJ_MAT_SPECULAR_INTENSITY);
+}
+
+void Svx3DWin::Resize()
+{
+ Size aWinSize( GetOutputSizePixel() ); // why rSize in Resizing()?
+
+ if( aWinSize.Height() >= GetMinOutputSizePixel().Height() &&
+ aWinSize.Width() >= GetMinOutputSizePixel().Width() )
+ {
+ // Hide
+ m_xBtnUpdate->hide();
+ m_xBtnAssign->hide();
+
+ m_xBtnConvertTo3D->hide();
+ m_xBtnLatheObject->hide();
+ m_xBtnPerspective->hide();
+
+ m_xCtlPreview->Hide();
+ m_xLightPreviewGrid->hide();
+
+ m_xFLGeometrie->hide();
+ m_xFLRepresentation->hide();
+ m_xFLLight->hide();
+ m_xFLTexture->hide();
+ m_xFLMaterial->hide();
+
+ // Show
+ m_xBtnUpdate->show();
+ m_xBtnAssign->show();
+
+ m_xBtnConvertTo3D->show();
+ m_xBtnLatheObject->show();
+ m_xBtnPerspective->show();
+
+ if( m_xBtnGeo->get_active() )
+ ClickViewTypeHdl(*m_xBtnGeo);
+ if( m_xBtnRepresentation->get_active() )
+ ClickViewTypeHdl(*m_xBtnRepresentation);
+ if( m_xBtnLight->get_active() )
+ ClickViewTypeHdl(*m_xBtnLight);
+ if( m_xBtnTexture->get_active() )
+ ClickViewTypeHdl(*m_xBtnTexture);
+ if( m_xBtnMaterial->get_active() )
+ ClickViewTypeHdl(*m_xBtnMaterial);
+ }
+
+ SfxDockingWindow::Resize();
+}
+
+IMPL_LINK_NOARG(Svx3DWin, ClickUpdateHdl, weld::Toggleable&, void)
+{
+ bUpdate = m_xBtnUpdate->get_active();
+
+ if( bUpdate )
+ {
+ SfxDispatcher* pDispatcher = LocalGetDispatcher(pBindings);
+ if (pDispatcher != nullptr)
+ {
+ SfxBoolItem aItem( SID_3D_STATE, true );
+ pDispatcher->ExecuteList(SID_3D_STATE,
+ SfxCallMode::ASYNCHRON | SfxCallMode::RECORD, { &aItem });
+ }
+ }
+ else
+ {
+ // Controls can be disabled during certain circumstances
+ }
+}
+
+IMPL_LINK_NOARG(Svx3DWin, ClickAssignHdl, weld::Button&, void)
+{
+ SfxDispatcher* pDispatcher = LocalGetDispatcher(pBindings);
+ if (pDispatcher != nullptr)
+ {
+ SfxBoolItem aItem( SID_3D_ASSIGN, true );
+ pDispatcher->ExecuteList(SID_3D_ASSIGN,
+ SfxCallMode::ASYNCHRON | SfxCallMode::RECORD, { &aItem });
+ }
+}
+
+IMPL_LINK( Svx3DWin, ClickViewTypeHdl, weld::Button&, rBtn, void )
+{
+ // Since the permanent updating of the preview would be too expensive
+ bool bUpdatePreview = m_xBtnLight->get_active();
+
+ m_xBtnGeo->set_active(m_xBtnGeo.get() == &rBtn);
+ m_xBtnRepresentation->set_active(m_xBtnRepresentation.get() == &rBtn);
+ m_xBtnLight->set_active(m_xBtnLight.get() == &rBtn);
+ m_xBtnTexture->set_active(m_xBtnTexture.get() == &rBtn);
+ m_xBtnMaterial->set_active(m_xBtnMaterial.get() == &rBtn);
+
+ if( m_xBtnGeo->get_active() )
+ eViewType = ViewType3D::Geo;
+ if( m_xBtnRepresentation->get_active() )
+ eViewType = ViewType3D::Representation;
+ if( m_xBtnLight->get_active() )
+ eViewType = ViewType3D::Light;
+ if( m_xBtnTexture->get_active() )
+ eViewType = ViewType3D::Texture;
+ if( m_xBtnMaterial->get_active() )
+ eViewType = ViewType3D::Material;
+
+ // Geometry
+ if( eViewType == ViewType3D::Geo )
+ {
+ m_xFLSegments->show();
+ m_xFLGeometrie->show();
+ m_xFLNormals->show();
+ }
+ else
+ {
+ m_xFLSegments->hide();
+ m_xFLGeometrie->hide();
+ m_xFLNormals->hide();
+ }
+
+ // Representation
+ if( eViewType == ViewType3D::Representation )
+ {
+ m_xFLShadow->show();
+ m_xFLCamera->show();
+ m_xFLRepresentation->show();
+ }
+ else
+ {
+ m_xFLShadow->hide();
+ m_xFLCamera->hide();
+ m_xFLRepresentation->hide();
+ }
+
+ // Lighting
+ if( eViewType == ViewType3D::Light )
+ {
+ m_xFLLight->show();
+
+ ColorListBox* pLb = GetCLbByButton();
+ if( pLb )
+ pLb->show();
+
+ m_xLightPreviewGrid->show();
+ m_xCtlPreview->Hide();
+ }
+ else
+ {
+ m_xFLLight->hide();
+
+ if( !m_xCtlPreview->IsVisible() )
+ {
+ m_xCtlPreview->Show();
+ m_xLightPreviewGrid->hide();
+ }
+ }
+
+ // Textures
+ if (eViewType == ViewType3D::Texture)
+ m_xFLTexture->show();
+ else
+ m_xFLTexture->hide();
+
+ // Material
+ if( eViewType == ViewType3D::Material )
+ {
+ m_xFLMatSpecular->show();
+ m_xFLMaterial->show();
+ }
+ else
+ {
+ m_xFLMatSpecular->hide();
+ m_xFLMaterial->hide();
+ }
+ if( bUpdatePreview && !m_xBtnLight->get_active() )
+ UpdatePreview();
+}
+
+IMPL_LINK( Svx3DWin, ClickHdl, weld::Button&, rBtn, void )
+{
+ bool bUpdatePreview = false;
+ sal_uInt16 nSId = 0;
+
+ if( &rBtn == m_xBtnConvertTo3D.get() )
+ {
+ nSId = SID_CONVERT_TO_3D;
+ }
+ else if( &rBtn == m_xBtnLatheObject.get() )
+ {
+ nSId = SID_CONVERT_TO_3D_LATHE_FAST;
+ }
+ // Geometry
+ else if( &rBtn == m_xBtnNormalsObj.get() ||
+ &rBtn == m_xBtnNormalsFlat.get() ||
+ &rBtn == m_xBtnNormalsSphere.get() )
+ {
+ m_xBtnNormalsObj->set_active( &rBtn == m_xBtnNormalsObj.get() );
+ m_xBtnNormalsFlat->set_active( &rBtn == m_xBtnNormalsFlat.get() );
+ m_xBtnNormalsSphere->set_active( &rBtn == m_xBtnNormalsSphere.get() );
+ bUpdatePreview = true;
+ }
+ else if( &rBtn == m_xBtnLight1->get_widget() ||
+ &rBtn == m_xBtnLight2->get_widget() ||
+ &rBtn == m_xBtnLight3->get_widget() ||
+ &rBtn == m_xBtnLight4->get_widget() ||
+ &rBtn == m_xBtnLight5->get_widget() ||
+ &rBtn == m_xBtnLight6->get_widget() ||
+ &rBtn == m_xBtnLight7->get_widget() ||
+ &rBtn == m_xBtnLight8->get_widget() )
+ {
+ // Lighting
+ LightButton* pToggleBtn = GetLbByButton(&rBtn);
+
+ ColorListBox* pLb = GetCLbByButton(pToggleBtn);
+ pLb->show();
+
+ bool bIsChecked = pToggleBtn->get_prev_active();
+
+ if (pToggleBtn != m_xBtnLight1.get() && m_xBtnLight1->get_active())
+ {
+ m_xBtnLight1->set_active( false );
+ m_xBtnLight1->set_prev_active(false);
+ m_xLbLight1->hide();
+ }
+ if (pToggleBtn != m_xBtnLight2.get() && m_xBtnLight2->get_active())
+ {
+ m_xBtnLight2->set_active( false );
+ m_xBtnLight2->set_prev_active(false);
+ m_xLbLight2->hide();
+ }
+ if( pToggleBtn != m_xBtnLight3.get() && m_xBtnLight3->get_active() )
+ {
+ m_xBtnLight3->set_active( false );
+ m_xBtnLight3->set_prev_active(false);
+ m_xLbLight3->hide();
+ }
+ if( pToggleBtn != m_xBtnLight4.get() && m_xBtnLight4->get_active() )
+ {
+ m_xBtnLight4->set_active( false );
+ m_xBtnLight4->set_prev_active(false);
+ m_xLbLight4->hide();
+ }
+ if( pToggleBtn != m_xBtnLight5.get() && m_xBtnLight5->get_active() )
+ {
+ m_xBtnLight5->set_active( false );
+ m_xBtnLight5->set_prev_active(false);
+ m_xLbLight5->hide();
+ }
+ if( pToggleBtn != m_xBtnLight6.get() && m_xBtnLight6->get_active() )
+ {
+ m_xBtnLight6->set_active( false );
+ m_xBtnLight6->set_prev_active(false);
+ m_xLbLight6->hide();
+ }
+ if( pToggleBtn != m_xBtnLight7.get() && m_xBtnLight7->get_active() )
+ {
+ m_xBtnLight7->set_active( false );
+ m_xBtnLight7->set_prev_active(false);
+ m_xLbLight7->hide();
+ }
+ if( pToggleBtn != m_xBtnLight8.get() && m_xBtnLight8->get_active() )
+ {
+ m_xBtnLight8->set_active( false );
+ m_xBtnLight8->set_prev_active(false);
+ m_xLbLight8->hide();
+ }
+
+ //update light button
+ pToggleBtn->set_active(true);
+ pToggleBtn->set_prev_active(true);
+ if (bIsChecked)
+ pToggleBtn->switchLightOn(!pToggleBtn->isLightOn());
+
+ bool bEnable = pToggleBtn->isLightOn();
+ m_xBtnLightColor->set_sensitive( bEnable );
+ pLb->set_sensitive( bEnable );
+
+ ClickLight(*pToggleBtn);
+ bUpdatePreview = true;
+ }
+ // Textures
+ else if( &rBtn == m_xBtnTexLuminance.get() ||
+ &rBtn == m_xBtnTexColor.get() )
+ {
+ m_xBtnTexLuminance->set_active( &rBtn == m_xBtnTexLuminance.get() );
+ m_xBtnTexColor->set_active( &rBtn == m_xBtnTexColor.get() );
+ bUpdatePreview = true;
+ }
+ else if( &rBtn == m_xBtnTexReplace.get() ||
+ &rBtn == m_xBtnTexModulate.get() )
+ {
+ m_xBtnTexReplace->set_active( &rBtn == m_xBtnTexReplace.get() );
+ m_xBtnTexModulate->set_active( &rBtn == m_xBtnTexModulate.get() );
+ bUpdatePreview = true;
+ }
+ else if( &rBtn == m_xBtnTexParallelX.get() ||
+ &rBtn == m_xBtnTexCircleX.get() ||
+ &rBtn == m_xBtnTexObjectX.get() )
+ {
+ m_xBtnTexParallelX->set_active( &rBtn == m_xBtnTexParallelX.get() );
+ m_xBtnTexCircleX->set_active( &rBtn == m_xBtnTexCircleX.get() );
+ m_xBtnTexObjectX->set_active( &rBtn == m_xBtnTexObjectX.get() );
+ bUpdatePreview = true;
+ }
+ else if( &rBtn == m_xBtnTexParallelY.get() ||
+ &rBtn == m_xBtnTexCircleY.get() ||
+ &rBtn == m_xBtnTexObjectY.get() )
+ {
+ m_xBtnTexParallelY->set_active( &rBtn == m_xBtnTexParallelY.get() );
+ m_xBtnTexCircleY->set_active( &rBtn == m_xBtnTexCircleY.get() );
+ m_xBtnTexObjectY->set_active( &rBtn == m_xBtnTexObjectY.get() );
+ bUpdatePreview = true;
+ }
+ else if (&rBtn == m_xBtnShadow3d.get())
+ {
+ m_xFtSlant->set_sensitive( m_xBtnShadow3d->get_active() );
+ m_xMtrSlant->set_sensitive( m_xBtnShadow3d->get_active() );
+ bUpdatePreview = true;
+ }
+ // Other (no groups)
+ else
+ {
+ bUpdatePreview = true;
+ }
+
+ if( nSId > 0 )
+ {
+ SfxDispatcher* pDispatcher = LocalGetDispatcher(pBindings);
+ if (pDispatcher != nullptr)
+ {
+ SfxBoolItem aItem( nSId, true );
+ pDispatcher->ExecuteList(nSId,
+ SfxCallMode::ASYNCHRON | SfxCallMode::RECORD, { &aItem });
+ }
+ }
+ else if( bUpdatePreview )
+ UpdatePreview();
+}
+
+IMPL_LINK( Svx3DWin, ClickColorHdl, weld::Button&, rBtn, void)
+{
+ SvColorDialog aColorDlg;
+ ColorListBox* pLb;
+
+ if( &rBtn == m_xBtnLightColor.get() )
+ pLb = GetCLbByButton();
+ else if( &rBtn == m_xBtnAmbientColor.get() )
+ pLb = m_xLbAmbientlight.get();
+ else if( &rBtn == m_xBtnMatColor.get() )
+ pLb = m_xLbMatColor.get();
+ else if( &rBtn == m_xBtnEmissionColor.get() )
+ pLb = m_xLbMatEmission.get();
+ else // if( &rBtn == m_xBtnSpecularColor.get() )
+ pLb = m_xLbMatSpecular.get();
+
+ Color aColor = pLb->GetSelectEntryColor();
+
+ aColorDlg.SetColor( aColor );
+ if( aColorDlg.Execute(GetFrameWeld()) == RET_OK )
+ {
+ aColor = aColorDlg.GetColor();
+ LBSelectColor(pLb, aColor);
+ SelectColorHdl(*pLb);
+ }
+}
+
+IMPL_LINK( Svx3DWin, SelectHdl, weld::ComboBox&, rListBox, void )
+{
+ bool bUpdatePreview = false;
+
+ // Material
+ if (&rListBox == m_xLbMatFavorites.get())
+ {
+ Color aColObj( COL_WHITE );
+ Color aColEmis( COL_BLACK );
+ Color aColSpec( COL_WHITE );
+ sal_uInt16 nSpecIntens = 20;
+
+ switch( m_xLbMatFavorites->get_active() )
+ {
+ case 1: // Metall
+ {
+ aColObj = Color(230,230,255);
+ aColEmis = Color(10,10,30);
+ aColSpec = Color(200,200,200);
+ nSpecIntens = 20;
+ }
+ break;
+
+ case 2: // Gold
+ {
+ aColObj = Color(230,255,0);
+ aColEmis = Color(51,0,0);
+ aColSpec = Color(255,255,240);
+ nSpecIntens = 20;
+ }
+ break;
+
+ case 3: // Chrome
+ {
+ aColObj = Color(36,117,153);
+ aColEmis = Color(18,30,51);
+ aColSpec = Color(230,230,255);
+ nSpecIntens = 2;
+ }
+ break;
+
+ case 4: // Plastic
+ {
+ aColObj = Color(255,48,57);
+ aColEmis = Color(35,0,0);
+ aColSpec = Color(179,202,204);
+ nSpecIntens = 60;
+ }
+ break;
+
+ case 5: // Wood
+ {
+ aColObj = Color(153,71,1);
+ aColEmis = Color(21,22,0);
+ aColSpec = Color(255,255,153);
+ nSpecIntens = 75;
+ }
+ break;
+ }
+ LBSelectColor( m_xLbMatColor.get(), aColObj );
+ LBSelectColor( m_xLbMatEmission.get(), aColEmis );
+ LBSelectColor( m_xLbMatSpecular.get(), aColSpec );
+ m_xMtrMatSpecularIntensity->set_value(nSpecIntens, FieldUnit::PERCENT);
+
+ bUpdatePreview = true;
+ }
+ else if (&rListBox == m_xLbShademode.get())
+ bUpdatePreview = true;
+
+ if( bUpdatePreview )
+ UpdatePreview();
+}
+
+IMPL_LINK( Svx3DWin, SelectColorHdl, ColorListBox&, rListBox, void )
+{
+ bool bUpdatePreview = false;
+
+ if( &rListBox == m_xLbMatColor.get() ||
+ &rListBox == m_xLbMatEmission.get() ||
+ &rListBox == m_xLbMatSpecular.get() )
+ {
+ m_xLbMatFavorites->set_active( 0 );
+ bUpdatePreview = true;
+ }
+ // Lighting
+ else if( &rListBox == m_xLbAmbientlight.get() )
+ {
+ bUpdatePreview = true;
+ }
+ else if( &rListBox == m_xLbLight1.get() ||
+ &rListBox == m_xLbLight2.get() ||
+ &rListBox == m_xLbLight3.get() ||
+ &rListBox == m_xLbLight4.get() ||
+ &rListBox == m_xLbLight5.get() ||
+ &rListBox == m_xLbLight6.get() ||
+ &rListBox == m_xLbLight7.get() ||
+ &rListBox == m_xLbLight8.get() )
+ {
+ bUpdatePreview = true;
+ }
+
+ if( bUpdatePreview )
+ UpdatePreview();
+}
+
+IMPL_LINK_NOARG( Svx3DWin, ModifyMetricHdl, weld::MetricSpinButton&, void )
+{
+ UpdatePreview();
+}
+
+IMPL_LINK_NOARG( Svx3DWin, ModifySpinHdl, weld::SpinButton&, void )
+{
+ UpdatePreview();
+}
+
+void Svx3DWin::ClickLight(const LightButton& rBtn)
+{
+ sal_uInt16 nLightSource = GetLightSource( &rBtn );
+ ColorListBox* pLb = GetCLbByButton( &rBtn );
+ Color aColor( pLb->GetSelectEntryColor() );
+ SfxItemSet aLightItemSet(m_xCtlLightPreview->GetSvx3DLightControl().Get3DAttributes());
+ const bool bOnOff(rBtn.isLightOn());
+
+ switch(nLightSource)
+ {
+ case 0: aLightItemSet.Put(makeSvx3DLightcolor1Item(aColor)); aLightItemSet.Put(makeSvx3DLightOnOff1Item(bOnOff)); break;
+ case 1: aLightItemSet.Put(makeSvx3DLightcolor2Item(aColor)); aLightItemSet.Put(makeSvx3DLightOnOff2Item(bOnOff)); break;
+ case 2: aLightItemSet.Put(makeSvx3DLightcolor3Item(aColor)); aLightItemSet.Put(makeSvx3DLightOnOff3Item(bOnOff)); break;
+ case 3: aLightItemSet.Put(makeSvx3DLightcolor4Item(aColor)); aLightItemSet.Put(makeSvx3DLightOnOff4Item(bOnOff)); break;
+ case 4: aLightItemSet.Put(makeSvx3DLightcolor5Item(aColor)); aLightItemSet.Put(makeSvx3DLightOnOff5Item(bOnOff)); break;
+ case 5: aLightItemSet.Put(makeSvx3DLightcolor6Item(aColor)); aLightItemSet.Put(makeSvx3DLightOnOff6Item(bOnOff)); break;
+ case 6: aLightItemSet.Put(makeSvx3DLightcolor7Item(aColor)); aLightItemSet.Put(makeSvx3DLightOnOff7Item(bOnOff)); break;
+ default:
+ case 7: aLightItemSet.Put(makeSvx3DLightcolor8Item(aColor)); aLightItemSet.Put(makeSvx3DLightOnOff8Item(bOnOff)); break;
+ }
+
+ m_xCtlLightPreview->GetSvx3DLightControl().Set3DAttributes(aLightItemSet);
+ m_xCtlLightPreview->GetSvx3DLightControl().SelectLight(nLightSource);
+ m_xCtlLightPreview->CheckSelection();
+}
+
+IMPL_LINK_NOARG(Svx3DWin, ChangeSelectionCallbackHdl, SvxLightCtl3D*, void)
+{
+ const sal_uInt32 nLight(m_xCtlLightPreview->GetSvx3DLightControl().GetSelectedLight());
+ weld::Button* pBtn = nullptr;
+
+ switch( nLight )
+ {
+ case 0: pBtn = m_xBtnLight1->get_widget(); break;
+ case 1: pBtn = m_xBtnLight2->get_widget(); break;
+ case 2: pBtn = m_xBtnLight3->get_widget(); break;
+ case 3: pBtn = m_xBtnLight4->get_widget(); break;
+ case 4: pBtn = m_xBtnLight5->get_widget(); break;
+ case 5: pBtn = m_xBtnLight6->get_widget(); break;
+ case 6: pBtn = m_xBtnLight7->get_widget(); break;
+ case 7: pBtn = m_xBtnLight8->get_widget(); break;
+ default: break;
+ }
+
+ if (pBtn)
+ ClickHdl(*pBtn);
+ else
+ {
+ // Status: No lamp selected
+ if( m_xBtnLight1->get_active() )
+ {
+ m_xBtnLight1->set_active( false );
+ m_xLbLight1->set_sensitive( false );
+ }
+ else if( m_xBtnLight2->get_active() )
+ {
+ m_xBtnLight2->set_active( false );
+ m_xLbLight2->set_sensitive( false );
+ }
+ else if( m_xBtnLight3->get_active() )
+ {
+ m_xBtnLight3->set_active( false );
+ m_xLbLight3->set_sensitive( false );
+ }
+ else if( m_xBtnLight4->get_active() )
+ {
+ m_xBtnLight4->set_active( false );
+ m_xLbLight4->set_sensitive( false );
+ }
+ else if( m_xBtnLight5->get_active() )
+ {
+ m_xBtnLight5->set_active( false );
+ m_xLbLight5->set_sensitive( false );
+ }
+ else if( m_xBtnLight6->get_active() )
+ {
+ m_xBtnLight6->set_active( false );
+ m_xLbLight6->set_sensitive( false );
+ }
+ else if( m_xBtnLight7->get_active() )
+ {
+ m_xBtnLight7->set_active( false );
+ m_xLbLight7->set_sensitive( false );
+ }
+ else if( m_xBtnLight8->get_active() )
+ {
+ m_xBtnLight8->set_active( false );
+ m_xLbLight8->set_sensitive( false );
+ }
+ m_xBtnLightColor->set_sensitive( false );
+ }
+}
+
+namespace
+{
+ OUString lcl_makeColorName(const Color& rColor)
+ {
+ OUString aStr = SvxResId(RID_SVXFLOAT3D_FIX_R) +
+ OUString::number(rColor.GetRed()) +
+ " " +
+ SvxResId(RID_SVXFLOAT3D_FIX_G) +
+ OUString::number(rColor.GetGreen()) +
+ " " +
+ SvxResId(RID_SVXFLOAT3D_FIX_B) +
+ OUString::number(rColor.GetBlue());
+ return aStr;
+ }
+}
+
+// Method to ensure that the LB is also associated with a color
+void Svx3DWin::LBSelectColor( ColorListBox* pLb, const Color& rColor )
+{
+ pLb->SetNoSelection();
+ pLb->SelectEntry(std::make_pair(rColor, lcl_makeColorName(rColor)));
+}
+
+void Svx3DWin::UpdatePreview()
+{
+ if(!pModel)
+ {
+ pModel.reset(new FmFormModel());
+ }
+
+ // Get Itemset
+ SfxItemSetFixed<SDRATTR_START, SDRATTR_END> aSet( pModel->GetItemPool() );
+
+ // Get Attributes and set the preview
+ GetAttr( aSet );
+ m_xCtlPreview->Set3DAttributes( aSet );
+ m_xCtlLightPreview->GetSvx3DLightControl().Set3DAttributes( aSet );
+}
+
+
+// document is to be reloaded, destroy remembered ItemSet
+void Svx3DWin::DocumentReload()
+{
+ mpRemember2DAttributes.reset();
+}
+
+void Svx3DWin::InitColorLB()
+{
+ // First...
+ Color aColWhite( COL_WHITE );
+ Color aColBlack( COL_BLACK );
+ m_xLbLight1->SelectEntry( aColWhite );
+ m_xLbLight2->SelectEntry( aColWhite );
+ m_xLbLight3->SelectEntry( aColWhite );
+ m_xLbLight4->SelectEntry( aColWhite );
+ m_xLbLight5->SelectEntry( aColWhite );
+ m_xLbLight6->SelectEntry( aColWhite );
+ m_xLbLight7->SelectEntry( aColWhite );
+ m_xLbLight8->SelectEntry( aColWhite );
+ m_xLbAmbientlight->SelectEntry( aColBlack );
+ m_xLbMatColor->SelectEntry( aColWhite );
+ m_xLbMatEmission->SelectEntry( aColBlack );
+ m_xLbMatSpecular->SelectEntry( aColWhite );
+}
+
+sal_uInt16 Svx3DWin::GetLightSource( const LightButton* pBtn ) const
+{
+ sal_uInt16 nLight = 8;
+
+ if (pBtn == m_xBtnLight1.get())
+ nLight = 0;
+ else if (pBtn == m_xBtnLight2.get())
+ nLight = 1;
+ else if( pBtn == m_xBtnLight3.get() )
+ nLight = 2;
+ else if( pBtn == m_xBtnLight4.get() )
+ nLight = 3;
+ else if( pBtn == m_xBtnLight5.get() )
+ nLight = 4;
+ else if( pBtn == m_xBtnLight6.get() )
+ nLight = 5;
+ else if( pBtn == m_xBtnLight7.get() )
+ nLight = 6;
+ else if( pBtn == m_xBtnLight8.get() )
+ nLight = 7;
+
+ return nLight;
+};
+
+ColorListBox* Svx3DWin::GetCLbByButton( const LightButton* pBtn )
+{
+ ColorListBox* pLb = nullptr;
+
+ if( pBtn == nullptr )
+ {
+ if( m_xBtnLight1->get_active() )
+ pLb = m_xLbLight1.get();
+ else if( m_xBtnLight2->get_active() )
+ pLb = m_xLbLight2.get();
+ else if( m_xBtnLight3->get_active() )
+ pLb = m_xLbLight3.get();
+ else if( m_xBtnLight4->get_active() )
+ pLb = m_xLbLight4.get();
+ else if( m_xBtnLight5->get_active() )
+ pLb = m_xLbLight5.get();
+ else if( m_xBtnLight6->get_active() )
+ pLb = m_xLbLight6.get();
+ else if( m_xBtnLight7->get_active() )
+ pLb = m_xLbLight7.get();
+ else if( m_xBtnLight8->get_active() )
+ pLb = m_xLbLight8.get();
+ }
+ else
+ {
+ if( pBtn == m_xBtnLight1.get() )
+ pLb = m_xLbLight1.get();
+ else if (pBtn == m_xBtnLight2.get())
+ pLb = m_xLbLight2.get();
+ else if( pBtn == m_xBtnLight3.get() )
+ pLb = m_xLbLight3.get();
+ else if( pBtn == m_xBtnLight4.get() )
+ pLb = m_xLbLight4.get();
+ else if( pBtn == m_xBtnLight5.get() )
+ pLb = m_xLbLight5.get();
+ else if( pBtn == m_xBtnLight6.get() )
+ pLb = m_xLbLight6.get();
+ else if( pBtn == m_xBtnLight7.get() )
+ pLb = m_xLbLight7.get();
+ else if( pBtn == m_xBtnLight8.get() )
+ pLb = m_xLbLight8.get();
+ }
+ return pLb;
+};
+
+LightButton* Svx3DWin::GetLbByButton( const weld::Button* pBtn )
+{
+ LightButton* pLb = nullptr;
+
+ if( pBtn == m_xBtnLight1->get_widget() )
+ pLb = m_xBtnLight1.get();
+ else if (pBtn == m_xBtnLight2->get_widget() )
+ pLb = m_xBtnLight2.get();
+ else if( pBtn == m_xBtnLight3->get_widget() )
+ pLb = m_xBtnLight3.get();
+ else if( pBtn == m_xBtnLight4->get_widget() )
+ pLb = m_xBtnLight4.get();
+ else if( pBtn == m_xBtnLight5->get_widget() )
+ pLb = m_xBtnLight5.get();
+ else if( pBtn == m_xBtnLight6->get_widget() )
+ pLb = m_xBtnLight6.get();
+ else if( pBtn == m_xBtnLight7->get_widget() )
+ pLb = m_xBtnLight7.get();
+ else if( pBtn == m_xBtnLight8->get_widget() )
+ pLb = m_xBtnLight8.get();
+
+ return pLb;
+};
+
+// Derivation from SfxChildWindow as "containers" for effects
+Svx3DChildWindow::Svx3DChildWindow( vcl::Window* _pParent,
+ sal_uInt16 nId,
+ SfxBindings* pBindings,
+ SfxChildWinInfo* pInfo ) :
+ SfxChildWindow( _pParent, nId )
+{
+ VclPtr<Svx3DWin> pWin = VclPtr<Svx3DWin>::Create( pBindings, this, _pParent );
+ SetWindow(pWin);
+
+ pWin->Initialize( pInfo );
+}
+
+Svx3DCtrlItem::Svx3DCtrlItem( sal_uInt16 _nId,
+ SfxBindings* _pBindings) :
+ SfxControllerItem( _nId, *_pBindings )
+{
+}
+
+
+void Svx3DCtrlItem::StateChangedAtToolBoxControl( sal_uInt16 /*nSId*/,
+ SfxItemState /*eState*/, const SfxPoolItem* /*pItem*/ )
+{
+}
+
+// ControllerItem for Status Slot SID_CONVERT_TO_3D
+
+SvxConvertTo3DItem::SvxConvertTo3DItem(sal_uInt16 _nId, SfxBindings* _pBindings)
+: SfxControllerItem(_nId, *_pBindings),
+ bState(false)
+{
+}
+
+void SvxConvertTo3DItem::StateChangedAtToolBoxControl(sal_uInt16 /*_nId*/, SfxItemState eState, const SfxPoolItem* /*pState*/)
+{
+ bool bNewState = (eState != SfxItemState::DISABLED);
+ if(bNewState != bState)
+ {
+ bState = bNewState;
+ SfxDispatcher* pDispatcher = LocalGetDispatcher(&GetBindings());
+ if (pDispatcher != nullptr)
+ {
+ SfxBoolItem aItem( SID_3D_STATE, true );
+ pDispatcher->ExecuteList(SID_3D_STATE,
+ SfxCallMode::ASYNCHRON|SfxCallMode::RECORD, { &aItem });
+ }
+ }
+}
+
+LightButton::LightButton(std::unique_ptr<weld::ToggleButton> xButton)
+ : m_xButton(std::move(xButton))
+ , m_bLightOn(false)
+ , m_bButtonPrevActive(false)
+{
+ m_xButton->set_from_icon_name(RID_SVXBMP_LAMP_OFF);
+}
+
+void LightButton::switchLightOn(bool bOn)
+{
+ if (m_bLightOn == bOn)
+ return;
+ m_bLightOn = bOn;
+ if (m_bLightOn)
+ m_xButton->set_from_icon_name(RID_SVXBMP_LAMP_ON);
+ else
+ m_xButton->set_from_icon_name(RID_SVXBMP_LAMP_OFF);
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/engine3d/helperhittest3d.cxx b/svx/source/engine3d/helperhittest3d.cxx
new file mode 100644
index 000000000..65ed16af7
--- /dev/null
+++ b/svx/source/engine3d/helperhittest3d.cxx
@@ -0,0 +1,277 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+
+#include <svx/helperhittest3d.hxx>
+#include <basegfx/point/b2dpoint.hxx>
+#include <svx/svdpage.hxx>
+#include <svx/scene3d.hxx>
+#include <svx/svditer.hxx>
+#include <drawinglayer/processor3d/cutfindprocessor3d.hxx>
+#include <sdr/contact/viewcontactofe3d.hxx>
+#include <svx/sdr/contact/viewcontactofe3dscene.hxx>
+#include <com/sun/star/uno/Sequence.h>
+
+
+using namespace com::sun::star;
+
+namespace {
+
+class ImplPairDephAndObject
+{
+private:
+ const E3dCompoundObject* mpObject;
+ double mfDepth;
+
+public:
+ ImplPairDephAndObject(const E3dCompoundObject* pObject, double fDepth)
+ : mpObject(pObject),
+ mfDepth(fDepth)
+ {}
+
+ // for ::std::sort
+ bool operator<(const ImplPairDephAndObject& rComp) const
+ {
+ return (mfDepth < rComp.mfDepth);
+ }
+
+ // data read access
+ const E3dCompoundObject* getObject() const { return mpObject; }
+};
+
+}
+
+static void getAllHit3DObjectWithRelativePoint(
+ const basegfx::B3DPoint& rFront,
+ const basegfx::B3DPoint& rBack,
+ const E3dCompoundObject& rObject,
+ const drawinglayer::geometry::ViewInformation3D& rObjectViewInformation3D,
+ ::std::vector< basegfx::B3DPoint >& o_rResult,
+ bool bAnyHit)
+{
+ o_rResult.clear();
+
+ if(rFront.equal(rBack))
+ return;
+
+ // rObject is an E3dCompoundObject, so it cannot be a scene (which is an E3dObject)
+ const sdr::contact::ViewContactOfE3d& rVCObject = static_cast< sdr::contact::ViewContactOfE3d& >(rObject.GetViewContact());
+ const drawinglayer::primitive3d::Primitive3DContainer aPrimitives(rVCObject.getViewIndependentPrimitive3DContainer());
+
+ if(aPrimitives.empty())
+ return;
+
+ // make BoundVolume empty and overlapping test for speedup
+ const basegfx::B3DRange aObjectRange(aPrimitives.getB3DRange(rObjectViewInformation3D));
+
+ if(!aObjectRange.isEmpty())
+ {
+ const basegfx::B3DRange aFrontBackRange(rFront, rBack);
+
+ if(aObjectRange.overlaps(aFrontBackRange))
+ {
+ // bound volumes hit, geometric cut tests needed
+ drawinglayer::processor3d::CutFindProcessor aCutFindProcessor(rObjectViewInformation3D, rFront, rBack, bAnyHit);
+ aCutFindProcessor.process(aPrimitives);
+ o_rResult = aCutFindProcessor.getCutPoints();
+ }
+ }
+}
+
+
+E3dScene* fillViewInformation3DForCompoundObject(drawinglayer::geometry::ViewInformation3D& o_rViewInformation3D, const E3dCompoundObject& rCandidate)
+{
+ // Search for root scene (outmost scene) of the 3d object since e.g. in chart, multiple scenes may
+ // be placed between object and outmost scene. On that search, remember the in-between scene's
+ // transformation for the correct complete ObjectTransformation. For historical reasons, the
+ // root scene's own object transformation is part of the scene's ViewTransformation, o do not
+ // add it. For more details, see ViewContactOfE3dScene::createViewInformation3D.
+ E3dScene* pParentScene(rCandidate.getParentE3dSceneFromE3dObject());
+ E3dScene* pRootScene(nullptr);
+ basegfx::B3DHomMatrix aInBetweenSceneMatrix;
+
+ while(pParentScene)
+ {
+ E3dScene* pParentParentScene(pParentScene->getParentE3dSceneFromE3dObject());
+
+ if(pParentParentScene)
+ {
+ // pParentScene is an in-between scene
+ aInBetweenSceneMatrix = pParentScene->GetTransform() * aInBetweenSceneMatrix;
+ }
+ else
+ {
+ // pParentScene is the root scene
+ pRootScene = pParentScene;
+ }
+
+ pParentScene = pParentParentScene;
+ }
+
+ if(pRootScene)
+ {
+ const sdr::contact::ViewContactOfE3dScene& rVCScene = static_cast< sdr::contact::ViewContactOfE3dScene& >(pRootScene->GetViewContact());
+
+ if(aInBetweenSceneMatrix.isIdentity())
+ {
+ o_rViewInformation3D = rVCScene.getViewInformation3D();
+ }
+ else
+ {
+ // build new ViewInformation containing all transforms for the candidate
+ const drawinglayer::geometry::ViewInformation3D& aViewInfo3D(rVCScene.getViewInformation3D());
+
+ o_rViewInformation3D = drawinglayer::geometry::ViewInformation3D(
+ aViewInfo3D.getObjectTransformation() * aInBetweenSceneMatrix,
+ aViewInfo3D.getOrientation(),
+ aViewInfo3D.getProjection(),
+ aViewInfo3D.getDeviceToView(),
+ aViewInfo3D.getViewTime(),
+ aViewInfo3D.getExtendedInformationSequence());
+ }
+ }
+ else
+ {
+ const uno::Sequence< beans::PropertyValue > aEmptyParameters;
+ o_rViewInformation3D = drawinglayer::geometry::ViewInformation3D(aEmptyParameters);
+ }
+
+ return pRootScene;
+}
+
+
+void getAllHit3DObjectsSortedFrontToBack(
+ const basegfx::B2DPoint& rPoint,
+ const E3dScene& rScene,
+ ::std::vector< const E3dCompoundObject* >& o_rResult)
+{
+ o_rResult.clear();
+ SdrObjList* pList = rScene.GetSubList();
+
+ if(nullptr == pList || 0 == pList->GetObjCount())
+ return;
+
+ // prepare relative HitPoint. To do so, get the VC of the 3DScene and from there
+ // the Scene's 2D transformation. Multiplying with the inverse transformation
+ // will create a point relative to the 3D scene as unit-2d-object
+ const sdr::contact::ViewContactOfE3dScene& rVCScene = static_cast< sdr::contact::ViewContactOfE3dScene& >(rScene.GetViewContact());
+ basegfx::B2DHomMatrix aInverseSceneTransform(rVCScene.getObjectTransformation());
+ aInverseSceneTransform.invert();
+ const basegfx::B2DPoint aRelativePoint(aInverseSceneTransform * rPoint);
+
+ // check if test point is inside scene's area at all
+ if(!(aRelativePoint.getX() >= 0.0 && aRelativePoint.getX() <= 1.0 && aRelativePoint.getY() >= 0.0 && aRelativePoint.getY() <= 1.0))
+ return;
+
+ SdrObjListIter aIterator(pList, SdrIterMode::DeepNoGroups);
+ ::std::vector< ImplPairDephAndObject > aDepthAndObjectResults;
+ const uno::Sequence< beans::PropertyValue > aEmptyParameters;
+ drawinglayer::geometry::ViewInformation3D aViewInfo3D(aEmptyParameters);
+
+ while(aIterator.IsMore())
+ {
+ const E3dCompoundObject* pCandidate = dynamic_cast< const E3dCompoundObject* >(aIterator.Next());
+
+ if(pCandidate)
+ {
+ fillViewInformation3DForCompoundObject(aViewInfo3D, *pCandidate);
+
+ // create HitPoint Front and Back, transform to object coordinates
+ basegfx::B3DHomMatrix aViewToObject(aViewInfo3D.getObjectToView());
+ aViewToObject.invert();
+ const basegfx::B3DPoint aFront(aViewToObject * basegfx::B3DPoint(aRelativePoint.getX(), aRelativePoint.getY(), 0.0));
+ const basegfx::B3DPoint aBack(aViewToObject * basegfx::B3DPoint(aRelativePoint.getX(), aRelativePoint.getY(), 1.0));
+
+ if(!aFront.equal(aBack))
+ {
+ // get all hit points with object
+ ::std::vector< basegfx::B3DPoint > aHitsWithObject;
+ getAllHit3DObjectWithRelativePoint(aFront, aBack, *pCandidate, aViewInfo3D, aHitsWithObject, false);
+
+ for(const basegfx::B3DPoint & a : aHitsWithObject)
+ {
+ const basegfx::B3DPoint aPointInViewCoordinates(aViewInfo3D.getObjectToView() * a);
+ aDepthAndObjectResults.emplace_back(pCandidate, aPointInViewCoordinates.getZ());
+ }
+ }
+ }
+ }
+
+ // fill nRetval
+ const sal_uInt32 nCount(aDepthAndObjectResults.size());
+
+ if(nCount)
+ {
+ // sort aDepthAndObjectResults by depth
+ ::std::sort(aDepthAndObjectResults.begin(), aDepthAndObjectResults.end());
+
+ // copy SdrObject pointers to return result set
+ for(const auto& rResult : aDepthAndObjectResults)
+ {
+ o_rResult.push_back(rResult.getObject());
+ }
+ }
+}
+
+
+bool checkHitSingle3DObject(
+ const basegfx::B2DPoint& rPoint,
+ const E3dCompoundObject& rCandidate)
+{
+ const uno::Sequence< beans::PropertyValue > aEmptyParameters;
+ drawinglayer::geometry::ViewInformation3D aViewInfo3D(aEmptyParameters);
+ E3dScene* pRootScene = fillViewInformation3DForCompoundObject(aViewInfo3D, rCandidate);
+
+ if(pRootScene)
+ {
+ // prepare relative HitPoint. To do so, get the VC of the 3DScene and from there
+ // the Scene's 2D transformation. Multiplying with the inverse transformation
+ // will create a point relative to the 3D scene as unit-2d-object
+ const sdr::contact::ViewContactOfE3dScene& rVCScene = static_cast< sdr::contact::ViewContactOfE3dScene& >(pRootScene->GetViewContact());
+ basegfx::B2DHomMatrix aInverseSceneTransform(rVCScene.getObjectTransformation());
+ aInverseSceneTransform.invert();
+ const basegfx::B2DPoint aRelativePoint(aInverseSceneTransform * rPoint);
+
+ // check if test point is inside scene's area at all
+ if(aRelativePoint.getX() >= 0.0 && aRelativePoint.getX() <= 1.0 && aRelativePoint.getY() >= 0.0 && aRelativePoint.getY() <= 1.0)
+ {
+ // create HitPoint Front and Back, transform to object coordinates
+ basegfx::B3DHomMatrix aViewToObject(aViewInfo3D.getObjectToView());
+ aViewToObject.invert();
+ const basegfx::B3DPoint aFront(aViewToObject * basegfx::B3DPoint(aRelativePoint.getX(), aRelativePoint.getY(), 0.0));
+ const basegfx::B3DPoint aBack(aViewToObject * basegfx::B3DPoint(aRelativePoint.getX(), aRelativePoint.getY(), 1.0));
+
+ if(!aFront.equal(aBack))
+ {
+ // get all hit points with object
+ ::std::vector< basegfx::B3DPoint > aHitsWithObject;
+ getAllHit3DObjectWithRelativePoint(aFront, aBack, rCandidate, aViewInfo3D, aHitsWithObject, true);
+
+ if(!aHitsWithObject.empty())
+ {
+ return true;
+ }
+ }
+ }
+ }
+
+ return false;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/engine3d/helperminimaldepth3d.cxx b/svx/source/engine3d/helperminimaldepth3d.cxx
new file mode 100644
index 000000000..cfcc2c7d1
--- /dev/null
+++ b/svx/source/engine3d/helperminimaldepth3d.cxx
@@ -0,0 +1,201 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+
+#include "helperminimaldepth3d.hxx"
+#include <drawinglayer/processor3d/baseprocessor3d.hxx>
+#include <drawinglayer/primitive3d/drawinglayer_primitivetypes3d.hxx>
+#include <drawinglayer/primitive3d/transformprimitive3d.hxx>
+#include <drawinglayer/primitive3d/polygonprimitive3d.hxx>
+#include <drawinglayer/primitive3d/polypolygonprimitive3d.hxx>
+#include <sdr/contact/viewcontactofe3d.hxx>
+#include <svx/sdr/contact/viewcontactofe3dscene.hxx>
+#include <svx/obj3d.hxx>
+#include <svx/scene3d.hxx>
+
+
+namespace drawinglayer::processor3d
+{
+ namespace {
+
+ class MinimalDephInViewExtractor : public BaseProcessor3D
+ {
+ private:
+ // the value which will be fetched as result
+ double mfMinimalDepth;
+
+ // as tooling, the process() implementation takes over API handling and calls this
+ // virtual render method when the primitive implementation is BasePrimitive3D-based.
+ virtual void processBasePrimitive3D(const primitive3d::BasePrimitive3D& rCandidate) override;
+
+ public:
+ explicit MinimalDephInViewExtractor(const geometry::ViewInformation3D& rViewInformation)
+ : BaseProcessor3D(rViewInformation),
+ mfMinimalDepth(DBL_MAX)
+ {}
+
+ // data access
+ double getMinimalDepth() const { return mfMinimalDepth; }
+ };
+
+ }
+
+ void MinimalDephInViewExtractor::processBasePrimitive3D(const primitive3d::BasePrimitive3D& rCandidate)
+ {
+ // it is a BasePrimitive3D implementation, use getPrimitive3DID() call for switch
+ switch(rCandidate.getPrimitive3DID())
+ {
+ case PRIMITIVE3D_ID_TRANSFORMPRIMITIVE3D :
+ {
+ // transform group. Remember current transformations
+ const primitive3d::TransformPrimitive3D& rPrimitive = static_cast< const primitive3d::TransformPrimitive3D& >(rCandidate);
+ const geometry::ViewInformation3D aLastViewInformation3D(getViewInformation3D());
+
+ // create new transformation; add new object transform from right side
+ const geometry::ViewInformation3D aNewViewInformation3D(
+ aLastViewInformation3D.getObjectTransformation() * rPrimitive.getTransformation(),
+ aLastViewInformation3D.getOrientation(),
+ aLastViewInformation3D.getProjection(),
+ aLastViewInformation3D.getDeviceToView(),
+ aLastViewInformation3D.getViewTime(),
+ aLastViewInformation3D.getExtendedInformationSequence());
+ updateViewInformation(aNewViewInformation3D);
+
+ // let break down
+ process(rPrimitive.getChildren());
+
+ // restore transformations
+ updateViewInformation(aLastViewInformation3D);
+ break;
+ }
+ case PRIMITIVE3D_ID_POLYGONHAIRLINEPRIMITIVE3D :
+ {
+ // PolygonHairlinePrimitive3D
+ const primitive3d::PolygonHairlinePrimitive3D& rPrimitive = static_cast< const primitive3d::PolygonHairlinePrimitive3D& >(rCandidate);
+ const basegfx::B3DPolygon& rPolygon = rPrimitive.getB3DPolygon();
+ const sal_uInt32 nCount(rPolygon.count());
+
+ for(sal_uInt32 a(0); a < nCount; a++)
+ {
+ const basegfx::B3DPoint aPointInView(getViewInformation3D().getObjectToView() * rPolygon.getB3DPoint(a));
+
+ if(aPointInView.getZ() < mfMinimalDepth)
+ {
+ mfMinimalDepth = aPointInView.getZ();
+ }
+ }
+
+ break;
+ }
+ case PRIMITIVE3D_ID_POLYPOLYGONMATERIALPRIMITIVE3D :
+ {
+ // PolyPolygonMaterialPrimitive3D
+ const primitive3d::PolyPolygonMaterialPrimitive3D& rPrimitive = static_cast< const primitive3d::PolyPolygonMaterialPrimitive3D& >(rCandidate);
+ const basegfx::B3DPolyPolygon& rPolyPolygon = rPrimitive.getB3DPolyPolygon();
+ const sal_uInt32 nPolyCount(rPolyPolygon.count());
+
+ for(sal_uInt32 a(0); a < nPolyCount; a++)
+ {
+ const basegfx::B3DPolygon& aPolygon(rPolyPolygon.getB3DPolygon(a));
+ const sal_uInt32 nCount(aPolygon.count());
+
+ for(sal_uInt32 b(0); b < nCount; b++)
+ {
+ const basegfx::B3DPoint aPointInView(getViewInformation3D().getObjectToView() * aPolygon.getB3DPoint(b));
+
+ if(aPointInView.getZ() < mfMinimalDepth)
+ {
+ mfMinimalDepth = aPointInView.getZ();
+ }
+ }
+ }
+
+ break;
+ }
+ default :
+ {
+ // process recursively
+ process(rCandidate.get3DDecomposition(getViewInformation3D()));
+ break;
+ }
+ }
+ }
+} // end of namespace
+
+
+// changed to create values using VCs, Primitive3DContainer and ViewInformation3D to allow
+// removal of old 3D bucket geometry. There is one slight difference in the result, it's
+// in [0.0 .. 1.0] for Z-Depth since the scaling of the scene as 2D object is no longer
+// part of the 3D transformations. This could be added since the ViewContactOfE3dScene is
+// given, but is not needed since the permutation of the depth values needs only be correct
+// relative to each other
+
+double getMinimalDepthInViewCoordinates(const E3dCompoundObject& rObject)
+{
+ // this is an E3dCompoundObject, so it cannot be a scene (which is an E3dObject).
+ // Get primitive sequence using VC
+ const sdr::contact::ViewContactOfE3d& rVCObject = static_cast< sdr::contact::ViewContactOfE3d& >(rObject.GetViewContact());
+ const drawinglayer::primitive3d::Primitive3DContainer aPrimitives = rVCObject.getViewIndependentPrimitive3DContainer();
+ double fRetval(DBL_MAX);
+
+ if(!aPrimitives.empty())
+ {
+ const E3dScene* pScene(rObject.getRootE3dSceneFromE3dObject());
+
+ if(pScene)
+ {
+ // get ViewInformation3D from scene using VC
+ const sdr::contact::ViewContactOfE3dScene& rVCScene = static_cast< sdr::contact::ViewContactOfE3dScene& >(pScene->GetViewContact());
+ const drawinglayer::geometry::ViewInformation3D& aViewInfo3D(rVCScene.getViewInformation3D());
+
+ // the scene's object transformation is already part of aViewInfo3D.getObjectTransformation()
+ // for historical reasons (see ViewContactOfE3dScene::createViewInformation3D for more info)
+ // and the object's transform is part of aPrimitives (and taken into account when decomposing
+ // to PolygonHairlinePrimitive3D and PolyPolygonMaterialPrimitive3D). The missing part may be
+ // some Scene SdrObjects lying in-between which may need to be added. This is e.g. used in chart,
+ // and generally allowed in 3d scenes and their 3d object hierarchy
+ basegfx::B3DHomMatrix aInBetweenSceneMatrix;
+ E3dScene* pParentScene(rObject.getParentE3dSceneFromE3dObject());
+
+ while(pParentScene && pParentScene != pScene)
+ {
+ aInBetweenSceneMatrix = pParentScene->GetTransform() * aInBetweenSceneMatrix;
+ pParentScene = pParentScene->getParentE3dSceneFromE3dObject();
+ }
+
+ // build new ViewInformation containing all transforms
+ const drawinglayer::geometry::ViewInformation3D aNewViewInformation3D(
+ aViewInfo3D.getObjectTransformation() * aInBetweenSceneMatrix,
+ aViewInfo3D.getOrientation(),
+ aViewInfo3D.getProjection(),
+ aViewInfo3D.getDeviceToView(),
+ aViewInfo3D.getViewTime(),
+ aViewInfo3D.getExtendedInformationSequence());
+
+ // create extractor helper, process geometry and get return value
+ drawinglayer::processor3d::MinimalDephInViewExtractor aExtractor(aNewViewInformation3D);
+ aExtractor.process(aPrimitives);
+ fRetval = aExtractor.getMinimalDepth();
+ }
+ }
+
+ return fRetval;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/engine3d/helperminimaldepth3d.hxx b/svx/source/engine3d/helperminimaldepth3d.hxx
new file mode 100644
index 000000000..392ba49f6
--- /dev/null
+++ b/svx/source/engine3d/helperminimaldepth3d.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_SVX_SOURCE_ENGINE3D_HELPERMINIMALDEPTH3D_HXX
+#define INCLUDED_SVX_SOURCE_ENGINE3D_HELPERMINIMALDEPTH3D_HXX
+
+// predefines
+
+class E3dCompoundObject;
+
+/** support extracting the minimal depth of a 3d object in its scene
+
+ @param rObject
+ The 3D Object from which the minimal depth needs to be calculated. The scene
+ is defined by the object already
+
+ @return
+ The minimal depth of this object in unified ViewCoordinates. This is the
+ Z-Coordinate of one object point in the range of [0.0 .. 1.0]. ViewCoordinates
+ means the transformations (esp. rotation) of the scene are taken into account
+
+*/
+// support extracting the minimal depth of a 3d object in its scene
+
+double getMinimalDepthInViewCoordinates(const E3dCompoundObject& rObject);
+
+#endif // INCLUDED_SVX_SOURCE_ENGINE3D_HELPERMINIMALDEPTH3D_HXX
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/engine3d/lathe3d.cxx b/svx/source/engine3d/lathe3d.cxx
new file mode 100644
index 000000000..70a6b7ab0
--- /dev/null
+++ b/svx/source/engine3d/lathe3d.cxx
@@ -0,0 +1,201 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+
+#include <svx/deflt3d.hxx>
+#include <svx/strings.hrc>
+#include <svx/dialmgr.hxx>
+#include <svx/svdobjkind.hxx>
+#include <svx/lathe3d.hxx>
+#include <svx/svdopath.hxx>
+#include <svx/svdmodel.hxx>
+#include <svx/svx3ditems.hxx>
+#include <svx/xlineit0.hxx>
+#include <sdr/properties/e3dlatheproperties.hxx>
+#include <sdr/contact/viewcontactofe3dlathe.hxx>
+#include <basegfx/polygon/b2dpolypolygontools.hxx>
+#include <basegfx/matrix/b2dhommatrix.hxx>
+
+
+// DrawContact section
+std::unique_ptr<sdr::contact::ViewContact> E3dLatheObj::CreateObjectSpecificViewContact()
+{
+ return std::make_unique<sdr::contact::ViewContactOfE3dLathe>(*this);
+}
+
+std::unique_ptr<sdr::properties::BaseProperties> E3dLatheObj::CreateObjectSpecificProperties()
+{
+ return std::make_unique<sdr::properties::E3dLatheProperties>(*this);
+}
+
+// Constructor from 3D polygon, scale is the conversion factor for the coordinates
+E3dLatheObj::E3dLatheObj(
+ SdrModel& rSdrModel,
+ const E3dDefaultAttributes& rDefault,
+ const basegfx::B2DPolyPolygon& rPoly2D)
+: E3dCompoundObject(rSdrModel),
+ maPolyPoly2D(rPoly2D)
+{
+ // since the old class PolyPolygon3D did mirror the given PolyPolygons in Y, do the same here
+ basegfx::B2DHomMatrix aMirrorY;
+ aMirrorY.scale(1.0, -1.0);
+ maPolyPoly2D.transform(aMirrorY);
+
+ // Set Defaults
+ SetDefaultAttributes(rDefault);
+
+ // Superfluous items removed, in particular to prevent duplicate
+ // start and end points
+ maPolyPoly2D.removeDoublePoints();
+
+ if(maPolyPoly2D.count())
+ {
+ const basegfx::B2DPolygon rPoly(maPolyPoly2D.getB2DPolygon(0));
+ sal_uInt32 nSegCnt(rPoly.count());
+
+ if(nSegCnt && !rPoly.isClosed())
+ {
+ nSegCnt -= 1;
+ }
+
+ GetProperties().SetObjectItemDirect(makeSvx3DVerticalSegmentsItem(nSegCnt));
+ }
+}
+
+E3dLatheObj::E3dLatheObj(SdrModel& rSdrModel)
+: E3dCompoundObject(rSdrModel)
+{
+ // Set Defaults
+ const E3dDefaultAttributes aDefault;
+
+ SetDefaultAttributes(aDefault);
+}
+
+E3dLatheObj::E3dLatheObj(SdrModel& rSdrModel, E3dLatheObj const & rSource)
+: E3dCompoundObject(rSdrModel, rSource)
+{
+ // Set Defaults
+ const E3dDefaultAttributes aDefault;
+
+ SetDefaultAttributes(aDefault);
+
+ maPolyPoly2D = rSource.maPolyPoly2D;
+}
+
+E3dLatheObj::~E3dLatheObj()
+{
+}
+
+void E3dLatheObj::SetDefaultAttributes(const E3dDefaultAttributes& rDefault)
+{
+ GetProperties().SetObjectItemDirect(Svx3DSmoothNormalsItem(rDefault.GetDefaultLatheSmoothed()));
+ GetProperties().SetObjectItemDirect(Svx3DSmoothLidsItem(rDefault.GetDefaultLatheSmoothFrontBack()));
+ GetProperties().SetObjectItemDirect(Svx3DCharacterModeItem(rDefault.GetDefaultLatheCharacterMode()));
+ GetProperties().SetObjectItemDirect(Svx3DCloseFrontItem(rDefault.GetDefaultLatheCloseFront()));
+ GetProperties().SetObjectItemDirect(Svx3DCloseBackItem(rDefault.GetDefaultLatheCloseBack()));
+}
+
+SdrObjKind E3dLatheObj::GetObjIdentifier() const
+{
+ return SdrObjKind::E3D_Lathe;
+}
+
+E3dLatheObj* E3dLatheObj::CloneSdrObject(SdrModel& rTargetModel) const
+{
+ return new E3dLatheObj(rTargetModel, *this);
+}
+
+// Convert the object to group object consisting of n polygons
+
+SdrObjectUniquePtr E3dLatheObj::DoConvertToPolyObj(bool /*bBezier*/, bool /*bAddText*/) const
+{
+ return nullptr;
+}
+
+// Set Local parameters set to re-create geometry
+
+void E3dLatheObj::SetPolyPoly2D(const basegfx::B2DPolyPolygon& rNew)
+{
+ if(maPolyPoly2D == rNew)
+ return;
+
+ maPolyPoly2D = rNew;
+ maPolyPoly2D.removeDoublePoints();
+
+ if(maPolyPoly2D.count())
+ {
+ const basegfx::B2DPolygon rPoly(maPolyPoly2D.getB2DPolygon(0));
+ sal_uInt32 nSegCnt(rPoly.count());
+
+ if(nSegCnt && !rPoly.isClosed())
+ {
+ nSegCnt -= 1;
+ }
+
+ GetProperties().SetObjectItemDirect(makeSvx3DVerticalSegmentsItem(nSegCnt));
+ }
+
+ ActionChanged();
+}
+
+// Get the name of the object (singular)
+
+OUString E3dLatheObj::TakeObjNameSingul() const
+{
+ OUString sName(SvxResId(STR_ObjNameSingulLathe3d));
+
+ OUString aName(GetName());
+ if (!aName.isEmpty())
+ {
+ sName += " '" + aName + "'";
+ }
+ return sName;
+}
+
+// Get the name of the object (plural)
+
+OUString E3dLatheObj::TakeObjNamePlural() const
+{
+ return SvxResId(STR_ObjNamePluralLathe3d);
+}
+
+bool E3dLatheObj::IsBreakObjPossible()
+{
+ return true;
+}
+
+std::unique_ptr<SdrAttrObj,SdrObjectFreeOp> E3dLatheObj::GetBreakObj()
+{
+ // create PathObj
+ basegfx::B3DPolyPolygon aLathePoly3D(basegfx::utils::createB3DPolyPolygonFromB2DPolyPolygon(maPolyPoly2D));
+ basegfx::B2DPolyPolygon aTransPoly(TransformToScreenCoor(aLathePoly3D));
+ std::unique_ptr<SdrPathObj,SdrObjectFreeOp> pPathObj(new SdrPathObj(getSdrModelFromSdrObject(), SdrObjKind::PolyLine, aTransPoly));
+
+ // Set Attribute
+ SfxItemSet aSet(GetObjectItemSet());
+
+ // Enable lines to guarantee that the object becomes visible
+ aSet.Put(XLineStyleItem(css::drawing::LineStyle_SOLID));
+
+ pPathObj->SetMergedItemSet(aSet);
+
+ return std::unique_ptr<SdrAttrObj,SdrObjectFreeOp>(pPathObj.release());
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/engine3d/obj3d.cxx b/svx/source/engine3d/obj3d.cxx
new file mode 100644
index 000000000..692e3d757
--- /dev/null
+++ b/svx/source/engine3d/obj3d.cxx
@@ -0,0 +1,617 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * 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/numeric.hxx>
+
+#include <svx/strings.hrc>
+#include <svx/dialmgr.hxx>
+#include <svx/svdhdl.hxx>
+#include <svx/svdmodel.hxx>
+#include <svx/svdobjkind.hxx>
+#include <svx/scene3d.hxx>
+#include <svx/obj3d.hxx>
+#include <sdr/properties/e3dproperties.hxx>
+#include <sdr/properties/e3dcompoundproperties.hxx>
+#include <basegfx/polygon/b3dpolypolygontools.hxx>
+#include <basegfx/point/b3dpoint.hxx>
+#include <basegfx/matrix/b3dhommatrix.hxx>
+#include <basegfx/matrix/b2dhommatrix.hxx>
+#include <basegfx/polygon/b2dpolypolygontools.hxx>
+#include <svx/helperhittest3d.hxx>
+#include <sdr/contact/viewcontactofe3d.hxx>
+#include <drawinglayer/geometry/viewinformation3d.hxx>
+#include <com/sun/star/uno/Sequence.h>
+#include <svx/sdr/contact/viewcontactofe3dscene.hxx>
+#include <svx/e3dsceneupdater.hxx>
+#include <unotools/configmgr.hxx>
+
+using namespace com::sun::star;
+
+std::unique_ptr<sdr::properties::BaseProperties> E3dObject::CreateObjectSpecificProperties()
+{
+ return std::make_unique<sdr::properties::E3dProperties>(*this);
+}
+
+E3dObject::E3dObject(SdrModel& rSdrModel)
+: SdrAttrObj(rSdrModel),
+ mbTfHasChanged(true),
+ mbIsSelected(false)
+{
+ m_bIs3DObj = true;
+ m_bClosedObj = true;
+}
+
+E3dObject::E3dObject(SdrModel& rSdrModel, E3dObject const & rSource)
+: SdrAttrObj(rSdrModel, rSource),
+ mbTfHasChanged(true),
+ mbIsSelected(false)
+{
+ m_bIs3DObj = true;
+ m_bClosedObj = true;
+
+ // BoundVol can be copied since also the children are copied
+ maLocalBoundVol = rSource.maLocalBoundVol;
+ maTransformation = rSource.maTransformation;
+
+ // Because the parent may have changed, definitely redefine the total
+ // transformation next time
+ SetTransformChanged();
+
+ // Copy selection status
+ mbIsSelected = rSource.mbIsSelected;
+}
+
+E3dObject::~E3dObject()
+{
+}
+
+void E3dObject::SetSelected(bool bNew)
+{
+ if(mbIsSelected != bNew)
+ {
+ mbIsSelected = bNew;
+ }
+}
+
+// Break, default implementations
+bool E3dObject::IsBreakObjPossible()
+{
+ return false;
+}
+
+std::unique_ptr<SdrAttrObj,SdrObjectFreeOp> E3dObject::GetBreakObj()
+{
+ return nullptr;
+}
+
+SdrInventor E3dObject::GetObjInventor() const
+{
+ return SdrInventor::E3d;
+}
+
+SdrObjKind E3dObject::GetObjIdentifier() const
+{
+ return SdrObjKind::E3D_Object;
+}
+
+// Determine the capabilities of the object
+void E3dObject::TakeObjInfo(SdrObjTransformInfoRec& rInfo) const
+{
+ rInfo.bResizeFreeAllowed = true;
+ rInfo.bResizePropAllowed = true;
+ rInfo.bRotateFreeAllowed = true;
+ rInfo.bRotate90Allowed = true;
+ rInfo.bMirrorFreeAllowed = false;
+ rInfo.bMirror45Allowed = false;
+ rInfo.bMirror90Allowed = false;
+ rInfo.bShearAllowed = false;
+ rInfo.bEdgeRadiusAllowed = false;
+ rInfo.bCanConvToPath = false;
+
+ // no transparence for 3d objects
+ rInfo.bTransparenceAllowed = false;
+
+ // Convert 3D objects in a group of polygons:
+ // At first not only possible, because the creation of a group of
+ // 2D polygons would be required which need to be sorted by depth,
+ // ie at intersections be cut relative to each other. Also the texture
+ // coordinates were an unsolved problem.
+ rInfo.bCanConvToPoly = false;
+ rInfo.bCanConvToContour = false;
+ rInfo.bCanConvToPathLineToArea = false;
+ rInfo.bCanConvToPolyLineToArea = false;
+}
+
+// resize object, used from old 2d interfaces, e.g. in Move/Scale dialog (F4)
+void E3dObject::NbcResize(const Point& rRef, const Fraction& xFact, const Fraction& yFact)
+{
+ // Movement in X, Y in the eye coordinate system
+ E3dScene* pScene(getRootE3dSceneFromE3dObject());
+
+ if(nullptr == pScene)
+ {
+ return;
+ }
+
+ // transform pos from 2D world to 3D eye
+ const sdr::contact::ViewContactOfE3dScene& rVCScene = static_cast< sdr::contact::ViewContactOfE3dScene& >(pScene->GetViewContact());
+ const drawinglayer::geometry::ViewInformation3D& aViewInfo3D(rVCScene.getViewInformation3D());
+ basegfx::B2DPoint aScaleCenter2D(static_cast<double>(rRef.X()), static_cast<double>(rRef.Y()));
+ basegfx::B2DHomMatrix aInverseSceneTransform(rVCScene.getObjectTransformation());
+
+ aInverseSceneTransform.invert();
+ aScaleCenter2D = aInverseSceneTransform * aScaleCenter2D;
+
+ basegfx::B3DPoint aScaleCenter3D(aScaleCenter2D.getX(), aScaleCenter2D.getY(), 0.5);
+ basegfx::B3DHomMatrix aInverseViewToEye(aViewInfo3D.getDeviceToView() * aViewInfo3D.getProjection());
+
+ aInverseViewToEye.invert();
+ aScaleCenter3D = aInverseViewToEye * aScaleCenter3D;
+
+ // Get scale factors
+ double fScaleX(xFact);
+ double fScaleY(yFact);
+
+ // build transform
+ basegfx::B3DHomMatrix aInverseOrientation(aViewInfo3D.getOrientation());
+ aInverseOrientation.invert();
+ basegfx::B3DHomMatrix aFullTransform(GetFullTransform());
+ basegfx::B3DHomMatrix aTrans(aFullTransform);
+
+ aTrans *= aViewInfo3D.getOrientation();
+ aTrans.translate(-aScaleCenter3D.getX(), -aScaleCenter3D.getY(), -aScaleCenter3D.getZ());
+ aTrans.scale(fScaleX, fScaleY, 1.0);
+ aTrans.translate(aScaleCenter3D.getX(), aScaleCenter3D.getY(), aScaleCenter3D.getZ());
+ aTrans *= aInverseOrientation;
+ aFullTransform.invert();
+ aTrans *= aFullTransform;
+
+ // Apply
+ basegfx::B3DHomMatrix aObjTrans(GetTransform());
+ aObjTrans *= aTrans;
+
+ E3DModifySceneSnapRectUpdater aUpdater(this);
+ SetTransform(aObjTrans);
+}
+
+// Move object in 2D is needed when using cursor keys
+void E3dObject::NbcMove(const Size& rSize)
+{
+ // Movement in X, Y in the eye coordinate system
+ E3dScene* pScene(getRootE3dSceneFromE3dObject());
+
+ if(nullptr == pScene)
+ {
+ return;
+ }
+
+ //Dimensions of the scene in 3D and 2D for comparison
+ tools::Rectangle aRect = pScene->GetSnapRect();
+ basegfx::B3DHomMatrix aInvDispTransform;
+ E3dScene* pParent(getParentE3dSceneFromE3dObject());
+
+ if(nullptr != pParent)
+ {
+ aInvDispTransform = pParent->GetFullTransform();
+ aInvDispTransform.invert();
+ }
+
+ // BoundVolume from 3d world to 3d eye
+ const sdr::contact::ViewContactOfE3dScene& rVCScene = static_cast< sdr::contact::ViewContactOfE3dScene& >(pScene->GetViewContact());
+ const drawinglayer::geometry::ViewInformation3D& aViewInfo3D(rVCScene.getViewInformation3D());
+ basegfx::B3DRange aEyeVol(pScene->GetBoundVolume());
+ aEyeVol.transform(aViewInfo3D.getOrientation());
+
+ if ((aRect.GetWidth() == 0) || (aRect.GetHeight() == 0))
+ throw o3tl::divide_by_zero();
+
+ // build relative movement vector in eye coordinates
+ basegfx::B3DPoint aMove(
+ static_cast<double>(rSize.Width()) * aEyeVol.getWidth() / static_cast<double>(aRect.GetWidth()),
+ static_cast<double>(-rSize.Height()) * aEyeVol.getHeight() / static_cast<double>(aRect.GetHeight()),
+ 0.0);
+ basegfx::B3DPoint aPos(0.0, 0.0, 0.0);
+
+ // movement vector to local coordinates of objects' parent
+ basegfx::B3DHomMatrix aInverseOrientation(aViewInfo3D.getOrientation());
+ aInverseOrientation.invert();
+ basegfx::B3DHomMatrix aCompleteTrans(aInvDispTransform * aInverseOrientation);
+
+ aMove = aCompleteTrans * aMove;
+ aPos = aCompleteTrans * aPos;
+
+ // build transformation and apply
+ basegfx::B3DHomMatrix aTranslate;
+ aTranslate.translate(aMove.getX() - aPos.getX(), aMove.getY() - aPos.getY(), aMove.getZ() - aPos.getZ());
+
+ E3DModifySceneSnapRectUpdater aUpdater(pScene);
+ SetTransform(aTranslate * GetTransform());
+}
+
+void E3dObject::RecalcSnapRect()
+{
+ maSnapRect = tools::Rectangle();
+}
+
+// Inform parent of changes in the structure (eg by transformation), in this
+// process the object in which the change has occurred is returned.
+void E3dObject::StructureChanged()
+{
+ E3dScene* pParent(getParentE3dSceneFromE3dObject());
+
+ if(nullptr != pParent)
+ {
+ pParent->InvalidateBoundVolume();
+ pParent->StructureChanged();
+ }
+}
+
+E3dScene* E3dObject::getParentE3dSceneFromE3dObject() const
+{
+ return dynamic_cast< E3dScene* >(getParentSdrObjectFromSdrObject());
+}
+
+// Determine the top-level scene object
+E3dScene* E3dObject::getRootE3dSceneFromE3dObject() const
+{
+ E3dScene* pParent(getParentE3dSceneFromE3dObject());
+
+ if(nullptr != pParent)
+ {
+ return pParent->getRootE3dSceneFromE3dObject();
+ }
+
+ return nullptr;
+}
+
+// Calculate enclosed volume, including all child objects
+basegfx::B3DRange E3dObject::RecalcBoundVolume() const
+{
+ basegfx::B3DRange aRetval;
+ if (utl::ConfigManager::IsFuzzing()) // skip slow path for fuzzing
+ return aRetval;
+
+ const sdr::contact::ViewContactOfE3d* pVCOfE3D = dynamic_cast< const sdr::contact::ViewContactOfE3d* >(&GetViewContact());
+
+ if(pVCOfE3D)
+ {
+ // BoundVolume is without 3D object transformation, use correct sequence
+ const drawinglayer::primitive3d::Primitive3DContainer& xLocalSequence(pVCOfE3D->getVIP3DSWithoutObjectTransform());
+
+ if(!xLocalSequence.empty())
+ {
+ const uno::Sequence< beans::PropertyValue > aEmptyParameters;
+ const drawinglayer::geometry::ViewInformation3D aLocalViewInformation3D(aEmptyParameters);
+
+ aRetval = xLocalSequence.getB3DRange(aLocalViewInformation3D);
+ }
+ }
+
+ return aRetval;
+}
+
+// Get enclosed volume and possibly recalculate it
+const basegfx::B3DRange& E3dObject::GetBoundVolume() const
+{
+ if(maLocalBoundVol.isEmpty())
+ {
+ const_cast< E3dObject* >(this)->maLocalBoundVol = RecalcBoundVolume();
+ }
+
+ return maLocalBoundVol;
+}
+
+void E3dObject::InvalidateBoundVolume()
+{
+ maLocalBoundVol.reset();
+}
+
+// Pass on the changes in transformation to all child objects
+void E3dObject::SetTransformChanged()
+{
+ InvalidateBoundVolume();
+ mbTfHasChanged = true;
+}
+
+// Define the hierarchical transformation over all Parents, store in
+// maFullTransform and return them
+const basegfx::B3DHomMatrix& E3dObject::GetFullTransform() const
+{
+ if(mbTfHasChanged)
+ {
+ basegfx::B3DHomMatrix aNewFullTransformation(maTransformation);
+ E3dScene* pParent(getParentE3dSceneFromE3dObject());
+
+ if(nullptr != pParent)
+ {
+ aNewFullTransformation = pParent->GetFullTransform() * aNewFullTransformation;
+ }
+
+ const_cast< E3dObject* >(this)->maFullTransform = aNewFullTransformation;
+ const_cast< E3dObject* >(this)->mbTfHasChanged = false;
+ }
+
+ return maFullTransform;
+}
+
+void E3dObject::NbcSetTransform(const basegfx::B3DHomMatrix& rMatrix)
+{
+ if(maTransformation != rMatrix)
+ {
+ maTransformation = rMatrix;
+ SetTransformChanged();
+ StructureChanged();
+ }
+}
+
+// Set transformation matrix with repaint broadcast
+void E3dObject::SetTransform(const basegfx::B3DHomMatrix& rMatrix)
+{
+ if(rMatrix != maTransformation)
+ {
+ NbcSetTransform(rMatrix);
+ SetChanged();
+ BroadcastObjectChange();
+ if (m_pUserCall != nullptr) m_pUserCall->Changed(*this, SdrUserCallType::Resize, tools::Rectangle());
+ }
+}
+
+basegfx::B3DPolyPolygon E3dObject::CreateWireframe() const
+{
+ const basegfx::B3DRange aBoundVolume(GetBoundVolume());
+ return basegfx::utils::createCubePolyPolygonFromB3DRange(aBoundVolume);
+}
+
+// Get the name of the object (singular)
+OUString E3dObject::TakeObjNameSingul() const
+{
+ OUString sName = SvxResId(STR_ObjNameSingulObj3d);
+
+ OUString aName(GetName());
+ if (!aName.isEmpty())
+ {
+ sName += " '" + aName + "'";
+ }
+ return sName;
+}
+
+// Get the name of the object (plural)
+OUString E3dObject::TakeObjNamePlural() const
+{
+ return SvxResId(STR_ObjNamePluralObj3d);
+}
+
+E3dObject* E3dObject::CloneSdrObject(SdrModel& rTargetModel) const
+{
+ return new E3dObject(rTargetModel, *this);
+}
+
+std::unique_ptr<SdrObjGeoData> E3dObject::NewGeoData() const
+{
+ return std::make_unique<E3DObjGeoData>();
+}
+
+void E3dObject::SaveGeoData(SdrObjGeoData& rGeo) const
+{
+ SdrAttrObj::SaveGeoData (rGeo);
+
+ static_cast<E3DObjGeoData &>(rGeo).maLocalBoundVol = maLocalBoundVol;
+ static_cast<E3DObjGeoData &>(rGeo).maTransformation = maTransformation;
+}
+
+void E3dObject::RestoreGeoData(const SdrObjGeoData& rGeo)
+{
+ maLocalBoundVol = static_cast<const E3DObjGeoData &>(rGeo).maLocalBoundVol;
+ E3DModifySceneSnapRectUpdater aUpdater(this);
+ NbcSetTransform(static_cast<const E3DObjGeoData &>(rGeo).maTransformation);
+ SdrAttrObj::RestoreGeoData (rGeo);
+}
+
+// 2D-rotation of a 3D-body, normally this is done by the scene itself.
+// This is however a correct implementation, because everything that has
+// happened is a rotation around the axis perpendicular to the screen and that
+// is regardless of how the scene has been rotated up until now.
+void E3dObject::NbcRotate(const Point& rRef, Degree100 nAngle, double sn, double cs)
+{
+ // So currently the gluepoints are defined relative to the scene aOutRect.
+ // Before turning the gluepoints are defined relative to the page. They
+ // take no part in the rotation of the scene. To ensure this, there is the
+ // SetGlueReallyAbsolute(sal_True);
+ double fAngleInRad = toRadians(nAngle);
+
+ basegfx::B3DHomMatrix aRotateZ;
+ aRotateZ.rotate(0.0, 0.0, fAngleInRad);
+ NbcSetTransform(aRotateZ * GetTransform());
+
+ SetBoundAndSnapRectsDirty(); // This forces a recalculation of all BoundRects
+ NbcRotateGluePoints(rRef,nAngle,sn,cs); // Rotate the gluepoints (who still
+ // have coordinates relative to the
+ // original page)
+ SetGlueReallyAbsolute(false); // from now they are again relative to BoundRect (that is defined as aOutRect)
+}
+
+std::unique_ptr<sdr::properties::BaseProperties> E3dCompoundObject::CreateObjectSpecificProperties()
+{
+ return std::make_unique<sdr::properties::E3dCompoundProperties>(*this);
+}
+
+E3dCompoundObject::E3dCompoundObject(SdrModel& rSdrModel)
+: E3dObject(rSdrModel)
+{
+}
+
+E3dCompoundObject::E3dCompoundObject(SdrModel& rSdrModel, E3dCompoundObject const & rSource)
+: E3dObject(rSdrModel, rSource)
+{
+}
+
+E3dCompoundObject::~E3dCompoundObject ()
+{
+}
+
+basegfx::B2DPolyPolygon E3dCompoundObject::TakeXorPoly() const
+{
+ basegfx::B2DPolyPolygon aRetval;
+ const uno::Sequence< beans::PropertyValue > aEmptyParameters;
+ drawinglayer::geometry::ViewInformation3D aViewInfo3D(aEmptyParameters);
+ E3dScene* pRootScene = fillViewInformation3DForCompoundObject(aViewInfo3D, *this);
+
+ if(pRootScene)
+ {
+ const sdr::contact::ViewContactOfE3dScene& rVCScene = static_cast< sdr::contact::ViewContactOfE3dScene& >(pRootScene->GetViewContact());
+ const basegfx::B3DPolyPolygon aCubePolyPolygon(CreateWireframe());
+ aRetval = basegfx::utils::createB2DPolyPolygonFromB3DPolyPolygon(aCubePolyPolygon,
+ aViewInfo3D.getObjectToView() * GetTransform());
+ aRetval.transform(rVCScene.getObjectTransformation());
+ }
+
+ return aRetval;
+}
+
+sal_uInt32 E3dCompoundObject::GetHdlCount() const
+{
+ // 8 corners + 1 E3dVolumeMarker (= Wireframe representation)
+ return 9;
+}
+
+void E3dCompoundObject::AddToHdlList(SdrHdlList& rHdlList) const
+{
+ const uno::Sequence< beans::PropertyValue > aEmptyParameters;
+ drawinglayer::geometry::ViewInformation3D aViewInfo3D(aEmptyParameters);
+ E3dScene* pRootScene = fillViewInformation3DForCompoundObject(aViewInfo3D, *this);
+
+ if(pRootScene)
+ {
+ const basegfx::B3DRange aBoundVolume(GetBoundVolume());
+
+ if(!aBoundVolume.isEmpty())
+ {
+ const sdr::contact::ViewContactOfE3dScene& rVCScene = static_cast< sdr::contact::ViewContactOfE3dScene& >(pRootScene->GetViewContact());
+
+ for(sal_uInt32 a(0); a < 8; a++)
+ {
+ basegfx::B3DPoint aPos3D;
+
+ switch(a)
+ {
+ case 0 : aPos3D.setX(aBoundVolume.getMinX()); aPos3D.setY(aBoundVolume.getMinY()); aPos3D.setZ(aBoundVolume.getMinZ()); break;
+ case 1 : aPos3D.setX(aBoundVolume.getMinX()); aPos3D.setY(aBoundVolume.getMinY()); aPos3D.setZ(aBoundVolume.getMaxZ()); break;
+ case 2 : aPos3D.setX(aBoundVolume.getMinX()); aPos3D.setY(aBoundVolume.getMaxY()); aPos3D.setZ(aBoundVolume.getMinZ()); break;
+ case 3 : aPos3D.setX(aBoundVolume.getMinX()); aPos3D.setY(aBoundVolume.getMaxY()); aPos3D.setZ(aBoundVolume.getMaxZ()); break;
+ case 4 : aPos3D.setX(aBoundVolume.getMaxX()); aPos3D.setY(aBoundVolume.getMinY()); aPos3D.setZ(aBoundVolume.getMinZ()); break;
+ case 5 : aPos3D.setX(aBoundVolume.getMaxX()); aPos3D.setY(aBoundVolume.getMinY()); aPos3D.setZ(aBoundVolume.getMaxZ()); break;
+ case 6 : aPos3D.setX(aBoundVolume.getMaxX()); aPos3D.setY(aBoundVolume.getMaxY()); aPos3D.setZ(aBoundVolume.getMinZ()); break;
+ case 7 : aPos3D.setX(aBoundVolume.getMaxX()); aPos3D.setY(aBoundVolume.getMaxY()); aPos3D.setZ(aBoundVolume.getMaxZ()); break;
+ }
+
+ // to 3d view coor
+ aPos3D *= aViewInfo3D.getObjectToView() * GetTransform();
+
+ // create 2d relative scene
+ basegfx::B2DPoint aPos2D(aPos3D.getX(), aPos3D.getY());
+
+ // to 2d world coor
+ aPos2D *= rVCScene.getObjectTransformation();
+
+ rHdlList.AddHdl(std::make_unique<SdrHdl>(Point(basegfx::fround(aPos2D.getX()), basegfx::fround(aPos2D.getY())), SdrHdlKind::BezierWeight));
+ }
+ }
+ }
+
+ const basegfx::B2DPolyPolygon aPolyPolygon(TakeXorPoly());
+
+ if(aPolyPolygon.count())
+ {
+ rHdlList.AddHdl(std::make_unique<E3dVolumeMarker>(aPolyPolygon));
+ }
+}
+
+SdrObjKind E3dCompoundObject::GetObjIdentifier() const
+{
+ return SdrObjKind::E3D_CompoundObject;
+}
+
+void E3dCompoundObject::RecalcSnapRect()
+{
+ const uno::Sequence< beans::PropertyValue > aEmptyParameters;
+ drawinglayer::geometry::ViewInformation3D aViewInfo3D(aEmptyParameters);
+ E3dScene* pRootScene = fillViewInformation3DForCompoundObject(aViewInfo3D, *this);
+ maSnapRect = tools::Rectangle();
+
+ if(!pRootScene)
+ return;
+
+ // get VC of 3D candidate
+ const sdr::contact::ViewContactOfE3d* pVCOfE3D = dynamic_cast< const sdr::contact::ViewContactOfE3d* >(&GetViewContact());
+
+ if(!pVCOfE3D)
+ return;
+
+ // get 3D primitive sequence
+ const drawinglayer::primitive3d::Primitive3DContainer xLocalSequence(pVCOfE3D->getViewIndependentPrimitive3DContainer());
+
+ if(xLocalSequence.empty())
+ return;
+
+ // get BoundVolume
+ basegfx::B3DRange aBoundVolume(xLocalSequence.getB3DRange(aViewInfo3D));
+
+ // transform bound volume to relative scene coordinates
+ aBoundVolume.transform(aViewInfo3D.getObjectToView());
+
+ // build 2d relative scene range
+ basegfx::B2DRange aSnapRange(
+ aBoundVolume.getMinX(), aBoundVolume.getMinY(),
+ aBoundVolume.getMaxX(), aBoundVolume.getMaxY());
+
+ // transform to 2D world coordinates
+ const sdr::contact::ViewContactOfE3dScene& rVCScene = static_cast< sdr::contact::ViewContactOfE3dScene& >(pRootScene->GetViewContact());
+ aSnapRange.transform(rVCScene.getObjectTransformation());
+
+ // snap to integer
+ maSnapRect = tools::Rectangle(
+ sal_Int32(floor(aSnapRange.getMinX())), sal_Int32(floor(aSnapRange.getMinY())),
+ sal_Int32(ceil(aSnapRange.getMaxX())), sal_Int32(ceil(aSnapRange.getMaxY())));
+}
+
+E3dCompoundObject* E3dCompoundObject::CloneSdrObject(SdrModel& rTargetModel) const
+{
+ return new E3dCompoundObject(rTargetModel, *this);
+}
+
+// convert given basegfx::B3DPolyPolygon to screen coor
+basegfx::B2DPolyPolygon E3dCompoundObject::TransformToScreenCoor(const basegfx::B3DPolyPolygon& rCandidate) const
+{
+ const uno::Sequence< beans::PropertyValue > aEmptyParameters;
+ drawinglayer::geometry::ViewInformation3D aViewInfo3D(aEmptyParameters);
+ E3dScene* pRootScene = fillViewInformation3DForCompoundObject(aViewInfo3D, *this);
+ basegfx::B2DPolyPolygon aRetval;
+
+ if(pRootScene)
+ {
+ aRetval = basegfx::utils::createB2DPolyPolygonFromB3DPolyPolygon(rCandidate,
+ aViewInfo3D.getObjectToView() * GetTransform());
+ const sdr::contact::ViewContactOfE3dScene& rVCScene = static_cast< sdr::contact::ViewContactOfE3dScene& >(pRootScene->GetViewContact());
+ aRetval.transform(rVCScene.getObjectTransformation());
+ }
+
+ return aRetval;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/engine3d/objfac3d.cxx b/svx/source/engine3d/objfac3d.cxx
new file mode 100644
index 000000000..4153ffab5
--- /dev/null
+++ b/svx/source/engine3d/objfac3d.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 <svx/svdobjkind.hxx>
+#include <svx/cube3d.hxx>
+#include <svx/sphere3d.hxx>
+#include <extrud3d.hxx>
+#include <svx/lathe3d.hxx>
+#include <polygn3d.hxx>
+#include <svx/objfac3d.hxx>
+#include <svx/svdobj.hxx>
+#include <svx/scene3d.hxx>
+
+static bool bInit = false;
+
+E3dObjFactory::E3dObjFactory()
+{
+ if ( !bInit )
+ {
+ SdrObjFactory::InsertMakeObjectHdl(LINK(this, E3dObjFactory, MakeObject));
+ bInit = true;
+ }
+}
+
+// Generate chart internal objects
+
+IMPL_STATIC_LINK( E3dObjFactory, MakeObject, SdrObjCreatorParams, aParams, SdrObject* )
+{
+ if ( aParams.nInventor == SdrInventor::E3d )
+ {
+ switch ( aParams.nObjIdentifier )
+ {
+ case SdrObjKind::E3D_Scene:
+ return new E3dScene(aParams.rSdrModel);
+ case SdrObjKind::E3D_Polygon :
+ return new E3dPolygonObj(aParams.rSdrModel);
+ case SdrObjKind::E3D_Cube :
+ return new E3dCubeObj(aParams.rSdrModel);
+ case SdrObjKind::E3D_Sphere:
+ return new E3dSphereObj(aParams.rSdrModel);
+ case SdrObjKind::E3D_Extrusion:
+ return new E3dExtrudeObj(aParams.rSdrModel);
+ case SdrObjKind::E3D_Lathe:
+ return new E3dLatheObj(aParams.rSdrModel);
+ case SdrObjKind::E3D_CompoundObject:
+ return new E3dCompoundObject(aParams.rSdrModel);
+ default:
+ break;
+ }
+ }
+ return nullptr;
+}
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/engine3d/polygn3d.cxx b/svx/source/engine3d/polygn3d.cxx
new file mode 100644
index 000000000..e7a5623bf
--- /dev/null
+++ b/svx/source/engine3d/polygn3d.cxx
@@ -0,0 +1,239 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <polygn3d.hxx>
+#include <svx/svdobjkind.hxx>
+#include <basegfx/point/b3dpoint.hxx>
+#include <sdr/contact/viewcontactofe3dpolygon.hxx>
+#include <basegfx/polygon/b3dpolygon.hxx>
+#include <basegfx/polygon/b3dpolygontools.hxx>
+
+// DrawContact section
+std::unique_ptr<sdr::contact::ViewContact> E3dPolygonObj::CreateObjectSpecificViewContact()
+{
+ return std::make_unique<sdr::contact::ViewContactOfE3dPolygon>(*this);
+}
+
+E3dPolygonObj::E3dPolygonObj(SdrModel& rSdrModel, const basegfx::B3DPolyPolygon& rPolyPoly3D)
+ : E3dCompoundObject(rSdrModel)
+ , bLineOnly(true)
+{
+ // Set geometry
+ SetPolyPolygon3D(rPolyPoly3D);
+
+ // Create default normals
+ CreateDefaultNormals();
+
+ // Create default texture coordinates
+ CreateDefaultTexture();
+}
+
+E3dPolygonObj::E3dPolygonObj(SdrModel& rSdrModel)
+ : E3dCompoundObject(rSdrModel)
+ , bLineOnly(false)
+{
+ // Create no geometry
+}
+
+E3dPolygonObj::E3dPolygonObj(SdrModel& rSdrModel, E3dPolygonObj const& rSource)
+ : E3dCompoundObject(rSdrModel, rSource)
+ , bLineOnly(false)
+{
+ // Create no geometry
+
+ aPolyPoly3D = rSource.aPolyPoly3D;
+ aPolyNormals3D = rSource.aPolyNormals3D;
+ aPolyTexture2D = rSource.aPolyTexture2D;
+ bLineOnly = rSource.bLineOnly;
+}
+
+void E3dPolygonObj::CreateDefaultNormals()
+{
+ basegfx::B3DPolyPolygon aPolyNormals;
+
+ // Create a complete tools::PolyPolygon with the plane normal
+ for (sal_uInt32 a(0); a < aPolyPoly3D.count(); a++)
+ {
+ // Find source polygon
+ const basegfx::B3DPolygon aPolygon(aPolyPoly3D.getB3DPolygon(a));
+
+ // Creating a new polygon for the normal
+ basegfx::B3DPolygon aNormals;
+
+ // Get normal (and invert)
+ basegfx::B3DVector aNormal(-aPolygon.getNormal());
+
+ // Fill new polygon
+ for (sal_uInt32 b(0); b < aPolygon.count(); b++)
+ {
+ aNormals.append(aNormal);
+ }
+
+ // Insert new polygon into the PolyPolygon
+ aPolyNormals.append(aNormals);
+ }
+
+ // Set default normal
+ SetPolyNormals3D(aPolyNormals);
+}
+
+void E3dPolygonObj::CreateDefaultTexture()
+{
+ basegfx::B2DPolyPolygon aPolyTexture;
+ // Create a complete tools::PolyPolygon with the texture coordinates
+ // The texture coordinates extend over X,Y and Z
+ // on the whole extreme values in the range 0.0 .. 1.0
+ for (sal_uInt32 a(0); a < aPolyPoly3D.count(); a++)
+ {
+ // Find source polygon
+ const basegfx::B3DPolygon& aPolygon(aPolyPoly3D.getB3DPolygon(a));
+
+ // Determine the total size of the object
+ basegfx::B3DRange aVolume(basegfx::utils::getRange(aPolygon));
+
+ // Get normal
+ basegfx::B3DVector aNormal(aPolygon.getNormal());
+ aNormal.setX(fabs(aNormal.getX()));
+ aNormal.setY(fabs(aNormal.getY()));
+ aNormal.setZ(fabs(aNormal.getZ()));
+
+ // Decide which coordinates should be used as a source for the mapping
+ sal_uInt16 nSourceMode = 0;
+
+ // Determine the greatest degree of freedom
+ if (aNormal.getX() <= aNormal.getY() || aNormal.getX() <= aNormal.getZ())
+ {
+ if (aNormal.getY() > aNormal.getZ())
+ {
+ // Y is the largest, use X,Z as mapping
+ nSourceMode = 1;
+ }
+ else
+ {
+ // Z is the largest, use X,Y as mapping
+ nSourceMode = 2;
+ }
+ }
+
+ // Create new polygon for texture coordinates
+ basegfx::B2DPolygon aTexture;
+
+ // Fill new polygon
+ for (sal_uInt32 b(0); b < aPolygon.count(); b++)
+ {
+ basegfx::B2DPoint aTex;
+ const basegfx::B3DPoint aCandidate(aPolygon.getB3DPoint(b));
+
+ switch (nSourceMode)
+ {
+ case 0: //Source is Y,Z
+ if (aVolume.getHeight())
+ aTex.setX((aCandidate.getY() - aVolume.getMinY()) / aVolume.getHeight());
+ if (aVolume.getDepth())
+ aTex.setY((aCandidate.getZ() - aVolume.getMinZ()) / aVolume.getDepth());
+ break;
+
+ case 1: // Source is X,Z
+ if (aVolume.getWidth())
+ aTex.setX((aCandidate.getX() - aVolume.getMinX()) / aVolume.getWidth());
+ if (aVolume.getDepth())
+ aTex.setY((aCandidate.getZ() - aVolume.getMinZ()) / aVolume.getDepth());
+ break;
+
+ case 2: // Source is X,Y
+ if (aVolume.getWidth())
+ aTex.setX((aCandidate.getX() - aVolume.getMinX()) / aVolume.getWidth());
+ if (aVolume.getHeight())
+ aTex.setY((aCandidate.getY() - aVolume.getMinY()) / aVolume.getHeight());
+ break;
+ }
+
+ aTexture.append(aTex);
+ }
+
+ // Insert new polygon into the PolyPolygon
+ aPolyTexture.append(aTexture);
+ }
+
+ // Set default Texture coordinates
+ SetPolyTexture2D(aPolyTexture);
+}
+
+E3dPolygonObj::~E3dPolygonObj() {}
+
+SdrObjKind E3dPolygonObj::GetObjIdentifier() const { return SdrObjKind::E3D_Polygon; }
+
+void E3dPolygonObj::SetPolyPolygon3D(const basegfx::B3DPolyPolygon& rNewPolyPoly3D)
+{
+ if (aPolyPoly3D != rNewPolyPoly3D)
+ {
+ // New PolyPolygon; copying
+ aPolyPoly3D = rNewPolyPoly3D;
+
+ // Create new geometry
+ ActionChanged();
+ }
+}
+
+void E3dPolygonObj::SetPolyNormals3D(const basegfx::B3DPolyPolygon& rNewPolyNormals3D)
+{
+ if (aPolyNormals3D != rNewPolyNormals3D)
+ {
+ // New PolyPolygon; copying
+ aPolyNormals3D = rNewPolyNormals3D;
+
+ // Create new geometry
+ ActionChanged();
+ }
+}
+
+void E3dPolygonObj::SetPolyTexture2D(const basegfx::B2DPolyPolygon& rNewPolyTexture2D)
+{
+ if (aPolyTexture2D != rNewPolyTexture2D)
+ {
+ // New PolyPolygon; copying
+ aPolyTexture2D = rNewPolyTexture2D;
+
+ // Create new geometry
+ ActionChanged();
+ }
+}
+
+// Convert the object into a group object consisting of 6 polygons
+
+SdrObjectUniquePtr E3dPolygonObj::DoConvertToPolyObj(bool /*bBezier*/, bool /*bAddText*/) const
+{
+ return nullptr;
+}
+
+E3dPolygonObj* E3dPolygonObj::CloneSdrObject(SdrModel& rTargetModel) const
+{
+ return new E3dPolygonObj(rTargetModel, *this);
+}
+
+void E3dPolygonObj::SetLineOnly(bool bNew)
+{
+ if (bNew != bLineOnly)
+ {
+ bLineOnly = bNew;
+ ActionChanged();
+ }
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/engine3d/scene3d.cxx b/svx/source/engine3d/scene3d.cxx
new file mode 100644
index 000000000..07aa9c718
--- /dev/null
+++ b/svx/source/engine3d/scene3d.cxx
@@ -0,0 +1,898 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <sal/config.h>
+
+#include <cstdlib>
+
+#include <svx/strings.hrc>
+#include <svx/dialmgr.hxx>
+#include <svx/svditer.hxx>
+
+#include <svx/svdobjkind.hxx>
+#include <svx/svdpage.hxx>
+#include <svx/scene3d.hxx>
+#include <svx/svdtrans.hxx>
+#include <sdr/properties/e3dsceneproperties.hxx>
+#include <svx/sdr/contact/viewcontactofe3dscene.hxx>
+#include <svx/svddrag.hxx>
+#include "helperminimaldepth3d.hxx"
+#include <algorithm>
+#include <drawinglayer/geometry/viewinformation3d.hxx>
+#include <basegfx/polygon/b2dpolypolygontools.hxx>
+#include <svx/e3dsceneupdater.hxx>
+#include <svx/svdmodel.hxx>
+
+namespace {
+
+class ImpRemap3DDepth
+{
+ sal_uInt32 mnOrdNum;
+ double mfMinimalDepth;
+
+ // bit field
+ bool mbIsScene : 1;
+
+public:
+ ImpRemap3DDepth(sal_uInt32 nOrdNum, double fMinimalDepth);
+ explicit ImpRemap3DDepth(sal_uInt32 nOrdNum);
+
+ // for ::std::sort
+ bool operator<(const ImpRemap3DDepth& rComp) const;
+
+ sal_uInt32 GetOrdNum() const { return mnOrdNum; }
+ bool IsScene() const { return mbIsScene; }
+};
+
+}
+
+ImpRemap3DDepth::ImpRemap3DDepth(sal_uInt32 nOrdNum, double fMinimalDepth)
+: mnOrdNum(nOrdNum),
+ mfMinimalDepth(fMinimalDepth),
+ mbIsScene(false)
+{
+}
+
+ImpRemap3DDepth::ImpRemap3DDepth(sal_uInt32 nOrdNum)
+: mnOrdNum(nOrdNum),
+ mfMinimalDepth(0.0),
+ mbIsScene(true)
+{
+}
+
+bool ImpRemap3DDepth::operator<(const ImpRemap3DDepth& rComp) const
+{
+ if(IsScene())
+ {
+ return false;
+ }
+ else
+ {
+ if(rComp.IsScene())
+ {
+ return true;
+ }
+ else
+ {
+ return mfMinimalDepth < rComp.mfMinimalDepth;
+ }
+ }
+}
+
+class Imp3DDepthRemapper
+{
+ std::vector< ImpRemap3DDepth > maVector;
+
+public:
+ explicit Imp3DDepthRemapper(E3dScene const & rScene);
+
+ sal_uInt32 RemapOrdNum(sal_uInt32 nOrdNum) const;
+};
+
+Imp3DDepthRemapper::Imp3DDepthRemapper(E3dScene const & rScene)
+{
+ // only called when rScene.GetSubList() and nObjCount > 1
+ SdrObjList* pList = rScene.GetSubList();
+ const size_t nObjCount(pList->GetObjCount());
+
+ for(size_t a = 0; a < nObjCount; ++a)
+ {
+ SdrObject* pCandidate = pList->GetObj(a);
+
+ if(pCandidate)
+ {
+ if(auto pCompoundObj = dynamic_cast< const E3dCompoundObject*>(pCandidate))
+ {
+ // single 3d object, calc depth
+ const double fMinimalDepth(getMinimalDepthInViewCoordinates(*pCompoundObj));
+ ImpRemap3DDepth aEntry(a, fMinimalDepth);
+ maVector.push_back(aEntry);
+ }
+ else
+ {
+ // scene, use standard entry for scene
+ ImpRemap3DDepth aEntry(a);
+ maVector.push_back(aEntry);
+ }
+ }
+ }
+
+ // now, we need to sort the maVector by its members minimal depth. The
+ // smaller, the nearer to the viewer.
+ ::std::sort(maVector.begin(), maVector.end());
+}
+
+sal_uInt32 Imp3DDepthRemapper::RemapOrdNum(sal_uInt32 nOrdNum) const
+{
+ if(nOrdNum < maVector.size())
+ {
+ nOrdNum = maVector[(maVector.size() - 1) - nOrdNum].GetOrdNum();
+ }
+
+ return nOrdNum;
+}
+
+
+// BaseProperties section
+
+std::unique_ptr<sdr::properties::BaseProperties> E3dScene::CreateObjectSpecificProperties()
+{
+ return std::make_unique<sdr::properties::E3dSceneProperties>(*this);
+}
+
+
+// DrawContact section
+
+std::unique_ptr<sdr::contact::ViewContact> E3dScene::CreateObjectSpecificViewContact()
+{
+ return std::make_unique<sdr::contact::ViewContactOfE3dScene>(*this);
+}
+
+
+E3dScene::E3dScene(SdrModel& rSdrModel)
+: E3dObject(rSdrModel),
+ aCamera(basegfx::B3DPoint(0.0, 0.0, 4.0), basegfx::B3DPoint()),
+ bDrawOnlySelected(false),
+ mbSkipSettingDirty(false)
+{
+ // Set defaults
+ SetDefaultAttributes();
+}
+
+E3dScene::E3dScene(SdrModel& rSdrModel, E3dScene const & rSource)
+: E3dObject(rSdrModel, rSource),
+ aCamera(basegfx::B3DPoint(0.0, 0.0, 4.0), basegfx::B3DPoint()),
+ bDrawOnlySelected(false),
+ mbSkipSettingDirty(false)
+{
+ // Set defaults
+ SetDefaultAttributes();
+
+ // copy child SdrObjects
+ if (rSource.GetSubList())
+ {
+ CopyObjects(*rSource.GetSubList());
+
+ // tdf#116979: needed here, we need bSnapRectDirty to be true
+ // which it is after using SdrObject::operator= (see above),
+ // but set to false again using CopyObjects
+ SetBoundAndSnapRectsDirty();
+ }
+
+ // copy local data
+ aCamera = rSource.aCamera;
+ aCameraSet = rSource.aCameraSet;
+ static_cast<sdr::properties::E3dSceneProperties&>(GetProperties()).SetSceneItemsFromCamera();
+ InvalidateBoundVolume();
+ RebuildLists();
+ ImpCleanup3DDepthMapper();
+ GetViewContact().ActionChanged();
+}
+
+void E3dScene::SetDefaultAttributes()
+{
+ // For WIN95/NT turn off the FP-Exceptions
+#if defined(_WIN32)
+ _control87( _MCW_EM, _MCW_EM );
+#endif
+
+ // Set defaults
+ aCamera.SetViewWindow(-2, -2, 4, 4);
+ aCameraSet.SetDeviceRectangle(-2, 2, -2, 2);
+ aCamera.SetDeviceWindow(tools::Rectangle(0, 0, 10, 10));
+ tools::Rectangle aRect(0, 0, 10, 10);
+ aCameraSet.SetViewportRectangle(aRect);
+
+ // set defaults for Camera from ItemPool
+ aCamera.SetProjection(GetPerspective());
+ basegfx::B3DPoint aActualPosition(aCamera.GetPosition());
+ double fNew = GetDistance();
+
+ if(fabs(fNew - aActualPosition.getZ()) > 1.0)
+ {
+ aCamera.SetPosition( basegfx::B3DPoint( aActualPosition.getX(), aActualPosition.getY(), fNew) );
+ }
+
+ fNew = GetFocalLength() / 100.0;
+ aCamera.SetFocalLength(fNew);
+}
+
+E3dScene::~E3dScene()
+{
+ ImpCleanup3DDepthMapper();
+}
+
+SdrPage* E3dScene::getSdrPageFromSdrObjList() const
+{
+ return getSdrPageFromSdrObject();
+}
+
+SdrObject* E3dScene::getSdrObjectFromSdrObjList() const
+{
+ return const_cast< E3dScene* >(this);
+}
+
+SdrObjList* E3dScene::getChildrenOfSdrObject() const
+{
+ return const_cast< E3dScene* >(this);
+}
+
+basegfx::B2DPolyPolygon E3dScene::TakeXorPoly() const
+{
+ const sdr::contact::ViewContactOfE3dScene& rVCScene = static_cast< sdr::contact::ViewContactOfE3dScene& >(GetViewContact());
+ const drawinglayer::geometry::ViewInformation3D& aViewInfo3D(rVCScene.getViewInformation3D());
+ const basegfx::B3DPolyPolygon aCubePolyPolygon(CreateWireframe());
+
+ basegfx::B2DPolyPolygon aRetval(basegfx::utils::createB2DPolyPolygonFromB3DPolyPolygon(aCubePolyPolygon,
+ aViewInfo3D.getObjectToView()));
+ aRetval.transform(rVCScene.getObjectTransformation());
+
+ return aRetval;
+}
+
+void E3dScene::ImpCleanup3DDepthMapper()
+{
+ mp3DDepthRemapper.reset();
+}
+
+sal_uInt32 E3dScene::RemapOrdNum(sal_uInt32 nNewOrdNum) const
+{
+ if(!mp3DDepthRemapper)
+ {
+ const size_t nObjCount(GetSubList() ? GetSubList()->GetObjCount() : 0);
+
+ if(nObjCount > 1)
+ {
+ mp3DDepthRemapper.reset(new Imp3DDepthRemapper(*this));
+ }
+ }
+
+ if(mp3DDepthRemapper)
+ {
+ return mp3DDepthRemapper->RemapOrdNum(nNewOrdNum);
+ }
+
+ return nNewOrdNum;
+}
+
+SdrObjKind E3dScene::GetObjIdentifier() const
+{
+ return SdrObjKind::E3D_Scene;
+}
+
+void E3dScene::SetBoundRectDirty()
+{
+ E3dScene* pScene(getRootE3dSceneFromE3dObject());
+
+ if(pScene == this)
+ {
+ // avoid resetting aOutRect which in case of a 3D scene used as 2d object
+ // is model data,not re-creatable view data
+ }
+ else
+ {
+ // if not the outmost scene it is used as group in 3d, call parent
+ E3dObject::SetBoundRectDirty();
+ }
+}
+
+void E3dScene::NbcSetSnapRect(const tools::Rectangle& rRect)
+{
+ SetBoundAndSnapRectsDirty();
+ E3dObject::NbcSetSnapRect(rRect);
+ aCamera.SetDeviceWindow(rRect);
+ aCameraSet.SetViewportRectangle(rRect);
+
+ ImpCleanup3DDepthMapper();
+}
+
+void E3dScene::NbcMove(const Size& rSize)
+{
+ tools::Rectangle aNewSnapRect = GetSnapRect();
+ aNewSnapRect.Move(rSize);
+ NbcSetSnapRect(aNewSnapRect);
+}
+
+void E3dScene::NbcResize(const Point& rRef, const Fraction& rXFact,
+ const Fraction& rYFact)
+{
+ tools::Rectangle aNewSnapRect = GetSnapRect();
+ ResizeRect(aNewSnapRect, rRef, rXFact, rYFact);
+ NbcSetSnapRect(aNewSnapRect);
+}
+
+// Set new camera, and thus mark the scene and if possible the bound volume
+// as changed
+
+void E3dScene::SetCamera(const Camera3D& rNewCamera)
+{
+ aCamera = rNewCamera;
+ static_cast<sdr::properties::E3dSceneProperties&>(GetProperties()).SetSceneItemsFromCamera();
+
+ SetBoundAndSnapRectsDirty();
+
+ // Turn off ratio
+ GetCameraSet().SetRatio(0.0);
+
+ // Set Imaging geometry
+ basegfx::B3DPoint aVRP(aCamera.GetViewPoint());
+ basegfx::B3DVector aVPN(aVRP - aCamera.GetVRP());
+ basegfx::B3DVector aVUV(aCamera.GetVUV());
+
+ // use SetViewportValues() to set VRP, VPN and VUV as vectors, too.
+ // Else these values would not be exported/imported correctly.
+ GetCameraSet().SetViewportValues(aVRP, aVPN, aVUV);
+
+ // Set perspective
+ GetCameraSet().SetPerspective(aCamera.GetProjection() == ProjectionType::Perspective);
+ GetCameraSet().SetViewportRectangle(aCamera.GetDeviceWindow());
+
+ ImpCleanup3DDepthMapper();
+}
+
+// Inform parent of changes of a child
+
+void E3dScene::StructureChanged()
+{
+ E3dObject::StructureChanged();
+
+ E3dScene* pScene(getRootE3dSceneFromE3dObject());
+
+ if(nullptr != pScene && !pScene->mbSkipSettingDirty)
+ {
+ SetBoundAndSnapRectsDirty();
+ }
+
+ ImpCleanup3DDepthMapper();
+}
+
+// Determine the overall scene object
+
+E3dScene* E3dScene::getRootE3dSceneFromE3dObject() const
+{
+ E3dScene* pParent(getParentE3dSceneFromE3dObject());
+
+ if(nullptr != pParent)
+ {
+ return pParent->getRootE3dSceneFromE3dObject();
+ }
+
+ return const_cast< E3dScene* >(this);
+}
+
+void E3dScene::removeAllNonSelectedObjects()
+{
+ E3DModifySceneSnapRectUpdater aUpdater(this);
+
+ for(size_t a = 0; a < GetObjCount(); ++a)
+ {
+ SdrObject* pObj = GetObj(a);
+
+ if(pObj)
+ {
+ bool bRemoveObject(false);
+
+ if(auto pScene = dynamic_cast<E3dScene*>(pObj))
+ {
+ // iterate over this sub-scene
+ pScene->removeAllNonSelectedObjects();
+
+ // check object count. Empty scenes can be deleted
+ const size_t nObjCount(pScene->GetSubList() ? pScene->GetSubList()->GetObjCount() : 0);
+
+ if(!nObjCount)
+ {
+ // all objects removed, scene can be removed, too
+ bRemoveObject = true;
+ }
+ }
+ else if(auto pCompound = dynamic_cast<E3dCompoundObject*>(pObj))
+ {
+ if(!pCompound->GetSelected())
+ {
+ bRemoveObject = true;
+ }
+ }
+
+ if(bRemoveObject)
+ {
+ NbcRemoveObject(pObj->GetOrdNum());
+ a--;
+ SdrObject::Free(pObj);
+ }
+ }
+ }
+}
+
+E3dScene* E3dScene::CloneSdrObject(SdrModel& rTargetModel) const
+{
+ return new E3dScene(rTargetModel, *this);
+}
+
+void E3dScene::SuspendReportingDirtyRects()
+{
+ E3dScene* pScene(getRootE3dSceneFromE3dObject());
+
+ if(nullptr != pScene)
+ {
+ pScene->mbSkipSettingDirty = true;
+ }
+}
+
+void E3dScene::ResumeReportingDirtyRects()
+{
+ E3dScene* pScene(getRootE3dSceneFromE3dObject());
+
+ if(nullptr != pScene)
+ {
+ pScene->mbSkipSettingDirty = false;
+ }
+}
+
+void E3dScene::SetAllSceneRectsDirty()
+{
+ E3dScene* pScene(getRootE3dSceneFromE3dObject());
+
+ if(nullptr != pScene)
+ {
+ pScene->SetBoundAndSnapRectsDirty();
+ }
+}
+
+// Rebuild Light- and label- object lists rebuild (after loading, allocation)
+
+void E3dScene::RebuildLists()
+{
+ // first delete
+ const SdrLayerID nCurrLayerID(GetLayer());
+ SdrObjListIter a3DIterator(GetSubList(), SdrIterMode::Flat);
+
+ // then examine all the objects in the scene
+ while(a3DIterator.IsMore())
+ {
+ E3dObject* p3DObj(static_cast< E3dObject* >(a3DIterator.Next()));
+ p3DObj->NbcSetLayer(nCurrLayerID);
+ }
+
+ ImpCleanup3DDepthMapper();
+}
+
+std::unique_ptr<SdrObjGeoData> E3dScene::NewGeoData() const
+{
+ return std::make_unique<E3DSceneGeoData>();
+}
+
+void E3dScene::SaveGeoData(SdrObjGeoData& rGeo) const
+{
+ E3dObject::SaveGeoData (rGeo);
+
+ static_cast<E3DSceneGeoData &>(rGeo).aCamera = aCamera;
+}
+
+void E3dScene::RestoreGeoData(const SdrObjGeoData& rGeo)
+{
+ // #i94832# removed E3DModifySceneSnapRectUpdater here.
+ // It should not be needed, is already part of E3dObject::RestoreGeoData
+ E3dObject::RestoreGeoData (rGeo);
+ SetCamera (static_cast<const E3DSceneGeoData &>(rGeo).aCamera);
+}
+
+// Something was changed in the style sheet, so change scene
+
+void E3dScene::Notify(SfxBroadcaster &rBC, const SfxHint &rHint)
+{
+ SetBoundAndSnapRectsDirty();
+ E3dObject::Notify(rBC, rHint);
+}
+
+void E3dScene::RotateScene (const Point& rRef, double sn, double cs)
+{
+ Point UpperLeft, LowerRight, Center, NewCenter;
+
+ UpperLeft = m_aOutRect.TopLeft();
+ LowerRight = m_aOutRect.BottomRight();
+
+ tools::Long dxOutRectHalf = std::abs(UpperLeft.X() - LowerRight.X());
+ dxOutRectHalf /= 2;
+ tools::Long dyOutRectHalf = std::abs(UpperLeft.Y() - LowerRight.Y());
+ dyOutRectHalf /= 2;
+
+ // Only the center is moved. The corners are moved by NbcMove. For the
+ // rotation a cartesian coordinate system is used in which the pivot
+ // point is the origin, and the y-axis increases upward, the X-axis to
+ // the right. This must be especially noted for the Y-values.
+ // (When considering a flat piece of paper the Y-axis pointing downwards
+ Center.setX( (UpperLeft.X() + dxOutRectHalf) - rRef.X() );
+ Center.setY( -((UpperLeft.Y() + dyOutRectHalf) - rRef.Y()) );
+ // A few special cases has to be dealt with first (n * 90 degrees n integer)
+ if (sn==1.0 && cs==0.0) { // 90deg
+ NewCenter.setX( -Center.Y() );
+ NewCenter.setY( -Center.X() );
+ } else if (sn==0.0 && cs==-1.0) { // 180deg
+ NewCenter.setX( -Center.X() );
+ NewCenter.setY( -Center.Y() );
+ } else if (sn==-1.0 && cs==0.0) { // 270deg
+ NewCenter.setX( Center.Y() );
+ NewCenter.setY( -Center.X() );
+ }
+ else // Here it is rotated to any angle in the mathematically
+ // positive direction!
+ { // xnew = x * cos(alpha) - y * sin(alpha)
+ // ynew = x * sin(alpha) + y * cos(alpha)
+ // Bottom Right is not rotated: the pages of aOutRect must
+ // remain parallel to the coordinate axes.
+ NewCenter.setX( static_cast<tools::Long>(Center.X() * cs - Center.Y() * sn) );
+ NewCenter.setY( static_cast<tools::Long>(Center.X() * sn + Center.Y() * cs) );
+ }
+
+ Size Differenz;
+ Point DiffPoint = NewCenter - Center;
+ Differenz.setWidth( DiffPoint.X() );
+ Differenz.setHeight( -DiffPoint.Y() ); // Note that the Y-axis is counted ad positive downward.
+ NbcMove (Differenz); // Actually executes the coordinate transformation.
+}
+
+OUString E3dScene::TakeObjNameSingul() const
+{
+ OUString sName(SvxResId(STR_ObjNameSingulScene3d));
+
+ OUString aName(GetName());
+ if (!aName.isEmpty())
+ sName += " '" + aName + "'";
+ return sName;
+}
+
+OUString E3dScene::TakeObjNamePlural() const
+{
+ return SvxResId(STR_ObjNamePluralScene3d);
+}
+
+// The NbcRotate routine overrides the one of the SdrObject. The idea is
+// to be able to rotate the scene relative to the position of the scene
+// and then the objects in the scene
+
+void E3dScene::NbcSetTransform(const basegfx::B3DHomMatrix& rMatrix)
+{
+ if(maTransformation != rMatrix)
+ {
+ // call parent
+ E3dObject::NbcSetTransform(rMatrix);
+ }
+}
+
+void E3dScene::SetTransform(const basegfx::B3DHomMatrix& rMatrix)
+{
+ if(rMatrix != maTransformation)
+ {
+ // call parent
+ E3dObject::SetTransform(rMatrix);
+ }
+}
+
+void E3dScene::NbcRotate(const Point& rRef, Degree100 nAngle, double sn, double cs)
+{
+ // So currently the gluepoints are defined relative to the scene aOutRect.
+ // Before turning the gluepoints are defined relative to the page. They
+ // take no part in the rotation of the scene. To ensure this, there is the
+ // SetGlueReallyAbsolute(sal_True);
+
+ // So that was the scene, now the objects used in the scene
+ // 3D objects, if there is only one it can still have multiple surfaces but
+ // the surfaces do not have to be connected. This allows you to access child
+ // objects. So going through the entire list and rotate around the Z axis
+ // through the enter of aOutRect's (Steiner's theorem), so RotateZ
+
+ RotateScene (rRef, sn, cs); // Rotates the scene
+ double fAngleInRad = toRadians(nAngle);
+
+ basegfx::B3DHomMatrix aRotation;
+ aRotation.rotate(0.0, 0.0, fAngleInRad);
+ NbcSetTransform(aRotation * GetTransform());
+
+ SetBoundAndSnapRectsDirty(); // This forces a recalculation of all BoundRects
+ NbcRotateGluePoints(rRef,nAngle,sn,cs); // Rotate the gluepoints (who still
+ // have coordinates relative to the
+ // original page)
+ SetGlueReallyAbsolute(false); // from now they are again relative to BoundRect (that is defined as aOutRect)
+ SetBoundAndSnapRectsDirty();
+}
+
+void E3dScene::RecalcSnapRect()
+{
+ E3dScene* pScene(getRootE3dSceneFromE3dObject());
+
+ if(pScene == this)
+ {
+ // The Scene is used as a 2D-Object, take the SnapRect from the
+ // 2D Display settings
+ maSnapRect = pScene->aCamera.GetDeviceWindow();
+ }
+ else
+ {
+ // The Scene itself is a member of another scene, get the SnapRect
+ // as a composite object
+ // call parent
+ E3dObject::RecalcSnapRect();
+
+ for(size_t a = 0; a < GetObjCount(); ++a)
+ {
+ E3dObject* pCandidate(dynamic_cast< E3dObject* >(GetObj(a)));
+
+ if(pCandidate)
+ {
+ maSnapRect.Union(pCandidate->GetSnapRect());
+ }
+ }
+ }
+}
+
+bool E3dScene::IsBreakObjPossible()
+{
+ // Break scene, if all members are able to break
+ SdrObjListIter a3DIterator(GetSubList(), SdrIterMode::DeepWithGroups);
+
+ while ( a3DIterator.IsMore() )
+ {
+ E3dObject* pObj = static_cast<E3dObject*>(a3DIterator.Next());
+ if(!pObj->IsBreakObjPossible())
+ return false;
+ }
+
+ return true;
+}
+
+basegfx::B2DPolyPolygon E3dScene::TakeCreatePoly(const SdrDragStat& /*rDrag*/) const
+{
+ return TakeXorPoly();
+}
+
+bool E3dScene::BegCreate(SdrDragStat& rStat)
+{
+ rStat.SetOrtho4Possible();
+ tools::Rectangle aRect1(rStat.GetStart(), rStat.GetNow());
+ aRect1.Justify();
+ rStat.SetActionRect(aRect1);
+ NbcSetSnapRect(aRect1);
+ return true;
+}
+
+bool E3dScene::MovCreate(SdrDragStat& rStat)
+{
+ tools::Rectangle aRect1;
+ rStat.TakeCreateRect(aRect1);
+ aRect1.Justify();
+ rStat.SetActionRect(aRect1);
+ NbcSetSnapRect(aRect1);
+ SetBoundRectDirty();
+ m_bSnapRectDirty=true;
+ return true;
+}
+
+bool E3dScene::EndCreate(SdrDragStat& rStat, SdrCreateCmd eCmd)
+{
+ tools::Rectangle aRect1;
+ rStat.TakeCreateRect(aRect1);
+ aRect1.Justify();
+ NbcSetSnapRect(aRect1);
+ SetBoundAndSnapRectsDirty();
+ return (eCmd==SdrCreateCmd::ForceEnd || rStat.GetPointCount()>=2);
+}
+
+bool E3dScene::BckCreate(SdrDragStat& /*rStat*/)
+{
+ return false;
+}
+
+void E3dScene::BrkCreate(SdrDragStat& /*rStat*/)
+{
+}
+
+void E3dScene::SetSelected(bool bNew)
+{
+ // call parent
+ E3dObject::SetSelected(bNew);
+
+ for(size_t a(0); a < GetObjCount(); a++)
+ {
+ E3dObject* pCandidate(dynamic_cast< E3dObject* >(GetObj(a)));
+
+ if(pCandidate)
+ {
+ pCandidate->SetSelected(bNew);
+ }
+ }
+}
+
+void E3dScene::NbcInsertObject(SdrObject* pObj, size_t nPos)
+{
+ // Is it even a 3D object?
+ if(nullptr != dynamic_cast< const E3dObject* >(pObj))
+ {
+ // Normal 3D object, insert means call parent
+ SdrObjList::NbcInsertObject(pObj, nPos);
+
+ // local needed stuff
+ InvalidateBoundVolume();
+ StructureChanged();
+ }
+ else
+ {
+ // No 3D object, inserted a page in place in a scene ...
+ getSdrObjectFromSdrObjList()->getSdrPageFromSdrObject()->InsertObject(pObj, nPos);
+ }
+}
+
+void E3dScene::InsertObject(SdrObject* pObj, size_t nPos)
+{
+ // Is it even a 3D object?
+ if(nullptr != dynamic_cast< const E3dObject* >(pObj))
+ {
+ // call parent
+ SdrObjList::InsertObject(pObj, nPos);
+
+ // local needed stuff
+ InvalidateBoundVolume();
+ StructureChanged();
+ }
+ else
+ {
+ // No 3D object, inserted a page in place in a scene ...
+ getSdrObjectFromSdrObjList()->getSdrPageFromSdrObject()->InsertObject(pObj, nPos);
+ }
+}
+
+SdrObject* E3dScene::NbcRemoveObject(size_t nObjNum)
+{
+ // call parent
+ SdrObject* pRetval = SdrObjList::NbcRemoveObject(nObjNum);
+
+ InvalidateBoundVolume();
+ StructureChanged();
+
+ return pRetval;
+}
+
+SdrObject* E3dScene::RemoveObject(size_t nObjNum)
+{
+ // call parent
+ SdrObject* pRetval(SdrObjList::RemoveObject(nObjNum));
+
+ InvalidateBoundVolume();
+ StructureChanged();
+
+ return pRetval;
+}
+
+void E3dScene::SetBoundAndSnapRectsDirty(bool bNotMyself, bool bRecursive)
+{
+ // call parent
+ E3dObject::SetBoundAndSnapRectsDirty(bNotMyself, bRecursive);
+
+ for(size_t a = 0; a < GetObjCount(); ++a)
+ {
+ E3dObject* pCandidate = dynamic_cast< E3dObject* >(GetObj(a));
+
+ if(pCandidate)
+ {
+ pCandidate->SetBoundAndSnapRectsDirty(bNotMyself, false);
+ }
+ }
+}
+
+void E3dScene::NbcSetLayer(SdrLayerID nLayer)
+{
+ // call parent
+ E3dObject::NbcSetLayer(nLayer);
+
+ for(size_t a = 0; a < GetObjCount(); ++a)
+ {
+ E3dObject* pCandidate = dynamic_cast< E3dObject* >(GetObj(a));
+
+ if(pCandidate)
+ {
+ pCandidate->NbcSetLayer(nLayer);
+ }
+ }
+}
+
+void E3dScene::handlePageChange(SdrPage* pOldPage, SdrPage* pNewPage)
+{
+ if(pOldPage == pNewPage)
+ return;
+
+ // call parent
+ E3dObject::handlePageChange(pOldPage, pNewPage);
+
+ for(size_t a(0); a < GetObjCount(); a++)
+ {
+ E3dObject* pCandidate = dynamic_cast< E3dObject* >(GetObj(a));
+
+ if(pCandidate)
+ {
+ pCandidate->handlePageChange(pOldPage, pNewPage);
+ }
+ else
+ {
+ OSL_ENSURE(false, "E3dScene::handlePageChange invalid object list (!)");
+ }
+ }
+}
+
+SdrObjList* E3dScene::GetSubList() const
+{
+ return const_cast< E3dScene* >(this);
+}
+
+basegfx::B3DRange E3dScene::RecalcBoundVolume() const
+{
+ basegfx::B3DRange aRetval;
+ const size_t nObjCnt(GetObjCount());
+
+ for(size_t a = 0; a < nObjCnt; ++a)
+ {
+ const E3dObject* p3DObject = dynamic_cast< const E3dObject* >(GetObj(a));
+
+ if(p3DObject)
+ {
+ basegfx::B3DRange aLocalRange(p3DObject->GetBoundVolume());
+ aLocalRange.transform(p3DObject->GetTransform());
+ aRetval.expand(aLocalRange);
+ }
+ }
+
+ return aRetval;
+}
+
+void E3dScene::SetTransformChanged()
+{
+ // call parent
+ E3dObject::SetTransformChanged();
+
+ for(size_t a = 0; a < GetObjCount(); ++a)
+ {
+ E3dObject* pCandidate = dynamic_cast< E3dObject* >(GetObj(a));
+
+ if(pCandidate)
+ {
+ pCandidate->SetTransformChanged();
+ }
+ }
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/engine3d/sphere3d.cxx b/svx/source/engine3d/sphere3d.cxx
new file mode 100644
index 000000000..cac250907
--- /dev/null
+++ b/svx/source/engine3d/sphere3d.cxx
@@ -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 .
+ */
+
+
+#include <svx/strings.hrc>
+#include <svx/deflt3d.hxx>
+#include <svx/dialmgr.hxx>
+#include <svx/svdmodel.hxx>
+#include <svx/svdobjkind.hxx>
+#include <svx/sphere3d.hxx>
+
+#include <sdr/properties/e3dsphereproperties.hxx>
+#include <basegfx/vector/b3dvector.hxx>
+#include <basegfx/point/b3dpoint.hxx>
+#include <sdr/contact/viewcontactofe3dsphere.hxx>
+
+// DrawContact section
+std::unique_ptr<sdr::contact::ViewContact> E3dSphereObj::CreateObjectSpecificViewContact()
+{
+ return std::make_unique<sdr::contact::ViewContactOfE3dSphere>(*this);
+}
+
+std::unique_ptr<sdr::properties::BaseProperties> E3dSphereObj::CreateObjectSpecificProperties()
+{
+ return std::make_unique<sdr::properties::E3dSphereProperties>(*this);
+}
+
+// Build Sphere from polygon facets in latitude and longitude
+E3dSphereObj::E3dSphereObj(
+ SdrModel& rSdrModel,
+ const E3dDefaultAttributes& rDefault,
+ const basegfx::B3DPoint& rCenter,
+ const basegfx::B3DVector& r3DSize)
+: E3dCompoundObject(rSdrModel)
+{
+ // Set defaults
+ SetDefaultAttributes(rDefault);
+
+ aCenter = rCenter;
+ aSize = r3DSize;
+}
+
+E3dSphereObj::E3dSphereObj(SdrModel& rSdrModel)
+: E3dCompoundObject(rSdrModel)
+{
+ // Set defaults
+ const E3dDefaultAttributes aDefault;
+
+ SetDefaultAttributes(aDefault);
+}
+
+E3dSphereObj::E3dSphereObj(SdrModel& rSdrModel, E3dSphereObj const & rSource)
+: E3dCompoundObject(rSdrModel, rSource)
+{
+ // Set defaults
+ const E3dDefaultAttributes aDefault;
+ SetDefaultAttributes(aDefault);
+
+ aCenter = rSource.aCenter;
+ aSize = rSource.aSize;
+}
+
+E3dSphereObj::~E3dSphereObj()
+{
+}
+
+void E3dSphereObj::SetDefaultAttributes(const E3dDefaultAttributes& rDefault)
+{
+ // Set defaults
+ aCenter = rDefault.GetDefaultSphereCenter();
+ aSize = rDefault.GetDefaultSphereSize();
+}
+
+SdrObjKind E3dSphereObj::GetObjIdentifier() const
+{
+ return SdrObjKind::E3D_Sphere;
+}
+
+// Convert the object into a group object consisting of n polygons
+
+SdrObjectUniquePtr E3dSphereObj::DoConvertToPolyObj(bool /*bBezier*/, bool /*bAddText*/) const
+{
+ return nullptr;
+}
+
+E3dSphereObj* E3dSphereObj::CloneSdrObject(SdrModel& rTargetModel) const
+{
+ return new E3dSphereObj(rTargetModel, *this);
+}
+
+// Set local parameters with geometry re-creating
+
+void E3dSphereObj::SetCenter(const basegfx::B3DPoint& rNew)
+{
+ if(aCenter != rNew)
+ {
+ aCenter = rNew;
+ ActionChanged();
+ }
+}
+
+void E3dSphereObj::SetSize(const basegfx::B3DVector& rNew)
+{
+ if(aSize != rNew)
+ {
+ aSize = rNew;
+ ActionChanged();
+ }
+}
+
+// Get the name of the object (singular)
+
+OUString E3dSphereObj::TakeObjNameSingul() const
+{
+ OUString sName(SvxResId(STR_ObjNameSingulSphere3d));
+
+ OUString aName(GetName());
+ if (!aName.isEmpty())
+ {
+ sName += " '" + aName + "'";
+ }
+ return sName;
+}
+
+// Get the name of the object (plural)
+
+OUString E3dSphereObj::TakeObjNamePlural() const
+{
+ return SvxResId(STR_ObjNamePluralSphere3d);
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/engine3d/svx3ditems.cxx b/svx/source/engine3d/svx3ditems.cxx
new file mode 100644
index 000000000..68f31b120
--- /dev/null
+++ b/svx/source/engine3d/svx3ditems.cxx
@@ -0,0 +1,277 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <svx/svx3ditems.hxx>
+#include <com/sun/star/drawing/NormalsKind.hpp>
+#include <com/sun/star/drawing/TextureProjectionMode.hpp>
+#include <com/sun/star/drawing/TextureKind.hpp>
+#include <com/sun/star/drawing/TextureMode.hpp>
+#include <com/sun/star/drawing/ProjectionMode.hpp>
+#include <com/sun/star/drawing/ShadeMode.hpp>
+
+using namespace ::com::sun::star;
+
+// #i28528#
+// Added extra Item (Bool) for chart2 to be able to show reduced line geometry
+
+Svx3DReducedLineGeometryItem::Svx3DReducedLineGeometryItem(bool bVal)
+ : SfxBoolItem(SDRATTR_3DOBJ_REDUCED_LINE_GEOMETRY, bVal)
+{
+}
+
+Svx3DReducedLineGeometryItem* Svx3DReducedLineGeometryItem::Clone(SfxItemPool*) const
+{
+ return new Svx3DReducedLineGeometryItem(*this);
+}
+
+Svx3DNormalsKindItem::Svx3DNormalsKindItem(sal_uInt16 nVal)
+ : SfxUInt16Item(SDRATTR_3DOBJ_NORMALS_KIND, nVal)
+{
+}
+
+Svx3DTextureProjectionXItem::Svx3DTextureProjectionXItem(sal_uInt16 nVal)
+ : SfxUInt16Item(SDRATTR_3DOBJ_TEXTURE_PROJ_X, nVal)
+{
+}
+
+Svx3DTextureProjectionYItem::Svx3DTextureProjectionYItem(sal_uInt16 nVal)
+ : SfxUInt16Item(SDRATTR_3DOBJ_TEXTURE_PROJ_Y, nVal)
+{
+}
+
+Svx3DTextureKindItem::Svx3DTextureKindItem(sal_uInt16 nVal)
+ : SfxUInt16Item(SDRATTR_3DOBJ_TEXTURE_KIND, nVal)
+{
+}
+
+Svx3DTextureModeItem::Svx3DTextureModeItem(sal_uInt16 nVal)
+ : SfxUInt16Item(SDRATTR_3DOBJ_TEXTURE_MODE, nVal)
+{
+}
+
+Svx3DPerspectiveItem::Svx3DPerspectiveItem(ProjectionType nVal)
+ : SfxUInt16Item(SDRATTR_3DSCENE_PERSPECTIVE, static_cast<sal_uInt16>(nVal))
+{
+}
+
+Svx3DShadeModeItem::Svx3DShadeModeItem(sal_uInt16 nVal)
+ : SfxUInt16Item(SDRATTR_3DSCENE_SHADE_MODE, nVal)
+{
+}
+
+Svx3DSmoothNormalsItem::Svx3DSmoothNormalsItem(bool bVal)
+ : SfxBoolItem(SDRATTR_3DOBJ_SMOOTH_NORMALS, bVal)
+{
+}
+
+Svx3DSmoothNormalsItem* Svx3DSmoothNormalsItem::Clone(SfxItemPool*) const
+{
+ return new Svx3DSmoothNormalsItem(*this);
+}
+
+Svx3DSmoothLidsItem::Svx3DSmoothLidsItem(bool bVal)
+ : SfxBoolItem(SDRATTR_3DOBJ_SMOOTH_LIDS, bVal)
+{
+}
+
+Svx3DSmoothLidsItem* Svx3DSmoothLidsItem::Clone(SfxItemPool*) const
+{
+ return new Svx3DSmoothLidsItem(*this);
+}
+
+Svx3DCharacterModeItem::Svx3DCharacterModeItem(bool bVal)
+ : SfxBoolItem(SDRATTR_3DOBJ_CHARACTER_MODE, bVal)
+{
+}
+
+Svx3DCharacterModeItem* Svx3DCharacterModeItem::Clone(SfxItemPool*) const
+{
+ return new Svx3DCharacterModeItem(*this);
+}
+
+Svx3DCloseFrontItem::Svx3DCloseFrontItem(bool bVal)
+ : SfxBoolItem(SDRATTR_3DOBJ_CLOSE_FRONT, bVal)
+{
+}
+
+Svx3DCloseFrontItem* Svx3DCloseFrontItem::Clone(SfxItemPool*) const
+{
+ return new Svx3DCloseFrontItem(*this);
+}
+
+Svx3DCloseBackItem::Svx3DCloseBackItem(bool bVal)
+ : SfxBoolItem(SDRATTR_3DOBJ_CLOSE_BACK, bVal)
+{
+}
+
+Svx3DCloseBackItem* Svx3DCloseBackItem::Clone(SfxItemPool*) const
+{
+ return new Svx3DCloseBackItem(*this);
+}
+
+// Svx3DNormalsKindItem: use drawing::NormalsKind
+bool Svx3DNormalsKindItem::QueryValue(uno::Any& rVal, sal_uInt8 /*nMemberId*/) const
+{
+ rVal <<= static_cast<drawing::NormalsKind>(GetValue());
+ return true;
+}
+
+bool Svx3DNormalsKindItem::PutValue(const uno::Any& rVal, sal_uInt8 /*nMemberId*/)
+{
+ drawing::NormalsKind eVar;
+ if (!(rVal >>= eVar))
+ return false;
+ SetValue(static_cast<sal_Int16>(eVar));
+ return true;
+}
+
+Svx3DNormalsKindItem* Svx3DNormalsKindItem::Clone(SfxItemPool* /*pPool*/) const
+{
+ return new Svx3DNormalsKindItem(*this);
+}
+
+// Svx3DTextureProjectionXItem: use drawing::TextureProjectionMode
+bool Svx3DTextureProjectionXItem::QueryValue(uno::Any& rVal, sal_uInt8 /*nMemberId*/) const
+{
+ rVal <<= static_cast<drawing::TextureProjectionMode>(GetValue());
+ return true;
+}
+
+bool Svx3DTextureProjectionXItem::PutValue(const uno::Any& rVal, sal_uInt8 /*nMemberId*/)
+{
+ drawing::TextureProjectionMode eVar;
+ if (!(rVal >>= eVar))
+ return false;
+ SetValue(static_cast<sal_Int16>(eVar));
+ return true;
+}
+
+Svx3DTextureProjectionXItem* Svx3DTextureProjectionXItem::Clone(SfxItemPool* /*pPool*/) const
+{
+ return new Svx3DTextureProjectionXItem(*this);
+}
+
+// Svx3DTextureProjectionYItem: use drawing::TextureProjectionMode
+bool Svx3DTextureProjectionYItem::QueryValue(uno::Any& rVal, sal_uInt8 /*nMemberId*/) const
+{
+ rVal <<= static_cast<drawing::TextureProjectionMode>(GetValue());
+ return true;
+}
+
+bool Svx3DTextureProjectionYItem::PutValue(const uno::Any& rVal, sal_uInt8 /*nMemberId*/)
+{
+ drawing::TextureProjectionMode eVar;
+ if (!(rVal >>= eVar))
+ return false;
+ SetValue(static_cast<sal_Int16>(eVar));
+ return true;
+}
+
+Svx3DTextureProjectionYItem* Svx3DTextureProjectionYItem::Clone(SfxItemPool* /*pPool*/) const
+{
+ return new Svx3DTextureProjectionYItem(*this);
+}
+
+// Svx3DTextureKindItem: use drawing::TextureKind
+bool Svx3DTextureKindItem::QueryValue(uno::Any& rVal, sal_uInt8 /*nMemberId*/) const
+{
+ rVal <<= static_cast<drawing::TextureKind>(GetValue());
+ return true;
+}
+
+bool Svx3DTextureKindItem::PutValue(const uno::Any& rVal, sal_uInt8 /*nMemberId*/)
+{
+ drawing::TextureKind eVar;
+ if (!(rVal >>= eVar))
+ return false;
+ SetValue(static_cast<sal_Int16>(eVar));
+ return true;
+}
+
+Svx3DTextureKindItem* Svx3DTextureKindItem::Clone(SfxItemPool* /*pPool*/) const
+{
+ return new Svx3DTextureKindItem(*this);
+}
+
+// Svx3DTextureModeItem: use drawing:TextureMode
+bool Svx3DTextureModeItem::QueryValue(uno::Any& rVal, sal_uInt8 /*nMemberId*/) const
+{
+ rVal <<= static_cast<drawing::TextureMode>(GetValue());
+ return true;
+}
+
+bool Svx3DTextureModeItem::PutValue(const uno::Any& rVal, sal_uInt8 /*nMemberId*/)
+{
+ drawing::TextureMode eVar;
+ if (!(rVal >>= eVar))
+ return false;
+ SetValue(static_cast<sal_Int16>(eVar));
+ return true;
+}
+
+Svx3DTextureModeItem* Svx3DTextureModeItem::Clone(SfxItemPool* /*pPool*/) const
+{
+ return new Svx3DTextureModeItem(*this);
+}
+
+// Svx3DPerspectiveItem: use drawing::ProjectionMode
+bool Svx3DPerspectiveItem::QueryValue(uno::Any& rVal, sal_uInt8 /*nMemberId*/) const
+{
+ rVal <<= static_cast<drawing::ProjectionMode>(GetValue());
+ return true;
+}
+
+bool Svx3DPerspectiveItem::PutValue(const uno::Any& rVal, sal_uInt8 /*nMemberId*/)
+{
+ drawing::ProjectionMode eVar;
+ if (!(rVal >>= eVar))
+ return false;
+ SetValue(static_cast<sal_Int16>(eVar));
+ return true;
+}
+
+Svx3DPerspectiveItem* Svx3DPerspectiveItem::Clone(SfxItemPool* /*pPool*/) const
+{
+ return new Svx3DPerspectiveItem(*this);
+}
+
+// Svx3DShadeModeItem: use drawing::ShadeMode
+bool Svx3DShadeModeItem::QueryValue(uno::Any& rVal, sal_uInt8 /*nMemberId*/) const
+{
+ rVal <<= static_cast<drawing::ShadeMode>(GetValue());
+ return true;
+}
+
+bool Svx3DShadeModeItem::PutValue(const uno::Any& rVal, sal_uInt8 /*nMemberId*/)
+{
+ drawing::ShadeMode eVar;
+ if (!(rVal >>= eVar))
+ return false;
+ SetValue(static_cast<sal_Int16>(eVar));
+ return true;
+}
+
+Svx3DShadeModeItem* Svx3DShadeModeItem::Clone(SfxItemPool* /*pPool*/) const
+{
+ return new Svx3DShadeModeItem(*this);
+}
+
+// EOF
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/engine3d/view3d.cxx b/svx/source/engine3d/view3d.cxx
new file mode 100644
index 000000000..e5caf5355
--- /dev/null
+++ b/svx/source/engine3d/view3d.cxx
@@ -0,0 +1,1593 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * 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/svdopath.hxx>
+#include <svx/svditer.hxx>
+#include <svx/svdmodel.hxx>
+#include <svx/svdpagv.hxx>
+#include <editeng/colritem.hxx>
+#include <editeng/eeitem.hxx>
+#include <svx/svdview.hxx>
+#include <svx/strings.hrc>
+#include <svx/dialmgr.hxx>
+#include <svx/obj3d.hxx>
+#include <svx/lathe3d.hxx>
+#include <extrud3d.hxx>
+#include <dragmt3d.hxx>
+#include <svx/scene3d.hxx>
+#include <svx/view3d.hxx>
+#include <svx/svdundo.hxx>
+#include <svx/xflclit.hxx>
+#include <svx/xlnclit.hxx>
+#include <svx/xfillit0.hxx>
+#include <svx/xlineit0.hxx>
+#include <basegfx/range/b2drange.hxx>
+#include <basegfx/polygon/b2dpolypolygontools.hxx>
+#include <svx/xlnwtit.hxx>
+#include <svx/sdr/overlay/overlaypolypolygon.hxx>
+#include <svx/sdr/overlay/overlaymanager.hxx>
+#include <svx/sdrpaintwindow.hxx>
+#include <svx/sdr/contact/viewcontact.hxx>
+#include <drawinglayer/primitive2d/unifiedtransparenceprimitive2d.hxx>
+#include <svx/sdr/overlay/overlayprimitive2dsequenceobject.hxx>
+#include <drawinglayer/primitive2d/transformprimitive2d.hxx>
+#include <basegfx/matrix/b2dhommatrixtools.hxx>
+#include <basegfx/polygon/b2dpolypolygoncutter.hxx>
+#include <svx/e3dsceneupdater.hxx>
+
+using namespace com::sun::star;
+
+
+// Migrate Marking
+
+class Impl3DMirrorConstructOverlay
+{
+ // The OverlayObjects
+ sdr::overlay::OverlayObjectList maObjects;
+
+ // the view
+ const E3dView& mrView;
+
+ // the object count
+ size_t mnCount;
+
+ // the unmirrored polygons
+ basegfx::B2DPolyPolygon* mpPolygons;
+
+ // the overlay geometry from selected objects
+ drawinglayer::primitive2d::Primitive2DContainer maFullOverlay;
+
+ // Copy assignment is forbidden and not implemented.
+ Impl3DMirrorConstructOverlay (const Impl3DMirrorConstructOverlay &) = delete;
+ Impl3DMirrorConstructOverlay & operator= (const Impl3DMirrorConstructOverlay &) = delete;
+
+public:
+ explicit Impl3DMirrorConstructOverlay(const E3dView& rView);
+ ~Impl3DMirrorConstructOverlay();
+
+ void SetMirrorAxis(Point aMirrorAxisA, Point aMirrorAxisB);
+};
+
+Impl3DMirrorConstructOverlay::Impl3DMirrorConstructOverlay(const E3dView& rView)
+: mrView(rView),
+ mnCount(rView.GetMarkedObjectCount()),
+ mpPolygons(nullptr)
+{
+ if(!mnCount)
+ return;
+
+ if(mrView.IsSolidDragging())
+ {
+ SdrPageView* pPV = rView.GetSdrPageView();
+
+ if(pPV && pPV->PageWindowCount())
+ {
+ for(size_t a = 0; a < mnCount; ++a)
+ {
+ SdrObject* pObject = mrView.GetMarkedObjectByIndex(a);
+
+ if(pObject)
+ {
+ // use the view-independent primitive representation (without
+ // evtl. GridOffset, that may be applied to the DragEntry individually)
+ pObject->GetViewContact().getViewIndependentPrimitive2DContainer(maFullOverlay);
+ }
+ }
+ }
+ }
+ else
+ {
+ mpPolygons = new basegfx::B2DPolyPolygon[mnCount];
+
+ for(size_t a = 0; a < mnCount; ++a)
+ {
+ SdrObject* pObject = mrView.GetMarkedObjectByIndex(a);
+ mpPolygons[mnCount - (a + 1)] = pObject->TakeXorPoly();
+ }
+ }
+}
+
+Impl3DMirrorConstructOverlay::~Impl3DMirrorConstructOverlay()
+{
+ // The OverlayObjects are cleared using the destructor of OverlayObjectList.
+ // That destructor calls clear() at the list which removes all objects from the
+ // OverlayManager and deletes them.
+ delete[] mpPolygons;
+}
+
+void Impl3DMirrorConstructOverlay::SetMirrorAxis(Point aMirrorAxisA, Point aMirrorAxisB)
+{
+ // get rid of old overlay objects
+ maObjects.clear();
+
+ // create new ones
+ for(sal_uInt32 a(0); a < mrView.PaintWindowCount(); a++)
+ {
+ SdrPaintWindow* pCandidate = mrView.GetPaintWindow(a);
+ const rtl::Reference< sdr::overlay::OverlayManager >& xTargetOverlay = pCandidate->GetOverlayManager();
+
+ if(xTargetOverlay.is())
+ {
+ // build transformation: translate and rotate so that given edge is
+ // on x axis, them mirror in y and translate back
+ const basegfx::B2DVector aEdge(aMirrorAxisB.X() - aMirrorAxisA.X(), aMirrorAxisB.Y() - aMirrorAxisA.Y());
+ basegfx::B2DHomMatrix aMatrixTransform(basegfx::utils::createTranslateB2DHomMatrix(
+ -aMirrorAxisA.X(), -aMirrorAxisA.Y()));
+ aMatrixTransform.rotate(-atan2(aEdge.getY(), aEdge.getX()));
+ aMatrixTransform.scale(1.0, -1.0);
+ aMatrixTransform.rotate(atan2(aEdge.getY(), aEdge.getX()));
+ aMatrixTransform.translate(aMirrorAxisA.X(), aMirrorAxisA.Y());
+
+ if(mrView.IsSolidDragging())
+ {
+ if(!maFullOverlay.empty())
+ {
+ drawinglayer::primitive2d::Primitive2DContainer aContent(maFullOverlay);
+
+ if(!aMatrixTransform.isIdentity())
+ {
+ // embed in transformation group
+ drawinglayer::primitive2d::Primitive2DReference aTransformPrimitive2D(new drawinglayer::primitive2d::TransformPrimitive2D(aMatrixTransform, std::move(aContent)));
+ aContent = drawinglayer::primitive2d::Primitive2DContainer { aTransformPrimitive2D };
+ }
+
+ // if we have full overlay from selected objects, embed with 50% transparence, the
+ // transformation is added to the OverlayPrimitive2DSequenceObject
+ drawinglayer::primitive2d::Primitive2DReference aUnifiedTransparencePrimitive2D(new drawinglayer::primitive2d::UnifiedTransparencePrimitive2D(std::move(aContent), 0.5));
+ aContent = drawinglayer::primitive2d::Primitive2DContainer { aUnifiedTransparencePrimitive2D };
+
+ std::unique_ptr<sdr::overlay::OverlayPrimitive2DSequenceObject> pNew(new sdr::overlay::OverlayPrimitive2DSequenceObject(std::move(aContent)));
+
+ xTargetOverlay->add(*pNew);
+ maObjects.append(std::move(pNew));
+ }
+ }
+ else
+ {
+ for(size_t b = 0; b < mnCount; ++b)
+ {
+ // apply to polygon
+ basegfx::B2DPolyPolygon aPolyPolygon(mpPolygons[b]);
+ aPolyPolygon.transform(aMatrixTransform);
+
+ std::unique_ptr<sdr::overlay::OverlayPolyPolygonStripedAndFilled> pNew(new sdr::overlay::OverlayPolyPolygonStripedAndFilled(
+ aPolyPolygon));
+ xTargetOverlay->add(*pNew);
+ maObjects.append(std::move(pNew));
+ }
+ }
+ }
+ }
+}
+
+E3dView::E3dView(
+ SdrModel& rSdrModel,
+ OutputDevice* pOut)
+: SdrView(rSdrModel, pOut)
+{
+ InitView();
+}
+
+// DrawMarkedObj override, since possibly only a single 3D object is to be
+// drawn
+
+void E3dView::DrawMarkedObj(OutputDevice& rOut) const
+{
+ // Does 3D objects exist which scenes are not selected?
+ bool bSpecialHandling = false;
+ E3dScene *pScene = nullptr;
+
+ const size_t nCnt = GetMarkedObjectCount();
+ for(size_t nObjs = 0; nObjs < nCnt; ++nObjs)
+ {
+ SdrObject *pObj = GetMarkedObjectByIndex(nObjs);
+ if(auto pCompoundObject = dynamic_cast<E3dCompoundObject*>(pObj))
+ {
+ // related scene
+ pScene = pCompoundObject->getRootE3dSceneFromE3dObject();
+
+ if(nullptr != pScene && !IsObjMarked(pScene))
+ {
+ bSpecialHandling = true;
+ }
+ }
+ // Reset all selection flags
+ if(auto p3dObject = dynamic_cast< const E3dObject*>(pObj))
+ {
+ pScene = p3dObject->getRootE3dSceneFromE3dObject();
+
+ if(nullptr != pScene)
+ {
+ pScene->SetSelected(false);
+ }
+ }
+ }
+
+ if(bSpecialHandling)
+ {
+ // Set selection flag to "not selected" for scenes related to all 3D
+ // objects
+ for(size_t nObjs = 0; nObjs < nCnt; ++nObjs)
+ {
+ SdrObject *pObj = GetMarkedObjectByIndex(nObjs);
+ if(auto pCompoundObject = dynamic_cast<E3dCompoundObject*>(pObj))
+ {
+ // related scene
+ pScene = pCompoundObject->getRootE3dSceneFromE3dObject();
+
+ if(nullptr != pScene)
+ {
+ pScene->SetSelected(false);
+ }
+ }
+ }
+
+ for(size_t nObjs = 0; nObjs < nCnt; ++nObjs)
+ {
+ SdrObject *pObj = GetMarkedObjectByIndex(nObjs);
+ if(auto p3DObj = dynamic_cast<E3dObject*>(pObj))
+ {
+ // Select object
+ p3DObj->SetSelected(true);
+ pScene = p3DObj->getRootE3dSceneFromE3dObject();
+ }
+ }
+
+ if(nullptr != pScene)
+ {
+ // code from parent
+ SortMarkedObjects();
+
+ pScene->SetDrawOnlySelected(true);
+ pScene->SingleObjectPainter(rOut);
+ pScene->SetDrawOnlySelected(false);
+ }
+
+ // Reset selection flag
+ for(size_t nObjs = 0; nObjs < nCnt; ++nObjs)
+ {
+ SdrObject *pObj = GetMarkedObjectByIndex(nObjs);
+ if(auto pCompoundObject = dynamic_cast<E3dCompoundObject*>(pObj))
+ {
+ // related scene
+ pScene = pCompoundObject->getRootE3dSceneFromE3dObject();
+
+ if(nullptr != pScene)
+ {
+ pScene->SetSelected(false);
+ }
+ }
+ }
+ }
+ else
+ {
+ // call parent
+ SdrExchangeView::DrawMarkedObj(rOut);
+ }
+}
+
+// override get model, since in some 3D objects an additional scene
+// must be pushed in
+
+std::unique_ptr<SdrModel> E3dView::CreateMarkedObjModel() const
+{
+ // Does 3D objects exist which scenes are not selected?
+ bool bSpecialHandling(false);
+ const size_t nCount(GetMarkedObjectCount());
+ E3dScene *pScene = nullptr;
+
+ for(size_t nObjs = 0; nObjs < nCount; ++nObjs)
+ {
+ const SdrObject* pObj = GetMarkedObjectByIndex(nObjs);
+
+ if(!bSpecialHandling)
+ if(auto pCompoundObj = dynamic_cast< const E3dCompoundObject*>(pObj))
+ {
+ // if the object is selected, but it's scene not,
+ // we need special handling
+ pScene = pCompoundObj->getRootE3dSceneFromE3dObject();
+
+ if(nullptr != pScene && !IsObjMarked(pScene))
+ {
+ bSpecialHandling = true;
+ }
+ }
+
+ if(auto p3dObject = dynamic_cast< const E3dObject*>(pObj))
+ {
+ // reset all selection flags at 3D objects
+ pScene = p3dObject->getRootE3dSceneFromE3dObject();
+
+ if(nullptr != pScene)
+ {
+ pScene->SetSelected(false);
+ }
+ }
+ }
+
+ if(!bSpecialHandling)
+ {
+ // call parent
+ return SdrView::CreateMarkedObjModel();
+ }
+
+ std::unique_ptr<SdrModel> pNewModel;
+ tools::Rectangle aSelectedSnapRect;
+
+ // set 3d selection flags at all directly selected objects
+ // and collect SnapRect of selected objects
+ for(size_t nObjs = 0; nObjs < nCount; ++nObjs)
+ {
+ SdrObject *pObj = GetMarkedObjectByIndex(nObjs);
+
+ if(auto p3DObj = dynamic_cast<E3dCompoundObject*>(pObj))
+ {
+ // mark object, but not scenes
+ p3DObj->SetSelected(true);
+ aSelectedSnapRect.Union(p3DObj->GetSnapRect());
+ }
+ }
+
+ // create new mark list which contains all indirectly selected3d
+ // scenes as selected objects
+ SdrMarkList aOldML(GetMarkedObjectList());
+ SdrMarkList aNewML;
+ SdrMarkList& rCurrentMarkList = const_cast<E3dView*>(this)->GetMarkedObjectListWriteAccess();
+ rCurrentMarkList = aNewML;
+
+ for(size_t nObjs = 0; nObjs < nCount; ++nObjs)
+ {
+ SdrObject *pObj = aOldML.GetMark(nObjs)->GetMarkedSdrObj();
+
+ if(auto p3dObject = dynamic_cast< E3dObject* >(pObj))
+ {
+ pScene = p3dObject->getRootE3dSceneFromE3dObject();
+
+ if(nullptr != pScene && !IsObjMarked(pScene) && GetSdrPageView())
+ {
+ const_cast<E3dView*>(this)->MarkObj(pScene, GetSdrPageView(), false, true);
+ }
+ }
+ }
+
+ // call parent. This will copy all scenes and the selection flags at the 3D objects. So
+ // it will be possible to delete all non-selected 3d objects from the cloned 3d scenes
+ pNewModel = SdrView::CreateMarkedObjModel();
+
+ if(pNewModel)
+ {
+ for(sal_uInt16 nPg(0); nPg < pNewModel->GetPageCount(); nPg++)
+ {
+ const SdrPage* pSrcPg=pNewModel->GetPage(nPg);
+ const size_t nObjCount(pSrcPg->GetObjCount());
+
+ for(size_t nOb = 0; nOb < nObjCount; ++nOb)
+ {
+ const SdrObject* pSrcOb=pSrcPg->GetObj(nOb);
+
+ if(auto p3dscene = dynamic_cast< const E3dScene* >( pSrcOb))
+ {
+ pScene = const_cast<E3dScene*>(p3dscene);
+
+ // delete all not intentionally cloned 3d objects
+ pScene->removeAllNonSelectedObjects();
+
+ // reset select flags and set SnapRect of all selected objects
+ pScene->SetSelected(false);
+ pScene->SetSnapRect(aSelectedSnapRect);
+ }
+ }
+ }
+ }
+
+ // restore old selection
+ rCurrentMarkList = aOldML;
+
+ return pNewModel;
+}
+
+// When pasting objects have to integrated if a scene is inserted, but
+// not the scene itself
+
+bool E3dView::Paste(
+ const SdrModel& rMod, const Point& rPos, SdrObjList* pLst, SdrInsertFlags nOptions)
+{
+ bool bRetval = false;
+
+ // Get list
+ Point aPos(rPos);
+ SdrObjList* pDstList = pLst;
+ ImpGetPasteObjList(aPos, pDstList);
+
+ if(!pDstList)
+ return false;
+
+ // Get owner of the list
+ E3dScene* pDstScene(dynamic_cast< E3dScene* >(pDstList->getSdrObjectFromSdrObjList()));
+
+ if(nullptr != pDstScene)
+ {
+ BegUndo(SvxResId(RID_SVX_3D_UNDO_EXCHANGE_PASTE));
+
+ // Copy all objects from E3dScenes and insert them directly
+ for(sal_uInt16 nPg(0); nPg < rMod.GetPageCount(); nPg++)
+ {
+ const SdrPage* pSrcPg=rMod.GetPage(nPg);
+ const size_t nObjCount(pSrcPg->GetObjCount());
+
+ // calculate offset for paste
+ tools::Rectangle aR = pSrcPg->GetAllObjBoundRect();
+ Point aDist(aPos - aR.Center());
+
+ // Insert sub-objects for scenes
+ for(size_t nOb = 0; nOb < nObjCount; ++nOb)
+ {
+ const SdrObject* pSrcOb = pSrcPg->GetObj(nOb);
+ if(auto p3dscene = dynamic_cast< const E3dScene* >(pSrcOb))
+ {
+ E3dScene* pSrcScene = const_cast<E3dScene*>(p3dscene);
+ ImpCloneAll3DObjectsToDestScene(pSrcScene, pDstScene, aDist);
+ }
+ }
+ }
+ EndUndo();
+ }
+ else
+ {
+ // call parent
+ bRetval = SdrView::Paste(rMod, rPos, pLst, nOptions);
+ }
+
+ return bRetval;
+}
+
+// Service routine used from local Clone() and from SdrCreateView::EndCreateObj(...)
+bool E3dView::ImpCloneAll3DObjectsToDestScene(E3dScene const * pSrcScene, E3dScene* pDstScene, Point /*aOffset*/)
+{
+ bool bRetval(false);
+
+ if(pSrcScene && pDstScene)
+ {
+ for(size_t i = 0; i < pSrcScene->GetSubList()->GetObjCount(); ++i)
+ {
+ E3dCompoundObject* pCompoundObj = dynamic_cast< E3dCompoundObject* >(pSrcScene->GetSubList()->GetObj(i));
+
+ if(pCompoundObj)
+ {
+ E3dCompoundObject* pNewCompoundObj(pCompoundObj->CloneSdrObject(pDstScene->getSdrModelFromSdrObject()));
+
+ if(pNewCompoundObj)
+ {
+ // get dest scene's current range in 3D world coordinates
+ const basegfx::B3DHomMatrix aSceneToWorldTrans(pDstScene->GetFullTransform());
+ basegfx::B3DRange aSceneRange(pDstScene->GetBoundVolume());
+ aSceneRange.transform(aSceneToWorldTrans);
+
+ // get new object's implied object transformation
+ const basegfx::B3DHomMatrix aNewObjectTrans(pNewCompoundObj->GetTransform());
+
+ // get new object's range in 3D world coordinates in dest scene
+ // as if it were already added
+ const basegfx::B3DHomMatrix aObjectToWorldTrans(aSceneToWorldTrans * aNewObjectTrans);
+ basegfx::B3DRange aObjectRange(pNewCompoundObj->GetBoundVolume());
+ aObjectRange.transform(aObjectToWorldTrans);
+
+ // get scale adaptation
+ const basegfx::B3DVector aSceneScale(aSceneRange.getRange());
+ const basegfx::B3DVector aObjectScale(aObjectRange.getRange());
+ double fScale(1.0);
+
+ // if new object's size in X,Y or Z is bigger that 80% of dest scene, adapt scale
+ // to not change the scene by the inserted object
+ const double fSizeFactor(0.5);
+
+ if(aObjectScale.getX() * fScale > aSceneScale.getX() * fSizeFactor)
+ {
+ const double fObjSize(aObjectScale.getX() * fScale);
+ const double fFactor((aSceneScale.getX() * fSizeFactor) / (basegfx::fTools::equalZero(fObjSize) ? 1.0 : fObjSize));
+ fScale *= fFactor;
+ }
+
+ if(aObjectScale.getY() * fScale > aSceneScale.getY() * fSizeFactor)
+ {
+ const double fObjSize(aObjectScale.getY() * fScale);
+ const double fFactor((aSceneScale.getY() * fSizeFactor) / (basegfx::fTools::equalZero(fObjSize) ? 1.0 : fObjSize));
+ fScale *= fFactor;
+ }
+
+ if(aObjectScale.getZ() * fScale > aSceneScale.getZ() * fSizeFactor)
+ {
+ const double fObjSize(aObjectScale.getZ() * fScale);
+ const double fFactor((aSceneScale.getZ() * fSizeFactor) / (basegfx::fTools::equalZero(fObjSize) ? 1.0 : fObjSize));
+ fScale *= fFactor;
+ }
+
+ // get translation adaptation
+ const basegfx::B3DPoint aSceneCenter(aSceneRange.getCenter());
+ const basegfx::B3DPoint aObjectCenter(aObjectRange.getCenter());
+
+ // build full modification transform. The object's transformation
+ // shall be modified, so start at object coordinates; transform to 3d world coor
+ basegfx::B3DHomMatrix aModifyingTransform(aObjectToWorldTrans);
+
+ // translate to absolute center in 3d world coor
+ aModifyingTransform.translate(-aObjectCenter.getX(), -aObjectCenter.getY(), -aObjectCenter.getZ());
+
+ // scale to dest size in 3d world coor
+ aModifyingTransform.scale(fScale, fScale, fScale);
+
+ // translate to dest scene center in 3d world coor
+ aModifyingTransform.translate(aSceneCenter.getX(), aSceneCenter.getY(), aSceneCenter.getZ());
+
+ // transform from 3d world to dest object coordinates
+ basegfx::B3DHomMatrix aWorldToObject(aObjectToWorldTrans);
+ aWorldToObject.invert();
+ aModifyingTransform = aWorldToObject * aModifyingTransform;
+
+ // correct implied object transform by applying changing one in object coor
+ pNewCompoundObj->SetTransform(aModifyingTransform * aNewObjectTrans);
+
+ // fill and insert new object
+ pNewCompoundObj->NbcSetLayer(pCompoundObj->GetLayer());
+ pNewCompoundObj->NbcSetStyleSheet(pCompoundObj->GetStyleSheet(), true);
+ pDstScene->InsertObject(pNewCompoundObj);
+ bRetval = true;
+
+ // Create undo
+ if( GetModel()->IsUndoEnabled() )
+ AddUndo(GetModel()->GetSdrUndoFactory().CreateUndoNewObject(*pNewCompoundObj));
+ }
+ }
+ }
+ }
+
+ return bRetval;
+}
+
+bool E3dView::IsConvertTo3DObjPossible() const
+{
+ bool bAny3D(false);
+ bool bGroupSelected(false);
+ bool bRetval(true);
+
+ for(size_t a=0; !bAny3D && a<GetMarkedObjectCount(); ++a)
+ {
+ SdrObject *pObj = GetMarkedObjectByIndex(a);
+ if(pObj)
+ {
+ ImpIsConvertTo3DPossible(pObj, bAny3D, bGroupSelected);
+ }
+ }
+
+ bRetval = !bAny3D
+ && (
+ IsConvertToPolyObjPossible()
+ || IsConvertToPathObjPossible()
+ || IsImportMtfPossible());
+ return bRetval;
+}
+
+void E3dView::ImpIsConvertTo3DPossible(SdrObject const * pObj, bool& rAny3D,
+ bool& rGroupSelected) const
+{
+ if(!pObj)
+ return;
+
+ if(dynamic_cast< const E3dObject* >(pObj) != nullptr)
+ {
+ rAny3D = true;
+ }
+ else
+ {
+ if(pObj->IsGroupObject())
+ {
+ SdrObjListIter aIter(*pObj, SdrIterMode::DeepNoGroups);
+ while(aIter.IsMore())
+ {
+ SdrObject* pNewObj = aIter.Next();
+ ImpIsConvertTo3DPossible(pNewObj, rAny3D, rGroupSelected);
+ }
+ rGroupSelected = true;
+ }
+ }
+}
+
+void E3dView::ImpChangeSomeAttributesFor3DConversion(SdrObject* pObj)
+{
+ if(dynamic_cast<const SdrTextObj*>( pObj) == nullptr)
+ return;
+
+ const SfxItemSet& rSet = pObj->GetMergedItemSet();
+ const SvxColorItem& rTextColorItem = rSet.Get(EE_CHAR_COLOR);
+ if(rTextColorItem.GetValue() != COL_BLACK)
+ return;
+
+ //For black text objects, the color set to gray
+ if(pObj->getSdrPageFromSdrObject())
+ {
+ // if black is only default attribute from
+ // pattern set it hard so that it is used in undo.
+ pObj->SetMergedItem(SvxColorItem(COL_BLACK, EE_CHAR_COLOR));
+
+ // add undo now
+ if( GetModel()->IsUndoEnabled() )
+ AddUndo(GetModel()->GetSdrUndoFactory().CreateUndoAttrObject(*pObj));
+ }
+
+ pObj->SetMergedItem(SvxColorItem(COL_GRAY, EE_CHAR_COLOR));
+}
+
+void E3dView::ImpChangeSomeAttributesFor3DConversion2(SdrObject* pObj)
+{
+ auto pPathObj = dynamic_cast<const SdrPathObj*>( pObj);
+ if(!pPathObj)
+ return;
+
+ const SfxItemSet& rSet = pObj->GetMergedItemSet();
+ sal_Int32 nLineWidth = rSet.Get(XATTR_LINEWIDTH).GetValue();
+ drawing::LineStyle eLineStyle = rSet.Get(XATTR_LINESTYLE).GetValue();
+ drawing::FillStyle eFillStyle = rSet.Get(XATTR_FILLSTYLE).GetValue();
+
+ if(pPathObj->IsClosed()
+ && eLineStyle == drawing::LineStyle_SOLID
+ && !nLineWidth
+ && eFillStyle != drawing::FillStyle_NONE)
+ {
+ if(pObj->getSdrPageFromSdrObject() && GetModel()->IsUndoEnabled() )
+ {
+ AddUndo(GetModel()->GetSdrUndoFactory().CreateUndoAttrObject(*pObj));
+ }
+
+ pObj->SetMergedItem(XLineStyleItem(drawing::LineStyle_NONE));
+ pObj->SetMergedItem(XLineWidthItem(0));
+ }
+}
+
+void E3dView::ImpCreateSingle3DObjectFlat(E3dScene* pScene, SdrObject* pObj, bool bExtrude, double fDepth, basegfx::B2DHomMatrix const & rLatheMat)
+{
+ // Single PathObject, transform this
+ SdrPathObj* pPath = dynamic_cast<SdrPathObj*>( pObj );
+
+ if(!pPath)
+ return;
+
+ E3dDefaultAttributes aDefault = Get3DDefaultAttributes();
+
+ if(bExtrude)
+ {
+ aDefault.SetDefaultExtrudeCharacterMode(true);
+ }
+ else
+ {
+ aDefault.SetDefaultLatheCharacterMode(true);
+ }
+
+ // Get Itemset of the original object
+ SfxItemSet aSet(pObj->GetMergedItemSet());
+
+ drawing::FillStyle eFillStyle = aSet.Get(XATTR_FILLSTYLE).GetValue();
+
+ // line style turned off
+ aSet.Put(XLineStyleItem(drawing::LineStyle_NONE));
+
+ //Determining if FILL_Attribute is set.
+ if(!pPath->IsClosed() || eFillStyle == drawing::FillStyle_NONE)
+ {
+ // This SdrPathObj is not filled, leave the front and rear face out.
+ // Moreover, a two-sided representation necessary.
+ aDefault.SetDefaultExtrudeCloseFront(false);
+ aDefault.SetDefaultExtrudeCloseBack(false);
+
+ aSet.Put(makeSvx3DDoubleSidedItem(true));
+
+ // Set fill attribute
+ aSet.Put(XFillStyleItem(drawing::FillStyle_SOLID));
+
+ // Fill color must be the color line, because the object was
+ // previously just a line
+ Color aColorLine = aSet.Get(XATTR_LINECOLOR).GetColorValue();
+ aSet.Put(XFillColorItem(OUString(), aColorLine));
+ }
+
+ // Create a new extrude object
+ E3dObject* p3DObj = nullptr;
+ if(bExtrude)
+ {
+ p3DObj = new E3dExtrudeObj(pObj->getSdrModelFromSdrObject(), aDefault, pPath->GetPathPoly(), fDepth);
+ }
+ else
+ {
+ // rLatheMat expects coordinates with y-axis up, pPath uses y-axis down
+ basegfx::B2DHomMatrix aFlipVerticalMat(1.0, 0.0, 0.0, 0.0, -1.0, 0.0);
+ basegfx::B2DPolyPolygon aPolyPoly2D(pPath->GetPathPoly());
+ aPolyPoly2D.transform(aFlipVerticalMat);
+ aPolyPoly2D.transform(rLatheMat);
+ // ctor E3dLatheObj expects coordinates with y-axis down
+ aPolyPoly2D.transform(aFlipVerticalMat);
+ p3DObj = new E3dLatheObj(pObj->getSdrModelFromSdrObject(), aDefault, aPolyPoly2D);
+ }
+
+ // Set attribute
+ p3DObj->NbcSetLayer(pObj->GetLayer());
+
+ p3DObj->SetMergedItemSet(aSet);
+
+ p3DObj->NbcSetStyleSheet(pObj->GetStyleSheet(), true);
+
+ // Insert a new extrude object
+ pScene->InsertObject(p3DObj);
+}
+
+void E3dView::ImpCreate3DObject(E3dScene* pScene, SdrObject* pObj, bool bExtrude, double fDepth, basegfx::B2DHomMatrix const & rLatheMat)
+{
+ if(!pObj)
+ return;
+
+ // change text color attribute for not so dark colors
+ if(pObj->IsGroupObject())
+ {
+ SdrObjListIter aIter(*pObj, SdrIterMode::DeepWithGroups);
+ while(aIter.IsMore())
+ {
+ SdrObject* pGroupMember = aIter.Next();
+ ImpChangeSomeAttributesFor3DConversion(pGroupMember);
+ }
+ }
+ else
+ ImpChangeSomeAttributesFor3DConversion(pObj);
+
+ // convert completely to path objects
+ SdrObject* pNewObj1 = pObj->ConvertToPolyObj(false, false).release();
+
+ if(!pNewObj1)
+ return;
+
+ // change text color attribute for not so dark colors
+ if(pNewObj1->IsGroupObject())
+ {
+ SdrObjListIter aIter(*pNewObj1, SdrIterMode::DeepWithGroups);
+ while(aIter.IsMore())
+ {
+ SdrObject* pGroupMember = aIter.Next();
+ ImpChangeSomeAttributesFor3DConversion2(pGroupMember);
+ }
+ }
+ else
+ ImpChangeSomeAttributesFor3DConversion2(pNewObj1);
+
+ // convert completely to path objects
+ SdrObject* pNewObj2 = pObj->ConvertToContourObj(pNewObj1, true);
+
+ if(pNewObj2)
+ {
+ // add all to flat scene
+ if(pNewObj2->IsGroupObject())
+ {
+ SdrObjListIter aIter(*pNewObj2, SdrIterMode::DeepWithGroups);
+ while(aIter.IsMore())
+ {
+ SdrObject* pGroupMember = aIter.Next();
+ ImpCreateSingle3DObjectFlat(pScene, pGroupMember, bExtrude, fDepth, rLatheMat);
+ }
+ }
+ else
+ ImpCreateSingle3DObjectFlat(pScene, pNewObj2, bExtrude, fDepth, rLatheMat);
+
+ // delete object in between
+ if (pNewObj2 != pObj && pNewObj2 != pNewObj1)
+ SdrObject::Free( pNewObj2 );
+ }
+
+ // delete object in between
+ if (pNewObj1 != pObj)
+ SdrObject::Free( pNewObj1 );
+}
+
+void E3dView::ConvertMarkedObjTo3D(bool bExtrude, const basegfx::B2DPoint& rPnt1, const basegfx::B2DPoint& rPnt2)
+{
+ if(!AreObjectsMarked())
+ return;
+
+ // Create undo
+ if(bExtrude)
+ BegUndo(SvxResId(RID_SVX_3D_UNDO_EXTRUDE));
+ else
+ BegUndo(SvxResId(RID_SVX_3D_UNDO_LATHE));
+
+ SdrModel& rSdrModel(GetSdrMarkByIndex(0)->GetMarkedSdrObj()->getSdrModelFromSdrObject());
+
+ // Create a new scene for the created 3D object
+ E3dScene* pScene = new E3dScene(rSdrModel);
+
+ // Determine rectangle and possibly correct it
+ tools::Rectangle aRect = GetAllMarkedRect();
+ if(aRect.GetWidth() <= 1)
+ aRect.SetSize(Size(500, aRect.GetHeight()));
+ if(aRect.GetHeight() <= 1)
+ aRect.SetSize(Size(aRect.GetWidth(), 500));
+
+ // Determine the depth relative to the size of the selection
+ double fDepth = 0.0;
+ double fRot3D = 0.0;
+ basegfx::B2DHomMatrix aLatheMat;
+
+ if(bExtrude)
+ {
+ double fW = static_cast<double>(aRect.GetWidth());
+ double fH = static_cast<double>(aRect.GetHeight());
+ fDepth = sqrt(fW*fW + fH*fH) / 6.0;
+ }
+ if(!bExtrude)
+ {
+ // Create transformation for the polygons rotating body
+ if (rPnt1 != rPnt2)
+ {
+ // Rotation around control point #1 with set angle
+ // for 3D coordinates
+ basegfx::B2DPoint aDiff(rPnt1 - rPnt2);
+ fRot3D = atan2(aDiff.getY(), aDiff.getX()) - M_PI_2;
+
+ if(basegfx::fTools::equalZero(fabs(fRot3D)))
+ fRot3D = 0.0;
+
+ if(fRot3D != 0.0)
+ {
+ aLatheMat = basegfx::utils::createRotateAroundPoint(rPnt2, -fRot3D)
+ * aLatheMat;
+ }
+ }
+
+ if (rPnt2.getX() != 0.0)
+ {
+ // Translation to Y=0 - axis
+ aLatheMat.translate(-rPnt2.getX(), 0.0);
+ }
+ else
+ {
+ aLatheMat.translate(static_cast<double>(-aRect.Left()), 0.0);
+ }
+
+ // Form the inverse matrix to determine the target expansion
+ basegfx::B2DHomMatrix aInvLatheMat(aLatheMat);
+ aInvLatheMat.invert();
+
+ // SnapRect extension enables mirroring in the axis of rotation
+ for(size_t a=0; a<GetMarkedObjectCount(); ++a)
+ {
+ SdrMark* pMark = GetSdrMarkByIndex(a);
+ SdrObject* pObj = pMark->GetMarkedSdrObj();
+ tools::Rectangle aTurnRect = pObj->GetSnapRect();
+ basegfx::B2DPoint aRot;
+ Point aRotPnt;
+
+ aRot = basegfx::B2DPoint(aTurnRect.Left(), -aTurnRect.Top());
+ aRot *= aLatheMat;
+ aRot.setX(-aRot.getX());
+ aRot *= aInvLatheMat;
+ aRotPnt = Point(static_cast<tools::Long>(aRot.getX() + 0.5), static_cast<tools::Long>(-aRot.getY() - 0.5));
+ aRect.Union(tools::Rectangle(aRotPnt, aRotPnt));
+
+ aRot = basegfx::B2DPoint(aTurnRect.Left(), -aTurnRect.Bottom());
+ aRot *= aLatheMat;
+ aRot.setX(-aRot.getX());
+ aRot *= aInvLatheMat;
+ aRotPnt = Point(static_cast<tools::Long>(aRot.getX() + 0.5), static_cast<tools::Long>(-aRot.getY() - 0.5));
+ aRect.Union(tools::Rectangle(aRotPnt, aRotPnt));
+
+ aRot = basegfx::B2DPoint(aTurnRect.Right(), -aTurnRect.Top());
+ aRot *= aLatheMat;
+ aRot.setX(-aRot.getX());
+ aRot *= aInvLatheMat;
+ aRotPnt = Point(static_cast<tools::Long>(aRot.getX() + 0.5), static_cast<tools::Long>(-aRot.getY() - 0.5));
+ aRect.Union(tools::Rectangle(aRotPnt, aRotPnt));
+
+ aRot = basegfx::B2DPoint(aTurnRect.Right(), -aTurnRect.Bottom());
+ aRot *= aLatheMat;
+ aRot.setX(-aRot.getX());
+ aRot *= aInvLatheMat;
+ aRotPnt = Point(static_cast<tools::Long>(aRot.getX() + 0.5), static_cast<tools::Long>(-aRot.getY() - 0.5));
+ aRect.Union(tools::Rectangle(aRotPnt, aRotPnt));
+ }
+ }
+
+ // Walk through the selection and convert it into 3D, complete with
+ // Conversion to SdrPathObject, also fonts
+ for(size_t a=0; a<GetMarkedObjectCount(); ++a)
+ {
+ SdrMark* pMark = GetSdrMarkByIndex(a);
+ SdrObject* pObj = pMark->GetMarkedSdrObj();
+
+ ImpCreate3DObject(pScene, pObj, bExtrude, fDepth, aLatheMat);
+ }
+
+ if(pScene->GetSubList() && pScene->GetSubList()->GetObjCount() != 0)
+ {
+ // Arrange all created objects by depth
+ if(bExtrude)
+ DoDepthArrange(pScene, fDepth);
+
+ // Center 3D objects in the middle of the overall rectangle
+ basegfx::B3DPoint aCenter(pScene->GetBoundVolume().getCenter());
+ basegfx::B3DHomMatrix aMatrix;
+
+ aMatrix.translate(-aCenter.getX(), -aCenter.getY(), -aCenter.getZ());
+ pScene->SetTransform(aMatrix * pScene->GetTransform());
+
+ // Initialize scene
+ pScene->NbcSetSnapRect(aRect);
+ basegfx::B3DRange aBoundVol = pScene->GetBoundVolume();
+ InitScene(pScene, static_cast<double>(aRect.GetWidth()), static_cast<double>(aRect.GetHeight()), aBoundVol.getDepth());
+
+ // Insert scene instead of the first selected object and throw away
+ // all the old objects
+ SdrObject* pRepObj = GetMarkedObjectByIndex(0);
+ SdrPageView* pPV = GetSdrPageViewOfMarkedByIndex(0);
+ MarkObj(pRepObj, pPV, true);
+ ReplaceObjectAtView(pRepObj, *pPV, pScene, false);
+ DeleteMarked();
+ MarkObj(pScene, pPV);
+
+ // Rotate Rotation body around the axis of rotation
+ if(!bExtrude && fRot3D != 0.0)
+ {
+ basegfx::B3DHomMatrix aRotate;
+ aRotate.rotate(0.0, 0.0, fRot3D);
+ pScene->SetTransform(aRotate * pScene->GetTransform());
+ }
+
+ // Set default rotation
+ {
+ basegfx::B3DHomMatrix aRotate;
+ aRotate.rotate(basegfx::deg2rad(20.0), 0.0, 0.0);
+ // E3DModifySceneSnapRectUpdater updates the 2D representation of the scene.
+ // It prepares things in ctor and acts in dtor.
+ E3DModifySceneSnapRectUpdater aUpdater(pScene->getSdrObjectFromSdrObjList());
+ pScene->SetTransform(aRotate * pScene->GetTransform());
+ }
+ }
+ else
+ {
+ // No 3D object was created, throw away everything
+ // always use SdrObject::Free(...) for SdrObjects (!)
+ SdrObject* pTemp(pScene);
+ SdrObject::Free(pTemp);
+ }
+
+ EndUndo();
+}
+
+//Arrange all created extrude objects by depth
+
+namespace {
+
+struct E3dDepthNeighbour
+{
+ E3dExtrudeObj* mpObj;
+ basegfx::B2DPolyPolygon maPreparedPolyPolygon;
+
+ E3dDepthNeighbour(E3dExtrudeObj* pObj, basegfx::B2DPolyPolygon const & rPreparedPolyPolygon)
+ : mpObj(pObj),
+ maPreparedPolyPolygon(rPreparedPolyPolygon)
+ {
+ }
+};
+
+struct E3dDepthLayer
+{
+ E3dDepthLayer* mpDown;
+ std::vector<E3dDepthNeighbour> mvNeighbours;
+
+ E3dDepthLayer()
+ : mpDown(nullptr)
+ {
+ }
+};
+
+}
+
+void E3dView::DoDepthArrange(E3dScene const * pScene, double fDepth)
+{
+ if(!(pScene && pScene->GetSubList() && pScene->GetSubList()->GetObjCount() > 1))
+ return;
+
+ SdrObjList* pSubList = pScene->GetSubList();
+ SdrObjListIter aIter(pSubList, SdrIterMode::Flat);
+ E3dDepthLayer* pBaseLayer = nullptr;
+ E3dDepthLayer* pLayer = nullptr;
+ sal_Int32 nNumLayers = 0;
+
+ while(aIter.IsMore())
+ {
+ E3dExtrudeObj* pExtrudeObj = dynamic_cast< E3dExtrudeObj* >(aIter.Next());
+
+ if(pExtrudeObj)
+ {
+ const basegfx::B2DPolyPolygon aExtrudePoly(
+ basegfx::utils::prepareForPolygonOperation(pExtrudeObj->GetExtrudePolygon()));
+ const SfxItemSet& rLocalSet = pExtrudeObj->GetMergedItemSet();
+ const drawing::FillStyle eLocalFillStyle = rLocalSet.Get(XATTR_FILLSTYLE).GetValue();
+ const Color aLocalColor = rLocalSet.Get(XATTR_FILLCOLOR).GetColorValue();
+
+ // sort in ExtrudeObj
+ if(pLayer)
+ {
+ // do we have overlap with an object of this layer?
+ bool bOverlap(false);
+
+ for(const auto& rAct : pLayer->mvNeighbours)
+ {
+ // do rAct.mpObj and pExtrudeObj overlap? Check by
+ // using logical AND clipping
+ const basegfx::B2DPolyPolygon aAndPolyPolygon(
+ basegfx::utils::solvePolygonOperationAnd(
+ aExtrudePoly,
+ rAct.maPreparedPolyPolygon));
+
+ if(aAndPolyPolygon.count() != 0)
+ {
+ // second criteria: is another fillstyle or color used?
+ const SfxItemSet& rCompareSet = rAct.mpObj->GetMergedItemSet();
+
+ drawing::FillStyle eCompareFillStyle = rCompareSet.Get(XATTR_FILLSTYLE).GetValue();
+
+ if(eLocalFillStyle == eCompareFillStyle)
+ {
+ if(eLocalFillStyle == drawing::FillStyle_SOLID)
+ {
+ Color aCompareColor = rCompareSet.Get(XATTR_FILLCOLOR).GetColorValue();
+
+ if(aCompareColor == aLocalColor)
+ {
+ continue;
+ }
+ }
+ else if(eLocalFillStyle == drawing::FillStyle_NONE)
+ {
+ continue;
+ }
+ }
+
+ bOverlap = true;
+ break;
+ }
+ }
+
+ if(bOverlap)
+ {
+ // yes, start a new layer
+ pLayer->mpDown = new E3dDepthLayer;
+ pLayer = pLayer->mpDown;
+ nNumLayers++;
+ pLayer->mvNeighbours.emplace_back(pExtrudeObj, aExtrudePoly);
+ }
+ else
+ {
+ // no, add to current layer
+ pLayer->mvNeighbours.emplace(pLayer->mvNeighbours.begin(), pExtrudeObj, aExtrudePoly);
+ }
+ }
+ else
+ {
+ // first layer ever
+ pBaseLayer = new E3dDepthLayer;
+ pLayer = pBaseLayer;
+ nNumLayers++;
+ pLayer->mvNeighbours.emplace_back(pExtrudeObj, aExtrudePoly);
+ }
+ }
+ }
+
+ // number of layers is done
+ if(nNumLayers > 1)
+ {
+ // need to be arranged
+ double fMinDepth = fDepth * 0.8;
+ double fStep = (fDepth - fMinDepth) / static_cast<double>(nNumLayers);
+ pLayer = pBaseLayer;
+
+ while(pLayer)
+ {
+ // move along layer
+ for(auto& rAct : pLayer->mvNeighbours)
+ {
+ // adapt extrude value
+ rAct.mpObj->SetMergedItem(SfxUInt32Item(SDRATTR_3DOBJ_DEPTH, sal_uInt32(fMinDepth + 0.5)));
+ }
+
+ // next layer
+ pLayer = pLayer->mpDown;
+ fMinDepth += fStep;
+ }
+ }
+
+ // cleanup
+ while(pBaseLayer)
+ {
+ pLayer = pBaseLayer->mpDown;
+ delete pBaseLayer;
+ pBaseLayer = pLayer;
+ }
+}
+
+// Start drag, create for 3D objects before possibly drag method
+
+bool E3dView::BegDragObj(const Point& rPnt, OutputDevice* pOut,
+ SdrHdl* pHdl, short nMinMov,
+ SdrDragMethod* pForcedMeth)
+{
+ if(Is3DRotationCreationActive() && GetMarkedObjectCount())
+ {
+ // Determine all selected polygons and return the mirrored helper overlay
+ mpMirrorOverlay->SetMirrorAxis(maRef1, maRef2);
+ }
+ else
+ {
+ bool bOwnActionNecessary;
+ if (pHdl == nullptr)
+ {
+ bOwnActionNecessary = true;
+ }
+ else if (pHdl->IsVertexHdl() || pHdl->IsCornerHdl())
+ {
+ bOwnActionNecessary = true;
+ }
+ else
+ {
+ bOwnActionNecessary = false;
+ }
+
+ if(bOwnActionNecessary && GetMarkedObjectCount() > 0)
+ {
+ E3dDragConstraint eConstraint = E3dDragConstraint::XYZ;
+ bool bThereAreRootScenes = false;
+ bool bThereAre3DObjects = false;
+ const size_t nCnt = GetMarkedObjectCount();
+ for(size_t nObjs = 0; nObjs < nCnt; ++nObjs)
+ {
+ SdrObject *pObj = GetMarkedObjectByIndex(nObjs);
+ if(pObj)
+ {
+ if( auto pScene = dynamic_cast< const E3dScene* >(pObj) )
+ if( pScene->getRootE3dSceneFromE3dObject() == pObj )
+ bThereAreRootScenes = true;
+
+ if(dynamic_cast< const E3dObject* >(pObj) != nullptr)
+ {
+ bThereAre3DObjects = true;
+ }
+ }
+ }
+ if( bThereAre3DObjects )
+ {
+ meDragHdl = ( pHdl == nullptr ? SdrHdlKind::Move : pHdl->GetKind() );
+ switch ( meDragMode )
+ {
+ case SdrDragMode::Rotate:
+ case SdrDragMode::Shear:
+ {
+ switch ( meDragHdl )
+ {
+ case SdrHdlKind::Left:
+ case SdrHdlKind::Right:
+ {
+ eConstraint = E3dDragConstraint::X;
+ }
+ break;
+
+ case SdrHdlKind::Upper:
+ case SdrHdlKind::Lower:
+ {
+ eConstraint = E3dDragConstraint::Y;
+ }
+ break;
+
+ case SdrHdlKind::UpperLeft:
+ case SdrHdlKind::UpperRight:
+ case SdrHdlKind::LowerLeft:
+ case SdrHdlKind::LowerRight:
+ {
+ eConstraint = E3dDragConstraint::Z;
+ }
+ break;
+ default: break;
+ }
+
+ // do not mask the allowed rotations
+ eConstraint &= E3dDragConstraint::XYZ;
+ pForcedMeth = new E3dDragRotate(*this, GetMarkedObjectList(), eConstraint, IsSolidDragging());
+ }
+ break;
+
+ case SdrDragMode::Move:
+ {
+ if(!bThereAreRootScenes)
+ {
+ pForcedMeth = new E3dDragMove(*this, GetMarkedObjectList(), meDragHdl, eConstraint, IsSolidDragging());
+ }
+ }
+ break;
+
+ // later on
+ case SdrDragMode::Mirror:
+ case SdrDragMode::Crook:
+ case SdrDragMode::Transparence:
+ case SdrDragMode::Gradient:
+ default:
+ {
+ }
+ break;
+ }
+ }
+ }
+ }
+ return SdrView::BegDragObj(rPnt, pOut, pHdl, nMinMov, pForcedMeth);
+}
+
+// Set current 3D drawing object, create the scene for this
+E3dScene* E3dView::SetCurrent3DObj(E3dObject* p3DObj)
+{
+ DBG_ASSERT(p3DObj != nullptr, "Who puts in a NULL-pointer here");
+
+ // get transformed BoundVolume of the object
+ basegfx::B3DRange aVolume(p3DObj->GetBoundVolume());
+ aVolume.transform(p3DObj->GetTransform());
+ double fW(aVolume.getWidth());
+ double fH(aVolume.getHeight());
+
+ tools::Rectangle aRect(0,0, static_cast<tools::Long>(fW), static_cast<tools::Long>(fH));
+
+ E3dScene* pScene = new E3dScene(p3DObj->getSdrModelFromSdrObject());
+
+ InitScene(pScene, fW, fH, aVolume.getMaxZ() + ((fW + fH) / 4.0));
+
+ pScene->InsertObject(p3DObj);
+ pScene->NbcSetSnapRect(aRect);
+
+ return pScene;
+}
+
+void E3dView::InitScene(E3dScene* pScene, double fW, double fH, double fCamZ)
+{
+ Camera3D aCam(pScene->GetCamera());
+
+ aCam.SetAutoAdjustProjection(false);
+ aCam.SetViewWindow(- fW / 2, - fH / 2, fW, fH);
+ basegfx::B3DPoint aLookAt;
+
+ double fDefaultCamPosZ = GetDefaultCamPosZ();
+ basegfx::B3DPoint aCamPos(0.0, 0.0, fCamZ < fDefaultCamPosZ ? fDefaultCamPosZ : fCamZ);
+
+ aCam.SetPosAndLookAt(aCamPos, aLookAt);
+ aCam.SetFocalLength(GetDefaultCamFocal());
+ pScene->SetCamera(aCam);
+}
+
+void E3dView::Start3DCreation()
+{
+ if (!GetMarkedObjectCount())
+ return;
+
+ //positioned
+ tools::Long nOutMin = 0;
+ tools::Long nOutMax = 0;
+ tools::Long nMinLen = 0;
+ tools::Long nObjDst = 0;
+ tools::Long nOutHgt = 0;
+ OutputDevice* pOut = GetFirstOutputDevice();
+
+ // first determine representation boundaries
+ if (pOut != nullptr)
+ {
+ nMinLen = pOut->PixelToLogic(Size(0,50)).Height();
+ nObjDst = pOut->PixelToLogic(Size(0,20)).Height();
+
+ tools::Long nDst = pOut->PixelToLogic(Size(0,10)).Height();
+
+ nOutMin = -pOut->GetMapMode().GetOrigin().Y();
+ nOutMax = pOut->GetOutputSize().Height() - 1 + nOutMin;
+ nOutMin += nDst;
+ nOutMax -= nDst;
+
+ if (nOutMax - nOutMin < nDst)
+ {
+ nOutMin += nOutMax + 1;
+ nOutMin /= 2;
+ nOutMin -= (nDst + 1) / 2;
+ nOutMax = nOutMin + nDst;
+ }
+
+ nOutHgt = nOutMax - nOutMin;
+
+ tools::Long nTemp = nOutHgt / 4;
+ if (nTemp > nMinLen) nMinLen = nTemp;
+ }
+
+ // and then attach the marks at the top and bottom of the object
+ basegfx::B2DRange aR;
+ for(size_t nMark = 0; nMark < GetMarkedObjectCount(); ++nMark)
+ {
+ SdrObject* pMark = GetMarkedObjectByIndex(nMark);
+ basegfx::B2DPolyPolygon aXPP(pMark->TakeXorPoly());
+ aR.expand(basegfx::utils::getRange(aXPP));
+ }
+
+ basegfx::B2DPoint aCenter(aR.getCenter());
+ tools::Long nMarkHgt = basegfx::fround(aR.getHeight()) - 1;
+ tools::Long nHgt = nMarkHgt + nObjDst * 2;
+
+ if (nHgt < nMinLen) nHgt = nMinLen;
+
+ tools::Long nY1 = basegfx::fround(aCenter.getY()) - (nHgt + 1) / 2;
+ tools::Long nY2 = nY1 + nHgt;
+
+ if (pOut && (nMinLen > nOutHgt)) nMinLen = nOutHgt;
+ if (pOut)
+ {
+ if (nY1 < nOutMin)
+ {
+ nY1 = nOutMin;
+ if (nY2 < nY1 + nMinLen) nY2 = nY1 + nMinLen;
+ }
+ if (nY2 > nOutMax)
+ {
+ nY2 = nOutMax;
+ if (nY1 > nY2 - nMinLen) nY1 = nY2 - nMinLen;
+ }
+ }
+
+ maRef1.setX( basegfx::fround(aR.getMinX()) ); // Initial move axis 2/100mm to the left
+ maRef1.setY( nY1 );
+ maRef2.setX( maRef1.X() );
+ maRef2.setY( nY2 );
+
+ // Turn on marks
+ SetMarkHandles(nullptr);
+
+ //HMHif (bVis) ShowMarkHdl();
+ if (AreObjectsMarked()) MarkListHasChanged();
+
+ // Show mirror polygon IMMEDIATELY
+ const SdrHdlList &aHdlList = GetHdlList();
+ mpMirrorOverlay.reset(new Impl3DMirrorConstructOverlay(*this));
+ mpMirrorOverlay->SetMirrorAxis(aHdlList.GetHdl(SdrHdlKind::Ref1)->GetPos(), aHdlList.GetHdl(SdrHdlKind::Ref2)->GetPos());
+}
+
+// what happens with a mouse movement when the object is created?
+
+void E3dView::MovAction(const Point& rPnt)
+{
+ if(Is3DRotationCreationActive())
+ {
+ SdrHdl* pHdl = GetDragHdl();
+
+ if (pHdl)
+ {
+ SdrHdlKind eHdlKind = pHdl->GetKind();
+
+ // reacts only due to a mirror axis
+ if ((eHdlKind == SdrHdlKind::Ref1) ||
+ (eHdlKind == SdrHdlKind::Ref2) ||
+ (eHdlKind == SdrHdlKind::MirrorAxis))
+ {
+ const SdrHdlList &aHdlList = GetHdlList ();
+
+ // delete the mirrored polygon, mirrors the original and draws
+ // it anew
+ SdrView::MovAction (rPnt);
+ mpMirrorOverlay->SetMirrorAxis(
+ aHdlList.GetHdl (SdrHdlKind::Ref1)->GetPos(),
+ aHdlList.GetHdl (SdrHdlKind::Ref2)->GetPos());
+ }
+ }
+ else
+ {
+ SdrView::MovAction (rPnt);
+ }
+ }
+ else
+ {
+ SdrView::MovAction (rPnt);
+ }
+}
+
+// The End. Create object and any child objects through ImpCreate3DLathe.
+// With the parameter value sal_True (SDefault: sal_False) is simply a
+// rotation body created, without letting the user set the position of the
+// axis. It is sufficient with this call, if an object is selected.
+// (No initialization necessary)
+
+void E3dView::End3DCreation(bool bUseDefaultValuesForMirrorAxes)
+{
+ ResetCreationActive();
+
+ if(!AreObjectsMarked())
+ return;
+
+ if(bUseDefaultValuesForMirrorAxes)
+ {
+ tools::Rectangle aRect = GetAllMarkedRect();
+ if(aRect.GetWidth() <= 1)
+ aRect.SetSize(Size(500, aRect.GetHeight()));
+ if(aRect.GetHeight() <= 1)
+ aRect.SetSize(Size(aRect.GetWidth(), 500));
+
+ basegfx::B2DPoint aPnt1(aRect.Left(), -aRect.Top());
+ basegfx::B2DPoint aPnt2(aRect.Left(), -aRect.Bottom());
+
+ ConvertMarkedObjTo3D(false, aPnt1, aPnt2);
+ }
+ else
+ {
+ // Turn off helper overlay
+ // Determine from the handle positions and the displacement of
+ // the points
+ const SdrHdlList &aHdlList = GetHdlList();
+ Point aMirrorRef1 = aHdlList.GetHdl(SdrHdlKind::Ref1)->GetPos();
+ Point aMirrorRef2 = aHdlList.GetHdl(SdrHdlKind::Ref2)->GetPos();
+
+ basegfx::B2DPoint aPnt1(aMirrorRef1.X(), -aMirrorRef1.Y());
+ basegfx::B2DPoint aPnt2(aMirrorRef2.X(), -aMirrorRef2.Y());
+
+ ConvertMarkedObjTo3D(false, aPnt1, aPnt2);
+ }
+}
+
+E3dView::~E3dView ()
+{
+}
+
+void E3dView::ResetCreationActive ()
+{
+ mpMirrorOverlay.reset();
+}
+
+void E3dView::InitView ()
+{
+ mpMirrorOverlay = nullptr;
+}
+
+bool E3dView::IsBreak3DObjPossible() const
+{
+ const size_t nCount = GetMarkedObjectCount();
+
+ if (nCount > 0)
+ {
+ for (size_t i = 0; i < nCount; ++i)
+ {
+ SdrObject* pObj = GetMarkedObjectByIndex(i);
+
+ if (auto p3dObject = dynamic_cast< E3dObject* >(pObj))
+ {
+ if(!p3dObject->IsBreakObjPossible())
+ return false;
+ }
+ else
+ {
+ return false;
+ }
+ }
+ }
+ else
+ {
+ return false;
+ }
+
+ return true;
+}
+
+void E3dView::Break3DObj()
+{
+ if(!IsBreak3DObjPossible())
+ return;
+
+ // ALL selected objects are changed
+ const size_t nCount = GetMarkedObjectCount();
+
+ BegUndo(SvxResId(RID_SVX_3D_UNDO_BREAK_LATHE));
+ for(size_t a=0; a<nCount; ++a)
+ {
+ E3dObject* pObj = static_cast<E3dObject*>(GetMarkedObjectByIndex(a));
+ BreakSingle3DObj(pObj);
+ }
+ DeleteMarked();
+ EndUndo();
+}
+
+void E3dView::BreakSingle3DObj(E3dObject* pObj)
+{
+ if(dynamic_cast< const E3dScene* >(pObj) != nullptr)
+ {
+ SdrObjList* pSubList = pObj->GetSubList();
+ SdrObjListIter aIter(pSubList, SdrIterMode::Flat);
+
+ while(aIter.IsMore())
+ {
+ E3dObject* pSubObj = static_cast<E3dObject*>(aIter.Next());
+ BreakSingle3DObj(pSubObj);
+ }
+ }
+ else
+ {
+ SdrAttrObj* pNewObj = pObj->GetBreakObj().release();
+ if (pNewObj)
+ {
+ if (InsertObjectAtView(pNewObj, *GetSdrPageView(), SdrInsertFlags::DONTMARK))
+ {
+ pNewObj->SetChanged();
+ pNewObj->BroadcastObjectChange();
+ }
+ }
+ }
+}
+
+void E3dView::CheckPossibilities()
+{
+ // call parent
+ SdrView::CheckPossibilities();
+
+ // Set other flags
+ if(!(m_bGroupPossible || m_bUnGroupPossible || m_bGrpEnterPossible))
+ return;
+
+ const size_t nMarkCnt = GetMarkedObjectCount();
+ bool bCompound = false;
+ bool b3DObject = false;
+ for(size_t nObjs = 0; (nObjs < nMarkCnt) && !bCompound; ++nObjs)
+ {
+ SdrObject *pObj = GetMarkedObjectByIndex(nObjs);
+ if(dynamic_cast< const E3dCompoundObject* >(pObj))
+ bCompound = true;
+ if(dynamic_cast< const E3dObject* >(pObj))
+ b3DObject = true;
+ }
+
+ // So far: there are two or more of any objects selected. See if
+ // compound objects are involved. If yes, ban grouping.
+ if(m_bGroupPossible && bCompound)
+ m_bGroupPossible = false;
+
+ if(m_bUnGroupPossible && b3DObject)
+ m_bUnGroupPossible = false;
+
+ if(m_bGrpEnterPossible && bCompound)
+ m_bGrpEnterPossible = false;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/engine3d/view3d1.cxx b/svx/source/engine3d/view3d1.cxx
new file mode 100644
index 000000000..22ec229bf
--- /dev/null
+++ b/svx/source/engine3d/view3d1.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 <svl/itempool.hxx>
+#include <svx/svdmodel.hxx>
+#include <svx/svxids.hrc>
+#include <svx/strings.hrc>
+#include <svx/dialmgr.hxx>
+#include <svx/lathe3d.hxx>
+#include <svx/scene3d.hxx>
+#include <svx/sphere3d.hxx>
+#include <extrud3d.hxx>
+#include <svx/view3d.hxx>
+#include <svx/cube3d.hxx>
+#include <svx/xlineit0.hxx>
+#include <com/sun/star/drawing/LineStyle.hpp>
+
+void E3dView::ConvertMarkedToPolyObj()
+{
+ SdrObject* pNewObj = nullptr;
+
+ if (GetMarkedObjectCount() == 1)
+ {
+ SdrObject* pObj = GetMarkedObjectByIndex(0);
+
+ if (pObj)
+ {
+ auto pScene = dynamic_cast< const E3dScene* >(pObj);
+ if (pScene)
+ {
+ pNewObj = pScene->ConvertToPolyObj(false/*bBezier*/, false/*bLineToArea*/).release();
+ if (pNewObj)
+ {
+ BegUndo(SvxResId(RID_SVX_3D_UNDO_EXTRUDE));
+ ReplaceObjectAtView(pObj, *GetSdrPageView(), pNewObj);
+ EndUndo();
+ }
+ }
+ }
+ }
+
+ if (!pNewObj)
+ {
+ SdrView::ConvertMarkedToPolyObj();
+ }
+}
+
+static void Imp_E3dView_InorderRun3DObjects(const SdrObject* pObj, sal_uInt32& rMask)
+{
+ if(dynamic_cast< const E3dLatheObj* >(pObj) != nullptr)
+ {
+ rMask |= 0x0001;
+ }
+ else if(dynamic_cast< const E3dExtrudeObj* >(pObj) != nullptr)
+ {
+ rMask |= 0x0002;
+ }
+ else if(dynamic_cast< const E3dSphereObj* >(pObj) != nullptr)
+ {
+ rMask |= 0x0004;
+ }
+ else if(dynamic_cast< const E3dCubeObj* >(pObj) != nullptr)
+ {
+ rMask |= 0x0008;
+ }
+ else if(pObj->IsGroupObject())
+ {
+ SdrObjList* pList = pObj->GetSubList();
+ for(size_t a = 0; a < pList->GetObjCount(); ++a)
+ Imp_E3dView_InorderRun3DObjects(pList->GetObj(a), rMask);
+ }
+}
+
+SfxItemSet E3dView::Get3DAttributes() const
+{
+ // Creating itemset with corresponding field
+ SfxItemSet aSet(
+ mpModel->GetItemPool(),
+ svl::Items<SDRATTR_START, SDRATTR_END,
+ SID_ATTR_3D_INTERN, SID_ATTR_3D_INTERN>);
+
+ sal_uInt32 nSelectedItems(0);
+
+ // get attributes from all selected objects
+ MergeAttrFromMarked(aSet, false);
+
+ // calc flags for SID_ATTR_3D_INTERN
+ const SdrMarkList& rMarkList = GetMarkedObjectList();
+ const size_t nMarkCnt(rMarkList.GetMarkCount());
+
+ for(size_t a = 0; a < nMarkCnt; ++a)
+ {
+ SdrObject* pObj = GetMarkedObjectByIndex(a);
+ Imp_E3dView_InorderRun3DObjects(pObj, nSelectedItems);
+ }
+
+ // Set SID_ATTR_3D_INTERN on the status of the selected objects
+ aSet.Put(SfxUInt32Item(SID_ATTR_3D_INTERN, nSelectedItems));
+
+ // maintain default values
+ if(!nSelectedItems)
+ {
+ // Get defaults and apply
+ SfxItemSetFixed<SDRATTR_3D_FIRST, SDRATTR_3D_LAST> aDefaultSet(mpModel->GetItemPool());
+ GetAttributes(aDefaultSet);
+ aSet.Put(aDefaultSet);
+
+ // ... but no lines for 3D
+ aSet.Put(XLineStyleItem (css::drawing::LineStyle_NONE));
+
+ // new defaults for distance and focal length
+ aSet.Put(makeSvx3DDistanceItem(100));
+ aSet.Put(makeSvx3DFocalLengthItem(10000));
+ }
+
+ // return ItemSet
+ return aSet;
+}
+
+void E3dView::Set3DAttributes( const SfxItemSet& rAttr)
+{
+ sal_uInt32 nSelectedItems(0);
+
+ // #i94832# removed usage of E3DModifySceneSnapRectUpdater here.
+ // They are not needed here, they are already handled in SetAttrToMarked
+
+ // set at selected objects
+ SetAttrToMarked(rAttr, false/*bReplaceAll*/);
+
+ // old run
+ const SdrMarkList& rMarkList = GetMarkedObjectList();
+ const size_t nMarkCnt(rMarkList.GetMarkCount());
+
+ for(size_t a = 0; a < nMarkCnt; ++a)
+ {
+ SdrObject* pObj = GetMarkedObjectByIndex(a);
+ Imp_E3dView_InorderRun3DObjects(pObj, nSelectedItems);
+ }
+
+ // Maintain default values
+ if(!nSelectedItems)
+ {
+ // Set defaults
+ SfxItemSetFixed<SDRATTR_3D_FIRST, SDRATTR_3D_LAST> aDefaultSet(mpModel->GetItemPool());
+ aDefaultSet.Put(rAttr);
+ SetAttributes(aDefaultSet);
+ }
+}
+
+double E3dView::GetDefaultCamPosZ()
+{
+ return static_cast<double>(mpModel->GetItemPool().GetDefaultItem(SDRATTR_3DSCENE_DISTANCE).GetValue());
+}
+
+double E3dView::GetDefaultCamFocal()
+{
+ return static_cast<double>(mpModel->GetItemPool().GetDefaultItem(SDRATTR_3DSCENE_FOCAL_LENGTH).GetValue());
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/engine3d/viewpt3d2.cxx b/svx/source/engine3d/viewpt3d2.cxx
new file mode 100644
index 000000000..c2e55a085
--- /dev/null
+++ b/svx/source/engine3d/viewpt3d2.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 <svx/viewpt3d.hxx>
+
+Viewport3D::Viewport3D() :
+ aVRP(0, 0, 5),
+ aVPN(0, 0, 1),
+ aVUV(0, 1, 1),
+ aPRP(0, 0, 2),
+ eProjection(ProjectionType::Perspective),
+ aDeviceRect(Point(0,0), Size(-1,-1)),
+ aViewPoint (0, 0, 5000),
+ bTfValid(false)
+{
+ aViewWin.X = -1; aViewWin.Y = -1;
+ aViewWin.W = 2; aViewWin.H = 2;
+}
+
+// Set ViewWindow (in View coordinates)
+
+void Viewport3D::SetViewWindow(double fX, double fY, double fW, double fH)
+{
+ aViewWin.X = fX;
+ aViewWin.Y = fY;
+ if ( fW > 0 ) aViewWin.W = fW;
+ else aViewWin.W = 1.0;
+ if ( fH > 0 ) aViewWin.H = fH;
+ else aViewWin.H = 1.0;
+}
+
+// Returns observer position (PRP) in world coordinates
+
+const basegfx::B3DPoint& Viewport3D::GetViewPoint()
+{
+ // Calculate View transformations matrix
+ if ( !bTfValid )
+ {
+ double fV, fXupVp, fYupVp;
+ aViewPoint = aVRP + aVPN * aPRP.getZ();
+
+ // Reset to Identity matrix
+ aViewTf.identity();
+
+ // shift in the origin
+ aViewTf.translate(-aVRP.getX(), -aVRP.getY(), -aVRP.getZ());
+
+ // fV = Length of the projection of aVPN on the yz plane:
+ fV = aVPN.getYZLength();
+
+ if ( fV != 0 )
+ {
+ basegfx::B3DHomMatrix aTemp;
+ const double fSin(aVPN.getY() / fV);
+ const double fCos(aVPN.getZ() / fV);
+ aTemp.set(2, 2, fCos);
+ aTemp.set(1, 1, fCos);
+ aTemp.set(2, 1, fSin);
+ aTemp.set(1, 2, -fSin);
+ aViewTf *= aTemp;
+ }
+
+ {
+ basegfx::B3DHomMatrix aTemp;
+ const double fSin(-aVPN.getX());
+ const double fCos(fV);
+ aTemp.set(2, 2, fCos);
+ aTemp.set(0, 0, fCos);
+ aTemp.set(0, 2, fSin);
+ aTemp.set(2, 0, -fSin);
+ aViewTf *= aTemp;
+ }
+
+ // Convert X- and Y- coordinates of the view up vector to the
+ // (preliminary) view coordinate system.
+ fXupVp = aViewTf.get(0, 0) * aVUV.getX() + aViewTf.get(0, 1) * aVUV.getY() + aViewTf.get(0, 2) * aVUV.getZ();
+ fYupVp = aViewTf.get(1, 0) * aVUV.getX() + aViewTf.get(1, 1) * aVUV.getY() + aViewTf.get(1, 2) * aVUV.getZ();
+ fV = std::hypot(fXupVp, fYupVp);
+
+ if ( fV != 0 )
+ {
+ basegfx::B3DHomMatrix aTemp;
+ const double fSin(fXupVp / fV);
+ const double fCos(fYupVp / fV);
+ aTemp.set(1, 1, fCos);
+ aTemp.set(0, 0, fCos);
+ aTemp.set(1, 0, fSin);
+ aTemp.set(0, 1, -fSin);
+ aViewTf *= aTemp;
+ }
+
+ bTfValid = true;
+ }
+ return aViewPoint;
+}
+
+void Viewport3D::SetDeviceWindow(const tools::Rectangle& rRect)
+{
+ aDeviceRect = rRect;
+}
+
+// Set View Reference Point
+
+void Viewport3D::SetVRP(const basegfx::B3DPoint& rNewVRP)
+{
+ aVRP = rNewVRP;
+ bTfValid = false;
+}
+
+// Set View Plane Normal
+
+void Viewport3D::SetVPN(const basegfx::B3DVector& rNewVPN)
+{
+ aVPN = rNewVPN;
+ aVPN.normalize();
+ bTfValid = false;
+}
+
+// Set View Up Vector
+
+void Viewport3D::SetVUV(const basegfx::B3DVector& rNewVUV)
+{
+ aVUV = rNewVUV;
+ bTfValid = false;
+}
+
+// Set Center Of Projection
+
+void Viewport3D::SetPRP(const basegfx::B3DPoint& rNewPRP)
+{
+ aPRP = rNewPRP;
+ aPRP.setX(0.0);
+ aPRP.setY(0.0);
+ bTfValid = false;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/fmcomp/dbaexchange.cxx b/svx/source/fmcomp/dbaexchange.cxx
new file mode 100644
index 000000000..904740e38
--- /dev/null
+++ b/svx/source/fmcomp/dbaexchange.cxx
@@ -0,0 +1,630 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * 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/dbaexchange.hxx>
+#include <osl/diagnose.h>
+#include <com/sun/star/beans/XPropertySet.hpp>
+#include <com/sun/star/sdb/CommandType.hpp>
+#include <com/sun/star/sdbc/XConnection.hpp>
+#include <com/sun/star/sdbcx/XTablesSupplier.hpp>
+#include <fmprop.hxx>
+#include <comphelper/extract.hxx>
+#include <sot/formats.hxx>
+#include <sot/exchange.hxx>
+#include <o3tl/string_view.hxx>
+
+
+namespace svx
+{
+
+
+ using namespace ::com::sun::star::uno;
+ using namespace ::com::sun::star::beans;
+ using namespace ::com::sun::star::sdb;
+ using namespace ::com::sun::star::sdbc;
+ using namespace ::com::sun::star::lang;
+ using namespace ::com::sun::star::sdbcx;
+ using namespace ::com::sun::star::container;
+ using namespace ::com::sun::star::datatransfer;
+
+ OColumnTransferable::OColumnTransferable(ColumnTransferFormatFlags nFormats)
+ : m_nFormatFlags(nFormats)
+ {
+ }
+
+ void OColumnTransferable::setDescriptor(const ODataAccessDescriptor& rDescriptor)
+ {
+ ClearFormats();
+
+ OUString sDataSource, sDatabaseLocation, sConnectionResource, sCommand, sFieldName;
+ if ( rDescriptor.has( DataAccessDescriptorProperty::DataSource ) ) rDescriptor[ DataAccessDescriptorProperty::DataSource ] >>= sDataSource;
+ if ( rDescriptor.has( DataAccessDescriptorProperty::DatabaseLocation ) ) rDescriptor[ DataAccessDescriptorProperty::DatabaseLocation ] >>= sDatabaseLocation;
+ if ( rDescriptor.has( DataAccessDescriptorProperty::ConnectionResource ) ) rDescriptor[ DataAccessDescriptorProperty::ConnectionResource ] >>= sConnectionResource;
+ if ( rDescriptor.has( DataAccessDescriptorProperty::Command ) ) rDescriptor[ DataAccessDescriptorProperty::Command ] >>= sCommand;
+ if ( rDescriptor.has( DataAccessDescriptorProperty::ColumnName ) ) rDescriptor[ DataAccessDescriptorProperty::ColumnName ] >>= sFieldName;
+
+ sal_Int32 nCommandType = CommandType::TABLE;
+ OSL_VERIFY( rDescriptor[ DataAccessDescriptorProperty::CommandType ] >>= nCommandType );
+
+ implConstruct(
+ sDataSource.isEmpty() ? sDatabaseLocation : sDataSource,
+ sConnectionResource, nCommandType, sCommand, sFieldName );
+
+ if ( m_nFormatFlags & ColumnTransferFormatFlags::COLUMN_DESCRIPTOR )
+ {
+ if ( rDescriptor.has( DataAccessDescriptorProperty::Connection ) )
+ m_aDescriptor[ DataAccessDescriptorProperty::Connection ] = rDescriptor[ DataAccessDescriptorProperty::Connection ];
+ if ( rDescriptor.has( DataAccessDescriptorProperty::ColumnObject ) )
+ m_aDescriptor[ DataAccessDescriptorProperty::ColumnObject ] = rDescriptor[ DataAccessDescriptorProperty::ColumnObject ];
+ }
+ }
+
+ OColumnTransferable::OColumnTransferable(const Reference< XPropertySet >& _rxForm,
+ const OUString& _rFieldName, const Reference< XPropertySet >& _rxColumn,
+ const Reference< XConnection >& _rxConnection, ColumnTransferFormatFlags _nFormats)
+ :m_nFormatFlags(_nFormats)
+ {
+ OSL_ENSURE(_rxForm.is(), "OColumnTransferable::OColumnTransferable: invalid form!");
+ // collect the necessary information from the form
+ OUString sCommand;
+ sal_Int32 nCommandType = CommandType::TABLE;
+ OUString sDatasource,sURL;
+
+ bool bTryToParse = true;
+ try
+ {
+ _rxForm->getPropertyValue(FM_PROP_COMMANDTYPE) >>= nCommandType;
+ _rxForm->getPropertyValue(FM_PROP_COMMAND) >>= sCommand;
+ _rxForm->getPropertyValue(FM_PROP_DATASOURCE) >>= sDatasource;
+ _rxForm->getPropertyValue(FM_PROP_URL) >>= sURL;
+ bTryToParse = ::cppu::any2bool(_rxForm->getPropertyValue(FM_PROP_ESCAPE_PROCESSING));
+ }
+ catch(Exception&)
+ {
+ OSL_FAIL("OColumnTransferable::OColumnTransferable: could not collect essential data source attributes !");
+ }
+
+ // If the data source is an SQL-statement and simple enough (means "select <field list> from <table> where...")
+ // we are able to fake the drag information we are about to create.
+ if (bTryToParse && (CommandType::COMMAND == nCommandType))
+ {
+ try
+ {
+ Reference< XTablesSupplier > xSupTab;
+ _rxForm->getPropertyValue("SingleSelectQueryComposer") >>= xSupTab;
+
+ if(xSupTab.is())
+ {
+ Reference< XNameAccess > xNames = xSupTab->getTables();
+ if (xNames.is())
+ {
+ Sequence< OUString > aTables = xNames->getElementNames();
+ if (1 == aTables.getLength())
+ {
+ sCommand = aTables[0];
+ nCommandType = CommandType::TABLE;
+ }
+ }
+ }
+ }
+ catch(Exception&)
+ {
+ OSL_FAIL("OColumnTransferable::OColumnTransferable: could not collect essential data source attributes (part two) !");
+ }
+ }
+
+ implConstruct(sDatasource, sURL,nCommandType, sCommand, _rFieldName);
+
+ if ((m_nFormatFlags & ColumnTransferFormatFlags::COLUMN_DESCRIPTOR) == ColumnTransferFormatFlags::COLUMN_DESCRIPTOR)
+ {
+ if (_rxColumn.is())
+ m_aDescriptor[DataAccessDescriptorProperty::ColumnObject] <<= _rxColumn;
+ if (_rxConnection.is())
+ m_aDescriptor[DataAccessDescriptorProperty::Connection] <<= _rxConnection;
+ }
+ }
+
+
+ SotClipboardFormatId OColumnTransferable::getDescriptorFormatId()
+ {
+ static SotClipboardFormatId s_nFormat = static_cast<SotClipboardFormatId>(-1);
+ if (static_cast<SotClipboardFormatId>(-1) == s_nFormat)
+ {
+ s_nFormat = SotExchange::RegisterFormatName("application/x-openoffice;windows_formatname=\"dbaccess.ColumnDescriptorTransfer\"");
+ OSL_ENSURE(static_cast<SotClipboardFormatId>(-1) != s_nFormat, "OColumnTransferable::getDescriptorFormatId: bad exchange id!");
+ }
+ return s_nFormat;
+ }
+
+
+ void OColumnTransferable::implConstruct( const OUString& _rDatasource
+ ,const OUString& _rConnectionResource
+ ,const sal_Int32 _nCommandType
+ ,const OUString& _rCommand
+ , const OUString& _rFieldName)
+ {
+ const sal_Unicode cSeparator = u'\x000B';
+ const OUString sSeparator(&cSeparator, 1);
+
+ m_sCompatibleFormat.clear();
+ m_sCompatibleFormat += _rDatasource;
+ m_sCompatibleFormat += sSeparator;
+ m_sCompatibleFormat += _rCommand;
+ m_sCompatibleFormat += sSeparator;
+
+ sal_Unicode cCommandType;
+ switch (_nCommandType)
+ {
+ case CommandType::TABLE:
+ cCommandType = '0';
+ break;
+ case CommandType::QUERY:
+ cCommandType = '1';
+ break;
+ default:
+ cCommandType = '2';
+ break;
+ }
+ m_sCompatibleFormat += OUStringChar(cCommandType);
+ m_sCompatibleFormat += sSeparator;
+ m_sCompatibleFormat += _rFieldName;
+
+ m_aDescriptor.clear();
+ if ((m_nFormatFlags & ColumnTransferFormatFlags::COLUMN_DESCRIPTOR) == ColumnTransferFormatFlags::COLUMN_DESCRIPTOR)
+ {
+ m_aDescriptor.setDataSource(_rDatasource);
+ if ( !_rConnectionResource.isEmpty() )
+ m_aDescriptor[DataAccessDescriptorProperty::ConnectionResource] <<= _rConnectionResource;
+
+ m_aDescriptor[DataAccessDescriptorProperty::Command] <<= _rCommand;
+ m_aDescriptor[DataAccessDescriptorProperty::CommandType] <<= _nCommandType;
+ m_aDescriptor[DataAccessDescriptorProperty::ColumnName] <<= _rFieldName;
+ }
+ }
+
+
+ void OColumnTransferable::AddSupportedFormats()
+ {
+ if (ColumnTransferFormatFlags::CONTROL_EXCHANGE & m_nFormatFlags)
+ AddFormat(SotClipboardFormatId::SBA_CTRLDATAEXCHANGE);
+
+ if (ColumnTransferFormatFlags::FIELD_DESCRIPTOR & m_nFormatFlags)
+ AddFormat(SotClipboardFormatId::SBA_FIELDDATAEXCHANGE);
+
+ if (ColumnTransferFormatFlags::COLUMN_DESCRIPTOR & m_nFormatFlags)
+ AddFormat(getDescriptorFormatId());
+ }
+
+
+ bool OColumnTransferable::GetData( const DataFlavor& _rFlavor, const OUString& /*rDestDoc*/ )
+ {
+ const SotClipboardFormatId nFormatId = SotExchange::GetFormat(_rFlavor);
+ switch (nFormatId)
+ {
+ case SotClipboardFormatId::SBA_FIELDDATAEXCHANGE:
+ case SotClipboardFormatId::SBA_CTRLDATAEXCHANGE:
+ return SetString(m_sCompatibleFormat);
+ default: break;
+ }
+ if (nFormatId == getDescriptorFormatId())
+ return SetAny( Any( m_aDescriptor.createPropertyValueSequence() ) );
+
+ return false;
+ }
+
+
+ bool OColumnTransferable::canExtractColumnDescriptor(const DataFlavorExVector& _rFlavors, ColumnTransferFormatFlags _nFormats)
+ {
+ bool bFieldFormat = bool(_nFormats & ColumnTransferFormatFlags::FIELD_DESCRIPTOR);
+ bool bControlFormat = bool(_nFormats & ColumnTransferFormatFlags::CONTROL_EXCHANGE);
+ bool bDescriptorFormat = bool(_nFormats & ColumnTransferFormatFlags::COLUMN_DESCRIPTOR);
+ SotClipboardFormatId nFormatId = getDescriptorFormatId();
+ return std::any_of(_rFlavors.begin(), _rFlavors.end(),
+ [&](const DataFlavorEx& rCheck) {
+ return (bFieldFormat && (SotClipboardFormatId::SBA_FIELDDATAEXCHANGE == rCheck.mnSotId))
+ || (bControlFormat && (SotClipboardFormatId::SBA_CTRLDATAEXCHANGE == rCheck.mnSotId))
+ || (bDescriptorFormat && (nFormatId == rCheck.mnSotId));
+ });
+ }
+
+
+ ODataAccessDescriptor OColumnTransferable::extractColumnDescriptor(const TransferableDataHelper& _rData)
+ {
+ if (_rData.HasFormat(getDescriptorFormatId()))
+ {
+ // the object has a real descriptor object (not just the old compatible format)
+
+ // extract the any from the transferable
+ DataFlavor aFlavor;
+ bool bSuccess =
+ SotExchange::GetFormatDataFlavor(getDescriptorFormatId(), aFlavor);
+ OSL_ENSURE(bSuccess, "OColumnTransferable::extractColumnDescriptor: invalid data format (no flavor)!");
+
+ Any aDescriptor = _rData.GetAny(aFlavor, OUString());
+
+ // extract the property value sequence
+ Sequence< PropertyValue > aDescriptorProps;
+ bSuccess = aDescriptor >>= aDescriptorProps;
+ OSL_ENSURE(bSuccess, "OColumnTransferable::extractColumnDescriptor: invalid clipboard format!");
+
+ // build the real descriptor
+ return ODataAccessDescriptor(aDescriptorProps);
+ }
+
+ // only the old (compatible) format exists -> use the other extract method ...
+ OUString sDatasource, sCommand, sFieldName,sDatabaseLocation,sConnectionResource;
+ sal_Int32 nCommandType = CommandType::COMMAND;
+
+ ODataAccessDescriptor aDescriptor;
+ if (extractColumnDescriptor(_rData, sDatasource, sDatabaseLocation,sConnectionResource,nCommandType, sCommand, sFieldName))
+ {
+ // and build an own descriptor
+ if ( !sDatasource.isEmpty() )
+ aDescriptor[DataAccessDescriptorProperty::DataSource] <<= sDatasource;
+ if ( !sDatabaseLocation.isEmpty() )
+ aDescriptor[DataAccessDescriptorProperty::DatabaseLocation] <<= sDatabaseLocation;
+ if ( !sConnectionResource.isEmpty() )
+ aDescriptor[DataAccessDescriptorProperty::ConnectionResource] <<= sConnectionResource;
+
+ aDescriptor[DataAccessDescriptorProperty::Command] <<= sCommand;
+ aDescriptor[DataAccessDescriptorProperty::CommandType] <<= nCommandType;
+ aDescriptor[DataAccessDescriptorProperty::ColumnName] <<= sFieldName;
+ }
+ return aDescriptor;
+ }
+
+
+ bool OColumnTransferable::extractColumnDescriptor(const TransferableDataHelper& _rData
+ ,OUString& _rDatasource
+ ,OUString& _rDatabaseLocation
+ ,OUString& _rConnectionResource
+ ,sal_Int32& _nCommandType
+ ,OUString& _rCommand
+ ,OUString& _rFieldName)
+ {
+ if ( _rData.HasFormat(getDescriptorFormatId()) )
+ {
+ ODataAccessDescriptor aDescriptor = extractColumnDescriptor(_rData);
+ if ( aDescriptor.has(DataAccessDescriptorProperty::DataSource) )
+ aDescriptor[DataAccessDescriptorProperty::DataSource] >>= _rDatasource;
+ if ( aDescriptor.has(DataAccessDescriptorProperty::DatabaseLocation) )
+ aDescriptor[DataAccessDescriptorProperty::DatabaseLocation] >>= _rDatabaseLocation;
+ if ( aDescriptor.has(DataAccessDescriptorProperty::ConnectionResource) )
+ aDescriptor[DataAccessDescriptorProperty::ConnectionResource] >>= _rConnectionResource;
+
+ aDescriptor[DataAccessDescriptorProperty::Command] >>= _rCommand;
+ aDescriptor[DataAccessDescriptorProperty::CommandType] >>= _nCommandType;
+ aDescriptor[DataAccessDescriptorProperty::ColumnName] >>= _rFieldName;
+ return true;
+ }
+
+ // check if we have a (string) format we can use...
+ SotClipboardFormatId nRecognizedFormat = SotClipboardFormatId::NONE;
+ if (_rData.HasFormat(SotClipboardFormatId::SBA_FIELDDATAEXCHANGE))
+ nRecognizedFormat = SotClipboardFormatId::SBA_FIELDDATAEXCHANGE;
+ if (_rData.HasFormat(SotClipboardFormatId::SBA_CTRLDATAEXCHANGE))
+ nRecognizedFormat = SotClipboardFormatId::SBA_CTRLDATAEXCHANGE;
+ if (nRecognizedFormat == SotClipboardFormatId::NONE)
+ return false;
+
+ OUString sFieldDescription;
+ (void)_rData.GetString(nRecognizedFormat, sFieldDescription);
+
+ const sal_Unicode cSeparator = u'\x000B';
+ sal_Int32 nIdx{ 0 };
+ _rDatasource = sFieldDescription.getToken(0, cSeparator, nIdx);
+ _rCommand = sFieldDescription.getToken(0, cSeparator, nIdx);
+ _nCommandType = o3tl::toInt32(o3tl::getToken(sFieldDescription, 0, cSeparator, nIdx));
+ _rFieldName = sFieldDescription.getToken(0, cSeparator, nIdx);
+
+ return true;
+ }
+
+ ODataAccessObjectTransferable::ODataAccessObjectTransferable()
+ {
+ }
+
+ void ODataAccessObjectTransferable::Update(
+ const OUString& _rDatasource,
+ const sal_Int32 _nCommandType,
+ const OUString& _rCommand)
+ {
+ construct(_rDatasource,OUString(),_nCommandType,_rCommand,nullptr,(CommandType::COMMAND == _nCommandType),_rCommand);
+ }
+
+ void ODataAccessObjectTransferable::Update(
+ const OUString& _rDatasource,
+ const sal_Int32 _nCommandType,
+ const OUString& _rCommand,
+ const Reference< XConnection >& _rxConnection)
+ {
+ OSL_ENSURE(_rxConnection.is(), "Wrong Update used.!");
+ construct(_rDatasource,OUString(),_nCommandType,_rCommand,_rxConnection,(CommandType::COMMAND == _nCommandType),_rCommand);
+ }
+
+ ODataAccessObjectTransferable::ODataAccessObjectTransferable(const Reference< XPropertySet >& _rxLivingForm)
+ {
+ // collect some properties of the form
+ OUString sDatasourceName,sConnectionResource;
+ sal_Int32 nObjectType = CommandType::COMMAND;
+ OUString sObjectName;
+ Reference< XConnection > xConnection;
+ try
+ {
+ _rxLivingForm->getPropertyValue(FM_PROP_COMMANDTYPE) >>= nObjectType;
+ _rxLivingForm->getPropertyValue(FM_PROP_COMMAND) >>= sObjectName;
+ _rxLivingForm->getPropertyValue(FM_PROP_DATASOURCE) >>= sDatasourceName;
+ _rxLivingForm->getPropertyValue(FM_PROP_URL) >>= sConnectionResource;
+ _rxLivingForm->getPropertyValue(FM_PROP_ACTIVE_CONNECTION) >>= xConnection;
+ }
+ catch(Exception&)
+ {
+ OSL_FAIL("ODataAccessObjectTransferable::ODataAccessObjectTransferable: could not collect essential form attributes !");
+ return;
+ }
+
+ // check if the SQL-statement is modified
+ OUString sCompleteStatement;
+ try
+ {
+ _rxLivingForm->getPropertyValue(FM_PROP_ACTIVECOMMAND) >>= sCompleteStatement;
+ }
+ catch (const Exception&)
+ {
+ OSL_FAIL("ODataAccessObjectTransferable::ODataAccessObjectTransferable: could not collect essential form attributes (part two) !");
+ return;
+ }
+
+ construct( sDatasourceName
+ ,sConnectionResource
+ ,nObjectType
+ ,sObjectName,xConnection
+ ,CommandType::QUERY != nObjectType
+ ,sCompleteStatement);
+ }
+
+
+ void ODataAccessObjectTransferable::AddSupportedFormats()
+ {
+ sal_Int32 nObjectType = CommandType::COMMAND;
+ m_aDescriptor[DataAccessDescriptorProperty::CommandType] >>= nObjectType;
+ switch (nObjectType)
+ {
+ case CommandType::TABLE:
+ AddFormat(SotClipboardFormatId::DBACCESS_TABLE);
+ break;
+ case CommandType::QUERY:
+ AddFormat(SotClipboardFormatId::DBACCESS_QUERY);
+ break;
+ case CommandType::COMMAND:
+ AddFormat(SotClipboardFormatId::DBACCESS_COMMAND);
+ break;
+ }
+
+ if (!m_sCompatibleObjectDescription.isEmpty())
+ AddFormat(SotClipboardFormatId::SBA_DATAEXCHANGE);
+ }
+
+
+ bool ODataAccessObjectTransferable::GetData( const DataFlavor& rFlavor, const OUString& /*rDestDoc*/ )
+ {
+ SotClipboardFormatId nFormat = SotExchange::GetFormat(rFlavor);
+ switch (nFormat)
+ {
+ case SotClipboardFormatId::DBACCESS_TABLE:
+ case SotClipboardFormatId::DBACCESS_QUERY:
+ case SotClipboardFormatId::DBACCESS_COMMAND:
+ return SetAny( Any(m_aDescriptor.createPropertyValueSequence()) );
+
+ case SotClipboardFormatId::SBA_DATAEXCHANGE:
+ return SetString(m_sCompatibleObjectDescription);
+ default: break;
+ }
+ return false;
+ }
+
+
+ bool ODataAccessObjectTransferable::canExtractObjectDescriptor(const DataFlavorExVector& _rFlavors)
+ {
+ return std::any_of(_rFlavors.begin(), _rFlavors.end(),
+ [](const DataFlavorEx& rCheck) {
+ return SotClipboardFormatId::DBACCESS_TABLE == rCheck.mnSotId
+ || SotClipboardFormatId::DBACCESS_QUERY == rCheck.mnSotId
+ || SotClipboardFormatId::DBACCESS_COMMAND == rCheck.mnSotId;
+ });
+ }
+
+
+ ODataAccessDescriptor ODataAccessObjectTransferable::extractObjectDescriptor(const TransferableDataHelper& _rData)
+ {
+ SotClipboardFormatId nKnownFormatId = SotClipboardFormatId::NONE;
+ if ( _rData.HasFormat( SotClipboardFormatId::DBACCESS_TABLE ) )
+ nKnownFormatId = SotClipboardFormatId::DBACCESS_TABLE;
+ if ( _rData.HasFormat( SotClipboardFormatId::DBACCESS_QUERY ) )
+ nKnownFormatId = SotClipboardFormatId::DBACCESS_QUERY;
+ if ( _rData.HasFormat( SotClipboardFormatId::DBACCESS_COMMAND ) )
+ nKnownFormatId = SotClipboardFormatId::DBACCESS_COMMAND;
+
+ if (SotClipboardFormatId::NONE != nKnownFormatId)
+ {
+ // extract the any from the transferable
+ DataFlavor aFlavor;
+ bool bSuccess =
+ SotExchange::GetFormatDataFlavor(nKnownFormatId, aFlavor);
+ OSL_ENSURE(bSuccess, "OColumnTransferable::extractColumnDescriptor: invalid data format (no flavor)!");
+
+ Any aDescriptor = _rData.GetAny(aFlavor, OUString());
+
+ // extract the property value sequence
+ Sequence< PropertyValue > aDescriptorProps;
+ bSuccess = aDescriptor >>= aDescriptorProps;
+ OSL_ENSURE(bSuccess, "OColumnTransferable::extractColumnDescriptor: invalid clipboard format!");
+
+ // build the real descriptor
+ return ODataAccessDescriptor(aDescriptorProps);
+ }
+
+ OSL_FAIL( "OColumnTransferable::extractColumnDescriptor: unsupported formats only!" );
+ return ODataAccessDescriptor();
+ }
+
+
+ void ODataAccessObjectTransferable::addCompatibleSelectionDescription( const Sequence< Any >& _rSelRows )
+ {
+ const sal_Unicode cSeparator(11);
+ const OUString sSeparator(&cSeparator, 1);
+
+ for ( const Any& rSelRow : _rSelRows )
+ {
+ sal_Int32 nSelectedRow( 0 );
+ OSL_VERIFY( rSelRow >>= nSelectedRow );
+
+ m_sCompatibleObjectDescription += OUString::number(nSelectedRow);
+ m_sCompatibleObjectDescription += sSeparator;
+ }
+ }
+
+
+ void ODataAccessObjectTransferable::ObjectReleased()
+ {
+ m_aDescriptor.clear();
+ }
+
+ void ODataAccessObjectTransferable::construct( const OUString& _rDatasource
+ ,const OUString& _rConnectionResource
+ ,const sal_Int32 _nCommandType
+ ,const OUString& _rCommand
+ ,const Reference< XConnection >& _rxConnection
+ ,bool _bAddCommand
+ ,const OUString& _sActiveCommand)
+ {
+ m_aDescriptor.setDataSource(_rDatasource);
+ // build the descriptor (the property sequence)
+ if ( !_rConnectionResource.isEmpty() )
+ m_aDescriptor[DataAccessDescriptorProperty::ConnectionResource] <<= _rConnectionResource;
+ if ( _rxConnection.is() )
+ m_aDescriptor[DataAccessDescriptorProperty::Connection] <<= _rxConnection;
+ m_aDescriptor[DataAccessDescriptorProperty::Command] <<= _rCommand;
+ m_aDescriptor[DataAccessDescriptorProperty::CommandType] <<= _nCommandType;
+
+ // extract the single values from the sequence
+
+ OUString sObjectName = _rCommand;
+
+ // for compatibility: create a string which can be used for the SotClipboardFormatId::SBA_DATAEXCHANGE format
+
+ bool bTreatAsStatement = (CommandType::COMMAND == _nCommandType);
+ // statements are - in this old and ugly format - described as queries
+
+ const sal_Unicode cSeparator = u'\x000B';
+ const OUString sSeparator(&cSeparator, 1);
+
+ const sal_Unicode cTableMark = '1';
+ const sal_Unicode cQueryMark = '0';
+
+ // build the descriptor string
+ m_sCompatibleObjectDescription += _rDatasource;
+ m_sCompatibleObjectDescription += sSeparator;
+ m_sCompatibleObjectDescription += bTreatAsStatement ? OUString() : sObjectName;
+ m_sCompatibleObjectDescription += sSeparator;
+ switch (_nCommandType)
+ {
+ case CommandType::TABLE:
+ m_sCompatibleObjectDescription += OUStringChar(cTableMark);
+ break;
+ case CommandType::QUERY:
+ m_sCompatibleObjectDescription += OUStringChar(cQueryMark);
+ break;
+ case CommandType::COMMAND:
+ m_sCompatibleObjectDescription += OUStringChar(cQueryMark);
+ // think of it as a query
+ break;
+ }
+ m_sCompatibleObjectDescription += sSeparator;
+ m_sCompatibleObjectDescription += _bAddCommand ? _sActiveCommand : OUString();
+ m_sCompatibleObjectDescription += sSeparator;
+ }
+
+ OMultiColumnTransferable::OMultiColumnTransferable()
+ {
+ }
+
+ void OMultiColumnTransferable::setDescriptors(const Sequence< PropertyValue >& rDescriptors)
+ {
+ ClearFormats();
+ m_aDescriptors = rDescriptors;
+ }
+
+ SotClipboardFormatId OMultiColumnTransferable::getDescriptorFormatId()
+ {
+ static SotClipboardFormatId s_nFormat = static_cast<SotClipboardFormatId>(-1);
+ if (static_cast<SotClipboardFormatId>(-1) == s_nFormat)
+ {
+ s_nFormat = SotExchange::RegisterFormatName("application/x-openoffice;windows_formatname=\"dbaccess.MultipleColumnDescriptorTransfer\"");
+ OSL_ENSURE(static_cast<SotClipboardFormatId>(-1) != s_nFormat, "OColumnTransferable::getDescriptorFormatId: bad exchange id!");
+ }
+ return s_nFormat;
+ }
+
+ void OMultiColumnTransferable::AddSupportedFormats()
+ {
+ AddFormat(getDescriptorFormatId());
+ }
+
+ bool OMultiColumnTransferable::GetData( const DataFlavor& _rFlavor, const OUString& /*rDestDoc*/ )
+ {
+ const SotClipboardFormatId nFormatId = SotExchange::GetFormat(_rFlavor);
+ if (nFormatId == getDescriptorFormatId())
+ {
+ return SetAny( Any( m_aDescriptors ) );
+ }
+
+ return false;
+ }
+
+ bool OMultiColumnTransferable::canExtractDescriptor(const DataFlavorExVector& _rFlavors)
+ {
+ const SotClipboardFormatId nFormatId = getDescriptorFormatId();
+ return std::all_of(_rFlavors.begin(), _rFlavors.end(),
+ [&nFormatId](const DataFlavorEx& rCheck) { return nFormatId == rCheck.mnSotId; });
+ }
+
+ Sequence< PropertyValue > OMultiColumnTransferable::extractDescriptor(const TransferableDataHelper& _rData)
+ {
+ Sequence< PropertyValue > aList;
+ if (_rData.HasFormat(getDescriptorFormatId()))
+ {
+ // extract the any from the transferable
+ DataFlavor aFlavor;
+ bool bSuccess =
+ SotExchange::GetFormatDataFlavor(getDescriptorFormatId(), aFlavor);
+ OSL_ENSURE(bSuccess, "OColumnTransferable::extractColumnDescriptor: invalid data format (no flavor)!");
+
+ _rData.GetAny(aFlavor, OUString()) >>= aList;
+ } // if (_rData.HasFormat(getDescriptorFormatId()))
+ return aList;
+ }
+
+ void OMultiColumnTransferable::ObjectReleased()
+ {
+ m_aDescriptors.realloc(0);
+ }
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/fmcomp/dbaobjectex.cxx b/svx/source/fmcomp/dbaobjectex.cxx
new file mode 100644
index 000000000..e3d22d2c3
--- /dev/null
+++ b/svx/source/fmcomp/dbaobjectex.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 <svx/dbaobjectex.hxx>
+#include <osl/diagnose.h>
+#include <com/sun/star/sdbcx/XTablesSupplier.hpp>
+#include <com/sun/star/sdb/XSQLQueryComposerFactory.hpp>
+#include <com/sun/star/ucb/XContent.hpp>
+#include <sot/formats.hxx>
+#include <sot/exchange.hxx>
+
+
+namespace svx
+{
+
+
+ using namespace ::com::sun::star::uno;
+ using namespace ::com::sun::star::beans;
+ using namespace ::com::sun::star::sdb;
+ using namespace ::com::sun::star::lang;
+ using namespace ::com::sun::star::ucb;
+ using namespace ::com::sun::star::sdbcx;
+ using namespace ::com::sun::star::container;
+ using namespace ::com::sun::star::datatransfer;
+
+ OComponentTransferable::OComponentTransferable()
+ {
+ }
+
+ void OComponentTransferable::Update(const OUString& rDatasourceOrLocation
+ ,const Reference< XContent>& xContent)
+ {
+ ClearFormats();
+
+ m_aDescriptor.setDataSource(rDatasourceOrLocation);
+ m_aDescriptor[DataAccessDescriptorProperty::Component] <<= xContent;
+
+ AddSupportedFormats();
+ }
+
+ SotClipboardFormatId OComponentTransferable::getDescriptorFormatId(bool _bExtractForm)
+ {
+ static SotClipboardFormatId s_nReportFormat = static_cast<SotClipboardFormatId>(-1);
+ static SotClipboardFormatId s_nFormFormat = static_cast<SotClipboardFormatId>(-1);
+ if ( _bExtractForm && static_cast<SotClipboardFormatId>(-1) == s_nFormFormat )
+ {
+ s_nFormFormat = SotExchange::RegisterFormatName("application/x-openoffice;windows_formatname=\"dbaccess.FormComponentDescriptorTransfer\"");
+ OSL_ENSURE(static_cast<SotClipboardFormatId>(-1) != s_nFormFormat, "OComponentTransferable::getDescriptorFormatId: bad exchange id!");
+ }
+ else if ( !_bExtractForm && static_cast<SotClipboardFormatId>(-1) == s_nReportFormat)
+ {
+ s_nReportFormat = SotExchange::RegisterFormatName("application/x-openoffice;windows_formatname=\"dbaccess.ReportComponentDescriptorTransfer\"");
+ OSL_ENSURE(static_cast<SotClipboardFormatId>(-1) != s_nReportFormat, "OComponentTransferable::getDescriptorFormatId: bad exchange id!");
+ }
+ return _bExtractForm ? s_nFormFormat : s_nReportFormat;
+ }
+
+
+ void OComponentTransferable::AddSupportedFormats()
+ {
+ bool bForm = true;
+ try
+ {
+ Reference<XPropertySet> xProp;
+ m_aDescriptor[DataAccessDescriptorProperty::Component] >>= xProp;
+ if ( xProp.is() )
+ xProp->getPropertyValue("IsForm") >>= bForm;
+ }
+ catch(const Exception&)
+ {}
+ AddFormat(getDescriptorFormatId(bForm));
+ }
+
+
+ bool OComponentTransferable::GetData( const DataFlavor& _rFlavor, const OUString& /*rDestDoc*/ )
+ {
+ const SotClipboardFormatId nFormatId = SotExchange::GetFormat(_rFlavor);
+ if ( nFormatId == getDescriptorFormatId(true) || nFormatId == getDescriptorFormatId(false) )
+ return SetAny( Any( m_aDescriptor.createPropertyValueSequence() ) );
+
+ return false;
+ }
+
+
+ bool OComponentTransferable::canExtractComponentDescriptor(const DataFlavorExVector& _rFlavors, bool _bForm )
+ {
+ SotClipboardFormatId nFormatId = getDescriptorFormatId(_bForm);
+ return std::any_of(_rFlavors.begin(), _rFlavors.end(),
+ [&nFormatId](const DataFlavorEx& rCheck) { return nFormatId == rCheck.mnSotId; });
+ }
+
+
+ ODataAccessDescriptor OComponentTransferable::extractComponentDescriptor(const TransferableDataHelper& _rData)
+ {
+ bool bForm = _rData.HasFormat(getDescriptorFormatId(true));
+ if ( bForm || _rData.HasFormat(getDescriptorFormatId(false)) )
+ {
+ // the object has a real descriptor object (not just the old compatible format)
+
+ // extract the any from the transferable
+ DataFlavor aFlavor;
+ bool bSuccess =
+ SotExchange::GetFormatDataFlavor(getDescriptorFormatId(bForm), aFlavor);
+ OSL_ENSURE(bSuccess, "OComponentTransferable::extractColumnDescriptor: invalid data format (no flavor)!");
+
+ Any aDescriptor = _rData.GetAny(aFlavor, OUString());
+
+ // extract the property value sequence
+ Sequence< PropertyValue > aDescriptorProps;
+ bSuccess = aDescriptor >>= aDescriptorProps;
+ OSL_ENSURE(bSuccess, "OComponentTransferable::extractColumnDescriptor: invalid clipboard format!");
+
+ // build the real descriptor
+ return ODataAccessDescriptor(aDescriptorProps);
+ }
+
+ return ODataAccessDescriptor();
+ }
+
+
+}
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/fmcomp/fmgridcl.cxx b/svx/source/fmcomp/fmgridcl.cxx
new file mode 100644
index 000000000..aacac128e
--- /dev/null
+++ b/svx/source/fmcomp/fmgridcl.cxx
@@ -0,0 +1,2096 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * 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/fmgridif.hxx>
+#include <fmprop.hxx>
+#include <svx/fmtools.hxx>
+#include <fmservs.hxx>
+#include <fmurl.hxx>
+#include <formcontrolfactory.hxx>
+#include <gridcell.hxx>
+#include <gridcols.hxx>
+#include <svx/dbaexchange.hxx>
+#include <svx/dialmgr.hxx>
+#include <svx/strings.hrc>
+#include <svx/fmgridcl.hxx>
+#include <svx/svxdlg.hxx>
+#include <svx/svxids.hrc>
+#include <bitmaps.hlst>
+
+#include <com/sun/star/form/XConfirmDeleteListener.hpp>
+#include <com/sun/star/form/XFormComponent.hpp>
+#include <com/sun/star/form/XGridColumnFactory.hpp>
+#include <com/sun/star/io/XPersistObject.hpp>
+#include <com/sun/star/sdb/CommandType.hpp>
+#include <com/sun/star/sdb/RowChangeAction.hpp>
+#include <com/sun/star/sdb/XQueriesSupplier.hpp>
+#include <com/sun/star/sdbc/DataType.hpp>
+#include <com/sun/star/sdbc/SQLException.hpp>
+#include <com/sun/star/sdbc/XPreparedStatement.hpp>
+#include <com/sun/star/sdbc/XResultSetUpdate.hpp>
+#include <com/sun/star/sdbcx/XColumnsSupplier.hpp>
+#include <com/sun/star/sdbcx/XDeleteRows.hpp>
+#include <com/sun/star/sdbcx/XTablesSupplier.hpp>
+#include <com/sun/star/util/XNumberFormats.hpp>
+#include <com/sun/star/util/XNumberFormatsSupplier.hpp>
+#include <com/sun/star/util/URLTransformer.hpp>
+#include <com/sun/star/util/XURLTransformer.hpp>
+#include <com/sun/star/view/XSelectionSupplier.hpp>
+#include <comphelper/processfactory.hxx>
+#include <comphelper/property.hxx>
+#include <comphelper/string.hxx>
+#include <comphelper/types.hxx>
+#include <connectivity/dbtools.hxx>
+#include <sfx2/dispatch.hxx>
+#include <sfx2/viewfrm.hxx>
+#include <svl/eitem.hxx>
+#include <vcl/commandevent.hxx>
+#include <vcl/svapp.hxx>
+#include <tools/debug.hxx>
+#include <tools/multisel.hxx>
+#include <tools/diagnose_ex.h>
+#include <vcl/help.hxx>
+#include <vcl/settings.hxx>
+#include <sal/log.hxx>
+#include <i18nlangtag/languagetag.hxx>
+#include <memory>
+#include <string_view>
+
+using namespace ::com::sun::star::uno;
+using namespace ::com::sun::star::view;
+using namespace ::com::sun::star::beans;
+using namespace ::com::sun::star::lang;
+using namespace ::com::sun::star::sdbcx;
+using namespace ::com::sun::star::sdbc;
+using namespace ::com::sun::star::sdb;
+using namespace ::com::sun::star::form;
+using namespace ::com::sun::star::util;
+using namespace ::com::sun::star::container;
+using namespace ::cppu;
+using namespace ::svxform;
+using namespace ::svx;
+using namespace ::dbtools;
+
+struct FmGridHeaderData
+{
+ ODataAccessDescriptor aDropData;
+ Point aDropPosPixel;
+ sal_Int8 nDropAction;
+ Reference< XInterface > xDroppedStatement;
+ Reference< XInterface > xDroppedResultSet;
+};
+
+static void InsertMenuItem(weld::Menu& rMenu, int nMenuPos, std::string_view id, const OUString& rText, const OUString& rImgId)
+{
+ rMenu.insert(nMenuPos, OUString::fromUtf8(id), rText, &rImgId, nullptr, nullptr, TRISTATE_INDET);
+}
+
+FmGridHeader::FmGridHeader( BrowseBox* pParent, WinBits nWinBits)
+ :EditBrowserHeader(pParent, nWinBits)
+ ,DropTargetHelper(this)
+ ,m_pImpl(new FmGridHeaderData)
+{
+}
+
+FmGridHeader::~FmGridHeader()
+{
+ disposeOnce();
+}
+
+void FmGridHeader::dispose()
+{
+ m_pImpl.reset();
+ DropTargetHelper::dispose();
+ svt::EditBrowserHeader::dispose();
+}
+
+sal_uInt16 FmGridHeader::GetModelColumnPos(sal_uInt16 nId) const
+{
+ return static_cast<FmGridControl*>(GetParent())->GetModelColumnPos(nId);
+}
+
+void FmGridHeader::notifyColumnSelect(sal_uInt16 nColumnId)
+{
+ sal_uInt16 nPos = GetModelColumnPos(nColumnId);
+ Reference< XIndexAccess > xColumns = static_cast<FmGridControl*>(GetParent())->GetPeer()->getColumns();
+ if ( nPos < xColumns->getCount() )
+ {
+ Reference< XSelectionSupplier > xSelSupplier(xColumns, UNO_QUERY);
+ if ( xSelSupplier.is() )
+ {
+ Reference< XPropertySet > xColumn;
+ xColumns->getByIndex(nPos) >>= xColumn;
+ xSelSupplier->select(Any(xColumn));
+ }
+ }
+}
+
+void FmGridHeader::Select()
+{
+ EditBrowserHeader::Select();
+ notifyColumnSelect(GetCurItemId());
+}
+
+void FmGridHeader::RequestHelp( const HelpEvent& rHEvt )
+{
+ sal_uInt16 nItemId = GetItemId( ScreenToOutputPixel( rHEvt.GetMousePosPixel() ) );
+ if ( nItemId )
+ {
+ if ( rHEvt.GetMode() & (HelpEventMode::QUICK | HelpEventMode::BALLOON) )
+ {
+ tools::Rectangle aItemRect = GetItemRect( nItemId );
+ Point aPt = OutputToScreenPixel( aItemRect.TopLeft() );
+ aItemRect.SetLeft( aPt.X() );
+ aItemRect.SetTop( aPt.Y() );
+ aPt = OutputToScreenPixel( aItemRect.BottomRight() );
+ aItemRect.SetRight( aPt.X() );
+ aItemRect.SetBottom( aPt.Y() );
+
+ sal_uInt16 nPos = GetModelColumnPos(nItemId);
+ Reference< css::container::XIndexContainer > xColumns(static_cast<FmGridControl*>(GetParent())->GetPeer()->getColumns());
+ try
+ {
+ Reference< css::beans::XPropertySet > xColumn(xColumns->getByIndex(nPos),UNO_QUERY);
+ OUString aHelpText;
+ xColumn->getPropertyValue(FM_PROP_HELPTEXT) >>= aHelpText;
+ if ( aHelpText.isEmpty() )
+ xColumn->getPropertyValue(FM_PROP_DESCRIPTION) >>= aHelpText;
+ if ( !aHelpText.isEmpty() )
+ {
+ if ( rHEvt.GetMode() & HelpEventMode::BALLOON )
+ Help::ShowBalloon( this, aItemRect.Center(), aItemRect, aHelpText );
+ else
+ Help::ShowQuickHelp( this, aItemRect, aHelpText );
+ return;
+ }
+ }
+ catch(Exception&)
+ {
+ return;
+ }
+ }
+ }
+ EditBrowserHeader::RequestHelp( rHEvt );
+}
+
+sal_Int8 FmGridHeader::AcceptDrop( const AcceptDropEvent& rEvt )
+{
+ // drop allowed in design mode only
+ if (!static_cast<FmGridControl*>(GetParent())->IsDesignMode())
+ return DND_ACTION_NONE;
+
+ // search for recognized formats
+ const DataFlavorExVector& rFlavors = GetDataFlavorExVector();
+ if (OColumnTransferable::canExtractColumnDescriptor(rFlavors, ColumnTransferFormatFlags::COLUMN_DESCRIPTOR | ColumnTransferFormatFlags::FIELD_DESCRIPTOR))
+ return rEvt.mnAction;
+
+ return DND_ACTION_NONE;
+}
+
+sal_Int8 FmGridHeader::ExecuteDrop( const ExecuteDropEvent& _rEvt )
+{
+ if (!static_cast<FmGridControl*>(GetParent())->IsDesignMode())
+ return DND_ACTION_NONE;
+
+ TransferableDataHelper aDroppedData(_rEvt.maDropEvent.Transferable);
+
+ // check the formats
+ bool bColumnDescriptor = OColumnTransferable::canExtractColumnDescriptor(aDroppedData.GetDataFlavorExVector(), ColumnTransferFormatFlags::COLUMN_DESCRIPTOR);
+ bool bFieldDescriptor = OColumnTransferable::canExtractColumnDescriptor(aDroppedData.GetDataFlavorExVector(), ColumnTransferFormatFlags::FIELD_DESCRIPTOR);
+ if (!bColumnDescriptor && !bFieldDescriptor)
+ {
+ OSL_FAIL("FmGridHeader::ExecuteDrop: should never have reached this (no extractable format)!");
+ return DND_ACTION_NONE;
+ }
+
+ // extract the descriptor
+ OUString sDatasource, sCommand, sFieldName,sDatabaseLocation;
+ sal_Int32 nCommandType = CommandType::COMMAND;
+ Reference< XPreparedStatement > xStatement;
+ Reference< XResultSet > xResultSet;
+ Reference< XPropertySet > xField;
+ Reference< XConnection > xConnection;
+
+ ODataAccessDescriptor aColumn = OColumnTransferable::extractColumnDescriptor(aDroppedData);
+ if (aColumn.has(DataAccessDescriptorProperty::DataSource)) aColumn[DataAccessDescriptorProperty::DataSource] >>= sDatasource;
+ if (aColumn.has(DataAccessDescriptorProperty::DatabaseLocation)) aColumn[DataAccessDescriptorProperty::DatabaseLocation] >>= sDatabaseLocation;
+ if (aColumn.has(DataAccessDescriptorProperty::Command)) aColumn[DataAccessDescriptorProperty::Command] >>= sCommand;
+ if (aColumn.has(DataAccessDescriptorProperty::CommandType)) aColumn[DataAccessDescriptorProperty::CommandType] >>= nCommandType;
+ if (aColumn.has(DataAccessDescriptorProperty::ColumnName)) aColumn[DataAccessDescriptorProperty::ColumnName] >>= sFieldName;
+ if (aColumn.has(DataAccessDescriptorProperty::ColumnObject))aColumn[DataAccessDescriptorProperty::ColumnObject] >>= xField;
+ if (aColumn.has(DataAccessDescriptorProperty::Connection)) aColumn[DataAccessDescriptorProperty::Connection] >>= xConnection;
+
+ if ( sFieldName.isEmpty()
+ || sCommand.isEmpty()
+ || ( sDatasource.isEmpty()
+ && sDatabaseLocation.isEmpty()
+ && !xConnection.is()
+ )
+ )
+ {
+ OSL_FAIL( "FmGridHeader::ExecuteDrop: somebody started a nonsense drag operation!!" );
+ return DND_ACTION_NONE;
+ }
+
+ try
+ {
+ // need a connection
+ if (!xConnection.is())
+ { // the transferable did not contain the connection -> build an own one
+ try
+ {
+ OUString sSignificantSource( sDatasource.isEmpty() ? sDatabaseLocation : sDatasource );
+ xConnection = getConnection_withFeedback(sSignificantSource, OUString(), OUString(),
+ static_cast<FmGridControl*>(GetParent())->getContext(), nullptr );
+ }
+ catch(NoSuchElementException&)
+ { // allowed, means sDatasource isn't a valid data source name...
+ }
+ catch(Exception&)
+ {
+ OSL_FAIL("FmGridHeader::ExecuteDrop: could not retrieve the database access object !");
+ }
+
+ if (!xConnection.is())
+ {
+ OSL_FAIL("FmGridHeader::ExecuteDrop: could not retrieve the database access object !");
+ return DND_ACTION_NONE;
+ }
+ }
+
+ // try to obtain the column object
+ if (!xField.is())
+ {
+#ifdef DBG_UTIL
+ Reference< XServiceInfo > xServiceInfo(xConnection, UNO_QUERY);
+ DBG_ASSERT(xServiceInfo.is() && xServiceInfo->supportsService(SRV_SDB_CONNECTION), "FmGridHeader::ExecuteDrop: invalid connection (no database access connection !)");
+#endif
+
+ Reference< XNameAccess > xFields;
+ switch (nCommandType)
+ {
+ case CommandType::TABLE:
+ {
+ Reference< XTablesSupplier > xSupplyTables(xConnection, UNO_QUERY);
+ Reference< XColumnsSupplier > xSupplyColumns;
+ xSupplyTables->getTables()->getByName(sCommand) >>= xSupplyColumns;
+ xFields = xSupplyColumns->getColumns();
+ }
+ break;
+ case CommandType::QUERY:
+ {
+ Reference< XQueriesSupplier > xSupplyQueries(xConnection, UNO_QUERY);
+ Reference< XColumnsSupplier > xSupplyColumns;
+ xSupplyQueries->getQueries()->getByName(sCommand) >>= xSupplyColumns;
+ xFields = xSupplyColumns->getColumns();
+ }
+ break;
+ default:
+ {
+ xStatement = xConnection->prepareStatement(sCommand);
+ // not interested in any results
+
+ Reference< XPropertySet > xStatProps(xStatement,UNO_QUERY);
+ xStatProps->setPropertyValue("MaxRows", Any(sal_Int32(0)));
+
+ xResultSet = xStatement->executeQuery();
+ Reference< XColumnsSupplier > xSupplyCols(xResultSet, UNO_QUERY);
+ if (xSupplyCols.is())
+ xFields = xSupplyCols->getColumns();
+ }
+ }
+
+ if (xFields.is() && xFields->hasByName(sFieldName))
+ xFields->getByName(sFieldName) >>= xField;
+
+ if (!xField.is())
+ {
+ ::comphelper::disposeComponent(xStatement);
+ return DND_ACTION_NONE;
+ }
+ }
+
+ // do the drop asynchronously
+ // (85957 - UI actions within the drop are not allowed, but we want to open a popup menu)
+ m_pImpl->aDropData = aColumn;
+ m_pImpl->aDropData[DataAccessDescriptorProperty::Connection] <<= xConnection;
+ m_pImpl->aDropData[DataAccessDescriptorProperty::ColumnObject] <<= xField;
+
+ m_pImpl->nDropAction = _rEvt.mnAction;
+ m_pImpl->aDropPosPixel = _rEvt.maPosPixel;
+ m_pImpl->xDroppedStatement = xStatement;
+ m_pImpl->xDroppedResultSet = xResultSet;
+
+ PostUserEvent(LINK(this, FmGridHeader, OnAsyncExecuteDrop), nullptr, true);
+ }
+ catch (Exception&)
+ {
+ TOOLS_WARN_EXCEPTION("svx", "caught an exception while creatin' the column !");
+ ::comphelper::disposeComponent(xStatement);
+ return DND_ACTION_NONE;
+ }
+
+ return DND_ACTION_LINK;
+}
+
+IMPL_LINK_NOARG( FmGridHeader, OnAsyncExecuteDrop, void*, void )
+{
+ OUString sCommand, sFieldName,sURL;
+ sal_Int32 nCommandType = CommandType::COMMAND;
+ Reference< XPropertySet > xField;
+ Reference< XConnection > xConnection;
+
+ OUString sDatasource = m_pImpl->aDropData.getDataSource();
+ if ( sDatasource.isEmpty() && m_pImpl->aDropData.has(DataAccessDescriptorProperty::ConnectionResource) )
+ m_pImpl->aDropData[DataAccessDescriptorProperty::ConnectionResource] >>= sURL;
+ m_pImpl->aDropData[DataAccessDescriptorProperty::Command] >>= sCommand;
+ m_pImpl->aDropData[DataAccessDescriptorProperty::CommandType] >>= nCommandType;
+ m_pImpl->aDropData[DataAccessDescriptorProperty::ColumnName] >>= sFieldName;
+ m_pImpl->aDropData[DataAccessDescriptorProperty::Connection] >>= xConnection;
+ m_pImpl->aDropData[DataAccessDescriptorProperty::ColumnObject] >>= xField;
+
+ try
+ {
+ // need number formats
+ Reference< XNumberFormatsSupplier > xSupplier = getNumberFormats(xConnection, true);
+ Reference< XNumberFormats > xNumberFormats;
+ if (xSupplier.is())
+ xNumberFormats = xSupplier->getNumberFormats();
+ if (!xNumberFormats.is())
+ {
+ ::comphelper::disposeComponent(m_pImpl->xDroppedResultSet);
+ ::comphelper::disposeComponent(m_pImpl->xDroppedStatement);
+ return;
+ }
+
+ // The field now needs two pieces of information:
+ // a.) Name of the field for label and ControlSource
+ // b.) FormatKey, to determine which field is to be created
+ sal_Int32 nDataType = 0;
+ xField->getPropertyValue(FM_PROP_FIELDTYPE) >>= nDataType;
+ // these datatypes can not be processed in Gridcontrol
+ switch (nDataType)
+ {
+ case DataType::BLOB:
+ case DataType::LONGVARBINARY:
+ case DataType::BINARY:
+ case DataType::VARBINARY:
+ case DataType::OTHER:
+ ::comphelper::disposeComponent(m_pImpl->xDroppedResultSet);
+ ::comphelper::disposeComponent(m_pImpl->xDroppedStatement);
+ return;
+ }
+
+ // Creating the column
+ Reference< XIndexContainer > xCols(static_cast<FmGridControl*>(GetParent())->GetPeer()->getColumns());
+ Reference< XGridColumnFactory > xFactory(xCols, UNO_QUERY);
+
+ sal_uInt16 nColId = GetItemId(m_pImpl->aDropPosPixel);
+ // insert position, always before the current column
+ sal_uInt16 nPos = GetModelColumnPos(nColId);
+ Reference< XPropertySet > xCol, xSecondCol;
+
+ // Create Column based on type, default textfield
+ std::vector<OString> aPossibleTypes;
+ std::vector<OUString> aImgResId;
+ std::vector<TranslateId> aStrResId;
+
+ switch (nDataType)
+ {
+ case DataType::BIT:
+ case DataType::BOOLEAN:
+ aPossibleTypes.emplace_back(FM_COL_CHECKBOX);
+ aImgResId.emplace_back(RID_SVXBMP_CHECKBOX);
+ aStrResId.emplace_back(RID_STR_PROPTITLE_CHECKBOX);
+ break;
+ case DataType::TINYINT:
+ case DataType::SMALLINT:
+ case DataType::INTEGER:
+ aPossibleTypes.emplace_back(FM_COL_NUMERICFIELD);
+ aImgResId.emplace_back(RID_SVXBMP_NUMERICFIELD);
+ aStrResId.emplace_back(RID_STR_PROPTITLE_NUMERICFIELD);
+ aPossibleTypes.emplace_back(FM_COL_FORMATTEDFIELD);
+ aImgResId.emplace_back(RID_SVXBMP_FORMATTEDFIELD);
+ aStrResId.emplace_back(RID_STR_PROPTITLE_FORMATTED);
+ break;
+ case DataType::REAL:
+ case DataType::DOUBLE:
+ case DataType::NUMERIC:
+ case DataType::DECIMAL:
+ aPossibleTypes.emplace_back(FM_COL_FORMATTEDFIELD);
+ aImgResId.emplace_back(RID_SVXBMP_FORMATTEDFIELD);
+ aStrResId.emplace_back(RID_STR_PROPTITLE_FORMATTED);
+ aPossibleTypes.emplace_back(FM_COL_NUMERICFIELD);
+ aImgResId.emplace_back(RID_SVXBMP_NUMERICFIELD);
+ aStrResId.emplace_back(RID_STR_PROPTITLE_NUMERICFIELD);
+ break;
+ case DataType::TIMESTAMP:
+ aPossibleTypes.emplace_back("dateandtimefield");
+ aImgResId.emplace_back(RID_SVXBMP_DATE_N_TIME_FIELDS);
+ aStrResId.emplace_back(RID_STR_DATE_AND_TIME);
+ aPossibleTypes.emplace_back(FM_COL_DATEFIELD);
+ aImgResId.emplace_back(RID_SVXBMP_DATEFIELD);
+ aStrResId.emplace_back(RID_STR_PROPTITLE_DATEFIELD);
+ aPossibleTypes.emplace_back(FM_COL_TIMEFIELD);
+ aImgResId.emplace_back(RID_SVXBMP_TIMEFIELD);
+ aStrResId.emplace_back(RID_STR_PROPTITLE_TIMEFIELD);
+ aPossibleTypes.emplace_back(FM_COL_FORMATTEDFIELD);
+ aImgResId.emplace_back(RID_SVXBMP_FORMATTEDFIELD);
+ aStrResId.emplace_back(RID_STR_PROPTITLE_FORMATTED);
+ break;
+ case DataType::DATE:
+ aPossibleTypes.emplace_back(FM_COL_DATEFIELD);
+ aImgResId.emplace_back(RID_SVXBMP_DATEFIELD);
+ aStrResId.emplace_back(RID_STR_PROPTITLE_DATEFIELD);
+ aPossibleTypes.emplace_back(FM_COL_FORMATTEDFIELD);
+ aImgResId.emplace_back(RID_SVXBMP_FORMATTEDFIELD);
+ aStrResId.emplace_back(RID_STR_PROPTITLE_FORMATTED);
+ break;
+ case DataType::TIME:
+ aPossibleTypes.emplace_back(FM_COL_TIMEFIELD);
+ aImgResId.emplace_back(RID_SVXBMP_TIMEFIELD);
+ aStrResId.emplace_back(RID_STR_PROPTITLE_TIMEFIELD);
+ aPossibleTypes.emplace_back(FM_COL_FORMATTEDFIELD);
+ aImgResId.emplace_back(RID_SVXBMP_FORMATTEDFIELD);
+ aStrResId.emplace_back(RID_STR_PROPTITLE_FORMATTED);
+ break;
+ case DataType::CHAR:
+ case DataType::VARCHAR:
+ case DataType::LONGVARCHAR:
+ default:
+ aPossibleTypes.emplace_back(FM_COL_TEXTFIELD);
+ aImgResId.emplace_back(RID_SVXBMP_EDITBOX);
+ aStrResId.emplace_back(RID_STR_PROPTITLE_EDIT);
+ aPossibleTypes.emplace_back(FM_COL_FORMATTEDFIELD);
+ aImgResId.emplace_back(RID_SVXBMP_FORMATTEDFIELD);
+ aStrResId.emplace_back(RID_STR_PROPTITLE_FORMATTED);
+ break;
+ }
+ // if it's a currency field, a "currency field" option
+ try
+ {
+ if ( ::comphelper::hasProperty(FM_PROP_ISCURRENCY, xField)
+ && ::comphelper::getBOOL(xField->getPropertyValue(FM_PROP_ISCURRENCY)))
+ {
+ aPossibleTypes.insert(aPossibleTypes.begin(), FM_COL_CURRENCYFIELD);
+ aImgResId.insert(aImgResId.begin(), RID_SVXBMP_CURRENCYFIELD);
+ aStrResId.insert(aStrResId.begin(), RID_STR_PROPTITLE_CURRENCYFIELD);
+ }
+ }
+ catch (const Exception&)
+ {
+ TOOLS_WARN_EXCEPTION("svx", "");
+ }
+
+ assert(aPossibleTypes.size() == aImgResId.size());
+
+ bool bDateNTimeCol = false;
+ if (!aPossibleTypes.empty())
+ {
+ OString sPreferredType = aPossibleTypes[0];
+ if ((m_pImpl->nDropAction == DND_ACTION_LINK) && (aPossibleTypes.size() > 1))
+ {
+ std::unique_ptr<weld::Builder> xBuilder(Application::CreateBuilder(nullptr, "svx/ui/colsmenu.ui"));
+ std::unique_ptr<weld::Menu> xTypeMenu(xBuilder->weld_menu("insertmenu"));
+
+ int nMenuPos = 0;
+ std::vector<OString>::const_iterator iter;
+ std::vector<TranslateId>::const_iterator striter;
+ std::vector<OUString>::const_iterator imgiter;
+ for (iter = aPossibleTypes.begin(), imgiter = aImgResId.begin(), striter = aStrResId.begin();
+ iter != aPossibleTypes.end(); ++iter, ++striter, ++imgiter)
+ {
+ InsertMenuItem(*xTypeMenu, nMenuPos++, *iter, SvxResId(*striter), *imgiter);
+ }
+
+ ::tools::Rectangle aRect(m_pImpl->aDropPosPixel, Size(1,1));
+ weld::Window* pParent = weld::GetPopupParent(*this, aRect);
+ OString sResult = xTypeMenu->popup_at_rect(pParent, aRect);
+ if (!sResult.isEmpty())
+ sPreferredType = sResult;
+ }
+
+ bDateNTimeCol = sPreferredType == "dateandtimefield";
+ sal_uInt16 nColCount = bDateNTimeCol ? 2 : 1;
+ OUString sFieldService;
+ while (nColCount--)
+ {
+ if (bDateNTimeCol)
+ sPreferredType = nColCount ? FM_COL_DATEFIELD : FM_COL_TIMEFIELD;
+
+ sFieldService = OUString::fromUtf8(sPreferredType);
+ Reference< XPropertySet > xThisRoundCol;
+ if ( !sFieldService.isEmpty() )
+ xThisRoundCol = xFactory->createColumn(sFieldService);
+ if (nColCount)
+ xSecondCol = xThisRoundCol;
+ else
+ xCol = xThisRoundCol;
+ }
+ }
+
+ if (!xCol.is() || (bDateNTimeCol && !xSecondCol.is()))
+ {
+ ::comphelper::disposeComponent(xCol); // in case only the creation of the second column failed
+ ::comphelper::disposeComponent(m_pImpl->xDroppedResultSet);
+ ::comphelper::disposeComponent(m_pImpl->xDroppedStatement);
+ return;
+ }
+
+ if (bDateNTimeCol)
+ {
+ OUString sTimePostfix(SvxResId(RID_STR_POSTFIX_TIME));
+ xCol->setPropertyValue(FM_PROP_LABEL, Any( OUString( sFieldName + sTimePostfix ) ) );
+
+ OUString sDatePostfix(SvxResId( RID_STR_POSTFIX_DATE));
+ xSecondCol->setPropertyValue(FM_PROP_LABEL, Any( OUString( sFieldName + sDatePostfix ) ) );
+ }
+ else
+ xCol->setPropertyValue(FM_PROP_LABEL, Any(sFieldName));
+
+ // insert now
+ Any aElement;
+ aElement <<= xCol;
+
+ xCols->insertByIndex(nPos, aElement);
+
+ FormControlFactory aControlFactory;
+ aControlFactory.initializeControlModel( DocumentClassification::classifyHostDocument( xCols ), xCol );
+ FormControlFactory::initializeFieldDependentProperties( xField, xCol, xNumberFormats );
+
+ xCol->setPropertyValue(FM_PROP_CONTROLSOURCE, Any(sFieldName));
+ if ( xSecondCol.is() )
+ xSecondCol->setPropertyValue(FM_PROP_CONTROLSOURCE, Any(sFieldName));
+
+ if (bDateNTimeCol)
+ {
+ OUString aPostfix[] = {
+ SvxResId(RID_STR_POSTFIX_DATE),
+ SvxResId(RID_STR_POSTFIX_TIME)
+ };
+
+ for ( size_t i=0; i<2; ++i )
+ {
+ OUString sPurePostfix = comphelper::string::stripStart(aPostfix[i], ' ');
+ sPurePostfix = comphelper::string::stripStart(sPurePostfix, '(');
+ sPurePostfix = comphelper::string::stripEnd(sPurePostfix, ')');
+ OUString sRealName = sFieldName + "_" + sPurePostfix;
+ if (i)
+ xSecondCol->setPropertyValue(FM_PROP_NAME, Any(sRealName));
+ else
+ xCol->setPropertyValue(FM_PROP_NAME, Any(sRealName));
+ }
+ }
+ else
+ xCol->setPropertyValue(FM_PROP_NAME, Any(sFieldName));
+
+ if (bDateNTimeCol)
+ {
+ aElement <<= xSecondCol;
+ xCols->insertByIndex(nPos == sal_uInt16(-1) ? nPos : ++nPos, aElement);
+ }
+
+ // is the component::Form tied to the database?
+ Reference< XFormComponent > xFormCp(xCols, UNO_QUERY);
+ Reference< XPropertySet > xForm(xFormCp->getParent(), UNO_QUERY);
+ if (xForm.is())
+ {
+ if (::comphelper::getString(xForm->getPropertyValue(FM_PROP_DATASOURCE)).isEmpty())
+ {
+ if ( !sDatasource.isEmpty() )
+ xForm->setPropertyValue(FM_PROP_DATASOURCE, Any(sDatasource));
+ else
+ xForm->setPropertyValue(FM_PROP_URL, Any(sURL));
+ }
+
+ if (::comphelper::getString(xForm->getPropertyValue(FM_PROP_COMMAND)).isEmpty())
+ {
+ xForm->setPropertyValue(FM_PROP_COMMAND, Any(sCommand));
+ Any aCommandType;
+ switch (nCommandType)
+ {
+ case CommandType::TABLE:
+ aCommandType <<= sal_Int32(CommandType::TABLE);
+ break;
+ case CommandType::QUERY:
+ aCommandType <<= sal_Int32(CommandType::QUERY);
+ break;
+ default:
+ aCommandType <<= sal_Int32(CommandType::COMMAND);
+ xForm->setPropertyValue(FM_PROP_ESCAPE_PROCESSING, css::uno::Any(2 == nCommandType));
+ break;
+ }
+ xForm->setPropertyValue(FM_PROP_COMMANDTYPE, aCommandType);
+ }
+ }
+ }
+ catch (Exception&)
+ {
+ TOOLS_WARN_EXCEPTION("svx", "caught an exception while creatin' the column !");
+ ::comphelper::disposeComponent(m_pImpl->xDroppedResultSet);
+ ::comphelper::disposeComponent(m_pImpl->xDroppedStatement);
+ return;
+ }
+
+ ::comphelper::disposeComponent(m_pImpl->xDroppedResultSet);
+ ::comphelper::disposeComponent(m_pImpl->xDroppedStatement);
+}
+
+void FmGridHeader::PreExecuteColumnContextMenu(sal_uInt16 nColId, weld::Menu& rMenu,
+ weld::Menu& rInsertMenu, weld::Menu& rChangeMenu,
+ weld::Menu& rShowMenu)
+{
+ bool bDesignMode = static_cast<FmGridControl*>(GetParent())->IsDesignMode();
+
+ Reference< css::container::XIndexContainer > xCols(static_cast<FmGridControl*>(GetParent())->GetPeer()->getColumns());
+ // Building of the Insert Menu
+ // mark the column if nColId != HEADERBAR_ITEM_NOTFOUND
+ if(nColId > 0)
+ {
+ sal_uInt16 nPos2 = GetModelColumnPos(nColId);
+
+ Reference< css::container::XIndexContainer > xColumns(static_cast<FmGridControl*>(GetParent())->GetPeer()->getColumns());
+ Reference< css::beans::XPropertySet> xColumn( xColumns->getByIndex(nPos2), css::uno::UNO_QUERY);
+ Reference< css::view::XSelectionSupplier > xSelSupplier(xColumns, UNO_QUERY);
+ if (xSelSupplier.is())
+ xSelSupplier->select(Any(xColumn));
+ }
+
+ // insert position, always before the current column
+ sal_uInt16 nPos = GetModelColumnPos(nColId);
+ bool bMarked = nColId && static_cast<FmGridControl*>(GetParent())->isColumnMarked(nColId);
+
+ if (bDesignMode)
+ {
+ int nMenuPos = 0;
+ InsertMenuItem(rInsertMenu, nMenuPos++, FM_COL_TEXTFIELD, SvxResId(RID_STR_PROPTITLE_EDIT), RID_SVXBMP_EDITBOX);
+ InsertMenuItem(rInsertMenu, nMenuPos++, FM_COL_CHECKBOX, SvxResId(RID_STR_PROPTITLE_CHECKBOX), RID_SVXBMP_CHECKBOX);
+ InsertMenuItem(rInsertMenu, nMenuPos++, FM_COL_COMBOBOX, SvxResId(RID_STR_PROPTITLE_COMBOBOX), RID_SVXBMP_COMBOBOX);
+ InsertMenuItem(rInsertMenu, nMenuPos++, FM_COL_LISTBOX, SvxResId(RID_STR_PROPTITLE_LISTBOX), RID_SVXBMP_LISTBOX);
+ InsertMenuItem(rInsertMenu, nMenuPos++, FM_COL_DATEFIELD, SvxResId(RID_STR_PROPTITLE_DATEFIELD), RID_SVXBMP_DATEFIELD);
+ InsertMenuItem(rInsertMenu, nMenuPos++, FM_COL_TIMEFIELD, SvxResId(RID_STR_PROPTITLE_TIMEFIELD), RID_SVXBMP_TIMEFIELD);
+ InsertMenuItem(rInsertMenu, nMenuPos++, FM_COL_NUMERICFIELD, SvxResId(RID_STR_PROPTITLE_NUMERICFIELD), RID_SVXBMP_NUMERICFIELD);
+ InsertMenuItem(rInsertMenu, nMenuPos++, FM_COL_CURRENCYFIELD, SvxResId(RID_STR_PROPTITLE_CURRENCYFIELD), RID_SVXBMP_CURRENCYFIELD);
+ InsertMenuItem(rInsertMenu, nMenuPos++, FM_COL_PATTERNFIELD, SvxResId(RID_STR_PROPTITLE_PATTERNFIELD), RID_SVXBMP_PATTERNFIELD);
+ InsertMenuItem(rInsertMenu, nMenuPos++, FM_COL_FORMATTEDFIELD, SvxResId(RID_STR_PROPTITLE_FORMATTED), RID_SVXBMP_FORMATTEDFIELD);
+ }
+
+ if (xCols.is() && nColId)
+ {
+ Reference< css::beans::XPropertySet > xPropSet( xCols->getByIndex(nPos), css::uno::UNO_QUERY);
+
+ Reference< css::io::XPersistObject > xServiceQuestion(xPropSet, UNO_QUERY);
+ sal_Int32 nColType = xServiceQuestion.is() ? getColumnTypeByModelName(xServiceQuestion->getServiceName()) : 0;
+ if (nColType == TYPE_TEXTFIELD)
+ { // edit fields and formatted fields have the same service name, thus getColumnTypeByModelName returns TYPE_TEXTFIELD
+ // in both cases. And as columns don't have a css::lang::XServiceInfo interface, we have to distinguish both
+ // types via the existence of special properties
+ if (xPropSet.is())
+ {
+ Reference< css::beans::XPropertySetInfo > xPropsInfo = xPropSet->getPropertySetInfo();
+ if (xPropsInfo.is() && xPropsInfo->hasPropertyByName(FM_PROP_FORMATSSUPPLIER))
+ nColType = TYPE_FORMATTEDFIELD;
+ }
+ }
+
+ if (bDesignMode)
+ {
+ int nMenuPos = 0;
+ if (nColType != TYPE_TEXTFIELD)
+ InsertMenuItem(rChangeMenu, nMenuPos++, FM_COL_TEXTFIELD"1", SvxResId(RID_STR_PROPTITLE_EDIT), RID_SVXBMP_EDITBOX);
+ if (nColType != TYPE_CHECKBOX)
+ InsertMenuItem(rChangeMenu, nMenuPos++, FM_COL_CHECKBOX"1", SvxResId(RID_STR_PROPTITLE_CHECKBOX), RID_SVXBMP_CHECKBOX);
+ if (nColType != TYPE_COMBOBOX)
+ InsertMenuItem(rChangeMenu, nMenuPos++, FM_COL_COMBOBOX"1", SvxResId(RID_STR_PROPTITLE_COMBOBOX), RID_SVXBMP_COMBOBOX);
+ if (nColType != TYPE_LISTBOX)
+ InsertMenuItem(rChangeMenu, nMenuPos++, FM_COL_LISTBOX"1", SvxResId(RID_STR_PROPTITLE_LISTBOX), RID_SVXBMP_LISTBOX);
+ if (nColType != TYPE_DATEFIELD)
+ InsertMenuItem(rChangeMenu, nMenuPos++, FM_COL_DATEFIELD"1", SvxResId(RID_STR_PROPTITLE_DATEFIELD), RID_SVXBMP_DATEFIELD);
+ if (nColType != TYPE_TIMEFIELD)
+ InsertMenuItem(rChangeMenu, nMenuPos++, FM_COL_TIMEFIELD"1", SvxResId(RID_STR_PROPTITLE_TIMEFIELD), RID_SVXBMP_TIMEFIELD);
+ if (nColType != TYPE_NUMERICFIELD)
+ InsertMenuItem(rChangeMenu, nMenuPos++, FM_COL_NUMERICFIELD"1", SvxResId(RID_STR_PROPTITLE_NUMERICFIELD), RID_SVXBMP_NUMERICFIELD);
+ if (nColType != TYPE_CURRENCYFIELD)
+ InsertMenuItem(rChangeMenu, nMenuPos++, FM_COL_CURRENCYFIELD"1", SvxResId(RID_STR_PROPTITLE_CURRENCYFIELD), RID_SVXBMP_CURRENCYFIELD);
+ if (nColType != TYPE_PATTERNFIELD)
+ InsertMenuItem(rChangeMenu, nMenuPos++, FM_COL_PATTERNFIELD"1", SvxResId(RID_STR_PROPTITLE_PATTERNFIELD), RID_SVXBMP_PATTERNFIELD);
+ if (nColType != TYPE_FORMATTEDFIELD)
+ InsertMenuItem(rChangeMenu, nMenuPos++, FM_COL_FORMATTEDFIELD"1", SvxResId(RID_STR_PROPTITLE_FORMATTED), RID_SVXBMP_FORMATTEDFIELD);
+ }
+
+
+ rMenu.set_visible("change", bDesignMode && bMarked && xCols.is());
+ rMenu.set_sensitive("change", bDesignMode && bMarked && xCols.is());
+ }
+ else
+ {
+ rMenu.set_visible("change", false);
+ rMenu.set_sensitive("change", false);
+ }
+
+ rMenu.set_visible("insert", bDesignMode && xCols.is());
+ rMenu.set_sensitive("insert", bDesignMode && xCols.is());
+ rMenu.set_visible("delete", bDesignMode && bMarked && xCols.is());
+ rMenu.set_sensitive("delete", bDesignMode && bMarked && xCols.is());
+ rMenu.set_visible("column", bDesignMode && bMarked && xCols.is());
+ rMenu.set_sensitive("column", bDesignMode && bMarked && xCols.is());
+
+ sal_uInt16 nHiddenCols = 0;
+ if (xCols.is())
+ {
+ // check for hidden cols
+ Reference< css::beans::XPropertySet > xCurCol;
+ Any aHidden,aName;
+ for (sal_Int32 i=0; i<xCols->getCount(); ++i)
+ {
+ xCurCol.set(xCols->getByIndex(i), css::uno::UNO_QUERY);
+ DBG_ASSERT(xCurCol.is(), "FmGridHeader::PreExecuteColumnContextMenu : the Peer has invalid columns !");
+ aHidden = xCurCol->getPropertyValue(FM_PROP_HIDDEN);
+ DBG_ASSERT(aHidden.getValueType().getTypeClass() == TypeClass_BOOLEAN,
+ "FmGridHeader::PreExecuteColumnContextMenu : the property 'hidden' should be boolean !");
+ if (::comphelper::getBOOL(aHidden))
+ {
+ // put the column name into the 'show col' menu
+ if (nHiddenCols < 16)
+ {
+ // (only the first 16 items to keep the menu rather small)
+ aName = xCurCol->getPropertyValue(FM_PROP_LABEL);
+ // the ID is arbitrary, but should be unique within the whole menu
+ rMenu.insert(nHiddenCols, OUString::number(nHiddenCols + 1), ::comphelper::getString(aName),
+ nullptr, nullptr, nullptr, TRISTATE_INDET);
+ }
+ ++nHiddenCols;
+ }
+ }
+ }
+ rShowMenu.set_visible("more", xCols.is() && (nHiddenCols > 16));
+ rMenu.set_visible("show", xCols.is() && (nHiddenCols > 0));
+ rMenu.set_sensitive("show", xCols.is() && (nHiddenCols > 0));
+
+ // allow the 'hide column' item ?
+ bool bAllowHide = bMarked; // a column is marked
+ bAllowHide = bAllowHide || (!bDesignMode && (nPos != sal_uInt16(-1))); // OR we are in alive mode and have hit a column
+ bAllowHide = bAllowHide && xCols.is(); // AND we have a column container
+ bAllowHide = bAllowHide && (xCols->getCount()-nHiddenCols > 1); // AND there are at least two visible columns
+ rMenu.set_visible("hide", bAllowHide);
+ rMenu.set_sensitive("hide", bAllowHide);
+
+ if (!bMarked)
+ return;
+
+ SfxViewFrame* pCurrentFrame = SfxViewFrame::Current();
+ // ask the bindings of the current view frame (which should be the one we're residing in) for the state
+ if (pCurrentFrame)
+ {
+ std::unique_ptr<SfxBoolItem> pItem;
+ SfxItemState eState = pCurrentFrame->GetBindings().QueryState(SID_FM_CTL_PROPERTIES, pItem);
+
+ if (eState >= SfxItemState::DEFAULT && pItem != nullptr)
+ {
+ bool bChecked = pItem && pItem->GetValue();
+ rMenu.set_active("column", bChecked);
+ }
+ }
+}
+
+namespace {
+
+enum InspectorAction { eOpenInspector, eCloseInspector, eUpdateInspector, eNone };
+
+}
+
+void FmGridHeader::PostExecuteColumnContextMenu(sal_uInt16 nColId, const weld::Menu& rMenu, const OString& rExecutionResult)
+{
+ Reference< css::container::XIndexContainer > xCols(static_cast<FmGridControl*>(GetParent())->GetPeer()->getColumns());
+ sal_uInt16 nPos = GetModelColumnPos(nColId);
+
+ OUString aFieldType;
+ bool bReplace = false;
+ InspectorAction eInspectorAction = eNone;
+
+ if (rExecutionResult == "delete")
+ {
+ Reference< XInterface > xCol(
+ xCols->getByIndex(nPos), css::uno::UNO_QUERY);
+ xCols->removeByIndex(nPos);
+ ::comphelper::disposeComponent(xCol);
+ }
+ else if (rExecutionResult == "hide")
+ {
+ Reference< css::beans::XPropertySet > xCurCol( xCols->getByIndex(nPos), css::uno::UNO_QUERY);
+ xCurCol->setPropertyValue(FM_PROP_HIDDEN, Any(true));
+ }
+ else if (rExecutionResult == "column")
+ {
+ eInspectorAction = rMenu.get_active("column") ? eOpenInspector : eCloseInspector;
+ }
+ else if (rExecutionResult.startsWith(FM_COL_TEXTFIELD))
+ {
+ if (rExecutionResult != FM_COL_TEXTFIELD)
+ bReplace = true;
+ aFieldType = FM_COL_TEXTFIELD;
+ }
+ else if (rExecutionResult.startsWith(FM_COL_COMBOBOX))
+ {
+ if (rExecutionResult != FM_COL_COMBOBOX)
+ bReplace = true;
+ aFieldType = FM_COL_COMBOBOX;
+ }
+ else if (rExecutionResult.startsWith(FM_COL_LISTBOX))
+ {
+ if (rExecutionResult != FM_COL_LISTBOX)
+ bReplace = true;
+ aFieldType = FM_COL_LISTBOX;
+ }
+ else if (rExecutionResult.startsWith(FM_COL_CHECKBOX))
+ {
+ if (rExecutionResult != FM_COL_CHECKBOX)
+ bReplace = true;
+ aFieldType = FM_COL_CHECKBOX;
+ }
+ else if (rExecutionResult.startsWith(FM_COL_DATEFIELD))
+ {
+ if (rExecutionResult != FM_COL_DATEFIELD)
+ bReplace = true;
+ aFieldType = FM_COL_DATEFIELD;
+ }
+ else if (rExecutionResult.startsWith(FM_COL_TIMEFIELD))
+ {
+ if (rExecutionResult != FM_COL_TIMEFIELD)
+ bReplace = true;
+ aFieldType = FM_COL_TIMEFIELD;
+ }
+ else if (rExecutionResult.startsWith(FM_COL_NUMERICFIELD))
+ {
+ if (rExecutionResult != FM_COL_NUMERICFIELD)
+ bReplace = true;
+ aFieldType = FM_COL_NUMERICFIELD;
+ }
+ else if (rExecutionResult.startsWith(FM_COL_CURRENCYFIELD))
+ {
+ if (rExecutionResult != FM_COL_CURRENCYFIELD)
+ bReplace = true;
+ aFieldType = FM_COL_CURRENCYFIELD;
+ }
+ else if (rExecutionResult.startsWith(FM_COL_PATTERNFIELD))
+ {
+ if (rExecutionResult != FM_COL_PATTERNFIELD)
+ bReplace = true;
+ aFieldType = FM_COL_PATTERNFIELD;
+ }
+ else if (rExecutionResult.startsWith(FM_COL_FORMATTEDFIELD))
+ {
+ if (rExecutionResult != FM_COL_FORMATTEDFIELD)
+ bReplace = true;
+ aFieldType = FM_COL_FORMATTEDFIELD;
+ }
+ else if (rExecutionResult == "more")
+ {
+ SvxAbstractDialogFactory* pFact = SvxAbstractDialogFactory::Create();
+ ScopedVclPtr<AbstractFmShowColsDialog> pDlg(pFact->CreateFmShowColsDialog(GetFrameWeld()));
+ pDlg->SetColumns(xCols);
+ pDlg->Execute();
+ }
+ else if (rExecutionResult == "all")
+ {
+ // just iterate through all the cols ...
+ Reference< css::beans::XPropertySet > xCurCol;
+ for (sal_Int32 i=0; i<xCols->getCount(); ++i)
+ {
+ xCurCol.set(xCols->getByIndex(i), css::uno::UNO_QUERY);
+ xCurCol->setPropertyValue(FM_PROP_HIDDEN, Any(false));
+ }
+ // TODO : there must be a more clever way to do this...
+ // with the above the view is updated after every single model update ...
+ }
+ else if (!rExecutionResult.isEmpty())
+ {
+ sal_Int32 nExecutionResult = rExecutionResult.toInt32();
+ if (nExecutionResult>0 && nExecutionResult<=16)
+ {
+ // it was a "show column/<colname>" command (there are at most 16 such items)
+ // search the nExecutionResult'th hidden col
+ Reference< css::beans::XPropertySet > xCurCol;
+ for (sal_Int32 i=0; i<xCols->getCount() && nExecutionResult; ++i)
+ {
+ xCurCol.set(xCols->getByIndex(i), css::uno::UNO_QUERY);
+ Any aHidden = xCurCol->getPropertyValue(FM_PROP_HIDDEN);
+ if (::comphelper::getBOOL(aHidden))
+ if (!--nExecutionResult)
+ {
+ xCurCol->setPropertyValue(FM_PROP_HIDDEN, Any(false));
+ break;
+ }
+ }
+ }
+ }
+
+ if ( !aFieldType.isEmpty() )
+ {
+ try
+ {
+ Reference< XGridColumnFactory > xFactory( xCols, UNO_QUERY_THROW );
+ Reference< XPropertySet > xNewCol( xFactory->createColumn( aFieldType ), UNO_SET_THROW );
+
+ if ( bReplace )
+ {
+ // rescue over a few properties
+ Reference< XPropertySet > xReplaced( xCols->getByIndex( nPos ), UNO_QUERY );
+
+ TransferFormComponentProperties(
+ xReplaced, xNewCol, Application::GetSettings().GetUILanguageTag().getLocale() );
+
+ xCols->replaceByIndex( nPos, Any( xNewCol ) );
+ ::comphelper::disposeComponent( xReplaced );
+
+ eInspectorAction = eUpdateInspector;
+ }
+ else
+ {
+ FormControlFactory factory;
+
+ OUString sLabel = FormControlFactory::getDefaultUniqueName_ByComponentType(
+ Reference< XNameAccess >( xCols, UNO_QUERY_THROW ), xNewCol );
+ xNewCol->setPropertyValue( FM_PROP_LABEL, Any( sLabel ) );
+ xNewCol->setPropertyValue( FM_PROP_NAME, Any( sLabel ) );
+
+ factory.initializeControlModel( DocumentClassification::classifyHostDocument( xCols ), xNewCol );
+
+ xCols->insertByIndex( nPos, Any( xNewCol ) );
+ }
+ }
+ catch( const Exception& )
+ {
+ DBG_UNHANDLED_EXCEPTION("svx");
+ }
+ }
+
+ SfxViewFrame* pCurrentFrame = SfxViewFrame::Current();
+ OSL_ENSURE( pCurrentFrame, "FmGridHeader::PostExecuteColumnContextMenu: no view frame -> no bindings -> no property browser!" );
+ if ( !pCurrentFrame )
+ return;
+
+ if ( eInspectorAction == eUpdateInspector )
+ {
+ if ( !pCurrentFrame->HasChildWindow( SID_FM_SHOW_PROPERTIES ) )
+ eInspectorAction = eNone;
+ }
+
+ if ( eInspectorAction != eNone )
+ {
+ SfxBoolItem aShowItem( SID_FM_SHOW_PROPERTIES, eInspectorAction != eCloseInspector );
+
+ pCurrentFrame->GetBindings().GetDispatcher()->ExecuteList(
+ SID_FM_SHOW_PROPERTY_BROWSER, SfxCallMode::ASYNCHRON,
+ { &aShowItem });
+ }
+}
+
+void FmGridHeader::triggerColumnContextMenu( const ::Point& _rPreferredPos )
+{
+ // the affected col
+ sal_uInt16 nColId = GetItemId( _rPreferredPos );
+
+ // the menu
+ std::unique_ptr<weld::Builder> xBuilder(Application::CreateBuilder(nullptr, "svx/ui/colsmenu.ui"));
+ std::unique_ptr<weld::Menu> xContextMenu(xBuilder->weld_menu("menu"));
+ std::unique_ptr<weld::Menu> xInsertMenu(xBuilder->weld_menu("insertmenu"));
+ std::unique_ptr<weld::Menu> xChangeMenu(xBuilder->weld_menu("changemenu"));
+ std::unique_ptr<weld::Menu> xShowMenu(xBuilder->weld_menu("showmenu"));
+
+ // let derivatives modify the menu
+ PreExecuteColumnContextMenu(nColId, *xContextMenu, *xInsertMenu, *xChangeMenu, *xShowMenu);
+
+ bool bEmpty = true;
+ for (int i = 0, nCount = xContextMenu->n_children(); i < nCount; ++i)
+ {
+ bEmpty = !xContextMenu->get_sensitive(xContextMenu->get_id(i));
+ if (!bEmpty)
+ break;
+ }
+ if (bEmpty)
+ return;
+
+ // execute the menu
+ ::tools::Rectangle aRect(_rPreferredPos, Size(1,1));
+ weld::Window* pParent = weld::GetPopupParent(*this, aRect);
+ OString sResult = xContextMenu->popup_at_rect(pParent, aRect);
+
+ // let derivatives handle the result
+ PostExecuteColumnContextMenu(nColId, *xContextMenu, sResult);
+}
+
+void FmGridHeader::Command(const CommandEvent& rEvt)
+{
+ switch (rEvt.GetCommand())
+ {
+ case CommandEventId::ContextMenu:
+ {
+ if (!rEvt.IsMouseEvent())
+ return;
+
+ triggerColumnContextMenu( rEvt.GetMousePosPixel() );
+ }
+ break;
+ default:
+ EditBrowserHeader::Command(rEvt);
+ }
+}
+
+FmGridControl::FmGridControl(
+ const Reference< css::uno::XComponentContext >& _rxContext,
+ vcl::Window* pParent,
+ FmXGridPeer* _pPeer,
+ WinBits nBits)
+ :DbGridControl(_rxContext, pParent, nBits)
+ ,m_pPeer(_pPeer)
+ ,m_nCurrentSelectedColumn(-1)
+ ,m_nMarkedColumnId(BROWSER_INVALIDID)
+ ,m_bSelecting(false)
+ ,m_bInColumnMove(false)
+{
+ EnableInteractiveRowHeight( );
+}
+
+void FmGridControl::Command(const CommandEvent& _rEvt)
+{
+ if ( CommandEventId::ContextMenu == _rEvt.GetCommand() )
+ {
+ FmGridHeader* pMyHeader = static_cast< FmGridHeader* >( GetHeaderBar() );
+ if ( pMyHeader && !_rEvt.IsMouseEvent() )
+ { // context menu requested by keyboard
+ if ( 1 == GetSelectColumnCount() || IsDesignMode() )
+ {
+ sal_uInt16 nSelId = GetColumnId(
+ sal::static_int_cast< sal_uInt16 >( FirstSelectedColumn() ) );
+ ::tools::Rectangle aColRect( GetFieldRectPixel( 0, nSelId, false ) );
+
+ Point aRelativePos( pMyHeader->ScreenToOutputPixel( OutputToScreenPixel( aColRect.TopCenter() ) ) );
+ pMyHeader->triggerColumnContextMenu(aRelativePos);
+
+ // handled
+ return;
+ }
+ }
+ }
+
+ DbGridControl::Command( _rEvt );
+}
+
+// css::beans::XPropertyChangeListener
+void FmGridControl::propertyChange(const css::beans::PropertyChangeEvent& evt)
+{
+ if (evt.PropertyName == FM_PROP_ROWCOUNT)
+ {
+ // if we're not in the main thread call AdjustRows asynchronously
+ implAdjustInSolarThread(true);
+ return;
+ }
+
+ const DbGridRowRef& xRow = GetCurrentRow();
+ // no adjustment of the properties is carried out during positioning
+ Reference<XPropertySet> xSet(evt.Source,UNO_QUERY);
+ if (!(xRow.is() && (::cppu::any2bool(xSet->getPropertyValue(FM_PROP_ISNEW))|| CompareBookmark(getDataSource()->getBookmark(), xRow->GetBookmark()))))
+ return;
+
+ if (evt.PropertyName == FM_PROP_ISMODIFIED)
+ {
+ // modified or clean ?
+ GridRowStatus eStatus = ::comphelper::getBOOL(evt.NewValue) ? GridRowStatus::Modified : GridRowStatus::Clean;
+ if (eStatus != xRow->GetStatus())
+ {
+ xRow->SetStatus(eStatus);
+ SolarMutexGuard aGuard;
+ RowModified(GetCurrentPos());
+ }
+ }
+}
+
+void FmGridControl::SetDesignMode(bool bMode)
+{
+ bool bOldMode = IsDesignMode();
+ DbGridControl::SetDesignMode(bMode);
+ if (bOldMode == bMode)
+ return;
+
+ if (!bMode)
+ {
+ // cancel selection
+ markColumn(USHRT_MAX);
+ }
+ else
+ {
+ Reference< css::container::XIndexContainer > xColumns(GetPeer()->getColumns());
+ Reference< css::view::XSelectionSupplier > xSelSupplier(xColumns, UNO_QUERY);
+ if (xSelSupplier.is())
+ {
+ Any aSelection = xSelSupplier->getSelection();
+ Reference< css::beans::XPropertySet > xColumn;
+ if (aSelection.getValueType().getTypeClass() == TypeClass_INTERFACE)
+ xColumn.set(aSelection, css::uno::UNO_QUERY);
+ Reference< XInterface > xCurrent;
+ for (sal_Int32 i=0; i<xColumns->getCount(); ++i)
+ {
+ xCurrent.set(xColumns->getByIndex(i), css::uno::UNO_QUERY);
+ if (xCurrent == xColumn)
+ {
+ markColumn(GetColumnIdFromModelPos(i));
+ break;
+ }
+ }
+ }
+ }
+}
+
+void FmGridControl::DeleteSelectedRows()
+{
+ if (!m_pSeekCursor)
+ return;
+
+ // how many rows are selected?
+ sal_Int32 nSelectedRows = GetSelectRowCount();
+
+ // the current line should be deleted but it is currently in edit mode
+ if ( IsCurrentAppending() )
+ return;
+ // is the insert row selected
+ if (GetEmptyRow().is() && IsRowSelected(GetRowCount() - 1))
+ nSelectedRows -= 1;
+
+ // nothing to do
+ if (nSelectedRows <= 0)
+ return;
+
+ // try to confirm the delete
+ Reference< css::frame::XDispatchProvider > xDispatcher = static_cast<css::frame::XDispatchProvider*>(GetPeer());
+ if (xDispatcher.is())
+ {
+ css::util::URL aUrl;
+ aUrl.Complete = FMURL_CONFIRM_DELETION;
+ Reference< css::util::XURLTransformer > xTransformer(
+ css::util::URLTransformer::create(::comphelper::getProcessComponentContext()) );
+ xTransformer->parseStrict( aUrl );
+
+ Reference< css::frame::XDispatch > xDispatch = xDispatcher->queryDispatch(aUrl, OUString(), 0);
+ Reference< css::form::XConfirmDeleteListener > xConfirm(xDispatch, UNO_QUERY);
+ if (xConfirm.is())
+ {
+ css::sdb::RowChangeEvent aEvent;
+ aEvent.Source = Reference< XInterface >(*getDataSource());
+ aEvent.Rows = nSelectedRows;
+ aEvent.Action = css::sdb::RowChangeAction::DELETE;
+ if (!xConfirm->confirmDelete(aEvent))
+ return;
+ }
+ }
+
+ const MultiSelection* pRowSelection = GetSelection();
+ if ( pRowSelection && pRowSelection->IsAllSelected() )
+ {
+ BeginCursorAction();
+ CursorWrapper* pCursor = getDataSource();
+ Reference< XResultSetUpdate > xUpdateCursor(Reference< XInterface >(*pCursor), UNO_QUERY);
+ try
+ {
+ pCursor->beforeFirst();
+ while( pCursor->next() )
+ xUpdateCursor->deleteRow();
+
+ SetUpdateMode(false);
+ SetNoSelection();
+
+ xUpdateCursor->moveToInsertRow();
+ }
+ catch(const Exception&)
+ {
+ TOOLS_WARN_EXCEPTION("svx", "Exception caught while deleting rows!");
+ }
+ // adapt to the data cursor
+ AdjustDataSource(true);
+ EndCursorAction();
+ SetUpdateMode(true);
+ }
+ else
+ {
+ Reference< css::sdbcx::XDeleteRows > xDeleteThem(Reference< XInterface >(*getDataSource()), UNO_QUERY);
+
+ // collect the bookmarks of the selected rows
+ Sequence < Any> aBookmarks = getSelectionBookmarks();
+
+ // determine the next row to position after deletion
+ Any aBookmark;
+ bool bNewPos = false;
+ // if the current row isn't selected we take the row as row after deletion
+ OSL_ENSURE( GetCurrentRow().is(), "FmGridControl::DeleteSelectedRows: no current row here?" );
+ // crash reports suggest it can happen we don't have a current row - how?
+ // #154303# / 2008-04-23 / frank.schoenheit@sun.com
+ if ( !IsRowSelected( GetCurrentPos() ) && !IsCurrentAppending() && GetCurrentRow().is() )
+ {
+ aBookmark = GetCurrentRow()->GetBookmark();
+ bNewPos = true;
+ }
+ else
+ {
+ // we look for the first row after the selected block for selection
+ tools::Long nIdx = LastSelectedRow() + 1;
+ if (nIdx < GetRowCount() - 1)
+ {
+ // there is a next row to position on
+ if (SeekCursor(nIdx))
+ {
+ GetSeekRow()->SetState(m_pSeekCursor.get(), true);
+
+ bNewPos = true;
+ // if it's not the row for inserting we keep the bookmark
+ if (!IsInsertionRow(nIdx))
+ aBookmark = m_pSeekCursor->getBookmark();
+ }
+ }
+ else
+ {
+ // we look for the first row before the selected block for selection after deletion
+ nIdx = FirstSelectedRow() - 1;
+ if (nIdx >= 0 && SeekCursor(nIdx))
+ {
+ GetSeekRow()->SetState(m_pSeekCursor.get(), true);
+
+ bNewPos = true;
+ aBookmark = m_pSeekCursor->getBookmark();
+ }
+ }
+ }
+
+ // Are all rows selected?
+ // Second condition if no insertion line exists
+ bool bAllSelected = GetTotalCount() == nSelectedRows || GetRowCount() == nSelectedRows;
+
+ BeginCursorAction();
+
+ // now delete the row
+ Sequence<sal_Int32> aDeletedRows;
+ SetUpdateMode( false );
+ try
+ {
+ aDeletedRows = xDeleteThem->deleteRows(aBookmarks);
+ }
+ catch(SQLException&)
+ {
+ }
+ SetUpdateMode( true );
+
+ // how many rows are deleted?
+ sal_Int32 nDeletedRows = static_cast<sal_Int32>(std::count_if(std::cbegin(aDeletedRows), std::cend(aDeletedRows),
+ [](const sal_Int32 nRow) { return nRow != 0; }));
+
+ // have rows been deleted?
+ if (nDeletedRows)
+ {
+ SetUpdateMode(false);
+ SetNoSelection();
+ try
+ {
+ // did we delete all the rows than try to move to the next possible row
+ if (nDeletedRows == aDeletedRows.getLength())
+ {
+ // there exists a new position to move on
+ if (bNewPos)
+ {
+ if (aBookmark.hasValue())
+ getDataSource()->moveToBookmark(aBookmark);
+ // no valid bookmark so move to the insert row
+ else
+ {
+ Reference< XResultSetUpdate > xUpdateCursor(Reference< XInterface >(*m_pDataCursor), UNO_QUERY);
+ xUpdateCursor->moveToInsertRow();
+ }
+ }
+ else
+ {
+ Reference< css::beans::XPropertySet > xSet(Reference< XInterface >(*m_pDataCursor), UNO_QUERY);
+
+ sal_Int32 nRecordCount(0);
+ xSet->getPropertyValue(FM_PROP_ROWCOUNT) >>= nRecordCount;
+ if ( m_pDataCursor->rowDeleted() )
+ --nRecordCount;
+
+ // there are no rows left and we have an insert row
+ if (!nRecordCount && GetEmptyRow().is())
+ {
+ Reference< XResultSetUpdate > xUpdateCursor(Reference< XInterface >(*m_pDataCursor), UNO_QUERY);
+ xUpdateCursor->moveToInsertRow();
+ }
+ else if (nRecordCount)
+ // move to the first row
+ getDataSource()->first();
+ }
+ }
+ // not all the rows where deleted, so move to the first row which remained in the resultset
+ else
+ {
+ auto pRow = std::find(std::cbegin(aDeletedRows), std::cend(aDeletedRows), 0);
+ if (pRow != std::cend(aDeletedRows))
+ {
+ auto i = static_cast<sal_Int32>(std::distance(std::cbegin(aDeletedRows), pRow));
+ getDataSource()->moveToBookmark(aBookmarks[i]);
+ }
+ }
+ }
+ catch(const Exception&)
+ {
+ try
+ {
+ // positioning went wrong so try to move to the first row
+ getDataSource()->first();
+ }
+ catch(const Exception&)
+ {
+ }
+ }
+
+ // adapt to the data cursor
+ AdjustDataSource(true);
+
+ // not all rows could be deleted;
+ // never select again there the ones that could not be deleted
+ if (nDeletedRows < nSelectedRows)
+ {
+ // were all selected
+ if (bAllSelected)
+ {
+ SelectAll();
+ if (IsInsertionRow(GetRowCount() - 1)) // not the insertion row
+ SelectRow(GetRowCount() - 1, false);
+ }
+ else
+ {
+ // select the remaining rows
+ for (const sal_Int32 nSuccess : std::as_const(aDeletedRows))
+ {
+ try
+ {
+ if (!nSuccess)
+ {
+ m_pSeekCursor->moveToBookmark(m_pDataCursor->getBookmark());
+ SetSeekPos(m_pSeekCursor->getRow() - 1);
+ SelectRow(GetSeekPos());
+ }
+ }
+ catch(const Exception&)
+ {
+ // keep the seekpos in all cases
+ SetSeekPos(m_pSeekCursor->getRow() - 1);
+ }
+ }
+ }
+ }
+
+ EndCursorAction();
+ SetUpdateMode(true);
+ }
+ else // row could not be deleted
+ {
+ EndCursorAction();
+ try
+ {
+ // currentrow is the insert row?
+ if (!IsCurrentAppending())
+ getDataSource()->refreshRow();
+ }
+ catch(const Exception&)
+ {
+ }
+ }
+ }
+
+ // if there is no selection anymore we can start editing
+ if (!GetSelectRowCount())
+ ActivateCell();
+}
+
+// XCurrentRecordListener
+void FmGridControl::positioned()
+{
+ SAL_INFO("svx.fmcomp", "FmGridControl::positioned");
+ // position on the data source (force it to be done in the main thread)
+ implAdjustInSolarThread(false);
+}
+
+bool FmGridControl::commit()
+{
+ // execute commit only if an update is not already executed by the
+ // css::form::component::GridControl
+ if (!IsUpdating())
+ {
+ if (Controller().is() && Controller()->IsValueChangedFromSaved())
+ {
+ if (!SaveModified())
+ return false;
+ }
+ }
+ return true;
+}
+
+void FmGridControl::inserted()
+{
+ const DbGridRowRef& xRow = GetCurrentRow();
+ if (!xRow.is())
+ return;
+
+ // line has been inserted, then reset the status and mode
+ xRow->SetState(m_pDataCursor.get(), false);
+ xRow->SetNew(false);
+
+}
+
+VclPtr<BrowserHeader> FmGridControl::imp_CreateHeaderBar(BrowseBox* pParent)
+{
+ DBG_ASSERT( pParent == this, "FmGridControl::imp_CreateHeaderBar: parent?" );
+ return VclPtr<FmGridHeader>::Create( pParent );
+}
+
+void FmGridControl::markColumn(sal_uInt16 nId)
+{
+ if (!(GetHeaderBar() && m_nMarkedColumnId != nId))
+ return;
+
+ // deselect
+ if (m_nMarkedColumnId != BROWSER_INVALIDID)
+ {
+ HeaderBarItemBits aBits = GetHeaderBar()->GetItemBits(m_nMarkedColumnId) & ~HeaderBarItemBits::FLAT;
+ GetHeaderBar()->SetItemBits(m_nMarkedColumnId, aBits);
+ }
+
+
+ if (nId != BROWSER_INVALIDID)
+ {
+ HeaderBarItemBits aBits = GetHeaderBar()->GetItemBits(nId) | HeaderBarItemBits::FLAT;
+ GetHeaderBar()->SetItemBits(nId, aBits);
+ }
+ m_nMarkedColumnId = nId;
+}
+
+bool FmGridControl::isColumnMarked(sal_uInt16 nId) const
+{
+ return m_nMarkedColumnId == nId;
+}
+
+tools::Long FmGridControl::QueryMinimumRowHeight()
+{
+ tools::Long const nMinimalLogicHeight = 20; // 0.2 cm
+ tools::Long nMinimalPixelHeight = LogicToPixel(Point(0, nMinimalLogicHeight), MapMode(MapUnit::Map10thMM)).Y();
+ return CalcZoom( nMinimalPixelHeight );
+}
+
+void FmGridControl::RowHeightChanged()
+{
+ DbGridControl::RowHeightChanged();
+
+ Reference< XPropertySet > xModel( GetPeer()->getColumns(), UNO_QUERY );
+ DBG_ASSERT( xModel.is(), "FmGridControl::RowHeightChanged: no model!" );
+ if ( !xModel.is() )
+ return;
+
+ try
+ {
+ sal_Int32 nUnzoomedPixelHeight = CalcReverseZoom( GetDataRowHeight() );
+ Any aProperty( static_cast<sal_Int32>(PixelToLogic( Point(0, nUnzoomedPixelHeight), MapMode(MapUnit::Map10thMM)).Y()) );
+ xModel->setPropertyValue( FM_PROP_ROWHEIGHT, aProperty );
+ }
+ catch( const Exception& )
+ {
+ TOOLS_WARN_EXCEPTION( "svx", "FmGridControl::RowHeightChanged" );
+ }
+}
+
+void FmGridControl::ColumnResized(sal_uInt16 nId)
+{
+ DbGridControl::ColumnResized(nId);
+
+ // transfer value to the model
+ DbGridColumn* pCol = DbGridControl::GetColumns()[ GetModelColumnPos(nId) ].get();
+ const Reference< css::beans::XPropertySet >& xColModel(pCol->getModel());
+ if (xColModel.is())
+ {
+ Any aWidth;
+ sal_Int32 nColumnWidth = GetColumnWidth(nId);
+ nColumnWidth = CalcReverseZoom(nColumnWidth);
+ // convert to 10THMM
+ aWidth <<= static_cast<sal_Int32>(PixelToLogic(Point(nColumnWidth, 0), MapMode(MapUnit::Map10thMM)).X());
+ xColModel->setPropertyValue(FM_PROP_WIDTH, aWidth);
+ }
+}
+
+void FmGridControl::CellModified()
+{
+ DbGridControl::CellModified();
+ GetPeer()->CellModified();
+}
+
+void FmGridControl::BeginCursorAction()
+{
+ DbGridControl::BeginCursorAction();
+ m_pPeer->stopCursorListening();
+}
+
+void FmGridControl::EndCursorAction()
+{
+ m_pPeer->startCursorListening();
+ DbGridControl::EndCursorAction();
+}
+
+void FmGridControl::ColumnMoved(sal_uInt16 nId)
+{
+ m_bInColumnMove = true;
+
+ DbGridControl::ColumnMoved(nId);
+ Reference< css::container::XIndexContainer > xColumns(GetPeer()->getColumns());
+
+ if (xColumns.is())
+ {
+ // locate the column and move in the model;
+ // get ColumnPos
+ DbGridColumn* pCol = DbGridControl::GetColumns()[ GetModelColumnPos(nId) ].get();
+ Reference< css::beans::XPropertySet > xCol;
+
+ // inserting must be based on the column positions
+ sal_Int32 i;
+ Reference< XInterface > xCurrent;
+ for (i = 0; !xCol.is() && i < xColumns->getCount(); i++)
+ {
+ xCurrent.set(xColumns->getByIndex(i), css::uno::UNO_QUERY);
+ if (xCurrent == pCol->getModel())
+ {
+ xCol = pCol->getModel();
+ break;
+ }
+ }
+
+ DBG_ASSERT(i < xColumns->getCount(), "Wrong css::sdbcx::Index");
+ xColumns->removeByIndex(i);
+ Any aElement;
+ aElement <<= xCol;
+ xColumns->insertByIndex(GetModelColumnPos(nId), aElement);
+ pCol->setModel(xCol);
+ // if the column which is shown here is selected ...
+ if ( isColumnSelected(pCol) )
+ markColumn(nId); // ... -> mark it
+ }
+
+ m_bInColumnMove = false;
+}
+
+void FmGridControl::InitColumnsByModels(const Reference< css::container::XIndexContainer >& xColumns)
+{
+ // reset columns;
+ // if there is only one HandleColumn, then don't
+ if (GetModelColCount())
+ {
+ RemoveColumns();
+ InsertHandleColumn();
+ }
+
+ if (!xColumns.is())
+ return;
+
+ SetUpdateMode(false);
+
+ // inserting must be based on the column positions
+ sal_Int32 i;
+ Any aWidth;
+ for (i = 0; i < xColumns->getCount(); ++i)
+ {
+ Reference< css::beans::XPropertySet > xCol(
+ xColumns->getByIndex(i), css::uno::UNO_QUERY);
+
+ OUString aName(
+ comphelper::getString(xCol->getPropertyValue(FM_PROP_LABEL)));
+
+ aWidth = xCol->getPropertyValue(FM_PROP_WIDTH);
+ sal_Int32 nWidth = 0;
+ if (aWidth >>= nWidth)
+ nWidth = LogicToPixel(Point(nWidth, 0), MapMode(MapUnit::Map10thMM)).X();
+
+ AppendColumn(aName, static_cast<sal_uInt16>(nWidth));
+ DbGridColumn* pCol = DbGridControl::GetColumns()[ i ].get();
+ pCol->setModel(xCol);
+ }
+
+ // and now remove the hidden columns as well
+ // (we did not already make it in the upper loop, since we would then have gotten
+ // problems with the IDs of the columns: AppendColumn allocates them automatically,
+ // but the column _after_ a hidden one needs an ID increased by one ...)
+ Any aHidden;
+ for (i = 0; i < xColumns->getCount(); ++i)
+ {
+ Reference< css::beans::XPropertySet > xCol( xColumns->getByIndex(i), css::uno::UNO_QUERY);
+ aHidden = xCol->getPropertyValue(FM_PROP_HIDDEN);
+ if (::comphelper::getBOOL(aHidden))
+ HideColumn(GetColumnIdFromModelPos(static_cast<sal_uInt16>(i)));
+ }
+
+ SetUpdateMode(true);
+}
+
+void FmGridControl::InitColumnByField(
+ DbGridColumn* _pColumn, const Reference< XPropertySet >& _rxColumnModel,
+ const Reference< XNameAccess >& _rxFieldsByNames, const Reference< XIndexAccess >& _rxFieldsByIndex )
+{
+ DBG_ASSERT( _rxFieldsByNames == _rxFieldsByIndex, "FmGridControl::InitColumnByField: invalid container interfaces!" );
+
+ // lookup the column which belongs to the control source
+ OUString sFieldName;
+ _rxColumnModel->getPropertyValue( FM_PROP_CONTROLSOURCE ) >>= sFieldName;
+ Reference< XPropertySet > xField;
+ _rxColumnModel->getPropertyValue( FM_PROP_BOUNDFIELD ) >>= xField;
+
+
+ if ( !xField.is() && /*sFieldName.getLength() && */_rxFieldsByNames->hasByName( sFieldName ) ) // #i93452# do not check for name length
+ _rxFieldsByNames->getByName( sFieldName ) >>= xField;
+
+ // determine the position of this column
+ sal_Int32 nFieldPos = -1;
+ if ( xField.is() )
+ {
+ Reference< XPropertySet > xCheck;
+ sal_Int32 nFieldCount = _rxFieldsByIndex->getCount();
+ for ( sal_Int32 i = 0; i < nFieldCount; ++i)
+ {
+ _rxFieldsByIndex->getByIndex( i ) >>= xCheck;
+ if ( xField.get() == xCheck.get() )
+ {
+ nFieldPos = i;
+ break;
+ }
+ }
+ }
+
+ if ( xField.is() && ( nFieldPos >= 0 ) )
+ {
+ // some data types are not allowed
+ sal_Int32 nDataType = DataType::OTHER;
+ xField->getPropertyValue( FM_PROP_FIELDTYPE ) >>= nDataType;
+
+ bool bIllegalType = false;
+ switch ( nDataType )
+ {
+ case DataType::BLOB:
+ case DataType::LONGVARBINARY:
+ case DataType::BINARY:
+ case DataType::VARBINARY:
+ case DataType::OTHER:
+ bIllegalType = true;
+ break;
+ }
+
+ if ( bIllegalType )
+ {
+ _pColumn->SetObject( static_cast<sal_Int16>(nFieldPos) );
+ return;
+ }
+ }
+
+ // the control type is determined by the ColumnServiceName
+ static constexpr OUStringLiteral s_sPropColumnServiceName = u"ColumnServiceName";
+ if ( !::comphelper::hasProperty( s_sPropColumnServiceName, _rxColumnModel ) )
+ return;
+
+ _pColumn->setModel( _rxColumnModel );
+
+ OUString sColumnServiceName;
+ _rxColumnModel->getPropertyValue( s_sPropColumnServiceName ) >>= sColumnServiceName;
+
+ sal_Int32 nTypeId = getColumnTypeByModelName( sColumnServiceName );
+ _pColumn->CreateControl( nFieldPos, xField, nTypeId );
+}
+
+void FmGridControl::InitColumnsByFields(const Reference< css::container::XIndexAccess >& _rxFields)
+{
+ if ( !_rxFields.is() )
+ return;
+
+ // initialize columns
+ Reference< XIndexContainer > xColumns( GetPeer()->getColumns() );
+ Reference< XNameAccess > xFieldsAsNames( _rxFields, UNO_QUERY );
+
+ // inserting must be based on the column positions
+ for (sal_Int32 i = 0; i < xColumns->getCount(); i++)
+ {
+ DbGridColumn* pCol = GetColumns()[ i ].get();
+ OSL_ENSURE(pCol,"No grid column!");
+ if ( pCol )
+ {
+ Reference< XPropertySet > xColumnModel(
+ xColumns->getByIndex( i ), css::uno::UNO_QUERY);
+
+ InitColumnByField( pCol, xColumnModel, xFieldsAsNames, _rxFields );
+ }
+ }
+}
+
+void FmGridControl::HideColumn(sal_uInt16 nId)
+{
+ DbGridControl::HideColumn(nId);
+
+ sal_uInt16 nPos = GetModelColumnPos(nId);
+ if (nPos == sal_uInt16(-1))
+ return;
+
+ DbGridColumn* pColumn = GetColumns()[ nPos ].get();
+ if (pColumn->IsHidden())
+ GetPeer()->columnHidden(pColumn);
+
+ if (nId == m_nMarkedColumnId)
+ m_nMarkedColumnId = sal_uInt16(-1);
+}
+
+bool FmGridControl::isColumnSelected(DbGridColumn const * _pColumn) const
+{
+ OSL_ENSURE(_pColumn,"Column can not be null!");
+ bool bSelected = false;
+ // if the column which is shown here is selected ...
+ Reference< css::view::XSelectionSupplier > xSelSupplier(GetPeer()->getColumns(), UNO_QUERY);
+ if ( xSelSupplier.is() )
+ {
+ Reference< css::beans::XPropertySet > xColumn;
+ xSelSupplier->getSelection() >>= xColumn;
+ bSelected = (xColumn.get() == _pColumn->getModel().get());
+ }
+ return bSelected;
+}
+
+void FmGridControl::ShowColumn(sal_uInt16 nId)
+{
+ DbGridControl::ShowColumn(nId);
+
+ sal_uInt16 nPos = GetModelColumnPos(nId);
+ if (nPos == sal_uInt16(-1))
+ return;
+
+ DbGridColumn* pColumn = GetColumns()[ nPos ].get();
+ if (!pColumn->IsHidden())
+ GetPeer()->columnVisible(pColumn);
+
+ // if the column which is shown here is selected ...
+ if ( isColumnSelected(pColumn) )
+ markColumn(nId); // ... -> mark it
+}
+
+bool FmGridControl::selectBookmarks(const Sequence< Any >& _rBookmarks)
+{
+ SolarMutexGuard aGuard;
+ // need to lock the SolarMutex so that no paint call disturbs us ...
+
+ if ( !m_pSeekCursor )
+ {
+ OSL_FAIL( "FmGridControl::selectBookmarks: no seek cursor!" );
+ return false;
+ }
+
+ SetNoSelection();
+
+ bool bAllSuccessful = true;
+ try
+ {
+ for (const Any& rBookmark : _rBookmarks)
+ {
+ // move the seek cursor to the row given
+ if (m_pSeekCursor->moveToBookmark(rBookmark))
+ SelectRow( m_pSeekCursor->getRow() - 1);
+ else
+ bAllSuccessful = false;
+ }
+ }
+ catch(Exception&)
+ {
+ OSL_FAIL("FmGridControl::selectBookmarks: could not move to one of the bookmarks!");
+ return false;
+ }
+
+ return bAllSuccessful;
+}
+
+Sequence< Any> FmGridControl::getSelectionBookmarks()
+{
+ // lock our update so no paint-triggered seeks interfere ...
+ SetUpdateMode(false);
+
+ sal_Int32 nSelectedRows = GetSelectRowCount(), i = 0;
+ Sequence< Any> aBookmarks(nSelectedRows);
+ if ( nSelectedRows )
+ {
+ Any* pBookmarks = aBookmarks.getArray();
+
+ // (I'm not sure if the problem isn't deeper: The scenario: a large table displayed by a grid with a
+ // thread-safe cursor (dBase). On loading the sdb-cursor started a counting thread. While this counting progress
+ // was running, I tried do delete 3 records from within the grid. Deletion caused a SeekCursor, which made a
+ // m_pSeekCursor->moveRelative and a m_pSeekCursor->getPosition.
+ // Unfortunately the first call caused a propertyChanged(RECORDCOUNT) which resulted in a repaint of the
+ // navigation bar and the grid. The latter itself will result in SeekRow calls. So after (successfully) returning
+ // from the moveRelative the getPosition returns an invalid value. And so the SeekCursor fails.
+ // In the consequence ALL parts of code where two calls to the seek cursor are done, while the second call _relies_ on
+ // the first one, should be secured against recursion, with a broad-minded interpretation of "recursion": if any of these
+ // code parts is executed, no other should be accessible. But this sounds very difficult to achieve...
+ // )
+
+ // The next problem caused by the same behavior (SeekCursor causes a propertyChanged): when adjusting rows we implicitly
+ // change our selection. So a "FirstSelected(); SeekCursor(); NextSelected();" may produce unpredictable results.
+ // That's why we _first_ collect the indices of the selected rows and _then_ their bookmarks.
+ tools::Long nIdx = FirstSelectedRow();
+ while (nIdx != BROWSER_ENDOFSELECTION)
+ {
+ // (we misuse the bookmarks array for this ...)
+ pBookmarks[i++] <<= static_cast<sal_Int32>(nIdx);
+ nIdx = NextSelectedRow();
+ }
+ DBG_ASSERT(i == nSelectedRows, "FmGridControl::DeleteSelectedRows : could not collect the row indices !");
+
+ for (i=0; i<nSelectedRows; ++i)
+ {
+ nIdx = ::comphelper::getINT32(pBookmarks[i]);
+ if (IsInsertionRow(nIdx))
+ {
+ // do not delete empty row
+ aBookmarks.realloc(--nSelectedRows);
+ SelectRow(nIdx, false); // cancel selection for empty row
+ break;
+ }
+
+ // first, position the data cursor on the selected block
+ if (SeekCursor(nIdx))
+ {
+ GetSeekRow()->SetState(m_pSeekCursor.get(), true);
+
+ pBookmarks[i] = m_pSeekCursor->getBookmark();
+ }
+ #ifdef DBG_UTIL
+ else
+ OSL_FAIL("FmGridControl::DeleteSelectedRows : a bookmark could not be determined !");
+ #endif
+ }
+ }
+ SetUpdateMode(true);
+
+ // if one of the SeekCursor-calls failed...
+ aBookmarks.realloc(i);
+
+ // (the alternative : while collecting the bookmarks lock our propertyChanged, this should resolve both our problems.
+ // but this would be incompatible as we need a locking flag, then...)
+
+ return aBookmarks;
+}
+
+namespace
+{
+ OUString getColumnPropertyFromPeer(FmXGridPeer* _pPeer,sal_Int32 _nPosition,const OUString& _sPropName)
+ {
+ OUString sRetText;
+ if ( _pPeer && _nPosition != -1)
+ {
+ Reference<XIndexContainer> xIndex = _pPeer->getColumns();
+ if ( xIndex.is() && xIndex->getCount() > _nPosition )
+ {
+ Reference<XPropertySet> xProp;
+ xIndex->getByIndex( _nPosition ) >>= xProp;
+ if ( xProp.is() )
+ {
+ try {
+ xProp->getPropertyValue( _sPropName ) >>= sRetText;
+ } catch (UnknownPropertyException const&) {
+ TOOLS_WARN_EXCEPTION("svx.fmcomp", "");
+ }
+ }
+ }
+ }
+ return sRetText;
+ }
+}
+
+// Object data and state
+OUString FmGridControl::GetAccessibleObjectName( AccessibleBrowseBoxObjType _eObjType,sal_Int32 _nPosition ) const
+{
+ OUString sRetText;
+ switch( _eObjType )
+ {
+ case AccessibleBrowseBoxObjType::BrowseBox:
+ if ( GetPeer() )
+ {
+ Reference<XPropertySet> xProp(GetPeer()->getColumns(),UNO_QUERY);
+ if ( xProp.is() )
+ xProp->getPropertyValue(FM_PROP_NAME) >>= sRetText;
+ }
+ break;
+ case AccessibleBrowseBoxObjType::ColumnHeaderCell:
+ sRetText = getColumnPropertyFromPeer(
+ GetPeer(),
+ GetModelColumnPos(
+ sal::static_int_cast< sal_uInt16 >(_nPosition)),
+ FM_PROP_LABEL);
+ break;
+ default:
+ sRetText = DbGridControl::GetAccessibleObjectName(_eObjType,_nPosition);
+ }
+ return sRetText;
+}
+
+OUString FmGridControl::GetAccessibleObjectDescription( AccessibleBrowseBoxObjType _eObjType,sal_Int32 _nPosition ) const
+{
+ OUString sRetText;
+ switch( _eObjType )
+ {
+ case AccessibleBrowseBoxObjType::BrowseBox:
+ if ( GetPeer() )
+ {
+ Reference<XPropertySet> xProp(GetPeer()->getColumns(),UNO_QUERY);
+ if ( xProp.is() )
+ {
+ xProp->getPropertyValue(FM_PROP_HELPTEXT) >>= sRetText;
+ if ( sRetText.isEmpty() )
+ xProp->getPropertyValue(FM_PROP_DESCRIPTION) >>= sRetText;
+ }
+ }
+ break;
+ case AccessibleBrowseBoxObjType::ColumnHeaderCell:
+ sRetText = getColumnPropertyFromPeer(
+ GetPeer(),
+ GetModelColumnPos(
+ sal::static_int_cast< sal_uInt16 >(_nPosition)),
+ FM_PROP_HELPTEXT);
+ if ( sRetText.isEmpty() )
+ sRetText = getColumnPropertyFromPeer(
+ GetPeer(),
+ GetModelColumnPos(
+ sal::static_int_cast< sal_uInt16 >(_nPosition)),
+ FM_PROP_DESCRIPTION);
+
+ break;
+ default:
+ sRetText = DbGridControl::GetAccessibleObjectDescription(_eObjType,_nPosition);
+ }
+ return sRetText;
+}
+
+void FmGridControl::Select()
+{
+ DbGridControl::Select();
+ // ... does it affect our columns?
+ const MultiSelection* pColumnSelection = GetColumnSelection();
+
+ sal_uInt16 nSelectedColumn =
+ pColumnSelection && pColumnSelection->GetSelectCount()
+ ? sal::static_int_cast< sal_uInt16 >(
+ const_cast<MultiSelection*>(pColumnSelection)->FirstSelected())
+ : SAL_MAX_UINT16;
+ // the HandleColumn is not selected
+ switch (nSelectedColumn)
+ {
+ case SAL_MAX_UINT16: break; // no selection
+ case 0 : nSelectedColumn = SAL_MAX_UINT16; break;
+ // handle col can't be selected
+ default :
+ // get the model col pos instead of the view col pos
+ nSelectedColumn = GetModelColumnPos(GetColumnIdFromViewPos(nSelectedColumn - 1));
+ break;
+ }
+
+ if (nSelectedColumn == m_nCurrentSelectedColumn)
+ return;
+
+ // BEFORE calling the select at the SelectionSupplier!
+ m_nCurrentSelectedColumn = nSelectedColumn;
+
+ if (m_bSelecting)
+ return;
+
+ m_bSelecting = true;
+
+ try
+ {
+ Reference< XIndexAccess > xColumns = GetPeer()->getColumns();
+ Reference< XSelectionSupplier > xSelSupplier(xColumns, UNO_QUERY);
+ if (xSelSupplier.is())
+ {
+ if (nSelectedColumn != SAL_MAX_UINT16)
+ {
+ Reference< XPropertySet > xColumn(
+ xColumns->getByIndex(nSelectedColumn),
+ css::uno::UNO_QUERY);
+ xSelSupplier->select(Any(xColumn));
+ }
+ else
+ {
+ xSelSupplier->select(Any());
+ }
+ }
+ }
+ catch(Exception&)
+ {
+ }
+
+
+ m_bSelecting = false;
+}
+
+
+void FmGridControl::KeyInput( const KeyEvent& rKEvt )
+{
+ bool bDone = false;
+ const vcl::KeyCode& rKeyCode = rKEvt.GetKeyCode();
+ if ( IsDesignMode()
+ && !rKeyCode.IsShift()
+ && !rKeyCode.IsMod1()
+ && !rKeyCode.IsMod2()
+ && GetParent() )
+ {
+ switch ( rKeyCode.GetCode() )
+ {
+ case KEY_ESCAPE:
+ GetParent()->GrabFocus();
+ bDone = true;
+ break;
+ case KEY_DELETE:
+ if ( GetSelectColumnCount() && GetPeer() && m_nCurrentSelectedColumn >= 0 )
+ {
+ Reference< css::container::XIndexContainer > xCols(GetPeer()->getColumns());
+ if ( xCols.is() )
+ {
+ try
+ {
+ if ( m_nCurrentSelectedColumn < xCols->getCount() )
+ {
+ Reference< XInterface > xCol;
+ xCols->getByIndex(m_nCurrentSelectedColumn) >>= xCol;
+ xCols->removeByIndex(m_nCurrentSelectedColumn);
+ ::comphelper::disposeComponent(xCol);
+ }
+ }
+ catch(const Exception&)
+ {
+ TOOLS_WARN_EXCEPTION("svx", "exception occurred while deleting a column");
+ }
+ }
+ }
+ bDone = true;
+ break;
+ }
+ }
+ if ( !bDone )
+ DbGridControl::KeyInput( rKEvt );
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/fmcomp/fmgridif.cxx b/svx/source/fmcomp/fmgridif.cxx
new file mode 100644
index 000000000..bce6303d2
--- /dev/null
+++ b/svx/source/fmcomp/fmgridif.cxx
@@ -0,0 +1,2808 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * 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 <svx/fmgridif.hxx>
+#include <fmprop.hxx>
+#include <fmservs.hxx>
+#include <svx/fmtools.hxx>
+#include <fmurl.hxx>
+#include <formcontrolfactory.hxx>
+#include <gridcell.hxx>
+#include <sdbdatacolumn.hxx>
+#include <svx/fmgridcl.hxx>
+#include <tools/urlobj.hxx>
+
+#include <com/sun/star/awt/PosSize.hpp>
+#include <com/sun/star/beans/PropertyAttribute.hpp>
+#include <com/sun/star/form/FormComponentType.hpp>
+#include <com/sun/star/form/XFormComponent.hpp>
+#include <com/sun/star/form/XLoadable.hpp>
+#include <com/sun/star/form/XReset.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/sdbc/ResultSetType.hpp>
+#include <com/sun/star/sdbcx/XColumnsSupplier.hpp>
+#include <com/sun/star/util/URLTransformer.hpp>
+#include <com/sun/star/util/XURLTransformer.hpp>
+#include <com/sun/star/view/XSelectionSupplier.hpp>
+#include <com/sun/star/sdbcx/XRowLocate.hpp>
+
+#include <comphelper/enumhelper.hxx>
+#include <comphelper/processfactory.hxx>
+#include <comphelper/property.hxx>
+#include <comphelper/sequence.hxx>
+#include <comphelper/types.hxx>
+#include <cppuhelper/supportsservice.hxx>
+#include <cppuhelper/queryinterface.hxx>
+#include <o3tl/safeint.hxx>
+#include <vcl/unohelp.hxx>
+#include <vcl/svapp.hxx>
+#include <tools/debug.hxx>
+#include <tools/diagnose_ex.h>
+#include <sal/macros.h>
+
+using namespace ::svxform;
+using namespace ::com::sun::star::container;
+using namespace ::com::sun::star::sdb;
+using namespace ::com::sun::star::sdbc;
+using namespace ::com::sun::star::uno;
+using namespace ::com::sun::star::view;
+using namespace ::com::sun::star::beans;
+using namespace ::com::sun::star::lang;
+using namespace ::com::sun::star::form;
+using namespace ::com::sun::star::util;
+using namespace ::com::sun::star;
+
+using ::com::sun::star::sdbcx::XColumnsSupplier;
+using ::com::sun::star::frame::XDispatchProviderInterceptor;
+using ::com::sun::star::frame::XDispatchProvider;
+using ::com::sun::star::accessibility::XAccessible;
+using ::com::sun::star::accessibility::XAccessibleContext;
+using ::com::sun::star::sdb::XRowSetSupplier;
+using ::com::sun::star::awt::XVclWindowPeer;
+
+
+static css::awt::FontDescriptor ImplCreateFontDescriptor( const vcl::Font& rFont )
+{
+ css::awt::FontDescriptor aFD;
+ aFD.Name = rFont.GetFamilyName();
+ aFD.StyleName = rFont.GetStyleName();
+ aFD.Height = static_cast<sal_Int16>(rFont.GetFontSize().Height());
+ aFD.Width = static_cast<sal_Int16>(rFont.GetFontSize().Width());
+ aFD.Family = static_cast<sal_Int16>(rFont.GetFamilyType());
+ aFD.CharSet = rFont.GetCharSet();
+ aFD.Pitch = static_cast<sal_Int16>(rFont.GetPitch());
+ aFD.CharacterWidth = vcl::unohelper::ConvertFontWidth( rFont.GetWidthType() );
+ aFD.Weight= vcl::unohelper::ConvertFontWeight( rFont.GetWeight() );
+ aFD.Slant = vcl::unohelper::ConvertFontSlant( rFont.GetItalic() );
+ aFD.Underline = static_cast<sal_Int16>(rFont.GetUnderline());
+ aFD.Strikeout = static_cast<sal_Int16>(rFont.GetStrikeout());
+ aFD.Orientation = toDegrees(rFont.GetOrientation());
+ aFD.Kerning = rFont.IsKerning();
+ aFD.WordLineMode = rFont.IsWordLineMode();
+ aFD.Type = 0; // ??? => only to metric...
+ return aFD;
+}
+
+
+static vcl::Font ImplCreateFont( const css::awt::FontDescriptor& rDescr )
+{
+ vcl::Font aFont;
+ aFont.SetFamilyName( rDescr.Name );
+ aFont.SetStyleName( rDescr.StyleName );
+ aFont.SetFontSize( ::Size( rDescr.Width, rDescr.Height ) );
+ aFont.SetFamily( static_cast<FontFamily>(rDescr.Family) );
+ aFont.SetCharSet( static_cast<rtl_TextEncoding>(rDescr.CharSet) );
+ aFont.SetPitch( static_cast<FontPitch>(rDescr.Pitch) );
+ aFont.SetWidthType( vcl::unohelper::ConvertFontWidth( rDescr.CharacterWidth ) );
+ aFont.SetWeight( vcl::unohelper::ConvertFontWeight( rDescr.Weight ) );
+ aFont.SetItalic( static_cast<FontItalic>(rDescr.Slant) );
+ aFont.SetUnderline( static_cast<::FontLineStyle>(rDescr.Underline) );
+ aFont.SetStrikeout( static_cast<::FontStrikeout>(rDescr.Strikeout) );
+ aFont.SetOrientation( Degree10(static_cast<sal_Int16>(rDescr.Orientation * 10)) );
+ aFont.SetKerning( static_cast<FontKerning>(rDescr.Kerning) );
+ aFont.SetWordLineMode( rDescr.WordLineMode );
+ return aFont;
+}
+
+FmXModifyMultiplexer::FmXModifyMultiplexer( ::cppu::OWeakObject& rSource, ::osl::Mutex& _rMutex )
+ :OWeakSubObject( rSource )
+ ,OInterfaceContainerHelper3( _rMutex )
+{
+}
+
+
+Any SAL_CALL FmXModifyMultiplexer::queryInterface(const Type& _rType)
+{
+ Any aReturn = ::cppu::queryInterface(_rType,
+ static_cast< css::util::XModifyListener*>(this),
+ static_cast< XEventListener*>(this)
+ );
+
+ if (!aReturn.hasValue())
+ aReturn = OWeakSubObject::queryInterface( _rType );
+
+ return aReturn;
+}
+
+
+void FmXModifyMultiplexer::disposing(const EventObject& )
+{
+}
+
+
+void FmXModifyMultiplexer::modified(const EventObject& e)
+{
+ EventObject aMulti( e);
+ aMulti.Source = &m_rParent;
+ notifyEach( &XModifyListener::modified, aMulti );
+}
+
+FmXUpdateMultiplexer::FmXUpdateMultiplexer( ::cppu::OWeakObject& rSource, ::osl::Mutex& _rMutex )
+ :OWeakSubObject( rSource )
+ ,OInterfaceContainerHelper3( _rMutex )
+{
+}
+
+
+Any SAL_CALL FmXUpdateMultiplexer::queryInterface(const Type& _rType)
+{
+ Any aReturn = ::cppu::queryInterface(_rType,
+ static_cast< XUpdateListener*>(this),
+ static_cast< XEventListener*>(this)
+ );
+
+ if (!aReturn.hasValue())
+ aReturn = OWeakSubObject::queryInterface( _rType );
+
+ return aReturn;
+}
+
+
+void FmXUpdateMultiplexer::disposing(const EventObject& )
+{
+}
+
+
+sal_Bool FmXUpdateMultiplexer::approveUpdate(const EventObject &e)
+{
+ EventObject aMulti( e );
+ aMulti.Source = &m_rParent;
+
+ bool bResult = true;
+ if (getLength())
+ {
+ ::comphelper::OInterfaceIteratorHelper3 aIter(*this);
+ while ( bResult && aIter.hasMoreElements() )
+ bResult = aIter.next()->approveUpdate( aMulti );
+ }
+
+ return bResult;
+}
+
+
+void FmXUpdateMultiplexer::updated(const EventObject &e)
+{
+ EventObject aMulti( e );
+ aMulti.Source = &m_rParent;
+ notifyEach( &XUpdateListener::updated, aMulti );
+}
+
+FmXSelectionMultiplexer::FmXSelectionMultiplexer( ::cppu::OWeakObject& rSource, ::osl::Mutex& _rMutex )
+ :OWeakSubObject( rSource )
+ ,OInterfaceContainerHelper3( _rMutex )
+{
+}
+
+
+Any SAL_CALL FmXSelectionMultiplexer::queryInterface(const Type& _rType)
+{
+ Any aReturn = ::cppu::queryInterface(_rType,
+ static_cast< XSelectionChangeListener*>(this),
+ static_cast< XEventListener*>(this)
+ );
+
+ if (!aReturn.hasValue())
+ aReturn = OWeakSubObject::queryInterface( _rType );
+
+ return aReturn;
+}
+
+
+void FmXSelectionMultiplexer::disposing(const EventObject& )
+{
+}
+
+
+void SAL_CALL FmXSelectionMultiplexer::selectionChanged( const EventObject& _rEvent )
+{
+ EventObject aMulti(_rEvent);
+ aMulti.Source = &m_rParent;
+ notifyEach( &XSelectionChangeListener::selectionChanged, aMulti );
+}
+
+FmXContainerMultiplexer::FmXContainerMultiplexer( ::cppu::OWeakObject& rSource, ::osl::Mutex& _rMutex )
+ :OWeakSubObject( rSource )
+ ,OInterfaceContainerHelper3( _rMutex )
+{
+}
+
+
+Any SAL_CALL FmXContainerMultiplexer::queryInterface(const Type& _rType)
+{
+ Any aReturn = ::cppu::queryInterface(_rType,
+ static_cast< XContainerListener*>(this),
+ static_cast< XEventListener*>(this)
+ );
+
+ if (!aReturn.hasValue())
+ aReturn = OWeakSubObject::queryInterface( _rType );
+
+ return aReturn;
+}
+
+
+void FmXContainerMultiplexer::disposing(const EventObject& )
+{
+}
+
+void FmXContainerMultiplexer::elementInserted(const ContainerEvent& e)
+{
+ ContainerEvent aMulti( e );
+ aMulti.Source = &m_rParent;
+ notifyEach( &XContainerListener::elementInserted, aMulti );
+}
+
+
+void FmXContainerMultiplexer::elementRemoved(const ContainerEvent& e)
+{
+ ContainerEvent aMulti( e );
+ aMulti.Source = &m_rParent;
+ notifyEach( &XContainerListener::elementRemoved, aMulti );
+}
+
+
+void FmXContainerMultiplexer::elementReplaced(const ContainerEvent& e)
+{
+ ContainerEvent aMulti( e );
+ aMulti.Source = &m_rParent;
+ notifyEach( &XContainerListener::elementReplaced, aMulti );
+}
+
+FmXGridControlMultiplexer::FmXGridControlMultiplexer( ::cppu::OWeakObject& rSource, ::osl::Mutex& _rMutex )
+ :OWeakSubObject( rSource )
+ ,OInterfaceContainerHelper3( _rMutex )
+{
+}
+
+
+Any SAL_CALL FmXGridControlMultiplexer::queryInterface(const Type& _rType)
+{
+ Any aReturn = ::cppu::queryInterface( _rType,
+ static_cast< XGridControlListener*>(this)
+ );
+
+ if (!aReturn.hasValue())
+ aReturn = OWeakSubObject::queryInterface( _rType );
+
+ return aReturn;
+}
+
+
+void FmXGridControlMultiplexer::disposing( const EventObject& )
+{
+}
+
+
+void SAL_CALL FmXGridControlMultiplexer::columnChanged( const EventObject& _event )
+{
+ EventObject aForwardedEvent( _event );
+ aForwardedEvent.Source = &m_rParent;
+ notifyEach( &XGridControlListener::columnChanged, aForwardedEvent );
+}
+
+
+//= FmXGridControl
+
+
+Reference< XInterface > FmXGridControl_NewInstance_Impl(const Reference< XMultiServiceFactory>& _rxFactory)
+{
+ return *(new FmXGridControl( comphelper::getComponentContext(_rxFactory) ));
+}
+
+FmXGridControl::FmXGridControl(const Reference< XComponentContext >& _rxContext)
+ :m_aModifyListeners(*this, GetMutex())
+ ,m_aUpdateListeners(*this, GetMutex())
+ ,m_aContainerListeners(*this, GetMutex())
+ ,m_aSelectionListeners(*this, GetMutex())
+ ,m_aGridControlListeners(*this, GetMutex())
+ ,m_bInDraw(false)
+ ,m_xContext(_rxContext)
+{
+}
+
+
+FmXGridControl::~FmXGridControl()
+{
+}
+
+
+Any SAL_CALL FmXGridControl::queryAggregation(const Type& _rType)
+{
+ Any aReturn = FmXGridControl_BASE::queryInterface(_rType);
+
+ if (!aReturn.hasValue())
+ aReturn = UnoControl::queryAggregation( _rType );
+ return aReturn;
+}
+
+
+Sequence< Type> SAL_CALL FmXGridControl::getTypes( )
+{
+ return comphelper::concatSequences(UnoControl::getTypes(),FmXGridControl_BASE::getTypes());
+}
+
+
+Sequence<sal_Int8> SAL_CALL FmXGridControl::getImplementationId( )
+{
+ return css::uno::Sequence<sal_Int8>();
+}
+
+// XServiceInfo
+sal_Bool SAL_CALL FmXGridControl::supportsService(const OUString& ServiceName)
+{
+ return cppu::supportsService(this, ServiceName);
+}
+
+OUString SAL_CALL FmXGridControl::getImplementationName()
+{
+ return "com.sun.star.form.FmXGridControl";
+}
+
+css::uno::Sequence<OUString> SAL_CALL FmXGridControl::getSupportedServiceNames()
+{
+ return { FM_SUN_CONTROL_GRIDCONTROL, "com.sun.star.awt.UnoControl" };
+}
+
+
+void SAL_CALL FmXGridControl::dispose()
+{
+ SolarMutexGuard aGuard;
+
+ EventObject aEvt;
+ aEvt.Source = static_cast< ::cppu::OWeakObject* >(this);
+ m_aModifyListeners.disposeAndClear(aEvt);
+ m_aUpdateListeners.disposeAndClear(aEvt);
+ m_aContainerListeners.disposeAndClear(aEvt);
+
+ UnoControl::dispose();
+}
+
+
+OUString FmXGridControl::GetComponentServiceName() const
+{
+ return "DBGrid";
+}
+
+
+sal_Bool SAL_CALL FmXGridControl::setModel(const Reference< css::awt::XControlModel >& rModel)
+{
+ SolarMutexGuard aGuard;
+
+ if (!UnoControl::setModel(rModel))
+ return false;
+
+ Reference< XGridPeer > xGridPeer(getPeer(), UNO_QUERY);
+ if (xGridPeer.is())
+ {
+ Reference< XIndexContainer > xCols(mxModel, UNO_QUERY);
+ xGridPeer->setColumns(xCols);
+ }
+ return true;
+}
+
+
+rtl::Reference<FmXGridPeer> FmXGridControl::imp_CreatePeer(vcl::Window* pParent)
+{
+ rtl::Reference<FmXGridPeer> pReturn = new FmXGridPeer(m_xContext);
+
+ // translate properties into WinBits
+ WinBits nStyle = WB_TABSTOP;
+ Reference< XPropertySet > xModelSet(getModel(), UNO_QUERY);
+ if (xModelSet.is())
+ {
+ try
+ {
+ if (::comphelper::getINT16(xModelSet->getPropertyValue(FM_PROP_BORDER)))
+ nStyle |= WB_BORDER;
+ }
+ catch(const Exception&)
+ {
+ OSL_FAIL("Can not get style");
+ }
+ }
+
+ pReturn->Create(pParent, nStyle);
+ return pReturn;
+}
+
+
+void SAL_CALL FmXGridControl::createPeer(const Reference< css::awt::XToolkit >& /*rToolkit*/, const Reference< css::awt::XWindowPeer >& rParentPeer)
+{
+ if ( !mxModel.is() )
+ throw DisposedException( OUString(), *this );
+
+ DBG_ASSERT(/*(0 == m_nPeerCreationLevel) && */!mbCreatingPeer, "FmXGridControl::createPeer : recursion!");
+ // I think this should never assert, now that we're using the base class' mbCreatingPeer in addition to
+ // our own m_nPeerCreationLevel
+ // But I'm not sure as I don't _fully_ understand the underlying toolkit implementations...
+ // (if this asserts, we still need m_nPeerCreationLevel. If not, we could omit it...)
+ // 14.05.2001 - 86836 - frank.schoenheit@germany.sun.com
+
+ // TODO: why the hell this whole class does not use any mutex?
+
+ if (getPeer().is())
+ return;
+
+ mbCreatingPeer = true;
+ // mbCreatingPeer is virtually the same as m_nPeerCreationLevel, but it's the base class' method
+ // to prevent recursion.
+
+ vcl::Window* pParentWin = nullptr;
+ if (rParentPeer.is())
+ {
+ VCLXWindow* pParent = comphelper::getFromUnoTunnel<VCLXWindow>(rParentPeer);
+ if (pParent)
+ pParentWin = pParent->GetWindow();
+ }
+
+ rtl::Reference<FmXGridPeer> pPeer = imp_CreatePeer(pParentWin);
+ DBG_ASSERT(pPeer != nullptr, "FmXGridControl::createPeer : imp_CreatePeer didn't return a peer !");
+ setPeer( pPeer );
+
+ // reading the properties from the model
+// ++m_nPeerCreationLevel;
+ updateFromModel();
+
+ // consider the following ugly scenario: updateFromModel leads to a propertiesChanges on the Control,
+ // which determines, dat a "critical" property has changed (e.g. "Border") and therefore starts a new
+ // Peer, which lands again here in createPeer we also start a second FmXGridPeer and initialise it.
+ // Then we exit from the first incarnation's updateFromModel and continue working with the pPeer,
+ // that is in fact now already obsolete (as another peer is being started in the second incarnation).
+ // Therefore the effort with the PeerCreationLevel, which ensures that we really use the Peer
+ // created at the deepest level, but first initialise it in the top-level.
+// if (--m_nPeerCreationLevel == 0)
+ {
+ DBG_ASSERT(getPeer().is(), "FmXGridControl::createPeer : something went wrong ... no top level peer !");
+ pPeer = comphelper::getFromUnoTunnel<FmXGridPeer>(getPeer());
+
+ setPosSize( maComponentInfos.nX, maComponentInfos.nY, maComponentInfos.nWidth, maComponentInfos.nHeight, css::awt::PosSize::POSSIZE );
+
+ Reference< XIndexContainer > xColumns(getModel(), UNO_QUERY);
+ if (xColumns.is())
+ pPeer->setColumns(xColumns);
+
+ if (maComponentInfos.bVisible)
+ pPeer->setVisible(true);
+
+ if (!maComponentInfos.bEnable)
+ pPeer->setEnable(false);
+
+ if (maWindowListeners.getLength())
+ pPeer->addWindowListener( &maWindowListeners );
+
+ if (maFocusListeners.getLength())
+ pPeer->addFocusListener( &maFocusListeners );
+
+ if (maKeyListeners.getLength())
+ pPeer->addKeyListener( &maKeyListeners );
+
+ if (maMouseListeners.getLength())
+ pPeer->addMouseListener( &maMouseListeners );
+
+ if (maMouseMotionListeners.getLength())
+ pPeer->addMouseMotionListener( &maMouseMotionListeners );
+
+ if (maPaintListeners.getLength())
+ pPeer->addPaintListener( &maPaintListeners );
+
+ if (m_aModifyListeners.getLength())
+ pPeer->addModifyListener( &m_aModifyListeners );
+
+ if (m_aUpdateListeners.getLength())
+ pPeer->addUpdateListener( &m_aUpdateListeners );
+
+ if (m_aContainerListeners.getLength())
+ pPeer->addContainerListener( &m_aContainerListeners );
+
+ // forward the design mode
+ bool bForceAlivePeer = m_bInDraw && !maComponentInfos.bVisible;
+ // (we force an alive-mode peer if we're in "draw", cause in this case the peer will be used for drawing in
+ // foreign devices. We ensure this with the visibility check as a living peer is assumed to be noncritical
+ // only if invisible)
+ Any aOldCursorBookmark;
+ if (!mbDesignMode || bForceAlivePeer)
+ {
+ Reference< XFormComponent > xComp(getModel(), UNO_QUERY);
+ if (xComp.is())
+ {
+ Reference< XRowSet > xForm(xComp->getParent(), UNO_QUERY);
+ // is the form alive?
+ // we can see that if the form contains columns
+ Reference< css::sdbcx::XColumnsSupplier > xColumnsSupplier(xForm, UNO_QUERY);
+ if (xColumnsSupplier.is())
+ {
+ if (Reference< XIndexAccess > (xColumnsSupplier->getColumns(),UNO_QUERY_THROW)->getCount())
+ {
+ // we get only a new bookmark if the resultset is not forwardonly
+ if (::comphelper::getINT32(Reference< XPropertySet > (xForm, UNO_QUERY_THROW)->getPropertyValue(FM_PROP_RESULTSET_TYPE)) != ResultSetType::FORWARD_ONLY)
+ {
+ // as the FmGridControl touches the data source it is connected to we have to remember the current
+ // cursor position (and restore afterwards)
+ // OJ: but only when we stand on a valid row
+ if ( !xForm->isBeforeFirst() && !xForm->isAfterLast() )
+ {
+ try
+ {
+ aOldCursorBookmark = Reference< css::sdbcx::XRowLocate > (xForm, UNO_QUERY_THROW)->getBookmark();
+ }
+ catch( const Exception& )
+ {
+ DBG_UNHANDLED_EXCEPTION("svx");
+ }
+ }
+ }
+ }
+ }
+ pPeer->setRowSet(xForm);
+ }
+ }
+ pPeer->setDesignMode(mbDesignMode && !bForceAlivePeer);
+
+ try
+ {
+ if (aOldCursorBookmark.hasValue())
+ { // we have a valid bookmark, so we have to restore the cursor's position
+ Reference< XFormComponent > xComp(getModel(), UNO_QUERY);
+ Reference< css::sdbcx::XRowLocate > xLocate(xComp->getParent(), UNO_QUERY);
+ xLocate->moveToBookmark(aOldCursorBookmark);
+ }
+ }
+ catch( const Exception& )
+ {
+ DBG_UNHANDLED_EXCEPTION("svx");
+ }
+
+ Reference< css::awt::XView > xPeerView(getPeer(), UNO_QUERY);
+ xPeerView->setZoom( maComponentInfos.nZoomX, maComponentInfos.nZoomY );
+ xPeerView->setGraphics( mxGraphics );
+ }
+ mbCreatingPeer = false;
+}
+
+
+void FmXGridControl::addModifyListener(const Reference< css::util::XModifyListener >& l)
+{
+ m_aModifyListeners.addInterface( l );
+ if( getPeer().is() && m_aModifyListeners.getLength() == 1 )
+ {
+ Reference< css::util::XModifyBroadcaster > xGrid(getPeer(), UNO_QUERY);
+ xGrid->addModifyListener( &m_aModifyListeners);
+ }
+}
+
+
+sal_Bool SAL_CALL FmXGridControl::select( const Any& _rSelection )
+{
+ SolarMutexGuard aGuard;
+ Reference< XSelectionSupplier > xPeer(getPeer(), UNO_QUERY);
+ return xPeer->select(_rSelection);
+}
+
+
+Any SAL_CALL FmXGridControl::getSelection( )
+{
+ SolarMutexGuard aGuard;
+ Reference< XSelectionSupplier > xPeer(getPeer(), UNO_QUERY);
+ return xPeer->getSelection();
+}
+
+
+void SAL_CALL FmXGridControl::addSelectionChangeListener( const Reference< XSelectionChangeListener >& _rxListener )
+{
+ m_aSelectionListeners.addInterface( _rxListener );
+ if( getPeer().is() && 1 == m_aSelectionListeners.getLength() )
+ {
+ Reference< XSelectionSupplier > xGrid(getPeer(), UNO_QUERY);
+ xGrid->addSelectionChangeListener( &m_aSelectionListeners);
+ }
+}
+
+
+void SAL_CALL FmXGridControl::removeSelectionChangeListener( const Reference< XSelectionChangeListener >& _rxListener )
+{
+ if( getPeer().is() && 1 == m_aSelectionListeners.getLength() )
+ {
+ Reference< XSelectionSupplier > xGrid(getPeer(), UNO_QUERY);
+ xGrid->removeSelectionChangeListener( &m_aSelectionListeners);
+ }
+ m_aSelectionListeners.removeInterface( _rxListener );
+}
+
+
+Sequence< sal_Bool > SAL_CALL FmXGridControl::queryFieldDataType( const Type& xType )
+{
+ if (getPeer().is())
+ {
+ Reference< XGridFieldDataSupplier > xPeerSupplier(getPeer(), UNO_QUERY);
+ if (xPeerSupplier.is())
+ return xPeerSupplier->queryFieldDataType(xType);
+ }
+
+ return Sequence<sal_Bool>();
+}
+
+
+Sequence< Any > SAL_CALL FmXGridControl::queryFieldData( sal_Int32 nRow, const Type& xType )
+{
+ if (getPeer().is())
+ {
+ Reference< XGridFieldDataSupplier > xPeerSupplier(getPeer(), UNO_QUERY);
+ if (xPeerSupplier.is())
+ return xPeerSupplier->queryFieldData(nRow, xType);
+ }
+
+ return Sequence< Any>();
+}
+
+
+void SAL_CALL FmXGridControl::removeModifyListener(const Reference< css::util::XModifyListener >& l)
+{
+ if( getPeer().is() && m_aModifyListeners.getLength() == 1 )
+ {
+ Reference< css::util::XModifyBroadcaster > xGrid(getPeer(), UNO_QUERY);
+ xGrid->removeModifyListener( &m_aModifyListeners);
+ }
+ m_aModifyListeners.removeInterface( l );
+}
+
+
+void SAL_CALL FmXGridControl::draw( sal_Int32 x, sal_Int32 y )
+{
+ SolarMutexGuard aGuard;
+ m_bInDraw = true;
+ UnoControl::draw(x, y);
+ m_bInDraw = false;
+}
+
+
+void SAL_CALL FmXGridControl::setDesignMode(sal_Bool bOn)
+{
+ css::util::ModeChangeEvent aModeChangeEvent;
+
+ // --- <mutex_lock> ---
+ {
+ SolarMutexGuard aGuard;
+
+ Reference< XRowSetSupplier > xGrid(getPeer(), UNO_QUERY);
+
+ if (xGrid.is() && (bool(bOn) != mbDesignMode || (!bOn && !xGrid->getRowSet().is())))
+ {
+ if (bOn)
+ {
+ xGrid->setRowSet(Reference< XRowSet > ());
+ }
+ else
+ {
+ Reference< XFormComponent > xComp(getModel(), UNO_QUERY);
+ if (xComp.is())
+ {
+ Reference< XRowSet > xForm(xComp->getParent(), UNO_QUERY);
+ xGrid->setRowSet(xForm);
+ }
+ }
+
+ // Avoid infinite recursion when calling XVclWindowPeer::setDesignMode below
+ mbDesignMode = bOn;
+
+ Reference< XVclWindowPeer > xVclWindowPeer( getPeer(), UNO_QUERY );
+ if (xVclWindowPeer.is())
+ xVclWindowPeer->setDesignMode(bOn);
+ }
+ else
+ {
+ mbDesignMode = bOn;
+ }
+
+ // dispose our current AccessibleContext, if we have one
+ // (changing the design mode implies having a new implementation for this context,
+ // so the old one must be declared DEFUNC)
+ DisposeAccessibleContext(
+ Reference<XComponent>(maAccessibleContext, UNO_QUERY));
+ maAccessibleContext.clear();
+
+ // prepare firing an event
+ aModeChangeEvent.Source = *this;
+ aModeChangeEvent.NewMode = mbDesignMode ? std::u16string_view( u"design" ) : std::u16string_view( u"alive" );
+ }
+
+ // --- </mutex_lock> ---
+ maModeChangeListeners.notifyEach( &XModeChangeListener::modeChanged, aModeChangeEvent );
+}
+
+// XBoundComponent
+
+void SAL_CALL FmXGridControl::addUpdateListener(const Reference< XUpdateListener >& l)
+{
+ m_aUpdateListeners.addInterface( l );
+ if( getPeer().is() && m_aUpdateListeners.getLength() == 1 )
+ {
+ Reference< XBoundComponent > xBound(getPeer(), UNO_QUERY);
+ xBound->addUpdateListener( &m_aUpdateListeners);
+ }
+}
+
+
+void SAL_CALL FmXGridControl::removeUpdateListener(const Reference< XUpdateListener >& l)
+{
+ if( getPeer().is() && m_aUpdateListeners.getLength() == 1 )
+ {
+ Reference< XBoundComponent > xBound(getPeer(), UNO_QUERY);
+ xBound->removeUpdateListener( &m_aUpdateListeners);
+ }
+ m_aUpdateListeners.removeInterface( l );
+}
+
+
+sal_Bool SAL_CALL FmXGridControl::commit()
+{
+ Reference< XBoundComponent > xBound(getPeer(), UNO_QUERY);
+ if (xBound.is())
+ return xBound->commit();
+ else
+ return true;
+}
+
+// XContainer
+
+void SAL_CALL FmXGridControl::addContainerListener(const Reference< XContainerListener >& l)
+{
+ m_aContainerListeners.addInterface( l );
+ if( getPeer().is() && m_aContainerListeners.getLength() == 1 )
+ {
+ Reference< XContainer > xContainer(getPeer(), UNO_QUERY);
+ xContainer->addContainerListener( &m_aContainerListeners);
+ }
+}
+
+
+void SAL_CALL FmXGridControl::removeContainerListener(const Reference< XContainerListener >& l)
+{
+ if( getPeer().is() && m_aContainerListeners.getLength() == 1 )
+ {
+ Reference< XContainer > xContainer(getPeer(), UNO_QUERY);
+ xContainer->removeContainerListener( &m_aContainerListeners);
+ }
+ m_aContainerListeners.removeInterface( l );
+}
+
+
+Reference< css::frame::XDispatch > SAL_CALL FmXGridControl::queryDispatch(const css::util::URL& aURL, const OUString& aTargetFrameName, sal_Int32 nSearchFlags)
+{
+ Reference< css::frame::XDispatchProvider > xPeerProvider(getPeer(), UNO_QUERY);
+ if (xPeerProvider.is())
+ return xPeerProvider->queryDispatch(aURL, aTargetFrameName, nSearchFlags);
+ else
+ return Reference< css::frame::XDispatch > ();
+}
+
+
+Sequence< Reference< css::frame::XDispatch > > SAL_CALL FmXGridControl::queryDispatches(const Sequence< css::frame::DispatchDescriptor>& aDescripts)
+{
+ Reference< css::frame::XDispatchProvider > xPeerProvider(getPeer(), UNO_QUERY);
+ if (xPeerProvider.is())
+ return xPeerProvider->queryDispatches(aDescripts);
+ else
+ return Sequence< Reference< css::frame::XDispatch > >();
+}
+
+
+void SAL_CALL FmXGridControl::registerDispatchProviderInterceptor(const Reference< css::frame::XDispatchProviderInterceptor >& _xInterceptor)
+{
+ Reference< css::frame::XDispatchProviderInterception > xPeerInterception(getPeer(), UNO_QUERY);
+ if (xPeerInterception.is())
+ xPeerInterception->registerDispatchProviderInterceptor(_xInterceptor);
+}
+
+
+void SAL_CALL FmXGridControl::releaseDispatchProviderInterceptor(const Reference< css::frame::XDispatchProviderInterceptor >& _xInterceptor)
+{
+ Reference< css::frame::XDispatchProviderInterception > xPeerInterception(getPeer(), UNO_QUERY);
+ if (xPeerInterception.is())
+ xPeerInterception->releaseDispatchProviderInterceptor(_xInterceptor);
+}
+
+
+void SAL_CALL FmXGridControl::addGridControlListener( const Reference< XGridControlListener >& _listener )
+{
+ ::osl::MutexGuard aGuard( GetMutex() );
+
+ m_aGridControlListeners.addInterface( _listener );
+ if ( getPeer().is() && 1 == m_aGridControlListeners.getLength() )
+ {
+ Reference< XGridControl > xPeerGrid( getPeer(), UNO_QUERY );
+ if ( xPeerGrid.is() )
+ xPeerGrid->addGridControlListener( &m_aGridControlListeners );
+ }
+}
+
+
+void SAL_CALL FmXGridControl::removeGridControlListener( const Reference< XGridControlListener >& _listener )
+{
+ ::osl::MutexGuard aGuard( GetMutex() );
+
+ if( getPeer().is() && 1 == m_aGridControlListeners.getLength() )
+ {
+ Reference< XGridControl > xPeerGrid( getPeer(), UNO_QUERY );
+ if ( xPeerGrid.is() )
+ xPeerGrid->removeGridControlListener( &m_aGridControlListeners );
+ }
+
+ m_aGridControlListeners.removeInterface( _listener );
+}
+
+
+sal_Int16 SAL_CALL FmXGridControl::getCurrentColumnPosition()
+{
+ Reference< XGridControl > xGrid( getPeer(), UNO_QUERY );
+ return xGrid.is() ? xGrid->getCurrentColumnPosition() : -1;
+}
+
+
+void SAL_CALL FmXGridControl::setCurrentColumnPosition(sal_Int16 nPos)
+{
+ Reference< XGridControl > xGrid( getPeer(), UNO_QUERY );
+ if ( xGrid.is() )
+ {
+ SolarMutexGuard aGuard;
+ xGrid->setCurrentColumnPosition( nPos );
+ }
+}
+
+// XElementAccess
+
+sal_Bool SAL_CALL FmXGridControl::hasElements()
+{
+ Reference< XElementAccess > xPeer(getPeer(), UNO_QUERY);
+ return xPeer.is() && xPeer->hasElements();
+}
+
+
+Type SAL_CALL FmXGridControl::getElementType( )
+{
+ return cppu::UnoType<css::awt::XTextComponent>::get();
+}
+
+// XEnumerationAccess
+
+Reference< XEnumeration > SAL_CALL FmXGridControl::createEnumeration()
+{
+ Reference< XEnumerationAccess > xPeer(getPeer(), UNO_QUERY);
+ if (xPeer.is())
+ return xPeer->createEnumeration();
+ else
+ return new ::comphelper::OEnumerationByIndex(this);
+}
+
+// XIndexAccess
+
+sal_Int32 SAL_CALL FmXGridControl::getCount()
+{
+ Reference< XIndexAccess > xPeer(getPeer(), UNO_QUERY);
+ return xPeer.is() ? xPeer->getCount() : 0;
+}
+
+
+Any SAL_CALL FmXGridControl::getByIndex(sal_Int32 _nIndex)
+{
+ Reference< XIndexAccess > xPeer(getPeer(), UNO_QUERY);
+ if (!xPeer.is())
+ throw IndexOutOfBoundsException();
+
+ return xPeer->getByIndex(_nIndex);
+}
+
+// css::util::XModeSelector
+
+void SAL_CALL FmXGridControl::setMode(const OUString& Mode)
+{
+ Reference< css::util::XModeSelector > xPeer(getPeer(), UNO_QUERY);
+ if (!xPeer.is())
+ throw NoSupportException();
+
+ xPeer->setMode(Mode);
+}
+
+
+OUString SAL_CALL FmXGridControl::getMode()
+{
+ Reference< css::util::XModeSelector > xPeer(getPeer(), UNO_QUERY);
+ return xPeer.is() ? xPeer->getMode() : OUString();
+}
+
+
+css::uno::Sequence<OUString> SAL_CALL FmXGridControl::getSupportedModes()
+{
+ Reference< css::util::XModeSelector > xPeer(getPeer(), UNO_QUERY);
+ return xPeer.is() ? xPeer->getSupportedModes() : css::uno::Sequence<OUString>();
+}
+
+
+sal_Bool SAL_CALL FmXGridControl::supportsMode(const OUString& Mode)
+{
+ Reference< css::util::XModeSelector > xPeer(getPeer(), UNO_QUERY);
+ return xPeer.is() && xPeer->supportsMode(Mode);
+}
+
+void SAL_CALL FmXGridControl::setFocus()
+{
+ FmXGridPeer* pPeer = comphelper::getFromUnoTunnel<FmXGridPeer>(getPeer());
+ if (pPeer)
+ {
+ VclPtr<FmGridControl> xGrid = pPeer->GetAs<FmGridControl>();
+ bool bAlreadyHasFocus = xGrid->HasChildPathFocus() || xGrid->ControlHasFocus();
+ // if the focus is already in the control don't grab focus again which
+ // would grab focus away from any native widgets hosted in the control
+ if (bAlreadyHasFocus)
+ return;
+ }
+ UnoControl::setFocus();
+}
+
+// helper class which prevents that in the peer's header the FmGridListener must be known
+class FmXGridPeer::GridListenerDelegator : public FmGridListener
+{
+protected:
+ FmXGridPeer* m_pPeer;
+
+public:
+ explicit GridListenerDelegator( FmXGridPeer* _pPeer );
+ virtual ~GridListenerDelegator();
+
+protected:
+ virtual void selectionChanged() override;
+ virtual void columnChanged() override;
+};
+
+
+FmXGridPeer::GridListenerDelegator::GridListenerDelegator(FmXGridPeer* _pPeer)
+ :m_pPeer(_pPeer)
+{
+ DBG_ASSERT(m_pPeer, "GridListenerDelegator::GridListenerDelegator");
+}
+
+FmXGridPeer::GridListenerDelegator::~GridListenerDelegator()
+{
+}
+
+
+void FmXGridPeer::GridListenerDelegator::selectionChanged()
+{
+ m_pPeer->selectionChanged();
+}
+
+
+void FmXGridPeer::GridListenerDelegator::columnChanged()
+{
+ m_pPeer->columnChanged();
+}
+
+void FmXGridPeer::selectionChanged()
+{
+ EventObject aSource;
+ aSource.Source = static_cast< ::cppu::OWeakObject* >(this);
+ m_aSelectionListeners.notifyEach( &XSelectionChangeListener::selectionChanged, aSource);
+}
+
+
+void FmXGridPeer::columnChanged()
+{
+ EventObject aEvent( *this );
+ m_aGridControlListeners.notifyEach( &XGridControlListener::columnChanged, aEvent );
+}
+
+
+FmXGridPeer::FmXGridPeer(const Reference< XComponentContext >& _rxContext)
+ :m_xContext(_rxContext)
+ ,m_aModifyListeners(m_aMutex)
+ ,m_aUpdateListeners(m_aMutex)
+ ,m_aContainerListeners(m_aMutex)
+ ,m_aSelectionListeners(m_aMutex)
+ ,m_aGridControlListeners(m_aMutex)
+ ,m_aMode("DataMode")
+ ,m_nCursorListening(0)
+ ,m_bInterceptingDispatch(false)
+{
+ // Create must be called after this constructor
+ m_pGridListener.reset( new GridListenerDelegator( this ) );
+}
+
+
+VclPtr<FmGridControl> FmXGridPeer::imp_CreateControl(vcl::Window* pParent, WinBits nStyle)
+{
+ return VclPtr<FmGridControl>::Create(m_xContext, pParent, this, nStyle);
+}
+
+
+void FmXGridPeer::Create(vcl::Window* pParent, WinBits nStyle)
+{
+ VclPtr<FmGridControl> pWin = imp_CreateControl(pParent, nStyle);
+ DBG_ASSERT(pWin != nullptr, "FmXGridPeer::Create : imp_CreateControl didn't return a control !");
+
+ pWin->SetStateProvider(LINK(this, FmXGridPeer, OnQueryGridSlotState));
+ pWin->SetSlotExecutor(LINK(this, FmXGridPeer, OnExecuteGridSlot));
+
+ // want to hear about row selections
+ pWin->setGridListener( m_pGridListener.get() );
+
+ // Init must always be called
+ pWin->Init();
+ pWin->SetComponentInterface(this);
+
+ getSupportedURLs();
+}
+
+FmXGridPeer::~FmXGridPeer()
+{
+ setRowSet(Reference< XRowSet > ());
+ setColumns(Reference< XIndexContainer > ());
+}
+
+UNO3_GETIMPLEMENTATION2_IMPL(FmXGridPeer, VCLXWindow);
+
+// XEventListener
+
+void FmXGridPeer::disposing(const EventObject& e)
+{
+ bool bKnownSender = false;
+
+ Reference< XIndexContainer > xCols( e.Source, UNO_QUERY );
+ if ( xCols.is() )
+ {
+ setColumns(Reference< XIndexContainer > ());
+ bKnownSender = true;
+ }
+
+ Reference< XRowSet > xCursor(e.Source, UNO_QUERY);
+ if (xCursor.is())
+ {
+ setRowSet( m_xCursor );
+ m_xCursor = nullptr;
+ bKnownSender = true;
+ }
+
+
+ if ( !bKnownSender && m_pDispatchers )
+ {
+ const Sequence< URL>& aSupportedURLs = getSupportedURLs();
+ const URL* pSupportedURLs = aSupportedURLs.getConstArray();
+ for ( sal_Int32 i=0; i < ( aSupportedURLs.getLength() ) && !bKnownSender; ++i, ++pSupportedURLs )
+ {
+ if ( m_pDispatchers[i] == e.Source )
+ {
+ m_pDispatchers[i]->removeStatusListener( static_cast< css::frame::XStatusListener* >( this ), *pSupportedURLs );
+ m_pDispatchers[i] = nullptr;
+ m_pStateCache[i] = false;
+ bKnownSender = true;
+ }
+ }
+ }
+
+ if ( !bKnownSender )
+ VCLXWindow::disposing(e);
+}
+
+
+void FmXGridPeer::addModifyListener(const Reference< css::util::XModifyListener >& l)
+{
+ m_aModifyListeners.addInterface( l );
+}
+
+
+void FmXGridPeer::removeModifyListener(const Reference< css::util::XModifyListener >& l)
+{
+ m_aModifyListeners.removeInterface( l );
+}
+
+
+#define LAST_KNOWN_TYPE FormComponentType::PATTERNFIELD
+Sequence< sal_Bool > SAL_CALL FmXGridPeer::queryFieldDataType( const Type& xType )
+{
+ // a 'conversion table'
+ static const bool bCanConvert[LAST_KNOWN_TYPE][4] =
+ {
+ { false, false, false, false }, // FormComponentType::CONTROL
+ { false, false, false, false }, // FormComponentType::COMMANDBUTTON
+ { false, false, false, false }, // FormComponentType::RADIOBUTTON
+ { false, false, false, false }, // FormComponentType::IMAGEBUTTON
+ { false, false, false, true }, // FormComponentType::CHECKBOX
+ { false, false, false, false }, // FormComponentType::LISTBOX
+ { false, false, false, false }, // FormComponentType::COMBOBOX
+ { false, false, false, false }, // FormComponentType::GROUPBOX
+ { true , false, false, false }, // FormComponentType::TEXTFIELD
+ { false, false, false, false }, // FormComponentType::FIXEDTEXT
+ { false, false, false, false }, // FormComponentType::GRIDCONTROL
+ { false, false, false, false }, // FormComponentType::FILECONTROL
+ { false, false, false, false }, // FormComponentType::HIDDENCONTROL
+ { false, false, false, false }, // FormComponentType::IMAGECONTROL
+ { true , true , true , false }, // FormComponentType::DATEFIELD
+ { true , true , false, false }, // FormComponentType::TIMEFIELD
+ { true , true , false, false }, // FormComponentType::NUMERICFIELD
+ { true , true , false, false }, // FormComponentType::CURRENCYFIELD
+ { true , false, false, false } // FormComponentType::PATTERNFIELD
+ };
+
+
+ sal_Int16 nMapColumn = -1;
+ switch (xType.getTypeClass())
+ {
+ case TypeClass_STRING : nMapColumn = 0; break;
+ case TypeClass_FLOAT:
+ case TypeClass_DOUBLE : nMapColumn = 1; break;
+ case TypeClass_SHORT:
+ case TypeClass_LONG:
+ case TypeClass_UNSIGNED_LONG:
+ case TypeClass_UNSIGNED_SHORT : nMapColumn = 2; break;
+ case TypeClass_BOOLEAN : nMapColumn = 3; break;
+ default:
+ break;
+ }
+
+ Reference< XIndexContainer > xColumns = getColumns();
+
+ VclPtr< FmGridControl > pGrid = GetAs< FmGridControl >();
+ sal_Int32 nColumns = pGrid->GetViewColCount();
+
+ std::vector< std::unique_ptr<DbGridColumn> > const & aColumns = pGrid->GetColumns();
+
+ Sequence<sal_Bool> aReturnSequence(nColumns);
+ sal_Bool* pReturnArray = aReturnSequence.getArray();
+
+ bool bRequestedAsAny = (xType.getTypeClass() == TypeClass_ANY);
+
+ DbGridColumn* pCol;
+ Reference< css::sdb::XColumn > xFieldContent;
+ Reference< XPropertySet > xCurrentColumn;
+ for (sal_Int32 i=0; i<nColumns; ++i)
+ {
+ if (bRequestedAsAny)
+ {
+ pReturnArray[i] = true;
+ continue;
+ }
+
+ pReturnArray[i] = false;
+
+ sal_uInt16 nModelPos = pGrid->GetModelColumnPos(pGrid->GetColumnIdFromViewPos(static_cast<sal_uInt16>(i)));
+ DBG_ASSERT(nModelPos != sal_uInt16(-1), "FmXGridPeer::queryFieldDataType : no model pos !");
+
+ pCol = aColumns[ nModelPos ].get();
+ const DbGridRowRef xRow = pGrid->GetSeekRow();
+ xFieldContent = (xRow.is() && xRow->HasField(pCol->GetFieldPos())) ? xRow->GetField(pCol->GetFieldPos()).getColumn() : Reference< css::sdb::XColumn > ();
+ if (!xFieldContent.is())
+ // can't supply anything without a field content
+ // FS - 07.12.99 - 54391
+ continue;
+
+ xColumns->getByIndex(nModelPos) >>= xCurrentColumn;
+ if (!::comphelper::hasProperty(FM_PROP_CLASSID, xCurrentColumn))
+ continue;
+
+ sal_Int16 nClassId = sal_Int16();
+ xCurrentColumn->getPropertyValue(FM_PROP_CLASSID) >>= nClassId;
+ if (nClassId>LAST_KNOWN_TYPE)
+ continue;
+ DBG_ASSERT(nClassId>0, "FmXGridPeer::queryFieldDataType : somebody changed the definition of the FormComponentType enum !");
+
+ if (nMapColumn != -1)
+ pReturnArray[i] = bCanConvert[nClassId-1][nMapColumn];
+ }
+
+ return aReturnSequence;
+}
+
+
+Sequence< Any > SAL_CALL FmXGridPeer::queryFieldData( sal_Int32 nRow, const Type& xType )
+{
+ VclPtr< FmGridControl > pGrid = GetAs< FmGridControl >();
+ DBG_ASSERT(pGrid && pGrid->IsOpen(), "FmXGridPeer::queryFieldData : have no valid grid window !");
+ if (!pGrid || !pGrid->IsOpen())
+ return Sequence< Any>();
+
+ // move the control to the specified row
+ if (!pGrid->SeekRow(nRow))
+ {
+ throw IllegalArgumentException();
+ }
+
+ // don't use GetCurrentRow as this isn't affected by the above SeekRow
+ // FS - 30.09.99 - 68644
+ DbGridRowRef xPaintRow = pGrid->GetPaintRow();
+ ENSURE_OR_THROW( xPaintRow.is(), "invalid paint row" );
+
+ // I need the columns of the control for GetFieldText
+ std::vector< std::unique_ptr<DbGridColumn> > const & aColumns = pGrid->GetColumns();
+
+ // and through all the columns
+ sal_Int32 nColumnCount = pGrid->GetViewColCount();
+
+ Sequence< Any> aReturnSequence(nColumnCount);
+ Any* pReturnArray = aReturnSequence.getArray();
+
+ bool bRequestedAsAny = (xType.getTypeClass() == TypeClass_ANY);
+ Reference< css::sdb::XColumn > xFieldContent;
+ for (sal_Int32 i=0; i < nColumnCount; ++i)
+ {
+ sal_uInt16 nModelPos = pGrid->GetModelColumnPos(pGrid->GetColumnIdFromViewPos(static_cast<sal_uInt16>(i)));
+ DBG_ASSERT(nModelPos != sal_uInt16(-1), "FmXGridPeer::queryFieldData : invalid model pos !");
+
+ // don't use GetCurrentFieldValue to determine the field content as this isn't affected by the above SeekRow
+ // FS - 30.09.99 - 68644
+ DbGridColumn* pCol = aColumns[ nModelPos ].get();
+ xFieldContent = xPaintRow->HasField( pCol->GetFieldPos() )
+ ? xPaintRow->GetField( pCol->GetFieldPos() ).getColumn()
+ : Reference< XColumn > ();
+
+ if ( !xFieldContent.is() )
+ continue;
+
+ if (bRequestedAsAny)
+ {
+ Reference< XPropertySet > xFieldSet(xFieldContent, UNO_QUERY);
+ pReturnArray[i] = xFieldSet->getPropertyValue(FM_PROP_VALUE);
+ }
+ else
+ {
+ switch (xType.getTypeClass())
+ {
+ // Strings are dealt with directly by the GetFieldText
+ case TypeClass_STRING :
+ {
+ OUString sText = aColumns[ nModelPos ]->GetCellText( xPaintRow.get(), pGrid->getNumberFormatter() );
+ pReturnArray[i] <<= sText;
+ }
+ break;
+ // everything else is requested in the DatabaseVariant
+ case TypeClass_FLOAT : pReturnArray[i] <<= xFieldContent->getFloat(); break;
+ case TypeClass_DOUBLE : pReturnArray[i] <<= xFieldContent->getDouble(); break;
+ case TypeClass_SHORT : pReturnArray[i] <<= xFieldContent->getShort(); break;
+ case TypeClass_LONG : pReturnArray[i] <<= static_cast<sal_Int32>(xFieldContent->getLong()); break;
+ case TypeClass_UNSIGNED_SHORT : pReturnArray[i] <<= static_cast<sal_uInt16>(xFieldContent->getShort()); break;
+ case TypeClass_UNSIGNED_LONG : pReturnArray[i] <<= static_cast<sal_uInt32>(xFieldContent->getLong()); break;
+ case TypeClass_BOOLEAN : pReturnArray[i] <<= xFieldContent->getBoolean(); break;
+ default:
+ {
+ throw IllegalArgumentException();
+ }
+ }
+ }
+ }
+ return aReturnSequence;
+}
+
+
+void FmXGridPeer::CellModified()
+{
+ EventObject aEvt;
+ aEvt.Source = static_cast< ::cppu::OWeakObject* >(this);
+ m_aModifyListeners.notifyEach( &XModifyListener::modified, aEvt );
+}
+
+// XPropertyChangeListener
+
+void FmXGridPeer::propertyChange(const PropertyChangeEvent& evt)
+{
+ SolarMutexGuard aGuard;
+ // want to do a lot of VCL stuff here ...
+ // this should not be (deadlock) critical, as by definition, every component should release
+ // any own mutexes before notifying
+
+ VclPtr< FmGridControl > pGrid = GetAs< FmGridControl >();
+ if (!pGrid)
+ return;
+
+ // Database event
+ if (evt.PropertyName == FM_PROP_VALUE || m_xCursor == evt.Source)
+ pGrid->propertyChange(evt);
+ else if (pGrid && m_xColumns.is() && m_xColumns->hasElements())
+ {
+ // next find which column has changed
+ css::uno::Reference<css::uno::XInterface> xCurrent;
+ sal_Int32 i;
+
+ for ( i = 0; i < m_xColumns->getCount(); i++)
+ {
+ xCurrent.set(m_xColumns->getByIndex(i), css::uno::UNO_QUERY);
+ if (evt.Source == xCurrent)
+ break;
+ }
+
+ if (i >= m_xColumns->getCount())
+ // this is valid because we are listening at the cursor, too (RecordCount, -status, edit mode)
+ return;
+
+ sal_uInt16 nId = pGrid->GetColumnIdFromModelPos(static_cast<sal_uInt16>(i));
+ bool bInvalidateColumn = false;
+
+ if (evt.PropertyName == FM_PROP_LABEL)
+ {
+ OUString aName = ::comphelper::getString(evt.NewValue);
+ if (aName != pGrid->GetColumnTitle(nId))
+ pGrid->SetColumnTitle(nId, aName);
+ }
+ else if (evt.PropertyName == FM_PROP_WIDTH)
+ {
+ sal_Int32 nWidth = 0;
+ if (evt.NewValue.getValueType().getTypeClass() == TypeClass_VOID)
+ nWidth = pGrid->GetDefaultColumnWidth(pGrid->GetColumnTitle(nId));
+ // GetDefaultColumnWidth already considered the zoom factor
+ else
+ {
+ sal_Int32 nTest = 0;
+ if (evt.NewValue >>= nTest)
+ {
+ nWidth = pGrid->LogicToPixel(Point(nTest, 0), MapMode(MapUnit::Map10thMM)).X();
+ // take the zoom factor into account
+ nWidth = pGrid->CalcZoom(nWidth);
+ }
+ }
+ if (nWidth != (sal_Int32(pGrid->GetColumnWidth(nId))))
+ {
+ if (pGrid->IsEditing())
+ {
+ pGrid->DeactivateCell();
+ pGrid->ActivateCell();
+ }
+ pGrid->SetColumnWidth(nId, nWidth);
+ }
+ }
+ else if (evt.PropertyName == FM_PROP_HIDDEN)
+ {
+ DBG_ASSERT(evt.NewValue.getValueType().getTypeClass() == TypeClass_BOOLEAN,
+ "FmXGridPeer::propertyChange : the property 'hidden' should be of type boolean !");
+ if (::comphelper::getBOOL(evt.NewValue))
+ pGrid->HideColumn(nId);
+ else
+ pGrid->ShowColumn(nId);
+ }
+ else if (evt.PropertyName == FM_PROP_ALIGN)
+ {
+ // in design mode it doesn't matter
+ if (!isDesignMode())
+ {
+ DbGridColumn* pCol = pGrid->GetColumns()[i].get();
+
+ pCol->SetAlignmentFromModel(-1);
+ bInvalidateColumn = true;
+ }
+ }
+ else if (evt.PropertyName == FM_PROP_FORMATKEY)
+ {
+ if (!isDesignMode())
+ bInvalidateColumn = true;
+ }
+
+ // need to invalidate the affected column ?
+ if (bInvalidateColumn)
+ {
+ bool bWasEditing = pGrid->IsEditing();
+ if (bWasEditing)
+ pGrid->DeactivateCell();
+
+ ::tools::Rectangle aColRect = pGrid->GetFieldRect(nId);
+ aColRect.SetTop( 0 );
+ aColRect.SetBottom( pGrid->GetSizePixel().Height() );
+ pGrid->Invalidate(aColRect);
+
+ if (bWasEditing)
+ pGrid->ActivateCell();
+ }
+ }
+}
+
+// XBoundComponent
+
+void FmXGridPeer::addUpdateListener(const Reference< XUpdateListener >& l)
+{
+ m_aUpdateListeners.addInterface(l);
+}
+
+
+void FmXGridPeer::removeUpdateListener(const Reference< XUpdateListener >& l)
+{
+ m_aUpdateListeners.removeInterface(l);
+}
+
+
+sal_Bool FmXGridPeer::commit()
+{
+ VclPtr< FmGridControl > pGrid = GetAs< FmGridControl >();
+ if (!m_xCursor.is() || !pGrid)
+ return true;
+
+ EventObject aEvt(static_cast< ::cppu::OWeakObject* >(this));
+ ::comphelper::OInterfaceIteratorHelper3 aIter(m_aUpdateListeners);
+ bool bCancel = false;
+ while (aIter.hasMoreElements() && !bCancel)
+ if ( !aIter.next()->approveUpdate( aEvt ) )
+ bCancel = true;
+
+ if (!bCancel)
+ bCancel = !pGrid->commit();
+
+ if (!bCancel)
+ m_aUpdateListeners.notifyEach( &XUpdateListener::updated, aEvt );
+ return !bCancel;
+}
+
+
+void FmXGridPeer::cursorMoved(const EventObject& _rEvent)
+{
+ VclPtr< FmGridControl > pGrid = GetAs< FmGridControl >();
+ // we are not interested in moving to insert row only in the reset event
+ // which is fired after positioning and the insert row
+ if (pGrid && pGrid->IsOpen() && !::comphelper::getBOOL(Reference< XPropertySet > (_rEvent.Source, UNO_QUERY_THROW)->getPropertyValue(FM_PROP_ISNEW)))
+ pGrid->positioned();
+}
+
+
+void FmXGridPeer::rowChanged(const EventObject& /*_rEvent*/)
+{
+ VclPtr< FmGridControl > pGrid = GetAs< FmGridControl >();
+ if (pGrid && pGrid->IsOpen())
+ {
+ if (m_xCursor->rowUpdated() && !pGrid->IsCurrentAppending())
+ pGrid->RowModified(pGrid->GetCurrentPos());
+ else if (m_xCursor->rowInserted())
+ pGrid->inserted();
+ }
+}
+
+
+void FmXGridPeer::rowSetChanged(const EventObject& /*event*/)
+{
+ // not interested in ...
+ // (our parent is a form which means we get a loaded or reloaded after this rowSetChanged)
+}
+
+// XLoadListener
+
+void FmXGridPeer::loaded(const EventObject& /*rEvent*/)
+{
+ updateGrid(m_xCursor);
+}
+
+
+void FmXGridPeer::unloaded(const EventObject& /*rEvent*/)
+{
+ updateGrid( Reference< XRowSet > (nullptr) );
+}
+
+
+void FmXGridPeer::reloading(const EventObject& /*aEvent*/)
+{
+ // empty the grid
+ updateGrid( Reference< XRowSet > (nullptr) );
+}
+
+
+void FmXGridPeer::unloading(const EventObject& /*aEvent*/)
+{
+ // empty the grid
+ updateGrid( Reference< XRowSet > (nullptr) );
+}
+
+
+void FmXGridPeer::reloaded(const EventObject& aEvent)
+{
+ {
+ const sal_Int32 cnt = m_xColumns->getCount();
+ for(sal_Int32 i=0; i<cnt; ++i)
+ {
+ Reference< XLoadListener> xll(m_xColumns->getByIndex(i), UNO_QUERY);
+ if(xll.is())
+ {
+ xll->reloaded(aEvent);
+ }
+ }
+ }
+ updateGrid(m_xCursor);
+}
+
+// XGridPeer
+
+Reference< XIndexContainer > FmXGridPeer::getColumns()
+{
+ return m_xColumns;
+}
+
+
+void FmXGridPeer::addColumnListeners(const Reference< XPropertySet >& xCol)
+{
+ static const rtl::OUStringConstExpr aPropsListenedTo[] =
+ {
+ FM_PROP_LABEL, FM_PROP_WIDTH, FM_PROP_HIDDEN, FM_PROP_ALIGN,
+ FM_PROP_FORMATKEY
+ };
+
+ // as not all properties have to be supported by all columns we have to check this
+ // before adding a listener
+ Reference< XPropertySetInfo > xInfo = xCol->getPropertySetInfo();
+ for (size_t i=0; i<SAL_N_ELEMENTS(aPropsListenedTo); ++i)
+ {
+ if ( xInfo->hasPropertyByName( aPropsListenedTo[i] ) )
+ {
+ Property aPropDesc = xInfo->getPropertyByName( aPropsListenedTo[i] );
+ if ( 0 != ( aPropDesc.Attributes & PropertyAttribute::BOUND ) )
+ xCol->addPropertyChangeListener( aPropsListenedTo[i], this );
+ }
+ }
+}
+
+
+void FmXGridPeer::removeColumnListeners(const Reference< XPropertySet >& xCol)
+{
+ // the same props as in addColumnListeners... linux has problems with global static UStrings, so
+ // we have to do it this way...
+ static const rtl::OUStringConstExpr aPropsListenedTo[] =
+ {
+ FM_PROP_LABEL, FM_PROP_WIDTH, FM_PROP_HIDDEN, FM_PROP_ALIGN,
+ FM_PROP_FORMATKEY
+ };
+
+ Reference< XPropertySetInfo > xInfo = xCol->getPropertySetInfo();
+ for (const auto & i : aPropsListenedTo)
+ if (xInfo->hasPropertyByName(i))
+ xCol->removePropertyChangeListener(i, this);
+}
+
+
+void FmXGridPeer::setColumns(const Reference< XIndexContainer >& Columns)
+{
+ SolarMutexGuard aGuard;
+
+ VclPtr< FmGridControl > pGrid = GetAs< FmGridControl >();
+
+ if (m_xColumns.is())
+ {
+ Reference< XPropertySet > xCol;
+ for (sal_Int32 i = 0; i < m_xColumns->getCount(); i++)
+ {
+ xCol.set(m_xColumns->getByIndex(i), css::uno::UNO_QUERY);
+ removeColumnListeners(xCol);
+ }
+ Reference< XContainer > xContainer(m_xColumns, UNO_QUERY);
+ xContainer->removeContainerListener(this);
+
+ Reference< XSelectionSupplier > xSelSupplier(m_xColumns, UNO_QUERY);
+ xSelSupplier->removeSelectionChangeListener(this);
+
+ Reference< XReset > xColumnReset(m_xColumns, UNO_QUERY);
+ if (xColumnReset.is())
+ xColumnReset->removeResetListener(static_cast<XResetListener*>(this));
+ }
+ if (Columns.is())
+ {
+ Reference< XContainer > xContainer(Columns, UNO_QUERY);
+ xContainer->addContainerListener(this);
+
+ Reference< XSelectionSupplier > xSelSupplier(Columns, UNO_QUERY);
+ xSelSupplier->addSelectionChangeListener(this);
+
+ Reference< XPropertySet > xCol;
+ for (sal_Int32 i = 0; i < Columns->getCount(); i++)
+ {
+ xCol.set(Columns->getByIndex(i), css::uno::UNO_QUERY);
+ addColumnListeners(xCol);
+ }
+
+ Reference< XReset > xColumnReset(Columns, UNO_QUERY);
+ if (xColumnReset.is())
+ xColumnReset->addResetListener(static_cast<XResetListener*>(this));
+ }
+ m_xColumns = Columns;
+ if (pGrid)
+ {
+ pGrid->InitColumnsByModels(m_xColumns);
+
+ if (m_xColumns.is())
+ {
+ EventObject aEvt(m_xColumns);
+ selectionChanged(aEvt);
+ }
+ }
+}
+
+
+void FmXGridPeer::setDesignMode(sal_Bool bOn)
+{
+ if (bOn != isDesignMode())
+ {
+ VclPtr<vcl::Window> pWin = GetWindow();
+ if (pWin)
+ static_cast<FmGridControl*>(pWin.get())->SetDesignMode(bOn);
+ }
+
+ if (bOn)
+ DisConnectFromDispatcher();
+ else
+ UpdateDispatches(); // will connect if not already connected and just update else
+}
+
+
+sal_Bool FmXGridPeer::isDesignMode()
+{
+ VclPtr<vcl::Window> pWin = GetWindow();
+ if (pWin)
+ return static_cast<FmGridControl*>(pWin.get())->IsDesignMode();
+ else
+ return false;
+}
+
+
+void FmXGridPeer::elementInserted(const ContainerEvent& evt)
+{
+ SolarMutexGuard aGuard;
+
+ VclPtr< FmGridControl > pGrid = GetAs< FmGridControl >();
+ // take handle column into account
+ if (!pGrid || !m_xColumns.is() || pGrid->IsInColumnMove() || m_xColumns->getCount() == static_cast<sal_Int32>(pGrid->GetModelColCount()))
+ return;
+
+ Reference< XPropertySet > xNewColumn(evt.Element, css::uno::UNO_QUERY);
+ addColumnListeners(xNewColumn);
+
+ OUString aName = ::comphelper::getString(xNewColumn->getPropertyValue(FM_PROP_LABEL));
+ Any aWidth = xNewColumn->getPropertyValue(FM_PROP_WIDTH);
+ sal_Int32 nWidth = 0;
+ if (aWidth >>= nWidth)
+ nWidth = pGrid->LogicToPixel(Point(nWidth, 0), MapMode(MapUnit::Map10thMM)).X();
+
+ pGrid->AppendColumn(aName, static_cast<sal_uInt16>(nWidth), static_cast<sal_Int16>(::comphelper::getINT32(evt.Accessor)));
+
+ // now set the column
+ DbGridColumn* pCol = pGrid->GetColumns()[ ::comphelper::getINT32(evt.Accessor) ].get();
+ pCol->setModel(xNewColumn);
+
+ Any aHidden = xNewColumn->getPropertyValue(FM_PROP_HIDDEN);
+ if (::comphelper::getBOOL(aHidden))
+ pGrid->HideColumn(pCol->GetId());
+
+ FormControlFactory( m_xContext ).initializeTextFieldLineEnds( xNewColumn );
+}
+
+
+void FmXGridPeer::elementReplaced(const ContainerEvent& evt)
+{
+ SolarMutexGuard aGuard;
+
+ VclPtr< FmGridControl > pGrid = GetAs< FmGridControl >();
+
+ // take handle column into account
+ if (!pGrid || !m_xColumns.is() || pGrid->IsInColumnMove())
+ return;
+
+ Reference< XPropertySet > xNewColumn(evt.Element, css::uno::UNO_QUERY);
+ Reference< XPropertySet > xOldColumn(
+ evt.ReplacedElement, css::uno::UNO_QUERY);
+
+ bool bWasEditing = pGrid->IsEditing();
+ if (bWasEditing)
+ pGrid->DeactivateCell();
+
+ pGrid->RemoveColumn(pGrid->GetColumnIdFromModelPos(static_cast<sal_uInt16>(::comphelper::getINT32(evt.Accessor))));
+
+ removeColumnListeners(xOldColumn);
+ addColumnListeners(xNewColumn);
+
+ OUString aName = ::comphelper::getString(xNewColumn->getPropertyValue(FM_PROP_LABEL));
+ Any aWidth = xNewColumn->getPropertyValue(FM_PROP_WIDTH);
+ sal_Int32 nWidth = 0;
+ if (aWidth >>= nWidth)
+ nWidth = pGrid->LogicToPixel(Point(nWidth, 0), MapMode(MapUnit::Map10thMM)).X();
+ sal_uInt16 nNewId = pGrid->AppendColumn(aName, static_cast<sal_uInt16>(nWidth), static_cast<sal_Int16>(::comphelper::getINT32(evt.Accessor)));
+ sal_uInt16 nNewPos = pGrid->GetModelColumnPos(nNewId);
+
+ // set the model of the new column
+ DbGridColumn* pCol = pGrid->GetColumns()[ nNewPos ].get();
+
+ // for initializing this grid column, we need the fields of the grid's data source
+ Reference< XColumnsSupplier > xSuppColumns;
+ CursorWrapper* pGridDataSource = pGrid->getDataSource();
+ if ( pGridDataSource )
+ xSuppColumns.set(Reference< XInterface >( *pGridDataSource ), css::uno::UNO_QUERY);
+ Reference< XNameAccess > xColumnsByName;
+ if ( xSuppColumns.is() )
+ xColumnsByName = xSuppColumns->getColumns();
+ Reference< XIndexAccess > xColumnsByIndex( xColumnsByName, UNO_QUERY );
+
+ if ( xColumnsByIndex.is() )
+ FmGridControl::InitColumnByField( pCol, xNewColumn, xColumnsByName, xColumnsByIndex );
+ else
+ // the simple version, applies when the grid is not yet connected to a data source
+ pCol->setModel(xNewColumn);
+
+ if (bWasEditing)
+ pGrid->ActivateCell();
+}
+
+
+void FmXGridPeer::elementRemoved(const ContainerEvent& evt)
+{
+ SolarMutexGuard aGuard;
+
+ VclPtr< FmGridControl > pGrid = GetAs< FmGridControl >();
+
+ // take handle column into account
+ if (!pGrid || !m_xColumns.is() || pGrid->IsInColumnMove() || m_xColumns->getCount() == static_cast<sal_Int32>(pGrid->GetModelColCount()))
+ return;
+
+ pGrid->RemoveColumn(pGrid->GetColumnIdFromModelPos(static_cast<sal_uInt16>(::comphelper::getINT32(evt.Accessor))));
+
+ Reference< XPropertySet > xOldColumn(evt.Element, css::uno::UNO_QUERY);
+ removeColumnListeners(xOldColumn);
+}
+
+
+void FmXGridPeer::setProperty( const OUString& PropertyName, const Any& Value)
+{
+ SolarMutexGuard aGuard;
+
+ VclPtr< FmGridControl > pGrid = GetAs< FmGridControl >();
+
+ bool bVoid = !Value.hasValue();
+
+ if ( PropertyName == FM_PROP_TEXTLINECOLOR )
+ {
+ ::Color aTextLineColor( bVoid ? COL_TRANSPARENT : ::Color(ColorTransparency, ::comphelper::getINT32( Value )) );
+ if (bVoid)
+ {
+ pGrid->SetTextLineColor();
+ pGrid->GetDataWindow().SetTextLineColor();
+ }
+ else
+ {
+ pGrid->SetTextLineColor(aTextLineColor);
+ pGrid->GetDataWindow().SetTextLineColor(aTextLineColor);
+ }
+
+ // need to forward this to the columns
+ std::vector< std::unique_ptr<DbGridColumn> > const & rColumns = pGrid->GetColumns();
+ for (auto const & pLoop : rColumns)
+ {
+ FmXGridCell* pXCell = pLoop->GetCell();
+ if (pXCell)
+ {
+ if (bVoid)
+ pXCell->SetTextLineColor();
+ else
+ pXCell->SetTextLineColor(aTextLineColor);
+ }
+ }
+
+ if (isDesignMode())
+ pGrid->Invalidate();
+ }
+ else if ( PropertyName == FM_PROP_FONTEMPHASISMARK )
+ {
+ vcl::Font aGridFont = pGrid->GetControlFont();
+ sal_Int16 nValue = ::comphelper::getINT16(Value);
+ aGridFont.SetEmphasisMark( static_cast<FontEmphasisMark>(nValue) );
+ pGrid->SetControlFont( aGridFont );
+ }
+ else if ( PropertyName == FM_PROP_FONTRELIEF )
+ {
+ vcl::Font aGridFont = pGrid->GetControlFont();
+ sal_Int16 nValue = ::comphelper::getINT16(Value);
+ aGridFont.SetRelief( static_cast<FontRelief>(nValue) );
+ pGrid->SetControlFont( aGridFont );
+ }
+ else if ( PropertyName == FM_PROP_HELPURL )
+ {
+ OUString sHelpURL;
+ OSL_VERIFY( Value >>= sHelpURL );
+ INetURLObject aHID( sHelpURL );
+ if ( aHID.GetProtocol() == INetProtocol::Hid )
+ sHelpURL = aHID.GetURLPath();
+ pGrid->SetHelpId( OUStringToOString( sHelpURL, RTL_TEXTENCODING_UTF8 ) );
+ }
+ else if ( PropertyName == FM_PROP_DISPLAYSYNCHRON )
+ {
+ pGrid->setDisplaySynchron(::comphelper::getBOOL(Value));
+ }
+ else if ( PropertyName == FM_PROP_CURSORCOLOR )
+ {
+ if (bVoid)
+ pGrid->SetCursorColor(COL_TRANSPARENT);
+ else
+ pGrid->SetCursorColor( ::Color(ColorTransparency, ::comphelper::getINT32(Value)));
+ if (isDesignMode())
+ pGrid->Invalidate();
+ }
+ else if ( PropertyName == FM_PROP_ALWAYSSHOWCURSOR )
+ {
+ pGrid->EnablePermanentCursor(::comphelper::getBOOL(Value));
+ if (isDesignMode())
+ pGrid->Invalidate();
+ }
+ else if ( PropertyName == FM_PROP_FONT )
+ {
+ if ( bVoid )
+ pGrid->SetControlFont( vcl::Font() );
+ else
+ {
+ css::awt::FontDescriptor aFont;
+ if (Value >>= aFont)
+ {
+ vcl::Font aNewVclFont;
+ if (aFont != ::comphelper::getDefaultFont()) // is this the default
+ aNewVclFont = ImplCreateFont( aFont );
+
+ // need to add relief and emphasis (they're stored in a VCL-Font, but not in a FontDescriptor
+ vcl::Font aOldVclFont = pGrid->GetControlFont();
+ aNewVclFont.SetRelief( aOldVclFont.GetRelief() );
+ aNewVclFont.SetEmphasisMark( aOldVclFont.GetEmphasisMark() );
+
+ // now set it ...
+ pGrid->SetControlFont( aNewVclFont );
+
+ // if our row-height property is void (which means "calculate it font-dependent") we have
+ // to adjust the control's row height
+ Reference< XPropertySet > xModelSet(getColumns(), UNO_QUERY);
+ if (xModelSet.is() && ::comphelper::hasProperty(FM_PROP_ROWHEIGHT, xModelSet))
+ {
+ Any aHeight = xModelSet->getPropertyValue(FM_PROP_ROWHEIGHT);
+ if (!aHeight.hasValue())
+ pGrid->SetDataRowHeight(0);
+ }
+
+ }
+ }
+ }
+ else if ( PropertyName == FM_PROP_BACKGROUNDCOLOR )
+ {
+ if ( bVoid )
+ {
+ pGrid->SetControlBackground();
+ }
+ else
+ {
+ ::Color aColor( ColorTransparency, ::comphelper::getINT32(Value) );
+ pGrid->SetBackground( aColor );
+ pGrid->SetControlBackground( aColor );
+ }
+ }
+ else if ( PropertyName == FM_PROP_TEXTCOLOR )
+ {
+ if ( bVoid )
+ {
+ pGrid->SetControlForeground();
+ }
+ else
+ {
+ ::Color aColor( ColorTransparency, ::comphelper::getINT32(Value) );
+ pGrid->SetTextColor( aColor );
+ pGrid->SetControlForeground( aColor );
+ }
+ }
+ else if ( PropertyName == FM_PROP_ROWHEIGHT )
+ {
+ sal_Int32 nLogHeight(0);
+ if (Value >>= nLogHeight)
+ {
+ sal_Int32 nHeight = pGrid->LogicToPixel(Point(0, nLogHeight), MapMode(MapUnit::Map10thMM)).Y();
+ // take the zoom factor into account
+ nHeight = pGrid->CalcZoom(nHeight);
+ pGrid->SetDataRowHeight(nHeight);
+ }
+ else if (bVoid)
+ pGrid->SetDataRowHeight(0);
+ }
+ else if ( PropertyName == FM_PROP_HASNAVIGATION )
+ {
+ bool bValue( true );
+ OSL_VERIFY( Value >>= bValue );
+ pGrid->EnableNavigationBar( bValue );
+ }
+ else if ( PropertyName == FM_PROP_RECORDMARKER )
+ {
+ bool bValue( true );
+ OSL_VERIFY( Value >>= bValue );
+ pGrid->EnableHandle( bValue );
+ }
+ else if ( PropertyName == FM_PROP_ENABLED )
+ {
+ bool bValue( true );
+ OSL_VERIFY( Value >>= bValue );
+
+ // In design mode, disable only the data window.
+ // Else the control cannot be configured anymore.
+ if (isDesignMode())
+ pGrid->GetDataWindow().Enable( bValue );
+ else
+ pGrid->Enable( bValue );
+ }
+ else
+ VCLXWindow::setProperty( PropertyName, Value );
+}
+
+
+Reference< XAccessibleContext > FmXGridPeer::CreateAccessibleContext()
+{
+ Reference< XAccessibleContext > xContext;
+
+ // use the AccessibleContext provided by the VCL window
+ VclPtr<vcl::Window> pGrid = GetWindow();
+ if ( pGrid )
+ {
+ Reference< XAccessible > xAcc( pGrid->GetAccessible() );
+ if ( xAcc.is() )
+ xContext = xAcc->getAccessibleContext();
+ // TODO: this has a slight conceptual problem:
+
+ // We know that the XAccessible and XAccessibleContext implementation of the browse
+ // box is the same (the class implements both interfaces), which, speaking strictly,
+ // is bad here (means when a browse box acts as UnoControl): We (the FmXGridPeer) are
+ // the XAccessible here, and the browse box should be able to provide us an XAccessibleContext,
+ // but it should _not_ be the XAccessible itself.
+ // However, as long as no client implementation uses dirty hacks such as querying an
+ // XAccessibleContext for XAccessible, this should not be a problem.
+ }
+
+ if ( !xContext.is() )
+ xContext = VCLXWindow::CreateAccessibleContext( );
+
+ return xContext;
+}
+
+
+Any FmXGridPeer::getProperty( const OUString& _rPropertyName )
+{
+ Any aProp;
+ if (GetWindow())
+ {
+ VclPtr< FmGridControl > pGrid = GetAs< FmGridControl >();
+ vcl::Window* pDataWindow = &pGrid->GetDataWindow();
+
+ if ( _rPropertyName == FM_PROP_NAME )
+ {
+ vcl::Font aFont = pDataWindow->GetControlFont();
+ aProp <<= ImplCreateFontDescriptor( aFont );
+ }
+ else if ( _rPropertyName == FM_PROP_TEXTCOLOR )
+ {
+ aProp <<= pDataWindow->GetControlForeground();
+ }
+ else if ( _rPropertyName == FM_PROP_BACKGROUNDCOLOR )
+ {
+ aProp <<= pDataWindow->GetControlBackground();
+ }
+ else if ( _rPropertyName == FM_PROP_ROWHEIGHT )
+ {
+ sal_Int32 nPixelHeight = pGrid->GetDataRowHeight();
+ // take the zoom factor into account
+ nPixelHeight = pGrid->CalcReverseZoom(nPixelHeight);
+ aProp <<= static_cast<sal_Int32>(pGrid->PixelToLogic(Point(0, nPixelHeight), MapMode(MapUnit::Map10thMM)).Y());
+ }
+ else if ( _rPropertyName == FM_PROP_HASNAVIGATION )
+ {
+ bool bHasNavBar = pGrid->HasNavigationBar();
+ aProp <<= bHasNavBar;
+ }
+ else if ( _rPropertyName == FM_PROP_RECORDMARKER )
+ {
+ bool bHasHandle = pGrid->HasHandle();
+ aProp <<= bHasHandle;
+ }
+ else if ( _rPropertyName == FM_PROP_ENABLED )
+ {
+ aProp <<= pDataWindow->IsEnabled();
+ }
+ else
+ aProp = VCLXWindow::getProperty( _rPropertyName );
+ }
+ return aProp;
+}
+
+
+void FmXGridPeer::dispose()
+{
+ EventObject aEvt;
+ aEvt.Source = static_cast< ::cppu::OWeakObject* >(this);
+ m_aModifyListeners.disposeAndClear(aEvt);
+ m_aUpdateListeners.disposeAndClear(aEvt);
+ m_aContainerListeners.disposeAndClear(aEvt);
+
+ // release all interceptors
+ Reference< XDispatchProviderInterceptor > xInterceptor( m_xFirstDispatchInterceptor );
+ m_xFirstDispatchInterceptor.clear();
+ while ( xInterceptor.is() )
+ {
+ // tell the interceptor it has a new (means no) predecessor
+ xInterceptor->setMasterDispatchProvider( nullptr );
+
+ // ask for its successor
+ Reference< XDispatchProvider > xSlave = xInterceptor->getSlaveDispatchProvider();
+ // and give it the new (means no) successoert
+ xInterceptor->setSlaveDispatchProvider( nullptr );
+
+ // start over with the next chain element
+ xInterceptor.set(xSlave, css::uno::UNO_QUERY);
+ }
+
+ DisConnectFromDispatcher();
+
+ // unregister all listeners
+ if (m_xCursor.is())
+ {
+ m_xCursor->removeRowSetListener(this);
+
+ Reference< XReset > xReset(m_xCursor, UNO_QUERY);
+ if (xReset.is())
+ xReset->removeResetListener(this);
+ Reference< XLoadable > xLoadable(m_xCursor, UNO_QUERY);
+ if (xLoadable.is())
+ xLoadable->removeLoadListener(this);
+ Reference< XPropertySet > xSet(m_xCursor, UNO_QUERY);
+ if (xSet.is())
+ {
+ xSet->removePropertyChangeListener(FM_PROP_ISMODIFIED, this);
+ xSet->removePropertyChangeListener(FM_PROP_ROWCOUNT, this);
+ }
+ m_xCursor.clear();
+ }
+
+ VclPtr< FmGridControl > pGrid = GetAs< FmGridControl >();
+ if (pGrid)
+ {
+ pGrid->setDataSource(Reference< XRowSet > ());
+ pGrid->DisposeAccessible();
+ }
+
+ VCLXWindow::dispose();
+}
+
+// XContainer
+
+void FmXGridPeer::addContainerListener(const Reference< XContainerListener >& l)
+{
+ m_aContainerListeners.addInterface( l );
+}
+
+void FmXGridPeer::removeContainerListener(const Reference< XContainerListener >& l)
+{
+ m_aContainerListeners.removeInterface( l );
+}
+
+// css::data::XDatabaseCursorSupplier
+
+void FmXGridPeer::startCursorListening()
+{
+ if (!m_nCursorListening)
+ {
+ if (m_xCursor.is())
+ m_xCursor->addRowSetListener(this);
+
+ Reference< XReset > xReset(m_xCursor, UNO_QUERY);
+ if (xReset.is())
+ xReset->addResetListener(this);
+
+ // register all listeners
+ Reference< XPropertySet > xSet(m_xCursor, UNO_QUERY);
+ if (xSet.is())
+ {
+ xSet->addPropertyChangeListener(FM_PROP_ISMODIFIED, this);
+ xSet->addPropertyChangeListener(FM_PROP_ROWCOUNT, this);
+ }
+ }
+ m_nCursorListening++;
+}
+
+
+void FmXGridPeer::stopCursorListening()
+{
+ if (--m_nCursorListening)
+ return;
+
+ if (m_xCursor.is())
+ m_xCursor->removeRowSetListener(this);
+
+ Reference< XReset > xReset(m_xCursor, UNO_QUERY);
+ if (xReset.is())
+ xReset->removeResetListener(this);
+
+ Reference< XPropertySet > xSet(m_xCursor, UNO_QUERY);
+ if (xSet.is())
+ {
+ xSet->removePropertyChangeListener(FM_PROP_ISMODIFIED, this);
+ xSet->removePropertyChangeListener(FM_PROP_ROWCOUNT, this);
+ }
+}
+
+
+void FmXGridPeer::updateGrid(const Reference< XRowSet >& _rxCursor)
+{
+ VclPtr< FmGridControl > pGrid = GetAs< FmGridControl >();
+ if (pGrid)
+ pGrid->setDataSource(_rxCursor);
+}
+
+
+Reference< XRowSet > FmXGridPeer::getRowSet()
+{
+ return m_xCursor;
+}
+
+
+void FmXGridPeer::setRowSet(const Reference< XRowSet >& _rDatabaseCursor)
+{
+ VclPtr< FmGridControl > pGrid = GetAs< FmGridControl >();
+ if (!pGrid || !m_xColumns.is() || !m_xColumns->getCount())
+ return;
+ // unregister all listeners
+ if (m_xCursor.is())
+ {
+ Reference< XLoadable > xLoadable(m_xCursor, UNO_QUERY);
+ // only if the form is loaded we set the rowset
+ if (xLoadable.is())
+ {
+ stopCursorListening();
+ xLoadable->removeLoadListener(this);
+ }
+ }
+
+ m_xCursor = _rDatabaseCursor;
+
+ if (!pGrid)
+ return;
+
+ Reference< XLoadable > xLoadable(m_xCursor, UNO_QUERY);
+ // only if the form is loaded we set the rowset
+ if (xLoadable.is() && xLoadable->isLoaded())
+ pGrid->setDataSource(m_xCursor);
+ else
+ pGrid->setDataSource(Reference< XRowSet > ());
+
+ if (xLoadable.is())
+ {
+ startCursorListening();
+ xLoadable->addLoadListener(this);
+ }
+}
+
+
+void SAL_CALL FmXGridPeer::addGridControlListener( const Reference< XGridControlListener >& _listener )
+{
+ m_aGridControlListeners.addInterface( _listener );
+}
+
+
+void SAL_CALL FmXGridPeer::removeGridControlListener( const Reference< XGridControlListener >& _listener )
+{
+ m_aGridControlListeners.removeInterface( _listener );
+}
+
+
+sal_Int16 FmXGridPeer::getCurrentColumnPosition()
+{
+ VclPtr< FmGridControl > pGrid = GetAs< FmGridControl >();
+ return pGrid ? pGrid->GetViewColumnPos(pGrid->GetCurColumnId()) : -1;
+}
+
+
+void FmXGridPeer::setCurrentColumnPosition(sal_Int16 nPos)
+{
+ VclPtr< FmGridControl > pGrid = GetAs< FmGridControl >();
+ if (pGrid)
+ pGrid->GoToColumnId(pGrid->GetColumnIdFromViewPos(nPos));
+}
+
+
+void FmXGridPeer::selectionChanged(const EventObject& evt)
+{
+ SolarMutexGuard aGuard;
+
+ VclPtr< FmGridControl > pGrid = GetAs< FmGridControl >();
+ if (!pGrid)
+ return;
+
+ Reference< css::view::XSelectionSupplier > xSelSupplier(evt.Source, UNO_QUERY);
+ Any aSelection = xSelSupplier->getSelection();
+ DBG_ASSERT(aSelection.getValueType().getTypeClass() == TypeClass_INTERFACE, "FmXGridPeer::selectionChanged : invalid selection !");
+ Reference< XPropertySet > xSelection;
+ aSelection >>= xSelection;
+ if (xSelection.is())
+ {
+ Reference< XPropertySet > xCol;
+ sal_Int32 i = 0;
+ sal_Int32 nColCount = m_xColumns->getCount();
+
+ for (; i < nColCount; ++i)
+ {
+ m_xColumns->getByIndex(i) >>= xCol;
+ if ( xCol == xSelection )
+ {
+ pGrid->markColumn(pGrid->GetColumnIdFromModelPos(static_cast<sal_uInt16>(i)));
+ break;
+ }
+ }
+ // The columns have to be 1-based for the VCL control.
+ // If necessary, pass on the selection to the VCL control
+ if ( i != pGrid->GetSelectedColumn() )
+ { // (if this does not take effect, the selectionChanged was implicitly triggered by the control itself)
+ if ( i < nColCount )
+ {
+ pGrid->SelectColumnPos(pGrid->GetViewColumnPos(pGrid->GetColumnIdFromModelPos( static_cast<sal_uInt16>(i) )) + 1);
+ // SelectColumnPos has led to an implicit ActivateCell again
+ if (pGrid->IsEditing())
+ pGrid->DeactivateCell();
+ }
+ else
+ pGrid->SetNoSelection();
+ }
+ }
+ else
+ pGrid->markColumn(USHRT_MAX);
+}
+
+// XElementAccess
+
+sal_Bool FmXGridPeer::hasElements()
+{
+ return getCount() != 0;
+}
+
+
+Type SAL_CALL FmXGridPeer::getElementType( )
+{
+ return cppu::UnoType<css::awt::XControl>::get();
+}
+
+// XEnumerationAccess
+
+Reference< XEnumeration > FmXGridPeer::createEnumeration()
+{
+ return new ::comphelper::OEnumerationByIndex(this);
+}
+
+// XIndexAccess
+
+sal_Int32 FmXGridPeer::getCount()
+{
+ VclPtr< FmGridControl > pGrid = GetAs< FmGridControl >();
+ if (pGrid)
+ return pGrid->GetViewColCount();
+ else
+ return 0;
+}
+
+
+Any FmXGridPeer::getByIndex(sal_Int32 _nIndex)
+{
+ VclPtr< FmGridControl > pGrid = GetAs< FmGridControl >();
+ if (_nIndex < 0 ||
+ _nIndex >= getCount() || !pGrid)
+ throw IndexOutOfBoundsException();
+
+ Any aElement;
+ // get the columnid
+ sal_uInt16 nId = pGrid->GetColumnIdFromViewPos(static_cast<sal_uInt16>(_nIndex));
+ // get the list position
+ sal_uInt16 nPos = pGrid->GetModelColumnPos(nId);
+
+ if ( nPos == GRID_COLUMN_NOT_FOUND )
+ return aElement;
+
+ DbGridColumn* pCol = pGrid->GetColumns()[ nPos ].get();
+ Reference< css::awt::XControl > xControl(pCol->GetCell());
+ aElement <<= xControl;
+
+ return aElement;
+}
+
+// css::util::XModeSelector
+
+void FmXGridPeer::setMode(const OUString& Mode)
+{
+ if (!supportsMode(Mode))
+ throw NoSupportException();
+
+ if (Mode == m_aMode)
+ return;
+
+ m_aMode = Mode;
+
+ VclPtr< FmGridControl > pGrid = GetAs< FmGridControl >();
+ if ( Mode == "FilterMode" )
+ pGrid->SetFilterMode(true);
+ else
+ {
+ pGrid->SetFilterMode(false);
+ pGrid->setDataSource(m_xCursor);
+ }
+}
+
+
+OUString FmXGridPeer::getMode()
+{
+ return m_aMode;
+}
+
+
+css::uno::Sequence<OUString> FmXGridPeer::getSupportedModes()
+{
+ static css::uno::Sequence<OUString> const aModes
+ {
+ "DataMode",
+ "FilterMode"
+ };
+ return aModes;
+}
+
+
+sal_Bool FmXGridPeer::supportsMode(const OUString& Mode)
+{
+ css::uno::Sequence<OUString> aModes(getSupportedModes());
+ return comphelper::findValue(aModes, Mode) != -1;
+}
+
+
+void FmXGridPeer::columnVisible(DbGridColumn const * pColumn)
+{
+ VclPtr< FmGridControl > pGrid = GetAs< FmGridControl >();
+
+ sal_Int32 _nIndex = pGrid->GetModelColumnPos(pColumn->GetId());
+ Reference< css::awt::XControl > xControl(pColumn->GetCell());
+ ContainerEvent aEvt;
+ aEvt.Source = static_cast<XContainer*>(this);
+ aEvt.Accessor <<= _nIndex;
+ aEvt.Element <<= xControl;
+
+ m_aContainerListeners.notifyEach( &XContainerListener::elementInserted, aEvt );
+}
+
+
+void FmXGridPeer::columnHidden(DbGridColumn const * pColumn)
+{
+ VclPtr< FmGridControl > pGrid = GetAs< FmGridControl >();
+
+ sal_Int32 _nIndex = pGrid->GetModelColumnPos(pColumn->GetId());
+ Reference< css::awt::XControl > xControl(pColumn->GetCell());
+ ContainerEvent aEvt;
+ aEvt.Source = static_cast<XContainer*>(this);
+ aEvt.Accessor <<= _nIndex;
+ aEvt.Element <<= xControl;
+
+ m_aContainerListeners.notifyEach( &XContainerListener::elementRemoved, aEvt );
+}
+
+
+void FmXGridPeer::draw( sal_Int32 x, sal_Int32 y )
+{
+ VclPtr< FmGridControl > pGrid = GetAs< FmGridControl >();
+ EditBrowseBoxFlags nOldFlags = pGrid->GetBrowserFlags();
+ pGrid->SetBrowserFlags(nOldFlags | EditBrowseBoxFlags::NO_HANDLE_COLUMN_CONTENT);
+
+ VCLXWindow::draw(x, y);
+
+ pGrid->SetBrowserFlags(nOldFlags);
+}
+
+
+Reference< css::frame::XDispatch > FmXGridPeer::queryDispatch(const css::util::URL& aURL, const OUString& aTargetFrameName, sal_Int32 nSearchFlags)
+{
+ Reference< css::frame::XDispatch > xResult;
+
+ // first ask our interceptor chain
+ if (m_xFirstDispatchInterceptor.is() && !m_bInterceptingDispatch)
+ {
+ m_bInterceptingDispatch = true;
+ // safety against recursion : as we are master of the first chain element and slave of the last one we would
+ // have an infinite loop without this if no dispatcher can fulfill the request
+ xResult = m_xFirstDispatchInterceptor->queryDispatch(aURL, aTargetFrameName, nSearchFlags);
+ m_bInterceptingDispatch = false;
+ }
+
+ // then ask ourself : we don't have any dispatches
+ return xResult;
+}
+
+
+Sequence< Reference< css::frame::XDispatch > > FmXGridPeer::queryDispatches(const Sequence< css::frame::DispatchDescriptor>& aDescripts)
+{
+ if (m_xFirstDispatchInterceptor.is())
+ return m_xFirstDispatchInterceptor->queryDispatches(aDescripts);
+
+ // then ask ourself : we don't have any dispatches
+ return Sequence< Reference< css::frame::XDispatch > >();
+}
+
+
+void FmXGridPeer::registerDispatchProviderInterceptor(const Reference< css::frame::XDispatchProviderInterceptor >& _xInterceptor)
+{
+ if (!_xInterceptor.is())
+ return;
+
+ if (m_xFirstDispatchInterceptor.is())
+ {
+ // there is already an interceptor; the new one will become its master
+ _xInterceptor->setSlaveDispatchProvider(m_xFirstDispatchInterceptor);
+ m_xFirstDispatchInterceptor->setMasterDispatchProvider(m_xFirstDispatchInterceptor);
+ }
+ else
+ {
+ // it is the first interceptor; set ourself as slave
+ _xInterceptor->setSlaveDispatchProvider(static_cast<css::frame::XDispatchProvider*>(this));
+ }
+
+ // we are the master of the chain's first interceptor
+ m_xFirstDispatchInterceptor = _xInterceptor;
+ m_xFirstDispatchInterceptor->setMasterDispatchProvider(static_cast<css::frame::XDispatchProvider*>(this));
+
+ // we have a new interceptor and we're alive ?
+ if (!isDesignMode())
+ // -> check for new dispatchers
+ UpdateDispatches();
+}
+
+
+void FmXGridPeer::releaseDispatchProviderInterceptor(const Reference< css::frame::XDispatchProviderInterceptor >& _xInterceptor)
+{
+ if (!_xInterceptor.is())
+ return;
+
+ Reference< css::frame::XDispatchProviderInterceptor > xChainWalk(m_xFirstDispatchInterceptor);
+
+ if (m_xFirstDispatchInterceptor == _xInterceptor)
+ { // our chain will have a new first element
+ Reference< css::frame::XDispatchProviderInterceptor > xSlave(m_xFirstDispatchInterceptor->getSlaveDispatchProvider(), UNO_QUERY);
+ m_xFirstDispatchInterceptor = xSlave;
+ }
+ // do this before removing the interceptor from the chain as we won't know it's slave afterwards)
+
+ while (xChainWalk.is())
+ {
+ // walk along the chain of interceptors and look for the interceptor that has to be removed
+ Reference< css::frame::XDispatchProviderInterceptor > xSlave(xChainWalk->getSlaveDispatchProvider(), UNO_QUERY);
+
+ if (xChainWalk == _xInterceptor)
+ {
+ // old master may be an interceptor too
+ Reference< css::frame::XDispatchProviderInterceptor > xMaster(xChainWalk->getMasterDispatchProvider(), UNO_QUERY);
+
+ // unchain the interceptor that has to be removed
+ xChainWalk->setSlaveDispatchProvider(Reference< css::frame::XDispatchProvider > ());
+ xChainWalk->setMasterDispatchProvider(Reference< css::frame::XDispatchProvider > ());
+
+ // reconnect the chain
+ if (xMaster.is())
+ {
+ if (xSlave.is())
+ xMaster->setSlaveDispatchProvider(xSlave);
+ else
+ // it's the first interceptor of the chain, set ourself as slave
+ xMaster->setSlaveDispatchProvider(static_cast<css::frame::XDispatchProvider*>(this));
+ }
+ else
+ {
+ // the chain's first element was removed, set ourself as new master of the second one
+ if (xSlave.is())
+ xSlave->setMasterDispatchProvider(static_cast<css::frame::XDispatchProvider*>(this));
+ }
+ }
+
+ xChainWalk = xSlave;
+ }
+ // our interceptor chain has changed and we're alive ?
+ if (!isDesignMode())
+ // -> check the dispatchers
+ UpdateDispatches();
+}
+
+
+void FmXGridPeer::statusChanged(const css::frame::FeatureStateEvent& Event)
+{
+ DBG_ASSERT(m_pStateCache, "FmXGridPeer::statusChanged : invalid call !");
+ DBG_ASSERT(m_pDispatchers, "FmXGridPeer::statusChanged : invalid call !");
+
+ const Sequence< css::util::URL>& aUrls = getSupportedURLs();
+
+ const std::vector<DbGridControlNavigationBarState>& aSlots = getSupportedGridSlots();
+
+ auto pUrl = std::find_if(aUrls.begin(), aUrls.end(),
+ [&Event](const css::util::URL& rUrl) { return rUrl.Main == Event.FeatureURL.Main; });
+ if (pUrl != aUrls.end())
+ {
+ auto i = static_cast<sal_uInt32>(std::distance(aUrls.begin(), pUrl));
+ DBG_ASSERT(m_pDispatchers[i] == Event.Source, "FmXGridPeer::statusChanged : the event source is a little bit suspect !");
+ m_pStateCache[i] = Event.IsEnabled;
+ VclPtr< FmGridControl > pGrid = GetAs< FmGridControl >();
+ if (aSlots[i] != DbGridControlNavigationBarState::Undo)
+ pGrid->GetNavigationBar().InvalidateState(aSlots[i]);
+ }
+ DBG_ASSERT(pUrl != aUrls.end(), "FmXGridPeer::statusChanged : got a call for an unknown url !");
+}
+
+
+sal_Bool FmXGridPeer::approveReset(const EventObject& /*rEvent*/)
+{
+ return true;
+}
+
+
+sal_Bool SAL_CALL FmXGridPeer::select( const Any& _rSelection )
+{
+ Sequence< Any > aBookmarks;
+ if ( !( _rSelection >>= aBookmarks ) )
+ throw IllegalArgumentException();
+
+ return GetAs< FmGridControl >()->selectBookmarks(aBookmarks);
+
+ // TODO:
+ // speaking strictly, we would have to adjust our model, as our ColumnSelection may have changed.
+ // Our model is a XSelectionSupplier, too, it handles the selection of single columns.
+ // This is somewhat strange, as selection should be a view (not a model) aspect.
+ // So for a clean solution, we should handle column selection ourself, and the model shouldn't
+ // deal with selection at all.
+}
+
+
+Any SAL_CALL FmXGridPeer::getSelection( )
+{
+ VclPtr< FmGridControl > pVclControl = GetAs< FmGridControl >();
+ Sequence< Any > aSelectionBookmarks = pVclControl->getSelectionBookmarks();
+ return Any(aSelectionBookmarks);
+}
+
+
+void SAL_CALL FmXGridPeer::addSelectionChangeListener( const Reference< XSelectionChangeListener >& _rxListener )
+{
+ m_aSelectionListeners.addInterface( _rxListener );
+}
+
+
+void SAL_CALL FmXGridPeer::removeSelectionChangeListener( const Reference< XSelectionChangeListener >& _rxListener )
+{
+ m_aSelectionListeners.removeInterface( _rxListener );
+}
+
+
+void FmXGridPeer::resetted(const EventObject& rEvent)
+{
+ if (m_xColumns == rEvent.Source)
+ { // my model was reset -> refresh the grid content
+ SolarMutexGuard aGuard;
+ VclPtr< FmGridControl > pGrid = GetAs< FmGridControl >();
+ if (!pGrid)
+ return;
+ pGrid->resetCurrentRow();
+ }
+ // if the cursor fired a reset event we seem to be on the insert row
+ else if (m_xCursor == rEvent.Source)
+ {
+ SolarMutexGuard aGuard;
+ VclPtr< FmGridControl > pGrid = GetAs< FmGridControl >();
+ if (pGrid && pGrid->IsOpen())
+ pGrid->positioned();
+ }
+}
+
+
+const std::vector<DbGridControlNavigationBarState>& FmXGridPeer::getSupportedGridSlots()
+{
+ static const std::vector<DbGridControlNavigationBarState> aSupported {
+ DbGridControlNavigationBarState::First,
+ DbGridControlNavigationBarState::Prev,
+ DbGridControlNavigationBarState::Next,
+ DbGridControlNavigationBarState::Last,
+ DbGridControlNavigationBarState::New,
+ DbGridControlNavigationBarState::Undo
+ };
+ return aSupported;
+}
+
+
+Sequence< css::util::URL>& FmXGridPeer::getSupportedURLs()
+{
+ static Sequence< css::util::URL> aSupported = []()
+ {
+ static const rtl::OUStringConstExpr sSupported[] = {
+ FMURL_RECORD_MOVEFIRST,
+ FMURL_RECORD_MOVEPREV,
+ FMURL_RECORD_MOVENEXT,
+ FMURL_RECORD_MOVELAST,
+ FMURL_RECORD_MOVETONEW,
+ FMURL_RECORD_UNDO
+ };
+ Sequence< css::util::URL> tmp(SAL_N_ELEMENTS(sSupported));
+ css::util::URL* pSupported = tmp.getArray();
+
+ for ( sal_Int32 i = 0; i < tmp.getLength(); ++i, ++pSupported)
+ pSupported->Complete = sSupported[i];
+
+ // let a css::util::URL-transformer normalize the URLs
+ Reference< css::util::XURLTransformer > xTransformer(
+ util::URLTransformer::create(::comphelper::getProcessComponentContext()) );
+ for (css::util::URL & rURL : asNonConstRange(tmp))
+ xTransformer->parseStrict(rURL);
+ return tmp;
+ }();
+
+ return aSupported;
+}
+
+
+void FmXGridPeer::UpdateDispatches()
+{
+ if (!m_pStateCache)
+ { // we don't have any dispatchers yet -> do the initial connect
+ ConnectToDispatcher();
+ return;
+ }
+
+ sal_uInt16 nDispatchersGot = 0;
+ const Sequence< css::util::URL>& aSupportedURLs = getSupportedURLs();
+ const css::util::URL* pSupportedURLs = aSupportedURLs.getConstArray();
+ Reference< css::frame::XDispatch > xNewDispatch;
+ for (sal_Int32 i=0; i<aSupportedURLs.getLength(); ++i, ++pSupportedURLs)
+ {
+ xNewDispatch = queryDispatch(*pSupportedURLs, OUString(), 0);
+ if (xNewDispatch != m_pDispatchers[i])
+ {
+ if (m_pDispatchers[i].is())
+ m_pDispatchers[i]->removeStatusListener(static_cast<css::frame::XStatusListener*>(this), *pSupportedURLs);
+ m_pDispatchers[i] = xNewDispatch;
+ if (m_pDispatchers[i].is())
+ m_pDispatchers[i]->addStatusListener(static_cast<css::frame::XStatusListener*>(this), *pSupportedURLs);
+ }
+ if (m_pDispatchers[i].is())
+ ++nDispatchersGot;
+ }
+
+ if (!nDispatchersGot)
+ {
+ m_pStateCache.reset();
+ m_pDispatchers.reset();
+ }
+}
+
+
+void FmXGridPeer::ConnectToDispatcher()
+{
+ DBG_ASSERT((m_pStateCache != nullptr) == (m_pDispatchers != nullptr), "FmXGridPeer::ConnectToDispatcher : inconsistent !");
+ if (m_pStateCache)
+ { // already connected -> just do an update
+ UpdateDispatches();
+ return;
+ }
+
+ const Sequence< css::util::URL>& aSupportedURLs = getSupportedURLs();
+
+ // _before_ adding the status listeners (as the add should result in a statusChanged-call) !
+ m_pStateCache.reset(new bool[aSupportedURLs.getLength()]);
+ m_pDispatchers.reset(new Reference< css::frame::XDispatch > [aSupportedURLs.getLength()]);
+
+ sal_uInt16 nDispatchersGot = 0;
+ const css::util::URL* pSupportedURLs = aSupportedURLs.getConstArray();
+ for (sal_Int32 i=0; i<aSupportedURLs.getLength(); ++i, ++pSupportedURLs)
+ {
+ m_pStateCache[i] = false;
+ m_pDispatchers[i] = queryDispatch(*pSupportedURLs, OUString(), 0);
+ if (m_pDispatchers[i].is())
+ {
+ m_pDispatchers[i]->addStatusListener(static_cast<css::frame::XStatusListener*>(this), *pSupportedURLs);
+ ++nDispatchersGot;
+ }
+ }
+
+ if (!nDispatchersGot)
+ {
+ m_pStateCache.reset();
+ m_pDispatchers.reset();
+ }
+}
+
+
+void FmXGridPeer::DisConnectFromDispatcher()
+{
+ if (!m_pStateCache || !m_pDispatchers)
+ return;
+ // we're not connected
+
+ const Sequence< css::util::URL>& aSupportedURLs = getSupportedURLs();
+ const css::util::URL* pSupportedURLs = aSupportedURLs.getConstArray();
+ for (sal_Int32 i=0; i<aSupportedURLs.getLength(); ++i, ++pSupportedURLs)
+ {
+ if (m_pDispatchers[i].is())
+ m_pDispatchers[i]->removeStatusListener(static_cast<css::frame::XStatusListener*>(this), *pSupportedURLs);
+ }
+
+ m_pStateCache.reset();
+ m_pDispatchers.reset();
+}
+
+
+IMPL_LINK(FmXGridPeer, OnQueryGridSlotState, DbGridControlNavigationBarState, nSlot, int)
+{
+ if (!m_pStateCache)
+ return -1; // unspecified
+
+ // search the given slot with our supported sequence
+ const std::vector<DbGridControlNavigationBarState>& aSupported = getSupportedGridSlots();
+ for (size_t i=0; i<aSupported.size(); ++i)
+ {
+ if (aSupported[i] == nSlot)
+ {
+ if (!m_pDispatchers[i].is())
+ return -1; // nothing known about this slot
+ else
+ return m_pStateCache[i] ? 1 : 0;
+ }
+ }
+
+ return -1;
+}
+
+
+IMPL_LINK(FmXGridPeer, OnExecuteGridSlot, DbGridControlNavigationBarState, nSlot, bool)
+{
+ if (!m_pDispatchers)
+ return false; // not handled
+
+ Sequence< css::util::URL>& aUrls = getSupportedURLs();
+ const css::util::URL* pUrls = aUrls.getConstArray();
+
+ const std::vector<DbGridControlNavigationBarState>& aSlots = getSupportedGridSlots();
+
+ DBG_ASSERT(aSlots.size() == o3tl::make_unsigned(aUrls.getLength()), "FmXGridPeer::OnExecuteGridSlot : inconsistent data returned by getSupportedURLs/getSupportedGridSlots!");
+
+ for (size_t i=0; i<aSlots.size(); ++i, ++pUrls)
+ {
+ if (aSlots[i] == nSlot)
+ {
+ if (m_pDispatchers[i].is())
+ {
+ // commit any changes done so far, if it's not the undoRecord URL
+ if ( pUrls->Complete == FMURL_RECORD_UNDO || commit() )
+ m_pDispatchers[i]->dispatch(*pUrls, Sequence< PropertyValue>());
+
+ return true; // handled
+ }
+ }
+ }
+
+ return false; // not handled
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/fmcomp/gridcell.cxx b/svx/source/fmcomp/gridcell.cxx
new file mode 100644
index 000000000..67e5d0040
--- /dev/null
+++ b/svx/source/fmcomp/gridcell.cxx
@@ -0,0 +1,4615 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * 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/log.hxx>
+#include <fmprop.hxx>
+#include <svx/strings.hrc>
+#include <svx/fmtools.hxx>
+#include <gridcell.hxx>
+#include <gridcols.hxx>
+#include <sdbdatacolumn.hxx>
+
+#include <com/sun/star/awt/LineEndFormat.hpp>
+#include <com/sun/star/awt/MouseWheelBehavior.hpp>
+#include <com/sun/star/awt/VisualEffect.hpp>
+#include <com/sun/star/container/XChild.hpp>
+#include <com/sun/star/container/XIndexAccess.hpp>
+#include <com/sun/star/form/FormComponentType.hpp>
+#include <com/sun/star/form/XBoundComponent.hpp>
+#include <com/sun/star/script/XEventAttacherManager.hpp>
+#include <com/sun/star/sdbcx/XTablesSupplier.hpp>
+#include <com/sun/star/sdbcx/XColumnsSupplier.hpp>
+#include <com/sun/star/sdbc/DataType.hpp>
+#include <com/sun/star/sdbc/SQLException.hpp>
+#include <com/sun/star/sdbc/XRowSet.hpp>
+#include <com/sun/star/sdbc/XStatement.hpp>
+#include <com/sun/star/util/XNumberFormatsSupplier.hpp>
+#include <com/sun/star/util/XNumberFormatter.hpp>
+#include <com/sun/star/util/Time.hpp>
+#include <com/sun/star/util/Date.hpp>
+
+#include <comphelper/numbers.hxx>
+#include <comphelper/property.hxx>
+#include <comphelper/servicehelper.hxx>
+#include <comphelper/string.hxx>
+#include <comphelper/types.hxx>
+#include <connectivity/formattedcolumnvalue.hxx>
+#include <i18nlangtag/lang.h>
+#include <o3tl/safeint.hxx>
+#include <svl/numformat.hxx>
+#include <svl/numuno.hxx>
+#include <svx/dialmgr.hxx>
+#include <toolkit/helper/listenermultiplexer.hxx>
+#include <toolkit/helper/vclunohelper.hxx>
+#include <tools/debug.hxx>
+#include <tools/fract.hxx>
+#include <tools/diagnose_ex.h>
+#include <vcl/settings.hxx>
+#include <vcl/svapp.hxx>
+#include <connectivity/dbtools.hxx>
+#include <connectivity/dbconversion.hxx>
+#include <connectivity/sqlnode.hxx>
+
+using namespace ::connectivity;
+using namespace ::svxform;
+using namespace ::comphelper;
+using namespace ::svt;
+using namespace ::com::sun::star;
+using namespace ::com::sun::star::uno;
+using namespace ::com::sun::star::sdbc;
+using namespace ::com::sun::star::sdbcx;
+using namespace ::com::sun::star::sdb;
+using namespace ::com::sun::star::beans;
+using namespace ::com::sun::star::form;
+using namespace ::dbtools::DBTypeConversion;
+using namespace ::dbtools;
+
+using ::com::sun::star::util::XNumberFormatter;
+
+constexpr OUStringLiteral INVALIDTEXT = u"###";
+constexpr OUStringLiteral OBJECTTEXT = u"<OBJECT>";
+
+
+//= helper
+
+namespace
+{
+ LineEnd getModelLineEndSetting( const Reference< XPropertySet >& _rxModel )
+ {
+ LineEnd eFormat = LINEEND_LF;
+
+ try
+ {
+ sal_Int16 nLineEndFormat = awt::LineEndFormat::LINE_FEED;
+
+ Reference< XPropertySetInfo > xPSI;
+ if ( _rxModel.is() )
+ xPSI = _rxModel->getPropertySetInfo();
+
+ OSL_ENSURE( xPSI.is(), "getModelLineEndSetting: invalid column model!" );
+ if ( xPSI.is() && xPSI->hasPropertyByName( FM_PROP_LINEENDFORMAT ) )
+ {
+ OSL_VERIFY( _rxModel->getPropertyValue( FM_PROP_LINEENDFORMAT ) >>= nLineEndFormat );
+
+ switch ( nLineEndFormat )
+ {
+ case awt::LineEndFormat::CARRIAGE_RETURN: eFormat = LINEEND_CR; break;
+ case awt::LineEndFormat::LINE_FEED: eFormat = LINEEND_LF; break;
+ case awt::LineEndFormat::CARRIAGE_RETURN_LINE_FEED: eFormat = LINEEND_CRLF; break;
+ default:
+ OSL_FAIL( "getModelLineEndSetting: what's this?" );
+ }
+ }
+ }
+ catch( const Exception& )
+ {
+ TOOLS_WARN_EXCEPTION( "svx", "getModelLineEndSetting" );
+ }
+ return eFormat;
+ }
+}
+
+
+//= DbGridColumn
+
+
+CellControllerRef DbGridColumn::s_xEmptyController;
+
+
+void DbGridColumn::CreateControl(sal_Int32 _nFieldPos, const Reference< css::beans::XPropertySet >& xField, sal_Int32 nTypeId)
+{
+ Clear();
+
+ m_nTypeId = static_cast<sal_Int16>(nTypeId);
+ if (xField != m_xField)
+ {
+ // initial setting
+ m_xField = xField;
+ xField->getPropertyValue(FM_PROP_FORMATKEY) >>= m_nFormatKey;
+ m_nFieldPos = static_cast<sal_Int16>(_nFieldPos);
+ m_bReadOnly = ::comphelper::getBOOL(xField->getPropertyValue(FM_PROP_ISREADONLY));
+ m_bAutoValue = ::comphelper::getBOOL(xField->getPropertyValue(FM_PROP_AUTOINCREMENT));
+ m_nFieldType = static_cast<sal_Int16>(::comphelper::getINT32(xField->getPropertyValue(FM_PROP_FIELDTYPE)));
+
+ switch (m_nFieldType)
+ {
+ case DataType::DATE:
+ case DataType::TIME:
+ case DataType::TIMESTAMP:
+ 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:
+ m_nAlign = css::awt::TextAlign::RIGHT;
+ m_bNumeric = true;
+ break;
+ default:
+ m_nAlign = css::awt::TextAlign::LEFT;
+ break;
+ }
+ }
+
+ std::unique_ptr<DbCellControl> pCellControl;
+ if (m_rParent.IsFilterMode())
+ {
+ pCellControl.reset(new DbFilterField(m_rParent.getContext(),*this));
+ }
+ else
+ {
+
+ switch (nTypeId)
+ {
+ case TYPE_CHECKBOX: pCellControl.reset(new DbCheckBox(*this)); break;
+ case TYPE_COMBOBOX: pCellControl.reset(new DbComboBox(*this)); break;
+ case TYPE_CURRENCYFIELD: pCellControl.reset(new DbCurrencyField(*this)); break;
+ case TYPE_DATEFIELD: pCellControl.reset(new DbDateField(*this)); break;
+ case TYPE_LISTBOX: pCellControl.reset(new DbListBox(*this)); break;
+ case TYPE_NUMERICFIELD: pCellControl.reset(new DbNumericField(*this)); break;
+ case TYPE_PATTERNFIELD: pCellControl.reset(new DbPatternField( *this, m_rParent.getContext() )); break;
+ case TYPE_TEXTFIELD: pCellControl.reset(new DbTextField(*this)); break;
+ case TYPE_TIMEFIELD: pCellControl.reset(new DbTimeField(*this)); break;
+ case TYPE_FORMATTEDFIELD: pCellControl.reset(new DbFormattedField(*this)); break;
+ default:
+ OSL_FAIL("DbGridColumn::CreateControl: Unknown Column");
+ return;
+ }
+
+ }
+ Reference< XRowSet > xCur;
+ if (m_rParent.getDataSource())
+ xCur.set(Reference< XInterface >(*m_rParent.getDataSource()), UNO_QUERY);
+ // TODO : the cursor wrapper should use an XRowSet interface, too
+
+ pCellControl->Init( m_rParent.GetDataWindow(), xCur );
+
+ // now create the control wrapper
+ auto pTempCellControl = pCellControl.get();
+ if (m_rParent.IsFilterMode())
+ m_pCell = new FmXFilterCell(this, std::unique_ptr<DbFilterField>(static_cast<DbFilterField*>(pCellControl.release())));
+ else
+ {
+ switch (nTypeId)
+ {
+ case TYPE_CHECKBOX: m_pCell = new FmXCheckBoxCell( this, std::move(pCellControl) ); break;
+ case TYPE_LISTBOX: m_pCell = new FmXListBoxCell( this, std::move(pCellControl) ); break;
+ case TYPE_COMBOBOX: m_pCell = new FmXComboBoxCell( this, std::move(pCellControl) ); break;
+ default:
+ m_pCell = new FmXEditCell( this, std::move(pCellControl) );
+ }
+ }
+ m_pCell->init();
+
+ impl_toggleScriptManager_nothrow( true );
+
+ // only if we use have a bound field, we use a controller for displaying the
+ // window in the grid
+ if (m_xField.is())
+ m_xController = pTempCellControl->CreateController();
+}
+
+
+void DbGridColumn::impl_toggleScriptManager_nothrow( bool _bAttach )
+{
+ try
+ {
+ Reference< container::XChild > xChild( m_xModel, UNO_QUERY_THROW );
+ Reference< script::XEventAttacherManager > xManager( xChild->getParent(), UNO_QUERY_THROW );
+ Reference< container::XIndexAccess > xContainer( xChild->getParent(), UNO_QUERY_THROW );
+
+ sal_Int32 nIndexInParent( getElementPos( xContainer, m_xModel ) );
+
+ Reference< XInterface > xCellInterface( *m_pCell, UNO_QUERY );
+ if ( _bAttach )
+ xManager->attach( nIndexInParent, xCellInterface, Any( xCellInterface ) );
+ else
+ xManager->detach( nIndexInParent, xCellInterface );
+ }
+ catch( const Exception& )
+ {
+ DBG_UNHANDLED_EXCEPTION("svx");
+ }
+}
+
+void DbGridColumn::UpdateFromField(const DbGridRow* pRow, const Reference< XNumberFormatter >& xFormatter)
+{
+ if (FmXFilterCell* pCell = dynamic_cast<FmXFilterCell*>(m_pCell.get()))
+ pCell->Update();
+ else if (pRow && pRow->IsValid() && m_nFieldPos >= 0 && m_pCell.is() && pRow->HasField(m_nFieldPos))
+ {
+ dynamic_cast<FmXDataCell&>(*m_pCell).UpdateFromField( pRow->GetField( m_nFieldPos ).getColumn(), xFormatter );
+ }
+}
+
+bool DbGridColumn::Commit()
+{
+ bool bResult = true;
+ if (!m_bInSave && m_pCell.is())
+ {
+ m_bInSave = true;
+ bResult = m_pCell->Commit();
+
+ // store the data into the model
+ FmXDataCell* pDataCell = dynamic_cast<FmXDataCell*>( m_pCell.get() );
+ if (bResult && pDataCell)
+ {
+ Reference< css::form::XBoundComponent > xComp(m_xModel, UNO_QUERY);
+ if (xComp.is())
+ bResult = xComp->commit();
+ }
+ m_bInSave = false;
+ }
+ return bResult;
+}
+
+DbGridColumn::~DbGridColumn()
+{
+ Clear();
+}
+
+void DbGridColumn::setModel(const css::uno::Reference< css::beans::XPropertySet >& _xModel)
+{
+ if ( m_pCell.is() )
+ impl_toggleScriptManager_nothrow( false );
+
+ m_xModel = _xModel;
+
+ if ( m_pCell.is() )
+ impl_toggleScriptManager_nothrow( true );
+}
+
+
+void DbGridColumn::Clear()
+{
+ if ( m_pCell.is() )
+ {
+ impl_toggleScriptManager_nothrow( false );
+
+ m_pCell->dispose();
+ m_pCell.clear();
+ }
+
+ m_xController = nullptr;
+ m_xField = nullptr;
+
+ m_nFormatKey = 0;
+ m_nFieldPos = -1;
+ m_bReadOnly = true;
+ m_bAutoValue = false;
+ m_nFieldType = DataType::OTHER;
+}
+
+
+sal_Int16 DbGridColumn::SetAlignment(sal_Int16 _nAlign)
+{
+ if (_nAlign == -1)
+ { // 'Standard'
+ if (m_xField.is())
+ {
+ sal_Int32 nType = 0;
+ m_xField->getPropertyValue(FM_PROP_FIELDTYPE) >>= nType;
+
+ switch (nType)
+ {
+ case DataType::NUMERIC:
+ case DataType::DECIMAL:
+ case DataType::DOUBLE:
+ case DataType::REAL:
+ case DataType::BIGINT:
+ case DataType::INTEGER:
+ case DataType::SMALLINT:
+ case DataType::TINYINT:
+ case DataType::DATE:
+ case DataType::TIME:
+ case DataType::TIMESTAMP:
+ _nAlign = css::awt::TextAlign::RIGHT;
+ break;
+ case DataType::BIT:
+ case DataType::BOOLEAN:
+ _nAlign = css::awt::TextAlign::CENTER;
+ break;
+ default:
+ _nAlign = css::awt::TextAlign::LEFT;
+ break;
+ }
+ }
+ else
+ _nAlign = css::awt::TextAlign::LEFT;
+ }
+
+ m_nAlign = _nAlign;
+ if (m_pCell.is() && m_pCell->isAlignedController())
+ m_pCell->AlignControl(m_nAlign);
+
+ return m_nAlign;
+}
+
+
+sal_Int16 DbGridColumn::SetAlignmentFromModel(sal_Int16 nStandardAlign)
+{
+ Any aAlign( m_xModel->getPropertyValue(FM_PROP_ALIGN));
+ if (aAlign.hasValue())
+ {
+ sal_Int16 nTest = sal_Int16();
+ if (aAlign >>= nTest)
+ nStandardAlign = nTest;
+ }
+ return SetAlignment(nStandardAlign);
+}
+
+
+void DbGridColumn::setLock(bool _bLock)
+{
+ if (m_bLocked == _bLock)
+ return;
+ m_bLocked = _bLock;
+
+ // is the column we represent active ?
+ if (m_bHidden)
+ return; // no, it isn't (or at least it shouldn't be ...)
+
+ if (m_rParent.GetCurColumnId() == m_nId)
+ {
+ m_rParent.DeactivateCell();
+ m_rParent.ActivateCell(m_rParent.GetCurRow(), m_rParent.GetCurColumnId());
+ }
+}
+
+
+OUString DbGridColumn::GetCellText(const DbGridRow* pRow, const Reference< XNumberFormatter >& xFormatter) const
+{
+ OUString aText;
+ if (m_pCell.is() && dynamic_cast<const FmXFilterCell*>( m_pCell.get() ) != nullptr)
+ return aText;
+
+ if (!pRow || !pRow->IsValid())
+ aText = INVALIDTEXT;
+ else if (pRow->HasField(m_nFieldPos))
+ {
+ aText = GetCellText( pRow->GetField( m_nFieldPos ).getColumn(), xFormatter );
+ }
+ return aText;
+}
+
+OUString DbGridColumn::GetCellText(const Reference< css::sdb::XColumn >& xField, const Reference< XNumberFormatter >& xFormatter) const
+{
+ OUString aText;
+ if (xField.is())
+ {
+ FmXTextCell* pTextCell = dynamic_cast<FmXTextCell*>( m_pCell.get() );
+ if (pTextCell)
+ aText = pTextCell->GetText(xField, xFormatter);
+ else if (m_bObject)
+ aText = OBJECTTEXT;
+ }
+ return aText;
+}
+
+Reference< css::sdb::XColumn > DbGridColumn::GetCurrentFieldValue() const
+{
+ Reference< css::sdb::XColumn > xField;
+ const DbGridRowRef xRow = m_rParent.GetCurrentRow();
+ if (xRow.is() && xRow->HasField(m_nFieldPos))
+ {
+ xField = xRow->GetField(m_nFieldPos).getColumn();
+ }
+ return xField;
+}
+
+
+void DbGridColumn::Paint(OutputDevice& rDev,
+ const tools::Rectangle& rRect,
+ const DbGridRow* pRow,
+ const Reference< XNumberFormatter >& xFormatter)
+{
+ bool bEnabled = ( rDev.GetOutDevType() != OUTDEV_WINDOW )
+ || ( rDev.GetOwnerWindow()->IsEnabled() );
+
+ FmXDataCell* pDataCell = dynamic_cast<FmXDataCell*>( m_pCell.get() );
+ if (pDataCell)
+ {
+ if (!pRow || !pRow->IsValid())
+ {
+ DrawTextFlags nStyle = DrawTextFlags::Clip | DrawTextFlags::Center;
+ if ( !bEnabled )
+ nStyle |= DrawTextFlags::Disable;
+
+ rDev.DrawText(rRect, OUString(INVALIDTEXT), nStyle);
+ }
+ else if (m_bAutoValue && pRow->IsNew())
+ {
+ DrawTextFlags nStyle = DrawTextFlags::Clip | DrawTextFlags::VCenter;
+ if ( !bEnabled )
+ nStyle |= DrawTextFlags::Disable;
+
+ switch (GetAlignment())
+ {
+ case css::awt::TextAlign::RIGHT:
+ nStyle |= DrawTextFlags::Right;
+ break;
+ case css::awt::TextAlign::CENTER:
+ nStyle |= DrawTextFlags::Center;
+ break;
+ default:
+ nStyle |= DrawTextFlags::Left;
+ }
+
+ rDev.DrawText(rRect, SvxResId(RID_STR_AUTOFIELD), nStyle);
+ }
+ else if (pRow->HasField(m_nFieldPos))
+ {
+ pDataCell->PaintFieldToCell(rDev, rRect, pRow->GetField( m_nFieldPos ).getColumn(), xFormatter);
+ }
+ }
+ else if (!m_pCell.is())
+ {
+ if (!pRow || !pRow->IsValid())
+ {
+ DrawTextFlags nStyle = DrawTextFlags::Clip | DrawTextFlags::Center;
+ if ( !bEnabled )
+ nStyle |= DrawTextFlags::Disable;
+
+ rDev.DrawText(rRect, OUString(INVALIDTEXT), nStyle);
+ }
+ else if (pRow->HasField(m_nFieldPos) && m_bObject)
+ {
+ DrawTextFlags nStyle = DrawTextFlags::Clip | DrawTextFlags::Center;
+ if ( !bEnabled )
+ nStyle |= DrawTextFlags::Disable;
+ rDev.DrawText(rRect, OUString(OBJECTTEXT), nStyle);
+ }
+ }
+ else if ( auto pFilterCell = dynamic_cast<FmXFilterCell*>( m_pCell.get() ) )
+ pFilterCell->PaintCell( rDev, rRect );
+}
+
+
+void DbGridColumn::ImplInitWindow( vcl::Window const & rParent, const InitWindowFacet _eInitWhat )
+{
+ if ( m_pCell.is() )
+ m_pCell->ImplInitWindow( rParent, _eInitWhat );
+}
+
+
+//= cell controls
+
+
+DbCellControl::DbCellControl( DbGridColumn& _rColumn )
+ :OPropertyChangeListener(m_aMutex)
+ ,m_bTransparent( false )
+ ,m_bAlignedController( true )
+ ,m_bAccessingValueProperty( false )
+ ,m_rColumn( _rColumn )
+ ,m_pPainter( nullptr )
+ ,m_pWindow( nullptr )
+{
+ Reference< XPropertySet > xColModelProps = _rColumn.getModel();
+ if ( !xColModelProps.is() )
+ return;
+
+ // if our model's format key changes we want to propagate the new value to our windows
+ m_pModelChangeBroadcaster = new ::comphelper::OPropertyChangeMultiplexer(this, _rColumn.getModel());
+
+ // be listener for some common properties
+ implDoPropertyListening( FM_PROP_READONLY, false );
+ implDoPropertyListening( FM_PROP_ENABLED, false );
+
+ // add as listener for all known "value" properties
+ implDoPropertyListening( FM_PROP_VALUE, false );
+ implDoPropertyListening( FM_PROP_STATE, false );
+ implDoPropertyListening( FM_PROP_TEXT, false );
+ implDoPropertyListening( FM_PROP_EFFECTIVE_VALUE, false );
+ implDoPropertyListening( FM_PROP_SELECT_SEQ, false );
+ implDoPropertyListening( FM_PROP_DATE, false );
+ implDoPropertyListening( FM_PROP_TIME, false );
+
+ // be listener at the bound field as well
+ try
+ {
+ Reference< XPropertySetInfo > xPSI( xColModelProps->getPropertySetInfo(), UNO_SET_THROW );
+ if ( xPSI->hasPropertyByName( FM_PROP_BOUNDFIELD ) )
+ {
+ Reference< XPropertySet > xField;
+ xColModelProps->getPropertyValue( FM_PROP_BOUNDFIELD ) >>= xField;
+ if ( xField.is() )
+ {
+ m_pFieldChangeBroadcaster = new ::comphelper::OPropertyChangeMultiplexer(this, xField);
+ m_pFieldChangeBroadcaster->addProperty( FM_PROP_ISREADONLY );
+ }
+ }
+ }
+ catch( const Exception& )
+ {
+ TOOLS_WARN_EXCEPTION( "svx", "DbCellControl::doPropertyListening" );
+ }
+}
+
+
+void DbCellControl::implDoPropertyListening(const OUString& _rPropertyName, bool _bWarnIfNotExistent)
+{
+ try
+ {
+ Reference< XPropertySet > xColModelProps = m_rColumn.getModel();
+ Reference< XPropertySetInfo > xPSI;
+ if ( xColModelProps.is() )
+ xPSI = xColModelProps->getPropertySetInfo();
+
+ DBG_ASSERT( !_bWarnIfNotExistent || ( xPSI.is() && xPSI->hasPropertyByName( _rPropertyName ) ),
+ "DbCellControl::doPropertyListening: no property set info or non-existent property!" );
+
+ if ( xPSI.is() && xPSI->hasPropertyByName( _rPropertyName ) )
+ m_pModelChangeBroadcaster->addProperty( _rPropertyName );
+ }
+ catch( const Exception& )
+ {
+ TOOLS_WARN_EXCEPTION( "svx", "DbCellControl::doPropertyListening" );
+ }
+}
+
+
+void DbCellControl::doPropertyListening(const OUString& _rPropertyName)
+{
+ implDoPropertyListening( _rPropertyName, true );
+}
+
+static void lcl_clearBroadCaster(rtl::Reference<::comphelper::OPropertyChangeMultiplexer>& _pBroadcaster)
+{
+ if ( _pBroadcaster.is() )
+ {
+ _pBroadcaster->dispose();
+ _pBroadcaster.clear();
+ // no delete, this is done implicitly
+ }
+}
+
+DbCellControl::~DbCellControl()
+{
+ lcl_clearBroadCaster(m_pModelChangeBroadcaster);
+ lcl_clearBroadCaster(m_pFieldChangeBroadcaster);
+
+ m_pWindow.disposeAndClear();
+ m_pPainter.disposeAndClear();
+}
+
+void DbCellControl::implValuePropertyChanged( )
+{
+ OSL_ENSURE( !isValuePropertyLocked(),
+ "DbCellControl::implValuePropertyChanged: not to be called with the value property locked!" );
+
+ if ( m_pWindow )
+ {
+ if ( m_rColumn.getModel().is() )
+ updateFromModel( m_rColumn.getModel() );
+ }
+}
+
+
+void DbCellControl::implAdjustGenericFieldSetting( const Reference< XPropertySet >& /*_rxModel*/ )
+{
+ // nothing to do here
+}
+
+
+void DbCellControl::_propertyChanged(const PropertyChangeEvent& _rEvent)
+{
+ SolarMutexGuard aGuard;
+
+ Reference< XPropertySet > xSourceProps( _rEvent.Source, UNO_QUERY );
+
+ if ( _rEvent.PropertyName == FM_PROP_VALUE
+ || _rEvent.PropertyName == FM_PROP_STATE
+ || _rEvent.PropertyName == FM_PROP_TEXT
+ || _rEvent.PropertyName == FM_PROP_EFFECTIVE_VALUE
+ || _rEvent.PropertyName == FM_PROP_SELECT_SEQ
+ || _rEvent.PropertyName == FM_PROP_DATE
+ || _rEvent.PropertyName == FM_PROP_TIME
+ )
+ { // it was one of the known "value" properties
+ if ( !isValuePropertyLocked() )
+ {
+ implValuePropertyChanged( );
+ }
+ }
+ else if ( _rEvent.PropertyName == FM_PROP_READONLY )
+ {
+ implAdjustReadOnly( xSourceProps, true);
+ }
+ else if ( _rEvent.PropertyName == FM_PROP_ISREADONLY )
+ {
+ bool bReadOnly = true;
+ _rEvent.NewValue >>= bReadOnly;
+ m_rColumn.SetReadOnly(bReadOnly);
+ implAdjustReadOnly( xSourceProps, false);
+ }
+ else if ( _rEvent.PropertyName == FM_PROP_ENABLED )
+ {
+ implAdjustEnabled( xSourceProps );
+ }
+ else
+ implAdjustGenericFieldSetting( xSourceProps );
+}
+
+bool DbCellControl::Commit()
+{
+ // lock the listening for value property changes
+ lockValueProperty();
+ // commit the content of the control into the model's value property
+ bool bReturn = false;
+ try
+ {
+ bReturn = commitControl();
+ }
+ catch( const Exception& )
+ {
+ DBG_UNHANDLED_EXCEPTION("svx");
+ }
+ // unlock the listening for value property changes
+ unlockValueProperty();
+ // outta here
+ return bReturn;
+}
+
+void DbCellControl::ImplInitWindow( vcl::Window const & rParent, const InitWindowFacet _eInitWhat )
+{
+ svt::ControlBase* pWindows[] = { m_pPainter, m_pWindow };
+
+ if (_eInitWhat & InitWindowFacet::WritingMode)
+ {
+ for (svt::ControlBase* pWindow : pWindows)
+ {
+ if (pWindow)
+ pWindow->EnableRTL(rParent.IsRTLEnabled());
+ }
+ }
+
+ if (_eInitWhat & InitWindowFacet::Font)
+ {
+ const StyleSettings& rStyleSettings = Application::GetSettings().GetStyleSettings();
+ const Fraction& rZoom = rParent.GetZoom();
+
+ for (svt::ControlBase* pWindow : pWindows)
+ {
+ if (!pWindow)
+ continue;
+
+ vcl::Font aFont = rStyleSettings.GetFieldFont();
+ aFont.SetTransparent(isTransparent());
+
+ if (rParent.IsControlFont())
+ aFont.Merge(rParent.GetControlFont());
+
+ if (rZoom.GetNumerator() != rZoom.GetDenominator())
+ {
+ Size aSize = aFont.GetFontSize();
+ aSize.setWidth(std::round(double(aSize.Width() * rZoom)));
+ aSize.setHeight(std::round(double(aSize.Height() * rZoom)));
+ aFont.SetFontSize(aSize);
+ }
+
+ pWindow->SetPointFont(aFont);
+ }
+ }
+
+ if ((_eInitWhat & InitWindowFacet::Font) || (_eInitWhat & InitWindowFacet::Foreground))
+ {
+ Color aTextColor(rParent.IsControlForeground() ? rParent.GetControlForeground() : rParent.GetTextColor());
+
+ bool bTextLineColor = rParent.IsTextLineColor();
+ Color aTextLineColor(rParent.GetTextLineColor());
+
+ for (svt::ControlBase* pWindow : pWindows)
+ {
+ if (pWindow)
+ {
+ pWindow->SetTextColor(aTextColor);
+ if (rParent.IsControlForeground())
+ pWindow->SetControlForeground(aTextColor);
+
+ if (bTextLineColor)
+ pWindow->SetTextLineColor();
+ else
+ pWindow->SetTextLineColor(aTextLineColor);
+ }
+ }
+ }
+
+ if (!(_eInitWhat & InitWindowFacet::Background))
+ return;
+
+ if (rParent.IsControlBackground())
+ {
+ Color aColor(rParent.GetControlBackground());
+ for (svt::ControlBase* pWindow : pWindows)
+ {
+ if (pWindow)
+ {
+ if (isTransparent())
+ pWindow->SetBackground();
+ else
+ {
+ pWindow->SetBackground(aColor);
+ pWindow->SetControlBackground(aColor);
+ }
+ pWindow->GetOutDev()->SetFillColor(aColor);
+ }
+ }
+ }
+ else
+ {
+ if (m_pPainter)
+ {
+ if (isTransparent())
+ m_pPainter->SetBackground();
+ else
+ m_pPainter->SetBackground(rParent.GetBackground());
+ m_pPainter->GetOutDev()->SetFillColor(rParent.GetOutDev()->GetFillColor());
+ }
+
+ if (m_pWindow)
+ {
+ if (isTransparent())
+ m_pWindow->SetBackground(rParent.GetBackground());
+ else
+ m_pWindow->GetOutDev()->SetFillColor(rParent.GetOutDev()->GetFillColor());
+ }
+ }
+}
+
+void DbCellControl::implAdjustReadOnly( const Reference< XPropertySet >& _rxModel,bool i_bReadOnly )
+{
+ DBG_ASSERT( m_pWindow, "DbCellControl::implAdjustReadOnly: not to be called without window!" );
+ DBG_ASSERT( _rxModel.is(), "DbCellControl::implAdjustReadOnly: invalid model!" );
+ if ( !(m_pWindow && _rxModel.is()) )
+ return;
+
+ bool bReadOnly = m_rColumn.IsReadOnly();
+ if ( !bReadOnly )
+ {
+ _rxModel->getPropertyValue( i_bReadOnly ? OUString(FM_PROP_READONLY) : OUString(FM_PROP_ISREADONLY)) >>= bReadOnly;
+ }
+ m_pWindow->SetEditableReadOnly(bReadOnly);
+}
+
+void DbCellControl::implAdjustEnabled( const Reference< XPropertySet >& _rxModel )
+{
+ DBG_ASSERT( m_pWindow, "DbCellControl::implAdjustEnabled: not to be called without window!" );
+ DBG_ASSERT( _rxModel.is(), "DbCellControl::implAdjustEnabled: invalid model!" );
+ if ( m_pWindow && _rxModel.is() )
+ {
+ bool bEnable = true;
+ _rxModel->getPropertyValue( FM_PROP_ENABLED ) >>= bEnable;
+ m_pWindow->Enable( bEnable );
+ }
+}
+
+void DbCellControl::Init(BrowserDataWin& rParent, const Reference< XRowSet >& _rxCursor)
+{
+ ImplInitWindow( rParent, InitWindowFacet::All );
+
+ if ( m_pWindow )
+ {
+ // align the control
+ if ( isAlignedController() )
+ AlignControl( m_rColumn.GetAlignment() );
+
+ try
+ {
+ // some other common properties
+ Reference< XPropertySet > xModel( m_rColumn.getModel(), UNO_SET_THROW );
+ Reference< XPropertySetInfo > xModelPSI( xModel->getPropertySetInfo(), UNO_SET_THROW );
+
+ if ( xModelPSI->hasPropertyByName( FM_PROP_READONLY ) )
+ {
+ implAdjustReadOnly( xModel,true );
+ }
+
+ if ( xModelPSI->hasPropertyByName( FM_PROP_ENABLED ) )
+ {
+ implAdjustEnabled( xModel );
+ }
+
+ if ( xModelPSI->hasPropertyByName( FM_PROP_MOUSE_WHEEL_BEHAVIOR ) )
+ {
+ sal_Int16 nWheelBehavior = css::awt::MouseWheelBehavior::SCROLL_FOCUS_ONLY;
+ OSL_VERIFY( xModel->getPropertyValue( FM_PROP_MOUSE_WHEEL_BEHAVIOR ) >>= nWheelBehavior );
+ MouseWheelBehaviour nVclSetting = MouseWheelBehaviour::FocusOnly;
+ switch ( nWheelBehavior )
+ {
+ case css::awt::MouseWheelBehavior::SCROLL_DISABLED: nVclSetting = MouseWheelBehaviour::Disable; break;
+ case css::awt::MouseWheelBehavior::SCROLL_FOCUS_ONLY: nVclSetting = MouseWheelBehaviour::FocusOnly; break;
+ case css::awt::MouseWheelBehavior::SCROLL_ALWAYS: nVclSetting = MouseWheelBehaviour::ALWAYS; break;
+ default:
+ OSL_FAIL( "DbCellControl::Init: invalid MouseWheelBehavior!" );
+ break;
+ }
+
+ AllSettings aSettings = m_pWindow->GetSettings();
+ MouseSettings aMouseSettings = aSettings.GetMouseSettings();
+ aMouseSettings.SetWheelBehavior( nVclSetting );
+ aSettings.SetMouseSettings( aMouseSettings );
+ m_pWindow->SetSettings( aSettings, true );
+ }
+ }
+ catch( const Exception& )
+ {
+ DBG_UNHANDLED_EXCEPTION("svx");
+ }
+ }
+ m_xCursor = _rxCursor;
+ if ( m_rColumn.getModel().is() )
+ updateFromModel( m_rColumn.getModel() );
+}
+
+
+void DbCellControl::SetTextLineColor()
+{
+ if (m_pWindow)
+ m_pWindow->SetTextLineColor();
+ if (m_pPainter)
+ m_pPainter->SetTextLineColor();
+}
+
+
+void DbCellControl::SetTextLineColor(const Color& _rColor)
+{
+ if (m_pWindow)
+ m_pWindow->SetTextLineColor(_rColor);
+ if (m_pPainter)
+ m_pPainter->SetTextLineColor(_rColor);
+}
+
+namespace
+{
+ void lcl_implAlign( vcl::Window* _pWindow, WinBits _nAlignmentBit )
+ {
+ if (EditControlBase* pControl = dynamic_cast<EditControlBase*>(_pWindow))
+ {
+ switch (_nAlignmentBit)
+ {
+ case WB_LEFT:
+ pControl->get_widget().set_alignment(TxtAlign::Left);
+ break;
+ case WB_CENTER:
+ pControl->get_widget().set_alignment(TxtAlign::Center);
+ break;
+ case WB_RIGHT:
+ pControl->get_widget().set_alignment(TxtAlign::Right);
+ break;
+ }
+ return;
+ }
+
+ WinBits nStyle = _pWindow->GetStyle();
+ nStyle &= ~(WB_LEFT | WB_RIGHT | WB_CENTER);
+ _pWindow->SetStyle( nStyle | _nAlignmentBit );
+ }
+}
+
+void DbCellControl::AlignControl(sal_Int16 nAlignment)
+{
+ WinBits nAlignmentBit = 0;
+ switch (nAlignment)
+ {
+ case css::awt::TextAlign::RIGHT:
+ nAlignmentBit = WB_RIGHT;
+ break;
+ case css::awt::TextAlign::CENTER:
+ nAlignmentBit = WB_CENTER;
+ break;
+ default:
+ nAlignmentBit = WB_LEFT;
+ break;
+ }
+ lcl_implAlign( m_pWindow, nAlignmentBit );
+ if ( m_pPainter )
+ lcl_implAlign( m_pPainter, nAlignmentBit );
+}
+
+void DbCellControl::PaintCell(OutputDevice& rDev, const tools::Rectangle& rRect)
+{
+ m_pPainter->SetSizePixel(rRect.GetSize());
+ m_pPainter->Draw(&rDev, rRect.TopLeft(), SystemTextColorFlags::NONE);
+}
+
+void DbCellControl::PaintFieldToCell( OutputDevice& _rDev, const tools::Rectangle& _rRect, const Reference< XColumn >& _rxField, const Reference< XNumberFormatter >& _rxFormatter )
+{
+ m_pPainter->SetText( GetFormatText( _rxField, _rxFormatter ) );
+ PaintCell( _rDev, _rRect );
+}
+
+double DbCellControl::GetValue(const Reference< css::sdb::XColumn >& _rxField, const Reference< XNumberFormatter >& xFormatter) const
+{
+ double fValue = 0;
+ if (m_rColumn.IsNumeric())
+ {
+ try
+ {
+ fValue = _rxField->getDouble();
+ }
+ catch(const Exception&) { }
+ }
+ else
+ {
+ bool bSuccess = false;
+ try
+ {
+ fValue = _rxField->getDouble();
+ bSuccess = true;
+ }
+ catch(const Exception&) { }
+ if (!bSuccess)
+ {
+ try
+ {
+ fValue = xFormatter->convertStringToNumber(m_rColumn.GetKey(), _rxField->getString());
+ }
+ catch(const Exception&) { }
+ }
+ }
+ return fValue;
+}
+
+void DbCellControl::invalidatedController()
+{
+ m_rColumn.GetParent().refreshController(m_rColumn.GetId(), DbGridControl::GrantControlAccess());
+}
+
+// CellModels
+
+DbLimitedLengthField::DbLimitedLengthField( DbGridColumn& _rColumn )
+ :DbCellControl( _rColumn )
+{
+ doPropertyListening( FM_PROP_MAXTEXTLEN );
+}
+
+
+void DbLimitedLengthField::implAdjustGenericFieldSetting( const Reference< XPropertySet >& _rxModel )
+{
+ DBG_ASSERT( m_pWindow, "DbLimitedLengthField::implAdjustGenericFieldSetting: not to be called without window!" );
+ DBG_ASSERT( _rxModel.is(), "DbLimitedLengthField::implAdjustGenericFieldSetting: invalid model!" );
+ if ( m_pWindow && _rxModel.is() )
+ {
+ sal_Int16 nMaxLen = 0;
+ _rxModel->getPropertyValue( FM_PROP_MAXTEXTLEN ) >>= nMaxLen;
+ implSetMaxTextLen( nMaxLen );
+ }
+}
+
+void DbLimitedLengthField::implSetEffectiveMaxTextLen(sal_Int32 nMaxLen)
+{
+ dynamic_cast<EditControlBase&>(*m_pWindow).get_widget().set_max_length(nMaxLen);
+ if (m_pPainter)
+ dynamic_cast<EditControlBase&>(*m_pPainter).get_widget().set_max_length(nMaxLen);
+}
+
+DbTextField::DbTextField(DbGridColumn& _rColumn)
+ :DbLimitedLengthField(_rColumn)
+ ,m_bIsMultiLineEdit(false)
+{
+}
+
+DbTextField::~DbTextField( )
+{
+ m_pPainterImplementation.reset();
+ m_pEdit.reset();
+}
+
+void DbTextField::Init(BrowserDataWin& rParent, const Reference< XRowSet >& xCursor)
+{
+ sal_Int16 nAlignment = m_rColumn.SetAlignmentFromModel(-1);
+
+ Reference< XPropertySet > xModel( m_rColumn.getModel() );
+
+ bool bLeftAlign = true;
+
+ // is this a multi-line field?
+ bool bIsMultiLine = false;
+ try
+ {
+ if ( xModel.is() )
+ {
+ OSL_VERIFY( xModel->getPropertyValue( FM_PROP_MULTILINE ) >>= bIsMultiLine );
+ }
+ }
+ catch( const Exception& )
+ {
+ TOOLS_WARN_EXCEPTION("svx",
+ "caught an exception while determining the multi-line capabilities!");
+ }
+
+ m_bIsMultiLineEdit = bIsMultiLine;
+ if ( bIsMultiLine )
+ {
+ auto xEditControl = VclPtr<MultiLineTextCell>::Create(&rParent);
+ auto xEditPainter = VclPtr<MultiLineTextCell>::Create(&rParent);
+
+ switch (nAlignment)
+ {
+ case awt::TextAlign::RIGHT:
+ xEditControl->get_widget().set_alignment(TxtAlign::Right);
+ xEditPainter->get_widget().set_alignment(TxtAlign::Right);
+ bLeftAlign = false;
+ break;
+ case awt::TextAlign::CENTER:
+ xEditControl->get_widget().set_alignment(TxtAlign::Center);
+ xEditPainter->get_widget().set_alignment(TxtAlign::Center);
+ bLeftAlign = false;
+ break;
+ }
+
+ m_pWindow = xEditControl;
+ m_pEdit.reset(new MultiLineEditImplementation(*xEditControl));
+
+ m_pPainter = xEditPainter;
+ m_pPainterImplementation.reset(new MultiLineEditImplementation(*xEditPainter));
+ }
+ else
+ {
+ auto xEditControl = VclPtr<EditControl>::Create(&rParent);
+ auto xEditPainter = VclPtr<EditControl>::Create(&rParent);
+
+ switch (nAlignment)
+ {
+ case awt::TextAlign::RIGHT:
+ xEditControl->get_widget().set_alignment(TxtAlign::Right);
+ xEditPainter->get_widget().set_alignment(TxtAlign::Right);
+ bLeftAlign = false;
+ break;
+ case awt::TextAlign::CENTER:
+ xEditControl->get_widget().set_alignment(TxtAlign::Center);
+ xEditPainter->get_widget().set_alignment(TxtAlign::Center);
+ bLeftAlign = false;
+ break;
+ }
+
+ m_pWindow = xEditControl;
+ m_pEdit.reset(new EntryImplementation(*xEditControl));
+
+ m_pPainter = xEditPainter;
+ m_pPainterImplementation.reset(new EntryImplementation(*xEditPainter));
+ }
+
+ if (bLeftAlign)
+ {
+ // this is so that when getting the focus, the selection is oriented left-to-right
+ AllSettings aSettings = m_pWindow->GetSettings();
+ StyleSettings aStyleSettings = aSettings.GetStyleSettings();
+ aStyleSettings.SetSelectionOptions(
+ aStyleSettings.GetSelectionOptions() | SelectionOptions::ShowFirst);
+ aSettings.SetStyleSettings(aStyleSettings);
+ m_pWindow->SetSettings(aSettings);
+ }
+
+ implAdjustGenericFieldSetting( xModel );
+
+ DbLimitedLengthField::Init( rParent, xCursor );
+}
+
+CellControllerRef DbTextField::CreateController() const
+{
+ return new EditCellController( m_pEdit.get() );
+}
+
+void DbTextField::PaintFieldToCell( OutputDevice& _rDev, const tools::Rectangle& _rRect, const Reference< XColumn >& _rxField, const Reference< XNumberFormatter >& _rxFormatter )
+{
+ if ( m_pPainterImplementation )
+ m_pPainterImplementation->SetText( GetFormatText( _rxField, _rxFormatter ) );
+
+ DbLimitedLengthField::PaintFieldToCell( _rDev, _rRect, _rxField, _rxFormatter );
+}
+
+OUString DbTextField::GetFormatText(const Reference< XColumn >& _rxField, const Reference< XNumberFormatter >& xFormatter, const Color** /*ppColor*/)
+{
+ if (!_rxField.is())
+ return OUString();
+
+ const css::uno::Reference<css::beans::XPropertySet> xPS(_rxField, UNO_QUERY);
+ FormattedColumnValue fmter( xFormatter, xPS );
+
+ try
+ {
+ return fmter.getFormattedValue();
+ }
+ catch( const Exception& )
+ {
+ DBG_UNHANDLED_EXCEPTION("svx");
+ }
+ return OUString();
+
+}
+
+void DbTextField::UpdateFromField(const Reference< css::sdb::XColumn >& _rxField, const Reference< XNumberFormatter >& xFormatter)
+{
+ m_pEdit->SetText( GetFormatText( _rxField, xFormatter ) );
+ m_pEdit->SetSelection( Selection( SELECTION_MAX, SELECTION_MIN ) );
+}
+
+void DbTextField::updateFromModel( Reference< XPropertySet > _rxModel )
+{
+ OSL_ENSURE( _rxModel.is() && m_pWindow, "DbTextField::updateFromModel: invalid call!" );
+
+ OUString sText;
+ _rxModel->getPropertyValue( FM_PROP_TEXT ) >>= sText;
+
+ sal_Int32 nMaxTextLen = m_pEdit->GetMaxTextLen();
+ if (nMaxTextLen > 0 && sText.getLength() > nMaxTextLen)
+ {
+ sal_Int32 nDiff = sText.getLength() - nMaxTextLen;
+ sText = sText.replaceAt(sText.getLength() - nDiff,nDiff, u"");
+ }
+
+ m_pEdit->SetText( sText );
+ m_pEdit->SetSelection( Selection( SELECTION_MAX, SELECTION_MIN ) );
+}
+
+bool DbTextField::commitControl()
+{
+ OUString aText( m_pEdit->GetText( getModelLineEndSetting( m_rColumn.getModel() ) ) );
+ // we have to check if the length before we can decide if the value was modified
+ sal_Int32 nMaxTextLen = m_pEdit->GetMaxTextLen();
+ if (nMaxTextLen > 0)
+ {
+ OUString sOldValue;
+ m_rColumn.getModel()->getPropertyValue( FM_PROP_TEXT ) >>= sOldValue;
+ // if the new value didn't change we must set the old long value again
+ if ( sOldValue.getLength() > nMaxTextLen && sOldValue.compareTo(aText,nMaxTextLen) == 0 )
+ aText = sOldValue;
+ }
+ m_rColumn.getModel()->setPropertyValue( FM_PROP_TEXT, Any( aText ) );
+ return true;
+}
+
+void DbTextField::implSetEffectiveMaxTextLen( sal_Int32 _nMaxLen )
+{
+ if ( m_pEdit )
+ m_pEdit->SetMaxTextLen( _nMaxLen );
+ if ( m_pPainterImplementation )
+ m_pPainterImplementation->SetMaxTextLen( _nMaxLen );
+}
+
+DbFormattedField::DbFormattedField(DbGridColumn& _rColumn)
+ :DbLimitedLengthField(_rColumn)
+{
+ // if our model's format key changes we want to propagate the new value to our windows
+ doPropertyListening( FM_PROP_FORMATKEY );
+}
+
+DbFormattedField::~DbFormattedField()
+{
+}
+
+void DbFormattedField::Init( BrowserDataWin& rParent, const Reference< XRowSet >& xCursor)
+{
+ sal_Int16 nAlignment = m_rColumn.SetAlignmentFromModel(-1);
+
+ Reference< css::beans::XPropertySet > xUnoModel = m_rColumn.getModel();
+
+ auto xEditControl = VclPtr<FormattedControl>::Create(&rParent, false);
+ auto xEditPainter = VclPtr<FormattedControl>::Create(&rParent, false);
+
+ weld::EntryFormatter& rControlFormatter = xEditControl->get_formatter();
+ weld::EntryFormatter& rPainterFormatter = xEditPainter->get_formatter();
+
+ m_pWindow = xEditControl.get();
+ m_pPainter = xEditPainter.get();
+
+ switch (nAlignment)
+ {
+ case awt::TextAlign::RIGHT:
+ xEditControl->get_widget().set_alignment(TxtAlign::Right);
+ xEditPainter->get_widget().set_alignment(TxtAlign::Right);
+ break;
+ case awt::TextAlign::CENTER:
+ xEditControl->get_widget().set_alignment(TxtAlign::Center);
+ xEditPainter->get_widget().set_alignment(TxtAlign::Center);
+ break;
+ default:
+ {
+ // Everything just so that the selection goes from right to left when getting focus
+ SelectionOptions eOptions = rControlFormatter.GetEntrySelectionOptions();
+ rControlFormatter.SetEntrySelectionOptions(eOptions | SelectionOptions::ShowFirst);
+ break;
+ }
+ }
+
+ implAdjustGenericFieldSetting( xUnoModel );
+
+ rControlFormatter.SetStrictFormat(false);
+ rPainterFormatter.SetStrictFormat(false);
+ // if one allows any formatting, one cannot make an entry check anyway
+ // (the FormattedField does not support that anyway, only derived classes)
+
+ // get the formatter from the uno model
+ // (I could theoretically also go via the css::util::NumberFormatter, which the cursor would
+ // surely give me. The problem is that I can not really rely on the fact that the two
+ // formatters are the same. Clean is the whole thing if I go via the UNO model.)
+ sal_Int32 nFormatKey = -1;
+
+ // let's see if the model has one ...
+ DBG_ASSERT(::comphelper::hasProperty(FM_PROP_FORMATSSUPPLIER, xUnoModel), "DbFormattedField::Init : invalid UNO model !");
+ Any aSupplier( xUnoModel->getPropertyValue(FM_PROP_FORMATSSUPPLIER));
+ if (aSupplier.hasValue())
+ {
+ m_xSupplier.set(aSupplier, css::uno::UNO_QUERY);
+ if (m_xSupplier.is())
+ {
+ // if we take the supplier from the model, then also the key
+ Any aFmtKey( xUnoModel->getPropertyValue(FM_PROP_FORMATKEY));
+ if (aFmtKey.hasValue())
+ {
+ DBG_ASSERT(aFmtKey.getValueType().getTypeClass() == TypeClass_LONG, "DbFormattedField::Init : invalid format key property (no sal_Int32) !");
+ nFormatKey = ::comphelper::getINT32(aFmtKey);
+ }
+ else
+ {
+ SAL_INFO("svx.fmcomp", "DbFormattedField::Init : my uno-model has no format-key, but a formats supplier !");
+ // the OFormattedModel which we usually are working with ensures that the model has a format key
+ // as soon as the form is loaded. Unfortunally this method here is called from within loaded, too.
+ // So if our LoadListener is called before the LoadListener of the model, this "else case" is
+ // allowed.
+ // Of course our property listener for the FormatKey property will notify us if the prop is changed,
+ // so this here isn't really bad...
+ nFormatKey = 0;
+ }
+ }
+ }
+
+ // No? Maybe the css::form::component::Form behind the cursor?
+ if (!m_xSupplier.is())
+ {
+ if (xCursor.is())
+ { // If we take the formatter from the cursor, then also the key from the field to which we are bound
+ m_xSupplier = getNumberFormats(getConnection(xCursor));
+
+ if (m_rColumn.GetField().is())
+ nFormatKey = ::comphelper::getINT32(m_rColumn.GetField()->getPropertyValue(FM_PROP_FORMATKEY));
+ }
+ }
+
+ SvNumberFormatter* pFormatterUsed = nullptr;
+ if (m_xSupplier.is())
+ {
+ SvNumberFormatsSupplierObj* pImplementation = comphelper::getFromUnoTunnel<SvNumberFormatsSupplierObj>(m_xSupplier);
+ if (pImplementation)
+ pFormatterUsed = pImplementation->GetNumberFormatter();
+ else
+ // Everything is invalid: the supplier is of the wrong type, then we can not
+ // rely on a standard formatter to know the (possibly non-standard) key.
+ nFormatKey = -1;
+ }
+
+ // a standard formatter ...
+ if (pFormatterUsed == nullptr)
+ {
+ pFormatterUsed = rControlFormatter.StandardFormatter();
+ DBG_ASSERT(pFormatterUsed != nullptr, "DbFormattedField::Init : no standard formatter given by the numeric field !");
+ }
+ // ... and a standard key
+ if (nFormatKey == -1)
+ nFormatKey = 0;
+
+ rControlFormatter.SetFormatter(pFormatterUsed);
+ rPainterFormatter.SetFormatter(pFormatterUsed);
+
+ rControlFormatter.SetFormatKey(nFormatKey);
+ rPainterFormatter.SetFormatKey(nFormatKey);
+
+ rControlFormatter.TreatAsNumber(m_rColumn.IsNumeric());
+ rPainterFormatter.TreatAsNumber(m_rColumn.IsNumeric());
+
+ // min and max values
+ if (m_rColumn.IsNumeric())
+ {
+ bool bClearMin = true;
+ if (::comphelper::hasProperty(FM_PROP_EFFECTIVE_MIN, xUnoModel))
+ {
+ Any aMin( xUnoModel->getPropertyValue(FM_PROP_EFFECTIVE_MIN));
+ if (aMin.getValueType().getTypeClass() != TypeClass_VOID)
+ {
+ DBG_ASSERT(aMin.getValueType().getTypeClass() == TypeClass_DOUBLE, "DbFormattedField::Init : the model has an invalid min value !");
+ double dMin = ::comphelper::getDouble(aMin);
+ rControlFormatter.SetMinValue(dMin);
+ rPainterFormatter.SetMinValue(dMin);
+ bClearMin = false;
+ }
+ }
+ if (bClearMin)
+ {
+ rControlFormatter.ClearMinValue();
+ rPainterFormatter.ClearMinValue();
+ }
+ bool bClearMax = true;
+ if (::comphelper::hasProperty(FM_PROP_EFFECTIVE_MAX, xUnoModel))
+ {
+ Any aMax(xUnoModel->getPropertyValue(FM_PROP_EFFECTIVE_MAX));
+ if (aMax.getValueType().getTypeClass() != TypeClass_VOID)
+ {
+ DBG_ASSERT(aMax.getValueType().getTypeClass() == TypeClass_DOUBLE, "DbFormattedField::Init : the model has an invalid max value !");
+ double dMax = ::comphelper::getDouble(aMax);
+ rControlFormatter.SetMaxValue(dMax);
+ rPainterFormatter.SetMaxValue(dMax);
+ bClearMax = false;
+ }
+ }
+ if (bClearMax)
+ {
+ rControlFormatter.ClearMaxValue();
+ rPainterFormatter.ClearMaxValue();
+ }
+ }
+
+ // the default value
+ Any aDefault( xUnoModel->getPropertyValue(FM_PROP_EFFECTIVE_DEFAULT));
+ if (aDefault.hasValue())
+ { // the thing can be a double or a string
+ switch (aDefault.getValueType().getTypeClass())
+ {
+ case TypeClass_DOUBLE:
+ if (m_rColumn.IsNumeric())
+ {
+ rControlFormatter.SetDefaultValue(::comphelper::getDouble(aDefault));
+ rPainterFormatter.SetDefaultValue(::comphelper::getDouble(aDefault));
+ }
+ else
+ {
+ OUString sConverted;
+ const Color* pDummy;
+ pFormatterUsed->GetOutputString(::comphelper::getDouble(aDefault), 0, sConverted, &pDummy);
+ rControlFormatter.SetDefaultText(sConverted);
+ rPainterFormatter.SetDefaultText(sConverted);
+ }
+ break;
+ case TypeClass_STRING:
+ {
+ OUString sDefault( ::comphelper::getString(aDefault) );
+ if (m_rColumn.IsNumeric())
+ {
+ double dVal;
+ sal_uInt32 nTestFormat(0);
+ if (pFormatterUsed->IsNumberFormat(sDefault, nTestFormat, dVal))
+ {
+ rControlFormatter.SetDefaultValue(dVal);
+ rPainterFormatter.SetDefaultValue(dVal);
+ }
+ }
+ else
+ {
+ rControlFormatter.SetDefaultText(sDefault);
+ rPainterFormatter.SetDefaultText(sDefault);
+ }
+ }
+ break;
+ default:
+ OSL_FAIL( "DbFormattedField::Init: unexpected value type!" );
+ break;
+ }
+ }
+ DbLimitedLengthField::Init( rParent, xCursor );
+}
+
+CellControllerRef DbFormattedField::CreateController() const
+{
+ return new ::svt::FormattedFieldCellController(static_cast<FormattedControlBase*>(m_pWindow.get()));
+}
+
+void DbFormattedField::_propertyChanged( const PropertyChangeEvent& _rEvent )
+{
+ if (_rEvent.PropertyName == FM_PROP_FORMATKEY )
+ {
+ sal_Int32 nNewKey = _rEvent.NewValue.hasValue() ? ::comphelper::getINT32(_rEvent.NewValue) : 0;
+
+ DBG_ASSERT(m_pWindow && m_pPainter, "DbFormattedField::_propertyChanged : where are my windows ?");
+ if (m_pWindow)
+ static_cast<FormattedControlBase*>(m_pWindow.get())->get_formatter().SetFormatKey(nNewKey);
+ if (m_pPainter)
+ static_cast<FormattedControlBase*>(m_pPainter.get())->get_formatter().SetFormatKey(nNewKey);
+ }
+ else
+ {
+ DbLimitedLengthField::_propertyChanged( _rEvent );
+ }
+}
+
+OUString DbFormattedField::GetFormatText(const Reference< css::sdb::XColumn >& _rxField, const Reference< XNumberFormatter >& /*xFormatter*/, const Color** ppColor)
+{
+ // no color specification by default
+ if (ppColor != nullptr)
+ *ppColor = nullptr;
+
+ // NULL value -> empty text
+ if (!_rxField.is())
+ return OUString();
+
+ FormattedControlBase* pControl = static_cast<FormattedControlBase*>(m_pPainter.get());
+ weld::EntryFormatter& rPainterFormatter = pControl->get_formatter();
+
+ OUString aText;
+ try
+ {
+ if (m_rColumn.IsNumeric())
+ {
+ // The IsNumeric at the column says nothing about the class of the used format, but
+ // about the class of the field bound to the column. So when you bind a FormattedField
+ // column to a double field and format it as text, m_rColumn.IsNumeric() returns
+ // sal_True. So that simply means that I can query the contents of the variant using
+ // getDouble, and then I can leave the rest (the formatting) to the FormattedField.
+ double dValue = getValue( _rxField, m_rColumn.GetParent().getNullDate() );
+ if (_rxField->wasNull())
+ return aText;
+ rPainterFormatter.SetValue(dValue);
+ }
+ else
+ {
+ // Here I can not work with a double, since the field can not provide it to me.
+ // So simply bind the text from the css::util::NumberFormatter to the correct css::form::component::Form.
+ aText = _rxField->getString();
+ if (_rxField->wasNull())
+ return aText;
+ rPainterFormatter.SetTextFormatted(aText);
+ }
+ }
+ catch( const Exception& )
+ {
+ DBG_UNHANDLED_EXCEPTION("svx");
+ }
+
+ aText = pControl->get_widget().get_text();
+ if (ppColor != nullptr)
+ *ppColor = rPainterFormatter.GetLastOutputColor();
+
+ return aText;
+}
+
+void DbFormattedField::UpdateFromField(const Reference< css::sdb::XColumn >& _rxField, const Reference< XNumberFormatter >& /*xFormatter*/)
+{
+ try
+ {
+ FormattedControlBase* pEditControl = static_cast<FormattedControlBase*>(m_pWindow.get());
+ weld::Entry& rEntry = pEditControl->get_widget();
+ weld::EntryFormatter& rEditFormatter = pEditControl->get_formatter();
+
+ if (!_rxField.is())
+ {
+ // NULL value -> empty text
+ rEntry.set_text(OUString());
+ }
+ else if (m_rColumn.IsNumeric())
+ {
+ // The IsNumeric at the column says nothing about the class of the used format, but
+ // about the class of the field bound to the column. So when you bind a FormattedField
+ // column to a double field and format it as text, m_rColumn.IsNumeric() returns
+ // sal_True. So that simply means that I can query the contents of the variant using
+ // getDouble, and then I can leave the rest (the formatting) to the FormattedField.
+ double dValue = getValue( _rxField, m_rColumn.GetParent().getNullDate() );
+ if (_rxField->wasNull())
+ rEntry.set_text(OUString());
+ else
+ rEditFormatter.SetValue(dValue);
+ }
+ else
+ {
+ // Here I can not work with a double, since the field can not provide it to me.
+ // So simply bind the text from the css::util::NumberFormatter to the correct css::form::component::Form.
+ OUString sText( _rxField->getString());
+
+ rEditFormatter.SetTextFormatted( sText );
+ rEntry.select_region(0, -1);
+ }
+ }
+ catch( const Exception& )
+ {
+ DBG_UNHANDLED_EXCEPTION("svx");
+ }
+}
+
+void DbFormattedField::updateFromModel( Reference< XPropertySet > _rxModel )
+{
+ OSL_ENSURE( _rxModel.is() && m_pWindow, "DbFormattedField::updateFromModel: invalid call!" );
+
+ FormattedControlBase* pEditControl = static_cast<FormattedControlBase*>(m_pWindow.get());
+ weld::Entry& rEntry = pEditControl->get_widget();
+ weld::EntryFormatter& rEditFormatter = pEditControl->get_formatter();
+
+ OUString sText;
+ Any aValue = _rxModel->getPropertyValue( FM_PROP_EFFECTIVE_VALUE );
+ if ( !aValue.hasValue() || (aValue >>= sText) )
+ {
+ // our effective value is transferred as string
+ rEditFormatter.SetTextFormatted( sText );
+ rEntry.select_region(0, -1);
+ }
+ else
+ {
+ double dValue = 0;
+ aValue >>= dValue;
+ rEditFormatter.SetValue(dValue);
+ }
+}
+
+bool DbFormattedField::commitControl()
+{
+ Any aNewVal;
+
+ FormattedControlBase* pEditControl = static_cast<FormattedControlBase*>(m_pWindow.get());
+ weld::Entry& rEntry = pEditControl->get_widget();
+ weld::EntryFormatter& rEditFormatter = pEditControl->get_formatter();
+
+ if (m_rColumn.IsNumeric())
+ {
+ if (!rEntry.get_text().isEmpty())
+ aNewVal <<= rEditFormatter.GetValue();
+ // an empty string is passed on as void by default, to start with
+ }
+ else
+ aNewVal <<= rEditFormatter.GetTextValue();
+
+ m_rColumn.getModel()->setPropertyValue(FM_PROP_EFFECTIVE_VALUE, aNewVal);
+ return true;
+}
+
+DbCheckBox::DbCheckBox( DbGridColumn& _rColumn )
+ :DbCellControl( _rColumn )
+{
+ setAlignedController( false );
+}
+
+namespace
+{
+ void setCheckBoxStyle( vcl::Window* _pWindow, bool bMono )
+ {
+ AllSettings aSettings = _pWindow->GetSettings();
+ StyleSettings aStyleSettings = aSettings.GetStyleSettings();
+ if( bMono )
+ aStyleSettings.SetOptions( aStyleSettings.GetOptions() | StyleSettingsOptions::Mono );
+ else
+ aStyleSettings.SetOptions( aStyleSettings.GetOptions() & (~StyleSettingsOptions::Mono) );
+ aSettings.SetStyleSettings( aStyleSettings );
+ _pWindow->SetSettings( aSettings );
+ }
+}
+
+void DbCheckBox::Init(BrowserDataWin& rParent, const Reference< XRowSet >& xCursor)
+{
+ setTransparent( true );
+
+ m_pWindow = VclPtr<CheckBoxControl>::Create( &rParent );
+ m_pPainter = VclPtr<CheckBoxControl>::Create( &rParent );
+
+ m_pWindow->SetPaintTransparent( true );
+ m_pPainter->SetPaintTransparent( true );
+
+ m_pPainter->SetBackground();
+
+ try
+ {
+ Reference< XPropertySet > xModel( m_rColumn.getModel(), UNO_SET_THROW );
+
+ sal_Int16 nStyle = awt::VisualEffect::LOOK3D;
+ OSL_VERIFY( xModel->getPropertyValue( FM_PROP_VISUALEFFECT ) >>= nStyle );
+
+ setCheckBoxStyle( m_pWindow, nStyle == awt::VisualEffect::FLAT );
+ setCheckBoxStyle( m_pPainter, nStyle == awt::VisualEffect::FLAT );
+
+ bool bTristate = true;
+ OSL_VERIFY( xModel->getPropertyValue( FM_PROP_TRISTATE ) >>= bTristate );
+ static_cast< CheckBoxControl* >( m_pWindow.get() )->EnableTriState( bTristate );
+ static_cast< CheckBoxControl* >( m_pPainter.get() )->EnableTriState( bTristate );
+ }
+ catch( const Exception& )
+ {
+ DBG_UNHANDLED_EXCEPTION("svx");
+ }
+
+ DbCellControl::Init( rParent, xCursor );
+}
+
+CellControllerRef DbCheckBox::CreateController() const
+{
+ return new CheckBoxCellController(static_cast<CheckBoxControl*>(m_pWindow.get()));
+}
+
+static void lcl_setCheckBoxState( const Reference< css::sdb::XColumn >& _rxField,
+ CheckBoxControl* _pCheckBoxControl )
+{
+ TriState eState = TRISTATE_INDET;
+ if (_rxField.is())
+ {
+ try
+ {
+ bool bValue = _rxField->getBoolean();
+ if (!_rxField->wasNull())
+ eState = bValue ? TRISTATE_TRUE : TRISTATE_FALSE;
+ }
+ catch( const Exception& )
+ {
+ DBG_UNHANDLED_EXCEPTION("svx");
+ }
+ }
+ _pCheckBoxControl->SetState(eState);
+}
+
+void DbCheckBox::UpdateFromField(const Reference< css::sdb::XColumn >& _rxField, const Reference< XNumberFormatter >& /*xFormatter*/)
+{
+ lcl_setCheckBoxState( _rxField, static_cast<CheckBoxControl*>(m_pWindow.get()) );
+}
+
+void DbCheckBox::PaintFieldToCell(OutputDevice& rDev, const tools::Rectangle& rRect,
+ const Reference< css::sdb::XColumn >& _rxField,
+ const Reference< XNumberFormatter >& xFormatter)
+{
+ CheckBoxControl* pControl = static_cast<CheckBoxControl*>(m_pPainter.get());
+ lcl_setCheckBoxState( _rxField, pControl );
+
+ Size aBoxSize;
+
+ switch (rDev.GetOutDevType())
+ {
+ case OUTDEV_WINDOW:
+ case OUTDEV_VIRDEV:
+ aBoxSize = pControl->GetBox().get_preferred_size();
+ break;
+ case OUTDEV_PRINTER:
+ case OUTDEV_PDF:
+ {
+ auto nSize = std::min(rRect.GetWidth(), rRect.GetHeight());
+ aBoxSize = Size(nSize, nSize);
+ break;
+ }
+ }
+
+ tools::Rectangle aRect(Point(rRect.Left() + ((rRect.GetWidth() - aBoxSize.Width()) / 2),
+ rRect.Top() + ((rRect.GetHeight() - aBoxSize.Height()) / 2)),
+ aBoxSize);
+
+ DbCellControl::PaintFieldToCell(rDev, aRect, _rxField, xFormatter);
+}
+
+void DbCheckBox::PaintCell(OutputDevice& rDev, const tools::Rectangle& rRect)
+{
+ switch (rDev.GetOutDevType())
+ {
+ case OUTDEV_WINDOW:
+ case OUTDEV_VIRDEV:
+ DbCellControl::PaintCell(rDev, rRect);
+ break;
+ case OUTDEV_PRINTER:
+ case OUTDEV_PDF:
+ {
+ TriState eState = static_cast<CheckBoxControl*>(m_pWindow.get())->GetState();
+
+ MapMode aResMapMode(MapUnit::Map100thMM);
+ Size aImageSize = rDev.LogicToPixel(Size(300, 300), aResMapMode);
+ Size aBrd1Size = rDev.LogicToPixel(Size(20, 20), aResMapMode);
+ Size aBrd2Size = rDev.LogicToPixel(Size(30, 30), aResMapMode);
+ int nCheckWidth = rDev.LogicToPixel(Size(20, 20), aResMapMode).Width();
+
+ tools::Rectangle aStateRect;
+ aStateRect.SetLeft(rRect.Left() + ((rRect.GetWidth() - aImageSize.Width()) / 2));
+ aStateRect.SetTop(rRect.Top() + ((rRect.GetHeight() - aImageSize.Height()) / 2));
+ aStateRect.SetRight(aStateRect.Left() + aImageSize.Width() - 1);
+ aStateRect.SetBottom(aStateRect.Top() + aImageSize.Height() - 1);
+
+ rDev.Push();
+ rDev.SetMapMode();
+
+ rDev.SetLineColor();
+ rDev.SetFillColor(COL_BLACK);
+ rDev.DrawRect(aStateRect);
+ aStateRect.AdjustLeft(aBrd1Size.Width());
+ aStateRect.AdjustTop(aBrd1Size.Height());
+ aStateRect.AdjustRight(-aBrd1Size.Width());
+ aStateRect.AdjustBottom(-aBrd1Size.Height());
+ if (eState == TRISTATE_INDET)
+ rDev.SetFillColor(COL_LIGHTGRAY);
+ else
+ rDev.SetFillColor(COL_WHITE);
+ rDev.DrawRect(aStateRect);
+
+ if (eState == TRISTATE_TRUE)
+ {
+ aStateRect.AdjustLeft(aBrd2Size.Width());
+ aStateRect.AdjustTop(aBrd2Size.Height());
+ aStateRect.AdjustRight(-aBrd2Size.Width());
+ aStateRect.AdjustBottom(-aBrd2Size.Height());
+ Point aPos11(aStateRect.TopLeft());
+ Point aPos12(aStateRect.BottomRight());
+ Point aPos21(aStateRect.TopRight());
+ Point aPos22(aStateRect.BottomLeft());
+ Point aTempPos11(aPos11);
+ Point aTempPos12(aPos12);
+ Point aTempPos21(aPos21);
+ Point aTempPos22(aPos22);
+ rDev.SetLineColor(COL_BLACK);
+ int nDX = 0;
+ for (int i = 0; i < nCheckWidth; i++)
+ {
+ if ( !(i % 2) )
+ {
+ aTempPos11.setX(aPos11.X() + nDX);
+ aTempPos12.setX(aPos12.X() + nDX);
+ aTempPos21.setX(aPos21.X() + nDX);
+ aTempPos22.setX(aPos22.X() + nDX);
+ }
+ else
+ {
+ nDX++;
+ aTempPos11.setX(aPos11.X() - nDX);
+ aTempPos12.setX(aPos12.X() - nDX);
+ aTempPos21.setX(aPos21.X() - nDX);
+ aTempPos22.setX(aPos22.X() - nDX);
+ }
+ rDev.DrawLine(aTempPos11, aTempPos12);
+ rDev.DrawLine(aTempPos21, aTempPos22);
+ }
+ }
+
+ rDev.Pop();
+ break;
+ }
+ }
+}
+
+void DbCheckBox::updateFromModel( Reference< XPropertySet > _rxModel )
+{
+ OSL_ENSURE( _rxModel.is() && m_pWindow, "DbCheckBox::updateFromModel: invalid call!" );
+
+ sal_Int16 nState = TRISTATE_INDET;
+ _rxModel->getPropertyValue( FM_PROP_STATE ) >>= nState;
+ static_cast< CheckBoxControl* >( m_pWindow.get() )->SetState( static_cast< TriState >( nState ) );
+}
+
+bool DbCheckBox::commitControl()
+{
+ m_rColumn.getModel()->setPropertyValue( FM_PROP_STATE,
+ Any( static_cast<sal_Int16>( static_cast< CheckBoxControl* >( m_pWindow.get() )->GetState() ) ) );
+ return true;
+}
+
+OUString DbCheckBox::GetFormatText(const Reference< XColumn >& /*_rxField*/, const Reference< XNumberFormatter >& /*xFormatter*/, const Color** /*ppColor*/)
+{
+ return OUString();
+}
+
+DbPatternField::DbPatternField( DbGridColumn& _rColumn, const Reference<XComponentContext>& _rContext )
+ :DbCellControl( _rColumn )
+ ,m_xContext( _rContext )
+{
+ doPropertyListening( FM_PROP_LITERALMASK );
+ doPropertyListening( FM_PROP_EDITMASK );
+ doPropertyListening( FM_PROP_STRICTFORMAT );
+}
+
+void DbPatternField::implAdjustGenericFieldSetting( const Reference< XPropertySet >& _rxModel )
+{
+ DBG_ASSERT( m_pWindow, "DbPatternField::implAdjustGenericFieldSetting: not to be called without window!" );
+ DBG_ASSERT( _rxModel.is(), "DbPatternField::implAdjustGenericFieldSetting: invalid model!" );
+ if ( !m_pWindow || !_rxModel.is() )
+ return;
+
+ OUString aLitMask;
+ OUString aEditMask;
+ bool bStrict = false;
+
+ _rxModel->getPropertyValue( FM_PROP_LITERALMASK ) >>= aLitMask;
+ _rxModel->getPropertyValue( FM_PROP_EDITMASK ) >>= aEditMask;
+ _rxModel->getPropertyValue( FM_PROP_STRICTFORMAT ) >>= bStrict;
+
+ OString aAsciiEditMask(OUStringToOString(aEditMask, RTL_TEXTENCODING_ASCII_US));
+
+ weld::PatternFormatter& rEditFormatter = static_cast<PatternControl*>(m_pWindow.get())->get_formatter();
+ rEditFormatter.SetMask(aAsciiEditMask, aLitMask);
+ rEditFormatter.SetStrictFormat(bStrict);
+
+ weld::PatternFormatter& rPaintFormatter = static_cast<PatternControl*>(m_pPainter.get())->get_formatter();
+ rPaintFormatter.SetMask(aAsciiEditMask, aLitMask);
+ rPaintFormatter.SetStrictFormat(bStrict);
+}
+
+void DbPatternField::Init(BrowserDataWin& rParent, const Reference< XRowSet >& xCursor)
+{
+ m_rColumn.SetAlignmentFromModel(-1);
+
+ m_pWindow = VclPtr<PatternControl>::Create(&rParent);
+ m_pPainter= VclPtr<PatternControl>::Create(&rParent);
+
+ Reference< XPropertySet > xModel( m_rColumn.getModel() );
+ implAdjustGenericFieldSetting( xModel );
+
+ DbCellControl::Init( rParent, xCursor );
+}
+
+CellControllerRef DbPatternField::CreateController() const
+{
+ return new EditCellController(static_cast<PatternControl*>(m_pWindow.get()));
+}
+
+OUString DbPatternField::impl_formatText( const OUString& _rText )
+{
+ weld::PatternFormatter& rPaintFormatter = static_cast<PatternControl*>(m_pPainter.get())->get_formatter();
+ rPaintFormatter.get_widget().set_text(_rText);
+ rPaintFormatter.ReformatAll();
+ return rPaintFormatter.get_widget().get_text();
+}
+
+OUString DbPatternField::GetFormatText(const Reference< css::sdb::XColumn >& _rxField, const Reference< XNumberFormatter >& /*xFormatter*/, const Color** /*ppColor*/)
+{
+ bool bIsForPaint = _rxField != m_rColumn.GetField();
+ ::std::unique_ptr< FormattedColumnValue >& rpFormatter = bIsForPaint ? m_pPaintFormatter : m_pValueFormatter;
+
+ if (!rpFormatter)
+ {
+ rpFormatter = std::make_unique< FormattedColumnValue> (
+ m_xContext, getCursor(), Reference< XPropertySet >( _rxField, UNO_QUERY ) );
+ OSL_ENSURE(rpFormatter, "DbPatternField::Init: no value formatter!");
+ }
+ else
+ OSL_ENSURE( rpFormatter->getColumn() == _rxField, "DbPatternField::GetFormatText: my value formatter is working for another field ...!" );
+ // re-creating the value formatter here every time would be quite expensive ...
+
+ OUString sText;
+ if (rpFormatter)
+ sText = rpFormatter->getFormattedValue();
+
+ return impl_formatText( sText );
+}
+
+void DbPatternField::UpdateFromField( const Reference< XColumn >& _rxField, const Reference< XNumberFormatter >& _rxFormatter )
+{
+ weld::Entry& rEntry = static_cast<PatternControl*>(m_pWindow.get())->get_widget();
+ rEntry.set_text(GetFormatText(_rxField, _rxFormatter));
+ rEntry.select_region(-1, 0);
+}
+
+void DbPatternField::updateFromModel( Reference< XPropertySet > _rxModel )
+{
+ OSL_ENSURE( _rxModel.is() && m_pWindow, "DbPatternField::updateFromModel: invalid call!" );
+
+ OUString sText;
+ _rxModel->getPropertyValue( FM_PROP_TEXT ) >>= sText;
+
+ weld::Entry& rEntry = static_cast<PatternControl*>(m_pWindow.get())->get_widget();
+ rEntry.set_text(impl_formatText(sText));
+ rEntry.select_region(-1, 0);
+}
+
+bool DbPatternField::commitControl()
+{
+ weld::Entry& rEntry = static_cast<PatternControl*>(m_pWindow.get())->get_widget();
+ m_rColumn.getModel()->setPropertyValue(FM_PROP_TEXT, Any(rEntry.get_text()));
+ return true;
+}
+
+DbSpinField::DbSpinField( DbGridColumn& _rColumn, sal_Int16 _nStandardAlign )
+ :DbCellControl( _rColumn )
+ ,m_nStandardAlign( _nStandardAlign )
+{
+}
+
+void DbSpinField::Init(BrowserDataWin& _rParent, const Reference< XRowSet >& _rxCursor)
+{
+ m_rColumn.SetAlignmentFromModel( m_nStandardAlign );
+
+ Reference< XPropertySet > xModel( m_rColumn.getModel() );
+
+ // determine if we need a spinbutton version
+ bool bSpinButton(false);
+ if ( ::comphelper::getBOOL( xModel->getPropertyValue( FM_PROP_SPIN ) ) )
+ bSpinButton = true;
+ // create the fields
+ m_pWindow = createField( &_rParent, bSpinButton, xModel );
+ m_pPainter = createField( &_rParent, bSpinButton, xModel );
+
+ // adjust all other settings which depend on the property values
+ implAdjustGenericFieldSetting( xModel );
+
+ // call the base class
+ DbCellControl::Init( _rParent, _rxCursor );
+}
+
+CellControllerRef DbSpinField::CreateController() const
+{
+ return new ::svt::FormattedFieldCellController(static_cast<FormattedControlBase*>(m_pWindow.get()));
+}
+
+DbNumericField::DbNumericField( DbGridColumn& _rColumn )
+ :DbSpinField( _rColumn )
+{
+ doPropertyListening( FM_PROP_DECIMAL_ACCURACY );
+ doPropertyListening( FM_PROP_VALUEMIN );
+ doPropertyListening( FM_PROP_VALUEMAX );
+ doPropertyListening( FM_PROP_VALUESTEP );
+ doPropertyListening( FM_PROP_STRICTFORMAT );
+ doPropertyListening( FM_PROP_SHOWTHOUSANDSEP );
+}
+
+void DbNumericField::implAdjustGenericFieldSetting( const Reference< XPropertySet >& _rxModel )
+{
+ DBG_ASSERT( m_pWindow, "DbNumericField::implAdjustGenericFieldSetting: not to be called without window!" );
+ DBG_ASSERT( _rxModel.is(), "DbNumericField::implAdjustGenericFieldSetting: invalid model!" );
+ if ( !m_pWindow || !_rxModel.is() )
+ return;
+
+ sal_Int32 nMin = static_cast<sal_Int32>(getDouble( _rxModel->getPropertyValue( FM_PROP_VALUEMIN ) ));
+ sal_Int32 nMax = static_cast<sal_Int32>(getDouble( _rxModel->getPropertyValue( FM_PROP_VALUEMAX ) ));
+ sal_Int32 nStep = static_cast<sal_Int32>(getDouble( _rxModel->getPropertyValue( FM_PROP_VALUESTEP ) ));
+ bool bStrict = getBOOL( _rxModel->getPropertyValue( FM_PROP_STRICTFORMAT ) );
+ sal_Int16 nScale = getINT16( _rxModel->getPropertyValue( FM_PROP_DECIMAL_ACCURACY ) );
+ bool bThousand = getBOOL( _rxModel->getPropertyValue( FM_PROP_SHOWTHOUSANDSEP ) );
+
+ Formatter& rEditFormatter = static_cast<FormattedControlBase*>(m_pWindow.get())->get_formatter();
+ rEditFormatter.SetMinValue(nMin);
+ rEditFormatter.SetMaxValue(nMax);
+ rEditFormatter.SetSpinSize(nStep);
+ rEditFormatter.SetStrictFormat(bStrict);
+
+ Formatter& rPaintFormatter = static_cast<FormattedControlBase*>(m_pPainter.get())->get_formatter();
+ rPaintFormatter.SetMinValue(nMin);
+ rPaintFormatter.SetMaxValue(nMax);
+ rPaintFormatter.SetStrictFormat(bStrict);
+
+ // give a formatter to the field and the painter;
+ // test first if I can get from the service behind a connection
+ Reference< css::util::XNumberFormatsSupplier > xSupplier;
+ Reference< XRowSet > xForm;
+ if ( m_rColumn.GetParent().getDataSource() )
+ xForm.set( Reference< XInterface >(*m_rColumn.GetParent().getDataSource()), UNO_QUERY );
+ if ( xForm.is() )
+ xSupplier = getNumberFormats( getConnection( xForm ), true );
+ SvNumberFormatter* pFormatterUsed = nullptr;
+ if ( xSupplier.is() )
+ {
+ SvNumberFormatsSupplierObj* pImplementation = comphelper::getFromUnoTunnel<SvNumberFormatsSupplierObj>( xSupplier );
+ pFormatterUsed = pImplementation ? pImplementation->GetNumberFormatter() : nullptr;
+ }
+ if ( nullptr == pFormatterUsed )
+ { // the cursor didn't lead to success -> standard
+ pFormatterUsed = rEditFormatter.StandardFormatter();
+ DBG_ASSERT( pFormatterUsed != nullptr, "DbNumericField::implAdjustGenericFieldSetting: no standard formatter given by the numeric field !" );
+ }
+ rEditFormatter.SetFormatter( pFormatterUsed );
+ rPaintFormatter.SetFormatter( pFormatterUsed );
+
+ // and then generate a format which has the desired length after the decimal point, etc.
+ LanguageType aAppLanguage = Application::GetSettings().GetUILanguageTag().getLanguageType();
+ OUString sFormatString = pFormatterUsed->GenerateFormat(0, aAppLanguage, bThousand, false, nScale);
+
+ rEditFormatter.SetFormat( sFormatString, aAppLanguage );
+ rPaintFormatter.SetFormat( sFormatString, aAppLanguage );
+}
+
+VclPtr<svt::ControlBase> DbNumericField::createField(BrowserDataWin* pParent, bool bSpinButton, const Reference<XPropertySet>& /*rxModel*/)
+{
+ return VclPtr<DoubleNumericControl>::Create(pParent, bSpinButton);
+}
+
+namespace
+{
+ OUString lcl_setFormattedNumeric_nothrow( FormattedControlBase& _rField, const DbCellControl& _rControl,
+ const Reference< XColumn >& _rxField, const Reference< XNumberFormatter >& _rxFormatter )
+ {
+ OUString sValue;
+ if ( _rxField.is() )
+ {
+ try
+ {
+ double fValue = _rControl.GetValue( _rxField, _rxFormatter );
+ if ( !_rxField->wasNull() )
+ {
+ _rField.get_formatter().SetValue(fValue);
+ sValue = _rField.get_widget().get_text();
+ }
+ }
+ catch( const Exception& )
+ {
+ DBG_UNHANDLED_EXCEPTION("svx");
+ }
+ }
+ return sValue;
+ }
+}
+
+OUString DbNumericField::GetFormatText(const Reference< css::sdb::XColumn >& _rxField, const Reference< css::util::XNumberFormatter >& _rxFormatter, const Color** /*ppColor*/)
+{
+ return lcl_setFormattedNumeric_nothrow(dynamic_cast<FormattedControlBase&>(*m_pPainter), *this, _rxField, _rxFormatter);
+}
+
+void DbNumericField::UpdateFromField(const Reference< css::sdb::XColumn >& _rxField, const Reference< css::util::XNumberFormatter >& _rxFormatter)
+{
+ lcl_setFormattedNumeric_nothrow(dynamic_cast<FormattedControlBase&>(*m_pWindow), *this, _rxField, _rxFormatter);
+}
+
+void DbNumericField::updateFromModel( Reference< XPropertySet > _rxModel )
+{
+ OSL_ENSURE( _rxModel.is() && m_pWindow, "DbNumericField::updateFromModel: invalid call!" );
+
+ FormattedControlBase* pControl = static_cast<FormattedControlBase*>(m_pWindow.get());
+
+ double dValue = 0;
+ if ( _rxModel->getPropertyValue( FM_PROP_VALUE ) >>= dValue )
+ {
+ Formatter& rFormatter = pControl->get_formatter();
+ rFormatter.SetValue(dValue);
+ }
+ else
+ pControl->get_widget().set_text(OUString());
+}
+
+bool DbNumericField::commitControl()
+{
+ FormattedControlBase* pControl = static_cast<FormattedControlBase*>(m_pWindow.get());
+ OUString aText(pControl->get_widget().get_text());
+ Any aVal;
+
+ if (!aText.isEmpty()) // not empty
+ {
+ Formatter& rFormatter = pControl->get_formatter();
+ double fValue = rFormatter.GetValue();
+ aVal <<= fValue;
+ }
+ m_rColumn.getModel()->setPropertyValue(FM_PROP_VALUE, aVal);
+ return true;
+}
+
+DbCurrencyField::DbCurrencyField(DbGridColumn& _rColumn)
+ :DbSpinField( _rColumn )
+{
+ doPropertyListening( FM_PROP_DECIMAL_ACCURACY );
+ doPropertyListening( FM_PROP_VALUEMIN );
+ doPropertyListening( FM_PROP_VALUEMAX );
+ doPropertyListening( FM_PROP_VALUESTEP );
+ doPropertyListening( FM_PROP_STRICTFORMAT );
+ doPropertyListening( FM_PROP_SHOWTHOUSANDSEP );
+ doPropertyListening( FM_PROP_CURRENCYSYMBOL );
+}
+
+void DbCurrencyField::implAdjustGenericFieldSetting( const Reference< XPropertySet >& _rxModel )
+{
+ DBG_ASSERT( m_pWindow, "DbCurrencyField::implAdjustGenericFieldSetting: not to be called without window!" );
+ DBG_ASSERT( _rxModel.is(), "DbCurrencyField::implAdjustGenericFieldSetting: invalid model!" );
+ if ( !m_pWindow || !_rxModel.is() )
+ return;
+
+ sal_Int16 nScale = getINT16( _rxModel->getPropertyValue( FM_PROP_DECIMAL_ACCURACY ) );
+ double nMin = getDouble( _rxModel->getPropertyValue( FM_PROP_VALUEMIN ) );
+ double nMax = getDouble( _rxModel->getPropertyValue( FM_PROP_VALUEMAX ) );
+ double nStep = getDouble( _rxModel->getPropertyValue( FM_PROP_VALUESTEP ) );
+ bool bStrict = getBOOL( _rxModel->getPropertyValue( FM_PROP_STRICTFORMAT ) );
+ bool bThousand = getBOOL( _rxModel->getPropertyValue( FM_PROP_SHOWTHOUSANDSEP ) );
+ OUString aStr( getString( _rxModel->getPropertyValue(FM_PROP_CURRENCYSYMBOL ) ) );
+
+ Formatter& rEditFormatter = static_cast<FormattedControlBase*>(m_pWindow.get())->get_formatter();
+ rEditFormatter.SetDecimalDigits(nScale);
+ rEditFormatter.SetMinValue(nMin);
+ rEditFormatter.SetMaxValue(nMax);
+ rEditFormatter.SetSpinSize(nStep);
+ rEditFormatter.SetStrictFormat(bStrict);
+ weld::LongCurrencyFormatter& rCurrencyEditFormatter = static_cast<weld::LongCurrencyFormatter&>(rEditFormatter);
+ rCurrencyEditFormatter.SetUseThousandSep(bThousand);
+ rCurrencyEditFormatter.SetCurrencySymbol(aStr);
+
+ Formatter& rPaintFormatter = static_cast<FormattedControlBase*>(m_pPainter.get())->get_formatter();
+ rPaintFormatter.SetDecimalDigits(nScale);
+ rPaintFormatter.SetMinValue(nMin);
+ rPaintFormatter.SetMaxValue(nMax);
+ rPaintFormatter.SetStrictFormat(bStrict);
+ weld::LongCurrencyFormatter& rPaintCurrencyFormatter = static_cast<weld::LongCurrencyFormatter&>(rPaintFormatter);
+ rPaintCurrencyFormatter.SetUseThousandSep(bThousand);
+ rPaintCurrencyFormatter.SetCurrencySymbol(aStr);
+}
+
+VclPtr<svt::ControlBase> DbCurrencyField::createField(BrowserDataWin* pParent, bool bSpinButton, const Reference< XPropertySet >& /*rxModel*/)
+{
+ return VclPtr<LongCurrencyControl>::Create(pParent, bSpinButton);
+}
+
+namespace
+{
+ OUString lcl_setFormattedCurrency_nothrow( FormattedControlBase& _rField, const DbCurrencyField& _rControl,
+ const Reference< XColumn >& _rxField, const Reference< XNumberFormatter >& _rxFormatter )
+ {
+ OUString sValue;
+ if ( _rxField.is() )
+ {
+ try
+ {
+ double fValue = _rControl.GetValue( _rxField, _rxFormatter );
+ if ( !_rxField->wasNull() )
+ {
+ _rField.get_formatter().SetValue(fValue);
+ sValue = _rField.get_widget().get_text();
+ }
+ }
+ catch( const Exception& )
+ {
+ DBG_UNHANDLED_EXCEPTION("svx");
+ }
+ }
+ return sValue;
+ }
+}
+
+OUString DbCurrencyField::GetFormatText(const Reference< css::sdb::XColumn >& _rxField, const Reference< css::util::XNumberFormatter >& _rxFormatter, const Color** /*ppColor*/)
+{
+ return lcl_setFormattedCurrency_nothrow(dynamic_cast<FormattedControlBase&>(*m_pPainter), *this, _rxField, _rxFormatter);
+}
+
+void DbCurrencyField::UpdateFromField(const Reference< css::sdb::XColumn >& _rxField, const Reference< css::util::XNumberFormatter >& _rxFormatter)
+{
+ lcl_setFormattedCurrency_nothrow(dynamic_cast<FormattedControlBase&>(*m_pWindow), *this, _rxField, _rxFormatter);
+}
+
+void DbCurrencyField::updateFromModel( Reference< XPropertySet > _rxModel )
+{
+ OSL_ENSURE( _rxModel.is() && m_pWindow, "DbCurrencyField::updateFromModel: invalid call!" );
+
+ FormattedControlBase* pControl = static_cast<FormattedControlBase*>(m_pWindow.get());
+
+ double dValue = 0;
+ if ( _rxModel->getPropertyValue( FM_PROP_VALUE ) >>= dValue )
+ {
+ Formatter& rFormatter = pControl->get_formatter();
+ rFormatter.SetValue(dValue);
+ }
+ else
+ pControl->get_widget().set_text(OUString());
+}
+
+bool DbCurrencyField::commitControl()
+{
+ FormattedControlBase* pControl = static_cast<FormattedControlBase*>(m_pWindow.get());
+ OUString aText(pControl->get_widget().get_text());
+ Any aVal;
+
+ if (!aText.isEmpty()) // not empty
+ {
+ Formatter& rFormatter = pControl->get_formatter();
+ double fValue = rFormatter.GetValue();
+ aVal <<= fValue;
+ }
+ m_rColumn.getModel()->setPropertyValue(FM_PROP_VALUE, aVal);
+ return true;
+}
+
+DbDateField::DbDateField( DbGridColumn& _rColumn )
+ :DbSpinField( _rColumn )
+{
+ doPropertyListening( FM_PROP_DATEFORMAT );
+ doPropertyListening( FM_PROP_DATEMIN );
+ doPropertyListening( FM_PROP_DATEMAX );
+ doPropertyListening( FM_PROP_STRICTFORMAT );
+ doPropertyListening( FM_PROP_DATE_SHOW_CENTURY );
+}
+
+VclPtr<svt::ControlBase> DbDateField::createField(BrowserDataWin* pParent, bool bSpinButton, const Reference< XPropertySet >& rxModel)
+{
+ // check if there is a DropDown property set to TRUE
+ bool bDropDown = !hasProperty( FM_PROP_DROPDOWN, rxModel )
+ || getBOOL( rxModel->getPropertyValue( FM_PROP_DROPDOWN ) );
+ // given the apparent inability to set a custom up/down action for a gtk
+ // spinbutton to have different up/down dates depending on the zone the
+ // mouse is in, show the dropdown calendar for both the spin or dropdown case
+ return VclPtr<DateControl>::Create(pParent, bSpinButton || bDropDown);
+}
+
+void DbDateField::implAdjustGenericFieldSetting( const Reference< XPropertySet >& _rxModel )
+{
+ DBG_ASSERT( m_pWindow, "DbDateField::implAdjustGenericFieldSetting: not to be called without window!" );
+ DBG_ASSERT( _rxModel.is(), "DbDateField::implAdjustGenericFieldSetting: invalid model!" );
+ if ( !m_pWindow || !_rxModel.is() )
+ return;
+
+ sal_Int16 nFormat = getINT16( _rxModel->getPropertyValue( FM_PROP_DATEFORMAT ) );
+ util::Date aMin;
+ OSL_VERIFY( _rxModel->getPropertyValue( FM_PROP_DATEMIN ) >>= aMin );
+ util::Date aMax;
+ OSL_VERIFY( _rxModel->getPropertyValue( FM_PROP_DATEMAX ) >>= aMax );
+ bool bStrict = getBOOL( _rxModel->getPropertyValue( FM_PROP_STRICTFORMAT ) );
+
+ FormattedControlBase* pControl = static_cast<FormattedControlBase*>(m_pWindow.get());
+ weld::DateFormatter& rControlFormatter = static_cast<weld::DateFormatter&>(pControl->get_formatter());
+
+ FormattedControlBase* pPainter = static_cast<FormattedControlBase*>(m_pPainter.get());
+ weld::DateFormatter& rPainterFormatter = static_cast<weld::DateFormatter&>(pPainter->get_formatter());
+
+ Any aCentury = _rxModel->getPropertyValue( FM_PROP_DATE_SHOW_CENTURY );
+ if ( aCentury.getValueType().getTypeClass() != TypeClass_VOID )
+ {
+ bool bShowDateCentury = getBOOL( aCentury );
+
+ rControlFormatter.SetShowDateCentury(bShowDateCentury);
+ rPainterFormatter.SetShowDateCentury(bShowDateCentury);
+ }
+
+ rControlFormatter.SetExtDateFormat( static_cast<ExtDateFieldFormat>(nFormat) );
+ rControlFormatter.SetMin( aMin );
+ rControlFormatter.SetMax( aMax );
+ rControlFormatter.SetStrictFormat( bStrict );
+ rControlFormatter.EnableEmptyField( true );
+
+ rPainterFormatter.SetExtDateFormat( static_cast<ExtDateFieldFormat>(nFormat) );
+ rPainterFormatter.SetMin( aMin );
+ rPainterFormatter.SetMax( aMax );
+ rPainterFormatter.SetStrictFormat( bStrict );
+ rPainterFormatter.EnableEmptyField( true );
+}
+
+namespace
+{
+ OUString lcl_setFormattedDate_nothrow(DateControl& _rField, const Reference<XColumn>& _rxField)
+ {
+ OUString sDate;
+ if ( _rxField.is() )
+ {
+ try
+ {
+ css::util::Date aValue = _rxField->getDate();
+ if (!_rxField->wasNull())
+ {
+ _rField.SetDate(::Date(aValue.Day, aValue.Month, aValue.Year));
+ sDate = _rField.get_widget().get_text();
+ }
+ }
+ catch( const Exception& )
+ {
+ DBG_UNHANDLED_EXCEPTION("svx");
+ }
+ }
+ return sDate;
+ }
+}
+
+OUString DbDateField::GetFormatText(const Reference< css::sdb::XColumn >& _rxField, const Reference< css::util::XNumberFormatter >& /*xFormatter*/, const Color** /*ppColor*/)
+{
+ return lcl_setFormattedDate_nothrow(*static_cast<DateControl*>(m_pPainter.get()), _rxField);
+}
+
+void DbDateField::UpdateFromField(const Reference< css::sdb::XColumn >& _rxField, const Reference< XNumberFormatter >& /*xFormatter*/)
+{
+ lcl_setFormattedDate_nothrow(*static_cast<DateControl*>(m_pWindow.get()), _rxField);
+}
+
+void DbDateField::updateFromModel( Reference< XPropertySet > _rxModel )
+{
+ OSL_ENSURE( _rxModel.is() && m_pWindow, "DbDateField::updateFromModel: invalid call!" );
+
+ DateControl* pControl = static_cast<DateControl*>(m_pWindow.get());
+
+ util::Date aDate;
+ if ( _rxModel->getPropertyValue( FM_PROP_DATE ) >>= aDate )
+ pControl->SetDate(::Date(aDate));
+ else
+ pControl->get_widget().set_text(OUString());
+}
+
+bool DbDateField::commitControl()
+{
+ FormattedControlBase* pControl = static_cast<FormattedControlBase*>(m_pWindow.get());
+ OUString aText(pControl->get_widget().get_text());
+ Any aVal;
+
+ if (!aText.isEmpty()) // not empty
+ {
+ weld::DateFormatter& rControlFormatter = static_cast<weld::DateFormatter&>(pControl->get_formatter());
+ aVal <<= rControlFormatter.GetDate().GetUNODate();
+ }
+
+ m_rColumn.getModel()->setPropertyValue(FM_PROP_DATE, aVal);
+ return true;
+}
+
+DbTimeField::DbTimeField( DbGridColumn& _rColumn )
+ :DbSpinField( _rColumn, css::awt::TextAlign::LEFT )
+{
+ doPropertyListening( FM_PROP_TIMEFORMAT );
+ doPropertyListening( FM_PROP_TIMEMIN );
+ doPropertyListening( FM_PROP_TIMEMAX );
+ doPropertyListening( FM_PROP_STRICTFORMAT );
+}
+
+VclPtr<svt::ControlBase> DbTimeField::createField(BrowserDataWin* pParent, bool bSpinButton, const Reference< XPropertySet >& /*rxModel*/ )
+{
+ return VclPtr<TimeControl>::Create(pParent, bSpinButton);
+}
+
+void DbTimeField::implAdjustGenericFieldSetting( const Reference< XPropertySet >& _rxModel )
+{
+ DBG_ASSERT( m_pWindow, "DbTimeField::implAdjustGenericFieldSetting: not to be called without window!" );
+ DBG_ASSERT( _rxModel.is(), "DbTimeField::implAdjustGenericFieldSetting: invalid model!" );
+ if ( !m_pWindow || !_rxModel.is() )
+ return;
+
+ sal_Int16 nFormat = getINT16( _rxModel->getPropertyValue( FM_PROP_TIMEFORMAT ) );
+ util::Time aMin;
+ OSL_VERIFY( _rxModel->getPropertyValue( FM_PROP_TIMEMIN ) >>= aMin );
+ util::Time aMax;
+ OSL_VERIFY( _rxModel->getPropertyValue( FM_PROP_TIMEMAX ) >>= aMax );
+ bool bStrict = getBOOL( _rxModel->getPropertyValue( FM_PROP_STRICTFORMAT ) );
+
+ FormattedControlBase* pControl = static_cast<FormattedControlBase*>(m_pWindow.get());
+ weld::TimeFormatter& rControlFormatter = static_cast<weld::TimeFormatter&>(pControl->get_formatter());
+
+ rControlFormatter.SetExtFormat(static_cast<ExtTimeFieldFormat>(nFormat));
+ rControlFormatter.SetMin(aMin);
+ rControlFormatter.SetMax(aMax);
+ rControlFormatter.SetStrictFormat(bStrict);
+ rControlFormatter.EnableEmptyField(true);
+
+ FormattedControlBase* pPainter = static_cast<FormattedControlBase*>(m_pPainter.get());
+ weld::TimeFormatter& rPainterFormatter = static_cast<weld::TimeFormatter&>(pPainter->get_formatter());
+
+ rPainterFormatter.SetExtFormat(static_cast<ExtTimeFieldFormat>(nFormat));
+ rPainterFormatter.SetMin(aMin);
+ rPainterFormatter.SetMax(aMax);
+ rPainterFormatter.SetStrictFormat(bStrict);
+ rPainterFormatter.EnableEmptyField(true);
+}
+
+namespace
+{
+ OUString lcl_setFormattedTime_nothrow(TimeControl& _rField, const Reference<XColumn>& _rxField)
+ {
+ OUString sTime;
+ if ( _rxField.is() )
+ {
+ try
+ {
+ css::util::Time aValue = _rxField->getTime();
+ if (!_rxField->wasNull())
+ {
+ static_cast<weld::TimeFormatter&>(_rField.get_formatter()).SetTime( ::tools::Time( aValue ) );
+ sTime = _rField.get_widget().get_text();
+ }
+ }
+ catch( const Exception& )
+ {
+ DBG_UNHANDLED_EXCEPTION("svx");
+ }
+ }
+ return sTime;
+ }
+}
+
+OUString DbTimeField::GetFormatText(const Reference< css::sdb::XColumn >& _rxField, const Reference< css::util::XNumberFormatter >& /*xFormatter*/, const Color** /*ppColor*/)
+{
+ return lcl_setFormattedTime_nothrow(*static_cast<TimeControl*>(m_pPainter.get()), _rxField);
+}
+
+void DbTimeField::UpdateFromField(const Reference< css::sdb::XColumn >& _rxField, const Reference< XNumberFormatter >& /*xFormatter*/)
+{
+ lcl_setFormattedTime_nothrow(*static_cast<TimeControl*>(m_pWindow.get()), _rxField);
+}
+
+void DbTimeField::updateFromModel( Reference< XPropertySet > _rxModel )
+{
+ OSL_ENSURE( _rxModel.is() && m_pWindow, "DbTimeField::updateFromModel: invalid call!" );
+
+ FormattedControlBase* pControl = static_cast<FormattedControlBase*>(m_pWindow.get());
+ weld::TimeFormatter& rControlFormatter = static_cast<weld::TimeFormatter&>(pControl->get_formatter());
+
+ util::Time aTime;
+ if ( _rxModel->getPropertyValue( FM_PROP_TIME ) >>= aTime )
+ rControlFormatter.SetTime(::tools::Time(aTime));
+ else
+ pControl->get_widget().set_text(OUString());
+}
+
+bool DbTimeField::commitControl()
+{
+ FormattedControlBase* pControl = static_cast<FormattedControlBase*>(m_pWindow.get());
+ OUString aText(pControl->get_widget().get_text());
+ Any aVal;
+
+ if (!aText.isEmpty()) // not empty
+ {
+ weld::TimeFormatter& rControlFormatter = static_cast<weld::TimeFormatter&>(pControl->get_formatter());
+ aVal <<= rControlFormatter.GetTime().GetUNOTime();
+ }
+
+ m_rColumn.getModel()->setPropertyValue(FM_PROP_TIME, aVal);
+ return true;
+}
+
+DbComboBox::DbComboBox(DbGridColumn& _rColumn)
+ :DbCellControl(_rColumn)
+{
+ setAlignedController( false );
+
+ doPropertyListening( FM_PROP_STRINGITEMLIST );
+ doPropertyListening( FM_PROP_LINECOUNT );
+}
+
+void DbComboBox::_propertyChanged( const PropertyChangeEvent& _rEvent )
+{
+ if ( _rEvent.PropertyName == FM_PROP_STRINGITEMLIST )
+ {
+ SetList(_rEvent.NewValue);
+ }
+ else
+ {
+ DbCellControl::_propertyChanged( _rEvent ) ;
+ }
+}
+
+void DbComboBox::SetList(const Any& rItems)
+{
+ ComboBoxControl* pField = static_cast<ComboBoxControl*>(m_pWindow.get());
+ weld::ComboBox& rComboBox = pField->get_widget();
+ rComboBox.clear();
+
+ css::uno::Sequence<OUString> aTest;
+ if (rItems >>= aTest)
+ {
+ for (const OUString& rString : std::as_const(aTest))
+ rComboBox.append_text(rString);
+
+ // tell the grid control that this controller is invalid and has to be re-initialized
+ invalidatedController();
+ }
+}
+
+void DbComboBox::implAdjustGenericFieldSetting(const Reference<XPropertySet>&)
+{
+ // we no longer pay attention to FM_PROP_LINECOUNT
+}
+
+void DbComboBox::Init(BrowserDataWin& rParent, const Reference< XRowSet >& xCursor)
+{
+ m_rColumn.SetAlignmentFromModel(css::awt::TextAlign::LEFT);
+
+ m_pWindow = VclPtr<ComboBoxControl>::Create( &rParent );
+
+ // selection from right to left
+ AllSettings aSettings = m_pWindow->GetSettings();
+ StyleSettings aStyleSettings = aSettings.GetStyleSettings();
+ aStyleSettings.SetSelectionOptions(
+ aStyleSettings.GetSelectionOptions() | SelectionOptions::ShowFirst);
+ aSettings.SetStyleSettings(aStyleSettings);
+ m_pWindow->SetSettings(aSettings, true);
+
+ // some initial properties
+ Reference< XPropertySet > xModel(m_rColumn.getModel());
+ SetList( xModel->getPropertyValue( FM_PROP_STRINGITEMLIST ) );
+ implAdjustGenericFieldSetting( xModel );
+
+ DbCellControl::Init( rParent, xCursor );
+}
+
+CellControllerRef DbComboBox::CreateController() const
+{
+ return new ComboBoxCellController(static_cast<ComboBoxControl*>(m_pWindow.get()));
+}
+
+OUString DbComboBox::GetFormatText(const Reference< css::sdb::XColumn >& _rxField, const Reference< XNumberFormatter >& xFormatter, const Color** /*ppColor*/)
+{
+ const css::uno::Reference<css::beans::XPropertySet> xPS(_rxField, UNO_QUERY);
+ ::dbtools::FormattedColumnValue fmter( xFormatter, xPS );
+
+ return fmter.getFormattedValue();
+}
+
+void DbComboBox::UpdateFromField(const Reference< css::sdb::XColumn >& _rxField, const Reference< XNumberFormatter >& xFormatter)
+{
+ ComboBoxControl* pControl = static_cast<ComboBoxControl*>(m_pWindow.get());
+ pControl->get_widget().set_entry_text(GetFormatText(_rxField, xFormatter));
+}
+
+void DbComboBox::updateFromModel( Reference< XPropertySet > _rxModel )
+{
+ OSL_ENSURE( _rxModel.is() && m_pWindow, "DbComboBox::updateFromModel: invalid call!" );
+
+ OUString sText;
+ _rxModel->getPropertyValue( FM_PROP_TEXT ) >>= sText;
+
+ ComboBoxControl* pControl = static_cast<ComboBoxControl*>(m_pWindow.get());
+ weld::ComboBox& rComboBox = pControl->get_widget();
+
+ OUString sOldActive = rComboBox.get_active_text();
+ rComboBox.set_entry_text(sText);
+ rComboBox.select_entry_region(0, -1);
+
+ if (sOldActive != rComboBox.get_active_text())
+ pControl->TriggerAuxModify();
+}
+
+bool DbComboBox::commitControl()
+{
+ ComboBoxControl* pControl = static_cast<ComboBoxControl*>(m_pWindow.get());
+ weld::ComboBox& rComboBox = pControl->get_widget();
+ OUString aText(rComboBox.get_active_text());
+ m_rColumn.getModel()->setPropertyValue(FM_PROP_TEXT, Any(aText));
+ return true;
+}
+
+
+DbListBox::DbListBox(DbGridColumn& _rColumn)
+ :DbCellControl(_rColumn)
+ ,m_bBound(false)
+{
+ setAlignedController( false );
+
+ doPropertyListening( FM_PROP_STRINGITEMLIST );
+ doPropertyListening( FM_PROP_LINECOUNT );
+}
+
+void DbListBox::_propertyChanged( const css::beans::PropertyChangeEvent& _rEvent )
+{
+ if ( _rEvent.PropertyName == FM_PROP_STRINGITEMLIST )
+ {
+ SetList(_rEvent.NewValue);
+ }
+ else
+ {
+ DbCellControl::_propertyChanged( _rEvent ) ;
+ }
+}
+
+void DbListBox::SetList(const Any& rItems)
+{
+ ListBoxControl* pField = static_cast<ListBoxControl*>(m_pWindow.get());
+
+ weld::ComboBox& rFieldList = pField->get_widget();
+
+ rFieldList.clear();
+ m_bBound = false;
+
+ css::uno::Sequence<OUString> aTest;
+ if (!(rItems >>= aTest))
+ return;
+
+ if (aTest.hasElements())
+ {
+ for (const OUString& rString : std::as_const(aTest))
+ rFieldList.append_text(rString);
+
+ m_rColumn.getModel()->getPropertyValue(FM_PROP_VALUE_SEQ) >>= m_aValueList;
+ m_bBound = m_aValueList.hasElements();
+
+ // tell the grid control that this controller is invalid and has to be re-initialized
+ invalidatedController();
+ }
+}
+
+void DbListBox::Init(BrowserDataWin& rParent, const Reference< XRowSet >& xCursor)
+{
+ m_rColumn.SetAlignment(css::awt::TextAlign::LEFT);
+
+ m_pWindow = VclPtr<ListBoxControl>::Create( &rParent );
+
+ // some initial properties
+ Reference< XPropertySet > xModel( m_rColumn.getModel() );
+ SetList( xModel->getPropertyValue( FM_PROP_STRINGITEMLIST ) );
+ implAdjustGenericFieldSetting( xModel );
+
+ DbCellControl::Init( rParent, xCursor );
+}
+
+void DbListBox::implAdjustGenericFieldSetting( const Reference< XPropertySet >& /*rxModel*/ )
+{
+ // ignore FM_PROP_LINECOUNT
+}
+
+CellControllerRef DbListBox::CreateController() const
+{
+ return new ListBoxCellController(static_cast<ListBoxControl*>(m_pWindow.get()));
+}
+
+OUString DbListBox::GetFormatText(const Reference< css::sdb::XColumn >& _rxField, const Reference< XNumberFormatter >& /*xFormatter*/, const Color** /*ppColor*/)
+{
+ OUString sText;
+ if ( _rxField.is() )
+ {
+ try
+ {
+ sText = _rxField->getString();
+ if ( m_bBound )
+ {
+ sal_Int32 nPos = ::comphelper::findValue( m_aValueList, sText );
+ if ( nPos != -1 )
+ sText = static_cast<svt::ListBoxControl*>(m_pWindow.get())->get_widget().get_text(nPos);
+ else
+ sText.clear();
+ }
+ }
+ catch( const Exception& )
+ {
+ DBG_UNHANDLED_EXCEPTION("svx");
+ }
+ }
+ return sText;
+}
+
+void DbListBox::UpdateFromField(const Reference< css::sdb::XColumn >& _rxField, const Reference< XNumberFormatter >& xFormatter)
+{
+ OUString sFormattedText( GetFormatText( _rxField, xFormatter ) );
+ weld::ComboBox& rComboBox = static_cast<ListBoxControl*>(m_pWindow.get())->get_widget();
+ if (!sFormattedText.isEmpty())
+ rComboBox.set_active_text(sFormattedText);
+ else
+ rComboBox.set_active(-1);
+}
+
+void DbListBox::updateFromModel( Reference< XPropertySet > _rxModel )
+{
+ OSL_ENSURE( _rxModel.is() && m_pWindow, "DbListBox::updateFromModel: invalid call!" );
+
+ Sequence< sal_Int16 > aSelection;
+ _rxModel->getPropertyValue( FM_PROP_SELECT_SEQ ) >>= aSelection;
+
+ sal_Int16 nSelection = -1;
+ if ( aSelection.hasElements() )
+ nSelection = aSelection[ 0 ];
+
+ ListBoxControl* pControl = static_cast<ListBoxControl*>(m_pWindow.get());
+ weld::ComboBox& rComboBox = pControl->get_widget();
+
+ int nOldActive = rComboBox.get_active();
+ if (nSelection >= 0 && nSelection < rComboBox.get_count())
+ rComboBox.set_active(nSelection);
+ else
+ rComboBox.set_active(-1);
+
+ if (nOldActive != rComboBox.get_active())
+ pControl->TriggerAuxModify();
+}
+
+bool DbListBox::commitControl()
+{
+ Any aVal;
+ Sequence<sal_Int16> aSelectSeq;
+ weld::ComboBox& rComboBox = static_cast<ListBoxControl*>(m_pWindow.get())->get_widget();
+ auto nActive = rComboBox.get_active();
+ if (nActive != -1)
+ {
+ aSelectSeq.realloc(1);
+ *aSelectSeq.getArray() = static_cast<sal_Int16>(nActive);
+ }
+ aVal <<= aSelectSeq;
+ m_rColumn.getModel()->setPropertyValue(FM_PROP_SELECT_SEQ, aVal);
+ return true;
+}
+
+DbFilterField::DbFilterField(const Reference< XComponentContext >& rxContext,DbGridColumn& _rColumn)
+ :DbCellControl(_rColumn)
+ ,OSQLParserClient(rxContext)
+ ,m_nControlClass(css::form::FormComponentType::TEXTFIELD)
+ ,m_bFilterList(false)
+ ,m_bFilterListFilled(false)
+{
+
+ setAlignedController( false );
+}
+
+DbFilterField::~DbFilterField()
+{
+ if (m_nControlClass == css::form::FormComponentType::CHECKBOX)
+ static_cast<CheckBoxControl*>(m_pWindow.get())->SetToggleHdl(Link<weld::CheckButton&,void>());
+
+}
+
+void DbFilterField::PaintCell(OutputDevice& rDev, const tools::Rectangle& rRect)
+{
+ static const DrawTextFlags nStyle = DrawTextFlags::Clip | DrawTextFlags::VCenter | DrawTextFlags::Left;
+ switch (m_nControlClass)
+ {
+ case FormComponentType::CHECKBOX:
+ {
+ // center the checkbox within the space available
+ CheckBoxControl* pControl = static_cast<CheckBoxControl*>(m_pPainter.get());
+ Size aBoxSize = pControl->GetBox().get_preferred_size();
+ tools::Rectangle aRect(Point(rRect.Left() + ((rRect.GetWidth() - aBoxSize.Width()) / 2),
+ rRect.Top() + ((rRect.GetHeight() - aBoxSize.Height()) / 2)),
+ aBoxSize);
+
+ DbCellControl::PaintCell(rDev, aRect);
+ break;
+ }
+ case FormComponentType::LISTBOX:
+ rDev.DrawText(rRect, static_cast<ListBoxControl*>(m_pWindow.get())->get_widget().get_active_text(), nStyle);
+ break;
+ default:
+ rDev.DrawText(rRect, m_aText, nStyle);
+ }
+}
+
+void DbFilterField::SetList(const Any& rItems, bool bComboBox)
+{
+ css::uno::Sequence<OUString> aTest;
+ rItems >>= aTest;
+ if (!aTest.hasElements())
+ return;
+
+ if (bComboBox)
+ {
+ ComboBoxControl* pField = static_cast<ComboBoxControl*>(m_pWindow.get());
+ weld::ComboBox& rComboBox = pField->get_widget();
+ for (const OUString& rString : std::as_const(aTest))
+ rComboBox.append_text(rString);
+ }
+ else
+ {
+ ListBoxControl* pField = static_cast<ListBoxControl*>(m_pWindow.get());
+ weld::ComboBox& rFieldBox = pField->get_widget();
+ for (const OUString& rString : std::as_const(aTest))
+ rFieldBox.append_text(rString);
+
+ m_rColumn.getModel()->getPropertyValue(FM_PROP_VALUE_SEQ) >>= m_aValueList;
+ }
+}
+
+void DbFilterField::CreateControl(BrowserDataWin* pParent, const Reference< css::beans::XPropertySet >& xModel)
+{
+ switch (m_nControlClass)
+ {
+ case css::form::FormComponentType::CHECKBOX:
+ m_pWindow = VclPtr<CheckBoxControl>::Create(pParent);
+ m_pWindow->SetPaintTransparent( true );
+ static_cast<CheckBoxControl*>(m_pWindow.get())->SetToggleHdl(LINK(this, DbFilterField, OnToggle));
+
+ m_pPainter = VclPtr<CheckBoxControl>::Create(pParent);
+ m_pPainter->SetPaintTransparent( true );
+ m_pPainter->SetBackground();
+ break;
+ case css::form::FormComponentType::LISTBOX:
+ {
+ m_pWindow = VclPtr<ListBoxControl>::Create(pParent);
+ Any aItems = xModel->getPropertyValue(FM_PROP_STRINGITEMLIST);
+ SetList(aItems, false);
+ } break;
+ case css::form::FormComponentType::COMBOBOX:
+ {
+ m_pWindow = VclPtr<ComboBoxControl>::Create(pParent);
+
+ AllSettings aSettings = m_pWindow->GetSettings();
+ StyleSettings aStyleSettings = aSettings.GetStyleSettings();
+ aStyleSettings.SetSelectionOptions(
+ aStyleSettings.GetSelectionOptions() | SelectionOptions::ShowFirst);
+ aSettings.SetStyleSettings(aStyleSettings);
+ m_pWindow->SetSettings(aSettings, true);
+
+ if (!m_bFilterList)
+ {
+ Any aItems = xModel->getPropertyValue(FM_PROP_STRINGITEMLIST);
+ SetList(aItems, true);
+ }
+
+ } break;
+ default:
+ {
+ m_pWindow = VclPtr<EditControl>::Create(pParent);
+ AllSettings aSettings = m_pWindow->GetSettings();
+ StyleSettings aStyleSettings = aSettings.GetStyleSettings();
+ aStyleSettings.SetSelectionOptions(
+ aStyleSettings.GetSelectionOptions() | SelectionOptions::ShowFirst);
+ aSettings.SetStyleSettings(aStyleSettings);
+ m_pWindow->SetSettings(aSettings, true);
+ }
+ }
+}
+
+void DbFilterField::Init(BrowserDataWin& rParent, const Reference< XRowSet >& xCursor)
+{
+ Reference< css::beans::XPropertySet > xModel(m_rColumn.getModel());
+ m_rColumn.SetAlignment(css::awt::TextAlign::LEFT);
+
+ if (xModel.is())
+ {
+ m_bFilterList = ::comphelper::hasProperty(FM_PROP_FILTERPROPOSAL, xModel) && ::comphelper::getBOOL(xModel->getPropertyValue(FM_PROP_FILTERPROPOSAL));
+ if (m_bFilterList)
+ m_nControlClass = css::form::FormComponentType::COMBOBOX;
+ else
+ {
+ sal_Int16 nClassId = ::comphelper::getINT16(xModel->getPropertyValue(FM_PROP_CLASSID));
+ switch (nClassId)
+ {
+ case FormComponentType::CHECKBOX:
+ case FormComponentType::LISTBOX:
+ case FormComponentType::COMBOBOX:
+ m_nControlClass = nClassId;
+ break;
+ default:
+ if (m_bFilterList)
+ m_nControlClass = FormComponentType::COMBOBOX;
+ else
+ m_nControlClass = FormComponentType::TEXTFIELD;
+ }
+ }
+ }
+
+ CreateControl( &rParent, xModel );
+ DbCellControl::Init( rParent, xCursor );
+
+ // filter cells are never readonly
+ m_pWindow->SetEditableReadOnly(false);
+}
+
+CellControllerRef DbFilterField::CreateController() const
+{
+ CellControllerRef xController;
+ switch (m_nControlClass)
+ {
+ case css::form::FormComponentType::CHECKBOX:
+ xController = new CheckBoxCellController(static_cast<CheckBoxControl*>(m_pWindow.get()));
+ break;
+ case css::form::FormComponentType::LISTBOX:
+ xController = new ListBoxCellController(static_cast<ListBoxControl*>(m_pWindow.get()));
+ break;
+ case css::form::FormComponentType::COMBOBOX:
+ xController = new ComboBoxCellController(static_cast<ComboBoxControl*>(m_pWindow.get()));
+ break;
+ default:
+ if (m_bFilterList)
+ xController = new ComboBoxCellController(static_cast<ComboBoxControl*>(m_pWindow.get()));
+ else
+ xController = new EditCellController(static_cast<EditControlBase*>(m_pWindow.get()));
+ }
+ return xController;
+}
+
+void DbFilterField::updateFromModel( Reference< XPropertySet > _rxModel )
+{
+ OSL_ENSURE( _rxModel.is() && m_pWindow, "DbFilterField::updateFromModel: invalid call!" );
+
+ OSL_FAIL( "DbFilterField::updateFromModel: not implemented yet (how the hell did you reach this?)!" );
+ // TODO: implement this.
+ // remember: updateFromModel should be some kind of opposite of commitControl
+}
+
+bool DbFilterField::commitControl()
+{
+ OUString aText(m_aText);
+ switch (m_nControlClass)
+ {
+ case css::form::FormComponentType::CHECKBOX:
+ return true;
+ case css::form::FormComponentType::LISTBOX:
+ {
+ aText.clear();
+ weld::ComboBox& rComboBox = static_cast<svt::ListBoxControl*>(m_pWindow.get())->get_widget();
+ auto nActive = rComboBox.get_active();
+ if (nActive != -1)
+ {
+ sal_Int16 nPos = static_cast<sal_Int16>(nActive);
+ if ( ( nPos >= 0 ) && ( nPos < m_aValueList.getLength() ) )
+ aText = m_aValueList.getConstArray()[nPos];
+ }
+
+ if (m_aText != aText)
+ {
+ m_aText = aText;
+ m_aCommitLink.Call(*this);
+ }
+ return true;
+ }
+ case css::form::FormComponentType::COMBOBOX:
+ {
+ aText = static_cast<ComboBoxControl*>(m_pWindow.get())->get_widget().get_active_text();
+ break;
+ }
+ default:
+ {
+ aText = static_cast<EditControlBase*>(m_pWindow.get())->get_widget().get_text();
+ break;
+ }
+ }
+
+ if (m_aText != aText)
+ {
+ // check the text with the SQL-Parser
+ OUString aNewText(comphelper::string::stripEnd(aText, ' '));
+ if (!aNewText.isEmpty())
+ {
+ OUString aErrorMsg;
+ Reference< XNumberFormatter > xNumberFormatter(m_rColumn.GetParent().getNumberFormatter());
+
+ std::unique_ptr< OSQLParseNode > pParseNode = predicateTree(aErrorMsg, aNewText,xNumberFormatter, m_rColumn.GetField());
+ if (pParseNode != nullptr)
+ {
+ OUString aPreparedText;
+
+ css::lang::Locale aAppLocale = Application::GetSettings().GetUILanguageTag().getLocale();
+
+ Reference< XRowSet > xDataSourceRowSet(
+ Reference< XInterface >(*m_rColumn.GetParent().getDataSource()), UNO_QUERY);
+ Reference< XConnection > xConnection(getConnection(xDataSourceRowSet));
+
+ pParseNode->parseNodeToPredicateStr(aPreparedText,
+ xConnection,
+ xNumberFormatter,
+ m_rColumn.GetField(),
+ OUString(),
+ aAppLocale,
+ OUString("."),
+ getParseContext());
+ m_aText = aPreparedText;
+ }
+ else
+ {
+
+ SQLException aError;
+ aError.Message = aErrorMsg;
+ displayException(aError, VCLUnoHelper::GetInterface(m_pWindow->GetParent()));
+ // TODO: transport the title
+
+ return false;
+ }
+ }
+ else
+ m_aText = aText;
+
+ m_pWindow->SetText(m_aText);
+ m_aCommitLink.Call(*this);
+ }
+ return true;
+}
+
+
+void DbFilterField::SetText(const OUString& rText)
+{
+ m_aText = rText;
+ switch (m_nControlClass)
+ {
+ case css::form::FormComponentType::CHECKBOX:
+ {
+ TriState eState;
+ if (rText == "1")
+ eState = TRISTATE_TRUE;
+ else if (rText == "0")
+ eState = TRISTATE_FALSE;
+ else
+ eState = TRISTATE_INDET;
+
+ static_cast<CheckBoxControl*>(m_pWindow.get())->SetState(eState);
+ static_cast<CheckBoxControl*>(m_pPainter.get())->SetState(eState);
+ } break;
+ case css::form::FormComponentType::LISTBOX:
+ {
+ sal_Int32 nPos = ::comphelper::findValue(m_aValueList, m_aText);
+ static_cast<ListBoxControl*>(m_pWindow.get())->get_widget().set_active(nPos);
+ } break;
+ case css::form::FormComponentType::COMBOBOX:
+ {
+ static_cast<ComboBoxControl*>(m_pWindow.get())->get_widget().set_entry_text(m_aText);
+ break;
+ }
+ default:
+ {
+ static_cast<EditControlBase*>(m_pWindow.get())->get_widget().set_text(m_aText);
+ break;
+ }
+ }
+
+ // now force a repaint on the window
+ m_rColumn.GetParent().RowModified(0);
+}
+
+
+void DbFilterField::Update()
+{
+ // should we fill the combobox with a filter proposal?
+ if (!m_bFilterList || m_bFilterListFilled)
+ return;
+
+ m_bFilterListFilled = true;
+ Reference< css::beans::XPropertySet > xField = m_rColumn.GetField();
+ if (!xField.is())
+ return;
+
+ OUString aName;
+ xField->getPropertyValue(FM_PROP_NAME) >>= aName;
+
+ // the columnmodel
+ Reference< css::container::XChild > xModelAsChild(m_rColumn.getModel(), UNO_QUERY);
+ // the grid model
+ xModelAsChild.set(xModelAsChild->getParent(),UNO_QUERY);
+ Reference< XRowSet > xForm(xModelAsChild->getParent(), UNO_QUERY);
+ if (!xForm.is())
+ return;
+
+ Reference<XPropertySet> xFormProp(xForm,UNO_QUERY);
+ Reference< XTablesSupplier > xSupTab;
+ xFormProp->getPropertyValue("SingleSelectQueryComposer") >>= xSupTab;
+
+ Reference< XConnection > xConnection(getConnection(xForm));
+ if (!xSupTab.is())
+ return;
+
+ // search the field
+ Reference< XColumnsSupplier > xSupCol(xSupTab,UNO_QUERY);
+ Reference< css::container::XNameAccess > xFieldNames = xSupCol->getColumns();
+ if (!xFieldNames->hasByName(aName))
+ return;
+
+ Reference< css::container::XNameAccess > xTablesNames = xSupTab->getTables();
+ Reference< css::beans::XPropertySet > xComposerFieldAsSet(xFieldNames->getByName(aName),UNO_QUERY);
+
+ if (!xComposerFieldAsSet.is() ||
+ !::comphelper::hasProperty(FM_PROP_TABLENAME, xComposerFieldAsSet) ||
+ !::comphelper::hasProperty(FM_PROP_FIELDSOURCE, xComposerFieldAsSet))
+ return;
+
+ OUString aFieldName;
+ OUString aTableName;
+ xComposerFieldAsSet->getPropertyValue(FM_PROP_FIELDSOURCE) >>= aFieldName;
+ xComposerFieldAsSet->getPropertyValue(FM_PROP_TABLENAME) >>= aTableName;
+
+ // no possibility to create a select statement
+ // looking for the complete table name
+ if (!xTablesNames->hasByName(aTableName))
+ return;
+
+ // build a statement and send as query;
+ // Access to the connection
+ Reference< XStatement > xStatement;
+ Reference< XResultSet > xListCursor;
+ Reference< css::sdb::XColumn > xDataField;
+
+ try
+ {
+ Reference< XDatabaseMetaData > xMeta = xConnection->getMetaData();
+
+ OUString aQuote(xMeta->getIdentifierQuoteString());
+ OUStringBuffer aStatement("SELECT DISTINCT ");
+ aStatement.append(quoteName(aQuote, aName));
+ if (!aFieldName.isEmpty() && aName != aFieldName)
+ {
+ aStatement.append(" AS ");
+ aStatement.append(quoteName(aQuote, aFieldName));
+ }
+
+ aStatement.append(" FROM ");
+
+ Reference< XPropertySet > xTableNameAccess(xTablesNames->getByName(aTableName), UNO_QUERY_THROW);
+ aStatement.append(composeTableNameForSelect(xConnection, xTableNameAccess));
+
+ xStatement = xConnection->createStatement();
+ Reference< css::beans::XPropertySet > xStatementProps(xStatement, UNO_QUERY);
+ xStatementProps->setPropertyValue(FM_PROP_ESCAPE_PROCESSING, Any(true));
+
+ xListCursor = xStatement->executeQuery(aStatement.makeStringAndClear());
+
+ Reference< css::sdbcx::XColumnsSupplier > xSupplyCols(xListCursor, UNO_QUERY);
+ Reference< css::container::XIndexAccess > xFields(xSupplyCols->getColumns(), UNO_QUERY);
+ xDataField.set(xFields->getByIndex(0), css::uno::UNO_QUERY);
+ if (!xDataField.is())
+ return;
+ }
+ catch(const Exception&)
+ {
+ ::comphelper::disposeComponent(xStatement);
+ return;
+ }
+
+ sal_Int16 i = 0;
+ ::std::vector< OUString > aStringList;
+ aStringList.reserve(16);
+ OUString aStr;
+ css::util::Date aNullDate = m_rColumn.GetParent().getNullDate();
+ sal_Int32 nFormatKey = m_rColumn.GetKey();
+ Reference< XNumberFormatter > xFormatter = m_rColumn.GetParent().getNumberFormatter();
+ sal_Int16 nKeyType = ::comphelper::getNumberFormatType(xFormatter->getNumberFormatsSupplier()->getNumberFormats(), nFormatKey);
+
+ while (!xListCursor->isAfterLast() && i++ < SHRT_MAX) // max number of entries
+ {
+ aStr = getFormattedValue(xDataField, xFormatter, aNullDate, nFormatKey, nKeyType);
+ aStringList.push_back(aStr);
+ (void)xListCursor->next();
+ }
+
+ ComboBoxControl* pField = static_cast<ComboBoxControl*>(m_pWindow.get());
+ weld::ComboBox& rComboBox = pField->get_widget();
+ // filling the entries for the combobox
+ for (const auto& rString : aStringList)
+ rComboBox.append_text(rString);
+}
+
+OUString DbFilterField::GetFormatText(const Reference< XColumn >& /*_rxField*/, const Reference< XNumberFormatter >& /*xFormatter*/, const Color** /*ppColor*/)
+{
+ return OUString();
+}
+
+void DbFilterField::UpdateFromField(const Reference< XColumn >& /*_rxField*/, const Reference< XNumberFormatter >& /*xFormatter*/)
+{
+ OSL_FAIL( "DbFilterField::UpdateFromField: cannot update a filter control from a field!" );
+}
+
+IMPL_LINK_NOARG(DbFilterField, OnToggle, weld::CheckButton&, void)
+{
+ TriState eState = static_cast<CheckBoxControl*>(m_pWindow.get())->GetState();
+ OUStringBuffer aTextBuf;
+
+ Reference< XRowSet > xDataSourceRowSet(
+ Reference< XInterface >(*m_rColumn.GetParent().getDataSource()), UNO_QUERY);
+ Reference< XConnection > xConnection(getConnection(xDataSourceRowSet));
+ const sal_Int32 nBooleanComparisonMode = ::dbtools::DatabaseMetaData( xConnection ).getBooleanComparisonMode();
+
+ switch (eState)
+ {
+ case TRISTATE_TRUE:
+ ::dbtools::getBooleanComparisonPredicate(u"", true, nBooleanComparisonMode, aTextBuf);
+ break;
+ case TRISTATE_FALSE:
+ ::dbtools::getBooleanComparisonPredicate(u"", false, nBooleanComparisonMode, aTextBuf);
+ break;
+ case TRISTATE_INDET:
+ break;
+ }
+
+ const OUString aText(aTextBuf.makeStringAndClear());
+
+ if (m_aText != aText)
+ {
+ m_aText = aText;
+ m_aCommitLink.Call(*this);
+ }
+}
+
+FmXGridCell::FmXGridCell( DbGridColumn* pColumn, std::unique_ptr<DbCellControl> _pControl )
+ :OComponentHelper(m_aMutex)
+ ,m_pColumn(pColumn)
+ ,m_pCellControl( std::move(_pControl) )
+ ,m_aWindowListeners( m_aMutex )
+ ,m_aFocusListeners( m_aMutex )
+ ,m_aKeyListeners( m_aMutex )
+ ,m_aMouseListeners( m_aMutex )
+ ,m_aMouseMotionListeners( m_aMutex )
+{
+}
+
+void FmXGridCell::init()
+{
+ svt::ControlBase* pEventWindow( getEventWindow() );
+ if ( pEventWindow )
+ {
+ pEventWindow->SetFocusInHdl(LINK( this, FmXGridCell, OnFocusGained));
+ pEventWindow->SetFocusOutHdl(LINK( this, FmXGridCell, OnFocusLost));
+ pEventWindow->SetMousePressHdl(LINK( this, FmXGridCell, OnMousePress));
+ pEventWindow->SetMouseReleaseHdl(LINK( this, FmXGridCell, OnMouseRelease));
+ pEventWindow->SetMouseMoveHdl(LINK( this, FmXGridCell, OnMouseMove));
+ pEventWindow->SetKeyInputHdl( LINK( this, FmXGridCell, OnKeyInput) );
+ pEventWindow->SetKeyReleaseHdl( LINK( this, FmXGridCell, OnKeyRelease) );
+ }
+}
+
+svt::ControlBase* FmXGridCell::getEventWindow() const
+{
+ if ( m_pCellControl )
+ return &m_pCellControl->GetWindow();
+ return nullptr;
+}
+
+FmXGridCell::~FmXGridCell()
+{
+ if (!OComponentHelper::rBHelper.bDisposed)
+ {
+ acquire();
+ dispose();
+ }
+
+}
+
+void FmXGridCell::SetTextLineColor()
+{
+ if (m_pCellControl)
+ m_pCellControl->SetTextLineColor();
+}
+
+void FmXGridCell::SetTextLineColor(const Color& _rColor)
+{
+ if (m_pCellControl)
+ m_pCellControl->SetTextLineColor(_rColor);
+}
+
+// XTypeProvider
+
+Sequence< Type > SAL_CALL FmXGridCell::getTypes( )
+{
+ Sequence< uno::Type > aTypes = ::comphelper::concatSequences(
+ ::cppu::OComponentHelper::getTypes(),
+ FmXGridCell_Base::getTypes()
+ );
+ if ( m_pCellControl )
+ aTypes = ::comphelper::concatSequences(
+ aTypes,
+ FmXGridCell_WindowBase::getTypes()
+ );
+ return aTypes;
+}
+
+
+IMPLEMENT_GET_IMPLEMENTATION_ID( FmXGridCell )
+
+// OComponentHelper
+
+void FmXGridCell::disposing()
+{
+ lang::EventObject aEvent( *this );
+ m_aWindowListeners.disposeAndClear( aEvent );
+ m_aFocusListeners.disposeAndClear( aEvent );
+ m_aKeyListeners.disposeAndClear( aEvent );
+ m_aMouseListeners.disposeAndClear( aEvent );
+ m_aMouseMotionListeners.disposeAndClear( aEvent );
+
+ OComponentHelper::disposing();
+ m_pColumn = nullptr;
+ m_pCellControl.reset();
+}
+
+
+Any SAL_CALL FmXGridCell::queryAggregation( const css::uno::Type& _rType )
+{
+ Any aReturn = OComponentHelper::queryAggregation( _rType );
+
+ if ( !aReturn.hasValue() )
+ aReturn = FmXGridCell_Base::queryInterface( _rType );
+
+ if ( !aReturn.hasValue() && ( m_pCellControl != nullptr ) )
+ aReturn = FmXGridCell_WindowBase::queryInterface( _rType );
+
+ return aReturn;
+}
+
+// css::awt::XControl
+
+Reference< XInterface > FmXGridCell::getContext()
+{
+ return Reference< XInterface > ();
+}
+
+
+Reference< css::awt::XControlModel > FmXGridCell::getModel()
+{
+ checkDisposed(OComponentHelper::rBHelper.bDisposed);
+ return Reference< css::awt::XControlModel > (m_pColumn->getModel(), UNO_QUERY);
+}
+
+// css::form::XBoundControl
+
+sal_Bool FmXGridCell::getLock()
+{
+ checkDisposed(OComponentHelper::rBHelper.bDisposed);
+ return m_pColumn->isLocked();
+}
+
+
+void FmXGridCell::setLock(sal_Bool _bLock)
+{
+ checkDisposed(OComponentHelper::rBHelper.bDisposed);
+ if (getLock() == _bLock)
+ return;
+ else
+ {
+ ::osl::MutexGuard aGuard(m_aMutex);
+ m_pColumn->setLock(_bLock);
+ }
+}
+
+
+void SAL_CALL FmXGridCell::setPosSize( ::sal_Int32, ::sal_Int32, ::sal_Int32, ::sal_Int32, ::sal_Int16 )
+{
+ OSL_FAIL( "FmXGridCell::setPosSize: not implemented" );
+ // not allowed to tamper with this for a grid cell
+}
+
+
+awt::Rectangle SAL_CALL FmXGridCell::getPosSize( )
+{
+ OSL_FAIL( "FmXGridCell::getPosSize: not implemented" );
+ return awt::Rectangle();
+}
+
+
+void SAL_CALL FmXGridCell::setVisible( sal_Bool )
+{
+ OSL_FAIL( "FmXGridCell::setVisible: not implemented" );
+ // not allowed to tamper with this for a grid cell
+}
+
+
+void SAL_CALL FmXGridCell::setEnable( sal_Bool )
+{
+ OSL_FAIL( "FmXGridCell::setEnable: not implemented" );
+ // not allowed to tamper with this for a grid cell
+}
+
+
+void SAL_CALL FmXGridCell::setFocus( )
+{
+ OSL_FAIL( "FmXGridCell::setFocus: not implemented" );
+ // not allowed to tamper with this for a grid cell
+}
+
+
+void SAL_CALL FmXGridCell::addWindowListener( const Reference< awt::XWindowListener >& _rxListener )
+{
+ checkDisposed(OComponentHelper::rBHelper.bDisposed);
+ m_aWindowListeners.addInterface( _rxListener );
+}
+
+
+void SAL_CALL FmXGridCell::removeWindowListener( const Reference< awt::XWindowListener >& _rxListener )
+{
+ checkDisposed(OComponentHelper::rBHelper.bDisposed);
+ m_aWindowListeners.removeInterface( _rxListener );
+}
+
+
+void SAL_CALL FmXGridCell::addFocusListener( const Reference< awt::XFocusListener >& _rxListener )
+{
+ checkDisposed(OComponentHelper::rBHelper.bDisposed);
+ m_aFocusListeners.addInterface( _rxListener );
+}
+
+
+void SAL_CALL FmXGridCell::removeFocusListener( const Reference< awt::XFocusListener >& _rxListener )
+{
+ checkDisposed(OComponentHelper::rBHelper.bDisposed);
+ m_aFocusListeners.removeInterface( _rxListener );
+}
+
+
+void SAL_CALL FmXGridCell::addKeyListener( const Reference< awt::XKeyListener >& _rxListener )
+{
+ checkDisposed(OComponentHelper::rBHelper.bDisposed);
+ m_aKeyListeners.addInterface( _rxListener );
+}
+
+
+void SAL_CALL FmXGridCell::removeKeyListener( const Reference< awt::XKeyListener >& _rxListener )
+{
+ checkDisposed(OComponentHelper::rBHelper.bDisposed);
+ m_aKeyListeners.removeInterface( _rxListener );
+}
+
+
+void SAL_CALL FmXGridCell::addMouseListener( const Reference< awt::XMouseListener >& _rxListener )
+{
+ checkDisposed(OComponentHelper::rBHelper.bDisposed);
+ m_aMouseListeners.addInterface( _rxListener );
+}
+
+
+void SAL_CALL FmXGridCell::removeMouseListener( const Reference< awt::XMouseListener >& _rxListener )
+{
+ checkDisposed(OComponentHelper::rBHelper.bDisposed);
+ m_aMouseListeners.removeInterface( _rxListener );
+}
+
+
+void SAL_CALL FmXGridCell::addMouseMotionListener( const Reference< awt::XMouseMotionListener >& _rxListener )
+{
+ checkDisposed(OComponentHelper::rBHelper.bDisposed);
+ m_aMouseMotionListeners.addInterface( _rxListener );
+}
+
+
+void SAL_CALL FmXGridCell::removeMouseMotionListener( const Reference< awt::XMouseMotionListener >& _rxListener )
+{
+ checkDisposed(OComponentHelper::rBHelper.bDisposed);
+ m_aMouseMotionListeners.removeInterface( _rxListener );
+}
+
+void SAL_CALL FmXGridCell::addPaintListener( const Reference< awt::XPaintListener >& )
+{
+ OSL_FAIL( "FmXGridCell::addPaintListener: not implemented" );
+}
+
+void SAL_CALL FmXGridCell::removePaintListener( const Reference< awt::XPaintListener >& )
+{
+ OSL_FAIL( "FmXGridCell::removePaintListener: not implemented" );
+}
+
+void FmXGridCell::onFocusGained( const awt::FocusEvent& _rEvent )
+{
+ checkDisposed(OComponentHelper::rBHelper.bDisposed);
+ m_aFocusListeners.notifyEach( &awt::XFocusListener::focusGained, _rEvent );
+}
+
+void FmXGridCell::onFocusLost( const awt::FocusEvent& _rEvent )
+{
+ checkDisposed(OComponentHelper::rBHelper.bDisposed);
+ m_aFocusListeners.notifyEach( &awt::XFocusListener::focusLost, _rEvent );
+}
+
+IMPL_LINK_NOARG(FmXGridCell, OnFocusGained, LinkParamNone*, void)
+{
+ if (!m_aFocusListeners.getLength())
+ return;
+
+ awt::FocusEvent aEvent;
+ aEvent.Source = *this;
+ aEvent.Temporary = false;
+
+ onFocusGained(aEvent);
+}
+
+IMPL_LINK_NOARG(FmXGridCell, OnFocusLost, LinkParamNone*, void)
+{
+ if (!m_aFocusListeners.getLength())
+ return;
+
+ awt::FocusEvent aEvent;
+ aEvent.Source = *this;
+ aEvent.Temporary = false;
+
+ onFocusLost(aEvent);
+}
+
+IMPL_LINK(FmXGridCell, OnMousePress, const MouseEvent&, rEventData, void)
+{
+ if (!m_aMouseListeners.getLength())
+ return;
+
+ awt::MouseEvent aEvent(VCLUnoHelper::createMouseEvent(rEventData, *this));
+ m_aMouseListeners.notifyEach(&awt::XMouseListener::mousePressed, aEvent);
+}
+
+IMPL_LINK(FmXGridCell, OnMouseRelease, const MouseEvent&, rEventData, void)
+{
+ if (!m_aMouseListeners.getLength())
+ return;
+
+ awt::MouseEvent aEvent(VCLUnoHelper::createMouseEvent(rEventData, *this));
+ m_aMouseListeners.notifyEach(&awt::XMouseListener::mouseReleased, aEvent);
+}
+
+IMPL_LINK(FmXGridCell, OnMouseMove, const MouseEvent&, rMouseEvent, void)
+{
+ if ( rMouseEvent.IsEnterWindow() || rMouseEvent.IsLeaveWindow() )
+ {
+ if ( m_aMouseListeners.getLength() != 0 )
+ {
+ awt::MouseEvent aEvent( VCLUnoHelper::createMouseEvent( rMouseEvent, *this ) );
+ m_aMouseListeners.notifyEach( rMouseEvent.IsEnterWindow() ? &awt::XMouseListener::mouseEntered: &awt::XMouseListener::mouseExited, aEvent );
+ }
+ }
+ else if ( !rMouseEvent.IsEnterWindow() && !rMouseEvent.IsLeaveWindow() )
+ {
+ if ( m_aMouseMotionListeners.getLength() != 0 )
+ {
+ awt::MouseEvent aEvent( VCLUnoHelper::createMouseEvent( rMouseEvent, *this ) );
+ aEvent.ClickCount = 0;
+ const bool bSimpleMove = bool( rMouseEvent.GetMode() & MouseEventModifiers::SIMPLEMOVE );
+ m_aMouseMotionListeners.notifyEach( bSimpleMove ? &awt::XMouseMotionListener::mouseMoved: &awt::XMouseMotionListener::mouseDragged, aEvent );
+ }
+ }
+}
+
+IMPL_LINK(FmXGridCell, OnKeyInput, const KeyEvent&, rEventData, void)
+{
+ if (!m_aKeyListeners.getLength())
+ return;
+
+ awt::KeyEvent aEvent(VCLUnoHelper::createKeyEvent(rEventData, *this));
+ m_aKeyListeners.notifyEach(&awt::XKeyListener::keyPressed, aEvent);
+}
+
+IMPL_LINK(FmXGridCell, OnKeyRelease, const KeyEvent&, rEventData, void)
+{
+ if (!m_aKeyListeners.getLength())
+ return;
+
+ awt::KeyEvent aEvent(VCLUnoHelper::createKeyEvent(rEventData, *this));
+ m_aKeyListeners.notifyEach(&awt::XKeyListener::keyReleased, aEvent);
+}
+
+void FmXDataCell::PaintFieldToCell(OutputDevice& rDev, const tools::Rectangle& rRect,
+ const Reference< css::sdb::XColumn >& _rxField,
+ const Reference< XNumberFormatter >& xFormatter)
+{
+ m_pCellControl->PaintFieldToCell( rDev, rRect, _rxField, xFormatter );
+}
+
+void FmXDataCell::UpdateFromColumn()
+{
+ Reference< css::sdb::XColumn > xField(m_pColumn->GetCurrentFieldValue());
+ if (xField.is())
+ m_pCellControl->UpdateFromField(xField, m_pColumn->GetParent().getNumberFormatter());
+}
+
+FmXTextCell::FmXTextCell( DbGridColumn* pColumn, std::unique_ptr<DbCellControl> pControl )
+ :FmXDataCell( pColumn, std::move(pControl) )
+ ,m_bIsMultiLineText(false)
+{
+}
+
+void FmXTextCell::PaintFieldToCell(OutputDevice& rDev,
+ const tools::Rectangle& rRect,
+ const Reference< css::sdb::XColumn >& _rxField,
+ const Reference< XNumberFormatter >& xFormatter)
+{
+ DrawTextFlags nStyle = DrawTextFlags::Clip;
+ if ( ( rDev.GetOutDevType() == OUTDEV_WINDOW ) && !rDev.GetOwnerWindow()->IsEnabled() )
+ nStyle |= DrawTextFlags::Disable;
+
+ switch (m_pColumn->GetAlignment())
+ {
+ case css::awt::TextAlign::RIGHT:
+ nStyle |= DrawTextFlags::Right;
+ break;
+ case css::awt::TextAlign::CENTER:
+ nStyle |= DrawTextFlags::Center;
+ break;
+ default:
+ nStyle |= DrawTextFlags::Left;
+ }
+
+ if (!m_bIsMultiLineText)
+ nStyle |= DrawTextFlags::VCenter;
+ else
+ nStyle |= DrawTextFlags::Top | DrawTextFlags::MultiLine | DrawTextFlags::WordBreak;
+
+ try
+ {
+ const Color* pColor = nullptr;
+ OUString aText = GetText(_rxField, xFormatter, &pColor);
+ if (pColor != nullptr)
+ {
+ Color aOldTextColor( rDev.GetTextColor() );
+ rDev.SetTextColor( *pColor );
+ rDev.DrawText(rRect, aText, nStyle);
+ rDev.SetTextColor( aOldTextColor );
+ }
+ else
+ rDev.DrawText(rRect, aText, nStyle);
+ }
+ catch (const Exception&)
+ {
+ TOOLS_WARN_EXCEPTION("svx.fmcomp", "PaintFieldToCell");
+ }
+}
+
+FmXEditCell::FmXEditCell( DbGridColumn* pColumn, std::unique_ptr<DbCellControl> pControl )
+ :FmXTextCell( pColumn, std::move(pControl) )
+ ,m_aTextListeners(m_aMutex)
+ ,m_aChangeListeners( m_aMutex )
+ ,m_pEditImplementation( nullptr )
+ ,m_bOwnEditImplementation( false )
+{
+
+ DbTextField* pTextField = dynamic_cast<DbTextField*>( m_pCellControl.get() );
+ if ( pTextField )
+ {
+
+ m_pEditImplementation = pTextField->GetEditImplementation();
+ m_bIsMultiLineText = pTextField->IsMultiLineEdit();
+ }
+ else
+ {
+ m_pEditImplementation = new EntryImplementation(static_cast<EditControlBase&>(m_pCellControl->GetWindow()));
+ m_bOwnEditImplementation = true;
+ }
+ m_pEditImplementation->SetAuxModifyHdl(LINK(this, FmXEditCell, ModifyHdl));
+}
+
+FmXEditCell::~FmXEditCell()
+{
+ if (!OComponentHelper::rBHelper.bDisposed)
+ {
+ acquire();
+ dispose();
+ }
+}
+
+// OComponentHelper
+void FmXEditCell::disposing()
+{
+ css::lang::EventObject aEvt(*this);
+ m_aTextListeners.disposeAndClear(aEvt);
+ m_aChangeListeners.disposeAndClear(aEvt);
+
+ if ( m_bOwnEditImplementation )
+ delete m_pEditImplementation;
+ m_pEditImplementation = nullptr;
+
+ FmXDataCell::disposing();
+}
+
+Any SAL_CALL FmXEditCell::queryAggregation( const css::uno::Type& _rType )
+{
+ Any aReturn = FmXTextCell::queryAggregation( _rType );
+
+ if ( !aReturn.hasValue() )
+ aReturn = FmXEditCell_Base::queryInterface( _rType );
+
+ return aReturn;
+}
+
+Sequence< css::uno::Type > SAL_CALL FmXEditCell::getTypes( )
+{
+ return ::comphelper::concatSequences(
+ FmXTextCell::getTypes(),
+ FmXEditCell_Base::getTypes()
+ );
+}
+
+IMPLEMENT_GET_IMPLEMENTATION_ID( FmXEditCell )
+
+// css::awt::XTextComponent
+void SAL_CALL FmXEditCell::addTextListener(const Reference< css::awt::XTextListener >& l)
+{
+ m_aTextListeners.addInterface( l );
+}
+
+
+void SAL_CALL FmXEditCell::removeTextListener(const Reference< css::awt::XTextListener >& l)
+{
+ m_aTextListeners.removeInterface( l );
+}
+
+void SAL_CALL FmXEditCell::setText( const OUString& aText )
+{
+ ::osl::MutexGuard aGuard( m_aMutex );
+
+ if ( m_pEditImplementation )
+ {
+ m_pEditImplementation->SetText( aText );
+
+ // In Java, a textChanged is fired as well; not in VCL.
+ // css::awt::Toolkit must be Java-compliant...
+ onTextChanged();
+ }
+}
+
+void SAL_CALL FmXEditCell::insertText(const css::awt::Selection& rSel, const OUString& aText)
+{
+ ::osl::MutexGuard aGuard( m_aMutex );
+
+ if ( m_pEditImplementation )
+ {
+ m_pEditImplementation->SetSelection( Selection( rSel.Min, rSel.Max ) );
+ m_pEditImplementation->ReplaceSelected( aText );
+ }
+}
+
+OUString SAL_CALL FmXEditCell::getText()
+{
+ ::osl::MutexGuard aGuard( m_aMutex );
+
+ OUString aText;
+ if ( m_pEditImplementation )
+ {
+ if ( m_pEditImplementation->GetControl().IsVisible() && m_pColumn->GetParent().getDisplaySynchron())
+ {
+ // if the display isn't sync with the cursor we can't ask the edit field
+ LineEnd eLineEndFormat = getModelLineEndSetting( m_pColumn->getModel() );
+ aText = m_pEditImplementation->GetText( eLineEndFormat );
+ }
+ else
+ {
+ Reference< css::sdb::XColumn > xField(m_pColumn->GetCurrentFieldValue());
+ if (xField.is())
+ aText = GetText(xField, m_pColumn->GetParent().getNumberFormatter());
+ }
+ }
+ return aText;
+}
+
+OUString SAL_CALL FmXEditCell::getSelectedText()
+{
+ ::osl::MutexGuard aGuard( m_aMutex );
+
+ OUString aText;
+ if ( m_pEditImplementation )
+ {
+ LineEnd eLineEndFormat = m_pColumn ? getModelLineEndSetting( m_pColumn->getModel() ) : LINEEND_LF;
+ aText = m_pEditImplementation->GetSelected( eLineEndFormat );
+ }
+ return aText;
+}
+
+void SAL_CALL FmXEditCell::setSelection( const css::awt::Selection& aSelection )
+{
+ ::osl::MutexGuard aGuard( m_aMutex );
+
+ if ( m_pEditImplementation )
+ m_pEditImplementation->SetSelection( Selection( aSelection.Min, aSelection.Max ) );
+}
+
+css::awt::Selection SAL_CALL FmXEditCell::getSelection()
+{
+ ::osl::MutexGuard aGuard( m_aMutex );
+
+ Selection aSel;
+ if ( m_pEditImplementation )
+ aSel = m_pEditImplementation->GetSelection();
+
+ return css::awt::Selection(aSel.Min(), aSel.Max());
+}
+
+sal_Bool SAL_CALL FmXEditCell::isEditable()
+{
+ ::osl::MutexGuard aGuard( m_aMutex );
+
+ return m_pEditImplementation && !m_pEditImplementation->IsReadOnly() && m_pEditImplementation->GetControl().IsEnabled();
+}
+
+void SAL_CALL FmXEditCell::setEditable( sal_Bool bEditable )
+{
+ ::osl::MutexGuard aGuard( m_aMutex );
+
+ if ( m_pEditImplementation )
+ m_pEditImplementation->SetReadOnly( !bEditable );
+}
+
+sal_Int16 SAL_CALL FmXEditCell::getMaxTextLen()
+{
+ ::osl::MutexGuard aGuard( m_aMutex );
+
+ return m_pEditImplementation ? m_pEditImplementation->GetMaxTextLen() : 0;
+}
+
+void SAL_CALL FmXEditCell::setMaxTextLen( sal_Int16 nLen )
+{
+ ::osl::MutexGuard aGuard( m_aMutex );
+
+ if ( m_pEditImplementation )
+ m_pEditImplementation->SetMaxTextLen( nLen );
+}
+
+void SAL_CALL FmXEditCell::addChangeListener( const Reference< form::XChangeListener >& Listener )
+{
+ m_aChangeListeners.addInterface( Listener );
+}
+
+void SAL_CALL FmXEditCell::removeChangeListener( const Reference< form::XChangeListener >& Listener )
+{
+ m_aChangeListeners.removeInterface( Listener );
+}
+
+void FmXEditCell::onTextChanged()
+{
+ css::awt::TextEvent aEvent;
+ aEvent.Source = *this;
+ m_aTextListeners.notifyEach( &awt::XTextListener::textChanged, aEvent );
+}
+
+void FmXEditCell::onFocusGained( const awt::FocusEvent& _rEvent )
+{
+ FmXTextCell::onFocusGained( _rEvent );
+ m_sValueOnEnter = getText();
+}
+
+void FmXEditCell::onFocusLost( const awt::FocusEvent& _rEvent )
+{
+ FmXTextCell::onFocusLost( _rEvent );
+
+ if ( getText() != m_sValueOnEnter )
+ {
+ lang::EventObject aEvent( *this );
+ m_aChangeListeners.notifyEach( &XChangeListener::changed, aEvent );
+ }
+}
+
+IMPL_LINK_NOARG(FmXEditCell, ModifyHdl, LinkParamNone*, void)
+{
+ if (m_aTextListeners.getLength())
+ onTextChanged();
+}
+
+FmXCheckBoxCell::FmXCheckBoxCell( DbGridColumn* pColumn, std::unique_ptr<DbCellControl> pControl )
+ :FmXDataCell( pColumn, std::move(pControl) )
+ ,m_aItemListeners(m_aMutex)
+ ,m_aActionListeners( m_aMutex )
+ ,m_pBox( & static_cast< CheckBoxControl& >( m_pCellControl->GetWindow() ) )
+{
+ m_pBox->SetAuxModifyHdl(LINK(this, FmXCheckBoxCell, ModifyHdl));
+}
+
+FmXCheckBoxCell::~FmXCheckBoxCell()
+{
+ if (!OComponentHelper::rBHelper.bDisposed)
+ {
+ acquire();
+ dispose();
+ }
+}
+
+// OComponentHelper
+void FmXCheckBoxCell::disposing()
+{
+ css::lang::EventObject aEvt(*this);
+ m_aItemListeners.disposeAndClear(aEvt);
+ m_aActionListeners.disposeAndClear(aEvt);
+
+ m_pBox->SetToggleHdl(Link<weld::CheckButton&,void>());
+ m_pBox = nullptr;
+
+ FmXDataCell::disposing();
+}
+
+
+Any SAL_CALL FmXCheckBoxCell::queryAggregation( const css::uno::Type& _rType )
+{
+ Any aReturn = FmXDataCell::queryAggregation( _rType );
+
+ if ( !aReturn.hasValue() )
+ aReturn = FmXCheckBoxCell_Base::queryInterface( _rType );
+
+ return aReturn;
+}
+
+
+Sequence< css::uno::Type > SAL_CALL FmXCheckBoxCell::getTypes( )
+{
+ return ::comphelper::concatSequences(
+ FmXDataCell::getTypes(),
+ FmXCheckBoxCell_Base::getTypes()
+ );
+}
+
+
+IMPLEMENT_GET_IMPLEMENTATION_ID( FmXCheckBoxCell )
+
+void SAL_CALL FmXCheckBoxCell::addItemListener( const Reference< css::awt::XItemListener >& l )
+{
+ m_aItemListeners.addInterface( l );
+}
+
+void SAL_CALL FmXCheckBoxCell::removeItemListener( const Reference< css::awt::XItemListener >& l )
+{
+ m_aItemListeners.removeInterface( l );
+}
+
+void SAL_CALL FmXCheckBoxCell::setState( sal_Int16 n )
+{
+ ::osl::MutexGuard aGuard( m_aMutex );
+
+ if (m_pBox)
+ {
+ UpdateFromColumn();
+ m_pBox->SetState( static_cast<TriState>(n) );
+ }
+}
+
+sal_Int16 SAL_CALL FmXCheckBoxCell::getState()
+{
+ ::osl::MutexGuard aGuard( m_aMutex );
+
+ if (m_pBox)
+ {
+ UpdateFromColumn();
+ return static_cast<sal_Int16>(m_pBox->GetState());
+ }
+ return TRISTATE_INDET;
+}
+
+void SAL_CALL FmXCheckBoxCell::enableTriState(sal_Bool b)
+{
+ ::osl::MutexGuard aGuard( m_aMutex );
+
+ if (m_pBox)
+ m_pBox->EnableTriState( b );
+}
+
+void SAL_CALL FmXCheckBoxCell::addActionListener( const Reference< awt::XActionListener >& Listener )
+{
+ m_aActionListeners.addInterface( Listener );
+}
+
+
+void SAL_CALL FmXCheckBoxCell::removeActionListener( const Reference< awt::XActionListener >& Listener )
+{
+ m_aActionListeners.removeInterface( Listener );
+}
+
+void SAL_CALL FmXCheckBoxCell::setLabel( const OUString& Label )
+{
+ SolarMutexGuard aGuard;
+ if ( m_pColumn )
+ {
+ DbGridControl& rGrid( m_pColumn->GetParent() );
+ rGrid.SetColumnTitle( rGrid.GetColumnId( m_pColumn->GetFieldPos() ), Label );
+ }
+}
+
+void SAL_CALL FmXCheckBoxCell::setActionCommand( const OUString& Command )
+{
+ m_aActionCommand = Command;
+}
+
+IMPL_LINK_NOARG(FmXCheckBoxCell, ModifyHdl, LinkParamNone*, void)
+{
+ // check boxes are to be committed immediately (this holds for ordinary check box controls in
+ // documents, and this must hold for check boxes in grid columns, too
+ // 91210 - 22.08.2001 - frank.schoenheit@sun.com
+ m_pCellControl->Commit();
+
+ Reference< XWindow > xKeepAlive( this );
+ if ( m_aItemListeners.getLength() && m_pBox )
+ {
+ awt::ItemEvent aEvent;
+ aEvent.Source = *this;
+ aEvent.Highlighted = 0;
+ aEvent.Selected = m_pBox->GetState();
+ m_aItemListeners.notifyEach( &awt::XItemListener::itemStateChanged, aEvent );
+ }
+ if ( m_aActionListeners.getLength() )
+ {
+ awt::ActionEvent aEvent;
+ aEvent.Source = *this;
+ aEvent.ActionCommand = m_aActionCommand;
+ m_aActionListeners.notifyEach( &awt::XActionListener::actionPerformed, aEvent );
+ }
+}
+
+FmXListBoxCell::FmXListBoxCell(DbGridColumn* pColumn, std::unique_ptr<DbCellControl> pControl)
+ : FmXTextCell(pColumn, std::move(pControl))
+ , m_aItemListeners(m_aMutex)
+ , m_aActionListeners(m_aMutex)
+ , m_pBox(&static_cast<svt::ListBoxControl&>(m_pCellControl->GetWindow()))
+ , m_nLines(Application::GetSettings().GetStyleSettings().GetListBoxMaximumLineCount())
+ , m_bMulti(false)
+{
+ m_pBox->SetAuxModifyHdl(LINK(this, FmXListBoxCell, ChangedHdl));
+}
+
+FmXListBoxCell::~FmXListBoxCell()
+{
+ if (!OComponentHelper::rBHelper.bDisposed)
+ {
+ acquire();
+ dispose();
+ }
+}
+
+// OComponentHelper
+void FmXListBoxCell::disposing()
+{
+ css::lang::EventObject aEvt(*this);
+ m_aItemListeners.disposeAndClear(aEvt);
+ m_aActionListeners.disposeAndClear(aEvt);
+
+ m_pBox->SetAuxModifyHdl(Link<bool,void>());
+ m_pBox = nullptr;
+
+ FmXTextCell::disposing();
+}
+
+Any SAL_CALL FmXListBoxCell::queryAggregation( const css::uno::Type& _rType )
+{
+ Any aReturn = FmXTextCell::queryAggregation(_rType);
+
+ if ( !aReturn.hasValue() )
+ aReturn = FmXListBoxCell_Base::queryInterface( _rType );
+
+ return aReturn;
+}
+
+Sequence< css::uno::Type > SAL_CALL FmXListBoxCell::getTypes( )
+{
+ return ::comphelper::concatSequences(
+ FmXTextCell::getTypes(),
+ FmXListBoxCell_Base::getTypes()
+ );
+}
+
+IMPLEMENT_GET_IMPLEMENTATION_ID( FmXListBoxCell )
+
+void SAL_CALL FmXListBoxCell::addItemListener(const Reference< css::awt::XItemListener >& l)
+{
+ m_aItemListeners.addInterface( l );
+}
+
+void SAL_CALL FmXListBoxCell::removeItemListener(const Reference< css::awt::XItemListener >& l)
+{
+ m_aItemListeners.removeInterface( l );
+}
+
+void SAL_CALL FmXListBoxCell::addActionListener(const Reference< css::awt::XActionListener >& l)
+{
+ m_aActionListeners.addInterface( l );
+}
+
+void SAL_CALL FmXListBoxCell::removeActionListener(const Reference< css::awt::XActionListener >& l)
+{
+ m_aActionListeners.removeInterface( l );
+}
+
+void SAL_CALL FmXListBoxCell::addItem(const OUString& aItem, sal_Int16 nPos)
+{
+ ::osl::MutexGuard aGuard( m_aMutex );
+ if (m_pBox)
+ {
+ weld::ComboBox& rBox = m_pBox->get_widget();
+ rBox.insert_text(nPos, aItem);
+ }
+}
+
+void SAL_CALL FmXListBoxCell::addItems(const css::uno::Sequence<OUString>& aItems, sal_Int16 nPos)
+{
+ ::osl::MutexGuard aGuard( m_aMutex );
+ if (m_pBox)
+ {
+ weld::ComboBox& rBox = m_pBox->get_widget();
+ sal_uInt16 nP = nPos;
+ for ( const auto& rItem : aItems )
+ {
+ rBox.insert_text(nP, rItem);
+ if ( nPos != -1 ) // Not if 0xFFFF, because LIST_APPEND
+ nP++;
+ }
+ }
+}
+
+void SAL_CALL FmXListBoxCell::removeItems(sal_Int16 nPos, sal_Int16 nCount)
+{
+ ::osl::MutexGuard aGuard( m_aMutex );
+ if ( m_pBox )
+ {
+ weld::ComboBox& rBox = m_pBox->get_widget();
+ for ( sal_uInt16 n = nCount; n; )
+ rBox.remove( nPos + (--n) );
+ }
+}
+
+sal_Int16 SAL_CALL FmXListBoxCell::getItemCount()
+{
+ ::osl::MutexGuard aGuard( m_aMutex );
+ if (!m_pBox)
+ return 0;
+ weld::ComboBox& rBox = m_pBox->get_widget();
+ return rBox.get_count();
+}
+
+OUString SAL_CALL FmXListBoxCell::getItem(sal_Int16 nPos)
+{
+ ::osl::MutexGuard aGuard( m_aMutex );
+ if (!m_pBox)
+ return OUString();
+ weld::ComboBox& rBox = m_pBox->get_widget();
+ return rBox.get_text(nPos);
+}
+
+css::uno::Sequence<OUString> SAL_CALL FmXListBoxCell::getItems()
+{
+ ::osl::MutexGuard aGuard( m_aMutex );
+
+ css::uno::Sequence<OUString> aSeq;
+ if (m_pBox)
+ {
+ weld::ComboBox& rBox = m_pBox->get_widget();
+ const sal_Int32 nEntries = rBox.get_count();
+ aSeq = css::uno::Sequence<OUString>( nEntries );
+ for ( sal_Int32 n = nEntries; n; )
+ {
+ --n;
+ aSeq.getArray()[n] = rBox.get_text( n );
+ }
+ }
+ return aSeq;
+}
+
+sal_Int16 SAL_CALL FmXListBoxCell::getSelectedItemPos()
+{
+ ::osl::MutexGuard aGuard( m_aMutex );
+ if (m_pBox)
+ {
+ UpdateFromColumn();
+ weld::ComboBox& rBox = m_pBox->get_widget();
+ sal_Int32 nPos = rBox.get_active();
+ if (nPos > SHRT_MAX || nPos < SHRT_MIN)
+ throw std::out_of_range("awt::XListBox::getSelectedItemPos can only return a short");
+ return nPos;
+ }
+ return 0;
+}
+
+Sequence< sal_Int16 > SAL_CALL FmXListBoxCell::getSelectedItemsPos()
+{
+ ::osl::MutexGuard aGuard( m_aMutex );
+
+ if (m_pBox)
+ {
+ UpdateFromColumn();
+ weld::ComboBox& rBox = m_pBox->get_widget();
+ auto nActive = rBox.get_active();
+ if (nActive != -1)
+ {
+ return { o3tl::narrowing<short>(nActive) };
+ }
+ }
+ return {};
+}
+
+OUString SAL_CALL FmXListBoxCell::getSelectedItem()
+{
+ ::osl::MutexGuard aGuard( m_aMutex );
+
+ OUString aItem;
+
+ if (m_pBox)
+ {
+ UpdateFromColumn();
+ weld::ComboBox& rBox = m_pBox->get_widget();
+ aItem = rBox.get_active_text();
+ }
+
+ return aItem;
+}
+
+css::uno::Sequence<OUString> SAL_CALL FmXListBoxCell::getSelectedItems()
+{
+ ::osl::MutexGuard aGuard( m_aMutex );
+
+ if (m_pBox)
+ {
+ UpdateFromColumn();
+ weld::ComboBox& rBox = m_pBox->get_widget();
+ auto nActive = rBox.get_active();
+ if (nActive != -1)
+ {
+ return { rBox.get_text(nActive) };
+ }
+ }
+ return {};
+}
+
+void SAL_CALL FmXListBoxCell::selectItemPos(sal_Int16 nPos, sal_Bool bSelect)
+{
+ ::osl::MutexGuard aGuard( m_aMutex );
+
+ if (m_pBox)
+ {
+ weld::ComboBox& rBox = m_pBox->get_widget();
+ if (bSelect)
+ rBox.set_active(nPos);
+ else if (nPos == rBox.get_active())
+ rBox.set_active(-1);
+ }
+}
+
+void SAL_CALL FmXListBoxCell::selectItemsPos(const Sequence< sal_Int16 >& aPositions, sal_Bool bSelect)
+{
+ ::osl::MutexGuard aGuard( m_aMutex );
+
+ if (m_pBox)
+ {
+ weld::ComboBox& rBox = m_pBox->get_widget();
+ for ( sal_uInt16 n = static_cast<sal_uInt16>(aPositions.getLength()); n; )
+ {
+ auto nPos = static_cast<sal_uInt16>(aPositions.getConstArray()[--n]);
+ if (bSelect)
+ rBox.set_active(nPos);
+ else if (nPos == rBox.get_active())
+ rBox.set_active(-1);
+ }
+ }
+}
+
+void SAL_CALL FmXListBoxCell::selectItem(const OUString& aItem, sal_Bool bSelect)
+{
+ ::osl::MutexGuard aGuard( m_aMutex );
+
+ if (m_pBox)
+ {
+ weld::ComboBox& rBox = m_pBox->get_widget();
+ auto nPos = rBox.find_text(aItem);
+ if (bSelect)
+ rBox.set_active(nPos);
+ else if (nPos == rBox.get_active())
+ rBox.set_active(-1);
+ }
+}
+
+sal_Bool SAL_CALL FmXListBoxCell::isMutipleMode()
+{
+ ::osl::MutexGuard aGuard( m_aMutex );
+
+ return m_bMulti;
+}
+
+void SAL_CALL FmXListBoxCell::setMultipleMode(sal_Bool bMulti)
+{
+ ::osl::MutexGuard aGuard( m_aMutex );
+
+ m_bMulti = bMulti;
+}
+
+sal_Int16 SAL_CALL FmXListBoxCell::getDropDownLineCount()
+{
+ ::osl::MutexGuard aGuard( m_aMutex );
+ return m_nLines;
+}
+
+void SAL_CALL FmXListBoxCell::setDropDownLineCount(sal_Int16 nLines)
+{
+ ::osl::MutexGuard aGuard( m_aMutex );
+
+ m_nLines = nLines; // just store it to return it
+}
+
+void SAL_CALL FmXListBoxCell::makeVisible(sal_Int16 /*nEntry*/)
+{
+}
+
+IMPL_LINK(FmXListBoxCell, ChangedHdl, bool, bInteractive, void)
+{
+ if (!m_pBox)
+ return;
+
+ weld::ComboBox& rBox = m_pBox->get_widget();
+
+ if (bInteractive && !rBox.changed_by_direct_pick())
+ return;
+
+ OnDoubleClick();
+
+ css::awt::ItemEvent aEvent;
+ aEvent.Source = *this;
+ aEvent.Highlighted = 0;
+
+ // with multiple selection 0xFFFF, otherwise the ID
+ aEvent.Selected = (rBox.get_active() != -1 )
+ ? rBox.get_active() : 0xFFFF;
+
+ m_aItemListeners.notifyEach( &awt::XItemListener::itemStateChanged, aEvent );
+}
+
+void FmXListBoxCell::OnDoubleClick()
+{
+ css::awt::ActionEvent aEvent;
+ aEvent.Source = *this;
+ weld::ComboBox& rBox = m_pBox->get_widget();
+ aEvent.ActionCommand = rBox.get_active_text();
+
+ m_aActionListeners.notifyEach( &css::awt::XActionListener::actionPerformed, aEvent );
+}
+
+FmXComboBoxCell::FmXComboBoxCell( DbGridColumn* pColumn, std::unique_ptr<DbCellControl> pControl )
+ :FmXTextCell( pColumn, std::move(pControl) )
+ ,m_aItemListeners( m_aMutex )
+ ,m_aActionListeners( m_aMutex )
+ ,m_pComboBox(&static_cast<ComboBoxControl&>(m_pCellControl->GetWindow()))
+ ,m_nLines(Application::GetSettings().GetStyleSettings().GetListBoxMaximumLineCount())
+{
+ m_pComboBox->SetAuxModifyHdl(LINK(this, FmXComboBoxCell, ChangedHdl));
+}
+
+FmXComboBoxCell::~FmXComboBoxCell()
+{
+ if ( !OComponentHelper::rBHelper.bDisposed )
+ {
+ acquire();
+ dispose();
+ }
+
+}
+
+void FmXComboBoxCell::disposing()
+{
+ css::lang::EventObject aEvt(*this);
+ m_aItemListeners.disposeAndClear(aEvt);
+ m_aActionListeners.disposeAndClear(aEvt);
+
+ m_pComboBox->SetAuxModifyHdl(Link<bool,void>());
+ m_pComboBox = nullptr;
+
+ FmXTextCell::disposing();
+}
+
+Any SAL_CALL FmXComboBoxCell::queryAggregation( const css::uno::Type& _rType )
+{
+ Any aReturn = FmXTextCell::queryAggregation(_rType);
+
+ if ( !aReturn.hasValue() )
+ aReturn = FmXComboBoxCell_Base::queryInterface( _rType );
+
+ return aReturn;
+}
+
+Sequence< Type > SAL_CALL FmXComboBoxCell::getTypes( )
+{
+ return ::comphelper::concatSequences(
+ FmXTextCell::getTypes(),
+ FmXComboBoxCell_Base::getTypes()
+ );
+}
+
+IMPLEMENT_GET_IMPLEMENTATION_ID( FmXComboBoxCell )
+
+void SAL_CALL FmXComboBoxCell::addItemListener(const Reference< awt::XItemListener >& l)
+{
+ m_aItemListeners.addInterface( l );
+}
+
+void SAL_CALL FmXComboBoxCell::removeItemListener(const Reference< awt::XItemListener >& l)
+{
+ m_aItemListeners.removeInterface( l );
+}
+
+void SAL_CALL FmXComboBoxCell::addActionListener(const Reference< awt::XActionListener >& l)
+{
+ m_aActionListeners.addInterface( l );
+}
+
+
+void SAL_CALL FmXComboBoxCell::removeActionListener(const Reference< awt::XActionListener >& l)
+{
+ m_aActionListeners.removeInterface( l );
+}
+
+void SAL_CALL FmXComboBoxCell::addItem( const OUString& Item, sal_Int16 Pos )
+{
+ ::osl::MutexGuard aGuard( m_aMutex );
+ if (!m_pComboBox)
+ return;
+ weld::ComboBox& rBox = m_pComboBox->get_widget();
+ rBox.insert_text(Pos, Item);
+}
+
+void SAL_CALL FmXComboBoxCell::addItems( const Sequence< OUString >& Items, sal_Int16 Pos )
+{
+ ::osl::MutexGuard aGuard( m_aMutex );
+ if (!m_pComboBox)
+ return;
+ weld::ComboBox& rBox = m_pComboBox->get_widget();
+ sal_uInt16 nP = Pos;
+ for ( const auto& rItem : Items )
+ {
+ rBox.insert_text(nP, rItem);
+ if ( Pos != -1 )
+ nP++;
+ }
+}
+
+void SAL_CALL FmXComboBoxCell::removeItems( sal_Int16 Pos, sal_Int16 Count )
+{
+ ::osl::MutexGuard aGuard( m_aMutex );
+ if (!m_pComboBox)
+ return;
+ weld::ComboBox& rBox = m_pComboBox->get_widget();
+ for ( sal_uInt16 n = Count; n; )
+ rBox.remove( Pos + (--n) );
+}
+
+sal_Int16 SAL_CALL FmXComboBoxCell::getItemCount()
+{
+ ::osl::MutexGuard aGuard( m_aMutex );
+ if (!m_pComboBox)
+ return 0;
+ weld::ComboBox& rBox = m_pComboBox->get_widget();
+ return rBox.get_count();
+}
+
+OUString SAL_CALL FmXComboBoxCell::getItem( sal_Int16 Pos )
+{
+ ::osl::MutexGuard aGuard( m_aMutex );
+ if (!m_pComboBox)
+ return OUString();
+ weld::ComboBox& rBox = m_pComboBox->get_widget();
+ return rBox.get_text(Pos);
+}
+
+Sequence< OUString > SAL_CALL FmXComboBoxCell::getItems()
+{
+ ::osl::MutexGuard aGuard( m_aMutex );
+
+ Sequence< OUString > aItems;
+ if (m_pComboBox)
+ {
+ weld::ComboBox& rBox = m_pComboBox->get_widget();
+ const sal_Int32 nEntries = rBox.get_count();
+ aItems.realloc( nEntries );
+ OUString* pItem = aItems.getArray();
+ for ( sal_Int32 n=0; n<nEntries; ++n, ++pItem )
+ *pItem = rBox.get_text(n);
+ }
+ return aItems;
+}
+
+sal_Int16 SAL_CALL FmXComboBoxCell::getDropDownLineCount()
+{
+ ::osl::MutexGuard aGuard( m_aMutex );
+ return m_nLines;
+}
+
+void SAL_CALL FmXComboBoxCell::setDropDownLineCount(sal_Int16 nLines)
+{
+ ::osl::MutexGuard aGuard( m_aMutex );
+ m_nLines = nLines; // just store it to return it
+}
+
+IMPL_LINK(FmXComboBoxCell, ChangedHdl, bool, bInteractive, void)
+{
+ if (!m_pComboBox)
+ return;
+
+ weld::ComboBox& rComboBox = m_pComboBox->get_widget();
+
+ if (bInteractive && !rComboBox.changed_by_direct_pick())
+ return;
+
+ awt::ItemEvent aEvent;
+ aEvent.Source = *this;
+ aEvent.Highlighted = 0;
+
+ // with invalid selection 0xFFFF, otherwise the position
+ aEvent.Selected = ( rComboBox.get_active() != -1 )
+ ? rComboBox.get_active()
+ : 0xFFFF;
+ m_aItemListeners.notifyEach( &awt::XItemListener::itemStateChanged, aEvent );
+}
+
+FmXFilterCell::FmXFilterCell(DbGridColumn* pColumn, std::unique_ptr<DbFilterField> pControl )
+ :FmXGridCell( pColumn, std::move(pControl) )
+ ,m_aTextListeners(m_aMutex)
+{
+ static_cast<DbFilterField*>(m_pCellControl.get())->SetCommitHdl( LINK( this, FmXFilterCell, OnCommit ) );
+}
+
+FmXFilterCell::~FmXFilterCell()
+{
+ if (!OComponentHelper::rBHelper.bDisposed)
+ {
+ acquire();
+ dispose();
+ }
+
+}
+
+// XUnoTunnel
+sal_Int64 SAL_CALL FmXFilterCell::getSomething( const Sequence< sal_Int8 >& _rIdentifier )
+{
+ return comphelper::getSomethingImpl(_rIdentifier, this);
+}
+
+const Sequence<sal_Int8>& FmXFilterCell::getUnoTunnelId()
+{
+ static const comphelper::UnoIdInit theFmXFilterCellUnoTunnelId;
+ return theFmXFilterCellUnoTunnelId.getSeq();
+}
+
+
+void FmXFilterCell::PaintCell( OutputDevice& rDev, const tools::Rectangle& rRect )
+{
+ static_cast< DbFilterField* >( m_pCellControl.get() )->PaintCell( rDev, rRect );
+}
+
+// OComponentHelper
+
+void FmXFilterCell::disposing()
+{
+ css::lang::EventObject aEvt(*this);
+ m_aTextListeners.disposeAndClear(aEvt);
+
+ static_cast<DbFilterField*>(m_pCellControl.get())->SetCommitHdl(Link<DbFilterField&,void>());
+
+ FmXGridCell::disposing();
+}
+
+
+Any SAL_CALL FmXFilterCell::queryAggregation( const css::uno::Type& _rType )
+{
+ Any aReturn = FmXGridCell::queryAggregation(_rType);
+
+ if ( !aReturn.hasValue() )
+ aReturn = FmXFilterCell_Base::queryInterface( _rType );
+
+ return aReturn;
+}
+
+
+Sequence< css::uno::Type > SAL_CALL FmXFilterCell::getTypes( )
+{
+ return ::comphelper::concatSequences(
+ FmXGridCell::getTypes(),
+ FmXFilterCell_Base::getTypes()
+ );
+}
+
+
+IMPLEMENT_GET_IMPLEMENTATION_ID( FmXFilterCell )
+
+// css::awt::XTextComponent
+
+void SAL_CALL FmXFilterCell::addTextListener(const Reference< css::awt::XTextListener >& l)
+{
+ m_aTextListeners.addInterface( l );
+}
+
+
+void SAL_CALL FmXFilterCell::removeTextListener(const Reference< css::awt::XTextListener >& l)
+{
+ m_aTextListeners.removeInterface( l );
+}
+
+void SAL_CALL FmXFilterCell::setText( const OUString& aText )
+{
+ ::osl::MutexGuard aGuard( m_aMutex );
+ static_cast<DbFilterField*>(m_pCellControl.get())->SetText(aText);
+}
+
+void SAL_CALL FmXFilterCell::insertText( const css::awt::Selection& /*rSel*/, const OUString& /*aText*/ )
+{
+}
+
+OUString SAL_CALL FmXFilterCell::getText()
+{
+ ::osl::MutexGuard aGuard( m_aMutex );
+ return static_cast<DbFilterField*>(m_pCellControl.get())->GetText();
+}
+
+OUString SAL_CALL FmXFilterCell::getSelectedText()
+{
+ return getText();
+}
+
+void SAL_CALL FmXFilterCell::setSelection( const css::awt::Selection& /*aSelection*/ )
+{
+}
+
+css::awt::Selection SAL_CALL FmXFilterCell::getSelection()
+{
+ return css::awt::Selection();
+}
+
+sal_Bool SAL_CALL FmXFilterCell::isEditable()
+{
+ return true;
+}
+
+void SAL_CALL FmXFilterCell::setEditable( sal_Bool /*bEditable*/ )
+{
+}
+
+sal_Int16 SAL_CALL FmXFilterCell::getMaxTextLen()
+{
+ return 0;
+}
+
+void SAL_CALL FmXFilterCell::setMaxTextLen( sal_Int16 /*nLen*/ )
+{
+}
+
+IMPL_LINK_NOARG(FmXFilterCell, OnCommit, DbFilterField&, void)
+{
+ css::awt::TextEvent aEvt;
+ aEvt.Source = *this;
+ m_aTextListeners.notifyEach( &css::awt::XTextListener::textChanged, aEvt );
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/fmcomp/gridcols.cxx b/svx/source/fmcomp/gridcols.cxx
new file mode 100644
index 000000000..92546d155
--- /dev/null
+++ b/svx/source/fmcomp/gridcols.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 <gridcols.hxx>
+#include <tools/debug.hxx>
+#include <fmservs.hxx>
+#include <com/sun/star/uno/Sequence.hxx>
+
+using namespace ::com::sun::star::uno;
+
+
+static const css::uno::Sequence<OUString>& getColumnTypes()
+{
+ static css::uno::Sequence<OUString> aColumnTypes = []()
+ {
+ css::uno::Sequence<OUString> tmp(10);
+ OUString* pNames = tmp.getArray();
+ pNames[TYPE_CHECKBOX] = FM_COL_CHECKBOX;
+ pNames[TYPE_COMBOBOX] = FM_COL_COMBOBOX;
+ pNames[TYPE_CURRENCYFIELD] = FM_COL_CURRENCYFIELD;
+ pNames[TYPE_DATEFIELD] = FM_COL_DATEFIELD;
+ pNames[TYPE_FORMATTEDFIELD] = FM_COL_FORMATTEDFIELD;
+ pNames[TYPE_LISTBOX] = FM_COL_LISTBOX;
+ pNames[TYPE_NUMERICFIELD] = FM_COL_NUMERICFIELD;
+ pNames[TYPE_PATTERNFIELD] = FM_COL_PATTERNFIELD;
+ pNames[TYPE_TEXTFIELD] = FM_COL_TEXTFIELD;
+ pNames[TYPE_TIMEFIELD] = FM_COL_TIMEFIELD;
+ return tmp;
+ }();
+ return aColumnTypes;
+}
+
+
+extern "C" {
+
+// comparison of PropertyInfo
+static int NameCompare(const void* pFirst, const void* pSecond)
+{
+ return static_cast<OUString const *>(pFirst)->compareTo(*static_cast<OUString const *>(pSecond));
+}
+
+}
+
+namespace
+{
+
+ sal_Int32 lcl_findPos(const OUString& aStr, const Sequence< OUString>& rList)
+ {
+ const OUString* pStrList = rList.getConstArray();
+ OUString* pResult = static_cast<OUString*>(bsearch(&aStr, static_cast<void const *>(pStrList), rList.getLength(), sizeof(OUString),
+ &NameCompare));
+
+ if (pResult)
+ return (pResult - pStrList);
+ else
+ return -1;
+ }
+}
+
+
+sal_Int32 getColumnTypeByModelName(const OUString& aModelName)
+{
+ static const OUStringLiteral aModelPrefix(u"com.sun.star.form.component.");
+ static const OUStringLiteral aCompatibleModelPrefix(u"stardiv.one.form.component.");
+
+ sal_Int32 nTypeId = -1;
+ if (aModelName == FM_COMPONENT_EDIT)
+ nTypeId = TYPE_TEXTFIELD;
+ else
+ {
+ sal_Int32 nPrefixPos = aModelName.indexOf(aModelPrefix);
+#ifdef DBG_UTIL
+ sal_Int32 nCompatiblePrefixPos = aModelName.indexOf(aCompatibleModelPrefix);
+ DBG_ASSERT( (nPrefixPos != -1) || (nCompatiblePrefixPos != -1), "::getColumnTypeByModelName() : wrong service!");
+#endif
+
+ OUString aColumnType = (nPrefixPos != -1)
+ ? aModelName.copy(aModelPrefix.getLength())
+ : aModelName.copy(aCompatibleModelPrefix.getLength());
+
+ const css::uno::Sequence<OUString>& rColumnTypes = getColumnTypes();
+ nTypeId = lcl_findPos(aColumnType, rColumnTypes);
+ }
+ return nTypeId;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/fmcomp/gridctrl.cxx b/svx/source/fmcomp/gridctrl.cxx
new file mode 100644
index 000000000..ab8f128a7
--- /dev/null
+++ b/svx/source/fmcomp/gridctrl.cxx
@@ -0,0 +1,3396 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * 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 <helpids.h>
+#include <svx/gridctrl.hxx>
+#include <gridcell.hxx>
+#include <svx/fmtools.hxx>
+#include <svtools/stringtransfer.hxx>
+#include <connectivity/dbtools.hxx>
+#include <connectivity/dbconversion.hxx>
+
+#include <fmprop.hxx>
+#include <com/sun/star/sdbc/ResultSetConcurrency.hpp>
+#include <com/sun/star/accessibility/XAccessible.hpp>
+#include <com/sun/star/sdb/XResultSetAccess.hpp>
+#include <com/sun/star/sdb/RowChangeAction.hpp>
+#include <com/sun/star/sdb/XRowsChangeBroadcaster.hpp>
+#include <com/sun/star/sdbc/SQLException.hpp>
+#include <com/sun/star/sdbc/XResultSetUpdate.hpp>
+#include <com/sun/star/sdbc/XRowSet.hpp>
+#include <com/sun/star/sdbcx/Privilege.hpp>
+#include <com/sun/star/util/NumberFormatter.hpp>
+#include <com/sun/star/util/XNumberFormatsSupplier.hpp>
+#include <com/sun/star/beans/XPropertySet.hpp>
+#include <com/sun/star/beans/PropertyChangeEvent.hpp>
+#include <com/sun/star/container/XIndexAccess.hpp>
+#include <tools/diagnose_ex.h>
+#include <tools/debug.hxx>
+#include <vcl/settings.hxx>
+#include <vcl/commandevent.hxx>
+#include <vcl/svapp.hxx>
+#include <vcl/weld.hxx>
+#include <vcl/weldutils.hxx>
+
+#include <svx/strings.hrc>
+
+#include <svx/dialmgr.hxx>
+#include <sdbdatacolumn.hxx>
+
+#include <comphelper/property.hxx>
+#include <comphelper/types.hxx>
+#include <cppuhelper/implbase.hxx>
+
+#include <algorithm>
+#include <cstdlib>
+#include <map>
+#include <memory>
+
+using namespace ::dbtools;
+using namespace ::dbtools::DBTypeConversion;
+using namespace ::svxform;
+using namespace ::svt;
+using namespace ::com::sun::star::beans;
+using namespace ::com::sun::star::lang;
+using namespace ::com::sun::star::uno;
+using namespace ::com::sun::star::sdbc;
+using namespace ::com::sun::star::sdbcx;
+using namespace ::com::sun::star::sdb;
+using namespace ::com::sun::star::datatransfer;
+using namespace ::com::sun::star::container;
+using namespace com::sun::star::accessibility;
+
+#define ROWSTATUS(row) (!row.is() ? "NULL" : row->GetStatus() == GridRowStatus::Clean ? "CLEAN" : row->GetStatus() == GridRowStatus::Modified ? "MODIFIED" : row->GetStatus() == GridRowStatus::Deleted ? "DELETED" : "INVALID")
+
+constexpr auto DEFAULT_BROWSE_MODE =
+ BrowserMode::COLUMNSELECTION
+ | BrowserMode::MULTISELECTION
+ | BrowserMode::KEEPHIGHLIGHT
+ | BrowserMode::TRACKING_TIPS
+ | BrowserMode::HLINES
+ | BrowserMode::VLINES
+ | BrowserMode::HEADERBAR_NEW;
+
+class RowSetEventListener : public ::cppu::WeakImplHelper<XRowsChangeListener>
+{
+ VclPtr<DbGridControl> m_pControl;
+public:
+ explicit RowSetEventListener(DbGridControl* i_pControl) : m_pControl(i_pControl)
+ {
+ }
+
+private:
+ // XEventListener
+ virtual void SAL_CALL disposing(const css::lang::EventObject& /*i_aEvt*/) override
+ {
+ }
+ virtual void SAL_CALL rowsChanged(const css::sdb::RowsChangeEvent& i_aEvt) override
+ {
+ if ( i_aEvt.Action != RowChangeAction::UPDATE )
+ return;
+
+ ::DbGridControl::GrantControlAccess aAccess;
+ CursorWrapper* pSeek = m_pControl->GetSeekCursor(aAccess);
+ const DbGridRowRef& rSeekRow = m_pControl->GetSeekRow(aAccess);
+ for(const Any& rBookmark : i_aEvt.Bookmarks)
+ {
+ pSeek->moveToBookmark(rBookmark);
+ // get the data
+ rSeekRow->SetState(pSeek, true);
+ sal_Int32 nSeekPos = pSeek->getRow() - 1;
+ m_pControl->SetSeekPos(nSeekPos,aAccess);
+ m_pControl->RowModified(nSeekPos);
+ }
+ }
+};
+
+class GridFieldValueListener;
+typedef std::map<sal_uInt16, GridFieldValueListener*> ColumnFieldValueListeners;
+
+class GridFieldValueListener : protected ::comphelper::OPropertyChangeListener
+{
+ osl::Mutex m_aMutex;
+ DbGridControl& m_rParent;
+ rtl::Reference<::comphelper::OPropertyChangeMultiplexer> m_pRealListener;
+ sal_uInt16 m_nId;
+ sal_Int16 m_nSuspended;
+ bool m_bDisposed : 1;
+
+public:
+ GridFieldValueListener(DbGridControl& _rParent, const Reference< XPropertySet >& xField, sal_uInt16 _nId);
+ virtual ~GridFieldValueListener() override;
+
+ virtual void _propertyChanged(const PropertyChangeEvent& evt) override;
+
+ void suspend() { ++m_nSuspended; }
+ void resume() { --m_nSuspended; }
+
+ void dispose();
+};
+
+GridFieldValueListener::GridFieldValueListener(DbGridControl& _rParent, const Reference< XPropertySet >& _rField, sal_uInt16 _nId)
+ :OPropertyChangeListener(m_aMutex)
+ ,m_rParent(_rParent)
+ ,m_nId(_nId)
+ ,m_nSuspended(0)
+ ,m_bDisposed(false)
+{
+ if (_rField.is())
+ {
+ m_pRealListener = new ::comphelper::OPropertyChangeMultiplexer(this, _rField);
+ m_pRealListener->addProperty(FM_PROP_VALUE);
+ }
+}
+
+GridFieldValueListener::~GridFieldValueListener()
+{
+ dispose();
+}
+
+void GridFieldValueListener::_propertyChanged(const PropertyChangeEvent& /*_evt*/)
+{
+ DBG_ASSERT(m_nSuspended>=0, "GridFieldValueListener::_propertyChanged : resume > suspend !");
+ if (m_nSuspended <= 0)
+ m_rParent.FieldValueChanged(m_nId);
+}
+
+void GridFieldValueListener::dispose()
+{
+ if (m_bDisposed)
+ {
+ DBG_ASSERT(!m_pRealListener, "GridFieldValueListener::dispose : inconsistent !");
+ return;
+ }
+
+ if (m_pRealListener.is())
+ {
+ m_pRealListener->dispose();
+ m_pRealListener.clear();
+ }
+
+ m_bDisposed = true;
+ m_rParent.FieldListenerDisposing(m_nId);
+}
+
+class DisposeListenerGridBridge : public FmXDisposeListener
+{
+ DbGridControl& m_rParent;
+ rtl::Reference<FmXDisposeMultiplexer> m_xRealListener;
+
+public:
+ DisposeListenerGridBridge( DbGridControl& _rParent, const Reference< XComponent >& _rxObject);
+ virtual ~DisposeListenerGridBridge() override;
+
+ virtual void disposing(sal_Int16 _nId) override { m_rParent.disposing(_nId); }
+};
+
+DisposeListenerGridBridge::DisposeListenerGridBridge(DbGridControl& _rParent, const Reference< XComponent >& _rxObject)
+ :FmXDisposeListener()
+ ,m_rParent(_rParent)
+{
+
+ if (_rxObject.is())
+ {
+ m_xRealListener = new FmXDisposeMultiplexer(this, _rxObject);
+ }
+}
+
+DisposeListenerGridBridge::~DisposeListenerGridBridge()
+{
+ if (m_xRealListener.is())
+ {
+ m_xRealListener->dispose();
+ }
+}
+
+const DbGridControlNavigationBarState ControlMap[] =
+ {
+ DbGridControlNavigationBarState::Text,
+ DbGridControlNavigationBarState::Absolute,
+ DbGridControlNavigationBarState::Of,
+ DbGridControlNavigationBarState::Count,
+ DbGridControlNavigationBarState::First,
+ DbGridControlNavigationBarState::Next,
+ DbGridControlNavigationBarState::Prev,
+ DbGridControlNavigationBarState::Last,
+ DbGridControlNavigationBarState::New,
+ DbGridControlNavigationBarState::NONE
+ };
+
+bool CompareBookmark(const Any& aLeft, const Any& aRight)
+{
+ return aLeft == aRight;
+}
+
+class FmXGridSourcePropListener : public ::comphelper::OPropertyChangeListener
+{
+ VclPtr<DbGridControl> m_pParent;
+
+ // a DbGridControl has no mutex, so we use our own as the base class expects one
+ osl::Mutex m_aMutex;
+ sal_Int16 m_nSuspended;
+
+public:
+ explicit FmXGridSourcePropListener(DbGridControl* _pParent);
+
+ void suspend() { ++m_nSuspended; }
+ void resume() { --m_nSuspended; }
+
+ virtual void _propertyChanged(const PropertyChangeEvent& evt) override;
+};
+
+FmXGridSourcePropListener::FmXGridSourcePropListener(DbGridControl* _pParent)
+ :OPropertyChangeListener(m_aMutex)
+ ,m_pParent(_pParent)
+ ,m_nSuspended(0)
+{
+ DBG_ASSERT(m_pParent, "FmXGridSourcePropListener::FmXGridSourcePropListener : invalid parent !");
+}
+
+void FmXGridSourcePropListener::_propertyChanged(const PropertyChangeEvent& evt)
+{
+ DBG_ASSERT(m_nSuspended>=0, "FmXGridSourcePropListener::_propertyChanged : resume > suspend !");
+ if (m_nSuspended <= 0)
+ m_pParent->DataSourcePropertyChanged(evt);
+}
+
+const int nReserveNumDigits = 7;
+
+NavigationBar::AbsolutePos::AbsolutePos(std::unique_ptr<weld::Entry> xEntry, NavigationBar* pBar)
+ : RecordItemWindowBase(std::move(xEntry))
+ , m_xParent(pBar)
+{
+}
+
+bool NavigationBar::AbsolutePos::DoKeyInput(const KeyEvent& rEvt)
+{
+ if (rEvt.GetKeyCode() == KEY_TAB)
+ {
+ m_xParent->GetParent()->GrabFocus();
+ return true;
+ }
+ return RecordItemWindowBase::DoKeyInput(rEvt);
+}
+
+void NavigationBar::AbsolutePos::PositionFired(sal_Int64 nRecord)
+{
+ m_xParent->PositionDataSource(nRecord);
+ m_xParent->InvalidateState(DbGridControlNavigationBarState::Absolute);
+}
+
+void NavigationBar::PositionDataSource(sal_Int32 nRecord)
+{
+ if (m_bPositioning)
+ return;
+ // the MoveToPosition may cause a LoseFocus which would lead to a second MoveToPosition,
+ // so protect against this recursion
+ m_bPositioning = true;
+ static_cast<DbGridControl*>(GetParent())->MoveToPosition(nRecord - 1);
+ m_bPositioning = false;
+}
+
+NavigationBar::NavigationBar(vcl::Window* pParent)
+ : InterimItemWindow(pParent, "svx/ui/navigationbar.ui", "NavigationBar")
+ , m_xRecordText(m_xBuilder->weld_label("recordtext"))
+ , m_xAbsolute(new NavigationBar::AbsolutePos(m_xBuilder->weld_entry("entry-noframe"), this))
+ , m_xRecordOf(m_xBuilder->weld_label("recordof"))
+ , m_xRecordCount(m_xBuilder->weld_label("recordcount"))
+ , m_xFirstBtn(m_xBuilder->weld_button("first"))
+ , m_xPrevBtn(m_xBuilder->weld_button("prev"))
+ , m_xNextBtn(m_xBuilder->weld_button("next"))
+ , m_xLastBtn(m_xBuilder->weld_button("last"))
+ , m_xNewBtn(m_xBuilder->weld_button("new"))
+ , m_xPrevRepeater(std::make_shared<weld::ButtonPressRepeater>(*m_xPrevBtn, LINK(this,NavigationBar,OnClick)))
+ , m_xNextRepeater(std::make_shared<weld::ButtonPressRepeater>(*m_xNextBtn, LINK(this,NavigationBar,OnClick)))
+ , m_nCurrentPos(-1)
+ , m_bPositioning(false)
+{
+ vcl::Font aApplFont(Application::GetSettings().GetStyleSettings().GetToolFont());
+ m_xAbsolute->set_font(aApplFont);
+ aApplFont.SetTransparent(true);
+ m_xRecordText->set_font(aApplFont);
+ m_xRecordOf->set_font(aApplFont);
+ m_xRecordCount->set_font(aApplFont);
+
+ m_xFirstBtn->set_help_id(HID_GRID_TRAVEL_FIRST);
+ m_xPrevBtn->set_help_id(HID_GRID_TRAVEL_PREV);
+ m_xNextBtn->set_help_id(HID_GRID_TRAVEL_NEXT);
+ m_xLastBtn->set_help_id(HID_GRID_TRAVEL_LAST);
+ m_xNewBtn->set_help_id(HID_GRID_TRAVEL_NEW);
+ m_xAbsolute->set_help_id(HID_GRID_TRAVEL_ABSOLUTE);
+ m_xRecordCount->set_help_id(HID_GRID_NUMBEROFRECORDS);
+
+ // set handlers for buttons
+ m_xFirstBtn->connect_clicked(LINK(this,NavigationBar,OnClick));
+ m_xLastBtn->connect_clicked(LINK(this,NavigationBar,OnClick));
+ m_xNewBtn->connect_clicked(LINK(this,NavigationBar,OnClick));
+
+ m_xRecordText->set_label(SvxResId(RID_STR_REC_TEXT));
+ m_xRecordOf->set_label(SvxResId(RID_STR_REC_FROM_TEXT));
+ m_xRecordCount->set_label(OUString('?'));
+
+ auto nReserveWidth = m_xRecordCount->get_approximate_digit_width() * nReserveNumDigits;
+ m_xAbsolute->GetWidget()->set_size_request(nReserveWidth, -1);
+ m_xRecordCount->set_size_request(nReserveWidth, -1);
+}
+
+NavigationBar::~NavigationBar()
+{
+ disposeOnce();
+}
+
+void NavigationBar::dispose()
+{
+ m_xRecordText.reset();
+ m_xAbsolute.reset();
+ m_xRecordOf.reset();
+ m_xRecordCount.reset();
+ m_xFirstBtn.reset();
+ m_xPrevBtn.reset();
+ m_xNextBtn.reset();
+ m_xLastBtn.reset();
+ m_xNewBtn.reset();
+ InterimItemWindow::dispose();
+}
+
+sal_uInt16 NavigationBar::ArrangeControls()
+{
+ return m_xContainer->get_preferred_size().Width();
+}
+
+IMPL_LINK(NavigationBar, OnClick, weld::Button&, rButton, void)
+{
+ DbGridControl* pParent = static_cast<DbGridControl*>(GetParent());
+
+ if (pParent->m_aMasterSlotExecutor.IsSet())
+ {
+ bool lResult = false;
+ if (&rButton == m_xFirstBtn.get())
+ lResult = pParent->m_aMasterSlotExecutor.Call(DbGridControlNavigationBarState::First);
+ else if( &rButton == m_xPrevBtn.get() )
+ lResult = pParent->m_aMasterSlotExecutor.Call(DbGridControlNavigationBarState::Prev);
+ else if( &rButton == m_xNextBtn.get() )
+ lResult = pParent->m_aMasterSlotExecutor.Call(DbGridControlNavigationBarState::Next);
+ else if( &rButton == m_xLastBtn.get() )
+ lResult = pParent->m_aMasterSlotExecutor.Call(DbGridControlNavigationBarState::Last);
+ else if( &rButton == m_xNewBtn.get() )
+ lResult = pParent->m_aMasterSlotExecutor.Call(DbGridControlNavigationBarState::New);
+
+ if (lResult)
+ // the link already handled it
+ return;
+ }
+
+ if (&rButton == m_xFirstBtn.get())
+ pParent->MoveToFirst();
+ else if( &rButton == m_xPrevBtn.get() )
+ pParent->MoveToPrev();
+ else if( &rButton == m_xNextBtn.get() )
+ pParent->MoveToNext();
+ else if( &rButton == m_xLastBtn.get() )
+ pParent->MoveToLast();
+ else if( &rButton == m_xNewBtn.get() )
+ pParent->AppendNew();
+}
+
+void NavigationBar::InvalidateAll(sal_Int32 nCurrentPos, bool bAll)
+{
+ if (!(m_nCurrentPos != nCurrentPos || nCurrentPos < 0 || bAll))
+ return;
+
+ DbGridControl* pParent = static_cast<DbGridControl*>(GetParent());
+
+ sal_Int32 nAdjustedRowCount = pParent->GetRowCount() - ((pParent->GetOptions() & DbGridControlOptions::Insert) ? 2 : 1);
+
+ // check if everything needs to be invalidated
+ bAll = bAll || m_nCurrentPos <= 0;
+ bAll = bAll || nCurrentPos <= 0;
+ bAll = bAll || m_nCurrentPos >= nAdjustedRowCount;
+ bAll = bAll || nCurrentPos >= nAdjustedRowCount;
+
+ if ( bAll )
+ {
+ m_nCurrentPos = nCurrentPos;
+ int i = 0;
+ while (ControlMap[i] != DbGridControlNavigationBarState::NONE)
+ SetState(ControlMap[i++]);
+ }
+ else // is in the center
+ {
+ m_nCurrentPos = nCurrentPos;
+ SetState(DbGridControlNavigationBarState::Count);
+ SetState(DbGridControlNavigationBarState::Absolute);
+ }
+}
+
+bool NavigationBar::GetState(DbGridControlNavigationBarState nWhich) const
+{
+ DbGridControl* pParent = static_cast<DbGridControl*>(GetParent());
+
+ if (!pParent->IsOpen() || pParent->IsDesignMode() || !pParent->IsEnabled()
+ || pParent->IsFilterMode() )
+ return false;
+ else
+ {
+ // check if we have a master state provider
+ if (pParent->m_aMasterStateProvider.IsSet())
+ {
+ tools::Long nState = pParent->m_aMasterStateProvider.Call( nWhich );
+ if (nState>=0)
+ return (nState>0);
+ }
+
+ bool bAvailable = true;
+
+ switch (nWhich)
+ {
+ case DbGridControlNavigationBarState::First:
+ case DbGridControlNavigationBarState::Prev:
+ bAvailable = m_nCurrentPos > 0;
+ break;
+ case DbGridControlNavigationBarState::Next:
+ if(pParent->m_bRecordCountFinal)
+ {
+ bAvailable = m_nCurrentPos < pParent->GetRowCount() - 1;
+ if (!bAvailable && pParent->GetOptions() & DbGridControlOptions::Insert)
+ bAvailable = (m_nCurrentPos == pParent->GetRowCount() - 2) && pParent->IsModified();
+ }
+ break;
+ case DbGridControlNavigationBarState::Last:
+ if(pParent->m_bRecordCountFinal)
+ {
+ if (pParent->GetOptions() & DbGridControlOptions::Insert)
+ bAvailable = pParent->IsCurrentAppending() ? pParent->GetRowCount() > 1 :
+ m_nCurrentPos != pParent->GetRowCount() - 2;
+ else
+ bAvailable = m_nCurrentPos != pParent->GetRowCount() - 1;
+ }
+ break;
+ case DbGridControlNavigationBarState::New:
+ bAvailable = (pParent->GetOptions() & DbGridControlOptions::Insert) && pParent->GetRowCount() && m_nCurrentPos < pParent->GetRowCount() - 1;
+ break;
+ case DbGridControlNavigationBarState::Absolute:
+ bAvailable = pParent->GetRowCount() > 0;
+ break;
+ default: break;
+ }
+ return bAvailable;
+ }
+}
+
+void NavigationBar::SetState(DbGridControlNavigationBarState nWhich)
+{
+ bool bAvailable = GetState(nWhich);
+ DbGridControl* pParent = static_cast<DbGridControl*>(GetParent());
+ weld::Widget* pWnd = nullptr;
+ switch (nWhich)
+ {
+ case DbGridControlNavigationBarState::First:
+ pWnd = m_xFirstBtn.get();
+ break;
+ case DbGridControlNavigationBarState::Prev:
+ pWnd = m_xPrevBtn.get();
+ break;
+ case DbGridControlNavigationBarState::Next:
+ pWnd = m_xNextBtn.get();
+ break;
+ case DbGridControlNavigationBarState::Last:
+ pWnd = m_xLastBtn.get();
+ break;
+ case DbGridControlNavigationBarState::New:
+ pWnd = m_xNewBtn.get();
+ break;
+ case DbGridControlNavigationBarState::Absolute:
+ pWnd = m_xAbsolute->GetWidget();
+ if (bAvailable)
+ m_xAbsolute->set_text(OUString::number(m_nCurrentPos + 1));
+ else
+ m_xAbsolute->set_text(OUString());
+ break;
+ case DbGridControlNavigationBarState::Text:
+ pWnd = m_xRecordText.get();
+ break;
+ case DbGridControlNavigationBarState::Of:
+ pWnd = m_xRecordOf.get();
+ break;
+ case DbGridControlNavigationBarState::Count:
+ {
+ pWnd = m_xRecordCount.get();
+ OUString aText;
+ if (bAvailable)
+ {
+ if (pParent->GetOptions() & DbGridControlOptions::Insert)
+ {
+ if (pParent->IsCurrentAppending() && !pParent->IsModified())
+ aText = OUString::number(pParent->GetRowCount());
+ else
+ aText = OUString::number(pParent->GetRowCount() - 1);
+ }
+ else
+ aText = OUString::number(pParent->GetRowCount());
+ if(!pParent->m_bRecordCountFinal)
+ aText += " *";
+ }
+ else
+ aText.clear();
+
+ // add the number of selected rows, if applicable
+ if (pParent->GetSelectRowCount())
+ {
+ OUString aExtendedInfo = aText + " (" +
+ OUString::number(pParent->GetSelectRowCount()) + ")";
+ m_xRecordCount->set_label(aExtendedInfo);
+ }
+ else
+ m_xRecordCount->set_label(aText);
+
+ pParent->SetRealRowCount(aText);
+ } break;
+ default: break;
+ }
+ DBG_ASSERT(pWnd, "no window");
+ if (!(pWnd && (pWnd->get_sensitive() != bAvailable)))
+ return;
+
+ // this "pWnd->IsEnabled() != bAvailable" is a little hack : Window::Enable always generates a user
+ // event (ImplGenerateMouseMove) even if nothing happened. This may lead to some unwanted effects, so we
+ // do this check.
+ // For further explanation see Bug 69900.
+ pWnd->set_sensitive(bAvailable);
+ if (!bAvailable)
+ {
+ if (pWnd == m_xNextBtn.get())
+ m_xNextRepeater->Stop();
+ else if (pWnd == m_xPrevBtn.get())
+ m_xPrevRepeater->Stop();
+ }
+}
+
+DbGridRow::DbGridRow():m_eStatus(GridRowStatus::Clean), m_bIsNew(true)
+{}
+
+DbGridRow::DbGridRow(CursorWrapper* pCur, bool bPaintCursor)
+ :m_bIsNew(false)
+{
+
+ if (pCur && pCur->Is())
+ {
+ Reference< XIndexAccess > xColumns(pCur->getColumns(), UNO_QUERY);
+ for (sal_Int32 i = 0; i < xColumns->getCount(); ++i)
+ {
+ Reference< XPropertySet > xColSet(
+ xColumns->getByIndex(i), css::uno::UNO_QUERY);
+ m_aVariants.emplace_back( new DataColumn(xColSet) );
+ }
+
+ if (pCur->rowDeleted())
+ m_eStatus = GridRowStatus::Deleted;
+ else
+ {
+ if (bPaintCursor)
+ m_eStatus = (pCur->isAfterLast() || pCur->isBeforeFirst()) ? GridRowStatus::Invalid : GridRowStatus::Clean;
+ else
+ {
+ const Reference< XPropertySet >& xSet = pCur->getPropertySet();
+ if (xSet.is())
+ {
+ m_bIsNew = ::comphelper::getBOOL(xSet->getPropertyValue(FM_PROP_ISNEW));
+ if (!m_bIsNew && (pCur->isAfterLast() || pCur->isBeforeFirst()))
+ m_eStatus = GridRowStatus::Invalid;
+ else if (::comphelper::getBOOL(xSet->getPropertyValue(FM_PROP_ISMODIFIED)))
+ m_eStatus = GridRowStatus::Modified;
+ else
+ m_eStatus = GridRowStatus::Clean;
+ }
+ else
+ m_eStatus = GridRowStatus::Invalid;
+ }
+ }
+ if (!m_bIsNew && IsValid())
+ m_aBookmark = pCur->getBookmark();
+ else
+ m_aBookmark = Any();
+ }
+ else
+ m_eStatus = GridRowStatus::Invalid;
+}
+
+DbGridRow::~DbGridRow()
+{
+}
+
+void DbGridRow::SetState(CursorWrapper* pCur, bool bPaintCursor)
+{
+ if (pCur && pCur->Is())
+ {
+ if (pCur->rowDeleted())
+ {
+ m_eStatus = GridRowStatus::Deleted;
+ m_bIsNew = false;
+ }
+ else
+ {
+ m_eStatus = GridRowStatus::Clean;
+ if (!bPaintCursor)
+ {
+ const Reference< XPropertySet >& xSet = pCur->getPropertySet();
+ DBG_ASSERT(xSet.is(), "DbGridRow::SetState : invalid cursor !");
+
+ if (::comphelper::getBOOL(xSet->getPropertyValue(FM_PROP_ISMODIFIED)))
+ m_eStatus = GridRowStatus::Modified;
+ m_bIsNew = ::comphelper::getBOOL(xSet->getPropertyValue(FM_PROP_ISNEW));
+ }
+ else
+ m_bIsNew = false;
+ }
+
+ try
+ {
+ if (!m_bIsNew && IsValid())
+ m_aBookmark = pCur->getBookmark();
+ else
+ m_aBookmark = Any();
+ }
+ catch(SQLException&)
+ {
+ DBG_UNHANDLED_EXCEPTION("svx");
+ m_aBookmark = Any();
+ m_eStatus = GridRowStatus::Invalid;
+ m_bIsNew = false;
+ }
+ }
+ else
+ {
+ m_aBookmark = Any();
+ m_eStatus = GridRowStatus::Invalid;
+ m_bIsNew = false;
+ }
+}
+
+DbGridControl::DbGridControl(
+ Reference< XComponentContext > const & _rxContext,
+ vcl::Window* pParent,
+ WinBits nBits)
+ :EditBrowseBox(pParent, EditBrowseBoxFlags::NONE, nBits, DEFAULT_BROWSE_MODE )
+ ,m_xContext(_rxContext)
+ ,m_aBar(VclPtr<NavigationBar>::Create(this))
+ ,m_nAsynAdjustEvent(nullptr)
+ ,m_pDataSourcePropListener(nullptr)
+ ,m_pFieldListeners(nullptr)
+ ,m_pGridListener(nullptr)
+ ,m_nSeekPos(-1)
+ ,m_nTotalCount(-1)
+ ,m_aNullDate(::dbtools::DBTypeConversion::getStandardDate())
+ ,m_nMode(DEFAULT_BROWSE_MODE)
+ ,m_nCurrentPos(-1)
+ ,m_nDeleteEvent(nullptr)
+ ,m_nOptions(DbGridControlOptions::Readonly)
+ ,m_nOptionMask(DbGridControlOptions::Insert | DbGridControlOptions::Update | DbGridControlOptions::Delete)
+ ,m_nLastColId(sal_uInt16(-1))
+ ,m_nLastRowId(-1)
+ ,m_bDesignMode(false)
+ ,m_bRecordCountFinal(false)
+ ,m_bNavigationBar(true)
+ ,m_bSynchDisplay(true)
+ ,m_bHandle(true)
+ ,m_bFilterMode(false)
+ ,m_bWantDestruction(false)
+ ,m_bPendingAdjustRows(false)
+ ,m_bHideScrollbars( false )
+ ,m_bUpdating(false)
+{
+
+ OUString sName(SvxResId(RID_STR_NAVIGATIONBAR));
+ m_aBar->SetAccessibleName(sName);
+ m_aBar->Show();
+ ImplInitWindow( InitWindowFacet::All );
+}
+
+void DbGridControl::InsertHandleColumn()
+{
+ // BrowseBox has problems when painting without a handleColumn (hide it here)
+ if (HasHandle())
+ BrowseBox::InsertHandleColumn(GetDefaultColumnWidth(OUString()));
+ else
+ BrowseBox::InsertHandleColumn(0);
+}
+
+void DbGridControl::Init()
+{
+ VclPtr<BrowserHeader> pNewHeader = CreateHeaderBar(this);
+ pHeader->SetMouseTransparent(false);
+
+ SetHeaderBar(pNewHeader);
+ SetMode(m_nMode);
+ SetCursorColor(Color(0xFF, 0, 0));
+
+ InsertHandleColumn();
+}
+
+DbGridControl::~DbGridControl()
+{
+ disposeOnce();
+}
+
+void DbGridControl::dispose()
+{
+ RemoveColumns();
+
+ m_bWantDestruction = true;
+ osl::MutexGuard aGuard(m_aDestructionSafety);
+ if (m_pFieldListeners)
+ DisconnectFromFields();
+ m_pCursorDisposeListener.reset();
+
+ if (m_nDeleteEvent)
+ Application::RemoveUserEvent(m_nDeleteEvent);
+
+ if (m_pDataSourcePropMultiplexer.is())
+ {
+ m_pDataSourcePropMultiplexer->dispose();
+ m_pDataSourcePropMultiplexer.clear(); // this should delete the multiplexer
+ delete m_pDataSourcePropListener;
+ m_pDataSourcePropListener = nullptr;
+ }
+ m_xRowSetListener.clear();
+
+ m_pDataCursor.reset();
+ m_pSeekCursor.reset();
+
+ m_aBar.disposeAndClear();
+
+ EditBrowseBox::dispose();
+}
+
+void DbGridControl::StateChanged( StateChangedType nType )
+{
+ EditBrowseBox::StateChanged( nType );
+
+ switch (nType)
+ {
+ case StateChangedType::Mirroring:
+ ImplInitWindow( InitWindowFacet::WritingMode );
+ Invalidate();
+ break;
+
+ case StateChangedType::Zoom:
+ {
+ ImplInitWindow( InitWindowFacet::Font );
+
+ // and give it a chance to rearrange
+ Point aPoint = GetControlArea().TopLeft();
+ sal_uInt16 nX = static_cast<sal_uInt16>(aPoint.X());
+ ArrangeControls(nX, static_cast<sal_uInt16>(aPoint.Y()));
+ ReserveControlArea(nX);
+ }
+ break;
+ case StateChangedType::ControlFont:
+ ImplInitWindow( InitWindowFacet::Font );
+ Invalidate();
+ break;
+ case StateChangedType::ControlForeground:
+ ImplInitWindow( InitWindowFacet::Foreground );
+ Invalidate();
+ break;
+ case StateChangedType::ControlBackground:
+ ImplInitWindow( InitWindowFacet::Background );
+ Invalidate();
+ break;
+ default:;
+ }
+}
+
+void DbGridControl::DataChanged( const DataChangedEvent& rDCEvt )
+{
+ EditBrowseBox::DataChanged( rDCEvt );
+ if ( (rDCEvt.GetType() == DataChangedEventType::SETTINGS ) &&
+ (rDCEvt.GetFlags() & AllSettingsFlags::STYLE) )
+ {
+ ImplInitWindow( InitWindowFacet::All );
+ Invalidate();
+ }
+}
+
+void DbGridControl::Select()
+{
+ EditBrowseBox::Select();
+
+ // as the selected rows may have changed, update the according display in our navigation bar
+ m_aBar->InvalidateState(DbGridControlNavigationBarState::Count);
+
+ if (m_pGridListener)
+ m_pGridListener->selectionChanged();
+}
+
+void DbGridControl::ImplInitWindow( const InitWindowFacet _eInitWhat )
+{
+ for (auto const & pCol : m_aColumns)
+ {
+ pCol->ImplInitWindow( GetDataWindow(), _eInitWhat );
+ }
+
+ if ( _eInitWhat & InitWindowFacet::WritingMode )
+ {
+ if ( m_bNavigationBar )
+ {
+ m_aBar->EnableRTL( IsRTLEnabled() );
+ }
+ }
+
+ if ( _eInitWhat & InitWindowFacet::Font )
+ {
+ if ( m_bNavigationBar )
+ {
+ if ( IsControlFont() )
+ m_aBar->SetControlFont( GetControlFont() );
+ else
+ m_aBar->SetControlFont();
+
+ m_aBar->SetZoom( GetZoom() );
+ }
+ }
+
+ if ( !(_eInitWhat & InitWindowFacet::Background) )
+ return;
+
+ if (IsControlBackground())
+ {
+ GetDataWindow().SetBackground(GetControlBackground());
+ GetDataWindow().SetControlBackground(GetControlBackground());
+ GetDataWindow().GetOutDev()->SetFillColor(GetControlBackground());
+ }
+ else
+ {
+ GetDataWindow().SetControlBackground();
+ GetDataWindow().GetOutDev()->SetFillColor(GetOutDev()->GetFillColor());
+ }
+}
+
+void DbGridControl::RemoveRows(bool bNewCursor)
+{
+ // Did the data cursor change?
+ if (!bNewCursor)
+ {
+ m_pSeekCursor.reset();
+ m_xPaintRow = m_xDataRow = m_xEmptyRow = m_xCurrentRow = m_xSeekRow = nullptr;
+ m_nCurrentPos = m_nSeekPos = -1;
+ m_nOptions = DbGridControlOptions::Readonly;
+
+ RowRemoved(0, GetRowCount(), false);
+ m_nTotalCount = -1;
+ }
+ else
+ {
+ RemoveRows();
+ }
+}
+
+void DbGridControl::RemoveRows()
+{
+ // we're going to remove all columns and all row, so deactivate the current cell
+ if (IsEditing())
+ DeactivateCell();
+
+ // de-initialize all columns
+ // if there are columns, free all controllers
+ for (auto const & pColumn : m_aColumns)
+ pColumn->Clear();
+
+ m_pSeekCursor.reset();
+ m_pDataCursor.reset();
+
+ m_xPaintRow = m_xDataRow = m_xEmptyRow = m_xCurrentRow = m_xSeekRow = nullptr;
+ m_nCurrentPos = m_nSeekPos = m_nTotalCount = -1;
+ m_nOptions = DbGridControlOptions::Readonly;
+
+ // reset number of sentences to zero in the browser
+ EditBrowseBox::RemoveRows();
+ m_aBar->InvalidateAll(m_nCurrentPos, true);
+}
+
+void DbGridControl::ArrangeControls(sal_uInt16& nX, sal_uInt16 nY)
+{
+ // positioning of the controls
+ if (m_bNavigationBar)
+ {
+ tools::Rectangle aRect(GetControlArea());
+ m_aBar->SetPosSizePixel(Point(0, nY + 1), Size(aRect.GetSize().Width(), aRect.GetSize().Height() - 1));
+ nX = m_aBar->ArrangeControls();
+ }
+}
+
+void DbGridControl::EnableHandle(bool bEnable)
+{
+ if (m_bHandle == bEnable)
+ return;
+
+ // HandleColumn is only hidden because there are a lot of problems while painting otherwise
+ RemoveColumn( HandleColumnId );
+ m_bHandle = bEnable;
+ InsertHandleColumn();
+}
+
+namespace
+{
+ bool adjustModeForScrollbars( BrowserMode& _rMode, bool _bNavigationBar, bool _bHideScrollbars )
+ {
+ BrowserMode nOldMode = _rMode;
+
+ if ( !_bNavigationBar )
+ {
+ _rMode &= ~BrowserMode::AUTO_HSCROLL;
+ }
+
+ if ( _bHideScrollbars )
+ {
+ _rMode |= BrowserMode::NO_HSCROLL | BrowserMode::NO_VSCROLL;
+ _rMode &= ~BrowserMode( BrowserMode::AUTO_HSCROLL | BrowserMode::AUTO_VSCROLL );
+ }
+ else
+ {
+ _rMode |= BrowserMode::AUTO_HSCROLL | BrowserMode::AUTO_VSCROLL;
+ _rMode &= ~BrowserMode( BrowserMode::NO_HSCROLL | BrowserMode::NO_VSCROLL );
+ }
+
+ // note: if we have a navigation bar, we always have an AUTO_HSCROLL. In particular,
+ // _bHideScrollbars is ignored then
+ if ( _bNavigationBar )
+ {
+ _rMode |= BrowserMode::AUTO_HSCROLL;
+ _rMode &= ~BrowserMode::NO_HSCROLL;
+ }
+
+ return nOldMode != _rMode;
+ }
+}
+
+void DbGridControl::EnableNavigationBar(bool bEnable)
+{
+ if (m_bNavigationBar == bEnable)
+ return;
+
+ m_bNavigationBar = bEnable;
+
+ if (bEnable)
+ {
+ m_aBar->Show();
+ m_aBar->Enable();
+ m_aBar->InvalidateAll(m_nCurrentPos, true);
+
+ if ( adjustModeForScrollbars( m_nMode, m_bNavigationBar, m_bHideScrollbars ) )
+ SetMode( m_nMode );
+
+ // get size of the reserved ControlArea
+ Point aPoint = GetControlArea().TopLeft();
+ sal_uInt16 nX = static_cast<sal_uInt16>(aPoint.X());
+
+ ArrangeControls(nX, static_cast<sal_uInt16>(aPoint.Y()));
+ ReserveControlArea(nX);
+ }
+ else
+ {
+ m_aBar->Hide();
+ m_aBar->Disable();
+
+ if ( adjustModeForScrollbars( m_nMode, m_bNavigationBar, m_bHideScrollbars ) )
+ SetMode( m_nMode );
+
+ ReserveControlArea();
+ }
+}
+
+DbGridControlOptions DbGridControl::SetOptions(DbGridControlOptions nOpt)
+{
+ DBG_ASSERT(!m_xCurrentRow.is() || !m_xCurrentRow->IsModified(),
+ "DbGridControl::SetOptions : please do not call when editing a record (things are much easier this way ;) !");
+
+ // for the next setDataSource (which is triggered by a refresh, for instance)
+ m_nOptionMask = nOpt;
+
+ // normalize the new options
+ Reference< XPropertySet > xDataSourceSet = m_pDataCursor->getPropertySet();
+ if (xDataSourceSet.is())
+ {
+ // check what kind of options are available
+ sal_Int32 nPrivileges = 0;
+ xDataSourceSet->getPropertyValue(FM_PROP_PRIVILEGES) >>= nPrivileges;
+ if ((nPrivileges & Privilege::INSERT) == 0)
+ nOpt &= ~DbGridControlOptions::Insert;
+ if ((nPrivileges & Privilege::UPDATE) == 0)
+ nOpt &= ~DbGridControlOptions::Update;
+ if ((nPrivileges & Privilege::DELETE) == 0)
+ nOpt &= ~DbGridControlOptions::Delete;
+ }
+ else
+ nOpt = DbGridControlOptions::Readonly;
+
+ // need to do something after that ?
+ if (nOpt == m_nOptions)
+ return m_nOptions;
+
+ // the 'update' option only affects our BrowserMode (with or w/o focus rect)
+ BrowserMode nNewMode = m_nMode;
+ if (!(m_nMode & BrowserMode::CURSOR_WO_FOCUS))
+ {
+ if (nOpt & DbGridControlOptions::Update)
+ nNewMode |= BrowserMode::HIDECURSOR;
+ else
+ nNewMode &= ~BrowserMode::HIDECURSOR;
+ }
+ else
+ nNewMode &= ~BrowserMode::HIDECURSOR;
+ // should not be necessary if EnablePermanentCursor is used to change the cursor behaviour, but to be sure ...
+
+ if (nNewMode != m_nMode)
+ {
+ SetMode(nNewMode);
+ m_nMode = nNewMode;
+ }
+
+ // _after_ setting the mode because this results in an ActivateCell
+ DeactivateCell();
+
+ bool bInsertChanged = (nOpt & DbGridControlOptions::Insert) != (m_nOptions & DbGridControlOptions::Insert);
+ m_nOptions = nOpt;
+ // we need to set this before the code below because it indirectly uses m_nOptions
+
+ // the 'insert' option affects our empty row
+ if (bInsertChanged)
+ {
+ if (m_nOptions & DbGridControlOptions::Insert)
+ { // the insert option is to be set
+ m_xEmptyRow = new DbGridRow();
+ RowInserted(GetRowCount());
+ }
+ else
+ { // the insert option is to be reset
+ m_xEmptyRow = nullptr;
+ if ((GetCurRow() == GetRowCount() - 1) && (GetCurRow() > 0))
+ GoToRowColumnId(GetCurRow() - 1, GetCurColumnId());
+ RowRemoved(GetRowCount());
+ }
+ }
+
+ // the 'delete' options has no immediate consequences
+
+ ActivateCell();
+ Invalidate();
+ return m_nOptions;
+}
+
+void DbGridControl::ForceHideScrollbars()
+{
+ if ( m_bHideScrollbars )
+ return;
+
+ m_bHideScrollbars = true;
+
+ if ( adjustModeForScrollbars( m_nMode, m_bNavigationBar, m_bHideScrollbars ) )
+ SetMode( m_nMode );
+}
+
+void DbGridControl::EnablePermanentCursor(bool bEnable)
+{
+ if (IsPermanentCursorEnabled() == bEnable)
+ return;
+
+ if (bEnable)
+ {
+ m_nMode &= ~BrowserMode::HIDECURSOR; // without this BrowserMode::CURSOR_WO_FOCUS won't have any affect
+ m_nMode |= BrowserMode::CURSOR_WO_FOCUS;
+ }
+ else
+ {
+ if (m_nOptions & DbGridControlOptions::Update)
+ m_nMode |= BrowserMode::HIDECURSOR; // no cursor at all
+ else
+ m_nMode &= ~BrowserMode::HIDECURSOR; // at least the "non-permanent" cursor
+
+ m_nMode &= ~BrowserMode::CURSOR_WO_FOCUS;
+ }
+ SetMode(m_nMode);
+
+ bool bWasEditing = IsEditing();
+ DeactivateCell();
+ if (bWasEditing)
+ ActivateCell();
+}
+
+bool DbGridControl::IsPermanentCursorEnabled() const
+{
+ return (m_nMode & BrowserMode::CURSOR_WO_FOCUS) && !(m_nMode & BrowserMode::HIDECURSOR);
+}
+
+void DbGridControl::refreshController(sal_uInt16 _nColId, GrantControlAccess /*_aAccess*/)
+{
+ if ((GetCurColumnId() == _nColId) && IsEditing())
+ { // the controller which is currently active needs to be refreshed
+ DeactivateCell();
+ ActivateCell();
+ }
+}
+
+void DbGridControl::setDataSource(const Reference< XRowSet >& _xCursor, DbGridControlOptions nOpts)
+{
+ if (!_xCursor.is() && !m_pDataCursor)
+ return;
+
+ if (m_pDataSourcePropMultiplexer.is())
+ {
+ m_pDataSourcePropMultiplexer->dispose();
+ m_pDataSourcePropMultiplexer.clear(); // this should delete the multiplexer
+ delete m_pDataSourcePropListener;
+ m_pDataSourcePropListener = nullptr;
+ }
+ m_xRowSetListener.clear();
+
+ // is the new cursor valid ?
+ // the cursor is only valid if it contains some columns
+ // if there is no cursor or the cursor is not valid we have to clean up and leave
+ if (!_xCursor.is() || !Reference< XColumnsSupplier > (_xCursor, UNO_QUERY_THROW)->getColumns()->hasElements())
+ {
+ RemoveRows();
+ return;
+ }
+
+ // did the data cursor change?
+ sal_uInt16 nCurPos = GetColumnPos(GetCurColumnId());
+
+ SetUpdateMode(false);
+ RemoveRows();
+ DisconnectFromFields();
+
+ m_pCursorDisposeListener.reset();
+
+ {
+ ::osl::MutexGuard aGuard(m_aAdjustSafety);
+ if (m_nAsynAdjustEvent)
+ {
+ // the adjust was thought to work with the old cursor which we don't have anymore
+ RemoveUserEvent(m_nAsynAdjustEvent);
+ m_nAsynAdjustEvent = nullptr;
+ }
+ }
+
+ // get a new formatter and data cursor
+ m_xFormatter = nullptr;
+ Reference< css::util::XNumberFormatsSupplier > xSupplier = getNumberFormats(getConnection(_xCursor), true);
+ if (xSupplier.is())
+ {
+ m_xFormatter = css::util::NumberFormatter::create(m_xContext);
+ m_xFormatter->attachNumberFormatsSupplier(xSupplier);
+
+ // retrieve the datebase of the Numberformatter
+ try
+ {
+ xSupplier->getNumberFormatSettings()->getPropertyValue("NullDate") >>= m_aNullDate;
+ }
+ catch(Exception&)
+ {
+ }
+ }
+
+ m_pDataCursor.reset(new CursorWrapper(_xCursor));
+
+ // now create a cursor for painting rows
+ // we need that cursor only if we are not in insert only mode
+ Reference< XResultSet > xClone;
+ Reference< XResultSetAccess > xAccess( _xCursor, UNO_QUERY );
+ try
+ {
+ xClone = xAccess.is() ? xAccess->createResultSet() : Reference< XResultSet > ();
+ }
+ catch(Exception&)
+ {
+ }
+ if (xClone.is())
+ m_pSeekCursor.reset(new CursorWrapper(xClone));
+
+ // property listening on the data source
+ // (Normally one class would be sufficient : the multiplexer which could forward the property change to us.
+ // But for that we would have been derived from ::comphelper::OPropertyChangeListener, which isn't exported.
+ // So we introduce a second class, which is a ::comphelper::OPropertyChangeListener (in the implementation file we know this class)
+ // and forwards the property changes to our special method "DataSourcePropertyChanged".)
+ if (m_pDataCursor)
+ {
+ m_pDataSourcePropListener = new FmXGridSourcePropListener(this);
+ m_pDataSourcePropMultiplexer = new ::comphelper::OPropertyChangeMultiplexer(m_pDataSourcePropListener, m_pDataCursor->getPropertySet() );
+ m_pDataSourcePropMultiplexer->addProperty(FM_PROP_ISMODIFIED);
+ m_pDataSourcePropMultiplexer->addProperty(FM_PROP_ISNEW);
+ }
+
+ BrowserMode nOldMode = m_nMode;
+ if (m_pSeekCursor)
+ {
+ try
+ {
+ Reference< XPropertySet > xSet(_xCursor, UNO_QUERY);
+ if (xSet.is())
+ {
+ // check what kind of options are available
+ sal_Int32 nConcurrency = ResultSetConcurrency::READ_ONLY;
+ xSet->getPropertyValue(FM_PROP_RESULTSET_CONCURRENCY) >>= nConcurrency;
+
+ if ( ResultSetConcurrency::UPDATABLE == nConcurrency )
+ {
+ sal_Int32 nPrivileges = 0;
+ xSet->getPropertyValue(FM_PROP_PRIVILEGES) >>= nPrivileges;
+
+ // Insert Option should be set if insert only otherwise you won't see any rows
+ // and no insertion is possible
+ if ((m_nOptionMask & DbGridControlOptions::Insert)
+ && ((nPrivileges & Privilege::INSERT) == Privilege::INSERT) && (nOpts & DbGridControlOptions::Insert))
+ m_nOptions |= DbGridControlOptions::Insert;
+ if ((m_nOptionMask & DbGridControlOptions::Update)
+ && ((nPrivileges & Privilege::UPDATE) == Privilege::UPDATE) && (nOpts & DbGridControlOptions::Update))
+ m_nOptions |= DbGridControlOptions::Update;
+ if ((m_nOptionMask & DbGridControlOptions::Delete)
+ && ((nPrivileges & Privilege::DELETE) == Privilege::DELETE) && (nOpts & DbGridControlOptions::Delete))
+ m_nOptions |= DbGridControlOptions::Delete;
+ }
+ }
+ }
+ catch( const Exception& )
+ {
+ DBG_UNHANDLED_EXCEPTION("svx");
+ }
+
+ bool bPermanentCursor = IsPermanentCursorEnabled();
+ m_nMode = DEFAULT_BROWSE_MODE;
+
+ if ( bPermanentCursor )
+ {
+ m_nMode |= BrowserMode::CURSOR_WO_FOCUS;
+ m_nMode &= ~BrowserMode::HIDECURSOR;
+ }
+ else
+ {
+ // updates are allowed -> no focus rectangle
+ if ( m_nOptions & DbGridControlOptions::Update )
+ m_nMode |= BrowserMode::HIDECURSOR;
+ }
+
+ m_nMode |= BrowserMode::MULTISELECTION;
+
+ adjustModeForScrollbars( m_nMode, m_bNavigationBar, m_bHideScrollbars );
+
+ Reference< XColumnsSupplier > xSupplyColumns(_xCursor, UNO_QUERY);
+ if (xSupplyColumns.is())
+ InitColumnsByFields(Reference< XIndexAccess > (xSupplyColumns->getColumns(), UNO_QUERY));
+
+ ConnectToFields();
+ }
+
+ sal_uInt32 nRecordCount(0);
+
+ if (m_pSeekCursor)
+ {
+ Reference< XPropertySet > xSet = m_pDataCursor->getPropertySet();
+ xSet->getPropertyValue(FM_PROP_ROWCOUNT) >>= nRecordCount;
+ m_bRecordCountFinal = ::comphelper::getBOOL(xSet->getPropertyValue(FM_PROP_ROWCOUNTFINAL));
+
+ m_xRowSetListener = new RowSetEventListener(this);
+ Reference< XRowsChangeBroadcaster> xChangeBroad(xSet,UNO_QUERY);
+ if ( xChangeBroad.is( ) )
+ xChangeBroad->addRowsChangeListener(m_xRowSetListener);
+
+
+ // insert the currently known rows
+ // and one row if we are able to insert rows
+ if (m_nOptions & DbGridControlOptions::Insert)
+ {
+ // insert the empty row for insertion
+ m_xEmptyRow = new DbGridRow();
+ ++nRecordCount;
+ }
+ if (nRecordCount)
+ {
+ m_xPaintRow = m_xSeekRow = new DbGridRow(m_pSeekCursor.get(), true);
+ m_xDataRow = new DbGridRow(m_pDataCursor.get(), false);
+ RowInserted(0, nRecordCount, false);
+
+ if (m_xSeekRow->IsValid())
+ try
+ {
+ m_nSeekPos = m_pSeekCursor->getRow() - 1;
+ }
+ catch( const Exception& )
+ {
+ DBG_UNHANDLED_EXCEPTION("svx");
+ m_nSeekPos = -1;
+ }
+ }
+ else
+ {
+ // no rows so we don't need a seekcursor
+ m_pSeekCursor.reset();
+ }
+ }
+
+ // go to the old column
+ if (nCurPos == BROWSER_INVALIDID || nCurPos >= ColCount())
+ nCurPos = 0;
+
+ // Column zero is a valid choice and guaranteed to exist,
+ // but invisible to the user; if we have at least one
+ // user-visible column, go to that one.
+ if (nCurPos == 0 && ColCount() > 1)
+ nCurPos = 1;
+
+ // there are rows so go to the selected current column
+ if (nRecordCount)
+ GoToRowColumnId(0, GetColumnId(nCurPos));
+ // else stop the editing if necessary
+ else if (IsEditing())
+ DeactivateCell();
+
+ // now reset the mode
+ if (m_nMode != nOldMode)
+ SetMode(m_nMode);
+
+ // RecalcRows was already called while resizing
+ if (!IsResizing() && GetRowCount())
+ RecalcRows(GetTopRow(), GetVisibleRows(), true);
+
+ m_aBar->InvalidateAll(m_nCurrentPos, true);
+ SetUpdateMode(true);
+
+ // start listening on the seek cursor
+ if (m_pSeekCursor)
+ m_pCursorDisposeListener.reset(new DisposeListenerGridBridge(*this, Reference< XComponent > (Reference< XInterface >(*m_pSeekCursor), UNO_QUERY)));
+}
+
+void DbGridControl::RemoveColumns()
+{
+ if ( !isDisposed() && IsEditing() )
+ DeactivateCell();
+
+ m_aColumns.clear();
+
+ EditBrowseBox::RemoveColumns();
+}
+
+std::unique_ptr<DbGridColumn> DbGridControl::CreateColumn(sal_uInt16 nId)
+{
+ return std::unique_ptr<DbGridColumn>(new DbGridColumn(nId, *this));
+}
+
+sal_uInt16 DbGridControl::AppendColumn(const OUString& rName, sal_uInt16 nWidth, sal_uInt16 nModelPos, sal_uInt16 nId)
+{
+ DBG_ASSERT(nId == BROWSER_INVALIDID, "DbGridControl::AppendColumn : I want to set the ID myself ...");
+ sal_uInt16 nRealPos = nModelPos;
+ if (nModelPos != HEADERBAR_APPEND)
+ {
+ // calc the view pos. we can't use our converting functions because the new column
+ // has no VCL-representation, yet.
+ sal_Int16 nViewPos = nModelPos;
+ while (nModelPos--)
+ {
+ if ( m_aColumns[ nModelPos ]->IsHidden() )
+ --nViewPos;
+ }
+ // restore nModelPos, we need it later
+ nModelPos = nRealPos;
+ // the position the base class gets is the view pos + 1 (because of the handle column)
+ nRealPos = nViewPos + 1;
+ }
+
+ // calculate the new id
+ for (nId=1; (GetModelColumnPos(nId) != GRID_COLUMN_NOT_FOUND) && size_t(nId) <= m_aColumns.size(); ++nId)
+ ;
+ DBG_ASSERT(GetViewColumnPos(nId) == GRID_COLUMN_NOT_FOUND, "DbGridControl::AppendColumn : inconsistent internal state !");
+ // my column's models say "there is no column with id nId", but the view (the base class) says "there is a column ..."
+
+ EditBrowseBox::AppendColumn(rName, nWidth, nRealPos, nId);
+ if (nModelPos == HEADERBAR_APPEND)
+ m_aColumns.push_back( CreateColumn(nId) );
+ else
+ m_aColumns.insert( m_aColumns.begin() + nModelPos, CreateColumn(nId) );
+
+ return nId;
+}
+
+void DbGridControl::RemoveColumn(sal_uInt16 nId)
+{
+ EditBrowseBox::RemoveColumn(nId);
+
+ const sal_uInt16 nIndex = GetModelColumnPos(nId);
+ if(nIndex != GRID_COLUMN_NOT_FOUND)
+ {
+ m_aColumns.erase( m_aColumns.begin()+nIndex );
+ }
+}
+
+void DbGridControl::ColumnMoved(sal_uInt16 nId)
+{
+ EditBrowseBox::ColumnMoved(nId);
+
+ // remove the col from the model
+ sal_uInt16 nOldModelPos = GetModelColumnPos(nId);
+#ifdef DBG_UTIL
+ DbGridColumn* pCol = m_aColumns[ nOldModelPos ].get();
+ DBG_ASSERT(!pCol->IsHidden(), "DbGridControl::ColumnMoved : moved a hidden col ? how this ?");
+#endif
+
+ // for the new model pos we can't use GetModelColumnPos because we are altering the model at the moment
+ // so the method won't work (in fact it would return the old model pos)
+
+ // the new view pos is calculated easily
+ sal_uInt16 nNewViewPos = GetViewColumnPos(nId);
+
+ // from that we can compute the new model pos
+ size_t nNewModelPos;
+ for (nNewModelPos = 0; nNewModelPos < m_aColumns.size(); ++nNewModelPos)
+ {
+ if (!m_aColumns[ nNewModelPos ]->IsHidden())
+ {
+ if (!nNewViewPos)
+ break;
+ else
+ --nNewViewPos;
+ }
+ }
+ DBG_ASSERT( nNewModelPos < m_aColumns.size(), "DbGridControl::ColumnMoved : could not find the new model position !");
+
+ // this will work. of course the model isn't fully consistent with our view right now, but let's
+ // look at the situation : a column has been moved with in the VIEW from pos m to n, say m<n (in the
+ // other case we can use analogue arguments).
+ // All cols k with m<k<=n have been shifted left on pos, the former col m now has pos n.
+ // In the model this affects a range of cols x to y, where x<=m and y<=n. And the number of hidden cols
+ // within this range is constant, so we may calculate the view pos from the model pos in the above way.
+
+ // for instance, let's look at a grid with six columns where the third one is hidden. this will
+ // initially look like this :
+
+ // +---+---+---+---+---+---+
+ // model pos | 0 | 1 |*2*| 3 | 4 | 5 |
+ // +---+---+---+---+---+---+
+ // ID | 1 | 2 | 3 | 4 | 5 | 6 |
+ // +---+---+---+---+---+---+
+ // view pos | 0 | 1 | - | 2 | 3 | 4 |
+ // +---+---+---+---+---+---+
+
+ // if we move the column at (view) pos 1 to (view) pos 3 we have :
+
+ // +---+---+---+---+---+---+
+ // model pos | 0 | 3 |*2*| 4 | 1 | 5 | // not reflecting the changes, yet
+ // +---+---+---+---+---+---+
+ // ID | 1 | 4 | 3 | 5 | 2 | 6 | // already reflecting the changes
+ // +---+---+---+---+---+---+
+ // view pos | 0 | 1 | - | 2 | 3 | 4 |
+ // +---+---+---+---+---+---+
+
+ // or, sorted by the out-of-date model positions :
+
+ // +---+---+---+---+---+---+
+ // model pos | 0 | 1 |*2*| 3 | 4 | 5 |
+ // +---+---+---+---+---+---+
+ // ID | 1 | 2 | 3 | 4 | 5 | 6 |
+ // +---+---+---+---+---+---+
+ // view pos | 0 | 3 | - | 1 | 2 | 4 |
+ // +---+---+---+---+---+---+
+
+ // We know the new view pos (3) of the moved column because our base class tells us. So we look at our
+ // model for the 4th (the pos is zero-based) visible column, it is at (model) position 4. And this is
+ // exactly the pos where we have to re-insert our column's model, so it looks ike this :
+
+ // +---+---+---+---+---+---+
+ // model pos | 0 |*1*| 2 | 3 | 4 | 5 |
+ // +---+---+---+---+---+---+
+ // ID | 1 | 3 | 4 | 5 | 2 | 6 |
+ // +---+---+---+---+---+---+
+ // view pos | 0 | - | 1 | 2 | 3 | 4 |
+ // +---+---+---+---+---+---+
+
+ // Now, all is consistent again.
+ // (except of the hidden column : The cycling of the cols occurred on the model, not on the view. maybe
+ // the user expected the latter but there really is no good argument against our method ;) ...)
+
+ // And no, this large explanation isn't just because I wanted to play a board game or something like
+ // that. It's because it took me a while to see it myself, and the whole theme (hidden cols, model col
+ // positions, view col positions) is really painful (at least for me) so the above pictures helped me a lot ;)
+
+ auto temp = std::move(m_aColumns[ nOldModelPos ]);
+ m_aColumns.erase( m_aColumns.begin() + nOldModelPos );
+ m_aColumns.insert( m_aColumns.begin() + nNewModelPos, std::move(temp) );
+}
+
+bool DbGridControl::SeekRow(sal_Int32 nRow)
+{
+ // in filter mode or in insert only mode we don't have any cursor!
+ if ( !SeekCursor( nRow ) )
+ return false;
+
+ if ( IsFilterMode() )
+ {
+ DBG_ASSERT( IsFilterRow( nRow ), "DbGridControl::SeekRow(): No filter row, wrong mode" );
+ m_xPaintRow = m_xEmptyRow;
+ }
+ else
+ {
+ // on the current position we have to take the current row for display as we want
+ // to have the most recent values for display
+ if ( ( nRow == m_nCurrentPos ) && getDisplaySynchron() )
+ m_xPaintRow = m_xCurrentRow;
+ // seek to the empty insert row
+ else if ( IsInsertionRow( nRow ) )
+ m_xPaintRow = m_xEmptyRow;
+ else
+ {
+ m_xSeekRow->SetState( m_pSeekCursor.get(), true );
+ m_xPaintRow = m_xSeekRow;
+ }
+ }
+
+ EditBrowseBox::SeekRow(nRow);
+
+ return m_nSeekPos >= 0;
+}
+
+// Is called whenever the visible amount of data changes
+void DbGridControl::VisibleRowsChanged( sal_Int32 nNewTopRow, sal_uInt16 nLinesOnScreen )
+{
+ RecalcRows(nNewTopRow, nLinesOnScreen, false);
+}
+
+void DbGridControl::RecalcRows(sal_Int32 nNewTopRow, sal_uInt16 nLinesOnScreen, bool bUpdateCursor)
+{
+ // If no cursor -> no rows in the browser.
+ if (!m_pSeekCursor)
+ {
+ DBG_ASSERT(GetRowCount() == 0,"DbGridControl: without cursor no rows are allowed to be there");
+ return;
+ }
+
+ // ignore any implicitly made updates
+ bool bDisablePaint = !bUpdateCursor && IsPaintEnabled();
+ if (bDisablePaint)
+ EnablePaint(false);
+
+ // adjust cache to the visible area
+ Reference< XPropertySet > xSet = m_pSeekCursor->getPropertySet();
+ sal_Int32 nCacheSize = 0;
+ xSet->getPropertyValue(FM_PROP_FETCHSIZE) >>= nCacheSize;
+ bool bCacheAligned = false;
+ // no further cursor movements after initializing (m_nSeekPos < 0) because it is already
+ // positioned on the first sentence
+ tools::Long nDelta = nNewTopRow - GetTopRow();
+ // limit for relative positioning
+ tools::Long nLimit = nCacheSize ? nCacheSize / 2 : 0;
+
+ // more lines on screen than in cache
+ if (nLimit < nLinesOnScreen)
+ {
+ Any aCacheSize;
+ aCacheSize <<= sal_Int32(nLinesOnScreen*2);
+ xSet->setPropertyValue(FM_PROP_FETCHSIZE, aCacheSize);
+ // here we need to update the cursor for sure
+ bUpdateCursor = true;
+ bCacheAligned = true;
+ nLimit = nLinesOnScreen;
+ }
+
+ // In the following, all positionings are done as it is
+ // ensured that there are enough lines in the data cache
+
+ // window goes downwards with less than two windows difference or
+ // the cache was updated and no rowcount yet
+ if (nDelta < nLimit && (nDelta > 0
+ || (bCacheAligned && m_nTotalCount < 0)) )
+ SeekCursor(nNewTopRow + nLinesOnScreen - 1);
+ else if (nDelta < 0 && std::abs(nDelta) < nLimit)
+ SeekCursor(nNewTopRow);
+ else if (nDelta != 0 || bUpdateCursor)
+ SeekCursor(nNewTopRow, true);
+
+ AdjustRows();
+
+ // ignore any updates implicit made
+ EnablePaint(true);
+}
+
+void DbGridControl::RowInserted(sal_Int32 nRow, sal_Int32 nNumRows, bool bDoPaint)
+{
+ if (!nNumRows)
+ return;
+
+ if (m_bRecordCountFinal && m_nTotalCount < 0)
+ {
+ // if we have an insert row we have to reduce to count by 1
+ // as the total count reflects only the existing rows in database
+ m_nTotalCount = GetRowCount() + nNumRows;
+ if (m_xEmptyRow.is())
+ --m_nTotalCount;
+ }
+ else if (m_nTotalCount >= 0)
+ m_nTotalCount += nNumRows;
+
+ EditBrowseBox::RowInserted(nRow, nNumRows, bDoPaint);
+ m_aBar->InvalidateState(DbGridControlNavigationBarState::Count);
+}
+
+void DbGridControl::RowRemoved(sal_Int32 nRow, sal_Int32 nNumRows, bool bDoPaint)
+{
+ if (!nNumRows)
+ return;
+
+ if (m_bRecordCountFinal && m_nTotalCount < 0)
+ {
+ m_nTotalCount = GetRowCount() - nNumRows;
+ // if we have an insert row reduce by 1
+ if (m_xEmptyRow.is())
+ --m_nTotalCount;
+ }
+ else if (m_nTotalCount >= 0)
+ m_nTotalCount -= nNumRows;
+
+ EditBrowseBox::RowRemoved(nRow, nNumRows, bDoPaint);
+ m_aBar->InvalidateState(DbGridControlNavigationBarState::Count);
+}
+
+void DbGridControl::AdjustRows()
+{
+ if (!m_pSeekCursor)
+ return;
+
+ Reference< XPropertySet > xSet = m_pDataCursor->getPropertySet();
+
+ // refresh RecordCount
+ sal_Int32 nRecordCount = 0;
+ xSet->getPropertyValue(FM_PROP_ROWCOUNT) >>= nRecordCount;
+ if (!m_bRecordCountFinal)
+ m_bRecordCountFinal = ::comphelper::getBOOL(xSet->getPropertyValue(FM_PROP_ROWCOUNTFINAL));
+
+ // Did the number of rows change?
+ // Here we need to consider that there might be an additional row for adding new data sets
+
+ // add additional AppendRow for insertion
+ if (m_nOptions & DbGridControlOptions::Insert)
+ ++nRecordCount;
+
+ // If there is currently an insertion, so do not consider this added row in RecordCount or Appendrow
+ if (!IsUpdating() && m_bRecordCountFinal && IsModified() && m_xCurrentRow != m_xEmptyRow &&
+ m_xCurrentRow->IsNew())
+ ++nRecordCount;
+ // ensured with !m_bUpdating: otherwise the edited data set (that SaveRow added and why this
+ // method was called) would be called twice (if m_bUpdating == sal_True): once in RecordCount
+ // and a second time here (60787 - FS)
+
+ if (nRecordCount != GetRowCount())
+ {
+ tools::Long nDelta = GetRowCount() - static_cast<tools::Long>(nRecordCount);
+ if (nDelta > 0) // too many
+ {
+ RowRemoved(GetRowCount() - nDelta, nDelta, false);
+ // some rows are gone, thus, repaint starting at the current position
+ Invalidate();
+
+ sal_Int32 nNewPos = AlignSeekCursor();
+ if (m_bSynchDisplay)
+ EditBrowseBox::GoToRow(nNewPos);
+
+ SetCurrent(nNewPos);
+ // there are rows so go to the selected current column
+ if (nRecordCount)
+ GoToRowColumnId(nNewPos, GetColumnId(GetCurColumnId()));
+ if (!IsResizing() && GetRowCount())
+ RecalcRows(GetTopRow(), GetVisibleRows(), true);
+ m_aBar->InvalidateAll(m_nCurrentPos, true);
+ }
+ else // too few
+ RowInserted(GetRowCount(), -nDelta);
+ }
+
+ if (m_bRecordCountFinal && m_nTotalCount < 0)
+ {
+ if (m_nOptions & DbGridControlOptions::Insert)
+ m_nTotalCount = GetRowCount() - 1;
+ else
+ m_nTotalCount = GetRowCount();
+ }
+ m_aBar->InvalidateState(DbGridControlNavigationBarState::Count);
+}
+
+svt::EditBrowseBox::RowStatus DbGridControl::GetRowStatus(sal_Int32 nRow) const
+{
+ if (IsFilterRow(nRow))
+ return EditBrowseBox::FILTER;
+ else if (m_nCurrentPos >= 0 && nRow == m_nCurrentPos)
+ {
+ // new row
+ if (!IsValid(m_xCurrentRow))
+ return EditBrowseBox::DELETED;
+ else if (IsModified())
+ return EditBrowseBox::MODIFIED;
+ else if (m_xCurrentRow->IsNew())
+ return EditBrowseBox::CURRENTNEW;
+ else
+ return EditBrowseBox::CURRENT;
+ }
+ else if (IsInsertionRow(nRow))
+ return EditBrowseBox::NEW;
+ else if (!IsValid(m_xSeekRow))
+ return EditBrowseBox::DELETED;
+ else
+ return EditBrowseBox::CLEAN;
+}
+
+void DbGridControl::PaintCell(OutputDevice& rDev, const tools::Rectangle& rRect, sal_uInt16 nColumnId) const
+{
+ if (!IsValid(m_xPaintRow))
+ return;
+
+ size_t Location = GetModelColumnPos(nColumnId);
+ DbGridColumn* pColumn = (Location < m_aColumns.size() ) ? m_aColumns[ Location ].get() : nullptr;
+ if (pColumn)
+ {
+ tools::Rectangle aArea(rRect);
+ if ((GetMode() & BrowserMode::CURSOR_WO_FOCUS) == BrowserMode::CURSOR_WO_FOCUS)
+ {
+ aArea.AdjustTop(1 );
+ aArea.AdjustBottom( -1 );
+ }
+ pColumn->Paint(rDev, aArea, m_xPaintRow.get(), getNumberFormatter());
+ }
+}
+
+bool DbGridControl::CursorMoving(sal_Int32 nNewRow, sal_uInt16 nNewCol)
+{
+
+ DeactivateCell( false );
+
+ if ( m_pDataCursor
+ && ( m_nCurrentPos != nNewRow )
+ && !SetCurrent( nNewRow )
+ )
+ {
+ ActivateCell();
+ return false;
+ }
+
+ return EditBrowseBox::CursorMoving( nNewRow, nNewCol );
+}
+
+bool DbGridControl::SetCurrent(sal_Int32 nNewRow)
+{
+ // Each movement of the datacursor must start with BeginCursorAction and end with
+ // EndCursorAction to block all notifications during the movement
+ BeginCursorAction();
+
+ try
+ {
+ // compare positions
+ if (SeekCursor(nNewRow))
+ {
+ if (IsFilterRow(nNewRow)) // special mode for filtering
+ {
+ m_xCurrentRow = m_xDataRow = m_xPaintRow = m_xEmptyRow;
+ m_nCurrentPos = nNewRow;
+ }
+ else
+ {
+ bool bNewRowInserted = false;
+ // Should we go to the insertrow ?
+ if (IsInsertionRow(nNewRow))
+ {
+ // to we need to move the cursor to the insert row?
+ // we need to insert the if the current row isn't the insert row or if the
+ // cursor triggered the move by itself and we need a reinitialization of the row
+ Reference< XPropertySet > xCursorProps = m_pDataCursor->getPropertySet();
+ if ( !::comphelper::getBOOL(xCursorProps->getPropertyValue(FM_PROP_ISNEW)) )
+ {
+ Reference< XResultSetUpdate > xUpdateCursor(Reference< XInterface >(*m_pDataCursor), UNO_QUERY);
+ xUpdateCursor->moveToInsertRow();
+ }
+ bNewRowInserted = true;
+ }
+ else
+ {
+
+ if ( !m_pSeekCursor->isBeforeFirst() && !m_pSeekCursor->isAfterLast() )
+ {
+ Any aBookmark = m_pSeekCursor->getBookmark();
+ if (!m_xCurrentRow.is() || m_xCurrentRow->IsNew() || !CompareBookmark(aBookmark, m_pDataCursor->getBookmark()))
+ {
+ // adjust the cursor to the new desired row
+ if (!m_pDataCursor->moveToBookmark(aBookmark))
+ {
+ EndCursorAction();
+ return false;
+ }
+ }
+ }
+ }
+ m_xDataRow->SetState(m_pDataCursor.get(), false);
+ m_xCurrentRow = m_xDataRow;
+
+ tools::Long nPaintPos = -1;
+ // do we have to repaint the last regular row in case of setting defaults or autovalues
+ if (m_nCurrentPos >= 0 && m_nCurrentPos >= (GetRowCount() - 2))
+ nPaintPos = m_nCurrentPos;
+
+ m_nCurrentPos = nNewRow;
+
+ // repaint the new row to display all defaults
+ if (bNewRowInserted)
+ RowModified(m_nCurrentPos);
+ if (nPaintPos >= 0)
+ RowModified(nPaintPos);
+ }
+ }
+ else
+ {
+ OSL_FAIL("DbGridControl::SetCurrent : SeekRow failed !");
+ EndCursorAction();
+ return false;
+ }
+ }
+ catch ( const Exception& )
+ {
+ DBG_UNHANDLED_EXCEPTION("svx");
+ EndCursorAction();
+ return false;
+ }
+
+ EndCursorAction();
+ return true;
+}
+
+void DbGridControl::CursorMoved()
+{
+
+ // cursor movement due to deletion or insertion of rows
+ if (m_pDataCursor && m_nCurrentPos != GetCurRow())
+ {
+ DeactivateCell();
+ SetCurrent(GetCurRow());
+ }
+
+ EditBrowseBox::CursorMoved();
+ m_aBar->InvalidateAll(m_nCurrentPos);
+
+ // select the new column when they moved
+ if ( IsDesignMode() && GetSelectedColumnCount() > 0 && GetCurColumnId() )
+ {
+ SelectColumnId( GetCurColumnId() );
+ }
+
+ if ( m_nLastColId != GetCurColumnId() )
+ onColumnChange();
+ m_nLastColId = GetCurColumnId();
+
+ if ( m_nLastRowId != GetCurRow() )
+ onRowChange();
+ m_nLastRowId = GetCurRow();
+}
+
+void DbGridControl::onRowChange()
+{
+ // not interested in
+}
+
+void DbGridControl::onColumnChange()
+{
+ if ( m_pGridListener )
+ m_pGridListener->columnChanged();
+}
+
+void DbGridControl::setDisplaySynchron(bool bSync)
+{
+ if (bSync != m_bSynchDisplay)
+ {
+ m_bSynchDisplay = bSync;
+ if (m_bSynchDisplay)
+ AdjustDataSource();
+ }
+}
+
+void DbGridControl::AdjustDataSource(bool bFull)
+{
+ SAL_INFO("svx.fmcomp", "DbGridControl::AdjustDataSource");
+ SolarMutexGuard aGuard;
+ // If the current row is recalculated at the moment, do not adjust
+
+ if (bFull)
+ m_xCurrentRow = nullptr;
+ // if we are on the same row only repaint
+ // but this is only possible for rows which are not inserted, in that case the comparison result
+ // may not be correct
+ else
+ if ( m_xCurrentRow.is()
+ && !m_xCurrentRow->IsNew()
+ && !m_pDataCursor->isBeforeFirst()
+ && !m_pDataCursor->isAfterLast()
+ && !m_pDataCursor->rowDeleted()
+ )
+ {
+ bool bEqualBookmarks = CompareBookmark( m_xCurrentRow->GetBookmark(), m_pDataCursor->getBookmark() );
+
+ bool bDataCursorIsOnNew = false;
+ m_pDataCursor->getPropertySet()->getPropertyValue( FM_PROP_ISNEW ) >>= bDataCursorIsOnNew;
+
+ if ( bEqualBookmarks && !bDataCursorIsOnNew )
+ {
+ // position of my data cursor is the same as the position our current row points tpo
+ // sync the status, repaint, done
+ DBG_ASSERT(m_xDataRow == m_xCurrentRow, "Errors in the data row");
+ SAL_INFO("svx.fmcomp", "same position, new state: " << ROWSTATUS(m_xCurrentRow));
+ RowModified(m_nCurrentPos);
+ return;
+ }
+ }
+
+ // away from the data cursor's row
+ if (m_xPaintRow == m_xCurrentRow)
+ m_xPaintRow = m_xSeekRow;
+
+ // not up-to-date row, thus, adjust completely
+ if (!m_xCurrentRow.is())
+ AdjustRows();
+
+ sal_Int32 nNewPos = AlignSeekCursor();
+ if (nNewPos < 0)// could not find any position
+ return;
+
+ if (nNewPos != m_nCurrentPos)
+ {
+ if (m_bSynchDisplay)
+ EditBrowseBox::GoToRow(nNewPos);
+
+ if (!m_xCurrentRow.is())
+ // Happens e.g. when deleting the n last datasets (n>1) while the cursor was positioned
+ // on the last one. In this case, AdjustRows deletes two rows from BrowseBox, by what
+ // CurrentRow is corrected to point two rows down, so that GoToRow will point into
+ // emptiness (since we are - purportedly - at the correct position)
+ SetCurrent(nNewPos);
+ }
+ else
+ {
+ SetCurrent(nNewPos);
+ RowModified(nNewPos);
+ }
+
+ // if the data cursor was moved from outside, this section is voided
+ SetNoSelection();
+ m_aBar->InvalidateAll(m_nCurrentPos, m_xCurrentRow.is());
+}
+
+sal_Int32 DbGridControl::AlignSeekCursor()
+{
+ // position SeekCursor onto the data cursor, no data transmission
+
+ if (!m_pSeekCursor)
+ return -1;
+
+ Reference< XPropertySet > xSet = m_pDataCursor->getPropertySet();
+
+ // now align the seek cursor and the data cursor
+ if (::comphelper::getBOOL(xSet->getPropertyValue(FM_PROP_ISNEW)))
+ m_nSeekPos = GetRowCount() - 1;
+ else
+ {
+ try
+ {
+ if ( m_pDataCursor->isBeforeFirst() )
+ {
+ // this is somewhat strange, but can nevertheless happen
+ SAL_INFO( "svx.fmcomp", "DbGridControl::AlignSeekCursor: nobody should tamper with my cursor this way (before first)!" );
+ m_pSeekCursor->first();
+ m_pSeekCursor->previous();
+ m_nSeekPos = -1;
+ }
+ else if ( m_pDataCursor->isAfterLast() )
+ {
+ SAL_INFO( "svx.fmcomp", "DbGridControl::AlignSeekCursor: nobody should tamper with my cursor this way (after last)!" );
+ m_pSeekCursor->last();
+ m_pSeekCursor->next();
+ m_nSeekPos = -1;
+ }
+ else
+ {
+ m_pSeekCursor->moveToBookmark(m_pDataCursor->getBookmark());
+ if (!CompareBookmark(m_pDataCursor->getBookmark(), m_pSeekCursor->getBookmark()))
+ // unfortunately, moveToBookmark might lead to a re-positioning of the seek
+ // cursor (if the complex moveToBookmark with all its events fires an update
+ // somewhere) -> retry
+ m_pSeekCursor->moveToBookmark(m_pDataCursor->getBookmark());
+ // Now there is still the chance of a failure but it is less likely.
+ // The alternative would be a loop until everything is fine - no good solution...
+ m_nSeekPos = m_pSeekCursor->getRow() - 1;
+ }
+ }
+ catch(Exception&)
+ {
+ }
+ }
+ return m_nSeekPos;
+}
+
+bool DbGridControl::SeekCursor(sal_Int32 nRow, bool bAbsolute)
+{
+ // position SeekCursor onto the data cursor, no data transmission
+
+ // additions for the filtermode
+ if (IsFilterRow(nRow))
+ {
+ m_nSeekPos = 0;
+ return true;
+ }
+
+ if (!m_pSeekCursor)
+ return false;
+
+ // is this an insertion?
+ if (IsValid(m_xCurrentRow) && m_xCurrentRow->IsNew() &&
+ nRow >= m_nCurrentPos)
+ {
+ // if so, scrolling down must be prevented as this is already the last data set!
+ if (nRow == m_nCurrentPos)
+ {
+ // no adjustment necessary
+ m_nSeekPos = nRow;
+ }
+ else if (IsInsertionRow(nRow)) // blank row for data insertion
+ m_nSeekPos = nRow;
+ }
+ else if (IsInsertionRow(nRow)) // blank row for data insertion
+ m_nSeekPos = nRow;
+ else if ((-1 == nRow) && (GetRowCount() == ((m_nOptions & DbGridControlOptions::Insert) ? 1 : 0)) && m_pSeekCursor->isAfterLast())
+ m_nSeekPos = nRow;
+ else
+ {
+ bool bSuccess = false;
+ tools::Long nSteps = 0;
+ try
+ {
+ if ( m_pSeekCursor->rowDeleted() )
+ {
+ // somebody deleted the current row of the seek cursor. Move it away from this row.
+ m_pSeekCursor->next();
+ if ( m_pSeekCursor->isAfterLast() || m_pSeekCursor->isBeforeFirst() )
+ bAbsolute = true;
+ }
+
+ if ( !bAbsolute )
+ {
+ DBG_ASSERT( !m_pSeekCursor->isAfterLast() && !m_pSeekCursor->isBeforeFirst(),
+ "DbGridControl::SeekCursor: how did the seek cursor get to this position?!" );
+ nSteps = nRow - (m_pSeekCursor->getRow() - 1);
+ bAbsolute = std::abs(nSteps) > 100;
+ }
+
+ if ( bAbsolute )
+ {
+ bSuccess = m_pSeekCursor->absolute(nRow + 1);
+ if (bSuccess)
+ m_nSeekPos = nRow;
+ }
+ else
+ {
+ if (nSteps > 0) // position onto the last needed data set
+ {
+ if (m_pSeekCursor->isAfterLast())
+ bSuccess = false;
+ else if (m_pSeekCursor->isBeforeFirst())
+ bSuccess = m_pSeekCursor->absolute(nSteps);
+ else
+ bSuccess = m_pSeekCursor->relative(nSteps);
+ }
+ else if (nSteps < 0)
+ {
+ if (m_pSeekCursor->isBeforeFirst())
+ bSuccess = false;
+ else if (m_pSeekCursor->isAfterLast())
+ bSuccess = m_pSeekCursor->absolute(nSteps);
+ else
+ bSuccess = m_pSeekCursor->relative(nSteps);
+ }
+ else
+ {
+ m_nSeekPos = nRow;
+ return true;
+ }
+ }
+ }
+ catch(Exception&)
+ {
+ OSL_FAIL("DbGridControl::SeekCursor : failed ...");
+ }
+
+ try
+ {
+ if (!bSuccess)
+ {
+ if (bAbsolute || nSteps > 0)
+ {
+ if (m_pSeekCursor->isLast())
+ bSuccess = true;
+ else
+ bSuccess = m_pSeekCursor->last();
+ }
+ else
+ {
+ if (m_pSeekCursor->isFirst())
+ bSuccess = true;
+ else
+ bSuccess = m_pSeekCursor->first();
+ }
+ }
+
+ if (bSuccess)
+ m_nSeekPos = m_pSeekCursor->getRow() - 1;
+ else
+ m_nSeekPos = -1;
+ }
+ catch(Exception&)
+ {
+ DBG_UNHANDLED_EXCEPTION("svx");
+ OSL_FAIL("DbGridControl::SeekCursor : failed ...");
+ m_nSeekPos = -1; // no further data set available
+ }
+ }
+ return m_nSeekPos == nRow;
+}
+
+void DbGridControl::MoveToFirst()
+{
+ if (m_pSeekCursor && (GetCurRow() != 0))
+ MoveToPosition(0);
+}
+
+void DbGridControl::MoveToLast()
+{
+ if (!m_pSeekCursor)
+ return;
+
+ if (m_nTotalCount < 0) // no RecordCount, yet
+ {
+ try
+ {
+ bool bRes = m_pSeekCursor->last();
+
+ if (bRes)
+ {
+ m_nSeekPos = m_pSeekCursor->getRow() - 1;
+ AdjustRows();
+ }
+ }
+ catch(Exception&)
+ {
+ }
+ }
+
+ // position onto the last data set not on a blank row
+ if (m_nOptions & DbGridControlOptions::Insert)
+ {
+ if ((GetRowCount() - 1) > 0)
+ MoveToPosition(GetRowCount() - 2);
+ }
+ else if (GetRowCount())
+ MoveToPosition(GetRowCount() - 1);
+}
+
+void DbGridControl::MoveToPrev()
+{
+ sal_Int32 nNewRow = std::max(GetCurRow() - 1, sal_Int32(0));
+ if (GetCurRow() != nNewRow)
+ MoveToPosition(nNewRow);
+}
+
+void DbGridControl::MoveToNext()
+{
+ if (!m_pSeekCursor)
+ return;
+
+ if (m_nTotalCount > 0)
+ {
+ // move the data cursor to the right position
+ tools::Long nNewRow = std::min(GetRowCount() - 1, GetCurRow() + 1);
+ if (GetCurRow() != nNewRow)
+ MoveToPosition(nNewRow);
+ }
+ else
+ {
+ bool bOk = false;
+ try
+ {
+ // try to move to next row
+ // when not possible our paint cursor is already on the last row
+ // then we must be sure that the data cursor is on the position
+ // we call ourself again
+ bOk = m_pSeekCursor->next();
+ if (bOk)
+ {
+ m_nSeekPos = m_pSeekCursor->getRow() - 1;
+ MoveToPosition(GetCurRow() + 1);
+ }
+ }
+ catch(SQLException &)
+ {
+ DBG_UNHANDLED_EXCEPTION("svx");
+ }
+
+ if(!bOk)
+ {
+ AdjustRows();
+ if (m_nTotalCount > 0) // only to avoid infinite recursion
+ MoveToNext();
+ }
+ }
+}
+
+void DbGridControl::MoveToPosition(sal_uInt32 nPos)
+{
+ if (!m_pSeekCursor)
+ return;
+
+ if (m_nTotalCount < 0 && static_cast<tools::Long>(nPos) >= GetRowCount())
+ {
+ try
+ {
+ if (!m_pSeekCursor->absolute(nPos + 1))
+ {
+ AdjustRows();
+ return;
+ }
+ else
+ {
+ m_nSeekPos = m_pSeekCursor->getRow() - 1;
+ AdjustRows();
+ }
+ }
+ catch(Exception&)
+ {
+ return;
+ }
+ }
+ EditBrowseBox::GoToRow(nPos);
+ m_aBar->InvalidateAll(m_nCurrentPos);
+}
+
+void DbGridControl::AppendNew()
+{
+ if (!m_pSeekCursor || !(m_nOptions & DbGridControlOptions::Insert))
+ return;
+
+ if (m_nTotalCount < 0) // no RecordCount, yet
+ {
+ try
+ {
+ bool bRes = m_pSeekCursor->last();
+
+ if (bRes)
+ {
+ m_nSeekPos = m_pSeekCursor->getRow() - 1;
+ AdjustRows();
+ }
+ }
+ catch(Exception&)
+ {
+ return;
+ }
+ }
+
+ tools::Long nNewRow = m_nTotalCount + 1;
+ if (nNewRow > 0 && GetCurRow() != nNewRow)
+ MoveToPosition(nNewRow - 1);
+}
+
+void DbGridControl::SetDesignMode(bool bMode)
+{
+ if (IsDesignMode() == bMode)
+ return;
+
+ // adjust Enable/Disable for design mode so that the headerbar remains configurable
+ if (bMode)
+ {
+ if (!IsEnabled())
+ {
+ Enable();
+ GetDataWindow().Disable();
+ }
+ }
+ else
+ {
+ // disable completely
+ if (!GetDataWindow().IsEnabled())
+ Disable();
+ }
+
+ m_bDesignMode = bMode;
+ GetDataWindow().SetMouseTransparent(bMode);
+ SetMouseTransparent(bMode);
+
+ m_aBar->InvalidateAll(m_nCurrentPos, true);
+}
+
+void DbGridControl::SetFilterMode(bool bMode)
+{
+ if (IsFilterMode() == bMode)
+ return;
+
+ m_bFilterMode = bMode;
+
+ if (bMode)
+ {
+ SetUpdateMode(false);
+
+ // there is no cursor anymore
+ if (IsEditing())
+ DeactivateCell();
+ RemoveRows(false);
+
+ m_xEmptyRow = new DbGridRow();
+
+ // setting the new filter controls
+ for (auto const & pCurCol : m_aColumns)
+ {
+ if (!pCurCol->IsHidden())
+ pCurCol->UpdateControl();
+ }
+
+ // one row for filtering
+ RowInserted(0);
+ SetUpdateMode(true);
+ }
+ else
+ setDataSource(Reference< XRowSet > ());
+}
+
+OUString DbGridControl::GetCellText(sal_Int32 _nRow, sal_uInt16 _nColId) const
+{
+ size_t Location = GetModelColumnPos( _nColId );
+ DbGridColumn* pColumn = ( Location < m_aColumns.size() ) ? m_aColumns[ Location ].get() : nullptr;
+ OUString sRet;
+ if ( const_cast<DbGridControl*>(this)->SeekRow(_nRow) )
+ sRet = GetCurrentRowCellText(pColumn, m_xPaintRow);
+ return sRet;
+}
+
+OUString DbGridControl::GetCurrentRowCellText(DbGridColumn const * pColumn,const DbGridRowRef& _rRow) const
+{
+ // text output for a single row
+ OUString aText;
+ if ( pColumn && IsValid(_rRow) )
+ aText = pColumn->GetCellText(_rRow.get(), m_xFormatter);
+ return aText;
+}
+
+sal_uInt32 DbGridControl::GetTotalCellWidth(sal_Int32 nRow, sal_uInt16 nColId)
+{
+ if (SeekRow(nRow))
+ {
+ size_t Location = GetModelColumnPos( nColId );
+ DbGridColumn* pColumn = ( Location < m_aColumns.size() ) ? m_aColumns[ Location ].get() : nullptr;
+ return GetDataWindow().GetTextWidth(GetCurrentRowCellText(pColumn,m_xPaintRow));
+ }
+ else
+ return 30; // FIXME magic number for default cell width
+}
+
+void DbGridControl::PreExecuteRowContextMenu(weld::Menu& rMenu)
+{
+ bool bDelete = (m_nOptions & DbGridControlOptions::Delete) && GetSelectRowCount() && !IsCurrentAppending();
+ // if only a blank row is selected then do not delete
+ bDelete = bDelete && !((m_nOptions & DbGridControlOptions::Insert) && GetSelectRowCount() == 1 && IsRowSelected(GetRowCount() - 1));
+
+ rMenu.set_visible("delete", bDelete);
+ rMenu.set_visible("save", IsModified());
+
+ // the undo is more difficult
+ bool bCanUndo = IsModified();
+ int nState = -1;
+ if (m_aMasterStateProvider.IsSet())
+ nState = m_aMasterStateProvider.Call(DbGridControlNavigationBarState::Undo);
+ bCanUndo &= ( 0 != nState );
+
+ rMenu.set_visible("undo", bCanUndo);
+}
+
+void DbGridControl::PostExecuteRowContextMenu(const OString& rExecutionResult)
+{
+ if (rExecutionResult == "delete")
+ {
+ // delete asynchronously
+ if (m_nDeleteEvent)
+ Application::RemoveUserEvent(m_nDeleteEvent);
+ m_nDeleteEvent = Application::PostUserEvent(LINK(this,DbGridControl,OnDelete), nullptr, true);
+ }
+ else if (rExecutionResult == "undo")
+ Undo();
+ else if (rExecutionResult == "save")
+ SaveRow();
+}
+
+void DbGridControl::DataSourcePropertyChanged(const PropertyChangeEvent& evt)
+{
+ SAL_INFO("svx.fmcomp", "DbGridControl::DataSourcePropertyChanged");
+ SolarMutexGuard aGuard;
+ // prop "IsModified" changed ?
+ // during update don't care about the modified state
+ if (IsUpdating() || evt.PropertyName != FM_PROP_ISMODIFIED)
+ return;
+
+ Reference< XPropertySet > xSource(evt.Source, UNO_QUERY);
+ DBG_ASSERT( xSource.is(), "DbGridControl::DataSourcePropertyChanged: invalid event source!" );
+ bool bIsNew = false;
+ if (xSource.is())
+ bIsNew = ::comphelper::getBOOL(xSource->getPropertyValue(FM_PROP_ISNEW));
+
+ if (bIsNew && m_xCurrentRow.is())
+ {
+ DBG_ASSERT(::comphelper::getBOOL(xSource->getPropertyValue(FM_PROP_ROWCOUNTFINAL)), "DbGridControl::DataSourcePropertyChanged : somebody moved the form to a new record before the row count was final !");
+ sal_Int32 nRecordCount = 0;
+ xSource->getPropertyValue(FM_PROP_ROWCOUNT) >>= nRecordCount;
+ if (::comphelper::getBOOL(evt.NewValue))
+ { // modified state changed from sal_False to sal_True and we're on an insert row
+ // -> we've to add a new grid row
+ if ((nRecordCount == GetRowCount() - 1) && m_xCurrentRow->IsNew())
+ {
+ RowInserted(GetRowCount());
+ InvalidateStatusCell(m_nCurrentPos);
+ m_aBar->InvalidateAll(m_nCurrentPos);
+ }
+ }
+ else
+ { // modified state changed from sal_True to sal_False and we're on an insert row
+ // we have two "new row"s at the moment : the one we're editing currently (where the current
+ // column is the only dirty element) and a "new new" row which is completely clean. As the first
+ // one is about to be cleaned, too, the second one is obsolete now.
+ if (m_xCurrentRow->IsNew() && nRecordCount == (GetRowCount() - 2))
+ {
+ RowRemoved(GetRowCount() - 1);
+ InvalidateStatusCell(m_nCurrentPos);
+ m_aBar->InvalidateAll(m_nCurrentPos);
+ }
+ }
+ }
+ if (m_xCurrentRow.is())
+ {
+ m_xCurrentRow->SetStatus(::comphelper::getBOOL(evt.NewValue) ? GridRowStatus::Modified : GridRowStatus::Clean);
+ m_xCurrentRow->SetNew( bIsNew );
+ InvalidateStatusCell(m_nCurrentPos);
+ SAL_INFO("svx.fmcomp", "modified flag changed, new state: " << ROWSTATUS(m_xCurrentRow));
+ }
+}
+
+void DbGridControl::StartDrag( sal_Int8 /*nAction*/, const Point& rPosPixel )
+{
+ if (!m_pSeekCursor || IsResizing())
+ return;
+
+ sal_uInt16 nColId = GetColumnId(GetColumnAtXPosPixel(rPosPixel.X()));
+ tools::Long nRow = GetRowAtYPosPixel(rPosPixel.Y());
+ if (nColId != HandleColumnId && nRow >= 0)
+ {
+ if (GetDataWindow().IsMouseCaptured())
+ GetDataWindow().ReleaseMouse();
+
+ size_t Location = GetModelColumnPos( nColId );
+ DbGridColumn* pColumn = ( Location < m_aColumns.size() ) ? m_aColumns[ Location ].get() : nullptr;
+ rtl::Reference<OStringTransferable> pTransferable = new OStringTransferable(GetCurrentRowCellText(pColumn,m_xPaintRow));
+ pTransferable->StartDrag(this, DND_ACTION_COPY);
+ }
+}
+
+bool DbGridControl::canCopyCellText(sal_Int32 _nRow, sal_uInt16 _nColId)
+{
+ return (_nRow >= 0)
+ && (_nRow < GetRowCount())
+ && (_nColId != HandleColumnId)
+ && (GetModelColumnPos(_nColId) != GRID_COLUMN_NOT_FOUND);
+}
+
+void DbGridControl::copyCellText(sal_Int32 _nRow, sal_uInt16 _nColId)
+{
+ DBG_ASSERT(canCopyCellText(_nRow, _nColId), "DbGridControl::copyCellText: invalid call!");
+ DbGridColumn* pColumn = m_aColumns[ GetModelColumnPos(_nColId) ].get();
+ SeekRow(_nRow);
+ OStringTransfer::CopyString( GetCurrentRowCellText( pColumn,m_xPaintRow ), this );
+}
+
+void DbGridControl::executeRowContextMenu(const Point& _rPreferredPos)
+{
+ std::unique_ptr<weld::Builder> xBuilder(Application::CreateBuilder(nullptr, "svx/ui/rowsmenu.ui"));
+ std::unique_ptr<weld::Menu> xContextMenu(xBuilder->weld_menu("menu"));
+
+ tools::Rectangle aRect(_rPreferredPos, Size(1,1));
+ weld::Window* pParent = weld::GetPopupParent(*this, aRect);
+
+ PreExecuteRowContextMenu(*xContextMenu);
+ PostExecuteRowContextMenu(xContextMenu->popup_at_rect(pParent, aRect));
+}
+
+void DbGridControl::Command(const CommandEvent& rEvt)
+{
+ switch (rEvt.GetCommand())
+ {
+ case CommandEventId::ContextMenu:
+ {
+ if ( !m_pSeekCursor )
+ {
+ EditBrowseBox::Command(rEvt);
+ return;
+ }
+
+ if ( !rEvt.IsMouseEvent() )
+ { // context menu requested by keyboard
+ if ( GetSelectRowCount() )
+ {
+ tools::Long nRow = FirstSelectedRow( );
+
+ ::tools::Rectangle aRowRect( GetRowRectPixel( nRow ) );
+ executeRowContextMenu(aRowRect.LeftCenter());
+
+ // handled
+ return;
+ }
+ }
+
+ sal_uInt16 nColId = GetColumnId(GetColumnAtXPosPixel(rEvt.GetMousePosPixel().X()));
+ tools::Long nRow = GetRowAtYPosPixel(rEvt.GetMousePosPixel().Y());
+
+ if (nColId == HandleColumnId)
+ {
+ executeRowContextMenu(rEvt.GetMousePosPixel());
+ }
+ else if (canCopyCellText(nRow, nColId))
+ {
+ ::tools::Rectangle aRect(rEvt.GetMousePosPixel(), Size(1, 1));
+ weld::Window* pPopupParent = weld::GetPopupParent(*this, aRect);
+ std::unique_ptr<weld::Builder> xBuilder(Application::CreateBuilder(pPopupParent, "svx/ui/cellmenu.ui"));
+ std::unique_ptr<weld::Menu> xContextMenu(xBuilder->weld_menu("menu"));
+ if (!xContextMenu->popup_at_rect(pPopupParent, aRect).isEmpty())
+ copyCellText(nRow, nColId);
+ }
+ else
+ {
+ EditBrowseBox::Command(rEvt);
+ return;
+ }
+
+ [[fallthrough]];
+ }
+ default:
+ EditBrowseBox::Command(rEvt);
+ }
+}
+
+IMPL_LINK_NOARG(DbGridControl, OnDelete, void*, void)
+{
+ m_nDeleteEvent = nullptr;
+ DeleteSelectedRows();
+}
+
+void DbGridControl::DeleteSelectedRows()
+{
+ DBG_ASSERT(GetSelection(), "no selection!!!");
+
+ if (!m_pSeekCursor)
+ return;
+}
+
+CellController* DbGridControl::GetController(sal_Int32 /*nRow*/, sal_uInt16 nColumnId)
+{
+ if (!IsValid(m_xCurrentRow) || !IsEnabled())
+ return nullptr;
+
+ size_t Location = GetModelColumnPos(nColumnId);
+ DbGridColumn* pColumn = ( Location < m_aColumns.size() ) ? m_aColumns[ Location ].get() : nullptr;
+ if (!pColumn)
+ return nullptr;
+
+ CellController* pReturn = nullptr;
+ if (IsFilterMode())
+ pReturn = pColumn->GetController().get();
+ else
+ {
+ if (::comphelper::hasProperty(FM_PROP_ENABLED, pColumn->getModel()))
+ {
+ if (!::comphelper::getBOOL(pColumn->getModel()->getPropertyValue(FM_PROP_ENABLED)))
+ return nullptr;
+ }
+
+ bool bInsert = (m_xCurrentRow->IsNew() && (m_nOptions & DbGridControlOptions::Insert));
+ bool bUpdate = (!m_xCurrentRow->IsNew() && (m_nOptions & DbGridControlOptions::Update));
+
+ if ((bInsert && !pColumn->IsAutoValue()) || bUpdate)
+ {
+ pReturn = pColumn->GetController().get();
+ }
+ }
+ return pReturn;
+}
+
+void DbGridControl::CellModified()
+{
+ SAL_INFO("svx.fmcomp", "DbGridControl::CellModified");
+
+ {
+ ::osl::MutexGuard aGuard(m_aAdjustSafety);
+ if (m_nAsynAdjustEvent)
+ {
+ SAL_INFO("svx.fmcomp", "forcing a synchron call to " << (m_bPendingAdjustRows ? "AdjustRows" : "AdustDataSource"));
+ RemoveUserEvent(m_nAsynAdjustEvent);
+ m_nAsynAdjustEvent = nullptr;
+
+ // force the call : this should be no problem as we're probably running in the solar thread here
+ // (cell modified is triggered by user actions)
+ if (m_bPendingAdjustRows)
+ AdjustRows();
+ else
+ AdjustDataSource();
+ }
+ }
+
+ if (IsFilterMode() || !IsValid(m_xCurrentRow) || m_xCurrentRow->IsModified())
+ return;
+
+ // enable edit mode
+ // a data set should be inserted
+ if (m_xCurrentRow->IsNew())
+ {
+ m_xCurrentRow->SetStatus(GridRowStatus::Modified);
+ SAL_INFO("svx.fmcomp", "current row is new, new state: MODIFIED");
+ // if no row was added yet, do it now
+ if (m_nCurrentPos == GetRowCount() - 1)
+ {
+ // increment RowCount
+ RowInserted(GetRowCount());
+ InvalidateStatusCell(m_nCurrentPos);
+ m_aBar->InvalidateAll(m_nCurrentPos);
+ }
+ }
+ else if (m_xCurrentRow->GetStatus() != GridRowStatus::Modified)
+ {
+ m_xCurrentRow->SetState(m_pDataCursor.get(), false);
+ SAL_INFO("svx.fmcomp", "current row is not new, after SetState, new state: " << ROWSTATUS(m_xCurrentRow));
+ m_xCurrentRow->SetStatus(GridRowStatus::Modified);
+ SAL_INFO("svx.fmcomp", "current row is not new, new state: MODIFIED");
+ InvalidateStatusCell(m_nCurrentPos);
+ }
+}
+
+void DbGridControl::Dispatch(sal_uInt16 nId)
+{
+ if (nId == BROWSER_CURSORENDOFFILE)
+ {
+ if (m_nOptions & DbGridControlOptions::Insert)
+ AppendNew();
+ else
+ MoveToLast();
+ }
+ else
+ EditBrowseBox::Dispatch(nId);
+}
+
+void DbGridControl::Undo()
+{
+ if (IsFilterMode() || !IsValid(m_xCurrentRow) || !IsModified())
+ return;
+
+ // check if we have somebody doin' the UNDO for us
+ int nState = -1;
+ if (m_aMasterStateProvider.IsSet())
+ nState = m_aMasterStateProvider.Call(DbGridControlNavigationBarState::Undo);
+ if (nState>0)
+ { // yes, we have, and the slot is enabled
+ DBG_ASSERT(m_aMasterSlotExecutor.IsSet(), "DbGridControl::Undo : a state, but no execute link ?");
+ bool lResult = m_aMasterSlotExecutor.Call(DbGridControlNavigationBarState::Undo);
+ if (lResult)
+ // handled
+ return;
+ }
+ else if (nState == 0)
+ // yes, we have, and the slot is disabled
+ return;
+
+ BeginCursorAction();
+
+ bool bAppending = m_xCurrentRow->IsNew();
+ bool bDirty = m_xCurrentRow->IsModified();
+
+ try
+ {
+ // cancel editing
+ Reference< XResultSetUpdate > xUpdateCursor(Reference< XInterface >(*m_pDataCursor), UNO_QUERY);
+ // no effects if we're not updating currently
+ if (bAppending)
+ // just refresh the row
+ xUpdateCursor->moveToInsertRow();
+ else
+ xUpdateCursor->cancelRowUpdates();
+
+ }
+ catch(Exception&)
+ {
+ DBG_UNHANDLED_EXCEPTION("svx");
+ }
+
+ EndCursorAction();
+
+ m_xDataRow->SetState(m_pDataCursor.get(), false);
+ if (m_xPaintRow == m_xCurrentRow)
+ m_xPaintRow = m_xCurrentRow = m_xDataRow;
+ else
+ m_xCurrentRow = m_xDataRow;
+
+ if (bAppending && (EditBrowseBox::IsModified() || bDirty))
+ // remove the row
+ if (m_nCurrentPos == GetRowCount() - 2)
+ { // maybe we already removed it (in resetCurrentRow, called if the above moveToInsertRow
+ // caused our data source form to be reset - which should be the usual case...)
+ RowRemoved(GetRowCount() - 1);
+ m_aBar->InvalidateAll(m_nCurrentPos);
+ }
+
+ RowModified(m_nCurrentPos);
+}
+
+void DbGridControl::resetCurrentRow()
+{
+ if (IsModified())
+ {
+ // scenario : we're on the insert row, the row is dirty, and thus there exists a "second" insert row (which
+ // is clean). Normally in DataSourcePropertyChanged we would remove this second row if the modified state of
+ // the insert row changes from sal_True to sal_False. But if our current cell is the only modified element (means the
+ // data source isn't modified) and we're reset this DataSourcePropertyChanged would never be called, so we
+ // would never delete the obsolete "second insert row". Thus in this special case this method here
+ // is the only possibility to determine the redundance of the row (resetCurrentRow is called when the
+ // "first insert row" is about to be cleaned, so of course the "second insert row" is redundant now)
+ Reference< XPropertySet > xDataSource = getDataSource()->getPropertySet();
+ if (xDataSource.is() && !::comphelper::getBOOL(xDataSource->getPropertyValue(FM_PROP_ISMODIFIED)))
+ {
+ // are we on a new row currently ?
+ if (m_xCurrentRow->IsNew())
+ {
+ if (m_nCurrentPos == GetRowCount() - 2)
+ {
+ RowRemoved(GetRowCount() - 1);
+ m_aBar->InvalidateAll(m_nCurrentPos);
+ }
+ }
+ }
+
+ // update the rows
+ m_xDataRow->SetState(m_pDataCursor.get(), false);
+ if (m_xPaintRow == m_xCurrentRow)
+ m_xPaintRow = m_xCurrentRow = m_xDataRow;
+ else
+ m_xCurrentRow = m_xDataRow;
+ }
+
+ RowModified(GetCurRow()); // will update the current controller if affected
+}
+
+void DbGridControl::RowModified( sal_Int32 nRow )
+{
+ if (nRow == m_nCurrentPos && IsEditing())
+ {
+ CellControllerRef aTmpRef = Controller();
+ aTmpRef->SaveValue();
+ InitController(aTmpRef, m_nCurrentPos, GetCurColumnId());
+ }
+ EditBrowseBox::RowModified(nRow);
+}
+
+bool DbGridControl::IsModified() const
+{
+ return !IsFilterMode() && IsValid(m_xCurrentRow) && (m_xCurrentRow->IsModified() || EditBrowseBox::IsModified());
+}
+
+bool DbGridControl::IsCurrentAppending() const
+{
+ return m_xCurrentRow.is() && m_xCurrentRow->IsNew();
+}
+
+bool DbGridControl::IsInsertionRow(sal_Int32 nRow) const
+{
+ return (m_nOptions & DbGridControlOptions::Insert) && m_nTotalCount >= 0 && (nRow == GetRowCount() - 1);
+}
+
+bool DbGridControl::SaveModified()
+{
+ SAL_INFO("svx.fmcomp", "DbGridControl::SaveModified");
+ DBG_ASSERT(IsValid(m_xCurrentRow), "GridControl:: Invalid row");
+ if (!IsValid(m_xCurrentRow))
+ return true;
+
+ // accept input for this field
+ // Where there changes at the current input field?
+ if (!EditBrowseBox::IsModified())
+ return true;
+
+ size_t Location = GetModelColumnPos( GetCurColumnId() );
+ DbGridColumn* pColumn = ( Location < m_aColumns.size() ) ? m_aColumns[ Location ].get() : nullptr;
+ bool bOK = pColumn && pColumn->Commit();
+ DBG_ASSERT( Controller().is(), "DbGridControl::SaveModified: was modified, by have no controller?!" );
+ if ( !Controller().is() )
+ // this might happen if the callbacks implicitly triggered by Commit
+ // fiddled with the form or the control ...
+ // (Note that this here is a workaround, at most. We need a general concept how
+ // to treat this, you can imagine an arbitrary number of scenarios where a callback
+ // triggers something which leaves us in an expected state.)
+ // #i67147# / 2006-07-17 / frank.schoenheit@sun.com
+ return bOK;
+
+ if (bOK)
+ {
+ Controller()->SaveValue();
+
+ if ( IsValid(m_xCurrentRow) )
+ {
+ m_xCurrentRow->SetState(m_pDataCursor.get(), false);
+ SAL_INFO("svx.fmcomp", "explicit SetState, new state: " << ROWSTATUS(m_xCurrentRow));
+ InvalidateStatusCell( m_nCurrentPos );
+ }
+ else
+ {
+ SAL_INFO("svx.fmcomp", "no SetState, new state: " << ROWSTATUS(m_xCurrentRow));
+ }
+ }
+
+ return bOK;
+}
+
+bool DbGridControl::SaveRow()
+{
+ SAL_INFO("svx.fmcomp", "DbGridControl::SaveRow");
+ // valid row
+ if (!IsValid(m_xCurrentRow) || !IsModified())
+ return true;
+ // value of the controller was not saved, yet
+ else if (Controller().is() && Controller()->IsValueChangedFromSaved())
+ {
+ if (!SaveModified())
+ return false;
+ }
+ m_bUpdating = true;
+
+ BeginCursorAction();
+ bool bAppending = m_xCurrentRow->IsNew();
+ bool bSuccess = false;
+ try
+ {
+ Reference< XResultSetUpdate > xUpdateCursor(Reference< XInterface >(*m_pDataCursor), UNO_QUERY);
+ if (bAppending)
+ xUpdateCursor->insertRow();
+ else
+ xUpdateCursor->updateRow();
+ bSuccess = true;
+ }
+ catch(SQLException&)
+ {
+ EndCursorAction();
+ m_bUpdating = false;
+ return false;
+ }
+
+ try
+ {
+ if (bSuccess)
+ {
+ // if we are appending we still sit on the insert row
+ // we don't move just clear the flags not to move on the current row
+ m_xCurrentRow->SetState(m_pDataCursor.get(), false);
+ SAL_INFO("svx.fmcomp", "explicit SetState after a successful update, new state: " << ROWSTATUS(m_xCurrentRow));
+ m_xCurrentRow->SetNew(false);
+
+ // adjust the seekcursor if it is on the same position as the datacursor
+ if (m_nSeekPos == m_nCurrentPos || bAppending)
+ {
+ // get the bookmark to refetch the data
+ // in insert mode we take the new bookmark of the data cursor
+ Any aBookmark = bAppending ? m_pDataCursor->getBookmark() : m_pSeekCursor->getBookmark();
+ m_pSeekCursor->moveToBookmark(aBookmark);
+ // get the data
+ m_xSeekRow->SetState(m_pSeekCursor.get(), true);
+ m_nSeekPos = m_pSeekCursor->getRow() - 1;
+ }
+ }
+ // and repaint the row
+ RowModified(m_nCurrentPos);
+ }
+ catch(Exception&)
+ {
+ }
+
+ m_bUpdating = false;
+ EndCursorAction();
+
+ // The old code returned (nRecords != 0) here.
+ // Me thinks this is wrong : If something goes wrong while update the record, an exception will be thrown,
+ // which results in a "return sal_False" (see above). If no exception is thrown, everything is fine. If nRecords
+ // is zero, this simply means all fields had their original values.
+ // FS - 06.12.99 - 70502
+ return true;
+}
+
+bool DbGridControl::PreNotify(NotifyEvent& rEvt)
+{
+ // do not handle events of the Navbar
+ if (m_aBar->IsWindowOrChild(rEvt.GetWindow()))
+ return BrowseBox::PreNotify(rEvt);
+
+ switch (rEvt.GetType())
+ {
+ case MouseNotifyEvent::KEYINPUT:
+ {
+ const KeyEvent* pKeyEvent = rEvt.GetKeyEvent();
+
+ sal_uInt16 nCode = pKeyEvent->GetKeyCode().GetCode();
+ bool bShift = pKeyEvent->GetKeyCode().IsShift();
+ bool bCtrl = pKeyEvent->GetKeyCode().IsMod1();
+ bool bAlt = pKeyEvent->GetKeyCode().IsMod2();
+ if ( ( KEY_TAB == nCode ) && bCtrl && !bAlt )
+ {
+ // Ctrl-Tab is used to step out of the control, without traveling to the
+ // remaining cells first
+ // -> build a new key event without the Ctrl-key, and let the very base class handle it
+ vcl::KeyCode aNewCode( KEY_TAB, bShift, false, false, false );
+ KeyEvent aNewEvent( pKeyEvent->GetCharCode(), aNewCode );
+
+ // call the Control - our direct base class will interpret this in a way we do not want (and do
+ // a cell traveling)
+ Control::KeyInput( aNewEvent );
+ return true;
+ }
+
+ if ( !bShift && !bCtrl && ( KEY_ESCAPE == nCode ) )
+ {
+ if (IsModified())
+ {
+ Undo();
+ return true;
+ }
+ }
+ else if ( ( KEY_DELETE == nCode ) && !bShift && !bCtrl ) // delete rows
+ {
+ if ((m_nOptions & DbGridControlOptions::Delete) && GetSelectRowCount())
+ {
+ // delete asynchronously
+ if (m_nDeleteEvent)
+ Application::RemoveUserEvent(m_nDeleteEvent);
+ m_nDeleteEvent = Application::PostUserEvent(LINK(this,DbGridControl,OnDelete), nullptr, true);
+ return true;
+ }
+ }
+
+ [[fallthrough]];
+ }
+ default:
+ return EditBrowseBox::PreNotify(rEvt);
+ }
+}
+
+bool DbGridControl::IsTabAllowed(bool bRight) const
+{
+ if (bRight)
+ // Tab only if not on the _last_ row
+ return GetCurRow() < (GetRowCount() - 1) || !m_bRecordCountFinal ||
+ GetViewColumnPos(GetCurColumnId()) < (GetViewColCount() - 1);
+ else
+ {
+ // Tab only if not on the _first_ row
+ return GetCurRow() > 0 || (GetCurColumnId() && GetViewColumnPos(GetCurColumnId()) > 0);
+ }
+}
+
+void DbGridControl::KeyInput( const KeyEvent& rEvt )
+{
+ if (rEvt.GetKeyCode().GetFunction() == KeyFuncType::COPY)
+ {
+ tools::Long nRow = GetCurRow();
+ sal_uInt16 nColId = GetCurColumnId();
+ if (nRow >= 0 && nRow < GetRowCount() && nColId < ColCount())
+ {
+ size_t Location = GetModelColumnPos( nColId );
+ DbGridColumn* pColumn = ( Location < m_aColumns.size() ) ? m_aColumns[ Location ].get() : nullptr;
+ OStringTransfer::CopyString( GetCurrentRowCellText( pColumn, m_xCurrentRow ), this );
+ return;
+ }
+ }
+ EditBrowseBox::KeyInput(rEvt);
+}
+
+void DbGridControl::HideColumn(sal_uInt16 nId)
+{
+ DeactivateCell();
+
+ // determine the col for the focus to set to after removal
+ sal_uInt16 nPos = GetViewColumnPos(nId);
+ sal_uInt16 nNewColId = nPos == (ColCount()-1)
+ ? GetColumnIdFromViewPos(nPos-1) // last col is to be removed -> take the previous
+ : GetColumnIdFromViewPos(nPos+1); // take the next
+
+ tools::Long lCurrentWidth = GetColumnWidth(nId);
+ EditBrowseBox::RemoveColumn(nId);
+ // don't use my own RemoveColumn, this would remove it from m_aColumns, too
+
+ // update my model
+ size_t Location = GetModelColumnPos( nId );
+ DbGridColumn* pColumn = ( Location < m_aColumns.size() ) ? m_aColumns[ Location ].get() : nullptr;
+ DBG_ASSERT(pColumn, "DbGridControl::HideColumn : somebody did hide a nonexistent column !");
+ if (pColumn)
+ {
+ pColumn->m_bHidden = true;
+ pColumn->m_nLastVisibleWidth = CalcReverseZoom(lCurrentWidth);
+ }
+
+ // and reset the focus
+ if ( nId == GetCurColumnId() )
+ GoToColumnId( nNewColId );
+}
+
+void DbGridControl::ShowColumn(sal_uInt16 nId)
+{
+ sal_uInt16 nPos = GetModelColumnPos(nId);
+ DBG_ASSERT(nPos != GRID_COLUMN_NOT_FOUND, "DbGridControl::ShowColumn : invalid argument !");
+ if (nPos == GRID_COLUMN_NOT_FOUND)
+ return;
+
+ DbGridColumn* pColumn = m_aColumns[ nPos ].get();
+ if (!pColumn->IsHidden())
+ {
+ DBG_ASSERT(GetViewColumnPos(nId) != GRID_COLUMN_NOT_FOUND, "DbGridControl::ShowColumn : inconsistent internal state !");
+ // if the column isn't marked as hidden, it should be visible, shouldn't it ?
+ return;
+ }
+ DBG_ASSERT(GetViewColumnPos(nId) == GRID_COLUMN_NOT_FOUND, "DbGridControl::ShowColumn : inconsistent internal state !");
+ // the opposite situation ...
+
+ // to determine the new view position we need an adjacent non-hidden column
+ sal_uInt16 nNextNonHidden = BROWSER_INVALIDID;
+ // first search the cols to the right
+ for ( size_t i = nPos + 1; i < m_aColumns.size(); ++i )
+ {
+ DbGridColumn* pCurCol = m_aColumns[ i ].get();
+ if (!pCurCol->IsHidden())
+ {
+ nNextNonHidden = i;
+ break;
+ }
+ }
+ if ((nNextNonHidden == BROWSER_INVALIDID) && (nPos > 0))
+ {
+ // then to the left
+ for ( size_t i = nPos; i > 0; --i )
+ {
+ DbGridColumn* pCurCol = m_aColumns[ i-1 ].get();
+ if (!pCurCol->IsHidden())
+ {
+ nNextNonHidden = i-1;
+ break;
+ }
+ }
+ }
+ sal_uInt16 nNewViewPos = (nNextNonHidden == BROWSER_INVALIDID)
+ ? 1 // there is no visible column -> insert behind the handle col
+ : GetViewColumnPos( m_aColumns[ nNextNonHidden ]->GetId() ) + 1;
+ // the first non-handle col has "view pos" 0, but the pos arg for InsertDataColumn expects
+ // a position 1 for the first non-handle col -> +1
+ DBG_ASSERT(nNewViewPos != GRID_COLUMN_NOT_FOUND, "DbGridControl::ShowColumn : inconsistent internal state !");
+ // we found a col marked as visible but got no view pos for it ...
+
+ if ((nNextNonHidden<nPos) && (nNextNonHidden != BROWSER_INVALIDID))
+ // nNextNonHidden is a column to the left, so we want to insert the new col _right_ beside it's pos
+ ++nNewViewPos;
+
+ DeactivateCell();
+
+ OUString aName;
+ pColumn->getModel()->getPropertyValue(FM_PROP_LABEL) >>= aName;
+ InsertDataColumn(nId, aName, CalcZoom(pColumn->m_nLastVisibleWidth), HeaderBarItemBits::CENTER | HeaderBarItemBits::CLICKABLE, nNewViewPos);
+ pColumn->m_bHidden = false;
+
+ ActivateCell();
+ Invalidate();
+}
+
+sal_uInt16 DbGridControl::GetColumnIdFromModelPos( sal_uInt16 nPos ) const
+{
+ if (nPos >= m_aColumns.size())
+ {
+ OSL_FAIL("DbGridControl::GetColumnIdFromModelPos : invalid argument !");
+ return GRID_COLUMN_NOT_FOUND;
+ }
+
+ DbGridColumn* pCol = m_aColumns[ nPos ].get();
+#if (OSL_DEBUG_LEVEL > 0) || defined DBG_UTIL
+ // in the debug version, we convert the ModelPos into a ViewPos and compare this with the
+ // value we will return (nId at the corresponding Col in m_aColumns)
+
+ if (!pCol->IsHidden())
+ { // makes sense only if the column is visible
+ sal_uInt16 nViewPos = nPos;
+ for ( size_t i = 0; i < m_aColumns.size() && i < nPos; ++i)
+ if ( m_aColumns[ i ]->IsHidden())
+ --nViewPos;
+
+ DBG_ASSERT(pCol && GetColumnIdFromViewPos(nViewPos) == pCol->GetId(),
+ "DbGridControl::GetColumnIdFromModelPos : this isn't consistent... did I misunderstand something ?");
+ }
+#endif
+ return pCol->GetId();
+}
+
+sal_uInt16 DbGridControl::GetModelColumnPos( sal_uInt16 nId ) const
+{
+ for ( size_t i = 0; i < m_aColumns.size(); ++i )
+ if ( m_aColumns[ i ]->GetId() == nId )
+ return i;
+
+ return GRID_COLUMN_NOT_FOUND;
+}
+
+void DbGridControl::implAdjustInSolarThread(bool _bRows)
+{
+ SAL_INFO("svx.fmcomp", "DbGridControl::implAdjustInSolarThread");
+ ::osl::MutexGuard aGuard(m_aAdjustSafety);
+ if (!Application::IsMainThread())
+ {
+ m_nAsynAdjustEvent = PostUserEvent(LINK(this, DbGridControl, OnAsyncAdjust), reinterpret_cast< void* >( _bRows ), true);
+ m_bPendingAdjustRows = _bRows;
+ if (_bRows)
+ SAL_INFO("svx.fmcomp", "posting an AdjustRows");
+ else
+ SAL_INFO("svx.fmcomp", "posting an AdjustDataSource");
+ }
+ else
+ {
+ if (_bRows)
+ SAL_INFO("svx.fmcomp", "doing an AdjustRows");
+ else
+ SAL_INFO("svx.fmcomp", "doing an AdjustDataSource");
+ // always adjust the rows before adjusting the data source
+ // If this is not necessary (because the row count did not change), nothing is done
+ // The problem is that we can't rely on the order of which the calls come in: If the cursor was moved
+ // to a position behind row count know 'til now, the cursorMoved notification may come before the
+ // RowCountChanged notification
+ // 94093 - 02.11.2001 - frank.schoenheit@sun.com
+ AdjustRows();
+
+ if ( !_bRows )
+ AdjustDataSource();
+ }
+}
+
+IMPL_LINK(DbGridControl, OnAsyncAdjust, void*, pAdjustWhat, void)
+{
+ m_nAsynAdjustEvent = nullptr;
+
+ AdjustRows();
+ // see implAdjustInSolarThread for a comment why we do this every time
+
+ if ( !pAdjustWhat )
+ AdjustDataSource();
+}
+
+void DbGridControl::BeginCursorAction()
+{
+ if (m_pFieldListeners)
+ {
+ ColumnFieldValueListeners* pListeners = static_cast<ColumnFieldValueListeners*>(m_pFieldListeners);
+ for (const auto& rListener : *pListeners)
+ {
+ GridFieldValueListener* pCurrent = rListener.second;
+ if (pCurrent)
+ pCurrent->suspend();
+ }
+ }
+
+ if (m_pDataSourcePropListener)
+ m_pDataSourcePropListener->suspend();
+}
+
+void DbGridControl::EndCursorAction()
+{
+ if (m_pFieldListeners)
+ {
+ ColumnFieldValueListeners* pListeners = static_cast<ColumnFieldValueListeners*>(m_pFieldListeners);
+ for (const auto& rListener : *pListeners)
+ {
+ GridFieldValueListener* pCurrent = rListener.second;
+ if (pCurrent)
+ pCurrent->resume();
+ }
+ }
+
+ if (m_pDataSourcePropListener)
+ m_pDataSourcePropListener->resume();
+}
+
+void DbGridControl::ConnectToFields()
+{
+ ColumnFieldValueListeners* pListeners = static_cast<ColumnFieldValueListeners*>(m_pFieldListeners);
+ DBG_ASSERT(!pListeners || pListeners->empty(), "DbGridControl::ConnectToFields : please call DisconnectFromFields first !");
+
+ if (!pListeners)
+ {
+ pListeners = new ColumnFieldValueListeners;
+ m_pFieldListeners = pListeners;
+ }
+
+ for (auto const & pCurrent : m_aColumns)
+ {
+ sal_uInt16 nViewPos = pCurrent ? GetViewColumnPos(pCurrent->GetId()) : GRID_COLUMN_NOT_FOUND;
+ if (GRID_COLUMN_NOT_FOUND == nViewPos)
+ continue;
+
+ Reference< XPropertySet > xField = pCurrent->GetField();
+ if (!xField.is())
+ continue;
+
+ // column is visible and bound here
+ GridFieldValueListener*& rpListener = (*pListeners)[pCurrent->GetId()];
+ DBG_ASSERT(!rpListener, "DbGridControl::ConnectToFields : already a listener for this column ?!");
+ rpListener = new GridFieldValueListener(*this, xField, pCurrent->GetId());
+ }
+}
+
+void DbGridControl::DisconnectFromFields()
+{
+ if (!m_pFieldListeners)
+ return;
+
+ ColumnFieldValueListeners* pListeners = static_cast<ColumnFieldValueListeners*>(m_pFieldListeners);
+ while (!pListeners->empty())
+ {
+ sal_Int32 nOldSize = pListeners->size();
+ pListeners->begin()->second->dispose();
+ DBG_ASSERT(nOldSize > static_cast<sal_Int32>(pListeners->size()), "DbGridControl::DisconnectFromFields : dispose on a listener should result in a removal from my list !");
+ }
+
+ delete pListeners;
+ m_pFieldListeners = nullptr;
+}
+
+void DbGridControl::FieldValueChanged(sal_uInt16 _nId)
+{
+ osl::MutexGuard aPreventDestruction(m_aDestructionSafety);
+ // needed as this may run in a thread other than the main one
+ if (GetRowStatus(GetCurRow()) != EditBrowseBox::MODIFIED)
+ // all other cases are handled elsewhere
+ return;
+
+ size_t Location = GetModelColumnPos( _nId );
+ DbGridColumn* pColumn = ( Location < m_aColumns.size() ) ? m_aColumns[ Location ].get() : nullptr;
+ if (!pColumn)
+ return;
+
+ std::unique_ptr<vcl::SolarMutexTryAndBuyGuard> pGuard;
+ while (!m_bWantDestruction && (!pGuard || !pGuard->isAcquired()))
+ pGuard.reset(new vcl::SolarMutexTryAndBuyGuard);
+
+ if (m_bWantDestruction)
+ { // at this moment, within another thread, our destructor tries to destroy the listener which called this method
+ // => don't do anything
+ // 73365 - 23.02.00 - FS
+ return;
+ }
+
+ // and finally do the update ...
+ pColumn->UpdateFromField(m_xCurrentRow.get(), m_xFormatter);
+ RowModified(GetCurRow());
+}
+
+void DbGridControl::FieldListenerDisposing(sal_uInt16 _nId)
+{
+ ColumnFieldValueListeners* pListeners = static_cast<ColumnFieldValueListeners*>(m_pFieldListeners);
+ if (!pListeners)
+ {
+ OSL_FAIL("DbGridControl::FieldListenerDisposing : invalid call (have no listener array) !");
+ return;
+ }
+
+ ColumnFieldValueListeners::const_iterator aPos = pListeners->find(_nId);
+ if (aPos == pListeners->end())
+ {
+ OSL_FAIL("DbGridControl::FieldListenerDisposing : invalid call (did not find the listener) !");
+ return;
+ }
+
+ delete aPos->second;
+
+ pListeners->erase(aPos);
+}
+
+void DbGridControl::disposing(sal_uInt16 _nId)
+{
+ if (_nId == 0)
+ { // the seek cursor is being disposed
+ ::osl::MutexGuard aGuard(m_aAdjustSafety);
+ setDataSource(nullptr, DbGridControlOptions::Readonly); // our clone was disposed so we set our datasource to null to avoid later access to it
+ if (m_nAsynAdjustEvent)
+ {
+ RemoveUserEvent(m_nAsynAdjustEvent);
+ m_nAsynAdjustEvent = nullptr;
+ }
+ }
+}
+
+sal_Int32 DbGridControl::GetAccessibleControlCount() const
+{
+ return EditBrowseBox::GetAccessibleControlCount() + 1; // the navigation control
+}
+
+Reference<XAccessible > DbGridControl::CreateAccessibleControl( sal_Int32 _nIndex )
+{
+ Reference<XAccessible > xRet;
+ if ( _nIndex == EditBrowseBox::GetAccessibleControlCount() )
+ {
+ xRet = m_aBar->GetAccessible();
+ }
+ else
+ xRet = EditBrowseBox::CreateAccessibleControl( _nIndex );
+ return xRet;
+}
+
+Reference< XAccessible > DbGridControl::CreateAccessibleCell( sal_Int32 _nRow, sal_uInt16 _nColumnPos )
+{
+ sal_uInt16 nColumnId = GetColumnId( _nColumnPos );
+ size_t Location = GetModelColumnPos(nColumnId);
+ DbGridColumn* pColumn = ( Location < m_aColumns.size() ) ? m_aColumns[ Location ].get() : nullptr;
+ if ( pColumn )
+ {
+ Reference< css::awt::XControl> xInt(pColumn->GetCell());
+ Reference< css::awt::XCheckBox> xBox(xInt,UNO_QUERY);
+ if ( xBox.is() )
+ {
+ TriState eValue = TRISTATE_FALSE;
+ switch( xBox->getState() )
+ {
+ case 0:
+ eValue = TRISTATE_FALSE;
+ break;
+ case 1:
+ eValue = TRISTATE_TRUE;
+ break;
+ case 2:
+ eValue = TRISTATE_INDET;
+ break;
+ }
+ return EditBrowseBox::CreateAccessibleCheckBoxCell( _nRow, _nColumnPos,eValue );
+ }
+ }
+ return EditBrowseBox::CreateAccessibleCell( _nRow, _nColumnPos );
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/fmcomp/xmlexchg.cxx b/svx/source/fmcomp/xmlexchg.cxx
new file mode 100644
index 000000000..1f8d44ee7
--- /dev/null
+++ b/svx/source/fmcomp/xmlexchg.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 <svx/xmlexchg.hxx>
+#include <sot/formats.hxx>
+#include <sot/exchange.hxx>
+
+namespace svx
+{
+
+
+ using namespace ::com::sun::star::datatransfer;
+
+ OXFormsTransferable::OXFormsTransferable( const OXFormsDescriptor &rhs ) :
+ m_aDescriptor(rhs)
+ {
+ }
+
+ void OXFormsTransferable::AddSupportedFormats()
+ {
+ AddFormat( SotClipboardFormatId::XFORMS );
+ }
+
+ bool OXFormsTransferable::GetData( const DataFlavor& _rFlavor, const OUString& /*rDestDoc*/ )
+ {
+ const SotClipboardFormatId nFormatId = SotExchange::GetFormat( _rFlavor );
+ if ( SotClipboardFormatId::XFORMS == nFormatId )
+ {
+ return SetString("XForms-Transferable");
+ }
+ return false;
+ }
+
+ const OXFormsDescriptor &OXFormsTransferable::extractDescriptor( const TransferableDataHelper &_rData ) {
+
+ using namespace ::com::sun::star::uno;
+ Reference<XTransferable> &transfer = const_cast<Reference<XTransferable> &>(_rData.GetTransferable());
+ XTransferable *pInterface = transfer.get();
+ OXFormsTransferable& rThis = dynamic_cast<OXFormsTransferable&>(*pInterface);
+ return rThis.m_aDescriptor;
+ }
+
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/form/ParseContext.cxx b/svx/source/form/ParseContext.cxx
new file mode 100644
index 000000000..636341791
--- /dev/null
+++ b/svx/source/form/ParseContext.cxx
@@ -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 .
+ */
+
+
+#include <sal/macros.h>
+#include <svx/ParseContext.hxx>
+#include <svx/strings.hrc>
+
+#include <svx/dialmgr.hxx>
+
+#include <i18nlangtag/languagetag.hxx>
+#include <unotools/syslocale.hxx>
+#include <vcl/svapp.hxx>
+#include <osl/diagnose.h>
+#include <fmstring.hrc>
+#include <mutex>
+
+using namespace svxform;
+using namespace ::connectivity;
+
+OSystemParseContext::OSystemParseContext()
+ : IParseContext()
+{
+ for (size_t i = 0; i < SAL_N_ELEMENTS(RID_RSC_SQL_INTERNATIONAL); ++i)
+ m_aLocalizedKeywords.push_back(SvxResId(RID_RSC_SQL_INTERNATIONAL[i]));
+}
+
+OSystemParseContext::~OSystemParseContext()
+{
+}
+
+css::lang::Locale OSystemParseContext::getPreferredLocale( ) const
+{
+ return SvtSysLocale().GetLanguageTag().getLocale();
+}
+
+OUString OSystemParseContext::getErrorMessage(ErrorCode _eCode) const
+{
+ OUString aMsg;
+ SolarMutexGuard aGuard;
+ switch (_eCode)
+ {
+ case ErrorCode::General: aMsg = SvxResId(RID_STR_SVT_SQL_SYNTAX_ERROR); break;
+ case ErrorCode::ValueNoLike: aMsg = SvxResId(RID_STR_SVT_SQL_SYNTAX_VALUE_NO_LIKE); break;
+ case ErrorCode::FieldNoLike: aMsg = SvxResId(RID_STR_SVT_SQL_SYNTAX_FIELD_NO_LIKE); break;
+ case ErrorCode::InvalidCompare: aMsg = SvxResId(RID_STR_SVT_SQL_SYNTAX_CRIT_NO_COMPARE); break;
+ case ErrorCode::InvalidIntCompare: aMsg = SvxResId(RID_STR_SVT_SQL_SYNTAX_INT_NO_VALID); break;
+ case ErrorCode::InvalidDateCompare: aMsg = SvxResId(RID_STR_SVT_SQL_SYNTAX_ACCESS_DAT_NO_VALID); break;
+ case ErrorCode::InvalidRealCompare: aMsg = SvxResId(RID_STR_SVT_SQL_SYNTAX_REAL_NO_VALID); break;
+ case ErrorCode::InvalidTableNosuch: aMsg = SvxResId(RID_STR_SVT_SQL_SYNTAX_TABLE); break;
+ case ErrorCode::InvalidTableOrQuery: aMsg = SvxResId(RID_STR_SVT_SQL_SYNTAX_TABLE_OR_QUERY); break;
+ case ErrorCode::InvalidColumn: aMsg = SvxResId(RID_STR_SVT_SQL_SYNTAX_COLUMN); break;
+ case ErrorCode::InvalidTableExist: aMsg = SvxResId(RID_STR_SVT_SQL_SYNTAX_TABLE_EXISTS); break;
+ case ErrorCode::InvalidQueryExist: aMsg = SvxResId(RID_STR_SVT_SQL_SYNTAX_QUERY_EXISTS); break;
+ default: break;
+ }
+ return aMsg;
+}
+
+OString OSystemParseContext::getIntlKeywordAscii(InternationalKeyCode _eKey) const
+{
+ size_t nIndex = 0;
+ switch ( _eKey )
+ {
+ case InternationalKeyCode::Like: nIndex = 0; break;
+ case InternationalKeyCode::Not: nIndex = 1; break;
+ case InternationalKeyCode::Null: nIndex = 2; break;
+ case InternationalKeyCode::True: nIndex = 3; break;
+ case InternationalKeyCode::False: nIndex = 4; break;
+ case InternationalKeyCode::Is: nIndex = 5; break;
+ case InternationalKeyCode::Between: nIndex = 6; break;
+ case InternationalKeyCode::Or: nIndex = 7; break;
+ case InternationalKeyCode::And: nIndex = 8; break;
+ case InternationalKeyCode::Avg: nIndex = 9; break;
+ case InternationalKeyCode::Count: nIndex = 10; break;
+ case InternationalKeyCode::Max: nIndex = 11; break;
+ case InternationalKeyCode::Min: nIndex = 12; break;
+ case InternationalKeyCode::Sum: nIndex = 13; break;
+ case InternationalKeyCode::Every: nIndex = 14; break;
+ case InternationalKeyCode::Any: nIndex = 15; break;
+ case InternationalKeyCode::Some: nIndex = 16; break;
+ case InternationalKeyCode::StdDevPop: nIndex = 17; break;
+ case InternationalKeyCode::StdDevSamp: nIndex = 18; break;
+ case InternationalKeyCode::VarSamp: nIndex = 19; break;
+ case InternationalKeyCode::VarPop: nIndex = 20; break;
+ case InternationalKeyCode::Collect: nIndex = 21; break;
+ case InternationalKeyCode::Fusion: nIndex = 22; break;
+ case InternationalKeyCode::Intersection: nIndex = 23; break;
+ case InternationalKeyCode::None:
+ OSL_FAIL( "OSystemParseContext::getIntlKeywordAscii: illegal argument!" );
+ break;
+ }
+
+ OSL_ENSURE( nIndex < m_aLocalizedKeywords.size(), "OSystemParseContext::getIntlKeywordAscii: invalid index!" );
+
+ OString sKeyword;
+ if ( nIndex < m_aLocalizedKeywords.size() )
+ sKeyword = OUStringToOString(m_aLocalizedKeywords[nIndex], RTL_TEXTENCODING_UTF8);
+ return sKeyword;
+}
+
+
+IParseContext::InternationalKeyCode OSystemParseContext::getIntlKeyCode(const OString& rToken) const
+{
+ static const IParseContext::InternationalKeyCode Intl_TokenID[] =
+ {
+ InternationalKeyCode::Like, InternationalKeyCode::Not, InternationalKeyCode::Null, InternationalKeyCode::True,
+ InternationalKeyCode::False, InternationalKeyCode::Is, InternationalKeyCode::Between, InternationalKeyCode::Or,
+ InternationalKeyCode::And, InternationalKeyCode::Avg, InternationalKeyCode::Count, InternationalKeyCode::Max,
+ InternationalKeyCode::Min, InternationalKeyCode::Sum, InternationalKeyCode::Every,
+ InternationalKeyCode::Any, InternationalKeyCode::Some, InternationalKeyCode::StdDevPop,
+ InternationalKeyCode::StdDevSamp, InternationalKeyCode::VarSamp, InternationalKeyCode::VarPop,
+ InternationalKeyCode::Collect, InternationalKeyCode::Fusion, InternationalKeyCode::Intersection
+ };
+
+ sal_uInt32 const nCount = SAL_N_ELEMENTS(Intl_TokenID);
+ for (sal_uInt32 i = 0; i < nCount; i++)
+ {
+ OString aKey = getIntlKeywordAscii(Intl_TokenID[i]);
+ if (rToken.equalsIgnoreAsciiCase(aKey))
+ return Intl_TokenID[i];
+ }
+
+ return InternationalKeyCode::None;
+}
+
+
+namespace
+{
+
+ std::mutex& getSafetyMutex()
+ {
+ static ::std::mutex s_aSafety;
+ return s_aSafety;
+ }
+
+ int s_nCounter;
+
+ OSystemParseContext* getSharedContext(OSystemParseContext* _pContext, bool _bSet)
+ {
+ static OSystemParseContext* s_pSharedContext = nullptr;
+ if ( _pContext && !s_pSharedContext )
+ {
+ s_pSharedContext = _pContext;
+ return s_pSharedContext;
+ }
+ if ( _bSet )
+ {
+ OSystemParseContext* pReturn = _pContext ? _pContext : s_pSharedContext;
+ s_pSharedContext = _pContext;
+ return pReturn;
+ }
+ return s_pSharedContext;
+ }
+
+}
+
+OParseContextClient::OParseContextClient()
+{
+ std::scoped_lock aGuard( getSafetyMutex() );
+ ++s_nCounter;
+ if ( 1 == s_nCounter )
+ { // first instance
+ getSharedContext( new OSystemParseContext, false );
+ }
+}
+
+
+OParseContextClient::~OParseContextClient()
+{
+ std::scoped_lock aGuard( getSafetyMutex() );
+ --s_nCounter;
+ if ( 0 == s_nCounter )
+ delete getSharedContext(nullptr,true);
+}
+
+const OSystemParseContext* OParseContextClient::getParseContext() const
+{
+ return getSharedContext(nullptr, false);
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/form/dataaccessdescriptor.cxx b/svx/source/form/dataaccessdescriptor.cxx
new file mode 100644
index 000000000..78538fbeb
--- /dev/null
+++ b/svx/source/form/dataaccessdescriptor.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 <svx/dataaccessdescriptor.hxx>
+#include <osl/diagnose.h>
+#include <com/sun/star/ucb/XContent.hpp>
+#include <com/sun/star/beans/PropertyValue.hpp>
+#include <com/sun/star/beans/XPropertySet.hpp>
+#include <tools/urlobj.hxx>
+#include <map>
+
+namespace svx
+{
+ using namespace ::com::sun::star::uno;
+ using namespace ::com::sun::star::beans;
+ using namespace ::com::sun::star::ucb;
+
+ typedef std::pair<OUString const, DataAccessDescriptorProperty> PropertyMapEntry;
+
+ class ODADescriptorImpl
+ {
+ protected:
+ bool m_bSetOutOfDate : 1;
+ bool m_bSequenceOutOfDate : 1;
+
+ public:
+ typedef ::std::map< DataAccessDescriptorProperty, Any > DescriptorValues;
+ DescriptorValues m_aValues;
+ Sequence< PropertyValue > m_aAsSequence;
+
+ typedef ::std::map< OUString, DataAccessDescriptorProperty > MapString2PropertyEntry;
+
+ public:
+ ODADescriptorImpl();
+ ODADescriptorImpl(const ODADescriptorImpl& _rSource);
+
+ void invalidateExternRepresentations();
+
+ void updateSequence();
+
+ /** builds the descriptor from a property value sequence
+ @return <TRUE/>
+ if and only if the sequence contained valid properties only
+ */
+ bool buildFrom( const Sequence< PropertyValue >& _rValues );
+
+ /** builds the descriptor from a property set
+ @return <TRUE/>
+ if and only if the set contained valid properties only
+ */
+ bool buildFrom( const Reference< XPropertySet >& _rValues );
+
+ protected:
+ static PropertyValue buildPropertyValue( const DescriptorValues::const_iterator& _rPos );
+ static const MapString2PropertyEntry& getPropertyMap( );
+ static PropertyMapEntry const * getPropertyMapEntry( const DescriptorValues::const_iterator& _rPos );
+ };
+
+ ODADescriptorImpl::ODADescriptorImpl()
+ :m_bSetOutOfDate(true)
+ ,m_bSequenceOutOfDate(true)
+ {
+ }
+
+ ODADescriptorImpl::ODADescriptorImpl(const ODADescriptorImpl& _rSource)
+ :m_bSetOutOfDate( _rSource.m_bSetOutOfDate )
+ ,m_bSequenceOutOfDate( _rSource.m_bSequenceOutOfDate )
+ ,m_aValues( _rSource.m_aValues )
+ {
+ if (!m_bSequenceOutOfDate)
+ m_aAsSequence = _rSource.m_aAsSequence;
+ }
+
+ bool ODADescriptorImpl::buildFrom( const Sequence< PropertyValue >& _rValues )
+ {
+ const MapString2PropertyEntry& rProperties = getPropertyMap();
+
+ bool bValidPropsOnly = true;
+
+ // loop through the sequence, and fill our m_aValues
+ for (const PropertyValue& rValue : _rValues)
+ {
+ MapString2PropertyEntry::const_iterator aPropPos = rProperties.find( rValue.Name );
+ if ( aPropPos != rProperties.end() )
+ {
+ DataAccessDescriptorProperty eProperty = aPropPos->second;
+ m_aValues[eProperty] = rValue.Value;
+ }
+ else
+ // unknown property
+ bValidPropsOnly = false;
+ }
+
+ if (bValidPropsOnly)
+ {
+ m_aAsSequence = _rValues;
+ m_bSequenceOutOfDate = false;
+ }
+ else
+ m_bSequenceOutOfDate = true;
+
+ return bValidPropsOnly;
+ }
+
+ bool ODADescriptorImpl::buildFrom( const Reference< XPropertySet >& _rxValues )
+ {
+ Reference< XPropertySetInfo > xPropInfo;
+ if (_rxValues.is())
+ xPropInfo = _rxValues->getPropertySetInfo();
+ if (!xPropInfo.is())
+ {
+ OSL_FAIL("ODADescriptorImpl::buildFrom: invalid property set!");
+ return false;
+ }
+
+ // build a PropertyValue sequence with the current values
+ const Sequence< Property > aProperties = xPropInfo->getProperties();
+
+ Sequence< PropertyValue > aValues(aProperties.getLength());
+ PropertyValue* pValues = aValues.getArray();
+
+ for (const Property& rProperty : aProperties)
+ {
+ pValues->Name = rProperty.Name;
+ pValues->Value = _rxValues->getPropertyValue(rProperty.Name);
+ ++pValues;
+ }
+
+ bool bValidPropsOnly = buildFrom(aValues);
+ m_bSetOutOfDate = !bValidPropsOnly;
+
+ return bValidPropsOnly;
+ }
+
+ void ODADescriptorImpl::invalidateExternRepresentations()
+ {
+ m_bSetOutOfDate = true;
+ m_bSequenceOutOfDate = true;
+ }
+
+ const ODADescriptorImpl::MapString2PropertyEntry& ODADescriptorImpl::getPropertyMap( )
+ {
+ // the properties we know
+ static MapString2PropertyEntry s_aProperties
+ {
+ { OUString("ActiveConnection"), DataAccessDescriptorProperty::Connection, },
+ { OUString("BookmarkSelection"), DataAccessDescriptorProperty::BookmarkSelection, },
+ { OUString("Column"), DataAccessDescriptorProperty::ColumnObject, },
+ { OUString("ColumnName"), DataAccessDescriptorProperty::ColumnName, },
+ { OUString("Command"), DataAccessDescriptorProperty::Command, },
+ { OUString("CommandType"), DataAccessDescriptorProperty::CommandType, },
+ { OUString("Component"), DataAccessDescriptorProperty::Component, },
+ { OUString("ConnectionResource"), DataAccessDescriptorProperty::ConnectionResource, },
+ { OUString("Cursor"), DataAccessDescriptorProperty::Cursor, },
+ { OUString("DataSourceName"), DataAccessDescriptorProperty::DataSource, },
+ { OUString("DatabaseLocation"), DataAccessDescriptorProperty::DatabaseLocation, },
+ { OUString("EscapeProcessing"), DataAccessDescriptorProperty::EscapeProcessing, },
+ { OUString("Filter"), DataAccessDescriptorProperty::Filter, },
+ { OUString("Selection"), DataAccessDescriptorProperty::Selection, }
+ };
+
+ return s_aProperties;
+ }
+
+ PropertyMapEntry const * ODADescriptorImpl::getPropertyMapEntry( const DescriptorValues::const_iterator& _rPos )
+ {
+ const MapString2PropertyEntry& rProperties = getPropertyMap();
+
+ DataAccessDescriptorProperty nNeededHandle = _rPos->first;
+
+ auto loop = std::find_if(rProperties.begin(), rProperties.end(),
+ [&nNeededHandle](const MapString2PropertyEntry::value_type& rProp) { return nNeededHandle == rProp.second; });
+ if (loop != rProperties.end())
+ return &*loop;
+ throw RuntimeException();
+ }
+
+ PropertyValue ODADescriptorImpl::buildPropertyValue( const DescriptorValues::const_iterator& _rPos )
+ {
+ // the map entry
+ PropertyMapEntry const * pProperty = getPropertyMapEntry( _rPos );
+
+ // build the property value
+ PropertyValue aReturn;
+ aReturn.Name = pProperty->first;
+ aReturn.Handle = static_cast<sal_Int32>(pProperty->second);
+ aReturn.Value = _rPos->second;
+ aReturn.State = PropertyState_DIRECT_VALUE;
+
+ // outta here
+ return aReturn;
+ }
+
+ void ODADescriptorImpl::updateSequence()
+ {
+ if (!m_bSequenceOutOfDate)
+ return;
+
+ m_aAsSequence.realloc(m_aValues.size());
+ PropertyValue* pValue = m_aAsSequence.getArray();
+
+ // loop through all our values
+ for ( DescriptorValues::const_iterator aLoop = m_aValues.begin();
+ aLoop != m_aValues.end();
+ ++aLoop, ++pValue
+ )
+ {
+ *pValue = buildPropertyValue(aLoop);
+ }
+
+ // don't need to rebuild next time
+ m_bSequenceOutOfDate = false;
+ }
+
+ ODataAccessDescriptor::ODataAccessDescriptor()
+ :m_pImpl(new ODADescriptorImpl)
+ {
+ }
+
+ ODataAccessDescriptor::ODataAccessDescriptor( const ODataAccessDescriptor& _rSource )
+ :m_pImpl(new ODADescriptorImpl(*_rSource.m_pImpl))
+ {
+ }
+
+ ODataAccessDescriptor::ODataAccessDescriptor(ODataAccessDescriptor&& _rSource) noexcept
+ :m_pImpl(std::move(_rSource.m_pImpl))
+ {
+ }
+
+ ODataAccessDescriptor& ODataAccessDescriptor::operator=(const ODataAccessDescriptor& _rSource)
+ {
+ if (this != &_rSource)
+ m_pImpl.reset(new ODADescriptorImpl(*_rSource.m_pImpl));
+ return *this;
+ }
+
+ ODataAccessDescriptor& ODataAccessDescriptor::operator=(ODataAccessDescriptor&& _rSource) noexcept
+ {
+ m_pImpl = std::move(_rSource.m_pImpl);
+ return *this;
+ }
+
+ ODataAccessDescriptor::ODataAccessDescriptor( const Reference< XPropertySet >& _rValues )
+ :m_pImpl(new ODADescriptorImpl)
+ {
+ m_pImpl->buildFrom(_rValues);
+ }
+
+ ODataAccessDescriptor::ODataAccessDescriptor( const Any& _rValues )
+ :m_pImpl(new ODADescriptorImpl)
+ {
+ // check if we know the format in the Any
+ Sequence< PropertyValue > aValues;
+ Reference< XPropertySet > xValues;
+ if ( _rValues >>= aValues )
+ m_pImpl->buildFrom( aValues );
+ else if ( _rValues >>= xValues )
+ m_pImpl->buildFrom( xValues );
+ }
+
+ ODataAccessDescriptor::ODataAccessDescriptor( const Sequence< PropertyValue >& _rValues )
+ :m_pImpl(new ODADescriptorImpl)
+ {
+ m_pImpl->buildFrom(_rValues);
+ }
+
+ ODataAccessDescriptor::~ODataAccessDescriptor()
+ {
+ }
+
+ void ODataAccessDescriptor::clear()
+ {
+ m_pImpl->m_aValues.clear();
+ }
+
+ void ODataAccessDescriptor::erase(DataAccessDescriptorProperty _eWhich)
+ {
+ OSL_ENSURE(has(_eWhich), "ODataAccessDescriptor::erase: invalid call!");
+ if (has(_eWhich))
+ m_pImpl->m_aValues.erase(_eWhich);
+ }
+
+ bool ODataAccessDescriptor::has(DataAccessDescriptorProperty _eWhich) const
+ {
+ return m_pImpl->m_aValues.find(_eWhich) != m_pImpl->m_aValues.end();
+ }
+
+ const Any& ODataAccessDescriptor::operator [] ( DataAccessDescriptorProperty _eWhich ) const
+ {
+ if (!has(_eWhich))
+ {
+ OSL_FAIL("ODataAccessDescriptor::operator[]: invalid accessor!");
+ static const Any aDummy;
+ return aDummy;
+ }
+
+ return m_pImpl->m_aValues[_eWhich];
+ }
+
+ Any& ODataAccessDescriptor::operator[] ( DataAccessDescriptorProperty _eWhich )
+ {
+ m_pImpl->invalidateExternRepresentations();
+ return m_pImpl->m_aValues[_eWhich];
+ }
+
+ void ODataAccessDescriptor::initializeFrom(const Sequence< PropertyValue >& _rValues)
+ {
+ clear();
+ m_pImpl->buildFrom(_rValues);
+ }
+
+ Sequence< PropertyValue > const & ODataAccessDescriptor::createPropertyValueSequence()
+ {
+ m_pImpl->updateSequence();
+ return m_pImpl->m_aAsSequence;
+ }
+
+ OUString ODataAccessDescriptor::getDataSource() const
+ {
+ OUString sDataSourceName;
+ if ( has(DataAccessDescriptorProperty::DataSource) )
+ (*this)[DataAccessDescriptorProperty::DataSource] >>= sDataSourceName;
+ else if ( has(DataAccessDescriptorProperty::DatabaseLocation) )
+ (*this)[DataAccessDescriptorProperty::DatabaseLocation] >>= sDataSourceName;
+ return sDataSourceName;
+ }
+
+ void ODataAccessDescriptor::setDataSource(const OUString& _sDataSourceNameOrLocation)
+ {
+ if ( !_sDataSourceNameOrLocation.isEmpty() )
+ {
+ INetURLObject aURL(_sDataSourceNameOrLocation);
+ (*this)[ (( aURL.GetProtocol() == INetProtocol::File ) ? DataAccessDescriptorProperty::DatabaseLocation : DataAccessDescriptorProperty::DataSource)] <<= _sDataSourceNameOrLocation;
+ }
+ else
+ (*this)[ DataAccessDescriptorProperty::DataSource ] <<= OUString();
+ }
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/form/databaselocationinput.cxx b/svx/source/form/databaselocationinput.cxx
new file mode 100644
index 000000000..018fd3f5e
--- /dev/null
+++ b/svx/source/form/databaselocationinput.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/databaselocationinput.hxx>
+#include <svx/dialmgr.hxx>
+
+#include <svx/strings.hrc>
+
+#include <com/sun/star/ui/dialogs/TemplateDescription.hpp>
+#include <com/sun/star/container/XNameAccess.hpp>
+#include <com/sun/star/uno/XComponentContext.hpp>
+
+#include <comphelper/namedvaluecollection.hxx>
+#include <rtl/ustrbuf.hxx>
+#include <sfx2/filedlghelper.hxx>
+#include <svl/filenotation.hxx>
+#include <svtools/inettbc.hxx>
+#include <tools/diagnose_ex.h>
+#include <unotools/confignode.hxx>
+#include <unotools/ucbhelper.hxx>
+#include <vcl/svapp.hxx>
+#include <vcl/weld.hxx>
+
+namespace svx
+{
+ using ::com::sun::star::uno::Sequence;
+ using ::com::sun::star::uno::Reference;
+ using ::com::sun::star::uno::XComponentContext;
+ using ::com::sun::star::container::XNameAccess;
+ using ::com::sun::star::uno::UNO_QUERY_THROW;
+ using ::com::sun::star::uno::Exception;
+
+ namespace TemplateDescription = ::com::sun::star::ui::dialogs::TemplateDescription;
+
+ class DatabaseLocationInputController_Impl
+ {
+ public:
+ DatabaseLocationInputController_Impl(
+ const Reference<XComponentContext>& _rContext,
+ SvtURLBox& _rLocationInput,
+ weld::Button& _rBrowseButton,
+ weld::Window& _rDialog
+ );
+
+ bool prepareCommit();
+ void setURL( const OUString& _rURL );
+ OUString getURL() const;
+
+ private:
+ void impl_initFilterProperties_nothrow();
+ void impl_onBrowseButtonClicked();
+ OUString impl_getCurrentURL() const;
+
+ DECL_LINK( OnButtonAction, weld::Button&, void );
+
+ private:
+ const Reference<XComponentContext> m_xContext;
+ SvtURLBox& m_rLocationInput;
+ weld::Window& m_rDialog;
+ Sequence< OUString > m_aFilterExtensions;
+ OUString m_sFilterUIName;
+ bool m_bNeedExistenceCheck;
+ };
+
+ DatabaseLocationInputController_Impl::DatabaseLocationInputController_Impl(const Reference<XComponentContext>& _rContext,
+ SvtURLBox& _rLocationInput, weld::Button& _rBrowseButton, weld::Window& _rDialog)
+ :m_xContext( _rContext )
+ ,m_rLocationInput( _rLocationInput )
+ ,m_rDialog( _rDialog )
+ ,m_bNeedExistenceCheck( true )
+ {
+ impl_initFilterProperties_nothrow();
+
+ // forward the allowed extensions to the input control
+ OUStringBuffer aExtensionList;
+ for ( auto const & extension : std::as_const(m_aFilterExtensions) )
+ {
+ aExtensionList.append( extension );
+ aExtensionList.append( ';' );
+ }
+ m_rLocationInput.SetFilter( aExtensionList.makeStringAndClear() );
+ _rBrowseButton.connect_clicked(LINK(this, DatabaseLocationInputController_Impl, OnButtonAction));
+ }
+
+ bool DatabaseLocationInputController_Impl::prepareCommit()
+ {
+ OUString sURL( impl_getCurrentURL() );
+ if ( sURL.isEmpty() )
+ return false;
+
+ // check if the name exists
+ if ( m_bNeedExistenceCheck )
+ {
+ if ( ::utl::UCBContentHelper::Exists( sURL ) )
+ {
+ std::unique_ptr<weld::MessageDialog> xQueryBox(Application::CreateMessageDialog(m_rLocationInput.getWidget(),
+ VclMessageType::Question, VclButtonsType::YesNo,
+ SvxResId(RID_STR_ALREADYEXISTOVERWRITE)));
+ if (xQueryBox->run() != RET_YES)
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ void DatabaseLocationInputController_Impl::setURL( const OUString& _rURL )
+ {
+ ::svt::OFileNotation aTransformer( _rURL );
+ m_rLocationInput.set_entry_text( aTransformer.get( ::svt::OFileNotation::N_SYSTEM ) );
+ }
+
+ OUString DatabaseLocationInputController_Impl::getURL() const
+ {
+ return impl_getCurrentURL();
+ }
+
+ void DatabaseLocationInputController_Impl::impl_initFilterProperties_nothrow()
+ {
+ try
+ {
+ // get the name of the default filter for database documents
+ ::utl::OConfigurationTreeRoot aConfig(
+ ::utl::OConfigurationTreeRoot::createWithComponentContext(
+ m_xContext,
+ "/org.openoffice.Setup/Office/Factories/com.sun.star.sdb.OfficeDatabaseDocument"
+ ) );
+ OUString sDatabaseFilter;
+ OSL_VERIFY( aConfig.getNodeValue( "ooSetupFactoryActualFilter" ) >>= sDatabaseFilter );
+
+ // get the type this filter is responsible for
+ Reference< XNameAccess > xFilterFactory(
+ m_xContext->getServiceManager()->createInstanceWithContext("com.sun.star.document.FilterFactory", m_xContext),
+ UNO_QUERY_THROW );
+ ::comphelper::NamedValueCollection aFilterProperties( xFilterFactory->getByName( sDatabaseFilter ) );
+ OUString sDocumentType = aFilterProperties.getOrDefault( "Type", OUString() );
+
+ // get the extension(s) for this type
+ Reference< XNameAccess > xTypeDetection(
+ m_xContext->getServiceManager()->createInstanceWithContext("com.sun.star.document.TypeDetection", m_xContext),
+ UNO_QUERY_THROW );
+
+ ::comphelper::NamedValueCollection aTypeProperties( xTypeDetection->getByName( sDocumentType ) );
+ m_aFilterExtensions = aTypeProperties.getOrDefault( "Extensions", m_aFilterExtensions );
+ m_sFilterUIName = aTypeProperties.getOrDefault( "UIName", m_sFilterUIName );
+ }
+ catch( const Exception& )
+ {
+ DBG_UNHANDLED_EXCEPTION("svx");
+ }
+
+ // ensure we have at least one extension
+ OSL_ENSURE( m_aFilterExtensions.hasElements(),
+ "DatabaseLocationInputController_Impl::impl_initFilterProperties_nothrow: unable to determine the file extension(s)!" );
+ if ( !m_aFilterExtensions.hasElements() )
+ {
+ m_aFilterExtensions = { "*.odb" };
+ }
+ }
+
+ IMPL_LINK_NOARG(DatabaseLocationInputController_Impl, OnButtonAction, weld::Button&, void)
+ {
+ impl_onBrowseButtonClicked();
+ }
+
+ OUString DatabaseLocationInputController_Impl::impl_getCurrentURL() const
+ {
+ OUString sCurrentFile( m_rLocationInput.get_active_text() );
+ if ( !sCurrentFile.isEmpty() )
+ {
+ ::svt::OFileNotation aCurrentFile( sCurrentFile );
+ sCurrentFile = aCurrentFile.get( ::svt::OFileNotation::N_URL );
+ }
+ return sCurrentFile;
+ }
+
+ void DatabaseLocationInputController_Impl::impl_onBrowseButtonClicked()
+ {
+ ::sfx2::FileDialogHelper aFileDlg(
+ TemplateDescription::FILESAVE_AUTOEXTENSION,
+ FileDialogFlags::NONE,
+ &m_rDialog
+ );
+ aFileDlg.SetDisplayDirectory( impl_getCurrentURL() );
+
+ aFileDlg.AddFilter( m_sFilterUIName, "*." + m_aFilterExtensions[0] );
+ aFileDlg.SetCurrentFilter( m_sFilterUIName );
+
+ if ( aFileDlg.Execute() == ERRCODE_NONE )
+ {
+ INetURLObject aURL( aFileDlg.GetPath() );
+ if( aURL.GetProtocol() != INetProtocol::NotValid )
+ {
+ ::svt::OFileNotation aFileNotation( aURL.GetMainURL( INetURLObject::DecodeMechanism::NONE ) );
+ m_rLocationInput.set_entry_text(aFileNotation.get(::svt::OFileNotation::N_SYSTEM));
+ m_rLocationInput.trigger_changed();
+ // the dialog already checked for the file's existence, so we don't need to, again
+ m_bNeedExistenceCheck = false;
+ }
+ }
+ }
+
+ DatabaseLocationInputController::DatabaseLocationInputController( const Reference<XComponentContext>& _rContext,
+ SvtURLBox& _rLocationInput, weld::Button& _rBrowseButton, weld::Window& _rDialog )
+ :m_pImpl( new DatabaseLocationInputController_Impl( _rContext, _rLocationInput, _rBrowseButton, _rDialog ) )
+ {
+ }
+
+ DatabaseLocationInputController::~DatabaseLocationInputController()
+ {
+ }
+
+ bool DatabaseLocationInputController::prepareCommit()
+ {
+ return m_pImpl->prepareCommit();
+ }
+
+ void DatabaseLocationInputController::setURL( const OUString& _rURL )
+ {
+ m_pImpl->setURL( _rURL );
+ }
+
+ OUString DatabaseLocationInputController::getURL() const
+ {
+ return m_pImpl->getURL();
+ }
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/form/datalistener.cxx b/svx/source/form/datalistener.cxx
new file mode 100644
index 000000000..fda74a92c
--- /dev/null
+++ b/svx/source/form/datalistener.cxx
@@ -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 .
+ */
+
+
+#include <datalistener.hxx>
+#include <datanavi.hxx>
+
+#include <sal/log.hxx>
+
+using namespace ::com::sun::star::container;
+using namespace ::com::sun::star::frame;
+using namespace ::com::sun::star::lang;
+using namespace ::com::sun::star::uno;
+using namespace ::com::sun::star::xml::dom::events;
+
+
+namespace svxform
+{
+
+
+ DataListener::DataListener( DataNavigatorWindow* pNaviWin ) :
+
+ m_pNaviWin( pNaviWin )
+
+ {
+ DBG_ASSERT( m_pNaviWin, "DataListener::Ctor(): no navigator win" );
+ }
+
+ DataListener::~DataListener()
+ {
+ }
+
+ // XContainerListener
+ void SAL_CALL DataListener::elementInserted( const ContainerEvent& /*Event*/ )
+ {
+ m_pNaviWin->NotifyChanges();
+ }
+
+ void SAL_CALL DataListener::elementRemoved( const ContainerEvent& /*Event*/ )
+ {
+ m_pNaviWin->NotifyChanges();
+ }
+
+ void SAL_CALL DataListener::elementReplaced( const ContainerEvent& /*Event*/ )
+ {
+ m_pNaviWin->NotifyChanges();
+ }
+
+ // XFrameActionListener
+ void SAL_CALL DataListener::frameAction( const FrameActionEvent& rActionEvt )
+ {
+ if ( FrameAction_COMPONENT_ATTACHED == rActionEvt.Action ||
+ FrameAction_COMPONENT_REATTACHED == rActionEvt.Action )
+ {
+ m_pNaviWin->NotifyChanges( FrameAction_COMPONENT_REATTACHED == rActionEvt.Action );
+ }
+ }
+
+ // xml::dom::events::XEventListener
+ void SAL_CALL DataListener::handleEvent( const Reference< XEvent >& /*evt*/ )
+ {
+ m_pNaviWin->NotifyChanges();
+ }
+
+ // lang::XEventListener
+ void SAL_CALL DataListener::disposing( const EventObject& /*Source*/ )
+ {
+ SAL_WARN( "svx.form", "disposing" );
+ }
+
+
+}
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/form/datanavi.cxx b/svx/source/form/datanavi.cxx
new file mode 100644
index 000000000..80b4dab1b
--- /dev/null
+++ b/svx/source/form/datanavi.cxx
@@ -0,0 +1,3090 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * 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 <sal/log.hxx>
+#include <datanavi.hxx>
+#include <fmservs.hxx>
+
+#include <bitmaps.hlst>
+#include <fpicker/strings.hrc>
+#include <o3tl/safeint.hxx>
+#include <o3tl/string_view.hxx>
+#include <svx/svxids.hrc>
+#include <tools/diagnose_ex.h>
+#include <unotools/resmgr.hxx>
+#include <svx/xmlexchg.hxx>
+#include <unotools/viewoptions.hxx>
+#include <sfx2/filedlghelper.hxx>
+#include <sfx2/viewfrm.hxx>
+#include <sfx2/objsh.hxx>
+#include <sfx2/bindings.hxx>
+#include <sfx2/dispatch.hxx>
+#include <vcl/commandevent.hxx>
+#include <vcl/event.hxx>
+#include <vcl/weld.hxx>
+#include <com/sun/star/beans/PropertyAttribute.hpp>
+#include <com/sun/star/container/XSet.hpp>
+#include <com/sun/star/frame/XController.hpp>
+#include <com/sun/star/ui/dialogs/TemplateDescription.hpp>
+#include <com/sun/star/xforms/XFormsSupplier.hpp>
+#include <com/sun/star/xml/dom/XDocument.hpp>
+#include <comphelper/string.hxx>
+
+using namespace ::com::sun::star::beans;
+using namespace ::com::sun::star::container;
+using namespace ::com::sun::star::datatransfer;
+using namespace ::com::sun::star::frame;
+using namespace ::com::sun::star::uno;
+using namespace ::com::sun::star::xml::dom::events;
+using namespace ::svx;
+
+constexpr OUStringLiteral CFGNAME_DATANAVIGATOR = u"DataNavigator";
+constexpr OUStringLiteral CFGNAME_SHOWDETAILS = u"ShowDetails";
+constexpr OUStringLiteral MSG_VARIABLE = u"%1";
+constexpr OUStringLiteral MODELNAME = u"$MODELNAME";
+constexpr OUStringLiteral INSTANCENAME = u"$INSTANCENAME";
+constexpr OUStringLiteral ELEMENTNAME = u"$ELEMENTNAME";
+constexpr OUStringLiteral ATTRIBUTENAME = u"$ATTRIBUTENAME";
+constexpr OUStringLiteral SUBMISSIONNAME = u"$SUBMISSIONNAME";
+constexpr OUStringLiteral BINDINGNAME = u"$BINDINGNAME";
+
+
+namespace svxform
+{
+
+ // properties of instance
+ constexpr OUStringLiteral PN_INSTANCE_MODEL = u"Instance";
+ constexpr OUStringLiteral PN_INSTANCE_ID = u"ID";
+ constexpr OUStringLiteral PN_INSTANCE_URL = u"URL";
+
+ // properties of binding
+ constexpr OUStringLiteral PN_BINDING_ID = u"BindingID";
+ constexpr OUStringLiteral PN_BINDING_EXPR = u"BindingExpression";
+ constexpr OUStringLiteral PN_BINDING_MODEL = u"Model";
+ constexpr OUStringLiteral PN_BINDING_NAMESPACES = u"ModelNamespaces";
+ constexpr OUStringLiteral PN_READONLY_EXPR = u"ReadonlyExpression";
+ constexpr OUStringLiteral PN_RELEVANT_EXPR = u"RelevantExpression";
+ constexpr OUStringLiteral PN_REQUIRED_EXPR = u"RequiredExpression";
+ constexpr OUStringLiteral PN_CONSTRAINT_EXPR = u"ConstraintExpression";
+ constexpr OUStringLiteral PN_CALCULATE_EXPR = u"CalculateExpression";
+ constexpr OUStringLiteral PN_BINDING_TYPE = u"Type";
+
+ // properties of submission
+ constexpr OUStringLiteral PN_SUBMISSION_ID = u"ID";
+ constexpr OUStringLiteral PN_SUBMISSION_BIND = u"Bind";
+ constexpr OUStringLiteral PN_SUBMISSION_REF = u"Ref";
+ constexpr OUStringLiteral PN_SUBMISSION_ACTION = u"Action";
+ constexpr OUStringLiteral PN_SUBMISSION_METHOD = u"Method";
+ constexpr OUStringLiteral PN_SUBMISSION_REPLACE = u"Replace";
+
+ // other const strings
+ constexpr OUStringLiteral TRUE_VALUE = u"true()";
+ constexpr OUStringLiteral NEW_ELEMENT = u"newElement";
+ constexpr OUStringLiteral NEW_ATTRIBUTE = u"newAttribute";
+ constexpr OUStringLiteral EVENTTYPE_CHARDATA = u"DOMCharacterDataModified";
+ constexpr OUStringLiteral EVENTTYPE_ATTR = u"DOMAttrModified";
+
+ #define MIN_PAGE_COUNT 3 // at least one instance, one submission and one binding page
+
+ struct ItemNode
+ {
+ Reference< css::xml::dom::XNode > m_xNode;
+ Reference< XPropertySet > m_xPropSet;
+
+ explicit ItemNode( const Reference< css::xml::dom::XNode >& _rxNode ) :
+ m_xNode( _rxNode ) {}
+ explicit ItemNode( const Reference< XPropertySet >& _rxSet ) :
+ m_xPropSet( _rxSet ) {}
+ };
+
+ DataTreeDropTarget::DataTreeDropTarget(weld::TreeView& rWidget)
+ : DropTargetHelper(rWidget.get_drop_target())
+ {
+ }
+
+ sal_Int8 DataTreeDropTarget::AcceptDrop( const AcceptDropEvent& /*rEvt*/ )
+ {
+ return DND_ACTION_NONE;
+ }
+
+ sal_Int8 DataTreeDropTarget::ExecuteDrop( const ExecuteDropEvent& /*rEvt*/ )
+ {
+ return DND_ACTION_NONE;
+ }
+
+ IMPL_LINK(XFormsPage, PopupMenuHdl, const CommandEvent&, rCEvt, bool)
+ {
+ if (rCEvt.GetCommand() != CommandEventId::ContextMenu)
+ return false;
+
+ Point aPos(rCEvt.GetMousePosPixel());
+
+ if (m_xItemList->get_dest_row_at_pos(aPos, m_xScratchIter.get(), false) && !m_xItemList->is_selected(*m_xScratchIter))
+ {
+ m_xItemList->select(*m_xScratchIter);
+ ItemSelectHdl(*m_xItemList);
+ }
+
+ std::unique_ptr<weld::Builder> xBuilder(Application::CreateBuilder(m_xItemList.get(), "svx/ui/formdatamenu.ui"));
+ std::unique_ptr<weld::Menu> xMenu(xBuilder->weld_menu("menu"));
+
+ if (DGTInstance == m_eGroup)
+ xMenu->set_visible("additem", false);
+ else
+ {
+ xMenu->set_visible("addelement", false);
+ xMenu->set_visible("addattribute", false);
+
+ if (DGTSubmission == m_eGroup)
+ {
+ xMenu->set_label("additem", SvxResId(RID_STR_DATANAV_ADD_SUBMISSION));
+ xMenu->set_label("edit", SvxResId(RID_STR_DATANAV_EDIT_SUBMISSION));
+ xMenu->set_label("delete", SvxResId(RID_STR_DATANAV_REMOVE_SUBMISSION));
+ }
+ else
+ {
+ xMenu->set_label("additem", SvxResId(RID_STR_DATANAV_ADD_BINDING));
+ xMenu->set_label("edit", SvxResId(RID_STR_DATANAV_EDIT_BINDING));
+ xMenu->set_label("delete", SvxResId(RID_STR_DATANAV_REMOVE_BINDING));
+ }
+ }
+ EnableMenuItems(xMenu.get());
+ OString sCommand = xMenu->popup_at_rect(m_xItemList.get(), tools::Rectangle(aPos, Size(1,1)));
+ if (!sCommand.isEmpty())
+ DoMenuAction(sCommand);
+ return true;
+ }
+
+ void XFormsPage::DeleteAndClearTree()
+ {
+ m_xItemList->all_foreach([this](weld::TreeIter& rEntry) {
+ delete weld::fromId<ItemNode*>(m_xItemList->get_id(rEntry));
+ return false;
+ });
+ m_xItemList->clear();
+ }
+
+ XFormsPage::XFormsPage(weld::Container* pPage, DataNavigatorWindow* _pNaviWin, DataGroupType _eGroup)
+ : BuilderPage(pPage, nullptr, "svx/ui/xformspage.ui", "XFormsPage")
+ , m_pParent(pPage)
+ , m_xToolBox(m_xBuilder->weld_toolbar("toolbar"))
+ , m_xItemList(m_xBuilder->weld_tree_view("items"))
+ , m_xScratchIter(m_xItemList->make_iterator())
+ , m_aDropHelper(*m_xItemList)
+ , m_pNaviWin(_pNaviWin)
+ , m_bHasModel(false)
+ , m_eGroup(_eGroup)
+ , m_bLinkOnce(false)
+ {
+ m_xItemList->set_show_expanders(DGTInstance == m_eGroup || DGTSubmission == m_eGroup);
+
+ if ( DGTInstance == m_eGroup )
+ m_xToolBox->set_item_visible("additem", false);
+ else
+ {
+ m_xToolBox->set_item_visible("addelement", false);
+ m_xToolBox->set_item_visible("addattribute", false);
+
+ if ( DGTSubmission == m_eGroup )
+ {
+ m_xToolBox->set_item_label("additem", SvxResId(RID_STR_DATANAV_ADD_SUBMISSION));
+ m_xToolBox->set_item_label("edit", SvxResId(RID_STR_DATANAV_EDIT_SUBMISSION));
+ m_xToolBox->set_item_label("delete", SvxResId(RID_STR_DATANAV_REMOVE_SUBMISSION));
+ }
+ else
+ {
+ m_xToolBox->set_item_label("additem", SvxResId(RID_STR_DATANAV_ADD_BINDING));
+ m_xToolBox->set_item_label("edit", SvxResId(RID_STR_DATANAV_EDIT_BINDING));
+ m_xToolBox->set_item_label("delete", SvxResId(RID_STR_DATANAV_REMOVE_BINDING));
+ }
+ }
+
+ m_xToolBox->connect_clicked(LINK(this, XFormsPage, TbxSelectHdl));
+
+ m_xItemList->connect_changed(LINK(this, XFormsPage, ItemSelectHdl));
+ m_xItemList->connect_key_press(LINK(this, XFormsPage, KeyInputHdl));
+ m_xItemList->connect_popup_menu(LINK(this, XFormsPage, PopupMenuHdl));
+ ItemSelectHdl(*m_xItemList);
+ }
+
+ XFormsPage::~XFormsPage()
+ {
+ DeleteAndClearTree();
+ m_pNaviWin = nullptr;
+ m_pParent->move(m_xContainer.get(), nullptr);
+ }
+
+ IMPL_LINK(XFormsPage, TbxSelectHdl, const OString&, rIdent, void)
+ {
+ DoToolBoxAction(rIdent);
+ }
+
+ IMPL_LINK_NOARG(XFormsPage, ItemSelectHdl, weld::TreeView&, void)
+ {
+ EnableMenuItems(nullptr);
+ PrepDnD();
+ }
+
+ void XFormsPage::PrepDnD()
+ {
+ rtl::Reference<TransferDataContainer> xTransferable(new TransferDataContainer);
+ m_xItemList->enable_drag_source(xTransferable, DND_ACTION_NONE);
+
+ if (!m_xItemList->get_selected(m_xScratchIter.get()))
+ {
+ // no drag without an entry
+ return;
+ }
+
+ if ( m_eGroup == DGTBinding )
+ {
+ // for the moment, bindings cannot be dragged.
+ // #i59395# / 2005-12-15 / frank.schoenheit@sun.com
+ return;
+ }
+
+ // GetServiceNameForNode() requires a datatype repository which
+ // will be automatically build if requested???
+ Reference< css::xforms::XModel > xModel( GetXFormsHelper(), UNO_QUERY );
+ Reference< css::xforms::XDataTypeRepository > xDataTypes =
+ xModel->getDataTypeRepository();
+ if(!xDataTypes.is())
+ return;
+
+ ItemNode *pItemNode = weld::fromId<ItemNode*>(m_xItemList->get_id(*m_xScratchIter));
+ if (!pItemNode)
+ {
+ // the only known (and allowed?) case where this happens are sub-entries of a submission
+ // entry
+ DBG_ASSERT( DGTSubmission == m_eGroup, "DataTreeListBox::StartDrag: how this?" );
+ bool bSelected = m_xItemList->iter_parent(*m_xScratchIter);
+ DBG_ASSERT(bSelected && !m_xItemList->get_iter_depth(*m_xScratchIter), "DataTreeListBox::StartDrag: what kind of entry *is* this?");
+ // on the submission page, we have only top-level entries (the submission themself)
+ // plus direct children of those (facets of a submission)
+ pItemNode = bSelected ? weld::fromId<ItemNode*>(m_xItemList->get_id(*m_xScratchIter)) : nullptr;
+ if (!pItemNode)
+ return;
+ }
+
+ OXFormsDescriptor desc;
+ desc.szName = m_xItemList->get_text(*m_xScratchIter);
+ if(pItemNode->m_xNode.is()) {
+ // a valid node interface tells us that we need to create a control from a binding
+ desc.szServiceName = GetServiceNameForNode(pItemNode->m_xNode);
+ desc.xPropSet = GetBindingForNode(pItemNode->m_xNode);
+ DBG_ASSERT( desc.xPropSet.is(), "DataTreeListBox::StartDrag(): invalid node binding" );
+ }
+ else {
+ desc.szServiceName = FM_COMPONENT_COMMANDBUTTON;
+ desc.xPropSet = pItemNode->m_xPropSet;
+ }
+ xTransferable = rtl::Reference<TransferDataContainer>(new OXFormsTransferable(desc));
+ m_xItemList->enable_drag_source(xTransferable, DND_ACTION_COPY);
+ }
+
+ void XFormsPage::AddChildren(const weld::TreeIter* _pParent,
+ const Reference< css::xml::dom::XNode >& _xNode)
+ {
+ DBG_ASSERT( m_xUIHelper.is(), "XFormsPage::AddChildren(): invalid UIHelper" );
+
+ try
+ {
+ Reference< css::xml::dom::XNodeList > xNodeList = _xNode->getChildNodes();
+ if ( xNodeList.is() )
+ {
+ bool bShowDetails = m_pNaviWin->IsShowDetails();
+ sal_Int32 i, nNodeCount = xNodeList->getLength();
+ for ( i = 0; i < nNodeCount; ++i )
+ {
+ Reference< css::xml::dom::XNode > xChild = xNodeList->item(i);
+ css::xml::dom::NodeType eChildType = xChild->getNodeType();
+ OUString aExpImg;
+ switch ( eChildType )
+ {
+ case css::xml::dom::NodeType_ATTRIBUTE_NODE:
+ aExpImg = RID_SVXBMP_ATTRIBUTE;
+ break;
+ case css::xml::dom::NodeType_ELEMENT_NODE:
+ aExpImg = RID_SVXBMP_ELEMENT;
+ break;
+ case css::xml::dom::NodeType_TEXT_NODE:
+ aExpImg = RID_SVXBMP_TEXT;
+ break;
+ default:
+ aExpImg = RID_SVXBMP_OTHER;
+ }
+
+ OUString sName = m_xUIHelper->getNodeDisplayName( xChild, bShowDetails );
+ if ( !sName.isEmpty() )
+ {
+ ItemNode* pNode = new ItemNode( xChild );
+ OUString sId(weld::toId(pNode));
+ std::unique_ptr<weld::TreeIter> xEntry = m_xItemList->make_iterator();
+ m_xItemList->insert(_pParent, -1, &sName, &sId, nullptr, nullptr, false, xEntry.get());
+ m_xItemList->set_image(*xEntry, aExpImg);
+
+ if ( xChild->hasAttributes() )
+ {
+ Reference< css::xml::dom::XNamedNodeMap > xMap = xChild->getAttributes();
+ if ( xMap.is() )
+ {
+ aExpImg = RID_SVXBMP_ATTRIBUTE;
+ sal_Int32 j, nMapLen = xMap->getLength();
+ for ( j = 0; j < nMapLen; ++j )
+ {
+ Reference< css::xml::dom::XNode > xAttr = xMap->item(j);
+ pNode = new ItemNode( xAttr );
+ OUString sSubId(weld::toId(pNode));
+ OUString sAttrName = m_xUIHelper->getNodeDisplayName( xAttr, bShowDetails );
+ m_xItemList->insert(xEntry.get(), -1, &sAttrName, &sSubId, nullptr, nullptr, false, m_xScratchIter.get());
+ m_xItemList->set_image(*m_xScratchIter, aExpImg);
+ }
+ }
+ }
+ if ( xChild->hasChildNodes() )
+ AddChildren(xEntry.get(), xChild);
+ }
+ }
+ }
+ }
+ catch( Exception& )
+ {
+ DBG_UNHANDLED_EXCEPTION("svx");
+ }
+ }
+
+ bool XFormsPage::DoToolBoxAction(std::string_view rToolBoxID)
+ {
+ bool bHandled = false;
+ bool bIsDocModified = false;
+ m_pNaviWin->DisableNotify( true );
+
+ if (rToolBoxID == "additem" || rToolBoxID == "addelement" || rToolBoxID == "addattribute")
+ {
+ bHandled = true;
+ Reference< css::xforms::XModel > xModel( m_xUIHelper, UNO_QUERY );
+ DBG_ASSERT( xModel.is(), "XFormsPage::DoToolBoxAction(): Action without model" );
+ if ( DGTSubmission == m_eGroup )
+ {
+ AddSubmissionDialog aDlg(m_pNaviWin->GetFrameWeld(), nullptr, m_xUIHelper);
+ if ( aDlg.run() == RET_OK && aDlg.GetNewSubmission().is() )
+ {
+ try
+ {
+ Reference< css::xforms::XSubmission > xNewSubmission = aDlg.GetNewSubmission();
+ Reference< XSet > xSubmissions = xModel->getSubmissions();
+ xSubmissions->insert( Any( xNewSubmission ) );
+ AddEntry(xNewSubmission, m_xScratchIter.get());
+ m_xItemList->select(*m_xScratchIter);
+ bIsDocModified = true;
+ }
+ catch ( Exception const & )
+ {
+ TOOLS_WARN_EXCEPTION( "svx.form", "XFormsPage::DoToolBoxAction()" );
+ }
+ }
+ }
+ else
+ {
+ DataItemType eType = DITElement;
+
+ std::unique_ptr<weld::TreeIter> xEntry(m_xItemList->make_iterator());
+ bool bEntry = m_xItemList->get_selected(xEntry.get());
+
+ std::unique_ptr<ItemNode> pNode;
+ Reference< css::xml::dom::XNode > xParentNode;
+ Reference< XPropertySet > xNewBinding;
+ TranslateId pResId;
+ bool bIsElement = true;
+ if ( DGTInstance == m_eGroup )
+ {
+ if ( !m_sInstanceURL.isEmpty() )
+ {
+ LinkedInstanceWarningBox aMsgBox(m_pNaviWin->GetFrameWeld());
+ if (aMsgBox.run() != RET_OK)
+ return bHandled;
+ }
+
+ DBG_ASSERT( bEntry, "XFormsPage::DoToolBoxAction(): no entry" );
+ ItemNode* pParentNode = weld::fromId<ItemNode*>(m_xItemList->get_id(*xEntry));
+ DBG_ASSERT( pParentNode, "XFormsPage::DoToolBoxAction(): no parent node" );
+ xParentNode = pParentNode->m_xNode;
+ Reference< css::xml::dom::XNode > xNewNode;
+ if (rToolBoxID == "addelement")
+ {
+ try
+ {
+ pResId = RID_STR_DATANAV_ADD_ELEMENT;
+ xNewNode = m_xUIHelper->createElement( xParentNode, NEW_ELEMENT );
+ }
+ catch ( Exception const & )
+ {
+ TOOLS_WARN_EXCEPTION( "svx.form", "XFormsPage::DoToolBoxAction(): exception while create element" );
+ }
+ }
+ else
+ {
+ pResId = RID_STR_DATANAV_ADD_ATTRIBUTE;
+ bIsElement = false;
+ eType = DITAttribute;
+ try
+ {
+ xNewNode = m_xUIHelper->createAttribute( xParentNode, NEW_ATTRIBUTE );
+ }
+ catch ( Exception const & )
+ {
+ TOOLS_WARN_EXCEPTION( "svx.form", "XFormsPage::DoToolBoxAction(): exception while create attribute" );
+ }
+ }
+
+ try
+ {
+ xNewNode = xParentNode->appendChild( xNewNode );
+ }
+ catch ( Exception const & )
+ {
+ TOOLS_WARN_EXCEPTION( "svx.form", "XFormsPage::DoToolBoxAction(): exception while append child" );
+ }
+
+ try
+ {
+ Reference< css::xml::dom::XNode > xPNode;
+ if ( xNewNode.is() )
+ xPNode = xNewNode->getParentNode();
+ // attributes don't have parents in the DOM model
+ DBG_ASSERT( rToolBoxID == "addattribute"
+ || xPNode.is(), "XFormsPage::DoToolboxAction(): node not added" );
+ }
+ catch ( Exception const & )
+ {
+ TOOLS_WARN_EXCEPTION( "svx.form", "XFormsPage::DoToolboxAction()" );
+ }
+
+ try
+ {
+ m_xUIHelper->getBindingForNode( xNewNode, true );
+ }
+ catch ( Exception const & )
+ {
+ TOOLS_WARN_EXCEPTION( "svx.form", "XFormsPage::DoToolBoxAction(): exception while get binding for node" );
+ }
+ pNode.reset(new ItemNode( xNewNode ));
+ }
+ else
+ {
+ try
+ {
+ pResId = RID_STR_DATANAV_ADD_BINDING;
+ xNewBinding = xModel->createBinding();
+ Reference< XSet > xBindings = xModel->getBindings();
+ xBindings->insert( Any( xNewBinding ) );
+ pNode.reset(new ItemNode( xNewBinding ));
+ eType = DITBinding;
+ }
+ catch ( Exception const & )
+ {
+ TOOLS_WARN_EXCEPTION( "svx.form", "XFormsPage::DoToolBoxAction(): exception while adding binding" );
+ }
+ }
+
+ AddDataItemDialog aDlg(m_pNaviWin->GetFrameWeld(), pNode.get(), m_xUIHelper);
+ aDlg.set_title(SvxResId(pResId));
+ aDlg.InitText( eType );
+ short nReturn = aDlg.run();
+ if ( DGTInstance == m_eGroup )
+ {
+ if ( RET_OK == nReturn )
+ {
+ AddEntry( std::move(pNode), bIsElement, m_xScratchIter.get());
+ m_xItemList->scroll_to_row(*m_xScratchIter);
+ m_xItemList->select(*m_xScratchIter);
+ bIsDocModified = true;
+ }
+ else
+ {
+ try
+ {
+ Reference< css::xml::dom::XNode > xPNode;
+ Reference< css::xml::dom::XNode > xNode =
+ xParentNode->removeChild( pNode->m_xNode );
+ if ( xNode.is() )
+ xPNode = xNode->getParentNode();
+ DBG_ASSERT( !xPNode.is(), "XFormsPage::RemoveEntry(): node not removed" );
+ }
+ catch ( Exception const & )
+ {
+ TOOLS_WARN_EXCEPTION( "svx.form", "XFormsPage::DoToolboxAction()" );
+ }
+ }
+ }
+ else
+ {
+ if ( RET_OK == nReturn )
+ {
+ AddEntry(xNewBinding, m_xScratchIter.get());
+ m_xItemList->select(*m_xScratchIter);
+ bIsDocModified = true;
+ }
+ else
+ {
+ try
+ {
+ Reference< XSet > xBindings = xModel->getBindings();
+ xBindings->remove( Any( xNewBinding ) );
+ }
+ catch ( Exception const & )
+ {
+ TOOLS_WARN_EXCEPTION( "svx.form", "XFormsPage::DoToolboxAction()" );
+ }
+ }
+ }
+ }
+ }
+ else if (rToolBoxID == "edit")
+ {
+ bHandled = true;
+
+ std::unique_ptr<weld::TreeIter> xEntry(m_xItemList->make_iterator());
+ bool bEntry = m_xItemList->get_selected(xEntry.get());
+ if ( bEntry )
+ {
+ if ( DGTSubmission == m_eGroup && m_xItemList->get_iter_depth(*xEntry) )
+ {
+ m_xItemList->iter_parent(*xEntry);
+ }
+ ItemNode* pNode = weld::fromId<ItemNode*>(m_xItemList->get_id(*xEntry));
+ if ( DGTInstance == m_eGroup || DGTBinding == m_eGroup )
+ {
+ if ( DGTInstance == m_eGroup && !m_sInstanceURL.isEmpty() )
+ {
+ LinkedInstanceWarningBox aMsgBox(m_pNaviWin->GetFrameWeld());
+ if (aMsgBox.run() != RET_OK)
+ return bHandled;
+ }
+
+ AddDataItemDialog aDlg(m_pNaviWin->GetFrameWeld(), pNode, m_xUIHelper);
+ DataItemType eType = DITElement;
+ TranslateId pResId = RID_STR_DATANAV_EDIT_ELEMENT;
+ if ( pNode && pNode->m_xNode.is() )
+ {
+ try
+ {
+ css::xml::dom::NodeType eChildType = pNode->m_xNode->getNodeType();
+ if ( eChildType == css::xml::dom::NodeType_ATTRIBUTE_NODE )
+ {
+ pResId = RID_STR_DATANAV_EDIT_ATTRIBUTE;
+ eType = DITAttribute;
+ }
+ }
+ catch ( Exception const & )
+ {
+ TOOLS_WARN_EXCEPTION( "svx.form", "XFormsPage::DoToolboxAction()" );
+ }
+ }
+ else if ( DGTBinding == m_eGroup )
+ {
+ pResId = RID_STR_DATANAV_EDIT_BINDING;
+ eType = DITBinding;
+ }
+ aDlg.set_title(SvxResId(pResId));
+ aDlg.InitText( eType );
+ if (aDlg.run() == RET_OK)
+ {
+ // Set the new name
+ OUString sNewName;
+ if ( DGTInstance == m_eGroup )
+ {
+ try
+ {
+ sNewName = m_xUIHelper->getNodeDisplayName(
+ pNode->m_xNode, m_pNaviWin->IsShowDetails() );
+ }
+ catch ( Exception const & )
+ {
+ TOOLS_WARN_EXCEPTION( "svx.form", "XFormsPage::DoToolboxAction()" );
+ }
+ }
+ else if (pNode)
+ {
+ try
+ {
+ OUString sTemp;
+ pNode->m_xPropSet->getPropertyValue( PN_BINDING_ID ) >>= sTemp;
+ sNewName += sTemp + ": ";
+ pNode->m_xPropSet->getPropertyValue( PN_BINDING_EXPR ) >>= sTemp;
+ sNewName += sTemp;
+ }
+ catch ( Exception const & )
+ {
+ TOOLS_WARN_EXCEPTION( "svx.form", "XFormsPage::DoToolboxAction()" );
+ }
+ }
+
+ m_xItemList->set_text(*xEntry, sNewName);
+ bIsDocModified = true;
+ }
+ }
+ else
+ {
+ AddSubmissionDialog aDlg(m_pNaviWin->GetFrameWeld(), pNode, m_xUIHelper);
+ aDlg.set_title(SvxResId(RID_STR_DATANAV_EDIT_SUBMISSION));
+ if (aDlg.run() == RET_OK)
+ {
+ EditEntry( pNode->m_xPropSet );
+ bIsDocModified = true;
+ }
+ }
+ }
+ }
+ else if (rToolBoxID == "delete")
+ {
+ bHandled = true;
+ if ( DGTInstance == m_eGroup && !m_sInstanceURL.isEmpty() )
+ {
+ LinkedInstanceWarningBox aMsgBox(m_pNaviWin->GetFrameWeld());
+ if (aMsgBox.run() != RET_OK)
+ return bHandled;
+ }
+ bIsDocModified = RemoveEntry();
+ }
+ else
+ {
+ OSL_FAIL( "XFormsPage::DoToolboxAction: unknown ID!" );
+ }
+
+ m_pNaviWin->DisableNotify( false );
+ EnableMenuItems( nullptr );
+ if ( bIsDocModified )
+ svxform::DataNavigatorWindow::SetDocModified();
+ return bHandled;
+ }
+
+ void XFormsPage::AddEntry(std::unique_ptr<ItemNode> _pNewNode, bool _bIsElement, weld::TreeIter* pRet)
+ {
+ if (!pRet)
+ pRet = m_xScratchIter.get();
+
+ std::unique_ptr<weld::TreeIter> xParent(m_xItemList->make_iterator());
+ if (!m_xItemList->get_selected(xParent.get()))
+ xParent.reset();
+ OUString aImage(_bIsElement ? OUString(RID_SVXBMP_ELEMENT) : OUString(RID_SVXBMP_ATTRIBUTE));
+ OUString sName;
+ try
+ {
+ sName = m_xUIHelper->getNodeDisplayName(
+ _pNewNode->m_xNode, m_pNaviWin->IsShowDetails() );
+ }
+ catch ( Exception& )
+ {
+ DBG_UNHANDLED_EXCEPTION("svx");
+ }
+ OUString sId(weld::toId(_pNewNode.release()));
+ m_xItemList->insert(xParent.get(), -1, &sName, &sId, nullptr, nullptr, false, pRet);
+ m_xItemList->set_image(*pRet, aImage);
+ if (xParent && !m_xItemList->get_row_expanded(*xParent) && m_xItemList->iter_has_child(*xParent))
+ m_xItemList->expand_row(*xParent);
+ }
+
+ void XFormsPage::AddEntry(const Reference< XPropertySet >& _rEntry, weld::TreeIter* pRet)
+ {
+ if (!pRet)
+ pRet = m_xScratchIter.get();
+
+ OUString aImage(RID_SVXBMP_ELEMENT);
+
+ ItemNode* pNode = new ItemNode( _rEntry );
+ OUString sTemp;
+
+ if ( DGTSubmission == m_eGroup )
+ {
+ try
+ {
+ // ID
+ _rEntry->getPropertyValue( PN_SUBMISSION_ID ) >>= sTemp;
+ OUString sId(weld::toId(pNode));
+ m_xItemList->insert(nullptr, -1, &sTemp, &sId, nullptr, nullptr, false, pRet);
+ m_xItemList->set_image(*pRet, aImage);
+ std::unique_ptr<weld::TreeIter> xRes(m_xItemList->make_iterator());
+ // Action
+ _rEntry->getPropertyValue( PN_SUBMISSION_ACTION ) >>= sTemp;
+ OUString sEntry = SvxResId( RID_STR_DATANAV_SUBM_ACTION ) + sTemp;
+ m_xItemList->insert(pRet, -1, &sEntry, nullptr, nullptr, nullptr, false, xRes.get());
+ m_xItemList->set_image(*xRes, aImage);
+ // Method
+ _rEntry->getPropertyValue( PN_SUBMISSION_METHOD ) >>= sTemp;
+ sEntry = SvxResId( RID_STR_DATANAV_SUBM_METHOD ) +
+ m_aMethodString.toUI( sTemp );
+ m_xItemList->insert(pRet, -1, &sEntry, nullptr, nullptr, nullptr, false, xRes.get());
+ m_xItemList->set_image(*xRes, aImage);
+ // Ref
+ _rEntry->getPropertyValue( PN_SUBMISSION_REF ) >>= sTemp;
+ sEntry = SvxResId( RID_STR_DATANAV_SUBM_REF ) + sTemp;
+ m_xItemList->insert(pRet, -1, &sEntry, nullptr, nullptr, nullptr, false, xRes.get());
+ m_xItemList->set_image(*xRes, aImage);
+ // Bind
+ _rEntry->getPropertyValue( PN_SUBMISSION_BIND ) >>= sTemp;
+ sEntry = SvxResId( RID_STR_DATANAV_SUBM_BIND ) + sTemp;
+ m_xItemList->insert(pRet, -1, &sEntry, nullptr, nullptr, nullptr, false, xRes.get());
+ m_xItemList->set_image(*xRes, aImage);
+ // Replace
+ _rEntry->getPropertyValue( PN_SUBMISSION_REPLACE ) >>= sTemp;
+ sEntry = SvxResId( RID_STR_DATANAV_SUBM_REPLACE ) +
+ m_aReplaceString.toUI( sTemp );
+ m_xItemList->insert(pRet, -1, &sEntry, nullptr, nullptr, nullptr, false, xRes.get());
+ m_xItemList->set_image(*xRes, aImage);
+ }
+ catch ( Exception const & )
+ {
+ TOOLS_WARN_EXCEPTION( "svx.form", "XFormsPage::AddEntry(Ref)" );
+ }
+ }
+ else // then Binding Page
+ {
+ try
+ {
+ OUString sName;
+ _rEntry->getPropertyValue( PN_BINDING_ID ) >>= sTemp;
+ sName += sTemp + ": ";
+ _rEntry->getPropertyValue( PN_BINDING_EXPR ) >>= sTemp;
+ sName += sTemp;
+
+ OUString sId(weld::toId(pNode));
+ m_xItemList->insert(nullptr, -1, &sName, &sId, nullptr, nullptr, false, pRet);
+ m_xItemList->set_image(*pRet, aImage);
+ }
+ catch ( Exception const & )
+ {
+ TOOLS_WARN_EXCEPTION( "svx.form", "XFormsPage::AddEntry(Ref)" );
+ }
+ }
+ }
+
+ void XFormsPage::EditEntry( const Reference< XPropertySet >& _rEntry )
+ {
+ if ( DGTSubmission != m_eGroup )
+ return;
+
+ try
+ {
+ std::unique_ptr<weld::TreeIter> xEntry(m_xItemList->make_iterator());
+ if (!m_xItemList->get_selected(xEntry.get()))
+ {
+ SAL_WARN( "svx.form", "corrupt tree" );
+ return;
+ }
+
+ // #i36262# may be called for submission entry *or* for
+ // submission children. If we don't have any children, we
+ // assume the latter case and use the parent
+ if (!m_xItemList->iter_has_child(*xEntry))
+ m_xItemList->iter_parent(*xEntry);
+
+ OUString sTemp;
+ _rEntry->getPropertyValue( PN_SUBMISSION_ID ) >>= sTemp;
+ m_xItemList->set_text(*xEntry, sTemp);
+
+ _rEntry->getPropertyValue( PN_SUBMISSION_BIND ) >>= sTemp;
+ OUString sEntry = SvxResId( RID_STR_DATANAV_SUBM_BIND ) + sTemp;
+ if (!m_xItemList->iter_children(*xEntry))
+ {
+ SAL_WARN( "svx.form", "corrupt tree" );
+ return;
+ }
+ m_xItemList->set_text(*xEntry, sEntry);
+ _rEntry->getPropertyValue( PN_SUBMISSION_REF ) >>= sTemp;
+ sEntry = SvxResId( RID_STR_DATANAV_SUBM_REF ) + sTemp;
+ if (!m_xItemList->iter_next_sibling(*xEntry))
+ {
+ SAL_WARN( "svx.form", "corrupt tree" );
+ return;
+ }
+ m_xItemList->set_text(*xEntry, sEntry);
+ _rEntry->getPropertyValue( PN_SUBMISSION_ACTION ) >>= sTemp;
+ sEntry = SvxResId( RID_STR_DATANAV_SUBM_ACTION ) + sTemp;
+ if (!m_xItemList->iter_next_sibling(*xEntry))
+ {
+ SAL_WARN( "svx.form", "corrupt tree" );
+ return;
+ }
+ _rEntry->getPropertyValue( PN_SUBMISSION_METHOD ) >>= sTemp;
+ sEntry = SvxResId( RID_STR_DATANAV_SUBM_METHOD ) +
+ m_aMethodString.toUI( sTemp );
+ if (!m_xItemList->iter_next_sibling(*xEntry))
+ {
+ SAL_WARN( "svx.form", "corrupt tree" );
+ return;
+ }
+ m_xItemList->set_text(*xEntry, sEntry);
+ _rEntry->getPropertyValue( PN_SUBMISSION_REPLACE ) >>= sTemp;
+ sEntry = SvxResId( RID_STR_DATANAV_SUBM_REPLACE ) +
+ m_aReplaceString.toUI( sTemp );
+ if (!m_xItemList->iter_next_sibling(*xEntry))
+ {
+ SAL_WARN( "svx.form", "corrupt tree" );
+ return;
+ }
+ m_xItemList->set_text(*xEntry, sEntry);
+ }
+ catch ( Exception const & )
+ {
+ TOOLS_WARN_EXCEPTION( "svx.form", "XFormsPage::EditEntry()" );
+ }
+ }
+
+ bool XFormsPage::RemoveEntry()
+ {
+ bool bRet = false;
+
+ std::unique_ptr<weld::TreeIter> xEntry(m_xItemList->make_iterator());
+ bool bEntry = m_xItemList->get_selected(xEntry.get());
+ if ( bEntry &&
+ ( DGTInstance != m_eGroup || m_xItemList->get_iter_depth(*xEntry) ) )
+ {
+ Reference< css::xforms::XModel > xModel( m_xUIHelper, UNO_QUERY );
+ DBG_ASSERT( xModel.is(), "XFormsPage::RemoveEntry(): no model" );
+ ItemNode* pNode = weld::fromId<ItemNode*>(m_xItemList->get_id(*xEntry));
+ DBG_ASSERT( pNode, "XFormsPage::RemoveEntry(): no node" );
+
+ if ( DGTInstance == m_eGroup )
+ {
+ try
+ {
+ DBG_ASSERT( pNode->m_xNode.is(), "XFormsPage::RemoveEntry(): no XNode" );
+ css::xml::dom::NodeType eChildType = pNode->m_xNode->getNodeType();
+ bool bIsElement = ( eChildType == css::xml::dom::NodeType_ELEMENT_NODE );
+ TranslateId pResId = bIsElement ? RID_STR_QRY_REMOVE_ELEMENT : RID_STR_QRY_REMOVE_ATTRIBUTE;
+ OUString sVar = bIsElement ? OUString(ELEMENTNAME) : OUString(ATTRIBUTENAME);
+ std::unique_ptr<weld::MessageDialog> xQBox(Application::CreateMessageDialog(m_pNaviWin->GetFrameWeld(),
+ VclMessageType::Question, VclButtonsType::YesNo,
+ SvxResId(pResId)));
+ OUString sMessText = xQBox->get_primary_text();
+ sMessText = sMessText.replaceFirst(
+ sVar, m_xUIHelper->getNodeDisplayName( pNode->m_xNode, false ) );
+ xQBox->set_primary_text(sMessText);
+ if (xQBox->run() == RET_YES)
+ {
+ std::unique_ptr<weld::TreeIter> xParent(m_xItemList->make_iterator(xEntry.get()));
+ bool bParent = m_xItemList->iter_parent(*xParent); (void)bParent;
+ assert(bParent && "XFormsPage::RemoveEntry(): no parent entry");
+ ItemNode* pParentNode = weld::fromId<ItemNode*>(m_xItemList->get_id(*xParent));
+ DBG_ASSERT( pParentNode && pParentNode->m_xNode.is(), "XFormsPage::RemoveEntry(): no parent XNode" );
+
+ Reference< css::xml::dom::XNode > xPNode;
+ Reference< css::xml::dom::XNode > xNode =
+ pParentNode->m_xNode->removeChild( pNode->m_xNode );
+ if ( xNode.is() )
+ xPNode = xNode->getParentNode();
+ DBG_ASSERT( !xPNode.is(), "XFormsPage::RemoveEntry(): node not removed" );
+ bRet = true;
+ }
+ }
+ catch ( Exception const & )
+ {
+ TOOLS_WARN_EXCEPTION( "svx.form", "XFormsPage::RemoveEntry()" );
+ }
+ }
+ else
+ {
+ DBG_ASSERT( pNode->m_xPropSet.is(), "XFormsPage::RemoveEntry(): no propset" );
+ bool bSubmission = ( DGTSubmission == m_eGroup );
+ TranslateId pResId = bSubmission ? RID_STR_QRY_REMOVE_SUBMISSION : RID_STR_QRY_REMOVE_BINDING;
+ OUString sProperty = bSubmission ? OUString(PN_SUBMISSION_ID) : OUString(PN_BINDING_ID);
+ OUString sSearch = bSubmission ? OUString(SUBMISSIONNAME) : OUString(BINDINGNAME);
+ OUString sName;
+ try
+ {
+ pNode->m_xPropSet->getPropertyValue( sProperty ) >>= sName;
+ }
+ catch ( Exception const & )
+ {
+ TOOLS_WARN_EXCEPTION( "svx.form", "XFormsPage::RemoveEntry()" );
+ }
+ std::unique_ptr<weld::MessageDialog> xQBox(Application::CreateMessageDialog(m_pNaviWin->GetFrameWeld(),
+ VclMessageType::Question, VclButtonsType::YesNo,
+ SvxResId(pResId)));
+ OUString sMessText = xQBox->get_primary_text();
+ sMessText = sMessText.replaceFirst( sSearch, sName);
+ xQBox->set_primary_text(sMessText);
+ if (xQBox->run() == RET_YES)
+ {
+ try
+ {
+ if ( bSubmission )
+ xModel->getSubmissions()->remove( Any( pNode->m_xPropSet ) );
+ else // then Binding Page
+ xModel->getBindings()->remove( Any( pNode->m_xPropSet ) );
+ bRet = true;
+ }
+ catch ( Exception const & )
+ {
+ TOOLS_WARN_EXCEPTION( "svx.form", "XFormsPage::RemoveEntry()" );
+ }
+ }
+ }
+
+ if (bRet)
+ {
+ m_xItemList->remove(*xEntry);
+ delete pNode;
+ }
+ }
+
+ return bRet;
+ }
+
+ IMPL_LINK(XFormsPage, KeyInputHdl, const KeyEvent&, rKEvt, bool)
+ {
+ bool bHandled = false;
+
+ sal_uInt16 nCode = rKEvt.GetKeyCode().GetCode();
+ if (nCode == KEY_DELETE)
+ bHandled = DoMenuAction("delete");
+
+ return bHandled;
+ }
+
+ OUString XFormsPage::SetModel( const Reference< css::xforms::XModel >& _xModel, int _nPagePos )
+ {
+ DBG_ASSERT( _xModel.is(), "XFormsPage::SetModel(): invalid model" );
+
+ m_xUIHelper.set( _xModel, UNO_QUERY );
+ OUString sRet;
+ m_bHasModel = true;
+
+ switch ( m_eGroup )
+ {
+ case DGTInstance :
+ {
+ DBG_ASSERT( _nPagePos != -1, "XFormsPage::SetModel(): invalid page position" );
+ try
+ {
+ Reference< XContainer > xContainer( _xModel->getInstances(), UNO_QUERY );
+ if ( xContainer.is() )
+ m_pNaviWin->AddContainerBroadcaster( xContainer );
+
+ Reference< XEnumerationAccess > xNumAccess = _xModel->getInstances();
+ if ( xNumAccess.is() )
+ {
+ Reference < XEnumeration > xNum = xNumAccess->createEnumeration();
+ if ( xNum.is() && xNum->hasMoreElements() )
+ {
+ int nIter = 0;
+ while ( xNum->hasMoreElements() )
+ {
+ if ( nIter == _nPagePos )
+ {
+ Sequence< PropertyValue > xPropSeq;
+ Any aAny = xNum->nextElement();
+ if ( aAny >>= xPropSeq )
+ sRet = LoadInstance(xPropSeq);
+ else
+ {
+ SAL_WARN( "svx.form", "XFormsPage::SetModel(): invalid instance" );
+ }
+ break;
+ }
+ else
+ {
+ xNum->nextElement();
+ ++nIter;
+ }
+ }
+ }
+ }
+ }
+ catch( Exception const & )
+ {
+ TOOLS_WARN_EXCEPTION( "svx.form", "XFormsPage::SetModel()" );
+ }
+ break;
+ }
+
+ case DGTSubmission :
+ {
+ DBG_ASSERT( _nPagePos == -1, "XFormsPage::SetModel(): invalid page position" );
+ try
+ {
+ Reference< XContainer > xContainer( _xModel->getSubmissions(), UNO_QUERY );
+ if ( xContainer.is() )
+ m_pNaviWin->AddContainerBroadcaster( xContainer );
+
+ Reference< XEnumerationAccess > xNumAccess = _xModel->getSubmissions();
+ if ( xNumAccess.is() )
+ {
+ Reference < XEnumeration > xNum = xNumAccess->createEnumeration();
+ if ( xNum.is() && xNum->hasMoreElements() )
+ {
+ while ( xNum->hasMoreElements() )
+ {
+ Reference< XPropertySet > xPropSet;
+ Any aAny = xNum->nextElement();
+ if ( aAny >>= xPropSet )
+ AddEntry( xPropSet );
+ }
+ }
+ }
+ }
+ catch( Exception const & )
+ {
+ TOOLS_WARN_EXCEPTION( "svx.form", "XFormsPage::SetModel()" );
+ }
+ break;
+ }
+
+ case DGTBinding :
+ {
+ DBG_ASSERT( _nPagePos == -1, "XFormsPage::SetModel(): invalid page position" );
+ try
+ {
+ Reference< XContainer > xContainer( _xModel->getBindings(), UNO_QUERY );
+ if ( xContainer.is() )
+ m_pNaviWin->AddContainerBroadcaster( xContainer );
+
+ Reference< XEnumerationAccess > xNumAccess = _xModel->getBindings();
+ if ( xNumAccess.is() )
+ {
+ Reference < XEnumeration > xNum = xNumAccess->createEnumeration();
+ if ( xNum.is() && xNum->hasMoreElements() )
+ {
+ OUString aImage(RID_SVXBMP_ELEMENT);
+ std::unique_ptr<weld::TreeIter> xRes(m_xItemList->make_iterator());
+ while ( xNum->hasMoreElements() )
+ {
+ Reference< XPropertySet > xPropSet;
+ Any aAny = xNum->nextElement();
+ if ( aAny >>= xPropSet )
+ {
+ OUString sEntry;
+ OUString sTemp;
+ xPropSet->getPropertyValue( PN_BINDING_ID ) >>= sTemp;
+ sEntry += sTemp + ": ";
+ xPropSet->getPropertyValue( PN_BINDING_EXPR ) >>= sTemp;
+ sEntry += sTemp;
+
+ ItemNode* pNode = new ItemNode( xPropSet );
+
+ OUString sId(weld::toId(pNode));
+ m_xItemList->insert(nullptr, -1, &sEntry, &sId, nullptr, nullptr, false, xRes.get());
+ m_xItemList->set_image(*xRes, aImage);
+ }
+ }
+ }
+ }
+ }
+ catch( Exception const & )
+ {
+ TOOLS_WARN_EXCEPTION( "svx.form", "XFormsPage::SetModel()" );
+ }
+ break;
+ }
+ default:
+ OSL_FAIL( "XFormsPage::SetModel: unknown group!" );
+ break;
+ }
+
+ EnableMenuItems( nullptr );
+
+ return sRet;
+ }
+
+ void XFormsPage::ClearModel()
+ {
+ m_bHasModel = false;
+ DeleteAndClearTree();
+ }
+
+ OUString XFormsPage::LoadInstance(const Sequence< PropertyValue >& _xPropSeq)
+ {
+ OUString sRet;
+ OUString sTemp;
+ OUString sInstModel = PN_INSTANCE_MODEL;
+ OUString sInstName = PN_INSTANCE_ID;
+ OUString sInstURL = PN_INSTANCE_URL;
+ for ( const PropertyValue& rProp : _xPropSeq )
+ {
+ if ( sInstModel == rProp.Name )
+ {
+ Reference< css::xml::dom::XNode > xRoot;
+ if ( rProp.Value >>= xRoot )
+ {
+ try
+ {
+ Reference< XEventTarget > xTarget( xRoot, UNO_QUERY );
+ if ( xTarget.is() )
+ m_pNaviWin->AddEventBroadcaster( xTarget );
+
+ OUString sNodeName =
+ m_xUIHelper->getNodeDisplayName( xRoot, m_pNaviWin->IsShowDetails() );
+ if ( sNodeName.isEmpty() )
+ sNodeName = xRoot->getNodeName();
+ if ( xRoot->hasChildNodes() )
+ AddChildren(nullptr, xRoot);
+ }
+ catch ( Exception const & )
+ {
+ TOOLS_WARN_EXCEPTION( "svx.form", "XFormsPage::LoadInstance()" );
+ }
+ }
+ }
+ else if ( sInstName == rProp.Name && ( rProp.Value >>= sTemp ) )
+ m_sInstanceName = sRet = sTemp;
+ else if ( sInstURL == rProp.Name && ( rProp.Value >>= sTemp ) )
+ m_sInstanceURL = sTemp;
+ }
+
+ return sRet;
+ }
+
+ bool XFormsPage::DoMenuAction(std::string_view rMenuID)
+ {
+ return DoToolBoxAction(rMenuID);
+ }
+
+ void XFormsPage::EnableMenuItems(weld::Menu* pMenu)
+ {
+ bool bEnableAdd = false;
+ bool bEnableEdit = false;
+ bool bEnableRemove = false;
+
+ std::unique_ptr<weld::TreeIter> xEntry(m_xItemList->make_iterator());
+ bool bEntry = m_xItemList->get_selected(xEntry.get());
+ if (bEntry)
+ {
+ bEnableAdd = true;
+ bool bSubmitChild = false;
+ if (DGTSubmission == m_eGroup && m_xItemList->get_iter_depth(*xEntry))
+ {
+ m_xItemList->iter_parent(*xEntry);
+ bSubmitChild = true;
+ }
+ ItemNode* pNode = weld::fromId<ItemNode*>(m_xItemList->get_id(*xEntry));
+ if ( pNode && ( pNode->m_xNode.is() || pNode->m_xPropSet.is() ) )
+ {
+ bEnableEdit = true;
+ bEnableRemove = !bSubmitChild;
+ if ( DGTInstance == m_eGroup && !m_xItemList->get_iter_depth(*xEntry) )
+ bEnableRemove = false;
+ if ( pNode->m_xNode.is() )
+ {
+ try
+ {
+ css::xml::dom::NodeType eChildType = pNode->m_xNode->getNodeType();
+ if ( eChildType != css::xml::dom::NodeType_ELEMENT_NODE
+ && eChildType != css::xml::dom::NodeType_DOCUMENT_NODE )
+ {
+ bEnableAdd = false;
+ }
+ }
+ catch ( Exception const & )
+ {
+ TOOLS_WARN_EXCEPTION( "svx.form", "XFormsPage::EnableMenuItems()" );
+ }
+ }
+ }
+ }
+ else if ( m_eGroup != DGTInstance )
+ bEnableAdd = true;
+
+ m_xToolBox->set_item_sensitive("additem", bEnableAdd);
+ m_xToolBox->set_item_sensitive("addelement", bEnableAdd);
+ m_xToolBox->set_item_sensitive("addattribute", bEnableAdd);
+ m_xToolBox->set_item_sensitive("edit", bEnableEdit);
+ m_xToolBox->set_item_sensitive("delete", bEnableRemove);
+
+ if (pMenu)
+ {
+ pMenu->set_sensitive("additem", bEnableAdd);
+ pMenu->set_sensitive("addelement", bEnableAdd);
+ pMenu->set_sensitive("addattribute", bEnableAdd);
+ pMenu->set_sensitive("edit", bEnableEdit);
+ pMenu->set_sensitive("delete", bEnableRemove);
+ }
+ if ( DGTInstance != m_eGroup )
+ return;
+
+ TranslateId pResId1 = RID_STR_DATANAV_EDIT_ELEMENT;
+ TranslateId pResId2 = RID_STR_DATANAV_REMOVE_ELEMENT;
+ if (bEntry)
+ {
+ ItemNode* pNode = weld::fromId<ItemNode*>(m_xItemList->get_id(*xEntry));
+ if ( pNode && pNode->m_xNode.is() )
+ {
+ try
+ {
+ css::xml::dom::NodeType eChildType = pNode->m_xNode->getNodeType();
+ if ( eChildType == css::xml::dom::NodeType_ATTRIBUTE_NODE )
+ {
+ pResId1 = RID_STR_DATANAV_EDIT_ATTRIBUTE;
+ pResId2 = RID_STR_DATANAV_REMOVE_ATTRIBUTE;
+ }
+ }
+ catch ( Exception const & )
+ {
+ TOOLS_WARN_EXCEPTION( "svx.form", "XFormsPage::EnableMenuItems()" );
+ }
+ }
+ }
+ m_xToolBox->set_item_label("edit", SvxResId(pResId1));
+ m_xToolBox->set_item_label("delete", SvxResId(pResId2));
+ if (pMenu)
+ {
+ pMenu->set_label("edit", SvxResId( pResId1 ) );
+ pMenu->set_label("delete", SvxResId( pResId2 ) );
+ }
+ }
+
+ DataNavigatorWindow::DataNavigatorWindow(vcl::Window* pParent, weld::Builder& rBuilder, SfxBindings const * pBindings)
+ : m_xParent(pParent)
+ , m_xModelsBox(rBuilder.weld_combo_box("modelslist"))
+ , m_xModelBtn(rBuilder.weld_menu_button("modelsbutton"))
+ , m_xTabCtrl(rBuilder.weld_notebook("tabcontrol"))
+ , m_xInstanceBtn(rBuilder.weld_menu_button("instances"))
+ , m_nLastSelectedPos(-1)
+ , m_bShowDetails(false)
+ , m_bIsNotifyDisabled(false)
+ , m_aUpdateTimer("svx DataNavigatorWindow m_aUpdateTimer")
+ , m_xDataListener(new DataListener(this))
+ {
+ // handler
+ m_xModelsBox->connect_changed( LINK( this, DataNavigatorWindow, ModelSelectListBoxHdl ) );
+ Link<const OString&, void> aLink1 = LINK( this, DataNavigatorWindow, MenuSelectHdl );
+ m_xModelBtn->connect_selected(aLink1);
+ m_xInstanceBtn->connect_selected(aLink1);
+ Link<weld::Toggleable&,void> aLink2 = LINK( this, DataNavigatorWindow, MenuActivateHdl );
+ m_xModelBtn->connect_toggled( aLink2 );
+ m_xInstanceBtn->connect_toggled( aLink2 );
+ m_xTabCtrl->connect_enter_page( LINK( this, DataNavigatorWindow, ActivatePageHdl ) );
+ m_aUpdateTimer.SetTimeout( 2000 );
+ m_aUpdateTimer.SetInvokeHandler( LINK( this, DataNavigatorWindow, UpdateHdl ) );
+
+ // init tabcontrol
+ OString sPageId("instance");
+ SvtViewOptions aViewOpt( EViewType::TabDialog, CFGNAME_DATANAVIGATOR );
+ if ( aViewOpt.Exists() )
+ {
+ OString sNewPageId = aViewOpt.GetPageID();
+ if (m_xTabCtrl->get_page_index(sNewPageId) != -1)
+ sPageId = sNewPageId;
+ aViewOpt.GetUserItem(CFGNAME_SHOWDETAILS) >>= m_bShowDetails;
+ }
+
+ m_xInstanceBtn->set_item_active("instancesdetails", m_bShowDetails);
+
+ m_xTabCtrl->set_current_page(sPageId);
+ ActivatePageHdl(sPageId);
+
+ // get our frame
+ DBG_ASSERT( pBindings != nullptr,
+ "DataNavigatorWindow::LoadModels(): no SfxBindings; can't get frame" );
+ m_xFrame = pBindings->GetDispatcher()->GetFrame()->GetFrame().GetFrameInterface();
+ DBG_ASSERT( m_xFrame.is(), "DataNavigatorWindow::LoadModels(): no frame" );
+ // add frameaction listener
+ Reference< XFrameActionListener > xListener = m_xDataListener;
+ m_xFrame->addFrameActionListener( xListener );
+
+ // load xforms models of the current document
+ LoadModels();
+ }
+
+ DataNavigatorWindow::~DataNavigatorWindow()
+ {
+ Reference< XFrameActionListener > xListener = m_xDataListener;
+ m_xFrame->removeFrameActionListener( xListener );
+
+ SvtViewOptions aViewOpt( EViewType::TabDialog, CFGNAME_DATANAVIGATOR );
+ aViewOpt.SetPageID(m_xTabCtrl->get_current_page_ident());
+ aViewOpt.SetUserItem(CFGNAME_SHOWDETAILS, Any(m_bShowDetails));
+
+ m_xInstPage.reset();
+ m_xSubmissionPage.reset();
+ m_xBindingPage.reset();
+
+ sal_Int32 i, nCount = m_aPageList.size();
+ for ( i = 0; i < nCount; ++i )
+ m_aPageList[i].reset();
+ m_aPageList.clear();
+
+ RemoveBroadcaster();
+ m_xDataListener.clear();
+ }
+
+ IMPL_LINK( DataNavigatorWindow, ModelSelectListBoxHdl, weld::ComboBox&, rBox, void )
+ {
+ ModelSelectHdl(&rBox);
+ }
+
+ void DataNavigatorWindow::ModelSelectHdl(const weld::ComboBox* pBox)
+ {
+ sal_Int32 nPos = m_xModelsBox->get_active();
+ // pBox == NULL, if you want to force a new fill.
+ if ( nPos != m_nLastSelectedPos || !pBox )
+ {
+ m_nLastSelectedPos = nPos;
+ ClearAllPageModels( pBox != nullptr );
+ InitPages();
+ SetPageModel(GetCurrentPage());
+ }
+ }
+
+ IMPL_LINK(DataNavigatorWindow, MenuSelectHdl, const OString&, rIdent, void)
+ {
+ bool bIsDocModified = false;
+ Reference< css::xforms::XFormsUIHelper1 > xUIHelper;
+ sal_Int32 nSelectedPos = m_xModelsBox->get_active();
+ OUString sSelectedModel(m_xModelsBox->get_text(nSelectedPos));
+ Reference< css::xforms::XModel > xModel;
+ try
+ {
+ Any aAny = m_xDataContainer->getByName( sSelectedModel );
+ if ( aAny >>= xModel )
+ xUIHelper.set( xModel, UNO_QUERY );
+ }
+ catch ( Exception const & )
+ {
+ TOOLS_WARN_EXCEPTION( "svx.form", "DataNavigatorWindow::MenuSelectHdl()" );
+ }
+ DBG_ASSERT( xUIHelper.is(), "DataNavigatorWindow::MenuSelectHdl(): no UIHelper" );
+
+ m_bIsNotifyDisabled = true;
+
+ if (rIdent == "modelsadd")
+ {
+ AddModelDialog aDlg(GetFrameWeld(), false);
+ bool bShowDialog = true;
+ while ( bShowDialog )
+ {
+ bShowDialog = false;
+ if (aDlg.run() == RET_OK)
+ {
+ OUString sNewName = aDlg.GetName();
+ bool bDocumentData = aDlg.GetModifyDoc();
+
+ if (m_xModelsBox->find_text(sNewName) != -1)
+ {
+ // error: model name already exists
+ std::unique_ptr<weld::MessageDialog> xErrBox(Application::CreateMessageDialog(GetFrameWeld(),
+ VclMessageType::Warning, VclButtonsType::Ok,
+ SvxResId(RID_STR_DOUBLE_MODELNAME)));
+ xErrBox->set_primary_text(xErrBox->get_primary_text().replaceFirst(MSG_VARIABLE, sNewName));
+ xErrBox->run();
+ bShowDialog = true;
+ }
+ else
+ {
+ try
+ {
+ // add new model to frame model
+ Reference< css::xforms::XModel > xNewModel(
+ xUIHelper->newModel( m_xFrameModel, sNewName ), UNO_SET_THROW );
+
+ Reference< XPropertySet > xModelProps( xNewModel, UNO_QUERY_THROW );
+ xModelProps->setPropertyValue("ExternalData", Any( !bDocumentData ) );
+
+ m_xModelsBox->append_text(sNewName);
+ m_xModelsBox->set_active(m_xModelsBox->get_count() - 1);
+ ModelSelectHdl(m_xModelsBox.get());
+ bIsDocModified = true;
+ }
+ catch ( Exception const & )
+ {
+ TOOLS_WARN_EXCEPTION( "svx.form", "DataNavigatorWindow::MenuSelectHdl()" );
+ }
+ }
+ }
+ }
+ }
+ else if (rIdent == "modelsedit")
+ {
+ AddModelDialog aDlg(GetFrameWeld(), true);
+ aDlg.SetName( sSelectedModel );
+
+ bool bDocumentData( false );
+ try
+ {
+ Reference< css::xforms::XFormsSupplier > xFormsSupp( m_xFrameModel, UNO_QUERY_THROW );
+ Reference< XNameContainer > xXForms( xFormsSupp->getXForms(), UNO_SET_THROW );
+ Reference< XPropertySet > xModelProps( xXForms->getByName( sSelectedModel ), UNO_QUERY_THROW );
+ bool bExternalData = false;
+ OSL_VERIFY( xModelProps->getPropertyValue( "ExternalData" ) >>= bExternalData );
+ bDocumentData = !bExternalData;
+ }
+ catch( const Exception& )
+ {
+ DBG_UNHANDLED_EXCEPTION("svx");
+ }
+ aDlg.SetModifyDoc( bDocumentData );
+
+ if (aDlg.run() == RET_OK)
+ {
+ if ( aDlg.GetModifyDoc() != bDocumentData )
+ {
+ bDocumentData = aDlg.GetModifyDoc();
+ try
+ {
+ Reference< css::xforms::XFormsSupplier > xFormsSupp( m_xFrameModel, UNO_QUERY_THROW );
+ Reference< XNameContainer > xXForms( xFormsSupp->getXForms(), UNO_SET_THROW );
+ Reference< XPropertySet > xModelProps( xXForms->getByName( sSelectedModel ), UNO_QUERY_THROW );
+ xModelProps->setPropertyValue( "ExternalData", Any( !bDocumentData ) );
+ bIsDocModified = true;
+ }
+ catch( const Exception& )
+ {
+ DBG_UNHANDLED_EXCEPTION("svx");
+ }
+ }
+
+ OUString sNewName = aDlg.GetName();
+ if ( !sNewName.isEmpty() && ( sNewName != sSelectedModel ) )
+ {
+ try
+ {
+ xUIHelper->renameModel( m_xFrameModel, sSelectedModel, sNewName );
+
+ m_xModelsBox->remove(nSelectedPos);
+ m_xModelsBox->append_text(sNewName);
+ m_xModelsBox->set_active(m_xModelsBox->get_count() - 1);
+ bIsDocModified = true;
+ }
+ catch ( Exception const & )
+ {
+ TOOLS_WARN_EXCEPTION( "svx.form", "DataNavigatorWindow::MenuSelectHdl()" );
+ }
+ }
+ }
+ }
+ else if (rIdent == "modelsremove")
+ {
+ std::unique_ptr<weld::MessageDialog> xQBox(Application::CreateMessageDialog(GetFrameWeld(),
+ VclMessageType::Question, VclButtonsType::YesNo,
+ SvxResId( RID_STR_QRY_REMOVE_MODEL)));
+ OUString sText = xQBox->get_primary_text();
+ sText = sText.replaceFirst( MODELNAME, sSelectedModel );
+ xQBox->set_primary_text(sText);
+ if (xQBox->run() == RET_YES)
+ {
+ try
+ {
+ xUIHelper->removeModel( m_xFrameModel, sSelectedModel );
+ }
+ catch ( Exception const & )
+ {
+ TOOLS_WARN_EXCEPTION( "svx.form", "DataNavigatorWindow::MenuSelectHdl()" );
+ }
+ m_xModelsBox->remove(nSelectedPos);
+ if (m_xModelsBox->get_count() <= nSelectedPos)
+ nSelectedPos = m_xModelsBox->get_count() - 1;
+ m_xModelsBox->set_active(nSelectedPos);
+ ModelSelectHdl(m_xModelsBox.get());
+ bIsDocModified = true;
+ }
+ }
+ else if (rIdent == "instancesadd")
+ {
+ AddInstanceDialog aDlg(GetFrameWeld(), false);
+ if (aDlg.run() == RET_OK)
+ {
+ OString sPageId = GetNewPageId(); // ModelSelectHdl will cause a page of this id to be created
+
+ OUString sName = aDlg.GetName();
+ if (sName.isEmpty())
+ {
+ SAL_WARN( "svx.form", "DataNavigatorWindow::CreateInstancePage(): instance without name" );
+ sName = "untitled";
+ }
+
+ OUString sURL = aDlg.GetURL();
+ bool bLinkOnce = aDlg.IsLinkInstance();
+ try
+ {
+ xUIHelper->newInstance( sName, sURL, !bLinkOnce );
+ }
+ catch ( Exception const & )
+ {
+ TOOLS_WARN_EXCEPTION( "svx.form", "DataNavigatorWindow::MenuSelectHdl()" );
+ }
+ ModelSelectHdl( nullptr );
+
+ XFormsPage* pPage = GetPage(sPageId);
+ pPage->SetInstanceName(sName);
+ pPage->SetInstanceURL(sURL);
+ pPage->SetLinkOnce(bLinkOnce);
+ ActivatePageHdl(sPageId);
+
+ bIsDocModified = true;
+ }
+ }
+ else if (rIdent == "instancesedit")
+ {
+ OString sIdent = GetCurrentPage();
+ XFormsPage* pPage = GetPage(sIdent);
+ if ( pPage )
+ {
+ AddInstanceDialog aDlg(GetFrameWeld(), true);
+ aDlg.SetName( pPage->GetInstanceName() );
+ aDlg.SetURL( pPage->GetInstanceURL() );
+ aDlg.SetLinkInstance( pPage->GetLinkOnce() );
+ OUString sOldName = aDlg.GetName();
+ if (aDlg.run() == RET_OK)
+ {
+ OUString sNewName = aDlg.GetName();
+ OUString sURL = aDlg.GetURL();
+ bool bLinkOnce = aDlg.IsLinkInstance();
+ try
+ {
+ xUIHelper->renameInstance( sOldName,
+ sNewName,
+ sURL,
+ !bLinkOnce );
+ }
+ catch ( Exception const & )
+ {
+ TOOLS_WARN_EXCEPTION( "svx.form", "DataNavigatorWindow::MenuSelectHdl()" );
+ }
+ pPage->SetInstanceName(sNewName);
+ pPage->SetInstanceURL(sURL);
+ pPage->SetLinkOnce(bLinkOnce);
+ m_xTabCtrl->set_tab_label_text(sIdent, sNewName);
+ bIsDocModified = true;
+ }
+ }
+ }
+ else if (rIdent == "instancesremove")
+ {
+ OString sIdent = GetCurrentPage();
+ XFormsPage* pPage = GetPage(sIdent);
+ if (pPage)
+ {
+ OUString sInstName = pPage->GetInstanceName();
+ std::unique_ptr<weld::MessageDialog> xQBox(Application::CreateMessageDialog(GetFrameWeld(),
+ VclMessageType::Question, VclButtonsType::YesNo,
+ SvxResId(RID_STR_QRY_REMOVE_INSTANCE)));
+ OUString sMessText = xQBox->get_primary_text();
+ sMessText = sMessText.replaceFirst( INSTANCENAME, sInstName );
+ xQBox->set_primary_text(sMessText);
+ if (xQBox->run() == RET_YES)
+ {
+ bool bDoRemove = false;
+ if (IsAdditionalPage(sIdent))
+ {
+ auto aPageListEnd = m_aPageList.end();
+ auto aFoundPage = std::find_if(m_aPageList.begin(), aPageListEnd,
+ [pPage](const auto&elem) { return elem.get() == pPage; });
+ if ( aFoundPage != aPageListEnd )
+ {
+ m_aPageList.erase( aFoundPage );
+ bDoRemove = true;
+ }
+ }
+ else
+ {
+ m_xInstPage.reset();
+ bDoRemove = true;
+ }
+
+ if ( bDoRemove )
+ {
+ try
+ {
+ xUIHelper->removeInstance( sInstName );
+ }
+ catch (const Exception&)
+ {
+ TOOLS_WARN_EXCEPTION( "svx.form", "DataNavigatorWindow::MenuSelectHdl()" );
+ }
+ m_xTabCtrl->remove_page(sIdent);
+ m_xTabCtrl->set_current_page("instance");
+ ModelSelectHdl( nullptr );
+ bIsDocModified = true;
+ }
+ }
+ }
+ }
+ else if (rIdent == "instancesdetails")
+ {
+ m_bShowDetails = !m_bShowDetails;
+ m_xInstanceBtn->set_item_active("instancesdetails", m_bShowDetails);
+ ModelSelectHdl(m_xModelsBox.get());
+ }
+ else
+ {
+ SAL_WARN( "svx.form", "DataNavigatorWindow::MenuSelectHdl(): wrong menu item" );
+ }
+
+ m_bIsNotifyDisabled = false;
+
+ if ( bIsDocModified )
+ SetDocModified();
+ }
+
+ bool DataNavigatorWindow::IsAdditionalPage(std::string_view rIdent)
+ {
+ return o3tl::starts_with(rIdent, "additional");
+ }
+
+ IMPL_LINK( DataNavigatorWindow, MenuActivateHdl, weld::Toggleable&, rBtn, void )
+ {
+ if (m_xInstanceBtn.get() == &rBtn)
+ {
+ OString sIdent(m_xTabCtrl->get_current_page_ident());
+ bool bIsInstPage = (IsAdditionalPage(sIdent) || sIdent == "instance");
+ m_xInstanceBtn->set_item_sensitive( "instancesedit", bIsInstPage );
+ m_xInstanceBtn->set_item_sensitive( "instancesremove",
+ bIsInstPage && m_xTabCtrl->get_n_pages() > MIN_PAGE_COUNT );
+ m_xInstanceBtn->set_item_sensitive( "instancesdetails", bIsInstPage );
+ }
+ else if (m_xModelBtn.get() == &rBtn)
+ {
+ // we need at least one model!
+ m_xModelBtn->set_item_sensitive("modelsremove", m_xModelsBox->get_count() > 1 );
+ }
+ else
+ {
+ SAL_WARN( "svx.form", "DataNavigatorWindow::MenuActivateHdl(): wrong button" );
+ }
+ }
+
+ IMPL_LINK(DataNavigatorWindow, ActivatePageHdl, const OString&, rIdent, void)
+ {
+ XFormsPage* pPage = GetPage(rIdent);
+ if (!pPage)
+ return;
+ if (m_xDataContainer.is() && !pPage->HasModel())
+ SetPageModel(rIdent);
+ }
+
+ IMPL_LINK_NOARG(DataNavigatorWindow, UpdateHdl, Timer *, void)
+ {
+ ModelSelectHdl( nullptr );
+ }
+
+ XFormsPage* DataNavigatorWindow::GetPage(const OString& rCurId)
+ {
+ XFormsPage* pPage = nullptr;
+ if (rCurId == "submissions")
+ {
+ if (!m_xSubmissionPage)
+ m_xSubmissionPage.reset(new XFormsPage(m_xTabCtrl->get_page(rCurId), this, DGTSubmission));
+ pPage = m_xSubmissionPage.get();
+ }
+ else if (rCurId == "bindings")
+ {
+ if (!m_xBindingPage)
+ m_xBindingPage.reset(new XFormsPage(m_xTabCtrl->get_page(rCurId), this, DGTBinding));
+ pPage = m_xBindingPage.get();
+ }
+ else if (rCurId == "instance")
+ {
+ if (!m_xInstPage)
+ m_xInstPage.reset(new XFormsPage(m_xTabCtrl->get_page(rCurId), this, DGTInstance));
+ pPage = m_xInstPage.get();
+ }
+ else
+ {
+ sal_uInt16 nPos = m_xTabCtrl->get_page_index(rCurId);
+ if (HasFirstInstancePage() && nPos > 0)
+ nPos--;
+ if (m_aPageList.size() > nPos)
+ pPage = m_aPageList[nPos].get();
+ else
+ {
+ m_aPageList.emplace_back(std::make_unique<XFormsPage>(m_xTabCtrl->get_page(rCurId), this, DGTInstance));
+ pPage = m_aPageList.back().get();
+ }
+ }
+ return pPage;
+ }
+
+ OString DataNavigatorWindow::GetCurrentPage() const
+ {
+ return m_xTabCtrl->get_current_page_ident();
+ }
+
+ void DataNavigatorWindow::LoadModels()
+ {
+ if ( !m_xFrameModel.is() )
+ {
+ // get model of active frame
+ Reference< XController > xCtrl = m_xFrame->getController();
+ if ( xCtrl.is() )
+ {
+ try
+ {
+ m_xFrameModel = xCtrl->getModel();
+ }
+ catch ( Exception const & )
+ {
+ TOOLS_WARN_EXCEPTION( "svx.form", "DataNavigatorWindow::LoadModels()" );
+ }
+ }
+ }
+
+ if ( m_xFrameModel.is() )
+ {
+ try
+ {
+ Reference< css::xforms::XFormsSupplier > xFormsSupp( m_xFrameModel, UNO_QUERY );
+ if ( xFormsSupp.is() )
+ {
+ Reference< XNameContainer > xContainer = xFormsSupp->getXForms();
+ if ( xContainer.is() )
+ {
+ m_xDataContainer = xContainer;
+ const Sequence< OUString > aNameList = m_xDataContainer->getElementNames();
+ for ( const OUString& rName : aNameList )
+ {
+ Any aAny = m_xDataContainer->getByName( rName );
+ Reference< css::xforms::XModel > xFormsModel;
+ if ( aAny >>= xFormsModel )
+ m_xModelsBox->append_text(xFormsModel->getID());
+ }
+ }
+ }
+ }
+ catch( Exception const & )
+ {
+ TOOLS_WARN_EXCEPTION( "svx.form", "DataNavigatorWindow::LoadModels()" );
+ }
+ }
+
+ if (m_xModelsBox->get_count() > 0)
+ {
+ m_xModelsBox->set_active(0);
+ ModelSelectHdl(m_xModelsBox.get());
+ }
+ }
+
+ void DataNavigatorWindow::SetPageModel(const OString& rIdent)
+ {
+ OUString sModel(m_xModelsBox->get_active_text());
+ try
+ {
+ Any aAny = m_xDataContainer->getByName( sModel );
+ Reference< css::xforms::XModel > xFormsModel;
+ if ( aAny >>= xFormsModel )
+ {
+ int nPagePos = -1;
+ XFormsPage* pPage = GetPage(rIdent);
+ DBG_ASSERT( pPage, "DataNavigatorWindow::SetPageModel(): no page" );
+ if (IsAdditionalPage(rIdent) || rIdent == "instance")
+ {
+ // instance page
+ nPagePos = m_xTabCtrl->get_page_index(rIdent);
+ }
+ m_bIsNotifyDisabled = true;
+ OUString sText = pPage->SetModel( xFormsModel, nPagePos );
+ m_bIsNotifyDisabled = false;
+ if (!sText.isEmpty())
+ m_xTabCtrl->set_tab_label_text(rIdent, sText);
+ }
+ }
+ catch (const NoSuchElementException& )
+ {
+ SAL_WARN( "svx.form", "DataNavigatorWindow::SetPageModel(): no such element" );
+ }
+ catch( Exception const & )
+ {
+ TOOLS_WARN_EXCEPTION( "svx.form", "DataNavigatorWindow::SetPageModel()" );
+ }
+ }
+
+ void DataNavigatorWindow::InitPages()
+ {
+ OUString sModel(m_xModelsBox->get_active_text());
+ try
+ {
+ Any aAny = m_xDataContainer->getByName( sModel );
+ Reference< css::xforms::XModel > xModel;
+ if ( aAny >>= xModel )
+ {
+ Reference< XEnumerationAccess > xNumAccess = xModel->getInstances();
+ if ( xNumAccess.is() )
+ {
+ Reference < XEnumeration > xNum = xNumAccess->createEnumeration();
+ if ( xNum.is() && xNum->hasMoreElements() )
+ {
+ sal_Int32 nAlreadyLoadedCount = m_aPageList.size();
+ if ( !HasFirstInstancePage() && nAlreadyLoadedCount > 0 )
+ nAlreadyLoadedCount--;
+ sal_Int32 nIdx = 0;
+ while ( xNum->hasMoreElements() )
+ {
+ if ( nIdx > nAlreadyLoadedCount )
+ {
+ Sequence< PropertyValue > xPropSeq;
+ if ( xNum->nextElement() >>= xPropSeq )
+ CreateInstancePage( xPropSeq );
+ else
+ {
+ SAL_WARN( "svx.form", "DataNavigator::InitPages(): invalid instance" );
+ }
+ }
+ else
+ xNum->nextElement();
+ nIdx++;
+ }
+ }
+ }
+ }
+ }
+ catch ( NoSuchElementException& )
+ {
+ SAL_WARN( "svx.form", "DataNavigatorWindow::SetPageModel(): no such element" );
+ }
+ catch( Exception const & )
+ {
+ TOOLS_WARN_EXCEPTION( "svx.form", "DataNavigatorWindow::SetPageModel()" );
+ }
+ }
+
+ void DataNavigatorWindow::ClearAllPageModels( bool bClearPages )
+ {
+ if ( m_xInstPage )
+ m_xInstPage->ClearModel();
+ if ( m_xSubmissionPage )
+ m_xSubmissionPage->ClearModel();
+ if ( m_xBindingPage )
+ m_xBindingPage->ClearModel();
+
+ sal_Int32 nCount = m_aPageList.size();
+ for (sal_Int32 i = 0; i < nCount; ++i)
+ {
+ XFormsPage* pPage = m_aPageList[i].get();
+ pPage->ClearModel();
+ }
+
+ if ( bClearPages )
+ {
+ m_aPageList.clear();
+ while ( m_xTabCtrl->get_n_pages() > MIN_PAGE_COUNT )
+ m_xTabCtrl->remove_page(m_xTabCtrl->get_page_ident(1));
+ }
+ }
+
+ void DataNavigatorWindow::CreateInstancePage( const Sequence< PropertyValue >& _xPropSeq )
+ {
+ OUString sInstName;
+ auto pProp = std::find_if(_xPropSeq.begin(), _xPropSeq.end(),
+ [](const PropertyValue& rProp) { return PN_INSTANCE_ID == rProp.Name; });
+ if (pProp != _xPropSeq.end())
+ pProp->Value >>= sInstName;
+
+ OString sPageId = GetNewPageId();
+ if ( sInstName.isEmpty() )
+ {
+ SAL_WARN( "svx.form", "DataNavigatorWindow::CreateInstancePage(): instance without name" );
+ sInstName = "untitled";
+ }
+ m_xTabCtrl->insert_page(sPageId, sInstName, m_xTabCtrl->get_n_pages() - 2);
+ }
+
+ bool DataNavigatorWindow::HasFirstInstancePage() const
+ {
+ return m_xTabCtrl->get_page_ident(0) == "instance";
+ }
+
+ OString DataNavigatorWindow::GetNewPageId() const
+ {
+ int nMax = 0;
+
+ int nCount = m_xTabCtrl->get_n_pages();
+ for (int i = 0; i < nCount; ++i)
+ {
+ OString sIdent = m_xTabCtrl->get_page_ident(i);
+ OString sNumber;
+ if (!sIdent.startsWith("additional", &sNumber))
+ continue;
+ int nPageId = sNumber.toInt32();
+ if (nMax < nPageId)
+ nMax = nPageId;
+ }
+
+ return "additional" + OString::number(nMax + 1);
+ }
+
+ void DataNavigatorWindow::SetDocModified()
+ {
+ SfxObjectShell* pCurrentDoc = SfxObjectShell::Current();
+ DBG_ASSERT( pCurrentDoc, "DataNavigatorWindow::SetDocModified(): no objectshell" );
+ if (pCurrentDoc && !pCurrentDoc->IsModified() && pCurrentDoc->IsEnableSetModified())
+ pCurrentDoc->SetModified();
+ }
+
+ void DataNavigatorWindow::NotifyChanges( bool _bLoadAll )
+ {
+ if ( m_bIsNotifyDisabled )
+ return;
+
+ if ( _bLoadAll )
+ {
+ // reset all members
+ RemoveBroadcaster();
+ m_xDataContainer.clear();
+ m_xFrameModel.clear();
+ m_xModelsBox->clear();
+ m_nLastSelectedPos = -1;
+ // for a reload
+ LoadModels();
+ }
+ else
+ m_aUpdateTimer.Start();
+ }
+
+ void DataNavigatorWindow::AddContainerBroadcaster( const css::uno::Reference< css::container::XContainer >& xContainer )
+ {
+ Reference< XContainerListener > xListener = m_xDataListener;
+ xContainer->addContainerListener( xListener );
+ m_aContainerList.push_back( xContainer );
+ }
+
+
+ void DataNavigatorWindow::AddEventBroadcaster( const css::uno::Reference< css::xml::dom::events::XEventTarget >& xTarget )
+ {
+ Reference< XEventListener > xListener = m_xDataListener;
+ xTarget->addEventListener( EVENTTYPE_CHARDATA, xListener, true );
+ xTarget->addEventListener( EVENTTYPE_CHARDATA, xListener, false );
+ xTarget->addEventListener( EVENTTYPE_ATTR, xListener, true );
+ xTarget->addEventListener( EVENTTYPE_ATTR, xListener, false );
+ m_aEventTargetList.push_back( xTarget );
+ }
+
+ void DataNavigatorWindow::RemoveBroadcaster()
+ {
+ Reference< XContainerListener > xContainerListener = m_xDataListener;
+ sal_Int32 i, nCount = m_aContainerList.size();
+ for ( i = 0; i < nCount; ++i )
+ m_aContainerList[i]->removeContainerListener( xContainerListener );
+ Reference< XEventListener > xEventListener = m_xDataListener;
+ nCount = m_aEventTargetList.size();
+ for ( i = 0; i < nCount; ++i )
+ {
+ m_aEventTargetList[i]->removeEventListener( EVENTTYPE_CHARDATA, xEventListener, true );
+ m_aEventTargetList[i]->removeEventListener( EVENTTYPE_CHARDATA, xEventListener, false );
+ m_aEventTargetList[i]->removeEventListener( EVENTTYPE_ATTR, xEventListener, true );
+ m_aEventTargetList[i]->removeEventListener( EVENTTYPE_ATTR, xEventListener, false );
+ }
+ }
+
+ DataNavigator::DataNavigator(SfxBindings* _pBindings, SfxChildWindow* _pMgr, vcl::Window* _pParent)
+ : SfxDockingWindow(_pBindings, _pMgr, _pParent, "DataNavigator", "svx/ui/datanavigator.ui")
+ , SfxControllerItem(SID_FM_DATANAVIGATOR_CONTROL, *_pBindings)
+ , m_xDataWin(new DataNavigatorWindow(this, *m_xBuilder, _pBindings))
+ {
+ SetText( SvxResId( RID_STR_DATANAVIGATOR ) );
+
+ Size aSize = GetOptimalSize();
+ Size aLogSize = PixelToLogic(aSize, MapMode(MapUnit::MapAppFont));
+ SfxDockingWindow::SetFloatingSize( aLogSize );
+ }
+
+ DataNavigator::~DataNavigator()
+ {
+ disposeOnce();
+ }
+
+ void DataNavigator::dispose()
+ {
+ m_xDataWin.reset();
+ ::SfxControllerItem::dispose();
+ SfxDockingWindow::dispose();
+ }
+
+ void DataNavigator::StateChangedAtToolBoxControl( sal_uInt16 , SfxItemState , const SfxPoolItem* )
+ {
+ }
+
+ Size DataNavigator::CalcDockingSize( SfxChildAlignment eAlign )
+ {
+ if ( ( eAlign == SfxChildAlignment::TOP ) || ( eAlign == SfxChildAlignment::BOTTOM ) )
+ return Size();
+
+ return SfxDockingWindow::CalcDockingSize( eAlign );
+ }
+
+ SfxChildAlignment DataNavigator::CheckAlignment( SfxChildAlignment eActAlign, SfxChildAlignment eAlign )
+ {
+ switch ( eAlign )
+ {
+ case SfxChildAlignment::LEFT:
+ case SfxChildAlignment::RIGHT:
+ case SfxChildAlignment::NOALIGNMENT:
+ return eAlign;
+ default:
+ break;
+ }
+ return eActAlign;
+ }
+
+ SFX_IMPL_DOCKINGWINDOW( DataNavigatorManager, SID_FM_SHOW_DATANAVIGATOR )
+
+ DataNavigatorManager::DataNavigatorManager(
+ vcl::Window* _pParent, sal_uInt16 _nId, SfxBindings* _pBindings, SfxChildWinInfo* _pInfo ) :
+
+ SfxChildWindow( _pParent, _nId )
+
+ {
+ SetWindow( VclPtr<DataNavigator>::Create( _pBindings, this, _pParent ) );
+ SetAlignment(SfxChildAlignment::RIGHT);
+ GetWindow()->SetSizePixel( Size( 250, 400 ) );
+ static_cast<SfxDockingWindow*>(GetWindow())->Initialize( _pInfo );
+ }
+
+ AddDataItemDialog::AddDataItemDialog(weld::Window* pParent, ItemNode* _pNode,
+ const Reference< css::xforms::XFormsUIHelper1 >& _rUIHelper)
+ : GenericDialogController(pParent, "svx/ui/adddataitemdialog.ui", "AddDataItemDialog")
+ , m_xUIHelper(_rUIHelper)
+ , m_pItemNode(_pNode)
+ , m_eItemType(DITNone)
+ , m_sFL_Element(SvxResId(RID_STR_ELEMENT))
+ , m_sFL_Attribute(SvxResId(RID_STR_ATTRIBUTE))
+ , m_sFL_Binding(SvxResId(RID_STR_BINDING))
+ , m_sFT_BindingExp(SvxResId(RID_STR_BINDING_EXPR))
+ , m_xItemFrame(m_xBuilder->weld_frame("itemframe"))
+ , m_xNameFT(m_xBuilder->weld_label("nameft"))
+ , m_xNameED(m_xBuilder->weld_entry("name"))
+ , m_xDefaultFT(m_xBuilder->weld_label("valueft"))
+ , m_xDefaultED(m_xBuilder->weld_entry("value"))
+ , m_xDefaultBtn(m_xBuilder->weld_button("browse"))
+ , m_xSettingsFrame(m_xBuilder->weld_widget("settingsframe"))
+ , m_xDataTypeFT(m_xBuilder->weld_label("datatypeft"))
+ , m_xDataTypeLB(m_xBuilder->weld_combo_box("datatype"))
+ , m_xRequiredCB(m_xBuilder->weld_check_button("required"))
+ , m_xRequiredBtn(m_xBuilder->weld_button("requiredcond"))
+ , m_xRelevantCB(m_xBuilder->weld_check_button("relevant"))
+ , m_xRelevantBtn(m_xBuilder->weld_button("relevantcond"))
+ , m_xConstraintCB(m_xBuilder->weld_check_button("constraint"))
+ , m_xConstraintBtn(m_xBuilder->weld_button("constraintcond"))
+ , m_xReadonlyCB(m_xBuilder->weld_check_button("readonly"))
+ , m_xReadonlyBtn(m_xBuilder->weld_button("readonlycond"))
+ , m_xCalculateCB(m_xBuilder->weld_check_button("calculate"))
+ , m_xCalculateBtn(m_xBuilder->weld_button("calculatecond"))
+ , m_xOKBtn(m_xBuilder->weld_button("ok"))
+ {
+ InitDialog();
+ InitFromNode();
+ InitDataTypeBox();
+ Check(nullptr);
+ }
+
+ AddDataItemDialog::~AddDataItemDialog()
+ {
+ if ( m_xTempBinding.is() )
+ {
+ Reference< css::xforms::XModel > xModel( m_xUIHelper, UNO_QUERY );
+ if ( xModel.is() )
+ {
+ try
+ {
+ Reference < XSet > xBindings = xModel->getBindings();
+ if ( xBindings.is() )
+ xBindings->remove( Any( m_xTempBinding ) );
+ }
+ catch (const Exception&)
+ {
+ TOOLS_WARN_EXCEPTION( "svx.form", "AddDataItemDialog::Dtor()" );
+ }
+ }
+ }
+ if( m_xUIHelper.is() && m_xBinding.is() )
+ {
+ // remove binding, if it does not convey 'useful' information
+ m_xUIHelper->removeBindingIfUseless( m_xBinding );
+ }
+ }
+
+ IMPL_LINK(AddDataItemDialog, CheckHdl, weld::Toggleable&, rBox, void)
+ {
+ Check(&rBox);
+ }
+
+ void AddDataItemDialog::Check(const weld::Toggleable* pBox)
+ {
+ // Condition buttons are only enable if their check box is checked
+ m_xReadonlyBtn->set_sensitive( m_xReadonlyCB->get_active() );
+ m_xRequiredBtn->set_sensitive( m_xRequiredCB->get_active() );
+ m_xRelevantBtn->set_sensitive( m_xRelevantCB->get_active() );
+ m_xConstraintBtn->set_sensitive( m_xConstraintCB->get_active() );
+ m_xCalculateBtn->set_sensitive( m_xCalculateCB->get_active() );
+
+ if ( !(pBox && m_xTempBinding.is()) )
+ return;
+
+ OUString sTemp, sPropName;
+ if ( m_xRequiredCB.get() == pBox )
+ sPropName = PN_REQUIRED_EXPR;
+ else if ( m_xRelevantCB.get() == pBox )
+ sPropName = PN_RELEVANT_EXPR;
+ else if ( m_xConstraintCB.get() == pBox )
+ sPropName = PN_CONSTRAINT_EXPR;
+ else if ( m_xReadonlyCB.get() == pBox )
+ sPropName = PN_READONLY_EXPR;
+ else if ( m_xCalculateCB.get() == pBox )
+ sPropName = PN_CALCULATE_EXPR;
+ bool bIsChecked = pBox->get_active();
+ m_xTempBinding->getPropertyValue( sPropName ) >>= sTemp;
+ if ( bIsChecked && sTemp.isEmpty() )
+ sTemp = TRUE_VALUE;
+ else if ( !bIsChecked && !sTemp.isEmpty() )
+ sTemp.clear();
+ m_xTempBinding->setPropertyValue( sPropName, Any( sTemp ) );
+ }
+
+ IMPL_LINK(AddDataItemDialog, ConditionHdl, weld::Button&, rBtn, void)
+ {
+ OUString sPropName;
+ if ( m_xDefaultBtn.get() == &rBtn )
+ sPropName = PN_BINDING_EXPR;
+ else if ( m_xRequiredBtn.get() == &rBtn )
+ sPropName = PN_REQUIRED_EXPR;
+ else if ( m_xRelevantBtn.get() == &rBtn )
+ sPropName = PN_RELEVANT_EXPR;
+ else if ( m_xConstraintBtn.get() == &rBtn )
+ sPropName = PN_CONSTRAINT_EXPR;
+ else if (m_xReadonlyBtn.get() == &rBtn)
+ sPropName = PN_READONLY_EXPR;
+ else if (m_xCalculateBtn.get() == &rBtn)
+ sPropName = PN_CALCULATE_EXPR;
+ AddConditionDialog aDlg(m_xDialog.get(), sPropName, m_xTempBinding);
+ bool bIsDefBtn = ( m_xDefaultBtn.get() == &rBtn );
+ OUString sCondition;
+ if ( bIsDefBtn )
+ sCondition = m_xDefaultED->get_text();
+ else
+ {
+ OUString sTemp;
+ m_xTempBinding->getPropertyValue( sPropName ) >>= sTemp;
+ if ( sTemp.isEmpty() )
+ sTemp = TRUE_VALUE;
+ sCondition = sTemp;
+ }
+ aDlg.SetCondition( sCondition );
+
+ if (aDlg.run() == RET_OK)
+ {
+ OUString sNewCondition = aDlg.GetCondition();
+ if ( bIsDefBtn )
+ m_xDefaultED->set_text(sNewCondition);
+ else
+ {
+
+ m_xTempBinding->setPropertyValue(
+ sPropName, Any( sNewCondition ) );
+ }
+ }
+ }
+
+ static void copyPropSet( const Reference< XPropertySet >& xFrom, Reference< XPropertySet > const & xTo )
+ {
+ DBG_ASSERT( xFrom.is(), "copyPropSet(): no source" );
+ DBG_ASSERT( xTo.is(), "copyPropSet(): no target" );
+
+ try
+ {
+ // get property names & infos, and iterate over target properties
+ const Sequence< Property > aProperties = xTo->getPropertySetInfo()->getProperties();
+ Reference< XPropertySetInfo > xFromInfo = xFrom->getPropertySetInfo();
+ for ( const Property& rProperty : aProperties )
+ {
+ const OUString& rName = rProperty.Name;
+
+ // if both set have the property, copy the value
+ // (catch and ignore exceptions, if any)
+ if ( xFromInfo->hasPropertyByName( rName ) )
+ {
+ // don't set readonly properties
+ Property aProperty = xFromInfo->getPropertyByName( rName );
+ if ( ( aProperty.Attributes & PropertyAttribute::READONLY ) == 0 )
+ xTo->setPropertyValue(rName, xFrom->getPropertyValue( rName ));
+ }
+ // else: no property? then ignore.
+ }
+ }
+ catch ( Exception const & )
+ {
+ TOOLS_WARN_EXCEPTION( "svx.form", "copyPropSet()" );
+ }
+ }
+
+ IMPL_LINK_NOARG(AddDataItemDialog, OKHdl, weld::Button&, void)
+ {
+ bool bIsHandleBinding = ( DITBinding == m_eItemType );
+ bool bIsHandleText = ( DITText == m_eItemType );
+ OUString sNewName( m_xNameED->get_text() );
+
+ if ( ( !bIsHandleBinding && !bIsHandleText && !m_xUIHelper->isValidXMLName( sNewName ) ) ||
+ ( bIsHandleBinding && sNewName.isEmpty() ) )
+ {
+ // Error and don't close the dialog
+ std::unique_ptr<weld::MessageDialog> xErrBox(Application::CreateMessageDialog(m_xDialog.get(),
+ VclMessageType::Warning, VclButtonsType::Ok,
+ SvxResId(RID_STR_INVALID_XMLNAME)));
+ xErrBox->set_primary_text(xErrBox->get_primary_text().replaceFirst(MSG_VARIABLE, sNewName));
+ xErrBox->run();
+ return;
+ }
+
+ OUString sDataType( m_xDataTypeLB->get_active_text() );
+ m_xTempBinding->setPropertyValue( PN_BINDING_TYPE, Any( sDataType ) );
+
+ if ( bIsHandleBinding )
+ {
+ // copy properties from temp binding to original binding
+ copyPropSet( m_xTempBinding, m_pItemNode->m_xPropSet );
+ try
+ {
+ OUString sValue = m_xNameED->get_text();
+ m_pItemNode->m_xPropSet->setPropertyValue( PN_BINDING_ID, Any( sValue ) );
+ sValue = m_xDefaultED->get_text();
+ m_pItemNode->m_xPropSet->setPropertyValue( PN_BINDING_EXPR, Any( sValue ) );
+ }
+ catch ( Exception const & )
+ {
+ TOOLS_WARN_EXCEPTION( "svx.form", "AddDataDialog::OKHdl()" );
+ }
+ }
+ else
+ {
+ // copy properties from temp binding to original binding
+ copyPropSet( m_xTempBinding, m_xBinding );
+ try
+ {
+ if ( bIsHandleText )
+ m_xUIHelper->setNodeValue( m_pItemNode->m_xNode, m_xDefaultED->get_text() );
+ else
+ {
+ Reference< css::xml::dom::XNode > xNewNode =
+ m_xUIHelper->renameNode( m_pItemNode->m_xNode, m_xNameED->get_text() );
+ m_xUIHelper->setNodeValue( xNewNode, m_xDefaultED->get_text() );
+ m_pItemNode->m_xNode = xNewNode;
+ }
+ }
+ catch ( Exception const & )
+ {
+ TOOLS_WARN_EXCEPTION( "svx.form", "AddDataDialog::OKHdl()" );
+ }
+ }
+ // then close the dialog
+ m_xDialog->response(RET_OK);
+ }
+
+ void AddDataItemDialog::InitDialog()
+ {
+ // set handler
+ Link<weld::Toggleable&,void> aLink = LINK( this, AddDataItemDialog, CheckHdl );
+ m_xRequiredCB->connect_toggled( aLink );
+ m_xRelevantCB->connect_toggled( aLink );
+ m_xConstraintCB->connect_toggled( aLink );
+ m_xReadonlyCB->connect_toggled( aLink );
+ m_xCalculateCB->connect_toggled( aLink );
+
+ Link<weld::Button&,void> aLink2 = LINK( this, AddDataItemDialog, ConditionHdl );
+ m_xDefaultBtn->connect_clicked( aLink2 );
+ m_xRequiredBtn->connect_clicked( aLink2 );
+ m_xRelevantBtn->connect_clicked( aLink2 );
+ m_xConstraintBtn->connect_clicked( aLink2 );
+ m_xReadonlyBtn->connect_clicked( aLink2 );
+ m_xCalculateBtn->connect_clicked( aLink2 );
+
+ m_xOKBtn->connect_clicked( LINK( this, AddDataItemDialog, OKHdl ) );
+ }
+
+ void AddDataItemDialog::InitFromNode()
+ {
+ if ( m_pItemNode )
+ {
+ if ( m_pItemNode->m_xNode.is() )
+ {
+ try
+ {
+ // detect type of the node
+ css::xml::dom::NodeType eChildType = m_pItemNode->m_xNode->getNodeType();
+ switch ( eChildType )
+ {
+ case css::xml::dom::NodeType_ATTRIBUTE_NODE:
+ m_eItemType = DITAttribute;
+ break;
+ case css::xml::dom::NodeType_ELEMENT_NODE:
+ m_eItemType = DITElement;
+ break;
+ case css::xml::dom::NodeType_TEXT_NODE:
+ m_eItemType = DITText;
+ break;
+ default:
+ OSL_FAIL( "AddDataItemDialog::InitFronNode: cannot handle this node type!" );
+ break;
+ }
+
+ /** Get binding of the node and clone it
+ Then use this temporary binding in the dialog.
+ When the user click OK the temporary binding will be copied
+ into the original binding.
+ */
+
+ Reference< css::xml::dom::XNode > xNode = m_pItemNode->m_xNode;
+ m_xBinding = m_xUIHelper->getBindingForNode( xNode, true );
+ if ( m_xBinding.is() )
+ {
+ Reference< css::xforms::XModel > xModel( m_xUIHelper, UNO_QUERY );
+ if ( xModel.is() )
+ {
+ m_xTempBinding = m_xUIHelper->cloneBindingAsGhost( m_xBinding );
+ Reference < XSet > xBindings = xModel->getBindings();
+ if ( xBindings.is() )
+ xBindings->insert( Any( m_xTempBinding ) );
+ }
+ }
+
+ if ( m_eItemType != DITText )
+ {
+ OUString sName( m_xUIHelper->getNodeName( m_pItemNode->m_xNode ) );
+ m_xNameED->set_text( sName );
+ }
+ m_xDefaultED->set_text( m_pItemNode->m_xNode->getNodeValue() );
+ }
+ catch( Exception const & )
+ {
+ TOOLS_WARN_EXCEPTION( "svx.form", "AddDataItemDialog::InitFromNode()" );
+ }
+ }
+ else if ( m_pItemNode->m_xPropSet.is() )
+ {
+ m_eItemType = DITBinding;
+ Reference< css::xforms::XModel > xModel( m_xUIHelper, UNO_QUERY );
+ if ( xModel.is() )
+ {
+ try
+ {
+ m_xTempBinding = m_xUIHelper->cloneBindingAsGhost( m_pItemNode->m_xPropSet );
+ Reference < XSet > xBindings = xModel->getBindings();
+ if ( xBindings.is() )
+ xBindings->insert( Any( m_xTempBinding ) );
+ }
+ catch ( Exception const & )
+ {
+ TOOLS_WARN_EXCEPTION( "svx.form", "AddDataItemDialog::InitFromNode()" );
+ }
+ }
+ try
+ {
+ Reference< XPropertySetInfo > xInfo = m_pItemNode->m_xPropSet->getPropertySetInfo();
+ OUString sTemp;
+ if ( xInfo->hasPropertyByName( PN_BINDING_ID ) )
+ {
+ m_pItemNode->m_xPropSet->getPropertyValue( PN_BINDING_ID ) >>= sTemp;
+ m_xNameED->set_text( sTemp );
+ m_pItemNode->m_xPropSet->getPropertyValue( PN_BINDING_EXPR ) >>= sTemp;
+ m_xDefaultED->set_text( sTemp );
+ }
+ else if ( xInfo->hasPropertyByName( PN_SUBMISSION_BIND ) )
+ {
+ m_pItemNode->m_xPropSet->getPropertyValue( PN_SUBMISSION_ID ) >>= sTemp;
+ m_xNameED->set_text( sTemp );
+ }
+ }
+ catch( Exception const & )
+ {
+ TOOLS_WARN_EXCEPTION( "svx.form", "AddDataItemDialog::InitFromNode()" );
+ }
+
+ m_xDefaultBtn->show();
+ }
+
+ if ( m_xTempBinding.is() )
+ {
+ try
+ {
+ OUString sTemp;
+ if ( ( m_xTempBinding->getPropertyValue( PN_REQUIRED_EXPR ) >>= sTemp )
+ && !sTemp.isEmpty() )
+ m_xRequiredCB->set_active(true);
+ if ( ( m_xTempBinding->getPropertyValue( PN_RELEVANT_EXPR ) >>= sTemp )
+ && !sTemp.isEmpty() )
+ m_xRelevantCB->set_active(true);
+ if ( ( m_xTempBinding->getPropertyValue( PN_CONSTRAINT_EXPR ) >>= sTemp )
+ && !sTemp.isEmpty() )
+ m_xConstraintCB->set_active(true);
+ if ( ( m_xTempBinding->getPropertyValue( PN_READONLY_EXPR ) >>= sTemp )
+ && !sTemp.isEmpty() )
+ m_xReadonlyCB->set_active(true);
+ if ( ( m_xTempBinding->getPropertyValue( PN_CALCULATE_EXPR ) >>= sTemp )
+ && !sTemp.isEmpty() )
+ m_xCalculateCB->set_active(true);
+ }
+ catch (const Exception&)
+ {
+ TOOLS_WARN_EXCEPTION( "svx.form", "AddDataItemDialog::InitFromNode()" );
+ }
+ }
+ }
+
+ if ( DITText == m_eItemType )
+ {
+ m_xSettingsFrame->hide();
+ m_xNameFT->set_sensitive(false);
+ m_xNameED->set_sensitive(false);
+ }
+ }
+
+ void AddDataItemDialog::InitDataTypeBox()
+ {
+ if ( m_eItemType == DITText )
+ return;
+
+ Reference< css::xforms::XModel > xModel( m_xUIHelper, UNO_QUERY );
+ if ( !xModel.is() )
+ return;
+
+ try
+ {
+ Reference< css::xforms::XDataTypeRepository > xDataTypes =
+ xModel->getDataTypeRepository();
+ if ( xDataTypes.is() )
+ {
+ const Sequence< OUString > aNameList = xDataTypes->getElementNames();
+ for ( const OUString& rName : aNameList )
+ m_xDataTypeLB->append_text(rName);
+ }
+
+ if ( m_xTempBinding.is() )
+ {
+ OUString sTemp;
+ if ( m_xTempBinding->getPropertyValue( PN_BINDING_TYPE ) >>= sTemp )
+ {
+ int nPos = m_xDataTypeLB->find_text(sTemp);
+ if (nPos == -1)
+ {
+ m_xDataTypeLB->append_text(sTemp);
+ nPos = m_xDataTypeLB->get_count() - 1;
+ }
+ m_xDataTypeLB->set_active(nPos);
+ }
+ }
+ }
+ catch ( Exception const & )
+ {
+ TOOLS_WARN_EXCEPTION( "svx.form", "AddDataItemDialog::InitDataTypeBox()" );
+ }
+ }
+
+ void AddDataItemDialog::InitText( DataItemType _eType )
+ {
+ OUString sText;
+
+ switch ( _eType )
+ {
+ case DITAttribute :
+ {
+ sText = m_sFL_Attribute;
+ break;
+ }
+
+ case DITBinding :
+ {
+ sText = m_sFL_Binding;
+ m_xDefaultFT->set_label(m_sFT_BindingExp);
+ break;
+ }
+
+ default:
+ {
+ sText = m_sFL_Element;
+ }
+ }
+
+ m_xItemFrame->set_label(sText);
+ }
+
+ AddConditionDialog::AddConditionDialog(weld::Window* pParent,
+ const OUString& _rPropertyName,
+ const Reference< XPropertySet >& _rPropSet)
+ : GenericDialogController(pParent, "svx/ui/addconditiondialog.ui", "AddConditionDialog")
+ , m_aResultIdle("svx AddConditionDialog m_aResultIdle")
+ , m_sPropertyName(_rPropertyName)
+ , m_xBinding(_rPropSet)
+ , m_xConditionED(m_xBuilder->weld_text_view("condition"))
+ , m_xResultWin(m_xBuilder->weld_text_view("result"))
+ , m_xEditNamespacesBtn(m_xBuilder->weld_button("edit"))
+ , m_xOKBtn(m_xBuilder->weld_button("ok"))
+ {
+ DBG_ASSERT( m_xBinding.is(), "AddConditionDialog::Ctor(): no Binding" );
+
+ m_xConditionED->set_size_request(m_xConditionED->get_approximate_digit_width() * 52,
+ m_xConditionED->get_height_rows(4));
+ m_xResultWin->set_size_request(m_xResultWin->get_approximate_digit_width() * 52,
+ m_xResultWin->get_height_rows(4));
+
+ m_xConditionED->connect_changed( LINK( this, AddConditionDialog, ModifyHdl ) );
+ m_xEditNamespacesBtn->connect_clicked( LINK( this, AddConditionDialog, EditHdl ) );
+ m_xOKBtn->connect_clicked( LINK( this, AddConditionDialog, OKHdl ) );
+ m_aResultIdle.SetPriority( TaskPriority::LOWEST );
+ m_aResultIdle.SetInvokeHandler( LINK( this, AddConditionDialog, ResultHdl ) );
+
+ if ( !m_sPropertyName.isEmpty() )
+ {
+ try
+ {
+ OUString sTemp;
+ if ( ( m_xBinding->getPropertyValue( m_sPropertyName ) >>= sTemp )
+ && !sTemp.isEmpty() )
+ {
+ m_xConditionED->set_text( sTemp );
+ }
+ else
+ {
+//! m_xBinding->setPropertyValue( m_sPropertyName, makeAny( TRUE_VALUE ) );
+ m_xConditionED->set_text( TRUE_VALUE );
+ }
+
+ Reference< css::xforms::XModel > xModel;
+ if ( ( m_xBinding->getPropertyValue( PN_BINDING_MODEL ) >>= xModel ) && xModel.is() )
+ m_xUIHelper.set( xModel, UNO_QUERY );
+ }
+ catch (const Exception&)
+ {
+ TOOLS_WARN_EXCEPTION( "svx.form", "AddConditionDialog::Ctor()" );
+ }
+ }
+
+ DBG_ASSERT( m_xUIHelper.is(), "AddConditionDialog::Ctor(): no UIHelper" );
+ ResultHdl( &m_aResultIdle );
+ }
+
+ AddConditionDialog::~AddConditionDialog()
+ {
+ }
+
+ IMPL_LINK_NOARG(AddConditionDialog, EditHdl, weld::Button&, void)
+ {
+ Reference< XNameContainer > xNameContnr;
+ try
+ {
+ m_xBinding->getPropertyValue( PN_BINDING_NAMESPACES ) >>= xNameContnr;
+ }
+ catch ( Exception const & )
+ {
+ TOOLS_WARN_EXCEPTION( "svx.form", "AddDataItemDialog::EditHdl()" );
+ }
+ NamespaceItemDialog aDlg(this, xNameContnr);
+ aDlg.run();
+ try
+ {
+ m_xBinding->setPropertyValue( PN_BINDING_NAMESPACES, Any( xNameContnr ) );
+ }
+ catch ( Exception const & )
+ {
+ TOOLS_WARN_EXCEPTION( "svx.form", "AddDataItemDialog::EditHdl()" );
+ }
+ }
+
+ IMPL_LINK_NOARG(AddConditionDialog, OKHdl, weld::Button&, void)
+ {
+ m_xDialog->response(RET_OK);
+ }
+
+ IMPL_LINK_NOARG(AddConditionDialog, ModifyHdl, weld::TextView&, void)
+ {
+ m_aResultIdle.Start();
+ }
+
+ IMPL_LINK_NOARG(AddConditionDialog, ResultHdl, Timer *, void)
+ {
+ OUString sCondition = comphelper::string::strip(m_xConditionED->get_text(), ' ');
+ OUString sResult;
+ if ( !sCondition.isEmpty() )
+ {
+ try
+ {
+ sResult = m_xUIHelper->getResultForExpression( m_xBinding, ( m_sPropertyName == PN_BINDING_EXPR ), sCondition );
+ }
+ catch ( Exception const & )
+ {
+ TOOLS_WARN_EXCEPTION( "svx.form", "AddConditionDialog::ResultHdl()" );
+ }
+ }
+ m_xResultWin->set_text(sResult);
+ }
+
+ NamespaceItemDialog::NamespaceItemDialog(AddConditionDialog* pCondDlg, Reference<XNameContainer>& rContainer)
+ : GenericDialogController(pCondDlg->getDialog(), "svx/ui/namespacedialog.ui", "NamespaceDialog")
+ , m_pConditionDlg(pCondDlg)
+ , m_rNamespaces(rContainer)
+ , m_xNamespacesList(m_xBuilder->weld_tree_view("namespaces"))
+ , m_xAddNamespaceBtn(m_xBuilder->weld_button("add"))
+ , m_xEditNamespaceBtn(m_xBuilder->weld_button("edit"))
+ , m_xDeleteNamespaceBtn(m_xBuilder->weld_button("delete"))
+ , m_xOKBtn(m_xBuilder->weld_button("ok"))
+ {
+ m_xNamespacesList->set_size_request(m_xNamespacesList->get_approximate_digit_width() * 80,
+ m_xNamespacesList->get_height_rows(8));
+
+ std::vector<int> aWidths
+ {
+ o3tl::narrowing<int>(m_xNamespacesList->get_approximate_digit_width() * 20)
+ };
+ m_xNamespacesList->set_column_fixed_widths(aWidths);
+
+ m_xNamespacesList->connect_changed( LINK( this, NamespaceItemDialog, SelectHdl ) );
+ Link<weld::Button&,void> aLink = LINK( this, NamespaceItemDialog, ClickHdl );
+ m_xAddNamespaceBtn->connect_clicked( aLink );
+ m_xEditNamespaceBtn->connect_clicked( aLink );
+ m_xDeleteNamespaceBtn->connect_clicked( aLink );
+ m_xOKBtn->connect_clicked( LINK( this, NamespaceItemDialog, OKHdl ) );
+
+ LoadNamespaces();
+ SelectHdl(*m_xNamespacesList);
+ }
+
+ NamespaceItemDialog::~NamespaceItemDialog()
+ {
+ }
+
+ IMPL_LINK_NOARG( NamespaceItemDialog, SelectHdl, weld::TreeView&, void)
+ {
+ bool bEnable = m_xNamespacesList->get_selected_index() != -1;
+ m_xEditNamespaceBtn->set_sensitive( bEnable );
+ m_xDeleteNamespaceBtn->set_sensitive( bEnable );
+ }
+
+ IMPL_LINK( NamespaceItemDialog, ClickHdl, weld::Button&, rButton, void )
+ {
+ if (m_xAddNamespaceBtn.get() == &rButton)
+ {
+ ManageNamespaceDialog aDlg(m_xDialog.get(), m_pConditionDlg, false);
+ if (aDlg.run() == RET_OK)
+ {
+ m_xNamespacesList->append_text(aDlg.GetPrefix());
+ int nRow = m_xNamespacesList->n_children();
+ m_xNamespacesList->set_text(nRow - 1, aDlg.GetURL(), 1);
+ }
+ }
+ else if (m_xEditNamespaceBtn.get() == &rButton)
+ {
+ ManageNamespaceDialog aDlg(m_xDialog.get(), m_pConditionDlg, true);
+ int nEntry = m_xNamespacesList->get_selected_index();
+ DBG_ASSERT( nEntry != -1, "NamespaceItemDialog::ClickHdl(): no entry" );
+ OUString sPrefix(m_xNamespacesList->get_text(nEntry, 0));
+ aDlg.SetNamespace(sPrefix, m_xNamespacesList->get_text(nEntry, 1));
+ if (aDlg.run() == RET_OK)
+ {
+ // if a prefix was changed, mark the old prefix as 'removed'
+ if( sPrefix != aDlg.GetPrefix() )
+ m_aRemovedList.push_back( sPrefix );
+
+ m_xNamespacesList->set_text(nEntry, aDlg.GetPrefix(), 0);
+ m_xNamespacesList->set_text(nEntry, aDlg.GetURL(), 1);
+ }
+ }
+ else if (m_xDeleteNamespaceBtn.get() == &rButton)
+ {
+ int nEntry = m_xNamespacesList->get_selected_index();
+ DBG_ASSERT( nEntry != -1, "NamespaceItemDialog::ClickHdl(): no entry" );
+ OUString sPrefix(m_xNamespacesList->get_text(nEntry, 0));
+ m_aRemovedList.push_back( sPrefix );
+ m_xNamespacesList->remove(nEntry);
+ }
+ else
+ {
+ SAL_WARN( "svx.form", "NamespaceItemDialog::ClickHdl(): invalid button" );
+ }
+
+ SelectHdl(*m_xNamespacesList);
+ }
+
+ IMPL_LINK_NOARG(NamespaceItemDialog, OKHdl, weld::Button&, void)
+ {
+ try
+ {
+ // update namespace container
+ sal_Int32 i, nRemovedCount = m_aRemovedList.size();
+ for( i = 0; i < nRemovedCount; ++i )
+ m_rNamespaces->removeByName( m_aRemovedList[i] );
+
+ sal_Int32 nEntryCount = m_xNamespacesList->n_children();
+ for( i = 0; i < nEntryCount; ++i )
+ {
+ OUString sPrefix(m_xNamespacesList->get_text(i, 0));
+ OUString sURL(m_xNamespacesList->get_text(i, 1));
+
+ if ( m_rNamespaces->hasByName( sPrefix ) )
+ m_rNamespaces->replaceByName( sPrefix, Any( sURL ) );
+ else
+ m_rNamespaces->insertByName( sPrefix, Any( sURL ) );
+ }
+ }
+ catch ( Exception const & )
+ {
+ TOOLS_WARN_EXCEPTION( "svx.form", "NamespaceItemDialog::OKHdl()" );
+ }
+ // and close the dialog
+ m_xDialog->response(RET_OK);
+ }
+
+ void NamespaceItemDialog::LoadNamespaces()
+ {
+ try
+ {
+ int nRow = 0;
+ const Sequence< OUString > aAllNames = m_rNamespaces->getElementNames();
+ for ( const OUString& sPrefix : aAllNames )
+ {
+ if ( m_rNamespaces->hasByName( sPrefix ) )
+ {
+ OUString sURL;
+ Any aAny = m_rNamespaces->getByName( sPrefix );
+ if (aAny >>= sURL)
+ {
+ m_xNamespacesList->append_text(sPrefix);
+ m_xNamespacesList->set_text(nRow, sURL, 1);
+ ++nRow;
+ }
+ }
+ }
+ }
+ catch ( Exception const & )
+ {
+ TOOLS_WARN_EXCEPTION( "svx.form", "NamespaceItemDialog::LoadNamespaces()" );
+ }
+ }
+
+ ManageNamespaceDialog::ManageNamespaceDialog(weld::Window* pParent, AddConditionDialog* pCondDlg, bool bIsEdit)
+ : GenericDialogController(pParent, "svx/ui/addnamespacedialog.ui", "AddNamespaceDialog")
+ , m_pConditionDlg(pCondDlg)
+ , m_xPrefixED(m_xBuilder->weld_entry("prefix"))
+ , m_xUrlED(m_xBuilder->weld_entry("url"))
+ , m_xOKBtn(m_xBuilder->weld_button("ok"))
+ , m_xAltTitle(m_xBuilder->weld_label("alttitle"))
+ {
+ if (bIsEdit)
+ m_xDialog->set_title(m_xAltTitle->get_label());
+
+ m_xOKBtn->connect_clicked(LINK(this, ManageNamespaceDialog, OKHdl));
+ }
+
+ ManageNamespaceDialog::~ManageNamespaceDialog()
+ {
+ }
+
+ IMPL_LINK_NOARG(ManageNamespaceDialog, OKHdl, weld::Button&, void)
+ {
+ OUString sPrefix = m_xPrefixED->get_text();
+
+ try
+ {
+ if (!m_pConditionDlg->GetUIHelper()->isValidPrefixName(sPrefix))
+ {
+ std::unique_ptr<weld::MessageDialog> xErrBox(Application::CreateMessageDialog(m_xDialog.get(),
+ VclMessageType::Warning, VclButtonsType::Ok,
+ SvxResId(RID_STR_INVALID_XMLPREFIX)));
+ xErrBox->set_primary_text(xErrBox->get_primary_text().replaceFirst(MSG_VARIABLE, sPrefix));
+ xErrBox->run();
+ return;
+ }
+ }
+ catch ( Exception const & )
+ {
+ TOOLS_WARN_EXCEPTION( "svx.form", "ManageNamespacesDialog::OKHdl()" );
+ }
+
+ // no error so close the dialog
+ m_xDialog->response(RET_OK);
+ }
+
+ AddSubmissionDialog::AddSubmissionDialog(
+ weld::Window* pParent, ItemNode* _pNode,
+ const Reference< css::xforms::XFormsUIHelper1 >& _rUIHelper)
+ : GenericDialogController(pParent, "svx/ui/addsubmissiondialog.ui", "AddSubmissionDialog")
+ , m_pItemNode(_pNode)
+ , m_xUIHelper(_rUIHelper)
+ , m_xNameED(m_xBuilder->weld_entry("name"))
+ , m_xActionED(m_xBuilder->weld_entry("action"))
+ , m_xMethodLB(m_xBuilder->weld_combo_box("method"))
+ , m_xRefED(m_xBuilder->weld_entry("expression"))
+ , m_xRefBtn(m_xBuilder->weld_button("browse"))
+ , m_xBindLB(m_xBuilder->weld_combo_box("binding"))
+ , m_xReplaceLB(m_xBuilder->weld_combo_box("replace"))
+ , m_xOKBtn(m_xBuilder->weld_button("ok"))
+ {
+ FillAllBoxes();
+
+ m_xRefBtn->connect_clicked( LINK( this, AddSubmissionDialog, RefHdl ) );
+ m_xOKBtn->connect_clicked( LINK( this, AddSubmissionDialog, OKHdl ) );
+ }
+
+ AddSubmissionDialog::~AddSubmissionDialog()
+ {
+ // #i38991# if we have added a binding, we need to remove it as well.
+ if( m_xCreatedBinding.is() && m_xUIHelper.is() )
+ m_xUIHelper->removeBindingIfUseless( m_xCreatedBinding );
+ }
+
+ IMPL_LINK_NOARG(AddSubmissionDialog, RefHdl, weld::Button&, void)
+ {
+ AddConditionDialog aDlg(m_xDialog.get(), PN_BINDING_EXPR, m_xTempBinding );
+ aDlg.SetCondition( m_xRefED->get_text() );
+ if ( aDlg.run() == RET_OK )
+ m_xRefED->set_text(aDlg.GetCondition());
+ }
+
+ IMPL_LINK_NOARG(AddSubmissionDialog, OKHdl, weld::Button&, void)
+ {
+ OUString sName(m_xNameED->get_text());
+ if(sName.isEmpty())
+ {
+ std::unique_ptr<weld::MessageDialog> xErrorBox(Application::CreateMessageDialog(m_xDialog.get(),
+ VclMessageType::Warning, VclButtonsType::Ok,
+ SvxResId(RID_STR_EMPTY_SUBMISSIONNAME)));
+ xErrorBox->run();
+ return;
+ }
+
+ if ( !m_xSubmission.is() )
+ {
+ DBG_ASSERT( !m_xNewSubmission.is(),
+ "AddSubmissionDialog::OKHdl(): new submission already exists" );
+
+ // add a new submission
+ Reference< css::xforms::XModel > xModel( m_xUIHelper, UNO_QUERY );
+ if ( xModel.is() )
+ {
+ try
+ {
+ m_xNewSubmission = xModel->createSubmission();
+ m_xSubmission = m_xNewSubmission;
+ }
+ catch ( Exception const & )
+ {
+ TOOLS_WARN_EXCEPTION( "svx.form", "AddSubmissionDialog::OKHdl()" );
+ }
+ }
+ }
+
+ if ( m_xSubmission.is() )
+ {
+ OUString sTemp = m_xNameED->get_text();
+ try
+ {
+ m_xSubmission->setPropertyValue( PN_SUBMISSION_ID, Any( sTemp ) );
+ sTemp = m_xActionED->get_text();
+ m_xSubmission->setPropertyValue( PN_SUBMISSION_ACTION, Any( sTemp ) );
+ sTemp = m_aMethodString.toAPI( m_xMethodLB->get_active_text() );
+ m_xSubmission->setPropertyValue( PN_SUBMISSION_METHOD, Any( sTemp ) );
+ sTemp = m_xRefED->get_text();
+ m_xSubmission->setPropertyValue( PN_SUBMISSION_REF, Any( sTemp ) );
+ OUString sEntry = m_xBindLB->get_active_text();
+ sal_Int32 nColonIdx = sEntry.indexOf(':');
+ if (nColonIdx != -1)
+ sEntry = sEntry.copy(0, nColonIdx);
+ sTemp = sEntry;
+ m_xSubmission->setPropertyValue( PN_SUBMISSION_BIND, Any( sTemp ) );
+ sTemp = m_aReplaceString.toAPI( m_xReplaceLB->get_active_text() );
+ m_xSubmission->setPropertyValue( PN_SUBMISSION_REPLACE, Any( sTemp ) );
+ }
+ catch ( Exception const & )
+ {
+ TOOLS_WARN_EXCEPTION( "svx.form", "AddSubmissionDialog::OKHdl()" );
+ }
+ }
+
+ m_xDialog->response(RET_OK);
+ }
+
+ void AddSubmissionDialog::FillAllBoxes()
+ {
+ // method box
+ m_xMethodLB->append_text(SvxResId(RID_STR_METHOD_POST));
+ m_xMethodLB->append_text(SvxResId(RID_STR_METHOD_PUT));
+ m_xMethodLB->append_text(SvxResId(RID_STR_METHOD_GET));
+ m_xMethodLB->set_active(0);
+
+ // binding box
+ Reference< css::xforms::XModel > xModel( m_xUIHelper, UNO_QUERY );
+ if ( xModel.is() )
+ {
+ try
+ {
+ Reference< XEnumerationAccess > xNumAccess = xModel->getBindings();
+ if ( xNumAccess.is() )
+ {
+ Reference < XEnumeration > xNum = xNumAccess->createEnumeration();
+ if ( xNum.is() && xNum->hasMoreElements() )
+ {
+ while ( xNum->hasMoreElements() )
+ {
+ Reference< XPropertySet > xPropSet;
+ Any aAny = xNum->nextElement();
+ if ( aAny >>= xPropSet )
+ {
+ OUString sEntry;
+ OUString sTemp;
+ xPropSet->getPropertyValue( PN_BINDING_ID ) >>= sTemp;
+ sEntry += sTemp + ": ";
+ xPropSet->getPropertyValue( PN_BINDING_EXPR ) >>= sTemp;
+ sEntry += sTemp;
+ m_xBindLB->append_text(sEntry);
+
+ if ( !m_xTempBinding.is() )
+ m_xTempBinding = xPropSet;
+ }
+ }
+ }
+ }
+ }
+ catch ( Exception const & )
+ {
+ TOOLS_WARN_EXCEPTION( "svx.form", "AddSubmissionDialog::FillAllBoxes()" );
+ }
+ }
+
+ // #i36342# we need a temporary binding; create one if no existing binding
+ // is found
+ if( !m_xTempBinding.is() )
+ {
+ m_xCreatedBinding = m_xUIHelper->getBindingForNode(
+ Reference<css::xml::dom::XNode>(
+ xModel->getDefaultInstance()->getDocumentElement(),
+ UNO_QUERY_THROW ),
+ true );
+ m_xTempBinding = m_xCreatedBinding;
+ }
+
+ // replace box
+ m_xReplaceLB->append_text(SvxResId(RID_STR_REPLACE_NONE));
+ m_xReplaceLB->append_text(SvxResId(RID_STR_REPLACE_INST));
+ m_xReplaceLB->append_text(SvxResId(RID_STR_REPLACE_DOC));
+
+
+ // init the controls with the values of the submission
+ if ( m_pItemNode && m_pItemNode->m_xPropSet.is() )
+ {
+ m_xSubmission = m_pItemNode->m_xPropSet;
+ try
+ {
+ OUString sTemp;
+ m_xSubmission->getPropertyValue( PN_SUBMISSION_ID ) >>= sTemp;
+ m_xNameED->set_text( sTemp );
+ m_xSubmission->getPropertyValue( PN_SUBMISSION_ACTION ) >>= sTemp;
+ m_xActionED->set_text( sTemp );
+ m_xSubmission->getPropertyValue( PN_SUBMISSION_REF ) >>= sTemp;
+ m_xRefED->set_text(sTemp);
+
+ m_xSubmission->getPropertyValue( PN_SUBMISSION_METHOD ) >>= sTemp;
+ sTemp = m_aMethodString.toUI( sTemp );
+ sal_Int32 nPos = m_xMethodLB->find_text( sTemp );
+ if (nPos == -1)
+ {
+ m_xMethodLB->append_text( sTemp );
+ nPos = m_xMethodLB->get_count() - 1;
+ }
+ m_xMethodLB->set_active( nPos );
+
+ m_xSubmission->getPropertyValue( PN_SUBMISSION_BIND ) >>= sTemp;
+ nPos = m_xBindLB->find_text(sTemp);
+ if (nPos == -1)
+ {
+ m_xBindLB->append_text(sTemp);
+ nPos = m_xBindLB->get_count() - 1;
+ }
+ m_xBindLB->set_active(nPos);
+
+ m_xSubmission->getPropertyValue( PN_SUBMISSION_REPLACE ) >>= sTemp;
+ sTemp = m_aReplaceString.toUI( sTemp );
+ if ( sTemp.isEmpty() )
+ sTemp = m_xReplaceLB->get_text(0); // first entry == "none"
+ nPos = m_xReplaceLB->find_text(sTemp);
+ if (nPos == -1)
+ {
+ m_xReplaceLB->append_text(sTemp);
+ nPos = m_xReplaceLB->get_count() - 1;
+ }
+ m_xReplaceLB->set_active(nPos);
+ }
+ catch ( Exception const & )
+ {
+ TOOLS_WARN_EXCEPTION( "svx.form", "AddSubmissionDialog::FillAllBoxes()" );
+ }
+ }
+
+ m_xRefBtn->set_sensitive(m_xTempBinding.is());
+ }
+
+ AddModelDialog::AddModelDialog(weld::Window* pParent, bool bIsEdit)
+ : GenericDialogController(pParent, "svx/ui/addmodeldialog.ui", "AddModelDialog")
+ , m_xNameED(m_xBuilder->weld_entry("name"))
+ , m_xModifyCB(m_xBuilder->weld_check_button("modify"))
+ , m_xAltTitle(m_xBuilder->weld_label("alttitle"))
+ {
+ if (bIsEdit)
+ m_xDialog->set_title(m_xAltTitle->get_label());
+ }
+
+ AddModelDialog::~AddModelDialog()
+ {
+ }
+
+ AddInstanceDialog::AddInstanceDialog(weld::Window* pParent, bool _bEdit)
+ : GenericDialogController(pParent, "svx/ui/addinstancedialog.ui", "AddInstanceDialog")
+ , m_xNameED(m_xBuilder->weld_entry("name"))
+ , m_xURLFT(m_xBuilder->weld_label("urlft"))
+ , m_xURLED(new SvtURLBox(m_xBuilder->weld_combo_box("url")))
+ , m_xFilePickerBtn(m_xBuilder->weld_button("browse"))
+ , m_xLinkInstanceCB(m_xBuilder->weld_check_button("link"))
+ , m_xAltTitle(m_xBuilder->weld_label("alttitle"))
+ {
+ if (_bEdit)
+ m_xDialog->set_title(m_xAltTitle->get_label());
+
+ m_xURLED->DisableHistory();
+ m_xFilePickerBtn->connect_clicked(LINK(this, AddInstanceDialog, FilePickerHdl));
+
+ // load the filter name from fps resource
+ m_sAllFilterName = Translate::get(STR_FILTERNAME_ALL, Translate::Create("fps"));
+ }
+
+ AddInstanceDialog::~AddInstanceDialog()
+ {
+ }
+
+ IMPL_LINK_NOARG(AddInstanceDialog, FilePickerHdl, weld::Button&, void)
+ {
+ ::sfx2::FileDialogHelper aDlg(
+ css::ui::dialogs::TemplateDescription::FILEOPEN_SIMPLE,
+ FileDialogFlags::NONE, m_xDialog.get());
+ aDlg.SetContext(sfx2::FileDialogHelper::FormsAddInstance);
+
+ aDlg.AddFilter( m_sAllFilterName, FILEDIALOG_FILTER_ALL );
+ OUString sFilterName( "XML" );
+ aDlg.AddFilter( sFilterName, "*.xml" );
+ aDlg.SetCurrentFilter( sFilterName );
+
+ if (aDlg.Execute() == ERRCODE_NONE)
+ m_xURLED->set_entry_text(aDlg.GetPath());
+ }
+
+ LinkedInstanceWarningBox::LinkedInstanceWarningBox(weld::Widget* pParent)
+ : MessageDialogController(pParent, "svx/ui/formlinkwarndialog.ui",
+ "FormLinkWarnDialog")
+ {
+ }
+
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/form/dbcharsethelper.cxx b/svx/source/form/dbcharsethelper.cxx
new file mode 100644
index 000000000..a20ffa7c1
--- /dev/null
+++ b/svx/source/form/dbcharsethelper.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 <dbcharsethelper.hxx>
+
+#include <connectivity/dbcharset.hxx>
+
+using namespace ::dbtools;
+
+namespace svxform::charset_helper
+{
+
+ sal_Int32 getSupportedTextEncodings( ::std::vector< rtl_TextEncoding >& _rEncs )
+ {
+ OCharsetMap aCharsetInfo;
+ _rEncs.clear();
+
+ OCharsetMap::const_iterator aLoop = aCharsetInfo.begin();
+ OCharsetMap::const_iterator aLoopEnd = aCharsetInfo.end();
+ while (aLoop != aLoopEnd)
+ {
+ _rEncs.push_back( (*aLoop).getEncoding() );
+ ++aLoop;
+ }
+
+ return _rEncs.size();
+ }
+
+}
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/form/delayedevent.cxx b/svx/source/form/delayedevent.cxx
new file mode 100644
index 000000000..8a9cb218d
--- /dev/null
+++ b/svx/source/form/delayedevent.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 <delayedevent.hxx>
+#include <vcl/svapp.hxx>
+#include <sal/log.hxx>
+
+namespace svxform
+{
+ void DelayedEvent::Call()
+ {
+ CancelPendingCall();
+ SAL_WARN_IF( m_nEventId != nullptr, "svx.form", "DelayedEvent::Call: CancelPendingCall did not work!" );
+
+ m_nEventId = Application::PostUserEvent( LINK( this, DelayedEvent, OnCall ) );
+ }
+
+ void DelayedEvent::CancelPendingCall()
+ {
+ if ( m_nEventId )
+ Application::RemoveUserEvent( m_nEventId );
+ m_nEventId = nullptr;
+ }
+
+ IMPL_LINK( DelayedEvent, OnCall, void*, _pArg, void )
+ {
+ m_nEventId = nullptr;
+ m_aHandler.Call( _pArg );
+ }
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/form/filtnav.cxx b/svx/source/form/filtnav.cxx
new file mode 100644
index 000000000..9b1703f6a
--- /dev/null
+++ b/svx/source/form/filtnav.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 <memory>
+#include <filtnav.hxx>
+#include <fmexch.hxx>
+#include <helpids.h>
+#include <fmprop.hxx>
+#include <svx/strings.hrc>
+
+#include <com/sun/star/awt/XControl.hpp>
+#include <com/sun/star/form/runtime/XFormController.hpp>
+#include <com/sun/star/util/NumberFormatter.hpp>
+#include <com/sun/star/sdb/SQLContext.hpp>
+
+#include <comphelper/processfactory.hxx>
+#include <comphelper/string.hxx>
+#include <connectivity/dbtools.hxx>
+#include <connectivity/sqlnode.hxx>
+#include <cppuhelper/implbase.hxx>
+#include <i18nlangtag/languagetag.hxx>
+#include <fmshimp.hxx>
+#include <o3tl/safeint.hxx>
+#include <sfx2/objitem.hxx>
+#include <svx/dialmgr.hxx>
+#include <svx/fmshell.hxx>
+#include <svx/fmtools.hxx>
+#include <svx/svxids.hrc>
+#include <toolkit/helper/vclunohelper.hxx>
+#include <tools/diagnose_ex.h>
+#include <vcl/commandevent.hxx>
+#include <vcl/event.hxx>
+#include <vcl/settings.hxx>
+#include <vcl/svapp.hxx>
+
+#include <bitmaps.hlst>
+
+#include <functional>
+
+using namespace ::svxform;
+using namespace ::connectivity;
+using namespace ::dbtools;
+
+namespace svxform
+{
+ using ::com::sun::star::uno::Reference;
+ using ::com::sun::star::container::XIndexAccess;
+ using ::com::sun::star::uno::UNO_QUERY;
+ using ::com::sun::star::beans::XPropertySet;
+ using ::com::sun::star::form::runtime::XFormController;
+ using ::com::sun::star::form::runtime::XFilterController;
+ using ::com::sun::star::form::runtime::XFilterControllerListener;
+ using ::com::sun::star::form::runtime::FilterEvent;
+ using ::com::sun::star::lang::EventObject;
+ using ::com::sun::star::form::XForm;
+ using ::com::sun::star::container::XChild;
+ using ::com::sun::star::awt::XControl;
+ using ::com::sun::star::sdbc::XConnection;
+ using ::com::sun::star::util::XNumberFormatsSupplier;
+ using ::com::sun::star::util::XNumberFormatter;
+ using ::com::sun::star::util::NumberFormatter;
+ using ::com::sun::star::sdbc::XRowSet;
+ using ::com::sun::star::lang::Locale;
+ using ::com::sun::star::sdb::SQLContext;
+ using ::com::sun::star::uno::XInterface;
+ using ::com::sun::star::uno::UNO_QUERY_THROW;
+ using ::com::sun::star::uno::UNO_SET_THROW;
+ using ::com::sun::star::uno::Exception;
+ using ::com::sun::star::uno::Sequence;
+
+
+OFilterItemExchange::OFilterItemExchange()
+ : m_pFormItem(nullptr)
+{
+}
+
+void OFilterItemExchange::AddSupportedFormats()
+{
+ AddFormat(getFormatId());
+}
+
+SotClipboardFormatId OFilterItemExchange::getFormatId()
+{
+ static SotClipboardFormatId s_nFormat =
+ SotExchange::RegisterFormatName("application/x-openoffice;windows_formatname=\"form.FilterControlExchange\"");
+ DBG_ASSERT(static_cast<SotClipboardFormatId>(-1) != s_nFormat, "OFilterExchangeHelper::getFormatId: bad exchange id!");
+ return s_nFormat;
+}
+
+rtl::Reference<OLocalExchange> OFilterExchangeHelper::createExchange() const
+{
+ return new OFilterItemExchange;
+}
+
+OUString FmFilterData::GetImage() const
+{
+ return OUString();
+}
+
+FmParentData::~FmParentData()
+{
+}
+
+OUString FmFormItem::GetImage() const
+{
+ return RID_SVXBMP_FORM;
+}
+
+FmFilterItem* FmFilterItems::Find( const ::sal_Int32 _nFilterComponentIndex ) const
+{
+ for ( auto & pData : m_aChildren )
+ {
+ FmFilterItem& rCondition = dynamic_cast<FmFilterItem&>(*pData);
+ if ( _nFilterComponentIndex == rCondition.GetComponentIndex() )
+ return &rCondition;
+ }
+ return nullptr;
+}
+
+OUString FmFilterItems::GetImage() const
+{
+ return RID_SVXBMP_FILTER;
+}
+
+FmFilterItem::FmFilterItem( FmFilterItems* pParent,
+ const OUString& aFieldName,
+ const OUString& aText,
+ const sal_Int32 _nComponentIndex )
+ :FmFilterData(pParent, aText)
+ ,m_aFieldName(aFieldName)
+ ,m_nComponentIndex( _nComponentIndex )
+{
+}
+
+OUString FmFilterItem::GetImage() const
+{
+ return RID_SVXBMP_FIELD;
+}
+
+// Hints for communication between model and view
+
+namespace {
+
+class FmFilterHint : public SfxHint
+{
+ FmFilterData* m_pData;
+
+public:
+ explicit FmFilterHint(FmFilterData* pData):m_pData(pData){}
+ FmFilterData* GetData() const { return m_pData; }
+};
+
+class FmFilterInsertedHint : public FmFilterHint
+{
+ size_t m_nPos; // Position relative to the parent of the data
+
+public:
+ FmFilterInsertedHint(FmFilterData* pData, size_t nRelPos)
+ :FmFilterHint(pData)
+ ,m_nPos(nRelPos){}
+
+ size_t GetPos() const { return m_nPos; }
+};
+
+class FmFilterRemovedHint : public FmFilterHint
+{
+public:
+ explicit FmFilterRemovedHint(FmFilterData* pData)
+ :FmFilterHint(pData){}
+};
+
+
+class FmFilterTextChangedHint : public FmFilterHint
+{
+public:
+ explicit FmFilterTextChangedHint(FmFilterData* pData)
+ :FmFilterHint(pData){}
+};
+
+class FilterClearingHint : public SfxHint
+{
+public:
+ FilterClearingHint(){}
+};
+
+class FmFilterCurrentChangedHint : public SfxHint
+{
+public:
+ FmFilterCurrentChangedHint(){}
+};
+
+}
+
+// class FmFilterAdapter, listener at the FilterControls
+class FmFilterAdapter : public ::cppu::WeakImplHelper< XFilterControllerListener >
+{
+ FmFilterModel* m_pModel;
+ Reference< XIndexAccess > m_xControllers;
+
+public:
+ FmFilterAdapter(FmFilterModel* pModel, const Reference< XIndexAccess >& xControllers);
+
+// XEventListener
+ virtual void SAL_CALL disposing(const EventObject& Source) override;
+
+// XFilterControllerListener
+ virtual void SAL_CALL predicateExpressionChanged( const FilterEvent& Event ) override;
+ virtual void SAL_CALL disjunctiveTermRemoved( const FilterEvent& Event ) override;
+ virtual void SAL_CALL disjunctiveTermAdded( const FilterEvent& Event ) override;
+
+// helpers
+ /// @throws RuntimeException
+ void dispose();
+
+ void AddOrRemoveListener( const Reference< XIndexAccess >& _rxControllers, const bool _bAdd );
+
+ static void setText(sal_Int32 nPos,
+ const FmFilterItem* pFilterItem,
+ const OUString& rText);
+};
+
+
+FmFilterAdapter::FmFilterAdapter(FmFilterModel* pModel, const Reference< XIndexAccess >& xControllers)
+ :m_pModel( pModel )
+ ,m_xControllers( xControllers )
+{
+ AddOrRemoveListener( m_xControllers, true );
+}
+
+
+void FmFilterAdapter::dispose()
+{
+ AddOrRemoveListener( m_xControllers, false );
+}
+
+
+void FmFilterAdapter::AddOrRemoveListener( const Reference< XIndexAccess >& _rxControllers, const bool _bAdd )
+{
+ for (sal_Int32 i = 0, nLen = _rxControllers->getCount(); i < nLen; ++i)
+ {
+ Reference< XIndexAccess > xElement( _rxControllers->getByIndex(i), UNO_QUERY );
+
+ // step down
+ AddOrRemoveListener( xElement, _bAdd );
+
+ // handle this particular controller
+ Reference< XFilterController > xController( xElement, UNO_QUERY );
+ OSL_ENSURE( xController.is(), "FmFilterAdapter::InsertElements: no XFilterController, cannot sync data!" );
+ if ( xController.is() )
+ {
+ if ( _bAdd )
+ xController->addFilterControllerListener( this );
+ else
+ xController->removeFilterControllerListener( this );
+ }
+ }
+}
+
+
+void FmFilterAdapter::setText(sal_Int32 nRowPos,
+ const FmFilterItem* pFilterItem,
+ const OUString& rText)
+{
+ FmFormItem* pFormItem = dynamic_cast<FmFormItem*>( pFilterItem->GetParent()->GetParent() );
+ assert(pFormItem);
+ try
+ {
+ Reference< XFilterController > xController( pFormItem->GetController(), UNO_QUERY_THROW );
+ xController->setPredicateExpression( pFilterItem->GetComponentIndex(), nRowPos, rText );
+ }
+ catch( const Exception& )
+ {
+ DBG_UNHANDLED_EXCEPTION("svx");
+ }
+}
+
+
+// XEventListener
+
+void SAL_CALL FmFilterAdapter::disposing(const EventObject& /*e*/)
+{
+}
+
+
+namespace
+{
+ OUString lcl_getLabelName_nothrow( const Reference< XControl >& _rxControl )
+ {
+ OUString sLabelName;
+ try
+ {
+ Reference< XPropertySet > xModel( _rxControl->getModel(), UNO_QUERY_THROW );
+ sLabelName = getLabelName( xModel );
+ }
+ catch( const Exception& )
+ {
+ DBG_UNHANDLED_EXCEPTION("svx");
+ }
+ return sLabelName;
+ }
+
+ Reference< XPropertySet > lcl_getBoundField_nothrow( const Reference< XControl >& _rxControl )
+ {
+ Reference< XPropertySet > xField;
+ try
+ {
+ Reference< XPropertySet > xModelProps( _rxControl->getModel(), UNO_QUERY_THROW );
+ xField.set( xModelProps->getPropertyValue( FM_PROP_BOUNDFIELD ), UNO_QUERY_THROW );
+ }
+ catch( const Exception& )
+ {
+ DBG_UNHANDLED_EXCEPTION("svx");
+ }
+ return xField;
+ }
+}
+
+// XFilterControllerListener
+void FmFilterAdapter::predicateExpressionChanged( const FilterEvent& Event )
+{
+ SolarMutexGuard aGuard;
+
+ if ( !m_pModel )
+ return;
+
+ // the controller which sent the event
+ Reference< XFormController > xController( Event.Source, UNO_QUERY_THROW );
+ Reference< XFilterController > xFilterController( Event.Source, UNO_QUERY_THROW );
+ Reference< XForm > xForm( xController->getModel(), UNO_QUERY_THROW );
+
+ FmFormItem* pFormItem = m_pModel->Find( m_pModel->m_aChildren, xForm );
+ OSL_ENSURE( pFormItem, "FmFilterAdapter::predicateExpressionChanged: don't know this form!" );
+ if ( !pFormItem )
+ return;
+
+ const sal_Int32 nActiveTerm( xFilterController->getActiveTerm() );
+
+ FmFilterData* pData = pFormItem->GetChildren()[nActiveTerm].get();
+ FmFilterItems& rFilter = dynamic_cast<FmFilterItems&>(*pData);
+ FmFilterItem* pFilterItem = rFilter.Find( Event.FilterComponent );
+ if ( pFilterItem )
+ {
+ if ( !Event.PredicateExpression.isEmpty())
+ {
+ pFilterItem->SetText( Event.PredicateExpression );
+ // notify the UI
+ FmFilterTextChangedHint aChangeHint(pFilterItem);
+ m_pModel->Broadcast( aChangeHint );
+ }
+ else
+ {
+ // no text anymore so remove the condition
+ m_pModel->Remove(pFilterItem);
+ }
+ }
+ else
+ {
+ // searching the component by field name
+ OUString aFieldName( lcl_getLabelName_nothrow( xFilterController->getFilterComponent( Event.FilterComponent ) ) );
+
+ std::unique_ptr<FmFilterItem> pNewFilterItem(new FmFilterItem(&rFilter, aFieldName, Event.PredicateExpression, Event.FilterComponent));
+ m_pModel->Insert(rFilter.GetChildren().end(), std::move(pNewFilterItem));
+ }
+
+ // ensure there's one empty term in the filter, just in case the active term was previously empty
+ m_pModel->EnsureEmptyFilterRows( *pFormItem );
+}
+
+
+void SAL_CALL FmFilterAdapter::disjunctiveTermRemoved( const FilterEvent& Event )
+{
+ SolarMutexGuard aGuard;
+
+ Reference< XFormController > xController( Event.Source, UNO_QUERY_THROW );
+ Reference< XFilterController > xFilterController( Event.Source, UNO_QUERY_THROW );
+ Reference< XForm > xForm( xController->getModel(), UNO_QUERY_THROW );
+
+ FmFormItem* pFormItem = m_pModel->Find( m_pModel->m_aChildren, xForm );
+ OSL_ENSURE( pFormItem, "FmFilterAdapter::disjunctiveTermRemoved: don't know this form!" );
+ if ( !pFormItem )
+ return;
+
+ auto& rTermItems = pFormItem->GetChildren();
+ const bool bValidIndex = ( Event.DisjunctiveTerm >= 0 ) && ( o3tl::make_unsigned(Event.DisjunctiveTerm) < rTermItems.size() );
+ OSL_ENSURE( bValidIndex, "FmFilterAdapter::disjunctiveTermRemoved: invalid term index!" );
+ if ( !bValidIndex )
+ return;
+
+ // if the first term was removed, then the to-be first term needs its text updated
+ if ( Event.DisjunctiveTerm == 0 )
+ {
+ rTermItems[1]->SetText( SvxResId(RID_STR_FILTER_FILTER_FOR));
+ FmFilterTextChangedHint aChangeHint( rTermItems[1].get() );
+ m_pModel->Broadcast( aChangeHint );
+ }
+
+ // finally remove the entry from the model
+ m_pModel->Remove( rTermItems.begin() + Event.DisjunctiveTerm );
+
+ // ensure there's one empty term in the filter, just in case the currently removed one was the last empty one
+ m_pModel->EnsureEmptyFilterRows( *pFormItem );
+}
+
+
+void SAL_CALL FmFilterAdapter::disjunctiveTermAdded( const FilterEvent& Event )
+{
+ SolarMutexGuard aGuard;
+
+ Reference< XFormController > xController( Event.Source, UNO_QUERY_THROW );
+ Reference< XFilterController > xFilterController( Event.Source, UNO_QUERY_THROW );
+ Reference< XForm > xForm( xController->getModel(), UNO_QUERY_THROW );
+
+ FmFormItem* pFormItem = m_pModel->Find( m_pModel->m_aChildren, xForm );
+ OSL_ENSURE( pFormItem, "FmFilterAdapter::disjunctiveTermAdded: don't know this form!" );
+ if ( !pFormItem )
+ return;
+
+ const sal_Int32 nInsertPos = Event.DisjunctiveTerm;
+ bool bValidIndex = ( nInsertPos >= 0 ) && ( o3tl::make_unsigned(nInsertPos) <= pFormItem->GetChildren().size() );
+ if ( !bValidIndex )
+ {
+ OSL_FAIL( "FmFilterAdapter::disjunctiveTermAdded: invalid index!" );
+ return;
+ }
+
+ auto insertPos = pFormItem->GetChildren().begin() + nInsertPos;
+
+ // "Filter for" for first position, "Or" for the other positions
+ std::unique_ptr<FmFilterItems> pFilterItems(new FmFilterItems(pFormItem, (nInsertPos?SvxResId(RID_STR_FILTER_FILTER_OR):SvxResId(RID_STR_FILTER_FILTER_FOR))));
+ m_pModel->Insert( insertPos, std::move(pFilterItems) );
+}
+
+
+FmFilterModel::FmFilterModel()
+ :FmParentData(nullptr, OUString())
+ ,OSQLParserClient(comphelper::getProcessComponentContext())
+ ,m_pCurrentItems(nullptr)
+{
+}
+
+
+FmFilterModel::~FmFilterModel()
+{
+ Clear();
+}
+
+
+void FmFilterModel::Clear()
+{
+ // notify
+ FilterClearingHint aClearedHint;
+ Broadcast( aClearedHint );
+
+ // lose endings
+ if (m_pAdapter.is())
+ {
+ m_pAdapter->dispose();
+ m_pAdapter.clear();
+ }
+
+ m_pCurrentItems = nullptr;
+ m_xController = nullptr;
+ m_xControllers = nullptr;
+
+ m_aChildren.clear();
+}
+
+
+void FmFilterModel::Update(const Reference< XIndexAccess > & xControllers, const Reference< XFormController > & xCurrent)
+{
+ if ( xCurrent == m_xController )
+ return;
+
+ if (!xControllers.is())
+ {
+ Clear();
+ return;
+ }
+
+ // there is only a new current controller
+ if ( m_xControllers != xControllers )
+ {
+ Clear();
+
+ m_xControllers = xControllers;
+ Update(m_xControllers, this);
+
+ DBG_ASSERT(xCurrent.is(), "FmFilterModel::Update(...) no current controller");
+
+ // Listening for TextChanges
+ m_pAdapter = new FmFilterAdapter(this, xControllers);
+
+ SetCurrentController(xCurrent);
+ EnsureEmptyFilterRows( *this );
+ }
+ else
+ SetCurrentController(xCurrent);
+}
+
+
+void FmFilterModel::Update(const Reference< XIndexAccess > & xControllers, FmParentData* pParent)
+{
+ try
+ {
+ sal_Int32 nCount = xControllers->getCount();
+ for ( sal_Int32 i = 0; i < nCount; ++i )
+ {
+ Reference< XFormController > xController( xControllers->getByIndex(i), UNO_QUERY_THROW );
+
+ Reference< XPropertySet > xFormProperties( xController->getModel(), UNO_QUERY_THROW );
+ OUString aName;
+ OSL_VERIFY( xFormProperties->getPropertyValue( FM_PROP_NAME ) >>= aName );
+
+ // Insert a new item for the form
+ FmFormItem* pFormItem = new FmFormItem( pParent, xController, aName );
+ Insert( pParent->GetChildren().end(), std::unique_ptr<FmFilterData>(pFormItem) );
+
+ Reference< XFilterController > xFilterController( pFormItem->GetFilterController(), UNO_SET_THROW );
+
+ // insert the existing filters for the form
+ OUString aTitle(SvxResId(RID_STR_FILTER_FILTER_FOR));
+
+ const Sequence< Sequence< OUString > > aExpressions = xFilterController->getPredicateExpressions();
+ for ( auto const & conjunctionTerm : aExpressions )
+ {
+ // we always display one row, even if there's no term to be displayed
+ FmFilterItems* pFilterItems = new FmFilterItems( pFormItem, aTitle );
+ Insert( pFormItem->GetChildren().end(), std::unique_ptr<FmFilterData>(pFilterItems) );
+
+ const Sequence< OUString >& rDisjunction( conjunctionTerm );
+ sal_Int32 nComponentIndex = -1;
+ for ( const OUString& rDisjunctiveTerm : rDisjunction )
+ {
+ ++nComponentIndex;
+
+ if ( rDisjunctiveTerm.isEmpty() )
+ // no condition for this particular component in this particular conjunction term
+ continue;
+
+ // determine the display name of the control
+ const Reference< XControl > xFilterControl( xFilterController->getFilterComponent( nComponentIndex ) );
+ const OUString sDisplayName( lcl_getLabelName_nothrow( xFilterControl ) );
+
+ // insert a new entry
+ std::unique_ptr<FmFilterItem> pANDCondition(new FmFilterItem( pFilterItems, sDisplayName, rDisjunctiveTerm, nComponentIndex ));
+ Insert( pFilterItems->GetChildren().end(), std::move(pANDCondition) );
+ }
+
+ // title for the next conditions
+ aTitle = SvxResId( RID_STR_FILTER_FILTER_OR );
+ }
+
+ // now add dependent controllers
+ Update( xController, pFormItem );
+ }
+ }
+ catch( const Exception& )
+ {
+ DBG_UNHANDLED_EXCEPTION("svx");
+ }
+}
+
+
+FmFormItem* FmFilterModel::Find(const ::std::vector<std::unique_ptr<FmFilterData>>& rItems, const Reference< XFormController > & xController) const
+{
+ for (const auto& rItem : rItems)
+ {
+ FmFormItem* pForm = dynamic_cast<FmFormItem*>( rItem.get() );
+ if (pForm)
+ {
+ if ( xController == pForm->GetController() )
+ return pForm;
+ else
+ {
+ pForm = Find(pForm->GetChildren(), xController);
+ if (pForm)
+ return pForm;
+ }
+ }
+ }
+ return nullptr;
+}
+
+
+FmFormItem* FmFilterModel::Find(const ::std::vector<std::unique_ptr<FmFilterData>>& rItems, const Reference< XForm >& xForm) const
+{
+ for (const auto& rItem : rItems)
+ {
+ FmFormItem* pForm = dynamic_cast<FmFormItem*>( rItem.get() );
+ if (pForm)
+ {
+ if (xForm == pForm->GetController()->getModel())
+ return pForm;
+ else
+ {
+ pForm = Find(pForm->GetChildren(), xForm);
+ if (pForm)
+ return pForm;
+ }
+ }
+ }
+ return nullptr;
+}
+
+void FmFilterModel::SetCurrentController(const Reference< XFormController > & xCurrent)
+{
+ if ( xCurrent == m_xController )
+ return;
+
+ m_xController = xCurrent;
+
+ FmFormItem* pItem = Find( m_aChildren, xCurrent );
+ if ( !pItem )
+ return;
+
+ try
+ {
+ Reference< XFilterController > xFilterController( m_xController, UNO_QUERY_THROW );
+ const sal_Int32 nActiveTerm( xFilterController->getActiveTerm() );
+ if (nActiveTerm != -1 && pItem->GetChildren().size() > o3tl::make_unsigned(nActiveTerm))
+ {
+ SetCurrentItems( static_cast< FmFilterItems* >( pItem->GetChildren()[ nActiveTerm ].get() ) );
+ }
+ }
+ catch( const Exception& )
+ {
+ DBG_UNHANDLED_EXCEPTION("svx");
+ }
+}
+
+void FmFilterModel::AppendFilterItems( FmFormItem& _rFormItem )
+{
+ // insert the condition behind the last filter items
+ auto iter = std::find_if(_rFormItem.GetChildren().rbegin(), _rFormItem.GetChildren().rend(),
+ [](const std::unique_ptr<FmFilterData>& rChild) { return dynamic_cast<const FmFilterItems*>(rChild.get()) != nullptr; });
+
+ sal_Int32 nInsertPos = iter.base() - _rFormItem.GetChildren().begin();
+ // delegate this to the FilterController, it will notify us, which will let us update our model
+ try
+ {
+ Reference< XFilterController > xFilterController( _rFormItem.GetFilterController(), UNO_SET_THROW );
+ if ( nInsertPos >= xFilterController->getDisjunctiveTerms() )
+ xFilterController->appendEmptyDisjunctiveTerm();
+ }
+ catch( const Exception& )
+ {
+ DBG_UNHANDLED_EXCEPTION("svx");
+ }
+}
+
+void FmFilterModel::Insert(const ::std::vector<std::unique_ptr<FmFilterData>>::iterator& rPos, std::unique_ptr<FmFilterData> pData)
+{
+ auto pTemp = pData.get();
+ size_t nPos;
+ ::std::vector<std::unique_ptr<FmFilterData>>& rItems = pData->GetParent()->GetChildren();
+ if (rPos == rItems.end())
+ {
+ nPos = rItems.size();
+ rItems.push_back(std::move(pData));
+ }
+ else
+ {
+ nPos = rPos - rItems.begin();
+ rItems.insert(rPos, std::move(pData));
+ }
+
+ // notify the UI
+ FmFilterInsertedHint aInsertedHint(pTemp, nPos);
+ Broadcast( aInsertedHint );
+}
+
+void FmFilterModel::Remove(FmFilterData* pData)
+{
+ FmParentData* pParent = pData->GetParent();
+ ::std::vector<std::unique_ptr<FmFilterData>>& rItems = pParent->GetChildren();
+
+ // erase the item from the model
+ auto i = ::std::find_if(rItems.begin(), rItems.end(),
+ [&](const std::unique_ptr<FmFilterData>& p) { return p.get() == pData; } );
+ DBG_ASSERT(i != rItems.end(), "FmFilterModel::Remove(): unknown Item");
+ // position within the parent
+ sal_Int32 nPos = i - rItems.begin();
+ if (auto pFilterItems = dynamic_cast<FmFilterItems*>( pData))
+ {
+ FmFormItem* pFormItem = static_cast<FmFormItem*>(pParent);
+
+ try
+ {
+ Reference< XFilterController > xFilterController( pFormItem->GetFilterController(), UNO_SET_THROW );
+
+ bool bEmptyLastTerm = ( ( nPos == 0 ) && xFilterController->getDisjunctiveTerms() == 1 );
+ if ( bEmptyLastTerm )
+ {
+ // remove all children (by setting an empty predicate expression)
+ ::std::vector< std::unique_ptr<FmFilterData> >& rChildren = pFilterItems->GetChildren();
+ while ( !rChildren.empty() )
+ {
+ auto removePos = rChildren.end() - 1;
+ if (FmFilterItem* pFilterItem = dynamic_cast<FmFilterItem*>( removePos->get() ))
+ {
+ FmFilterAdapter::setText( nPos, pFilterItem, OUString() );
+ }
+ Remove( removePos );
+ }
+ }
+ else
+ {
+ xFilterController->removeDisjunctiveTerm( nPos );
+ }
+ }
+ catch( const Exception& )
+ {
+ DBG_UNHANDLED_EXCEPTION("svx");
+ }
+ }
+ else // FormItems can not be deleted
+ {
+ FmFilterItem& rFilterItem = dynamic_cast<FmFilterItem&>(*pData);
+
+ // if it's the last condition remove the parent
+ if (rItems.size() == 1)
+ Remove(rFilterItem.GetParent());
+ else
+ {
+ // find the position of the father within his father
+ ::std::vector<std::unique_ptr<FmFilterData>>& rParentParentItems = pData->GetParent()->GetParent()->GetChildren();
+ auto j = ::std::find_if(rParentParentItems.begin(), rParentParentItems.end(),
+ [&](const std::unique_ptr<FmFilterData>& p) { return p.get() == rFilterItem.GetParent(); });
+ DBG_ASSERT(j != rParentParentItems.end(), "FmFilterModel::Remove(): unknown Item");
+ sal_Int32 nParentPos = j - rParentParentItems.begin();
+
+ // EmptyText removes the filter
+ FmFilterAdapter::setText(nParentPos, &rFilterItem, OUString());
+ Remove( i );
+ }
+ }
+}
+
+void FmFilterModel::Remove( const ::std::vector<std::unique_ptr<FmFilterData>>::iterator& rPos )
+{
+ // remove from parent's child list
+ std::unique_ptr<FmFilterData> pData = std::move(*rPos);
+ pData->GetParent()->GetChildren().erase( rPos );
+
+ // notify the view, this will remove the actual SvTreeListEntry
+ FmFilterRemovedHint aRemoveHint( pData.get() );
+ Broadcast( aRemoveHint );
+}
+
+
+bool FmFilterModel::ValidateText(FmFilterItem const * pItem, OUString& rText, OUString& rErrorMsg) const
+{
+ FmFormItem* pFormItem = dynamic_cast<FmFormItem*>( pItem->GetParent()->GetParent() );
+ assert(pFormItem);
+ try
+ {
+ Reference< XFormController > xFormController( pFormItem->GetController() );
+ // obtain the connection of the form belonging to the controller
+ Reference< XRowSet > xRowSet( xFormController->getModel(), UNO_QUERY_THROW );
+ Reference< XConnection > xConnection( getConnection( xRowSet ) );
+
+ // obtain a number formatter for this connection
+ // TODO: shouldn't this be cached?
+ Reference< XNumberFormatsSupplier > xFormatSupplier = getNumberFormats( xConnection, true );
+ Reference< XNumberFormatter > xFormatter( NumberFormatter::create( comphelper::getProcessComponentContext() ), UNO_QUERY_THROW );
+ xFormatter->attachNumberFormatsSupplier( xFormatSupplier );
+
+ // get the field (database column) which the item is responsible for
+ Reference< XFilterController > xFilterController( xFormController, UNO_QUERY_THROW );
+ Reference< XPropertySet > xField( lcl_getBoundField_nothrow( xFilterController->getFilterComponent( pItem->GetComponentIndex() ) ), UNO_SET_THROW );
+
+ // parse the given text as filter predicate
+ OUString aErr, aTxt( rText );
+ std::unique_ptr< OSQLParseNode > pParseNode = predicateTree( aErr, aTxt, xFormatter, xField );
+ rErrorMsg = aErr;
+ rText = aTxt;
+ if ( pParseNode != nullptr )
+ {
+ OUString aPreparedText;
+ Locale aAppLocale = Application::GetSettings().GetUILanguageTag().getLocale();
+ pParseNode->parseNodeToPredicateStr(
+ aPreparedText, xConnection, xFormatter, xField, OUString(), aAppLocale, OUString("."), getParseContext() );
+ rText = aPreparedText;
+ return true;
+ }
+ }
+ catch( const Exception& )
+ {
+ DBG_UNHANDLED_EXCEPTION("svx");
+ }
+
+ return false;
+}
+
+
+void FmFilterModel::Append(FmFilterItems* pItems, std::unique_ptr<FmFilterItem> pFilterItem)
+{
+ Insert(pItems->GetChildren().end(), std::move(pFilterItem));
+}
+
+
+void FmFilterModel::SetTextForItem(FmFilterItem* pItem, const OUString& rText)
+{
+ ::std::vector<std::unique_ptr<FmFilterData>>& rItems = pItem->GetParent()->GetParent()->GetChildren();
+ auto i = ::std::find_if(rItems.begin(), rItems.end(),
+ [&](const std::unique_ptr<FmFilterData>& p) { return p.get() == pItem->GetParent(); });
+ sal_Int32 nParentPos = i - rItems.begin();
+
+ FmFilterAdapter::setText(nParentPos, pItem, rText);
+
+ if (rText.isEmpty())
+ Remove(pItem);
+ else
+ {
+ // Change the text
+ pItem->SetText(rText);
+ FmFilterTextChangedHint aChangeHint(pItem);
+ Broadcast( aChangeHint );
+ }
+}
+
+
+void FmFilterModel::SetCurrentItems(FmFilterItems* pCurrent)
+{
+ if (m_pCurrentItems == pCurrent)
+ return;
+
+ // search for the condition
+ if (pCurrent)
+ {
+ FmFormItem* pFormItem = static_cast<FmFormItem*>(pCurrent->GetParent());
+ ::std::vector<std::unique_ptr<FmFilterData>>& rItems = pFormItem->GetChildren();
+ auto i = ::std::find_if(rItems.begin(), rItems.end(),
+ [&](const std::unique_ptr<FmFilterData>& p) { return p.get() == pCurrent; });
+
+ if (i != rItems.end())
+ {
+ // determine the filter position
+ sal_Int32 nPos = i - rItems.begin();
+ try
+ {
+ Reference< XFilterController > xFilterController( pFormItem->GetFilterController(), UNO_SET_THROW );
+ xFilterController->setActiveTerm( nPos );
+ }
+ catch( const Exception& )
+ {
+ DBG_UNHANDLED_EXCEPTION("svx");
+ }
+
+ if ( m_xController != pFormItem->GetController() )
+ // calls SetCurrentItems again
+ SetCurrentController( pFormItem->GetController() );
+ else
+ m_pCurrentItems = pCurrent;
+ }
+ else
+ m_pCurrentItems = nullptr;
+ }
+ else
+ m_pCurrentItems = nullptr;
+
+
+ // notify the UI
+ FmFilterCurrentChangedHint aHint;
+ Broadcast( aHint );
+}
+
+
+void FmFilterModel::EnsureEmptyFilterRows( FmParentData& _rItem )
+{
+ // checks whether for each form there's one free level for input
+ ::std::vector< std::unique_ptr<FmFilterData> >& rChildren = _rItem.GetChildren();
+ bool bAppendLevel = dynamic_cast<const FmFormItem*>(&_rItem) != nullptr;
+
+ for ( const auto& rpChild : rChildren )
+ {
+ FmFilterItems* pItems = dynamic_cast<FmFilterItems*>( rpChild.get() );
+ if ( pItems && pItems->GetChildren().empty() )
+ {
+ bAppendLevel = false;
+ break;
+ }
+
+ FmFormItem* pFormItem = dynamic_cast<FmFormItem*>( rpChild.get() );
+ if (pFormItem)
+ {
+ EnsureEmptyFilterRows( *pFormItem );
+ continue;
+ }
+ }
+
+ if ( bAppendLevel )
+ {
+ FmFormItem* pFormItem = dynamic_cast<FmFormItem*>( &_rItem );
+ OSL_ENSURE( pFormItem, "FmFilterModel::EnsureEmptyFilterRows: no FmFormItem, but a FmFilterItems child?" );
+ if ( pFormItem )
+ AppendFilterItems( *pFormItem );
+ }
+}
+
+const int nxD = 4;
+const int nxDBmp = 12;
+
+IMPL_STATIC_LINK(FmFilterNavigator, CustomGetSizeHdl, weld::TreeView::get_size_args, aPayload, Size)
+{
+ vcl::RenderContext& rRenderContext = aPayload.first;
+ const OUString& rId = aPayload.second;
+
+ Size aSize;
+
+ FmFilterData* pData = weld::fromId<FmFilterData*>(rId);
+ OUString sText = pData->GetText();
+
+ if (FmFilterItem* pItem = dynamic_cast<FmFilterItem*>(pData))
+ {
+ rRenderContext.Push(vcl::PushFlags::FONT);
+ vcl::Font aFont(rRenderContext.GetFont());
+ aFont.SetWeight(WEIGHT_BOLD);
+ rRenderContext.SetFont(aFont);
+
+ OUString sName = pItem->GetFieldName() + ": ";
+ aSize = Size(rRenderContext.GetTextWidth(sName), rRenderContext.GetTextHeight());
+
+ rRenderContext.Pop();
+
+ aSize.AdjustWidth(rRenderContext.GetTextWidth(sText) + nxD);
+ }
+ else
+ {
+ aSize = Size(rRenderContext.GetTextWidth(sText), rRenderContext.GetTextHeight());
+ if (dynamic_cast<FmFilterItems*>(pData))
+ aSize.AdjustWidth(nxDBmp);
+ }
+
+ return aSize;
+}
+
+IMPL_STATIC_LINK(FmFilterNavigator, CustomRenderHdl, weld::TreeView::render_args, aPayload, void)
+{
+ vcl::RenderContext& rRenderContext = std::get<0>(aPayload);
+ const ::tools::Rectangle& rRect = std::get<1>(aPayload);
+ ::tools::Rectangle aRect(rRect.TopLeft(), Size(rRenderContext.GetOutputSize().Width() - rRect.Left(), rRect.GetHeight()));
+ bool bSelected = std::get<2>(aPayload);
+ const OUString& rId = std::get<3>(aPayload);
+
+ rRenderContext.Push(vcl::PushFlags::TEXTCOLOR);
+ const StyleSettings& rStyleSettings = Application::GetSettings().GetStyleSettings();
+ if (bSelected)
+ rRenderContext.SetTextColor(rStyleSettings.GetHighlightTextColor());
+ else
+ rRenderContext.SetTextColor(rStyleSettings.GetDialogTextColor());
+
+ FmFilterData* pData = weld::fromId<FmFilterData*>(rId);
+ OUString sText = pData->GetText();
+ Point aPos(aRect.TopLeft());
+
+ if (FmFilterItem* pFilter = dynamic_cast<FmFilterItem*>(pData))
+ {
+ vcl::Font aFont(rRenderContext.GetFont());
+ aFont.SetWeight(WEIGHT_BOLD);
+
+ rRenderContext.Push(vcl::PushFlags::FONT);
+ rRenderContext.SetFont(aFont);
+
+ OUString sName = pFilter->GetFieldName() + ": ";
+ rRenderContext.DrawText(aPos, sName);
+
+ // position for the second text
+ aPos.AdjustX(rRenderContext.GetTextWidth(sName) + nxD);
+ rRenderContext.Pop();
+
+ rRenderContext.DrawText(aPos, sText);
+ }
+ else if (FmFilterItems* pRow = dynamic_cast<FmFilterItems*>(pData))
+ {
+ FmFormItem* pForm = static_cast<FmFormItem*>(pRow->GetParent());
+
+ // current filter is significant painted
+ const bool bIsCurrentFilter = pForm->GetChildren()[ pForm->GetFilterController()->getActiveTerm() ].get() == pRow;
+ if (bIsCurrentFilter)
+ {
+ rRenderContext.Push(vcl::PushFlags::LINECOLOR);
+ rRenderContext.SetLineColor(rRenderContext.GetTextColor());
+
+ Point aFirst(aPos.X(), aRect.Bottom() - 6);
+ Point aSecond(aFirst .X() + 2, aFirst.Y() + 3);
+
+ rRenderContext.DrawLine(aFirst, aSecond);
+
+ aFirst = aSecond;
+ aFirst.AdjustX(1);
+ aSecond.AdjustX(6);
+ aSecond.AdjustY(-5);
+
+ rRenderContext.DrawLine(aFirst, aSecond);
+ rRenderContext.Pop();
+ }
+
+ rRenderContext.DrawText(Point(aPos.X() + nxDBmp, aPos.Y()), sText);
+ }
+ else
+ rRenderContext.DrawText(aPos, sText);
+
+ rRenderContext.Pop();
+}
+
+FmFilterNavigatorDropTarget::FmFilterNavigatorDropTarget(FmFilterNavigator& rTreeView)
+ : DropTargetHelper(rTreeView.get_widget().get_drop_target())
+ , m_rTreeView(rTreeView)
+{
+}
+
+sal_Int8 FmFilterNavigatorDropTarget::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, true);
+ }
+
+ return nAccept;
+}
+
+sal_Int8 FmFilterNavigatorDropTarget::ExecuteDrop(const ExecuteDropEvent& rEvt)
+{
+ return m_rTreeView.ExecuteDrop(rEvt);
+}
+
+FmFilterNavigator::FmFilterNavigator(vcl::Window* pTopLevel, std::unique_ptr<weld::TreeView> xTreeView)
+ : m_xTopLevel(pTopLevel)
+ , m_xTreeView(std::move(xTreeView))
+ , m_aDropTargetHelper(*this)
+ , m_nAsyncRemoveEvent(nullptr)
+{
+ m_xTreeView->set_help_id(HID_FILTER_NAVIGATOR);
+
+ m_xTreeView->set_selection_mode(SelectionMode::Multiple);
+
+ m_pModel.reset( new FmFilterModel() );
+ StartListening( *m_pModel );
+
+ m_xTreeView->connect_custom_get_size(LINK(this, FmFilterNavigator, CustomGetSizeHdl));
+ m_xTreeView->connect_custom_render(LINK(this, FmFilterNavigator, CustomRenderHdl));
+ m_xTreeView->set_column_custom_renderer(0, true);
+
+ m_xTreeView->connect_changed(LINK(this, FmFilterNavigator, SelectHdl));
+ m_xTreeView->connect_key_press(LINK(this, FmFilterNavigator, KeyInputHdl));
+ m_xTreeView->connect_popup_menu(LINK(this, FmFilterNavigator, PopupMenuHdl));
+ m_xTreeView->connect_editing(LINK(this, FmFilterNavigator, EditingEntryHdl),
+ LINK(this, FmFilterNavigator, EditedEntryHdl));
+ m_xTreeView->connect_drag_begin(LINK(this, FmFilterNavigator, DragBeginHdl));
+}
+
+FmFilterNavigator::~FmFilterNavigator()
+{
+ if (m_nAsyncRemoveEvent)
+ Application::RemoveUserEvent(m_nAsyncRemoveEvent);
+ EndListening(*m_pModel);
+ m_pModel.reset();
+}
+
+void FmFilterNavigator::UpdateContent(const Reference< XIndexAccess > & xControllers, const Reference< XFormController > & xCurrent)
+{
+ if (xCurrent == m_pModel->GetCurrentController())
+ return;
+
+ m_pModel->Update(xControllers, xCurrent);
+
+ // expand the filters for the current controller
+ std::unique_ptr<weld::TreeIter> xEntry = FindEntry(m_pModel->GetCurrentForm());
+ if (!xEntry || m_xTreeView->get_row_expanded(*xEntry))
+ return;
+
+ m_xTreeView->unselect_all();
+
+ m_xTreeView->expand_row(*xEntry);
+
+ xEntry = FindEntry(m_pModel->GetCurrentItems());
+ if (xEntry)
+ {
+ if (!m_xTreeView->get_row_expanded(*xEntry))
+ m_xTreeView->expand_row(*xEntry);
+ m_xTreeView->select(*xEntry);
+ SelectHdl(*m_xTreeView);
+ }
+}
+
+IMPL_LINK(FmFilterNavigator, EditingEntryHdl, const weld::TreeIter&, rIter, bool)
+{
+ // returns true to allow editing
+ if (dynamic_cast<const FmFilterItem*>(weld::fromId<FmFilterData*>(m_xTreeView->get_id(rIter))))
+ {
+ m_xEditingCurrently = m_xTreeView->make_iterator(&rIter);
+ return true;
+ }
+ m_xEditingCurrently.reset();
+ return false;
+}
+
+IMPL_LINK(FmFilterNavigator, EditedEntryHdl, const IterString&, rIterString, bool)
+{
+ const weld::TreeIter& rIter = rIterString.first;
+ const OUString& rNewText = rIterString.second;
+
+ assert(m_xEditingCurrently && m_xTreeView->iter_compare(rIter, *m_xEditingCurrently) == 0 &&
+ "FmFilterNavigator::EditedEntry: suspicious entry!");
+ m_xEditingCurrently.reset();
+
+ FmFilterData* pData = weld::fromId<FmFilterData*>(m_xTreeView->get_id(rIter));
+
+ DBG_ASSERT(dynamic_cast<const FmFilterItem*>(pData) != nullptr,
+ "FmFilterNavigator::EditedEntry() wrong entry");
+
+ OUString aText(comphelper::string::strip(rNewText, ' '));
+ if (aText.isEmpty())
+ {
+ // deleting the entry asynchron
+ m_nAsyncRemoveEvent = Application::PostUserEvent(LINK(this, FmFilterNavigator, OnRemove), pData);
+ }
+ else
+ {
+ OUString aErrorMsg;
+
+ if (m_pModel->ValidateText(static_cast<FmFilterItem*>(pData), aText, aErrorMsg))
+ {
+ // this will set the text at the FmFilterItem, as well as update any filter controls
+ // which are connected to this particular entry
+ m_pModel->SetTextForItem(static_cast<FmFilterItem*>(pData), aText);
+ m_xTreeView->set_text(rIter, aText);
+ }
+ else
+ {
+ // display the error and return sal_False
+ SQLContext aError;
+ aError.Message = SvxResId(RID_STR_SYNTAXERROR);
+ aError.Details = aErrorMsg;
+ displayException(aError, VCLUnoHelper::GetInterface(m_xTopLevel));
+
+ return false;
+ }
+ }
+ return true;
+}
+
+IMPL_LINK( FmFilterNavigator, OnRemove, void*, p, void )
+{
+ m_nAsyncRemoveEvent = nullptr;
+ // now remove the entry
+ m_pModel->Remove(static_cast<FmFilterData*>(p));
+}
+
+sal_Int8 FmFilterNavigator::AcceptDrop( const AcceptDropEvent& rEvt )
+{
+ if (!m_aControlExchange.isDragSource())
+ return DND_ACTION_NONE;
+
+ if (!OFilterItemExchange::hasFormat(m_aDropTargetHelper.GetDataFlavorExVector()))
+ return DND_ACTION_NONE;
+
+ // do we contain the formitem?
+ if (!FindEntry(m_aControlExchange->getFormItem()))
+ return DND_ACTION_NONE;
+
+ Point aDropPos = rEvt.maPosPixel;
+ std::unique_ptr<weld::TreeIter> xDropTarget(m_xTreeView->make_iterator());
+ // get_dest_row_at_pos with false cause we must drop exactly "on" a node to paste a condition into it
+ if (!m_xTreeView->get_dest_row_at_pos(aDropPos, xDropTarget.get(), false))
+ xDropTarget.reset();
+
+ if (!xDropTarget)
+ return DND_ACTION_NONE;
+
+ FmFilterData* pData = weld::fromId<FmFilterData*>(m_xTreeView->get_id(*xDropTarget));
+ FmFormItem* pForm = nullptr;
+ if (dynamic_cast<const FmFilterItem*>(pData) != nullptr)
+ {
+ pForm = dynamic_cast<FmFormItem*>( pData->GetParent()->GetParent() );
+ if (pForm != m_aControlExchange->getFormItem())
+ return DND_ACTION_NONE;
+ }
+ else if (dynamic_cast<const FmFilterItems*>( pData) != nullptr)
+ {
+ pForm = dynamic_cast<FmFormItem*>( pData->GetParent() );
+ if (pForm != m_aControlExchange->getFormItem())
+ return DND_ACTION_NONE;
+ }
+ else
+ return DND_ACTION_NONE;
+
+ return rEvt.mnAction;
+}
+
+namespace
+{
+ FmFilterItems* getTargetItems(const weld::TreeView& rTreeView, const weld::TreeIter& rTarget)
+ {
+ FmFilterData* pData = weld::fromId<FmFilterData*>(rTreeView.get_id(rTarget));
+ FmFilterItems* pTargetItems = dynamic_cast<FmFilterItems*>(pData);
+ if (!pTargetItems)
+ pTargetItems = dynamic_cast<FmFilterItems*>(pData->GetParent());
+ return pTargetItems;
+ }
+}
+
+sal_Int8 FmFilterNavigator::ExecuteDrop( const ExecuteDropEvent& rEvt )
+{
+ if (!m_aControlExchange.isDragSource())
+ return DND_ACTION_NONE;
+
+ Point aDropPos = rEvt.maPosPixel;
+ std::unique_ptr<weld::TreeIter> xDropTarget(m_xTreeView->make_iterator());
+ // get_dest_row_at_pos with false cause we must drop exactly "on" a node to paste a condition into it
+ if (!m_xTreeView->get_dest_row_at_pos(aDropPos, xDropTarget.get(), false))
+ xDropTarget.reset();
+ if (!xDropTarget)
+ return DND_ACTION_NONE;
+
+ // search the container where to add the items
+ FmFilterItems* pTargetItems = getTargetItems(*m_xTreeView, *xDropTarget);
+ m_xTreeView->unselect_all();
+ std::unique_ptr<weld::TreeIter> xEntry = FindEntry(pTargetItems);
+ if (xEntry)
+ {
+ m_xTreeView->select(*xEntry);
+ m_xTreeView->set_cursor(*xEntry);
+ }
+
+ insertFilterItem(m_aControlExchange->getDraggedEntries(),pTargetItems,DND_ACTION_COPY == rEvt.mnAction);
+
+ return DND_ACTION_COPY;
+}
+
+IMPL_LINK_NOARG(FmFilterNavigator, SelectHdl, weld::TreeView&, void)
+{
+ std::unique_ptr<weld::TreeIter> xIter(m_xTreeView->make_iterator());
+ if (!m_xTreeView->get_selected(xIter.get()))
+ return;
+
+ FmFilterData* pData = weld::fromId<FmFilterData*>(m_xTreeView->get_id(*xIter));
+
+ FmFormItem* pFormItem = nullptr;
+ if (FmFilterItem* pItem = dynamic_cast<FmFilterItem*>(pData))
+ pFormItem = static_cast<FmFormItem*>(pItem->GetParent()->GetParent());
+ else if (FmFilterItems* pItems = dynamic_cast<FmFilterItems*>(pData))
+ pFormItem = static_cast<FmFormItem*>(pItems->GetParent()->GetParent());
+ else
+ pFormItem = dynamic_cast<FmFormItem*>(pData);
+
+ if (pFormItem)
+ {
+ // will the controller be exchanged?
+ if (FmFilterItem* pItem = dynamic_cast<FmFilterItem*>(pData))
+ m_pModel->SetCurrentItems(static_cast<FmFilterItems*>(pItem->GetParent()));
+ else if (FmFilterItems* pItems = dynamic_cast<FmFilterItems*>(pData))
+ m_pModel->SetCurrentItems(pItems);
+ else
+ m_pModel->SetCurrentController(pFormItem->GetController());
+ }
+}
+
+void FmFilterNavigator::Notify( SfxBroadcaster& /*rBC*/, const SfxHint& rHint )
+{
+ if (const FmFilterInsertedHint* pInsertHint = dynamic_cast<const FmFilterInsertedHint*>(&rHint))
+ {
+ Insert(pInsertHint->GetData(), pInsertHint->GetPos());
+ }
+ else if( dynamic_cast<const FilterClearingHint*>(&rHint) )
+ {
+ m_xTreeView->clear();
+ }
+ else if (const FmFilterRemovedHint* pRemoveHint = dynamic_cast<const FmFilterRemovedHint*>(&rHint))
+ {
+ Remove(pRemoveHint->GetData());
+ }
+ else if (const FmFilterTextChangedHint *pChangeHint = dynamic_cast<const FmFilterTextChangedHint*>(&rHint))
+ {
+ std::unique_ptr<weld::TreeIter> xEntry = FindEntry(pChangeHint->GetData());
+ if (xEntry)
+ m_xTreeView->set_text(*xEntry, pChangeHint->GetData()->GetText());
+ }
+ else if( dynamic_cast<const FmFilterCurrentChangedHint*>(&rHint) )
+ {
+ m_xTreeView->queue_draw();
+ }
+}
+
+std::unique_ptr<weld::TreeIter> FmFilterNavigator::FindEntry(const FmFilterData* pItem) const
+{
+ if (!pItem)
+ return nullptr;
+ std::unique_ptr<weld::TreeIter> xEntry = m_xTreeView->make_iterator();
+ if (!m_xTreeView->get_iter_first(*xEntry))
+ return nullptr;
+ do
+ {
+ FmFilterData* pEntryItem = weld::fromId<FmFilterData*>(m_xTreeView->get_id(*xEntry));
+ if (pEntryItem == pItem)
+ return xEntry;
+ }
+ while (m_xTreeView->iter_next(*xEntry));
+
+ return nullptr;
+}
+
+void FmFilterNavigator::Insert(const FmFilterData* pItem, int nPos)
+{
+ const FmParentData* pParent = pItem->GetParent() ? pItem->GetParent() : m_pModel.get();
+
+ // insert the item
+ std::unique_ptr<weld::TreeIter> xParentEntry = FindEntry(pParent);
+
+ OUString sId(weld::toId(pItem));
+ std::unique_ptr<weld::TreeIter> xRet(m_xTreeView->make_iterator());
+ m_xTreeView->insert(xParentEntry.get(), nPos, &pItem->GetText(), &sId,
+ nullptr, nullptr, false, xRet.get());
+ m_xTreeView->set_image(*xRet, pItem->GetImage());
+
+ if (!xParentEntry)
+ return;
+ m_xTreeView->expand_row(*xParentEntry);
+}
+
+void FmFilterNavigator::EndEditing()
+{
+ if (m_xEditingCurrently)
+ {
+ // end editing
+ m_xTreeView->end_editing();
+ m_xEditingCurrently.reset();
+ }
+}
+
+void FmFilterNavigator::Remove(FmFilterData const * pItem)
+{
+ // the entry for the data
+ std::unique_ptr<weld::TreeIter> xEntry = FindEntry(pItem);
+ if (!xEntry)
+ return;
+
+ if (m_xEditingCurrently && m_xTreeView->iter_compare(*xEntry, *m_xEditingCurrently) == 0)
+ EndEditing();
+
+ m_xTreeView->remove(*xEntry);
+}
+
+FmFormItem* FmFilterNavigator::getSelectedFilterItems(::std::vector<FmFilterItem*>& _rItemList)
+{
+ // be sure that the data is only used within only one form!
+ FmFormItem* pFirstItem = nullptr;
+
+ bool bHandled = true;
+ bool bFoundSomething = false;
+
+ m_xTreeView->selected_foreach([this, &bHandled, &bFoundSomething, &pFirstItem, &_rItemList](weld::TreeIter& rEntry) {
+ FmFilterData* pFilterEntry = weld::fromId<FmFilterData*>(m_xTreeView->get_id(rEntry));
+ FmFilterItem* pFilter = dynamic_cast<FmFilterItem*>(pFilterEntry);
+ if (pFilter)
+ {
+ FmFormItem* pForm = dynamic_cast<FmFormItem*>( pFilter->GetParent()->GetParent() );
+ if (!pForm)
+ bHandled = false;
+ else if (!pFirstItem)
+ pFirstItem = pForm;
+ else if (pFirstItem != pForm)
+ bHandled = false;
+
+ if (bHandled)
+ {
+ _rItemList.push_back(pFilter);
+ bFoundSomething = true;
+ }
+ }
+ return !bHandled;
+ });
+
+ if ( !bHandled || !bFoundSomething )
+ pFirstItem = nullptr;
+ return pFirstItem;
+}
+
+void FmFilterNavigator::insertFilterItem(const ::std::vector<FmFilterItem*>& _rFilterList,FmFilterItems* _pTargetItems,bool _bCopy)
+{
+ for (FmFilterItem* pLookupItem : _rFilterList)
+ {
+ if ( pLookupItem->GetParent() == _pTargetItems )
+ continue;
+
+ FmFilterItem* pFilterItem = _pTargetItems->Find( pLookupItem->GetComponentIndex() );
+ OUString aText = pLookupItem->GetText();
+ if ( !pFilterItem )
+ {
+ pFilterItem = new FmFilterItem( _pTargetItems, pLookupItem->GetFieldName(), aText, pLookupItem->GetComponentIndex() );
+ m_pModel->Append( _pTargetItems, std::unique_ptr<FmFilterItem>(pFilterItem) );
+ }
+
+ if ( !_bCopy )
+ m_pModel->Remove( pLookupItem );
+
+ // now set the text for the new dragged item
+ m_pModel->SetTextForItem( pFilterItem, aText );
+ }
+
+ m_pModel->EnsureEmptyFilterRows( *_pTargetItems->GetParent() );
+}
+
+IMPL_LINK(FmFilterNavigator, DragBeginHdl, bool&, rUnsetDragIcon, bool)
+{
+ rUnsetDragIcon = false;
+
+ // be sure that the data is only used within an only one form!
+ m_aControlExchange.prepareDrag();
+
+ ::std::vector<FmFilterItem*> aItemList;
+ if (FmFormItem* pFirstItem = getSelectedFilterItems(aItemList))
+ {
+ m_aControlExchange->setDraggedEntries(std::move(aItemList));
+ m_aControlExchange->setFormItem(pFirstItem);
+
+ OFilterItemExchange& rExchange = *m_aControlExchange;
+ rtl::Reference<TransferDataContainer> xHelper(&rExchange);
+ m_xTreeView->enable_drag_source(xHelper, DND_ACTION_COPYMOVE);
+ rExchange.setDragging(true);
+
+ return false;
+ }
+ return true;
+}
+
+IMPL_LINK(FmFilterNavigator, PopupMenuHdl, const CommandEvent&, rEvt, bool)
+{
+ bool bHandled = false;
+ switch (rEvt.GetCommand())
+ {
+ case CommandEventId::ContextMenu:
+ {
+ // the place where it was clicked
+ Point aWhere;
+ std::unique_ptr<weld::TreeIter> xClicked(m_xTreeView->make_iterator());
+ if (rEvt.IsMouseEvent())
+ {
+ aWhere = rEvt.GetMousePosPixel();
+ if (!m_xTreeView->get_dest_row_at_pos(aWhere, xClicked.get(), false))
+ break;
+
+ if (!m_xTreeView->is_selected(*xClicked))
+ {
+ m_xTreeView->unselect_all();
+ m_xTreeView->select(*xClicked);
+ m_xTreeView->set_cursor(*xClicked);
+ }
+ }
+ else
+ {
+ if (!m_xTreeView->get_cursor(xClicked.get()))
+ break;
+ aWhere = m_xTreeView->get_row_area(*xClicked).Center();
+ }
+
+ ::std::vector<FmFilterData*> aSelectList;
+ m_xTreeView->selected_foreach([this, &aSelectList](weld::TreeIter& rEntry) {
+ FmFilterData* pFilterEntry = weld::fromId<FmFilterData*>(m_xTreeView->get_id(rEntry));
+
+ // don't delete forms
+ FmFormItem* pForm = dynamic_cast<FmFormItem*>(pFilterEntry);
+ if (!pForm)
+ aSelectList.push_back(pFilterEntry);
+
+ return false;
+ });
+
+ if (aSelectList.size() == 1)
+ {
+ // don't delete the only empty row of a form
+ FmFilterItems* pFilterItems = dynamic_cast<FmFilterItems*>( aSelectList[0] );
+ if (pFilterItems && pFilterItems->GetChildren().empty()
+ && pFilterItems->GetParent()->GetChildren().size() == 1)
+ aSelectList.clear();
+ }
+
+ std::unique_ptr<weld::Builder> xBuilder(Application::CreateBuilder(m_xTreeView.get(), "svx/ui/filtermenu.ui"));
+ std::unique_ptr<weld::Menu> xContextMenu(xBuilder->weld_menu("menu"));
+
+ // every condition could be deleted except the first one if it's the only one
+ bool bNoDelete = false;
+ if (aSelectList.empty())
+ {
+ bNoDelete = true;
+ xContextMenu->remove("delete");
+ }
+
+ FmFilterData* pFilterEntry = weld::fromId<FmFilterData*>(m_xTreeView->get_id(*xClicked));
+ auto pFilterItem = dynamic_cast<FmFilterItem*>(pFilterEntry);
+ bool bEdit = pFilterItem &&
+ m_xTreeView->is_selected(*xClicked) && m_xTreeView->count_selected_rows() == 1;
+
+ if (bNoDelete && !bEdit)
+ {
+ // nothing is in the menu, don't bother
+ return true;
+ }
+
+ if (!bEdit)
+ {
+ xContextMenu->remove("edit");
+ xContextMenu->remove("isnull");
+ xContextMenu->remove("isnotnull");
+ }
+
+ OString sIdent = xContextMenu->popup_at_rect(m_xTreeView.get(), tools::Rectangle(aWhere, ::Size(1, 1)));
+ if (sIdent == "edit")
+ {
+ m_xTreeView->start_editing(*xClicked);
+ }
+ else if (sIdent == "isnull")
+ {
+ OUString aErrorMsg;
+ OUString aText = "IS NULL";
+ assert(pFilterItem && "if item is null this menu entry was removed and unavailable");
+ m_pModel->ValidateText(pFilterItem, aText, aErrorMsg);
+ m_pModel->SetTextForItem(pFilterItem, aText);
+ }
+ else if (sIdent == "isnotnull")
+ {
+ OUString aErrorMsg;
+ OUString aText = "IS NOT NULL";
+
+ assert(pFilterItem && "if item is null this menu entry was removed and unavailable");
+ m_pModel->ValidateText(pFilterItem, aText, aErrorMsg);
+ m_pModel->SetTextForItem(pFilterItem, aText);
+ }
+ else if (sIdent == "delete")
+ {
+ DeleteSelection();
+ }
+ bHandled = true;
+ }
+ break;
+ default: break;
+ }
+
+ return bHandled;
+}
+
+bool FmFilterNavigator::getNextEntry(weld::TreeIter& rEntry)
+{
+ bool bEntry = m_xTreeView->iter_next(rEntry);
+ // we need the next filter entry
+ if (bEntry)
+ {
+ while (!m_xTreeView->iter_has_child(rEntry))
+ {
+ std::unique_ptr<weld::TreeIter> xNext = m_xTreeView->make_iterator(&rEntry);
+ if (!m_xTreeView->iter_next(*xNext))
+ break;
+ m_xTreeView->copy_iterator(*xNext, rEntry);
+ }
+ }
+ return bEntry;
+}
+
+bool FmFilterNavigator::getPrevEntry(weld::TreeIter& rEntry)
+{
+ bool bEntry = m_xTreeView->iter_previous(rEntry);
+ // check if the previous entry is a filter, if so get the next prev
+ if (bEntry && m_xTreeView->iter_has_child(rEntry))
+ {
+ bEntry = m_xTreeView->iter_previous(rEntry);
+ // if the entry is still no leaf return
+ if (bEntry && m_xTreeView->iter_has_child(rEntry))
+ bEntry = false;
+ }
+ return bEntry;
+}
+IMPL_LINK(FmFilterNavigator, KeyInputHdl, const ::KeyEvent&, rKEvt, bool)
+{
+ const vcl::KeyCode& rKeyCode = rKEvt.GetKeyCode();
+
+ switch ( rKeyCode.GetCode() )
+ {
+ case KEY_UP:
+ case KEY_DOWN:
+ {
+ if ( !rKeyCode.IsMod1() || !rKeyCode.IsMod2() || rKeyCode.IsShift() )
+ break;
+
+ ::std::vector<FmFilterItem*> aItemList;
+ if ( !getSelectedFilterItems( aItemList ) )
+ break;
+
+
+ std::vector<std::unique_ptr<weld::TreeIter>> aSelected;
+ m_xTreeView->selected_foreach([this, &aSelected](weld::TreeIter& rEntry){
+ aSelected.emplace_back(m_xTreeView->make_iterator(&rEntry));
+ return false;
+ });
+
+ std::unique_ptr<weld::TreeIter> xTarget;
+ ::std::function<bool(FmFilterNavigator*, weld::TreeIter&)> getter;
+
+ if (rKeyCode.GetCode() == KEY_UP)
+ {
+ xTarget = m_xTreeView->make_iterator(aSelected.front().get());
+ getter = ::std::mem_fn(&FmFilterNavigator::getPrevEntry);
+ }
+ else
+ {
+ xTarget = m_xTreeView->make_iterator(aSelected.back().get());
+ getter = ::std::mem_fn(&FmFilterNavigator::getNextEntry);
+ }
+
+ bool bTarget = getter(this, *xTarget);
+ if (!bTarget)
+ break;
+
+ FmFilterItems* pTargetItems = getTargetItems(*m_xTreeView, *xTarget);
+ if (!pTargetItems)
+ break;
+
+ ::std::vector<FmFilterItem*>::const_iterator aEnd = aItemList.end();
+ bool bNextTargetItem = true;
+ while ( bNextTargetItem )
+ {
+ ::std::vector<FmFilterItem*>::const_iterator i = aItemList.begin();
+ for (; i != aEnd; ++i)
+ {
+ if ( (*i)->GetParent() == pTargetItems )
+ {
+ bTarget = getter(this, *xTarget);
+ if (!bTarget)
+ return true;
+ pTargetItems = getTargetItems(*m_xTreeView, *xTarget);
+ break;
+ }
+ else
+ {
+ FmFilterItem* pFilterItem = pTargetItems->Find( (*i)->GetComponentIndex() );
+ // we found the text component so jump above
+ if ( pFilterItem )
+ {
+ bTarget = getter(this, *xTarget);
+ if (!bTarget)
+ return true;
+
+ pTargetItems = getTargetItems(*m_xTreeView, *xTarget);
+ break;
+ }
+ }
+ }
+ bNextTargetItem = i != aEnd && pTargetItems;
+ }
+
+ if ( pTargetItems )
+ {
+ insertFilterItem( aItemList, pTargetItems, false );
+ return true;
+ }
+ }
+ break;
+
+ case KEY_DELETE:
+ {
+ if ( rKeyCode.GetModifier() )
+ break;
+
+ std::unique_ptr<weld::TreeIter> xEntry = m_xTreeView->make_iterator();
+ if (m_xTreeView->get_iter_first(*xEntry) && !m_xTreeView->is_selected(*xEntry))
+ DeleteSelection();
+
+ return true;
+ }
+ }
+
+ return false;
+}
+
+void FmFilterNavigator::DeleteSelection()
+{
+ // to avoid the deletion of an entry twice (e.g. deletion of a parent and afterward
+ // the deletion of its child, I have to shrink the selection list
+ std::vector<FmFilterData*> aEntryList;
+
+ m_xTreeView->selected_foreach([this, &aEntryList](weld::TreeIter& rEntry) {
+ FmFilterData* pFilterEntry = weld::fromId<FmFilterData*>(m_xTreeView->get_id(rEntry));
+
+ if (dynamic_cast<FmFilterItem*>(pFilterEntry))
+ {
+ std::unique_ptr<weld::TreeIter> xParent(m_xTreeView->make_iterator(&rEntry));
+ if (m_xTreeView->iter_parent(*xParent) && m_xTreeView->is_selected(*xParent))
+ return false;
+ }
+
+ FmFormItem* pForm = dynamic_cast<FmFormItem*>(pFilterEntry);
+ if (!pForm)
+ aEntryList.emplace_back(pFilterEntry);
+
+ return false;
+ });
+
+ // Remove the selection
+ m_xTreeView->unselect_all();
+
+ for (auto i = aEntryList.rbegin(); i != aEntryList.rend(); ++i)
+ m_pModel->Remove(*i);
+}
+
+FmFilterNavigatorWin::FmFilterNavigatorWin(SfxBindings* _pBindings, SfxChildWindow* _pMgr,
+ vcl::Window* _pParent)
+ : SfxDockingWindow(_pBindings, _pMgr, _pParent, "FilterNavigator", "svx/ui/filternavigator.ui")
+ , SfxControllerItem( SID_FM_FILTER_NAVIGATOR_CONTROL, *_pBindings )
+ , m_xNavigatorTree(new FmFilterNavigator(this, m_xBuilder->weld_tree_view("treeview")))
+{
+ SetHelpId( HID_FILTER_NAVIGATOR_WIN );
+
+ SetText( SvxResId(RID_STR_FILTER_NAVIGATOR) );
+ SfxDockingWindow::SetFloatingSize( Size(200,200) );
+}
+
+FmFilterNavigatorWin::~FmFilterNavigatorWin()
+{
+ disposeOnce();
+}
+
+void FmFilterNavigatorWin::dispose()
+{
+ m_xNavigatorTree.reset();
+ ::SfxControllerItem::dispose();
+ SfxDockingWindow::dispose();
+}
+
+void FmFilterNavigatorWin::UpdateContent(FmFormShell const * pFormShell)
+{
+ if (!m_xNavigatorTree)
+ return;
+
+ if (!pFormShell)
+ m_xNavigatorTree->UpdateContent( nullptr, nullptr );
+ else
+ {
+ Reference<XFormController> const xController(pFormShell->GetImpl()->getActiveInternalController_Lock());
+ Reference< XIndexAccess > xContainer;
+ if (xController.is())
+ {
+ Reference< XChild > xChild = xController;
+ for (Reference< XInterface > xParent(xChild->getParent());
+ xParent.is();
+ xParent = xChild.is() ? xChild->getParent() : Reference< XInterface > ())
+ {
+ xContainer.set(xParent, UNO_QUERY);
+ xChild.set(xParent, UNO_QUERY);
+ }
+ }
+ m_xNavigatorTree->UpdateContent(xContainer, xController);
+ }
+}
+
+void FmFilterNavigatorWin::StateChangedAtToolBoxControl( sal_uInt16 nSID, SfxItemState eState, const SfxPoolItem* pState )
+{
+ if( !pState || SID_FM_FILTER_NAVIGATOR_CONTROL != nSID )
+ return;
+
+ if( eState >= SfxItemState::DEFAULT )
+ {
+ FmFormShell* pShell = dynamic_cast<FmFormShell*>( static_cast<const SfxObjectItem*>(pState)->GetShell() );
+ UpdateContent( pShell );
+ }
+ else
+ UpdateContent( nullptr );
+}
+
+bool FmFilterNavigatorWin::Close()
+{
+ if (m_xNavigatorTree)
+ m_xNavigatorTree->EndEditing();
+
+ UpdateContent( nullptr );
+ return SfxDockingWindow::Close();
+}
+
+void FmFilterNavigatorWin::FillInfo( SfxChildWinInfo& rInfo ) const
+{
+ SfxDockingWindow::FillInfo( rInfo );
+ rInfo.bVisible = false;
+}
+
+Size FmFilterNavigatorWin::CalcDockingSize( SfxChildAlignment eAlign )
+{
+ if ( ( eAlign == SfxChildAlignment::TOP ) || ( eAlign == SfxChildAlignment::BOTTOM ) )
+ return Size();
+
+ return SfxDockingWindow::CalcDockingSize( eAlign );
+}
+
+SfxChildAlignment FmFilterNavigatorWin::CheckAlignment( SfxChildAlignment eActAlign, SfxChildAlignment eAlign )
+{
+ switch (eAlign)
+ {
+ case SfxChildAlignment::LEFT:
+ case SfxChildAlignment::RIGHT:
+ case SfxChildAlignment::NOALIGNMENT:
+ return eAlign;
+ default:
+ break;
+ }
+
+ return eActAlign;
+}
+
+void FmFilterNavigatorWin::GetFocus()
+{
+ // oj #97405#
+ if (m_xNavigatorTree)
+ m_xNavigatorTree->GrabFocus();
+ else
+ SfxDockingWindow::GetFocus();
+}
+
+SFX_IMPL_DOCKINGWINDOW( FmFilterNavigatorWinMgr, SID_FM_FILTER_NAVIGATOR )
+
+
+FmFilterNavigatorWinMgr::FmFilterNavigatorWinMgr( vcl::Window *_pParent, sal_uInt16 _nId,
+ SfxBindings *_pBindings, SfxChildWinInfo* _pInfo )
+ :SfxChildWindow( _pParent, _nId )
+{
+ SetWindow( VclPtr<FmFilterNavigatorWin>::Create( _pBindings, this, _pParent ) );
+ static_cast<SfxDockingWindow*>(GetWindow())->Initialize( _pInfo );
+}
+
+
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/form/fmPropBrw.cxx b/svx/source/form/fmPropBrw.cxx
new file mode 100644
index 000000000..1b24f9703
--- /dev/null
+++ b/svx/source/form/fmPropBrw.cxx
@@ -0,0 +1,581 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+
+#include <sal/macros.h>
+
+#include <fmprop.hxx>
+#include <fmPropBrw.hxx>
+#include <svx/strings.hrc>
+#include <fmservs.hxx>
+#include <fmshimp.hxx>
+#include <fmpgeimp.hxx>
+
+#include <svx/dialmgr.hxx>
+#include <svx/fmpage.hxx>
+#include <svx/fmshell.hxx>
+#include <svx/fmview.hxx>
+#include <svx/sdrpagewindow.hxx>
+#include <svx/svdpagv.hxx>
+#include <svx/svxids.hrc>
+
+#include <com/sun/star/awt/XControlContainer.hpp>
+#include <com/sun/star/beans/XPropertySet.hpp>
+#include <com/sun/star/form/XForm.hpp>
+#include <com/sun/star/form/FormComponentType.hpp>
+#include <com/sun/star/form/inspection/DefaultFormComponentInspectorModel.hpp>
+#include <com/sun/star/frame/Frame.hpp>
+#include <com/sun/star/inspection/ObjectInspector.hpp>
+#include <com/sun/star/inspection/XObjectInspectorUI.hpp>
+#include <com/sun/star/inspection/DefaultHelpProvider.hpp>
+#include <com/sun/star/lang/XServiceInfo.hpp>
+#include <com/sun/star/util/VetoException.hpp>
+
+#include <comphelper/processfactory.hxx>
+#include <comphelper/property.hxx>
+#include <comphelper/sequence.hxx>
+#include <comphelper/types.hxx>
+#include <cppuhelper/component_context.hxx>
+#include <o3tl/deleter.hxx>
+#include <sfx2/bindings.hxx>
+#include <sfx2/childwin.hxx>
+#include <sfx2/objitem.hxx>
+#include <sfx2/objsh.hxx>
+#include <tools/diagnose_ex.h>
+#include <unotools/confignode.hxx>
+#include <vcl/stdtext.hxx>
+#include <vcl/svapp.hxx>
+#include <vcl/weld.hxx>
+#include <vcl/weldutils.hxx>
+
+using namespace ::com::sun::star;
+using namespace ::com::sun::star::beans;
+using namespace ::com::sun::star::container;
+using namespace ::com::sun::star::form;
+using namespace ::com::sun::star::form::inspection;
+using namespace ::com::sun::star::frame;
+using namespace ::com::sun::star::inspection;
+using namespace ::com::sun::star::lang;
+using namespace ::com::sun::star::uno;
+using namespace ::com::sun::star::util;
+using namespace ::svxform;
+using ::com::sun::star::awt::XWindow;
+
+//= FmPropBrwMgr
+SFX_IMPL_MODELESSDIALOGCONTOLLER(FmPropBrwMgr, SID_FM_SHOW_PROPERTIES)
+
+FmPropBrwMgr::FmPropBrwMgr( vcl::Window* _pParent, sal_uInt16 _nId,
+ SfxBindings* _pBindings, const SfxChildWinInfo* _pInfo)
+ :SfxChildWindow(_pParent, _nId)
+{
+ std::shared_ptr<FmPropBrw> xControl(new FmPropBrw(::comphelper::getProcessComponentContext(), _pBindings,
+ this, _pParent->GetFrameWeld(), _pInfo), o3tl::default_delete<FmPropBrw>());
+ SetController(std::move(xControl));
+ static_cast<FmPropBrw*>(GetController().get())->Initialize( _pInfo );
+}
+
+static OUString GetUIHeadlineName(sal_Int16 nClassId, const Any& aUnoObj)
+{
+ TranslateId pClassNameResourceId;
+
+ switch ( nClassId )
+ {
+ case FormComponentType::TEXTFIELD:
+ {
+ Reference< XInterface > xIFace;
+ aUnoObj >>= xIFace;
+ pClassNameResourceId = RID_STR_PROPTITLE_EDIT;
+ if (xIFace.is())
+ { // we have a chance to check if it's a formatted field model
+ Reference< XServiceInfo > xInfo(xIFace, UNO_QUERY);
+ if (xInfo.is() && (xInfo->supportsService(FM_SUN_COMPONENT_FORMATTEDFIELD)))
+ pClassNameResourceId = RID_STR_PROPTITLE_FORMATTED;
+ else if (!xInfo.is())
+ {
+ // couldn't distinguish between formatted and edit with the service name, so try with the properties
+ Reference< XPropertySet > xProps(xIFace, UNO_QUERY);
+ if (xProps.is())
+ {
+ Reference< XPropertySetInfo > xPropsInfo = xProps->getPropertySetInfo();
+ if (xPropsInfo.is() && xPropsInfo->hasPropertyByName(FM_PROP_FORMATSSUPPLIER))
+ pClassNameResourceId = RID_STR_PROPTITLE_FORMATTED;
+ }
+ }
+ }
+ }
+ break;
+
+ case FormComponentType::COMMANDBUTTON:
+ pClassNameResourceId = RID_STR_PROPTITLE_PUSHBUTTON; break;
+ case FormComponentType::RADIOBUTTON:
+ pClassNameResourceId = RID_STR_PROPTITLE_RADIOBUTTON; break;
+ case FormComponentType::CHECKBOX:
+ pClassNameResourceId = RID_STR_PROPTITLE_CHECKBOX; break;
+ case FormComponentType::LISTBOX:
+ pClassNameResourceId = RID_STR_PROPTITLE_LISTBOX; break;
+ case FormComponentType::COMBOBOX:
+ pClassNameResourceId = RID_STR_PROPTITLE_COMBOBOX; break;
+ case FormComponentType::GROUPBOX:
+ pClassNameResourceId = RID_STR_PROPTITLE_GROUPBOX; break;
+ case FormComponentType::IMAGEBUTTON:
+ pClassNameResourceId = RID_STR_PROPTITLE_IMAGEBUTTON; break;
+ case FormComponentType::FIXEDTEXT:
+ pClassNameResourceId = RID_STR_PROPTITLE_FIXEDTEXT; break;
+ case FormComponentType::GRIDCONTROL:
+ pClassNameResourceId = RID_STR_PROPTITLE_DBGRID; break;
+ case FormComponentType::FILECONTROL:
+ pClassNameResourceId = RID_STR_PROPTITLE_FILECONTROL; break;
+ case FormComponentType::DATEFIELD:
+ pClassNameResourceId = RID_STR_PROPTITLE_DATEFIELD; break;
+ case FormComponentType::TIMEFIELD:
+ pClassNameResourceId = RID_STR_PROPTITLE_TIMEFIELD; break;
+ case FormComponentType::NUMERICFIELD:
+ pClassNameResourceId = RID_STR_PROPTITLE_NUMERICFIELD; break;
+ case FormComponentType::CURRENCYFIELD:
+ pClassNameResourceId = RID_STR_PROPTITLE_CURRENCYFIELD; break;
+ case FormComponentType::PATTERNFIELD:
+ pClassNameResourceId = RID_STR_PROPTITLE_PATTERNFIELD; break;
+ case FormComponentType::IMAGECONTROL:
+ pClassNameResourceId = RID_STR_PROPTITLE_IMAGECONTROL; break;
+ case FormComponentType::HIDDENCONTROL:
+ pClassNameResourceId = RID_STR_PROPTITLE_HIDDEN; break;
+ case FormComponentType::SCROLLBAR:
+ pClassNameResourceId = RID_STR_PROPTITLE_SCROLLBAR; break;
+ case FormComponentType::SPINBUTTON:
+ pClassNameResourceId = RID_STR_PROPTITLE_SPINBUTTON; break;
+ case FormComponentType::NAVIGATIONBAR:
+ pClassNameResourceId = RID_STR_PROPTITLE_NAVBAR; break;
+ case FormComponentType::CONTROL:
+ default:
+ pClassNameResourceId = RID_STR_CONTROL; break;
+ }
+
+ return SvxResId(pClassNameResourceId);
+}
+
+FmPropBrw::FmPropBrw(const Reference< XComponentContext >& _xORB, SfxBindings* _pBindings,
+ SfxChildWindow* _pMgr, weld::Window* _pParent, const SfxChildWinInfo* _pInfo)
+ : SfxModelessDialogController(_pBindings, _pMgr, _pParent, "svx/ui/formpropertydialog.ui", "FormPropertyDialog")
+ , SfxControllerItem(SID_FM_PROPERTY_CONTROL, *_pBindings)
+ , m_bInitialStateChange(true)
+ , m_pParent(_pParent)
+ , m_nAsyncGetFocusId(nullptr)
+ , m_xContainer(m_xBuilder->weld_container("container"))
+ , m_xORB(_xORB)
+{
+ m_xContainer->set_size_request(m_xContainer->get_approximate_digit_width() * 72, m_xContainer->get_text_height() * 20);
+
+ try
+ {
+ // create a frame wrapper for myself
+ m_xMeAsFrame = Frame::create(m_xORB);
+
+ // transport the container area of this dialog to be the container window of the frame
+ css::uno::Reference<css::awt::XWindow> xFrameContainerWindow(new weld::TransportAsXWindow(m_xContainer.get()));
+ m_xMeAsFrame->initialize(xFrameContainerWindow);
+ m_xMeAsFrame->setName("form property browser");
+ }
+ catch (const Exception&)
+ {
+ OSL_FAIL("FmPropBrw::FmPropBrw: could not create/initialize my frame!");
+ m_xMeAsFrame.clear();
+ }
+
+ if ( _pInfo )
+ m_sLastActivePage = _pInfo->aExtraString;
+}
+
+FmPropBrw::~FmPropBrw()
+{
+ if (m_nAsyncGetFocusId)
+ {
+ Application::RemoveUserEvent(m_nAsyncGetFocusId);
+ m_nAsyncGetFocusId = nullptr;
+ }
+
+ if (m_xBrowserController.is())
+ implDetachController();
+ try
+ {
+ // remove our own properties from the component context. We cannot ensure that the component context
+ // is freed (there might be refcount problems :-\), so at least ensure the context itself
+ // does hold the objects anymore
+ Reference<XNameContainer> xName(m_xInspectorContext,uno::UNO_QUERY);
+ if ( xName.is() )
+ {
+ const OUString pProps[] = { OUString( "ContextDocument" )
+ , OUString( "DialogParentWindow" )
+ , OUString( "ControlContext" )
+ , OUString( "ControlShapeAccess" ) };
+ for (const auto & i : pProps)
+ xName->removeByName( i );
+ }
+ }
+ catch (const Exception& )
+ {
+ DBG_UNHANDLED_EXCEPTION("svx");
+ }
+ ::SfxControllerItem::dispose();
+}
+
+OUString FmPropBrw::getCurrentPage() const
+{
+ OUString sCurrentPage;
+ try
+ {
+ if ( m_xBrowserController.is() )
+ {
+ OSL_VERIFY( m_xBrowserController->getViewData() >>= sCurrentPage );
+ }
+
+ if ( sCurrentPage.isEmpty() )
+ sCurrentPage = m_sLastActivePage;
+ }
+ catch( const Exception& )
+ {
+ TOOLS_WARN_EXCEPTION("svx.form", "caught an exception while retrieving the current page!");
+ }
+ return sCurrentPage;
+}
+
+void FmPropBrw::implDetachController()
+{
+ m_sLastActivePage = getCurrentPage();
+
+ implSetNewSelection( InterfaceBag() );
+
+ if ( m_xMeAsFrame.is() )
+ {
+ try
+ {
+ m_xMeAsFrame->setComponent(nullptr, nullptr);
+ }
+ catch( const Exception& )
+ {
+ TOOLS_WARN_EXCEPTION("svx.form", "caught an exception while resetting the component!");
+ }
+ }
+
+ // we attached a frame to the controller manually, so we need to manually tell it that it's detached, too
+ if ( m_xBrowserController.is() )
+ {
+ m_xBrowserController->attachFrame( nullptr );
+ }
+
+ m_xBrowserController.clear();
+ m_xInspectorModel.clear();
+ m_xMeAsFrame.clear();
+}
+
+void FmPropBrw::Close()
+{
+ // suspend the controller (it is allowed to veto)
+ if ( m_xMeAsFrame.is() )
+ {
+ try
+ {
+ Reference< XController > xController( m_xMeAsFrame->getController() );
+ if ( xController.is() && !xController->suspend( true ) )
+ return;
+ }
+ catch( const Exception& )
+ {
+ TOOLS_WARN_EXCEPTION("svx.form", "caught an exception while asking the controller!");
+ }
+ }
+
+ implDetachController();
+
+ // remember our bindings: while we're closed, we're deleted, too, so accessing the bindings after this
+ // would be deadly
+ // 10/19/00 - 79321 - FS
+ SfxBindings& rBindings = SfxControllerItem::GetBindings();
+
+ SfxModelessDialogController::Close();
+
+ rBindings.Invalidate(SID_FM_CTL_PROPERTIES);
+ rBindings.Invalidate(SID_FM_PROPERTIES);
+}
+
+bool FmPropBrw::implIsReadOnlyModel() const
+{
+ try
+ {
+ if ( m_xInspectorModel.is() )
+ return m_xInspectorModel->getIsReadOnly();
+ return false;
+ }
+ catch( const Exception& )
+ {
+ DBG_UNHANDLED_EXCEPTION("svx");
+ }
+ return true;
+}
+
+
+void FmPropBrw::implSetNewSelection( const InterfaceBag& _rSelection )
+{
+ if ( !m_xBrowserController.is() )
+ return;
+
+ try
+ {
+ Reference< XObjectInspector > xInspector( m_xBrowserController, UNO_QUERY_THROW );
+
+ // tell it the objects to inspect
+ xInspector->inspect( comphelper::containerToSequence(_rSelection) );
+ }
+ catch( const VetoException& )
+ {
+ return;
+ }
+ catch( const Exception& )
+ {
+ TOOLS_WARN_EXCEPTION("svx.form", "");
+ return;
+ }
+
+ // set the new title according to the selected object
+ OUString sTitle;
+
+ if ( _rSelection.empty() )
+ {
+ sTitle = SvxResId(RID_STR_NO_PROPERTIES);
+ }
+ else if ( _rSelection.size() > 1 )
+ {
+ // no form component and (no form or no name) -> Multiselection
+ sTitle = SvxResId(RID_STR_PROPERTIES_CONTROL) +
+ SvxResId(RID_STR_PROPTITLE_MULTISELECT);
+ }
+ else
+ {
+ Reference< XPropertySet > xSingleSelection( *_rSelection.begin(), UNO_QUERY);
+ if ( ::comphelper::hasProperty( FM_PROP_CLASSID, xSingleSelection ) )
+ {
+ sal_Int16 nClassID = FormComponentType::CONTROL;
+ xSingleSelection->getPropertyValue( FM_PROP_CLASSID ) >>= nClassID;
+
+ sTitle = SvxResId(RID_STR_PROPERTIES_CONTROL) +
+ GetUIHeadlineName(nClassID, Any(xSingleSelection));
+ }
+ else if ( Reference< XForm >( xSingleSelection, UNO_QUERY ).is() )
+ sTitle = SvxResId(RID_STR_PROPERTIES_FORM);
+ }
+
+ if ( implIsReadOnlyModel() )
+ sTitle += SvxResId(RID_STR_READONLY_VIEW);
+
+ m_xDialog->set_title(sTitle);
+}
+
+void FmPropBrw::FillInfo( SfxChildWinInfo& rInfo ) const
+{
+ rInfo.bVisible = false;
+ rInfo.aExtraString = getCurrentPage();
+}
+
+IMPL_LINK_NOARG( FmPropBrw, OnAsyncGetFocus, void*, void )
+{
+ m_nAsyncGetFocusId = nullptr;
+}
+
+namespace
+{
+ bool lcl_shouldEnableHelpSection( const Reference< XComponentContext >& _rxContext )
+ {
+ ::utl::OConfigurationTreeRoot aConfiguration(
+ ::utl::OConfigurationTreeRoot::createWithComponentContext(
+ _rxContext,
+ "/org.openoffice.Office.Common/Forms/PropertyBrowser/" ) );
+
+ bool bEnabled = false;
+ OSL_VERIFY( aConfiguration.getNodeValue( "DirectHelp" ) >>= bEnabled );
+ return bEnabled;
+ }
+}
+
+void FmPropBrw::impl_createPropertyBrowser_throw( FmFormShell* _pFormShell )
+{
+ // the document in which we live
+ Reference< XInterface > xDocument;
+ if ( _pFormShell && _pFormShell->GetObjectShell() )
+ xDocument = _pFormShell->GetObjectShell()->GetModel();
+
+ // the context of the controls in our document
+ Reference< awt::XControlContainer > xControlContext;
+ if ( _pFormShell && _pFormShell->GetFormView() )
+ {
+ SdrPageView* pPageView = _pFormShell->GetFormView()->GetSdrPageView();
+
+ if(pPageView)
+ {
+ SdrPageWindow* pPageWindow = pPageView->GetPageWindow(0);
+
+ if(pPageWindow)
+ {
+ xControlContext = pPageWindow->GetControlContainer();
+ }
+ }
+ }
+
+ // the default parent window for message boxes
+ Reference< XWindow > xParentWindow(m_xDialog->GetXWindow());
+
+ // the mapping from control models to control shapes
+ Reference< XMap > xControlMap;
+ FmFormPage* pFormPage = _pFormShell ? _pFormShell->GetCurPage() : nullptr;
+ if ( pFormPage )
+ xControlMap = pFormPage->GetImpl().getControlToShapeMap();
+
+ // our own component context
+
+ // a ComponentContext for the
+ ::cppu::ContextEntry_Init aHandlerContextInfo[] =
+ {
+ ::cppu::ContextEntry_Init( "ContextDocument", Any( xDocument ) ),
+ ::cppu::ContextEntry_Init( "DialogParentWindow", Any( xParentWindow ) ),
+ ::cppu::ContextEntry_Init( "ControlContext", Any( xControlContext ) ),
+ ::cppu::ContextEntry_Init( "ControlShapeAccess", Any( xControlMap ) )
+ };
+ m_xInspectorContext.set(
+ ::cppu::createComponentContext( aHandlerContextInfo, SAL_N_ELEMENTS( aHandlerContextInfo ),
+ m_xORB ) );
+
+ bool bEnableHelpSection = lcl_shouldEnableHelpSection( m_xORB );
+
+ // an object inspector model
+ m_xInspectorModel =
+ bEnableHelpSection
+ ? DefaultFormComponentInspectorModel::createWithHelpSection( m_xInspectorContext, 3, 5 )
+ : DefaultFormComponentInspectorModel::createDefault( m_xInspectorContext );
+
+ // an object inspector
+ m_xBrowserController =
+ ObjectInspector::createWithModel(
+ m_xInspectorContext, m_xInspectorModel
+ );
+
+ if ( !m_xBrowserController.is() )
+ {
+ ShowServiceNotAvailableError(m_pParent, u"com.sun.star.inspection.ObjectInspector", true);
+ }
+ else
+ {
+ m_xBrowserController->attachFrame( Reference<XFrame>(m_xMeAsFrame,UNO_QUERY_THROW) );
+ }
+
+ if ( bEnableHelpSection )
+ {
+ Reference< XObjectInspector > xInspector( m_xBrowserController, UNO_QUERY_THROW );
+ Reference< XObjectInspectorUI > xInspectorUI( xInspector->getInspectorUI() );
+ DefaultHelpProvider::create( m_xInspectorContext, xInspectorUI );
+ }
+}
+
+
+void FmPropBrw::impl_ensurePropertyBrowser_nothrow( FmFormShell* _pFormShell )
+{
+ // the document in which we live
+ Reference< XInterface > xDocument;
+ SfxObjectShell* pObjectShell = _pFormShell ? _pFormShell->GetObjectShell() : nullptr;
+ if ( pObjectShell )
+ xDocument = pObjectShell->GetModel();
+ if ( ( xDocument == m_xLastKnownDocument ) && m_xBrowserController.is() )
+ // nothing to do
+ return;
+
+ try
+ {
+ // clean up any previous instances of the object inspector
+ if ( m_xMeAsFrame.is() )
+ m_xMeAsFrame->setComponent( nullptr, nullptr );
+ else
+ ::comphelper::disposeComponent( m_xBrowserController );
+ m_xBrowserController.clear();
+ m_xInspectorModel.clear();
+
+ // and create a new one
+ impl_createPropertyBrowser_throw( _pFormShell );
+ }
+ catch( const Exception& )
+ {
+ DBG_UNHANDLED_EXCEPTION("svx");
+ }
+ m_xLastKnownDocument = xDocument;
+}
+
+
+void FmPropBrw::StateChangedAtToolBoxControl(sal_uInt16 nSID, SfxItemState eState, const SfxPoolItem* pState)
+{
+ if (!pState || SID_FM_PROPERTY_CONTROL != nSID)
+ return;
+
+ try
+ {
+ if (eState >= SfxItemState::DEFAULT)
+ {
+ FmFormShell* pShell = dynamic_cast<FmFormShell*>( static_cast<const SfxObjectItem*>(pState)->GetShell() );
+ InterfaceBag aSelection;
+ if ( pShell )
+ pShell->GetImpl()->getCurrentSelection_Lock(aSelection);
+
+ impl_ensurePropertyBrowser_nothrow( pShell );
+
+ // set the new object to inspect
+ implSetNewSelection( aSelection );
+
+ // if this is the first time we're here, some additional things need to be done ...
+ if ( m_bInitialStateChange )
+ {
+ // if we're just newly created, we want to have the focus
+ m_nAsyncGetFocusId = Application::PostUserEvent(LINK(this, FmPropBrw, OnAsyncGetFocus));
+
+ // and additionally, we want to show the page which was active during
+ // our previous incarnation
+ if ( !m_sLastActivePage.isEmpty() )
+ {
+ try
+ {
+ if ( m_xBrowserController.is() )
+ m_xBrowserController->restoreViewData( Any( m_sLastActivePage ) );
+ }
+ catch( const Exception& )
+ {
+ TOOLS_WARN_EXCEPTION("svx.form",
+ "caught an exception while setting the initial page!");
+ }
+ }
+
+ m_bInitialStateChange = false;
+ }
+
+ }
+ else
+ {
+ implSetNewSelection( InterfaceBag() );
+ }
+ }
+ catch (Exception&)
+ {
+ TOOLS_WARN_EXCEPTION("svx.form", "");
+ }
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/form/fmcontrolbordermanager.cxx b/svx/source/form/fmcontrolbordermanager.cxx
new file mode 100644
index 000000000..b46733995
--- /dev/null
+++ b/svx/source/form/fmcontrolbordermanager.cxx
@@ -0,0 +1,425 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+
+#include <fmcontrolbordermanager.hxx>
+
+#include <fmprop.hxx>
+
+#include <com/sun/star/form/validation/XValidatableFormComponent.hpp>
+#include <com/sun/star/awt/XTextComponent.hpp>
+#include <com/sun/star/awt/XListBox.hpp>
+#include <tools/debug.hxx>
+#include <tools/diagnose_ex.h>
+#include <osl/diagnose.h>
+
+
+namespace svxform
+{
+
+
+ using namespace ::com::sun::star::uno;
+ using namespace ::com::sun::star::awt;
+ using namespace ::com::sun::star::form::validation;
+
+
+ //= helper
+
+
+ static void setUnderline( const Reference< XVclWindowPeer >& _rxPeer, const UnderlineDescriptor& _rUnderline )
+ {
+ OSL_ENSURE( _rxPeer.is(), "setUnderline: invalid peer!" );
+
+ // the underline type is an aspect of the font
+ FontDescriptor aFont;
+ OSL_VERIFY( _rxPeer->getProperty( FM_PROP_FONT ) >>= aFont );
+ aFont.Underline = _rUnderline.nUnderlineType;
+ _rxPeer->setProperty( FM_PROP_FONT, Any( aFont ) );
+ // the underline color is a separate property
+ _rxPeer->setProperty( FM_PROP_TEXTLINECOLOR, Any( _rUnderline.nUnderlineColor ) );
+ }
+
+
+ static void getUnderline( const Reference< XVclWindowPeer >& _rxPeer, UnderlineDescriptor& _rUnderline )
+ {
+ OSL_ENSURE( _rxPeer.is(), "getUnderline: invalid peer!" );
+
+ FontDescriptor aFont;
+ OSL_VERIFY( _rxPeer->getProperty( FM_PROP_FONT ) >>= aFont );
+ _rUnderline.nUnderlineType = aFont.Underline;
+
+ OSL_VERIFY( _rxPeer->getProperty( FM_PROP_TEXTLINECOLOR ) >>= _rUnderline.nUnderlineColor );
+ }
+
+
+ static void getBorder( const Reference< XVclWindowPeer >& _rxPeer, BorderDescriptor& _rBorder )
+ {
+ OSL_ENSURE( _rxPeer.is(), "getBorder: invalid peer!" );
+
+ OSL_VERIFY( _rxPeer->getProperty( FM_PROP_BORDER ) >>= _rBorder.nBorderType );
+ OSL_VERIFY( _rxPeer->getProperty( FM_PROP_BORDERCOLOR ) >>= _rBorder.nBorderColor );
+ }
+
+
+ static void setBorder( const Reference< XVclWindowPeer >& _rxPeer, const BorderDescriptor& _rBorder )
+ {
+ OSL_ENSURE( _rxPeer.is(), "setBorder: invalid peer!" );
+
+ _rxPeer->setProperty( FM_PROP_BORDER, Any( _rBorder.nBorderType ) );
+ _rxPeer->setProperty( FM_PROP_BORDERCOLOR, Any( _rBorder.nBorderColor ) );
+ }
+
+ ControlBorderManager::ControlBorderManager()
+ :m_nFocusColor ( 0x000000FF )
+ ,m_nMouseHoveColor( 0x007098BE )
+ ,m_nInvalidColor ( 0x00FF0000 )
+ ,m_bDynamicBorderColors( false )
+ {
+ }
+
+
+ ControlBorderManager::~ControlBorderManager()
+ {
+ }
+
+
+ bool ControlBorderManager::canColorBorder( const Reference< XVclWindowPeer >& _rxPeer )
+ {
+ OSL_PRECOND( _rxPeer.is(), "ControlBorderManager::canColorBorder: invalid peer!" );
+
+ PeerBag::const_iterator aPos = m_aColorableControls.find( _rxPeer );
+ if ( aPos != m_aColorableControls.end() )
+ return true;
+
+ aPos = m_aNonColorableControls.find( _rxPeer );
+ if ( aPos != m_aNonColorableControls.end() )
+ return false;
+
+ // this peer is not yet known
+
+ // no border coloring for controls which are not for text input
+ // #i37434# / 2004-11-19 / frank.schoenheit@sun.com
+ Reference< XTextComponent > xText( _rxPeer, UNO_QUERY );
+ Reference< XListBox > xListBox( _rxPeer, UNO_QUERY );
+ if ( xText.is() || xListBox.is() )
+ {
+ sal_Int16 nBorderStyle = VisualEffect::NONE;
+ OSL_VERIFY( _rxPeer->getProperty( FM_PROP_BORDER ) >>= nBorderStyle );
+ if ( nBorderStyle == VisualEffect::FLAT )
+ // if you change this to also accept LOOK3D, then this would also work, but look ugly
+ {
+ m_aColorableControls.insert( _rxPeer );
+ return true;
+ }
+ }
+
+ m_aNonColorableControls.insert( _rxPeer );
+ return false;
+ }
+
+
+ ControlStatus ControlBorderManager::getControlStatus( const Reference< XControl >& _rxControl )
+ {
+ ControlStatus nStatus = ControlStatus::NONE;
+
+ if ( _rxControl.get() == m_aFocusControl.xControl.get() )
+ nStatus |= ControlStatus::Focused;
+
+ if ( _rxControl.get() == m_aMouseHoverControl.xControl.get() )
+ nStatus |= ControlStatus::MouseHover;
+
+ if ( m_aInvalidControls.find( ControlData( _rxControl ) ) != m_aInvalidControls.end() )
+ nStatus |= ControlStatus::Invalid;
+
+ return nStatus;
+ }
+
+
+ Color ControlBorderManager::getControlColorByStatus( ControlStatus _nStatus ) const
+ {
+ // "invalid" is ranked highest
+ if ( _nStatus & ControlStatus::Invalid )
+ return m_nInvalidColor;
+
+ // then, "focused" is more important than ...
+ if ( _nStatus & ControlStatus::Focused )
+ return m_nFocusColor;
+
+ // ... "mouse over"
+ if ( _nStatus & ControlStatus::MouseHover )
+ return m_nMouseHoveColor;
+
+ OSL_FAIL( "ControlBorderManager::getControlColorByStatus: invalid status!" );
+ return Color(0);
+ }
+
+
+ void ControlBorderManager::updateBorderStyle( const Reference< XControl >& _rxControl, const Reference< XVclWindowPeer >& _rxPeer, const BorderDescriptor& _rFallback )
+ {
+ OSL_PRECOND( _rxControl.is() && _rxPeer.is(), "ControlBorderManager::updateBorderStyle: invalid parameters!" );
+
+ ControlStatus nStatus = getControlStatus( _rxControl );
+ BorderDescriptor aBorder;
+ aBorder.nBorderType = ( nStatus == ControlStatus::NONE )
+ ? _rFallback.nBorderType
+ : VisualEffect::FLAT;
+ aBorder.nBorderColor = ( nStatus == ControlStatus::NONE )
+ ? _rFallback.nBorderColor
+ : getControlColorByStatus( nStatus );
+ setBorder( _rxPeer, aBorder );
+ }
+
+
+ void ControlBorderManager::determineOriginalBorderStyle( const Reference< XControl >& _rxControl, BorderDescriptor& _rData ) const
+ {
+ _rData = ControlData();
+ if ( m_aFocusControl.xControl.get() == _rxControl.get() )
+ {
+ _rData = m_aFocusControl;
+ }
+ else if ( m_aMouseHoverControl.xControl.get() == _rxControl.get() )
+ {
+ _rData = m_aMouseHoverControl;
+ }
+ else
+ {
+ ControlBag::const_iterator aPos = m_aInvalidControls.find( _rxControl );
+ if ( aPos != m_aInvalidControls.end() )
+ {
+ _rData = *aPos;
+ }
+ else
+ {
+ Reference< XVclWindowPeer > xPeer( _rxControl->getPeer(), UNO_QUERY );
+ getBorder( xPeer, _rData );
+ }
+ }
+ }
+
+
+ void ControlBorderManager::controlStatusGained( const Reference< XInterface >& _rxControl, ControlData& _rControlData )
+ {
+ if ( _rxControl == _rControlData.xControl )
+ // nothing to do - though suspicious
+ return;
+
+ Reference< XControl > xAsControl( _rxControl, UNO_QUERY );
+ DBG_ASSERT( xAsControl.is(), "ControlBorderManager::controlStatusGained: invalid control!" );
+ if ( !xAsControl.is() )
+ return;
+
+ try
+ {
+ Reference< XVclWindowPeer > xPeer( xAsControl->getPeer(), UNO_QUERY );
+ if ( xPeer.is() && canColorBorder( xPeer ) )
+ {
+ // remember the control and its current border color
+ _rControlData.xControl.clear(); // so determineOriginalBorderStyle doesn't get confused
+
+ determineOriginalBorderStyle( xAsControl, _rControlData );
+
+ _rControlData.xControl = xAsControl;
+
+ updateBorderStyle( xAsControl, xPeer, _rControlData );
+ }
+ }
+ catch( const Exception& )
+ {
+ TOOLS_WARN_EXCEPTION( "svx", "ControlBorderManager::controlStatusGained" );
+ }
+ }
+
+
+ void ControlBorderManager::controlStatusLost( const Reference< XInterface >& _rxControl, ControlData& _rControlData )
+ {
+ if ( _rxControl != _rControlData.xControl )
+ // nothing to do
+ return;
+
+ OSL_PRECOND( _rControlData.xControl.is(), "ControlBorderManager::controlStatusLost: invalid control data - this will crash!" );
+ try
+ {
+ Reference< XVclWindowPeer > xPeer( _rControlData.xControl->getPeer(), UNO_QUERY );
+ if ( xPeer.is() && canColorBorder( xPeer ) )
+ {
+ ControlData aPreviousStatus( _rControlData );
+ _rControlData = ControlData();
+ updateBorderStyle( aPreviousStatus.xControl, xPeer, aPreviousStatus );
+ }
+ }
+ catch( const Exception& )
+ {
+ TOOLS_WARN_EXCEPTION( "svx", "ControlBorderManager::controlStatusLost" );
+ }
+ }
+
+
+ void ControlBorderManager::enableDynamicBorderColor( )
+ {
+ m_bDynamicBorderColors = true;
+ }
+
+
+ void ControlBorderManager::disableDynamicBorderColor( )
+ {
+ m_bDynamicBorderColors = false;
+ restoreAll();
+ }
+
+
+ void ControlBorderManager::setStatusColor( ControlStatus _nStatus, Color _nColor )
+ {
+ switch ( _nStatus )
+ {
+ case ControlStatus::Focused:
+ m_nFocusColor = _nColor;
+ break;
+ case ControlStatus::MouseHover:
+ m_nMouseHoveColor = _nColor;
+ break;
+ case ControlStatus::Invalid:
+ m_nInvalidColor = _nColor;
+ break;
+ default:
+ OSL_FAIL( "ControlBorderManager::setStatusColor: invalid status!" );
+ }
+ }
+
+
+ void ControlBorderManager::restoreAll()
+ {
+ if ( m_aFocusControl.xControl.is() )
+ controlStatusLost( m_aFocusControl.xControl, m_aFocusControl );
+ if ( m_aMouseHoverControl.xControl.is() )
+ controlStatusLost( m_aMouseHoverControl.xControl, m_aMouseHoverControl );
+
+ ControlBag aInvalidControls;
+ m_aInvalidControls.swap( aInvalidControls );
+
+ for (const auto& rControl : aInvalidControls)
+ {
+ Reference< XVclWindowPeer > xPeer( rControl.xControl->getPeer(), UNO_QUERY );
+ if ( xPeer.is() )
+ {
+ updateBorderStyle( rControl.xControl, xPeer, rControl );
+ xPeer->setProperty( FM_PROP_HELPTEXT, Any( rControl.sOriginalHelpText ) );
+ setUnderline( xPeer, rControl );
+ }
+ }
+ }
+
+
+ void ControlBorderManager::focusGained( const Reference< XInterface >& _rxControl )
+ {
+ if ( m_bDynamicBorderColors )
+ controlStatusGained( _rxControl, m_aFocusControl );
+ }
+
+
+ void ControlBorderManager::focusLost( const Reference< XInterface >& _rxControl )
+ {
+ if ( m_bDynamicBorderColors )
+ controlStatusLost( _rxControl, m_aFocusControl );
+ }
+
+
+ void ControlBorderManager::mouseEntered( const Reference< XInterface >& _rxControl )
+ {
+ if ( m_bDynamicBorderColors )
+ controlStatusGained( _rxControl, m_aMouseHoverControl );
+ }
+
+
+ void ControlBorderManager::mouseExited( const Reference< XInterface >& _rxControl )
+ {
+ if ( m_bDynamicBorderColors )
+ controlStatusLost( _rxControl, m_aMouseHoverControl );
+ }
+
+
+ void ControlBorderManager::validityChanged( const Reference< XControl >& _rxControl, const Reference< XValidatableFormComponent >& _rxValidatable )
+ {
+ try
+ {
+ OSL_ENSURE( _rxControl.is(), "ControlBorderManager::validityChanged: invalid control!" );
+ OSL_ENSURE( _rxValidatable.is(), "ControlBorderManager::validityChanged: invalid validatable!" );
+
+ Reference< XVclWindowPeer > xPeer( _rxControl.is() ? _rxControl->getPeer() : Reference< XWindowPeer >(), UNO_QUERY );
+ if ( !xPeer.is() || !_rxValidatable.is() )
+ return;
+
+ ControlData aData( _rxControl );
+
+ if ( _rxValidatable->isValid() )
+ {
+ ControlBag::iterator aPos = m_aInvalidControls.find( aData );
+ if ( aPos != m_aInvalidControls.end() )
+ { // invalid before, valid now
+ ControlData aOriginalLayout( *aPos );
+ m_aInvalidControls.erase( aPos );
+
+ // restore all the things we used to indicate invalidity
+ if ( m_bDynamicBorderColors )
+ updateBorderStyle( _rxControl, xPeer, aOriginalLayout );
+ xPeer->setProperty( FM_PROP_HELPTEXT, Any( aOriginalLayout.sOriginalHelpText ) );
+ setUnderline( xPeer, aOriginalLayout );
+ }
+ return;
+ }
+
+ // we're here in the INVALID case
+ if ( m_aInvalidControls.find( _rxControl ) == m_aInvalidControls.end() )
+ { // valid before, invalid now
+
+ // remember the current border
+ determineOriginalBorderStyle( _rxControl, aData );
+ // and tool tip
+ xPeer->getProperty( FM_PROP_HELPTEXT ) >>= aData.sOriginalHelpText;
+ // and font
+ getUnderline( xPeer, aData );
+
+ m_aInvalidControls.insert( aData );
+
+ // update the border to the new invalidity
+ if ( m_bDynamicBorderColors && canColorBorder( xPeer ) )
+ updateBorderStyle( _rxControl, xPeer, aData );
+ else
+ {
+ // and also the new font
+ setUnderline( xPeer, UnderlineDescriptor( css::awt::FontUnderline::WAVE, m_nInvalidColor ) );
+ }
+ }
+
+ // update the explanation for invalidity (this is always done, even if the validity did not change)
+ Reference< XValidator > xValidator = _rxValidatable->getValidator();
+ OSL_ENSURE( xValidator.is(), "ControlBorderManager::validityChanged: invalid, but no validator?" );
+ OUString sExplainInvalidity = xValidator.is() ? xValidator->explainInvalid( _rxValidatable->getCurrentValue() ) : OUString();
+ xPeer->setProperty( FM_PROP_HELPTEXT, Any( sExplainInvalidity ) );
+ }
+ catch( const Exception& )
+ {
+ TOOLS_WARN_EXCEPTION( "svx", "ControlBorderManager::validityChanged" );
+ }
+ }
+
+
+}
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/form/fmcontrollayout.cxx b/svx/source/form/fmcontrollayout.cxx
new file mode 100644
index 000000000..985fc6ef1
--- /dev/null
+++ b/svx/source/form/fmcontrollayout.cxx
@@ -0,0 +1,312 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+
+#include <fmcontrollayout.hxx>
+#include <fmprop.hxx>
+
+#include <com/sun/star/form/FormComponentType.hpp>
+#include <com/sun/star/awt/VisualEffect.hpp>
+#include <com/sun/star/i18n/ScriptType.hpp>
+#include <com/sun/star/lang/Locale.hpp>
+#include <com/sun/star/awt/FontDescriptor.hpp>
+#include <com/sun/star/style/XStyleFamiliesSupplier.hpp>
+#include <com/sun/star/lang/XServiceInfo.hpp>
+#include <com/sun/star/container/XChild.hpp>
+
+#include <comphelper/processfactory.hxx>
+#include <i18nlangtag/mslangid.hxx>
+#include <i18nlangtag/languagetag.hxx>
+#include <unotools/confignode.hxx>
+#include <unotools/syslocale.hxx>
+#include <unotools/localedatawrapper.hxx>
+
+#include <toolkit/helper/vclunohelper.hxx>
+#include <tools/debug.hxx>
+#include <tools/diagnose_ex.h>
+#include <vcl/outdev.hxx>
+
+
+namespace svxform
+{
+
+
+ using namespace ::utl;
+ using ::com::sun::star::uno::Reference;
+ using ::com::sun::star::uno::XInterface;
+ using ::com::sun::star::uno::UNO_QUERY;
+ using ::com::sun::star::uno::UNO_QUERY_THROW;
+ using ::com::sun::star::uno::UNO_SET_THROW;
+ using ::com::sun::star::uno::Exception;
+ using ::com::sun::star::uno::RuntimeException;
+ using ::com::sun::star::uno::Any;
+ using ::com::sun::star::beans::XPropertySet;
+ using ::com::sun::star::beans::XPropertySetInfo;
+ using ::com::sun::star::lang::Locale;
+ using ::com::sun::star::awt::FontDescriptor;
+ using ::com::sun::star::style::XStyleFamiliesSupplier;
+ using ::com::sun::star::lang::XServiceInfo;
+ using ::com::sun::star::container::XNameAccess;
+ using ::com::sun::star::container::XChild;
+
+ namespace FormComponentType = ::com::sun::star::form::FormComponentType;
+ namespace VisualEffect = ::com::sun::star::awt::VisualEffect;
+ namespace ScriptType = ::com::sun::star::i18n::ScriptType;
+
+
+ namespace
+ {
+ ::utl::OConfigurationNode getLayoutSettings( DocumentType _eDocType )
+ {
+ OUString sConfigName = "/org.openoffice.Office.Common/Forms/ControlLayout/" +
+ DocumentClassification::getModuleIdentifierForDocumentType( _eDocType );
+ return OConfigurationTreeRoot::createWithComponentContext(
+ ::comphelper::getProcessComponentContext(), // TODO
+ sConfigName );
+ }
+
+ template< class INTERFACE_TYPE >
+ Reference< INTERFACE_TYPE > getTypedModelNode( const Reference< XInterface >& _rxModelNode )
+ {
+ Reference< INTERFACE_TYPE > xTypedNode( _rxModelNode, UNO_QUERY );
+ if ( xTypedNode.is() )
+ return xTypedNode;
+ else
+ {
+ Reference< XChild > xChild( _rxModelNode, UNO_QUERY );
+ if ( xChild.is() )
+ return getTypedModelNode< INTERFACE_TYPE >( xChild->getParent() );
+ else
+ return nullptr;
+ }
+ }
+
+
+ bool lcl_getDocumentDefaultStyleAndFamily( const Reference< XInterface >& _rxDocument, OUString& _rFamilyName, OUString& _rStyleName )
+ {
+ bool bSuccess = true;
+ Reference< XServiceInfo > xDocumentSI( _rxDocument, UNO_QUERY );
+ if ( xDocumentSI.is() )
+ {
+ if ( xDocumentSI->supportsService("com.sun.star.text.TextDocument")
+ || xDocumentSI->supportsService("com.sun.star.text.WebDocument")
+ )
+ {
+ _rFamilyName = "ParagraphStyles";
+ _rStyleName = "Standard";
+ }
+ else if ( xDocumentSI->supportsService("com.sun.star.sheet.SpreadsheetDocument") )
+ {
+ _rFamilyName = "CellStyles";
+ _rStyleName = "Default";
+ }
+ else if ( xDocumentSI->supportsService("com.sun.star.drawing.DrawingDocument")
+ || xDocumentSI->supportsService("com.sun.star.presentation.PresentationDocument")
+ )
+ {
+ _rFamilyName = "graphics";
+ _rStyleName = "standard";
+ }
+ else
+ bSuccess = false;
+ }
+ return bSuccess;
+ }
+
+
+ void lcl_initializeControlFont( const Reference< XPropertySet >& _rxModel )
+ {
+ try
+ {
+ Reference< XPropertySet > xStyle( ControlLayouter::getDefaultDocumentTextStyle( _rxModel ), UNO_SET_THROW );
+ Reference< XPropertySetInfo > xStylePSI( xStyle->getPropertySetInfo(), UNO_SET_THROW );
+
+ // determine the script type associated with the system locale
+ const SvtSysLocale aSysLocale;
+ const LocaleDataWrapper& rSysLocaleData = aSysLocale.GetLocaleData();
+ const sal_Int16 eSysLocaleScriptType = MsLangId::getScriptType( rSysLocaleData.getLanguageTag().getLanguageType() );
+
+ // depending on this script type, use the right property from the document's style which controls the
+ // default locale for document content
+ const char* pCharLocalePropertyName = "CharLocale";
+ switch ( eSysLocaleScriptType )
+ {
+ case ScriptType::LATIN:
+ // already defaulted above
+ break;
+ case ScriptType::ASIAN:
+ pCharLocalePropertyName = "CharLocaleAsian";
+ break;
+ case ScriptType::COMPLEX:
+ pCharLocalePropertyName = "CharLocaleComplex";
+ break;
+ default:
+ OSL_FAIL( "lcl_initializeControlFont: unexpected script type for system locale!" );
+ break;
+ }
+
+ OUString sCharLocalePropertyName = OUString::createFromAscii( pCharLocalePropertyName );
+ Locale aDocumentCharLocale;
+ if ( xStylePSI->hasPropertyByName( sCharLocalePropertyName ) )
+ {
+ OSL_VERIFY( xStyle->getPropertyValue( sCharLocalePropertyName ) >>= aDocumentCharLocale );
+ }
+ // fall back to CharLocale property at the style
+ if ( aDocumentCharLocale.Language.isEmpty() )
+ {
+ sCharLocalePropertyName = "CharLocale";
+ if ( xStylePSI->hasPropertyByName( sCharLocalePropertyName ) )
+ {
+ OSL_VERIFY( xStyle->getPropertyValue( sCharLocalePropertyName ) >>= aDocumentCharLocale );
+ }
+ }
+ // fall back to the system locale
+ if ( aDocumentCharLocale.Language.isEmpty() )
+ {
+ aDocumentCharLocale = rSysLocaleData.getLanguageTag().getLocale();
+ }
+
+ // retrieve a default font for this locale, and set it at the control
+ vcl::Font aFont = OutputDevice::GetDefaultFont( DefaultFontType::SANS, LanguageTag::convertToLanguageType( aDocumentCharLocale ), GetDefaultFontFlags::OnlyOne );
+ FontDescriptor aFontDesc = VCLUnoHelper::CreateFontDescriptor( aFont );
+ _rxModel->setPropertyValue("FontDescriptor", Any( aFontDesc )
+ );
+ }
+ catch( const Exception& )
+ {
+ DBG_UNHANDLED_EXCEPTION("svx");
+ }
+ }
+ }
+
+
+ //= ControlLayouter
+
+
+ Reference< XPropertySet > ControlLayouter::getDefaultDocumentTextStyle( const Reference< XPropertySet >& _rxModel )
+ {
+ // the style family collection
+ Reference< XStyleFamiliesSupplier > xSuppStyleFamilies( getTypedModelNode< XStyleFamiliesSupplier >( _rxModel ), UNO_SET_THROW );
+ Reference< XNameAccess > xStyleFamilies( xSuppStyleFamilies->getStyleFamilies(), UNO_SET_THROW );
+
+ // the names of the family, and the style - depends on the document type we live in
+ OUString sFamilyName, sStyleName;
+ if ( !lcl_getDocumentDefaultStyleAndFamily( xSuppStyleFamilies, sFamilyName, sStyleName ) )
+ throw RuntimeException("unknown document type!");
+
+ // the concrete style
+ Reference< XNameAccess > xStyleFamily( xStyleFamilies->getByName( sFamilyName ), UNO_QUERY_THROW );
+ return Reference< XPropertySet >( xStyleFamily->getByName( sStyleName ), UNO_QUERY_THROW );
+ }
+
+
+ void ControlLayouter::initializeControlLayout( const Reference< XPropertySet >& _rxControlModel, DocumentType _eDocType )
+ {
+ DBG_ASSERT( _rxControlModel.is(), "ControlLayouter::initializeControlLayout: invalid model!" );
+ if ( !_rxControlModel.is() )
+ return;
+
+ try
+ {
+ Reference< XPropertySetInfo > xPSI( _rxControlModel->getPropertySetInfo(), UNO_SET_THROW );
+
+ // the control type
+ sal_Int16 nClassId = FormComponentType::CONTROL;
+ _rxControlModel->getPropertyValue( FM_PROP_CLASSID ) >>= nClassId;
+
+ // the document type
+ if ( _eDocType == eUnknownDocumentType )
+ _eDocType = DocumentClassification::classifyHostDocument( _rxControlModel );
+
+ // let's see what the configuration says about the visual effect
+ OConfigurationNode aConfig = getLayoutSettings( _eDocType );
+ Any aVisualEffect = aConfig.getNodeValue( OUString( "VisualEffect" ) );
+ if ( aVisualEffect.hasValue() )
+ {
+ OUString sVisualEffect;
+ OSL_VERIFY( aVisualEffect >>= sVisualEffect );
+
+ sal_Int16 nVisualEffect = VisualEffect::NONE;
+ if ( sVisualEffect == "flat" )
+ nVisualEffect = VisualEffect::FLAT;
+ else if ( sVisualEffect == "3D" )
+ nVisualEffect = VisualEffect::LOOK3D;
+
+ if ( xPSI->hasPropertyByName( FM_PROP_BORDER ) )
+ {
+ if ( ( nClassId != FormComponentType::COMMANDBUTTON )
+ && ( nClassId != FormComponentType::RADIOBUTTON )
+ && ( nClassId != FormComponentType::CHECKBOX )
+ && ( nClassId != FormComponentType::GROUPBOX )
+ && ( nClassId != FormComponentType::FIXEDTEXT )
+ && ( nClassId != FormComponentType::SCROLLBAR )
+ && ( nClassId != FormComponentType::SPINBUTTON )
+ )
+ {
+ _rxControlModel->setPropertyValue( FM_PROP_BORDER, Any( nVisualEffect ) );
+ if ( ( nVisualEffect == VisualEffect::FLAT )
+ && ( xPSI->hasPropertyByName( FM_PROP_BORDERCOLOR ) )
+ )
+ // light gray flat border
+ _rxControlModel->setPropertyValue( FM_PROP_BORDERCOLOR, Any( sal_Int32(0x00C0C0C0) ) );
+ }
+ }
+ if ( xPSI->hasPropertyByName( FM_PROP_VISUALEFFECT ) )
+ _rxControlModel->setPropertyValue( FM_PROP_VISUALEFFECT, Any( nVisualEffect ) );
+ }
+
+ // the font (only if we use the document's ref devices for rendering control text, otherwise, the
+ // default font of VCL controls is assumed to be fine)
+ if ( useDocumentReferenceDevice( _eDocType )
+ && xPSI->hasPropertyByName( FM_PROP_FONT )
+ )
+ lcl_initializeControlFont( _rxControlModel );
+ }
+ catch( const Exception& )
+ {
+ TOOLS_WARN_EXCEPTION( "svx", "ControlLayouter::initializeControlLayout" );
+ }
+ }
+
+ bool ControlLayouter::useDynamicBorderColor( DocumentType _eDocType )
+ {
+ OConfigurationNode aConfig = getLayoutSettings( _eDocType );
+ Any aDynamicBorderColor = aConfig.getNodeValue( OUString( "DynamicBorderColors" ) );
+ bool bDynamicBorderColor = false;
+ OSL_VERIFY( aDynamicBorderColor >>= bDynamicBorderColor );
+ return bDynamicBorderColor;
+ }
+
+
+ bool ControlLayouter::useDocumentReferenceDevice( DocumentType _eDocType )
+ {
+ if ( _eDocType == eUnknownDocumentType )
+ return false;
+ OConfigurationNode aConfig = getLayoutSettings( _eDocType );
+ Any aUseRefDevice = aConfig.getNodeValue( OUString( "UseDocumentTextMetrics" ) );
+ bool bUseRefDevice = false;
+ OSL_VERIFY( aUseRefDevice >>= bUseRefDevice );
+ return bUseRefDevice;
+ }
+
+
+}
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/form/fmdmod.cxx b/svx/source/form/fmdmod.cxx
new file mode 100644
index 000000000..e475cf8f8
--- /dev/null
+++ b/svx/source/form/fmdmod.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 <sal/config.h>
+
+#include <sal/macros.h>
+#include <svx/fmdmod.hxx>
+#include <fmservs.hxx>
+#include <fmobj.hxx>
+#include <svx/unoshape.hxx>
+#include <comphelper/processfactory.hxx>
+#include <comphelper/sequence.hxx>
+#include <com/sun/star/uno/XComponentContext.hpp>
+
+using namespace ::svxform;
+
+
+::com::sun::star::uno::Reference< ::com::sun::star::uno::XInterface > SAL_CALL SvxFmMSFactory::createInstance(const OUString& rServiceSpecifier)
+{
+ ::com::sun::star::uno::Reference< ::com::sun::star::uno::XInterface > xRet;
+
+ if ( rServiceSpecifier.startsWith( "com.sun.star.form.component." ) )
+ {
+ css::uno::Reference<css::uno::XComponentContext> xContext = comphelper::getProcessComponentContext();
+ xRet = xContext->getServiceManager()->createInstanceWithContext(rServiceSpecifier, xContext);
+ }
+ else if ( rServiceSpecifier == "com.sun.star.drawing.ControlShape" )
+ {
+ SdrModel& rTargetModel(getSdrModelFromUnoModel());
+ SdrObject* pObj = new FmFormObj(rTargetModel);
+ xRet = static_cast<cppu::OWeakObject*>(static_cast<SvxShape_UnoImplHelper*>(new SvxShapeControl(pObj)));
+ }
+
+ if (!xRet.is())
+ {
+ xRet = SvxUnoDrawMSFactory::createInstance(rServiceSpecifier);
+ }
+
+ return xRet;
+}
+
+
+::com::sun::star::uno::Sequence< OUString > SAL_CALL SvxFmMSFactory::getAvailableServiceNames()
+{
+ static const rtl::OUStringConstExpr aSvxComponentServiceNameList[] =
+ {
+ FM_SUN_COMPONENT_TEXTFIELD,
+ FM_SUN_COMPONENT_FORM,
+ FM_SUN_COMPONENT_LISTBOX,
+ FM_SUN_COMPONENT_COMBOBOX,
+ FM_SUN_COMPONENT_RADIOBUTTON,
+ FM_SUN_COMPONENT_GROUPBOX,
+ FM_SUN_COMPONENT_FIXEDTEXT,
+ FM_SUN_COMPONENT_COMMANDBUTTON,
+ FM_SUN_COMPONENT_CHECKBOX,
+ FM_SUN_COMPONENT_GRIDCONTROL,
+ FM_SUN_COMPONENT_IMAGEBUTTON,
+ FM_SUN_COMPONENT_FILECONTROL,
+ FM_SUN_COMPONENT_TIMEFIELD,
+ FM_SUN_COMPONENT_DATEFIELD,
+ FM_SUN_COMPONENT_NUMERICFIELD,
+ FM_SUN_COMPONENT_CURRENCYFIELD,
+ FM_SUN_COMPONENT_PATTERNFIELD,
+ FM_SUN_COMPONENT_HIDDENCONTROL,
+ FM_SUN_COMPONENT_IMAGECONTROL
+ };
+
+ static const sal_uInt16 nSvxComponentServiceNameListCount = SAL_N_ELEMENTS(aSvxComponentServiceNameList);
+
+ auto aSeq( comphelper::arrayToSequence< OUString >(aSvxComponentServiceNameList, nSvxComponentServiceNameListCount) );
+
+ ::com::sun::star::uno::Sequence< OUString > aParentSeq( SvxUnoDrawMSFactory::getAvailableServiceNames() );
+ return comphelper::concatSequences( aParentSeq, aSeq );
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/form/fmdocumentclassification.cxx b/svx/source/form/fmdocumentclassification.cxx
new file mode 100644
index 000000000..e624e23f5
--- /dev/null
+++ b/svx/source/form/fmdocumentclassification.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 <fmdocumentclassification.hxx>
+
+#include <com/sun/star/container/XChild.hpp>
+#include <com/sun/star/lang/XServiceInfo.hpp>
+#include <com/sun/star/xforms/XFormsSupplier.hpp>
+#include <com/sun/star/frame/XModule.hpp>
+
+#include <o3tl/string_view.hxx>
+#include <tools/diagnose_ex.h>
+
+
+namespace svxform
+{
+
+
+ namespace
+ {
+ using ::com::sun::star::uno::Reference;
+ using ::com::sun::star::uno::XInterface;
+ using ::com::sun::star::container::XChild;
+ using ::com::sun::star::frame::XModel;
+ using ::com::sun::star::uno::UNO_QUERY;
+ using ::com::sun::star::frame::XModule;
+
+
+ template< class TYPE >
+ Reference< TYPE > getTypedModelNode( const Reference< XInterface >& _rxModelNode )
+ {
+ Reference< TYPE > xTypedNode( _rxModelNode, UNO_QUERY );
+ if ( xTypedNode.is() )
+ return xTypedNode;
+ else
+ {
+ Reference< XChild > xChild( _rxModelNode, UNO_QUERY );
+ if ( xChild.is() )
+ return getTypedModelNode< TYPE >( xChild->getParent() );
+ else
+ return Reference< TYPE >();
+ }
+ }
+
+
+ Reference< XModel > getDocument( const Reference< XInterface >& _rxModelNode )
+ {
+ return getTypedModelNode< XModel >( _rxModelNode );
+ }
+ }
+
+ using namespace ::com::sun::star::uno;
+ using namespace ::com::sun::star::frame;
+ using namespace ::com::sun::star::lang;
+ using namespace ::com::sun::star::xforms;
+ using namespace ::com::sun::star::container;
+
+
+ namespace
+ {
+
+ struct ModuleInfo
+ {
+ const char* pAsciiModuleOrServiceName;
+ DocumentType eType;
+ };
+
+
+ const ModuleInfo* lcl_getModuleInfo()
+ {
+ static const ModuleInfo aModuleInfo[] =
+ {
+ { "com.sun.star.text.TextDocument", eTextDocument },
+ { "com.sun.star.text.WebDocument", eWebDocument },
+ { "com.sun.star.sheet.SpreadsheetDocument", eSpreadsheetDocument },
+ { "com.sun.star.drawing.DrawingDocument", eDrawingDocument },
+ { "com.sun.star.presentation.PresentationDocument", ePresentationDocument },
+ { "com.sun.star.xforms.XMLFormDocument", eEnhancedForm },
+ { "com.sun.star.sdb.FormDesign", eDatabaseForm },
+ { "com.sun.star.sdb.TextReportDesign", eDatabaseReport },
+ { "com.sun.star.text.GlobalDocument", eTextDocument },
+ { nullptr, eUnknownDocumentType }
+ };
+ return aModuleInfo;
+ }
+ }
+
+
+ //= DocumentClassification
+
+
+ DocumentType DocumentClassification::classifyDocument( const Reference< XModel >& _rxDocumentModel )
+ {
+ DocumentType eType( eUnknownDocumentType );
+
+ OSL_ENSURE( _rxDocumentModel.is(), "DocumentClassification::classifyDocument: invalid document!" );
+ if ( !_rxDocumentModel.is() )
+ return eType;
+
+ try
+ {
+ // first, check whether the document has a ModuleIdentifier which we know
+ Reference< XModule > xModule( _rxDocumentModel, UNO_QUERY );
+ if ( xModule.is() )
+ eType = getDocumentTypeForModuleIdentifier( xModule->getIdentifier() );
+ if ( eType != eUnknownDocumentType )
+ return eType;
+
+ // second, check whether it supports one of the services we know
+ Reference< XServiceInfo > xSI( _rxDocumentModel, UNO_QUERY_THROW );
+ const ModuleInfo* pModuleInfo = lcl_getModuleInfo();
+ while ( pModuleInfo->pAsciiModuleOrServiceName )
+ {
+ if ( xSI->supportsService( OUString::createFromAscii( pModuleInfo->pAsciiModuleOrServiceName ) ) )
+ return pModuleInfo->eType;
+ ++pModuleInfo;
+ }
+
+ // last: uhm, there is no last resort
+ OSL_FAIL( "DocumentClassification::classifyDocument: unknown document!" );
+ }
+ catch( const Exception& )
+ {
+ DBG_UNHANDLED_EXCEPTION("svx");
+ }
+
+ return eType;
+ }
+
+
+ DocumentType DocumentClassification::classifyHostDocument( const Reference< XInterface >& _rxFormComponent )
+ {
+ DocumentType eType( eUnknownDocumentType );
+
+ try
+ {
+ Reference< XModel > xDocument( getDocument( _rxFormComponent ) );
+ if ( !xDocument.is() )
+ return eUnknownDocumentType;
+ eType = classifyDocument( xDocument );
+ }
+ catch( const Exception& )
+ {
+ TOOLS_WARN_EXCEPTION( "svx", "DocumentClassification::classifyHostDocument" );
+ }
+
+ return eType;
+ }
+
+
+ DocumentType DocumentClassification::getDocumentTypeForModuleIdentifier( std::u16string_view _rModuleIdentifier )
+ {
+ const ModuleInfo* pModuleInfo = lcl_getModuleInfo();
+ while ( pModuleInfo->pAsciiModuleOrServiceName )
+ {
+ if ( o3tl::equalsAscii(_rModuleIdentifier, pModuleInfo->pAsciiModuleOrServiceName ) )
+ return pModuleInfo->eType;
+ ++pModuleInfo;
+ }
+ return eUnknownDocumentType;
+ }
+
+
+ OUString DocumentClassification::getModuleIdentifierForDocumentType( DocumentType _eType )
+ {
+ const ModuleInfo* pModuleInfo = lcl_getModuleInfo();
+ while ( pModuleInfo->pAsciiModuleOrServiceName )
+ {
+ if ( pModuleInfo->eType == _eType )
+ return OUString::createFromAscii( pModuleInfo->pAsciiModuleOrServiceName );
+ ++pModuleInfo;
+ }
+ return OUString();
+ }
+
+
+}
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/form/fmdpage.cxx b/svx/source/form/fmdpage.cxx
new file mode 100644
index 000000000..2351564e0
--- /dev/null
+++ b/svx/source/form/fmdpage.cxx
@@ -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 .
+ */
+
+#include <svx/fmpage.hxx>
+#include <fmobj.hxx>
+#include <svx/fmdpage.hxx>
+#include <svx/unoshape.hxx>
+#include <vcl/svapp.hxx>
+#include <cppuhelper/queryinterface.hxx>
+
+using ::com::sun::star::uno::Any;
+using ::com::sun::star::form::XFormsSupplier2;
+
+SvxFmDrawPage::SvxFmDrawPage( SdrPage* pInPage ) :
+ SvxDrawPage( pInPage )
+{
+}
+
+SvxFmDrawPage::~SvxFmDrawPage() noexcept
+{
+}
+
+css::uno::Sequence< sal_Int8 > SAL_CALL SvxFmDrawPage::getImplementationId()
+{
+ return css::uno::Sequence<sal_Int8>();
+}
+
+Any SAL_CALL SvxFmDrawPage::queryAggregation( const css::uno::Type& _rType )
+{
+ Any aRet = ::cppu::queryInterface ( _rType
+ , static_cast< XFormsSupplier2* >( this )
+ , static_cast< XFormsSupplier* >( this )
+ );
+ if ( !aRet.hasValue() )
+ aRet = SvxDrawPage::queryAggregation( _rType );
+
+ return aRet;
+}
+
+css::uno::Sequence< css::uno::Type > SAL_CALL SvxFmDrawPage::getTypes( )
+{
+ return comphelper::concatSequences(SvxDrawPage::getTypes(),
+ css::uno::Sequence { cppu::UnoType<css::form::XFormsSupplier>::get() });
+}
+
+SdrObject *SvxFmDrawPage::CreateSdrObject_( const css::uno::Reference< css::drawing::XShape > & xDescr )
+{
+ OUString aShapeType( xDescr->getShapeType() );
+
+ if ( aShapeType == "com.sun.star.drawing.ShapeControl" // compatibility
+ || aShapeType == "com.sun.star.drawing.ControlShape"
+ )
+ {
+ return new FmFormObj(GetSdrPage()->getSdrModelFromSdrPage());
+ }
+ else
+ {
+ return SvxDrawPage::CreateSdrObject_( xDescr );
+ }
+}
+
+css::uno::Reference< css::drawing::XShape > SvxFmDrawPage::CreateShape( SdrObject *pObj ) const
+{
+ if( SdrInventor::FmForm == pObj->GetObjInventor() )
+ {
+ css::uno::Reference< css::drawing::XShape > xShape = static_cast<SvxShape*>(new SvxShapeControl( pObj ));
+ return xShape;
+ }
+ else
+ return SvxDrawPage::CreateShape( pObj );
+}
+
+// XFormsSupplier
+css::uno::Reference< css::container::XNameContainer > SAL_CALL SvxFmDrawPage::getForms()
+{
+ SolarMutexGuard g;
+
+ css::uno::Reference< css::container::XNameContainer > xForms;
+
+ FmFormPage *pFmPage = dynamic_cast<FmFormPage*>( GetSdrPage() );
+ if( pFmPage )
+ xForms.set( pFmPage->GetForms(), css::uno::UNO_QUERY_THROW );
+
+ return xForms;
+}
+
+// XFormsSupplier2
+sal_Bool SAL_CALL SvxFmDrawPage::hasForms()
+{
+ SolarMutexGuard g;
+
+ bool bHas = false;
+ FmFormPage* pFormPage = dynamic_cast<FmFormPage*>( GetSdrPage() );
+ if ( pFormPage )
+ bHas = pFormPage->GetForms( false ).is();
+ return bHas;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/form/fmexch.cxx b/svx/source/form/fmexch.cxx
new file mode 100644
index 000000000..5a5429b10
--- /dev/null
+++ b/svx/source/form/fmexch.cxx
@@ -0,0 +1,347 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <fmexch.hxx>
+
+#include <sot/formats.hxx>
+#include <tools/debug.hxx>
+#include <tools/diagnose_ex.h>
+
+namespace svxform
+{
+ using namespace ::com::sun::star::uno;
+ using namespace ::com::sun::star::datatransfer;
+
+ OLocalExchange::OLocalExchange( )
+ :m_bDragging( false )
+ ,m_bClipboardOwner( false )
+ {
+ }
+
+ void OLocalExchange::copyToClipboard(const weld::Widget& rWidget, const GrantAccess&)
+ {
+ if ( m_bClipboardOwner )
+ { // simulate a lostOwnership to notify parties interested in
+ m_aClipboardListener.Call( *this );
+ }
+
+ m_bClipboardOwner = true;
+ CopyToClipboard(rWidget.get_clipboard());
+ }
+
+ void OLocalExchange::clear()
+ {
+ if ( !isClipboardOwner() )
+ return;
+
+ try
+ {
+ Reference< clipboard::XClipboard > xClipBoard( getOwnClipboard() );
+ if ( xClipBoard.is() )
+ xClipBoard->setContents( nullptr, nullptr );
+ }
+ catch( const Exception& )
+ {
+ DBG_UNHANDLED_EXCEPTION("svx");
+ }
+ m_bClipboardOwner = false;
+ }
+
+ void SAL_CALL OLocalExchange::lostOwnership( const Reference< clipboard::XClipboard >& _rxClipboard, const Reference< XTransferable >& _rxTrans )
+ {
+ TransferDataContainer::implCallOwnLostOwnership( _rxClipboard, _rxTrans );
+ m_bClipboardOwner = false;
+
+ m_aClipboardListener.Call( *this );
+ }
+
+ void OLocalExchange::setDragging(bool bDragging)
+ {
+ m_bDragging = bDragging;
+ }
+
+ void OLocalExchange::DragFinished( sal_Int8 nDropAction )
+ {
+ TransferDataContainer::DragFinished( nDropAction );
+ setDragging(false);
+ }
+
+ bool OLocalExchange::hasFormat( const DataFlavorExVector& _rFormats, SotClipboardFormatId _nFormatId )
+ {
+ return std::any_of(_rFormats.begin(), _rFormats.end(),
+ [&_nFormatId](const DataFlavorEx& rFormat) { return rFormat.mnSotId == _nFormatId; });
+ }
+
+ bool OLocalExchange::GetData( const css::datatransfer::DataFlavor& /*_rFlavor*/, const OUString& /*rDestDoc*/ )
+ {
+ return false; // do not have any formats by default
+ }
+
+ OControlTransferData::OControlTransferData( )
+ : m_bFocusEntry(false)
+ {
+ }
+
+ OControlTransferData::OControlTransferData( const Reference< XTransferable >& _rxTransferable )
+ : m_bFocusEntry(false)
+ {
+ TransferableDataHelper aExchangedData( _rxTransferable );
+
+ // try the formats we know
+ if ( OControlExchange::hasControlPathFormat( aExchangedData.GetDataFlavorExVector() ) )
+ { // paths to the controls, relative to a root
+ Sequence< Any > aControlPathData;
+ if ( aExchangedData.GetAny(OControlExchange::getControlPathFormatId(), OUString()) >>= aControlPathData )
+ {
+ DBG_ASSERT( aControlPathData.getLength() >= 2, "OControlTransferData::OControlTransferData: invalid data for the control path format!" );
+ if ( aControlPathData.getLength() >= 2 )
+ {
+ aControlPathData[0] >>= m_xFormsRoot;
+ aControlPathData[1] >>= m_aControlPaths;
+ }
+ }
+ else
+ {
+ OSL_FAIL( "OControlTransferData::OControlTransferData: invalid data for the control path format (2)!" );
+ }
+ }
+ if ( OControlExchange::hasHiddenControlModelsFormat( aExchangedData.GetDataFlavorExVector() ) )
+ { // sequence of models of hidden controls
+ aExchangedData.GetAny(OControlExchange::getHiddenControlModelsFormatId(), OUString()) >>= m_aHiddenControlModels;
+ }
+
+ updateFormats( );
+ }
+
+
+ static bool lcl_fillDataFlavorEx( SotClipboardFormatId nId, DataFlavorEx& _rFlavor )
+ {
+ _rFlavor.mnSotId = nId;
+ return SotExchange::GetFormatDataFlavor( _rFlavor.mnSotId, _rFlavor );
+ }
+
+
+ void OControlTransferData::updateFormats( )
+ {
+ m_aCurrentFormats.clear();
+ m_aCurrentFormats.reserve( 3 );
+
+ DataFlavorEx aFlavor;
+
+ if ( m_aHiddenControlModels.hasElements() )
+ {
+ if ( lcl_fillDataFlavorEx( OControlExchange::getHiddenControlModelsFormatId(), aFlavor ) )
+ m_aCurrentFormats.push_back( aFlavor );
+ }
+
+ if ( m_xFormsRoot.is() && m_aControlPaths.hasElements() )
+ {
+ if ( lcl_fillDataFlavorEx( OControlExchange::getControlPathFormatId(), aFlavor ) )
+ m_aCurrentFormats.push_back( aFlavor );
+ }
+
+ if ( !m_aSelectedEntries.empty() )
+ {
+ if ( lcl_fillDataFlavorEx( OControlExchange::getFieldExchangeFormatId(), aFlavor ) )
+ m_aCurrentFormats.push_back( aFlavor );
+ }
+ }
+
+ size_t OControlTransferData::onEntryRemoved(const weld::TreeView* pView, const weld::TreeIter* _pEntry)
+ {
+ auto aIter = std::find_if(m_aSelectedEntries.begin(), m_aSelectedEntries.end(),
+ [pView, _pEntry](const auto& rElem) {
+ return pView->iter_compare(*rElem, *_pEntry) == 0;
+ });
+ if (aIter != m_aSelectedEntries.end())
+ m_aSelectedEntries.erase(aIter);
+
+ return m_aSelectedEntries.size();
+ }
+
+ void OControlTransferData::addSelectedEntry(std::unique_ptr<weld::TreeIter> xEntry)
+ {
+ m_aSelectedEntries.emplace(std::move(xEntry));
+ }
+
+ void OControlTransferData::setFocusEntry(bool _bFocusEntry)
+ {
+ m_bFocusEntry = _bFocusEntry;
+ }
+
+ void OControlTransferData::addHiddenControlsFormat(const css::uno::Sequence< css::uno::Reference< css::uno::XInterface > >& seqInterfaces)
+ {
+ m_aHiddenControlModels = seqInterfaces;
+ }
+
+ void OControlTransferData::buildPathFormat(const weld::TreeView* pTreeBox, const weld::TreeIter* pRoot)
+ {
+ m_aControlPaths.realloc(0);
+
+ sal_Int32 nEntryCount = m_aSelectedEntries.size();
+ if (nEntryCount == 0)
+ return;
+
+ m_aControlPaths.realloc(nEntryCount);
+ css::uno::Sequence<sal_uInt32>* pAllPaths = m_aControlPaths.getArray();
+ for (const auto& rCurrentEntry : m_aSelectedEntries)
+ {
+ // first we collect the path in an array
+ ::std::vector< sal_uInt32 > aCurrentPath;
+
+ std::unique_ptr<weld::TreeIter> xLoop(pTreeBox->make_iterator(rCurrentEntry.get()));
+ while (pTreeBox->iter_compare(*xLoop, *pRoot) != 0)
+ {
+ aCurrentPath.push_back(pTreeBox->get_iter_index_in_parent(*xLoop));
+ bool bLoop = pTreeBox->iter_parent(*xLoop);
+ assert(bLoop && "OControlTransferData::buildPathFormat: invalid root or entry !"); (void)bLoop;
+ }
+
+ // then we can transfer it into css::uno::Sequence
+ Sequence<sal_uInt32>& rCurrentPath = *pAllPaths;
+ sal_Int32 nDepth = aCurrentPath.size();
+
+ rCurrentPath.realloc(nDepth);
+ sal_uInt32* pSeq = rCurrentPath.getArray();
+ sal_Int32 j,k;
+ for (j = nDepth - 1, k = 0; k<nDepth; --j, ++k)
+ pSeq[j] = aCurrentPath[k];
+ ++pAllPaths;
+ }
+ }
+
+ void OControlTransferData::buildListFromPath(const weld::TreeView* pTreeBox, const weld::TreeIter* pRoot)
+ {
+ ListBoxEntrySet().swap(m_aSelectedEntries);
+
+ for (const css::uno::Sequence<sal_uInt32>& rPaths : std::as_const(m_aControlPaths))
+ {
+ std::unique_ptr<weld::TreeIter> xSearch(pTreeBox->make_iterator(pRoot));
+ for (const sal_uInt32 nThisPath : rPaths)
+ pTreeBox->iter_nth_child(*xSearch, nThisPath);
+ m_aSelectedEntries.emplace(std::move(xSearch));
+ }
+ }
+
+ OControlExchange::OControlExchange( )
+ {
+ }
+
+ bool OControlExchange::GetData( const DataFlavor& _rFlavor, const OUString& rDestDoc )
+ {
+ const SotClipboardFormatId nFormatId = SotExchange::GetFormat( _rFlavor );
+
+ if ( getControlPathFormatId( ) == nFormatId )
+ {
+ // ugly. We have to pack all the info into one object
+ Sequence< Any > aCompleteInfo( 2 );
+ OSL_ENSURE( m_xFormsRoot.is(), "OLocalExchange::GetData: invalid forms root for this format!" );
+ aCompleteInfo.getArray()[ 0 ] <<= m_xFormsRoot;
+ aCompleteInfo.getArray()[ 1 ] <<= m_aControlPaths;
+
+ SetAny( Any( aCompleteInfo ) );
+ }
+ else if ( getHiddenControlModelsFormatId() == nFormatId )
+ {
+ // just need to transfer the models
+ SetAny( Any( m_aHiddenControlModels ) );
+ }
+ else
+ return OLocalExchange::GetData(_rFlavor, rDestDoc);
+
+ return true;
+ }
+
+ void OControlExchange::AddSupportedFormats()
+ {
+ if (m_bFocusEntry && !m_aSelectedEntries.empty())
+ AddFormat(getFieldExchangeFormatId());
+
+ if (m_aControlPaths.hasElements())
+ AddFormat(getControlPathFormatId());
+
+ if (m_aHiddenControlModels.hasElements())
+ AddFormat(getHiddenControlModelsFormatId());
+ }
+
+ SotClipboardFormatId OControlExchange::getControlPathFormatId()
+ {
+ static SotClipboardFormatId s_nFormat =
+ SotExchange::RegisterFormatName("application/x-openoffice;windows_formatname=\"svxform.ControlPathExchange\"");
+ DBG_ASSERT(static_cast<SotClipboardFormatId>(-1) != s_nFormat, "OControlExchange::getControlPathFormatId: bad exchange id!");
+ return s_nFormat;
+ }
+
+ SotClipboardFormatId OControlExchange::getHiddenControlModelsFormatId()
+ {
+ static SotClipboardFormatId s_nFormat =
+ SotExchange::RegisterFormatName("application/x-openoffice;windows_formatname=\"svxform.HiddenControlModelsExchange\"");
+ DBG_ASSERT(static_cast<SotClipboardFormatId>(-1) != s_nFormat, "OControlExchange::getHiddenControlModelsFormatId: bad exchange id!");
+ return s_nFormat;
+ }
+
+
+ SotClipboardFormatId OControlExchange::getFieldExchangeFormatId()
+ {
+ static SotClipboardFormatId s_nFormat =
+ SotExchange::RegisterFormatName("application/x-openoffice;windows_formatname=\"svxform.FieldNameExchange\"");
+ DBG_ASSERT(static_cast<SotClipboardFormatId>(-1) != s_nFormat, "OControlExchange::getFieldExchangeFormatId: bad exchange id!");
+ return s_nFormat;
+ }
+
+ //= OControlExchangeHelper
+ rtl::Reference<OLocalExchange> OControlExchangeHelper::createExchange() const
+ {
+ return new OControlExchange;
+ }
+
+ OLocalExchangeHelper::OLocalExchangeHelper()
+ {
+ }
+
+ OLocalExchangeHelper::~OLocalExchangeHelper()
+ {
+ implReset();
+ }
+
+ void OLocalExchangeHelper::copyToClipboard(const weld::Widget& rWidget) const
+ {
+ DBG_ASSERT( m_xTransferable.is(), "OLocalExchangeHelper::copyToClipboard: not prepared!" );
+ m_xTransferable->copyToClipboard(rWidget, OLocalExchange::GrantAccess());
+ }
+
+ void OLocalExchangeHelper::implReset()
+ {
+ if (m_xTransferable.is())
+ {
+ m_xTransferable->setClipboardListener( Link<OLocalExchange&,void>() );
+ m_xTransferable.clear();
+ }
+ }
+
+ void OLocalExchangeHelper::prepareDrag( )
+ {
+ DBG_ASSERT(!m_xTransferable.is() || !m_xTransferable->isDragging(), "OLocalExchangeHelper::prepareDrag: recursive DnD?");
+
+ implReset();
+ m_xTransferable = createExchange();
+ }
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/form/fmexpl.cxx b/svx/source/form/fmexpl.cxx
new file mode 100644
index 000000000..8ab6006bd
--- /dev/null
+++ b/svx/source/form/fmexpl.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 <svx/strings.hrc>
+#include <fmexpl.hxx>
+
+#include <helpids.h>
+#include <svx/svdobjkind.hxx>
+#include <svx/fmtools.hxx>
+#include <fmexch.hxx>
+
+#include <svx/svxids.hrc>
+
+#include <fmprop.hxx>
+#include <bitmaps.hlst>
+#include <svx/dialmgr.hxx>
+#include <com/sun/star/beans/XPropertySet.hpp>
+#include <com/sun/star/lang/XServiceInfo.hpp>
+#include <sfx2/objitem.hxx>
+
+#include <svx/fmshell.hxx>
+#include <comphelper/types.hxx>
+
+using namespace ::svxform;
+using namespace ::com::sun::star::uno;
+using namespace ::com::sun::star::form;
+using namespace ::com::sun::star::beans;
+using namespace ::com::sun::star::lang;
+using namespace ::com::sun::star::container;
+
+FmNavInsertedHint::FmNavInsertedHint( FmEntryData* pInsertedEntryData, sal_uInt32 nRelPos )
+ :pEntryData( pInsertedEntryData )
+ ,nPos( nRelPos )
+
+{
+}
+
+
+FmNavInsertedHint::~FmNavInsertedHint()
+{
+}
+
+
+
+FmNavModelReplacedHint::FmNavModelReplacedHint( FmEntryData* pAffectedEntryData )
+ :pEntryData( pAffectedEntryData )
+{
+}
+
+
+FmNavModelReplacedHint::~FmNavModelReplacedHint()
+{
+}
+
+FmNavRemovedHint::FmNavRemovedHint( FmEntryData* pRemovedEntryData )
+ :pEntryData( pRemovedEntryData )
+{
+}
+
+
+FmNavRemovedHint::~FmNavRemovedHint()
+{
+}
+
+FmNavNameChangedHint::FmNavNameChangedHint( FmEntryData* pData, const OUString& rNewName )
+ :pEntryData( pData )
+ ,aNewName( rNewName )
+{
+}
+
+
+FmNavNameChangedHint::~FmNavNameChangedHint()
+{
+}
+
+FmNavClearedHint::FmNavClearedHint()
+{
+}
+
+
+FmNavClearedHint::~FmNavClearedHint()
+{
+}
+
+
+FmEntryDataList::FmEntryDataList()
+{
+}
+
+
+FmEntryDataList::~FmEntryDataList()
+{
+}
+
+
+void FmEntryDataList::removeNoDelete( FmEntryData* pItem )
+{
+ auto it = std::find_if(maEntryDataList.begin(), maEntryDataList.end(),
+ [&pItem](const std::unique_ptr<FmEntryData>& rEntryData) { return rEntryData.get() == pItem; });
+ if (it != maEntryDataList.end())
+ {
+ it->release();
+ maEntryDataList.erase( it );
+ return;
+ }
+ assert(false);
+}
+
+
+void FmEntryDataList::insert( std::unique_ptr<FmEntryData> pItem, size_t Index )
+{
+ if ( Index < maEntryDataList.size() )
+ {
+ maEntryDataList.insert( maEntryDataList.begin() + Index, std::move(pItem) );
+ }
+ else
+ maEntryDataList.push_back( std::move(pItem) );
+}
+
+
+void FmEntryDataList::clear()
+{
+ maEntryDataList.clear();
+}
+
+
+FmEntryData::FmEntryData( FmEntryData* pParentData, const Reference< XInterface >& _rxIFace )
+ :pParent( pParentData )
+{
+ pChildList.reset( new FmEntryDataList() );
+
+ newObject( _rxIFace );
+}
+
+
+FmEntryData::~FmEntryData()
+{
+ pChildList->clear();
+}
+
+
+void FmEntryData::newObject( const css::uno::Reference< css::uno::XInterface >& _rxIFace )
+{
+ // do not just copy, normalize it
+ m_xNormalizedIFace.set( _rxIFace, UNO_QUERY );
+ m_xProperties.set(m_xNormalizedIFace, css::uno::UNO_QUERY);
+ m_xChild.set(m_xNormalizedIFace, css::uno::UNO_QUERY);
+}
+
+
+FmEntryData::FmEntryData( const FmEntryData& rEntryData )
+{
+ pChildList.reset( new FmEntryDataList() );
+ aText = rEntryData.GetText();
+ m_aNormalImage = rEntryData.GetNormalImage();
+ pParent = rEntryData.GetParent();
+
+ FmEntryData* pChildData;
+ size_t nEntryCount = rEntryData.GetChildList()->size();
+ for( size_t i = 0; i < nEntryCount; i++ )
+ {
+ pChildData = rEntryData.GetChildList()->at( i );
+ std::unique_ptr<FmEntryData> pNewChildData = pChildData->Clone();
+ pChildList->insert( std::move(pNewChildData), size_t(-1) );
+ }
+
+ m_xNormalizedIFace = rEntryData.m_xNormalizedIFace;
+ m_xProperties = rEntryData.m_xProperties;
+ m_xChild = rEntryData.m_xChild;
+}
+
+
+
+bool FmEntryData::IsEqualWithoutChildren( FmEntryData* pEntryData )
+{
+ if(this == pEntryData)
+ return true;
+
+ if( !pEntryData )
+ return false;
+
+ if( aText != pEntryData->GetText() )
+ return false;
+
+ if( !pEntryData->GetParent() && pParent )
+ return false;
+
+ if( pEntryData->GetParent() && !pParent )
+ return false;
+
+ if( !pEntryData->GetParent() && !pParent )
+ return true;
+
+ if( !pParent->IsEqualWithoutChildren(pEntryData->GetParent()) )
+ return false;
+
+ return true;
+}
+
+FmFormData::FmFormData(const Reference< XForm >& _rxForm, FmFormData* _pParent)
+ : FmEntryData(_pParent, _rxForm)
+ , m_xForm(_rxForm)
+{
+ // set images
+ m_aNormalImage = RID_SVXBMP_FORM;
+
+ // set title
+ if (m_xForm.is())
+ {
+ Reference< XPropertySet > xSet(m_xForm, UNO_QUERY);
+ if (xSet.is())
+ {
+ OUString aEntryName(::comphelper::getString(xSet->getPropertyValue( FM_PROP_NAME )));
+ SetText(aEntryName);
+ }
+ }
+ else
+ SetText( OUString() );
+}
+
+FmFormData::~FmFormData()
+{
+}
+
+FmFormData::FmFormData( const FmFormData& rFormData )
+ :FmEntryData( rFormData )
+{
+ m_xForm = rFormData.GetFormIface();
+}
+
+
+std::unique_ptr<FmEntryData> FmFormData::Clone()
+{
+ return std::unique_ptr<FmEntryData>(new FmFormData( *this ));
+}
+
+
+bool FmFormData::IsEqualWithoutChildren( FmEntryData* pEntryData )
+{
+ if(this == pEntryData)
+ return true;
+ FmFormData* pFormData = dynamic_cast<FmFormData*>(pEntryData);
+ if( !pFormData )
+ return false;
+ if( m_xForm.get() != pFormData->GetFormIface().get() )
+ return false;
+
+ return FmEntryData::IsEqualWithoutChildren( pFormData );
+}
+
+FmControlData::FmControlData(const Reference< XFormComponent >& _rxComponent, FmFormData* _pParent)
+: FmEntryData( _pParent, _rxComponent ),
+ m_xFormComponent( _rxComponent )
+{
+
+ // set images
+ m_aNormalImage = GetImage();
+
+
+ // set title
+ Reference< XPropertySet > xSet(m_xFormComponent, UNO_QUERY);
+ if( xSet.is() )
+ {
+ SetText( ::comphelper::getString(xSet->getPropertyValue( FM_PROP_NAME )));
+ }
+}
+
+
+FmControlData::~FmControlData()
+{
+}
+
+
+FmControlData::FmControlData( const FmControlData& rControlData )
+ :FmEntryData( rControlData )
+{
+ m_xFormComponent = rControlData.GetFormComponent();
+}
+
+
+std::unique_ptr<FmEntryData> FmControlData::Clone()
+{
+ return std::unique_ptr<FmEntryData>(new FmControlData( *this ));
+}
+
+
+OUString FmControlData::GetImage() const
+{
+ // Default-Image
+ OUString aImage(RID_SVXBMP_CONTROL);
+
+ Reference< XServiceInfo > xInfo( m_xFormComponent, UNO_QUERY );
+ if (!m_xFormComponent.is())
+ return aImage;
+
+
+ // Spezielle Control-Images
+ SdrObjKind nObjectType = getControlTypeByObject(xInfo);
+ switch (nObjectType)
+ {
+ case SdrObjKind::FormButton:
+ aImage = RID_SVXBMP_BUTTON;
+ break;
+
+ case SdrObjKind::FormFixedText:
+ aImage = RID_SVXBMP_FIXEDTEXT;
+ break;
+
+ case SdrObjKind::FormEdit:
+ aImage = RID_SVXBMP_EDITBOX;
+ break;
+
+ case SdrObjKind::FormRadioButton:
+ aImage = RID_SVXBMP_RADIOBUTTON;
+ break;
+
+ case SdrObjKind::FormCheckbox:
+ aImage = RID_SVXBMP_CHECKBOX;
+ break;
+
+ case SdrObjKind::FormListbox:
+ aImage = RID_SVXBMP_LISTBOX;
+ break;
+
+ case SdrObjKind::FormCombobox:
+ aImage = RID_SVXBMP_COMBOBOX;
+ break;
+
+ case SdrObjKind::FormNavigationBar:
+ aImage = RID_SVXBMP_NAVIGATIONBAR;
+ break;
+
+ case SdrObjKind::FormGroupBox:
+ aImage = RID_SVXBMP_GROUPBOX;
+ break;
+
+ case SdrObjKind::FormImageButton:
+ aImage = RID_SVXBMP_IMAGEBUTTON;
+ break;
+
+ case SdrObjKind::FormFileControl:
+ aImage = RID_SVXBMP_FILECONTROL;
+ break;
+
+ case SdrObjKind::FormHidden:
+ aImage = RID_SVXBMP_HIDDEN;
+ break;
+
+ case SdrObjKind::FormDateField:
+ aImage = RID_SVXBMP_DATEFIELD;
+ break;
+
+ case SdrObjKind::FormTimeField:
+ aImage = RID_SVXBMP_TIMEFIELD;
+ break;
+
+ case SdrObjKind::FormNumericField:
+ aImage = RID_SVXBMP_NUMERICFIELD;
+ break;
+
+ case SdrObjKind::FormCurrencyField:
+ aImage = RID_SVXBMP_CURRENCYFIELD;
+ break;
+
+ case SdrObjKind::FormPatternField:
+ aImage = RID_SVXBMP_PATTERNFIELD;
+ break;
+
+ case SdrObjKind::FormImageControl:
+ aImage = RID_SVXBMP_IMAGECONTROL;
+ break;
+
+ case SdrObjKind::FormFormattedField:
+ aImage = RID_SVXBMP_FORMATTEDFIELD;
+ break;
+
+ case SdrObjKind::FormGrid:
+ aImage = RID_SVXBMP_GRID;
+ break;
+
+ case SdrObjKind::FormScrollbar:
+ aImage = RID_SVXBMP_SCROLLBAR;
+ break;
+
+ case SdrObjKind::FormSpinButton:
+ aImage = RID_SVXBMP_SPINBUTTON;
+ break;
+
+ default:;
+ }
+
+ return aImage;
+}
+
+bool FmControlData::IsEqualWithoutChildren( FmEntryData* pEntryData )
+{
+ if(this == pEntryData)
+ return true;
+
+ FmControlData* pControlData = dynamic_cast<FmControlData*>(pEntryData);
+ if( !pControlData )
+ return false;
+
+ if( m_xFormComponent.get() != pControlData->GetFormComponent().get() )
+ return false;
+
+ return FmEntryData::IsEqualWithoutChildren( pControlData );
+}
+
+void FmControlData::ModelReplaced(const Reference< XFormComponent >& _rxNew)
+{
+ m_xFormComponent = _rxNew;
+ newObject( m_xFormComponent );
+ // set images anew
+ m_aNormalImage = GetImage();
+}
+
+namespace svxform
+{
+
+ NavigatorFrame::NavigatorFrame( SfxBindings* _pBindings, SfxChildWindow* _pMgr,
+ vcl::Window* _pParent )
+ : SfxDockingWindow(_pBindings, _pMgr, _pParent, "FormNavigator", "svx/ui/formnavigator.ui")
+ , SfxControllerItem( SID_FM_FMEXPLORER_CONTROL, *_pBindings )
+ , m_xNavigatorTree(new NavigatorTree(m_xBuilder->weld_tree_view("treeview")))
+ {
+ SetHelpId( HID_FORM_NAVIGATOR_WIN );
+
+ SetText( SvxResId(RID_STR_FMEXPLORER) );
+ SfxDockingWindow::SetFloatingSize( Size(200,200) );
+ }
+
+ NavigatorFrame::~NavigatorFrame()
+ {
+ disposeOnce();
+ }
+
+ void NavigatorFrame::dispose()
+ {
+ m_xNavigatorTree.reset();
+ ::SfxControllerItem::dispose();
+ SfxDockingWindow::dispose();
+ }
+
+ void NavigatorFrame::UpdateContent( FmFormShell* pFormShell )
+ {
+ m_xNavigatorTree->UpdateContent(pFormShell);
+ }
+
+ void NavigatorFrame::StateChangedAtToolBoxControl( sal_uInt16 nSID, SfxItemState eState, const SfxPoolItem* pState )
+ {
+ if( !pState || SID_FM_FMEXPLORER_CONTROL != nSID )
+ return;
+
+ if( eState >= SfxItemState::DEFAULT )
+ {
+ FmFormShell* pShell = dynamic_cast<FmFormShell*>( static_cast<const SfxObjectItem*>(pState)->GetShell() );
+ UpdateContent( pShell );
+ }
+ else
+ UpdateContent( nullptr );
+ }
+
+ void NavigatorFrame::GetFocus()
+ {
+ if (m_xNavigatorTree )
+ m_xNavigatorTree->GrabFocus();
+ else
+ SfxDockingWindow::GetFocus();
+ }
+
+ bool NavigatorFrame::Close()
+ {
+ UpdateContent( nullptr );
+ return SfxDockingWindow::Close();
+ }
+
+ void NavigatorFrame::FillInfo( SfxChildWinInfo& rInfo ) const
+ {
+ SfxDockingWindow::FillInfo( rInfo );
+ rInfo.bVisible = false;
+ }
+
+ Size NavigatorFrame::CalcDockingSize( SfxChildAlignment eAlign )
+ {
+ if ( ( eAlign == SfxChildAlignment::TOP ) || ( eAlign == SfxChildAlignment::BOTTOM ) )
+ return Size();
+
+ return SfxDockingWindow::CalcDockingSize( eAlign );
+ }
+
+ SfxChildAlignment NavigatorFrame::CheckAlignment( SfxChildAlignment _eActAlign, SfxChildAlignment _eAlign )
+ {
+ if ( ( _eAlign == SfxChildAlignment::LEFT ) || ( _eAlign == SfxChildAlignment::RIGHT ) || ( _eAlign == SfxChildAlignment::NOALIGNMENT ) )
+ return _eAlign;
+ return _eActAlign;
+ }
+
+ SFX_IMPL_DOCKINGWINDOW( NavigatorFrameManager, SID_FM_SHOW_FMEXPLORER )
+
+ NavigatorFrameManager::NavigatorFrameManager( vcl::Window* _pParent, sal_uInt16 _nId,
+ SfxBindings* _pBindings, SfxChildWinInfo* _pInfo )
+ :SfxChildWindow( _pParent, _nId )
+ {
+ SetWindow( VclPtr<NavigatorFrame>::Create( _pBindings, this, _pParent ) );
+ static_cast<SfxDockingWindow*>(GetWindow())->Initialize( _pInfo );
+ }
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/form/fmmodel.cxx b/svx/source/form/fmmodel.cxx
new file mode 100644
index 000000000..e518319aa
--- /dev/null
+++ b/svx/source/form/fmmodel.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 <fmundo.hxx>
+#include <fmdocumentclassification.hxx>
+#include <fmcontrollayout.hxx>
+
+#include <com/sun/star/form/XForms.hpp>
+#include <svx/fmmodel.hxx>
+#include <svx/fmpage.hxx>
+
+#include <sfx2/objsh.hxx>
+#include <osl/diagnose.h>
+
+#include <optional>
+
+using ::com::sun::star::uno::Reference;
+using ::com::sun::star::container::XNameContainer;
+using namespace svxform;
+
+
+struct FmFormModelImplData
+{
+ rtl::Reference<FmXUndoEnvironment> mxUndoEnv;
+ bool bOpenInDesignIsDefaulted;
+ std::optional<bool> aControlsUseRefDevice;
+
+ FmFormModelImplData()
+ :bOpenInDesignIsDefaulted( true )
+ {
+ }
+};
+
+FmFormModel::FmFormModel(
+ SfxItemPool* pPool,
+ SfxObjectShell* pPers)
+: SdrModel(
+ pPool,
+ pPers)
+ , m_pObjShell(nullptr)
+ , m_bOpenInDesignMode(false)
+ , m_bAutoControlFocus(false)
+{
+ m_pImpl.reset( new FmFormModelImplData );
+ m_pImpl->mxUndoEnv = new FmXUndoEnvironment(*this);
+}
+
+FmFormModel::~FmFormModel()
+{
+ if (m_pObjShell && m_pImpl->mxUndoEnv->IsListening(*m_pObjShell))
+ SetObjectShell(nullptr);
+
+ ClearUndoBuffer();
+ // minimum limit for undos
+ SetMaxUndoActionCount(1);
+}
+
+rtl::Reference<SdrPage> FmFormModel::AllocPage(bool bMasterPage)
+{
+ return new FmFormPage(*this, bMasterPage);
+}
+
+void FmFormModel::InsertPage(SdrPage* pPage, sal_uInt16 nPos)
+{
+ // hack for as long as the method is internal
+ if (m_pObjShell && !m_pImpl->mxUndoEnv->IsListening( *m_pObjShell ))
+ SetObjectShell(m_pObjShell);
+
+ SdrModel::InsertPage( pPage, nPos );
+}
+
+rtl::Reference<SdrPage> FmFormModel::RemovePage(sal_uInt16 nPgNum)
+{
+ FmFormPage* pToBeRemovedPage = dynamic_cast< FmFormPage* >( GetPage( nPgNum ) );
+ OSL_ENSURE( pToBeRemovedPage, "FmFormModel::RemovePage: *which page*?" );
+
+ if ( pToBeRemovedPage )
+ {
+ Reference< XNameContainer > xForms( pToBeRemovedPage->GetForms( false ) );
+ if ( xForms.is() )
+ m_pImpl->mxUndoEnv->RemoveForms( xForms );
+ }
+
+ rtl::Reference<FmFormPage> pRemovedPage = static_cast<FmFormPage*>(SdrModel::RemovePage(nPgNum).get());
+ OSL_ENSURE( pRemovedPage == pToBeRemovedPage, "FmFormModel::RemovePage: inconsistency!" );
+ return pRemovedPage;
+}
+
+void FmFormModel::InsertMasterPage(SdrPage* pPage, sal_uInt16 nPos)
+{
+ // hack for as long as the method is internal
+ if (m_pObjShell && !m_pImpl->mxUndoEnv->IsListening( *m_pObjShell ))
+ SetObjectShell(m_pObjShell);
+
+ SdrModel::InsertMasterPage(pPage, nPos);
+}
+
+rtl::Reference<SdrPage> FmFormModel::RemoveMasterPage(sal_uInt16 nPgNum)
+{
+ rtl::Reference<FmFormPage> pPage = static_cast<FmFormPage*>(SdrModel::RemoveMasterPage(nPgNum).get());
+
+ if ( pPage )
+ {
+ Reference< XNameContainer > xForms( pPage->GetForms( false ) );
+ if ( xForms.is() )
+ m_pImpl->mxUndoEnv->RemoveForms( xForms );
+ }
+
+ return pPage;
+}
+
+
+void FmFormModel::implSetOpenInDesignMode( bool _bOpenDesignMode )
+{
+ if( _bOpenDesignMode != m_bOpenInDesignMode )
+ {
+ m_bOpenInDesignMode = _bOpenDesignMode;
+
+ if ( m_pObjShell )
+ m_pObjShell->SetModified();
+ }
+ // no matter if we really did it or not - from now on, it does not count as defaulted anymore
+ m_pImpl->bOpenInDesignIsDefaulted = false;
+}
+
+
+void FmFormModel::SetOpenInDesignMode( bool bOpenDesignMode )
+{
+ implSetOpenInDesignMode( bOpenDesignMode );
+}
+
+
+bool FmFormModel::OpenInDesignModeIsDefaulted( )
+{
+ return m_pImpl->bOpenInDesignIsDefaulted;
+}
+
+
+bool FmFormModel::ControlsUseRefDevice() const
+{
+ if ( !m_pImpl->aControlsUseRefDevice )
+ {
+ DocumentType eDocType = eUnknownDocumentType;
+ if ( m_pObjShell )
+ eDocType = DocumentClassification::classifyHostDocument( m_pObjShell->GetModel() );
+ m_pImpl->aControlsUseRefDevice = ControlLayouter::useDocumentReferenceDevice(eDocType);
+ }
+ return *m_pImpl->aControlsUseRefDevice;
+}
+
+
+void FmFormModel::SetAutoControlFocus( bool _bAutoControlFocus )
+{
+ if( _bAutoControlFocus != m_bAutoControlFocus )
+ {
+ m_bAutoControlFocus = _bAutoControlFocus;
+ m_pObjShell->SetModified();
+ }
+}
+
+
+void FmFormModel::SetObjectShell( SfxObjectShell* pShell )
+{
+ if (pShell == m_pObjShell)
+ return;
+
+ if (m_pObjShell)
+ {
+ m_pImpl->mxUndoEnv->EndListening( *this );
+ m_pImpl->mxUndoEnv->EndListening( *m_pObjShell );
+ }
+
+ m_pObjShell = pShell;
+
+ if (m_pObjShell)
+ {
+ m_pImpl->mxUndoEnv->SetReadOnly( m_pObjShell->IsReadOnly() || m_pObjShell->IsReadOnlyUI(), FmXUndoEnvironment::Accessor() );
+
+ if (!m_pImpl->mxUndoEnv->IsReadOnly())
+ m_pImpl->mxUndoEnv->StartListening(*this);
+
+ m_pImpl->mxUndoEnv->StartListening( *m_pObjShell );
+ }
+}
+
+
+FmXUndoEnvironment& FmFormModel::GetUndoEnv()
+{
+ return *m_pImpl->mxUndoEnv;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/form/fmobj.cxx b/svx/source/form/fmobj.cxx
new file mode 100644
index 000000000..e337f79d7
--- /dev/null
+++ b/svx/source/form/fmobj.cxx
@@ -0,0 +1,653 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <fmobj.hxx>
+#include <fmprop.hxx>
+#include <fmvwimp.hxx>
+#include <fmpgeimp.hxx>
+#include <o3tl/string_view.hxx>
+#include <svx/fmview.hxx>
+#include <svx/fmpage.hxx>
+#include <svx/svdovirt.hxx>
+#include <svx/fmmodel.hxx>
+
+#include <com/sun/star/awt/XDevice.hpp>
+#include <com/sun/star/beans/XPropertySet.hpp>
+#include <com/sun/star/form/Forms.hpp>
+#include <com/sun/star/script/XEventAttacherManager.hpp>
+#include <svx/fmtools.hxx>
+
+#include <comphelper/property.hxx>
+#include <comphelper/processfactory.hxx>
+#include <toolkit/awt/vclxdevice.hxx>
+#include <tools/debug.hxx>
+#include <tools/diagnose_ex.h>
+
+using namespace ::com::sun::star::io;
+using namespace ::com::sun::star::uno;
+using namespace ::com::sun::star::awt;
+using namespace ::com::sun::star::lang;
+using namespace ::com::sun::star::util;
+using namespace ::com::sun::star::form;
+using namespace ::com::sun::star::beans;
+using namespace ::com::sun::star::script;
+using namespace ::com::sun::star::container;
+using namespace ::svxform;
+
+
+FmFormObj::FmFormObj(
+ SdrModel& rSdrModel,
+ const OUString& rModelName)
+: SdrUnoObj(rSdrModel, rModelName)
+ ,m_nPos(-1)
+ ,m_pLastKnownRefDevice(nullptr)
+{
+ // normally, this is done in SetUnoControlModel, but if the call happened in the base class ctor,
+ // then our incarnation of it was not called (since we were not constructed at this time).
+ impl_checkRefDevice_nothrow( true );
+}
+
+FmFormObj::FmFormObj(SdrModel& rSdrModel)
+: SdrUnoObj(rSdrModel, "")
+ ,m_nPos(-1)
+ ,m_pLastKnownRefDevice(nullptr)
+{
+ // Stuff that old SetModel also did:
+ impl_checkRefDevice_nothrow();
+}
+
+FmFormObj::FmFormObj(SdrModel& rSdrModel, FmFormObj const & rSource)
+: SdrUnoObj(rSdrModel, rSource)
+ ,m_nPos(-1)
+ ,m_pLastKnownRefDevice(nullptr)
+{
+ // Stuff that old SetModel also did:
+ impl_checkRefDevice_nothrow();
+
+ // If UnoControlModel is part of an event environment,
+ // events may assigned to it.
+ Reference< XFormComponent > xContent(rSource.xUnoControlModel, UNO_QUERY);
+ if (xContent.is())
+ {
+ Reference< XEventAttacherManager > xManager(xContent->getParent(), UNO_QUERY);
+ Reference< XIndexAccess > xManagerAsIndex(xManager, UNO_QUERY);
+ if (xManagerAsIndex.is())
+ {
+ sal_Int32 nPos = getElementPos( xManagerAsIndex, xContent );
+ if ( nPos >= 0 )
+ aEvts = xManager->getScriptEvents( nPos );
+ }
+ }
+ else
+ aEvts = rSource.aEvts;
+
+ Reference< XChild > xSourceAsChild(rSource.GetUnoControlModel(), UNO_QUERY);
+ if (!xSourceAsChild.is())
+ return;
+
+ Reference< XInterface > xSourceContainer = xSourceAsChild->getParent();
+
+ m_xEnvironmentHistory = css::form::Forms::create( comphelper::getProcessComponentContext() );
+
+ ensureModelEnv(xSourceContainer, m_xEnvironmentHistory);
+ m_aEventsHistory = aEvts;
+ // if we were clone there was a call to operator=, so aEvts are exactly the events we need here...
+}
+
+FmFormObj::~FmFormObj()
+{
+
+ if (m_xEnvironmentHistory.is())
+ m_xEnvironmentHistory->dispose();
+
+ m_xEnvironmentHistory = nullptr;
+ m_aEventsHistory.realloc(0);
+}
+
+
+void FmFormObj::SetObjEnv(const Reference< XIndexContainer > & xForm, const sal_Int32 nIdx,
+ const Sequence< ScriptEventDescriptor >& rEvts)
+{
+ m_xParent = xForm;
+ aEvts = rEvts;
+ m_nPos = nIdx;
+}
+
+
+void FmFormObj::ClearObjEnv()
+{
+ m_xParent.clear();
+ aEvts.realloc( 0 );
+ m_nPos = -1;
+}
+
+
+void FmFormObj::impl_checkRefDevice_nothrow( bool _force )
+{
+ const FmFormModel* pFormModel = dynamic_cast<FmFormModel*>(&getSdrModelFromSdrObject());
+ if ( !pFormModel || !pFormModel->ControlsUseRefDevice() )
+ return;
+
+ OutputDevice* pCurrentRefDevice = pFormModel->GetRefDevice();
+ if ( ( m_pLastKnownRefDevice.get() == pCurrentRefDevice ) && !_force )
+ return;
+
+ Reference< XControlModel > xControlModel( GetUnoControlModel() );
+ if ( !xControlModel.is() )
+ return;
+
+ m_pLastKnownRefDevice = pCurrentRefDevice;
+ if ( !m_pLastKnownRefDevice )
+ return;
+
+ try
+ {
+ Reference< XPropertySet > xModelProps( GetUnoControlModel(), UNO_QUERY_THROW );
+ Reference< XPropertySetInfo > xPropertyInfo( xModelProps->getPropertySetInfo(), UNO_SET_THROW );
+
+ static constexpr OUStringLiteral sRefDevicePropName = u"ReferenceDevice";
+ if ( xPropertyInfo->hasPropertyByName( sRefDevicePropName ) )
+ {
+ rtl::Reference<VCLXDevice> pUnoRefDevice = new VCLXDevice;
+ pUnoRefDevice->SetOutputDevice( m_pLastKnownRefDevice );
+ Reference< XDevice > xRefDevice( pUnoRefDevice );
+ xModelProps->setPropertyValue( sRefDevicePropName, Any( xRefDevice ) );
+ }
+ }
+ catch( const Exception& )
+ {
+ DBG_UNHANDLED_EXCEPTION("svx");
+ }
+}
+
+
+void FmFormObj::impl_isolateControlModel_nothrow()
+{
+ try
+ {
+ Reference< XChild > xControlModel( GetUnoControlModel(), UNO_QUERY );
+ if ( xControlModel.is() )
+ {
+ Reference< XIndexContainer> xParent( xControlModel->getParent(), UNO_QUERY );
+ if ( xParent.is() )
+ {
+ sal_Int32 nPos = getElementPos( xParent, xControlModel );
+ xParent->removeByIndex( nPos );
+ }
+ }
+ }
+ catch( const Exception& )
+ {
+ DBG_UNHANDLED_EXCEPTION("svx");
+ }
+}
+
+
+void FmFormObj::handlePageChange(SdrPage* pOldPage, SdrPage* pNewPage)
+{
+ FmFormPage* pOldFormPage(dynamic_cast< FmFormPage* >(getSdrPageFromSdrObject()));
+ if ( pOldFormPage )
+ pOldFormPage->GetImpl().formObjectRemoved( *this );
+
+ FmFormPage* pNewFormPage = dynamic_cast<FmFormPage*>( pNewPage );
+ if ( !pNewFormPage )
+ {
+ // Maybe it makes sense to create an environment history here : if somebody set's our page to NULL, and we have a valid page before,
+ // me may want to remember our place within the old page. For this we could create a new m_xEnvironmentHistory to store it.
+ // So the next SetPage with a valid new page would restore that environment within the new page.
+ // But for the original Bug (#57300#) we don't need that, so I omit it here. Maybe this will be implemented later.
+ impl_isolateControlModel_nothrow();
+ SdrUnoObj::handlePageChange(pOldPage, pNewPage);
+ return;
+ }
+
+ Reference< css::form::XForms > xNewPageForms = pNewFormPage->GetForms();
+ Reference< XIndexContainer > xNewParent;
+ Sequence< ScriptEventDescriptor> aNewEvents;
+
+ // calc the new parent for my model (within the new page's forms hierarchy)
+ // do we have a history ? (from :Clone)
+ if ( m_xEnvironmentHistory.is() )
+ {
+ // the element in m_xEnvironmentHistory which is equivalent to my new parent (which (perhaps) has to be created within pNewPage->GetForms)
+ // is the right-most element in the tree.
+ Reference< XIndexContainer > xRightMostLeaf( m_xEnvironmentHistory, UNO_QUERY_THROW );
+ try
+ {
+ while ( xRightMostLeaf->getCount() )
+ {
+ xRightMostLeaf.set(
+ xRightMostLeaf->getByIndex( xRightMostLeaf->getCount() - 1 ),
+ UNO_QUERY_THROW
+ );
+ }
+
+ xNewParent.set( ensureModelEnv( xRightMostLeaf, xNewPageForms ), UNO_QUERY_THROW );
+
+ // we successfully cloned the environment in m_xEnvironmentHistory, so we can use m_aEventsHistory
+ // (which describes the events of our model at the moment m_xEnvironmentHistory was created)
+ aNewEvents = m_aEventsHistory;
+ }
+ catch( const Exception& )
+ {
+ DBG_UNHANDLED_EXCEPTION("svx");
+ }
+ }
+
+ if ( !xNewParent.is() )
+ {
+ // are we a valid part of our current page forms ?
+ Reference< XIndexContainer > xOldForms;
+ if ( pOldFormPage )
+ xOldForms.set( pOldFormPage->GetForms(), UNO_QUERY_THROW );
+
+ if ( xOldForms.is() )
+ {
+ // search (upward from our model) for xOldForms
+ Reference< XChild > xSearch( GetUnoControlModel(), UNO_QUERY );
+ while (xSearch.is())
+ {
+ if ( xSearch == xOldForms )
+ break;
+ xSearch.set( xSearch->getParent(), UNO_QUERY );
+ }
+ if ( xSearch.is() ) // implies xSearch == xOldForms, which means we're a valid part of our current page forms hierarchy
+ {
+ Reference< XChild > xMeAsChild( GetUnoControlModel(), UNO_QUERY );
+ xNewParent.set( ensureModelEnv( xMeAsChild->getParent(), xNewPageForms ), UNO_QUERY );
+
+ if ( xNewParent.is() )
+ {
+ try
+ {
+ // transfer the events from our (model's) parent to the new (model's) parent, too
+ Reference< XEventAttacherManager > xEventManager(xMeAsChild->getParent(), UNO_QUERY);
+ Reference< XIndexAccess > xManagerAsIndex(xEventManager, UNO_QUERY);
+ if (xManagerAsIndex.is())
+ {
+ sal_Int32 nPos = getElementPos(xManagerAsIndex, xMeAsChild);
+ if (nPos >= 0)
+ aNewEvents = xEventManager->getScriptEvents(nPos);
+ }
+ else
+ aNewEvents = aEvts;
+ }
+ catch( const Exception& )
+ {
+ DBG_UNHANDLED_EXCEPTION("svx");
+ }
+ }
+ }
+ }
+ }
+
+ // now set the page
+ SdrUnoObj::handlePageChange(pOldPage, pNewPage);
+
+ // place my model within the new parent container
+ if (xNewParent.is())
+ {
+ Reference< XFormComponent > xMeAsFormComp(GetUnoControlModel(), UNO_QUERY);
+ if (xMeAsFormComp.is())
+ {
+ // check if I have another parent (and remove me, if necessary)
+ Reference< XIndexContainer > xOldParent(xMeAsFormComp->getParent(), UNO_QUERY);
+ if (xOldParent.is())
+ {
+ sal_Int32 nPos = getElementPos(xOldParent, xMeAsFormComp);
+ if (nPos > -1)
+ xOldParent->removeByIndex(nPos);
+ }
+
+ // and insert into the new container
+ xNewParent->insertByIndex(xNewParent->getCount(), Any(xMeAsFormComp));
+
+ // transfer the events
+ if (aNewEvents.hasElements())
+ {
+ try
+ {
+ Reference< XEventAttacherManager > xEventManager(xNewParent, UNO_QUERY);
+ Reference< XIndexAccess > xManagerAsIndex(xEventManager, UNO_QUERY);
+ if (xManagerAsIndex.is())
+ {
+ sal_Int32 nPos = getElementPos(xManagerAsIndex, xMeAsFormComp);
+ DBG_ASSERT(nPos >= 0, "FmFormObj::SetPage : inserted but not present ?");
+ xEventManager->registerScriptEvents(nPos, aNewEvents);
+ }
+ }
+ catch( const Exception& )
+ {
+ DBG_UNHANDLED_EXCEPTION("svx");
+ }
+
+ }
+ }
+ }
+
+ // delete my history
+ if (m_xEnvironmentHistory.is())
+ m_xEnvironmentHistory->dispose();
+
+ m_xEnvironmentHistory = nullptr;
+ m_aEventsHistory.realloc(0);
+
+ pNewFormPage->GetImpl().formObjectInserted( *this );
+}
+
+SdrInventor FmFormObj::GetObjInventor() const
+{
+ return SdrInventor::FmForm;
+}
+
+SdrObjKind FmFormObj::GetObjIdentifier() const
+{
+ return SdrObjKind::UNO;
+}
+
+FmFormObj* FmFormObj::CloneSdrObject(SdrModel& rTargetModel) const
+{
+ return new FmFormObj(rTargetModel, *this);
+}
+
+void FmFormObj::NbcReformatText()
+{
+ impl_checkRefDevice_nothrow();
+ SdrUnoObj::NbcReformatText();
+}
+
+
+namespace
+{
+ OUString lcl_getFormComponentAccessPath(const Reference< XInterface >& _xElement, Reference< XInterface >& _rTopLevelElement)
+ {
+ Reference< css::form::XFormComponent> xChild(_xElement, UNO_QUERY);
+ Reference< css::container::XIndexAccess> xParent;
+ if (xChild.is())
+ xParent.set(xChild->getParent(), UNO_QUERY);
+
+ // while the current content is a form
+ OUString sReturn;
+ while (xChild.is())
+ {
+ // get the content's relative pos within its parent container
+ sal_Int32 nPos = getElementPos(xParent, xChild);
+
+ // prepend this current relative pos
+ OUString sCurrentIndex = OUString::number(nPos);
+ if (!sReturn.isEmpty())
+ {
+ sCurrentIndex += "\\" + sReturn;
+ }
+
+ sReturn = sCurrentIndex;
+
+ // travel up
+ xChild.set(xParent, css::uno::UNO_QUERY);
+ if (xChild.is())
+ xParent.set(xChild->getParent(), UNO_QUERY);
+ }
+
+ _rTopLevelElement = xParent;
+ return sReturn;
+ }
+}
+
+
+Reference< XInterface > FmFormObj::ensureModelEnv(const Reference< XInterface > & _rSourceContainer, const Reference<css::form::XForms>& _rTopLevelDestContainer)
+{
+ Reference< XInterface > xTopLevelSource;
+ OUString sAccessPath = lcl_getFormComponentAccessPath(_rSourceContainer, xTopLevelSource);
+ if (!xTopLevelSource.is())
+ // something went wrong, maybe _rSourceContainer isn't part of a valid forms hierarchy
+ return Reference< XInterface > ();
+
+ Reference< XIndexContainer > xDestContainer(_rTopLevelDestContainer, UNO_QUERY_THROW);
+ Reference< XIndexContainer > xSourceContainer(xTopLevelSource, UNO_QUERY);
+ DBG_ASSERT(xSourceContainer.is(), "FmFormObj::ensureModelEnv : the top level source is invalid !");
+
+ sal_Int32 nTokIndex = 0;
+ do
+ {
+ std::u16string_view aToken = o3tl::getToken(sAccessPath, 0, '\\', nTokIndex );
+ sal_uInt16 nIndex = static_cast<sal_uInt16>(o3tl::toInt32(aToken));
+
+ // get the DSS of the source form (we have to find an equivalent for)
+ DBG_ASSERT(nIndex<xSourceContainer->getCount(), "FmFormObj::ensureModelEnv : invalid access path !");
+ Reference< XPropertySet > xSourceForm;
+ xSourceContainer->getByIndex(nIndex) >>= xSourceForm;
+ DBG_ASSERT(xSourceForm.is(), "FmFormObj::ensureModelEnv : invalid source form !");
+
+ Any aSrcCursorSource, aSrcCursorSourceType, aSrcDataSource;
+ DBG_ASSERT(::comphelper::hasProperty(FM_PROP_COMMAND, xSourceForm) && ::comphelper::hasProperty(FM_PROP_COMMANDTYPE, xSourceForm)
+ && ::comphelper::hasProperty(FM_PROP_DATASOURCE, xSourceForm), "FmFormObj::ensureModelEnv : invalid access path or invalid form (missing props) !");
+ // the parent access path should refer to a row set
+ try
+ {
+ aSrcCursorSource = xSourceForm->getPropertyValue(FM_PROP_COMMAND);
+ aSrcCursorSourceType = xSourceForm->getPropertyValue(FM_PROP_COMMANDTYPE);
+ aSrcDataSource = xSourceForm->getPropertyValue(FM_PROP_DATASOURCE);
+ }
+ catch(Exception&)
+ {
+ OSL_FAIL("FmFormObj::ensureModelEnv : could not retrieve a source DSS !");
+ }
+
+
+ // calc the number of (source) form siblings with the same DSS
+ Reference< XPropertySet > xCurrentSourceForm, xCurrentDestForm;
+ sal_Int16 nCurrentSourceIndex = 0;
+ sal_Int32 nCurrentDestIndex = 0;
+ while (nCurrentSourceIndex <= nIndex)
+ {
+ bool bEqualDSS = false;
+ while (!bEqualDSS) // (we don't have to check nCurrentSourceIndex here : it's bound by nIndex)
+ {
+ xSourceContainer->getByIndex(nCurrentSourceIndex) >>= xCurrentSourceForm;
+ DBG_ASSERT(xCurrentSourceForm.is(), "FmFormObj::ensureModelEnv : invalid form ancestor (2) !");
+ bEqualDSS = false;
+ if (::comphelper::hasProperty(FM_PROP_DATASOURCE, xCurrentSourceForm))
+ { // it is a form
+ try
+ {
+ if ( xCurrentSourceForm->getPropertyValue(FM_PROP_COMMAND) == aSrcCursorSource
+ && xCurrentSourceForm->getPropertyValue(FM_PROP_COMMANDTYPE) == aSrcCursorSourceType
+ && xCurrentSourceForm->getPropertyValue(FM_PROP_DATASOURCE) == aSrcDataSource
+ )
+ {
+ bEqualDSS = true;
+ }
+ }
+ catch(Exception&)
+ {
+ TOOLS_WARN_EXCEPTION("svx.form",
+ "exception while getting a sibling's DSS !");
+ }
+
+ }
+ ++nCurrentSourceIndex;
+ }
+
+ DBG_ASSERT(bEqualDSS, "FmFormObj::ensureModelEnv : found no source form !");
+ // ??? at least the nIndex-th one should have been found ???
+
+ // now search the next one with the given DSS (within the destination container)
+ bEqualDSS = false;
+ while (!bEqualDSS && (nCurrentDestIndex < xDestContainer->getCount()))
+ {
+ xDestContainer->getByIndex(nCurrentDestIndex) >>= xCurrentDestForm;
+ DBG_ASSERT(xCurrentDestForm.is(), "FmFormObj::ensureModelEnv : invalid destination form !");
+ bEqualDSS = false;
+ if (::comphelper::hasProperty(FM_PROP_DATASOURCE, xCurrentDestForm))
+ { // it is a form
+ try
+ {
+ if ( xCurrentDestForm->getPropertyValue(FM_PROP_COMMAND) == aSrcCursorSource
+ && xCurrentDestForm->getPropertyValue(FM_PROP_COMMANDTYPE) == aSrcCursorSourceType
+ && xCurrentDestForm->getPropertyValue(FM_PROP_DATASOURCE) == aSrcDataSource
+ )
+ {
+ bEqualDSS = true;
+ }
+ }
+ catch(Exception&)
+ {
+ TOOLS_WARN_EXCEPTION("svx.form",
+ "exception while getting a destination DSS !");
+ }
+
+ }
+ ++nCurrentDestIndex;
+ }
+
+ if (!bEqualDSS)
+ { // There is at least one more source form with the given DSS than destination forms are.
+ // correct this ...
+ try
+ {
+ // create and insert (into the destination) a copy of the form
+ xCurrentDestForm.set(
+ ::comphelper::getProcessServiceFactory()->createInstance("com.sun.star.form.component.DataForm"),
+ UNO_QUERY_THROW );
+ ::comphelper::copyProperties( xCurrentSourceForm, xCurrentDestForm );
+
+ DBG_ASSERT(nCurrentDestIndex == xDestContainer->getCount(), "FmFormObj::ensureModelEnv : something went wrong with the numbers !");
+ xDestContainer->insertByIndex(nCurrentDestIndex, Any(xCurrentDestForm));
+
+ ++nCurrentDestIndex;
+ // like nCurrentSourceIndex, nCurrentDestIndex now points 'behind' the form it actually means
+ }
+ catch(Exception&)
+ {
+ OSL_FAIL("FmFormObj::ensureModelEnv : something went seriously wrong while creating a new form !");
+ // no more options anymore ...
+ return Reference< XInterface > ();
+ }
+
+ }
+ }
+
+ // now xCurrentDestForm is a form equivalent to xSourceForm (which means they have the same DSS and the same number
+ // of left siblings with the same DSS, which counts for all their ancestors, too)
+
+ // go down
+ xDestContainer.set(xCurrentDestForm, UNO_QUERY);
+ xSourceContainer.set(xSourceForm, UNO_QUERY);
+ DBG_ASSERT(xDestContainer.is() && xSourceContainer.is(), "FmFormObj::ensureModelEnv : invalid container !");
+ }
+ while ( nTokIndex >= 0 );
+
+ return Reference<XInterface>( xDestContainer, UNO_QUERY );
+}
+
+FmFormObj* FmFormObj::GetFormObject( SdrObject* _pSdrObject )
+{
+ FmFormObj* pFormObject = dynamic_cast< FmFormObj* >( _pSdrObject );
+ if ( !pFormObject )
+ {
+ SdrVirtObj* pVirtualObject = dynamic_cast< SdrVirtObj* >( _pSdrObject );
+ if ( pVirtualObject )
+ pFormObject = dynamic_cast< FmFormObj* >( &pVirtualObject->ReferencedObj() );
+ }
+ return pFormObject;
+}
+
+
+const FmFormObj* FmFormObj::GetFormObject( const SdrObject* _pSdrObject )
+{
+ const FmFormObj* pFormObject = dynamic_cast< const FmFormObj* >( _pSdrObject );
+ if ( !pFormObject )
+ {
+ const SdrVirtObj* pVirtualObject = dynamic_cast< const SdrVirtObj* >( _pSdrObject );
+ if ( pVirtualObject )
+ pFormObject = dynamic_cast< const FmFormObj* >( &pVirtualObject->GetReferencedObj() );
+ }
+ return pFormObject;
+}
+
+
+void FmFormObj::SetUnoControlModel( const Reference< css::awt::XControlModel >& _rxModel )
+{
+ SdrUnoObj::SetUnoControlModel( _rxModel );
+
+ FmFormPage* pFormPage(dynamic_cast< FmFormPage* >(getSdrPageFromSdrObject()));
+ if ( pFormPage )
+ pFormPage->GetImpl().formModelAssigned( *this );
+
+ impl_checkRefDevice_nothrow( true );
+}
+
+
+bool FmFormObj::EndCreate( SdrDragStat& rStat, SdrCreateCmd eCmd )
+{
+ bool bResult = SdrUnoObj::EndCreate(rStat, eCmd);
+ if ( bResult && SdrCreateCmd::ForceEnd == eCmd && rStat.GetView() )
+ {
+ FmFormPage* pFormPage(dynamic_cast< FmFormPage* >(getSdrPageFromSdrObject()));
+
+ if (nullptr != pFormPage)
+ {
+ try
+ {
+ Reference< XFormComponent > xContent( xUnoControlModel, UNO_QUERY_THROW );
+ Reference< XForm > xParentForm( xContent->getParent(), UNO_QUERY );
+
+ Reference< XIndexContainer > xFormToInsertInto;
+
+ if ( !xParentForm.is() )
+ { // model is not yet part of a form component hierarchy
+ xParentForm.set( pFormPage->GetImpl().findPlaceInFormComponentHierarchy( xContent ), UNO_SET_THROW );
+ xFormToInsertInto.set( xParentForm, UNO_QUERY_THROW );
+ }
+
+ FmFormPageImpl::setUniqueName( xContent, xParentForm );
+
+ if ( xFormToInsertInto.is() )
+ xFormToInsertInto->insertByIndex( xFormToInsertInto->getCount(), Any( xContent ) );
+ }
+ catch( const Exception& )
+ {
+ DBG_UNHANDLED_EXCEPTION("svx");
+ }
+ }
+
+ FmFormView* pView( dynamic_cast< FmFormView* >( rStat.GetView() ) );
+ FmXFormView* pViewImpl = pView ? pView->GetImpl() : nullptr;
+ OSL_ENSURE( pViewImpl, "FmFormObj::EndCreate: no view!?" );
+ if ( pViewImpl )
+ pViewImpl->onCreatedFormObject( *this );
+ }
+ return bResult;
+}
+
+
+void FmFormObj::BrkCreate( SdrDragStat& rStat )
+{
+ SdrUnoObj::BrkCreate( rStat );
+ impl_isolateControlModel_nothrow();
+
+ FmFormView* pView( dynamic_cast< FmFormView* >( rStat.GetView() ) );
+ FmXFormView* pViewImpl = pView ? pView->GetImpl() : nullptr;
+ OSL_ENSURE( pViewImpl, "FmFormObj::EndCreate: no view!?" );
+ if ( pViewImpl )
+ pViewImpl->breakCreateFormObject();
+}
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/form/fmobjfac.cxx b/svx/source/form/fmobjfac.cxx
new file mode 100644
index 000000000..7bdf2d4cc
--- /dev/null
+++ b/svx/source/form/fmobjfac.cxx
@@ -0,0 +1,231 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <config_features.h>
+#include <config_fuzzers.h>
+
+#include <tools/diagnose_ex.h>
+#include <svx/svdobj.hxx>
+#include <svx/fmtools.hxx>
+#include <fmservs.hxx>
+
+#include <svx/fmobjfac.hxx>
+
+#include <svx/svdobjkind.hxx>
+
+#include <fmobj.hxx>
+
+#include <svx/fmshell.hxx>
+
+#include <svx/svxids.hrc>
+#include <tbxform.hxx>
+
+#include <tabwin.hxx>
+#include <fmexpl.hxx>
+#include <filtnav.hxx>
+
+#include <fmprop.hxx>
+#include <fmPropBrw.hxx>
+#include <datanavi.hxx>
+
+using namespace ::com::sun::star::uno;
+using namespace ::com::sun::star::beans;
+using namespace ::svxform;
+
+static bool bInit = false;
+
+FmFormObjFactory::FmFormObjFactory()
+{
+ if ( bInit )
+ return;
+
+ SdrObjFactory::InsertMakeObjectHdl(LINK(this, FmFormObjFactory, MakeObject));
+
+
+ // register the configuration css::frame::Controller and the NavigationBar
+ SvxFmTbxCtlAbsRec::RegisterControl( SID_FM_RECORD_ABSOLUTE );
+ SvxFmTbxCtlRecText::RegisterControl( SID_FM_RECORD_TEXT );
+ SvxFmTbxCtlRecFromText::RegisterControl( SID_FM_RECORD_FROM_TEXT );
+ SvxFmTbxCtlRecTotal::RegisterControl( SID_FM_RECORD_TOTAL );
+ SvxFmTbxPrevRec::RegisterControl( SID_FM_RECORD_PREV );
+ SvxFmTbxNextRec::RegisterControl( SID_FM_RECORD_NEXT );
+
+ // registering global windows
+ FmFieldWinMgr::RegisterChildWindow();
+ FmPropBrwMgr::RegisterChildWindow();
+ NavigatorFrameManager::RegisterChildWindow();
+ DataNavigatorManager::RegisterChildWindow();
+#if HAVE_FEATURE_DBCONNECTIVITY && !ENABLE_FUZZERS
+ FmFilterNavigatorWinMgr::RegisterChildWindow();
+#endif
+
+ // register the interface for the Formshell
+ FmFormShell::RegisterInterface();
+
+ ImplSmartRegisterUnoServices();
+ bInit = true;
+}
+
+// create css::form::Form objects
+namespace
+{
+ void lcl_initProperty( FmFormObj const * _pObject, const OUString& _rPropName, const Any& _rValue )
+ {
+ try
+ {
+ Reference< XPropertySet > xModelSet( _pObject->GetUnoControlModel(), UNO_QUERY );
+ if ( xModelSet.is() )
+ xModelSet->setPropertyValue( _rPropName, _rValue );
+ }
+ catch( const Exception& )
+ {
+ TOOLS_WARN_EXCEPTION( "svx", "lcl_initProperty" );
+ }
+ }
+}
+
+IMPL_STATIC_LINK(FmFormObjFactory, MakeObject, SdrObjCreatorParams, aParams, SdrObject*)
+{
+ SdrObject* pNewObj = nullptr;
+
+ if (aParams.nInventor == SdrInventor::FmForm)
+ {
+ OUString sServiceSpecifier;
+
+ typedef ::std::vector< ::std::pair< OUString, Any > > PropertyValueArray;
+ PropertyValueArray aInitialProperties;
+
+ switch ( aParams.nObjIdentifier )
+ {
+ case SdrObjKind::FormEdit:
+ sServiceSpecifier = FM_COMPONENT_EDIT;
+ break;
+
+ case SdrObjKind::FormButton:
+ sServiceSpecifier = FM_COMPONENT_COMMANDBUTTON;
+ break;
+
+ case SdrObjKind::FormFixedText:
+ sServiceSpecifier = FM_COMPONENT_FIXEDTEXT;
+ break;
+
+ case SdrObjKind::FormListbox:
+ sServiceSpecifier = FM_COMPONENT_LISTBOX;
+ break;
+
+ case SdrObjKind::FormCheckbox:
+ sServiceSpecifier = FM_COMPONENT_CHECKBOX;
+ break;
+
+ case SdrObjKind::FormRadioButton:
+ sServiceSpecifier = FM_COMPONENT_RADIOBUTTON;
+ break;
+
+ case SdrObjKind::FormGroupBox:
+ sServiceSpecifier = FM_COMPONENT_GROUPBOX;
+ break;
+
+ case SdrObjKind::FormCombobox:
+ sServiceSpecifier = FM_COMPONENT_COMBOBOX;
+ break;
+
+ case SdrObjKind::FormGrid:
+ sServiceSpecifier = FM_COMPONENT_GRID;
+ break;
+
+ case SdrObjKind::FormImageButton:
+ sServiceSpecifier = FM_COMPONENT_IMAGEBUTTON;
+ break;
+
+ case SdrObjKind::FormFileControl:
+ sServiceSpecifier = FM_COMPONENT_FILECONTROL;
+ break;
+
+ case SdrObjKind::FormDateField:
+ sServiceSpecifier = FM_COMPONENT_DATEFIELD;
+ break;
+
+ case SdrObjKind::FormTimeField:
+ sServiceSpecifier = FM_COMPONENT_TIMEFIELD;
+ aInitialProperties.emplace_back( FM_PROP_TIMEMAX, Any( tools::Time( 23, 59, 59, 999999999 ).GetUNOTime() ) );
+ break;
+
+ case SdrObjKind::FormNumericField:
+ sServiceSpecifier = FM_COMPONENT_NUMERICFIELD;
+ break;
+
+ case SdrObjKind::FormCurrencyField:
+ sServiceSpecifier = FM_COMPONENT_CURRENCYFIELD;
+ break;
+
+ case SdrObjKind::FormPatternField:
+ sServiceSpecifier = FM_COMPONENT_PATTERNFIELD;
+ break;
+
+ case SdrObjKind::FormHidden:
+ sServiceSpecifier = FM_COMPONENT_HIDDEN;
+ break;
+
+ case SdrObjKind::FormImageControl:
+ sServiceSpecifier = FM_COMPONENT_IMAGECONTROL;
+ break;
+
+ case SdrObjKind::FormFormattedField:
+ sServiceSpecifier = FM_COMPONENT_FORMATTEDFIELD;
+ break;
+
+ case SdrObjKind::FormNavigationBar:
+ sServiceSpecifier = FM_SUN_COMPONENT_NAVIGATIONBAR;
+ break;
+
+ case SdrObjKind::FormScrollbar:
+ sServiceSpecifier = FM_SUN_COMPONENT_SCROLLBAR;
+ aInitialProperties.emplace_back( FM_PROP_BORDER, Any( sal_Int16(0) ) );
+ break;
+
+ case SdrObjKind::FormSpinButton:
+ sServiceSpecifier = FM_SUN_COMPONENT_SPINBUTTON;
+ aInitialProperties.emplace_back( FM_PROP_BORDER, Any( sal_Int16(0) ) );
+ break;
+
+ default:
+ break;
+ }
+
+ // create the actual object
+ if ( !sServiceSpecifier.isEmpty() )
+ pNewObj = new FmFormObj(aParams.rSdrModel, sServiceSpecifier);
+ else
+ pNewObj = new FmFormObj(aParams.rSdrModel);
+
+ // initialize some properties which we want to differ from the defaults
+ for (const auto& rInitProp : aInitialProperties)
+ {
+ lcl_initProperty(
+ static_cast< FmFormObj* >( pNewObj ),
+ rInitProp.first,
+ rInitProp.second
+ );
+ }
+ }
+ return pNewObj;
+}
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/form/fmpage.cxx b/svx/source/form/fmpage.cxx
new file mode 100644
index 000000000..c4f0bcad5
--- /dev/null
+++ b/svx/source/form/fmpage.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 <svx/fmpage.hxx>
+#include <com/sun/star/beans/XPropertySet.hpp>
+
+#include <svx/fmmodel.hxx>
+
+#include <fmobj.hxx>
+
+#include <fmpgeimp.hxx>
+
+#include <svx/svdview.hxx>
+#include <tools/urlobj.hxx>
+#include <vcl/help.hxx>
+#include <vcl/window.hxx>
+#include <osl/diagnose.h>
+
+
+#include <fmprop.hxx>
+#include <fmundo.hxx>
+using namespace ::svxform;
+#include <comphelper/property.hxx>
+#include <comphelper/types.hxx>
+
+using com::sun::star::uno::Reference;
+using com::sun::star::uno::UNO_QUERY;
+
+
+FmFormPage::FmFormPage(FmFormModel& rModel, bool bMasterPage)
+: SdrPage(rModel, bMasterPage)
+ ,m_pImpl( new FmFormPageImpl( *this ) )
+{
+}
+
+void FmFormPage::lateInit(const FmFormPage& rPage)
+{
+ // call parent
+ SdrPage::lateInit( rPage );
+
+ // copy local variables (former stuff from copy constructor)
+ m_pImpl->initFrom( rPage.GetImpl() );
+ m_sPageName = rPage.m_sPageName;
+}
+
+
+FmFormPage::~FmFormPage()
+{
+}
+
+rtl::Reference<SdrPage> FmFormPage::CloneSdrPage(SdrModel& rTargetModel) const
+{
+ FmFormModel& rFmFormModel(static_cast< FmFormModel& >(rTargetModel));
+ rtl::Reference<FmFormPage> pClonedFmFormPage =
+ new FmFormPage(
+ rFmFormModel,
+ IsMasterPage());
+ pClonedFmFormPage->lateInit(*this);
+ return pClonedFmFormPage;
+}
+
+
+void FmFormPage::InsertObject(SdrObject* pObj, size_t nPos)
+{
+ SdrPage::InsertObject( pObj, nPos );
+ static_cast< FmFormModel& >(getSdrModelFromSdrPage()).GetUndoEnv().Inserted(pObj);
+}
+
+
+const Reference< css::form::XForms > & FmFormPage::GetForms( bool _bForceCreate ) const
+{
+ const SdrPage& rMasterPage( *this );
+ const FmFormPage* pFormPage = dynamic_cast< const FmFormPage* >( &rMasterPage );
+ OSL_ENSURE( pFormPage, "FmFormPage::GetForms: referenced page is no FmFormPage - is this allowed?!" );
+ if ( !pFormPage )
+ pFormPage = this;
+
+ return pFormPage->m_pImpl->getForms( _bForceCreate );
+}
+
+
+bool FmFormPage::RequestHelp( vcl::Window* pWindow, SdrView const * pView,
+ const HelpEvent& rEvt )
+{
+ if( pView->IsAction() )
+ return false;
+
+ Point aPos = rEvt.GetMousePosPixel();
+ aPos = pWindow->ScreenToOutputPixel( aPos );
+ aPos = pWindow->PixelToLogic( aPos );
+
+ SdrPageView* pPV = nullptr;
+ SdrObject* pObj = pView->PickObj(aPos, 0, pPV, SdrSearchOptions::DEEP);
+ if (!pObj)
+ return false;
+
+ FmFormObj* pFormObject = FmFormObj::GetFormObject( pObj );
+ if ( !pFormObject )
+ return false;
+
+ OUString aHelpText;
+ css::uno::Reference< css::beans::XPropertySet > xSet( pFormObject->GetUnoControlModel(), css::uno::UNO_QUERY );
+ if (xSet.is())
+ {
+ if (::comphelper::hasProperty(FM_PROP_HELPTEXT, xSet))
+ aHelpText = ::comphelper::getString(xSet->getPropertyValue(FM_PROP_HELPTEXT));
+
+ if (aHelpText.isEmpty() && ::comphelper::hasProperty(FM_PROP_TARGET_URL, xSet))
+ {
+ OUString aText = ::comphelper::getString(xSet->getPropertyValue(FM_PROP_TARGET_URL));
+ INetURLObject aUrl(aText);
+
+ // test if it is a protocol type that I want to display
+ INetProtocol aProtocol = aUrl.GetProtocol();
+ static const INetProtocol s_aQuickHelpSupported[] =
+ { INetProtocol::Ftp, INetProtocol::Http, INetProtocol::File, INetProtocol::Mailto,
+ INetProtocol::Https, INetProtocol::Javascript,
+ INetProtocol::Ldap
+ };
+ for (const INetProtocol& i : s_aQuickHelpSupported)
+ if (i == aProtocol)
+ {
+ aHelpText = aUrl.GetURLNoPass(INetURLObject::DecodeMechanism::Unambiguous);
+ break;
+ }
+ }
+ }
+ if ( !aHelpText.isEmpty() )
+ {
+ // display the help
+ tools::Rectangle aItemRect = pObj->GetCurrentBoundRect();
+ aItemRect = pWindow->LogicToPixel( aItemRect );
+ Point aPt = pWindow->OutputToScreenPixel( aItemRect.TopLeft() );
+ aItemRect.SetLeft( aPt.X() );
+ aItemRect.SetTop( aPt.Y() );
+ aPt = pWindow->OutputToScreenPixel( aItemRect.BottomRight() );
+ aItemRect.SetRight( aPt.X() );
+ aItemRect.SetBottom( aPt.Y() );
+ if( rEvt.GetMode() == HelpEventMode::BALLOON )
+ Help::ShowBalloon( pWindow, aItemRect.Center(), aItemRect, aHelpText);
+ else
+ Help::ShowQuickHelp( pWindow, aItemRect, aHelpText );
+ }
+ return true;
+}
+
+
+SdrObject* FmFormPage::RemoveObject(size_t nObjNum)
+{
+ SdrObject* pObj = SdrPage::RemoveObject(nObjNum);
+ if (pObj)
+ static_cast< FmFormModel& >(getSdrModelFromSdrPage()).GetUndoEnv().Removed(pObj);
+ return pObj;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/form/fmpgeimp.cxx b/svx/source/form/fmpgeimp.cxx
new file mode 100644
index 000000000..5e8c2027d
--- /dev/null
+++ b/svx/source/form/fmpgeimp.cxx
@@ -0,0 +1,713 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+
+#include <fmpgeimp.hxx>
+#include <fmundo.hxx>
+#include <svx/fmtools.hxx>
+#include <fmprop.hxx>
+#include <fmservs.hxx>
+#include <fmobj.hxx>
+#include <formcontrolfactory.hxx>
+#include <svx/svditer.hxx>
+#include <svx/strings.hrc>
+#include <treevisitor.hxx>
+
+#include <com/sun/star/sdb/CommandType.hpp>
+#include <com/sun/star/sdbc/XRowSet.hpp>
+#include <com/sun/star/container/EnumerableMap.hpp>
+#include <com/sun/star/drawing/XControlShape.hpp>
+#include <com/sun/star/form/Forms.hpp>
+#include <com/sun/star/form/FormComponentType.hpp>
+
+#include <sal/log.hxx>
+#include <sfx2/objsh.hxx>
+#include <svx/fmpage.hxx>
+#include <svx/fmmodel.hxx>
+#include <tools/debug.hxx>
+#include <tools/diagnose_ex.h>
+#include <svx/dialmgr.hxx>
+#include <comphelper/processfactory.hxx>
+#include <comphelper/types.hxx>
+#include <connectivity/dbtools.hxx>
+
+using namespace ::com::sun::star::uno;
+using namespace ::com::sun::star::lang;
+using namespace ::com::sun::star::sdbc;
+using namespace ::com::sun::star::sdb;
+using namespace ::com::sun::star::container;
+using namespace ::com::sun::star::beans;
+using namespace ::com::sun::star::form;
+using ::com::sun::star::awt::XControlModel;
+using ::com::sun::star::container::XMap;
+using ::com::sun::star::container::EnumerableMap;
+using ::com::sun::star::drawing::XControlShape;
+using namespace ::svxform;
+using namespace ::dbtools;
+
+
+FmFormPageImpl::FmFormPageImpl( FmFormPage& _rPage )
+ :m_rPage( _rPage )
+ ,m_bFirstActivation( true )
+ ,m_bAttemptedFormCreation( false )
+{
+}
+
+
+namespace
+{
+ class FormComponentInfo
+ {
+ public:
+ static size_t childCount( const Reference< XInterface >& _component )
+ {
+ Reference< XIndexAccess > xContainer( _component, UNO_QUERY );
+ if ( xContainer.is() )
+ return xContainer->getCount();
+ return 0;
+ }
+
+ static Reference< XInterface > getChild( const Reference< XInterface >& _component, size_t _index )
+ {
+ Reference< XIndexAccess > xContainer( _component, UNO_QUERY_THROW );
+ return Reference< XInterface >( xContainer->getByIndex( _index ), UNO_QUERY );
+ }
+ };
+
+ typedef ::std::pair< Reference< XInterface >, Reference< XInterface > > FormComponentPair;
+
+ class FormHierarchyComparator
+ {
+ public:
+ FormHierarchyComparator()
+ {
+ }
+
+ static size_t childCount( const FormComponentPair& _components )
+ {
+ size_t lhsCount = FormComponentInfo::childCount( _components.first );
+ size_t rhsCount = FormComponentInfo::childCount( _components.second );
+ if ( lhsCount != rhsCount )
+ throw RuntimeException( "Found inconsistent form component hierarchies (1)!" );
+ return lhsCount;
+ }
+
+ static FormComponentPair getChild( const FormComponentPair& _components, size_t _index )
+ {
+ return FormComponentPair(
+ FormComponentInfo::getChild( _components.first, _index ),
+ FormComponentInfo::getChild( _components.second, _index )
+ );
+ }
+ };
+
+ typedef ::std::map< Reference< XControlModel >, Reference< XControlModel > > MapControlModels;
+
+ class FormComponentAssignment
+ {
+ public:
+ explicit FormComponentAssignment( MapControlModels& _out_controlModelMap )
+ :m_rControlModelMap( _out_controlModelMap )
+ {
+ }
+
+ void process( const FormComponentPair& _component )
+ {
+ Reference< XControlModel > lhsControlModel( _component.first, UNO_QUERY );
+ Reference< XControlModel > rhsControlModel( _component.second, UNO_QUERY );
+ if ( lhsControlModel.is() != rhsControlModel.is() )
+ throw RuntimeException( "Found inconsistent form component hierarchies (2)!" );
+
+ if ( lhsControlModel.is() )
+ m_rControlModelMap[ lhsControlModel ] = rhsControlModel;
+ }
+
+ private:
+ MapControlModels& m_rControlModelMap;
+ };
+}
+
+
+void FmFormPageImpl::initFrom( FmFormPageImpl& i_foreignImpl )
+{
+ // clone the Forms collection
+ const Reference< css::form::XForms > xForeignForms( i_foreignImpl.getForms( false ) );
+
+ if ( !xForeignForms.is() )
+ return;
+
+ try
+ {
+ m_xForms.set( xForeignForms->createClone(), UNO_QUERY_THROW );
+
+ // create a mapping between the original control models and their clones
+ MapControlModels aModelAssignment;
+
+ typedef TreeVisitor< FormComponentPair, FormHierarchyComparator, FormComponentAssignment > FormComponentVisitor;
+ FormComponentVisitor aVisitor{ FormHierarchyComparator() };
+
+ FormComponentAssignment aAssignmentProcessor( aModelAssignment );
+ aVisitor.process( FormComponentPair( xForeignForms, m_xForms ), aAssignmentProcessor );
+
+ // assign the cloned models to their SdrObjects
+ SdrObjListIter aForeignIter( &i_foreignImpl.m_rPage );
+ SdrObjListIter aOwnIter( &m_rPage );
+
+ OSL_ENSURE( aForeignIter.IsMore() == aOwnIter.IsMore(), "FmFormPageImpl::FmFormPageImpl: inconsistent number of objects (1)!" );
+ while ( aForeignIter.IsMore() && aOwnIter.IsMore() )
+ {
+ FmFormObj* pForeignObj = dynamic_cast< FmFormObj* >( aForeignIter.Next() );
+ FmFormObj* pOwnObj = dynamic_cast< FmFormObj* >( aOwnIter.Next() );
+
+ bool bForeignIsForm = pForeignObj && ( pForeignObj->GetObjInventor() == SdrInventor::FmForm );
+ bool bOwnIsForm = pOwnObj && ( pOwnObj->GetObjInventor() == SdrInventor::FmForm );
+
+ if ( bForeignIsForm != bOwnIsForm )
+ {
+ // if this fires, don't attempt to do further assignments, something's completely messed up
+ SAL_WARN( "svx.form", "FmFormPageImpl::FmFormPageImpl: inconsistent ordering of objects!" );
+ break;
+ }
+
+ if ( !bForeignIsForm )
+ // no form control -> next round
+ continue;
+
+ Reference< XControlModel > xForeignModel( pForeignObj->GetUnoControlModel() );
+ if ( !xForeignModel.is() )
+ {
+ // if this fires, the SdrObject does not have a UNO Control Model. This is pathological, but well ...
+ // So the cloned SdrObject will also not have a UNO Control Model.
+ SAL_WARN( "svx.form", "FmFormPageImpl::FmFormPageImpl: control shape without control!" );
+ continue;
+ }
+
+ MapControlModels::const_iterator assignment = aModelAssignment.find( xForeignModel );
+ if ( assignment == aModelAssignment.end() )
+ {
+ // if this fires, the source SdrObject has a model, but it is not part of the model hierarchy in
+ // i_foreignImpl.getForms().
+ // Pathological, too ...
+ SAL_WARN( "svx.form", "FmFormPageImpl::FmFormPageImpl: no clone found for this model!" );
+ continue;
+ }
+
+ pOwnObj->SetUnoControlModel( assignment->second );
+ }
+ OSL_ENSURE( aForeignIter.IsMore() == aOwnIter.IsMore(), "FmFormPageImpl::FmFormPageImpl: inconsistent number of objects (2)!" );
+ }
+ catch( const Exception& )
+ {
+ DBG_UNHANDLED_EXCEPTION("svx");
+ }
+}
+
+
+Reference< XMap > FmFormPageImpl::getControlToShapeMap()
+{
+ Reference< XMap > xControlShapeMap( m_aControlShapeMap.get(), UNO_QUERY );
+ if ( xControlShapeMap.is() )
+ return xControlShapeMap;
+
+ xControlShapeMap = impl_createControlShapeMap_nothrow();
+ m_aControlShapeMap = xControlShapeMap;
+ return xControlShapeMap;
+}
+
+
+namespace
+{
+ void lcl_insertFormObject_throw( const FmFormObj& _object, const Reference< XMap >& _map )
+ {
+ // the control model
+ Reference< XControlModel > xControlModel = _object.GetUnoControlModel();
+ OSL_ENSURE( xControlModel.is(), "lcl_insertFormObject_throw: suspicious: no control model!" );
+ if ( !xControlModel.is() )
+ return;
+
+ Reference< XControlShape > xControlShape( const_cast< FmFormObj& >( _object ).getUnoShape(), UNO_QUERY );
+ OSL_ENSURE( xControlShape.is(), "lcl_insertFormObject_throw: suspicious: no control shape!" );
+ if ( !xControlShape.is() )
+ return;
+
+ _map->put( Any( xControlModel ), Any( xControlShape ) );
+ }
+
+ void lcl_removeFormObject_throw( const FmFormObj& _object, const Reference< XMap >& _map )
+ {
+ // the control model
+ Reference< XControlModel > xControlModel = _object.GetUnoControlModel();
+ OSL_ENSURE( xControlModel.is(), "lcl_removeFormObject: suspicious: no control model!" );
+ if ( !xControlModel.is() )
+ {
+ return;
+ }
+
+ Any aOldAssignment = _map->remove( Any( xControlModel ) );
+ OSL_ENSURE(
+ aOldAssignment == Any( Reference< XControlShape >( const_cast< FmFormObj& >( _object ).getUnoShape(), UNO_QUERY ) ),
+ "lcl_removeFormObject: map was inconsistent!" );
+ }
+}
+
+
+Reference< XMap > FmFormPageImpl::impl_createControlShapeMap_nothrow()
+{
+ Reference< XMap > xMap;
+
+ try
+ {
+ xMap = EnumerableMap::create( comphelper::getProcessComponentContext(),
+ ::cppu::UnoType< XControlModel >::get(),
+ ::cppu::UnoType< XControlShape >::get()
+ );
+
+ SdrObjListIter aPageIter( &m_rPage );
+ while ( aPageIter.IsMore() )
+ {
+ // only FmFormObjs are what we're interested in
+ FmFormObj* pCurrent = FmFormObj::GetFormObject( aPageIter.Next() );
+ if ( !pCurrent )
+ continue;
+
+ lcl_insertFormObject_throw( *pCurrent, xMap );
+ }
+ }
+ catch( const Exception& )
+ {
+ DBG_UNHANDLED_EXCEPTION("svx");
+ }
+ return xMap;
+}
+
+
+const Reference< css::form::XForms >& FmFormPageImpl::getForms( bool _bForceCreate )
+{
+ if ( m_xForms.is() || !_bForceCreate )
+ return m_xForms;
+
+ if ( !m_bAttemptedFormCreation )
+ {
+ m_bAttemptedFormCreation = true;
+
+ Reference<XComponentContext> xContext = comphelper::getProcessComponentContext();
+ m_xForms = css::form::Forms::create( xContext );
+
+ if ( m_aFormsCreationHdl.IsSet() )
+ {
+ m_aFormsCreationHdl.Call( *this );
+ }
+
+ FmFormModel& rFmFormModel(dynamic_cast< FmFormModel& >(m_rPage.getSdrModelFromSdrPage()));
+
+ // give the newly created collection a place in the universe
+ SfxObjectShell* pObjShell(rFmFormModel.GetObjectShell());
+ if ( pObjShell )
+ m_xForms->setParent( pObjShell->GetModel() );
+
+ // tell the UNDO environment that we have a new forms collection
+ rFmFormModel.GetUndoEnv().AddForms( Reference<XNameContainer>(m_xForms,UNO_QUERY_THROW) );
+ }
+ return m_xForms;
+}
+
+
+FmFormPageImpl::~FmFormPageImpl()
+{
+ xCurrentForm = nullptr;
+
+ ::comphelper::disposeComponent( m_xForms );
+}
+
+
+bool FmFormPageImpl::validateCurForm()
+{
+ if ( !xCurrentForm.is() )
+ return false;
+
+ if ( !xCurrentForm->getParent().is() )
+ xCurrentForm.clear();
+
+ return xCurrentForm.is();
+}
+
+
+void FmFormPageImpl::setCurForm(const Reference< css::form::XForm >& xForm)
+{
+ xCurrentForm = xForm;
+}
+
+
+Reference< XForm > FmFormPageImpl::getDefaultForm()
+{
+ Reference< XForm > xForm;
+
+ Reference< XForms > xForms( getForms() );
+
+ // by default, we use our "current form"
+ if ( !validateCurForm() )
+ {
+ // check whether there is a "standard" form
+ if ( Reference<XNameAccess>(xForms,UNO_QUERY_THROW)->hasElements() )
+ {
+ // find the standard form
+ OUString sStandardFormname = SvxResId(RID_STR_STDFORMNAME);
+
+ try
+ {
+ if ( xForms->hasByName( sStandardFormname ) )
+ xForm.set( xForms->getByName( sStandardFormname ), UNO_QUERY_THROW );
+ else
+ {
+ xForm.set( xForms->getByIndex(0), UNO_QUERY_THROW );
+ }
+ }
+ catch( const Exception& )
+ {
+ DBG_UNHANDLED_EXCEPTION("svx");
+ }
+ }
+ }
+ else
+ {
+ xForm = xCurrentForm;
+ }
+
+ // did not find an existing suitable form -> create a new one
+ if ( !xForm.is() )
+ {
+ SdrModel& rModel(m_rPage.getSdrModelFromSdrPage());
+
+ if( rModel.IsUndoEnabled() )
+ {
+ OUString aStr(SvxResId(RID_STR_FORM));
+ OUString aUndoStr(SvxResId(RID_STR_UNDO_CONTAINER_INSERT));
+ rModel.BegUndo(aUndoStr.replaceFirst("'#'", aStr));
+ }
+
+ try
+ {
+ xForm.set( ::comphelper::getProcessServiceFactory()->createInstance( FM_SUN_COMPONENT_FORM ), UNO_QUERY );
+
+ // a form should always have the command type table as default
+ Reference< XPropertySet > xFormProps( xForm, UNO_QUERY_THROW );
+ xFormProps->setPropertyValue( FM_PROP_COMMANDTYPE, Any( sal_Int32( CommandType::TABLE ) ) );
+
+ // and the "Standard" name
+ OUString sName = SvxResId(RID_STR_STDFORMNAME);
+ xFormProps->setPropertyValue( FM_PROP_NAME, Any( sName ) );
+
+ if( rModel.IsUndoEnabled() )
+ {
+ rModel.AddUndo(
+ std::make_unique<FmUndoContainerAction>(
+ static_cast< FmFormModel& >(rModel),
+ FmUndoContainerAction::Inserted,
+ xForms,
+ xForm,
+ xForms->getCount()));
+ }
+ xForms->insertByName( sName, Any( xForm ) );
+ xCurrentForm = xForm;
+ }
+ catch( const Exception& )
+ {
+ DBG_UNHANDLED_EXCEPTION("svx");
+ xForm.clear();
+ }
+
+ if( rModel.IsUndoEnabled() )
+ rModel.EndUndo();
+ }
+
+ return xForm;
+}
+
+
+Reference< css::form::XForm > FmFormPageImpl::findPlaceInFormComponentHierarchy(
+ const Reference< XFormComponent > & rContent, const Reference< XDataSource > & rDatabase,
+ const OUString& rDBTitle, const OUString& rCursorSource, sal_Int32 nCommandType )
+{
+ // if the control already is child of a form, don't do anything
+ if (!rContent.is() || rContent->getParent().is())
+ return nullptr;
+
+ Reference< XForm > xForm;
+
+ // If database and CursorSource are set, the form is searched for using
+ // these criteria, otherwise only current and the DefaultForm.
+ if (rDatabase.is() && !rCursorSource.isEmpty())
+ {
+ validateCurForm();
+
+ // first search in the current form
+ xForm = findFormForDataSource( xCurrentForm, rDatabase, rCursorSource, nCommandType );
+
+ Reference< css::container::XIndexAccess > xFormsByIndex = getForms();
+ DBG_ASSERT(xFormsByIndex.is(), "FmFormPageImpl::findPlaceInFormComponentHierarchy : no index access for my forms collection !");
+ sal_Int32 nCount = xFormsByIndex->getCount();
+ for (sal_Int32 i = 0; !xForm.is() && i < nCount; i++)
+ {
+ Reference< css::form::XForm > xToSearch;
+ xFormsByIndex->getByIndex(i) >>= xToSearch;
+ xForm = findFormForDataSource( xToSearch, rDatabase, rCursorSource, nCommandType );
+ }
+
+ // If no css::form found, then create a new one
+ if (!xForm.is())
+ {
+ SdrModel& rModel(m_rPage.getSdrModelFromSdrPage());
+ const bool bUndo(rModel.IsUndoEnabled());
+
+ if( bUndo )
+ {
+ OUString aStr(SvxResId(RID_STR_FORM));
+ OUString aUndoStr(SvxResId(RID_STR_UNDO_CONTAINER_INSERT));
+ aUndoStr = aUndoStr.replaceFirst("#", aStr);
+ rModel.BegUndo(aUndoStr);
+ }
+
+ xForm.set(::comphelper::getProcessServiceFactory()->createInstance(FM_SUN_COMPONENT_FORM), UNO_QUERY);
+ // a form should always have the command type table as default
+ Reference< css::beans::XPropertySet > xFormProps(xForm, UNO_QUERY);
+ try { xFormProps->setPropertyValue(FM_PROP_COMMANDTYPE, Any(sal_Int32(CommandType::TABLE))); }
+ catch(Exception&) { }
+
+ if (!rDBTitle.isEmpty())
+ xFormProps->setPropertyValue(FM_PROP_DATASOURCE,Any(rDBTitle));
+ else
+ {
+ Reference< css::beans::XPropertySet > xDatabaseProps(rDatabase, UNO_QUERY);
+ Any aDatabaseUrl = xDatabaseProps->getPropertyValue(FM_PROP_URL);
+ xFormProps->setPropertyValue(FM_PROP_URL, aDatabaseUrl);
+ }
+
+ xFormProps->setPropertyValue(FM_PROP_COMMAND,Any(rCursorSource));
+ xFormProps->setPropertyValue(FM_PROP_COMMANDTYPE, Any(nCommandType));
+
+ Reference< css::container::XNameAccess > xNamedSet = getForms();
+
+ const bool bTableOrQuery = ( CommandType::TABLE == nCommandType ) || ( CommandType::QUERY == nCommandType );
+ OUString sName = FormControlFactory::getUniqueName( xNamedSet,
+ bTableOrQuery ? rCursorSource : SvxResId(RID_STR_STDFORMNAME) );
+
+ xFormProps->setPropertyValue( FM_PROP_NAME, Any( sName ) );
+
+ if( bUndo )
+ {
+ Reference< css::container::XIndexContainer > xContainer = getForms();
+ rModel.AddUndo(
+ std::make_unique<FmUndoContainerAction>(
+ static_cast< FmFormModel& >(rModel),
+ FmUndoContainerAction::Inserted,
+ xContainer,
+ xForm,
+ xContainer->getCount()));
+ }
+
+ getForms()->insertByName( sName, Any( xForm ) );
+
+ if( bUndo )
+ rModel.EndUndo();
+ }
+ xCurrentForm = xForm;
+ }
+
+ xForm = getDefaultForm();
+ return xForm;
+}
+
+
+Reference< XForm > FmFormPageImpl::findFormForDataSource(
+ const Reference< XForm > & rForm, const Reference< XDataSource > & _rxDatabase,
+ const OUString& _rCursorSource, sal_Int32 nCommandType)
+{
+ Reference< XForm > xResultForm;
+ Reference< XRowSet > xDBForm(rForm, UNO_QUERY);
+ Reference< XPropertySet > xFormProps(rForm, UNO_QUERY);
+ if (!xDBForm.is() || !xFormProps.is())
+ return xResultForm;
+
+ OSL_ENSURE(_rxDatabase.is(), "FmFormPageImpl::findFormForDataSource: invalid data source!");
+ OUString sLookupName; // the name of the data source we're looking for
+ OUString sFormDataSourceName; // the name of the data source the current connection in the form is based on
+ try
+ {
+ Reference< XPropertySet > xDSProps(_rxDatabase, UNO_QUERY);
+ if (xDSProps.is())
+ xDSProps->getPropertyValue(FM_PROP_NAME) >>= sLookupName;
+
+ xFormProps->getPropertyValue(FM_PROP_DATASOURCE) >>= sFormDataSourceName;
+ // if there's no DataSourceName set at the form, check whether we can deduce one from its
+ // ActiveConnection
+ if (sFormDataSourceName.isEmpty())
+ {
+ Reference< XConnection > xFormConnection;
+ xFormProps->getPropertyValue( FM_PROP_ACTIVE_CONNECTION ) >>= xFormConnection;
+ if ( !xFormConnection.is() )
+ isEmbeddedInDatabase( xFormProps, xFormConnection );
+ if (xFormConnection.is())
+ {
+ Reference< XChild > xConnAsChild(xFormConnection, UNO_QUERY);
+ if (xConnAsChild.is())
+ {
+ Reference< XDataSource > xFormDS(xConnAsChild->getParent(), UNO_QUERY);
+ if (xFormDS.is())
+ {
+ xDSProps.set(xFormDS, css::uno::UNO_QUERY);
+ if (xDSProps.is())
+ xDSProps->getPropertyValue(FM_PROP_NAME) >>= sFormDataSourceName;
+ }
+ }
+ }
+ }
+ }
+ catch(const Exception&)
+ {
+ TOOLS_WARN_EXCEPTION("svx", "FmFormPageImpl::findFormForDataSource");
+ }
+
+ if (sLookupName == sFormDataSourceName)
+ {
+ // now check whether CursorSource and type match
+ OUString aCursorSource = ::comphelper::getString(xFormProps->getPropertyValue(FM_PROP_COMMAND));
+ sal_Int32 nType = ::comphelper::getINT32(xFormProps->getPropertyValue(FM_PROP_COMMANDTYPE));
+ if (aCursorSource.isEmpty() || ((nType == nCommandType) && (aCursorSource == _rCursorSource))) // found the form
+ {
+ xResultForm = rForm;
+ // if no data source is set yet, it is done here
+ if (aCursorSource.isEmpty())
+ {
+ xFormProps->setPropertyValue(FM_PROP_COMMAND, Any(_rCursorSource));
+ xFormProps->setPropertyValue(FM_PROP_COMMANDTYPE, Any(nCommandType));
+ }
+ }
+ }
+
+ // as long as xResultForm is NULL, search the child forms of rForm
+ Reference< XIndexAccess > xComponents(rForm, UNO_QUERY);
+ sal_Int32 nCount = xComponents->getCount();
+ for (sal_Int32 i = 0; !xResultForm.is() && i < nCount; ++i)
+ {
+ Reference< css::form::XForm > xSearchForm;
+ xComponents->getByIndex(i) >>= xSearchForm;
+ // continue searching in the sub form
+ if (xSearchForm.is())
+ xResultForm = findFormForDataSource( xSearchForm, _rxDatabase, _rCursorSource, nCommandType );
+ }
+ return xResultForm;
+}
+
+
+OUString FmFormPageImpl::setUniqueName(const Reference< XFormComponent > & xFormComponent, const Reference< XForm > & xControls)
+{
+#if OSL_DEBUG_LEVEL > 0
+ try
+ {
+ OSL_ENSURE( !xFormComponent->getParent().is(), "FmFormPageImpl::setUniqueName: to be called before insertion!" );
+ }
+ catch( const Exception& )
+ {
+ DBG_UNHANDLED_EXCEPTION("svx");
+ }
+#endif
+ OUString sName;
+ Reference< css::beans::XPropertySet > xSet(xFormComponent, UNO_QUERY);
+ if (xSet.is())
+ {
+ sName = ::comphelper::getString( xSet->getPropertyValue( FM_PROP_NAME ) );
+ Reference< css::container::XNameAccess > xNameAcc(xControls, UNO_QUERY);
+
+ if (sName.isEmpty() || xNameAcc->hasByName(sName))
+ {
+ // set a default name via the ClassId
+ sal_Int16 nClassId( FormComponentType::CONTROL );
+ xSet->getPropertyValue( FM_PROP_CLASSID ) >>= nClassId;
+
+ OUString sDefaultName = FormControlFactory::getDefaultUniqueName_ByComponentType(
+ Reference< XNameAccess >( xControls, UNO_QUERY ), xSet );
+
+ // do not overwrite the name of radio buttons that have it!
+ if (sName.isEmpty() || nClassId != css::form::FormComponentType::RADIOBUTTON)
+ {
+ xSet->setPropertyValue(FM_PROP_NAME, Any(sDefaultName));
+ }
+
+ sName = sDefaultName;
+ }
+ }
+ return sName;
+}
+
+
+void FmFormPageImpl::formModelAssigned( const FmFormObj& _object )
+{
+ Reference< XMap > xControlShapeMap( m_aControlShapeMap.get(), UNO_QUERY );
+ if ( !xControlShapeMap.is() )
+ // our map does not exist -> not interested in this event
+ return;
+
+ try
+ {
+ lcl_removeFormObject_throw( _object, xControlShapeMap );
+ lcl_insertFormObject_throw( _object, xControlShapeMap );
+ }
+ catch( const Exception& )
+ {
+ DBG_UNHANDLED_EXCEPTION("svx");
+ }
+}
+
+
+void FmFormPageImpl::formObjectInserted( const FmFormObj& _object )
+{
+ Reference< XMap > xControlShapeMap( m_aControlShapeMap.get(), UNO_QUERY );
+ if ( !xControlShapeMap.is() )
+ // our map does not exist -> not interested in this event
+ return;
+
+ try
+ {
+ lcl_insertFormObject_throw( _object, xControlShapeMap );
+ }
+ catch( const Exception& )
+ {
+ DBG_UNHANDLED_EXCEPTION("svx");
+ }
+}
+
+
+void FmFormPageImpl::formObjectRemoved( const FmFormObj& _object )
+{
+ Reference< XMap > xControlShapeMap( m_aControlShapeMap.get(), UNO_QUERY );
+ if ( !xControlShapeMap.is() )
+ // our map does not exist -> not interested in this event
+ return;
+
+ try
+ {
+ lcl_removeFormObject_throw( _object, xControlShapeMap );
+ }
+ catch( const Exception& )
+ {
+ DBG_UNHANDLED_EXCEPTION("svx");
+ }
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/form/fmscriptingenv.cxx b/svx/source/form/fmscriptingenv.cxx
new file mode 100644
index 000000000..31de56304
--- /dev/null
+++ b/svx/source/form/fmscriptingenv.cxx
@@ -0,0 +1,928 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * 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 <fmscriptingenv.hxx>
+#include <svx/fmmodel.hxx>
+
+#include <com/sun/star/awt/XControl.hpp>
+#include <com/sun/star/beans/XPropertySet.hpp>
+#include <com/sun/star/lang/DisposedException.hpp>
+#include <com/sun/star/lang/EventObject.hpp>
+#include <com/sun/star/lang/IllegalArgumentException.hpp>
+#include <com/sun/star/script/XScriptListener.hpp>
+
+#include <tools/diagnose_ex.h>
+#include <tools/debug.hxx>
+#include <cppuhelper/implbase.hxx>
+#include <vcl/svapp.hxx>
+#include <mutex>
+#include <o3tl/sorted_vector.hxx>
+#include <sfx2/objsh.hxx>
+#include <sfx2/app.hxx>
+#include <basic/basmgr.hxx>
+
+#include <memory>
+#include <utility>
+
+using std::pair;
+
+namespace svxform
+{
+
+
+ using ::com::sun::star::uno::Reference;
+ using ::com::sun::star::script::XEventAttacherManager;
+ using ::com::sun::star::lang::IllegalArgumentException;
+ using ::com::sun::star::script::XScriptListener;
+ using ::com::sun::star::script::ScriptEvent;
+ using ::com::sun::star::lang::EventObject;
+ using ::com::sun::star::uno::Any;
+ using ::com::sun::star::uno::UNO_QUERY_THROW;
+ using ::com::sun::star::lang::DisposedException;
+ using ::com::sun::star::uno::RuntimeException;
+ using ::com::sun::star::uno::Exception;
+ using ::com::sun::star::uno::Sequence;
+ using ::com::sun::star::awt::XControl;
+ using ::com::sun::star::beans::XPropertySet;
+
+ //= FormScriptListener
+
+ typedef ::cppu::WeakImplHelper < XScriptListener
+ > FormScriptListener_Base;
+
+ /** implements the XScriptListener interface, is used by FormScriptingEnvironment
+ */
+ class FormScriptListener :public FormScriptListener_Base
+ {
+ private:
+ std::mutex m_aMutex;
+ FormScriptingEnvironment *m_pScriptExecutor;
+
+ public:
+ explicit FormScriptListener( FormScriptingEnvironment * pScriptExecutor );
+
+ // XScriptListener
+ virtual void SAL_CALL firing( const ScriptEvent& aEvent ) override;
+ virtual Any SAL_CALL approveFiring( const ScriptEvent& aEvent ) override;
+ // XEventListener
+ virtual void SAL_CALL disposing( const EventObject& Source ) override;
+
+ // lifetime control
+ void dispose();
+
+ protected:
+ virtual ~FormScriptListener() override;
+
+ private:
+ /** determines whether calling a given method at a given listener interface can be done asynchronously
+
+ @param _rListenerType
+ the name of the UNO type whose method is to be checked
+ @param _rMethodName
+ the name of the method at the interface determined by _rListenerType
+
+ @return
+ <TRUE/> if and only if the method is declared <code>oneway</code>, i.e. can be called asynchronously
+ */
+ static bool impl_allowAsynchronousCall_nothrow( const OUString& _rListenerType, const OUString& _rMethodName );
+
+ /** determines whether the instance is already disposed
+ */
+ bool impl_isDisposed_nothrow() const { return !m_pScriptExecutor; }
+
+ /** fires the given script event in a thread-safe manner
+
+ This methods calls our script executor's doFireScriptEvent, with previously releasing the given mutex guard,
+ but ensuring that our script executor is not deleted between this release and the actual call.
+
+ @param _rGuard
+ a clearable guard to our mutex. Must be the only active guard to our mutex.
+ @param _rEvent
+ the event to fire
+ @param _pSynchronousResult
+ a place to take a possible result of the script call.
+
+ @precond
+ m_pScriptExecutor is not <NULL/>.
+ */
+ void impl_doFireScriptEvent_nothrow( std::unique_lock<std::mutex>& _rGuard, const ScriptEvent& _rEvent, Any* _pSynchronousResult );
+
+ private:
+ DECL_LINK( OnAsyncScriptEvent, void*, void );
+ };
+
+ FormScriptListener::FormScriptListener( FormScriptingEnvironment* pScriptExecutor )
+ :m_pScriptExecutor( pScriptExecutor )
+ {
+ }
+
+
+ FormScriptListener::~FormScriptListener()
+ {
+ }
+
+
+ bool FormScriptListener::impl_allowAsynchronousCall_nothrow( const OUString& _rListenerType, const OUString& _rMethodName )
+ {
+ // This used to be implemented as:
+ // is (_rListenerType + "::" + _rMethodName) a oneway function?
+ // since we got rid of the notion of oneway, this is the list
+ // of oneway methods, autogenerated by postprocessing of
+ // commitdiff 90eac3e69749a9227c4b6902b1f3cef1e338c6d1
+ static const o3tl::sorted_vector<pair<OUString, OUString>> delayed_event_listeners{
+ pair<OUString,OUString>("com.sun.star.accessibility.XAccessibleComponent","grabFocus"),
+ pair<OUString,OUString>("com.sun.star.accessibility.XAccessibleEventBroadcaster","addAccessibleEventListener"),
+ pair<OUString,OUString>("com.sun.star.accessibility.XAccessibleEventBroadcaster","removeAccessibleEventListener"),
+ pair<OUString,OUString>("com.sun.star.accessibility.XAccessibleSelection","clearAccessibleSelection"),
+ pair<OUString,OUString>("com.sun.star.accessibility.XAccessibleSelection","selectAllAccessibleChildren"),
+ pair<OUString,OUString>("com.sun.star.awt.XActionListener","actionPerformed"),
+ pair<OUString,OUString>("com.sun.star.awt.XActivateListener","windowActivated"),
+ pair<OUString,OUString>("com.sun.star.awt.XActivateListener","windowDeactivated"),
+ pair<OUString,OUString>("com.sun.star.awt.XAdjustmentListener","adjustmentValueChanged"),
+ pair<OUString,OUString>("com.sun.star.awt.XButton","addActionListener"),
+ pair<OUString,OUString>("com.sun.star.awt.XButton","removeActionListener"),
+ pair<OUString,OUString>("com.sun.star.awt.XButton","setLabel"),
+ pair<OUString,OUString>("com.sun.star.awt.XButton","setActionCommand"),
+ pair<OUString,OUString>("com.sun.star.awt.XCheckBox","addItemListener"),
+ pair<OUString,OUString>("com.sun.star.awt.XCheckBox","removeItemListener"),
+ pair<OUString,OUString>("com.sun.star.awt.XCheckBox","setState"),
+ pair<OUString,OUString>("com.sun.star.awt.XCheckBox","setLabel"),
+ pair<OUString,OUString>("com.sun.star.awt.XCheckBox","enableTriState"),
+ pair<OUString,OUString>("com.sun.star.awt.XComboBox","addItemListener"),
+ pair<OUString,OUString>("com.sun.star.awt.XComboBox","removeItemListener"),
+ pair<OUString,OUString>("com.sun.star.awt.XComboBox","addActionListener"),
+ pair<OUString,OUString>("com.sun.star.awt.XComboBox","removeActionListener"),
+ pair<OUString,OUString>("com.sun.star.awt.XComboBox","addItem"),
+ pair<OUString,OUString>("com.sun.star.awt.XComboBox","addItems"),
+ pair<OUString,OUString>("com.sun.star.awt.XComboBox","removeItems"),
+ pair<OUString,OUString>("com.sun.star.awt.XComboBox","setDropDownLineCount"),
+ pair<OUString,OUString>("com.sun.star.awt.XControl","setContext"),
+ pair<OUString,OUString>("com.sun.star.awt.XControl","createPeer"),
+ pair<OUString,OUString>("com.sun.star.awt.XControl","setDesignMode"),
+ pair<OUString,OUString>("com.sun.star.awt.XControlContainer","setStatusText"),
+ pair<OUString,OUString>("com.sun.star.awt.XControlContainer","addControl"),
+ pair<OUString,OUString>("com.sun.star.awt.XControlContainer","removeControl"),
+ pair<OUString,OUString>("com.sun.star.awt.XCurrencyField","setValue"),
+ pair<OUString,OUString>("com.sun.star.awt.XCurrencyField","setMin"),
+ pair<OUString,OUString>("com.sun.star.awt.XCurrencyField","setMax"),
+ pair<OUString,OUString>("com.sun.star.awt.XCurrencyField","setFirst"),
+ pair<OUString,OUString>("com.sun.star.awt.XCurrencyField","setLast"),
+ pair<OUString,OUString>("com.sun.star.awt.XCurrencyField","setSpinSize"),
+ pair<OUString,OUString>("com.sun.star.awt.XCurrencyField","setDecimalDigits"),
+ pair<OUString,OUString>("com.sun.star.awt.XCurrencyField","setStrictFormat"),
+ pair<OUString,OUString>("com.sun.star.awt.XDateField","setDate"),
+ pair<OUString,OUString>("com.sun.star.awt.XDateField","setMin"),
+ pair<OUString,OUString>("com.sun.star.awt.XDateField","setMax"),
+ pair<OUString,OUString>("com.sun.star.awt.XDateField","setFirst"),
+ pair<OUString,OUString>("com.sun.star.awt.XDateField","setLast"),
+ pair<OUString,OUString>("com.sun.star.awt.XDateField","setLongFormat"),
+ pair<OUString,OUString>("com.sun.star.awt.XDateField","setStrictFormat"),
+ pair<OUString,OUString>("com.sun.star.awt.XDialog","setTitle"),
+ pair<OUString,OUString>("com.sun.star.awt.XDisplayConnection","addEventHandler"),
+ pair<OUString,OUString>("com.sun.star.awt.XDisplayConnection","removeEventHandler"),
+ pair<OUString,OUString>("com.sun.star.awt.XDisplayConnection","addErrorHandler"),
+ pair<OUString,OUString>("com.sun.star.awt.XDisplayConnection","removeErrorHandler"),
+ pair<OUString,OUString>("com.sun.star.awt.XExtendedToolkit","addTopWindowListener"),
+ pair<OUString,OUString>("com.sun.star.awt.XExtendedToolkit","removeTopWindowListener"),
+ pair<OUString,OUString>("com.sun.star.awt.XExtendedToolkit","addKeyHandler"),
+ pair<OUString,OUString>("com.sun.star.awt.XExtendedToolkit","removeKeyHandler"),
+ pair<OUString,OUString>("com.sun.star.awt.XExtendedToolkit","addFocusListener"),
+ pair<OUString,OUString>("com.sun.star.awt.XExtendedToolkit","removeFocusListener"),
+ pair<OUString,OUString>("com.sun.star.awt.XExtendedToolkit","fireFocusGained"),
+ pair<OUString,OUString>("com.sun.star.awt.XExtendedToolkit","fireFocusLost"),
+ pair<OUString,OUString>("com.sun.star.awt.XFileDialog","setPath"),
+ pair<OUString,OUString>("com.sun.star.awt.XFileDialog","setFilters"),
+ pair<OUString,OUString>("com.sun.star.awt.XFileDialog","setCurrentFilter"),
+ pair<OUString,OUString>("com.sun.star.awt.XFixedHyperlink","setText"),
+ pair<OUString,OUString>("com.sun.star.awt.XFixedHyperlink","setURL"),
+ pair<OUString,OUString>("com.sun.star.awt.XFixedHyperlink","setAlignment"),
+ pair<OUString,OUString>("com.sun.star.awt.XFixedHyperlink","addActionListener"),
+ pair<OUString,OUString>("com.sun.star.awt.XFixedHyperlink","removeActionListener"),
+ pair<OUString,OUString>("com.sun.star.awt.XFixedText","setText"),
+ pair<OUString,OUString>("com.sun.star.awt.XFixedText","setAlignment"),
+ pair<OUString,OUString>("com.sun.star.awt.XFocusListener","focusGained"),
+ pair<OUString,OUString>("com.sun.star.awt.XFocusListener","focusLost"),
+ pair<OUString,OUString>("com.sun.star.awt.XGraphics","setFont"),
+ pair<OUString,OUString>("com.sun.star.awt.XGraphics","selectFont"),
+ pair<OUString,OUString>("com.sun.star.awt.XGraphics","setTextColor"),
+ pair<OUString,OUString>("com.sun.star.awt.XGraphics","setTextFillColor"),
+ pair<OUString,OUString>("com.sun.star.awt.XGraphics","setLineColor"),
+ pair<OUString,OUString>("com.sun.star.awt.XGraphics","setFillColor"),
+ pair<OUString,OUString>("com.sun.star.awt.XGraphics","setRasterOp"),
+ pair<OUString,OUString>("com.sun.star.awt.XGraphics","setClipRegion"),
+ pair<OUString,OUString>("com.sun.star.awt.XGraphics","intersectClipRegion"),
+ pair<OUString,OUString>("com.sun.star.awt.XGraphics","push"),
+ pair<OUString,OUString>("com.sun.star.awt.XGraphics","pop"),
+ pair<OUString,OUString>("com.sun.star.awt.XGraphics","copy"),
+ pair<OUString,OUString>("com.sun.star.awt.XGraphics","draw"),
+ pair<OUString,OUString>("com.sun.star.awt.XGraphics","drawPixel"),
+ pair<OUString,OUString>("com.sun.star.awt.XGraphics","drawLine"),
+ pair<OUString,OUString>("com.sun.star.awt.XGraphics","drawRect"),
+ pair<OUString,OUString>("com.sun.star.awt.XGraphics","drawRoundedRect"),
+ pair<OUString,OUString>("com.sun.star.awt.XGraphics","drawPolyLine"),
+ pair<OUString,OUString>("com.sun.star.awt.XGraphics","drawPolygon"),
+ pair<OUString,OUString>("com.sun.star.awt.XGraphics","drawPolyPolygon"),
+ pair<OUString,OUString>("com.sun.star.awt.XGraphics","drawEllipse"),
+ pair<OUString,OUString>("com.sun.star.awt.XGraphics","drawArc"),
+ pair<OUString,OUString>("com.sun.star.awt.XGraphics","drawPie"),
+ pair<OUString,OUString>("com.sun.star.awt.XGraphics","drawChord"),
+ pair<OUString,OUString>("com.sun.star.awt.XGraphics","drawGradient"),
+ pair<OUString,OUString>("com.sun.star.awt.XGraphics","drawText"),
+ pair<OUString,OUString>("com.sun.star.awt.XGraphics","drawTextArray"),
+ pair<OUString,OUString>("com.sun.star.awt.XImageButton","addActionListener"),
+ pair<OUString,OUString>("com.sun.star.awt.XImageButton","removeActionListener"),
+ pair<OUString,OUString>("com.sun.star.awt.XImageButton","setActionCommand"),
+ pair<OUString,OUString>("com.sun.star.awt.XImageConsumer","init"),
+ pair<OUString,OUString>("com.sun.star.awt.XImageConsumer","setColorModel"),
+ pair<OUString,OUString>("com.sun.star.awt.XImageConsumer","setPixelsByBytes"),
+ pair<OUString,OUString>("com.sun.star.awt.XImageConsumer","setPixelsByLongs"),
+ pair<OUString,OUString>("com.sun.star.awt.XImageConsumer","complete"),
+ pair<OUString,OUString>("com.sun.star.awt.XImageProducer","addConsumer"),
+ pair<OUString,OUString>("com.sun.star.awt.XImageProducer","removeConsumer"),
+ pair<OUString,OUString>("com.sun.star.awt.XImageProducer","startProduction"),
+ pair<OUString,OUString>("com.sun.star.awt.XItemEventBroadcaster","addItemListener"),
+ pair<OUString,OUString>("com.sun.star.awt.XItemEventBroadcaster","removeItemListener"),
+ pair<OUString,OUString>("com.sun.star.awt.XItemListener","itemStateChanged"),
+ pair<OUString,OUString>("com.sun.star.awt.XKeyListener","keyPressed"),
+ pair<OUString,OUString>("com.sun.star.awt.XKeyListener","keyReleased"),
+ pair<OUString,OUString>("com.sun.star.awt.XListBox","addItemListener"),
+ pair<OUString,OUString>("com.sun.star.awt.XListBox","removeItemListener"),
+ pair<OUString,OUString>("com.sun.star.awt.XListBox","addActionListener"),
+ pair<OUString,OUString>("com.sun.star.awt.XListBox","removeActionListener"),
+ pair<OUString,OUString>("com.sun.star.awt.XListBox","addItem"),
+ pair<OUString,OUString>("com.sun.star.awt.XListBox","addItems"),
+ pair<OUString,OUString>("com.sun.star.awt.XListBox","removeItems"),
+ pair<OUString,OUString>("com.sun.star.awt.XListBox","selectItemPos"),
+ pair<OUString,OUString>("com.sun.star.awt.XListBox","selectItemsPos"),
+ pair<OUString,OUString>("com.sun.star.awt.XListBox","selectItem"),
+ pair<OUString,OUString>("com.sun.star.awt.XListBox","setMultipleMode"),
+ pair<OUString,OUString>("com.sun.star.awt.XListBox","setDropDownLineCount"),
+ pair<OUString,OUString>("com.sun.star.awt.XListBox","makeVisible"),
+ pair<OUString,OUString>("com.sun.star.awt.XMenu","addMenuListener"),
+ pair<OUString,OUString>("com.sun.star.awt.XMenu","removeMenuListener"),
+ pair<OUString,OUString>("com.sun.star.awt.XMenu","insertItem"),
+ pair<OUString,OUString>("com.sun.star.awt.XMenu","removeItem"),
+ pair<OUString,OUString>("com.sun.star.awt.XMenu","enableItem"),
+ pair<OUString,OUString>("com.sun.star.awt.XMenu","setItemText"),
+ pair<OUString,OUString>("com.sun.star.awt.XMenu","setPopupMenu"),
+ pair<OUString,OUString>("com.sun.star.awt.XMenuListener","highlight"),
+ pair<OUString,OUString>("com.sun.star.awt.XMenuListener","select"),
+ pair<OUString,OUString>("com.sun.star.awt.XMenuListener","activate"),
+ pair<OUString,OUString>("com.sun.star.awt.XMenuListener","deactivate"),
+ pair<OUString,OUString>("com.sun.star.awt.XMessageBox","setCaptionText"),
+ pair<OUString,OUString>("com.sun.star.awt.XMessageBox","setMessageText"),
+ pair<OUString,OUString>("com.sun.star.awt.XMouseListener","mousePressed"),
+ pair<OUString,OUString>("com.sun.star.awt.XMouseListener","mouseReleased"),
+ pair<OUString,OUString>("com.sun.star.awt.XMouseListener","mouseEntered"),
+ pair<OUString,OUString>("com.sun.star.awt.XMouseListener","mouseExited"),
+ pair<OUString,OUString>("com.sun.star.awt.XNumericField","setValue"),
+ pair<OUString,OUString>("com.sun.star.awt.XNumericField","setMin"),
+ pair<OUString,OUString>("com.sun.star.awt.XNumericField","setMax"),
+ pair<OUString,OUString>("com.sun.star.awt.XNumericField","setFirst"),
+ pair<OUString,OUString>("com.sun.star.awt.XNumericField","setLast"),
+ pair<OUString,OUString>("com.sun.star.awt.XNumericField","setSpinSize"),
+ pair<OUString,OUString>("com.sun.star.awt.XNumericField","setDecimalDigits"),
+ pair<OUString,OUString>("com.sun.star.awt.XNumericField","setStrictFormat"),
+ pair<OUString,OUString>("com.sun.star.awt.XPaintListener","windowPaint"),
+ pair<OUString,OUString>("com.sun.star.awt.XPatternField","setMasks"),
+ pair<OUString,OUString>("com.sun.star.awt.XPatternField","setString"),
+ pair<OUString,OUString>("com.sun.star.awt.XPatternField","setStrictFormat"),
+ pair<OUString,OUString>("com.sun.star.awt.XPointer","setType"),
+ pair<OUString,OUString>("com.sun.star.awt.XPopupMenu","insertSeparator"),
+ pair<OUString,OUString>("com.sun.star.awt.XPopupMenu","setDefaultItem"),
+ pair<OUString,OUString>("com.sun.star.awt.XPopupMenu","checkItem"),
+ pair<OUString,OUString>("com.sun.star.awt.XProgressBar","setForegroundColor"),
+ pair<OUString,OUString>("com.sun.star.awt.XProgressBar","setBackgroundColor"),
+ pair<OUString,OUString>("com.sun.star.awt.XProgressBar","setRange"),
+ pair<OUString,OUString>("com.sun.star.awt.XProgressBar","setValue"),
+ pair<OUString,OUString>("com.sun.star.awt.XProgressMonitor","addText"),
+ pair<OUString,OUString>("com.sun.star.awt.XProgressMonitor","removeText"),
+ pair<OUString,OUString>("com.sun.star.awt.XProgressMonitor","updateText"),
+ pair<OUString,OUString>("com.sun.star.awt.XRadioButton","addItemListener"),
+ pair<OUString,OUString>("com.sun.star.awt.XRadioButton","removeItemListener"),
+ pair<OUString,OUString>("com.sun.star.awt.XRadioButton","setState"),
+ pair<OUString,OUString>("com.sun.star.awt.XRadioButton","setLabel"),
+ pair<OUString,OUString>("com.sun.star.awt.XRegion","clear"),
+ pair<OUString,OUString>("com.sun.star.awt.XRegion","move"),
+ pair<OUString,OUString>("com.sun.star.awt.XRegion","unionRectangle"),
+ pair<OUString,OUString>("com.sun.star.awt.XRegion","intersectRectangle"),
+ pair<OUString,OUString>("com.sun.star.awt.XRegion","excludeRectangle"),
+ pair<OUString,OUString>("com.sun.star.awt.XRegion","xOrRectangle"),
+ pair<OUString,OUString>("com.sun.star.awt.XRegion","unionRegion"),
+ pair<OUString,OUString>("com.sun.star.awt.XRegion","intersectRegion"),
+ pair<OUString,OUString>("com.sun.star.awt.XRegion","excludeRegion"),
+ pair<OUString,OUString>("com.sun.star.awt.XRegion","xOrRegion"),
+ pair<OUString,OUString>("com.sun.star.awt.XScrollBar","addAdjustmentListener"),
+ pair<OUString,OUString>("com.sun.star.awt.XScrollBar","removeAdjustmentListener"),
+ pair<OUString,OUString>("com.sun.star.awt.XScrollBar","setValue"),
+ pair<OUString,OUString>("com.sun.star.awt.XScrollBar","setValues"),
+ pair<OUString,OUString>("com.sun.star.awt.XScrollBar","setMaximum"),
+ pair<OUString,OUString>("com.sun.star.awt.XScrollBar","setLineIncrement"),
+ pair<OUString,OUString>("com.sun.star.awt.XScrollBar","setBlockIncrement"),
+ pair<OUString,OUString>("com.sun.star.awt.XScrollBar","setVisibleSize"),
+ pair<OUString,OUString>("com.sun.star.awt.XScrollBar","setOrientation"),
+ pair<OUString,OUString>("com.sun.star.awt.XSpinField","addSpinListener"),
+ pair<OUString,OUString>("com.sun.star.awt.XSpinField","removeSpinListener"),
+ pair<OUString,OUString>("com.sun.star.awt.XSpinField","up"),
+ pair<OUString,OUString>("com.sun.star.awt.XSpinField","down"),
+ pair<OUString,OUString>("com.sun.star.awt.XSpinField","first"),
+ pair<OUString,OUString>("com.sun.star.awt.XSpinField","last"),
+ pair<OUString,OUString>("com.sun.star.awt.XSpinField","enableRepeat"),
+ pair<OUString,OUString>("com.sun.star.awt.XSpinListener","up"),
+ pair<OUString,OUString>("com.sun.star.awt.XSpinListener","down"),
+ pair<OUString,OUString>("com.sun.star.awt.XSpinListener","first"),
+ pair<OUString,OUString>("com.sun.star.awt.XSpinListener","last"),
+ pair<OUString,OUString>("com.sun.star.awt.XSpinValue","addAdjustmentListener"),
+ pair<OUString,OUString>("com.sun.star.awt.XSpinValue","removeAdjustmentListener"),
+ pair<OUString,OUString>("com.sun.star.awt.XSpinValue","setValue"),
+ pair<OUString,OUString>("com.sun.star.awt.XSpinValue","setValues"),
+ pair<OUString,OUString>("com.sun.star.awt.XSpinValue","setMinimum"),
+ pair<OUString,OUString>("com.sun.star.awt.XSpinValue","setMaximum"),
+ pair<OUString,OUString>("com.sun.star.awt.XSpinValue","setSpinIncrement"),
+ pair<OUString,OUString>("com.sun.star.awt.XTabController","setModel"),
+ pair<OUString,OUString>("com.sun.star.awt.XTabController","setContainer"),
+ pair<OUString,OUString>("com.sun.star.awt.XTabController","autoTabOrder"),
+ pair<OUString,OUString>("com.sun.star.awt.XTabController","activateTabOrder"),
+ pair<OUString,OUString>("com.sun.star.awt.XTabController","activateFirst"),
+ pair<OUString,OUString>("com.sun.star.awt.XTabController","activateLast"),
+ pair<OUString,OUString>("com.sun.star.awt.XTabControllerModel","setGroupControl"),
+ pair<OUString,OUString>("com.sun.star.awt.XTabControllerModel","setControlModels"),
+ pair<OUString,OUString>("com.sun.star.awt.XTabControllerModel","setGroup"),
+ pair<OUString,OUString>("com.sun.star.awt.XTextComponent","addTextListener"),
+ pair<OUString,OUString>("com.sun.star.awt.XTextComponent","removeTextListener"),
+ pair<OUString,OUString>("com.sun.star.awt.XTextComponent","setText"),
+ pair<OUString,OUString>("com.sun.star.awt.XTextComponent","insertText"),
+ pair<OUString,OUString>("com.sun.star.awt.XTextComponent","setSelection"),
+ pair<OUString,OUString>("com.sun.star.awt.XTextComponent","setEditable"),
+ pair<OUString,OUString>("com.sun.star.awt.XTextComponent","setMaxTextLen"),
+ pair<OUString,OUString>("com.sun.star.awt.XTextEditField","setEchoChar"),
+ pair<OUString,OUString>("com.sun.star.awt.XTextListener","textChanged"),
+ pair<OUString,OUString>("com.sun.star.awt.XTimeField","setTime"),
+ pair<OUString,OUString>("com.sun.star.awt.XTimeField","setMin"),
+ pair<OUString,OUString>("com.sun.star.awt.XTimeField","setMax"),
+ pair<OUString,OUString>("com.sun.star.awt.XTimeField","setFirst"),
+ pair<OUString,OUString>("com.sun.star.awt.XTimeField","setLast"),
+ pair<OUString,OUString>("com.sun.star.awt.XTimeField","setStrictFormat"),
+ pair<OUString,OUString>("com.sun.star.awt.XTopWindow","addTopWindowListener"),
+ pair<OUString,OUString>("com.sun.star.awt.XTopWindow","removeTopWindowListener"),
+ pair<OUString,OUString>("com.sun.star.awt.XTopWindow","toFront"),
+ pair<OUString,OUString>("com.sun.star.awt.XTopWindow","toBack"),
+ pair<OUString,OUString>("com.sun.star.awt.XTopWindow","setMenuBar"),
+ pair<OUString,OUString>("com.sun.star.awt.XTopWindowListener","windowOpened"),
+ pair<OUString,OUString>("com.sun.star.awt.XTopWindowListener","windowClosing"),
+ pair<OUString,OUString>("com.sun.star.awt.XTopWindowListener","windowClosed"),
+ pair<OUString,OUString>("com.sun.star.awt.XTopWindowListener","windowMinimized"),
+ pair<OUString,OUString>("com.sun.star.awt.XTopWindowListener","windowNormalized"),
+ pair<OUString,OUString>("com.sun.star.awt.XTopWindowListener","windowActivated"),
+ pair<OUString,OUString>("com.sun.star.awt.XTopWindowListener","windowDeactivated"),
+ pair<OUString,OUString>("com.sun.star.awt.XUnoControlContainer","setTabControllers"),
+ pair<OUString,OUString>("com.sun.star.awt.XUnoControlContainer","addTabController"),
+ pair<OUString,OUString>("com.sun.star.awt.XUnoControlContainer","removeTabController"),
+ pair<OUString,OUString>("com.sun.star.awt.XUserInputInterception","addKeyHandler"),
+ pair<OUString,OUString>("com.sun.star.awt.XUserInputInterception","removeKeyHandler"),
+ pair<OUString,OUString>("com.sun.star.awt.XUserInputInterception","addMouseClickHandler"),
+ pair<OUString,OUString>("com.sun.star.awt.XUserInputInterception","removeMouseClickHandler"),
+ pair<OUString,OUString>("com.sun.star.awt.XVclContainer","addVclContainerListener"),
+ pair<OUString,OUString>("com.sun.star.awt.XVclContainer","removeVclContainerListener"),
+ pair<OUString,OUString>("com.sun.star.awt.XVclContainerListener","windowAdded"),
+ pair<OUString,OUString>("com.sun.star.awt.XVclContainerListener","windowRemoved"),
+ pair<OUString,OUString>("com.sun.star.awt.XVclContainerPeer","enableDialogControl"),
+ pair<OUString,OUString>("com.sun.star.awt.XVclContainerPeer","setTabOrder"),
+ pair<OUString,OUString>("com.sun.star.awt.XVclContainerPeer","setGroup"),
+ pair<OUString,OUString>("com.sun.star.awt.XVclWindowPeer","setDesignMode"),
+ pair<OUString,OUString>("com.sun.star.awt.XVclWindowPeer","enableClipSiblings"),
+ pair<OUString,OUString>("com.sun.star.awt.XVclWindowPeer","setForeground"),
+ pair<OUString,OUString>("com.sun.star.awt.XVclWindowPeer","setControlFont"),
+ pair<OUString,OUString>("com.sun.star.awt.XView","draw"),
+ pair<OUString,OUString>("com.sun.star.awt.XView","setZoom"),
+ pair<OUString,OUString>("com.sun.star.awt.XWindow","setPosSize"),
+ pair<OUString,OUString>("com.sun.star.awt.XWindow","setVisible"),
+ pair<OUString,OUString>("com.sun.star.awt.XWindow","setEnable"),
+ pair<OUString,OUString>("com.sun.star.awt.XWindow","setFocus"),
+ pair<OUString,OUString>("com.sun.star.awt.XWindow","addWindowListener"),
+ pair<OUString,OUString>("com.sun.star.awt.XWindow","removeWindowListener"),
+ pair<OUString,OUString>("com.sun.star.awt.XWindow","addFocusListener"),
+ pair<OUString,OUString>("com.sun.star.awt.XWindow","removeFocusListener"),
+ pair<OUString,OUString>("com.sun.star.awt.XWindow","addKeyListener"),
+ pair<OUString,OUString>("com.sun.star.awt.XWindow","removeKeyListener"),
+ pair<OUString,OUString>("com.sun.star.awt.XWindow","addMouseListener"),
+ pair<OUString,OUString>("com.sun.star.awt.XWindow","removeMouseListener"),
+ pair<OUString,OUString>("com.sun.star.awt.XWindow","addMouseMotionListener"),
+ pair<OUString,OUString>("com.sun.star.awt.XWindow","removeMouseMotionListener"),
+ pair<OUString,OUString>("com.sun.star.awt.XWindow","addPaintListener"),
+ pair<OUString,OUString>("com.sun.star.awt.XWindow","removePaintListener"),
+ pair<OUString,OUString>("com.sun.star.awt.XWindowListener","windowResized"),
+ pair<OUString,OUString>("com.sun.star.awt.XWindowListener","windowMoved"),
+ pair<OUString,OUString>("com.sun.star.awt.XWindowListener","windowShown"),
+ pair<OUString,OUString>("com.sun.star.awt.XWindowListener","windowHidden"),
+ pair<OUString,OUString>("com.sun.star.awt.XWindowListener2","windowEnabled"),
+ pair<OUString,OUString>("com.sun.star.awt.XWindowListener2","windowDisabled"),
+ pair<OUString,OUString>("com.sun.star.awt.XWindowPeer","setPointer"),
+ pair<OUString,OUString>("com.sun.star.awt.XWindowPeer","setBackground"),
+ pair<OUString,OUString>("com.sun.star.awt.XWindowPeer","invalidate"),
+ pair<OUString,OUString>("com.sun.star.awt.XWindowPeer","invalidateRect"),
+ pair<OUString,OUString>("com.sun.star.awt.grid.XGridSelectionListener","selectionChanged"),
+ pair<OUString,OUString>("com.sun.star.awt.tab.XTabPageContainer","addTabPageContainerListener"),
+ pair<OUString,OUString>("com.sun.star.awt.tab.XTabPageContainer","removeTabPageContainerListener"),
+ pair<OUString,OUString>("com.sun.star.awt.tab.XTabPageContainerListener","tabPageActivated"),
+ pair<OUString,OUString>("com.sun.star.configuration.backend.XBackendChangesNotifier","addChangesListener"),
+ pair<OUString,OUString>("com.sun.star.configuration.backend.XBackendChangesNotifier","removeChangesListener"),
+ pair<OUString,OUString>("com.sun.star.datatransfer.clipboard.XClipboard","setContents"),
+ pair<OUString,OUString>("com.sun.star.datatransfer.clipboard.XClipboardListener","changedContents"),
+ pair<OUString,OUString>("com.sun.star.datatransfer.clipboard.XClipboardNotifier","addClipboardListener"),
+ pair<OUString,OUString>("com.sun.star.datatransfer.clipboard.XClipboardNotifier","removeClipboardListener"),
+ pair<OUString,OUString>("com.sun.star.datatransfer.clipboard.XClipboardOwner","lostOwnership"),
+ pair<OUString,OUString>("com.sun.star.datatransfer.dnd.XAutoscroll","autoscroll"),
+ pair<OUString,OUString>("com.sun.star.datatransfer.dnd.XDragGestureListener","dragGestureRecognized"),
+ pair<OUString,OUString>("com.sun.star.datatransfer.dnd.XDragGestureRecognizer","addDragGestureListener"),
+ pair<OUString,OUString>("com.sun.star.datatransfer.dnd.XDragGestureRecognizer","removeDragGestureListener"),
+ pair<OUString,OUString>("com.sun.star.datatransfer.dnd.XDragSource","startDrag"),
+ pair<OUString,OUString>("com.sun.star.datatransfer.dnd.XDragSourceContext","setCursor"),
+ pair<OUString,OUString>("com.sun.star.datatransfer.dnd.XDragSourceContext","setImage"),
+ pair<OUString,OUString>("com.sun.star.datatransfer.dnd.XDragSourceContext","transferablesFlavorsChanged"),
+ pair<OUString,OUString>("com.sun.star.datatransfer.dnd.XDragSourceListener","dragDropEnd"),
+ pair<OUString,OUString>("com.sun.star.datatransfer.dnd.XDragSourceListener","dragEnter"),
+ pair<OUString,OUString>("com.sun.star.datatransfer.dnd.XDragSourceListener","dragExit"),
+ pair<OUString,OUString>("com.sun.star.datatransfer.dnd.XDragSourceListener","dragOver"),
+ pair<OUString,OUString>("com.sun.star.datatransfer.dnd.XDragSourceListener","dropActionChanged"),
+ pair<OUString,OUString>("com.sun.star.datatransfer.dnd.XDropTarget","addDropTargetListener"),
+ pair<OUString,OUString>("com.sun.star.datatransfer.dnd.XDropTarget","removeDropTargetListener"),
+ pair<OUString,OUString>("com.sun.star.datatransfer.dnd.XDropTarget","setDefaultActions"),
+ pair<OUString,OUString>("com.sun.star.datatransfer.dnd.XDropTargetDragContext","acceptDrag"),
+ pair<OUString,OUString>("com.sun.star.datatransfer.dnd.XDropTargetDragContext","rejectDrag"),
+ pair<OUString,OUString>("com.sun.star.datatransfer.dnd.XDropTargetDropContext","acceptDrop"),
+ pair<OUString,OUString>("com.sun.star.datatransfer.dnd.XDropTargetDropContext","rejectDrop"),
+ pair<OUString,OUString>("com.sun.star.datatransfer.dnd.XDropTargetDropContext","dropComplete"),
+ pair<OUString,OUString>("com.sun.star.datatransfer.dnd.XDropTargetListener","dragEnter"),
+ pair<OUString,OUString>("com.sun.star.datatransfer.dnd.XDropTargetListener","dragExit"),
+ pair<OUString,OUString>("com.sun.star.datatransfer.dnd.XDropTargetListener","dragOver"),
+ pair<OUString,OUString>("com.sun.star.datatransfer.dnd.XDropTargetListener","dropActionChanged"),
+ pair<OUString,OUString>("com.sun.star.document.XEventBroadcaster","addEventListener"),
+ pair<OUString,OUString>("com.sun.star.document.XEventBroadcaster","removeEventListener"),
+ pair<OUString,OUString>("com.sun.star.document.XEventListener","notifyEvent"),
+ pair<OUString,OUString>("com.sun.star.document.XStorageChangeListener","notifyStorageChange"),
+ pair<OUString,OUString>("com.sun.star.drawing.XControlShape","setControl"),
+ pair<OUString,OUString>("com.sun.star.form.XApproveActionBroadcaster","addApproveActionListener"),
+ pair<OUString,OUString>("com.sun.star.form.XApproveActionBroadcaster","removeApproveActionListener"),
+ pair<OUString,OUString>("com.sun.star.form.XBoundControl","setLock"),
+ pair<OUString,OUString>("com.sun.star.form.XChangeBroadcaster","addChangeListener"),
+ pair<OUString,OUString>("com.sun.star.form.XChangeBroadcaster","removeChangeListener"),
+ pair<OUString,OUString>("com.sun.star.form.XChangeListener","changed"),
+ pair<OUString,OUString>("com.sun.star.form.XConfirmDeleteBroadcaster","addConfirmDeleteListener"),
+ pair<OUString,OUString>("com.sun.star.form.XConfirmDeleteBroadcaster","removeConfirmDeleteListener"),
+ pair<OUString,OUString>("com.sun.star.form.XDatabaseParameterBroadcaster","addParameterListener"),
+ pair<OUString,OUString>("com.sun.star.form.XDatabaseParameterBroadcaster","removeParameterListener"),
+ pair<OUString,OUString>("com.sun.star.form.XDatabaseParameterBroadcaster2","addDatabaseParameterListener"),
+ pair<OUString,OUString>("com.sun.star.form.XDatabaseParameterBroadcaster2","removeDatabaseParameterListener"),
+ pair<OUString,OUString>("com.sun.star.form.XErrorBroadcaster","addErrorListener"),
+ pair<OUString,OUString>("com.sun.star.form.XErrorBroadcaster","removeErrorListener"),
+ pair<OUString,OUString>("com.sun.star.form.XFormController","addActivateListener"),
+ pair<OUString,OUString>("com.sun.star.form.XFormController","removeActivateListener"),
+ pair<OUString,OUString>("com.sun.star.form.XFormControllerListener","formActivated"),
+ pair<OUString,OUString>("com.sun.star.form.XFormControllerListener","formDeactivated"),
+ pair<OUString,OUString>("com.sun.star.form.XGrid","setCurrentColumnPosition"),
+ pair<OUString,OUString>("com.sun.star.form.XGridPeer","setColumns"),
+ pair<OUString,OUString>("com.sun.star.form.XLoadListener","loaded"),
+ pair<OUString,OUString>("com.sun.star.form.XLoadListener","unloading"),
+ pair<OUString,OUString>("com.sun.star.form.XLoadListener","unloaded"),
+ pair<OUString,OUString>("com.sun.star.form.XLoadListener","reloading"),
+ pair<OUString,OUString>("com.sun.star.form.XLoadListener","reloaded"),
+ pair<OUString,OUString>("com.sun.star.form.XLoadable","load"),
+ pair<OUString,OUString>("com.sun.star.form.XLoadable","unload"),
+ pair<OUString,OUString>("com.sun.star.form.XLoadable","reload"),
+ pair<OUString,OUString>("com.sun.star.form.XLoadable","addLoadListener"),
+ pair<OUString,OUString>("com.sun.star.form.XLoadable","removeLoadListener"),
+ pair<OUString,OUString>("com.sun.star.form.XPositioningListener","positioned"),
+ pair<OUString,OUString>("com.sun.star.form.XReset","reset"),
+ pair<OUString,OUString>("com.sun.star.form.XReset","addResetListener"),
+ pair<OUString,OUString>("com.sun.star.form.XReset","removeResetListener"),
+ pair<OUString,OUString>("com.sun.star.form.XResetListener","resetted"),
+ pair<OUString,OUString>("com.sun.star.form.XSubmit","submit"),
+ pair<OUString,OUString>("com.sun.star.form.XSubmit","addSubmitListener"),
+ pair<OUString,OUString>("com.sun.star.form.XSubmit","removeSubmitListener"),
+ pair<OUString,OUString>("com.sun.star.form.XUpdateBroadcaster","addUpdateListener"),
+ pair<OUString,OUString>("com.sun.star.form.XUpdateBroadcaster","removeUpdateListener"),
+ pair<OUString,OUString>("com.sun.star.frame.XBrowseHistoryRegistry","updateViewData"),
+ pair<OUString,OUString>("com.sun.star.frame.XBrowseHistoryRegistry","createNewEntry"),
+ pair<OUString,OUString>("com.sun.star.frame.XConfigManager","addPropertyChangeListener"),
+ pair<OUString,OUString>("com.sun.star.frame.XConfigManager","removePropertyChangeListener"),
+ pair<OUString,OUString>("com.sun.star.frame.XConfigManager","flush"),
+ pair<OUString,OUString>("com.sun.star.frame.XDesktop","addTerminateListener"),
+ pair<OUString,OUString>("com.sun.star.frame.XDesktop","removeTerminateListener"),
+ pair<OUString,OUString>("com.sun.star.frame.XDispatch","dispatch"),
+ pair<OUString,OUString>("com.sun.star.frame.XDispatch","addStatusListener"),
+ pair<OUString,OUString>("com.sun.star.frame.XDispatch","removeStatusListener"),
+ pair<OUString,OUString>("com.sun.star.frame.XDocumentTemplates","update"),
+ pair<OUString,OUString>("com.sun.star.frame.XFrame","setCreator"),
+ pair<OUString,OUString>("com.sun.star.frame.XFrame","setName"),
+ pair<OUString,OUString>("com.sun.star.frame.XFrame","activate"),
+ pair<OUString,OUString>("com.sun.star.frame.XFrame","deactivate"),
+ pair<OUString,OUString>("com.sun.star.frame.XFrame","addFrameActionListener"),
+ pair<OUString,OUString>("com.sun.star.frame.XFrame","removeFrameActionListener"),
+ pair<OUString,OUString>("com.sun.star.frame.XFrameActionListener","frameAction"),
+ pair<OUString,OUString>("com.sun.star.frame.XFrameLoader","load"),
+ pair<OUString,OUString>("com.sun.star.frame.XFrameLoader","cancel"),
+ pair<OUString,OUString>("com.sun.star.frame.XLoadEventListener","loadFinished"),
+ pair<OUString,OUString>("com.sun.star.frame.XLoadEventListener","loadCancelled"),
+ pair<OUString,OUString>("com.sun.star.frame.XModel","connectController"),
+ pair<OUString,OUString>("com.sun.star.frame.XModel","disconnectController"),
+ pair<OUString,OUString>("com.sun.star.frame.XModel","lockControllers"),
+ pair<OUString,OUString>("com.sun.star.frame.XModel","unlockControllers"),
+ pair<OUString,OUString>("com.sun.star.frame.XNotifyingDispatch","dispatchWithNotification"),
+ pair<OUString,OUString>("com.sun.star.frame.XRecordableDispatch","dispatchAndRecord"),
+ pair<OUString,OUString>("com.sun.star.frame.XSessionManagerClient","addSessionManagerListener"),
+ pair<OUString,OUString>("com.sun.star.frame.XSessionManagerClient","removeSessionManagerListener"),
+ pair<OUString,OUString>("com.sun.star.frame.XSessionManagerClient","queryInteraction"),
+ pair<OUString,OUString>("com.sun.star.frame.XSessionManagerClient","interactionDone"),
+ pair<OUString,OUString>("com.sun.star.frame.XSessionManagerClient","saveDone"),
+ pair<OUString,OUString>("com.sun.star.frame.XSessionManagerListener","doSave"),
+ pair<OUString,OUString>("com.sun.star.frame.XSessionManagerListener","approveInteraction"),
+ pair<OUString,OUString>("com.sun.star.frame.XSessionManagerListener","shutdownCanceled"),
+ pair<OUString,OUString>("com.sun.star.frame.XSessionManagerListener2","doQuit"),
+ pair<OUString,OUString>("com.sun.star.frame.XStatusListener","statusChanged"),
+ pair<OUString,OUString>("com.sun.star.frame.XTask","tileWindows"),
+ pair<OUString,OUString>("com.sun.star.frame.XTask","arrangeWindowsVertical"),
+ pair<OUString,OUString>("com.sun.star.frame.XTask","arrangeWindowsHorizontal"),
+ pair<OUString,OUString>("com.sun.star.frame.XWindowArranger","arrange"),
+ pair<OUString,OUString>("com.sun.star.inspection.XPropertyControlContext","activateNextControl"),
+ pair<OUString,OUString>("com.sun.star.inspection.XPropertyControlObserver","focusGained"),
+ pair<OUString,OUString>("com.sun.star.inspection.XPropertyControlObserver","valueChanged"),
+ pair<OUString,OUString>("com.sun.star.mozilla.XCloseSessionListener","sessionClosed"),
+ pair<OUString,OUString>("com.sun.star.mozilla.XMenuProxy","addMenuProxyListener"),
+ pair<OUString,OUString>("com.sun.star.mozilla.XMenuProxy","removeMenuProxyListener"),
+ pair<OUString,OUString>("com.sun.star.mozilla.XPluginInstance","start"),
+ pair<OUString,OUString>("com.sun.star.mozilla.XPluginInstance","stop"),
+ pair<OUString,OUString>("com.sun.star.mozilla.XPluginInstance","destroy"),
+ pair<OUString,OUString>("com.sun.star.mozilla.XPluginInstance","createWindow"),
+ pair<OUString,OUString>("com.sun.star.mozilla.XPluginInstance","newStream"),
+ pair<OUString,OUString>("com.sun.star.mozilla.XPluginInstance","newURL"),
+ pair<OUString,OUString>("com.sun.star.mozilla.XPluginInstanceNotifySink","notifyURL"),
+ pair<OUString,OUString>("com.sun.star.mozilla.XPluginInstancePeer","showStatusMessage"),
+ pair<OUString,OUString>("com.sun.star.mozilla.XPluginInstancePeer","enableScripting"),
+ pair<OUString,OUString>("com.sun.star.mozilla.XPluginInstancePeer","newStream"),
+ pair<OUString,OUString>("com.sun.star.mozilla.XPluginInstancePeer","getURL"),
+ pair<OUString,OUString>("com.sun.star.mozilla.XPluginInstancePeer","postURL"),
+ pair<OUString,OUString>("com.sun.star.mozilla.XPluginWindowPeer","setChildWindow"),
+ pair<OUString,OUString>("com.sun.star.script.vba.XVBACompatibility","addVBAScriptListener"),
+ pair<OUString,OUString>("com.sun.star.script.vba.XVBACompatibility","removeVBAScriptListener"),
+ pair<OUString,OUString>("com.sun.star.sdb.XDatabaseAccess","addDatabaseAccessListener"),
+ pair<OUString,OUString>("com.sun.star.sdb.XDatabaseAccess","removeDatabaseAccessListener"),
+ pair<OUString,OUString>("com.sun.star.sdb.XDatabaseAccessListener","connectionChanged"),
+ pair<OUString,OUString>("com.sun.star.sdb.XDatabaseAccessListener","connectionClosing"),
+ pair<OUString,OUString>("com.sun.star.sdb.XRowSetApproveBroadcaster","addRowSetApproveListener"),
+ pair<OUString,OUString>("com.sun.star.sdb.XRowSetApproveBroadcaster","removeRowSetApproveListener"),
+ pair<OUString,OUString>("com.sun.star.sdb.XRowSetChangeListener","onRowSetChanged"),
+ pair<OUString,OUString>("com.sun.star.sdb.XRowSetSupplier","setRowSet"),
+ pair<OUString,OUString>("com.sun.star.sdb.XRowsChangeListener","rowsChanged"),
+ pair<OUString,OUString>("com.sun.star.sdb.XSQLErrorBroadcaster","addSQLErrorListener"),
+ pair<OUString,OUString>("com.sun.star.sdb.XSQLErrorBroadcaster","removeSQLErrorListener"),
+ pair<OUString,OUString>("com.sun.star.sdbc.XRowSet","addRowSetListener"),
+ pair<OUString,OUString>("com.sun.star.sdbc.XRowSet","removeRowSetListener"),
+ pair<OUString,OUString>("com.sun.star.sdbc.XRowSetListener","cursorMoved"),
+ pair<OUString,OUString>("com.sun.star.sdbc.XRowSetListener","rowChanged"),
+ pair<OUString,OUString>("com.sun.star.sdbc.XRowSetListener","rowSetChanged"),
+ pair<OUString,OUString>("com.sun.star.sheet.XCalculatable","enableAutomaticCalculation"),
+ pair<OUString,OUString>("com.sun.star.sheet.XVolatileResult","addResultListener"),
+ pair<OUString,OUString>("com.sun.star.sheet.XVolatileResult","removeResultListener"),
+ pair<OUString,OUString>("com.sun.star.task.XJobExecutor","trigger"),
+ pair<OUString,OUString>("com.sun.star.task.XStatusIndicator","start"),
+ pair<OUString,OUString>("com.sun.star.task.XStatusIndicator","end"),
+ pair<OUString,OUString>("com.sun.star.task.XStatusIndicator","setText"),
+ pair<OUString,OUString>("com.sun.star.task.XStatusIndicator","setValue"),
+ pair<OUString,OUString>("com.sun.star.task.XStatusIndicator","reset"),
+ pair<OUString,OUString>("com.sun.star.text.XSimpleText","insertString"),
+ pair<OUString,OUString>("com.sun.star.text.XTextCursor","collapseToStart"),
+ pair<OUString,OUString>("com.sun.star.text.XTextCursor","collapseToEnd"),
+ pair<OUString,OUString>("com.sun.star.text.XTextRange","setString"),
+ pair<OUString,OUString>("com.sun.star.text.XTextViewCursor","setVisible"),
+ pair<OUString,OUString>("com.sun.star.ucb.XCommandProcessor","abort"),
+ pair<OUString,OUString>("com.sun.star.ucb.XCommandProcessor2","releaseCommandIdentifier"),
+ pair<OUString,OUString>("com.sun.star.ucb.XContent","addContentEventListener"),
+ pair<OUString,OUString>("com.sun.star.ucb.XContent","removeContentEventListener"),
+ pair<OUString,OUString>("com.sun.star.ucb.XContentProviderManager","deregisterContentProvider"),
+ pair<OUString,OUString>("com.sun.star.ucb.XContentTransmitter","transmit"),
+ pair<OUString,OUString>("com.sun.star.ucb.XPropertySetRegistry","removePropertySet"),
+ pair<OUString,OUString>("com.sun.star.ui.XUIConfigurationListener","elementInserted"),
+ pair<OUString,OUString>("com.sun.star.ui.XUIConfigurationListener","elementRemoved"),
+ pair<OUString,OUString>("com.sun.star.ui.XUIConfigurationListener","elementReplaced"),
+ pair<OUString,OUString>("com.sun.star.ui.dialogs.XFilePickerNotifier","addFilePickerListener"),
+ pair<OUString,OUString>("com.sun.star.ui.dialogs.XFilePickerNotifier","removeFilePickerListener"),
+ pair<OUString,OUString>("com.sun.star.util.XBroadcaster","lockBroadcasts"),
+ pair<OUString,OUString>("com.sun.star.util.XBroadcaster","unlockBroadcasts"),
+ pair<OUString,OUString>("com.sun.star.util.XChangesListener","changesOccurred"),
+ pair<OUString,OUString>("com.sun.star.util.XChangesNotifier","addChangesListener"),
+ pair<OUString,OUString>("com.sun.star.util.XChangesNotifier","removeChangesListener"),
+ pair<OUString,OUString>("com.sun.star.util.XCloseBroadcaster","addCloseListener"),
+ pair<OUString,OUString>("com.sun.star.util.XCloseBroadcaster","removeCloseListener"),
+ pair<OUString,OUString>("com.sun.star.util.XFlushable","addFlushListener"),
+ pair<OUString,OUString>("com.sun.star.util.XFlushable","removeFlushListener"),
+ pair<OUString,OUString>("com.sun.star.util.XModeChangeListener","modeChanged"),
+ pair<OUString,OUString>("com.sun.star.util.XModifyBroadcaster","addModifyListener"),
+ pair<OUString,OUString>("com.sun.star.util.XModifyBroadcaster","removeModifyListener"),
+ pair<OUString,OUString>("com.sun.star.util.XRefreshable","addRefreshListener"),
+ pair<OUString,OUString>("com.sun.star.util.XRefreshable","removeRefreshListener"),
+ pair<OUString,OUString>("com.sun.star.util.XSearchDescriptor","setSearchString"),
+ pair<OUString,OUString>("com.sun.star.view.XPrintJobBroadcaster","addPrintJobListener"),
+ pair<OUString,OUString>("com.sun.star.view.XPrintJobBroadcaster","removePrintJobListener"),
+ pair<OUString,OUString>("com.sun.star.view.XPrintJobListener","printJobEvent"),
+ pair<OUString,OUString>("com.sun.star.view.XPrintableBroadcaster","addPrintableListener"),
+ pair<OUString,OUString>("com.sun.star.view.XPrintableBroadcaster","removePrintableListener"),
+ pair<OUString,OUString>("com.sun.star.view.XPrintableListener","stateChanged"),
+ pair<OUString,OUString>("com.sun.star.view.XSelectionChangeListener","selectionChanged"),
+ pair<OUString,OUString>("com.sun.star.beans.XMultiPropertySet","addPropertiesChangeListener"),
+ pair<OUString,OUString>("com.sun.star.beans.XMultiPropertySet","removePropertiesChangeListener"),
+ pair<OUString,OUString>("com.sun.star.beans.XMultiPropertySet","firePropertiesChangeEvent"),
+ pair<OUString,OUString>("com.sun.star.beans.XPropertiesChangeNotifier","addPropertiesChangeListener"),
+ pair<OUString,OUString>("com.sun.star.beans.XPropertiesChangeNotifier","removePropertiesChangeListener"),
+ pair<OUString,OUString>("com.sun.star.container.XContainer","addContainerListener"),
+ pair<OUString,OUString>("com.sun.star.container.XContainer","removeContainerListener"),
+ pair<OUString,OUString>("com.sun.star.container.XContainerListener","elementInserted"),
+ pair<OUString,OUString>("com.sun.star.container.XContainerListener","elementRemoved"),
+ pair<OUString,OUString>("com.sun.star.container.XContainerListener","elementReplaced"),
+ pair<OUString,OUString>("com.sun.star.container.XNamed","setName"),
+ pair<OUString,OUString>("com.sun.star.io.XDataExporter","exportData"),
+ pair<OUString,OUString>("com.sun.star.io.XDataExporter","cancel"),
+ pair<OUString,OUString>("com.sun.star.io.XDataImporter","importData"),
+ pair<OUString,OUString>("com.sun.star.io.XDataImporter","cancel"),
+ pair<OUString,OUString>("com.sun.star.io.XDataTransferEventListener","finished"),
+ pair<OUString,OUString>("com.sun.star.io.XDataTransferEventListener","cancelled"),
+ pair<OUString,OUString>("com.sun.star.lang.XConnectionPointContainer","advise"),
+ pair<OUString,OUString>("com.sun.star.lang.XConnectionPointContainer","unadvise"),
+ pair<OUString,OUString>("com.sun.star.script.XAllListener","firing"),
+ pair<OUString,OUString>("com.sun.star.uno.XInterface","acquire"),
+ pair<OUString,OUString>("com.sun.star.uno.XInterface","release"),
+ pair<OUString,OUString>("com.sun.star.uno.XReference","dispose")};
+
+ pair<OUString,OUString> k(_rListenerType, _rMethodName);
+ return delayed_event_listeners.find(k) != delayed_event_listeners.end();
+ }
+
+
+ void FormScriptListener::impl_doFireScriptEvent_nothrow( std::unique_lock<std::mutex>& _rGuard, const ScriptEvent& _rEvent, Any* _pSynchronousResult )
+ {
+ OSL_PRECOND( m_pScriptExecutor, "FormScriptListener::impl_doFireScriptEvent_nothrow: this will crash!" );
+
+ _rGuard.unlock();
+ m_pScriptExecutor->doFireScriptEvent( _rEvent, _pSynchronousResult );
+ }
+
+
+ void SAL_CALL FormScriptListener::firing( const ScriptEvent& _rEvent )
+ {
+ if ( _rEvent.ScriptType == "VBAInterop" )
+ return; // not handled here
+
+ std::unique_lock aGuard( m_aMutex );
+
+ if ( impl_isDisposed_nothrow() )
+ return;
+
+ if ( !impl_allowAsynchronousCall_nothrow( _rEvent.ListenerType.getTypeName(), _rEvent.MethodName ) )
+ {
+ impl_doFireScriptEvent_nothrow( aGuard, _rEvent, nullptr );
+ return;
+ }
+
+ acquire();
+ Application::PostUserEvent( LINK( this, FormScriptListener, OnAsyncScriptEvent ), new ScriptEvent( _rEvent ) );
+ }
+
+
+ Any SAL_CALL FormScriptListener::approveFiring( const ScriptEvent& _rEvent )
+ {
+ Any aResult;
+
+ std::unique_lock aGuard( m_aMutex );
+ if ( !impl_isDisposed_nothrow() )
+ impl_doFireScriptEvent_nothrow( aGuard, _rEvent, &aResult );
+
+ return aResult;
+ }
+
+
+ void SAL_CALL FormScriptListener::disposing( const EventObject& /*Source*/ )
+ {
+ // not interested in
+ }
+
+
+ void FormScriptListener::dispose()
+ {
+ std::unique_lock aGuard( m_aMutex );
+ m_pScriptExecutor = nullptr;
+ }
+
+ IMPL_LINK( FormScriptListener, OnAsyncScriptEvent, void*, p, void )
+ {
+ ScriptEvent* _pEvent = static_cast<ScriptEvent*>(p);
+ OSL_PRECOND( _pEvent != nullptr, "FormScriptListener::OnAsyncScriptEvent: invalid event!" );
+ if ( !_pEvent )
+ return;
+
+ {
+ std::unique_lock aGuard( m_aMutex );
+
+ if ( !impl_isDisposed_nothrow() )
+ impl_doFireScriptEvent_nothrow( aGuard, *_pEvent, nullptr );
+ }
+
+ delete _pEvent;
+ // we acquired ourself immediately before posting the event
+ release();
+ }
+
+ FormScriptingEnvironment::FormScriptingEnvironment( FmFormModel& _rModel )
+ :m_rFormModel( _rModel )
+ ,m_bDisposed( false )
+ {
+ m_pScriptListener = new FormScriptListener( this );
+ // note that this is a cyclic reference between the FormScriptListener and the FormScriptingEnvironment
+ // This cycle is broken up when our instance is disposed.
+ }
+
+ void FormScriptingEnvironment::impl_registerOrRevoke_throw( const Reference< XEventAttacherManager >& _rxManager, bool _bRegister )
+ {
+ ::osl::MutexGuard aGuard( m_aMutex );
+
+ if ( !_rxManager.is() )
+ throw IllegalArgumentException();
+ if ( m_bDisposed )
+ throw DisposedException();
+
+ try
+ {
+ if ( _bRegister )
+ _rxManager->addScriptListener( m_pScriptListener );
+ else
+ _rxManager->removeScriptListener( m_pScriptListener );
+ }
+ catch( const RuntimeException& ) { throw; }
+ catch( const Exception& )
+ {
+ DBG_UNHANDLED_EXCEPTION("svx");
+ }
+ }
+
+
+ void FormScriptingEnvironment::registerEventAttacherManager( const Reference< XEventAttacherManager >& _rxManager )
+ {
+ impl_registerOrRevoke_throw( _rxManager, true );
+ }
+
+
+ void FormScriptingEnvironment::revokeEventAttacherManager( const Reference< XEventAttacherManager >& _rxManager )
+ {
+ impl_registerOrRevoke_throw( _rxManager, false );
+ }
+
+#if HAVE_FEATURE_SCRIPTING
+ namespace
+ {
+ class NewStyleUNOScript
+ {
+ SfxObjectShell& m_rObjectShell;
+ const OUString m_sScriptCode;
+
+ public:
+ NewStyleUNOScript( SfxObjectShell& _rObjectShell, const OUString& _rScriptCode )
+ :m_rObjectShell( _rObjectShell )
+ ,m_sScriptCode( _rScriptCode )
+ {
+ }
+
+ void invoke( const Sequence< Any >& _rArguments, Any& _rSynchronousResult );
+ };
+
+
+ void NewStyleUNOScript::invoke( const Sequence< Any >& _rArguments, Any& _rSynchronousResult )
+ {
+ Sequence< sal_Int16 > aOutArgsIndex;
+ Sequence< Any > aOutArgs;
+ EventObject aEvent;
+ Any aCaller;
+ if ( _rArguments.hasElements() && ( _rArguments[ 0 ] >>= aEvent ) )
+ {
+ try
+ {
+ Reference< XControl > xControl( aEvent.Source, UNO_QUERY_THROW );
+ Reference< XPropertySet > xProps( xControl->getModel(), UNO_QUERY_THROW );
+ aCaller = xProps->getPropertyValue("Name");
+ }
+ catch( Exception& ) {}
+ }
+ m_rObjectShell.CallXScript( m_sScriptCode, _rArguments, _rSynchronousResult, aOutArgsIndex, aOutArgs, true, aCaller.hasValue() ? &aCaller : nullptr );
+ }
+ }
+#endif
+
+ void FormScriptingEnvironment::doFireScriptEvent( const ScriptEvent& _rEvent, Any* _pSynchronousResult )
+ {
+#if !HAVE_FEATURE_SCRIPTING
+ (void) _rEvent;
+ (void) _pSynchronousResult;
+ (void) m_rFormModel;
+#else
+ SolarMutexClearableGuard aSolarGuard;
+ ::osl::ClearableMutexGuard aGuard( m_aMutex );
+
+ if ( m_bDisposed )
+ return;
+
+ // SfxObjectShellRef is good here since the model controls the lifetime of the object
+ SfxObjectShellRef xObjectShell = m_rFormModel.GetObjectShell();
+ if( !xObjectShell.is() )
+ return;
+
+ // the script to execute
+ std::shared_ptr< NewStyleUNOScript > pScript;
+
+ if ( _rEvent.ScriptType != "StarBasic" )
+ {
+ pScript = std::make_shared<NewStyleUNOScript>( *xObjectShell, _rEvent.ScriptCode );
+ }
+ else
+ {
+ OUString sScriptCode = _rEvent.ScriptCode;
+ OUString sMacroLocation;
+
+ // is there a location in the script name ("application" or "document")?
+ sal_Int32 nPrefixLen = sScriptCode.indexOf( ':' );
+ DBG_ASSERT( 0 <= nPrefixLen, "FormScriptingEnvironment::doFireScriptEvent: Basic script name in old format encountered!" );
+
+ if ( 0 <= nPrefixLen )
+ {
+ // and it has such a prefix
+ sMacroLocation = sScriptCode.copy( 0, nPrefixLen );
+ DBG_ASSERT( sMacroLocation == "document"
+ || sMacroLocation == "application",
+ "FormScriptingEnvironment::doFireScriptEvent: invalid (unknown) prefix!" );
+
+ // strip the prefix: the SfxObjectShell::CallScript knows nothing about such prefixes
+ sScriptCode = sScriptCode.copy( nPrefixLen + 1 );
+ }
+
+ if ( sMacroLocation.isEmpty() )
+ {
+ // legacy format: use the app-wide Basic, if it has a respective method, otherwise fall back to the doc's Basic
+ if ( SfxApplication::GetBasicManager()->HasMacro( sScriptCode ) )
+ sMacroLocation = "application";
+ else
+ sMacroLocation = "document";
+ }
+
+ OUString sScriptURI = "vnd.sun.star.script:" +
+ sScriptCode +
+ "?language=Basic&location=" +
+ sMacroLocation;
+
+ pScript = std::make_shared<NewStyleUNOScript>( *xObjectShell, sScriptURI );
+ }
+
+ assert(pScript && "FormScriptingEnvironment::doFireScriptEvent: no script to execute!");
+
+ aGuard.clear();
+ aSolarGuard.clear();
+
+ Any aIgnoreResult;
+ pScript->invoke( _rEvent.Arguments, _pSynchronousResult ? *_pSynchronousResult : aIgnoreResult );
+ pScript.reset();
+
+ {
+ // object shells are not thread safe, so guard the destruction
+ SolarMutexGuard aSolarGuarsReset;
+ xObjectShell = nullptr;
+ }
+#endif
+ }
+
+
+ void FormScriptingEnvironment::dispose()
+ {
+ ::osl::MutexGuard aGuard( m_aMutex );
+ m_bDisposed = true;
+ m_pScriptListener->dispose();
+ m_pScriptListener.clear();
+ }
+
+}
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/form/fmservs.cxx b/svx/source/form/fmservs.cxx
new file mode 100644
index 000000000..a6c0daf5d
--- /dev/null
+++ b/svx/source/form/fmservs.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 <com/sun/star/container/XSet.hpp>
+#include <cppuhelper/factory.hxx>
+#include <comphelper/processfactory.hxx>
+#include <com/sun/star/lang/XSingleServiceFactory.hpp>
+#include <com/sun/star/lang/XMultiServiceFactory.hpp>
+#include <fmservs.hxx>
+
+using namespace com::sun::star;
+
+#define REGISTER_SERVICE(ImplName, ServiceName) \
+ sString = (ServiceName); \
+ xSingleFactory = ::cppu::createSingleFactory(xServiceFactory, \
+ OUString(), ImplName##_NewInstance_Impl, \
+ uno::Sequence< OUString>(&sString, 1)); \
+ if (xSingleFactory.is()) \
+ xSet->insert(uno::Any(xSingleFactory));
+
+namespace svxform
+{
+ void ImplSmartRegisterUnoServices()
+ {
+ uno::Reference< lang::XMultiServiceFactory > xServiceFactory = ::comphelper::getProcessServiceFactory();
+ uno::Reference< container::XSet > xSet(xServiceFactory, uno::UNO_QUERY);
+ if (!xSet.is())
+ return;
+
+ uno::Reference< lang::XSingleServiceFactory > xSingleFactory;
+
+ OUString sString;
+
+
+ // FormController
+ REGISTER_SERVICE( FormController, "com.sun.star.form.runtime.FormController" );
+ REGISTER_SERVICE( LegacyFormController, "com.sun.star.form.FormController" );
+
+
+ // FormController - register selfaware service
+ xSingleFactory = ::cppu::createSingleFactory( xServiceFactory,
+ OAddConditionDialog_GetImplementationName(),
+ OAddConditionDialog_Create,
+ OAddConditionDialog_GetSupportedServiceNames()
+ );
+ if ( xSingleFactory.is() )
+ xSet->insert( uno::Any( xSingleFactory ) );
+
+
+ // DBGridControl
+ REGISTER_SERVICE(FmXGridControl, FM_CONTROL_GRID); // compatibility
+ REGISTER_SERVICE(FmXGridControl, FM_CONTROL_GRIDCONTROL);
+ REGISTER_SERVICE(FmXGridControl, FM_SUN_CONTROL_GRIDCONTROL);
+ };
+
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/form/fmshell.cxx b/svx/source/form/fmshell.cxx
new file mode 100644
index 000000000..5ffa29917
--- /dev/null
+++ b/svx/source/form/fmshell.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 <fmvwimp.hxx>
+#include <svx/fmshell.hxx>
+#include <svx/fmtools.hxx>
+#include <fmprop.hxx>
+#include <fmundo.hxx>
+#include <com/sun/star/beans/XPropertySet.hpp>
+#include <com/sun/star/awt/XTabControllerModel.hpp>
+#include <sfx2/viewfrm.hxx>
+#include <vcl/svapp.hxx>
+#include <vcl/weld.hxx>
+#include <svl/whiter.hxx>
+#include <sfx2/app.hxx>
+#include <svl/intitem.hxx>
+#include <svl/stritem.hxx>
+#include <svl/visitem.hxx>
+#include <unotools/moduleoptions.hxx>
+#include <sfx2/objface.hxx>
+#include <sfx2/request.hxx>
+#include <sfx2/dispatch.hxx>
+#include <svx/svdobj.hxx>
+#include <svx/fmpage.hxx>
+#include <svx/svditer.hxx>
+
+#include <svx/svxids.hrc>
+
+#include <svx/svdobjkind.hxx>
+#include <svl/eitem.hxx>
+#include <tools/diagnose_ex.h>
+#include <svx/svdpage.hxx>
+#include <svx/fmmodel.hxx>
+#include <fmshimp.hxx>
+#include <svx/svdpagv.hxx>
+#include <sfx2/objitem.hxx>
+#include <sfx2/viewsh.hxx>
+#include <fmexpl.hxx>
+#include <formcontrolling.hxx>
+#include <comphelper/types.hxx>
+#include <fmdocumentclassification.hxx>
+#include <formtoolbars.hxx>
+
+#include <svx/svxdlg.hxx>
+
+#include <svx/sdrobjectfilter.hxx>
+
+#define ShellClass_FmFormShell
+#include <svxslots.hxx>
+
+#include <memory>
+
+// is used for Invalidate -> maintain it as well
+// sort ascending !!!!!!
+sal_uInt16 const ControllerSlotMap[] = // slots of the controller
+{
+ SID_FM_CONFIG,
+ SID_FM_PUSHBUTTON,
+ SID_FM_RADIOBUTTON,
+ SID_FM_CHECKBOX,
+ SID_FM_FIXEDTEXT,
+ SID_FM_GROUPBOX,
+ SID_FM_EDIT,
+ SID_FM_LISTBOX,
+ SID_FM_COMBOBOX,
+ SID_FM_DBGRID,
+ SID_FM_IMAGEBUTTON,
+ SID_FM_FILECONTROL,
+ SID_FM_NAVIGATIONBAR,
+ SID_FM_CTL_PROPERTIES,
+ SID_FM_PROPERTIES,
+ SID_FM_TAB_DIALOG,
+ SID_FM_ADD_FIELD,
+ SID_FM_DESIGN_MODE,
+ SID_FM_SHOW_FMEXPLORER,
+ SID_FM_SHOW_PROPERTIES,
+ SID_FM_FMEXPLORER_CONTROL,
+ SID_FM_DATEFIELD,
+ SID_FM_TIMEFIELD,
+ SID_FM_NUMERICFIELD,
+ SID_FM_CURRENCYFIELD,
+ SID_FM_PATTERNFIELD,
+ SID_FM_OPEN_READONLY,
+ SID_FM_IMAGECONTROL,
+ SID_FM_USE_WIZARDS,
+ SID_FM_FORMATTEDFIELD,
+ SID_FM_FILTER_NAVIGATOR,
+ SID_FM_AUTOCONTROLFOCUS,
+ SID_FM_SCROLLBAR,
+ SID_FM_SPINBUTTON,
+ SID_FM_SHOW_DATANAVIGATOR,
+ SID_FM_DATANAVIGATOR_CONTROL,
+
+ 0
+};
+
+using namespace ::com::sun::star::uno;
+using namespace ::com::sun::star::awt;
+using namespace ::com::sun::star::sdbc;
+using namespace ::com::sun::star::sdbcx;
+using namespace ::com::sun::star::beans;
+using namespace ::com::sun::star::form;
+using namespace ::com::sun::star::form::runtime;
+using namespace ::svxform;
+
+FmDesignModeChangedHint::FmDesignModeChangedHint( bool bDesMode )
+ :m_bDesignMode( bDesMode )
+{
+}
+
+
+FmDesignModeChangedHint::~FmDesignModeChangedHint()
+{
+}
+
+SFX_IMPL_INTERFACE(FmFormShell, SfxShell)
+
+void FmFormShell::InitInterface_Impl()
+{
+ GetStaticInterface()->RegisterObjectBar(SFX_OBJECTBAR_NAVIGATION, SfxVisibilityFlags::Standard|SfxVisibilityFlags::ReadonlyDoc,
+ ToolbarId::SvxTbx_Form_Navigation,
+ SfxShellFeature::FormShowDatabaseBar);
+
+ GetStaticInterface()->RegisterObjectBar(SFX_OBJECTBAR_NAVIGATION, SfxVisibilityFlags::Standard|SfxVisibilityFlags::ReadonlyDoc,
+ ToolbarId::SvxTbx_Form_Filter,
+ SfxShellFeature::FormShowFilterBar);
+
+ GetStaticInterface()->RegisterObjectBar(SFX_OBJECTBAR_OBJECT, SfxVisibilityFlags::Standard | SfxVisibilityFlags::ReadonlyDoc,
+ ToolbarId::SvxTbx_Text_Control_Attributes,
+ SfxShellFeature::FormShowTextControlBar);
+
+ GetStaticInterface()->RegisterChildWindow(SID_FM_ADD_FIELD, false, SfxShellFeature::FormShowField);
+ GetStaticInterface()->RegisterChildWindow(SID_FM_SHOW_PROPERTIES, false, SfxShellFeature::FormShowProperties);
+ GetStaticInterface()->RegisterChildWindow(SID_FM_SHOW_FMEXPLORER, false, SfxShellFeature::FormShowExplorer);
+ GetStaticInterface()->RegisterChildWindow(SID_FM_FILTER_NAVIGATOR, false, SfxShellFeature::FormShowFilterNavigator);
+ GetStaticInterface()->RegisterChildWindow(SID_FM_SHOW_DATANAVIGATOR, false, SfxShellFeature::FormShowDataNavigator);
+
+ GetStaticInterface()->RegisterObjectBar(SFX_OBJECTBAR_OBJECT, SfxVisibilityFlags::Standard,
+ ToolbarId::SvxTbx_Controls,
+ SfxShellFeature::FormTBControls);
+
+ GetStaticInterface()->RegisterObjectBar(SFX_OBJECTBAR_OBJECT, SfxVisibilityFlags::Standard,
+ ToolbarId::SvxTbx_FormDesign,
+ SfxShellFeature::FormTBDesign);
+}
+
+
+FmFormShell::FmFormShell( SfxViewShell* _pParent, FmFormView* pView )
+ :SfxShell(_pParent)
+ ,m_pImpl(new FmXFormShell(*this, _pParent->GetViewFrame()))
+ ,m_pFormView( pView )
+ ,m_pFormModel( nullptr )
+ ,m_nLastSlot( 0 )
+ ,m_bDesignMode( true )
+ ,m_bHasForms(false)
+{
+ SetPool( &SfxGetpApp()->GetPool() );
+ SetName( "Form" );
+
+ SetView(m_pFormView);
+}
+
+
+FmFormShell::~FmFormShell()
+{
+ if ( m_pFormView )
+ SetView( nullptr );
+
+ m_pImpl->dispose();
+}
+
+
+void FmFormShell::NotifyMarkListChanged(FmFormView* pWhichView)
+{
+ FmNavViewMarksChanged aChangeNotification(pWhichView);
+ Broadcast(aChangeNotification);
+}
+
+
+bool FmFormShell::PrepareClose(bool bUI)
+{
+ if (GetImpl()->didPrepareClose_Lock())
+ // we already made a PrepareClose for the current modifications of the current form
+ return true;
+
+ bool bResult = true;
+ // Save the data records, not in DesignMode and FilterMode
+ if (!m_bDesignMode && !GetImpl()->isInFilterMode_Lock() &&
+ m_pFormView && m_pFormView->GetActualOutDev() &&
+ m_pFormView->GetActualOutDev()->GetOutDevType() == OUTDEV_WINDOW)
+ {
+ SdrPageView* pCurPageView = m_pFormView->GetSdrPageView();
+
+ // sal_uInt16 nPos = pCurPageView ? pCurPageView->GetWinList().Find((OutputDevice*)m_pFormView->GetActualOutDev()) : SDRPAGEVIEWWIN_NOTFOUND;
+ SdrPageWindow* pWindow = pCurPageView ? pCurPageView->FindPageWindow(*const_cast<OutputDevice*>(m_pFormView->GetActualOutDev())) : nullptr;
+
+ if(pWindow)
+ {
+ // First, the current contents of the controls are stored.
+ // If everything has gone smoothly, the modified records are stored.
+ if (GetImpl()->getActiveController_Lock().is())
+ {
+ const svx::ControllerFeatures& rController = GetImpl()->getActiveControllerFeatures_Lock();
+ if ( rController->commitCurrentControl() )
+ {
+ const bool bModified = rController->isModifiedRow();
+
+ if ( bModified && bUI )
+ {
+ SfxViewShell* pShell = GetViewShell();
+ vcl::Window* pShellWnd = pShell ? pShell->GetWindow() : nullptr;
+ weld::Widget* pFrameWeld = pShellWnd ? pShellWnd->GetFrameWeld() : nullptr;
+ std::unique_ptr<weld::Builder> xBuilder(Application::CreateBuilder(pFrameWeld, "svx/ui/savemodifieddialog.ui"));
+ std::unique_ptr<weld::MessageDialog> xQry(xBuilder->weld_message_dialog("SaveModifiedDialog"));
+ switch (xQry->run())
+ {
+ case RET_YES:
+ bResult = rController->commitCurrentRecord( );
+ [[fallthrough]];
+ case RET_NO:
+ GetImpl()->didPrepareClose_Lock(true);
+ break;
+
+ case RET_CANCEL:
+ return false;
+ }
+ }
+ }
+ }
+ }
+ }
+ return bResult;
+}
+
+
+void FmFormShell::impl_setDesignMode(bool bDesign)
+{
+ if (m_pFormView)
+ {
+ if (!bDesign)
+ m_nLastSlot = SID_FM_DESIGN_MODE;
+
+ GetImpl()->SetDesignMode_Lock(bDesign);
+ // my m_bDesignMode is also set by the Impl ...
+ }
+ else
+ {
+ m_bHasForms = false;
+ m_bDesignMode = bDesign;
+ UIFeatureChanged();
+ }
+
+ GetViewShell()->GetViewFrame()->GetBindings().Invalidate(ControllerSlotMap);
+}
+
+
+bool FmFormShell::HasUIFeature(SfxShellFeature nFeature) const
+{
+ assert((nFeature & ~SfxShellFeature::FormMask) == SfxShellFeature::NONE);
+ bool bResult = false;
+ if (nFeature & SfxShellFeature::FormShowDatabaseBar)
+ {
+ // only if forms are also available
+ bResult = !m_bDesignMode && GetImpl()->hasDatabaseBar_Lock() && !GetImpl()->isInFilterMode_Lock();
+ }
+ else if (nFeature & SfxShellFeature::FormShowFilterBar)
+ {
+ // only if forms are also available
+ bResult = !m_bDesignMode && GetImpl()->hasDatabaseBar_Lock() && GetImpl()->isInFilterMode_Lock();
+ }
+ else if (nFeature & SfxShellFeature::FormShowFilterNavigator)
+ {
+ bResult = !m_bDesignMode && GetImpl()->hasDatabaseBar_Lock() && GetImpl()->isInFilterMode_Lock();
+ }
+ else if (nFeature & SfxShellFeature::FormShowField)
+ {
+ bResult = m_bDesignMode && m_pFormView && m_bHasForms;
+ }
+ else if (nFeature & SfxShellFeature::FormShowProperties)
+ {
+ bResult = m_bDesignMode && m_pFormView && m_bHasForms;
+ }
+ else if (nFeature & SfxShellFeature::FormShowExplorer)
+ {
+ bResult = m_bDesignMode; // OJ #101593# && m_pFormView && m_bHasForms;
+ }
+ else if (nFeature & SfxShellFeature::FormShowTextControlBar)
+ {
+ bResult = !GetImpl()->IsReadonlyDoc_Lock() && m_pImpl->IsActiveControl_Lock(true);
+ }
+ else if (nFeature & SfxShellFeature::FormShowDataNavigator)
+ {
+ bResult = GetImpl()->isEnhancedForm_Lock();
+ }
+ else if ( (nFeature & SfxShellFeature::FormTBControls)
+ || (nFeature & SfxShellFeature::FormTBDesign)
+ )
+ {
+ bResult = true;
+ }
+
+ return bResult;
+}
+
+
+void FmFormShell::Execute(SfxRequest &rReq)
+{
+ sal_uInt16 nSlot = rReq.GetSlot();
+
+
+ // set MasterSlot
+ switch( nSlot )
+ {
+ case SID_FM_PUSHBUTTON:
+ case SID_FM_RADIOBUTTON:
+ case SID_FM_CHECKBOX:
+ case SID_FM_FIXEDTEXT:
+ case SID_FM_GROUPBOX:
+ case SID_FM_LISTBOX:
+ case SID_FM_COMBOBOX:
+ case SID_FM_NAVIGATIONBAR:
+ case SID_FM_EDIT:
+ case SID_FM_DBGRID:
+ case SID_FM_IMAGEBUTTON:
+ case SID_FM_IMAGECONTROL:
+ case SID_FM_FILECONTROL:
+ case SID_FM_DATEFIELD:
+ case SID_FM_TIMEFIELD:
+ case SID_FM_NUMERICFIELD:
+ case SID_FM_CURRENCYFIELD:
+ case SID_FM_PATTERNFIELD:
+ case SID_FM_FORMATTEDFIELD:
+ case SID_FM_SCROLLBAR:
+ case SID_FM_SPINBUTTON:
+ m_nLastSlot = nSlot;
+ break;
+ }
+
+
+ // set the Identifier and Inventor of the Uno control
+ SdrObjKind nIdentifier = SdrObjKind::NONE;
+ switch( nSlot )
+ {
+ case SID_FM_CHECKBOX:
+ nIdentifier = SdrObjKind::FormCheckbox;
+ break;
+ case SID_FM_PUSHBUTTON:
+ nIdentifier = SdrObjKind::FormButton;
+ break;
+ case SID_FM_FIXEDTEXT:
+ nIdentifier = SdrObjKind::FormFixedText;
+ break;
+ case SID_FM_LISTBOX:
+ nIdentifier = SdrObjKind::FormListbox;
+ break;
+ case SID_FM_EDIT:
+ nIdentifier = SdrObjKind::FormEdit;
+ break;
+ case SID_FM_RADIOBUTTON:
+ nIdentifier = SdrObjKind::FormRadioButton;
+ break;
+ case SID_FM_GROUPBOX:
+ nIdentifier = SdrObjKind::FormGroupBox;
+ break;
+ case SID_FM_COMBOBOX:
+ nIdentifier = SdrObjKind::FormCombobox;
+ break;
+ case SID_FM_NAVIGATIONBAR:
+ nIdentifier = SdrObjKind::FormNavigationBar;
+ break;
+ case SID_FM_DBGRID:
+ nIdentifier = SdrObjKind::FormGrid;
+ break;
+ case SID_FM_IMAGEBUTTON:
+ nIdentifier = SdrObjKind::FormImageButton;
+ break;
+ case SID_FM_IMAGECONTROL:
+ nIdentifier = SdrObjKind::FormImageControl;
+ break;
+ case SID_FM_FILECONTROL:
+ nIdentifier = SdrObjKind::FormFileControl;
+ break;
+ case SID_FM_DATEFIELD:
+ nIdentifier = SdrObjKind::FormDateField;
+ break;
+ case SID_FM_TIMEFIELD:
+ nIdentifier = SdrObjKind::FormTimeField;
+ break;
+ case SID_FM_NUMERICFIELD:
+ nIdentifier = SdrObjKind::FormNumericField;
+ break;
+ case SID_FM_CURRENCYFIELD:
+ nIdentifier = SdrObjKind::FormCurrencyField;
+ break;
+ case SID_FM_PATTERNFIELD:
+ nIdentifier = SdrObjKind::FormPatternField;
+ break;
+ case SID_FM_FORMATTEDFIELD:
+ nIdentifier = SdrObjKind::FormFormattedField;
+ break;
+ case SID_FM_SCROLLBAR:
+ nIdentifier = SdrObjKind::FormScrollbar;
+ break;
+ case SID_FM_SPINBUTTON:
+ nIdentifier = SdrObjKind::FormSpinButton;
+ break;
+ }
+
+ switch ( nSlot )
+ {
+ case SID_FM_CHECKBOX:
+ case SID_FM_PUSHBUTTON:
+ case SID_FM_FIXEDTEXT:
+ case SID_FM_LISTBOX:
+ case SID_FM_EDIT:
+ case SID_FM_RADIOBUTTON:
+ case SID_FM_COMBOBOX:
+ case SID_FM_NAVIGATIONBAR:
+ case SID_FM_GROUPBOX:
+ case SID_FM_DBGRID:
+ case SID_FM_IMAGEBUTTON:
+ case SID_FM_IMAGECONTROL:
+ case SID_FM_FILECONTROL:
+ case SID_FM_DATEFIELD:
+ case SID_FM_TIMEFIELD:
+ case SID_FM_NUMERICFIELD:
+ case SID_FM_CURRENCYFIELD:
+ case SID_FM_PATTERNFIELD:
+ case SID_FM_FORMATTEDFIELD:
+ case SID_FM_SCROLLBAR:
+ case SID_FM_SPINBUTTON:
+ {
+ const SfxBoolItem* pGrabFocusItem = rReq.GetArg<SfxBoolItem>(SID_FM_TOGGLECONTROLFOCUS);
+ if ( pGrabFocusItem && pGrabFocusItem->GetValue() )
+ { // see below
+ SfxViewShell* pShell = GetViewShell();
+ vcl::Window* pShellWnd = pShell ? pShell->GetWindow() : nullptr;
+ if ( pShellWnd )
+ pShellWnd->GrabFocus();
+ break;
+ }
+
+ SfxUInt16Item aIdentifierItem( SID_FM_CONTROL_IDENTIFIER, static_cast<sal_uInt16>(nIdentifier) );
+ SfxUInt32Item aInventorItem( SID_FM_CONTROL_INVENTOR, sal_uInt32(SdrInventor::FmForm) );
+ const SfxPoolItem* pArgs[] =
+ {
+ &aIdentifierItem, &aInventorItem, nullptr
+ };
+ const SfxPoolItem* pInternalArgs[] =
+ {
+ nullptr
+ };
+
+ GetViewShell()->GetViewFrame()->GetDispatcher()->Execute( SID_FM_CREATE_CONTROL, SfxCallMode::ASYNCHRON,
+ pArgs, rReq.GetModifier(), pInternalArgs );
+
+ if ( rReq.GetModifier() & KEY_MOD1 )
+ {
+ // #99013# if selected with control key, return focus to current view
+ // do this asynchron, so that the creation can be finished first
+ // reusing the SID_FM_TOGGLECONTROLFOCUS is somewhat hacky... which it wouldn't if it would have another
+ // name, so I do not really have a big problem with this...
+ SfxBoolItem aGrabFocusIndicatorItem( SID_FM_TOGGLECONTROLFOCUS, true );
+ GetViewShell()->GetViewFrame()->GetDispatcher()->ExecuteList(
+ nSlot, SfxCallMode::ASYNCHRON,
+ { &aGrabFocusIndicatorItem });
+ }
+
+ rReq.Done();
+ } break;
+ }
+
+ // individual actions
+ switch( nSlot )
+ {
+ case SID_FM_FORM_DESIGN_TOOLS:
+ {
+ FormToolboxes aToolboxAccess(GetImpl()->getHostFrame_Lock());
+ aToolboxAccess.toggleToolbox( nSlot );
+ rReq.Done();
+ }
+ break;
+
+ case SID_FM_TOGGLECONTROLFOCUS:
+ {
+ FmFormView* pFormView = GetFormView();
+ if ( !pFormView )
+ break;
+
+ // if we execute this ourself, then either the application does not implement an own handling for this,
+ // of we're on the top of the dispatcher stack, which means a control has the focus.
+ // In the latter case, we put the focus to the document window, otherwise, we focus the first control
+ const bool bHasControlFocus = GetImpl()->HasControlFocus_Lock();
+ if ( bHasControlFocus )
+ {
+ if (m_pFormView)
+ {
+ const OutputDevice* pDevice = m_pFormView->GetActualOutDev();
+ vcl::Window* pWindow = pDevice->GetOwnerWindow();
+ if ( pWindow )
+ pWindow->GrabFocus();
+ }
+ }
+ else
+ {
+ pFormView->GrabFirstControlFocus( );
+ }
+ }
+ break;
+
+ case SID_FM_VIEW_AS_GRID:
+ GetImpl()->CreateExternalView_Lock();
+ break;
+ case SID_FM_CONVERTTO_EDIT :
+ case SID_FM_CONVERTTO_BUTTON :
+ case SID_FM_CONVERTTO_FIXEDTEXT :
+ case SID_FM_CONVERTTO_LISTBOX :
+ case SID_FM_CONVERTTO_CHECKBOX :
+ case SID_FM_CONVERTTO_RADIOBUTTON :
+ case SID_FM_CONVERTTO_GROUPBOX :
+ case SID_FM_CONVERTTO_COMBOBOX :
+ case SID_FM_CONVERTTO_IMAGEBUTTON :
+ case SID_FM_CONVERTTO_FILECONTROL :
+ case SID_FM_CONVERTTO_DATE :
+ case SID_FM_CONVERTTO_TIME :
+ case SID_FM_CONVERTTO_NUMERIC :
+ case SID_FM_CONVERTTO_CURRENCY :
+ case SID_FM_CONVERTTO_PATTERN :
+ case SID_FM_CONVERTTO_IMAGECONTROL :
+ case SID_FM_CONVERTTO_FORMATTED :
+ case SID_FM_CONVERTTO_SCROLLBAR :
+ case SID_FM_CONVERTTO_SPINBUTTON :
+ case SID_FM_CONVERTTO_NAVIGATIONBAR :
+ GetImpl()->executeControlConversionSlot_Lock(FmXFormShell::SlotToIdent(nSlot));
+ // after the conversion, re-determine the selection, since the
+ // selected object has changed
+ GetImpl()->SetSelection_Lock(GetFormView()->GetMarkedObjectList());
+ break;
+ case SID_FM_LEAVE_CREATE:
+ m_nLastSlot = 0;
+ rReq.Done();
+ break;
+ case SID_FM_SHOW_PROPERTY_BROWSER:
+ {
+ const SfxBoolItem* pShowItem = rReq.GetArg<SfxBoolItem>(SID_FM_SHOW_PROPERTIES);
+ bool bShow = true;
+ if ( pShowItem )
+ bShow = pShowItem->GetValue();
+ GetImpl()->ShowSelectionProperties_Lock(bShow);
+
+ rReq.Done();
+ } break;
+
+ case SID_FM_PROPERTIES:
+ {
+ // display the PropertyBrowser
+ const SfxBoolItem* pShowItem = rReq.GetArg<SfxBoolItem>(nSlot);
+ bool bShow = pShowItem == nullptr || pShowItem->GetValue();
+
+ InterfaceBag aOnlyTheForm;
+ aOnlyTheForm.insert(Reference<XInterface>(GetImpl()->getCurrentForm_Lock(), UNO_QUERY));
+ GetImpl()->setCurrentSelection_Lock(std::move(aOnlyTheForm));
+
+ GetImpl()->ShowSelectionProperties_Lock(bShow);
+
+ rReq.Done();
+ } break;
+
+ case SID_FM_CTL_PROPERTIES:
+ {
+ const SfxBoolItem* pShowItem = rReq.GetArg<SfxBoolItem>(nSlot);
+ bool bShow = pShowItem == nullptr || pShowItem->GetValue();
+
+ OSL_ENSURE( GetImpl()->onlyControlsAreMarked_Lock(), "FmFormShell::Execute: ControlProperties should be disabled!" );
+ if ( bShow )
+ GetImpl()->selectLastMarkedControls_Lock();
+ GetImpl()->ShowSelectionProperties_Lock(bShow);
+
+ rReq.Done();
+ } break;
+ case SID_FM_SHOW_PROPERTIES:
+ case SID_FM_ADD_FIELD:
+ case SID_FM_FILTER_NAVIGATOR:
+ case SID_FM_SHOW_DATANAVIGATOR :
+ {
+ GetViewShell()->GetViewFrame()->ToggleChildWindow(nSlot);
+ rReq.Done();
+ } break;
+ case SID_FM_SHOW_FMEXPLORER:
+ {
+ if (!m_pFormView) // force setting the view
+ GetViewShell()->GetViewFrame()->GetDispatcher()->Execute(SID_CREATE_SW_DRAWVIEW);
+
+ GetViewShell()->GetViewFrame()->ChildWindowExecute(rReq);
+ rReq.Done();
+ }
+ break;
+
+ case SID_FM_TAB_DIALOG:
+ {
+ GetImpl()->ExecuteTabOrderDialog_Lock(
+ Reference<XTabControllerModel>(GetImpl()->getCurrentForm_Lock(), UNO_QUERY));
+ rReq.Done();
+ }
+ break;
+
+ case SID_FM_DESIGN_MODE:
+ {
+ const SfxBoolItem* pDesignItem = rReq.GetArg<SfxBoolItem>(nSlot);
+ bool bDesignMode = pDesignItem ? pDesignItem->GetValue() : !m_bDesignMode;
+ SetDesignMode( bDesignMode );
+ if ( m_bDesignMode == bDesignMode )
+ rReq.Done();
+
+ m_nLastSlot = SID_FM_DESIGN_MODE;
+ }
+ break;
+
+ case SID_FM_AUTOCONTROLFOCUS:
+ {
+ FmFormModel* pModel = GetFormModel();
+ DBG_ASSERT(pModel, "FmFormShell::Execute : invalid call !");
+ // should have been disabled in GetState if we don't have a FormModel
+ pModel->SetAutoControlFocus( !pModel->GetAutoControlFocus() );
+ GetViewShell()->GetViewFrame()->GetBindings().Invalidate(SID_FM_AUTOCONTROLFOCUS);
+ }
+ break;
+ case SID_FM_OPEN_READONLY:
+ {
+ FmFormModel* pModel = GetFormModel();
+ DBG_ASSERT(pModel, "FmFormShell::Execute : invalid call !");
+ // should have been disabled in GetState if we don't have a FormModel
+ pModel->SetOpenInDesignMode( !pModel->GetOpenInDesignMode() );
+ GetViewShell()->GetViewFrame()->GetBindings().Invalidate(SID_FM_OPEN_READONLY);
+ }
+ break;
+ case SID_FM_USE_WIZARDS:
+ {
+ GetImpl()->SetWizardUsing_Lock(!GetImpl()->GetWizardUsing_Lock());
+ GetViewShell()->GetViewFrame()->GetBindings().Invalidate(SID_FM_USE_WIZARDS);
+ }
+ break;
+ case SID_FM_SEARCH:
+ {
+ const svx::ControllerFeatures& rController = GetImpl()->getActiveControllerFeatures_Lock();
+ if ( rController->commitCurrentControl() && rController->commitCurrentRecord() )
+ GetImpl()->ExecuteSearch_Lock();
+ rReq.Done();
+ }
+ break;
+
+ case SID_FM_RECORD_FIRST:
+ case SID_FM_RECORD_PREV:
+ case SID_FM_RECORD_NEXT:
+ case SID_FM_RECORD_LAST:
+ case SID_FM_RECORD_NEW:
+ case SID_FM_REFRESH:
+ case SID_FM_REFRESH_FORM_CONTROL:
+ case SID_FM_RECORD_DELETE:
+ case SID_FM_RECORD_UNDO:
+ case SID_FM_RECORD_SAVE:
+ case SID_FM_REMOVE_FILTER_SORT:
+ case SID_FM_SORTDOWN:
+ case SID_FM_SORTUP:
+ case SID_FM_AUTOFILTER:
+ case SID_FM_ORDERCRIT:
+ case SID_FM_FORM_FILTERED:
+ {
+ GetImpl()->ExecuteFormSlot_Lock(nSlot);
+ rReq.Done();
+ }
+ break;
+
+ case SID_FM_RECORD_ABSOLUTE:
+ {
+ const svx::ControllerFeatures& rController = GetImpl()->getNavControllerFeatures_Lock();
+ sal_Int32 nRecord = -1;
+
+ const SfxItemSet* pArgs = rReq.GetArgs();
+ if ( pArgs )
+ {
+ const SfxPoolItem* pItem;
+ if ( ( pArgs->GetItemState( FN_PARAM_1, true, &pItem ) ) == SfxItemState::SET )
+ {
+ const SfxInt32Item* pTypedItem = dynamic_cast<const SfxInt32Item* >( pItem );
+ if ( pTypedItem )
+ nRecord = std::max( pTypedItem->GetValue(), sal_Int32(0) );
+ }
+ }
+ else
+ {
+ SvxAbstractDialogFactory* pFact = SvxAbstractDialogFactory::Create();
+ ScopedVclPtr<AbstractFmInputRecordNoDialog> dlg(pFact->CreateFmInputRecordNoDialog(rReq.GetFrameWeld()));
+ dlg->SetValue( rController->getCursor()->getRow() );
+ if ( dlg->Execute() == RET_OK )
+ nRecord = dlg->GetValue();
+
+ rReq.AppendItem( SfxInt32Item( FN_PARAM_1, nRecord ) );
+ }
+
+ if ( nRecord != -1 )
+ rController->execute( nSlot, "Position", Any( nRecord ) );
+
+ rReq.Done();
+ } break;
+ case SID_FM_FILTER_EXECUTE:
+ case SID_FM_FILTER_EXIT:
+ {
+ bool bCancelled = ( SID_FM_FILTER_EXIT == nSlot );
+ bool bReopenNavigator = false;
+
+ if ( !bCancelled )
+ {
+ // if the filter navigator is still open, we need to close it, so it can possibly
+ // commit it's most recent changes
+ if ( GetViewShell() && GetViewShell()->GetViewFrame() )
+ if ( GetViewShell()->GetViewFrame()->HasChildWindow( SID_FM_FILTER_NAVIGATOR ) )
+ {
+ GetViewShell()->GetViewFrame()->ToggleChildWindow( SID_FM_FILTER_NAVIGATOR );
+ bReopenNavigator = true;
+ }
+
+ Reference<runtime::XFormController> const xController(GetImpl()->getActiveController_Lock());
+
+ if ( GetViewShell()->GetViewFrame()->HasChildWindow( SID_FM_FILTER_NAVIGATOR )
+ // closing the window was denied, for instance because of an invalid criterion
+
+ || ( xController.is()
+ && !GetImpl()->getActiveControllerFeatures_Lock()->commitCurrentControl()
+ )
+ // committing the controller was denied
+ )
+ {
+ rReq.Done();
+ break;
+ }
+ }
+
+ GetImpl()->stopFiltering_Lock(!bCancelled);
+ rReq.Done();
+
+ if ( bReopenNavigator )
+ // we closed the navigator only to implicitly commit it (as we do not have another
+ // direct wire to it), but to the user, it should look as it was always open
+ GetViewShell()->GetViewFrame()->ToggleChildWindow( SID_FM_FILTER_NAVIGATOR );
+ }
+ break;
+
+ case SID_FM_FILTER_START:
+ {
+ GetImpl()->startFiltering_Lock();
+ rReq.Done();
+
+ // initially open the filter navigator, the whole form based filter is pretty useless without it
+ SfxBoolItem aIdentifierItem( SID_FM_FILTER_NAVIGATOR, true );
+ GetViewShell()->GetViewFrame()->GetDispatcher()->ExecuteList(
+ SID_FM_FILTER_NAVIGATOR, SfxCallMode::ASYNCHRON,
+ { &aIdentifierItem });
+ } break;
+ }
+}
+
+
+void FmFormShell::GetState(SfxItemSet &rSet)
+{
+ SfxWhichIter aIter( rSet );
+ sal_uInt16 nWhich = aIter.FirstWhich();
+ while ( nWhich )
+ {
+ switch( nWhich )
+ {
+ case SID_FM_FORM_DESIGN_TOOLS:
+ {
+ FormToolboxes aToolboxAccess(GetImpl()->getHostFrame_Lock());
+ rSet.Put( SfxBoolItem( nWhich, aToolboxAccess.isToolboxVisible( nWhich ) ) );
+ }
+ break;
+
+ case SID_FM_FILTER_EXECUTE:
+ case SID_FM_FILTER_EXIT:
+ if (!GetImpl()->isInFilterMode_Lock())
+ rSet.DisableItem( nWhich );
+ break;
+
+ case SID_FM_USE_WIZARDS:
+ if ( !SvtModuleOptions().IsModuleInstalled( SvtModuleOptions::EModule::DATABASE ) )
+ rSet.Put( SfxVisibilityItem( nWhich, false ) );
+ else if (!GetFormModel())
+ rSet.DisableItem( nWhich );
+ else
+ rSet.Put(SfxBoolItem(nWhich, GetImpl()->GetWizardUsing_Lock()));
+ break;
+ case SID_FM_AUTOCONTROLFOCUS:
+ if (!GetFormModel())
+ rSet.DisableItem( nWhich );
+ else
+ rSet.Put( SfxBoolItem(nWhich, GetFormModel()->GetAutoControlFocus() ) );
+ break;
+ case SID_FM_OPEN_READONLY:
+ if (!GetFormModel())
+ rSet.DisableItem( nWhich );
+ else
+ rSet.Put( SfxBoolItem(nWhich, GetFormModel()->GetOpenInDesignMode() ) );
+ break;
+
+ case SID_FM_NAVIGATIONBAR:
+ case SID_FM_DBGRID:
+ if ( !SvtModuleOptions().IsModuleInstalled( SvtModuleOptions::EModule::DATABASE ) )
+ {
+ rSet.Put( SfxVisibilityItem( nWhich, false ) );
+ break;
+ }
+ [[fallthrough]];
+
+ case SID_FM_SCROLLBAR:
+ case SID_FM_IMAGECONTROL:
+ case SID_FM_FILECONTROL:
+ case SID_FM_CURRENCYFIELD:
+ case SID_FM_PATTERNFIELD:
+ case SID_FM_IMAGEBUTTON:
+ case SID_FM_RADIOBUTTON:
+ case SID_FM_COMBOBOX:
+ case SID_FM_GROUPBOX:
+ case SID_FM_CHECKBOX:
+ case SID_FM_PUSHBUTTON:
+ case SID_FM_FIXEDTEXT:
+ case SID_FM_LISTBOX:
+ case SID_FM_EDIT:
+ case SID_FM_DATEFIELD:
+ case SID_FM_TIMEFIELD:
+ case SID_FM_NUMERICFIELD:
+ case SID_FM_FORMATTEDFIELD:
+ case SID_FM_SPINBUTTON:
+ if (!m_bDesignMode)
+ rSet.DisableItem( nWhich );
+ else
+ {
+ bool bLayerLocked = false;
+ if (m_pFormView)
+ {
+ // If the css::drawing::Layer is locked, the slots must be disabled. #36897
+ SdrPageView* pPV = m_pFormView->GetSdrPageView();
+ if (pPV != nullptr)
+ bLayerLocked = pPV->IsLayerLocked(m_pFormView->GetActiveLayer());
+ }
+ if (bLayerLocked)
+ rSet.DisableItem( nWhich );
+ else
+ rSet.Put( SfxBoolItem(nWhich, (nWhich==m_nLastSlot)) );
+ }
+ break;
+ case SID_FM_FILTER_NAVIGATOR_CONTROL:
+ {
+ if (GetImpl()->isInFilterMode_Lock())
+ rSet.Put(SfxObjectItem(nWhich, this));
+ else
+ rSet.Put(SfxObjectItem(nWhich));
+ } break;
+ case SID_FM_FIELDS_CONTROL:
+ case SID_FM_PROPERTY_CONTROL:
+ {
+ if (!m_bDesignMode || !m_pFormView || !m_bHasForms)
+ rSet.Put(SfxObjectItem(nWhich));
+ else
+ rSet.Put(SfxObjectItem(nWhich, this));
+
+ } break;
+ case SID_FM_FMEXPLORER_CONTROL:
+ case SID_FM_DATANAVIGATOR_CONTROL :
+ {
+ if (!m_bDesignMode || !m_pFormView)
+ rSet.Put(SfxObjectItem(nWhich));
+ else
+ rSet.Put(SfxObjectItem(nWhich, this));
+
+ } break;
+ case SID_FM_ADD_FIELD:
+ case SID_FM_SHOW_FMEXPLORER:
+ case SID_FM_SHOW_PROPERTIES:
+ case SID_FM_FILTER_NAVIGATOR:
+ case SID_FM_SHOW_DATANAVIGATOR:
+ {
+ if ( GetViewShell()->GetViewFrame()->KnowsChildWindow(nWhich) )
+ rSet.Put( SfxBoolItem( nWhich, GetViewShell()->GetViewFrame()->HasChildWindow(nWhich)) );
+ else
+ rSet.DisableItem(nWhich);
+ } break;
+
+ case SID_FM_SHOW_PROPERTY_BROWSER:
+ {
+ rSet.Put(SfxBoolItem(nWhich, GetImpl()->IsPropBrwOpen_Lock()));
+ }
+ break;
+
+ case SID_FM_CTL_PROPERTIES:
+ {
+ // potentially, give the Impl the opportunity to update its
+ // current objects which are aligned with the current MarkList
+ if (GetImpl()->IsSelectionUpdatePending_Lock())
+ GetImpl()->ForceUpdateSelection_Lock();
+
+ if (!m_pFormView || !m_bDesignMode || !GetImpl()->onlyControlsAreMarked_Lock())
+ rSet.DisableItem( nWhich );
+ else
+ {
+ bool const bChecked = GetImpl()->IsPropBrwOpen_Lock() && !GetImpl()->isSolelySelected_Lock(GetImpl()->getCurrentForm_Lock());
+ // if the property browser is open, and only controls are marked, and the current selection
+ // does not consist of only the current form, then the current selection is the (composition of)
+ // the currently marked controls
+ rSet.Put( SfxBoolItem( nWhich, bChecked ) );
+ }
+ } break;
+
+ case SID_FM_PROPERTIES:
+ {
+ // potentially, give the Impl the opportunity to update its
+ // current objects which are aligned with the current MarkList
+ if (GetImpl()->IsSelectionUpdatePending_Lock())
+ GetImpl()->ForceUpdateSelection_Lock();
+
+ if (!m_pFormView || !m_bDesignMode || !GetImpl()->getCurrentForm_Lock().is())
+ rSet.DisableItem( nWhich );
+ else
+ {
+ bool const bChecked = GetImpl()->IsPropBrwOpen_Lock() && GetImpl()->isSolelySelected_Lock(GetImpl()->getCurrentForm_Lock());
+ rSet.Put(SfxBoolItem(nWhich, bChecked));
+ }
+ } break;
+ case SID_FM_TAB_DIALOG:
+ // potentially, give the Impl the opportunity to update its
+ // current objects which are aligned with the current MarkList
+ if (GetImpl()->IsSelectionUpdatePending_Lock())
+ GetImpl()->ForceUpdateSelection_Lock();
+
+ if (!m_pFormView || !m_bDesignMode || !GetImpl()->getCurrentForm_Lock().is() )
+ rSet.DisableItem( nWhich );
+ break;
+ case SID_FM_DESIGN_MODE:
+ if (!m_pFormView || GetImpl()->IsReadonlyDoc_Lock())
+ rSet.DisableItem( nWhich );
+ else
+ rSet.Put( SfxBoolItem(nWhich, m_bDesignMode) );
+ break;
+ case SID_FM_SEARCH:
+ case SID_FM_RECORD_FIRST:
+ case SID_FM_RECORD_NEXT:
+ case SID_FM_RECORD_PREV:
+ case SID_FM_RECORD_LAST:
+ case SID_FM_RECORD_NEW:
+ case SID_FM_RECORD_DELETE:
+ case SID_FM_RECORD_ABSOLUTE:
+ case SID_FM_RECORD_TOTAL:
+ case SID_FM_RECORD_SAVE:
+ case SID_FM_RECORD_UNDO:
+ case SID_FM_FORM_FILTERED:
+ case SID_FM_REMOVE_FILTER_SORT:
+ case SID_FM_SORTUP:
+ case SID_FM_SORTDOWN:
+ case SID_FM_ORDERCRIT:
+ case SID_FM_FILTER_START:
+ case SID_FM_AUTOFILTER:
+ case SID_FM_REFRESH:
+ case SID_FM_REFRESH_FORM_CONTROL:
+ case SID_FM_VIEW_AS_GRID:
+ GetFormState(rSet,nWhich);
+ break;
+
+ case SID_FM_CHANGECONTROLTYPE:
+ {
+ if ( !m_pFormView || !m_bDesignMode )
+ rSet.DisableItem( nWhich );
+ else
+ {
+ if (!GetImpl()->canConvertCurrentSelectionToControl_Lock("ConvertToFixed"))
+ // if it cannot be converted to a fixed text, it is no single control
+ rSet.DisableItem( nWhich );
+ }
+ } break;
+
+ case SID_FM_CONVERTTO_FILECONTROL :
+ case SID_FM_CONVERTTO_CURRENCY :
+ case SID_FM_CONVERTTO_PATTERN :
+ case SID_FM_CONVERTTO_IMAGECONTROL :
+ case SID_FM_CONVERTTO_SCROLLBAR :
+ case SID_FM_CONVERTTO_NAVIGATIONBAR :
+ case SID_FM_CONVERTTO_IMAGEBUTTON :
+ case SID_FM_CONVERTTO_EDIT :
+ case SID_FM_CONVERTTO_BUTTON :
+ case SID_FM_CONVERTTO_FIXEDTEXT :
+ case SID_FM_CONVERTTO_LISTBOX :
+ case SID_FM_CONVERTTO_CHECKBOX :
+ case SID_FM_CONVERTTO_RADIOBUTTON :
+ case SID_FM_CONVERTTO_GROUPBOX :
+ case SID_FM_CONVERTTO_COMBOBOX :
+ case SID_FM_CONVERTTO_DATE :
+ case SID_FM_CONVERTTO_TIME :
+ case SID_FM_CONVERTTO_NUMERIC :
+ case SID_FM_CONVERTTO_FORMATTED :
+ case SID_FM_CONVERTTO_SPINBUTTON :
+ {
+ if (!m_pFormView || !m_bDesignMode || !GetImpl()->canConvertCurrentSelectionToControl_Lock(FmXFormShell::SlotToIdent(nWhich)))
+ rSet.DisableItem( nWhich );
+ else
+ {
+ rSet.Put( SfxBoolItem( nWhich, false ) );
+ // just to have a defined state (available and not checked)
+ }
+ }
+ break;
+ }
+ nWhich = aIter.NextWhich();
+ }
+}
+
+
+void FmFormShell::GetFormState(SfxItemSet &rSet, sal_uInt16 nWhich)
+{
+ if ( !GetImpl()->getNavController_Lock().is()
+ || !isRowSetAlive(GetImpl()->getNavController_Lock()->getModel())
+ || !m_pFormView
+ || m_bDesignMode
+ || !GetImpl()->getActiveForm_Lock().is()
+ || GetImpl()->isInFilterMode_Lock()
+ )
+ rSet.DisableItem(nWhich);
+ else
+ {
+ bool bEnable = false;
+ try
+ {
+ switch (nWhich)
+ {
+ case SID_FM_VIEW_AS_GRID:
+ if (GetImpl()->getHostFrame_Lock().is() && GetImpl()->getNavController_Lock().is())
+ {
+ bEnable = true;
+ bool bDisplayingCurrent =
+ GetImpl()->getInternalForm_Lock(
+ Reference<XForm>(GetImpl()->getNavController_Lock()->getModel(), UNO_QUERY)
+ ) == GetImpl()->getExternallyDisplayedForm_Lock();
+ rSet.Put(SfxBoolItem(nWhich, bDisplayingCurrent));
+ }
+ break;
+
+ case SID_FM_SEARCH:
+ {
+ Reference<css::beans::XPropertySet> const xNavSet(GetImpl()->getActiveForm_Lock(), UNO_QUERY);
+ sal_Int32 nCount = ::comphelper::getINT32(xNavSet->getPropertyValue(FM_PROP_ROWCOUNT));
+ bEnable = nCount != 0;
+ } break;
+ case SID_FM_RECORD_ABSOLUTE:
+ case SID_FM_RECORD_TOTAL:
+ {
+ FeatureState aState;
+ GetImpl()->getNavControllerFeatures_Lock()->getState( nWhich, aState );
+ if ( SID_FM_RECORD_ABSOLUTE == nWhich )
+ {
+ sal_Int32 nPosition = 0;
+ aState.State >>= nPosition;
+ rSet.Put( SfxInt32Item( nWhich, nPosition ) );
+ }
+ else if ( SID_FM_RECORD_TOTAL == nWhich )
+ {
+ OUString sTotalCount;
+ aState.State >>= sTotalCount;
+ rSet.Put( SfxStringItem( nWhich, sTotalCount ) );
+ }
+ bEnable = aState.Enabled;
+ }
+ break;
+
+ // first, prev, next, last, and absolute affect the nav controller, not the
+ // active controller
+ case SID_FM_RECORD_FIRST:
+ case SID_FM_RECORD_PREV:
+ case SID_FM_RECORD_NEXT:
+ case SID_FM_RECORD_LAST:
+ case SID_FM_RECORD_NEW:
+ case SID_FM_RECORD_SAVE:
+ case SID_FM_RECORD_UNDO:
+ case SID_FM_RECORD_DELETE:
+ case SID_FM_REFRESH:
+ case SID_FM_REFRESH_FORM_CONTROL:
+ case SID_FM_REMOVE_FILTER_SORT:
+ case SID_FM_SORTUP:
+ case SID_FM_SORTDOWN:
+ case SID_FM_AUTOFILTER:
+ case SID_FM_ORDERCRIT:
+ bEnable = GetImpl()->IsFormSlotEnabled( nWhich, nullptr );
+ break;
+
+ case SID_FM_FORM_FILTERED:
+ {
+ FeatureState aState;
+ bEnable = GetImpl()->IsFormSlotEnabled( nWhich, &aState );
+
+ rSet.Put( SfxBoolItem( nWhich, ::comphelper::getBOOL( aState.State ) ) );
+ }
+ break;
+
+ case SID_FM_FILTER_START:
+ bEnable = GetImpl()->getActiveControllerFeatures_Lock()->canDoFormFilter();
+ break;
+ }
+ }
+ catch( const Exception& )
+ {
+ TOOLS_WARN_EXCEPTION("svx.form", "caught an exception while determining the state!");
+ }
+ if (!bEnable)
+ rSet.DisableItem(nWhich);
+ }
+}
+
+
+FmFormPage* FmFormShell::GetCurPage() const
+{
+ FmFormPage* pP = nullptr;
+ if (m_pFormView && m_pFormView->GetSdrPageView())
+ pP = dynamic_cast<FmFormPage*>( m_pFormView->GetSdrPageView()->GetPage() );
+ return pP;
+}
+
+
+void FmFormShell::SetView( FmFormView* _pView )
+{
+ if ( m_pFormView )
+ {
+ if ( IsActive() )
+ GetImpl()->viewDeactivated_Lock(*m_pFormView);
+
+ m_pFormView->SetFormShell( nullptr, FmFormView::FormShellAccess() );
+ m_pFormView = nullptr;
+ m_pFormModel = nullptr;
+ }
+
+ if ( !_pView )
+ return;
+
+ m_pFormView = _pView;
+ m_pFormView->SetFormShell( this, FmFormView::FormShellAccess() );
+ m_pFormModel = static_cast<FmFormModel*>(m_pFormView->GetModel());
+
+ impl_setDesignMode( m_pFormView->IsDesignMode() );
+
+ // We activate our view if we are activated ourself, but sometimes the Activate precedes the SetView.
+ // But here we know both the view and our activation state so we at least are able to pass the latter
+ // to the former.
+ // FS - 30.06.99 - 67308
+ if ( IsActive() )
+ GetImpl()->viewActivated_Lock(*m_pFormView);
+}
+
+
+void FmFormShell::DetermineForms(bool bInvalidate)
+{
+ // are there forms on the current page
+ bool bForms = GetImpl()->hasForms_Lock();
+ if (bForms != m_bHasForms)
+ {
+ m_bHasForms = bForms;
+ if (bInvalidate)
+ UIFeatureChanged();
+ }
+}
+
+
+bool FmFormShell::GetY2KState(sal_uInt16& nReturn)
+{
+ return GetImpl()->GetY2KState_Lock(nReturn);
+}
+
+
+void FmFormShell::SetY2KState(sal_uInt16 n)
+{
+ GetImpl()->SetY2KState_Lock(n);
+}
+
+
+void FmFormShell::Activate(bool bMDI)
+{
+ SfxShell::Activate(bMDI);
+
+ if ( m_pFormView )
+ GetImpl()->viewActivated_Lock(*m_pFormView, true);
+}
+
+
+void FmFormShell::Deactivate(bool bMDI)
+{
+ SfxShell::Deactivate(bMDI);
+
+ if ( m_pFormView )
+ GetImpl()->viewDeactivated_Lock(*m_pFormView, false);
+}
+
+
+void FmFormShell::ExecuteTextAttribute( SfxRequest& _rReq )
+{
+ m_pImpl->ExecuteTextAttribute_Lock(_rReq);
+}
+
+
+void FmFormShell::GetTextAttributeState( SfxItemSet& _rSet )
+{
+ m_pImpl->GetTextAttributeState_Lock(_rSet);
+}
+
+
+bool FmFormShell::IsActiveControl() const
+{
+ return m_pImpl->IsActiveControl_Lock(false);
+}
+
+
+void FmFormShell::ForgetActiveControl()
+{
+ m_pImpl->ForgetActiveControl_Lock();
+}
+
+
+void FmFormShell::SetControlActivationHandler( const Link<LinkParamNone*,void>& _rHdl )
+{
+ m_pImpl->SetControlActivationHandler_Lock(_rHdl);
+}
+
+
+namespace
+{
+ SdrUnoObj* lcl_findUnoObject( const SdrObjList& _rObjList, const Reference< XControlModel >& _rxModel )
+ {
+ SdrObjListIter aIter( &_rObjList );
+ while ( aIter.IsMore() )
+ {
+ SdrObject* pObject = aIter.Next();
+ SdrUnoObj* pUnoObject = dynamic_cast<SdrUnoObj*>( pObject );
+ if ( !pUnoObject )
+ continue;
+
+ Reference< XControlModel > xControlModel = pUnoObject->GetUnoControlModel();
+ if ( !xControlModel.is() )
+ continue;
+
+ if ( _rxModel == xControlModel )
+ return pUnoObject;
+ }
+ return nullptr;
+ }
+}
+
+
+void FmFormShell::ToggleControlFocus( const SdrUnoObj& i_rUnoObject, const SdrView& i_rView, const OutputDevice& i_rDevice ) const
+{
+ try
+ {
+ // check if the focus currently is in a control
+ // Well, okay, do it the other way 'round: Check whether the current control of the active controller
+ // actually has the focus. This should be equivalent.
+ const bool bHasControlFocus = GetImpl()->HasControlFocus_Lock();
+
+ if ( bHasControlFocus )
+ {
+ vcl::Window* pWindow = i_rDevice.GetOwnerWindow();
+ OSL_ENSURE( pWindow, "FmFormShell::ToggleControlFocus: I need a Window, really!" );
+ if ( pWindow )
+ pWindow->GrabFocus();
+ }
+ else
+ {
+ Reference< XControl > xControl;
+ GetFormControl( i_rUnoObject.GetUnoControlModel(), i_rView, i_rDevice, xControl );
+ Reference< XWindow > xControlWindow( xControl, UNO_QUERY );
+ if ( xControlWindow.is() )
+ xControlWindow->setFocus();
+ }
+ }
+ catch( const Exception& )
+ {
+ DBG_UNHANDLED_EXCEPTION("svx");
+ }
+}
+
+
+namespace
+{
+ class FocusableControlsFilter : public svx::ISdrObjectFilter
+ {
+ public:
+ FocusableControlsFilter( const SdrView& i_rView, const OutputDevice& i_rDevice )
+ :m_rView( i_rView )
+ ,m_rDevice( i_rDevice )
+ {
+ }
+
+ public:
+ virtual bool includeObject( const SdrObject& i_rObject ) const override
+ {
+ const SdrUnoObj* pUnoObj = dynamic_cast< const SdrUnoObj* >( &i_rObject );
+ if ( !pUnoObj )
+ return false;
+
+ Reference< XControl > xControl = pUnoObj->GetUnoControl( m_rView, m_rDevice );
+ return FmXFormView::isFocusable( xControl );
+ }
+
+ private:
+ const SdrView& m_rView;
+ const OutputDevice& m_rDevice;
+ };
+}
+
+
+::std::unique_ptr< svx::ISdrObjectFilter > FmFormShell::CreateFocusableControlFilter( const SdrView& i_rView, const OutputDevice& i_rDevice )
+{
+ ::std::unique_ptr< svx::ISdrObjectFilter > pFilter;
+
+ if ( !i_rView.IsDesignMode() )
+ pFilter.reset( new FocusableControlsFilter( i_rView, i_rDevice ) );
+
+ return pFilter;
+}
+
+
+SdrUnoObj* FmFormShell::GetFormControl( const Reference< XControlModel >& _rxModel, const SdrView& _rView, const OutputDevice& _rDevice, Reference< XControl >& _out_rxControl ) const
+{
+ if ( !_rxModel.is() )
+ return nullptr;
+
+ // we can only retrieve controls for SdrObjects which belong to page which is actually displayed in the given view
+ SdrPageView* pPageView = _rView.GetSdrPageView();
+ SdrPage* pPage = pPageView ? pPageView->GetPage() : nullptr;
+ OSL_ENSURE( pPage, "FmFormShell::GetFormControl: no page displayed in the given view!" );
+ if ( !pPage )
+ return nullptr;
+
+ SdrUnoObj* pUnoObject = lcl_findUnoObject( *pPage, _rxModel );
+ if ( pUnoObject )
+ {
+ _out_rxControl = pUnoObject->GetUnoControl( _rView, _rDevice );
+ return pUnoObject;
+ }
+
+#if OSL_DEBUG_LEVEL > 0
+ // perhaps we are fed with a control model which lives on a page other than the one displayed
+ // in the given view. This is worth being reported as error, in non-product builds.
+ FmFormModel* pModel = GetFormModel();
+ if ( pModel )
+ {
+ sal_uInt16 pageCount = pModel->GetPageCount();
+ for ( sal_uInt16 page = 0; page < pageCount; ++page )
+ {
+ pPage = pModel->GetPage( page );
+ OSL_ENSURE( pPage, "FmFormShell::GetFormControl: NULL page encountered!" );
+ if ( !pPage )
+ continue;
+
+ pUnoObject = lcl_findUnoObject( *pPage, _rxModel );
+ OSL_ENSURE( !pUnoObject, "FmFormShell::GetFormControl: the given control model belongs to a wrong page (displayed elsewhere)!" );
+ }
+ }
+#else
+ (void) this; // avoid loplugin:staticmethods
+#endif
+
+ return nullptr;
+}
+
+
+Reference< runtime::XFormController > FmFormShell::GetFormController( const Reference< XForm >& _rxForm, const SdrView& _rView, const OutputDevice& _rDevice )
+{
+ const FmFormView* pFormView = dynamic_cast< const FmFormView* >( &_rView );
+ if ( !pFormView )
+ return nullptr;
+
+ return pFormView->GetFormController( _rxForm, _rDevice );
+}
+
+
+void FmFormShell::SetDesignMode( bool _bDesignMode )
+{
+ if ( _bDesignMode == m_bDesignMode )
+ return;
+
+ FmFormModel* pModel = GetFormModel();
+ if (pModel)
+ // Switch off the undo environment for the time of the transition. This ensures that
+ // one can also change non-transient properties there. (It should be done with
+ // caution, however, and it should always be reversed when one switches the mode back.
+ // An example is the setting of the maximum text length by the OEditModel on its control.)
+ pModel->GetUndoEnv().Lock();
+
+ // then the actual switch
+ if ( m_bDesignMode || PrepareClose() )
+ impl_setDesignMode(!m_bDesignMode );
+
+ // and my undo environment back on
+ if ( pModel )
+ pModel->GetUndoEnv().UnLock();
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/form/fmshimp.cxx b/svx/source/form/fmshimp.cxx
new file mode 100644
index 000000000..8eb83000b
--- /dev/null
+++ b/svx/source/form/fmshimp.cxx
@@ -0,0 +1,3960 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * 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/macros.h>
+#include <sal/log.hxx>
+#include <fmobj.hxx>
+#include <fmpgeimp.hxx>
+#include <svx/fmtools.hxx>
+#include <fmprop.hxx>
+#include <fmservs.hxx>
+#include <fmshimp.hxx>
+#include <fmtextcontrolshell.hxx>
+#include <fmundo.hxx>
+#include <fmurl.hxx>
+#include <fmvwimp.hxx>
+#include <gridcols.hxx>
+#include <svx/svditer.hxx>
+#include <svx/dialmgr.hxx>
+#include <svx/strings.hrc>
+#include <svx/svdobjkind.hxx>
+#include <svx/fmmodel.hxx>
+#include <svx/fmpage.hxx>
+#include <svx/fmshell.hxx>
+#include <svx/fmview.hxx>
+#include <svx/obj3d.hxx>
+#include <svx/sdrpagewindow.hxx>
+#include <svx/svdpagv.hxx>
+#include <svx/svxdlg.hxx>
+#include <svx/svxids.hrc>
+#include <bitmaps.hlst>
+#include <formnavi.hrc>
+
+#include <com/sun/star/awt/XWindow2.hpp>
+#include <com/sun/star/awt/XCheckBox.hpp>
+#include <com/sun/star/awt/XListBox.hpp>
+#include <com/sun/star/awt/XTextComponent.hpp>
+#include <com/sun/star/beans/theIntrospection.hpp>
+#include <com/sun/star/beans/PropertyAttribute.hpp>
+#include <com/sun/star/beans/XPropertyState.hpp>
+#include <com/sun/star/container/XContainer.hpp>
+#include <com/sun/star/container/XIndexAccess.hpp>
+#include <com/sun/star/container/XNamed.hpp>
+#include <com/sun/star/form/ListSourceType.hpp>
+#include <com/sun/star/form/TabOrderDialog.hpp>
+#include <com/sun/star/form/XGrid.hpp>
+#include <com/sun/star/form/XGridPeer.hpp>
+#include <com/sun/star/form/XLoadable.hpp>
+#include <com/sun/star/form/XReset.hpp>
+#include <com/sun/star/form/binding/XBindableValue.hpp>
+#include <com/sun/star/form/binding/XListEntrySink.hpp>
+#include <com/sun/star/frame/FrameSearchFlag.hpp>
+#include <com/sun/star/lang/XServiceInfo.hpp>
+#include <com/sun/star/script/XEventAttacherManager.hpp>
+#include <com/sun/star/sdbc/SQLException.hpp>
+#include <com/sun/star/ui/dialogs/XExecutableDialog.hpp>
+#include <com/sun/star/util/XModeSelector.hpp>
+#include <com/sun/star/util/XNumberFormatsSupplier.hpp>
+#include <com/sun/star/view/XSelectionSupplier.hpp>
+
+#include <comphelper/evtmethodhelper.hxx>
+#include <comphelper/processfactory.hxx>
+#include <comphelper/property.hxx>
+#include <comphelper/sequence.hxx>
+#include <comphelper/solarmutex.hxx>
+#include <comphelper/string.hxx>
+#include <comphelper/types.hxx>
+#include <connectivity/dbtools.hxx>
+#include <sfx2/dispatch.hxx>
+#include <sfx2/frame.hxx>
+#include <sfx2/objsh.hxx>
+#include <sfx2/viewfrm.hxx>
+#include <sfx2/viewsh.hxx>
+#include <toolkit/helper/vclunohelper.hxx>
+#include <tools/debug.hxx>
+#include <tools/diagnose_ex.h>
+#include <vcl/settings.hxx>
+#include <vcl/svapp.hxx>
+#include <vcl/weld.hxx>
+#include <vcl/window.hxx>
+
+#include <algorithm>
+#include <map>
+#include <memory>
+#include <string_view>
+#include <vector>
+
+// is used for Invalidate -> maintain it as well
+const sal_uInt16 DatabaseSlotMap[] =
+{
+ SID_FM_RECORD_FIRST,
+ SID_FM_RECORD_NEXT,
+ SID_FM_RECORD_PREV,
+ SID_FM_RECORD_LAST,
+ SID_FM_RECORD_NEW,
+ SID_FM_RECORD_DELETE,
+ SID_FM_RECORD_ABSOLUTE,
+ SID_FM_RECORD_TOTAL,
+ SID_FM_RECORD_SAVE,
+ SID_FM_RECORD_UNDO,
+ SID_FM_REMOVE_FILTER_SORT,
+ SID_FM_SORTUP,
+ SID_FM_SORTDOWN,
+ SID_FM_ORDERCRIT,
+ SID_FM_AUTOFILTER,
+ SID_FM_FORM_FILTERED,
+ SID_FM_REFRESH,
+ SID_FM_REFRESH_FORM_CONTROL,
+ SID_FM_SEARCH,
+ SID_FM_FILTER_START,
+ SID_FM_VIEW_AS_GRID,
+ 0
+};
+
+// is used for Invalidate -> maintain it as well
+// sort ascending !!!!!!
+const sal_Int16 DlgSlotMap[] = // slots of the controller
+{
+ SID_FM_CTL_PROPERTIES,
+ SID_FM_PROPERTIES,
+ SID_FM_TAB_DIALOG,
+ SID_FM_ADD_FIELD,
+ SID_FM_SHOW_FMEXPLORER,
+ SID_FM_FIELDS_CONTROL,
+ SID_FM_SHOW_PROPERTIES,
+ SID_FM_PROPERTY_CONTROL,
+ SID_FM_FMEXPLORER_CONTROL,
+ SID_FM_SHOW_DATANAVIGATOR,
+ SID_FM_DATANAVIGATOR_CONTROL,
+ 0
+};
+
+const sal_Int16 SelObjectSlotMap[] = // slots depending on the SelObject
+{
+ SID_FM_CONVERTTO_EDIT,
+ SID_FM_CONVERTTO_BUTTON,
+ SID_FM_CONVERTTO_FIXEDTEXT,
+ SID_FM_CONVERTTO_LISTBOX,
+ SID_FM_CONVERTTO_CHECKBOX,
+ SID_FM_CONVERTTO_RADIOBUTTON,
+ SID_FM_CONVERTTO_GROUPBOX,
+ SID_FM_CONVERTTO_COMBOBOX,
+ SID_FM_CONVERTTO_IMAGEBUTTON,
+ SID_FM_CONVERTTO_FILECONTROL,
+ SID_FM_CONVERTTO_DATE,
+ SID_FM_CONVERTTO_TIME,
+ SID_FM_CONVERTTO_NUMERIC,
+ SID_FM_CONVERTTO_CURRENCY,
+ SID_FM_CONVERTTO_PATTERN,
+ SID_FM_CONVERTTO_IMAGECONTROL,
+ SID_FM_CONVERTTO_FORMATTED,
+ SID_FM_CONVERTTO_SCROLLBAR,
+ SID_FM_CONVERTTO_SPINBUTTON,
+ SID_FM_CONVERTTO_NAVIGATIONBAR,
+
+ SID_FM_FMEXPLORER_CONTROL,
+ SID_FM_DATANAVIGATOR_CONTROL,
+
+ 0
+};
+
+// the following arrays must be consistent, i.e., corresponding entries should
+// be at the same relative position within their respective arrays
+static const char* aConvertSlots[] =
+{
+ "ConvertToEdit",
+ "ConvertToButton",
+ "ConvertToFixed",
+ "ConvertToList",
+ "ConvertToCheckBox",
+ "ConvertToRadio",
+ "ConvertToGroup",
+ "ConvertToCombo",
+ "ConvertToImageBtn",
+ "ConvertToFileControl",
+ "ConvertToDate",
+ "ConvertToTime",
+ "ConvertToNumeric",
+ "ConvertToCurrency",
+ "ConvertToPattern",
+ "ConvertToImageControl",
+ "ConvertToFormatted",
+ "ConvertToScrollBar",
+ "ConvertToSpinButton",
+ "ConvertToNavigationBar"
+};
+
+constexpr rtl::OUStringConstExpr aImgIds[] =
+{
+ RID_SVXBMP_EDITBOX,
+ RID_SVXBMP_BUTTON,
+ RID_SVXBMP_FIXEDTEXT,
+ RID_SVXBMP_LISTBOX,
+ RID_SVXBMP_CHECKBOX,
+ RID_SVXBMP_RADIOBUTTON,
+ RID_SVXBMP_GROUPBOX,
+ RID_SVXBMP_COMBOBOX,
+ RID_SVXBMP_IMAGEBUTTON,
+ RID_SVXBMP_FILECONTROL,
+ RID_SVXBMP_DATEFIELD,
+ RID_SVXBMP_TIMEFIELD,
+ RID_SVXBMP_NUMERICFIELD,
+ RID_SVXBMP_CURRENCYFIELD,
+ RID_SVXBMP_PATTERNFIELD,
+ RID_SVXBMP_IMAGECONTROL,
+ RID_SVXBMP_FORMATTEDFIELD,
+ RID_SVXBMP_SCROLLBAR,
+ RID_SVXBMP_SPINBUTTON,
+ RID_SVXBMP_NAVIGATIONBAR
+};
+
+const SdrObjKind nObjectTypes[] =
+{
+ SdrObjKind::FormEdit,
+ SdrObjKind::FormButton,
+ SdrObjKind::FormFixedText,
+ SdrObjKind::FormListbox,
+ SdrObjKind::FormCheckbox,
+ SdrObjKind::FormRadioButton,
+ SdrObjKind::FormGroupBox,
+ SdrObjKind::FormCombobox,
+ SdrObjKind::FormImageButton,
+ SdrObjKind::FormFileControl,
+ SdrObjKind::FormDateField,
+ SdrObjKind::FormTimeField,
+ SdrObjKind::FormNumericField,
+ SdrObjKind::FormCurrencyField,
+ SdrObjKind::FormPatternField,
+ SdrObjKind::FormImageControl,
+ SdrObjKind::FormFormattedField,
+ SdrObjKind::FormScrollbar,
+ SdrObjKind::FormSpinButton,
+ SdrObjKind::FormNavigationBar
+};
+
+using namespace ::com::sun::star;
+using namespace ::com::sun::star::ui;
+using namespace ::com::sun::star::uno;
+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::container;
+using namespace ::com::sun::star::form;
+using namespace ::com::sun::star::form::binding;
+using namespace ::com::sun::star::form::runtime;
+using namespace ::com::sun::star::awt;
+using namespace ::com::sun::star::view;
+using namespace ::com::sun::star::util;
+using namespace ::com::sun::star::script;
+using namespace ::svxform;
+using namespace ::svx;
+using namespace ::dbtools;
+
+
+//= helper
+
+namespace
+{
+
+ void collectInterfacesFromMarkList( const SdrMarkList& _rMarkList, InterfaceBag& /* [out] */ _rInterfaces )
+ {
+ _rInterfaces.clear();
+
+ const size_t nMarkCount = _rMarkList.GetMarkCount();
+ for ( size_t i = 0; i < nMarkCount; ++i)
+ {
+ SdrObject* pCurrent = _rMarkList.GetMark( i )->GetMarkedSdrObj();
+
+ std::unique_ptr<SdrObjListIter> pGroupIterator;
+ if ( pCurrent->IsGroupObject() )
+ {
+ pGroupIterator.reset(new SdrObjListIter( pCurrent->GetSubList() ));
+ pCurrent = pGroupIterator->IsMore() ? pGroupIterator->Next() : nullptr;
+ }
+
+ while ( pCurrent )
+ {
+ FmFormObj* pAsFormObject = FmFormObj::GetFormObject( pCurrent );
+ // note this will de-reference virtual objects, if necessary/possible
+ if ( pAsFormObject )
+ {
+ Reference< XInterface > xControlModel( pAsFormObject->GetUnoControlModel(), UNO_QUERY );
+ // the UNO_QUERY is important for normalization
+ if ( xControlModel.is() )
+ _rInterfaces.insert( xControlModel );
+ }
+
+ // next element
+ pCurrent = pGroupIterator && pGroupIterator->IsMore() ? pGroupIterator->Next() : nullptr;
+ }
+ }
+ }
+
+
+ sal_Int32 GridView2ModelPos(const Reference< XIndexAccess>& rColumns, sal_Int16 nViewPos)
+ {
+ try
+ {
+ if (rColumns.is())
+ {
+ // loop through all columns
+ sal_Int32 i;
+ Reference< XPropertySet> xCur;
+ for (i=0; i<rColumns->getCount(); ++i)
+ {
+ rColumns->getByIndex(i) >>= xCur;
+ if (!::comphelper::getBOOL(xCur->getPropertyValue(FM_PROP_HIDDEN)))
+ {
+ // for every visible col : if nViewPos is greater zero, decrement it, else we
+ // have found the model position
+ if (!nViewPos)
+ break;
+ else
+ --nViewPos;
+ }
+ }
+ if (i<rColumns->getCount())
+ return i;
+ }
+ }
+ catch(const Exception&)
+ {
+ DBG_UNHANDLED_EXCEPTION("svx");
+ }
+ return -1;
+ }
+
+
+ void TransferEventScripts(const Reference< XControlModel>& xModel, const Reference< XControl>& xControl,
+ const Sequence< ScriptEventDescriptor>& rTransferIfAvailable)
+ {
+ // first check if we have a XEventAttacherManager for the model
+ Reference< XChild> xModelChild(xModel, UNO_QUERY);
+ if (!xModelChild.is())
+ return; // nothing to do
+
+ Reference< XEventAttacherManager> xEventManager(xModelChild->getParent(), UNO_QUERY);
+ if (!xEventManager.is())
+ return; // nothing to do
+
+ if (!rTransferIfAvailable.hasElements())
+ return; // nothing to do
+
+ // check for the index of the model within its parent
+ Reference< XIndexAccess> xParentIndex(xModelChild->getParent(), UNO_QUERY);
+ if (!xParentIndex.is())
+ return; // nothing to do
+ sal_Int32 nIndex = getElementPos(xParentIndex, xModel);
+ if (nIndex<0 || nIndex>=xParentIndex->getCount())
+ return; // nothing to do
+
+ // then we need information about the listeners supported by the control and the model
+ Sequence< Type> aModelListeners;
+ Sequence< Type> aControlListeners;
+
+ Reference< XIntrospection> xIntrospection = theIntrospection::get(::comphelper::getProcessComponentContext());
+
+ if (xModel.is())
+ {
+ Any aModel(xModel);
+ aModelListeners = xIntrospection->inspect(aModel)->getSupportedListeners();
+ }
+
+ if (xControl.is())
+ {
+ Any aControl(xControl);
+ aControlListeners = xIntrospection->inspect(aControl)->getSupportedListeners();
+ }
+
+ sal_Int32 nMaxNewLen = aModelListeners.getLength() + aControlListeners.getLength();
+ if (!nMaxNewLen)
+ return; // the model and the listener don't support any listeners (or we were unable to retrieve these infos)
+
+ Sequence< ScriptEventDescriptor> aTransferable(nMaxNewLen);
+ ScriptEventDescriptor* pTransferable = aTransferable.getArray();
+
+ for (const ScriptEventDescriptor& rCurrent : rTransferIfAvailable)
+ {
+ // search the model/control idl classes for the event described by pCurrent
+ for (const Sequence< Type>* pCurrentArray : { &aModelListeners, &aControlListeners })
+ {
+ for (const Type& rCurrentListener : *pCurrentArray)
+ {
+ OUString aListener = rCurrentListener.getTypeName();
+ if (!aListener.isEmpty())
+ aListener = aListener.copy(aListener.lastIndexOf('.')+1);
+
+ if (aListener == rCurrent.ListenerType)
+ // the current ScriptEventDescriptor doesn't match the current listeners class
+ continue;
+
+ // now check the methods
+ Sequence< OUString> aMethodsNames = ::comphelper::getEventMethodsForType(rCurrentListener);
+
+ if (comphelper::findValue(aMethodsNames, rCurrent.EventMethod) != -1)
+ {
+ // we can transfer the script event : the model (control) supports it
+ *pTransferable = rCurrent;
+ ++pTransferable;
+ break;
+ }
+ }
+ }
+ }
+
+ sal_Int32 nRealNewLen = pTransferable - aTransferable.getArray();
+ aTransferable.realloc(nRealNewLen);
+
+ xEventManager->registerScriptEvents(nIndex, aTransferable);
+ }
+
+
+ OUString getServiceNameByControlType(SdrObjKind nType)
+ {
+ switch (nType)
+ {
+ case SdrObjKind::FormEdit : return FM_COMPONENT_TEXTFIELD;
+ case SdrObjKind::FormButton : return FM_COMPONENT_COMMANDBUTTON;
+ case SdrObjKind::FormFixedText : return FM_COMPONENT_FIXEDTEXT;
+ case SdrObjKind::FormListbox : return FM_COMPONENT_LISTBOX;
+ case SdrObjKind::FormCheckbox : return FM_COMPONENT_CHECKBOX;
+ case SdrObjKind::FormRadioButton : return FM_COMPONENT_RADIOBUTTON;
+ case SdrObjKind::FormGroupBox : return FM_COMPONENT_GROUPBOX;
+ case SdrObjKind::FormCombobox : return FM_COMPONENT_COMBOBOX;
+ case SdrObjKind::FormGrid : return FM_COMPONENT_GRIDCONTROL;
+ case SdrObjKind::FormImageButton : return FM_COMPONENT_IMAGEBUTTON;
+ case SdrObjKind::FormFileControl : return FM_COMPONENT_FILECONTROL;
+ case SdrObjKind::FormDateField : return FM_COMPONENT_DATEFIELD;
+ case SdrObjKind::FormTimeField : return FM_COMPONENT_TIMEFIELD;
+ case SdrObjKind::FormNumericField : return FM_COMPONENT_NUMERICFIELD;
+ case SdrObjKind::FormCurrencyField : return FM_COMPONENT_CURRENCYFIELD;
+ case SdrObjKind::FormPatternField : return FM_COMPONENT_PATTERNFIELD;
+ case SdrObjKind::FormHidden : return FM_COMPONENT_HIDDENCONTROL;
+ case SdrObjKind::FormImageControl : return FM_COMPONENT_IMAGECONTROL;
+ case SdrObjKind::FormFormattedField : return FM_COMPONENT_FORMATTEDFIELD;
+ case SdrObjKind::FormScrollbar : return FM_SUN_COMPONENT_SCROLLBAR;
+ case SdrObjKind::FormSpinButton : return FM_SUN_COMPONENT_SPINBUTTON;
+ case SdrObjKind::FormNavigationBar : return FM_SUN_COMPONENT_NAVIGATIONBAR;
+ default:;
+ }
+ return OUString();
+ }
+
+}
+
+
+// check if the control has one of the interfaces we can use for searching
+// *_pCurrentText will be filled with the current text of the control (as used when searching this control)
+bool IsSearchableControl( const css::uno::Reference< css::uno::XInterface>& _rxControl,
+ OUString* _pCurrentText )
+{
+ if ( !_rxControl.is() )
+ return false;
+
+ Reference< XTextComponent > xAsText( _rxControl, UNO_QUERY );
+ if ( xAsText.is() )
+ {
+ if ( _pCurrentText )
+ *_pCurrentText = xAsText->getText();
+ return true;
+ }
+
+ Reference< XListBox > xListBox( _rxControl, UNO_QUERY );
+ if ( xListBox.is() )
+ {
+ if ( _pCurrentText )
+ *_pCurrentText = xListBox->getSelectedItem();
+ return true;
+ }
+
+ Reference< XCheckBox > xCheckBox( _rxControl, UNO_QUERY );
+ if ( xCheckBox.is() )
+ {
+ if ( _pCurrentText )
+ {
+ switch ( static_cast<::TriState>(xCheckBox->getState()) )
+ {
+ case TRISTATE_FALSE: *_pCurrentText = "0"; break;
+ case TRISTATE_TRUE: *_pCurrentText = "1"; break;
+ default: _pCurrentText->clear(); break;
+ }
+ }
+ return true;
+ }
+
+ return false;
+}
+
+
+bool FmXBoundFormFieldIterator::ShouldStepInto(const Reference< XInterface>& _rContainer) const
+{
+ if (_rContainer == m_xStartingPoint)
+ // would be quite stupid to step over the root...
+ return true;
+
+ return Reference< XControlModel>(_rContainer, UNO_QUERY).is();
+}
+
+
+bool FmXBoundFormFieldIterator::ShouldHandleElement(const Reference< XInterface>& _rElement)
+{
+ if (!_rElement.is())
+ // NULL element
+ return false;
+
+ if (Reference< XForm>(_rElement, UNO_QUERY).is() || Reference< XGrid>(_rElement, UNO_QUERY).is())
+ // a forms or a grid
+ return false;
+
+ Reference< XPropertySet> xSet(_rElement, UNO_QUERY);
+ if (!xSet.is() || !::comphelper::hasProperty(FM_PROP_BOUNDFIELD, xSet))
+ // no "BoundField" property
+ return false;
+
+ Any aVal( xSet->getPropertyValue(FM_PROP_BOUNDFIELD) );
+ if (aVal.getValueTypeClass() != TypeClass_INTERFACE)
+ // void or invalid property value
+ return false;
+
+ return aVal.hasValue();
+}
+
+
+static bool isControlList(const SdrMarkList& rMarkList)
+{
+ // the list contains only controls and at least one control
+ const size_t nMarkCount = rMarkList.GetMarkCount();
+ bool bControlList = nMarkCount != 0;
+
+ bool bHadAnyLeafs = false;
+
+ for (size_t i = 0; i < nMarkCount && bControlList; ++i)
+ {
+ SdrObject *pObj = rMarkList.GetMark(i)->GetMarkedSdrObj();
+ E3dObject* pAs3DObject = dynamic_cast< E3dObject* >( pObj);
+ // E3dObject's do not contain any 2D-objects (by definition)
+ // we need this extra check here : an E3dObject->IsGroupObject says "YES", but an SdrObjListIter working
+ // with an E3dObject doesn't give me any Nodes (E3dObject has a sub list, but no members in that list,
+ // cause there implementation differs from the one of "normal" SdrObject's. Unfortunally SdrObject::IsGroupObject
+ // doesn't check the element count of the sub list, which is simply a bug in IsGroupObject we can't fix at the moment).
+ // So at the end of this function bControlList would have the same value it was initialized with above : sal_True
+ // And this would be wrong :)
+ // 03.02.00 - 72529 - FS
+ if (!pAs3DObject)
+ {
+ if (pObj->IsGroupObject())
+ {
+ SdrObjListIter aIter(pObj->GetSubList());
+ while (aIter.IsMore() && bControlList)
+ {
+ bControlList = SdrInventor::FmForm == aIter.Next()->GetObjInventor();
+ bHadAnyLeafs = true;
+ }
+ }
+ else
+ {
+ bHadAnyLeafs = true;
+ bControlList = SdrInventor::FmForm == pObj->GetObjInventor();
+ }
+ }
+ }
+
+ return bControlList && bHadAnyLeafs;
+}
+
+
+static Reference< XForm > GetForm(const Reference< XInterface>& _rxElement)
+{
+ Reference< XForm > xForm( _rxElement, UNO_QUERY );
+ if ( xForm.is() )
+ return xForm;
+
+ Reference< XChild > xChild( _rxElement, UNO_QUERY );
+ if ( xChild.is() )
+ return GetForm( xChild->getParent() );
+
+ return Reference< XForm >();
+}
+
+FmXFormShell_Base_Disambiguation::FmXFormShell_Base_Disambiguation( ::osl::Mutex& _rMutex )
+ :FmXFormShell_BD_BASE( _rMutex )
+{
+}
+
+FmXFormShell::FmXFormShell( FmFormShell& _rShell, SfxViewFrame* _pViewFrame )
+ :FmXFormShell_BASE(m_aMutex)
+ ,FmXFormShell_CFGBASE("Office.Common/Misc", ConfigItemMode::NONE)
+ ,m_aMarkTimer("svx::FmXFormShell m_aMarkTimer")
+ ,m_eNavigate( NavigationBarMode_NONE )
+ ,m_nInvalidationEvent( nullptr )
+ ,m_nActivationEvent( nullptr )
+ ,m_pShell( &_rShell )
+ ,m_pTextShell( new svx::FmTextControlShell( _pViewFrame ) )
+ ,m_aActiveControllerFeatures( this )
+ ,m_aNavControllerFeatures( this )
+ ,m_eDocumentType( eUnknownDocumentType )
+ ,m_nLockSlotInvalidation( 0 )
+ ,m_bHadPropertyBrowserInDesignMode( false )
+ ,m_bTrackProperties( true )
+ ,m_bUseWizards( true )
+ ,m_bDatabaseBar( false )
+ ,m_bInActivate( false )
+ ,m_bSetFocus( false )
+ ,m_bFilterMode( false )
+ ,m_bChangingDesignMode( false )
+ ,m_bPreparedClose( false )
+ ,m_bFirstActivation( true )
+{
+ m_aMarkTimer.SetTimeout(100);
+ m_aMarkTimer.SetInvokeHandler(LINK(this, FmXFormShell, OnTimeOut_Lock));
+
+ m_xAttachedFrame = _pViewFrame->GetFrame().GetFrameInterface();
+
+ // to prevent deletion of this we acquire our refcounter once
+ osl_atomic_increment(&m_refCount);
+
+ // correct the refcounter
+ osl_atomic_decrement(&m_refCount);
+
+ // cache the current configuration settings we're interested in
+ implAdjustConfigCache_Lock();
+ // and register for changes on this settings
+ Sequence< OUString > aNames { "FormControlPilotsEnabled" };
+ EnableNotification(aNames);
+}
+
+
+FmXFormShell::~FmXFormShell()
+{
+}
+
+
+Reference< css::frame::XModel > FmXFormShell::getContextDocument_Lock() const
+{
+ Reference< css::frame::XModel > xModel;
+
+ // determine the type of document we live in
+ try
+ {
+ Reference< css::frame::XController > xController;
+ if ( m_xAttachedFrame.is() )
+ xController = m_xAttachedFrame->getController();
+ if ( xController.is() )
+ xModel = xController->getModel();
+ }
+ catch( const Exception& )
+ {
+ DBG_UNHANDLED_EXCEPTION("svx");
+ }
+ return xModel;
+}
+
+
+bool FmXFormShell::isEnhancedForm_Lock() const
+{
+ return getDocumentType_Lock() == eEnhancedForm;
+}
+
+
+bool FmXFormShell::impl_checkDisposed_Lock() const
+{
+ DBG_TESTSOLARMUTEX();
+ if ( !m_pShell )
+ {
+ OSL_FAIL( "FmXFormShell::impl_checkDisposed: already disposed!" );
+ return true;
+ }
+ return false;
+}
+
+
+::svxform::DocumentType FmXFormShell::getDocumentType_Lock() const
+{
+ if ( m_eDocumentType != eUnknownDocumentType )
+ return m_eDocumentType;
+
+ // determine the type of document we live in
+ Reference<css::frame::XModel> xModel = getContextDocument_Lock();
+ if ( xModel.is() )
+ m_eDocumentType = DocumentClassification::classifyDocument( xModel );
+ else
+ {
+ OSL_FAIL( "FmXFormShell::getDocumentType: can't determine the document type!" );
+ m_eDocumentType = eTextDocument;
+ // fallback, just to have a defined state
+ }
+
+ return m_eDocumentType;
+}
+
+
+bool FmXFormShell::IsReadonlyDoc_Lock() const
+{
+ if (impl_checkDisposed_Lock())
+ return true;
+
+ FmFormModel* pModel = m_pShell->GetFormModel();
+ if ( pModel && pModel->GetObjectShell() )
+ return pModel->GetObjectShell()->IsReadOnly() || pModel->GetObjectShell()->IsReadOnlyUI();
+ return true;
+}
+
+// EventListener
+
+void SAL_CALL FmXFormShell::disposing(const lang::EventObject& e)
+{
+ SolarMutexGuard g;
+
+ if (m_xActiveController == e.Source)
+ {
+ // the controller will release, then release everything
+ stopListening_Lock();
+ m_xActiveForm = nullptr;
+ m_xActiveController = nullptr;
+ m_xNavigationController = nullptr;
+
+ m_aActiveControllerFeatures.dispose();
+ m_aNavControllerFeatures.dispose();
+
+ if ( m_pShell )
+ m_pShell->GetViewShell()->GetViewFrame()->GetBindings().InvalidateShell(*m_pShell);
+ }
+
+ if (e.Source != m_xExternalViewController)
+ return;
+
+ Reference< runtime::XFormController > xFormController( m_xExternalViewController, UNO_QUERY );
+ OSL_ENSURE( xFormController.is(), "FmXFormShell::disposing: invalid external view controller!" );
+ if (xFormController.is())
+ xFormController->removeActivateListener(static_cast<XFormControllerListener*>(this));
+
+ if (m_xExternalViewController.is())
+ m_xExternalViewController->removeEventListener(static_cast<XEventListener*>(static_cast<XPropertyChangeListener*>(this)));
+
+ m_xExternalViewController = nullptr;
+ m_xExternalDisplayedForm = nullptr;
+ m_xExtViewTriggerController = nullptr;
+
+ InvalidateSlot_Lock( SID_FM_VIEW_AS_GRID, false );
+}
+
+
+void SAL_CALL FmXFormShell::propertyChange(const PropertyChangeEvent& evt)
+{
+ SolarMutexGuard g;
+
+ if (impl_checkDisposed_Lock())
+ return;
+
+ if (evt.PropertyName == FM_PROP_ROWCOUNT)
+ {
+ // The update following this forces a re-painting of the corresponding
+ // slots. But if I am not in the MainThread of the application (because,
+ // for example, a cursor is counting data sets at the moment and always
+ // gives me this PropertyChanges), this can clash with normal paints in
+ // the MainThread of the application. (Such paints happen, for example,
+ // if one simply places another application over the office and switches
+ // back again).
+ // Therefore the use of the SolarMutex, which safeguards that.
+ comphelper::SolarMutex& rSolarSafety = Application::GetSolarMutex();
+ if (rSolarSafety.tryToAcquire())
+ {
+ m_pShell->GetViewShell()->GetViewFrame()->GetBindings().Invalidate(SID_FM_RECORD_TOTAL, true);
+ m_pShell->GetViewShell()->GetViewFrame()->GetBindings().Update(SID_FM_RECORD_TOTAL);
+ rSolarSafety.release();
+ }
+ else
+ {
+ // with the following the slot is invalidated asynchron
+ LockSlotInvalidation_Lock(true);
+ InvalidateSlot_Lock(SID_FM_RECORD_TOTAL, false);
+ LockSlotInvalidation_Lock(false);
+ }
+ }
+
+ // this may be called from a non-main-thread so invalidate the shell asynchronously
+ LockSlotInvalidation_Lock(true);
+ InvalidateSlot_Lock(0, false); // special meaning : invalidate m_pShell
+ LockSlotInvalidation_Lock(false);
+}
+
+
+void FmXFormShell::invalidateFeatures( const ::std::vector< sal_Int32 >& _rFeatures )
+{
+ SolarMutexGuard g;
+
+ if (impl_checkDisposed_Lock())
+ return;
+
+ OSL_ENSURE( !_rFeatures.empty(), "FmXFormShell::invalidateFeatures: invalid arguments!" );
+
+ if ( !(m_pShell->GetViewShell() && m_pShell->GetViewShell()->GetViewFrame()) )
+ return;
+
+ // unfortunately, SFX requires sal_uInt16
+ ::std::vector< sal_uInt16 > aSlotIds( _rFeatures.begin(), _rFeatures.end() );
+
+ // furthermore, SFX wants a terminating 0
+ aSlotIds.push_back( 0 );
+
+ // and, last but not least, SFX wants the ids to be sorted
+ ::std::sort( aSlotIds.begin(), aSlotIds.end() - 1 );
+
+ sal_uInt16 *pSlotIds = aSlotIds.data();
+ m_pShell->GetViewShell()->GetViewFrame()->GetBindings().Invalidate( pSlotIds );
+}
+
+
+void SAL_CALL FmXFormShell::formActivated(const lang::EventObject& rEvent)
+{
+ SolarMutexGuard g;
+
+ if (impl_checkDisposed_Lock())
+ return;
+
+ Reference< runtime::XFormController > xController( rEvent.Source, UNO_QUERY_THROW );
+ m_pTextShell->formActivated( xController );
+ setActiveController_Lock(xController);
+}
+
+
+void SAL_CALL FmXFormShell::formDeactivated(const lang::EventObject& rEvent)
+{
+ SolarMutexGuard g;
+
+ if (impl_checkDisposed_Lock())
+ return;
+
+ Reference< runtime::XFormController > xController( rEvent.Source, UNO_QUERY_THROW );
+ m_pTextShell->formDeactivated( xController );
+}
+
+
+void FmXFormShell::disposing()
+{
+ SolarMutexGuard g;
+
+ FmXFormShell_BASE::disposing();
+
+ if ( m_pShell && !m_pShell->IsDesignMode() )
+ setActiveController_Lock(nullptr, true);
+ // do NOT save the content of the old form (the second parameter tells this)
+ // if we're here, then we expect that PrepareClose has been called, and thus the user
+ // got a chance to commit or reject any changes. So in case we're here and there
+ // are still uncommitted changes, the user explicitly wanted this.
+
+ m_pTextShell->dispose();
+
+ m_xAttachedFrame = nullptr;
+
+ CloseExternalFormViewer_Lock();
+
+ while ( !m_aLoadingPages.empty() )
+ {
+ Application::RemoveUserEvent( m_aLoadingPages.front().nEventId );
+ m_aLoadingPages.pop();
+ }
+
+ {
+ if (m_nInvalidationEvent)
+ {
+ Application::RemoveUserEvent(m_nInvalidationEvent);
+ m_nInvalidationEvent = nullptr;
+ }
+ if ( m_nActivationEvent )
+ {
+ Application::RemoveUserEvent( m_nActivationEvent );
+ m_nActivationEvent = nullptr;
+ }
+ }
+
+ {
+ DBG_ASSERT(!m_nInvalidationEvent, "FmXFormShell::~FmXFormShell : still have an invalidation event !");
+ // should have been deleted while being disposed
+
+ m_aMarkTimer.Stop();
+ }
+
+ DisableNotification();
+
+ RemoveElement_Lock(m_xForms);
+ m_xForms.clear();
+
+ impl_switchActiveControllerListening_Lock(false);
+ m_xActiveController = nullptr;
+ m_xActiveForm = nullptr;
+
+ m_pShell = nullptr;
+ m_xNavigationController = nullptr;
+ m_xCurrentForm = nullptr;
+ m_xLastGridFound = nullptr;
+ m_xAttachedFrame = nullptr;
+ m_xExternalViewController = nullptr;
+ m_xExtViewTriggerController = nullptr;
+ m_xExternalDisplayedForm = nullptr;
+
+ InterfaceBag().swap(m_aCurrentSelection);
+
+ m_aActiveControllerFeatures.dispose();
+ m_aNavControllerFeatures.dispose();
+}
+
+
+void FmXFormShell::UpdateSlot_Lock(sal_Int16 _nId)
+{
+ if (impl_checkDisposed_Lock())
+ return;
+
+ if ( m_nLockSlotInvalidation )
+ {
+ OSL_FAIL( "FmXFormShell::UpdateSlot: cannot update if invalidation is currently locked!" );
+ InvalidateSlot_Lock(_nId, false);
+ }
+ else
+ {
+ OSL_ENSURE( _nId, "FmXFormShell::UpdateSlot: can't update the complete shell!" );
+ m_pShell->GetViewShell()->GetViewFrame()->GetBindings().Invalidate( _nId, true, true );
+ m_pShell->GetViewShell()->GetViewFrame()->GetBindings().Update( _nId );
+ }
+}
+
+
+void FmXFormShell::InvalidateSlot_Lock(sal_Int16 nId, bool bWithId)
+{
+ if (impl_checkDisposed_Lock())
+ return;
+
+ if (m_nLockSlotInvalidation)
+ {
+ sal_uInt8 nFlags = ( bWithId ? 0x01 : 0 );
+ m_arrInvalidSlots.emplace_back(nId, nFlags );
+ }
+ else
+ if (nId)
+ m_pShell->GetViewShell()->GetViewFrame()->GetBindings().Invalidate(nId, true, bWithId);
+ else
+ m_pShell->GetViewShell()->GetViewFrame()->GetBindings().InvalidateShell(*m_pShell);
+}
+
+
+void FmXFormShell::LockSlotInvalidation_Lock(bool bLock)
+{
+ if (impl_checkDisposed_Lock())
+ return;
+
+ DBG_ASSERT(bLock || m_nLockSlotInvalidation>0, "FmXFormShell::LockSlotInvalidation : invalid call !");
+
+ if (bLock)
+ ++m_nLockSlotInvalidation;
+ else if (!--m_nLockSlotInvalidation)
+ {
+ // (asynchronously) invalidate everything accumulated during the locked phase
+ if (!m_nInvalidationEvent)
+ m_nInvalidationEvent = Application::PostUserEvent(LINK(this, FmXFormShell, OnInvalidateSlots_Lock));
+ }
+}
+
+
+IMPL_LINK_NOARG(FmXFormShell, OnInvalidateSlots_Lock, void*,void)
+{
+ if (impl_checkDisposed_Lock())
+ return;
+
+ m_nInvalidationEvent = nullptr;
+
+ for (const auto& rInvalidSlot : m_arrInvalidSlots)
+ {
+ if (rInvalidSlot.id)
+ m_pShell->GetViewShell()->GetViewFrame()->GetBindings().Invalidate(rInvalidSlot.id, true, (rInvalidSlot.flags & 0x01));
+ else
+ m_pShell->GetViewShell()->GetViewFrame()->GetBindings().InvalidateShell(*m_pShell);
+ }
+ m_arrInvalidSlots.clear();
+}
+
+
+void FmXFormShell::ForceUpdateSelection_Lock()
+{
+ if (impl_checkDisposed_Lock())
+ return;
+
+ if (IsSelectionUpdatePending_Lock())
+ {
+ m_aMarkTimer.Stop();
+
+ // optionally turn off the invalidation of slots which is implicitly done by SetSelection
+ LockSlotInvalidation_Lock(true);
+
+ SetSelection_Lock(m_pShell->GetFormView()->GetMarkedObjectList());
+
+ LockSlotInvalidation_Lock(false);
+ }
+}
+
+void FmXFormShell::GetConversionMenu_Lock(weld::Menu& rNewMenu)
+{
+ for (size_t i = 0; i < SAL_N_ELEMENTS(aConvertSlots); ++i)
+ {
+ // the corresponding image at it
+ rNewMenu.append(OUString::createFromAscii(aConvertSlots[i]), SvxResId(RID_SVXSW_CONVERTMENU[i]), aImgIds[i]);
+ }
+}
+
+OString FmXFormShell::SlotToIdent(sal_uInt16 nSlot)
+{
+ static_assert(SAL_N_ELEMENTS(SelObjectSlotMap) >= SAL_N_ELEMENTS(aConvertSlots));
+
+ for (size_t i = 0; i < SAL_N_ELEMENTS(aConvertSlots); ++i)
+ {
+ if (nSlot == SelObjectSlotMap[i])
+ return aConvertSlots[i];
+ }
+
+ return OString();
+}
+
+bool FmXFormShell::isControlConversionSlot(std::string_view rIdent)
+{
+ for (const auto& rConvertSlot : aConvertSlots)
+ if (rIdent == rConvertSlot)
+ return true;
+ return false;
+}
+
+void FmXFormShell::executeControlConversionSlot_Lock(std::string_view rIdent)
+{
+ OSL_PRECOND( canConvertCurrentSelectionToControl_Lock(rIdent), "FmXFormShell::executeControlConversionSlot: illegal call!" );
+ InterfaceBag::const_iterator aSelectedElement = m_aCurrentSelection.begin();
+ if ( aSelectedElement == m_aCurrentSelection.end() )
+ return;
+
+ executeControlConversionSlot_Lock(Reference<XFormComponent>(*aSelectedElement, UNO_QUERY), rIdent);
+}
+
+bool FmXFormShell::executeControlConversionSlot_Lock(const Reference<XFormComponent>& _rxObject, std::string_view rIdent)
+{
+ if (impl_checkDisposed_Lock())
+ return false;
+
+ OSL_ENSURE( _rxObject.is(), "FmXFormShell::executeControlConversionSlot: invalid object!" );
+ if ( !_rxObject.is() )
+ return false;
+
+ SdrPage* pPage = m_pShell->GetCurPage();
+ FmFormPage* pFormPage = dynamic_cast< FmFormPage* >( pPage );
+ OSL_ENSURE( pFormPage, "FmXFormShell::executeControlConversionSlot: no current (form) page!" );
+ if ( !pFormPage )
+ return false;
+
+ OSL_ENSURE( isSolelySelected_Lock(_rxObject),
+ "FmXFormShell::executeControlConversionSlot: hmm ... shouldn't this parameter be redundant?" );
+
+ for (size_t lookupSlot = 0; lookupSlot < SAL_N_ELEMENTS(aConvertSlots); ++lookupSlot)
+ {
+ if (rIdent == aConvertSlots[lookupSlot])
+ {
+ Reference< XInterface > xNormalizedObject( _rxObject, UNO_QUERY );
+
+ FmFormObj* pFormObject = nullptr;
+ SdrObjListIter aPageIter( pFormPage );
+ while ( aPageIter.IsMore() )
+ {
+ SdrObject* pCurrent = aPageIter.Next();
+ pFormObject = FmFormObj::GetFormObject( pCurrent );
+ if ( !pFormObject )
+ continue;
+
+ Reference< XInterface > xCurrentNormalized( pFormObject->GetUnoControlModel(), UNO_QUERY );
+ if ( xCurrentNormalized.get() == xNormalizedObject.get() )
+ break;
+
+ pFormObject = nullptr;
+ }
+
+ if ( !pFormObject )
+ return false;
+
+ OUString sNewName( getServiceNameByControlType( nObjectTypes[ lookupSlot ] ) );
+ Reference<XComponentContext> xContext = comphelper::getProcessComponentContext();
+ Reference< XControlModel> xNewModel( xContext->getServiceManager()->createInstanceWithContext(sNewName, xContext), UNO_QUERY );
+ if (!xNewModel.is())
+ return false;
+
+ Reference< XControlModel> xOldModel( pFormObject->GetUnoControlModel() );
+
+ // transfer properties
+ Reference< XPropertySet> xOldSet(xOldModel, UNO_QUERY);
+ Reference< XPropertySet> xNewSet(xNewModel, UNO_QUERY);
+
+
+ lang::Locale aNewLanguage = Application::GetSettings().GetUILanguageTag().getLocale();
+ TransferFormComponentProperties(xOldSet, xNewSet, aNewLanguage);
+
+ Sequence< css::script::ScriptEventDescriptor> aOldScripts;
+ Reference< XChild> xChild(xOldModel, UNO_QUERY);
+ if (xChild.is())
+ {
+ Reference< XIndexAccess> xParent(xChild->getParent(), UNO_QUERY);
+
+ // remember old script events
+ Reference< css::script::XEventAttacherManager> xEvManager(xChild->getParent(), UNO_QUERY);
+ if (xParent.is() && xEvManager.is())
+ {
+ sal_Int32 nIndex = getElementPos(xParent, xOldModel);
+ if (nIndex>=0 && nIndex<xParent->getCount())
+ aOldScripts = xEvManager->getScriptEvents(nIndex);
+ }
+
+ // replace the model within the parent container
+ Reference< XIndexContainer> xIndexParent(xChild->getParent(), UNO_QUERY);
+ if (xIndexParent.is())
+ {
+ // the form container works with FormComponents
+ Reference< XFormComponent> xComponent(xNewModel, UNO_QUERY);
+ DBG_ASSERT(xComponent.is(), "FmXFormShell::executeControlConversionSlot: the new model is no form component !");
+ Any aNewModel(xComponent);
+ try
+ {
+
+ sal_Int32 nIndex = getElementPos(xParent, xOldModel);
+ if (nIndex>=0 && nIndex<xParent->getCount())
+ xIndexParent->replaceByIndex(nIndex, aNewModel);
+ else
+ {
+ OSL_FAIL("FmXFormShell::executeControlConversionSlot: could not replace the model !");
+ Reference< css::lang::XComponent> xNewComponent(xNewModel, UNO_QUERY);
+ if (xNewComponent.is())
+ xNewComponent->dispose();
+ return false;
+ }
+ }
+ catch(Exception&)
+ {
+ OSL_FAIL("FmXFormShell::executeControlConversionSlot: could not replace the model !");
+ Reference< css::lang::XComponent> xNewComponent(xNewModel, UNO_QUERY);
+ if (xNewComponent.is())
+ xNewComponent->dispose();
+ return false;
+ }
+
+ }
+ }
+
+ // special handling for the LabelControl-property : can only be set when the model is placed
+ // within the forms hierarchy
+ if (::comphelper::hasProperty(FM_PROP_CONTROLLABEL, xOldSet) && ::comphelper::hasProperty(FM_PROP_CONTROLLABEL, xNewSet))
+ {
+ try
+ {
+ xNewSet->setPropertyValue(FM_PROP_CONTROLLABEL, xOldSet->getPropertyValue(FM_PROP_CONTROLLABEL));
+ }
+ catch(Exception&)
+ {
+ }
+
+ }
+
+ // set new model
+ pFormObject->SetChanged();
+ pFormObject->SetUnoControlModel(xNewModel);
+
+ // transfer script events
+ // (do this _after_ SetUnoControlModel as we need the new (implicitly created) control)
+ if (aOldScripts.hasElements())
+ {
+ // find the control for the model
+ Reference<XControlContainer> xControlContainer(getControlContainerForView_Lock());
+
+ const Sequence< Reference< XControl> > aControls( xControlContainer->getControls() );
+
+ Reference< XControl> xControl;
+ auto pControl = std::find_if(aControls.begin(), aControls.end(),
+ [&xNewModel](const Reference< XControl>& rControl) { return rControl->getModel() == xNewModel; });
+ if (pControl != aControls.end())
+ xControl = *pControl;
+ TransferEventScripts(xNewModel, xControl, aOldScripts);
+ }
+
+ // transfer value bindings, if possible
+ {
+ Reference< XBindableValue > xOldBindable( xOldModel, UNO_QUERY );
+ Reference< XBindableValue > xNewBindable( xNewModel, UNO_QUERY );
+ if ( xOldBindable.is() )
+ {
+ try
+ {
+ if ( xNewBindable.is() )
+ xNewBindable->setValueBinding( xOldBindable->getValueBinding() );
+ xOldBindable->setValueBinding( nullptr );
+ }
+ catch(const Exception&)
+ {
+ DBG_UNHANDLED_EXCEPTION("svx");
+ }
+ }
+ }
+ // same for list entry sources
+ {
+ Reference< XListEntrySink > xOldSink( xOldModel, UNO_QUERY );
+ Reference< XListEntrySink > xNewSink( xNewModel, UNO_QUERY );
+ if ( xOldSink.is() )
+ {
+ try
+ {
+ if ( xNewSink.is() )
+ xNewSink->setListEntrySource( xOldSink->getListEntrySource() );
+ xOldSink->setListEntrySource( nullptr );
+ }
+ catch(const Exception&)
+ {
+ DBG_UNHANDLED_EXCEPTION("svx");
+ }
+ }
+ }
+
+ // create an undo action
+ FmFormModel* pModel = m_pShell->GetFormModel();
+ DBG_ASSERT(pModel != nullptr, "FmXFormShell::executeControlConversionSlot: my shell has no model !");
+ if (pModel && pModel->IsUndoEnabled() )
+ {
+ pModel->AddUndo(std::make_unique<FmUndoModelReplaceAction>(*pModel, pFormObject, xOldModel));
+ }
+ else
+ {
+ FmUndoModelReplaceAction::DisposeElement( xOldModel );
+ }
+
+ return true;
+ }
+ }
+ return false;
+}
+
+bool FmXFormShell::canConvertCurrentSelectionToControl_Lock(std::string_view rIdent)
+{
+ if ( m_aCurrentSelection.empty() )
+ return false;
+
+ InterfaceBag::const_iterator aCheck = m_aCurrentSelection.begin();
+ Reference< lang::XServiceInfo > xElementInfo( *aCheck, UNO_QUERY );
+ if ( !xElementInfo.is() )
+ // no service info -> cannot determine this
+ return false;
+
+ if ( ++aCheck != m_aCurrentSelection.end() )
+ // more than one element
+ return false;
+
+ if ( Reference< XForm >::query( xElementInfo ).is() )
+ // it's a form
+ return false;
+
+ SdrObjKind nObjectType = getControlTypeByObject( xElementInfo );
+
+ if ( ( SdrObjKind::FormHidden == nObjectType )
+ || ( SdrObjKind::FormControl == nObjectType )
+ || ( SdrObjKind::FormGrid == nObjectType )
+ )
+ return false; // those types cannot be converted
+
+ DBG_ASSERT(SAL_N_ELEMENTS(aConvertSlots) == SAL_N_ELEMENTS(nObjectTypes),
+ "FmXFormShell::canConvertCurrentSelectionToControl: aConvertSlots & nObjectTypes must have the same size !");
+
+ for (size_t i = 0; i < SAL_N_ELEMENTS(aConvertSlots); ++i)
+ if (rIdent == aConvertSlots[i])
+ return nObjectTypes[i] != nObjectType;
+
+ return true; // all other slots: assume "yes"
+}
+
+void FmXFormShell::checkControlConversionSlotsForCurrentSelection_Lock(weld::Menu& rMenu)
+{
+ for (int i = 0, nCount = rMenu.n_children(); i < nCount; ++i)
+ {
+ // the context is already of a type that corresponds to the entry -> disable
+ OString sIdent(aConvertSlots[i]);
+ rMenu.set_sensitive(sIdent, canConvertCurrentSelectionToControl_Lock(sIdent));
+ }
+}
+
+void FmXFormShell::LoopGrids_Lock(LoopGridsSync nSync, LoopGridsFlags nFlags)
+{
+ if (impl_checkDisposed_Lock())
+ return;
+
+ Reference< XIndexContainer> xControlModels(m_xActiveForm, UNO_QUERY);
+ if (!xControlModels.is())
+ return;
+
+ for (sal_Int32 i=0; i<xControlModels->getCount(); ++i)
+ {
+ Reference< XPropertySet> xModelSet;
+ xControlModels->getByIndex(i) >>= xModelSet;
+ if (!xModelSet.is())
+ continue;
+
+ if (!::comphelper::hasProperty(FM_PROP_CLASSID, xModelSet))
+ continue;
+ sal_Int16 nClassId = ::comphelper::getINT16(xModelSet->getPropertyValue(FM_PROP_CLASSID));
+ if (FormComponentType::GRIDCONTROL != nClassId)
+ continue;
+
+ if (!::comphelper::hasProperty(FM_PROP_CURSORCOLOR, xModelSet) || !::comphelper::hasProperty(FM_PROP_ALWAYSSHOWCURSOR, xModelSet) || !::comphelper::hasProperty(FM_PROP_DISPLAYSYNCHRON, xModelSet))
+ continue;
+
+ switch (nSync)
+ {
+ case LoopGridsSync::DISABLE_SYNC:
+ {
+ xModelSet->setPropertyValue(FM_PROP_DISPLAYSYNCHRON, Any(false));
+ }
+ break;
+ case LoopGridsSync::FORCE_SYNC:
+ {
+ Any aOldVal( xModelSet->getPropertyValue(FM_PROP_DISPLAYSYNCHRON) );
+ xModelSet->setPropertyValue(FM_PROP_DISPLAYSYNCHRON, Any(true));
+ xModelSet->setPropertyValue(FM_PROP_DISPLAYSYNCHRON, aOldVal);
+ }
+ break;
+ case LoopGridsSync::ENABLE_SYNC:
+ {
+ xModelSet->setPropertyValue(FM_PROP_DISPLAYSYNCHRON, Any(true));
+ }
+ break;
+ }
+
+ if (nFlags & LoopGridsFlags::DISABLE_ROCTRLR)
+ {
+ xModelSet->setPropertyValue(FM_PROP_ALWAYSSHOWCURSOR, Any(false));
+ Reference< XPropertyState> xModelPropState(xModelSet, UNO_QUERY);
+ if (xModelPropState.is())
+ xModelPropState->setPropertyToDefault(FM_PROP_CURSORCOLOR);
+ else
+ xModelSet->setPropertyValue(FM_PROP_CURSORCOLOR, Any()); // this should be the default
+ }
+ }
+}
+
+
+Reference< XControlContainer > FmXFormShell::getControlContainerForView_Lock() const
+{
+ if (impl_checkDisposed_Lock())
+ return nullptr;
+
+ SdrPageView* pPageView = nullptr;
+ if ( m_pShell && m_pShell->GetFormView() )
+ pPageView = m_pShell->GetFormView()->GetSdrPageView();
+
+ Reference< XControlContainer> xControlContainer;
+ if ( pPageView )
+ xControlContainer = pPageView->GetPageWindow(0)->GetControlContainer();
+
+ return xControlContainer;
+}
+
+
+void FmXFormShell::ExecuteTabOrderDialog_Lock(const Reference<XTabControllerModel>& _rxForForm)
+{
+ if (impl_checkDisposed_Lock())
+ return;
+
+ OSL_PRECOND( _rxForForm.is(), "FmXFormShell::ExecuteTabOrderDialog: invalid tabbing model!" );
+ if ( !_rxForForm.is() )
+ return;
+
+ try
+ {
+ Reference< XWindow > xParentWindow;
+ if ( m_pShell->GetViewShell() && m_pShell->GetViewShell()->GetViewFrame() )
+ xParentWindow = VCLUnoHelper::GetInterface ( &m_pShell->GetViewShell()->GetViewFrame()->GetWindow() );
+
+ Reference< dialogs::XExecutableDialog > xDialog = form::TabOrderDialog::createWithModel(
+ comphelper::getProcessComponentContext(),
+ _rxForForm, getControlContainerForView_Lock(), xParentWindow
+ );
+
+ xDialog->execute();
+ }
+ catch( const Exception& )
+ {
+ TOOLS_WARN_EXCEPTION( "svx", "FmXFormShell::ExecuteTabOrderDialog" );
+ }
+}
+
+
+void FmXFormShell::ExecuteSearch_Lock()
+{
+ if (impl_checkDisposed_Lock())
+ return;
+
+ // a collection of all (logical) forms
+ FmFormArray().swap(m_aSearchForms);
+ ::std::vector< OUString > aContextNames;
+ impl_collectFormSearchContexts_nothrow_Lock(
+ m_pShell->GetCurPage()->GetForms(), u"",
+ m_aSearchForms, aContextNames);
+
+ if ( m_aSearchForms.size() != aContextNames.size() )
+ {
+ SAL_WARN ( "svx.form", "FmXFormShell::ExecuteSearch: nonsense!" );
+ return;
+ }
+
+ // filter out the forms which do not contain valid controls at all
+ {
+ FmFormArray aValidForms;
+ ::std::vector< OUString > aValidContexts;
+ FmFormArray::const_iterator form = m_aSearchForms.begin();
+ ::std::vector< OUString >::const_iterator contextName = aContextNames.begin();
+ for ( ; form != m_aSearchForms.end(); ++form, ++contextName )
+ {
+ FmSearchContext aTestContext;
+ aTestContext.nContext = static_cast< sal_Int16 >( form - m_aSearchForms.begin() );
+ sal_uInt32 nValidControls = OnSearchContextRequest_Lock(aTestContext);
+ if ( nValidControls > 0 )
+ {
+ aValidForms.push_back( *form );
+ aValidContexts.push_back( *contextName );
+ }
+ }
+
+ m_aSearchForms.swap( aValidForms );
+ aContextNames.swap( aValidContexts );
+ }
+
+ if (m_aSearchForms.empty() )
+ {
+ // there are no controls that meet all the conditions for a search
+ std::unique_ptr<weld::MessageDialog> xBox(Application::CreateMessageDialog(nullptr,
+ VclMessageType::Warning, VclButtonsType::Ok,
+ SvxResId(RID_STR_NODATACONTROLS)));
+ xBox->run();
+ return;
+ }
+
+ // now I need another 'initial context'
+ sal_Int16 nInitialContext = 0;
+ Reference<XForm> xActiveForm(getActiveForm_Lock());
+ for ( size_t i=0; i<m_aSearchForms.size(); ++i )
+ {
+ if (m_aSearchForms.at(i) == xActiveForm)
+ {
+ nInitialContext = static_cast<sal_Int16>(i);
+ break;
+ }
+ }
+
+ // If the dialog should initially offer the text of the active control,
+ // this must have an XTextComponent interface. An addition, this makes
+ // sense only if the current field is also bound to a table (or whatever) field.
+ OUString strActiveField;
+ OUString strInitialText;
+ // ... this I get from my FormController
+ DBG_ASSERT(m_xActiveController.is(), "FmXFormShell::ExecuteSearch : no active controller !");
+ Reference< XControl> xActiveControl( m_xActiveController->getCurrentControl());
+ if (xActiveControl.is())
+ {
+ // the control can tell me its model ...
+ Reference< XControlModel> xActiveModel( xActiveControl->getModel());
+ DBG_ASSERT(xActiveModel.is(), "FmXFormShell::ExecuteSearch : active control has no model !");
+
+ // I ask the model for the ControlSource property ...
+ Reference< XPropertySet> xProperties(xActiveControl->getModel(), UNO_QUERY);
+ if (::comphelper::hasProperty(FM_PROP_CONTROLSOURCE, xProperties) && ::comphelper::hasProperty(FM_PROP_BOUNDFIELD, xProperties))
+ {
+ Reference< XPropertySet> xField;
+ xProperties->getPropertyValue(FM_PROP_BOUNDFIELD) >>= xField;
+ if (xField.is()) // (only when the thing is really bound)
+ {
+ // and the control itself for a TextComponent interface (so that I can pick up the text there)
+ Reference< XTextComponent> xText(xActiveControl, UNO_QUERY);
+ if (xText.is())
+ {
+ strActiveField = getLabelName(xProperties);
+ strInitialText = xText->getText();
+ }
+ }
+ }
+ else
+ {
+ // the control itself has no ControlSource, but maybe it is a GridControl
+ Reference< XGrid> xGrid(xActiveControl, UNO_QUERY);
+ if (xGrid.is())
+ {
+ // for strActiveField I need the ControlSource of the column,
+ // for that the columns container, for that the GridPeer
+ Reference< XGridPeer> xGridPeer(xActiveControl->getPeer(), UNO_QUERY);
+ Reference< XIndexAccess> xColumns;
+ if (xGridPeer.is())
+ xColumns = xGridPeer->getColumns();
+
+ sal_Int16 nViewCol = xGrid->getCurrentColumnPosition();
+ sal_Int32 nModelCol = GridView2ModelPos(xColumns, nViewCol);
+ Reference< XPropertySet> xCurrentCol;
+ if(xColumns.is())
+ xColumns->getByIndex(nModelCol) >>= xCurrentCol;
+ if (xCurrentCol.is())
+ strActiveField = ::comphelper::getString(xCurrentCol->getPropertyValue(FM_PROP_LABEL));
+
+ // the text of the current column
+ Reference< XIndexAccess> xColControls(xGridPeer, UNO_QUERY);
+ Reference< XInterface> xCurControl;
+ xColControls->getByIndex(nViewCol) >>= xCurControl;
+ OUString sInitialText;
+ if (IsSearchableControl(xCurControl, &sInitialText))
+ strInitialText = sInitialText;
+ }
+ }
+ }
+
+ // taking care of possible GridControls that I know
+ LoopGrids_Lock(LoopGridsSync::DISABLE_SYNC);
+
+ // Now I am ready for the dialogue.
+ // When the potential deadlocks caused by the use of the solar mutex in
+ // MTs VCLX... classes are eventually cleared, an SM_USETHREAD should be
+ // placed here, because the search in a separate thread is nevertheless
+ // somewhat more fluid. Should be, however, somehow made dependent of the
+ // underlying cursor. DAO for example is not thread-safe.
+ SvxAbstractDialogFactory* pFact = SvxAbstractDialogFactory::Create();
+ ScopedVclPtr<AbstractFmSearchDialog> pDialog(
+ pFact->CreateFmSearchDialog(
+ m_pShell->GetViewShell()->GetViewFrame()->GetFrameWeld(),
+ strInitialText, aContextNames, nInitialContext,
+ LINK(this, FmXFormShell, OnSearchContextRequest_Lock) ));
+ pDialog->SetActiveField( strActiveField );
+ pDialog->SetFoundHandler(LINK(this, FmXFormShell, OnFoundData_Lock));
+ pDialog->SetCanceledNotFoundHdl(LINK(this, FmXFormShell, OnCanceledNotFound_Lock));
+ pDialog->Execute();
+ pDialog.disposeAndClear();
+
+ // restore GridControls again
+ LoopGrids_Lock(LoopGridsSync::ENABLE_SYNC, LoopGridsFlags::DISABLE_ROCTRLR);
+
+ m_pShell->GetFormView()->UnMarkAll(m_pShell->GetFormView()->GetSdrPageView());
+ // because I marked controls in OnFoundData (if I was there)
+}
+
+
+bool FmXFormShell::GetY2KState_Lock(sal_uInt16& n)
+{
+ if (impl_checkDisposed_Lock())
+ return false;
+
+ if (m_pShell->IsDesignMode())
+ // in the design mode (without active controls) the main document is to take care of it
+ return false;
+
+ Reference<XForm> xForm(getActiveForm_Lock());
+ if (!xForm.is())
+ // no current form (in particular no current control) -> the main document is to take care
+ return false;
+
+ Reference< XRowSet> xDB(xForm, UNO_QUERY);
+ DBG_ASSERT(xDB.is(), "FmXFormShell::GetY2KState : current form has no dbform-interface !");
+
+ Reference< XNumberFormatsSupplier> xSupplier( getNumberFormats(getConnection(xDB)));
+ if (xSupplier.is())
+ {
+ Reference< XPropertySet> xSet(xSupplier->getNumberFormatSettings());
+ if (xSet.is())
+ {
+ try
+ {
+ Any aVal( xSet->getPropertyValue("TwoDigitDateStart") );
+ aVal >>= n;
+ return true;
+ }
+ catch(Exception&)
+ {
+ }
+
+ }
+ }
+ return false;
+}
+
+
+void FmXFormShell::SetY2KState_Lock(sal_uInt16 n)
+{
+ if (impl_checkDisposed_Lock())
+ return;
+
+ Reference<XForm> xActiveForm(getActiveForm_Lock());
+ Reference< XRowSet > xActiveRowSet( xActiveForm, UNO_QUERY );
+ if ( xActiveRowSet.is() )
+ {
+ Reference< XNumberFormatsSupplier > xSupplier( getNumberFormats( getConnection( xActiveRowSet ) ) );
+ if (xSupplier.is())
+ {
+ Reference< XPropertySet> xSet(xSupplier->getNumberFormatSettings());
+ if (xSet.is())
+ {
+ try
+ {
+ xSet->setPropertyValue("TwoDigitDateStart", Any(sal_uInt16(n)));
+ }
+ catch(Exception&)
+ {
+ TOOLS_WARN_EXCEPTION("svx.form", "");
+ }
+
+ }
+ return;
+ }
+ }
+
+ // no active form found -> iterate through all current forms
+ Reference< XIndexAccess> xCurrentForms( m_xForms);
+ if (!xCurrentForms.is())
+ { // in the alive mode, my forms are not set, but the ones on the page are
+ if (m_pShell->GetCurPage())
+ xCurrentForms = m_pShell->GetCurPage()->GetForms( false );
+ }
+ if (!xCurrentForms.is())
+ return;
+
+ ::comphelper::IndexAccessIterator aIter(xCurrentForms);
+ Reference< XInterface> xCurrentElement( aIter.Next());
+ while (xCurrentElement.is())
+ {
+ // is the current element a DatabaseForm?
+ Reference< XRowSet> xElementAsRowSet( xCurrentElement, UNO_QUERY );
+ if ( xElementAsRowSet.is() )
+ {
+ Reference< XNumberFormatsSupplier > xSupplier( getNumberFormats( getConnection( xElementAsRowSet ) ) );
+ if (!xSupplier.is())
+ continue;
+
+ Reference< XPropertySet> xSet(xSupplier->getNumberFormatSettings());
+ if (xSet.is())
+ {
+ try
+ {
+ xSet->setPropertyValue("TwoDigitDateStart", Any(sal_uInt16(n)));
+ }
+ catch(Exception&)
+ {
+ TOOLS_WARN_EXCEPTION("svx.form", "");
+ }
+
+ }
+ }
+ xCurrentElement = aIter.Next();
+ }
+}
+
+
+void FmXFormShell::CloseExternalFormViewer_Lock()
+{
+ if (impl_checkDisposed_Lock())
+ return;
+
+ if (!m_xExternalViewController.is())
+ return;
+
+ Reference< css::frame::XFrame> xExternalViewFrame( m_xExternalViewController->getFrame());
+ Reference< css::frame::XDispatchProvider> xCommLink(xExternalViewFrame, UNO_QUERY);
+ if (!xCommLink.is())
+ return;
+
+ xExternalViewFrame->setComponent(nullptr,nullptr);
+ ::comphelper::disposeComponent(xExternalViewFrame);
+ m_xExternalViewController = nullptr;
+ m_xExtViewTriggerController = nullptr;
+ m_xExternalDisplayedForm = nullptr;
+}
+
+
+Reference<XResultSet> FmXFormShell::getInternalForm_Lock(const Reference<XResultSet>& _xForm) const
+{
+ if (impl_checkDisposed_Lock())
+ return nullptr;
+
+ Reference< runtime::XFormController> xExternalCtrlr(m_xExternalViewController, UNO_QUERY);
+ if (xExternalCtrlr.is() && (_xForm == xExternalCtrlr->getModel()))
+ {
+ DBG_ASSERT(m_xExternalDisplayedForm.is(), "FmXFormShell::getInternalForm : invalid external form !");
+ return m_xExternalDisplayedForm;
+ }
+ return _xForm;
+}
+
+
+Reference<XForm> FmXFormShell::getInternalForm_Lock(const Reference<XForm>& _xForm) const
+{
+ if (impl_checkDisposed_Lock())
+ return nullptr;
+
+ Reference< runtime::XFormController > xExternalCtrlr(m_xExternalViewController, UNO_QUERY);
+ if (xExternalCtrlr.is() && (_xForm == xExternalCtrlr->getModel()))
+ {
+ DBG_ASSERT(m_xExternalDisplayedForm.is(), "FmXFormShell::getInternalForm : invalid external form !");
+ return Reference< XForm>(m_xExternalDisplayedForm, UNO_QUERY);
+ }
+ return _xForm;
+}
+
+
+namespace
+{
+ bool lcl_isNavigationRelevant( sal_Int32 _nWhich )
+ {
+ return ( _nWhich == SID_FM_RECORD_FIRST )
+ || ( _nWhich == SID_FM_RECORD_PREV )
+ || ( _nWhich == SID_FM_RECORD_NEXT )
+ || ( _nWhich == SID_FM_RECORD_LAST )
+ || ( _nWhich == SID_FM_RECORD_NEW );
+ }
+}
+
+
+bool FmXFormShell::IsFormSlotEnabled( sal_Int32 _nSlot, FeatureState* _pCompleteState ) const
+{
+ const svx::ControllerFeatures& rController =
+ lcl_isNavigationRelevant( _nSlot )
+ ? getNavControllerFeatures_Lock()
+ : getActiveControllerFeatures_Lock();
+
+ if ( !_pCompleteState )
+ return rController->isEnabled( _nSlot );
+
+ rController->getState( _nSlot, *_pCompleteState );
+ return _pCompleteState->Enabled;
+}
+
+
+void FmXFormShell::ExecuteFormSlot_Lock( sal_Int32 _nSlot )
+{
+ const svx::ControllerFeatures& rController =
+ lcl_isNavigationRelevant( _nSlot )
+ ? getNavControllerFeatures_Lock()
+ : getActiveControllerFeatures_Lock();
+
+ rController->execute( _nSlot );
+
+ if ( _nSlot != SID_FM_RECORD_UNDO )
+ return;
+
+ // if we're doing an UNDO, *and* if the affected form is the form which we also display
+ // as external view, then we need to reset the controls of the external form, too
+ if (getInternalForm_Lock(getActiveForm_Lock()) != m_xExternalDisplayedForm)
+ return;
+
+ Reference< XIndexAccess > xContainer( m_xExternalDisplayedForm, UNO_QUERY );
+ if ( !xContainer.is() )
+ return;
+
+ Reference< XReset > xReset;
+ for ( sal_Int32 i = 0; i < xContainer->getCount(); ++i )
+ {
+ if ( ( xContainer->getByIndex( i ) >>= xReset ) && xReset.is() )
+ {
+ // no resets on sub forms
+ Reference< XForm > xAsForm( xReset, UNO_QUERY );
+ if ( !xAsForm.is() )
+ xReset->reset();
+ }
+ }
+}
+
+
+void FmXFormShell::impl_switchActiveControllerListening_Lock(const bool _bListen)
+{
+ if ( !m_xActiveController.is() )
+ return;
+
+ if ( _bListen )
+ m_xActiveController->addEventListener( static_cast<XFormControllerListener*>(this) );
+ else
+ m_xActiveController->removeEventListener( static_cast<XFormControllerListener*>(this) );
+}
+
+
+void FmXFormShell::setActiveController_Lock(const Reference<runtime::XFormController>& xController, bool _bNoSaveOldContent)
+{
+ if (impl_checkDisposed_Lock())
+ return;
+
+ if (m_bChangingDesignMode)
+ return;
+ DBG_ASSERT(!m_pShell->IsDesignMode(), "only to be used in alive mode");
+
+ // if the routine has been called a second time,
+ // the focus should no longer be transferred
+ if (m_bInActivate)
+ {
+ m_bSetFocus = xController != m_xActiveController;
+ return;
+ }
+
+ if (xController == m_xActiveController)
+ return;
+
+ // switch all nav dispatchers belonging to the form of the current nav controller to 'non active'
+ Reference< XResultSet> xNavigationForm;
+ if (m_xNavigationController.is())
+ xNavigationForm.set(m_xNavigationController->getModel(), UNO_QUERY);
+
+ m_bInActivate = true;
+
+ // check if the 2 controllers serve different forms
+ Reference< XResultSet> xOldForm;
+ if (m_xActiveController.is())
+ xOldForm.set(m_xActiveController->getModel(), UNO_QUERY);
+ Reference< XResultSet> xNewForm;
+ if (xController.is())
+ xNewForm = Reference< XResultSet>(xController->getModel(), UNO_QUERY);
+ xOldForm = getInternalForm_Lock(xOldForm);
+ xNewForm = getInternalForm_Lock(xNewForm);
+
+ bool bDifferentForm = ( xOldForm.get() != xNewForm.get() );
+ bool bNeedSave = bDifferentForm && !_bNoSaveOldContent;
+ // we save the content of the old form if we move to a new form, and saving old content is allowed
+
+ if ( m_xActiveController.is() && bNeedSave )
+ {
+ // save content on change of the controller; a commit has already been executed
+ if ( m_aActiveControllerFeatures->commitCurrentControl() )
+ {
+ m_bSetFocus = true;
+ if ( m_aActiveControllerFeatures->isModifiedRow() )
+ {
+ bool bIsNew = m_aActiveControllerFeatures->isInsertionRow();
+ bool bResult = m_aActiveControllerFeatures->commitCurrentRecord();
+ if ( !bResult && m_bSetFocus )
+ {
+ // if we couldn't save the current record, set the focus back to the
+ // current control
+ Reference< XWindow > xWindow( m_xActiveController->getCurrentControl(), UNO_QUERY );
+ if ( xWindow.is() )
+ xWindow->setFocus();
+ m_bInActivate = false;
+ return;
+ }
+ else if ( bResult && bIsNew )
+ {
+ Reference< XResultSet > xCursor( m_aActiveControllerFeatures->getCursor() );
+ if ( xCursor.is() )
+ {
+ DO_SAFE( xCursor->last(); );
+ }
+ }
+ }
+ }
+ }
+
+ stopListening_Lock();
+
+ impl_switchActiveControllerListening_Lock(false);
+
+ m_aActiveControllerFeatures.dispose();
+ m_xActiveController = xController;
+ if ( m_xActiveController.is() )
+ m_aActiveControllerFeatures.assign( m_xActiveController );
+
+ impl_switchActiveControllerListening_Lock(true);
+
+ if ( m_xActiveController.is() )
+ m_xActiveForm = getInternalForm_Lock(Reference<XForm>(m_xActiveController->getModel(), UNO_QUERY));
+ else
+ m_xActiveForm = nullptr;
+
+ startListening_Lock();
+
+ // activate all dispatchers belonging to form of the new navigation controller
+ xNavigationForm = nullptr;
+ if (m_xNavigationController.is())
+ xNavigationForm.set(m_xNavigationController->getModel(), UNO_QUERY);
+
+ m_bInActivate = false;
+
+ m_pShell->UIFeatureChanged();
+ m_pShell->GetViewShell()->GetViewFrame()->GetBindings().InvalidateShell(*m_pShell);
+
+ InvalidateSlot_Lock(SID_FM_FILTER_NAVIGATOR_CONTROL, true);
+}
+
+
+void FmXFormShell::getCurrentSelection_Lock(InterfaceBag& /* [out] */ _rSelection) const
+{
+ _rSelection = m_aCurrentSelection;
+}
+
+
+bool FmXFormShell::setCurrentSelectionFromMark_Lock(const SdrMarkList& _rMarkList)
+{
+ m_aLastKnownMarkedControls.clear();
+
+ if ( ( _rMarkList.GetMarkCount() > 0 ) && isControlList( _rMarkList ) )
+ collectInterfacesFromMarkList( _rMarkList, m_aLastKnownMarkedControls );
+
+ return setCurrentSelection_Lock(o3tl::sorted_vector(m_aLastKnownMarkedControls));
+}
+
+
+bool FmXFormShell::selectLastMarkedControls_Lock()
+{
+ return setCurrentSelection_Lock(o3tl::sorted_vector(m_aLastKnownMarkedControls));
+}
+
+
+bool FmXFormShell::setCurrentSelection_Lock( InterfaceBag&& _rSelection )
+{
+ if (impl_checkDisposed_Lock())
+ return false;
+
+ DBG_ASSERT( m_pShell->IsDesignMode(), "FmXFormShell::setCurrentSelection: only to be used in design mode!" );
+
+ if ( _rSelection.empty() && m_aCurrentSelection.empty() )
+ // nothing to do
+ return false;
+
+ if ( _rSelection.size() == m_aCurrentSelection.size() )
+ {
+ InterfaceBag::const_iterator aNew = _rSelection.begin();
+ InterfaceBag::const_iterator aOld = m_aCurrentSelection.begin();
+ for ( ; aNew != _rSelection.end(); ++aNew, ++aOld )
+ {
+ OSL_ENSURE( Reference< XInterface >( *aNew, UNO_QUERY ).get() == aNew->get(), "FmXFormShell::setCurrentSelection: new interface not normalized!" );
+ OSL_ENSURE( Reference< XInterface >( *aOld, UNO_QUERY ).get() == aOld->get(), "FmXFormShell::setCurrentSelection: old interface not normalized!" );
+
+ if ( aNew->get() != aOld->get() )
+ break;
+ }
+
+ if ( aNew == _rSelection.end() )
+ // both bags equal
+ return false;
+ }
+
+ // the following is some strange code to ensure that when you have two grid controls in a document,
+ // only one of them can have a selected column.
+ // TODO: this should happen elsewhere, but not here - shouldn't it?
+ if ( !m_aCurrentSelection.empty() )
+ {
+ Reference< XChild > xCur; if ( m_aCurrentSelection.size() == 1 ) xCur.set(*m_aCurrentSelection.begin(), css::uno::UNO_QUERY);
+ Reference< XChild > xNew; if ( _rSelection.size() == 1 ) xNew.set(*_rSelection.begin(), css::uno::UNO_QUERY);
+
+ // is there nothing to be selected, or the parents differ, and the parent of the current object
+ // is a selection supplier, then deselect
+ if ( xCur.is() && ( !xNew.is() || ( xCur->getParent() != xNew->getParent() ) ) )
+ {
+ Reference< XSelectionSupplier > xSel( xCur->getParent(), UNO_QUERY );
+ if ( xSel.is() )
+ xSel->select( Any() );
+ }
+ }
+
+ m_aCurrentSelection = _rSelection;
+
+ // determine the form which all the selected objects belong to, if any
+ Reference< XForm > xNewCurrentForm;
+ for (const auto& rpSelection : m_aCurrentSelection)
+ {
+ Reference< XForm > xThisRoundsForm( GetForm( rpSelection ) );
+ OSL_ENSURE( xThisRoundsForm.is(), "FmXFormShell::setCurrentSelection: *everything* should belong to a form!" );
+
+ if ( !xNewCurrentForm.is() )
+ { // the first form we encountered
+ xNewCurrentForm = xThisRoundsForm;
+ }
+ else if ( xNewCurrentForm != xThisRoundsForm )
+ { // different forms -> no "current form" at all
+ xNewCurrentForm.clear();
+ break;
+ }
+ }
+
+ if ( !m_aCurrentSelection.empty() )
+ impl_updateCurrentForm_Lock(xNewCurrentForm);
+
+ // ensure some slots are updated
+ for (sal_Int16 i : SelObjectSlotMap)
+ InvalidateSlot_Lock(i, false);
+
+ return true;
+}
+
+
+bool FmXFormShell::isSolelySelected_Lock(const Reference<XInterface>& _rxObject)
+{
+ return ( m_aCurrentSelection.size() == 1 ) && ( *m_aCurrentSelection.begin() == _rxObject );
+}
+
+
+void FmXFormShell::forgetCurrentForm_Lock()
+{
+ if ( !m_xCurrentForm.is() )
+ return;
+
+ // reset ...
+ impl_updateCurrentForm_Lock(nullptr);
+
+ // ... and try finding a new current form
+ // #i88186# / 2008-04-12 / frank.schoenheit@sun.com
+ impl_defaultCurrentForm_nothrow_Lock();
+}
+
+
+void FmXFormShell::impl_updateCurrentForm_Lock(const Reference<XForm>& _rxNewCurForm)
+{
+ if (impl_checkDisposed_Lock())
+ return;
+
+ m_xCurrentForm = _rxNewCurForm;
+
+ // propagate to the FormPage(Impl)
+ FmFormPage* pPage = m_pShell->GetCurPage();
+ if ( pPage )
+ pPage->GetImpl().setCurForm( m_xCurrentForm );
+
+ // ensure the UI which depends on the current form is up-to-date
+ for (sal_Int16 i : DlgSlotMap)
+ InvalidateSlot_Lock(i, false);
+}
+
+
+void FmXFormShell::startListening_Lock()
+{
+ if (impl_checkDisposed_Lock())
+ return;
+
+ Reference< XRowSet> xDatabaseForm(m_xActiveForm, UNO_QUERY);
+ if (xDatabaseForm.is() && getConnection(xDatabaseForm).is())
+ {
+ Reference< XPropertySet> xActiveFormSet(m_xActiveForm, UNO_QUERY);
+ if (xActiveFormSet.is())
+ {
+ // if there is a data source, then build the listener
+ // TODO: this is strange - shouldn't this depend on a isLoaded instead of
+ // a "has command value"? Finally, the command value only means that it was
+ // intended to be loaded, not that it actually *is* loaded
+ OUString aSource = ::comphelper::getString(xActiveFormSet->getPropertyValue(FM_PROP_COMMAND));
+ if (!aSource.isEmpty())
+ {
+ m_bDatabaseBar = true;
+
+ xActiveFormSet->getPropertyValue(FM_PROP_NAVIGATION) >>= m_eNavigate;
+
+ switch (m_eNavigate)
+ {
+ case NavigationBarMode_PARENT:
+ {
+ // search for the controller via which navigation is possible
+ Reference< XChild> xChild = m_xActiveController;
+ Reference< runtime::XFormController > xParent;
+ while (xChild.is())
+ {
+ xChild.set(xChild->getParent(), UNO_QUERY);
+ xParent.set(xChild, UNO_QUERY);
+ Reference< XPropertySet> xParentSet;
+ if (xParent.is())
+ xParentSet.set(xParent->getModel(), UNO_QUERY);
+ if (xParentSet.is())
+ {
+ xParentSet->getPropertyValue(FM_PROP_NAVIGATION) >>= m_eNavigate;
+ if (m_eNavigate == NavigationBarMode_CURRENT)
+ break;
+ }
+ }
+ m_xNavigationController = xParent;
+ }
+ break;
+
+ case NavigationBarMode_CURRENT:
+ m_xNavigationController = m_xActiveController;
+ break;
+
+ default:
+ m_xNavigationController = nullptr;
+ m_bDatabaseBar = false;
+ }
+
+ m_aNavControllerFeatures.dispose();
+ if ( m_xNavigationController.is() && ( m_xNavigationController != m_xActiveController ) )
+ m_aNavControllerFeatures.assign( m_xNavigationController );
+
+ // because of RecordCount, listen at the controller which controls the navigation
+ Reference< XPropertySet> xNavigationSet;
+ if (m_xNavigationController.is())
+ {
+ xNavigationSet.set(m_xNavigationController->getModel(), UNO_QUERY);
+ if (xNavigationSet.is())
+ xNavigationSet->addPropertyChangeListener(FM_PROP_ROWCOUNT,this);
+ }
+ return;
+ }
+ }
+ }
+
+ m_eNavigate = NavigationBarMode_NONE;
+ m_bDatabaseBar = false;
+ m_xNavigationController = nullptr;
+}
+
+
+void FmXFormShell::stopListening_Lock()
+{
+ if (impl_checkDisposed_Lock())
+ return;
+
+ Reference< XRowSet> xDatabaseForm(m_xActiveForm, UNO_QUERY);
+ if ( xDatabaseForm.is() )
+ {
+ if (m_xNavigationController.is())
+ {
+ Reference< XPropertySet> xSet(m_xNavigationController->getModel(), UNO_QUERY);
+ if (xSet.is())
+ xSet->removePropertyChangeListener(FM_PROP_ROWCOUNT, this);
+
+ }
+ }
+
+ m_bDatabaseBar = false;
+ m_eNavigate = NavigationBarMode_NONE;
+ m_xNavigationController = nullptr;
+}
+
+
+void FmXFormShell::ShowSelectionProperties_Lock(bool bShow)
+{
+ if (impl_checkDisposed_Lock())
+ return;
+
+ // if the window is already visible, only update the state
+ bool bHasChild = m_pShell->GetViewShell()->GetViewFrame()->HasChildWindow( SID_FM_SHOW_PROPERTIES );
+ if ( bHasChild && bShow )
+ UpdateSlot_Lock(SID_FM_PROPERTY_CONTROL);
+
+ // else toggle state
+ else
+ m_pShell->GetViewShell()->GetViewFrame()->ToggleChildWindow(SID_FM_SHOW_PROPERTIES);
+
+ InvalidateSlot_Lock(SID_FM_PROPERTIES, false);
+ InvalidateSlot_Lock(SID_FM_CTL_PROPERTIES, false);
+}
+
+
+IMPL_LINK(FmXFormShell, OnFoundData_Lock, FmFoundRecordInformation&, rfriWhere, void)
+{
+ if (impl_checkDisposed_Lock())
+ return;
+
+ DBG_ASSERT((rfriWhere.nContext >= 0) && (o3tl::make_unsigned(rfriWhere.nContext) < m_aSearchForms.size()),
+ "FmXFormShell::OnFoundData : invalid context!");
+ Reference< XForm> xForm( m_aSearchForms.at(rfriWhere.nContext));
+ DBG_ASSERT(xForm.is(), "FmXFormShell::OnFoundData : invalid form!");
+
+ Reference< XRowLocate> xCursor(xForm, UNO_QUERY);
+ if (!xCursor.is())
+ return; // what should I do there?
+
+ // to the record
+ try
+ {
+ xCursor->moveToBookmark(rfriWhere.aPosition);
+ }
+ catch(const SQLException&)
+ {
+ OSL_FAIL("Can position on bookmark!");
+ }
+
+ LoopGrids_Lock(LoopGridsSync::FORCE_SYNC);
+
+ // and to the field (for that, I collected the XVclComponent interfaces before the start of the search)
+ SAL_WARN_IF(o3tl::make_unsigned(rfriWhere.nFieldPos) >=
+ m_arrSearchedControls.size(),
+ "svx.form", "FmXFormShell::OnFoundData : invalid index!");
+ SdrObject* pObject = m_arrSearchedControls.at(rfriWhere.nFieldPos);
+
+ m_pShell->GetFormView()->UnMarkAll(m_pShell->GetFormView()->GetSdrPageView());
+ m_pShell->GetFormView()->MarkObj(pObject, m_pShell->GetFormView()->GetSdrPageView());
+
+ FmFormObj* pFormObject = FmFormObj::GetFormObject( pObject );
+ Reference< XControlModel > xControlModel( pFormObject ? pFormObject->GetUnoControlModel() : Reference< XControlModel >() );
+ DBG_ASSERT( xControlModel.is(), "FmXFormShell::OnFoundData: invalid control!" );
+ if ( !xControlModel.is() )
+ return;
+
+ // disable the permanent cursor for the last grid we found a record
+ if (m_xLastGridFound.is() && (m_xLastGridFound != xControlModel))
+ {
+ Reference< XPropertySet> xOldSet(m_xLastGridFound, UNO_QUERY);
+ xOldSet->setPropertyValue(FM_PROP_ALWAYSSHOWCURSOR, Any( false ) );
+ Reference< XPropertyState> xOldSetState(xOldSet, UNO_QUERY);
+ if (xOldSetState.is())
+ xOldSetState->setPropertyToDefault(FM_PROP_CURSORCOLOR);
+ else
+ xOldSet->setPropertyValue(FM_PROP_CURSORCOLOR, Any());
+ }
+
+ // if the field is in a GridControl, I have to additionally go into the corresponding column there
+ sal_Int32 nGridColumn = m_arrRelativeGridColumn[rfriWhere.nFieldPos];
+ if (nGridColumn != -1)
+ { // unfortunately, I have to first get the control again
+ Reference<XControl> xControl(pFormObject ? impl_getControl_Lock(xControlModel, *pFormObject) : Reference<XControl>());
+ Reference< XGrid> xGrid(xControl, UNO_QUERY);
+ DBG_ASSERT(xGrid.is(), "FmXFormShell::OnFoundData : invalid control!");
+ // if one of the asserts fires, I probably did something wrong on building of m_arrSearchedControls
+
+ // enable a permanent cursor for the grid so we can see the found text
+ Reference< XPropertySet> xModelSet(xControlModel, UNO_QUERY);
+ DBG_ASSERT(xModelSet.is(), "FmXFormShell::OnFoundData : invalid control model (no property set) !");
+ xModelSet->setPropertyValue( FM_PROP_ALWAYSSHOWCURSOR, Any( true ) );
+ xModelSet->setPropertyValue( FM_PROP_CURSORCOLOR, Any( COL_LIGHTRED ) );
+ m_xLastGridFound = xControlModel;
+
+ if ( xGrid.is() )
+ xGrid->setCurrentColumnPosition(static_cast<sal_Int16>(nGridColumn));
+ }
+
+ // As the cursor has been repositioned, I have (in positioned) invalidated
+ // my form bar slots. But that does not take effect here unfortunately, as
+ // generally the (modal) search dialog is of course at the top ... So, force ...
+ sal_uInt16 nPos = 0;
+ while (DatabaseSlotMap[nPos])
+ m_pShell->GetViewShell()->GetViewFrame()->GetBindings().Update(DatabaseSlotMap[nPos++]);
+ // unfortunately the update goes against the invalidate with only individual slots
+}
+
+
+IMPL_LINK(FmXFormShell, OnCanceledNotFound_Lock, FmFoundRecordInformation&, rfriWhere, void)
+{
+ if (impl_checkDisposed_Lock())
+ return;
+
+ DBG_ASSERT((rfriWhere.nContext >= 0) && (o3tl::make_unsigned(rfriWhere.nContext) < m_aSearchForms.size()),
+ "FmXFormShell::OnCanceledNotFound : invalid context!");
+ Reference< XForm> xForm( m_aSearchForms.at(rfriWhere.nContext));
+ DBG_ASSERT(xForm.is(), "FmXFormShell::OnCanceledNotFound : invalid form!");
+
+ Reference< XRowLocate> xCursor(xForm, UNO_QUERY);
+ if (!xCursor.is())
+ return; // what should I do there?
+
+ // to the record
+ try
+ {
+ xCursor->moveToBookmark(rfriWhere.aPosition);
+ }
+ catch(const SQLException&)
+ {
+ OSL_FAIL("Can position on bookmark!");
+ }
+
+
+ m_pShell->GetFormView()->UnMarkAll(m_pShell->GetFormView()->GetSdrPageView());
+}
+
+
+IMPL_LINK(FmXFormShell, OnSearchContextRequest_Lock, FmSearchContext&, rfmscContextInfo, sal_uInt32)
+{
+ if (impl_checkDisposed_Lock())
+ return 0;
+
+ DBG_ASSERT(rfmscContextInfo.nContext < static_cast<sal_Int16>(m_aSearchForms.size()), "FmXFormShell::OnSearchContextRequest : invalid parameter !");
+ Reference< XForm> xForm( m_aSearchForms.at(rfmscContextInfo.nContext));
+ DBG_ASSERT(xForm.is(), "FmXFormShell::OnSearchContextRequest : unexpected : invalid context !");
+
+ Reference< XResultSet> xIter(xForm, UNO_QUERY);
+ DBG_ASSERT(xIter.is(), "FmXFormShell::OnSearchContextRequest : unexpected : context has no iterator !");
+
+
+ // assemble the list of fields to involve (that is, the ControlSources of all fields that have such a property)
+ OUString strFieldList, sFieldDisplayNames;
+ m_arrSearchedControls.clear();
+ m_arrRelativeGridColumn.clear();
+
+ // small problem: To mark found fields, I need SdrObjects. To determine which controls
+ // to include in the search, I need Controls (that is, XControl interfaces). So I have
+ // to iterate over one of them and get the other in some way. Unfortunately, there is
+ // no direct connection between the two worlds (except from a GetUnoControl to a
+ // SdrUnoObject, but this requires an OutputDevice I can not do anything with.
+ // However I can get to the Model from the Control and also from the SdrObject, and in
+ // this way the assignment SdrObject<->Control is possible with a double loop.
+ // The alternative to this (ugly but certainly not entirely fixable) solution would be
+ // to renounce the caching of the SdrObjects, which would lead to significant extra
+ // work in OnFoundData (since there I'd have to get the SdrObject first thing every
+ // time). But since OnFoundData is usually called more often than ExecuteSearch, I'll
+ // do that here.
+
+ Reference< XNameAccess> xValidFormFields;
+ Reference< XColumnsSupplier> xSupplyCols(xIter, UNO_QUERY);
+ DBG_ASSERT(xSupplyCols.is(), "FmXFormShell::OnSearchContextRequest : invalid cursor : no columns supplier !");
+ if (xSupplyCols.is())
+ xValidFormFields = xSupplyCols->getColumns();
+ DBG_ASSERT(xValidFormFields.is(), "FmXFormShell::OnSearchContextRequest : form has no fields !");
+
+ // current Page/Controller
+ FmFormPage* pCurrentPage = m_pShell->GetCurPage();
+ assert(pCurrentPage && "FmXFormShell::OnSearchContextRequest : no page !");
+ // Search all SdrControls of this page...
+ OUString sControlSource, aName;
+
+ SdrObjListIter aPageIter( pCurrentPage );
+ while ( aPageIter.IsMore() )
+ {
+ SdrObject* pCurrent = aPageIter.Next();
+ FmFormObj* pFormObject = FmFormObj::GetFormObject( pCurrent );
+ // note that in case pCurrent is a virtual object, pFormObject points to the referenced object
+
+ if ( !pFormObject )
+ continue;
+
+ // the current object's model, in different tastes
+ Reference< XControlModel> xControlModel( pFormObject->GetUnoControlModel() );
+ Reference< XFormComponent > xCurrentFormComponent( xControlModel, UNO_QUERY );
+ DBG_ASSERT( xCurrentFormComponent.is(), "FmXFormShell::OnSearchContextRequest: invalid objects!" );
+ if ( !xCurrentFormComponent.is() )
+ continue;
+
+ // does the component belong to the form which we're interested in?
+ if ( xCurrentFormComponent->getParent() != xForm )
+ continue;
+
+ // ... ask for the ControlSource property
+ SearchableControlIterator iter( xCurrentFormComponent );
+ Reference< XControl> xControl;
+ // the control that has model xControlModel
+ // (the following while can be passed through several times, without the Control
+ // being modified, so I don't have to search every time from scratch)
+
+ Reference< XInterface > xSearchable( iter.Next() );
+ while ( xSearchable.is() )
+ {
+ sControlSource = iter.getCurrentValue();
+ if ( sControlSource.isEmpty() )
+ {
+ // the current element has no ControlSource, so it is a GridControl (that
+ // is the only thing that still permits the SearchableControlIteratore)
+ xControl = impl_getControl_Lock(xControlModel, *pFormObject);
+ DBG_ASSERT(xControl.is(), "FmXFormShell::OnSearchContextRequest : didn't ::std::find a control with requested model !");
+
+ Reference< XGridPeer> xGridPeer;
+ if ( xControl.is() )
+ xGridPeer.set( xControl->getPeer(), UNO_QUERY );
+ do
+ {
+ if (!xGridPeer.is())
+ break;
+
+ Reference< XIndexAccess> xPeerContainer(xGridPeer, UNO_QUERY);
+ if (!xPeerContainer.is())
+ break;
+
+ Reference< XIndexAccess> xModelColumns = xGridPeer->getColumns();
+ DBG_ASSERT(xModelColumns.is(), "FmXFormShell::OnSearchContextRequest : there is a grid control without columns !");
+ // the case 'no columns' should be indicated with an empty container, I think ...
+ DBG_ASSERT(xModelColumns->getCount() >= xPeerContainer->getCount(), "FmXFormShell::OnSearchContextRequest : impossible : have more view than model columns !");
+
+ Reference< XInterface> xCurrentColumn;
+ for (sal_Int32 nViewPos=0; nViewPos<xPeerContainer->getCount(); ++nViewPos)
+ {
+ xPeerContainer->getByIndex(nViewPos) >>= xCurrentColumn;
+ if (!xCurrentColumn.is())
+ continue;
+
+ // can we use this column control for searching ?
+ if (!IsSearchableControl(xCurrentColumn))
+ continue;
+
+ sal_Int32 nModelPos = GridView2ModelPos(xModelColumns, nViewPos);
+ Reference< XPropertySet> xCurrentColModel;
+ xModelColumns->getByIndex(nModelPos) >>= xCurrentColModel;
+ aName = ::comphelper::getString(xCurrentColModel->getPropertyValue(FM_PROP_CONTROLSOURCE));
+ // the cursor has a field matching the control source ?
+ if (xValidFormFields->hasByName(aName))
+ {
+ strFieldList += aName + ";";
+
+ sFieldDisplayNames +=
+ ::comphelper::getString(xCurrentColModel->getPropertyValue(FM_PROP_LABEL)) +
+ ";";
+
+ rfmscContextInfo.arrFields.push_back(xCurrentColumn);
+
+ // and the SdrOject to the Field
+ m_arrSearchedControls.push_back(pCurrent);
+ // the number of the column
+ m_arrRelativeGridColumn.push_back(nViewPos);
+ }
+ }
+ } while (false);
+ }
+ else
+ {
+ if (!sControlSource.isEmpty() && xValidFormFields->hasByName(sControlSource))
+ {
+ // now I need the Control to SdrObject
+ if (!xControl.is())
+ {
+ xControl = impl_getControl_Lock(xControlModel, *pFormObject);
+ DBG_ASSERT(xControl.is(), "FmXFormShell::OnSearchContextRequest : didn't ::std::find a control with requested model !");
+ }
+
+ if (IsSearchableControl(xControl))
+ {
+ // all tests passed -> take along in the list
+ strFieldList += sControlSource + ";";
+
+ // the label which should appear for the control :
+ sFieldDisplayNames +=
+ getLabelName(Reference< XPropertySet>(xControlModel, UNO_QUERY)) +
+ ";";
+
+ // mark the SdrObject (accelerates the treatment in OnFoundData)
+ m_arrSearchedControls.push_back(pCurrent);
+
+ // the number of the column (here a dummy, since it is only interesting for GridControls)
+ m_arrRelativeGridColumn.push_back(-1);
+
+ // and for the formatted search...
+ rfmscContextInfo.arrFields.emplace_back( xControl, UNO_QUERY );
+ }
+ }
+ }
+
+ xSearchable = iter.Next();
+ }
+ }
+
+ strFieldList = comphelper::string::stripEnd(strFieldList, ';');
+ sFieldDisplayNames = comphelper::string::stripEnd(sFieldDisplayNames, ';');
+
+ if (rfmscContextInfo.arrFields.empty())
+ {
+ rfmscContextInfo.arrFields.clear();
+ rfmscContextInfo.xCursor = nullptr;
+ rfmscContextInfo.strUsedFields.clear();
+ return 0;
+ }
+
+ rfmscContextInfo.xCursor = xIter;
+ rfmscContextInfo.strUsedFields = strFieldList;
+ rfmscContextInfo.sFieldDisplayNames = sFieldDisplayNames;
+
+ // 66463 - 31.05.99 - FS
+ // when the cursor is a non-STANDARD RecordMode, set it back
+ Reference< XPropertySet> xCursorSet(rfmscContextInfo.xCursor, UNO_QUERY);
+ Reference< XResultSetUpdate> xUpdateCursor(rfmscContextInfo.xCursor, UNO_QUERY);
+ if (xUpdateCursor.is() && xCursorSet.is())
+ {
+ if (::comphelper::getBOOL(xCursorSet->getPropertyValue(FM_PROP_ISNEW)))
+ xUpdateCursor->moveToCurrentRow();
+ else if (::comphelper::getBOOL(xCursorSet->getPropertyValue(FM_PROP_ISMODIFIED)))
+ xUpdateCursor->cancelRowUpdates();
+ }
+
+ return rfmscContextInfo.arrFields.size();
+}
+
+ // XContainerListener
+
+void SAL_CALL FmXFormShell::elementInserted(const ContainerEvent& evt)
+{
+ SolarMutexGuard g;
+
+ if (impl_checkDisposed_Lock())
+ return;
+
+ // new object to listen to
+ Reference< XInterface> xTemp;
+ evt.Element >>= xTemp;
+ AddElement_Lock(xTemp);
+
+ m_pShell->DetermineForms(true);
+}
+
+
+void SAL_CALL FmXFormShell::elementReplaced(const ContainerEvent& evt)
+{
+ SolarMutexGuard g;
+
+ if (impl_checkDisposed_Lock() )
+ return;
+
+ Reference< XInterface> xTemp;
+ evt.ReplacedElement >>= xTemp;
+ RemoveElement_Lock(xTemp);
+ evt.Element >>= xTemp;
+ AddElement_Lock(xTemp);
+}
+
+
+void SAL_CALL FmXFormShell::elementRemoved(const ContainerEvent& evt)
+{
+ SolarMutexGuard g;
+
+ if (impl_checkDisposed_Lock())
+ return;
+
+ Reference< XInterface> xTemp;
+ evt.Element >>= xTemp;
+ RemoveElement_Lock(xTemp);
+
+ m_pShell->DetermineForms(true);
+}
+
+
+void FmXFormShell::UpdateForms_Lock(bool _bInvalidate)
+{
+ if (impl_checkDisposed_Lock())
+ return;
+
+ Reference< XIndexAccess > xForms;
+
+ FmFormPage* pPage = m_pShell->GetCurPage();
+ if ( pPage && m_pShell->m_bDesignMode )
+ xForms = pPage->GetForms( false );
+
+ if ( m_xForms != xForms )
+ {
+ RemoveElement_Lock( m_xForms );
+ m_xForms = xForms;
+ AddElement_Lock(m_xForms);
+ }
+
+ SolarMutexGuard g;
+ m_pShell->DetermineForms( _bInvalidate );
+}
+
+
+void FmXFormShell::AddElement_Lock(const Reference<XInterface>& _xElement)
+{
+ if (impl_checkDisposed_Lock())
+ return;
+ impl_AddElement_nothrow(_xElement);
+}
+
+void FmXFormShell::impl_AddElement_nothrow(const Reference< XInterface>& Element)
+{
+ // listen at the container
+ const Reference< XIndexContainer> xContainer(Element, UNO_QUERY);
+ if (xContainer.is())
+ {
+ const sal_uInt32 nCount = xContainer->getCount();
+ Reference< XInterface> xElement;
+ for (sal_uInt32 i = 0; i < nCount; ++i)
+ {
+ xElement.set(xContainer->getByIndex(i),UNO_QUERY);
+ impl_AddElement_nothrow(xElement);
+ }
+
+ const Reference< XContainer> xCont(Element, UNO_QUERY);
+ if (xCont.is())
+ xCont->addContainerListener(this);
+ }
+
+ const Reference< css::view::XSelectionSupplier> xSelSupplier(Element, UNO_QUERY);
+ if (xSelSupplier.is())
+ xSelSupplier->addSelectionChangeListener(this);
+}
+
+
+void FmXFormShell::RemoveElement_Lock(const Reference<XInterface>& Element)
+{
+ if (impl_checkDisposed_Lock())
+ return;
+ impl_RemoveElement_nothrow_Lock(Element);
+}
+
+void FmXFormShell::impl_RemoveElement_nothrow_Lock(const Reference<XInterface>& Element)
+{
+ const Reference< css::view::XSelectionSupplier> xSelSupplier(Element, UNO_QUERY);
+ if (xSelSupplier.is())
+ xSelSupplier->removeSelectionChangeListener(this);
+
+ // remove connection to children
+ const Reference< XIndexContainer> xContainer(Element, UNO_QUERY);
+ if (xContainer.is())
+ {
+ const Reference< XContainer> xCont(Element, UNO_QUERY);
+ if (xCont.is())
+ xCont->removeContainerListener(this);
+
+ const sal_uInt32 nCount = xContainer->getCount();
+ Reference< XInterface> xElement;
+ for (sal_uInt32 i = 0; i < nCount; i++)
+ {
+ xElement.set(xContainer->getByIndex(i),UNO_QUERY);
+ impl_RemoveElement_nothrow_Lock(xElement);
+ }
+ }
+
+ auto wasSelectedPos = m_aCurrentSelection.find( Element );
+ if ( wasSelectedPos != m_aCurrentSelection.end() )
+ m_aCurrentSelection.erase( wasSelectedPos );
+}
+
+
+void SAL_CALL FmXFormShell::selectionChanged(const lang::EventObject& rEvent)
+{
+ SolarMutexGuard g;
+
+ if (impl_checkDisposed_Lock())
+ return;
+
+ Reference< XSelectionSupplier > xSupplier( rEvent.Source, UNO_QUERY );
+ Reference< XInterface > xSelObj( xSupplier->getSelection(), UNO_QUERY );
+ // a selection was removed, this can only be done by the shell
+ if ( !xSelObj.is() )
+ return;
+
+ EnableTrackProperties_Lock(false);
+
+ bool bMarkChanged = m_pShell->GetFormView()->checkUnMarkAll(rEvent.Source);
+
+ InterfaceBag aNewSelection;
+ aNewSelection.insert( Reference<XInterface>( xSelObj, UNO_QUERY ) );
+
+ if (setCurrentSelection_Lock(std::move(aNewSelection)) && IsPropBrwOpen_Lock())
+ ShowSelectionProperties_Lock(true);
+
+ EnableTrackProperties_Lock(true);
+
+ if ( bMarkChanged )
+ m_pShell->NotifyMarkListChanged( m_pShell->GetFormView() );
+}
+
+
+IMPL_LINK_NOARG(FmXFormShell, OnTimeOut_Lock, Timer*, void)
+{
+ if (impl_checkDisposed_Lock())
+ return;
+
+ if (m_pShell->IsDesignMode() && m_pShell->GetFormView())
+ SetSelection_Lock(m_pShell->GetFormView()->GetMarkedObjectList());
+}
+
+
+void FmXFormShell::SetSelectionDelayed_Lock()
+{
+ if (impl_checkDisposed_Lock())
+ return;
+
+ if (m_pShell->IsDesignMode() && IsTrackPropertiesEnabled_Lock() && !m_aMarkTimer.IsActive())
+ m_aMarkTimer.Start();
+}
+
+
+void FmXFormShell::SetSelection_Lock(const SdrMarkList& rMarkList)
+{
+ if (impl_checkDisposed_Lock())
+ return;
+
+ DetermineSelection_Lock(rMarkList);
+ m_pShell->NotifyMarkListChanged(m_pShell->GetFormView());
+}
+
+
+void FmXFormShell::DetermineSelection_Lock(const SdrMarkList& rMarkList)
+{
+ if (setCurrentSelectionFromMark_Lock(rMarkList) && IsPropBrwOpen_Lock())
+ ShowSelectionProperties_Lock(true);
+}
+
+
+bool FmXFormShell::IsPropBrwOpen_Lock() const
+{
+ if (impl_checkDisposed_Lock())
+ return false;
+
+ return m_pShell->GetViewShell() && m_pShell->GetViewShell()->GetViewFrame()
+ && m_pShell->GetViewShell()->GetViewFrame()->HasChildWindow(SID_FM_SHOW_PROPERTIES);
+}
+
+
+class FmXFormShell::SuspendPropertyTracking
+{
+private:
+ FmXFormShell& m_rShell;
+ bool m_bEnabled;
+
+public:
+ explicit SuspendPropertyTracking( FmXFormShell& _rShell )
+ :m_rShell( _rShell )
+ ,m_bEnabled( false )
+ {
+ if (m_rShell.IsTrackPropertiesEnabled_Lock())
+ {
+ m_rShell.EnableTrackProperties_Lock(false);
+ m_bEnabled = true;
+ }
+ }
+
+ ~SuspendPropertyTracking( )
+ {
+ if ( m_bEnabled ) // note that ( false != m_bEnabled ) implies ( NULL != m_pShell )
+ m_rShell.EnableTrackProperties_Lock(true);
+ }
+};
+
+
+void FmXFormShell::SetDesignMode_Lock(bool bDesign)
+{
+ if (impl_checkDisposed_Lock())
+ return;
+
+ DBG_ASSERT(m_pShell->GetFormView(), "FmXFormShell::SetDesignMode : invalid call (have no shell or no view) !");
+ m_bChangingDesignMode = true;
+
+ // 67506 - 15.07.99 - FS
+ // if we're switching off the design mode we have to force the property browser to be closed
+ // so it can commit it's changes _before_ we load the forms
+ if (!bDesign)
+ {
+ m_bHadPropertyBrowserInDesignMode = m_pShell->GetViewShell()->GetViewFrame()->HasChildWindow(SID_FM_SHOW_PROPERTIES);
+ if (m_bHadPropertyBrowserInDesignMode)
+ m_pShell->GetViewShell()->GetViewFrame()->ToggleChildWindow(SID_FM_SHOW_PROPERTIES);
+ }
+
+ FmFormView* pFormView = m_pShell->GetFormView();
+ if (bDesign)
+ {
+ // we are currently filtering, so stop filtering
+ if (m_bFilterMode)
+ stopFiltering_Lock(false);
+
+ // unsubscribe from the objects of my MarkList
+ pFormView->GetImpl()->stopMarkListWatching();
+ }
+ else
+ {
+ m_aMarkTimer.Stop();
+
+ SuspendPropertyTracking aSuspend( *this );
+ pFormView->GetImpl()->saveMarkList();
+ }
+
+ if (bDesign && m_xExternalViewController.is())
+ CloseExternalFormViewer_Lock();
+
+ pFormView->ChangeDesignMode(bDesign);
+
+ // notify listeners
+ FmDesignModeChangedHint aChangedHint( bDesign );
+ m_pShell->Broadcast(aChangedHint);
+
+ m_pShell->m_bDesignMode = bDesign;
+ UpdateForms_Lock(false);
+
+ m_pTextShell->designModeChanged();
+
+ if (bDesign)
+ {
+ SdrMarkList aList;
+ {
+ // during changing the mark list, don't track the selected objects in the property browser
+ SuspendPropertyTracking aSuspend( *this );
+ // restore the marks
+ pFormView->GetImpl()->restoreMarkList( aList );
+ }
+
+ // synchronize with the restored mark list
+ if ( aList.GetMarkCount() )
+ SetSelection_Lock(aList);
+ }
+ else
+ {
+ // subscribe to the model of the view (so that I'm informed when someone deletes
+ // during the alive mode controls that I had saved in the saveMarklist (60343)
+ pFormView->GetImpl()->startMarkListWatching();
+ }
+
+ m_pShell->UIFeatureChanged();
+
+ // 67506 - 15.07.99 - FS
+ if (bDesign && m_bHadPropertyBrowserInDesignMode)
+ {
+ // The UIFeatureChanged performs an update (a check of the available features) asynchronously.
+ // So we can't call ShowSelectionProperties directly as the according feature isn't enabled yet.
+ // That's why we use an asynchron execution on the dispatcher.
+ // (And that's why this has to be done AFTER the UIFeatureChanged.)
+ m_pShell->GetViewShell()->GetViewFrame()->GetDispatcher()->Execute( SID_FM_SHOW_PROPERTY_BROWSER, SfxCallMode::ASYNCHRON );
+ }
+ m_bChangingDesignMode = false;
+}
+
+
+Reference< XControl> FmXFormShell::impl_getControl_Lock(const Reference<XControlModel>& i_rxModel, const FmFormObj& i_rKnownFormObj)
+{
+ if (impl_checkDisposed_Lock())
+ return nullptr;
+
+ Reference< XControl > xControl;
+ try
+ {
+ Reference< XControlContainer> xControlContainer(getControlContainerForView_Lock(), UNO_SET_THROW);
+
+ const Sequence< Reference< XControl > > seqControls( xControlContainer->getControls() );
+ // ... that I can then search
+ for (Reference< XControl > const & control : seqControls)
+ {
+ xControl.set( control, UNO_SET_THROW );
+ Reference< XControlModel > xCurrentModel( xControl->getModel() );
+ if ( xCurrentModel == i_rxModel )
+ break;
+ xControl.clear();
+ }
+
+ if ( !xControl.is() )
+ {
+ // fallback (some controls might not have been created, yet, since they were never visible so far)
+ Reference< XControl > xContainerControl( xControlContainer, UNO_QUERY_THROW );
+ const vcl::Window* pContainerWindow = VCLUnoHelper::GetWindow( xContainerControl->getPeer() );
+ ENSURE_OR_THROW( pContainerWindow, "unexpected control container implementation" );
+
+ const SdrView* pSdrView = m_pShell ? m_pShell->GetFormView() : nullptr;
+ ENSURE_OR_THROW( pSdrView, "no current view" );
+
+ xControl.set( i_rKnownFormObj.GetUnoControl( *pSdrView, *pContainerWindow->GetOutDev() ), UNO_SET_THROW );
+ }
+ }
+ catch( const Exception& )
+ {
+ DBG_UNHANDLED_EXCEPTION("svx");
+ }
+
+ OSL_ENSURE( xControl.is(), "FmXFormShell::impl_getControl: no control found!" );
+ return xControl;
+}
+
+// note: _out_rForms is a member so needs lock
+void FmXFormShell::impl_collectFormSearchContexts_nothrow_Lock( const Reference<XInterface>& _rxStartingPoint,
+ std::u16string_view _rCurrentLevelPrefix, FmFormArray& _out_rForms, ::std::vector< OUString >& _out_rNames )
+{
+ try
+ {
+ Reference< XIndexAccess> xContainer( _rxStartingPoint, UNO_QUERY );
+ if ( !xContainer.is() )
+ return;
+
+ sal_Int32 nCount( xContainer->getCount() );
+ if ( nCount == 0 )
+ return;
+
+ OUString sCurrentFormName;
+ OUStringBuffer aNextLevelPrefix;
+ for ( sal_Int32 i=0; i<nCount; ++i )
+ {
+ // is the current child a form?
+ Reference< XForm > xCurrentAsForm( xContainer->getByIndex(i), UNO_QUERY );
+ if ( !xCurrentAsForm.is() )
+ continue;
+
+ Reference< XNamed > xNamed( xCurrentAsForm, UNO_QUERY_THROW );
+ sCurrentFormName = xNamed->getName();
+
+ // the name of the current form
+ OUString sCompleteCurrentName( sCurrentFormName );
+ if ( !_rCurrentLevelPrefix.empty() )
+ {
+ sCompleteCurrentName += OUString::Concat(" (") + _rCurrentLevelPrefix + ")";
+ }
+
+ // the prefix for the next level
+ aNextLevelPrefix = _rCurrentLevelPrefix;
+ if ( !_rCurrentLevelPrefix.empty() )
+ aNextLevelPrefix.append( '/' );
+ aNextLevelPrefix.append( sCurrentFormName );
+
+ // remember both the form and its "display name"
+ _out_rForms.push_back( xCurrentAsForm );
+ _out_rNames.push_back( sCompleteCurrentName );
+
+ // and descend
+ impl_collectFormSearchContexts_nothrow_Lock(
+ xCurrentAsForm, aNextLevelPrefix,
+ _out_rForms, _out_rNames);
+ aNextLevelPrefix.setLength(0);
+ }
+ }
+ catch( const Exception& )
+ {
+ DBG_UNHANDLED_EXCEPTION("svx");
+ }
+}
+
+
+void FmXFormShell::startFiltering_Lock()
+{
+ if (impl_checkDisposed_Lock())
+ return;
+
+ // setting all forms in filter mode
+ FmXFormView* pXView = m_pShell->GetFormView()->GetImpl();
+
+ // if the active controller is our external one we have to use the trigger controller
+ Reference< XControlContainer> xContainer;
+ if (getActiveController_Lock() == m_xExternalViewController)
+ {
+ DBG_ASSERT(m_xExtViewTriggerController.is(), "FmXFormShell::startFiltering : inconsistent : active external controller, but no one triggered this !");
+ xContainer = m_xExtViewTriggerController->getContainer();
+ }
+ else
+ xContainer = getActiveController_Lock()->getContainer();
+
+ rtl::Reference< FormViewPageWindowAdapter > pAdapter = pXView->findWindow( xContainer );
+ if ( pAdapter.is() )
+ {
+ const ::std::vector< Reference< runtime::XFormController> >& rControllerList = pAdapter->GetList();
+ for (const auto& rpController : rControllerList)
+ {
+ Reference< XModeSelector> xModeSelector(rpController, UNO_QUERY);
+ if (xModeSelector.is())
+ xModeSelector->setMode( "FilterMode" );
+ }
+ }
+
+ m_bFilterMode = true;
+
+ m_pShell->UIFeatureChanged();
+ SfxViewFrame* pViewFrame = m_pShell->GetViewShell()->GetViewFrame();
+ pViewFrame->GetBindings().InvalidateShell( *m_pShell );
+
+ if ( pViewFrame->KnowsChildWindow( SID_FM_FILTER_NAVIGATOR )
+ && !pViewFrame->HasChildWindow( SID_FM_FILTER_NAVIGATOR )
+ )
+ {
+ pViewFrame->ToggleChildWindow( SID_FM_FILTER_NAVIGATOR );
+ }
+}
+
+
+static void saveFilter(const Reference< runtime::XFormController >& _rxController)
+{
+ Reference< XPropertySet> xFormAsSet(_rxController->getModel(), UNO_QUERY);
+ Reference< XPropertySet> xControllerAsSet(_rxController, UNO_QUERY);
+
+ // call the subcontroller
+ Reference< runtime::XFormController > xController;
+ for (sal_Int32 i = 0, nCount = _rxController->getCount(); i < nCount; ++i)
+ {
+ _rxController->getByIndex(i) >>= xController;
+ saveFilter(xController);
+ }
+
+ try
+ {
+
+ xFormAsSet->setPropertyValue(FM_PROP_FILTER, xControllerAsSet->getPropertyValue(FM_PROP_FILTER));
+ xFormAsSet->setPropertyValue(FM_PROP_APPLYFILTER, Any( true ) );
+ }
+ catch (const Exception& )
+ {
+ DBG_UNHANDLED_EXCEPTION("svx");
+ }
+
+}
+
+
+void FmXFormShell::stopFiltering_Lock(bool bSave)
+{
+ if (impl_checkDisposed_Lock())
+ return;
+
+ m_bFilterMode = false;
+
+ FmXFormView* pXView = m_pShell->GetFormView()->GetImpl();
+
+ // if the active controller is our external one we have to use the trigger controller
+ Reference< XControlContainer> xContainer;
+ if (getActiveController_Lock() == m_xExternalViewController)
+ {
+ DBG_ASSERT(m_xExtViewTriggerController.is(), "FmXFormShell::stopFiltering : inconsistent : active external controller, but no one triggered this !");
+ xContainer = m_xExtViewTriggerController->getContainer();
+ }
+ else
+ xContainer = getActiveController_Lock()->getContainer();
+
+ rtl::Reference< FormViewPageWindowAdapter > pAdapter = pXView->findWindow(xContainer);
+ if ( pAdapter.is() )
+ {
+ const ::std::vector< Reference< runtime::XFormController > >& rControllerList = pAdapter->GetList();
+ ::std::vector < OUString > aOriginalFilters;
+ ::std::vector < bool > aOriginalApplyFlags;
+
+ if (bSave)
+ {
+ for (const auto& rpController : rControllerList)
+ {
+ // remember the current filter settings in case we're going to reload the forms below (which may fail)
+ try
+ {
+ Reference< XPropertySet > xFormAsSet(rpController->getModel(), UNO_QUERY);
+ aOriginalFilters.push_back(::comphelper::getString(xFormAsSet->getPropertyValue(FM_PROP_FILTER)));
+ aOriginalApplyFlags.push_back(::comphelper::getBOOL(xFormAsSet->getPropertyValue(FM_PROP_APPLYFILTER)));
+ }
+ catch(Exception&)
+ {
+ OSL_FAIL("FmXFormShell::stopFiltering : could not get the original filter !");
+ // put dummies into the arrays so the they have the right size
+
+ if (aOriginalFilters.size() == aOriginalApplyFlags.size())
+ // the first getPropertyValue failed -> use two dummies
+ aOriginalFilters.emplace_back( );
+ aOriginalApplyFlags.push_back( false );
+ }
+ saveFilter(rpController);
+ }
+ }
+ for (const auto& rController : rControllerList)
+ {
+
+ Reference< XModeSelector> xModeSelector(rController, UNO_QUERY);
+ if (xModeSelector.is())
+ xModeSelector->setMode( "DataMode" );
+ }
+ if (bSave) // execute the filter
+ {
+ const ::std::vector< Reference< runtime::XFormController > > & rControllers = pAdapter->GetList();
+ for (::std::vector< Reference< runtime::XFormController > > ::const_iterator j = rControllers.begin();
+ j != rControllers.end(); ++j)
+ {
+ Reference< XLoadable> xReload((*j)->getModel(), UNO_QUERY);
+ if (!xReload.is())
+ continue;
+ Reference< XPropertySet > xFormSet(xReload, UNO_QUERY);
+
+ try
+ {
+ xReload->reload();
+ }
+ catch(Exception&)
+ {
+ TOOLS_WARN_EXCEPTION("svx.form", "");
+ }
+
+ if (!isRowSetAlive(xFormSet))
+ { // something went wrong -> restore the original state
+ OUString sOriginalFilter = aOriginalFilters[ j - rControllers.begin() ];
+ bool bOriginalApplyFlag = aOriginalApplyFlags[ j - rControllers.begin() ];
+ try
+ {
+ xFormSet->setPropertyValue(FM_PROP_FILTER, Any(sOriginalFilter));
+ xFormSet->setPropertyValue(FM_PROP_APPLYFILTER, Any(bOriginalApplyFlag));
+ xReload->reload();
+ }
+ catch(const Exception&)
+ {
+ DBG_UNHANDLED_EXCEPTION("svx");
+ }
+ }
+ }
+ }
+ }
+
+ m_pShell->UIFeatureChanged();
+ m_pShell->GetViewShell()->GetViewFrame()->GetBindings().InvalidateShell(*m_pShell);
+}
+
+
+void FmXFormShell::CreateExternalView_Lock()
+{
+ if (impl_checkDisposed_Lock())
+ return;
+
+ DBG_ASSERT(m_xAttachedFrame.is(), "FmXFormShell::CreateExternalView : no frame !");
+
+ // the frame the external view is displayed in
+ bool bAlreadyExistent = m_xExternalViewController.is();
+ Reference< css::frame::XFrame> xExternalViewFrame;
+
+ Reference<runtime::XFormController> xCurrentNavController(getNavController_Lock());
+ // the creation of the "partwindow" may cause a deactivate of the document which will result in our nav controller to be set to NULL
+
+ // _first_ check if we have any valid fields we can use for the grid view
+ // FS - 21.10.99 - 69219
+ {
+ FmXBoundFormFieldIterator aModelIterator(xCurrentNavController->getModel());
+ bool bHaveUsableControls = false;
+ for (;;)
+ {
+ Reference< XPropertySet> xCurrentModelSet(aModelIterator.Next(), UNO_QUERY);
+ if (!xCurrentModelSet.is())
+ break;
+ // the FmXBoundFormFieldIterator only supplies controls with a valid control source
+ // so we just have to check the field type
+ sal_Int16 nClassId = ::comphelper::getINT16(xCurrentModelSet->getPropertyValue(FM_PROP_CLASSID));
+ switch (nClassId)
+ {
+ case FormComponentType::IMAGECONTROL:
+ case FormComponentType::CONTROL:
+ continue;
+ }
+ bHaveUsableControls = true;
+ break;
+ }
+
+ if (!bHaveUsableControls)
+ {
+ std::unique_ptr<weld::MessageDialog> xBox(Application::CreateMessageDialog(nullptr,
+ VclMessageType::Warning, VclButtonsType::Ok,
+ SvxResId(RID_STR_NOCONTROLS_FOR_EXTERNALDISPLAY)));
+ xBox->run();
+ return;
+ }
+ }
+
+ // load the component for external form views
+ if (!bAlreadyExistent)
+ {
+ OUString sFrameName("_beamer");
+ URL aWantToDispatch;
+ aWantToDispatch.Complete = FMURL_COMPONENT_FORMGRIDVIEW;
+
+ Reference< css::frame::XDispatchProvider> xProv(m_xAttachedFrame, UNO_QUERY);
+ Reference< css::frame::XDispatch> xDisp;
+ if (xProv.is())
+ xDisp = xProv->queryDispatch(aWantToDispatch, sFrameName,
+ css::frame::FrameSearchFlag::CHILDREN | css::frame::FrameSearchFlag::CREATE);
+ if (xDisp.is())
+ {
+ xDisp->dispatch(aWantToDispatch, Sequence< PropertyValue>());
+ }
+
+ // with this the component should be loaded, now search the frame where it resides in
+ xExternalViewFrame = m_xAttachedFrame->findFrame(sFrameName, css::frame::FrameSearchFlag::CHILDREN);
+ if (xExternalViewFrame.is())
+ {
+ m_xExternalViewController = xExternalViewFrame->getController();
+ if (m_xExternalViewController.is())
+ m_xExternalViewController->addEventListener(static_cast<XEventListener*>(static_cast<XPropertyChangeListener*>(this)));
+ }
+ }
+ else
+ {
+ xExternalViewFrame = m_xExternalViewController->getFrame();
+ Reference< css::frame::XDispatchProvider> xCommLink(xExternalViewFrame, UNO_QUERY);
+
+ // if we display the active form we interpret the slot as "remove it"
+ Reference< XForm> xCurrentModel(xCurrentNavController->getModel(), UNO_QUERY);
+ if ((xCurrentModel == m_xExternalDisplayedForm) || (getInternalForm_Lock(xCurrentModel) == m_xExternalDisplayedForm))
+ {
+ if (m_xExternalViewController == getActiveController_Lock())
+ {
+ Reference< runtime::XFormController > xAsFormController( m_xExternalViewController, UNO_QUERY );
+ ControllerFeatures aHelper( xAsFormController );
+ (void)aHelper->commitCurrentControl();
+ }
+
+ Reference< runtime::XFormController > xNewController(m_xExtViewTriggerController);
+ CloseExternalFormViewer_Lock();
+ setActiveController_Lock(xNewController);
+ return;
+ }
+
+ URL aClearURL;
+ aClearURL.Complete = FMURL_GRIDVIEW_CLEARVIEW;
+
+ Reference< css::frame::XDispatch> xClear( xCommLink->queryDispatch(aClearURL, OUString(), 0));
+ if (xClear.is())
+ xClear->dispatch(aClearURL, Sequence< PropertyValue>());
+ }
+
+ // TODO: We need an interceptor at the xSupplier, which forwards all queryDispatch requests to the FormController
+ // instance for which this "external view" was triggered
+
+ // get the dispatch interface of the frame so we can communicate (interceptable) with the controller
+ Reference< css::frame::XDispatchProvider> xCommLink(xExternalViewFrame, UNO_QUERY);
+
+ if (m_xExternalViewController.is())
+ {
+ DBG_ASSERT(xCommLink.is(), "FmXFormShell::CreateExternalView : the component doesn't have the necessary interfaces !");
+ // collect the dispatchers we will need
+ URL aAddColumnURL;
+ aAddColumnURL.Complete = FMURL_GRIDVIEW_ADDCOLUMN;
+ Reference< css::frame::XDispatch> xAddColumnDispatch( xCommLink->queryDispatch(aAddColumnURL, OUString(), 0));
+ URL aAttachURL;
+ aAttachURL.Complete = FMURL_GRIDVIEW_ATTACHTOFORM;
+ Reference< css::frame::XDispatch> xAttachDispatch( xCommLink->queryDispatch(aAttachURL, OUString(), 0));
+
+ if (xAddColumnDispatch.is() && xAttachDispatch.is())
+ {
+ DBG_ASSERT(xCurrentNavController.is(), "FmXFormShell::CreateExternalView : invalid call : have no nav controller !");
+ // first : dispatch the descriptions for the columns to add
+ sal_Int16 nAddedColumns = 0;
+
+ // for radio buttons we need some special structures
+ typedef std::map< OUString, Sequence< OUString> > MapUString2UstringSeq;
+ typedef std::map< OUString, OUString > FmMapUString2UString;
+ typedef std::map< OUString, sal_Int16 > FmMapUString2Int16;
+
+ MapUString2UstringSeq aRadioValueLists;
+ MapUString2UstringSeq aRadioListSources;
+ FmMapUString2UString aRadioControlSources;
+ FmMapUString2Int16 aRadioPositions;
+
+ FmXBoundFormFieldIterator aModelIterator(xCurrentNavController->getModel());
+ OUString sColumnType,aGroupName,sControlSource;
+ Sequence< Property> aProps;
+ for (;;)
+ {
+ Reference< XPropertySet> xCurrentModelSet(aModelIterator.Next(), UNO_QUERY);
+ if (!xCurrentModelSet.is())
+ break;
+ OSL_ENSURE(xCurrentModelSet.is(),"xCurrentModelSet is null!");
+ // create a description of the column to be created
+ // first : determine it's type
+
+ sal_Int16 nClassId = ::comphelper::getINT16(xCurrentModelSet->getPropertyValue(FM_PROP_CLASSID));
+ switch (nClassId)
+ {
+ case FormComponentType::RADIOBUTTON:
+ {
+ // get the label of the button (this is the access key for our structures)
+ aGroupName = getLabelName(xCurrentModelSet);
+
+ // add the reference value of the radio button to the list source sequence
+ Sequence< OUString>& aThisGroupLabels = aRadioListSources[aGroupName];
+ sal_Int32 nNewSizeL = aThisGroupLabels.getLength() + 1;
+ aThisGroupLabels.realloc(nNewSizeL);
+ aThisGroupLabels.getArray()[nNewSizeL - 1] = ::comphelper::getString(xCurrentModelSet->getPropertyValue(FM_PROP_REFVALUE));
+
+ // add the label to the value list sequence
+ Sequence< OUString>& aThisGroupControlSources = aRadioValueLists[aGroupName];
+ sal_Int32 nNewSizeC = aThisGroupControlSources.getLength() + 1;
+ aThisGroupControlSources.realloc(nNewSizeC);
+ aThisGroupControlSources.getArray()[nNewSizeC - 1] = ::comphelper::getString(xCurrentModelSet->getPropertyValue(FM_PROP_LABEL));
+
+ // remember the controls source of the radio group
+ sControlSource = ::comphelper::getString(xCurrentModelSet->getPropertyValue(FM_PROP_CONTROLSOURCE));
+ if (aRadioControlSources.find(aGroupName) == aRadioControlSources.end())
+ aRadioControlSources[aGroupName] = sControlSource;
+#ifdef DBG_UTIL
+ else
+ DBG_ASSERT(aRadioControlSources[aGroupName] == sControlSource,
+ "FmXFormShell::CreateExternalView : inconsistent radio buttons detected !");
+ // (radio buttons with the same name should have the same control source)
+#endif
+ // remember the position within the columns
+ if (aRadioPositions.find(aGroupName) == aRadioPositions.end())
+ aRadioPositions[aGroupName] = nAddedColumns;
+
+ // any further handling is done below
+ }
+ continue;
+
+ case FormComponentType::IMAGECONTROL:
+ case FormComponentType::CONTROL:
+ // no grid columns for these types (though they have a control source)
+ continue;
+ case FormComponentType::CHECKBOX:
+ sColumnType = FM_COL_CHECKBOX; break;
+ case FormComponentType::LISTBOX:
+ sColumnType = FM_COL_LISTBOX; break;
+ case FormComponentType::COMBOBOX:
+ sColumnType = FM_COL_COMBOBOX; break;
+ case FormComponentType::DATEFIELD:
+ sColumnType = FM_COL_DATEFIELD; break;
+ case FormComponentType::TIMEFIELD:
+ sColumnType = FM_COL_TIMEFIELD; break;
+ case FormComponentType::NUMERICFIELD:
+ sColumnType = FM_COL_NUMERICFIELD; break;
+ case FormComponentType::CURRENCYFIELD:
+ sColumnType = FM_COL_CURRENCYFIELD; break;
+ case FormComponentType::PATTERNFIELD:
+ sColumnType = FM_COL_PATTERNFIELD; break;
+
+ case FormComponentType::TEXTFIELD:
+ {
+ sColumnType = FM_COL_TEXTFIELD;
+ // we know at least two different controls which are TextFields : the basic edit field and the formatted
+ // field. we distinguish them by their service name
+ Reference< lang::XServiceInfo> xInfo(xCurrentModelSet, UNO_QUERY);
+ if (xInfo.is())
+ {
+ SdrObjKind nObjectType = getControlTypeByObject(xInfo);
+ if (SdrObjKind::FormFormattedField == nObjectType)
+ sColumnType = FM_COL_FORMATTEDFIELD;
+ }
+ }
+ break;
+ default:
+ sColumnType = FM_COL_TEXTFIELD; break;
+ }
+
+ const sal_Int16 nDispatchArgs = 3;
+ Sequence< PropertyValue> aDispatchArgs(nDispatchArgs);
+ PropertyValue* pDispatchArgs = aDispatchArgs.getArray();
+
+ // properties describing "meta data" about the column
+ // the type
+ pDispatchArgs->Name = FMARG_ADDCOL_COLUMNTYPE;
+ pDispatchArgs->Value <<= sColumnType;
+ ++pDispatchArgs;
+
+ // the pos : append the col
+ pDispatchArgs->Name = FMARG_ADDCOL_COLUMNPOS;
+ pDispatchArgs->Value <<= nAddedColumns;
+ ++pDispatchArgs;
+
+ // the properties to forward to the new column
+ Sequence< PropertyValue> aColumnProps(1);
+ PropertyValue* pColumnProps = aColumnProps.getArray();
+
+ // the label
+ pColumnProps->Name = FM_PROP_LABEL;
+ pColumnProps->Value <<= getLabelName(xCurrentModelSet);
+ ++pColumnProps;
+
+ // for all other props : transfer them
+ Reference< XPropertySetInfo> xControlModelInfo( xCurrentModelSet->getPropertySetInfo());
+ DBG_ASSERT(xControlModelInfo.is(), "FmXFormShell::CreateExternalView : the control model has no property info ! This will crash !");
+ aProps = xControlModelInfo->getProperties();
+
+ // realloc the control description sequence
+ sal_Int32 nExistentDescs = pColumnProps - aColumnProps.getArray();
+ aColumnProps.realloc(nExistentDescs + aProps.getLength());
+ pColumnProps = aColumnProps.getArray() + nExistentDescs;
+
+ for (const Property& rProp : std::as_const(aProps))
+ {
+ if (rProp.Name == FM_PROP_LABEL)
+ // already set
+ continue;
+ if (rProp.Name == FM_PROP_DEFAULTCONTROL)
+ // allow the column's own "default control"
+ continue;
+ if (rProp.Attributes & PropertyAttribute::READONLY)
+ // assume that properties which are readonly for the control are ro for the column to be created, too
+ continue;
+
+ pColumnProps->Name = rProp.Name;
+ pColumnProps->Value = xCurrentModelSet->getPropertyValue(rProp.Name);
+ ++pColumnProps;
+ }
+ aColumnProps.realloc(pColumnProps - aColumnProps.getArray());
+
+ // columns props are a dispatch argument
+ pDispatchArgs->Name = "ColumnProperties"; // TODO : fmurl.*
+ pDispatchArgs->Value <<= aColumnProps;
+ ++pDispatchArgs;
+ DBG_ASSERT(nDispatchArgs == (pDispatchArgs - aDispatchArgs.getConstArray()),
+ "FmXFormShell::CreateExternalView : forgot to adjust nDispatchArgs ?");
+
+ // dispatch the "add column"
+ xAddColumnDispatch->dispatch(aAddColumnURL, aDispatchArgs);
+ ++nAddedColumns;
+ }
+
+ // now for the radio button handling
+ sal_Int16 nOffset(0);
+ // properties describing the "direct" column properties
+ const sal_Int16 nListBoxDescription = 6;
+ Sequence< PropertyValue> aListBoxDescription(nListBoxDescription);
+ for (const auto& rCtrlSource : aRadioControlSources)
+ {
+ PropertyValue* pListBoxDescription = aListBoxDescription.getArray();
+ // label
+ pListBoxDescription->Name = FM_PROP_LABEL;
+ pListBoxDescription->Value <<= rCtrlSource.first;
+ ++pListBoxDescription;
+
+ // control source
+ pListBoxDescription->Name = FM_PROP_CONTROLSOURCE;
+ pListBoxDescription->Value <<= rCtrlSource.second;
+ ++pListBoxDescription;
+
+ // bound column
+ pListBoxDescription->Name = FM_PROP_BOUNDCOLUMN;
+ pListBoxDescription->Value <<= sal_Int16(1);
+ ++pListBoxDescription;
+
+ // content type
+ pListBoxDescription->Name = FM_PROP_LISTSOURCETYPE;
+ pListBoxDescription->Value <<= ListSourceType_VALUELIST;
+ ++pListBoxDescription;
+
+ // list source
+ MapUString2UstringSeq::const_iterator aCurrentListSource = aRadioListSources.find(rCtrlSource.first);
+ DBG_ASSERT(aCurrentListSource != aRadioListSources.end(),
+ "FmXFormShell::CreateExternalView : inconsistent radio descriptions !");
+ pListBoxDescription->Name = FM_PROP_LISTSOURCE;
+ pListBoxDescription->Value <<= (*aCurrentListSource).second;
+ ++pListBoxDescription;
+
+ // value list
+ MapUString2UstringSeq::const_iterator aCurrentValueList = aRadioValueLists.find(rCtrlSource.first);
+ DBG_ASSERT(aCurrentValueList != aRadioValueLists.end(),
+ "FmXFormShell::CreateExternalView : inconsistent radio descriptions !");
+ pListBoxDescription->Name = FM_PROP_STRINGITEMLIST;
+ pListBoxDescription->Value <<= (*aCurrentValueList).second;
+ ++pListBoxDescription;
+
+ DBG_ASSERT(nListBoxDescription == (pListBoxDescription - aListBoxDescription.getConstArray()),
+ "FmXFormShell::CreateExternalView : forgot to adjust nListBoxDescription ?");
+
+ // properties describing the column "meta data"
+ const sal_Int16 nDispatchArgs = 3;
+ Sequence< PropertyValue> aDispatchArgs(nDispatchArgs);
+ PropertyValue* pDispatchArgs = aDispatchArgs.getArray();
+
+ // column type : listbox
+ pDispatchArgs->Name = FMARG_ADDCOL_COLUMNTYPE;
+ pDispatchArgs->Value <<= OUString(FM_COL_LISTBOX);
+// pDispatchArgs->Value <<= (OUString)FM_COL_LISTBOX;
+ ++pDispatchArgs;
+
+ // column position
+ pDispatchArgs->Name = FMARG_ADDCOL_COLUMNPOS;
+ FmMapUString2Int16::const_iterator aOffset = aRadioPositions.find(rCtrlSource.first);
+ DBG_ASSERT(aOffset != aRadioPositions.end(),
+ "FmXFormShell::CreateExternalView : inconsistent radio descriptions !");
+ sal_Int16 nPosition = (*aOffset).second;
+ nPosition = nPosition + nOffset;
+ // we already inserted nOffset additional columns...
+ pDispatchArgs->Value <<= nPosition;
+ ++pDispatchArgs;
+
+ // the
+ pDispatchArgs->Name = "ColumnProperties"; // TODO : fmurl.*
+ pDispatchArgs->Value <<= aListBoxDescription;
+ ++pDispatchArgs;
+ DBG_ASSERT(nDispatchArgs == (pDispatchArgs - aDispatchArgs.getConstArray()),
+ "FmXFormShell::CreateExternalView : forgot to adjust nDispatchArgs ?");
+
+ // dispatch the "add column"
+ xAddColumnDispatch->dispatch(aAddColumnURL, aDispatchArgs);
+ ++nAddedColumns;
+ ++nOffset;
+ }
+
+
+ DBG_ASSERT(nAddedColumns > 0, "FmXFormShell::CreateExternalView : no controls (inconsistent) !");
+ // we should have checked if we have any usable controls (see above).
+
+ // "load" the "form" of the external view
+ PropertyValue aArg;
+ aArg.Name = FMARG_ATTACHTO_MASTERFORM;
+ Reference< XResultSet> xForm(xCurrentNavController->getModel(), UNO_QUERY);
+ aArg.Value <<= xForm;
+
+ m_xExternalDisplayedForm = xForm;
+ // do this before dispatching the "attach" command, as the attach may result in a call to our queryDispatch (for the FormSlots)
+ // which needs the m_xExternalDisplayedForm
+
+ xAttachDispatch->dispatch(aAttachURL, Sequence< PropertyValue>(&aArg, 1));
+
+ m_xExtViewTriggerController = xCurrentNavController;
+
+ // we want to know modifications done in the external view
+ // if the external controller is a XFormController we can use all our default handlings for it
+ Reference< runtime::XFormController > xFormController( m_xExternalViewController, UNO_QUERY );
+ OSL_ENSURE( xFormController.is(), "FmXFormShell::CreateExternalView:: invalid external view controller!" );
+ if (xFormController.is())
+ xFormController->addActivateListener(static_cast<XFormControllerListener*>(this));
+ }
+ }
+#ifdef DBG_UTIL
+ else
+ {
+ OSL_FAIL("FmXFormShell::CreateExternalView : could not create the external form view !");
+ }
+#endif
+ InvalidateSlot_Lock(SID_FM_VIEW_AS_GRID, false);
+}
+
+
+void FmXFormShell::implAdjustConfigCache_Lock()
+{
+ // get (cache) the wizard usage flag
+ Sequence< OUString > aNames { "FormControlPilotsEnabled" };
+ Sequence< Any > aFlags = GetProperties(aNames);
+ if (1 == aFlags.getLength())
+ m_bUseWizards = ::cppu::any2bool(aFlags[0]);
+}
+
+
+void FmXFormShell::Notify( const css::uno::Sequence< OUString >& _rPropertyNames)
+{
+ DBG_TESTSOLARMUTEX();
+ if (impl_checkDisposed_Lock())
+ return;
+
+ for (const OUString& rName : _rPropertyNames)
+ if (rName == "FormControlPilotsEnabled")
+ {
+ implAdjustConfigCache_Lock();
+ InvalidateSlot_Lock(SID_FM_USE_WIZARDS, true);
+ }
+}
+
+void FmXFormShell::ImplCommit()
+{
+}
+
+
+void FmXFormShell::SetWizardUsing_Lock(bool _bUseThem)
+{
+ m_bUseWizards = _bUseThem;
+
+ Sequence< OUString > aNames { "FormControlPilotsEnabled" };
+ Sequence< Any > aValues{ Any(m_bUseWizards) };
+ PutProperties(aNames, aValues);
+}
+
+
+void FmXFormShell::viewDeactivated_Lock(FmFormView& _rCurrentView, bool _bDeactivateController)
+{
+
+ if ( _rCurrentView.GetImpl() && !_rCurrentView.IsDesignMode() )
+ {
+ _rCurrentView.GetImpl()->Deactivate( _bDeactivateController );
+ }
+
+ // if we have an async load operation pending for the 0-th page for this view,
+ // we need to cancel this
+ if (FmFormPage* pPage = _rCurrentView.GetCurPage())
+ {
+ // move all events from our queue to a new one, omit the events for the deactivated
+ // page
+ ::std::queue< FmLoadAction > aNewEvents;
+ while ( !m_aLoadingPages.empty() )
+ {
+ FmLoadAction aAction = m_aLoadingPages.front();
+ m_aLoadingPages.pop();
+ if ( pPage != aAction.pPage )
+ {
+ aNewEvents.push( aAction );
+ }
+ else
+ {
+ Application::RemoveUserEvent( aAction.nEventId );
+ }
+ }
+ m_aLoadingPages = aNewEvents;
+
+ // remove callbacks at the page
+ pPage->GetImpl().SetFormsCreationHdl( Link<FmFormPageImpl&,void>() );
+ }
+ UpdateForms_Lock(true);
+}
+
+
+IMPL_LINK_NOARG( FmXFormShell, OnFirstTimeActivation_Lock, void*, void )
+{
+ if (impl_checkDisposed_Lock())
+ return;
+
+ m_nActivationEvent = nullptr;
+ SfxObjectShell* pDocument = m_pShell->GetObjectShell();
+
+ if ( pDocument && !pDocument->HasName() )
+ {
+ if (isEnhancedForm_Lock())
+ {
+ // show the data navigator
+ if ( !m_pShell->GetViewShell()->GetViewFrame()->HasChildWindow( SID_FM_SHOW_DATANAVIGATOR ) )
+ m_pShell->GetViewShell()->GetViewFrame()->ToggleChildWindow( SID_FM_SHOW_DATANAVIGATOR );
+ }
+ }
+}
+
+
+IMPL_LINK_NOARG( FmXFormShell, OnFormsCreated_Lock, FmFormPageImpl&, void )
+{
+ UpdateForms_Lock(true);
+}
+
+
+void FmXFormShell::viewActivated_Lock(FmFormView& _rCurrentView, bool _bSyncAction)
+{
+ FmFormPage* pPage = _rCurrentView.GetCurPage();
+
+ // activate our view if we are activated ourself
+ // FS - 30.06.99 - 67308
+ if ( _rCurrentView.GetImpl() && !_rCurrentView.IsDesignMode() )
+ {
+ // load forms for the page the current view belongs to
+ if ( pPage )
+ {
+ if ( !pPage->GetImpl().hasEverBeenActivated() )
+ loadForms_Lock(pPage, LoadFormsFlags::Load
+ | (_bSyncAction ? LoadFormsFlags::Sync
+ : LoadFormsFlags::Async));
+ pPage->GetImpl().setHasBeenActivated( );
+ }
+
+ // first-time initializations for the views
+ if ( !_rCurrentView.GetImpl()->hasEverBeenActivated( ) )
+ {
+ _rCurrentView.GetImpl()->onFirstViewActivation( dynamic_cast<FmFormModel*>( _rCurrentView.GetModel() ) );
+ _rCurrentView.GetImpl()->setHasBeenActivated( );
+ }
+
+ // activate the current view
+ _rCurrentView.GetImpl()->Activate( _bSyncAction );
+ }
+
+ // set callbacks at the page
+ if ( pPage )
+ {
+ pPage->GetImpl().SetFormsCreationHdl(LINK(this, FmXFormShell, OnFormsCreated_Lock));
+ }
+
+ UpdateForms_Lock(true);
+
+ if ( m_bFirstActivation )
+ {
+ m_nActivationEvent = Application::PostUserEvent(LINK(this, FmXFormShell, OnFirstTimeActivation_Lock));
+ m_bFirstActivation = false;
+ }
+
+ // find a default "current form", if there is none, yet
+ // #i88186# / 2008-04-12 / frank.schoenheit@sun.com
+ impl_defaultCurrentForm_nothrow_Lock();
+}
+
+
+void FmXFormShell::impl_defaultCurrentForm_nothrow_Lock()
+{
+ if (impl_checkDisposed_Lock())
+ return;
+
+ if ( m_xCurrentForm.is() )
+ // no action required
+ return;
+
+ FmFormView* pFormView = m_pShell->GetFormView();
+ FmFormPage* pPage = pFormView ? pFormView->GetCurPage() : nullptr;
+ if ( !pPage )
+ return;
+
+ try
+ {
+ Reference< XIndexAccess > xForms = pPage->GetForms( false );
+ if ( !xForms.is() || !xForms->hasElements() )
+ return;
+
+ Reference< XForm > xNewCurrentForm( xForms->getByIndex(0), UNO_QUERY_THROW );
+ impl_updateCurrentForm_Lock(xNewCurrentForm);
+ }
+ catch( const Exception& )
+ {
+ DBG_UNHANDLED_EXCEPTION("svx");
+ }
+}
+
+
+void FmXFormShell::smartControlReset( const Reference< XIndexAccess >& _rxModels )
+{
+ if (!_rxModels.is())
+ {
+ OSL_FAIL("FmXFormShell::smartControlReset: invalid container!");
+ return;
+ }
+
+ sal_Int32 nCount = _rxModels->getCount();
+ Reference< XPropertySet > xCurrent;
+ Reference< XPropertySetInfo > xCurrentInfo;
+ Reference< XPropertySet > xBoundField;
+
+ for (sal_Int32 i=0; i<nCount; ++i)
+ {
+ _rxModels->getByIndex(i) >>= xCurrent;
+ if (xCurrent.is())
+ xCurrentInfo = xCurrent->getPropertySetInfo();
+ else
+ xCurrentInfo.clear();
+ if (!xCurrentInfo.is())
+ continue;
+
+ if (xCurrentInfo->hasPropertyByName(FM_PROP_CLASSID))
+ { // it's a control model
+
+ // check if this control is bound to a living database field
+ if (xCurrentInfo->hasPropertyByName(FM_PROP_BOUNDFIELD))
+ xCurrent->getPropertyValue(FM_PROP_BOUNDFIELD) >>= xBoundField;
+ else
+ xBoundField.clear();
+
+ // reset only if it's *not* bound
+ bool bReset = !xBoundField.is();
+
+ // and additionally, check if it has an external value binding
+ Reference< XBindableValue > xBindable( xCurrent, UNO_QUERY );
+ if ( xBindable.is() && xBindable->getValueBinding().is() )
+ bReset = false;
+
+ if ( bReset )
+ {
+ Reference< XReset > xControlReset( xCurrent, UNO_QUERY );
+ if ( xControlReset.is() )
+ xControlReset->reset();
+ }
+ }
+ else
+ {
+ Reference< XIndexAccess > xContainer(xCurrent, UNO_QUERY);
+ if (xContainer.is())
+ smartControlReset(xContainer);
+ }
+ }
+}
+
+
+IMPL_LINK_NOARG( FmXFormShell, OnLoadForms_Lock, void*, void )
+{
+ FmLoadAction aAction = m_aLoadingPages.front();
+ m_aLoadingPages.pop();
+
+ loadForms_Lock(aAction.pPage, aAction.nFlags & ~LoadFormsFlags::Async);
+}
+
+
+namespace
+{
+ bool lcl_isLoadable( const Reference< XInterface >& _rxLoadable )
+ {
+ // determines whether a form should be loaded or not
+ // if there is no datasource or connection there is no reason to load a form
+ Reference< XPropertySet > xSet( _rxLoadable, UNO_QUERY );
+ if ( !xSet.is() )
+ return false;
+ try
+ {
+ Reference< XConnection > xConn;
+ if ( isEmbeddedInDatabase( _rxLoadable, xConn ) )
+ return true;
+
+ // is there already an active connection
+ xSet->getPropertyValue(FM_PROP_ACTIVE_CONNECTION) >>= xConn;
+ if ( xConn.is() )
+ return true;
+
+ OUString sPropertyValue;
+ OSL_VERIFY( xSet->getPropertyValue( FM_PROP_DATASOURCE ) >>= sPropertyValue );
+ if ( !sPropertyValue.isEmpty() )
+ return true;
+
+ OSL_VERIFY( xSet->getPropertyValue( FM_PROP_URL ) >>= sPropertyValue );
+ if ( !sPropertyValue.isEmpty() )
+ return true;
+ }
+ catch(const Exception&)
+ {
+ DBG_UNHANDLED_EXCEPTION("svx");
+ }
+ return false;
+ }
+}
+
+
+void FmXFormShell::loadForms_Lock(FmFormPage* _pPage, const LoadFormsFlags _nBehaviour /* LoadFormsFlags::Load | LoadFormsFlags::Sync */)
+{
+ DBG_ASSERT( ( _nBehaviour & ( LoadFormsFlags::Async | LoadFormsFlags::Unload ) ) != ( LoadFormsFlags::Async | LoadFormsFlags::Unload ),
+ "FmXFormShell::loadForms: async loading not supported - this will heavily fail!" );
+
+ if ( _nBehaviour & LoadFormsFlags::Async )
+ {
+ m_aLoadingPages.push( FmLoadAction(
+ _pPage,
+ _nBehaviour,
+ Application::PostUserEvent(LINK(this, FmXFormShell, OnLoadForms_Lock), _pPage)
+ ) );
+ return;
+ }
+
+ DBG_ASSERT( _pPage, "FmXFormShell::loadForms: invalid page!" );
+ if ( !_pPage )
+ return;
+
+ // lock the undo env so the forms can change non-transient properties while loading
+ // (without this my doc's modified flag would be set)
+ FmFormModel& rFmFormModel(dynamic_cast< FmFormModel& >(_pPage->getSdrModelFromSdrPage()));
+ rFmFormModel.GetUndoEnv().Lock();
+
+ // load all forms
+ Reference< XIndexAccess > xForms = _pPage->GetForms( false );
+
+ if ( xForms.is() )
+ {
+ Reference< XLoadable > xForm;
+ for ( sal_Int32 j = 0, nCount = xForms->getCount(); j < nCount; ++j )
+ {
+ xForms->getByIndex( j ) >>= xForm;
+ bool bFormWasLoaded = false;
+ // a database form must be loaded for
+ try
+ {
+ if ( !( _nBehaviour & LoadFormsFlags::Unload ) )
+ {
+ if ( lcl_isLoadable( xForm ) && !xForm->isLoaded() )
+ xForm->load();
+ }
+ else
+ {
+ if ( xForm->isLoaded() )
+ {
+ bFormWasLoaded = true;
+ xForm->unload();
+ }
+ }
+ }
+ catch( const Exception& )
+ {
+ DBG_UNHANDLED_EXCEPTION("svx");
+ }
+
+ // reset the form if it was loaded
+ if ( bFormWasLoaded )
+ {
+ Reference< XIndexAccess > xContainer( xForm, UNO_QUERY );
+ DBG_ASSERT( xContainer.is(), "FmXFormShell::loadForms: the form is no container!" );
+ if ( xContainer.is() )
+ smartControlReset( xContainer );
+ }
+ }
+ }
+
+ // unlock the environment
+ rFmFormModel.GetUndoEnv().UnLock();
+}
+
+
+void FmXFormShell::ExecuteTextAttribute_Lock(SfxRequest& _rReq)
+{
+ DBG_TESTSOLARMUTEX();
+ m_pTextShell->ExecuteTextAttribute( _rReq );
+}
+
+
+void FmXFormShell::GetTextAttributeState_Lock(SfxItemSet& _rSet)
+{
+ DBG_TESTSOLARMUTEX();
+ m_pTextShell->GetTextAttributeState( _rSet );
+}
+
+
+bool FmXFormShell::IsActiveControl_Lock(bool _bCountRichTextOnly ) const
+{
+ DBG_TESTSOLARMUTEX();
+ return m_pTextShell->IsActiveControl( _bCountRichTextOnly );
+}
+
+
+void FmXFormShell::ForgetActiveControl_Lock()
+{
+ DBG_TESTSOLARMUTEX();
+ m_pTextShell->ForgetActiveControl();
+}
+
+
+void FmXFormShell::SetControlActivationHandler_Lock(const Link<LinkParamNone*,void>& _rHdl)
+{
+ DBG_TESTSOLARMUTEX();
+ m_pTextShell->SetControlActivationHandler( _rHdl );
+}
+
+void FmXFormShell::handleShowPropertiesRequest_Lock()
+{
+ if (onlyControlsAreMarked_Lock())
+ ShowSelectionProperties_Lock( true );
+}
+
+
+void FmXFormShell::handleMouseButtonDown_Lock(const SdrViewEvent& _rViewEvent)
+{
+ // catch simple double clicks
+ if (_rViewEvent.mnMouseClicks == 2 && _rViewEvent.mnMouseCode == MOUSE_LEFT)
+ {
+ if ( _rViewEvent.meHit == SdrHitKind::MarkedObject )
+ {
+ if (onlyControlsAreMarked_Lock())
+ ShowSelectionProperties_Lock( true );
+ }
+ }
+}
+
+
+bool FmXFormShell::HasControlFocus_Lock() const
+{
+ bool bHasControlFocus = false;
+
+ try
+ {
+ Reference<runtime::XFormController> xController(getActiveController_Lock());
+ Reference< XControl > xCurrentControl;
+ if ( xController.is() )
+ xCurrentControl.set( xController->getCurrentControl() );
+ if ( xCurrentControl.is() )
+ {
+ Reference< XWindow2 > xPeerWindow( xCurrentControl->getPeer(), UNO_QUERY_THROW );
+ bHasControlFocus = xPeerWindow->hasFocus();
+ }
+ }
+ catch( const Exception& )
+ {
+ DBG_UNHANDLED_EXCEPTION("svx");
+ }
+
+ return bHasControlFocus;
+}
+
+
+SearchableControlIterator::SearchableControlIterator(Reference< XInterface> const & xStartingPoint)
+ :IndexAccessIterator(xStartingPoint)
+{
+}
+
+
+bool SearchableControlIterator::ShouldHandleElement(const Reference< XInterface>& xElement)
+{
+ // if the thing has a ControlSource and a BoundField property
+ Reference< XPropertySet> xProperties(xElement, UNO_QUERY);
+ if (::comphelper::hasProperty(FM_PROP_CONTROLSOURCE, xProperties) && ::comphelper::hasProperty(FM_PROP_BOUNDFIELD, xProperties))
+ {
+ // and the BoundField is valid
+ Reference< XPropertySet> xField;
+ xProperties->getPropertyValue(FM_PROP_BOUNDFIELD) >>= xField;
+ if (xField.is())
+ {
+ // we take it
+ m_sCurrentValue = ::comphelper::getString(xProperties->getPropertyValue(FM_PROP_CONTROLSOURCE));
+ return true;
+ }
+ }
+
+ // if it is a grid control
+ if (::comphelper::hasProperty(FM_PROP_CLASSID, xProperties))
+ {
+ Any aClassId( xProperties->getPropertyValue(FM_PROP_CLASSID) );
+ if (::comphelper::getINT16(aClassId) == FormComponentType::GRIDCONTROL)
+ {
+ m_sCurrentValue.clear();
+ return true;
+ }
+ }
+
+ return false;
+}
+
+
+bool SearchableControlIterator::ShouldStepInto(const Reference< XInterface>& /*xContainer*/) const
+{
+ return true;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/form/fmsrccfg.cxx b/svx/source/form/fmsrccfg.cxx
new file mode 100644
index 000000000..499d2cb16
--- /dev/null
+++ b/svx/source/form/fmsrccfg.cxx
@@ -0,0 +1,286 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * 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/fmsrccfg.hxx>
+
+#include <sal/log.hxx>
+#include <comphelper/processfactory.hxx>
+#include <i18nutil/transliteration.hxx>
+
+using namespace ::com::sun::star::uno;
+
+namespace svxform
+{
+ // search parameters
+
+ FmSearchParams::FmSearchParams()
+ :nTransliterationFlags( TransliterationFlags::NONE )
+ ,nSearchForType ( 0 )
+ ,nPosition ( MATCHING_ANYWHERE )
+ ,nLevOther ( 2 )
+ ,nLevShorter ( 2 )
+ ,nLevLonger ( 2 )
+ ,bLevRelaxed ( true )
+ ,bAllFields ( false )
+ ,bUseFormatter ( true )
+ ,bBackwards ( false )
+ ,bWildcard ( false )
+ ,bRegular ( false )
+ ,bApproxSearch ( false )
+ ,bSoundsLikeCJK ( false )
+ {
+ nTransliterationFlags =
+ TransliterationFlags::ignoreSpace_ja_JP
+ | TransliterationFlags::ignoreMiddleDot_ja_JP
+ | TransliterationFlags::ignoreProlongedSoundMark_ja_JP
+ | TransliterationFlags::ignoreSeparator_ja_JP
+ | TransliterationFlags::IGNORE_CASE;
+ }
+
+ bool FmSearchParams::isIgnoreWidthCJK( ) const
+ {
+ return bool(nTransliterationFlags & TransliterationFlags::IGNORE_WIDTH);
+ }
+
+ bool FmSearchParams::isCaseSensitive( ) const
+ {
+ return !(nTransliterationFlags & TransliterationFlags::IGNORE_CASE);
+ }
+
+ void FmSearchParams::setCaseSensitive( bool _bCase )
+ {
+ if ( _bCase )
+ nTransliterationFlags &= ~TransliterationFlags::IGNORE_CASE;
+ else
+ nTransliterationFlags |= TransliterationFlags::IGNORE_CASE;
+ }
+
+ // maps from ascii values to int values
+
+ namespace {
+
+ struct Ascii2Int16
+ {
+ const char* pAscii;
+ sal_Int16 nValue;
+ };
+
+ }
+
+ static const Ascii2Int16* lcl_getSearchForTypeValueMap()
+ {
+ static const Ascii2Int16 s_aSearchForTypeMap[] =
+ {
+ { "text", 0 },
+ { "null", 1 },
+ { "non-null", 2 },
+ { nullptr, -1 }
+ };
+ return s_aSearchForTypeMap;
+ }
+
+ static const Ascii2Int16* lcl_getSearchPositionValueMap()
+ {
+ static const Ascii2Int16 s_aSearchPositionMap[] =
+ {
+ { "anywhere-in-field", MATCHING_ANYWHERE },
+ { "beginning-of-field", MATCHING_BEGINNING },
+ { "end-of-field", MATCHING_END },
+ { "complete-field", MATCHING_WHOLETEXT },
+ { nullptr, -1 }
+ };
+ return s_aSearchPositionMap;
+ }
+
+ static sal_Int16 lcl_implMapAsciiValue( const OUString& _rAsciiValue, const Ascii2Int16* _pMap )
+ {
+ // search the map for the given ascii value
+ const Ascii2Int16* pSearch = _pMap;
+ while ( pSearch && pSearch->pAscii )
+ {
+ if ( _rAsciiValue.equalsAscii( pSearch->pAscii ) )
+ // found
+ return pSearch->nValue;
+ ++pSearch;
+ }
+
+ SAL_WARN(
+ "svx.form", "could not convert the ascii value " << _rAsciiValue);
+ return -1;
+ }
+
+ static const char* lcl_implMapIntValue( const sal_Int16 _nValue, const Ascii2Int16* _pMap )
+ {
+ // search the map for the given integer value
+ const Ascii2Int16* pSearch = _pMap;
+ while ( pSearch && pSearch->pAscii )
+ {
+ if ( _nValue == pSearch->nValue )
+ // found
+ return pSearch->pAscii;
+ ++pSearch;
+ }
+
+ SAL_WARN( "svx", "lcl_implMapIntValue: could not convert the integer value "
+ << _nValue << " !");
+ static const char* const s_pDummy = "";
+ // just as a fallback...
+ return s_pDummy;
+ }
+
+ // class FmSearchConfigItem - a config item that stores search parameters
+
+#define TA( c ) &c, cppu::UnoType<decltype(c)>::get()
+
+ FmSearchConfigItem::FmSearchConfigItem()
+ :OConfigurationValueContainer( ::comphelper::getProcessComponentContext(), m_aMutex, "/org.openoffice.Office.DataAccess/FormSearchOptions", 2 )
+ {
+ // register our members so the data exchange with the node values is done automatically
+
+ registerExchangeLocation( "SearchHistory", TA( aHistory ) );
+ registerExchangeLocation( "LevenshteinOther", TA( nLevOther ) );
+ registerExchangeLocation( "LevenshteinShorter", TA( nLevShorter ) );
+ registerExchangeLocation( "LevenshteinLonger", TA( nLevLonger ) );
+ registerExchangeLocation( "IsLevenshteinRelaxed", TA( bLevRelaxed ) );
+ registerExchangeLocation( "IsSearchAllFields", TA( bAllFields ) );
+ registerExchangeLocation( "IsUseFormatter", TA( bUseFormatter ) );
+ registerExchangeLocation( "IsBackwards", TA( bBackwards ) );
+ registerExchangeLocation( "IsWildcardSearch", TA( bWildcard ) );
+ registerExchangeLocation( "IsUseRegularExpression", TA( bRegular ) );
+ registerExchangeLocation( "IsSimilaritySearch", TA( bApproxSearch ) );
+ registerExchangeLocation( "IsUseAsianOptions", TA( bSoundsLikeCJK ) );
+
+ // the properties which need to be translated
+ registerExchangeLocation( "SearchType", TA( m_sSearchForType ) );
+ registerExchangeLocation( "SearchPosition", TA( m_sSearchPosition ) );
+
+ registerExchangeLocation( "IsMatchCase", TA( m_bIsMatchCase ) );
+ registerExchangeLocation( "Japanese/IsMatchFullHalfWidthForms", TA( m_bIsMatchFullHalfWidthForms ) );
+ registerExchangeLocation( "Japanese/IsMatchHiraganaKatakana", TA( m_bIsMatchHiraganaKatakana ) );
+ registerExchangeLocation( "Japanese/IsMatchContractions", TA( m_bIsMatchContractions ) );
+ registerExchangeLocation( "Japanese/IsMatchMinusDashCho-on", TA( m_bIsMatchMinusDashCho_on ) );
+ registerExchangeLocation( "Japanese/IsMatchRepeatCharMarks", TA( m_bIsMatchRepeatCharMarks ) );
+ registerExchangeLocation( "Japanese/IsMatchVariantFormKanji", TA( m_bIsMatchVariantFormKanji ) );
+ registerExchangeLocation( "Japanese/IsMatchOldKanaForms", TA( m_bIsMatchOldKanaForms ) );
+ registerExchangeLocation( "Japanese/IsMatch_DiZi_DuZu", TA( m_bIsMatch_DiZi_DuZu ) );
+ registerExchangeLocation( "Japanese/IsMatch_BaVa_HaFa", TA( m_bIsMatch_BaVa_HaFa ) );
+ registerExchangeLocation( "Japanese/IsMatch_TsiThiChi_DhiZi", TA( m_bIsMatch_TsiThiChi_DhiZi ) );
+ registerExchangeLocation( "Japanese/IsMatch_HyuIyu_ByuVyu", TA( m_bIsMatch_HyuIyu_ByuVyu ) );
+ registerExchangeLocation( "Japanese/IsMatch_SeShe_ZeJe", TA( m_bIsMatch_SeShe_ZeJe ) );
+ registerExchangeLocation( "Japanese/IsMatch_IaIya", TA( m_bIsMatch_IaIya ) );
+ registerExchangeLocation( "Japanese/IsMatch_KiKu", TA( m_bIsMatch_KiKu ) );
+ registerExchangeLocation( "Japanese/IsIgnorePunctuation", TA( m_bIsIgnorePunctuation ) );
+ registerExchangeLocation( "Japanese/IsIgnoreWhitespace", TA( m_bIsIgnoreWhitespace ) );
+ registerExchangeLocation( "Japanese/IsIgnoreProlongedSoundMark",TA( m_bIsIgnoreProlongedSoundMark ) );
+ registerExchangeLocation( "Japanese/IsIgnoreMiddleDot", TA( m_bIsIgnoreMiddleDot ) );
+
+ read( );
+ }
+
+ FmSearchConfigItem::~FmSearchConfigItem()
+ {
+ commit( );
+ }
+
+ void FmSearchConfigItem::implTranslateFromConfig( )
+ {
+ // the search-for string
+ nSearchForType = lcl_implMapAsciiValue( m_sSearchForType, lcl_getSearchForTypeValueMap() );
+
+ // the search position
+ nPosition = lcl_implMapAsciiValue( m_sSearchPosition, lcl_getSearchPositionValueMap() );
+
+ // the transliteration flags
+ nTransliterationFlags = TransliterationFlags::NONE;
+
+ if ( !m_bIsMatchCase ) nTransliterationFlags |= TransliterationFlags::IGNORE_CASE;
+ if ( m_bIsMatchFullHalfWidthForms ) nTransliterationFlags |= TransliterationFlags::IGNORE_WIDTH;
+ if ( m_bIsMatchHiraganaKatakana ) nTransliterationFlags |= TransliterationFlags::IGNORE_KANA;
+ if ( m_bIsMatchContractions ) nTransliterationFlags |= TransliterationFlags::ignoreSize_ja_JP;
+ if ( m_bIsMatchMinusDashCho_on ) nTransliterationFlags |= TransliterationFlags::ignoreMinusSign_ja_JP;
+ if ( m_bIsMatchRepeatCharMarks ) nTransliterationFlags |= TransliterationFlags::ignoreIterationMark_ja_JP;
+ if ( m_bIsMatchVariantFormKanji ) nTransliterationFlags |= TransliterationFlags::ignoreTraditionalKanji_ja_JP;
+ if ( m_bIsMatchOldKanaForms ) nTransliterationFlags |= TransliterationFlags::ignoreTraditionalKana_ja_JP;
+ if ( m_bIsMatch_DiZi_DuZu ) nTransliterationFlags |= TransliterationFlags::ignoreZiZu_ja_JP;
+ if ( m_bIsMatch_BaVa_HaFa ) nTransliterationFlags |= TransliterationFlags::ignoreBaFa_ja_JP;
+ if ( m_bIsMatch_TsiThiChi_DhiZi ) nTransliterationFlags |= TransliterationFlags::ignoreTiJi_ja_JP;
+ if ( m_bIsMatch_HyuIyu_ByuVyu ) nTransliterationFlags |= TransliterationFlags::ignoreHyuByu_ja_JP;
+ if ( m_bIsMatch_SeShe_ZeJe ) nTransliterationFlags |= TransliterationFlags::ignoreSeZe_ja_JP;
+ if ( m_bIsMatch_IaIya ) nTransliterationFlags |= TransliterationFlags::ignoreIandEfollowedByYa_ja_JP;
+ if ( m_bIsMatch_KiKu ) nTransliterationFlags |= TransliterationFlags::ignoreKiKuFollowedBySa_ja_JP;
+
+ if ( m_bIsIgnorePunctuation ) nTransliterationFlags |= TransliterationFlags::ignoreSeparator_ja_JP;
+ if ( m_bIsIgnoreWhitespace ) nTransliterationFlags |= TransliterationFlags::ignoreSpace_ja_JP;
+ if ( m_bIsIgnoreProlongedSoundMark ) nTransliterationFlags |= TransliterationFlags::ignoreProlongedSoundMark_ja_JP;
+ if ( m_bIsIgnoreMiddleDot ) nTransliterationFlags |= TransliterationFlags::ignoreMiddleDot_ja_JP;
+ }
+
+ void FmSearchConfigItem::implTranslateToConfig( )
+ {
+ // the search-for string
+ m_sSearchForType = OUString::createFromAscii( lcl_implMapIntValue( nSearchForType, lcl_getSearchForTypeValueMap() ) );
+
+ // the search position
+ m_sSearchPosition = OUString::createFromAscii( lcl_implMapIntValue( nPosition, lcl_getSearchPositionValueMap() ) );
+
+ // the transliteration flags
+
+ m_bIsMatchCase = !( nTransliterationFlags & TransliterationFlags::IGNORE_CASE );
+ m_bIsMatchFullHalfWidthForms = bool( nTransliterationFlags & TransliterationFlags::IGNORE_WIDTH );
+ m_bIsMatchHiraganaKatakana = bool( nTransliterationFlags & TransliterationFlags::IGNORE_KANA );
+ m_bIsMatchContractions = bool( nTransliterationFlags & TransliterationFlags::ignoreSize_ja_JP );
+ m_bIsMatchMinusDashCho_on = bool( nTransliterationFlags & TransliterationFlags::ignoreMinusSign_ja_JP );
+ m_bIsMatchRepeatCharMarks = bool( nTransliterationFlags & TransliterationFlags::ignoreIterationMark_ja_JP );
+ m_bIsMatchVariantFormKanji = bool( nTransliterationFlags & TransliterationFlags::ignoreTraditionalKanji_ja_JP );
+ m_bIsMatchOldKanaForms = bool( nTransliterationFlags & TransliterationFlags::ignoreTraditionalKana_ja_JP );
+ m_bIsMatch_DiZi_DuZu = bool( nTransliterationFlags & TransliterationFlags::ignoreZiZu_ja_JP );
+ m_bIsMatch_BaVa_HaFa = bool( nTransliterationFlags & TransliterationFlags::ignoreBaFa_ja_JP );
+ m_bIsMatch_TsiThiChi_DhiZi = bool( nTransliterationFlags & TransliterationFlags::ignoreTiJi_ja_JP );
+ m_bIsMatch_HyuIyu_ByuVyu = bool( nTransliterationFlags & TransliterationFlags::ignoreHyuByu_ja_JP );
+ m_bIsMatch_SeShe_ZeJe = bool( nTransliterationFlags & TransliterationFlags::ignoreSeZe_ja_JP );
+ m_bIsMatch_IaIya = bool( nTransliterationFlags & TransliterationFlags::ignoreIandEfollowedByYa_ja_JP );
+ m_bIsMatch_KiKu = bool( nTransliterationFlags & TransliterationFlags::ignoreKiKuFollowedBySa_ja_JP );
+
+ m_bIsIgnorePunctuation = bool( nTransliterationFlags & TransliterationFlags::ignoreSeparator_ja_JP );
+ m_bIsIgnoreWhitespace = bool( nTransliterationFlags & TransliterationFlags::ignoreSpace_ja_JP );
+ m_bIsIgnoreProlongedSoundMark = bool( nTransliterationFlags & TransliterationFlags::ignoreProlongedSoundMark_ja_JP );
+ m_bIsIgnoreMiddleDot = bool( nTransliterationFlags & TransliterationFlags::ignoreMiddleDot_ja_JP );
+ }
+
+ const FmSearchParams& FmSearchConfigItem::getParams() const
+ {
+ // ensure that the properties which are not stored directly are up-to-date
+ const_cast< FmSearchConfigItem* >( this )->implTranslateFromConfig( );
+
+ // and return our FmSearchParams part
+ return *this;
+ }
+
+ void FmSearchConfigItem::setParams( const FmSearchParams& _rParams )
+ {
+ // copy the FmSearchParams part
+ *static_cast< FmSearchParams* >( this ) = _rParams;
+
+ // translate the settings not represented by a direct config value
+ implTranslateToConfig();
+ }
+
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/form/fmsrcimp.cxx b/svx/source/form/fmsrcimp.cxx
new file mode 100644
index 000000000..6ebd99c93
--- /dev/null
+++ b/svx/source/form/fmsrcimp.cxx
@@ -0,0 +1,1058 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <sal/config.h>
+
+#include <o3tl/safeint.hxx>
+#include <o3tl/string_view.hxx>
+#include <svx/fmtools.hxx>
+#include <svx/fmsrccfg.hxx>
+#include <tools/debug.hxx>
+#include <tools/diagnose_ex.h>
+#include <tools/wldcrd.hxx>
+#include <vcl/svapp.hxx>
+#include <unotools/textsearch.hxx>
+#include <com/sun/star/awt/XTextComponent.hpp>
+#include <com/sun/star/awt/XListBox.hpp>
+#include <com/sun/star/awt/XCheckBox.hpp>
+#include <com/sun/star/beans/XPropertySet.hpp>
+#include <com/sun/star/container/XIndexAccess.hpp>
+#include <com/sun/star/util/SearchAlgorithms2.hpp>
+#include <com/sun/star/util/SearchFlags.hpp>
+#include <com/sun/star/lang/Locale.hpp>
+#include <com/sun/star/i18n/CollatorOptions.hpp>
+
+#include <com/sun/star/sdb/XColumn.hpp>
+#include <com/sun/star/sdbc/XConnection.hpp>
+#include <com/sun/star/sdbc/XDatabaseMetaData.hpp>
+#include <com/sun/star/sdbcx/XColumnsSupplier.hpp>
+
+#include <fmprop.hxx>
+#include <svx/fmsrcimp.hxx>
+
+#include <comphelper/types.hxx>
+#include <unotools/syslocale.hxx>
+#include <i18nutil/searchopt.hxx>
+
+#define EQUAL_BOOKMARKS(a, b) a == b
+
+#define IFACECAST(c) static_cast<const Reference< XInterface >&>(c)
+
+using namespace ::com::sun::star::uno;
+using namespace ::com::sun::star::util;
+using namespace ::com::sun::star::lang;
+using namespace ::com::sun::star::sdbc;
+using namespace ::com::sun::star::i18n;
+using namespace ::com::sun::star::beans;
+using namespace ::svxform;
+
+
+// = FmRecordCountListener
+
+// SMART_UNO_IMPLEMENTATION(FmRecordCountListener, UsrObject);
+
+
+FmRecordCountListener::FmRecordCountListener(const Reference< css::sdbc::XResultSet > & dbcCursor)
+{
+
+ m_xListening.set(dbcCursor, UNO_QUERY);
+ if (!m_xListening.is())
+ return;
+
+ if (::comphelper::getBOOL(m_xListening->getPropertyValue(FM_PROP_ROWCOUNTFINAL)))
+ {
+ m_xListening = nullptr;
+ // there's nothing to do as the record count is already known
+ return;
+ }
+
+ m_xListening->addPropertyChangeListener(FM_PROP_ROWCOUNT, static_cast<css::beans::XPropertyChangeListener*>(this));
+}
+
+
+void FmRecordCountListener::SetPropChangeHandler(const Link<sal_Int32,void>& lnk)
+{
+ m_lnkWhoWantsToKnow = lnk;
+
+ if (m_xListening.is())
+ NotifyCurrentCount();
+}
+
+
+FmRecordCountListener::~FmRecordCountListener()
+{
+
+}
+
+
+void FmRecordCountListener::DisConnect()
+{
+ if(m_xListening.is())
+ m_xListening->removePropertyChangeListener(FM_PROP_ROWCOUNT, static_cast<css::beans::XPropertyChangeListener*>(this));
+ m_xListening = nullptr;
+}
+
+
+void SAL_CALL FmRecordCountListener::disposing(const css::lang::EventObject& /*Source*/)
+{
+ DBG_ASSERT(m_xListening.is(), "FmRecordCountListener::disposing should never have been called without a propset !");
+ DisConnect();
+}
+
+
+void FmRecordCountListener::NotifyCurrentCount()
+{
+ if (m_lnkWhoWantsToKnow.IsSet())
+ {
+ DBG_ASSERT(m_xListening.is(), "FmRecordCountListener::NotifyCurrentCount : I have no propset ... !?");
+ sal_Int32 theCount = ::comphelper::getINT32(m_xListening->getPropertyValue(FM_PROP_ROWCOUNT));
+ m_lnkWhoWantsToKnow.Call(theCount);
+ }
+}
+
+
+void FmRecordCountListener::propertyChange(const css::beans::PropertyChangeEvent& /*evt*/)
+{
+ NotifyCurrentCount();
+}
+
+
+// FmSearchEngine - local classes
+
+SimpleTextWrapper::SimpleTextWrapper(const Reference< css::awt::XTextComponent > & _xText)
+ :ControlTextWrapper(_xText)
+ ,m_xText(_xText)
+{
+ DBG_ASSERT(m_xText.is(), "FmSearchEngine::SimpleTextWrapper::SimpleTextWrapper : invalid argument !");
+}
+
+
+OUString SimpleTextWrapper::getCurrentText() const
+{
+ return m_xText->getText();
+}
+
+
+ListBoxWrapper::ListBoxWrapper(const Reference< css::awt::XListBox > & _xBox)
+ :ControlTextWrapper(_xBox)
+ ,m_xBox(_xBox)
+{
+ DBG_ASSERT(m_xBox.is(), "FmSearchEngine::ListBoxWrapper::ListBoxWrapper : invalid argument !");
+}
+
+
+OUString ListBoxWrapper::getCurrentText() const
+{
+ return m_xBox->getSelectedItem();
+}
+
+
+CheckBoxWrapper::CheckBoxWrapper(const Reference< css::awt::XCheckBox > & _xBox)
+ :ControlTextWrapper(_xBox)
+ ,m_xBox(_xBox)
+{
+ DBG_ASSERT(m_xBox.is(), "FmSearchEngine::CheckBoxWrapper::CheckBoxWrapper : invalid argument !");
+}
+
+
+OUString CheckBoxWrapper::getCurrentText() const
+{
+ switch (static_cast<TriState>(m_xBox->getState()))
+ {
+ case TRISTATE_FALSE: return "0";
+ case TRISTATE_TRUE: return "1";
+ default: break;
+ }
+ return OUString();
+}
+
+
+// = FmSearchEngine
+
+bool FmSearchEngine::MoveCursor()
+{
+ bool bSuccess = true;
+ try
+ {
+ if (m_bForward)
+ if (m_xSearchCursor.isLast())
+ m_xSearchCursor.first();
+ else
+ m_xSearchCursor.next();
+ else
+ if (m_xSearchCursor.isFirst())
+ {
+ rtl::Reference<FmRecordCountListener> prclListener = new FmRecordCountListener(m_xSearchCursor);
+ prclListener->SetPropChangeHandler(LINK(this, FmSearchEngine, OnNewRecordCount));
+
+ m_xSearchCursor.last();
+
+ prclListener->DisConnect();
+ }
+ else
+ m_xSearchCursor.previous();
+ }
+ catch(Exception const&)
+ {
+ TOOLS_WARN_EXCEPTION( "svx", "FmSearchEngine::MoveCursor");
+ bSuccess = false;
+ }
+ catch(...)
+ {
+ TOOLS_WARN_EXCEPTION( "svx", "FmSearchEngine::MoveCursor : caught an unknown Exception !");
+ bSuccess = false;
+ }
+
+ return bSuccess;
+}
+
+
+bool FmSearchEngine::MoveField(sal_Int32& nPos, FieldCollection::iterator& iter, const FieldCollection::iterator& iterBegin, const FieldCollection::iterator& iterEnd)
+{
+ bool bSuccess(true);
+ if (m_bForward)
+ {
+ ++iter;
+ ++nPos;
+ if (iter == iterEnd)
+ {
+ bSuccess = MoveCursor();
+ iter = iterBegin;
+ nPos = 0;
+ }
+ } else
+ {
+ if (iter == iterBegin)
+ {
+ bSuccess = MoveCursor();
+ iter = iterEnd;
+ nPos = iter-iterBegin;
+ }
+ --iter;
+ --nPos;
+ }
+ return bSuccess;
+}
+
+
+void FmSearchEngine::BuildAndInsertFieldInfo(const Reference< css::container::XIndexAccess > & xAllFields, sal_Int32 nField)
+{
+ DBG_ASSERT( xAllFields.is() && ( nField >= 0 ) && ( nField < xAllFields->getCount() ),
+ "FmSearchEngine::BuildAndInsertFieldInfo: invalid field descriptor!" );
+
+ // the field itself
+ Reference< XInterface > xCurrentField;
+ xAllFields->getByIndex(nField) >>= xCurrentField;
+
+ // From this I now know that it supports the DatabaseRecord service (I hope).
+ // For the FormatKey and the type I need the PropertySet.
+ Reference< css::beans::XPropertySet > xProperties(xCurrentField, UNO_QUERY_THROW);
+
+ // build the FieldInfo for that
+ FieldInfo fiCurrent;
+ fiCurrent.xContents.set(xCurrentField, UNO_QUERY);
+
+ // and memorize
+ m_arrUsedFields.insert(m_arrUsedFields.end(), fiCurrent);
+
+}
+
+OUString FmSearchEngine::FormatField(sal_Int32 nWhich)
+{
+ DBG_ASSERT(o3tl::make_unsigned(nWhich) < m_aControlTexts.size(), "FmSearchEngine::FormatField(sal_Int32) : invalid position !");
+ DBG_ASSERT(m_aControlTexts[nWhich], "FmSearchEngine::FormatField(sal_Int32) : invalid object in array !");
+ DBG_ASSERT(m_aControlTexts[nWhich]->getControl().is(), "FmSearchEngine::FormatField : invalid control !");
+
+ if (m_nCurrentFieldIndex != -1)
+ {
+ DBG_ASSERT((nWhich == 0) || (nWhich == m_nCurrentFieldIndex), "FmSearchEngine::FormatField : parameter nWhich is invalid");
+ // analogous situation as below
+ nWhich = m_nCurrentFieldIndex;
+ }
+
+ DBG_ASSERT((nWhich >= 0) && (o3tl::make_unsigned(nWhich) < m_aControlTexts.size()),
+ "FmSearchEngine::FormatField : invalid argument nWhich !");
+ return m_aControlTexts[m_nCurrentFieldIndex == -1 ? nWhich : m_nCurrentFieldIndex]->getCurrentText();
+}
+
+
+FmSearchEngine::SearchResult FmSearchEngine::SearchSpecial(bool _bSearchForNull, sal_Int32& nFieldPos,
+ FieldCollection::iterator& iterFieldLoop, const FieldCollection::iterator& iterBegin, const FieldCollection::iterator& iterEnd)
+{
+ // memorize the start position
+ Any aStartMark;
+ try { aStartMark = m_xSearchCursor.getBookmark(); }
+ catch ( const Exception& ) { DBG_UNHANDLED_EXCEPTION("svx"); return SearchResult::Error; }
+ FieldCollection::const_iterator iterInitialField = iterFieldLoop;
+
+
+ bool bFound(false);
+ bool bMovedAround(false);
+ do
+ {
+ Application::Reschedule( true );
+
+ // the content to be compared currently
+ iterFieldLoop->xContents->getString(); // needed for wasNull
+ bFound = _bSearchForNull == bool(iterFieldLoop->xContents->wasNull());
+ if (bFound)
+ break;
+
+ // next field (implicitly next record, if necessary)
+ if (!MoveField(nFieldPos, iterFieldLoop, iterBegin, iterEnd))
+ { // When moving to the next field, something went wrong...
+ // Continuing is not possible, since the next time exactly the same
+ // will definitely go wrong again, thus abort.
+ // Before, however, so that the search continues at the current position:
+ try { m_aPreviousLocBookmark = m_xSearchCursor.getBookmark(); }
+ catch ( const Exception& ) { DBG_UNHANDLED_EXCEPTION("svx"); }
+ m_iterPreviousLocField = iterFieldLoop;
+ // and leave
+ return SearchResult::Error;
+ }
+
+ Any aCurrentBookmark;
+ try { aCurrentBookmark = m_xSearchCursor.getBookmark(); }
+ catch ( const Exception& ) { DBG_UNHANDLED_EXCEPTION("svx"); return SearchResult::Error; }
+
+ bMovedAround = EQUAL_BOOKMARKS(aStartMark, aCurrentBookmark) && (iterFieldLoop == iterInitialField);
+
+ if (nFieldPos == 0)
+ // that is, I've moved to a new record
+ PropagateProgress(bMovedAround);
+ // if we moved to the starting position we don't have to propagate an 'overflow' message
+ // FS - 07.12.99 - 68530
+
+ // cancel requested?
+ if (CancelRequested())
+ return SearchResult::Cancelled;
+
+ } while (!bMovedAround);
+
+ return bFound ? SearchResult::Found : SearchResult::NotFound;
+}
+
+
+FmSearchEngine::SearchResult FmSearchEngine::SearchWildcard(std::u16string_view strExpression, sal_Int32& nFieldPos,
+ FieldCollection::iterator& iterFieldLoop, const FieldCollection::iterator& iterBegin, const FieldCollection::iterator& iterEnd)
+{
+ // memorize the start position
+ Any aStartMark;
+ try { aStartMark = m_xSearchCursor.getBookmark(); }
+ catch ( const Exception& ) { DBG_UNHANDLED_EXCEPTION("svx"); return SearchResult::Error; }
+ FieldCollection::const_iterator iterInitialField = iterFieldLoop;
+
+ WildCard aSearchExpression(strExpression);
+
+
+ bool bFound(false);
+ bool bMovedAround(false);
+ do
+ {
+ Application::Reschedule( true );
+
+ // the content to be compared currently
+ OUString sCurrentCheck;
+ if (m_bFormatter)
+ sCurrentCheck = FormatField(nFieldPos);
+ else
+ sCurrentCheck = iterFieldLoop->xContents->getString();
+
+ if (!GetCaseSensitive())
+ // norm the string
+ sCurrentCheck = m_aCharacterClassficator.lowercase(sCurrentCheck);
+
+ // now the test is easy...
+ bFound = aSearchExpression.Matches(sCurrentCheck);
+
+ if (bFound)
+ break;
+
+ // next field (implicitly next record, if necessary)
+ if (!MoveField(nFieldPos, iterFieldLoop, iterBegin, iterEnd))
+ { // When moving to the next field, something went wrong...
+ // Continuing is not possible, since the next time exactly the same
+ // will definitely go wrong again, thus abort.
+ // Before, however, so that the search continues at the current position:
+ try { m_aPreviousLocBookmark = m_xSearchCursor.getBookmark(); }
+ catch ( const Exception& ) { DBG_UNHANDLED_EXCEPTION("svx"); }
+ m_iterPreviousLocField = iterFieldLoop;
+ // and leave
+ return SearchResult::Error;
+ }
+
+ Any aCurrentBookmark;
+ try { aCurrentBookmark = m_xSearchCursor.getBookmark(); }
+ catch ( const Exception& ) { DBG_UNHANDLED_EXCEPTION("svx"); return SearchResult::Error; }
+
+ bMovedAround = EQUAL_BOOKMARKS(aStartMark, aCurrentBookmark) && (iterFieldLoop == iterInitialField);
+
+ if (nFieldPos == 0)
+ // that is, I've moved to a new record
+ PropagateProgress(bMovedAround);
+ // if we moved to the starting position we don't have to propagate an 'overflow' message
+ // FS - 07.12.99 - 68530
+
+ // cancel requested?
+ if (CancelRequested())
+ return SearchResult::Cancelled;
+
+ } while (!bMovedAround);
+
+ return bFound ? SearchResult::Found : SearchResult::NotFound;
+}
+
+
+FmSearchEngine::SearchResult FmSearchEngine::SearchRegularApprox(const OUString& strExpression, sal_Int32& nFieldPos,
+ FieldCollection::iterator& iterFieldLoop, const FieldCollection::iterator& iterBegin, const FieldCollection::iterator& iterEnd)
+{
+ DBG_ASSERT(m_bLevenshtein || m_bRegular,
+ "FmSearchEngine::SearchRegularApprox : invalid search mode!");
+ DBG_ASSERT(!m_bLevenshtein || !m_bRegular,
+ "FmSearchEngine::SearchRegularApprox : cannot search for regular expressions and similarities at the same time!");
+
+ // memorize start position
+ Any aStartMark;
+ try { aStartMark = m_xSearchCursor.getBookmark(); }
+ catch ( const Exception& ) { DBG_UNHANDLED_EXCEPTION("svx"); return SearchResult::Error; }
+ FieldCollection::const_iterator iterInitialField = iterFieldLoop;
+
+ // collect parameters
+ i18nutil::SearchOptions2 aParam;
+ aParam.AlgorithmType2 = m_bRegular ? SearchAlgorithms2::REGEXP : SearchAlgorithms2::APPROXIMATE;
+ aParam.searchFlag = 0;
+ aParam.transliterateFlags = GetTransliterationFlags();
+ if ( !GetTransliteration() )
+ { // if transliteration is not enabled, the only flags which matter are IGNORE_CASE and IGNORE_WIDTH
+ aParam.transliterateFlags &= TransliterationFlags::IGNORE_CASE | TransliterationFlags::IGNORE_WIDTH;
+ }
+ if (m_bLevenshtein)
+ {
+ if (m_bLevRelaxed)
+ aParam.searchFlag |= SearchFlags::LEV_RELAXED;
+ aParam.changedChars = m_nLevOther;
+ aParam.deletedChars = m_nLevShorter;
+ aParam.insertedChars = m_nLevLonger;
+ }
+ aParam.searchString = strExpression;
+ aParam.Locale = SvtSysLocale().GetLanguageTag().getLocale();
+ ::utl::TextSearch aLocalEngine( aParam);
+
+
+ bool bFound = false;
+ bool bMovedAround(false);
+ do
+ {
+ Application::Reschedule( true );
+
+ // the content to be compared currently
+ OUString sCurrentCheck;
+ if (m_bFormatter)
+ sCurrentCheck = FormatField(nFieldPos);
+ else
+ sCurrentCheck = iterFieldLoop->xContents->getString();
+
+ // (don't care about case here, this is done by the TextSearch object, 'cause we passed our case parameter to it)
+
+ sal_Int32 nStart = 0, nEnd = sCurrentCheck.getLength();
+ bFound = aLocalEngine.SearchForward(sCurrentCheck, &nStart, &nEnd);
+ // it says 'forward' here, but that only refers to the search within
+ // sCurrentCheck, so it has nothing to do with the direction of my
+ // record migration (MoveField takes care of that)
+
+ // check if the position is correct
+ if (bFound)
+ {
+ switch (m_nPosition)
+ {
+ case MATCHING_WHOLETEXT :
+ if (nEnd != sCurrentCheck.getLength())
+ {
+ bFound = false;
+ break;
+ }
+ [[fallthrough]];
+ case MATCHING_BEGINNING :
+ if (nStart != 0)
+ bFound = false;
+ break;
+ case MATCHING_END :
+ if (nEnd != sCurrentCheck.getLength())
+ bFound = false;
+ break;
+ }
+ }
+
+ if (bFound) // still?
+ break;
+
+ // next field (implicitly next record, if necessary)
+ if (!MoveField(nFieldPos, iterFieldLoop, iterBegin, iterEnd))
+ { // When moving to the next field, something went wrong...
+ // Continuing is not possible, since the next time exactly the same
+ // will definitely go wrong again, thus abort (without error
+ // notification, I expect it to be displayed in the Move).
+ // Before, however, so that the search continues at the current position:
+ try { m_aPreviousLocBookmark = m_xSearchCursor.getBookmark(); }
+ catch ( const Exception& ) { DBG_UNHANDLED_EXCEPTION("svx"); }
+ m_iterPreviousLocField = iterFieldLoop;
+ // and leave
+ return SearchResult::Error;
+ }
+
+ Any aCurrentBookmark;
+ try { aCurrentBookmark = m_xSearchCursor.getBookmark(); }
+ catch ( const Exception& ) { DBG_UNHANDLED_EXCEPTION("svx"); return SearchResult::Error; }
+ bMovedAround = EQUAL_BOOKMARKS(aStartMark, aCurrentBookmark) && (iterFieldLoop == iterInitialField);
+
+ if (nFieldPos == 0)
+ // that is, I've moved to a new record
+ PropagateProgress(bMovedAround);
+ // if we moved to the starting position we don't have to propagate an 'overflow' message
+ // FS - 07.12.99 - 68530
+
+ // cancel requested?
+ if (CancelRequested())
+ return SearchResult::Cancelled;
+
+ } while (!bMovedAround);
+
+ return bFound ? SearchResult::Found : SearchResult::NotFound;
+}
+
+
+FmSearchEngine::FmSearchEngine(const Reference< XComponentContext >& _rxContext,
+ const Reference< XResultSet > & xCursor, std::u16string_view sVisibleFields,
+ const InterfaceArray& arrFields)
+ :m_xSearchCursor(xCursor)
+ ,m_aCharacterClassficator( _rxContext, SvtSysLocale().GetLanguageTag() )
+ ,m_aStringCompare( _rxContext )
+ ,m_nCurrentFieldIndex(-2) // -1 already has a meaning, so I take -2 for 'invalid'
+ ,m_xOriginalIterator(xCursor)
+ ,m_xClonedIterator(m_xOriginalIterator, true)
+ ,m_eSearchForType(SearchFor::String)
+ ,m_srResult(SearchResult::Found)
+ ,m_bCancelAsynchRequest(false)
+ ,m_bSearchingCurrently(false)
+ ,m_bFormatter(true) // this must be consistent with m_xSearchCursor, which is generally == m_xOriginalIterator
+ ,m_bForward(false)
+ ,m_bWildcard(false)
+ ,m_bRegular(false)
+ ,m_bLevenshtein(false)
+ ,m_bTransliteration(false)
+ ,m_bLevRelaxed(false)
+ ,m_nLevOther(0)
+ ,m_nLevShorter(0)
+ ,m_nLevLonger(0)
+ ,m_nPosition(MATCHING_ANYWHERE)
+ ,m_nTransliterationFlags(TransliterationFlags::NONE)
+{
+
+ fillControlTexts(arrFields);
+ Init(sVisibleFields);
+}
+
+
+void FmSearchEngine::SetIgnoreWidthCJK(bool bSet)
+{
+ if (bSet)
+ m_nTransliterationFlags |= TransliterationFlags::IGNORE_WIDTH;
+ else
+ m_nTransliterationFlags &= ~TransliterationFlags::IGNORE_WIDTH;
+}
+
+
+bool FmSearchEngine::GetIgnoreWidthCJK() const
+{
+ return bool(m_nTransliterationFlags & TransliterationFlags::IGNORE_WIDTH);
+}
+
+
+void FmSearchEngine::SetCaseSensitive(bool bSet)
+{
+ if (bSet)
+ m_nTransliterationFlags &= ~TransliterationFlags::IGNORE_CASE;
+ else
+ m_nTransliterationFlags |= TransliterationFlags::IGNORE_CASE;
+}
+
+
+bool FmSearchEngine::GetCaseSensitive() const
+{
+ return !(m_nTransliterationFlags & TransliterationFlags::IGNORE_CASE);
+}
+
+
+void FmSearchEngine::fillControlTexts(const InterfaceArray& arrFields)
+{
+ m_aControlTexts.clear();
+ Reference< XInterface > xCurrent;
+ for (const auto & rField : arrFields)
+ {
+ xCurrent = rField;
+ DBG_ASSERT(xCurrent.is(), "FmSearchEngine::fillControlTexts : invalid field interface !");
+ // check which type of control this is
+ Reference< css::awt::XTextComponent > xAsText(xCurrent, UNO_QUERY);
+ if (xAsText.is())
+ {
+ m_aControlTexts.emplace_back(new SimpleTextWrapper(xAsText));
+ continue;
+ }
+
+ Reference< css::awt::XListBox > xAsListBox(xCurrent, UNO_QUERY);
+ if (xAsListBox.is())
+ {
+ m_aControlTexts.emplace_back(new ListBoxWrapper(xAsListBox));
+ continue;
+ }
+
+ Reference< css::awt::XCheckBox > xAsCheckBox(xCurrent, UNO_QUERY);
+ DBG_ASSERT(xAsCheckBox.is(), "FmSearchEngine::fillControlTexts : invalid field interface (no supported type) !");
+ // we don't have any more options ...
+ m_aControlTexts.emplace_back(new CheckBoxWrapper(xAsCheckBox));
+ }
+}
+
+
+void FmSearchEngine::Init(std::u16string_view sVisibleFields)
+{
+ // analyze the fields
+ // additionally, create the mapping: because the list of used columns can be shorter than the list
+ // of columns of the cursor, we need a mapping: "used column number n" -> "cursor column m"
+ m_arrFieldMapping.clear();
+
+ // important: The case of the columns does not need to be exact - for instance:
+ // - a user created a form which works on a table, for which the driver returns a column name "COLUMN"
+ // - the driver itself works case-insensitive with column names
+ // - a control in the form is bound to "column" - not the different case
+ // In such a scenario, the form and the field would work okay, but we here need to case for the different case
+ // explicitly
+ // #i8755#
+
+ // so first of all, check if the database handles identifiers case sensitive
+ Reference< XConnection > xConn;
+ Reference< XDatabaseMetaData > xMeta;
+ Reference< XPropertySet > xCursorProps( IFACECAST( m_xSearchCursor ), UNO_QUERY );
+ if ( xCursorProps.is() )
+ {
+ try
+ {
+ xCursorProps->getPropertyValue( FM_PROP_ACTIVE_CONNECTION ) >>= xConn;
+ }
+ catch( const Exception& ) { /* silent this - will be asserted below */ }
+ }
+ if ( xConn.is() )
+ xMeta = xConn->getMetaData();
+ OSL_ENSURE( xMeta.is(), "FmSearchEngine::Init: very strange cursor (could not derive connection meta data from it)!" );
+
+ bool bCaseSensitiveIdentifiers = true; // assume case sensitivity
+ if ( xMeta.is() )
+ bCaseSensitiveIdentifiers = xMeta->supportsMixedCaseQuotedIdentifiers();
+
+ // now that we have this information, we need a collator which is able to case (in)sensitivity compare strings
+ m_aStringCompare.loadDefaultCollator( SvtSysLocale().GetLanguageTag().getLocale(),
+ bCaseSensitiveIdentifiers ? 0 : css::i18n::CollatorOptions::CollatorOptions_IGNORE_CASE );
+
+ try
+ {
+ // the cursor can give me a record (as PropertySet), which supports the DatabaseRecord service
+ Reference< css::sdbcx::XColumnsSupplier > xSupplyCols(IFACECAST(m_xSearchCursor), UNO_QUERY);
+ DBG_ASSERT(xSupplyCols.is(), "FmSearchEngine::Init : invalid cursor (no columns supplier) !");
+ Reference< css::container::XNameAccess > xAllFieldNames = xSupplyCols->getColumns();
+ const Sequence< OUString > seqFieldNames = xAllFieldNames->getElementNames();
+
+ OUString sCurrentField;
+ sal_Int32 nIndex = 0;
+ do
+ {
+ sCurrentField = o3tl::getToken(sVisibleFields, 0, ';' , nIndex);
+
+ // search in the field collection
+ sal_Int32 nFoundIndex = -1;
+ auto pFieldName = std::find_if(seqFieldNames.begin(), seqFieldNames.end(),
+ [this, &sCurrentField](const OUString& rFieldName) {
+ return 0 == m_aStringCompare.compareString( rFieldName, sCurrentField ); });
+ if (pFieldName != seqFieldNames.end())
+ nFoundIndex = static_cast<sal_Int32>(std::distance(seqFieldNames.begin(), pFieldName));
+ DBG_ASSERT(nFoundIndex != -1, "FmSearchEngine::Init : Invalid field name were given !");
+ m_arrFieldMapping.push_back(nFoundIndex);
+ }
+ while ( nIndex >= 0 );
+ }
+ catch (const Exception&)
+ {
+ TOOLS_WARN_EXCEPTION("svx.form", "");
+ }
+
+}
+
+
+void FmSearchEngine::SetFormatterUsing(bool bSet)
+{
+ if (m_bFormatter == bSet)
+ return;
+ m_bFormatter = bSet;
+
+ // I did not use a formatter, but TextComponents -> the SearchIterator needs to be adjusted
+ try
+ {
+ if (m_bFormatter)
+ {
+ DBG_ASSERT(m_xSearchCursor == m_xClonedIterator, "FmSearchEngine::SetFormatterUsing : inconsistent state !");
+ m_xSearchCursor = m_xOriginalIterator;
+ m_xSearchCursor.moveToBookmark(m_xClonedIterator.getBookmark());
+ // so that I continue with the new iterator at the actual place where I previously stopped
+ }
+ else
+ {
+ DBG_ASSERT(m_xSearchCursor == m_xOriginalIterator, "FmSearchEngine::SetFormatterUsing : inconsistent state !");
+ m_xSearchCursor = m_xClonedIterator;
+ m_xSearchCursor.moveToBookmark(m_xOriginalIterator.getBookmark());
+ }
+ }
+ catch( const Exception& )
+ {
+ DBG_UNHANDLED_EXCEPTION("svx");
+ }
+
+ // I have to re-bind the fields, because the text exchange might take
+ // place over these fields and the underlying cursor has changed
+ RebuildUsedFields(m_nCurrentFieldIndex, true);
+}
+
+
+void FmSearchEngine::PropagateProgress(bool _bDontPropagateOverflow)
+{
+ if (!m_aProgressHandler.IsSet())
+ return;
+
+ FmSearchProgress aProgress;
+ try
+ {
+ aProgress.aSearchState = FmSearchProgress::State::Progress;
+ aProgress.nCurrentRecord = m_xSearchCursor.getRow() - 1;
+ if (m_bForward)
+ aProgress.bOverflow = !_bDontPropagateOverflow && m_xSearchCursor.isFirst();
+ else
+ aProgress.bOverflow = !_bDontPropagateOverflow && m_xSearchCursor.isLast();
+ }
+ catch( const Exception& )
+ {
+ DBG_UNHANDLED_EXCEPTION("svx");
+ }
+
+ m_aProgressHandler.Call(&aProgress);
+}
+
+
+void FmSearchEngine::SearchNextImpl()
+{
+ DBG_ASSERT(!(m_bWildcard && m_bRegular) && !(m_bRegular && m_bLevenshtein) && !(m_bLevenshtein && m_bWildcard),
+ "FmSearchEngine::SearchNextImpl : search parameters are mutually exclusive!");
+
+ DBG_ASSERT(m_xSearchCursor.is(), "FmSearchEngine::SearchNextImpl : have invalid iterator!");
+
+ // the parameters of the search
+ OUString strSearchExpression(m_strSearchExpression); // I need non-const
+ if (!GetCaseSensitive())
+ // norm the string
+ strSearchExpression = m_aCharacterClassficator.lowercase(strSearchExpression);
+
+ if (!m_bRegular && !m_bLevenshtein)
+ { // 'normal' search I run through WildCards in any case, but must before adjust the OUString depending on the mode
+
+ if (!m_bWildcard)
+ { // since in all other cases * and ? in the search string are of course
+ // also allowed, but should not count as WildCards, I need to normalize
+ OUString aTmp(strSearchExpression);
+ aTmp = aTmp.replaceAll("*", "\\*");
+ aTmp = aTmp.replaceAll("?", "\\?");
+ strSearchExpression = aTmp;
+
+ switch (m_nPosition)
+ {
+ case MATCHING_ANYWHERE :
+ strSearchExpression = "*" + strSearchExpression + "*";
+ break;
+ case MATCHING_BEGINNING :
+ strSearchExpression += "*";
+ break;
+ case MATCHING_END :
+ strSearchExpression = "*" + strSearchExpression;
+ break;
+ case MATCHING_WHOLETEXT :
+ break;
+ default :
+ OSL_FAIL("FmSearchEngine::SearchNextImpl() : the methods listbox may contain only 4 entries ...");
+ }
+ }
+ }
+
+ // for work on field list
+ FieldCollection::iterator iterBegin = m_arrUsedFields.begin();
+ FieldCollection::iterator iterEnd = m_arrUsedFields.end();
+ FieldCollection::iterator iterFieldCheck;
+
+ sal_Int32 nFieldPos;
+
+ if (m_aPreviousLocBookmark.hasValue())
+ {
+ DBG_ASSERT(EQUAL_BOOKMARKS(m_aPreviousLocBookmark, m_xSearchCursor.getBookmark()),
+ "FmSearchEngine::SearchNextImpl : invalid position!");
+ iterFieldCheck = m_iterPreviousLocField;
+ // continue in the field after (or before) the last discovery
+ nFieldPos = iterFieldCheck - iterBegin;
+ MoveField(nFieldPos, iterFieldCheck, iterBegin, iterEnd);
+ }
+ else
+ {
+ if (m_bForward)
+ iterFieldCheck = iterBegin;
+ else
+ {
+ iterFieldCheck = iterEnd;
+ --iterFieldCheck;
+ }
+ nFieldPos = iterFieldCheck - iterBegin;
+ }
+
+ PropagateProgress(true);
+ SearchResult srResult;
+ if (m_eSearchForType != SearchFor::String)
+ srResult = SearchSpecial(m_eSearchForType == SearchFor::Null, nFieldPos, iterFieldCheck, iterBegin, iterEnd);
+ else if (!m_bRegular && !m_bLevenshtein)
+ srResult = SearchWildcard(strSearchExpression, nFieldPos, iterFieldCheck, iterBegin, iterEnd);
+ else
+ srResult = SearchRegularApprox(strSearchExpression, nFieldPos, iterFieldCheck, iterBegin, iterEnd);
+
+ m_srResult = srResult;
+
+ if (SearchResult::Error == m_srResult)
+ return;
+
+ // found?
+ if (SearchResult::Found == m_srResult)
+ {
+ // memorize the position
+ try { m_aPreviousLocBookmark = m_xSearchCursor.getBookmark(); }
+ catch ( const Exception& ) { DBG_UNHANDLED_EXCEPTION("svx"); }
+ m_iterPreviousLocField = iterFieldCheck;
+ }
+ else
+ // invalidate the "last discovery"
+ InvalidatePreviousLoc();
+}
+
+
+void FmSearchEngine::OnSearchTerminated()
+{
+ if (!m_aProgressHandler.IsSet())
+ return;
+
+ FmSearchProgress aProgress;
+ try
+ {
+ switch (m_srResult)
+ {
+ case SearchResult::Error :
+ aProgress.aSearchState = FmSearchProgress::State::Error;
+ break;
+ case SearchResult::Found :
+ aProgress.aSearchState = FmSearchProgress::State::Successful;
+ aProgress.aBookmark = m_aPreviousLocBookmark;
+ aProgress.nFieldIndex = m_iterPreviousLocField - m_arrUsedFields.begin();
+ break;
+ case SearchResult::NotFound :
+ aProgress.aSearchState = FmSearchProgress::State::NothingFound;
+ aProgress.aBookmark = m_xSearchCursor.getBookmark();
+ break;
+ case SearchResult::Cancelled :
+ aProgress.aSearchState = FmSearchProgress::State::Canceled;
+ aProgress.aBookmark = m_xSearchCursor.getBookmark();
+ break;
+ }
+ aProgress.nCurrentRecord = m_xSearchCursor.getRow() - 1;
+ }
+ catch( const Exception& )
+ {
+ DBG_UNHANDLED_EXCEPTION("svx");
+ }
+
+ // by definition, the link must be thread-safe (I just require that),
+ // so that I do not have to worry about such things here
+ m_aProgressHandler.Call(&aProgress);
+
+ m_bSearchingCurrently = false;
+}
+
+
+IMPL_LINK(FmSearchEngine, OnNewRecordCount, sal_Int32, theCounter, void)
+{
+ if (!m_aProgressHandler.IsSet())
+ return;
+
+ FmSearchProgress aProgress;
+ aProgress.nCurrentRecord = theCounter;
+ aProgress.aSearchState = FmSearchProgress::State::ProgressCounting;
+ m_aProgressHandler.Call(&aProgress);
+}
+
+
+bool FmSearchEngine::CancelRequested()
+{
+ bool bReturn = m_bCancelAsynchRequest;
+ return bReturn;
+}
+
+
+void FmSearchEngine::CancelSearch()
+{
+ m_bCancelAsynchRequest = true;
+}
+
+
+void FmSearchEngine::SwitchToContext(const Reference< css::sdbc::XResultSet > & xCursor, std::u16string_view sVisibleFields, const InterfaceArray& arrFields,
+ sal_Int32 nFieldIndex)
+{
+ DBG_ASSERT(!m_bSearchingCurrently, "FmSearchEngine::SwitchToContext : please do not call while I'm searching !");
+ if (m_bSearchingCurrently)
+ return;
+
+ m_xSearchCursor = xCursor;
+ m_xOriginalIterator = xCursor;
+ m_xClonedIterator = CursorWrapper(m_xOriginalIterator, true);
+
+ fillControlTexts(arrFields);
+
+ Init(sVisibleFields);
+ RebuildUsedFields(nFieldIndex, true);
+}
+
+
+void FmSearchEngine::ImplStartNextSearch()
+{
+ m_bCancelAsynchRequest = false;
+ m_bSearchingCurrently = true;
+
+ SearchNextImpl();
+ OnSearchTerminated();
+}
+
+
+void FmSearchEngine::SearchNext(const OUString& strExpression)
+{
+ m_strSearchExpression = strExpression;
+ m_eSearchForType = SearchFor::String;
+ ImplStartNextSearch();
+}
+
+
+void FmSearchEngine::SearchNextSpecial(bool _bSearchForNull)
+{
+ m_eSearchForType = _bSearchForNull ? SearchFor::Null : SearchFor::NotNull;
+ ImplStartNextSearch();
+}
+
+
+void FmSearchEngine::StartOver(const OUString& strExpression)
+{
+ try
+ {
+ if (m_bForward)
+ m_xSearchCursor.first();
+ else
+ m_xSearchCursor.last();
+ }
+ catch( const Exception& )
+ {
+ DBG_UNHANDLED_EXCEPTION("svx");
+ return;
+ }
+
+ InvalidatePreviousLoc();
+ SearchNext(strExpression);
+}
+
+
+void FmSearchEngine::StartOverSpecial(bool _bSearchForNull)
+{
+ try
+ {
+ if (m_bForward)
+ m_xSearchCursor.first();
+ else
+ m_xSearchCursor.last();
+ }
+ catch( const Exception& )
+ {
+ DBG_UNHANDLED_EXCEPTION("svx");
+ return;
+ }
+
+ InvalidatePreviousLoc();
+ SearchNextSpecial(_bSearchForNull);
+}
+
+
+void FmSearchEngine::InvalidatePreviousLoc()
+{
+ m_aPreviousLocBookmark.clear();
+ m_iterPreviousLocField = m_arrUsedFields.end();
+}
+
+
+void FmSearchEngine::RebuildUsedFields(sal_Int32 nFieldIndex, bool bForce)
+{
+ if (!bForce && (nFieldIndex == m_nCurrentFieldIndex))
+ return;
+ // (since I allow no change of the iterator from the outside, the same css::sdbcx::Index
+ // also always means the same column, so I have nothing to do)
+
+ DBG_ASSERT((nFieldIndex == -1) ||
+ ((nFieldIndex >= 0) &&
+ (o3tl::make_unsigned(nFieldIndex) < m_arrFieldMapping.size())),
+ "FmSearchEngine::RebuildUsedFields : nFieldIndex is invalid!");
+ // collect all fields I need to search through
+ m_arrUsedFields.clear();
+ if (nFieldIndex == -1)
+ {
+ Reference< css::container::XIndexAccess > xFields;
+ for (sal_Int32 i : m_arrFieldMapping)
+ {
+ Reference< css::sdbcx::XColumnsSupplier > xSupplyCols(IFACECAST(m_xSearchCursor), UNO_QUERY);
+ DBG_ASSERT(xSupplyCols.is(), "FmSearchEngine::RebuildUsedFields : invalid cursor (no columns supplier) !");
+ xFields.set(xSupplyCols->getColumns(), UNO_QUERY);
+ BuildAndInsertFieldInfo(xFields, i);
+ }
+ }
+ else
+ {
+ Reference< css::container::XIndexAccess > xFields;
+ Reference< css::sdbcx::XColumnsSupplier > xSupplyCols(IFACECAST(m_xSearchCursor), UNO_QUERY);
+ DBG_ASSERT(xSupplyCols.is(), "FmSearchEngine::RebuildUsedFields : invalid cursor (no columns supplier) !");
+ xFields.set (xSupplyCols->getColumns(), UNO_QUERY);
+ BuildAndInsertFieldInfo(xFields, m_arrFieldMapping[static_cast< size_t >(nFieldIndex)]);
+ }
+
+ m_nCurrentFieldIndex = nFieldIndex;
+ // and of course I start the next search in a virgin state again
+ InvalidatePreviousLoc();
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/form/fmtextcontroldialogs.cxx b/svx/source/form/fmtextcontroldialogs.cxx
new file mode 100644
index 000000000..c365becea
--- /dev/null
+++ b/svx/source/form/fmtextcontroldialogs.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 <fmtextcontroldialogs.hxx>
+#include <svx/dialogs.hrc>
+#include <svx/svxids.hrc>
+
+#include <svx/flagsdef.hxx>
+#include <svl/intitem.hxx>
+
+#include <svl/cjkoptions.hxx>
+
+
+namespace svx
+{
+
+ TextControlCharAttribDialog::TextControlCharAttribDialog(weld::Window* pParent, const SfxItemSet& rCoreSet, const SvxFontListItem& rFontList)
+ : SfxTabDialogController(pParent, "svx/ui/textcontrolchardialog.ui", "TextControlCharacterPropertiesDialog", &rCoreSet)
+ , m_aFontList(rFontList)
+ {
+ AddTabPage("font", RID_SVXPAGE_CHAR_NAME);
+ AddTabPage("fonteffects", RID_SVXPAGE_CHAR_EFFECTS);
+ AddTabPage("position", RID_SVXPAGE_CHAR_POSITION);
+ }
+
+ void TextControlCharAttribDialog::PageCreated(const OString& rId, SfxTabPage& rPage)
+ {
+ SfxAllItemSet aSet(*(GetInputSetImpl()->GetPool()));
+
+ if (rId == "font")
+ {
+ aSet.Put (m_aFontList);
+ rPage.PageCreated(aSet);
+ }
+ else if (rId == "fonteffects")
+ {
+ aSet.Put (SfxUInt16Item(SID_DISABLE_CTL,DISABLE_CASEMAP));
+ rPage.PageCreated(aSet);
+ }
+ else if (rId == "position")
+ {
+ aSet.Put( SfxUInt32Item(SID_FLAG_TYPE, SVX_PREVIEW_CHARACTER) );
+ rPage.PageCreated(aSet);
+ }
+ }
+
+ TextControlParaAttribDialog::TextControlParaAttribDialog(weld::Window* pParent, const SfxItemSet& rCoreSet)
+ : SfxTabDialogController(pParent, "svx/ui/textcontrolparadialog.ui", "TextControlParagraphPropertiesDialog", &rCoreSet)
+ {
+ AddTabPage("labelTP_PARA_STD", RID_SVXPAGE_STD_PARAGRAPH);
+ AddTabPage("labelTP_PARA_ALIGN", RID_SVXPAGE_ALIGN_PARAGRAPH);
+
+ if( SvtCJKOptions::IsAsianTypographyEnabled() )
+ AddTabPage("labelTP_PARA_ASIAN", RID_SVXPAGE_PARA_ASIAN);
+ else
+ RemoveTabPage("labelTP_PARA_ASIAN");
+
+ AddTabPage("labelTP_TABULATOR", RID_SVXPAGE_TABULATOR);
+ }
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/form/fmtextcontrolfeature.cxx b/svx/source/form/fmtextcontrolfeature.cxx
new file mode 100644
index 000000000..68bb8606a
--- /dev/null
+++ b/svx/source/form/fmtextcontrolfeature.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 <fmtextcontrolfeature.hxx>
+#include <fmtextcontrolshell.hxx>
+
+#include <osl/diagnose.h>
+#include <tools/diagnose_ex.h>
+
+namespace svx
+{
+
+
+ using namespace ::com::sun::star::uno;
+ using namespace ::com::sun::star::frame;
+ using namespace ::com::sun::star::lang;
+ using namespace ::com::sun::star::beans;
+ using namespace ::com::sun::star::util;
+
+ FmTextControlFeature::FmTextControlFeature( const Reference< XDispatch >& _rxDispatcher, const URL& _rFeatureURL, SfxSlotId _nSlotId, FmTextControlShell* _pInvalidator )
+ :m_xDispatcher ( _rxDispatcher )
+ ,m_aFeatureURL ( _rFeatureURL )
+ ,m_nSlotId ( _nSlotId )
+ ,m_pInvalidator ( _pInvalidator )
+ ,m_bFeatureEnabled( false )
+ {
+ OSL_ENSURE( _rxDispatcher.is(), "FmTextControlFeature::FmTextControlFeature: invalid dispatcher!" );
+ OSL_ENSURE( m_nSlotId, "FmTextControlFeature::FmTextControlFeature: invalid slot id!" );
+ OSL_ENSURE( m_pInvalidator, "FmTextControlFeature::FmTextControlFeature: invalid invalidator!" );
+
+ osl_atomic_increment( &m_refCount );
+ try
+ {
+ m_xDispatcher->addStatusListener( this, m_aFeatureURL );
+ }
+ catch( const Exception& )
+ {
+ TOOLS_WARN_EXCEPTION( "svx", "FmTextControlFeature::FmTextControlFeature" );
+ }
+ osl_atomic_decrement( &m_refCount );
+ }
+
+
+ FmTextControlFeature::~FmTextControlFeature( )
+ {
+ }
+
+
+ void FmTextControlFeature::dispatch() const
+ {
+ dispatch( Sequence< PropertyValue >( ) );
+ }
+
+
+ void FmTextControlFeature::dispatch( const Sequence< PropertyValue >& _rArgs ) const
+ {
+ try
+ {
+ if ( m_xDispatcher.is() )
+ m_xDispatcher->dispatch( m_aFeatureURL, _rArgs );
+ }
+ catch( const Exception& )
+ {
+ TOOLS_WARN_EXCEPTION( "svx", "FmTextControlFeature::dispatch" );
+ }
+ }
+
+
+ void SAL_CALL FmTextControlFeature::statusChanged( const FeatureStateEvent& _rState )
+ {
+ m_aFeatureState = _rState.State;
+ m_bFeatureEnabled = _rState.IsEnabled;
+
+ if ( m_pInvalidator )
+ m_pInvalidator->Invalidate( m_nSlotId );
+ }
+
+
+ void SAL_CALL FmTextControlFeature::disposing( const EventObject& /*Source*/ )
+ {
+ // nothing to do
+ }
+
+
+ void FmTextControlFeature::dispose()
+ {
+ try
+ {
+ m_xDispatcher->removeStatusListener( this, m_aFeatureURL );
+ m_xDispatcher.clear();
+ }
+ catch( const Exception& )
+ {
+ TOOLS_WARN_EXCEPTION( "svx", "FmTextControlFeature::dispose" );
+ }
+ }
+
+
+}
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/form/fmtextcontrolshell.cxx b/svx/source/form/fmtextcontrolshell.cxx
new file mode 100644
index 000000000..d97dfeb85
--- /dev/null
+++ b/svx/source/form/fmtextcontrolshell.cxx
@@ -0,0 +1,1315 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+
+#include <fmprop.hxx>
+#include <fmtextcontroldialogs.hxx>
+#include <fmtextcontrolfeature.hxx>
+#include <fmtextcontrolshell.hxx>
+#include <editeng/crossedoutitem.hxx>
+#include <editeng/editeng.hxx>
+#include <editeng/scriptspaceitem.hxx>
+#include <svx/svxids.hrc>
+#include <editeng/udlnitem.hxx>
+
+#include <com/sun/star/beans/XPropertySet.hpp>
+#include <com/sun/star/frame/XDispatchProvider.hpp>
+#include <com/sun/star/container/XChild.hpp>
+#include <com/sun/star/awt/XFocusListener.hpp>
+#include <com/sun/star/awt/XMouseListener.hpp>
+#include <com/sun/star/awt/XWindow.hpp>
+#include <com/sun/star/util/URLTransformer.hpp>
+
+#include <comphelper/processfactory.hxx>
+#include <comphelper/propertyvalue.hxx>
+#include <cppuhelper/implbase.hxx>
+#include <sfx2/app.hxx>
+#include <sfx2/bindings.hxx>
+#include <sfx2/dispatch.hxx>
+#include <sfx2/msgpool.hxx>
+#include <sfx2/msg.hxx>
+#include <sfx2/objsh.hxx>
+#include <sfx2/request.hxx>
+#include <sfx2/sfxuno.hxx>
+#include <sfx2/viewfrm.hxx>
+#include <svl/eitem.hxx>
+#include <svl/itempool.hxx>
+#include <svl/ctloptions.hxx>
+#include <svtools/stringtransfer.hxx>
+#include <svl/whiter.hxx>
+#include <toolkit/helper/vclunohelper.hxx>
+#include <tools/debug.hxx>
+#include <tools/diagnose_ex.h>
+#include <sal/log.hxx>
+#include <vcl/svapp.hxx>
+#include <vcl/window.hxx>
+
+#include <memory>
+
+
+namespace svx
+{
+
+
+ using namespace ::com::sun::star;
+ using namespace ::com::sun::star::uno;
+ using namespace ::com::sun::star::form;
+ using namespace ::com::sun::star::form::runtime;
+ using namespace ::com::sun::star::lang;
+ using namespace ::com::sun::star::frame;
+ using namespace ::com::sun::star::util;
+ using namespace ::com::sun::star::beans;
+ using namespace ::com::sun::star::container;
+
+
+ typedef sal_uInt16 WhichId;
+
+
+ static SfxSlotId pTextControlSlots[] =
+ {
+ SID_CLIPBOARD_FORMAT_ITEMS,
+ SID_CUT,
+ SID_COPY,
+ SID_PASTE,
+ SID_SELECTALL,
+// SID_ATTR_TABSTOP, /* 2 */
+ SID_ATTR_CHAR_FONT,
+ SID_ATTR_CHAR_POSTURE,
+ SID_ATTR_CHAR_WEIGHT,
+ SID_ATTR_CHAR_SHADOWED,
+ SID_ATTR_CHAR_WORDLINEMODE,
+ SID_ATTR_CHAR_CONTOUR,
+ SID_ATTR_CHAR_STRIKEOUT,
+ SID_ATTR_CHAR_UNDERLINE,
+ SID_ATTR_CHAR_FONTHEIGHT,
+ SID_ATTR_CHAR_COLOR,
+ SID_ATTR_CHAR_KERNING,
+ SID_ATTR_CHAR_LANGUAGE, /* 20 */
+ SID_ATTR_CHAR_ESCAPEMENT,
+ SID_ATTR_PARA_ADJUST, /* 28 */
+ SID_ATTR_PARA_ADJUST_LEFT,
+ SID_ATTR_PARA_ADJUST_RIGHT,
+ SID_ATTR_PARA_ADJUST_CENTER,
+ SID_ATTR_PARA_ADJUST_BLOCK,
+ SID_ATTR_PARA_LINESPACE, /* 33 */
+ SID_ATTR_PARA_LINESPACE_10,
+ SID_ATTR_PARA_LINESPACE_15,
+ SID_ATTR_PARA_LINESPACE_20,
+ SID_ATTR_LRSPACE, /* 48 */
+ SID_ATTR_ULSPACE, /* 49 */
+ SID_ATTR_CHAR_AUTOKERN,
+ SID_SET_SUPER_SCRIPT,
+ SID_SET_SUB_SCRIPT,
+ SID_CHAR_DLG,
+ SID_PARA_DLG,
+// SID_TEXTDIRECTION_LEFT_TO_RIGHT, /* 907 */
+// SID_TEXTDIRECTION_TOP_TO_BOTTOM,
+ SID_ATTR_CHAR_SCALEWIDTH, /* 911 */
+ SID_ATTR_CHAR_RELIEF,
+ SID_ATTR_PARA_LEFT_TO_RIGHT, /* 950 */
+ SID_ATTR_PARA_RIGHT_TO_LEFT,
+ SID_ATTR_CHAR_OVERLINE,
+ 0
+ };
+
+ // slots which we are not responsible for on the SfxShell level, but
+ // need to handle during the "paragraph attributes" and/or "character
+ // attributes" dialogs
+ static SfxSlotId pDialogSlots[] =
+ {
+ SID_ATTR_TABSTOP,
+ SID_ATTR_PARA_HANGPUNCTUATION,
+ SID_ATTR_PARA_FORBIDDEN_RULES,
+ SID_ATTR_PARA_SCRIPTSPACE,
+ SID_ATTR_CHAR_LATIN_LANGUAGE,
+ SID_ATTR_CHAR_CJK_LANGUAGE,
+ SID_ATTR_CHAR_CTL_LANGUAGE,
+ SID_ATTR_CHAR_LATIN_FONT,
+ SID_ATTR_CHAR_CJK_FONT,
+ SID_ATTR_CHAR_CTL_FONT,
+ SID_ATTR_CHAR_LATIN_FONTHEIGHT,
+ SID_ATTR_CHAR_CJK_FONTHEIGHT,
+ SID_ATTR_CHAR_CTL_FONTHEIGHT,
+ SID_ATTR_CHAR_LATIN_WEIGHT,
+ SID_ATTR_CHAR_CJK_WEIGHT,
+ SID_ATTR_CHAR_CTL_WEIGHT,
+ SID_ATTR_CHAR_LATIN_POSTURE,
+ SID_ATTR_CHAR_CJK_POSTURE,
+ SID_ATTR_CHAR_CTL_POSTURE,
+ SID_ATTR_CHAR_EMPHASISMARK,
+ 0
+ };
+
+ typedef ::cppu::WeakImplHelper < css::awt::XFocusListener
+ > FmFocusListenerAdapter_Base;
+ class FmFocusListenerAdapter : public FmFocusListenerAdapter_Base
+ {
+ private:
+ IFocusObserver* m_pObserver;
+ Reference< css::awt::XWindow > m_xWindow;
+
+ public:
+ FmFocusListenerAdapter( const Reference< css::awt::XControl >& _rxControl, IFocusObserver* _pObserver );
+
+ // clean up the instance
+ void dispose();
+
+ protected:
+ virtual ~FmFocusListenerAdapter() override;
+
+ protected:
+ virtual void SAL_CALL focusGained( const css::awt::FocusEvent& e ) override;
+ virtual void SAL_CALL focusLost( const css::awt::FocusEvent& e ) override;
+ virtual void SAL_CALL disposing( const EventObject& Source ) override;
+ };
+
+
+ FmFocusListenerAdapter::FmFocusListenerAdapter( const Reference< css::awt::XControl >& _rxControl, IFocusObserver* _pObserver )
+ :m_pObserver( _pObserver )
+ ,m_xWindow( _rxControl, UNO_QUERY )
+ {
+
+ DBG_ASSERT( m_xWindow.is(), "FmFocusListenerAdapter::FmFocusListenerAdapter: invalid control!" );
+ osl_atomic_increment( &m_refCount );
+ {
+ try
+ {
+ if ( m_xWindow.is() )
+ m_xWindow->addFocusListener( this );
+ }
+ catch( const Exception& )
+ {
+ DBG_UNHANDLED_EXCEPTION("svx");
+ }
+ }
+ osl_atomic_decrement( &m_refCount );
+ }
+
+
+ FmFocusListenerAdapter::~FmFocusListenerAdapter()
+ {
+ acquire();
+ dispose();
+
+ }
+
+
+ void FmFocusListenerAdapter::dispose()
+ {
+ if ( m_xWindow.is() )
+ {
+ m_xWindow->removeFocusListener( this );
+ m_xWindow.clear();
+ }
+ }
+
+
+ void SAL_CALL FmFocusListenerAdapter::focusGained( const css::awt::FocusEvent& e )
+ {
+ if ( m_pObserver )
+ m_pObserver->focusGained( e );
+ }
+
+
+ void SAL_CALL FmFocusListenerAdapter::focusLost( const css::awt::FocusEvent& e )
+ {
+ if ( m_pObserver )
+ m_pObserver->focusLost( e );
+ }
+
+
+ void SAL_CALL FmFocusListenerAdapter::disposing( const EventObject& Source )
+ {
+ DBG_ASSERT( Source.Source == m_xWindow, "FmFocusListenerAdapter::disposing: where did this come from?" );
+ m_xWindow.clear();
+ }
+
+ typedef ::cppu::WeakImplHelper < css::awt::XMouseListener
+ > FmMouseListenerAdapter_Base;
+ class FmMouseListenerAdapter : public FmMouseListenerAdapter_Base
+ {
+ private:
+ IContextRequestObserver* m_pObserver;
+ Reference< css::awt::XWindow > m_xWindow;
+
+ public:
+ FmMouseListenerAdapter( const Reference< css::awt::XControl >& _rxControl, IContextRequestObserver* _pObserver );
+
+ // clean up the instance
+ void dispose();
+
+ protected:
+ virtual ~FmMouseListenerAdapter() override;
+
+ protected:
+ virtual void SAL_CALL mousePressed( const css::awt::MouseEvent& e ) override;
+ virtual void SAL_CALL mouseReleased( const css::awt::MouseEvent& e ) override;
+ virtual void SAL_CALL mouseEntered( const css::awt::MouseEvent& e ) override;
+ virtual void SAL_CALL mouseExited( const css::awt::MouseEvent& e ) override;
+ virtual void SAL_CALL disposing( const EventObject& Source ) override;
+ };
+
+ FmMouseListenerAdapter::FmMouseListenerAdapter( const Reference< css::awt::XControl >& _rxControl, IContextRequestObserver* _pObserver )
+ :m_pObserver( _pObserver )
+ ,m_xWindow( _rxControl, UNO_QUERY )
+ {
+
+ DBG_ASSERT( m_xWindow.is(), "FmMouseListenerAdapter::FmMouseListenerAdapter: invalid control!" );
+ osl_atomic_increment( &m_refCount );
+ {
+ try
+ {
+ if ( m_xWindow.is() )
+ m_xWindow->addMouseListener( this );
+ }
+ catch( const Exception& )
+ {
+ DBG_UNHANDLED_EXCEPTION("svx");
+ }
+ }
+ osl_atomic_decrement( &m_refCount );
+ }
+
+
+ FmMouseListenerAdapter::~FmMouseListenerAdapter()
+ {
+ acquire();
+ dispose();
+
+ }
+
+
+ void FmMouseListenerAdapter::dispose()
+ {
+ if ( m_xWindow.is() )
+ {
+ m_xWindow->removeMouseListener( this );
+ m_xWindow.clear();
+ }
+ }
+
+
+ void SAL_CALL FmMouseListenerAdapter::mousePressed( const css::awt::MouseEvent& _rEvent )
+ {
+ SolarMutexGuard aGuard;
+ // is this a request for a context menu?
+ if ( _rEvent.PopupTrigger )
+ {
+ if ( m_pObserver )
+ m_pObserver->contextMenuRequested();
+ }
+ }
+
+
+ void SAL_CALL FmMouseListenerAdapter::mouseReleased( const css::awt::MouseEvent& /*e*/ )
+ {
+ // not interested in
+ }
+
+
+ void SAL_CALL FmMouseListenerAdapter::mouseEntered( const css::awt::MouseEvent& /*e*/ )
+ {
+ // not interested in
+ }
+
+
+ void SAL_CALL FmMouseListenerAdapter::mouseExited( const css::awt::MouseEvent& /*e*/ )
+ {
+ // not interested in
+ }
+
+
+ void SAL_CALL FmMouseListenerAdapter::disposing( const EventObject& Source )
+ {
+ DBG_ASSERT( Source.Source == m_xWindow, "FmMouseListenerAdapter::disposing: where did this come from?" );
+ m_xWindow.clear();
+ }
+
+
+ //= FmTextControlShell
+
+
+ namespace
+ {
+
+ void lcl_translateUnoStateToItem( SfxSlotId _nSlot, const Any& _rUnoState, SfxItemSet& _rSet )
+ {
+ WhichId nWhich = _rSet.GetPool()->GetWhich( _nSlot );
+ if ( !_rUnoState.hasValue() )
+ {
+ if ( ( _nSlot != SID_CUT )
+ && ( _nSlot != SID_COPY )
+ && ( _nSlot != SID_PASTE )
+ )
+ {
+ _rSet.InvalidateItem( nWhich );
+ }
+ }
+ else
+ {
+ switch ( _rUnoState.getValueType().getTypeClass() )
+ {
+ case TypeClass_BOOLEAN:
+ {
+ bool bState = false;
+ _rUnoState >>= bState;
+ if ( _nSlot == SID_ATTR_PARA_SCRIPTSPACE )
+ _rSet.Put( SvxScriptSpaceItem( bState, nWhich ) );
+ else
+ _rSet.Put( SfxBoolItem( nWhich, bState ) );
+ }
+ break;
+
+ default:
+ {
+ Sequence< PropertyValue > aComplexState;
+ if ( _rUnoState >>= aComplexState )
+ {
+ if ( !aComplexState.hasElements() )
+ _rSet.InvalidateItem( nWhich );
+ else
+ {
+ SfxAllItemSet aAllItems( _rSet );
+ TransformParameters( _nSlot, aComplexState, aAllItems );
+ const SfxPoolItem* pTransformed = aAllItems.GetItem( nWhich );
+ OSL_ENSURE( pTransformed, "lcl_translateUnoStateToItem: non-empty parameter sequence leading to empty item?" );
+ if ( pTransformed )
+ _rSet.Put( *pTransformed );
+ else
+ _rSet.InvalidateItem( nWhich );
+ }
+ }
+ else
+ {
+ OSL_FAIL( "lcl_translateUnoStateToItem: invalid state!" );
+ }
+ }
+ }
+ }
+ }
+
+
+ OUString lcl_getUnoSlotName( SfxSlotId _nSlotId )
+ {
+ OUString sSlotUnoName;
+
+ SfxSlotPool& rSlotPool = SfxSlotPool::GetSlotPool();
+ const SfxSlot* pSlot = rSlotPool.GetSlot( _nSlotId );
+
+ const char* pAsciiUnoName = nullptr;
+ if ( pSlot )
+ {
+ pAsciiUnoName = pSlot->GetUnoName();
+ }
+ else
+ {
+ // some hard-coded slots, which do not have a UNO name at SFX level, but which
+ // we nevertheless need to transport via UNO mechanisms, so we need a name
+ switch ( _nSlotId )
+ {
+ case SID_ATTR_PARA_HANGPUNCTUATION: pAsciiUnoName = "AllowHangingPunctuation"; break;
+ case SID_ATTR_PARA_FORBIDDEN_RULES: pAsciiUnoName = "ApplyForbiddenCharacterRules"; break;
+ case SID_ATTR_PARA_SCRIPTSPACE: pAsciiUnoName = "UseScriptSpacing"; break;
+ }
+ }
+
+ if ( pAsciiUnoName )
+ {
+ sSlotUnoName = ".uno:" + OUString::createFromAscii( pAsciiUnoName );
+ }
+ else
+ {
+ SAL_WARN( "svx", "lcl_getUnoSlotName: invalid slot id, or invalid slot, or no UNO name! "
+ "(slot id: " << _nSlotId << ")");
+ }
+ return sSlotUnoName;
+ }
+
+
+ bool lcl_determineReadOnly( const Reference< css::awt::XControl >& _rxControl )
+ {
+ bool bIsReadOnlyModel = true;
+ try
+ {
+ Reference< XPropertySet > xModelProps;
+ if ( _rxControl.is() )
+ xModelProps.set(_rxControl->getModel(), css::uno::UNO_QUERY);
+ Reference< XPropertySetInfo > xModelPropInfo;
+ if ( xModelProps.is() )
+ xModelPropInfo = xModelProps->getPropertySetInfo();
+
+ if ( !xModelPropInfo.is() || !xModelPropInfo->hasPropertyByName( FM_PROP_READONLY ) )
+ bIsReadOnlyModel = true;
+ else
+ {
+ bool bReadOnly = true;
+ xModelProps->getPropertyValue( FM_PROP_READONLY ) >>= bReadOnly;
+ bIsReadOnlyModel = bReadOnly;
+ }
+ }
+ catch( const Exception& )
+ {
+ DBG_UNHANDLED_EXCEPTION("svx");
+ }
+ return bIsReadOnlyModel;
+ }
+
+
+ vcl::Window* lcl_getWindow( const Reference< css::awt::XControl >& _rxControl )
+ {
+ vcl::Window* pWindow = nullptr;
+ try
+ {
+ Reference< css::awt::XWindowPeer > xControlPeer;
+ if ( _rxControl.is() )
+ xControlPeer = _rxControl->getPeer();
+ if ( xControlPeer.is() )
+ pWindow = VCLUnoHelper::GetWindow( xControlPeer );
+ }
+ catch( const Exception& )
+ {
+ DBG_UNHANDLED_EXCEPTION("svx");
+ }
+
+ return pWindow;
+ }
+
+
+ bool lcl_isRichText( const Reference< css::awt::XControl >& _rxControl )
+ {
+ if ( !_rxControl.is() )
+ return false;
+
+ bool bIsRichText = false;
+ try
+ {
+ Reference< XPropertySet > xModelProps( _rxControl->getModel(), UNO_QUERY );
+ Reference< XPropertySetInfo > xPSI;
+ if ( xModelProps.is() )
+ xPSI = xModelProps->getPropertySetInfo();
+ OUString sRichTextPropertyName = "RichText";
+ if ( xPSI.is() && xPSI->hasPropertyByName( sRichTextPropertyName ) )
+ {
+ OSL_VERIFY( xModelProps->getPropertyValue( sRichTextPropertyName ) >>= bIsRichText );
+ }
+ }
+ catch( const Exception& )
+ {
+ DBG_UNHANDLED_EXCEPTION("svx");
+ }
+ return bIsRichText;
+ }
+ }
+
+
+ FmTextControlShell::FmTextControlShell( SfxViewFrame* _pFrame )
+ :m_bActiveControl( false )
+ ,m_bActiveControlIsReadOnly( true )
+ ,m_bActiveControlIsRichText( false )
+ ,m_pViewFrame( _pFrame )
+ ,m_rBindings( _pFrame->GetBindings() )
+ ,m_aClipboardInvalidation("svx FmTextControlShell m_aClipboardInvalidation")
+ ,m_bNeedClipboardInvalidation( true )
+ {
+ m_aClipboardInvalidation.SetInvokeHandler( LINK( this, FmTextControlShell, OnInvalidateClipboard ) );
+ m_aClipboardInvalidation.SetTimeout( 200 );
+ }
+
+
+ FmTextControlShell::~FmTextControlShell()
+ {
+ dispose();
+ }
+
+
+ IMPL_LINK_NOARG( FmTextControlShell, OnInvalidateClipboard, Timer*, void )
+ {
+ if ( m_bNeedClipboardInvalidation )
+ {
+ SAL_INFO("svx.form", "invalidating clipboard slots" );
+ m_rBindings.Invalidate( SID_CUT );
+ m_rBindings.Invalidate( SID_COPY );
+ m_rBindings.Invalidate( SID_PASTE );
+ m_bNeedClipboardInvalidation = false;
+ }
+ }
+
+
+ void FmTextControlShell::transferFeatureStatesToItemSet( ControlFeatures& _rDispatchers, SfxAllItemSet& _rSet, bool _bTranslateLatin )
+ {
+ SfxItemPool& rPool = *_rSet.GetPool();
+
+ for (const auto& rFeature : _rDispatchers)
+ {
+ SfxSlotId nSlotId( rFeature.first );
+#if OSL_DEBUG_LEVEL > 0
+ OUString sUnoSlotName;
+ if ( SfxGetpApp() )
+ sUnoSlotName = lcl_getUnoSlotName( nSlotId );
+ else
+ sUnoSlotName = "<unknown>";
+ OString sUnoSlotNameAscii = "\"" +
+ OString( sUnoSlotName.getStr(), sUnoSlotName.getLength(), RTL_TEXTENCODING_ASCII_US ) +
+ "\"";
+#endif
+
+ if ( _bTranslateLatin )
+ {
+ // A rich text control offers a dispatcher for the "Font" slot/feature.
+ // Sadly, the semantics of the dispatches is that the feature "Font" depends
+ // on the current cursor position: If it's on latin text, it's the "latin font"
+ // which is set up at the control. If it's on CJK text, it's the "CJK font", and
+ // equivalent for "CTL font".
+ // The same holds for some other font related features/slots.
+ // Thus, we have separate dispatches for "Latin Font", "Latin Font Size", etc,
+ // which are only "virtual", in a sense that there exist no item with this id.
+ // So when we encounter such a dispatcher for, say, "Latin Font", we need to
+ // put an item into the set which has the "Font" id.
+
+ switch ( nSlotId )
+ {
+ case SID_ATTR_CHAR_LATIN_FONT: nSlotId = SID_ATTR_CHAR_FONT; break;
+ case SID_ATTR_CHAR_LATIN_FONTHEIGHT:nSlotId = SID_ATTR_CHAR_FONTHEIGHT; break;
+ case SID_ATTR_CHAR_LATIN_LANGUAGE: nSlotId = SID_ATTR_CHAR_LANGUAGE; break;
+ case SID_ATTR_CHAR_LATIN_POSTURE: nSlotId = SID_ATTR_CHAR_POSTURE; break;
+ case SID_ATTR_CHAR_LATIN_WEIGHT: nSlotId = SID_ATTR_CHAR_WEIGHT; break;
+ }
+ }
+
+ WhichId nWhich = rPool.GetWhich( nSlotId );
+ bool bIsInPool = rPool.IsInRange( nWhich );
+ if ( bIsInPool )
+ {
+#if OSL_DEBUG_LEVEL > 0
+ bool bFeatureIsEnabled = rFeature.second->isFeatureEnabled();
+ OString sMessage = "found a feature state for " + sUnoSlotNameAscii;
+ if ( !bFeatureIsEnabled )
+ sMessage += " (disabled)";
+ SAL_INFO("svx.form", sMessage );
+#endif
+
+ lcl_translateUnoStateToItem( nSlotId, rFeature.second->getFeatureState(), _rSet );
+ }
+#if OSL_DEBUG_LEVEL > 0
+ else
+ {
+ SAL_WARN("svx.form", "found a feature state for " << sUnoSlotNameAscii << ", but could not translate it into an item!" );
+ }
+#endif
+ }
+ }
+
+
+ void FmTextControlShell::executeAttributeDialog( AttributeSet _eSet, SfxRequest& rReq )
+ {
+ const SvxFontListItem* pFontList = dynamic_cast<const SvxFontListItem*>( m_pViewFrame->GetObjectShell()->GetItem( SID_ATTR_CHAR_FONTLIST ) );
+ DBG_ASSERT( pFontList, "FmTextControlShell::executeAttributeDialog: no font list item!" );
+ if ( !pFontList )
+ return;
+
+ rtl::Reference<SfxItemPool> pPool(EditEngine::CreatePool());
+ pPool->FreezeIdRanges();
+ std::optional< SfxItemSet > xPureItems(( SfxItemSet( *pPool ) ));
+
+ // put the current states of the items into the set
+ std::optional<SfxAllItemSet> xCurrentItems(( SfxAllItemSet( *xPureItems ) ));
+ transferFeatureStatesToItemSet( m_aControlFeatures, *xCurrentItems, false );
+
+ // additional items, which we are not responsible for at the SfxShell level,
+ // but which need to be forwarded to the dialog, anyway
+ ControlFeatures aAdditionalFestures;
+ fillFeatureDispatchers( m_xActiveControl, pDialogSlots, aAdditionalFestures );
+ transferFeatureStatesToItemSet( aAdditionalFestures, *xCurrentItems, true );
+
+ std::unique_ptr<SfxTabDialogController> xDialog;
+ if (_eSet == eCharAttribs)
+ xDialog = std::make_unique<TextControlCharAttribDialog>(rReq.GetFrameWeld(), *xCurrentItems, *pFontList);
+ else
+ xDialog = std::make_unique<TextControlParaAttribDialog>(rReq.GetFrameWeld(), *xCurrentItems);
+ if ( RET_OK == xDialog->run() )
+ {
+ const SfxItemSet& rModifiedItems = *xDialog->GetOutputItemSet();
+ for ( WhichId nWhich = pPool->GetFirstWhich(); nWhich <= pPool->GetLastWhich(); ++nWhich )
+ {
+ if ( rModifiedItems.GetItemState( nWhich ) == SfxItemState::SET )
+ {
+ SfxSlotId nSlotForItemSet = pPool->GetSlotId( nWhich );
+ const SfxPoolItem* pModifiedItem = rModifiedItems.GetItem( nWhich );
+
+
+ SfxSlotId nSlotForDispatcher = nSlotForItemSet;
+ switch ( nSlotForDispatcher )
+ {
+ case SID_ATTR_CHAR_FONT: nSlotForDispatcher = SID_ATTR_CHAR_LATIN_FONT; break;
+ case SID_ATTR_CHAR_FONTHEIGHT:nSlotForDispatcher = SID_ATTR_CHAR_LATIN_FONTHEIGHT; break;
+ case SID_ATTR_CHAR_LANGUAGE: nSlotForDispatcher = SID_ATTR_CHAR_LATIN_LANGUAGE; break;
+ case SID_ATTR_CHAR_POSTURE: nSlotForDispatcher = SID_ATTR_CHAR_LATIN_POSTURE; break;
+ case SID_ATTR_CHAR_WEIGHT: nSlotForDispatcher = SID_ATTR_CHAR_LATIN_WEIGHT; break;
+ }
+
+ // do we already have a dispatcher for this slot/feature?
+ ControlFeatures::const_iterator aFeaturePos = m_aControlFeatures.find( nSlotForDispatcher );
+ bool bFound = aFeaturePos != m_aControlFeatures.end( );
+
+ if ( !bFound )
+ {
+ aFeaturePos = aAdditionalFestures.find( nSlotForDispatcher );
+ bFound = aFeaturePos != aAdditionalFestures.end( );
+ }
+
+ if ( bFound )
+ {
+ Sequence< PropertyValue > aArgs;
+ // temporarily put the modified item into a "clean" set,
+ // and let TransformItems calc the respective UNO parameters
+ xPureItems->Put( *pModifiedItem );
+ TransformItems( nSlotForItemSet, *xPureItems, aArgs );
+ xPureItems->ClearItem( nWhich );
+
+ if ( ( nSlotForItemSet == SID_ATTR_PARA_HANGPUNCTUATION )
+ || ( nSlotForItemSet == SID_ATTR_PARA_FORBIDDEN_RULES )
+ || ( nSlotForItemSet == SID_ATTR_PARA_SCRIPTSPACE )
+ )
+ {
+ // these are no UNO slots, they need special handling since TransformItems cannot
+ // handle them
+ DBG_ASSERT( !aArgs.hasElements(), "FmTextControlShell::executeAttributeDialog: these are no UNO slots - are they?" );
+
+ const SfxBoolItem* pBoolItem = dynamic_cast<const SfxBoolItem*>( pModifiedItem );
+ DBG_ASSERT( pBoolItem, "FmTextControlShell::executeAttributeDialog: no bool item?!" );
+ if ( pBoolItem )
+ {
+ aArgs = { comphelper::makePropertyValue("Enable",
+ pBoolItem->GetValue()) };
+ }
+ }
+
+ // dispatch this
+ aFeaturePos->second->dispatch( aArgs );
+ }
+ #if OSL_DEBUG_LEVEL > 0
+ else
+ {
+ OUString sUnoSlotName = lcl_getUnoSlotName( nSlotForItemSet );
+ if ( sUnoSlotName.isEmpty() )
+ sUnoSlotName = "unknown (no SfxSlot)";
+ SAL_WARN( "svx", "FmTextControShell::executeAttributeDialog: Could not handle the following item:"
+ "\n SlotID: " << nSlotForItemSet
+ << "\n WhichID: " << nWhich
+ << "\n UNO name: " << sUnoSlotName );
+ }
+ #endif
+ }
+ }
+ rReq.Done( rModifiedItems );
+ }
+
+ xDialog.reset();
+ xCurrentItems.reset();
+ xPureItems.reset();
+ pPool.clear();
+ }
+
+
+ void FmTextControlShell::executeSelectAll( )
+ {
+ try
+ {
+ if ( m_xActiveTextComponent.is() )
+ {
+ sal_Int32 nTextLen = m_xActiveTextComponent->getText().getLength();
+ m_xActiveTextComponent->setSelection( css::awt::Selection( 0, nTextLen ) );
+ }
+ }
+ catch( const Exception& )
+ {
+ DBG_UNHANDLED_EXCEPTION("svx");
+ }
+ }
+
+
+ void FmTextControlShell::executeClipboardSlot( SfxSlotId _nSlot )
+ {
+ try
+ {
+ if ( m_xActiveTextComponent.is() )
+ {
+ switch ( _nSlot )
+ {
+ case SID_COPY:
+ case SID_CUT:
+ {
+ OUString sSelectedText( m_xActiveTextComponent->getSelectedText() );
+ ::svt::OStringTransfer::CopyString( sSelectedText, lcl_getWindow( m_xActiveControl ) );
+ if ( SID_CUT == _nSlot )
+ {
+ css::awt::Selection aSelection( m_xActiveTextComponent->getSelection() );
+ m_xActiveTextComponent->insertText( aSelection, OUString() );
+ }
+ }
+ break;
+ case SID_PASTE:
+ {
+ OUString sClipboardContent;
+ OSL_VERIFY( ::svt::OStringTransfer::PasteString( sClipboardContent, lcl_getWindow( m_xActiveControl ) ) );
+ css::awt::Selection aSelection( m_xActiveTextComponent->getSelection() );
+ m_xActiveTextComponent->insertText( aSelection, sClipboardContent );
+ }
+ break;
+ default:
+ OSL_FAIL( "FmTextControlShell::executeClipboardSlot: invalid slot!" );
+ }
+ }
+ }
+ catch( const Exception& )
+ {
+ DBG_UNHANDLED_EXCEPTION("svx");
+ }
+ }
+
+
+ void FmTextControlShell::ExecuteTextAttribute( SfxRequest& _rReq )
+ {
+ SfxSlotId nSlot = _rReq.GetSlot();
+
+ ControlFeatures::const_iterator aFeaturePos = m_aControlFeatures.find( nSlot );
+ if ( aFeaturePos == m_aControlFeatures.end() )
+ {
+ // special slots
+ switch ( nSlot )
+ {
+ case SID_CHAR_DLG:
+ executeAttributeDialog( eCharAttribs, _rReq );
+ break;
+
+ case SID_PARA_DLG:
+ executeAttributeDialog( eParaAttribs, _rReq );
+ break;
+
+ case SID_SELECTALL:
+ executeSelectAll();
+ break;
+
+ case SID_CUT:
+ case SID_COPY:
+ case SID_PASTE:
+ executeClipboardSlot( nSlot );
+ break;
+
+ default:
+ DBG_ASSERT( aFeaturePos != m_aControlFeatures.end(), "FmTextControShell::ExecuteTextAttribute: I have no such dispatcher, and cannot handle it at all!" );
+ return;
+ }
+ }
+ else
+ {
+ // slots which are dispatched to the control
+
+ switch ( nSlot )
+ {
+ case SID_ATTR_CHAR_STRIKEOUT:
+ case SID_ATTR_CHAR_UNDERLINE:
+ case SID_ATTR_CHAR_OVERLINE:
+ {
+ SfxItemSet aToggled( *_rReq.GetArgs() );
+
+ lcl_translateUnoStateToItem( nSlot, aFeaturePos->second->getFeatureState(), aToggled );
+ WhichId nWhich = aToggled.GetPool()->GetWhich( nSlot );
+ const SfxPoolItem* pItem = aToggled.GetItem( nWhich );
+ if ( ( SID_ATTR_CHAR_UNDERLINE == nSlot ) || ( SID_ATTR_CHAR_OVERLINE == nSlot ) )
+ {
+ const SvxTextLineItem* pTextLine = dynamic_cast<const SvxTextLineItem*>( pItem );
+ DBG_ASSERT( pTextLine, "FmTextControlShell::ExecuteTextAttribute: ooops - no underline/overline item!" );
+ if ( pTextLine )
+ {
+ FontLineStyle eTL = pTextLine->GetLineStyle();
+ if ( SID_ATTR_CHAR_UNDERLINE == nSlot ) {
+ aToggled.Put( SvxUnderlineItem( eTL == LINESTYLE_SINGLE ? LINESTYLE_NONE : LINESTYLE_SINGLE, nWhich ) );
+ } else {
+ aToggled.Put( SvxOverlineItem( eTL == LINESTYLE_SINGLE ? LINESTYLE_NONE : LINESTYLE_SINGLE, nWhich ) );
+ }
+ }
+ }
+ else
+ {
+ const SvxCrossedOutItem* pCrossedOut = dynamic_cast<const SvxCrossedOutItem*>( pItem );
+ DBG_ASSERT( pCrossedOut, "FmTextControlShell::ExecuteTextAttribute: ooops - no CrossedOut item!" );
+ if ( pCrossedOut )
+ {
+ FontStrikeout eFS = pCrossedOut->GetStrikeout();
+ aToggled.Put( SvxCrossedOutItem( eFS == STRIKEOUT_SINGLE ? STRIKEOUT_NONE : STRIKEOUT_SINGLE, nWhich ) );
+ }
+ }
+
+ Sequence< PropertyValue > aArguments;
+ TransformItems( nSlot, aToggled, aArguments );
+ aFeaturePos->second->dispatch( aArguments );
+ }
+ break;
+
+ case SID_ATTR_CHAR_FONTHEIGHT:
+ case SID_ATTR_CHAR_FONT:
+ case SID_ATTR_CHAR_POSTURE:
+ case SID_ATTR_CHAR_WEIGHT:
+ case SID_ATTR_CHAR_SHADOWED:
+ case SID_ATTR_CHAR_CONTOUR:
+ case SID_SET_SUPER_SCRIPT:
+ case SID_SET_SUB_SCRIPT:
+ {
+ const SfxItemSet* pArgs = _rReq.GetArgs();
+ Sequence< PropertyValue > aArgs;
+ if ( pArgs )
+ TransformItems( nSlot, *pArgs, aArgs );
+ aFeaturePos->second->dispatch( aArgs );
+ }
+ break;
+
+ default:
+ if ( aFeaturePos->second->isFeatureEnabled() )
+ aFeaturePos->second->dispatch();
+ break;
+ }
+ }
+ _rReq.Done();
+ }
+
+
+ void FmTextControlShell::GetTextAttributeState( SfxItemSet& _rSet )
+ {
+ SfxWhichIter aIter( _rSet );
+ sal_uInt16 nSlot = aIter.FirstWhich();
+ while ( nSlot )
+ {
+ if ( ( nSlot == SID_ATTR_PARA_LEFT_TO_RIGHT )
+ || ( nSlot == SID_ATTR_PARA_RIGHT_TO_LEFT )
+ )
+ {
+ if ( !SvtCTLOptions().IsCTLFontEnabled() )
+ {
+ _rSet.DisableItem( nSlot );
+ nSlot = aIter.NextWhich();
+ continue;
+ }
+ }
+
+ ControlFeatures::const_iterator aFeaturePos = m_aControlFeatures.find( nSlot );
+ if ( aFeaturePos != m_aControlFeatures.end() )
+ {
+ if ( aFeaturePos->second->isFeatureEnabled() )
+ lcl_translateUnoStateToItem( nSlot, aFeaturePos->second->getFeatureState(), _rSet );
+ else
+ _rSet.DisableItem( nSlot );
+ }
+ else
+ {
+ bool bDisable = false;
+
+ bool bNeedWriteableControl = false;
+ bool bNeedTextComponent = false;
+ bool bNeedSelection = false;
+
+ switch ( nSlot )
+ {
+ case SID_CHAR_DLG:
+ case SID_PARA_DLG:
+ bDisable |= m_aControlFeatures.empty();
+ bNeedWriteableControl = true;
+ break;
+
+ case SID_CUT:
+ bNeedSelection = true;
+ bNeedTextComponent = true;
+ bNeedWriteableControl = true;
+ SAL_INFO("svx.form", "need to invalidate again" );
+ m_bNeedClipboardInvalidation = true;
+ break;
+
+ case SID_PASTE:
+ {
+ vcl::Window* pActiveControlVCLWindow = lcl_getWindow( m_xActiveControl );
+ if ( pActiveControlVCLWindow )
+ {
+ TransferableDataHelper aDataHelper( TransferableDataHelper::CreateFromSystemClipboard( pActiveControlVCLWindow) );
+ bDisable |= !aDataHelper.HasFormat( SotClipboardFormatId::STRING );
+ }
+ else
+ bDisable = true;
+
+ bNeedTextComponent = true;
+ bNeedWriteableControl = true;
+ }
+ break;
+
+ case SID_COPY:
+ bNeedTextComponent = true;
+ bNeedSelection = true;
+ break;
+
+ case SID_SELECTALL:
+ bNeedTextComponent = true;
+ break;
+
+ default:
+ // slot is unknown at all
+ bDisable = true;
+ break;
+ }
+ SAL_WARN_IF( bNeedSelection && !bNeedTextComponent, "svx.form", "FmTextControlShell::GetTextAttributeState: bNeedSelection should imply bNeedTextComponent!" );
+
+ if ( !bDisable && bNeedWriteableControl )
+ bDisable |= !IsActiveControl( ) || m_bActiveControlIsReadOnly;
+
+ if ( !bDisable && bNeedTextComponent )
+ bDisable |= !m_xActiveTextComponent.is();
+
+ if ( !bDisable && bNeedSelection )
+ {
+ css::awt::Selection aSelection = m_xActiveTextComponent->getSelection();
+ bDisable |= aSelection.Min == aSelection.Max;
+ }
+
+ if ( bDisable )
+ _rSet.DisableItem( nSlot );
+ }
+
+ nSlot = aIter.NextWhich();
+ }
+ }
+
+
+ bool FmTextControlShell::IsActiveControl( bool _bCountRichTextOnly ) const
+ {
+ if ( _bCountRichTextOnly && !m_bActiveControlIsRichText )
+ return false;
+
+ return m_bActiveControl;
+ }
+
+
+ void FmTextControlShell::dispose()
+ {
+ if ( IsActiveControl() )
+ controlDeactivated();
+ if ( isControllerListening() )
+ stopControllerListening();
+ }
+
+
+ void FmTextControlShell::designModeChanged()
+ {
+ m_rBindings.Invalidate( pTextControlSlots );
+ }
+
+
+ void FmTextControlShell::formActivated( const Reference< runtime::XFormController >& _rxController )
+ {
+#if OSL_DEBUG_LEVEL > 0
+ SAL_INFO("svx.form", "0x" << OUString::number( reinterpret_cast<sal_IntPtr>(_rxController.get()), 16 ));
+#endif
+
+ DBG_ASSERT( _rxController.is(), "FmTextControlShell::formActivated: invalid controller!" );
+ if ( !_rxController.is() )
+ return;
+
+ // sometimes, a form controller notifies activations, even if it's already activated
+ if ( m_xActiveController == _rxController )
+ return;
+
+ try
+ {
+ startControllerListening( _rxController );
+ controlActivated( _rxController->getCurrentControl() );
+ }
+ catch( const Exception& )
+ {
+ DBG_UNHANDLED_EXCEPTION("svx");
+ }
+ }
+
+
+ void FmTextControlShell::formDeactivated( const Reference< runtime::XFormController >& _rxController )
+ {
+ SAL_INFO("svx.form", "0x" << OUString::number( reinterpret_cast<sal_IntPtr>(_rxController.get()), 16 ));
+
+ if ( IsActiveControl() )
+ controlDeactivated();
+ if ( isControllerListening() )
+ stopControllerListening();
+ }
+
+
+ void FmTextControlShell::startControllerListening( const Reference< runtime::XFormController >& _rxController )
+ {
+ OSL_PRECOND( _rxController.is(), "FmTextControlShell::startControllerListening: invalid controller!" );
+ if ( !_rxController.is() )
+ return;
+
+ OSL_PRECOND( !isControllerListening(), "FmTextControlShell::startControllerListening: already listening!" );
+ if ( isControllerListening() )
+ stopControllerListening( );
+ DBG_ASSERT( !isControllerListening(), "FmTextControlShell::startControllerListening: inconsistence!" );
+
+ try
+ {
+ const Sequence< Reference< css::awt::XControl > > aControls( _rxController->getControls() );
+ m_aControlObservers.resize( 0 );
+ m_aControlObservers.reserve( aControls.getLength() );
+
+ std::transform(aControls.begin(), aControls.end(), std::back_inserter(m_aControlObservers),
+ [this](const Reference< css::awt::XControl >& rControl) -> FocusListenerAdapter {
+ return FocusListenerAdapter( new FmFocusListenerAdapter( rControl, this ) ); });
+ }
+ catch( const Exception& )
+ {
+ DBG_UNHANDLED_EXCEPTION("svx");
+ }
+
+ m_xActiveController = _rxController;
+ }
+
+
+ void FmTextControlShell::stopControllerListening( )
+ {
+ OSL_PRECOND( isControllerListening(), "FmTextControlShell::stopControllerListening: inconsistence!" );
+
+ // dispose all listeners associated with the controls of the active controller
+ for (auto& rpObserver : m_aControlObservers)
+ {
+ rpObserver->dispose();
+ }
+
+ FocusListenerAdapters().swap(m_aControlObservers);
+
+ m_xActiveController.clear();
+ }
+
+
+ void FmTextControlShell::implClearActiveControlRef()
+ {
+ // no more features for this control
+ for (auto& rFeature : m_aControlFeatures)
+ {
+ rFeature.second->dispose();
+ }
+
+ ControlFeatures().swap(m_aControlFeatures);
+
+ if ( m_aContextMenuObserver )
+ {
+ m_aContextMenuObserver->dispose();
+ m_aContextMenuObserver = MouseListenerAdapter();
+ }
+
+ if ( m_xActiveTextComponent.is() )
+ {
+ SAL_INFO("svx.form", "stopping timer for clipboard invalidation" );
+ m_aClipboardInvalidation.Stop();
+ }
+ // no more active control
+ m_xActiveControl.clear();
+ m_xActiveTextComponent.clear();
+ m_bActiveControlIsReadOnly = true;
+ m_bActiveControlIsRichText = false;
+ m_bActiveControl = false;
+ }
+
+
+ void FmTextControlShell::controlDeactivated( )
+ {
+ DBG_ASSERT( IsActiveControl(), "FmTextControlShell::controlDeactivated: no active control!" );
+
+ m_bActiveControl = false;
+
+ m_rBindings.Invalidate( pTextControlSlots );
+ }
+
+
+ void FmTextControlShell::controlActivated( const Reference< css::awt::XControl >& _rxControl )
+ {
+ // ensure that all knittings with the previously active control are lost
+ if ( m_xActiveControl.is() )
+ implClearActiveControlRef();
+ DBG_ASSERT( m_aControlFeatures.empty(), "FmTextControlShell::controlActivated: should have no dispatchers when I'm here!" );
+
+#if OSL_DEBUG_LEVEL > 0
+ {
+ Sequence< Reference< css::awt::XControl > > aActiveControls;
+ if ( m_xActiveController.is() )
+ aActiveControls = m_xActiveController->getControls();
+
+ bool bFoundThisControl = false;
+
+ const Reference< css::awt::XControl >* pControls = aActiveControls.getConstArray();
+ const Reference< css::awt::XControl >* pControlsEnd = pControls + aActiveControls.getLength();
+ for ( ; ( pControls != pControlsEnd ) && !bFoundThisControl; ++pControls )
+ {
+ if ( *pControls == _rxControl )
+ bFoundThisControl = true;
+ }
+ DBG_ASSERT( bFoundThisControl, "FmTextControlShell::controlActivated: only controls which belong to the active controller can be activated!" );
+ }
+#endif
+ // ask the control for dispatchers for our text-related slots
+ fillFeatureDispatchers( _rxControl, pTextControlSlots, m_aControlFeatures );
+
+ // remember this control
+ m_xActiveControl = _rxControl;
+ m_xActiveTextComponent.set(_rxControl, css::uno::UNO_QUERY);
+ m_bActiveControlIsReadOnly = lcl_determineReadOnly( m_xActiveControl );
+ m_bActiveControlIsRichText = lcl_isRichText( m_xActiveControl );
+
+ // if we found a rich text control, we need context menu support
+ if ( m_bActiveControlIsRichText )
+ {
+ DBG_ASSERT( !m_aContextMenuObserver, "FmTextControlShell::controlActivated: already have an observer!" );
+ m_aContextMenuObserver = MouseListenerAdapter( new FmMouseListenerAdapter( _rxControl, this ) );
+ }
+
+ if ( m_xActiveTextComponent.is() )
+ {
+ SAL_INFO("svx.form", "starting timer for clipboard invalidation" );
+ m_aClipboardInvalidation.Start();
+ }
+
+ m_bActiveControl = true;
+
+ m_rBindings.Invalidate( pTextControlSlots );
+
+ if ( m_pViewFrame )
+ m_pViewFrame->UIFeatureChanged();
+
+ // don't call the activation handler if we don't have any slots we can serve
+ // The activation handler is used to put the shell on the top of the dispatcher stack,
+ // so it's preferred when slots are distributed.
+ // Note that this is a slight hack, to prevent that we grab slots from the SfxDispatcher
+ // which should be served by other shells (e.g. Cut/Copy/Paste).
+ // A real solution would be a forwarding-mechanism for slots: We should be on the top
+ // if we're active, but if we cannot handle the slot, then we need to tell the dispatcher
+ // to skip our shell, and pass the slot to the next one. However, this mechanism is not
+ // not in place in SFX.
+ // Another possibility would be to have dedicated shells for the slots which we might
+ // or might not be able to serve. However, this could probably increase the number of
+ // shells too much (In theory, nearly every slot could have an own shell then).
+
+ // #i51621# / 2005-08-19 / frank.schoenheit@sun.com
+ // bool bHaveAnyServeableSlots = m_xActiveTextComponent.is() || !m_aControlFeatures.empty();
+ // LEM: not calling m_aControlActivatonHandler causes fdo#63695, so disable this hack for now.
+ m_aControlActivationHandler.Call( nullptr );
+
+ m_bNeedClipboardInvalidation = true;
+ }
+
+
+ void FmTextControlShell::fillFeatureDispatchers(const Reference< css::awt::XControl >& _rxControl, SfxSlotId* _pZeroTerminatedSlots,
+ ControlFeatures& _rDispatchers)
+ {
+ Reference< XDispatchProvider > xProvider( _rxControl, UNO_QUERY );
+ SfxApplication* pApplication = SfxGetpApp();
+ DBG_ASSERT( pApplication, "FmTextControlShell::fillFeatureDispatchers: no SfxApplication!" );
+ if ( xProvider.is() && pApplication )
+ {
+ SfxSlotId* pSlots = _pZeroTerminatedSlots;
+ while ( *pSlots )
+ {
+ rtl::Reference<FmTextControlFeature> pDispatcher = implGetFeatureDispatcher( xProvider, pApplication, *pSlots );
+ if ( pDispatcher )
+ _rDispatchers.emplace( *pSlots, pDispatcher );
+
+ ++pSlots;
+ }
+ }
+ }
+
+
+ rtl::Reference<FmTextControlFeature> FmTextControlShell::implGetFeatureDispatcher( const Reference< XDispatchProvider >& _rxProvider, SfxApplication const * _pApplication, SfxSlotId _nSlot )
+ {
+ OSL_PRECOND( _rxProvider.is() && _pApplication, "FmTextControlShell::implGetFeatureDispatcher: invalid arg(s)!" );
+ URL aFeatureURL;
+ aFeatureURL.Complete = lcl_getUnoSlotName( _nSlot );
+ try
+ {
+ if ( !m_xURLTransformer.is() )
+ {
+ m_xURLTransformer = util::URLTransformer::create( ::comphelper::getProcessComponentContext() );
+ }
+ if ( m_xURLTransformer.is() )
+ m_xURLTransformer->parseStrict( aFeatureURL );
+ }
+ catch( const Exception& )
+ {
+ DBG_UNHANDLED_EXCEPTION("svx");
+ }
+ Reference< XDispatch > xDispatcher = _rxProvider->queryDispatch( aFeatureURL, OUString(), 0xFF );
+ if ( xDispatcher.is() )
+ return new FmTextControlFeature( xDispatcher, aFeatureURL, _nSlot, this );
+ return nullptr;
+ }
+
+
+ void FmTextControlShell::Invalidate( SfxSlotId _nSlot )
+ {
+ m_rBindings.Invalidate( _nSlot );
+ // despite this method being called "Invalidate", we also update here - this gives more immediate
+ // feedback in the UI
+ m_rBindings.Update( _nSlot );
+ }
+
+
+ void FmTextControlShell::focusGained( const css::awt::FocusEvent& _rEvent )
+ {
+ Reference< css::awt::XControl > xControl( _rEvent.Source, UNO_QUERY );
+
+#if OSL_DEBUG_LEVEL > 0
+ SAL_INFO("svx.form", "0x" << OUString::number( reinterpret_cast<sal_IntPtr>(xControl.get()), 16 ));
+#endif
+
+ DBG_ASSERT( xControl.is(), "FmTextControlShell::focusGained: suspicious focus event!" );
+ if ( xControl.is() )
+ controlActivated( xControl );
+ }
+
+
+ void FmTextControlShell::focusLost( const css::awt::FocusEvent& _rEvent )
+ {
+ Reference< css::awt::XControl > xControl( _rEvent.Source, UNO_QUERY );
+
+#if OSL_DEBUG_LEVEL > 0
+ SAL_INFO("svx.form", "0x" << OUString::number( reinterpret_cast<sal_IntPtr>(xControl.get()), 16 ));
+#endif
+
+ m_bActiveControl = false;
+ }
+
+
+ void FmTextControlShell::ForgetActiveControl()
+ {
+ implClearActiveControlRef();
+ }
+
+
+ void FmTextControlShell::contextMenuRequested()
+ {
+ m_rBindings.GetDispatcher()->ExecutePopup( "formrichtext" );
+ }
+
+
+}
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/form/fmtools.cxx b/svx/source/form/fmtools.cxx
new file mode 100644
index 000000000..1089754d5
--- /dev/null
+++ b/svx/source/form/fmtools.cxx
@@ -0,0 +1,371 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+
+#include <fmprop.hxx>
+#include <fmservs.hxx>
+#include <svx/fmtools.hxx>
+#include <svx/svdobjkind.hxx>
+
+#include <com/sun/star/beans/XPropertySet.hpp>
+#include <com/sun/star/container/XIndexAccess.hpp>
+#include <com/sun/star/io/XPersistObject.hpp>
+#include <com/sun/star/lang/XServiceInfo.hpp>
+#include <com/sun/star/sdb/ErrorCondition.hpp>
+#include <com/sun/star/sdb/ErrorMessageDialog.hpp>
+#include <com/sun/star/sdb/SQLContext.hpp>
+#include <com/sun/star/sdb/SQLErrorEvent.hpp>
+#include <com/sun/star/sdb/XCompletedConnection.hpp>
+#include <com/sun/star/sdb/XResultSetAccess.hpp>
+#include <com/sun/star/sdbc/XRowSet.hpp>
+#include <com/sun/star/ui/dialogs/XExecutableDialog.hpp>
+#include <com/sun/star/util/Language.hpp>
+
+#include <comphelper/processfactory.hxx>
+#include <comphelper/property.hxx>
+#include <comphelper/types.hxx>
+#include <tools/debug.hxx>
+#include <tools/diagnose_ex.h>
+
+using namespace ::com::sun::star::uno;
+using namespace ::com::sun::star::util;
+using namespace ::com::sun::star::lang;
+using namespace ::com::sun::star::awt;
+using namespace ::com::sun::star::beans;
+using namespace ::com::sun::star::container;
+using namespace ::com::sun::star::ui::dialogs;
+using namespace ::com::sun::star::sdbc;
+using namespace ::com::sun::star::sdbcx;
+using namespace ::com::sun::star::sdb;
+using namespace ::com::sun::star::task;
+using namespace ::com::sun::star::form;
+using namespace ::svxform;
+
+
+namespace
+{
+ bool lcl_shouldDisplayError( const Any& _rError )
+ {
+ SQLException aError;
+ if ( !( _rError >>= aError ) )
+ return true;
+
+ if ( ! aError.Message.startsWith( "[OOoBase]" ) )
+ // it is an exception *not* thrown by an OOo Base core component
+ return true;
+
+ // the only exception we do not display ATM is a RowSetVetoException, which
+ // has been raised because an XRowSetApprovalListener vetoed a change
+ if ( aError.ErrorCode + ErrorCondition::ROW_SET_OPERATION_VETOED == 0 )
+ return false;
+
+ // everything else is to be displayed
+ return true;
+ }
+}
+
+void displayException(const Any& _rExcept, const css::uno::Reference<css::awt::XWindow>& rParent)
+{
+ // check whether we need to display it
+ if ( !lcl_shouldDisplayError( _rExcept ) )
+ return;
+
+ try
+ {
+ Reference< XExecutableDialog > xErrorDialog = ErrorMessageDialog::create(::comphelper::getProcessComponentContext(), "", rParent, _rExcept);
+ xErrorDialog->execute();
+ }
+ catch(const Exception&)
+ {
+ TOOLS_WARN_EXCEPTION("svx.form", "could not display the error message!");
+ }
+}
+
+void displayException(const css::sdbc::SQLException& _rExcept, const css::uno::Reference<css::awt::XWindow>& rParent)
+{
+ displayException(Any(_rExcept), rParent);
+}
+
+void displayException(const css::sdb::SQLContext& _rExcept, const css::uno::Reference<css::awt::XWindow>& rParent)
+{
+ displayException(Any(_rExcept), rParent);
+}
+
+void displayException(const css::sdb::SQLErrorEvent& _rEvent, const css::uno::Reference<css::awt::XWindow>& rParent)
+{
+ displayException(_rEvent.Reason, rParent);
+}
+
+sal_Int32 getElementPos(const Reference< css::container::XIndexAccess>& xCont, const Reference< XInterface >& xElement)
+{
+ sal_Int32 nIndex = -1;
+ if (!xCont.is())
+ return nIndex;
+
+
+ Reference< XInterface > xNormalized( xElement, UNO_QUERY );
+ DBG_ASSERT( xNormalized.is(), "getElementPos: invalid element!" );
+ if ( xNormalized.is() )
+ {
+ // find child position
+ nIndex = xCont->getCount();
+ while (nIndex--)
+ {
+ try
+ {
+ Reference< XInterface > xCurrent(xCont->getByIndex( nIndex ),UNO_QUERY);
+ DBG_ASSERT( xCurrent.get() == Reference< XInterface >( xCurrent, UNO_QUERY ).get(),
+ "getElementPos: container element not normalized!" );
+ if ( xNormalized.get() == xCurrent.get() )
+ break;
+ }
+ catch(Exception&)
+ {
+ TOOLS_WARN_EXCEPTION( "svx", "getElementPos" );
+ }
+
+ }
+ }
+ return nIndex;
+}
+
+
+OUString getLabelName(const Reference< css::beans::XPropertySet>& xControlModel)
+{
+ if (!xControlModel.is())
+ return OUString();
+
+ if (::comphelper::hasProperty(FM_PROP_CONTROLLABEL, xControlModel))
+ {
+ Reference< css::beans::XPropertySet> xLabelSet;
+ xControlModel->getPropertyValue(FM_PROP_CONTROLLABEL) >>= xLabelSet;
+ if (xLabelSet.is() && ::comphelper::hasProperty(FM_PROP_LABEL, xLabelSet))
+ {
+ Any aLabel( xLabelSet->getPropertyValue(FM_PROP_LABEL) );
+ if ((aLabel.getValueTypeClass() == TypeClass_STRING) && !::comphelper::getString(aLabel).isEmpty())
+ return ::comphelper::getString(aLabel);
+ }
+ }
+
+ return ::comphelper::getString(xControlModel->getPropertyValue(FM_PROP_CONTROLSOURCE));
+}
+
+
+// = CursorWrapper
+
+CursorWrapper::CursorWrapper(const Reference< css::sdbc::XRowSet>& _rxCursor, bool bUseCloned)
+{
+ ImplConstruct(Reference< css::sdbc::XResultSet>(_rxCursor), bUseCloned);
+}
+
+
+CursorWrapper::CursorWrapper(const Reference< css::sdbc::XResultSet>& _rxCursor, bool bUseCloned)
+{
+ ImplConstruct(_rxCursor, bUseCloned);
+}
+
+
+void CursorWrapper::ImplConstruct(const Reference< css::sdbc::XResultSet>& _rxCursor, bool bUseCloned)
+{
+ if (bUseCloned)
+ {
+ Reference< css::sdb::XResultSetAccess> xAccess(_rxCursor, UNO_QUERY);
+ try
+ {
+ m_xMoveOperations = xAccess.is() ? xAccess->createResultSet() : Reference< css::sdbc::XResultSet>();
+ }
+ catch(Exception&)
+ {
+ }
+ }
+ else
+ m_xMoveOperations = _rxCursor;
+
+ m_xBookmarkOperations.set(m_xMoveOperations, css::uno::UNO_QUERY);
+ m_xColumnsSupplier.set(m_xMoveOperations, css::uno::UNO_QUERY);
+ m_xPropertyAccess.set(m_xMoveOperations, css::uno::UNO_QUERY);
+
+ if ( !m_xMoveOperations.is() || !m_xBookmarkOperations.is() || !m_xColumnsSupplier.is() || !m_xPropertyAccess.is() )
+ { // all or nothing !!
+ m_xMoveOperations = nullptr;
+ m_xBookmarkOperations = nullptr;
+ m_xColumnsSupplier = nullptr;
+ }
+ else
+ m_xGeneric = m_xMoveOperations.get();
+}
+
+CursorWrapper& CursorWrapper::operator=(const Reference< css::sdbc::XRowSet>& _rxCursor)
+{
+ m_xMoveOperations.set(_rxCursor);
+ m_xBookmarkOperations.set(_rxCursor, UNO_QUERY);
+ m_xColumnsSupplier.set(_rxCursor, UNO_QUERY);
+ if (!m_xMoveOperations.is() || !m_xBookmarkOperations.is() || !m_xColumnsSupplier.is())
+ { // all or nothing !!
+ m_xMoveOperations = nullptr;
+ m_xBookmarkOperations = nullptr;
+ m_xColumnsSupplier = nullptr;
+ }
+ return *this;
+}
+
+FmXDisposeListener::~FmXDisposeListener()
+{
+ setAdapter(nullptr);
+}
+
+void FmXDisposeListener::setAdapter(FmXDisposeMultiplexer* pAdapter)
+{
+ std::scoped_lock aGuard(m_aMutex);
+ m_pAdapter = pAdapter;
+}
+
+FmXDisposeMultiplexer::FmXDisposeMultiplexer(FmXDisposeListener* _pListener, const Reference< css::lang::XComponent>& _rxObject)
+ :m_xObject(_rxObject)
+ ,m_pListener(_pListener)
+{
+ m_pListener->setAdapter(this);
+
+ if (m_xObject.is())
+ m_xObject->addEventListener(this);
+}
+
+FmXDisposeMultiplexer::~FmXDisposeMultiplexer()
+{
+}
+
+// css::lang::XEventListener
+
+void FmXDisposeMultiplexer::disposing(const css::lang::EventObject& /*Source*/)
+{
+ Reference< css::lang::XEventListener> xPreventDelete(this);
+
+ if (m_pListener)
+ {
+ m_pListener->disposing(0);
+ m_pListener->setAdapter(nullptr);
+ m_pListener = nullptr;
+ }
+ m_xObject = nullptr;
+}
+
+
+void FmXDisposeMultiplexer::dispose()
+{
+ if (m_xObject.is())
+ {
+ Reference< css::lang::XEventListener> xPreventDelete(this);
+
+ m_xObject->removeEventListener(this);
+ m_xObject = nullptr;
+
+ m_pListener->setAdapter(nullptr);
+ m_pListener = nullptr;
+ }
+}
+
+
+SdrObjKind getControlTypeByObject(const Reference< css::lang::XServiceInfo>& _rxObject)
+{
+ // ask for the persistent service name
+ Reference< css::io::XPersistObject> xPersistence(_rxObject, UNO_QUERY);
+ DBG_ASSERT(xPersistence.is(), "::getControlTypeByObject : argument should be a css::io::XPersistObject !");
+ if (!xPersistence.is())
+ return SdrObjKind::FormControl;
+
+ OUString sPersistentServiceName = xPersistence->getServiceName();
+ if (sPersistentServiceName == FM_COMPONENT_EDIT) // 5.0-Name
+ {
+ // may be a simple edit field or a formatted field, dependent of the supported services
+ if (_rxObject->supportsService(FM_SUN_COMPONENT_FORMATTEDFIELD))
+ return SdrObjKind::FormFormattedField;
+ return SdrObjKind::FormEdit;
+ }
+ if (sPersistentServiceName == FM_COMPONENT_TEXTFIELD)
+ return SdrObjKind::FormEdit;
+ if (sPersistentServiceName == FM_COMPONENT_COMMANDBUTTON)
+ return SdrObjKind::FormButton;
+ if (sPersistentServiceName == FM_COMPONENT_FIXEDTEXT)
+ return SdrObjKind::FormFixedText;
+ if (sPersistentServiceName == FM_COMPONENT_LISTBOX)
+ return SdrObjKind::FormListbox;
+ if (sPersistentServiceName == FM_COMPONENT_CHECKBOX)
+ return SdrObjKind::FormCheckbox;
+ if (sPersistentServiceName == FM_COMPONENT_RADIOBUTTON)
+ return SdrObjKind::FormRadioButton;
+ if (sPersistentServiceName == FM_COMPONENT_GROUPBOX)
+ return SdrObjKind::FormGroupBox;
+ if (sPersistentServiceName == FM_COMPONENT_COMBOBOX)
+ return SdrObjKind::FormCombobox;
+ if (sPersistentServiceName == FM_COMPONENT_GRID) // 5.0-Name
+ return SdrObjKind::FormGrid;
+ if (sPersistentServiceName == FM_COMPONENT_GRIDCONTROL)
+ return SdrObjKind::FormGrid;
+ if (sPersistentServiceName == FM_COMPONENT_IMAGEBUTTON)
+ return SdrObjKind::FormImageButton;
+ if (sPersistentServiceName == FM_COMPONENT_FILECONTROL)
+ return SdrObjKind::FormFileControl;
+ if (sPersistentServiceName == FM_COMPONENT_DATEFIELD)
+ return SdrObjKind::FormDateField;
+ if (sPersistentServiceName == FM_COMPONENT_TIMEFIELD)
+ return SdrObjKind::FormTimeField;
+ if (sPersistentServiceName == FM_COMPONENT_NUMERICFIELD)
+ return SdrObjKind::FormNumericField;
+ if (sPersistentServiceName == FM_COMPONENT_CURRENCYFIELD)
+ return SdrObjKind::FormCurrencyField;
+ if (sPersistentServiceName == FM_COMPONENT_PATTERNFIELD)
+ return SdrObjKind::FormPatternField;
+ if (sPersistentServiceName == FM_COMPONENT_HIDDEN) // 5.0-Name
+ return SdrObjKind::FormHidden;
+ if (sPersistentServiceName == FM_COMPONENT_HIDDENCONTROL)
+ return SdrObjKind::FormHidden;
+ if (sPersistentServiceName == FM_COMPONENT_IMAGECONTROL)
+ return SdrObjKind::FormImageControl;
+ if (sPersistentServiceName == FM_COMPONENT_FORMATTEDFIELD)
+ {
+ OSL_FAIL("::getControlTypeByObject : suspicious persistent service name (formatted field) !");
+ // objects with that service name should exist as they aren't compatible with older versions
+ return SdrObjKind::FormFormattedField;
+ }
+ if ( sPersistentServiceName == FM_SUN_COMPONENT_SCROLLBAR )
+ return SdrObjKind::FormScrollbar;
+ if ( sPersistentServiceName == FM_SUN_COMPONENT_SPINBUTTON )
+ return SdrObjKind::FormSpinButton;
+ if ( sPersistentServiceName == FM_SUN_COMPONENT_NAVIGATIONBAR )
+ return SdrObjKind::FormNavigationBar;
+
+ OSL_FAIL("::getControlTypeByObject : unknown object type !");
+ return SdrObjKind::FormControl;
+}
+
+
+bool isRowSetAlive(const Reference< XInterface >& _rxRowSet)
+{
+ bool bIsAlive = false;
+ Reference< css::sdbcx::XColumnsSupplier> xSupplyCols(_rxRowSet, UNO_QUERY);
+ Reference< css::container::XIndexAccess> xCols;
+ if (xSupplyCols.is())
+ xCols.set(xSupplyCols->getColumns(), UNO_QUERY);
+ if (xCols.is() && (xCols->getCount() > 0))
+ bIsAlive = true;
+
+ return bIsAlive;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/form/fmundo.cxx b/svx/source/form/fmundo.cxx
new file mode 100644
index 000000000..57593d36c
--- /dev/null
+++ b/svx/source/form/fmundo.cxx
@@ -0,0 +1,1263 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * 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 <map>
+
+#include <sal/macros.h>
+#include <fmundo.hxx>
+#include <fmpgeimp.hxx>
+#include <svx/svditer.hxx>
+#include <fmobj.hxx>
+#include <fmprop.hxx>
+#include <svx/strings.hrc>
+#include <svx/dialmgr.hxx>
+#include <svx/fmmodel.hxx>
+#include <svx/fmpage.hxx>
+
+#include <com/sun/star/util/XModifyBroadcaster.hpp>
+#include <com/sun/star/beans/PropertyAttribute.hpp>
+#include <com/sun/star/container/XContainer.hpp>
+#include <com/sun/star/container/XContainerListener.hpp>
+#include <com/sun/star/script/XEventAttacherManager.hpp>
+#include <com/sun/star/form/binding/XBindableValue.hpp>
+#include <com/sun/star/form/binding/XListEntrySink.hpp>
+#include <com/sun/star/sdbc/XConnection.hpp>
+#include <com/sun/star/uno/XComponentContext.hpp>
+
+#include <svx/fmtools.hxx>
+#include <tools/debug.hxx>
+#include <tools/diagnose_ex.h>
+#include <sfx2/objsh.hxx>
+#include <sfx2/event.hxx>
+#include <osl/mutex.hxx>
+#include <comphelper/property.hxx>
+#include <comphelper/types.hxx>
+#include <connectivity/dbtools.hxx>
+#include <vcl/svapp.hxx>
+
+using namespace ::com::sun::star::uno;
+using namespace ::com::sun::star::awt;
+using namespace ::com::sun::star::beans;
+using namespace ::com::sun::star::container;
+using namespace ::com::sun::star::script;
+using namespace ::com::sun::star::lang;
+using namespace ::com::sun::star::form;
+using namespace ::com::sun::star::util;
+using namespace ::com::sun::star::form::binding;
+using namespace ::com::sun::star::sdbc;
+using namespace ::svxform;
+using namespace ::dbtools;
+
+
+#include <com/sun/star/script/XScriptListener.hpp>
+#include <comphelper/processfactory.hxx>
+#include <cppuhelper/implbase.hxx>
+
+namespace {
+
+class ScriptEventListenerWrapper : public cppu::WeakImplHelper< XScriptListener >
+{
+public:
+ /// @throws css::uno::RuntimeException
+ explicit ScriptEventListenerWrapper( FmFormModel& _rModel)
+ :m_rModel( _rModel )
+ ,m_attemptedListenerCreation( false )
+ {
+
+ }
+ // XEventListener
+ virtual void SAL_CALL disposing(const EventObject& ) override {}
+
+ // XScriptListener
+ virtual void SAL_CALL firing(const ScriptEvent& evt) override
+ {
+ attemptListenerCreation();
+ if ( m_vbaListener.is() )
+ {
+ m_vbaListener->firing( evt );
+ }
+ }
+
+ virtual Any SAL_CALL approveFiring(const ScriptEvent& evt) override
+ {
+ attemptListenerCreation();
+ if ( m_vbaListener.is() )
+ {
+ return m_vbaListener->approveFiring( evt );
+ }
+ return Any();
+ }
+
+private:
+ void attemptListenerCreation()
+ {
+ if ( m_attemptedListenerCreation )
+ return;
+ m_attemptedListenerCreation = true;
+
+ try
+ {
+ css::uno::Reference<css::uno::XComponentContext> context(
+ comphelper::getProcessComponentContext());
+ Reference< XScriptListener > const xScriptListener(
+ context->getServiceManager()->createInstanceWithContext(
+ "ooo.vba.EventListener", context),
+ UNO_QUERY_THROW);
+ Reference< XPropertySet > const xListenerProps( xScriptListener, UNO_QUERY_THROW );
+ // SfxObjectShellRef is good here since the model controls the lifetime of the shell
+ SfxObjectShellRef const xObjectShell = m_rModel.GetObjectShell();
+ ENSURE_OR_THROW( xObjectShell.is(), "no object shell!" );
+ xListenerProps->setPropertyValue("Model", Any( xObjectShell->GetModel() ) );
+
+ m_vbaListener = xScriptListener;
+ }
+ catch( Exception const & )
+ {
+ DBG_UNHANDLED_EXCEPTION("svx");
+ }
+ }
+ FmFormModel& m_rModel;
+ Reference< XScriptListener > m_vbaListener;
+ bool m_attemptedListenerCreation;
+
+
+};
+
+
+// some helper structs for caching property infos
+
+struct PropertyInfo
+{
+ bool bIsTransientOrReadOnly : 1; // the property is transient or read-only, thus we need no undo action for it
+ bool bIsValueProperty : 1; // the property is the special value property, thus it may be handled
+ // as if it's transient or persistent
+};
+
+struct PropertySetInfo
+{
+ typedef std::map<OUString, PropertyInfo> AllProperties;
+
+ AllProperties aProps; // all properties of this set which we know so far
+ bool bHasEmptyControlSource; // sal_True -> the set has a DataField property, and the current value is an empty string
+ // sal_False -> the set has _no_ such property or its value isn't empty
+};
+
+}
+
+typedef std::map<Reference< XPropertySet >, PropertySetInfo> PropertySetInfoCache;
+
+
+static OUString static_STR_UNDO_PROPERTY;
+
+
+FmXUndoEnvironment::FmXUndoEnvironment(FmFormModel& _rModel)
+ :rModel( _rModel )
+ ,m_pPropertySetCache( nullptr )
+ ,m_pScriptingEnv( new svxform::FormScriptingEnvironment( _rModel ) )
+ ,m_Locks( 0 )
+ ,bReadOnly( false )
+ ,m_bDisposed( false )
+{
+ try
+ {
+ m_vbaListener = new ScriptEventListenerWrapper( _rModel );
+ }
+ catch( Exception& )
+ {
+ }
+}
+
+FmXUndoEnvironment::~FmXUndoEnvironment()
+{
+ if ( !m_bDisposed ) // i120746, call FormScriptingEnvironment::dispose to avoid memory leak
+ m_pScriptingEnv->dispose();
+
+ if (m_pPropertySetCache)
+ delete static_cast<PropertySetInfoCache*>(m_pPropertySetCache);
+}
+
+void FmXUndoEnvironment::dispose()
+{
+ OSL_ENSURE( !m_bDisposed, "FmXUndoEnvironment::dispose: disposed twice?" );
+ if ( !m_bDisposed )
+ return;
+
+ Lock();
+
+ sal_uInt16 nCount = rModel.GetPageCount();
+ sal_uInt16 i;
+ for (i = 0; i < nCount; i++)
+ {
+ FmFormPage* pPage = dynamic_cast<FmFormPage*>( rModel.GetPage(i) );
+ if ( pPage )
+ {
+ Reference< css::form::XForms > xForms = pPage->GetForms( false );
+ if ( xForms.is() )
+ RemoveElement( xForms );
+ }
+ }
+
+ nCount = rModel.GetMasterPageCount();
+ for (i = 0; i < nCount; i++)
+ {
+ FmFormPage* pPage = dynamic_cast<FmFormPage*>( rModel.GetMasterPage(i) );
+ if ( pPage )
+ {
+ Reference< css::form::XForms > xForms = pPage->GetForms( false );
+ if ( xForms.is() )
+ RemoveElement( xForms );
+ }
+ }
+
+ UnLock();
+
+ OSL_PRECOND( rModel.GetObjectShell(), "FmXUndoEnvironment::dispose: no object shell anymore!" );
+ if ( rModel.GetObjectShell() )
+ EndListening( *rModel.GetObjectShell() );
+
+ if ( IsListening( rModel ) )
+ EndListening( rModel );
+
+ m_pScriptingEnv->dispose();
+
+ m_bDisposed = true;
+}
+
+
+void FmXUndoEnvironment::ModeChanged()
+{
+ OSL_PRECOND( rModel.GetObjectShell(), "FmXUndoEnvironment::ModeChanged: no object shell anymore!" );
+ if ( !rModel.GetObjectShell() )
+ return;
+
+ if (bReadOnly == (rModel.GetObjectShell()->IsReadOnly() || rModel.GetObjectShell()->IsReadOnlyUI()))
+ return;
+
+ bReadOnly = !bReadOnly;
+
+ sal_uInt16 nCount = rModel.GetPageCount();
+ sal_uInt16 i;
+ for (i = 0; i < nCount; i++)
+ {
+ FmFormPage* pPage = dynamic_cast<FmFormPage*>( rModel.GetPage(i) );
+ if ( pPage )
+ {
+ Reference< css::form::XForms > xForms = pPage->GetForms( false );
+ if ( xForms.is() )
+ TogglePropertyListening( xForms );
+ }
+ }
+
+ nCount = rModel.GetMasterPageCount();
+ for (i = 0; i < nCount; i++)
+ {
+ FmFormPage* pPage = dynamic_cast<FmFormPage*>( rModel.GetMasterPage(i) );
+ if ( pPage )
+ {
+ Reference< css::form::XForms > xForms = pPage->GetForms( false );
+ if ( xForms.is() )
+ TogglePropertyListening( xForms );
+ }
+ }
+
+ if (!bReadOnly)
+ StartListening(rModel);
+ else
+ EndListening(rModel);
+}
+
+
+void FmXUndoEnvironment::Notify( SfxBroadcaster& /*rBC*/, const SfxHint& rHint )
+{
+ if (rHint.GetId() == SfxHintId::ThisIsAnSdrHint)
+ {
+ const SdrHint* pSdrHint = static_cast<const SdrHint*>(&rHint);
+ switch (pSdrHint->GetKind())
+ {
+ case SdrHintKind::ObjectInserted:
+ {
+ SdrObject* pSdrObj = const_cast<SdrObject*>(pSdrHint->GetObject());
+ Inserted( pSdrObj );
+ } break;
+ case SdrHintKind::ObjectRemoved:
+ {
+ SdrObject* pSdrObj = const_cast<SdrObject*>(pSdrHint->GetObject());
+ Removed( pSdrObj );
+ }
+ break;
+ default:
+ break;
+ }
+ }
+ else if (rHint.GetId() != SfxHintId::NONE)
+ {
+ switch (rHint.GetId())
+ {
+ case SfxHintId::Dying:
+ dispose();
+ rModel.SetObjectShell( nullptr );
+ break;
+ case SfxHintId::ModeChanged:
+ ModeChanged();
+ break;
+ default: break;
+ }
+ }
+ else if (const SfxEventHint* pEventHint = dynamic_cast<const SfxEventHint*>(&rHint))
+ {
+ switch (pEventHint->GetEventId())
+ {
+ case SfxEventHintId::CreateDoc:
+ case SfxEventHintId::OpenDoc:
+ ModeChanged();
+ break;
+ default: break;
+ }
+ }
+}
+
+void FmXUndoEnvironment::Inserted(SdrObject* pObj)
+{
+ if (pObj->GetObjInventor() == SdrInventor::FmForm)
+ {
+ FmFormObj* pFormObj = dynamic_cast<FmFormObj*>( pObj );
+ Inserted( pFormObj );
+ }
+ else if (pObj->IsGroupObject())
+ {
+ SdrObjListIter aIter(pObj->GetSubList());
+ while ( aIter.IsMore() )
+ Inserted( aIter.Next() );
+ }
+}
+
+
+namespace
+{
+ bool lcl_searchElement(const Reference< XIndexAccess>& xCont, const Reference< XInterface >& xElement)
+ {
+ if (!xCont.is() || !xElement.is())
+ return false;
+
+ sal_Int32 nCount = xCont->getCount();
+ Reference< XInterface > xComp;
+ for (sal_Int32 i = 0; i < nCount; i++)
+ {
+ try
+ {
+ xCont->getByIndex(i) >>= xComp;
+ if (xComp.is())
+ {
+ if ( xElement == xComp )
+ return true;
+ else
+ {
+ Reference< XIndexAccess> xCont2(xComp, UNO_QUERY);
+ if (xCont2.is() && lcl_searchElement(xCont2, xElement))
+ return true;
+ }
+ }
+ }
+ catch(const Exception&)
+ {
+ DBG_UNHANDLED_EXCEPTION("svx");
+ }
+ }
+ return false;
+ }
+}
+
+
+void FmXUndoEnvironment::Inserted(FmFormObj* pObj)
+{
+ DBG_ASSERT( pObj, "FmXUndoEnvironment::Inserted: invalid object!" );
+ if ( !pObj )
+ return;
+
+ // is the control still assigned to a form
+ Reference< XInterface > xModel(pObj->GetUnoControlModel(), UNO_QUERY);
+ Reference< XFormComponent > xContent(xModel, UNO_QUERY);
+ if (!(xContent.is() && pObj->getSdrPageFromSdrObject()))
+ return;
+
+ // if the component doesn't belong to a form, yet, find one to insert into
+ if (!xContent->getParent().is())
+ {
+ try
+ {
+ const Reference< XIndexContainer >& xObjectParent = pObj->GetOriginalParent();
+
+ FmFormPage& rPage(dynamic_cast< FmFormPage& >( *pObj->getSdrPageFromSdrObject()));
+ Reference< XIndexAccess > xForms( rPage.GetForms(), UNO_QUERY_THROW );
+
+ Reference< XIndexContainer > xNewParent;
+ Reference< XForm > xForm;
+ sal_Int32 nPos = -1;
+ if ( lcl_searchElement( xForms, xObjectParent ) )
+ {
+ // the form which was the parent of the object when it was removed is still
+ // part of the form component hierarchy of the current page
+ xNewParent = xObjectParent;
+ xForm.set( xNewParent, UNO_QUERY_THROW );
+ nPos = ::std::min( pObj->GetOriginalIndex(), xNewParent->getCount() );
+ }
+ else
+ {
+ xForm.set( rPage.GetImpl().findPlaceInFormComponentHierarchy( xContent ), UNO_SET_THROW );
+ xNewParent.set( xForm, UNO_QUERY_THROW );
+ nPos = xNewParent->getCount();
+ }
+
+ FmFormPageImpl::setUniqueName( xContent, xForm );
+ xNewParent->insertByIndex( nPos, Any( xContent ) );
+
+ Reference< XEventAttacherManager > xManager( xNewParent, UNO_QUERY_THROW );
+ xManager->registerScriptEvents( nPos, pObj->GetOriginalEvents() );
+ }
+ catch( const Exception& )
+ {
+ DBG_UNHANDLED_EXCEPTION("svx");
+ }
+ }
+
+ // reset FormObject
+ pObj->ClearObjEnv();
+}
+
+
+void FmXUndoEnvironment::Removed(SdrObject* pObj)
+{
+ if ( pObj->IsVirtualObj() )
+ // for virtual objects, we've already been notified of the removal of the master
+ // object, which is sufficient here
+ return;
+
+ if (pObj->GetObjInventor() == SdrInventor::FmForm)
+ {
+ FmFormObj* pFormObj = dynamic_cast<FmFormObj*>( pObj );
+ Removed(pFormObj);
+ }
+ else if (pObj->IsGroupObject())
+ {
+ SdrObjListIter aIter(pObj->GetSubList());
+ while ( aIter.IsMore() )
+ Removed( aIter.Next() );
+ }
+}
+
+
+void FmXUndoEnvironment::Removed(FmFormObj* pObj)
+{
+ DBG_ASSERT( pObj, "FmXUndoEnvironment::Removed: invalid object!" );
+ if ( !pObj )
+ return;
+
+ // is the control still assigned to a form
+ Reference< XFormComponent > xContent(pObj->GetUnoControlModel(), UNO_QUERY);
+ if (!xContent.is())
+ return;
+
+ // The object is taken out of a list.
+ // If a father exists, the object is removed at the father and
+ // noted at the FormObject!
+
+ // If the object is reinserted and a parent exists, this parent is set though.
+ Reference< XIndexContainer > xForm(xContent->getParent(), UNO_QUERY);
+ if (!xForm.is())
+ return;
+
+ // determine which position the child was at
+ const sal_Int32 nPos = getElementPos(xForm, xContent);
+ if (nPos < 0)
+ return;
+
+ Sequence< ScriptEventDescriptor > aEvts;
+ Reference< XEventAttacherManager > xManager(xForm, UNO_QUERY);
+ if (xManager.is())
+ aEvts = xManager->getScriptEvents(nPos);
+
+ try
+ {
+ pObj->SetObjEnv(xForm, nPos, aEvts);
+ xForm->removeByIndex(nPos);
+ }
+ catch(Exception&)
+ {
+ DBG_UNHANDLED_EXCEPTION("svx");
+ }
+}
+
+// XEventListener
+
+void SAL_CALL FmXUndoEnvironment::disposing(const EventObject& e)
+{
+ // check if it's an object we have cached information about
+ if (m_pPropertySetCache)
+ {
+ Reference< XPropertySet > xSourceSet(e.Source, UNO_QUERY);
+ if (xSourceSet.is())
+ {
+ PropertySetInfoCache* pCache = static_cast<PropertySetInfoCache*>(m_pPropertySetCache);
+ PropertySetInfoCache::iterator aSetPos = pCache->find(xSourceSet);
+ if (aSetPos != pCache->end())
+ pCache->erase(aSetPos);
+ }
+ }
+}
+
+// XPropertyChangeListener
+
+void SAL_CALL FmXUndoEnvironment::propertyChange(const PropertyChangeEvent& evt)
+{
+ ::osl::ClearableMutexGuard aGuard( m_aMutex );
+
+ if (!IsLocked())
+ {
+ Reference< XPropertySet > xSet(evt.Source, UNO_QUERY);
+ if (!xSet.is())
+ return;
+
+ // if it's a "default value" property of a control model, set the according "value" property
+ static constexpr rtl::OUStringConstExpr pDefaultValueProperties[] = {
+ FM_PROP_DEFAULT_TEXT, FM_PROP_DEFAULTCHECKED, FM_PROP_DEFAULT_DATE, FM_PROP_DEFAULT_TIME,
+ FM_PROP_DEFAULT_VALUE, FM_PROP_DEFAULT_SELECT_SEQ, FM_PROP_EFFECTIVE_DEFAULT
+ };
+ static constexpr rtl::OUStringConstExpr aValueProperties[] = {
+ FM_PROP_TEXT, FM_PROP_STATE, FM_PROP_DATE, FM_PROP_TIME,
+ FM_PROP_VALUE, FM_PROP_SELECT_SEQ, FM_PROP_EFFECTIVE_VALUE
+ };
+ sal_Int32 nDefaultValueProps = SAL_N_ELEMENTS(pDefaultValueProperties);
+ OSL_ENSURE(SAL_N_ELEMENTS(aValueProperties) == nDefaultValueProps,
+ "FmXUndoEnvironment::propertyChange: inconsistence!");
+ for (sal_Int32 i=0; i<nDefaultValueProps; ++i)
+ {
+ if (evt.PropertyName == pDefaultValueProperties[i])
+ {
+ try
+ {
+ xSet->setPropertyValue(aValueProperties[i], evt.NewValue);
+ }
+ catch(const Exception&)
+ {
+ OSL_FAIL("FmXUndoEnvironment::propertyChange: could not adjust the value property!");
+ }
+ }
+ }
+
+ // no Undo for transient and readonly props. But unfortunately "transient" is not only that the
+ // "transient" flag is set for the property in question, instead it is somewhat more complex
+ // Transience criterions are:
+ // - the "transient" flag is set for the property
+ // - OR the control has a non-empty COntrolSource property, i.e. is intended to be bound
+ // to a database column. Note that it doesn't matter here whether the control actually
+ // *is* bound to a column
+ // - OR the control is bound to an external value via XBindableValue/XValueBinding
+ // which does not have a "ExternalData" property being <TRUE/>
+
+ if (!m_pPropertySetCache)
+ m_pPropertySetCache = new PropertySetInfoCache;
+ PropertySetInfoCache* pCache = static_cast<PropertySetInfoCache*>(m_pPropertySetCache);
+
+ // let's see if we know something about the set
+ PropertySetInfoCache::iterator aSetPos = pCache->find(xSet);
+ if (aSetPos == pCache->end())
+ {
+ PropertySetInfo aNewEntry;
+ if (!::comphelper::hasProperty(FM_PROP_CONTROLSOURCE, xSet))
+ {
+ aNewEntry.bHasEmptyControlSource = false;
+ }
+ else
+ {
+ try
+ {
+ Any aCurrentControlSource = xSet->getPropertyValue(FM_PROP_CONTROLSOURCE);
+ aNewEntry.bHasEmptyControlSource = !aCurrentControlSource.hasValue() || ::comphelper::getString(aCurrentControlSource).isEmpty();
+ }
+ catch(const Exception&)
+ {
+ DBG_UNHANDLED_EXCEPTION("svx");
+ }
+ }
+ aSetPos = pCache->emplace(xSet,aNewEntry).first;
+ DBG_ASSERT(aSetPos != pCache->end(), "FmXUndoEnvironment::propertyChange : just inserted it ... why it's not there ?");
+ }
+ else
+ { // is it the DataField property ?
+ if (evt.PropertyName == FM_PROP_CONTROLSOURCE)
+ {
+ aSetPos->second.bHasEmptyControlSource = !evt.NewValue.hasValue() || ::comphelper::getString(evt.NewValue).isEmpty();
+ }
+ }
+
+ // now we have access to the cached info about the set
+ // let's see what we know about the property
+ PropertySetInfo::AllProperties& rPropInfos = aSetPos->second.aProps;
+ PropertySetInfo::AllProperties::iterator aPropertyPos = rPropInfos.find(evt.PropertyName);
+ if (aPropertyPos == rPropInfos.end())
+ { // nothing 'til now ... have to change this...
+ PropertyInfo aNewEntry;
+
+ // the attributes
+ sal_Int32 nAttributes = xSet->getPropertySetInfo()->getPropertyByName(evt.PropertyName).Attributes;
+ aNewEntry.bIsTransientOrReadOnly = ((nAttributes & PropertyAttribute::READONLY) != 0) || ((nAttributes & PropertyAttribute::TRANSIENT) != 0);
+
+ // check if it is the special "DataFieldProperty"
+ aNewEntry.bIsValueProperty = false;
+ try
+ {
+ if (::comphelper::hasProperty(FM_PROP_CONTROLSOURCEPROPERTY, xSet))
+ {
+ Any aControlSourceProperty = xSet->getPropertyValue(FM_PROP_CONTROLSOURCEPROPERTY);
+ OUString sControlSourceProperty;
+ aControlSourceProperty >>= sControlSourceProperty;
+
+ aNewEntry.bIsValueProperty = (sControlSourceProperty == evt.PropertyName);
+ }
+ }
+ catch(const Exception&)
+ {
+ DBG_UNHANDLED_EXCEPTION("svx");
+ }
+
+ // insert the new entry
+ aPropertyPos = rPropInfos.emplace(evt.PropertyName,aNewEntry).first;
+ DBG_ASSERT(aPropertyPos != rPropInfos.end(), "FmXUndoEnvironment::propertyChange : just inserted it ... why it's not there ?");
+ }
+
+ // now we have access to the cached info about the property affected
+ // and are able to decide whether or not we need an undo action
+
+ bool bAddUndoAction = rModel.IsUndoEnabled();
+ // no UNDO for transient/readonly properties
+ if ( bAddUndoAction && aPropertyPos->second.bIsTransientOrReadOnly )
+ bAddUndoAction = false;
+
+ if ( bAddUndoAction && aPropertyPos->second.bIsValueProperty )
+ {
+ // no UNDO when the "value" property changes, but the ControlSource is non-empty
+ // (in this case the control is intended to be bound to a database column)
+ if ( !aSetPos->second.bHasEmptyControlSource )
+ bAddUndoAction = false;
+
+ // no UNDO if the control is currently bound to an external value
+ if ( bAddUndoAction )
+ {
+ Reference< XBindableValue > xBindable( evt.Source, UNO_QUERY );
+ Reference< XValueBinding > xBinding;
+ if ( xBindable.is() )
+ xBinding = xBindable->getValueBinding();
+
+ Reference< XPropertySet > xBindingProps;
+ Reference< XPropertySetInfo > xBindingPropsPSI;
+ if ( xBindable.is() )
+ xBindingProps.set( xBinding, UNO_QUERY );
+ if ( xBindingProps.is() )
+ xBindingPropsPSI = xBindingProps->getPropertySetInfo();
+ // TODO: we should cache all those things, else this might be too expensive.
+ // However, this requires we're notified of changes in the value binding
+
+ static constexpr OUStringLiteral s_sExternalData = u"ExternalData";
+ if ( xBindingPropsPSI.is() && xBindingPropsPSI->hasPropertyByName( s_sExternalData ) )
+ {
+ bool bExternalData = true;
+ OSL_VERIFY( xBindingProps->getPropertyValue( s_sExternalData ) >>= bExternalData );
+ bAddUndoAction = !bExternalData;
+ }
+ else
+ bAddUndoAction = !xBinding.is();
+ }
+ }
+
+ if ( bAddUndoAction && ( evt.PropertyName == FM_PROP_STRINGITEMLIST ) )
+ {
+ Reference< XListEntrySink > xSink( evt.Source, UNO_QUERY );
+ if ( xSink.is() && xSink->getListEntrySource().is() )
+ // #i41029# / 2005-01-31 / frank.schoenheit@sun.com
+ bAddUndoAction = false;
+ }
+
+ if ( bAddUndoAction )
+ {
+ aGuard.clear();
+ // TODO: this is a potential race condition: two threads here could in theory
+ // add their undo actions out-of-order
+
+ SolarMutexGuard aSolarGuard;
+ rModel.AddUndo(std::make_unique<FmUndoPropertyAction>(rModel, evt));
+ }
+ }
+ else
+ {
+ // if it's the DataField property we may have to adjust our cache
+ if (m_pPropertySetCache && evt.PropertyName == FM_PROP_CONTROLSOURCE)
+ {
+ Reference< XPropertySet > xSet(evt.Source, UNO_QUERY);
+ PropertySetInfoCache* pCache = static_cast<PropertySetInfoCache*>(m_pPropertySetCache);
+ PropertySetInfo& rSetInfo = (*pCache)[xSet];
+ rSetInfo.bHasEmptyControlSource = !evt.NewValue.hasValue() || ::comphelper::getString(evt.NewValue).isEmpty();
+ }
+ }
+}
+
+// XContainerListener
+
+void SAL_CALL FmXUndoEnvironment::elementInserted(const ContainerEvent& evt)
+{
+ SolarMutexGuard aSolarGuard;
+ ::osl::MutexGuard aGuard( m_aMutex );
+
+ // new object for listening
+ Reference< XInterface > xIface;
+ evt.Element >>= xIface;
+ OSL_ENSURE(xIface.is(), "FmXUndoEnvironment::elementInserted: invalid container notification!");
+ AddElement(xIface);
+
+ implSetModified();
+}
+
+
+void FmXUndoEnvironment::implSetModified()
+{
+ if ( !IsLocked() && rModel.GetObjectShell() )
+ {
+ rModel.GetObjectShell()->SetModified();
+ }
+}
+
+
+void SAL_CALL FmXUndoEnvironment::elementReplaced(const ContainerEvent& evt)
+{
+ SolarMutexGuard aSolarGuard;
+ ::osl::MutexGuard aGuard( m_aMutex );
+
+ Reference< XInterface > xIface;
+ evt.ReplacedElement >>= xIface;
+ OSL_ENSURE(xIface.is(), "FmXUndoEnvironment::elementReplaced: invalid container notification!");
+ RemoveElement(xIface);
+
+ evt.Element >>= xIface;
+ AddElement(xIface);
+
+ implSetModified();
+}
+
+
+void SAL_CALL FmXUndoEnvironment::elementRemoved(const ContainerEvent& evt)
+{
+ SolarMutexGuard aSolarGuard;
+ ::osl::MutexGuard aGuard( m_aMutex );
+
+ Reference< XInterface > xIface( evt.Element, UNO_QUERY );
+ OSL_ENSURE(xIface.is(), "FmXUndoEnvironment::elementRemoved: invalid container notification!");
+ RemoveElement(xIface);
+
+ implSetModified();
+}
+
+
+void SAL_CALL FmXUndoEnvironment::modified( const EventObject& /*aEvent*/ )
+{
+ implSetModified();
+}
+
+
+void FmXUndoEnvironment::AddForms(const Reference< XNameContainer > & rForms)
+{
+ Lock();
+ AddElement(Reference<XInterface>( rForms, UNO_QUERY ));
+ UnLock();
+}
+
+
+void FmXUndoEnvironment::RemoveForms(const Reference< XNameContainer > & rForms)
+{
+ Lock();
+ RemoveElement(Reference<XInterface>( rForms, UNO_QUERY ));
+ UnLock();
+}
+
+
+void FmXUndoEnvironment::TogglePropertyListening(const Reference< XInterface > & Element)
+{
+ // listen at the container
+ Reference< XIndexContainer > xContainer(Element, UNO_QUERY);
+ if (xContainer.is())
+ {
+ sal_uInt32 nCount = xContainer->getCount();
+ Reference< XInterface > xIface;
+ for (sal_uInt32 i = 0; i < nCount; i++)
+ {
+ xContainer->getByIndex(i) >>= xIface;
+ TogglePropertyListening(xIface);
+ }
+ }
+
+ Reference< XPropertySet > xSet(Element, UNO_QUERY);
+ if (xSet.is())
+ {
+ if (!bReadOnly)
+ xSet->addPropertyChangeListener( OUString(), this );
+ else
+ xSet->removePropertyChangeListener( OUString(), this );
+ }
+}
+
+
+void FmXUndoEnvironment::switchListening( const Reference< XIndexContainer >& _rxContainer, bool _bStartListening )
+{
+ OSL_PRECOND( _rxContainer.is(), "FmXUndoEnvironment::switchListening: invalid container!" );
+ if ( !_rxContainer.is() )
+ return;
+
+ try
+ {
+ // if it's an EventAttacherManager, then we need to listen for
+ // script events
+ Reference< XEventAttacherManager > xManager( _rxContainer, UNO_QUERY );
+ if ( xManager.is() )
+ {
+ if ( _bStartListening )
+ {
+ m_pScriptingEnv->registerEventAttacherManager( xManager );
+ if ( m_vbaListener.is() )
+ xManager->addScriptListener( m_vbaListener );
+ }
+ else
+ {
+ m_pScriptingEnv->revokeEventAttacherManager( xManager );
+ if ( m_vbaListener.is() )
+ xManager->removeScriptListener( m_vbaListener );
+ }
+ }
+
+ // also handle all children of this element
+ sal_uInt32 nCount = _rxContainer->getCount();
+ Reference< XInterface > xInterface;
+ for ( sal_uInt32 i = 0; i < nCount; ++i )
+ {
+ _rxContainer->getByIndex( i ) >>= xInterface;
+ if ( _bStartListening )
+ AddElement( xInterface );
+ else
+ RemoveElement( xInterface );
+ }
+
+ // be notified of any changes in the container elements
+ Reference< XContainer > xSimpleContainer( _rxContainer, UNO_QUERY );
+ OSL_ENSURE( xSimpleContainer.is(), "FmXUndoEnvironment::switchListening: how are we expected to be notified of changes in the container?" );
+ if ( xSimpleContainer.is() )
+ {
+ if ( _bStartListening )
+ xSimpleContainer->addContainerListener( this );
+ else
+ xSimpleContainer->removeContainerListener( this );
+ }
+ }
+ catch( const Exception& )
+ {
+ TOOLS_WARN_EXCEPTION( "svx", "FmXUndoEnvironment::switchListening" );
+ }
+}
+
+
+void FmXUndoEnvironment::switchListening( const Reference< XInterface >& _rxObject, bool _bStartListening )
+{
+ OSL_PRECOND( _rxObject.is(), "FmXUndoEnvironment::switchListening: how should I listen at a NULL object?" );
+
+ try
+ {
+ if ( !bReadOnly )
+ {
+ Reference< XPropertySet > xProps( _rxObject, UNO_QUERY );
+ if ( xProps.is() )
+ {
+ if ( _bStartListening )
+ xProps->addPropertyChangeListener( OUString(), this );
+ else
+ xProps->removePropertyChangeListener( OUString(), this );
+ }
+ }
+
+ Reference< XModifyBroadcaster > xBroadcaster( _rxObject, UNO_QUERY );
+ if ( xBroadcaster.is() )
+ {
+ if ( _bStartListening )
+ xBroadcaster->addModifyListener( this );
+ else
+ xBroadcaster->removeModifyListener( this );
+ }
+ }
+ catch( const Exception& )
+ {
+ TOOLS_WARN_EXCEPTION( "svx", "FmXUndoEnvironment::switchListening" );
+ }
+}
+
+
+void FmXUndoEnvironment::AddElement(const Reference< XInterface >& _rxElement )
+{
+ OSL_ENSURE( !m_bDisposed, "FmXUndoEnvironment::AddElement: not when I'm already disposed!" );
+
+ // listen at the container
+ Reference< XIndexContainer > xContainer( _rxElement, UNO_QUERY );
+ if ( xContainer.is() )
+ switchListening( xContainer, true );
+
+ switchListening( _rxElement, true );
+}
+
+
+void FmXUndoEnvironment::RemoveElement(const Reference< XInterface >& _rxElement)
+{
+ if ( m_bDisposed )
+ return;
+
+ switchListening( _rxElement, false );
+
+ if (!bReadOnly)
+ {
+ // reset the ActiveConnection if the form is to be removed. This will (should) free the resources
+ // associated with this connection
+ // 86299 - 05/02/2001 - frank.schoenheit@germany.sun.com
+ Reference< XForm > xForm( _rxElement, UNO_QUERY );
+ Reference< XPropertySet > xFormProperties( xForm, UNO_QUERY );
+ if ( xFormProperties.is() )
+ {
+ Reference< XConnection > xDummy;
+ if ( !isEmbeddedInDatabase( _rxElement, xDummy ) )
+ // (if there is a connection in the context of the component, setting
+ // a new connection would be vetoed, anyway)
+ // #i34196#
+ xFormProperties->setPropertyValue( FM_PROP_ACTIVE_CONNECTION, Any() );
+ }
+ }
+
+ Reference< XIndexContainer > xContainer( _rxElement, UNO_QUERY );
+ if ( xContainer.is() )
+ switchListening( xContainer, false );
+}
+
+
+FmUndoPropertyAction::FmUndoPropertyAction(FmFormModel& rNewMod, const PropertyChangeEvent& evt)
+ :SdrUndoAction(rNewMod)
+ ,xObj(evt.Source, UNO_QUERY)
+ ,aPropertyName(evt.PropertyName)
+ ,aNewValue(evt.NewValue)
+ ,aOldValue(evt.OldValue)
+{
+ if (rNewMod.GetObjectShell())
+ rNewMod.GetObjectShell()->SetModified();
+ if(static_STR_UNDO_PROPERTY.isEmpty())
+ static_STR_UNDO_PROPERTY = SvxResId(RID_STR_UNDO_PROPERTY);
+}
+
+
+void FmUndoPropertyAction::Undo()
+{
+ FmXUndoEnvironment& rEnv = static_cast<FmFormModel&>(rMod).GetUndoEnv();
+
+ if (!xObj.is() || rEnv.IsLocked())
+ return;
+
+ rEnv.Lock();
+ try
+ {
+ xObj->setPropertyValue( aPropertyName, aOldValue );
+ }
+ catch( const Exception& )
+ {
+ TOOLS_WARN_EXCEPTION( "svx", "FmUndoPropertyAction::Undo" );
+ }
+ rEnv.UnLock();
+}
+
+
+void FmUndoPropertyAction::Redo()
+{
+ FmXUndoEnvironment& rEnv = static_cast<FmFormModel&>(rMod).GetUndoEnv();
+
+ if (!xObj.is() || rEnv.IsLocked())
+ return;
+
+ rEnv.Lock();
+ try
+ {
+ xObj->setPropertyValue( aPropertyName, aNewValue );
+ }
+ catch( const Exception& )
+ {
+ TOOLS_WARN_EXCEPTION( "svx", "FmUndoPropertyAction::Redo" );
+ }
+ rEnv.UnLock();
+}
+
+
+OUString FmUndoPropertyAction::GetComment() const
+{
+ OUString aStr = static_STR_UNDO_PROPERTY.replaceFirst( "#", aPropertyName );
+ return aStr;
+}
+
+
+FmUndoContainerAction::FmUndoContainerAction(FmFormModel& _rMod,
+ Action _eAction,
+ const Reference< XIndexContainer > & xCont,
+ const Reference< XInterface > & xElem,
+ sal_Int32 nIdx)
+ :SdrUndoAction( _rMod )
+ ,m_xContainer( xCont )
+ ,m_nIndex( nIdx )
+ ,m_eAction( _eAction )
+{
+ OSL_ENSURE( nIdx >= 0, "FmUndoContainerAction::FmUndoContainerAction: invalid index!" );
+ // some old code suggested this could be a valid argument. However, this code was
+ // buggy, and it *seemed* that nobody used it - so it was removed.
+
+ if ( !(xCont.is() && xElem.is()) )
+ return;
+
+ // normalize
+ m_xElement = xElem;
+ if ( m_eAction != Removed )
+ return;
+
+ if (m_nIndex >= 0)
+ {
+ Reference< XEventAttacherManager > xManager( xCont, UNO_QUERY );
+ if ( xManager.is() )
+ m_aEvents = xManager->getScriptEvents(m_nIndex);
+ }
+ else
+ m_xElement = nullptr;
+
+ // we now own the element
+ m_xOwnElement = m_xElement;
+}
+
+
+FmUndoContainerAction::~FmUndoContainerAction()
+{
+ // if we own the object...
+ DisposeElement( m_xOwnElement );
+}
+
+
+void FmUndoContainerAction::DisposeElement( const Reference< XInterface > & xElem )
+{
+ Reference< XComponent > xComp( xElem, UNO_QUERY );
+ if ( xComp.is() )
+ {
+ // and the object does not have a parent
+ Reference< XChild > xChild( xElem, UNO_QUERY );
+ if ( xChild.is() && !xChild->getParent().is() )
+ // -> dispose it
+ xComp->dispose();
+ }
+}
+
+
+void FmUndoContainerAction::implReInsert( )
+{
+ if ( m_xContainer->getCount() < m_nIndex )
+ return;
+
+ // insert the element
+ Any aVal;
+ if ( m_xContainer->getElementType() == cppu::UnoType<XFormComponent>::get() )
+ {
+ aVal <<= Reference< XFormComponent >( m_xElement, UNO_QUERY );
+ }
+ else
+ {
+ aVal <<= Reference< XForm >( m_xElement, UNO_QUERY );
+ }
+ m_xContainer->insertByIndex( m_nIndex, aVal );
+
+ OSL_ENSURE( getElementPos( m_xContainer, m_xElement ) == m_nIndex, "FmUndoContainerAction::implReInsert: insertion did not work!" );
+
+ // register the events
+ Reference< XEventAttacherManager > xManager( m_xContainer, UNO_QUERY );
+ if ( xManager.is() )
+ xManager->registerScriptEvents( m_nIndex, m_aEvents );
+
+ // we don't own the object anymore
+ m_xOwnElement = nullptr;
+}
+
+
+void FmUndoContainerAction::implReRemove( )
+{
+ Reference< XInterface > xElement;
+ if ( ( m_nIndex >= 0 ) && ( m_nIndex < m_xContainer->getCount() ) )
+ m_xContainer->getByIndex( m_nIndex ) >>= xElement;
+
+ if ( xElement != m_xElement )
+ {
+ // the indexes in the container changed. Okay, so go the long way and
+ // manually determine the index
+ m_nIndex = getElementPos( m_xContainer, m_xElement );
+ if ( m_nIndex != -1 )
+ xElement = m_xElement;
+ }
+
+ OSL_ENSURE( xElement == m_xElement, "FmUndoContainerAction::implReRemove: cannot find the element which I'm responsible for!" );
+ if ( xElement == m_xElement )
+ {
+ Reference< XEventAttacherManager > xManager( m_xContainer, UNO_QUERY );
+ if ( xManager.is() )
+ m_aEvents = xManager->getScriptEvents( m_nIndex );
+ m_xContainer->removeByIndex( m_nIndex );
+ // from now on, we own this object
+ m_xOwnElement = m_xElement;
+ }
+}
+
+
+void FmUndoContainerAction::Undo()
+{
+ FmXUndoEnvironment& rEnv = static_cast< FmFormModel& >( rMod ).GetUndoEnv();
+
+ if ( !(m_xContainer.is() && !rEnv.IsLocked() && m_xElement.is()) )
+ return;
+
+ rEnv.Lock();
+ try
+ {
+ switch ( m_eAction )
+ {
+ case Inserted:
+ implReRemove();
+ break;
+
+ case Removed:
+ implReInsert();
+ break;
+ }
+ }
+ catch( const Exception& )
+ {
+ TOOLS_WARN_EXCEPTION( "svx", "FmUndoContainerAction::Undo" );
+ }
+ rEnv.UnLock();
+}
+
+
+void FmUndoContainerAction::Redo()
+{
+ FmXUndoEnvironment& rEnv = static_cast< FmFormModel& >( rMod ).GetUndoEnv();
+ if ( !(m_xContainer.is() && !rEnv.IsLocked() && m_xElement.is()) )
+ return;
+
+ rEnv.Lock();
+ try
+ {
+ switch ( m_eAction )
+ {
+ case Inserted:
+ implReInsert();
+ break;
+
+ case Removed:
+ implReRemove();
+ break;
+ }
+ }
+ catch( const Exception& )
+ {
+ TOOLS_WARN_EXCEPTION( "svx", "FmUndoContainerAction::Redo" );
+ }
+ rEnv.UnLock();
+}
+
+
+FmUndoModelReplaceAction::FmUndoModelReplaceAction(FmFormModel& _rMod, SdrUnoObj* _pObject, const Reference< XControlModel > & _xReplaced)
+ :SdrUndoAction(_rMod)
+ ,m_xReplaced(_xReplaced)
+ ,m_pObject(_pObject)
+{
+}
+
+
+FmUndoModelReplaceAction::~FmUndoModelReplaceAction()
+{
+ // dispose our element if nobody else is responsible for
+ DisposeElement(m_xReplaced);
+}
+
+
+void FmUndoModelReplaceAction::DisposeElement( const css::uno::Reference< css::awt::XControlModel>& xReplaced )
+{
+ Reference< XComponent > xComp(xReplaced, UNO_QUERY);
+ if (xComp.is())
+ {
+ Reference< XChild > xChild(xReplaced, UNO_QUERY);
+ if (!xChild.is() || !xChild->getParent().is())
+ xComp->dispose();
+ }
+}
+
+
+void FmUndoModelReplaceAction::Undo()
+{
+ try
+ {
+ Reference< XControlModel > xCurrentModel( m_pObject->GetUnoControlModel() );
+
+ // replace the model within the parent
+ Reference< XChild > xCurrentAsChild( xCurrentModel, UNO_QUERY );
+ Reference< XNameContainer > xCurrentsParent;
+ if ( xCurrentAsChild.is() )
+ xCurrentsParent.set(xCurrentAsChild->getParent(), css::uno::UNO_QUERY);
+ DBG_ASSERT( xCurrentsParent.is(), "FmUndoModelReplaceAction::Undo: invalid current model!" );
+
+ if ( xCurrentsParent.is() )
+ {
+ // the form container works with FormComponents
+ Reference< XFormComponent > xComponent( m_xReplaced, UNO_QUERY );
+ DBG_ASSERT( xComponent.is(), "FmUndoModelReplaceAction::Undo: the new model is no form component !" );
+
+ Reference< XPropertySet > xCurrentAsSet( xCurrentModel, UNO_QUERY );
+ DBG_ASSERT( ::comphelper::hasProperty(FM_PROP_NAME, xCurrentAsSet ), "FmUndoModelReplaceAction::Undo : one of the models is invalid !");
+
+ OUString sName;
+ xCurrentAsSet->getPropertyValue( FM_PROP_NAME ) >>= sName;
+ xCurrentsParent->replaceByName( sName, Any( xComponent ) );
+
+ m_pObject->SetUnoControlModel(m_xReplaced);
+ m_pObject->SetChanged();
+
+ m_xReplaced = xCurrentModel;
+ }
+ }
+ catch(Exception&)
+ {
+ OSL_FAIL("FmUndoModelReplaceAction::Undo : could not replace the model !");
+ }
+}
+
+
+OUString FmUndoModelReplaceAction::GetComment() const
+{
+ return SvxResId(RID_STR_UNDO_MODEL_REPLACE);
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/form/fmview.cxx b/svx/source/form/fmview.cxx
new file mode 100644
index 000000000..141bdc097
--- /dev/null
+++ b/svx/source/form/fmview.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 <sfx2/docfile.hxx>
+#ifdef REFERENCE
+#undef REFERENCE
+#endif
+#include <com/sun/star/sdb/SQLContext.hpp>
+#include <com/sun/star/sdbc/XConnection.hpp>
+#include <com/sun/star/form/XLoadable.hpp>
+#include <fmvwimp.hxx>
+#include <sfx2/objsh.hxx>
+#include <sfx2/viewsh.hxx>
+#include <sfx2/viewfrm.hxx>
+#include <sfx2/bindings.hxx>
+#include <fmobj.hxx>
+#include <svx/svditer.hxx>
+#include <svx/svdpagv.hxx>
+#include <svx/fmview.hxx>
+#include <svx/fmmodel.hxx>
+#include <svx/fmpage.hxx>
+#include <svx/fmshell.hxx>
+#include <fmshimp.hxx>
+#include <fmservs.hxx>
+#include <fmundo.hxx>
+#include <svx/dataaccessdescriptor.hxx>
+#include <comphelper/namedvaluecollection.hxx>
+#include <o3tl/deleter.hxx>
+#include <toolkit/helper/vclunohelper.hxx>
+#include <com/sun/star/lang/XServiceInfo.hpp>
+#include <com/sun/star/awt/XControl.hpp>
+#include <tools/debug.hxx>
+#include <svx/sdrpagewindow.hxx>
+#include <svx/sdrpaintwindow.hxx>
+#include <svx/svxids.hrc>
+#include <vcl/i18nhelp.hxx>
+#include <vcl/window.hxx>
+#include <o3tl/string_view.hxx>
+
+using namespace ::com::sun::star;
+using namespace ::com::sun::star::uno;
+using namespace ::com::sun::star::lang;
+using namespace ::com::sun::star::sdbc;
+using namespace ::com::sun::star::sdb;
+using namespace ::com::sun::star::beans;
+using namespace ::com::sun::star::container;
+using namespace ::com::sun::star::form;
+using namespace ::com::sun::star::util;
+using namespace ::svxform;
+using namespace ::svx;
+
+FmFormView::FmFormView(
+ SdrModel& rSdrModel,
+ OutputDevice* pOut)
+: E3dView(rSdrModel, pOut)
+{
+ Init();
+}
+
+void FmFormView::Init()
+{
+ pFormShell = nullptr;
+ pImpl = new FmXFormView(this);
+
+ // set model
+ SdrModel* pModel = GetModel();
+
+ DBG_ASSERT( dynamic_cast<const FmFormModel*>( pModel) != nullptr, "Wrong model" );
+ FmFormModel* pFormModel = dynamic_cast<FmFormModel*>(pModel);
+ if( !pFormModel )
+ return;
+
+ // get DesignMode from model
+ bool bInitDesignMode = pFormModel->GetOpenInDesignMode();
+ if ( pFormModel->OpenInDesignModeIsDefaulted( ) )
+ { // this means that nobody ever explicitly set this on the model, and the model has never
+ // been loaded from a stream.
+ // This means this is a newly created document. This means, we want to have it in design
+ // mode by default (though a newly created model returns true for GetOpenInDesignMode).
+ // We _want_ to have this because it makes a lot of hacks following the original fix
+ DBG_ASSERT( !bInitDesignMode, "FmFormView::Init: doesn't the model default to FALSE anymore?" );
+ // if this asserts, either the on-construction default in the model has changed (then this here
+ // may not be necessary anymore), or we're not dealing with a new document...
+ bInitDesignMode = true;
+ }
+
+ SfxObjectShell* pObjShell = pFormModel->GetObjectShell();
+ if ( pObjShell && pObjShell->GetMedium() )
+ {
+ if ( const SfxUnoAnyItem *pItem = pObjShell->GetMedium()->GetItemSet()->GetItemIfSet( SID_COMPONENTDATA, false ) )
+ {
+ ::comphelper::NamedValueCollection aComponentData( pItem->GetValue() );
+ bInitDesignMode = aComponentData.getOrDefault( "ApplyFormDesignMode", bInitDesignMode );
+ }
+ }
+
+ // this will be done in the shell
+ // bDesignMode = !bInitDesignMode; // forces execution of SetDesignMode
+ SetDesignMode( bInitDesignMode );
+}
+
+FmFormView::~FmFormView()
+{
+ if (pFormShell)
+ suppress_fun_call_w_exception(pFormShell->SetView(nullptr));
+
+ pImpl->notifyViewDying();
+}
+
+FmFormPage* FmFormView::GetCurPage()
+{
+ SdrPageView* pPageView = GetSdrPageView();
+ FmFormPage* pCurPage = pPageView ? dynamic_cast<FmFormPage*>( pPageView->GetPage() ) : nullptr;
+ return pCurPage;
+}
+
+void FmFormView::MarkListHasChanged()
+{
+ E3dView::MarkListHasChanged();
+
+ if ( !(pFormShell && IsDesignMode()) )
+ return;
+
+ FmFormObj* pObj = getMarkedGrid();
+ if ( pImpl->m_pMarkedGrid && pImpl->m_pMarkedGrid != pObj )
+ {
+ pImpl->m_pMarkedGrid = nullptr;
+ if ( pImpl->m_xWindow.is() )
+ {
+ pImpl->m_xWindow->removeFocusListener(pImpl);
+ pImpl->m_xWindow = nullptr;
+ }
+ SetMoveOutside(false);
+ //OLMRefreshAllIAOManagers();
+ }
+
+ pFormShell->GetImpl()->SetSelectionDelayed_Lock();
+}
+
+namespace
+{
+ const SdrPageWindow* findPageWindow( const SdrPaintView* _pView, OutputDevice const * _pWindow )
+ {
+ SdrPageView* pPageView = _pView->GetSdrPageView();
+ if(pPageView)
+ {
+ for ( sal_uInt32 window = 0; window < pPageView->PageWindowCount(); ++window )
+ {
+ const SdrPageWindow* pPageWindow = pPageView->GetPageWindow( window );
+ if ( !pPageWindow || &pPageWindow->GetPaintWindow().GetOutputDevice() != _pWindow )
+ continue;
+
+ return pPageWindow;
+ }
+ }
+ return nullptr;
+ }
+}
+
+
+void FmFormView::AddWindowToPaintView(OutputDevice* pNewWin, vcl::Window* pWindow)
+{
+ E3dView::AddWindowToPaintView(pNewWin, pWindow);
+
+ if ( !pNewWin )
+ return;
+
+ // look up the PageViewWindow for the newly inserted window, and care for it
+ // #i39269# / 2004-12-20 / frank.schoenheit@sun.com
+ const SdrPageWindow* pPageWindow = findPageWindow( this, pNewWin );
+ if ( pPageWindow )
+ pImpl->addWindow( *pPageWindow );
+}
+
+
+void FmFormView::DeleteWindowFromPaintView(OutputDevice* pNewWin)
+{
+ const SdrPageWindow* pPageWindow = findPageWindow( this, pNewWin );
+ if ( pPageWindow )
+ pImpl->removeWindow( pPageWindow->GetControlContainer() );
+
+ E3dView::DeleteWindowFromPaintView(pNewWin);
+}
+
+
+void FmFormView::ChangeDesignMode(bool bDesign)
+{
+ if (bDesign == IsDesignMode())
+ return;
+
+ FmFormModel* pModel = dynamic_cast<FmFormModel*>( GetModel() );
+ if (pModel)
+ { // For the duration of the transition the Undo-Environment is disabled. This ensures that non-transient Properties can
+ // also be changed (this should be done with care and also reversed before switching the mode back. An example is the
+ // setting of the maximal length of the text by FmXEditModel on its control.)
+ pModel->GetUndoEnv().Lock();
+ }
+
+ // --- 1. deactivate all controls if we are switching to design mode
+ if ( bDesign )
+ DeactivateControls( GetSdrPageView() );
+
+ // --- 2. simulate a deactivation (the shell will handle some things there ...?)
+ if ( pFormShell && pFormShell->GetImpl() )
+ pFormShell->GetImpl()->viewDeactivated_Lock(*this);
+ else
+ pImpl->Deactivate();
+
+ // --- 3. activate all controls, if we're switching to alive mode
+ if ( !bDesign )
+ ActivateControls( GetSdrPageView() );
+
+ // --- 4. load resp. unload the forms
+ FmFormPage* pCurPage = GetCurPage();
+ if ( pCurPage )
+ {
+ if ( pFormShell && pFormShell->GetImpl() )
+ pFormShell->GetImpl()->loadForms_Lock(pCurPage, (bDesign ? LoadFormsFlags::Unload : LoadFormsFlags::Load));
+ }
+
+ // --- 5. base class functionality
+ SetDesignMode( bDesign );
+
+ // --- 6. simulate an activation (the shell will handle some things there ...?)
+ OSL_PRECOND( pFormShell && pFormShell->GetImpl(), "FmFormView::ChangeDesignMode: is this really allowed? No shell?" );
+ if ( pFormShell && pFormShell->GetImpl() )
+ pFormShell->GetImpl()->viewActivated_Lock(*this);
+ else
+ pImpl->Activate();
+
+ if ( pCurPage )
+ {
+ if ( bDesign )
+ {
+ if ( GetActualOutDev() && GetActualOutDev()->GetOutDevType() == OUTDEV_WINDOW )
+ {
+ const vcl::Window* pWindow = GetActualOutDev()->GetOwnerWindow();
+ const_cast< vcl::Window* >( pWindow )->GrabFocus();
+ }
+
+ // redraw UNO objects
+ if ( GetSdrPageView() )
+ {
+ SdrObjListIter aIter(pCurPage);
+ while( aIter.IsMore() )
+ {
+ SdrObject* pObj = aIter.Next();
+ if (pObj && pObj->IsUnoObj())
+ {
+ // For redraw just use ActionChanged()
+ // pObj->BroadcastObjectChange();
+ pObj->ActionChanged();
+ }
+ }
+ }
+ }
+ else
+ {
+ // set the auto focus to the first control (if indicated by the model to do so)
+ bool bForceControlFocus = pModel && pModel->GetAutoControlFocus();
+ if (bForceControlFocus)
+ pImpl->AutoFocus();
+ }
+ }
+
+ // Unlock Undo-Environment
+ if (pModel)
+ pModel->GetUndoEnv().UnLock();
+}
+
+
+void FmFormView::GrabFirstControlFocus()
+{
+ if ( !IsDesignMode() )
+ pImpl->AutoFocus();
+}
+
+
+SdrPageView* FmFormView::ShowSdrPage(SdrPage* pPage)
+{
+ SdrPageView* pPV = E3dView::ShowSdrPage(pPage);
+
+ if (pPage)
+ {
+ if (!IsDesignMode())
+ {
+ // creating the controllers
+ ActivateControls(pPV);
+
+ // Deselect all
+ UnmarkAll();
+ }
+ else if ( pFormShell && pFormShell->IsDesignMode() )
+ {
+ FmXFormShell* pFormShellImpl = pFormShell->GetImpl();
+ pFormShellImpl->UpdateForms_Lock(true);
+
+ // so that the form navigator can react to the pagechange
+ pFormShell->GetViewShell()->GetViewFrame()->GetBindings().Invalidate(SID_FM_FMEXPLORER_CONTROL, true);
+
+ pFormShellImpl->SetSelection_Lock(GetMarkedObjectList());
+ }
+ }
+
+ // notify our shell that we have been activated
+ if ( pFormShell && pFormShell->GetImpl() )
+ pFormShell->GetImpl()->viewActivated_Lock(*this);
+ else
+ pImpl->Activate();
+
+ return pPV;
+}
+
+
+void FmFormView::HideSdrPage()
+{
+ // --- 1. deactivate controls
+ if ( !IsDesignMode() )
+ DeactivateControls(GetSdrPageView());
+
+ // --- 2. tell the shell the view is (going to be) deactivated
+ if ( pFormShell && pFormShell->GetImpl() )
+ pFormShell->GetImpl()->viewDeactivated_Lock(*this);
+ else
+ pImpl->Deactivate();
+
+ // --- 3. base class behavior
+ E3dView::HideSdrPage();
+}
+
+
+void FmFormView::ActivateControls(SdrPageView const * pPageView)
+{
+ if (!pPageView)
+ return;
+
+ for (sal_uInt32 i = 0; i < pPageView->PageWindowCount(); ++i)
+ {
+ const SdrPageWindow& rPageWindow = *pPageView->GetPageWindow(i);
+ pImpl->addWindow(rPageWindow);
+ }
+}
+
+
+void FmFormView::DeactivateControls(SdrPageView const * pPageView)
+{
+ if( !pPageView )
+ return;
+
+ for (sal_uInt32 i = 0; i < pPageView->PageWindowCount(); ++i)
+ {
+ const SdrPageWindow& rPageWindow = *pPageView->GetPageWindow(i);
+ pImpl->removeWindow(rPageWindow.GetControlContainer() );
+ }
+}
+
+
+SdrObjectUniquePtr FmFormView::CreateFieldControl( const ODataAccessDescriptor& _rColumnDescriptor )
+{
+ return pImpl->implCreateFieldControl( _rColumnDescriptor );
+}
+
+
+SdrObjectUniquePtr FmFormView::CreateXFormsControl( const OXFormsDescriptor &_rDesc )
+{
+ return pImpl->implCreateXFormsControl(_rDesc);
+}
+
+
+SdrObjectUniquePtr FmFormView::CreateFieldControl(std::u16string_view rFieldDesc) const
+{
+ sal_Int32 nIdx{ 0 };
+ OUString sDataSource( o3tl::getToken(rFieldDesc, 0, u'\x000B', nIdx));
+ OUString sObjectName( o3tl::getToken(rFieldDesc, 0, u'\x000B', nIdx));
+ sal_uInt16 nObjectType = static_cast<sal_uInt16>(o3tl::toInt32(o3tl::getToken(rFieldDesc, 0, u'\x000B', nIdx)));
+ OUString sFieldName( o3tl::getToken(rFieldDesc, 0, u'\x000B', nIdx));
+
+ if (sFieldName.isEmpty() || sObjectName.isEmpty() || sDataSource.isEmpty())
+ return nullptr;
+
+ ODataAccessDescriptor aColumnDescriptor;
+ aColumnDescriptor.setDataSource(sDataSource);
+ aColumnDescriptor[ DataAccessDescriptorProperty::Command ] <<= sObjectName;
+ aColumnDescriptor[ DataAccessDescriptorProperty::CommandType ] <<= nObjectType;
+ aColumnDescriptor[ DataAccessDescriptorProperty::ColumnName ] <<= sFieldName;
+
+ return pImpl->implCreateFieldControl( aColumnDescriptor );
+}
+
+
+void FmFormView::InsertControlContainer(const Reference< css::awt::XControlContainer > & xCC)
+{
+ if( IsDesignMode() )
+ return;
+
+ SdrPageView* pPageView = GetSdrPageView();
+ if( !pPageView )
+ return;
+
+ for( sal_uInt32 i = 0; i < pPageView->PageWindowCount(); i++ )
+ {
+ const SdrPageWindow& rPageWindow = *pPageView->GetPageWindow(i);
+
+ if( rPageWindow.GetControlContainer( false ) == xCC )
+ {
+ pImpl->addWindow(rPageWindow);
+ break;
+ }
+ }
+}
+
+
+void FmFormView::RemoveControlContainer(const Reference< css::awt::XControlContainer > & xCC)
+{
+ if( !IsDesignMode() )
+ {
+ pImpl->removeWindow( xCC );
+ }
+}
+
+
+SdrPaintWindow* FmFormView::BeginCompleteRedraw(OutputDevice* pOut)
+{
+ SdrPaintWindow* pPaintWindow = E3dView::BeginCompleteRedraw( pOut );
+ pImpl->suspendTabOrderUpdate();
+ return pPaintWindow;
+}
+
+
+void FmFormView::EndCompleteRedraw( SdrPaintWindow& rPaintWindow, bool bPaintFormLayer )
+{
+ E3dView::EndCompleteRedraw( rPaintWindow, bPaintFormLayer );
+ pImpl->resumeTabOrderUpdate();
+}
+
+
+bool FmFormView::KeyInput(const KeyEvent& rKEvt, vcl::Window* pWin)
+{
+ bool bDone = false;
+ const vcl::KeyCode& rKeyCode = rKEvt.GetKeyCode();
+ if ( IsDesignMode()
+ && rKeyCode.GetCode() == KEY_RETURN
+ )
+ {
+ // RETURN alone enters grid controls, for keyboard accessibility
+ if ( pWin
+ && !rKeyCode.IsShift()
+ && !rKeyCode.IsMod1()
+ && !rKeyCode.IsMod2()
+ )
+ {
+ FmFormObj* pObj = getMarkedGrid();
+ if ( pObj )
+ {
+ Reference< awt::XWindow > xWindow( pObj->GetUnoControl( *this, *pWin->GetOutDev() ), UNO_QUERY );
+ if ( xWindow.is() )
+ {
+ pImpl->m_pMarkedGrid = pObj;
+ pImpl->m_xWindow = xWindow;
+ // add as listener to get notified when ESC will be pressed inside the grid
+ pImpl->m_xWindow->addFocusListener(pImpl);
+ SetMoveOutside(true);
+ //OLMRefreshAllIAOManagers();
+ xWindow->setFocus();
+ bDone = true;
+ }
+ }
+ }
+ // Alt-RETURN alone shows the properties of the selection
+ if ( pFormShell
+ && pFormShell->GetImpl()
+ && !rKeyCode.IsShift()
+ && !rKeyCode.IsMod1()
+ && rKeyCode.IsMod2()
+ )
+ {
+ pFormShell->GetImpl()->handleShowPropertiesRequest_Lock();
+ }
+
+ }
+
+ // tdf#139804 Allow selecting form controls with Alt-<Mnemonic>
+ if (rKeyCode.IsMod2() && rKeyCode.GetCode())
+ {
+ if (FmFormPage* pCurPage = GetCurPage())
+ {
+ for (size_t a = 0; a < pCurPage->GetObjCount(); ++a)
+ {
+ SdrObject* pObj = pCurPage->GetObj(a);
+ FmFormObj* pFormObject = FmFormObj::GetFormObject(pObj);
+ if (!pFormObject)
+ continue;
+
+ Reference<awt::XControl> xControl = pFormObject->GetUnoControl(*this, *pWin->GetOutDev());
+ if (!xControl.is())
+ continue;
+ const vcl::I18nHelper& rI18nHelper = Application::GetSettings().GetUILocaleI18nHelper();
+ VclPtr<vcl::Window> pWindow = VCLUnoHelper::GetWindow(xControl->getPeer());
+ if (rI18nHelper.MatchMnemonic(pWindow->GetText(), rKEvt.GetCharCode()))
+ {
+ pWindow->GrabFocus();
+ pWindow->KeyInput(rKEvt);
+ bDone = true;
+ break;
+ }
+ }
+ }
+ }
+
+ if ( !bDone )
+ bDone = E3dView::KeyInput(rKEvt,pWin);
+ return bDone;
+}
+
+bool FmFormView::checkUnMarkAll(const Reference< XInterface >& _xSource)
+{
+ Reference< css::awt::XControl> xControl(pImpl->m_xWindow,UNO_QUERY);
+ bool bRet = !xControl.is() || !_xSource.is() || _xSource != xControl->getModel();
+ if ( bRet )
+ UnmarkAll();
+
+ return bRet;
+}
+
+
+bool FmFormView::MouseButtonDown( const MouseEvent& _rMEvt, OutputDevice* _pWin )
+{
+ bool bReturn = E3dView::MouseButtonDown( _rMEvt, _pWin );
+
+ if ( pFormShell && pFormShell->GetImpl() )
+ {
+ SdrViewEvent aViewEvent;
+ PickAnything( _rMEvt, SdrMouseEventKind::BUTTONDOWN, aViewEvent );
+ pFormShell->GetImpl()->handleMouseButtonDown_Lock(aViewEvent);
+ }
+
+ return bReturn;
+}
+
+
+FmFormObj* FmFormView::getMarkedGrid() const
+{
+ FmFormObj* pFormObject = nullptr;
+ const SdrMarkList& rMarkList = GetMarkedObjectList();
+ if ( 1 == rMarkList.GetMarkCount() )
+ {
+ SdrMark* pMark = rMarkList.GetMark(0);
+ if ( pMark )
+ {
+ pFormObject = FmFormObj::GetFormObject( pMark->GetMarkedSdrObj() );
+ if ( pFormObject )
+ {
+ Reference< XServiceInfo > xServInfo( pFormObject->GetUnoControlModel(), UNO_QUERY );
+ if ( !xServInfo.is() || !xServInfo->supportsService( FM_SUN_COMPONENT_GRIDCONTROL ) )
+ pFormObject = nullptr;
+ }
+ }
+ }
+ return pFormObject;
+}
+
+void FmFormView::createControlLabelPair( OutputDevice const * _pOutDev, sal_Int32 _nXOffsetMM, sal_Int32 _nYOffsetMM,
+ const Reference< XPropertySet >& _rxField, const Reference< XNumberFormats >& _rxNumberFormats,
+ SdrObjKind _nControlObjectID, SdrInventor _nInventor, SdrObjKind _nLabelObjectID,
+ SdrModel& _rModel,
+ std::unique_ptr<SdrUnoObj, SdrObjectFreeOp>& _rpLabel,
+ std::unique_ptr<SdrUnoObj, SdrObjectFreeOp>& _rpControl )
+{
+ FmXFormView::createControlLabelPair(
+ *_pOutDev, _nXOffsetMM, _nYOffsetMM,
+ _rxField, _rxNumberFormats,
+ _nControlObjectID, u"", _nInventor, _nLabelObjectID,
+ _rModel,
+ _rpLabel, _rpControl
+ );
+}
+
+Reference< runtime::XFormController > FmFormView::GetFormController( const Reference< XForm >& _rxForm, const OutputDevice& _rDevice ) const
+{
+ return pImpl->getFormController( _rxForm, _rDevice );
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/form/fmvwimp.cxx b/svx/source/form/fmvwimp.cxx
new file mode 100644
index 000000000..c95c5d710
--- /dev/null
+++ b/svx/source/form/fmvwimp.cxx
@@ -0,0 +1,1918 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * 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 <fmdocumentclassification.hxx>
+#include <fmobj.hxx>
+#include <fmpgeimp.hxx>
+#include <fmprop.hxx>
+#include <svx/strings.hrc>
+#include <fmservs.hxx>
+#include <fmshimp.hxx>
+#include <svx/fmtools.hxx>
+#include <fmvwimp.hxx>
+#include <formcontrolfactory.hxx>
+#include <svx/sdrpaintwindow.hxx>
+#include <svx/svditer.hxx>
+#include <svx/dataaccessdescriptor.hxx>
+#include <svx/dialmgr.hxx>
+#include <svx/svdobjkind.hxx>
+#include <svx/fmmodel.hxx>
+#include <svx/fmpage.hxx>
+#include <svx/fmshell.hxx>
+#include <svx/fmview.hxx>
+#include <svx/sdrpagewindow.hxx>
+#include <svx/svdogrp.hxx>
+#include <svx/svdpagv.hxx>
+#include <svx/xmlexchg.hxx>
+#include <toolkit/helper/vclunohelper.hxx>
+
+#include <com/sun/star/lang/IndexOutOfBoundsException.hpp>
+#include <com/sun/star/ui/dialogs/XExecutableDialog.hpp>
+#include <com/sun/star/sdbc/XRowSet.hpp>
+#include <com/sun/star/util/XNumberFormatsSupplier.hpp>
+#include <com/sun/star/util/XNumberFormats.hpp>
+#include <com/sun/star/sdb/CommandType.hpp>
+#include <com/sun/star/sdbc/DataType.hpp>
+#include <com/sun/star/form/FormComponentType.hpp>
+#include <com/sun/star/form/FormButtonType.hpp>
+#include <com/sun/star/form/binding/XBindableValue.hpp>
+#include <com/sun/star/form/binding/XValueBinding.hpp>
+#include <com/sun/star/form/runtime/FormController.hpp>
+#include <com/sun/star/form/submission/XSubmissionSupplier.hpp>
+#include <com/sun/star/awt/XTabControllerModel.hpp>
+#include <com/sun/star/awt/XControlContainer.hpp>
+#include <com/sun/star/awt/XTabController.hpp>
+#include <com/sun/star/container/XIndexAccess.hpp>
+#include <com/sun/star/awt/XControl.hpp>
+#include <com/sun/star/sdbc/SQLException.hpp>
+#include <com/sun/star/sdb/XQueriesSupplier.hpp>
+#include <com/sun/star/container/XContainer.hpp>
+
+#include <comphelper/namedvaluecollection.hxx>
+#include <comphelper/property.hxx>
+#include <comphelper/processfactory.hxx>
+#include <comphelper/types.hxx>
+#include <cppuhelper/exc_hlp.hxx>
+#include <unotools/moduleoptions.hxx>
+#include <tools/debug.hxx>
+#include <tools/diagnose_ex.h>
+#include <sal/log.hxx>
+#include <vcl/svapp.hxx>
+#include <vcl/stdtext.hxx>
+#include <vcl/window.hxx>
+#include <connectivity/dbtools.hxx>
+
+#include <algorithm>
+
+using namespace ::comphelper;
+using namespace ::svx;
+using namespace ::svxform;
+using namespace ::dbtools;
+
+ using namespace ::com::sun::star;
+ using ::com::sun::star::uno::Exception;
+ using ::com::sun::star::uno::XInterface;
+ using ::com::sun::star::uno::Sequence;
+ using ::com::sun::star::uno::UNO_QUERY;
+ using ::com::sun::star::uno::UNO_QUERY_THROW;
+ using ::com::sun::star::uno::UNO_SET_THROW;
+ using ::com::sun::star::uno::Type;
+ using ::com::sun::star::uno::Reference;
+ using ::com::sun::star::uno::Any;
+ using ::com::sun::star::uno::XComponentContext;
+ using ::com::sun::star::form::FormButtonType_SUBMIT;
+ using ::com::sun::star::form::binding::XValueBinding;
+ using ::com::sun::star::form::binding::XBindableValue;
+ using ::com::sun::star::lang::XComponent;
+ using ::com::sun::star::container::XIndexAccess;
+ using ::com::sun::star::form::runtime::FormController;
+ using ::com::sun::star::form::runtime::XFormController;
+ using ::com::sun::star::script::XEventAttacherManager;
+ using ::com::sun::star::awt::XTabControllerModel;
+ using ::com::sun::star::container::XChild;
+ using ::com::sun::star::task::XInteractionHandler;
+ using ::com::sun::star::awt::XTabController;
+ using ::com::sun::star::awt::XControlContainer;
+ using ::com::sun::star::awt::XControl;
+ using ::com::sun::star::form::XFormComponent;
+ using ::com::sun::star::form::XForm;
+ using ::com::sun::star::lang::IndexOutOfBoundsException;
+ using ::com::sun::star::container::XContainer;
+ using ::com::sun::star::container::ContainerEvent;
+ using ::com::sun::star::lang::EventObject;
+ using ::com::sun::star::sdb::SQLErrorEvent;
+ using ::com::sun::star::sdbc::XRowSet;
+ using ::com::sun::star::beans::XPropertySet;
+ using ::com::sun::star::container::XElementAccess;
+ using ::com::sun::star::awt::XWindow;
+ using ::com::sun::star::awt::FocusEvent;
+ using ::com::sun::star::ui::dialogs::XExecutableDialog;
+ using ::com::sun::star::sdbc::XDataSource;
+ using ::com::sun::star::container::XIndexContainer;
+ using ::com::sun::star::sdbc::XConnection;
+ using ::com::sun::star::container::XNameAccess;
+ using ::com::sun::star::sdbc::SQLException;
+ using ::com::sun::star::util::XNumberFormatsSupplier;
+ using ::com::sun::star::util::XNumberFormats;
+ using ::com::sun::star::beans::XPropertySetInfo;
+
+ namespace FormComponentType = ::com::sun::star::form::FormComponentType;
+ namespace CommandType = ::com::sun::star::sdb::CommandType;
+ namespace DataType = ::com::sun::star::sdbc::DataType;
+
+
+class FmXFormView::ObjectRemoveListener : public SfxListener
+{
+ FmXFormView* m_pParent;
+public:
+ explicit ObjectRemoveListener( FmXFormView* pParent );
+ virtual void Notify( SfxBroadcaster& rBC, const SfxHint& rHint ) override;
+};
+
+FormViewPageWindowAdapter::FormViewPageWindowAdapter( const css::uno::Reference<css::uno::XComponentContext>& _rContext, const SdrPageWindow& _rWindow, FmXFormView* _pViewImpl )
+: m_xControlContainer( _rWindow.GetControlContainer() ),
+ m_xContext( _rContext ),
+ m_pViewImpl( _pViewImpl ),
+ m_pWindow( _rWindow.GetPaintWindow().GetOutputDevice().GetOwnerWindow() )
+{
+
+ // create an XFormController for every form
+ FmFormPage* pFormPage = dynamic_cast< FmFormPage* >( _rWindow.GetPageView().GetPage() );
+ DBG_ASSERT( pFormPage, "FormViewPageWindowAdapter::FormViewPageWindowAdapter: no FmFormPage found!" );
+ if ( !pFormPage )
+ return;
+
+ try
+ {
+ Reference< XIndexAccess > xForms( pFormPage->GetForms(), UNO_QUERY_THROW );
+ sal_uInt32 nLength = xForms->getCount();
+ for (sal_uInt32 i = 0; i < nLength; i++)
+ {
+ Reference< XForm > xForm( xForms->getByIndex(i), UNO_QUERY );
+ if ( xForm.is() )
+ setController( xForm, nullptr );
+ }
+ }
+ catch (const Exception&)
+ {
+ DBG_UNHANDLED_EXCEPTION("svx");
+ }
+}
+
+FormViewPageWindowAdapter::~FormViewPageWindowAdapter()
+{
+}
+
+void FormViewPageWindowAdapter::dispose()
+{
+ for ( ::std::vector< Reference< XFormController > >::const_iterator i = m_aControllerList.begin();
+ i != m_aControllerList.end();
+ ++i
+ )
+ {
+ try
+ {
+ Reference< XFormController > xController( *i, UNO_SET_THROW );
+
+ // detaching the events
+ Reference< XChild > xControllerModel( xController->getModel(), UNO_QUERY );
+ if ( xControllerModel.is() )
+ {
+ Reference< XEventAttacherManager > xEventManager( xControllerModel->getParent(), UNO_QUERY_THROW );
+ Reference< XInterface > xControllerNormalized( xController, UNO_QUERY_THROW );
+ xEventManager->detach( i - m_aControllerList.begin(), xControllerNormalized );
+ }
+
+ // dispose the formcontroller
+ xController->dispose();
+ }
+ catch (const Exception&)
+ {
+ DBG_UNHANDLED_EXCEPTION("svx");
+ }
+ }
+
+ m_aControllerList.clear();
+}
+
+sal_Bool SAL_CALL FormViewPageWindowAdapter::hasElements()
+{
+ return getCount() != 0;
+}
+
+Type SAL_CALL FormViewPageWindowAdapter::getElementType()
+{
+ return cppu::UnoType<XFormController>::get();
+}
+
+// XIndexAccess
+sal_Int32 SAL_CALL FormViewPageWindowAdapter::getCount()
+{
+ return m_aControllerList.size();
+}
+
+Any SAL_CALL FormViewPageWindowAdapter::getByIndex(sal_Int32 nIndex)
+{
+ if (nIndex < 0 ||
+ nIndex >= getCount())
+ throw IndexOutOfBoundsException();
+
+ Any aElement;
+ aElement <<= m_aControllerList[nIndex];
+ return aElement;
+}
+
+void SAL_CALL FormViewPageWindowAdapter::makeVisible( const Reference< XControl >& Control )
+{
+ SolarMutexGuard aSolarGuard;
+
+ Reference< XWindow > xWindow( Control, UNO_QUERY );
+ if ( xWindow.is() && m_pViewImpl->getView() && m_pWindow )
+ {
+ awt::Rectangle aRect = xWindow->getPosSize();
+ ::tools::Rectangle aNewRect( aRect.X, aRect.Y, aRect.X + aRect.Width, aRect.Y + aRect.Height );
+ aNewRect = m_pWindow->PixelToLogic( aNewRect );
+ m_pViewImpl->getView()->MakeVisible( aNewRect, *m_pWindow );
+ }
+}
+
+static Reference< XFormController > getControllerSearchChildren( const Reference< XIndexAccess > & xIndex, const Reference< XTabControllerModel > & xModel)
+{
+ if (xIndex.is() && xIndex->getCount())
+ {
+ Reference< XFormController > xController;
+
+ for (sal_Int32 n = xIndex->getCount(); n-- && !xController.is(); )
+ {
+ xIndex->getByIndex(n) >>= xController;
+ if (xModel.get() == xController->getModel().get())
+ return xController;
+ else
+ {
+ xController = getControllerSearchChildren(xController, xModel);
+ if ( xController.is() )
+ return xController;
+ }
+ }
+ }
+ return Reference< XFormController > ();
+}
+
+// Search the according controller
+Reference< XFormController > FormViewPageWindowAdapter::getController( const Reference< XForm > & xForm ) const
+{
+ Reference< XTabControllerModel > xModel(xForm, UNO_QUERY);
+ for (const auto& rpController : m_aControllerList)
+ {
+ if (rpController->getModel().get() == xModel.get())
+ return rpController;
+
+ // the current-round controller isn't the right one. perhaps one of its children ?
+ Reference< XFormController > xChildSearch = getControllerSearchChildren(Reference< XIndexAccess > (rpController, UNO_QUERY), xModel);
+ if (xChildSearch.is())
+ return xChildSearch;
+ }
+ return Reference< XFormController > ();
+}
+
+
+void FormViewPageWindowAdapter::setController(const Reference< XForm > & xForm, const Reference< XFormController >& _rxParentController )
+{
+ DBG_ASSERT( xForm.is(), "FormViewPageWindowAdapter::setController: there should be a form!" );
+ Reference< XIndexAccess > xFormCps(xForm, UNO_QUERY);
+ if (!xFormCps.is())
+ return;
+
+ Reference< XTabControllerModel > xTabOrder(xForm, UNO_QUERY);
+
+ // create a form controller
+ Reference< XFormController > xController( FormController::create(m_xContext) );
+
+ Reference< XInteractionHandler > xHandler;
+ if ( _rxParentController.is() )
+ xHandler = _rxParentController->getInteractionHandler();
+ else
+ {
+ // TODO: should we create a default handler? Not really necessary, since the
+ // FormController itself has a default fallback
+ }
+ if ( xHandler.is() )
+ xController->setInteractionHandler( xHandler );
+
+ xController->setContext( this );
+
+ xController->setModel( xTabOrder );
+ xController->setContainer( m_xControlContainer );
+ xController->activateTabOrder();
+ xController->addActivateListener( m_pViewImpl );
+
+ if ( _rxParentController.is() )
+ _rxParentController->addChildController( xController );
+ else
+ {
+ m_aControllerList.push_back(xController);
+
+ xController->setParent( *this );
+
+ // attaching the events
+ Reference< XEventAttacherManager > xEventManager( xForm->getParent(), UNO_QUERY );
+ xEventManager->attach(m_aControllerList.size() - 1, Reference<XInterface>( xController, UNO_QUERY ), Any(xController) );
+ }
+
+ // now go through the subforms
+ sal_uInt32 nLength = xFormCps->getCount();
+ Reference< XForm > xSubForm;
+ for (sal_uInt32 i = 0; i < nLength; i++)
+ {
+ if ( xFormCps->getByIndex(i) >>= xSubForm )
+ setController( xSubForm, xController );
+ }
+}
+
+
+void FormViewPageWindowAdapter::updateTabOrder( const Reference< XForm >& _rxForm )
+{
+ OSL_PRECOND( _rxForm.is(), "FormViewPageWindowAdapter::updateTabOrder: illegal argument!" );
+ if ( !_rxForm.is() )
+ return;
+
+ try
+ {
+ Reference< XTabController > xTabCtrl( getController( _rxForm ) );
+ if ( xTabCtrl.is() )
+ { // if there already is a TabController for this form, then delegate the "updateTabOrder" request
+ xTabCtrl->activateTabOrder();
+ }
+ else
+ { // otherwise, create a TabController
+
+ // if it's a sub form, then we must ensure there exist TabControllers
+ // for all its ancestors, too
+ Reference< XForm > xParentForm( _rxForm->getParent(), UNO_QUERY );
+ // there is a parent form -> look for the respective controller
+ Reference< XFormController > xParentController;
+ if ( xParentForm.is() )
+ xParentController = getController( xParentForm );
+
+ setController( _rxForm, xParentController );
+ }
+ }
+ catch (const Exception&)
+ {
+ DBG_UNHANDLED_EXCEPTION("svx");
+ }
+}
+
+
+FmXFormView::FmXFormView(FmFormView* _pView )
+ :m_pMarkedGrid(nullptr)
+ ,m_pView(_pView)
+ ,m_nActivationEvent(nullptr)
+ ,m_nErrorMessageEvent( nullptr )
+ ,m_nAutoFocusEvent( nullptr )
+ ,m_nControlWizardEvent( nullptr )
+ ,m_bFirstActivation( true )
+ ,m_isTabOrderUpdateSuspended( false )
+{
+}
+
+
+void FmXFormView::cancelEvents()
+{
+ if ( m_nActivationEvent )
+ {
+ Application::RemoveUserEvent( m_nActivationEvent );
+ m_nActivationEvent = nullptr;
+ }
+
+ if ( m_nErrorMessageEvent )
+ {
+ Application::RemoveUserEvent( m_nErrorMessageEvent );
+ m_nErrorMessageEvent = nullptr;
+ }
+
+ if ( m_nAutoFocusEvent )
+ {
+ Application::RemoveUserEvent( m_nAutoFocusEvent );
+ m_nAutoFocusEvent = nullptr;
+ }
+
+ if ( m_nControlWizardEvent )
+ {
+ Application::RemoveUserEvent( m_nControlWizardEvent );
+ m_nControlWizardEvent = nullptr;
+ }
+}
+
+
+void FmXFormView::notifyViewDying( )
+{
+ DBG_ASSERT( m_pView, "FmXFormView::notifyViewDying: my view already died!" );
+ m_pView = nullptr;
+ cancelEvents();
+}
+
+
+FmXFormView::~FmXFormView()
+{
+ DBG_ASSERT( m_aPageWindowAdapters.empty(), "FmXFormView::~FmXFormView: Window list not empty!" );
+ for (const auto& rpAdapter : m_aPageWindowAdapters)
+ {
+ rpAdapter->dispose();
+ }
+
+ cancelEvents();
+}
+
+// EventListener
+
+void SAL_CALL FmXFormView::disposing(const EventObject& Source)
+{
+ if ( m_xWindow.is() && Source.Source == m_xWindow )
+ {
+ m_xWindow->removeFocusListener(this);
+ if ( m_pView )
+ {
+ m_pView->SetMoveOutside( false, FmFormView::ImplAccess() );
+ }
+ m_xWindow = nullptr;
+ }
+}
+
+// XFormControllerListener
+
+void SAL_CALL FmXFormView::formActivated(const EventObject& rEvent)
+{
+ if ( m_pView && m_pView->GetFormShell() && m_pView->GetFormShell()->GetImpl() )
+ m_pView->GetFormShell()->GetImpl()->formActivated( rEvent );
+}
+
+
+void SAL_CALL FmXFormView::formDeactivated(const EventObject& rEvent)
+{
+ if ( m_pView && m_pView->GetFormShell() && m_pView->GetFormShell()->GetImpl() )
+ m_pView->GetFormShell()->GetImpl()->formDeactivated( rEvent );
+}
+
+// XContainerListener
+
+void SAL_CALL FmXFormView::elementInserted(const ContainerEvent& evt)
+{
+ try
+ {
+ Reference< XControlContainer > xControlContainer( evt.Source, UNO_QUERY_THROW );
+ Reference< XControl > xControl( evt.Element, UNO_QUERY_THROW );
+ Reference< XFormComponent > xControlModel( xControl->getModel(), UNO_QUERY_THROW );
+ Reference< XForm > xForm( xControlModel->getParent(), UNO_QUERY_THROW );
+
+ if ( m_isTabOrderUpdateSuspended )
+ {
+ // remember the container and the control, so we can update the tab order on resumeTabOrderUpdate
+ m_aNeedTabOrderUpdate[ xControlContainer ].insert( xForm );
+ }
+ else
+ {
+ rtl::Reference< FormViewPageWindowAdapter > pAdapter = findWindow( xControlContainer );
+ if ( pAdapter.is() )
+ pAdapter->updateTabOrder( xForm );
+ }
+ }
+ catch (const Exception&)
+ {
+ DBG_UNHANDLED_EXCEPTION("svx");
+ }
+}
+
+
+void SAL_CALL FmXFormView::elementReplaced(const ContainerEvent& evt)
+{
+ elementInserted(evt);
+}
+
+
+void SAL_CALL FmXFormView::elementRemoved(const ContainerEvent& /*evt*/)
+{
+}
+
+
+rtl::Reference< FormViewPageWindowAdapter > FmXFormView::findWindow( const Reference< XControlContainer >& _rxCC ) const
+{
+ auto i = std::find_if(m_aPageWindowAdapters.begin(), m_aPageWindowAdapters.end(),
+ [&_rxCC](const rtl::Reference< FormViewPageWindowAdapter >& rpAdapter) { return _rxCC == rpAdapter->getControlContainer(); });
+ if (i != m_aPageWindowAdapters.end())
+ return *i;
+ return nullptr;
+}
+
+
+void FmXFormView::addWindow(const SdrPageWindow& rWindow)
+{
+ FmFormPage* pFormPage = dynamic_cast<FmFormPage*>( rWindow.GetPageView().GetPage() );
+ if ( !pFormPage )
+ return;
+
+ const Reference< XControlContainer >& xCC = rWindow.GetControlContainer();
+ if ( xCC.is()
+ && ( !findWindow( xCC ).is() )
+ )
+ {
+ rtl::Reference< FormViewPageWindowAdapter > pAdapter = new FormViewPageWindowAdapter( comphelper::getProcessComponentContext(), rWindow, this );
+ m_aPageWindowAdapters.push_back( pAdapter );
+
+ // listen at the ControlContainer to notice changes
+ Reference< XContainer > xContainer( xCC, UNO_QUERY );
+ if ( xContainer.is() )
+ xContainer->addContainerListener( this );
+ }
+}
+
+
+void FmXFormView::removeWindow( const Reference< XControlContainer >& _rxCC )
+{
+ // Is called if
+ // - the design mode is being switched to
+ // - a window is deleted while in the design mode
+ // - the control container for a window is removed while the active mode is on
+
+ auto i = std::find_if(m_aPageWindowAdapters.begin(), m_aPageWindowAdapters.end(),
+ [&_rxCC](const rtl::Reference< FormViewPageWindowAdapter >& rpAdapter) { return _rxCC == rpAdapter->getControlContainer(); });
+ if (i != m_aPageWindowAdapters.end())
+ {
+ Reference< XContainer > xContainer( _rxCC, UNO_QUERY );
+ if ( xContainer.is() )
+ xContainer->removeContainerListener( this );
+
+ (*i)->dispose();
+ m_aPageWindowAdapters.erase( i );
+ }
+}
+
+void FmXFormView::displayAsyncErrorMessage( const SQLErrorEvent& _rEvent )
+{
+ DBG_ASSERT( nullptr == m_nErrorMessageEvent, "FmXFormView::displayAsyncErrorMessage: not too fast, please!" );
+ // This should not happen - usually, the PostUserEvent is faster than any possible user
+ // interaction which could trigger a new error. If it happens, we need a queue for the events.
+ m_aAsyncError = _rEvent;
+ m_nErrorMessageEvent = Application::PostUserEvent( LINK( this, FmXFormView, OnDelayedErrorMessage ) );
+}
+
+IMPL_LINK_NOARG(FmXFormView, OnDelayedErrorMessage, void*, void)
+{
+ m_nErrorMessageEvent = nullptr;
+ displayException(m_aAsyncError, GetParentWindow());
+}
+
+void FmXFormView::onFirstViewActivation( const FmFormModel* _pDocModel )
+{
+ if ( _pDocModel && _pDocModel->GetAutoControlFocus() )
+ m_nAutoFocusEvent = Application::PostUserEvent( LINK( this, FmXFormView, OnAutoFocus ) );
+}
+
+void FmXFormView::suspendTabOrderUpdate()
+{
+ OSL_ENSURE( !m_isTabOrderUpdateSuspended, "FmXFormView::suspendTabOrderUpdate: nesting not allowed!" );
+ m_isTabOrderUpdateSuspended = true;
+}
+
+void FmXFormView::resumeTabOrderUpdate()
+{
+ OSL_ENSURE( m_isTabOrderUpdateSuspended, "FmXFormView::resumeTabOrderUpdate: not suspended!" );
+ m_isTabOrderUpdateSuspended = false;
+
+ // update the tab orders for all components which were collected since the suspendTabOrderUpdate call.
+ for (const auto& rContainer : m_aNeedTabOrderUpdate)
+ {
+ rtl::Reference< FormViewPageWindowAdapter > pAdapter = findWindow( rContainer.first );
+ if ( !pAdapter.is() )
+ continue;
+
+ for (const auto& rForm : rContainer.second)
+ {
+ pAdapter->updateTabOrder( rForm );
+ }
+ }
+ m_aNeedTabOrderUpdate.clear();
+}
+
+namespace
+{
+ bool isActivableDatabaseForm(const Reference< XFormController > &xController)
+ {
+ // only database forms are to be activated
+ Reference< XRowSet > xForm(xController->getModel(), UNO_QUERY);
+ if ( !xForm.is() || !getConnection( xForm ).is() )
+ return false;
+
+ Reference< XPropertySet > xFormSet( xForm, UNO_QUERY );
+ if ( !xFormSet.is() )
+ {
+ SAL_WARN( "svx.form", "FmXFormView::OnActivate: a form which does not have properties?" );
+ return false;
+ }
+
+ const OUString aSource = ::comphelper::getString( xFormSet->getPropertyValue( FM_PROP_COMMAND ) );
+
+ return !aSource.isEmpty();
+ }
+
+ class find_active_databaseform
+ {
+ const Reference< XFormController > xActiveController;
+
+ public:
+
+ explicit find_active_databaseform( const Reference< XFormController >& _xActiveController )
+ : xActiveController(_xActiveController )
+ {}
+
+ Reference < XFormController > operator() (const Reference< XFormController > &xController)
+ {
+ if(xController == xActiveController && isActivableDatabaseForm(xController))
+ return xController;
+
+ if ( !xController.is() )
+ {
+ SAL_WARN( "svx.form", "FmXFormView::OnActivate: a form controller which does not have children?" );
+ return nullptr;
+ }
+
+ for(sal_Int32 i = 0; i < xController->getCount(); ++i)
+ {
+ const Any a(xController->getByIndex(i));
+ Reference < XFormController > xI;
+ if ((a >>= xI) && xI.is())
+ {
+ Reference < XFormController > xRes(operator()(xI));
+ if (xRes.is())
+ return xRes;
+ }
+ }
+
+ return nullptr;
+ }
+ };
+}
+
+
+IMPL_LINK_NOARG(FmXFormView, OnActivate, void*, void)
+{
+ m_nActivationEvent = nullptr;
+
+ if ( !m_pView )
+ {
+ OSL_FAIL( "FmXFormView::OnActivate: well... seems we have a timing problem (the view already died)!" );
+ return;
+ }
+
+ // setting the controller to activate
+ if (!(m_pView->GetFormShell() && m_pView->GetActualOutDev() && m_pView->GetActualOutDev()->GetOutDevType() == OUTDEV_WINDOW))
+ return;
+
+ FmXFormShell* const pShImpl = m_pView->GetFormShell()->GetImpl();
+
+ if(!pShImpl)
+ return;
+
+ find_active_databaseform fad(pShImpl->getActiveController_Lock());
+
+ vcl::Window* pWindow = m_pView->GetActualOutDev()->GetOwnerWindow();
+ rtl::Reference< FormViewPageWindowAdapter > pAdapter = m_aPageWindowAdapters.empty() ? nullptr : m_aPageWindowAdapters[0];
+ for (const auto& rpPageWindowAdapter : m_aPageWindowAdapters)
+ {
+ if ( pWindow == rpPageWindowAdapter->getWindow() )
+ pAdapter = rpPageWindowAdapter;
+ }
+
+ if ( !pAdapter.is() )
+ return;
+
+ Reference< XFormController > xControllerToActivate;
+ for (const Reference< XFormController > & xController : pAdapter->GetList())
+ {
+ if ( !xController.is() )
+ continue;
+
+ {
+ Reference< XFormController > xActiveController(fad(xController));
+ if (xActiveController.is())
+ {
+ xControllerToActivate = xActiveController;
+ break;
+ }
+ }
+
+ if(xControllerToActivate.is() || !isActivableDatabaseForm(xController))
+ continue;
+
+ xControllerToActivate = xController;
+ }
+ pShImpl->setActiveController_Lock(xControllerToActivate);
+}
+
+
+void FmXFormView::Activate(bool bSync)
+{
+ if (m_nActivationEvent)
+ {
+ Application::RemoveUserEvent(m_nActivationEvent);
+ m_nActivationEvent = nullptr;
+ }
+
+ if (bSync)
+ {
+ LINK(this,FmXFormView,OnActivate).Call(nullptr);
+ }
+ else
+ m_nActivationEvent = Application::PostUserEvent(LINK(this,FmXFormView,OnActivate));
+}
+
+
+void FmXFormView::Deactivate(bool bDeactivateController)
+{
+ if (m_nActivationEvent)
+ {
+ Application::RemoveUserEvent(m_nActivationEvent);
+ m_nActivationEvent = nullptr;
+ }
+
+ FmXFormShell* pShImpl = m_pView->GetFormShell() ? m_pView->GetFormShell()->GetImpl() : nullptr;
+ if (pShImpl && bDeactivateController)
+ pShImpl->setActiveController_Lock(nullptr);
+}
+
+
+FmFormShell* FmXFormView::GetFormShell() const
+{
+ return m_pView ? m_pView->GetFormShell() : nullptr;
+}
+
+void FmXFormView::AutoFocus()
+{
+ if (m_nAutoFocusEvent)
+ Application::RemoveUserEvent(m_nAutoFocusEvent);
+
+ m_nAutoFocusEvent = Application::PostUserEvent(LINK(this, FmXFormView, OnAutoFocus));
+}
+
+
+bool FmXFormView::isFocusable( const Reference< XControl >& i_rControl )
+{
+ if ( !i_rControl.is() )
+ return false;
+
+ try
+ {
+ Reference< XPropertySet > xModelProps( i_rControl->getModel(), UNO_QUERY_THROW );
+
+ // only enabled controls are allowed to participate
+ bool bEnabled = false;
+ OSL_VERIFY( xModelProps->getPropertyValue( FM_PROP_ENABLED ) >>= bEnabled );
+ if ( !bEnabled )
+ return false;
+
+ // check the class id of the control model
+ sal_Int16 nClassId = FormComponentType::CONTROL;
+ OSL_VERIFY( xModelProps->getPropertyValue( FM_PROP_CLASSID ) >>= nClassId );
+
+ // controls which are not focussable
+ if ( ( FormComponentType::CONTROL != nClassId )
+ && ( FormComponentType::IMAGEBUTTON != nClassId )
+ && ( FormComponentType::GROUPBOX != nClassId )
+ && ( FormComponentType::FIXEDTEXT != nClassId )
+ && ( FormComponentType::HIDDENCONTROL != nClassId )
+ && ( FormComponentType::IMAGECONTROL != nClassId )
+ && ( FormComponentType::SCROLLBAR != nClassId )
+ && ( FormComponentType::SPINBUTTON!= nClassId )
+ )
+ {
+ return true;
+ }
+ }
+ catch (const Exception&)
+ {
+ DBG_UNHANDLED_EXCEPTION("svx");
+ }
+ return false;
+}
+
+
+static Reference< XControl > lcl_firstFocussableControl( const Sequence< Reference< XControl > >& _rControls )
+{
+ Reference< XControl > xReturn;
+
+ // loop through all the controls
+ for ( auto const & control : _rControls )
+ {
+ if ( !control.is() )
+ continue;
+
+ if ( FmXFormView::isFocusable( control ) )
+ {
+ xReturn = control;
+ break;
+ }
+ }
+
+ if ( !xReturn.is() && _rControls.hasElements() )
+ xReturn = _rControls[0];
+
+ return xReturn;
+}
+
+
+namespace
+{
+
+ void lcl_ensureControlsOfFormExist_nothrow( const SdrPage& _rPage, const SdrView& _rView, const vcl::Window& _rWindow, const Reference< XForm >& _rxForm )
+ {
+ try
+ {
+ Reference< XInterface > xNormalizedForm( _rxForm, UNO_QUERY_THROW );
+
+ SdrObjListIter aSdrObjectLoop( &_rPage, SdrIterMode::DeepNoGroups );
+ while ( aSdrObjectLoop.IsMore() )
+ {
+ FmFormObj* pFormObject = FmFormObj::GetFormObject( aSdrObjectLoop.Next() );
+ if ( !pFormObject )
+ continue;
+
+ Reference< XChild > xModel( pFormObject->GetUnoControlModel(), UNO_QUERY_THROW );
+ Reference< XInterface > xModelParent( xModel->getParent(), UNO_QUERY );
+
+ if ( xNormalizedForm.get() != xModelParent.get() )
+ continue;
+
+ pFormObject->GetUnoControl( _rView, *_rWindow.GetOutDev() );
+ }
+ }
+ catch (const Exception&)
+ {
+ DBG_UNHANDLED_EXCEPTION("svx");
+ }
+ }
+}
+
+
+Reference< XFormController > FmXFormView::getFormController( const Reference< XForm >& _rxForm, const OutputDevice& _rDevice ) const
+{
+ Reference< XFormController > xController;
+
+ for (const rtl::Reference< FormViewPageWindowAdapter >& pAdapter : m_aPageWindowAdapters)
+ {
+ if ( !pAdapter )
+ {
+ SAL_WARN( "svx.form", "FmXFormView::getFormController: invalid page window adapter!" );
+ continue;
+ }
+
+ if ( pAdapter->getWindow() != _rDevice.GetOwnerWindow() )
+ // wrong device
+ continue;
+
+ xController = pAdapter->getController( _rxForm );
+ if ( xController.is() )
+ break;
+ }
+ return xController;
+}
+
+
+IMPL_LINK_NOARG(FmXFormView, OnAutoFocus, void*, void)
+{
+ m_nAutoFocusEvent = nullptr;
+
+ // go to the first form of our page, examine it's TabController, go to its first (in terms of the tab order)
+ // control, give it the focus
+
+ SdrPageView *pPageView = m_pView ? m_pView->GetSdrPageView() : nullptr;
+ SdrPage *pSdrPage = pPageView ? pPageView->GetPage() : nullptr;
+ // get the forms collection of the page we belong to
+ FmFormPage* pPage = dynamic_cast<FmFormPage*>( pSdrPage );
+ Reference< XIndexAccess > xForms( pPage ? Reference< XIndexAccess >( pPage->GetForms() ) : Reference< XIndexAccess >() );
+
+ const rtl::Reference< FormViewPageWindowAdapter > pAdapter = m_aPageWindowAdapters.empty() ? nullptr : m_aPageWindowAdapters[0];
+ const vcl::Window* pWindow = pAdapter ? pAdapter->getWindow() : nullptr;
+
+ ENSURE_OR_RETURN_VOID( xForms.is() && pWindow, "FmXFormView::OnAutoFocus: could not collect all essentials!" );
+
+ try
+ {
+ // go for the tab controller of the first form
+ if ( !xForms->getCount() )
+ return;
+ Reference< XForm > xForm( xForms->getByIndex( 0 ), UNO_QUERY_THROW );
+ Reference< XTabController > xTabController( pAdapter->getController( xForm ), UNO_QUERY_THROW );
+
+ // go for the first control of the controller
+ Sequence< Reference< XControl > > aControls( xTabController->getControls() );
+ if ( !aControls.hasElements() )
+ {
+ Reference< XElementAccess > xFormElementAccess( xForm, UNO_QUERY_THROW );
+ if (xFormElementAccess->hasElements() && pPage && m_pView)
+ {
+ // there are control models in the form, but no controls, yet.
+ // Well, since some time controls are created on demand only. In particular,
+ // they're normally created when they're first painted.
+ // Unfortunately, the FormController does not have any way to
+ // trigger the creation itself, so we must hack this ...
+ lcl_ensureControlsOfFormExist_nothrow( *pPage, *m_pView, *pWindow, xForm );
+ aControls = xTabController->getControls();
+ OSL_ENSURE( aControls.hasElements(), "FmXFormView::OnAutoFocus: no controls at all!" );
+ }
+ }
+
+ // set the focus to this first control
+ Reference< XWindow > xControlWindow( lcl_firstFocussableControl( aControls ), UNO_QUERY );
+ if ( !xControlWindow.is() )
+ return;
+
+ xControlWindow->setFocus();
+
+ // ensure that the control is visible
+ // 80210 - 12/07/00 - FS
+ const OutputDevice* pOut = m_pView ? m_pView->GetActualOutDev() : nullptr;
+ const vcl::Window* pCurrentWindow = pOut ? pOut->GetOwnerWindow() : nullptr;
+ if ( pCurrentWindow )
+ {
+ awt::Rectangle aRect = xControlWindow->getPosSize();
+ ::tools::Rectangle aNonUnoRect( aRect.X, aRect.Y, aRect.X + aRect.Width, aRect.Y + aRect.Height );
+ m_pView->MakeVisible( pCurrentWindow->PixelToLogic( aNonUnoRect ), *const_cast< vcl::Window* >( pCurrentWindow ) );
+ }
+ }
+ catch (const Exception&)
+ {
+ DBG_UNHANDLED_EXCEPTION("svx");
+ }
+}
+
+
+void FmXFormView::onCreatedFormObject( FmFormObj const & _rFormObject )
+{
+ FmFormShell* pShell = m_pView ? m_pView->GetFormShell() : nullptr;
+ FmXFormShell* pShellImpl = pShell ? pShell->GetImpl() : nullptr;
+ OSL_ENSURE( pShellImpl, "FmXFormView::onCreatedFormObject: no form shell!" );
+ if ( !pShellImpl )
+ return;
+
+ // it is valid that the form shell's forms collection is not initialized, yet
+ pShellImpl->UpdateForms_Lock(true);
+
+ m_xLastCreatedControlModel.set( _rFormObject.GetUnoControlModel(), UNO_QUERY );
+ if ( !m_xLastCreatedControlModel.is() )
+ return;
+
+ // some initial property defaults
+ FormControlFactory aControlFactory;
+ aControlFactory.initializeControlModel(pShellImpl->getDocumentType_Lock(), _rFormObject);
+
+ if (!pShellImpl->GetWizardUsing_Lock())
+ return;
+
+ // #i31958# don't call wizards in XForms mode
+ if (pShellImpl->isEnhancedForm_Lock())
+ return;
+
+ // #i46898# no wizards if there is no Base installed - currently, all wizards are
+ // database related
+ if ( !SvtModuleOptions().IsModuleInstalled( SvtModuleOptions::EModule::DATABASE ) )
+ return;
+
+ if ( m_nControlWizardEvent )
+ Application::RemoveUserEvent( m_nControlWizardEvent );
+ m_nControlWizardEvent = Application::PostUserEvent( LINK( this, FmXFormView, OnStartControlWizard ) );
+}
+
+void FmXFormView::breakCreateFormObject()
+{
+ if (m_nControlWizardEvent != nullptr)
+ {
+ Application::RemoveUserEvent(m_nControlWizardEvent);
+ m_nControlWizardEvent = nullptr;
+ }
+ m_xLastCreatedControlModel.clear();
+}
+
+Reference<XWindow> FmXFormView::GetParentWindow() const
+{
+ const OutputDevice* pOut = m_pView ? m_pView->GetActualOutDev() : nullptr;
+ const vcl::Window* pCurrentWindow = pOut ? pOut->GetOwnerWindow() : nullptr;
+ return VCLUnoHelper::GetInterface(const_cast<vcl::Window*>(pCurrentWindow));
+}
+
+IMPL_LINK_NOARG( FmXFormView, OnStartControlWizard, void*, void )
+{
+ m_nControlWizardEvent = nullptr;
+ OSL_PRECOND( m_xLastCreatedControlModel.is(), "FmXFormView::OnStartControlWizard: illegal call!" );
+ if ( !m_xLastCreatedControlModel.is() )
+ return;
+
+ sal_Int16 nClassId = FormComponentType::CONTROL;
+ try
+ {
+ OSL_VERIFY( m_xLastCreatedControlModel->getPropertyValue( FM_PROP_CLASSID ) >>= nClassId );
+ }
+ catch (const Exception&)
+ {
+ DBG_UNHANDLED_EXCEPTION("svx");
+ }
+
+ const char* pWizardAsciiName = nullptr;
+ switch ( nClassId )
+ {
+ case FormComponentType::GRIDCONTROL:
+ pWizardAsciiName = "com.sun.star.sdb.GridControlAutoPilot";
+ break;
+ case FormComponentType::LISTBOX:
+ case FormComponentType::COMBOBOX:
+ pWizardAsciiName = "com.sun.star.sdb.ListComboBoxAutoPilot";
+ break;
+ case FormComponentType::GROUPBOX:
+ pWizardAsciiName = "com.sun.star.sdb.GroupBoxAutoPilot";
+ break;
+ }
+
+ if ( pWizardAsciiName )
+ {
+ // build the argument list
+ ::comphelper::NamedValueCollection aWizardArgs;
+ aWizardArgs.put("ObjectModel", m_xLastCreatedControlModel);
+ aWizardArgs.put("ParentWindow", GetParentWindow());
+
+ // create the wizard object
+ Reference< XExecutableDialog > xWizard;
+ try
+ {
+ Reference<XComponentContext> xContext = comphelper::getProcessComponentContext();
+ xWizard.set( xContext->getServiceManager()->createInstanceWithArgumentsAndContext( OUString::createFromAscii(pWizardAsciiName), aWizardArgs.getWrappedPropertyValues(), xContext ), UNO_QUERY);
+ }
+ catch (const Exception&)
+ {
+ DBG_UNHANDLED_EXCEPTION("svx");
+ }
+
+ if ( !xWizard.is() )
+ {
+ ShowServiceNotAvailableError( nullptr, OUString::createFromAscii(pWizardAsciiName), true );
+ }
+ else
+ {
+ // execute the wizard
+ try
+ {
+ xWizard->execute();
+ }
+ catch (const Exception&)
+ {
+ DBG_UNHANDLED_EXCEPTION("svx");
+ }
+ }
+ }
+
+ m_xLastCreatedControlModel.clear();
+}
+
+
+namespace
+{
+ void lcl_insertIntoFormComponentHierarchy_throw( const FmFormView& _rView, const SdrUnoObj& _rSdrObj,
+ const Reference< XDataSource >& _rxDataSource, const OUString& _rDataSourceName,
+ const OUString& _rCommand, const sal_Int32 _nCommandType )
+ {
+ FmFormPage& rPage = static_cast< FmFormPage& >( *_rView.GetSdrPageView()->GetPage() );
+
+ Reference< XFormComponent > xFormComponent( _rSdrObj.GetUnoControlModel(), UNO_QUERY_THROW );
+ Reference< XForm > xTargetForm(
+ rPage.GetImpl().findPlaceInFormComponentHierarchy( xFormComponent, _rxDataSource, _rDataSourceName, _rCommand, _nCommandType ),
+ UNO_SET_THROW );
+
+ FmFormPageImpl::setUniqueName( xFormComponent, xTargetForm );
+
+ Reference< XIndexContainer > xFormAsContainer( xTargetForm, UNO_QUERY_THROW );
+ xFormAsContainer->insertByIndex( xFormAsContainer->getCount(), Any( xFormComponent ) );
+ }
+}
+
+
+SdrObjectUniquePtr FmXFormView::implCreateFieldControl( const svx::ODataAccessDescriptor& _rColumnDescriptor )
+{
+ // not if we're in design mode
+ if ( !m_pView->IsDesignMode() )
+ return nullptr;
+
+ OUString sCommand, sFieldName;
+ sal_Int32 nCommandType = CommandType::COMMAND;
+ SharedConnection xConnection;
+
+ OUString sDataSource = _rColumnDescriptor.getDataSource();
+ _rColumnDescriptor[ DataAccessDescriptorProperty::Command ] >>= sCommand;
+ _rColumnDescriptor[ DataAccessDescriptorProperty::ColumnName ] >>= sFieldName;
+ _rColumnDescriptor[ DataAccessDescriptorProperty::CommandType ] >>= nCommandType;
+ {
+ Reference< XConnection > xExternalConnection;
+ _rColumnDescriptor[ DataAccessDescriptorProperty::Connection ] >>= xExternalConnection;
+ xConnection.reset( xExternalConnection, SharedConnection::NoTakeOwnership );
+ }
+
+ if ( sCommand.isEmpty()
+ || sFieldName.isEmpty()
+ || ( sDataSource.isEmpty()
+ && !xConnection.is()
+ )
+ )
+ {
+ OSL_FAIL( "FmXFormView::implCreateFieldControl: nonsense!" );
+ }
+
+ Reference< XDataSource > xDataSource;
+ SQLErrorEvent aError;
+ try
+ {
+ if ( xConnection.is() && !xDataSource.is() && sDataSource.isEmpty() )
+ {
+ Reference< XChild > xChild( xConnection, UNO_QUERY );
+ if ( xChild.is() )
+ xDataSource.set(xChild->getParent(), css::uno::UNO_QUERY);
+ }
+
+ // obtain the data source
+ if ( !xDataSource.is() )
+ xDataSource = getDataSource( sDataSource, comphelper::getProcessComponentContext() );
+
+ // and the connection, if necessary
+ if ( !xConnection.is() )
+ xConnection.reset( getConnection_withFeedback(
+ sDataSource,
+ OUString(),
+ OUString(),
+ comphelper::getProcessComponentContext(),
+ nullptr
+ ) );
+ }
+ catch (const SQLException&)
+ {
+ aError.Reason = ::cppu::getCaughtException();
+ }
+ catch (const Exception& )
+ {
+ /* will be asserted below */
+ }
+ if (aError.Reason.hasValue())
+ {
+ displayAsyncErrorMessage( aError );
+ return nullptr;
+ }
+
+ // need a data source and a connection here
+ if (!xDataSource.is() || !xConnection.is())
+ {
+ OSL_FAIL("FmXFormView::implCreateFieldControl : could not retrieve the data source or the connection!");
+ return nullptr;
+ }
+
+ Reference< XComponent > xKeepFieldsAlive;
+ // go
+ try
+ {
+ // determine the table/query field which we should create a control for
+ Reference< XPropertySet > xField;
+
+ Reference< XNameAccess > xFields = getFieldsByCommandDescriptor(
+ xConnection, nCommandType, sCommand, xKeepFieldsAlive );
+
+ if (xFields.is() && xFields->hasByName(sFieldName))
+ xFields->getByName(sFieldName) >>= xField;
+ if ( !xField.is() )
+ return nullptr;
+
+ Reference< XNumberFormatsSupplier > xSupplier( getNumberFormats( xConnection ), UNO_SET_THROW );
+ Reference< XNumberFormats > xNumberFormats( xSupplier->getNumberFormats(), UNO_SET_THROW );
+
+ OUString sLabelPostfix;
+
+
+ // only for text size
+ OutputDevice* pOutDev = nullptr;
+ if (m_pView->GetActualOutDev() && m_pView->GetActualOutDev()->GetOutDevType() == OUTDEV_WINDOW)
+ pOutDev = const_cast<OutputDevice*>(m_pView->GetActualOutDev());
+ else
+ {// find OutDev
+ if (SdrPageView* pPageView = m_pView->GetSdrPageView())
+ {
+ // const SdrPageViewWinList& rWinList = pPageView->GetWinList();
+ // const SdrPageViewWindows& rPageViewWindows = pPageView->GetPageViewWindows();
+
+ for( sal_uInt32 i = 0; i < pPageView->PageWindowCount(); i++ )
+ {
+ const SdrPageWindow& rPageWindow = *pPageView->GetPageWindow(i);
+
+ if( rPageWindow.GetPaintWindow().OutputToWindow())
+ {
+ pOutDev = &rPageWindow.GetPaintWindow().GetOutputDevice();
+ break;
+ }
+ }
+ }
+ }
+
+ if ( !pOutDev )
+ return nullptr;
+
+ sal_Int32 nDataType = ::comphelper::getINT32(xField->getPropertyValue(FM_PROP_FIELDTYPE));
+ if ((DataType::BINARY == nDataType) || (DataType::VARBINARY == nDataType))
+ return nullptr;
+
+
+ // determine the control type by examining the data type of the bound column
+ SdrObjKind nOBJID = SdrObjKind::NONE;
+ bool bDateNTimeField = false;
+
+ bool bIsCurrency = false;
+ if (::comphelper::hasProperty(FM_PROP_ISCURRENCY, xField))
+ bIsCurrency = ::comphelper::getBOOL(xField->getPropertyValue(FM_PROP_ISCURRENCY));
+
+ if (bIsCurrency)
+ nOBJID = SdrObjKind::FormCurrencyField;
+ else
+ switch (nDataType)
+ {
+ case DataType::BLOB:
+ case DataType::LONGVARBINARY:
+ nOBJID = SdrObjKind::FormImageControl;
+ break;
+ case DataType::LONGVARCHAR:
+ case DataType::CLOB:
+ nOBJID = SdrObjKind::FormEdit;
+ break;
+ case DataType::BINARY:
+ case DataType::VARBINARY:
+ return nullptr;
+ case DataType::BIT:
+ case DataType::BOOLEAN:
+ nOBJID = SdrObjKind::FormCheckbox;
+ break;
+ case DataType::TINYINT:
+ case DataType::SMALLINT:
+ case DataType::INTEGER:
+ nOBJID = SdrObjKind::FormNumericField;
+ break;
+ case DataType::REAL:
+ case DataType::DOUBLE:
+ case DataType::NUMERIC:
+ case DataType::DECIMAL:
+ nOBJID = SdrObjKind::FormFormattedField;
+ break;
+ case DataType::TIMESTAMP:
+ bDateNTimeField = true;
+ sLabelPostfix = SvxResId(RID_STR_POSTFIX_DATE);
+ [[fallthrough]];
+ case DataType::DATE:
+ nOBJID = SdrObjKind::FormDateField;
+ break;
+ case DataType::TIME:
+ nOBJID = SdrObjKind::FormTimeField;
+ break;
+ case DataType::CHAR:
+ case DataType::VARCHAR:
+ default:
+ nOBJID = SdrObjKind::FormEdit;
+ break;
+ }
+ if (nOBJID == SdrObjKind::NONE)
+ return nullptr;
+
+ std::unique_ptr<SdrUnoObj, SdrObjectFreeOp> pLabel;
+ std::unique_ptr<SdrUnoObj, SdrObjectFreeOp> pControl;
+ if ( !createControlLabelPair( *pOutDev, 0, 0, xField, xNumberFormats, nOBJID, sLabelPostfix,
+ pLabel, pControl, xDataSource, sDataSource, sCommand, nCommandType )
+ )
+ {
+ return nullptr;
+ }
+
+
+ // group objects
+ bool bCheckbox = ( SdrObjKind::FormCheckbox == nOBJID );
+ OSL_ENSURE( !bCheckbox || !pLabel, "FmXFormView::implCreateFieldControl: why was there a label created for a check box?" );
+ if ( bCheckbox )
+ return SdrObjectUniquePtr(pControl.release());
+
+ SdrObjGroup* pGroup = new SdrObjGroup(getView()->getSdrModelFromSdrView());
+ SdrObjList* pObjList = pGroup->GetSubList();
+ pObjList->InsertObject( pLabel.release() );
+ pObjList->InsertObject( pControl.release() );
+
+ if ( bDateNTimeField )
+ { // so far we created a date field only, but we also need a time field
+ if ( createControlLabelPair( *pOutDev, 0, 1000, xField, xNumberFormats, SdrObjKind::FormTimeField,
+ SvxResId(RID_STR_POSTFIX_TIME), pLabel, pControl,
+ xDataSource, sDataSource, sCommand, nCommandType )
+ )
+ {
+ pObjList->InsertObject( pLabel.release() );
+ pObjList->InsertObject( pControl.release() );
+ }
+ }
+
+ return SdrObjectUniquePtr(pGroup); // and done
+ }
+ catch (const Exception&)
+ {
+ DBG_UNHANDLED_EXCEPTION("svx");
+ }
+
+
+ return nullptr;
+}
+
+
+SdrObjectUniquePtr FmXFormView::implCreateXFormsControl( const svx::OXFormsDescriptor &_rDesc )
+{
+ // not if we're in design mode
+ if ( !m_pView->IsDesignMode() )
+ return nullptr;
+
+ // go
+ try
+ {
+ // determine the table/query field which we should create a control for
+ Reference< XNumberFormats > xNumberFormats;
+ OUString sLabelPostfix = _rDesc.szName;
+
+
+ // only for text size
+ OutputDevice* pOutDev = nullptr;
+ if (m_pView->GetActualOutDev() && m_pView->GetActualOutDev()->GetOutDevType() == OUTDEV_WINDOW)
+ pOutDev = const_cast<OutputDevice*>(m_pView->GetActualOutDev());
+ else
+ {// find OutDev
+ if (SdrPageView* pPageView = m_pView->GetSdrPageView())
+ {
+ // const SdrPageViewWinList& rWinList = pPageView->GetWinList();
+ // const SdrPageViewWindows& rPageViewWindows = pPageView->GetPageViewWindows();
+
+ for( sal_uInt32 i = 0; i < pPageView->PageWindowCount(); i++ )
+ {
+ const SdrPageWindow& rPageWindow = *pPageView->GetPageWindow(i);
+
+ if( rPageWindow.GetPaintWindow().GetOutputDevice().GetOutDevType() == OUTDEV_WINDOW)
+ {
+ pOutDev = &rPageWindow.GetPaintWindow().GetOutputDevice();
+ break;
+ }
+ }
+ }
+ }
+
+ if ( !pOutDev )
+ return nullptr;
+
+
+ // The service name decides which control should be created
+ SdrObjKind nOBJID = SdrObjKind::FormEdit;
+ if(_rDesc.szServiceName == FM_SUN_COMPONENT_NUMERICFIELD)
+ nOBJID = SdrObjKind::FormNumericField;
+ if(_rDesc.szServiceName == FM_SUN_COMPONENT_CHECKBOX)
+ nOBJID = SdrObjKind::FormCheckbox;
+ if(_rDesc.szServiceName == FM_COMPONENT_COMMANDBUTTON)
+ nOBJID = SdrObjKind::FormButton;
+
+ Reference< css::form::submission::XSubmission > xSubmission(_rDesc.xPropSet, UNO_QUERY);
+
+ // xform control or submission button?
+ if ( !xSubmission.is() )
+ {
+ std::unique_ptr<SdrUnoObj, SdrObjectFreeOp> pLabel;
+ std::unique_ptr<SdrUnoObj, SdrObjectFreeOp> pControl;
+ if ( !createControlLabelPair( *pOutDev, 0, 0, nullptr, xNumberFormats, nOBJID, sLabelPostfix,
+ pLabel, pControl, nullptr, "", "", -1 )
+ )
+ {
+ return nullptr;
+ }
+
+
+ // Now build the connection between the control and the data item.
+ Reference< XValueBinding > xValueBinding(_rDesc.xPropSet,UNO_QUERY);
+ Reference< XBindableValue > xBindableValue(pControl->GetUnoControlModel(),UNO_QUERY);
+
+ DBG_ASSERT( xBindableValue.is(), "FmXFormView::implCreateXFormsControl: control's not bindable!" );
+ if ( xBindableValue.is() )
+ xBindableValue->setValueBinding(xValueBinding);
+
+ bool bCheckbox = ( SdrObjKind::FormCheckbox == nOBJID );
+ OSL_ENSURE( !bCheckbox || !pLabel, "FmXFormView::implCreateXFormsControl: why was there a label created for a check box?" );
+ if ( bCheckbox )
+ return SdrObjectUniquePtr(pControl.release());
+
+
+ // group objects
+ SdrObjGroup* pGroup = new SdrObjGroup(getView()->getSdrModelFromSdrView());
+ SdrObjList* pObjList = pGroup->GetSubList();
+ pObjList->InsertObject(pLabel.release());
+ pObjList->InsertObject(pControl.release());
+
+ return SdrObjectUniquePtr(pGroup);
+ }
+ else {
+
+ // create a button control
+ const MapMode& eTargetMode( pOutDev->GetMapMode() );
+ const MapMode eSourceMode(MapUnit::Map100thMM);
+ const SdrObjKind nObjID = SdrObjKind::FormButton;
+ ::Size controlSize(4000, 500);
+ FmFormObj *pControl = static_cast<FmFormObj*>(
+ SdrObjFactory::MakeNewObject(
+ getView()->getSdrModelFromSdrView(),
+ SdrInventor::FmForm,
+ nObjID));
+ controlSize.setWidth( tools::Long(controlSize.Width() * eTargetMode.GetScaleX()) );
+ controlSize.setHeight( tools::Long(controlSize.Height() * eTargetMode.GetScaleY()) );
+ ::Point controlPos( OutputDevice::LogicToLogic( ::Point( controlSize.Width(), 0 ), eSourceMode, eTargetMode ) );
+ ::tools::Rectangle controlRect( controlPos, OutputDevice::LogicToLogic( controlSize, eSourceMode, eTargetMode ) );
+ pControl->SetLogicRect(controlRect);
+
+ // set the button label
+ Reference< XPropertySet > xControlSet(pControl->GetUnoControlModel(), UNO_QUERY);
+ xControlSet->setPropertyValue(FM_PROP_LABEL, Any(_rDesc.szName));
+
+ // connect the submission with the submission supplier (aka the button)
+ xControlSet->setPropertyValue( FM_PROP_BUTTON_TYPE,
+ Any( FormButtonType_SUBMIT ) );
+ Reference< css::form::submission::XSubmissionSupplier > xSubmissionSupplier(pControl->GetUnoControlModel(), UNO_QUERY);
+ xSubmissionSupplier->setSubmission(xSubmission);
+
+ return SdrObjectUniquePtr(pControl);
+ }
+ }
+ catch (const Exception&)
+ {
+ TOOLS_WARN_EXCEPTION("svx.form", "caught an exception while creating the control !");
+ }
+
+
+ return nullptr;
+}
+
+bool FmXFormView::createControlLabelPair( OutputDevice const & _rOutDev, sal_Int32 _nXOffsetMM, sal_Int32 _nYOffsetMM,
+ const Reference< XPropertySet >& _rxField, const Reference< XNumberFormats >& _rxNumberFormats,
+ SdrObjKind _nControlObjectID, std::u16string_view _rFieldPostfix,
+ std::unique_ptr<SdrUnoObj, SdrObjectFreeOp>& _rpLabel,
+ std::unique_ptr<SdrUnoObj, SdrObjectFreeOp>& _rpControl,
+ const Reference< XDataSource >& _rxDataSource, const OUString& _rDataSourceName,
+ const OUString& _rCommand, const sal_Int32 _nCommandType )
+{
+ if(!createControlLabelPair(
+ _rOutDev,
+ _nXOffsetMM,
+ _nYOffsetMM,
+ _rxField,
+ _rxNumberFormats,
+ _nControlObjectID,
+ _rFieldPostfix,
+ SdrInventor::FmForm,
+ SdrObjKind::FormFixedText,
+
+ // tdf#118963 Hand over a SdrModel to SdrObject-creation. It uses the local m_pView
+ // and already returning false when nullptr == getView() could be done, but m_pView
+ // is already dereferenced here in many places (see below), so just use it for now.
+ getView()->getSdrModelFromSdrView(),
+
+ _rpLabel,
+ _rpControl))
+ {
+ return false;
+ }
+
+ // insert the control model(s) into the form component hierarchy
+ if ( _rpLabel )
+ lcl_insertIntoFormComponentHierarchy_throw( *m_pView, *_rpLabel, _rxDataSource, _rDataSourceName, _rCommand, _nCommandType );
+ lcl_insertIntoFormComponentHierarchy_throw( *m_pView, *_rpControl, _rxDataSource, _rDataSourceName, _rCommand, _nCommandType );
+
+ // some context-dependent initializations
+ FormControlFactory aControlFactory;
+ if ( _rpLabel )
+ aControlFactory.initializeControlModel( impl_getDocumentType(), *_rpLabel );
+ aControlFactory.initializeControlModel( impl_getDocumentType(), *_rpControl );
+
+ return true;
+}
+
+
+bool FmXFormView::createControlLabelPair( OutputDevice const & _rOutDev, sal_Int32 _nXOffsetMM, sal_Int32 _nYOffsetMM,
+ const Reference< XPropertySet >& _rxField,
+ const Reference< XNumberFormats >& _rxNumberFormats, SdrObjKind _nControlObjectID,
+ std::u16string_view _rFieldPostfix, SdrInventor _nInventor, SdrObjKind _nLabelObjectID,
+ SdrModel& _rModel,
+ std::unique_ptr<SdrUnoObj, SdrObjectFreeOp>& _rpLabel, std::unique_ptr<SdrUnoObj, SdrObjectFreeOp>& _rpControl)
+{
+ sal_Int32 nDataType = 0;
+ OUString sFieldName;
+ Any aFieldName;
+ if ( _rxField.is() )
+ {
+ nDataType = ::comphelper::getINT32(_rxField->getPropertyValue(FM_PROP_FIELDTYPE));
+ aFieldName = _rxField->getPropertyValue(FM_PROP_NAME);
+ aFieldName >>= sFieldName;
+ }
+
+ // calculate the positions, respecting the settings of the target device
+ ::Size aTextSize( _rOutDev.GetTextWidth(sFieldName + _rFieldPostfix), _rOutDev.GetTextHeight() );
+
+ MapMode eTargetMode( _rOutDev.GetMapMode() ),
+ eSourceMode( MapUnit::Map100thMM );
+
+ // text width is at least 4 centimeters
+ // text height is always half a centimeter
+ ::Size aDefTxtSize(4000, 500);
+ ::Size aDefSize(4000, 500);
+ ::Size aDefImageSize(4000, 4000);
+
+ ::Size aRealSize = OutputDevice::LogicToLogic(aTextSize, eTargetMode, eSourceMode);
+ aRealSize.setWidth( std::max(aRealSize.Width(), aDefTxtSize.Width()) );
+ aRealSize.setHeight( aDefSize.Height() );
+
+ // adjust to scaling of the target device (#53523#)
+ aRealSize.setWidth( tools::Long(Fraction(aRealSize.Width(), 1) * eTargetMode.GetScaleX()) );
+ aRealSize.setHeight( tools::Long(Fraction(aRealSize.Height(), 1) * eTargetMode.GetScaleY()) );
+
+ // for boolean fields, we do not create a label, but just a checkbox
+ bool bNeedLabel = ( _nControlObjectID != SdrObjKind::FormCheckbox );
+
+ // the label
+ ::std::unique_ptr< SdrUnoObj, SdrObjectFreeOp > pLabel;
+ Reference< XPropertySet > xLabelModel;
+
+ if ( bNeedLabel )
+ {
+ pLabel.reset( dynamic_cast< SdrUnoObj* >(
+ SdrObjFactory::MakeNewObject(
+ _rModel,
+ _nInventor,
+ _nLabelObjectID)));
+
+ OSL_ENSURE(pLabel, "FmXFormView::createControlLabelPair: could not create the label!");
+
+ if (!pLabel)
+ return false;
+
+ xLabelModel.set( pLabel->GetUnoControlModel(), UNO_QUERY );
+ if ( xLabelModel.is() )
+ {
+ OUString sLabel;
+ if ( _rxField.is() && _rxField->getPropertySetInfo()->hasPropertyByName(FM_PROP_LABEL) )
+ _rxField->getPropertyValue(FM_PROP_LABEL) >>= sLabel;
+ if ( sLabel.isEmpty() )
+ sLabel = sFieldName;
+
+ xLabelModel->setPropertyValue( FM_PROP_LABEL, Any( sLabel + _rFieldPostfix ) );
+ OUString sObjectLabel(SvxResId(RID_STR_OBJECT_LABEL).replaceAll("#object#", sFieldName));
+ xLabelModel->setPropertyValue(FM_PROP_NAME, Any(sObjectLabel));
+ }
+
+ pLabel->SetLogicRect( ::tools::Rectangle(
+ OutputDevice::LogicToLogic( ::Point( _nXOffsetMM, _nYOffsetMM ), eSourceMode, eTargetMode ),
+ OutputDevice::LogicToLogic( aRealSize, eSourceMode, eTargetMode )
+ ) );
+ }
+
+ // the control
+ ::std::unique_ptr< SdrUnoObj, SdrObjectFreeOp > pControl( dynamic_cast< SdrUnoObj* >(
+ SdrObjFactory::MakeNewObject(
+ _rModel,
+ _nInventor,
+ _nControlObjectID)));
+
+ OSL_ENSURE(pControl, "FmXFormView::createControlLabelPair: could not create the control!");
+
+ if (!pControl)
+ return false;
+
+ Reference< XPropertySet > xControlSet( pControl->GetUnoControlModel(), UNO_QUERY );
+ if ( !xControlSet.is() )
+ return false;
+
+ // size of the control
+ ::Size aControlSize( aDefSize );
+ switch ( nDataType )
+ {
+ case DataType::BIT:
+ case DataType::BOOLEAN:
+ aControlSize = aDefSize;
+ break;
+ case DataType::LONGVARCHAR:
+ case DataType::CLOB:
+ case DataType::LONGVARBINARY:
+ case DataType::BLOB:
+ aControlSize = aDefImageSize;
+ break;
+ }
+
+ if ( SdrObjKind::FormImageControl == _nControlObjectID )
+ aControlSize = aDefImageSize;
+
+ aControlSize.setWidth( tools::Long(Fraction(aControlSize.Width(), 1) * eTargetMode.GetScaleX()) );
+ aControlSize.setHeight( tools::Long(Fraction(aControlSize.Height(), 1) * eTargetMode.GetScaleY()) );
+
+ pControl->SetLogicRect( ::tools::Rectangle(
+ OutputDevice::LogicToLogic( ::Point( aRealSize.Width() + _nXOffsetMM, _nYOffsetMM ), eSourceMode, eTargetMode ),
+ OutputDevice::LogicToLogic( aControlSize, eSourceMode, eTargetMode )
+ ) );
+
+ // some initializations
+ Reference< XPropertySetInfo > xControlPropInfo = xControlSet->getPropertySetInfo();
+
+ if ( aFieldName.hasValue() )
+ {
+ xControlSet->setPropertyValue( FM_PROP_CONTROLSOURCE, aFieldName );
+ xControlSet->setPropertyValue( FM_PROP_NAME, aFieldName );
+ if ( !bNeedLabel )
+ {
+ // no dedicated label control => use the label property
+ if ( xControlPropInfo->hasPropertyByName( FM_PROP_LABEL ) )
+ xControlSet->setPropertyValue( FM_PROP_LABEL, Any( sFieldName + _rFieldPostfix ) );
+ else
+ OSL_FAIL( "FmXFormView::createControlLabelPair: can't set a label for the control!" );
+ }
+ }
+
+ if ( (nDataType == DataType::LONGVARCHAR || nDataType == DataType::CLOB) && xControlPropInfo->hasPropertyByName( FM_PROP_MULTILINE ) )
+ {
+ xControlSet->setPropertyValue( FM_PROP_MULTILINE, Any( true ) );
+ }
+
+ // announce the label to the control
+ if ( xControlPropInfo->hasPropertyByName( FM_PROP_CONTROLLABEL ) && xLabelModel.is() )
+ {
+ try
+ {
+ xControlSet->setPropertyValue( FM_PROP_CONTROLLABEL, Any( xLabelModel ) );
+ }
+ catch (const Exception&)
+ {
+ DBG_UNHANDLED_EXCEPTION("svx");
+ }
+ }
+
+ if ( _rxField.is() )
+ {
+ FormControlFactory::initializeFieldDependentProperties( _rxField, xControlSet, _rxNumberFormats );
+ }
+
+ _rpLabel = std::move(pLabel);
+ _rpControl = std::move(pControl);
+ return true;
+}
+
+
+FmXFormView::ObjectRemoveListener::ObjectRemoveListener( FmXFormView* pParent )
+ :m_pParent( pParent )
+{
+}
+
+
+void FmXFormView::ObjectRemoveListener::Notify( SfxBroadcaster& /*rBC*/, const SfxHint& rHint )
+{
+ if (rHint.GetId() != SfxHintId::ThisIsAnSdrHint)
+ return;
+ const SdrHint* pSdrHint = static_cast<const SdrHint*>(&rHint);
+ if (pSdrHint->GetKind() == SdrHintKind::ObjectRemoved)
+ m_pParent->ObjectRemovedInAliveMode(pSdrHint->GetObject());
+}
+
+
+void FmXFormView::ObjectRemovedInAliveMode( const SdrObject* pObject )
+{
+ // if the remote object in my MarkList, which I have memorized when switching to the
+ // Alive mode, I have to take it out now, because I otherwise try to set the mark
+ // again when switching back (interestingly, this fails only with grouped objects
+ // (when accessing their ObjList GPF), not with individual ones)
+
+ const size_t nCount = m_aMark.GetMarkCount();
+ for (size_t i = 0; i < nCount; ++i)
+ {
+ SdrMark* pMark = m_aMark.GetMark(i);
+ SdrObject* pCurrent = pMark->GetMarkedSdrObj();
+ if (pObject == pCurrent)
+ {
+ m_aMark.DeleteMark(i);
+ return;
+ }
+ // I do not need to descend into GroupObjects: if an object is deleted there,
+ // then the pointer, which I have, to the GroupObject still remains valid ...
+ }
+}
+
+
+void FmXFormView::stopMarkListWatching()
+{
+ if ( m_pWatchStoredList )
+ {
+ m_pWatchStoredList->EndListeningAll();
+ m_pWatchStoredList.reset();
+ }
+}
+
+
+void FmXFormView::startMarkListWatching()
+{
+ if ( !m_pWatchStoredList )
+ {
+ FmFormModel* pModel = GetFormShell() ? GetFormShell()->GetFormModel() : nullptr;
+ DBG_ASSERT( pModel != nullptr, "FmXFormView::startMarkListWatching: shell has no model!" );
+ if (pModel)
+ {
+ m_pWatchStoredList.reset(new ObjectRemoveListener( this ));
+ m_pWatchStoredList->StartListening( *static_cast< SfxBroadcaster* >( pModel ) );
+ }
+ }
+ else
+ {
+ OSL_FAIL( "FmXFormView::startMarkListWatching: already listening!" );
+ }
+}
+
+void FmXFormView::saveMarkList()
+{
+ if ( m_pView )
+ {
+ m_aMark = m_pView->GetMarkedObjectList();
+ const size_t nCount = m_aMark.GetMarkCount( );
+ for ( size_t i = 0; i < nCount; ++i )
+ {
+ SdrMark* pMark = m_aMark.GetMark(i);
+ SdrObject* pObj = pMark->GetMarkedSdrObj();
+
+ if ( m_pView->IsObjMarked( pObj ) )
+ {
+ if ( pObj->IsGroupObject() )
+ {
+ SdrObjListIter aIter( pObj->GetSubList() );
+ bool bMixed = false;
+ while ( aIter.IsMore() && !bMixed )
+ bMixed = ( aIter.Next()->GetObjInventor() != SdrInventor::FmForm );
+
+ if ( !bMixed )
+ {
+ // all objects in the group are form objects
+ m_pView->MarkObj( pMark->GetMarkedSdrObj(), pMark->GetPageView(), true /* unmark! */ );
+ }
+ }
+ else
+ {
+ if ( pObj->GetObjInventor() == SdrInventor::FmForm )
+ { // this is a form layer object
+ m_pView->MarkObj( pMark->GetMarkedSdrObj(), pMark->GetPageView(), true /* unmark! */ );
+ }
+ }
+ }
+ }
+ }
+ else
+ {
+ OSL_FAIL( "FmXFormView::saveMarkList: invalid view!" );
+ m_aMark.Clear();
+ }
+}
+
+static bool lcl_hasObject( SdrObjListIter& rIter, SdrObject const * pObj )
+{
+ bool bFound = false;
+ while (rIter.IsMore() && !bFound)
+ bFound = pObj == rIter.Next();
+
+ rIter.Reset();
+ return bFound;
+}
+
+
+void FmXFormView::restoreMarkList( SdrMarkList& _rRestoredMarkList )
+{
+ if ( !m_pView )
+ return;
+
+ _rRestoredMarkList.Clear();
+
+ const SdrMarkList& rCurrentList = m_pView->GetMarkedObjectList();
+ FmFormPage* pPage = GetFormShell() ? GetFormShell()->GetCurPage() : nullptr;
+ if (!pPage)
+ return;
+
+ if (rCurrentList.GetMarkCount())
+ { // there is a current mark ... hmm. Is it a subset of the mark we remembered in saveMarkList?
+ bool bMisMatch = false;
+
+ // loop through all current marks
+ const size_t nCurrentCount = rCurrentList.GetMarkCount();
+ for ( size_t i=0; i<nCurrentCount && !bMisMatch; ++i )
+ {
+ const SdrObject* pCurrentMarked = rCurrentList.GetMark( i )->GetMarkedSdrObj();
+
+ // loop through all saved marks, check for equality
+ bool bFound = false;
+ const size_t nSavedCount = m_aMark.GetMarkCount();
+ for ( size_t j=0; j<nSavedCount && !bFound; ++j )
+ {
+ if ( m_aMark.GetMark( j )->GetMarkedSdrObj() == pCurrentMarked )
+ bFound = true;
+ }
+
+ // did not find a current mark in the saved marks
+ if ( !bFound )
+ bMisMatch = true;
+ }
+
+ if ( bMisMatch )
+ {
+ m_aMark.Clear();
+ _rRestoredMarkList = rCurrentList;
+ return;
+ }
+ }
+ // it is important that the objects of the mark list are not accessed,
+ // because they can be already destroyed
+ SdrPageView* pCurPageView = m_pView->GetSdrPageView();
+ SdrObjListIter aPageIter( pPage );
+ bool bFound = true;
+
+ // do all objects still exist
+ const size_t nCount = m_aMark.GetMarkCount();
+ for (size_t i = 0; i < nCount && bFound; ++i)
+ {
+ SdrMark* pMark = m_aMark.GetMark(i);
+ SdrObject* pObj = pMark->GetMarkedSdrObj();
+ if (pObj->IsGroupObject())
+ {
+ SdrObjListIter aIter(pObj->GetSubList());
+ while (aIter.IsMore() && bFound)
+ bFound = lcl_hasObject(aPageIter, aIter.Next());
+ }
+ else
+ bFound = lcl_hasObject(aPageIter, pObj);
+
+ bFound = bFound && pCurPageView == pMark->GetPageView();
+ }
+
+ if (bFound)
+ {
+ // evaluate the LastObject
+ if (nCount) // now mark the objects
+ {
+ for (size_t i = 0; i < nCount; ++i)
+ {
+ SdrMark* pMark = m_aMark.GetMark(i);
+ SdrObject* pObj = pMark->GetMarkedSdrObj();
+ if ( pObj->GetObjInventor() == SdrInventor::FmForm )
+ if ( !m_pView->IsObjMarked( pObj ) )
+ m_pView->MarkObj( pObj, pMark->GetPageView() );
+ }
+
+ _rRestoredMarkList = m_aMark;
+ }
+ }
+ m_aMark.Clear();
+}
+
+void SAL_CALL FmXFormView::focusGained( const FocusEvent& /*e*/ )
+{
+ if ( m_xWindow.is() && m_pView )
+ {
+ m_pView->SetMoveOutside( true, FmFormView::ImplAccess() );
+ }
+}
+
+void SAL_CALL FmXFormView::focusLost( const FocusEvent& /*e*/ )
+{
+ // when switch the focus outside the office the mark didn't change
+ // so we can not remove us as focus listener
+ if ( m_xWindow.is() && m_pView )
+ {
+ m_pView->SetMoveOutside( false, FmFormView::ImplAccess() );
+ }
+}
+
+DocumentType FmXFormView::impl_getDocumentType() const
+{
+ if ( GetFormShell() && GetFormShell()->GetImpl() )
+ return GetFormShell()->GetImpl()->getDocumentType_Lock();
+ return eUnknownDocumentType;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/form/formcontrolfactory.cxx b/svx/source/form/formcontrolfactory.cxx
new file mode 100644
index 000000000..61a28b21a
--- /dev/null
+++ b/svx/source/form/formcontrolfactory.cxx
@@ -0,0 +1,702 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+
+#include <formcontrolfactory.hxx>
+#include <fmcontrollayout.hxx>
+#include <fmprop.hxx>
+#include <svx/strings.hrc>
+#include <fmservs.hxx>
+#include <svx/dialmgr.hxx>
+#include <svx/svdouno.hxx>
+
+#include <com/sun/star/form/XFormComponent.hpp>
+#include <com/sun/star/form/FormComponentType.hpp>
+#include <com/sun/star/awt/ScrollBarOrientation.hpp>
+#include <com/sun/star/awt/MouseWheelBehavior.hpp>
+#include <com/sun/star/form/XGridColumnFactory.hpp>
+#include <com/sun/star/style/VerticalAlignment.hpp>
+#include <com/sun/star/awt/LineEndFormat.hpp>
+#include <com/sun/star/awt/ImageScaleMode.hpp>
+#include <com/sun/star/sdbc/DataType.hpp>
+#include <com/sun/star/sdbc/XDataSource.hpp>
+#include <com/sun/star/util/XNumberFormatTypes.hpp>
+#include <com/sun/star/sdbc/ColumnValue.hpp>
+#include <com/sun/star/text/WritingMode2.hpp>
+
+#include <comphelper/numbers.hxx>
+#include <comphelper/processfactory.hxx>
+#include <unotools/syslocale.hxx>
+#include <tools/gen.hxx>
+#include <tools/diagnose_ex.h>
+#include <connectivity/dbtools.hxx>
+#include <i18nlangtag/languagetag.hxx>
+
+#include <set>
+
+using namespace ::dbtools;
+
+namespace svxform
+{
+
+
+ using ::com::sun::star::uno::Reference;
+ using ::com::sun::star::uno::UNO_QUERY;
+ using ::com::sun::star::uno::UNO_QUERY_THROW;
+ using ::com::sun::star::uno::UNO_SET_THROW;
+ using ::com::sun::star::uno::Exception;
+ using ::com::sun::star::uno::Any;
+ using ::com::sun::star::uno::Sequence;
+ using ::com::sun::star::uno::XComponentContext;
+ using ::com::sun::star::beans::XPropertySet;
+ using ::com::sun::star::form::XFormComponent;
+ using ::com::sun::star::container::XIndexAccess;
+ using ::com::sun::star::beans::XPropertySetInfo;
+ using ::com::sun::star::beans::PropertyValue;
+ using ::com::sun::star::container::XChild;
+ using ::com::sun::star::form::XGridColumnFactory;
+ using ::com::sun::star::style::VerticalAlignment_MIDDLE;
+ using ::com::sun::star::beans::Property;
+ using ::com::sun::star::uno::TypeClass_DOUBLE;
+ using ::com::sun::star::uno::TypeClass_LONG;
+ using ::com::sun::star::util::XNumberFormats;
+ using ::com::sun::star::util::XNumberFormatTypes;
+ using ::com::sun::star::lang::XServiceInfo;
+ using ::com::sun::star::container::XNameAccess;
+
+ namespace FormComponentType = ::com::sun::star::form::FormComponentType;
+ namespace ScrollBarOrientation = ::com::sun::star::awt::ScrollBarOrientation;
+ namespace MouseWheelBehavior = ::com::sun::star::awt::MouseWheelBehavior;
+ namespace LineEndFormat = ::com::sun::star::awt::LineEndFormat;
+ namespace ImageScaleMode = ::com::sun::star::awt::ImageScaleMode;
+ namespace DataType = ::com::sun::star::sdbc::DataType;
+ namespace ColumnValue = ::com::sun::star::sdbc::ColumnValue;
+ namespace WritingMode2 = ::com::sun::star::text::WritingMode2;
+
+ FormControlFactory::FormControlFactory( const Reference<XComponentContext>& _rContext )
+ :m_xContext( _rContext )
+ {
+ }
+
+ FormControlFactory::FormControlFactory( )
+ :m_xContext( comphelper::getProcessComponentContext() )
+ {
+ }
+
+
+ FormControlFactory::~FormControlFactory()
+ {
+ }
+
+
+ sal_Int16 FormControlFactory::initializeControlModel( const DocumentType _eDocType, const SdrUnoObj& _rObject )
+ {
+ return initializeControlModel(
+ _eDocType,
+ Reference< XPropertySet >( _rObject.GetUnoControlModel(), UNO_QUERY ),
+ _rObject.GetCurrentBoundRect()
+ );
+ }
+
+
+ void FormControlFactory::initializeControlModel( const DocumentType _eDocType, const Reference< XPropertySet >& _rxControlModel )
+ {
+ initializeControlModel(
+ _eDocType, _rxControlModel, tools::Rectangle()
+ );
+ }
+
+
+ namespace
+ {
+
+ OUString lcl_getUniqueLabel_nothrow( const Reference< XPropertySet >& _rxControlModel, const OUString& _rBaseLabel )
+ {
+ OUString sLabel( _rBaseLabel );
+ try
+ {
+ typedef ::std::set< OUString > StringBag;
+ StringBag aUsedLabels;
+
+ Reference< XFormComponent > xFormComponent( _rxControlModel, UNO_QUERY_THROW );
+ Reference< XIndexAccess > xContainer( xFormComponent->getParent(), UNO_QUERY_THROW );
+ // loop through all siblings of the control model, and collect their labels
+ for ( sal_Int32 index=xContainer->getCount(); index>0; )
+ {
+ Reference< XPropertySet > xElement( xContainer->getByIndex( --index ), UNO_QUERY_THROW );
+ if ( xElement == _rxControlModel )
+ continue;
+
+ Reference< XPropertySetInfo > xPSI( xElement->getPropertySetInfo(), UNO_SET_THROW );
+ if ( !xPSI->hasPropertyByName( FM_PROP_LABEL ) )
+ continue;
+
+ OUString sElementLabel;
+ OSL_VERIFY( xElement->getPropertyValue( FM_PROP_LABEL ) >>= sElementLabel );
+ aUsedLabels.insert( sElementLabel );
+ }
+
+ // now find a free label
+ sal_Int32 i=2;
+ while ( aUsedLabels.find( sLabel ) != aUsedLabels.end() )
+ {
+ OUStringBuffer aBuffer( _rBaseLabel );
+ aBuffer.append( " " );
+ aBuffer.append( i++ );
+ sLabel = aBuffer.makeStringAndClear();
+ }
+ }
+ catch( const Exception& )
+ {
+ DBG_UNHANDLED_EXCEPTION("svx");
+ }
+ return sLabel;
+ }
+
+
+ Sequence< PropertyValue > lcl_getDataSourceIndirectProperties( const Reference< XPropertySet >& _rxControlModel,
+ const Reference<XComponentContext>& _rContext )
+ {
+ OSL_PRECOND( _rxControlModel.is(), "lcl_getDataSourceIndirectProperties: invalid model!" );
+
+ Sequence< PropertyValue > aInfo;
+ try
+ {
+ Reference< XChild > xChild( _rxControlModel, UNO_QUERY );
+ Reference< XPropertySet > xForm;
+ if ( xChild.is() )
+ xForm.set(xChild->getParent(), css::uno::UNO_QUERY);
+
+ if ( Reference< XGridColumnFactory >( xForm, UNO_QUERY ).is() )
+ { // hmm. the model is a grid column, in real
+ xChild.set(xForm, css::uno::UNO_QUERY);
+ xForm.set(xChild->getParent(), css::uno::UNO_QUERY);
+ }
+
+ OSL_ENSURE( xForm.is(), "lcl_getDataSourceIndirectProperties: could not determine the form!" );
+ if ( !xForm.is() )
+ return aInfo;
+ OUString sDataSourceName;
+ xForm->getPropertyValue( FM_PROP_DATASOURCE ) >>= sDataSourceName;
+
+ Reference< XPropertySet > xDsProperties;
+ if ( !sDataSourceName.isEmpty() )
+ xDsProperties.set(getDataSource( sDataSourceName, _rContext ), css::uno::UNO_QUERY);
+ if ( xDsProperties.is() )
+ xDsProperties->getPropertyValue("Info") >>= aInfo;
+ }
+ catch( const Exception& )
+ {
+ TOOLS_WARN_EXCEPTION( "svx", "lcl_getDataSourceIndirectProperties" );
+ }
+ return aInfo;
+ }
+
+
+ const char* aCharacterAndParagraphProperties[] =
+ {
+ "CharFontName",
+ "CharFontStyleName",
+ "CharFontFamily",
+ "CharFontCharSet",
+ "CharFontPitch",
+ "CharColor",
+ "CharEscapement",
+ "CharHeight",
+ "CharUnderline",
+ "CharWeight",
+ "CharPosture",
+ "CharAutoKerning",
+ "CharBackColor",
+ "CharBackTransparent",
+ "CharCaseMap",
+ "CharCrossedOut",
+ "CharFlash",
+ "CharStrikeout",
+ "CharWordMode",
+ "CharKerning",
+ "CharLocale",
+ "CharKeepTogether",
+ "CharNoLineBreak",
+ "CharShadowed",
+ "CharFontType",
+ "CharStyleName",
+ "CharContoured",
+ "CharCombineIsOn",
+ "CharCombinePrefix",
+ "CharCombineSuffix",
+ "CharEmphasize",
+ "CharRelief",
+ "RubyText",
+ "RubyAdjust",
+ "RubyCharStyleName",
+ "RubyIsAbove",
+ "CharRotation",
+ "CharRotationIsFitToLine",
+ "CharScaleWidth",
+ "HyperLinkURL",
+ "HyperLinkTarget",
+ "HyperLinkName",
+ "VisitedCharStyleName",
+ "UnvisitedCharStyleName",
+ "CharEscapementHeight",
+ "CharNoHyphenation",
+ "CharUnderlineColor",
+ "CharUnderlineHasColor",
+ "CharStyleNames",
+ "CharHeightAsian",
+ "CharWeightAsian",
+ "CharFontNameAsian",
+ "CharFontStyleNameAsian",
+ "CharFontFamilyAsian",
+ "CharFontCharSetAsian",
+ "CharFontPitchAsian",
+ "CharPostureAsian",
+ "CharLocaleAsian",
+ "ParaIsCharacterDistance",
+ "ParaIsForbiddenRules",
+ "ParaIsHangingPunctuation",
+ "CharHeightComplex",
+ "CharWeightComplex",
+ "CharFontNameComplex",
+ "CharFontStyleNameComplex",
+ "CharFontFamilyComplex",
+ "CharFontCharSetComplex",
+ "CharFontPitchComplex",
+ "CharPostureComplex",
+ "CharLocaleComplex",
+ "ParaAdjust",
+ "ParaLineSpacing",
+ "ParaBackColor",
+ "ParaBackTransparent",
+ "ParaBackGraphic",
+ "ParaBackGraphicURL",
+ "ParaBackGraphicFilter",
+ "ParaBackGraphicLocation",
+ "ParaLastLineAdjust",
+ "ParaExpandSingleWord",
+ "ParaLeftMargin",
+ "ParaRightMargin",
+ "ParaTopMargin",
+ "ParaBottomMargin",
+ "ParaLineNumberCount",
+ "ParaLineNumberStartValue",
+ "PageDescName",
+ "PageNumberOffset",
+ "ParaRegisterModeActive",
+ "ParaTabStops",
+ "ParaStyleName",
+ "DropCapFormat",
+ "DropCapWholeWord",
+ "ParaKeepTogether",
+ "Setting",
+ "ParaSplit",
+ "Setting",
+ "NumberingLevel",
+ "NumberingRules",
+ "NumberingStartValue",
+ "ParaIsNumberingRestart",
+ "NumberingStyleName",
+ "ParaOrphans",
+ "ParaWidows",
+ "ParaShadowFormat",
+ "LeftBorder",
+ "RightBorder",
+ "TopBorder",
+ "BottomBorder",
+ "BorderDistance",
+ "LeftBorderDistance",
+ "RightBorderDistance",
+ "TopBorderDistance",
+ "BottomBorderDistance",
+ "BreakType",
+ "DropCapCharStyleName",
+ "ParaFirstLineIndent",
+ "ParaIsAutoFirstLineIndent",
+ "ParaIsHyphenation",
+ "ParaHyphenationMaxHyphens",
+ "ParaHyphenationMaxLeadingChars",
+ "ParaHyphenationMaxTrailingChars",
+ "ParaVertAlignment",
+ "ParaUserDefinedAttributes",
+ "NumberingIsNumber",
+ "ParaIsConnectBorder",
+ nullptr
+ };
+
+
+ void lcl_initializeCharacterAttributes( const Reference< XPropertySet >& _rxModel )
+ {
+ try
+ {
+ Reference< XPropertySet > xStyle( ControlLayouter::getDefaultDocumentTextStyle( _rxModel ), UNO_SET_THROW );
+
+ // transfer all properties which are described by the style
+ Reference< XPropertySetInfo > xSourcePropInfo( xStyle->getPropertySetInfo(), UNO_SET_THROW );
+ Reference< XPropertySetInfo > xDestPropInfo( _rxModel->getPropertySetInfo(), UNO_SET_THROW );
+
+ OUString sPropertyName;
+ const char** pCharacterProperty = aCharacterAndParagraphProperties;
+ while ( *pCharacterProperty )
+ {
+ sPropertyName = OUString::createFromAscii( *pCharacterProperty );
+
+ if ( xSourcePropInfo->hasPropertyByName( sPropertyName ) && xDestPropInfo->hasPropertyByName( sPropertyName ) )
+ _rxModel->setPropertyValue( sPropertyName, xStyle->getPropertyValue( sPropertyName ) );
+
+ ++pCharacterProperty;
+ }
+ }
+ catch( const Exception& )
+ {
+ DBG_UNHANDLED_EXCEPTION("svx");
+ }
+ }
+ }
+
+
+ sal_Int16 FormControlFactory::initializeControlModel( const DocumentType _eDocType, const Reference< XPropertySet >& _rxControlModel,
+ const tools::Rectangle& _rControlBoundRect )
+ {
+ sal_Int16 nClassId = FormComponentType::CONTROL;
+
+ OSL_ENSURE( _rxControlModel.is(), "FormControlFactory::initializeControlModel: invalid model!" );
+ if ( !_rxControlModel.is() )
+ return nClassId;
+
+ try
+ {
+ ControlLayouter::initializeControlLayout( _rxControlModel, _eDocType );
+
+ _rxControlModel->getPropertyValue( FM_PROP_CLASSID ) >>= nClassId;
+ Reference< XPropertySetInfo > xPSI( _rxControlModel->getPropertySetInfo(), UNO_SET_THROW );
+ switch ( nClassId )
+ {
+ case FormComponentType::SCROLLBAR:
+ _rxControlModel->setPropertyValue("LiveScroll", Any( true ) );
+ [[fallthrough]];
+ case FormComponentType::SPINBUTTON:
+ {
+ sal_Int32 eOrientation = ScrollBarOrientation::HORIZONTAL;
+ if ( !_rControlBoundRect.IsEmpty() && ( _rControlBoundRect.GetWidth() < _rControlBoundRect.GetHeight() ) )
+ eOrientation = ScrollBarOrientation::VERTICAL;
+ _rxControlModel->setPropertyValue( FM_PROP_ORIENTATION, Any( eOrientation ) );
+ }
+ break;
+
+ case FormComponentType::LISTBOX:
+ case FormComponentType::COMBOBOX:
+ {
+ bool bDropDown = !_rControlBoundRect.IsEmpty() && ( _rControlBoundRect.GetWidth() >= 3 * _rControlBoundRect.GetHeight() );
+ if ( xPSI->hasPropertyByName( FM_PROP_DROPDOWN ) )
+ _rxControlModel->setPropertyValue( FM_PROP_DROPDOWN, Any( bDropDown ) );
+ _rxControlModel->setPropertyValue( FM_PROP_LINECOUNT, Any( sal_Int16( 20 ) ) );
+ }
+ break;
+
+ case FormComponentType::TEXTFIELD:
+ {
+ initializeTextFieldLineEnds( _rxControlModel );
+ lcl_initializeCharacterAttributes( _rxControlModel );
+
+ if ( !_rControlBoundRect.IsEmpty()
+ && ( _rControlBoundRect.GetWidth() <= 4 * _rControlBoundRect.GetHeight() )
+ )
+ {
+ if ( xPSI->hasPropertyByName( FM_PROP_MULTILINE ) )
+ _rxControlModel->setPropertyValue( FM_PROP_MULTILINE, Any( true ) );
+ }
+ }
+ break;
+
+ case FormComponentType::RADIOBUTTON:
+ case FormComponentType::CHECKBOX:
+ case FormComponentType::FIXEDTEXT:
+ {
+ OUString sVertAlignPropertyName( "VerticalAlign" );
+ if ( xPSI->hasPropertyByName( sVertAlignPropertyName ) )
+ _rxControlModel->setPropertyValue( sVertAlignPropertyName, Any( VerticalAlignment_MIDDLE ) );
+ }
+ break;
+
+ case FormComponentType::IMAGEBUTTON:
+ case FormComponentType::IMAGECONTROL:
+ {
+ static const OUStringLiteral sScaleModeProperty( u"ScaleMode" );
+ if ( xPSI->hasPropertyByName( sScaleModeProperty ) )
+ _rxControlModel->setPropertyValue( sScaleModeProperty, Any( ImageScaleMode::ISOTROPIC ) );
+ }
+ break;
+ }
+
+ // initial default label for the control
+ if ( xPSI->hasPropertyByName( FM_PROP_LABEL ) )
+ {
+ OUString sExistingLabel;
+ OSL_VERIFY( _rxControlModel->getPropertyValue( FM_PROP_LABEL ) >>= sExistingLabel );
+ if ( sExistingLabel.isEmpty() )
+ {
+ OUString sInitialLabel;
+ OSL_VERIFY( _rxControlModel->getPropertyValue( FM_PROP_NAME ) >>= sInitialLabel );
+
+ TranslateId pTitleResId;
+ switch ( nClassId )
+ {
+ case FormComponentType::COMMANDBUTTON: pTitleResId = RID_STR_PROPTITLE_PUSHBUTTON; break;
+ case FormComponentType::RADIOBUTTON: pTitleResId = RID_STR_PROPTITLE_RADIOBUTTON; break;
+ case FormComponentType::CHECKBOX: pTitleResId = RID_STR_PROPTITLE_CHECKBOX; break;
+ case FormComponentType::GROUPBOX: pTitleResId = RID_STR_PROPTITLE_GROUPBOX; break;
+ case FormComponentType::FIXEDTEXT: pTitleResId = RID_STR_PROPTITLE_FIXEDTEXT; break;
+ }
+
+ if (pTitleResId)
+ sInitialLabel = SvxResId(pTitleResId);
+
+ _rxControlModel->setPropertyValue(
+ FM_PROP_LABEL,
+ Any( lcl_getUniqueLabel_nothrow( _rxControlModel, sInitialLabel ) )
+ );
+ }
+ }
+
+ // strict format = yes is the default (i93467)
+ if ( xPSI->hasPropertyByName( FM_PROP_STRICTFORMAT ) )
+ {
+ _rxControlModel->setPropertyValue( FM_PROP_STRICTFORMAT, Any( true ) );
+ }
+
+ // mouse wheel: don't use it for scrolling by default (i110036)
+ if ( xPSI->hasPropertyByName( FM_PROP_MOUSE_WHEEL_BEHAVIOR ) )
+ {
+ _rxControlModel->setPropertyValue( FM_PROP_MOUSE_WHEEL_BEHAVIOR, Any( MouseWheelBehavior::SCROLL_DISABLED ) );
+ }
+
+ if ( xPSI->hasPropertyByName( FM_PROP_WRITING_MODE ) )
+ _rxControlModel->setPropertyValue( FM_PROP_WRITING_MODE, Any( WritingMode2::CONTEXT ) );
+ }
+ catch( const Exception& )
+ {
+ DBG_UNHANDLED_EXCEPTION("svx");
+ }
+ return nClassId;
+ }
+
+
+ void FormControlFactory::initializeTextFieldLineEnds( const Reference< XPropertySet >& _rxModel )
+ {
+ OSL_PRECOND( _rxModel.is(), "initializeTextFieldLineEnds: invalid model!" );
+ if ( !_rxModel.is() )
+ return;
+
+ try
+ {
+ Reference< XPropertySetInfo > xInfo = _rxModel->getPropertySetInfo();
+ if ( !xInfo.is() || !xInfo->hasPropertyByName( FM_PROP_LINEENDFORMAT ) )
+ return;
+
+ // let's see if the data source which the form belongs to (if any)
+ // has a setting for the preferred line end format
+ bool bDosLineEnds = false;
+ const Sequence< PropertyValue > aInfo = lcl_getDataSourceIndirectProperties( _rxModel, m_xContext );
+ const PropertyValue* pInfo = std::find_if(aInfo.begin(), aInfo.end(),
+ [](const PropertyValue& rInfo) { return rInfo.Name == "PreferDosLikeLineEnds"; });
+ if (pInfo != aInfo.end())
+ pInfo->Value >>= bDosLineEnds;
+
+ sal_Int16 nLineEndFormat = bDosLineEnds ? LineEndFormat::CARRIAGE_RETURN_LINE_FEED : LineEndFormat::LINE_FEED;
+ _rxModel->setPropertyValue( FM_PROP_LINEENDFORMAT, Any( nLineEndFormat ) );
+ }
+ catch( const Exception& )
+ {
+ DBG_UNHANDLED_EXCEPTION("svx");
+ }
+ }
+
+
+ void FormControlFactory::initializeFieldDependentProperties( const Reference< XPropertySet >& _rxDatabaseField,
+ const Reference< XPropertySet >& _rxControlModel, const Reference< XNumberFormats >& _rxNumberFormats )
+ {
+ OSL_PRECOND( _rxDatabaseField.is() && _rxControlModel.is(),
+ "FormControlFactory::initializeFieldDependentProperties: illegal params!" );
+ if ( !_rxDatabaseField.is() || !_rxControlModel.is() )
+ return;
+
+ try
+ {
+
+ // if the field has a numeric format, and the model has a "Scale" property, sync it
+ Reference< XPropertySetInfo > xFieldPSI( _rxDatabaseField->getPropertySetInfo(), UNO_SET_THROW );
+ Reference< XPropertySetInfo > xModelPSI( _rxControlModel->getPropertySetInfo(), UNO_SET_THROW );
+
+ if ( xModelPSI->hasPropertyByName( FM_PROP_DECIMAL_ACCURACY ) )
+ {
+ sal_Int32 nFormatKey = 0;
+ if ( xFieldPSI->hasPropertyByName( FM_PROP_FORMATKEY ) )
+ {
+ _rxDatabaseField->getPropertyValue( FM_PROP_FORMATKEY ) >>= nFormatKey;
+ }
+ else
+ {
+ nFormatKey = getDefaultNumberFormat(
+ _rxDatabaseField,
+ Reference< XNumberFormatTypes >( _rxNumberFormats, UNO_QUERY ),
+ SvtSysLocale().GetLanguageTag().getLocale()
+ );
+ }
+
+ Any aScaleVal( ::comphelper::getNumberFormatDecimals( _rxNumberFormats, nFormatKey ) );
+ _rxControlModel->setPropertyValue( FM_PROP_DECIMAL_ACCURACY, aScaleVal );
+ }
+
+
+ // minimum and maximum of the control according to the type of the database field
+ sal_Int32 nDataType = DataType::OTHER;
+ OSL_VERIFY( _rxDatabaseField->getPropertyValue( FM_PROP_FIELDTYPE ) >>= nDataType );
+
+ if ( xModelPSI->hasPropertyByName( FM_PROP_VALUEMIN )
+ && xModelPSI->hasPropertyByName( FM_PROP_VALUEMAX )
+ )
+ {
+ sal_Int32 nMinValue = -1000000000, nMaxValue = 1000000000;
+ switch ( nDataType )
+ {
+ case DataType::TINYINT : nMinValue = 0; nMaxValue = 255; break;
+ case DataType::SMALLINT : nMinValue = -32768; nMaxValue = 32767; break;
+ case DataType::INTEGER : nMinValue = 0x80000000; nMaxValue = 0x7FFFFFFF; break;
+ // double and singles are ignored
+ }
+
+ Any aValue;
+
+ // both the minimum and the maximum value properties can be either Long or Double
+ Property aProperty = xModelPSI->getPropertyByName( FM_PROP_VALUEMIN );
+ if ( aProperty.Type.getTypeClass() == TypeClass_DOUBLE )
+ aValue <<= static_cast<double>(nMinValue);
+ else if ( aProperty.Type.getTypeClass() == TypeClass_LONG )
+ aValue <<= nMinValue;
+ else
+ {
+ OSL_FAIL( "FormControlFactory::initializeFieldDependentProperties: unexpected property type (MinValue)!" );
+ }
+ _rxControlModel->setPropertyValue( FM_PROP_VALUEMIN, aValue );
+
+ // both the minimum and the maximum value properties can be either Long or Double
+ aProperty = xModelPSI->getPropertyByName( FM_PROP_VALUEMAX );
+ if ( aProperty.Type.getTypeClass() == TypeClass_DOUBLE )
+ aValue <<= static_cast<double>(nMaxValue);
+ else if ( aProperty.Type.getTypeClass() == TypeClass_LONG )
+ aValue <<= nMaxValue;
+ else
+ {
+ OSL_FAIL( "FormControlFactory::initializeFieldDependentProperties: unexpected property type (MaxValue)!" );
+ }
+ _rxControlModel->setPropertyValue( FM_PROP_VALUEMAX, aValue );
+ }
+
+
+ // a check box can be tristate if and only if the column it is bound to is nullable
+ sal_Int16 nClassId = FormComponentType::CONTROL;
+ OSL_VERIFY( _rxControlModel->getPropertyValue( FM_PROP_CLASSID ) >>= nClassId );
+ if ( nClassId == FormComponentType::CHECKBOX )
+ {
+ sal_Int32 nNullable = ColumnValue::NULLABLE_UNKNOWN;
+ OSL_VERIFY( _rxDatabaseField->getPropertyValue( FM_PROP_ISNULLABLE ) >>= nNullable );
+ _rxControlModel->setPropertyValue( FM_PROP_TRISTATE, Any( ColumnValue::NO_NULLS != nNullable ) );
+ }
+ }
+ catch( const Exception& )
+ {
+ DBG_UNHANDLED_EXCEPTION("svx");
+ }
+ }
+
+
+ OUString FormControlFactory::getDefaultName( sal_Int16 _nClassId, const Reference< XServiceInfo >& _rxObject )
+ {
+ TranslateId pResId;
+
+ switch ( _nClassId )
+ {
+ case FormComponentType::COMMANDBUTTON: pResId = RID_STR_PROPTITLE_PUSHBUTTON; break;
+ case FormComponentType::RADIOBUTTON: pResId = RID_STR_PROPTITLE_RADIOBUTTON; break;
+ case FormComponentType::CHECKBOX: pResId = RID_STR_PROPTITLE_CHECKBOX; break;
+ case FormComponentType::LISTBOX: pResId = RID_STR_PROPTITLE_LISTBOX; break;
+ case FormComponentType::COMBOBOX: pResId = RID_STR_PROPTITLE_COMBOBOX; break;
+ case FormComponentType::GROUPBOX: pResId = RID_STR_PROPTITLE_GROUPBOX; break;
+ case FormComponentType::IMAGEBUTTON: pResId = RID_STR_PROPTITLE_IMAGEBUTTON; break;
+ case FormComponentType::FIXEDTEXT: pResId = RID_STR_PROPTITLE_FIXEDTEXT; break;
+ case FormComponentType::GRIDCONTROL: pResId = RID_STR_PROPTITLE_DBGRID; break;
+ case FormComponentType::FILECONTROL: pResId = RID_STR_PROPTITLE_FILECONTROL; break;
+ case FormComponentType::DATEFIELD: pResId = RID_STR_PROPTITLE_DATEFIELD; break;
+ case FormComponentType::TIMEFIELD: pResId = RID_STR_PROPTITLE_TIMEFIELD; break;
+ case FormComponentType::NUMERICFIELD: pResId = RID_STR_PROPTITLE_NUMERICFIELD; break;
+ case FormComponentType::CURRENCYFIELD: pResId = RID_STR_PROPTITLE_CURRENCYFIELD; break;
+ case FormComponentType::PATTERNFIELD: pResId = RID_STR_PROPTITLE_PATTERNFIELD; break;
+ case FormComponentType::IMAGECONTROL: pResId = RID_STR_PROPTITLE_IMAGECONTROL; break;
+ case FormComponentType::HIDDENCONTROL: pResId = RID_STR_PROPTITLE_HIDDEN; break;
+ case FormComponentType::SCROLLBAR: pResId = RID_STR_PROPTITLE_SCROLLBAR; break;
+ case FormComponentType::SPINBUTTON: pResId = RID_STR_PROPTITLE_SPINBUTTON; break;
+ case FormComponentType::NAVIGATIONBAR: pResId = RID_STR_PROPTITLE_NAVBAR; break;
+
+ case FormComponentType::TEXTFIELD:
+ pResId = RID_STR_PROPTITLE_EDIT;
+ if ( _rxObject.is() && _rxObject->supportsService( FM_SUN_COMPONENT_FORMATTEDFIELD ) )
+ pResId = RID_STR_PROPTITLE_FORMATTED;
+ break;
+
+ default:
+ pResId = RID_STR_CONTROL; break;
+ }
+
+ return SvxResId(pResId);
+ }
+
+
+ OUString FormControlFactory::getDefaultUniqueName_ByComponentType( const Reference< XNameAccess >& _rxContainer,
+ const Reference< XPropertySet >& _rxObject )
+ {
+ sal_Int16 nClassId = FormComponentType::CONTROL;
+ OSL_VERIFY( _rxObject->getPropertyValue( FM_PROP_CLASSID ) >>= nClassId );
+ OUString sBaseName = getDefaultName( nClassId, Reference< XServiceInfo >( _rxObject, UNO_QUERY ) );
+
+ return getUniqueName( _rxContainer, sBaseName );
+ }
+
+
+ OUString FormControlFactory::getUniqueName( const Reference< XNameAccess >& _rxContainer, std::u16string_view _rBaseName )
+ {
+ sal_Int32 n = 0;
+ OUString sName;
+ do
+ {
+ OUStringBuffer aBuf( _rBaseName );
+ aBuf.append( " " );
+ aBuf.append( ++n );
+ sName = aBuf.makeStringAndClear();
+ }
+ while ( _rxContainer->hasByName( sName ) );
+
+ return sName;
+ }
+
+
+}
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/form/formcontroller.cxx b/svx/source/form/formcontroller.cxx
new file mode 100644
index 000000000..d53b58939
--- /dev/null
+++ b/svx/source/form/formcontroller.cxx
@@ -0,0 +1,4168 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+
+#include <fmcontrolbordermanager.hxx>
+#include <fmcontrollayout.hxx>
+#include <formcontroller.hxx>
+#include <formfeaturedispatcher.hxx>
+#include <fmdocumentclassification.hxx>
+#include <formcontrolling.hxx>
+#include <fmprop.hxx>
+#include <svx/dialmgr.hxx>
+#include <svx/strings.hrc>
+#include <fmservs.hxx>
+#include <svx/fmtools.hxx>
+#include <fmurl.hxx>
+
+#include <com/sun/star/awt/FocusChangeReason.hpp>
+#include <com/sun/star/awt/XCheckBox.hpp>
+#include <com/sun/star/awt/XComboBox.hpp>
+#include <com/sun/star/awt/XListBox.hpp>
+#include <com/sun/star/awt/XVclWindowPeer.hpp>
+#include <com/sun/star/awt/TabController.hpp>
+#include <com/sun/star/beans/PropertyAttribute.hpp>
+#include <com/sun/star/container/XIdentifierReplace.hpp>
+#include <com/sun/star/form/TabulatorCycle.hpp>
+#include <com/sun/star/form/validation/XValidatableFormComponent.hpp>
+#include <com/sun/star/form/XBoundComponent.hpp>
+#include <com/sun/star/form/XBoundControl.hpp>
+#include <com/sun/star/form/XGridControl.hpp>
+#include <com/sun/star/form/XLoadable.hpp>
+#include <com/sun/star/form/XReset.hpp>
+#include <com/sun/star/form/control/FilterControl.hpp>
+#include <com/sun/star/lang/IndexOutOfBoundsException.hpp>
+#include <com/sun/star/lang/XMultiServiceFactory.hpp>
+#include <com/sun/star/lang/NoSupportException.hpp>
+#include <com/sun/star/sdb/ParametersRequest.hpp>
+#include <com/sun/star/sdb/RowChangeAction.hpp>
+#include <com/sun/star/sdb/SQLFilterOperator.hpp>
+#include <com/sun/star/sdb/XInteractionSupplyParameters.hpp>
+#include <com/sun/star/sdbc/ColumnValue.hpp>
+#include <com/sun/star/task/InteractionHandler.hpp>
+#include <com/sun/star/form/runtime/FormOperations.hpp>
+#include <com/sun/star/form/runtime/FormFeature.hpp>
+#include <com/sun/star/container/XContainer.hpp>
+#include <com/sun/star/sdbcx/XColumnsSupplier.hpp>
+#include <com/sun/star/util/NumberFormatter.hpp>
+#include <com/sun/star/sdb/SQLContext.hpp>
+#include <com/sun/star/sdb/XColumn.hpp>
+
+#include <comphelper/enumhelper.hxx>
+#include <comphelper/interaction.hxx>
+#include <comphelper/processfactory.hxx>
+#include <comphelper/property.hxx>
+#include <comphelper/sequence.hxx>
+#include <comphelper/flagguard.hxx>
+#include <comphelper/types.hxx>
+#include <cppuhelper/supportsservice.hxx>
+#include <connectivity/IParseContext.hxx>
+#include <connectivity/dbtools.hxx>
+#include <connectivity/sqlparse.hxx>
+#include <toolkit/controls/unocontrol.hxx>
+#include <toolkit/helper/vclunohelper.hxx>
+#include <tools/debug.hxx>
+#include <tools/diagnose_ex.h>
+#include <unotools/localedatawrapper.hxx>
+#include <vcl/svapp.hxx>
+#include <vcl/settings.hxx>
+#include <o3tl/safeint.hxx>
+#include <osl/mutex.hxx>
+#include <sal/log.hxx>
+
+#include <algorithm>
+#include <iterator>
+
+using namespace ::com::sun::star;
+using namespace ::comphelper;
+using namespace ::connectivity;
+using namespace ::dbtools;
+
+
+css::uno::Reference< css::uno::XInterface >
+ FormController_NewInstance_Impl( const css::uno::Reference< css::lang::XMultiServiceFactory > & _rxORB )
+{
+ return *( new ::svxform::FormController( comphelper::getComponentContext(_rxORB) ) );
+}
+
+namespace svxform
+{
+
+ using ::com::sun::star::sdb::XColumn;
+ using ::com::sun::star::awt::XControl;
+ using ::com::sun::star::awt::TabController;
+ using ::com::sun::star::awt::XToolkit;
+ using ::com::sun::star::awt::XWindowPeer;
+ using ::com::sun::star::form::XGrid;
+ using ::com::sun::star::beans::XPropertySet;
+ using ::com::sun::star::uno::UNO_SET_THROW;
+ using ::com::sun::star::uno::UNO_QUERY_THROW;
+ using ::com::sun::star::container::XIndexAccess;
+ using ::com::sun::star::uno::Exception;
+ using ::com::sun::star::uno::XInterface;
+ using ::com::sun::star::uno::UNO_QUERY;
+ using ::com::sun::star::uno::Sequence;
+ using ::com::sun::star::uno::Reference;
+ using ::com::sun::star::beans::XPropertySetInfo;
+ using ::com::sun::star::beans::PropertyValue;
+ using ::com::sun::star::lang::IndexOutOfBoundsException;
+ using ::com::sun::star::sdb::XInteractionSupplyParameters;
+ using ::com::sun::star::awt::XTextComponent;
+ using ::com::sun::star::awt::XTextListener;
+ using ::com::sun::star::uno::Any;
+ using ::com::sun::star::frame::XDispatch;
+ using ::com::sun::star::lang::XMultiServiceFactory;
+ using ::com::sun::star::uno::Type;
+ using ::com::sun::star::lang::IllegalArgumentException;
+ using ::com::sun::star::sdbc::XConnection;
+ using ::com::sun::star::sdbc::XRowSet;
+ using ::com::sun::star::sdbc::XDatabaseMetaData;
+ using ::com::sun::star::util::XNumberFormatsSupplier;
+ using ::com::sun::star::util::NumberFormatter;
+ using ::com::sun::star::util::XNumberFormatter;
+ using ::com::sun::star::sdbcx::XColumnsSupplier;
+ using ::com::sun::star::container::XNameAccess;
+ using ::com::sun::star::lang::EventObject;
+ using ::com::sun::star::beans::Property;
+ using ::com::sun::star::container::XEnumeration;
+ using ::com::sun::star::form::XFormComponent;
+ using ::com::sun::star::form::runtime::XFormOperations;
+ using ::com::sun::star::form::runtime::FilterEvent;
+ using ::com::sun::star::form::runtime::XFilterControllerListener;
+ using ::com::sun::star::awt::XControlContainer;
+ using ::com::sun::star::container::XIdentifierReplace;
+ using ::com::sun::star::form::XFormControllerListener;
+ using ::com::sun::star::awt::XWindow;
+ using ::com::sun::star::sdbc::XResultSet;
+ using ::com::sun::star::awt::XControlModel;
+ using ::com::sun::star::awt::XTabControllerModel;
+ using ::com::sun::star::beans::PropertyChangeEvent;
+ using ::com::sun::star::form::validation::XValidatableFormComponent;
+ using ::com::sun::star::form::XLoadable;
+ using ::com::sun::star::form::XBoundControl;
+ using ::com::sun::star::beans::XPropertyChangeListener;
+ using ::com::sun::star::awt::TextEvent;
+ using ::com::sun::star::form::XBoundComponent;
+ using ::com::sun::star::awt::XCheckBox;
+ using ::com::sun::star::awt::XComboBox;
+ using ::com::sun::star::awt::XListBox;
+ using ::com::sun::star::awt::ItemEvent;
+ using ::com::sun::star::util::XModifyListener;
+ using ::com::sun::star::form::XReset;
+ using ::com::sun::star::frame::XDispatchProviderInterception;
+ using ::com::sun::star::form::XGridControl;
+ using ::com::sun::star::awt::XVclWindowPeer;
+ using ::com::sun::star::form::validation::XValidator;
+ using ::com::sun::star::awt::FocusEvent;
+ using ::com::sun::star::sdb::SQLContext;
+ using ::com::sun::star::container::XChild;
+ using ::com::sun::star::form::TabulatorCycle_RECORDS;
+ using ::com::sun::star::container::ContainerEvent;
+ using ::com::sun::star::lang::DisposedException;
+ using ::com::sun::star::lang::Locale;
+ using ::com::sun::star::lang::NoSupportException;
+ using ::com::sun::star::sdb::RowChangeEvent;
+ using ::com::sun::star::frame::XStatusListener;
+ using ::com::sun::star::frame::XDispatchProviderInterceptor;
+ using ::com::sun::star::sdb::SQLErrorEvent;
+ using ::com::sun::star::form::DatabaseParameterEvent;
+ using ::com::sun::star::sdb::ParametersRequest;
+ using ::com::sun::star::task::XInteractionRequest;
+ using ::com::sun::star::util::URL;
+ using ::com::sun::star::frame::FeatureStateEvent;
+ using ::com::sun::star::form::runtime::XFormControllerContext;
+ using ::com::sun::star::task::InteractionHandler;
+ using ::com::sun::star::task::XInteractionHandler;
+ using ::com::sun::star::form::runtime::FormOperations;
+ using ::com::sun::star::container::XContainer;
+ using ::com::sun::star::sdbc::SQLWarning;
+
+ namespace ColumnValue = ::com::sun::star::sdbc::ColumnValue;
+ namespace PropertyAttribute = ::com::sun::star::beans::PropertyAttribute;
+ namespace FocusChangeReason = ::com::sun::star::awt::FocusChangeReason;
+ namespace RowChangeAction = ::com::sun::star::sdb::RowChangeAction;
+ namespace FormFeature = ::com::sun::star::form::runtime::FormFeature;
+
+namespace {
+
+struct ColumnInfo
+{
+ // information about the column itself
+ Reference< XColumn > xColumn;
+ sal_Int32 nNullable;
+ bool bAutoIncrement;
+ bool bReadOnly;
+ OUString sName;
+
+ // information about the control(s) bound to this column
+
+ /// the first control which is bound to the given column, and which requires input
+ Reference< XControl > xFirstControlWithInputRequired;
+ /** the first grid control which contains a column which is bound to the given database column, and requires
+ input
+ */
+ Reference< XGrid > xFirstGridWithInputRequiredColumn;
+ /** if xFirstControlWithInputRequired is a grid control, then nRequiredGridColumn specifies the position
+ of the grid column which is actually bound
+ */
+ sal_Int32 nRequiredGridColumn;
+
+ ColumnInfo()
+ :nNullable( ColumnValue::NULLABLE_UNKNOWN )
+ ,bAutoIncrement( false )
+ ,bReadOnly( false )
+ ,nRequiredGridColumn( -1 )
+ {
+ }
+};
+
+}
+
+class ColumnInfoCache
+{
+public:
+ explicit ColumnInfoCache( const Reference< XColumnsSupplier >& _rxColSupplier );
+
+ size_t getColumnCount() const { return m_aColumns.size(); }
+ const ColumnInfo& getColumnInfo( size_t _pos );
+
+ bool controlsInitialized() const { return m_bControlsInitialized; }
+ void initializeControls( const Sequence< Reference< XControl > >& _rControls );
+ void deinitializeControls();
+
+private:
+ typedef ::std::vector< ColumnInfo > ColumnInfos;
+ ColumnInfos m_aColumns;
+ bool m_bControlsInitialized;
+};
+
+
+ColumnInfoCache::ColumnInfoCache( const Reference< XColumnsSupplier >& _rxColSupplier )
+ :m_bControlsInitialized( false )
+{
+ try
+ {
+ m_aColumns.clear();
+
+ Reference< XIndexAccess > xColumns( _rxColSupplier->getColumns(), UNO_QUERY_THROW );
+ sal_Int32 nColumnCount = xColumns->getCount();
+ m_aColumns.reserve( nColumnCount );
+
+ Reference< XPropertySet > xColumnProps;
+ for ( sal_Int32 i = 0; i < nColumnCount; ++i )
+ {
+ ColumnInfo aColInfo;
+ aColInfo.xColumn.set( xColumns->getByIndex(i), UNO_QUERY_THROW );
+
+ xColumnProps.set( aColInfo.xColumn, UNO_QUERY_THROW );
+ OSL_VERIFY( xColumnProps->getPropertyValue( FM_PROP_ISNULLABLE ) >>= aColInfo.nNullable );
+ OSL_VERIFY( xColumnProps->getPropertyValue( FM_PROP_AUTOINCREMENT ) >>= aColInfo.bAutoIncrement );
+ OSL_VERIFY( xColumnProps->getPropertyValue( FM_PROP_NAME ) >>= aColInfo.sName );
+ OSL_VERIFY( xColumnProps->getPropertyValue( FM_PROP_ISREADONLY ) >>= aColInfo.bReadOnly );
+
+ m_aColumns.push_back( aColInfo );
+ }
+ }
+ catch( const Exception& )
+ {
+ DBG_UNHANDLED_EXCEPTION("svx");
+ }
+}
+
+
+namespace
+{
+ bool lcl_isBoundTo( const Reference< XPropertySet >& _rxControlModel, const Reference< XInterface >& _rxNormDBField )
+ {
+ Reference< XInterface > xNormBoundField( _rxControlModel->getPropertyValue( FM_PROP_BOUNDFIELD ), UNO_QUERY );
+ return ( xNormBoundField == _rxNormDBField );
+ }
+
+ bool lcl_isInputRequired( const Reference< XPropertySet >& _rxControlModel )
+ {
+ bool bInputRequired = false;
+ OSL_VERIFY( _rxControlModel->getPropertyValue( FM_PROP_INPUT_REQUIRED ) >>= bInputRequired );
+ return bInputRequired;
+ }
+
+ void lcl_resetColumnControlInfo( ColumnInfo& _rColInfo )
+ {
+ _rColInfo.xFirstControlWithInputRequired.clear();
+ _rColInfo.xFirstGridWithInputRequiredColumn.clear();
+ _rColInfo.nRequiredGridColumn = -1;
+ }
+}
+
+
+void ColumnInfoCache::deinitializeControls()
+{
+ for (auto& rCol : m_aColumns)
+ {
+ lcl_resetColumnControlInfo( rCol );
+ }
+ m_bControlsInitialized = false;
+}
+
+
+void ColumnInfoCache::initializeControls( const Sequence< Reference< XControl > >& _rControls )
+{
+ try
+ {
+ // for every of our known columns, find the controls which are bound to this column
+ for (auto& rCol : m_aColumns)
+ {
+ OSL_ENSURE( !rCol.xFirstControlWithInputRequired.is() && !rCol.xFirstGridWithInputRequiredColumn.is()
+ && ( rCol.nRequiredGridColumn == -1 ), "ColumnInfoCache::initializeControls: called me twice?" );
+
+ lcl_resetColumnControlInfo( rCol );
+
+ Reference< XInterface > xNormColumn( rCol.xColumn, UNO_QUERY_THROW );
+
+ const Reference< XControl >* pControl( _rControls.getConstArray() );
+ const Reference< XControl >* pControlEnd( pControl + _rControls.getLength() );
+ for ( ; pControl != pControlEnd; ++pControl )
+ {
+ if ( !pControl->is() )
+ continue;
+
+ Reference< XPropertySet > xModel( (*pControl)->getModel(), UNO_QUERY_THROW );
+ Reference< XPropertySetInfo > xModelPSI( xModel->getPropertySetInfo(), UNO_SET_THROW );
+
+ // special handling for grid controls
+ Reference< XGrid > xGrid( *pControl, UNO_QUERY );
+ if ( xGrid.is() )
+ {
+ Reference< XIndexAccess > xGridColAccess( xModel, UNO_QUERY_THROW );
+ sal_Int32 gridColCount = xGridColAccess->getCount();
+ sal_Int32 gridCol = 0;
+ for ( gridCol = 0; gridCol < gridColCount; ++gridCol )
+ {
+ Reference< XPropertySet > xGridColumnModel( xGridColAccess->getByIndex( gridCol ), UNO_QUERY_THROW );
+
+ if ( !lcl_isBoundTo( xGridColumnModel, xNormColumn )
+ || !lcl_isInputRequired( xGridColumnModel )
+ )
+ continue; // with next grid column
+
+ break;
+ }
+
+ if ( gridCol < gridColCount )
+ {
+ // found a grid column which is bound to the given
+ rCol.xFirstGridWithInputRequiredColumn = xGrid;
+ rCol.nRequiredGridColumn = gridCol;
+ break;
+ }
+
+ continue; // with next control
+ }
+
+ if ( !xModelPSI->hasPropertyByName( FM_PROP_BOUNDFIELD )
+ || !lcl_isBoundTo( xModel, xNormColumn )
+ || !lcl_isInputRequired( xModel )
+ )
+ continue; // with next control
+
+ break;
+ }
+
+ if ( pControl == pControlEnd )
+ // did not find a control which is bound to this particular column, and for which the input is required
+ continue; // with next DB column
+
+ rCol.xFirstControlWithInputRequired = *pControl;
+ }
+ }
+ catch( const Exception& )
+ {
+ DBG_UNHANDLED_EXCEPTION("svx");
+ }
+
+ m_bControlsInitialized = true;
+}
+
+
+const ColumnInfo& ColumnInfoCache::getColumnInfo( size_t _pos )
+{
+ if ( _pos >= m_aColumns.size() )
+ throw IndexOutOfBoundsException();
+
+ return m_aColumns[ _pos ];
+}
+
+namespace {
+
+class OParameterContinuation : public OInteraction< XInteractionSupplyParameters >
+{
+ Sequence< PropertyValue > m_aValues;
+
+public:
+ OParameterContinuation() { }
+
+ const Sequence< PropertyValue >& getValues() const { return m_aValues; }
+
+// XInteractionSupplyParameters
+ virtual void SAL_CALL setParameters( const Sequence< PropertyValue >& _rValues ) override;
+};
+
+}
+
+void SAL_CALL OParameterContinuation::setParameters( const Sequence< PropertyValue >& _rValues )
+{
+ m_aValues = _rValues;
+}
+
+
+// FmXAutoControl
+
+struct FmFieldInfo
+{
+ OUString aFieldName;
+ Reference< XPropertySet > xField;
+ Reference< XTextComponent > xText;
+
+ FmFieldInfo(const Reference< XPropertySet >& _xField, const Reference< XTextComponent >& _xText)
+ :xField(_xField)
+ ,xText(_xText)
+ {xField->getPropertyValue(FM_PROP_NAME) >>= aFieldName;}
+};
+
+namespace {
+
+class FmXAutoControl: public UnoControl
+
+{
+public:
+ FmXAutoControl()
+ {
+ }
+
+ virtual OUString GetComponentServiceName() const override {return "Edit";}
+ virtual void SAL_CALL createPeer( const Reference< XToolkit > & rxToolkit, const Reference< XWindowPeer > & rParentPeer ) override;
+
+protected:
+ virtual void ImplSetPeerProperty( const OUString& rPropName, const Any& rVal ) override;
+};
+
+}
+
+void FmXAutoControl::createPeer( const Reference< XToolkit > & rxToolkit, const Reference< XWindowPeer > & rParentPeer )
+{
+ UnoControl::createPeer( rxToolkit, rParentPeer );
+
+ Reference< XTextComponent > xText(getPeer() , UNO_QUERY);
+ if (xText.is())
+ {
+ xText->setText(SvxResId(RID_STR_AUTOFIELD));
+ xText->setEditable(false);
+ }
+}
+
+
+void FmXAutoControl::ImplSetPeerProperty( const OUString& rPropName, const Any& rVal )
+{
+ // these properties are ignored
+ if (rPropName == FM_PROP_TEXT)
+ return;
+
+ UnoControl::ImplSetPeerProperty( rPropName, rVal );
+}
+
+
+IMPL_LINK_NOARG( FormController, OnActivateTabOrder, Timer*, void )
+{
+ activateTabOrder();
+}
+
+namespace {
+
+struct UpdateAllListeners
+{
+ bool operator()( const Reference< XDispatch >& _rxDispatcher ) const
+ {
+ static_cast< svx::OSingleFeatureDispatcher* >( _rxDispatcher.get() )->updateAllListeners();
+ // the return is a dummy only so we can use this struct in a lambda expression
+ return true;
+ }
+};
+
+}
+
+IMPL_LINK_NOARG( FormController, OnInvalidateFeatures, Timer*, void )
+{
+ ::osl::MutexGuard aGuard( m_aMutex );
+ for (const auto& rFeature : m_aInvalidFeatures)
+ {
+ DispatcherContainer::const_iterator aDispatcherPos = m_aFeatureDispatchers.find( rFeature );
+ if ( aDispatcherPos != m_aFeatureDispatchers.end() )
+ {
+ // TODO: for the real and actual listener notifications, we should release
+ // our mutex
+ UpdateAllListeners( )( aDispatcherPos->second );
+ }
+ }
+}
+
+FormController::FormController(const Reference< css::uno::XComponentContext > & _rxORB )
+ :FormController_BASE( m_aMutex )
+ ,OPropertySetHelper( FormController_BASE::rBHelper )
+ ,OSQLParserClient( _rxORB )
+ ,m_xComponentContext( _rxORB )
+ ,m_aActivateListeners(m_aMutex)
+ ,m_aModifyListeners(m_aMutex)
+ ,m_aErrorListeners(m_aMutex)
+ ,m_aDeleteListeners(m_aMutex)
+ ,m_aRowSetApproveListeners(m_aMutex)
+ ,m_aParameterListeners(m_aMutex)
+ ,m_aFilterListeners(m_aMutex)
+ ,m_aTabActivationIdle("svx FormController m_aTabActivationIdle")
+ ,m_aFeatureInvalidationTimer("svx FormController m_aFeatureInvalidationTimer")
+ ,m_aMode( OUString( "DataMode" ) )
+ ,m_aLoadEvent( LINK( this, FormController, OnLoad ) )
+ ,m_aToggleEvent( LINK( this, FormController, OnToggleAutoFields ) )
+ ,m_aActivationEvent( LINK( this, FormController, OnActivated ) )
+ ,m_aDeactivationEvent( LINK( this, FormController, OnDeactivated ) )
+ ,m_nCurrentFilterPosition(-1)
+ ,m_bCurrentRecordModified(false)
+ ,m_bCurrentRecordNew(false)
+ ,m_bLocked(false)
+ ,m_bDBConnection(false)
+ ,m_bCycle(false)
+ ,m_bCanInsert(false)
+ ,m_bCanUpdate(false)
+ ,m_bCommitLock(false)
+ ,m_bModified(false)
+ ,m_bControlsSorted(false)
+ ,m_bFiltering(false)
+ ,m_bAttachEvents(true)
+ ,m_bDetachEvents(true)
+ ,m_bAttemptedHandlerCreation( false )
+ ,m_bSuspendFilterTextListening( false )
+{
+
+ osl_atomic_increment(&m_refCount);
+ {
+ m_xTabController = TabController::create( m_xComponentContext );
+ m_xAggregate.set( m_xTabController, UNO_QUERY_THROW );
+ m_xAggregate->setDelegator( *this );
+ }
+ osl_atomic_decrement(&m_refCount);
+
+ m_aTabActivationIdle.SetPriority( TaskPriority::LOWEST );
+ m_aTabActivationIdle.SetInvokeHandler( LINK( this, FormController, OnActivateTabOrder ) );
+
+ m_aFeatureInvalidationTimer.SetTimeout( 200 );
+ m_aFeatureInvalidationTimer.SetInvokeHandler( LINK( this, FormController, OnInvalidateFeatures ) );
+}
+
+
+FormController::~FormController()
+{
+ {
+ ::osl::MutexGuard aGuard( m_aMutex );
+
+ m_aLoadEvent.CancelPendingCall();
+ m_aToggleEvent.CancelPendingCall();
+ m_aActivationEvent.CancelPendingCall();
+ m_aDeactivationEvent.CancelPendingCall();
+
+ if ( m_aTabActivationIdle.IsActive() )
+ m_aTabActivationIdle.Stop();
+ }
+
+ if ( m_aFeatureInvalidationTimer.IsActive() )
+ m_aFeatureInvalidationTimer.Stop();
+
+ disposeAllFeaturesAndDispatchers();
+
+ if ( m_xFormOperations.is() )
+ m_xFormOperations->dispose();
+ m_xFormOperations.clear();
+
+ // release of aggregation
+ if ( m_xAggregate.is() )
+ {
+ m_xAggregate->setDelegator( nullptr );
+ m_xAggregate.clear();
+ }
+}
+
+
+void SAL_CALL FormController::acquire() noexcept
+{
+ FormController_BASE::acquire();
+}
+
+
+void SAL_CALL FormController::release() noexcept
+{
+ FormController_BASE::release();
+}
+
+
+Any SAL_CALL FormController::queryInterface( const Type& _rType )
+{
+ Any aRet = FormController_BASE::queryInterface( _rType );
+ if ( !aRet.hasValue() )
+ aRet = OPropertySetHelper::queryInterface( _rType );
+ if ( !aRet.hasValue() )
+ aRet = m_xAggregate->queryAggregation( _rType );
+ return aRet;
+}
+
+
+Sequence< sal_Int8 > SAL_CALL FormController::getImplementationId()
+{
+ return css::uno::Sequence<sal_Int8>();
+}
+
+Sequence< Type > SAL_CALL FormController::getTypes( )
+{
+ return comphelper::concatSequences(
+ FormController_BASE::getTypes(),
+ ::cppu::OPropertySetHelper::getTypes()
+ );
+}
+
+// XServiceInfo
+sal_Bool SAL_CALL FormController::supportsService(const OUString& ServiceName)
+{
+ return cppu::supportsService(this, ServiceName);
+}
+
+OUString SAL_CALL FormController::getImplementationName()
+{
+ return "org.openoffice.comp.svx.FormController";
+}
+
+Sequence< OUString> SAL_CALL FormController::getSupportedServiceNames()
+{
+ // service names which are supported only, but cannot be used to created an
+ // instance at a service factory
+ Sequence<OUString> aNonCreatableServiceNames { "com.sun.star.form.FormControllerDispatcher" };
+
+ // services which can be used to created an instance at a service factory
+ Sequence< OUString > aCreatableServiceNames( getSupportedServiceNames_Static() );
+ return ::comphelper::concatSequences( aCreatableServiceNames, aNonCreatableServiceNames );
+}
+
+
+sal_Bool SAL_CALL FormController::approveReset(const EventObject& /*rEvent*/)
+{
+ return true;
+}
+
+
+void SAL_CALL FormController::resetted(const EventObject& rEvent)
+{
+ ::osl::MutexGuard aGuard(m_aMutex);
+ if (getCurrentControl().is() && (getCurrentControl()->getModel() == rEvent.Source))
+ m_bModified = false;
+}
+
+
+Sequence< OUString> const & FormController::getSupportedServiceNames_Static()
+{
+ static Sequence< OUString> const aServices
+ {
+ "com.sun.star.form.runtime.FormController",
+ "com.sun.star.awt.control.TabController"
+ };
+ return aServices;
+}
+
+
+namespace
+{
+ struct ResetComponentText
+ {
+ void operator()( const Reference< XTextComponent >& _rxText )
+ {
+ _rxText->setText( OUString() );
+ }
+ };
+
+ struct RemoveComponentTextListener
+ {
+ explicit RemoveComponentTextListener( const Reference< XTextListener >& _rxListener )
+ :m_xListener( _rxListener )
+ {
+ }
+
+ void operator()( const Reference< XTextComponent >& _rxText )
+ {
+ _rxText->removeTextListener( m_xListener );
+ }
+
+ private:
+ Reference< XTextListener > m_xListener;
+ };
+}
+
+
+void FormController::impl_setTextOnAllFilter_throw()
+{
+ m_bSuspendFilterTextListening = true;
+ ::comphelper::FlagGuard aResetFlag( m_bSuspendFilterTextListening );
+
+ // reset the text for all controls
+ ::std::for_each( m_aFilterComponents.begin(), m_aFilterComponents.end(), ResetComponentText() );
+
+ if ( m_aFilterRows.empty() )
+ // nothing to do anymore
+ return;
+
+ if ( m_nCurrentFilterPosition < 0 )
+ return;
+
+ // set the text for all filters
+ OSL_ENSURE( m_aFilterRows.size() > o3tl::make_unsigned(m_nCurrentFilterPosition),
+ "FormController::impl_setTextOnAllFilter_throw: m_nCurrentFilterPosition too big" );
+
+ if ( o3tl::make_unsigned(m_nCurrentFilterPosition) < m_aFilterRows.size() )
+ {
+ FmFilterRow& rRow = m_aFilterRows[ m_nCurrentFilterPosition ];
+ for (const auto& rEntry : rRow)
+ {
+ rEntry.first->setText( rEntry.second );
+ }
+ }
+}
+// OPropertySetHelper
+
+sal_Bool FormController::convertFastPropertyValue( Any & /*rConvertedValue*/, Any & /*rOldValue*/,
+ sal_Int32 /*nHandle*/, const Any& /*rValue*/ )
+{
+ return false;
+}
+
+
+void FormController::setFastPropertyValue_NoBroadcast( sal_Int32 /*nHandle*/, const Any& /*rValue*/ )
+{
+}
+
+
+void FormController::getFastPropertyValue( Any& rValue, sal_Int32 nHandle ) const
+{
+ switch (nHandle)
+ {
+ case FM_ATTR_FILTER:
+ {
+ OUStringBuffer aFilter;
+ Reference<XConnection> xConnection(getConnection(Reference< XRowSet>(m_xModelAsIndex, UNO_QUERY)));
+ if (xConnection.is())
+ {
+ Reference< XNumberFormatsSupplier> xFormatSupplier( getNumberFormats( xConnection, true ) );
+ Reference< XNumberFormatter> xFormatter = NumberFormatter::create(m_xComponentContext);
+ xFormatter->attachNumberFormatsSupplier(xFormatSupplier);
+
+ // now add the filter rows
+ try
+ {
+ for (const FmFilterRow& rRow : m_aFilterRows)
+ {
+ if ( rRow.empty() )
+ continue;
+
+ OUStringBuffer aRowFilter;
+ for ( FmFilterRow::const_iterator condition = rRow.begin(); condition != rRow.end(); ++condition )
+ {
+ // get the field of the controls map
+ Reference< XControl > xControl( condition->first, UNO_QUERY_THROW );
+ Reference< XPropertySet > xModelProps( xControl->getModel(), UNO_QUERY_THROW );
+ Reference< XPropertySet > xField( xModelProps->getPropertyValue( FM_PROP_BOUNDFIELD ), UNO_QUERY_THROW );
+
+ OUString sFilterValue( condition->second );
+
+ OUString sErrorMsg;
+ const std::unique_ptr< OSQLParseNode > pParseNode =
+ predicateTree( sErrorMsg, sFilterValue, xFormatter, xField );
+ OSL_ENSURE( pParseNode != nullptr, "FormController::getFastPropertyValue: could not parse the field value predicate!" );
+ if ( pParseNode != nullptr )
+ {
+ OUString sCriteria;
+ // don't use a parse context here, we need it unlocalized
+ pParseNode->parseNodeToStr( sCriteria, xConnection );
+ if ( condition != rRow.begin() )
+ aRowFilter.append( " AND " );
+ aRowFilter.append( sCriteria );
+ }
+ }
+ if ( !aRowFilter.isEmpty() )
+ {
+ if ( !aFilter.isEmpty() )
+ aFilter.append( " OR " );
+
+ aFilter.append( "( " );
+ aFilter.append( aRowFilter );
+ aFilter.append( " )" );
+ }
+ }
+ }
+ catch( const Exception& )
+ {
+ DBG_UNHANDLED_EXCEPTION("svx");
+ aFilter.setLength(0);
+ }
+ }
+ rValue <<= aFilter.makeStringAndClear();
+ }
+ break;
+
+ case FM_ATTR_FORM_OPERATIONS:
+ rValue <<= m_xFormOperations;
+ break;
+ }
+}
+
+
+Reference< XPropertySetInfo > FormController::getPropertySetInfo()
+{
+ static Reference< XPropertySetInfo > xInfo( createPropertySetInfo( getInfoHelper() ) );
+ return xInfo;
+}
+
+
+void FormController::fillProperties(
+ Sequence< Property >& /* [out] */ _rProps,
+ Sequence< Property >& /* [out] */ /*_rAggregateProps*/
+ ) const
+{
+ _rProps.realloc(2);
+ sal_Int32 nPos = 0;
+ Property* pDesc = _rProps.getArray();
+
+ pDesc[nPos++] = Property(FM_PROP_FILTER, FM_ATTR_FILTER,
+ cppu::UnoType<OUString>::get(),
+ PropertyAttribute::READONLY);
+ pDesc[nPos++] = Property(FM_PROP_FORM_OPERATIONS, FM_ATTR_FORM_OPERATIONS,
+ cppu::UnoType<XFormOperations>::get(),
+ PropertyAttribute::READONLY);
+}
+
+
+::cppu::IPropertyArrayHelper& FormController::getInfoHelper()
+{
+ return *getArrayHelper();
+}
+
+// XFilterController
+
+void SAL_CALL FormController::addFilterControllerListener( const Reference< XFilterControllerListener >& Listener )
+{
+ m_aFilterListeners.addInterface( Listener );
+}
+
+
+void SAL_CALL FormController::removeFilterControllerListener( const Reference< XFilterControllerListener >& Listener )
+{
+ m_aFilterListeners.removeInterface( Listener );
+}
+
+
+::sal_Int32 SAL_CALL FormController::getFilterComponents()
+{
+ ::osl::MutexGuard aGuard( m_aMutex );
+ impl_checkDisposed_throw();
+
+ return m_aFilterComponents.size();
+}
+
+
+::sal_Int32 SAL_CALL FormController::getDisjunctiveTerms()
+{
+ ::osl::MutexGuard aGuard( m_aMutex );
+ impl_checkDisposed_throw();
+
+ return m_aFilterRows.size();
+}
+
+
+void SAL_CALL FormController::setPredicateExpression( ::sal_Int32 Component, ::sal_Int32 Term, const OUString& PredicateExpression )
+{
+ ::osl::MutexGuard aGuard( m_aMutex );
+ impl_checkDisposed_throw();
+
+ if ( ( Component < 0 ) || ( Component >= getFilterComponents() ) || ( Term < 0 ) || ( Term >= getDisjunctiveTerms() ) )
+ throw IndexOutOfBoundsException( OUString(), *this );
+
+ Reference< XTextComponent > xText( m_aFilterComponents[ Component ] );
+ xText->setText( PredicateExpression );
+
+ FmFilterRow& rFilterRow = m_aFilterRows[ Term ];
+ if ( !PredicateExpression.isEmpty() )
+ rFilterRow[ xText ] = PredicateExpression;
+ else
+ rFilterRow.erase( xText );
+}
+
+
+Reference< XControl > FormController::getFilterComponent( ::sal_Int32 Component )
+{
+ ::osl::MutexGuard aGuard( m_aMutex );
+ impl_checkDisposed_throw();
+
+ if ( ( Component < 0 ) || ( Component >= getFilterComponents() ) )
+ throw IndexOutOfBoundsException( OUString(), *this );
+
+ return Reference< XControl >( m_aFilterComponents[ Component ], UNO_QUERY );
+}
+
+
+Sequence< Sequence< OUString > > FormController::getPredicateExpressions()
+{
+ ::osl::MutexGuard aGuard( m_aMutex );
+ impl_checkDisposed_throw();
+
+ Sequence< Sequence< OUString > > aExpressions( m_aFilterRows.size() );
+ auto aExpressionsRange = asNonConstRange(aExpressions);
+ sal_Int32 termIndex = 0;
+ for (const FmFilterRow& rRow : m_aFilterRows)
+ {
+ Sequence< OUString > aConjunction( m_aFilterComponents.size() );
+ auto aConjunctionRange = asNonConstRange(aConjunction);
+ sal_Int32 componentIndex = 0;
+ for (const auto& rComp : m_aFilterComponents)
+ {
+ FmFilterRow::const_iterator predicate = rRow.find( rComp );
+ if ( predicate != rRow.end() )
+ aConjunctionRange[ componentIndex ] = predicate->second;
+ ++componentIndex;
+ }
+
+ aExpressionsRange[ termIndex ] = aConjunction;
+ ++termIndex;
+ }
+
+ return aExpressions;
+}
+
+
+void SAL_CALL FormController::removeDisjunctiveTerm( ::sal_Int32 Term )
+{
+ // SYNCHRONIZED -->
+ ::osl::ClearableMutexGuard aGuard( m_aMutex );
+ impl_checkDisposed_throw();
+
+ if ( ( Term < 0 ) || ( Term >= getDisjunctiveTerms() ) )
+ throw IndexOutOfBoundsException( OUString(), *this );
+
+ // if the to-be-deleted row is our current row, we need to shift
+ if ( Term == m_nCurrentFilterPosition )
+ {
+ if ( m_nCurrentFilterPosition < sal_Int32( m_aFilterRows.size() - 1 ) )
+ ++m_nCurrentFilterPosition;
+ else
+ --m_nCurrentFilterPosition;
+ }
+
+ FmFilterRows::iterator pos = m_aFilterRows.begin() + Term;
+ m_aFilterRows.erase( pos );
+
+ // adjust m_nCurrentFilterPosition if the removed row preceded it
+ if ( Term < m_nCurrentFilterPosition )
+ --m_nCurrentFilterPosition;
+
+ SAL_WARN_IF( !( ( m_nCurrentFilterPosition < 0 ) != ( m_aFilterRows.empty() ) ),
+ "svx.form", "FormController::removeDisjunctiveTerm: inconsistency!" );
+
+ // update the texts in the filter controls
+ impl_setTextOnAllFilter_throw();
+
+ FilterEvent aEvent;
+ aEvent.Source = *this;
+ aEvent.DisjunctiveTerm = Term;
+ aGuard.clear();
+ // <-- SYNCHRONIZED
+
+ m_aFilterListeners.notifyEach( &XFilterControllerListener::disjunctiveTermRemoved, aEvent );
+}
+
+
+void SAL_CALL FormController::appendEmptyDisjunctiveTerm()
+{
+ // SYNCHRONIZED -->
+ ::osl::ClearableMutexGuard aGuard( m_aMutex );
+ impl_checkDisposed_throw();
+
+ impl_appendEmptyFilterRow( aGuard );
+ // <-- SYNCHRONIZED
+}
+
+
+::sal_Int32 SAL_CALL FormController::getActiveTerm()
+{
+ ::osl::MutexGuard aGuard( m_aMutex );
+ impl_checkDisposed_throw();
+
+ return m_nCurrentFilterPosition;
+}
+
+
+void SAL_CALL FormController::setActiveTerm( ::sal_Int32 ActiveTerm )
+{
+ ::osl::MutexGuard aGuard( m_aMutex );
+ impl_checkDisposed_throw();
+
+ if ( ( ActiveTerm < 0 ) || ( ActiveTerm >= getDisjunctiveTerms() ) )
+ throw IndexOutOfBoundsException( OUString(), *this );
+
+ if ( ActiveTerm == getActiveTerm() )
+ return;
+
+ m_nCurrentFilterPosition = ActiveTerm;
+ impl_setTextOnAllFilter_throw();
+}
+
+// XElementAccess
+
+sal_Bool SAL_CALL FormController::hasElements()
+{
+ ::osl::MutexGuard aGuard( m_aMutex );
+ return !m_aChildren.empty();
+}
+
+
+Type SAL_CALL FormController::getElementType()
+{
+ return cppu::UnoType<XFormController>::get();
+
+}
+
+// XEnumerationAccess
+
+Reference< XEnumeration > SAL_CALL FormController::createEnumeration()
+{
+ ::osl::MutexGuard aGuard( m_aMutex );
+ return new ::comphelper::OEnumerationByIndex(this);
+}
+
+// XIndexAccess
+
+sal_Int32 SAL_CALL FormController::getCount()
+{
+ ::osl::MutexGuard aGuard( m_aMutex );
+ return m_aChildren.size();
+}
+
+
+Any SAL_CALL FormController::getByIndex(sal_Int32 Index)
+{
+ ::osl::MutexGuard aGuard( m_aMutex );
+ if (Index < 0 ||
+ o3tl::make_unsigned(Index) >= m_aChildren.size())
+ throw IndexOutOfBoundsException();
+
+ return Any( m_aChildren[ Index ] );
+}
+
+// EventListener
+
+void SAL_CALL FormController::disposing(const EventObject& e)
+{
+ // has the container been disposed
+ ::osl::MutexGuard aGuard( m_aMutex );
+ Reference< XControlContainer > xContainer(e.Source, UNO_QUERY);
+ if (xContainer.is())
+ {
+ setContainer(Reference< XControlContainer > ());
+ }
+ else
+ {
+ // has a control been disposed
+ Reference< XControl > xControl(e.Source, UNO_QUERY);
+ if (xControl.is())
+ {
+ if (getContainer().is())
+ removeControl(xControl);
+ }
+ }
+}
+
+// OComponentHelper
+
+void FormController::disposeAllFeaturesAndDispatchers()
+{
+ for (auto& rDispatcher : m_aFeatureDispatchers)
+ {
+ try
+ {
+ ::comphelper::disposeComponent( rDispatcher.second );
+ }
+ catch( const Exception& )
+ {
+ DBG_UNHANDLED_EXCEPTION("svx");
+ }
+ }
+ m_aFeatureDispatchers.clear();
+}
+
+
+void FormController::disposing()
+{
+ EventObject aEvt( *this );
+
+ // if we're still active, simulate a "deactivated" event
+ if ( m_xActiveControl.is() )
+ m_aActivateListeners.notifyEach( &XFormControllerListener::formDeactivated, aEvt );
+
+ // notify all our listeners
+ m_aActivateListeners.disposeAndClear(aEvt);
+ m_aModifyListeners.disposeAndClear(aEvt);
+ m_aErrorListeners.disposeAndClear(aEvt);
+ m_aDeleteListeners.disposeAndClear(aEvt);
+ m_aRowSetApproveListeners.disposeAndClear(aEvt);
+ m_aParameterListeners.disposeAndClear(aEvt);
+ m_aFilterListeners.disposeAndClear(aEvt);
+
+ removeBoundFieldListener();
+ stopFiltering();
+
+ m_aControlBorderManager.restoreAll();
+
+ m_aFilterRows.clear();
+
+ ::osl::MutexGuard aGuard( m_aMutex );
+ m_xActiveControl = nullptr;
+ implSetCurrentControl( nullptr );
+
+ // clean up our children
+ for (const auto& rpChild : m_aChildren)
+ {
+ // search the position of the model within the form
+ Reference< XFormComponent > xForm(rpChild->getModel(), UNO_QUERY);
+ sal_uInt32 nPos = m_xModelAsIndex->getCount();
+ Reference< XFormComponent > xTemp;
+ for( ; nPos; )
+ {
+
+ m_xModelAsIndex->getByIndex( --nPos ) >>= xTemp;
+ if ( xForm.get() == xTemp.get() )
+ {
+ Reference< XInterface > xIfc( rpChild, UNO_QUERY );
+ m_xModelAsManager->detach( nPos, xIfc );
+ break;
+ }
+ }
+
+ Reference< XComponent > (rpChild, UNO_QUERY_THROW)->dispose();
+ }
+ m_aChildren.clear();
+
+ disposeAllFeaturesAndDispatchers();
+
+ if ( m_xFormOperations.is() )
+ m_xFormOperations->dispose();
+ m_xFormOperations.clear();
+
+ if (m_bDBConnection)
+ unload();
+
+ setContainer( nullptr );
+ setModel( nullptr );
+ setParent( nullptr );
+
+ ::comphelper::disposeComponent( m_xComposer );
+
+ m_bDBConnection = false;
+}
+
+
+namespace
+{
+ bool lcl_shouldUseDynamicControlBorder( const Reference< XInterface >& _rxForm, const Any& _rDynamicColorProp )
+ {
+ bool bDoUse = false;
+ if ( !( _rDynamicColorProp >>= bDoUse ) )
+ {
+ DocumentType eDocType = DocumentClassification::classifyHostDocument( _rxForm );
+ return ControlLayouter::useDynamicBorderColor( eDocType );
+ }
+ return bDoUse;
+ }
+}
+
+
+void SAL_CALL FormController::propertyChange(const PropertyChangeEvent& evt)
+{
+ OSL_ENSURE( !impl_isDisposed_nofail(), "FormController: already disposed!" );
+ if ( evt.PropertyName == FM_PROP_BOUNDFIELD )
+ {
+ Reference<XPropertySet> xOldBound;
+ evt.OldValue >>= xOldBound;
+ if ( !xOldBound.is() && evt.NewValue.hasValue() )
+ {
+ Reference< XControlModel > xControlModel(evt.Source,UNO_QUERY);
+ Reference< XControl > xControl = findControl(m_aControls,xControlModel,false,false);
+ if ( xControl.is() )
+ {
+ startControlModifyListening( xControl );
+ Reference<XPropertySet> xProp(xControlModel,UNO_QUERY);
+ if ( xProp.is() )
+ xProp->removePropertyChangeListener(FM_PROP_BOUNDFIELD, this);
+ }
+ }
+ }
+ else
+ {
+ bool bModifiedChanged = (evt.PropertyName == FM_PROP_ISMODIFIED);
+ bool bNewChanged = (evt.PropertyName == FM_PROP_ISNEW);
+ if (bModifiedChanged || bNewChanged)
+ {
+ ::osl::MutexGuard aGuard( m_aMutex );
+ if (bModifiedChanged)
+ m_bCurrentRecordModified = ::comphelper::getBOOL(evt.NewValue);
+ else
+ m_bCurrentRecordNew = ::comphelper::getBOOL(evt.NewValue);
+
+ // toggle the locking
+ if (m_bLocked != determineLockState())
+ {
+ m_bLocked = !m_bLocked;
+ setLocks();
+ if (isListeningForChanges())
+ startListening();
+ else
+ stopListening();
+ }
+
+ if ( bNewChanged )
+ m_aToggleEvent.Call();
+
+ if (!m_bCurrentRecordModified)
+ m_bModified = false;
+ }
+ else if ( evt.PropertyName == FM_PROP_DYNAMIC_CONTROL_BORDER )
+ {
+ bool bEnable = lcl_shouldUseDynamicControlBorder( evt.Source, evt.NewValue );
+ if ( bEnable )
+ {
+ m_aControlBorderManager.enableDynamicBorderColor();
+ if ( m_xActiveControl.is() )
+ m_aControlBorderManager.focusGained( m_xActiveControl );
+ }
+ else
+ {
+ m_aControlBorderManager.disableDynamicBorderColor();
+ }
+ }
+ }
+}
+
+
+bool FormController::replaceControl( const Reference< XControl >& _rxExistentControl, const Reference< XControl >& _rxNewControl )
+{
+ bool bSuccess = false;
+ try
+ {
+ Reference< XIdentifierReplace > xContainer( getContainer(), UNO_QUERY );
+ DBG_ASSERT( xContainer.is(), "FormController::replaceControl: yes, it's not required by the service description, but XIdentifierReplace would be nice!" );
+ if ( xContainer.is() )
+ {
+ // look up the ID of _rxExistentControl
+ const Sequence< sal_Int32 > aIdentifiers( xContainer->getIdentifiers() );
+ const sal_Int32* pIdentifiers = std::find_if(aIdentifiers.begin(), aIdentifiers.end(),
+ [&xContainer, &_rxExistentControl](const sal_Int32 nId) {
+ Reference< XControl > xCheck( xContainer->getByIdentifier( nId ), UNO_QUERY );
+ return xCheck == _rxExistentControl;
+ });
+ DBG_ASSERT( pIdentifiers != aIdentifiers.end(), "FormController::replaceControl: did not find the control in the container!" );
+ if ( pIdentifiers != aIdentifiers.end() )
+ {
+ bool bReplacedWasActive = ( m_xActiveControl.get() == _rxExistentControl.get() );
+ bool bReplacedWasCurrent = ( m_xCurrentControl.get() == _rxExistentControl.get() );
+
+ if ( bReplacedWasActive )
+ {
+ m_xActiveControl = nullptr;
+ implSetCurrentControl( nullptr );
+ }
+ else if ( bReplacedWasCurrent )
+ {
+ implSetCurrentControl( _rxNewControl );
+ }
+
+ // carry over the model
+ _rxNewControl->setModel( _rxExistentControl->getModel() );
+
+ xContainer->replaceByIdentifer( *pIdentifiers, Any( _rxNewControl ) );
+ bSuccess = true;
+
+ if ( bReplacedWasActive )
+ {
+ Reference< XWindow > xControlWindow( _rxNewControl, UNO_QUERY );
+ if ( xControlWindow.is() )
+ xControlWindow->setFocus();
+ }
+ }
+ }
+ }
+ catch( const Exception& )
+ {
+ DBG_UNHANDLED_EXCEPTION("svx");
+ }
+
+ Reference< XControl > xDisposeIt( bSuccess ? _rxExistentControl : _rxNewControl );
+ ::comphelper::disposeComponent( xDisposeIt );
+ return bSuccess;
+}
+
+
+void FormController::toggleAutoFields(bool bAutoFields)
+{
+ OSL_ENSURE( !impl_isDisposed_nofail(), "FormController: already disposed!" );
+
+
+ Sequence< Reference< XControl > > aControlsCopy( m_aControls );
+ const Reference< XControl >* pControls = aControlsCopy.getConstArray();
+ sal_Int32 nControls = aControlsCopy.getLength();
+
+ if (bAutoFields)
+ {
+ // as we don't want new controls to be attached to the scripting environment
+ // we change attach flags
+ m_bAttachEvents = false;
+ for (sal_Int32 i = nControls; i > 0;)
+ {
+ Reference< XControl > xControl = pControls[--i];
+ if (xControl.is())
+ {
+ Reference< XPropertySet > xSet(xControl->getModel(), UNO_QUERY);
+ if (xSet.is() && ::comphelper::hasProperty(FM_PROP_BOUNDFIELD, xSet))
+ {
+ // does the model use a bound field ?
+ Reference< XPropertySet > xField;
+ xSet->getPropertyValue(FM_PROP_BOUNDFIELD) >>= xField;
+
+ // is it an autofield?
+ if ( xField.is()
+ && ::comphelper::hasProperty( FM_PROP_AUTOINCREMENT, xField )
+ && ::comphelper::getBOOL( xField->getPropertyValue( FM_PROP_AUTOINCREMENT ) )
+ )
+ {
+ replaceControl( xControl, new FmXAutoControl() );
+ }
+ }
+ }
+ }
+ m_bAttachEvents = true;
+ }
+ else
+ {
+ m_bDetachEvents = false;
+ for (sal_Int32 i = nControls; i > 0;)
+ {
+ Reference< XControl > xControl = pControls[--i];
+ if (xControl.is())
+ {
+ Reference< XPropertySet > xSet(xControl->getModel(), UNO_QUERY);
+ if (xSet.is() && ::comphelper::hasProperty(FM_PROP_BOUNDFIELD, xSet))
+ {
+ // does the model use a bound field ?
+ Reference< XPropertySet > xField;
+ xSet->getPropertyValue(FM_PROP_BOUNDFIELD) >>= xField;
+
+ // is it an autofield?
+ if ( xField.is()
+ && ::comphelper::hasProperty( FM_PROP_AUTOINCREMENT, xField )
+ && ::comphelper::getBOOL( xField->getPropertyValue(FM_PROP_AUTOINCREMENT ) )
+ )
+ {
+ OUString sServiceName;
+ OSL_VERIFY( xSet->getPropertyValue( FM_PROP_DEFAULTCONTROL ) >>= sServiceName );
+ Reference< XControl > xNewControl( m_xComponentContext->getServiceManager()->createInstanceWithContext( sServiceName, m_xComponentContext ), UNO_QUERY );
+ replaceControl( xControl, xNewControl );
+ }
+ }
+ }
+ }
+ m_bDetachEvents = true;
+ }
+}
+
+
+IMPL_LINK_NOARG(FormController, OnToggleAutoFields, void*, void)
+{
+ OSL_ENSURE( !impl_isDisposed_nofail(), "FormController: already disposed!" );
+
+ toggleAutoFields(m_bCurrentRecordNew);
+}
+
+// XTextListener
+void SAL_CALL FormController::textChanged(const TextEvent& e)
+{
+ // SYNCHRONIZED -->
+ ::osl::ClearableMutexGuard aGuard( m_aMutex );
+ OSL_ENSURE( !impl_isDisposed_nofail(), "FormController: already disposed!" );
+ if ( !m_bFiltering )
+ {
+ impl_onModify();
+ return;
+ }
+
+ if ( m_bSuspendFilterTextListening )
+ return;
+
+ Reference< XTextComponent > xText(e.Source,UNO_QUERY);
+ OUString aText = xText->getText();
+
+ if ( m_aFilterRows.empty() )
+ appendEmptyDisjunctiveTerm();
+
+ // find the current row
+ if ( ( m_nCurrentFilterPosition < 0 ) || ( o3tl::make_unsigned(m_nCurrentFilterPosition) >= m_aFilterRows.size() ) )
+ {
+ OSL_ENSURE( false, "FormController::textChanged: m_nCurrentFilterPosition is wrong!" );
+ return;
+ }
+
+ FmFilterRow& rRow = m_aFilterRows[ m_nCurrentFilterPosition ];
+
+ // do we have a new filter
+ if (!aText.isEmpty())
+ rRow[xText] = aText;
+ else
+ {
+ // do we have the control in the row
+ FmFilterRow::iterator iter = rRow.find(xText);
+ // erase the entry out of the row
+ if (iter != rRow.end())
+ rRow.erase(iter);
+ }
+
+ // multiplex the event to our FilterControllerListeners
+ FilterEvent aEvent;
+ aEvent.Source = *this;
+ aEvent.FilterComponent = ::std::find( m_aFilterComponents.begin(), m_aFilterComponents.end(), xText ) - m_aFilterComponents.begin();
+ aEvent.DisjunctiveTerm = getActiveTerm();
+ aEvent.PredicateExpression = aText;
+
+ aGuard.clear();
+ // <-- SYNCHRONIZED
+
+ // notify the changed filter expression
+ m_aFilterListeners.notifyEach( &XFilterControllerListener::predicateExpressionChanged, aEvent );
+}
+
+// XItemListener
+void SAL_CALL FormController::itemStateChanged(const ItemEvent& /*rEvent*/)
+{
+ OSL_ENSURE( !impl_isDisposed_nofail(), "FormController: already disposed!" );
+ impl_onModify();
+}
+
+// XModificationBroadcaster
+void SAL_CALL FormController::addModifyListener(const Reference< XModifyListener > & l)
+{
+ ::osl::MutexGuard aGuard( m_aMutex );
+ impl_checkDisposed_throw();
+ m_aModifyListeners.addInterface( l );
+}
+
+void FormController::removeModifyListener(const Reference< XModifyListener > & l)
+{
+ ::osl::MutexGuard aGuard( m_aMutex );
+ impl_checkDisposed_throw();
+ m_aModifyListeners.removeInterface( l );
+}
+
+// XModificationListener
+void FormController::modified( const EventObject& _rEvent )
+{
+ OSL_ENSURE( !impl_isDisposed_nofail(), "FormController: already disposed!" );
+
+ try
+ {
+ if ( _rEvent.Source != m_xActiveControl )
+ { // let this control grab the focus
+ // (this case may happen if somebody moves the scroll wheel of the mouse over a control
+ // which does not have the focus)
+ // 85511 - 29.05.2001 - frank.schoenheit@germany.sun.com
+
+ // also, it happens when an image control gets a new image by double-clicking it
+ // #i88458# / 2009-01-12 / frank.schoenheit@sun.com
+ Reference< XWindow > xControlWindow( _rEvent.Source, UNO_QUERY_THROW );
+ xControlWindow->setFocus();
+ }
+ }
+ catch( const Exception& )
+ {
+ DBG_UNHANDLED_EXCEPTION("svx");
+ }
+
+ impl_onModify();
+}
+
+void FormController::impl_checkDisposed_throw() const
+{
+ if ( impl_isDisposed_nofail() )
+ throw DisposedException( OUString(), *const_cast< FormController* >( this ) );
+}
+
+void FormController::impl_onModify()
+{
+ OSL_ENSURE( !impl_isDisposed_nofail(), "FormController: already disposed!" );
+
+ {
+ ::osl::MutexGuard aGuard( m_aMutex );
+ if ( !m_bModified )
+ m_bModified = true;
+ }
+
+ EventObject aEvt(static_cast<cppu::OWeakObject*>(this));
+ m_aModifyListeners.notifyEach( &XModifyListener::modified, aEvt );
+}
+
+void FormController::impl_addFilterRow( const FmFilterRow& _row )
+{
+ m_aFilterRows.push_back( _row );
+
+ if ( m_aFilterRows.size() == 1 )
+ { // that's the first row ever
+ OSL_ENSURE( m_nCurrentFilterPosition == -1, "FormController::impl_addFilterRow: inconsistency!" );
+ m_nCurrentFilterPosition = 0;
+ }
+}
+
+void FormController::impl_appendEmptyFilterRow( ::osl::ClearableMutexGuard& _rClearBeforeNotify )
+{
+ // SYNCHRONIZED -->
+ impl_addFilterRow( FmFilterRow() );
+
+ // notify the listeners
+ FilterEvent aEvent;
+ aEvent.Source = *this;
+ aEvent.DisjunctiveTerm = static_cast<sal_Int32>(m_aFilterRows.size()) - 1;
+ _rClearBeforeNotify.clear();
+ // <-- SYNCHRONIZED
+ m_aFilterListeners.notifyEach( &XFilterControllerListener::disjunctiveTermAdded, aEvent );
+}
+
+bool FormController::determineLockState() const
+{
+ OSL_ENSURE( !impl_isDisposed_nofail(), "FormController: already disposed!" );
+ // a.) in filter mode we are always locked
+ // b.) if we have no valid model or our model (a result set) is not alive -> we're locked
+ // c.) if we are inserting everything is OK and we are not locked
+ // d.) if are not updatable or on invalid position
+ Reference< XResultSet > xResultSet(m_xModelAsIndex, UNO_QUERY);
+ if (m_bFiltering || !xResultSet.is() || !isRowSetAlive(xResultSet))
+ return true;
+ else
+ return !(m_bCanInsert && m_bCurrentRecordNew)
+ && (xResultSet->isBeforeFirst() || xResultSet->isAfterLast() || xResultSet->rowDeleted() || !m_bCanUpdate);
+}
+
+// FocusListener
+void FormController::focusGained(const FocusEvent& e)
+{
+ // SYNCHRONIZED -->
+ ::osl::ClearableMutexGuard aGuard( m_aMutex );
+ impl_checkDisposed_throw();
+
+ m_aControlBorderManager.focusGained( e.Source );
+
+ Reference< XControl > xControl(e.Source, UNO_QUERY);
+ if (m_bDBConnection)
+ {
+ // do we need to keep the locking of the commit
+ // we hold the lock as long as the control differs from the current
+ // otherwise we disabled the lock
+ m_bCommitLock = m_bCommitLock && xControl.get() != m_xCurrentControl.get();
+ if (m_bCommitLock)
+ return;
+
+ // when do we have to commit a value to form or a filter
+ // a.) if the current value is modified
+ // b.) there must be a current control
+ // c.) and it must be different from the new focus owning control or
+ // d.) the focus is moving around (so we have only one control)
+
+ if ( ( m_bModified || m_bFiltering )
+ && m_xCurrentControl.is()
+ && ( ( xControl.get() != m_xCurrentControl.get() )
+ || ( ( e.FocusFlags & FocusChangeReason::AROUND )
+ && ( m_bCycle || m_bFiltering )
+ )
+ )
+ )
+ {
+ // check the old control if the content is ok
+#if OSL_DEBUG_LEVEL > 0 && !defined NDEBUG
+ Reference< XBoundControl > xLockingTest(m_xCurrentControl, UNO_QUERY);
+ bool bControlIsLocked = xLockingTest.is() && xLockingTest->getLock();
+ assert(!bControlIsLocked && "FormController::Gained: I'm modified and the current control is locked ? How this ?");
+ // normally, a locked control should not be modified, so probably my bModified must
+ // have been set from a different context, which I would not understand ...
+#endif
+ DBG_ASSERT(m_xCurrentControl.is(), "no CurrentControl set");
+ // first the control ask if it supports the IFace
+ Reference< XBoundComponent > xBound(m_xCurrentControl, UNO_QUERY);
+ if (!xBound.is() && m_xCurrentControl.is())
+ xBound.set(m_xCurrentControl->getModel(), UNO_QUERY);
+
+ // lock if we lose the focus during commit
+ m_bCommitLock = true;
+
+ // commit unsuccessful, reset focus
+ if (xBound.is() && !xBound->commit())
+ {
+ // the commit failed and we don't commit again until the current control
+ // which couldn't be commit gains the focus again
+ Reference< XWindow > xWindow(m_xCurrentControl, UNO_QUERY);
+ if (xWindow.is())
+ xWindow->setFocus();
+ return;
+ }
+ else
+ {
+ m_bModified = false;
+ m_bCommitLock = false;
+ }
+ }
+
+ if (!m_bFiltering && m_bCycle && (e.FocusFlags & FocusChangeReason::AROUND) && m_xCurrentControl.is())
+ {
+ OSL_ENSURE( m_xFormOperations.is(), "FormController::focusGained: hmm?" );
+ // should have been created in setModel
+ try
+ {
+ if ( e.FocusFlags & FocusChangeReason::FORWARD )
+ {
+ if ( m_xFormOperations.is() && m_xFormOperations->isEnabled( FormFeature::MoveToNext ) )
+ m_xFormOperations->execute( FormFeature::MoveToNext );
+ }
+ else // backward
+ {
+ if ( m_xFormOperations.is() && m_xFormOperations->isEnabled( FormFeature::MoveToPrevious ) )
+ m_xFormOperations->execute( FormFeature::MoveToPrevious );
+ }
+ }
+ catch ( const Exception& )
+ {
+ // don't handle this any further. That's an ... admissible error.
+ DBG_UNHANDLED_EXCEPTION("svx");
+ }
+ }
+ }
+
+ // still one and the same control
+ if ( ( m_xActiveControl == xControl )
+ && ( xControl == m_xCurrentControl )
+ )
+ {
+ DBG_ASSERT(m_xCurrentControl.is(), "No CurrentControl selected");
+ return;
+ }
+
+ bool bActivated = !m_xActiveControl.is() && xControl.is();
+
+ m_xActiveControl = xControl;
+
+ implSetCurrentControl( xControl );
+ SAL_WARN_IF( !m_xCurrentControl.is(), "svx.form", "implSetCurrentControl did nonsense!" );
+
+ if ( bActivated )
+ {
+ // (asynchronously) call activation handlers
+ m_aActivationEvent.Call();
+
+ // call modify listeners
+ if ( m_bModified )
+ m_aModifyListeners.notifyEach( &XModifyListener::modified, EventObject( *this ) );
+ }
+
+ // invalidate all features which depend on the currently focused control
+ if ( m_bDBConnection && !m_bFiltering )
+ implInvalidateCurrentControlDependentFeatures();
+
+ if ( !m_xCurrentControl.is() )
+ return;
+
+ // control gets focus, then possibly in the visible range
+ Reference< XFormControllerContext > xContext( m_xFormControllerContext );
+ Reference< XControl > xCurrentControl( m_xCurrentControl );
+ aGuard.clear();
+ // <-- SYNCHRONIZED
+
+ if ( xContext.is() )
+ xContext->makeVisible( xCurrentControl );
+}
+
+IMPL_LINK_NOARG( FormController, OnActivated, void*, void )
+{
+ EventObject aEvent;
+ aEvent.Source = *this;
+ m_aActivateListeners.notifyEach( &XFormControllerListener::formActivated, aEvent );
+}
+
+IMPL_LINK_NOARG( FormController, OnDeactivated, void*, void )
+{
+ EventObject aEvent;
+ aEvent.Source = *this;
+ m_aActivateListeners.notifyEach( &XFormControllerListener::formDeactivated, aEvent );
+}
+
+void FormController::focusLost(const FocusEvent& e)
+{
+ OSL_ENSURE( !impl_isDisposed_nofail(), "FormController: already disposed!" );
+
+ m_aControlBorderManager.focusLost( e.Source );
+
+ Reference< XWindowPeer > xNext(e.NextFocus, UNO_QUERY);
+ // if focus hasn't passed to some other window, e.g. focus in a welded item, don't deactivate
+ if (!xNext)
+ return;
+ Reference< XControl > xNextControl = isInList(xNext);
+ if (!xNextControl.is())
+ {
+ m_xActiveControl = nullptr;
+ m_aDeactivationEvent.Call();
+ }
+}
+
+void SAL_CALL FormController::mousePressed( const awt::MouseEvent& /*_rEvent*/ )
+{
+ // not interested in
+}
+
+void SAL_CALL FormController::mouseReleased( const awt::MouseEvent& /*_rEvent*/ )
+{
+ // not interested in
+}
+
+void SAL_CALL FormController::mouseEntered( const awt::MouseEvent& _rEvent )
+{
+ m_aControlBorderManager.mouseEntered( _rEvent.Source );
+}
+
+void SAL_CALL FormController::mouseExited( const awt::MouseEvent& _rEvent )
+{
+ m_aControlBorderManager.mouseExited( _rEvent.Source );
+}
+
+void SAL_CALL FormController::componentValidityChanged( const EventObject& _rSource )
+{
+ Reference< XControl > xControl( findControl( m_aControls, Reference< XControlModel >( _rSource.Source, UNO_QUERY ), false, false ) );
+ Reference< XValidatableFormComponent > xValidatable( _rSource.Source, UNO_QUERY );
+
+ OSL_ENSURE( xControl.is() && xValidatable.is(), "FormController::componentValidityChanged: huh?" );
+
+ if ( xControl.is() && xValidatable.is() )
+ m_aControlBorderManager.validityChanged( xControl, xValidatable );
+}
+
+
+void FormController::setModel(const Reference< XTabControllerModel > & Model)
+{
+ ::osl::MutexGuard aGuard( m_aMutex );
+ impl_checkDisposed_throw();
+
+ DBG_ASSERT(m_xTabController.is(), "FormController::setModel : invalid aggregate !");
+
+ try
+ {
+ // disconnect from the old model
+ if (m_xModelAsIndex.is())
+ {
+ if (m_bDBConnection)
+ {
+ // we are currently working on the model
+ EventObject aEvt(m_xModelAsIndex);
+ unloaded(aEvt);
+ }
+
+ Reference< XLoadable > xForm(m_xModelAsIndex, UNO_QUERY);
+ if (xForm.is())
+ xForm->removeLoadListener(this);
+
+ Reference< XSQLErrorBroadcaster > xBroadcaster(m_xModelAsIndex, UNO_QUERY);
+ if (xBroadcaster.is())
+ xBroadcaster->removeSQLErrorListener(this);
+
+ Reference< XDatabaseParameterBroadcaster > xParamBroadcaster(m_xModelAsIndex, UNO_QUERY);
+ if (xParamBroadcaster.is())
+ xParamBroadcaster->removeParameterListener(this);
+
+ }
+
+ disposeAllFeaturesAndDispatchers();
+
+ if ( m_xFormOperations.is() )
+ m_xFormOperations->dispose();
+ m_xFormOperations.clear();
+
+ // set the new model wait for the load event
+ if (m_xTabController.is())
+ m_xTabController->setModel(Model);
+ m_xModelAsIndex.set(Model, UNO_QUERY);
+ m_xModelAsManager.set(Model, UNO_QUERY);
+
+ // only if both ifaces exit, the controller will work successful
+ if (!m_xModelAsIndex.is() || !m_xModelAsManager.is())
+ {
+ m_xModelAsManager = nullptr;
+ m_xModelAsIndex = nullptr;
+ }
+
+ if (m_xModelAsIndex.is())
+ {
+ // re-create m_xFormOperations
+ m_xFormOperations = FormOperations::createWithFormController( m_xComponentContext, this );
+ m_xFormOperations->setFeatureInvalidation( this );
+
+ // adding load and ui interaction listeners
+ Reference< XLoadable > xForm(Model, UNO_QUERY);
+ if (xForm.is())
+ xForm->addLoadListener(this);
+
+ Reference< XSQLErrorBroadcaster > xBroadcaster(Model, UNO_QUERY);
+ if (xBroadcaster.is())
+ xBroadcaster->addSQLErrorListener(this);
+
+ Reference< XDatabaseParameterBroadcaster > xParamBroadcaster(Model, UNO_QUERY);
+ if (xParamBroadcaster.is())
+ xParamBroadcaster->addParameterListener(this);
+
+ // well, is the database already loaded?
+ // then we have to simulate a load event
+ Reference< XLoadable > xCursor(m_xModelAsIndex, UNO_QUERY);
+ if (xCursor.is() && xCursor->isLoaded())
+ {
+ EventObject aEvt(xCursor);
+ loaded(aEvt);
+ }
+
+ Reference< XPropertySet > xModelProps( m_xModelAsIndex, UNO_QUERY );
+ Reference< XPropertySetInfo > xPropInfo( xModelProps->getPropertySetInfo() );
+ if ( xPropInfo.is()
+ && xPropInfo->hasPropertyByName( FM_PROP_DYNAMIC_CONTROL_BORDER )
+ && xPropInfo->hasPropertyByName( FM_PROP_CONTROL_BORDER_COLOR_FOCUS )
+ && xPropInfo->hasPropertyByName( FM_PROP_CONTROL_BORDER_COLOR_MOUSE )
+ && xPropInfo->hasPropertyByName( FM_PROP_CONTROL_BORDER_COLOR_INVALID )
+ )
+ {
+ bool bEnableDynamicControlBorder = lcl_shouldUseDynamicControlBorder(
+ xModelProps, xModelProps->getPropertyValue( FM_PROP_DYNAMIC_CONTROL_BORDER ) );
+ if ( bEnableDynamicControlBorder )
+ m_aControlBorderManager.enableDynamicBorderColor();
+ else
+ m_aControlBorderManager.disableDynamicBorderColor();
+
+ Color nColor;
+ if ( xModelProps->getPropertyValue( FM_PROP_CONTROL_BORDER_COLOR_FOCUS ) >>= nColor )
+ m_aControlBorderManager.setStatusColor( ControlStatus::Focused, nColor );
+ if ( xModelProps->getPropertyValue( FM_PROP_CONTROL_BORDER_COLOR_MOUSE ) >>= nColor )
+ m_aControlBorderManager.setStatusColor( ControlStatus::MouseHover, nColor );
+ if ( xModelProps->getPropertyValue( FM_PROP_CONTROL_BORDER_COLOR_INVALID ) >>= nColor )
+ m_aControlBorderManager.setStatusColor( ControlStatus::Invalid, nColor );
+ }
+ }
+ }
+ catch( const Exception& )
+ {
+ DBG_UNHANDLED_EXCEPTION("svx");
+ }
+}
+
+
+Reference< XTabControllerModel > FormController::getModel()
+{
+ ::osl::MutexGuard aGuard( m_aMutex );
+ impl_checkDisposed_throw();
+
+ DBG_ASSERT(m_xTabController.is(), "FormController::getModel : invalid aggregate !");
+ if (!m_xTabController.is())
+ return Reference< XTabControllerModel > ();
+ return m_xTabController->getModel();
+}
+
+
+void FormController::addToEventAttacher(const Reference< XControl > & xControl)
+{
+ OSL_ENSURE( !impl_isDisposed_nofail(), "FormController: already disposed!" );
+ OSL_ENSURE( xControl.is(), "FormController::addToEventAttacher: invalid control - how did you reach this?" );
+ if ( !xControl.is() )
+ return; /* throw IllegalArgumentException(); */
+
+ // register at the event attacher
+ Reference< XFormComponent > xComp(xControl->getModel(), UNO_QUERY);
+ if (!(xComp.is() && m_xModelAsIndex.is()))
+ return;
+
+ // and look for the position of the ControlModel in it
+ sal_uInt32 nPos = m_xModelAsIndex->getCount();
+ Reference< XFormComponent > xTemp;
+ for( ; nPos; )
+ {
+ m_xModelAsIndex->getByIndex(--nPos) >>= xTemp;
+ if (xComp.get() == xTemp.get())
+ {
+ m_xModelAsManager->attach( nPos, Reference<XInterface>( xControl, UNO_QUERY ), Any(xControl) );
+ break;
+ }
+ }
+}
+
+
+void FormController::removeFromEventAttacher(const Reference< XControl > & xControl)
+{
+ OSL_ENSURE( !impl_isDisposed_nofail(), "FormController: already disposed!" );
+ OSL_ENSURE( xControl.is(), "FormController::removeFromEventAttacher: invalid control - how did you reach this?" );
+ if ( !xControl.is() )
+ return; /* throw IllegalArgumentException(); */
+
+ // register at the event attacher
+ Reference< XFormComponent > xComp(xControl->getModel(), UNO_QUERY);
+ if ( !(xComp.is() && m_xModelAsIndex.is()) )
+ return;
+
+ // and look for the position of the ControlModel in it
+ sal_uInt32 nPos = m_xModelAsIndex->getCount();
+ Reference< XFormComponent > xTemp;
+ for( ; nPos; )
+ {
+ m_xModelAsIndex->getByIndex(--nPos) >>= xTemp;
+ if (xComp.get() == xTemp.get())
+ {
+ m_xModelAsManager->detach( nPos, Reference<XInterface>( xControl, UNO_QUERY ) );
+ break;
+ }
+ }
+}
+
+
+void FormController::setContainer(const Reference< XControlContainer > & xContainer)
+{
+ OSL_ENSURE( !impl_isDisposed_nofail(), "FormController: already disposed!" );
+ Reference< XTabControllerModel > xTabModel(getModel());
+ DBG_ASSERT(xTabModel.is() || !xContainer.is(), "No Model defined");
+ // if we have a new container we need a model
+ DBG_ASSERT(m_xTabController.is(), "FormController::setContainer : invalid aggregate !");
+
+ ::osl::MutexGuard aGuard( m_aMutex );
+ Reference< XContainer > xCurrentContainer;
+ if (m_xTabController.is())
+ xCurrentContainer.set(m_xTabController->getContainer(), UNO_QUERY);
+ if (xCurrentContainer.is())
+ {
+ xCurrentContainer->removeContainerListener(this);
+
+ if ( m_aTabActivationIdle.IsActive() )
+ m_aTabActivationIdle.Stop();
+
+ // clear the filter map
+ ::std::for_each( m_aFilterComponents.begin(), m_aFilterComponents.end(), RemoveComponentTextListener( this ) );
+ m_aFilterComponents.clear();
+
+ // collecting the controls
+ for ( const Reference< XControl >& rControl : std::as_const(m_aControls) )
+ implControlRemoved( rControl, true );
+
+ // make database-specific things
+ if (m_bDBConnection && isListeningForChanges())
+ stopListening();
+
+ m_aControls.realloc( 0 );
+ }
+
+ if (m_xTabController.is())
+ m_xTabController->setContainer(xContainer);
+
+ // What controls belong to the container?
+ if (xContainer.is() && xTabModel.is())
+ {
+ const Sequence< Reference< XControlModel > > aModels = xTabModel->getControlModels();
+ Sequence< Reference< XControl > > aAllControls = xContainer->getControls();
+
+ sal_Int32 nCount = aModels.getLength();
+ m_aControls = Sequence< Reference< XControl > >( nCount );
+ Reference< XControl > * pControls = m_aControls.getArray();
+
+ // collecting the controls
+ sal_Int32 j = 0;
+ for (const Reference< XControlModel >& rModel : aModels )
+ {
+ Reference< XControl > xControl = findControl( aAllControls, rModel, false, true );
+ if ( xControl.is() )
+ {
+ pControls[j++] = xControl;
+ implControlInserted( xControl, true );
+ }
+ }
+
+ // not every model had an associated control
+ if (j != nCount)
+ m_aControls.realloc(j);
+
+ // listen at the container
+ Reference< XContainer > xNewContainer(xContainer, UNO_QUERY);
+ if (xNewContainer.is())
+ xNewContainer->addContainerListener(this);
+
+ // make database-specific things
+ if (m_bDBConnection)
+ {
+ m_bLocked = determineLockState();
+ setLocks();
+ if (!isLocked())
+ startListening();
+ }
+ }
+ // the controls are in the right order
+ m_bControlsSorted = true;
+}
+
+
+Reference< XControlContainer > FormController::getContainer()
+{
+ ::osl::MutexGuard aGuard( m_aMutex );
+ impl_checkDisposed_throw();
+
+ DBG_ASSERT(m_xTabController.is(), "FormController::getContainer : invalid aggregate !");
+ if (!m_xTabController.is())
+ return Reference< XControlContainer > ();
+ return m_xTabController->getContainer();
+}
+
+
+Sequence< Reference< XControl > > FormController::getControls()
+{
+ ::osl::MutexGuard aGuard( m_aMutex );
+ impl_checkDisposed_throw();
+
+ if (!m_bControlsSorted)
+ {
+ Reference< XTabControllerModel > xModel = getModel();
+ if (!xModel.is())
+ return m_aControls;
+
+ const Sequence< Reference< XControlModel > > aControlModels = xModel->getControlModels();
+ sal_Int32 nModels = aControlModels.getLength();
+
+ Sequence< Reference< XControl > > aNewControls(nModels);
+
+ Reference< XControl > * pControls = aNewControls.getArray();
+ Reference< XControl > xControl;
+
+ // rearrange the controls according to the tab order sequence
+ sal_Int32 j = 0;
+ for ( const Reference< XControlModel >& rModel : aControlModels )
+ {
+ xControl = findControl( m_aControls, rModel, true, true );
+ if ( xControl.is() )
+ pControls[j++] = xControl;
+ }
+
+ // not every model had an associated control
+ if ( j != nModels )
+ aNewControls.realloc( j );
+
+ m_aControls = aNewControls;
+ m_bControlsSorted = true;
+ }
+ return m_aControls;
+}
+
+
+void FormController::autoTabOrder()
+{
+ ::osl::MutexGuard aGuard( m_aMutex );
+ impl_checkDisposed_throw();
+
+ DBG_ASSERT(m_xTabController.is(), "FormController::autoTabOrder : invalid aggregate !");
+ if (m_xTabController.is())
+ m_xTabController->autoTabOrder();
+}
+
+
+void FormController::activateTabOrder()
+{
+ ::osl::MutexGuard aGuard( m_aMutex );
+ impl_checkDisposed_throw();
+
+ DBG_ASSERT(m_xTabController.is(), "FormController::activateTabOrder : invalid aggregate !");
+ if (m_xTabController.is())
+ m_xTabController->activateTabOrder();
+}
+
+
+void FormController::setControlLock(const Reference< XControl > & xControl)
+{
+ OSL_ENSURE( !impl_isDisposed_nofail(), "FormController: already disposed!" );
+ bool bLocked = isLocked();
+
+ // It is locked
+ // a. if the entire record is locked
+ // b. if the associated field is locked
+ Reference< XBoundControl > xBound(xControl, UNO_QUERY);
+ if (!(xBound.is() &&
+ ( (bLocked && bLocked != bool(xBound->getLock())) ||
+ !bLocked))) // always uncheck individual fields when unlocking
+ return;
+
+ // there is a data source
+ Reference< XPropertySet > xSet(xControl->getModel(), UNO_QUERY);
+ if (!(xSet.is() && ::comphelper::hasProperty(FM_PROP_BOUNDFIELD, xSet)))
+ return;
+
+ // what about the ReadOnly and Enable properties
+ bool bTouch = true;
+ if (::comphelper::hasProperty(FM_PROP_ENABLED, xSet))
+ bTouch = ::comphelper::getBOOL(xSet->getPropertyValue(FM_PROP_ENABLED));
+ if (::comphelper::hasProperty(FM_PROP_READONLY, xSet))
+ bTouch = !::comphelper::getBOOL(xSet->getPropertyValue(FM_PROP_READONLY));
+
+ if (!bTouch)
+ return;
+
+ Reference< XPropertySet > xField;
+ xSet->getPropertyValue(FM_PROP_BOUNDFIELD) >>= xField;
+ if (!xField.is())
+ return;
+
+ if (bLocked)
+ xBound->setLock(bLocked);
+ else
+ {
+ try
+ {
+ Any aVal = xField->getPropertyValue(FM_PROP_ISREADONLY);
+ if (aVal.hasValue() && ::comphelper::getBOOL(aVal))
+ xBound->setLock(true);
+ else
+ xBound->setLock(bLocked);
+ }
+ catch( const Exception& )
+ {
+ DBG_UNHANDLED_EXCEPTION("svx");
+ }
+
+ }
+}
+
+
+void FormController::setLocks()
+{
+ OSL_ENSURE( !impl_isDisposed_nofail(), "FormController: already disposed!" );
+ // lock/unlock all controls connected to a data source
+ for ( const Reference< XControl >& rControl : std::as_const(m_aControls) )
+ setControlLock( rControl );
+}
+
+
+namespace
+{
+ bool lcl_shouldListenForModifications( const Reference< XControl >& _rxControl, const Reference< XPropertyChangeListener >& _rxBoundFieldListener )
+ {
+ bool bShould = false;
+
+ Reference< XBoundComponent > xBound( _rxControl, UNO_QUERY );
+ if ( xBound.is() )
+ {
+ bShould = true;
+ }
+ else if ( _rxControl.is() )
+ {
+ Reference< XPropertySet > xModelProps( _rxControl->getModel(), UNO_QUERY );
+ if ( xModelProps.is() && ::comphelper::hasProperty( FM_PROP_BOUNDFIELD, xModelProps ) )
+ {
+ Reference< XPropertySet > xField;
+ xModelProps->getPropertyValue( FM_PROP_BOUNDFIELD ) >>= xField;
+ bShould = xField.is();
+
+ if ( !bShould && _rxBoundFieldListener.is() )
+ xModelProps->addPropertyChangeListener( FM_PROP_BOUNDFIELD, _rxBoundFieldListener );
+ }
+ }
+
+ return bShould;
+ }
+}
+
+
+void FormController::startControlModifyListening(const Reference< XControl > & xControl)
+{
+ OSL_ENSURE( !impl_isDisposed_nofail(), "FormController: already disposed!" );
+
+ bool bModifyListening = lcl_shouldListenForModifications( xControl, this );
+
+ // artificial while
+ while ( bModifyListening )
+ {
+ Reference< XModifyBroadcaster > xMod(xControl, UNO_QUERY);
+ if (xMod.is())
+ {
+ xMod->addModifyListener(this);
+ break;
+ }
+
+ // all the text to prematurely recognize a modified
+ Reference< XTextComponent > xText(xControl, UNO_QUERY);
+ if (xText.is())
+ {
+ xText->addTextListener(this);
+ break;
+ }
+
+ Reference< XCheckBox > xBox(xControl, UNO_QUERY);
+ if (xBox.is())
+ {
+ xBox->addItemListener(this);
+ break;
+ }
+
+ Reference< XComboBox > xCbBox(xControl, UNO_QUERY);
+ if (xCbBox.is())
+ {
+ xCbBox->addItemListener(this);
+ break;
+ }
+
+ Reference< XListBox > xListBox(xControl, UNO_QUERY);
+ if (xListBox.is())
+ {
+ xListBox->addItemListener(this);
+ break;
+ }
+ break;
+ }
+}
+
+
+void FormController::stopControlModifyListening(const Reference< XControl > & xControl)
+{
+ OSL_ENSURE( !impl_isDisposed_nofail(), "FormController: already disposed!" );
+
+ bool bModifyListening = lcl_shouldListenForModifications( xControl, nullptr );
+
+ // artificial while
+ while (bModifyListening)
+ {
+ Reference< XModifyBroadcaster > xMod(xControl, UNO_QUERY);
+ if (xMod.is())
+ {
+ xMod->removeModifyListener(this);
+ break;
+ }
+ // all the text to prematurely recognize a modified
+ Reference< XTextComponent > xText(xControl, UNO_QUERY);
+ if (xText.is())
+ {
+ xText->removeTextListener(this);
+ break;
+ }
+
+ Reference< XCheckBox > xBox(xControl, UNO_QUERY);
+ if (xBox.is())
+ {
+ xBox->removeItemListener(this);
+ break;
+ }
+
+ Reference< XComboBox > xCbBox(xControl, UNO_QUERY);
+ if (xCbBox.is())
+ {
+ xCbBox->removeItemListener(this);
+ break;
+ }
+
+ Reference< XListBox > xListBox(xControl, UNO_QUERY);
+ if (xListBox.is())
+ {
+ xListBox->removeItemListener(this);
+ break;
+ }
+ break;
+ }
+}
+
+
+void FormController::startListening()
+{
+ OSL_ENSURE( !impl_isDisposed_nofail(), "FormController: already disposed!" );
+ m_bModified = false;
+
+ // now register at bound fields
+ for ( const Reference< XControl >& rControl : std::as_const(m_aControls) )
+ startControlModifyListening( rControl );
+}
+
+
+void FormController::stopListening()
+{
+ OSL_ENSURE( !impl_isDisposed_nofail(), "FormController: already disposed!" );
+ m_bModified = false;
+
+ // now register at bound fields
+ for ( const Reference< XControl >& rControl : std::as_const(m_aControls) )
+ stopControlModifyListening( rControl );
+}
+
+
+Reference< XControl > FormController::findControl(Sequence< Reference< XControl > >& _rControls, const Reference< XControlModel > & xCtrlModel ,bool _bRemove,bool _bOverWrite) const
+{
+ OSL_ENSURE( !impl_isDisposed_nofail(), "FormController: already disposed!" );
+ DBG_ASSERT( xCtrlModel.is(), "findControl - which ?!" );
+
+ const Reference< XControl >* pControls = std::find_if(std::cbegin(_rControls), std::cend(_rControls),
+ [&xCtrlModel](const Reference< XControl >& rControl) {
+ return rControl.is() && rControl->getModel().get() == xCtrlModel.get(); });
+ if (pControls != std::cend(_rControls))
+ {
+ Reference< XControl > xControl( *pControls );
+ auto i = static_cast<sal_Int32>(std::distance(std::cbegin(_rControls), pControls));
+ if ( _bRemove )
+ ::comphelper::removeElementAt( _rControls, i );
+ else if ( _bOverWrite )
+ _rControls.getArray()[i].clear();
+ return xControl;
+ }
+ return Reference< XControl > ();
+}
+
+
+void FormController::implControlInserted( const Reference< XControl>& _rxControl, bool _bAddToEventAttacher )
+{
+ Reference< XWindow > xWindow( _rxControl, UNO_QUERY );
+ if ( xWindow.is() )
+ {
+ xWindow->addFocusListener( this );
+ xWindow->addMouseListener( this );
+
+ if ( _bAddToEventAttacher )
+ addToEventAttacher( _rxControl );
+ }
+
+ // add a dispatch interceptor to the control (if supported)
+ Reference< XDispatchProviderInterception > xInterception( _rxControl, UNO_QUERY );
+ if ( xInterception.is() )
+ createInterceptor( xInterception );
+
+ if ( !_rxControl.is() )
+ return;
+
+ Reference< XControlModel > xModel( _rxControl->getModel() );
+
+ // we want to know about the reset of the model of our controls
+ // (for correctly resetting m_bModified)
+ Reference< XReset > xReset( xModel, UNO_QUERY );
+ if ( xReset.is() )
+ xReset->addResetListener( this );
+
+ // and we want to know about the validity, to visually indicate it
+ Reference< XValidatableFormComponent > xValidatable( xModel, UNO_QUERY );
+ if ( xValidatable.is() )
+ {
+ xValidatable->addFormComponentValidityListener( this );
+ m_aControlBorderManager.validityChanged( _rxControl, xValidatable );
+ }
+
+}
+
+
+void FormController::implControlRemoved( const Reference< XControl>& _rxControl, bool _bRemoveFromEventAttacher )
+{
+ Reference< XWindow > xWindow( _rxControl, UNO_QUERY );
+ if ( xWindow.is() )
+ {
+ xWindow->removeFocusListener( this );
+ xWindow->removeMouseListener( this );
+
+ if ( _bRemoveFromEventAttacher )
+ removeFromEventAttacher( _rxControl );
+ }
+
+ Reference< XDispatchProviderInterception > xInterception( _rxControl, UNO_QUERY);
+ if ( xInterception.is() )
+ deleteInterceptor( xInterception );
+
+ if ( _rxControl.is() )
+ {
+ Reference< XControlModel > xModel( _rxControl->getModel() );
+
+ Reference< XReset > xReset( xModel, UNO_QUERY );
+ if ( xReset.is() )
+ xReset->removeResetListener( this );
+
+ Reference< XValidatableFormComponent > xValidatable( xModel, UNO_QUERY );
+ if ( xValidatable.is() )
+ xValidatable->removeFormComponentValidityListener( this );
+ }
+}
+
+
+void FormController::implSetCurrentControl( const Reference< XControl >& _rxControl )
+{
+ if ( m_xCurrentControl.get() == _rxControl.get() )
+ return;
+
+ Reference< XGridControl > xGridControl( m_xCurrentControl, UNO_QUERY );
+ if ( xGridControl.is() )
+ xGridControl->removeGridControlListener( this );
+
+ m_xCurrentControl = _rxControl;
+
+ xGridControl.set( m_xCurrentControl, UNO_QUERY );
+ if ( xGridControl.is() )
+ xGridControl->addGridControlListener( this );
+}
+
+
+void FormController::insertControl(const Reference< XControl > & xControl)
+{
+ OSL_ENSURE( !impl_isDisposed_nofail(), "FormController: already disposed!" );
+ m_bControlsSorted = false;
+ m_aControls.realloc(m_aControls.getLength() + 1);
+ m_aControls.getArray()[m_aControls.getLength() - 1] = xControl;
+
+ if (m_pColumnInfoCache)
+ m_pColumnInfoCache->deinitializeControls();
+
+ implControlInserted( xControl, m_bAttachEvents );
+
+ if (m_bDBConnection && !m_bFiltering)
+ setControlLock(xControl);
+
+ if (isListeningForChanges() && m_bAttachEvents)
+ startControlModifyListening( xControl );
+}
+
+
+void FormController::removeControl(const Reference< XControl > & xControl)
+{
+ OSL_ENSURE( !impl_isDisposed_nofail(), "FormController: already disposed!" );
+ auto pControl = std::find_if(std::cbegin(m_aControls), std::cend(m_aControls),
+ [&xControl](const Reference< XControl >& rControl) { return xControl.get() == rControl.get(); });
+ if (pControl != std::cend(m_aControls))
+ {
+ auto nIndex = static_cast<sal_Int32>(std::distance(std::cbegin(m_aControls), pControl));
+ ::comphelper::removeElementAt( m_aControls, nIndex );
+ }
+
+ FilterComponents::iterator componentPos = ::std::find( m_aFilterComponents.begin(), m_aFilterComponents.end(), xControl );
+ if ( componentPos != m_aFilterComponents.end() )
+ m_aFilterComponents.erase( componentPos );
+
+ implControlRemoved( xControl, m_bDetachEvents );
+
+ if ( isListeningForChanges() && m_bDetachEvents )
+ stopControlModifyListening( xControl );
+}
+
+// XLoadListener
+
+void FormController::loaded(const EventObject& rEvent)
+{
+ OSL_ENSURE( rEvent.Source == m_xModelAsIndex, "FormController::loaded: where did this come from?" );
+
+ OSL_ENSURE( !impl_isDisposed_nofail(), "FormController: already disposed!" );
+ ::osl::MutexGuard aGuard( m_aMutex );
+ Reference< XRowSet > xForm(rEvent.Source, UNO_QUERY);
+ // do we have a connected data source
+ if (xForm.is() && getConnection(xForm).is())
+ {
+ Reference< XPropertySet > xSet(xForm, UNO_QUERY);
+ if (xSet.is())
+ {
+ Any aVal = xSet->getPropertyValue(FM_PROP_CYCLE);
+ sal_Int32 aVal2 = 0;
+ ::cppu::enum2int(aVal2,aVal);
+ m_bCycle = !aVal.hasValue() || static_cast<form::TabulatorCycle>(aVal2) == TabulatorCycle_RECORDS;
+ m_bCanUpdate = canUpdate(xSet);
+ m_bCanInsert = canInsert(xSet);
+ m_bCurrentRecordModified = ::comphelper::getBOOL(xSet->getPropertyValue(FM_PROP_ISMODIFIED));
+ m_bCurrentRecordNew = ::comphelper::getBOOL(xSet->getPropertyValue(FM_PROP_ISNEW));
+
+ startFormListening( xSet, false );
+
+ // set the locks for the current controls
+ if (getContainer().is())
+ {
+ m_aLoadEvent.Call();
+ }
+ }
+ else
+ {
+ m_bCanInsert = m_bCanUpdate = m_bCycle = false;
+ m_bCurrentRecordModified = false;
+ m_bCurrentRecordNew = false;
+ m_bLocked = false;
+ }
+ m_bDBConnection = true;
+ }
+ else
+ {
+ m_bDBConnection = false;
+ m_bCanInsert = m_bCanUpdate = m_bCycle = false;
+ m_bCurrentRecordModified = false;
+ m_bCurrentRecordNew = false;
+ m_bLocked = false;
+ }
+
+ Reference< XColumnsSupplier > xFormColumns( xForm, UNO_QUERY );
+ m_pColumnInfoCache.reset( xFormColumns.is() ? new ColumnInfoCache( xFormColumns ) : nullptr );
+
+ updateAllDispatchers();
+}
+
+
+void FormController::updateAllDispatchers() const
+{
+ ::std::for_each(
+ m_aFeatureDispatchers.begin(),
+ m_aFeatureDispatchers.end(),
+ [] (const DispatcherContainer::value_type& dispatcher) {
+ UpdateAllListeners()(dispatcher.second);
+ });
+}
+
+
+IMPL_LINK_NOARG(FormController, OnLoad, void*, void)
+{
+ OSL_ENSURE( !impl_isDisposed_nofail(), "FormController: already disposed!" );
+ m_bLocked = determineLockState();
+
+ setLocks();
+
+ if (!m_bLocked)
+ startListening();
+
+ // just one exception toggle the auto values
+ if (m_bCurrentRecordNew)
+ toggleAutoFields(true);
+}
+
+
+void FormController::unloaded(const EventObject& /*rEvent*/)
+{
+ ::osl::MutexGuard aGuard( m_aMutex );
+ impl_checkDisposed_throw();
+
+ updateAllDispatchers();
+}
+
+
+void FormController::reloading(const EventObject& /*aEvent*/)
+{
+ ::osl::MutexGuard aGuard( m_aMutex );
+ impl_checkDisposed_throw();
+
+ // do the same like in unloading
+ // just one exception toggle the auto values
+ m_aToggleEvent.CancelPendingCall();
+ unload();
+}
+
+
+void FormController::reloaded(const EventObject& aEvent)
+{
+ ::osl::MutexGuard aGuard( m_aMutex );
+ impl_checkDisposed_throw();
+
+ loaded(aEvent);
+}
+
+
+void FormController::unloading(const EventObject& /*aEvent*/)
+{
+ ::osl::MutexGuard aGuard( m_aMutex );
+ impl_checkDisposed_throw();
+
+ unload();
+}
+
+
+void FormController::unload()
+{
+ ::osl::MutexGuard aGuard( m_aMutex );
+ impl_checkDisposed_throw();
+
+ m_aLoadEvent.CancelPendingCall();
+
+ // be sure not to have autofields
+ if (m_bCurrentRecordNew)
+ toggleAutoFields(false);
+
+ // remove bound field listing again
+ removeBoundFieldListener();
+
+ if (m_bDBConnection && isListeningForChanges())
+ stopListening();
+
+ Reference< XPropertySet > xSet( m_xModelAsIndex, UNO_QUERY );
+ if ( m_bDBConnection && xSet.is() )
+ stopFormListening( xSet, false );
+
+ m_bDBConnection = false;
+ m_bCanInsert = m_bCanUpdate = m_bCycle = false;
+ m_bCurrentRecordModified = m_bCurrentRecordNew = m_bLocked = false;
+
+ m_pColumnInfoCache.reset();
+}
+
+
+void FormController::removeBoundFieldListener()
+{
+ for ( const Reference< XControl >& rControl : std::as_const(m_aControls) )
+ {
+ Reference< XPropertySet > xProp( rControl, UNO_QUERY );
+ if ( xProp.is() )
+ xProp->removePropertyChangeListener( FM_PROP_BOUNDFIELD, this );
+ }
+}
+
+
+void FormController::startFormListening( const Reference< XPropertySet >& _rxForm, bool _bPropertiesOnly )
+{
+ try
+ {
+ if ( m_bCanInsert || m_bCanUpdate ) // form can be modified
+ {
+ _rxForm->addPropertyChangeListener( FM_PROP_ISNEW, this );
+ _rxForm->addPropertyChangeListener( FM_PROP_ISMODIFIED, this );
+
+ if ( !_bPropertiesOnly )
+ {
+ // set the Listener for UI interaction
+ Reference< XRowSetApproveBroadcaster > xApprove( _rxForm, UNO_QUERY );
+ if ( xApprove.is() )
+ xApprove->addRowSetApproveListener( this );
+
+ // listener for row set changes
+ Reference< XRowSet > xRowSet( _rxForm, UNO_QUERY );
+ if ( xRowSet.is() )
+ xRowSet->addRowSetListener( this );
+ }
+ }
+
+ Reference< XPropertySetInfo > xInfo = _rxForm->getPropertySetInfo();
+ if ( xInfo.is() && xInfo->hasPropertyByName( FM_PROP_DYNAMIC_CONTROL_BORDER ) )
+ _rxForm->addPropertyChangeListener( FM_PROP_DYNAMIC_CONTROL_BORDER, this );
+ }
+ catch( const Exception& )
+ {
+ DBG_UNHANDLED_EXCEPTION("svx");
+ }
+}
+
+
+void FormController::stopFormListening( const Reference< XPropertySet >& _rxForm, bool _bPropertiesOnly )
+{
+ try
+ {
+ if ( m_bCanInsert || m_bCanUpdate )
+ {
+ _rxForm->removePropertyChangeListener( FM_PROP_ISNEW, this );
+ _rxForm->removePropertyChangeListener( FM_PROP_ISMODIFIED, this );
+
+ if ( !_bPropertiesOnly )
+ {
+ Reference< XRowSetApproveBroadcaster > xApprove( _rxForm, UNO_QUERY );
+ if (xApprove.is())
+ xApprove->removeRowSetApproveListener(this);
+
+ Reference< XRowSet > xRowSet( _rxForm, UNO_QUERY );
+ if ( xRowSet.is() )
+ xRowSet->removeRowSetListener( this );
+ }
+ }
+
+ Reference< XPropertySetInfo > xInfo = _rxForm->getPropertySetInfo();
+ if ( xInfo.is() && xInfo->hasPropertyByName( FM_PROP_DYNAMIC_CONTROL_BORDER ) )
+ _rxForm->removePropertyChangeListener( FM_PROP_DYNAMIC_CONTROL_BORDER, this );
+ }
+ catch( const Exception& )
+ {
+ DBG_UNHANDLED_EXCEPTION("svx");
+ }
+}
+
+// css::sdbc::XRowSetListener
+
+void FormController::cursorMoved(const EventObject& /*event*/)
+{
+ ::osl::MutexGuard aGuard( m_aMutex );
+ impl_checkDisposed_throw();
+
+ // toggle the locking ?
+ if (m_bLocked != determineLockState())
+ {
+ m_bLocked = !m_bLocked;
+ setLocks();
+ if (isListeningForChanges())
+ startListening();
+ else
+ stopListening();
+ }
+
+ // neither the current control nor the current record are modified anymore
+ m_bCurrentRecordModified = m_bModified = false;
+}
+
+
+void FormController::rowChanged(const EventObject& /*event*/)
+{
+ // not interested in ...
+}
+
+void FormController::rowSetChanged(const EventObject& /*event*/)
+{
+ // not interested in ...
+}
+
+
+// XContainerListener
+
+void SAL_CALL FormController::elementInserted(const ContainerEvent& evt)
+{
+ ::osl::MutexGuard aGuard( m_aMutex );
+ impl_checkDisposed_throw();
+
+ Reference< XControl > xControl( evt.Element, UNO_QUERY );
+ if ( !xControl.is() )
+ return;
+
+ Reference< XFormComponent > xModel(xControl->getModel(), UNO_QUERY);
+ if (xModel.is() && m_xModelAsIndex == xModel->getParent())
+ {
+ insertControl(xControl);
+
+ if ( m_aTabActivationIdle.IsActive() )
+ m_aTabActivationIdle.Stop();
+
+ m_aTabActivationIdle.Start();
+ }
+ // are we in filtermode and a XModeSelector has inserted an element
+ else if (m_bFiltering && Reference< XModeSelector > (evt.Source, UNO_QUERY).is())
+ {
+ xModel.set(evt.Source, UNO_QUERY);
+ if (xModel.is() && m_xModelAsIndex == xModel->getParent())
+ {
+ Reference< XPropertySet > xSet(xControl->getModel(), UNO_QUERY);
+ if (xSet.is() && ::comphelper::hasProperty(FM_PROP_BOUNDFIELD, xSet))
+ {
+ // does the model use a bound field ?
+ Reference< XPropertySet > xField;
+ xSet->getPropertyValue(FM_PROP_BOUNDFIELD) >>= xField;
+
+ Reference< XTextComponent > xText(xControl, UNO_QUERY);
+ // may we filter the field?
+ if (xText.is() && xField.is() && ::comphelper::hasProperty(FM_PROP_SEARCHABLE, xField) &&
+ ::comphelper::getBOOL(xField->getPropertyValue(FM_PROP_SEARCHABLE)))
+ {
+ m_aFilterComponents.push_back( xText );
+ xText->addTextListener( this );
+ }
+ }
+ }
+ }
+}
+
+
+void SAL_CALL FormController::elementReplaced(const ContainerEvent& evt)
+{
+ // simulate an elementRemoved
+ ContainerEvent aRemoveEvent( evt );
+ aRemoveEvent.Element = evt.ReplacedElement;
+ aRemoveEvent.ReplacedElement = Any();
+ elementRemoved( aRemoveEvent );
+
+ // simulate an elementInserted
+ ContainerEvent aInsertEvent( evt );
+ aInsertEvent.ReplacedElement = Any();
+ elementInserted( aInsertEvent );
+}
+
+
+void SAL_CALL FormController::elementRemoved(const ContainerEvent& evt)
+{
+ ::osl::MutexGuard aGuard( m_aMutex );
+ impl_checkDisposed_throw();
+
+ Reference< XControl > xControl;
+ evt.Element >>= xControl;
+ if (!xControl.is())
+ return;
+
+ Reference< XFormComponent > xModel(xControl->getModel(), UNO_QUERY);
+ if (xModel.is() && m_xModelAsIndex == xModel->getParent())
+ {
+ removeControl(xControl);
+ // Do not recalculate TabOrder, because it must already work internally!
+ }
+ // are we in filtermode and a XModeSelector has inserted an element
+ else if (m_bFiltering && Reference< XModeSelector > (evt.Source, UNO_QUERY).is())
+ {
+ FilterComponents::iterator componentPos = ::std::find(
+ m_aFilterComponents.begin(), m_aFilterComponents.end(), xControl );
+ if ( componentPos != m_aFilterComponents.end() )
+ m_aFilterComponents.erase( componentPos );
+ }
+}
+
+
+Reference< XControl > FormController::isInList(const Reference< XWindowPeer > & xPeer) const
+{
+ OSL_ENSURE( !impl_isDisposed_nofail(), "FormController: already disposed!" );
+ const Reference< XControl >* pControls = m_aControls.getConstArray();
+
+ sal_uInt32 nCtrls = m_aControls.getLength();
+ for ( sal_uInt32 n = 0; n < nCtrls && xPeer.is(); ++n, ++pControls )
+ {
+ if ( pControls->is() )
+ {
+ Reference< XVclWindowPeer > xCtrlPeer( (*pControls)->getPeer(), UNO_QUERY);
+ if ( ( xCtrlPeer.get() == xPeer.get() ) || xCtrlPeer->isChild( xPeer ) )
+ return *pControls;
+ }
+ }
+ return Reference< XControl > ();
+}
+
+
+void FormController::activateFirst()
+{
+ ::osl::MutexGuard aGuard( m_aMutex );
+ impl_checkDisposed_throw();
+
+ DBG_ASSERT(m_xTabController.is(), "FormController::activateFirst : invalid aggregate !");
+ if (m_xTabController.is())
+ m_xTabController->activateFirst();
+}
+
+
+void FormController::activateLast()
+{
+ ::osl::MutexGuard aGuard( m_aMutex );
+ impl_checkDisposed_throw();
+
+ DBG_ASSERT(m_xTabController.is(), "FormController::activateLast : invalid aggregate !");
+ if (m_xTabController.is())
+ m_xTabController->activateLast();
+}
+
+// XFormController
+
+Reference< XFormOperations > SAL_CALL FormController::getFormOperations()
+{
+ ::osl::MutexGuard aGuard( m_aMutex );
+ impl_checkDisposed_throw();
+
+ return m_xFormOperations;
+}
+
+
+Reference< XControl> SAL_CALL FormController::getCurrentControl()
+{
+ ::osl::MutexGuard aGuard( m_aMutex );
+ impl_checkDisposed_throw();
+ return m_xCurrentControl;
+}
+
+
+void SAL_CALL FormController::addActivateListener(const Reference< XFormControllerListener > & l)
+{
+ ::osl::MutexGuard aGuard( m_aMutex );
+ impl_checkDisposed_throw();
+ m_aActivateListeners.addInterface(l);
+}
+
+void SAL_CALL FormController::removeActivateListener(const Reference< XFormControllerListener > & l)
+{
+ ::osl::MutexGuard aGuard( m_aMutex );
+ impl_checkDisposed_throw();
+ m_aActivateListeners.removeInterface(l);
+}
+
+
+void SAL_CALL FormController::addChildController( const Reference< XFormController >& ChildController )
+{
+ ::osl::MutexGuard aGuard( m_aMutex );
+ impl_checkDisposed_throw();
+
+ if ( !ChildController.is() )
+ throw IllegalArgumentException( OUString(), *this, 1 );
+ // TODO: (localized) error message
+
+ // the parent of our (to-be-)child must be our own model
+ Reference< XFormComponent > xFormOfChild( ChildController->getModel(), UNO_QUERY );
+ if ( !xFormOfChild.is() )
+ throw IllegalArgumentException( OUString(), *this, 1 );
+ // TODO: (localized) error message
+
+ if ( xFormOfChild->getParent() != m_xModelAsIndex )
+ throw IllegalArgumentException( OUString(), *this, 1 );
+ // TODO: (localized) error message
+
+ m_aChildren.push_back( ChildController );
+ ChildController->setParent( *this );
+
+ // search the position of the model within the form
+ sal_uInt32 nPos = m_xModelAsIndex->getCount();
+ Reference< XFormComponent > xTemp;
+ for( ; nPos; )
+ {
+ m_xModelAsIndex->getByIndex(--nPos) >>= xTemp;
+ if ( xFormOfChild == xTemp )
+ {
+ m_xModelAsManager->attach( nPos, Reference<XInterface>( ChildController, UNO_QUERY ), Any( ChildController) );
+ break;
+ }
+ }
+}
+
+
+Reference< XFormControllerContext > SAL_CALL FormController::getContext()
+{
+ ::osl::MutexGuard aGuard( m_aMutex );
+ impl_checkDisposed_throw();
+ return m_xFormControllerContext;
+}
+
+
+void SAL_CALL FormController::setContext( const Reference< XFormControllerContext >& _context )
+{
+ ::osl::MutexGuard aGuard( m_aMutex );
+ impl_checkDisposed_throw();
+ m_xFormControllerContext = _context;
+}
+
+
+Reference< XInteractionHandler > SAL_CALL FormController::getInteractionHandler()
+{
+ ::osl::MutexGuard aGuard( m_aMutex );
+ impl_checkDisposed_throw();
+ return m_xInteractionHandler;
+}
+
+
+void SAL_CALL FormController::setInteractionHandler( const Reference< XInteractionHandler >& _interactionHandler )
+{
+ ::osl::MutexGuard aGuard( m_aMutex );
+ impl_checkDisposed_throw();
+ m_xInteractionHandler = _interactionHandler;
+}
+
+
+void FormController::setFilter(::std::vector<FmFieldInfo>& rFieldInfos)
+{
+ OSL_ENSURE( !impl_isDisposed_nofail(), "FormController: already disposed!" );
+ // create the composer
+ Reference< XRowSet > xForm(m_xModelAsIndex, UNO_QUERY);
+ Reference< XConnection > xConnection(getConnection(xForm));
+ if (xForm.is())
+ {
+ try
+ {
+ Reference< XMultiServiceFactory > xFactory( xConnection, UNO_QUERY_THROW );
+ m_xComposer.set(
+ xFactory->createInstance("com.sun.star.sdb.SingleSelectQueryComposer"),
+ UNO_QUERY_THROW );
+
+ Reference< XPropertySet > xSet( xForm, UNO_QUERY );
+ OUString sStatement = ::comphelper::getString( xSet->getPropertyValue( FM_PROP_ACTIVECOMMAND ) );
+ OUString sFilter = ::comphelper::getString( xSet->getPropertyValue( FM_PROP_FILTER ) );
+ m_xComposer->setElementaryQuery( sStatement );
+ m_xComposer->setFilter( sFilter );
+ }
+ catch( const Exception& )
+ {
+ DBG_UNHANDLED_EXCEPTION("svx");
+ }
+ }
+
+ if (m_xComposer.is())
+ {
+ const Sequence< Sequence < PropertyValue > > aFilterRows = m_xComposer->getStructuredFilter();
+
+ // ok, we receive the list of filters as sequence of fieldnames, value
+ // now we have to transform the fieldname into UI names, that could be a label of the field or
+ // an aliasname or the fieldname itself
+
+ // first adjust the field names if necessary
+ Reference< XNameAccess > xQueryColumns =
+ Reference< XColumnsSupplier >( m_xComposer, UNO_QUERY_THROW )->getColumns();
+
+ for (auto& rFieldInfo : rFieldInfos)
+ {
+ if ( xQueryColumns->hasByName(rFieldInfo.aFieldName) )
+ {
+ if ( (xQueryColumns->getByName(rFieldInfo.aFieldName) >>= rFieldInfo.xField) && rFieldInfo.xField.is() )
+ rFieldInfo.xField->getPropertyValue(FM_PROP_REALNAME) >>= rFieldInfo.aFieldName;
+ }
+ }
+
+ Reference< XDatabaseMetaData> xMetaData(xConnection->getMetaData());
+ // now transfer the filters into Value/TextComponent pairs
+ ::comphelper::UStringMixEqual aCompare(xMetaData->storesMixedCaseQuotedIdentifiers());
+
+ // need to parse criteria localized
+ Reference< XNumberFormatsSupplier> xFormatSupplier( getNumberFormats(xConnection, true));
+ Reference< XNumberFormatter> xFormatter = NumberFormatter::create(m_xComponentContext);
+ xFormatter->attachNumberFormatsSupplier(xFormatSupplier);
+ Locale aAppLocale = Application::GetSettings().GetUILanguageTag().getLocale();
+ const LocaleDataWrapper& rLocaleWrapper( Application::GetSettings().GetUILocaleDataWrapper() );
+ OUString strDecimalSeparator = rLocaleWrapper.getNumDecimalSep();
+
+ // retrieving the filter
+ for (const Sequence < PropertyValue >& rRow : aFilterRows)
+ {
+ FmFilterRow aRow;
+
+ // search a field for the given name
+ for (const PropertyValue& rRefValue : rRow)
+ {
+ // look for the text component
+ Reference< XPropertySet > xField;
+ try
+ {
+ Reference< XPropertySet > xSet;
+ OUString aRealName;
+
+ // first look with the given name
+ if (xQueryColumns->hasByName(rRefValue.Name))
+ {
+ xQueryColumns->getByName(rRefValue.Name) >>= xSet;
+
+ // get the RealName
+ xSet->getPropertyValue("RealName") >>= aRealName;
+
+ // compare the condition field name and the RealName
+ if (aCompare(aRealName, rRefValue.Name))
+ xField = xSet;
+ }
+ if (!xField.is())
+ {
+ // no we have to check every column to find the realname
+ Reference< XIndexAccess > xColumnsByIndex(xQueryColumns, UNO_QUERY);
+ for (sal_Int32 n = 0, nCount = xColumnsByIndex->getCount(); n < nCount; n++)
+ {
+ xColumnsByIndex->getByIndex(n) >>= xSet;
+ xSet->getPropertyValue("RealName") >>= aRealName;
+ if (aCompare(aRealName, rRefValue.Name))
+ {
+ // get the column by its alias
+ xField = xSet;
+ break;
+ }
+ }
+ }
+ if (!xField.is())
+ continue;
+ }
+ catch (const Exception&)
+ {
+ continue;
+ }
+
+ // find the text component
+ for (const auto& rFieldInfo : rFieldInfos)
+ {
+ // we found the field so insert a new entry to the filter row
+ if (rFieldInfo.xField == xField)
+ {
+ // do we already have the control ?
+ if (aRow.find(rFieldInfo.xText) != aRow.end())
+ {
+ OString aVal = m_pParser->getContext().getIntlKeywordAscii(IParseContext::InternationalKeyCode::And);
+ OUString aCompText = aRow[rFieldInfo.xText] + " " +
+ OUString(aVal.getStr(),aVal.getLength(),RTL_TEXTENCODING_ASCII_US) + " " +
+ ::comphelper::getString(rRefValue.Value);
+ aRow[rFieldInfo.xText] = aCompText;
+ }
+ else
+ {
+ OUString sPredicate,sErrorMsg;
+ rRefValue.Value >>= sPredicate;
+ std::unique_ptr< OSQLParseNode > pParseNode = predicateTree(sErrorMsg, sPredicate, xFormatter, xField);
+ if ( pParseNode != nullptr )
+ {
+ OUString sCriteria;
+ switch (rRefValue.Handle)
+ {
+ case css::sdb::SQLFilterOperator::EQUAL:
+ sCriteria += "=";
+ break;
+ case css::sdb::SQLFilterOperator::NOT_EQUAL:
+ sCriteria += "!=";
+ break;
+ case css::sdb::SQLFilterOperator::LESS:
+ sCriteria += "<";
+ break;
+ case css::sdb::SQLFilterOperator::GREATER:
+ sCriteria += ">";
+ break;
+ case css::sdb::SQLFilterOperator::LESS_EQUAL:
+ sCriteria += "<=";
+ break;
+ case css::sdb::SQLFilterOperator::GREATER_EQUAL:
+ sCriteria += ">=";
+ break;
+ case css::sdb::SQLFilterOperator::LIKE:
+ sCriteria += "LIKE ";
+ break;
+ case css::sdb::SQLFilterOperator::NOT_LIKE:
+ sCriteria += "NOT LIKE ";
+ break;
+ case css::sdb::SQLFilterOperator::SQLNULL:
+ sCriteria += "IS NULL";
+ break;
+ case css::sdb::SQLFilterOperator::NOT_SQLNULL:
+ sCriteria += "IS NOT NULL";
+ break;
+ }
+ pParseNode->parseNodeToPredicateStr( sCriteria
+ ,xConnection
+ ,xFormatter
+ ,xField
+ ,OUString()
+ ,aAppLocale
+ ,strDecimalSeparator
+ ,getParseContext());
+ aRow[rFieldInfo.xText] = sCriteria;
+ }
+ }
+ }
+ }
+ }
+
+ if (aRow.empty())
+ continue;
+
+ impl_addFilterRow( aRow );
+ }
+ }
+
+ // now set the filter controls
+ for (const auto& rFieldInfo : rFieldInfos)
+ {
+ m_aFilterComponents.push_back( rFieldInfo.xText );
+ }
+}
+
+
+void FormController::startFiltering()
+{
+ OSL_ENSURE( !impl_isDisposed_nofail(), "FormController: already disposed!" );
+
+ Reference< XConnection > xConnection( getConnection( Reference< XRowSet >( m_xModelAsIndex, UNO_QUERY ) ) );
+ if ( !xConnection.is() )
+ // nothing to do - can't filter a form which is not connected
+ return;
+
+ // stop listening for controls
+ if (isListeningForChanges())
+ stopListening();
+
+ m_bFiltering = true;
+
+ // as we don't want new controls to be attached to the scripting environment
+ // we change attach flags
+ m_bAttachEvents = false;
+
+ // exchanging the controls for the current form
+ Sequence< Reference< XControl > > aControlsCopy( m_aControls );
+ const Reference< XControl >* pControls = aControlsCopy.getConstArray();
+ sal_Int32 nControlCount = aControlsCopy.getLength();
+
+ // the control we have to activate after replacement
+ Reference< XNumberFormatsSupplier > xFormatSupplier = getNumberFormats(xConnection, true);
+ Reference< XNumberFormatter > xFormatter = NumberFormatter::create(m_xComponentContext);
+ xFormatter->attachNumberFormatsSupplier(xFormatSupplier);
+
+ // structure for storing the field info
+ ::std::vector<FmFieldInfo> aFieldInfos;
+
+ for (sal_Int32 i = nControlCount; i > 0;)
+ {
+ Reference< XControl > xControl = pControls[--i];
+ if (xControl.is())
+ {
+ // no events for the control anymore
+ removeFromEventAttacher(xControl);
+
+ // do we have a mode selector
+ Reference< XModeSelector > xSelector(xControl, UNO_QUERY);
+ if (xSelector.is())
+ {
+ xSelector->setMode( "FilterMode" );
+
+ // listening for new controls of the selector
+ Reference< XContainer > xContainer(xSelector, UNO_QUERY);
+ if (xContainer.is())
+ xContainer->addContainerListener(this);
+
+ Reference< XEnumerationAccess > xElementAccess(xSelector, UNO_QUERY);
+ if (xElementAccess.is())
+ {
+ Reference< XEnumeration > xEnumeration(xElementAccess->createEnumeration());
+ Reference< XControl > xSubControl;
+ while (xEnumeration->hasMoreElements())
+ {
+ xEnumeration->nextElement() >>= xSubControl;
+ if (xSubControl.is())
+ {
+ Reference< XPropertySet > xSet(xSubControl->getModel(), UNO_QUERY);
+ if (xSet.is() && ::comphelper::hasProperty(FM_PROP_BOUNDFIELD, xSet))
+ {
+ // does the model use a bound field ?
+ Reference< XPropertySet > xField;
+ xSet->getPropertyValue(FM_PROP_BOUNDFIELD) >>= xField;
+
+ Reference< XTextComponent > xText(xSubControl, UNO_QUERY);
+ // may we filter the field?
+ if (xText.is() && xField.is() && ::comphelper::hasProperty(FM_PROP_SEARCHABLE, xField) &&
+ ::comphelper::getBOOL(xField->getPropertyValue(FM_PROP_SEARCHABLE)))
+ {
+ aFieldInfos.emplace_back(xField, xText);
+ xText->addTextListener(this);
+ }
+ }
+ }
+ }
+ }
+ continue;
+ }
+
+ Reference< XPropertySet > xModel( xControl->getModel(), UNO_QUERY );
+ if (xModel.is() && ::comphelper::hasProperty(FM_PROP_BOUNDFIELD, xModel))
+ {
+ // does the model use a bound field ?
+ Any aVal = xModel->getPropertyValue(FM_PROP_BOUNDFIELD);
+ Reference< XPropertySet > xField;
+ aVal >>= xField;
+
+ // may we filter the field?
+
+ if ( xField.is()
+ && ::comphelper::hasProperty( FM_PROP_SEARCHABLE, xField )
+ && ::comphelper::getBOOL( xField->getPropertyValue( FM_PROP_SEARCHABLE ) )
+ )
+ {
+ // create a filter control
+ Reference< XControl > xFilterControl = form::control::FilterControl::createWithFormat(
+ m_xComponentContext,
+ getDialogParentWindow(this),
+ xFormatter,
+ xModel);
+
+ if ( replaceControl( xControl, xFilterControl ) )
+ {
+ Reference< XTextComponent > xFilterText( xFilterControl, UNO_QUERY );
+ aFieldInfos.emplace_back( xField, xFilterText );
+ xFilterText->addTextListener(this);
+ }
+ }
+ }
+ else
+ {
+ // unsubscribe from EventManager
+ }
+ }
+ }
+
+ // we have all filter controls now, so the next step is to read the filters from the form
+ // resolve all aliases and set the current filter to the according structure
+ setFilter(aFieldInfos);
+
+ Reference< XPropertySet > xSet( m_xModelAsIndex, UNO_QUERY );
+ if ( xSet.is() )
+ stopFormListening( xSet, true );
+
+ impl_setTextOnAllFilter_throw();
+
+ // lock all controls which are not used for filtering
+ m_bLocked = determineLockState();
+ setLocks();
+ m_bAttachEvents = true;
+}
+
+
+void FormController::stopFiltering()
+{
+ OSL_ENSURE( !impl_isDisposed_nofail(), "FormController: already disposed!" );
+ if ( !m_bFiltering ) // #104693# OJ
+ { // nothing to do
+ return;
+ }
+
+ m_bFiltering = false;
+ m_bDetachEvents = false;
+
+ ::comphelper::disposeComponent(m_xComposer);
+
+ // exchanging the controls for the current form
+ Sequence< Reference< XControl > > aControlsCopy( m_aControls );
+ const Reference< XControl > * pControls = aControlsCopy.getConstArray();
+ sal_Int32 nControlCount = aControlsCopy.getLength();
+
+ // clear the filter control map
+ ::std::for_each( m_aFilterComponents.begin(), m_aFilterComponents.end(), RemoveComponentTextListener( this ) );
+ m_aFilterComponents.clear();
+
+ for ( sal_Int32 i = nControlCount; i > 0; )
+ {
+ Reference< XControl > xControl = pControls[--i];
+ if (xControl.is())
+ {
+ // now enable event handling again
+ addToEventAttacher(xControl);
+
+ Reference< XModeSelector > xSelector(xControl, UNO_QUERY);
+ if (xSelector.is())
+ {
+ xSelector->setMode( "DataMode" );
+
+ // listening for new controls of the selector
+ Reference< XContainer > xContainer(xSelector, UNO_QUERY);
+ if (xContainer.is())
+ xContainer->removeContainerListener(this);
+ continue;
+ }
+
+ Reference< XPropertySet > xSet(xControl->getModel(), UNO_QUERY);
+ if (xSet.is() && ::comphelper::hasProperty(FM_PROP_BOUNDFIELD, xSet))
+ {
+ // does the model use a bound field ?
+ Reference< XPropertySet > xField;
+ xSet->getPropertyValue(FM_PROP_BOUNDFIELD) >>= xField;
+
+ // may we filter the field?
+ if ( xField.is()
+ && ::comphelper::hasProperty( FM_PROP_SEARCHABLE, xField )
+ && ::comphelper::getBOOL( xField->getPropertyValue( FM_PROP_SEARCHABLE ) )
+ )
+ {
+ OUString sServiceName;
+ OSL_VERIFY( xSet->getPropertyValue( FM_PROP_DEFAULTCONTROL ) >>= sServiceName );
+ Reference< XControl > xNewControl( m_xComponentContext->getServiceManager()->createInstanceWithContext( sServiceName, m_xComponentContext ), UNO_QUERY );
+ replaceControl( xControl, xNewControl );
+ }
+ }
+ }
+ }
+
+ Reference< XPropertySet > xSet( m_xModelAsIndex, UNO_QUERY );
+ if ( xSet.is() )
+ startFormListening( xSet, true );
+
+ m_bDetachEvents = true;
+
+ m_aFilterRows.clear();
+ m_nCurrentFilterPosition = -1;
+
+ // release the locks if possible
+ // lock all controls which are not used for filtering
+ m_bLocked = determineLockState();
+ setLocks();
+
+ // restart listening for control modifications
+ if (isListeningForChanges())
+ startListening();
+}
+
+// XModeSelector
+
+void FormController::setMode(const OUString& Mode)
+{
+ ::osl::MutexGuard aGuard( m_aMutex );
+ impl_checkDisposed_throw();
+
+ if (!supportsMode(Mode))
+ throw NoSupportException();
+
+ if (Mode == m_aMode)
+ return;
+
+ m_aMode = Mode;
+
+ if ( Mode == "FilterMode" )
+ startFiltering();
+ else
+ stopFiltering();
+
+ for (const auto& rChild : m_aChildren)
+ {
+ Reference< XModeSelector > xMode(rChild, UNO_QUERY);
+ if ( xMode.is() )
+ xMode->setMode(Mode);
+ }
+}
+
+
+OUString SAL_CALL FormController::getMode()
+{
+ ::osl::MutexGuard aGuard( m_aMutex );
+ impl_checkDisposed_throw();
+
+ return m_aMode;
+}
+
+
+Sequence< OUString > SAL_CALL FormController::getSupportedModes()
+{
+ ::osl::MutexGuard aGuard( m_aMutex );
+ impl_checkDisposed_throw();
+
+ static Sequence< OUString > const aModes
+ {
+ "DataMode",
+ "FilterMode"
+ };
+ return aModes;
+}
+
+sal_Bool SAL_CALL FormController::supportsMode(const OUString& Mode)
+{
+ ::osl::MutexGuard aGuard( m_aMutex );
+ impl_checkDisposed_throw();
+
+ Sequence< OUString > aModes(getSupportedModes());
+ return comphelper::findValue(aModes, Mode) != -1;
+}
+
+css::uno::Reference<css::awt::XWindow> FormController::getDialogParentWindow(css::uno::Reference<css::form::runtime::XFormController> xFormController)
+{
+ try
+ {
+ Reference< XControl > xContainerControl( xFormController->getContainer(), UNO_QUERY_THROW );
+ Reference<XWindow> xContainerWindow(xContainerControl->getPeer(), UNO_QUERY_THROW);
+ return xContainerWindow;
+ }
+ catch( const Exception& )
+ {
+ DBG_UNHANDLED_EXCEPTION("svx");
+ }
+ return nullptr;
+}
+
+bool FormController::checkFormComponentValidity( OUString& /* [out] */ _rFirstInvalidityExplanation, Reference< XControlModel >& /* [out] */ _rxFirstInvalidModel )
+{
+ try
+ {
+ Reference< XEnumerationAccess > xControlEnumAcc( getModel(), UNO_QUERY );
+ Reference< XEnumeration > xControlEnumeration;
+ if ( xControlEnumAcc.is() )
+ xControlEnumeration = xControlEnumAcc->createEnumeration();
+ OSL_ENSURE( xControlEnumeration.is(), "FormController::checkFormComponentValidity: cannot enumerate the controls!" );
+ if ( !xControlEnumeration.is() )
+ // assume all valid
+ return true;
+
+ Reference< XValidatableFormComponent > xValidatable;
+ while ( xControlEnumeration->hasMoreElements() )
+ {
+ if ( !( xControlEnumeration->nextElement() >>= xValidatable ) )
+ // control does not support validation
+ continue;
+
+ if ( xValidatable->isValid() )
+ continue;
+
+ Reference< XValidator > xValidator( xValidatable->getValidator() );
+ OSL_ENSURE( xValidator.is(), "FormController::checkFormComponentValidity: invalid, but no validator?" );
+ if ( !xValidator.is() )
+ // this violates the interface definition of css.form.validation.XValidatableFormComponent ...
+ continue;
+
+ _rFirstInvalidityExplanation = xValidator->explainInvalid( xValidatable->getCurrentValue() );
+ _rxFirstInvalidModel.set(xValidatable, css::uno::UNO_QUERY);
+ return false;
+ }
+ }
+ catch( const Exception& )
+ {
+ DBG_UNHANDLED_EXCEPTION("svx");
+ }
+ return true;
+}
+
+
+Reference< XControl > FormController::locateControl( const Reference< XControlModel >& _rxModel )
+{
+ try
+ {
+ const Sequence< Reference< XControl > > aControls( getControls() );
+
+ for ( auto const & control : aControls )
+ {
+ OSL_ENSURE( control.is(), "FormController::locateControl: NULL-control?" );
+ if ( control.is() )
+ {
+ if ( control->getModel() == _rxModel )
+ return control;
+ }
+ }
+ OSL_FAIL( "FormController::locateControl: did not find a control for this model!" );
+ }
+ catch( const Exception& )
+ {
+ DBG_UNHANDLED_EXCEPTION("svx");
+ }
+ return nullptr;
+}
+
+
+namespace
+{
+ void displayErrorSetFocus(const OUString& _rMessage, const Reference<XControl>& _rxFocusControl,
+ const css::uno::Reference<css::awt::XWindow>& rDialogParent)
+ {
+ SQLContext aError;
+ aError.Message = SvxResId(RID_STR_WRITEERROR);
+ aError.Details = _rMessage;
+ displayException(aError, rDialogParent);
+
+ if ( _rxFocusControl.is() )
+ {
+ Reference< XWindow > xControlWindow( _rxFocusControl, UNO_QUERY );
+ OSL_ENSURE( xControlWindow.is(), "displayErrorSetFocus: invalid control!" );
+ if ( xControlWindow.is() )
+ xControlWindow->setFocus();
+ }
+ }
+
+ bool lcl_shouldValidateRequiredFields_nothrow( const Reference< XInterface >& _rxForm )
+ {
+ try
+ {
+ static constexpr OUStringLiteral s_sFormsCheckRequiredFields = u"FormsCheckRequiredFields";
+
+ // first, check whether the form has a property telling us the answer
+ // this allows people to use the XPropertyContainer interface of a form to control
+ // the behaviour on a per-form basis.
+ Reference< XPropertySet > xFormProps( _rxForm, UNO_QUERY_THROW );
+ Reference< XPropertySetInfo > xPSI( xFormProps->getPropertySetInfo() );
+ if ( xPSI->hasPropertyByName( s_sFormsCheckRequiredFields ) )
+ {
+ bool bShouldValidate = true;
+ OSL_VERIFY( xFormProps->getPropertyValue( s_sFormsCheckRequiredFields ) >>= bShouldValidate );
+ return bShouldValidate;
+ }
+
+ // next, check the data source which created the connection
+ Reference< XChild > xConnectionAsChild( xFormProps->getPropertyValue( FM_PROP_ACTIVE_CONNECTION ), UNO_QUERY_THROW );
+ Reference< XPropertySet > xDataSource( xConnectionAsChild->getParent(), UNO_QUERY );
+ if ( !xDataSource.is() )
+ // seldom (but possible): this is not a connection created by a data source
+ return true;
+
+ Reference< XPropertySet > xDataSourceSettings(
+ xDataSource->getPropertyValue("Settings"),
+ UNO_QUERY_THROW );
+
+ bool bShouldValidate = true;
+ OSL_VERIFY( xDataSourceSettings->getPropertyValue( s_sFormsCheckRequiredFields ) >>= bShouldValidate );
+ return bShouldValidate;
+ }
+ catch( const Exception& )
+ {
+ DBG_UNHANDLED_EXCEPTION("svx");
+ }
+
+ return true;
+ }
+}
+
+// XRowSetApproveListener
+
+sal_Bool SAL_CALL FormController::approveRowChange(const RowChangeEvent& _rEvent)
+{
+ ::osl::ClearableMutexGuard aGuard( m_aMutex );
+ impl_checkDisposed_throw();
+
+ ::comphelper::OInterfaceIteratorHelper3 aIter(m_aRowSetApproveListeners);
+ bool bValid = true;
+ if (aIter.hasMoreElements())
+ {
+ RowChangeEvent aEvt( _rEvent );
+ aEvt.Source = *this;
+ bValid = aIter.next()->approveRowChange(aEvt);
+ }
+
+ if ( !bValid )
+ return bValid;
+
+ if ( ( _rEvent.Action != RowChangeAction::INSERT )
+ && ( _rEvent.Action != RowChangeAction::UPDATE )
+ )
+ return bValid;
+
+ // if some of the control models are bound to validators, check them
+ OUString sInvalidityExplanation;
+ Reference< XControlModel > xInvalidModel;
+ if ( !checkFormComponentValidity( sInvalidityExplanation, xInvalidModel ) )
+ {
+ Reference< XControl > xControl( locateControl( xInvalidModel ) );
+ aGuard.clear();
+ displayErrorSetFocus( sInvalidityExplanation, xControl, getDialogParentWindow(this) );
+ return false;
+ }
+
+ // check values on NULL and required flag
+ if ( !lcl_shouldValidateRequiredFields_nothrow( _rEvent.Source ) )
+ return true;
+
+ OSL_ENSURE(m_pColumnInfoCache, "FormController::approveRowChange: no column infos!");
+ if (!m_pColumnInfoCache)
+ return true;
+
+ try
+ {
+ if ( !m_pColumnInfoCache->controlsInitialized() )
+ m_pColumnInfoCache->initializeControls( getControls() );
+
+ size_t colCount = m_pColumnInfoCache->getColumnCount();
+ for ( size_t col = 0; col < colCount; ++col )
+ {
+ const ColumnInfo& rColInfo = m_pColumnInfoCache->getColumnInfo( col );
+
+ if ( rColInfo.bAutoIncrement )
+ continue;
+
+ if ( rColInfo.bReadOnly )
+ continue;
+
+ if ( !rColInfo.xFirstControlWithInputRequired.is() && !rColInfo.xFirstGridWithInputRequiredColumn.is() )
+ {
+ continue;
+ }
+
+ // TODO: in case of binary fields, this "getString" below is extremely expensive
+ if ( !rColInfo.xColumn->getString().isEmpty() || !rColInfo.xColumn->wasNull() )
+ continue;
+
+ OUString sMessage( SvxResId( RID_ERR_FIELDREQUIRED ) );
+ sMessage = sMessage.replaceFirst( "#", rColInfo.sName );
+
+ // the control to focus
+ Reference< XControl > xControl( rColInfo.xFirstControlWithInputRequired );
+ if ( !xControl.is() )
+ xControl.set( rColInfo.xFirstGridWithInputRequiredColumn, UNO_QUERY );
+
+ aGuard.clear();
+ displayErrorSetFocus( sMessage, rColInfo.xFirstControlWithInputRequired, getDialogParentWindow(this) );
+ return false;
+ }
+ }
+ catch( const Exception& )
+ {
+ DBG_UNHANDLED_EXCEPTION("svx");
+ }
+
+ return true;
+}
+
+
+sal_Bool SAL_CALL FormController::approveCursorMove(const EventObject& event)
+{
+ ::osl::MutexGuard aGuard( m_aMutex );
+ impl_checkDisposed_throw();
+
+ ::comphelper::OInterfaceIteratorHelper3 aIter(m_aRowSetApproveListeners);
+ if (aIter.hasMoreElements())
+ {
+ EventObject aEvt(event);
+ aEvt.Source = *this;
+ return aIter.next()->approveCursorMove(aEvt);
+ }
+
+ return true;
+}
+
+
+sal_Bool SAL_CALL FormController::approveRowSetChange(const EventObject& event)
+{
+ ::osl::MutexGuard aGuard( m_aMutex );
+ impl_checkDisposed_throw();
+
+ ::comphelper::OInterfaceIteratorHelper3 aIter(m_aRowSetApproveListeners);
+ if (aIter.hasMoreElements())
+ {
+ EventObject aEvt(event);
+ aEvt.Source = *this;
+ return aIter.next()->approveRowSetChange(aEvt);
+ }
+
+ return true;
+}
+
+// XRowSetApproveBroadcaster
+
+void SAL_CALL FormController::addRowSetApproveListener(const Reference< XRowSetApproveListener > & _rxListener)
+{
+ ::osl::MutexGuard aGuard( m_aMutex );
+ impl_checkDisposed_throw();
+
+ m_aRowSetApproveListeners.addInterface(_rxListener);
+}
+
+
+void SAL_CALL FormController::removeRowSetApproveListener(const Reference< XRowSetApproveListener > & _rxListener)
+{
+ ::osl::MutexGuard aGuard( m_aMutex );
+ impl_checkDisposed_throw();
+
+ m_aRowSetApproveListeners.removeInterface(_rxListener);
+}
+
+// XErrorListener
+
+void SAL_CALL FormController::errorOccured(const SQLErrorEvent& aEvent)
+{
+ ::osl::ClearableMutexGuard aGuard( m_aMutex );
+ impl_checkDisposed_throw();
+
+ ::comphelper::OInterfaceIteratorHelper3 aIter(m_aErrorListeners);
+ if (aIter.hasMoreElements())
+ {
+ SQLErrorEvent aEvt(aEvent);
+ aEvt.Source = *this;
+ aIter.next()->errorOccured(aEvt);
+ }
+ else
+ {
+ aGuard.clear();
+ displayException(aEvent, getDialogParentWindow(this));
+ }
+}
+
+// XErrorBroadcaster
+
+void SAL_CALL FormController::addSQLErrorListener(const Reference< XSQLErrorListener > & aListener)
+{
+ ::osl::MutexGuard aGuard( m_aMutex );
+ impl_checkDisposed_throw();
+
+ m_aErrorListeners.addInterface(aListener);
+}
+
+
+void SAL_CALL FormController::removeSQLErrorListener(const Reference< XSQLErrorListener > & aListener)
+{
+ ::osl::MutexGuard aGuard( m_aMutex );
+ impl_checkDisposed_throw();
+
+ m_aErrorListeners.removeInterface(aListener);
+}
+
+// XDatabaseParameterBroadcaster2
+
+void SAL_CALL FormController::addDatabaseParameterListener(const Reference< XDatabaseParameterListener > & aListener)
+{
+ ::osl::MutexGuard aGuard( m_aMutex );
+ impl_checkDisposed_throw();
+
+ m_aParameterListeners.addInterface(aListener);
+}
+
+
+void SAL_CALL FormController::removeDatabaseParameterListener(const Reference< XDatabaseParameterListener > & aListener)
+{
+ ::osl::MutexGuard aGuard( m_aMutex );
+ impl_checkDisposed_throw();
+
+ m_aParameterListeners.removeInterface(aListener);
+}
+
+// XDatabaseParameterBroadcaster
+
+void SAL_CALL FormController::addParameterListener(const Reference< XDatabaseParameterListener > & aListener)
+{
+ FormController::addDatabaseParameterListener( aListener );
+}
+
+
+void SAL_CALL FormController::removeParameterListener(const Reference< XDatabaseParameterListener > & aListener)
+{
+ FormController::removeDatabaseParameterListener( aListener );
+}
+
+// XDatabaseParameterListener
+
+sal_Bool SAL_CALL FormController::approveParameter(const DatabaseParameterEvent& aEvent)
+{
+ SolarMutexGuard aSolarGuard;
+ ::osl::MutexGuard aGuard( m_aMutex );
+ impl_checkDisposed_throw();
+
+ ::comphelper::OInterfaceIteratorHelper3 aIter(m_aParameterListeners);
+ if (aIter.hasMoreElements())
+ {
+ DatabaseParameterEvent aEvt(aEvent);
+ aEvt.Source = *this;
+ return aIter.next()->approveParameter(aEvt);
+ }
+ else
+ {
+ // default handling: instantiate an interaction handler and let it handle the parameter request
+ try
+ {
+ if ( !ensureInteractionHandler() )
+ return false;
+
+ // two continuations allowed: OK and Cancel
+ rtl::Reference<OParameterContinuation> pParamValues = new OParameterContinuation;
+ rtl::Reference<OInteractionAbort> pAbort = new OInteractionAbort;
+ // the request
+ ParametersRequest aRequest;
+ aRequest.Parameters = aEvent.Parameters;
+ aRequest.Connection = getConnection(Reference< XRowSet >(aEvent.Source, UNO_QUERY));
+ rtl::Reference<OInteractionRequest> pParamRequest = new OInteractionRequest(Any(aRequest));
+ // some knittings
+ pParamRequest->addContinuation(pParamValues);
+ pParamRequest->addContinuation(pAbort);
+
+ // handle the request
+ m_xInteractionHandler->handle(pParamRequest);
+
+ if (!pParamValues->wasSelected())
+ // canceled
+ return false;
+
+ // transfer the values into the parameter supplier
+ Sequence< PropertyValue > aFinalValues = pParamValues->getValues();
+ if (aFinalValues.getLength() != aRequest.Parameters->getCount())
+ {
+ OSL_FAIL("FormController::approveParameter: the InteractionHandler returned nonsense!");
+ return false;
+ }
+ const PropertyValue* pFinalValues = aFinalValues.getConstArray();
+ for (sal_Int32 i=0; i<aFinalValues.getLength(); ++i, ++pFinalValues)
+ {
+ Reference< XPropertySet > xParam(
+ aRequest.Parameters->getByIndex(i), css::uno::UNO_QUERY);
+ if (xParam.is())
+ {
+#ifdef DBG_UTIL
+ OUString sName;
+ xParam->getPropertyValue(FM_PROP_NAME) >>= sName;
+ DBG_ASSERT(sName == pFinalValues->Name, "FormController::approveParameter: suspicious value names!");
+#endif
+ try { xParam->setPropertyValue(FM_PROP_VALUE, pFinalValues->Value); }
+ catch(Exception&)
+ {
+ OSL_FAIL("FormController::approveParameter: setting one of the properties failed!");
+ }
+ }
+ }
+ }
+ catch(Exception&)
+ {
+ DBG_UNHANDLED_EXCEPTION("svx");
+ }
+ }
+ return true;
+}
+
+// XConfirmDeleteBroadcaster
+
+void SAL_CALL FormController::addConfirmDeleteListener(const Reference< XConfirmDeleteListener > & aListener)
+{
+ ::osl::MutexGuard aGuard( m_aMutex );
+ impl_checkDisposed_throw();
+
+ m_aDeleteListeners.addInterface(aListener);
+}
+
+
+void SAL_CALL FormController::removeConfirmDeleteListener(const Reference< XConfirmDeleteListener > & aListener)
+{
+ ::osl::MutexGuard aGuard( m_aMutex );
+ impl_checkDisposed_throw();
+
+ m_aDeleteListeners.removeInterface(aListener);
+}
+
+// XConfirmDeleteListener
+
+sal_Bool SAL_CALL FormController::confirmDelete(const RowChangeEvent& aEvent)
+{
+ ::osl::MutexGuard aGuard( m_aMutex );
+ impl_checkDisposed_throw();
+
+ ::comphelper::OInterfaceIteratorHelper3 aIter(m_aDeleteListeners);
+ if (aIter.hasMoreElements())
+ {
+ RowChangeEvent aEvt(aEvent);
+ aEvt.Source = *this;
+ return aIter.next()->confirmDelete(aEvt);
+ }
+ // default handling: instantiate an interaction handler and let it handle the request
+
+ OUString sTitle;
+ sal_Int32 nLength = aEvent.Rows;
+ if ( nLength > 1 )
+ {
+ sTitle = SvxResId( RID_STR_DELETECONFIRM_RECORDS );
+ sTitle = sTitle.replaceFirst( "#", OUString::number(nLength) );
+ }
+ else
+ sTitle = SvxResId( RID_STR_DELETECONFIRM_RECORD );
+
+ try
+ {
+ if ( !ensureInteractionHandler() )
+ return false;
+
+ // two continuations allowed: Yes and No
+ rtl::Reference<OInteractionApprove> pApprove = new OInteractionApprove;
+ rtl::Reference<OInteractionDisapprove> pDisapprove = new OInteractionDisapprove;
+
+ // the request
+ SQLWarning aWarning;
+ aWarning.Message = sTitle;
+ SQLWarning aDetails;
+ aDetails.Message = SvxResId(RID_STR_DELETECONFIRM);
+ aWarning.NextException <<= aDetails;
+
+ rtl::Reference<OInteractionRequest> pRequest = new OInteractionRequest( Any( aWarning ) );
+
+ // some knittings
+ pRequest->addContinuation( pApprove );
+ pRequest->addContinuation( pDisapprove );
+
+ // handle the request
+ m_xInteractionHandler->handle( pRequest );
+
+ if ( pApprove->wasSelected() )
+ return true;
+ }
+ catch( const Exception& )
+ {
+ DBG_UNHANDLED_EXCEPTION("svx");
+ }
+
+ return false;
+}
+
+
+void SAL_CALL FormController::invalidateFeatures( const Sequence< ::sal_Int16 >& Features )
+{
+ ::osl::MutexGuard aGuard( m_aMutex );
+ // for now, just copy the ids of the features, because...
+ m_aInvalidFeatures.insert( Features.begin(), Features.end() );
+
+ // ... we will do the real invalidation asynchronously
+ if ( !m_aFeatureInvalidationTimer.IsActive() )
+ m_aFeatureInvalidationTimer.Start();
+}
+
+
+void SAL_CALL FormController::invalidateAllFeatures( )
+{
+ ::osl::ClearableMutexGuard aGuard( m_aMutex );
+
+ Sequence< sal_Int16 > aInterceptedFeatures( comphelper::mapKeysToSequence(m_aFeatureDispatchers) );
+
+ aGuard.clear();
+ if ( aInterceptedFeatures.hasElements() )
+ invalidateFeatures( aInterceptedFeatures );
+}
+
+
+Reference< XDispatch >
+FormController::interceptedQueryDispatch( const URL& aURL,
+ const OUString& /*aTargetFrameName*/, sal_Int32 /*nSearchFlags*/)
+{
+ OSL_ENSURE( !impl_isDisposed_nofail(), "FormController: already disposed!" );
+ Reference< XDispatch > xReturn;
+ // dispatches handled by ourself
+ if ( ( aURL.Complete == FMURL_CONFIRM_DELETION )
+ || ( ( aURL.Complete == "private:/InteractionHandler" )
+ && ensureInteractionHandler()
+ )
+ )
+ xReturn = static_cast< XDispatch* >( this );
+
+ // dispatches of FormSlot-URLs we have to translate
+ if ( !xReturn.is() && m_xFormOperations.is() )
+ {
+ // find the slot id which corresponds to the URL
+ sal_Int32 nFeatureSlotId = svx::FeatureSlotTranslation::getControllerFeatureSlotIdForURL( aURL.Main );
+ sal_Int16 nFormFeature = ( nFeatureSlotId != -1 ) ? svx::FeatureSlotTranslation::getFormFeatureForSlotId( nFeatureSlotId ) : -1;
+ if ( nFormFeature > 0 )
+ {
+ // get the dispatcher for this feature, create if necessary
+ DispatcherContainer::const_iterator aDispatcherPos = m_aFeatureDispatchers.find( nFormFeature );
+ if ( aDispatcherPos == m_aFeatureDispatchers.end() )
+ {
+ aDispatcherPos = m_aFeatureDispatchers.emplace(
+ nFormFeature, new svx::OSingleFeatureDispatcher( aURL, nFormFeature, m_xFormOperations, m_aMutex )
+ ).first;
+ }
+
+ OSL_ENSURE( aDispatcherPos->second.is(), "FormController::interceptedQueryDispatch: should have a dispatcher by now!" );
+ return aDispatcherPos->second;
+ }
+ }
+
+ // no more to offer
+ return xReturn;
+}
+
+
+void SAL_CALL FormController::dispatch( const URL& _rURL, const Sequence< PropertyValue >& _rArgs )
+{
+ if ( _rArgs.getLength() != 1 )
+ {
+ OSL_FAIL( "FormController::dispatch: no arguments -> no dispatch!" );
+ return;
+ }
+
+ if ( _rURL.Complete == "private:/InteractionHandler" )
+ {
+ Reference< XInteractionRequest > xRequest;
+ OSL_VERIFY( _rArgs[0].Value >>= xRequest );
+ if ( xRequest.is() )
+ handle( xRequest );
+ return;
+ }
+
+ if ( _rURL.Complete == FMURL_CONFIRM_DELETION )
+ {
+ OSL_FAIL( "FormController::dispatch: How do you expect me to return something via this call?" );
+ // confirmDelete has a return value - dispatch hasn't
+ return;
+ }
+
+ OSL_FAIL( "FormController::dispatch: unknown URL!" );
+}
+
+
+void SAL_CALL FormController::addStatusListener( const Reference< XStatusListener >& _rxListener, const URL& _rURL )
+{
+ if (_rURL.Complete == FMURL_CONFIRM_DELETION)
+ {
+ if (_rxListener.is())
+ { // send an initial statusChanged event
+ FeatureStateEvent aEvent;
+ aEvent.FeatureURL = _rURL;
+ aEvent.IsEnabled = true;
+ _rxListener->statusChanged(aEvent);
+ // and don't add the listener at all (the status will never change)
+ }
+ }
+ else
+ OSL_FAIL("FormController::addStatusListener: invalid (unsupported) URL!");
+}
+
+
+Reference< XInterface > SAL_CALL FormController::getParent()
+{
+ return m_xParent;
+}
+
+
+void SAL_CALL FormController::setParent( const Reference< XInterface >& Parent)
+{
+ m_xParent = Parent;
+}
+
+
+void SAL_CALL FormController::removeStatusListener( const Reference< XStatusListener >& /*_rxListener*/, const URL& _rURL )
+{
+ OSL_ENSURE(_rURL.Complete == FMURL_CONFIRM_DELETION, "FormController::removeStatusListener: invalid (unsupported) URL!");
+ // we never really added the listener, so we don't need to remove it
+}
+
+
+Reference< XDispatchProviderInterceptor > FormController::createInterceptor(const Reference< XDispatchProviderInterception > & _xInterception)
+{
+ OSL_ENSURE( !impl_isDisposed_nofail(), "FormController: already disposed!" );
+#ifdef DBG_UTIL
+ // check if we already have an interceptor for the given object
+ for ( const auto & it : m_aControlDispatchInterceptors )
+ {
+ if (it->getIntercepted() == _xInterception)
+ OSL_FAIL("FormController::createInterceptor : we already do intercept this objects dispatches !");
+ }
+#endif
+
+ rtl::Reference<DispatchInterceptionMultiplexer> pInterceptor(new DispatchInterceptionMultiplexer( _xInterception, this ));
+ m_aControlDispatchInterceptors.push_back( pInterceptor );
+
+ return pInterceptor;
+}
+
+
+bool FormController::ensureInteractionHandler()
+{
+ if ( m_xInteractionHandler.is() )
+ return true;
+ if ( m_bAttemptedHandlerCreation )
+ return false;
+ m_bAttemptedHandlerCreation = true;
+
+ m_xInteractionHandler = InteractionHandler::createWithParent(m_xComponentContext,
+ getDialogParentWindow(this));
+ return m_xInteractionHandler.is();
+}
+
+
+void SAL_CALL FormController::handle( const Reference< XInteractionRequest >& _rRequest )
+{
+ if ( !ensureInteractionHandler() )
+ return;
+ m_xInteractionHandler->handle( _rRequest );
+}
+
+
+void FormController::deleteInterceptor(const Reference< XDispatchProviderInterception > & _xInterception)
+{
+ OSL_ENSURE( !impl_isDisposed_nofail(), "FormController: already disposed!" );
+ // search the interceptor responsible for the given object
+ auto aIter = std::find_if(m_aControlDispatchInterceptors.begin(), m_aControlDispatchInterceptors.end(),
+ [&_xInterception](const rtl::Reference<DispatchInterceptionMultiplexer>& rpInterceptor) {
+ return rpInterceptor->getIntercepted() == _xInterception;
+ });
+ if (aIter != m_aControlDispatchInterceptors.end())
+ {
+ // log off the interception from its interception object
+ (*aIter)->dispose();
+ // remove the interceptor from our array
+ m_aControlDispatchInterceptors.erase(aIter);
+ }
+}
+
+
+void FormController::implInvalidateCurrentControlDependentFeatures()
+{
+ Sequence< sal_Int16 > aCurrentControlDependentFeatures
+ {
+ FormFeature::SortAscending,
+ FormFeature::SortDescending,
+ FormFeature::AutoFilter,
+ FormFeature::RefreshCurrentControl
+ };
+
+ invalidateFeatures( aCurrentControlDependentFeatures );
+}
+
+
+void SAL_CALL FormController::columnChanged( const EventObject& /*_event*/ )
+{
+ implInvalidateCurrentControlDependentFeatures();
+}
+
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/form/formcontrolling.cxx b/svx/source/form/formcontrolling.cxx
new file mode 100644
index 000000000..221ae3584
--- /dev/null
+++ b/svx/source/form/formcontrolling.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 <sal/macros.h>
+#include <formcontrolling.hxx>
+#include <fmurl.hxx>
+#include <svx/svxids.hrc>
+#include <fmprop.hxx>
+#include <formcontroller.hxx>
+#include <svx/fmtools.hxx>
+
+#include <com/sun/star/form/runtime/FormOperations.hpp>
+#include <com/sun/star/form/runtime/FormFeature.hpp>
+#include <com/sun/star/beans/XPropertySet.hpp>
+#include <com/sun/star/sdbc/SQLException.hpp>
+
+#include <tools/diagnose_ex.h>
+#include <comphelper/anytostring.hxx>
+#include <comphelper/processfactory.hxx>
+#include <cppuhelper/exc_hlp.hxx>
+#include <osl/diagnose.h>
+
+#include <algorithm>
+
+
+namespace svx
+{
+
+
+ using ::com::sun::star::uno::Reference;
+ using ::com::sun::star::form::runtime::XFormController;
+ using ::com::sun::star::form::runtime::FormOperations;
+ using ::com::sun::star::uno::Exception;
+ using ::com::sun::star::sdbc::XRowSet;
+ using ::com::sun::star::form::runtime::FeatureState;
+ using ::com::sun::star::uno::Any;
+ using ::com::sun::star::uno::Sequence;
+ using ::com::sun::star::beans::NamedValue;
+ using ::com::sun::star::beans::XPropertySet;
+ using ::com::sun::star::uno::UNO_QUERY_THROW;
+ using ::com::sun::star::sdbc::SQLException;
+ using ::com::sun::star::sdb::SQLErrorEvent;
+ using ::com::sun::star::lang::EventObject;
+
+ namespace FormFeature = ::com::sun::star::form::runtime::FormFeature;
+
+
+ //= FeatureSlotTranslation
+
+ namespace
+ {
+ struct FeatureDescription
+ {
+ OUString sURL; // the URL
+ sal_Int32 nSlotId; // the SFX-compatible slot ID
+ sal_Int16 nFormFeature; // the css.form.runtime.FormFeature ID
+ };
+ typedef ::std::vector< FeatureDescription > FeatureDescriptions;
+
+
+ const FeatureDescriptions& getFeatureDescriptions()
+ {
+ static const FeatureDescriptions s_aFeatureDescriptions({
+ { OUString(FMURL_FORM_POSITION), SID_FM_RECORD_ABSOLUTE, FormFeature::MoveAbsolute },
+ { OUString(FMURL_FORM_RECORDCOUNT), SID_FM_RECORD_TOTAL, FormFeature::TotalRecords },
+ { OUString(FMURL_RECORD_MOVEFIRST), SID_FM_RECORD_FIRST, FormFeature::MoveToFirst },
+ { OUString(FMURL_RECORD_MOVEPREV), SID_FM_RECORD_PREV, FormFeature::MoveToPrevious },
+ { OUString(FMURL_RECORD_MOVENEXT), SID_FM_RECORD_NEXT, FormFeature::MoveToNext },
+ { OUString(FMURL_RECORD_MOVELAST), SID_FM_RECORD_LAST, FormFeature::MoveToLast },
+ { OUString(FMURL_RECORD_MOVETONEW), SID_FM_RECORD_NEW, FormFeature::MoveToInsertRow },
+ { OUString(FMURL_RECORD_SAVE), SID_FM_RECORD_SAVE, FormFeature::SaveRecordChanges },
+ { OUString(FMURL_RECORD_DELETE), SID_FM_RECORD_DELETE, FormFeature::DeleteRecord },
+ { OUString(FMURL_FORM_REFRESH), SID_FM_REFRESH, FormFeature::ReloadForm },
+ { OUString(FMURL_FORM_REFRESH_CURRENT_CONTROL),
+ SID_FM_REFRESH_FORM_CONTROL,FormFeature::RefreshCurrentControl },
+ { OUString(FMURL_RECORD_UNDO), SID_FM_RECORD_UNDO, FormFeature::UndoRecordChanges },
+ { OUString(FMURL_FORM_SORT_UP), SID_FM_SORTUP, FormFeature::SortAscending },
+ { OUString(FMURL_FORM_SORT_DOWN), SID_FM_SORTDOWN, FormFeature::SortDescending },
+ { OUString(FMURL_FORM_SORT), SID_FM_ORDERCRIT, FormFeature::InteractiveSort },
+ { OUString(FMURL_FORM_AUTO_FILTER), SID_FM_AUTOFILTER, FormFeature::AutoFilter },
+ { OUString(FMURL_FORM_FILTER), SID_FM_FILTERCRIT, FormFeature::InteractiveFilter },
+ { OUString(FMURL_FORM_APPLY_FILTER), SID_FM_FORM_FILTERED, FormFeature::ToggleApplyFilter },
+ { OUString(FMURL_FORM_REMOVE_FILTER), SID_FM_REMOVE_FILTER_SORT, FormFeature::RemoveFilterAndSort }
+ });
+ return s_aFeatureDescriptions;
+ }
+ }
+
+
+ namespace
+ {
+
+ struct MatchFeatureDescriptionByURL
+ {
+ const OUString& m_rURL;
+ explicit MatchFeatureDescriptionByURL( const OUString& _rURL ) :m_rURL( _rURL ) { }
+
+ bool operator()( const FeatureDescription& _compare )
+ {
+ return m_rURL == _compare.sURL;
+ }
+ };
+
+
+ struct MatchFeatureDescriptionBySlotId
+ {
+ sal_Int32 m_nSlotId;
+ explicit MatchFeatureDescriptionBySlotId( sal_Int32 _nSlotId ) :m_nSlotId( _nSlotId ) { }
+
+ bool operator()( const FeatureDescription& _compare )
+ {
+ return m_nSlotId == _compare.nSlotId;
+ }
+ };
+
+
+ struct MatchFeatureDescriptionByFormFeature
+ {
+ sal_Int32 m_nFormFeature;
+ explicit MatchFeatureDescriptionByFormFeature( sal_Int32 _nFormFeature ) :m_nFormFeature( _nFormFeature ) { }
+
+ bool operator()( const FeatureDescription& _compare )
+ {
+ return m_nFormFeature == _compare.nFormFeature;
+ }
+ };
+
+
+ struct FormFeatureToSlotId
+ {
+ sal_Int32 operator()( sal_Int16 FormFeature )
+ {
+ return FeatureSlotTranslation::getSlotIdForFormFeature( FormFeature );
+ }
+ };
+ }
+
+
+ sal_Int32 FeatureSlotTranslation::getControllerFeatureSlotIdForURL( const OUString& _rMainURL )
+ {
+ const FeatureDescriptions& rDescriptions( getFeatureDescriptions() );
+ FeatureDescriptions::const_iterator pos = ::std::find_if( rDescriptions.begin(), rDescriptions.end(), MatchFeatureDescriptionByURL( _rMainURL ) );
+ return ( pos != rDescriptions.end() ) ? pos->nSlotId : -1;
+ }
+
+
+ sal_Int16 FeatureSlotTranslation::getFormFeatureForSlotId( sal_Int32 _nSlotId )
+ {
+ const FeatureDescriptions& rDescriptions( getFeatureDescriptions() );
+ FeatureDescriptions::const_iterator pos = ::std::find_if( rDescriptions.begin(), rDescriptions.end(), MatchFeatureDescriptionBySlotId( _nSlotId ) );
+ OSL_ENSURE( pos != rDescriptions.end(), "FeatureSlotTranslation::getFormFeatureForSlotId: not found!" );
+ return ( pos != rDescriptions.end() ) ? pos->nFormFeature : -1;
+ }
+
+
+ sal_Int32 FeatureSlotTranslation::getSlotIdForFormFeature( sal_Int16 _nFormFeature )
+ {
+ const FeatureDescriptions& rDescriptions( getFeatureDescriptions() );
+ FeatureDescriptions::const_iterator pos = ::std::find_if( rDescriptions.begin(), rDescriptions.end(), MatchFeatureDescriptionByFormFeature( _nFormFeature ) );
+ OSL_ENSURE( pos != rDescriptions.end(), "FeatureSlotTranslation::getSlotIdForFormFeature: not found!" );
+ return ( pos != rDescriptions.end() ) ? pos->nSlotId : -1;
+ }
+
+ ControllerFeatures::ControllerFeatures( IControllerFeatureInvalidation* _pInvalidationCallback )
+ :m_pInvalidationCallback( _pInvalidationCallback )
+ {
+ }
+
+
+ ControllerFeatures::ControllerFeatures( const Reference< XFormController >& _rxController )
+ :m_pInvalidationCallback( nullptr )
+ {
+ assign( _rxController );
+ }
+
+
+ void ControllerFeatures::assign( const Reference< XFormController >& _rxController )
+ {
+ dispose();
+ m_pImpl = new FormControllerHelper( _rxController, m_pInvalidationCallback );
+ }
+
+
+ ControllerFeatures::~ControllerFeatures()
+ {
+ dispose();
+ }
+
+
+ void ControllerFeatures::dispose()
+ {
+ if ( m_pImpl.is() )
+ {
+ m_pImpl->dispose();
+ m_pImpl.clear();
+ }
+ }
+
+ FormControllerHelper::FormControllerHelper( const Reference< XFormController >& _rxController, IControllerFeatureInvalidation* _pInvalidationCallback )
+ :m_pInvalidationCallback( _pInvalidationCallback )
+ {
+ osl_atomic_increment( &m_refCount );
+ try
+ {
+ m_xFormOperations = FormOperations::createWithFormController( comphelper::getProcessComponentContext(), _rxController );
+ if ( m_xFormOperations.is() )
+ m_xFormOperations->setFeatureInvalidation( this );
+ }
+ catch( const Exception& )
+ {
+ DBG_UNHANDLED_EXCEPTION("svx");
+ }
+ osl_atomic_decrement( &m_refCount );
+ }
+
+
+ FormControllerHelper::~FormControllerHelper( )
+ {
+ try
+ {
+ acquire();
+ dispose();
+ }
+ catch( const Exception& )
+ {
+ DBG_UNHANDLED_EXCEPTION("svx");
+ }
+ }
+
+
+ void FormControllerHelper::dispose()
+ {
+ if ( m_xFormOperations.is() )
+ m_xFormOperations->dispose();
+ m_xFormOperations.clear();
+ }
+
+
+ bool FormControllerHelper::isEnabled( sal_Int32 _nSlotId ) const
+ {
+ if ( !m_xFormOperations.is() )
+ return false;
+ return m_xFormOperations->isEnabled( FeatureSlotTranslation::getFormFeatureForSlotId( _nSlotId ) );
+ }
+
+
+ Reference< XRowSet > FormControllerHelper::getCursor() const
+ {
+ Reference< XRowSet > xCursor;
+ if ( m_xFormOperations.is() )
+ xCursor = m_xFormOperations->getCursor();
+ return xCursor;
+ }
+
+
+ void FormControllerHelper::getState( sal_Int32 _nSlotId, FeatureState& _rState ) const
+ {
+ if ( m_xFormOperations.is() )
+ _rState = m_xFormOperations->getState( FeatureSlotTranslation::getFormFeatureForSlotId( _nSlotId ) );
+ }
+
+
+ bool FormControllerHelper::commitCurrentControl( ) const
+ {
+ return impl_operateForm_nothrow( COMMIT_CONTROL );
+ }
+
+
+ bool FormControllerHelper::commitCurrentRecord() const
+ {
+ return impl_operateForm_nothrow( COMMIT_RECORD );
+ }
+
+
+ void FormControllerHelper::execute( sal_Int32 _nSlotId, const OUString& _rParamName, const Any& _rParamValue ) const
+ {
+ Sequence< NamedValue > aArguments { { _rParamName, _rParamValue } };
+ impl_operateForm_nothrow( EXECUTE_ARGS, FeatureSlotTranslation::getFormFeatureForSlotId( _nSlotId ), aArguments );
+ }
+
+
+ bool FormControllerHelper::impl_operateForm_nothrow( const FormOperation _eWhat, const sal_Int16 _nFeature,
+ const Sequence< NamedValue >& _rArguments ) const
+ {
+ if ( !m_xFormOperations.is() )
+ return false;
+
+ Any aError;
+ bool bSuccess = false;
+ const_cast< FormControllerHelper* >( this )->m_aOperationError.clear();
+ try
+ {
+ // to prevent the controller from displaying any error messages which happen while we operate on it,
+ // we add ourself as XSQLErrorListener. By contract, a FormController displays errors if and only if
+ // no SQLErrorListeners are registered.
+ m_xFormOperations->getController()->addSQLErrorListener( const_cast< FormControllerHelper* >(this) );
+
+ switch ( _eWhat )
+ {
+ case COMMIT_CONTROL:
+ bSuccess = m_xFormOperations->commitCurrentControl();
+ break;
+
+ case COMMIT_RECORD:
+ {
+ sal_Bool bDummy( false );
+ bSuccess = m_xFormOperations->commitCurrentRecord( bDummy );
+ }
+ break;
+
+ case EXECUTE:
+ m_xFormOperations->execute( _nFeature );
+ bSuccess = true;
+ break;
+
+ case EXECUTE_ARGS:
+ m_xFormOperations->executeWithArguments( _nFeature, _rArguments );
+ bSuccess = true;
+ break;
+ }
+ }
+ catch ( const SQLException& )
+ {
+ m_xFormOperations->getController()->removeSQLErrorListener( const_cast< FormControllerHelper* >(this) );
+ aError = ::cppu::getCaughtException();
+ }
+ catch( const Exception& )
+ {
+ m_xFormOperations->getController()->removeSQLErrorListener( const_cast< FormControllerHelper* >(this) );
+ SQLException aFallbackError;
+ aFallbackError.Message = ::comphelper::anyToString( ::cppu::getCaughtException() );
+ aError <<= aFallbackError;
+ }
+
+ if ( bSuccess )
+ return true;
+
+ // display the error. Prefer the one reported in errorOccurred over the one caught.
+ if ( m_aOperationError.hasValue() )
+ displayException(m_aOperationError, svxform::FormController::getDialogParentWindow(m_xFormOperations->getController()));
+ else if ( aError.hasValue() )
+ displayException(aError, svxform::FormController::getDialogParentWindow(m_xFormOperations->getController()));
+ else
+ OSL_FAIL( "FormControllerHelper::impl_operateForm_nothrow: no success, but no error?" );
+
+ return false;
+ }
+
+
+ void FormControllerHelper::execute( sal_Int32 _nSlotId ) const
+ {
+ impl_operateForm_nothrow( EXECUTE, FeatureSlotTranslation::getFormFeatureForSlotId( _nSlotId ),
+ Sequence< NamedValue >() );
+ }
+
+
+ void SAL_CALL FormControllerHelper::invalidateFeatures( const Sequence< ::sal_Int16 >& Features )
+ {
+ if ( !m_pInvalidationCallback )
+ // nobody's interested in ...
+ return;
+
+ ::std::vector< sal_Int32 > aFeatures( Features.getLength() );
+ ::std::transform(
+ Features.begin(),
+ Features.end(),
+ aFeatures.begin(),
+ FormFeatureToSlotId()
+ );
+
+ m_pInvalidationCallback->invalidateFeatures( aFeatures );
+ }
+
+
+ void SAL_CALL FormControllerHelper::invalidateAllFeatures()
+ {
+ if ( !m_pInvalidationCallback )
+ // nobody's interested in ...
+ return;
+
+ // actually, it's a little bit more than the supported features,
+ // but on the medium term, we are to support everything listed
+ // here
+ ::std::vector< sal_Int32 > aSupportedFeatures;
+ const sal_Int32 pSupportedFeatures[] =
+ {
+ SID_FM_RECORD_FIRST,
+ SID_FM_RECORD_NEXT,
+ SID_FM_RECORD_PREV,
+ SID_FM_RECORD_LAST,
+ SID_FM_RECORD_NEW,
+ SID_FM_RECORD_DELETE,
+ SID_FM_RECORD_ABSOLUTE,
+ SID_FM_RECORD_TOTAL,
+ SID_FM_RECORD_SAVE,
+ SID_FM_RECORD_UNDO,
+ SID_FM_REMOVE_FILTER_SORT,
+ SID_FM_SORTUP,
+ SID_FM_SORTDOWN,
+ SID_FM_ORDERCRIT,
+ SID_FM_AUTOFILTER,
+ SID_FM_FILTERCRIT,
+ SID_FM_FORM_FILTERED,
+ SID_FM_REFRESH,
+ SID_FM_REFRESH_FORM_CONTROL,
+ SID_FM_SEARCH,
+ SID_FM_FILTER_START,
+ SID_FM_VIEW_AS_GRID
+ };
+ sal_Int32 nFeatureCount = SAL_N_ELEMENTS( pSupportedFeatures );
+ aSupportedFeatures.reserve(nFeatureCount); // work around GCC12 spurious -Werror=stringop-overflow=
+ aSupportedFeatures.insert( aSupportedFeatures.begin(), pSupportedFeatures, pSupportedFeatures + nFeatureCount );
+
+ m_pInvalidationCallback->invalidateFeatures( aSupportedFeatures );
+ }
+
+
+ void SAL_CALL FormControllerHelper::errorOccured( const SQLErrorEvent& Event )
+ {
+ OSL_ENSURE( !m_aOperationError.hasValue(), "FormControllerHelper::errorOccurred: two errors during one operation?" );
+ m_aOperationError = Event.Reason;
+ }
+
+
+ void SAL_CALL FormControllerHelper::disposing( const EventObject& /*_Source*/ )
+ {
+ // not interested in
+ }
+
+
+ bool FormControllerHelper::isInsertionRow() const
+ {
+ bool bIs = false;
+ if ( m_xFormOperations.is() )
+ bIs = m_xFormOperations->isInsertionRow();
+ return bIs;
+ }
+
+
+ bool FormControllerHelper::isModifiedRow() const
+ {
+ bool bIs = false;
+ if ( m_xFormOperations.is() )
+ bIs = m_xFormOperations->isModifiedRow();
+ return bIs;
+ }
+
+ bool FormControllerHelper::canDoFormFilter() const
+ {
+ if ( !m_xFormOperations.is() )
+ return false;
+
+ bool bCanDo = false;
+ try
+ {
+ Reference< XPropertySet > xCursorProperties( m_xFormOperations->getCursor(), UNO_QUERY_THROW );
+
+ bool bEscapeProcessing( false );
+ OSL_VERIFY( xCursorProperties->getPropertyValue( FM_PROP_ESCAPE_PROCESSING ) >>= bEscapeProcessing );
+
+ OUString sActiveCommand;
+ OSL_VERIFY( xCursorProperties->getPropertyValue( FM_PROP_ACTIVECOMMAND ) >>= sActiveCommand );
+
+ bool bInsertOnlyForm( false );
+ OSL_VERIFY( xCursorProperties->getPropertyValue( FM_PROP_INSERTONLY ) >>= bInsertOnlyForm );
+
+ bCanDo = bEscapeProcessing && !sActiveCommand.isEmpty() && !bInsertOnlyForm;
+ }
+ catch( const Exception& )
+ {
+ DBG_UNHANDLED_EXCEPTION("svx");
+ }
+ return bCanDo;
+ }
+
+
+}
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/form/formdispatchinterceptor.cxx b/svx/source/form/formdispatchinterceptor.cxx
new file mode 100644
index 000000000..cc75b0b01
--- /dev/null
+++ b/svx/source/form/formdispatchinterceptor.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 <formdispatchinterceptor.hxx>
+
+namespace svxform
+{
+ using ::com::sun::star::uno::Reference;
+ using ::com::sun::star::uno::UNO_QUERY;
+ using ::com::sun::star::uno::Sequence;
+ using ::com::sun::star::frame::XDispatchProviderInterception;
+ using ::com::sun::star::frame::XDispatchProviderInterceptor;
+ using ::com::sun::star::lang::XComponent;
+ using ::com::sun::star::util::URL;
+ using ::com::sun::star::frame::XDispatch;
+ using ::com::sun::star::frame::DispatchDescriptor;
+ using ::com::sun::star::frame::XDispatchProvider;
+ using ::com::sun::star::lang::EventObject;
+
+ DispatchInterceptionMultiplexer::DispatchInterceptionMultiplexer(
+ const Reference< XDispatchProviderInterception >& _rxToIntercept, DispatchInterceptor* _pMaster )
+ :DispatchInterceptionMultiplexer_BASE(_pMaster && _pMaster->getInterceptorMutex() ? *_pMaster->getInterceptorMutex() : m_aFallback)
+ ,m_pMutex( _pMaster && _pMaster->getInterceptorMutex() ? _pMaster->getInterceptorMutex() : &m_aFallback )
+ ,m_xIntercepted(_rxToIntercept)
+ ,m_bListening(false)
+ ,m_pMaster(_pMaster)
+ {
+
+ ::osl::MutexGuard aGuard( *m_pMutex );
+ osl_atomic_increment(&m_refCount);
+ if (_rxToIntercept.is())
+ {
+ _rxToIntercept->registerDispatchProviderInterceptor(static_cast<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
+ Reference< XComponent> xInterceptedComponent(_rxToIntercept, UNO_QUERY);
+ if (xInterceptedComponent.is())
+ {
+ xInterceptedComponent->addEventListener(this);
+ m_bListening = true;
+ }
+ }
+ osl_atomic_decrement(&m_refCount);
+ }
+
+
+ DispatchInterceptionMultiplexer::~DispatchInterceptionMultiplexer()
+ {
+ if (!rBHelper.bDisposed)
+ dispose();
+
+ }
+
+
+ Reference< XDispatch > SAL_CALL DispatchInterceptionMultiplexer::queryDispatch( const URL& aURL, const OUString& aTargetFrameName, sal_Int32 nSearchFlags )
+ {
+ ::osl::MutexGuard aGuard( *m_pMutex );
+ Reference< XDispatch> xResult;
+ // ask our 'real' interceptor
+ if (m_pMaster)
+ xResult = m_pMaster->interceptedQueryDispatch( aURL, aTargetFrameName, nSearchFlags);
+
+ // ask our slave provider
+ if (!xResult.is() && m_xSlaveDispatcher.is())
+ xResult = m_xSlaveDispatcher->queryDispatch(aURL, aTargetFrameName, nSearchFlags);
+
+ return xResult;
+ }
+
+
+ Sequence< Reference< XDispatch > > SAL_CALL
+ DispatchInterceptionMultiplexer::queryDispatches( const Sequence< DispatchDescriptor >& aDescripts )
+ {
+ ::osl::MutexGuard aGuard( *m_pMutex );
+ Sequence< Reference< XDispatch> > aReturn(aDescripts.getLength());
+ std::transform(aDescripts.begin(), aDescripts.end(), aReturn.getArray(),
+ [this](const DispatchDescriptor& rDescript) -> Reference< XDispatch> {
+ return queryDispatch(rDescript.FeatureURL, rDescript.FrameName, rDescript.SearchFlags); });
+ return aReturn;
+ }
+
+
+ Reference< XDispatchProvider > SAL_CALL DispatchInterceptionMultiplexer::getSlaveDispatchProvider( )
+ {
+ ::osl::MutexGuard aGuard( *m_pMutex );
+ return m_xSlaveDispatcher;
+ }
+
+
+ void SAL_CALL DispatchInterceptionMultiplexer::setSlaveDispatchProvider(const Reference< XDispatchProvider>& xNewDispatchProvider)
+ {
+ ::osl::MutexGuard aGuard( *m_pMutex );
+ m_xSlaveDispatcher = xNewDispatchProvider;
+ }
+
+
+ Reference< XDispatchProvider> SAL_CALL DispatchInterceptionMultiplexer::getMasterDispatchProvider()
+ {
+ ::osl::MutexGuard aGuard( *m_pMutex );
+ return m_xMasterDispatcher;
+ }
+
+
+ void SAL_CALL DispatchInterceptionMultiplexer::setMasterDispatchProvider(const Reference< XDispatchProvider>& xNewSupplier)
+ {
+ ::osl::MutexGuard aGuard( *m_pMutex );
+ m_xMasterDispatcher = xNewSupplier;
+ }
+
+
+ void SAL_CALL DispatchInterceptionMultiplexer::disposing(const EventObject& Source)
+ {
+ if (m_bListening)
+ {
+ Reference< XDispatchProviderInterception > xIntercepted(m_xIntercepted.get(), UNO_QUERY);
+ if (Source.Source == xIntercepted)
+ ImplDetach();
+ }
+ }
+
+
+ void DispatchInterceptionMultiplexer::ImplDetach()
+ {
+ ::osl::MutexGuard aGuard( *m_pMutex );
+ OSL_ENSURE(m_bListening, "DispatchInterceptionMultiplexer::ImplDetach: invalid call!");
+
+ // deregister ourself from the interception component
+ Reference< XDispatchProviderInterception > xIntercepted(m_xIntercepted.get(), UNO_QUERY);
+ if (xIntercepted.is())
+ xIntercepted->releaseDispatchProviderInterceptor(static_cast<XDispatchProviderInterceptor*>(this));
+
+ // m_xIntercepted.clear();
+ // Don't reset m_xIntercepted: It may be needed by our owner to check for which object we were
+ // responsible. As we hold the object with a weak reference only, this should be no problem.
+ // 88936 - 23.07.2001 - frank.schoenheit@sun.com
+ m_pMaster = nullptr;
+ m_pMutex = &m_aFallback;
+ m_bListening = false;
+ }
+
+
+ void DispatchInterceptionMultiplexer::disposing()
+ {
+ // remove ourself as event listener from the interception component
+ if (m_bListening)
+ {
+ Reference< XComponent> xInterceptedComponent(m_xIntercepted.get(), UNO_QUERY);
+ if (xInterceptedComponent.is())
+ xInterceptedComponent->removeEventListener(static_cast<XEventListener*>(this));
+
+ // detach from the interception component
+ ImplDetach();
+ }
+ }
+
+
+}
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/form/formfeaturedispatcher.cxx b/svx/source/form/formfeaturedispatcher.cxx
new file mode 100644
index 000000000..b90cd4c92
--- /dev/null
+++ b/svx/source/form/formfeaturedispatcher.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 <formfeaturedispatcher.hxx>
+
+#include <comphelper/namedvaluecollection.hxx>
+#include <tools/diagnose_ex.h>
+
+
+namespace svx
+{
+
+
+ using namespace ::com::sun::star::uno;
+ using namespace ::com::sun::star::lang;
+ using namespace ::com::sun::star::frame;
+ using namespace ::com::sun::star::beans;
+ using namespace ::com::sun::star::util;
+ using namespace ::com::sun::star::form::runtime;
+
+ OSingleFeatureDispatcher::OSingleFeatureDispatcher( const URL& _rFeatureURL, const sal_Int16 _nFormFeature,
+ const Reference< XFormOperations >& _rxFormOperations, ::osl::Mutex& _rMutex )
+ :m_rMutex( _rMutex )
+ ,m_aStatusListeners( _rMutex )
+ ,m_xFormOperations( _rxFormOperations )
+ ,m_aFeatureURL( _rFeatureURL )
+ ,m_nFormFeature( _nFormFeature )
+ ,m_bLastKnownEnabled( false )
+ {
+ }
+
+
+ void OSingleFeatureDispatcher::getUnoState( FeatureStateEvent& /* [out] */ _rState ) const
+ {
+ _rState.Source = *const_cast< OSingleFeatureDispatcher* >( this );
+
+ FeatureState aState( m_xFormOperations->getState( m_nFormFeature ) );
+
+ _rState.FeatureURL = m_aFeatureURL;
+ _rState.IsEnabled = aState.Enabled;
+ _rState.Requery = false;
+ _rState.State = aState.State;
+ }
+
+
+ void OSingleFeatureDispatcher::updateAllListeners()
+ {
+ ::osl::ClearableMutexGuard aGuard( m_rMutex );
+
+ FeatureStateEvent aUnoState;
+ getUnoState( aUnoState );
+
+ if ( ( m_aLastKnownState == aUnoState.State ) && ( m_bLastKnownEnabled == bool(aUnoState.IsEnabled) ) )
+ return;
+
+ m_aLastKnownState = aUnoState.State;
+ m_bLastKnownEnabled = aUnoState.IsEnabled;
+
+ notifyStatus( nullptr, aGuard );
+ }
+
+
+ void OSingleFeatureDispatcher::notifyStatus( const Reference< XStatusListener >& _rxListener, ::osl::ClearableMutexGuard& _rFreeForNotification )
+ {
+ FeatureStateEvent aUnoState;
+ getUnoState( aUnoState );
+
+ if ( _rxListener.is() )
+ {
+ try
+ {
+ _rFreeForNotification.clear();
+ _rxListener->statusChanged( aUnoState );
+ }
+ catch( const Exception& )
+ {
+ TOOLS_WARN_EXCEPTION( "svx", "OSingleFeatureDispatcher::notifyStatus" );
+ }
+ }
+ else
+ {
+ ::comphelper::OInterfaceIteratorHelper3 aIter( m_aStatusListeners );
+ _rFreeForNotification.clear();
+
+ while ( aIter.hasMoreElements() )
+ {
+ try
+ {
+ aIter.next()->statusChanged( aUnoState );
+ }
+ catch( const DisposedException& )
+ {
+ TOOLS_WARN_EXCEPTION("svx.form",
+ "caught a DisposedException - removing the listener!");
+ aIter.remove( );
+ }
+ catch( const Exception& )
+ {
+ TOOLS_WARN_EXCEPTION(
+ "svx.form",
+ "caught a generic exception while notifying a single listener!");
+ }
+ }
+ }
+ }
+
+
+ void SAL_CALL OSingleFeatureDispatcher::dispatch( const URL& _rURL, const Sequence< PropertyValue >& _rArguments )
+ {
+ ::osl::ClearableMutexGuard aGuard( m_rMutex );
+
+ OSL_ENSURE( _rURL.Complete == m_aFeatureURL.Complete, "OSingleFeatureDispatcher::dispatch: not responsible for this URL!" );
+
+ if ( !m_xFormOperations->isEnabled( m_nFormFeature ) )
+ return;
+
+ // release our mutex before executing the command
+ sal_Int16 nFormFeature( m_nFormFeature );
+ Reference< XFormOperations > xFormOperations( m_xFormOperations );
+ aGuard.clear();
+
+ try
+ {
+ if ( !_rArguments.hasElements() )
+ {
+ xFormOperations->execute( nFormFeature );
+ }
+ else
+ { // at the moment we only support one parameter
+ ::comphelper::NamedValueCollection aArgs( _rArguments );
+ xFormOperations->executeWithArguments( nFormFeature, aArgs.getNamedValues() );
+ }
+ }
+ catch( const RuntimeException& )
+ {
+ throw;
+ }
+ catch( const Exception& )
+ {
+ DBG_UNHANDLED_EXCEPTION("svx");
+ }
+ }
+
+
+ void SAL_CALL OSingleFeatureDispatcher::addStatusListener( const Reference< XStatusListener >& _rxControl, const URL& _rURL )
+ {
+ OSL_ENSURE( _rURL.Complete == m_aFeatureURL.Complete, "OSingleFeatureDispatcher::addStatusListener: unexpected URL!" );
+ OSL_ENSURE( _rxControl.is(), "OSingleFeatureDispatcher::addStatusListener: senseless call!" );
+ if ( !_rxControl.is() )
+ return;
+
+ ::osl::ClearableMutexGuard aGuard( m_rMutex );
+
+ m_aStatusListeners.addInterface( _rxControl );
+
+ // initially update the status
+ notifyStatus( _rxControl, aGuard );
+ }
+
+
+ void SAL_CALL OSingleFeatureDispatcher::removeStatusListener( const Reference< XStatusListener >& _rxControl, const URL& _rURL )
+ {
+ OSL_ENSURE( _rURL.Complete == m_aFeatureURL.Complete, "OSingleFeatureDispatcher::removeStatusListener: unexpected URL!" );
+ OSL_ENSURE( _rxControl.is(), "OSingleFeatureDispatcher::removeStatusListener: senseless call!" );
+ if ( !_rxControl.is() )
+ return;
+
+ ::osl::MutexGuard aGuard( m_rMutex );
+
+ m_aStatusListeners.removeInterface( _rxControl );
+ }
+
+
+}
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/form/formtoolbars.cxx b/svx/source/form/formtoolbars.cxx
new file mode 100644
index 000000000..d46e7cc1b
--- /dev/null
+++ b/svx/source/form/formtoolbars.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 <formtoolbars.hxx>
+
+#include <com/sun/star/beans/XPropertySet.hpp>
+#include <osl/diagnose.h>
+#include <tools/diagnose_ex.h>
+
+#include <svx/svxids.hrc>
+
+
+namespace svxform
+{
+
+
+ using namespace ::com::sun::star::uno;
+ using namespace ::com::sun::star::frame;
+ using namespace ::com::sun::star::beans;
+
+ FormToolboxes::FormToolboxes( const Reference< XFrame >& _rxFrame )
+ {
+ // the layout manager
+ Reference< XPropertySet > xFrameProps( _rxFrame, UNO_QUERY );
+ if ( xFrameProps.is() )
+ xFrameProps->getPropertyValue("LayoutManager") >>= m_xLayouter;
+ }
+
+
+ void FormToolboxes::toggleToolbox( sal_uInt16 _nSlotId ) const
+ {
+ try
+ {
+ Reference< XLayoutManager > xManager( m_xLayouter );
+ OSL_ENSURE( xManager. is(), "FormToolboxes::toggleToolbox: couldn't obtain the layout manager!" );
+ if ( xManager. is() )
+ {
+ OUString sToolboxResource( getToolboxResourceName( _nSlotId ) );
+ if ( xManager->isElementVisible( sToolboxResource ) )
+ {
+ xManager->hideElement( sToolboxResource );
+ xManager->destroyElement( sToolboxResource );
+ }
+ else
+ {
+ xManager->createElement( sToolboxResource );
+ xManager->showElement( sToolboxResource );
+ }
+ }
+ }
+ catch( const Exception& )
+ {
+ TOOLS_WARN_EXCEPTION( "svx", "FormToolboxes::toggleToolbox" );
+ }
+ }
+
+
+ bool FormToolboxes::isToolboxVisible( sal_uInt16 _nSlotId ) const
+ {
+ return m_xLayouter.is() && m_xLayouter->isElementVisible(
+ getToolboxResourceName( _nSlotId ) );
+ }
+
+
+ OUString FormToolboxes::getToolboxResourceName( sal_uInt16 _nSlotId )
+ {
+ OSL_ENSURE( _nSlotId == SID_FM_FORM_DESIGN_TOOLS ,
+ "FormToolboxes::getToolboxResourceName: unsupported slot!" );
+
+ return "private:resource/toolbar/formdesign";
+ }
+
+
+}
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/form/labelitemwindow.cxx b/svx/source/form/labelitemwindow.cxx
new file mode 100644
index 000000000..77965cbd4
--- /dev/null
+++ b/svx/source/form/labelitemwindow.cxx
@@ -0,0 +1,45 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4; fill-column: 100 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+#include <svx/labelitemwindow.hxx>
+
+LabelItemWindow::LabelItemWindow(vcl::Window* pParent, const OUString& rLabel)
+ : InterimItemWindow(pParent, "svx/ui/labelbox.ui", "LabelBox")
+ , m_xLabel(m_xBuilder->weld_label("label"))
+{
+ InitControlBase(m_xLabel.get());
+
+ m_xLabel->set_label(rLabel);
+
+ SetOptimalSize();
+
+ m_xLabel->set_toolbar_background();
+}
+
+void LabelItemWindow::SetOptimalSize()
+{
+ Size aSize(m_xLabel->get_preferred_size());
+ aSize.AdjustWidth(12);
+
+ SetSizePixel(aSize);
+}
+
+void LabelItemWindow::set_label(const OUString& rLabel) { m_xLabel->set_label(rLabel); }
+
+OUString LabelItemWindow::get_label() const { return m_xLabel->get_label(); }
+
+void LabelItemWindow::dispose()
+{
+ m_xLabel.reset();
+ InterimItemWindow::dispose();
+}
+
+LabelItemWindow::~LabelItemWindow() { disposeOnce(); }
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab cinoptions=b1,g0,N-s cinkeys+=0=break: */
diff --git a/svx/source/form/legacyformcontroller.cxx b/svx/source/form/legacyformcontroller.cxx
new file mode 100644
index 000000000..94b2ae983
--- /dev/null
+++ b/svx/source/form/legacyformcontroller.cxx
@@ -0,0 +1,201 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+
+#include <fmservs.hxx>
+
+#include <com/sun/star/form/XFormController.hpp>
+#include <com/sun/star/form/runtime/FormController.hpp>
+#include <com/sun/star/lang/XMultiServiceFactory.hpp>
+#include <com/sun/star/lang/XServiceInfo.hpp>
+
+#include <cppuhelper/implbase.hxx>
+#include <cppuhelper/supportsservice.hxx>
+#include <comphelper/processfactory.hxx>
+
+
+namespace svxform
+{
+
+
+ using ::com::sun::star::uno::Reference;
+ using ::com::sun::star::uno::XInterface;
+ using ::com::sun::star::uno::Sequence;
+ using ::com::sun::star::uno::XComponentContext;
+ using ::com::sun::star::lang::XMultiServiceFactory;
+ using ::com::sun::star::awt::XControl;
+ using ::com::sun::star::awt::XTabControllerModel;
+ using ::com::sun::star::awt::XControlContainer;
+ using ::com::sun::star::lang::XServiceInfo;
+ using ::com::sun::star::form::runtime::FormController;
+
+ using namespace ::com::sun::star;
+
+
+ //= LegacyFormController
+
+ typedef ::cppu::WeakImplHelper < form::XFormController
+ , XServiceInfo
+ > LegacyFormController_Base;
+
+ namespace {
+
+ /** is an implementation of the legacy form controller service, namely css.form.FormController, supporting the
+ css.form.XFormController interface.
+
+ This legacy API is superseded by css.form.runtime.(X)FormController, and though we migrated all OOo-internal
+ usage of this old API, their might be clients external to OOo still using it (though this is rather unlikely).
+ */
+ class LegacyFormController : public LegacyFormController_Base
+ {
+ public:
+ static Reference< XInterface > Create( const Reference< XMultiServiceFactory >& _rxFactory )
+ {
+ return *( new LegacyFormController( comphelper::getComponentContext(_rxFactory) ) );
+ }
+
+ protected:
+ explicit LegacyFormController( const Reference< XComponentContext >& _rxContext )
+ :m_xDelegator( FormController::create(_rxContext) )
+ {
+ }
+
+ // form::XFormController
+ virtual Reference< XControl > SAL_CALL getCurrentControl( ) override;
+ virtual void SAL_CALL addActivateListener( const Reference< form::XFormControllerListener >& l ) override;
+ virtual void SAL_CALL removeActivateListener( const Reference< form::XFormControllerListener >& l ) override;
+
+ // awt::XTabController
+ virtual void SAL_CALL setModel( const Reference< XTabControllerModel >& Model ) override;
+ virtual Reference< XTabControllerModel > SAL_CALL getModel( ) override;
+ virtual void SAL_CALL setContainer( const Reference< XControlContainer >& Container ) override;
+ virtual Reference< XControlContainer > SAL_CALL getContainer( ) override;
+ virtual Sequence< Reference< XControl > > SAL_CALL getControls( ) override;
+ virtual void SAL_CALL autoTabOrder( ) override;
+ virtual void SAL_CALL activateTabOrder( ) override;
+ virtual void SAL_CALL activateFirst( ) override;
+ virtual void SAL_CALL activateLast( ) 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;
+
+ private:
+ const Reference< form::runtime::XFormController > m_xDelegator;
+ };
+
+ }
+
+ Reference< XControl > SAL_CALL LegacyFormController::getCurrentControl( )
+ {
+ return m_xDelegator->getCurrentControl();
+ }
+
+
+ void SAL_CALL LegacyFormController::addActivateListener( const Reference< form::XFormControllerListener >& _listener )
+ {
+ m_xDelegator->addActivateListener( _listener );
+ }
+
+
+ void SAL_CALL LegacyFormController::removeActivateListener( const Reference< form::XFormControllerListener >& _listener )
+ {
+ m_xDelegator->removeActivateListener( _listener );
+ }
+
+
+ void SAL_CALL LegacyFormController::setModel( const Reference< XTabControllerModel >& _model )
+ {
+ m_xDelegator->setModel( _model );
+ }
+
+
+ Reference< XTabControllerModel > SAL_CALL LegacyFormController::getModel( )
+ {
+ return m_xDelegator->getModel();
+ }
+
+
+ void SAL_CALL LegacyFormController::setContainer( const Reference< XControlContainer >& _container )
+ {
+ m_xDelegator->setContainer( _container );
+ }
+
+
+ Reference< XControlContainer > SAL_CALL LegacyFormController::getContainer( )
+ {
+ return m_xDelegator->getContainer();
+ }
+
+
+ Sequence< Reference< XControl > > SAL_CALL LegacyFormController::getControls( )
+ {
+ return m_xDelegator->getControls();
+ }
+
+
+ void SAL_CALL LegacyFormController::autoTabOrder( )
+ {
+ m_xDelegator->autoTabOrder();
+ }
+
+
+ void SAL_CALL LegacyFormController::activateTabOrder( )
+ {
+ m_xDelegator->activateTabOrder();
+ }
+
+
+ void SAL_CALL LegacyFormController::activateFirst( )
+ {
+ m_xDelegator->activateFirst();
+ }
+
+
+ void SAL_CALL LegacyFormController::activateLast( )
+ {
+ m_xDelegator->activateLast();
+ }
+
+
+ OUString SAL_CALL LegacyFormController::getImplementationName( )
+ {
+ return "org.openoffice.comp.svx.LegacyFormController";
+ }
+
+ sal_Bool SAL_CALL LegacyFormController::supportsService( const OUString& _serviceName )
+ {
+ return cppu::supportsService(this, _serviceName);
+ }
+
+ Sequence< OUString > SAL_CALL LegacyFormController::getSupportedServiceNames( )
+ {
+ return { "com.sun.star.form.FormController", "com.sun.star.awt.control.TabController" };
+ }
+
+}
+
+css::uno::Reference< css::uno::XInterface >
+ LegacyFormController_NewInstance_Impl( const css::uno::Reference< css::lang::XMultiServiceFactory > & _rxORB )
+{
+ return ::svxform::LegacyFormController::Create( _rxORB );
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/form/navigatortree.cxx b/svx/source/form/navigatortree.cxx
new file mode 100644
index 000000000..9ac6e6a87
--- /dev/null
+++ b/svx/source/form/navigatortree.cxx
@@ -0,0 +1,2044 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * 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/dialmgr.hxx>
+#include <svx/fmshell.hxx>
+#include <svx/fmmodel.hxx>
+#include <svx/fmpage.hxx>
+#include <svx/svdpagv.hxx>
+#include <svx/svditer.hxx>
+
+#include <helpids.h>
+#include <fmexpl.hxx>
+#include <fmshimp.hxx>
+#include <fmservs.hxx>
+#include <fmundo.hxx>
+#include <fmpgeimp.hxx>
+#include <fmobj.hxx>
+#include <fmprop.hxx>
+#include <sal/log.hxx>
+#include <vcl/svapp.hxx>
+#include <sfx2/viewsh.hxx>
+#include <sfx2/dispatch.hxx>
+#include <sfx2/viewfrm.hxx>
+#include <comphelper/processfactory.hxx>
+#include <comphelper/property.hxx>
+#include <comphelper/types.hxx>
+#include <com/sun/star/form/FormComponentType.hpp>
+#include <com/sun/star/sdb/CommandType.hpp>
+#include <com/sun/star/beans/PropertyAttribute.hpp>
+#include <com/sun/star/script/XEventAttacherManager.hpp>
+#include <com/sun/star/datatransfer/clipboard/XClipboard.hpp>
+#include <com/sun/star/datatransfer/XTransferable.hpp>
+#include <com/sun/star/uno/XComponentContext.hpp>
+#include <svx/sdrpaintwindow.hxx>
+
+#include <svx/strings.hrc>
+#include <tools/diagnose_ex.h>
+#include <svx/svxids.hrc>
+#include <bitmaps.hlst>
+#include <vcl/commandevent.hxx>
+
+namespace svxform
+{
+ #define EXPLORER_SYNC_DELAY 200
+ // Time (in ms) until explorer synchronizes the view after select or deselect
+
+ using namespace ::com::sun::star::uno;
+ using namespace ::com::sun::star::lang;
+ using namespace ::com::sun::star::beans;
+ using namespace ::com::sun::star::form;
+ using namespace ::com::sun::star::awt;
+ using namespace ::com::sun::star::container;
+ using namespace ::com::sun::star::script;
+ using namespace ::com::sun::star::datatransfer;
+ using namespace ::com::sun::star::datatransfer::clipboard;
+ using namespace ::com::sun::star::sdb;
+
+
+ // helper
+
+
+ typedef ::std::map< Reference< XInterface >, SdrObject* > MapModelToShape;
+
+
+ static void collectShapeModelMapping( SdrPage const * _pPage, MapModelToShape& _rMapping )
+ {
+ OSL_ENSURE( _pPage, "collectShapeModelMapping: invalid arg!" );
+
+ _rMapping.clear();
+
+ SdrObjListIter aIter( _pPage );
+ while ( aIter.IsMore() )
+ {
+ SdrObject* pSdrObject = aIter.Next();
+ FmFormObj* pFormObject = FmFormObj::GetFormObject( pSdrObject );
+ if ( !pFormObject )
+ continue;
+
+ Reference< XInterface > xNormalizedModel( pFormObject->GetUnoControlModel(), UNO_QUERY );
+ // note that this is normalized (i.e. queried for XInterface explicitly)
+
+ ::std::pair< MapModelToShape::iterator, bool > aPos =
+ _rMapping.emplace( xNormalizedModel, pSdrObject );
+ DBG_ASSERT( aPos.second, "collectShapeModelMapping: model was already existent!" );
+ // if this asserts, this would mean we have 2 shapes pointing to the same model
+ }
+ }
+
+ NavigatorTreeDropTarget::NavigatorTreeDropTarget(NavigatorTree& rTreeView)
+ : DropTargetHelper(rTreeView.get_widget().get_drop_target())
+ , m_rTreeView(rTreeView)
+ {
+ }
+
+ sal_Int8 NavigatorTreeDropTarget::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, true);
+ }
+
+ return nAccept;
+ }
+
+ sal_Int8 NavigatorTreeDropTarget::ExecuteDrop(const ExecuteDropEvent& rEvt)
+ {
+ return m_rTreeView.ExecuteDrop(rEvt);
+ }
+
+ NavigatorTree::NavigatorTree(std::unique_ptr<weld::TreeView> xTreeView)
+ :m_xTreeView(std::move(xTreeView))
+ ,m_aDropTargetHelper(*this)
+ ,m_aSynchronizeTimer("svx NavigatorTree m_aSynchronizeTimer")
+ ,nEditEvent(nullptr)
+ ,m_sdiState(SDI_DIRTY)
+ ,m_nSelectLock(0)
+ ,m_nFormsSelected(0)
+ ,m_nControlsSelected(0)
+ ,m_nHiddenControls(0)
+ ,m_bDragDataDirty(false)
+ ,m_bPrevSelectionMixed(false)
+ ,m_bRootSelected(false)
+ ,m_bInitialUpdate(true)
+ ,m_bKeyboardCut( false )
+ ,m_bEditing( false )
+ {
+ m_xTreeView->set_help_id(HID_FORM_NAVIGATOR);
+ m_xTreeView->set_size_request(200, 200);
+
+ m_xTreeView->set_selection_mode(SelectionMode::Multiple);
+
+ m_pNavModel.reset(new NavigatorTreeModel());
+ Clear();
+
+ StartListening( *m_pNavModel );
+
+ m_aSynchronizeTimer.SetInvokeHandler(LINK(this, NavigatorTree, OnSynchronizeTimer));
+ m_xTreeView->connect_changed(LINK(this, NavigatorTree, OnEntrySelDesel));
+ m_xTreeView->connect_key_press(LINK(this, NavigatorTree, KeyInputHdl));
+ m_xTreeView->connect_popup_menu(LINK(this, NavigatorTree, PopupMenuHdl));
+ m_xTreeView->connect_editing(LINK(this, NavigatorTree, EditingEntryHdl),
+ LINK(this, NavigatorTree, EditedEntryHdl));
+ m_xTreeView->connect_drag_begin(LINK(this, NavigatorTree, DragBeginHdl));
+ }
+
+ NavigatorTree::~NavigatorTree()
+ {
+ if( nEditEvent )
+ Application::RemoveUserEvent( nEditEvent );
+
+ if (m_aSynchronizeTimer.IsActive())
+ m_aSynchronizeTimer.Stop();
+
+ DBG_ASSERT(GetNavModel() != nullptr, "NavigatorTree::~NavigatorTree : unexpected : no ExplorerModel");
+ EndListening( *m_pNavModel );
+ Clear();
+ m_pNavModel.reset();
+ }
+
+ void NavigatorTree::Clear()
+ {
+ m_pNavModel->Clear();
+ }
+
+ void NavigatorTree::UpdateContent( FmFormShell* pFormShell )
+ {
+ if (m_bInitialUpdate)
+ {
+ GrabFocus();
+ m_bInitialUpdate = false;
+ }
+
+ FmFormShell* pOldShell = GetNavModel()->GetFormShell();
+ FmFormPage* pOldPage = GetNavModel()->GetFormPage();
+ FmFormPage* pNewPage = pFormShell ? pFormShell->GetCurPage() : nullptr;
+
+ if ((pOldShell != pFormShell) || (pOldPage != pNewPage))
+ {
+ // new shell during editing
+ if (IsEditingActive())
+ {
+ m_xTreeView->end_editing();
+ m_bEditing = false;
+ }
+
+ m_bDragDataDirty = true; // as a precaution, although I don't drag
+ }
+ GetNavModel()->UpdateContent( pFormShell );
+
+ // if there is a form, expand root
+ if (m_xRootEntry && !m_xTreeView->get_row_expanded(*m_xRootEntry))
+ m_xTreeView->expand_row(*m_xRootEntry);
+ // if there is EXACTLY ONE form, expand it too
+ if (m_xRootEntry)
+ {
+ std::unique_ptr<weld::TreeIter> xFirst(m_xTreeView->make_iterator(m_xRootEntry.get()));
+ bool bFirst = m_xTreeView->iter_children(*xFirst);
+ if (bFirst)
+ {
+ std::unique_ptr<weld::TreeIter> xSibling(m_xTreeView->make_iterator(xFirst.get()));
+ if (!m_xTreeView->iter_next_sibling(*xSibling))
+ m_xTreeView->expand_row(*xFirst);
+ }
+ }
+ }
+
+ bool NavigatorTree::implAllowExchange( sal_Int8 _nAction, bool* _pHasNonHidden )
+ {
+ bool bCurEntry = m_xTreeView->get_cursor(nullptr);
+ if (!bCurEntry)
+ return false;
+
+ // Information for AcceptDrop and Execute Drop
+ CollectSelectionData(SDI_ALL);
+ if (m_arrCurrentSelection.empty())
+ // nothing to do
+ return false;
+
+ // check whether there are only hidden controls
+ // I may add a format to pCtrlExch
+ bool bHasNonHidden = std::any_of(m_arrCurrentSelection.begin(), m_arrCurrentSelection.end(),
+ [this](const auto& rEntry) {
+ FmEntryData* pCurrent = weld::fromId<FmEntryData*>(m_xTreeView->get_id(*rEntry));
+ return !IsHiddenControl( pCurrent );
+ });
+
+ if ( bHasNonHidden && ( 0 == ( _nAction & DND_ACTION_MOVE ) ) )
+ // non-hidden controls need to be moved
+ return false;
+
+ if ( _pHasNonHidden )
+ *_pHasNonHidden = bHasNonHidden;
+
+ return true;
+ }
+
+ bool NavigatorTree::implPrepareExchange( sal_Int8 _nAction )
+ {
+ bool bHasNonHidden = false;
+ if ( !implAllowExchange( _nAction, &bHasNonHidden ) )
+ return false;
+
+ m_aControlExchange.prepareDrag();
+ m_aControlExchange->setFocusEntry(m_xTreeView->get_cursor(nullptr));
+
+ for (const auto& rpEntry : m_arrCurrentSelection)
+ m_aControlExchange->addSelectedEntry(m_xTreeView->make_iterator(rpEntry.get()));
+
+ m_aControlExchange->setFormsRoot( GetNavModel()->GetFormPage()->GetForms() );
+ m_aControlExchange->buildPathFormat(m_xTreeView.get(), m_xRootEntry.get());
+
+ if (!bHasNonHidden)
+ {
+ // create a sequence
+ Sequence< Reference< XInterface > > seqIFaces(m_arrCurrentSelection.size());
+ Reference< XInterface >* pArray = seqIFaces.getArray();
+ for (const auto& rpEntry : m_arrCurrentSelection)
+ {
+ *pArray = weld::fromId<FmEntryData*>(m_xTreeView->get_id(*rpEntry))->GetElement();
+ ++pArray;
+ }
+ // and the new format
+ m_aControlExchange->addHiddenControlsFormat(seqIFaces);
+ }
+
+ m_bDragDataDirty = false;
+ return true;
+ }
+
+ IMPL_LINK(NavigatorTree, DragBeginHdl, bool&, rUnsetDragIcon, bool)
+ {
+ rUnsetDragIcon = false;
+
+ bool bSuccess = implPrepareExchange(DND_ACTION_COPYMOVE);
+ if (bSuccess)
+ {
+ OControlExchange& rExchange = *m_aControlExchange;
+ rtl::Reference<TransferDataContainer> xHelper(&rExchange);
+ m_xTreeView->enable_drag_source(xHelper, DND_ACTION_COPYMOVE);
+ rExchange.setDragging(true);
+ }
+ return !bSuccess;
+ }
+
+ IMPL_LINK(NavigatorTree, PopupMenuHdl, const CommandEvent&, rEvt, bool)
+ {
+ bool bHandled = false;
+ switch( rEvt.GetCommand() )
+ {
+ case CommandEventId::ContextMenu:
+ {
+ // Position of click
+ ::Point ptWhere;
+ if (rEvt.IsMouseEvent())
+ {
+ ptWhere = rEvt.GetMousePosPixel();
+ std::unique_ptr<weld::TreeIter> xClickedOn(m_xTreeView->make_iterator());
+ if (!m_xTreeView->get_dest_row_at_pos(ptWhere, xClickedOn.get(), false))
+ break;
+ if (!m_xTreeView->is_selected(*xClickedOn))
+ {
+ m_xTreeView->unselect_all();
+ m_xTreeView->select(*xClickedOn);
+ m_xTreeView->set_cursor(*xClickedOn);
+ }
+ }
+ else
+ {
+ if (m_arrCurrentSelection.empty()) // only happens with context menu via keyboard
+ break;
+
+ std::unique_ptr<weld::TreeIter> xCurrent(m_xTreeView->make_iterator());
+ if (!m_xTreeView->get_cursor(xCurrent.get()))
+ break;
+ ptWhere = m_xTreeView->get_row_area(*xCurrent).Center();
+ }
+
+ // update my selection data
+ CollectSelectionData(SDI_ALL);
+
+ // if there is at least one no-root-entry and the root selected, I deselect root
+ if ( (m_arrCurrentSelection.size() > 1) && m_bRootSelected )
+ {
+ const std::unique_ptr<weld::TreeIter>& rIter = *m_arrCurrentSelection.begin();
+ m_xTreeView->set_cursor(*rIter);
+ m_xTreeView->unselect(*m_xRootEntry);
+ }
+ bool bSingleSelection = (m_arrCurrentSelection.size() == 1);
+
+
+ DBG_ASSERT( (!m_arrCurrentSelection.empty()) || m_bRootSelected, "no entries selected" );
+ // shouldn't happen, because I would have selected one during call to IsSelected,
+ // if there was none before
+
+
+ // create menu
+ FmFormShell* pFormShell = GetNavModel()->GetFormShell();
+ FmFormModel* pFormModel = pFormShell ? pFormShell->GetFormModel() : nullptr;
+ if( pFormShell && pFormModel )
+ {
+ std::unique_ptr<weld::Builder> xBuilder(Application::CreateBuilder(m_xTreeView.get(), "svx/ui/formnavimenu.ui"));
+ std::unique_ptr<weld::Menu> xContextMenu(xBuilder->weld_menu("menu"));
+ std::unique_ptr<weld::Menu> xSubMenuNew(xBuilder->weld_menu("submenu"));
+
+ // menu 'New' only exists, if only the root or only one form is selected
+ bool bShowNew = bSingleSelection && (m_nFormsSelected || m_bRootSelected);
+ if (!bShowNew)
+ xContextMenu->remove("new");
+
+ // 'New'\'Form' under the same terms
+ bool bShowForm = bSingleSelection && (m_nFormsSelected || m_bRootSelected);
+ if (bShowForm)
+ xSubMenuNew->append("form", SvxResId(RID_STR_FORM), RID_SVXBMP_FORM);
+
+ // 'New'\'hidden...', if exactly one form is selected
+ bool bShowHidden = bSingleSelection && m_nFormsSelected;
+ if (bShowHidden)
+ xSubMenuNew->append("hidden", SvxResId(RID_STR_HIDDEN), RID_SVXBMP_HIDDEN);
+
+ // 'Delete': everything which is not root can be removed
+ if (m_bRootSelected)
+ xContextMenu->remove("delete");
+
+ // 'Cut', 'Copy' and 'Paste'
+ bool bShowCut = !m_bRootSelected && implAllowExchange(DND_ACTION_MOVE);
+ if (!bShowCut)
+ xContextMenu->remove("cut");
+ bool bShowCopy = !m_bRootSelected && implAllowExchange(DND_ACTION_COPY);
+ if (!bShowCopy)
+ xContextMenu->remove("copy");
+ if (!implAcceptPaste())
+ xContextMenu->remove("paste");
+
+ // TabDialog, if exactly one form
+ bool bShowTabOrder = bSingleSelection && m_nFormsSelected;
+ if (!bShowTabOrder)
+ xContextMenu->remove("taborder");
+
+ bool bShowProps = true;
+ // in XML forms, we don't allow for the properties of a form
+ // #i36484#
+ if (pFormShell->GetImpl()->isEnhancedForm_Lock() && !m_nControlsSelected)
+ bShowProps = false;
+ // if the property browser is already open, we don't allow for the properties, too
+ if (pFormShell->GetImpl()->IsPropBrwOpen_Lock())
+ bShowProps = false;
+
+ // and finally, if there's a mixed selection of forms and controls, disable the entry, too
+ if (bShowProps && !pFormShell->GetImpl()->IsPropBrwOpen_Lock())
+ bShowProps =
+ (m_nControlsSelected && !m_nFormsSelected) || (!m_nControlsSelected && m_nFormsSelected);
+
+ if (!bShowProps)
+ xContextMenu->remove("props");
+
+ // rename, if one element and no root
+ bool bShowRename = bSingleSelection && !m_bRootSelected;
+ if (!bShowRename)
+ xContextMenu->remove("rename");
+
+ if (!m_bRootSelected)
+ {
+ // Readonly-entry is only for root
+ xContextMenu->remove("designmode");
+ // the same for automatic control focus
+ xContextMenu->remove("controlfocus");
+ }
+
+ std::unique_ptr<weld::Menu> xConversionMenu(xBuilder->weld_menu("changemenu"));
+ // ConvertTo-Slots are enabled, if one control is selected
+ // the corresponding slot is disabled
+ if (!m_bRootSelected && !m_nFormsSelected && (m_nControlsSelected == 1))
+ {
+ FmXFormShell::GetConversionMenu_Lock(*xConversionMenu);
+#if OSL_DEBUG_LEVEL > 0
+ const std::unique_ptr<weld::TreeIter>& rIter = *m_arrCurrentSelection.begin();
+ FmControlData* pCurrent = weld::fromId<FmControlData*>(m_xTreeView->get_id(*rIter));
+ OSL_ENSURE( pFormShell->GetImpl()->isSolelySelected_Lock( pCurrent->GetFormComponent() ),
+ "NavigatorTree::Command: inconsistency between the navigator selection, and the selection as the shell knows it!" );
+#endif
+
+ pFormShell->GetImpl()->checkControlConversionSlotsForCurrentSelection_Lock(*xConversionMenu);
+ }
+ else
+ xContextMenu->remove("change");
+
+ if (m_bRootSelected)
+ {
+ // set OpenReadOnly
+ xContextMenu->set_active("designmode", pFormModel->GetOpenInDesignMode());
+ xContextMenu->set_active("controlfocus", pFormModel->GetAutoControlFocus());
+ }
+
+ OString sIdent = xContextMenu->popup_at_rect(m_xTreeView.get(), tools::Rectangle(ptWhere, ::Size(1, 1)));
+ if (sIdent == "form")
+ {
+ OUString aStr(SvxResId(RID_STR_FORM));
+ OUString aUndoStr = SvxResId(RID_STR_UNDO_CONTAINER_INSERT).replaceAll("#", aStr);
+
+ pFormModel->BegUndo(aUndoStr);
+ // slot was only available, if there is only one selected entry,
+ // which is a root or a form
+ const std::unique_ptr<weld::TreeIter>& rIter = *m_arrCurrentSelection.begin();
+ NewForm(*rIter);
+ pFormModel->EndUndo();
+ }
+ else if (sIdent == "hidden")
+ {
+ OUString aStr(SvxResId(RID_STR_CONTROL));
+ OUString aUndoStr = SvxResId(RID_STR_UNDO_CONTAINER_INSERT).replaceAll("#", aStr);
+
+ pFormModel->BegUndo(aUndoStr);
+ // slot was valid for (exactly) one selected form
+ const std::unique_ptr<weld::TreeIter>& rIter = *m_arrCurrentSelection.begin();
+ NewControl(FM_COMPONENT_HIDDEN, *rIter, true);
+ pFormModel->EndUndo();
+ }
+ else if (sIdent == "cut")
+ doCut();
+ else if (sIdent == "copy")
+ doCopy();
+ else if (sIdent == "paste")
+ doPaste();
+ else if (sIdent == "delete")
+ DeleteSelection();
+ else if (sIdent == "taborder")
+ {
+ // this slot was effective for exactly one selected form
+ const std::unique_ptr<weld::TreeIter>& rSelectedForm = *m_arrCurrentSelection.begin();
+ DBG_ASSERT( IsFormEntry(*rSelectedForm), "NavigatorTree::Command: This entry must be a FormEntry." );
+
+ FmFormData* pFormData = weld::fromId<FmFormData*>(m_xTreeView->get_id(*rSelectedForm));
+ const Reference< XForm >& xForm( pFormData->GetFormIface());
+
+ Reference< XTabControllerModel > xTabController(xForm, UNO_QUERY);
+ if( !xTabController.is() )
+ break;
+ GetNavModel()->GetFormShell()->GetImpl()->ExecuteTabOrderDialog_Lock(xTabController);
+ }
+ else if (sIdent == "props")
+ ShowSelectionProperties(true);
+ else if (sIdent == "rename")
+ {
+ // only allowed for one no-root-entry
+ const std::unique_ptr<weld::TreeIter>& rIter = *m_arrCurrentSelection.begin();
+ m_xTreeView->start_editing(*rIter);
+ m_bEditing = true;
+ }
+ else if (sIdent == "designmode")
+ {
+ pFormModel->SetOpenInDesignMode( !pFormModel->GetOpenInDesignMode() );
+ pFormShell->GetViewShell()->GetViewFrame()->GetBindings().Invalidate(SID_FM_OPEN_READONLY);
+ }
+ else if (sIdent == "controlfocus")
+ {
+ pFormModel->SetAutoControlFocus( !pFormModel->GetAutoControlFocus() );
+ pFormShell->GetViewShell()->GetViewFrame()->GetBindings().Invalidate(SID_FM_AUTOCONTROLFOCUS);
+ }
+ else if (FmXFormShell::isControlConversionSlot(sIdent))
+ {
+ const std::unique_ptr<weld::TreeIter>& rIter = *m_arrCurrentSelection.begin();
+ FmControlData* pCurrent = weld::fromId<FmControlData*>(m_xTreeView->get_id(*rIter));
+ if (pFormShell->GetImpl()->executeControlConversionSlot_Lock(pCurrent->GetFormComponent(), sIdent))
+ ShowSelectionProperties();
+ }
+ }
+ bHandled = true;
+ }
+ break;
+ default: break;
+ }
+
+ return bHandled;
+ }
+
+ std::unique_ptr<weld::TreeIter> NavigatorTree::FindEntry(FmEntryData* pEntryData)
+ {
+ std::unique_ptr<weld::TreeIter> xRet;
+ if(!pEntryData)
+ return xRet;
+
+ m_xTreeView->all_foreach([this, pEntryData, &xRet](weld::TreeIter& rEntry){
+ FmEntryData* pCurEntryData = weld::fromId<FmEntryData*>(m_xTreeView->get_id(rEntry));
+ if (pCurEntryData && pCurEntryData->IsEqualWithoutChildren(pEntryData))
+ {
+ xRet = m_xTreeView->make_iterator(&rEntry);
+ return true;
+ }
+ return false;
+ });
+
+ return xRet;
+ }
+
+ void NavigatorTree::Notify( SfxBroadcaster& /*rBC*/, const SfxHint& rHint )
+ {
+ if( auto pRemovedHint = dynamic_cast<const FmNavRemovedHint*>(&rHint) )
+ {
+ FmEntryData* pEntryData = pRemovedHint->GetEntryData();
+ Remove( pEntryData );
+ }
+ else if( auto pInsertedHint = dynamic_cast<const FmNavInsertedHint*>(&rHint) )
+ {
+ FmEntryData* pEntryData = pInsertedHint->GetEntryData();
+ sal_uInt32 nRelPos = pInsertedHint->GetRelPos();
+ Insert( pEntryData, nRelPos );
+ }
+ else if( auto pReplacedHint = dynamic_cast<const FmNavModelReplacedHint*>(&rHint) )
+ {
+ FmEntryData* pData = pReplacedHint->GetEntryData();
+ std::unique_ptr<weld::TreeIter> xEntry = FindEntry(pData);
+ if (xEntry)
+ {
+ // reset image
+ m_xTreeView->set_image(*xEntry, pData->GetNormalImage());
+ }
+ }
+ else if( auto pNameChangedHint = dynamic_cast<const FmNavNameChangedHint*>(&rHint) )
+ {
+ std::unique_ptr<weld::TreeIter> xEntry = FindEntry(pNameChangedHint->GetEntryData());
+ m_xTreeView->set_text(*xEntry, pNameChangedHint->GetNewName());
+ }
+ else if( dynamic_cast<const FmNavClearedHint*>(&rHint) )
+ {
+ m_aCutEntries.clear();
+ if (m_aControlExchange.isDataExchangeActive())
+ m_aControlExchange.clear();
+ m_xTreeView->clear();
+
+ // default-entry "Forms"
+ OUString sText(SvxResId(RID_STR_FORMS));
+ m_xRootEntry = m_xTreeView->make_iterator();
+ m_xTreeView->insert(nullptr, -1, &sText, nullptr, nullptr, nullptr,
+ false, m_xRootEntry.get());
+ m_xTreeView->set_image(*m_xRootEntry, RID_SVXBMP_FORMS);
+ m_xTreeView->set_sensitive(*m_xRootEntry, true);
+ }
+ else if (auto pSelectHint = dynamic_cast<FmNavRequestSelectHint*>(const_cast<SfxHint*>(&rHint)))
+ {
+ FmEntryDataArray& arredToSelect = pSelectHint->GetItems();
+ SynchronizeSelection(arredToSelect);
+
+ if (pSelectHint->IsMixedSelection())
+ // in this case I deselect all, although the view had a mixed selection
+ // during next selection, I must adapt the navigator to the view
+ m_bPrevSelectionMixed = true;
+ }
+ }
+
+ std::unique_ptr<weld::TreeIter> NavigatorTree::Insert(const FmEntryData* pEntryData, int nRelPos)
+ {
+ // insert current entry
+ std::unique_ptr<weld::TreeIter> xParentEntry = FindEntry( pEntryData->GetParent() );
+ std::unique_ptr<weld::TreeIter> xNewEntry(m_xTreeView->make_iterator());
+ OUString sId(weld::toId(pEntryData));
+
+ if(!xParentEntry)
+ {
+ m_xTreeView->insert(m_xRootEntry.get(), nRelPos, &pEntryData->GetText(), &sId,
+ nullptr, nullptr, false, xNewEntry.get());
+ }
+ else
+ {
+ m_xTreeView->insert(xParentEntry.get(), nRelPos, &pEntryData->GetText(), &sId,
+ nullptr, nullptr, false, xNewEntry.get());
+ }
+
+ m_xTreeView->set_image(*xNewEntry, pEntryData->GetNormalImage());
+ m_xTreeView->set_sensitive(*xNewEntry, true);
+
+ // If root-entry, expand root
+ if (!xParentEntry)
+ m_xTreeView->expand_row(*m_xRootEntry);
+
+ // insert children
+ FmEntryDataList* pChildList = pEntryData->GetChildList();
+ size_t nChildCount = pChildList->size();
+ for( size_t i = 0; i < nChildCount; i++ )
+ {
+ FmEntryData* pChildData = pChildList->at( i );
+ Insert(pChildData, -1);
+ }
+
+ return xNewEntry;
+ }
+
+ void NavigatorTree::Remove( FmEntryData* pEntryData )
+ {
+ if( !pEntryData )
+ return;
+
+ // entry for the data
+ std::unique_ptr<weld::TreeIter> xEntry = FindEntry(pEntryData);
+ if (!xEntry)
+ return;
+
+ // delete entry from TreeListBox
+ // I'm not allowed, to treat the selection, which I trigger:
+ // select changes the MarkList of the view, if somebody else does this at the same time
+ // and removes a selection, we get a problem
+ // e.g. Group controls with open navigator
+ LockSelectionHandling();
+
+ // little problem: I remember the selected data, but if somebody deletes one of these entries,
+ // I get inconsistent... this would be bad
+ m_xTreeView->unselect(*xEntry);
+
+ // selection can be modified during deletion,
+ // but because I disabled SelectionHandling, I have to do it later
+ auto nExpectedSelectionCount = m_xTreeView->count_selected_rows();
+
+ ModelHasRemoved(xEntry.get());
+ m_xTreeView->remove(*xEntry);
+
+ if (nExpectedSelectionCount != m_xTreeView->count_selected_rows())
+ SynchronizeSelection();
+
+ // by default I treat the selection of course
+ UnlockSelectionHandling();
+ }
+
+ bool NavigatorTree::IsFormEntry(const weld::TreeIter& rEntry)
+ {
+ FmEntryData* pEntryData = weld::fromId<FmEntryData*>(m_xTreeView->get_id(rEntry));
+ return !pEntryData || dynamic_cast<const FmFormData*>( pEntryData) != nullptr;
+ }
+
+ bool NavigatorTree::IsFormComponentEntry(const weld::TreeIter& rEntry)
+ {
+ FmEntryData* pEntryData = weld::fromId<FmEntryData*>(m_xTreeView->get_id(rEntry));
+ return dynamic_cast<const FmControlData*>( pEntryData) != nullptr;
+ }
+
+ bool NavigatorTree::implAcceptPaste( )
+ {
+ auto nSelectedEntries = m_xTreeView->count_selected_rows();
+ if (nSelectedEntries != 1)
+ // no selected entry, or at least two selected entries
+ return false;
+
+ // get the clipboard
+ TransferableDataHelper aClipboardContent(TransferableDataHelper::CreateFromClipboard(m_xTreeView->get_clipboard()));
+
+ sal_Int8 nAction = m_aControlExchange.isClipboardOwner() && doingKeyboardCut( ) ? DND_ACTION_MOVE : DND_ACTION_COPY;
+ std::unique_ptr<weld::TreeIter> xSelected(m_xTreeView->make_iterator());
+ if (!m_xTreeView->get_selected(xSelected.get()))
+ xSelected.reset();
+ return nAction == implAcceptDataTransfer(aClipboardContent.GetDataFlavorExVector(), nAction, xSelected.get(), false);
+ }
+
+ sal_Int8 NavigatorTree::implAcceptDataTransfer( const DataFlavorExVector& _rFlavors, sal_Int8 _nAction, const weld::TreeIter* _pTargetEntry, bool _bDnD )
+ {
+ // no target -> no drop
+ if (!_pTargetEntry)
+ return DND_ACTION_NONE;
+
+ // format check
+ bool bHasDefControlFormat = OControlExchange::hasFieldExchangeFormat( _rFlavors );
+ bool bHasControlPathFormat = OControlExchange::hasControlPathFormat( _rFlavors );
+ bool bHasHiddenControlsFormat = OControlExchange::hasHiddenControlModelsFormat( _rFlavors );
+ if (!bHasDefControlFormat && !bHasControlPathFormat && !bHasHiddenControlsFormat)
+ return DND_ACTION_NONE;
+
+ bool bSelfSource = _bDnD ? m_aControlExchange.isDragSource() : m_aControlExchange.isClipboardOwner();
+
+ if ( bHasHiddenControlsFormat )
+ { // bHasHiddenControlsFormat means that only hidden controls are part of the data
+
+ // hidden controls can be copied to a form only
+ if (m_xTreeView->iter_compare(*_pTargetEntry, *m_xRootEntry) == 0 || !IsFormEntry(*_pTargetEntry))
+ return DND_ACTION_NONE;
+
+ return bSelfSource ? ( DND_ACTION_COPYMOVE & _nAction ) : DND_ACTION_COPY;
+ }
+
+ if ( !bSelfSource )
+ {
+ // DnD or CnP crossing navigator boundaries
+ // The main problem here is that the current API does not allow us to sneak into the content which
+ // is to be inserted. So we have to allow it for the moment, but maybe reject later on (in the real drop).
+
+ // TODO: this smart behaviour later on ... at the moment, we disallow data transfer crossing navigator
+ // boundaries.
+
+ return DND_ACTION_NONE;
+ }
+
+ DBG_ASSERT( _bDnD ? m_aControlExchange.isDragSource() : m_aControlExchange.isClipboardOwner(),
+ "NavigatorTree::implAcceptDataTransfer: here only with source=dest!" );
+ // somebody changed the logic of this method ...
+
+ // from here on, I can work with m_aControlExchange instead of _rData!
+
+ bool bForeignCollection = m_aControlExchange->getFormsRoot().get() != GetNavModel()->GetFormPage()->GetForms().get();
+ if ( bForeignCollection )
+ {
+ // crossing shell/page boundaries, we can exchange hidden controls only
+ // But if we survived the checks above, we do not have hidden controls.
+ // -> no data transfer
+ DBG_ASSERT( !bHasHiddenControlsFormat, "NavigatorTree::implAcceptDataTransfer: still hidden controls format!" );
+ // somebody changed the logic of this method ...
+
+ return DND_ACTION_COPY;
+ }
+
+ if (DND_ACTION_MOVE != _nAction) // 'normal' controls within a shell are moved only (never copied)
+ return DND_ACTION_NONE;
+
+ if ( m_bDragDataDirty || !bHasDefControlFormat )
+ {
+ if (!bHasControlPathFormat)
+ // I am in the shell/page, which has the controls, but I have no format,
+ // which survived the shell change (SVX_FM_CONTROLS_AS_PATH)
+ return DND_ACTION_NONE;
+
+ // I must recreate the list of the ExchangeObjects, because the shell was changed during dragging
+ // (there are SvLBoxEntries in it, and we lost them during change)
+ m_aControlExchange->buildListFromPath(m_xTreeView.get(), m_xRootEntry.get());
+ m_bDragDataDirty = false;
+ }
+
+ // List of dropped entries from DragServer
+ const ListBoxEntrySet& rDropped = m_aControlExchange->selected();
+ DBG_ASSERT(!rDropped.empty(), "NavigatorTree::implAcceptDataTransfer: no entries !");
+
+ bool bDropTargetIsComponent = IsFormComponentEntry( *_pTargetEntry );
+
+ // conditions to disallow the drop
+ // 0) the root entry is part of the list (can't DnD the root!)
+ // 1) one of the dragged entries is to be dropped onto its own parent
+ // 2) - " - is to be dropped onto itself
+ // 3) - " - is a Form and to be dropped onto one of its descendants
+ // 4) one of the entries is a control and to be dropped onto the root
+ // 5) a control or form will be dropped onto a control which is _not_ a sibling (dropping onto a sibling
+ // means moving the control)
+
+ // collect the ancestors of the drop target (speeds up 3)
+ SvLBoxEntrySortedArray arrDropAncestors;
+ std::unique_ptr<weld::TreeIter> xLoop(m_xTreeView->make_iterator(_pTargetEntry));
+ do
+ {
+ arrDropAncestors.emplace(m_xTreeView->make_iterator(xLoop.get()));
+ }
+ while (m_xTreeView->iter_parent(*xLoop));
+
+ for (const auto& rCurrent : rDropped)
+ {
+ // test for 0)
+ if (m_xTreeView->iter_compare(*rCurrent, *m_xRootEntry) == 0)
+ return DND_ACTION_NONE;
+
+ std::unique_ptr<weld::TreeIter> xCurrentParent(m_xTreeView->make_iterator(rCurrent.get()));
+ m_xTreeView->iter_parent(*xCurrentParent);
+
+ // test for 1)
+ if (m_xTreeView->iter_compare(*_pTargetEntry, *xCurrentParent) == 0)
+ return DND_ACTION_NONE;
+
+ // test for 2)
+ if (m_xTreeView->iter_compare(*rCurrent, *_pTargetEntry) == 0)
+ return DND_ACTION_NONE;
+
+ // test for 5)
+ if (bDropTargetIsComponent)
+ return DND_ACTION_NONE;
+
+ // test for 3)
+ if (IsFormEntry(*rCurrent))
+ {
+ auto aIter = std::find_if(arrDropAncestors.begin(), arrDropAncestors.end(),
+ [this, &rCurrent](const auto& rElem) {
+ return m_xTreeView->iter_compare(*rElem, *rCurrent) == 0;
+ });
+
+ if ( aIter != arrDropAncestors.end() )
+ return DND_ACTION_NONE;
+ }
+ else if (IsFormComponentEntry(*rCurrent))
+ {
+ // test for 4)
+ if (m_xTreeView->iter_compare(*_pTargetEntry, *m_xRootEntry) == 0)
+ return DND_ACTION_NONE;
+ }
+ }
+ return DND_ACTION_MOVE;
+ }
+
+ sal_Int8 NavigatorTree::AcceptDrop( const AcceptDropEvent& rEvt )
+ {
+ ::Point aDropPos = rEvt.maPosPixel;
+ std::unique_ptr<weld::TreeIter> xDropTarget(m_xTreeView->make_iterator());
+ // get_dest_row_at_pos with false cause we must drop exactly "on" a form to paste a control into it
+ if (!m_xTreeView->get_dest_row_at_pos(aDropPos, xDropTarget.get(), false))
+ xDropTarget.reset();
+ return implAcceptDataTransfer(m_aDropTargetHelper.GetDataFlavorExVector(), rEvt.mnAction, xDropTarget.get(), true);
+ }
+
+ sal_Int8 NavigatorTree::implExecuteDataTransfer( const OControlTransferData& _rData, sal_Int8 _nAction, const ::Point& _rDropPos, bool _bDnD )
+ {
+ std::unique_ptr<weld::TreeIter> xDrop(m_xTreeView->make_iterator());
+ // get_dest_row_at_pos with false cause we must drop exactly "on" a form to paste a control into it
+ if (!m_xTreeView->get_dest_row_at_pos(_rDropPos, xDrop.get(), false))
+ xDrop.reset();
+ return implExecuteDataTransfer( _rData, _nAction, xDrop.get(), _bDnD );
+ }
+
+ sal_Int8 NavigatorTree::implExecuteDataTransfer( const OControlTransferData& _rData, sal_Int8 _nAction, const weld::TreeIter* _pTargetEntry, bool _bDnD )
+ {
+ const DataFlavorExVector& rDataFlavors = _rData.GetDataFlavorExVector();
+
+ if ( DND_ACTION_NONE == implAcceptDataTransfer( rDataFlavors, _nAction, _pTargetEntry, _bDnD ) )
+ // under some platforms, it may happen that ExecuteDrop is called though AcceptDrop returned DND_ACTION_NONE
+ return DND_ACTION_NONE;
+
+ if (!_pTargetEntry)
+ // no target -> no drop
+ return DND_ACTION_NONE;
+
+ // format checks
+#ifdef DBG_UTIL
+ bool bHasHiddenControlsFormat = OControlExchange::hasHiddenControlModelsFormat( rDataFlavors );
+ bool bForeignCollection = _rData.getFormsRoot().get() != GetNavModel()->GetFormPage()->GetForms().get();
+ DBG_ASSERT(!bForeignCollection || bHasHiddenControlsFormat, "NavigatorTree::implExecuteDataTransfer: invalid format (AcceptDrop shouldn't have let this pass) !");
+ DBG_ASSERT(bForeignCollection || !m_bDragDataDirty, "NavigatorTree::implExecuteDataTransfer: invalid state (shell changed since last exchange resync) !");
+ // this should be done in AcceptDrop: the list of controls is created in _rData
+ // and m_bDragDataDirty is reset
+#endif
+
+ if ( DND_ACTION_COPY == _nAction )
+ { // bHasHiddenControlsFormat means that only hidden controls are part of the data
+#ifdef DBG_UTIL
+ DBG_ASSERT( bHasHiddenControlsFormat, "NavigatorTree::implExecuteDataTransfer: copy allowed for hidden controls only!" );
+#endif
+ DBG_ASSERT( _pTargetEntry && m_xTreeView->iter_compare(*_pTargetEntry, *m_xRootEntry) != 0 && IsFormEntry( *_pTargetEntry ),
+ "NavigatorTree::implExecuteDataTransfer: should not be here!" );
+ // implAcceptDataTransfer should have caught both cases
+
+#ifdef DBG_UTIL
+ DBG_ASSERT(bHasHiddenControlsFormat, "NavigatorTree::implExecuteDataTransfer: only copying of hidden controls is supported !");
+ // should be caught by AcceptDrop
+#endif
+
+ // because i want to select all targets (and only them)
+ m_xTreeView->unselect_all();
+
+ const Sequence< Reference< XInterface > >& aControls = _rData.hiddenControls();
+ sal_Int32 nCount = aControls.getLength();
+ const Reference< XInterface >* pControls = aControls.getConstArray();
+
+ FmFormShell* pFormShell = GetNavModel()->GetFormShell();
+ FmFormModel* pFormModel = pFormShell ? pFormShell->GetFormModel() : nullptr;
+
+ // within undo
+ if (pFormModel)
+ {
+ OUString aStr(SvxResId(RID_STR_CONTROL));
+ OUString aUndoStr = SvxResId(RID_STR_UNDO_CONTAINER_INSERT).replaceAll("#", aStr);
+ pFormModel->BegUndo(aUndoStr);
+ }
+
+ // copy controls
+ for (sal_Int32 i=0; i<nCount; ++i)
+ {
+ // create new control
+ FmControlData* pNewControlData = NewControl( FM_COMPONENT_HIDDEN, *_pTargetEntry, false);
+ Reference< XPropertySet > xNewPropSet( pNewControlData->GetPropertySet() );
+
+ // copy properties form old control to new one
+ Reference< XPropertySet > xCurrent(pControls[i], UNO_QUERY);
+#if (OSL_DEBUG_LEVEL > 0)
+ // check whether it is a hidden control
+ sal_Int16 nClassId = ::comphelper::getINT16(xCurrent->getPropertyValue(FM_PROP_CLASSID));
+ OSL_ENSURE(nClassId == FormComponentType::HIDDENCONTROL, "NavigatorTree::implExecuteDataTransfer: invalid control in drop list !");
+ // if SVX_FM_HIDDEN_CONTROLS-format exists, the sequence
+ // should only contain hidden controls
+#endif // (OSL_DEBUG_LEVEL > 0)
+ Reference< XPropertySetInfo > xPropInfo( xCurrent->getPropertySetInfo());
+ const Sequence< Property> seqAllCurrentProps = xPropInfo->getProperties();
+ for (Property const & currentProp : seqAllCurrentProps)
+ {
+ if (((currentProp.Attributes & PropertyAttribute::READONLY) == 0) && (currentProp.Name != FM_PROP_NAME))
+ { // (read-only attribs aren't set, ditto name,
+ // NewControl defined it uniquely
+ xNewPropSet->setPropertyValue(currentProp.Name, xCurrent->getPropertyValue(currentProp.Name));
+ }
+ }
+
+ std::unique_ptr<weld::TreeIter> xToSelect = FindEntry(pNewControlData);
+ m_xTreeView->select(*xToSelect);
+ if (i == 0)
+ m_xTreeView->set_cursor(*xToSelect);
+ }
+
+ if (pFormModel)
+ pFormModel->EndUndo();
+
+ return _nAction;
+ }
+
+ if ( !OControlExchange::hasFieldExchangeFormat( _rData.GetDataFlavorExVector() ) )
+ {
+ // can't do anything without the internal format here ... usually happens when doing DnD or CnP
+ // over navigator boundaries
+ return DND_ACTION_NONE;
+ }
+
+ // some data for the target
+ bool bDropTargetIsForm = IsFormEntry(*_pTargetEntry);
+ FmFormData* pTargetData = bDropTargetIsForm ? weld::fromId<FmFormData*>(m_xTreeView->get_id(*_pTargetEntry)) : nullptr;
+
+ DBG_ASSERT( DND_ACTION_COPY != _nAction, "NavigatorTree::implExecuteDataTransfer: somebody changed the logics!" );
+
+ // list of dragged entries
+ const ListBoxEntrySet& rDropped = _rData.selected();
+ DBG_ASSERT(!rDropped.empty(), "NavigatorTree::implExecuteDataTransfer: no entries!");
+
+ // make a copy because rDropped is updated on deleting an entry which we do in the processing loop
+ ListBoxEntrySet aDropped;
+ for (const auto& rEntry : rDropped)
+ aDropped.emplace(m_xTreeView->make_iterator(rEntry.get()));
+
+ // shell and model
+ FmFormShell* pFormShell = GetNavModel()->GetFormShell();
+ FmFormModel* pFormModel = pFormShell ? pFormShell->GetFormModel() : nullptr;
+ if (!pFormModel)
+ return DND_ACTION_NONE;
+
+ // for Undo
+ const bool bUndo = pFormModel->IsUndoEnabled();
+
+ if( bUndo )
+ {
+ OUString strUndoDescription(SvxResId(RID_STR_UNDO_CONTAINER_REPLACE));
+ pFormModel->BegUndo(strUndoDescription);
+ }
+
+ // remove selection before adding an entry, so the mark doesn't flicker
+ // -> lock action of selection
+ LockSelectionHandling();
+
+ // go through all dropped entries
+ for ( ListBoxEntrySet::const_iterator dropped = aDropped.begin();
+ dropped != aDropped.end();
+ ++dropped
+ )
+ {
+ bool bFirstEntry = aDropped.begin() == dropped;
+
+ // some data of the current element
+ const auto& rCurrent = *dropped;
+ DBG_ASSERT(rCurrent, "NavigatorTree::implExecuteDataTransfer: invalid entry");
+ DBG_ASSERT(m_xTreeView->get_iter_depth(*rCurrent) != 0, "NavigatorTree::implExecuteDataTransfer: invalid entry");
+ // don't drag root
+
+ FmEntryData* pCurrentUserData = weld::fromId<FmEntryData*>(m_xTreeView->get_id(*rCurrent));
+
+ Reference< XChild > xCurrentChild = pCurrentUserData->GetChildIFace();
+ Reference< XIndexContainer > xContainer(xCurrentChild->getParent(), UNO_QUERY);
+
+ FmFormData* pCurrentParentUserData = static_cast<FmFormData*>(pCurrentUserData->GetParent());
+ DBG_ASSERT(pCurrentParentUserData == nullptr || dynamic_cast<const FmFormData*>(pCurrentUserData->GetParent()) != nullptr, "NavigatorTree::implExecuteDataTransfer: invalid parent");
+
+ // remove from parent
+ if (pCurrentParentUserData)
+ pCurrentParentUserData->GetChildList()->removeNoDelete( pCurrentUserData );
+ else
+ GetNavModel()->GetRootList()->removeNoDelete( pCurrentUserData );
+
+ // remove from container
+ sal_Int32 nIndex = getElementPos(xContainer, xCurrentChild);
+ GetNavModel()->m_pPropChangeList->Lock();
+ // UndoAction for removal
+ if ( bUndo && GetNavModel()->m_pPropChangeList->CanUndo())
+ {
+ pFormModel->AddUndo(std::make_unique<FmUndoContainerAction>(*pFormModel, FmUndoContainerAction::Removed,
+ xContainer, xCurrentChild, nIndex));
+ }
+ else if( !GetNavModel()->m_pPropChangeList->CanUndo() )
+ {
+ FmUndoContainerAction::DisposeElement( xCurrentChild );
+ }
+
+ // copy events
+ Reference< XEventAttacherManager > xManager(xContainer, UNO_QUERY);
+ Sequence< ScriptEventDescriptor > aEvts;
+
+ if (xManager.is() && nIndex >= 0)
+ aEvts = xManager->getScriptEvents(nIndex);
+ xContainer->removeByIndex(nIndex);
+
+ // remove selection
+ m_xTreeView->unselect(*rCurrent);
+ // and delete it
+ Remove(pCurrentUserData);
+
+ // position in DropParents, where to insert dropped entries
+ if (pTargetData)
+ xContainer.set(pTargetData->GetElement(), UNO_QUERY);
+ else
+ xContainer = GetNavModel()->GetForms();
+
+ // always insert at the end
+ nIndex = xContainer->getCount();
+
+ // UndoAction for insertion
+ if ( bUndo && GetNavModel()->m_pPropChangeList->CanUndo())
+ pFormModel->AddUndo(std::make_unique<FmUndoContainerAction>(*pFormModel, FmUndoContainerAction::Inserted,
+ xContainer, xCurrentChild, nIndex));
+
+ // insert in new container
+ if (pTargetData)
+ {
+ // insert in a form needs a FormComponent
+ xContainer->insertByIndex( nIndex,
+ Any( Reference< XFormComponent >( xCurrentChild, UNO_QUERY ) ) );
+ }
+ else
+ {
+ xContainer->insertByIndex( nIndex,
+ Any( Reference< XForm >( xCurrentChild, UNO_QUERY ) ) );
+ }
+
+ if (aEvts.hasElements())
+ {
+ xManager.set(xContainer, UNO_QUERY);
+ if (xManager.is())
+ xManager->registerScriptEvents(nIndex, aEvts);
+ }
+
+ GetNavModel()->m_pPropChangeList->UnLock();
+
+ // give an entry the new parent
+ pCurrentUserData->SetParent(pTargetData);
+
+ // give parent the new child
+ if (pTargetData)
+ pTargetData->GetChildList()->insert( std::unique_ptr<FmEntryData>(pCurrentUserData), nIndex );
+ else
+ GetNavModel()->GetRootList()->insert( std::unique_ptr<FmEntryData>(pCurrentUserData), nIndex );
+
+ // announce to myself and reselect
+ std::unique_ptr<weld::TreeIter> xNew = Insert( pCurrentUserData, nIndex );
+ if (bFirstEntry && xNew)
+ {
+ if (m_xTreeView->iter_parent(*xNew))
+ m_xTreeView->expand_row(*xNew);
+ }
+ }
+
+ UnlockSelectionHandling();
+
+ if( bUndo )
+ pFormModel->EndUndo();
+
+ // During the move, the markings of the underlying view did not change (because the view is not affected by the logical
+ // hierarchy of the form/control models. But my selection changed - which means I have to adjust it according to the
+ // view marks, again.
+ SynchronizeSelection();
+
+ // in addition, with the move of controls such things as "the current form" may have changed - force the shell
+ // to update itself accordingly
+ if( pFormShell && pFormShell->GetImpl() && pFormShell->GetFormView() )
+ pFormShell->GetImpl()->DetermineSelection_Lock( pFormShell->GetFormView()->GetMarkedObjectList() );
+
+ if ( m_aControlExchange.isClipboardOwner() && ( DND_ACTION_MOVE == _nAction ) )
+ m_aControlExchange->clear();
+
+ return _nAction;
+ }
+
+ sal_Int8 NavigatorTree::ExecuteDrop( const ExecuteDropEvent& rEvt )
+ {
+ sal_Int8 nResult( DND_ACTION_NONE );
+ if ( m_aControlExchange.isDragSource() )
+ nResult = implExecuteDataTransfer( *m_aControlExchange, rEvt.mnAction, rEvt.maPosPixel, true );
+ else
+ {
+ OControlTransferData aDroppedData( rEvt.maDropEvent.Transferable );
+ nResult = implExecuteDataTransfer( aDroppedData, rEvt.mnAction, rEvt.maPosPixel, true );
+ }
+ return nResult;
+ }
+
+ void NavigatorTree::doPaste()
+ {
+ std::unique_ptr<weld::TreeIter> xSelected(m_xTreeView->make_iterator());
+ if (!m_xTreeView->get_selected(xSelected.get()))
+ xSelected.reset();
+
+ try
+ {
+ if ( m_aControlExchange.isClipboardOwner() )
+ {
+ implExecuteDataTransfer( *m_aControlExchange, doingKeyboardCut( ) ? DND_ACTION_MOVE : DND_ACTION_COPY, xSelected.get(), false );
+ }
+ else
+ {
+ // the clipboard content
+ Reference< XClipboard > xClipboard(m_xTreeView->get_clipboard());
+ Reference< XTransferable > xTransferable;
+ if ( xClipboard.is() )
+ xTransferable = xClipboard->getContents();
+
+ OControlTransferData aClipboardContent( xTransferable );
+ implExecuteDataTransfer( aClipboardContent, DND_ACTION_COPY, xSelected.get(), false );
+ }
+ }
+ catch( const Exception& )
+ {
+ TOOLS_WARN_EXCEPTION( "svx", "NavigatorTree::doPaste" );
+ }
+ }
+
+ void NavigatorTree::doCopy()
+ {
+ if ( implPrepareExchange( DND_ACTION_COPY ) )
+ {
+ m_aControlExchange.setClipboardListener( LINK( this, NavigatorTree, OnClipboardAction ) );
+ m_aControlExchange.copyToClipboard(*m_xTreeView);
+ }
+ }
+
+ void NavigatorTree::ModelHasRemoved(const weld::TreeIter* pTypedEntry)
+ {
+ if (doingKeyboardCut())
+ {
+ auto aIter = std::find_if(m_aCutEntries.begin(), m_aCutEntries.end(),
+ [this, pTypedEntry](const auto& rElem) {
+ return m_xTreeView->iter_compare(*rElem, *pTypedEntry) == 0;
+ });
+ if (aIter != m_aCutEntries.end())
+ m_aCutEntries.erase(aIter);
+ }
+
+ if (m_aControlExchange.isDataExchangeActive())
+ {
+ if (0 == m_aControlExchange->onEntryRemoved(m_xTreeView.get(), pTypedEntry))
+ {
+ // last of the entries which we put into the clipboard has been deleted from the tree.
+ // Give up the clipboard ownership.
+ m_aControlExchange.clear();
+ }
+ }
+ }
+
+ void NavigatorTree::doCut()
+ {
+ if ( !implPrepareExchange( DND_ACTION_MOVE ) )
+ return;
+
+ m_aControlExchange.setClipboardListener( LINK( this, NavigatorTree, OnClipboardAction ) );
+ m_aControlExchange.copyToClipboard(*m_xTreeView);
+ m_bKeyboardCut = true;
+
+ // mark all the entries we just "cut" into the clipboard as "nearly moved"
+ for (const auto& rEntry : m_arrCurrentSelection )
+ {
+ if (!rEntry)
+ continue;
+ m_aCutEntries.emplace(m_xTreeView->make_iterator(rEntry.get()));
+ m_xTreeView->set_sensitive(*rEntry, false);
+ }
+ }
+
+ IMPL_LINK(NavigatorTree, KeyInputHdl, const ::KeyEvent&, rKEvt, bool)
+ {
+ const vcl::KeyCode& rCode = rKEvt.GetKeyCode();
+
+ // delete?
+ if (rCode.GetCode() == KEY_DELETE && !rCode.GetModifier())
+ {
+ DeleteSelection();
+ return true;
+ }
+
+ // copy'n'paste?
+ switch ( rCode.GetFunction() )
+ {
+ case KeyFuncType::CUT:
+ doCut();
+ break;
+
+ case KeyFuncType::PASTE:
+ if ( implAcceptPaste() )
+ doPaste();
+ break;
+
+ case KeyFuncType::COPY:
+ doCopy();
+ break;
+
+ default:
+ break;
+ }
+
+ return false;
+ }
+
+ IMPL_LINK(NavigatorTree, EditingEntryHdl, const weld::TreeIter&, rIter, bool)
+ {
+ // root, which isn't allowed to be renamed, has UserData=NULL
+ m_bEditing = !m_xTreeView->get_id(rIter).isEmpty();
+ return m_bEditing;
+ }
+
+ void NavigatorTree::NewForm(const weld::TreeIter& rParentEntry)
+ {
+ // get ParentFormData
+ if (!IsFormEntry(rParentEntry))
+ return;
+
+ FmFormData* pParentFormData = weld::fromId<FmFormData*>(m_xTreeView->get_id(rParentEntry));
+
+
+ // create new form
+ Reference<XComponentContext> xContext = comphelper::getProcessComponentContext();
+ Reference< XForm > xNewForm(xContext->getServiceManager()->createInstanceWithContext(FM_SUN_COMPONENT_FORM, xContext), UNO_QUERY);
+ if (!xNewForm.is())
+ return;
+
+ Reference< XPropertySet > xPropertySet(xNewForm, UNO_QUERY);
+ if (!xPropertySet.is())
+ return;
+
+ FmFormData* pNewFormData = new FmFormData(xNewForm, pParentFormData);
+
+
+ // set name
+ OUString aName = GenerateName(pNewFormData);
+ pNewFormData->SetText(aName);
+
+ try
+ {
+ xPropertySet->setPropertyValue( FM_PROP_NAME, Any(aName) );
+ // a form should always have the command type table as default
+ xPropertySet->setPropertyValue( FM_PROP_COMMANDTYPE, Any(sal_Int32(CommandType::TABLE)));
+ }
+ catch ( const Exception& )
+ {
+ OSL_FAIL("NavigatorTree::NewForm : could not set essential properties!");
+ }
+
+
+ // insert form
+ GetNavModel()->Insert(pNewFormData, SAL_MAX_UINT32, true);
+
+
+ // set new form as active
+ FmFormShell* pFormShell = GetNavModel()->GetFormShell();
+ if( pFormShell )
+ {
+ InterfaceBag aSelection;
+ aSelection.insert( Reference<XInterface>( xNewForm, UNO_QUERY ) );
+ pFormShell->GetImpl()->setCurrentSelection_Lock(std::move(aSelection));
+
+ pFormShell->GetViewShell()->GetViewFrame()->GetBindings().Invalidate(SID_FM_PROPERTIES, true, true);
+ }
+ GetNavModel()->SetModified();
+
+ // switch to EditMode
+ std::unique_ptr<weld::TreeIter> xNewEntry = FindEntry(pNewFormData);
+ m_xTreeView->start_editing(*xNewEntry);
+ m_bEditing = true;
+ }
+
+ FmControlData* NavigatorTree::NewControl(const OUString& rServiceName, const weld::TreeIter& rParentEntry, bool bEditName)
+ {
+ // get ParentForm
+ if (!GetNavModel()->GetFormShell())
+ return nullptr;
+ if (!IsFormEntry(rParentEntry))
+ return nullptr;
+
+ FmFormData* pParentFormData = weld::fromId<FmFormData*>(m_xTreeView->get_id(rParentEntry));
+ Reference<XForm> xParentForm(pParentFormData->GetFormIface());
+
+ // create new component
+ Reference<XComponentContext> xContext = comphelper::getProcessComponentContext();
+ Reference<XFormComponent> xNewComponent( xContext->getServiceManager()->createInstanceWithContext(rServiceName, xContext), UNO_QUERY);
+ if (!xNewComponent.is())
+ return nullptr;
+
+ FmControlData* pNewFormControlData = new FmControlData(xNewComponent, pParentFormData);
+
+ // set name
+ OUString sName = FmFormPageImpl::setUniqueName( xNewComponent, xParentForm );
+
+ pNewFormControlData->SetText( sName );
+
+ // insert FormComponent
+ GetNavModel()->Insert(pNewFormControlData, SAL_MAX_UINT32, true);
+ GetNavModel()->SetModified();
+
+ if (bEditName)
+ {
+ // switch to EditMode
+ std::unique_ptr<weld::TreeIter> xNewEntry = FindEntry( pNewFormControlData );
+ m_xTreeView->select(*xNewEntry);
+
+ m_xTreeView->start_editing(*xNewEntry);
+ m_bEditing = true;
+ }
+
+ return pNewFormControlData;
+ }
+
+ OUString NavigatorTree::GenerateName( FmEntryData const * pEntryData )
+ {
+ const sal_uInt16 nMaxCount = 99;
+ OUString aNewName;
+
+ // create base name
+ OUString aBaseName;
+ if( dynamic_cast<const FmFormData*>( pEntryData) != nullptr )
+ aBaseName = SvxResId( RID_STR_STDFORMNAME );
+ else if( dynamic_cast<const FmControlData*>( pEntryData) != nullptr )
+ aBaseName = SvxResId( RID_STR_CONTROL );
+
+
+ // create new name
+ FmFormData* pFormParentData = static_cast<FmFormData*>(pEntryData->GetParent());
+
+ for( sal_Int32 i=0; i<nMaxCount; i++ )
+ {
+ aNewName = aBaseName;
+ if( i>0 )
+ {
+ aNewName += " " + OUString::number(i);
+ }
+
+ if( GetNavModel()->FindData(aNewName, pFormParentData,false) == nullptr )
+ break;
+ }
+
+ return aNewName;
+ }
+
+ IMPL_LINK(NavigatorTree, EditedEntryHdl, const IterString&, rIterString, bool)
+ {
+ m_bEditing = false;
+
+ const weld::TreeIter& rIter = rIterString.first;
+
+ FmEntryData* pEntryData = weld::fromId<FmEntryData*>(m_xTreeView->get_id(rIter));
+ bool bRes = NavigatorTreeModel::Rename(pEntryData, rIterString.second);
+ if (!bRes)
+ {
+ m_xEditEntry = m_xTreeView->make_iterator(&rIter);
+ nEditEvent = Application::PostUserEvent(LINK(this, NavigatorTree, OnEdit));
+ }
+
+ return bRes;
+ }
+
+ IMPL_LINK_NOARG(NavigatorTree, OnEdit, void*, void)
+ {
+ nEditEvent = nullptr;
+ m_xTreeView->start_editing(*m_xEditEntry);
+ m_bEditing = true;
+ m_xEditEntry.reset();
+ }
+
+ IMPL_LINK_NOARG(NavigatorTree, OnEntrySelDesel, weld::TreeView&, void)
+ {
+ m_sdiState = SDI_DIRTY;
+
+ if (IsSelectionHandlingLocked())
+ return;
+
+ if (m_aSynchronizeTimer.IsActive())
+ m_aSynchronizeTimer.Stop();
+
+ m_aSynchronizeTimer.SetTimeout(EXPLORER_SYNC_DELAY);
+ m_aSynchronizeTimer.Start();
+ }
+
+ IMPL_LINK_NOARG(NavigatorTree, OnSynchronizeTimer, Timer *, void)
+ {
+ SynchronizeMarkList();
+ }
+
+ IMPL_LINK_NOARG(NavigatorTree, OnClipboardAction, OLocalExchange&, void)
+ {
+ if ( m_aControlExchange.isClipboardOwner() )
+ return;
+
+ if ( !doingKeyboardCut() )
+ return;
+
+ for (const auto& rEntry : m_aCutEntries)
+ {
+ if (!rEntry)
+ continue;
+ m_xTreeView->set_sensitive(*rEntry, true);
+ }
+ ListBoxEntrySet().swap(m_aCutEntries);
+
+ m_bKeyboardCut = false;
+ }
+
+ void NavigatorTree::ShowSelectionProperties(bool bForce)
+ {
+ // at first i need the FormShell
+ FmFormShell* pFormShell = GetNavModel()->GetFormShell();
+ if (!pFormShell)
+ // no shell -> impossible to set curObject -> leave
+ return;
+
+ CollectSelectionData(SDI_ALL);
+ SAL_WARN_IF(static_cast<size_t>(m_nFormsSelected + m_nControlsSelected
+ + (m_bRootSelected ? 1 : 0)) != m_arrCurrentSelection.size(),
+ "svx.form",
+ "NavigatorTree::ShowSelectionProperties : selection meta data invalid !");
+
+
+ InterfaceBag aSelection;
+ bool bSetSelectionAsMarkList = false;
+
+ if (m_bRootSelected)
+ ; // no properties for the root, neither for single nor for multi selection
+ else if ( m_nFormsSelected + m_nControlsSelected == 0 ) // none of the two should be less 0
+ ; // no selection -> no properties
+ else if ( m_nFormsSelected * m_nControlsSelected != 0 )
+ ; // mixed selection -> no properties
+ else
+ { // either only forms, or only controls are selected
+ if (m_arrCurrentSelection.size() == 1)
+ {
+ const std::unique_ptr<weld::TreeIter>& rIter = *m_arrCurrentSelection.begin();
+ if (m_nFormsSelected > 0)
+ { // exactly one form is selected
+ FmFormData* pFormData = weld::fromId<FmFormData*>(m_xTreeView->get_id(*rIter));
+ aSelection.insert( Reference< XInterface >( pFormData->GetFormIface(), UNO_QUERY ) );
+ }
+ else
+ { // exactly one control is selected (whatever hidden or normal)
+ FmEntryData* pEntryData = weld::fromId<FmEntryData*>(m_xTreeView->get_id(*rIter));
+
+ aSelection.insert( Reference< XInterface >( pEntryData->GetElement(), UNO_QUERY ) );
+ }
+ }
+ else
+ { // it's a MultiSelection, so we must build a MultiSet
+ if (m_nFormsSelected > 0)
+ { // ... only forms
+ // first of all collect PropertySet-Interfaces of the forms
+ SvLBoxEntrySortedArray::const_iterator it = m_arrCurrentSelection.begin();
+ for ( sal_Int32 i = 0; i < m_nFormsSelected; ++i )
+ {
+ const std::unique_ptr<weld::TreeIter>& rIter = *it;
+ FmFormData* pFormData = weld::fromId<FmFormData*>(m_xTreeView->get_id(*rIter));
+ aSelection.insert( pFormData->GetPropertySet() );
+ ++it;
+ }
+ }
+ else
+ { // ... only controls
+ if (m_nHiddenControls == m_nControlsSelected)
+ { // a MultiSet for properties of hidden controls
+ SvLBoxEntrySortedArray::const_iterator it = m_arrCurrentSelection.begin();
+ for ( sal_Int32 i = 0; i < m_nHiddenControls; ++i )
+ {
+ const std::unique_ptr<weld::TreeIter>& rIter = *it;
+ FmEntryData* pEntryData = weld::fromId<FmEntryData*>(m_xTreeView->get_id(*rIter));
+ aSelection.insert( pEntryData->GetPropertySet() );
+ ++it;
+ }
+ }
+ else if (m_nHiddenControls == 0)
+ { // only normal controls
+ bSetSelectionAsMarkList = true;
+ }
+ }
+ }
+
+ }
+
+ // and now my form and my SelObject
+ if ( bSetSelectionAsMarkList )
+ pFormShell->GetImpl()->setCurrentSelectionFromMark_Lock(pFormShell->GetFormView()->GetMarkedObjectList());
+ else
+ pFormShell->GetImpl()->setCurrentSelection_Lock(std::move(aSelection));
+
+ if (pFormShell->GetImpl()->IsPropBrwOpen_Lock() || bForce)
+ {
+ // and now deliver all to the PropertyBrowser
+ pFormShell->GetViewShell()->GetViewFrame()->GetDispatcher()->Execute( SID_FM_SHOW_PROPERTY_BROWSER, SfxCallMode::ASYNCHRON );
+ }
+ }
+
+
+ void NavigatorTree::DeleteSelection()
+ {
+ // of course, i can't delete root
+ bool bRootSelected = m_xTreeView->is_selected(*m_xRootEntry);
+ auto nSelectedEntries = m_xTreeView->count_selected_rows();
+ if (bRootSelected && (nSelectedEntries > 1)) // root and other elements ?
+ m_xTreeView->unselect(*m_xRootEntry); // yes -> remove root from selection
+
+ if ((nSelectedEntries == 0) || bRootSelected) // still root ?
+ return; // -> only selected element -> leave
+
+ DBG_ASSERT(!m_bPrevSelectionMixed, "NavigatorTree::DeleteSelection() : delete permitted if mark and selection are inconsistent");
+
+ // i need the FormModel later
+ FmFormShell* pFormShell = GetNavModel()->GetFormShell();
+ if (!pFormShell)
+ return;
+ FmFormModel* pFormModel = pFormShell->GetFormModel();
+ if (!pFormModel)
+ return;
+
+ // now I have to safeguard the DeleteList: if you delete a form and a dependent element
+ // - in this order - than the SvLBoxEntryPtr of the dependent element is already invalid,
+ // when it should be deleted... you have to prohibit this GPF, that of course would happen,
+ // so I take the 'normalized' list
+ CollectSelectionData( SDI_NORMALIZED );
+
+ // see below for why we need this mapping from models to shapes
+ FmFormView* pFormView = pFormShell->GetFormView();
+ SdrPageView* pPageView = pFormView ? pFormView->GetSdrPageView() : nullptr;
+ SdrPage* pPage = pPageView ? pPageView->GetPage() : nullptr;
+ DBG_ASSERT( pPage, "NavigatorTree::DeleteSelection: invalid form page!" );
+
+ MapModelToShape aModelShapes;
+ if ( pPage )
+ collectShapeModelMapping( pPage, aModelShapes );
+
+ // problem: we have to use ExplorerModel::Remove, since only this one properly deletes Form objects.
+ // But, the controls themself must be deleted via DeleteMarked (else, the Writer has some problems
+ // somewhere). In case I'd first delete the structure, then the controls, the UNDO would not work
+ // (since UNDO then would mean to first restore the controls, then the structure, means their parent
+ // form). The other way round, the EntryDatas would be invalid, if I'd first delete the controls and
+ // then go on to the structure. This means I have to delete the forms *after* the normal controls, so
+ // that during UNDO, they're restored in the proper order.
+ pFormShell->GetImpl()->EnableTrackProperties_Lock(false);
+ for (SvLBoxEntrySortedArray::reverse_iterator it = m_arrCurrentSelection.rbegin();
+ it != m_arrCurrentSelection.rend(); )
+ {
+ const std::unique_ptr<weld::TreeIter>& rIter = *it;
+ FmEntryData* pCurrent = weld::fromId<FmEntryData*>(m_xTreeView->get_id(*rIter));
+
+ // a form ?
+ auto pFormData = dynamic_cast<FmFormData*>(pCurrent);
+
+ // because deletion is done by the view, and i build on its MarkList,
+ // but normally only direct controls, no indirect ones, are marked in a marked form,
+ // I have to do it later
+ if (pFormData)
+ MarkViewObj(pFormData, true/*deep*/);
+
+ // a hidden control ?
+ bool bIsHidden = IsHiddenControl(pCurrent);
+
+ // keep forms and hidden controls, the rest not
+ if (!pFormData && !bIsHidden)
+ {
+ // well, no form and no hidden control -> we can remove it from m_arrCurrentSelection, as it will
+ // be deleted automatically. This is because for every model (except forms and hidden control models)
+ // there exist a shape, which is marked _if_and_only_if_ the model is selected in our tree.
+ if ( aModelShapes.find( pCurrent->GetElement() ) != aModelShapes.end() )
+ {
+ // if there's a shape for the current entry, then either it is marked or it is in a
+ // hidden layer (#i28502#), or something like this.
+ // In the first case, it will be deleted below, in the second case, we currently don't
+ // delete it, as there's no real (working!) API for this, neither in UNO nor in non-UNO.
+ m_arrCurrentSelection.erase( --(it.base()) );
+ }
+ else
+ ++it;
+ // In case there is no shape for the current entry, we keep the entry in m_arrCurrentSelection,
+ // since then we can definitely remove it.
+ }
+ else
+ ++it;
+ }
+ pFormShell->GetImpl()->EnableTrackProperties_Lock(true);
+
+ // let the view delete the marked controls
+ pFormShell->GetFormView()->DeleteMarked();
+
+ // start UNDO at this point. Unfortunately, this results in 2 UNDO actions, since DeleteMarked is
+ // creating an own one. However, if we'd move it before DeleteMarked, Writer does not really like
+ // this ... :(
+ // #i31038#
+ {
+
+ // initialize UNDO
+ OUString aUndoStr;
+ if ( m_arrCurrentSelection.size() == 1 )
+ {
+ aUndoStr = SvxResId(RID_STR_UNDO_CONTAINER_REMOVE);
+ if (m_nFormsSelected)
+ aUndoStr = aUndoStr.replaceFirst( "#", SvxResId( RID_STR_FORM ) );
+ else
+ // it must be a control (else the root would be selected, but it cannot be deleted)
+ aUndoStr = aUndoStr.replaceFirst( "#", SvxResId( RID_STR_CONTROL ) );
+ }
+ else
+ {
+ aUndoStr = SvxResId(RID_STR_UNDO_CONTAINER_REMOVE_MULTIPLE);
+ aUndoStr = aUndoStr.replaceFirst( "#", OUString::number( m_arrCurrentSelection.size() ) );
+ }
+ pFormModel->BegUndo(aUndoStr);
+ }
+
+ // remove remaining structure
+ for (const auto& rpSelection : m_arrCurrentSelection)
+ {
+ FmEntryData* pCurrent = weld::fromId<FmEntryData*>(m_xTreeView->get_id(*rpSelection));
+
+ // if the entry still has children, we skipped deletion of one of those children.
+ // This may for instance be because the shape is in a hidden layer, where we're unable
+ // to remove it
+ if ( pCurrent->GetChildList()->size() )
+ continue;
+
+ // one remaining subtile problem, before deleting it : if it's a form and the shell
+ // knows it as CurrentObject, I have to tell it something else
+ if (auto pFormData = dynamic_cast<FmFormData*>( pCurrent))
+ {
+ Reference< XForm > xCurrentForm( pFormData->GetFormIface() );
+ if (pFormShell->GetImpl()->getCurrentForm_Lock() == xCurrentForm) // shell knows form to be deleted ?
+ pFormShell->GetImpl()->forgetCurrentForm_Lock(); // -> take away ...
+ }
+ GetNavModel()->Remove(pCurrent, true);
+ }
+ pFormModel->EndUndo();
+ }
+
+
+ void NavigatorTree::CollectSelectionData(SELDATA_ITEMS sdiHow)
+ {
+ DBG_ASSERT(sdiHow != SDI_DIRTY, "NavigatorTree::CollectSelectionData : ever thought about your parameter ? DIRTY ?");
+ if (sdiHow == m_sdiState)
+ return;
+
+ m_arrCurrentSelection.clear();
+ m_nFormsSelected = m_nControlsSelected = m_nHiddenControls = 0;
+ m_bRootSelected = false;
+
+ m_xTreeView->selected_foreach([this, sdiHow](weld::TreeIter& rSelectionLoop){
+ // count different elements
+ if (m_xTreeView->iter_compare(rSelectionLoop, *m_xRootEntry) == 0)
+ m_bRootSelected = true;
+ else
+ {
+ if (IsFormEntry(rSelectionLoop))
+ ++m_nFormsSelected;
+ else
+ {
+ ++m_nControlsSelected;
+ if (IsHiddenControl(weld::fromId<FmEntryData*>(m_xTreeView->get_id(rSelectionLoop))))
+ ++m_nHiddenControls;
+ }
+ }
+
+ if (sdiHow == SDI_NORMALIZED)
+ {
+ // don't take something with a selected ancestor
+ if (m_xTreeView->iter_compare(rSelectionLoop, *m_xRootEntry) == 0)
+ m_arrCurrentSelection.emplace(m_xTreeView->make_iterator(&rSelectionLoop));
+ else
+ {
+ std::unique_ptr<weld::TreeIter> xParentLoop(m_xTreeView->make_iterator(&rSelectionLoop));
+ bool bParentLoop = m_xTreeView->iter_parent(*xParentLoop);
+ while (bParentLoop)
+ {
+ // actually i would have to test, if parent is part of m_arr_CurrentSelection ...
+ // but if it's selected, then it's in m_arrCurrentSelection
+ // or one of its ancestors, which was selected earlier.
+ // In both cases IsSelected is enough
+ if (m_xTreeView->is_selected(*xParentLoop))
+ break;
+ else
+ {
+ if (m_xTreeView->iter_compare(*xParentLoop, *m_xRootEntry) == 0)
+ {
+ // until root (exclusive), there was no selected parent -> entry belongs to normalized list
+ m_arrCurrentSelection.emplace(m_xTreeView->make_iterator(&rSelectionLoop));
+ break;
+ }
+ else
+ bParentLoop = m_xTreeView->iter_parent(*xParentLoop);
+ }
+ }
+ }
+ }
+ else if (sdiHow == SDI_NORMALIZED_FORMARK)
+ {
+ std::unique_ptr<weld::TreeIter> xParent(m_xTreeView->make_iterator(&rSelectionLoop));
+ bool bParent = m_xTreeView->iter_parent(*xParent);
+ if (!bParent || !m_xTreeView->is_selected(*xParent) || IsFormEntry(rSelectionLoop))
+ m_arrCurrentSelection.emplace(m_xTreeView->make_iterator(&rSelectionLoop));
+ }
+ else
+ m_arrCurrentSelection.emplace(m_xTreeView->make_iterator(&rSelectionLoop));
+
+ return false;
+ });
+
+ m_sdiState = sdiHow;
+ }
+
+ void NavigatorTree::SynchronizeSelection(FmEntryDataArray& arredToSelect)
+ {
+ LockSelectionHandling();
+ if (arredToSelect.empty())
+ {
+ m_xTreeView->unselect_all();
+ }
+ else
+ {
+ // compare current selection with requested SelectList
+ m_xTreeView->selected_foreach([this, &arredToSelect](weld::TreeIter& rSelection) {
+ FmEntryData* pCurrent = weld::fromId<FmEntryData*>(m_xTreeView->get_id(rSelection));
+ if (pCurrent != nullptr)
+ {
+ FmEntryDataArray::iterator it = arredToSelect.find(pCurrent);
+ if ( it != arredToSelect.end() )
+ { // entry already selected, but also in SelectList
+ // remove it from there
+ arredToSelect.erase(it);
+ } else
+ { // entry selected, but not in SelectList -> remove selection
+ m_xTreeView->unselect(rSelection);
+ // make it visible (maybe it's the only modification i do in this handler
+ // so you should see it
+ m_xTreeView->scroll_to_row(rSelection);
+ }
+ }
+ else
+ m_xTreeView->unselect(rSelection);
+
+ return false;
+ });
+
+ // now SelectList contains only entries, which have to be selected
+ // two possibilities : 1) run through SelectList, get SvTreeListEntry for every entry and select it (is more intuitive)
+ // 2) run through my SvLBoxEntries and select those, i can find in the SelectList
+ // 1) needs =(k*n) (k=length of SelectList, n=number of entries),
+ // plus the fact, that FindEntry uses extensive IsEqualWithoutChilden instead of comparing pointer to UserData
+ // 2) needs =(n*log k), duplicates some code from FindEntry
+ // This may be a frequently used code ( at every change in mark of the view!),
+ // so i use latter one
+ m_xTreeView->all_foreach([this, &arredToSelect](weld::TreeIter& rLoop){
+ FmEntryData* pCurEntryData = weld::fromId<FmEntryData*>(m_xTreeView->get_id(rLoop));
+ FmEntryDataArray::iterator it = arredToSelect.find(pCurEntryData);
+ if (it != arredToSelect.end())
+ {
+ m_xTreeView->select(rLoop);
+ m_xTreeView->scroll_to_row(rLoop);
+ }
+
+ return false;
+ });
+ }
+ UnlockSelectionHandling();
+ }
+
+
+ void NavigatorTree::SynchronizeSelection()
+ {
+ // shell and view
+ FmFormShell* pFormShell = GetNavModel()->GetFormShell();
+ if(!pFormShell) return;
+
+ FmFormView* pFormView = pFormShell->GetFormView();
+ if (!pFormView) return;
+
+ GetNavModel()->BroadcastMarkedObjects(pFormView->GetMarkedObjectList());
+ }
+
+
+ void NavigatorTree::SynchronizeMarkList()
+ {
+ // i'll need this shell
+ FmFormShell* pFormShell = GetNavModel()->GetFormShell();
+ if (!pFormShell) return;
+
+ CollectSelectionData(SDI_NORMALIZED_FORMARK);
+
+ // the view shouldn't notify now if MarkList changed
+ pFormShell->GetImpl()->EnableTrackProperties_Lock(false);
+
+ UnmarkAllViewObj();
+
+ for (auto& rSelectionLoop : m_arrCurrentSelection)
+ {
+ // When form selection, mark all controls of form
+ if (IsFormEntry(*rSelectionLoop) && m_xTreeView->iter_compare(*rSelectionLoop, *m_xRootEntry) != 0)
+ MarkViewObj(weld::fromId<FmFormData*>(m_xTreeView->get_id(*rSelectionLoop)), false/*deep*/);
+
+ // When control selection, mark Control-SdrObjects
+ else if (IsFormComponentEntry(*rSelectionLoop))
+ {
+ FmControlData* pControlData = weld::fromId<FmControlData*>(m_xTreeView->get_id(*rSelectionLoop));
+ if (pControlData)
+ {
+
+ // When HiddenControl no object can be selected
+ Reference< XFormComponent > xFormComponent( pControlData->GetFormComponent());
+ if (!xFormComponent.is())
+ continue;
+ Reference< XPropertySet > xSet(xFormComponent, UNO_QUERY);
+ if (!xSet.is())
+ continue;
+
+ sal_uInt16 nClassId = ::comphelper::getINT16(xSet->getPropertyValue(FM_PROP_CLASSID));
+ if (nClassId != FormComponentType::HIDDENCONTROL)
+ MarkViewObj(pControlData);
+ }
+ }
+ }
+
+ // if PropertyBrowser is open, I have to adopt it according to my selection
+ // (Not as MarkList of view : if a form is selected, all belonging controls are selected in the view
+ // but of course i want to see the form-properties
+ ShowSelectionProperties();
+
+ // reset flag at view
+ pFormShell->GetImpl()->EnableTrackProperties_Lock(true);
+
+ // if exactly one form is selected now, shell should notice it as CurrentForm
+ // (if selection handling isn't locked, view cares about it in MarkListHasChanged
+ // but mechanism doesn't work, if form is empty for example
+ if ((m_arrCurrentSelection.size() != 1) || (m_nFormsSelected != 1))
+ return;
+
+ std::unique_ptr<weld::TreeIter> xSelected(m_xTreeView->make_iterator());
+ if (!m_xTreeView->get_selected(xSelected.get()))
+ xSelected.reset();
+ FmFormData* pSingleSelectionData = xSelected ? dynamic_cast<FmFormData*>(weld::fromId<FmEntryData*>(m_xTreeView->get_id(*xSelected)))
+ : nullptr;
+ DBG_ASSERT( pSingleSelectionData, "NavigatorTree::SynchronizeMarkList: invalid selected form!" );
+ if ( pSingleSelectionData )
+ {
+ InterfaceBag aSelection;
+ aSelection.insert( Reference< XInterface >( pSingleSelectionData->GetFormIface(), UNO_QUERY ) );
+ pFormShell->GetImpl()->setCurrentSelection_Lock(std::move(aSelection));
+ }
+ }
+
+ bool NavigatorTree::IsHiddenControl(FmEntryData const * pEntryData)
+ {
+ if (pEntryData == nullptr) return false;
+
+ Reference< XPropertySet > xProperties( pEntryData->GetPropertySet() );
+ if (::comphelper::hasProperty(FM_PROP_CLASSID, xProperties))
+ {
+ Any aClassID = xProperties->getPropertyValue( FM_PROP_CLASSID );
+ return (::comphelper::getINT16(aClassID) == FormComponentType::HIDDENCONTROL);
+ }
+ return false;
+ }
+
+ void NavigatorTree::UnmarkAllViewObj()
+ {
+ FmFormShell* pFormShell = GetNavModel()->GetFormShell();
+ if( !pFormShell )
+ return;
+ FmFormView* pFormView = pFormShell->GetFormView();
+ pFormView->UnMarkAll();
+ }
+
+ void NavigatorTree::MarkViewObj(FmFormData const * pFormData, bool bDeep )
+ {
+ FmFormShell* pFormShell = GetNavModel()->GetFormShell();
+ if( !pFormShell )
+ return;
+
+ // first collect all sdrobjects
+ ::std::set< Reference< XFormComponent > > aObjects;
+ CollectObjects(pFormData,bDeep,aObjects);
+
+
+ // find and select appropriate SdrObj in page
+ FmFormView* pFormView = pFormShell->GetFormView();
+ SdrPageView* pPageView = pFormView->GetSdrPageView();
+ SdrPage* pPage = pPageView->GetPage();
+ //FmFormPage* pFormPage = dynamic_cast< FmFormPage* >( pPage );
+
+ SdrObjListIter aIter( pPage );
+ while ( aIter.IsMore() )
+ {
+ SdrObject* pSdrObject = aIter.Next();
+ FmFormObj* pFormObject = FmFormObj::GetFormObject( pSdrObject );
+ if ( !pFormObject )
+ continue;
+
+ Reference< XFormComponent > xControlModel( pFormObject->GetUnoControlModel(),UNO_QUERY );
+ if ( xControlModel.is() && aObjects.find(xControlModel) != aObjects.end() && !pFormView->IsObjMarked( pSdrObject ) )
+ {
+ // unfortunately, the writer doesn't like marking an already-marked object, again, so reset the mark first
+ pFormView->MarkObj( pSdrObject, pPageView );
+ }
+ } // while ( aIter.IsMore() )
+ // make the mark visible
+ ::tools::Rectangle aMarkRect( pFormView->GetAllMarkedRect());
+ for ( sal_uInt32 i = 0; i < pFormView->PaintWindowCount(); ++i )
+ {
+ SdrPaintWindow* pPaintWindow = pFormView->GetPaintWindow( i );
+ OutputDevice& rOutDev = pPaintWindow->GetOutputDevice();
+ if ( ( OUTDEV_WINDOW == rOutDev.GetOutDevType() ) && !aMarkRect.IsEmpty() )
+ {
+ pFormView->MakeVisible( aMarkRect, *rOutDev.GetOwnerWindow() );
+ }
+ } // for ( sal_uInt32 i = 0; i < pFormView->PaintWindowCount(); ++i )
+ }
+
+ void NavigatorTree::CollectObjects(FmFormData const * pFormData, bool bDeep, ::std::set< Reference< XFormComponent > >& _rObjects)
+ {
+ FmEntryDataList* pChildList = pFormData->GetChildList();
+ for( size_t i = 0; i < pChildList->size(); ++i )
+ {
+ FmEntryData* pEntryData = pChildList->at( i );
+ if( auto pControlData = dynamic_cast<FmControlData*>( pEntryData) )
+ {
+ _rObjects.insert(pControlData->GetFormComponent());
+ } // if( dynamic_cast<const FmControlData*>( pEntryData) != nullptr )
+ else if (bDeep)
+ if (auto pEntryFormData = dynamic_cast<FmFormData*>( pEntryData))
+ CollectObjects(pEntryFormData, bDeep, _rObjects);
+ } // for( sal_uInt32 i=0; i<pChildList->Count(); i++ )
+ }
+
+ void NavigatorTree::MarkViewObj( FmControlData const * pControlData)
+ {
+ if( !pControlData )
+ return;
+ FmFormShell* pFormShell = GetNavModel()->GetFormShell();
+ if( !pFormShell )
+ return;
+
+
+ // find and select appropriate SdrObj
+ FmFormView* pFormView = pFormShell->GetFormView();
+ Reference< XFormComponent > xFormComponent( pControlData->GetFormComponent());
+ SdrPageView* pPageView = pFormView->GetSdrPageView();
+ SdrPage* pPage = pPageView->GetPage();
+
+ bool bPaint = false;
+ SdrObjListIter aIter( pPage );
+ while ( aIter.IsMore() )
+ {
+ SdrObject* pSdrObject = aIter.Next();
+ FmFormObj* pFormObject = FmFormObj::GetFormObject( pSdrObject );
+ if ( !pFormObject )
+ continue;
+
+ Reference< XInterface > xControlModel( pFormObject->GetUnoControlModel() );
+ if ( xControlModel != xFormComponent )
+ continue;
+
+ // mark the object
+ if ( !pFormView->IsObjMarked( pSdrObject ) )
+ // unfortunately, the writer doesn't like marking an already-marked object, again, so reset the mark first
+ pFormView->MarkObj( pSdrObject, pPageView );
+
+ bPaint = true;
+
+ } // while ( aIter.IsMore() )
+ if ( !bPaint )
+ return;
+
+ // make the mark visible
+ ::tools::Rectangle aMarkRect( pFormView->GetAllMarkedRect());
+ for ( sal_uInt32 i = 0; i < pFormView->PaintWindowCount(); ++i )
+ {
+ SdrPaintWindow* pPaintWindow = pFormView->GetPaintWindow( i );
+ OutputDevice& rOutDev = pPaintWindow->GetOutputDevice();
+ if ( OUTDEV_WINDOW == rOutDev.GetOutDevType() )
+ {
+ pFormView->MakeVisible( aMarkRect, *rOutDev.GetOwnerWindow() );
+ }
+ } // for ( sal_uInt32 i = 0; i < pFormView->PaintWindowCount(); ++i )
+ }
+
+
+}
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/form/navigatortreemodel.cxx b/svx/source/form/navigatortreemodel.cxx
new file mode 100644
index 000000000..218e2ba4a
--- /dev/null
+++ b/svx/source/form/navigatortreemodel.cxx
@@ -0,0 +1,907 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * 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/dialmgr.hxx>
+#include <svx/fmshell.hxx>
+#include <svx/fmmodel.hxx>
+#include <svx/fmpage.hxx>
+#include <svx/svditer.hxx>
+#include <svx/svdogrp.hxx>
+
+#include <fmprop.hxx>
+
+#include <fmundo.hxx>
+#include <fmexpl.hxx>
+#include <svx/strings.hrc>
+#include <fmshimp.hxx>
+#include <fmobj.hxx>
+#include <o3tl/safeint.hxx>
+#include <sfx2/objsh.hxx>
+#include <tools/diagnose_ex.h>
+#include <com/sun/star/container/XContainer.hpp>
+#include <comphelper/types.hxx>
+
+
+namespace svxform
+{
+
+
+ using namespace ::com::sun::star::uno;
+ using namespace ::com::sun::star::lang;
+ using namespace ::com::sun::star::beans;
+ using namespace ::com::sun::star::form;
+ using namespace ::com::sun::star::awt;
+ using namespace ::com::sun::star::container;
+ using namespace ::com::sun::star::script;
+ using namespace ::com::sun::star::sdb;
+
+ OFormComponentObserver::OFormComponentObserver(NavigatorTreeModel* _pModel)
+ :m_pNavModel(_pModel)
+ ,m_nLocks(0)
+ ,m_bCanUndo(true)
+ {
+ }
+
+ // XPropertyChangeListener
+
+ void SAL_CALL OFormComponentObserver::disposing(const EventObject& Source)
+ {
+ Remove( Source.Source );
+ }
+
+
+ void SAL_CALL OFormComponentObserver::propertyChange(const PropertyChangeEvent& evt)
+ {
+ if( !m_pNavModel ) return;
+ if( evt.PropertyName != FM_PROP_NAME ) return;
+
+ Reference< XFormComponent > xFormComponent(evt.Source, UNO_QUERY);
+ Reference< XForm > xForm(evt.Source, UNO_QUERY);
+
+ FmEntryData* pEntryData( nullptr );
+ if( xForm.is() )
+ pEntryData = m_pNavModel->FindData( xForm, m_pNavModel->GetRootList() );
+ else if( xFormComponent.is() )
+ pEntryData = m_pNavModel->FindData( xFormComponent, m_pNavModel->GetRootList() );
+
+ if( pEntryData )
+ {
+ OUString aNewName = ::comphelper::getString(evt.NewValue);
+ pEntryData->SetText( aNewName );
+ FmNavNameChangedHint aNameChangedHint( pEntryData, aNewName );
+ m_pNavModel->Broadcast( aNameChangedHint );
+ }
+ }
+
+ // XContainerListener
+
+ void SAL_CALL OFormComponentObserver::elementInserted(const ContainerEvent& evt)
+ {
+ if (IsLocked() || !m_pNavModel)
+ return;
+
+ // insert no Undoaction
+ m_bCanUndo = false;
+
+ Reference< XInterface > xTemp;
+ evt.Element >>= xTemp;
+ Insert(xTemp, ::comphelper::getINT32(evt.Accessor));
+
+ m_bCanUndo = true;
+ }
+
+
+ void OFormComponentObserver::Insert(const Reference< XInterface > & xIface, sal_Int32 nIndex)
+ {
+ Reference< XForm > xForm(xIface, UNO_QUERY);
+ if (xForm.is())
+ {
+ m_pNavModel->InsertForm(xForm, sal_uInt32(nIndex));
+ Reference< XIndexContainer > xContainer(xForm, UNO_QUERY);
+ Reference< XInterface > xTemp;
+ for (sal_Int32 i = 0; i < xContainer->getCount(); i++)
+ {
+ xContainer->getByIndex(i) >>= xTemp;
+ Insert(xTemp, i);
+ }
+ }
+ else
+ {
+ Reference< XFormComponent > xFormComp(xIface, UNO_QUERY);
+ if (xFormComp.is())
+ m_pNavModel->InsertFormComponent(xFormComp, sal_uInt32(nIndex));
+ }
+ }
+
+
+ void SAL_CALL OFormComponentObserver::elementReplaced(const ContainerEvent& evt)
+ {
+ if (IsLocked() || !m_pNavModel)
+ return;
+
+ m_bCanUndo = false;
+
+ // delete EntryData
+ Reference< XFormComponent > xReplaced;
+ evt.ReplacedElement >>= xReplaced;
+ FmEntryData* pEntryData = m_pNavModel->FindData(xReplaced, m_pNavModel->GetRootList());
+ if (pEntryData)
+ {
+ if (dynamic_cast<const FmControlData*>( pEntryData) != nullptr)
+ {
+ Reference< XFormComponent > xComp;
+ evt.Element >>= xComp;
+ DBG_ASSERT(xComp.is(), "OFormComponentObserver::elementReplaced : invalid argument !");
+ // FmControlData should be coupled with XFormComponent
+ m_pNavModel->ReplaceFormComponent(xReplaced, xComp);
+ }
+ else if (dynamic_cast<const FmFormData*>( pEntryData) != nullptr)
+ {
+ OSL_FAIL("replacing forms not implemented yet !");
+ }
+ }
+
+ m_bCanUndo = true;
+ }
+
+
+ void OFormComponentObserver::Remove( const css::uno::Reference< css::uno::XInterface >& _rxElement )
+ {
+ if (IsLocked() || !m_pNavModel)
+ return;
+
+ m_bCanUndo = false;
+
+
+ // delete EntryData
+ FmEntryData* pEntryData = m_pNavModel->FindData( _rxElement, m_pNavModel->GetRootList() );
+ if (pEntryData)
+ m_pNavModel->Remove(pEntryData);
+
+ m_bCanUndo = true;
+ }
+
+
+ void SAL_CALL OFormComponentObserver::elementRemoved(const ContainerEvent& evt)
+ {
+ Reference< XInterface > xElement;
+ evt.Element >>= xElement;
+ Remove( xElement );
+ }
+
+ NavigatorTreeModel::NavigatorTreeModel()
+ :m_pFormShell(nullptr)
+ ,m_pFormPage(nullptr)
+ ,m_pFormModel(nullptr)
+ {
+ m_pPropChangeList = new OFormComponentObserver(this);
+ m_pRootList.reset( new FmEntryDataList() );
+ }
+
+ NavigatorTreeModel::~NavigatorTreeModel()
+ {
+
+ // unregister Listener
+ if( m_pFormShell)
+ {
+ FmFormModel* pFormModel = m_pFormShell->GetFormModel();
+ if( pFormModel && IsListening(*pFormModel))
+ EndListening( *pFormModel );
+
+ if (IsListening(*m_pFormShell))
+ EndListening(*m_pFormShell);
+ }
+
+ Clear();
+ m_pRootList.reset();
+ m_pPropChangeList->ReleaseModel();
+ }
+
+
+ void NavigatorTreeModel::SetModified()
+ {
+ if( !m_pFormShell ) return;
+ SfxObjectShell* pObjShell = m_pFormShell->GetFormModel()->GetObjectShell();
+ if( !pObjShell ) return;
+ pObjShell->SetModified();
+ }
+
+
+ void NavigatorTreeModel::Clear()
+ {
+ Reference< css::form::XForms > xForms( GetForms());
+ if(xForms.is())
+ xForms->removeContainerListener(m_pPropChangeList);
+
+
+ // delete RootList
+ GetRootList()->clear();
+
+
+ // notify UI
+ FmNavClearedHint aClearedHint;
+ Broadcast( aClearedHint );
+ }
+
+
+ Reference< css::form::XForms > NavigatorTreeModel::GetForms() const
+ {
+ if( !m_pFormShell || !m_pFormShell->GetCurPage())
+ return nullptr;
+ else
+ return m_pFormShell->GetCurPage()->GetForms();
+ }
+
+
+ void NavigatorTreeModel::Insert(FmEntryData* pEntry, sal_uInt32 nRelPos, bool bAlterModel)
+ {
+ if (IsListening(*m_pFormModel))
+ EndListening(*m_pFormModel);
+
+ m_pPropChangeList->Lock();
+ FmFormData* pFolder = static_cast<FmFormData*>( pEntry->GetParent() );
+ Reference< XChild > xElement( pEntry->GetChildIFace() );
+ if (bAlterModel)
+ {
+ OUString aStr;
+ if (dynamic_cast<const FmFormData*>( pEntry) != nullptr)
+ aStr = SvxResId(RID_STR_FORM);
+ else
+ aStr = SvxResId(RID_STR_CONTROL);
+
+ Reference< XIndexContainer > xContainer;
+ if (pFolder)
+ xContainer.set(pFolder->GetFormIface(), UNO_QUERY);
+ else
+ xContainer = GetForms();
+
+ bool bUndo = m_pFormModel->IsUndoEnabled();
+
+ if( bUndo )
+ {
+ OUString aUndoStr(SvxResId(RID_STR_UNDO_CONTAINER_INSERT));
+ aUndoStr = aUndoStr.replaceFirst("#", aStr);
+ m_pFormModel->BegUndo(aUndoStr);
+ }
+
+ if (nRelPos >= o3tl::make_unsigned(xContainer->getCount()))
+ nRelPos = static_cast<sal_uInt32>(xContainer->getCount());
+
+ // UndoAction
+ if ( bUndo && m_pPropChangeList->CanUndo())
+ {
+ m_pFormModel->AddUndo(std::make_unique<FmUndoContainerAction>(*m_pFormModel,
+ FmUndoContainerAction::Inserted,
+ xContainer,
+ xElement,
+ nRelPos));
+ }
+
+ // Element has to be of the expected type by the container
+ if (xContainer->getElementType() ==
+ cppu::UnoType<XForm>::get())
+
+ {
+ Reference< XForm > xElementAsForm(xElement, UNO_QUERY);
+ xContainer->insertByIndex(nRelPos, Any(xElementAsForm));
+ }
+ else if (xContainer->getElementType() ==
+ cppu::UnoType<XFormComponent>::get())
+
+ {
+ Reference< XFormComponent > xElementAsComponent(xElement, UNO_QUERY);
+ xContainer->insertByIndex(nRelPos, Any(xElementAsComponent));
+ }
+ else
+ {
+ OSL_FAIL("NavigatorTreeModel::Insert : the parent container needs an elementtype I don't know !");
+ }
+
+ if( bUndo )
+ m_pFormModel->EndUndo();
+ }
+
+ // register as PropertyChangeListener
+ Reference< XPropertySet > xSet(xElement, UNO_QUERY);
+ if( xSet.is() )
+ xSet->addPropertyChangeListener( FM_PROP_NAME, m_pPropChangeList );
+
+
+ // Remove data from model
+ if (dynamic_cast<const FmFormData*>( pEntry) != nullptr)
+ {
+ Reference< XContainer > xContainer(xElement, UNO_QUERY);
+ if (xContainer.is())
+ xContainer->addContainerListener(m_pPropChangeList);
+ }
+
+ if (pFolder)
+ pFolder->GetChildList()->insert( std::unique_ptr<FmEntryData>(pEntry), nRelPos );
+ else
+ GetRootList()->insert( std::unique_ptr<FmEntryData>(pEntry), nRelPos );
+
+
+ // notify UI
+ FmNavInsertedHint aInsertedHint( pEntry, nRelPos );
+ Broadcast( aInsertedHint );
+
+ m_pPropChangeList->UnLock();
+ if (IsListening(*m_pFormModel))
+ StartListening(*m_pFormModel);
+ }
+
+
+ void NavigatorTreeModel::Remove(FmEntryData* pEntry, bool bAlterModel)
+ {
+
+ // get form and parent
+ if (!pEntry || !m_pFormModel)
+ return;
+
+ if (IsListening(*m_pFormModel))
+ EndListening(*m_pFormModel);
+
+ const bool bUndo = m_pFormModel->IsUndoEnabled();
+
+ m_pPropChangeList->Lock();
+ FmFormData* pFolder = static_cast<FmFormData*>( pEntry->GetParent() );
+ Reference< XChild > xElement ( pEntry->GetChildIFace() );
+ if (bAlterModel)
+ {
+ OUString aStr;
+ if (dynamic_cast<const FmFormData*>( pEntry) != nullptr)
+ aStr = SvxResId(RID_STR_FORM);
+ else
+ aStr = SvxResId(RID_STR_CONTROL);
+
+ if( bUndo )
+ {
+ OUString aUndoStr(SvxResId(RID_STR_UNDO_CONTAINER_REMOVE));
+ aUndoStr = aUndoStr.replaceFirst("#", aStr);
+ m_pFormModel->BegUndo(aUndoStr);
+ }
+ }
+
+ // now real deletion of data form model
+ if (auto pFormData = dynamic_cast<FmFormData*>( pEntry))
+ RemoveForm(pFormData);
+ else
+ RemoveFormComponent(static_cast<FmControlData*>(pEntry));
+
+
+ if (bAlterModel)
+ {
+ Reference< XIndexContainer > xContainer(xElement->getParent(), UNO_QUERY);
+ // remove from Container
+ sal_Int32 nContainerIndex = getElementPos(xContainer, xElement);
+ // UndoAction
+ if (nContainerIndex >= 0)
+ {
+ if ( bUndo && m_pPropChangeList->CanUndo())
+ {
+ m_pFormModel->AddUndo(std::make_unique<FmUndoContainerAction>(*m_pFormModel,
+ FmUndoContainerAction::Removed,
+ xContainer,
+ xElement, nContainerIndex ));
+ }
+ else if( !m_pPropChangeList->CanUndo() )
+ {
+ FmUndoContainerAction::DisposeElement( xElement );
+ }
+
+ xContainer->removeByIndex(nContainerIndex );
+ }
+
+ if( bUndo )
+ m_pFormModel->EndUndo();
+ }
+
+ // remove from parent
+ if (pFolder)
+ pFolder->GetChildList()->removeNoDelete( pEntry );
+ else
+ {
+ GetRootList()->removeNoDelete( pEntry );
+
+ // If root has no more form, reset CurForm at shell
+ if ( !GetRootList()->size() )
+ m_pFormShell->GetImpl()->forgetCurrentForm_Lock();
+ }
+
+
+ // notify UI
+ FmNavRemovedHint aRemovedHint( pEntry );
+ Broadcast( aRemovedHint );
+
+ // delete entry
+ delete pEntry;
+
+ m_pPropChangeList->UnLock();
+ StartListening(*m_pFormModel);
+ }
+
+
+ void NavigatorTreeModel::RemoveForm(FmFormData const * pFormData)
+ {
+
+ // get form and parent
+ if (!pFormData || !m_pFormModel)
+ return;
+
+ FmEntryDataList* pChildList = pFormData->GetChildList();
+ for ( size_t i = pChildList->size(); i > 0; )
+ {
+ FmEntryData* pEntryData = pChildList->at( --i );
+
+
+ // Child is form -> recursive call
+ if( auto pChildFormData = dynamic_cast<FmFormData*>( pEntryData) )
+ RemoveForm(pChildFormData);
+ else if( auto pChildControlData = dynamic_cast<FmControlData*>( pEntryData) )
+ RemoveFormComponent(pChildControlData);
+ }
+
+
+ // unregister as PropertyChangeListener
+ Reference< XPropertySet > xSet( pFormData->GetPropertySet() );
+ if ( xSet.is() )
+ xSet->removePropertyChangeListener( FM_PROP_NAME, m_pPropChangeList );
+ }
+
+
+ void NavigatorTreeModel::RemoveFormComponent(FmControlData const * pControlData)
+ {
+
+ // get control and parent
+ if (!pControlData)
+ return;
+
+
+ // unregister as PropertyChangeListener
+ Reference< XPropertySet > xSet( pControlData->GetPropertySet() );
+ if (xSet.is())
+ xSet->removePropertyChangeListener( FM_PROP_NAME, m_pPropChangeList);
+ }
+
+
+ void NavigatorTreeModel::FillBranch( FmFormData* pFormData )
+ {
+
+ // insert forms from root
+ if( pFormData == nullptr )
+ {
+ Reference< XIndexContainer > xForms = GetForms();
+ if (!xForms.is())
+ return;
+
+ Reference< XForm > xSubForm;
+ for (sal_Int32 i=0; i<xForms->getCount(); ++i)
+ {
+ DBG_ASSERT( xForms->getByIndex(i).getValueType() == cppu::UnoType<XForm>::get(),
+ "NavigatorTreeModel::FillBranch : the root container should supply only elements of type XForm");
+
+ xForms->getByIndex(i) >>= xSubForm;
+ FmFormData* pSubFormData = new FmFormData(xSubForm, pFormData);
+ Insert( pSubFormData );
+
+ // new branch, if SubForm contains Subforms itself
+ FillBranch( pSubFormData );
+ }
+ }
+
+
+ // insert components
+ else
+ {
+ Reference< XIndexContainer > xComponents( GetFormComponents(pFormData));
+ if( !xComponents.is() ) return;
+
+ FmControlData* pNewControlData;
+ FmFormData* pSubFormData;
+
+ Reference< XFormComponent > xCurrentComponent;
+ for (sal_Int32 j=0; j<xComponents->getCount(); ++j)
+ {
+ xComponents->getByIndex(j) >>= xCurrentComponent;
+ Reference< XForm > xSubForm(xCurrentComponent, UNO_QUERY);
+
+ if (xSubForm.is())
+ { // actual component is a form
+ pSubFormData = new FmFormData(xSubForm, pFormData);
+ Insert(pSubFormData);
+
+
+ // new branch, if SubForm contains Subforms itself
+ FillBranch(pSubFormData);
+ }
+ else
+ {
+ pNewControlData = new FmControlData(xCurrentComponent, pFormData);
+ Insert(pNewControlData);
+ }
+ }
+ }
+ }
+
+
+ void NavigatorTreeModel::InsertForm(const Reference< XForm > & xForm, sal_uInt32 nRelPos)
+ {
+ FmFormData* pFormData = static_cast<FmFormData*>(FindData( xForm, GetRootList() ));
+ if (pFormData)
+ return;
+
+
+ // set ParentData
+ Reference< XInterface > xIFace( xForm->getParent());
+ Reference< XForm > xParentForm(xIFace, UNO_QUERY);
+ FmFormData* pParentData = nullptr;
+ if (xParentForm.is())
+ pParentData = static_cast<FmFormData*>(FindData( xParentForm, GetRootList() ));
+
+ pFormData = new FmFormData(xForm, pParentData);
+ Insert( pFormData, nRelPos );
+ }
+
+
+ void NavigatorTreeModel::InsertFormComponent(const Reference< XFormComponent > & xComp, sal_uInt32 nRelPos)
+ {
+
+ // set ParentData
+ Reference< XInterface > xIFace( xComp->getParent());
+ Reference< XForm > xForm(xIFace, UNO_QUERY);
+ if (!xForm.is())
+ return;
+
+ FmFormData* pParentData = static_cast<FmFormData*>(FindData( xForm, GetRootList() ));
+ if( !pParentData )
+ {
+ pParentData = new FmFormData(xForm, nullptr);
+ Insert( pParentData );
+ }
+
+ if (!FindData(xComp, pParentData->GetChildList(),false))
+ {
+
+ // set new EntryData
+ FmEntryData* pNewEntryData = new FmControlData(xComp, pParentData);
+
+
+ // insert new EntryData
+ Insert( pNewEntryData, nRelPos );
+ }
+ }
+
+ void NavigatorTreeModel::ReplaceFormComponent(
+ const Reference< XFormComponent > & xOld,
+ const Reference< XFormComponent > & xNew
+ )
+ {
+ FmEntryData* pData = FindData(xOld, GetRootList());
+ assert(dynamic_cast<const FmControlData*>( pData)); //NavigatorTreeModel::ReplaceFormComponent : invalid argument
+ auto pControlData = dynamic_cast<FmControlData*>( pData);
+ if (!pControlData)
+ return;
+ pControlData->ModelReplaced(xNew);
+
+ FmNavModelReplacedHint aReplacedHint( pData );
+ Broadcast( aReplacedHint );
+ }
+
+ FmEntryData* NavigatorTreeModel::FindData(const Reference< XInterface > & xElement, FmEntryDataList* pDataList, bool bRecurs)
+ {
+ // normalize
+ Reference< XInterface > xIFace( xElement, UNO_QUERY );
+
+ for ( size_t i = 0; i < pDataList->size(); i++ )
+ {
+ FmEntryData* pEntryData = pDataList->at( i );
+ if ( pEntryData->GetElement().get() == xIFace.get() )
+ return pEntryData;
+ else if (bRecurs)
+ {
+ pEntryData = FindData( xElement, pEntryData->GetChildList() );
+ if (pEntryData)
+ return pEntryData;
+ }
+ }
+ return nullptr;
+ }
+
+
+ FmEntryData* NavigatorTreeModel::FindData( const OUString& rText, FmFormData const * pParentData, bool bRecurs )
+ {
+ FmEntryDataList* pDataList;
+ if( !pParentData )
+ pDataList = GetRootList();
+ else
+ pDataList = pParentData->GetChildList();
+
+ OUString aEntryText;
+ FmEntryData* pEntryData;
+ FmEntryData* pChildData;
+
+ for( size_t i = 0; i < pDataList->size(); i++ )
+ {
+ pEntryData = pDataList->at( i );
+ aEntryText = pEntryData->GetText();
+
+ if (rText == aEntryText)
+ return pEntryData;
+
+ if (FmFormData* pFormData = bRecurs ? dynamic_cast<FmFormData*>(pEntryData) : nullptr)
+ {
+ pChildData = FindData(rText, pFormData, true);
+ if( pChildData )
+ return pChildData;
+ }
+ }
+
+ return nullptr;
+ }
+
+ void NavigatorTreeModel::Notify( SfxBroadcaster& /*rBC*/, const SfxHint& rHint )
+ {
+ if (rHint.GetId() == SfxHintId::ThisIsAnSdrHint)
+ {
+ const SdrHint* pSdrHint = static_cast<const SdrHint*>(&rHint);
+ switch( pSdrHint->GetKind() )
+ {
+ case SdrHintKind::ObjectInserted:
+ InsertSdrObj(pSdrHint->GetObject());
+ break;
+ case SdrHintKind::ObjectRemoved:
+ RemoveSdrObj(pSdrHint->GetObject());
+ break;
+ default:
+ break;
+ }
+ }
+ // is shell gone?
+ else if (rHint.GetId() == SfxHintId::Dying)
+ {
+ UpdateContent(nullptr);
+ }
+ // changed mark of controls?
+ else if (const FmNavViewMarksChanged* pvmcHint = dynamic_cast<const FmNavViewMarksChanged*>(&rHint))
+ {
+ BroadcastMarkedObjects(pvmcHint->GetAffectedView()->GetMarkedObjectList());
+ }
+ }
+
+ void NavigatorTreeModel::InsertSdrObj( const SdrObject* pObj )
+ {
+ const FmFormObj* pFormObject = FmFormObj::GetFormObject( pObj );
+ if ( pFormObject )
+ {
+ try
+ {
+ Reference< XFormComponent > xFormComponent( pFormObject->GetUnoControlModel(), UNO_QUERY_THROW );
+ Reference< XIndexAccess > xContainer( xFormComponent->getParent(), UNO_QUERY_THROW );
+
+ sal_Int32 nPos = getElementPos( xContainer, xFormComponent );
+ InsertFormComponent( xFormComponent, nPos );
+ }
+ catch( const Exception& )
+ {
+ DBG_UNHANDLED_EXCEPTION("svx");
+ }
+ }
+ else if ( pObj->IsGroupObject() )
+ {
+ SdrObjListIter aIter( pObj->GetSubList() );
+ while ( aIter.IsMore() )
+ InsertSdrObj( aIter.Next() );
+ }
+ }
+
+
+ void NavigatorTreeModel::RemoveSdrObj( const SdrObject* pObj )
+ {
+ const FmFormObj* pFormObject = FmFormObj::GetFormObject( pObj );
+ if ( pFormObject )
+ {
+ try
+ {
+ Reference< XFormComponent > xFormComponent( pFormObject->GetUnoControlModel(), UNO_QUERY_THROW );
+ FmEntryData* pEntryData = FindData( xFormComponent, GetRootList() );
+ if ( pEntryData )
+ Remove( pEntryData );
+ }
+ catch( const Exception& )
+ {
+ DBG_UNHANDLED_EXCEPTION("svx");
+ }
+ }
+ else if ( pObj->IsGroupObject() )
+ {
+ SdrObjListIter aIter( pObj->GetSubList() );
+ while ( aIter.IsMore() )
+ RemoveSdrObj( aIter.Next() );
+ }
+ }
+
+ bool NavigatorTreeModel::InsertFormComponent(FmNavRequestSelectHint& rHint, SdrObject* pObject)
+ {
+ if ( auto pObjGroup = dynamic_cast<const SdrObjGroup*>( pObject) )
+ { // descend recursively
+ const SdrObjList *pChildren = pObjGroup->GetSubList();
+ for ( size_t i=0; i<pChildren->GetObjCount(); ++i )
+ {
+ SdrObject* pCurrent = pChildren->GetObj(i);
+ if (!InsertFormComponent(rHint, pCurrent))
+ return false;
+ }
+ }
+ else
+ {
+ FmFormObj* pFormObject = FmFormObj::GetFormObject( pObject );
+ if ( !pFormObject )
+ return false;
+
+ try
+ {
+ Reference< XFormComponent > xFormViewControl( pFormObject->GetUnoControlModel(), UNO_QUERY_THROW );
+ FmEntryData* pControlData = FindData( xFormViewControl, GetRootList() );
+ if ( !pControlData )
+ return false;
+
+ rHint.AddItem( pControlData );
+ return true;
+ }
+ catch( const Exception& )
+ {
+ DBG_UNHANDLED_EXCEPTION("svx");
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ void NavigatorTreeModel::BroadcastMarkedObjects(const SdrMarkList& mlMarked)
+ {
+ // search all objects, which can be handled, out of marked objects
+ FmNavRequestSelectHint rshRequestSelection;
+ bool bIsMixedSelection = false;
+
+ for (size_t i=0; (i<mlMarked.GetMarkCount()) && !bIsMixedSelection; ++i)
+ {
+ SdrObject* pobjCurrent = mlMarked.GetMark(i)->GetMarkedSdrObj();
+ bIsMixedSelection |= !InsertFormComponent(rshRequestSelection, pobjCurrent);
+ // if Not-Form-Control, InsertFormComponent returns sal_False !
+ }
+
+ rshRequestSelection.SetMixedSelection(bIsMixedSelection);
+ if (bIsMixedSelection)
+ rshRequestSelection.ClearItems();
+
+ Broadcast(rshRequestSelection);
+ // an empty list causes NavigatorTree to remove his selection
+ }
+
+
+ void NavigatorTreeModel::UpdateContent( const Reference< css::form::XForms > & xForms )
+ {
+
+ // refill model form root upward
+ Clear();
+ if (!xForms.is())
+ return;
+
+ xForms->addContainerListener(m_pPropChangeList);
+
+ FillBranch(nullptr);
+
+ // select same control in tree as in view
+ // (or all of them), if there is one ...
+ if(!m_pFormShell) return; // no shell
+
+ FmFormView* pFormView = m_pFormShell->GetFormView();
+ DBG_ASSERT(pFormView != nullptr, "NavigatorTreeModel::UpdateContent : no FormView");
+ BroadcastMarkedObjects(pFormView->GetMarkedObjectList());
+ }
+
+
+ void NavigatorTreeModel::UpdateContent( FmFormShell* pShell )
+ {
+
+ // If shell is unchanged, do nothing
+ FmFormPage* pNewPage = pShell ? pShell->GetCurPage() : nullptr;
+ if ((pShell == m_pFormShell) && (m_pFormPage == pNewPage))
+ return;
+
+
+ // unregister as Listener
+ if( m_pFormShell )
+ {
+ if (m_pFormModel)
+ EndListening( *m_pFormModel );
+ m_pFormModel = nullptr;
+ EndListening( *m_pFormShell );
+ Clear();
+ }
+
+
+ // entire update
+ m_pFormShell = pShell;
+ if (m_pFormShell)
+ {
+ m_pFormPage = pNewPage;
+ UpdateContent(m_pFormPage->GetForms());
+ } else
+ m_pFormPage = nullptr;
+
+
+ // register as Listener again
+ if( m_pFormShell )
+ {
+ StartListening( *m_pFormShell );
+ m_pFormModel = m_pFormShell->GetFormModel();
+ if( m_pFormModel )
+ StartListening( *m_pFormModel );
+ }
+ }
+
+
+ Reference< XIndexContainer > NavigatorTreeModel::GetFormComponents( FmFormData const * pFormData )
+ {
+
+ // get components from form
+ if (pFormData)
+ return Reference< XIndexContainer > (pFormData->GetFormIface(), UNO_QUERY);
+
+ return Reference< XIndexContainer > ();
+ }
+
+
+ bool NavigatorTreeModel::Rename( FmEntryData* pEntryData, const OUString& rNewText )
+ {
+
+ // If name already exist, error message
+ pEntryData->SetText( rNewText );
+
+
+ // get PropertySet
+ Reference< XFormComponent > xFormComponent;
+
+ if( auto pFormData = dynamic_cast<FmFormData*>( pEntryData))
+ {
+ xFormComponent = pFormData->GetFormIface();
+ }
+
+ if( auto pControlData = dynamic_cast<FmControlData*>( pEntryData) )
+ {
+ xFormComponent = pControlData->GetFormComponent();
+ }
+
+ if( !xFormComponent.is() ) return false;
+ Reference< XPropertySet > xSet(xFormComponent, UNO_QUERY);
+ if( !xSet.is() ) return false;
+
+
+ // set name
+ xSet->setPropertyValue( FM_PROP_NAME, Any(rNewText) );
+
+ return true;
+ }
+
+}
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/form/sdbdatacolumn.cxx b/svx/source/form/sdbdatacolumn.cxx
new file mode 100644
index 000000000..19a7fd016
--- /dev/null
+++ b/svx/source/form/sdbdatacolumn.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 <sdbdatacolumn.hxx>
+
+
+namespace svxform
+{
+
+
+ using namespace ::com::sun::star::uno;
+ using namespace ::com::sun::star::lang;
+ using namespace ::com::sun::star::beans;
+ using namespace ::com::sun::star::sdbc;
+ using namespace ::com::sun::star::util;
+ using namespace ::com::sun::star::io;
+ using namespace ::com::sun::star::container;
+
+
+ //= DataColumn - a class wrapping an object implementing a sdb::DataColumn service
+
+ DataColumn::DataColumn(const Reference< css::beans::XPropertySet>& _rxIFace)
+ {
+ m_xPropertySet = _rxIFace;
+ m_xColumn.set(_rxIFace, UNO_QUERY);
+ m_xColumnUpdate.set(_rxIFace, UNO_QUERY);
+
+ if (!m_xPropertySet.is() || !m_xColumn.is())
+ {
+ m_xPropertySet = nullptr;
+ m_xColumn = nullptr;
+ m_xColumnUpdate = nullptr;
+ }
+ }
+
+}
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/form/sqlparserclient.cxx b/svx/source/form/sqlparserclient.cxx
new file mode 100644
index 000000000..232f0418d
--- /dev/null
+++ b/svx/source/form/sqlparserclient.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 <sqlparserclient.hxx>
+
+#include <connectivity/sqlparse.hxx>
+
+using namespace ::dbtools;
+using namespace ::connectivity;
+
+namespace svxform
+{
+ using namespace ::com::sun::star::uno;
+ using namespace ::com::sun::star::lang;
+
+ OSQLParserClient::OSQLParserClient(const Reference< XComponentContext >& rxContext)
+ : m_pParser(std::make_shared<OSQLParser>(rxContext, getParseContext()))
+ {
+ }
+
+ std::unique_ptr< ::connectivity::OSQLParseNode > OSQLParserClient::predicateTree(
+ OUString& _rErrorMessage,
+ const OUString& _rStatement,
+ const css::uno::Reference< css::util::XNumberFormatter >& _rxFormatter,
+ const css::uno::Reference< css::beans::XPropertySet >& _rxField
+ ) const
+ {
+ return m_pParser->predicateTree(_rErrorMessage, _rStatement, _rxFormatter, _rxField);
+ }
+
+}
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/form/tabwin.cxx b/svx/source/form/tabwin.cxx
new file mode 100644
index 000000000..4aa65b56e
--- /dev/null
+++ b/svx/source/form/tabwin.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 <tabwin.hxx>
+#include <fmservs.hxx>
+
+#include <svx/strings.hrc>
+#include <svx/svxids.hrc>
+#include <com/sun/star/sdb/CommandType.hpp>
+#include <com/sun/star/sdbc/XConnection.hpp>
+#include <comphelper/processfactory.hxx>
+#include <comphelper/types.hxx>
+
+#include <helpids.h>
+#include <svx/fmshell.hxx>
+#include <fmshimp.hxx>
+
+#include <fmprop.hxx>
+
+#include <svx/dialmgr.hxx>
+#include <sfx2/bindings.hxx>
+#include <sfx2/objitem.hxx>
+#include <sfx2/frame.hxx>
+#include <svx/dataaccessdescriptor.hxx>
+#include <tools/diagnose_ex.h>
+#include <tabwin.hrc>
+
+const tools::Long STD_WIN_SIZE_X = 120;
+const tools::Long STD_WIN_SIZE_Y = 150;
+
+using namespace ::com::sun::star::sdbc;
+using namespace ::com::sun::star::sdb;
+using namespace ::com::sun::star::uno;
+using namespace ::com::sun::star::datatransfer;
+using namespace ::com::sun::star::beans;
+using namespace ::com::sun::star::lang;
+using namespace ::com::sun::star::form;
+using namespace ::com::sun::star::container;
+using namespace ::com::sun::star;
+using namespace ::svxform;
+using namespace ::svx;
+using namespace ::dbtools;
+
+struct ColumnInfo
+{
+ OUString sColumnName;
+ explicit ColumnInfo(const OUString& i_sColumnName)
+ : sColumnName(i_sColumnName)
+ {
+ }
+};
+
+void FmFieldWin::addToList(const uno::Reference< container::XNameAccess>& i_xColumns )
+{
+ const uno::Sequence< OUString > aEntries = i_xColumns->getElementNames();
+ for ( const OUString& rEntry : aEntries )
+ {
+ uno::Reference< beans::XPropertySet> xColumn(i_xColumns->getByName(rEntry),UNO_QUERY_THROW);
+ OUString sLabel;
+ if ( xColumn->getPropertySetInfo()->hasPropertyByName(FM_PROP_LABEL) )
+ xColumn->getPropertyValue(FM_PROP_LABEL) >>= sLabel;
+ m_aListBoxData.emplace_back(new ColumnInfo(rEntry));
+ OUString sId(weld::toId(m_aListBoxData.back().get()));
+ if ( !sLabel.isEmpty() )
+ m_xListBox->append(sId, sLabel);
+ else
+ m_xListBox->append(sId, rEntry);
+ }
+}
+
+IMPL_LINK(FmFieldWin, DragBeginHdl, bool&, rUnsetDragIcon, bool)
+{
+ rUnsetDragIcon = false;
+
+ ColumnInfo* pSelected = weld::fromId<ColumnInfo*>(m_xListBox->get_selected_id());
+ if (!pSelected)
+ {
+ // no drag without a field
+ return true;
+ }
+
+ svx::ODataAccessDescriptor aDescriptor;
+ aDescriptor[ DataAccessDescriptorProperty::DataSource ] <<= GetDatabaseName();
+ aDescriptor[ DataAccessDescriptorProperty::Connection ] <<= GetConnection().getTyped();
+ aDescriptor[ DataAccessDescriptorProperty::Command ] <<= GetObjectName();
+ aDescriptor[ DataAccessDescriptorProperty::CommandType ]<<= GetObjectType();
+ aDescriptor[ DataAccessDescriptorProperty::ColumnName ] <<= pSelected->sColumnName;
+
+ m_xHelper->setDescriptor(aDescriptor);
+
+ return false;
+}
+
+FmFieldWin::FmFieldWin(SfxBindings* _pBindings, SfxChildWindow* _pMgr, weld::Window* _pParent)
+ : SfxModelessDialogController(_pBindings, _pMgr, _pParent, "svx/ui/formfielddialog.ui", "FormFieldDialog")
+ , SfxControllerItem(SID_FM_FIELDS_CONTROL, *_pBindings)
+ , comphelper::OPropertyChangeListener(m_aMutex)
+ , m_xListBox(m_xBuilder->weld_tree_view("treeview"))
+ , m_nObjectType(0)
+{
+ m_xDialog->set_help_id(HID_FIELD_SEL_WIN);
+ m_xListBox->set_help_id(HID_FIELD_SEL);
+
+ m_xListBox->connect_row_activated(LINK(this, FmFieldWin, RowActivatedHdl));
+ m_xHelper.set(new OColumnTransferable(
+ ColumnTransferFormatFlags::FIELD_DESCRIPTOR | ColumnTransferFormatFlags::CONTROL_EXCHANGE | ColumnTransferFormatFlags::COLUMN_DESCRIPTOR
+ ));
+ rtl::Reference<TransferDataContainer> xHelper(m_xHelper);
+ m_xListBox->enable_drag_source(xHelper, DND_ACTION_COPY);
+ m_xListBox->connect_drag_begin(LINK(this, FmFieldWin, DragBeginHdl));
+
+ UpdateContent(nullptr);
+ m_xDialog->set_size_request(STD_WIN_SIZE_X, STD_WIN_SIZE_Y);
+}
+
+FmFieldWin::~FmFieldWin()
+{
+ if (m_xChangeListener.is())
+ {
+ m_xChangeListener->dispose();
+ m_xChangeListener.clear();
+ }
+ ::SfxControllerItem::dispose();
+}
+
+IMPL_LINK_NOARG(FmFieldWin, RowActivatedHdl, weld::TreeView&, bool)
+{
+ return createSelectionControls();
+}
+
+bool FmFieldWin::createSelectionControls()
+{
+ ColumnInfo* pSelected = weld::fromId<ColumnInfo*>(m_xListBox->get_selected_id());
+ if (pSelected)
+ {
+ // build a descriptor for the currently selected field
+ ODataAccessDescriptor aDescr;
+ aDescr.setDataSource(GetDatabaseName());
+
+ aDescr[ DataAccessDescriptorProperty::Connection ] <<= GetConnection().getTyped();
+
+ aDescr[ DataAccessDescriptorProperty::Command ] <<= GetObjectName();
+ aDescr[ DataAccessDescriptorProperty::CommandType ] <<= GetObjectType();
+ aDescr[ DataAccessDescriptorProperty::ColumnName ] <<= pSelected->sColumnName;
+
+ // transfer this to the SFX world
+ SfxUnoAnyItem aDescriptorItem( SID_FM_DATACCESS_DESCRIPTOR, Any( aDescr.createPropertyValueSequence() ) );
+ const SfxPoolItem* pArgs[] =
+ {
+ &aDescriptorItem, nullptr
+ };
+
+ // execute the create slot
+ GetBindings().Execute( SID_FM_CREATE_FIELDCONTROL, pArgs );
+ }
+
+ return nullptr != pSelected;
+}
+
+void FmFieldWin::_propertyChanged(const css::beans::PropertyChangeEvent& evt)
+{
+ css::uno::Reference< css::form::XForm > xForm(evt.Source, css::uno::UNO_QUERY);
+ UpdateContent(xForm);
+}
+
+void FmFieldWin::StateChangedAtToolBoxControl(sal_uInt16 nSID, SfxItemState eState, const SfxPoolItem* pState)
+{
+ if (!pState || SID_FM_FIELDS_CONTROL != nSID)
+ return;
+
+ if (eState >= SfxItemState::DEFAULT)
+ {
+ FmFormShell* pShell = dynamic_cast<FmFormShell*>( static_cast<const SfxObjectItem*>(pState)->GetShell() );
+ UpdateContent(pShell);
+ }
+ else
+ UpdateContent(nullptr);
+}
+
+void FmFieldWin::UpdateContent(FmFormShell const * pShell)
+{
+ m_xListBox->clear();
+ m_aListBoxData.clear();
+ OUString aTitle(SvxResId(RID_STR_FIELDSELECTION));
+ m_xDialog->set_title(aTitle);
+
+ if (!pShell || !pShell->GetImpl())
+ return;
+
+ Reference<XForm> const xForm = pShell->GetImpl()->getCurrentForm_Lock();
+ if ( xForm.is() )
+ UpdateContent( xForm );
+}
+
+void FmFieldWin::UpdateContent(const css::uno::Reference< css::form::XForm > & xForm)
+{
+ try
+ {
+ // delete ListBox
+ m_xListBox->clear();
+ m_aListBoxData.clear();
+ OUString aTitle(SvxResId(RID_STR_FIELDSELECTION));
+ m_xDialog->set_title(aTitle);
+
+ if (!xForm.is())
+ return;
+
+ Reference< XPropertySet > xSet(xForm, UNO_QUERY);
+
+ m_aObjectName = ::comphelper::getString(xSet->getPropertyValue(FM_PROP_COMMAND));
+ m_aDatabaseName = ::comphelper::getString(xSet->getPropertyValue(FM_PROP_DATASOURCE));
+ m_nObjectType = ::comphelper::getINT32(xSet->getPropertyValue(FM_PROP_COMMANDTYPE));
+
+ // get the connection of the form
+ m_aConnection.reset(
+ connectRowset( Reference< XRowSet >( xForm, UNO_QUERY ), ::comphelper::getProcessComponentContext(), nullptr ),
+ SharedConnection::NoTakeOwnership
+ );
+ // TODO: When incompatible changes (such as extending the "virtualdbtools" interface by ensureRowSetConnection)
+ // are allowed, again, we should change this: dbtools should consistently use SharedConnection all over
+ // the place, and connectRowset should be replaced with ensureRowSetConnection
+
+ // get the fields of the object
+
+ if ( m_aConnection.is() && !m_aObjectName.isEmpty() )
+ {
+ Reference< XComponent > xKeepFieldsAlive;
+ Reference< XNameAccess > xColumns = getFieldsByCommandDescriptor( m_aConnection, m_nObjectType, m_aObjectName,xKeepFieldsAlive );
+ if ( xColumns.is() )
+ addToList(xColumns);
+ }
+
+ // set prefix
+ OUString aPrefix;
+
+ switch (m_nObjectType)
+ {
+ case CommandType::TABLE:
+ aPrefix = SvxResId(RID_RSC_TABWIN_PREFIX[0]);
+ break;
+ case CommandType::QUERY:
+ aPrefix = SvxResId(RID_RSC_TABWIN_PREFIX[1]);
+ break;
+ default:
+ aPrefix = SvxResId(RID_RSC_TABWIN_PREFIX[2]);
+ break;
+ }
+
+ // listen for changes at ControlSource in PropertySet
+ if (m_xChangeListener.is())
+ {
+ m_xChangeListener->dispose();
+ m_xChangeListener.clear();
+ }
+ m_xChangeListener = new ::comphelper::OPropertyChangeMultiplexer(this, xSet);
+ m_xChangeListener->addProperty(FM_PROP_DATASOURCE);
+ m_xChangeListener->addProperty(FM_PROP_COMMAND);
+ m_xChangeListener->addProperty(FM_PROP_COMMANDTYPE);
+
+ // set title
+ aTitle += " " + aPrefix + " " + m_aObjectName;
+ m_xDialog->set_title(aTitle);
+ }
+ catch( const Exception& )
+ {
+ TOOLS_WARN_EXCEPTION( "svx", "FmTabWin::UpdateContent" );
+ }
+}
+
+void FmFieldWin::FillInfo( SfxChildWinInfo& rInfo ) const
+{
+ rInfo.bVisible = false;
+}
+
+SFX_IMPL_MODELESSDIALOGCONTOLLER(FmFieldWinMgr, SID_FM_ADD_FIELD)
+
+FmFieldWinMgr::FmFieldWinMgr(vcl::Window* _pParent, sal_uInt16 _nId,
+ SfxBindings* _pBindings, SfxChildWinInfo const * _pInfo)
+ :SfxChildWindow(_pParent, _nId)
+{
+ auto xDlg = std::make_shared<FmFieldWin>(_pBindings, this, _pParent->GetFrameWeld());
+ SetController(xDlg);
+ SetHideNotDelete(true);
+ xDlg->Initialize(_pInfo);
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/form/tbxform.cxx b/svx/source/form/tbxform.cxx
new file mode 100644
index 000000000..05576133d
--- /dev/null
+++ b/svx/source/form/tbxform.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 <sal/config.h>
+
+#include <comphelper/propertyvalue.hxx>
+#include <svl/intitem.hxx>
+#include <svl/eitem.hxx>
+#include <svl/stritem.hxx>
+#include <vcl/toolbox.hxx>
+#include <vcl/settings.hxx>
+
+#include <svx/dialmgr.hxx>
+#include <svx/labelitemwindow.hxx>
+#include <svx/svxids.hrc>
+#include <svx/strings.hrc>
+#include <tbxform.hxx>
+
+using namespace ::com::sun::star::uno;
+using namespace ::com::sun::star::beans;
+
+SvxFmAbsRecWin::SvxFmAbsRecWin(vcl::Window* pParent, SfxToolBoxControl* pController)
+ : RecordItemWindow(pParent)
+ , m_pController(pController)
+{
+ m_xWidget->set_width_chars(6);
+ SetSizePixel(m_xWidget->get_preferred_size());
+}
+
+void SvxFmAbsRecWin::PositionFired(sal_Int64 nRecord)
+{
+ SfxInt32Item aPositionParam( FN_PARAM_1, static_cast<sal_Int32>(nRecord) );
+
+ Any a;
+ aPositionParam.QueryValue( a );
+ Sequence< PropertyValue > aArgs{ comphelper::makePropertyValue("Position", a) };
+ m_pController->Dispatch( ".uno:AbsoluteRecord",
+ aArgs );
+ m_pController->updateStatus();
+}
+
+SFX_IMPL_TOOLBOX_CONTROL( SvxFmTbxCtlAbsRec, SfxInt32Item );
+
+SvxFmTbxCtlAbsRec::SvxFmTbxCtlAbsRec( sal_uInt16 nSlotId, ToolBoxItemId nId, ToolBox& rTbx )
+ :SfxToolBoxControl( nSlotId, nId, rTbx )
+{
+}
+
+
+SvxFmTbxCtlAbsRec::~SvxFmTbxCtlAbsRec()
+{
+}
+
+void SvxFmTbxCtlAbsRec::StateChangedAtToolBoxControl( sal_uInt16 nSID, SfxItemState eState, const SfxPoolItem* pState )
+{
+ ToolBoxItemId nId = GetId();
+ ToolBox* pToolBox = &GetToolBox();
+ SvxFmAbsRecWin* pWin = static_cast<SvxFmAbsRecWin*>( pToolBox->GetItemWindow(nId) );
+
+ assert(pWin && "Control not found!");
+
+ if (pState)
+ {
+ const SfxInt32Item* pItem = dynamic_cast< const SfxInt32Item* >( pState );
+ DBG_ASSERT( pItem, "SvxFmTbxCtlAbsRec::StateChanged: invalid item!" );
+ pWin->set_text(OUString::number(pItem ? pItem->GetValue() : -1));
+ }
+
+ bool bEnable = SfxItemState::DISABLED != eState && pState;
+ if (!bEnable)
+ pWin->set_text(OUString());
+
+
+ // enabling/disabling of the window
+ pToolBox->EnableItem(nId, bEnable);
+ SfxToolBoxControl::StateChangedAtToolBoxControl( nSID, eState,pState );
+}
+
+VclPtr<InterimItemWindow> SvxFmTbxCtlAbsRec::CreateItemWindow( vcl::Window* pParent )
+{
+ return VclPtrInstance<SvxFmAbsRecWin>(pParent, this);
+}
+
+SFX_IMPL_TOOLBOX_CONTROL( SvxFmTbxCtlRecText, SfxBoolItem );
+
+SvxFmTbxCtlRecText::SvxFmTbxCtlRecText( sal_uInt16 nSlotId, ToolBoxItemId nId, ToolBox& rTbx )
+ :SfxToolBoxControl( nSlotId, nId, rTbx )
+{
+ rTbx.SetItemWindowNonInteractive(nId, true);
+}
+
+SvxFmTbxCtlRecText::~SvxFmTbxCtlRecText()
+{
+}
+
+VclPtr<InterimItemWindow> SvxFmTbxCtlRecText::CreateItemWindow( vcl::Window* pParent )
+{
+ OUString aText(SvxResId(RID_STR_REC_TEXT));
+ VclPtrInstance<LabelItemWindow> xFixedText(pParent, aText);
+
+ xFixedText->Show();
+
+ return xFixedText;
+}
+
+SFX_IMPL_TOOLBOX_CONTROL( SvxFmTbxCtlRecFromText, SfxBoolItem );
+
+SvxFmTbxCtlRecFromText::SvxFmTbxCtlRecFromText( sal_uInt16 nSlotId, ToolBoxItemId nId, ToolBox& rTbx )
+ :SfxToolBoxControl( nSlotId, nId, rTbx )
+{
+ rTbx.SetItemWindowNonInteractive(nId, true);
+}
+
+SvxFmTbxCtlRecFromText::~SvxFmTbxCtlRecFromText()
+{
+}
+
+VclPtr<InterimItemWindow> SvxFmTbxCtlRecFromText::CreateItemWindow( vcl::Window* pParent )
+{
+ OUString aText(SvxResId(RID_STR_REC_FROM_TEXT));
+ VclPtrInstance<LabelItemWindow> xFixedText(pParent, aText);
+
+ xFixedText->Show();
+
+ return xFixedText;
+}
+
+SFX_IMPL_TOOLBOX_CONTROL( SvxFmTbxCtlRecTotal, SfxStringItem );
+
+SvxFmTbxCtlRecTotal::SvxFmTbxCtlRecTotal( sal_uInt16 nSlotId, ToolBoxItemId nId, ToolBox& rTbx )
+ : SfxToolBoxControl( nSlotId, nId, rTbx )
+{
+ rTbx.SetItemWindowNonInteractive(nId, true);
+}
+
+SvxFmTbxCtlRecTotal::~SvxFmTbxCtlRecTotal()
+{
+}
+
+VclPtr<InterimItemWindow> SvxFmTbxCtlRecTotal::CreateItemWindow( vcl::Window* pParent )
+{
+ m_xFixedText.reset(VclPtr<LabelItemWindow>::Create(pParent, "123456"));
+ m_xFixedText->set_label("");
+
+ m_xFixedText->Show();
+
+ return m_xFixedText;
+}
+
+void SvxFmTbxCtlRecTotal::StateChangedAtToolBoxControl( sal_uInt16 nSID, SfxItemState eState, const SfxPoolItem* pState )
+{
+ // setting the FixedText
+ if (GetSlotId() != SID_FM_RECORD_TOTAL)
+ return;
+
+ OUString aText;
+ if (pState)
+ aText = static_cast<const SfxStringItem*>(pState)->GetValue();
+ else
+ aText = "?";
+
+ m_xFixedText->set_label(aText);
+
+ SfxToolBoxControl::StateChangedAtToolBoxControl( nSID, eState,pState );
+}
+
+SFX_IMPL_TOOLBOX_CONTROL( SvxFmTbxNextRec, SfxBoolItem );
+
+
+SvxFmTbxNextRec::SvxFmTbxNextRec( sal_uInt16 nSlotId, ToolBoxItemId nId, ToolBox& rTbx )
+ :SfxToolBoxControl( nSlotId, nId, rTbx )
+{
+ rTbx.SetItemBits(nId, rTbx.GetItemBits(nId) | ToolBoxItemBits::REPEAT);
+
+ AllSettings aSettings = rTbx.GetSettings();
+ MouseSettings aMouseSettings = aSettings.GetMouseSettings();
+ aMouseSettings.SetButtonRepeat(aMouseSettings.GetButtonRepeat() / 4);
+ aSettings.SetMouseSettings(aMouseSettings);
+ rTbx.SetSettings(aSettings, true);
+}
+
+SFX_IMPL_TOOLBOX_CONTROL( SvxFmTbxPrevRec, SfxBoolItem );
+
+
+SvxFmTbxPrevRec::SvxFmTbxPrevRec( sal_uInt16 nSlotId, ToolBoxItemId nId, ToolBox& rTbx )
+ :SfxToolBoxControl( nSlotId, nId, rTbx )
+{
+ rTbx.SetItemBits(nId, rTbx.GetItemBits(nId) | ToolBoxItemBits::REPEAT);
+}
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/form/typemap.cxx b/svx/source/form/typemap.cxx
new file mode 100644
index 000000000..b169b745c
--- /dev/null
+++ b/svx/source/form/typemap.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 <config_options.h>
+
+#include <sfx2/objitem.hxx>
+#include <sfx2/msg.hxx>
+#include <svl/memberid.h>
+#include <editeng/wghtitem.hxx>
+#include <editeng/udlnitem.hxx>
+#include <editeng/crossedoutitem.hxx>
+#include <editeng/contouritem.hxx>
+#include <editeng/shdditem.hxx>
+#include <editeng/postitem.hxx>
+#include <svx/clipfmtitem.hxx>
+#include <editeng/fhgtitem.hxx>
+#include <editeng/fontitem.hxx>
+#include <editeng/charreliefitem.hxx>
+#include <editeng/escapementitem.hxx>
+#include <editeng/colritem.hxx>
+#include <editeng/wrlmitem.hxx>
+#include <editeng/charscaleitem.hxx>
+#include <editeng/autokernitem.hxx>
+#include <editeng/ulspitem.hxx>
+#include <editeng/lrspitem.hxx>
+#include <editeng/lspcitem.hxx>
+#include <editeng/langitem.hxx>
+#include <editeng/kernitem.hxx>
+#include <editeng/adjustitem.hxx>
+#include <editeng/emphasismarkitem.hxx>
+
+#include <editeng/memberids.h>
+#define SFX_TYPEMAP
+#include <svxslots.hxx>
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/form/xfm_addcondition.cxx b/svx/source/form/xfm_addcondition.cxx
new file mode 100644
index 000000000..6c8d6ef35
--- /dev/null
+++ b/svx/source/form/xfm_addcondition.cxx
@@ -0,0 +1,157 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+
+#include <xfm_addcondition.hxx>
+
+#include <com/sun/star/beans/PropertyAttribute.hpp>
+#include <comphelper/processfactory.hxx>
+#include <vcl/svapp.hxx>
+#include <datanavi.hxx>
+#include <fmservs.hxx>
+
+namespace svxform
+{
+
+#define PROPERTY_ID_BINDING 5724
+#define PROPERTY_ID_FORM_MODEL 5725
+#define PROPERTY_ID_FACET_NAME 5726
+#define PROPERTY_ID_CONDITION_VALUE 5727
+
+ using namespace ::com::sun::star::uno;
+ using namespace ::com::sun::star::lang;
+ using namespace ::com::sun::star::beans;
+ using namespace ::com::sun::star::xforms;
+
+
+ //= OAddConditionDialog
+
+
+ Reference< XInterface > OAddConditionDialog_Create( const Reference< XMultiServiceFactory > & _rxORB )
+ {
+ return OAddConditionDialog::Create( _rxORB );
+ }
+
+
+ Sequence< OUString > OAddConditionDialog_GetSupportedServiceNames()
+ {
+ return { "com.sun.star.xforms.ui.dialogs.AddCondition" };
+ }
+
+
+ OUString OAddConditionDialog_GetImplementationName()
+ {
+ return "org.openoffice.comp.svx.OAddConditionDialog";
+ }
+
+ OAddConditionDialog::OAddConditionDialog( const Reference< XComponentContext >& _rxORB )
+ :OAddConditionDialogBase( _rxORB )
+ {
+ registerProperty(
+ "Binding",
+ PROPERTY_ID_BINDING,
+ PropertyAttribute::TRANSIENT,
+ &m_xBinding,
+ cppu::UnoType<decltype(m_xBinding)>::get()
+ );
+
+ registerProperty(
+ "FacetName",
+ PROPERTY_ID_FACET_NAME,
+ PropertyAttribute::TRANSIENT,
+ &m_sFacetName,
+ cppu::UnoType<decltype(m_sFacetName)>::get()
+ );
+
+ registerProperty(
+ "ConditionValue",
+ PROPERTY_ID_CONDITION_VALUE,
+ PropertyAttribute::TRANSIENT,
+ &m_sConditionValue,
+ cppu::UnoType<decltype(m_sConditionValue)>::get()
+ );
+
+ registerProperty(
+ "FormModel",
+ PROPERTY_ID_FORM_MODEL,
+ PropertyAttribute::TRANSIENT,
+ &m_xWorkModel,
+ cppu::UnoType<decltype(m_xWorkModel)>::get()
+ );
+ }
+
+
+ Sequence<sal_Int8> SAL_CALL OAddConditionDialog::getImplementationId( )
+ {
+ return css::uno::Sequence<sal_Int8>();
+ }
+
+
+ Reference< XInterface > OAddConditionDialog::Create( const Reference< XMultiServiceFactory >& _rxFactory )
+ {
+ return *( new OAddConditionDialog( comphelper::getComponentContext(_rxFactory) ) );
+ }
+
+
+ OUString SAL_CALL OAddConditionDialog::getImplementationName()
+ {
+ return OAddConditionDialog_GetImplementationName();
+ }
+
+
+ Sequence< OUString > SAL_CALL OAddConditionDialog::getSupportedServiceNames()
+ {
+ return OAddConditionDialog_GetSupportedServiceNames();
+ }
+
+
+ Reference<XPropertySetInfo> SAL_CALL OAddConditionDialog::getPropertySetInfo()
+ {
+ return createPropertySetInfo( getInfoHelper() );
+ }
+
+ ::cppu::IPropertyArrayHelper& OAddConditionDialog::getInfoHelper()
+ {
+ return *getArrayHelper();
+ }
+
+ ::cppu::IPropertyArrayHelper* OAddConditionDialog::createArrayHelper( ) const
+ {
+ Sequence< Property > aProperties;
+ describeProperties( aProperties );
+ return new ::cppu::OPropertyArrayHelper( aProperties );
+ }
+
+ std::unique_ptr<weld::DialogController> OAddConditionDialog::createDialog(const css::uno::Reference<css::awt::XWindow>& rParent)
+ {
+ if ( !m_xBinding.is() || m_sFacetName.isEmpty() )
+ throw RuntimeException( OUString(), *this );
+
+ return std::make_unique<AddConditionDialog>(Application::GetFrameWeld(rParent), m_sFacetName, m_xBinding);
+ }
+
+ void OAddConditionDialog::executedDialog( sal_Int16 _nExecutionResult )
+ {
+ OAddConditionDialogBase::executedDialog( _nExecutionResult );
+ if ( _nExecutionResult == RET_OK )
+ m_sConditionValue = static_cast<AddConditionDialog*>(m_xDialog.get())->GetCondition();
+ }
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/gallery2/GalleryControl.cxx b/svx/source/gallery2/GalleryControl.cxx
new file mode 100644
index 000000000..d7b2a9693
--- /dev/null
+++ b/svx/source/gallery2/GalleryControl.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 <GalleryControl.hxx>
+
+#include <svx/gallery1.hxx>
+#include "galbrws1.hxx"
+#include <galbrws2.hxx>
+
+namespace svx::sidebar {
+
+GalleryControl::GalleryControl(weld::Widget* pParent)
+ : PanelLayout(pParent, "GalleryPanel", "svx/ui/sidebargallery.ui")
+ , mpGallery(Gallery::GetGalleryInstance())
+ , mxBrowser1(new GalleryBrowser1(
+ *m_xBuilder,
+ mpGallery,
+ [this] ()
+ { return mxBrowser2->SelectTheme(mxBrowser1->GetSelectedTheme()); }))
+ , mxBrowser2(new GalleryBrowser2(*m_xBuilder, mpGallery))
+{
+ mxBrowser1->SelectTheme(0);
+}
+
+GalleryControl::~GalleryControl()
+{
+}
+
+} // end of namespace svx::sidebar
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/gallery2/codec.cxx b/svx/source/gallery2/codec.cxx
new file mode 100644
index 000000000..062c60dbe
--- /dev/null
+++ b/svx/source/gallery2/codec.cxx
@@ -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 .
+ */
+
+
+#include <tools/stream.hxx>
+#include <tools/zcodec.hxx>
+#include "codec.hxx"
+#include <memory>
+
+
+GalleryCodec::GalleryCodec( SvStream& rIOStm ) :
+ rStm( rIOStm )
+{
+}
+
+bool GalleryCodec::IsCoded( SvStream& rStm, sal_uInt32& rVersion )
+{
+ const sal_uInt64 nPos = rStm.Tell();
+ bool bRet;
+ sal_uInt8 cByte1, cByte2, cByte3, cByte4, cByte5, cByte6;
+
+ rStm.ReadUChar( cByte1 ).ReadUChar( cByte2 ).ReadUChar( cByte3 ).ReadUChar( cByte4 ).ReadUChar( cByte5 ).ReadUChar( cByte6 );
+
+ if ( cByte1 == 'S' && cByte2 == 'V' && cByte3 == 'R' && cByte4 == 'L' && cByte5 == 'E' && ( cByte6 == '1' || cByte6 == '2' ) )
+ {
+ rVersion = ( ( cByte6 == '1' ) ? 1 : 2 );
+ bRet = true;
+ }
+ else
+ {
+ rVersion = 0;
+ bRet = false;
+ }
+
+ rStm.Seek( nPos );
+
+ return bRet;
+}
+
+void GalleryCodec::Write( SvStream& rStmToWrite )
+{
+ sal_uInt32 nPos, nCompSize;
+
+ const sal_uInt32 nSize = rStmToWrite.TellEnd();
+ rStmToWrite.Seek( 0 );
+
+ rStm.WriteChar( 'S' ).WriteChar( 'V' ).WriteChar( 'R' ).WriteChar( 'L' ).WriteChar( 'E' ).WriteChar( '2' );
+ rStm.WriteUInt32( nSize );
+
+ nPos = rStm.Tell();
+ rStm.SeekRel( 4 );
+
+ ZCodec aCodec;
+ aCodec.BeginCompression();
+ aCodec.Compress( rStmToWrite, rStm );
+ aCodec.EndCompression();
+
+ nCompSize = rStm.Tell() - nPos - 4;
+ rStm.Seek( nPos );
+ rStm.WriteUInt32( nCompSize );
+ rStm.Seek( STREAM_SEEK_TO_END );
+}
+
+void GalleryCodec::Read( SvStream& rStmToRead )
+{
+ sal_uInt32 nVersion = 0;
+
+ if( !IsCoded( rStm, nVersion ) )
+ return;
+
+ sal_uInt32 nCompressedSize, nUnCompressedSize;
+
+ rStm.SeekRel( 6 );
+ rStm.ReadUInt32( nUnCompressedSize ).ReadUInt32( nCompressedSize );
+
+ // decompress
+ if( 1 == nVersion )
+ {
+ std::unique_ptr<sal_uInt8[]> pCompressedBuffer(new sal_uInt8[ nCompressedSize ]);
+ rStm.ReadBytes(pCompressedBuffer.get(), nCompressedSize);
+ sal_uInt8* pInBuf = pCompressedBuffer.get();
+ std::unique_ptr<sal_uInt8[]> pOutBuf(new sal_uInt8[ nUnCompressedSize ]);
+ sal_uInt8* pTmpBuf = pOutBuf.get();
+ sal_uInt8* pLast = pOutBuf.get() + nUnCompressedSize - 1;
+ sal_uIntPtr nIndex = 0, nCountByte, nRunByte;
+ bool bEndDecoding = false;
+
+ do
+ {
+ nCountByte = *pInBuf++;
+
+ if ( !nCountByte )
+ {
+ nRunByte = *pInBuf++;
+
+ if ( nRunByte > 2 )
+ {
+ // filling absolutely
+ memcpy( &pTmpBuf[ nIndex ], pInBuf, nRunByte );
+ pInBuf += nRunByte;
+ nIndex += nRunByte;
+
+ // note WORD alignment
+ if ( nRunByte & 1 )
+ pInBuf++;
+ }
+ else if ( nRunByte == 1 ) // End of the image
+ bEndDecoding = true;
+ }
+ else
+ {
+ const sal_uInt8 cVal = *pInBuf++;
+
+ memset( &pTmpBuf[ nIndex ], cVal, nCountByte );
+ nIndex += nCountByte;
+ }
+ }
+ while ( !bEndDecoding && ( pTmpBuf <= pLast ) );
+
+ rStmToRead.WriteBytes(pOutBuf.get(), nUnCompressedSize);
+ }
+ else if( 2 == nVersion )
+ {
+ ZCodec aCodec;
+
+ aCodec.BeginCompression();
+ aCodec.Decompress( rStm, rStmToRead );
+ aCodec.EndCompression();
+ }
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/gallery2/codec.hxx b/svx/source/gallery2/codec.hxx
new file mode 100644
index 000000000..726b80157
--- /dev/null
+++ b/svx/source/gallery2/codec.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 .
+ */
+
+#pragma once
+
+#include <sal/types.h>
+
+class SvStream;
+
+class GalleryCodec
+{
+private:
+ SvStream& rStm;
+
+public:
+ explicit GalleryCodec(SvStream& rIOStm);
+
+ void Write(SvStream& rStmToWrite);
+ void Read(SvStream& rStmToRead);
+
+ static bool IsCoded(SvStream& rStm, sal_uInt32& rVersion);
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/gallery2/galbrws1.cxx b/svx/source/gallery2/galbrws1.cxx
new file mode 100644
index 000000000..e33e16974
--- /dev/null
+++ b/svx/source/gallery2/galbrws1.cxx
@@ -0,0 +1,485 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <sal/config.h>
+
+#include <tools/datetime.hxx>
+#include <vcl/commandevent.hxx>
+#include <vcl/event.hxx>
+#include <vcl/svapp.hxx>
+#include <vcl/weld.hxx>
+#include <com/sun/star/ucb/ContentCreationException.hpp>
+#include <sfx2/app.hxx>
+#include <helpids.h>
+#include <svx/gallery1.hxx>
+#include <svx/galtheme.hxx>
+#include <svx/galmisc.hxx>
+#include "galbrws1.hxx"
+#include <svx/strings.hrc>
+#include <algorithm>
+#include <svx/dialmgr.hxx>
+#include <comphelper/dispatchcommand.hxx>
+#include <comphelper/propertyvalue.hxx>
+#include <com/sun/star/beans/PropertyValue.hpp>
+#include <svx/svxdlg.hxx>
+#include <memory>
+#include <bitmaps.hlst>
+
+using namespace ::com::sun::star;
+
+GalleryBrowser1::GalleryBrowser1(
+ weld::Builder& rBuilder,
+ Gallery* pGallery,
+ const std::function<void ()>& rThemeSelectionHandler)
+ :
+ mxNewTheme(rBuilder.weld_button("insert")),
+ mxThemes(rBuilder.weld_tree_view("themelist")),
+ mxMoreGalleries(rBuilder.weld_button("btnMoreGalleries")),
+ mpGallery ( pGallery ),
+ mpExchangeData ( new ExchangeData ),
+ aImgNormal ( RID_SVXBMP_THEME_NORMAL ),
+ aImgDefault ( RID_SVXBMP_THEME_DEFAULT ),
+ aImgReadOnly ( RID_SVXBMP_THEME_READONLY ),
+ maThemeSelectionHandler(rThemeSelectionHandler)
+{
+ mxNewTheme->set_help_id(HID_GALLERY_NEWTHEME);
+ mxNewTheme->connect_clicked( LINK( this, GalleryBrowser1, ClickNewThemeHdl ) );
+
+ mxThemes->make_sorted();
+ mxThemes->set_help_id( HID_GALLERY_THEMELIST );
+ mxThemes->connect_changed( LINK( this, GalleryBrowser1, SelectThemeHdl ) );
+ mxThemes->connect_popup_menu(LINK(this, GalleryBrowser1, PopupMenuHdl));
+ mxThemes->connect_key_press(LINK(this, GalleryBrowser1, KeyInputHdl));
+ mxThemes->set_size_request(-1, mxThemes->get_height_rows(6));
+
+ mxMoreGalleries->set_from_icon_name("cmd/sc_additionsdialog.png");
+ mxMoreGalleries->connect_clicked(LINK(this, GalleryBrowser1, OnMoreGalleriesClick));
+
+ // disable creation of new themes if a writable directory is not available
+ if( mpGallery->GetUserURL().GetProtocol() == INetProtocol::NotValid )
+ mxNewTheme->set_sensitive(false);
+
+ StartListening( *mpGallery );
+
+ for (size_t i = 0, nCount = mpGallery->GetThemeCount(); i < nCount; ++i)
+ ImplInsertThemeEntry( mpGallery->GetThemeInfo( i ) );
+}
+
+GalleryBrowser1::~GalleryBrowser1()
+{
+ EndListening( *mpGallery );
+ mpExchangeData.reset();
+}
+
+void GalleryBrowser1::ImplInsertThemeEntry( const GalleryThemeEntry* pEntry )
+{
+ static const bool bShowHiddenThemes = ( getenv( "GALLERY_SHOW_HIDDEN_THEMES" ) != nullptr );
+
+ if( !(pEntry && ( !pEntry->IsHidden() || bShowHiddenThemes )) )
+ return;
+
+ const OUString* pImage;
+
+ if( pEntry->IsReadOnly() )
+ pImage = &aImgReadOnly;
+ else if( pEntry->IsDefault() )
+ pImage = &aImgDefault;
+ else
+ pImage = &aImgNormal;
+
+ mxThemes->append("", pEntry->GetThemeName(), *pImage);
+}
+
+void GalleryBrowser1::ImplFillExchangeData( const GalleryTheme* pThm, ExchangeData& rData )
+{
+ rData.pTheme = const_cast<GalleryTheme*>(pThm);
+ rData.aEditedTitle = pThm->GetName();
+
+ try
+ {
+ DateTime aDateTime(pThm->getModificationDate());
+
+ rData.aThemeChangeDate = aDateTime;
+ rData.aThemeChangeTime = aDateTime;
+ }
+ catch( const ucb::ContentCreationException& )
+ {
+ }
+ catch( const uno::RuntimeException& )
+ {
+ }
+ catch( const uno::Exception& )
+ {
+ }
+}
+
+void GalleryBrowser1::ImplGetExecuteVector(std::vector<OString>& o_aExec)
+{
+ GalleryTheme* pTheme = mpGallery->AcquireTheme( GetSelectedTheme(), *this );
+
+ if( !pTheme )
+ return;
+
+ bool bUpdateAllowed, bRenameAllowed, bRemoveAllowed;
+ static const bool bIdDialog = ( getenv( "GALLERY_ENABLE_ID_DIALOG" ) != nullptr );
+
+ if( pTheme->IsReadOnly() )
+ bUpdateAllowed = bRenameAllowed = bRemoveAllowed = false;
+ else if( pTheme->IsDefault() )
+ {
+ bUpdateAllowed = bRenameAllowed = true;
+ bRemoveAllowed = false;
+ }
+ else
+ bUpdateAllowed = bRenameAllowed = bRemoveAllowed = true;
+
+ if( bUpdateAllowed && pTheme->GetObjectCount() )
+ o_aExec.emplace_back("update");
+
+ if( bRenameAllowed )
+ o_aExec.emplace_back("rename");
+
+ if( bRemoveAllowed )
+ o_aExec.emplace_back("delete");
+
+ if( bIdDialog && !pTheme->IsReadOnly() )
+ o_aExec.emplace_back("assign");
+
+ o_aExec.emplace_back("properties");
+
+ mpGallery->ReleaseTheme( pTheme, *this );
+}
+
+void GalleryBrowser1::ImplGalleryThemeProperties( std::u16string_view rThemeName, bool bCreateNew )
+{
+ DBG_ASSERT(!mpThemePropsDlgItemSet, "mpThemePropsDlgItemSet already set!");
+ mpThemePropsDlgItemSet.reset(new SfxItemSet( SfxGetpApp()->GetPool() ));
+ GalleryTheme* pTheme = mpGallery->AcquireTheme( rThemeName, *this );
+
+ ImplFillExchangeData( pTheme, *mpExchangeData );
+
+ SvxAbstractDialogFactory* pFact = SvxAbstractDialogFactory::Create();
+ VclPtr<VclAbstractDialog> xThemePropertiesDialog = pFact->CreateGalleryThemePropertiesDialog(mxThemes.get(), mpExchangeData.get(), mpThemePropsDlgItemSet.get());
+
+ if ( bCreateNew )
+ {
+ xThemePropertiesDialog->StartExecuteAsync([xThemePropertiesDialog, this](sal_Int32 nResult){
+ EndNewThemePropertiesDlgHdl(nResult);
+ xThemePropertiesDialog->disposeOnce();
+ });
+ }
+ else
+ {
+ xThemePropertiesDialog->StartExecuteAsync([xThemePropertiesDialog, this](sal_Int32 nResult){
+ EndThemePropertiesDlgHdl(nResult);
+ xThemePropertiesDialog->disposeOnce();
+ });
+ }
+}
+
+void GalleryBrowser1::ImplEndGalleryThemeProperties(bool bCreateNew, sal_Int32 nRet)
+{
+ if( nRet == RET_OK )
+ {
+ OUString aName( mpExchangeData->pTheme->GetName() );
+
+ if( !mpExchangeData->aEditedTitle.isEmpty() && aName != mpExchangeData->aEditedTitle )
+ {
+ OUString aTitle( mpExchangeData->aEditedTitle );
+ sal_uInt16 nCount = 0;
+
+ while( mpGallery->HasTheme( aTitle ) && ( nCount++ < 16000 ) )
+ {
+ aTitle = mpExchangeData->aEditedTitle + " " + OUString::number( nCount );
+ }
+
+ mpGallery->RenameTheme( aName, aTitle );
+ }
+
+ if ( bCreateNew )
+ {
+ mxThemes->select_text( mpExchangeData->pTheme->GetName() );
+ SelectThemeHdl( *mxThemes );
+ }
+ }
+
+ OUString aThemeName( mpExchangeData->pTheme->GetName() );
+ mpGallery->ReleaseTheme( mpExchangeData->pTheme, *this );
+
+ if ( bCreateNew && ( nRet != RET_OK ) )
+ {
+ mpGallery->RemoveTheme( aThemeName );
+ }
+}
+
+void GalleryBrowser1::EndNewThemePropertiesDlgHdl(sal_Int32 nResult)
+{
+ ImplEndGalleryThemeProperties(true, nResult);
+}
+
+void GalleryBrowser1::EndThemePropertiesDlgHdl(sal_Int32 nResult)
+{
+ ImplEndGalleryThemeProperties(false, nResult);
+}
+
+void GalleryBrowser1::ImplExecute(std::string_view rIdent)
+{
+ if (rIdent == "update")
+ {
+ GalleryTheme* pTheme = mpGallery->AcquireTheme( GetSelectedTheme(), *this );
+
+ SvxAbstractDialogFactory* pFact = SvxAbstractDialogFactory::Create();
+ ScopedVclPtr<VclAbstractDialog> aActualizeProgress(pFact->CreateActualizeProgressDialog(mxThemes.get(), pTheme));
+
+ aActualizeProgress->Execute();
+ mpGallery->ReleaseTheme( pTheme, *this );
+ }
+ else if (rIdent == "delete")
+ {
+ std::unique_ptr<weld::Builder> xBuilder(Application::CreateBuilder(mxThemes.get(), "svx/ui/querydeletethemedialog.ui"));
+ std::unique_ptr<weld::MessageDialog> xQuery(xBuilder->weld_message_dialog("QueryDeleteThemeDialog"));
+ if (xQuery->run() == RET_YES)
+ mpGallery->RemoveTheme( mxThemes->get_selected_text() );
+ }
+ else if (rIdent == "rename")
+ {
+ GalleryTheme* pTheme = mpGallery->AcquireTheme( GetSelectedTheme(), *this );
+ const OUString aOldName( pTheme->GetName() );
+
+ SvxAbstractDialogFactory* pFact = SvxAbstractDialogFactory::Create();
+ ScopedVclPtr<AbstractTitleDialog> aDlg(pFact->CreateTitleDialog(mxThemes.get(), aOldName));
+
+ if( aDlg->Execute() == RET_OK )
+ {
+ const OUString aNewName( aDlg->GetTitle() );
+
+ if( !aNewName.isEmpty() && ( aNewName != aOldName ) )
+ {
+ OUString aName( aNewName );
+ sal_uInt16 nCount = 0;
+
+ while( mpGallery->HasTheme( aName ) && ( nCount++ < 16000 ) )
+ {
+ aName = aNewName + " " + OUString::number( nCount );
+ }
+
+ mpGallery->RenameTheme( aOldName, aName );
+ }
+ }
+ mpGallery->ReleaseTheme( pTheme, *this );
+ }
+ else if (rIdent == "assign")
+ {
+ GalleryTheme* pTheme = mpGallery->AcquireTheme( GetSelectedTheme(), *this );
+
+ if (pTheme && !pTheme->IsReadOnly())
+ {
+
+ SvxAbstractDialogFactory* pFact = SvxAbstractDialogFactory::Create();
+ ScopedVclPtr<AbstractGalleryIdDialog> aDlg(pFact->CreateGalleryIdDialog(mxThemes.get(), pTheme));
+ if( aDlg->Execute() == RET_OK )
+ pTheme->SetId( aDlg->GetId(), true );
+ }
+
+ mpGallery->ReleaseTheme( pTheme, *this );
+ }
+ else if (rIdent == "properties")
+ {
+ ImplGalleryThemeProperties( GetSelectedTheme(), false );
+ }
+}
+
+void GalleryBrowser1::Notify( SfxBroadcaster&, const SfxHint& rHint )
+{
+ const GalleryHint& rGalleryHint = static_cast<const GalleryHint&>(rHint);
+
+ switch( rGalleryHint.GetType() )
+ {
+ case GalleryHintType::THEME_CREATED:
+ ImplInsertThemeEntry( mpGallery->GetThemeInfo( rGalleryHint.GetThemeName() ) );
+ break;
+
+ case GalleryHintType::THEME_RENAMED:
+ {
+ const sal_Int32 nCurSelectPos = mxThemes->get_selected_index();
+ const sal_Int32 nRenameEntryPos = mxThemes->find_text( rGalleryHint.GetThemeName() );
+
+ mxThemes->remove_text( rGalleryHint.GetThemeName() );
+ ImplInsertThemeEntry( mpGallery->GetThemeInfo( rGalleryHint.GetStringData() ) );
+
+ if( nCurSelectPos == nRenameEntryPos )
+ {
+ mxThemes->select_text( rGalleryHint.GetStringData() );
+ SelectThemeHdl( *mxThemes );
+ }
+ }
+ break;
+
+ case GalleryHintType::THEME_REMOVED:
+ {
+ mxThemes->remove_text( rGalleryHint.GetThemeName() );
+ }
+ break;
+
+ case GalleryHintType::CLOSE_THEME:
+ {
+ const sal_Int32 nCurSelectPos = mxThemes->get_selected_index();
+ const sal_Int32 nCloseEntryPos = mxThemes->find_text( rGalleryHint.GetThemeName() );
+
+ if( nCurSelectPos == nCloseEntryPos )
+ {
+ if( nCurSelectPos < ( mxThemes->n_children() - 1 ) )
+ mxThemes->select( nCurSelectPos + 1 );
+ else if( nCurSelectPos )
+ mxThemes->select( nCurSelectPos - 1 );
+ else
+ mxThemes->select(-1);
+
+ SelectThemeHdl( *mxThemes );
+ }
+ }
+ break;
+
+ default:
+ break;
+ }
+}
+
+IMPL_STATIC_LINK_NOARG( GalleryBrowser1, OnMoreGalleriesClick, weld::Button&, void)
+{
+ css::uno::Sequence<css::beans::PropertyValue> aArgs{
+ comphelper::makePropertyValue("AdditionsTag", OUString("Gallery"))
+ };
+ comphelper::dispatchCommand(".uno:AdditionsDialog", aArgs);
+}
+
+IMPL_LINK(GalleryBrowser1, KeyInputHdl, const KeyEvent&, rKEvt, bool)
+{
+ bool bRet = false;
+
+ std::vector<OString> aExecVector;
+ ImplGetExecuteVector(aExecVector);
+ OString sExecuteIdent;
+ bool bMod1 = rKEvt.GetKeyCode().IsMod1();
+
+ switch( rKEvt.GetKeyCode().GetCode() )
+ {
+ case KEY_INSERT:
+ ClickNewThemeHdl(*mxNewTheme);
+ break;
+
+ case KEY_I:
+ {
+ if( bMod1 )
+ ClickNewThemeHdl(*mxNewTheme);
+ }
+ break;
+
+ case KEY_U:
+ {
+ if( bMod1 )
+ sExecuteIdent = "update";
+ }
+ break;
+
+ case KEY_DELETE:
+ sExecuteIdent = "delete";
+ break;
+
+ case KEY_D:
+ {
+ if( bMod1 )
+ sExecuteIdent = "delete";
+ }
+ break;
+
+ case KEY_R:
+ {
+ if( bMod1 )
+ sExecuteIdent = "rename";
+ }
+ break;
+
+ case KEY_RETURN:
+ {
+ if( bMod1 )
+ sExecuteIdent = "properties";
+ }
+ break;
+ }
+
+ if (!sExecuteIdent.isEmpty() && (std::find( aExecVector.begin(), aExecVector.end(), sExecuteIdent) != aExecVector.end()))
+ {
+ ImplExecute(sExecuteIdent);
+ bRet = true;
+ }
+
+ return bRet;
+}
+
+IMPL_LINK(GalleryBrowser1, PopupMenuHdl, const CommandEvent&, rCEvt, bool)
+{
+ if (rCEvt.GetCommand() != CommandEventId::ContextMenu)
+ return false;
+
+ std::vector<OString> aExecVector;
+ ImplGetExecuteVector(aExecVector);
+
+ if (aExecVector.empty())
+ return true;
+
+ std::unique_ptr<weld::Builder> xBuilder(Application::CreateBuilder(mxThemes.get(), "svx/ui/gallerymenu1.ui"));
+ std::unique_ptr<weld::Menu> xMenu(xBuilder->weld_menu("menu"));
+
+ xMenu->set_visible("update", std::find( aExecVector.begin(), aExecVector.end(), "update" ) != aExecVector.end());
+ xMenu->set_visible("rename", std::find( aExecVector.begin(), aExecVector.end(), "rename" ) != aExecVector.end());
+ xMenu->set_visible("delete", std::find( aExecVector.begin(), aExecVector.end(), "delete" ) != aExecVector.end());
+ xMenu->set_visible("assign", std::find( aExecVector.begin(), aExecVector.end(), "assign" ) != aExecVector.end());
+ xMenu->set_visible("properties", std::find( aExecVector.begin(), aExecVector.end(), "properties" ) != aExecVector.end());
+
+ OString sCommand(xMenu->popup_at_rect(mxThemes.get(), tools::Rectangle(rCEvt.GetMousePosPixel(), Size(1,1))));
+ ImplExecute(sCommand);
+
+ return true;
+}
+
+IMPL_LINK_NOARG(GalleryBrowser1, SelectThemeHdl, weld::TreeView&, void)
+{
+ if (maThemeSelectionHandler)
+ maThemeSelectionHandler();
+}
+
+IMPL_LINK_NOARG(GalleryBrowser1, ClickNewThemeHdl, weld::Button&, void)
+{
+ OUString aNewTheme( SvxResId(RID_SVXSTR_GALLERY_NEWTHEME) );
+ OUString aName( aNewTheme );
+ sal_uInt16 nCount = 0;
+
+ while( mpGallery->HasTheme( aName ) && ( nCount++ < 16000 ) )
+ {
+ aName = aNewTheme + " " + OUString::number( nCount );
+ }
+
+ if( !mpGallery->HasTheme( aName ) && mpGallery->CreateTheme( aName ) )
+ {
+ ImplGalleryThemeProperties( aName, true );
+ }
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/gallery2/galbrws1.hxx b/svx/source/gallery2/galbrws1.hxx
new file mode 100644
index 000000000..ccbc7f24f
--- /dev/null
+++ b/svx/source/gallery2/galbrws1.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 .
+ */
+
+#pragma once
+
+#include <svl/lstner.hxx>
+#include <vcl/weld.hxx>
+#include <vector>
+
+#include <functional>
+
+class GalleryBrowser1;
+
+
+class Gallery;
+class GalleryThemeEntry;
+class GalleryTheme;
+class VclAbstractDialog;
+struct ExchangeData;
+class SfxItemSet;
+
+namespace svx::sidebar { class GalleryControl; }
+
+class GalleryBrowser1 final : public SfxListener
+{
+ friend class GalleryBrowser;
+ friend class svx::sidebar::GalleryControl;
+
+private:
+
+ std::unique_ptr<weld::Button> mxNewTheme;
+ std::unique_ptr<weld::TreeView> mxThemes;
+ std::unique_ptr<weld::Button> mxMoreGalleries;
+ Gallery* mpGallery;
+ std::unique_ptr<ExchangeData> mpExchangeData;
+ std::unique_ptr<SfxItemSet> mpThemePropsDlgItemSet;
+
+ OUString aImgNormal;
+ OUString aImgDefault;
+ OUString aImgReadOnly;
+
+ ::std::function<void ()> maThemeSelectionHandler;
+
+ void ImplInsertThemeEntry( const GalleryThemeEntry* pEntry );
+ static void ImplFillExchangeData( const GalleryTheme* pThm, ExchangeData& rData );
+ void ImplGetExecuteVector(std::vector<OString>& o_aExec);
+ void ImplExecute(std::string_view rIdent);
+ void ImplGalleryThemeProperties( std::u16string_view rThemeName, bool bCreateNew );
+ void EndNewThemePropertiesDlgHdl(sal_Int32 nResult);
+ void EndThemePropertiesDlgHdl(sal_Int32 nResult);
+ void ImplEndGalleryThemeProperties(bool bCreateNew, sal_Int32 nResult);
+
+ // SfxListener
+ virtual void Notify( SfxBroadcaster& rBC, const SfxHint& rHint ) override;
+
+ DECL_LINK( ClickNewThemeHdl, weld::Button&, void );
+ DECL_LINK( SelectThemeHdl, weld::TreeView&, void );
+ DECL_LINK( PopupMenuHdl, const CommandEvent&, bool );
+ DECL_LINK( KeyInputHdl, const KeyEvent&, bool );
+ DECL_STATIC_LINK( GalleryBrowser1, OnMoreGalleriesClick, weld::Button&, void );
+
+public:
+
+ GalleryBrowser1(
+ weld::Builder& rBuilder,
+ Gallery* pGallery,
+ const ::std::function<void ()>& rThemeSelectionHandler);
+
+ ~GalleryBrowser1();
+
+ void SelectTheme( sal_uInt16 nThemePos ) { mxThemes->select( nThemePos ); SelectThemeHdl( *mxThemes ); }
+ OUString GetSelectedTheme() const { return mxThemes->get_selected_text(); }
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/gallery2/galbrws2.cxx b/svx/source/gallery2/galbrws2.cxx
new file mode 100644
index 000000000..27ba2634d
--- /dev/null
+++ b/svx/source/gallery2/galbrws2.cxx
@@ -0,0 +1,1242 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * 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/formats.hxx>
+#include <vcl/commandevent.hxx>
+#include <vcl/event.hxx>
+#include <vcl/transfer.hxx>
+#include <vcl/virdev.hxx>
+#include <sfx2/bindings.hxx>
+#include <sfx2/viewfrm.hxx>
+#include <vcl/graphicfilter.hxx>
+#include <helpids.h>
+#include <svx/svxids.hrc>
+#include <galobj.hxx>
+#include <svx/gallery1.hxx>
+#include <svx/galtheme.hxx>
+#include <svx/galctrl.hxx>
+#include <svx/galmisc.hxx>
+#include <galbrws2.hxx>
+#include <vcl/svapp.hxx>
+#include <vcl/weld.hxx>
+#include <svx/svxdlg.hxx>
+#include <svx/galleryitem.hxx>
+#include <comphelper/processfactory.hxx>
+#include <comphelper/propertyvalue.hxx>
+#include <com/sun/star/frame/FrameSearchFlag.hpp>
+#include <com/sun/star/frame/XDispatchProvider.hpp>
+#include <com/sun/star/frame/XFrame.hpp>
+#include <com/sun/star/gallery/GalleryItemType.hpp>
+#include <com/sun/star/graphic/XGraphic.hpp>
+#include <com/sun/star/lang/XComponent.hpp>
+#include <com/sun/star/style/GraphicLocation.hpp>
+
+#include <cassert>
+#include <map>
+#include <memory>
+#include <cppuhelper/implbase.hxx>
+#include <osl/diagnose.h>
+#include <o3tl/string_view.hxx>
+
+GalleryBrowserMode GalleryBrowser2::meInitMode = GALLERYBROWSERMODE_ICON;
+
+struct DispatchInfo
+{
+ css::util::URL TargetURL;
+ css::uno::Sequence< css::beans::PropertyValue > Arguments;
+ css::uno::Reference< css::frame::XDispatch > Dispatch;
+};
+
+IMPL_STATIC_LINK( GalleryBrowser2, AsyncDispatch_Impl, void*, p, void )
+{
+ DispatchInfo* pDispatchInfo = static_cast<DispatchInfo*>(p);
+ if ( pDispatchInfo && pDispatchInfo->Dispatch.is() )
+ {
+ try
+ {
+ pDispatchInfo->Dispatch->dispatch( pDispatchInfo->TargetURL,
+ pDispatchInfo->Arguments );
+ }
+ catch ( const css::uno::Exception& )
+ {
+ }
+ }
+
+ delete pDispatchInfo;
+}
+
+namespace
+{
+
+struct CommandInfo
+{
+ css::util::URL URL;
+ css::uno::Reference< css::frame::XDispatch > Dispatch;
+
+ explicit CommandInfo( const OUString &rURL )
+ {
+ URL.Complete = rURL;
+ }
+};
+
+class GalleryThemePopup : public ::cppu::WeakImplHelper< css::frame::XStatusListener >
+{
+private:
+ const GalleryTheme* mpTheme;
+ sal_uInt32 mnObjectPos;
+ bool mbPreview;
+ std::unique_ptr<weld::Builder> mxBuilder;
+ std::unique_ptr<weld::Menu> mxPopupMenu;
+ std::unique_ptr<weld::Menu> mxBackgroundPopup;
+ GalleryBrowser2* mpBrowser;
+
+ typedef std::map< int, CommandInfo > CommandInfoMap;
+ CommandInfoMap m_aCommandInfo;
+
+ static void Execute( const CommandInfo &rCmdInfo,
+ const css::uno::Sequence< css::beans::PropertyValue > &rArguments );
+
+ void MenuSelectHdl(std::string_view rIdent);
+ void BackgroundMenuSelectHdl(sal_uInt16 nId);
+public:
+ GalleryThemePopup(weld::Widget* pParent,
+ const GalleryTheme* pTheme,
+ sal_uInt32 nObjectPos,
+ bool bPreview,
+ GalleryBrowser2* pBrowser);
+
+ void ExecutePopup(weld::Widget* pParent, const ::Point &rPos);
+
+ virtual void SAL_CALL statusChanged( const css::frame::FeatureStateEvent &rEvent) override;
+ virtual void SAL_CALL disposing( const css::lang::EventObject &rSource) override;
+};
+
+
+GalleryThemePopup::GalleryThemePopup(
+ weld::Widget* pParent,
+ const GalleryTheme* pTheme,
+ sal_uInt32 nObjectPos,
+ bool bPreview,
+ GalleryBrowser2* pBrowser )
+ : mpTheme( pTheme )
+ , mnObjectPos( nObjectPos )
+ , mbPreview( bPreview )
+ , mxBuilder(Application::CreateBuilder(pParent, "svx/ui/gallerymenu2.ui"))
+ , mxPopupMenu(mxBuilder->weld_menu("menu"))
+ , mxBackgroundPopup(mxBuilder->weld_menu("backgroundmenu"))
+ , mpBrowser( pBrowser )
+{
+ // SID_GALLERY_ENABLE_ADDCOPY
+ m_aCommandInfo.emplace(
+ SID_GALLERY_ENABLE_ADDCOPY,
+ CommandInfo( ".uno:GalleryEnableAddCopy" ));
+ // SID_GALLERY_BG_BRUSH
+ m_aCommandInfo.emplace(
+ SID_GALLERY_BG_BRUSH,
+ CommandInfo( ".uno:BackgroundImage" ));
+ // SID_GALLERY_FORMATS
+ m_aCommandInfo.emplace(
+ SID_GALLERY_FORMATS,
+ CommandInfo( ".uno:InsertGalleryPic" ));
+
+}
+
+void SAL_CALL GalleryThemePopup::statusChanged(
+ const css::frame::FeatureStateEvent &rEvent )
+{
+ const OUString &rURL = rEvent.FeatureURL.Complete;
+ if ( rURL == ".uno:GalleryEnableAddCopy" )
+ {
+ if ( !rEvent.IsEnabled )
+ {
+ mxPopupMenu->set_visible("add", false);
+ }
+ }
+ else if ( rURL == ".uno:BackgroundImage" )
+ {
+ mxBackgroundPopup->clear();
+ if ( rEvent.IsEnabled )
+ {
+ OUString sItem;
+ css::uno::Sequence< OUString > sItems;
+ if ( ( rEvent.State >>= sItem ) && sItem.getLength() )
+ {
+ mxBackgroundPopup->append(OUString::number(1), sItem);
+ }
+ else if ( ( rEvent.State >>= sItems ) && sItems.hasElements() )
+ {
+ sal_uInt16 nId = 1;
+ for ( const OUString& rStr : std::as_const(sItems) )
+ {
+ mxBackgroundPopup->append(OUString::number(nId), rStr);
+ nId++;
+ }
+ }
+ }
+ }
+}
+
+void SAL_CALL GalleryThemePopup::disposing(
+ const css::lang::EventObject &/*rSource*/)
+{
+}
+
+void GalleryThemePopup::Execute(
+ const CommandInfo &rCmdInfo,
+ const css::uno::Sequence< css::beans::PropertyValue > &rArguments )
+{
+ if ( rCmdInfo.Dispatch.is() )
+ {
+ std::unique_ptr<DispatchInfo> pInfo(new DispatchInfo);
+ pInfo->TargetURL = rCmdInfo.URL;
+ pInfo->Arguments = rArguments;
+ pInfo->Dispatch = rCmdInfo.Dispatch;
+
+ if ( Application::PostUserEvent(
+ LINK( nullptr, GalleryBrowser2, AsyncDispatch_Impl), pInfo.get() ) )
+ pInfo.release();
+ }
+}
+
+void GalleryThemePopup::ExecutePopup(weld::Widget* pParent, const ::Point &rPos)
+{
+ css::uno::Reference< css::frame::XStatusListener > xThis( this );
+
+ const SgaObjKind eObjKind = mpTheme->GetObjectKind( mnObjectPos );
+ INetURLObject aURL;
+
+ const_cast< GalleryTheme* >( mpTheme )->GetURL( mnObjectPos, aURL );
+ const bool bValidURL = ( aURL.GetProtocol() != INetProtocol::NotValid );
+
+ mxPopupMenu->set_visible("add", bValidURL && SgaObjKind::Sound != eObjKind);
+
+ mxPopupMenu->set_visible("preview", bValidURL);
+ mxPopupMenu->set_active("preview", mbPreview);
+
+ if( mpTheme->IsReadOnly() || !mpTheme->GetObjectCount() )
+ {
+ mxPopupMenu->set_visible("delete", false);
+ mxPopupMenu->set_visible("title", false);
+ if (mpTheme->IsReadOnly())
+ mxPopupMenu->set_visible("paste", false);
+
+ if (!mpTheme->GetObjectCount())
+ mxPopupMenu->set_visible("copy", false);
+ }
+ else
+ {
+ mxPopupMenu->set_visible("delete", !mbPreview);
+ mxPopupMenu->set_visible("title", true);
+ mxPopupMenu->set_visible("copy", true);
+ mxPopupMenu->set_visible("paste", true);
+ }
+
+ // update status
+ css::uno::Reference< css::frame::XDispatchProvider> xDispatchProvider(
+ GalleryBrowser2::GetFrame(), css::uno::UNO_QUERY );
+ css::uno::Reference< css::util::XURLTransformer > xTransformer(
+ mpBrowser->GetURLTransformer() );
+ for ( auto& rInfo : m_aCommandInfo )
+ {
+ try
+ {
+ CommandInfo &rCmdInfo = rInfo.second;
+ if ( xTransformer.is() )
+ xTransformer->parseStrict( rCmdInfo.URL );
+
+ if ( xDispatchProvider.is() )
+ {
+ rCmdInfo.Dispatch = xDispatchProvider->queryDispatch(
+ rCmdInfo.URL,
+ "_self",
+ css::frame::FrameSearchFlag::SELF );
+ }
+
+ if ( rCmdInfo.Dispatch.is() )
+ {
+ rCmdInfo.Dispatch->addStatusListener( this, rCmdInfo.URL );
+ rCmdInfo.Dispatch->removeStatusListener( this, rCmdInfo.URL );
+ }
+ }
+ catch ( ... )
+ {}
+ }
+
+ if( !mxBackgroundPopup->n_children() || ( eObjKind == SgaObjKind::SvDraw ) || ( eObjKind == SgaObjKind::Sound ) )
+ mxPopupMenu->set_visible("background", false);
+ else
+ mxPopupMenu->set_visible("background", true);
+
+ MenuSelectHdl(mxPopupMenu->popup_at_rect(pParent, tools::Rectangle(rPos, Size(1,1))));
+}
+
+void GalleryThemePopup::MenuSelectHdl(std::string_view rIdent)
+{
+ if (rIdent.empty())
+ return;
+
+ sal_uInt16 nSubMenuId = o3tl::toUInt32(rIdent);
+ if (nSubMenuId)
+ {
+ BackgroundMenuSelectHdl(nSubMenuId-1);
+ return;
+ }
+
+ if (rIdent == "add")
+ {
+ const CommandInfoMap::const_iterator it = m_aCommandInfo.find( SID_GALLERY_FORMATS );
+ if (it != m_aCommandInfo.end())
+ mpBrowser->DispatchAdd(it->second.Dispatch, it->second.URL);
+ }
+ else
+ mpBrowser->Execute(rIdent);
+}
+
+void GalleryThemePopup::BackgroundMenuSelectHdl(sal_uInt16 nPos)
+{
+ OUString aURL( mpBrowser->GetURL().GetMainURL( INetURLObject::DecodeMechanism::NONE ) );
+ OUString aFilterName( mpBrowser->GetFilterName() );
+
+ css::uno::Sequence< css::beans::PropertyValue > aArgs{
+ comphelper::makePropertyValue("Background.Transparent", sal_Int32( 0 )), // 0 - 100
+ comphelper::makePropertyValue("Background.BackColor", sal_Int32( - 1 )),
+ comphelper::makePropertyValue("Background.URL", aURL),
+ comphelper::makePropertyValue("Background.Filtername", aFilterName), // FIXME name should be FilterName
+ comphelper::makePropertyValue("Background.Position", css::style::GraphicLocation_TILED),
+ comphelper::makePropertyValue("Position", nPos)
+ };
+
+ const CommandInfoMap::const_iterator it = m_aCommandInfo.find( SID_GALLERY_BG_BRUSH );
+ if ( it != m_aCommandInfo.end() )
+ Execute( it->second, aArgs );
+}
+
+} // end anonymous namespace
+
+GalleryBrowser2::GalleryBrowser2(weld::Builder& rBuilder, Gallery* pGallery)
+ : mpGallery(pGallery)
+ , mpCurTheme(nullptr)
+ , mxIconView(new GalleryIconView(this, rBuilder.weld_scrolled_window("galleryscroll", true)))
+ , mxIconViewWin(new weld::CustomWeld(rBuilder, "gallery", *mxIconView))
+ , mxListView(rBuilder.weld_tree_view("gallerylist"))
+ , mxPreview(new GalleryPreview(this, rBuilder.weld_scrolled_window("previewscroll")))
+ , mxPreviewWin(new weld::CustomWeld(rBuilder, "preview", *mxPreview))
+ , mxIconButton(rBuilder.weld_toggle_button("icon"))
+ , mxListButton(rBuilder.weld_toggle_button("list"))
+ , mxInfoBar(rBuilder.weld_label("label"))
+ , maPreviewSize(28, 28)
+ , mnCurActionPos ( 0xffffffff )
+ , meMode ( GALLERYBROWSERMODE_NONE )
+ , meLastMode ( GALLERYBROWSERMODE_NONE )
+{
+ m_xContext.set( ::comphelper::getProcessComponentContext() );
+
+ int nHeight = mxListView->get_height_rows(10);
+ mxListView->set_size_request(-1, nHeight);
+ mxIconView->set_size_request(-1, nHeight);
+
+ m_xTransformer.set(
+ m_xContext->getServiceManager()->createInstanceWithContext(
+ "com.sun.star.util.URLTransformer", m_xContext ),
+ css::uno::UNO_QUERY );
+
+ mxIconButton->set_help_id(HID_GALLERY_ICONVIEW);
+ mxListButton->set_help_id(HID_GALLERY_LISTVIEW);
+
+ mxIconButton->connect_toggled( LINK( this, GalleryBrowser2, SelectTbxHdl ) );
+ mxListButton->connect_toggled( LINK( this, GalleryBrowser2, SelectTbxHdl ) );
+
+ mxIconView->SetSelectHdl( LINK( this, GalleryBrowser2, SelectObjectValueSetHdl ) );
+ mxListView->connect_visible_range_changed(LINK(this, GalleryBrowser2, VisRowsScrolledHdl));
+ mxListView->connect_size_allocate(LINK(this, GalleryBrowser2, SizeAllocHdl));
+ mxListView->connect_changed( LINK( this, GalleryBrowser2, SelectObjectHdl ) );
+ mxListView->connect_popup_menu(LINK(this, GalleryBrowser2, PopupMenuHdl));
+ mxListView->connect_key_press(LINK(this, GalleryBrowser2, KeyInputHdl));
+ mxListView->connect_row_activated(LINK(this, GalleryBrowser2, RowActivatedHdl));
+ mxDragDropTargetHelper.reset(new GalleryDragDrop(this, mxListView->get_drop_target()));
+ mxListView->connect_drag_begin(LINK(this, GalleryBrowser2, DragBeginHdl));
+
+ mxListView->set_help_id(HID_GALLERY_WINDOW);
+
+ SetMode( ( GALLERYBROWSERMODE_PREVIEW != GalleryBrowser2::meInitMode ) ? GalleryBrowser2::meInitMode : GALLERYBROWSERMODE_ICON );
+}
+
+IMPL_LINK(GalleryBrowser2, PopupMenuHdl, const CommandEvent&, rCEvt, bool)
+{
+ if (rCEvt.GetCommand() != CommandEventId::ContextMenu)
+ return false;
+ ShowContextMenu(rCEvt);
+ return true;
+}
+
+IMPL_LINK(GalleryBrowser2, KeyInputHdl, const KeyEvent&, rKEvt, bool)
+{
+ return KeyInput(rKEvt);
+}
+
+IMPL_LINK_NOARG(GalleryBrowser2, RowActivatedHdl, weld::TreeView&, bool)
+{
+ TogglePreview();
+ return true;
+}
+
+GalleryBrowser2::~GalleryBrowser2()
+{
+ if (mpCurTheme)
+ mpGallery->ReleaseTheme( mpCurTheme, *this );
+}
+
+void GalleryBrowser2::Notify( SfxBroadcaster&, const SfxHint& rHint )
+{
+ const GalleryHint& rGalleryHint = static_cast<const GalleryHint&>(rHint);
+
+ switch( rGalleryHint.GetType() )
+ {
+ case GalleryHintType::THEME_UPDATEVIEW:
+ {
+ if( GALLERYBROWSERMODE_PREVIEW == GetMode() )
+ SetMode( meLastMode );
+
+ ImplUpdateViews( reinterpret_cast<size_t>(rGalleryHint.GetData1()) + 1 );
+ }
+ break;
+
+ default:
+ break;
+ }
+}
+
+sal_Int8 GalleryBrowser2::AcceptDrop( const DropTargetHelper& rTarget )
+{
+ sal_Int8 nRet = DND_ACTION_NONE;
+
+ if( mpCurTheme && !mpCurTheme->IsReadOnly() )
+ {
+ if( !mpCurTheme->IsDragging() )
+ {
+ if( rTarget.IsDropFormatSupported( SotClipboardFormatId::DRAWING ) ||
+ rTarget.IsDropFormatSupported( SotClipboardFormatId::FILE_LIST ) ||
+ rTarget.IsDropFormatSupported( SotClipboardFormatId::SIMPLE_FILE ) ||
+ rTarget.IsDropFormatSupported( SotClipboardFormatId::SVXB ) ||
+ rTarget.IsDropFormatSupported( SotClipboardFormatId::GDIMETAFILE ) ||
+ rTarget.IsDropFormatSupported( SotClipboardFormatId::BITMAP ) )
+ {
+ nRet = DND_ACTION_COPY;
+ }
+ }
+ else
+ nRet = DND_ACTION_COPY;
+ }
+
+ return nRet;
+}
+
+sal_Int8 GalleryBrowser2::ExecuteDrop( const ExecuteDropEvent& rEvt )
+{
+ sal_Int8 nRet = DND_ACTION_NONE;
+
+ if( mpCurTheme )
+ {
+ Point aSelPos;
+ const sal_uInt32 nItemId = ImplGetSelectedItemId( &rEvt.maPosPixel, aSelPos );
+ const sal_uInt32 nInsertPos = (nItemId ? (nItemId - 1) : mpCurTheme->GetObjectCount());
+
+ if( mpCurTheme->IsDragging() )
+ mpCurTheme->ChangeObjectPos( mpCurTheme->GetDragPos(), nInsertPos );
+ else
+ nRet = mpCurTheme->InsertTransferable( rEvt.maDropEvent.Transferable, nInsertPos ) ? 1 : 0;
+ }
+
+ return nRet;
+}
+
+bool GalleryBrowser2::StartDrag()
+{
+ if (!mpCurTheme)
+ return true;
+ return m_xHelper->StartDrag();
+}
+
+IMPL_LINK(GalleryBrowser2, DragBeginHdl, bool&, rUnsetDragIcon, bool)
+{
+ rUnsetDragIcon = false;
+ return StartDrag();
+}
+
+void GalleryBrowser2::TogglePreview()
+{
+ SetMode( ( GALLERYBROWSERMODE_PREVIEW != GetMode() ) ? GALLERYBROWSERMODE_PREVIEW : meLastMode );
+ GetViewWindow()->grab_focus();
+}
+
+void GalleryBrowser2::ShowContextMenu(const CommandEvent& rCEvt)
+{
+ Point aMousePos = rCEvt.GetMousePosPixel();
+ Point aSelPos;
+ const sal_uInt32 nItemId = ImplGetSelectedItemId( rCEvt.IsMouseEvent() ? &aMousePos : nullptr, aSelPos );
+
+ if( !(mpCurTheme && nItemId && ( nItemId <= mpCurTheme->GetObjectCount() )) )
+ return;
+
+ ImplSelectItemId( nItemId );
+
+ css::uno::Reference< css::frame::XFrame > xFrame( GetFrame() );
+ if ( !xFrame.is() )
+ return;
+
+ weld::Widget* pParent = GetViewWindow();
+ mnCurActionPos = nItemId - 1;
+ rtl::Reference< GalleryThemePopup > xPopup(
+ new GalleryThemePopup(
+ pParent,
+ mpCurTheme,
+ mnCurActionPos,
+ GALLERYBROWSERMODE_PREVIEW == GetMode(),
+ this ) );
+ xPopup->ExecutePopup(pParent, aSelPos);
+}
+
+bool GalleryBrowser2::ViewBoxHasFocus() const
+{
+ return mxIconButton->has_focus() || mxListButton->has_focus();
+}
+
+bool GalleryBrowser2::KeyInput(const KeyEvent& rKEvt)
+{
+ Point aSelPos;
+ const sal_uInt32 nItemId = ImplGetSelectedItemId( nullptr, aSelPos );
+ bool bRet = false;
+
+ if (!ViewBoxHasFocus() && nItemId && mpCurTheme)
+ {
+ OString sExecuteIdent;
+ INetURLObject aURL;
+
+ mpCurTheme->GetURL( nItemId - 1, aURL );
+
+ const bool bValidURL = ( aURL.GetProtocol() != INetProtocol::NotValid );
+ bool bPreview = bValidURL;
+ bool bDelete = false;
+ bool bTitle = false;
+
+ if( !mpCurTheme->IsReadOnly() && mpCurTheme->GetObjectCount() )
+ {
+ bDelete = ( GALLERYBROWSERMODE_PREVIEW != GetMode() );
+ bTitle = true;
+ }
+
+ switch( rKEvt.GetKeyCode().GetCode() )
+ {
+ case KEY_SPACE:
+ case KEY_RETURN:
+ case KEY_P:
+ {
+ if( bPreview )
+ {
+ TogglePreview();
+ bRet = true;
+ }
+ }
+ break;
+
+ case KEY_INSERT:
+ case KEY_I:
+ {
+ // Inserting a gallery item in the document must be dispatched
+ if( bValidURL )
+ {
+ DispatchAdd(css::uno::Reference<css::frame::XDispatch>(), css::util::URL());
+ return true;
+ }
+ }
+ break;
+
+ case KEY_DELETE:
+ case KEY_D:
+ {
+ if( bDelete )
+ sExecuteIdent = "delete";
+ }
+ break;
+
+ case KEY_T:
+ {
+ if( bTitle )
+ sExecuteIdent = "title";
+ }
+ break;
+
+ default:
+ break;
+ }
+
+ if (!sExecuteIdent.isEmpty())
+ {
+ Execute(sExecuteIdent);
+ bRet = true;
+ }
+ }
+
+ return bRet;
+}
+
+void GalleryBrowser2::SelectTheme( std::u16string_view rThemeName )
+{
+ if( mpCurTheme )
+ mpGallery->ReleaseTheme( mpCurTheme, *this );
+
+ mpCurTheme = mpGallery->AcquireTheme( rThemeName, *this );
+
+ m_xHelper.set(new GalleryTransferable(mpCurTheme, 0, true));
+ rtl::Reference<TransferDataContainer> xHelper(m_xHelper);
+ mxListView->enable_drag_source(xHelper, DND_ACTION_COPY | DND_ACTION_LINK);
+ mxIconView->SetDragDataTransferrable(xHelper, DND_ACTION_COPY | DND_ACTION_LINK);
+ mxPreview->SetDragDataTransferrable(xHelper, DND_ACTION_COPY | DND_ACTION_LINK);
+
+ mxIconView->SetTheme(mpCurTheme);
+ mxPreview->SetTheme(mpCurTheme);
+
+ if( GALLERYBROWSERMODE_PREVIEW == GetMode() )
+ meMode = meLastMode;
+
+ ImplUpdateViews( 1 );
+
+ bool bIconMode = (GALLERYBROWSERMODE_ICON == GetMode());
+ mxIconButton->set_sensitive(true);
+ mxListButton->set_sensitive(true);
+ mxIconButton->set_active(bIconMode);
+ mxListButton->set_active(!bIconMode);
+}
+
+void GalleryBrowser2::SetMode( GalleryBrowserMode eMode )
+{
+ if( GetMode() == eMode )
+ return;
+
+ meLastMode = GetMode();
+
+ switch( eMode )
+ {
+ case GALLERYBROWSERMODE_ICON:
+ {
+ mxListView->hide();
+
+ mxPreview->Hide();
+ mxPreview->SetGraphic( Graphic() );
+ GalleryPreview::PreviewMedia( INetURLObject() );
+
+ mxIconView->Show();
+
+ mxIconButton->set_sensitive(true);
+ mxListButton->set_sensitive(true);
+
+ mxIconButton->set_active(true);
+ mxListButton->set_active(false);
+ }
+ break;
+
+ case GALLERYBROWSERMODE_LIST:
+ {
+ mxIconView->Hide();
+
+ mxPreview->Hide();
+ mxPreview->SetGraphic( Graphic() );
+ GalleryPreview::PreviewMedia( INetURLObject() );
+
+ mxListView->show();
+ UpdateRows(true);
+
+ mxIconButton->set_sensitive(true);
+ mxListButton->set_sensitive(true);
+
+ mxIconButton->set_active(false);
+ mxListButton->set_active(true);
+ }
+ break;
+
+ case GALLERYBROWSERMODE_PREVIEW:
+ {
+ Graphic aGraphic;
+ Point aSelPos;
+ const sal_uInt32 nItemId = ImplGetSelectedItemId( nullptr, aSelPos );
+
+ if( nItemId )
+ {
+ const sal_uInt32 nPos = nItemId - 1;
+
+ mxIconView->Hide();
+ mxListView->hide();
+
+ if( mpCurTheme )
+ mpCurTheme->GetGraphic( nPos, aGraphic );
+
+ mxPreview->SetGraphic( aGraphic );
+ mxPreview->Show();
+
+ if( mpCurTheme && mpCurTheme->GetObjectKind( nPos ) == SgaObjKind::Sound )
+ GalleryPreview::PreviewMedia( mpCurTheme->GetObjectURL( nPos ) );
+
+ mxIconButton->set_sensitive(false);
+ mxListButton->set_sensitive(false);
+ }
+ }
+ break;
+
+ default:
+ break;
+ }
+
+ GalleryBrowser2::meInitMode = meMode = eMode;
+}
+
+weld::Widget* GalleryBrowser2::GetViewWindow() const
+{
+ weld::Widget* pRet;
+
+ switch( GetMode() )
+ {
+ case GALLERYBROWSERMODE_LIST: pRet = mxListView.get(); break;
+ case GALLERYBROWSERMODE_PREVIEW: pRet = mxPreview->GetDrawingArea(); break;
+
+ default:
+ pRet = mxIconView->GetDrawingArea();
+ break;
+ }
+
+ return pRet;
+}
+
+void GalleryBrowser2::Travel( GalleryBrowserTravel eTravel )
+{
+ if( !mpCurTheme )
+ return;
+
+ Point aSelPos;
+ const sal_uInt32 nItemId = ImplGetSelectedItemId( nullptr, aSelPos );
+
+ if( !nItemId )
+ return;
+
+ sal_uInt32 nNewItemId = nItemId;
+
+ switch( eTravel )
+ {
+ case GalleryBrowserTravel::First: nNewItemId = 1; break;
+ case GalleryBrowserTravel::Last: nNewItemId = mpCurTheme->GetObjectCount(); break;
+ case GalleryBrowserTravel::Previous: nNewItemId--; break;
+ case GalleryBrowserTravel::Next: nNewItemId++; break;
+ default:
+ break;
+ }
+
+ if( nNewItemId < 1 )
+ nNewItemId = 1;
+ else if( nNewItemId > mpCurTheme->GetObjectCount() )
+ nNewItemId = mpCurTheme->GetObjectCount();
+
+ if( nNewItemId == nItemId )
+ return;
+
+ ImplSelectItemId( nNewItemId );
+ ImplUpdateInfoBar();
+
+ if( GALLERYBROWSERMODE_PREVIEW != GetMode() )
+ return;
+
+ Graphic aGraphic;
+ const sal_uInt32 nPos = nNewItemId - 1;
+
+ mpCurTheme->GetGraphic( nPos, aGraphic );
+ mxPreview->SetGraphic( aGraphic );
+
+ if( SgaObjKind::Sound == mpCurTheme->GetObjectKind( nPos ) )
+ GalleryPreview::PreviewMedia( mpCurTheme->GetObjectURL( nPos ) );
+
+ mxPreview->Invalidate();
+}
+
+void GalleryBrowser2::ImplUpdateViews( sal_uInt16 nSelectionId )
+{
+ mxIconView->Hide();
+ mxListView->hide();
+ mxPreview->Hide();
+
+ mxIconView->Clear();
+ mxListView->clear();
+
+ if( mpCurTheme )
+ {
+ const int nAlwaysUpToDate = 15;
+
+ mxListView->freeze();
+
+ sal_uInt32 nCount = mpCurTheme->GetObjectCount();
+ for (sal_uInt32 i = 0; i < nCount; ++i)
+ {
+ mxIconView->InsertItem(i + 1); // skip reserved id 0
+ mxListView->append(OUString::number(i), ""); // create on-demand in VisRowsScrolledHdl
+
+ if (i == nAlwaysUpToDate) // fill in the first block
+ UpdateRows(false);
+ }
+
+ if (nCount < nAlwaysUpToDate) // if less than block size, fill in all of them
+ UpdateRows(false);
+
+ mxListView->thaw();
+
+ ImplSelectItemId( std::min<sal_uInt16>( nSelectionId, mpCurTheme->GetObjectCount() ) );
+ }
+
+ switch( GetMode() )
+ {
+ case GALLERYBROWSERMODE_ICON: mxIconView->Show(); break;
+ case GALLERYBROWSERMODE_LIST:
+ mxListView->show();
+ UpdateRows(true);
+ break;
+ case GALLERYBROWSERMODE_PREVIEW: mxPreview->Show(); break;
+
+ default:
+ break;
+ }
+
+ ImplUpdateInfoBar();
+}
+
+void GalleryBrowser2::UpdateRows(bool bVisibleOnly)
+{
+ auto lambda = [this](weld::TreeIter& rEntry){
+ // id is non-null if the preview is pending creation
+ OUString sId(mxListView->get_id(rEntry));
+ if (sId.isEmpty())
+ return false;
+
+ // get the icon for the listview
+ BitmapEx aBitmapEx;
+ Size aPreparedSize;
+
+ OUString sItemTextTitle;
+ OUString sItemTextPath;
+
+ sal_Int32 i = sId.toUInt32();
+ mpCurTheme->GetPreviewBitmapExAndStrings(i, aBitmapEx, aPreparedSize, sItemTextTitle, sItemTextPath);
+
+ bool bNeedToCreate(aBitmapEx.IsEmpty());
+ if (!bNeedToCreate && (sItemTextTitle.isEmpty() || aPreparedSize != maPreviewSize))
+ bNeedToCreate = true;
+
+ if (bNeedToCreate)
+ {
+ std::unique_ptr<SgaObject> xObj = mpCurTheme->AcquireObject(i);
+ if (xObj)
+ {
+ aBitmapEx = xObj->createPreviewBitmapEx(maPreviewSize);
+ sItemTextTitle = GalleryBrowser2::GetItemText(*xObj, GalleryItemFlags::Title);
+ sItemTextPath = GalleryBrowser2::GetItemText(*xObj, GalleryItemFlags::Path);
+
+ mpCurTheme->SetPreviewBitmapExAndStrings(i, aBitmapEx, maPreviewSize, sItemTextTitle, sItemTextPath);
+ }
+ }
+
+ ScopedVclPtr<VirtualDevice> xDev(mxListView->create_virtual_device());
+ xDev->SetOutputSizePixel(maPreviewSize);
+
+ if (!aBitmapEx.IsEmpty())
+ {
+ const Size aBitmapExSizePixel(aBitmapEx.GetSizePixel());
+ const Point aPos(
+ ((maPreviewSize.Width() - aBitmapExSizePixel.Width()) >> 1),
+ ((maPreviewSize.Height() - aBitmapExSizePixel.Height()) >> 1));
+
+ if (aBitmapEx.IsAlpha())
+ {
+ // draw checkered background
+ GalleryIconView::drawTransparenceBackground(*xDev, aPos, aBitmapExSizePixel);
+ }
+
+ xDev->DrawBitmapEx(aPos, aBitmapEx);
+ }
+
+ mxListView->set_text(rEntry, sItemTextTitle);
+ mxListView->set_image(rEntry, *xDev);
+ mxListView->set_id(rEntry, OUString());
+
+ return false;
+ };
+
+ if (bVisibleOnly)
+ {
+ // ensure all visible entries are up to date
+ mxListView->visible_foreach(lambda);
+ // and ensure all selected entries are up to date
+ mxListView->selected_foreach(lambda);
+ return;
+ }
+
+ mxListView->all_foreach(lambda);
+}
+
+IMPL_LINK_NOARG(GalleryBrowser2, VisRowsScrolledHdl, weld::TreeView&, void)
+{
+ UpdateRows(true);
+}
+
+IMPL_LINK_NOARG(GalleryBrowser2, SizeAllocHdl, const Size&, void)
+{
+ UpdateRows(true);
+}
+
+void GalleryBrowser2::ImplUpdateInfoBar()
+{
+ if (!mpCurTheme)
+ return;
+ mxInfoBar->set_label( mpCurTheme->GetName() );
+}
+
+void GalleryBrowser2::ImplUpdateSelection()
+{
+ if (!mpCurTheme)
+ return;
+ auto nSelectedObject = (GALLERYBROWSERMODE_ICON == GetMode()) ? (mxIconView->GetSelectedItemId() - 1) : mxListView->get_selected_index();
+ m_xHelper->SelectObject(nSelectedObject);
+}
+
+sal_uInt32 GalleryBrowser2::ImplGetSelectedItemId( const Point* pSelPos, Point& rSelPos )
+{
+ sal_uInt32 nRet = 0;
+
+ if( GALLERYBROWSERMODE_PREVIEW == GetMode() )
+ {
+ nRet = ( ( GALLERYBROWSERMODE_ICON == meLastMode ) ? mxIconView->GetSelectedItemId() : ( mxListView->get_selected_index() + 1 ) );
+
+ if( pSelPos )
+ rSelPos = *pSelPos;
+ else
+ {
+ Size aOutputSizePixel(mxPreview->GetOutputSizePixel());
+ rSelPos = Point( aOutputSizePixel.Width() >> 1, aOutputSizePixel.Height() >> 1 );
+ }
+ }
+ else if (GALLERYBROWSERMODE_ICON == GetMode())
+ {
+ if (pSelPos)
+ {
+ nRet = mxIconView->GetItemId( *pSelPos );
+ rSelPos = *pSelPos;
+ }
+ else
+ {
+ nRet = mxIconView->GetSelectedItemId();
+ rSelPos = mxIconView->GetItemRect(nRet).Center();
+ }
+ }
+ else
+ {
+ std::unique_ptr<weld::TreeIter> xIter = mxListView->make_iterator();
+ if( pSelPos )
+ {
+ if (mxListView->get_dest_row_at_pos(*pSelPos, xIter.get(), false))
+ nRet = mxListView->get_iter_index_in_parent(*xIter) + 1;
+ rSelPos = *pSelPos;
+ }
+ else
+ {
+ if (mxListView->get_selected(xIter.get()))
+ {
+ nRet = mxListView->get_iter_index_in_parent(*xIter) + 1;
+ rSelPos = mxListView->get_row_area(*xIter).Center();
+ }
+ }
+ }
+
+ if( nRet && ( !mpCurTheme || ( nRet > mpCurTheme->GetObjectCount() ) ) )
+ {
+ nRet = 0;
+ }
+
+ return nRet;
+}
+
+void GalleryBrowser2::ImplSelectItemId(sal_uInt32 nItemId)
+{
+ if( nItemId )
+ {
+ mxIconView->SelectItem(nItemId);
+ mxListView->select( nItemId - 1 );
+ ImplUpdateSelection();
+ }
+}
+
+css::uno::Reference< css::frame::XFrame >
+GalleryBrowser2::GetFrame()
+{
+ css::uno::Reference< css::frame::XFrame > xFrame;
+ SfxViewFrame* pCurrentViewFrame = SfxViewFrame::Current();
+ if ( pCurrentViewFrame )
+ {
+ SfxBindings& rBindings = pCurrentViewFrame->GetBindings();
+ xFrame.set( rBindings.GetActiveFrame() );
+ }
+
+ return xFrame;
+}
+
+void GalleryBrowser2::DispatchAdd(
+ const css::uno::Reference< css::frame::XDispatch > &rxDispatch,
+ const css::util::URL &rURL)
+{
+ Point aSelPos;
+ const sal_uInt32 nItemId = ImplGetSelectedItemId( nullptr, aSelPos );
+
+ if( !mpCurTheme || !nItemId )
+ return;
+
+ mnCurActionPos = nItemId - 1;
+
+ css::uno::Reference< css::frame::XDispatch > xDispatch( rxDispatch );
+ css::util::URL aURL = rURL;
+
+ if ( !xDispatch.is() )
+ {
+ css::uno::Reference< css::frame::XDispatchProvider > xDispatchProvider(
+ GetFrame(), css::uno::UNO_QUERY );
+ if ( !xDispatchProvider.is() || !m_xTransformer.is() )
+ return;
+
+ aURL.Complete = ".uno:InsertGalleryPic";
+ m_xTransformer->parseStrict( aURL );
+ xDispatch = xDispatchProvider->queryDispatch(
+ aURL,
+ "_self",
+ css::frame::FrameSearchFlag::SELF );
+ }
+
+ if ( !xDispatch.is() )
+ return;
+
+ sal_Int8 nType = 0;
+ OUString aFilterName;
+ css::uno::Reference< css::lang::XComponent > xDrawing;
+ css::uno::Reference< css::graphic::XGraphic > xGraphic;
+
+ aFilterName = GetFilterName();
+
+ switch( mpCurTheme->GetObjectKind( mnCurActionPos ) )
+ {
+ case SgaObjKind::Bitmap:
+ case SgaObjKind::Animation:
+ case SgaObjKind::Inet:
+ // TODO drawing objects are inserted as drawings only via drag&drop
+ case SgaObjKind::SvDraw:
+ nType = css::gallery::GalleryItemType::GRAPHIC;
+ break;
+
+ case SgaObjKind::Sound :
+ nType = css::gallery::GalleryItemType::MEDIA;
+ break;
+
+ default:
+ nType = css::gallery::GalleryItemType::EMPTY;
+ break;
+ }
+
+ Graphic aGraphic;
+ bool bGraphic = mpCurTheme->GetGraphic( mnCurActionPos, aGraphic );
+ if ( bGraphic && !aGraphic.IsNone() )
+ xGraphic.set( aGraphic.GetXGraphic() );
+ OSL_ENSURE( xGraphic.is(), "gallery item is graphic, but the reference is invalid!" );
+
+ css::uno::Sequence< css::beans::PropertyValue > aSeq{
+ comphelper::makePropertyValue(SVXGALLERYITEM_TYPE, nType),
+ comphelper::makePropertyValue(SVXGALLERYITEM_URL, OUString()),
+ comphelper::makePropertyValue(SVXGALLERYITEM_FILTER, aFilterName),
+ comphelper::makePropertyValue(SVXGALLERYITEM_DRAWING, xDrawing),
+ comphelper::makePropertyValue(SVXGALLERYITEM_GRAPHIC, xGraphic)
+ };
+ assert(aSeq.getLength() == SVXGALLERYITEM_PARAMS);
+
+ css::uno::Sequence< css::beans::PropertyValue > aArgs{ comphelper::makePropertyValue(
+ SVXGALLERYITEM_ARGNAME, aSeq) };
+
+ std::unique_ptr<DispatchInfo> pInfo(new DispatchInfo);
+ pInfo->TargetURL = aURL;
+ pInfo->Arguments = aArgs;
+ pInfo->Dispatch = xDispatch;
+
+ if ( Application::PostUserEvent(
+ LINK( nullptr, GalleryBrowser2, AsyncDispatch_Impl), pInfo.get() ) )
+ pInfo.release();
+}
+
+void GalleryBrowser2::Execute(std::string_view rIdent)
+{
+ Point aSelPos;
+ const sal_uInt32 nItemId = ImplGetSelectedItemId( nullptr, aSelPos );
+
+ if( !(mpCurTheme && nItemId) )
+ return;
+
+ mnCurActionPos = nItemId - 1;
+
+ if (rIdent == "preview")
+ SetMode( ( GALLERYBROWSERMODE_PREVIEW != GetMode() ) ? GALLERYBROWSERMODE_PREVIEW : meLastMode );
+ else if (rIdent == "delete")
+ {
+ if (!mpCurTheme->IsReadOnly())
+ {
+ std::unique_ptr<weld::Builder> xBuilder(Application::CreateBuilder(GetViewWindow(), "svx/ui/querydeleteobjectdialog.ui"));
+ std::unique_ptr<weld::MessageDialog> xQuery(xBuilder->weld_message_dialog("QueryDeleteObjectDialog"));
+ if (xQuery->run() == RET_YES)
+ {
+ mpCurTheme->RemoveObject( mnCurActionPos );
+ }
+ }
+ }
+ else if (rIdent == "title")
+ {
+ std::unique_ptr<SgaObject> pObj = mpCurTheme->AcquireObject( mnCurActionPos );
+
+ if( pObj )
+ {
+ const OUString aOldTitle( GetItemText( *pObj, GalleryItemFlags::Title ) );
+
+ SvxAbstractDialogFactory* pFact = SvxAbstractDialogFactory::Create();
+ ScopedVclPtr<AbstractTitleDialog> aDlg(pFact->CreateTitleDialog(GetViewWindow(), aOldTitle));
+ if( aDlg->Execute() == RET_OK )
+ {
+ OUString aNewTitle( aDlg->GetTitle() );
+
+ if( ( aNewTitle.isEmpty() && !pObj->GetTitle().isEmpty() ) || ( aNewTitle != aOldTitle ) )
+ {
+ if( aNewTitle.isEmpty() )
+ aNewTitle = "__<empty>__";
+
+ pObj->SetTitle( aNewTitle );
+ mpCurTheme->InsertObject( *pObj );
+ }
+ }
+ }
+ }
+ else if (rIdent == "copy")
+ {
+ mpCurTheme->CopyToClipboard(*GetViewWindow(), mnCurActionPos);
+ }
+ else if (rIdent == "paste")
+ {
+ if( !mpCurTheme->IsReadOnly() )
+ {
+ weld::Widget* pParent = GetViewWindow();
+ TransferableDataHelper aDataHelper(TransferableDataHelper::CreateFromClipboard(pParent->get_clipboard()));
+ mpCurTheme->InsertTransferable( aDataHelper.GetTransferable(), mnCurActionPos );
+ }
+ }
+}
+
+OUString GalleryBrowser2::GetItemText( const SgaObject& rObj, GalleryItemFlags nItemTextFlags )
+{
+ OUString aRet;
+
+ const INetURLObject& aURL(rObj.GetURL());
+
+ if( nItemTextFlags & GalleryItemFlags::Title )
+ {
+ OUString aTitle( rObj.GetTitle() );
+
+ if( aTitle.isEmpty() )
+ aTitle = aURL.getBase( INetURLObject::LAST_SEGMENT, true, INetURLObject::DecodeMechanism::Unambiguous );
+
+ if( aTitle.isEmpty() )
+ {
+ aTitle = aURL.GetMainURL( INetURLObject::DecodeMechanism::Unambiguous );
+ aTitle = aTitle.copy( aTitle.lastIndexOf('/')+1 );
+ }
+
+ aRet += aTitle;
+ }
+
+ if( nItemTextFlags & GalleryItemFlags::Path )
+ {
+ const OUString aPath( aURL.getFSysPath( FSysStyle::Detect ) );
+
+ if( !aPath.isEmpty() && ( nItemTextFlags & GalleryItemFlags::Title ) )
+ aRet += " (";
+
+ aRet += aURL.getFSysPath( FSysStyle::Detect );
+
+ if( !aPath.isEmpty() && ( nItemTextFlags & GalleryItemFlags::Title ) )
+ aRet += ")";
+ }
+
+ return aRet;
+}
+
+INetURLObject GalleryBrowser2::GetURL() const
+{
+ INetURLObject aURL;
+
+ if( mpCurTheme && mnCurActionPos != 0xffffffff )
+ aURL = mpCurTheme->GetObjectURL( mnCurActionPos );
+
+ return aURL;
+}
+
+OUString GalleryBrowser2::GetFilterName() const
+{
+ OUString aFilterName;
+
+ if( mpCurTheme && mnCurActionPos != 0xffffffff )
+ {
+ const SgaObjKind eObjKind = mpCurTheme->GetObjectKind( mnCurActionPos );
+
+ if( ( SgaObjKind::Bitmap == eObjKind ) || ( SgaObjKind::Animation == eObjKind ) )
+ {
+ GraphicFilter& rFilter = GraphicFilter::GetGraphicFilter();
+ INetURLObject aURL;
+ mpCurTheme->GetURL( mnCurActionPos, aURL );
+ sal_uInt16 nFilter = rFilter.GetImportFormatNumberForShortName(aURL.GetFileExtension());
+
+ if( GRFILTER_FORMAT_DONTKNOW != nFilter )
+ aFilterName = rFilter.GetImportFormatName( nFilter );
+ }
+ }
+
+ return aFilterName;
+}
+
+IMPL_LINK_NOARG(GalleryBrowser2, SelectObjectValueSetHdl, ValueSet*, void)
+{
+ ImplUpdateSelection();
+}
+
+IMPL_LINK_NOARG(GalleryBrowser2, SelectObjectHdl, weld::TreeView&, void)
+{
+ ImplUpdateSelection();
+}
+
+IMPL_LINK(GalleryBrowser2, SelectTbxHdl, weld::Toggleable&, rBox, void)
+{
+ if (&rBox == mxIconButton.get())
+ SetMode(rBox.get_active() ? GALLERYBROWSERMODE_ICON : GALLERYBROWSERMODE_LIST);
+ else if (&rBox == mxListButton.get())
+ SetMode(rBox.get_active() ? GALLERYBROWSERMODE_LIST : GALLERYBROWSERMODE_ICON);
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/gallery2/galctrl.cxx b/svx/source/gallery2/galctrl.cxx
new file mode 100644
index 000000000..ae9de1ce7
--- /dev/null
+++ b/svx/source/gallery2/galctrl.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 <config_features.h>
+
+#include <sfx2/viewfrm.hxx>
+#include <sfx2/dispatch.hxx>
+#include <sfx2/sfxsids.hrc>
+#include <avmedia/mediaplayer.hxx>
+#include <helpids.h>
+#include <galbrws2.hxx>
+#include <svx/galtheme.hxx>
+#include <svx/galmisc.hxx>
+#include <svx/galctrl.hxx>
+#include <galobj.hxx>
+#include <avmedia/mediawindow.hxx>
+#include <vcl/event.hxx>
+#include <vcl/commandevent.hxx>
+#include <vcl/graphicfilter.hxx>
+#include <bitmaps.hlst>
+
+GalleryPreview::GalleryPreview(GalleryBrowser2* pParent, std::unique_ptr<weld::ScrolledWindow> xScrolledWindow)
+ : mxScrolledWindow(std::move(xScrolledWindow))
+ , mpParent(pParent)
+ , mpTheme(nullptr)
+{
+}
+
+void GalleryPreview::Show()
+{
+ mxScrolledWindow->show();
+ weld::CustomWidgetController::Show();
+}
+
+void GalleryPreview::Hide()
+{
+ weld::CustomWidgetController::Hide();
+ mxScrolledWindow->hide();
+}
+
+GalleryPreview::~GalleryPreview()
+{
+}
+
+void GalleryPreview::SetDrawingArea(weld::DrawingArea* pDrawingArea)
+{
+ CustomWidgetController::SetDrawingArea(pDrawingArea);
+ Size aSize = pDrawingArea->get_ref_device().LogicToPixel(Size(70, 88), MapMode(MapUnit::MapAppFont));
+ pDrawingArea->set_size_request(aSize.Width(), aSize.Height());
+ SetOutputSizePixel(aSize);
+
+ pDrawingArea->set_help_id(HID_GALLERY_WINDOW);
+
+ mxDragDropTargetHelper.reset(new GalleryDragDrop(mpParent, pDrawingArea->get_drop_target()));
+}
+
+namespace
+{
+ bool ImplGetGraphicCenterRect(const weld::CustomWidgetController& rWidget, const Graphic& rGraphic, tools::Rectangle& rResultRect)
+ {
+ const Size aWinSize(rWidget.GetOutputSizePixel());
+ Size aNewSize(rWidget.GetDrawingArea()->get_ref_device().LogicToPixel(rGraphic.GetPrefSize(), rGraphic.GetPrefMapMode()));
+ bool bRet = false;
+
+ if( aNewSize.Width() && aNewSize.Height() )
+ {
+ // scale to fit window
+ const double fGrfWH = static_cast<double>(aNewSize.Width()) / aNewSize.Height();
+ const double fWinWH = static_cast<double>(aWinSize.Width()) / aWinSize.Height();
+
+ if ( fGrfWH < fWinWH )
+ {
+ aNewSize.setWidth( static_cast<tools::Long>( aWinSize.Height() * fGrfWH ) );
+ aNewSize.setHeight( aWinSize.Height() );
+ }
+ else
+ {
+ aNewSize.setWidth( aWinSize.Width() );
+ aNewSize.setHeight( static_cast<tools::Long>( aWinSize.Width() / fGrfWH) );
+ }
+
+ const Point aNewPos( ( aWinSize.Width() - aNewSize.Width() ) >> 1,
+ ( aWinSize.Height() - aNewSize.Height() ) >> 1 );
+
+ rResultRect = tools::Rectangle( aNewPos, aNewSize );
+ bRet = true;
+ }
+
+ return bRet;
+ }
+}
+
+bool GalleryPreview::ImplGetGraphicCenterRect( const Graphic& rGraphic, tools::Rectangle& rResultRect ) const
+{
+ return ::ImplGetGraphicCenterRect(*this, rGraphic, rResultRect);
+}
+
+void GalleryPreview::Paint(vcl::RenderContext& rRenderContext, const tools::Rectangle& /*rRect*/)
+{
+ rRenderContext.SetBackground(Wallpaper(GALLERY_BG_COLOR));
+ rRenderContext.Erase();
+
+ if (ImplGetGraphicCenterRect(aGraphicObj.GetGraphic(), aPreviewRect))
+ {
+ const Point aPos( aPreviewRect.TopLeft() );
+ const Size aSize( aPreviewRect.GetSize() );
+
+ if( aGraphicObj.IsAnimated() )
+ aGraphicObj.StartAnimation(rRenderContext, aPos, aSize);
+ else
+ aGraphicObj.Draw(rRenderContext, aPos, aSize);
+ }
+}
+
+bool GalleryPreview::MouseButtonDown(const MouseEvent& rMEvt)
+{
+ if (mpTheme && (rMEvt.GetClicks() == 2))
+ mpParent->TogglePreview();
+ return true;
+}
+
+bool GalleryPreview::Command(const CommandEvent& rCEvt)
+{
+ if (mpTheme && (rCEvt.GetCommand() == CommandEventId::ContextMenu))
+ {
+ mpParent->ShowContextMenu(rCEvt);
+ return true;
+ }
+ return false;
+}
+
+bool GalleryPreview::KeyInput(const KeyEvent& rKEvt)
+{
+ if(mpTheme)
+ {
+ GalleryBrowser2* pBrowser = mpParent;
+
+ switch( rKEvt.GetKeyCode().GetCode() )
+ {
+ case KEY_BACKSPACE:
+ pBrowser->TogglePreview();
+ break;
+
+ case KEY_HOME:
+ pBrowser->Travel( GalleryBrowserTravel::First );
+ break;
+
+ case KEY_END:
+ pBrowser->Travel( GalleryBrowserTravel::Last );
+ break;
+
+ case KEY_LEFT:
+ case KEY_UP:
+ pBrowser->Travel( GalleryBrowserTravel::Previous );
+ break;
+
+ case KEY_RIGHT:
+ case KEY_DOWN:
+ pBrowser->Travel( GalleryBrowserTravel::Next );
+ break;
+
+ default:
+ {
+ if (!pBrowser->KeyInput(rKEvt))
+ return false;
+ }
+ break;
+ }
+
+ return true;
+ }
+ return false;
+}
+
+bool GalleryPreview::StartDrag()
+{
+ if (mpTheme)
+ return mpParent->StartDrag();
+ return true;
+}
+
+void GalleryPreview::PreviewMedia( const INetURLObject& rURL )
+{
+#if HAVE_FEATURE_AVMEDIA
+ if (rURL.GetProtocol() == INetProtocol::NotValid)
+ return;
+
+ ::avmedia::MediaFloater* pFloater = avmedia::getMediaFloater();
+
+ if (!pFloater)
+ {
+ if (SfxViewFrame* pViewFrm = SfxViewFrame::Current())
+ pViewFrm->GetBindings().GetDispatcher()->Execute( SID_AVMEDIA_PLAYER, SfxCallMode::SYNCHRON );
+ pFloater = avmedia::getMediaFloater();
+ }
+
+ if (pFloater)
+ pFloater->setURL( rURL.GetMainURL( INetURLObject::DecodeMechanism::Unambiguous ), "", true );
+#else
+ (void) rURL;
+#endif
+}
+
+DialogGalleryPreview::DialogGalleryPreview()
+{
+}
+
+void DialogGalleryPreview::SetDrawingArea(weld::DrawingArea* pDrawingArea)
+{
+ CustomWidgetController::SetDrawingArea(pDrawingArea);
+ Size aSize(pDrawingArea->get_ref_device().LogicToPixel(Size(70, 88), MapMode(MapUnit::MapAppFont)));
+ pDrawingArea->set_size_request(aSize.Width(), aSize.Height());
+ pDrawingArea->set_help_id(HID_GALLERY_WINDOW);
+}
+
+bool DialogGalleryPreview::SetGraphic( const INetURLObject& _aURL )
+{
+ bool bRet = true;
+ Graphic aGraphic;
+#if HAVE_FEATURE_AVMEDIA
+ if( ::avmedia::MediaWindow::isMediaURL( _aURL.GetMainURL( INetURLObject::DecodeMechanism::Unambiguous ), "" ) )
+ {
+ aGraphic = BitmapEx(RID_SVXBMP_GALLERY_MEDIA);
+ }
+ else
+#endif
+ {
+ GraphicFilter& rFilter = GraphicFilter::GetGraphicFilter();
+ GalleryProgress aProgress( &rFilter );
+ if( rFilter.ImportGraphic( aGraphic, _aURL ) )
+ bRet = false;
+ }
+
+ SetGraphic( aGraphic );
+ Invalidate();
+ return bRet;
+}
+
+bool DialogGalleryPreview::ImplGetGraphicCenterRect( const Graphic& rGraphic, tools::Rectangle& rResultRect ) const
+{
+ return ::ImplGetGraphicCenterRect(*this, rGraphic, rResultRect);
+}
+
+void DialogGalleryPreview::Paint(vcl::RenderContext& rRenderContext, const tools::Rectangle&)
+{
+ rRenderContext.SetBackground(Wallpaper(GALLERY_BG_COLOR));
+
+ if (ImplGetGraphicCenterRect(aGraphicObj.GetGraphic(), aPreviewRect))
+ {
+ const Point aPos( aPreviewRect.TopLeft() );
+ const Size aSize( aPreviewRect.GetSize() );
+
+ if( aGraphicObj.IsAnimated() )
+ aGraphicObj.StartAnimation(rRenderContext, aPos, aSize);
+ else
+ aGraphicObj.Draw(rRenderContext, aPos, aSize);
+ }
+}
+
+void GalleryIconView::drawTransparenceBackground(vcl::RenderContext& rOut, const Point& rPos, const Size& rSize)
+{
+ // draw checkered background
+ static const sal_uInt32 nLen(8);
+ static const Color aW(COL_WHITE);
+ static const Color aG(0xef, 0xef, 0xef);
+
+ rOut.DrawCheckered(rPos, rSize, nLen, aW, aG);
+}
+
+GalleryIconView::GalleryIconView(GalleryBrowser2* pParent, std::unique_ptr<weld::ScrolledWindow> xScrolledWindow)
+ : ValueSet(std::move(xScrolledWindow))
+ , mpParent(pParent)
+ , mpTheme(nullptr)
+{
+}
+
+GalleryIconView::~GalleryIconView()
+{
+}
+
+void GalleryIconView::SetDrawingArea(weld::DrawingArea* pDrawingArea)
+{
+ ValueSet::SetDrawingArea(pDrawingArea);
+
+ SetStyle(GetStyle() | WB_TABSTOP | WB_3DLOOK | WB_BORDER | WB_ITEMBORDER | WB_DOUBLEBORDER | WB_VSCROLL | WB_FLATVALUESET);
+ EnableFullItemMode( false );
+
+ SetHelpId( HID_GALLERY_WINDOW );
+ SetExtraSpacing( 2 );
+ SetItemWidth( S_THUMB + 6 );
+ SetItemHeight( S_THUMB + 6 );
+
+ mxDragDropTargetHelper.reset(new GalleryDragDrop(mpParent, pDrawingArea->get_drop_target()));
+}
+
+void GalleryIconView::UserDraw(const UserDrawEvent& rUDEvt)
+{
+ const sal_uInt16 nId = rUDEvt.GetItemId();
+
+ if (!nId || !mpTheme)
+ return;
+
+ const tools::Rectangle& rRect = rUDEvt.GetRect();
+ const Size aSize(rRect.GetWidth(), rRect.GetHeight());
+ BitmapEx aBitmapEx;
+ Size aPreparedSize;
+ OUString aItemTextTitle;
+ OUString aItemTextPath;
+
+ mpTheme->GetPreviewBitmapExAndStrings(nId - 1, aBitmapEx, aPreparedSize, aItemTextTitle, aItemTextPath);
+
+ bool bNeedToCreate(aBitmapEx.IsEmpty());
+
+ if (!bNeedToCreate && aItemTextTitle.isEmpty())
+ {
+ bNeedToCreate = true;
+ }
+
+ if (!bNeedToCreate && aPreparedSize != aSize)
+ {
+ bNeedToCreate = true;
+ }
+
+ if (bNeedToCreate)
+ {
+ std::unique_ptr<SgaObject> pObj = mpTheme->AcquireObject(nId - 1);
+
+ if(pObj)
+ {
+ aBitmapEx = pObj->createPreviewBitmapEx(aSize);
+ aItemTextTitle = GalleryBrowser2::GetItemText(*pObj, GalleryItemFlags::Title);
+
+ mpTheme->SetPreviewBitmapExAndStrings(nId - 1, aBitmapEx, aSize, aItemTextTitle, aItemTextPath);
+ }
+ }
+
+ if (!aBitmapEx.IsEmpty())
+ {
+ const Size aBitmapExSizePixel(aBitmapEx.GetSizePixel());
+ const Point aPos(
+ ((aSize.Width() - aBitmapExSizePixel.Width()) >> 1) + rRect.Left(),
+ ((aSize.Height() - aBitmapExSizePixel.Height()) >> 1) + rRect.Top());
+ OutputDevice* pDev = rUDEvt.GetRenderContext();
+
+ if(aBitmapEx.IsAlpha())
+ {
+ // draw checkered background for full rectangle.
+ GalleryIconView::drawTransparenceBackground(*pDev, rRect.TopLeft(), rRect.GetSize());
+ }
+
+ pDev->DrawBitmapEx(aPos, aBitmapEx);
+ }
+
+ SetItemText(nId, aItemTextTitle);
+}
+
+bool GalleryIconView::MouseButtonDown(const MouseEvent& rMEvt)
+{
+ bool bRet = ValueSet::MouseButtonDown(rMEvt);
+
+ if (rMEvt.GetClicks() == 2)
+ mpParent->TogglePreview();
+
+ return bRet;
+}
+
+bool GalleryIconView::Command(const CommandEvent& rCEvt)
+{
+ bool bRet = ValueSet::Command(rCEvt);
+
+ if (rCEvt.GetCommand() == CommandEventId::ContextMenu)
+ {
+ mpParent->ShowContextMenu(rCEvt);
+ }
+
+ return bRet;
+}
+
+bool GalleryIconView::KeyInput(const KeyEvent& rKEvt)
+{
+ if (!mpTheme || !mpParent->KeyInput(rKEvt))
+ return ValueSet::KeyInput(rKEvt);
+ return true;
+}
+
+bool GalleryIconView::StartDrag()
+{
+ Select();
+ return mpParent->StartDrag();
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/gallery2/galexpl.cxx b/svx/source/gallery2/galexpl.cxx
new file mode 100644
index 000000000..b1e70919c
--- /dev/null
+++ b/svx/source/gallery2/galexpl.cxx
@@ -0,0 +1,299 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * 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/gallery1.hxx>
+#include <svx/galtheme.hxx>
+#include <svx/gallery.hxx>
+#include <galobj.hxx>
+
+namespace
+{
+ SfxListener& theLockListener()
+ {
+ static SfxListener SINGLETON;
+ return SINGLETON;
+ }
+}
+
+
+bool GalleryExplorer::FillThemeList( std::vector<OUString>& rThemeList )
+{
+ Gallery* pGal = ::Gallery::GetGalleryInstance();
+
+ if( pGal )
+ {
+ for( sal_uInt32 i = 0, nCount = pGal->GetThemeCount(); i < nCount; i++ )
+ {
+ const GalleryThemeEntry* pEntry = pGal->GetThemeInfo( i );
+
+ if( pEntry && !pEntry->IsReadOnly() && !pEntry->IsHidden() )
+ rThemeList.push_back(pEntry->GetThemeName());
+ }
+ }
+
+ return !rThemeList.empty();
+}
+
+bool GalleryExplorer::FillObjList( std::u16string_view rThemeName, std::vector<OUString> &rObjList )
+{
+ Gallery* pGal = ::Gallery::GetGalleryInstance();
+
+ if( pGal )
+ {
+ SfxListener aListener;
+ GalleryTheme* pTheme = pGal->AcquireTheme( rThemeName, aListener );
+
+ if( pTheme )
+ {
+ for( sal_uInt32 i = 0, nCount = pTheme->GetObjectCount(); i < nCount; i++ )
+ rObjList.push_back( pTheme->GetObjectURL( i ).GetMainURL( INetURLObject::DecodeMechanism::NONE ) );
+
+ pGal->ReleaseTheme( pTheme, aListener );
+ }
+ }
+
+ return !rObjList.empty();
+}
+
+bool GalleryExplorer::FillObjList( const sal_uInt32 nThemeId, std::vector<OUString> &rObjList )
+{
+ Gallery* pGal = ::Gallery::GetGalleryInstance();
+
+ if (!pGal)
+ return false;
+
+ return FillObjList( pGal->GetThemeName( nThemeId ), rObjList );
+}
+
+bool GalleryExplorer::FillObjListTitle( const sal_uInt32 nThemeId, std::vector< OUString >& rList )
+{
+ Gallery* pGal = ::Gallery::GetGalleryInstance();
+ if( pGal )
+ {
+ SfxListener aListener;
+ GalleryTheme* pTheme = pGal->AcquireTheme( pGal->GetThemeName( nThemeId ), aListener );
+
+ if( pTheme )
+ {
+ for( sal_uInt32 i = 0, nCount = pTheme->GetObjectCount(); i < nCount; i++ )
+ {
+ std::unique_ptr<SgaObject> pObj = pTheme->AcquireObject( i );
+ if ( pObj )
+ {
+ OUString aTitle( pObj->GetTitle() );
+ rList.push_back( aTitle );
+ }
+ }
+ pGal->ReleaseTheme( pTheme, aListener );
+ }
+ }
+ return !rList.empty();
+}
+
+bool GalleryExplorer::InsertURL( std::u16string_view rThemeName, std::u16string_view rURL )
+{
+ Gallery* pGal = ::Gallery::GetGalleryInstance();
+ bool bRet = false;
+
+ if( pGal )
+ {
+ SfxListener aListener;
+ GalleryTheme* pTheme = pGal->AcquireTheme( rThemeName, aListener );
+
+ if( pTheme )
+ {
+ INetURLObject aURL( rURL );
+ DBG_ASSERT( aURL.GetProtocol() != INetProtocol::NotValid, "invalid URL" );
+ bRet = pTheme->InsertURL( aURL );
+ pGal->ReleaseTheme( pTheme, aListener );
+ }
+ }
+
+ return bRet;
+}
+
+bool GalleryExplorer::InsertURL( sal_uInt32 nThemeId, std::u16string_view rURL )
+{
+ Gallery* pGal = ::Gallery::GetGalleryInstance();
+ return pGal && InsertURL( pGal->GetThemeName( nThemeId ), rURL );
+}
+
+bool GalleryExplorer::GetGraphicObj( std::u16string_view rThemeName, sal_uInt32 nPos,
+ Graphic* pGraphic )
+{
+ Gallery* pGal = ::Gallery::GetGalleryInstance();
+ bool bRet = false;
+
+ if( pGal )
+ {
+ SfxListener aListener;
+ GalleryTheme* pTheme = pGal->AcquireTheme( rThemeName, aListener );
+
+ if( pTheme )
+ {
+ if( pGraphic )
+ bRet = bRet || pTheme->GetGraphic( nPos, *pGraphic );
+
+ pGal->ReleaseTheme( pTheme, aListener );
+ }
+ }
+
+ return bRet;
+}
+
+bool GalleryExplorer::GetGraphicObj( sal_uInt32 nThemeId, sal_uInt32 nPos,
+ Graphic* pGraphic )
+{
+ Gallery* pGal = ::Gallery::GetGalleryInstance();
+ return pGal && GetGraphicObj( pGal->GetThemeName( nThemeId ), nPos, pGraphic );
+}
+
+sal_uInt32 GalleryExplorer::GetSdrObjCount( std::u16string_view rThemeName )
+{
+ Gallery* pGal = ::Gallery::GetGalleryInstance();
+ sal_uInt32 nRet = 0;
+
+ if( pGal )
+ {
+ SfxListener aListener;
+ GalleryTheme* pTheme = pGal->AcquireTheme( rThemeName, aListener );
+
+ if( pTheme )
+ {
+ for( sal_uInt32 i = 0, nCount = pTheme->GetObjectCount(); i < nCount; i++ )
+ if( SgaObjKind::SvDraw == pTheme->GetObjectKind( i ) )
+ nRet++;
+
+ pGal->ReleaseTheme( pTheme, aListener );
+ }
+ }
+
+ return nRet;
+}
+
+sal_uInt32 GalleryExplorer::GetSdrObjCount( sal_uInt32 nThemeId )
+{
+ Gallery* pGal = ::Gallery::GetGalleryInstance();
+ return( pGal ? GetSdrObjCount( pGal->GetThemeName( nThemeId ) ) : 0 );
+}
+
+bool GalleryExplorer::GetSdrObj( std::u16string_view rThemeName, sal_uInt32 nSdrModelPos,
+ SdrModel* pModel, BitmapEx* pThumb )
+{
+ Gallery* pGal = ::Gallery::GetGalleryInstance();
+ bool bRet = false;
+
+ if( pGal )
+ {
+ SfxListener aListener;
+ GalleryTheme* pTheme = pGal->AcquireTheme( rThemeName, aListener );
+
+ if( pTheme )
+ {
+ for( sal_uInt32 i = 0, nCount = pTheme->GetObjectCount(), nActPos = 0; ( i < nCount ) && !bRet; i++ )
+ {
+ if( SgaObjKind::SvDraw == pTheme->GetObjectKind( i ) )
+ {
+ if( nActPos++ == nSdrModelPos )
+ {
+ if( pModel )
+ bRet = pTheme->GetModel(i, *pModel);
+
+ if( pThumb )
+ bRet = bRet || pTheme->GetThumb( i, *pThumb );
+ }
+ }
+ }
+
+ pGal->ReleaseTheme( pTheme, aListener );
+ }
+ }
+
+ return bRet;
+}
+
+bool GalleryExplorer::GetSdrObj( sal_uInt32 nThemeId, sal_uInt32 nSdrModelPos,
+ SdrModel* pModel, BitmapEx* pThumb )
+{
+ Gallery* pGal = ::Gallery::GetGalleryInstance();
+ return pGal && GetSdrObj( pGal->GetThemeName( nThemeId ), nSdrModelPos, pModel, pThumb );
+}
+
+bool GalleryExplorer::BeginLocking( std::u16string_view rThemeName )
+{
+ Gallery* pGal = ::Gallery::GetGalleryInstance();
+ bool bRet = false;
+
+ if( pGal )
+ {
+ GalleryTheme* pTheme = pGal->AcquireTheme( rThemeName, theLockListener() );
+
+ if( pTheme )
+ {
+ pTheme->LockTheme();
+ bRet = true;
+ }
+ }
+
+ return bRet;
+}
+
+bool GalleryExplorer::BeginLocking( sal_uInt32 nThemeId )
+{
+ Gallery* pGal = ::Gallery::GetGalleryInstance();
+ return pGal && BeginLocking( pGal->GetThemeName( nThemeId ) );
+}
+
+bool GalleryExplorer::EndLocking( std::u16string_view rThemeName )
+{
+ Gallery* pGal = ::Gallery::GetGalleryInstance();
+ bool bRet = false;
+
+ if( pGal )
+ {
+ SfxListener aListener;
+ GalleryTheme* pTheme = pGal->AcquireTheme( rThemeName, aListener );
+
+ if( pTheme )
+ {
+ const bool bReleaseLockedTheme = pTheme->UnlockTheme();
+
+ // release acquired theme
+ pGal->ReleaseTheme( pTheme, aListener );
+
+ if( bReleaseLockedTheme )
+ {
+ // release locked theme
+ pGal->ReleaseTheme( pTheme, theLockListener() );
+ bRet = true;
+ }
+ }
+ }
+
+ return bRet;
+}
+
+bool GalleryExplorer::EndLocking( sal_uInt32 nThemeId )
+{
+ Gallery* pGal = ::Gallery::GetGalleryInstance();
+ return pGal && EndLocking( pGal->GetThemeName( nThemeId ) );
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/gallery2/galini.cxx b/svx/source/gallery2/galini.cxx
new file mode 100644
index 000000000..8cdfb23ae
--- /dev/null
+++ b/svx/source/gallery2/galini.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/.
+ */
+
+/*
+ * The world's quickest and lamest .desktop / .ini file parser.
+ * Ideally the .thm file would move to a .desktop file in
+ * future.
+ */
+
+#include <sal/config.h>
+#include <sal/log.hxx>
+
+#include <unotools/ucbstreamhelper.hxx>
+#include <svx/gallerybinaryengineentry.hxx>
+#include <i18nlangtag/languagetag.hxx>
+#include <vcl/svapp.hxx>
+#include <vcl/settings.hxx>
+#include <o3tl/string_view.hxx>
+#include <memory>
+
+OUString GalleryBinaryEngineEntry::ReadStrFromIni(std::u16string_view aKeyName ) const
+{
+ std::unique_ptr<SvStream> pStrm(::utl::UcbStreamHelper::CreateStream(
+ GetStrURL().GetMainURL( INetURLObject::DecodeMechanism::NONE ),
+ StreamMode::READ ));
+
+ const LanguageTag &rLangTag = Application::GetSettings().GetUILanguageTag();
+
+ ::std::vector< OUString > aFallbacks = rLangTag.getFallbackStrings( true);
+
+ OUString aResult;
+ sal_Int32 nRank = 42;
+
+ if( pStrm )
+ {
+ OString aLine;
+ while( pStrm->ReadLine( aLine ) )
+ {
+ OUString aKey;
+ OUString aLocale;
+ OUString aValue;
+ sal_Int32 n;
+
+ // comments
+ if( aLine.startsWith( "#" ) )
+ continue;
+
+ // a[en_US] = Bob
+ if( ( n = aLine.indexOf( '=' ) ) >= 1)
+ {
+ aKey = OStringToOUString(
+ o3tl::trim(aLine.subView( 0, n )), RTL_TEXTENCODING_ASCII_US );
+ aValue = OStringToOUString(
+ o3tl::trim(aLine.subView( n + 1 )), RTL_TEXTENCODING_UTF8 );
+
+ if( ( n = aKey.indexOf( '[' ) ) >= 1 )
+ {
+ aLocale = o3tl::trim(aKey.subView( n + 1 ));
+ aKey = o3tl::trim(aKey.subView( 0, n ));
+ if( (n = aLocale.indexOf( ']' ) ) >= 1 )
+ aLocale = o3tl::trim(aLocale.subView( 0, n ));
+ }
+ }
+ SAL_INFO("svx", "ini file has '" << aKey << "' [ '" << aLocale << "' ] = '" << aValue << "'");
+
+ // grisly language matching, is this not available somewhere else?
+ if( aKey == aKeyName )
+ {
+ /* FIXME-BCP47: what is this supposed to do? */
+ n = 0;
+ OUString aLang = aLocale.replace('_','-');
+ for( const auto& rFallback : aFallbacks )
+ {
+ SAL_INFO( "svx", "compare '" << aLang << "' with '" << rFallback << "' rank " << nRank << " vs. " << n );
+ if( rFallback == aLang && n < nRank ) {
+ nRank = n; // try to get the most accurate match
+ aResult = aValue;
+ }
+ ++n;
+ }
+ }
+ }
+ }
+
+ SAL_INFO( "svx", "readStrFromIni returns '" << aResult << "'");
+ return aResult;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/gallery2/gallery1.cxx b/svx/source/gallery2/gallery1.cxx
new file mode 100644
index 000000000..3d412eacb
--- /dev/null
+++ b/svx/source/gallery2/gallery1.cxx
@@ -0,0 +1,724 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * 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>
+
+#if defined(MACOSX) && HAVE_FEATURE_READONLY_INSTALLSET
+#define __ASSERT_MACROS_DEFINE_VERSIONS_WITHOUT_UNDERSCORES 0
+#include <premac.h>
+#include <Foundation/Foundation.h>
+#include <postmac.h>
+#endif
+
+#include <sal/config.h>
+
+#include <comphelper/processfactory.hxx>
+#include <ucbhelper/content.hxx>
+#include <com/sun/star/ucb/ContentCreationException.hpp>
+#include <o3tl/string_view.hxx>
+#include <unotools/configmgr.hxx>
+#include <unotools/ucbstreamhelper.hxx>
+#include <unotools/pathoptions.hxx>
+#include <svx/dialmgr.hxx>
+#include <svx/gallery.hxx>
+#include <svx/galleryobjectcollection.hxx>
+#include <svx/strings.hrc>
+#include <strings.hxx>
+#include <svx/galmisc.hxx>
+#include <svx/galtheme.hxx>
+#include <svx/gallery1.hxx>
+#include <svx/gallerybinaryengineentry.hxx>
+#include <vcl/weld.hxx>
+#include <com/sun/star/sdbc/XResultSet.hpp>
+#include <com/sun/star/ucb/XContentAccess.hpp>
+#include <memory>
+
+
+using namespace ::com::sun::star;
+
+
+const std::pair<sal_uInt16, rtl::OUStringConstExpr> aUnlocalized[] =
+{
+ { GALLERY_THEME_HOMEPAGE, RID_GALLERYSTR_THEME_HTMLBUTTONS },
+ { GALLERY_THEME_POWERPOINT, RID_GALLERYSTR_THEME_POWERPOINT },
+ { GALLERY_THEME_USERSOUNDS, RID_GALLERYSTR_THEME_USERSOUNDS },
+ { GALLERY_THEME_DUMMY5, RID_GALLERYSTR_THEME_DUMMY5 },
+ { GALLERY_THEME_RULERS, RID_GALLERYSTR_THEME_RULERS },
+ { GALLERY_THEME_FONTWORK, RID_GALLERYSTR_THEME_FONTWORK },
+ { GALLERY_THEME_FONTWORK_VERTICAL, RID_GALLERYSTR_THEME_FONTWORK_VERTICAL }
+};
+
+const std::pair<sal_uInt16, TranslateId> aLocalized[] =
+{
+ { RID_GALLERY_THEME_3D, RID_GALLERYSTR_THEME_3D },
+ { RID_GALLERY_THEME_ANIMATIONS, RID_GALLERYSTR_THEME_ANIMATIONS },
+ { RID_GALLERY_THEME_BULLETS, RID_GALLERYSTR_THEME_BULLETS },
+ { RID_GALLERY_THEME_OFFICE, RID_GALLERYSTR_THEME_OFFICE },
+ { RID_GALLERY_THEME_FLAGS, RID_GALLERYSTR_THEME_FLAGS },
+ { RID_GALLERY_THEME_FLOWCHARTS, RID_GALLERYSTR_THEME_FLOWCHARTS },
+ { RID_GALLERY_THEME_EMOTICONS, RID_GALLERYSTR_THEME_EMOTICONS },
+ { RID_GALLERY_THEME_PHOTOS, RID_GALLERYSTR_THEME_PHOTOS },
+ { RID_GALLERY_THEME_BACKGROUNDS, RID_GALLERYSTR_THEME_BACKGROUNDS },
+ { RID_GALLERY_THEME_HOMEPAGE, RID_GALLERYSTR_THEME_HOMEPAGE },
+ { RID_GALLERY_THEME_INTERACTION, RID_GALLERYSTR_THEME_INTERACTION },
+ { RID_GALLERY_THEME_MAPS, RID_GALLERYSTR_THEME_MAPS },
+ { RID_GALLERY_THEME_PEOPLE, RID_GALLERYSTR_THEME_PEOPLE },
+ { RID_GALLERY_THEME_SURFACES, RID_GALLERYSTR_THEME_SURFACES },
+ { RID_GALLERY_THEME_SOUNDS, RID_GALLERYSTR_THEME_SOUNDS },
+ { RID_GALLERY_THEME_SYMBOLS, RID_GALLERYSTR_THEME_SYMBOLS },
+ { RID_GALLERY_THEME_MYTHEME, RID_GALLERYSTR_THEME_MYTHEME },
+
+ { RID_GALLERY_THEME_ARROWS, RID_GALLERYSTR_THEME_ARROWS },
+ { RID_GALLERY_THEME_BALLOONS, RID_GALLERYSTR_THEME_BALLOONS },
+ { RID_GALLERY_THEME_KEYBOARD, RID_GALLERYSTR_THEME_KEYBOARD },
+ { RID_GALLERY_THEME_TIME, RID_GALLERYSTR_THEME_TIME },
+ { RID_GALLERY_THEME_PRESENTATION, RID_GALLERYSTR_THEME_PRESENTATION },
+ { RID_GALLERY_THEME_CALENDAR, RID_GALLERYSTR_THEME_CALENDAR },
+ { RID_GALLERY_THEME_NAVIGATION, RID_GALLERYSTR_THEME_NAVIGATION },
+ { RID_GALLERY_THEME_COMMUNICATION, RID_GALLERYSTR_THEME_COMMUNICATION },
+ { RID_GALLERY_THEME_FINANCES, RID_GALLERYSTR_THEME_FINANCES },
+ { RID_GALLERY_THEME_COMPUTER, RID_GALLERYSTR_THEME_COMPUTER },
+
+ { RID_GALLERY_THEME_CLIMA, RID_GALLERYSTR_THEME_CLIMA },
+ { RID_GALLERY_THEME_EDUCATION, RID_GALLERYSTR_THEME_EDUCATION },
+ { RID_GALLERY_THEME_TROUBLE, RID_GALLERYSTR_THEME_TROUBLE },
+ { RID_GALLERY_THEME_SCREENBEANS, RID_GALLERYSTR_THEME_SCREENBEANS },
+
+ { RID_GALLERY_THEME_COMPUTERS, RID_GALLERYSTR_THEME_COMPUTERS },
+ { RID_GALLERY_THEME_DIAGRAMS, RID_GALLERYSTR_THEME_DIAGRAMS },
+ { RID_GALLERY_THEME_ENVIRONMENT, RID_GALLERYSTR_THEME_ENVIRONMENT },
+ { RID_GALLERY_THEME_FINANCE, RID_GALLERYSTR_THEME_FINANCE },
+ { RID_GALLERY_THEME_TRANSPORT, RID_GALLERYSTR_THEME_TRANSPORT },
+ { RID_GALLERY_THEME_TXTSHAPES, RID_GALLERYSTR_THEME_TXTSHAPES }
+};
+
+GalleryThemeEntry::GalleryThemeEntry( bool bCreateUniqueURL,
+ const INetURLObject& rBaseURL, const OUString& rName,
+ bool _bReadOnly, bool _bNewFile,
+ sal_uInt32 _nId, bool _bThemeNameFromResource ) :
+ nId ( _nId ),
+ bReadOnly ( _bReadOnly ),
+ bThemeNameFromResource ( _bThemeNameFromResource )
+{
+ INetURLObject aURL( rBaseURL );
+ DBG_ASSERT( aURL.GetProtocol() != INetProtocol::NotValid, "invalid URL" );
+
+ if (bCreateUniqueURL)
+ {
+ GalleryBinaryEngineEntry::CreateUniqueURL(rBaseURL,aURL);
+ }
+
+ mpGalleryStorageEngineEntry = std::make_unique<GalleryBinaryEngineEntry>();
+ setStorageLocations(aURL);
+
+ SetModified( _bNewFile );
+
+ aName = mpGalleryStorageEngineEntry->ReadStrFromIni( u"name" );
+
+ // This is awful - we shouldn't use these resources if we
+ // possibly can avoid them
+ if( aName.isEmpty() && nId && bThemeNameFromResource )
+ {
+ //some of these are supposed to *not* be localized
+ //so catch them before looking up the resource
+ for (size_t i = 0; i < SAL_N_ELEMENTS(aUnlocalized); ++i)
+ {
+ if (aUnlocalized[i].first == nId)
+ {
+ aName = aUnlocalized[i].second;
+ break;
+ }
+ }
+ //look up the rest of the ids in string resources
+ if (aName.isEmpty())
+ {
+ for (size_t i = 0; i < SAL_N_ELEMENTS(aLocalized); ++i)
+ {
+ if (aLocalized[i].first == nId)
+ {
+ aName = SvxResId(aLocalized[i].second);
+ break;
+ }
+ }
+ }
+ }
+
+ if( aName.isEmpty() )
+ aName = rName;
+}
+
+void GalleryThemeEntry::setStorageLocations(INetURLObject & rURL)
+{
+ mpGalleryStorageEngineEntry->setStorageLocations(rURL);
+}
+
+GalleryTheme* GalleryThemeEntry::createGalleryTheme(Gallery* pGallery)
+{
+ return new GalleryTheme(pGallery,this);
+}
+
+std::unique_ptr<GalleryBinaryEngine> GalleryThemeEntry::createGalleryStorageEngine(GalleryObjectCollection& mrGalleryObjectCollection)
+{
+ return mpGalleryStorageEngineEntry->createGalleryStorageEngine(mrGalleryObjectCollection, bReadOnly);
+}
+
+void GalleryTheme::InsertAllThemes(weld::ComboBox& rListBox)
+{
+ for (size_t i = 0; i < SAL_N_ELEMENTS(aUnlocalized); ++i)
+ rListBox.append_text(aUnlocalized[i].second);
+
+ for (size_t i = 0; i < SAL_N_ELEMENTS(aLocalized); ++i)
+ rListBox.append_text(SvxResId(aLocalized[i].second));
+}
+
+void GalleryThemeEntry::SetName( const OUString& rNewName )
+{
+ if( aName != rNewName )
+ {
+ aName = rNewName;
+ SetModified( true );
+ bThemeNameFromResource = false;
+ }
+}
+
+void GalleryThemeEntry::SetId( sal_uInt32 nNewId, bool bResetThemeName )
+{
+ nId = nNewId;
+ SetModified( true );
+ bThemeNameFromResource = ( nId && bResetThemeName );
+}
+
+void GalleryThemeEntry::removeTheme()
+{
+ mpGalleryStorageEngineEntry->removeTheme();
+}
+
+class GalleryThemeCacheEntry
+{
+private:
+
+ const GalleryThemeEntry* mpThemeEntry;
+ std::unique_ptr<GalleryTheme> mpTheme;
+
+public:
+
+ GalleryThemeCacheEntry( const GalleryThemeEntry* pThemeEntry, std::unique_ptr<GalleryTheme> pTheme ) :
+ mpThemeEntry( pThemeEntry ), mpTheme( std::move(pTheme) ) {}
+
+ const GalleryThemeEntry* GetThemeEntry() const { return mpThemeEntry; }
+ GalleryTheme* GetTheme() const { return mpTheme.get(); }
+};
+
+
+Gallery::Gallery( std::u16string_view rMultiPath )
+: bMultiPath ( false )
+{
+ ImplLoad( rMultiPath );
+}
+
+Gallery::~Gallery()
+{
+}
+
+Gallery* Gallery::GetGalleryInstance()
+{
+ // note: this would deadlock if it used osl::Mutex::getGlobalMutex()
+ static Gallery *const s_pGallery(
+ utl::ConfigManager::IsFuzzing() ? nullptr :
+ new Gallery(SvtPathOptions().GetGalleryPath()));
+
+ return s_pGallery;
+}
+
+void Gallery::ImplLoad( std::u16string_view rMultiPath )
+{
+ bool bIsReadOnlyDir {false};
+
+ bMultiPath = !rMultiPath.empty();
+
+ INetURLObject aCurURL(SvtPathOptions().GetConfigPath());
+ ImplLoadSubDirs( aCurURL, bIsReadOnlyDir );
+
+ if( !bIsReadOnlyDir )
+ aUserURL = aCurURL;
+
+ if( bMultiPath )
+ {
+ bool bIsRelURL {true};
+ sal_Int32 nIdx {0};
+ do
+ {
+ aCurURL = INetURLObject(o3tl::getToken(rMultiPath, 0, ';', nIdx));
+ if (bIsRelURL)
+ {
+ aRelURL = aCurURL;
+ bIsRelURL = false;
+ }
+
+ ImplLoadSubDirs( aCurURL, bIsReadOnlyDir );
+
+ if( !bIsReadOnlyDir )
+ aUserURL = aCurURL;
+ }
+ while (nIdx>0);
+ }
+ else
+ aRelURL = INetURLObject( rMultiPath );
+
+ DBG_ASSERT( aUserURL.GetProtocol() != INetProtocol::NotValid, "no writable Gallery user directory available" );
+ DBG_ASSERT( aRelURL.GetProtocol() != INetProtocol::NotValid, "invalid URL" );
+}
+
+void Gallery::ImplLoadSubDirs( const INetURLObject& rBaseURL, bool& rbDirIsReadOnly )
+{
+ rbDirIsReadOnly = false;
+
+ try
+ {
+ uno::Reference< ucb::XCommandEnvironment > xEnv;
+ ::ucbhelper::Content aCnt( rBaseURL.GetMainURL( INetURLObject::DecodeMechanism::NONE ), xEnv, comphelper::getProcessComponentContext() );
+
+ uno::Sequence<OUString> aProps { "Url" };
+
+ uno::Reference< sdbc::XResultSet > xResultSet( aCnt.createCursor( aProps, ::ucbhelper::INCLUDE_DOCUMENTS_ONLY ) );
+
+#if defined(MACOSX) && HAVE_FEATURE_READONLY_INSTALLSET
+ if( rBaseURL.GetProtocol() == INetProtocol::File )
+ {
+ const char *appBundle = [[[NSBundle mainBundle] bundlePath] UTF8String];
+ OUString path = rBaseURL.GetURLPath();
+ if( path.startsWith( OUStringConcatenation(OUString( appBundle, strlen( appBundle ), RTL_TEXTENCODING_UTF8 ) + "/") ) )
+ rbDirIsReadOnly = true;
+ }
+#else
+ try
+ {
+ // check readonlyness the very hard way
+ INetURLObject aTestURL( rBaseURL );
+
+ aTestURL.Append( u"cdefghij.klm" );
+ std::unique_ptr<SvStream> pTestStm(::utl::UcbStreamHelper::CreateStream( aTestURL.GetMainURL( INetURLObject::DecodeMechanism::NONE ), StreamMode::WRITE ));
+
+ if( pTestStm )
+ {
+ pTestStm->WriteInt32( sal_Int32(1) );
+
+ if( pTestStm->GetError() )
+ rbDirIsReadOnly = true;
+
+ pTestStm.reset();
+ KillFile( aTestURL );
+ }
+ else
+ rbDirIsReadOnly = true;
+ }
+ catch( const ucb::ContentCreationException& )
+ {
+ }
+ catch( const uno::RuntimeException& )
+ {
+ }
+ catch( const uno::Exception& )
+ {
+ }
+#endif
+ if( xResultSet.is() )
+ {
+ uno::Reference< ucb::XContentAccess > xContentAccess( xResultSet, uno::UNO_QUERY );
+
+ if( xContentAccess.is() )
+ {
+ static constexpr OUStringLiteral s_sTitle = u"Title";
+ static constexpr OUStringLiteral s_sIsReadOnly = u"IsReadOnly";
+
+ while( xResultSet->next() )
+ {
+ INetURLObject aThmURL( xContentAccess->queryContentIdentifierString() );
+
+ if (aThmURL.GetFileExtension().equalsIgnoreAsciiCase("thm"))
+ {
+ INetURLObject aSdgURL( aThmURL); aSdgURL.SetExtension( u"sdg" );
+ INetURLObject aSdvURL( aThmURL ); aSdvURL.SetExtension( u"sdv" );
+
+ try
+ {
+ ::ucbhelper::Content aThmCnt( aThmURL.GetMainURL( INetURLObject::DecodeMechanism::NONE ), xEnv, comphelper::getProcessComponentContext() );
+ ::ucbhelper::Content aSdgCnt( aSdgURL.GetMainURL( INetURLObject::DecodeMechanism::NONE ), xEnv, comphelper::getProcessComponentContext() );
+ ::ucbhelper::Content aSdvCnt( aSdvURL.GetMainURL( INetURLObject::DecodeMechanism::NONE ), xEnv, comphelper::getProcessComponentContext() );
+
+ OUString aTitle;
+ try
+ {
+ aThmCnt.getPropertyValue( s_sTitle ) >>= aTitle;
+ }
+ catch( const uno::RuntimeException& )
+ {
+ }
+ catch( const uno::Exception& )
+ {
+ }
+
+ if( !aTitle.isEmpty() )
+ {
+ bool bReadOnly = false;
+
+ try
+ {
+ aThmCnt.getPropertyValue( s_sIsReadOnly ) >>= bReadOnly;
+ }
+ catch( const uno::RuntimeException& )
+ {
+ }
+ catch( const uno::Exception& )
+ {
+ }
+
+ if( !bReadOnly )
+ {
+ try
+ {
+ aSdgCnt.getPropertyValue( s_sTitle ) >>= aTitle;
+ }
+ catch( const css::uno::RuntimeException& )
+ {
+ }
+ catch( const css::uno::Exception& )
+ {
+ }
+
+ if( !aTitle.isEmpty() )
+ {
+ try
+ {
+ aSdgCnt.getPropertyValue( s_sIsReadOnly ) >>= bReadOnly;
+ }
+ catch( const uno::RuntimeException& )
+ {
+ }
+ catch( const uno::Exception& )
+ {
+ }
+ }
+ }
+
+ if( !bReadOnly )
+ {
+ try
+ {
+ aSdvCnt.getPropertyValue( s_sTitle ) >>= aTitle;
+ }
+ catch( const css::uno::RuntimeException& )
+ {
+ }
+ catch( const css::uno::Exception& )
+ {
+ }
+
+ if( !aTitle.isEmpty() )
+ {
+ try
+ {
+ aSdvCnt.getPropertyValue( s_sIsReadOnly ) >>= bReadOnly;
+ }
+ catch( const uno::RuntimeException& )
+ {
+ }
+ catch( const uno::Exception& )
+ {
+ }
+ }
+ }
+
+ GalleryThemeEntry* pEntry = GalleryBinaryEngineEntry::CreateThemeEntry( aThmURL, rbDirIsReadOnly || bReadOnly );
+
+ if( pEntry )
+ aThemeList.emplace_back( pEntry );
+ }
+ }
+ catch( const ucb::ContentCreationException& )
+ {
+ }
+ catch( const uno::RuntimeException& )
+ {
+ }
+ catch( const uno::Exception& )
+ {
+ }
+ }
+ }
+ }
+ }
+ }
+ catch( const ucb::ContentCreationException& )
+ {
+ }
+ catch( const uno::RuntimeException& )
+ {
+ }
+ catch( const uno::Exception& )
+ {
+ }
+}
+
+GalleryThemeEntry* Gallery::ImplGetThemeEntry( std::u16string_view rThemeName )
+{
+ if( !rThemeName.empty() )
+ {
+ for ( size_t i = 0, n = aThemeList.size(); i < n; ++i )
+ if( rThemeName == aThemeList[ i ]->GetThemeName() )
+ return aThemeList[ i ].get();
+ }
+
+ return nullptr;
+}
+
+OUString Gallery::GetThemeName( sal_uInt32 nThemeId ) const
+{
+ GalleryThemeEntry* pFound = nullptr;
+
+ for ( size_t i = 0, n = aThemeList.size(); i < n && !pFound; ++i )
+ {
+ GalleryThemeEntry* pEntry = aThemeList[ i ].get();
+ if( nThemeId == pEntry->GetId() )
+ pFound = pEntry;
+ }
+
+ // try fallback, if no entry was found
+ if( !pFound )
+ {
+ OUString aFallback;
+
+ switch( nThemeId )
+ {
+ case GALLERY_THEME_3D:
+ aFallback = SvxResId(RID_GALLERYSTR_THEME_3D);
+ break;
+ case GALLERY_THEME_BULLETS:
+ aFallback = SvxResId(RID_GALLERYSTR_THEME_BULLETS);
+ break;
+ case GALLERY_THEME_HOMEPAGE:
+ aFallback = SvxResId(RID_GALLERYSTR_THEME_HOMEPAGE);
+ break;
+ case GALLERY_THEME_POWERPOINT:
+ aFallback = RID_GALLERYSTR_THEME_POWERPOINT;
+ break;
+ case GALLERY_THEME_FONTWORK:
+ aFallback = RID_GALLERYSTR_THEME_FONTWORK;
+ break;
+ case GALLERY_THEME_FONTWORK_VERTICAL:
+ aFallback = RID_GALLERYSTR_THEME_FONTWORK_VERTICAL;
+ break;
+ case GALLERY_THEME_SOUNDS:
+ aFallback = SvxResId(RID_GALLERYSTR_THEME_SOUNDS);
+ break;
+ case RID_GALLERY_THEME_ARROWS:
+ aFallback = SvxResId(RID_GALLERYSTR_THEME_ARROWS);
+ break;
+ case RID_GALLERY_THEME_COMPUTERS:
+ aFallback = SvxResId(RID_GALLERYSTR_THEME_COMPUTERS);
+ break;
+ case RID_GALLERY_THEME_DIAGRAMS:
+ aFallback = SvxResId(RID_GALLERYSTR_THEME_DIAGRAMS);
+ break;
+ case RID_GALLERY_THEME_EDUCATION:
+ aFallback = SvxResId(RID_GALLERYSTR_THEME_EDUCATION);
+ break;
+ case RID_GALLERY_THEME_ENVIRONMENT:
+ aFallback = SvxResId(RID_GALLERYSTR_THEME_ENVIRONMENT);
+ break;
+ case RID_GALLERY_THEME_FINANCE:
+ aFallback = SvxResId(RID_GALLERYSTR_THEME_FINANCE);
+ break;
+ case RID_GALLERY_THEME_PEOPLE:
+ aFallback = SvxResId(RID_GALLERYSTR_THEME_PEOPLE);
+ break;
+ case RID_GALLERY_THEME_SYMBOLS:
+ aFallback = SvxResId(RID_GALLERYSTR_THEME_SYMBOLS);
+ break;
+ case RID_GALLERY_THEME_TRANSPORT:
+ aFallback = SvxResId(RID_GALLERYSTR_THEME_TRANSPORT);
+ break;
+ case RID_GALLERY_THEME_TXTSHAPES:
+ aFallback = SvxResId(RID_GALLERYSTR_THEME_TXTSHAPES);
+ break;
+ default:
+ break;
+ }
+
+ pFound = const_cast<Gallery*>(this)->ImplGetThemeEntry(aFallback);
+ }
+
+ return( pFound ? pFound->GetThemeName() : OUString() );
+}
+
+bool Gallery::HasTheme( std::u16string_view rThemeName )
+{
+ return( ImplGetThemeEntry( rThemeName ) != nullptr );
+}
+
+bool Gallery::CreateTheme( const OUString& rThemeName )
+{
+ bool bRet = false;
+
+ if( !HasTheme( rThemeName ) && ( GetUserURL().GetProtocol() != INetProtocol::NotValid ) )
+ {
+ INetURLObject aURL( GetUserURL() );
+ aURL.Append( rThemeName );
+ GalleryThemeEntry* pNewEntry = new GalleryThemeEntry(
+ true, aURL, rThemeName,
+ false, true, 0, false );
+
+ aThemeList.emplace_back( pNewEntry );
+ delete pNewEntry->createGalleryTheme( this );
+ Broadcast( GalleryHint( GalleryHintType::THEME_CREATED, rThemeName ) );
+ bRet = true;
+ }
+
+ return bRet;
+}
+
+void Gallery::RenameTheme( const OUString& rOldName, const OUString& rNewName )
+{
+ GalleryThemeEntry* pThemeEntry = ImplGetThemeEntry( rOldName );
+
+ // check if the new theme name is already present
+ if( !pThemeEntry || HasTheme( rNewName ) || pThemeEntry->IsReadOnly() )
+ return;
+
+ SfxListener aListener;
+ GalleryTheme* pThm = AcquireTheme( rOldName, aListener );
+
+ if( pThm )
+ {
+ pThemeEntry->SetName( rNewName );
+ if (pThm->pThm->IsModified())
+ if (!pThm->mpGalleryStorageEngine->implWrite(*pThm, pThm->pThm))
+ pThm->ImplSetModified(false);
+
+ Broadcast( GalleryHint( GalleryHintType::THEME_RENAMED, rOldName, pThm->GetName() ) );
+ ReleaseTheme( pThm, aListener );
+ }
+}
+
+bool Gallery::RemoveTheme( const OUString& rThemeName )
+{
+ GalleryThemeEntry* pThemeEntry = ImplGetThemeEntry( rThemeName );
+ bool bRet = false;
+
+ if( pThemeEntry && !pThemeEntry->IsReadOnly() )
+ {
+ Broadcast( GalleryHint( GalleryHintType::CLOSE_THEME, rThemeName ) );
+
+ SfxListener aListener;
+ GalleryTheme* pThm = AcquireTheme( rThemeName, aListener );
+
+ if( pThm )
+ {
+ ReleaseTheme(pThm, aListener);
+ pThemeEntry->removeTheme();
+ }
+
+ auto it = std::find_if(aThemeList.begin(), aThemeList.end(),
+ [&pThemeEntry](const std::unique_ptr<GalleryThemeEntry>& rpEntry) { return pThemeEntry == rpEntry.get(); });
+ if (it != aThemeList.end())
+ aThemeList.erase( it );
+
+ Broadcast( GalleryHint( GalleryHintType::THEME_REMOVED, rThemeName ) );
+
+ bRet = true;
+ }
+
+ return bRet;
+}
+
+std::unique_ptr<GalleryTheme> GalleryThemeEntry::getCachedTheme(Gallery* pGallery)
+{
+ std::unique_ptr<GalleryTheme> pNewTheme;
+ pNewTheme.reset(createGalleryTheme(pGallery));
+ mpGalleryStorageEngineEntry->getCachedTheme(pNewTheme);
+ return pNewTheme;
+}
+
+GalleryTheme* Gallery::ImplGetCachedTheme(GalleryThemeEntry* pThemeEntry)
+{
+ GalleryTheme* pTheme = nullptr;
+
+ if( pThemeEntry )
+ {
+ auto it = std::find_if(aThemeCache.begin(), aThemeCache.end(),
+ [&pThemeEntry](const GalleryThemeCacheEntry* pEntry) { return pThemeEntry == pEntry->GetThemeEntry(); });
+ if (it != aThemeCache.end())
+ pTheme = (*it)->GetTheme();
+
+ if( !pTheme )
+ {
+ std::unique_ptr<GalleryTheme> pNewTheme = pThemeEntry->getCachedTheme(this);
+ if (pNewTheme)
+ {
+ aThemeCache.push_back( new GalleryThemeCacheEntry( pThemeEntry, std::move(pNewTheme) ));
+ pTheme = aThemeCache.back()->GetTheme();
+ }
+ }
+ }
+
+ return pTheme;
+}
+
+void Gallery::ImplDeleteCachedTheme( GalleryTheme const * pTheme )
+{
+ auto it = std::find_if(aThemeCache.begin(), aThemeCache.end(),
+ [&pTheme](const GalleryThemeCacheEntry* pEntry) { return pTheme == pEntry->GetTheme(); });
+ if (it != aThemeCache.end())
+ {
+ delete *it;
+ aThemeCache.erase(it);
+ }
+}
+
+GalleryTheme* Gallery::AcquireTheme( std::u16string_view rThemeName, SfxListener& rListener )
+{
+ GalleryTheme* pTheme = nullptr;
+ GalleryThemeEntry* pThemeEntry = ImplGetThemeEntry( rThemeName );
+
+ if( pThemeEntry )
+ {
+ pTheme = ImplGetCachedTheme( pThemeEntry );
+ if (pTheme)
+ rListener.StartListening(*pTheme, DuplicateHandling::Prevent);
+ }
+ return pTheme;
+}
+
+void Gallery::ReleaseTheme( GalleryTheme* pTheme, SfxListener& rListener )
+{
+ if( pTheme )
+ {
+ rListener.EndListening( *pTheme );
+
+ if( !pTheme->HasListeners() )
+ ImplDeleteCachedTheme( pTheme );
+ }
+}
+
+bool GalleryThemeEntry::IsDefault() const
+{
+ return nId > 0 && nId != GALLERY_THEME_MYTHEME;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/gallery2/gallerybinaryengine.cxx b/svx/source/gallery2/gallerybinaryengine.cxx
new file mode 100644
index 000000000..c86ffa6f5
--- /dev/null
+++ b/svx/source/gallery2/gallerybinaryengine.cxx
@@ -0,0 +1,810 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * 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/unomodel.hxx>
+#include <svx/fmmodel.hxx>
+#include <galobj.hxx>
+#include <svx/gallerybinaryengine.hxx>
+#include <svx/galleryobjectcollection.hxx>
+#include <svx/gallery1.hxx>
+#include <galleryobjectbinarystorage.hxx>
+#include <osl/thread.hxx>
+#include "codec.hxx"
+#include "gallerydrawmodel.hxx"
+#include <vcl/cvtgrf.hxx>
+#include <vcl/filter/SvmWriter.hxx>
+
+#include <sal/log.hxx>
+
+#include <com/sun/star/ucb/ContentCreationException.hpp>
+#include <com/sun/star/sdbc/XResultSet.hpp>
+#include <com/sun/star/ucb/XContentAccess.hpp>
+#include <comphelper/fileformat.h>
+#include <comphelper/graphicmimetype.hxx>
+#include <comphelper/processfactory.hxx>
+#include <tools/urlobj.hxx>
+#include <tools/diagnose_ex.h>
+#include <tools/datetime.hxx>
+#include <unotools/datetime.hxx>
+#include <unotools/ucbstreamhelper.hxx>
+#include <unotools/streamwrap.hxx>
+#include <unotools/tempfile.hxx>
+#include <ucbhelper/content.hxx>
+#include <tools/vcompat.hxx>
+
+using namespace ::com::sun::star;
+
+GalleryBinaryEngine::GalleryBinaryEngine(
+ const GalleryBinaryStorageLocations& rGalleryBinaryStorageLocations,
+ GalleryObjectCollection& rGalleryObjectCollection, bool bReadOnly)
+ : maGalleryStorageLocations(rGalleryBinaryStorageLocations)
+ , mrGalleryObjectCollection(rGalleryObjectCollection)
+ , mbReadOnly(bReadOnly)
+ , m_bDestDirRelative(false)
+{
+ ImplCreateSvDrawStorage();
+}
+
+GalleryBinaryEngine::~GalleryBinaryEngine() { clearSotStorage(); }
+
+void GalleryBinaryEngine::setDestDir(const OUString& rDestDir, bool bRelative)
+{
+ m_aDestDir = rDestDir;
+ m_bDestDirRelative = bRelative;
+}
+
+void GalleryBinaryEngine::clearSotStorage() { m_aSvDrawStorageRef.clear(); }
+
+void GalleryBinaryEngine::ImplCreateSvDrawStorage()
+{
+ try
+ {
+ m_aSvDrawStorageRef
+ = new SotStorage(false, GetSdvURL().GetMainURL(INetURLObject::DecodeMechanism::NONE),
+ mbReadOnly ? StreamMode::READ : StreamMode::STD_READWRITE);
+ // #i50423# ReadOnly may not been set though the file can't be written (because of security reasons)
+ if ((m_aSvDrawStorageRef->GetError() != ERRCODE_NONE) && !mbReadOnly)
+ m_aSvDrawStorageRef = new SotStorage(
+ false, GetSdvURL().GetMainURL(INetURLObject::DecodeMechanism::NONE),
+ StreamMode::READ);
+ }
+ catch (const css::ucb::ContentCreationException&)
+ {
+ TOOLS_WARN_EXCEPTION("svx", "failed to open: " << GetSdvURL().GetMainURL(
+ INetURLObject::DecodeMechanism::NONE)
+ << "due to");
+ }
+}
+
+const tools::SvRef<SotStorage>& GalleryBinaryEngine::GetSvDrawStorage() const
+{
+ return m_aSvDrawStorageRef;
+}
+
+bool GalleryBinaryEngine::implWrite(const GalleryTheme& rTheme, const GalleryThemeEntry* pThm)
+{
+ INetURLObject aPathURL(GetThmURL());
+
+ aPathURL.removeSegment();
+ aPathURL.removeFinalSlash();
+
+ DBG_ASSERT(aPathURL.GetProtocol() != INetProtocol::NotValid, "invalid URL");
+
+ if (FileExists(aPathURL) || CreateDir(aPathURL))
+ {
+#ifdef UNX
+ std::unique_ptr<SvStream> pOStm(::utl::UcbStreamHelper::CreateStream(
+ GetThmURL().GetMainURL(INetURLObject::DecodeMechanism::NONE),
+ StreamMode::WRITE | StreamMode::COPY_ON_SYMLINK | StreamMode::TRUNC));
+#else
+ std::unique_ptr<SvStream> pOStm(::utl::UcbStreamHelper::CreateStream(
+ GetThmURL().GetMainURL(INetURLObject::DecodeMechanism::NONE),
+ StreamMode::WRITE | StreamMode::TRUNC));
+#endif
+
+ if (pOStm)
+ {
+ writeGalleryTheme(*pOStm, rTheme, pThm);
+ pOStm.reset();
+ return true;
+ }
+
+ return false;
+ }
+ return true;
+}
+
+void GalleryBinaryEngine::insertObject(const SgaObject& rObj, GalleryObject* pFoundEntry,
+ sal_uInt32 nInsertPos)
+{
+ if (pFoundEntry)
+ {
+ GalleryObject aNewEntry;
+
+ // update title of new object if necessary
+ if (rObj.GetTitle().isEmpty())
+ {
+ std::unique_ptr<SgaObject> pOldObj(implReadSgaObject(pFoundEntry));
+
+ if (pOldObj)
+ {
+ const_cast<SgaObject&>(rObj).SetTitle(pOldObj->GetTitle());
+ }
+ }
+ else if (rObj.GetTitle() == "__<empty>__")
+ const_cast<SgaObject&>(rObj).SetTitle("");
+
+ implWriteSgaObject(rObj, nInsertPos, &aNewEntry);
+ pFoundEntry->nOffset = aNewEntry.nOffset;
+ }
+ else
+ implWriteSgaObject(rObj, nInsertPos, nullptr);
+}
+
+void GalleryBinaryEngine::removeObject(const std::unique_ptr<GalleryObject>& pEntry)
+{
+ if (mrGalleryObjectCollection.getObjectList().empty())
+ KillFile(GetSdgURL());
+
+ if (SgaObjKind::SvDraw == pEntry->eObjKind)
+ GetSvDrawStorage()->Remove(
+ pEntry->getURL().GetMainURL(INetURLObject::DecodeMechanism::NONE));
+}
+
+std::unique_ptr<SgaObject> GalleryBinaryEngine::implReadSgaObject(GalleryObject const* pEntry)
+{
+ std::unique_ptr<SgaObject> pSgaObj;
+
+ if (pEntry)
+ {
+ std::unique_ptr<SvStream> pIStm(::utl::UcbStreamHelper::CreateStream(
+ GetSdgURL().GetMainURL(INetURLObject::DecodeMechanism::NONE), StreamMode::READ));
+
+ if (pIStm)
+ {
+ sal_uInt32 nInventor;
+
+ // Check to ensure that the file is a valid SGA file
+ pIStm->Seek(pEntry->nOffset);
+ pIStm->ReadUInt32(nInventor);
+
+ if (nInventor == COMPAT_FORMAT('S', 'G', 'A', '3'))
+ {
+ pIStm->Seek(pEntry->nOffset);
+
+ switch (pEntry->eObjKind)
+ {
+ case SgaObjKind::Bitmap:
+ pSgaObj.reset(new SgaObjectBmp());
+ break;
+ case SgaObjKind::Animation:
+ pSgaObj.reset(new SgaObjectAnim());
+ break;
+ case SgaObjKind::Inet:
+ pSgaObj.reset(new SgaObjectINet());
+ break;
+ case SgaObjKind::SvDraw:
+ pSgaObj.reset(new SgaObjectSvDraw());
+ break;
+ case SgaObjKind::Sound:
+ pSgaObj.reset(new SgaObjectSound());
+ break;
+
+ default:
+ break;
+ }
+
+ if (pSgaObj)
+ {
+ ReadSgaObject(*pIStm, *pSgaObj);
+ pSgaObj->ImplUpdateURL(pEntry->getURL());
+ }
+ }
+ }
+ }
+
+ return pSgaObj;
+}
+
+bool GalleryBinaryEngine::implWriteSgaObject(const SgaObject& rObj, sal_uInt32 nPos,
+ GalleryObject* pExistentEntry)
+{
+ std::unique_ptr<SvStream> pOStm(::utl::UcbStreamHelper::CreateStream(
+ GetSdgURL().GetMainURL(INetURLObject::DecodeMechanism::NONE), StreamMode::WRITE));
+ bool bRet = false;
+
+ if (pOStm)
+ {
+ const sal_uInt32 nOffset = pOStm->Seek(STREAM_SEEK_TO_END);
+
+ rObj.WriteData(*pOStm, m_aDestDir);
+
+ if (!pOStm->GetError())
+ {
+ GalleryObject* pEntry;
+
+ if (!pExistentEntry)
+ {
+ pEntry = new GalleryObject;
+ if (nPos < mrGalleryObjectCollection.size())
+ {
+ mrGalleryObjectCollection.getObjectList().emplace(
+ mrGalleryObjectCollection.getObjectList().begin() + nPos, pEntry);
+ }
+ else
+ mrGalleryObjectCollection.getObjectList().emplace_back(pEntry);
+ }
+ else
+ pEntry = pExistentEntry;
+
+ pEntry->m_pGalleryObjectStorage = std::make_unique<GalleryObjectBinaryStorage>();
+ pEntry->m_pGalleryObjectStorage->setURL(rObj.GetURL());
+
+ pEntry->nOffset = nOffset;
+ pEntry->eObjKind = rObj.GetObjKind();
+ bRet = true;
+ }
+ }
+
+ return bRet;
+}
+
+bool GalleryBinaryEngine::readModel(const GalleryObject* pObject, SdrModel& rModel)
+{
+ tools::SvRef<SotStorage> xSotStorage(GetSvDrawStorage());
+ bool bRet = false;
+ const INetURLObject aURL(ImplGetURL(pObject));
+
+ if (xSotStorage.is())
+ {
+ const OUString aStreamName(GetSvDrawStreamNameFromURL(aURL));
+ tools::SvRef<SotStorageStream> xInputStream(
+ xSotStorage->OpenSotStream(aStreamName, StreamMode::READ));
+
+ if (xInputStream.is() && !xInputStream->GetError())
+ {
+ xInputStream->SetBufferSize(STREAMBUF_SIZE);
+ bRet = GallerySvDrawImport(*xInputStream, rModel);
+ xInputStream->SetBufferSize(0);
+ }
+ }
+ return bRet;
+}
+
+SgaObjectSvDraw GalleryBinaryEngine::insertModel(const FmFormModel& rModel,
+ const INetURLObject& rUserURL)
+{
+ INetURLObject aURL(implCreateUniqueURL(SgaObjKind::SvDraw, rUserURL));
+ tools::SvRef<SotStorage> xSotStorage(GetSvDrawStorage());
+ bool bRet = false;
+
+ if (xSotStorage.is())
+ {
+ const OUString aStreamName(GetSvDrawStreamNameFromURL(aURL));
+ tools::SvRef<SotStorageStream> xOutputStream(
+ xSotStorage->OpenSotStream(aStreamName, StreamMode::WRITE | StreamMode::TRUNC));
+
+ if (xOutputStream.is() && !xOutputStream->GetError())
+ {
+ SvMemoryStream aMemoryStream(65535, 65535);
+ FmFormModel* pFormModel = const_cast<FmFormModel*>(&rModel);
+
+ pFormModel->BurnInStyleSheetAttributes();
+
+ {
+ uno::Reference<io::XOutputStream> xDocOut(
+ new utl::OOutputStreamWrapper(aMemoryStream));
+
+ if (xDocOut.is())
+ (void)SvxDrawingLayerExport(pFormModel, xDocOut);
+ }
+
+ aMemoryStream.Seek(0);
+
+ xOutputStream->SetBufferSize(16348);
+ GalleryCodec aCodec(*xOutputStream);
+ aCodec.Write(aMemoryStream);
+
+ xOutputStream->SetBufferSize(0);
+ xOutputStream->Commit();
+ bRet = !xOutputStream->GetError();
+ }
+ }
+ if (bRet)
+ {
+ SgaObjectSvDraw aObjSvDraw(rModel, aURL);
+ return aObjSvDraw;
+ }
+ return SgaObjectSvDraw();
+}
+
+bool GalleryBinaryEngine::readModelStream(const GalleryObject* pObject,
+ tools::SvRef<SotTempStream> const& rxModelStream)
+{
+ const INetURLObject aURL(ImplGetURL(pObject));
+ tools::SvRef<SotStorage> xSotStorage(GetSvDrawStorage());
+ bool bRet = false;
+
+ if (xSotStorage.is())
+ {
+ const OUString aStreamName(GetSvDrawStreamNameFromURL(aURL));
+ tools::SvRef<SotStorageStream> xInputStream(
+ xSotStorage->OpenSotStream(aStreamName, StreamMode::READ));
+
+ if (xInputStream.is() && !xInputStream->GetError())
+ {
+ sal_uInt32 nVersion = 0;
+
+ xInputStream->SetBufferSize(16348);
+
+ if (GalleryCodec::IsCoded(*xInputStream, nVersion))
+ {
+ SvxGalleryDrawModel aModel;
+
+ if (aModel.GetModel())
+ {
+ if (GallerySvDrawImport(*xInputStream, *aModel.GetModel()))
+ {
+ aModel.GetModel()->BurnInStyleSheetAttributes();
+
+ {
+ uno::Reference<io::XOutputStream> xDocOut(
+ new utl::OOutputStreamWrapper(*rxModelStream));
+
+ SvxDrawingLayerExport(aModel.GetModel(), xDocOut);
+ }
+ }
+
+ bRet = (rxModelStream->GetError() == ERRCODE_NONE);
+ }
+ }
+
+ xInputStream->SetBufferSize(0);
+ }
+ }
+ return bRet;
+}
+
+SgaObjectSvDraw
+GalleryBinaryEngine::insertModelStream(const tools::SvRef<SotTempStream>& rxModelStream,
+ const INetURLObject& rUserURL)
+{
+ INetURLObject aURL(implCreateUniqueURL(SgaObjKind::SvDraw, rUserURL));
+ tools::SvRef<SotStorage> xSotStorage(GetSvDrawStorage());
+
+ if (xSotStorage.is())
+ {
+ const OUString aStreamName(GetSvDrawStreamNameFromURL(aURL));
+ tools::SvRef<SotStorageStream> xOutputStream(
+ xSotStorage->OpenSotStream(aStreamName, StreamMode::WRITE | StreamMode::TRUNC));
+
+ if (xOutputStream.is() && !xOutputStream->GetError())
+ {
+ GalleryCodec aCodec(*xOutputStream);
+
+ xOutputStream->SetBufferSize(16348);
+ aCodec.Write(*rxModelStream);
+
+ if (!xOutputStream->GetError())
+ {
+ xOutputStream->Seek(0);
+ SgaObjectSvDraw aObjSvDraw(*xOutputStream, aURL);
+ return aObjSvDraw;
+ }
+ }
+ }
+ return SgaObjectSvDraw();
+}
+
+INetURLObject GalleryBinaryEngine::implCreateUniqueURL(SgaObjKind eObjKind,
+ const INetURLObject& rUserURL,
+ ConvertDataFormat nFormat)
+{
+ INetURLObject aDir(rUserURL);
+ INetURLObject aInfoFileURL(rUserURL);
+ INetURLObject aNewURL;
+ sal_uInt32 nNextNumber = 1999;
+ char const* pExt = nullptr;
+ bool bExists;
+
+ aDir.Append(u"dragdrop");
+ CreateDir(aDir);
+
+ aInfoFileURL.Append(u"sdddndx1");
+
+ // read next possible number
+ if (FileExists(aInfoFileURL))
+ {
+ std::unique_ptr<SvStream> pIStm(::utl::UcbStreamHelper::CreateStream(
+ aInfoFileURL.GetMainURL(INetURLObject::DecodeMechanism::NONE), StreamMode::READ));
+
+ if (pIStm)
+ {
+ pIStm->ReadUInt32(nNextNumber);
+ }
+ }
+
+ pExt = comphelper::GraphicMimeTypeHelper::GetExtensionForConvertDataFormat(nFormat);
+
+ do
+ {
+ // get URL
+ if (SgaObjKind::SvDraw == eObjKind)
+ {
+ OUString aFileName = "gallery/svdraw/dd" + OUString::number(++nNextNumber % 99999999);
+ aNewURL = INetURLObject(aFileName, INetProtocol::PrivSoffice);
+
+ bExists = false;
+
+ for (auto const& pObject : mrGalleryObjectCollection.getObjectList())
+ {
+ if (pObject->getURL() == aNewURL)
+ {
+ bExists = true;
+ break;
+ }
+ }
+ }
+ else
+ {
+ OUString aFileName = "dd" + OUString::number(++nNextNumber % 999999);
+
+ if (pExt)
+ aFileName += OUString(pExt, strlen(pExt), RTL_TEXTENCODING_ASCII_US);
+
+ aNewURL = aDir;
+ aNewURL.Append(aFileName);
+
+ bExists = FileExists(aNewURL);
+ }
+ } while (bExists);
+
+ // write updated number
+ std::unique_ptr<SvStream> pOStm(::utl::UcbStreamHelper::CreateStream(
+ aInfoFileURL.GetMainURL(INetURLObject::DecodeMechanism::NONE), StreamMode::WRITE));
+
+ if (pOStm)
+ {
+ pOStm->WriteUInt32(nNextNumber);
+ }
+
+ return aNewURL;
+}
+
+SgaObjectBmp GalleryBinaryEngine::insertGraphic(const Graphic& rGraphic, const GfxLink& aGfxLink,
+ const ConvertDataFormat& nExportFormat,
+ const INetURLObject& rUserURL)
+{
+ const INetURLObject aURL(implCreateUniqueURL(SgaObjKind::Bitmap, rUserURL, nExportFormat));
+ std::unique_ptr<SvStream> pOStm(
+ ::utl::UcbStreamHelper::CreateStream(aURL.GetMainURL(INetURLObject::DecodeMechanism::NONE),
+ StreamMode::WRITE | StreamMode::TRUNC));
+ bool bRet = false;
+
+ if (pOStm)
+ {
+ pOStm->SetVersion(SOFFICE_FILEFORMAT_50);
+
+ if (ConvertDataFormat::SVM == nExportFormat)
+ {
+ GDIMetaFile aMtf(rGraphic.GetGDIMetaFile());
+
+ SvmWriter aWriter(*pOStm);
+ aWriter.Write(aMtf);
+ bRet = (pOStm->GetError() == ERRCODE_NONE);
+ }
+ else
+ {
+ if (aGfxLink.GetDataSize() && aGfxLink.GetData())
+ {
+ pOStm->WriteBytes(aGfxLink.GetData(), aGfxLink.GetDataSize());
+ bRet = (pOStm->GetError() == ERRCODE_NONE);
+ }
+ else
+ bRet = (GraphicConverter::Export(*pOStm, rGraphic, nExportFormat) == ERRCODE_NONE);
+ }
+
+ pOStm.reset();
+ }
+ if (bRet)
+ {
+ const SgaObjectBmp aObjBmp(aURL);
+ return aObjBmp;
+ }
+ return SgaObjectBmp();
+}
+
+SgaObjectSvDraw GalleryBinaryEngine::updateSvDrawObject(const GalleryObject* pEntry)
+{
+ if (GetSvDrawStorage().is())
+ {
+ const OUString aStmName(GetSvDrawStreamNameFromURL(pEntry->getURL()));
+ tools::SvRef<SotStorageStream> pIStm
+ = GetSvDrawStorage()->OpenSotStream(aStmName, StreamMode::READ);
+
+ if (pIStm.is() && !pIStm->GetError())
+ {
+ pIStm->SetBufferSize(16384);
+
+ SgaObjectSvDraw aNewObj(*pIStm, pEntry->getURL());
+
+ pIStm->SetBufferSize(0);
+
+ return aNewObj;
+ }
+ }
+ return SgaObjectSvDraw();
+}
+
+void GalleryBinaryEngine::updateTheme()
+{
+ ::utl::TempFile aTmp;
+ INetURLObject aInURL(GetSdgURL());
+ INetURLObject aTmpURL(aTmp.GetURL());
+
+ DBG_ASSERT(aInURL.GetProtocol() != INetProtocol::NotValid, "invalid URL");
+ DBG_ASSERT(aTmpURL.GetProtocol() != INetProtocol::NotValid, "invalid URL");
+
+ std::unique_ptr<SvStream> pIStm(::utl::UcbStreamHelper::CreateStream(
+ aInURL.GetMainURL(INetURLObject::DecodeMechanism::NONE), StreamMode::READ));
+ std::unique_ptr<SvStream> pTmpStm(::utl::UcbStreamHelper::CreateStream(
+ aTmpURL.GetMainURL(INetURLObject::DecodeMechanism::NONE),
+ StreamMode::WRITE | StreamMode::TRUNC));
+
+ if (pIStm && pTmpStm)
+ {
+ for (const auto& i : mrGalleryObjectCollection.getObjectList())
+ {
+ GalleryObject* pEntry = i.get();
+ std::unique_ptr<SgaObject> pObj;
+
+ switch (pEntry->eObjKind)
+ {
+ case SgaObjKind::Bitmap:
+ pObj.reset(new SgaObjectBmp());
+ break;
+ case SgaObjKind::Animation:
+ pObj.reset(new SgaObjectAnim());
+ break;
+ case SgaObjKind::Inet:
+ pObj.reset(new SgaObjectINet());
+ break;
+ case SgaObjKind::SvDraw:
+ pObj.reset(new SgaObjectSvDraw());
+ break;
+ case SgaObjKind::Sound:
+ pObj.reset(new SgaObjectSound());
+ break;
+
+ default:
+ break;
+ }
+
+ if (pObj)
+ {
+ pIStm->Seek(pEntry->nOffset);
+ ReadSgaObject(*pIStm, *pObj);
+ pEntry->nOffset = pTmpStm->Tell();
+ WriteSgaObject(*pTmpStm, *pObj);
+ }
+ }
+ }
+ else
+ {
+ OSL_FAIL("File(s) could not be opened");
+ }
+
+ pIStm.reset();
+ pTmpStm.reset();
+
+ CopyFile(aTmpURL, aInURL);
+ KillFile(aTmpURL);
+
+ ErrCode nStorErr = ERRCODE_NONE;
+
+ try
+ {
+ tools::SvRef<SotStorage> aTempStorageRef(
+ new SotStorage(false, aTmpURL.GetMainURL(INetURLObject::DecodeMechanism::NONE),
+ StreamMode::STD_READWRITE));
+ GetSvDrawStorage()->CopyTo(aTempStorageRef.get());
+ nStorErr = GetSvDrawStorage()->GetError();
+ }
+ catch (const css::ucb::ContentCreationException&)
+ {
+ TOOLS_WARN_EXCEPTION("svx", "failed to open: "
+ << aTmpURL.GetMainURL(INetURLObject::DecodeMechanism::NONE)
+ << "due to");
+ nStorErr = ERRCODE_IO_GENERAL;
+ }
+
+ if (nStorErr == ERRCODE_NONE)
+ {
+ clearSotStorage();
+ CopyFile(aTmpURL, GetSdvURL());
+ ImplCreateSvDrawStorage();
+ }
+
+ KillFile(aTmpURL);
+}
+
+void GalleryBinaryEngine::insertFileOrDirURL(const INetURLObject& rFileOrDirURL,
+ std::vector<INetURLObject>& rURLVector)
+{
+ INetURLObject aURL;
+ try
+ {
+ ::ucbhelper::Content aCnt(rFileOrDirURL.GetMainURL(INetURLObject::DecodeMechanism::NONE),
+ uno::Reference<ucb::XCommandEnvironment>(),
+ comphelper::getProcessComponentContext());
+ bool bFolder = false;
+
+ aCnt.getPropertyValue("IsFolder") >>= bFolder;
+
+ if (bFolder)
+ {
+ uno::Sequence<OUString> aProps{ "Url" };
+ uno::Reference<sdbc::XResultSet> xResultSet(
+ aCnt.createCursor(aProps, ::ucbhelper::INCLUDE_DOCUMENTS_ONLY));
+ uno::Reference<ucb::XContentAccess> xContentAccess(xResultSet, uno::UNO_QUERY);
+ if (xContentAccess.is())
+ {
+ while (xResultSet->next())
+ {
+ aURL.SetSmartURL(xContentAccess->queryContentIdentifierString());
+ rURLVector.push_back(aURL);
+ }
+ }
+ }
+ else
+ rURLVector.push_back(rFileOrDirURL);
+ }
+ catch (const ucb::ContentCreationException&)
+ {
+ }
+ catch (const uno::RuntimeException&)
+ {
+ }
+ catch (const uno::Exception&)
+ {
+ }
+}
+
+SvStream& GalleryBinaryEngine::writeGalleryTheme(SvStream& rOStm, const GalleryTheme& rTheme,
+ const GalleryThemeEntry* pThm)
+{
+ const INetURLObject rRelURL1 = rTheme.GetParent()->GetRelativeURL();
+ const INetURLObject rRelURL2 = rTheme.GetParent()->GetUserURL();
+ const sal_uInt32 rId = rTheme.GetId();
+ sal_uInt32 nCount = mrGalleryObjectCollection.size();
+ bool bRel;
+
+ rOStm.WriteUInt16(0x0004);
+ write_uInt16_lenPrefixed_uInt8s_FromOUString(rOStm, pThm->GetThemeName(),
+ RTL_TEXTENCODING_UTF8);
+ rOStm.WriteUInt32(nCount).WriteUInt16(osl_getThreadTextEncoding());
+
+ for (sal_uInt32 i = 0; i < nCount; i++)
+ {
+ const GalleryObject* pObj = mrGalleryObjectCollection.getForPosition(i);
+ OUString aPath;
+
+ if (SgaObjKind::SvDraw == pObj->eObjKind)
+ {
+ aPath = GetSvDrawStreamNameFromURL(pObj->getURL());
+ bRel = false;
+ }
+ else
+ {
+ aPath = pObj->getURL().GetMainURL(INetURLObject::DecodeMechanism::NONE);
+ aPath = aPath.copy(
+ 0, std::min(rRelURL1.GetMainURL(INetURLObject::DecodeMechanism::NONE).getLength(),
+ aPath.getLength()));
+ bRel = aPath == rRelURL1.GetMainURL(INetURLObject::DecodeMechanism::NONE);
+
+ if (bRel
+ && (pObj->getURL().GetMainURL(INetURLObject::DecodeMechanism::NONE).getLength()
+ > (rRelURL1.GetMainURL(INetURLObject::DecodeMechanism::NONE).getLength() + 1)))
+ {
+ aPath = pObj->getURL().GetMainURL(INetURLObject::DecodeMechanism::NONE);
+ aPath = aPath.copy(
+ std::min(rRelURL1.GetMainURL(INetURLObject::DecodeMechanism::NONE).getLength(),
+ aPath.getLength()));
+ }
+ else
+ {
+ aPath = pObj->getURL().GetMainURL(INetURLObject::DecodeMechanism::NONE);
+ aPath = aPath.copy(
+ 0,
+ std::min(rRelURL2.GetMainURL(INetURLObject::DecodeMechanism::NONE).getLength(),
+ aPath.getLength()));
+ bRel = aPath == rRelURL2.GetMainURL(INetURLObject::DecodeMechanism::NONE);
+
+ if (bRel
+ && (pObj->getURL().GetMainURL(INetURLObject::DecodeMechanism::NONE).getLength()
+ > (rRelURL2.GetMainURL(INetURLObject::DecodeMechanism::NONE).getLength()
+ + 1)))
+ {
+ aPath = pObj->getURL().GetMainURL(INetURLObject::DecodeMechanism::NONE);
+ aPath = aPath.copy(std::min(
+ rRelURL2.GetMainURL(INetURLObject::DecodeMechanism::NONE).getLength(),
+ aPath.getLength()));
+ }
+ else
+ aPath = pObj->getURL().GetMainURL(INetURLObject::DecodeMechanism::NONE);
+ }
+ }
+
+ if (!m_aDestDir.isEmpty())
+ {
+ bool aFound = aPath.indexOf(m_aDestDir) != -1;
+ aPath = aPath.replaceFirst(m_aDestDir, "");
+ if (aFound)
+ bRel = m_bDestDirRelative;
+ else
+ SAL_WARN("svx", "failed to replace destdir of '" << m_aDestDir << "' in '" << aPath
+ << "'");
+ }
+
+ rOStm.WriteBool(bRel);
+ write_uInt16_lenPrefixed_uInt8s_FromOUString(rOStm, aPath, RTL_TEXTENCODING_UTF8);
+ rOStm.WriteUInt32(pObj->nOffset).WriteUInt16(static_cast<sal_uInt16>(pObj->eObjKind));
+ }
+
+ // more recently, a 512-byte reserve buffer is written,
+ // to recognize them two sal_uInt32-Ids will be written.
+ rOStm.WriteUInt32(COMPAT_FORMAT('G', 'A', 'L', 'R'))
+ .WriteUInt32(COMPAT_FORMAT('E', 'S', 'R', 'V'));
+
+ const sal_uInt64 nReservePos = rOStm.Tell();
+ std::unique_ptr<VersionCompatWrite> pCompat(new VersionCompatWrite(rOStm, 2));
+
+ rOStm.WriteUInt32(rId).WriteBool(pThm->IsNameFromResource()); // From version 2 and up
+
+ pCompat.reset();
+
+ // Fill the rest of the buffer.
+ const tools::Long nRest
+ = std::max(tools::Long(512 - (rOStm.Tell() - nReservePos)), tools::Long(0));
+
+ if (nRest)
+ {
+ std::unique_ptr<char[]> pReserve(new char[nRest]);
+ memset(pReserve.get(), 0, nRest);
+ rOStm.WriteBytes(pReserve.get(), nRest);
+ }
+
+ return rOStm;
+}
+
+DateTime GalleryBinaryEngine::getModificationDate() const
+{
+ ::ucbhelper::Content aCnt(GetThmURL().GetMainURL(INetURLObject::DecodeMechanism::NONE),
+ uno::Reference<ucb::XCommandEnvironment>(),
+ comphelper::getProcessComponentContext());
+ util::DateTime aDateTimeModified;
+ DateTime aDateTime(DateTime::EMPTY);
+
+ aCnt.getPropertyValue("DateModified") >>= aDateTimeModified;
+ ::utl::typeConvert(aDateTimeModified, aDateTime);
+
+ return aDateTime;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/gallery2/gallerybinaryengineentry.cxx b/svx/source/gallery2/gallerybinaryengineentry.cxx
new file mode 100644
index 000000000..f892788ae
--- /dev/null
+++ b/svx/source/gallery2/gallerybinaryengineentry.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 <svx/gallerybinaryengineentry.hxx>
+#include <svx/galmisc.hxx>
+#include <svx/gallery1.hxx>
+
+#include <unotools/ucbstreamhelper.hxx>
+#include <tools/urlobj.hxx>
+#include <tools/vcompat.hxx>
+
+#include <com/sun/star/ucb/ContentCreationException.hpp>
+
+static bool FileExists(const INetURLObject& rURL, std::u16string_view rExt)
+{
+ INetURLObject aURL(rURL);
+ aURL.setExtension(rExt);
+ return FileExists(aURL);
+}
+
+GalleryBinaryEngineEntry::GalleryBinaryEngineEntry()
+{
+ mpGalleryStorageLocations = std::make_unique<GalleryBinaryStorageLocations>();
+}
+
+void GalleryBinaryEngineEntry::setStorageLocations(INetURLObject& rURL)
+{
+ mpGalleryStorageLocations->SetStorageLocations(rURL);
+}
+
+std::unique_ptr<GalleryBinaryEngine> GalleryBinaryEngineEntry::createGalleryStorageEngine(
+ GalleryObjectCollection& mrGalleryObjectCollection, bool& bReadOnly)
+{
+ return std::make_unique<GalleryBinaryEngine>(*mpGalleryStorageLocations,
+ mrGalleryObjectCollection, bReadOnly);
+}
+
+void GalleryBinaryEngineEntry::CreateUniqueURL(const INetURLObject& rBaseURL, INetURLObject& aURL)
+{
+ INetURLObject aBaseNoCase(GalleryBinaryStorageLocations::ImplGetURLIgnoreCase(rBaseURL));
+ aURL = aBaseNoCase;
+ static sal_Int32 nIdx = 0;
+ while (FileExists(aURL, u"thm"))
+ { // create new URLs
+ nIdx++;
+ aURL = aBaseNoCase;
+ aURL.setName(OUStringConcatenation(aURL.getName() + OUString::number(nIdx)));
+ }
+}
+
+GalleryThemeEntry* GalleryBinaryEngineEntry::CreateThemeEntry(const INetURLObject& rURL,
+ bool bReadOnly)
+{
+ DBG_ASSERT(rURL.GetProtocol() != INetProtocol::NotValid, "invalid URL");
+
+ GalleryThemeEntry* pRet = nullptr;
+
+ if (FileExists(rURL))
+ {
+ std::unique_ptr<SvStream> pIStm(::utl::UcbStreamHelper::CreateStream(
+ rURL.GetMainURL(INetURLObject::DecodeMechanism::NONE), StreamMode::READ));
+
+ if (pIStm)
+ {
+ sal_uInt16 nVersion;
+
+ pIStm->ReadUInt16(nVersion);
+
+ if (nVersion <= 0x00ff)
+ {
+ bool bThemeNameFromResource = false;
+ sal_uInt32 nThemeId = 0;
+
+ OString aTmpStr = read_uInt16_lenPrefixed_uInt8s_ToOString(*pIStm);
+ OUString aThemeName = OStringToOUString(aTmpStr, RTL_TEXTENCODING_UTF8);
+
+ // execute a character conversion
+ if (nVersion >= 0x0004)
+ {
+ sal_uInt32 nCount;
+ sal_uInt16 nTemp16;
+
+ pIStm->ReadUInt32(nCount).ReadUInt16(nTemp16);
+ pIStm->Seek(STREAM_SEEK_TO_END);
+
+ // check whether there is a newer version;
+ // therefore jump back by 520Bytes (8 bytes ID + 512Bytes reserve buffer)
+ // if this is at all possible.
+ if (pIStm->Tell() >= 520)
+ {
+ sal_uInt32 nId1, nId2;
+
+ pIStm->SeekRel(-520);
+ pIStm->ReadUInt32(nId1).ReadUInt32(nId2);
+
+ if (nId1 == COMPAT_FORMAT('G', 'A', 'L', 'R')
+ && nId2 == COMPAT_FORMAT('E', 'S', 'R', 'V'))
+ {
+ VersionCompatRead aCompat(*pIStm);
+
+ pIStm->ReadUInt32(nThemeId);
+
+ if (aCompat.GetVersion() >= 2)
+ {
+ pIStm->ReadCharAsBool(bThemeNameFromResource);
+ }
+ }
+ }
+ }
+
+ pRet = new GalleryThemeEntry(false, rURL, aThemeName, bReadOnly, false, nThemeId,
+ bThemeNameFromResource);
+ }
+ }
+ }
+
+ return pRet;
+}
+
+void GalleryBinaryEngineEntry::removeTheme()
+{
+ INetURLObject aThmURL(GetThmURL());
+ INetURLObject aSdgURL(GetSdgURL());
+ INetURLObject aSdvURL(GetSdvURL());
+ INetURLObject aStrURL(GetStrURL());
+
+ KillFile(aThmURL);
+ KillFile(aSdgURL);
+ KillFile(aSdvURL);
+ KillFile(aStrURL);
+}
+
+std::unique_ptr<GalleryTheme>&
+GalleryBinaryEngineEntry::getCachedTheme(std::unique_ptr<GalleryTheme>& pNewTheme)
+{
+ INetURLObject aURL = GetThmURL();
+
+ DBG_ASSERT(aURL.GetProtocol() != INetProtocol::NotValid, "invalid URL");
+
+ if (FileExists(aURL))
+ {
+ std::unique_ptr<SvStream> pIStm(::utl::UcbStreamHelper::CreateStream(
+ aURL.GetMainURL(INetURLObject::DecodeMechanism::NONE), StreamMode::READ));
+
+ if (pIStm)
+ {
+ try
+ {
+ ReadGalleryTheme(*pIStm, *pNewTheme);
+
+ if (pIStm->GetError())
+ pNewTheme.reset();
+ }
+ catch (const css::ucb::ContentCreationException&)
+ {
+ }
+ }
+ }
+ return pNewTheme;
+}
+
+SvStream& ReadGalleryTheme(SvStream& rIn, GalleryTheme& rTheme) { return rTheme.ReadData(rIn); }
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/gallery2/gallerybinarystoragelocations.cxx b/svx/source/gallery2/gallerybinarystoragelocations.cxx
new file mode 100644
index 000000000..dc896f6c3
--- /dev/null
+++ b/svx/source/gallery2/gallerybinarystoragelocations.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 <svx/gallerybinarystoragelocations.hxx>
+#include <svx/galmisc.hxx>
+
+INetURLObject GalleryBinaryStorageLocations::ImplGetURLIgnoreCase(const INetURLObject& rURL)
+{
+ INetURLObject aURL(rURL);
+
+ // check original file name
+ if (!FileExists(aURL))
+ {
+ // check upper case file name
+ aURL.setName(aURL.getName().toAsciiUpperCase());
+
+ if (!FileExists(aURL))
+ {
+ // check lower case file name
+ aURL.setName(aURL.getName().toAsciiLowerCase());
+ }
+ }
+
+ return aURL;
+}
+
+void GalleryBinaryStorageLocations::SetThmExtension(INetURLObject& aURL)
+{
+ aURL.setExtension(u"thm");
+ maThmURL = ImplGetURLIgnoreCase(aURL);
+}
+
+void GalleryBinaryStorageLocations::SetSdgExtension(INetURLObject& aURL)
+{
+ aURL.setExtension(u"sdg");
+ maSdgURL = ImplGetURLIgnoreCase(aURL);
+}
+
+void GalleryBinaryStorageLocations::SetSdvExtension(INetURLObject& aURL)
+{
+ aURL.setExtension(u"sdv");
+ maSdvURL = ImplGetURLIgnoreCase(aURL);
+}
+
+void GalleryBinaryStorageLocations::SetStrExtension(INetURLObject& aURL)
+{
+ aURL.setExtension(u"str");
+ maStrURL = ImplGetURLIgnoreCase(aURL);
+}
+
+void GalleryBinaryStorageLocations::SetStorageLocations(INetURLObject& rURL)
+{
+ SetThmExtension(rURL);
+ SetSdgExtension(rURL);
+ SetSdvExtension(rURL);
+ SetStrExtension(rURL);
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/gallery2/gallerydrawmodel.hxx b/svx/source/gallery2/gallerydrawmodel.hxx
new file mode 100644
index 000000000..0b2ef2582
--- /dev/null
+++ b/svx/source/gallery2/gallerydrawmodel.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 .
+ */
+
+#pragma once
+
+#include <sfx2/objsh.hxx>
+
+class FmFormModel;
+
+class SvxGalleryDrawModel
+{
+public:
+ SvxGalleryDrawModel();
+ ~SvxGalleryDrawModel();
+
+ FmFormModel* GetModel() const { return mpFormModel; }
+
+private:
+ SfxObjectShellLock mxDoc;
+ FmFormModel* mpFormModel;
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/gallery2/galleryfilestorage.cxx b/svx/source/gallery2/galleryfilestorage.cxx
new file mode 100644
index 000000000..dd8e74cd3
--- /dev/null
+++ b/svx/source/gallery2/galleryfilestorage.cxx
@@ -0,0 +1,24 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * 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/galleryfilestorage.hxx>
+
+GalleryFileStorage::~GalleryFileStorage(){};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/gallery2/galleryfilestorageentry.cxx b/svx/source/gallery2/galleryfilestorageentry.cxx
new file mode 100644
index 000000000..d04f49bf8
--- /dev/null
+++ b/svx/source/gallery2/galleryfilestorageentry.cxx
@@ -0,0 +1,24 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * 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/galleryfilestorageentry.hxx>
+
+GalleryFileStorageEntry::~GalleryFileStorageEntry(){};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/gallery2/galleryobjectbinarystorage.cxx b/svx/source/gallery2/galleryobjectbinarystorage.cxx
new file mode 100644
index 000000000..14bdc8b08
--- /dev/null
+++ b/svx/source/gallery2/galleryobjectbinarystorage.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 <galleryobjectbinarystorage.hxx>
+#include <tools/urlobj.hxx>
+
+void GalleryObjectBinaryStorage::setURL(INetURLObject aURL) { m_aURL = aURL; }
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/gallery2/galleryobjectcollection.cxx b/svx/source/gallery2/galleryobjectcollection.cxx
new file mode 100644
index 000000000..515f52759
--- /dev/null
+++ b/svx/source/gallery2/galleryobjectcollection.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 <svx/galleryobjectcollection.hxx>
+
+GalleryObjectCollection::GalleryObjectCollection() {}
+
+void GalleryObjectCollection::clear() { m_aObjectList.clear(); }
+
+const GalleryObject* GalleryObjectCollection::searchObjectWithURL(const INetURLObject& rURL)
+{
+ for (auto const& pObject : m_aObjectList)
+ {
+ if (pObject->getURL() == rURL)
+ return pObject.get();
+ }
+ return nullptr;
+}
+
+sal_uInt32 GalleryObjectCollection::searchPosWithObject(const GalleryObject* pObj)
+{
+ for (sal_uInt32 i = 0, n = size(); i < n; ++i)
+ if (pObj == get(i).get())
+ return i;
+ return SAL_MAX_UINT32;
+}
+
+const GalleryObject* GalleryObjectCollection::getForPosition(sal_uInt32 nPos) const
+{
+ if (nPos < size())
+ return get(nPos).get();
+ return nullptr;
+}
+
+const INetURLObject g_aInvalidURL;
+
+const INetURLObject& GalleryObjectCollection::getURLForPosition(sal_uInt32 nPos) const
+{
+ if (nPos < size())
+ return get(nPos)->getURL();
+ return g_aInvalidURL;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/gallery2/galleryobjectstorage.cxx b/svx/source/gallery2/galleryobjectstorage.cxx
new file mode 100644
index 000000000..2d324b3d0
--- /dev/null
+++ b/svx/source/gallery2/galleryobjectstorage.cxx
@@ -0,0 +1,24 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * 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/galleryobjectstorage.hxx>
+
+GalleryObjectStorage::~GalleryObjectStorage() = default;
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/gallery2/galleryobjectxmlstorage.cxx b/svx/source/gallery2/galleryobjectxmlstorage.cxx
new file mode 100644
index 000000000..f07b7b869
--- /dev/null
+++ b/svx/source/gallery2/galleryobjectxmlstorage.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 <galleryobjectxmlstorage.hxx>
+#include <tools/urlobj.hxx>
+
+void GalleryObjectXMLStorage::setURL(INetURLObject aURL) { m_aURL = aURL; }
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/gallery2/gallerystoragelocations.cxx b/svx/source/gallery2/gallerystoragelocations.cxx
new file mode 100644
index 000000000..d71ae452f
--- /dev/null
+++ b/svx/source/gallery2/gallerystoragelocations.cxx
@@ -0,0 +1,24 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * 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/gallerystoragelocations.hxx>
+
+GalleryStorageLocations::~GalleryStorageLocations(){};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/gallery2/galmisc.cxx b/svx/source/gallery2/galmisc.cxx
new file mode 100644
index 000000000..3422f5e29
--- /dev/null
+++ b/svx/source/gallery2/galmisc.cxx
@@ -0,0 +1,563 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * 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 <sot/storage.hxx>
+#include <unotools/streamwrap.hxx>
+#include <comphelper/processfactory.hxx>
+#include <comphelper/string.hxx>
+#include <ucbhelper/content.hxx>
+#include <com/sun/star/ucb/ContentCreationException.hpp>
+#include <tools/urlobj.hxx>
+#include <vcl/graphicfilter.hxx>
+#include <svl/itempool.hxx>
+#include <sfx2/docfile.hxx>
+#include <vcl/svapp.hxx>
+#include <vcl/window.hxx>
+#include <svx/svdpage.hxx>
+#include <svx/dialmgr.hxx>
+#include <svx/svdograf.hxx>
+#include <svx/fmmodel.hxx>
+#include <svx/unomodel.hxx>
+#include "codec.hxx"
+#include <svx/strings.hrc>
+#include <svx/galtheme.hxx>
+#include <svx/galmisc.hxx>
+#include <osl/diagnose.h>
+#include <com/sun/star/awt/XProgressMonitor.hpp>
+#include <com/sun/star/ucb/TransferInfo.hpp>
+#include <com/sun/star/ucb/NameClash.hpp>
+#include <memory>
+
+using namespace ::com::sun::star;
+
+GalleryGraphicImportRet GalleryGraphicImport( const INetURLObject& rURL, Graphic& rGraphic,
+ OUString& rFilterName )
+{
+ GalleryGraphicImportRet nRet = GalleryGraphicImportRet::IMPORT_NONE;
+ SfxMedium aMedium( rURL.GetMainURL( INetURLObject::DecodeMechanism::NONE ), StreamMode::READ );
+
+ aMedium.Download();
+
+ SvStream* pIStm = aMedium.GetInStream();
+
+ if( pIStm )
+ {
+ GraphicFilter& rGraphicFilter = GraphicFilter::GetGraphicFilter();
+ sal_uInt16 nFormat;
+
+ if( !rGraphicFilter.ImportGraphic( rGraphic, rURL.GetMainURL( INetURLObject::DecodeMechanism::NONE ), *pIStm, GRFILTER_FORMAT_DONTKNOW, &nFormat ) )
+ {
+ rFilterName = rGraphicFilter.GetImportFormatName( nFormat );
+ nRet = GalleryGraphicImportRet::IMPORT_FILE;
+ }
+ }
+
+ return nRet;
+}
+
+bool GallerySvDrawImport( SvStream& rIStm, SdrModel& rModel )
+{
+ sal_uInt32 nVersion;
+ bool bRet = false;
+
+ if( GalleryCodec::IsCoded( rIStm, nVersion ) )
+ {
+ SvMemoryStream aMemStm( 65535, 65535 );
+ GalleryCodec aCodec( rIStm );
+
+ aCodec.Read( aMemStm );
+ aMemStm.Seek( 0 );
+
+ if( 1 == nVersion )
+ {
+ OSL_FAIL( "staroffice binary file formats are no longer supported inside the gallery!" );
+ bRet = false;
+ }
+ else if( 2 == nVersion )
+ {
+ // recall to read as XML
+ bRet = GallerySvDrawImport( aMemStm, rModel );
+ }
+ }
+ else
+ {
+ // read as XML
+ uno::Reference< io::XInputStream > xInputStream( new utl::OInputStreamWrapper( rIStm ) );
+
+ rModel.GetItemPool().SetDefaultMetric( MapUnit::Map100thMM );
+ uno::Reference< lang::XComponent > xComponent;
+
+ bRet = SvxDrawingLayerImport( &rModel, xInputStream, xComponent, "com.sun.star.comp.Draw.XMLOasisImporter" );
+ if( !bRet || (rModel.GetPageCount() == 0) )
+ {
+ rIStm.Seek(0);
+ bRet = SvxDrawingLayerImport( &rModel, xInputStream, xComponent, "com.sun.star.comp.Draw.XMLImporter" );
+ }
+
+ }
+
+ return bRet;
+}
+
+bool CreateIMapGraphic( const FmFormModel& rModel, Graphic& rGraphic, ImageMap& rImageMap )
+{
+ if (! rModel.GetPageCount() )
+ return false;
+
+ const SdrPage* pPage = rModel.GetPage( 0 );
+ const SdrObject* pObj = pPage->GetObj( 0 );
+
+ if ( pPage->GetObjCount() != 1 )
+ return false;
+ auto pGrafObj = dynamic_cast<const SdrGrafObj*>( pObj);
+ if (!pGrafObj)
+ return false;
+
+ bool bRet = false;
+ const sal_uInt16 nCount = pObj->GetUserDataCount();
+
+ // Exist in the user data an IMap information?
+ for ( sal_uInt16 i = 0; i < nCount; i++ )
+ {
+ const SdrObjUserData* pUserData = pObj->GetUserData( i );
+
+ if ( ( pUserData->GetInventor() == SdrInventor::SgaImap ) && ( pUserData->GetId() == ID_IMAPINFO ) )
+ {
+ rGraphic = pGrafObj->GetGraphic();
+ rImageMap = static_cast<const SgaIMapInfo*>( pUserData )->GetImageMap();
+ bRet = true;
+ break;
+ }
+ }
+
+ return bRet;
+}
+
+OUString GetReducedString( const INetURLObject& rURL, sal_Int32 nMaxLen )
+{
+ OUString aReduced( rURL.GetMainURL( INetURLObject::DecodeMechanism::Unambiguous ) );
+
+ aReduced = aReduced.copy(aReduced.lastIndexOf('/')+1);
+
+ if( INetProtocol::PrivSoffice != rURL.GetProtocol() )
+ {
+ sal_Unicode aDelimiter;
+ const OUString aPath( rURL.getFSysPath( FSysStyle::Detect, &aDelimiter ) );
+ const OUString aName( aReduced );
+
+ if( aPath.getLength() > nMaxLen )
+ {
+ sal_Int32 nPathPrefixLen = nMaxLen - aName.getLength() - 4;
+
+ if (nPathPrefixLen >= 0)
+ {
+ aReduced = OUString::Concat(aPath.subView(0, nPathPrefixLen)) + "..."
+ + OUStringChar(aDelimiter) + aName;
+ }
+ else
+ {
+ aReduced += "..." + OUStringChar(aDelimiter) + "..."
+ + aName.subView( aName.getLength() - (nMaxLen - 7) );
+ }
+ }
+ else
+ aReduced = aPath;
+ }
+
+ return aReduced;
+}
+
+OUString GetSvDrawStreamNameFromURL( const INetURLObject& rSvDrawObjURL )
+{
+ OUString aRet;
+
+ if( rSvDrawObjURL.GetProtocol() == INetProtocol::PrivSoffice &&
+ comphelper::string::getTokenCount(rSvDrawObjURL.GetMainURL( INetURLObject::DecodeMechanism::NONE ), '/') == 3 )
+ {
+ aRet = rSvDrawObjURL.GetMainURL( INetURLObject::DecodeMechanism::NONE ).getToken( 2, '/' );
+ }
+
+ return aRet;
+}
+
+bool FileExists( const INetURLObject& rURL )
+{
+ bool bRet = false;
+
+ if( rURL.GetProtocol() != INetProtocol::NotValid )
+ {
+ try
+ {
+ ::ucbhelper::Content aCnt( rURL.GetMainURL( INetURLObject::DecodeMechanism::NONE ), uno::Reference< ucb::XCommandEnvironment >(), comphelper::getProcessComponentContext() );
+ OUString aTitle;
+
+ aCnt.getPropertyValue("Title") >>= aTitle;
+ bRet = ( !aTitle.isEmpty() );
+ }
+ catch( const ucb::ContentCreationException& )
+ {
+ }
+ catch( const uno::RuntimeException& )
+ {
+ }
+ catch( const uno::Exception& )
+ {
+ }
+ }
+
+ return bRet;
+}
+
+bool CreateDir( const INetURLObject& rURL )
+{
+ bool bRet = FileExists( rURL );
+
+ if( !bRet )
+ {
+ try
+ {
+ uno::Reference< ucb::XCommandEnvironment > aCmdEnv;
+ INetURLObject aParentURL( rURL );
+ aParentURL.removeSegment();
+ ::ucbhelper::Content aParent( aParentURL.GetMainURL( INetURLObject::DecodeMechanism::NONE ), aCmdEnv, comphelper::getProcessComponentContext() );
+ uno::Sequence< OUString > aProps{ "Title" };
+ uno::Sequence< uno::Any > aValues{ uno::Any(rURL.GetLastName()) };
+
+ ::ucbhelper::Content aContent( rURL.GetMainURL( INetURLObject::DecodeMechanism::NONE ), aCmdEnv, comphelper::getProcessComponentContext() );
+ bRet = aParent.insertNewContent( "application/vnd.sun.staroffice.fsys-folder", aProps, aValues, aContent );
+ }
+ catch( const ucb::ContentCreationException& )
+ {
+ }
+ catch( const uno::RuntimeException& )
+ {
+ }
+ catch( const uno::Exception& )
+ {
+ }
+ }
+
+ return bRet;
+}
+
+bool CopyFile( const INetURLObject& rSrcURL, const INetURLObject& rDstURL )
+{
+ bool bRet = false;
+
+ try
+ {
+ ::ucbhelper::Content aDestPath( rDstURL.GetMainURL( INetURLObject::DecodeMechanism::NONE ), uno::Reference< ucb::XCommandEnvironment >(), comphelper::getProcessComponentContext() );
+
+ aDestPath.executeCommand( "transfer",
+ uno::Any( ucb::TransferInfo( false, rSrcURL.GetMainURL( INetURLObject::DecodeMechanism::NONE ),
+ rDstURL.GetLastName(), ucb::NameClash::OVERWRITE ) ) );
+ bRet = true;
+ }
+ catch( const ucb::ContentCreationException& )
+ {
+ }
+ catch( const uno::RuntimeException& )
+ {
+ }
+ catch( const uno::Exception& )
+ {
+ }
+
+ return bRet;
+}
+
+bool KillFile( const INetURLObject& rURL )
+{
+ bool bRet = FileExists( rURL );
+
+ if( bRet )
+ {
+ try
+ {
+ ::ucbhelper::Content aCnt( rURL.GetMainURL( INetURLObject::DecodeMechanism::NONE ), uno::Reference< ucb::XCommandEnvironment >(), comphelper::getProcessComponentContext() );
+ aCnt.executeCommand( "delete", uno::Any( true ) );
+ }
+ catch( const ucb::ContentCreationException& )
+ {
+ bRet = false;
+ }
+ catch( const uno::RuntimeException& )
+ {
+ bRet = false;
+ }
+ catch( const uno::Exception& )
+ {
+ bRet = false;
+ }
+ }
+
+ return bRet;
+}
+
+
+GalleryProgress::GalleryProgress( const GraphicFilter* pFilter )
+{
+
+ uno::Reference< lang::XMultiServiceFactory > xMgr( ::comphelper::getProcessServiceFactory() );
+
+ uno::Reference< awt::XProgressMonitor > xMonitor( xMgr->createInstance( "com.sun.star.awt.XProgressMonitor" ),
+ uno::UNO_QUERY );
+
+ if ( !xMonitor.is() )
+ return;
+
+ mxProgressBar = xMonitor;
+
+ OUString aProgressText;
+
+ if( pFilter )
+ {
+ aProgressText = SvxResId(RID_SVXSTR_GALLERY_FILTER);
+// pFilter->SetUpdatePercentHdl( LINK( this, GalleryProgress, Update ) ); // sj: progress wasn't working up from SO7 at all
+// // so I am removing this. The gallery progress should
+// // be changed to use the XStatusIndicator instead of XProgressMonitor
+ }
+ else
+ aProgressText = "Gallery";
+
+ xMonitor->addText( "Gallery", aProgressText, false ) ;
+ mxProgressBar->setRange( 0, GALLERY_PROGRESS_RANGE );
+}
+
+GalleryProgress::~GalleryProgress()
+{
+}
+
+void GalleryProgress::Update( sal_Int32 nVal, sal_Int32 nMaxVal )
+{
+ if( mxProgressBar.is() && nMaxVal )
+ mxProgressBar->setValue( std::min<sal_Int32>( static_cast<double>(nVal) / nMaxVal * GALLERY_PROGRESS_RANGE,
+ GALLERY_PROGRESS_RANGE ) );
+}
+
+
+GalleryTransferable::GalleryTransferable( GalleryTheme* pTheme, sal_uInt32 nObjectPos, bool bLazy ) :
+ mpTheme( pTheme ),
+ meObjectKind( pTheme ? mpTheme->GetObjectKind(nObjectPos) : SgaObjKind::NONE),
+ mnObjectPos( nObjectPos )
+{
+
+ InitData( bLazy );
+}
+
+void GalleryTransferable::SelectObject(sal_uInt32 nObjectPos)
+{
+ if (nObjectPos == mnObjectPos)
+ return;
+ ClearFormats();
+ mnObjectPos = nObjectPos;
+ meObjectKind = mpTheme ? mpTheme->GetObjectKind(mnObjectPos) : SgaObjKind::NONE;
+ ObjectReleased();
+ InitData(true);
+}
+
+GalleryTransferable::~GalleryTransferable()
+{
+}
+
+void GalleryTransferable::InitData( bool bLazy )
+{
+ switch( meObjectKind )
+ {
+ case SgaObjKind::SvDraw:
+ {
+ if( !bLazy )
+ {
+ if( !mpGraphicObject )
+ {
+ Graphic aGraphic;
+
+ if (mpTheme && mpTheme->GetGraphic(mnObjectPos, aGraphic))
+ mpGraphicObject.reset(new GraphicObject( aGraphic ));
+ }
+
+ if( !mxModelStream.is() )
+ {
+ mxModelStream = new SotTempStream( "" );
+ mxModelStream->SetBufferSize( 16348 );
+
+ if (!mpTheme || !mpTheme->GetModelStream(mnObjectPos, mxModelStream))
+ mxModelStream.clear();
+ else
+ mxModelStream->Seek( 0 );
+ }
+ }
+ }
+ break;
+
+ case SgaObjKind::Animation:
+ case SgaObjKind::Bitmap:
+ case SgaObjKind::Inet:
+ case SgaObjKind::Sound:
+ {
+ if( !mpURL )
+ {
+ mpURL.reset(new INetURLObject);
+
+ if (!mpTheme || !mpTheme->GetURL(mnObjectPos, *mpURL))
+ {
+ mpURL.reset();
+ }
+ }
+
+ if( ( SgaObjKind::Sound != meObjectKind ) && !mpGraphicObject )
+ {
+ Graphic aGraphic;
+
+ if (mpTheme && mpTheme->GetGraphic(mnObjectPos, aGraphic))
+ mpGraphicObject.reset(new GraphicObject( aGraphic ));
+ }
+ }
+ break;
+
+ default:
+ OSL_FAIL( "GalleryTransferable::GalleryTransferable: invalid object type" );
+ break;
+ }
+}
+
+void GalleryTransferable::AddSupportedFormats()
+{
+ if( SgaObjKind::SvDraw == meObjectKind )
+ {
+ AddFormat( SotClipboardFormatId::DRAWING );
+ AddFormat( SotClipboardFormatId::SVXB );
+ AddFormat( SotClipboardFormatId::GDIMETAFILE );
+ AddFormat( SotClipboardFormatId::BITMAP );
+ }
+ else
+ {
+ if( mpURL )
+ AddFormat( SotClipboardFormatId::SIMPLE_FILE );
+
+ if( mpGraphicObject )
+ {
+ AddFormat( SotClipboardFormatId::SVXB );
+
+ if( mpGraphicObject->GetType() == GraphicType::GdiMetafile )
+ {
+ AddFormat( SotClipboardFormatId::GDIMETAFILE );
+ AddFormat( SotClipboardFormatId::BITMAP );
+ }
+ else
+ {
+ AddFormat( SotClipboardFormatId::BITMAP );
+ AddFormat( SotClipboardFormatId::GDIMETAFILE );
+ }
+ }
+ }
+}
+
+bool GalleryTransferable::GetData( const datatransfer::DataFlavor& rFlavor, const OUString& /*rDestDoc*/ )
+{
+ SotClipboardFormatId nFormat = SotExchange::GetFormat( rFlavor );
+ bool bRet = false;
+
+ InitData( false );
+
+ if( ( SotClipboardFormatId::DRAWING == nFormat ) && ( SgaObjKind::SvDraw == meObjectKind ) )
+ {
+ bRet = ( mxModelStream.is() && SetObject( mxModelStream.get(), 0, rFlavor ) );
+ }
+ else if( ( SotClipboardFormatId::SIMPLE_FILE == nFormat ) && mpURL )
+ {
+ bRet = SetString( mpURL->GetMainURL( INetURLObject::DecodeMechanism::NONE ) );
+ }
+ else if( ( SotClipboardFormatId::SVXB == nFormat ) && mpGraphicObject )
+ {
+ bRet = SetGraphic( mpGraphicObject->GetGraphic() );
+ }
+ else if( ( SotClipboardFormatId::GDIMETAFILE == nFormat ) && mpGraphicObject )
+ {
+ bRet = SetGDIMetaFile( mpGraphicObject->GetGraphic().GetGDIMetaFile() );
+ }
+ else if( ( SotClipboardFormatId::BITMAP == nFormat ) && mpGraphicObject )
+ {
+ bRet = SetBitmapEx( mpGraphicObject->GetGraphic().GetBitmapEx(), rFlavor );
+ }
+
+ return bRet;
+}
+
+bool GalleryTransferable::WriteObject( tools::SvRef<SotTempStream>& rxOStm, void* pUserObject,
+ sal_uInt32, const datatransfer::DataFlavor& )
+{
+ bool bRet = false;
+
+ if( pUserObject )
+ {
+ rxOStm->WriteStream( *static_cast< SotStorageStream* >( pUserObject ) );
+ bRet = ( rxOStm->GetError() == ERRCODE_NONE );
+ }
+
+ return bRet;
+}
+
+void GalleryTransferable::DragFinished( sal_Int8 nDropAction )
+{
+ if (mpTheme)
+ {
+ mpTheme->SetDragging( false );
+ mpTheme->SetDragPos( 0 );
+ }
+ if ( nDropAction )
+ {
+ vcl::Window *pFocusWindow = Application::GetFocusWindow();
+ if ( pFocusWindow )
+ pFocusWindow->GrabFocusToDocument();
+ }
+}
+
+void GalleryTransferable::ObjectReleased()
+{
+ mxModelStream.clear();
+ mpGraphicObject.reset();
+ mpURL.reset();
+}
+
+bool GalleryTransferable::StartDrag()
+{
+ INetURLObject aURL;
+ if (mpTheme && mpTheme->GetURL(mnObjectPos, aURL) && aURL.GetProtocol() != INetProtocol::NotValid)
+ {
+ mpTheme->SetDragging( true );
+ mpTheme->SetDragPos( mnObjectPos );
+ return false;
+ }
+ return true;
+}
+
+INetURLObject ImplGetURL(const GalleryObject* pObject)
+{
+ INetURLObject aURL;
+
+ if (pObject)
+ aURL = pObject->getURL();
+
+ return aURL;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/gallery2/galobj.cxx b/svx/source/gallery2/galobj.cxx
new file mode 100644
index 000000000..44362b9a7
--- /dev/null
+++ b/svx/source/gallery2/galobj.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 <sal/config.h>
+
+#include <com/sun/star/frame/XModel.hpp>
+#include <sfx2/objsh.hxx>
+#include <comphelper/fileformat.h>
+#include <comphelper/servicehelper.hxx>
+#include <tools/vcompat.hxx>
+#include <tools/helpers.hxx>
+#include <vcl/virdev.hxx>
+#include <svx/fmmodel.hxx>
+#include <svx/fmview.hxx>
+#include <svx/fmpage.hxx>
+#include <svx/galmisc.hxx>
+#include <galobj.hxx>
+#include <vcl/dibtools.hxx>
+#include <vcl/filter/SvmReader.hxx>
+#include <vcl/filter/SvmWriter.hxx>
+#include "gallerydrawmodel.hxx"
+#include <bitmaps.hlst>
+
+using namespace ::com::sun::star;
+
+SgaObject::SgaObject()
+: bIsValid ( false ),
+ bIsThumbBmp ( true )
+{
+}
+
+SgaObject::SgaObject(const SgaObject& aObject)
+ : aThumbBmp(aObject.aThumbBmp)
+ , aThumbMtf(aObject.aThumbMtf)
+ , aURL(aObject.aURL)
+ , aTitle(aObject.aTitle)
+ , bIsValid(aObject.bIsValid)
+ , bIsThumbBmp(aObject.bIsThumbBmp)
+{
+}
+
+BitmapEx SgaObject::createPreviewBitmapEx(const Size& rSizePixel) const
+{
+ BitmapEx aRetval;
+
+ if(rSizePixel.Width() && rSizePixel.Height())
+ {
+ if(SgaObjKind::Sound == GetObjKind())
+ {
+ aRetval = BitmapEx(RID_SVXBMP_GALLERY_MEDIA);
+ }
+ else if(IsThumbBitmap())
+ {
+ aRetval = GetThumbBmp();
+ }
+ else
+ {
+ const Graphic aGraphic(GetThumbMtf());
+
+ aRetval = aGraphic.GetBitmapEx();
+ }
+
+ if(!aRetval.IsEmpty())
+ {
+ const Size aCurrentSizePixel(aRetval.GetSizePixel());
+ const double fScaleX(static_cast<double>(rSizePixel.Width()) / static_cast<double>(aCurrentSizePixel.Width()));
+ const double fScaleY(static_cast<double>(rSizePixel.Height()) / static_cast<double>(aCurrentSizePixel.Height()));
+ const double fScale(std::min(fScaleX, fScaleY));
+
+ // only scale when need to decrease, no need to make bigger as original. Also
+ // prevent scaling close to 1.0 which is not needed for pixel graphics
+ if(fScale < 1.0 && fabs(1.0 - fScale) > 0.005)
+ {
+ aRetval.Scale(fScale, fScale, BmpScaleFlag::BestQuality);
+ }
+ }
+ }
+
+ return aRetval;
+}
+
+bool SgaObject::CreateThumb( const Graphic& rGraphic )
+{
+ bool bRet = false;
+
+ if( rGraphic.GetType() == GraphicType::Bitmap )
+ {
+ BitmapEx aBmpEx( rGraphic.GetBitmapEx() );
+ Size aBmpSize( aBmpEx.GetSizePixel() );
+
+ if( aBmpSize.Width() && aBmpSize.Height() )
+ {
+ if( aBmpEx.GetPrefMapMode().GetMapUnit() != MapUnit::MapPixel &&
+ aBmpEx.GetPrefSize().Width() > 0 &&
+ aBmpEx.GetPrefSize().Height() > 0 )
+ {
+ Size aLogSize( OutputDevice::LogicToLogic(aBmpEx.GetPrefSize(), aBmpEx.GetPrefMapMode(), MapMode(MapUnit::Map100thMM)) );
+
+ if( !aLogSize.IsEmpty() )
+ {
+ double fFactorLog = static_cast< double >( aLogSize.Width() ) / aLogSize.Height();
+ double fFactorPix = static_cast< double >( aBmpSize.Width() ) / aBmpSize.Height();
+
+ if( fFactorPix > fFactorLog )
+ aBmpSize.setWidth( FRound( aBmpSize.Height() * fFactorLog ) );
+ else
+ aBmpSize.setHeight( FRound( aBmpSize.Width() / fFactorLog ) );
+
+ aBmpEx.Scale(aBmpSize, BmpScaleFlag::BestQuality);
+ }
+ }
+
+ // take over BitmapEx
+ aThumbBmp = aBmpEx;
+
+ if( ( aBmpSize.Width() <= S_THUMB ) && ( aBmpSize.Height() <= S_THUMB ) )
+ {
+ aThumbBmp.Convert( BmpConversion::N8BitColors );
+ bRet = true;
+ }
+ else
+ {
+ const float fFactor = static_cast<float>(aBmpSize.Width()) / aBmpSize.Height();
+ const Size aNewSize( std::max( tools::Long(fFactor < 1. ? S_THUMB * fFactor : S_THUMB), tools::Long(8) ),
+ std::max( tools::Long(fFactor < 1. ? S_THUMB : S_THUMB / fFactor), tools::Long(8) ) );
+ if(aThumbBmp.Scale(
+ static_cast<double>(aNewSize.Width()) / aBmpSize.Width(),
+ static_cast<double>(aNewSize.Height()) / aBmpSize.Height(),
+ BmpScaleFlag::BestQuality ) )
+ {
+ aThumbBmp.Convert( BmpConversion::N8BitColors );
+ bRet = true;
+ }
+ }
+ }
+ }
+ else if( rGraphic.GetType() == GraphicType::GdiMetafile )
+ {
+ const Size aPrefSize( rGraphic.GetPrefSize() );
+ const double fFactor = static_cast<double>(aPrefSize.Width()) / static_cast<double>(aPrefSize.Height());
+ Size aSize( S_THUMB, S_THUMB );
+ if ( fFactor < 1.0 )
+ aSize.setWidth( static_cast<sal_Int32>( S_THUMB * fFactor ) );
+ else
+ aSize.setHeight( static_cast<sal_Int32>( S_THUMB / fFactor ) );
+
+ const GraphicConversionParameters aParameters(aSize, false, true, true /*TODO: extra ", true" post-#i121194#*/);
+ aThumbBmp = rGraphic.GetBitmapEx(aParameters);
+
+ if( !aThumbBmp.IsEmpty() )
+ {
+ aThumbBmp.Convert( BmpConversion::N8BitColors );
+ bRet = true;
+ }
+ }
+
+ return bRet;
+}
+
+void SgaObject::WriteData( SvStream& rOut, const OUString& rDestDir ) const
+{
+ static const sal_uInt32 nInventor = COMPAT_FORMAT( 'S', 'G', 'A', '3' );
+
+ rOut.WriteUInt32( nInventor ).WriteUInt16( 0x0004 ).WriteUInt16( GetVersion() ).WriteUInt16( static_cast<sal_uInt16>(GetObjKind()) );
+ rOut.WriteBool( bIsThumbBmp );
+
+ if( bIsThumbBmp )
+ {
+ const SvStreamCompressFlags nOldCompressMode = rOut.GetCompressMode();
+ const sal_Int32 nOldVersion = rOut.GetVersion();
+
+ rOut.SetCompressMode( SvStreamCompressFlags::ZBITMAP );
+ rOut.SetVersion( SOFFICE_FILEFORMAT_50 );
+
+ WriteDIBBitmapEx(aThumbBmp, rOut);
+
+ rOut.SetVersion( nOldVersion );
+ rOut.SetCompressMode( nOldCompressMode );
+ }
+ else
+ {
+ if(!rOut.GetError())
+ {
+ SvmWriter aWriter(rOut);
+ aWriter.Write(aThumbMtf);
+ }
+ }
+
+ OUString aURLWithoutDestDir = aURL.GetMainURL( INetURLObject::DecodeMechanism::NONE );
+ aURLWithoutDestDir = aURLWithoutDestDir.replaceFirst(rDestDir, "");
+ write_uInt16_lenPrefixed_uInt8s_FromOUString(rOut, aURLWithoutDestDir, RTL_TEXTENCODING_UTF8);
+}
+
+void SgaObject::ReadData(SvStream& rIn, sal_uInt16& rReadVersion )
+{
+ sal_uInt32 nTmp32;
+ sal_uInt16 nTmp16;
+
+ rIn.ReadUInt32( nTmp32 ).ReadUInt16( nTmp16 ).ReadUInt16( rReadVersion ).ReadUInt16( nTmp16 ).ReadCharAsBool( bIsThumbBmp );
+
+ if( bIsThumbBmp )
+ {
+ ReadDIBBitmapEx(aThumbBmp, rIn);
+ }
+ else
+ {
+ SvmReader aReader( rIn );
+ aReader.Read( aThumbMtf );
+ }
+
+ OUString aTmpStr = read_uInt16_lenPrefixed_uInt8s_ToOUString(rIn, RTL_TEXTENCODING_UTF8);
+ aURL = INetURLObject(aTmpStr);
+}
+
+OUString const & SgaObject::GetTitle() const
+{
+ return aTitle;
+}
+
+void SgaObject::SetTitle( const OUString& rTitle )
+{
+ aTitle = rTitle;
+}
+
+SvStream& WriteSgaObject( SvStream& rOut, const SgaObject& rObj )
+{
+ rObj.WriteData( rOut, "" );
+ return rOut;
+}
+
+SvStream& ReadSgaObject( SvStream& rIn, SgaObject& rObj )
+{
+ sal_uInt16 nReadVersion;
+
+ rObj.ReadData( rIn, nReadVersion );
+ rObj.bIsValid = ( rIn.GetError() == ERRCODE_NONE );
+
+ return rIn;
+}
+
+SgaObjectBmp::SgaObjectBmp()
+{
+}
+
+SgaObjectBmp::SgaObjectBmp( const INetURLObject& rURL )
+{
+ Graphic aGraphic;
+ OUString aFilter;
+
+ if ( GalleryGraphicImportRet::IMPORT_NONE != GalleryGraphicImport( rURL, aGraphic, aFilter ) )
+ Init( aGraphic, rURL );
+}
+
+SgaObjectBmp::SgaObjectBmp( const Graphic& rGraphic, const INetURLObject& rURL )
+{
+ if( FileExists( rURL ) )
+ Init( rGraphic, rURL );
+}
+
+void SgaObjectBmp::Init( const Graphic& rGraphic, const INetURLObject& rURL )
+{
+ aURL = rURL;
+ bIsValid = CreateThumb( rGraphic );
+}
+
+void SgaObjectBmp::WriteData( SvStream& rOut, const OUString& rDestDir ) const
+{
+ // Set version
+ SgaObject::WriteData( rOut, rDestDir );
+ char const aDummy[ 10 ] = { 0 };
+ rOut.WriteBytes(aDummy, 10);
+ write_uInt16_lenPrefixed_uInt8s_FromOString(rOut, ""); //dummy
+ write_uInt16_lenPrefixed_uInt8s_FromOUString(rOut, aTitle, RTL_TEXTENCODING_UTF8);
+}
+
+void SgaObjectBmp::ReadData( SvStream& rIn, sal_uInt16& rReadVersion )
+{
+
+ SgaObject::ReadData( rIn, rReadVersion );
+ rIn.SeekRel( 10 ); // 16, 16, 32, 16
+ read_uInt16_lenPrefixed_uInt8s_ToOString(rIn); //dummy
+
+ if( rReadVersion >= 5 )
+ aTitle = read_uInt16_lenPrefixed_uInt8s_ToOUString(rIn, RTL_TEXTENCODING_UTF8);
+}
+
+
+SgaObjectSound::SgaObjectSound() :
+ eSoundType( SOUND_STANDARD )
+{
+}
+
+SgaObjectSound::SgaObjectSound( const INetURLObject& rURL ) :
+ eSoundType( SOUND_STANDARD )
+{
+
+ if( FileExists( rURL ) )
+ {
+ aURL = rURL;
+ aThumbBmp = Bitmap(Size(1, 1), vcl::PixelFormat::N1_BPP);
+ bIsValid = true;
+ }
+ else
+ bIsValid = false;
+}
+
+SgaObjectSound::~SgaObjectSound()
+{
+}
+
+BitmapEx SgaObjectSound::GetThumbBmp() const
+{
+ OUString sId;
+
+ switch( eSoundType )
+ {
+ case SOUND_COMPUTER: sId = RID_SVXBMP_GALLERY_SOUND_1; break;
+ case SOUND_MISC: sId = RID_SVXBMP_GALLERY_SOUND_2; break;
+ case SOUND_MUSIC: sId = RID_SVXBMP_GALLERY_SOUND_3; break;
+ case SOUND_NATURE: sId = RID_SVXBMP_GALLERY_SOUND_4; break;
+ case SOUND_SPEECH: sId = RID_SVXBMP_GALLERY_SOUND_5; break;
+ case SOUND_TECHNIC: sId = RID_SVXBMP_GALLERY_SOUND_6; break;
+ case SOUND_ANIMAL: sId = RID_SVXBMP_GALLERY_SOUND_7; break;
+
+ // standard
+ default:
+ sId = RID_SVXBMP_GALLERY_MEDIA;
+ break;
+ }
+
+ const BitmapEx aBmpEx(sId);
+
+ return aBmpEx;
+}
+
+void SgaObjectSound::WriteData( SvStream& rOut, const OUString& rDestDir ) const
+{
+ SgaObject::WriteData( rOut, rDestDir );
+ rOut.WriteUInt16( eSoundType );
+ write_uInt16_lenPrefixed_uInt8s_FromOUString(rOut, aTitle, RTL_TEXTENCODING_UTF8);
+}
+
+void SgaObjectSound::ReadData( SvStream& rIn, sal_uInt16& rReadVersion )
+{
+ SgaObject::ReadData( rIn, rReadVersion );
+
+ if( rReadVersion >= 5 )
+ {
+ sal_uInt16 nTmp16;
+
+ rIn.ReadUInt16( nTmp16 ); eSoundType = static_cast<GalSoundType>(nTmp16);
+
+ if( rReadVersion >= 6 )
+ aTitle = read_uInt16_lenPrefixed_uInt8s_ToOUString(rIn, RTL_TEXTENCODING_UTF8);
+ }
+}
+
+SgaObjectAnim::SgaObjectAnim()
+{
+}
+
+SgaObjectAnim::SgaObjectAnim( const Graphic& rGraphic,
+ const INetURLObject& rURL )
+{
+ aURL = rURL;
+ bIsValid = CreateThumb( rGraphic );
+}
+
+SgaObjectINet::SgaObjectINet()
+{
+}
+
+SgaObjectINet::SgaObjectINet( const Graphic& rGraphic, const INetURLObject& rURL ) :
+ SgaObjectAnim ( rGraphic, rURL )
+{
+}
+
+SgaObjectSvDraw::SgaObjectSvDraw()
+{
+}
+
+SgaObjectSvDraw::SgaObjectSvDraw( const FmFormModel& rModel, const INetURLObject& rURL )
+{
+ aURL = rURL;
+ bIsValid = CreateThumb( rModel );
+}
+
+
+SvxGalleryDrawModel::SvxGalleryDrawModel()
+: mpFormModel( nullptr )
+{
+ mxDoc = SfxObjectShell::CreateObjectByFactoryName( "sdraw" );
+
+ if( !mxDoc.Is() )
+ return;
+
+ mxDoc->DoInitNew();
+
+ mpFormModel
+ = dynamic_cast<FmFormModel*>(comphelper::getFromUnoTunnel<SdrModel>(mxDoc->GetModel()));
+ if (mpFormModel)
+ {
+ mpFormModel->InsertPage(mpFormModel->AllocPage(false).get());
+ }
+}
+
+SvxGalleryDrawModel::~SvxGalleryDrawModel()
+{
+ if( mxDoc.Is() )
+ mxDoc->DoClose();
+
+}
+
+SgaObjectSvDraw::SgaObjectSvDraw( SvStream& rIStm, const INetURLObject& rURL )
+{
+ SvxGalleryDrawModel aModel;
+
+ if( aModel.GetModel() )
+ {
+ if( GallerySvDrawImport( rIStm, *aModel.GetModel() ) )
+ {
+ aURL = rURL;
+ bIsValid = CreateThumb( *aModel.GetModel() );
+ }
+ }
+}
+
+bool SgaObjectSvDraw::CreateThumb( const FmFormModel& rModel )
+{
+ Graphic aGraphic;
+ ImageMap aImageMap;
+ bool bRet = false;
+
+ if ( CreateIMapGraphic( rModel, aGraphic, aImageMap ) )
+ {
+ bRet = SgaObject::CreateThumb( aGraphic );
+ }
+ else
+ {
+ const FmFormPage* pPage = static_cast< const FmFormPage* >(rModel.GetPage(0));
+
+ if(pPage)
+ {
+ const tools::Rectangle aObjRect(pPage->GetAllObjBoundRect());
+
+ if(aObjRect.GetWidth() && aObjRect.GetHeight())
+ {
+ ScopedVclPtrInstance< VirtualDevice > pVDev;
+ FmFormView aView(const_cast< FmFormModel& >(rModel), pVDev);
+
+ aView.ShowSdrPage(const_cast< FmFormPage* >(pPage));
+ aView.MarkAllObj();
+ aThumbBmp = aView.GetMarkedObjBitmapEx(true);
+ aGraphic = Graphic(aThumbBmp);
+ bRet = SgaObject::CreateThumb(aGraphic);
+ }
+ }
+ }
+
+ return bRet;
+}
+
+void SgaObjectSvDraw::WriteData( SvStream& rOut, const OUString& rDestDir ) const
+{
+ SgaObject::WriteData( rOut, rDestDir );
+ write_uInt16_lenPrefixed_uInt8s_FromOUString(rOut, aTitle, RTL_TEXTENCODING_UTF8);
+}
+
+void SgaObjectSvDraw::ReadData( SvStream& rIn, sal_uInt16& rReadVersion )
+{
+ SgaObject::ReadData( rIn, rReadVersion );
+
+ if( rReadVersion >= 5 )
+ aTitle = read_uInt16_lenPrefixed_uInt8s_ToOUString(rIn, RTL_TEXTENCODING_UTF8);
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/gallery2/galtheme.cxx b/svx/source/gallery2/galtheme.cxx
new file mode 100644
index 000000000..4a9d10e60
--- /dev/null
+++ b/svx/source/gallery2/galtheme.cxx
@@ -0,0 +1,780 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * 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 <algorithm>
+
+#include <osl/file.hxx>
+#include <osl/thread.hxx>
+#include <tools/urlobj.hxx>
+#include <tools/vcompat.hxx>
+#include <tools/datetime.hxx>
+#include <sot/storage.hxx>
+#include <sot/formats.hxx>
+#include <sot/filelist.hxx>
+#include <vcl/virdev.hxx>
+#include <vcl/weld.hxx>
+#include <avmedia/mediawindow.hxx>
+#include <svx/svdograf.hxx>
+#include <svx/fmmodel.hxx>
+#include <svx/fmview.hxx>
+#include <svx/galmisc.hxx>
+#include <svx/galtheme.hxx>
+#include <svx/svdpage.hxx>
+#include <svx/galleryobjectcollection.hxx>
+#include <galleryobjectbinarystorage.hxx>
+#include <galobj.hxx>
+#include <svx/gallery1.hxx>
+#include "gallerydrawmodel.hxx"
+#include <memory>
+
+using namespace ::com::sun::star;
+
+GalleryTheme::GalleryTheme( Gallery* pGallery, GalleryThemeEntry* pThemeEntry )
+ : pParent(pGallery)
+ , pThm(pThemeEntry)
+ , mnThemeLockCount(0)
+ , mnBroadcasterLockCount(0)
+ , nDragPos(0)
+ , bDragging(false)
+ , bAbortActualize(false)
+{
+ mpGalleryStorageEngine = pThm->createGalleryStorageEngine(maGalleryObjectCollection);
+}
+
+GalleryTheme::~GalleryTheme()
+{
+ if(pThm->IsModified())
+ if(!mpGalleryStorageEngine->implWrite(*this, pThm))
+ ImplSetModified(false);
+
+ for (auto & pEntry : maGalleryObjectCollection.getObjectList())
+ {
+ Broadcast( GalleryHint( GalleryHintType::CLOSE_OBJECT, GetName(), pEntry.get() ) );
+ pEntry.reset();
+ }
+ maGalleryObjectCollection.clear();
+ mpGalleryStorageEngine->clearSotStorage();
+}
+
+void GalleryTheme::SetDestDir(const OUString& rDestDir, bool bRelative)
+{
+ mpGalleryStorageEngine->setDestDir(rDestDir, bRelative);
+}
+
+void GalleryTheme::ImplBroadcast(sal_uInt32 nUpdatePos)
+{
+ if( !IsBroadcasterLocked() )
+ {
+ if( GetObjectCount() && ( nUpdatePos >= GetObjectCount() ) )
+ nUpdatePos = GetObjectCount() - 1;
+
+ Broadcast( GalleryHint( GalleryHintType::THEME_UPDATEVIEW, GetName(), reinterpret_cast<void*>(nUpdatePos) ) );
+ }
+}
+
+bool GalleryTheme::UnlockTheme()
+{
+ DBG_ASSERT( mnThemeLockCount, "Theme is not locked" );
+
+ bool bRet = false;
+
+ if( mnThemeLockCount )
+ {
+ --mnThemeLockCount;
+ bRet = true;
+ }
+
+ return bRet;
+}
+
+void GalleryTheme::UnlockBroadcaster()
+{
+ DBG_ASSERT( mnBroadcasterLockCount, "Broadcaster is not locked" );
+
+ if( mnBroadcasterLockCount && !--mnBroadcasterLockCount )
+ ImplBroadcast( 0 );
+}
+
+bool GalleryTheme::InsertObject(const SgaObject& rObj, sal_uInt32 nInsertPos)
+{
+ if (!rObj.IsValid())
+ return false;
+
+ GalleryObject* pFoundEntry = nullptr;
+ sal_uInt32 iFoundPos = 0;
+ for (sal_uInt32 n = maGalleryObjectCollection.size(); iFoundPos < n; ++iFoundPos)
+ {
+ if (maGalleryObjectCollection.get(iFoundPos)->getURL() == rObj.GetURL())
+ {
+ pFoundEntry = maGalleryObjectCollection.get(iFoundPos).get();
+ break;
+ }
+ }
+
+ mpGalleryStorageEngine->insertObject(rObj, pFoundEntry, nInsertPos);
+
+ ImplSetModified(true);
+ ImplBroadcast(pFoundEntry? iFoundPos: nInsertPos);
+
+ return true;
+}
+
+std::unique_ptr<SgaObject> GalleryTheme::AcquireObject(sal_uInt32 nPos)
+{
+ return mpGalleryStorageEngine->implReadSgaObject(maGalleryObjectCollection.getForPosition(nPos));
+}
+
+void GalleryTheme::GetPreviewBitmapExAndStrings(sal_uInt32 nPos, BitmapEx& rBitmapEx, Size& rSize, OUString& rTitle, OUString& rPath)
+{
+ const GalleryObject* pGalleryObject = maGalleryObjectCollection.get(nPos).get();
+
+ rBitmapEx = pGalleryObject->maPreviewBitmapEx;
+ rSize = pGalleryObject->maPreparedSize;
+ rTitle = pGalleryObject->maTitle;
+ rPath = pGalleryObject->maPath;
+}
+
+void GalleryTheme::SetPreviewBitmapExAndStrings(sal_uInt32 nPos, const BitmapEx& rBitmapEx, const Size& rSize, const OUString& rTitle, const OUString& rPath)
+{
+ GalleryObject* pGalleryObject = maGalleryObjectCollection.get(nPos).get();
+
+ pGalleryObject->maPreviewBitmapEx = rBitmapEx;
+ pGalleryObject->maPreparedSize = rSize;
+ pGalleryObject->maTitle = rTitle;
+ pGalleryObject->maPath = rPath;
+}
+
+void GalleryTheme::RemoveObject(sal_uInt32 nPos)
+{
+ auto it = maGalleryObjectCollection.getObjectList().begin() + nPos;
+ std::unique_ptr<GalleryObject> pEntry = std::move(*it);
+ maGalleryObjectCollection.getObjectList().erase( it );
+
+ mpGalleryStorageEngine->removeObject(pEntry);
+
+ Broadcast( GalleryHint( GalleryHintType::CLOSE_OBJECT, GetName(), pEntry.get() ) );
+ pEntry.reset();
+
+ ImplSetModified( true );
+ ImplBroadcast( nPos );
+}
+
+bool GalleryTheme::ChangeObjectPos(sal_uInt32 nOldPos, sal_uInt32 nNewPos)
+{
+ if (nOldPos == nNewPos || nOldPos >= maGalleryObjectCollection.size())
+ return false;
+
+ std::unique_ptr<GalleryObject> pEntry = std::move(maGalleryObjectCollection.get(nOldPos));
+
+ maGalleryObjectCollection.getObjectList().insert(maGalleryObjectCollection.getObjectList().begin() + nNewPos, std::move(pEntry));
+
+ if (nNewPos < nOldPos)
+ nOldPos++;
+
+ auto it = maGalleryObjectCollection.getObjectList().begin() + nOldPos;
+ maGalleryObjectCollection.getObjectList().erase(it);
+
+ ImplSetModified(true);
+ ImplBroadcast((nNewPos < nOldPos)? nNewPos: (nNewPos - 1));
+
+ return true;
+}
+
+void GalleryTheme::Actualize( const Link<const INetURLObject&, void>& rActualizeLink, GalleryProgress* pProgress )
+{
+ if( IsReadOnly() )
+ return;
+
+ Graphic aGraphic;
+ OUString aFormat;
+ const sal_uInt32 nCount = maGalleryObjectCollection.size();
+
+ LockBroadcaster();
+ bAbortActualize = false;
+
+ // reset delete flag
+ for (sal_uInt32 i = 0; i < nCount; i++)
+ maGalleryObjectCollection.get(i)->mbDelete = false;
+
+ for (sal_uInt32 i = 0; ( i < nCount ) && !bAbortActualize; i++)
+ {
+ if( pProgress )
+ pProgress->Update( i, nCount - 1 );
+
+ GalleryObject* pEntry = maGalleryObjectCollection.get(i).get();
+
+ const INetURLObject aURL( pEntry->getURL());
+
+ rActualizeLink.Call( aURL );
+
+ // SvDraw objects will be updated later
+ if( pEntry->eObjKind != SgaObjKind::SvDraw )
+ {
+ // Still a function should be implemented,
+ // which assigns files to the relevant entry.
+ // insert graphics as graphic objects into the gallery
+ if( pEntry->eObjKind == SgaObjKind::Sound )
+ {
+ SgaObjectSound aObjSound( aURL );
+ if( !InsertObject( aObjSound ) )
+ pEntry->mbDelete = true;
+ }
+ else
+ {
+ aGraphic.Clear();
+
+ if ( GalleryGraphicImport( aURL, aGraphic, aFormat ) != GalleryGraphicImportRet::IMPORT_NONE )
+ {
+ std::unique_ptr<SgaObject> pNewObj;
+
+ if ( SgaObjKind::Inet == pEntry->eObjKind )
+ pNewObj.reset(new SgaObjectINet( aGraphic, aURL ));
+ else if ( aGraphic.IsAnimated() )
+ pNewObj.reset(new SgaObjectAnim( aGraphic, aURL ));
+ else
+ pNewObj.reset(new SgaObjectBmp( aGraphic, aURL ));
+
+ if( !InsertObject( *pNewObj ) )
+ pEntry->mbDelete = true;
+ }
+ else
+ pEntry->mbDelete = true; // set delete flag
+ }
+ }
+ else
+ {
+ //update SvDraw object
+ if ( mpGalleryStorageEngine->GetSvDrawStorage().is() )
+ {
+ SgaObjectSvDraw aNewObj = mpGalleryStorageEngine->updateSvDrawObject(pEntry);
+ if (aNewObj.IsValid() && !InsertObject(aNewObj))
+ pEntry->mbDelete = true;
+ }
+ }
+ }
+
+ // remove all entries with set flag
+ for ( auto it = maGalleryObjectCollection.getObjectList().begin(); it != maGalleryObjectCollection.getObjectList().end(); /* increment is in the body of loop */)
+ {
+ if( (*it)->mbDelete )
+ {
+ Broadcast( GalleryHint( GalleryHintType::CLOSE_OBJECT, GetName(), it->get() ) );
+ it = maGalleryObjectCollection.getObjectList().erase( it );
+ }
+ else
+ ++it;
+ }
+
+ // update theme
+ mpGalleryStorageEngine->updateTheme();
+ ImplSetModified( true );
+ if (pThm->IsModified())
+ if (!mpGalleryStorageEngine->implWrite(*this, pThm))
+ ImplSetModified(false);
+ UnlockBroadcaster();
+}
+
+bool GalleryTheme::GetThumb(sal_uInt32 nPos, BitmapEx& rBmp)
+{
+ std::unique_ptr<SgaObject> pObj = AcquireObject( nPos );
+ bool bRet = false;
+
+ if( pObj )
+ {
+ rBmp = pObj->GetThumbBmp();
+ bRet = true;
+ }
+
+ return bRet;
+}
+
+bool GalleryTheme::GetGraphic(sal_uInt32 nPos, Graphic& rGraphic)
+{
+ const GalleryObject* pObject = maGalleryObjectCollection.getForPosition( nPos );
+ bool bRet = false;
+
+ if( pObject )
+ {
+ const INetURLObject aURL( ImplGetURL( pObject ) );
+
+ switch( pObject->eObjKind )
+ {
+ case SgaObjKind::Bitmap:
+ case SgaObjKind::Animation:
+ case SgaObjKind::Inet:
+ {
+ OUString aFilterDummy;
+ bRet = ( GalleryGraphicImport( aURL, rGraphic, aFilterDummy ) != GalleryGraphicImportRet::IMPORT_NONE );
+ }
+ break;
+
+ case SgaObjKind::SvDraw:
+ {
+ SvxGalleryDrawModel aModel;
+
+ if( aModel.GetModel() )
+ {
+ if( GetModel( nPos, *aModel.GetModel() ) )
+ {
+ ImageMap aIMap;
+
+ if( CreateIMapGraphic( *aModel.GetModel(), rGraphic, aIMap ) )
+ bRet = true;
+ else
+ {
+ ScopedVclPtrInstance< VirtualDevice > pVDev;
+ pVDev->SetMapMode( MapMode( MapUnit::Map100thMM ) );
+ FmFormView aView(*aModel.GetModel(), pVDev);
+
+ aView.hideMarkHandles();
+ aView.ShowSdrPage(aView.GetModel()->GetPage(0));
+ aView.MarkAll();
+ rGraphic = aView.GetAllMarkedGraphic();
+ bRet = true;
+ }
+ }
+ }
+ }
+ break;
+
+ case SgaObjKind::Sound:
+ {
+ std::unique_ptr<SgaObject> pObj = AcquireObject( nPos );
+
+ if( pObj )
+ {
+ rGraphic = pObj->GetThumbBmp();
+ //Bitmap aBmp( pObj->GetThumbBmp() );
+ //aBmp.Replace( COL_LIGHTMAGENTA, COL_WHITE );
+ //rGraphic = aBmp;
+ bRet = true;
+ }
+ }
+ break;
+
+ default:
+ break;
+ }
+ }
+
+ return bRet;
+}
+
+bool GalleryTheme::InsertGraphic(const Graphic& rGraphic, sal_uInt32 nInsertPos)
+{
+ bool bRet = false;
+
+ if( rGraphic.GetType() != GraphicType::NONE )
+ {
+ ConvertDataFormat nExportFormat = ConvertDataFormat::Unknown;
+ const GfxLink aGfxLink( rGraphic.GetGfxLink() );
+
+ if( aGfxLink.GetDataSize() )
+ {
+ switch( aGfxLink.GetType() )
+ {
+ case GfxLinkType::EpsBuffer: nExportFormat = ConvertDataFormat::SVM; break;
+ case GfxLinkType::NativeGif: nExportFormat = ConvertDataFormat::GIF; break;
+
+ // #i15508# added BMP type
+ // could not find/trigger a call to this, but should do no harm
+ case GfxLinkType::NativeBmp: nExportFormat = ConvertDataFormat::BMP; break;
+
+ case GfxLinkType::NativeJpg: nExportFormat = ConvertDataFormat::JPG; break;
+ case GfxLinkType::NativePng: nExportFormat = ConvertDataFormat::PNG; break;
+ case GfxLinkType::NativeTif: nExportFormat = ConvertDataFormat::TIF; break;
+ case GfxLinkType::NativeWmf: nExportFormat = ConvertDataFormat::WMF; break;
+ case GfxLinkType::NativeMet: nExportFormat = ConvertDataFormat::MET; break;
+ case GfxLinkType::NativePct: nExportFormat = ConvertDataFormat::PCT; break;
+ case GfxLinkType::NativeSvg: nExportFormat = ConvertDataFormat::SVG; break;
+ case GfxLinkType::NativeWebp: nExportFormat = ConvertDataFormat::WEBP; break;
+ default:
+ break;
+ }
+ }
+ else
+ {
+ if( rGraphic.GetType() == GraphicType::Bitmap )
+ {
+ if( rGraphic.IsAnimated() )
+ nExportFormat = ConvertDataFormat::GIF;
+ else
+ nExportFormat = ConvertDataFormat::PNG;
+ }
+ else
+ nExportFormat = ConvertDataFormat::SVM;
+ }
+
+ const SgaObjectBmp aObjBmp = mpGalleryStorageEngine->insertGraphic(rGraphic, aGfxLink, nExportFormat, GetParent()->GetUserURL());
+
+ if (aObjBmp.IsValid())
+ bRet = InsertObject(aObjBmp, nInsertPos);
+ }
+
+ return bRet;
+}
+
+bool GalleryTheme::GetModel(sal_uInt32 nPos, SdrModel& rModel)
+{
+ const GalleryObject* pObject = maGalleryObjectCollection.getForPosition( nPos );
+ bool bRet = false;
+
+ if( pObject && ( SgaObjKind::SvDraw == pObject->eObjKind ) )
+ {
+ bRet = mpGalleryStorageEngine->readModel(pObject, rModel);
+ }
+
+ return bRet;
+}
+
+bool GalleryTheme::InsertModel(const FmFormModel& rModel, sal_uInt32 nInsertPos)
+{
+ bool bRet = false;
+ SgaObjectSvDraw aObjSvDraw = mpGalleryStorageEngine->insertModel(rModel, GetParent()->GetUserURL());
+ if(aObjSvDraw.IsValid())
+ bRet = InsertObject( aObjSvDraw, nInsertPos );
+ return bRet;
+}
+
+bool GalleryTheme::GetModelStream(sal_uInt32 nPos, tools::SvRef<SotTempStream> const & rxModelStream)
+{
+ const GalleryObject* pObject = maGalleryObjectCollection.getForPosition( nPos );
+ bool bRet = false;
+
+ if( pObject && ( SgaObjKind::SvDraw == pObject->eObjKind ) )
+ {
+ bRet = mpGalleryStorageEngine->readModelStream(pObject, rxModelStream);
+ }
+
+ return bRet;
+}
+
+bool GalleryTheme::InsertModelStream(const tools::SvRef<SotTempStream>& rxModelStream, sal_uInt32 nInsertPos)
+{
+ bool bRet = false;
+
+ const SgaObjectSvDraw aObjSvDraw = mpGalleryStorageEngine->insertModelStream(rxModelStream, GetParent()->GetUserURL());
+ if(aObjSvDraw.IsValid())
+ bRet = InsertObject( aObjSvDraw, nInsertPos );
+
+ return bRet;
+}
+
+bool GalleryTheme::GetURL(sal_uInt32 nPos, INetURLObject& rURL)
+{
+ const GalleryObject* pObject = maGalleryObjectCollection.getForPosition( nPos );
+ bool bRet = false;
+
+ if( pObject )
+ {
+ rURL = ImplGetURL( pObject );
+ bRet = true;
+ }
+
+ return bRet;
+}
+
+bool GalleryTheme::InsertURL(const INetURLObject& rURL, sal_uInt32 nInsertPos)
+{
+ Graphic aGraphic;
+ OUString aFormat;
+ std::unique_ptr<SgaObject> pNewObj;
+ const GalleryGraphicImportRet nImportRet = GalleryGraphicImport( rURL, aGraphic, aFormat );
+ bool bRet = false;
+
+ if( nImportRet != GalleryGraphicImportRet::IMPORT_NONE )
+ {
+ if ( aGraphic.IsAnimated() )
+ pNewObj.reset(new SgaObjectAnim( aGraphic, rURL ));
+ else
+ pNewObj.reset(new SgaObjectBmp( aGraphic, rURL ));
+ }
+#if HAVE_FEATURE_AVMEDIA
+ else if( ::avmedia::MediaWindow::isMediaURL( rURL.GetMainURL( INetURLObject::DecodeMechanism::Unambiguous ), ""/*TODO?*/ ) )
+ pNewObj.reset(new SgaObjectSound( rURL ));
+#endif
+ if( pNewObj && InsertObject( *pNewObj, nInsertPos ) )
+ bRet = true;
+
+ return bRet;
+}
+
+bool GalleryTheme::InsertFileOrDirURL(const INetURLObject& rFileOrDirURL, sal_uInt32 nInsertPos)
+{
+ bool bRet = false;
+ std::vector< INetURLObject > aURLVector;
+ GalleryBinaryEngine::insertFileOrDirURL(rFileOrDirURL, aURLVector);
+
+ for( const auto& rURL : aURLVector )
+ bRet = bRet || InsertURL( rURL, nInsertPos );
+
+ return bRet;
+}
+
+bool GalleryTheme::InsertTransferable(const uno::Reference< datatransfer::XTransferable >& rxTransferable, sal_uInt32 nInsertPos)
+{
+ bool bRet = false;
+
+ if( rxTransferable.is() )
+ {
+ TransferableDataHelper aDataHelper( rxTransferable );
+ std::unique_ptr<Graphic> pGraphic;
+
+ if( aDataHelper.HasFormat( SotClipboardFormatId::DRAWING ) )
+ {
+ tools::SvRef<SotTempStream> xModelStm;
+
+ if( aDataHelper.GetSotStorageStream( SotClipboardFormatId::DRAWING, xModelStm ) )
+ bRet = InsertModelStream( xModelStm, nInsertPos );
+ }
+ else if( aDataHelper.HasFormat( SotClipboardFormatId::FILE_LIST ) ||
+ aDataHelper.HasFormat( SotClipboardFormatId::SIMPLE_FILE ) )
+ {
+ FileList aFileList;
+
+ if( aDataHelper.HasFormat( SotClipboardFormatId::FILE_LIST ) )
+ aDataHelper.GetFileList( SotClipboardFormatId::FILE_LIST, aFileList );
+ else
+ {
+ OUString aFile;
+ if (aDataHelper.GetString(SotClipboardFormatId::SIMPLE_FILE, aFile) && !aFile.isEmpty())
+ aFileList.AppendFile( aFile );
+ }
+
+ for( sal_uInt32 i = 0, nCount = aFileList.Count(); i < nCount; ++i )
+ {
+ const OUString aFile( aFileList.GetFile( i ) );
+ INetURLObject aURL( aFile );
+
+ if( aURL.GetProtocol() == INetProtocol::NotValid )
+ {
+ OUString aLocalURL;
+
+ if( osl::FileBase::getFileURLFromSystemPath( aFile, aLocalURL ) == osl::FileBase::E_None )
+ aURL = INetURLObject( aLocalURL );
+ }
+
+ if( aURL.GetProtocol() != INetProtocol::NotValid )
+ bRet = InsertFileOrDirURL( aURL, nInsertPos );
+ }
+ }
+ else
+ {
+ Graphic aGraphic;
+ SotClipboardFormatId nFormat = SotClipboardFormatId::NONE;
+
+ if( aDataHelper.HasFormat( SotClipboardFormatId::SVXB ) )
+ nFormat = SotClipboardFormatId::SVXB;
+ else if( aDataHelper.HasFormat( SotClipboardFormatId::GDIMETAFILE ) )
+ nFormat = SotClipboardFormatId::GDIMETAFILE;
+ else if( aDataHelper.HasFormat( SotClipboardFormatId::BITMAP ) )
+ nFormat = SotClipboardFormatId::BITMAP;
+
+ if( nFormat != SotClipboardFormatId::NONE && aDataHelper.GetGraphic( nFormat, aGraphic ) )
+ pGraphic.reset(new Graphic( aGraphic ));
+ }
+
+ if( pGraphic )
+ {
+ bRet = false;
+
+ if( aDataHelper.HasFormat( SotClipboardFormatId::SVIM ) )
+ {
+
+ ImageMap aImageMap;
+
+ // according to KA we don't need a BaseURL here
+ if( aDataHelper.GetImageMap( SotClipboardFormatId::SVIM, aImageMap ) )
+ {
+ SvxGalleryDrawModel aModel;
+
+ if( aModel.GetModel() )
+ {
+ SdrPage* pPage = aModel.GetModel()->GetPage(0);
+ SdrGrafObj* pGrafObj = new SdrGrafObj(*aModel.GetModel(), *pGraphic );
+
+ pGrafObj->AppendUserData( std::unique_ptr<SdrObjUserData>(new SgaIMapInfo( aImageMap )) );
+ pPage->InsertObject( pGrafObj );
+ bRet = InsertModel( *aModel.GetModel(), nInsertPos );
+ }
+ }
+ }
+
+ if( !bRet )
+ bRet = InsertGraphic( *pGraphic, nInsertPos );
+ }
+ }
+
+ return bRet;
+}
+
+void GalleryTheme::CopyToClipboard(const weld::Widget& rWidget, sal_uInt32 nPos)
+{
+ rtl::Reference<GalleryTransferable> pTransferable = new GalleryTransferable( this, nPos, false );
+ pTransferable->CopyToClipboard(rWidget.get_clipboard());
+}
+
+DateTime GalleryTheme::getModificationDate() const
+{
+ return mpGalleryStorageEngine->getModificationDate();
+}
+
+SvStream& GalleryTheme::ReadData( SvStream& rIStm )
+{
+ sal_uInt32 nCount;
+ sal_uInt16 nVersion;
+
+ rIStm.ReadUInt16( nVersion );
+ read_uInt16_lenPrefixed_uInt8s_ToOString(rIStm);
+ rIStm.ReadUInt32( nCount );
+
+ if( nVersion >= 0x0004 )
+ {
+ sal_uInt16 nTmp16;
+ rIStm.ReadUInt16( nTmp16 );
+ }
+
+ if( nCount <= ( 1 << 14 ) )
+ {
+ INetURLObject aRelURL1( GetParent()->GetRelativeURL() );
+ INetURLObject aRelURL2( GetParent()->GetUserURL() );
+ sal_uInt32 nId1, nId2;
+ bool bRel;
+
+ for(auto & i : maGalleryObjectCollection.getObjectList())
+ {
+ GalleryObject* pObj = i.get();
+ Broadcast( GalleryHint( GalleryHintType::CLOSE_OBJECT, GetName(), pObj ) );
+ i.reset();
+ }
+ maGalleryObjectCollection.clear();
+
+ for( sal_uInt32 i = 0; i < nCount; i++ )
+ {
+ std::unique_ptr<GalleryObject> pObj(new GalleryObject);
+
+ OUString aFileName;
+ sal_uInt16 nTemp;
+
+ rIStm.ReadCharAsBool( bRel );
+ OString aTempFileName = read_uInt16_lenPrefixed_uInt8s_ToOString(rIStm);
+ rIStm.ReadUInt32( pObj->nOffset );
+ rIStm.ReadUInt16( nTemp ); pObj->eObjKind = static_cast<SgaObjKind>(nTemp);
+
+ aFileName = OStringToOUString(aTempFileName, osl_getThreadTextEncoding());
+
+ pObj->m_pGalleryObjectStorage.reset();
+ pObj->m_pGalleryObjectStorage = std::make_unique<GalleryObjectBinaryStorage>();
+
+ if( bRel )
+ {
+ aFileName = aFileName.replaceAll( "\\", "/" );
+ OUString aPath = aRelURL1.GetMainURL( INetURLObject::DecodeMechanism::NONE );
+
+ if( aFileName[ 0 ] != '/' )
+ aPath += "/";
+
+ aPath += aFileName;
+
+ pObj->m_pGalleryObjectStorage->setURL(INetURLObject(aPath));
+
+ if (!FileExists(pObj->getURL()))
+ {
+ aPath = aRelURL2.GetMainURL( INetURLObject::DecodeMechanism::NONE );
+
+ if( aFileName[0] != '/' )
+ aPath += "/";
+
+ aPath += aFileName;
+
+ // assign this URL, even in the case it is not valid (#94482)
+ pObj->m_pGalleryObjectStorage->setURL(INetURLObject(aPath));
+ }
+ }
+ else
+ {
+ if( SgaObjKind::SvDraw == pObj->eObjKind )
+ {
+ OUString aDummyURL = "gallery/svdraw/" + aFileName;
+ pObj->m_pGalleryObjectStorage->setURL(INetURLObject(aDummyURL, INetProtocol::PrivSoffice));
+ }
+ else
+ {
+ OUString aLocalURL;
+
+ pObj->m_pGalleryObjectStorage->setURL(INetURLObject(aFileName));
+
+ if( ( pObj->getURL().GetProtocol() == INetProtocol::NotValid ) &&
+ osl::FileBase::getFileURLFromSystemPath( aFileName, aLocalURL ) == osl::FileBase::E_None )
+ {
+ pObj->m_pGalleryObjectStorage->setURL(INetURLObject(aLocalURL));
+ }
+ }
+ }
+ maGalleryObjectCollection.getObjectList().push_back( std::move(pObj) );
+ }
+
+ rIStm.ReadUInt32( nId1 ).ReadUInt32( nId2 );
+
+ // In newer versions a 512 byte reserve buffer is located at the end,
+ // the data is located at the beginning of this buffer and are clamped
+ // by a VersionCompatRead.
+ if( !rIStm.eof() &&
+ nId1 == COMPAT_FORMAT( 'G', 'A', 'L', 'R' ) &&
+ nId2 == COMPAT_FORMAT( 'E', 'S', 'R', 'V' ) )
+ {
+ VersionCompatRead aCompat(rIStm);
+ sal_uInt32 nTemp32;
+ bool bThemeNameFromResource = false;
+
+ rIStm.ReadUInt32( nTemp32 );
+
+ if( aCompat.GetVersion() >= 2 )
+ {
+ rIStm.ReadCharAsBool( bThemeNameFromResource );
+ }
+
+ SetId( nTemp32, bThemeNameFromResource );
+ }
+ }
+ else
+ rIStm.SetError( SVSTREAM_READ_ERROR );
+
+ ImplSetModified( false );
+
+ return rIStm;
+}
+
+void GalleryTheme::ImplSetModified( bool bModified )
+{
+ pThm->SetModified(bModified);
+}
+
+sal_uInt32 GalleryTheme::GetId() const { return pThm->GetId(); }
+void GalleryTheme::SetId( sal_uInt32 nNewId, bool bResetThemeName ) { pThm->SetId( nNewId, bResetThemeName ); }
+bool GalleryTheme::IsReadOnly() const { return pThm->IsReadOnly(); }
+bool GalleryTheme::IsDefault() const { return pThm->IsDefault(); }
+
+const OUString& GalleryTheme::GetName() const { return pThm->GetThemeName(); }
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/gengal/gengal.cxx b/svx/source/gengal/gengal.cxx
new file mode 100644
index 000000000..95c415d60
--- /dev/null
+++ b/svx/source/gengal/gengal.cxx
@@ -0,0 +1,326 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+#include <sal/config.h>
+#include <sal/log.hxx>
+
+#include <stdio.h>
+
+#include <vector>
+
+#include <comphelper/processfactory.hxx>
+#include <cppuhelper/bootstrap.hxx>
+#include <com/sun/star/lang/XMultiServiceFactory.hpp>
+#include <com/sun/star/ucb/UniversalContentBroker.hpp>
+#include <com/sun/star/frame/Desktop.hpp>
+
+#include <tools/urlobj.hxx>
+#include <vcl/vclmain.hxx>
+
+#include <osl/file.hxx>
+#include <osl/process.h>
+#include <sfx2/app.hxx>
+#include <sal/types.h>
+#include <tools/diagnose_ex.h>
+#include <vcl/svapp.hxx>
+
+#include <svx/galtheme.hxx>
+#include <svx/gallery1.hxx>
+
+using namespace ::com::sun::star;
+
+namespace {
+
+class GalApp : public Application
+{
+ bool mbInBuildTree;
+ bool mbRelativeURLs;
+public:
+ GalApp() : mbInBuildTree( false ), mbRelativeURLs( false )
+ {
+ }
+ virtual int Main() override;
+
+protected:
+ uno::Reference<lang::XMultiServiceFactory> xMSF;
+ void Init() override;
+ void DeInit() override;
+};
+
+}
+
+static void createTheme( const OUString& aThemeName, std::u16string_view aGalleryURL,
+ const OUString& aDestDir, std::vector<INetURLObject> &rFiles,
+ bool bRelativeURLs )
+{
+ std::unique_ptr<Gallery> pGallery(new Gallery( aGalleryURL ));
+
+ fprintf( stderr, "Work on gallery '%s'\n",
+ OUStringToOString( aGalleryURL, RTL_TEXTENCODING_UTF8 ).getStr() );
+
+ fprintf( stderr, "Existing themes: %" SAL_PRI_SIZET "u\n",
+ pGallery->GetThemeCount() );
+
+ GalleryTheme *pGalTheme;
+ if( !pGallery->HasTheme( aThemeName) ) {
+ if( !pGallery->CreateTheme( aThemeName ) ) {
+ fprintf( stderr, "Failed to create theme\n" );
+ exit( 1 );
+ }
+ }
+
+ fprintf( stderr, "Existing themes: %" SAL_PRI_SIZET "u\n",
+ pGallery->GetThemeCount() );
+
+ SfxListener aListener;
+
+ pGalTheme = pGallery->AcquireTheme( aThemeName, aListener );
+ if ( !pGalTheme ) {
+ fprintf( stderr, "Failed to acquire theme\n" );
+ exit( 1 );
+ }
+
+ fprintf( stderr, "Using DestDir: %s\n",
+ OUStringToOString( aDestDir, RTL_TEXTENCODING_UTF8 ).getStr() );
+ pGalTheme->SetDestDir( aDestDir, bRelativeURLs );
+
+ for( const auto& rFile : rFiles )
+ {
+// Should/could use:
+// if ( ! pGalTheme->InsertFileOrDirURL( aURL ) ) {
+// Requires a load more components ...
+
+ if ( ! pGalTheme->InsertURL( rFile ) )
+ fprintf( stderr, "Failed to import '%s'\n",
+ OUStringToOString( rFile.GetMainURL(INetURLObject::DecodeMechanism::NONE), RTL_TEXTENCODING_UTF8 ).getStr() );
+ else
+ fprintf( stderr, "Imported file '%s' (%" SAL_PRIuUINT32 ")\n",
+ OUStringToOString( rFile.GetMainURL(INetURLObject::DecodeMechanism::NONE), RTL_TEXTENCODING_UTF8 ).getStr(),
+ pGalTheme->GetObjectCount() );
+ }
+
+ pGallery->ReleaseTheme( pGalTheme, aListener );
+}
+
+static int PrintHelp()
+{
+ fprintf( stdout, "Utility to generate LibreOffice gallery files\n\n" );
+
+ fprintf( stdout, "using: gengal --name <name> --path <dir> [ --destdir <path> ]\n");
+ fprintf( stdout, " [ files ... ]\n\n" );
+
+ fprintf( stdout, "options:\n");
+ fprintf( stdout, " --name <theme>\t\tdefines the user visible name of the created or updated theme.\n");
+
+ fprintf( stdout, " --path <dir>\t\tdefines directory where the gallery files are created\n");
+ fprintf( stdout, "\t\t\tor updated.\n");
+
+ fprintf( stdout, " --destdir <dir>\tdefines a path prefix to be removed from the paths\n");
+ fprintf( stdout, "\t\t\tstored in the gallery files. It is useful to create\n");
+ fprintf( stdout, "\t\t\tRPM packages using the BuildRoot feature.\n");
+
+ fprintf( stdout, " --relative-urls\t\tflags that after removing the destdir, the URL should be a path relative to the gallery folder in the install\n");
+ fprintf( stdout, "\t\t\tprimarily used for internal gallery generation at compile time.\n");
+ fprintf( stdout, "\t\t\ttheme files.\n");
+ fprintf( stdout, " files\t\t\tlists files to be added to the gallery. Absolute paths\n");
+ fprintf( stdout, "\t\t\tare required.\n");
+ // --build-tree not documented - only useful during the build ...
+
+ return EXIT_SUCCESS;
+}
+
+static INetURLObject Smartify( std::u16string_view rPath )
+{
+ INetURLObject aURL;
+ aURL.SetSmartURL( rPath );
+ return aURL;
+}
+
+void GalApp::Init()
+{
+ try {
+ if( !mbInBuildTree && getenv( "OOO_INSTALL_PREFIX" ) == nullptr ) {
+ OUString fileName = GetAppFileName();
+ int lastSlash = fileName.lastIndexOf( '/' );
+#ifdef _WIN32
+ // Don't know which directory separators GetAppFileName() returns on Windows.
+ // Be safe and take into consideration they might be backslashes.
+ if( fileName.lastIndexOf( '\\' ) > lastSlash )
+ lastSlash = fileName.lastIndexOf( '\\' );
+#endif
+ OUString baseBinDir = fileName.copy( 0, lastSlash );
+ OUString installPrefix = baseBinDir + "/../..";
+
+ OUString envVar( "OOO_INSTALL_PREFIX");
+ osl_setEnvironment(envVar.pData, installPrefix.pData);
+ }
+ SAL_INFO("svx", "OOO_INSTALL_PREFIX=" << getenv( "OOO_INSTALL_PREFIX" ) );
+
+ uno::Reference<uno::XComponentContext> xComponentContext
+ = ::cppu::defaultBootstrap_InitialComponentContext();
+ xMSF.set( xComponentContext->getServiceManager(), uno::UNO_QUERY );
+ if( !xMSF.is() )
+ {
+ fprintf( stderr, "Failed to bootstrap\n" );
+ exit( 1 );
+ }
+ ::comphelper::setProcessServiceFactory( xMSF );
+
+ // For backwards compatibility, in case some code still uses plain
+ // createInstance w/o args directly to obtain an instance:
+ css::ucb::UniversalContentBroker::create(xComponentContext);
+ } catch (const uno::Exception &e) {
+ fprintf( stderr, "Bootstrap exception '%s'\n",
+ OUStringToOString( e.Message, RTL_TEXTENCODING_UTF8 ).getStr() );
+ exit( 1 );
+ }
+}
+
+static std::vector<OUString> ReadResponseFile_Impl(OUString const& rInput)
+{
+ osl::File file(rInput);
+ osl::FileBase::RC rc = file.open(osl_File_OpenFlag_Read);
+ OString const uInput(OUStringToOString(rInput, RTL_TEXTENCODING_UTF8));
+ if (osl::FileBase::E_None != rc)
+ {
+ fprintf(stderr, "error while opening response file: %s (%d)\n",
+ uInput.getStr(), rc);
+ exit(1);
+ }
+
+ std::vector<OUString> ret;
+ OUStringBuffer b(256);
+ char buf[1<<16];
+ while (true)
+ {
+ sal_uInt64 size(0);
+ rc = file.read(buf, sizeof(buf), size);
+ if (osl::FileBase::E_None != rc)
+ {
+ fprintf(stderr, "error while reading response file: %s (%d)\n",
+ uInput.getStr(), rc);
+ exit(1);
+ }
+ if (!size)
+ break;
+ for (sal_uInt64 i = 0; i < size; ++i)
+ {
+ if (static_cast<unsigned char>(buf[i]) >= 128)
+ {
+ fprintf(stderr, "non-ASCII character in response file: %s\n",
+ uInput.getStr());
+ exit(1);
+ }
+ switch (buf[i])
+ {
+ case ' ' :
+ case '\t':
+ case '\r':
+ case '\n':
+ if (!b.isEmpty())
+ ret.push_back(b.makeStringAndClear());
+ break;
+ default:
+ b.append(buf[i]);
+ break;
+ }
+ }
+ }
+ if (!b.isEmpty())
+ ret.push_back(b.makeStringAndClear());
+ return ret;
+}
+
+static void
+ReadResponseFile(std::vector<INetURLObject> & rFiles, OUString const& rInput)
+{
+ std::vector<OUString> files(ReadResponseFile_Impl(rInput));
+ for (size_t i = 0; i < files.size(); ++i)
+ {
+ rFiles.push_back(Smartify(files[i]));
+ }
+}
+
+int GalApp::Main()
+{
+ try
+ {
+ SfxApplication::GetOrCreate();
+
+ OUString aPath, aDestDir;
+ OUString aName( "Default name" );
+ std::vector<INetURLObject> aFiles;
+
+ for( sal_uInt16 i = 0; i < GetCommandLineParamCount(); ++i )
+ {
+ OUString aParam = GetCommandLineParam( i );
+
+ if ( aParam.startsWith( "-env:" ) )
+ continue;
+ else if ( aParam == "--help" || aParam == "-h" )
+ return PrintHelp();
+ else if ( aParam == "--build-tree" )
+ {
+ mbRelativeURLs = true;
+ mbInBuildTree = true;
+ }
+ else if ( aParam == "--name" )
+ aName = GetCommandLineParam( ++i );
+ else if ( aParam == "--path" )
+ aPath = Smartify( GetCommandLineParam( ++i ) ).
+ GetMainURL(INetURLObject::DecodeMechanism::NONE);
+ else if ( aParam == "--destdir" )
+ aDestDir = GetCommandLineParam( ++i );
+ else if ( aParam == "--relative-urls" )
+ mbRelativeURLs = true;
+ else if ( aParam == "--number-from" )
+ fprintf ( stderr, "--number-from is deprecated, themes now "
+ "have filenames based on their names\n" );
+ else if ( aParam == "--filenames" )
+ ReadResponseFile(aFiles, GetCommandLineParam(++i));
+ else
+ aFiles.push_back( Smartify( aParam ) );
+ }
+
+ if( aFiles.empty() )
+ return PrintHelp();
+
+ createTheme( aName, aPath, aDestDir, aFiles, mbRelativeURLs );
+ }
+ catch (const uno::Exception&)
+ {
+ TOOLS_WARN_EXCEPTION("svx", "Fatal");
+ return EXIT_FAILURE;
+ }
+ catch (const std::exception &e)
+ {
+ SAL_WARN("svx", "Fatal: " << e.what());
+ return 1;
+ }
+
+ return EXIT_SUCCESS;
+}
+
+void GalApp::DeInit()
+{
+ auto xDesktop = css::frame::Desktop::create(comphelper::getProcessComponentContext());
+ xDesktop->terminate();
+ uno::Reference< lang::XComponent >(
+ comphelper::getProcessComponentContext(),
+ uno::UNO_QUERY_THROW )-> dispose();
+ ::comphelper::setProcessServiceFactory( nullptr );
+}
+
+void vclmain::createApplication()
+{
+ Application::EnableConsoleOnly();
+ static GalApp aGalApp;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/gengal/gengal.sh b/svx/source/gengal/gengal.sh
new file mode 100755
index 000000000..245c171c3
--- /dev/null
+++ b/svx/source/gengal/gengal.sh
@@ -0,0 +1,95 @@
+#!/bin/sh
+#
+# This file is part of the LibreOffice project.
+#
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+#
+# This file incorporates work covered by the following license notice:
+#
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements. See the NOTICE file distributed
+# with this work for additional information regarding copyright
+# ownership. The ASF licenses this file to you under the Apache
+# License, Version 2.0 (the "License"); you may not use this file
+# except in compliance with the License. You may obtain a copy of
+# the License at http://www.apache.org/licenses/LICENSE-2.0 .
+#
+
+# enable file locking
+SAL_ENABLE_FILE_LOCKING=1
+export SAL_ENABLE_FILE_LOCKING
+
+# resolve installation directory
+sd_cwd=$(pwd)
+sd_res=$0
+while [ -h "$sd_res" ] ; do
+ cd "$(dirname "$sd_res")"
+ sd_basename=$(basename "$sd_res")
+ sd_res=$(ls -l "$sd_basename" | sed "s/.*$sd_basename -> //g")
+done
+cd "$(dirname "$sd_res")"
+sd_prog=$(pwd)
+cd "$sd_cwd"
+
+# this is a temporary hack until we can live with the default search paths
+case "$(uname -s)" in
+OpenBSD)
+ LD_LIBRARY_PATH=$sd_prog${LD_LIBRARY_PATH:+:${LD_LIBRARY_PATH}}
+ JAVA_HOME=$(javaPathHelper -h libreoffice-java 2> /dev/null)
+ export LD_LIBRARY_PATH
+ if [ -n "${JAVA_HOME}" ]; then
+ export JAVA_HOME
+ fi
+ ;;
+NetBSD|FreeBSD|DragonFly)
+ LD_LIBRARY_PATH=$sd_prog${LD_LIBRARY_PATH:+:${LD_LIBRARY_PATH}}
+ export LD_LIBRARY_PATH
+ ;;
+AIX)
+ LIBPATH=$sd_prog${LIBPATH:+:${LIBPATH}}
+ export LIBPATH
+ ;;
+esac
+
+#collect all bootstrap variables specified on the command line
+#so that they can be passed as arguments to javaldx later on
+for arg in "$@"
+do
+ case "$arg" in
+ -env:*) BOOTSTRAPVARS=$BOOTSTRAPVARS" ""$arg";;
+ esac
+done
+
+# extend the ld_library_path for java: javaldx checks the sofficerc for us
+if [ -x "$sd_prog/javaldx" ] ; then
+ my_path=$("$sd_prog/javaldx" $BOOTSTRAPVARS \
+ "-env:INIFILENAME=vnd.sun.star.pathname:$sd_prog/redirectrc")
+ if [ -n "$my_path" ] ; then
+ sd_platform=$(uname -s)
+ case $sd_platform in
+ AIX)
+ LIBPATH=$my_path${LIBPATH:+:$LIBPATH}
+ export LIBPATH
+ ;;
+ *)
+ LD_LIBRARY_PATH=$my_path${LD_LIBRARY_PATH:+:$LD_LIBRARY_PATH}
+ export LD_LIBRARY_PATH
+ ;;
+ esac
+ fi
+fi
+
+unset XENVIRONMENT
+
+# uncomment line below to disable anti aliasing of fonts
+# SAL_ANTIALIAS_DISABLE=true; export SAL_ANTIALIAS_DISABLE
+
+# uncomment line below if you encounter problems starting soffice on your system
+# SAL_NO_XINITTHREADS=true; export SAL_NO_XINITTHREADS
+
+# execute binary
+exec "$sd_prog/gengal.bin" "$@" \
+ "-env:INIFILENAME=vnd.sun.star.pathname:$sd_prog/redirectrc"
+
diff --git a/svx/source/inc/AccessibleFrameSelector.hxx b/svx/source/inc/AccessibleFrameSelector.hxx
new file mode 100644
index 000000000..26f30aa67
--- /dev/null
+++ b/svx/source/inc/AccessibleFrameSelector.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_SVX_SOURCE_INC_ACCESSIBLEFRAMESELECTOR_HXX
+#define INCLUDED_SVX_SOURCE_INC_ACCESSIBLEFRAMESELECTOR_HXX
+
+#include <com/sun/star/accessibility/XAccessible.hpp>
+#include <com/sun/star/accessibility/XAccessibleContext.hpp>
+#include <cppuhelper/implbase1.hxx>
+#include<comphelper/accessiblecomponenthelper.hxx>
+#include <svx/framebordertype.hxx>
+
+namespace svx {
+
+class FrameSelector;
+
+namespace a11y {
+
+typedef ::cppu::ImplHelper1<css::accessibility::XAccessible> OAccessibleHelper_Base;
+
+class AccFrameSelector final : public ::comphelper::OAccessibleComponentHelper,
+ public OAccessibleHelper_Base
+{
+public:
+ explicit AccFrameSelector(FrameSelector& rFrameSel);
+ virtual ~AccFrameSelector() override;
+
+ DECLARE_XINTERFACE( )
+ DECLARE_XTYPEPROVIDER( )
+
+ //XAccessibleComponent
+ virtual void SAL_CALL grabFocus( ) override;
+ virtual css::uno::Reference< css::accessibility::XAccessible > SAL_CALL getAccessibleAtPoint( const css::awt::Point& aPoint ) override;
+
+ //XAccessibleContext
+ virtual sal_Int32 SAL_CALL getAccessibleChildCount( ) override;
+ virtual css::uno::Reference< css::accessibility::XAccessible > SAL_CALL getAccessibleChild( sal_Int32 i ) override;
+ virtual css::uno::Reference< css::accessibility::XAccessible > SAL_CALL getAccessibleParent( ) override;
+ virtual sal_Int16 SAL_CALL getAccessibleRole( ) override;
+ virtual OUString SAL_CALL getAccessibleDescription( ) override;
+ virtual OUString SAL_CALL getAccessibleName( ) override;
+ virtual css::uno::Reference< css::accessibility::XAccessibleRelationSet > SAL_CALL getAccessibleRelationSet( ) override;
+ virtual css::uno::Reference< css::accessibility::XAccessibleStateSet > SAL_CALL getAccessibleStateSet( ) override;
+
+ //XAccessible
+ virtual css::uno::Reference< css::accessibility::XAccessibleContext > SAL_CALL getAccessibleContext( ) override;
+
+ virtual sal_Int32 SAL_CALL getForeground( ) override;
+ virtual sal_Int32 SAL_CALL getBackground( ) override;
+
+ void Invalidate();
+
+private:
+ // OCommonAccessibleComponent
+ /// implements the calculation of the bounding rectangle
+ virtual css::awt::Rectangle implGetBounds( ) override;
+
+ /// @throws css::uno::RuntimeException
+ void IsValid();
+
+ FrameSelector* mpFrameSel;
+};
+
+class AccFrameSelectorChild final : public ::comphelper::OAccessibleComponentHelper,
+ public OAccessibleHelper_Base
+{
+public:
+ explicit AccFrameSelectorChild( FrameSelector& rFrameSel, FrameBorderType eBorder );
+
+ virtual ~AccFrameSelectorChild() override;
+
+ DECLARE_XINTERFACE( )
+ DECLARE_XTYPEPROVIDER( )
+
+ //XAccessibleComponent
+ virtual void SAL_CALL grabFocus( ) override;
+ virtual css::uno::Reference< css::accessibility::XAccessible > SAL_CALL getAccessibleAtPoint( const css::awt::Point& aPoint ) override;
+
+ //XAccessibleContext
+ virtual sal_Int32 SAL_CALL getAccessibleChildCount( ) override;
+ virtual css::uno::Reference< css::accessibility::XAccessible > SAL_CALL getAccessibleChild( sal_Int32 i ) override;
+ virtual css::uno::Reference< css::accessibility::XAccessible > SAL_CALL getAccessibleParent( ) override;
+ virtual sal_Int16 SAL_CALL getAccessibleRole( ) override;
+ virtual OUString SAL_CALL getAccessibleDescription( ) override;
+ virtual OUString SAL_CALL getAccessibleName( ) override;
+ virtual css::uno::Reference< css::accessibility::XAccessibleRelationSet > SAL_CALL getAccessibleRelationSet( ) override;
+ virtual css::uno::Reference< css::accessibility::XAccessibleStateSet > SAL_CALL getAccessibleStateSet( ) override;
+
+ //XAccessible
+ virtual css::uno::Reference< css::accessibility::XAccessibleContext > SAL_CALL getAccessibleContext( ) override;
+
+ virtual sal_Int32 SAL_CALL getForeground( ) override;
+ virtual sal_Int32 SAL_CALL getBackground( ) override;
+
+ void NotifyAccessibleEvent(const sal_Int16 _nEventId, const css::uno::Any& _rOldValue, const css::uno::Any& _rNewValue)
+ {
+ ::comphelper::OAccessibleComponentHelper::NotifyAccessibleEvent(_nEventId, _rOldValue, _rNewValue);
+ }
+
+ void Invalidate();
+
+private:
+ // OCommonAccessibleComponent
+ /// implements the calculation of the bounding rectangle
+ virtual css::awt::Rectangle implGetBounds( ) override;
+
+ /// @throws css::uno::RuntimeException
+ void IsValid();
+
+ FrameSelector* mpFrameSel;
+
+ FrameBorderType meBorder;
+};
+
+
+}
+}
+
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/inc/DefaultShapesPanel.hxx b/svx/source/inc/DefaultShapesPanel.hxx
new file mode 100644
index 000000000..71396c5ce
--- /dev/null
+++ b/svx/source/inc/DefaultShapesPanel.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_SVX_SOURCE_INC_DEFAULTSHAPESPANEL_HXX
+#define INCLUDED_SVX_SOURCE_INC_DEFAULTSHAPESPANEL_HXX
+
+#include <com/sun/star/frame/XFrame.hpp>
+#include <sfx2/sidebar/PanelLayout.hxx>
+#include <svtools/valueset.hxx>
+#include <map>
+#include "ShapesUtil.hxx"
+
+using namespace css;
+using namespace ::com::sun::star::uno;
+using namespace ::com::sun::star::frame;
+
+namespace svx::sidebar {
+
+/** This panel provides buttons for inserting shapes into a document.
+*/
+class DefaultShapesPanel final
+ : public PanelLayout, public SvxShapeCommandsMap
+{
+public:
+ DefaultShapesPanel (
+ weld::Widget* pParent,
+ const css::uno::Reference<css::frame::XFrame>& rxFrame);
+ static std::unique_ptr<PanelLayout> Create(
+ weld::Widget* pParent,
+ const css::uno::Reference<css::frame::XFrame>& rxFrame);
+ virtual ~DefaultShapesPanel() override;
+
+private:
+ std::unique_ptr<ValueSet> mxLineArrowSet;
+ std::unique_ptr<weld::CustomWeld> mxLineArrowSetWin;
+ std::unique_ptr<ValueSet> mxCurveSet;
+ std::unique_ptr<weld::CustomWeld> mxCurveSetWin;
+ std::unique_ptr<ValueSet> mxConnectorSet;
+ std::unique_ptr<weld::CustomWeld> mxConnectorSetWin;
+ std::unique_ptr<ValueSet> mxBasicShapeSet;
+ std::unique_ptr<weld::CustomWeld> mxBasicShapeSetWin;
+ std::unique_ptr<ValueSet> mxSymbolShapeSet;
+ std::unique_ptr<weld::CustomWeld> mxSymbolShapeSetWin;
+ std::unique_ptr<ValueSet> mxBlockArrowSet;
+ std::unique_ptr<weld::CustomWeld> mxBlockArrowSetWin;
+ std::unique_ptr<ValueSet> mxFlowchartSet;
+ std::unique_ptr<weld::CustomWeld> mxFlowchartSetWin;
+ std::unique_ptr<ValueSet> mxCalloutSet;
+ std::unique_ptr<weld::CustomWeld> mxCalloutSetWin;
+ std::unique_ptr<ValueSet> mxStarSet;
+ std::unique_ptr<weld::CustomWeld> mxStarSetWin;
+ std::unique_ptr<ValueSet> mx3DObjectSet;
+ std::unique_ptr<weld::CustomWeld> mx3DObjectSetWin;
+
+ Reference< XFrame > mxFrame;
+ std::map<ValueSet*, std::map<sal_uInt16, OUString>> mpShapesSetMap;
+
+ void populateShapes();
+ void Initialize();
+ DECL_LINK( ShapeSelectHdl, ValueSet*, void );
+};
+
+} // end of namespace sd::sidebar
+
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/inc/GraphCtlAccessibleContext.hxx b/svx/source/inc/GraphCtlAccessibleContext.hxx
new file mode 100644
index 000000000..5e77d3a11
--- /dev/null
+++ b/svx/source/inc/GraphCtlAccessibleContext.hxx
@@ -0,0 +1,190 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+
+#ifndef INCLUDED_SVX_SOURCE_INC_GRAPHCTLACCESSIBLECONTEXT_HXX
+#define INCLUDED_SVX_SOURCE_INC_GRAPHCTLACCESSIBLECONTEXT_HXX
+
+#include <cppuhelper/compbase7.hxx>
+#include <com/sun/star/uno/Reference.hxx>
+#include <com/sun/star/accessibility/XAccessible.hpp>
+#include <com/sun/star/accessibility/XAccessibleComponent.hpp>
+#include <com/sun/star/accessibility/XAccessibleContext.hpp>
+#include <com/sun/star/accessibility/XAccessibleEventBroadcaster.hpp>
+#include <com/sun/star/accessibility/XAccessibleSelection.hpp>
+#include <com/sun/star/lang/XServiceInfo.hpp>
+#include <com/sun/star/lang/XServiceName.hpp>
+#include <cppuhelper/basemutex.hxx>
+#include <svl/lstner.hxx>
+
+#include <map>
+
+#include <svx/AccessibleShapeTreeInfo.hxx>
+#include <svx/IAccessibleViewForwarder.hxx>
+#include <svx/AccessibleShape.hxx>
+
+namespace com::sun::star::awt {
+ struct Point;
+ struct Rectangle;
+ struct Size;
+ class XFocusListener;
+}
+namespace tools { class Rectangle; }
+class GraphCtrl;
+class SdrObject;
+class SdrModel;
+class SdrPage;
+class SdrView;
+
+/** @descr
+ This base class provides an implementation of the
+ <code>AccessibleContext</code> service.
+*/
+
+typedef ::cppu::WeakAggComponentImplHelper7<
+ css::accessibility::XAccessible,
+ css::accessibility::XAccessibleComponent,
+ css::accessibility::XAccessibleContext,
+ css::accessibility::XAccessibleEventBroadcaster,
+ css::accessibility::XAccessibleSelection,
+ css::lang::XServiceInfo,
+ css::lang::XServiceName >
+ SvxGraphCtrlAccessibleContext_Base;
+
+class SvxGraphCtrlAccessibleContext final :
+ private cppu::BaseMutex, public SvxGraphCtrlAccessibleContext_Base,
+ public SfxListener, public ::accessibility::IAccessibleViewForwarder
+{
+public:
+ friend class GraphCtrl;
+
+ // internal
+ SvxGraphCtrlAccessibleContext(GraphCtrl& rRepresentation);
+
+ void Notify( SfxBroadcaster& aBC, const SfxHint& aHint ) override;
+
+ // XAccessible
+ /// Return the XAccessibleContext.
+ virtual css::uno::Reference< css::accessibility::XAccessibleContext> SAL_CALL
+ getAccessibleContext() override;
+
+ // XAccessibleComponent
+ virtual sal_Bool SAL_CALL containsPoint( const css::awt::Point& rPoint ) override;
+ virtual css::uno::Reference< css::accessibility::XAccessible > SAL_CALL getAccessibleAtPoint( const css::awt::Point& rPoint ) 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;
+
+ // XAccessibleContext
+ virtual sal_Int32 SAL_CALL getAccessibleChildCount() override;
+ virtual css::uno::Reference< css::accessibility::XAccessible> SAL_CALL getAccessibleChild (sal_Int32 nIndex) override;
+ virtual css::uno::Reference< css::accessibility::XAccessible> SAL_CALL getAccessibleParent() override;
+ virtual sal_Int32 SAL_CALL getAccessibleIndexInParent() override;
+ virtual sal_Int16 SAL_CALL getAccessibleRole() override;
+ virtual OUString SAL_CALL getAccessibleDescription() override;
+ virtual OUString SAL_CALL getAccessibleName() override;
+ virtual css::uno::Reference< css::accessibility::XAccessibleRelationSet> SAL_CALL getAccessibleRelationSet() override;
+ virtual css::uno::Reference< css::accessibility::XAccessibleStateSet> SAL_CALL getAccessibleStateSet() override;
+ 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;
+
+ // 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;
+
+ // XServiceName
+ virtual OUString SAL_CALL getServiceName() 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;
+
+ // IAccessibleViewforwarder
+ virtual tools::Rectangle GetVisibleArea() const override;
+ virtual Point LogicToPixel (const Point& rPoint) const override;
+ virtual Size LogicToPixel (const Size& rSize) const override;
+
+ /** This method is used by the graph control to tell the
+ accessibility object about a new model and view.
+ */
+ void setModelAndView (SdrModel* pModel, SdrView* pView);
+
+private:
+ virtual ~SvxGraphCtrlAccessibleContext() override;
+ /// @throws css::lang::IndexOutOfBoundsException
+ void checkChildIndexOnSelection( tools::Long nIndexOfChild );
+
+ virtual void SAL_CALL disposing() final override;
+
+ /// @throws css::uno::RuntimeException
+ /// @throws css::lang::IndexOutOfBoundsException
+ SdrObject* getSdrObject( sal_Int32 nIndex );
+
+ void CommitChange (sal_Int16 aEventId, const css::uno::Any& rNewValue, const css::uno::Any& rOldValue);
+
+ css::uno::Reference< css::accessibility::XAccessible > getAccessible( const SdrObject* pObj );
+
+ ::accessibility::AccessibleShapeTreeInfo maTreeInfo;
+
+ /** Description of this object. This is not a constant because it can
+ be set from the outside.
+ */
+ OUString msDescription;
+
+ /** Name of this object.
+ */
+ OUString msName;
+
+ /// map of accessible shapes
+ typedef ::std::map< const SdrObject*, rtl::Reference<::accessibility::AccessibleShape> > ShapesMapType;
+ ShapesMapType mxShapes;
+
+ GraphCtrl* mpControl;
+
+ SdrModel* mpModel;
+ SdrPage* mpPage;
+ SdrView* mpView;
+
+ /// client id in the AccessibleEventNotifier queue
+ sal_uInt32 mnClientId;
+
+ bool mbDisposed;
+};
+
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/inc/ShapesUtil.hxx b/svx/source/inc/ShapesUtil.hxx
new file mode 100644
index 000000000..72d6e63dc
--- /dev/null
+++ b/svx/source/inc/ShapesUtil.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_SVX_SOURCE_INC_SHAPESUTIL_HXX
+#define INCLUDED_SVX_SOURCE_INC_SHAPESUTIL_HXX
+#include <map>
+#include <rtl/ustring.hxx>
+
+namespace svx::sidebar {
+
+class SvxShapeCommandsMap
+{
+public:
+ SvxShapeCommandsMap();
+ std::map<sal_uInt16, OUString> mpLineShapes, mpCurveShapes,
+ mpConnectorShapes, mpBasicShapes, mpSymbolShapes,
+ mpBlockArrowShapes, mpFlowchartShapes,
+ mpCalloutShapes, mpStarShapes, mp3DShapes;
+};
+
+}
+
+#endif
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ \ No newline at end of file
diff --git a/svx/source/inc/StylesPreviewToolBoxControl.hxx b/svx/source/inc/StylesPreviewToolBoxControl.hxx
new file mode 100644
index 000000000..d8d12a0f2
--- /dev/null
+++ b/svx/source/inc/StylesPreviewToolBoxControl.hxx
@@ -0,0 +1,70 @@
+/* -*- 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_SVX_SOURCE_INC_STYLES_PREVIEW_TOOLBOX_CONTROL_HXX
+#define INCLUDED_SVX_SOURCE_INC_STYLES_PREVIEW_TOOLBOX_CONTROL_HXX
+
+#include <svtools/toolboxcontroller.hxx>
+#include <com/sun/star/lang/XServiceInfo.hpp>
+#include "StylesPreviewWindow.hxx"
+#include <com/sun/star/frame/XModel.hpp>
+#include <com/sun/star/frame/XDispatchProvider.hpp>
+
+class StylesPreviewToolBoxControl final
+ : public cppu::ImplInheritanceHelper<svt::ToolboxController, css::lang::XServiceInfo>
+{
+ VclPtr<StylesPreviewWindow_Impl> m_xVclBox;
+ std::unique_ptr<StylesPreviewWindow_Base> m_xWeldBox;
+
+ css::uno::Reference<css::frame::XDispatchProvider> m_xDispatchProvider;
+
+ std::vector<std::pair<OUString, OUString>> m_aDefaultStyles;
+
+public:
+ StylesPreviewToolBoxControl();
+ virtual ~StylesPreviewToolBoxControl() override;
+
+ // XStatusListener
+ virtual void SAL_CALL statusChanged(const css::frame::FeatureStateEvent& rEvent) override;
+
+ // XToolbarController
+ virtual css::uno::Reference<css::awt::XWindow>
+ SAL_CALL createItemWindow(const css::uno::Reference<css::awt::XWindow>& rParent) override;
+
+ // XInitialization
+ virtual void SAL_CALL initialize(const css::uno::Sequence<css::uno::Any>& aArguments) override;
+
+ // XComponent
+ virtual void SAL_CALL dispose() override;
+
+ // XUpdatable
+ virtual void SAL_CALL update() 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;
+
+private:
+ void InitializeStyles(const css::uno::Reference<css::frame::XModel>& xModel);
+};
+
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab cinoptions=b1,g0,N-s cinkeys+=0=break: */
diff --git a/svx/source/inc/StylesPreviewWindow.hxx b/svx/source/inc/StylesPreviewWindow.hxx
new file mode 100644
index 000000000..3b652dcfb
--- /dev/null
+++ b/svx/source/inc/StylesPreviewWindow.hxx
@@ -0,0 +1,146 @@
+/* -*- 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_SVX_SOURCE_INC_STYLES_PREVIEW_WINDOW_HXX
+#define INCLUDED_SVX_SOURCE_INC_STYLES_PREVIEW_WINDOW_HXX
+
+#include <vcl/InterimItemWindow.hxx>
+#include <svl/style.hxx>
+#include <com/sun/star/frame/XDispatchProvider.hpp>
+#include <sfx2/sfxstatuslistener.hxx>
+
+class StylesPreviewWindow_Base;
+
+/// Listener for style selection
+class StyleStatusListener final : public SfxStatusListener
+{
+ StylesPreviewWindow_Base* m_pPreviewControl;
+
+public:
+ StyleStatusListener(
+ StylesPreviewWindow_Base* pPreviewControl,
+ const css::uno::Reference<css::frame::XDispatchProvider>& xDispatchProvider);
+
+ void StateChangedAtStatusListener(SfxItemState eState, const SfxPoolItem* pState) override;
+};
+
+/// Listener for styles creation or modification
+class StylePoolChangeListener final : public SfxListener
+{
+ StylesPreviewWindow_Base* m_pPreviewControl;
+ SfxStyleSheetBasePool* m_pStyleSheetPool;
+
+public:
+ StylePoolChangeListener(StylesPreviewWindow_Base* pPreviewControl);
+ ~StylePoolChangeListener();
+
+ virtual void Notify(SfxBroadcaster& rBC, const SfxHint& rHint) override;
+};
+
+class StyleItemController
+{
+ static constexpr unsigned LEFT_MARGIN = 8;
+
+ SfxStyleFamily m_eStyleFamily;
+ std::pair<OUString, OUString> m_aStyleName;
+
+public:
+ StyleItemController(const std::pair<OUString, OUString>& aStyleName);
+
+ void Paint(vcl::RenderContext& rRenderContext);
+
+private:
+ void DrawEntry(vcl::RenderContext& rRenderContext);
+ void DrawText(vcl::RenderContext& rRenderContext);
+ void DrawHighlight(vcl::RenderContext& rRenderContext, Color aFontBack);
+ static void DrawContentBackground(vcl::RenderContext& rRenderContext,
+ const tools::Rectangle& aContentRect, const Color& aColor);
+};
+
+class StylesListUpdateTask : public Idle
+{
+ StylesPreviewWindow_Base& m_rStylesList;
+
+public:
+ StylesListUpdateTask(StylesPreviewWindow_Base& rStylesList)
+ : Idle("StylesListUpdateTask")
+ , m_rStylesList(rStylesList)
+ {
+ SetPriority(TaskPriority::HIGH_IDLE);
+ }
+
+ virtual void Invoke() override;
+};
+
+class StylesPreviewWindow_Base
+{
+ friend class StylesListUpdateTask;
+
+protected:
+ static constexpr unsigned STYLES_COUNT = 6;
+
+ css::uno::Reference<css::frame::XDispatchProvider> m_xDispatchProvider;
+
+ std::unique_ptr<weld::IconView> m_xStylesView;
+
+ StylesListUpdateTask m_aUpdateTask;
+
+ rtl::Reference<StyleStatusListener> m_xStatusListener;
+ std::unique_ptr<StylePoolChangeListener> m_pStylePoolChangeListener;
+
+ std::vector<std::pair<OUString, OUString>> m_aDefaultStyles;
+ std::vector<std::pair<OUString, OUString>> m_aAllStyles;
+
+ OUString m_sSelectedStyle;
+
+ DECL_LINK(Selected, weld::IconView&, void);
+ DECL_LINK(DoubleClick, weld::IconView&, bool);
+ DECL_LINK(DoCommand, const CommandEvent&, bool);
+
+public:
+ StylesPreviewWindow_Base(
+ weld::Builder& xBuilder, std::vector<std::pair<OUString, OUString>>&& aDefaultStyles,
+ const css::uno::Reference<css::frame::XDispatchProvider>& xDispatchProvider);
+ ~StylesPreviewWindow_Base();
+
+ void Select(const OUString& rStyleName);
+ void RequestStylesListUpdate();
+
+private:
+ void UpdateStylesList();
+ void UpdateSelection();
+ bool Command(const CommandEvent& rEvent);
+};
+
+class StylesPreviewWindow_Impl final : public InterimItemWindow, public StylesPreviewWindow_Base
+{
+public:
+ StylesPreviewWindow_Impl(
+ vcl::Window* pParent, std::vector<std::pair<OUString, OUString>>&& aDefaultStyles,
+ const css::uno::Reference<css::frame::XDispatchProvider>& xDispatchProvider);
+ ~StylesPreviewWindow_Impl();
+
+ void dispose();
+
+ void SetOptimalSize();
+};
+
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab cinoptions=b1,g0,N-s cinkeys+=0=break: */
diff --git a/svx/source/inc/cell.hxx b/svx/source/inc/cell.hxx
new file mode 100644
index 000000000..36ce254d1
--- /dev/null
+++ b/svx/source/inc/cell.hxx
@@ -0,0 +1,220 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#ifndef INCLUDED_SVX_SOURCE_INC_CELL_HXX
+#define INCLUDED_SVX_SOURCE_INC_CELL_HXX
+
+#include <config_options.h>
+#include <com/sun/star/table/XMergeableCell.hpp>
+#include <com/sun/star/awt/XLayoutConstrains.hpp>
+#include <com/sun/star/lang/XEventListener.hpp>
+
+#include <rtl/ref.hxx>
+#include <svl/style.hxx>
+#include <svx/sdtaitm.hxx>
+#include "tablemodel.hxx"
+#include <editeng/unotext.hxx>
+#include <svx/svdtext.hxx>
+
+
+class SfxItemSet;
+class OutlinerParaObject;
+class SdrObject;
+namespace sdr::properties { class TextProperties; }
+
+
+namespace sdr::table {
+
+
+class UNLESS_MERGELIBS(SVXCORE_DLLPUBLIC) Cell final : public SdrText,
+ public SvxUnoTextBase,
+ public css::table::XMergeableCell,
+ public css::awt::XLayoutConstrains,
+ public css::lang::XEventListener,
+ public ::cppu::OWeakObject
+{
+ friend class CellUndo;
+
+public:
+ SVX_DLLPRIVATE static rtl::Reference< Cell > create( SdrTableObj& rTableObj );
+
+ // private
+ SVX_DLLPRIVATE void dispose();
+
+ // SdrTextShape proxy
+ bool IsActiveCell() const;
+ bool IsTextEditActive() const;
+ SVX_DLLPRIVATE bool hasText() const;
+
+ SVX_DLLPRIVATE void cloneFrom( const CellRef& rCell );
+
+ SVX_DLLPRIVATE void setCellRect( ::tools::Rectangle const & rCellRect ) { maCellRect = rCellRect; }
+ SVX_DLLPRIVATE const ::tools::Rectangle& getCellRect() const { return maCellRect; }
+ SVX_DLLPRIVATE ::tools::Rectangle& getCellRect() { return maCellRect; }
+
+ bool CanCreateEditOutlinerParaObject() const;
+ std::optional<OutlinerParaObject> CreateEditOutlinerParaObject() const;
+ SVX_DLLPRIVATE void SetStyleSheet( SfxStyleSheet* pStyleSheet, bool bDontRemoveHardAttr );
+ SVX_DLLPRIVATE virtual SfxStyleSheet* GetStyleSheet() const override;
+ SVX_DLLPRIVATE void TakeTextAnchorRect(tools::Rectangle& rAnchorRect) const;
+
+ SVX_DLLPRIVATE void SetMergedItemSetAndBroadcast(const SfxItemSet& rSet, bool bClearAllItems);
+ void SetMergedItem(const SfxPoolItem& rItem);
+
+ SVX_DLLPRIVATE sal_Int32 calcPreferredWidth( const Size aSize );
+ SVX_DLLPRIVATE sal_Int32 getMinimumWidth() const;
+ SVX_DLLPRIVATE sal_Int32 getMinimumHeight();
+
+ SVX_DLLPRIVATE tools::Long GetTextLeftDistance() const;
+ SVX_DLLPRIVATE tools::Long GetTextRightDistance() const;
+ SVX_DLLPRIVATE tools::Long GetTextUpperDistance() const;
+ SVX_DLLPRIVATE tools::Long GetTextLowerDistance() const;
+
+ SVX_DLLPRIVATE SdrTextVertAdjust GetTextVerticalAdjust() const;
+ SdrTextHorzAdjust GetTextHorizontalAdjust() const;
+
+ SVX_DLLPRIVATE void merge( sal_Int32 nColumnSpan, sal_Int32 nRowSpan );
+ SVX_DLLPRIVATE void mergeContent( const CellRef& xSourceCell );
+ SVX_DLLPRIVATE void replaceContentAndFormatting( const CellRef& xSourceCell );
+
+ SVX_DLLPRIVATE void setMerged();
+
+ SVX_DLLPRIVATE void copyFormatFrom( const CellRef& xSourceCell );
+
+ // XInterface
+ SVX_DLLPRIVATE virtual css::uno::Any SAL_CALL queryInterface( const css::uno::Type& Type ) override;
+ virtual void SAL_CALL acquire() noexcept override;
+ virtual void SAL_CALL release() noexcept override;
+
+ // XTypeProvider
+ SVX_DLLPRIVATE virtual css::uno::Sequence< css::uno::Type > SAL_CALL getTypes( ) override;
+ SVX_DLLPRIVATE virtual css::uno::Sequence< ::sal_Int8 > SAL_CALL getImplementationId( ) override;
+
+ // XLayoutConstrains
+ SVX_DLLPRIVATE virtual css::awt::Size SAL_CALL getMinimumSize() override;
+ SVX_DLLPRIVATE virtual css::awt::Size SAL_CALL getPreferredSize() override;
+ SVX_DLLPRIVATE virtual css::awt::Size SAL_CALL calcAdjustedSize( const css::awt::Size& aNewSize ) override;
+
+ // XMergeableCell
+ SVX_DLLPRIVATE virtual ::sal_Int32 SAL_CALL getRowSpan() override;
+ SVX_DLLPRIVATE virtual ::sal_Int32 SAL_CALL getColumnSpan() override;
+ virtual sal_Bool SAL_CALL isMerged() override;
+
+ // XCell
+ SVX_DLLPRIVATE virtual OUString SAL_CALL getFormula() override;
+ SVX_DLLPRIVATE virtual void SAL_CALL setFormula( const OUString& aFormula ) override;
+ SVX_DLLPRIVATE virtual double SAL_CALL getValue() override;
+ SVX_DLLPRIVATE virtual void SAL_CALL setValue( double nValue ) override;
+ SVX_DLLPRIVATE virtual css::table::CellContentType SAL_CALL getType() override;
+ SVX_DLLPRIVATE virtual sal_Int32 SAL_CALL getError() override;
+
+ // css::beans::XPropertySet
+ SVX_DLLPRIVATE virtual css::uno::Reference< css::beans::XPropertySetInfo > SAL_CALL getPropertySetInfo() override;
+ SVX_DLLPRIVATE virtual void SAL_CALL setPropertyValue( const OUString& aPropertyName, const css::uno::Any& aValue ) override;
+ SVX_DLLPRIVATE virtual css::uno::Any SAL_CALL getPropertyValue( const OUString& PropertyName ) override;
+ SVX_DLLPRIVATE virtual void SAL_CALL addPropertyChangeListener( const OUString& aPropertyName, const css::uno::Reference< css::beans::XPropertyChangeListener >& xListener ) override;
+ SVX_DLLPRIVATE virtual void SAL_CALL removePropertyChangeListener( const OUString& aPropertyName, const css::uno::Reference< css::beans::XPropertyChangeListener >& aListener ) override;
+ SVX_DLLPRIVATE virtual void SAL_CALL addVetoableChangeListener( const OUString& PropertyName, const css::uno::Reference< css::beans::XVetoableChangeListener >& aListener ) override;
+ SVX_DLLPRIVATE virtual void SAL_CALL removeVetoableChangeListener( const OUString& PropertyName, const css::uno::Reference< css::beans::XVetoableChangeListener >& aListener ) override;
+
+ // XMultiPropertySet
+ SVX_DLLPRIVATE virtual void SAL_CALL setPropertyValues( const css::uno::Sequence< OUString >& aPropertyNames, const css::uno::Sequence< css::uno::Any >& aValues ) override;
+ SVX_DLLPRIVATE virtual css::uno::Sequence< css::uno::Any > SAL_CALL getPropertyValues( const css::uno::Sequence< OUString >& aPropertyNames ) override;
+ SVX_DLLPRIVATE virtual void SAL_CALL addPropertiesChangeListener( const css::uno::Sequence< OUString >& aPropertyNames, const css::uno::Reference< css::beans::XPropertiesChangeListener >& xListener ) override;
+ SVX_DLLPRIVATE virtual void SAL_CALL removePropertiesChangeListener( const css::uno::Reference< css::beans::XPropertiesChangeListener >& xListener ) override;
+ SVX_DLLPRIVATE virtual void SAL_CALL firePropertiesChangeEvent( const css::uno::Sequence< OUString >& aPropertyNames, const css::uno::Reference< css::beans::XPropertiesChangeListener >& xListener ) override;
+
+ // css::beans::XPropertyState
+ SVX_DLLPRIVATE virtual css::beans::PropertyState SAL_CALL getPropertyState( const OUString& PropertyName ) override;
+ SVX_DLLPRIVATE virtual css::uno::Sequence< css::beans::PropertyState > SAL_CALL getPropertyStates( const css::uno::Sequence< OUString >& aPropertyName ) override;
+ SVX_DLLPRIVATE virtual void SAL_CALL setPropertyToDefault( const OUString& PropertyName ) override;
+ SVX_DLLPRIVATE virtual css::uno::Any SAL_CALL getPropertyDefault( const OUString& aPropertyName ) override;
+
+ // XMultiPropertyStates
+ SVX_DLLPRIVATE virtual void SAL_CALL setAllPropertiesToDefault() override;
+ SVX_DLLPRIVATE virtual void SAL_CALL setPropertiesToDefault( const css::uno::Sequence< OUString >& aPropertyNames ) override;
+ SVX_DLLPRIVATE virtual css::uno::Sequence< css::uno::Any > SAL_CALL getPropertyDefaults( const css::uno::Sequence< OUString >& aPropertyNames ) override;
+
+ // XText
+ SVX_DLLPRIVATE 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;
+ SVX_DLLPRIVATE virtual void SAL_CALL removeTextContent( const css::uno::Reference< css::text::XTextContent >& xContent ) override;
+
+ // XSimpleText
+ SVX_DLLPRIVATE virtual void SAL_CALL insertString( const css::uno::Reference< css::text::XTextRange >& xRange, const OUString& aString, sal_Bool bAbsorb ) override;
+ SVX_DLLPRIVATE virtual void SAL_CALL insertControlCharacter( const css::uno::Reference< css::text::XTextRange >& xRange, ::sal_Int16 nControlCharacter, sal_Bool bAbsorb ) override;
+
+ // XTextRange
+ SVX_DLLPRIVATE virtual OUString SAL_CALL getString( ) override;
+ SVX_DLLPRIVATE virtual void SAL_CALL setString( const OUString& aString ) override;
+
+ // XEventListener
+ SVX_DLLPRIVATE virtual void SAL_CALL disposing( const css::lang::EventObject& Source ) override;
+
+ SVX_DLLPRIVATE virtual void SetOutlinerParaObject( std::optional<OutlinerParaObject> pTextObject ) override;
+
+ SVX_DLLPRIVATE void AddUndo();
+
+ using SvxUnoTextRangeBase::setPropertyValue;
+ using SvxUnoTextRangeBase::getPropertyValue;
+
+ SVX_DLLPRIVATE sdr::properties::TextProperties* CloneProperties( SdrObject& rNewObj, Cell& rNewCell );
+
+ SVX_DLLPRIVATE static sdr::properties::TextProperties* CloneProperties( sdr::properties::TextProperties const * pProperties, SdrObject& rNewObj, Cell& rNewCell );
+
+ SVX_DLLPRIVATE void notifyModified();
+
+ void dumpAsXml(xmlTextWriterPtr pWriter, sal_Int32 nRow, sal_Int32 nCol) const;
+
+private:
+ SVX_DLLPRIVATE virtual const SfxItemSet& GetObjectItemSet() override;
+ SVX_DLLPRIVATE void SetObjectItem(const SfxPoolItem& rItem);
+
+ SVX_DLLPRIVATE static css::uno::Any GetAnyForItem( SfxItemSet const & aSet, const SfxItemPropertyMapEntry* pMap );
+
+ /// @throws css::uno::RuntimeException
+ SVX_DLLPRIVATE Cell( SdrTableObj& rTableObj );
+ SVX_DLLPRIVATE virtual ~Cell() COVERITY_NOEXCEPT_FALSE override;
+
+ Cell(Cell const &) = delete;
+ void operator =(Cell const &) = delete;
+
+ const SvxItemPropertySet* mpPropSet;
+
+ std::unique_ptr<sdr::properties::TextProperties> mpProperties;
+
+ css::table::CellContentType mnCellContentType;
+
+ OUString msFormula;
+ double mfValue;
+ ::sal_Int32 mnError;
+ bool mbMerged;
+ ::sal_Int32 mnRowSpan;
+ ::sal_Int32 mnColSpan;
+
+ tools::Rectangle maCellRect;
+
+ css::uno::Reference< css::table::XTable > mxTable;
+};
+
+
+}
+
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/inc/celltypes.hxx b/svx/source/inc/celltypes.hxx
new file mode 100644
index 000000000..39edf2c5a
--- /dev/null
+++ b/svx/source/inc/celltypes.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_SVX_SOURCE_INC_CELLTYPES_HXX
+#define INCLUDED_SVX_SOURCE_INC_CELLTYPES_HXX
+
+#include <rtl/ref.hxx>
+#include <vector>
+
+namespace sdr::table
+{
+class Cell;
+class TableModel;
+class TableRow;
+class TableColumn;
+class TableRows;
+class TableColumns;
+typedef rtl::Reference<Cell> CellRef;
+typedef rtl::Reference<TableModel> TableModelRef;
+typedef rtl::Reference<TableRow> TableRowRef;
+typedef rtl::Reference<TableColumn> TableColumnRef;
+typedef std::vector<CellRef> CellVector;
+typedef std::vector<TableRowRef> RowVector;
+typedef std::vector<TableColumnRef> ColumnVector;
+
+class TableDesignUser
+{
+public:
+ virtual bool isInUse() = 0;
+
+protected:
+ ~TableDesignUser() {}
+};
+
+template <typename T> class RangeIterator
+{
+public:
+ /** creates an iterator from rStart (including) to rEnd (excluding) if
+ bForward is true or from nEnd (excluding to nStart (including).
+ rStart must be <= rEnd.
+ */
+ RangeIterator(const T& rStart, const T& rEnd, bool bForward)
+ {
+ if (bForward)
+ {
+ maIter = rStart;
+ maEnd = rEnd;
+ }
+ else
+ {
+ maIter = rEnd - 1;
+ maEnd = rStart - 1;
+ }
+ }
+
+ /* iterates in the configured direction and returns true if rValue
+ now contains a valid position in the range of this iterator */
+ bool next(T& rValue)
+ {
+ if (maIter == maEnd)
+ return false;
+
+ rValue = maIter;
+ if (maIter < maEnd)
+ ++maIter;
+ else
+ --maIter;
+ return true;
+ }
+
+private:
+ T maEnd;
+ T maIter;
+};
+}
+
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/inc/charmapacc.hxx b/svx/source/inc/charmapacc.hxx
new file mode 100644
index 000000000..193314ba4
--- /dev/null
+++ b/svx/source/inc/charmapacc.hxx
@@ -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 .
+ */
+
+#ifndef INCLUDED_SVX_SOURCE_INC_CHARMAPACC_HXX
+#define INCLUDED_SVX_SOURCE_INC_CHARMAPACC_HXX
+
+#include <comphelper/accessibleselectionhelper.hxx>
+#include <com/sun/star/accessibility/XAccessibleAction.hpp>
+#include <com/sun/star/accessibility/XAccessibleTable.hpp>
+#include <tools/gen.hxx>
+#include <rtl/ref.hxx>
+
+#include <vector>
+class SvxShowCharSet;
+
+namespace svx
+{
+ typedef ::cppu::ImplHelper1 < css::accessibility::XAccessible
+ > OAccessibleHelper_Base_2;
+
+ class SvxShowCharSetAcc;
+
+ class SvxShowCharSetItemAcc;
+
+ // - SvxShowCharSetItem -
+
+ /** Simple struct to hold some information about the single items of the table.
+ */
+ struct SvxShowCharSetItem
+ {
+ SvxShowCharSet& mrParent;
+ sal_uInt16 mnId;
+ OUString maText;
+ tools::Rectangle maRect;
+ rtl::Reference<SvxShowCharSetItemAcc> m_xItem;
+ SvxShowCharSetAcc* m_pParent;
+
+ SvxShowCharSetItem( SvxShowCharSet& rParent, SvxShowCharSetAcc* _pParent, sal_uInt16 _nPos );
+ ~SvxShowCharSetItem();
+
+ css::uno::Reference< css::accessibility::XAccessible > GetAccessible();
+ };
+
+
+ typedef ::cppu::ImplHelper2 < css::accessibility::XAccessible,
+ css::accessibility::XAccessibleTable
+ > OAccessibleHelper_Base;
+
+ // - SvxShowCharSetAcc -
+
+ /** The table implementation of the vcl control.
+ */
+
+ class SvxShowCharSetAcc final : public ::comphelper::OAccessibleSelectionHelper,
+ public OAccessibleHelper_Base
+ {
+ ::std::vector< css::uno::Reference< css::accessibility::XAccessible > > m_aChildren;
+ SvxShowCharSet* m_pParent; // the vcl control
+ virtual void SAL_CALL disposing() override;
+ public:
+ SvxShowCharSetAcc(SvxShowCharSet* pParent);
+
+ DECLARE_XINTERFACE( )
+ DECLARE_XTYPEPROVIDER( )
+
+ // XAccessibleComponent
+ virtual void SAL_CALL grabFocus( ) override;
+ virtual css::uno::Reference< css::accessibility::XAccessible > SAL_CALL getAccessibleAtPoint( const css::awt::Point& aPoint ) override;
+
+ //OAccessibleContextHelper
+ // XAccessibleContext - still waiting to be overwritten
+ virtual sal_Int32 SAL_CALL getAccessibleChildCount() override;
+ virtual css::uno::Reference< css::accessibility::XAccessible > SAL_CALL getAccessibleChild( sal_Int32 i ) override;
+ virtual css::uno::Reference< css::accessibility::XAccessible > SAL_CALL getAccessibleParent( ) override;
+ virtual sal_Int16 SAL_CALL getAccessibleRole( ) override;
+ virtual OUString SAL_CALL getAccessibleDescription( ) override;
+ virtual OUString SAL_CALL getAccessibleName( ) override;
+ virtual css::uno::Reference< css::accessibility::XAccessibleRelationSet > SAL_CALL getAccessibleRelationSet( ) override;
+ virtual css::uno::Reference< css::accessibility::XAccessibleStateSet > SAL_CALL getAccessibleStateSet( ) override;
+
+ virtual css::uno::Reference< css::accessibility::XAccessibleContext > SAL_CALL getAccessibleContext( ) override { return this; }
+ virtual sal_Int32 SAL_CALL getForeground( ) override;
+ virtual sal_Int32 SAL_CALL getBackground( ) 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;
+
+
+ void fireEvent(
+ const sal_Int16 _nEventId,
+ const css::uno::Any& _rOldValue,
+ const css::uno::Any& _rNewValue
+ )
+ {
+ NotifyAccessibleEvent(_nEventId,_rOldValue,_rNewValue);
+ }
+
+ void clearCharSetControl() { m_pParent = nullptr; }
+ private:
+
+ virtual ~SvxShowCharSetAcc() override;
+
+ // OCommonAccessibleSelection
+ // return if the specified child is visible => watch for special ChildIndexes (ACCESSIBLE_SELECTION_CHILD_xxx)
+ virtual bool
+ implIsSelected( sal_Int32 nAccessibleChildIndex ) override;
+
+ // select the specified child => watch for special ChildIndexes (ACCESSIBLE_SELECTION_CHILD_xxx)
+ virtual void
+ implSelect( sal_Int32 nAccessibleChildIndex, bool bSelect ) override;
+
+ // OCommonAccessibleComponent
+ /// implements the calculation of the bounding rectangle - still waiting to be overwritten
+ virtual css::awt::Rectangle implGetBounds( ) override;
+ };
+
+
+ // - SvxShowCharSetItemAcc -
+
+ typedef ::cppu::ImplHelper2 < css::accessibility::XAccessible,
+ css::accessibility::XAccessibleAction
+ > OAccessibleHelper_Base_3;
+
+ /** The child implementation of the table.
+ */
+ class SvxShowCharSetItemAcc final : public ::comphelper::OAccessibleComponentHelper,
+ public OAccessibleHelper_Base_3
+ {
+ private:
+ SvxShowCharSetItem* mpParent;
+
+ virtual ~SvxShowCharSetItemAcc() override;
+
+ // OCommonAccessibleComponent
+ /// implements the calculation of the bounding rectangle - still waiting to be overwritten
+ virtual css::awt::Rectangle implGetBounds( ) override;
+ public:
+
+ // XInterface
+ DECLARE_XINTERFACE( )
+ DECLARE_XTYPEPROVIDER( )
+
+ SvxShowCharSetItemAcc( SvxShowCharSetItem* pParent );
+
+ void ParentDestroyed();
+
+ // XAccessibleComponent
+ virtual void SAL_CALL grabFocus( ) override;
+ virtual css::uno::Reference< css::accessibility::XAccessible > SAL_CALL getAccessibleAtPoint( const css::awt::Point& aPoint ) override;
+
+ //OAccessibleContextHelper
+ // XAccessibleContext - still waiting to be overwritten
+ virtual sal_Int32 SAL_CALL getAccessibleChildCount( ) override;
+ virtual css::uno::Reference< css::accessibility::XAccessible > SAL_CALL getAccessibleChild( sal_Int32 i ) override;
+ virtual css::uno::Reference< css::accessibility::XAccessible > SAL_CALL getAccessibleParent( ) override;
+ virtual sal_Int16 SAL_CALL getAccessibleRole( ) override;
+ virtual OUString SAL_CALL getAccessibleDescription( ) override;
+ virtual OUString SAL_CALL getAccessibleName( ) override;
+ virtual css::uno::Reference< css::accessibility::XAccessibleRelationSet > SAL_CALL getAccessibleRelationSet( ) override;
+ virtual css::uno::Reference< css::accessibility::XAccessibleStateSet > SAL_CALL getAccessibleStateSet( ) override;
+
+ virtual css::uno::Reference< css::accessibility::XAccessibleContext > SAL_CALL getAccessibleContext( ) override { return this; }
+
+ virtual sal_Int32 SAL_CALL getForeground( ) override { return mpParent->m_pParent->getForeground(); }
+ virtual sal_Int32 SAL_CALL getBackground( ) override { return mpParent->m_pParent->getBackground(); }
+
+ // 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;
+
+
+ void fireEvent(
+ const sal_Int16 _nEventId,
+ const css::uno::Any& _rOldValue,
+ const css::uno::Any& _rNewValue
+ )
+ {
+ NotifyAccessibleEvent(_nEventId,_rOldValue,_rNewValue);
+ }
+ };
+}
+
+
+#endif // INCLUDED_SVX_SOURCE_INC_CHARMAPACC_HXX
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/inc/clonelist.hxx b/svx/source/inc/clonelist.hxx
new file mode 100644
index 000000000..5ed3e4d8c
--- /dev/null
+++ b/svx/source/inc/clonelist.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_SVX_SOURCE_INC_CLONELIST_HXX
+#define INCLUDED_SVX_SOURCE_INC_CLONELIST_HXX
+
+#include <vector>
+
+#include <sal/types.h>
+
+class SdrObject;
+
+// #i13033#
+// New mechanism to hold a list of all original and cloned objects for later
+// re-creating the connections for contained connectors
+class CloneList
+{
+ std::vector<const SdrObject*> maOriginalList;
+ std::vector<SdrObject*> maCloneList;
+
+public:
+ void AddPair(const SdrObject* pOriginal, SdrObject* pClone);
+
+ const SdrObject* GetOriginal(sal_uInt32 nIndex) const;
+ SdrObject* GetClone(sal_uInt32 nIndex) const;
+
+ void CopyConnections() const;
+};
+
+#endif // INCLUDED_SVX_SOURCE_INC_CLONELIST_HXX
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/inc/datalistener.hxx b/svx/source/inc/datalistener.hxx
new file mode 100644
index 000000000..88f0a9f5a
--- /dev/null
+++ b/svx/source/inc/datalistener.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_SVX_SOURCE_INC_DATALISTENER_HXX
+#define INCLUDED_SVX_SOURCE_INC_DATALISTENER_HXX
+
+#include <cppuhelper/implbase.hxx>
+#include <com/sun/star/container/XContainerListener.hpp>
+#include <com/sun/star/frame/XFrameActionListener.hpp>
+#include <com/sun/star/xml/dom/events/XEventListener.hpp>
+
+
+namespace svxform
+{
+
+
+ class DataNavigatorWindow;
+
+ typedef cppu::WeakImplHelper<
+ css::container::XContainerListener,
+ css::frame::XFrameActionListener,
+ css::xml::dom::events::XEventListener > DataListener_t;
+
+ class DataListener final : public DataListener_t
+ {
+ private:
+ DataNavigatorWindow* m_pNaviWin;
+
+ public:
+ DataListener( DataNavigatorWindow* pNaviWin );
+
+ private:
+ virtual ~DataListener() override;
+
+ public:
+ // XContainerListener
+ virtual void SAL_CALL elementInserted( const css::container::ContainerEvent& Event ) override;
+ virtual void SAL_CALL elementRemoved( const css::container::ContainerEvent& Event ) override;
+ virtual void SAL_CALL elementReplaced( const css::container::ContainerEvent& Event ) override;
+
+ // XFrameActionListener
+ virtual void SAL_CALL frameAction( const css::frame::FrameActionEvent& Action ) override;
+
+ // xml::dom::events::XEventListener
+ virtual void SAL_CALL handleEvent( const css::uno::Reference< css::xml::dom::events::XEvent >& evt ) override;
+
+ // lang::XEventListener
+ virtual void SAL_CALL disposing( const css::lang::EventObject& Source ) override;
+ };
+
+
+}
+
+
+#endif // INCLUDED_SVX_SOURCE_INC_DATALISTENER_HXX
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/inc/datanavi.hxx b/svx/source/inc/datanavi.hxx
new file mode 100644
index 000000000..22f30e044
--- /dev/null
+++ b/svx/source/inc/datanavi.hxx
@@ -0,0 +1,580 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+#ifndef INCLUDED_SVX_SOURCE_INC_DATANAVI_HXX
+#define INCLUDED_SVX_SOURCE_INC_DATANAVI_HXX
+
+#include <config_options.h>
+#include <vcl/builderpage.hxx>
+#include <vcl/idle.hxx>
+#include <vcl/transfer.hxx>
+#include <vcl/weld.hxx>
+#include <svtools/inettbc.hxx>
+#include <sfx2/dockwin.hxx>
+#include <sfx2/childwin.hxx>
+#include <sfx2/ctrlitem.hxx>
+#include <svx/dialmgr.hxx>
+#include <svx/strings.hrc>
+#include <svx/svxdllapi.h>
+#include <rtl/ref.hxx>
+#include <com/sun/star/beans/XPropertySet.hpp>
+#include <com/sun/star/container/XContainer.hpp>
+#include <com/sun/star/container/XNameContainer.hpp>
+#include <com/sun/star/frame/XFrame.hpp>
+#include <com/sun/star/xforms/XFormsUIHelper1.hpp>
+#include <com/sun/star/xforms/XModel.hpp>
+#include <com/sun/star/xforms/XSubmission.hpp>
+#include <com/sun/star/xml/dom/XNode.hpp>
+#include <com/sun/star/xml/dom/events/XEventTarget.hpp>
+
+#include "datalistener.hxx"
+
+#include <memory>
+#include <string_view>
+#include <vector>
+
+class FmFormShell;
+
+
+namespace svxform
+{
+
+
+ enum DataGroupType
+ {
+ DGTUnknown = 0,
+ DGTInstance,
+ DGTSubmission,
+ DGTBinding
+ };
+
+ enum DataItemType
+ {
+ DITNone = 0,
+ DITText,
+ DITAttribute,
+ DITElement,
+ DITBinding
+ };
+
+ struct ItemNode;
+ class XFormsPage;
+ class DataNavigatorWindow;
+ class AddInstanceDialog;
+
+ class ReplaceString
+ {
+ OUString m_sDoc_UI;
+ OUString m_sInstance_UI;
+ OUString m_sNone_UI;
+
+ static constexpr OUStringLiteral m_sDoc_API = u"all";
+ static constexpr OUStringLiteral m_sInstance_API = u"instance";
+ static constexpr OUStringLiteral m_sNone_API = u"none";
+
+ ReplaceString( const ReplaceString& ) = delete;
+
+ public:
+ ReplaceString()
+ {
+ m_sDoc_UI = SvxResId(RID_STR_REPLACE_DOC);
+ m_sInstance_UI = SvxResId(RID_STR_REPLACE_INST);
+ m_sNone_UI = SvxResId(RID_STR_REPLACE_NONE);
+ }
+
+ /** convert submission replace string from API value to UI value.
+ Use 'none' as default. */
+ OUString const & toUI( std::u16string_view rStr ) const
+ {
+ if( rStr == m_sDoc_API )
+ return m_sDoc_UI;
+ else if( rStr == m_sInstance_API )
+ return m_sInstance_UI;
+ else
+ return m_sNone_UI;
+ }
+
+ /** convert submission replace string from UI to API.
+ Use 'none' as default. */
+ OUString toAPI( std::u16string_view rStr ) const
+ {
+ if( rStr == m_sDoc_UI )
+ return m_sDoc_API;
+ else if( rStr == m_sInstance_UI )
+ return m_sInstance_API;
+ else
+ return m_sNone_API;
+ }
+ };
+
+ class MethodString
+ {
+ OUString m_sPost_UI;
+ OUString m_sPut_UI;
+ OUString m_sGet_UI;
+
+ static constexpr OUStringLiteral m_sPost_API = u"post";
+ static constexpr OUStringLiteral m_sPut_API = u"put";
+ static constexpr OUStringLiteral m_sGet_API = u"get";
+
+ MethodString( const MethodString& ) = delete;
+
+ public:
+
+ MethodString()
+ {
+ m_sPost_UI = SvxResId(RID_STR_METHOD_POST);
+ m_sPut_UI = SvxResId(RID_STR_METHOD_PUT);
+ m_sGet_UI = SvxResId(RID_STR_METHOD_GET);
+ }
+
+ /** convert from API to UI; put is default. */
+ OUString const & toUI( std::u16string_view rStr ) const
+ {
+ if( rStr == m_sGet_API )
+ return m_sGet_UI;
+ else if( rStr == m_sPost_API )
+ return m_sPost_UI;
+ else
+ return m_sPut_UI;
+ }
+
+ /** convert from UI to API; put is default */
+ OUString toAPI( std::u16string_view rStr ) const
+ {
+ if( rStr == m_sGet_UI )
+ return m_sGet_API;
+ else if( rStr == m_sPost_UI )
+ return m_sPost_API;
+ else
+ return m_sPut_API;
+ }
+ };
+
+ class DataTreeDropTarget final : public DropTargetHelper
+ {
+ private:
+ virtual sal_Int8 AcceptDrop(const AcceptDropEvent& rEvt) override;
+ virtual sal_Int8 ExecuteDrop(const ExecuteDropEvent& rEvt) override;
+
+ public:
+ DataTreeDropTarget(weld::TreeView& rWidget);
+ };
+
+ class XFormsPage final : public BuilderPage
+ {
+ private:
+ MethodString m_aMethodString;
+ ReplaceString m_aReplaceString;
+
+ weld::Container* m_pParent;
+ std::unique_ptr<weld::Toolbar> m_xToolBox;
+ std::unique_ptr<weld::TreeView> m_xItemList;
+ std::unique_ptr<weld::TreeIter> m_xScratchIter;
+
+ DataTreeDropTarget m_aDropHelper;
+
+ css::uno::Reference< css::xforms::XFormsUIHelper1 >
+ m_xUIHelper;
+
+ DataNavigatorWindow* m_pNaviWin;
+ bool m_bHasModel;
+ DataGroupType m_eGroup;
+ // these strings are not valid on the Submission and Binding Page
+ // mb: furthermore these are properties of an instance, thus
+ // it would be much better to get/set them through the UIHelper
+ // interface.
+ OUString m_sInstanceName;
+ OUString m_sInstanceURL;
+ bool m_bLinkOnce;
+
+ DECL_LINK(TbxSelectHdl, const OString&, void);
+ DECL_LINK(ItemSelectHdl, weld::TreeView&, void);
+ DECL_LINK(KeyInputHdl, const KeyEvent&, bool);
+ DECL_LINK(PopupMenuHdl, const CommandEvent&, bool);
+
+ void AddChildren(const weld::TreeIter* _pParent,
+ const css::uno::Reference< css::xml::dom::XNode >& _xNode);
+ bool DoToolBoxAction(std::string_view rToolBoxID);
+ void AddEntry(std::unique_ptr<ItemNode> _pNewNode, bool _bIsElement, weld::TreeIter* pRet = nullptr);
+ void AddEntry(const css::uno::Reference< css::beans::XPropertySet >& _rPropSet, weld::TreeIter* pRet = nullptr);
+ void EditEntry( const css::uno::Reference< css::beans::XPropertySet >& _rPropSet );
+ bool RemoveEntry();
+
+ void PrepDnD();
+
+ void DeleteAndClearTree();
+
+ public:
+ XFormsPage(weld::Container* pParent, DataNavigatorWindow* _pNaviWin, DataGroupType _eGroup);
+ virtual ~XFormsPage() override;
+
+ bool HasModel() const { return m_bHasModel; }
+ OUString SetModel( const css::uno::Reference< css::xforms::XModel > & _xModel, int _nPagePos );
+ void ClearModel();
+ OUString LoadInstance(const css::uno::Sequence< css::beans::PropertyValue >& _xPropSeq);
+
+ bool DoMenuAction(std::string_view rMenuID);
+ void EnableMenuItems(weld::Menu* pMenu);
+
+ const OUString& GetInstanceName() const { return m_sInstanceName; }
+ const OUString& GetInstanceURL() const { return m_sInstanceURL; }
+ bool GetLinkOnce() const { return m_bLinkOnce; }
+ void SetInstanceName( const OUString &name ) { m_sInstanceName=name; }
+ void SetInstanceURL( const OUString &url ) { m_sInstanceURL=url; }
+ void SetLinkOnce( bool bLinkOnce ) { m_bLinkOnce=bLinkOnce; }
+
+ css::uno::Reference<css::beans::XPropertySet>
+ GetBindingForNode( const css::uno::Reference<css::xml::dom::XNode> &xNode ) { return m_xUIHelper->getBindingForNode(xNode,true); }
+ OUString GetServiceNameForNode( const css::uno::Reference<css::xml::dom::XNode> &xNode ) { return m_xUIHelper->getDefaultServiceNameForNode(xNode); }
+ const css::uno::Reference< css::xforms::XFormsUIHelper1 >&
+ GetXFormsHelper() const { return m_xUIHelper; }
+ };
+
+ class DataNavigatorWindow final
+ {
+ private:
+ VclPtr<vcl::Window> m_xParent;
+ std::unique_ptr<weld::ComboBox> m_xModelsBox;
+ std::unique_ptr<weld::MenuButton> m_xModelBtn;
+ std::unique_ptr<weld::Notebook> m_xTabCtrl;
+ std::unique_ptr<weld::MenuButton> m_xInstanceBtn;
+
+ std::unique_ptr<XFormsPage> m_xInstPage;
+ std::unique_ptr<XFormsPage> m_xSubmissionPage;
+ std::unique_ptr<XFormsPage> m_xBindingPage;
+
+ sal_Int32 m_nLastSelectedPos;
+ bool m_bShowDetails;
+ bool m_bIsNotifyDisabled;
+ std::vector< std::unique_ptr<XFormsPage> >
+ m_aPageList;
+ std::vector< css::uno::Reference< css::container::XContainer > >
+ m_aContainerList;
+ std::vector< css::uno::Reference< css::xml::dom::events::XEventTarget > >
+ m_aEventTargetList;
+ Timer m_aUpdateTimer;
+
+ ::rtl::Reference < DataListener >
+ m_xDataListener;
+ css::uno::Reference< css::container::XNameContainer >
+ m_xDataContainer;
+ css::uno::Reference< css::frame::XFrame >
+ m_xFrame;
+ css::uno::Reference< css::frame::XModel >
+ m_xFrameModel;
+
+ DECL_LINK( ModelSelectListBoxHdl, weld::ComboBox&, void );
+ DECL_LINK( MenuSelectHdl, const OString&, void );
+ DECL_LINK( MenuActivateHdl, weld::Toggleable&, void );
+ DECL_LINK( ActivatePageHdl, const OString&, void);
+ DECL_LINK( UpdateHdl, Timer *, void);
+ void ModelSelectHdl(const weld::ComboBox*);
+ OString GetCurrentPage() const;
+ XFormsPage* GetPage(const OString& rCurId);
+ void LoadModels();
+ void SetPageModel(const OString& rCurId);
+ void ClearAllPageModels( bool bClearPages );
+ void InitPages();
+ void CreateInstancePage( const css::uno::Sequence< css::beans::PropertyValue >& _xPropSeq );
+ bool HasFirstInstancePage() const;
+ OString GetNewPageId() const;
+
+ static bool IsAdditionalPage(std::string_view rIdent);
+
+ public:
+ DataNavigatorWindow(vcl::Window* pParent, weld::Builder& rBuilder, SfxBindings const * pBindings);
+ ~DataNavigatorWindow();
+
+ static void SetDocModified();
+ void NotifyChanges( bool _bLoadAll = false );
+ void AddContainerBroadcaster( const css::uno::Reference< css::container::XContainer > & xContainer );
+ void AddEventBroadcaster( const css::uno::Reference< css::xml::dom::events::XEventTarget >& xTarget );
+ void RemoveBroadcaster();
+
+ weld::Window* GetFrameWeld() const { return m_xParent->GetFrameWeld(); }
+
+ bool IsShowDetails() const { return m_bShowDetails; }
+ void DisableNotify( bool _bDisable ) { m_bIsNotifyDisabled = _bDisable; }
+ };
+
+ class DataNavigator final : public SfxDockingWindow, public SfxControllerItem
+ {
+ private:
+ std::unique_ptr<DataNavigatorWindow> m_xDataWin;
+
+ virtual Size CalcDockingSize( SfxChildAlignment ) override;
+ virtual SfxChildAlignment CheckAlignment( SfxChildAlignment, SfxChildAlignment ) override;
+
+ public:
+ DataNavigator( SfxBindings* pBindings, SfxChildWindow* pMgr, vcl::Window* pParent );
+ virtual ~DataNavigator() override;
+ virtual void dispose() override;
+
+ using SfxDockingWindow::StateChanged;
+
+ void StateChangedAtToolBoxControl( sal_uInt16 nSID, SfxItemState eState,
+ const SfxPoolItem* pState ) override;
+ };
+
+ class UNLESS_MERGELIBS(SVXCORE_DLLPUBLIC) DataNavigatorManager final : public SfxChildWindow
+ {
+ public:
+ SVX_DLLPRIVATE DataNavigatorManager( vcl::Window* pParent, sal_uInt16 nId,
+ SfxBindings* pBindings, SfxChildWinInfo* pInfo );
+ SFX_DECL_CHILDWINDOW( DataNavigatorManager );
+ };
+
+ class AddDataItemDialog final : public weld::GenericDialogController
+ {
+ private:
+ css::uno::Reference< css::xforms::XFormsUIHelper1 >
+ m_xUIHelper;
+ css::uno::Reference< css::beans::XPropertySet >
+ m_xBinding;
+ css::uno::Reference< css::beans::XPropertySet >
+ m_xTempBinding;
+
+ ItemNode* m_pItemNode;
+ DataItemType m_eItemType;
+ OUString m_sFL_Element;
+ OUString m_sFL_Attribute;
+ OUString m_sFL_Binding;
+ OUString m_sFT_BindingExp;
+
+ std::unique_ptr<weld::Frame> m_xItemFrame;
+ std::unique_ptr<weld::Label> m_xNameFT;
+ std::unique_ptr<weld::Entry> m_xNameED;
+ std::unique_ptr<weld::Label> m_xDefaultFT;
+ std::unique_ptr<weld::Entry> m_xDefaultED;
+ std::unique_ptr<weld::Button> m_xDefaultBtn;
+ std::unique_ptr<weld::Widget> m_xSettingsFrame;
+ std::unique_ptr<weld::Label> m_xDataTypeFT;
+ std::unique_ptr<weld::ComboBox> m_xDataTypeLB;
+ std::unique_ptr<weld::CheckButton> m_xRequiredCB;
+ std::unique_ptr<weld::Button> m_xRequiredBtn;
+ std::unique_ptr<weld::CheckButton> m_xRelevantCB;
+ std::unique_ptr<weld::Button> m_xRelevantBtn;
+ std::unique_ptr<weld::CheckButton> m_xConstraintCB;
+ std::unique_ptr<weld::Button> m_xConstraintBtn;
+ std::unique_ptr<weld::CheckButton> m_xReadonlyCB;
+ std::unique_ptr<weld::Button> m_xReadonlyBtn;
+ std::unique_ptr<weld::CheckButton> m_xCalculateCB;
+ std::unique_ptr<weld::Button> m_xCalculateBtn;
+ std::unique_ptr<weld::Button> m_xOKBtn;
+
+ void Check(const weld::Toggleable* pBox);
+ DECL_LINK(CheckHdl, weld::Toggleable&, void);
+ DECL_LINK(ConditionHdl, weld::Button&, void);
+ DECL_LINK(OKHdl, weld::Button&, void);
+
+ void InitDialog();
+ void InitFromNode();
+ void InitDataTypeBox();
+
+ public:
+ AddDataItemDialog(
+ weld::Window* pParent, ItemNode* _pNode,
+ const css::uno::Reference< css::xforms::XFormsUIHelper1 >& _rUIHelper );
+ virtual ~AddDataItemDialog() override;
+
+ void InitText( DataItemType _eType );
+ };
+
+ class AddConditionDialog final : public weld::GenericDialogController
+ {
+ private:
+ Idle m_aResultIdle;
+ OUString m_sPropertyName;
+
+ css::uno::Reference< css::xforms::XFormsUIHelper1 >
+ m_xUIHelper;
+ css::uno::Reference< css::beans::XPropertySet >
+ m_xBinding;
+
+ std::unique_ptr<weld::TextView> m_xConditionED;
+ std::unique_ptr<weld::TextView> m_xResultWin;
+ std::unique_ptr<weld::Button> m_xEditNamespacesBtn;
+ std::unique_ptr<weld::Button> m_xOKBtn;
+
+ DECL_LINK(ModifyHdl, weld::TextView&, void);
+ DECL_LINK(ResultHdl, Timer *, void);
+ DECL_LINK(EditHdl, weld::Button&, void);
+ DECL_LINK(OKHdl, weld::Button&, void);
+
+ public:
+ AddConditionDialog(weld::Window* pParent,
+ const OUString& _rPropertyName, const css::uno::Reference< css::beans::XPropertySet >& _rBinding);
+ virtual ~AddConditionDialog() override;
+
+ const css::uno::Reference< css::xforms::XFormsUIHelper1 >& GetUIHelper() const { return m_xUIHelper; }
+ OUString GetCondition() const { return m_xConditionED->get_text(); }
+ void SetCondition(const OUString& _rCondition)
+ {
+ m_xConditionED->set_text(_rCondition);
+ m_aResultIdle.Start();
+ }
+ };
+
+ class NamespaceItemDialog final : public weld::GenericDialogController
+ {
+ private:
+ AddConditionDialog* m_pConditionDlg;
+ std::vector< OUString > m_aRemovedList;
+
+ css::uno::Reference< css::container::XNameContainer >&
+ m_rNamespaces;
+
+ std::unique_ptr<weld::TreeView> m_xNamespacesList;
+ std::unique_ptr<weld::Button> m_xAddNamespaceBtn;
+ std::unique_ptr<weld::Button> m_xEditNamespaceBtn;
+ std::unique_ptr<weld::Button> m_xDeleteNamespaceBtn;
+ std::unique_ptr<weld::Button> m_xOKBtn;
+
+ DECL_LINK(SelectHdl, weld::TreeView&, void);
+ DECL_LINK(ClickHdl, weld::Button&, void);
+ DECL_LINK(OKHdl, weld::Button&, void);
+
+ void LoadNamespaces();
+
+ public:
+ NamespaceItemDialog(AddConditionDialog* pParent, css::uno::Reference< css::container::XNameContainer >& _rContainer);
+ virtual ~NamespaceItemDialog() override;
+ };
+
+ class ManageNamespaceDialog final : public weld::GenericDialogController
+ {
+ private:
+ AddConditionDialog* m_pConditionDlg;
+
+ std::unique_ptr<weld::Entry> m_xPrefixED;
+ std::unique_ptr<weld::Entry> m_xUrlED;
+ std::unique_ptr<weld::Button> m_xOKBtn;
+ std::unique_ptr<weld::Label> m_xAltTitle;
+
+ DECL_LINK(OKHdl, weld::Button&, void);
+
+ public:
+ ManageNamespaceDialog(weld::Window* pParent, AddConditionDialog* _pCondDlg, bool bIsEdit);
+ virtual ~ManageNamespaceDialog() override;
+
+ void SetNamespace(const OUString& _rPrefix, const OUString& _rURL)
+ {
+ m_xPrefixED->set_text(_rPrefix);
+ m_xUrlED->set_text(_rURL);
+ }
+ OUString GetPrefix() const { return m_xPrefixED->get_text(); }
+ OUString GetURL() const { return m_xUrlED->get_text(); }
+ };
+
+ class AddSubmissionDialog final : public weld::GenericDialogController
+ {
+ private:
+ MethodString m_aMethodString;
+ ReplaceString m_aReplaceString;
+
+ ItemNode* m_pItemNode;
+
+ css::uno::Reference< css::xforms::XFormsUIHelper1 >
+ m_xUIHelper;
+ css::uno::Reference< css::xforms::XSubmission >
+ m_xNewSubmission;
+ css::uno::Reference< css::beans::XPropertySet >
+ m_xSubmission;
+ css::uno::Reference< css::beans::XPropertySet >
+ m_xTempBinding;
+ css::uno::Reference< css::beans::XPropertySet >
+ m_xCreatedBinding;
+
+ std::unique_ptr<weld::Entry> m_xNameED;
+ std::unique_ptr<weld::Entry> m_xActionED;
+ std::unique_ptr<weld::ComboBox> m_xMethodLB;
+ std::unique_ptr<weld::Entry> m_xRefED;
+ std::unique_ptr<weld::Button> m_xRefBtn;
+ std::unique_ptr<weld::ComboBox> m_xBindLB;
+ std::unique_ptr<weld::ComboBox> m_xReplaceLB;
+ std::unique_ptr<weld::Button> m_xOKBtn;
+
+ DECL_LINK(RefHdl, weld::Button&, void);
+ DECL_LINK(OKHdl, weld::Button&, void);
+
+ void FillAllBoxes();
+
+ public:
+ AddSubmissionDialog(weld::Window* pParent, ItemNode* pNode,
+ const css::uno::Reference< css::xforms::XFormsUIHelper1 >& rUIHelper);
+ virtual ~AddSubmissionDialog() override;
+
+ const css::uno::Reference< css::xforms::XSubmission >& GetNewSubmission() const { return m_xNewSubmission; }
+ };
+
+ class AddModelDialog final : public weld::GenericDialogController
+ {
+ private:
+ std::unique_ptr<weld::Entry> m_xNameED;
+ std::unique_ptr<weld::CheckButton> m_xModifyCB;
+ std::unique_ptr<weld::Label> m_xAltTitle;
+
+ public:
+ AddModelDialog(weld::Window* pParent, bool _bEdit);
+ virtual ~AddModelDialog() override;
+
+ OUString GetName() const { return m_xNameED->get_text(); }
+ void SetName( const OUString& _rName ) { m_xNameED->set_text( _rName );}
+
+ bool GetModifyDoc() const { return m_xModifyCB->get_active(); }
+ void SetModifyDoc( const bool bModify ) { m_xModifyCB->set_active(bModify); }
+ };
+
+ class AddInstanceDialog final : public weld::GenericDialogController
+ {
+ private:
+ OUString m_sAllFilterName;
+
+ std::unique_ptr<weld::Entry> m_xNameED;
+ std::unique_ptr<weld::Label> m_xURLFT;
+ std::unique_ptr<SvtURLBox> m_xURLED;
+ std::unique_ptr<weld::Button> m_xFilePickerBtn;
+ std::unique_ptr<weld::CheckButton> m_xLinkInstanceCB;
+ std::unique_ptr<weld::Label> m_xAltTitle;
+
+ DECL_LINK(FilePickerHdl, weld::Button&, void);
+
+ public:
+ AddInstanceDialog(weld::Window* pParent, bool _bEdit);
+ virtual ~AddInstanceDialog() override;
+
+ OUString GetName() const { return m_xNameED->get_text(); }
+ void SetName( const OUString& rName ) { m_xNameED->set_text( rName );}
+ OUString GetURL() const { return m_xURLED->get_active_text(); }
+ void SetURL( const OUString& rURL ) { m_xURLED->set_entry_text( rURL );}
+ bool IsLinkInstance() const { return m_xLinkInstanceCB->get_active(); }
+ void SetLinkInstance( bool bLink ) { m_xLinkInstanceCB->set_active(bLink); }
+ };
+
+ class LinkedInstanceWarningBox final : public weld::MessageDialogController
+ {
+ public:
+ LinkedInstanceWarningBox(weld::Widget* pParent);
+ };
+}
+
+
+#endif // INCLUDED_SVX_SOURCE_INC_DATANAVI_HXX
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/inc/delayedevent.hxx b/svx/source/inc/delayedevent.hxx
new file mode 100644
index 000000000..4f7925c94
--- /dev/null
+++ b/svx/source/inc/delayedevent.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_SVX_SOURCE_INC_DELAYEDEVENT_HXX
+#define INCLUDED_SVX_SOURCE_INC_DELAYEDEVENT_HXX
+
+#include <tools/link.hxx>
+
+struct ImplSVEvent;
+
+namespace svxform
+{
+
+
+ //= DelayedEvent
+
+ /** small class which encapsulates posting a Link instance as ApplicationUserEvent
+
+ No thread safety at all here, just a little saving of code to type multiple times
+ */
+ class DelayedEvent
+ {
+ public:
+ DelayedEvent( const Link<void*,void>& _rHandler )
+ :m_aHandler( _rHandler )
+ ,m_nEventId( nullptr )
+ {
+ }
+
+ ~DelayedEvent()
+ {
+ CancelPendingCall();
+ }
+
+ /** calls the handler asynchronously
+
+ If there's already a call pending, this previous call is cancelled.
+ */
+ void Call();
+
+ /** cancels a call which is currently pending
+
+ If no call is currently pending, then this is ignored.
+ */
+ void CancelPendingCall();
+
+ private:
+ Link<void*,void> m_aHandler;
+ ImplSVEvent * m_nEventId;
+
+ private:
+ DECL_LINK( OnCall, void*, void );
+
+ private:
+ DelayedEvent( const DelayedEvent& ) = delete;
+ DelayedEvent& operator=( const DelayedEvent& ) = delete;
+ };
+
+
+}
+
+
+#endif // INCLUDED_SVX_SOURCE_INC_DELAYEDEVENT_HXX
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/inc/docrecovery.hxx b/svx/source/inc/docrecovery.hxx
new file mode 100644
index 000000000..9b69342bc
--- /dev/null
+++ b/svx/source/inc/docrecovery.hxx
@@ -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 .
+ */
+
+#ifndef INCLUDED_SVX_SOURCE_INC_DOCRECOVERY_HXX
+#define INCLUDED_SVX_SOURCE_INC_DOCRECOVERY_HXX
+
+#include <vcl/weld.hxx>
+#include <o3tl/typed_flags_set.hxx>
+
+#include <cppuhelper/implbase.hxx>
+#include <com/sun/star/task/XStatusIndicator.hpp>
+#include <com/sun/star/frame/XStatusListener.hpp>
+#include <com/sun/star/frame/XDispatch.hpp>
+#include <com/sun/star/lang/XComponent.hpp>
+#include <com/sun/star/uno/XComponentContext.hpp>
+
+
+#define RECOVERY_CMDPART_PROTOCOL "vnd.sun.star.autorecovery:"
+
+#define RECOVERY_CMDPART_DO_EMERGENCY_SAVE "/doEmergencySave"
+#define RECOVERY_CMDPART_DO_RECOVERY "/doAutoRecovery"
+#define RECOVERY_CMDPART_DO_BRINGTOFRONT "/doBringToFront"
+
+inline constexpr OUStringLiteral RECOVERY_CMD_DO_PREPARE_EMERGENCY_SAVE = u"vnd.sun.star.autorecovery:/doPrepareEmergencySave";
+inline constexpr OUStringLiteral RECOVERY_CMD_DO_EMERGENCY_SAVE = u"vnd.sun.star.autorecovery:/doEmergencySave";
+inline constexpr OUStringLiteral RECOVERY_CMD_DO_RECOVERY = u"vnd.sun.star.autorecovery:/doAutoRecovery";
+inline constexpr OUStringLiteral RECOVERY_CMD_DO_ENTRY_BACKUP = u"vnd.sun.star.autorecovery:/doEntryBackup";
+inline constexpr OUStringLiteral RECOVERY_CMD_DO_ENTRY_CLEANUP = u"vnd.sun.star.autorecovery:/doEntryCleanUp";
+
+inline constexpr OUStringLiteral PROP_STATUSINDICATOR = u"StatusIndicator";
+inline constexpr OUStringLiteral PROP_DISPATCHASYNCHRON = u"DispatchAsynchron";
+inline constexpr OUStringLiteral PROP_SAVEPATH = u"SavePath";
+inline constexpr OUStringLiteral PROP_ENTRYID = u"EntryID";
+
+inline constexpr OUStringLiteral STATEPROP_ID = u"ID";
+inline constexpr OUStringLiteral STATEPROP_STATE = u"DocumentState";
+inline constexpr OUStringLiteral STATEPROP_ORGURL = u"OriginalURL";
+inline constexpr OUStringLiteral STATEPROP_TEMPURL = u"TempURL";
+inline constexpr OUStringLiteral STATEPROP_FACTORYURL = u"FactoryURL";
+inline constexpr OUStringLiteral STATEPROP_TEMPLATEURL = u"TemplateURL";
+inline constexpr OUStringLiteral STATEPROP_TITLE = u"Title";
+inline constexpr OUStringLiteral STATEPROP_MODULE = u"Module";
+
+#define RECOVERY_OPERATIONSTATE_START "start"
+#define RECOVERY_OPERATIONSTATE_STOP "stop"
+#define RECOVERY_OPERATIONSTATE_UPDATE "update"
+
+#define DLG_RET_UNKNOWN -1
+#define DLG_RET_OK RET_OK
+#define DLG_RET_CANCEL RET_CANCEL
+#define DLG_RET_OK_AUTOLUNCH 101
+
+
+enum class EDocStates
+{
+ /* TEMP STATES */
+
+ /// default state, if a document was new created or loaded
+ Unknown = 0x000,
+ /** an action was started (saving/loading) ... Can be interesting later if the process may be was interrupted by an exception. */
+ TryLoadBackup = 0x010,
+ TryLoadOriginal = 0x020,
+
+ /* FINAL STATES */
+
+ /// the Auto/Emergency saved document isn't usable any longer
+ Damaged = 0x040,
+ /// the Auto/Emergency saved document is not really up-to-date (some changes can be missing)
+ Incomplete = 0x080,
+ /// the Auto/Emergency saved document was processed successfully
+ Succeeded = 0x200
+};
+namespace o3tl {
+ template<> struct typed_flags<EDocStates> : is_typed_flags<EDocStates, 0x2f0> {};
+}
+
+
+namespace svx{
+ namespace DocRecovery{
+
+
+enum ERecoveryState
+{
+ E_SUCCESSFULLY_RECOVERED,
+ E_ORIGINAL_DOCUMENT_RECOVERED,
+ E_RECOVERY_FAILED,
+ E_RECOVERY_IS_IN_PROGRESS,
+ E_NOT_RECOVERED_YET
+};
+
+
+struct TURLInfo
+{
+ public:
+
+ /// unique ID, which is specified by the underlying autorecovery core!
+ sal_Int32 ID;
+
+ /// the full qualified document URL
+ OUString OrgURL;
+
+ /// the full qualified URL of the temp. file (if it's exists)
+ OUString TempURL;
+
+ /// a may be existing factory URL (e.g. for untitled documents)
+ OUString FactoryURL;
+
+ /// may be the document base on a template file !?
+ OUString TemplateURL;
+
+ /// the pure file name, without path, disc etcpp.
+ OUString DisplayName;
+
+ /// the application module, where this document was loaded
+ OUString Module;
+
+ /// state info as e.g. VALID, CORRUPTED, NON EXISTING ...
+ EDocStates DocState;
+
+ /// ui representation for DocState!
+ ERecoveryState RecoveryState;
+
+ /// standard icon
+ OUString StandardImageId;
+
+ public:
+
+ TURLInfo()
+ : ID (-1 )
+ , DocState (EDocStates::Unknown)
+ , RecoveryState(E_NOT_RECOVERED_YET)
+ {}
+};
+
+
+typedef ::std::vector< TURLInfo > TURLList;
+
+
+class IRecoveryUpdateListener
+{
+ public:
+
+ // inform listener about changed items, which should be refreshed
+ virtual void updateItems() = 0;
+
+ // inform listener about ending of the asynchronous recovery operation
+ virtual void end() = 0;
+
+ // TODO
+ virtual void stepNext(TURLInfo* pItem) = 0;
+
+ protected:
+ ~IRecoveryUpdateListener() {}
+};
+
+
+class RecoveryCore final : public ::cppu::WeakImplHelper< css::frame::XStatusListener >
+{
+
+ // types, const
+ public:
+
+
+ // member
+ private:
+
+ /// TODO
+ css::uno::Reference< css::uno::XComponentContext > m_xContext;
+
+ /// TODO
+ css::uno::Reference< css::frame::XDispatch > m_xRealCore;
+
+ /// TODO
+ css::uno::Reference< css::task::XStatusIndicator > m_xProgress;
+
+ /// TODO
+ TURLList m_lURLs;
+
+ /// TODO
+ IRecoveryUpdateListener* m_pListener;
+
+ /** @short knows the reason, why we listen on our internal m_xRealCore
+ member.
+
+ @descr Because we listen for different operations
+ on the core dispatch implementation, we must know,
+ which URL we have to use for deregistration!
+ */
+ bool m_bListenForSaving;
+
+
+ // native interface
+ public:
+
+
+ /** @short TODO */
+ RecoveryCore(const css::uno::Reference< css::uno::XComponentContext >& rxContext,
+ bool bUsedForSaving);
+
+
+ /** @short TODO */
+ virtual ~RecoveryCore() override;
+
+
+ /** @short TODO */
+ const css::uno::Reference< css::uno::XComponentContext >& getComponentContext() const;
+
+
+ /** @short TODO */
+ TURLList& getURLListAccess();
+
+
+ /** @short TODO */
+ static bool isBrokenTempEntry(const TURLInfo& rInfo);
+ void saveBrokenTempEntries(const OUString& sSaveDir);
+ void saveAllTempEntries(const OUString& sSaveDir);
+ void forgetBrokenTempEntries();
+ void forgetAllRecoveryEntries();
+ void forgetBrokenRecoveryEntries();
+
+
+ /** @short TODO */
+ void setProgressHandler(const css::uno::Reference< css::task::XStatusIndicator >& xProgress);
+
+
+ /** @short TODO */
+ void setUpdateListener(IRecoveryUpdateListener* pListener);
+
+
+ /** @short TODO */
+ void doEmergencySavePrepare();
+ void doEmergencySave();
+ void doRecovery();
+
+
+ /** @short TODO */
+ static ERecoveryState mapDocState2RecoverState(EDocStates eDocState);
+
+
+ // uno interface
+ public:
+
+ // css.frame.XStatusListener
+ virtual void SAL_CALL statusChanged(const css::frame::FeatureStateEvent& aEvent) override;
+
+ // css.lang.XEventListener
+ virtual void SAL_CALL disposing(const css::lang::EventObject& aEvent) override;
+
+
+ // helper
+ private:
+
+
+ /** @short starts listening on the internal EmergencySave/AutoRecovery core.
+ */
+ void impl_startListening();
+
+
+ /** @short stop listening on the internal EmergencySave/AutoRecovery core.
+ */
+ void impl_stopListening();
+
+
+ /** @short TODO */
+ css::util::URL impl_getParsedURL(const OUString& sURL);
+};
+
+class PluginProgress final : public ::cppu::WeakImplHelper<css::task::XStatusIndicator, css::lang::XComponent>
+{
+// member
+private:
+ weld::ProgressBar* m_pProgressBar;
+ int m_nRange;
+
+// native interface
+public:
+ PluginProgress(weld::ProgressBar* pProgressBar);
+ virtual ~PluginProgress() override;
+
+// uno interface
+public:
+ // XStatusIndicator
+ virtual void SAL_CALL start(const OUString& sText, sal_Int32 nRange) override;
+ virtual void SAL_CALL end() override;
+ virtual void SAL_CALL setText(const OUString& sText) override;
+ virtual void SAL_CALL setValue(sal_Int32 nValue) override;
+ virtual void SAL_CALL reset() 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;
+};
+
+class SaveDialog final : public weld::GenericDialogController
+{
+// member
+private:
+ RecoveryCore* m_pCore;
+ std::unique_ptr<weld::TreeView> m_xFileListLB;
+ std::unique_ptr<weld::Button> m_xOkBtn;
+
+// interface
+public:
+ /** @short create all child controls of this dialog.
+
+ @descr The dialog isn't shown nor it starts any
+ action by itself!
+
+ @param pParent
+ can point to a parent window.
+ If it's set to 0, the defmodal-dialog-parent
+ is used automatically.
+
+ @param pCore
+ provides access to the recovery core service
+ and the current list of open documents,
+ which should be shown inside this dialog.
+ */
+ SaveDialog(weld::Window* pParent, RecoveryCore* pCore);
+ virtual ~SaveDialog() override;
+
+ DECL_LINK(OKButtonHdl, weld::Button&, void);
+};
+
+class SaveProgressDialog final : public weld::GenericDialogController
+ , public IRecoveryUpdateListener
+{
+ // member
+ private:
+ // @short TODO
+ RecoveryCore* m_pCore;
+
+ std::unique_ptr<weld::ProgressBar> m_xProgressBar;
+
+ // @short TODO
+ css::uno::Reference< css::task::XStatusIndicator > m_xProgress;
+ // interface
+ public:
+ /** @short create all child controls of this dialog.
+
+ @descr The dialog isn't shown nor it starts any
+ action by itself!
+
+ @param pParent
+ can point to a parent window.
+ If it's set to 0, the defmodal-dialog-parent
+ is used automatically.
+
+ @param pCore
+ used to start emergency save.
+ */
+ SaveProgressDialog(weld::Window* pParent,
+ RecoveryCore* pCore);
+ virtual ~SaveProgressDialog() override;
+
+ /** @short start the emergency save operation. */
+ virtual short run() override;
+
+ // IRecoveryUpdateListener
+ virtual void updateItems() override;
+ virtual void stepNext(TURLInfo* pItem) override;
+ virtual void end() override;
+};
+
+class RecoveryDialog final : public weld::GenericDialogController
+ , public IRecoveryUpdateListener
+{
+ // member
+ private:
+ OUString m_aTitleRecoveryInProgress;
+ OUString m_aRecoveryOnlyFinish;
+ OUString m_aRecoveryOnlyFinishDescr;
+
+ RecoveryCore* m_pCore;
+ css::uno::Reference< css::task::XStatusIndicator > m_xProgress;
+ enum EInternalRecoveryState
+ {
+ E_RECOVERY_PREPARED, // dialog started... recovery prepared
+ E_RECOVERY_IN_PROGRESS, // recovery core still in progress
+ E_RECOVERY_CORE_DONE, // recovery core finished it's task
+ E_RECOVERY_DONE, // user clicked "next" button
+ E_RECOVERY_CANCELED, // user clicked "cancel" button
+ E_RECOVERY_CANCELED_BEFORE, // user clicked "cancel" button before recovery was started
+ E_RECOVERY_CANCELED_AFTERWARDS, // user clicked "cancel" button after recovery was finished
+ E_RECOVERY_HANDLED // the recovery wizard page was shown already... and will be shown now again...
+ };
+ sal_Int32 m_eRecoveryState;
+ bool m_bWaitForCore;
+ bool m_bWasRecoveryStarted;
+
+ OUString m_aSuccessRecovStr;
+ OUString m_aOrigDocRecovStr;
+ OUString m_aRecovFailedStr;
+ OUString m_aRecovInProgrStr;
+ OUString m_aNotRecovYetStr;
+
+ std::unique_ptr<weld::Label> m_xDescrFT;
+ std::unique_ptr<weld::ProgressBar> m_xProgressBar;
+ std::unique_ptr<weld::TreeView> m_xFileListLB;
+ std::unique_ptr<weld::Button> m_xNextBtn;
+ std::unique_ptr<weld::Button> m_xCancelBtn;
+
+ // member
+ public:
+ /** @short TODO */
+ RecoveryDialog(weld::Window* pParent,
+ RecoveryCore* pCore);
+
+ virtual ~RecoveryDialog() override;
+
+ // IRecoveryUpdateListener
+ virtual void updateItems() override;
+ virtual void stepNext(TURLInfo* pItem) override;
+ virtual void end() override;
+
+ short execute();
+
+ // helper
+ private:
+ DECL_LINK(NextButtonHdl, weld::Button&, void);
+ DECL_LINK(CancelButtonHdl, weld::Button&, void);
+
+ OUString impl_getStatusString( const TURLInfo& rInfo ) const;
+ static OUString impl_getStatusImage( const TURLInfo& rInfo );
+};
+
+
+class BrokenRecoveryDialog final : public weld::GenericDialogController
+{
+// member
+private:
+ OUString m_sSavePath;
+ RecoveryCore* m_pCore;
+ bool const m_bBeforeRecovery;
+ bool m_bExecutionNeeded;
+
+ std::unique_ptr<weld::TreeView> m_xFileListLB;
+ std::unique_ptr<weld::Entry> m_xSaveDirED;
+ std::unique_ptr<weld::Button> m_xSaveDirBtn;
+ std::unique_ptr<weld::Button> m_xOkBtn;
+ std::unique_ptr<weld::Button> m_xCancelBtn;
+
+// interface
+public:
+
+ /** @short TODO */
+ BrokenRecoveryDialog(weld::Window* pParent,
+ RecoveryCore* pCore,
+ bool bBeforeRecovery);
+ virtual ~BrokenRecoveryDialog() override;
+
+ /** @short TODO */
+ bool isExecutionNeeded() const;
+
+
+ /** @short TODO */
+ const OUString& getSaveDirURL() const;
+
+
+// helper
+private:
+ /** @short TODO */
+ void impl_refresh();
+
+
+ /** @short TODO */
+ DECL_LINK(SaveButtonHdl, weld::Button&, void);
+
+
+ /** @short TODO */
+ DECL_LINK(OkButtonHdl, weld::Button&, void);
+
+
+ /** @short TODO */
+ DECL_LINK(CancelButtonHdl, weld::Button&, void);
+
+
+ /** @short TODO */
+ void impl_askForSavePath();
+};
+ }
+}
+
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/inc/filtnav.hxx b/svx/source/inc/filtnav.hxx
new file mode 100644
index 000000000..47af4017a
--- /dev/null
+++ b/svx/source/inc/filtnav.hxx
@@ -0,0 +1,339 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+#ifndef INCLUDED_SVX_SOURCE_INC_FILTNAV_HXX
+#define INCLUDED_SVX_SOURCE_INC_FILTNAV_HXX
+
+#include <com/sun/star/form/XForm.hpp>
+#include <com/sun/star/form/runtime/XFormController.hpp>
+#include <com/sun/star/form/runtime/XFilterController.hpp>
+#include <svl/lstner.hxx>
+#include <svl/SfxBroadcaster.hxx>
+
+#include <vcl/window.hxx>
+#include <sfx2/childwin.hxx>
+#include <svl/poolitem.hxx>
+#include <sfx2/bindings.hxx>
+#include <sfx2/dockwin.hxx>
+#include <sfx2/ctrlitem.hxx>
+
+#include "fmexch.hxx"
+#include "sqlparserclient.hxx"
+
+class FmFormShell;
+
+namespace svxform
+{
+
+class FmFilterItem;
+class FmFilterItems;
+class FmParentData;
+class FmFilterAdapter;
+
+// data structure for the filter model
+class FmFilterData
+{
+ FmParentData* m_pParent;
+ OUString m_aText;
+
+public:
+ FmFilterData(FmParentData* pParent, const OUString& rText)
+ :m_pParent( pParent )
+ ,m_aText( rText )
+ {}
+ virtual ~FmFilterData(){}
+
+ void SetText( const OUString& rText ){ m_aText = rText; }
+ const OUString& GetText() const { return m_aText; }
+ FmParentData* GetParent() const {return m_pParent;}
+
+ virtual OUString GetImage() const;
+};
+
+class FmParentData : public FmFilterData
+{
+protected:
+ ::std::vector< std::unique_ptr<FmFilterData> > m_aChildren;
+
+public:
+ FmParentData(FmParentData* pParent, const OUString& rText)
+ : FmFilterData(pParent, rText)
+ {}
+ virtual ~FmParentData() override;
+
+ ::std::vector< std::unique_ptr<FmFilterData> >& GetChildren() { return m_aChildren; }
+};
+
+// Item representing the forms and subforms
+class FmFormItem final : public FmParentData
+{
+ css::uno::Reference< css::form::runtime::XFormController > m_xController;
+ css::uno::Reference< css::form::runtime::XFilterController > m_xFilterController;
+
+public:
+
+ FmFormItem( FmParentData* _pParent,
+ const css::uno::Reference< css::form::runtime::XFormController > & _xController,
+ const OUString& _rText)
+ :FmParentData( _pParent, _rText )
+ ,m_xController( _xController )
+ ,m_xFilterController( _xController, css::uno::UNO_QUERY_THROW )
+ {
+ }
+
+ const css::uno::Reference< css::form::runtime::XFormController >&
+ GetController() const { return m_xController; }
+
+ const css::uno::Reference< css::form::runtime::XFilterController >&
+ GetFilterController() const { return m_xFilterController; }
+
+ virtual OUString GetImage() const override;
+};
+
+class FmFilterItems final : public FmParentData
+{
+public:
+ FmFilterItems(FmFormItem* pParent, const OUString& rText ) : FmParentData(pParent, rText) {}
+
+ FmFilterItem* Find( const ::sal_Int32 _nFilterComponentIndex ) const;
+ virtual OUString GetImage() const override;
+};
+
+class FmFilterItem final : public FmFilterData
+{
+ OUString m_aFieldName;
+ sal_Int32 m_nComponentIndex;
+
+public:
+ FmFilterItem(
+ FmFilterItems* pParent,
+ const OUString& aFieldName,
+ const OUString& aCondition,
+ const sal_Int32 _nComponentIndex
+ );
+
+ const OUString& GetFieldName() const {return m_aFieldName;}
+ sal_Int32 GetComponentIndex() const { return m_nComponentIndex; }
+
+ virtual OUString GetImage() const override;
+};
+
+class FmFilterModel final : public FmParentData
+ ,public SfxBroadcaster
+ ,public ::svxform::OSQLParserClient
+{
+ friend class FmFilterAdapter;
+
+ css::uno::Reference< css::container::XIndexAccess > m_xControllers;
+ css::uno::Reference< css::form::runtime::XFormController > m_xController;
+ rtl::Reference<FmFilterAdapter> m_pAdapter;
+ FmFilterItems* m_pCurrentItems;
+
+public:
+ FmFilterModel();
+ virtual ~FmFilterModel() override;
+
+ void Update(const css::uno::Reference< css::container::XIndexAccess > & xControllers, const css::uno::Reference< css::form::runtime::XFormController > & xCurrent);
+ void Clear();
+ bool ValidateText(FmFilterItem const * pItem, OUString& rText, OUString& rErrorMsg) const;
+ void Append(FmFilterItems* pItems, std::unique_ptr<FmFilterItem> pFilterItem);
+ void SetTextForItem(FmFilterItem* pItem, const OUString& rText);
+
+ FmFormItem* GetCurrentForm() const {return m_pCurrentItems ? static_cast<FmFormItem*>(m_pCurrentItems->GetParent()) : nullptr;}
+ FmFilterItems* GetCurrentItems() const {return m_pCurrentItems;}
+ void SetCurrentItems(FmFilterItems* pCurrent);
+
+ const css::uno::Reference< css::form::runtime::XFormController > & GetCurrentController() const {return m_xController;}
+ void SetCurrentController(const css::uno::Reference< css::form::runtime::XFormController > & xController);
+
+ void Remove(FmFilterData* pFilterItem);
+ static void AppendFilterItems( FmFormItem& _rItem );
+ void EnsureEmptyFilterRows( FmParentData& _rItem );
+
+private:
+ void Insert(const ::std::vector<std::unique_ptr<FmFilterData>>::iterator& rPos, std::unique_ptr<FmFilterData> pFilterItem);
+ void Remove( const ::std::vector<std::unique_ptr<FmFilterData>>::iterator& rPos );
+ FmFormItem* Find(const ::std::vector<std::unique_ptr<FmFilterData>>& rItems, const css::uno::Reference< css::form::runtime::XFormController > & xController) const;
+ FmFormItem* Find(const ::std::vector<std::unique_ptr<FmFilterData>>& rItems, const css::uno::Reference< css::form::XForm >& xForm) const;
+ void Update(const css::uno::Reference< css::container::XIndexAccess > & xControllers, FmParentData* pParent);
+};
+
+
+class OFilterItemExchange final : public OLocalExchange
+{
+ ::std::vector<FmFilterItem*> m_aDraggedEntries;
+ FmFormItem* m_pFormItem; // ensure that we drop on the same form
+
+public:
+ OFilterItemExchange();
+
+ static SotClipboardFormatId getFormatId( );
+ inline static bool hasFormat( const DataFlavorExVector& _rFormats );
+
+ const ::std::vector<FmFilterItem*>& getDraggedEntries() const { return m_aDraggedEntries; }
+ void setDraggedEntries(::std::vector<FmFilterItem*>&& _rList) { m_aDraggedEntries = std::move(_rList); }
+ FmFormItem* getFormItem() const { return m_pFormItem; }
+
+ void setFormItem( FmFormItem* _pItem ) { m_pFormItem = _pItem; }
+
+private:
+ virtual void AddSupportedFormats() override;
+};
+
+inline bool OFilterItemExchange::hasFormat( const DataFlavorExVector& _rFormats )
+{
+ return OLocalExchange::hasFormat( _rFormats, getFormatId() );
+}
+
+class OFilterExchangeHelper final : public OLocalExchangeHelper
+{
+public:
+ OFilterExchangeHelper() : OLocalExchangeHelper() { }
+
+ OFilterItemExchange* operator->() const { return static_cast<OFilterItemExchange*>(m_xTransferable.get()); }
+ OFilterItemExchange& operator*() const { return *static_cast<OFilterItemExchange*>(m_xTransferable.get()); }
+
+private:
+ virtual rtl::Reference<OLocalExchange> createExchange() const override;
+};
+
+class FmFilterNavigator;
+
+class FmFilterNavigatorDropTarget final : public DropTargetHelper
+{
+private:
+ FmFilterNavigator& m_rTreeView;
+
+ virtual sal_Int8 AcceptDrop( const AcceptDropEvent& rEvt ) override;
+ virtual sal_Int8 ExecuteDrop( const ExecuteDropEvent& rEvt ) override;
+
+public:
+ FmFilterNavigatorDropTarget(FmFilterNavigator& rTreeView);
+};
+
+class FmFilterNavigator final : public SfxListener
+{
+ VclPtr<vcl::Window> m_xTopLevel;
+ std::unique_ptr<weld::TreeView> m_xTreeView;
+ FmFilterNavigatorDropTarget m_aDropTargetHelper;
+
+ std::unique_ptr<FmFilterModel> m_pModel;
+ std::unique_ptr<weld::TreeIter> m_xEditingCurrently;
+ OFilterExchangeHelper m_aControlExchange;
+
+ ImplSVEvent* m_nAsyncRemoveEvent;
+
+public:
+ FmFilterNavigator(vcl::Window* pTopLevel, std::unique_ptr<weld::TreeView> xTreeView);
+ virtual ~FmFilterNavigator() override;
+
+ void GrabFocus() { m_xTreeView->grab_focus(); }
+
+ void EndEditing();
+
+ void UpdateContent(
+ const css::uno::Reference< css::container::XIndexAccess > & xControllers,
+ const css::uno::Reference< css::form::runtime::XFormController > & xCurrent
+ );
+
+ weld::TreeView& get_widget() { return *m_xTreeView; }
+
+ sal_Int8 AcceptDrop(const AcceptDropEvent& rEvt);
+ sal_Int8 ExecuteDrop(const ExecuteDropEvent& rEvt);
+
+private:
+ DECL_LINK(KeyInputHdl, const KeyEvent&, bool);
+ DECL_LINK(PopupMenuHdl, const CommandEvent&, bool);
+ virtual void Notify( SfxBroadcaster& rBC, const SfxHint& rHint ) override;
+
+ DECL_STATIC_LINK(FmFilterNavigator, CustomGetSizeHdl, weld::TreeView::get_size_args, Size);
+ DECL_STATIC_LINK(FmFilterNavigator, CustomRenderHdl, weld::TreeView::render_args, void);
+
+ DECL_LINK(SelectHdl, weld::TreeView&, void);
+ DECL_LINK(EditingEntryHdl, const weld::TreeIter&, bool);
+ typedef std::pair<const weld::TreeIter&, OUString> IterString;
+ DECL_LINK(EditedEntryHdl, const IterString&, bool);
+
+ DECL_LINK(DragBeginHdl, bool&, bool);
+
+ void DeleteSelection();
+ std::unique_ptr<weld::TreeIter> FindEntry(const FmFilterData* pItem) const;
+ void Insert(const FmFilterData* pItem, int nPos);
+ void Remove(FmFilterData const * pItem);
+
+ DECL_LINK(OnRemove, void*, void);
+
+ /** returns the first form item and the selected FilterItems in the vector
+ @param _rItemList
+ Is filled inside. <OUT/>
+ @return
+ The first form item.
+ */
+ FmFormItem* getSelectedFilterItems(::std::vector<FmFilterItem*>& _rItemList);
+
+ /**
+ * inserts the filter items into the tree model and creates new FilterItems if needed.
+ * @param _rFilterList
+ * The items which should be inserted.
+ * @param _pTargetItems
+ * The target where to insert the items.
+ * @param _bCopy
+ * If <TRUE/> the items will not be removed from the model, otherwise they will.
+ */
+ void insertFilterItem(const ::std::vector<FmFilterItem*>& _rFilterList,FmFilterItems* _pTargetItems, bool _bCopy);
+
+ bool getPrevEntry(weld::TreeIter& rEntry);
+ bool getNextEntry(weld::TreeIter& rEntry);
+};
+
+class FmFilterNavigatorWin final : public SfxDockingWindow, public SfxControllerItem
+{
+private:
+ std::unique_ptr<FmFilterNavigator> m_xNavigatorTree;
+
+ virtual bool Close() override;
+ virtual void GetFocus() override;
+ virtual Size CalcDockingSize( SfxChildAlignment ) override;
+ virtual SfxChildAlignment CheckAlignment( SfxChildAlignment, SfxChildAlignment ) override;
+
+ using SfxDockingWindow::StateChanged;
+
+public:
+ FmFilterNavigatorWin( SfxBindings *pBindings, SfxChildWindow *pMgr,
+ vcl::Window* pParent );
+ virtual ~FmFilterNavigatorWin() override;
+ virtual void dispose() override;
+
+ void UpdateContent( FmFormShell const * pFormShell );
+ void StateChangedAtToolBoxControl( sal_uInt16 nSID, SfxItemState eState, const SfxPoolItem* pState ) override;
+ void FillInfo( SfxChildWinInfo& rInfo ) const override;
+};
+
+class FmFilterNavigatorWinMgr final : public SfxChildWindow
+{
+public:
+ FmFilterNavigatorWinMgr( vcl::Window *pParent, sal_uInt16 nId, SfxBindings *pBindings,
+ SfxChildWinInfo *pInfo );
+ SFX_DECL_CHILDWINDOW( FmFilterNavigatorWinMgr );
+};
+
+}
+
+#endif // INCLUDED_SVX_SOURCE_INC_FILTNAV_HXX
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/inc/findtextfield.hxx b/svx/source/inc/findtextfield.hxx
new file mode 100644
index 000000000..bce8b5589
--- /dev/null
+++ b/svx/source/inc/findtextfield.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 .
+ */
+
+#pragma once
+
+#include <vcl/InterimItemWindow.hxx>
+#include <com/sun/star/frame/XFrame.hpp>
+#include <com/sun/star/uno/XComponentContext.hpp>
+
+namespace svt
+{
+class AcceleratorExecute;
+}
+
+class FindTextFieldControl final : public InterimItemWindow
+{
+public:
+ FindTextFieldControl(vcl::Window* pParent,
+ css::uno::Reference<css::frame::XFrame> const& xFrame,
+ const css::uno::Reference<css::uno::XComponentContext>& xContext);
+
+ virtual void dispose() override;
+
+ virtual ~FindTextFieldControl() override;
+
+ void Remember_Impl(const OUString& rStr);
+ void SetTextToSelected_Impl();
+
+ void connect_changed(const Link<weld::ComboBox&, void>& rLink);
+
+ int get_count() const;
+ OUString get_text(int nIndex) const;
+ OUString get_active_text() const;
+ void append_text(const OUString& rText);
+ void set_entry_message_type(weld::EntryMessageType eType);
+
+private:
+ ImplSVEvent* m_nAsyncGetFocusId;
+ std::unique_ptr<weld::ComboBox> m_xWidget;
+ css::uno::Reference<css::frame::XFrame> m_xFrame;
+ css::uno::Reference<css::uno::XComponentContext> m_xContext;
+ std::unique_ptr<svt::AcceleratorExecute> m_pAcc;
+ Link<weld::ComboBox&, void> m_aChangeHdl;
+
+ DECL_LINK(FocusInHdl, weld::Widget&, void);
+ DECL_LINK(KeyInputHdl, const KeyEvent&, bool);
+ DECL_LINK(ActivateHdl, weld::ComboBox&, bool);
+ DECL_LINK(OnAsyncGetFocus, void*, void);
+
+ void FocusIn();
+ void ActivateFind(bool bShift);
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/inc/fmPropBrw.hxx b/svx/source/inc/fmPropBrw.hxx
new file mode 100644
index 000000000..b33597b24
--- /dev/null
+++ b/svx/source/inc/fmPropBrw.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_SVX_SOURCE_INC_FMPROPBRW_HXX
+#define INCLUDED_SVX_SOURCE_INC_FMPROPBRW_HXX
+
+#include <com/sun/star/frame/XFrame2.hpp>
+#include <com/sun/star/inspection/XObjectInspectorModel.hpp>
+#include <com/sun/star/uno/XComponentContext.hpp>
+
+#include <sfx2/basedlgs.hxx>
+#include <sfx2/ctrlitem.hxx>
+#include <sfx2/childwin.hxx>
+#include <svx/fmtools.hxx>
+
+
+class FmPropBrwMgr final : public SfxChildWindow
+{
+public:
+ FmPropBrwMgr(vcl::Window *pParent, sal_uInt16 nId, SfxBindings *pBindings, const SfxChildWinInfo *pInfo);
+ SFX_DECL_CHILDWINDOW(FmPropBrwMgr);
+};
+
+class SfxBindings;
+class FmFormShell;
+
+class FmPropBrw final : public SfxModelessDialogController, public SfxControllerItem
+{
+ bool m_bInitialStateChange;
+ weld::Window* m_pParent;
+ ImplSVEvent* m_nAsyncGetFocusId;
+ OUString m_sLastActivePage;
+ std::unique_ptr<weld::Container> m_xContainer;
+ css::uno::Reference< css::uno::XComponentContext >
+ m_xInspectorContext;
+ css::uno::Reference< css::uno::XComponentContext >
+ m_xORB;
+ css::uno::Reference< css::frame::XFrame2 >
+ m_xMeAsFrame;
+ css::uno::Reference< css::uno::XInterface >
+ m_xLastKnownDocument;
+ css::uno::Reference< css::inspection::XObjectInspectorModel >
+ m_xInspectorModel;
+ css::uno::Reference< css::frame::XController >
+ m_xBrowserController;
+
+ virtual void StateChangedAtToolBoxControl(sal_uInt16 nSID, SfxItemState eState, const SfxPoolItem* pState) override;
+ virtual void FillInfo( SfxChildWinInfo& rInfo ) const override;
+ virtual void Close() override;
+
+ DECL_LINK( OnAsyncGetFocus, void*, void );
+
+ void implSetNewSelection( const InterfaceBag& _rSelection );
+ void implDetachController();
+ bool implIsReadOnlyModel() const;
+ OUString getCurrentPage() const;
+
+public:
+ FmPropBrw(
+ const css::uno::Reference< css::uno::XComponentContext >& _xORB,
+ SfxBindings* pBindings,
+ SfxChildWindow* pMgr,
+ weld::Window* pParent,
+ const SfxChildWinInfo* _pInfo
+ );
+ virtual ~FmPropBrw() override;
+
+private:
+
+ /** creates the PropertyBrowser (aka ObjectInspector) and plugs it into our frame
+
+ This method ensures that a new component is created every time the XModel which
+ we're working for changed. This is necessary since this model is part of the
+ ComponentContext we use to create the ObjectInspector.
+ */
+ void impl_ensurePropertyBrowser_nothrow( FmFormShell* _pFormShell );
+
+ /** creates a property browser
+
+ After this method returns, m_xBrowserController is not <NULL/>.
+
+ @precond
+ we don't have an ObjectInspector, yet, i.e. m_xBrowserController is <NULL/>.
+ */
+ void impl_createPropertyBrowser_throw( FmFormShell* _pFormShell );
+};
+#endif // INCLUDED_SVX_SOURCE_INC_FMPROPBRW_HXX
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/inc/fmcontrolbordermanager.hxx b/svx/source/inc/fmcontrolbordermanager.hxx
new file mode 100644
index 000000000..620d8b98d
--- /dev/null
+++ b/svx/source/inc/fmcontrolbordermanager.hxx
@@ -0,0 +1,230 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#ifndef INCLUDED_SVX_SOURCE_INC_FMCONTROLBORDERMANAGER_HXX
+#define INCLUDED_SVX_SOURCE_INC_FMCONTROLBORDERMANAGER_HXX
+
+#include <com/sun/star/awt/VisualEffect.hpp>
+#include <com/sun/star/awt/FontUnderline.hpp>
+#include <com/sun/star/awt/XControl.hpp>
+#include <com/sun/star/awt/XVclWindowPeer.hpp>
+#include <o3tl/typed_flags_set.hxx>
+#include <tools/color.hxx>
+
+#include <set>
+
+namespace com::sun::star::form::validation { class XValidatableFormComponent; }
+
+enum class ControlStatus {
+ NONE = 0x00,
+ Focused = 0x01,
+ MouseHover = 0x02,
+ Invalid = 0x04
+};
+namespace o3tl {
+ template<> struct typed_flags<ControlStatus> : is_typed_flags<ControlStatus, 0x07> {};
+}
+
+
+namespace svxform
+{
+
+
+ struct BorderDescriptor
+ {
+ sal_Int16 nBorderType;
+ Color nBorderColor;
+
+ BorderDescriptor()
+ :nBorderType( css::awt::VisualEffect::FLAT )
+ {
+ }
+ };
+
+ struct UnderlineDescriptor
+ {
+ sal_Int16 nUnderlineType;
+ Color nUnderlineColor;
+
+ UnderlineDescriptor()
+ :nUnderlineType( css::awt::FontUnderline::NONE )
+ {
+ }
+
+ UnderlineDescriptor( sal_Int16 _nUnderlineType, Color _nUnderlineColor )
+ :nUnderlineType( _nUnderlineType )
+ ,nUnderlineColor( _nUnderlineColor )
+ {
+ }
+ };
+
+ struct ControlData : public BorderDescriptor, UnderlineDescriptor
+ {
+ css::uno::Reference< css::awt::XControl > xControl;
+ OUString sOriginalHelpText;
+
+ ControlData() : BorderDescriptor() { }
+ ControlData( const css::uno::Reference< css::awt::XControl >& _rxControl )
+ :xControl( _rxControl )
+ {
+ }
+ };
+
+
+ //= ControlBorderManager
+
+ /** manages the dynamic border color for form controls
+
+ Used by the <type>FormController</type>, this class manages the dynamic changes in the
+ border color of form controls. For this a set of events have to be forwarded to the manager
+ instance, which then will switch the border color depending on the mouse and focus status
+ of the controls.
+ */
+ class ControlBorderManager
+ {
+ private:
+ struct ControlDataCompare
+ {
+ bool operator()( const ControlData& _rLHS, const ControlData& _rRHS ) const
+ {
+ return _rLHS.xControl.get() < _rRHS.xControl.get();
+ }
+ };
+
+ typedef ::std::set< ControlData, ControlDataCompare > ControlBag;
+ typedef ::std::set< css::uno::Reference< css::awt::XVclWindowPeer > > PeerBag;
+
+ PeerBag m_aColorableControls;
+ PeerBag m_aNonColorableControls;
+
+ ControlData m_aFocusControl;
+ ControlData m_aMouseHoverControl;
+ ControlBag m_aInvalidControls;
+
+
+ // attributes
+ Color m_nFocusColor;
+ Color m_nMouseHoveColor;
+ Color m_nInvalidColor;
+ bool m_bDynamicBorderColors;
+
+ public:
+ ControlBorderManager();
+ ~ControlBorderManager();
+
+ public:
+ void focusGained( const css::uno::Reference< css::uno::XInterface >& _rxControl );
+ void focusLost( const css::uno::Reference< css::uno::XInterface >& _rxControl );
+ void mouseEntered( const css::uno::Reference< css::uno::XInterface >& _rxControl );
+ void mouseExited( const css::uno::Reference< css::uno::XInterface >& _rxControl );
+
+ void validityChanged(
+ const css::uno::Reference< css::awt::XControl >& _rxControl,
+ const css::uno::Reference< css::form::validation::XValidatableFormComponent >& _rxValidatable
+ );
+
+ /// enables dynamic border color for the controls
+ void enableDynamicBorderColor( );
+ /// disables dynamic border color for the controls
+ void disableDynamicBorderColor( );
+
+ /** sets a color to be used for a given status
+ @param _nStatus
+ the status which the color should be applied for. Must not be ControlStatus::NONE
+ @param _nColor
+ the color to apply for the given status
+ */
+ void setStatusColor( ControlStatus _nStatus, Color _nColor );
+
+ /** restores all colors of all controls where we possibly changed them
+ */
+ void restoreAll();
+
+ private:
+ /** called when a control got one of the two possible statuses (focused, and hovered with the mouse)
+ @param _rxControl
+ the control which gained the status
+ @param _rControlData
+ the control's status data, as a reference to our respective member
+ */
+ void controlStatusGained(
+ const css::uno::Reference< css::uno::XInterface >& _rxControl,
+ ControlData& _rControlData
+ );
+
+ /** called when a control lost one of the two possible statuses (focused, and hovered with the mouse)
+ @param _rxControl
+ the control which lost the status
+ @param _rControlData
+ the control's status data, as a reference to our respective member
+ */
+ void controlStatusLost( const css::uno::Reference< css::uno::XInterface >& _rxControl, ControlData& _rControlData );
+
+ /** determines whether the border of a given peer can be colored
+ @param _rxPeer
+ the peer to examine. Must not be <NULL/>
+ */
+ bool canColorBorder( const css::uno::Reference< css::awt::XVclWindowPeer >& _rxPeer );
+
+ /** determines the status of the given control
+ */
+ ControlStatus getControlStatus( const css::uno::Reference< css::awt::XControl >& _rxControl );
+
+ /** retrieves the color associated with a given ControlStatus
+ @param _eStatus
+ the status of the control. Must not be <member>ControlStatus::none</member>
+ */
+ Color getControlColorByStatus( ControlStatus _eStatus ) const;
+
+ /** sets the border color for a given control, depending on its status
+ @param _rxControl
+ the control to set the border color for. Must not be <NULL/>
+ @param _rxPeer
+ the peer of the control, to be passed herein for optimization the caller usually needs it, anyway).
+ Must not be <NULL/>
+ @param _rFallback
+ the color/type to use when the control has the status ControlStatus::NONE
+ */
+ void updateBorderStyle(
+ const css::uno::Reference< css::awt::XControl >& _rxControl,
+ const css::uno::Reference< css::awt::XVclWindowPeer >& _rxPeer,
+ const BorderDescriptor& _rFallback
+ );
+
+ /** determines the to-be-remembered original border color and type for a control
+
+ The method also takes into account that the control may currently have an overwritten
+ border style
+
+ @param _rxControl
+ the control to examine. Must not be <NULL/>, and have a non-<NULL/> peer
+ */
+ void determineOriginalBorderStyle(
+ const css::uno::Reference< css::awt::XControl >& _rxControl,
+ BorderDescriptor& _rData
+ ) const;
+ };
+
+
+}
+
+
+#endif // INCLUDED_SVX_SOURCE_INC_FMCONTROLBORDERMANAGER_HXX
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/inc/fmcontrollayout.hxx b/svx/source/inc/fmcontrollayout.hxx
new file mode 100644
index 000000000..f2494d83e
--- /dev/null
+++ b/svx/source/inc/fmcontrollayout.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_SVX_SOURCE_INC_FMCONTROLLAYOUT_HXX
+#define INCLUDED_SVX_SOURCE_INC_FMCONTROLLAYOUT_HXX
+
+#include <com/sun/star/beans/XPropertySet.hpp>
+
+#include "fmdocumentclassification.hxx"
+
+
+namespace svxform::ControlLayouter
+{
+ /** initializes the layout of a newly created form control (model)
+ */
+ void initializeControlLayout(
+ const css::uno::Reference< css::beans::XPropertySet >& _rxControlModel,
+ DocumentType _eDocType
+ );
+
+ /** determines whether for the given document type, dynamic control border coloring is enabled
+ */
+ bool useDynamicBorderColor( DocumentType _eDocType );
+
+ /** determines whether for the given document type, form controls should use the document's reference device
+ for text rendering
+ */
+ bool useDocumentReferenceDevice( DocumentType _eDocType );
+
+ /** gets the "default" style in a document which can be used if some default text format is needed
+
+ It depends on the type document type which concrete kind of style is returned, but it is expected to support
+ the css.style.CharacterProperties service.
+
+ @param _rxModel
+ a form component.
+ */
+ css::uno::Reference< css::beans::XPropertySet >
+ getDefaultDocumentTextStyle( const css::uno::Reference< css::beans::XPropertySet >& _rxModel );
+
+}
+
+
+#endif // INCLUDED_SVX_SOURCE_INC_FMCONTROLLAYOUT_HXX
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/inc/fmdocumentclassification.hxx b/svx/source/inc/fmdocumentclassification.hxx
new file mode 100644
index 000000000..e50523348
--- /dev/null
+++ b/svx/source/inc/fmdocumentclassification.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_SVX_SOURCE_INC_FMDOCUMENTCLASSIFICATION_HXX
+#define INCLUDED_SVX_SOURCE_INC_FMDOCUMENTCLASSIFICATION_HXX
+
+#include <com/sun/star/frame/XModel.hpp>
+
+
+namespace svxform
+{
+
+ enum DocumentType
+ {
+ eTextDocument,
+ eWebDocument,
+ eSpreadsheetDocument,
+ eDrawingDocument,
+ ePresentationDocument,
+ eEnhancedForm,
+ eDatabaseForm,
+ eDatabaseReport,
+
+ eUnknownDocumentType
+ };
+
+ class DocumentClassification
+ {
+ public:
+ /** classifies a document model
+ */
+ static DocumentType classifyDocument(
+ const css::uno::Reference< css::frame::XModel >& _rxDocumentModel
+ );
+
+ static DocumentType classifyHostDocument(
+ const css::uno::Reference< css::uno::XInterface >& _rxFormComponent
+ );
+
+ static DocumentType getDocumentTypeForModuleIdentifier(
+ std::u16string_view _rModuleIdentifier
+ );
+
+ static OUString getModuleIdentifierForDocumentType(
+ DocumentType _eType
+ );
+ };
+
+
+}
+
+
+#endif // INCLUDED_SVX_SOURCE_INC_FMDOCUMENTCLASSIFICATION_HXX
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/inc/fmexch.hxx b/svx/source/inc/fmexch.hxx
new file mode 100644
index 000000000..b8878e76d
--- /dev/null
+++ b/svx/source/inc/fmexch.hxx
@@ -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 .
+ */
+#ifndef INCLUDED_SVX_SOURCE_INC_FMEXCH_HXX
+#define INCLUDED_SVX_SOURCE_INC_FMEXCH_HXX
+
+#include <config_options.h>
+#include <sal/config.h>
+
+#include <set>
+
+#include <sot/exchange.hxx>
+#include <vcl/transfer.hxx>
+#include <com/sun/star/uno/Sequence.hxx>
+#include <com/sun/star/form/XForms.hpp>
+#include <rtl/ref.hxx>
+#include <tools/link.hxx>
+#include <vcl/weld.hxx>
+#include <svx/svxdllapi.h>
+
+namespace svxform
+{
+ typedef ::std::set<std::unique_ptr<weld::TreeIter>> ListBoxEntrySet;
+
+ //= OLocalExchange
+ class UNLESS_MERGELIBS(SVXCORE_DLLPUBLIC) OLocalExchange : public TransferDataContainer
+ {
+ private:
+ Link<OLocalExchange&,void> m_aClipboardListener;
+ bool m_bDragging : 1;
+ bool m_bClipboardOwner : 1;
+
+ public:
+ class GrantAccess
+ {
+ friend class OLocalExchangeHelper;
+ };
+
+ public:
+ OLocalExchange( );
+
+ bool isDragging() const { return m_bDragging; }
+ bool isClipboardOwner() const { return m_bClipboardOwner; }
+
+ void setDragging(bool bDragging);
+ SVXCORE_DLLPRIVATE void copyToClipboard(const weld::Widget& rWidget, const GrantAccess&);
+
+ void setClipboardListener( const Link<OLocalExchange&,void>& _rListener ) { m_aClipboardListener = _rListener; }
+
+ SVXCORE_DLLPRIVATE void clear();
+
+ static bool hasFormat( const DataFlavorExVector& _rFormats, SotClipboardFormatId _nFormatId );
+
+ protected:
+ // XClipboardOwner
+ virtual void SAL_CALL lostOwnership( const css::uno::Reference< css::datatransfer::clipboard::XClipboard >& _rxClipboard, const css::uno::Reference< css::datatransfer::XTransferable >& _rxTrans ) override;
+
+ // TransferableHelper
+ virtual void DragFinished( sal_Int8 nDropAction ) override;
+ virtual bool GetData( const css::datatransfer::DataFlavor& rFlavor, const OUString& rDestDoc ) override;
+
+ private:
+ // don't allow this base class method to be called from outside
+ using TransferDataContainer::StartDrag;
+ };
+
+
+ //= OLocalExchangeHelper
+
+ /// a helper for navigator windows (SvTreeListBox'es) which allow DnD within themself
+ class UNLESS_MERGELIBS(SVXCORE_DLLPUBLIC) OLocalExchangeHelper
+ {
+ protected:
+ rtl::Reference<OLocalExchange> m_xTransferable;
+
+ public:
+ OLocalExchangeHelper();
+ virtual ~OLocalExchangeHelper();
+
+ void prepareDrag( );
+
+ SVXCORE_DLLPRIVATE void copyToClipboard(const weld::Widget& rWidget) const;
+
+ bool isDragSource() const { return m_xTransferable.is() && m_xTransferable->isDragging(); }
+ bool isClipboardOwner() const { return m_xTransferable.is() && m_xTransferable->isClipboardOwner(); }
+ bool isDataExchangeActive( ) const { return isDragSource() || isClipboardOwner(); }
+ void clear() { if ( isDataExchangeActive() ) m_xTransferable->clear(); }
+
+ SVX_DLLPRIVATE void setClipboardListener( const Link<OLocalExchange&,void>& _rListener ) { if ( m_xTransferable.is() ) m_xTransferable->setClipboardListener( _rListener ); }
+
+ protected:
+ SVX_DLLPRIVATE virtual rtl::Reference<OLocalExchange> createExchange() const = 0;
+
+ protected:
+ SVX_DLLPRIVATE void implReset();
+ };
+
+ class OControlTransferData
+ {
+ private:
+ DataFlavorExVector m_aCurrentFormats;
+
+ protected:
+ ListBoxEntrySet m_aSelectedEntries;
+ css::uno::Sequence< css::uno::Sequence< sal_uInt32 > >
+ m_aControlPaths;
+ css::uno::Sequence< css::uno::Reference< css::uno::XInterface > >
+ m_aHiddenControlModels;
+
+ css::uno::Reference< css::form::XForms >
+ m_xFormsRoot; // the root of the forms collection where the entries we represent reside
+ // this uniquely identifies the page and the document
+
+ bool m_bFocusEntry;
+
+ protected:
+ // updates m_aCurrentFormats with all formats we currently could supply
+ void updateFormats( );
+
+ public:
+ OControlTransferData( );
+
+ // ctor to construct the data from an arbitrary Transferable (usually clipboard data)
+ OControlTransferData(
+ const css::uno::Reference< css::datatransfer::XTransferable >& _rxTransferable
+ );
+
+ inline const DataFlavorExVector& GetDataFlavorExVector() const;
+
+ void addSelectedEntry(std::unique_ptr<weld::TreeIter> xEntry);
+ void setFocusEntry(bool _bFocusEntry);
+
+ /** notifies the data transfer object that a certain entry has been removed from the owning tree
+
+ In case the removed entry is part of the transfer object's selection, the entry is removed from
+ the selection.
+
+ @param _pEntry
+ @return the number of entries remaining in the selection.
+ */
+ size_t onEntryRemoved(const weld::TreeView* pView, const weld::TreeIter* _pEntry);
+
+ void setFormsRoot(
+ const css::uno::Reference< css::form::XForms >& _rxFormsRoot
+ ) { m_xFormsRoot = _rxFormsRoot; }
+
+ void buildPathFormat(const weld::TreeView* pTreeBox, const weld::TreeIter* pRoot);
+ // assembles m_aControlPaths from m_aSelectedEntries
+ // (it is assumed that the entries are sorted in m_aSelectedEntries with respect to the neighbor relationship)
+
+
+ void buildListFromPath(const weld::TreeView* pTreeBox, const weld::TreeIter* pRoot);
+ // The reverse way: throws everything out of m_aSelectedEntries and rebuilds it using m_aControlPaths
+
+ void addHiddenControlsFormat(const css::uno::Sequence< css::uno::Reference< css::uno::XInterface > >& seqInterfaces);
+ // adds an SVX_FML_HIDDEN_CONTROLS format and remembers the passed interfaces for it
+ // (it is NOT checked whether actually only hidden controls are denominated
+ // by this - the caller must ensure that)
+
+ const ListBoxEntrySet& selected() const { return m_aSelectedEntries; }
+ const css::uno::Sequence< css::uno::Reference< css::uno::XInterface > >&
+ hiddenControls() const { return m_aHiddenControlModels; }
+
+ const css::uno::Reference< css::form::XForms >&
+ getFormsRoot() const { return m_xFormsRoot; }
+ };
+
+
+ inline const DataFlavorExVector& OControlTransferData::GetDataFlavorExVector() const
+ {
+ const_cast< OControlTransferData* >( this )->updateFormats( );
+ return m_aCurrentFormats;
+ }
+
+ class OControlExchange final : public OLocalExchange, public OControlTransferData
+ {
+ public:
+ OControlExchange( );
+
+ public:
+ static SotClipboardFormatId getFieldExchangeFormatId( );
+ static SotClipboardFormatId getControlPathFormatId( );
+ static SotClipboardFormatId getHiddenControlModelsFormatId( );
+
+ inline static bool hasFieldExchangeFormat( const DataFlavorExVector& _rFormats );
+ inline static bool hasControlPathFormat( const DataFlavorExVector& _rFormats );
+ inline static bool hasHiddenControlModelsFormat( const DataFlavorExVector& _rFormats );
+
+ private:
+ virtual bool GetData( const css::datatransfer::DataFlavor& rFlavor, const OUString& rDestDoc ) override;
+ virtual void AddSupportedFormats() override;
+ };
+
+ class OControlExchangeHelper final : public OLocalExchangeHelper
+ {
+ public:
+ OControlExchangeHelper() : OLocalExchangeHelper() { }
+
+ OControlExchange* operator->() const { return static_cast< OControlExchange* >( m_xTransferable.get() ); }
+ OControlExchange& operator*() const { return *static_cast< OControlExchange* >( m_xTransferable.get() ); }
+
+ private:
+ virtual rtl::Reference<OLocalExchange> createExchange() const override;
+ };
+
+
+ inline bool OControlExchange::hasFieldExchangeFormat( const DataFlavorExVector& _rFormats )
+ {
+ return hasFormat( _rFormats, getFieldExchangeFormatId() );
+ }
+
+ inline bool OControlExchange::hasControlPathFormat( const DataFlavorExVector& _rFormats )
+ {
+ return hasFormat( _rFormats, getControlPathFormatId() );
+ }
+
+ inline bool OControlExchange::hasHiddenControlModelsFormat( const DataFlavorExVector& _rFormats )
+ {
+ return hasFormat( _rFormats, getHiddenControlModelsFormatId() );
+ }
+
+
+}
+
+
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/inc/fmexpl.hxx b/svx/source/inc/fmexpl.hxx
new file mode 100644
index 000000000..fc7823b69
--- /dev/null
+++ b/svx/source/inc/fmexpl.hxx
@@ -0,0 +1,551 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+#ifndef INCLUDED_SVX_SOURCE_INC_FMEXPL_HXX
+#define INCLUDED_SVX_SOURCE_INC_FMEXPL_HXX
+
+#include <config_options.h>
+#include <svl/lstner.hxx>
+#include <svl/SfxBroadcaster.hxx>
+#include <vcl/window.hxx>
+#include <sfx2/childwin.hxx>
+#include <svl/poolitem.hxx>
+#include <sfx2/bindings.hxx>
+#include <sfx2/dockwin.hxx>
+#include <sfx2/ctrlitem.hxx>
+
+#include <com/sun/star/form/XForm.hpp>
+#include <com/sun/star/form/XFormComponent.hpp>
+#include <com/sun/star/beans/PropertyChangeEvent.hpp>
+#include <com/sun/star/container/XContainerListener.hpp>
+#include <com/sun/star/beans/XPropertyChangeListener.hpp>
+#include <com/sun/star/container/XIndexContainer.hpp>
+
+#include <svx/fmview.hxx>
+
+#include "fmexch.hxx"
+#include <vector>
+#include <set>
+#include <cppuhelper/implbase.hxx>
+
+class SdrObjListIter;
+class FmFormShell;
+class SdrObject;
+class FmFormModel;
+class FmFormView;
+class SdrMarkList;
+
+
+class FmEntryData;
+class FmNavInsertedHint final : public SfxHint
+{
+ FmEntryData* pEntryData;
+ sal_uInt32 nPos;
+
+public:
+ FmNavInsertedHint( FmEntryData* pInsertedEntryData, sal_uInt32 nRelPos );
+ virtual ~FmNavInsertedHint() override;
+
+ FmEntryData* GetEntryData() const { return pEntryData; }
+ sal_uInt32 GetRelPos() const { return nPos; }
+};
+
+
+class FmNavModelReplacedHint final : public SfxHint
+{
+ FmEntryData* pEntryData; // the data of the entry that has got a new model
+
+public:
+ FmNavModelReplacedHint( FmEntryData* pAffectedEntryData );
+ virtual ~FmNavModelReplacedHint() override;
+
+ FmEntryData* GetEntryData() const { return pEntryData; }
+};
+
+
+class FmNavRemovedHint final : public SfxHint
+{
+ FmEntryData* pEntryData;
+
+public:
+ FmNavRemovedHint( FmEntryData* pInsertedEntryData );
+ virtual ~FmNavRemovedHint() override;
+
+ FmEntryData* GetEntryData() const { return pEntryData; }
+};
+
+
+class FmNavNameChangedHint final : public SfxHint
+{
+ FmEntryData* pEntryData;
+ OUString aNewName;
+
+public:
+ FmNavNameChangedHint( FmEntryData* pData, const OUString& rNewName );
+ virtual ~FmNavNameChangedHint() override;
+
+ FmEntryData* GetEntryData() const { return pEntryData; }
+ const OUString& GetNewName() const { return aNewName; }
+};
+
+
+class FmNavClearedHint final : public SfxHint
+{
+public:
+ FmNavClearedHint();
+ virtual ~FmNavClearedHint() override;
+};
+
+
+class FmNavViewMarksChanged final : public SfxHint
+{
+ FmFormView* pView;
+public:
+ FmNavViewMarksChanged(FmFormView* pWhichView) { pView = pWhichView; }
+
+ const FmFormView* GetAffectedView() const { return pView; }
+};
+
+
+class FmEntryDataList;
+class FmEntryData
+{
+private:
+ css::uno::Reference< css::uno::XInterface > m_xNormalizedIFace;
+ css::uno::Reference< css::beans::XPropertySet > m_xProperties;
+ css::uno::Reference< css::container::XChild > m_xChild;
+
+protected:
+ OUString m_aNormalImage;
+ OUString aText;
+
+ std::unique_ptr<FmEntryDataList>
+ pChildList;
+ FmEntryData* pParent;
+
+protected:
+ void newObject( const css::uno::Reference< css::uno::XInterface >& _rxIFace );
+
+public:
+
+ FmEntryData( FmEntryData* pParentData, const css::uno::Reference< css::uno::XInterface >& _rIFace );
+ FmEntryData( const FmEntryData& rEntryData );
+ virtual ~FmEntryData();
+
+ void SetText( const OUString& rText ){ aText = rText; }
+ void SetParent( FmEntryData* pParentData ){ pParent = pParentData; }
+
+ const OUString& GetNormalImage() const { return m_aNormalImage; }
+
+ const OUString& GetText() const { return aText; }
+ FmEntryData* GetParent() const { return pParent; }
+ FmEntryDataList* GetChildList() const { return pChildList.get(); }
+
+ virtual bool IsEqualWithoutChildren( FmEntryData* pEntryData );
+ virtual std::unique_ptr<FmEntryData> Clone() = 0;
+
+ // note that the interface returned is normalized, i.e. querying the given XInterface of the object
+ // for XInterface must return the interface itself.
+ const css::uno::Reference< css::uno::XInterface >& GetElement() const
+ {
+ return m_xNormalizedIFace;
+ }
+
+ const css::uno::Reference< css::beans::XPropertySet >& GetPropertySet() const
+ {
+ return m_xProperties;
+ }
+
+ const css::uno::Reference< css::container::XChild >& GetChildIFace() const
+ {
+ return m_xChild;
+ }
+};
+
+
+class FmEntryDataList final
+{
+private:
+ std::vector< std::unique_ptr<FmEntryData> > maEntryDataList;
+
+public:
+ FmEntryDataList();
+ ~FmEntryDataList();
+
+ FmEntryData* at( size_t Index )
+ { return maEntryDataList.at(Index).get(); }
+
+ size_t size() const { return maEntryDataList.size(); }
+ void removeNoDelete( FmEntryData* pItem );
+ void insert( std::unique_ptr<FmEntryData> pItem, size_t Index );
+ void clear();
+};
+
+
+// FmNavRequestSelectHint - someone tells the NavigatorTree to select certain entries
+
+typedef std::set<FmEntryData*> FmEntryDataArray;
+
+class FmNavRequestSelectHint final : public SfxHint
+{
+ FmEntryDataArray m_arredToSelect;
+ bool m_bMixedSelection;
+public:
+ FmNavRequestSelectHint()
+ : m_bMixedSelection(false)
+ {
+ }
+
+ void SetMixedSelection(bool bMixedSelection) { m_bMixedSelection = bMixedSelection; }
+ bool IsMixedSelection() const { return m_bMixedSelection; }
+ void AddItem(FmEntryData* pEntry) { m_arredToSelect.insert(pEntry); }
+ void ClearItems() { m_arredToSelect.clear(); }
+ FmEntryDataArray& GetItems() { return m_arredToSelect; }
+};
+
+
+class FmFormData final : public FmEntryData
+{
+ css::uno::Reference< css::form::XForm > m_xForm;
+
+public:
+ FmFormData(const css::uno::Reference< css::form::XForm >& _rxForm, FmFormData* _pParent);
+ FmFormData( const FmFormData& rFormData );
+ virtual ~FmFormData() override;
+
+ const css::uno::Reference< css::form::XForm >& GetFormIface() const { return m_xForm; }
+
+ virtual bool IsEqualWithoutChildren( FmEntryData* pEntryData ) override;
+ virtual std::unique_ptr<FmEntryData> Clone() override;
+};
+
+
+class FmControlData final : public FmEntryData
+{
+ css::uno::Reference< css::form::XFormComponent > m_xFormComponent;
+
+ OUString GetImage() const;
+
+public:
+
+ FmControlData(
+ const css::uno::Reference< css::form::XFormComponent >& _rxComponent,
+ FmFormData* _pParent
+ );
+ FmControlData( const FmControlData& rControlData );
+ virtual ~FmControlData() override;
+
+ const css::uno::Reference< css::form::XFormComponent >& GetFormComponent() const { return m_xFormComponent; }
+ virtual bool IsEqualWithoutChildren( FmEntryData* pEntryData ) override;
+ virtual std::unique_ptr<FmEntryData> Clone() override;
+
+ void ModelReplaced(const css::uno::Reference< css::form::XFormComponent >& _rxNew);
+};
+
+
+namespace svxform
+{
+
+
+ class NavigatorTreeModel;
+
+ class OFormComponentObserver final
+ :public ::cppu::WeakImplHelper < css::beans::XPropertyChangeListener
+ , css::container::XContainerListener
+ >
+ {
+ ::svxform::NavigatorTreeModel* m_pNavModel;
+ sal_uInt32 m_nLocks;
+ bool m_bCanUndo;
+
+ public:
+ OFormComponentObserver( ::svxform::NavigatorTreeModel* pModel );
+
+ // XEventListenerListener
+ virtual void SAL_CALL disposing(const css::lang::EventObject& Source) override;
+
+ // css::beans::XPropertyChangeListener
+ virtual void SAL_CALL propertyChange(const css::beans::PropertyChangeEvent& evt) override;
+
+ // css::container::XContainerListener
+
+ virtual void SAL_CALL elementInserted(const css::container::ContainerEvent& rEvent) override;
+ virtual void SAL_CALL elementReplaced(const css::container::ContainerEvent& rEvent) override;
+ virtual void SAL_CALL elementRemoved(const css::container::ContainerEvent& rEvent) override;
+
+ void Lock() { m_nLocks++; }
+ void UnLock() { m_nLocks--; }
+ bool IsLocked() const { return m_nLocks != 0; }
+ bool CanUndo() const { return m_bCanUndo; }
+ void ReleaseModel() { m_pNavModel = nullptr; }
+ private:
+ void Insert(const css::uno::Reference< css::uno::XInterface >& xIface, sal_Int32 nIndex);
+ void Remove( const css::uno::Reference< css::uno::XInterface >& _rxElement );
+ };
+
+ class NavigatorTreeModel final : public SfxBroadcaster
+ ,public SfxListener
+ {
+ friend class NavigatorTree;
+ friend class OFormComponentObserver;
+
+ std::unique_ptr<FmEntryDataList>
+ m_pRootList;
+ FmFormShell* m_pFormShell;
+ FmFormPage* m_pFormPage;
+ FmFormModel* m_pFormModel;
+ rtl::Reference<OFormComponentObserver> m_pPropChangeList;
+
+ void UpdateContent( const css::uno::Reference< css::form::XForms >& xForms );
+
+ void InsertForm(const css::uno::Reference< css::form::XForm >& xForm, sal_uInt32 nRelPos);
+ void RemoveForm(FmFormData const * pFormData);
+
+ void InsertFormComponent(const css::uno::Reference< css::form::XFormComponent >& xComp, sal_uInt32 nRelPos);
+ void RemoveFormComponent(FmControlData const * pControlData);
+ void InsertSdrObj(const SdrObject* pSdrObj);
+ void RemoveSdrObj(const SdrObject* pSdrObj);
+
+ void ReplaceFormComponent(const css::uno::Reference< css::form::XFormComponent >& xOld, const css::uno::Reference< css::form::XFormComponent >& xNew);
+
+ void BroadcastMarkedObjects(const SdrMarkList& mlMarked);
+ // send a RequestSelectHint with the currently selected objects
+ bool InsertFormComponent(FmNavRequestSelectHint& rHint, SdrObject* pObject);
+ // is a helper for previous, manages the ... in SdrObjGroups;
+ // returns sal_True if the object is a FormComponent (or recursively consists only of such)
+
+ public:
+ NavigatorTreeModel();
+ virtual ~NavigatorTreeModel() override;
+
+ void FillBranch( FmFormData* pParentData );
+ void UpdateContent( FmFormShell* pNewShell );
+
+ void Insert(FmEntryData* pEntryData, sal_uInt32 nRelPos = SAL_MAX_UINT32,
+ bool bAlterModel = false);
+ void Remove(FmEntryData* pEntryData, bool bAlterModel = false);
+
+ static bool Rename( FmEntryData* pEntryData, const OUString& rNewText );
+
+ void Clear();
+ void SetModified();
+
+ css::uno::Reference< css::form::XForms > GetForms() const;
+ FmFormShell* GetFormShell() const { return m_pFormShell; }
+ FmFormPage* GetFormPage() const { return m_pFormPage; }
+ FmEntryData* FindData( const css::uno::Reference< css::uno::XInterface >& xElement, FmEntryDataList* pDataList, bool bRecurs=true );
+ FmEntryData* FindData( const OUString& rText, FmFormData const * pParentData, bool bRecurs );
+ FmEntryDataList* GetRootList() const { return m_pRootList.get(); }
+ static css::uno::Reference< css::container::XIndexContainer > GetFormComponents( FmFormData const * pParentFormData );
+
+ virtual void Notify( SfxBroadcaster& rBC, const SfxHint& rHint ) override;
+ };
+
+ class NavigatorTree;
+
+ class NavigatorTreeDropTarget final : public DropTargetHelper
+ {
+ private:
+ NavigatorTree& m_rTreeView;
+
+ virtual sal_Int8 AcceptDrop( const AcceptDropEvent& rEvt ) override;
+ virtual sal_Int8 ExecuteDrop( const ExecuteDropEvent& rEvt ) override;
+
+ public:
+ NavigatorTreeDropTarget(NavigatorTree& rTreeView);
+ };
+
+ typedef std::set<std::unique_ptr<weld::TreeIter>> SvLBoxEntrySortedArray;
+
+ class NavigatorTree final : public SfxListener
+ {
+ std::unique_ptr<weld::TreeView> m_xTreeView;
+ NavigatorTreeDropTarget m_aDropTargetHelper;
+
+ enum SELDATA_ITEMS { SDI_DIRTY, SDI_ALL, SDI_NORMALIZED, SDI_NORMALIZED_FORMARK };
+
+ Timer m_aSynchronizeTimer;
+ // the meta-data about my current selection
+ SvLBoxEntrySortedArray m_arrCurrentSelection;
+ // the entries which, in the view, are currently marked as "cut" (painted semi-transparent)
+ ListBoxEntrySet m_aCutEntries;
+
+ ::svxform::OControlExchangeHelper m_aControlExchange;
+
+ std::unique_ptr<NavigatorTreeModel> m_pNavModel;
+ std::unique_ptr<weld::TreeIter> m_xRootEntry;
+ std::unique_ptr<weld::TreeIter> m_xEditEntry;
+
+ ImplSVEvent * nEditEvent;
+
+ SELDATA_ITEMS m_sdiState;
+
+ sal_uInt16 m_nSelectLock;
+ sal_uInt16 m_nFormsSelected;
+ sal_uInt16 m_nControlsSelected;
+ sal_uInt16 m_nHiddenControls; // (the number is included in m_nControlsSelected)
+
+ bool m_bDragDataDirty : 1; // ditto
+ bool m_bPrevSelectionMixed : 1;
+ bool m_bRootSelected : 1;
+ bool m_bInitialUpdate : 1; // am I the first time in the UpdateContent?
+ bool m_bKeyboardCut : 1;
+ bool m_bEditing : 1;
+
+ FmControlData* NewControl(const OUString& rServiceName, const weld::TreeIter& rParentEntry, bool bEditName);
+ void NewForm(const weld::TreeIter& rParentEntry);
+ std::unique_ptr<weld::TreeIter> Insert(const FmEntryData* pEntryData, int nRelPos);
+ void Remove( FmEntryData* pEntryData );
+
+
+ void CollectSelectionData(SELDATA_ITEMS sdiHow);
+ // Collects the currently selected entries in m_arrCurrentSelection, normalizes the list if requested.
+ // - SDI_NORMALIZED simply means that all entries that already have a selected ancestor are not collected.
+ // - SDI_NORMALIZED_FORMARK means that the procedure is the same as for SDI_NORMALIZED,
+ // but entries whose direct parent is not selected are collected (independent of the
+ // status of further ancestors). The same applies for forms that are selected,
+ // regardless of the status of any ancestors.
+ // For both normalized modes, the m_nFormsSelected, ... contain the correct number,
+ // even if not all of these entries end up in m_arrCurrentSelection.
+ // SDI_DIRTY is of course not allowed as a parameter.
+
+ // a single interface for all selected entries
+ void ShowSelectionProperties(bool bForce = false);
+ // delete all selected elements
+ void DeleteSelection();
+
+ void SynchronizeSelection(FmEntryDataArray& arredToSelect);
+ // after calling this method, exactly the entries marked in the array are selected
+ void SynchronizeSelection();
+ // makes the same, takes the MarkList of the View
+ void SynchronizeMarkList();
+ // reverse direction of SynchronizeMarkList: selects in the view all controls corresponding to the current selection
+
+ void CollectObjects(FmFormData const * pFormData, bool bDeep, ::std::set< css::uno::Reference< css::form::XFormComponent > >& _rObjects);
+
+ // in the Select I usually update the Marklist of the corresponding view,
+ // with the following functions I can control the locking of this behavior
+ void LockSelectionHandling() { ++m_nSelectLock; }
+ void UnlockSelectionHandling() { --m_nSelectLock; }
+ bool IsSelectionHandlingLocked() const { return m_nSelectLock>0; }
+
+ bool IsEditingActive() const { return m_bEditing; }
+
+ static bool IsHiddenControl(FmEntryData const * pEntryData);
+
+ DECL_LINK( KeyInputHdl, const KeyEvent&, bool );
+ DECL_LINK( PopupMenuHdl, const CommandEvent&, bool );
+
+ DECL_LINK(EditingEntryHdl, const weld::TreeIter&, bool);
+ typedef std::pair<const weld::TreeIter&, OUString> IterString;
+ DECL_LINK(EditedEntryHdl, const IterString&, bool);
+
+ DECL_LINK( OnEdit, void*, void );
+
+ DECL_LINK( OnEntrySelDesel, weld::TreeView&, void );
+ DECL_LINK( OnSynchronizeTimer, Timer*, void );
+
+ DECL_LINK( OnClipboardAction, OLocalExchange&, void );
+
+ DECL_LINK( DragBeginHdl, bool&, bool );
+
+ public:
+ NavigatorTree(std::unique_ptr<weld::TreeView> xTreeView);
+ virtual ~NavigatorTree() override;
+
+ void Clear();
+ void UpdateContent( FmFormShell* pFormShell );
+ void MarkViewObj( FmFormData const * pFormData, bool bDeep );
+ void MarkViewObj( FmControlData const * pControlData );
+ void UnmarkAllViewObj();
+
+ void GrabFocus() { m_xTreeView->grab_focus(); }
+
+ bool IsFormEntry(const weld::TreeIter& rEntry);
+ bool IsFormComponentEntry(const weld::TreeIter& rEntry);
+
+ OUString GenerateName( FmEntryData const * pEntryData );
+
+ NavigatorTreeModel* GetNavModel() const { return m_pNavModel.get(); }
+ std::unique_ptr<weld::TreeIter> FindEntry(FmEntryData* pEntryData);
+
+ virtual void Notify( SfxBroadcaster& rBC, const SfxHint& rHint ) override;
+
+ weld::TreeView& get_widget() { return *m_xTreeView; }
+
+ sal_Int8 AcceptDrop(const AcceptDropEvent& rEvt);
+ sal_Int8 ExecuteDrop(const ExecuteDropEvent& rEvt);
+
+ private:
+ sal_Int8 implAcceptDataTransfer( const DataFlavorExVector& _rFlavors, sal_Int8 _nAction, const weld::TreeIter* _pTargetEntry, bool _bDnD );
+
+ sal_Int8 implExecuteDataTransfer( const OControlTransferData& _rData, sal_Int8 _nAction, const Point& _rDropPos, bool _bDnD );
+ sal_Int8 implExecuteDataTransfer( const OControlTransferData& _rData, sal_Int8 _nAction, const weld::TreeIter* _pTargetEntry, bool _bDnD );
+
+ // check if a cut, copy, or drag operation can be started in the current situation
+ bool implAllowExchange( sal_Int8 _nAction, bool* _pHasNonHidden = nullptr );
+ // check if a paste with the current clipboard content can be accepted
+ bool implAcceptPaste( );
+
+ // fills m_aControlExchange in preparation of a DnD or clipboard operation
+ bool implPrepareExchange( sal_Int8 _nAction );
+
+ void ModelHasRemoved(const weld::TreeIter* _pEntry);
+
+ void doPaste();
+ void doCopy();
+ void doCut();
+
+ bool doingKeyboardCut( ) const { return m_bKeyboardCut; }
+ };
+
+ class NavigatorFrame final : public SfxDockingWindow, public SfxControllerItem
+ {
+ private:
+ std::unique_ptr<NavigatorTree> m_xNavigatorTree;
+
+ virtual bool Close() override;
+ virtual void GetFocus() override;
+ virtual Size CalcDockingSize( SfxChildAlignment ) override;
+ virtual SfxChildAlignment CheckAlignment( SfxChildAlignment, SfxChildAlignment ) override;
+
+ using SfxDockingWindow::StateChanged;
+
+ public:
+ NavigatorFrame( SfxBindings *pBindings, SfxChildWindow *pMgr,
+ vcl::Window* pParent );
+ virtual ~NavigatorFrame() override;
+ virtual void dispose() override;
+
+ void UpdateContent( FmFormShell* pFormShell );
+ void StateChangedAtToolBoxControl( sal_uInt16 nSID, SfxItemState eState, const SfxPoolItem* pState ) override;
+ void FillInfo( SfxChildWinInfo& rInfo ) const override;
+ };
+
+ class UNLESS_MERGELIBS(SVXCORE_DLLPUBLIC) NavigatorFrameManager final : public SfxChildWindow
+ {
+ public:
+ SVX_DLLPRIVATE NavigatorFrameManager( vcl::Window *pParent, sal_uInt16 nId, SfxBindings *pBindings,
+ SfxChildWinInfo *pInfo );
+ SFX_DECL_CHILDWINDOW( NavigatorFrameManager );
+ };
+}
+
+#endif // INCLUDED_SVX_SOURCE_INC_FMEXPL_HXX
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/inc/fmobj.hxx b/svx/source/inc/fmobj.hxx
new file mode 100644
index 000000000..d604cfd20
--- /dev/null
+++ b/svx/source/inc/fmobj.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_SVX_SOURCE_INC_FMOBJ_HXX
+#define INCLUDED_SVX_SOURCE_INC_FMOBJ_HXX
+
+#include <config_options.h>
+#include <svx/svdouno.hxx>
+#include <com/sun/star/container/XIndexContainer.hpp>
+#include <com/sun/star/form/XForms.hpp>
+#include <com/sun/star/script/ScriptEventDescriptor.hpp>
+
+
+// FmFormObj
+
+class UNLESS_MERGELIBS(SVXCORE_DLLPUBLIC) FmFormObj final : public SdrUnoObj
+{
+ FmFormObj( const FmFormObj& ) = delete;
+
+ css::uno::Sequence< css::script::ScriptEventDescriptor > aEvts; // events of the object
+ css::uno::Sequence< css::script::ScriptEventDescriptor> m_aEventsHistory;
+ // valid if and only if m_pEnvironmentHistory != NULL, this are the events which we're set when
+ // m_pEnvironmentHistory was created
+
+ // information for the control environment is only maintained if an object is not in an
+ // object list
+ css::uno::Reference< css::container::XIndexContainer> m_xParent;
+ css::uno::Reference< css::form::XForms > m_xEnvironmentHistory;
+ sal_Int32 m_nPos;
+
+ VclPtr<OutputDevice> m_pLastKnownRefDevice;
+ // the last ref device we know, as set at the model
+ // only to be used for comparison with the current ref device!
+
+ // protected destructor
+ SAL_DLLPRIVATE virtual ~FmFormObj() override;
+
+public:
+ FmFormObj(
+ SdrModel& rSdrModel,
+ const OUString& rModelName);
+ FmFormObj(SdrModel& rSdrModel);
+ // Copy constructor
+ FmFormObj(SdrModel& rSdrModel, FmFormObj const & rSource);
+
+ SAL_DLLPRIVATE const css::uno::Reference< css::container::XIndexContainer>&
+ GetOriginalParent() const { return m_xParent; }
+ SAL_DLLPRIVATE const css::uno::Sequence< css::script::ScriptEventDescriptor >&
+ GetOriginalEvents() const { return aEvts; }
+ SAL_DLLPRIVATE sal_Int32
+ GetOriginalIndex() const { return m_nPos; }
+
+ SAL_DLLPRIVATE void SetObjEnv(
+ const css::uno::Reference< css::container::XIndexContainer>& xForm,
+ const sal_Int32 nIdx,
+ const css::uno::Sequence< css::script::ScriptEventDescriptor >& rEvts );
+ SAL_DLLPRIVATE void ClearObjEnv();
+
+public:
+ // react on page change
+ virtual void handlePageChange(SdrPage* pOldPage, SdrPage* pNewPage) override;
+
+ SAL_DLLPRIVATE virtual SdrInventor GetObjInventor() const override;
+ SAL_DLLPRIVATE virtual SdrObjKind GetObjIdentifier() const override;
+ SAL_DLLPRIVATE virtual void NbcReformatText() override;
+
+ SAL_DLLPRIVATE virtual FmFormObj* CloneSdrObject(SdrModel& rTargetModel) const override;
+
+ SAL_DLLPRIVATE static css::uno::Reference< css::uno::XInterface> ensureModelEnv(
+ const css::uno::Reference< css::uno::XInterface>& _rSourceContainer,
+ const css::uno::Reference< css::form::XForms>& _rTopLevelDestContainer);
+
+ /** returns the FmFormObj behind the given SdrObject
+
+ In case the SdrObject *is* a FmFormObject, this is a simple cast. In case the SdrObject
+ is a virtual object whose referenced object is a FmFormObj, then this referenced
+ object is returned. In all other cases, NULL is returned.
+ */
+ SAL_DLLPRIVATE static FmFormObj* GetFormObject( SdrObject* _pSdrObject );
+ SAL_DLLPRIVATE static const FmFormObj* GetFormObject( const SdrObject* _pSdrObject );
+
+ SAL_DLLPRIVATE virtual void SetUnoControlModel( const css::uno::Reference< css::awt::XControlModel >& _rxModel ) override;
+
+private:
+ SAL_DLLPRIVATE virtual bool EndCreate( SdrDragStat& rStat, SdrCreateCmd eCmd ) override;
+ SAL_DLLPRIVATE virtual void BrkCreate( SdrDragStat& rStat ) override;
+
+ /** isolates the control model from its form component hierarchy, i.e. removes it from
+ its parent.
+ */
+ SAL_DLLPRIVATE void impl_isolateControlModel_nothrow();
+
+ /** forwards the reference device of our SdrModel to the control model
+ */
+ SAL_DLLPRIVATE void impl_checkRefDevice_nothrow( bool _force = false );
+};
+
+
+#endif // _FM_FMOBJ_HXX
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/inc/fmpgeimp.hxx b/svx/source/inc/fmpgeimp.hxx
new file mode 100644
index 000000000..1e2b63207
--- /dev/null
+++ b/svx/source/inc/fmpgeimp.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_SVX_SOURCE_INC_FMPGEIMP_HXX
+#define INCLUDED_SVX_SOURCE_INC_FMPGEIMP_HXX
+
+#include <config_options.h>
+#include <com/sun/star/sdbc/XDataSource.hpp>
+#include <com/sun/star/form/XFormComponent.hpp>
+#include <com/sun/star/form/XForm.hpp>
+#include <com/sun/star/form/XForms.hpp>
+#include <com/sun/star/container/XMap.hpp>
+
+#include <tools/link.hxx>
+#include <cppuhelper/weakref.hxx>
+
+#include <svx/svxdllapi.h>
+
+class FmFormObj;
+class FmFormPage;
+class SdrObject;
+
+
+// FmFormPageImpl
+// Listens to all containers to determine when objects have been inserted
+// and when they have been removed
+
+
+class FmFormPageImpl final
+{
+ css::uno::Reference< css::form::XForm > xCurrentForm;
+ css::uno::Reference< css::form::XForms > m_xForms;
+ css::uno::WeakReference< css::container::XMap > m_aControlShapeMap;
+
+ FmFormPage& m_rPage;
+ Link<FmFormPageImpl&,void> m_aFormsCreationHdl;
+
+ bool m_bFirstActivation;
+ bool m_bAttemptedFormCreation;
+
+public:
+ explicit FmFormPageImpl( FmFormPage& _rPage );
+ ~FmFormPageImpl();
+
+ void initFrom( FmFormPageImpl& i_foreignImpl );
+
+ // only important for the DesignMode
+ void setCurForm(const css::uno::Reference< css::form::XForm>& xForm);
+ css::uno::Reference< css::form::XForm> getDefaultForm();
+
+ /** finds a place in the form component hierarchy where to insert the given component
+
+ Note that no actual insertion happens, this is the responsibility of the caller (as
+ the caller might decide on a suitable place where in the returned container the insertion
+ should happen).
+ */
+ css::uno::Reference< css::form::XForm> findPlaceInFormComponentHierarchy(
+ const css::uno::Reference< css::form::XFormComponent>& rContent,
+ const css::uno::Reference< css::sdbc::XDataSource>& rDatabase = css::uno::Reference< css::sdbc::XDataSource>(),
+ const OUString& rDBTitle = OUString(),
+ const OUString& rCursorSource = OUString(),
+ sal_Int32 nCommandType = 0
+ );
+
+ // activation handling
+ bool hasEverBeenActivated( ) const { return !m_bFirstActivation; }
+ void setHasBeenActivated( ) { m_bFirstActivation = false; }
+
+ const css::uno::Reference< css::form::XForms>& getForms( bool _bForceCreate = true );
+
+ void SetFormsCreationHdl( const Link<FmFormPageImpl&,void>& _rFormsCreationHdl ) { m_aFormsCreationHdl = _rFormsCreationHdl; }
+
+private:
+ /** finds a form with a given data source signature
+ @param rForm
+ the form to start the search with. This form, including all possible sub forms,
+ will be examined
+ @param rDatabase
+ the data source which to which the found form must be bound
+ @param rCommand
+ the desired Command property value of the sought-after form
+ @param nCommandType
+ the desired CommandType property value of the sought-after form
+ */
+ css::uno::Reference< css::form::XForm> findFormForDataSource(
+ const css::uno::Reference< css::form::XForm>& rForm,
+ const css::uno::Reference< css::sdbc::XDataSource>& rDatabase,
+ const OUString& rCommand,
+ sal_Int32 nCommandType
+ );
+
+public:
+ static OUString setUniqueName(const css::uno::Reference< css::form::XFormComponent>& xFormComponent, const css::uno::Reference< css::form::XForm>& xControls);
+
+ void formObjectInserted( const FmFormObj& _object );
+ void formObjectRemoved( const FmFormObj& _object );
+ void formModelAssigned( const FmFormObj& _object );
+
+ /** returns an object mapping from control models to drawing shapes.
+ */
+ UNLESS_MERGELIBS(SVXCORE_DLLPUBLIC) css::uno::Reference< css::container::XMap > getControlToShapeMap();
+
+private:
+ /** validates whether <member>xCurrentForm</member> is still valid and to be used
+
+ There are situations where our current form becomes invalid, without us noticing this. Thus,
+ every method which accesses <member>xCurrentForm</member> should beforehand validate the current
+ form by calling this method.
+
+ If <member>xCurrentForm</member> is not valid anymore, it is reset to <NULL/>.
+
+ @return
+ <TRUE/> if and only if xCurrentForm is valid.
+
+ @since #i40086#
+ */
+ bool validateCurForm();
+
+ css::uno::Reference< css::container::XMap >
+ impl_createControlShapeMap_nothrow();
+
+ FmFormPageImpl( const FmFormPageImpl& ) = delete;
+ FmFormPageImpl& operator=( const FmFormPageImpl& ) = delete;
+};
+
+
+#endif // INCLUDED_SVX_SOURCE_INC_FMPGEIMP_HXX
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/inc/fmprop.hxx b/svx/source/inc/fmprop.hxx
new file mode 100644
index 000000000..a6125f2ff
--- /dev/null
+++ b/svx/source/inc/fmprop.hxx
@@ -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 .
+ */
+#ifndef INCLUDED_SVX_SOURCE_INC_FMPROP_HXX
+#define INCLUDED_SVX_SOURCE_INC_FMPROP_HXX
+
+#include <rtl/ustring.hxx>
+
+// PropertyIds, which have an assignment to a PropertyName, could continue
+// to be WhichIds in the future -> Itemset
+#define FM_ATTR_START 0
+
+#define FM_ATTR_FILTER ( FM_ATTR_START + 1 )
+#define FM_ATTR_FORM_OPERATIONS ( FM_ATTR_START + 2 )
+
+inline constexpr OUStringLiteral FM_PROP_NAME = u"Name";
+inline constexpr OUStringLiteral FM_PROP_CLASSID = u"ClassId";
+inline constexpr OUStringLiteral FM_PROP_ALIGN = u"Align";
+inline constexpr OUStringLiteral FM_PROP_ROWCOUNT = u"RowCount";
+inline constexpr OUStringLiteral FM_PROP_ROWCOUNTFINAL = u"IsRowCountFinal";
+inline constexpr OUStringLiteral FM_PROP_FETCHSIZE = u"FetchSize";
+inline constexpr OUStringLiteral FM_PROP_VALUE = u"Value";
+inline constexpr OUStringLiteral FM_PROP_VALUEMIN = u"ValueMin";
+inline constexpr OUStringLiteral FM_PROP_VALUEMAX = u"ValueMax";
+inline constexpr OUStringLiteral FM_PROP_VALUESTEP = u"ValueStep";
+inline constexpr OUStringLiteral FM_PROP_TEXT = u"Text";
+inline constexpr OUStringLiteral FM_PROP_LABEL = u"Label";
+inline constexpr OUStringLiteral FM_PROP_NAVIGATION = u"NavigationBarMode";
+inline constexpr OUStringLiteral FM_PROP_CYCLE = u"Cycle";
+inline constexpr OUStringLiteral FM_PROP_CONTROLSOURCE = u"DataField";
+inline constexpr OUStringLiteral FM_PROP_ENABLED = u"Enabled";
+inline constexpr OUStringLiteral FM_PROP_SPIN = u"Spin";
+inline constexpr OUStringLiteral FM_PROP_READONLY = u"ReadOnly";
+inline constexpr OUStringLiteral FM_PROP_FILTER = u"Filter";
+inline constexpr OUStringLiteral FM_PROP_AUTOINCREMENT = u"IsAutoIncrement";
+inline constexpr OUStringLiteral FM_PROP_WIDTH = u"Width";
+inline constexpr OUStringLiteral FM_PROP_SEARCHABLE = u"IsSearchable";
+inline constexpr OUStringLiteral FM_PROP_MULTILINE = u"MultiLine";
+inline constexpr OUStringLiteral FM_PROP_TARGET_URL = u"TargetURL";
+inline constexpr OUStringLiteral FM_PROP_DEFAULTCONTROL = u"DefaultControl";
+inline constexpr OUStringLiteral FM_PROP_MAXTEXTLEN = u"MaxTextLen";
+inline constexpr OUStringLiteral FM_PROP_DATE = u"Date";
+inline constexpr OUStringLiteral FM_PROP_TIME = u"Time";
+inline constexpr OUStringLiteral FM_PROP_STATE = u"State";
+inline constexpr OUStringLiteral FM_PROP_TRISTATE = u"TriState";
+inline constexpr OUStringLiteral FM_PROP_STRINGITEMLIST = u"StringItemList";
+inline constexpr OUStringLiteral FM_PROP_DEFAULT_TEXT = u"DefaultText";
+inline constexpr OUStringLiteral FM_PROP_DEFAULTCHECKED = u"DefaultState";
+inline constexpr OUStringLiteral FM_PROP_DEFAULT_DATE = u"DefaultDate";
+inline constexpr OUStringLiteral FM_PROP_DEFAULT_TIME = u"DefaultTime";
+inline constexpr OUStringLiteral FM_PROP_DEFAULT_VALUE = u"DefaultValue";
+inline constexpr OUStringLiteral FM_PROP_FORMATKEY = u"FormatKey";
+inline constexpr OUStringLiteral FM_PROP_FORMATSSUPPLIER = u"FormatsSupplier";
+inline constexpr OUStringLiteral FM_PROP_LISTSOURCETYPE = u"ListSourceType";
+inline constexpr OUStringLiteral FM_PROP_LISTSOURCE = u"ListSource";
+inline constexpr OUStringLiteral FM_PROP_SELECT_SEQ = u"SelectedItems";
+inline constexpr OUStringLiteral FM_PROP_VALUE_SEQ = u"ValueItemList";
+inline constexpr OUStringLiteral FM_PROP_DEFAULT_SELECT_SEQ = u"DefaultSelection";
+inline constexpr OUStringLiteral FM_PROP_DECIMAL_ACCURACY = u"DecimalAccuracy";
+inline constexpr OUStringLiteral FM_PROP_EDITMASK = u"EditMask";
+inline constexpr OUStringLiteral FM_PROP_ISREADONLY = u"IsReadOnly";
+inline constexpr OUStringLiteral FM_PROP_FIELDTYPE = u"Type";
+inline constexpr OUStringLiteral FM_PROP_REFVALUE = u"RefValue";
+inline constexpr OUStringLiteral FM_PROP_STRICTFORMAT = u"StrictFormat";
+inline constexpr OUStringLiteral FM_PROP_DATASOURCE = u"DataSourceName";
+inline constexpr OUStringLiteral FM_PROP_LITERALMASK = u"LiteralMask";
+inline constexpr OUStringLiteral FM_PROP_SHOWTHOUSANDSEP = u"ShowThousandsSeparator";
+inline constexpr OUStringLiteral FM_PROP_CURRENCYSYMBOL = u"CurrencySymbol";
+inline constexpr OUStringLiteral FM_PROP_DATEFORMAT = u"DateFormat";
+inline constexpr OUStringLiteral FM_PROP_DATEMIN = u"DateMin";
+inline constexpr OUStringLiteral FM_PROP_DATEMAX = u"DateMax";
+inline constexpr OUStringLiteral FM_PROP_DATE_SHOW_CENTURY = u"DateShowCentury";
+inline constexpr OUStringLiteral FM_PROP_TIMEFORMAT = u"TimeFormat";
+inline constexpr OUStringLiteral FM_PROP_TIMEMIN = u"TimeMin";
+inline constexpr OUStringLiteral FM_PROP_TIMEMAX = u"TimeMax";
+inline constexpr OUStringLiteral FM_PROP_LINECOUNT = u"LineCount";
+inline constexpr OUStringLiteral FM_PROP_BOUNDCOLUMN = u"BoundColumn";
+#define FM_PROP_HASNAVIGATION "HasNavigationBar"
+inline constexpr OUStringLiteral FM_PROP_FONT = u"FontDescriptor";
+#define FM_PROP_BACKGROUNDCOLOR "BackgroundColor"
+#define FM_PROP_TEXTCOLOR "TextColor"
+inline constexpr OUStringLiteral FM_PROP_BORDER = u"Border";
+inline constexpr OUStringLiteral FM_PROP_DROPDOWN = u"Dropdown";
+inline constexpr OUStringLiteral FM_PROP_ROWHEIGHT = u"RowHeight";
+inline constexpr OUStringLiteral FM_PROP_HELPTEXT = u"HelpText";
+#define FM_PROP_HELPURL "HelpURL"
+#define FM_PROP_RECORDMARKER "HasRecordMarker"
+inline constexpr OUStringLiteral FM_PROP_BOUNDFIELD = u"BoundField";
+inline constexpr OUStringLiteral FM_PROP_EFFECTIVE_VALUE = u"EffectiveValue";
+inline constexpr OUStringLiteral FM_PROP_EFFECTIVE_DEFAULT = u"EffectiveDefault";
+inline constexpr OUStringLiteral FM_PROP_EFFECTIVE_MIN = u"EffectiveMin";
+inline constexpr OUStringLiteral FM_PROP_EFFECTIVE_MAX = u"EffectiveMax";
+inline constexpr OUStringLiteral FM_PROP_HIDDEN = u"Hidden";
+inline constexpr OUStringLiteral FM_PROP_FILTERPROPOSAL = u"UseFilterValueProposal";
+inline constexpr OUStringLiteral FM_PROP_FIELDSOURCE = u"FieldSource";
+inline constexpr OUStringLiteral FM_PROP_TABLENAME = u"TableName";
+inline constexpr OUStringLiteral FM_PROP_CONTROLLABEL = u"LabelControl";
+inline constexpr OUStringLiteral FM_PROP_CURSORCOLOR = u"CursorColor";
+inline constexpr OUStringLiteral FM_PROP_ALWAYSSHOWCURSOR = u"AlwaysShowCursor";
+inline constexpr OUStringLiteral FM_PROP_DISPLAYSYNCHRON = u"DisplayIsSynchron";
+inline constexpr OUStringLiteral FM_PROP_ISMODIFIED = u"IsModified";
+inline constexpr OUStringLiteral FM_PROP_ISNEW = u"IsNew";
+inline constexpr OUStringLiteral FM_PROP_PRIVILEGES = u"Privileges";
+inline constexpr OUStringLiteral FM_PROP_COMMAND = u"Command";
+inline constexpr OUStringLiteral FM_PROP_COMMANDTYPE = u"CommandType";
+inline constexpr OUStringLiteral FM_PROP_RESULTSET_CONCURRENCY = u"ResultSetConcurrency";
+inline constexpr OUStringLiteral FM_PROP_INSERTONLY = u"IgnoreResult";
+inline constexpr OUStringLiteral FM_PROP_RESULTSET_TYPE = u"ResultSetType";
+inline constexpr OUStringLiteral FM_PROP_ESCAPE_PROCESSING = u"EscapeProcessing";
+inline constexpr OUStringLiteral FM_PROP_APPLYFILTER = u"ApplyFilter";
+inline constexpr OUStringLiteral FM_PROP_ISNULLABLE = u"IsNullable";
+inline constexpr OUStringLiteral FM_PROP_ACTIVECOMMAND = u"ActiveCommand";
+inline constexpr OUStringLiteral FM_PROP_ISCURRENCY = u"IsCurrency";
+inline constexpr OUStringLiteral FM_PROP_URL = u"URL";
+inline constexpr OUStringLiteral FM_PROP_ACTIVE_CONNECTION = u"ActiveConnection";
+inline constexpr OUStringLiteral FM_PROP_CONTROLSOURCEPROPERTY = u"DataFieldProperty";
+inline constexpr OUStringLiteral FM_PROP_REALNAME = u"RealName";
+inline constexpr OUStringLiteral FM_PROP_TEXTLINECOLOR = u"TextLineColor";
+#define FM_PROP_FONTEMPHASISMARK "FontEmphasisMark"
+#define FM_PROP_FONTRELIEF "FontRelief"
+inline constexpr OUStringLiteral FM_PROP_ORIENTATION = u"Orientation";
+inline constexpr OUStringLiteral FM_PROP_LINEENDFORMAT = u"LineEndFormat";
+inline constexpr OUStringLiteral FM_PROP_VISUALEFFECT = u"VisualEffect";
+inline constexpr OUStringLiteral FM_PROP_BORDERCOLOR = u"BorderColor";
+inline constexpr OUStringLiteral FM_PROP_DYNAMIC_CONTROL_BORDER = u"DynamicControlBorder";
+inline constexpr OUStringLiteral FM_PROP_CONTROL_BORDER_COLOR_FOCUS = u"ControlBorderColorOnFocus";
+inline constexpr OUStringLiteral FM_PROP_CONTROL_BORDER_COLOR_MOUSE = u"ControlBorderColorOnHover";
+inline constexpr OUStringLiteral FM_PROP_CONTROL_BORDER_COLOR_INVALID = u"ControlBorderColorOnInvalid";
+inline constexpr OUStringLiteral FM_PROP_BUTTON_TYPE = u"ButtonType";
+inline constexpr OUStringLiteral FM_PROP_FORM_OPERATIONS = u"FormOperations";
+inline constexpr OUStringLiteral FM_PROP_INPUT_REQUIRED = u"InputRequired";
+inline constexpr OUStringLiteral FM_PROP_WRITING_MODE = u"WritingMode";
+inline constexpr OUStringLiteral FM_PROP_MOUSE_WHEEL_BEHAVIOR = u"MouseWheelBehavior";
+inline constexpr OUStringLiteral FM_PROP_DESCRIPTION = u"Description";
+
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/inc/fmscriptingenv.hxx b/svx/source/inc/fmscriptingenv.hxx
new file mode 100644
index 000000000..18d8a5e44
--- /dev/null
+++ b/svx/source/inc/fmscriptingenv.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_SVX_SOURCE_INC_FMSCRIPTINGENV_HXX
+#define INCLUDED_SVX_SOURCE_INC_FMSCRIPTINGENV_HXX
+
+#include <com/sun/star/script/XEventAttacherManager.hpp>
+#include <rtl/ref.hxx>
+#include <salhelper/simplereferenceobject.hxx>
+
+class FmFormModel;
+
+namespace svxform
+{
+ class FormScriptListener;
+
+ //= IFormScriptingEnvironment
+
+ /** describes the interface implemented by a component which handles scripting requirements
+ in a form/control environment.
+ */
+ class FormScriptingEnvironment final : public ::salhelper::SimpleReferenceObject
+ {
+ friend class FormScriptListener;
+ public:
+ explicit FormScriptingEnvironment( FmFormModel& _rModel );
+ FormScriptingEnvironment(const FormScriptingEnvironment&) = delete;
+ FormScriptingEnvironment& operator=(const FormScriptingEnvironment&) = delete;
+
+ /** registers an XEventAttacherManager whose events should be monitored and handled
+
+ @param _rxManager
+ the XEventAttacherManager to monitor. Must not be <NULL/>.
+
+ @throws css::lang::IllegalArgumentException
+ if <arg>_rxManager</arg> is <NULL/>
+ @throws css::lang::DisposedException
+ if the instance is already disposed
+ @throws css::uno::RuntimeException
+ if attaching as script listener to the manager fails with a RuntimeException itself
+ */
+ void registerEventAttacherManager(
+ const css::uno::Reference< css::script::XEventAttacherManager >& _rxManager );
+
+ /** registers an XEventAttacherManager whose events should not be monitored and handled anymore
+
+ @param _rxManager
+ the XEventAttacherManager which was previously registered. Must not ne <NULL/>.
+
+ @throws css::lang::IllegalArgumentException
+ if <arg>_rxManager</arg> is <NULL/>
+ @throws css::lang::DisposedException
+ if the instance is already disposed
+ @throws css::uno::RuntimeException
+ if removing as script listener from the manager fails with a RuntimeException itself
+ */
+ void revokeEventAttacherManager(
+ const css::uno::Reference< css::script::XEventAttacherManager >& _rxManager );
+
+ /** disposes the scripting environment instance
+ */
+ void dispose();
+
+ private:
+ ::osl::Mutex m_aMutex;
+ rtl::Reference<FormScriptListener> m_pScriptListener;
+ FmFormModel& m_rFormModel;
+ bool m_bDisposed;
+
+ void impl_registerOrRevoke_throw( const css::uno::Reference< css::script::XEventAttacherManager >& _rxManager, bool _bRegister );
+ // callback for FormScriptListener
+ void doFireScriptEvent( const css::script::ScriptEvent& _rEvent, css::uno::Any* _pSynchronousResult );
+
+ };
+
+}
+
+
+#endif // INCLUDED_SVX_SOURCE_INC_FMSCRIPTINGENV_HXX
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/inc/fmservs.hxx b/svx/source/inc/fmservs.hxx
new file mode 100644
index 000000000..2b8ee7a7d
--- /dev/null
+++ b/svx/source/inc/fmservs.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_SVX_SOURCE_INC_FMSERVS_HXX
+#define INCLUDED_SVX_SOURCE_INC_FMSERVS_HXX
+
+#include <sal/config.h>
+
+#include <com/sun/star/uno/Reference.hxx>
+#include <svx/svxdllapi.h>
+
+namespace com::sun::star::lang { class XMultiServiceFactory; }
+
+inline constexpr OUStringLiteral FM_COMPONENT_EDIT = u"stardiv.one.form.component.Edit";
+inline constexpr OUStringLiteral FM_COMPONENT_TEXTFIELD = u"stardiv.one.form.component.TextField";
+inline constexpr OUStringLiteral FM_COMPONENT_LISTBOX = u"stardiv.one.form.component.ListBox";
+inline constexpr OUStringLiteral FM_COMPONENT_COMBOBOX = u"stardiv.one.form.component.ComboBox";
+inline constexpr OUStringLiteral FM_COMPONENT_RADIOBUTTON = u"stardiv.one.form.component.RadioButton";
+inline constexpr OUStringLiteral FM_COMPONENT_GROUPBOX = u"stardiv.one.form.component.GroupBox";
+inline constexpr OUStringLiteral FM_COMPONENT_FIXEDTEXT = u"stardiv.one.form.component.FixedText";
+inline constexpr OUStringLiteral FM_COMPONENT_COMMANDBUTTON = u"stardiv.one.form.component.CommandButton";
+inline constexpr OUStringLiteral FM_COMPONENT_CHECKBOX = u"stardiv.one.form.component.CheckBox";
+inline constexpr OUStringLiteral FM_COMPONENT_GRID = u"stardiv.one.form.component.Grid";
+inline constexpr OUStringLiteral FM_COMPONENT_GRIDCONTROL = u"stardiv.one.form.component.GridControl";
+inline constexpr OUStringLiteral FM_COMPONENT_IMAGEBUTTON = u"stardiv.one.form.component.ImageButton";
+inline constexpr OUStringLiteral FM_COMPONENT_FILECONTROL = u"stardiv.one.form.component.FileControl";
+inline constexpr OUStringLiteral FM_COMPONENT_TIMEFIELD = u"stardiv.one.form.component.TimeField";
+inline constexpr OUStringLiteral FM_COMPONENT_DATEFIELD = u"stardiv.one.form.component.DateField";
+inline constexpr OUStringLiteral FM_COMPONENT_NUMERICFIELD = u"stardiv.one.form.component.NumericField";
+inline constexpr OUStringLiteral FM_COMPONENT_CURRENCYFIELD = u"stardiv.one.form.component.CurrencyField";
+inline constexpr OUStringLiteral FM_COMPONENT_PATTERNFIELD = u"stardiv.one.form.component.PatternField";
+inline constexpr OUStringLiteral FM_COMPONENT_FORMATTEDFIELD = u"stardiv.one.form.component.FormattedField";
+inline constexpr OUStringLiteral FM_COMPONENT_HIDDEN = u"stardiv.one.form.component.Hidden";
+inline constexpr OUStringLiteral FM_COMPONENT_HIDDENCONTROL = u"stardiv.one.form.component.HiddenControl";
+inline constexpr OUStringLiteral FM_COMPONENT_IMAGECONTROL = u"stardiv.one.form.component.ImageControl";
+inline constexpr OUStringLiteral FM_CONTROL_GRID = u"stardiv.one.form.control.Grid";
+inline constexpr OUStringLiteral FM_CONTROL_GRIDCONTROL = u"stardiv.one.form.control.GridControl";
+inline constexpr OUStringLiteral SRV_SDB_CONNECTION = u"com.sun.star.sdb.Connection";
+inline constexpr OUStringLiteral FM_SUN_COMPONENT_FORM = u"com.sun.star.form.component.Form";
+inline constexpr OUStringLiteral FM_SUN_COMPONENT_TEXTFIELD = u"com.sun.star.form.component.TextField";
+inline constexpr OUStringLiteral FM_SUN_COMPONENT_LISTBOX = u"com.sun.star.form.component.ListBox";
+inline constexpr OUStringLiteral FM_SUN_COMPONENT_COMBOBOX = u"com.sun.star.form.component.ComboBox";
+inline constexpr OUStringLiteral FM_SUN_COMPONENT_RADIOBUTTON = u"com.sun.star.form.component.RadioButton";
+inline constexpr OUStringLiteral FM_SUN_COMPONENT_GROUPBOX = u"com.sun.star.form.component.GroupBox";
+inline constexpr OUStringLiteral FM_SUN_COMPONENT_FIXEDTEXT = u"com.sun.star.form.component.FixedText";
+inline constexpr OUStringLiteral FM_SUN_COMPONENT_COMMANDBUTTON = u"com.sun.star.form.component.CommandButton";
+inline constexpr OUStringLiteral FM_SUN_COMPONENT_CHECKBOX = u"com.sun.star.form.component.CheckBox";
+inline constexpr OUStringLiteral FM_SUN_COMPONENT_GRIDCONTROL = u"com.sun.star.form.component.GridControl";
+inline constexpr OUStringLiteral FM_SUN_COMPONENT_IMAGEBUTTON = u"com.sun.star.form.component.ImageButton";
+inline constexpr OUStringLiteral FM_SUN_COMPONENT_FILECONTROL = u"com.sun.star.form.component.FileControl";
+inline constexpr OUStringLiteral FM_SUN_COMPONENT_TIMEFIELD = u"com.sun.star.form.component.TimeField";
+inline constexpr OUStringLiteral FM_SUN_COMPONENT_DATEFIELD = u"com.sun.star.form.component.DateField";
+inline constexpr OUStringLiteral FM_SUN_COMPONENT_NUMERICFIELD = u"com.sun.star.form.component.NumericField";
+inline constexpr OUStringLiteral FM_SUN_COMPONENT_CURRENCYFIELD = u"com.sun.star.form.component.CurrencyField";
+inline constexpr OUStringLiteral FM_SUN_COMPONENT_PATTERNFIELD = u"com.sun.star.form.component.PatternField";
+inline constexpr OUStringLiteral FM_SUN_COMPONENT_HIDDENCONTROL = u"com.sun.star.form.component.HiddenControl";
+inline constexpr OUStringLiteral FM_SUN_COMPONENT_IMAGECONTROL = u"com.sun.star.form.component.DatabaseImageControl";
+inline constexpr OUStringLiteral FM_SUN_COMPONENT_FORMATTEDFIELD = u"com.sun.star.form.component.FormattedField";
+inline constexpr OUStringLiteral FM_SUN_COMPONENT_SCROLLBAR = u"com.sun.star.form.component.ScrollBar";
+inline constexpr OUStringLiteral FM_SUN_COMPONENT_SPINBUTTON = u"com.sun.star.form.component.SpinButton";
+inline constexpr OUStringLiteral FM_SUN_COMPONENT_NAVIGATIONBAR = u"com.sun.star.form.component.NavigationToolBar";
+inline constexpr OUStringLiteral FM_SUN_CONTROL_GRIDCONTROL = u"com.sun.star.form.control.GridControl";
+
+namespace svxform
+{
+ SVXCORE_DLLPUBLIC void ImplSmartRegisterUnoServices();
+
+ css::uno::Reference<css::uno::XInterface>
+ OAddConditionDialog_Create(
+ css::uno::Reference<css::lang::XMultiServiceFactory> const &);
+
+ OUString OAddConditionDialog_GetImplementationName();
+
+ css::uno::Sequence<OUString>
+ OAddConditionDialog_GetSupportedServiceNames();
+}
+
+/// @throws css::uno::Exception
+css::uno::Reference<css::uno::XInterface>
+FmXGridControl_NewInstance_Impl(
+ css::uno::Reference<css::lang::XMultiServiceFactory> const &);
+
+/// @throws css::uno::Exception
+css::uno::Reference<css::uno::XInterface>
+FormController_NewInstance_Impl(
+ css::uno::Reference<css::lang::XMultiServiceFactory> const &);
+
+/// @throws css::uno::Exception
+css::uno::Reference<css::uno::XInterface>
+LegacyFormController_NewInstance_Impl(
+ css::uno::Reference<css::lang::XMultiServiceFactory> const &);
+
+#endif // INCLUDED_SVX_SOURCE_INC_FMSERVS_HXX
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/inc/fmshimp.hxx b/svx/source/inc/fmshimp.hxx
new file mode 100644
index 000000000..a468bc030
--- /dev/null
+++ b/svx/source/inc/fmshimp.hxx
@@ -0,0 +1,562 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+#ifndef INCLUDED_SVX_SOURCE_INC_FMSHIMP_HXX
+#define INCLUDED_SVX_SOURCE_INC_FMSHIMP_HXX
+
+#include <config_options.h>
+#include <com/sun/star/sdbc/XResultSet.hpp>
+#include <com/sun/star/container/ContainerEvent.hpp>
+#include <com/sun/star/container/XContainerListener.hpp>
+#include <com/sun/star/awt/XControl.hpp>
+#include <com/sun/star/awt/XControlContainer.hpp>
+#include <com/sun/star/form/XForm.hpp>
+#include <com/sun/star/form/runtime/XFormController.hpp>
+#include <com/sun/star/form/XFormComponent.hpp>
+#include <com/sun/star/form/NavigationBarMode.hpp>
+#include <com/sun/star/frame/XFrame.hpp>
+#include <com/sun/star/view/XSelectionChangeListener.hpp>
+#include <com/sun/star/beans/XPropertyChangeListener.hpp>
+#include <com/sun/star/beans/PropertyChangeEvent.hpp>
+#include <com/sun/star/form/runtime/FeatureState.hpp>
+#include <tools/diagnose_ex.h>
+#include <vcl/timer.hxx>
+#include <sfx2/shell.hxx>
+#include <svx/svdmark.hxx>
+#include <svx/fmsearch.hxx>
+
+#include <svx/fmtools.hxx>
+#include <osl/mutex.hxx>
+#include <comphelper/container.hxx>
+#include <cppuhelper/basemutex.hxx>
+#include <cppuhelper/compbase.hxx>
+#include <unotools/configitem.hxx>
+#include "formcontrolling.hxx"
+#include "fmdocumentclassification.hxx"
+#include <o3tl/typed_flags_set.hxx>
+
+#include <queue>
+#include <string_view>
+#include <vector>
+#include <memory>
+
+struct ImplSVEvent;
+
+typedef std::vector< css::uno::Reference< css::form::XForm > > FmFormArray;
+
+// catch database exceptions if they occur
+#define DO_SAFE(statement) try { statement; } catch( const Exception& ) { TOOLS_WARN_EXCEPTION("svx", "unhandled exception (I tried to move a cursor (or something like that).)"); }
+
+enum class LoopGridsSync {
+ DISABLE_SYNC = 1,
+ FORCE_SYNC = 2,
+ ENABLE_SYNC = 3
+};
+enum class LoopGridsFlags
+{
+ NONE = 0,
+ DISABLE_ROCTRLR = 4
+};
+namespace o3tl
+{
+ template<> struct typed_flags<LoopGridsFlags> : is_typed_flags<LoopGridsFlags, 0x04> {};
+}
+
+
+// flags for controlling the behaviour when calling loadForms
+enum class LoadFormsFlags {
+ Load = 0x0000, // default: simply load
+ Sync = 0x0000, // default: do in synchronous
+ Unload = 0x0001, // unload
+ Async = 0x0002 // do this async
+};
+namespace o3tl {
+ template<> struct typed_flags<LoadFormsFlags> : is_typed_flags<LoadFormsFlags, 0x0003> {};
+}
+
+namespace weld {
+ class Menu;
+}
+
+// a class iterating through all fields of a form which are bound to a field
+// sub forms are ignored, grid columns (where the grid is a direct child of the form) are included
+class FmXBoundFormFieldIterator final : public ::comphelper::IndexAccessIterator
+{
+public:
+ FmXBoundFormFieldIterator(const css::uno::Reference< css::uno::XInterface>& _rStartingPoint) : ::comphelper::IndexAccessIterator(_rStartingPoint) { }
+
+private:
+ virtual bool ShouldHandleElement(const css::uno::Reference< css::uno::XInterface>& _rElement) override;
+ virtual bool ShouldStepInto(const css::uno::Reference< css::uno::XInterface>& _rContainer) const override;
+};
+
+class FmFormPage;
+class FmFormPageImpl;
+
+struct FmLoadAction
+{
+ FmFormPage* pPage;
+ ImplSVEvent * nEventId;
+ LoadFormsFlags nFlags;
+
+ FmLoadAction( FmFormPage* _pPage, LoadFormsFlags _nFlags, ImplSVEvent * _nEventId )
+ :pPage( _pPage ), nEventId( _nEventId ), nFlags( _nFlags )
+ {
+ }
+};
+
+
+class SfxViewFrame;
+typedef ::cppu::WeakComponentImplHelper< css::beans::XPropertyChangeListener
+ , css::container::XContainerListener
+ , css::view::XSelectionChangeListener
+ , css::form::XFormControllerListener
+ > FmXFormShell_BD_BASE;
+
+
+class FmXFormShell_Base_Disambiguation : public FmXFormShell_BD_BASE
+{
+ using css::beans::XPropertyChangeListener::disposing;
+protected:
+ FmXFormShell_Base_Disambiguation( ::osl::Mutex& _rMutex );
+ using WeakComponentImplHelperBase::disposing;
+};
+
+
+namespace svx
+{
+ class FmTextControlShell;
+}
+
+
+typedef FmXFormShell_Base_Disambiguation FmXFormShell_BASE;
+typedef ::utl::ConfigItem FmXFormShell_CFGBASE;
+
+struct SdrViewEvent;
+class FmFormShell;
+class FmFormView;
+class FmFormObj;
+class UNLESS_MERGELIBS(SVXCORE_DLLPUBLIC) FmXFormShell final : private cppu::BaseMutex
+ ,public FmXFormShell_BASE
+ ,public FmXFormShell_CFGBASE
+ ,public svx::IControllerFeatureInvalidation
+{
+ friend class FmFormView;
+ friend class FmXFormView;
+
+ class SuspendPropertyTracking;
+ friend class SuspendPropertyTracking;
+
+ // timer for delayed mark
+ Timer m_aMarkTimer;
+ std::vector<SdrObject*> m_arrSearchedControls;
+ // We enable a permanent cursor for the grid we found a searched text, it's disabled in the next "found" event.
+ FmFormArray m_aSearchForms;
+
+ struct SAL_DLLPRIVATE InvalidSlotInfo {
+ sal_uInt16 id;
+ sal_uInt8 flags;
+ InvalidSlotInfo(sal_uInt16 slotId, sal_uInt8 flgs) : id(slotId), flags(flgs) {};
+ };
+ std::vector<InvalidSlotInfo> m_arrInvalidSlots;
+ // we explicitly switch off the propbrw before leaving the design mode
+ // this flag tells us if we have to switch it on again when reentering
+
+ css::form::NavigationBarMode m_eNavigate; // kind of navigation
+
+ // since I want to mark an SdrObject when searching for the treatment of the "found",
+ // I get all relevant objects before yanking up of the search dialog
+ // (the array is thus only valid during the search process)
+ std::vector<sal_Int16> m_arrRelativeGridColumn;
+
+ ImplSVEvent * m_nInvalidationEvent;
+ ImplSVEvent * m_nActivationEvent;
+ ::std::queue< FmLoadAction >
+ m_aLoadingPages;
+
+ FmFormShell* m_pShell;
+ std::unique_ptr<svx::FmTextControlShell> m_pTextShell;
+
+ svx::ControllerFeatures m_aActiveControllerFeatures;
+ svx::ControllerFeatures m_aNavControllerFeatures;
+
+ // current form, controller
+ // only available in the alive mode
+ css::uno::Reference< css::form::runtime::XFormController > m_xActiveController;
+ css::uno::Reference< css::form::runtime::XFormController > m_xNavigationController;
+ css::uno::Reference< css::form::XForm > m_xActiveForm;
+
+ // current container of a page
+ // only available in the design mode
+ css::uno::Reference< css::container::XIndexAccess> m_xForms;
+
+ // the currently selected objects, as to be displayed in the property browser
+ InterfaceBag m_aCurrentSelection;
+ /// the currently selected form, or the form which all currently selected controls belong to, or <NULL/>
+ css::uno::Reference< css::form::XForm > m_xCurrentForm;
+ /// the last selection/marking of controls only. Necessary to implement the "Control properties" slot
+ InterfaceBag m_aLastKnownMarkedControls;
+
+
+ // And this is also for the 'found': When I find in GridControls, I need the column,
+ // but only get the number of the field corresponding to the number of the
+ // column + <offset>, where the offset depends on the position of the GridControl
+ // in the form. So here is a conversion.
+ css::uno::Reference< css::awt::XControlModel> m_xLastGridFound;
+ // the frame we live in
+ css::uno::Reference< css::frame::XFrame> m_xAttachedFrame;
+ // Administration of external form views (see the SID_FM_VIEW_AS_GRID-slot)
+ css::uno::Reference< css::frame::XController > m_xExternalViewController; // the controller for the external form view
+ css::uno::Reference< css::form::runtime::XFormController > m_xExtViewTriggerController; // the nav controller at the time the external display was triggered
+ css::uno::Reference< css::sdbc::XResultSet > m_xExternalDisplayedForm; // the form which the external view is based on
+
+ mutable ::svxform::DocumentType
+ m_eDocumentType; /// the type of document we're living in
+ sal_Int16 m_nLockSlotInvalidation;
+ bool m_bHadPropertyBrowserInDesignMode : 1;
+
+ bool m_bTrackProperties : 1;
+ // should I (or the owner of this impl class) take car of the update of the css::beans::Property-Browser?
+
+ bool m_bUseWizards : 1;
+
+ bool m_bDatabaseBar : 1; // is there a database bar
+ bool m_bInActivate : 1; // is a controller activated
+ bool m_bSetFocus : 1; // may the focus be changed over
+ bool m_bFilterMode : 1; // is a filter currently set to the controls
+ bool m_bChangingDesignMode:1; // sal_True within SetDesignMode
+ bool m_bPreparedClose : 1; // for the current modification state of the current form
+ // PrepareClose had been called and the user denied to save changes
+ bool m_bFirstActivation : 1; // has the shell ever been activated?
+
+public:
+ // attribute access
+ SAL_DLLPRIVATE const css::uno::Reference< css::frame::XFrame >&
+ getHostFrame_Lock() const { return m_xAttachedFrame; }
+ SAL_DLLPRIVATE const css::uno::Reference< css::sdbc::XResultSet >&
+ getExternallyDisplayedForm_Lock() const { return m_xExternalDisplayedForm; }
+
+ SAL_DLLPRIVATE bool
+ didPrepareClose_Lock() const { return m_bPreparedClose; }
+ SAL_DLLPRIVATE void
+ didPrepareClose_Lock(bool bDid) { m_bPreparedClose = bDid; }
+
+ SAL_DLLPRIVATE FmXFormShell(FmFormShell& _rShell, SfxViewFrame* _pViewFrame);
+
+private:
+ SAL_DLLPRIVATE virtual ~FmXFormShell() override;
+
+// EventListener
+ SAL_DLLPRIVATE virtual void SAL_CALL disposing(const css::lang::EventObject& Source) override;
+
+// css::container::XContainerListener
+ SAL_DLLPRIVATE virtual void SAL_CALL elementInserted(const css::container::ContainerEvent& rEvent) override;
+ SAL_DLLPRIVATE virtual void SAL_CALL elementReplaced(const css::container::ContainerEvent& rEvent) override;
+ SAL_DLLPRIVATE virtual void SAL_CALL elementRemoved(const css::container::ContainerEvent& rEvent) override;
+
+// XSelectionChangeListener
+ SAL_DLLPRIVATE virtual void SAL_CALL selectionChanged(const css::lang::EventObject& rEvent) override;
+
+// css::beans::XPropertyChangeListener
+ SAL_DLLPRIVATE virtual void SAL_CALL propertyChange(const css::beans::PropertyChangeEvent& evt) override;
+
+// css::form::XFormControllerListener
+ SAL_DLLPRIVATE virtual void SAL_CALL formActivated(const css::lang::EventObject& rEvent) override;
+ SAL_DLLPRIVATE virtual void SAL_CALL formDeactivated(const css::lang::EventObject& rEvent) override;
+
+// OComponentHelper
+ SAL_DLLPRIVATE virtual void SAL_CALL disposing() override;
+
+public:
+ SAL_DLLPRIVATE void EnableTrackProperties_Lock(bool bEnable) { m_bTrackProperties = bEnable; }
+ SAL_DLLPRIVATE bool IsTrackPropertiesEnabled_Lock() const { return m_bTrackProperties; }
+
+ // activation handling
+ SAL_DLLPRIVATE void viewActivated_Lock(FmFormView& _rCurrentView, bool _bSyncAction = false);
+ SAL_DLLPRIVATE void viewDeactivated_Lock(FmFormView& _rCurrentView, bool _bDeactivateController = true);
+
+ // IControllerFeatureInvalidation
+ SAL_DLLPRIVATE virtual void invalidateFeatures/*_NoLock*/( const ::std::vector< sal_Int32 >& _rFeatures ) override;
+
+ SAL_DLLPRIVATE void ExecuteTabOrderDialog_Lock( // execute SID_FM_TAB_DIALOG
+ const css::uno::Reference< css::awt::XTabControllerModel >& _rxForForm
+ );
+
+ // stuff
+ SAL_DLLPRIVATE void AddElement_Lock(const css::uno::Reference< css::uno::XInterface>& Element);
+ SAL_DLLPRIVATE void RemoveElement_Lock(const css::uno::Reference< css::uno::XInterface>& Element);
+
+ /** updates m_xForms, to be either <NULL/>, if we're in alive mode, or our current page's forms collection,
+ if in design mode
+ */
+ SAL_DLLPRIVATE void UpdateForms_Lock(bool bInvalidate);
+
+ SAL_DLLPRIVATE void ExecuteSearch_Lock(); // execute SID_FM_SEARCH
+ SAL_DLLPRIVATE void CreateExternalView_Lock(); // execute SID_FM_VIEW_AS_GRID
+
+ SAL_DLLPRIVATE bool GetY2KState_Lock(sal_uInt16 & n);
+ SAL_DLLPRIVATE void SetY2KState_Lock(sal_uInt16 n);
+
+private:
+ // form handling
+ /// load or unload the forms on a page
+ SAL_DLLPRIVATE void loadForms_Lock( FmFormPage* _pPage, const LoadFormsFlags _nBehaviour );
+ SAL_DLLPRIVATE void smartControlReset( const css::uno::Reference< css::container::XIndexAccess >& _rxModels );
+
+
+ SAL_DLLPRIVATE void startListening_Lock();
+ SAL_DLLPRIVATE void stopListening_Lock();
+
+ SAL_DLLPRIVATE css::uno::Reference< css::awt::XControl >
+ impl_getControl_Lock(
+ const css::uno::Reference< css::awt::XControlModel>& i_rxModel,
+ const FmFormObj& i_rKnownFormObj
+ );
+
+ // collects in strNames the names of all forms
+ SAL_DLLPRIVATE static void impl_collectFormSearchContexts_nothrow_Lock(
+ const css::uno::Reference< css::uno::XInterface>& _rxStartingPoint,
+ std::u16string_view _rCurrentLevelPrefix,
+ FmFormArray& _out_rForms,
+ ::std::vector< OUString >& _out_rNames );
+
+ /** checks whenever the instance is already disposed, if so, this is reported as assertion error (debug
+ builds only) and <TRUE/> is returned.
+ */
+ SAL_DLLPRIVATE bool impl_checkDisposed_Lock() const;
+
+public:
+ // method for non design mode (alive mode)
+ SAL_DLLPRIVATE void setActiveController_Lock(const css::uno::Reference< css::form::runtime::XFormController>& _xController, bool _bNoSaveOldContent = false);
+ SAL_DLLPRIVATE const css::uno::Reference< css::form::runtime::XFormController>& getActiveController_Lock() const { return m_xActiveController; }
+ SAL_DLLPRIVATE const css::uno::Reference< css::form::runtime::XFormController>& getActiveInternalController_Lock() const { return m_xActiveController == m_xExternalViewController ? m_xExtViewTriggerController : m_xActiveController; }
+ SAL_DLLPRIVATE const css::uno::Reference< css::form::XForm>& getActiveForm_Lock() const { return m_xActiveForm; }
+ SAL_DLLPRIVATE const css::uno::Reference< css::form::runtime::XFormController>& getNavController_Lock() const { return m_xNavigationController; }
+
+ SAL_DLLPRIVATE const svx::ControllerFeatures& getActiveControllerFeatures_Lock() const
+ { return m_aActiveControllerFeatures; }
+ SAL_DLLPRIVATE const svx::ControllerFeatures& getNavControllerFeatures_Lock() const
+ { return m_aNavControllerFeatures.isAssigned() ? m_aNavControllerFeatures : m_aActiveControllerFeatures; }
+
+ /** announces a new "current selection"
+ @return
+ <TRUE/> if and only if the to-bet-set selection was different from the previous selection
+ */
+ SAL_DLLPRIVATE bool setCurrentSelection_Lock(InterfaceBag&& rSelection);
+
+ /** sets the new selection to the last known marked controls
+ */
+ SAL_DLLPRIVATE bool selectLastMarkedControls_Lock();
+
+ /** retrieves the current selection
+ */
+ void getCurrentSelection_Lock(InterfaceBag& /* [out] */ _rSelection) const;
+
+ /** sets a new current selection as indicated by a mark list
+ @return
+ <TRUE/> if and only if the to-bet-set selection was different from the previous selection
+ */
+ SAL_DLLPRIVATE bool setCurrentSelectionFromMark_Lock(const SdrMarkList& rMarkList);
+
+ /// returns the currently selected form, or the form which all currently selected controls belong to, or <NULL/>
+ SAL_DLLPRIVATE const css::uno::Reference< css::form::XForm >&
+ getCurrentForm_Lock() const { return m_xCurrentForm; }
+ SAL_DLLPRIVATE void forgetCurrentForm_Lock();
+ /// returns whether the last known marking contained only controls
+ SAL_DLLPRIVATE bool onlyControlsAreMarked_Lock() const { return !m_aLastKnownMarkedControls.empty(); }
+
+ /// determines whether the current selection consists of exactly the given object
+ SAL_DLLPRIVATE bool isSolelySelected_Lock(
+ const css::uno::Reference< css::uno::XInterface >& _rxObject
+ );
+
+ /// handles a MouseButtonDown event of the FmFormView
+ SAL_DLLPRIVATE void handleMouseButtonDown_Lock( const SdrViewEvent& _rViewEvent );
+ /// handles the request for showing the "Properties"
+ SAL_DLLPRIVATE void handleShowPropertiesRequest_Lock();
+
+ SAL_DLLPRIVATE bool hasForms_Lock() const { return m_xForms.is() && m_xForms->getCount() != 0; }
+ SAL_DLLPRIVATE bool hasDatabaseBar_Lock() const { return m_bDatabaseBar; }
+
+ SAL_DLLPRIVATE void ShowSelectionProperties_Lock(bool bShow);
+ SAL_DLLPRIVATE bool IsPropBrwOpen_Lock() const;
+
+ SAL_DLLPRIVATE void DetermineSelection_Lock(const SdrMarkList& rMarkList);
+ SAL_DLLPRIVATE void SetSelection_Lock(const SdrMarkList& rMarkList);
+ SAL_DLLPRIVATE void SetSelectionDelayed_Lock();
+
+ SAL_DLLPRIVATE void SetDesignMode_Lock(bool bDesign);
+
+ SAL_DLLPRIVATE bool GetWizardUsing_Lock() const { return m_bUseWizards; }
+ SAL_DLLPRIVATE void SetWizardUsing_Lock(bool _bUseThem);
+
+ // setting the filter mode
+ SAL_DLLPRIVATE bool isInFilterMode_Lock() const { return m_bFilterMode; }
+ SAL_DLLPRIVATE void startFiltering_Lock();
+ SAL_DLLPRIVATE void stopFiltering_Lock(bool bSave);
+
+ // fills rMenu to be a menu that contains all ControlConversion entries
+ SAL_DLLPRIVATE static void GetConversionMenu_Lock(weld::Menu& rMenu);
+
+ /// checks whether a given control conversion slot can be applied to the current selection
+ SAL_DLLPRIVATE bool canConvertCurrentSelectionToControl_Lock(std::string_view rIdent);
+ /// enables or disables all conversion slots in a menu, according to the current selection
+ SAL_DLLPRIVATE void checkControlConversionSlotsForCurrentSelection_Lock(weld::Menu& rMenu);
+ /// executes a control conversion slot for a given object
+ SAL_DLLPRIVATE bool executeControlConversionSlot_Lock(const css::uno::Reference< css::form::XFormComponent >& _rxObject, std::string_view rIdent);
+ /** executes a control conversion slot for the current selection
+ @precond canConvertCurrentSelectionToControl( <arg>_nSlotId</arg> ) must return <TRUE/>
+ */
+ SAL_DLLPRIVATE void executeControlConversionSlot_Lock(std::string_view rIdent);
+ /// checks whether the given slot id denotes a control conversion slot
+ SAL_DLLPRIVATE static bool isControlConversionSlot(std::string_view rIdent);
+
+ SAL_DLLPRIVATE void ExecuteTextAttribute_Lock(SfxRequest& _rReq);
+ SAL_DLLPRIVATE void GetTextAttributeState_Lock(SfxItemSet& _rSet);
+ SAL_DLLPRIVATE bool IsActiveControl_Lock(bool _bCountRichTextOnly) const;
+ SAL_DLLPRIVATE void ForgetActiveControl_Lock();
+ SAL_DLLPRIVATE void SetControlActivationHandler_Lock(const Link<LinkParamNone*,void>& _rHdl);
+
+ /// classifies our host document
+ SAL_DLLPRIVATE ::svxform::DocumentType getDocumentType_Lock() const;
+ SAL_DLLPRIVATE bool isEnhancedForm_Lock() const;
+
+ /// determines whether our host document is currently read-only
+ SAL_DLLPRIVATE bool IsReadonlyDoc_Lock() const;
+
+ // Setting the curObject/selObject/curForm is delayed (SetSelectionDelayed). With the
+ // following functions this can be inquired/enforced.
+ SAL_DLLPRIVATE inline bool IsSelectionUpdatePending_Lock() const;
+ SAL_DLLPRIVATE void ForceUpdateSelection_Lock();
+
+ SAL_DLLPRIVATE css::uno::Reference< css::frame::XModel> getContextDocument_Lock() const;
+ SAL_DLLPRIVATE css::uno::Reference< css::form::XForm> getInternalForm_Lock(const css::uno::Reference< css::form::XForm>& _xForm) const;
+ SAL_DLLPRIVATE css::uno::Reference< css::sdbc::XResultSet> getInternalForm_Lock(const css::uno::Reference< css::sdbc::XResultSet>& _xForm) const;
+ // if the form belongs to the controller (extern) displaying a grid, the according internal form will
+ // be displayed, _xForm else
+
+ // check if the current control of the active controller has the focus
+ SAL_DLLPRIVATE bool HasControlFocus_Lock() const;
+
+private:
+ DECL_DLLPRIVATE_LINK(OnFoundData_Lock, FmFoundRecordInformation&, void);
+ DECL_DLLPRIVATE_LINK(OnCanceledNotFound_Lock, FmFoundRecordInformation&, void);
+ DECL_DLLPRIVATE_LINK(OnSearchContextRequest_Lock, FmSearchContext&, sal_uInt32);
+ DECL_DLLPRIVATE_LINK(OnTimeOut_Lock, Timer*, void);
+ DECL_DLLPRIVATE_LINK(OnFirstTimeActivation_Lock, void*, void);
+ DECL_DLLPRIVATE_LINK(OnFormsCreated_Lock, FmFormPageImpl&, void);
+
+ SAL_DLLPRIVATE void LoopGrids_Lock(LoopGridsSync nSync, LoopGridsFlags nWhat = LoopGridsFlags::NONE);
+
+ // invalidation of slots
+ SAL_DLLPRIVATE void InvalidateSlot_Lock(sal_Int16 nId, bool bWithId);
+ SAL_DLLPRIVATE void UpdateSlot_Lock(sal_Int16 nId);
+ // locking the invalidation - if the internal locking counter goes to 0, all accumulated slots
+ // are invalidated (asynchronously)
+ SAL_DLLPRIVATE void LockSlotInvalidation_Lock(bool bLock);
+
+ DECL_DLLPRIVATE_LINK(OnInvalidateSlots_Lock, void*, void);
+
+ SAL_DLLPRIVATE void CloseExternalFormViewer_Lock();
+ // closes the task-local beamer displaying a grid view for a form
+
+ // ConfigItem related stuff
+ SAL_DLLPRIVATE virtual void Notify( const css::uno::Sequence< OUString >& _rPropertyNames) override;
+ SAL_DLLPRIVATE void implAdjustConfigCache_Lock();
+
+ SAL_DLLPRIVATE css::uno::Reference< css::awt::XControlContainer >
+ getControlContainerForView_Lock() const;
+
+ /** finds and sets a default for m_xCurrentForm, if it is currently NULL
+ */
+ SAL_DLLPRIVATE void impl_defaultCurrentForm_nothrow_Lock();
+
+ /** sets m_xCurrentForm to the provided form, and updates everything which
+ depends on the current form
+ */
+ SAL_DLLPRIVATE void impl_updateCurrentForm_Lock( const css::uno::Reference< css::form::XForm >& _rxNewCurForm );
+
+ /** adds or removes ourself as XEventListener at m_xActiveController
+ */
+ SAL_DLLPRIVATE void impl_switchActiveControllerListening_Lock(const bool _bListen);
+
+ /** add an element
+ */
+ SAL_DLLPRIVATE void impl_AddElement_nothrow(const css::uno::Reference< css::uno::XInterface>& Element);
+
+ /** remove an element
+ */
+ SAL_DLLPRIVATE void impl_RemoveElement_nothrow_Lock(const css::uno::Reference< css::uno::XInterface>& Element);
+
+ SAL_DLLPRIVATE virtual void ImplCommit() override;
+
+ // asynchronous cursor actions/navigation slot handling
+
+public:
+ /** execute the given form slot
+ <p>Warning. Only a small set of slots implemented currently.</p>
+ @param _nSlot
+ the slot to execute
+ */
+ SAL_DLLPRIVATE void ExecuteFormSlot_Lock(sal_Int32 _nSlot);
+
+ /** determines whether the current form slot is currently enabled
+ */
+ SAL_DLLPRIVATE bool IsFormSlotEnabled( sal_Int32 _nSlot, css::form::runtime::FeatureState* _pCompleteState ) const;
+
+ SAL_DLLPRIVATE static OString SlotToIdent(sal_uInt16 nSlot);
+
+private:
+ DECL_DLLPRIVATE_LINK( OnLoadForms_Lock, void*, void );
+};
+
+
+inline bool FmXFormShell::IsSelectionUpdatePending_Lock() const
+{
+ return m_aMarkTimer.IsActive();
+}
+
+
+// = An iterator that, emanating from an interface, looks for an object whose
+// = css::beans::Property-Set has a ControlSource and a BoundField property, the
+// = latter having a non-NULL value. If the interface itself does not meet this
+// = condition, it is tested whether it is a container (that is, has a
+// = css::container::XIndexAccess), then it is descended there and the same tried
+// = for each element of the container (again possibly with descent). If any
+// = object thereby has the required property, the part with the container test
+// = for that object is omitted.
+// =
+
+class SearchableControlIterator final : public ::comphelper::IndexAccessIterator
+{
+ OUString m_sCurrentValue;
+ // the current value of the ControlSource css::beans::Property
+
+public:
+ const OUString& getCurrentValue() const { return m_sCurrentValue; }
+
+ SearchableControlIterator(css::uno::Reference< css::uno::XInterface> const & xStartingPoint);
+
+ virtual bool ShouldHandleElement(const css::uno::Reference< css::uno::XInterface>& rElement) override;
+ virtual bool ShouldStepInto(const css::uno::Reference< css::uno::XInterface>& xContainer) const override;
+ virtual void Invalidate() override { IndexAccessIterator::Invalidate(); m_sCurrentValue.clear(); }
+};
+
+#endif // INCLUDED_SVX_SOURCE_INC_FMSHIMP_HXX
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/inc/fmslotinvalidator.hxx b/svx/source/inc/fmslotinvalidator.hxx
new file mode 100644
index 000000000..4027b63fa
--- /dev/null
+++ b/svx/source/inc/fmslotinvalidator.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_SVX_SOURCE_INC_FMSLOTINVALIDATOR_HXX
+#define INCLUDED_SVX_SOURCE_INC_FMSLOTINVALIDATOR_HXX
+
+#include <sal/types.h>
+
+namespace svx
+{
+typedef sal_uInt16 SfxSlotId;
+}
+
+#endif // INCLUDED_SVX_SOURCE_INC_FMSLOTINVALIDATOR_HXX
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/inc/fmtextcontroldialogs.hxx b/svx/source/inc/fmtextcontroldialogs.hxx
new file mode 100644
index 000000000..e915b06f1
--- /dev/null
+++ b/svx/source/inc/fmtextcontroldialogs.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_SVX_SOURCE_INC_FMTEXTCONTROLDIALOGS_HXX
+#define INCLUDED_SVX_SOURCE_INC_FMTEXTCONTROLDIALOGS_HXX
+
+#include <sfx2/tabdlg.hxx>
+#include <editeng/flstitem.hxx>
+
+namespace svx
+{
+
+ class TextControlCharAttribDialog final : public SfxTabDialogController
+ {
+ private:
+ SvxFontListItem m_aFontList;
+
+ public:
+ TextControlCharAttribDialog(weld::Window* pParent, const SfxItemSet& rCoreSet, const SvxFontListItem& rFontList);
+
+ private:
+ virtual void PageCreated(const OString& rId, SfxTabPage& _rPage) override;
+ };
+
+ class TextControlParaAttribDialog final : public SfxTabDialogController
+ {
+ public:
+ TextControlParaAttribDialog(weld::Window* pParent, const SfxItemSet& rCoreSet);
+ };
+}
+
+#endif // INCLUDED_SVX_SOURCE_INC_FMTEXTCONTROLDIALOGS_HXX
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/inc/fmtextcontrolfeature.hxx b/svx/source/inc/fmtextcontrolfeature.hxx
new file mode 100644
index 000000000..2414f2bbd
--- /dev/null
+++ b/svx/source/inc/fmtextcontrolfeature.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_SVX_SOURCE_INC_FMTEXTCONTROLFEATURE_HXX
+#define INCLUDED_SVX_SOURCE_INC_FMTEXTCONTROLFEATURE_HXX
+
+#include <com/sun/star/beans/PropertyValue.hpp>
+#include <com/sun/star/frame/XDispatch.hpp>
+#include <com/sun/star/frame/XStatusListener.hpp>
+#include <com/sun/star/util/URL.hpp>
+#include <cppuhelper/implbase.hxx>
+#include "fmslotinvalidator.hxx"
+
+
+namespace svx
+{
+ class FmTextControlShell;
+
+ typedef ::cppu::WeakImplHelper < css::frame::XStatusListener
+ > FmTextControlFeature_Base;
+
+ class FmTextControlFeature final : public FmTextControlFeature_Base
+ {
+ private:
+ css::uno::Reference< css::frame::XDispatch >
+ m_xDispatcher;
+ css::util::URL m_aFeatureURL;
+ css::uno::Any m_aFeatureState;
+ SfxSlotId m_nSlotId;
+ FmTextControlShell* m_pInvalidator;
+ bool m_bFeatureEnabled;
+
+ public:
+ /** constructs a FmTextControlFeature object
+ @param _rxDispatcher
+ the dispatcher which the instance should work with
+ @param _rFeatureURL
+ the URL which the instance should be responsible for
+ */
+ FmTextControlFeature(
+ const css::uno::Reference< css::frame::XDispatch >& _rxDispatcher,
+ const css::util::URL& _rFeatureURL,
+ SfxSlotId _nId,
+ FmTextControlShell* _pInvalidator
+ );
+
+ /// determines whether the feature we're responsible for is currently enabled
+ bool isFeatureEnabled( ) const { return m_bFeatureEnabled; }
+ const css::uno::Any& getFeatureState( ) const { return m_aFeatureState; }
+
+ /** dispatches the feature URL to the dispatcher
+ */
+ void dispatch() const;
+
+ /** dispatches the feature URL to the dispatcher, with passing the given arguments
+ */
+ void dispatch( const css::uno::Sequence< css::beans::PropertyValue >& _rArgs ) const;
+
+ /// releases any resources associated with this instance
+ void dispose();
+
+ private:
+ virtual ~FmTextControlFeature() override;
+
+ // XStatusListener
+ virtual void SAL_CALL statusChanged( const css::frame::FeatureStateEvent& State ) override;
+ virtual void SAL_CALL disposing( const css::lang::EventObject& Source ) override;
+ };
+
+
+}
+
+
+#endif // INCLUDED_SVX_SOURCE_INC_FMTEXTCONTROLFEATURE_HXX
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/inc/fmtextcontrolshell.hxx b/svx/source/inc/fmtextcontrolshell.hxx
new file mode 100644
index 000000000..77ea5c5d2
--- /dev/null
+++ b/svx/source/inc/fmtextcontrolshell.hxx
@@ -0,0 +1,201 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+#ifndef INCLUDED_SVX_SOURCE_INC_FMTEXTCONTROLSHELL_HXX
+#define INCLUDED_SVX_SOURCE_INC_FMTEXTCONTROLSHELL_HXX
+
+#include <com/sun/star/frame/XDispatchProvider.hpp>
+#include <com/sun/star/awt/FocusEvent.hpp>
+#include <com/sun/star/awt/XTextComponent.hpp>
+#include <com/sun/star/form/runtime/XFormController.hpp>
+#include <com/sun/star/awt/XControl.hpp>
+#include <com/sun/star/util/XURLTransformer.hpp>
+#include <rtl/ref.hxx>
+#include <tools/link.hxx>
+#include <vcl/timer.hxx>
+#include "fmslotinvalidator.hxx"
+
+#include <vector>
+#include <map>
+
+class SfxRequest;
+class SfxItemSet;
+class SfxAllItemSet;
+class SfxBindings;
+class SfxViewFrame;
+class SfxApplication;
+
+
+namespace svx
+{
+
+
+ class FmFocusListenerAdapter;
+ class FmTextControlFeature;
+ class FmMouseListenerAdapter;
+
+ class IFocusObserver
+ {
+ public:
+ virtual void focusGained( const css::awt::FocusEvent& _rEvent ) = 0;
+ virtual void focusLost( const css::awt::FocusEvent& _rEvent ) = 0;
+
+ protected:
+ ~IFocusObserver() {}
+ };
+
+
+ //= IFocusObserver
+
+ class IContextRequestObserver
+ {
+ public:
+ virtual void contextMenuRequested() = 0;
+
+ protected:
+ ~IContextRequestObserver() {}
+ };
+
+ class FmTextControlShell final : public IFocusObserver
+ ,public IContextRequestObserver
+ {
+ private:
+ css::uno::Reference< css::util::XURLTransformer > m_xURLTransformer;
+ css::uno::Reference< css::awt::XControl > m_xActiveControl;
+ css::uno::Reference< css::awt::XTextComponent > m_xActiveTextComponent;
+ css::uno::Reference< css::form::runtime::XFormController > m_xActiveController;
+#ifndef DONT_REMEMBER_LAST_CONTROL
+ // without this define, m_xActiveControl remembers the *last* active control, even
+ // if it, in the meantime, already lost the focus
+ bool m_bActiveControl;
+ // so we need an additional boolean flag telling whether the active control
+ // is really focused
+#endif
+ bool m_bActiveControlIsReadOnly;
+ bool m_bActiveControlIsRichText;
+
+ // listening at all controls of the active controller for focus changes
+ typedef rtl::Reference<FmFocusListenerAdapter> FocusListenerAdapter;
+ typedef ::std::vector< FocusListenerAdapter > FocusListenerAdapters;
+ FocusListenerAdapters m_aControlObservers;
+
+ typedef rtl::Reference<FmMouseListenerAdapter> MouseListenerAdapter;
+ MouseListenerAdapter m_aContextMenuObserver;
+
+ // translating between "slots" of the framework and "features" of the active control
+ typedef rtl::Reference<FmTextControlFeature> ControlFeature;
+ typedef ::std::map< SfxSlotId, ControlFeature > ControlFeatures;
+ ControlFeatures m_aControlFeatures;
+
+ SfxViewFrame* m_pViewFrame;
+ // invalidating slots
+ SfxBindings& m_rBindings;
+ Link<LinkParamNone*,void> m_aControlActivationHandler;
+ AutoTimer m_aClipboardInvalidation;
+ bool m_bNeedClipboardInvalidation;
+
+ public:
+ FmTextControlShell( SfxViewFrame* _pFrame );
+ virtual ~FmTextControlShell();
+
+ // clean up any resources associated with this instance
+ void dispose();
+
+ void ExecuteTextAttribute( SfxRequest& _rReq );
+ void GetTextAttributeState( SfxItemSet& _rSet );
+ bool IsActiveControl( bool _bCountRichTextOnly = false ) const;
+ void ForgetActiveControl();
+ void SetControlActivationHandler( const Link<LinkParamNone*,void>& _rHdl ) { m_aControlActivationHandler = _rHdl; }
+
+ /** to be called when a form in our document has been activated
+ */
+ void formActivated( const css::uno::Reference< css::form::runtime::XFormController >& _rxController );
+ /** to be called when a form in our document has been deactivated
+ */
+ void formDeactivated( const css::uno::Reference< css::form::runtime::XFormController >& _rxController );
+
+ /** notifies the instance that the design mode has changed
+ */
+ void designModeChanged();
+
+ void Invalidate( SfxSlotId _nSlot );
+
+ private:
+ // IFocusObserver
+ virtual void focusGained( const css::awt::FocusEvent& _rEvent ) override;
+ virtual void focusLost( const css::awt::FocusEvent& _rEvent ) override;
+
+ // IContextRequestObserver
+ virtual void contextMenuRequested() override;
+
+ enum AttributeSet { eCharAttribs, eParaAttribs };
+ void executeAttributeDialog( AttributeSet _eSet, SfxRequest& _rReq );
+ void executeSelectAll( );
+ void executeClipboardSlot( SfxSlotId _nSlot );
+
+ bool isControllerListening() const { return !m_aControlObservers.empty(); }
+
+ rtl::Reference<FmTextControlFeature>
+ implGetFeatureDispatcher(
+ const css::uno::Reference< css::frame::XDispatchProvider >& _rxProvider,
+ SfxApplication const * _pApplication,
+ SfxSlotId _nSlot
+ );
+
+ // fills the given structure with dispatchers for the given slots, for the given control
+ void fillFeatureDispatchers(
+ const css::uno::Reference< css::awt::XControl >& _rxControl,
+ SfxSlotId* _pZeroTerminatedSlots,
+ ControlFeatures& _rDispatchers
+ );
+
+ /// creates SfxPoolItes for all features in the given set, and puts them into the given SfxAllItemSet
+ static void transferFeatureStatesToItemSet(
+ ControlFeatures& _rDispatchers,
+ SfxAllItemSet& _rSet,
+ bool _bTranslateLatin
+ );
+
+ /// to be called when a control has been activated
+ void controlActivated( const css::uno::Reference< css::awt::XControl >& _rxControl );
+ /// to be called when the currently active control has been deactivated
+ void controlDeactivated( );
+
+ void implClearActiveControlRef();
+
+ /** starts listening at all controls of the given controller for focus events
+ @precond
+ we don't have an active controller currently
+ */
+ void startControllerListening( const css::uno::Reference< css::form::runtime::XFormController >& _rxController );
+ /** stops listening at the active controller
+ @precond
+ we have an active controller currently
+ */
+ void stopControllerListening( );
+
+ DECL_LINK( OnInvalidateClipboard, Timer*, void );
+ };
+
+
+}
+
+
+#endif // INCLUDED_SVX_SOURCE_INC_FMTEXTCONTROLSHELL_HXX
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/inc/fmundo.hxx b/svx/source/inc/fmundo.hxx
new file mode 100644
index 000000000..61792e1af
--- /dev/null
+++ b/svx/source/inc/fmundo.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_SVX_SOURCE_INC_FMUNDO_HXX
+#define INCLUDED_SVX_SOURCE_INC_FMUNDO_HXX
+
+#include <svx/svdundo.hxx>
+#include <svx/svdouno.hxx>
+#include "fmscriptingenv.hxx"
+
+
+#include <com/sun/star/util/XModifyListener.hpp>
+#include <com/sun/star/beans/XPropertySet.hpp>
+#include <com/sun/star/beans/XPropertyChangeListener.hpp>
+#include <com/sun/star/beans/PropertyChangeEvent.hpp>
+#include <com/sun/star/script/ScriptEventDescriptor.hpp>
+#include <com/sun/star/container/XIndexContainer.hpp>
+#include <com/sun/star/container/XContainerListener.hpp>
+#include <com/sun/star/container/ContainerEvent.hpp>
+#include <com/sun/star/container/XNameContainer.hpp>
+#include <cppuhelper/implbase.hxx>
+
+
+#include <svl/lstner.hxx>
+
+class FmFormModel;
+class FmFormObj;
+class SdrObject;
+
+class FmUndoPropertyAction final : public SdrUndoAction
+{
+ css::uno::Reference< css::beans::XPropertySet> xObj;
+ OUString aPropertyName;
+ css::uno::Any aNewValue;
+ css::uno::Any aOldValue;
+
+public:
+ FmUndoPropertyAction(FmFormModel& rMod, const css::beans::PropertyChangeEvent& evt);
+
+ virtual void Undo() override;
+ virtual void Redo() override;
+
+ virtual OUString GetComment() const override;
+
+};
+
+class FmUndoContainerAction final : public SdrUndoAction
+{
+public:
+ enum Action
+ {
+ Inserted = 1,
+ Removed = 2
+ };
+
+ FmUndoContainerAction(FmFormModel& rMod,
+ Action _eAction,
+ const css::uno::Reference< css::container::XIndexContainer >& xCont,
+ const css::uno::Reference< css::uno::XInterface >& xElem,
+ sal_Int32 nIdx);
+ virtual ~FmUndoContainerAction() override;
+
+ virtual void Undo() override;
+ virtual void Redo() override;
+
+ static void DisposeElement( const css::uno::Reference< css::uno::XInterface >& xElem );
+
+private:
+ void implReInsert( );
+ void implReRemove( );
+
+ css::uno::Reference< css::container::XIndexContainer >
+ m_xContainer; // container which the action applies to
+ css::uno::Reference< css::uno::XInterface >
+ m_xElement; // object not owned by the action
+ css::uno::Reference< css::uno::XInterface >
+ m_xOwnElement; // object owned by the action
+ sal_Int32 m_nIndex; // index of the object within its container
+ css::uno::Sequence< css::script::ScriptEventDescriptor >
+ m_aEvents; // events of the object
+ Action m_eAction;
+};
+
+class FmUndoModelReplaceAction final : public SdrUndoAction
+{
+ css::uno::Reference< css::awt::XControlModel> m_xReplaced;
+ SdrUnoObj* m_pObject;
+
+public:
+ FmUndoModelReplaceAction(FmFormModel& rMod, SdrUnoObj* pObject, const css::uno::Reference< css::awt::XControlModel>& xReplaced);
+ virtual ~FmUndoModelReplaceAction() override;
+
+ virtual void Undo() override;
+ virtual void Redo() override { Undo(); }
+
+ virtual OUString GetComment() const override;
+
+ static void DisposeElement( const css::uno::Reference< css::awt::XControlModel>& xReplaced );
+};
+
+
+class FmXUndoEnvironment final
+ : public ::cppu::WeakImplHelper< css::beans::XPropertyChangeListener
+ , css::container::XContainerListener
+ , css::util::XModifyListener
+ >
+ , public SfxListener
+{
+public:
+ FmXUndoEnvironment(FmFormModel& _rModel);
+ virtual ~FmXUndoEnvironment() override;
+
+ // UNO binding
+ // SMART_UNO_DECLARATION(FmXUndoEnvironment, ::cppu::OWeakObject);
+ // virtual sal_Bool queryInterface(UsrUik, css::uno::Reference< css::uno::XInterface>&);
+ // virtual css::uno::Sequence< css::uno::Reference< css::reflection::XIdlClass>> getIdlClasses();
+
+ void Lock() { osl_atomic_increment( &m_Locks ); }
+ void UnLock() { osl_atomic_decrement( &m_Locks ); }
+ bool IsLocked() const { return m_Locks != 0; }
+
+ // access control
+ struct Accessor { friend class FmFormModel; private: Accessor() { } };
+
+ // addition and removal of form collections
+ void AddForms( const css::uno::Reference< css::container::XNameContainer>& rForms );
+ void RemoveForms( const css::uno::Reference< css::container::XNameContainer>& rForms );
+
+ // readonly-ness
+ void SetReadOnly( bool bRead, const Accessor& ) { bReadOnly = bRead; }
+ bool IsReadOnly() const {return bReadOnly;}
+
+ // Methods for assigning controls to forms,
+ // used by the page and the undo environment
+ void Inserted(SdrObject* pObj);
+ void Removed(SdrObject* pObj);
+
+ static void Inserted(FmFormObj* pObj);
+ static void Removed(FmFormObj* pObj);
+
+private:
+ // XEventListener
+ virtual void SAL_CALL disposing(const css::lang::EventObject& Source) override;
+
+ // XPropertyChangeListener
+ virtual void SAL_CALL propertyChange(const css::beans::PropertyChangeEvent& evt) override;
+
+ // XContainerListener
+ virtual void SAL_CALL elementInserted(const css::container::ContainerEvent& rEvent) override;
+ virtual void SAL_CALL elementReplaced(const css::container::ContainerEvent& rEvent) override;
+ virtual void SAL_CALL elementRemoved(const css::container::ContainerEvent& rEvent) override;
+
+ // XModifyListener
+ virtual void SAL_CALL modified( const css::lang::EventObject& aEvent ) override;
+
+ void ModeChanged();
+ void dispose();
+
+ virtual void Notify( SfxBroadcaster& rBC, const SfxHint& rHint ) override;
+
+ void AddElement(const css::uno::Reference< css::uno::XInterface>& Element);
+ void RemoveElement(const css::uno::Reference< css::uno::XInterface>& Element);
+ void TogglePropertyListening(const css::uno::Reference< css::uno::XInterface>& Element);
+
+ void implSetModified();
+
+ void switchListening( const css::uno::Reference< css::container::XIndexContainer >& _rxContainer, bool _bStartListening );
+ void switchListening( const css::uno::Reference< css::uno::XInterface >& _rxObject, bool _bStartListening );
+
+ FmFormModel& rModel;
+ void* m_pPropertySetCache;
+ ::rtl::Reference<svxform::FormScriptingEnvironment> m_pScriptingEnv;
+ oslInterlockedCount m_Locks;
+ ::osl::Mutex m_aMutex;
+ bool bReadOnly;
+ bool m_bDisposed;
+ css::uno::Reference< css::script::XScriptListener > m_vbaListener;
+};
+
+
+#endif // INCLUDED_SVX_SOURCE_INC_FMUNDO_HXX
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/inc/fmurl.hxx b/svx/source/inc/fmurl.hxx
new file mode 100644
index 000000000..5386270ae
--- /dev/null
+++ b/svx/source/inc/fmurl.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_SVX_SOURCE_INC_FMURL_HXX
+#define INCLUDED_SVX_SOURCE_INC_FMURL_HXX
+
+#include <rtl/ustring.hxx>
+
+inline constexpr OUStringLiteral FMURL_FORM_POSITION = u".uno:FormController/positionForm";
+inline constexpr OUStringLiteral FMURL_FORM_RECORDCOUNT = u".uno:FormController/RecordCount";
+inline constexpr OUStringLiteral FMURL_RECORD_MOVEFIRST = u".uno:FormController/moveToFirst";
+inline constexpr OUStringLiteral FMURL_RECORD_MOVEPREV = u".uno:FormController/moveToPrev";
+inline constexpr OUStringLiteral FMURL_RECORD_MOVENEXT = u".uno:FormController/moveToNext";
+inline constexpr OUStringLiteral FMURL_RECORD_MOVELAST = u".uno:FormController/moveToLast";
+inline constexpr OUStringLiteral FMURL_RECORD_MOVETONEW = u".uno:FormController/moveToNew";
+inline constexpr OUStringLiteral FMURL_RECORD_UNDO = u".uno:FormController/undoRecord";
+inline constexpr OUStringLiteral FMURL_RECORD_SAVE = u".uno:FormController/saveRecord";
+inline constexpr OUStringLiteral FMURL_RECORD_DELETE = u".uno:FormController/deleteRecord";
+inline constexpr OUStringLiteral FMURL_FORM_REFRESH = u".uno:FormController/refreshForm";
+inline constexpr OUStringLiteral FMURL_FORM_REFRESH_CURRENT_CONTROL = u".uno:FormController/refreshCurrentControl";
+inline constexpr OUStringLiteral FMURL_FORM_SORT_UP = u".uno:FormController/sortUp";
+inline constexpr OUStringLiteral FMURL_FORM_SORT_DOWN = u".uno:FormController/sortDown";
+inline constexpr OUStringLiteral FMURL_FORM_SORT = u".uno:FormController/sort";
+inline constexpr OUStringLiteral FMURL_FORM_AUTO_FILTER = u".uno:FormController/autoFilter";
+inline constexpr OUStringLiteral FMURL_FORM_FILTER = u".uno:FormController/filter";
+inline constexpr OUStringLiteral FMURL_FORM_APPLY_FILTER = u".uno:FormController/applyFilter";
+inline constexpr OUStringLiteral FMURL_FORM_REMOVE_FILTER = u".uno:FormController/removeFilterOrder";
+inline constexpr OUStringLiteral FMURL_CONFIRM_DELETION = u".uno:FormSlots/ConfirmDeletion";
+inline constexpr OUStringLiteral FMURL_COMPONENT_FORMGRIDVIEW = u".component:DB/FormGridView";
+inline constexpr OUStringLiteral FMURL_GRIDVIEW_CLEARVIEW = u".uno:FormSlots/ClearView";
+inline constexpr OUStringLiteral FMURL_GRIDVIEW_ADDCOLUMN = u".uno:FormSlots/AddGridColumn";
+inline constexpr OUStringLiteral FMURL_GRIDVIEW_ATTACHTOFORM = u".uno:FormSlots/AttachToForm";
+inline constexpr OUStringLiteral FMARG_ATTACHTO_MASTERFORM = u"MasterForm";
+inline constexpr OUStringLiteral FMARG_ADDCOL_COLUMNTYPE = u"ColumnType";
+inline constexpr OUStringLiteral FMARG_ADDCOL_COLUMNPOS = u"ColumnPosition";
+
+#endif // INCLUDED_SVX_SOURCE_INC_FMURL_HXX
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/inc/fmvwimp.hxx b/svx/source/inc/fmvwimp.hxx
new file mode 100644
index 000000000..c7cb3681b
--- /dev/null
+++ b/svx/source/inc/fmvwimp.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_SVX_SOURCE_INC_FMVWIMP_HXX
+#define INCLUDED_SVX_SOURCE_INC_FMVWIMP_HXX
+
+#include <sal/config.h>
+
+#include <map>
+#include <memory>
+#include <set>
+#include <string_view>
+
+#include <svx/svdmark.hxx>
+#include <svx/svdobj.hxx>
+#include "fmdocumentclassification.hxx"
+
+#include <com/sun/star/form/XForm.hpp>
+#include <com/sun/star/container/XIndexAccess.hpp>
+#include <com/sun/star/form/runtime/XFormController.hpp>
+#include <com/sun/star/form/runtime/XFormControllerContext.hpp>
+#include <com/sun/star/container/XContainerListener.hpp>
+#include <com/sun/star/container/ContainerEvent.hpp>
+#include <com/sun/star/awt/XFocusListener.hpp>
+#include <com/sun/star/sdb/SQLErrorEvent.hpp>
+#include <com/sun/star/sdbc/XDataSource.hpp>
+#include <com/sun/star/uno/XComponentContext.hpp>
+
+#include <tools/link.hxx>
+#include <cppuhelper/implbase.hxx>
+#include <rtl/ref.hxx>
+#include <vcl/vclptr.hxx>
+
+class SdrPageWindow;
+
+class SdrObject;
+class FmFormObj;
+class FmFormModel;
+class FmFormView;
+class FmFormShell;
+namespace vcl { class Window; }
+class OutputDevice;
+class SdrUnoObj;
+struct ImplSVEvent;
+enum class SdrInventor : sal_uInt32;
+
+namespace com::sun::star {
+ namespace awt {
+ class XControl;
+ class XWindow;
+ }
+ namespace beans {
+ class XPropertySet;
+ }
+ namespace util {
+ class XNumberFormats;
+ }
+}
+
+class FmXFormView;
+
+namespace svx {
+ class ODataAccessDescriptor;
+ struct OXFormsDescriptor;
+}
+
+
+// FormViewPageWindowAdapter
+
+typedef ::cppu::WeakImplHelper < css::container::XIndexAccess
+ , css::form::runtime::XFormControllerContext
+ > FormViewPageWindowAdapter_Base;
+
+class FormViewPageWindowAdapter final : public FormViewPageWindowAdapter_Base
+{
+ friend class FmXFormView;
+
+ ::std::vector< css::uno::Reference< css::form::runtime::XFormController > > m_aControllerList;
+ css::uno::Reference< css::awt::XControlContainer > m_xControlContainer;
+ css::uno::Reference<css::uno::XComponentContext> m_xContext;
+ FmXFormView* m_pViewImpl;
+ VclPtr<vcl::Window> m_pWindow;
+
+public:
+ FormViewPageWindowAdapter( const css::uno::Reference<css::uno::XComponentContext>& _rContext,
+ const SdrPageWindow&, FmXFormView* pView);
+ //const SdrPageViewWinRec*, FmXFormView* pView);
+
+ // XElementAccess
+ virtual css::uno::Type SAL_CALL getElementType() override;
+ virtual sal_Bool SAL_CALL hasElements() override;
+
+ // XIndexAccess
+ virtual sal_Int32 SAL_CALL getCount() override;
+ virtual css::uno::Any SAL_CALL getByIndex(sal_Int32 Index) override;
+
+ // XFormControllerContext
+ virtual void SAL_CALL makeVisible( const css::uno::Reference< css::awt::XControl >& Control ) override;
+
+ const ::std::vector< css::uno::Reference< css::form::runtime::XFormController > >& GetList() const {return m_aControllerList;}
+
+private:
+ virtual ~FormViewPageWindowAdapter() override;
+
+ css::uno::Reference< css::form::runtime::XFormController > getController( const css::uno::Reference< css::form::XForm >& xForm ) const;
+ void setController(
+ const css::uno::Reference< css::form::XForm >& xForm,
+ const css::uno::Reference< css::form::runtime::XFormController >& _rxParentController );
+ const css::uno::Reference< css::awt::XControlContainer >& getControlContainer() const { return m_xControlContainer; }
+ void updateTabOrder( const css::uno::Reference< css::form::XForm >& _rxForm );
+ void dispose();
+ vcl::Window* getWindow() const {return m_pWindow;}
+};
+
+class SdrModel;
+
+class FmXFormView final : public ::cppu::WeakImplHelper<
+ css::form::XFormControllerListener,
+ css::awt::XFocusListener,
+ css::container::XContainerListener>
+{
+ friend class FmFormView;
+ friend class FmFormShell;
+ friend class FmXFormShell;
+ friend class FormViewPageWindowAdapter;
+ class ObjectRemoveListener;
+ friend class ObjectRemoveListener;
+
+ css::uno::Reference< css::awt::XWindow> m_xWindow;
+ css::uno::Reference< css::beans::XPropertySet > m_xLastCreatedControlModel;
+
+ FmFormObj* m_pMarkedGrid;
+ FmFormView* m_pView;
+ ImplSVEvent * m_nActivationEvent;
+ ImplSVEvent * m_nErrorMessageEvent; // event for an asynchronous error message. See also m_aAsyncError
+ ImplSVEvent * m_nAutoFocusEvent; // event for asynchronously setting the focus to a control
+ ImplSVEvent * m_nControlWizardEvent; // event for asynchronously setting the focus to a control
+
+ css::sdb::SQLErrorEvent
+ m_aAsyncError; // error event which is to be displayed asyn. See m_nErrorMessageEvent.
+
+ std::vector< rtl::Reference< FormViewPageWindowAdapter > >
+ m_aPageWindowAdapters; // to be filled in alive mode only
+ typedef ::std::set< css::uno::Reference< css::form::XForm > > SetOfForms;
+ std::map< css::uno::Reference< css::awt::XControlContainer >, SetOfForms >
+ m_aNeedTabOrderUpdate; // map control container to set of forms
+
+ // list of selected objects, used for restoration when switching from Alive to DesignMode
+ SdrMarkList m_aMark;
+ std::unique_ptr<ObjectRemoveListener>
+ m_pWatchStoredList;
+
+ bool m_bFirstActivation;
+ bool m_isTabOrderUpdateSuspended;
+
+ FmFormShell* GetFormShell() const;
+
+ css::uno::Reference<css::awt::XWindow> GetParentWindow() const;
+
+ FmXFormView( FmFormView* _pView );
+ virtual ~FmXFormView() override;
+
+ void saveMarkList();
+ void restoreMarkList( SdrMarkList& _rRestoredMarkList );
+ void stopMarkListWatching();
+ void startMarkListWatching();
+
+ void notifyViewDying( );
+ // notifies this impl class that the anti-impl instance (m_pView) is going to die
+
+public:
+ // UNO binding
+
+// css::lang::XEventListener
+ virtual void SAL_CALL disposing(const css::lang::EventObject& Source) override;
+
+// css::container::XContainerListener
+ virtual void SAL_CALL elementInserted(const css::container::ContainerEvent& rEvent) override;
+ virtual void SAL_CALL elementReplaced(const css::container::ContainerEvent& rEvent) override;
+ virtual void SAL_CALL elementRemoved(const css::container::ContainerEvent& rEvent) override;
+
+// css::form::XFormControllerListener
+ virtual void SAL_CALL formActivated(const css::lang::EventObject& rEvent) override;
+ virtual void SAL_CALL formDeactivated(const css::lang::EventObject& rEvent) override;
+
+ // XFocusListener
+ virtual void SAL_CALL focusGained( const css::awt::FocusEvent& e ) override;
+ virtual void SAL_CALL focusLost( const css::awt::FocusEvent& e ) override;
+
+ FmFormView* getView() const {return m_pView;}
+ rtl::Reference< FormViewPageWindowAdapter > findWindow( const css::uno::Reference< css::awt::XControlContainer >& _rxCC ) const;
+
+ css::uno::Reference< css::form::runtime::XFormController >
+ getFormController( const css::uno::Reference< css::form::XForm >& _rxForm, const OutputDevice& _rDevice ) const;
+
+ // activation handling
+ bool hasEverBeenActivated( ) const { return !m_bFirstActivation; }
+ void setHasBeenActivated( ) { m_bFirstActivation = false; }
+
+ void onFirstViewActivation( const FmFormModel* _pDocModel );
+
+ /** suspends the calls to activateTabOrder, which normally happen whenever for any ControlContainer of the view,
+ new controls are inserted. Cannot be nested, i.e. you need to call resumeTabOrderUpdate before calling
+ suspendTabOrderUpdate, again.
+ */
+ void suspendTabOrderUpdate();
+
+ /** resumes calls to activateTabOrder, and also does all pending calls which were collected since the last
+ suspendTabOrderUpdate call.
+ */
+ void resumeTabOrderUpdate();
+
+ void onCreatedFormObject( FmFormObj const & _rFormObject );
+
+ void breakCreateFormObject();
+
+ static bool
+ isFocusable( const css::uno::Reference< css::awt::XControl >& i_rControl );
+
+private:
+ //void addWindow(const SdrPageViewWinRec*);
+ void addWindow(const SdrPageWindow&);
+ void removeWindow( const css::uno::Reference< css::awt::XControlContainer >& _rxCC );
+ void Activate(bool bSync = false);
+ void Deactivate(bool bDeactivateController = true);
+
+ SdrObjectUniquePtr implCreateFieldControl( const svx::ODataAccessDescriptor& _rColumnDescriptor );
+ SdrObjectUniquePtr implCreateXFormsControl( const svx::OXFormsDescriptor &_rDesc );
+
+ static bool createControlLabelPair(
+ OutputDevice const & _rOutDev,
+ sal_Int32 _nXOffsetMM,
+ sal_Int32 _nYOffsetMM,
+ const css::uno::Reference< css::beans::XPropertySet >& _rxField,
+ const css::uno::Reference< css::util::XNumberFormats >& _rxNumberFormats,
+ SdrObjKind _nControlObjectID,
+ std::u16string_view _rFieldPostfix,
+ SdrInventor _nInventor,
+ SdrObjKind _nLabelObjectID,
+
+ // tdf#118963 Need a SdrModel for SdrObject creation. To make the
+ // demand clear, hand over a SdrMldel&
+ SdrModel& _rModel,
+
+ std::unique_ptr<SdrUnoObj, SdrObjectFreeOp>& _rpLabel,
+ std::unique_ptr<SdrUnoObj, SdrObjectFreeOp>& _rpControl
+ );
+
+ bool createControlLabelPair(
+ OutputDevice const & _rOutDev,
+ sal_Int32 _nXOffsetMM,
+ sal_Int32 _nYOffsetMM,
+ const css::uno::Reference< css::beans::XPropertySet >& _rxField,
+ const css::uno::Reference< css::util::XNumberFormats >& _rxNumberFormats,
+ SdrObjKind _nControlObjectID,
+ std::u16string_view _rFieldPostfix,
+ std::unique_ptr<SdrUnoObj, SdrObjectFreeOp>& _rpLabel,
+ std::unique_ptr<SdrUnoObj, SdrObjectFreeOp>& _rpControl,
+ const css::uno::Reference< css::sdbc::XDataSource >& _rxDataSource,
+ const OUString& _rDataSourceName,
+ const OUString& _rCommand,
+ const sal_Int32 _nCommandType
+ );
+
+ void ObjectRemovedInAliveMode(const SdrObject* pObject);
+
+ // asynchronously displays an error message. See also OnDelayedErrorMessage.
+ void displayAsyncErrorMessage( const css::sdb::SQLErrorEvent& _rEvent );
+
+ // cancels all pending async events
+ void cancelEvents();
+
+ /// the auto focus to the first (in terms of the tab order) control
+ void AutoFocus();
+ DECL_LINK( OnActivate, void*, void );
+ DECL_LINK( OnAutoFocus, void*, void );
+ DECL_LINK( OnDelayedErrorMessage, void*, void );
+ DECL_LINK( OnStartControlWizard, void*, void );
+
+private:
+ ::svxform::DocumentType impl_getDocumentType() const;
+};
+
+
+#endif // INCLUDED_SVX_SOURCE_INC_FMVWIMP_HXX
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/inc/formcontrolfactory.hxx b/svx/source/inc/formcontrolfactory.hxx
new file mode 100644
index 000000000..a95c51095
--- /dev/null
+++ b/svx/source/inc/formcontrolfactory.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_SVX_SOURCE_INC_FORMCONTROLFACTORY_HXX
+#define INCLUDED_SVX_SOURCE_INC_FORMCONTROLFACTORY_HXX
+
+#include "fmdocumentclassification.hxx"
+
+#include <com/sun/star/beans/XPropertySet.hpp>
+#include <com/sun/star/util/XNumberFormats.hpp>
+#include <com/sun/star/lang/XServiceInfo.hpp>
+#include <com/sun/star/uno/XComponentContext.hpp>
+#include <com/sun/star/container/XNameAccess.hpp>
+
+#include <string_view>
+
+class SdrUnoObj;
+namespace tools { class Rectangle; }
+
+namespace comphelper {
+ class ComponentContext;
+}
+
+
+namespace svxform
+{
+
+ class FormControlFactory
+ {
+ public:
+ FormControlFactory( const css::uno::Reference<css::uno::XComponentContext>& _rContext );
+ FormControlFactory();
+ ~FormControlFactory();
+
+ /** initializes the given control model which is to be newly inserted into a document
+
+ @param _eDocType
+ the type of the document which the control is to be inserted into
+ @param _rxControlModel
+ the control model to be inserted
+ @param _rControlBoundRect
+ the bound rect of the control, if applicable
+ @return
+ the class ID of the control
+ */
+ sal_Int16 initializeControlModel(
+ const DocumentType _eDocType,
+ const css::uno::Reference< css::beans::XPropertySet >& _rxControlModel,
+ const tools::Rectangle& _rControlBoundRect
+ );
+
+ sal_Int16 initializeControlModel( const DocumentType _eDocType, const SdrUnoObj& _rObject );
+ void initializeControlModel( const DocumentType _eDocType, const css::uno::Reference< css::beans::XPropertySet >& _rxControlModel );
+
+ void initializeTextFieldLineEnds(
+ const css::uno::Reference< css::beans::XPropertySet >& _rxModel
+ );
+
+ static void initializeFieldDependentProperties(
+ const css::uno::Reference< css::beans::XPropertySet >& _rxDatabaseField,
+ const css::uno::Reference< css::beans::XPropertySet >& _rxControlModel,
+ const css::uno::Reference< css::util::XNumberFormats >& _rxNumberFormats
+ );
+
+ static OUString getDefaultName(
+ const sal_Int16 nClassId,
+ const css::uno::Reference< css::lang::XServiceInfo >& _rxObject
+ );
+
+ static OUString getDefaultUniqueName_ByComponentType(
+ const css::uno::Reference< css::container::XNameAccess >& _rxContainer,
+ const css::uno::Reference< css::beans::XPropertySet >& _rxObject
+ );
+
+ static OUString getUniqueName(
+ const css::uno::Reference< css::container::XNameAccess >& _rxContainer,
+ std::u16string_view _rBaseName
+ );
+
+ private:
+ css::uno::Reference<css::uno::XComponentContext> m_xContext;
+ };
+
+
+}
+
+
+#endif // INCLUDED_SVX_SOURCE_INC_FORMCONTROLFACTORY_HXX
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/inc/formcontroller.hxx b/svx/source/inc/formcontroller.hxx
new file mode 100644
index 000000000..22e3bcb43
--- /dev/null
+++ b/svx/source/inc/formcontroller.hxx
@@ -0,0 +1,570 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+#ifndef INCLUDED_SVX_SOURCE_INC_FORMCONTROLLER_HXX
+#define INCLUDED_SVX_SOURCE_INC_FORMCONTROLLER_HXX
+
+#include <sal/config.h>
+
+#include <memory>
+#include <vector>
+
+#include "delayedevent.hxx"
+#include "fmcontrolbordermanager.hxx"
+#include "formdispatchinterceptor.hxx"
+#include "sqlparserclient.hxx"
+
+#include <com/sun/star/awt/XControl.hpp>
+#include <com/sun/star/awt/XControlModel.hpp>
+#include <com/sun/star/awt/XFocusListener.hpp>
+#include <com/sun/star/awt/XItemListener.hpp>
+#include <com/sun/star/awt/XMouseListener.hpp>
+#include <com/sun/star/awt/XTabController.hpp>
+#include <com/sun/star/awt/XTextComponent.hpp>
+#include <com/sun/star/awt/XWindow.hpp>
+#include <com/sun/star/container/XContainerListener.hpp>
+#include <com/sun/star/form/DatabaseParameterEvent.hpp>
+#include <com/sun/star/form/validation/XFormComponentValidityListener.hpp>
+#include <com/sun/star/form/XConfirmDeleteListener.hpp>
+#include <com/sun/star/form/XDatabaseParameterListener.hpp>
+#include <com/sun/star/form/runtime/XFormController.hpp>
+#include <com/sun/star/form/runtime/XFilterController.hpp>
+#include <com/sun/star/form/XFormControllerListener.hpp>
+#include <com/sun/star/form/XGridControlListener.hpp>
+#include <com/sun/star/form/XLoadListener.hpp>
+#include <com/sun/star/form/XResetListener.hpp>
+#include <com/sun/star/frame/XDispatch.hpp>
+#include <com/sun/star/frame/XDispatchProviderInterception.hpp>
+#include <com/sun/star/frame/XDispatchProviderInterceptor.hpp>
+#include <com/sun/star/lang/XServiceInfo.hpp>
+#include <com/sun/star/script/XEventAttacherManager.hpp>
+#include <com/sun/star/sdb/XRowSetApproveListener.hpp>
+#include <com/sun/star/sdb/XSingleSelectQueryComposer.hpp>
+#include <com/sun/star/sdb/XSQLErrorListener.hpp>
+#include <com/sun/star/sdbc/XRowSetListener.hpp>
+#include <com/sun/star/task/XInteractionHandler.hpp>
+#include <com/sun/star/util/XModifyListener.hpp>
+
+#include <comphelper/proparrhlp.hxx>
+#include <comphelper/interfacecontainer3.hxx>
+#include <cppuhelper/basemutex.hxx>
+#include <cppuhelper/propshlp.hxx>
+#include <rtl/ref.hxx>
+#include <vcl/timer.hxx>
+#include <vcl/idle.hxx>
+
+#include <cppuhelper/compbase.hxx>
+
+struct FmXTextComponentLess
+{
+ bool operator() (const css::uno::Reference< css::awt::XTextComponent >& x, const css::uno::Reference< css::awt::XTextComponent >& y) const
+ {
+ return reinterpret_cast<sal_Int64>(x.get()) < reinterpret_cast<sal_Int64>(y.get());
+ }
+};
+
+typedef ::std::map< css::uno::Reference< css::awt::XTextComponent >, OUString, FmXTextComponentLess> FmFilterRow;
+typedef ::std::vector< FmFilterRow > FmFilterRows;
+
+namespace svxform
+{
+ typedef ::std::vector< css::uno::Reference< css::awt::XTextComponent > > FilterComponents;
+ class ControlBorderManager;
+ struct FmFieldInfo;
+
+ typedef cppu::WeakComponentImplHelper < css::form::runtime::XFormController
+ , css::form::runtime::XFilterController
+ , css::awt::XFocusListener
+ , css::form::XLoadListener
+ , css::beans::XPropertyChangeListener
+ , css::awt::XTextListener
+ , css::awt::XItemListener
+ , css::container::XContainerListener
+ , css::util::XModifyListener
+ , css::form::XConfirmDeleteListener
+ , css::sdb::XSQLErrorListener
+ , css::sdbc::XRowSetListener
+ , css::sdb::XRowSetApproveListener
+ , css::form::XDatabaseParameterListener
+ , css::lang::XServiceInfo
+ , css::form::XResetListener
+ , css::frame::XDispatch
+ , css::awt::XMouseListener
+ , css::form::validation::XFormComponentValidityListener
+ , css::task::XInteractionHandler
+ , css::form::XGridControlListener
+ , css::form::runtime::XFeatureInvalidation
+ > FormController_BASE;
+
+ class ColumnInfoCache;
+ class FormController final : public ::cppu::BaseMutex
+ ,public FormController_BASE
+ ,public ::cppu::OPropertySetHelper
+ ,public DispatchInterceptor
+ ,public ::comphelper::OAggregationArrayUsageHelper< FormController >
+ ,public ::svxform::OSQLParserClient
+ {
+ typedef ::std::map < sal_Int16,
+ css::uno::Reference< css::frame::XDispatch >
+ > DispatcherContainer;
+
+ css::uno::Reference< css::uno::XAggregation> m_xAggregate;
+ css::uno::Reference< css::awt::XTabController> m_xTabController;
+ css::uno::Reference< css::awt::XControl> m_xActiveControl, m_xCurrentControl;
+ css::uno::Reference< css::container::XIndexAccess> m_xModelAsIndex;
+ css::uno::Reference< css::script::XEventAttacherManager> m_xModelAsManager;
+ css::uno::Reference< css::uno::XInterface> m_xParent;
+ css::uno::Reference< css::uno::XComponentContext> m_xComponentContext;
+ // Composer used for checking filter conditions
+ css::uno::Reference< css::sdb::XSingleSelectQueryComposer > m_xComposer;
+ css::uno::Reference< css::task::XInteractionHandler > m_xInteractionHandler;
+ css::uno::Reference< css::form::runtime::XFormControllerContext > m_xFormControllerContext;
+
+ css::uno::Sequence< css::uno::Reference< css::awt::XControl> > m_aControls;
+ ::comphelper::OInterfaceContainerHelper3<css::form::XFormControllerListener>
+ m_aActivateListeners;
+ ::comphelper::OInterfaceContainerHelper3<css::util::XModifyListener>
+ m_aModifyListeners;
+ ::comphelper::OInterfaceContainerHelper3<css::sdb::XSQLErrorListener>
+ m_aErrorListeners;
+ ::comphelper::OInterfaceContainerHelper3<css::form::XConfirmDeleteListener>
+ m_aDeleteListeners;
+ ::comphelper::OInterfaceContainerHelper3<css::sdb::XRowSetApproveListener>
+ m_aRowSetApproveListeners;
+ ::comphelper::OInterfaceContainerHelper3<css::form::XDatabaseParameterListener>
+ m_aParameterListeners;
+ ::comphelper::OInterfaceContainerHelper3<css::form::runtime::XFilterControllerListener>
+ m_aFilterListeners;
+
+ std::vector< css::uno::Reference< css::form::runtime::XFormController > >
+ m_aChildren;
+ FilterComponents m_aFilterComponents;
+ FmFilterRows m_aFilterRows;
+
+ Idle m_aTabActivationIdle;
+ Timer m_aFeatureInvalidationTimer;
+
+ ::svxform::ControlBorderManager
+ m_aControlBorderManager;
+
+ css::uno::Reference< css::form::runtime::XFormOperations >
+ m_xFormOperations;
+ DispatcherContainer m_aFeatureDispatchers;
+ ::std::set< sal_Int16 > m_aInvalidFeatures; // for asynchronous feature invalidation
+
+ OUString m_aMode;
+
+ ::svxform::DelayedEvent m_aLoadEvent;
+ ::svxform::DelayedEvent m_aToggleEvent;
+ ::svxform::DelayedEvent m_aActivationEvent;
+ ::svxform::DelayedEvent m_aDeactivationEvent;
+
+ ::std::unique_ptr< ColumnInfoCache >
+ m_pColumnInfoCache;
+
+ sal_Int32 m_nCurrentFilterPosition; // current level for filtering (or-criteria)
+
+ bool m_bCurrentRecordModified : 1;
+ bool m_bCurrentRecordNew : 1;
+ bool m_bLocked : 1;
+ bool m_bDBConnection : 1; // focus listener only for database forms
+ bool m_bCycle : 1;
+ bool m_bCanInsert : 1;
+ bool m_bCanUpdate : 1;
+ bool m_bCommitLock : 1; // lock the committing of controls see focusGained
+ bool m_bModified : 1; // is the content of a control modified?
+ bool m_bControlsSorted : 1;
+ bool m_bFiltering : 1;
+ bool m_bAttachEvents : 1;
+ bool m_bDetachEvents : 1;
+ bool m_bAttemptedHandlerCreation : 1;
+ bool m_bSuspendFilterTextListening; // no bit field, passed around as reference
+
+ // as we want to intercept dispatches of _all_ controls we're responsible for, and an object implementing
+ // the css::frame::XDispatchProviderInterceptor interface can intercept only _one_ objects dispatches, we need a helper class
+ std::vector<rtl::Reference<DispatchInterceptionMultiplexer>> m_aControlDispatchInterceptors;
+
+ public:
+ FormController( const css::uno::Reference< css::uno::XComponentContext > & _rxORB );
+
+ // returns the window which should be used as parent window for dialogs
+ static css::uno::Reference<css::awt::XWindow> getDialogParentWindow(css::uno::Reference<css::form::runtime::XFormController> xFormController);
+
+ private:
+ virtual ~FormController() override;
+
+ // XInterface
+ virtual css::uno::Any SAL_CALL queryInterface( const css::uno::Type& type) override;
+ virtual void SAL_CALL acquire() noexcept override;
+ virtual void SAL_CALL release() noexcept override;
+
+ // XTypeProvider
+ virtual css::uno::Sequence< sal_Int8 > SAL_CALL getImplementationId() override;
+ virtual css::uno::Sequence< css::uno::Type > SAL_CALL getTypes( ) override;
+
+ // XDispatch
+ virtual void SAL_CALL dispatch( const css::util::URL& _rURL, const css::uno::Sequence< css::beans::PropertyValue >& _rArgs ) override;
+ virtual void SAL_CALL addStatusListener( const css::uno::Reference< css::frame::XStatusListener >& _rxListener, const css::util::URL& _rURL ) override;
+ virtual void SAL_CALL removeStatusListener( const css::uno::Reference< css::frame::XStatusListener >& _rxListener, const css::util::URL& _rURL ) override;
+
+ // css::container::XChild
+ virtual css::uno::Reference< css::uno::XInterface> SAL_CALL getParent() override;
+ virtual void SAL_CALL setParent(const css::uno::Reference< css::uno::XInterface>& Parent) override;
+
+ // css::lang::XEventListener
+ virtual void SAL_CALL disposing(const css::lang::EventObject& Source) override;
+
+ // OComponentHelper
+ virtual void SAL_CALL disposing() override;
+
+ // OPropertySetHelper
+ virtual sal_Bool SAL_CALL convertFastPropertyValue( css::uno::Any & rConvertedValue, css::uno::Any & rOldValue,
+ sal_Int32 nHandle, const css::uno::Any& rValue ) override;
+
+ virtual void SAL_CALL setFastPropertyValue_NoBroadcast( sal_Int32 nHandle, const css::uno::Any& rValue ) override;
+ virtual void SAL_CALL getFastPropertyValue( css::uno::Any& rValue, sal_Int32 nHandle ) const override;
+
+ virtual css::uno::Reference< css::beans::XPropertySetInfo> SAL_CALL getPropertySetInfo() override;
+ virtual ::cppu::IPropertyArrayHelper & SAL_CALL getInfoHelper() override;
+
+ using OPropertySetHelper::getFastPropertyValue;
+
+ // XFilterController
+ virtual ::sal_Int32 SAL_CALL getFilterComponents() override;
+ virtual ::sal_Int32 SAL_CALL getDisjunctiveTerms() override;
+ virtual void SAL_CALL addFilterControllerListener( const css::uno::Reference< css::form::runtime::XFilterControllerListener >& Listener ) override;
+ virtual void SAL_CALL removeFilterControllerListener( const css::uno::Reference< css::form::runtime::XFilterControllerListener >& Listener ) override;
+ virtual void SAL_CALL setPredicateExpression( ::sal_Int32 Component, ::sal_Int32 Term, const OUString& PredicateExpression ) override;
+ virtual css::uno::Reference< css::awt::XControl > SAL_CALL getFilterComponent( ::sal_Int32 Component ) override;
+ virtual css::uno::Sequence< css::uno::Sequence< OUString > > SAL_CALL getPredicateExpressions() override;
+ virtual void SAL_CALL removeDisjunctiveTerm( ::sal_Int32 Term ) override;
+ virtual void SAL_CALL appendEmptyDisjunctiveTerm() override;
+ virtual ::sal_Int32 SAL_CALL getActiveTerm() override;
+ virtual void SAL_CALL setActiveTerm( ::sal_Int32 ActiveTerm ) override;
+
+ // XElementAccess
+ virtual css::uno::Type SAL_CALL getElementType() override;
+ virtual sal_Bool SAL_CALL hasElements() override;
+
+ // css::container::XEnumerationAccess
+ virtual css::uno::Reference< css::container::XEnumeration> SAL_CALL createEnumeration() override;
+
+ // css::container::XContainerListener
+ virtual void SAL_CALL elementInserted(const css::container::ContainerEvent& rEvent) override;
+ virtual void SAL_CALL elementReplaced(const css::container::ContainerEvent& rEvent) override;
+ virtual void SAL_CALL elementRemoved(const css::container::ContainerEvent& rEvent) override;
+
+ // XLoadListener
+ virtual void SAL_CALL loaded(const css::lang::EventObject& rEvent) override;
+ virtual void SAL_CALL unloaded(const css::lang::EventObject& rEvent) override;
+ virtual void SAL_CALL unloading(const css::lang::EventObject& aEvent) override;
+ virtual void SAL_CALL reloading(const css::lang::EventObject& aEvent) override;
+ virtual void SAL_CALL reloaded(const css::lang::EventObject& aEvent) override;
+
+ // XModeSelector
+ virtual void SAL_CALL setMode(const OUString& Mode) override;
+ virtual OUString SAL_CALL getMode() override;
+ virtual css::uno::Sequence< OUString > SAL_CALL getSupportedModes() override;
+ virtual sal_Bool SAL_CALL supportsMode(const OUString& Mode) override;
+
+ // css::container::XIndexAccess
+ virtual sal_Int32 SAL_CALL getCount() override;
+ virtual css::uno::Any SAL_CALL getByIndex(sal_Int32 Index) override;
+
+ // XModifyBroadcaster
+ virtual void SAL_CALL addModifyListener(const css::uno::Reference< css::util::XModifyListener>& l) override;
+ virtual void SAL_CALL removeModifyListener(const css::uno::Reference< css::util::XModifyListener>& l) override;
+
+ // XFocusListener
+ virtual void SAL_CALL focusGained(const css::awt::FocusEvent& e) override;
+ virtual void SAL_CALL focusLost(const css::awt::FocusEvent& e) override;
+
+ // XMouseListener
+ virtual void SAL_CALL mousePressed( const css::awt::MouseEvent& _rEvent ) override;
+ virtual void SAL_CALL mouseReleased( const css::awt::MouseEvent& _rEvent ) override;
+ virtual void SAL_CALL mouseEntered( const css::awt::MouseEvent& _rEvent ) override;
+ virtual void SAL_CALL mouseExited( const css::awt::MouseEvent& _rEvent ) override;
+
+ // XFormComponentValidityListener
+ virtual void SAL_CALL componentValidityChanged( const css::lang::EventObject& _rSource ) override;
+
+ // XInteractionHandler
+ virtual void SAL_CALL handle( const css::uno::Reference< css::task::XInteractionRequest >& Request ) override;
+
+ // XGridControlListener
+ virtual void SAL_CALL columnChanged( const css::lang::EventObject& _event ) override;
+
+ // css::beans::XPropertyChangeListener -> change of states
+ virtual void SAL_CALL propertyChange(const css::beans::PropertyChangeEvent& evt) override;
+
+ // XTextListener -> set modify
+ virtual void SAL_CALL textChanged(const css::awt::TextEvent& rEvent) override;
+
+ // XItemListener -> set modify
+ virtual void SAL_CALL itemStateChanged(const css::awt::ItemEvent& rEvent) override;
+
+ // XModifyListener -> set modify
+ virtual void SAL_CALL modified(const css::lang::EventObject& rEvent) override;
+
+ // XFormController
+ virtual css::uno::Reference< css::form::runtime::XFormOperations > SAL_CALL getFormOperations() override;
+ virtual css::uno::Reference< css::awt::XControl> SAL_CALL getCurrentControl() override;
+ virtual void SAL_CALL addActivateListener(const css::uno::Reference< css::form::XFormControllerListener>& l) override;
+ virtual void SAL_CALL removeActivateListener(const css::uno::Reference< css::form::XFormControllerListener>& l) override;
+ virtual void SAL_CALL addChildController( const css::uno::Reference< css::form::runtime::XFormController >& ChildController ) override;
+
+ virtual css::uno::Reference< css::form::runtime::XFormControllerContext > SAL_CALL getContext() override;
+ virtual void SAL_CALL setContext( const css::uno::Reference< css::form::runtime::XFormControllerContext >& _context ) override;
+ virtual css::uno::Reference< css::task::XInteractionHandler > SAL_CALL getInteractionHandler() override;
+ virtual void SAL_CALL setInteractionHandler( const css::uno::Reference< css::task::XInteractionHandler >& _interactionHandler ) override;
+
+ // XTabController
+ virtual css::uno::Sequence< css::uno::Reference< css::awt::XControl> > SAL_CALL getControls() override;
+
+ virtual void SAL_CALL setModel(const css::uno::Reference< css::awt::XTabControllerModel>& Model) override;
+ virtual css::uno::Reference< css::awt::XTabControllerModel> SAL_CALL getModel() override;
+
+ virtual void SAL_CALL setContainer(const css::uno::Reference< css::awt::XControlContainer>& Container) override;
+ virtual css::uno::Reference< css::awt::XControlContainer> SAL_CALL getContainer() override;
+
+ virtual void SAL_CALL autoTabOrder() override;
+ virtual void SAL_CALL activateTabOrder() override;
+
+ virtual void SAL_CALL activateFirst() override;
+ virtual void SAL_CALL activateLast() override;
+
+ // css::sdbc::XRowSetListener
+ virtual void SAL_CALL cursorMoved(const css::lang::EventObject& event) override;
+ virtual void SAL_CALL rowChanged(const css::lang::EventObject& event) override;
+ virtual void SAL_CALL rowSetChanged(const css::lang::EventObject& event) override;
+
+ // XRowSetApproveListener
+ virtual sal_Bool SAL_CALL approveCursorMove(const css::lang::EventObject& event) override;
+ virtual sal_Bool SAL_CALL approveRowChange(const css::sdb::RowChangeEvent& event) override;
+ virtual sal_Bool SAL_CALL approveRowSetChange(const css::lang::EventObject& event) override;
+
+ // XRowSetApproveBroadcaster
+ virtual void SAL_CALL addRowSetApproveListener(const css::uno::Reference< css::sdb::XRowSetApproveListener>& listener) override;
+ virtual void SAL_CALL removeRowSetApproveListener(const css::uno::Reference< css::sdb::XRowSetApproveListener>& listener) override;
+
+ // XSQLErrorBroadcaster
+ virtual void SAL_CALL errorOccured(const css::sdb::SQLErrorEvent& aEvent) override;
+
+ // XSQLErrorListener
+ virtual void SAL_CALL addSQLErrorListener(const css::uno::Reference< css::sdb::XSQLErrorListener>& _rListener) override;
+ virtual void SAL_CALL removeSQLErrorListener(const css::uno::Reference< css::sdb::XSQLErrorListener>& _rListener) override;
+
+ // XDatabaseParameterBroadcaster2
+ virtual void SAL_CALL addDatabaseParameterListener(const css::uno::Reference< css::form::XDatabaseParameterListener>& aListener) override;
+ virtual void SAL_CALL removeDatabaseParameterListener(const css::uno::Reference< css::form::XDatabaseParameterListener>& aListener) override;
+
+ // XDatabaseParameterBroadcaster
+ virtual void SAL_CALL addParameterListener(const css::uno::Reference< css::form::XDatabaseParameterListener>& aListener) override;
+ virtual void SAL_CALL removeParameterListener(const css::uno::Reference< css::form::XDatabaseParameterListener>& aListener) override;
+
+ // XDatabaseParameterListener
+ virtual sal_Bool SAL_CALL approveParameter(const css::form::DatabaseParameterEvent& aEvent) override;
+
+ // XConfirmDeleteBroadcaster
+ virtual void SAL_CALL addConfirmDeleteListener(const css::uno::Reference< css::form::XConfirmDeleteListener>& aListener) override;
+ virtual void SAL_CALL removeConfirmDeleteListener(const css::uno::Reference< css::form::XConfirmDeleteListener>& aListener) override;
+
+ // XConfirmDeleteListener
+ virtual sal_Bool SAL_CALL confirmDelete(const css::sdb::RowChangeEvent& aEvent) override;
+
+ // XServiceInfo
+ virtual sal_Bool SAL_CALL supportsService(const OUString& ServiceName) override;
+ virtual OUString SAL_CALL getImplementationName() override;
+ virtual css::uno::Sequence< OUString > SAL_CALL getSupportedServiceNames() override;
+
+ // XResetListener
+ virtual sal_Bool SAL_CALL approveReset(const css::lang::EventObject& rEvent) override;
+ virtual void SAL_CALL resetted(const css::lang::EventObject& rEvent) override;
+
+ // XFeatureInvalidation
+ virtual void SAL_CALL invalidateFeatures( const css::uno::Sequence< ::sal_Int16 >& Features ) override;
+ virtual void SAL_CALL invalidateAllFeatures( ) override;
+
+// method for registration
+ static css::uno::Sequence< OUString > const & getSupportedServiceNames_Static();
+
+ // comphelper::OPropertyArrayUsageHelper
+ virtual void fillProperties(
+ css::uno::Sequence< css::beans::Property >& /* [out] */ _rProps,
+ css::uno::Sequence< css::beans::Property >& /* [out] */ _rAggregateProps
+ ) const override;
+
+ // DispatchInterceptor
+ virtual css::uno::Reference< css::frame::XDispatch>
+ interceptedQueryDispatch(
+ const css::util::URL& aURL,
+ const OUString& aTargetFrameName,
+ sal_Int32 nSearchFlags
+ ) override;
+
+ virtual ::osl::Mutex* getInterceptorMutex() override { return &m_aMutex; }
+
+ /// update all our dispatchers
+ void updateAllDispatchers() const;
+
+ /** disposes all dispatchers in m_aFeatureDispatchers, and empties m_aFeatureDispatchers
+ */
+ void disposeAllFeaturesAndDispatchers();
+
+ void startFiltering();
+ void stopFiltering();
+ void setFilter(::std::vector<FmFieldInfo>&);
+ void startListening();
+ void stopListening();
+
+ /** ensures that we have an interaction handler, if possible
+
+ If an interaction handler was provided at creation time (<member>initialize</member>), this
+ one will be used. Else, an attempt is made to create an <type scope="css::sdb">InteractionHandler</type>
+ is made.
+
+ @return <TRUE/>
+ if and only if <member>m_xInteractionHandler</member> is valid when the method returns
+ */
+ bool ensureInteractionHandler();
+
+ /** replaces one of our controls with another one
+
+ Upon successful replacing, the old control will be disposed. Also, internal members pointing
+ to the current or active control will be adjusted. Yet more, if the replaced control was
+ the active control, the new control will be made active.
+
+ @param _rxExistentControl
+ The control to replace. Must be one of the controls in our ControlContainer.
+ @param _rxNewControl
+ The control which should replace the existent control.
+ @return
+ <TRUE/> if and only if the control was successfully replaced
+ */
+ bool replaceControl(
+ const css::uno::Reference< css::awt::XControl >& _rxExistentControl,
+ const css::uno::Reference< css::awt::XControl >& _rxNewControl
+ );
+
+ // we're listening at all bound controls for modifications
+ void startControlModifyListening(const css::uno::Reference< css::awt::XControl>& xControl);
+ void stopControlModifyListening(const css::uno::Reference< css::awt::XControl>& xControl);
+
+ void setLocks();
+ void setControlLock(const css::uno::Reference< css::awt::XControl>& xControl);
+ void addToEventAttacher(const css::uno::Reference< css::awt::XControl>& xControl);
+ void removeFromEventAttacher(const css::uno::Reference< css::awt::XControl>& xControl);
+ void toggleAutoFields(bool bAutoFields);
+ /// @throws css::uno::RuntimeException
+ void unload();
+ void removeBoundFieldListener();
+
+ void startFormListening( const css::uno::Reference< css::beans::XPropertySet >& _rxForm, bool _bPropertiesOnly );
+ void stopFormListening( const css::uno::Reference< css::beans::XPropertySet >& _rxForm, bool _bPropertiesOnly );
+
+ css::uno::Reference< css::awt::XControl> findControl( css::uno::Sequence< css::uno::Reference< css::awt::XControl> >& rCtrls, const css::uno::Reference< css::awt::XControlModel>& rxCtrlModel, bool _bRemove, bool _bOverWrite ) const;
+
+ void insertControl(const css::uno::Reference< css::awt::XControl>& xControl);
+ void removeControl(const css::uno::Reference< css::awt::XControl>& xControl);
+
+ /// called when a new control is to be handled by the controller
+ void implControlInserted( const css::uno::Reference< css::awt::XControl>& _rxControl, bool _bAddToEventAttacher );
+ /// called when a control is not to be handled by the controller anymore
+ void implControlRemoved( const css::uno::Reference< css::awt::XControl>& _rxControl, bool _bRemoveFromEventAttacher );
+
+ /** sets m_xCurrentControl, plus does administrative tasks depending on it
+ */
+ void implSetCurrentControl( const css::uno::Reference< css::awt::XControl >& _rxControl );
+
+ /** invalidates the FormFeatures which depend on the current control
+ */
+ void implInvalidateCurrentControlDependentFeatures();
+
+ bool impl_isDisposed_nofail() const { return FormController_BASE::rBHelper.bDisposed; }
+ void impl_checkDisposed_throw() const;
+
+ void impl_onModify();
+
+ /** adds the given filter row to m_aFilterRows, setting m_nCurrentFilterPosition to 0 if the newly added
+ row is the first one.
+
+ @precond
+ our mutex is locked
+ */
+ void impl_addFilterRow( const FmFilterRow& _row );
+
+ /** adds an empty filter row to m_aFilterRows, and notifies our listeners
+ */
+ void impl_appendEmptyFilterRow( ::osl::ClearableMutexGuard& _rClearBeforeNotify );
+
+ bool isLocked() const {return m_bLocked;}
+ bool determineLockState() const;
+
+ css::uno::Reference< css::frame::XDispatchProviderInterceptor> createInterceptor(const css::uno::Reference< css::frame::XDispatchProviderInterception>& _xInterception);
+ // create a new interceptor, register it on the given object
+ void deleteInterceptor(const css::uno::Reference< css::frame::XDispatchProviderInterception>& _xInterception);
+ // if createInterceptor was called for the given object the according interceptor will be removed
+ // from the objects interceptor chain and released
+
+ /** checks all form controls belonging to our form for validity
+
+ If a form control supports the XValidatableFormComponent interface, this is used to determine
+ the validity of the control. If the interface is not supported, the control is supposed to be
+ valid.
+
+ @param _rFirstInvalidityExplanation
+ if the method returns <FALSE/> (i.e. if there is an invalid control), this string contains
+ the explanation for the invalidity, as obtained from the validator.
+
+ @param _rxFirstInvalidModel
+ if the method returns <FALSE/> (i.e. if there is an invalid control), this contains
+ the control model
+
+ @return
+ <TRUE/> if and only if all controls belonging to our form are valid
+ */
+ bool checkFormComponentValidity(
+ OUString& /* [out] */ _rFirstInvalidityExplanation,
+ css::uno::Reference< css::awt::XControlModel >& /* [out] */ _rxFirstInvalidModel
+ );
+
+ /** locates the control which belongs to a given model
+ */
+ css::uno::Reference< css::awt::XControl >
+ locateControl( const css::uno::Reference< css::awt::XControlModel >& _rxModel );
+
+ // set the text for all filters
+ void impl_setTextOnAllFilter_throw();
+
+ // in filter mode we do not listen for changes
+ bool isListeningForChanges() const {return m_bDBConnection && !m_bFiltering && !isLocked();}
+ css::uno::Reference< css::awt::XControl> isInList(const css::uno::Reference< css::awt::XWindowPeer>& xPeer) const;
+
+ DECL_LINK( OnActivateTabOrder, Timer*, void );
+ DECL_LINK( OnInvalidateFeatures, Timer*, void );
+ DECL_LINK( OnLoad, void*, void );
+ DECL_LINK( OnToggleAutoFields, void*, void );
+ DECL_LINK( OnActivated, void*, void );
+ DECL_LINK( OnDeactivated, void*, void );
+ };
+
+}
+
+#endif // INCLUDED_SVX_SOURCE_INC_FORMCONTROLLER_HXX
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/inc/formcontrolling.hxx b/svx/source/inc/formcontrolling.hxx
new file mode 100644
index 000000000..c9bd8f23f
--- /dev/null
+++ b/svx/source/inc/formcontrolling.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_SVX_SOURCE_INC_FORMCONTROLLING_HXX
+#define INCLUDED_SVX_SOURCE_INC_FORMCONTROLLING_HXX
+
+#include <com/sun/star/form/runtime/XFormController.hpp>
+#include <com/sun/star/form/runtime/FeatureState.hpp>
+#include <com/sun/star/form/runtime/XFormOperations.hpp>
+#include <com/sun/star/sdb/XSQLErrorListener.hpp>
+
+#include <cppuhelper/implbase.hxx>
+#include <rtl/ref.hxx>
+
+#include <vector>
+
+
+namespace svx
+{
+
+ class FeatureSlotTranslation
+ {
+ public:
+ /// retrieves the feature id for a given feature URL
+ static sal_Int32 getControllerFeatureSlotIdForURL( const OUString& _rMainURL );
+
+ /// retrieves the css.form.runtime.FormFeature ID for a given slot ID
+ static sal_Int16 getFormFeatureForSlotId( sal_Int32 _nSlotId );
+
+ /// retrieves the slot id for a given css.form.runtime.FormFeature ID
+ static sal_Int32 getSlotIdForFormFeature( sal_Int16 _nFormFeature );
+ };
+
+ class IControllerFeatureInvalidation
+ {
+ public:
+ /** invalidates the given features
+
+ Invalidation means that any user interface representation (such as toolbox buttons), or
+ any dispatches associated with the features in question are potentially out-of-date, and
+ need to be updated
+
+ @param _rFeatures
+ Ids of the features to be invalidated.
+ */
+ virtual void invalidateFeatures( const ::std::vector< sal_Int32 >& _rFeatures ) = 0;
+
+ protected:
+ ~IControllerFeatureInvalidation() {}
+ };
+
+ class FormControllerHelper;
+ /** easier access to a FormControllerHelper instance
+ */
+ class ControllerFeatures final
+ {
+ IControllerFeatureInvalidation* m_pInvalidationCallback; // necessary as long as m_pImpl is not yet constructed
+ rtl::Reference<FormControllerHelper> m_pImpl;
+
+ public:
+ /** standard ctor
+
+ The instance is not functional until <method>assign</method> is used.
+
+ @param _pInvalidationCallback
+ the callback for invalidating feature states
+ */
+ ControllerFeatures(
+ IControllerFeatureInvalidation* _pInvalidationCallback
+ );
+
+ /** constructs the instance from a <type scope="css::form::runtime">XFormController<type> instance
+
+ @param _rxController
+ The form controller which the helper should be responsible for. Must not
+ be <NULL/>, and must have a valid model (form).
+ */
+ ControllerFeatures(
+ const css::uno::Reference< css::form::runtime::XFormController >& _rxController
+ );
+
+ /// dtor
+ ~ControllerFeatures();
+
+ /// checks whether the instance is properly assigned to a form and/or controller
+ bool isAssigned( ) const { return m_pImpl != nullptr; }
+
+ /** assign to a controller
+ */
+ void assign(
+ const css::uno::Reference< css::form::runtime::XFormController >& _rxController
+ );
+
+ /// clears the instance so that it cannot be used afterwards
+ void dispose();
+
+ // access to the instance which implements the functionality. Not to be used when not assigned
+ const FormControllerHelper* operator->() const { return m_pImpl.get(); }
+ FormControllerHelper* operator->() { return m_pImpl.get(); }
+ };
+
+
+ //= FormControllerHelper
+
+ typedef ::cppu::WeakImplHelper < css::form::runtime::XFeatureInvalidation
+ , css::sdb::XSQLErrorListener
+ > FormControllerHelper_Base;
+ /** is a helper class which manages form controller functionality (such as moveNext etc.).
+
+ <p>The class helps implementing form controller functionality, by providing
+ methods to determine the state of, and execute, various common form features.<br/>
+ A <em>feature</em> is for instance moving the form associated with the controller
+ to a certain position, or reloading the form, and so on.</p>
+ */
+ class FormControllerHelper final : public FormControllerHelper_Base
+ {
+ IControllerFeatureInvalidation* m_pInvalidationCallback;
+ css::uno::Reference< css::form::runtime::XFormOperations >
+ m_xFormOperations;
+
+ css::uno::Any m_aOperationError;
+
+ public:
+ /** constructs the helper from a <type scope="css::form::runtime">XFormController<type> instance
+
+ @param _rxController
+ The form controller which the helper should be responsible for. Must not
+ be <NULL/>, and must have a valid model (form).
+ @param _pInvalidationCallback
+ the callback for invalidating feature states
+ */
+ FormControllerHelper(
+ const css::uno::Reference< css::form::runtime::XFormController >& _rxController,
+ IControllerFeatureInvalidation* _pInvalidationCallback
+ );
+
+ // forwards to the XFormOperations implementation
+ css::uno::Reference< css::sdbc::XRowSet >
+ getCursor() const;
+ void getState(
+ sal_Int32 _nSlotId,
+ css::form::runtime::FeatureState& _out_rState
+ ) const;
+ bool isEnabled( sal_Int32 _nSlotId ) const;
+ void execute( sal_Int32 _nSlotId ) const;
+ void execute( sal_Int32 _nSlotId, const OUString& _rParamName, const css::uno::Any& _rParamValue ) const;
+ bool commitCurrentRecord() const;
+ bool commitCurrentControl( ) const;
+ bool isInsertionRow() const;
+ bool isModifiedRow() const;
+
+ bool canDoFormFilter() const;
+
+ /** disposes this instance.
+
+ After this method has been called, the instance is not functional anymore
+ */
+ void dispose();
+
+ private:
+ /// dtor
+ virtual ~FormControllerHelper() override;
+
+ // XFeatureInvalidation
+ virtual void SAL_CALL invalidateFeatures( const css::uno::Sequence< ::sal_Int16 >& Features ) override;
+ virtual void SAL_CALL invalidateAllFeatures() override;
+
+ // XSQLErrorListener
+ virtual void SAL_CALL errorOccured( const css::sdb::SQLErrorEvent& Event ) override;
+
+ // XEventListener
+ virtual void SAL_CALL disposing( const css::lang::EventObject& Source ) override;
+
+ enum FormOperation { EXECUTE, EXECUTE_ARGS, COMMIT_CONTROL, COMMIT_RECORD };
+
+ bool impl_operateForm_nothrow(
+ const FormOperation _eWhat,
+ const sal_Int16 _nFeature, /* ignore for COMMIT_* */
+ const css::uno::Sequence< css::beans::NamedValue >& _rArguments /* ignore except for EXECUTE_ARGS */
+ ) const;
+ bool impl_operateForm_nothrow( const FormOperation _eWhat ) const
+ {
+ return impl_operateForm_nothrow( _eWhat, 0, css::uno::Sequence< css::beans::NamedValue >() );
+ }
+
+ private:
+ FormControllerHelper( const FormControllerHelper& ) = delete;
+ FormControllerHelper& operator=( const FormControllerHelper& ) = delete;
+ };
+
+
+}
+
+
+#endif // INCLUDED_SVX_SOURCE_INC_FORMCONTROLLING_HXX
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/inc/formdispatchinterceptor.hxx b/svx/source/inc/formdispatchinterceptor.hxx
new file mode 100644
index 000000000..d2947eec5
--- /dev/null
+++ b/svx/source/inc/formdispatchinterceptor.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_SVX_SOURCE_INC_FORMDISPATCHINTERCEPTOR_HXX
+#define INCLUDED_SVX_SOURCE_INC_FORMDISPATCHINTERCEPTOR_HXX
+
+#include <com/sun/star/frame/XDispatchProviderInterceptor.hpp>
+#include <com/sun/star/frame/XDispatchProviderInterception.hpp>
+
+#include <cppuhelper/compbase.hxx>
+
+
+namespace svxform
+{
+
+ class DispatchInterceptor
+ {
+ public:
+ /// @throws css::uno::RuntimeException
+ virtual css::uno::Reference< css::frame::XDispatch> interceptedQueryDispatch(
+ const css::util::URL& aURL, const OUString& aTargetFrameName, sal_Int32 nSearchFlags) = 0;
+
+ virtual ::osl::Mutex* getInterceptorMutex() = 0;
+
+ protected:
+ DispatchInterceptor() {}
+
+ ~DispatchInterceptor() {}
+ };
+
+
+ //=
+
+ typedef ::cppu::WeakComponentImplHelper< css::frame::XDispatchProviderInterceptor
+ , css::lang::XEventListener
+ > DispatchInterceptionMultiplexer_BASE;
+
+ class DispatchInterceptionMultiplexer final : public DispatchInterceptionMultiplexer_BASE
+ {
+ public:
+ css::uno::Reference< css::frame::XDispatchProviderInterception> getIntercepted() const { return m_xIntercepted; }
+
+ DispatchInterceptionMultiplexer(
+ const css::uno::Reference< css::frame::XDispatchProviderInterception>& _rToIntercept,
+ DispatchInterceptor* _pMaster
+ );
+
+ // css::frame::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;
+
+ // css::frame::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;
+
+ // css::lang::XEventListener
+ virtual void SAL_CALL disposing( const css::lang::EventObject& Source ) override;
+
+ // OComponentHelper
+ virtual void SAL_CALL disposing() override;
+
+ private:
+ virtual ~DispatchInterceptionMultiplexer() override;
+
+ void ImplDetach();
+
+ ::osl::Mutex m_aFallback;
+ ::osl::Mutex* m_pMutex;
+
+ // the component which's dispatches we're intercepting
+ css::uno::WeakReference< css::frame::XDispatchProviderInterception >
+ m_xIntercepted;
+ bool m_bListening;
+
+ // the real interceptor
+ DispatchInterceptor* m_pMaster;
+
+ // chaining
+ css::uno::Reference< css::frame::XDispatchProvider> m_xSlaveDispatcher;
+ css::uno::Reference< css::frame::XDispatchProvider> m_xMasterDispatcher;
+ };
+
+
+}
+
+
+#endif // INCLUDED_SVX_SOURCE_INC_FORMDISPATCHINTERCEPTOR_HXX
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/inc/formfeaturedispatcher.hxx b/svx/source/inc/formfeaturedispatcher.hxx
new file mode 100644
index 000000000..a57453dc8
--- /dev/null
+++ b/svx/source/inc/formfeaturedispatcher.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_SVX_SOURCE_INC_FORMFEATUREDISPATCHER_HXX
+#define INCLUDED_SVX_SOURCE_INC_FORMFEATUREDISPATCHER_HXX
+
+#include <com/sun/star/frame/XDispatch.hpp>
+#include <com/sun/star/form/runtime/XFormOperations.hpp>
+
+#include <cppuhelper/implbase.hxx>
+#include <comphelper/interfacecontainer3.hxx>
+
+
+namespace svx
+{
+
+ typedef ::cppu::WeakImplHelper < css::frame::XDispatch
+ > OSingleFeatureDispatcher_Base;
+
+ class OSingleFeatureDispatcher final : public OSingleFeatureDispatcher_Base
+ {
+ public:
+ /** constructs the dispatcher
+
+ @param _rFeatureURL
+ the URL of the feature which this instance is responsible for
+
+ @param _nFeatureId
+ the feature which this instance is responsible for
+
+ @param _rController
+ the controller which is responsible for providing the state of feature of this instance,
+ and for executing it. After disposing the dispatcher instance, the controller will
+ not be accessed anymore
+
+ @see dispose
+ */
+ OSingleFeatureDispatcher(
+ const css::util::URL& _rFeatureURL,
+ const sal_Int16 _nFormFeature,
+ const css::uno::Reference< css::form::runtime::XFormOperations >& _rxFormOperations,
+ ::osl::Mutex& _rMutex
+ );
+
+ /** notifies all our listeners of the current state
+ */
+ void updateAllListeners();
+
+ private:
+ // XDispatch
+ virtual void SAL_CALL dispatch( const css::util::URL& _rURL, const css::uno::Sequence< css::beans::PropertyValue >& _rArguments ) override;
+ virtual void SAL_CALL addStatusListener( const css::uno::Reference< css::frame::XStatusListener >& _rxControl, const css::util::URL& _rURL ) override;
+ virtual void SAL_CALL removeStatusListener( const css::uno::Reference< css::frame::XStatusListener >& _rxControl, const css::util::URL& _rURL ) override;
+
+ /** notifies our current state to one or all listeners
+
+ @param _rxListener
+ the listener to notify. May be NULL, in this case all our listeners will be
+ notified with the current state
+
+ @param _rFreeForNotification
+ a guard which currently locks our mutex, and which is to be cleared
+ for actually doing the notification(s)
+ */
+ void notifyStatus(
+ const css::uno::Reference< css::frame::XStatusListener >& _rxListener,
+ ::osl::ClearableMutexGuard& _rFreeForNotification
+ );
+
+ /** retrieves the current status of our feature, in a format which can be used
+ for UNO notifications
+
+ @precond
+ our mutex is locked
+ */
+ void getUnoState( css::frame::FeatureStateEvent& /* [out] */ _rState ) const;
+
+ ::osl::Mutex& m_rMutex;
+ ::comphelper::OInterfaceContainerHelper3<css::frame::XStatusListener> m_aStatusListeners;
+ css::uno::Reference< css::form::runtime::XFormOperations >
+ m_xFormOperations;
+ const css::util::URL m_aFeatureURL;
+ css::uno::Any m_aLastKnownState;
+ const sal_Int16 m_nFormFeature;
+ bool m_bLastKnownEnabled;
+
+ };
+
+
+}
+
+
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/inc/formtoolbars.hxx b/svx/source/inc/formtoolbars.hxx
new file mode 100644
index 000000000..1a77613bf
--- /dev/null
+++ b/svx/source/inc/formtoolbars.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_SVX_SOURCE_INC_FORMTOOLBARS_HXX
+#define INCLUDED_SVX_SOURCE_INC_FORMTOOLBARS_HXX
+
+#include <com/sun/star/frame/XLayoutManager.hpp>
+
+namespace svxform
+{
+ class FormToolboxes
+ {
+ private:
+ css::uno::Reference< css::frame::XLayoutManager > m_xLayouter;
+
+ public:
+ /** constructs an instance
+ @param _rxFrame
+ the frame to analyze
+ */
+ FormToolboxes(
+ const css::uno::Reference< css::frame::XFrame >& _rxFrame
+ );
+
+ public:
+ /** retrieves the URI for the toolbox associated with the given slot, depending
+ on the type of our document
+ */
+ static OUString
+ getToolboxResourceName( sal_uInt16 _nSlotId );
+
+ /** toggles the toolbox associated with the given slot
+ */
+ void toggleToolbox( sal_uInt16 _nSlotId ) const;
+
+ /** determines whether the toolbox associated with the given slot is currently visible
+ */
+ bool isToolboxVisible( sal_uInt16 _nSlotId ) const;
+
+ };
+
+}
+
+#endif // INCLUDED_SVX_SOURCE_INC_FORMTOOLBARS_HXX
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/inc/frmselimpl.hxx b/svx/source/inc/frmselimpl.hxx
new file mode 100644
index 000000000..79df99cfb
--- /dev/null
+++ b/svx/source/inc/frmselimpl.hxx
@@ -0,0 +1,285 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#ifndef INCLUDED_SVX_SOURCE_INC_FRMSELIMPL_HXX
+#define INCLUDED_SVX_SOURCE_INC_FRMSELIMPL_HXX
+
+#include <vcl/virdev.hxx>
+#include <vcl/image.hxx>
+#include <svx/frmsel.hxx>
+#include <svx/framelink.hxx>
+#include <svx/framelinkarray.hxx>
+#include <editeng/borderline.hxx>
+
+namespace svx {
+
+namespace a11y {
+ class AccFrameSelector;
+ class AccFrameSelectorChild;
+}
+
+class FrameBorder
+{
+public:
+ explicit FrameBorder(FrameBorderType eType);
+ static double GetDefaultPatternScale() { return 0.05; }
+
+ FrameBorderType GetType() const
+ {
+ return meType;
+ }
+
+ bool IsEnabled() const
+ {
+ return mbEnabled;
+ }
+ void Enable(FrameSelFlags nFlags);
+
+ FrameBorderState GetState() const
+ {
+ return meState;
+ }
+ void SetState(FrameBorderState eState);
+
+ bool IsSelected() const { return mbSelected; }
+ void Select( bool bSelect ) { mbSelected = bSelect; }
+
+ const editeng::SvxBorderLine& GetCoreStyle() const { return maCoreStyle; }
+ void SetCoreStyle( const editeng::SvxBorderLine* pStyle );
+
+ void SetUIColorPrim( const Color& rColor ) {maUIStyle.SetColorPrim( rColor ); }
+ void SetUIColorSecn( const Color& rColor ) {maUIStyle.SetColorSecn( rColor ); }
+ const frame::Style& GetUIStyle() const { return maUIStyle; }
+
+ void ClearFocusArea() { maFocusArea.Clear(); }
+ void AddFocusPolygon( const tools::Polygon& rFocus );
+ void MergeFocusToPolyPolygon( tools::PolyPolygon& rPPoly ) const;
+
+ void ClearClickArea() { maClickArea.Clear(); }
+ void AddClickRect( const tools::Rectangle& rRect );
+ bool ContainsClickPoint( const Point& rPos ) const;
+ tools::Rectangle GetClickBoundRect() const;
+
+ void SetKeyboardNeighbors(FrameBorderType eLeft, FrameBorderType eRight,
+ FrameBorderType eTop, FrameBorderType eBottom);
+ FrameBorderType GetKeyboardNeighbor( sal_uInt16 nKeyCode ) const;
+
+private:
+ const FrameBorderType meType; /// Frame border type (position in control).
+ FrameBorderState meState; /// Frame border state (on/off/don't care).
+ editeng::SvxBorderLine maCoreStyle; /// Core style from application.
+ frame::Style maUIStyle; /// Internal style to draw lines.
+ FrameBorderType meKeyLeft; /// Left neighbor for keyboard control.
+ FrameBorderType meKeyRight; /// Right neighbor for keyboard control.
+ FrameBorderType meKeyTop; /// Upper neighbor for keyboard control.
+ FrameBorderType meKeyBottom; /// Lower neighbor for keyboard control.
+ tools::PolyPolygon maFocusArea; /// Focus drawing areas.
+ tools::PolyPolygon maClickArea; /// Mouse click areas.
+ bool mbEnabled : 1; /// true = Border enabled in control.
+ bool mbSelected : 1; /// true = Border selected in control.
+};
+
+
+typedef std::vector< FrameBorder* > FrameBorderPtrVec;
+
+struct FrameSelectorImpl
+{
+ FrameSelector& mrFrameSel; /// The control itself.
+ ScopedVclPtr<VirtualDevice> mpVirDev; /// For all buffered drawing operations.
+ std::vector<Image> maArrows; /// Arrows in current system colors.
+ Color maBackCol; /// Background color.
+ Color maArrowCol; /// Selection arrow color.
+ Color maMarkCol; /// Selection marker color.
+ Color maHCLineCol; /// High contrast line color.
+ Point maVirDevPos; /// Position of virtual device in the control.
+
+ FrameBorder maLeft; /// All data of left frame border.
+ FrameBorder maRight; /// All data of right frame border.
+ FrameBorder maTop; /// All data of top frame border.
+ FrameBorder maBottom; /// All data of bottom frame border.
+ FrameBorder maHor; /// All data of inner horizontal frame border.
+ FrameBorder maVer; /// All data of inner vertical frame border.
+ FrameBorder maTLBR; /// All data of top-left to bottom-right frame border.
+ FrameBorder maBLTR; /// All data of bottom-left to top-right frame border.
+ editeng::SvxBorderLine maCurrStyle; /// Current style and color for new borders.
+ frame::Array maArray; /// Frame link array to draw an array of frame borders.
+
+ FrameSelFlags mnFlags; /// Flags for enabled frame borders.
+ FrameBorderPtrVec maAllBorders; /// Pointers to all frame borders.
+ FrameBorderPtrVec maEnabBorders; /// Pointers to enables frame borders.
+ Link<LinkParamNone*,void> maSelectHdl; /// Selection handler.
+
+ tools::Long mnCtrlSize; /// Size of the control (always square).
+ tools::Long mnArrowSize; /// Size of an arrow image.
+ tools::Long mnLine1; /// Middle of left/top frame borders.
+ tools::Long mnLine2; /// Middle of inner frame borders.
+ tools::Long mnLine3; /// Middle of right/bottom frame borders.
+ tools::Long mnFocusOffs; /// Offset from frame border middle to draw focus.
+
+ bool mbHor; /// true = Inner horizontal frame border enabled.
+ bool mbVer; /// true = Inner vertical frame border enabled.
+ bool mbTLBR; /// true = Top-left to bottom-right frame border enabled.
+ bool mbBLTR; /// true = Bottom-left to top-right frame border enabled.
+ bool mbFullRepaint; /// Used for repainting (false = only copy virtual device).
+ bool mbAutoSelect; /// true = Auto select a frame border, if focus reaches control.
+ bool mbHCMode; /// true = High contrast mode.
+
+ std::vector<rtl::Reference<a11y::AccFrameSelectorChild>>
+ maChildVec; /// Pointers to accessibility objects for frame borders.
+ explicit FrameSelectorImpl( FrameSelector& rFrameSel );
+ ~FrameSelectorImpl();
+
+ // initialization
+ /** Initializes the control, enables/disables frame borders according to flags. */
+ void Initialize( FrameSelFlags nFlags );
+
+ /** Fills all color members from current style settings. */
+ void InitColors();
+ /** Creates the image list with selection arrows regarding current style settings. */
+ void InitArrowImageList();
+ /** Initializes global coordinates. */
+ void InitGlobalGeometry();
+ /** Initializes coordinates of all frame borders. */
+ void InitBorderGeometry();
+ /** Draws the entire control into the internal virtual device. */
+ void InitVirtualDevice();
+ /** call this to recalculate based on parent size */
+ void sizeChanged();
+
+ // frame border access
+ /** Returns the object representing the specified frame border. */
+ const FrameBorder& GetBorder( FrameBorderType eBorder ) const;
+ /** Returns the object representing the specified frame border (write access). */
+ FrameBorder& GetBorderAccess( FrameBorderType eBorder );
+
+ // drawing
+ /** Draws the background of the entire control (the gray areas between borders). */
+ void DrawBackground();
+
+ /** Draws selection arrows for the specified frame border. */
+ void DrawArrows( const FrameBorder& rBorder );
+
+ /** Returns the color that has to be used to draw a frame border. */
+ Color GetDrawLineColor( const Color& rColor ) const;
+ /** Draws all frame borders. */
+ void DrawAllFrameBorders();
+
+ /** Draws all contents of the control. */
+ void DrawVirtualDevice();
+ /** Copies contents of the virtual device to the control. */
+ void CopyVirDevToControl(vcl::RenderContext& rRenderContext);
+
+ /** Draws tracking rectangles for all selected frame borders. */
+ void DrawAllTrackingRects(vcl::RenderContext& rRenderContext);
+
+ /** Converts a mouse position to the virtual device position. */
+ Point GetDevPosFromMousePos( const Point& rMousePos ) const;
+
+ /** Invalidates the control.
+ @param bFullRepaint true = Full repaint; false = update selection only. */
+ void DoInvalidate( bool bFullRepaint );
+
+ // frame border state and style
+ /** Sets the state of the specified frame border. */
+ void SetBorderState( FrameBorder& rBorder, FrameBorderState eState );
+ /** Sets the core style of the specified frame border, or hides the frame border, if pStyle is 0. */
+ void SetBorderCoreStyle( FrameBorder& rBorder, const editeng::SvxBorderLine* pStyle );
+
+ /** Changes the state of a frame border after a control event (mouse/keyboard). */
+ void ToggleBorderState( FrameBorder& rBorder );
+
+ // frame border selection
+ /** Selects a frame border and schedules redraw. */
+ void SelectBorder( FrameBorder& rBorder, bool bSelect );
+ /** Grabs focus without auto-selection of a frame border, if no border selected. */
+ void SilentGrabFocus();
+
+ /** Returns true, if all selected frame borders are equal (or if nothing is selected). */
+ bool SelectedBordersEqual() const;
+};
+
+
+/** Dummy predicate for frame border iterators to use all borders in a container. */
+struct FrameBorderDummy_Pred
+{
+ bool operator()( const FrameBorder* ) const { return true; }
+};
+
+/** Predicate for frame border iterators to use only visible borders in a container. */
+struct FrameBorderVisible_Pred
+{
+ bool operator()( const FrameBorder* pBorder ) const { return pBorder->GetState() == FrameBorderState::Show; }
+};
+
+/** Predicate for frame border iterators to use only selected borders in a container. */
+struct FrameBorderSelected_Pred
+{
+ bool operator()( const FrameBorder* pBorder ) const { return pBorder->IsSelected(); }
+};
+
+/** Template class for all types of frame border iterators. */
+template< typename Cont, typename Iter, typename Pred >
+class FrameBorderIterBase
+{
+public:
+ typedef Cont container_type;
+ typedef Iter iterator_type;
+ typedef typename Cont::value_type value_type;
+ typedef FrameBorderIterBase<Cont, Iter, Pred> this_type;
+
+ explicit FrameBorderIterBase( container_type& rCont );
+ bool Is() const { return maIt != maEnd; }
+ this_type& operator++();
+ value_type operator*() const { return *maIt; }
+
+private:
+ iterator_type maIt;
+ iterator_type maEnd;
+ Pred maPred;
+};
+
+/** Iterator for constant svx::FrameBorder containers, iterates over all borders. */
+typedef FrameBorderIterBase< const FrameBorderPtrVec, FrameBorderPtrVec::const_iterator, FrameBorderDummy_Pred >
+ FrameBorderCIter;
+
+/** Iterator for mutable svx::FrameBorder containers, iterates over all borders. */
+typedef FrameBorderIterBase< FrameBorderPtrVec, FrameBorderPtrVec::iterator, FrameBorderDummy_Pred >
+ FrameBorderIter;
+
+/** Iterator for constant svx::FrameBorder containers, iterates over visible borders. */
+typedef FrameBorderIterBase< const FrameBorderPtrVec, FrameBorderPtrVec::const_iterator, FrameBorderVisible_Pred >
+ VisFrameBorderCIter;
+
+/** Iterator for mutable svx::FrameBorder containers, iterates over visible borders. */
+typedef FrameBorderIterBase< FrameBorderPtrVec, FrameBorderPtrVec::iterator, FrameBorderVisible_Pred >
+ VisFrameBorderIter;
+
+/** Iterator for constant svx::FrameBorder containers, iterates over selected borders. */
+typedef FrameBorderIterBase< const FrameBorderPtrVec, FrameBorderPtrVec::const_iterator, FrameBorderSelected_Pred >
+ SelFrameBorderCIter;
+
+/** Iterator for mutable svx::FrameBorder containers, iterates over selected borders. */
+typedef FrameBorderIterBase< FrameBorderPtrVec, FrameBorderPtrVec::iterator, FrameBorderSelected_Pred >
+ SelFrameBorderIter;
+
+}
+
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/inc/gridcell.hxx b/svx/source/inc/gridcell.hxx
new file mode 100644
index 000000000..9ee419ea1
--- /dev/null
+++ b/svx/source/inc/gridcell.hxx
@@ -0,0 +1,1067 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#ifndef INCLUDED_SVX_SOURCE_INC_GRIDCELL_HXX
+#define INCLUDED_SVX_SOURCE_INC_GRIDCELL_HXX
+
+#include <memory>
+#include <svx/gridctrl.hxx>
+
+#include "sqlparserclient.hxx"
+
+#include <com/sun/star/sdb/XColumn.hpp>
+#include <com/sun/star/form/XBoundControl.hpp>
+#include <com/sun/star/awt/XTextComponent.hpp>
+#include <com/sun/star/awt/XListBox.hpp>
+#include <com/sun/star/awt/XComboBox.hpp>
+#include <com/sun/star/awt/TextAlign.hpp>
+#include <com/sun/star/awt/XControlModel.hpp>
+#include <com/sun/star/awt/XControl.hpp>
+#include <com/sun/star/awt/XCheckBox.hpp>
+#include <com/sun/star/awt/XButton.hpp>
+#include <com/sun/star/lang/XUnoTunnel.hpp>
+#include <com/sun/star/form/XChangeBroadcaster.hpp>
+#include <com/sun/star/awt/XWindow.hpp>
+#include <com/sun/star/util/XNumberFormatsSupplier.hpp>
+
+#include <comphelper/propmultiplex.hxx>
+#include <comphelper/interfacecontainer3.hxx>
+#include <comphelper/uno3.hxx>
+#include <connectivity/formattedcolumnvalue.hxx>
+#include <cppuhelper/basemutex.hxx>
+#include <cppuhelper/component.hxx>
+#include <cppuhelper/implbase1.hxx>
+#include <cppuhelper/implbase2.hxx>
+#include <tools/diagnose_ex.h>
+
+class DbCellControl;
+class Edit;
+class FmXGridCell;
+namespace dbtools {
+ class FormattedColumnValue;
+}
+
+// DbGridColumn, column description
+
+class DbGridColumn
+{
+ friend class DbGridControl;
+
+ css::uno::Reference< css::beans::XPropertySet > m_xModel;
+ css::uno::Reference< css::beans::XPropertySet > m_xField; // connection to the database field
+ ::svt::CellControllerRef m_xController; // structure for managing the controls for a column
+ // this is positioned by the DbBrowseBox on the respective
+ // cells of a column
+ rtl::Reference<FmXGridCell> m_pCell;
+ DbGridControl& m_rParent;
+ sal_Int32 m_nLastVisibleWidth; // only valid if m_bHidden == sal_True
+ sal_Int32 m_nFormatKey;
+ sal_Int16 m_nFieldType;
+ sal_Int16 m_nTypeId;
+ sal_uInt16 m_nId;
+ sal_Int16 m_nFieldPos;
+ sal_Int16 m_nAlign; // specified with TXT_ALIGN_LEFT...
+ bool m_bReadOnly : 1;
+ bool m_bAutoValue : 1;
+ bool m_bInSave : 1;
+ bool m_bNumeric : 1;
+ bool m_bObject : 1; // does the column reference an object datatype?
+ bool m_bHidden : 1;
+ bool m_bLocked : 1;
+
+ static ::svt::CellControllerRef s_xEmptyController;
+ // used by locked columns
+public:
+ DbGridColumn(sal_uInt16 _nId, DbGridControl& rParent)
+ :m_rParent(rParent)
+ ,m_nLastVisibleWidth(-1)
+ ,m_nFormatKey(0)
+ ,m_nFieldType(0)
+ ,m_nTypeId(0)
+ ,m_nId(_nId)
+ ,m_nFieldPos(-1)
+ ,m_nAlign(css::awt::TextAlign::LEFT)
+ ,m_bReadOnly(false)
+ ,m_bAutoValue(false)
+ ,m_bInSave(false)
+ ,m_bNumeric(false)
+ ,m_bObject(false)
+ ,m_bHidden(false)
+ ,m_bLocked(false)
+ {
+ }
+
+ ~DbGridColumn();
+
+ const css::uno::Reference< css::beans::XPropertySet >& getModel() const { return m_xModel; }
+ void setModel(const css::uno::Reference< css::beans::XPropertySet >& _xModel);
+
+
+ sal_uInt16 GetId() const {return m_nId;}
+ bool IsReadOnly() const {return m_bReadOnly;}
+ bool IsAutoValue() const {return m_bAutoValue;}
+ sal_Int16 GetAlignment() const {return m_nAlign;}
+ sal_Int16 GetFieldPos() const {return m_nFieldPos; }
+ bool IsNumeric() const {return m_bNumeric;}
+ bool IsHidden() const {return m_bHidden;}
+ sal_Int32 GetKey() const {return m_nFormatKey;}
+ const ::svt::CellControllerRef& GetController() const {return m_bLocked ? s_xEmptyController : m_xController;}
+ const css::uno::Reference< css::beans::XPropertySet >& GetField() const {return m_xField;}
+ DbGridControl& GetParent() const {return m_rParent;}
+ FmXGridCell* GetCell() const {return m_pCell.get();}
+
+ css::uno::Reference< css::sdb::XColumn > GetCurrentFieldValue() const;
+
+ // Drawing a field at a position. If a view is set, it takes over the drawing,
+ // e.g., for checkboxes.
+ void Paint(OutputDevice& rDev,
+ const tools::Rectangle& rRect,
+ const DbGridRow* pRow,
+ const css::uno::Reference< css::util::XNumberFormatter >& xFormatter);
+
+
+ // Initializing in the alive mode.
+ // If no ColumnController is set, a default initialization is performed.
+ void CreateControl(sal_Int32 _nFieldPos, const css::uno::Reference< css::beans::XPropertySet >& xField, sal_Int32 nTypeId);
+ void UpdateControl()
+ {
+ css::uno::Reference< css::beans::XPropertySet > xField(m_xField);
+ CreateControl(m_nFieldPos, xField, m_nTypeId);
+ }
+
+ // Editing a Zelle
+ void UpdateFromField(const DbGridRow* pRow, const css::uno::Reference< css::util::XNumberFormatter >& xFormatter);
+ bool Commit();
+
+ // releasing all the data required for the AliveMode
+ void Clear();
+
+ OUString GetCellText(const DbGridRow* pRow, const css::uno::Reference< css::util::XNumberFormatter >& xFormatter) const;
+ OUString GetCellText(const css::uno::Reference< css::sdb::XColumn >& xField, const css::uno::Reference< css::util::XNumberFormatter >& xFormatter) const;
+
+ void SetReadOnly(bool bRead){m_bReadOnly = bRead;}
+ void SetObject(sal_Int16 nPos) {m_bObject = m_bReadOnly = true; m_nFieldPos = nPos;}
+
+ void ImplInitWindow( vcl::Window const & rParent, const InitWindowFacet _eInitWhat );
+
+ // properties that can bleed through onto the css::frame::Controller
+ sal_Int16 SetAlignment(sal_Int16 _nAlign);
+ // if _nAlign is -1, the alignment is calculated from the type of the field we are bound to
+ // the value really set is returned
+ sal_Int16 SetAlignmentFromModel(sal_Int16 nStandardAlign);
+ // set the alignment according to the "Align"-property of m_xModel, use the given standard
+ // alignment if the property if void, return the really set alignment
+
+ // column locking
+ bool isLocked() const { return m_bLocked; }
+ void setLock(bool _bLock);
+
+private:
+ /** attaches or detaches our cell object to the SctriptEventAttacherManager implemented
+ by our model's parent
+ */
+ void impl_toggleScriptManager_nothrow( bool _bAttach );
+};
+
+
+// DbCellControl, provides the data for a CellController.
+// Is usually only required for complex controls such as combo boxes.
+
+class DbCellControl
+ :public cppu::BaseMutex // _before_ the listener, so the listener is to be destroyed first!
+ ,public ::comphelper::OPropertyChangeListener
+{
+private:
+ rtl::Reference<::comphelper::OPropertyChangeMultiplexer> m_pModelChangeBroadcaster;
+ rtl::Reference<::comphelper::OPropertyChangeMultiplexer> m_pFieldChangeBroadcaster;
+
+private:
+ bool m_bTransparent : 1;
+ bool m_bAlignedController : 1;
+ bool m_bAccessingValueProperty : 1;
+
+ css::uno::Reference< css::sdbc::XRowSet >
+ m_xCursor;
+
+protected:
+ DbGridColumn& m_rColumn;
+ VclPtr<svt::ControlBase> m_pPainter;
+ VclPtr<svt::ControlBase> m_pWindow;
+
+protected:
+ // attribute access
+ const css::uno::Reference< css::sdbc::XRowSet >& getCursor() const { return m_xCursor; }
+
+ // control transparency
+ bool isTransparent( ) const { return m_bTransparent; }
+ void setTransparent( bool _bSet ) { m_bTransparent = _bSet; }
+
+ // control alignment
+ void setAlignedController( bool _bAlign ) { m_bAlignedController = _bAlign; }
+
+
+ /** determined whether or not the value property is locked
+ @see lockValueProperty
+ */
+ inline bool isValuePropertyLocked() const;
+
+ /** locks the listening at the value property.
+ <p>This means that every subsequent change now done on the value property of the model ("Text", or "Value",
+ or whatever) is then ignored.<br/>
+ This base class uses this setting in <method>Commit</method>.</p>
+ @precond
+ Value locking can't be nested
+ @see unlockValueProperty
+ */
+ inline void lockValueProperty();
+ /** unlocks the listening at the value property
+ @see lockValueProperty
+ */
+ inline void unlockValueProperty();
+
+protected:
+ // adds the given property to the list of properties which we listen for
+ void doPropertyListening( const OUString& _rPropertyName );
+
+ // called whenever a property which affects field settings in general is called
+ // you should overwrite this method for every property you add yourself as listener to
+ // with doPropertyListening
+ virtual void implAdjustGenericFieldSetting( const css::uno::Reference< css::beans::XPropertySet >& _rxModel );
+
+ // called by _propertyChanged if a property which denotes the column value has changed
+ void implValuePropertyChanged( );
+
+
+public:
+ DbCellControl(DbGridColumn& _rColumn);
+ virtual ~DbCellControl() override;
+
+ svt::ControlBase& GetWindow() const
+ {
+ ENSURE_OR_THROW( m_pWindow, "no window" );
+ return *m_pWindow;
+ }
+
+ // control alignment
+ bool isAlignedController() const { return m_bAlignedController; }
+ void AlignControl(sal_Int16 nAlignment);
+
+ void SetTextLineColor();
+ void SetTextLineColor(const Color& _rColor);
+
+ // initializing before a control is displayed
+ virtual void Init( BrowserDataWin& rParent, const css::uno::Reference< css::sdbc::XRowSet >& xCursor );
+ virtual ::svt::CellControllerRef CreateController() const = 0;
+
+ // writing the value into the model
+ bool Commit();
+
+ // Formatting the field data to output text
+ virtual OUString GetFormatText(const css::uno::Reference< css::sdb::XColumn >& _rxField, const css::uno::Reference< css::util::XNumberFormatter >& xFormatter, const Color** ppColor = nullptr) = 0;
+
+ virtual void Update(){}
+ // Refresh the control by the field data
+ virtual void UpdateFromField(const css::uno::Reference< css::sdb::XColumn >& _rxField, const css::uno::Reference< css::util::XNumberFormatter >& xFormatter) = 0;
+
+ // painting a cell content in the specified rectangle
+ virtual void PaintFieldToCell( OutputDevice& rDev, const tools::Rectangle& rRect, const css::uno::Reference< css::sdb::XColumn >& _rxField, const css::uno::Reference< css::util::XNumberFormatter >& xFormatter);
+ virtual void PaintCell( OutputDevice& _rDev, const tools::Rectangle& _rRect );
+
+ void ImplInitWindow( vcl::Window const & rParent, const InitWindowFacet _eInitWhat );
+
+ double GetValue(const css::uno::Reference< css::sdb::XColumn >& _rxField, const css::uno::Reference< css::util::XNumberFormatter >& xFormatter) const;
+
+protected:
+ void invalidatedController();
+
+ /** commits the content of the control (e.g. the text of an edit field) into the column model
+ (e.g. the "Text" property of the model).
+ <p>To be overwritten in derived classes.</p>
+ @see updateFromModel
+ */
+ virtual bool commitControl( ) = 0;
+
+ /** updates the current content of the control (e.g. the text of an edit field) from the column model
+ (e.g. the "Text" property of the model).
+ <p>To be overwritten in derived classes.</p>
+ @precond
+ NULL != _rxModel
+ @precond
+ NULL != m_pWindow
+
+ @see commitControl
+ */
+ virtual void updateFromModel( css::uno::Reference< css::beans::XPropertySet > _rxModel ) = 0;
+
+protected:
+// OPropertyChangeListener
+ virtual void _propertyChanged(const css::beans::PropertyChangeEvent& evt) override;
+
+private:
+ void implDoPropertyListening( const OUString& _rPropertyName, bool _bWarnIfNotExistent );
+
+ /// updates the "readonly" setting on m_pWindow, according to the respective property value in the given model
+ void implAdjustReadOnly( const css::uno::Reference< css::beans::XPropertySet >& _rxModel,bool i_bReadOnly );
+
+ /// updates the "enabled" setting on m_pWindow, according to the respective property value in the given model
+ void implAdjustEnabled( const css::uno::Reference< css::beans::XPropertySet >& _rxModel );
+};
+
+
+inline bool DbCellControl::isValuePropertyLocked() const
+{
+ return m_bAccessingValueProperty;
+}
+
+
+inline void DbCellControl::lockValueProperty()
+{
+ OSL_ENSURE( !isValuePropertyLocked(), "DbCellControl::lockValueProperty: not to be nested!" );
+ m_bAccessingValueProperty = true;
+}
+
+
+inline void DbCellControl::unlockValueProperty()
+{
+ OSL_ENSURE( isValuePropertyLocked(), "DbCellControl::lockValueProperty: not locked so far!" );
+ m_bAccessingValueProperty = false;
+}
+
+
+/** a field which is bound to a column which supports the MaxTextLen property
+*/
+class DbLimitedLengthField : public DbCellControl
+{
+public:
+
+protected:
+ DbLimitedLengthField( DbGridColumn& _rColumn );
+
+protected:
+ // DbCellControl
+ virtual void implAdjustGenericFieldSetting( const css::uno::Reference< css::beans::XPropertySet >& _rxModel ) override;
+
+protected:
+ void implSetMaxTextLen( sal_Int16 _nMaxLen )
+ {
+ implSetEffectiveMaxTextLen(_nMaxLen);
+ }
+ virtual void implSetEffectiveMaxTextLen( sal_Int32 _nMaxLen );
+};
+
+
+class DbTextField final : public DbLimitedLengthField
+{
+ std::unique_ptr<::svt::IEditImplementation> m_pEdit;
+ std::unique_ptr<::svt::IEditImplementation> m_pPainterImplementation;
+ bool m_bIsMultiLineEdit;
+
+ virtual ~DbTextField( ) override;
+
+public:
+ DbTextField(DbGridColumn& _rColumn);
+
+ ::svt::IEditImplementation* GetEditImplementation() { return m_pEdit.get(); }
+ bool IsMultiLineEdit() const { return m_bIsMultiLineEdit; }
+
+ virtual void Init( BrowserDataWin& rParent, const css::uno::Reference< css::sdbc::XRowSet >& xCursor ) override;
+ virtual OUString GetFormatText(const css::uno::Reference< css::sdb::XColumn >& _rxField, const css::uno::Reference< css::util::XNumberFormatter >& xFormatter, const Color** ppColor = nullptr) override;
+ virtual void UpdateFromField(const css::uno::Reference< css::sdb::XColumn >& _rxField, const css::uno::Reference< css::util::XNumberFormatter >& xFormatter) override;
+ virtual ::svt::CellControllerRef CreateController() const override;
+ virtual void PaintFieldToCell( OutputDevice& _rDev, const tools::Rectangle& _rRect,
+ const css::uno::Reference< css::sdb::XColumn >& _rxField,
+ const css::uno::Reference< css::util::XNumberFormatter >& _rxFormatter ) override;
+
+private:
+ // DbCellControl
+ virtual bool commitControl( ) override;
+ virtual void updateFromModel( css::uno::Reference< css::beans::XPropertySet > _rxModel ) override;
+ // DbLimitedLengthField
+ virtual void implSetEffectiveMaxTextLen( sal_Int32 _nMaxLen ) override;
+};
+
+
+class DbFormattedField final : public DbLimitedLengthField
+{
+public:
+ DbFormattedField(DbGridColumn& _rColumn);
+ virtual ~DbFormattedField() override;
+
+ virtual void Init( BrowserDataWin& rParent, const css::uno::Reference< css::sdbc::XRowSet >& xCursor ) override;
+ virtual OUString GetFormatText(const css::uno::Reference< css::sdb::XColumn >& _rxField, const css::uno::Reference< css::util::XNumberFormatter >& xFormatter, const Color** ppColor = nullptr) override;
+ virtual void UpdateFromField(const css::uno::Reference< css::sdb::XColumn >& _rxField, const css::uno::Reference< css::util::XNumberFormatter >& xFormatter) override;
+ virtual ::svt::CellControllerRef CreateController() const override;
+
+private:
+ // DbCellControl
+ virtual bool commitControl( ) override;
+ virtual void updateFromModel( css::uno::Reference< css::beans::XPropertySet > _rxModel ) override;
+
+ // OPropertyChangeListener
+ virtual void _propertyChanged(const css::beans::PropertyChangeEvent& evt) override;
+
+ css::uno::Reference< css::util::XNumberFormatsSupplier > m_xSupplier;
+};
+
+
+class DbCheckBox final : public DbCellControl
+{
+public:
+ DbCheckBox(DbGridColumn& _rColumn);
+
+ virtual void Init( BrowserDataWin& rParent, const css::uno::Reference< css::sdbc::XRowSet >& xCursor ) override;
+ virtual void UpdateFromField(const css::uno::Reference< css::sdb::XColumn >& _rxField, const css::uno::Reference< css::util::XNumberFormatter >& xFormatter) override;
+ virtual ::svt::CellControllerRef CreateController() const override;
+ virtual void PaintFieldToCell(OutputDevice& rDev, const tools::Rectangle& rRect,
+ const css::uno::Reference< css::sdb::XColumn >& _rxField,
+ const css::uno::Reference< css::util::XNumberFormatter >& xFormatter) override;
+ virtual void PaintCell(OutputDevice& rDev, const tools::Rectangle& rRect) override;
+ virtual OUString GetFormatText(const css::uno::Reference< css::sdb::XColumn >& _rxField, const css::uno::Reference< css::util::XNumberFormatter >& xFormatter, const Color** ppColor = nullptr) override;
+
+private:
+ // DbCellControl
+ virtual bool commitControl( ) override;
+ virtual void updateFromModel( css::uno::Reference< css::beans::XPropertySet > _rxModel ) override;
+};
+
+
+class DbComboBox final : public DbCellControl
+{
+
+public:
+ DbComboBox(DbGridColumn& _rColumn);
+
+ virtual void Init( BrowserDataWin& rParent, const css::uno::Reference< css::sdbc::XRowSet >& xCursor ) override;
+ virtual OUString GetFormatText(const css::uno::Reference< css::sdb::XColumn >& _rxField, const css::uno::Reference< css::util::XNumberFormatter >& xFormatter, const Color** ppColor = nullptr) override;
+ virtual void UpdateFromField(const css::uno::Reference< css::sdb::XColumn >& _rxField, const css::uno::Reference< css::util::XNumberFormatter >& xFormatter) override;
+ virtual ::svt::CellControllerRef CreateController() const override;
+
+ void SetList(const css::uno::Any& rItems);
+
+private:
+ // DbCellControl
+ virtual bool commitControl( ) override;
+ virtual void updateFromModel( css::uno::Reference< css::beans::XPropertySet > _rxModel ) override;
+
+ virtual void implAdjustGenericFieldSetting( const css::uno::Reference< css::beans::XPropertySet >& _rxModel ) override;
+
+ // OPropertyChangeListener
+ virtual void _propertyChanged(const css::beans::PropertyChangeEvent& evt) override;
+};
+
+
+class DbListBox final : public DbCellControl
+{
+ bool m_bBound : 1;
+ css::uno::Sequence< OUString > m_aValueList;
+
+public:
+ DbListBox(DbGridColumn& _rColumn);
+
+ virtual void Init( BrowserDataWin& rParent, const css::uno::Reference< css::sdbc::XRowSet >& xCursor ) override;
+ virtual OUString GetFormatText(const css::uno::Reference< css::sdb::XColumn >& _rxField, const css::uno::Reference< css::util::XNumberFormatter >& xFormatter, const Color** ppColor = nullptr) override;
+ virtual void UpdateFromField(const css::uno::Reference< css::sdb::XColumn >& _rxField, const css::uno::Reference< css::util::XNumberFormatter >& xFormatter) override;
+ virtual ::svt::CellControllerRef CreateController() const override;
+
+ void SetList(const css::uno::Any& rItems);
+
+private:
+ // DbCellControl
+ virtual bool commitControl( ) override;
+ virtual void updateFromModel( css::uno::Reference< css::beans::XPropertySet > _rxModel ) override;
+
+ virtual void implAdjustGenericFieldSetting( const css::uno::Reference< css::beans::XPropertySet >& _rxModel ) override;
+
+ // OPropertyChangeListener
+ virtual void _propertyChanged(const css::beans::PropertyChangeEvent& evt) override;
+};
+
+
+class DbPatternField final : public DbCellControl
+{
+public:
+ DbPatternField( DbGridColumn& _rColumn, const css::uno::Reference<css::uno::XComponentContext>& _rContext );
+ virtual void Init( BrowserDataWin& rParent, const css::uno::Reference< css::sdbc::XRowSet >& xCursor ) override;
+ virtual OUString GetFormatText(const css::uno::Reference< css::sdb::XColumn >& _rxField, const css::uno::Reference< css::util::XNumberFormatter >& xFormatter, const Color** ppColor = nullptr) override;
+ virtual void UpdateFromField(const css::uno::Reference< css::sdb::XColumn >& _rxField, const css::uno::Reference< css::util::XNumberFormatter >& xFormatter) override;
+ virtual ::svt::CellControllerRef CreateController() const override;
+
+private:
+ /// DbCellControl
+ virtual bool commitControl( ) override;
+ virtual void updateFromModel( css::uno::Reference< css::beans::XPropertySet > _rxModel ) override;
+
+ virtual void implAdjustGenericFieldSetting( const css::uno::Reference< css::beans::XPropertySet >& _rxModel ) override;
+
+ OUString impl_formatText(const OUString& _rText);
+
+ ::std::unique_ptr< ::dbtools::FormattedColumnValue > m_pValueFormatter;
+ ::std::unique_ptr< ::dbtools::FormattedColumnValue > m_pPaintFormatter;
+ css::uno::Reference<css::uno::XComponentContext> m_xContext;
+};
+
+
+class DbSpinField : public DbCellControl
+{
+private:
+ sal_Int16 m_nStandardAlign;
+
+public:
+
+protected:
+ DbSpinField( DbGridColumn& _rColumn, sal_Int16 _nStandardAlign = css::awt::TextAlign::RIGHT );
+
+public:
+ virtual void Init( BrowserDataWin& rParent, const css::uno::Reference< css::sdbc::XRowSet >& _rxCursor ) override;
+ virtual ::svt::CellControllerRef CreateController() const override;
+
+protected:
+ virtual VclPtr<svt::ControlBase> createField(
+ BrowserDataWin* _pParent,
+ bool bSpinButton,
+ const css::uno::Reference< css::beans::XPropertySet >& _rxModel
+ ) = 0;
+};
+
+class DbDateField final : public DbSpinField
+{
+public:
+ DbDateField(DbGridColumn& _rColumn);
+ virtual OUString GetFormatText(const css::uno::Reference< css::sdb::XColumn >& _rxField, const css::uno::Reference< css::util::XNumberFormatter >& xFormatter, const Color** ppColor = nullptr) override;
+ virtual void UpdateFromField(const css::uno::Reference< css::sdb::XColumn >& _rxField, const css::uno::Reference< css::util::XNumberFormatter >& xFormatter) override;
+
+private:
+ // DbCellControl
+ virtual bool commitControl( ) override;
+ virtual void updateFromModel( css::uno::Reference< css::beans::XPropertySet > _rxModel ) override;
+
+ // DbSpinField
+ virtual VclPtr<svt::ControlBase> createField(
+ BrowserDataWin* _pParent,
+ bool bSpinButton,
+ const css::uno::Reference< css::beans::XPropertySet >& _rxModel
+ ) override;
+
+ /// initializes everything which relates to the properties describing the numeric behaviour
+ virtual void implAdjustGenericFieldSetting( const css::uno::Reference< css::beans::XPropertySet >& _rxModel ) override;
+};
+
+class DbTimeField final : public DbSpinField
+{
+public:
+ DbTimeField(DbGridColumn& _rColumn);
+ virtual OUString GetFormatText(const css::uno::Reference< css::sdb::XColumn >& _rxField, const css::uno::Reference< css::util::XNumberFormatter >& xFormatter, const Color** ppColor = nullptr) override;
+ virtual void UpdateFromField(const css::uno::Reference< css::sdb::XColumn >& _rxField, const css::uno::Reference< css::util::XNumberFormatter >& xFormatter) override;
+
+private:
+ // DbCellControl
+ virtual bool commitControl( ) override;
+ virtual void updateFromModel( css::uno::Reference< css::beans::XPropertySet > _rxModel ) override;
+
+ // DbSpinField
+ virtual VclPtr<svt::ControlBase> createField(
+ BrowserDataWin* _pParent,
+ bool bSpinButton,
+ const css::uno::Reference< css::beans::XPropertySet >& _rxModel
+ ) override;
+
+ /// initializes everything which relates to the properties describing the numeric behaviour
+ virtual void implAdjustGenericFieldSetting( const css::uno::Reference< css::beans::XPropertySet >& _rxModel ) override;
+};
+
+class DbCurrencyField final : public DbSpinField
+{
+public:
+ DbCurrencyField(DbGridColumn& _rColumn);
+ virtual OUString GetFormatText(const css::uno::Reference< css::sdb::XColumn >& _rxField, const css::uno::Reference< css::util::XNumberFormatter >& xFormatter, const Color** ppColor = nullptr) override;
+ virtual void UpdateFromField(const css::uno::Reference< css::sdb::XColumn >& _rxField, const css::uno::Reference< css::util::XNumberFormatter >& xFormatter) override;
+
+private:
+ // DbCellControl
+ virtual bool commitControl( ) override;
+ virtual void updateFromModel( css::uno::Reference< css::beans::XPropertySet > _rxModel ) override;
+
+ // DbSpinField
+ virtual VclPtr<svt::ControlBase> createField(
+ BrowserDataWin* _pParent,
+ bool bSpinButton,
+ const css::uno::Reference< css::beans::XPropertySet >& _rxModel
+ ) override;
+
+ /// initializes everything which relates to the properties describing the numeric behaviour
+ virtual void implAdjustGenericFieldSetting( const css::uno::Reference< css::beans::XPropertySet >& _rxModel ) override;
+};
+
+class DbNumericField final : public DbSpinField
+{
+public:
+ DbNumericField(DbGridColumn& _rColumn);
+
+ virtual OUString GetFormatText(const css::uno::Reference< css::sdb::XColumn >& _rxField, const css::uno::Reference< css::util::XNumberFormatter >& xFormatter, const Color** ppColor = nullptr) override;
+ virtual void UpdateFromField(const css::uno::Reference< css::sdb::XColumn >& _rxField, const css::uno::Reference< css::util::XNumberFormatter >& xFormatter) override;
+
+private:
+ // DbCellControl
+ virtual bool commitControl( ) override;
+ virtual void updateFromModel( css::uno::Reference< css::beans::XPropertySet > _rxModel ) override;
+
+ // DbSpinField
+ virtual VclPtr<svt::ControlBase> createField(
+ BrowserDataWin* _pParent,
+ bool bSpinButton,
+ const css::uno::Reference< css::beans::XPropertySet >& _rxModel
+ ) override;
+
+ /// initializes everything which relates to the properties describing the numeric behaviour
+ void implAdjustGenericFieldSetting( const css::uno::Reference< css::beans::XPropertySet >& _rxModel ) override;
+};
+
+class DbFilterField final
+ :public DbCellControl
+ ,public ::svxform::OSQLParserClient
+{
+public:
+ DbFilterField(const css::uno::Reference< css::uno::XComponentContext >& rxContext, DbGridColumn& _rColumn);
+ virtual ~DbFilterField() override;
+
+ virtual void Init( BrowserDataWin& rParent, const css::uno::Reference< css::sdbc::XRowSet >& xCursor ) override;
+ virtual ::svt::CellControllerRef CreateController() const override;
+ virtual void PaintCell(OutputDevice& rDev, const tools::Rectangle& rRect) override;
+ virtual void Update() override;
+ virtual OUString GetFormatText(const css::uno::Reference< css::sdb::XColumn >& _rxField, const css::uno::Reference< css::util::XNumberFormatter >& xFormatter, const Color** ppColor = nullptr) override;
+ virtual void UpdateFromField(const css::uno::Reference< css::sdb::XColumn >& _rxField, const css::uno::Reference< css::util::XNumberFormatter >& xFormatter) override;
+
+ const OUString& GetText() const {return m_aText;}
+ void SetText(const OUString& rText);
+
+ void SetCommitHdl( const Link<DbFilterField&,void>& rLink ) { m_aCommitLink = rLink; }
+
+private:
+ // DbCellControl
+ virtual bool commitControl( ) override;
+ virtual void updateFromModel( css::uno::Reference< css::beans::XPropertySet > _rxModel ) override;
+
+ void SetList(const css::uno::Any& rItems, bool bComboBox);
+ void CreateControl(BrowserDataWin* pParent, const css::uno::Reference< css::beans::XPropertySet >& xModel);
+ DECL_LINK(OnToggle, weld::CheckButton&, void);
+
+ css::uno::Sequence< OUString > m_aValueList;
+ OUString m_aText;
+ Link<DbFilterField&,void> m_aCommitLink;
+ sal_Int16 m_nControlClass;
+ bool m_bFilterList : 1;
+ bool m_bFilterListFilled : 1;
+};
+
+
+// Base class providing the access to a grid cell
+
+typedef ::cppu::ImplHelper2 < css::awt::XControl
+ , css::form::XBoundControl
+ > FmXGridCell_Base;
+typedef ::cppu::ImplHelper1 < css::awt::XWindow
+ > FmXGridCell_WindowBase;
+class FmXGridCell :public ::cppu::OComponentHelper
+ ,public FmXGridCell_Base
+ ,public FmXGridCell_WindowBase
+{
+protected:
+ ::osl::Mutex m_aMutex;
+ DbGridColumn* m_pColumn;
+ std::unique_ptr<DbCellControl> m_pCellControl;
+
+private:
+ ::comphelper::OInterfaceContainerHelper3<css::awt::XWindowListener> m_aWindowListeners;
+ ::comphelper::OInterfaceContainerHelper3<css::awt::XFocusListener> m_aFocusListeners;
+ ::comphelper::OInterfaceContainerHelper3<css::awt::XKeyListener> m_aKeyListeners;
+ ::comphelper::OInterfaceContainerHelper3<css::awt::XMouseListener> m_aMouseListeners;
+ ::comphelper::OInterfaceContainerHelper3<css::awt::XMouseMotionListener> m_aMouseMotionListeners;
+
+protected:
+ virtual ~FmXGridCell() override;
+
+public:
+ FmXGridCell( DbGridColumn* pColumn, std::unique_ptr<DbCellControl> pControl );
+ void init();
+
+ DECLARE_UNO3_AGG_DEFAULTS(FmXGridCell, OComponentHelper)
+ virtual css::uno::Any SAL_CALL queryAggregation( const css::uno::Type& _rType ) override;
+
+ void SetTextLineColor();
+ void SetTextLineColor(const Color& _rColor);
+
+// XTypeProvider
+ virtual css::uno::Sequence< css::uno::Type > SAL_CALL getTypes( ) override;
+ virtual css::uno::Sequence< sal_Int8 > SAL_CALL getImplementationId() override;
+
+// OComponentHelper
+ virtual void SAL_CALL disposing() override;
+
+// css::lang::XComponent
+ virtual void SAL_CALL dispose() override {OComponentHelper::dispose();}
+ virtual void SAL_CALL addEventListener(const css::uno::Reference< css::lang::XEventListener >& aListener) override { OComponentHelper::addEventListener(aListener);}
+ virtual void SAL_CALL removeEventListener(const css::uno::Reference< css::lang::XEventListener >& aListener) override { OComponentHelper::removeEventListener(aListener);}
+
+// css::awt::XControl
+ virtual void SAL_CALL setContext(const css::uno::Reference< css::uno::XInterface >& /*Context*/) override {}
+ virtual css::uno::Reference< css::uno::XInterface > SAL_CALL getContext() override;
+ virtual void SAL_CALL createPeer(const css::uno::Reference< css::awt::XToolkit >& /*Toolkit*/, const css::uno::Reference< css::awt::XWindowPeer >& /*Parent*/) override {}
+
+ virtual css::uno::Reference< css::awt::XWindowPeer > SAL_CALL getPeer() override {return css::uno::Reference< css::awt::XWindowPeer > ();}
+ virtual sal_Bool SAL_CALL setModel(const css::uno::Reference< css::awt::XControlModel >& /*Model*/) override {return false;}
+ virtual css::uno::Reference< css::awt::XControlModel > SAL_CALL getModel() override;
+ virtual css::uno::Reference< css::awt::XView > SAL_CALL getView() override {return css::uno::Reference< css::awt::XView > ();}
+ virtual void SAL_CALL setDesignMode(sal_Bool /*bOn*/) override {}
+ virtual sal_Bool SAL_CALL isDesignMode() override {return false;}
+ virtual sal_Bool SAL_CALL isTransparent() override {return false;}
+
+// css::form::XBoundControl
+ virtual sal_Bool SAL_CALL getLock() override;
+ virtual void SAL_CALL setLock(sal_Bool _bLock) override;
+
+ // XWindow
+ virtual void SAL_CALL setPosSize( ::sal_Int32 X, ::sal_Int32 Y, ::sal_Int32 Width, ::sal_Int32 Height, ::sal_Int16 Flags ) override;
+ virtual css::awt::Rectangle SAL_CALL getPosSize( ) override;
+ virtual void SAL_CALL setVisible( sal_Bool Visible ) override;
+ virtual void SAL_CALL setEnable( sal_Bool Enable ) override;
+ virtual void SAL_CALL setFocus( ) override;
+ virtual void SAL_CALL addWindowListener( const css::uno::Reference< css::awt::XWindowListener >& xListener ) override;
+ virtual void SAL_CALL removeWindowListener( const css::uno::Reference< css::awt::XWindowListener >& xListener ) override;
+ virtual void SAL_CALL addFocusListener( const css::uno::Reference< css::awt::XFocusListener >& xListener ) override;
+ virtual void SAL_CALL removeFocusListener( const css::uno::Reference< css::awt::XFocusListener >& xListener ) override;
+ virtual void SAL_CALL addKeyListener( const css::uno::Reference< css::awt::XKeyListener >& xListener ) override;
+ virtual void SAL_CALL removeKeyListener( const css::uno::Reference< css::awt::XKeyListener >& xListener ) override;
+ virtual void SAL_CALL addMouseListener( const css::uno::Reference< css::awt::XMouseListener >& xListener ) override;
+ virtual void SAL_CALL removeMouseListener( const css::uno::Reference< css::awt::XMouseListener >& xListener ) override;
+ virtual void SAL_CALL addMouseMotionListener( const css::uno::Reference< css::awt::XMouseMotionListener >& xListener ) override;
+ virtual void SAL_CALL removeMouseMotionListener( const css::uno::Reference< css::awt::XMouseMotionListener >& xListener ) override;
+ virtual void SAL_CALL addPaintListener( const css::uno::Reference< css::awt::XPaintListener >& xListener ) override;
+ virtual void SAL_CALL removePaintListener( const css::uno::Reference< css::awt::XPaintListener >& xListener ) override;
+
+ bool Commit() {return m_pCellControl->Commit();}
+ void ImplInitWindow( vcl::Window const & rParent, const InitWindowFacet _eInitWhat )
+ { m_pCellControl->ImplInitWindow( rParent, _eInitWhat ); }
+
+ bool isAlignedController() const { return m_pCellControl->isAlignedController(); }
+ void AlignControl(sal_Int16 nAlignment)
+ { m_pCellControl->AlignControl(nAlignment);}
+
+protected:
+ // default implementations call our focus listeners, don't forget to call them if you override this
+ virtual void onFocusGained( const css::awt::FocusEvent& _rEvent );
+ virtual void onFocusLost( const css::awt::FocusEvent& _rEvent );
+
+private:
+ svt::ControlBase* getEventWindow() const;
+ DECL_LINK(OnFocusGained, LinkParamNone*, void);
+ DECL_LINK(OnFocusLost, LinkParamNone*, void);
+ DECL_LINK(OnMousePress, const MouseEvent&, void);
+ DECL_LINK(OnMouseRelease, const MouseEvent&, void);
+ DECL_LINK(OnMouseMove, const MouseEvent&, void);
+ DECL_LINK(OnKeyInput, const KeyEvent&, void);
+ DECL_LINK(OnKeyRelease, const KeyEvent&, void);
+};
+
+
+class FmXDataCell : public FmXGridCell
+{
+public:
+ FmXDataCell( DbGridColumn* pColumn, std::unique_ptr<DbCellControl> pControl )
+ :FmXGridCell( pColumn, std::move(pControl) )
+ {
+ }
+
+ virtual void PaintFieldToCell(OutputDevice& rDev,
+ const tools::Rectangle& rRect,
+ const css::uno::Reference< css::sdb::XColumn >& xField,
+ const css::uno::Reference< css::util::XNumberFormatter >& xFormatter);
+
+ void UpdateFromField(const css::uno::Reference< css::sdb::XColumn >& xField,
+ const css::uno::Reference< css::util::XNumberFormatter >& xFormatter)
+ { m_pCellControl->UpdateFromField(xField, xFormatter); }
+
+protected:
+ void UpdateFromColumn();
+};
+
+
+class FmXTextCell : public FmXDataCell
+{
+protected:
+ bool m_bIsMultiLineText;
+
+public:
+ FmXTextCell( DbGridColumn* pColumn, std::unique_ptr<DbCellControl> pControl );
+
+ virtual void PaintFieldToCell(OutputDevice& rDev,
+ const tools::Rectangle& rRect,
+ const css::uno::Reference< css::sdb::XColumn >& xField,
+ const css::uno::Reference< css::util::XNumberFormatter >& xFormatter) override;
+
+ OUString GetText(const css::uno::Reference< css::sdb::XColumn >& _rxField,
+ const css::uno::Reference< css::util::XNumberFormatter >& xFormatter,
+ const Color** ppColor = nullptr)
+ {return m_pCellControl->GetFormatText(_rxField, xFormatter, ppColor);}
+};
+
+
+typedef ::cppu::ImplHelper2 < css::awt::XTextComponent
+ , css::form::XChangeBroadcaster
+ > FmXEditCell_Base;
+class FmXEditCell final : public FmXTextCell,
+ public FmXEditCell_Base
+{
+public:
+ FmXEditCell( DbGridColumn* pColumn, std::unique_ptr<DbCellControl> pControl );
+
+ DECLARE_UNO3_AGG_DEFAULTS(FmXEditCell, FmXTextCell)
+ virtual css::uno::Any SAL_CALL queryAggregation( const css::uno::Type& _rType ) override;
+
+// XTypeProvider
+ virtual css::uno::Sequence< css::uno::Type > SAL_CALL getTypes( ) override;
+ virtual css::uno::Sequence< sal_Int8 > SAL_CALL getImplementationId() override;
+
+// OComponentHelper
+ virtual void SAL_CALL disposing() override;
+
+// css::awt::XTextComponent
+ virtual void SAL_CALL addTextListener(const css::uno::Reference< css::awt::XTextListener >& l) override;
+ virtual void SAL_CALL removeTextListener(const css::uno::Reference< css::awt::XTextListener >& l) override;
+ virtual void SAL_CALL setText(const OUString& aText) override;
+ virtual void SAL_CALL insertText(const css::awt::Selection& Sel, const OUString& Text) override;
+ virtual OUString SAL_CALL getText() override;
+ virtual OUString SAL_CALL getSelectedText() override;
+ virtual void SAL_CALL setSelection(const css::awt::Selection& aSelection) override;
+ virtual css::awt::Selection SAL_CALL getSelection() override;
+ virtual sal_Bool SAL_CALL isEditable() override;
+ virtual void SAL_CALL setEditable(sal_Bool bEditable) override;
+ virtual void SAL_CALL setMaxTextLen(sal_Int16 nLen) override;
+ virtual sal_Int16 SAL_CALL getMaxTextLen() override;
+
+ // XChangeBroadcaster
+ virtual void SAL_CALL addChangeListener( const css::uno::Reference< css::form::XChangeListener >& aListener ) override;
+ virtual void SAL_CALL removeChangeListener( const css::uno::Reference< css::form::XChangeListener >& aListener ) override;
+
+private:
+ virtual ~FmXEditCell() override;
+
+ virtual void onFocusGained( const css::awt::FocusEvent& _rEvent ) override;
+ virtual void onFocusLost( const css::awt::FocusEvent& _rEvent ) override;
+
+ DECL_LINK(ModifyHdl, LinkParamNone*, void);
+
+ void onTextChanged();
+
+ OUString m_sValueOnEnter;
+ ::comphelper::OInterfaceContainerHelper3<css::awt::XTextListener> m_aTextListeners;
+ ::comphelper::OInterfaceContainerHelper3<css::form::XChangeListener> m_aChangeListeners;
+ ::svt::IEditImplementation* m_pEditImplementation;
+ bool m_bOwnEditImplementation;
+};
+
+typedef ::cppu::ImplHelper2 < css::awt::XCheckBox
+ , css::awt::XButton
+ > FmXCheckBoxCell_Base;
+class FmXCheckBoxCell final : public FmXDataCell,
+ public FmXCheckBoxCell_Base
+{
+ ::comphelper::OInterfaceContainerHelper3<css::awt::XItemListener> m_aItemListeners;
+ ::comphelper::OInterfaceContainerHelper3<css::awt::XActionListener> m_aActionListeners;
+ OUString m_aActionCommand;
+ VclPtr<::svt::CheckBoxControl> m_pBox;
+
+ DECL_LINK(ModifyHdl, LinkParamNone*, void);
+
+ virtual ~FmXCheckBoxCell() override;
+
+public:
+ FmXCheckBoxCell( DbGridColumn* pColumn, std::unique_ptr<DbCellControl> pControl );
+
+// UNO
+ DECLARE_UNO3_AGG_DEFAULTS(FmXCheckBoxCell, FmXDataCell)
+ virtual css::uno::Any SAL_CALL queryAggregation( const css::uno::Type& _rType ) override;
+ virtual css::uno::Sequence< css::uno::Type > SAL_CALL getTypes( ) override;
+ virtual css::uno::Sequence< sal_Int8 > SAL_CALL getImplementationId() override;
+
+// OComponentHelper
+ virtual void SAL_CALL disposing() override;
+
+// css::awt::XCheckBox
+ virtual void SAL_CALL addItemListener(const css::uno::Reference< css::awt::XItemListener >& l) override;
+ virtual void SAL_CALL removeItemListener(const css::uno::Reference< css::awt::XItemListener >& l) override;
+ virtual sal_Int16 SAL_CALL getState() override;
+ virtual void SAL_CALL setState(sal_Int16 n) override;
+ virtual void SAL_CALL setLabel(const OUString& Label) override;
+ virtual void SAL_CALL enableTriState(sal_Bool b) override;
+
+ // XButton
+ virtual void SAL_CALL addActionListener( const css::uno::Reference< css::awt::XActionListener >& l ) override;
+ virtual void SAL_CALL removeActionListener( const css::uno::Reference< css::awt::XActionListener >& l ) override;
+ //virtual void SAL_CALL setLabel( const OUString& Label ) throw (css::uno::RuntimeException);
+ virtual void SAL_CALL setActionCommand( const OUString& Command ) override;
+};
+
+typedef ::cppu::ImplHelper1 < css::awt::XListBox
+ > FmXListBoxCell_Base;
+class FmXListBoxCell final : public FmXTextCell
+ , public FmXListBoxCell_Base
+{
+public:
+ FmXListBoxCell( DbGridColumn* pColumn, std::unique_ptr<DbCellControl> pControl );
+
+ DECLARE_UNO3_AGG_DEFAULTS(FmXListBoxCell, FmXTextCell)
+ virtual css::uno::Any SAL_CALL queryAggregation( const css::uno::Type& _rType ) override;
+ virtual css::uno::Sequence< css::uno::Type > SAL_CALL getTypes( ) override;
+ virtual css::uno::Sequence< sal_Int8 > SAL_CALL getImplementationId() override;
+
+// OComponentHelper
+ virtual void SAL_CALL disposing() override;
+
+// css::awt::XListBox
+ virtual void SAL_CALL addItemListener(const css::uno::Reference< css::awt::XItemListener >& l) override;
+ virtual void SAL_CALL removeItemListener(const css::uno::Reference< css::awt::XItemListener >& l) override;
+ virtual void SAL_CALL addActionListener(const css::uno::Reference< css::awt::XActionListener >& l) override;
+ virtual void SAL_CALL removeActionListener(const css::uno::Reference< css::awt::XActionListener >& l) override;
+ virtual void SAL_CALL addItem(const OUString& aItem, sal_Int16 nPos) override;
+ virtual void SAL_CALL addItems(const css::uno::Sequence< OUString >& aItems, sal_Int16 nPos) override;
+ virtual void SAL_CALL removeItems(sal_Int16 nPos, sal_Int16 nCount) override;
+ virtual sal_Int16 SAL_CALL getItemCount() override;
+ virtual OUString SAL_CALL getItem(sal_Int16 nPos) override;
+ virtual css::uno::Sequence< OUString > SAL_CALL getItems() override;
+ virtual sal_Int16 SAL_CALL getSelectedItemPos() override;
+ virtual css::uno::Sequence< sal_Int16 > SAL_CALL getSelectedItemsPos() override;
+ virtual OUString SAL_CALL getSelectedItem() override;
+ virtual css::uno::Sequence< OUString > SAL_CALL getSelectedItems() override;
+ virtual void SAL_CALL selectItemPos(sal_Int16 nPos, sal_Bool bSelect) override;
+ virtual void SAL_CALL selectItemsPos(const css::uno::Sequence< sal_Int16 >& aPositions, sal_Bool bSelect) override;
+ virtual void SAL_CALL selectItem(const OUString& aItem, sal_Bool bSelect) override;
+ virtual sal_Bool SAL_CALL isMutipleMode() override;
+ virtual void SAL_CALL setMultipleMode(sal_Bool bMulti) override;
+ virtual sal_Int16 SAL_CALL getDropDownLineCount() override;
+ virtual void SAL_CALL setDropDownLineCount(sal_Int16 nLines) override;
+ virtual void SAL_CALL makeVisible(sal_Int16 nEntry) override;
+
+private:
+ virtual ~FmXListBoxCell() override;
+
+ DECL_LINK(ChangedHdl, bool, void);
+
+ void OnDoubleClick();
+
+ ::comphelper::OInterfaceContainerHelper3<css::awt::XItemListener> m_aItemListeners;
+ ::comphelper::OInterfaceContainerHelper3<css::awt::XActionListener> m_aActionListeners;
+ VclPtr<::svt::ListBoxControl> m_pBox;
+ sal_uInt16 m_nLines;
+ bool m_bMulti;
+};
+
+
+typedef ::cppu::ImplHelper1 < css::awt::XComboBox
+ > FmXComboBoxCell_Base;
+class FmXComboBoxCell final : public FmXTextCell
+ , public FmXComboBoxCell_Base
+{
+private:
+ ::comphelper::OInterfaceContainerHelper3<css::awt::XItemListener> m_aItemListeners;
+ ::comphelper::OInterfaceContainerHelper3<css::awt::XActionListener> m_aActionListeners;
+ VclPtr<::svt::ComboBoxControl> m_pComboBox;
+ sal_uInt16 m_nLines;
+
+ DECL_LINK(ChangedHdl, bool, void);
+
+ virtual ~FmXComboBoxCell() override;
+
+public:
+ FmXComboBoxCell( DbGridColumn* pColumn, std::unique_ptr<DbCellControl> pControl );
+
+ DECLARE_UNO3_AGG_DEFAULTS(FmXListBoxCell, FmXTextCell)
+ virtual css::uno::Any SAL_CALL queryAggregation( const css::uno::Type& _rType ) override;
+ virtual css::uno::Sequence< css::uno::Type > SAL_CALL getTypes( ) override;
+ virtual css::uno::Sequence< sal_Int8 > SAL_CALL getImplementationId() override;
+
+ // OComponentHelper
+ virtual void SAL_CALL disposing() override;
+
+ // XComboBox
+ virtual void SAL_CALL addItemListener( const css::uno::Reference< css::awt::XItemListener >& Listener ) override;
+ virtual void SAL_CALL removeItemListener( const css::uno::Reference< css::awt::XItemListener >& Listener ) override;
+ virtual void SAL_CALL addActionListener( const css::uno::Reference< css::awt::XActionListener >& Listener ) override;
+ virtual void SAL_CALL removeActionListener( const css::uno::Reference< css::awt::XActionListener >& Listener ) override;
+ virtual void SAL_CALL addItem( const OUString& Item, ::sal_Int16 Pos ) override;
+ virtual void SAL_CALL addItems( const css::uno::Sequence< OUString >& Items, ::sal_Int16 Pos ) override;
+ virtual void SAL_CALL removeItems( ::sal_Int16 nPos, ::sal_Int16 nCount ) override;
+ virtual ::sal_Int16 SAL_CALL getItemCount( ) override;
+ virtual OUString SAL_CALL getItem( ::sal_Int16 Pos ) override;
+ virtual css::uno::Sequence< OUString > SAL_CALL getItems( ) override;
+ virtual ::sal_Int16 SAL_CALL getDropDownLineCount( ) override;
+ virtual void SAL_CALL setDropDownLineCount( ::sal_Int16 Lines ) override;
+};
+
+typedef ::cppu::ImplHelper2 < css::awt::XTextComponent
+ , css::lang::XUnoTunnel
+ > FmXFilterCell_Base;
+class FmXFilterCell final : public FmXGridCell
+ ,public FmXFilterCell_Base
+{
+public:
+ FmXFilterCell(DbGridColumn* pColumn, std::unique_ptr<DbFilterField> pControl);
+
+
+ DECLARE_UNO3_AGG_DEFAULTS(FmXFilterCell, FmXGridCell)
+ virtual css::uno::Any SAL_CALL queryAggregation( const css::uno::Type& _rType ) override;
+ virtual css::uno::Sequence< css::uno::Type > SAL_CALL getTypes( ) override;
+ virtual css::uno::Sequence< sal_Int8 > SAL_CALL getImplementationId() override;
+
+// XUnoTunnel
+ virtual sal_Int64 SAL_CALL getSomething( const css::uno::Sequence< sal_Int8 >& aIdentifier ) override;
+
+// helpers for XUnoTunnel
+ static const css::uno::Sequence<sal_Int8>& getUnoTunnelId();
+
+// painting the filter text
+ void PaintCell(OutputDevice& rDev, const tools::Rectangle& rRect);
+ void Update(){m_pCellControl->Update();}
+
+// OComponentHelper
+ virtual void SAL_CALL disposing() override;
+
+// css::awt::XTextComponent
+ virtual void SAL_CALL addTextListener(const css::uno::Reference< css::awt::XTextListener >& l) override;
+ virtual void SAL_CALL removeTextListener(const css::uno::Reference< css::awt::XTextListener >& l) override;
+ virtual void SAL_CALL setText(const OUString& aText) override;
+ virtual void SAL_CALL insertText(const css::awt::Selection& Sel, const OUString& Text) override;
+ virtual OUString SAL_CALL getText() override;
+ virtual OUString SAL_CALL getSelectedText() override;
+ virtual void SAL_CALL setSelection(const css::awt::Selection& aSelection) override;
+ virtual css::awt::Selection SAL_CALL getSelection() override;
+ virtual sal_Bool SAL_CALL isEditable() override;
+ virtual void SAL_CALL setEditable(sal_Bool bEditable) override;
+ virtual void SAL_CALL setMaxTextLen(sal_Int16 nLen) override;
+ virtual sal_Int16 SAL_CALL getMaxTextLen() override;
+
+private:
+ DECL_LINK( OnCommit, DbFilterField&, void );
+ virtual ~FmXFilterCell() override;
+
+ ::comphelper::OInterfaceContainerHelper3<css::awt::XTextListener> m_aTextListeners;
+};
+
+#endif // INCLUDED_SVX_SOURCE_INC_GRIDCELL_HXX
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/inc/gridcols.hxx b/svx/source/inc/gridcols.hxx
new file mode 100644
index 000000000..180337f7f
--- /dev/null
+++ b/svx/source/inc/gridcols.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_SVX_SOURCE_INC_GRIDCOLS_HXX
+#define INCLUDED_SVX_SOURCE_INC_GRIDCOLS_HXX
+
+#include <sal/types.h>
+#include <rtl/ustring.hxx>
+
+#define FM_COL_TEXTFIELD "TextField"
+#define FM_COL_COMBOBOX "ComboBox"
+#define FM_COL_CHECKBOX "CheckBox"
+#define FM_COL_TIMEFIELD "TimeField"
+#define FM_COL_DATEFIELD "DateField"
+#define FM_COL_NUMERICFIELD "NumericField"
+#define FM_COL_CURRENCYFIELD "CurrencyField"
+#define FM_COL_PATTERNFIELD "PatternField"
+#define FM_COL_LISTBOX "ListBox"
+#define FM_COL_FORMATTEDFIELD "FormattedField"
+
+// column type ids
+#define TYPE_CHECKBOX 0
+#define TYPE_COMBOBOX 1
+#define TYPE_CURRENCYFIELD 2
+#define TYPE_DATEFIELD 3
+#define TYPE_FORMATTEDFIELD 4
+#define TYPE_LISTBOX 5
+#define TYPE_NUMERICFIELD 6
+#define TYPE_PATTERNFIELD 7
+#define TYPE_TEXTFIELD 8
+#define TYPE_TIMEFIELD 9
+
+
+sal_Int32 getColumnTypeByModelName(const OUString& aModelName);
+
+
+#endif // INCLUDED_SVX_SOURCE_INC_GRIDCOLS_HXX
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/inc/sdbdatacolumn.hxx b/svx/source/inc/sdbdatacolumn.hxx
new file mode 100644
index 000000000..76d8039c8
--- /dev/null
+++ b/svx/source/inc/sdbdatacolumn.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_SVX_SOURCE_INC_SDBDATACOLUMN_HXX
+#define INCLUDED_SVX_SOURCE_INC_SDBDATACOLUMN_HXX
+
+#include <com/sun/star/beans/XPropertySet.hpp>
+#include <com/sun/star/sdb/XColumn.hpp>
+#include <com/sun/star/sdb/XColumnUpdate.hpp>
+
+
+namespace svxform
+{
+
+
+ //= DataColumn - a class wrapping an object implementing a sdb::DataColumn service
+
+ class DataColumn
+ {
+ // interfaces needed for sddb::Column
+ css::uno::Reference< css::beans::XPropertySet> m_xPropertySet;
+ // interfaces needed for sdb::DataColumn
+ css::uno::Reference< css::sdb::XColumn> m_xColumn;
+ css::uno::Reference< css::sdb::XColumnUpdate> m_xColumnUpdate;
+
+ public:
+ DataColumn(const css::uno::Reference< css::beans::XPropertySet>& _rxIFace);
+ // if the object behind _rxIFace doesn't fully support the DataColumn service,
+ // (which is checked via the supported interfaces) _all_ members will be set to
+ // void !, even if the object has some of the needed interfaces.
+
+ // 'conversions'
+ const css::uno::Reference< css::sdb::XColumn>& getColumn() const
+ {
+ return m_xColumn;
+ }
+ };
+
+}
+
+#endif // INCLUDED_SVX_SOURCE_INC_SDBDATACOLUMN_HXX
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/inc/sqlparserclient.hxx b/svx/source/inc/sqlparserclient.hxx
new file mode 100644
index 000000000..a14ea3671
--- /dev/null
+++ b/svx/source/inc/sqlparserclient.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_SVX_SOURCE_INC_SQLPARSERCLIENT_HXX
+#define INCLUDED_SVX_SOURCE_INC_SQLPARSERCLIENT_HXX
+
+#include <config_options.h>
+#include <svx/ParseContext.hxx>
+
+#include <com/sun/star/uno/XComponentContext.hpp>
+
+#include <memory>
+
+namespace com::sun::star {
+ namespace util {
+ class XNumberFormatter;
+ }
+ namespace beans {
+ class XPropertySet;
+ }
+}
+namespace connectivity {
+ class OSQLParser;
+ class OSQLParseNode;
+}
+
+namespace svxform
+{
+ //= OSQLParserClient
+
+ class UNLESS_MERGELIBS(SVXCORE_DLLPUBLIC) OSQLParserClient : public ::svxform::OParseContextClient
+ {
+ protected:
+ mutable std::shared_ptr< ::connectivity::OSQLParser > m_pParser;
+
+ OSQLParserClient(
+ const css::uno::Reference< css::uno::XComponentContext >& rxContext);
+
+ std::unique_ptr< ::connectivity::OSQLParseNode > predicateTree(
+ OUString& _rErrorMessage,
+ const OUString& _rStatement,
+ const css::uno::Reference< css::util::XNumberFormatter >& _rxFormatter,
+ const css::uno::Reference< css::beans::XPropertySet >& _rxField
+ ) const;
+ };
+
+
+}
+
+
+#endif // INCLUDED_SVX_SOURCE_INC_SQLPARSERCLIENT_HXX
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/inc/svdobjplusdata.hxx b/svx/source/inc/svdobjplusdata.hxx
new file mode 100644
index 000000000..5e00a37c5
--- /dev/null
+++ b/svx/source/inc/svdobjplusdata.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/.
+*/
+
+#ifndef INCLUDED_SVX_SVDOBJPLUSDATA_HXX
+#define INCLUDED_SVX_SVDOBJPLUSDATA_HXX
+
+#include <rtl/ustring.hxx>
+#include <memory>
+
+class SdrObject;
+class SfxBroadcaster;
+class SdrObjUserDataList;
+class SdrGluePointList;
+
+// Bitsack for DrawObjects
+class SdrObjPlusData final
+{
+ friend class SdrObject;
+
+ std::unique_ptr<SfxBroadcaster> pBroadcast; // broadcaster, if this object is referenced (bVirtObj=true). Also for connectors etc.
+ std::unique_ptr<SdrObjUserDataList> pUserDataList; // application specific data
+ std::unique_ptr<SdrGluePointList> pGluePoints; // gluepoints for glueing object connectors
+
+ // #i68101#
+ // object name, title and description
+ OUString aObjName;
+ OUString aObjTitle;
+ OUString aObjDescription;
+
+public:
+ SdrObjPlusData();
+ ~SdrObjPlusData();
+ SdrObjPlusData* Clone(SdrObject* pObj1) const;
+
+ void SetGluePoints(const SdrGluePointList& rPts);
+};
+
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/inc/svdobjuserdatalist.hxx b/svx/source/inc/svdobjuserdatalist.hxx
new file mode 100644
index 000000000..d2cb0e16a
--- /dev/null
+++ b/svx/source/inc/svdobjuserdatalist.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_SVX_SVDOBJUSERDATALIST_HXX
+#define INCLUDED_SVX_SVDOBJUSERDATALIST_HXX
+
+#include <svx/svdobj.hxx>
+
+#include <vector>
+#include <memory>
+
+class SdrObjUserData;
+
+class SdrObjUserDataList
+{
+ typedef std::vector<std::unique_ptr<SdrObjUserData>> ListType;
+ ListType maList;
+
+public:
+ SdrObjUserDataList();
+ ~SdrObjUserDataList();
+
+ size_t GetUserDataCount() const;
+ SdrObjUserData& GetUserData(size_t nNum);
+ void AppendUserData(std::unique_ptr<SdrObjUserData> pData);
+ void DeleteUserData(size_t nNum);
+};
+
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/inc/svdoutlinercache.hxx b/svx/source/inc/svdoutlinercache.hxx
new file mode 100644
index 000000000..01679f6b9
--- /dev/null
+++ b/svx/source/inc/svdoutlinercache.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_SVX_SOURCE_INC_SVDOUTLINERCACHE_HXX
+#define INCLUDED_SVX_SOURCE_INC_SVDOUTLINERCACHE_HXX
+
+#include <memory>
+#include <vector>
+#include <o3tl/sorted_vector.hxx>
+
+class SdrModel;
+class SdrOutliner;
+enum class OutlinerMode;
+
+class SdrOutlinerCache
+{
+private:
+ SdrModel* mpModel;
+ std::vector< std::unique_ptr<SdrOutliner> > maModeOutline;
+ std::vector< std::unique_ptr<SdrOutliner> > maModeText;
+ o3tl::sorted_vector< SdrOutliner* > maActiveOutliners;
+
+public:
+ SdrOutlinerCache( SdrModel* pModel );
+ ~SdrOutlinerCache();
+
+ std::unique_ptr<SdrOutliner> createOutliner( OutlinerMode nOutlinerMode );
+ void disposeOutliner( std::unique_ptr<SdrOutliner> pOutliner );
+ std::vector< SdrOutliner* > GetActiveOutliners() const;
+};
+
+#endif
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/inc/svdpdf.hxx b/svx/source/inc/svdpdf.hxx
new file mode 100644
index 000000000..f8736a7bc
--- /dev/null
+++ b/svx/source/inc/svdpdf.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_SVX_SOURCE_SVDRAW_SVDPDF_HXX
+#define INCLUDED_SVX_SOURCE_SVDRAW_SVDPDF_HXX
+
+#include <sal/config.h>
+
+#include <memory>
+
+#include <tools/fract.hxx>
+#include <vcl/virdev.hxx>
+#include <vcl/graph.hxx>
+#include <svx/svdobj.hxx>
+#include <svx/xdash.hxx>
+
+#include <basegfx/matrix/b2dhommatrix.hxx>
+
+#include <vcl/filter/PDFiumLibrary.hxx>
+
+// Forward Declarations
+
+class SfxItemSet;
+class SdrObjList;
+class SdrModel;
+class SdrPage;
+class SdrObject;
+class SvdProgressInfo;
+
+// Helper Class to import PDF
+class ImpSdrPdfImport final
+{
+ std::vector<SdrObject*> maTmpList;
+ ScopedVclPtr<VirtualDevice> mpVD;
+ tools::Rectangle maScaleRect;
+ size_t mnMapScalingOfs; // from here on, not edited with MapScaling
+ std::unique_ptr<SfxItemSet> mpLineAttr;
+ std::unique_ptr<SfxItemSet> mpFillAttr;
+ std::unique_ptr<SfxItemSet> mpTextAttr;
+ SdrModel* mpModel;
+ SdrLayerID mnLayer;
+ Color maOldLineColor;
+ sal_Int32 mnLineWidth;
+ static constexpr css::drawing::LineCap gaLineCap = css::drawing::LineCap_BUTT;
+ XDash maDash;
+
+ bool mbMov;
+ bool mbSize;
+ Point maOfs;
+ double mfScaleX;
+ double mfScaleY;
+ Fraction maScaleX;
+ Fraction maScaleY;
+
+ bool mbFntDirty;
+
+ // to optimize (PenNULL,Brush,DrawPoly),(Pen,BrushNULL,DrawPoly) -> two-in-one
+ bool mbLastObjWasPolyWithoutLine;
+ bool mbNoLine;
+ bool mbNoFill;
+
+ // clipregion
+ basegfx::B2DPolyPolygon maClip;
+
+ std::unique_ptr<vcl::pdf::PDFiumDocument> mpPdfDocument;
+ int mnPageCount;
+ double mdPageHeightPts;
+ /// The current transformation matrix, typically used with Form objects.
+ basegfx::B2DHomMatrix maCurrentMatrix;
+
+ /// Correct the vertical coordinate to start at the top.
+ /// PDF coordinate system has origin at the bottom right.
+ double correctVertOrigin(double offsetPts) const { return mdPageHeightPts - offsetPts; }
+ /// Convert PDF points to logic (twips).
+ tools::Rectangle PointsToLogic(double left, double right, double top, double bottom) const;
+ Point PointsToLogic(double x, double y) const;
+
+ std::shared_ptr<vcl::pdf::PDFium> mpPDFium;
+
+ // check for clip and evtl. fill maClip
+ void checkClip();
+ bool isClip() const;
+
+ void ImportPdfObject(std::unique_ptr<vcl::pdf::PDFiumPageObject> const& pPageObject,
+ std::unique_ptr<vcl::pdf::PDFiumTextPage> const& pTextPage,
+ int nPageObjectIndex);
+ void ImportForm(std::unique_ptr<vcl::pdf::PDFiumPageObject> const& pPageObject,
+ std::unique_ptr<vcl::pdf::PDFiumTextPage> const& pTextPage,
+ int nPageObjectIndex);
+ void ImportImage(std::unique_ptr<vcl::pdf::PDFiumPageObject> const& pPageObject,
+ int nPageObjectIndex);
+ void ImportPath(std::unique_ptr<vcl::pdf::PDFiumPageObject> const& pPageObject,
+ int nPageObjectIndex);
+ void ImportText(std::unique_ptr<vcl::pdf::PDFiumPageObject> const& pPageObject,
+ std::unique_ptr<vcl::pdf::PDFiumTextPage> const& pTextPage,
+ int nPageObjectIndex);
+ void InsertTextObject(const Point& rPos, const Size& rSize, const OUString& rStr);
+
+ void SetupPageScale(const double dPageWidth, const double dPageHeight);
+ void SetAttributes(SdrObject* pObj, bool bForceTextAttr = false);
+ void InsertObj(SdrObject* pObj, bool bScale = true);
+ void MapScaling();
+
+ // #i73407# reformulation to use new B2DPolygon classes
+ bool CheckLastPolyLineAndFillMerge(const basegfx::B2DPolyPolygon& rPolyPolygon);
+
+ void DoObjects(SvdProgressInfo* pProgrInfo, sal_uInt32* pActionsToReport, int nPageIndex);
+
+ // Copy assignment is forbidden and not implemented.
+ ImpSdrPdfImport(const ImpSdrPdfImport&) = delete;
+ ImpSdrPdfImport& operator=(const ImpSdrPdfImport&) = delete;
+
+public:
+ ImpSdrPdfImport(SdrModel& rModel, SdrLayerID nLay, const tools::Rectangle& rRect,
+ Graphic const& rGraphic);
+
+ ~ImpSdrPdfImport();
+
+ int GetPageCount() const { return mnPageCount; }
+ size_t DoImport(SdrObjList& rDestList, size_t nInsPos, int nPageNumber,
+ SvdProgressInfo* pProgrInfo = nullptr);
+};
+
+#endif // INCLUDED_SVX_SOURCE_SVDRAW_SVDPDF_HXX
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/inc/svxpixelctlaccessiblecontext.hxx b/svx/source/inc/svxpixelctlaccessiblecontext.hxx
new file mode 100644
index 000000000..2e0f0abcf
--- /dev/null
+++ b/svx/source/inc/svxpixelctlaccessiblecontext.hxx
@@ -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 .
+ */
+#ifndef INCLUDED_SVX_SOURCE_INC_SVXPIXELCTLACCESSIBLECONTEXT_HXX
+#define INCLUDED_SVX_SOURCE_INC_SVXPIXELCTLACCESSIBLECONTEXT_HXX
+
+#include <com/sun/star/accessibility/XAccessible.hpp>
+#include <com/sun/star/accessibility/XAccessibleContext.hpp>
+
+#include <com/sun/star/uno/Reference.hxx>
+#include <comphelper/accessibleselectionhelper.hxx>
+
+#include <rtl/ref.hxx>
+#include <tools/gen.hxx>
+
+
+namespace com::sun::star::awt {
+ struct Point;
+ struct Rectangle;
+ struct Size;
+ class XFocusListener;
+}
+class SvxPixelCtl;
+class SvxPixelCtlAccessible;
+
+typedef ::cppu::ImplHelper1<css::accessibility::XAccessible> OAccessibleHelper_Base;
+
+class SvxPixelCtlAccessibleChild final : public ::comphelper::OAccessibleComponentHelper,
+ public OAccessibleHelper_Base
+{
+public:
+ SvxPixelCtlAccessibleChild(
+ SvxPixelCtl& rWindow,
+ bool bPixelColorOrBG,
+ const tools::Rectangle& rBounds,
+ const rtl::Reference<SvxPixelCtlAccessible>& xParent,
+ tools::Long nIndexInParent );
+
+ DECLARE_XINTERFACE( )
+ DECLARE_XTYPEPROVIDER( )
+
+ //XAccessibleComponent
+ virtual void SAL_CALL grabFocus( ) override;
+ virtual css::uno::Reference< css::accessibility::XAccessible > SAL_CALL getAccessibleAtPoint( const css::awt::Point& aPoint ) override;
+
+ //XAccessibleContext
+ virtual sal_Int32 SAL_CALL getAccessibleChildCount( ) override;
+ virtual css::uno::Reference< css::accessibility::XAccessible > SAL_CALL getAccessibleChild( sal_Int32 i ) override;
+ virtual css::uno::Reference< css::accessibility::XAccessible > SAL_CALL getAccessibleParent( ) override;
+ virtual sal_Int16 SAL_CALL getAccessibleRole( ) override;
+ virtual OUString SAL_CALL getAccessibleDescription( ) override;
+ virtual OUString SAL_CALL getAccessibleName( ) override;
+ virtual css::uno::Reference< css::accessibility::XAccessibleRelationSet > SAL_CALL getAccessibleRelationSet( ) override;
+ virtual css::uno::Reference< css::accessibility::XAccessibleStateSet > SAL_CALL getAccessibleStateSet( ) override;
+
+ //XAccessible
+ virtual css::uno::Reference< css::accessibility::XAccessibleContext > SAL_CALL getAccessibleContext( ) override;
+
+ virtual sal_Int32 SAL_CALL getForeground( ) override;
+ virtual sal_Int32 SAL_CALL getBackground( ) override;
+
+ void CheckChild();
+ void SelectChild( bool bSelect);
+ void ChangePixelColorOrBG(bool bPixelColorOrBG){ m_bPixelColorOrBG = bPixelColorOrBG ;}
+ OUString GetName() const;
+
+private:
+ virtual ~SvxPixelCtlAccessibleChild() override;
+
+ virtual void SAL_CALL disposing() override;
+
+ // OCommonAccessibleComponent
+ /// implements the calculation of the bounding rectangle
+ virtual css::awt::Rectangle implGetBounds( ) override;
+
+ SvxPixelCtl& mrParentWindow;
+ rtl::Reference<SvxPixelCtlAccessible> mxParent;
+ bool m_bPixelColorOrBG;//Pixel Color Or BackGround Color
+ tools::Rectangle maBoundingBox;
+ /// index of child in parent
+ tools::Long mnIndexInParent;
+};
+
+class SvxPixelCtlAccessible final : public ::comphelper::OAccessibleSelectionHelper,
+ public OAccessibleHelper_Base
+{
+public:
+ SvxPixelCtlAccessible(SvxPixelCtl* pPixelCtl);
+
+ DECLARE_XINTERFACE( )
+ DECLARE_XTYPEPROVIDER( )
+
+ //XAccessibleComponent
+ virtual void SAL_CALL grabFocus( ) override;
+ virtual css::uno::Reference< css::accessibility::XAccessible > SAL_CALL getAccessibleAtPoint( const css::awt::Point& aPoint ) override;
+
+ //XAccessible
+ //XAccessibleContext
+ virtual sal_Int32 SAL_CALL getAccessibleChildCount( ) override;
+ virtual css::uno::Reference< css::accessibility::XAccessible > SAL_CALL getAccessibleChild( sal_Int32 i ) override;
+ virtual css::uno::Reference< css::accessibility::XAccessible > SAL_CALL getAccessibleParent( ) override;
+ virtual sal_Int16 SAL_CALL getAccessibleRole( ) override;
+ virtual OUString SAL_CALL getAccessibleDescription( ) override;
+ virtual OUString SAL_CALL getAccessibleName( ) override;
+ virtual css::uno::Reference< css::accessibility::XAccessibleRelationSet > SAL_CALL getAccessibleRelationSet( ) override;
+ virtual css::uno::Reference< css::accessibility::XAccessibleStateSet > SAL_CALL getAccessibleStateSet( ) override;
+
+ virtual css::uno::Reference< css::accessibility::XAccessibleContext > SAL_CALL getAccessibleContext( ) override;
+ virtual sal_Int32 SAL_CALL getForeground( ) override;
+ virtual sal_Int32 SAL_CALL getBackground( ) override;
+
+ css::uno::Reference< css::accessibility::XAccessible >
+ CreateChild (tools::Long nIndex, Point mPoint);
+
+private:
+ virtual ~SvxPixelCtlAccessible() override;
+
+ // OCommonAccessibleSelection
+ // return if the specified child is visible => watch for special ChildIndexes (ACCESSIBLE_SELECTION_CHILD_xxx)
+ virtual bool implIsSelected(sal_Int32 nAccessibleChildIndex) override;
+
+ // select the specified child => watch for special ChildIndexes (ACCESSIBLE_SELECTION_CHILD_xxx)
+ virtual void implSelect(sal_Int32 nAccessibleChildIndex, bool bSelect) override;
+
+ // OCommonAccessibleComponent
+ virtual css::awt::Rectangle implGetBounds() override;
+
+ virtual void SAL_CALL disposing() override;
+
+ SvxPixelCtl* mpPixelCtl;
+ css::uno::Reference< css::accessibility::XAccessible> m_xCurChild;
+
+public:
+ void NotifyChild(tools::Long nIndex, bool bSelect, bool bCheck);
+};
+
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/inc/svxrectctaccessiblecontext.hxx b/svx/source/inc/svxrectctaccessiblecontext.hxx
new file mode 100644
index 000000000..ef20985a4
--- /dev/null
+++ b/svx/source/inc/svxrectctaccessiblecontext.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_SVX_SOURCE_INC_SVXRECTCTACCESSIBLECONTEXT_HXX
+#define INCLUDED_SVX_SOURCE_INC_SVXRECTCTACCESSIBLECONTEXT_HXX
+
+#include <com/sun/star/accessibility/XAccessible.hpp>
+#include <com/sun/star/accessibility/XAccessibleContext.hpp>
+#include <com/sun/star/accessibility/XAccessibleAction.hpp>
+#include <com/sun/star/accessibility/XAccessibleValue.hpp>
+#include <com/sun/star/uno/Reference.hxx>
+#include <cppuhelper/implbase3.hxx>
+#include <comphelper/accessibleselectionhelper.hxx>
+#include <rtl/ref.hxx>
+#include <svx/rectenum.hxx>
+#include <tools/gen.hxx>
+#include <vector>
+
+namespace com::sun::star::awt {
+ struct Point;
+ struct Rectangle;
+ struct Size;
+ class XFocusListener;
+}
+namespace tools { class Rectangle; }
+class SvxRectCtl;
+class SvxRectCtlChildAccessibleContext;
+
+typedef ::cppu::ImplHelper1<css::accessibility::XAccessible> OAccessibleHelper_Base;
+
+class SvxRectCtlAccessibleContext final : public ::comphelper::OAccessibleSelectionHelper,
+ public OAccessibleHelper_Base
+{
+public:
+ // internal
+ SvxRectCtlAccessibleContext(SvxRectCtl* pRepresentation);
+
+ DECLARE_XINTERFACE( )
+ DECLARE_XTYPEPROVIDER( )
+
+ // XAccessibleComponent
+ virtual void SAL_CALL grabFocus() override;
+ virtual css::uno::Reference< css::accessibility::XAccessible > SAL_CALL getAccessibleAtPoint(const css::awt::Point& rPoint) override;
+
+ // XAccessible
+ // XAccessibleContext
+ virtual sal_Int32 SAL_CALL getAccessibleChildCount() override;
+ virtual css::uno::Reference< css::accessibility::XAccessible> SAL_CALL getAccessibleChild(sal_Int32 nIndex) override;
+ virtual css::uno::Reference< css::accessibility::XAccessible> SAL_CALL getAccessibleParent() override;
+ virtual sal_Int16 SAL_CALL getAccessibleRole() override;
+ virtual OUString SAL_CALL getAccessibleDescription() override;
+ virtual OUString SAL_CALL getAccessibleName() override;
+ virtual css::uno::Reference< css::accessibility::XAccessibleRelationSet > SAL_CALL getAccessibleRelationSet() override;
+ virtual css::uno::Reference< css::accessibility::XAccessibleStateSet > SAL_CALL getAccessibleStateSet() override;
+
+ virtual css::uno::Reference< css::accessibility::XAccessibleContext> SAL_CALL getAccessibleContext() override { return this; }
+ virtual sal_Int32 SAL_CALL getForeground() override;
+ virtual sal_Int32 SAL_CALL getBackground() override;
+
+ /** Selects a new child by point.
+
+ <p>If the child was not selected before, the state of the child will
+ be updated. If the point is not invalid, the index will internally set to NOCHILDSELECTED</p>
+
+ @param eButton
+ Button which belongs to the child which should be selected.
+ */
+ void selectChild( RectPoint ePoint );
+ void FireChildFocus( RectPoint eButton );
+
+private:
+ virtual ~SvxRectCtlAccessibleContext() override;
+
+ // OCommonAccessibleSelection
+ // return if the specified child is visible => watch for special ChildIndexes (ACCESSIBLE_SELECTION_CHILD_xxx)
+ virtual bool implIsSelected(sal_Int32 nAccessibleChildIndex) override;
+
+ // select the specified child => watch for special ChildIndexes (ACCESSIBLE_SELECTION_CHILD_xxx)
+ virtual void implSelect(sal_Int32 nAccessibleChildIndex, bool bSelect) override;
+
+ // OCommonAccessibleComponent
+ virtual css::awt::Rectangle implGetBounds() override;
+
+ virtual void SAL_CALL disposing() override;
+
+ void checkChildIndex(tools::Long nIndex);
+
+ /** Selects a new child by index.
+
+ <p>If the child was not selected before, the state of the child will
+ be updated. If the index is invalid, the index will internally set to NOCHILDSELECTED</p>
+
+ @param nIndexOfChild
+ Index of the new child which should be selected.
+ */
+ void selectChild( tools::Long nIndexOfChild );
+
+ /** Description of this object. This is not a constant because it can
+ be set from the outside.
+ */
+ OUString msDescription;
+
+ /** Name of this object.
+ */
+ OUString msName;
+
+ /// pointer to internal representation
+ SvxRectCtl* mpRepr;
+
+ /// array for all possible children
+ std::vector<rtl::Reference<SvxRectCtlChildAccessibleContext>> mvChildren;
+
+ /// actual selected child
+ tools::Long mnSelectedChild;
+};
+
+typedef ::cppu::ImplHelper3 < css::accessibility::XAccessible,
+ css::accessibility::XAccessibleValue,
+ css::accessibility::XAccessibleAction
+ > OAccessibleHelper_Base_3;
+
+class SvxRectCtlChildAccessibleContext final : public ::comphelper::OAccessibleComponentHelper,
+ public OAccessibleHelper_Base_3
+{
+public:
+ SvxRectCtlChildAccessibleContext(
+ const css::uno::Reference< css::accessibility::XAccessible>& rxParent,
+ const OUString& rName, const OUString& rDescription,
+ const tools::Rectangle& rBoundingBox,
+ tools::Long nIndexInParent );
+
+ DECLARE_XINTERFACE( )
+ DECLARE_XTYPEPROVIDER( )
+
+ // XAccessibleComponent
+ virtual void SAL_CALL grabFocus() override;
+ virtual css::uno::Reference< css::accessibility::XAccessible > SAL_CALL getAccessibleAtPoint( const css::awt::Point& rPoint ) override;
+
+ // XAccessibleContext
+ virtual sal_Int32 SAL_CALL getAccessibleChildCount() override;
+ virtual css::uno::Reference< css::accessibility::XAccessible > SAL_CALL getAccessibleChild( sal_Int32 nIndex ) override;
+ virtual css::uno::Reference< css::accessibility::XAccessible > SAL_CALL getAccessibleParent() override;
+ virtual sal_Int16 SAL_CALL getAccessibleRole() override;
+ virtual OUString SAL_CALL getAccessibleDescription() override;
+ virtual OUString SAL_CALL getAccessibleName() override;
+ virtual css::uno::Reference< css::accessibility::XAccessibleRelationSet > SAL_CALL getAccessibleRelationSet() override;
+ virtual css::uno::Reference< css::accessibility::XAccessibleStateSet > SAL_CALL getAccessibleStateSet() override;
+
+ virtual css::uno::Reference< css::accessibility::XAccessibleContext> SAL_CALL getAccessibleContext() override { return this; }
+
+ virtual sal_Int32 SAL_CALL getForeground() override;
+ virtual sal_Int32 SAL_CALL getBackground() override;
+
+ // XAccessibleValue
+ virtual css::uno::Any SAL_CALL
+ getCurrentValue() override;
+
+ virtual sal_Bool SAL_CALL
+ setCurrentValue( const css::uno::Any& aNumber ) override;
+
+ virtual css::uno::Any SAL_CALL
+ getMaximumValue() override;
+
+ virtual css::uno::Any SAL_CALL
+ getMinimumValue() override;
+
+ virtual css::uno::Any SAL_CALL
+ getMinimumIncrement() override;
+
+ // 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;
+
+ // internal
+ /// Sets the checked status
+ void setStateChecked(bool bChecked);
+ void FireFocusEvent();
+
+private:
+ virtual ~SvxRectCtlChildAccessibleContext() override;
+
+ virtual void SAL_CALL disposing() override;
+
+ // OCommonAccessibleComponent
+ /// implements the calculation of the bounding rectangle
+ virtual css::awt::Rectangle implGetBounds( ) override;
+
+ /** Description of this object. This is not a constant because it can
+ be set from the outside. Furthermore, it changes according to the
+ draw page's display mode.
+ */
+ OUString msDescription;
+
+ /** Name of this object. It changes according the draw page's
+ display mode.
+ */
+ OUString msName;
+
+ /// Reference to the parent object.
+ css::uno::Reference< css::accessibility::XAccessible >
+ mxParent;
+
+ /// Bounding box
+ tools::Rectangle maBoundingBox;
+
+ /// index of child in parent
+ tools::Long mnIndexInParent;
+
+ /// Indicates, if object is checked
+ bool mbIsChecked;
+};
+
+
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/inc/tablemodel.hxx b/svx/source/inc/tablemodel.hxx
new file mode 100644
index 000000000..63c08a9b8
--- /dev/null
+++ b/svx/source/inc/tablemodel.hxx
@@ -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 .
+ */
+
+#ifndef INCLUDED_SVX_SOURCE_INC_TABLEMODEL_HXX
+#define INCLUDED_SVX_SOURCE_INC_TABLEMODEL_HXX
+
+#include <sal/types.h>
+#include <com/sun/star/util/XBroadcaster.hpp>
+#include <com/sun/star/table/XTable.hpp>
+#include <cppuhelper/compbase.hxx>
+#include <cppuhelper/basemutex.hxx>
+#include "celltypes.hxx"
+
+struct _xmlTextWriter;
+typedef struct _xmlTextWriter* xmlTextWriterPtr;
+
+namespace sdr::table {
+
+class SdrTableObj;
+
+/** base class for each object implementing an XCellRange */
+class ICellRange
+{
+public:
+ virtual sal_Int32 getLeft() = 0;
+ virtual sal_Int32 getTop() = 0;
+ virtual sal_Int32 getRight() = 0;
+ virtual sal_Int32 getBottom() = 0;
+ virtual css::uno::Reference< css::table::XTable > getTable() = 0;
+
+protected:
+ ~ICellRange() {}
+};
+
+typedef ::cppu::WeakComponentImplHelper< css::table::XTable, css::util::XBroadcaster > TableModelBase;
+
+class TableModel final : public ::cppu::BaseMutex,
+ public TableModelBase,
+ public ICellRange
+{
+ friend class InsertRowUndo;
+ friend class RemoveRowUndo;
+ friend class InsertColUndo;
+ friend class RemoveColUndo;
+ friend class TableColumnUndo;
+ friend class TableRowUndo;
+ friend class TableColumn;
+ friend class TableRow;
+ friend class TableRows;
+ friend class TableColumns;
+ friend class TableModelNotifyGuard;
+
+public:
+ explicit TableModel( SdrTableObj* pTableObj );
+ TableModel( SdrTableObj* pTableObj, const TableModelRef& xSourceTable );
+ virtual ~TableModel() override;
+
+ void init( sal_Int32 nColumns, sal_Int32 nRows );
+
+ SdrTableObj* getSdrTableObj() const { return mpTableObj; }
+
+ /** deletes rows and columns that are completely merged. Must be called between BegUndo/EndUndo! */
+ void optimize();
+
+ /// merges the cell at the given position with the given span
+ void merge( sal_Int32 nCol, sal_Int32 nRow, sal_Int32 nColSpan, sal_Int32 nRowSpan );
+ /// Get the width of all columns in this table.
+ std::vector<sal_Int32> getColumnWidths();
+
+ void dumpAsXml(xmlTextWriterPtr pWriter) const;
+
+ // ICellRange
+ virtual sal_Int32 getLeft() override;
+ virtual sal_Int32 getTop() override;
+ virtual sal_Int32 getRight() override;
+ virtual sal_Int32 getBottom() override;
+ virtual css::uno::Reference< css::table::XTable > getTable() override;
+
+ // XTable
+ virtual css::uno::Reference< css::table::XCellCursor > SAL_CALL createCursor( ) override;
+ virtual css::uno::Reference< css::table::XCellCursor > SAL_CALL createCursorByRange( const css::uno::Reference< css::table::XCellRange >& rRange ) override;
+ virtual ::sal_Int32 SAL_CALL getRowCount() override;
+ virtual ::sal_Int32 SAL_CALL getColumnCount() override;
+
+ // XComponent
+ virtual void SAL_CALL dispose( ) override;
+
+ // XModifiable
+ virtual sal_Bool SAL_CALL isModified( ) override;
+ virtual void SAL_CALL setModified( sal_Bool bModified ) override;
+
+ // XModifyBroadcaster
+ virtual void SAL_CALL addModifyListener( const css::uno::Reference< css::util::XModifyListener >& aListener ) override;
+ virtual void SAL_CALL removeModifyListener( const css::uno::Reference< css::util::XModifyListener >& aListener ) override;
+
+ // XColumnRowRange
+ virtual css::uno::Reference< css::table::XTableColumns > SAL_CALL getColumns() override;
+ virtual css::uno::Reference< css::table::XTableRows > SAL_CALL getRows() override;
+
+ // XCellRange
+ virtual css::uno::Reference< css::table::XCell > SAL_CALL getCellByPosition( ::sal_Int32 nColumn, ::sal_Int32 nRow ) override;
+ virtual css::uno::Reference< css::table::XCellRange > SAL_CALL getCellRangeByPosition( ::sal_Int32 nLeft, ::sal_Int32 nTop, ::sal_Int32 nRight, ::sal_Int32 nBottom ) override;
+ virtual css::uno::Reference< css::table::XCellRange > SAL_CALL getCellRangeByName( const OUString& aRange ) 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;
+
+ // XFastPropertySet
+ virtual void SAL_CALL setFastPropertyValue( ::sal_Int32 nHandle, const css::uno::Any& aValue ) override;
+ virtual css::uno::Any SAL_CALL getFastPropertyValue( ::sal_Int32 nHandle ) override;
+
+ // XBroadcaster
+ virtual void SAL_CALL lockBroadcasts() override;
+ virtual void SAL_CALL unlockBroadcasts() override;
+
+private:
+ void notifyModification();
+
+ void insertColumns( sal_Int32 nIndex, sal_Int32 nCount );
+ void removeColumns( sal_Int32 nIndex, sal_Int32 nCount );
+ void insertRows( sal_Int32 nIndex, sal_Int32 nCount );
+ void removeRows( sal_Int32 nIndex, sal_Int32 nCount );
+
+ sal_Int32 getRowCountImpl() const;
+ sal_Int32 getColumnCountImpl() const;
+
+ CellRef createCell();
+ CellRef getCell( ::sal_Int32 nCol, ::sal_Int32 nRow ) const;
+
+ void UndoInsertRows( sal_Int32 nIndex, sal_Int32 nCount );
+ void UndoRemoveRows( sal_Int32 nIndex, RowVector& aNewRows );
+
+ void UndoInsertColumns( sal_Int32 nIndex, sal_Int32 nCount );
+ void UndoRemoveColumns( sal_Int32 nIndex, ColumnVector& aNewCols, CellVector& aCells );
+
+private:
+ /** this function is called upon disposing the component
+ */
+ virtual void SAL_CALL disposing() override;
+
+ /// @throws css::lang::IndexOutOfBoundsException
+ TableRowRef const & getRow( sal_Int32 nRow ) const;
+ /// @throws css::lang::IndexOutOfBoundsException
+ TableColumnRef const & getColumn( sal_Int32 nColumn ) const;
+
+ void updateRows();
+ void updateColumns();
+
+ RowVector maRows;
+ ColumnVector maColumns;
+
+ rtl::Reference< TableColumns > mxTableColumns;
+ rtl::Reference< TableRows > mxTableRows;
+
+ SdrTableObj* mpTableObj; // TTTT should be reference
+
+ bool mbModified;
+ bool mbNotifyPending;
+
+ sal_Int32 mnNotifyLock;
+};
+
+class TableModelNotifyGuard
+{
+public:
+ explicit TableModelNotifyGuard( TableModel* pModel )
+ : mxBroadcaster( static_cast< css::util::XBroadcaster* >( pModel ) )
+ {
+ if( mxBroadcaster.is() )
+ mxBroadcaster->lockBroadcasts();
+ }
+
+ ~TableModelNotifyGuard()
+ {
+ if( mxBroadcaster.is() )
+ mxBroadcaster->unlockBroadcasts();
+ }
+
+private:
+ css::uno::Reference< css::util::XBroadcaster > mxBroadcaster;
+};
+
+}
+
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/inc/tabwin.hxx b/svx/source/inc/tabwin.hxx
new file mode 100644
index 000000000..c1cf70664
--- /dev/null
+++ b/svx/source/inc/tabwin.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_SVX_SOURCE_INC_TABWIN_HXX
+#define INCLUDED_SVX_SOURCE_INC_TABWIN_HXX
+
+#include <sfx2/basedlgs.hxx>
+#include <sfx2/childwin.hxx>
+#include <sfx2/ctrlitem.hxx>
+#include <svx/dbaexchange.hxx>
+#include <com/sun/star/form/XForm.hpp>
+
+#include <comphelper/propmultiplex.hxx>
+#include <connectivity/dbtools.hxx>
+
+class FmFormShell;
+struct ColumnInfo;
+
+class FmFieldWin final : public SfxModelessDialogController
+ , public SfxControllerItem
+ , public ::comphelper::OPropertyChangeListener
+{
+ ::osl::Mutex m_aMutex;
+ std::unique_ptr<weld::TreeView> m_xListBox;
+ std::vector<std::unique_ptr<ColumnInfo>> m_aListBoxData;
+ ::dbtools::SharedConnection
+ m_aConnection;
+ OUString m_aDatabaseName,
+ m_aObjectName;
+ sal_Int32 m_nObjectType;
+
+ rtl::Reference<comphelper::OPropertyChangeMultiplexer> m_xChangeListener;
+ rtl::Reference<svx::OColumnTransferable> m_xHelper;
+
+ void addToList(const css::uno::Reference<css::container::XNameAccess>& i_xColumns);
+
+ DECL_LINK(RowActivatedHdl, weld::TreeView&, bool);
+ DECL_LINK(DragBeginHdl, bool&, bool);
+public:
+ FmFieldWin(SfxBindings *pBindings, SfxChildWindow *pMgr, weld::Window* pParent);
+
+ virtual ~FmFieldWin() override;
+
+ virtual void StateChangedAtToolBoxControl(sal_uInt16 nSID, SfxItemState eState,
+ const SfxPoolItem* pState) override;
+
+ void UpdateContent(FmFormShell const *);
+ void UpdateContent(const css::uno::Reference< css::form::XForm > &);
+ void FillInfo( SfxChildWinInfo& rInfo ) const override;
+
+ const OUString& GetDatabaseName() const { return m_aDatabaseName; }
+ const ::dbtools::SharedConnection& GetConnection() const { return m_aConnection; }
+ const OUString& GetObjectName() const { return m_aObjectName; }
+ sal_Int32 GetObjectType() const { return m_nObjectType; }
+
+ bool createSelectionControls( );
+
+private:
+ // FmXChangeListener
+ virtual void _propertyChanged(const css::beans::PropertyChangeEvent& evt) override;
+
+ using SfxControllerItem::GetBindings;
+};
+
+
+class FmFieldWinMgr final : public SfxChildWindow
+{
+public:
+ FmFieldWinMgr(vcl::Window *pParent, sal_uInt16 nId,
+ SfxBindings *pBindings, SfxChildWinInfo const *pInfo);
+ SFX_DECL_CHILDWINDOW(FmFieldWinMgr);
+};
+
+
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/inc/tbxform.hxx b/svx/source/inc/tbxform.hxx
new file mode 100644
index 000000000..20c22a0b5
--- /dev/null
+++ b/svx/source/inc/tbxform.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_SVX_SOURCE_INC_TBXFORM_HXX
+#define INCLUDED_SVX_SOURCE_INC_TBXFORM_HXX
+
+#include <sfx2/tbxctrl.hxx>
+#include <svtools/recorditemwindow.hxx>
+
+class SvxFmAbsRecWin final : public RecordItemWindow
+{
+public:
+ SvxFmAbsRecWin(vcl::Window* _pParent, SfxToolBoxControl* _pController);
+
+private:
+ virtual void PositionFired(sal_Int64 nRecord) override;
+
+ SfxToolBoxControl* m_pController;
+};
+
+class SvxFmTbxCtlAbsRec final : public SfxToolBoxControl
+{
+public:
+ SFX_DECL_TOOLBOX_CONTROL();
+
+ SvxFmTbxCtlAbsRec(sal_uInt16 nSlotId, ToolBoxItemId nId, ToolBox& rTbx);
+ virtual ~SvxFmTbxCtlAbsRec() override;
+
+ virtual VclPtr<InterimItemWindow> CreateItemWindow(vcl::Window* pParent) override;
+
+ virtual void StateChangedAtToolBoxControl(sal_uInt16 nSID, SfxItemState eState,
+ const SfxPoolItem* pState) override;
+};
+
+class SvxFmTbxCtlRecText final : public SfxToolBoxControl
+{
+public:
+ SFX_DECL_TOOLBOX_CONTROL();
+
+ SvxFmTbxCtlRecText(sal_uInt16 nSlotId, ToolBoxItemId nId, ToolBox& rTbx);
+ virtual ~SvxFmTbxCtlRecText() override;
+
+ virtual VclPtr<InterimItemWindow> CreateItemWindow(vcl::Window* pParent) override;
+};
+
+class SvxFmTbxCtlRecFromText final : public SfxToolBoxControl
+{
+public:
+ SFX_DECL_TOOLBOX_CONTROL();
+
+ SvxFmTbxCtlRecFromText(sal_uInt16 nSlotId, ToolBoxItemId nId, ToolBox& rTbx);
+ virtual ~SvxFmTbxCtlRecFromText() override;
+
+ virtual VclPtr<InterimItemWindow> CreateItemWindow(vcl::Window* pParent) override;
+};
+
+class LabelItemWindow;
+
+class SvxFmTbxCtlRecTotal final : public SfxToolBoxControl
+{
+ VclPtr<LabelItemWindow> m_xFixedText;
+
+public:
+ SFX_DECL_TOOLBOX_CONTROL();
+
+ SvxFmTbxCtlRecTotal(sal_uInt16 nSlotId, ToolBoxItemId nId, ToolBox& rTbx);
+ virtual ~SvxFmTbxCtlRecTotal() override;
+
+ virtual VclPtr<InterimItemWindow> CreateItemWindow(vcl::Window* pParent) override;
+ virtual void StateChangedAtToolBoxControl(sal_uInt16 nSID, SfxItemState eState,
+ const SfxPoolItem* pState) override;
+};
+
+class SvxFmTbxNextRec final : public SfxToolBoxControl
+{
+public:
+ SFX_DECL_TOOLBOX_CONTROL();
+ SvxFmTbxNextRec(sal_uInt16 nSlotId, ToolBoxItemId nId, ToolBox& rTbx);
+};
+
+class SvxFmTbxPrevRec final : public SfxToolBoxControl
+{
+public:
+ SFX_DECL_TOOLBOX_CONTROL();
+ SvxFmTbxPrevRec(sal_uInt16 nSlotId, ToolBoxItemId nId, ToolBox& rTbx);
+};
+
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/inc/treevisitor.hxx b/svx/source/inc/treevisitor.hxx
new file mode 100644
index 000000000..50e7d7a35
--- /dev/null
+++ b/svx/source/inc/treevisitor.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_SVX_SOURCE_INC_TREEVISITOR_HXX
+#define INCLUDED_SVX_SOURCE_INC_TREEVISITOR_HXX
+
+#include <stack>
+
+template< class ELEMENT, class NODEINFO, class PROCESSOR >
+class TreeVisitor
+{
+public:
+ TreeVisitor( NODEINFO _nodeInfo )
+ :m_visitedRoot( false )
+ ,m_root()
+ ,m_current()
+ ,m_nodeInfo( _nodeInfo )
+ {
+ }
+
+ void process( const ELEMENT& _root, PROCESSOR& _processor )
+ {
+ m_root = _root;
+ m_visitedRoot = false;
+
+ while ( do_step() )
+ _processor.process( m_current );
+ }
+
+private:
+ bool do_step();
+
+private:
+ bool m_visitedRoot;
+ ELEMENT m_root;
+ ELEMENT m_current;
+ const NODEINFO m_nodeInfo;
+
+ ::std::stack< size_t > m_pathToCurrent;
+ ::std::stack< ELEMENT > m_currentAncestors;
+};
+
+template< class ELEMENT, class NODEINFO, class PROCESSOR >
+bool TreeVisitor< ELEMENT, NODEINFO, PROCESSOR >::do_step()
+{
+ if ( !m_visitedRoot )
+ {
+ m_current = m_root;
+ m_visitedRoot = true;
+ return true;
+ }
+
+ // can we step down from the current node?
+ size_t childCount = m_nodeInfo.childCount( m_current );
+ if ( childCount )
+ {
+ m_currentAncestors.push( m_current );
+ m_current = m_nodeInfo.getChild( m_current, 0 );
+ m_pathToCurrent.push( 0 );
+ return true;
+ }
+
+ // is there a right sibling of the current node?
+ while ( !m_pathToCurrent.empty() )
+ {
+ const ELEMENT& currentParent = m_currentAncestors.top();
+ childCount = m_nodeInfo.childCount( currentParent );
+
+ size_t currentChildPos = m_pathToCurrent.top();
+ if ( ++currentChildPos < childCount )
+ {
+ // yes there is
+ m_pathToCurrent.top() = currentChildPos;
+ m_current = m_nodeInfo.getChild( currentParent, currentChildPos );
+ return true;
+ }
+
+ // no there isn't => step up
+ m_currentAncestors.pop();
+ m_pathToCurrent.pop();
+ }
+
+ return false;
+}
+
+#endif // INCLUDED_SVX_SOURCE_INC_TREEVISITOR_HXX
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/inc/xfm_addcondition.hxx b/svx/source/inc/xfm_addcondition.hxx
new file mode 100644
index 000000000..10d4674cb
--- /dev/null
+++ b/svx/source/inc/xfm_addcondition.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_SVX_SOURCE_INC_XFM_ADDCONDITION_HXX
+#define INCLUDED_SVX_SOURCE_INC_XFM_ADDCONDITION_HXX
+
+#include <com/sun/star/lang/XMultiServiceFactory.hpp>
+#include <com/sun/star/xforms/XModel.hpp>
+#include <svtools/genericunodialog.hxx>
+#include <comphelper/proparrhlp.hxx>
+
+
+namespace svxform
+{
+
+ typedef ::svt::OGenericUnoDialog OAddConditionDialogBase;
+ class OAddConditionDialog final
+ :public OAddConditionDialogBase
+ ,public ::comphelper::OPropertyArrayUsageHelper< OAddConditionDialog >
+ {
+ public:
+ static css::uno::Reference< css::uno::XInterface >
+ Create( const css::uno::Reference< css::lang::XMultiServiceFactory >& );
+
+ private:
+ OAddConditionDialog( const css::uno::Reference< css::uno::XComponentContext >& _rxORB );
+
+ // XTypeProvider
+ virtual css::uno::Sequence<sal_Int8> SAL_CALL getImplementationId( ) override;
+
+ // XServiceInfo
+ virtual OUString SAL_CALL getImplementationName() override;
+ virtual css::uno::Sequence< OUString > SAL_CALL getSupportedServiceNames() override;
+
+ // XPropertySet
+ virtual css::uno::Reference<css::beans::XPropertySetInfo> SAL_CALL getPropertySetInfo() override;
+ virtual ::cppu::IPropertyArrayHelper& SAL_CALL getInfoHelper() override;
+
+ // OPropertyArrayUsageHelper
+ virtual ::cppu::IPropertyArrayHelper* createArrayHelper( ) const override;
+
+ // OGenericUnoDialog overridables
+ virtual std::unique_ptr<weld::DialogController> createDialog(const css::uno::Reference<css::awt::XWindow>& rParent) override;
+ virtual void executedDialog(sal_Int16 _nExecutionResult) override;
+
+ css::uno::Reference< css::beans::XPropertySet >
+ m_xBinding;
+ OUString m_sFacetName;
+ OUString m_sConditionValue;
+ css::uno::Reference< css::xforms::XModel >
+ m_xWorkModel;
+ };
+
+
+}
+
+
+#endif // INCLUDED_SVX_SOURCE_INC_XFM_ADDCONDITION_HXX
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/inc/xmlxtexp.hxx b/svx/source/inc/xmlxtexp.hxx
new file mode 100644
index 000000000..e47dbf1df
--- /dev/null
+++ b/svx/source/inc/xmlxtexp.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_SVX_SOURCE_INC_XMLXTEXP_HXX
+#define INCLUDED_SVX_SOURCE_INC_XMLXTEXP_HXX
+
+#include <xmloff/xmlexp.hxx>
+
+namespace com::sun::star {
+ namespace frame { class XModel; }
+ namespace uno { template<class X> class Reference; }
+ namespace uno { class XInterface; }
+ namespace container { class XNameContainer; }
+ namespace document { class XGraphicStorageHandler; }
+ namespace xml::sax { class XDocumentHandler; }
+}
+
+class SvxXMLXTableExportComponent final : public SvXMLExport
+{
+public:
+ SvxXMLXTableExportComponent(
+ const css::uno::Reference< css::uno::XComponentContext >& xContext,
+ const css::uno::Reference< css::xml::sax::XDocumentHandler > & xHandler,
+ const css::uno::Reference< css::container::XNameContainer > & xTable,
+ css::uno::Reference<css::document::XGraphicStorageHandler> const & xGraphicStorageHandler);
+
+ virtual ~SvxXMLXTableExportComponent() override;
+
+ /// @throws css::uno::RuntimeException
+ static bool save( const OUString& rURL,
+ const css::uno::Reference< css::container::XNameContainer >& xTable,
+ const css::uno::Reference< css::embed::XStorage > &xStorage,
+ OUString *pOptName );
+
+ // methods without content:
+ virtual void ExportAutoStyles_() override;
+ virtual void ExportMasterStyles_() override;
+ virtual void ExportContent_() override;
+
+private:
+ bool exportTable() noexcept;
+ const css::uno::Reference< css::container::XNameContainer > & mxTable;
+};
+
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/inc/xmlxtimp.hxx b/svx/source/inc/xmlxtimp.hxx
new file mode 100644
index 000000000..ee442ac40
--- /dev/null
+++ b/svx/source/inc/xmlxtimp.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_SVX_SOURCE_INC_XMLXTIMP_HXX
+#define INCLUDED_SVX_SOURCE_INC_XMLXTIMP_HXX
+
+#include <xmloff/xmlimp.hxx>
+
+namespace com::sun::star {
+ namespace uno { template<class X> class Reference; }
+ namespace uno { class XInterface; }
+ namespace document { class XGraphicStorageHandler; }
+ namespace container { class XNameContainer; }
+}
+
+class SvxXMLXTableImport final : public SvXMLImport
+{
+public:
+ SvxXMLXTableImport(
+ const css::uno::Reference< css::uno::XComponentContext >& rContext,
+ const css::uno::Reference< css::container::XNameContainer > & rTable,
+ css::uno::Reference<css::document::XGraphicStorageHandler> const & rxGraphicStorageHandler);
+
+ virtual ~SvxXMLXTableImport() noexcept override;
+
+ static bool load( const OUString &rPath, const OUString &rReferer,
+ const css::uno::Reference < css::embed::XStorage > &xStorage,
+ const css::uno::Reference< css::container::XNameContainer >& xTable,
+ bool *bOptLoadedFromStorage ) noexcept;
+private:
+ virtual SvXMLImportContext *CreateFastContext( sal_Int32 Element,
+ const ::css::uno::Reference< ::css::xml::sax::XFastAttributeList >& xAttrList ) override;
+
+ const css::uno::Reference< css::container::XNameContainer > & mrTable;
+};
+
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/items/SmartTagItem.cxx b/svx/source/items/SmartTagItem.cxx
new file mode 100644
index 000000000..fcec36315
--- /dev/null
+++ b/svx/source/items/SmartTagItem.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 <comphelper/propertysequence.hxx>
+#include <svx/SmartTagItem.hxx>
+#include <sal/log.hxx>
+
+#include <com/sun/star/container/XStringKeyMap.hpp>
+
+
+using namespace ::com::sun::star;
+
+
+SfxPoolItem* SvxSmartTagItem::CreateDefault() { SAL_WARN( "svx", "No SvxSmartTagItem factory available"); return nullptr; }
+
+SvxSmartTagItem::SvxSmartTagItem( const TypedWhichId<SvxSmartTagItem> nId,
+ const css::uno::Sequence < css::uno::Sequence< css::uno::Reference< css::smarttags::XSmartTagAction > > >& rActionComponentsSequence,
+ const css::uno::Sequence < css::uno::Sequence< sal_Int32 > >& rActionIndicesSequence,
+ const css::uno::Sequence< css::uno::Reference< css::container::XStringKeyMap > >& rStringKeyMaps,
+ const css::uno::Reference<css::text::XTextRange>& rRange,
+ const css::uno::Reference<css::frame::XController>& rController,
+ const css::lang::Locale& rLocale,
+ const OUString& rApplicationName,
+ const OUString& rRangeText ) :
+ SfxPoolItem( nId ),
+ maActionComponentsSequence( rActionComponentsSequence ),
+ maActionIndicesSequence( rActionIndicesSequence ),
+ maStringKeyMaps( rStringKeyMaps ),
+ mxRange( rRange ),
+ mxController( rController ),
+ maLocale( rLocale ),
+ maApplicationName( rApplicationName ),
+ maRangeText( rRangeText )
+{
+}
+
+
+bool SvxSmartTagItem::QueryValue( uno::Any& rVal, sal_uInt8 /*nMemberId*/ ) const
+{
+ rVal <<= comphelper::InitPropertySequence( {
+ { "ActionComponents", css::uno::Any( maActionComponentsSequence ) },
+ { "ActionIndices", css::uno::Any( maActionIndicesSequence ) },
+ { "StringKeyMaps", css::uno::Any( maStringKeyMaps ) },
+ { "TextRange", css::uno::Any( mxRange ) },
+ { "Controller", css::uno::Any( mxController ) },
+ { "Locale", css::uno::Any( maLocale ) },
+ { "ApplicationName", css::uno::Any( maApplicationName ) },
+ { "RangeText", css::uno::Any( maRangeText ) },
+ } );
+ return true;
+}
+
+bool SvxSmartTagItem::PutValue( const uno::Any& /*rVal*/, sal_uInt8 /* nMemberId */)
+{
+ return false;
+}
+
+
+bool SvxSmartTagItem::operator==( const SfxPoolItem& rAttr ) const
+{
+ assert(SfxPoolItem::operator==(rAttr));
+
+ const SvxSmartTagItem& rItem = static_cast<const SvxSmartTagItem&>(rAttr);
+
+ return maActionComponentsSequence == rItem.maActionComponentsSequence &&
+ maActionIndicesSequence == rItem.maActionIndicesSequence &&
+ maStringKeyMaps == rItem.maStringKeyMaps &&
+ mxRange == rItem.mxRange &&
+ mxController == rItem.mxController &&
+ maApplicationName == rItem.maApplicationName &&
+ maRangeText == rItem.maRangeText;
+}
+
+
+SvxSmartTagItem* SvxSmartTagItem::Clone( SfxItemPool * ) const
+{
+ return new SvxSmartTagItem( *this );
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/items/algitem.cxx b/svx/source/items/algitem.cxx
new file mode 100644
index 000000000..917edb1f6
--- /dev/null
+++ b/svx/source/items/algitem.cxx
@@ -0,0 +1,315 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <svx/strings.hrc>
+#include <osl/diagnose.h>
+#include <tools/mapunit.hxx>
+#include <tools/UnitConversion.hxx>
+#include <com/sun/star/table/CellOrientation.hpp>
+
+#include <svx/algitem.hxx>
+#include <svx/dialmgr.hxx>
+#include <editeng/itemtype.hxx>
+#include <editeng/eerdll.hxx>
+#include <svx/unomid.hxx>
+
+#include <climits>
+
+using namespace ::com::sun::star;
+
+
+SfxPoolItem* SvxMarginItem::CreateDefault() { return new SvxMarginItem(TypedWhichId<SvxMarginItem>(0)) ;}
+
+SvxOrientationItem::SvxOrientationItem( const SvxCellOrientation eOrientation,
+ const TypedWhichId<SvxOrientationItem> nId):
+ SfxEnumItem( nId, eOrientation )
+{
+}
+
+SvxOrientationItem::SvxOrientationItem( Degree100 nRotation, bool bStacked, const TypedWhichId<SvxOrientationItem> nId ) :
+ SfxEnumItem( nId, SvxCellOrientation::Standard )
+{
+ if( bStacked )
+ {
+ SetValue( SvxCellOrientation::Stacked );
+ }
+ else switch( nRotation.get() )
+ {
+ case 9000: SetValue( SvxCellOrientation::BottomUp ); break;
+ case 27000: SetValue( SvxCellOrientation::TopBottom ); break;
+ default: SetValue( SvxCellOrientation::Standard );
+ }
+}
+
+
+bool SvxOrientationItem::GetPresentation
+(
+ SfxItemPresentation /*ePres*/,
+ MapUnit /*eCoreUnit*/,
+ MapUnit /*ePresUnit*/,
+ OUString& rText,
+ const IntlWrapper& ) const
+{
+ rText = GetValueText( GetValue() );
+ return true;
+}
+
+
+bool SvxOrientationItem::QueryValue( uno::Any& rVal, sal_uInt8 /*nMemberId*/ ) const
+{
+ table::CellOrientation eUno = table::CellOrientation_STANDARD;
+ switch ( GetValue() )
+ {
+ case SvxCellOrientation::Standard: eUno = table::CellOrientation_STANDARD; break;
+ case SvxCellOrientation::TopBottom: eUno = table::CellOrientation_TOPBOTTOM; break;
+ case SvxCellOrientation::BottomUp: eUno = table::CellOrientation_BOTTOMTOP; break;
+ case SvxCellOrientation::Stacked: eUno = table::CellOrientation_STACKED; break;
+ }
+ rVal <<= eUno;
+ return true;
+}
+
+bool SvxOrientationItem::PutValue( const uno::Any& rVal, sal_uInt8 /*nMemberId*/ )
+{
+ table::CellOrientation eOrient;
+ if(!(rVal >>= eOrient))
+ {
+ sal_Int32 nValue = 0;
+ if(!(rVal >>= nValue))
+ return false;
+ eOrient = static_cast<table::CellOrientation>(nValue);
+ }
+ SvxCellOrientation eSvx = SvxCellOrientation::Standard;
+ switch (eOrient)
+ {
+ case table::CellOrientation_STANDARD: eSvx = SvxCellOrientation::Standard; break;
+ case table::CellOrientation_TOPBOTTOM: eSvx = SvxCellOrientation::TopBottom; break;
+ case table::CellOrientation_BOTTOMTOP: eSvx = SvxCellOrientation::BottomUp; break;
+ case table::CellOrientation_STACKED: eSvx = SvxCellOrientation::Stacked; break;
+ default: ; //prevent warning
+ }
+ SetValue( eSvx );
+ return true;
+}
+
+OUString SvxOrientationItem::GetValueText( SvxCellOrientation nVal )
+{
+ OString id = OString::Concat(RID_SVXITEMS_ORI_STANDARD.mpId) + OString::number(static_cast<int>(nVal));
+ return SvxResId(TranslateId(RID_SVXITEMS_ORI_STANDARD.mpContext, id.getStr()));
+}
+
+SvxOrientationItem* SvxOrientationItem::Clone( SfxItemPool* ) const
+{
+ return new SvxOrientationItem( *this );
+}
+
+sal_uInt16 SvxOrientationItem::GetValueCount() const
+{
+ return static_cast<sal_uInt16>(SvxCellOrientation::Stacked) + 1; // last enum value + 1
+}
+
+bool SvxOrientationItem::IsStacked() const
+{
+ return GetValue() == SvxCellOrientation::Stacked;
+}
+
+Degree100 SvxOrientationItem::GetRotation( Degree100 nStdAngle ) const
+{
+ Degree100 nAngle = nStdAngle;
+ switch( GetValue() )
+ {
+ case SvxCellOrientation::BottomUp: nAngle = 9000_deg100; break;
+ case SvxCellOrientation::TopBottom: nAngle = 27000_deg100; break;
+ default: ; //prevent warning
+ }
+ return nAngle;
+}
+
+SvxMarginItem::SvxMarginItem( const TypedWhichId<SvxMarginItem> nId ) :
+
+ SfxPoolItem( nId ),
+
+ nLeftMargin ( 20 ),
+ nTopMargin ( 20 ),
+ nRightMargin ( 20 ),
+ nBottomMargin( 20 )
+{
+}
+
+
+SvxMarginItem::SvxMarginItem( sal_Int16 nLeft,
+ sal_Int16 nTop,
+ sal_Int16 nRight,
+ sal_Int16 nBottom,
+ const TypedWhichId<SvxMarginItem> nId ) :
+ SfxPoolItem( nId ),
+
+ nLeftMargin ( nLeft ),
+ nTopMargin ( nTop ),
+ nRightMargin ( nRight ),
+ nBottomMargin( nBottom )
+{
+}
+
+
+bool SvxMarginItem::GetPresentation
+(
+ SfxItemPresentation ePres,
+ MapUnit eCoreUnit,
+ MapUnit ePresUnit,
+ OUString& rText, const IntlWrapper& rIntl
+) const
+{
+ OUString cpDelimTmp(cpDelim);
+
+ switch ( ePres )
+ {
+ case SfxItemPresentation::Nameless:
+ {
+ rText = GetMetricText( static_cast<tools::Long>(nLeftMargin), eCoreUnit, ePresUnit, &rIntl ) +
+ cpDelimTmp +
+ GetMetricText( static_cast<tools::Long>(nTopMargin), eCoreUnit, ePresUnit, &rIntl ) +
+ cpDelimTmp +
+ GetMetricText( static_cast<tools::Long>(nRightMargin), eCoreUnit, ePresUnit, &rIntl ) +
+ cpDelimTmp +
+ GetMetricText( static_cast<tools::Long>(nBottomMargin), eCoreUnit, ePresUnit, &rIntl );
+ return true;
+ }
+ case SfxItemPresentation::Complete:
+ {
+ rText = SvxResId(RID_SVXITEMS_MARGIN_LEFT) +
+ GetMetricText( static_cast<tools::Long>(nLeftMargin), eCoreUnit, ePresUnit, &rIntl ) +
+ " " + EditResId(GetMetricId(ePresUnit)) +
+ cpDelimTmp +
+ SvxResId(RID_SVXITEMS_MARGIN_TOP) +
+ GetMetricText( static_cast<tools::Long>(nTopMargin), eCoreUnit, ePresUnit, &rIntl ) +
+ " " + EditResId(GetMetricId(ePresUnit)) +
+ cpDelimTmp +
+ SvxResId(RID_SVXITEMS_MARGIN_RIGHT) +
+ GetMetricText( static_cast<tools::Long>(nRightMargin), eCoreUnit, ePresUnit, &rIntl ) +
+ " " + EditResId(GetMetricId(ePresUnit)) +
+ cpDelimTmp +
+ SvxResId(RID_SVXITEMS_MARGIN_BOTTOM) +
+ GetMetricText( static_cast<tools::Long>(nBottomMargin), eCoreUnit, ePresUnit, &rIntl ) +
+ " " + EditResId(GetMetricId(ePresUnit));
+ return true;
+ }
+ default: ; //prevent warning
+ }
+ return false;
+}
+
+
+bool SvxMarginItem::operator==( const SfxPoolItem& rItem ) const
+{
+ assert(SfxPoolItem::operator==(rItem));
+
+ return ( ( nLeftMargin == static_cast<const SvxMarginItem&>(rItem).nLeftMargin ) &&
+ ( nTopMargin == static_cast<const SvxMarginItem&>(rItem).nTopMargin ) &&
+ ( nRightMargin == static_cast<const SvxMarginItem&>(rItem).nRightMargin ) &&
+ ( nBottomMargin == static_cast<const SvxMarginItem&>(rItem).nBottomMargin ) );
+}
+
+SvxMarginItem* SvxMarginItem::Clone( SfxItemPool* ) const
+{
+ return new SvxMarginItem(*this);
+}
+
+bool SvxMarginItem::QueryValue( uno::Any& rVal, sal_uInt8 nMemberId ) const
+{
+ bool bConvert = 0!=(nMemberId&CONVERT_TWIPS);
+ nMemberId &= ~CONVERT_TWIPS;
+ switch ( nMemberId )
+ {
+ // now sign everything
+ case MID_MARGIN_L_MARGIN:
+ rVal <<= static_cast<sal_Int32>( bConvert ? convertTwipToMm100(nLeftMargin) : nLeftMargin );
+ break;
+ case MID_MARGIN_R_MARGIN:
+ rVal <<= static_cast<sal_Int32>( bConvert ? convertTwipToMm100(nRightMargin) : nRightMargin );
+ break;
+ case MID_MARGIN_UP_MARGIN:
+ rVal <<= static_cast<sal_Int32>( bConvert ? convertTwipToMm100(nTopMargin) : nTopMargin );
+ break;
+ case MID_MARGIN_LO_MARGIN:
+ rVal <<= static_cast<sal_Int32>( bConvert ? convertTwipToMm100(nBottomMargin) : nBottomMargin );
+ break;
+ default:
+ OSL_FAIL("unknown MemberId");
+ return false;
+ }
+ return true;
+}
+
+
+bool SvxMarginItem::PutValue( const uno::Any& rVal, sal_uInt8 nMemberId )
+{
+ bool bConvert = ( ( nMemberId & CONVERT_TWIPS ) != 0 );
+ tools::Long nMaxVal = bConvert ? convertTwipToMm100(SHRT_MAX) : SHRT_MAX; // members are sal_Int16
+ sal_Int32 nVal = 0;
+ if(!(rVal >>= nVal) || (nVal > nMaxVal))
+ return false;
+
+ switch ( nMemberId & ~CONVERT_TWIPS )
+ {
+ case MID_MARGIN_L_MARGIN:
+ nLeftMargin = static_cast<sal_Int16>( bConvert ? o3tl::toTwips(nVal, o3tl::Length::mm100) : nVal );
+ break;
+ case MID_MARGIN_R_MARGIN:
+ nRightMargin = static_cast<sal_Int16>( bConvert ? o3tl::toTwips(nVal, o3tl::Length::mm100) : nVal );
+ break;
+ case MID_MARGIN_UP_MARGIN:
+ nTopMargin = static_cast<sal_Int16>( bConvert ? o3tl::toTwips(nVal, o3tl::Length::mm100) : nVal );
+ break;
+ case MID_MARGIN_LO_MARGIN:
+ nBottomMargin = static_cast<sal_Int16>( bConvert ? o3tl::toTwips(nVal, o3tl::Length::mm100) : nVal );
+ break;
+ default:
+ OSL_FAIL("unknown MemberId");
+ return false;
+ }
+ return true;
+}
+
+
+void SvxMarginItem::SetLeftMargin( sal_Int16 nLeft )
+{
+ nLeftMargin = nLeft;
+}
+
+
+void SvxMarginItem::SetTopMargin( sal_Int16 nTop )
+{
+ nTopMargin = nTop;
+}
+
+
+void SvxMarginItem::SetRightMargin( sal_Int16 nRight )
+{
+ nRightMargin = nRight;
+}
+
+
+void SvxMarginItem::SetBottomMargin( sal_Int16 nBottom )
+{
+ nBottomMargin = nBottom;
+}
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/items/autoformathelper.cxx b/svx/source/items/autoformathelper.cxx
new file mode 100644
index 000000000..0ae522fbd
--- /dev/null
+++ b/svx/source/items/autoformathelper.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 <svx/autoformathelper.hxx>
+#include <tools/stream.hxx>
+#include <svl/legacyitem.hxx>
+#include <editeng/legacyitem.hxx>
+#include <svx/legacyitem.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 <editeng/contouritem.hxx>
+#include <editeng/shdditem.hxx>
+#include <editeng/colritem.hxx>
+#include <editeng/boxitem.hxx>
+#include <editeng/lineitem.hxx>
+#include <editeng/brushitem.hxx>
+#include <editeng/adjustitem.hxx>
+#include <editeng/justifyitem.hxx>
+#include <svl/eitem.hxx>
+#include <svx/algitem.hxx>
+#include <svl/intitem.hxx>
+#include <svx/rotmodit.hxx>
+#include <osl/thread.h>
+
+//////////////////////////////////////////////////////////////////////////////
+
+AutoFormatVersions::AutoFormatVersions()
+: nFontVersion(0),
+ nFontHeightVersion(0),
+ nWeightVersion(0),
+ nPostureVersion(0),
+ nUnderlineVersion(0),
+ nOverlineVersion(0),
+ nCrossedOutVersion(0),
+ nContourVersion(0),
+ nShadowedVersion(0),
+ nColorVersion(0),
+ nBoxVersion(0),
+ nLineVersion(0),
+ nBrushVersion(0),
+ nAdjustVersion(0),
+ nHorJustifyVersion(0),
+ nVerJustifyVersion(0),
+ nOrientationVersion(0),
+ nMarginVersion(0),
+ nBoolVersion(0),
+ nInt32Version(0),
+ nRotateModeVersion(0),
+ nNumFormatVersion(0)
+{
+}
+
+const sal_uInt16 AUTOFORMAT_ID_300OVRLN = 10031;
+const sal_uInt16 AUTOFORMAT_ID_680DR14 = 10011;
+const sal_uInt16 AUTOFORMAT_ID_504 = 9801;
+
+void AutoFormatVersions::LoadBlockA( SvStream& rStream, sal_uInt16 nVer )
+{
+ rStream.ReadUInt16( nFontVersion );
+ rStream.ReadUInt16( nFontHeightVersion );
+ rStream.ReadUInt16( nWeightVersion );
+ rStream.ReadUInt16( nPostureVersion );
+ rStream.ReadUInt16( nUnderlineVersion );
+ if ( nVer >= AUTOFORMAT_ID_300OVRLN )
+ rStream.ReadUInt16( nOverlineVersion );
+ rStream.ReadUInt16( nCrossedOutVersion );
+ rStream.ReadUInt16( nContourVersion );
+ rStream.ReadUInt16( nShadowedVersion );
+ rStream.ReadUInt16( nColorVersion );
+ rStream.ReadUInt16( nBoxVersion );
+ if ( nVer >= AUTOFORMAT_ID_680DR14 )
+ rStream.ReadUInt16( nLineVersion );
+ rStream.ReadUInt16( nBrushVersion );
+ rStream.ReadUInt16( nAdjustVersion );
+}
+
+void AutoFormatVersions::LoadBlockB( SvStream& rStream, sal_uInt16 nVer )
+{
+ rStream.ReadUInt16( nHorJustifyVersion );
+ rStream.ReadUInt16( nVerJustifyVersion );
+ rStream.ReadUInt16( nOrientationVersion );
+ rStream.ReadUInt16( nMarginVersion );
+ rStream.ReadUInt16( nBoolVersion );
+ if ( nVer >= AUTOFORMAT_ID_504 )
+ {
+ rStream.ReadUInt16( nInt32Version );
+ rStream.ReadUInt16( nRotateModeVersion );
+ }
+ rStream.ReadUInt16( nNumFormatVersion );
+}
+
+void AutoFormatVersions::WriteBlockA(SvStream& rStream, sal_uInt16 fileVersion)
+{
+ rStream.WriteUInt16(legacy::SvxFont::GetVersion(fileVersion));
+ rStream.WriteUInt16(legacy::SvxFontHeight::GetVersion(fileVersion));
+ rStream.WriteUInt16(legacy::SvxWeight::GetVersion(fileVersion));
+ rStream.WriteUInt16(legacy::SvxPosture::GetVersion(fileVersion));
+ rStream.WriteUInt16(legacy::SvxTextLine::GetVersion(fileVersion));
+ rStream.WriteUInt16(legacy::SvxTextLine::GetVersion(fileVersion));
+ rStream.WriteUInt16(legacy::SvxCrossedOut::GetVersion(fileVersion));
+ rStream.WriteUInt16(legacy::SfxBool::GetVersion(fileVersion));
+ rStream.WriteUInt16(legacy::SfxBool::GetVersion(fileVersion));
+ rStream.WriteUInt16(legacy::SvxColor::GetVersion(fileVersion));
+ rStream.WriteUInt16(legacy::SvxBox::GetVersion(fileVersion));
+ rStream.WriteUInt16(legacy::SvxLine::GetVersion(fileVersion));
+ rStream.WriteUInt16(legacy::SvxBrush::GetVersion(fileVersion));
+ rStream.WriteUInt16(legacy::SvxAdjust::GetVersion(fileVersion));
+}
+
+void AutoFormatVersions::WriteBlockB(SvStream& rStream, sal_uInt16 fileVersion)
+{
+ rStream.WriteUInt16(legacy::SvxHorJustify::GetVersion(fileVersion));
+ rStream.WriteUInt16(legacy::SvxVerJustify::GetVersion(fileVersion));
+ rStream.WriteUInt16(legacy::SvxOrientation::GetVersion(fileVersion));
+ rStream.WriteUInt16(legacy::SvxMargin::GetVersion(fileVersion));
+ rStream.WriteUInt16(legacy::SfxBool::GetVersion(fileVersion));
+ rStream.WriteUInt16(legacy::CntInt32::GetVersion(fileVersion));
+ rStream.WriteUInt16(legacy::SvxRotateMode::GetVersion(fileVersion));
+ rStream.WriteUInt16( 0 ); // NumberFormat
+}
+
+//////////////////////////////////////////////////////////////////////////////
+
+void AutoFormatBase::SetFont( const SvxFontItem& rNew ) { m_aFont.reset(rNew.Clone()); }
+void AutoFormatBase::SetHeight( const SvxFontHeightItem& rNew ) { m_aHeight.reset(rNew.Clone()); }
+void AutoFormatBase::SetWeight( const SvxWeightItem& rNew ) { m_aWeight.reset(rNew.Clone()); }
+void AutoFormatBase::SetPosture( const SvxPostureItem& rNew ) { m_aPosture.reset(rNew.Clone()); }
+void AutoFormatBase::SetCJKFont( const SvxFontItem& rNew ) { m_aCJKFont.reset(rNew.Clone()); }
+void AutoFormatBase::SetCJKHeight( const SvxFontHeightItem& rNew ) { m_aCJKHeight.reset(rNew.Clone()); }
+void AutoFormatBase::SetCJKWeight( const SvxWeightItem& rNew ) { m_aCJKWeight.reset(rNew.Clone()); }
+void AutoFormatBase::SetCJKPosture( const SvxPostureItem& rNew ) { m_aCJKPosture.reset(rNew.Clone()); }
+void AutoFormatBase::SetCTLFont( const SvxFontItem& rNew ) { m_aCTLFont.reset(rNew.Clone()); }
+void AutoFormatBase::SetCTLHeight( const SvxFontHeightItem& rNew ) { m_aCTLHeight.reset(rNew.Clone()); }
+void AutoFormatBase::SetCTLWeight( const SvxWeightItem& rNew ) { m_aCTLWeight.reset(rNew.Clone()); }
+void AutoFormatBase::SetCTLPosture( const SvxPostureItem& rNew ) { m_aCTLPosture.reset(rNew.Clone()); }
+void AutoFormatBase::SetUnderline( const SvxUnderlineItem& rNew ) { m_aUnderline.reset(rNew.Clone()); }
+void AutoFormatBase::SetOverline( const SvxOverlineItem& rNew ) { m_aOverline.reset(rNew.Clone()); }
+void AutoFormatBase::SetCrossedOut( const SvxCrossedOutItem& rNew ) { m_aCrossedOut.reset(rNew.Clone()); }
+void AutoFormatBase::SetContour( const SvxContourItem& rNew ) { m_aContour.reset(rNew.Clone()); }
+void AutoFormatBase::SetShadowed( const SvxShadowedItem& rNew ) { m_aShadowed.reset(rNew.Clone()); }
+void AutoFormatBase::SetColor( const SvxColorItem& rNew ) { m_aColor.reset(rNew.Clone()); }
+void AutoFormatBase::SetBox( const SvxBoxItem& rNew ) { m_aBox.reset(rNew.Clone()); }
+void AutoFormatBase::SetTLBR( const SvxLineItem& rNew ) { m_aTLBR.reset(rNew.Clone()); }
+void AutoFormatBase::SetBLTR( const SvxLineItem& rNew ) { m_aBLTR.reset(rNew.Clone()); }
+void AutoFormatBase::SetBackground( const SvxBrushItem& rNew ) { m_aBackground.reset(rNew.Clone()); }
+void AutoFormatBase::SetAdjust( const SvxAdjustItem& rNew ) { m_aAdjust.reset(rNew.Clone()); }
+void AutoFormatBase::SetHorJustify( const SvxHorJustifyItem& rNew ) { m_aHorJustify.reset(rNew.Clone()); }
+void AutoFormatBase::SetVerJustify( const SvxVerJustifyItem& rNew ) { m_aVerJustify.reset(rNew.Clone()); }
+void AutoFormatBase::SetStacked( const SfxBoolItem& rNew ) { m_aStacked.reset(rNew.Clone()); }
+void AutoFormatBase::SetMargin( const SvxMarginItem& rNew ) { m_aMargin.reset(rNew.Clone()); }
+void AutoFormatBase::SetLinebreak( const SfxBoolItem& rNew ) { m_aLinebreak.reset(rNew.Clone()); }
+void AutoFormatBase::SetRotateAngle( const SfxInt32Item& rNew ) { m_aRotateAngle.reset(rNew.Clone()); }
+void AutoFormatBase::SetRotateMode( const SvxRotateModeItem& rNew ) { m_aRotateMode.reset(rNew.Clone()); }
+
+AutoFormatBase::AutoFormatBase()
+{
+}
+
+AutoFormatBase::AutoFormatBase( const AutoFormatBase& rNew )
+: m_aFont(rNew.m_aFont->Clone()),
+ m_aHeight(rNew.m_aHeight->Clone()),
+ m_aWeight(rNew.m_aWeight->Clone()),
+ m_aPosture(rNew.m_aPosture->Clone()),
+ m_aCJKFont(rNew.m_aCJKFont->Clone()),
+ m_aCJKHeight(rNew.m_aCJKHeight->Clone()),
+ m_aCJKWeight(rNew.m_aCJKWeight->Clone()),
+ m_aCJKPosture(rNew.m_aCJKPosture->Clone()),
+ m_aCTLFont(rNew.m_aCTLFont->Clone()),
+ m_aCTLHeight(rNew.m_aCTLHeight->Clone()),
+ m_aCTLWeight(rNew.m_aCTLWeight->Clone()),
+ m_aCTLPosture(rNew.m_aCTLPosture->Clone()),
+ m_aUnderline(rNew.m_aUnderline->Clone()),
+ m_aOverline(rNew.m_aOverline->Clone()),
+ m_aCrossedOut(rNew.m_aCrossedOut->Clone()),
+ m_aContour(rNew.m_aContour->Clone()),
+ m_aShadowed(rNew.m_aShadowed->Clone()),
+ m_aColor(rNew.m_aColor->Clone()),
+ m_aBox(rNew.m_aBox->Clone()),
+ m_aTLBR(rNew.m_aTLBR->Clone()),
+ m_aBLTR(rNew.m_aBLTR->Clone()),
+ m_aBackground(rNew.m_aBackground->Clone()),
+ m_aAdjust(rNew.m_aAdjust->Clone()),
+ m_aHorJustify(rNew.m_aHorJustify->Clone()),
+ m_aVerJustify(rNew.m_aVerJustify->Clone()),
+ m_aStacked(rNew.m_aStacked->Clone()),
+ m_aMargin(rNew.m_aMargin->Clone()),
+ m_aLinebreak(rNew.m_aLinebreak->Clone()),
+ m_aRotateAngle(rNew.m_aRotateAngle->Clone()),
+ m_aRotateMode(rNew.m_aRotateMode->Clone())
+{
+}
+
+AutoFormatBase::~AutoFormatBase()
+{
+}
+
+AutoFormatBase& AutoFormatBase::operator=(const AutoFormatBase& rRef)
+{
+ // check self-assignment
+ if(this == &rRef)
+ {
+ return *this;
+ }
+
+ // copy local members - this will use ::Clone() on all involved Items
+ SetFont(rRef.GetFont());
+ SetHeight(rRef.GetHeight());
+ SetWeight(rRef.GetWeight());
+ SetPosture(rRef.GetPosture());
+ SetCJKFont(rRef.GetCJKFont());
+ SetCJKHeight(rRef.GetCJKHeight());
+ SetCJKWeight(rRef.GetCJKWeight());
+ SetCJKPosture(rRef.GetCJKPosture());
+ SetCTLFont(rRef.GetCTLFont());
+ SetCTLHeight(rRef.GetCTLHeight());
+ SetCTLWeight(rRef.GetCTLWeight());
+ SetCTLPosture(rRef.GetCTLPosture());
+ SetUnderline(rRef.GetUnderline());
+ SetOverline(rRef.GetOverline());
+ SetCrossedOut(rRef.GetCrossedOut());
+ SetContour(rRef.GetContour());
+ SetShadowed(rRef.GetShadowed());
+ SetColor(rRef.GetColor());
+ SetBox(rRef.GetBox());
+ SetTLBR(rRef.GetTLBR());
+ SetBLTR(rRef.GetBLTR());
+ SetBackground(rRef.GetBackground());
+ SetAdjust(rRef.GetAdjust());
+ SetHorJustify(rRef.GetHorJustify());
+ SetVerJustify(rRef.GetVerJustify());
+ SetStacked(rRef.GetStacked());
+ SetMargin(rRef.GetMargin());
+ SetLinebreak(rRef.GetLinebreak());
+ SetRotateAngle(rRef.GetRotateAngle());
+ SetRotateMode(rRef.GetRotateMode());
+
+ return *this;
+}
+
+const sal_uInt16 AUTOFORMAT_DATA_ID_641 = 10002;
+const sal_uInt16 AUTOFORMAT_DATA_ID_300OVRLN = 10032;
+const sal_uInt16 AUTOFORMAT_DATA_ID_680DR14 = 10012;
+const sal_uInt16 AUTOFORMAT_DATA_ID_504 = 9802;
+
+bool AutoFormatBase::LoadBlockA( SvStream& rStream, const AutoFormatVersions& rVersions, sal_uInt16 nVer )
+{
+ legacy::SvxFont::Create(*m_aFont, rStream, rVersions.nFontVersion);
+
+ if( rStream.GetStreamCharSet() == m_aFont->GetCharSet() )
+ {
+ m_aFont->SetCharSet(::osl_getThreadTextEncoding());
+ }
+
+ legacy::SvxFontHeight::Create(*m_aHeight, rStream, rVersions.nFontHeightVersion);
+ legacy::SvxWeight::Create(*m_aWeight, rStream, rVersions.nWeightVersion);
+ legacy::SvxPosture::Create(*m_aPosture, rStream, rVersions.nPostureVersion);
+
+ // --- from 641 on: CJK and CTL font settings
+ if( AUTOFORMAT_DATA_ID_641 <= nVer )
+ {
+ legacy::SvxFont::Create(*m_aCJKFont, rStream, rVersions.nFontVersion);
+ legacy::SvxFontHeight::Create(*m_aCJKHeight, rStream, rVersions.nFontHeightVersion);
+ legacy::SvxWeight::Create(*m_aCJKWeight, rStream, rVersions.nWeightVersion);
+ legacy::SvxPosture::Create(*m_aCJKPosture, rStream, rVersions.nPostureVersion);
+
+ legacy::SvxFont::Create(*m_aCTLFont, rStream, rVersions.nFontVersion);
+ legacy::SvxFontHeight::Create(*m_aCTLHeight, rStream, rVersions.nFontHeightVersion);
+ legacy::SvxWeight::Create(*m_aCTLWeight, rStream, rVersions.nWeightVersion);
+ legacy::SvxPosture::Create(*m_aCTLPosture, rStream, rVersions.nPostureVersion);
+ }
+
+ legacy::SvxTextLine::Create(*m_aUnderline, rStream, rVersions.nUnderlineVersion);
+
+ if( nVer >= AUTOFORMAT_DATA_ID_300OVRLN )
+ {
+ legacy::SvxTextLine::Create(*m_aOverline, rStream, rVersions.nOverlineVersion);
+ }
+
+ legacy::SvxCrossedOut::Create(*m_aCrossedOut, rStream, rVersions.nCrossedOutVersion);
+ legacy::SfxBool::Create(*m_aContour, rStream, rVersions.nContourVersion);
+ legacy::SfxBool::Create(*m_aShadowed, rStream, rVersions.nShadowedVersion);
+ legacy::SvxColor::Create(*m_aColor, rStream, rVersions.nColorVersion);
+ legacy::SvxBox::Create(*m_aBox, rStream, rVersions.nBoxVersion);
+
+ // --- from 680/dr14 on: diagonal frame lines
+ if( nVer >= AUTOFORMAT_DATA_ID_680DR14 )
+ {
+ legacy::SvxLine::Create(*m_aTLBR, rStream, rVersions.nLineVersion);
+ legacy::SvxLine::Create(*m_aBLTR, rStream, rVersions.nLineVersion);
+ }
+
+ legacy::SvxBrush::Create(*m_aBackground, rStream, rVersions.nBrushVersion);
+ legacy::SvxAdjust::Create(*m_aAdjust, rStream, rVersions.nAdjustVersion);
+
+ return ERRCODE_NONE == rStream.GetError();
+}
+
+bool AutoFormatBase::LoadBlockB( SvStream& rStream, const AutoFormatVersions& rVersions, sal_uInt16 nVer )
+{
+ legacy::SvxHorJustify::Create(*m_aHorJustify, rStream, rVersions.nHorJustifyVersion);
+ legacy::SvxVerJustify::Create(*m_aVerJustify, rStream, rVersions.nVerJustifyVersion);
+ SvxOrientationItem aOrientation( SvxCellOrientation::Standard, TypedWhichId<SvxOrientationItem>(0));
+ legacy::SvxOrientation::Create(aOrientation, rStream, rVersions.nOrientationVersion);
+ legacy::SvxMargin::Create(*m_aMargin, rStream, rVersions.nMarginVersion);
+ legacy::SfxBool::Create(*m_aLinebreak, rStream, rVersions.nBoolVersion);
+
+ if ( nVer >= AUTOFORMAT_DATA_ID_504 )
+ {
+ legacy::CntInt32::Create(*m_aRotateAngle, rStream, rVersions.nInt32Version);
+ legacy::SvxRotateMode::Create(*m_aRotateMode, rStream, rVersions.nRotateModeVersion);
+ }
+
+ m_aStacked->SetValue( aOrientation.IsStacked() );
+ m_aRotateAngle->SetValue( aOrientation.GetRotation( Degree100(m_aRotateAngle->GetValue()) ).get() );
+
+ return ERRCODE_NONE == rStream.GetError();
+}
+
+bool AutoFormatBase::SaveBlockA( SvStream& rStream, sal_uInt16 fileVersion ) const
+{
+ legacy::SvxFont::Store(*m_aFont, rStream, legacy::SvxFont::GetVersion(fileVersion));
+ legacy::SvxFontHeight::Store(*m_aHeight, rStream, legacy::SvxFontHeight::GetVersion(fileVersion));
+ legacy::SvxWeight::Store(*m_aWeight, rStream, legacy::SvxWeight::GetVersion(fileVersion));
+ legacy::SvxPosture::Store(*m_aPosture, rStream, legacy::SvxPosture::GetVersion(fileVersion));
+
+ // --- from 641 on: CJK and CTL font settings
+ legacy::SvxFont::Store(*m_aCJKFont, rStream, legacy::SvxFont::GetVersion(fileVersion));
+ legacy::SvxFontHeight::Store(*m_aCJKHeight, rStream, legacy::SvxFontHeight::GetVersion(fileVersion));
+ legacy::SvxWeight::Store(*m_aCJKWeight, rStream, legacy::SvxWeight::GetVersion(fileVersion));
+ legacy::SvxPosture::Store(*m_aCJKPosture, rStream, legacy::SvxPosture::GetVersion(fileVersion));
+
+ legacy::SvxFont::Store(*m_aCTLFont, rStream, legacy::SvxFont::GetVersion(fileVersion));
+ legacy::SvxFontHeight::Store(*m_aCTLHeight, rStream, legacy::SvxFontHeight::GetVersion(fileVersion));
+ legacy::SvxWeight::Store(*m_aCTLWeight, rStream, legacy::SvxWeight::GetVersion(fileVersion));
+ legacy::SvxPosture::Store(*m_aCTLPosture, rStream, legacy::SvxPosture::GetVersion(fileVersion));
+
+ legacy::SvxTextLine::Store(*m_aUnderline, rStream, legacy::SvxTextLine::GetVersion(fileVersion));
+
+ // --- from DEV300/overline2 on: overline support
+ legacy::SvxTextLine::Store(*m_aOverline, rStream, legacy::SvxTextLine::GetVersion(fileVersion));
+ legacy::SvxCrossedOut::Store(*m_aCrossedOut, rStream, legacy::SvxCrossedOut::GetVersion(fileVersion));
+ legacy::SfxBool::Store(*m_aContour, rStream, legacy::SfxBool::GetVersion(fileVersion));
+ legacy::SfxBool::Store(*m_aShadowed, rStream, legacy::SfxBool::GetVersion(fileVersion));
+ legacy::SvxColor::Store(*m_aColor, rStream, legacy::SvxColor::GetVersion(fileVersion));
+ legacy::SvxBox::Store(*m_aBox, rStream, legacy::SvxBox::GetVersion(fileVersion));
+
+ // --- from 680/dr14 on: diagonal frame lines
+ legacy::SvxLine::Store(*m_aTLBR, rStream, legacy::SvxLine::GetVersion(fileVersion));
+ legacy::SvxLine::Store(*m_aBLTR, rStream, legacy::SvxLine::GetVersion(fileVersion));
+ legacy::SvxBrush::Store(*m_aBackground, rStream, legacy::SvxBrush::GetVersion(fileVersion));
+ legacy::SvxAdjust::Store(*m_aAdjust, rStream, legacy::SvxAdjust::GetVersion(fileVersion));
+
+ return ERRCODE_NONE == rStream.GetError();
+}
+
+bool AutoFormatBase::SaveBlockB( SvStream& rStream, sal_uInt16 fileVersion ) const
+{
+ legacy::SvxHorJustify::Store(*m_aHorJustify, rStream, legacy::SvxHorJustify::GetVersion(fileVersion));
+ legacy::SvxVerJustify::Store(*m_aVerJustify, rStream, legacy::SvxVerJustify::GetVersion(fileVersion));
+ SvxOrientationItem aOrientation( Degree100(m_aRotateAngle->GetValue()), m_aStacked->GetValue(), TypedWhichId<SvxOrientationItem>(0) );
+ legacy::SvxOrientation::Store(aOrientation, rStream, legacy::SvxOrientation::GetVersion(fileVersion));
+ legacy::SvxMargin::Store(*m_aMargin, rStream, legacy::SvxMargin::GetVersion(fileVersion));
+ legacy::SfxBool::Store(*m_aLinebreak, rStream, legacy::SfxBool::GetVersion(fileVersion));
+
+ // Calc Rotation from SO5
+ legacy::CntInt32::Store(*m_aRotateAngle, rStream, legacy::CntInt32::GetVersion(fileVersion));
+ legacy::SvxRotateMode::Store(*m_aRotateMode, rStream, legacy::SvxRotateMode::GetVersion(fileVersion));
+
+ return ERRCODE_NONE == rStream.GetError();
+}
+
+//////////////////////////////////////////////////////////////////////////////
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/items/chrtitem.cxx b/svx/source/items/chrtitem.cxx
new file mode 100644
index 000000000..6fb6e8523
--- /dev/null
+++ b/svx/source/items/chrtitem.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 <rtl/math.hxx>
+#include <unotools/intlwrapper.hxx>
+#include <unotools/localedatawrapper.hxx>
+#include <com/sun/star/chart/ChartAxisArrangeOrderType.hpp>
+
+#include <svx/chrtitem.hxx>
+
+using namespace ::com::sun::star;
+
+
+SfxPoolItem* SvxDoubleItem::CreateDefault() { return new SvxDoubleItem(0.0, TypedWhichId<SvxDoubleItem>(0));}
+
+SvxChartTextOrderItem::SvxChartTextOrderItem(SvxChartTextOrder eOrder,
+ TypedWhichId<SvxChartTextOrderItem> nId) :
+ SfxEnumItem(nId, eOrder)
+{
+}
+
+SvxChartTextOrderItem* SvxChartTextOrderItem::Clone(SfxItemPool* /*pPool*/) const
+{
+ return new SvxChartTextOrderItem(*this);
+}
+
+bool SvxChartTextOrderItem::QueryValue( css::uno::Any& rVal, sal_uInt8 /*nMemberId*/ ) const
+{
+ // the order of the two enums is not equal, so a mapping is required
+ css::chart::ChartAxisArrangeOrderType eAO;
+ SvxChartTextOrder eOrder( GetValue());
+
+ switch( eOrder )
+ {
+ case SvxChartTextOrder::SideBySide:
+ eAO = css::chart::ChartAxisArrangeOrderType_SIDE_BY_SIDE; break;
+ case SvxChartTextOrder::UpDown:
+ eAO = css::chart::ChartAxisArrangeOrderType_STAGGER_ODD; break;
+ case SvxChartTextOrder::DownUp:
+ eAO = css::chart::ChartAxisArrangeOrderType_STAGGER_EVEN; break;
+ case SvxChartTextOrder::Auto:
+ eAO = css::chart::ChartAxisArrangeOrderType_AUTO; break;
+ }
+
+ rVal <<= eAO;
+
+ return true;
+}
+
+
+bool SvxChartTextOrderItem::PutValue( const css::uno::Any& rVal, sal_uInt8 /*nMemberId*/ )
+{
+ // the order of the two enums is not equal, so a mapping is required
+ css::chart::ChartAxisArrangeOrderType eAO;
+ SvxChartTextOrder eOrder;
+
+ if(!(rVal >>= eAO))
+ {
+ // also try an int (for Basic)
+ sal_Int32 nAO = 0;
+ if(!(rVal >>= nAO))
+ return false;
+ eAO = static_cast< css::chart::ChartAxisArrangeOrderType >( nAO );
+ }
+
+ switch( eAO )
+ {
+ case css::chart::ChartAxisArrangeOrderType_SIDE_BY_SIDE:
+ eOrder = SvxChartTextOrder::SideBySide; break;
+ case css::chart::ChartAxisArrangeOrderType_STAGGER_ODD:
+ eOrder = SvxChartTextOrder::UpDown; break;
+ case css::chart::ChartAxisArrangeOrderType_STAGGER_EVEN:
+ eOrder = SvxChartTextOrder::DownUp; break;
+ case css::chart::ChartAxisArrangeOrderType_AUTO:
+ eOrder = SvxChartTextOrder::Auto; break;
+ default:
+ return false;
+ }
+
+ SetValue( eOrder );
+
+ return true;
+}
+
+SvxDoubleItem::SvxDoubleItem(double fValue, TypedWhichId<SvxDoubleItem> nId) :
+ SfxPoolItem(nId),
+ fVal(fValue)
+{
+}
+
+SvxDoubleItem::SvxDoubleItem(const SvxDoubleItem& rItem) :
+ SfxPoolItem(rItem),
+ fVal(rItem.fVal)
+{
+}
+
+bool SvxDoubleItem::GetPresentation
+ ( SfxItemPresentation /*ePresentation*/, MapUnit /*eCoreMetric*/,
+ MapUnit /*ePresentationMetric*/, OUString& rText,
+ const IntlWrapper& rIntlWrapper) const
+{
+ rText = ::rtl::math::doubleToUString( fVal, rtl_math_StringFormat_E, 4,
+ rIntlWrapper.getLocaleData()->getNumDecimalSep()[0], true );
+ return true;
+}
+
+bool SvxDoubleItem::operator == (const SfxPoolItem& rItem) const
+{
+ assert(SfxPoolItem::operator==(rItem));
+ return static_cast<const SvxDoubleItem&>(rItem).fVal == fVal;
+}
+
+SvxDoubleItem* SvxDoubleItem::Clone(SfxItemPool* /*pPool*/) const
+{
+ return new SvxDoubleItem(*this);
+}
+
+bool SvxDoubleItem::QueryValue( uno::Any& rVal, sal_uInt8 /*nMemberId*/ ) const
+{
+ rVal <<= fVal;
+ return true;
+}
+
+bool SvxDoubleItem::PutValue( const uno::Any& rVal, sal_uInt8 /*nMemberId*/ )
+{
+ return rVal >>= fVal;
+}
+
+SvxChartKindErrorItem::SvxChartKindErrorItem(SvxChartKindError eOrient,
+ TypedWhichId<SvxChartKindErrorItem> nId) :
+ SfxEnumItem(nId, eOrient)
+{
+}
+
+SvxChartKindErrorItem* SvxChartKindErrorItem::Clone(SfxItemPool* /*pPool*/) const
+{
+ return new SvxChartKindErrorItem(*this);
+}
+
+SvxChartIndicateItem::SvxChartIndicateItem(SvxChartIndicate eOrient,
+ TypedWhichId<SvxChartIndicateItem> nId) :
+ SfxEnumItem(nId, eOrient)
+{
+}
+
+SvxChartIndicateItem* SvxChartIndicateItem::Clone(SfxItemPool* /*pPool*/) const
+{
+ return new SvxChartIndicateItem(*this);
+}
+
+SvxChartRegressItem::SvxChartRegressItem(SvxChartRegress eOrient,
+ TypedWhichId<SvxChartRegressItem> nId) :
+ SfxEnumItem(nId, eOrient)
+{
+}
+
+SvxChartRegressItem* SvxChartRegressItem::Clone(SfxItemPool* /*pPool*/) const
+{
+ return new SvxChartRegressItem(*this);
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/items/clipfmtitem.cxx b/svx/source/items/clipfmtitem.cxx
new file mode 100644
index 000000000..caae609f6
--- /dev/null
+++ b/svx/source/items/clipfmtitem.cxx
@@ -0,0 +1,151 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <sal/config.h>
+
+#include <vector>
+
+#include <svx/clipfmtitem.hxx>
+#include <com/sun/star/frame/status/ClipboardFormats.hpp>
+
+struct SvxClipboardFormatItem_Impl
+{
+ std::vector<OUString> aFmtNms;
+ std::vector<SotClipboardFormatId> aFmtIds;
+
+ SvxClipboardFormatItem_Impl() {}
+};
+
+SfxPoolItem* SvxClipboardFormatItem::CreateDefault() { return new SvxClipboardFormatItem(TypedWhichId<SvxClipboardFormatItem>(0)); };
+
+SvxClipboardFormatItem::SvxClipboardFormatItem( TypedWhichId<SvxClipboardFormatItem> nId )
+ : SfxPoolItem( nId ), pImpl( new SvxClipboardFormatItem_Impl )
+{
+}
+
+SvxClipboardFormatItem::SvxClipboardFormatItem( const SvxClipboardFormatItem& rCpy )
+ : SfxPoolItem( rCpy ),
+ pImpl( new SvxClipboardFormatItem_Impl( *rCpy.pImpl ) )
+{
+}
+
+SvxClipboardFormatItem::~SvxClipboardFormatItem()
+{
+}
+
+bool SvxClipboardFormatItem::QueryValue( css::uno::Any& rVal, sal_uInt8 /*nMemberId*/ ) const
+{
+ sal_uInt16 nCount = Count();
+
+ css::frame::status::ClipboardFormats aClipFormats;
+
+ aClipFormats.Identifiers.realloc( nCount );
+ auto pIdentifiers = aClipFormats.Identifiers.getArray();
+ aClipFormats.Names.realloc( nCount );
+ auto pNames = aClipFormats.Names.getArray();
+ for ( sal_uInt16 n=0; n < nCount; n++ )
+ {
+ pIdentifiers[n] = static_cast<sal_Int64>(GetClipbrdFormatId( n ));
+ pNames[n] = GetClipbrdFormatName( n );
+ }
+
+ rVal <<= aClipFormats;
+ return true;
+}
+
+bool SvxClipboardFormatItem::PutValue( const css::uno::Any& rVal, sal_uInt8 /*nMemberId*/ )
+{
+ css::frame::status::ClipboardFormats aClipFormats;
+ if ( rVal >>= aClipFormats )
+ {
+ sal_uInt16 nCount = sal_uInt16( aClipFormats.Identifiers.getLength() );
+
+ pImpl->aFmtIds.clear();
+ pImpl->aFmtNms.clear();
+ for ( sal_uInt16 n=0; n < nCount; ++n )
+ AddClipbrdFormat( static_cast<SotClipboardFormatId>(aClipFormats.Identifiers[n]), aClipFormats.Names[n], n );
+
+ return true;
+ }
+
+ return false;
+}
+
+bool SvxClipboardFormatItem::operator==( const SfxPoolItem& rComp ) const
+{
+ if (!SfxPoolItem::operator==(rComp))
+ return false;
+ const SvxClipboardFormatItem& rCmp = static_cast<const SvxClipboardFormatItem&>(rComp);
+ if(rCmp.pImpl->aFmtNms.size() != pImpl->aFmtNms.size())
+ return false;
+
+ int nRet = 1;
+ for( sal_uInt16 n = 0, nEnd = rCmp.pImpl->aFmtNms.size(); n < nEnd; ++n )
+ {
+ if( pImpl->aFmtIds[ n ] != rCmp.pImpl->aFmtIds[ n ] ||
+ pImpl->aFmtNms[n] != rCmp.pImpl->aFmtNms[n] )
+ {
+ nRet = 0;
+ break;
+ }
+ }
+
+ return nRet;
+}
+
+SvxClipboardFormatItem* SvxClipboardFormatItem::Clone( SfxItemPool * /*pPool*/ ) const
+{
+ return new SvxClipboardFormatItem( *this );
+}
+
+void SvxClipboardFormatItem::AddClipbrdFormat( SotClipboardFormatId nId )
+{
+ sal_uInt16 nPos = pImpl->aFmtNms.size();
+
+ pImpl->aFmtNms.insert( pImpl->aFmtNms.begin() + nPos, OUString());
+ pImpl->aFmtIds.insert( pImpl->aFmtIds.begin() + nPos, nId );
+}
+
+void SvxClipboardFormatItem::AddClipbrdFormat( SotClipboardFormatId nId, const OUString& rName,
+ sal_uInt16 nPos )
+{
+ if( nPos > pImpl->aFmtNms.size() )
+ nPos = pImpl->aFmtNms.size();
+
+ pImpl->aFmtNms.insert(pImpl->aFmtNms.begin() + nPos, rName);
+ pImpl->aFmtIds.insert( pImpl->aFmtIds.begin()+nPos, nId );
+}
+
+sal_uInt16 SvxClipboardFormatItem::Count() const
+{
+ return pImpl->aFmtIds.size();
+}
+
+SotClipboardFormatId SvxClipboardFormatItem::GetClipbrdFormatId( sal_uInt16 nPos ) const
+{
+ return pImpl->aFmtIds[ nPos ];
+}
+
+OUString const & SvxClipboardFormatItem::GetClipbrdFormatName( sal_uInt16 nPos ) const
+{
+ return pImpl->aFmtNms[nPos];
+}
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/items/customshapeitem.cxx b/svx/source/items/customshapeitem.cxx
new file mode 100644
index 000000000..3caa270de
--- /dev/null
+++ b/svx/source/items/customshapeitem.cxx
@@ -0,0 +1,347 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <sal/config.h>
+
+#include <o3tl/any.hxx>
+#include <comphelper/anycompare.hxx>
+#include <comphelper/anytohash.hxx>
+#include <svx/sdasitm.hxx>
+
+#include <com/sun/star/beans/PropertyValue.hpp>
+
+using namespace ::std;
+using namespace com::sun::star;
+
+
+SdrCustomShapeGeometryItem::SdrCustomShapeGeometryItem()
+: SfxPoolItem( SDRATTR_CUSTOMSHAPE_GEOMETRY )
+{}
+
+SdrCustomShapeGeometryItem::SdrCustomShapeGeometryItem( const uno::Sequence< beans::PropertyValue >& rVal )
+: SfxPoolItem( SDRATTR_CUSTOMSHAPE_GEOMETRY )
+{
+ SetPropSeq( rVal );
+}
+
+css::uno::Any* SdrCustomShapeGeometryItem::GetPropertyValueByName( const OUString& rPropName )
+{
+ css::uno::Any* pRet = nullptr;
+ PropertyHashMap::iterator aHashIter( aPropHashMap.find( rPropName ) );
+ if ( aHashIter != aPropHashMap.end() )
+ pRet = &aPropSeq.getArray()[ (*aHashIter).second ].Value;
+ return pRet;
+}
+
+const css::uno::Any* SdrCustomShapeGeometryItem::GetPropertyValueByName( const OUString& rPropName ) const
+{
+ const css::uno::Any* pRet = nullptr;
+ PropertyHashMap::const_iterator aHashIter( aPropHashMap.find( rPropName ) );
+ if ( aHashIter != aPropHashMap.end() )
+ pRet = &aPropSeq[ (*aHashIter).second ].Value;
+ return pRet;
+}
+
+css::uno::Any* SdrCustomShapeGeometryItem::GetPropertyValueByName( const OUString& rSequenceName, const OUString& rPropName )
+{
+ css::uno::Any* pRet = nullptr;
+ css::uno::Any* pSeqAny = GetPropertyValueByName( rSequenceName );
+ if ( pSeqAny )
+ {
+ if ( auto rSecSequence = o3tl::tryAccess<css::uno::Sequence<beans::PropertyValue>>(*pSeqAny) )
+ {
+ PropertyPairHashMap::iterator aHashIter( aPropPairHashMap.find( PropertyPair( rSequenceName, rPropName ) ) );
+ if ( aHashIter != aPropPairHashMap.end() )
+ {
+ pRet = &const_cast<css::uno::Sequence<css::beans::PropertyValue> &>(*rSecSequence).getArray()[ (*aHashIter).second ].Value;
+ }
+ }
+ }
+ return pRet;
+}
+
+const css::uno::Any* SdrCustomShapeGeometryItem::GetPropertyValueByName( const OUString& rSequenceName, const OUString& rPropName ) const
+{
+ const css::uno::Any* pRet = nullptr;
+ const css::uno::Any* pSeqAny = GetPropertyValueByName( rSequenceName );
+ if ( pSeqAny )
+ {
+ if ( auto rSecSequence = o3tl::tryAccess<css::uno::Sequence<beans::PropertyValue>>(*pSeqAny) )
+ {
+ PropertyPairHashMap::const_iterator aHashIter( aPropPairHashMap.find( PropertyPair( rSequenceName, rPropName ) ) );
+ if ( aHashIter != aPropPairHashMap.end() )
+ {
+ pRet = &(*rSecSequence)[ (*aHashIter).second ].Value;
+ }
+ }
+ }
+ return pRet;
+}
+
+void SdrCustomShapeGeometryItem::SetPropertyValue( const css::beans::PropertyValue& rPropVal )
+{
+ css::uno::Any* pAny = GetPropertyValueByName( rPropVal.Name );
+ if ( pAny )
+ { // property is already available
+ if ( auto rSecSequence = o3tl::tryAccess<css::uno::Sequence<beans::PropertyValue>>(*pAny) )
+ { // old property is a sequence->each entry has to be removed from the HashPairMap
+ for ( auto const & i : *rSecSequence )
+ {
+ PropertyPairHashMap::iterator aHashIter( aPropPairHashMap.find( PropertyPair( rPropVal.Name, i.Name ) ) );
+ if ( aHashIter != aPropPairHashMap.end() )
+ aPropPairHashMap.erase( aHashIter );
+ }
+ }
+ *pAny = rPropVal.Value;
+ if ( auto rSecSequence = o3tl::tryAccess<css::uno::Sequence<beans::PropertyValue>>(*pAny) )
+ { // the new property is a sequence->each entry has to be inserted into the HashPairMap
+ for ( sal_Int32 i = 0; i < rSecSequence->getLength(); i++ )
+ {
+ beans::PropertyValue const & rPropVal2 = (*rSecSequence)[ i ];
+ aPropPairHashMap[ PropertyPair( rPropVal.Name, rPropVal2.Name ) ] = i;
+ }
+ }
+ }
+ else
+ { // it's a new property
+ assert(std::none_of(std::cbegin(aPropSeq), std::cend(aPropSeq),
+ [&rPropVal](beans::PropertyValue const& rVal)
+ { return rVal.Name == rPropVal.Name; } ));
+ sal_uInt32 nIndex = aPropSeq.getLength();
+ aPropSeq.realloc( nIndex + 1 );
+ aPropSeq.getArray()[ nIndex ] = rPropVal ;
+
+ aPropHashMap[ rPropVal.Name ] = nIndex;
+ }
+ InvalidateHash();
+}
+
+void SdrCustomShapeGeometryItem::SetPropertyValue( const OUString& rSequenceName, const css::beans::PropertyValue& rPropVal )
+{
+ css::uno::Any* pAny = GetPropertyValueByName( rSequenceName, rPropVal.Name );
+ if ( pAny ) // just replacing
+ *pAny = rPropVal.Value;
+ else
+ {
+ css::uno::Any* pSeqAny = GetPropertyValueByName( rSequenceName );
+ if( pSeqAny == nullptr )
+ {
+ css::uno::Sequence < beans::PropertyValue > aSeq;
+ beans::PropertyValue aValue;
+ aValue.Name = rSequenceName;
+ aValue.Value <<= aSeq;
+
+ assert(std::none_of(std::cbegin(aPropSeq), std::cend(aPropSeq),
+ [&rSequenceName](beans::PropertyValue const& rV)
+ { return rV.Name == rSequenceName; } ));
+ sal_uInt32 nIndex = aPropSeq.getLength();
+ aPropSeq.realloc( nIndex + 1 );
+ auto pPropSeq = aPropSeq.getArray();
+ pPropSeq[ nIndex ] = aValue;
+ aPropHashMap[ rSequenceName ] = nIndex;
+
+ pSeqAny = &pPropSeq[ nIndex ].Value;
+ }
+
+ if (auto pSecSequence = o3tl::tryAccess<css::uno::Sequence<beans::PropertyValue>>(*pSeqAny))
+ {
+ PropertyPairHashMap::iterator aHashIter(
+ aPropPairHashMap.find(PropertyPair(rSequenceName, rPropVal.Name)));
+ auto& rSeq = const_cast<css::uno::Sequence<css::beans::PropertyValue>&>(*pSecSequence);
+ if (aHashIter != aPropPairHashMap.end())
+ {
+ rSeq.getArray()[(*aHashIter).second].Value = rPropVal.Value;
+ }
+ else
+ {
+ const sal_Int32 nCount = pSecSequence->getLength();
+ rSeq.realloc(nCount + 1);
+ rSeq.getArray()[nCount] = rPropVal;
+
+ aPropPairHashMap[PropertyPair(rSequenceName, rPropVal.Name)] = nCount;
+ }
+ }
+ }
+ InvalidateHash();
+}
+
+void SdrCustomShapeGeometryItem::ClearPropertyValue( const OUString& rPropName )
+{
+ if ( !aPropSeq.hasElements() )
+ return;
+
+ PropertyHashMap::iterator aHashIter( aPropHashMap.find( rPropName ) );
+ if ( aHashIter == aPropHashMap.end() )
+ return;
+
+ auto pPropSeq = aPropSeq.getArray();
+ css::uno::Any& rSeqAny = pPropSeq[(*aHashIter).second].Value;
+ if (auto pSecSequence
+ = o3tl::tryAccess<css::uno::Sequence<beans::PropertyValue>>(rSeqAny))
+ {
+ for (const auto& rPropVal : *pSecSequence)
+ {
+ auto _aHashIter(aPropPairHashMap.find(PropertyPair(rPropName, rPropVal.Name)));
+ if (_aHashIter != aPropPairHashMap.end())
+ aPropPairHashMap.erase(_aHashIter); // removing property from pair hashmap
+ }
+ }
+ sal_Int32 nLength = aPropSeq.getLength();
+ if ( nLength )
+ {
+ sal_Int32 nIndex = (*aHashIter).second;
+ if ( nIndex != ( nLength - 1 ) ) // resizing sequence
+ {
+ PropertyHashMap::iterator aHashIter2( aPropHashMap.find( aPropSeq[ nLength - 1 ].Name ) );
+ (*aHashIter2).second = nIndex;
+ pPropSeq[ nIndex ] = aPropSeq[ nLength - 1 ];
+ }
+ aPropSeq.realloc( nLength - 1 );
+ }
+ aPropHashMap.erase( aHashIter ); // removing property from hashmap
+ InvalidateHash();
+}
+
+SdrCustomShapeGeometryItem::~SdrCustomShapeGeometryItem()
+{
+}
+
+bool SdrCustomShapeGeometryItem::operator==( const SfxPoolItem& rCmp ) const
+{
+ if( !SfxPoolItem::operator==( rCmp ))
+ return false;
+ const SdrCustomShapeGeometryItem& other = static_cast<const SdrCustomShapeGeometryItem&>(rCmp);
+ // This is called often by SfxItemPool, and comparing uno sequences is relatively slow.
+ // So keep a hash of the sequence and if either of the sequences has a usable hash,
+ // compare using that.
+ UpdateHash();
+ other.UpdateHash();
+ if( aHashState != other.aHashState )
+ return false;
+ if( aHashState == HashState::Valid && aHash != other.aHash )
+ return false;
+
+ return aPropSeq == other.aPropSeq;
+}
+
+bool SdrCustomShapeGeometryItem::operator<( const SfxPoolItem& rCmp ) const
+{
+ assert(dynamic_cast<const SdrCustomShapeGeometryItem*>( &rCmp ));
+ const SdrCustomShapeGeometryItem& other = static_cast<const SdrCustomShapeGeometryItem&>(rCmp);
+ // Again, try to optimize by checking hashes first (this is operator< for sorting purposes,
+ // so the ordering can be somewhat arbitrary).
+ UpdateHash();
+ other.UpdateHash();
+ if( aHashState != other.aHashState )
+ return aHashState < other.aHashState;
+ if( aHashState == HashState::Valid )
+ return aHash < other.aHash;
+
+ return comphelper::anyLess( css::uno::Any( aPropSeq ),
+ css::uno::Any( other.aPropSeq ));
+}
+
+void SdrCustomShapeGeometryItem::UpdateHash() const
+{
+ if( aHashState != HashState::Unknown )
+ return;
+ std::optional< size_t > hash = comphelper::anyToHash( css::uno::Any( aPropSeq ));
+ if( hash.has_value())
+ {
+ aHash = *hash;
+ aHashState = HashState::Valid;
+ }
+ else
+ aHashState = HashState::Unusable;
+}
+
+void SdrCustomShapeGeometryItem::InvalidateHash()
+{
+ aHashState = HashState::Unknown;
+}
+
+bool SdrCustomShapeGeometryItem::GetPresentation(
+ SfxItemPresentation ePresentation, MapUnit /*eCoreMetric*/,
+ MapUnit /*ePresentationMetric*/, OUString &rText, const IntlWrapper&) const
+{
+ rText += " ";
+ if ( ePresentation == SfxItemPresentation::Complete )
+ {
+ rText = " " + rText;
+ return true;
+ }
+ else if ( ePresentation == SfxItemPresentation::Nameless )
+ return true;
+ return false;
+}
+
+SdrCustomShapeGeometryItem* SdrCustomShapeGeometryItem::Clone( SfxItemPool * /*pPool*/ ) const
+{
+ return new SdrCustomShapeGeometryItem( aPropSeq );
+}
+
+bool SdrCustomShapeGeometryItem::QueryValue( uno::Any& rVal, sal_uInt8 /*nMemberId*/ ) const
+{
+ rVal <<= aPropSeq;
+ return true;
+}
+
+bool SdrCustomShapeGeometryItem::PutValue( const uno::Any& rVal, sal_uInt8 /*nMemberId*/ )
+{
+ css::uno::Sequence< css::beans::PropertyValue > propSeq;
+ if ( ! ( rVal >>= propSeq ) )
+ return false;
+
+ SetPropSeq( propSeq );
+ return true;
+}
+
+void SdrCustomShapeGeometryItem::SetPropSeq( const css::uno::Sequence< css::beans::PropertyValue >& rVal )
+{
+ if( aPropSeq == rVal )
+ return;
+
+ aPropSeq = rVal;
+ aPropHashMap.clear();
+ aPropPairHashMap.clear();
+ for ( sal_Int32 i = 0; i < aPropSeq.getLength(); i++ )
+ {
+ const beans::PropertyValue& rPropVal = aPropSeq[ i ];
+ std::pair<PropertyHashMap::iterator, bool> const ret(
+ aPropHashMap.insert(std::make_pair(rPropVal.Name, i)));
+ assert(ret.second); // serious bug: duplicate xml attribute exported
+ if (!ret.second)
+ {
+ throw uno::RuntimeException(
+ "CustomShapeGeometry has duplicate property " + rPropVal.Name);
+ }
+ if (auto rPropSeq = o3tl::tryAccess<uno::Sequence<beans::PropertyValue>>(
+ rPropVal.Value))
+ {
+ for ( sal_Int32 j = 0; j < rPropSeq->getLength(); j++ )
+ {
+ beans::PropertyValue const & rPropVal2 = (*rPropSeq)[ j ];
+ aPropPairHashMap[ PropertyPair( rPropVal.Name, rPropVal2.Name ) ] = j;
+ }
+ }
+ }
+ InvalidateHash();
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/items/drawitem.cxx b/svx/source/items/drawitem.cxx
new file mode 100644
index 000000000..24a6acb39
--- /dev/null
+++ b/svx/source/items/drawitem.cxx
@@ -0,0 +1,349 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <svx/drawitem.hxx>
+#include <svx/xtable.hxx>
+
+using namespace ::com::sun::star;
+
+
+SfxPoolItem* SvxColorListItem::CreateDefault() { return new SvxColorListItem ;}
+SfxPoolItem* SvxGradientListItem::CreateDefault() { return new SvxGradientListItem ;}
+SfxPoolItem* SvxHatchListItem::CreateDefault() { return new SvxHatchListItem ;}
+SfxPoolItem* SvxBitmapListItem::CreateDefault() { return new SvxBitmapListItem ;}
+SfxPoolItem* SvxPatternListItem::CreateDefault() { return new SvxPatternListItem ;}
+SfxPoolItem* SvxDashListItem::CreateDefault() { return new SvxDashListItem ;}
+SfxPoolItem* SvxLineEndListItem::CreateDefault() { return new SvxLineEndListItem ;}
+
+SvxColorListItem::SvxColorListItem()
+{
+}
+
+
+SvxColorListItem::SvxColorListItem( XColorListRef const & pTable, TypedWhichId<SvxColorListItem> nW ) :
+ SfxPoolItem( nW ),
+ pColorList( pTable )
+{
+}
+
+
+SvxColorListItem::SvxColorListItem( const SvxColorListItem& rItem ) :
+ SfxPoolItem( rItem ),
+ pColorList( rItem.pColorList )
+{
+}
+
+bool SvxColorListItem::GetPresentation
+(
+ SfxItemPresentation /*ePres*/,
+ MapUnit /*eCoreUnit*/,
+ MapUnit /*ePresUnit*/,
+ OUString& rText, const IntlWrapper&
+) const
+{
+ rText.clear();
+ return false;
+}
+
+bool SvxColorListItem::operator==( const SfxPoolItem& rItem ) const
+{
+ assert(SfxPoolItem::operator==(rItem));
+ return static_cast<const SvxColorListItem&>(rItem).pColorList == pColorList;
+}
+
+SvxColorListItem* SvxColorListItem::Clone( SfxItemPool * ) const
+{
+ return new SvxColorListItem( *this );
+}
+
+// Should be a template class but ...
+#define QUERY_PUT_IMPL(svtype, xtype) \
+bool svtype::QueryValue( css::uno::Any& rVal, sal_uInt8 ) const \
+{ \
+ rVal <<= uno::Reference< uno::XWeak >( p##xtype ); \
+ return true; \
+} \
+\
+bool svtype::PutValue( const css::uno::Any& rVal, sal_uInt8 ) \
+{ \
+ uno::Reference< uno::XWeak > xRef; \
+ if( rVal >>= xRef ) { \
+ p##xtype = X##xtype##Ref(dynamic_cast<X##xtype *>(xRef.get())); \
+ return true; \
+ } \
+ return false; \
+}
+
+QUERY_PUT_IMPL( SvxColorListItem, ColorList )
+
+SvxGradientListItem::SvxGradientListItem()
+{
+}
+
+SvxGradientListItem::SvxGradientListItem( XGradientListRef const & pList, TypedWhichId<SvxGradientListItem> nW ) :
+ SfxPoolItem( nW ),
+ pGradientList( pList )
+{
+}
+
+
+SvxGradientListItem::SvxGradientListItem( const SvxGradientListItem& rItem ) :
+ SfxPoolItem( rItem ),
+ pGradientList( rItem.pGradientList )
+{
+}
+
+
+bool SvxGradientListItem::GetPresentation
+(
+ SfxItemPresentation /*ePres*/,
+ MapUnit /*eCoreUnit*/,
+ MapUnit /*ePresUnit*/,
+ OUString& rText, const IntlWrapper&
+) const
+{
+ rText.clear();
+ return false;
+}
+
+
+bool SvxGradientListItem::operator==( const SfxPoolItem& rItem ) const
+{
+ assert(SfxPoolItem::operator==(rItem));
+ return static_cast<const SvxGradientListItem&>(rItem).pGradientList == pGradientList;
+}
+
+SvxGradientListItem* SvxGradientListItem::Clone( SfxItemPool * ) const
+{
+ return new SvxGradientListItem( *this );
+}
+
+QUERY_PUT_IMPL( SvxGradientListItem, GradientList )
+
+SvxHatchListItem::SvxHatchListItem()
+{
+}
+
+
+SvxHatchListItem::SvxHatchListItem( XHatchListRef const & pList, TypedWhichId<SvxHatchListItem> nW ) :
+ SfxPoolItem( nW ),
+ pHatchList( pList )
+{
+}
+
+
+SvxHatchListItem::SvxHatchListItem( const SvxHatchListItem& rItem ) :
+ SfxPoolItem( rItem ),
+ pHatchList( rItem.pHatchList )
+{
+}
+
+
+bool SvxHatchListItem::GetPresentation
+(
+ SfxItemPresentation /*ePres*/,
+ MapUnit /*eCoreUnit*/,
+ MapUnit /*ePresUnit*/,
+ OUString& rText, const IntlWrapper&
+) const
+{
+ rText.clear();
+ return false;
+}
+
+
+bool SvxHatchListItem::operator==( const SfxPoolItem& rItem ) const
+{
+ assert(SfxPoolItem::operator==(rItem));
+ return static_cast<const SvxHatchListItem&>(rItem).pHatchList == pHatchList;
+}
+
+SvxHatchListItem* SvxHatchListItem::Clone( SfxItemPool * ) const
+{
+ return new SvxHatchListItem( *this );
+}
+
+QUERY_PUT_IMPL( SvxHatchListItem, HatchList )
+
+SvxBitmapListItem::SvxBitmapListItem()
+{
+}
+
+SvxBitmapListItem::SvxBitmapListItem( XBitmapListRef const & pList, TypedWhichId<SvxBitmapListItem> nW ) :
+ SfxPoolItem( nW ),
+ pBitmapList( pList )
+{
+}
+
+SvxBitmapListItem::SvxBitmapListItem( const SvxBitmapListItem& rItem ) :
+ SfxPoolItem( rItem ),
+ pBitmapList( rItem.pBitmapList )
+{
+}
+
+bool SvxBitmapListItem::GetPresentation
+(
+ SfxItemPresentation /*ePres*/,
+ MapUnit /*eCoreUnit*/,
+ MapUnit /*ePresUnit*/,
+ OUString& rText, const IntlWrapper&
+) const
+{
+ rText.clear();
+ return false;
+}
+
+bool SvxBitmapListItem::operator==( const SfxPoolItem& rItem ) const
+{
+ assert(SfxPoolItem::operator==(rItem));
+ return static_cast<const SvxBitmapListItem&>(rItem).pBitmapList == pBitmapList;
+}
+
+SvxBitmapListItem* SvxBitmapListItem::Clone( SfxItemPool * ) const
+{
+ return new SvxBitmapListItem( *this );
+}
+
+QUERY_PUT_IMPL( SvxBitmapListItem, BitmapList )
+
+SvxPatternListItem::SvxPatternListItem()
+{
+}
+
+SvxPatternListItem::SvxPatternListItem( XPatternListRef const & pList, TypedWhichId<SvxPatternListItem> nW ) :
+ SfxPoolItem( nW ),
+ pPatternList( pList )
+{
+}
+
+SvxPatternListItem::SvxPatternListItem( const SvxPatternListItem& rItem ) :
+ SfxPoolItem( rItem ),
+ pPatternList( rItem.pPatternList )
+{
+}
+
+bool SvxPatternListItem::GetPresentation
+(
+ SfxItemPresentation /*ePres*/,
+ MapUnit /*eCoreUnit*/,
+ MapUnit /*ePresUnit*/,
+ OUString& rText, const IntlWrapper&
+) const
+{
+ rText.clear();
+ return false;
+}
+
+bool SvxPatternListItem::operator==( const SfxPoolItem& rItem ) const
+{
+ assert(SfxPoolItem::operator==(rItem));
+ return static_cast<const SvxPatternListItem&>(rItem).pPatternList == pPatternList;
+}
+
+SvxPatternListItem* SvxPatternListItem::Clone( SfxItemPool * ) const
+{
+ return new SvxPatternListItem( *this );
+}
+
+QUERY_PUT_IMPL( SvxPatternListItem, PatternList )
+
+SvxDashListItem::SvxDashListItem()
+{
+}
+
+SvxDashListItem::SvxDashListItem( XDashListRef const & pList, TypedWhichId<SvxDashListItem> nW ) :
+ SfxPoolItem( nW ),
+ pDashList( pList )
+{
+}
+
+SvxDashListItem::SvxDashListItem( const SvxDashListItem& rItem ) :
+ SfxPoolItem( rItem ),
+ pDashList( rItem.pDashList )
+{
+}
+
+bool SvxDashListItem::GetPresentation
+(
+ SfxItemPresentation /*ePres*/,
+ MapUnit /*eCoreUnit*/,
+ MapUnit /*ePresUnit*/,
+ OUString& rText, const IntlWrapper&
+) const
+{
+ rText.clear();
+ return false;
+}
+
+bool SvxDashListItem::operator==( const SfxPoolItem& rItem ) const
+{
+ assert(SfxPoolItem::operator==(rItem));
+ return static_cast<const SvxDashListItem&>(rItem).pDashList == pDashList;
+}
+
+SvxDashListItem* SvxDashListItem::Clone( SfxItemPool * ) const
+{
+ return new SvxDashListItem( *this );
+}
+
+QUERY_PUT_IMPL( SvxDashListItem, DashList )
+
+SvxLineEndListItem::SvxLineEndListItem()
+{
+}
+
+SvxLineEndListItem::SvxLineEndListItem( XLineEndListRef const & pList, TypedWhichId<SvxLineEndListItem> nW ) :
+ SfxPoolItem( nW ),
+ pLineEndList( pList )
+{
+}
+
+SvxLineEndListItem::SvxLineEndListItem( const SvxLineEndListItem& rItem ) :
+ SfxPoolItem( rItem ),
+ pLineEndList( rItem.pLineEndList )
+{
+}
+
+bool SvxLineEndListItem::GetPresentation
+(
+ SfxItemPresentation /*ePres*/,
+ MapUnit /*eCoreUnit*/,
+ MapUnit /*ePresUnit*/,
+ OUString& rText, const IntlWrapper&
+) const
+{
+ rText.clear();
+ return false;
+}
+
+bool SvxLineEndListItem::operator==( const SfxPoolItem& rItem ) const
+{
+ assert(SfxPoolItem::operator==(rItem));
+ return static_cast<const SvxLineEndListItem&>(rItem).pLineEndList == pLineEndList;
+}
+
+SvxLineEndListItem* SvxLineEndListItem::Clone( SfxItemPool * ) const
+{
+ return new SvxLineEndListItem( *this );
+}
+
+QUERY_PUT_IMPL( SvxLineEndListItem, LineEndList )
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/items/e3ditem.cxx b/svx/source/items/e3ditem.cxx
new file mode 100644
index 000000000..135c7efaf
--- /dev/null
+++ b/svx/source/items/e3ditem.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 <com/sun/star/drawing/Direction3D.hpp>
+#include <libxml/xmlwriter.h>
+
+#include <svx/e3ditem.hxx>
+
+using namespace ::com::sun::star;
+
+
+SvxB3DVectorItem::~SvxB3DVectorItem()
+{
+}
+
+
+SvxB3DVectorItem::SvxB3DVectorItem( TypedWhichId<SvxB3DVectorItem> _nWhich, const basegfx::B3DVector& rVal ) :
+ SfxPoolItem( _nWhich ),
+ aVal( rVal )
+{
+}
+
+
+SvxB3DVectorItem::SvxB3DVectorItem( const SvxB3DVectorItem& rItem ) :
+ SfxPoolItem( rItem ),
+ aVal( rItem.aVal )
+{
+}
+
+
+bool SvxB3DVectorItem::operator==( const SfxPoolItem &rItem ) const
+{
+ assert(SfxPoolItem::operator==(rItem));
+ return static_cast<const SvxB3DVectorItem&>(rItem).aVal == aVal;
+}
+
+SvxB3DVectorItem* SvxB3DVectorItem::Clone( SfxItemPool* /*pPool*/ ) const
+{
+ return new SvxB3DVectorItem( *this );
+}
+
+bool SvxB3DVectorItem::QueryValue( uno::Any& rVal, sal_uInt8 /*nMemberId*/ ) const
+{
+ assert(!std::isnan(aVal.getX()) && !std::isnan(aVal.getY()) && !std::isnan(aVal.getZ()));
+
+ drawing::Direction3D aDirection;
+
+ // enter values
+ aDirection.DirectionX = aVal.getX();
+ aDirection.DirectionY = aVal.getY();
+ aDirection.DirectionZ = aVal.getZ();
+
+ rVal <<= aDirection;
+ return true;
+}
+
+
+bool SvxB3DVectorItem::PutValue( const uno::Any& rVal, sal_uInt8 /*nMemberId*/ )
+{
+ drawing::Direction3D aDirection;
+ if(!(rVal >>= aDirection))
+ return false;
+
+ aVal.setX(aDirection.DirectionX);
+ aVal.setY(aDirection.DirectionY);
+ aVal.setZ(aDirection.DirectionZ);
+
+ assert(!std::isnan(aVal.getX()) && !std::isnan(aVal.getY()) && !std::isnan(aVal.getZ()));
+
+ return true;
+}
+
+
+void SvxB3DVectorItem::dumpAsXml(xmlTextWriterPtr pWriter) const
+{
+ (void)xmlTextWriterStartElement(pWriter, BAD_CAST("SvxB3DVectorItem"));
+ (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("whichId"), BAD_CAST(OString::number(Which()).getStr()));
+ (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("x"), BAD_CAST(OString::number(aVal.getX()).getStr()));
+ (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("y"), BAD_CAST(OString::number(aVal.getY()).getStr()));
+ (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("z"), BAD_CAST(OString::number(aVal.getZ()).getStr()));
+ (void)xmlTextWriterEndElement(pWriter);
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/items/galleryitem.cxx b/svx/source/items/galleryitem.cxx
new file mode 100644
index 000000000..5e50c5efb
--- /dev/null
+++ b/svx/source/items/galleryitem.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 <svx/galleryitem.hxx>
+#include <com/sun/star/gallery/GalleryItemType.hpp>
+#include <com/sun/star/graphic/XGraphic.hpp>
+#include <com/sun/star/lang/XComponent.hpp>
+#include <com/sun/star/beans/PropertyValue.hpp>
+#include <com/sun/star/uno/Sequence.hxx>
+
+#include <comphelper/propertyvalue.hxx>
+
+#include <cassert>
+
+
+SfxPoolItem* SvxGalleryItem::CreateDefault() { return new SvxGalleryItem; }
+
+SvxGalleryItem::SvxGalleryItem()
+ : m_nType( css::gallery::GalleryItemType::EMPTY )
+{
+}
+
+SvxGalleryItem::SvxGalleryItem( const SvxGalleryItem &rItem )
+ : SfxPoolItem( rItem )
+ , m_nType( rItem.m_nType )
+ , m_aURL( rItem.m_aURL )
+ , m_xDrawing( rItem.m_xDrawing )
+ , m_xGraphic( rItem.m_xGraphic )
+{
+}
+
+SvxGalleryItem::~SvxGalleryItem()
+{
+}
+
+bool SvxGalleryItem::QueryValue( css::uno::Any& rVal, sal_uInt8 /* nMemberId */ ) const
+{
+ css::uno::Sequence< css::beans::PropertyValue > aSeq{
+ comphelper::makePropertyValue(SVXGALLERYITEM_TYPE, m_nType),
+ comphelper::makePropertyValue(SVXGALLERYITEM_URL, m_aURL),
+ comphelper::makePropertyValue(SVXGALLERYITEM_FILTER, m_aURL),
+ comphelper::makePropertyValue(SVXGALLERYITEM_DRAWING, m_xDrawing),
+ comphelper::makePropertyValue(SVXGALLERYITEM_GRAPHIC, m_xGraphic)
+ };
+ assert(aSeq.getLength() == SVXGALLERYITEM_PARAMS);
+
+ rVal <<= aSeq;
+
+ return true;
+}
+
+bool SvxGalleryItem::PutValue( const css::uno::Any& rVal, sal_uInt8 /* nMemberId */)
+{
+ css::uno::Sequence< css::beans::PropertyValue > aSeq;
+
+ if ( !( rVal >>= aSeq ) || ( aSeq.getLength() < SVXGALLERYITEM_PARAMS ) )
+ return false;
+
+ int nConverted(0);
+ bool bAllConverted( true );
+
+ sal_Int8 nType(0);
+ OUString aURL, aFilterName;
+ css::uno::Reference< css::lang::XComponent > xDrawing;
+ css::uno::Reference< css::graphic::XGraphic > xGraphic;
+
+ for ( const css::beans::PropertyValue& rProp : std::as_const(aSeq) )
+ {
+ if ( rProp.Name == SVXGALLERYITEM_TYPE )
+ {
+ bAllConverted &= ( rProp.Value >>= nType );
+ ++nConverted;
+ }
+ else if ( rProp.Name == SVXGALLERYITEM_URL )
+ {
+ bAllConverted &= ( rProp.Value >>= aURL );
+ ++nConverted;
+ }
+ else if ( rProp.Name == SVXGALLERYITEM_FILTER )
+ {
+ bAllConverted &= ( rProp.Value >>= aFilterName );
+ ++nConverted;
+ }
+ else if ( rProp.Name == SVXGALLERYITEM_DRAWING )
+ {
+ bAllConverted &= ( rProp.Value >>= xDrawing );
+ ++nConverted;
+ }
+ else if ( rProp.Name == SVXGALLERYITEM_GRAPHIC )
+ {
+ bAllConverted &= ( rProp.Value >>= xGraphic );
+ ++nConverted;
+ }
+ }
+
+ if ( !bAllConverted || nConverted != SVXGALLERYITEM_PARAMS )
+ return false;
+
+ m_nType = nType;
+ m_aURL = aURL;
+ m_xDrawing = xDrawing;
+ m_xGraphic = xGraphic;
+
+ return true;
+}
+
+bool SvxGalleryItem::operator==( const SfxPoolItem& rAttr ) const
+{
+ assert(SfxPoolItem::operator==(rAttr));
+
+ const SvxGalleryItem& rItem = static_cast<const SvxGalleryItem&>(rAttr);
+
+ return m_nType == rItem.m_nType &&
+ m_aURL == rItem.m_aURL &&
+ m_xDrawing == rItem.m_xDrawing &&
+ m_xGraphic == rItem.m_xGraphic;
+}
+
+SvxGalleryItem* SvxGalleryItem::Clone( SfxItemPool * ) const
+{
+ return new SvxGalleryItem( *this );
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/items/grfitem.cxx b/svx/source/items/grfitem.cxx
new file mode 100644
index 000000000..30c0977a0
--- /dev/null
+++ b/svx/source/items/grfitem.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 <svx/grfcrop.hxx>
+#include <editeng/itemtype.hxx>
+#include <com/sun/star/text/GraphicCrop.hpp>
+#include <tools/mapunit.hxx>
+#include <tools/UnitConversion.hxx>
+
+using namespace ::com::sun::star;
+
+SvxGrfCrop::SvxGrfCrop( TypedWhichId<SvxGrfCrop> nItemId )
+ : SfxPoolItem( nItemId ),
+ nLeft( 0 ), nRight( 0 ), nTop( 0 ), nBottom( 0 )
+{}
+
+SvxGrfCrop::SvxGrfCrop( sal_Int32 nL, sal_Int32 nR,
+ sal_Int32 nT, sal_Int32 nB, TypedWhichId<SvxGrfCrop> nItemId )
+ : SfxPoolItem( nItemId ),
+ nLeft( nL ), nRight( nR ), nTop( nT ), nBottom( nB )
+{}
+
+SvxGrfCrop::~SvxGrfCrop()
+{
+}
+
+bool SvxGrfCrop::operator==( const SfxPoolItem& rAttr ) const
+{
+ assert(SfxPoolItem::operator==(rAttr));
+
+ const SvxGrfCrop& rCrop = static_cast<const SvxGrfCrop&>(rAttr);
+ return nLeft == rCrop.GetLeft() &&
+ nRight == rCrop.GetRight() &&
+ nTop == rCrop.GetTop() &&
+ nBottom == rCrop.GetBottom();
+}
+
+
+bool SvxGrfCrop::QueryValue( uno::Any& rVal, sal_uInt8 nMemberId ) const
+{
+ bool bConvert = 0!=(nMemberId&CONVERT_TWIPS);
+ text::GraphicCrop aRet;
+ aRet.Left = nLeft;
+ aRet.Right = nRight;
+ aRet.Top = nTop;
+ aRet.Bottom = nBottom;
+
+ if( bConvert )
+ {
+ aRet.Right = convertTwipToMm100(aRet.Right );
+ aRet.Top = convertTwipToMm100(aRet.Top );
+ aRet.Left = convertTwipToMm100(aRet.Left );
+ aRet.Bottom = convertTwipToMm100(aRet.Bottom);
+ }
+
+
+ rVal <<= aRet;
+ return true;
+}
+
+bool SvxGrfCrop::PutValue( const uno::Any& rVal, sal_uInt8 nMemberId )
+{
+ bool bConvert = 0!=(nMemberId&CONVERT_TWIPS);
+ text::GraphicCrop aVal;
+
+ if(!(rVal >>= aVal))
+ return false;
+ if( bConvert )
+ {
+ aVal.Right = o3tl::toTwips(aVal.Right, o3tl::Length::mm100);
+ aVal.Top = o3tl::toTwips(aVal.Top, o3tl::Length::mm100);
+ aVal.Left = o3tl::toTwips(aVal.Left, o3tl::Length::mm100);
+ aVal.Bottom = o3tl::toTwips(aVal.Bottom, o3tl::Length::mm100);
+ }
+
+ nLeft = aVal.Left ;
+ nRight = aVal.Right ;
+ nTop = aVal.Top ;
+ nBottom = aVal.Bottom;
+ return true;
+}
+
+bool SvxGrfCrop::GetPresentation(
+ SfxItemPresentation ePres, MapUnit eCoreUnit, MapUnit /*ePresUnit*/,
+ OUString &rText, const IntlWrapper& rIntl ) const
+{
+ rText.clear();
+ switch( ePres )
+ {
+ case SfxItemPresentation::Nameless:
+ return true;
+ case SfxItemPresentation::Complete:
+ rText = "L: " + ::GetMetricText( GetLeft(), eCoreUnit, MapUnit::MapMM, &rIntl ) +
+ " R: " + ::GetMetricText( GetRight(), eCoreUnit, MapUnit::MapMM, &rIntl ) +
+ " T: " + ::GetMetricText( GetTop(), eCoreUnit, MapUnit::MapMM, &rIntl ) +
+ " B: " + ::GetMetricText( GetBottom(), eCoreUnit, MapUnit::MapMM, &rIntl );
+ return true;
+
+ default:
+ return false;
+ }
+}
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/items/hlnkitem.cxx b/svx/source/items/hlnkitem.cxx
new file mode 100644
index 000000000..4a8bc3290
--- /dev/null
+++ b/svx/source/items/hlnkitem.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 <svx/svxids.hrc>
+#include <sfx2/event.hxx>
+#include <svx/hlnkitem.hxx>
+
+
+SfxPoolItem* SvxHyperlinkItem::CreateDefault() { return new SvxHyperlinkItem(TypedWhichId<SvxHyperlinkItem>(0));}
+
+SvxHyperlinkItem::SvxHyperlinkItem( const SvxHyperlinkItem& rHyperlinkItem ):
+ SfxPoolItem(rHyperlinkItem)
+{
+ sName = rHyperlinkItem.sName;
+ sURL = rHyperlinkItem.sURL;
+ sTarget = rHyperlinkItem.sTarget;
+ eType = rHyperlinkItem.eType;
+ sIntName = rHyperlinkItem.sIntName;
+ nMacroEvents = rHyperlinkItem.nMacroEvents;
+
+ if( rHyperlinkItem.GetMacroTable() )
+ pMacroTable.reset( new SvxMacroTableDtor( *rHyperlinkItem.GetMacroTable() ) );
+
+};
+
+SvxHyperlinkItem::SvxHyperlinkItem( TypedWhichId<SvxHyperlinkItem> _nWhich, const OUString& rName, const OUString& rURL,
+ const OUString& rTarget, const OUString& rIntName, SvxLinkInsertMode eTyp,
+ HyperDialogEvent nEvents, SvxMacroTableDtor const *pMacroTbl ):
+ SfxPoolItem (_nWhich),
+ sName (rName),
+ sURL (rURL),
+ sTarget (rTarget),
+ eType (eTyp),
+ sIntName (rIntName),
+ nMacroEvents (nEvents)
+{
+ if (pMacroTbl)
+ pMacroTable.reset( new SvxMacroTableDtor ( *pMacroTbl ) );
+}
+
+SvxHyperlinkItem* SvxHyperlinkItem::Clone( SfxItemPool* ) const
+{
+ return new SvxHyperlinkItem( *this );
+}
+
+bool SvxHyperlinkItem::operator==( const SfxPoolItem& rAttr ) const
+{
+ assert(SfxPoolItem::operator==(rAttr));
+
+ const SvxHyperlinkItem& rItem = static_cast<const SvxHyperlinkItem&>(rAttr);
+
+ bool bRet = ( sName == rItem.sName &&
+ sURL == rItem.sURL &&
+ sTarget == rItem.sTarget &&
+ eType == rItem.eType &&
+ sIntName == rItem.sIntName &&
+ nMacroEvents == rItem.nMacroEvents);
+ if (!bRet)
+ return false;
+
+ const SvxMacroTableDtor* pOther = static_cast<const SvxHyperlinkItem&>(rAttr).pMacroTable.get();
+ if( !pMacroTable )
+ return ( !pOther || pOther->empty() );
+ if( !pOther )
+ return pMacroTable->empty();
+
+ const SvxMacroTableDtor& rOwn = *pMacroTable;
+ const SvxMacroTableDtor& rOther = *pOther;
+
+ return rOwn == rOther;
+}
+
+void SvxHyperlinkItem::SetMacro( HyperDialogEvent nEvent, const SvxMacro& rMacro )
+{
+ SvMacroItemId nSfxEvent = SvMacroItemId::NONE;
+ switch( nEvent )
+ {
+ case HyperDialogEvent::MouseOverObject:
+ nSfxEvent = SvMacroItemId::OnMouseOver;
+ break;
+ case HyperDialogEvent::MouseClickObject:
+ nSfxEvent = SvMacroItemId::OnClick;
+ break;
+ case HyperDialogEvent::MouseOutObject:
+ nSfxEvent = SvMacroItemId::OnMouseOut;
+ break;
+ default: break;
+ }
+
+ if( !pMacroTable )
+ pMacroTable.reset( new SvxMacroTableDtor );
+
+ pMacroTable->Insert( nSfxEvent, rMacro);
+}
+
+void SvxHyperlinkItem::SetMacroTable( const SvxMacroTableDtor& rTbl )
+{
+ pMacroTable.reset( new SvxMacroTableDtor ( rTbl ) );
+}
+
+bool SvxHyperlinkItem::QueryValue( css::uno::Any& rVal, sal_uInt8 nMemberId ) const
+{
+// sal_Bool bConvert = 0!=(nMemberId&CONVERT_TWIPS);
+ nMemberId &= ~CONVERT_TWIPS;
+ switch(nMemberId)
+ {
+ case MID_HLINK_NAME :
+ rVal <<= sIntName;
+ break;
+ case MID_HLINK_TEXT :
+ rVal <<= sName;
+ break;
+ case MID_HLINK_URL:
+ rVal <<= sURL;
+ break;
+ case MID_HLINK_TARGET:
+ rVal <<= sTarget;
+ break;
+ case MID_HLINK_TYPE:
+ rVal <<= static_cast<sal_Int32>(eType);
+ break;
+ default:
+ return false;
+ }
+
+ return true;
+}
+
+bool SvxHyperlinkItem::PutValue( const css::uno::Any& rVal, sal_uInt8 nMemberId )
+{
+ nMemberId &= ~CONVERT_TWIPS;
+ OUString aStr;
+ sal_Int32 nVal = 0;
+ switch(nMemberId)
+ {
+ case MID_HLINK_NAME :
+ if(!(rVal >>= aStr))
+ return false;
+ sIntName = aStr;
+ break;
+ case MID_HLINK_TEXT :
+ if(!(rVal >>= aStr))
+ return false;
+ sName = aStr;
+ break;
+ case MID_HLINK_URL:
+ if(!(rVal >>= aStr))
+ return false;
+ sURL = aStr;
+ break;
+ case MID_HLINK_TARGET:
+ if(!(rVal >>= aStr))
+ return false;
+ sTarget = aStr;
+ break;
+ case MID_HLINK_TYPE:
+ if(!(rVal >>= nVal))
+ return false;
+ eType = static_cast<SvxLinkInsertMode>(static_cast<sal_uInt16>(nVal));
+ break;
+ default:
+ return false;
+ }
+
+ return true;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/items/legacyitem.cxx b/svx/source/items/legacyitem.cxx
new file mode 100644
index 000000000..005f97d2d
--- /dev/null
+++ b/svx/source/items/legacyitem.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 <svx/legacyitem.hxx>
+#include <tools/stream.hxx>
+#include <svx/algitem.hxx>
+#include <svx/rotmodit.hxx>
+
+namespace legacy
+{
+ namespace SvxOrientation
+ {
+ sal_uInt16 GetVersion(sal_uInt16)
+ {
+ return 0;
+ }
+
+ void Create(SvxOrientationItem& rItem, SvStream& rStrm, sal_uInt16)
+ {
+ sal_uInt16 nVal(0);
+ rStrm.ReadUInt16( nVal );
+ rItem.SetValue(static_cast<::SvxCellOrientation>(nVal));
+ }
+
+ SvStream& Store(const SvxOrientationItem& rItem, SvStream& rStrm, sal_uInt16)
+ {
+ rStrm.WriteUInt16( static_cast<sal_uInt16>(rItem.GetValue()) );
+ return rStrm;
+ }
+ }
+
+ namespace SvxMargin
+ {
+ sal_uInt16 GetVersion(sal_uInt16)
+ {
+ return 0;
+ }
+
+ void Create(SvxMarginItem& rItem, SvStream& rStrm, sal_uInt16)
+ {
+ sal_Int16 nLeft(0);
+ sal_Int16 nTop(0);
+ sal_Int16 nRight(0);
+ sal_Int16 nBottom(0);
+
+ rStrm.ReadInt16( nLeft );
+ rStrm.ReadInt16( nTop );
+ rStrm.ReadInt16( nRight );
+ rStrm.ReadInt16( nBottom );
+
+ rItem.SetLeftMargin(nLeft);
+ rItem.SetTopMargin(nTop);
+ rItem.SetRightMargin(nRight);
+ rItem.SetBottomMargin(nBottom);
+ }
+
+ SvStream& Store(const SvxMarginItem& rItem, SvStream& rStrm, sal_uInt16)
+ {
+ rStrm.WriteInt16( rItem.GetLeftMargin() );
+ rStrm.WriteInt16( rItem.GetTopMargin() );
+ rStrm.WriteInt16( rItem.GetRightMargin() );
+ rStrm.WriteInt16( rItem.GetBottomMargin() );
+ return rStrm;
+ }
+ }
+
+ namespace SvxRotateMode
+ {
+ sal_uInt16 GetVersion(sal_uInt16)
+ {
+ return 0;
+ }
+
+ void Create(SvxRotateModeItem& rItem, SvStream& rStrm, sal_uInt16)
+ {
+ sal_uInt16 nVal(0);
+ rStrm.ReadUInt16( nVal );
+ rItem.SetValue(static_cast<::SvxRotateMode>(nVal));
+ }
+
+ SvStream& Store(const SvxRotateModeItem& rItem, SvStream& rStrm, sal_uInt16)
+ {
+ rStrm.WriteUInt16( static_cast<sal_uInt16>(rItem.GetValue()) );
+ return rStrm;
+ }
+ }
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/items/numfmtsh.cxx b/svx/source/items/numfmtsh.cxx
new file mode 100644
index 000000000..40811d846
--- /dev/null
+++ b/svx/source/items/numfmtsh.cxx
@@ -0,0 +1,1593 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * 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/color.hxx>
+
+#include <tools/debug.hxx>
+#include <i18nlangtag/mslangid.hxx>
+#include <o3tl/safeint.hxx>
+#include <svl/numformat.hxx>
+#include <svl/zforlist.hxx>
+#include <svl/zformat.hxx>
+#include <svl/currencytable.hxx>
+
+#include <svx/numfmtsh.hxx>
+#include <svx/flagsdef.hxx>
+#include <svx/tbcontrl.hxx>
+
+#include <limits>
+
+namespace
+{
+double GetDefaultValNum(const SvNumFormatType nType)
+{
+ switch (nType)
+ {
+ case SvNumFormatType::NUMBER:
+ return fSvxNumValConst[SvxNumValCategory::Standard];
+ case SvNumFormatType::CURRENCY:
+ return fSvxNumValConst[SvxNumValCategory::Currency];
+ case SvNumFormatType::PERCENT:
+ return fSvxNumValConst[SvxNumValCategory::Percent];
+ case SvNumFormatType::DATE:
+ case SvNumFormatType::DATETIME:
+ return fSvxNumValConst[SvxNumValCategory::Date];
+ case SvNumFormatType::TIME:
+ return fSvxNumValConst[SvxNumValCategory::Time];
+ case SvNumFormatType::SCIENTIFIC:
+ return fSvxNumValConst[SvxNumValCategory::Scientific];
+ case SvNumFormatType::FRACTION:
+ return fSvxNumValConst[SvxNumValCategory::Fraction];
+ case SvNumFormatType::LOGICAL:
+ return fSvxNumValConst[SvxNumValCategory::Boolean];
+ default:
+ break;
+ }
+ return fSvxNumValConst[SvxNumValCategory::NoValue];
+}
+}
+
+SvxNumberFormatShell* SvxNumberFormatShell::Create(SvNumberFormatter* pNumFormatter,
+ sal_uInt32 nFormatKey,
+ SvxNumberValueType eNumValType,
+ const OUString& rNumStr)
+{
+ return new SvxNumberFormatShell(pNumFormatter, nFormatKey, eNumValType, rNumStr);
+}
+
+SvxNumberFormatShell* SvxNumberFormatShell::Create(SvNumberFormatter* pNumFormatter,
+ sal_uInt32 nFormatKey,
+ SvxNumberValueType eNumValType, double nNumVal,
+ const OUString* pNumStr)
+{
+ return new SvxNumberFormatShell(pNumFormatter, nFormatKey, eNumValType, nNumVal, pNumStr);
+}
+
+SvxNumberFormatShell::SvxNumberFormatShell(SvNumberFormatter* pNumFormatter, sal_uInt32 nFormatKey,
+ SvxNumberValueType eNumValType, const OUString& rNumStr)
+ : pFormatter(pNumFormatter)
+ , pCurFmtTable(nullptr)
+ , eValType(eNumValType)
+ , bUndoAddList(true)
+ , nCurFormatKey(nFormatKey)
+ , nCurCategory(SvNumFormatType::ALL)
+ , eCurLanguage(LANGUAGE_NONE)
+ , pCurCurrencyEntry(nullptr)
+ , bBankingSymbol(false)
+ , nCurCurrencyEntryPos(sal_uInt16(SELPOS_NONE))
+ , bUseStarFormat(false)
+ , bIsDefaultValNum(true)
+{
+ nValNum = 0;
+
+ switch (eValType)
+ {
+ case SvxNumberValueType::String:
+ aValStr = rNumStr;
+ break;
+ case SvxNumberValueType::Number:
+ if (pFormatter)
+ {
+ nValNum = GetDefaultValNum(pFormatter->GetType(nCurFormatKey));
+ }
+ [[fallthrough]];
+ case SvxNumberValueType::Undefined:
+ default:
+ aValStr.clear();
+ }
+}
+
+SvxNumberFormatShell::SvxNumberFormatShell(SvNumberFormatter* pNumFormatter, sal_uInt32 nFormatKey,
+ SvxNumberValueType eNumValType, double nNumVal,
+ const OUString* pNumStr)
+ : pFormatter(pNumFormatter)
+ , pCurFmtTable(nullptr)
+ , eValType(eNumValType)
+ , bUndoAddList(true)
+ , nCurFormatKey(nFormatKey)
+ , nCurCategory(SvNumFormatType::ALL)
+ , eCurLanguage(LANGUAGE_NONE)
+ , pCurCurrencyEntry(nullptr)
+ , bBankingSymbol(false)
+ , nCurCurrencyEntryPos(sal_uInt16(SELPOS_NONE))
+ , bUseStarFormat(false)
+ , bIsDefaultValNum(false)
+{
+ // #50441# When used in Writer, the SvxNumberInfoItem contains the
+ // original string in addition to the value
+
+ if (pNumStr)
+ aValStr = *pNumStr;
+
+ switch (eValType)
+ {
+ case SvxNumberValueType::Number:
+ nValNum = nNumVal;
+ break;
+ case SvxNumberValueType::String:
+ case SvxNumberValueType::Undefined:
+ default:
+ nValNum = 0;
+ bIsDefaultValNum = true;
+ }
+}
+
+SvxNumberFormatShell::~SvxNumberFormatShell()
+{
+ /*
+ * At this point, depending on whether the added user-defined were
+ * validated (ValidateNewEntries()), the add list is removed from
+ * the number formatter again.
+ *
+ * Deleting formats from the formatter happens for Undo reasons
+ * only in the calling instance.
+ */
+
+ if (bUndoAddList)
+ {
+ // Added formats are invalid => remove them
+
+ for (const auto& rItem : aAddList)
+ pFormatter->DeleteEntry(rItem);
+ }
+}
+
+std::vector<sal_uInt32> const& SvxNumberFormatShell::GetUpdateData() const { return aDelList; }
+
+void SvxNumberFormatShell::CategoryChanged(sal_uInt16 nCatLbPos, short& rFmtSelPos,
+ std::vector<OUString>& rFmtEntries)
+{
+ SvNumFormatType nOldCategory = nCurCategory;
+ PosToCategory_Impl(nCatLbPos, nCurCategory);
+ pCurFmtTable = &(pFormatter->GetEntryTable(nCurCategory, nCurFormatKey, eCurLanguage));
+ // reinitialize currency if category newly entered
+ if (nCurCategory == SvNumFormatType::CURRENCY && nOldCategory != nCurCategory)
+ pCurCurrencyEntry = nullptr;
+ rFmtSelPos = FillEntryList_Impl(rFmtEntries);
+}
+
+void SvxNumberFormatShell::LanguageChanged(LanguageType eLangType, short& rFmtSelPos,
+ std::vector<OUString>& rFmtEntries)
+{
+ eCurLanguage = eLangType;
+ pCurFmtTable = &(pFormatter->ChangeCL(nCurCategory, nCurFormatKey, eCurLanguage));
+ rFmtSelPos = FillEntryList_Impl(rFmtEntries);
+}
+
+void SvxNumberFormatShell::FormatChanged(sal_uInt16 nFmtLbPos, OUString& rPreviewStr,
+ const Color*& rpFontColor)
+{
+ if (static_cast<size_t>(nFmtLbPos) >= aCurEntryList.size())
+ return;
+
+ nCurFormatKey = aCurEntryList[nFmtLbPos];
+
+ if (nCurFormatKey != NUMBERFORMAT_ENTRY_NOT_FOUND)
+ {
+ GetPreviewString_Impl(rPreviewStr, rpFontColor);
+ }
+ else if (nCurCategory == SvNumFormatType::CURRENCY)
+ {
+ if (static_cast<size_t>(nFmtLbPos) < aCurrencyFormatList.size())
+ {
+ MakePrevStringFromVal(aCurrencyFormatList[nFmtLbPos], rPreviewStr, rpFontColor,
+ nValNum);
+ }
+ }
+}
+
+bool SvxNumberFormatShell::AddFormat(OUString& rFormat, sal_Int32& rErrPos,
+ sal_uInt16& rCatLbSelPos, short& rFmtSelPos,
+ std::vector<OUString>& rFmtEntries)
+{
+ bool bInserted = false;
+ sal_uInt32 nAddKey = pFormatter->GetEntryKey(rFormat, eCurLanguage);
+
+ if (nAddKey != NUMBERFORMAT_ENTRY_NOT_FOUND) // exists already?
+ {
+ ::std::vector<sal_uInt32>::iterator nAt = GetRemoved_Impl(nAddKey);
+ if (nAt != aDelList.end())
+ {
+ aDelList.erase(nAt);
+ bInserted = true;
+ }
+ else
+ {
+ OSL_FAIL("duplicate format!");
+ }
+ }
+ else // new format
+ {
+ sal_Int32 nPos;
+ bInserted = pFormatter->PutEntry(rFormat, nPos, nCurCategory, nAddKey, eCurLanguage);
+ rErrPos = (nPos >= 0) ? nPos : -1;
+
+ if (bInserted)
+ {
+ // May be sorted under a different locale if LCID was parsed.
+ const SvNumberformat* pEntry = pFormatter->GetEntry(nAddKey);
+ if (pEntry)
+ {
+ LanguageType nLang = pEntry->GetLanguage();
+ if (eCurLanguage != nLang)
+ {
+ // Current language's list would not show entry, adapt.
+ eCurLanguage = nLang;
+ }
+ }
+ }
+ }
+
+ if (bInserted)
+ {
+ nCurFormatKey = nAddKey;
+ DBG_ASSERT(GetAdded_Impl(nCurFormatKey) == aAddList.end(), "duplicate format!");
+ aAddList.push_back(nCurFormatKey);
+
+ // get current table
+ pCurFmtTable = &(pFormatter->GetEntryTable(nCurCategory, nCurFormatKey, eCurLanguage));
+ nCurCategory = pFormatter->GetType(nAddKey);
+ CategoryToPos_Impl(nCurCategory, rCatLbSelPos);
+ rFmtSelPos = FillEntryList_Impl(rFmtEntries);
+ }
+ else if (rErrPos != 0) // syntax error
+ {
+ ;
+ }
+ else // insert twice not possible
+ {
+ OSL_FAIL("duplicate format!");
+ }
+
+ return bInserted;
+}
+
+void SvxNumberFormatShell::RemoveFormat(std::u16string_view rFormat, sal_uInt16& rCatLbSelPos,
+ short& rFmtSelPos, std::vector<OUString>& rFmtEntries)
+{
+ sal_uInt32 nDelKey = pFormatter->GetEntryKey(rFormat, eCurLanguage);
+
+ DBG_ASSERT(nDelKey != NUMBERFORMAT_ENTRY_NOT_FOUND, "entry not found!");
+ DBG_ASSERT(!IsRemoved_Impl(nDelKey), "entry already removed!");
+
+ if ((nDelKey == NUMBERFORMAT_ENTRY_NOT_FOUND) || IsRemoved_Impl(nDelKey))
+ return;
+
+ aDelList.push_back(nDelKey);
+
+ ::std::vector<sal_uInt32>::iterator nAt = GetAdded_Impl(nDelKey);
+ if (nAt != aAddList.end())
+ {
+ aAddList.erase(nAt);
+ }
+
+ nCurCategory = pFormatter->GetType(nDelKey);
+ pCurFmtTable = &(pFormatter->GetEntryTable(nCurCategory, nCurFormatKey, eCurLanguage));
+
+ nCurFormatKey = pFormatter->GetStandardFormat(nCurCategory, eCurLanguage);
+
+ CategoryToPos_Impl(nCurCategory, rCatLbSelPos);
+ rFmtSelPos = FillEntryList_Impl(rFmtEntries);
+}
+
+void SvxNumberFormatShell::MakeFormat(OUString& rFormat, bool bThousand, bool bNegRed,
+ sal_uInt16 nPrecision, sal_uInt16 nLeadingZeroes,
+ sal_uInt16 nCurrencyPos)
+{
+ if (aCurrencyFormatList.size() > static_cast<size_t>(nCurrencyPos))
+ {
+ sal_Int32 rErrPos = 0;
+ std::vector<OUString> aFmtEList;
+
+ sal_uInt32 nFound
+ = pFormatter->TestNewString(aCurrencyFormatList[nCurrencyPos], eCurLanguage);
+
+ if (nFound == NUMBERFORMAT_ENTRY_NOT_FOUND)
+ {
+ sal_uInt16 rCatLbSelPos = 0;
+ short rFmtSelPos = 0;
+ AddFormat(aCurrencyFormatList[nCurrencyPos], rErrPos, rCatLbSelPos, rFmtSelPos,
+ aFmtEList);
+ }
+
+ if (rErrPos == 0)
+ {
+ rFormat = pFormatter->GenerateFormat(nCurFormatKey, eCurLanguage, bThousand, bNegRed,
+ nPrecision, nLeadingZeroes);
+ }
+ }
+ else
+ {
+ rFormat = pFormatter->GenerateFormat(nCurFormatKey, eCurLanguage, bThousand, bNegRed,
+ nPrecision, nLeadingZeroes);
+ }
+}
+
+sal_uInt16 SvxNumberFormatShell::GetFormatIntegerDigits(std::u16string_view rFormat) const
+{
+ sal_uInt32 nFmtKey = pFormatter->GetEntryKey(rFormat, eCurLanguage);
+
+ return pFormatter->GetFormatIntegerDigits(nFmtKey);
+}
+
+void SvxNumberFormatShell::GetOptions(const OUString& rFormat, bool& rThousand, bool& rNegRed,
+ sal_uInt16& rPrecision, sal_uInt16& rLeadingZeroes,
+ sal_uInt16& rCatLbPos)
+{
+ sal_uInt32 nFmtKey = pFormatter->GetEntryKey(rFormat, eCurLanguage);
+
+ if (nFmtKey != NUMBERFORMAT_ENTRY_NOT_FOUND)
+ {
+ pFormatter->GetFormatSpecialInfo(nFmtKey, rThousand, rNegRed, rPrecision, rLeadingZeroes);
+
+ CategoryToPos_Impl(pFormatter->GetType(nFmtKey), rCatLbPos);
+ }
+ else
+ {
+ bool bTestBanking = false;
+ sal_uInt16 nPos = FindCurrencyTableEntry(rFormat, bTestBanking);
+
+ if (IsInTable(nPos, bTestBanking, rFormat)
+ && pFormatter->GetFormatSpecialInfo(rFormat, rThousand, rNegRed, rPrecision,
+ rLeadingZeroes, eCurLanguage)
+ == 0)
+ {
+ rCatLbPos = CAT_CURRENCY;
+ }
+ else
+ rCatLbPos = CAT_USERDEFINED;
+ }
+}
+
+void SvxNumberFormatShell::MakePreviewString(const OUString& rFormatStr, OUString& rPreviewStr,
+ const Color*& rpFontColor)
+{
+ rpFontColor = nullptr;
+
+ sal_uInt32 nExistingFormat = pFormatter->GetEntryKey(rFormatStr, eCurLanguage);
+ if (nExistingFormat == NUMBERFORMAT_ENTRY_NOT_FOUND)
+ {
+ // real preview - not implemented in NumberFormatter for text formats
+ pFormatter->GetPreviewString(rFormatStr, nValNum, rPreviewStr, &rpFontColor, eCurLanguage,
+ bUseStarFormat);
+ }
+ else
+ {
+ // format exists
+
+ // #50441# if a string was set in addition to the value, use it for text formats
+ bool bUseText = (eValType == SvxNumberValueType::String
+ || (!aValStr.isEmpty()
+ && (pFormatter->GetType(nExistingFormat) & SvNumFormatType::TEXT)));
+
+ if (bUseText)
+ {
+ pFormatter->GetOutputString(aValStr, nExistingFormat, rPreviewStr, &rpFontColor);
+ }
+ else
+ {
+ if (bIsDefaultValNum)
+ nValNum = GetDefaultValNum(pFormatter->GetType(nExistingFormat));
+ pFormatter->GetOutputString(nValNum, nExistingFormat, rPreviewStr, &rpFontColor,
+ bUseStarFormat);
+ }
+ }
+}
+
+bool SvxNumberFormatShell::IsUserDefined(const OUString& rFmtString)
+{
+ sal_uInt32 nFound = pFormatter->GetEntryKey(rFmtString, eCurLanguage);
+
+ bool bFlag = false;
+ if (nFound != NUMBERFORMAT_ENTRY_NOT_FOUND)
+ {
+ bFlag = pFormatter->IsUserDefined(rFmtString, eCurLanguage);
+
+ if (bFlag)
+ {
+ const SvNumberformat* pNumEntry = pFormatter->GetEntry(nFound);
+
+ if (pNumEntry != nullptr && pNumEntry->HasNewCurrency())
+ {
+ bool bTestBanking;
+ sal_uInt16 nPos = FindCurrencyTableEntry(rFmtString, bTestBanking);
+ bFlag = !IsInTable(nPos, bTestBanking, rFmtString);
+ }
+ }
+ }
+ return bFlag;
+}
+
+bool SvxNumberFormatShell::FindEntry(const OUString& rFmtString, sal_uInt32* pAt /* = NULL */)
+{
+ bool bRes = false;
+
+ sal_uInt32 nFound = NUMBERFORMAT_ENTRY_NOT_FOUND;
+ // There may be multiple builtin entries with the same format code, first
+ // try if the current key matches.
+ const SvNumberformat* pEntry = pFormatter->GetEntry(nCurFormatKey);
+ if (pEntry && pEntry->GetLanguage() == eCurLanguage && pEntry->GetFormatstring() == rFmtString)
+ nFound = nCurFormatKey;
+
+ if (nFound == NUMBERFORMAT_ENTRY_NOT_FOUND)
+ // Find the first matching format code.
+ nFound = pFormatter->TestNewString(rFmtString, eCurLanguage);
+
+ if (nFound == NUMBERFORMAT_ENTRY_NOT_FOUND)
+ {
+ bool bTestBanking = false;
+ sal_uInt16 nPos = FindCurrencyTableEntry(rFmtString, bTestBanking);
+
+ if (IsInTable(nPos, bTestBanking, rFmtString))
+ {
+ nFound = NUMBERFORMAT_ENTRY_NEW_CURRENCY;
+ bRes = true;
+ }
+ }
+ else
+ {
+ bRes = !IsRemoved_Impl(nFound);
+ }
+
+ if (pAt)
+ *pAt = nFound;
+
+ return bRes;
+}
+
+void SvxNumberFormatShell::GetInitSettings(sal_uInt16& nCatLbPos, LanguageType& rLangType,
+ sal_uInt16& nFmtLbSelPos,
+ std::vector<OUString>& rFmtEntries,
+ OUString& rPrevString, const Color*& rpPrevColor)
+{
+ // precondition: number formater found
+ DBG_ASSERT(pFormatter != nullptr, "Number formatter not found!");
+
+ short nSelPos = SELPOS_NONE;
+
+ // special treatment for undefined number format:
+ if ((eValType == SvxNumberValueType::Undefined) && (nCurFormatKey == 0))
+ PosToCategory_Impl(CAT_ALL, nCurCategory); // category = all
+ else
+ nCurCategory = SvNumFormatType::UNDEFINED; // category = undefined
+
+ pCurFmtTable = &(pFormatter->GetFirstEntryTable(nCurCategory, nCurFormatKey, eCurLanguage));
+
+ CategoryToPos_Impl(nCurCategory, nCatLbPos);
+ rLangType = eCurLanguage;
+
+ nSelPos = FillEntryList_Impl(rFmtEntries);
+
+ DBG_ASSERT(nSelPos != SELPOS_NONE, "Leere Formatliste!");
+
+ nFmtLbSelPos = (nSelPos != SELPOS_NONE) ? static_cast<sal_uInt16>(nSelPos) : 0;
+ GetPreviewString_Impl(rPrevString, rpPrevColor);
+}
+
+short SvxNumberFormatShell::FillEntryList_Impl(std::vector<OUString>& rList)
+{
+ /* Create a current list of format entries. The return value is
+ * the list position of the current format. If the list is empty
+ * or if there is no current format, SELPOS_NONE is delivered.
+ */
+ short nSelPos = SELPOS_NONE;
+
+ aCurEntryList.clear();
+
+ if (nCurCategory == SvNumFormatType::ALL)
+ {
+ FillEListWithStd_Impl(rList, SvNumFormatType::NUMBER, nSelPos);
+ nSelPos = FillEListWithUsD_Impl(rList, SvNumFormatType::NUMBER, nSelPos);
+
+ FillEListWithStd_Impl(rList, SvNumFormatType::PERCENT, nSelPos);
+ nSelPos = FillEListWithUsD_Impl(rList, SvNumFormatType::PERCENT, nSelPos);
+
+ FillEListWithStd_Impl(rList, SvNumFormatType::CURRENCY, nSelPos);
+ // No FillEListWithUsD_Impl() here, user defined currency formats
+ // were already added.
+
+ FillEListWithStd_Impl(rList, SvNumFormatType::DATE, nSelPos);
+ nSelPos = FillEListWithUsD_Impl(rList, SvNumFormatType::DATE, nSelPos);
+
+ FillEListWithStd_Impl(rList, SvNumFormatType::TIME, nSelPos);
+ nSelPos = FillEListWithUsD_Impl(rList, SvNumFormatType::TIME, nSelPos);
+
+ nSelPos = FillEListWithDateTime_Impl(rList, nSelPos, false);
+ nSelPos = FillEListWithUsD_Impl(rList, SvNumFormatType::DATETIME, nSelPos);
+
+ FillEListWithStd_Impl(rList, SvNumFormatType::SCIENTIFIC, nSelPos);
+ nSelPos = FillEListWithUsD_Impl(rList, SvNumFormatType::SCIENTIFIC, nSelPos);
+
+ FillEListWithStd_Impl(rList, SvNumFormatType::FRACTION, nSelPos);
+ nSelPos = FillEListWithUsD_Impl(rList, SvNumFormatType::FRACTION, nSelPos);
+
+ FillEListWithStd_Impl(rList, SvNumFormatType::LOGICAL, nSelPos);
+ nSelPos = FillEListWithUsD_Impl(rList, SvNumFormatType::LOGICAL, nSelPos);
+
+ FillEListWithStd_Impl(rList, SvNumFormatType::TEXT, nSelPos);
+ nSelPos = FillEListWithUsD_Impl(rList, SvNumFormatType::TEXT, nSelPos);
+ }
+ else
+ {
+ FillEListWithStd_Impl(rList, nCurCategory, nSelPos, true);
+ if (nCurCategory != SvNumFormatType::CURRENCY)
+ nSelPos = FillEListWithUsD_Impl(rList, nCurCategory, nSelPos);
+ if (nCurCategory == SvNumFormatType::DATE || nCurCategory == SvNumFormatType::TIME)
+ nSelPos = FillEListWithDateTime_Impl(rList, nSelPos, true);
+ }
+
+ return nSelPos;
+}
+
+void SvxNumberFormatShell::FillEListWithStd_Impl(std::vector<OUString>& rList,
+ SvNumFormatType eCategory, short& nSelPos,
+ bool bSuppressDuplicates)
+{
+ /* Create a current list of format entries. The return value is
+ * the list position of the current format. If the list is empty
+ * or if there is no current format, SELPOS_NONE is delivered.
+ */
+
+ assert(pCurFmtTable != nullptr);
+
+ aCurrencyFormatList.clear();
+
+ NfIndexTableOffset eOffsetStart;
+ NfIndexTableOffset eOffsetEnd;
+
+ switch (eCategory)
+ {
+ 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:
+ // Currency entries are generated and assembled, ignore
+ // bSuppressDuplicates.
+ nSelPos = FillEListWithCurrency_Impl(rList, nSelPos);
+ return;
+ 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;
+ // Fraction formats are internally generated by the number
+ // formatter and are not supposed to contain duplicates anyway.
+ nSelPos = FillEListWithFormats_Impl(rList, nSelPos, eOffsetStart, eOffsetEnd, false);
+ nSelPos
+ = FillEListWithFormats_Impl(rList, nSelPos, NF_FRACTION_3D, NF_FRACTION_100, false);
+ return;
+ case SvNumFormatType::LOGICAL:
+ eOffsetStart = NF_BOOLEAN;
+ eOffsetEnd = NF_BOOLEAN;
+ break;
+ case SvNumFormatType::TEXT:
+ eOffsetStart = NF_TEXT;
+ eOffsetEnd = NF_TEXT;
+ break;
+ default:
+ return;
+ }
+
+ nSelPos
+ = FillEListWithFormats_Impl(rList, nSelPos, eOffsetStart, eOffsetEnd, bSuppressDuplicates);
+}
+
+short SvxNumberFormatShell::FillEListWithFormats_Impl(std::vector<OUString>& rList, short nSelPos,
+ NfIndexTableOffset eOffsetStart,
+ NfIndexTableOffset eOffsetEnd,
+ bool bSuppressDuplicates)
+{
+ /* Create a current list of format entries. The return value is
+ * the list position of the current format. If the list is empty
+ * or if there is no current format, SELPOS_NONE is delivered.
+ */
+ for (tools::Long nIndex = eOffsetStart; nIndex <= eOffsetEnd; ++nIndex)
+ {
+ FillEListWithOneFormat_Impl(rList, nSelPos, bSuppressDuplicates,
+ static_cast<NfIndexTableOffset>(nIndex), false);
+ }
+
+ return nSelPos;
+}
+
+short SvxNumberFormatShell::FillEListWithDateTime_Impl(std::vector<OUString>& rList, short nSelPos,
+ bool bSuppressDuplicates)
+{
+ // Append a list of date+time formats.
+
+ // Add first, so a NF_DATETIME_SYSTEM_SHORT_HHMM may be suppressed in
+ // locales that do not use 2-digit years there and this here is the
+ // default.
+ FillEListWithOneFormat_Impl(rList, nSelPos, bSuppressDuplicates, NF_DATETIME_SYS_DDMMYYYY_HHMM,
+ true);
+
+ for (tools::Long nIndex = NF_DATETIME_START; nIndex <= NF_DATETIME_END; ++nIndex)
+ {
+ FillEListWithOneFormat_Impl(rList, nSelPos, bSuppressDuplicates,
+ static_cast<NfIndexTableOffset>(nIndex), true);
+ }
+
+ // Always add the internally generated ISO formats.
+ nSelPos = FillEListWithFormats_Impl(rList, nSelPos, NF_DATETIME_ISO_YYYYMMDD_HHMMSS,
+ NF_DATETIME_ISO_YYYYMMDDTHHMMSS000, false);
+
+ return nSelPos;
+}
+
+void SvxNumberFormatShell::FillEListWithOneFormat_Impl(std::vector<OUString>& rList, short& nSelPos,
+ bool bSuppressDuplicates,
+ NfIndexTableOffset nOffset,
+ bool bSuppressIsoDateTime)
+{
+ sal_uInt32 nNFEntry = pFormatter->GetFormatIndex(nOffset, eCurLanguage);
+
+ const SvNumberformat* pNumEntry = pFormatter->GetEntry(nNFEntry);
+ if (pNumEntry == nullptr)
+ return;
+
+ SvNumFormatType nMyCat = pNumEntry->GetMaskedType();
+ sal_uInt16 nMyType;
+ CategoryToPos_Impl(nMyCat, nMyType);
+ OUString aNewFormNInfo = pNumEntry->GetFormatstring();
+
+ if (nNFEntry == nCurFormatKey)
+ {
+ nSelPos = (!IsRemoved_Impl(nNFEntry)) ? aCurEntryList.size() : SELPOS_NONE;
+ }
+
+ // Ugly hack to suppress an ISO date+time format that is the default
+ // date+time format of the locale and identical to the internally generated
+ // one always to be added after/below.
+ const bool bSupIso
+ = bSuppressIsoDateTime && bSuppressDuplicates
+ && (aNewFormNInfo == "YYYY-MM-DD HH:MM:SS" || aNewFormNInfo == "YYYY-MM-DD\"T\"HH:MM:SS");
+
+ if (!bSupIso
+ && (!bSuppressDuplicates || IsEssentialFormat_Impl(nMyCat, nNFEntry)
+ || std::find(rList.begin(), rList.end(), aNewFormNInfo) == rList.end()))
+ {
+ rList.push_back(aNewFormNInfo);
+ aCurEntryList.push_back(nNFEntry);
+ }
+}
+
+bool SvxNumberFormatShell::IsEssentialFormat_Impl(SvNumFormatType eType, sal_uInt32 nKey)
+{
+ if (nKey == nCurFormatKey)
+ return true;
+
+ const NfIndexTableOffset nIndex = pFormatter->GetIndexTableOffset(nKey);
+ switch (nIndex)
+ {
+ // These are preferred or edit formats.
+ case NF_DATE_SYS_DDMMYYYY:
+ case NF_DATE_ISO_YYYYMMDD:
+ case NF_TIME_HH_MMSS:
+ case NF_TIME_MMSS00:
+ case NF_TIME_HH_MMSS00:
+ case NF_DATETIME_SYS_DDMMYYYY_HHMM:
+ case NF_DATETIME_SYS_DDMMYYYY_HHMMSS:
+ case NF_DATETIME_ISO_YYYYMMDD_HHMMSS:
+ case NF_DATETIME_ISO_YYYYMMDD_HHMMSS000:
+ case NF_DATETIME_ISO_YYYYMMDDTHHMMSS:
+ case NF_DATETIME_ISO_YYYYMMDDTHHMMSS000:
+ return true;
+ default:
+ break;
+ }
+
+ return nKey == pFormatter->GetStandardFormat(eType, eCurLanguage);
+}
+
+short SvxNumberFormatShell::FillEListWithCurrency_Impl(std::vector<OUString>& rList, short nSelPos)
+{
+ /* Create a current list of format entries. The return value is
+ * the list position of the current format. If the list is empty
+ * or if there is no current format, SELPOS_NONE is delivered.
+ */
+ DBG_ASSERT(pCurFmtTable != nullptr, "unknown NumberFormat");
+
+ const NfCurrencyEntry* pTmpCurrencyEntry;
+ bool bTmpBanking;
+ OUString rSymbol;
+
+ bool bFlag = pFormatter->GetNewCurrencySymbolString(nCurFormatKey, rSymbol, &pTmpCurrencyEntry,
+ &bTmpBanking);
+
+ if ((!bFlag && pCurCurrencyEntry == nullptr)
+ || (bFlag && pTmpCurrencyEntry == nullptr && rSymbol.isEmpty())
+ || (nCurCategory == SvNumFormatType::ALL))
+ {
+ if (nCurCategory == SvNumFormatType::ALL)
+ FillEListWithUserCurrencys(rList, nSelPos);
+ nSelPos = FillEListWithSysCurrencys(rList, nSelPos);
+ }
+ else
+ {
+ nSelPos = FillEListWithUserCurrencys(rList, nSelPos);
+ }
+
+ return nSelPos;
+}
+
+short SvxNumberFormatShell::FillEListWithSysCurrencys(std::vector<OUString>& rList, short nSelPos)
+{
+ /* Create a current list of format entries. The return value is
+ * the list position of the current format. If the list is empty
+ * or if there is no current format, SELPOS_NONE is delivered.
+ */
+ sal_uInt16 nMyType;
+
+ DBG_ASSERT(pCurFmtTable != nullptr, "unknown NumberFormat");
+
+ sal_uInt32 nNFEntry;
+ OUString aNewFormNInfo;
+
+ nCurCurrencyEntryPos = 0;
+
+ for (tools::Long nIndex = NF_CURRENCY_START; nIndex <= NF_CURRENCY_END; nIndex++)
+ {
+ nNFEntry
+ = pFormatter->GetFormatIndex(static_cast<NfIndexTableOffset>(nIndex), eCurLanguage);
+
+ if (nCurCategory == SvNumFormatType::ALL && nNFEntry != nCurFormatKey)
+ // Deprecated old currency entries, for ALL add only if used as
+ // current format key.
+ continue;
+
+ const SvNumberformat* pNumEntry = pFormatter->GetEntry(nNFEntry);
+
+ if (pNumEntry == nullptr)
+ continue;
+
+ SvNumFormatType nMyCat = pNumEntry->GetMaskedType();
+ CategoryToPos_Impl(nMyCat, nMyType);
+ aNewFormNInfo = pNumEntry->GetFormatstring();
+
+ if (nNFEntry == nCurFormatKey)
+ {
+ nSelPos = (!IsRemoved_Impl(nNFEntry)) ? aCurEntryList.size() : SELPOS_NONE;
+ }
+
+ rList.push_back(aNewFormNInfo);
+ aCurEntryList.push_back(nNFEntry);
+ }
+
+ if (nCurCategory != SvNumFormatType::ALL)
+ {
+ for (const auto& rEntry : *pCurFmtTable)
+ {
+ sal_uInt32 nKey = rEntry.first;
+ const SvNumberformat* pNumEntry = rEntry.second;
+
+ if (!IsRemoved_Impl(nKey))
+ {
+ bool bUserNewCurrency = false;
+ if (pNumEntry->HasNewCurrency())
+ {
+ const NfCurrencyEntry* pTmpCurrencyEntry;
+ bool bTmpBanking;
+ OUString rSymbol;
+
+ pFormatter->GetNewCurrencySymbolString(nKey, rSymbol, &pTmpCurrencyEntry,
+ &bTmpBanking);
+
+ bUserNewCurrency = (pTmpCurrencyEntry != nullptr);
+ }
+
+ if (!bUserNewCurrency && (pNumEntry->GetType() & SvNumFormatType::DEFINED))
+ {
+ SvNumFormatType nMyCat = pNumEntry->GetMaskedType();
+ CategoryToPos_Impl(nMyCat, nMyType);
+ aNewFormNInfo = pNumEntry->GetFormatstring();
+
+ if (nKey == nCurFormatKey)
+ nSelPos = aCurEntryList.size();
+ rList.push_back(aNewFormNInfo);
+ aCurEntryList.push_back(nKey);
+ }
+ }
+ }
+ }
+ return nSelPos;
+}
+
+short SvxNumberFormatShell::FillEListWithUserCurrencys(std::vector<OUString>& rList, short nSelPos)
+{
+ /* Create a current list of format entries. The return value is
+ * the list position of the current format. If the list is empty
+ * or if there is no current format, SELPOS_NONE is delivered.
+ */
+ sal_uInt16 nMyType;
+
+ DBG_ASSERT(pCurFmtTable != nullptr, "unknown NumberFormat");
+
+ OUString aNewFormNInfo;
+
+ const NfCurrencyEntry* pTmpCurrencyEntry;
+ bool bTmpBanking, bAdaptSelPos;
+ OUString rSymbol;
+ OUString rBankSymbol;
+
+ std::vector<OUString> aList;
+ std::vector<sal_uInt32> aKeyList;
+
+ pFormatter->GetNewCurrencySymbolString(nCurFormatKey, rSymbol, &pTmpCurrencyEntry,
+ &bTmpBanking);
+
+ OUString rShortSymbol;
+
+ if (pCurCurrencyEntry == nullptr)
+ {
+ // #110398# If no currency format was previously selected (we're not
+ // about to add another currency), try to select the initial currency
+ // format (nCurFormatKey) that was set in FormatChanged() after
+ // matching the format string entered in the dialog.
+ bAdaptSelPos = true;
+ pCurCurrencyEntry = const_cast<NfCurrencyEntry*>(pTmpCurrencyEntry);
+ bBankingSymbol = bTmpBanking;
+ nCurCurrencyEntryPos = FindCurrencyFormat(pTmpCurrencyEntry, bTmpBanking);
+ }
+ else
+ {
+ if (pTmpCurrencyEntry == pCurCurrencyEntry)
+ bAdaptSelPos = true;
+ else
+ {
+ bAdaptSelPos = false;
+ pTmpCurrencyEntry = pCurCurrencyEntry;
+ }
+ bTmpBanking = bBankingSymbol;
+ }
+
+ if (pTmpCurrencyEntry != nullptr)
+ {
+ rSymbol = pTmpCurrencyEntry->BuildSymbolString(false);
+ rBankSymbol = pTmpCurrencyEntry->BuildSymbolString(true);
+ rShortSymbol = pTmpCurrencyEntry->BuildSymbolString(bTmpBanking, true);
+ }
+
+ for (const auto& rEntry : *pCurFmtTable)
+ {
+ sal_uInt32 nKey = rEntry.first;
+ const SvNumberformat* pNumEntry = rEntry.second;
+
+ if (!IsRemoved_Impl(nKey))
+ {
+ if (pNumEntry->GetType() & SvNumFormatType::DEFINED || pNumEntry->IsAdditionalBuiltin())
+ {
+ SvNumFormatType nMyCat = pNumEntry->GetMaskedType();
+ CategoryToPos_Impl(nMyCat, nMyType);
+ aNewFormNInfo = pNumEntry->GetFormatstring();
+
+ bool bInsFlag = false;
+ if (pNumEntry->HasNewCurrency())
+ {
+ bInsFlag = true; // merge locale formats into currency selection
+ }
+ else if ((!bTmpBanking && aNewFormNInfo.indexOf(rSymbol) >= 0)
+ || (bTmpBanking && aNewFormNInfo.indexOf(rBankSymbol) >= 0))
+ {
+ bInsFlag = true;
+ }
+ else if (aNewFormNInfo.indexOf(rShortSymbol) >= 0)
+ {
+ OUString rTstSymbol;
+ const NfCurrencyEntry* pTstCurrencyEntry;
+ bool bTstBanking;
+
+ pFormatter->GetNewCurrencySymbolString(nKey, rTstSymbol, &pTstCurrencyEntry,
+ &bTstBanking);
+
+ if (pTmpCurrencyEntry == pTstCurrencyEntry && bTstBanking == bTmpBanking)
+ {
+ bInsFlag = true;
+ }
+ }
+
+ if (bInsFlag)
+ {
+ aList.push_back(aNewFormNInfo);
+ aKeyList.push_back(nKey);
+ }
+ }
+ }
+ }
+
+ NfWSStringsDtor aWSStringsDtor;
+ sal_uInt16 nDefault;
+ if (pTmpCurrencyEntry && nCurCategory != SvNumFormatType::ALL)
+ {
+ nDefault
+ = pFormatter->GetCurrencyFormatStrings(aWSStringsDtor, *pTmpCurrencyEntry, bTmpBanking);
+ if (!bTmpBanking)
+ pFormatter->GetCurrencyFormatStrings(aWSStringsDtor, *pTmpCurrencyEntry, true);
+ }
+ else
+ nDefault = 0;
+ if (!bTmpBanking && nCurCategory != SvNumFormatType::ALL)
+ {
+ // append formats for all currencies defined in the current I18N locale
+ const NfCurrencyTable& rCurrencyTable = SvNumberFormatter::GetTheCurrencyTable();
+ sal_uInt16 nCurrCount = rCurrencyTable.size();
+ LanguageType eLang = MsLangId::getRealLanguage(eCurLanguage);
+ for (sal_uInt16 i = 0; i < nCurrCount; ++i)
+ {
+ const NfCurrencyEntry* pCurr = &rCurrencyTable[i];
+ if (pCurr->GetLanguage() == eLang && pTmpCurrencyEntry != pCurr)
+ {
+ pFormatter->GetCurrencyFormatStrings(aWSStringsDtor, *pCurr, false);
+ pFormatter->GetCurrencyFormatStrings(aWSStringsDtor, *pCurr, true);
+ }
+ }
+ }
+
+ size_t nOldListCount = rList.size();
+ for (size_t i = 0, nPos = nOldListCount; i < aWSStringsDtor.size(); ++i)
+ {
+ bool bFlag = true;
+ OUString aInsStr(aWSStringsDtor[i]);
+ size_t j;
+ for (j = 0; j < aList.size(); ++j)
+ {
+ if (aList[j] == aInsStr)
+ {
+ bFlag = false;
+ break;
+ }
+ }
+ if (bFlag)
+ {
+ rList.push_back(aInsStr);
+ aCurEntryList.insert(aCurEntryList.begin() + (nPos++), NUMBERFORMAT_ENTRY_NOT_FOUND);
+ }
+ else
+ {
+ rList.push_back(aList[j]);
+ aList.erase(aList.begin() + j);
+ aCurEntryList.insert(aCurEntryList.begin() + (nPos++), aKeyList[j]);
+ aKeyList.erase(aKeyList.begin() + j);
+ }
+ }
+
+ for (size_t i = 0; i < aKeyList.size(); ++i)
+ {
+ if (aKeyList[i] != NUMBERFORMAT_ENTRY_NOT_FOUND)
+ {
+ rList.push_back(aList[i]);
+ aCurEntryList.push_back(aKeyList[i]);
+ }
+ }
+
+ for (size_t i = nOldListCount; i < rList.size(); ++i)
+ {
+ aCurrencyFormatList.push_back(rList[i]);
+
+ if (nSelPos == SELPOS_NONE && bAdaptSelPos && aCurEntryList[i] == nCurFormatKey)
+ nSelPos = i;
+ }
+
+ if (nSelPos == SELPOS_NONE && nCurCategory != SvNumFormatType::ALL)
+ nSelPos = nDefault;
+
+ return nSelPos;
+}
+
+short SvxNumberFormatShell::FillEListWithUsD_Impl(std::vector<OUString>& rList,
+ SvNumFormatType eCategory, short nSelPos)
+{
+ /* Create a current list of format entries. The return value is
+ * the list position of the current format. If the list is empty
+ * or if there is no current format, SELPOS_NONE is delivered.
+ */
+
+ assert(pCurFmtTable != nullptr);
+
+ OUString aNewFormNInfo;
+
+ const bool bCatDefined = (eCategory == SvNumFormatType::DEFINED);
+ const bool bCategoryMatch = (eCategory != SvNumFormatType::ALL && !bCatDefined);
+
+ for (const auto& rEntry : *pCurFmtTable)
+ {
+ const SvNumberformat* pNumEntry = rEntry.second;
+
+ if (bCategoryMatch && (pNumEntry->GetMaskedType() & eCategory) != eCategory)
+ continue; // for; type does not match category if not ALL
+
+ const bool bUserDefined = bool(pNumEntry->GetType() & SvNumFormatType::DEFINED);
+ if (!bUserDefined && bCatDefined)
+ continue; // for; not user defined in DEFINED category
+
+ if (!(bUserDefined || (!bCatDefined && pNumEntry->IsAdditionalBuiltin())))
+ continue; // for; does not match criteria at all
+
+ const sal_uInt32 nKey = rEntry.first;
+ if (!IsRemoved_Impl(nKey))
+ {
+ aNewFormNInfo = pNumEntry->GetFormatstring();
+
+ bool bAdd = true;
+ if (pNumEntry->HasNewCurrency())
+ {
+ bool bTestBanking;
+ sal_uInt16 nPos = FindCurrencyTableEntry(aNewFormNInfo, bTestBanking);
+ bAdd = !IsInTable(nPos, bTestBanking, aNewFormNInfo);
+ }
+ if (bAdd)
+ {
+ if (nKey == nCurFormatKey)
+ nSelPos = aCurEntryList.size();
+ rList.push_back(aNewFormNInfo);
+ aCurEntryList.push_back(nKey);
+ }
+ }
+ }
+ return nSelPos;
+}
+
+void SvxNumberFormatShell::GetPreviewString_Impl(OUString& rString, const Color*& rpColor)
+{
+ rpColor = nullptr;
+
+ // #50441# if a string was set in addition to the value, use it for text formats
+ bool bUseText
+ = (eValType == SvxNumberValueType::String
+ || (!aValStr.isEmpty() && (pFormatter->GetType(nCurFormatKey) & SvNumFormatType::TEXT)));
+
+ if (bUseText)
+ {
+ pFormatter->GetOutputString(aValStr, nCurFormatKey, rString, &rpColor);
+ }
+ else
+ {
+ pFormatter->GetOutputString(nValNum, nCurFormatKey, rString, &rpColor, bUseStarFormat);
+ }
+}
+
+::std::vector<sal_uInt32>::iterator SvxNumberFormatShell::GetRemoved_Impl(size_t nKey)
+{
+ return ::std::find(aDelList.begin(), aDelList.end(), nKey);
+}
+
+bool SvxNumberFormatShell::IsRemoved_Impl(size_t nKey)
+{
+ return GetRemoved_Impl(nKey) != aDelList.end();
+}
+
+::std::vector<sal_uInt32>::iterator SvxNumberFormatShell::GetAdded_Impl(size_t nKey)
+{
+ return ::std::find(aAddList.begin(), aAddList.end(), nKey);
+}
+
+// Conversion routines:
+void SvxNumberFormatShell::PosToCategory_Impl(sal_uInt16 nPos, SvNumFormatType& rCategory)
+{
+ // map category css::form positions (->resource)
+ switch (nPos)
+ {
+ case CAT_USERDEFINED:
+ rCategory = SvNumFormatType::DEFINED;
+ break;
+ case CAT_NUMBER:
+ rCategory = SvNumFormatType::NUMBER;
+ break;
+ case CAT_PERCENT:
+ rCategory = SvNumFormatType::PERCENT;
+ break;
+ case CAT_CURRENCY:
+ rCategory = SvNumFormatType::CURRENCY;
+ break;
+ case CAT_DATE:
+ rCategory = SvNumFormatType::DATE;
+ break;
+ case CAT_TIME:
+ rCategory = SvNumFormatType::TIME;
+ break;
+ case CAT_SCIENTIFIC:
+ rCategory = SvNumFormatType::SCIENTIFIC;
+ break;
+ case CAT_FRACTION:
+ rCategory = SvNumFormatType::FRACTION;
+ break;
+ case CAT_BOOLEAN:
+ rCategory = SvNumFormatType::LOGICAL;
+ break;
+ case CAT_TEXT:
+ rCategory = SvNumFormatType::TEXT;
+ break;
+ case CAT_ALL:
+ default:
+ rCategory = SvNumFormatType::ALL;
+ break;
+ }
+}
+
+void SvxNumberFormatShell::CategoryToPos_Impl(SvNumFormatType nCategory, sal_uInt16& rPos)
+{
+ // map category to css::form positions (->resource)
+ switch (nCategory)
+ {
+ case SvNumFormatType::DEFINED:
+ rPos = CAT_USERDEFINED;
+ break;
+ case SvNumFormatType::NUMBER:
+ rPos = CAT_NUMBER;
+ break;
+ case SvNumFormatType::PERCENT:
+ rPos = CAT_PERCENT;
+ break;
+ case SvNumFormatType::CURRENCY:
+ rPos = CAT_CURRENCY;
+ break;
+ case SvNumFormatType::DATETIME:
+ case SvNumFormatType::DATE:
+ rPos = CAT_DATE;
+ break;
+ case SvNumFormatType::TIME:
+ rPos = CAT_TIME;
+ break;
+ case SvNumFormatType::SCIENTIFIC:
+ rPos = CAT_SCIENTIFIC;
+ break;
+ case SvNumFormatType::FRACTION:
+ rPos = CAT_FRACTION;
+ break;
+ case SvNumFormatType::LOGICAL:
+ rPos = CAT_BOOLEAN;
+ break;
+ case SvNumFormatType::TEXT:
+ rPos = CAT_TEXT;
+ break;
+ case SvNumFormatType::ALL:
+ default:
+ rPos = CAT_ALL;
+ }
+}
+
+/*
+ * Function: Formats the number nValue dependent on rFormatStr
+ * and stores the result in rPreviewStr.
+ * Input: FormatString, color, number to format
+ * Output: Output string rPreviewStr
+ */
+void SvxNumberFormatShell::MakePrevStringFromVal(const OUString& rFormatStr, OUString& rPreviewStr,
+ const Color*& rpFontColor, double nValue)
+{
+ rpFontColor = nullptr;
+ pFormatter->GetPreviewString(rFormatStr, nValue, rPreviewStr, &rpFontColor, eCurLanguage);
+}
+
+/*
+ * Function: Returns the comment for a given entry.
+ * Input: Number of the entry
+ * Output: Comment string
+ */
+void SvxNumberFormatShell::SetComment4Entry(short nEntry, const OUString& aEntStr)
+{
+ SvNumberformat* pNumEntry;
+ if (nEntry < 0)
+ return;
+ sal_uInt32 nMyNfEntry = aCurEntryList[nEntry];
+ pNumEntry = const_cast<SvNumberformat*>(pFormatter->GetEntry(nMyNfEntry));
+ if (pNumEntry != nullptr)
+ pNumEntry->SetComment(aEntStr);
+}
+
+/*
+ * Function: Returns the comment for a given entry.
+ * Input: Number of the entry
+ * Output: Comment string
+ */
+OUString SvxNumberFormatShell::GetComment4Entry(short nEntry)
+{
+ if (nEntry < 0)
+ return OUString();
+
+ if (o3tl::make_unsigned(nEntry) < aCurEntryList.size())
+ {
+ sal_uInt32 nMyNfEntry = aCurEntryList[nEntry];
+ const SvNumberformat* pNumEntry = pFormatter->GetEntry(nMyNfEntry);
+ if (pNumEntry != nullptr)
+ return pNumEntry->GetComment();
+ }
+
+ return OUString();
+}
+
+/*
+ * Function: Returns the category number for a given entry.
+ * Input: Number of the entry
+ * Output: Category number
+ */
+short SvxNumberFormatShell::GetCategory4Entry(short nEntry) const
+{
+ if (nEntry < 0)
+ return 0;
+ if (o3tl::make_unsigned(nEntry) < aCurEntryList.size())
+ {
+ sal_uInt32 nMyNfEntry = aCurEntryList[nEntry];
+
+ if (nMyNfEntry != NUMBERFORMAT_ENTRY_NOT_FOUND)
+ {
+ const SvNumberformat* pNumEntry = pFormatter->GetEntry(nMyNfEntry);
+ if (pNumEntry != nullptr)
+ {
+ SvNumFormatType nMyCat = pNumEntry->GetMaskedType();
+ sal_uInt16 nMyType;
+ CategoryToPos_Impl(nMyCat, nMyType);
+
+ return static_cast<short>(nMyType);
+ }
+ return 0;
+ }
+ else if (!aCurrencyFormatList.empty())
+ {
+ return CAT_CURRENCY;
+ }
+ }
+ return 0;
+}
+
+/*
+ * Function: Returns the information about whether an entry is user-specific.
+ * Input: Number of the entry
+ * Output: User-specific?
+ */
+bool SvxNumberFormatShell::GetUserDefined4Entry(short nEntry)
+{
+ if (nEntry < 0)
+ return false;
+ if (o3tl::make_unsigned(nEntry) < aCurEntryList.size())
+ {
+ sal_uInt32 nMyNfEntry = aCurEntryList[nEntry];
+ const SvNumberformat* pNumEntry = pFormatter->GetEntry(nMyNfEntry);
+
+ if (pNumEntry != nullptr)
+ {
+ if (pNumEntry->GetType() & SvNumFormatType::DEFINED)
+ {
+ return true;
+ }
+ }
+ }
+ return false;
+}
+
+/*
+ * Function: Returns the format string for a given entry.
+ * Input: Number of the entry
+ * Output: Format string
+ */
+OUString SvxNumberFormatShell::GetFormat4Entry(short nEntry)
+{
+ if (nEntry < 0)
+ return OUString();
+
+ if (!aCurrencyFormatList.empty())
+ {
+ if (aCurrencyFormatList.size() > o3tl::make_unsigned(nEntry))
+ return aCurrencyFormatList[nEntry];
+ }
+ else
+ {
+ sal_uInt32 nMyNfEntry = aCurEntryList[nEntry];
+ const SvNumberformat* pNumEntry = pFormatter->GetEntry(nMyNfEntry);
+
+ if (pNumEntry != nullptr)
+ return pNumEntry->GetFormatstring();
+ }
+ return OUString();
+}
+
+/*
+ * Function: Returns the list number for a given format index.
+ * Input: Number of the entry
+ * Output: Category number
+ */
+short SvxNumberFormatShell::GetListPos4Entry(sal_uInt32 nIdx, std::u16string_view rFmtString)
+{
+ short nSelP = SELPOS_NONE;
+ if (nIdx != NUMBERFORMAT_ENTRY_NEW_CURRENCY)
+ {
+ // Check list size against return type limit.
+ if (aCurEntryList.size() <= o3tl::make_unsigned(::std::numeric_limits<short>::max()))
+ {
+ for (size_t i = 0; i < aCurEntryList.size(); ++i)
+ {
+ if (aCurEntryList[i] == nIdx)
+ {
+ nSelP = i;
+ break;
+ }
+ }
+ }
+ else
+ {
+ OSL_FAIL("svx::SvxNumberFormatShell::GetListPos4Entry(), list got too large!");
+ }
+ }
+ else
+ {
+ // A second list holds the generated currency formats.
+ for (size_t i = 0; i < aCurrencyFormatList.size(); ++i)
+ {
+ if (rFmtString == aCurrencyFormatList[i])
+ {
+ nSelP = static_cast<short>(i);
+ break;
+ }
+ }
+ }
+ return nSelP;
+}
+
+OUString SvxNumberFormatShell::GetStandardName() const
+{
+ return pFormatter->GetStandardName(eCurLanguage);
+}
+
+void SvxNumberFormatShell::GetCurrencySymbols(std::vector<OUString>& rList, sal_uInt16* pPos)
+{
+ const NfCurrencyEntry* pTmpCurrencyEntry = SvNumberFormatter::MatchSystemCurrency();
+
+ bool bFlag = (pTmpCurrencyEntry == nullptr);
+
+ SvxCurrencyToolBoxControl::GetCurrencySymbols(rList, bFlag, aCurCurrencyList);
+
+ if (pPos == nullptr)
+ return;
+
+ const NfCurrencyTable& rCurrencyTable = SvNumberFormatter::GetTheCurrencyTable();
+ sal_uInt16 nTableCount = rCurrencyTable.size();
+
+ *pPos = 0;
+ size_t nCount = aCurCurrencyList.size();
+
+ if (bFlag)
+ {
+ *pPos = 1;
+ nCurCurrencyEntryPos = 1;
+ }
+ else
+ {
+ for (size_t i = 1; i < nCount; i++)
+ {
+ const sal_uInt16 j = aCurCurrencyList[i];
+ if (j != sal_uInt16(-1) && j < nTableCount && pTmpCurrencyEntry == &rCurrencyTable[j])
+ {
+ *pPos = static_cast<sal_uInt16>(i);
+ nCurCurrencyEntryPos = static_cast<sal_uInt16>(i);
+ break;
+ }
+ }
+ }
+}
+
+void SvxNumberFormatShell::SetCurrencySymbol(sal_uInt32 nPos)
+{
+ const NfCurrencyTable& rCurrencyTable = SvNumberFormatter::GetTheCurrencyTable();
+ sal_uInt16 nCount = rCurrencyTable.size();
+
+ bBankingSymbol = (nPos >= nCount);
+
+ if (nPos >= aCurCurrencyList.size())
+ return;
+
+ sal_uInt16 nCurrencyPos = aCurCurrencyList[nPos];
+ if (nCurrencyPos != sal_uInt16(-1))
+ {
+ pCurCurrencyEntry = const_cast<NfCurrencyEntry*>(&rCurrencyTable[nCurrencyPos]);
+ nCurCurrencyEntryPos = nPos;
+ }
+ else
+ {
+ pCurCurrencyEntry = nullptr;
+ nCurCurrencyEntryPos = 0;
+ nCurFormatKey = pFormatter->GetFormatIndex(NF_CURRENCY_1000DEC2_RED, eCurLanguage);
+ }
+}
+
+void SvxNumberFormatShell::SetCurCurrencyEntry(NfCurrencyEntry* pCEntry)
+{
+ pCurCurrencyEntry = pCEntry;
+}
+
+bool SvxNumberFormatShell::IsTmpCurrencyFormat(const OUString& rFmtString)
+{
+ sal_uInt32 nFound;
+ FindEntry(rFmtString, &nFound);
+ return nFound == NUMBERFORMAT_ENTRY_NEW_CURRENCY;
+}
+
+sal_uInt16 SvxNumberFormatShell::FindCurrencyFormat(const OUString& rFmtString)
+{
+ const NfCurrencyTable& rCurrencyTable = SvNumberFormatter::GetTheCurrencyTable();
+ sal_uInt16 nCount = rCurrencyTable.size();
+
+ bool bTestBanking = false;
+
+ sal_uInt16 nPos = FindCurrencyTableEntry(rFmtString, bTestBanking);
+
+ if (nPos != sal_uInt16(-1))
+ {
+ sal_uInt16 nStart = 0;
+ if (bTestBanking && aCurCurrencyList.size() > nPos)
+ {
+ nStart = nCount;
+ }
+ for (size_t j = nStart; j < aCurCurrencyList.size(); j++)
+ {
+ if (aCurCurrencyList[j] == nPos)
+ return j;
+ }
+ }
+ return sal_uInt16(-1);
+}
+
+sal_uInt16 SvxNumberFormatShell::FindCurrencyTableEntry(const OUString& rFmtString,
+ bool& bTestBanking)
+{
+ sal_uInt16 nPos = sal_uInt16(-1);
+
+ const NfCurrencyTable& rCurrencyTable = SvNumberFormatter::GetTheCurrencyTable();
+ sal_uInt16 nCount = rCurrencyTable.size();
+
+ const SvNumberformat* pFormat;
+ OUString aSymbol, aExtension;
+ sal_uInt32 nFound = pFormatter->TestNewString(rFmtString, eCurLanguage);
+ if (nFound != NUMBERFORMAT_ENTRY_NOT_FOUND
+ && ((pFormat = pFormatter->GetEntry(nFound)) != nullptr)
+ && pFormat->GetNewCurrencySymbol(aSymbol, aExtension))
+ {
+ // eventually match with format locale
+ const NfCurrencyEntry* pTmpCurrencyEntry = SvNumberFormatter::GetCurrencyEntry(
+ bTestBanking, aSymbol, aExtension, pFormat->GetLanguage());
+ if (pTmpCurrencyEntry)
+ {
+ for (sal_uInt16 i = 0; i < nCount; i++)
+ {
+ if (pTmpCurrencyEntry == &rCurrencyTable[i])
+ {
+ nPos = i;
+ break;
+ }
+ }
+ }
+ }
+ else
+ {
+ // search symbol string only
+ for (sal_uInt16 i = 0; i < nCount; i++)
+ {
+ const NfCurrencyEntry* pTmpCurrencyEntry = &rCurrencyTable[i];
+ OUString _aSymbol = pTmpCurrencyEntry->BuildSymbolString(false);
+ OUString aBankSymbol = pTmpCurrencyEntry->BuildSymbolString(true);
+
+ if (rFmtString.indexOf(_aSymbol) != -1)
+ {
+ bTestBanking = false;
+ nPos = i;
+ break;
+ }
+ else if (rFmtString.indexOf(aBankSymbol) != -1)
+ {
+ bTestBanking = true;
+ nPos = i;
+ break;
+ }
+ }
+ }
+
+ return nPos;
+}
+
+sal_uInt16 SvxNumberFormatShell::FindCurrencyFormat(const NfCurrencyEntry* pTmpCurrencyEntry,
+ bool bTmpBanking)
+{
+ const NfCurrencyTable& rCurrencyTable = SvNumberFormatter::GetTheCurrencyTable();
+ sal_uInt16 nCount = rCurrencyTable.size();
+
+ sal_uInt16 nPos = 0;
+ for (sal_uInt16 i = 0; i < nCount; i++)
+ {
+ if (pTmpCurrencyEntry == &rCurrencyTable[i])
+ {
+ nPos = i;
+ break;
+ }
+ }
+
+ sal_uInt16 nStart = 0;
+ if (bTmpBanking && aCurCurrencyList.size() > nPos)
+ {
+ nStart = nCount;
+ }
+ for (size_t j = nStart; j < aCurCurrencyList.size(); j++)
+ {
+ if (aCurCurrencyList[j] == nPos)
+ return j;
+ }
+ return sal_uInt16(-1);
+}
+
+bool SvxNumberFormatShell::IsInTable(sal_uInt16 const nPos, bool const bTmpBanking,
+ std::u16string_view rFmtString) const
+{
+ bool bFlag = false;
+
+ if (nPos != sal_uInt16(-1))
+ {
+ const NfCurrencyTable& rCurrencyTable = SvNumberFormatter::GetTheCurrencyTable();
+
+ if (nPos < rCurrencyTable.size())
+ {
+ NfWSStringsDtor aWSStringsDtor;
+ pFormatter->GetCurrencyFormatStrings(aWSStringsDtor, rCurrencyTable[nPos], bTmpBanking);
+
+ for (const OUString& s : aWSStringsDtor)
+ {
+ if (s == rFmtString)
+ {
+ bFlag = true;
+ break;
+ }
+ }
+ }
+ }
+
+ return bFlag;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/items/numinf.cxx b/svx/source/items/numinf.cxx
new file mode 100644
index 000000000..d4bb1e915
--- /dev/null
+++ b/svx/source/items/numinf.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 <svx/numinf.hxx>
+
+
+SvxNumberInfoItem::SvxNumberInfoItem( const TypedWhichId<SvxNumberInfoItem> nId ) :
+ SfxPoolItem ( nId ),
+ pFormatter ( nullptr ),
+ eValueType ( SvxNumberValueType::Undefined ),
+ aStringVal ( "" ),
+ nDoubleVal ( 0 )
+{
+}
+
+
+SvxNumberInfoItem::SvxNumberInfoItem( SvNumberFormatter* pNumFormatter,
+ const TypedWhichId<SvxNumberInfoItem> nId ) :
+ SfxPoolItem ( nId ),
+ pFormatter ( pNumFormatter ),
+ eValueType ( SvxNumberValueType::Undefined ),
+ aStringVal ( "" ),
+ nDoubleVal ( 0 )
+{
+}
+
+
+SvxNumberInfoItem::SvxNumberInfoItem( SvNumberFormatter* pNumFormatter,
+ const OUString& rVal, const TypedWhichId<SvxNumberInfoItem> nId ) :
+ SfxPoolItem ( nId ),
+ pFormatter ( pNumFormatter ),
+ eValueType ( SvxNumberValueType::String ),
+ aStringVal ( rVal ),
+ nDoubleVal ( 0 )
+{
+}
+
+
+SvxNumberInfoItem::SvxNumberInfoItem( SvNumberFormatter* pNumFormatter,
+ const double& rVal, const TypedWhichId<SvxNumberInfoItem> nId ) :
+ SfxPoolItem ( nId ),
+ pFormatter ( pNumFormatter ),
+ eValueType ( SvxNumberValueType::Number ),
+ aStringVal ( "" ),
+ nDoubleVal ( rVal )
+{
+}
+
+
+SvxNumberInfoItem::SvxNumberInfoItem( SvNumberFormatter* pNumFormatter,
+ const double& rVal, const OUString& rValueStr,
+ const TypedWhichId<SvxNumberInfoItem> nId ) :
+ SfxPoolItem ( nId ),
+ pFormatter ( pNumFormatter ),
+ eValueType ( SvxNumberValueType::Number ),
+ aStringVal ( rValueStr ),
+ nDoubleVal ( rVal )
+{
+}
+
+
+SvxNumberInfoItem::SvxNumberInfoItem( const SvxNumberInfoItem& rItem ) :
+ SfxPoolItem ( rItem ),
+ pFormatter ( rItem.pFormatter ),
+ eValueType ( rItem.eValueType ),
+ aStringVal ( rItem.aStringVal ),
+ nDoubleVal ( rItem.nDoubleVal ),
+ mvDelFormats( rItem.mvDelFormats )
+{
+}
+
+
+SvxNumberInfoItem::~SvxNumberInfoItem()
+{
+}
+
+
+bool SvxNumberInfoItem::GetPresentation
+(
+ SfxItemPresentation /*ePres*/,
+ MapUnit /*eCoreUnit*/,
+ MapUnit /*ePresUnit*/,
+ OUString& rText, const IntlWrapper&
+) const
+{
+ rText.clear();
+ return false;
+}
+
+
+bool SvxNumberInfoItem::operator==( const SfxPoolItem& rItem ) const
+{
+ assert(SfxPoolItem::operator==(rItem));
+
+ const SvxNumberInfoItem& rOther = static_cast<const SvxNumberInfoItem&>(rItem);
+
+ return mvDelFormats == rOther.mvDelFormats &&
+ pFormatter == rOther.pFormatter &&
+ eValueType == rOther.eValueType &&
+ nDoubleVal == rOther.nDoubleVal &&
+ aStringVal == rOther.aStringVal;
+}
+
+SvxNumberInfoItem* SvxNumberInfoItem::Clone( SfxItemPool * ) const
+{
+ return new SvxNumberInfoItem( *this );
+}
+
+void SvxNumberInfoItem::SetDelFormats( std::vector<sal_uInt32> && aData )
+{
+ mvDelFormats = std::move(aData);
+}
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/items/ofaitem.cxx b/svx/source/items/ofaitem.cxx
new file mode 100644
index 000000000..688507592
--- /dev/null
+++ b/svx/source/items/ofaitem.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/ofaitem.hxx>
+#include <svx/xtable.hxx>
+
+OfaPtrItem::OfaPtrItem( sal_uInt16 _nWhich, void *_pPtr )
+ : SfxPoolItem( _nWhich ), pPtr( _pPtr )
+{
+
+}
+
+bool OfaPtrItem::operator==( const SfxPoolItem& rItem) const
+{
+ return SfxPoolItem::operator==(rItem) &&
+ static_cast<const OfaPtrItem&>(rItem).pPtr == pPtr;
+}
+
+OfaPtrItem* OfaPtrItem::Clone( SfxItemPool * ) const
+{
+ return new OfaPtrItem( *this );
+}
+
+OfaXColorListItem::OfaXColorListItem( sal_uInt16 _nWhich, const rtl::Reference<XColorList> &xRef )
+ : SfxPoolItem( _nWhich ), mxRef( xRef )
+{}
+
+bool OfaXColorListItem::operator==( const SfxPoolItem& rItem ) const
+{
+ return SfxPoolItem::operator==(rItem)
+ && mxRef == static_cast<OfaXColorListItem const &>(rItem).mxRef;
+}
+
+OfaXColorListItem* OfaXColorListItem::Clone( SfxItemPool* /*pPool = 0*/ ) const
+{
+ return new OfaXColorListItem( *this );
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/items/pageitem.cxx b/svx/source/items/pageitem.cxx
new file mode 100644
index 000000000..93239ea3d
--- /dev/null
+++ b/svx/source/items/pageitem.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 <sal/config.h>
+
+#include <utility>
+
+#include <osl/diagnose.h>
+#include <comphelper/processfactory.hxx>
+#include <tools/resary.hxx>
+#include <svx/pageitem.hxx>
+#include <svx/strarray.hxx>
+#include <editeng/itemtype.hxx>
+#include <svx/unomid.hxx>
+#include <com/sun/star/text/DefaultNumberingProvider.hpp>
+#include <com/sun/star/text/XNumberingTypeInfo.hpp>
+#include <com/sun/star/style/NumberingType.hpp>
+#include <com/sun/star/style/PageStyleLayout.hpp>
+#include <svl/itemset.hxx>
+#include <svx/strings.hrc>
+#include <svx/dialmgr.hxx>
+
+using namespace ::com::sun::star;
+
+SfxPoolItem* SvxPageItem::CreateDefault() { return new SvxPageItem(TypedWhichId<SvxPageItem>(0));}
+
+SvxPageItem::SvxPageItem( const TypedWhichId<SvxPageItem> nId ) : SfxPoolItem( nId ),
+
+ eNumType ( SVX_NUM_ARABIC ),
+ bLandscape ( false ),
+ eUse ( SvxPageUsage::All )
+{
+}
+
+// Copy-Ctor
+SvxPageItem::SvxPageItem( const SvxPageItem& rItem )
+ : SfxPoolItem( rItem )
+{
+ eNumType = rItem.eNumType;
+ bLandscape = rItem.bLandscape;
+ eUse = rItem.eUse;
+}
+
+SvxPageItem::~SvxPageItem() {}
+
+// Clone
+SvxPageItem* SvxPageItem::Clone( SfxItemPool * ) const
+{
+ return new SvxPageItem( *this );
+}
+
+// Test for equality
+bool SvxPageItem::operator==( const SfxPoolItem& rAttr ) const
+{
+ assert(SfxPoolItem::operator==(rAttr));
+ const SvxPageItem& rItem = static_cast<const SvxPageItem&>(rAttr);
+ return ( eNumType == rItem.eNumType &&
+ bLandscape == rItem.bLandscape &&
+ eUse == rItem.eUse );
+}
+
+static OUString GetUsageText( const SvxPageUsage eU )
+{
+ switch( eU )
+ {
+ case SvxPageUsage::Left : return SvxResId(RID_SVXITEMS_PAGE_USAGE_LEFT);
+ case SvxPageUsage::Right : return SvxResId(RID_SVXITEMS_PAGE_USAGE_RIGHT);
+ case SvxPageUsage::All : return SvxResId(RID_SVXITEMS_PAGE_USAGE_ALL);
+ case SvxPageUsage::Mirror: return SvxResId(RID_SVXITEMS_PAGE_USAGE_MIRROR);
+ default: return OUString();
+ }
+}
+
+const TranslateId RID_SVXITEMS_PAGE_NUMS[] =
+{
+ RID_SVXITEMS_PAGE_NUM_CHR_UPPER,
+ RID_SVXITEMS_PAGE_NUM_CHR_LOWER,
+ RID_SVXITEMS_PAGE_NUM_ROM_UPPER,
+ RID_SVXITEMS_PAGE_NUM_ROM_LOWER,
+ RID_SVXITEMS_PAGE_NUM_ARABIC,
+ RID_SVXITEMS_PAGE_NUM_NONE
+};
+
+namespace
+{
+ OUString GetNumberingDescription(SvxNumType eNumType)
+ {
+ // classic ones, keep displaying the old name
+ if (eNumType <= css::style::NumberingType::NUMBER_NONE)
+ return SvxResId(RID_SVXITEMS_PAGE_NUMS[eNumType]);
+ // new ones, reuse the text used in the numbering dropdown list
+ sal_uInt32 n = SvxNumberingTypeTable::FindIndex(eNumType);
+ if (n != RESARRAY_INDEX_NOTFOUND)
+ return SvxNumberingTypeTable::GetString(n);
+ css::uno::Reference<css::uno::XComponentContext> xContext = comphelper::getProcessComponentContext();
+ css::uno::Reference<css::text::XDefaultNumberingProvider> xDefNum = css::text::DefaultNumberingProvider::create(xContext);
+ css::uno::Reference<css::text::XNumberingTypeInfo> xInfo(xDefNum, css::uno::UNO_QUERY);
+ if (!xInfo.is())
+ return OUString();
+ return xInfo->getNumberingIdentifier(eNumType);
+ }
+}
+
+bool SvxPageItem::GetPresentation
+(
+ SfxItemPresentation ePres,
+ MapUnit /*eCoreUnit*/,
+ MapUnit /*ePresUnit*/,
+ OUString& rText, const IntlWrapper&
+) const
+{
+ rText.clear();
+ OUString cpDelimTmp(cpDelim);
+
+ switch ( ePres )
+ {
+ case SfxItemPresentation::Nameless:
+ {
+ if ( !aDescName.isEmpty() )
+ {
+ rText = aDescName + cpDelimTmp;
+ }
+ rText += GetNumberingDescription(eNumType) + cpDelimTmp;
+ if ( bLandscape )
+ rText += SvxResId(RID_SVXITEMS_PAGE_LAND_TRUE);
+ else
+ rText += SvxResId(RID_SVXITEMS_PAGE_LAND_FALSE);
+ OUString aUsageText = GetUsageText( eUse );
+ if (!aUsageText.isEmpty())
+ {
+ rText += cpDelimTmp + aUsageText;
+ }
+ return true;
+ }
+ case SfxItemPresentation::Complete:
+ {
+ rText += SvxResId(RID_SVXITEMS_PAGE_COMPLETE);
+ if ( !aDescName.isEmpty() )
+ {
+ rText += aDescName + cpDelimTmp;
+ }
+ rText += GetNumberingDescription(eNumType) + cpDelimTmp;
+ if ( bLandscape )
+ rText += SvxResId(RID_SVXITEMS_PAGE_LAND_TRUE);
+ else
+ rText += SvxResId(RID_SVXITEMS_PAGE_LAND_FALSE);
+ OUString aUsageText = GetUsageText( eUse );
+ if (!aUsageText.isEmpty())
+ {
+ rText += cpDelimTmp + aUsageText;
+ }
+ return true;
+ }
+ default: ;//prevent warning
+ }
+ return false;
+}
+
+
+bool SvxPageItem::QueryValue( uno::Any& rVal, sal_uInt8 nMemberId ) const
+{
+// sal_Bool bConvert = 0!=(nMemberId&CONVERT_TWIPS);
+ nMemberId &= ~CONVERT_TWIPS;
+ switch( nMemberId )
+ {
+ case MID_PAGE_NUMTYPE:
+ {
+ //! constants aren't in IDLs any more ?!?
+ rVal <<= static_cast<sal_Int16>( eNumType );
+ }
+ break;
+ case MID_PAGE_ORIENTATION:
+ rVal <<= bLandscape;
+ break;
+ case MID_PAGE_LAYOUT :
+ {
+ style::PageStyleLayout eRet;
+ switch(eUse)
+ {
+ case SvxPageUsage::Left : eRet = style::PageStyleLayout_LEFT; break;
+ case SvxPageUsage::Right : eRet = style::PageStyleLayout_RIGHT; break;
+ case SvxPageUsage::All : eRet = style::PageStyleLayout_ALL; break;
+ case SvxPageUsage::Mirror: eRet = style::PageStyleLayout_MIRRORED; break;
+ default:
+ OSL_FAIL("what layout is this?");
+ return false;
+ }
+ rVal <<= eRet;
+ }
+ break;
+ }
+
+ return true;
+}
+
+bool SvxPageItem::PutValue( const uno::Any& rVal, sal_uInt8 nMemberId )
+{
+ switch( nMemberId & ~CONVERT_TWIPS )
+ {
+ case MID_PAGE_NUMTYPE:
+ {
+ sal_Int32 nValue = 0;
+ if(!(rVal >>= nValue))
+ return false;
+
+ eNumType = static_cast<SvxNumType>(nValue);
+ }
+ break;
+ case MID_PAGE_ORIENTATION:
+ bLandscape = Any2Bool(rVal);
+ break;
+ case MID_PAGE_LAYOUT :
+ {
+ style::PageStyleLayout eLayout;
+ if(!(rVal >>= eLayout))
+ {
+ sal_Int32 nValue = 0;
+ if(!(rVal >>= nValue))
+ return false;
+ eLayout = static_cast<style::PageStyleLayout>(nValue);
+ }
+ switch( eLayout )
+ {
+ case style::PageStyleLayout_LEFT : eUse = SvxPageUsage::Left ; break;
+ case style::PageStyleLayout_RIGHT : eUse = SvxPageUsage::Right; break;
+ case style::PageStyleLayout_ALL : eUse = SvxPageUsage::All ; break;
+ case style::PageStyleLayout_MIRRORED: eUse = SvxPageUsage::Mirror;break;
+ default: ;//prevent warning
+ }
+ }
+ break;
+ }
+ return true;
+}
+
+// HeaderFooterSet
+SvxSetItem::SvxSetItem( const TypedWhichId<SvxSetItem> nId, const SfxItemSet& rSet ) :
+
+ SfxSetItem( nId, rSet )
+{
+}
+
+SvxSetItem::SvxSetItem( const SvxSetItem& rItem ) :
+
+ SfxSetItem( rItem )
+{
+}
+
+SvxSetItem::SvxSetItem( const TypedWhichId<SvxSetItem> nId, SfxItemSet&& _pSet ) :
+
+ SfxSetItem( nId, std::move(_pSet) )
+{
+}
+
+SvxSetItem* SvxSetItem::Clone( SfxItemPool * ) const
+{
+ return new SvxSetItem(*this);
+}
+
+bool SvxSetItem::GetPresentation
+(
+ SfxItemPresentation /*ePres*/,
+ MapUnit /*eCoreUnit*/,
+ MapUnit /*ePresUnit*/,
+ OUString& rText, const IntlWrapper&
+) const
+{
+ rText.clear();
+ return false;
+}
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/items/postattr.cxx b/svx/source/items/postattr.cxx
new file mode 100644
index 000000000..267c2cd9a
--- /dev/null
+++ b/svx/source/items/postattr.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 <svx/postattr.hxx>
+#include <svx/strings.hrc>
+#include <svx/dialmgr.hxx>
+
+SfxPoolItem* SvxPostItAuthorItem::CreateDefault() { return new SvxPostItAuthorItem(TypedWhichId<SvxPostItAuthorItem>(0)); }
+SfxPoolItem* SvxPostItDateItem::CreateDefault() { return new SvxPostItDateItem(TypedWhichId<SvxPostItDateItem>(0)); }
+SfxPoolItem* SvxPostItTextItem::CreateDefault() { return new SvxPostItTextItem(TypedWhichId<SvxPostItTextItem>(0)); }
+SfxPoolItem* SvxPostItIdItem::CreateDefault() { return new SvxPostItIdItem(TypedWhichId<SvxPostItIdItem>(0)); }
+
+SvxPostItAuthorItem::SvxPostItAuthorItem( TypedWhichId<SvxPostItAuthorItem> _nWhich )
+{
+ SetWhich( _nWhich );
+}
+
+
+SvxPostItAuthorItem::SvxPostItAuthorItem( const OUString& rAuthor,
+ TypedWhichId<SvxPostItAuthorItem> _nWhich ) :
+ SfxStringItem( _nWhich, rAuthor )
+{
+}
+
+
+bool SvxPostItAuthorItem::GetPresentation
+(
+ SfxItemPresentation ePres,
+ MapUnit /*eCoreUnit*/,
+ MapUnit /*ePresUnit*/,
+ OUString& rText, const IntlWrapper&
+) const
+{
+ switch ( ePres )
+ {
+ case SfxItemPresentation::Nameless:
+ rText = GetValue();
+ return true;
+ case SfxItemPresentation::Complete:
+ rText = SvxResId(RID_SVXITEMS_AUTHOR_COMPLETE) + GetValue();
+ return true;
+ default: ;//prevent warning
+ }
+ return false;
+}
+
+SvxPostItAuthorItem* SvxPostItAuthorItem::Clone( SfxItemPool * ) const
+{
+ return new SvxPostItAuthorItem( *this );
+}
+
+SvxPostItDateItem::SvxPostItDateItem( TypedWhichId<SvxPostItDateItem> _nWhich )
+ : SfxStringItem(_nWhich)
+{
+}
+
+
+SvxPostItDateItem::SvxPostItDateItem( const OUString& rDate, TypedWhichId<SvxPostItDateItem> _nWhich ) :
+ SfxStringItem( _nWhich, rDate )
+{
+}
+
+
+bool SvxPostItDateItem::GetPresentation
+(
+ SfxItemPresentation ePres,
+ MapUnit /*eCoreUnit*/,
+ MapUnit /*ePresUnit*/,
+ OUString& rText, const IntlWrapper&
+) const
+{
+ switch ( ePres )
+ {
+ case SfxItemPresentation::Nameless:
+ rText = GetValue();
+ return true;
+ case SfxItemPresentation::Complete:
+ rText = SvxResId(RID_SVXITEMS_DATE_COMPLETE) + GetValue();
+ return true;
+ default: ;//prevent warning
+ }
+ return false;
+}
+
+
+SvxPostItDateItem* SvxPostItDateItem::Clone( SfxItemPool * ) const
+{
+ return new SvxPostItDateItem( *this );
+}
+
+SvxPostItTextItem::SvxPostItTextItem( TypedWhichId<SvxPostItTextItem> _nWhich )
+{
+ SetWhich( _nWhich );
+}
+
+SvxPostItTextItem::SvxPostItTextItem( const OUString& rText, TypedWhichId<SvxPostItTextItem> _nWhich ) :
+
+ SfxStringItem( _nWhich, rText )
+{
+}
+
+bool SvxPostItTextItem::GetPresentation
+(
+ SfxItemPresentation ePres,
+ MapUnit /*eCoreUnit*/,
+ MapUnit /*ePresUnit*/,
+ OUString& rText, const IntlWrapper&
+) const
+{
+ switch ( ePres )
+ {
+ case SfxItemPresentation::Nameless:
+ rText = GetValue();
+ return true;
+ case SfxItemPresentation::Complete:
+ rText = SvxResId(RID_SVXITEMS_TEXT_COMPLETE) + GetValue();
+ return true;
+ default: ;//prevent warning
+ }
+ return false;
+}
+
+SvxPostItTextItem* SvxPostItTextItem::Clone( SfxItemPool * ) const
+{
+ return new SvxPostItTextItem( *this );
+}
+
+SvxPostItIdItem::SvxPostItIdItem( TypedWhichId<SvxPostItIdItem> _nWhich )
+{
+ SetWhich( _nWhich );
+}
+
+SvxPostItIdItem* SvxPostItIdItem::Clone( SfxItemPool * ) const
+{
+ return new SvxPostItIdItem( *this );
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/items/rotmodit.cxx b/svx/source/items/rotmodit.cxx
new file mode 100644
index 000000000..eb559fdae
--- /dev/null
+++ b/svx/source/items/rotmodit.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 <com/sun/star/table/CellVertJustify2.hpp>
+
+#include <svx/dialmgr.hxx>
+#include <svx/rotmodit.hxx>
+#include <rotationstrings.hrc>
+
+using namespace ::com::sun::star;
+
+
+SfxPoolItem* SvxRotateModeItem::CreateDefault() { return new SvxRotateModeItem(SVX_ROTATE_MODE_STANDARD, TypedWhichId<SvxRotateModeItem>(0));}
+
+
+// SvxRotateModeItem - orientation with turned text
+
+
+SvxRotateModeItem::SvxRotateModeItem( SvxRotateMode eMode, TypedWhichId<SvxRotateModeItem> _nWhich )
+ : SfxEnumItem( _nWhich, eMode )
+{
+}
+
+SvxRotateModeItem::SvxRotateModeItem( const SvxRotateModeItem& rItem )
+ : SfxEnumItem( rItem )
+{
+}
+
+SvxRotateModeItem::~SvxRotateModeItem()
+{
+}
+
+OUString SvxRotateModeItem::GetValueText(SvxRotateMode nVal)
+{
+ assert(nVal <= SVX_ROTATE_MODE_BOTTOM && "enum overflow!");
+ return SvxResId(RID_SVXITEMS_ROTATE_MODE[static_cast<size_t>(nVal)]);
+}
+
+bool SvxRotateModeItem::GetPresentation(
+ SfxItemPresentation ePres,
+ MapUnit /*eCoreUnit*/, MapUnit /*ePresUnit*/,
+ OUString& rText, const IntlWrapper& ) const
+{
+ rText.clear();
+
+ switch ( ePres )
+ {
+ case SfxItemPresentation::Complete:
+ rText += GetValueText(GetValue());
+ return true;
+
+ case SfxItemPresentation::Nameless:
+ rText += OUString::number( GetValue() );
+ return true;
+ default: ;//prevent warning
+ }
+
+ return false;
+}
+
+sal_uInt16 SvxRotateModeItem::GetValueCount() const
+{
+ return 4; // STANDARD, TOP, CENTER, BOTTOM
+}
+
+SvxRotateModeItem* SvxRotateModeItem::Clone( SfxItemPool* ) const
+{
+ return new SvxRotateModeItem( *this );
+}
+
+bool SvxRotateModeItem::QueryValue( uno::Any& rVal, sal_uInt8 /*nMemberId*/ ) const
+{
+ sal_Int32 nUno = table::CellVertJustify2::STANDARD;
+ switch ( GetValue() )
+ {
+ case SVX_ROTATE_MODE_STANDARD: nUno = table::CellVertJustify2::STANDARD; break;
+ case SVX_ROTATE_MODE_TOP: nUno = table::CellVertJustify2::TOP; break;
+ case SVX_ROTATE_MODE_CENTER: nUno = table::CellVertJustify2::CENTER; break;
+ case SVX_ROTATE_MODE_BOTTOM: nUno = table::CellVertJustify2::BOTTOM; break;
+ }
+ rVal <<= nUno;
+ return true;
+}
+
+bool SvxRotateModeItem::PutValue( const uno::Any& rVal, sal_uInt8 /*nMemberId*/ )
+{
+ sal_Int32 nUno(0);
+ if(!(rVal >>= nUno))
+ {
+ nUno = table::CellVertJustify2::STANDARD;
+ }
+
+ SvxRotateMode eSvx = SVX_ROTATE_MODE_STANDARD;
+ switch (nUno)
+ {
+ case table::CellVertJustify2::STANDARD: eSvx = SVX_ROTATE_MODE_STANDARD; break;
+ case table::CellVertJustify2::TOP: eSvx = SVX_ROTATE_MODE_TOP; break;
+ case table::CellVertJustify2::CENTER: eSvx = SVX_ROTATE_MODE_CENTER; break;
+ case table::CellVertJustify2::BOTTOM: eSvx = SVX_ROTATE_MODE_BOTTOM; break;
+ default: ;//prevent warning
+ }
+ SetValue( eSvx );
+ return true;
+}
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/items/svxerr.cxx b/svx/source/items/svxerr.cxx
new file mode 100644
index 000000000..24efadda4
--- /dev/null
+++ b/svx/source/items/svxerr.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 <svx/svxerr.hxx>
+#include <svx/dialmgr.hxx>
+#include <svxerr.hrc>
+
+SvxErrorHandler::SvxErrorHandler() :
+ SfxErrorHandler(
+ RID_SVXERRCODE, ErrCodeArea::Svx, ErrCodeArea::Svx, SvxResLocale())
+{
+}
+
+void SvxErrorHandler::ensure()
+{
+ static SvxErrorHandler SINGLETON;
+ // coverity[side_effect_free : FALSE] - not actually side-effect-free
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/items/viewlayoutitem.cxx b/svx/source/items/viewlayoutitem.cxx
new file mode 100644
index 000000000..d923133a1
--- /dev/null
+++ b/svx/source/items/viewlayoutitem.cxx
@@ -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 .
+ */
+
+#include <svx/viewlayoutitem.hxx>
+#include <com/sun/star/uno/Sequence.hxx>
+#include <com/sun/star/beans/PropertyValue.hpp>
+
+#include <comphelper/propertyvalue.hxx>
+#include <osl/diagnose.h>
+
+#include <cassert>
+
+
+SfxPoolItem* SvxViewLayoutItem::CreateDefault() { return new SvxViewLayoutItem; }
+
+constexpr OUStringLiteral VIEWLAYOUT_PARAM_COLUMNS = u"Columns";
+constexpr OUStringLiteral VIEWLAYOUT_PARAM_BOOKMODE = u"BookMode";
+#define VIEWLAYOUT_PARAMS 2
+
+
+SvxViewLayoutItem::SvxViewLayoutItem
+(
+ sal_uInt16 nColumns,
+ bool bBookMode,
+ TypedWhichId<SvxViewLayoutItem> _nWhich
+)
+: SfxUInt16Item( _nWhich, nColumns ),
+ mbBookMode( bBookMode )
+{
+}
+
+
+SvxViewLayoutItem::SvxViewLayoutItem( const SvxViewLayoutItem& rOrig )
+: SfxUInt16Item( rOrig ),
+ mbBookMode( rOrig.IsBookMode() )
+{
+}
+
+
+SvxViewLayoutItem::~SvxViewLayoutItem()
+{
+}
+
+SvxViewLayoutItem* SvxViewLayoutItem::Clone( SfxItemPool * /*pPool*/ ) const
+{
+ return new SvxViewLayoutItem( *this );
+}
+
+bool SvxViewLayoutItem::operator==( const SfxPoolItem& rAttr ) const
+{
+ assert(SfxPoolItem::operator==(rAttr));
+
+ const SvxViewLayoutItem& rItem = static_cast<const SvxViewLayoutItem&>(rAttr);
+
+ return ( GetValue() == rItem.GetValue() &&
+ mbBookMode == rItem.IsBookMode() );
+}
+
+bool SvxViewLayoutItem::QueryValue( css::uno::Any& rVal, sal_uInt8 nMemberId ) const
+{
+ nMemberId &= ~CONVERT_TWIPS;
+ switch ( nMemberId )
+ {
+ case 0 :
+ {
+ css::uno::Sequence< css::beans::PropertyValue > aSeq{
+ comphelper::makePropertyValue(VIEWLAYOUT_PARAM_COLUMNS, sal_Int32( GetValue() )),
+ comphelper::makePropertyValue(VIEWLAYOUT_PARAM_BOOKMODE, mbBookMode)
+ };
+ assert(aSeq.getLength() == VIEWLAYOUT_PARAMS);
+ rVal <<= aSeq;
+ }
+ break;
+
+ case MID_VIEWLAYOUT_COLUMNS : rVal <<= static_cast<sal_Int32>(GetValue()); break;
+ case MID_VIEWLAYOUT_BOOKMODE: rVal <<= mbBookMode; break;
+ default:
+ OSL_FAIL("svx::SvxViewLayoutItem::QueryValue(), Wrong MemberId!");
+ return false;
+ }
+
+ return true;
+}
+
+bool SvxViewLayoutItem::PutValue( const css::uno::Any& rVal, sal_uInt8 nMemberId )
+{
+ nMemberId &= ~CONVERT_TWIPS;
+ switch ( nMemberId )
+ {
+ case 0 :
+ {
+ css::uno::Sequence< css::beans::PropertyValue > aSeq;
+ if (( rVal >>= aSeq ) && ( aSeq.getLength() == VIEWLAYOUT_PARAMS ))
+ {
+ sal_Int32 nColumns( 0 );
+ bool bBookMode = false;
+ bool bAllConverted( true );
+ sal_Int16 nConvertedCount( 0 );
+ for ( const auto& rProp : std::as_const(aSeq) )
+ {
+ if ( rProp.Name == VIEWLAYOUT_PARAM_COLUMNS )
+ {
+ bAllConverted &= ( rProp.Value >>= nColumns );
+ ++nConvertedCount;
+ }
+ else if ( rProp.Name == VIEWLAYOUT_PARAM_BOOKMODE )
+ {
+ bAllConverted &= ( rProp.Value >>= bBookMode );
+ ++nConvertedCount;
+ }
+ }
+
+ if ( bAllConverted && nConvertedCount == VIEWLAYOUT_PARAMS )
+ {
+ SetValue( static_cast<sal_uInt16>(nColumns) );
+ mbBookMode = bBookMode;
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ case MID_VIEWLAYOUT_COLUMNS:
+ {
+ sal_Int32 nVal = 0;
+ if ( rVal >>= nVal )
+ {
+ SetValue( static_cast<sal_uInt16>(nVal) );
+ return true;
+ }
+ else
+ return false;
+ }
+
+ case MID_VIEWLAYOUT_BOOKMODE:
+ {
+ bool bBookMode = false;
+ if ( rVal >>= bBookMode )
+ {
+ mbBookMode = bBookMode;
+ return true;
+ }
+ else
+ return false;
+ }
+
+ default:
+ OSL_FAIL("svx::SvxViewLayoutItem::PutValue(), Wrong MemberId!");
+ return false;
+ }
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/items/zoomslideritem.cxx b/svx/source/items/zoomslideritem.cxx
new file mode 100644
index 000000000..046a03591
--- /dev/null
+++ b/svx/source/items/zoomslideritem.cxx
@@ -0,0 +1,219 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * 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/propertyvalue.hxx>
+#include <osl/diagnose.h>
+
+#include <svx/zoomslideritem.hxx>
+#include <com/sun/star/beans/PropertyValue.hpp>
+
+
+SfxPoolItem* SvxZoomSliderItem::CreateDefault() { return new SvxZoomSliderItem; }
+
+constexpr OUStringLiteral ZOOMSLIDER_PARAM_CURRENTZOOM = u"Columns";
+constexpr OUStringLiteral ZOOMSLIDER_PARAM_SNAPPINGPOINTS = u"SnappingPoints";
+constexpr OUStringLiteral ZOOMSLIDER_PARAM_MINZOOM = u"MinValue";
+constexpr OUStringLiteral ZOOMSLIDER_PARAM_MAXZOOM = u"MaxValue";
+#define ZOOMSLIDER_PARAMS 4
+
+
+SvxZoomSliderItem::SvxZoomSliderItem( sal_uInt16 nCurrentZoom, sal_uInt16 nMinZoom, sal_uInt16 nMaxZoom, TypedWhichId<SvxZoomSliderItem> _nWhich )
+: SfxUInt16Item( _nWhich, nCurrentZoom ), mnMinZoom( nMinZoom ), mnMaxZoom( nMaxZoom )
+{
+}
+
+SvxZoomSliderItem* SvxZoomSliderItem::Clone( SfxItemPool * /*pPool*/ ) const
+{
+ return new SvxZoomSliderItem( *this );
+}
+
+bool SvxZoomSliderItem::operator==( const SfxPoolItem& rAttr ) const
+{
+ assert(SfxPoolItem::operator==(rAttr));
+
+ const SvxZoomSliderItem& rItem = static_cast<const SvxZoomSliderItem&>(rAttr);
+
+ return ( GetValue() == rItem.GetValue() && maValues == rItem.maValues &&
+ mnMinZoom == rItem.mnMinZoom && mnMaxZoom == rItem.mnMaxZoom );
+}
+
+bool SvxZoomSliderItem::QueryValue( css::uno::Any& rVal, sal_uInt8 nMemberId ) const
+{
+ nMemberId &= ~CONVERT_TWIPS;
+ switch ( nMemberId )
+ {
+ case 0 :
+ {
+ css::uno::Sequence< css::beans::PropertyValue > aSeq{
+ comphelper::makePropertyValue(ZOOMSLIDER_PARAM_CURRENTZOOM, sal_Int32( GetValue() )),
+ comphelper::makePropertyValue(ZOOMSLIDER_PARAM_SNAPPINGPOINTS, maValues),
+ comphelper::makePropertyValue(ZOOMSLIDER_PARAM_MINZOOM, mnMinZoom),
+ comphelper::makePropertyValue(ZOOMSLIDER_PARAM_MAXZOOM, mnMaxZoom)
+ };
+ assert(aSeq.getLength() == ZOOMSLIDER_PARAMS);
+ rVal <<= aSeq;
+ }
+ break;
+
+ case MID_ZOOMSLIDER_CURRENTZOOM :
+ {
+ rVal <<= static_cast<sal_Int32>(GetValue());
+ }
+ break;
+ case MID_ZOOMSLIDER_SNAPPINGPOINTS:
+ {
+ rVal <<= maValues;
+ }
+ break;
+ case MID_ZOOMSLIDER_MINZOOM:
+ {
+ rVal <<= mnMinZoom;
+ }
+ break;
+ case MID_ZOOMSLIDER_MAXZOOM:
+ {
+ rVal <<= mnMaxZoom;
+ }
+ break;
+ default:
+ OSL_FAIL("svx::SvxZoomSliderItem::QueryValue(), Wrong MemberId!");
+ return false;
+ }
+
+ return true;
+}
+
+bool SvxZoomSliderItem::PutValue( const css::uno::Any& rVal, sal_uInt8 nMemberId )
+{
+ nMemberId &= ~CONVERT_TWIPS;
+ switch ( nMemberId )
+ {
+ case 0 :
+ {
+ css::uno::Sequence< css::beans::PropertyValue > aSeq;
+ if (( rVal >>= aSeq ) && ( aSeq.getLength() == ZOOMSLIDER_PARAMS ))
+ {
+ sal_Int32 nCurrentZoom( 0 );
+ css::uno::Sequence < sal_Int32 > aValues;
+
+ bool bAllConverted( true );
+ sal_Int16 nConvertedCount( 0 );
+ sal_Int32 nMinZoom( 0 ), nMaxZoom( 0 );
+
+ for ( const auto& rProp : std::as_const(aSeq) )
+ {
+ if ( rProp.Name == ZOOMSLIDER_PARAM_CURRENTZOOM )
+ {
+ bAllConverted &= ( rProp.Value >>= nCurrentZoom );
+ ++nConvertedCount;
+ }
+ else if ( rProp.Name == ZOOMSLIDER_PARAM_SNAPPINGPOINTS )
+ {
+ bAllConverted &= ( rProp.Value >>= aValues );
+ ++nConvertedCount;
+ }
+ else if( rProp.Name == ZOOMSLIDER_PARAM_MINZOOM )
+ {
+ bAllConverted &= ( rProp.Value >>= nMinZoom );
+ ++nConvertedCount;
+ }
+ else if( rProp.Name == ZOOMSLIDER_PARAM_MAXZOOM )
+ {
+ bAllConverted &= ( rProp.Value >>= nMaxZoom );
+ ++nConvertedCount;
+ }
+ }
+
+ if ( bAllConverted && nConvertedCount == ZOOMSLIDER_PARAMS )
+ {
+ SetValue( static_cast<sal_uInt16>(nCurrentZoom) );
+ maValues = aValues;
+ mnMinZoom = sal::static_int_cast< sal_uInt16 >( nMinZoom );
+ mnMaxZoom = sal::static_int_cast< sal_uInt16 >( nMaxZoom );
+
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ case MID_ZOOMSLIDER_CURRENTZOOM:
+ {
+ sal_Int32 nVal = 0;
+ if ( rVal >>= nVal )
+ {
+ SetValue( static_cast<sal_uInt16>(nVal) );
+ return true;
+ }
+ else
+ return false;
+ }
+
+ case MID_ZOOMSLIDER_SNAPPINGPOINTS:
+ {
+ css::uno::Sequence < sal_Int32 > aValues;
+ if ( rVal >>= aValues )
+ {
+ maValues = aValues;
+ return true;
+ }
+ else
+ return false;
+ }
+ case MID_ZOOMSLIDER_MINZOOM:
+ {
+ sal_Int32 nVal = 0;
+ if( rVal >>= nVal )
+ {
+ mnMinZoom = static_cast<sal_uInt16>(nVal);
+ return true;
+ }
+ else
+ return false;
+ }
+ case MID_ZOOMSLIDER_MAXZOOM:
+ {
+ sal_Int32 nVal = 0;
+ if( rVal >>= nVal )
+ {
+ mnMaxZoom = static_cast<sal_uInt16>(nVal);
+ return true;
+ }
+ else
+ return false;
+ }
+ default:
+ OSL_FAIL("svx::SvxZoomSliderItem::PutValue(), Wrong MemberId!");
+ return false;
+ }
+}
+
+void SvxZoomSliderItem::AddSnappingPoint( sal_Int32 nNew )
+{
+ const sal_Int32 nValues = maValues.getLength();
+ maValues.realloc( nValues + 1 );
+ sal_Int32* pValues = maValues.getArray();
+ pValues[ nValues ] = nNew;
+}
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/mnuctrls/clipboardctl.cxx b/svx/source/mnuctrls/clipboardctl.cxx
new file mode 100644
index 000000000..1633561a5
--- /dev/null
+++ b/svx/source/mnuctrls/clipboardctl.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 <sal/config.h>
+
+#include <comphelper/propertyvalue.hxx>
+#include <sfx2/tbxctrl.hxx>
+#include <svl/intitem.hxx>
+#include <vcl/svapp.hxx>
+#include <vcl/toolbox.hxx>
+#include <vcl/weldutils.hxx>
+#include <svx/clipboardctl.hxx>
+#include <svx/clipfmtitem.hxx>
+
+#include <svtools/insdlg.hxx>
+#include <svx/svxids.hrc>
+
+using namespace ::com::sun::star::uno;
+using namespace ::com::sun::star::beans;
+
+
+SFX_IMPL_TOOLBOX_CONTROL( SvxClipBoardControl, SfxVoidItem /*SfxUInt16Item*/ );
+
+
+SvxClipBoardControl::SvxClipBoardControl(
+ sal_uInt16 nSlotId, ToolBoxItemId nId, ToolBox& rTbx ) :
+
+ SfxToolBoxControl( nSlotId, nId, rTbx ),
+ bDisabled( false )
+{
+ addStatusListener( ".uno:ClipboardFormatItems");
+ ToolBox& rBox = GetToolBox();
+ rBox.SetItemBits( nId, ToolBoxItemBits::DROPDOWN | rBox.GetItemBits( nId ) );
+ rBox.Invalidate();
+}
+
+SvxClipBoardControl::~SvxClipBoardControl()
+{
+}
+
+void SvxClipBoardControl::CreatePopupWindow()
+{
+ const SvxClipboardFormatItem* pFmtItem = dynamic_cast<SvxClipboardFormatItem*>( pClipboardFmtItem.get() );
+ if ( pFmtItem )
+ {
+ std::unique_ptr<weld::Builder> xBuilder(Application::CreateBuilder(nullptr, "svx/ui/clipboardmenu.ui"));
+ std::unique_ptr<weld::Menu> xPopup(xBuilder->weld_menu("menu"));
+
+ sal_uInt16 nCount = pFmtItem->Count();
+ for (sal_uInt16 i = 0; i < nCount; ++i)
+ {
+ SotClipboardFormatId nFmtID = pFmtItem->GetClipbrdFormatId( i );
+ OUString aFmtStr( pFmtItem->GetClipbrdFormatName( i ) );
+ if (aFmtStr.isEmpty())
+ aFmtStr = SvPasteObjectHelper::GetSotFormatUIName( nFmtID );
+ xPopup->append(OUString::number(static_cast<sal_uInt32>(nFmtID)), aFmtStr);
+ }
+
+ ToolBox& rBox = GetToolBox();
+ ToolBoxItemId nId = GetId();
+ rBox.SetItemDown( nId, true );
+
+ ::tools::Rectangle aRect(rBox.GetItemRect(nId));
+ weld::Window* pParent = weld::GetPopupParent(rBox, aRect);
+ OString sResult = xPopup->popup_at_rect(pParent, aRect);
+
+ rBox.SetItemDown( nId, false );
+
+ SfxUInt32Item aItem(SID_CLIPBOARD_FORMAT_ITEMS, sResult.toUInt32());
+
+ Any a;
+ aItem.QueryValue( a );
+ Sequence< PropertyValue > aArgs{ comphelper::makePropertyValue("SelectedFormat", a) };
+ Dispatch( ".uno:ClipboardFormatItems",
+ aArgs );
+ }
+
+ GetToolBox().EndSelection();
+}
+
+void SvxClipBoardControl::StateChangedAtToolBoxControl( sal_uInt16 nSID, SfxItemState eState, const SfxPoolItem* pState )
+{
+ if ( SID_CLIPBOARD_FORMAT_ITEMS == nSID )
+ {
+ pClipboardFmtItem.reset();
+ if ( eState >= SfxItemState::DEFAULT )
+ {
+ pClipboardFmtItem.reset( pState->Clone() );
+ GetToolBox().SetItemBits( GetId(), GetToolBox().GetItemBits( GetId() ) | ToolBoxItemBits::DROPDOWN );
+ }
+ else if ( !bDisabled )
+ GetToolBox().SetItemBits( GetId(), GetToolBox().GetItemBits( GetId() ) & ~ToolBoxItemBits::DROPDOWN );
+ GetToolBox().Invalidate( GetToolBox().GetItemRect( GetId() ) );
+ }
+ else
+ {
+ // enable the item as a whole
+ bDisabled = (GetItemState(pState) == SfxItemState::DISABLED);
+ GetToolBox().EnableItem( GetId(), (GetItemState(pState) != SfxItemState::DISABLED) );
+ }
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/mnuctrls/smarttagmenu.cxx b/svx/source/mnuctrls/smarttagmenu.cxx
new file mode 100644
index 000000000..86f11e8d0
--- /dev/null
+++ b/svx/source/mnuctrls/smarttagmenu.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 <memory>
+#include <svtools/popupmenucontrollerbase.hxx>
+#include <svx/SmartTagItem.hxx>
+#include <toolkit/awt/vclxmenu.hxx>
+#include <vcl/commandinfoprovider.hxx>
+
+const sal_uInt16 MN_ST_INSERT_START = 500;
+
+namespace {
+
+class SmartTagMenuController : public svt::PopupMenuControllerBase
+{
+public:
+ explicit SmartTagMenuController( const css::uno::Reference< css::uno::XComponentContext >& rxContext );
+
+ // XStatusListener
+ virtual void SAL_CALL statusChanged( const css::frame::FeatureStateEvent& rEvent ) override;
+
+ // XMenuListener
+ virtual void SAL_CALL itemSelected( const css::awt::MenuEvent& rEvent ) override;
+
+ // XServiceInfo
+ virtual OUString SAL_CALL getImplementationName() override;
+ virtual css::uno::Sequence< OUString > SAL_CALL getSupportedServiceNames() override;
+
+private:
+ void FillMenu();
+ bool MenuSelect(sal_uInt16 nMyId);
+ struct InvokeAction
+ {
+ css::uno::Reference< css::smarttags::XSmartTagAction > m_xAction;
+ css::uno::Reference< css::container::XStringKeyMap > m_xSmartTagProperties;
+ sal_uInt32 m_nActionID;
+ InvokeAction( css::uno::Reference< css::smarttags::XSmartTagAction > const & xAction,
+ css::uno::Reference< css::container::XStringKeyMap > const & xSmartTagProperties,
+ sal_uInt32 nActionID ) : m_xAction( xAction ), m_xSmartTagProperties( xSmartTagProperties ), m_nActionID( nActionID ) {}
+ };
+ std::vector< InvokeAction > m_aInvokeActions;
+ std::unique_ptr< const SvxSmartTagItem > m_pSmartTagItem;
+};
+
+}
+
+SmartTagMenuController::SmartTagMenuController( const css::uno::Reference< css::uno::XComponentContext >& rxContext )
+ : svt::PopupMenuControllerBase( rxContext )
+{
+}
+
+void SmartTagMenuController::statusChanged( const css::frame::FeatureStateEvent& rEvent )
+{
+ resetPopupMenu( m_xPopupMenu );
+
+ css::uno::Sequence< css::beans::PropertyValue > aProperties;
+ if ( !rEvent.IsEnabled || !( rEvent.State >>= aProperties ) )
+ return;
+
+ css::uno::Sequence< css::uno::Sequence< css::uno::Reference< css::smarttags::XSmartTagAction > > > aActionComponents;
+ css::uno::Sequence< css::uno::Sequence< sal_Int32 > > aActionIndices;
+ css::uno::Sequence< css::uno::Reference< css::container::XStringKeyMap > > aStringKeyMaps;
+ css::uno::Reference< css::text::XTextRange > xTextRange;
+ css::uno::Reference< css::frame::XController > xController;
+ css::lang::Locale aLocale;
+ OUString aApplicationName;
+ OUString aRangeText;
+
+ for ( const auto& aProperty : std::as_const(aProperties) )
+ {
+ if ( aProperty.Name == "ActionComponents" )
+ aProperty.Value >>= aActionComponents;
+ else if ( aProperty.Name == "ActionIndices" )
+ aProperty.Value >>= aActionIndices;
+ else if ( aProperty.Name == "StringKeyMaps" )
+ aProperty.Value >>= aStringKeyMaps;
+ else if ( aProperty.Name == "TextRange" )
+ aProperty.Value >>= xTextRange;
+ else if ( aProperty.Name == "Controller" )
+ aProperty.Value >>= xController;
+ else if ( aProperty.Name == "Locale" )
+ aProperty.Value >>= aLocale;
+ else if ( aProperty.Name == "ApplicationName" )
+ aProperty.Value >>= aApplicationName;
+ else if ( aProperty.Name == "RangeText" )
+ aProperty.Value >>= aRangeText;
+ }
+ m_pSmartTagItem.reset( new SvxSmartTagItem( TypedWhichId<SvxSmartTagItem>(0), aActionComponents, aActionIndices, aStringKeyMaps, xTextRange, xController, aLocale, aApplicationName, aRangeText ) );
+ FillMenu();
+}
+
+void SmartTagMenuController::FillMenu()
+{
+ if ( !m_pSmartTagItem )
+ return;
+
+ sal_uInt16 nMenuId = 1;
+ sal_uInt16 nSubMenuId = MN_ST_INSERT_START;
+
+ const css::uno::Sequence< css::uno::Sequence< css::uno::Reference< css::smarttags::XSmartTagAction > > >& rActionComponentsSequence = m_pSmartTagItem->GetActionComponentsSequence();
+ const css::uno::Sequence< css::uno::Sequence< sal_Int32 > >& rActionIndicesSequence = m_pSmartTagItem->GetActionIndicesSequence();
+ const css::uno::Sequence< css::uno::Reference< css::container::XStringKeyMap > >& rStringKeyMaps = m_pSmartTagItem->GetStringKeyMaps();
+ const css::lang::Locale& rLocale = m_pSmartTagItem->GetLocale();
+ const OUString aApplicationName = m_pSmartTagItem->GetApplicationName();
+ const OUString aRangeText = m_pSmartTagItem->GetRangeText();
+ const css::uno::Reference< css::text::XTextRange >& xTextRange = m_pSmartTagItem->GetTextRange();
+ const css::uno::Reference< css::frame::XController >& xController = m_pSmartTagItem->GetController();
+
+ for ( sal_Int32 i = 0; i < rActionComponentsSequence.getLength(); ++i )
+ {
+ css::uno::Reference< css::container::XStringKeyMap > xSmartTagProperties = rStringKeyMaps[i];
+
+ // Get all actions references associated with the current smart tag type
+ const css::uno::Sequence< css::uno::Reference< css::smarttags::XSmartTagAction > >& rActionComponents = rActionComponentsSequence[i];
+ const css::uno::Sequence< sal_Int32 >& rActionIndices = rActionIndicesSequence[i];
+
+ if ( !rActionComponents.hasElements() || !rActionIndices.hasElements() )
+ continue;
+
+ // Ask first entry for the smart tag type caption
+ css::uno::Reference< css::smarttags::XSmartTagAction > xFirstAction = rActionComponents[0];
+
+ if ( !xFirstAction.is() )
+ continue;
+
+ const sal_Int32 nSmartTagIndex = rActionIndices[0];
+ const OUString aSmartTagType = xFirstAction->getSmartTagName( nSmartTagIndex );
+ const OUString aSmartTagCaption = xFirstAction->getSmartTagCaption( nSmartTagIndex, rLocale );
+
+ // No sub-menus if there's only one smart tag type listed
+ css::uno::Reference<css::awt::XPopupMenu> xSubMenu = m_xPopupMenu;
+ if ( 1 < rActionComponentsSequence.getLength() )
+ {
+ m_xPopupMenu->insertItem(nMenuId, aSmartTagCaption, 0, -1);
+ xSubMenu.set(new VCLXPopupMenu);
+ m_xPopupMenu->setPopupMenu(nMenuId++, xSubMenu);
+ }
+
+ // Sub-menu starts with smart tag caption and separator
+ const OUString aSmartTagCaption2 = aSmartTagCaption + ": " + aRangeText;
+ xSubMenu->insertItem(nMenuId++, aSmartTagCaption2, static_cast<sal_Int16>(MenuItemBits::NOSELECT), -1);
+ xSubMenu->insertSeparator(-1);
+
+ // Add subitem for every action reference for the current smart tag type
+ for ( const auto& xAction : rActionComponents )
+ {
+ for ( sal_Int32 j = 0; j < xAction->getActionCount( aSmartTagType, xController, xSmartTagProperties ); ++j )
+ {
+ const sal_uInt32 nActionID = xAction->getActionID( aSmartTagType, j, xController );
+ OUString aActionCaption = xAction->getActionCaptionFromID( nActionID,
+ aApplicationName,
+ rLocale,
+ xSmartTagProperties,
+ aRangeText,
+ OUString(),
+ xController,
+ xTextRange );
+
+ xSubMenu->insertItem(nSubMenuId++, aActionCaption, 0, -1);
+ InvokeAction aEntry( xAction, xSmartTagProperties, nActionID );
+ m_aInvokeActions.push_back( aEntry );
+ }
+ }
+ }
+
+ sal_Int16 nItemCount = m_xPopupMenu->getItemCount();
+ if (nItemCount > 0)
+ {
+ static const OUStringLiteral aCommand = u".uno:AutoCorrectDlg?OpenSmartTag:bool=true";
+ m_xPopupMenu->insertSeparator(nItemCount++);
+ auto aProperties = vcl::CommandInfoProvider::GetCommandProperties(aCommand, m_aModuleName);
+ m_xPopupMenu->insertItem(nMenuId, vcl::CommandInfoProvider::GetPopupLabelForCommand(aProperties),
+ 0, nItemCount);
+ m_xPopupMenu->setCommand(nMenuId, aCommand);
+ }
+}
+
+void SmartTagMenuController::itemSelected(const css::awt::MenuEvent& rEvent)
+{
+ if (MenuSelect(rEvent.MenuId))
+ return;
+ svt::PopupMenuControllerBase::itemSelected(rEvent);
+}
+
+bool SmartTagMenuController::MenuSelect(sal_uInt16 nMyId)
+{
+ if ( !m_pSmartTagItem )
+ return false;
+
+ if ( nMyId < MN_ST_INSERT_START )
+ return false;
+
+ nMyId -= MN_ST_INSERT_START;
+
+ // Compute SmartTag lib index and action index
+ css::uno::Reference< css::smarttags::XSmartTagAction > xSmartTagAction = m_aInvokeActions[nMyId].m_xAction;
+
+ if (!xSmartTagAction.is())
+ return false;
+
+ // Execute action
+ xSmartTagAction->invokeAction( m_aInvokeActions[nMyId].m_nActionID,
+ m_pSmartTagItem->GetApplicationName(),
+ m_pSmartTagItem->GetController(),
+ m_pSmartTagItem->GetTextRange(),
+ m_aInvokeActions[nMyId].m_xSmartTagProperties,
+ m_pSmartTagItem->GetRangeText(),
+ OUString(),
+ m_pSmartTagItem->GetLocale() );
+
+ return true;
+}
+
+OUString SmartTagMenuController::getImplementationName()
+{
+ return "com.sun.star.comp.svx.SmartTagMenuController";
+}
+
+css::uno::Sequence< OUString > SmartTagMenuController::getSupportedServiceNames()
+{
+ return { "com.sun.star.frame.PopupMenuController" };
+}
+
+extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface *
+com_sun_star_comp_svx_SmartTagMenuController_get_implementation(
+ css::uno::XComponentContext* xContext,
+ css::uno::Sequence< css::uno::Any > const & )
+{
+ return cppu::acquire( new SmartTagMenuController( xContext ) );
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/sdr/animation/animationstate.cxx b/svx/source/sdr/animation/animationstate.cxx
new file mode 100644
index 000000000..074f2082e
--- /dev/null
+++ b/svx/source/sdr/animation/animationstate.cxx
@@ -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 .
+ */
+
+#include <svx/sdr/animation/animationstate.hxx>
+#include <svx/sdr/contact/viewobjectcontact.hxx>
+#include <svx/sdr/animation/objectanimator.hxx>
+#include <svx/sdr/contact/objectcontact.hxx>
+#include <drawinglayer/primitive2d/animatedprimitive2d.hxx>
+#include <drawinglayer/animation/animationtiming.hxx>
+#include <comphelper/lok.hxx>
+
+
+namespace sdr::animation
+{
+ double PrimitiveAnimation::getSmallestNextTime(double fCurrentTime)
+ {
+ double fRetval(0.0);
+
+ if(!maAnimatedPrimitives.empty())
+ {
+ const sal_Int32 nCount(maAnimatedPrimitives.size());
+
+ for(sal_Int32 a(0); a < nCount; a++)
+ {
+ const drawinglayer::primitive2d::Primitive2DReference xRef(maAnimatedPrimitives[a]);
+ const drawinglayer::primitive2d::AnimatedSwitchPrimitive2D* pCandidate = static_cast< const drawinglayer::primitive2d::AnimatedSwitchPrimitive2D* >(xRef.get());
+
+ const drawinglayer::animation::AnimationEntry& rAnimEntry = pCandidate->getAnimationEntry();
+ const double fNextTime(rAnimEntry.getNextEventTime(fCurrentTime));
+
+ if(!::basegfx::fTools::equalZero(fNextTime))
+ {
+ if(::basegfx::fTools::equalZero(fRetval))
+ {
+ fRetval = fNextTime;
+ }
+ else if(::basegfx::fTools::less(fNextTime, fRetval))
+ {
+ fRetval = fNextTime;
+ }
+ }
+ }
+ }
+
+ return fRetval;
+ }
+
+ void PrimitiveAnimation::prepareNextEvent()
+ {
+ const double fCurrentTime(mrVOContact.GetObjectContact().getPrimitiveAnimator().GetTime());
+ const double fNextTime(getSmallestNextTime(fCurrentTime));
+
+ // getSmallestNextTime will be zero when animation ended. If not zero, a next step
+ // exists
+ if(::basegfx::fTools::equalZero(fNextTime))
+ return;
+
+ // next time point exists, use it
+ sal_uInt32 nNextTime;
+
+ if(fNextTime >= double(0xffffff00))
+ {
+ // take care for very late points in time, e.g. when a text animation stops
+ // in a defined AnimationEntryFixed with endless (0xffffffff) duration
+ nNextTime = GetTime() + (1000 * 60 * 60); // one hour, works with vcl timers, 0xffffff00 was too much...
+ }
+ else
+ {
+ nNextTime = static_cast<sal_uInt32>(fNextTime);
+ }
+
+ // ensure step forward in integer timing, the floating step difference maybe smaller than 1.0. Use
+ // at least 25ms for next step
+ const sal_uInt32 nMinimumStepTime(static_cast<sal_uInt32>(fCurrentTime) + 25);
+
+ if(nNextTime <= nMinimumStepTime)
+ {
+ nNextTime = nMinimumStepTime;
+ }
+
+ // set time and reactivate by re-adding to the scheduler
+ SetTime(nNextTime);
+ mrVOContact.GetObjectContact().getPrimitiveAnimator().InsertEvent(*this);
+ }
+
+ PrimitiveAnimation::PrimitiveAnimation(sdr::contact::ViewObjectContact& rVOContact, drawinglayer::primitive2d::Primitive2DContainer&& rAnimatedPrimitives)
+ : mrVOContact(rVOContact),
+ maAnimatedPrimitives(std::move(rAnimatedPrimitives))
+ {
+ if (!comphelper::LibreOfficeKit::isActive())
+ // setup initially
+ prepareNextEvent();
+ }
+
+ PrimitiveAnimation::~PrimitiveAnimation()
+ {
+ // ensure that Event member is removed from PrimitiveAnimator
+ mrVOContact.GetObjectContact().getPrimitiveAnimator().RemoveEvent(this);
+ }
+
+ // execute event, from base class Event
+ void PrimitiveAnimation::Trigger(sal_uInt32 /*nTime*/)
+ {
+ // schedule a repaint of associated object
+ mrVOContact.ActionChanged();
+
+ if (!comphelper::LibreOfficeKit::isActive())
+ // re-setup
+ prepareNextEvent();
+ }
+
+} // end of namespace
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/sdr/animation/objectanimator.cxx b/svx/source/sdr/animation/objectanimator.cxx
new file mode 100644
index 000000000..aad545fd7
--- /dev/null
+++ b/svx/source/sdr/animation/objectanimator.cxx
@@ -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 .
+ */
+
+#include <svx/sdr/animation/objectanimator.hxx>
+
+
+namespace sdr::animation
+{
+ primitiveAnimator::primitiveAnimator()
+ {
+ }
+
+ primitiveAnimator::~primitiveAnimator()
+ {
+ }
+
+} // end of namespace
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/sdr/animation/scheduler.cxx b/svx/source/sdr/animation/scheduler.cxx
new file mode 100644
index 000000000..d4451c099
--- /dev/null
+++ b/svx/source/sdr/animation/scheduler.cxx
@@ -0,0 +1,173 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <svx/sdr/animation/scheduler.hxx>
+
+#include <algorithm>
+#include <vector>
+
+
+// event class
+
+namespace sdr::animation
+{
+ Event::Event() : mnTime(0)
+ {
+ }
+
+ Event::~Event()
+ {
+ }
+
+
+ void Event::SetTime(sal_uInt32 nNew)
+ {
+ if(mnTime != nNew)
+ {
+ mnTime = nNew;
+ }
+ }
+
+ Scheduler::Scheduler()
+ : Timer("sdr::animation::Scheduler"),
+ mnTime(0),
+ mnDeltaTime(0),
+ mbIsPaused(false)
+ {
+ SetPriority(TaskPriority::POST_PAINT);
+ }
+
+ Scheduler::~Scheduler()
+ {
+ Stop();
+ }
+
+ void Scheduler::Invoke()
+ {
+ // stop timer and add time
+ Stop();
+ mnTime += mnDeltaTime;
+
+ // execute events
+ triggerEvents();
+
+ // re-start or stop timer according to event list
+ checkTimeout();
+ }
+
+ void Scheduler::triggerEvents()
+ {
+ if (mvEvents.empty())
+ return;
+
+ // copy events which need to be executed to a vector. Remove them from
+ // the scheduler
+ ::std::vector< Event* > aToBeExecutedList;
+
+ while(!mvEvents.empty() && mvEvents.front()->GetTime() <= mnTime)
+ {
+ Event* pNextEvent = mvEvents.front();
+ mvEvents.erase(mvEvents.begin());
+ aToBeExecutedList.push_back(pNextEvent);
+ }
+
+ // execute events from the vector
+ for(auto& rpCandidate : aToBeExecutedList)
+ {
+ // trigger event. This may re-insert the event to the scheduler again
+ rpCandidate->Trigger(mnTime);
+ }
+ }
+
+ void Scheduler::checkTimeout()
+ {
+ // re-start or stop timer according to event list
+ if(!IsPaused() && !mvEvents.empty())
+ {
+ mnDeltaTime = mvEvents.front()->GetTime() - mnTime;
+
+ if(0 != mnDeltaTime)
+ {
+ SetTimeout(mnDeltaTime);
+ Start();
+ }
+ }
+ else
+ {
+ Stop();
+ }
+ }
+
+
+ // #i38135#
+ void Scheduler::SetTime(sal_uInt32 nTime)
+ {
+ // reset time
+ Stop();
+ mnTime = nTime;
+
+ if (mvEvents.empty())
+ return;
+
+ // reset event time points
+ for (auto & rEvent : mvEvents)
+ {
+ rEvent->SetTime(nTime);
+ }
+
+ if(!IsPaused())
+ {
+ // without delta time, init events by triggering them. This will invalidate
+ // painted objects and add them to the scheduler again
+ mnDeltaTime = 0;
+ triggerEvents();
+ checkTimeout();
+ }
+ }
+
+ void Scheduler::InsertEvent(Event& rNew)
+ {
+ // insert maintaining time ordering
+ auto it = std::find_if(mvEvents.begin(), mvEvents.end(),
+ [&rNew](const Event* pEvent) { return rNew.GetTime() < pEvent->GetTime(); });
+ mvEvents.insert(it, &rNew);
+ checkTimeout();
+ }
+
+ void Scheduler::RemoveEvent(Event* pOld)
+ {
+ if(!mvEvents.empty())
+ {
+ mvEvents.erase(std::remove(mvEvents.begin(), mvEvents.end(), pOld), mvEvents.end());
+ checkTimeout();
+ }
+ }
+
+ void Scheduler::SetPaused(bool bNew)
+ {
+ if(bNew != mbIsPaused)
+ {
+ mbIsPaused = bNew;
+ checkTimeout();
+ }
+ }
+
+} // end of namespace
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/sdr/attribute/sdrallfillattributeshelper.cxx b/svx/source/sdr/attribute/sdrallfillattributeshelper.cxx
new file mode 100644
index 000000000..261eef295
--- /dev/null
+++ b/svx/source/sdr/attribute/sdrallfillattributeshelper.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 <svx/sdr/attribute/sdrallfillattributeshelper.hxx>
+#include <sdr/primitive2d/sdrattributecreator.hxx>
+#include <sdr/primitive2d/sdrdecompositiontools.hxx>
+#include <basegfx/polygon/b2dpolygontools.hxx>
+#include <basegfx/polygon/b2dpolygon.hxx>
+#include <drawinglayer/attribute/fillhatchattribute.hxx>
+#include <drawinglayer/attribute/sdrfillgraphicattribute.hxx>
+#include <vcl/graph.hxx>
+
+//////////////////////////////////////////////////////////////////////////////
+
+namespace drawinglayer::attribute
+{
+ void SdrAllFillAttributesHelper::createPrimitive2DSequence(
+ const basegfx::B2DRange& rPaintRange,
+ const basegfx::B2DRange& rDefineRange)
+ {
+ // reset and remember new target range for object geometry
+ maLastPaintRange = rPaintRange;
+ maLastDefineRange = rDefineRange;
+
+ if(isUsed())
+ {
+ maPrimitives.resize(1);
+ maPrimitives[0] = drawinglayer::primitive2d::createPolyPolygonFillPrimitive(
+ basegfx::B2DPolyPolygon(
+ basegfx::utils::createPolygonFromRect(
+ maLastPaintRange)),
+ maLastDefineRange,
+ maFillAttribute ? *maFillAttribute : drawinglayer::attribute::SdrFillAttribute(),
+ maFillGradientAttribute ? *maFillGradientAttribute : drawinglayer::attribute::FillGradientAttribute());
+ }
+ }
+
+ SdrAllFillAttributesHelper::SdrAllFillAttributesHelper(const Color& rColor)
+ {
+ maFillAttribute = drawinglayer::attribute::SdrFillAttribute(
+ 0.0,
+ rColor.GetRGBColor().getBColor(),
+ drawinglayer::attribute::FillGradientAttribute(),
+ drawinglayer::attribute::FillHatchAttribute(),
+ drawinglayer::attribute::SdrFillGraphicAttribute());
+ }
+
+ SdrAllFillAttributesHelper::SdrAllFillAttributesHelper(const SfxItemSet& rSet)
+ : maFillAttribute(
+ drawinglayer::primitive2d::createNewSdrFillAttribute(rSet)),
+ maFillGradientAttribute(
+ drawinglayer::primitive2d::createNewTransparenceGradientAttribute(rSet))
+ {
+ }
+
+ SdrAllFillAttributesHelper::~SdrAllFillAttributesHelper()
+ {
+ }
+
+ bool SdrAllFillAttributesHelper::isUsed() const
+ {
+ // only depends on fill, FillGradientAttribute alone defines no fill
+ return maFillAttribute && !maFillAttribute->isDefault();
+ }
+
+ bool SdrAllFillAttributesHelper::isTransparent() const
+ {
+ if(hasSdrFillAttribute() && 0.0 != maFillAttribute->getTransparence())
+ {
+ return true;
+ }
+
+ if(maFillGradientAttribute && !maFillGradientAttribute->isDefault())
+ {
+ return true;
+ }
+
+ if(hasSdrFillAttribute())
+ {
+ const Graphic& rGraphic = getFillAttribute().getFillGraphic().getFillGraphic();
+
+ return rGraphic.IsSupportedGraphic() && rGraphic.IsTransparent();
+ }
+
+ return false;
+ }
+
+ const drawinglayer::attribute::SdrFillAttribute& SdrAllFillAttributesHelper::getFillAttribute() const
+ {
+ if(!maFillAttribute)
+ {
+ const_cast< SdrAllFillAttributesHelper* >(this)->maFillAttribute.emplace();
+ }
+
+ return *maFillAttribute;
+ }
+
+ const drawinglayer::attribute::FillGradientAttribute& SdrAllFillAttributesHelper::getFillGradientAttribute() const
+ {
+ if(!maFillGradientAttribute)
+ {
+ const_cast< SdrAllFillAttributesHelper* >(this)->maFillGradientAttribute.emplace();
+ }
+
+ return *maFillGradientAttribute;
+ }
+
+ const drawinglayer::primitive2d::Primitive2DContainer& SdrAllFillAttributesHelper::getPrimitive2DSequence(
+ const basegfx::B2DRange& rPaintRange,
+ const basegfx::B2DRange& rDefineRange) const
+ {
+ if(!maPrimitives.empty() && (maLastPaintRange != rPaintRange || maLastDefineRange != rDefineRange))
+ {
+ const_cast< SdrAllFillAttributesHelper* >(this)->maPrimitives.clear();
+ }
+
+ if(maPrimitives.empty())
+ {
+ const_cast< SdrAllFillAttributesHelper* >(this)->createPrimitive2DSequence(rPaintRange, rDefineRange);
+ }
+
+ return maPrimitives;
+ }
+
+ basegfx::BColor SdrAllFillAttributesHelper::getAverageColor(const basegfx::BColor& rFallback) const
+ {
+ basegfx::BColor aRetval(rFallback);
+
+ if(maFillAttribute && !maFillAttribute->isDefault())
+ {
+ const drawinglayer::attribute::FillGradientAttribute& rFillGradientAttribute = maFillAttribute->getGradient();
+ const drawinglayer::attribute::FillHatchAttribute& rFillHatchAttribute = maFillAttribute->getHatch();
+ const drawinglayer::attribute::SdrFillGraphicAttribute& rSdrFillGraphicAttribute = maFillAttribute->getFillGraphic();
+ const drawinglayer::attribute::FillGradientAttribute& rFillTransparenceGradientAttribute = getFillGradientAttribute();
+ double fTransparence(maFillAttribute->getTransparence());
+
+ if(!rFillTransparenceGradientAttribute.isDefault())
+ {
+ const double fTransA = rFillTransparenceGradientAttribute.getStartColor().luminance();
+ const double fTransB = rFillTransparenceGradientAttribute.getEndColor().luminance();
+
+ fTransparence = (fTransA + fTransB) * 0.5;
+ }
+
+ if(!rFillGradientAttribute.isDefault())
+ {
+ // gradient fill
+ const basegfx::BColor& rStart = rFillGradientAttribute.getStartColor();
+ const basegfx::BColor& rEnd = rFillGradientAttribute.getEndColor();
+
+ aRetval = basegfx::interpolate(rStart, rEnd, 0.5);
+ }
+ else if(!rFillHatchAttribute.isDefault())
+ {
+ // hatch fill
+ const basegfx::BColor& rColor = rFillHatchAttribute.getColor();
+
+ if(rFillHatchAttribute.isFillBackground())
+ {
+ const basegfx::BColor& rBackgroundColor = maFillAttribute->getColor();
+
+ // mix colors 50%/50%
+ aRetval = basegfx::interpolate(rColor, rBackgroundColor, 0.5);
+ }
+ else
+ {
+ // mix color with fallback color
+ aRetval = basegfx::interpolate(rColor, rFallback, 0.5);
+ }
+ }
+ else if(!rSdrFillGraphicAttribute.isDefault())
+ {
+ // graphic fill
+
+ // not used yet by purpose (see SwPageFrm::GetDrawBackgrdColor()),
+ // use fallback (already set)
+ }
+ else
+ {
+ // color fill
+ aRetval = maFillAttribute->getColor();
+ }
+
+ if(!basegfx::fTools::equalZero(fTransparence))
+ {
+ // blend into transparency
+ aRetval = basegfx::interpolate(aRetval, rFallback, fTransparence);
+ }
+ }
+
+ return aRetval.clamp();
+ }
+
+ bool SdrAllFillAttributesHelper::needCompleteRepaint() const
+ {
+ if(!isUsed() || !hasSdrFillAttribute())
+ {
+ // not used or no fill
+ return false;
+ }
+
+ const drawinglayer::attribute::SdrFillAttribute& rSdrFillAttribute = getFillAttribute();
+
+ if(!rSdrFillAttribute.getHatch().isDefault())
+ {
+ // hatch is always top-left aligned, needs no full refreshes
+ return false;
+ }
+
+ if(!rSdrFillAttribute.getGradient().isDefault())
+ {
+ // gradients always scale with the object
+ return true;
+ }
+
+ if(!rSdrFillAttribute.getFillGraphic().isDefault())
+ {
+ // some graphic constellations may not need this, but since most do
+ // (stretch to fill, all but top-left aligned, ...) claim to do by default
+ return true;
+ }
+
+ // color fill
+ return false;
+ }
+
+} // end of namespace
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/sdr/attribute/sdreffectstextattribute.cxx b/svx/source/sdr/attribute/sdreffectstextattribute.cxx
new file mode 100644
index 000000000..3287b8c8e
--- /dev/null
+++ b/svx/source/sdr/attribute/sdreffectstextattribute.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 <sdr/attribute/sdreffectstextattribute.hxx>
+
+
+namespace drawinglayer::attribute
+{
+ SdrEffectsTextAttribute::SdrEffectsTextAttribute(
+ const SdrShadowAttribute& rShadow,
+ const SdrTextAttribute& rTextAttribute,
+ const SdrGlowAttribute& rGlow,
+ sal_Int32 nSoftEdgeRadius)
+ : maShadow(rShadow),
+ maTextAttribute(rTextAttribute),
+ maGlow(rGlow),
+ mnSoftEdgeRadius(nSoftEdgeRadius)
+ {
+ }
+
+ SdrEffectsTextAttribute::SdrEffectsTextAttribute()
+ {
+ }
+
+ SdrEffectsTextAttribute::SdrEffectsTextAttribute(const SdrEffectsTextAttribute& rCandidate)
+ : maShadow(rCandidate.getShadow()),
+ maTextAttribute(rCandidate.getText()),
+ maGlow(rCandidate.maGlow),
+ mnSoftEdgeRadius(rCandidate.mnSoftEdgeRadius)
+ {
+ }
+
+ SdrEffectsTextAttribute& SdrEffectsTextAttribute::operator=(const SdrEffectsTextAttribute& rCandidate)
+ {
+ maShadow = rCandidate.getShadow();
+ maTextAttribute = rCandidate.getText();
+ maGlow = rCandidate.maGlow;
+ mnSoftEdgeRadius = rCandidate.mnSoftEdgeRadius;
+
+ return *this;
+ }
+
+ bool SdrEffectsTextAttribute::isDefault() const
+ {
+ return (getShadow().isDefault()
+ && getText().isDefault() && maGlow.isDefault() && getSoftEdgeRadius() == 0);
+ }
+
+ bool SdrEffectsTextAttribute::operator==(const SdrEffectsTextAttribute& rCandidate) const
+ {
+ return (getShadow() == rCandidate.getShadow()
+ && getText() == rCandidate.getText()
+ && getGlow() == rCandidate.getGlow()
+ && getSoftEdgeRadius() == rCandidate.getSoftEdgeRadius());
+ }
+
+} // end of namespace
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/sdr/attribute/sdrfilltextattribute.cxx b/svx/source/sdr/attribute/sdrfilltextattribute.cxx
new file mode 100644
index 000000000..01593c982
--- /dev/null
+++ b/svx/source/sdr/attribute/sdrfilltextattribute.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 <sdr/attribute/sdrfilltextattribute.hxx>
+
+
+namespace drawinglayer::attribute
+{
+ SdrFillTextAttribute::SdrFillTextAttribute(
+ const SdrFillAttribute& rFill,
+ const FillGradientAttribute& rFillFloatTransGradient,
+ const SdrTextAttribute& rTextAttribute)
+ : maFill(rFill),
+ maFillFloatTransGradient(rFillFloatTransGradient),
+ maTextAttribute(rTextAttribute)
+ {
+ }
+
+ SdrFillTextAttribute::SdrFillTextAttribute()
+ {
+ }
+
+ SdrFillTextAttribute::SdrFillTextAttribute(const SdrFillTextAttribute& rCandidate)
+ : maFill(rCandidate.getFill()),
+ maFillFloatTransGradient(rCandidate.getFillFloatTransGradient()),
+ maTextAttribute(rCandidate.getText())
+ {
+ }
+
+ SdrFillTextAttribute& SdrFillTextAttribute::operator=(const SdrFillTextAttribute& rCandidate)
+ {
+ maFill = rCandidate.getFill();
+ maFillFloatTransGradient = rCandidate.getFillFloatTransGradient();
+ maTextAttribute = rCandidate.getText();
+
+ return *this;
+ }
+
+ bool SdrFillTextAttribute::operator==(const SdrFillTextAttribute& rCandidate) const
+ {
+ return(getFill() == rCandidate.getFill()
+ && getFillFloatTransGradient() == rCandidate.getFillFloatTransGradient()
+ && getText() == rCandidate.getText());
+ }
+
+} // end of namespace
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/sdr/attribute/sdrformtextattribute.cxx b/svx/source/sdr/attribute/sdrformtextattribute.cxx
new file mode 100644
index 000000000..69fff8a8d
--- /dev/null
+++ b/svx/source/sdr/attribute/sdrformtextattribute.cxx
@@ -0,0 +1,372 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <sdr/attribute/sdrformtextattribute.hxx>
+#include <basegfx/vector/b2enums.hxx>
+#include <svl/itemset.hxx>
+#include <svx/sdprcitm.hxx>
+#include <svx/svddef.hxx>
+#include <svx/xftdiit.hxx>
+#include <svx/xftstit.hxx>
+#include <svx/xftshxy.hxx>
+#include <xftshtit.hxx>
+#include <svx/xtextit0.hxx>
+#include <svx/xftadit.hxx>
+#include <svx/xftshit.hxx>
+#include <svx/xftshcit.hxx>
+#include <svx/xftmrit.hxx>
+#include <svx/xftouit.hxx>
+#include <svx/xlntrit.hxx>
+#include <svx/xlnclit.hxx>
+#include <svx/xlnwtit.hxx>
+#include <svx/xlinjoit.hxx>
+#include <svx/xlncapit.hxx>
+#include <svx/xlineit0.hxx>
+#include <svx/xdash.hxx>
+#include <svx/xlndsit.hxx>
+#include <drawinglayer/attribute/lineattribute.hxx>
+#include <drawinglayer/attribute/strokeattribute.hxx>
+#include <sdr/attribute/sdrformtextoutlineattribute.hxx>
+#include <com/sun/star/drawing/LineCap.hpp>
+#include <com/sun/star/drawing/LineStyle.hpp>
+
+
+// helper to get line, stroke and transparence attributes from SfxItemSet
+
+namespace
+{
+ basegfx::B2DLineJoin impGetB2DLineJoin(css::drawing::LineJoint eLineJoint)
+ {
+ switch(eLineJoint)
+ {
+ case css::drawing::LineJoint_BEVEL :
+ {
+ return basegfx::B2DLineJoin::Bevel;
+ }
+ case css::drawing::LineJoint_MIDDLE :
+ case css::drawing::LineJoint_MITER :
+ {
+ return basegfx::B2DLineJoin::Miter;
+ }
+ case css::drawing::LineJoint_ROUND :
+ {
+ return basegfx::B2DLineJoin::Round;
+ }
+ default : // css::drawing::LineJoint_NONE
+ {
+ return basegfx::B2DLineJoin::NONE; // XLINEJOINT_NONE
+ }
+ }
+ }
+
+ sal_uInt8 impGetStrokeTransparence(bool bShadow, const SfxItemSet& rSet)
+ {
+ sal_uInt8 nRetval;
+
+ if(bShadow)
+ {
+ nRetval = static_cast<sal_uInt8>((rSet.Get(SDRATTR_SHADOWTRANSPARENCE).GetValue() * 255) / 100);
+ }
+ else
+ {
+ nRetval = static_cast<sal_uInt8>((rSet.Get(XATTR_LINETRANSPARENCE).GetValue() * 255) / 100);
+ }
+
+ return nRetval;
+ }
+
+ drawinglayer::attribute::LineAttribute impGetLineAttribute(bool bShadow, const SfxItemSet& rSet)
+ {
+ basegfx::BColor aColorAttribute;
+
+ if(bShadow)
+ {
+ const Color aShadowColor(rSet.Get(SDRATTR_SHADOWCOLOR).GetColorValue());
+ aColorAttribute = aShadowColor.getBColor();
+ }
+ else
+ {
+ const Color aLineColor(rSet.Get(XATTR_LINECOLOR).GetColorValue());
+ aColorAttribute = aLineColor.getBColor();
+ }
+
+ const sal_uInt32 nLineWidth = rSet.Get(XATTR_LINEWIDTH).GetValue();
+ const css::drawing::LineJoint eLineJoint = rSet.Get(XATTR_LINEJOINT).GetValue();
+ const css::drawing::LineCap eLineCap = rSet.Get(XATTR_LINECAP).GetValue();
+
+ return drawinglayer::attribute::LineAttribute(
+ aColorAttribute,
+ static_cast<double>(nLineWidth),
+ impGetB2DLineJoin(eLineJoint),
+ eLineCap);
+ }
+
+ drawinglayer::attribute::StrokeAttribute impGetStrokeAttribute(const SfxItemSet& rSet)
+ {
+ const css::drawing::LineStyle eLineStyle = rSet.Get(XATTR_LINESTYLE).GetValue();
+ double fFullDotDashLen(0.0);
+ ::std::vector< double > aDotDashArray;
+
+ if(css::drawing::LineStyle_DASH == eLineStyle)
+ {
+ const XDash& rDash = rSet.Get(XATTR_LINEDASH).GetDashValue();
+
+ if(rDash.GetDots() || rDash.GetDashes())
+ {
+ const sal_uInt32 nLineWidth = rSet.Get(XATTR_LINEWIDTH).GetValue();
+ fFullDotDashLen = rDash.CreateDotDashArray(aDotDashArray, static_cast<double>(nLineWidth));
+ }
+ }
+
+ return drawinglayer::attribute::StrokeAttribute(std::move(aDotDashArray), fFullDotDashLen);
+ }
+} // end of anonymous namespace
+
+
+namespace drawinglayer::attribute
+{
+ class ImpSdrFormTextAttribute
+ {
+ public:
+ // FormText (FontWork) Attributes
+ sal_Int32 mnFormTextDistance; // distance from line in upright direction
+ sal_Int32 mnFormTextStart; // shift from polygon start
+ sal_Int32 mnFormTextShdwXVal; // shadow distance or 10th degrees
+ sal_Int32 mnFormTextShdwYVal; // shadow distance or scaling
+ sal_uInt16 mnFormTextShdwTransp; // shadow transparence
+ XFormTextStyle meFormTextStyle; // on/off and char orientation
+ XFormTextAdjust meFormTextAdjust; // adjustment (left/right/center) and scale
+ XFormTextShadow meFormTextShadow; // shadow mode
+ Color maFormTextShdwColor; // shadow color
+
+ // outline attributes; used when getFormTextOutline() is true and (for
+ // shadow) when getFormTextShadow() != XFormTextShadow::NONE
+ SdrFormTextOutlineAttribute maOutline;
+ SdrFormTextOutlineAttribute maShadowOutline;
+
+ bool mbFormTextMirror : 1; // change orientation
+ bool mbFormTextOutline : 1; // show contour of objects
+
+ explicit ImpSdrFormTextAttribute(const SfxItemSet& rSet)
+ : mnFormTextDistance(rSet.Get(XATTR_FORMTXTDISTANCE).GetValue()),
+ mnFormTextStart(rSet.Get(XATTR_FORMTXTSTART).GetValue()),
+ mnFormTextShdwXVal(rSet.Get(XATTR_FORMTXTSHDWXVAL).GetValue()),
+ mnFormTextShdwYVal(rSet.Get(XATTR_FORMTXTSHDWYVAL).GetValue()),
+ mnFormTextShdwTransp(rSet.Get(XATTR_FORMTXTSHDWTRANSP).GetValue()),
+ meFormTextStyle(rSet.Get(XATTR_FORMTXTSTYLE).GetValue()),
+ meFormTextAdjust(rSet.Get(XATTR_FORMTXTADJUST).GetValue()),
+ meFormTextShadow(rSet.Get(XATTR_FORMTXTSHADOW).GetValue()),
+ maFormTextShdwColor(rSet.Get(XATTR_FORMTXTSHDWCOLOR).GetColorValue()),
+ mbFormTextMirror(rSet.Get(XATTR_FORMTXTMIRROR).GetValue()),
+ mbFormTextOutline(rSet.Get(XATTR_FORMTXTOUTLINE).GetValue())
+ {
+ if(!getFormTextOutline())
+ return;
+
+ const StrokeAttribute aStrokeAttribute(impGetStrokeAttribute(rSet));
+
+ // also need to prepare attributes for outlines
+ {
+ const LineAttribute aLineAttribute(impGetLineAttribute(false, rSet));
+ const sal_uInt8 nTransparence(impGetStrokeTransparence(false, rSet));
+
+ maOutline = SdrFormTextOutlineAttribute(
+ aLineAttribute, aStrokeAttribute, nTransparence);
+ }
+
+ if(XFormTextShadow::NONE != getFormTextShadow())
+ {
+ // also need to prepare attributes for shadow outlines
+ const LineAttribute aLineAttribute(impGetLineAttribute(true, rSet));
+ const sal_uInt8 nTransparence(impGetStrokeTransparence(true, rSet));
+
+ maShadowOutline = SdrFormTextOutlineAttribute(
+ aLineAttribute, aStrokeAttribute, nTransparence);
+ }
+ }
+
+ ImpSdrFormTextAttribute()
+ : mnFormTextDistance(0),
+ mnFormTextStart(0),
+ mnFormTextShdwXVal(0),
+ mnFormTextShdwYVal(0),
+ mnFormTextShdwTransp(0),
+ meFormTextStyle(XFormTextStyle::NONE),
+ meFormTextAdjust(XFormTextAdjust::Center),
+ meFormTextShadow(XFormTextShadow::NONE),
+ mbFormTextMirror(false),
+ mbFormTextOutline(false)
+ {
+ }
+
+ // data read access
+ sal_Int32 getFormTextDistance() const { return mnFormTextDistance; }
+ sal_Int32 getFormTextStart() const { return mnFormTextStart; }
+ sal_Int32 getFormTextShdwXVal() const { return mnFormTextShdwXVal; }
+ sal_Int32 getFormTextShdwYVal() const { return mnFormTextShdwYVal; }
+ XFormTextStyle getFormTextStyle() const { return meFormTextStyle; }
+ XFormTextAdjust getFormTextAdjust() const { return meFormTextAdjust; }
+ XFormTextShadow getFormTextShadow() const { return meFormTextShadow; }
+ const Color& getFormTextShdwColor() const { return maFormTextShdwColor; }
+ const SdrFormTextOutlineAttribute& getOutline() const { return maOutline; }
+ const SdrFormTextOutlineAttribute& getShadowOutline() const { return maShadowOutline; }
+ bool getFormTextMirror() const { return mbFormTextMirror; }
+ bool getFormTextOutline() const { return mbFormTextOutline; }
+
+ // compare operator
+ bool operator==(const ImpSdrFormTextAttribute& rCandidate) const
+ {
+ return (getFormTextDistance() == rCandidate.getFormTextDistance()
+ && getFormTextStart() == rCandidate.getFormTextStart()
+ && getFormTextShdwXVal() == rCandidate.getFormTextShdwXVal()
+ && getFormTextShdwYVal() == rCandidate.getFormTextShdwYVal()
+ && mnFormTextShdwTransp == rCandidate.mnFormTextShdwTransp
+ && getFormTextStyle() == rCandidate.getFormTextStyle()
+ && getFormTextAdjust() == rCandidate.getFormTextAdjust()
+ && getFormTextShadow() == rCandidate.getFormTextShadow()
+ && getFormTextShdwColor() == rCandidate.getFormTextShdwColor()
+ && getOutline() == rCandidate.getOutline()
+ && getShadowOutline() == rCandidate.getShadowOutline()
+ && getFormTextMirror() == rCandidate.getFormTextMirror()
+ && getFormTextOutline() == rCandidate.getFormTextOutline());
+ }
+ };
+
+ namespace
+ {
+ SdrFormTextAttribute::ImplType& theGlobalDefault()
+ {
+ static SdrFormTextAttribute::ImplType SINGLETON;
+ return SINGLETON;
+ }
+ }
+
+ SdrFormTextAttribute::SdrFormTextAttribute(const SfxItemSet& rSet)
+ : mpSdrFormTextAttribute(ImpSdrFormTextAttribute(rSet))
+ {
+ }
+
+ SdrFormTextAttribute::SdrFormTextAttribute()
+ : mpSdrFormTextAttribute(theGlobalDefault())
+ {
+ }
+
+ SdrFormTextAttribute::SdrFormTextAttribute(const SdrFormTextAttribute& rCandidate)
+ : mpSdrFormTextAttribute(rCandidate.mpSdrFormTextAttribute)
+ {
+ }
+
+ SdrFormTextAttribute::SdrFormTextAttribute(SdrFormTextAttribute&& rCandidate) noexcept
+ : mpSdrFormTextAttribute(std::move(rCandidate.mpSdrFormTextAttribute))
+ {
+ }
+
+ SdrFormTextAttribute::~SdrFormTextAttribute()
+ {
+ }
+
+ bool SdrFormTextAttribute::isDefault() const
+ {
+ return mpSdrFormTextAttribute.same_object(theGlobalDefault());
+ }
+
+ SdrFormTextAttribute& SdrFormTextAttribute::operator=(const SdrFormTextAttribute& rCandidate)
+ {
+ mpSdrFormTextAttribute = rCandidate.mpSdrFormTextAttribute;
+ return *this;
+ }
+
+ SdrFormTextAttribute& SdrFormTextAttribute::operator=(SdrFormTextAttribute&& rCandidate) noexcept
+ {
+ mpSdrFormTextAttribute = std::move(rCandidate.mpSdrFormTextAttribute);
+ return *this;
+ }
+
+ bool SdrFormTextAttribute::operator==(const SdrFormTextAttribute& rCandidate) const
+ {
+ // tdf#87509 default attr is always != non-default attr, even with same values
+ if(rCandidate.isDefault() != isDefault())
+ return false;
+
+ return rCandidate.mpSdrFormTextAttribute == mpSdrFormTextAttribute;
+ }
+
+ sal_Int32 SdrFormTextAttribute::getFormTextDistance() const
+ {
+ return mpSdrFormTextAttribute->getFormTextDistance();
+ }
+
+ sal_Int32 SdrFormTextAttribute::getFormTextStart() const
+ {
+ return mpSdrFormTextAttribute->getFormTextStart();
+ }
+
+ sal_Int32 SdrFormTextAttribute::getFormTextShdwXVal() const
+ {
+ return mpSdrFormTextAttribute->getFormTextShdwXVal();
+ }
+
+ sal_Int32 SdrFormTextAttribute::getFormTextShdwYVal() const
+ {
+ return mpSdrFormTextAttribute->getFormTextShdwYVal();
+ }
+
+ XFormTextStyle SdrFormTextAttribute::getFormTextStyle() const
+ {
+ return mpSdrFormTextAttribute->getFormTextStyle();
+ }
+
+ XFormTextAdjust SdrFormTextAttribute::getFormTextAdjust() const
+ {
+ return mpSdrFormTextAttribute->getFormTextAdjust();
+ }
+
+ XFormTextShadow SdrFormTextAttribute::getFormTextShadow() const
+ {
+ return mpSdrFormTextAttribute->getFormTextShadow();
+ }
+
+ Color const & SdrFormTextAttribute::getFormTextShdwColor() const
+ {
+ return mpSdrFormTextAttribute->getFormTextShdwColor();
+ }
+
+ const SdrFormTextOutlineAttribute& SdrFormTextAttribute::getOutline() const
+ {
+ return mpSdrFormTextAttribute->getOutline();
+ }
+
+ const SdrFormTextOutlineAttribute& SdrFormTextAttribute::getShadowOutline() const
+ {
+ return mpSdrFormTextAttribute->getShadowOutline();
+ }
+
+ bool SdrFormTextAttribute::getFormTextMirror() const
+ {
+ return mpSdrFormTextAttribute->getFormTextMirror();
+ }
+
+ bool SdrFormTextAttribute::getFormTextOutline() const
+ {
+ return mpSdrFormTextAttribute->getFormTextOutline();
+ }
+
+} // end of namespace
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/sdr/attribute/sdrformtextoutlineattribute.cxx b/svx/source/sdr/attribute/sdrformtextoutlineattribute.cxx
new file mode 100644
index 000000000..ec97cf044
--- /dev/null
+++ b/svx/source/sdr/attribute/sdrformtextoutlineattribute.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 <sdr/attribute/sdrformtextoutlineattribute.hxx>
+#include <drawinglayer/attribute/lineattribute.hxx>
+#include <drawinglayer/attribute/strokeattribute.hxx>
+
+
+namespace drawinglayer::attribute
+{
+ class ImpSdrFormTextOutlineAttribute
+ {
+ public:
+ // one set of attributes for FormText (FontWork) outline visualisation
+ LineAttribute maLineAttribute;
+ StrokeAttribute maStrokeAttribute;
+ sal_uInt8 mnTransparence;
+
+ ImpSdrFormTextOutlineAttribute(
+ const LineAttribute& rLineAttribute,
+ const StrokeAttribute& rStrokeAttribute,
+ sal_uInt8 nTransparence)
+ : maLineAttribute(rLineAttribute),
+ maStrokeAttribute(rStrokeAttribute),
+ mnTransparence(nTransparence)
+ {
+ }
+
+ ImpSdrFormTextOutlineAttribute()
+ : mnTransparence(0)
+ {
+ }
+
+ // data read access
+ const LineAttribute& getLineAttribute() const { return maLineAttribute; }
+ const StrokeAttribute& getStrokeAttribute() const { return maStrokeAttribute; }
+ sal_uInt8 getTransparence() const { return mnTransparence; }
+
+ // compare operator
+ bool operator==(const ImpSdrFormTextOutlineAttribute& rCandidate) const
+ {
+ return (getLineAttribute() == rCandidate.getLineAttribute()
+ && getStrokeAttribute() == rCandidate.getStrokeAttribute()
+ && getTransparence() == rCandidate.getTransparence());
+ }
+ };
+
+ namespace
+ {
+ SdrFormTextOutlineAttribute::ImplType& theGlobalDefault()
+ {
+ static SdrFormTextOutlineAttribute::ImplType SINGLETON;
+ return SINGLETON;
+ }
+ }
+
+ SdrFormTextOutlineAttribute::SdrFormTextOutlineAttribute(
+ const LineAttribute& rLineAttribute,
+ const StrokeAttribute& rStrokeAttribute,
+ sal_uInt8 nTransparence)
+ : mpSdrFormTextOutlineAttribute(
+ ImpSdrFormTextOutlineAttribute(
+ rLineAttribute, rStrokeAttribute, nTransparence))
+ {
+ }
+
+ SdrFormTextOutlineAttribute::SdrFormTextOutlineAttribute()
+ : mpSdrFormTextOutlineAttribute(theGlobalDefault())
+ {
+ }
+
+ SdrFormTextOutlineAttribute::SdrFormTextOutlineAttribute(const SdrFormTextOutlineAttribute& rCandidate)
+ : mpSdrFormTextOutlineAttribute(rCandidate.mpSdrFormTextOutlineAttribute)
+ {
+ }
+
+ SdrFormTextOutlineAttribute::~SdrFormTextOutlineAttribute()
+ {
+ }
+
+ bool SdrFormTextOutlineAttribute::isDefault() const
+ {
+ return mpSdrFormTextOutlineAttribute.same_object(theGlobalDefault());
+ }
+
+ SdrFormTextOutlineAttribute& SdrFormTextOutlineAttribute::operator=(const SdrFormTextOutlineAttribute& rCandidate)
+ {
+ mpSdrFormTextOutlineAttribute = rCandidate.mpSdrFormTextOutlineAttribute;
+ return *this;
+ }
+
+ SdrFormTextOutlineAttribute& SdrFormTextOutlineAttribute::operator=(SdrFormTextOutlineAttribute&& rCandidate) noexcept
+ {
+ mpSdrFormTextOutlineAttribute = std::move(rCandidate.mpSdrFormTextOutlineAttribute);
+ return *this;
+ }
+
+ bool SdrFormTextOutlineAttribute::operator==(const SdrFormTextOutlineAttribute& rCandidate) const
+ {
+ // tdf#87509 default attr is always != non-default attr, even with same values
+ if(rCandidate.isDefault() != isDefault())
+ return false;
+
+ return rCandidate.mpSdrFormTextOutlineAttribute == mpSdrFormTextOutlineAttribute;
+ }
+
+ const LineAttribute& SdrFormTextOutlineAttribute::getLineAttribute() const
+ {
+ return mpSdrFormTextOutlineAttribute->getLineAttribute();
+ }
+
+ const StrokeAttribute& SdrFormTextOutlineAttribute::getStrokeAttribute() const
+ {
+ return mpSdrFormTextOutlineAttribute->getStrokeAttribute();
+ }
+
+ sal_uInt8 SdrFormTextOutlineAttribute::getTransparence() const
+ {
+ return mpSdrFormTextOutlineAttribute->getTransparence();
+ }
+
+} // end of namespace
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/sdr/attribute/sdrlineeffectstextattribute.cxx b/svx/source/sdr/attribute/sdrlineeffectstextattribute.cxx
new file mode 100644
index 000000000..72a3ef032
--- /dev/null
+++ b/svx/source/sdr/attribute/sdrlineeffectstextattribute.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 <sdr/attribute/sdrlineeffectstextattribute.hxx>
+
+
+namespace drawinglayer::attribute
+{
+ SdrLineEffectsTextAttribute::SdrLineEffectsTextAttribute(
+ const SdrLineAttribute& rLine,
+ const SdrLineStartEndAttribute& rLineStartEnd,
+ const SdrShadowAttribute& rShadow,
+ const SdrTextAttribute& rTextAttribute,
+ const SdrGlowAttribute& rGlow,
+ sal_Int32 nSoftEdgeRadius)
+ : SdrEffectsTextAttribute(rShadow, rTextAttribute, rGlow, nSoftEdgeRadius),
+ maLine(rLine),
+ maLineStartEnd(rLineStartEnd)
+ {
+ }
+
+ SdrLineEffectsTextAttribute::SdrLineEffectsTextAttribute()
+ {
+ }
+
+ SdrLineEffectsTextAttribute::SdrLineEffectsTextAttribute(const SdrLineEffectsTextAttribute& rCandidate)
+ : SdrEffectsTextAttribute(rCandidate),
+ maLine(rCandidate.getLine()),
+ maLineStartEnd(rCandidate.getLineStartEnd())
+ {
+ }
+
+ SdrLineEffectsTextAttribute& SdrLineEffectsTextAttribute::operator=(const SdrLineEffectsTextAttribute& rCandidate)
+ {
+ SdrEffectsTextAttribute::operator=(rCandidate);
+ maLine = rCandidate.getLine();
+ maLineStartEnd = rCandidate.getLineStartEnd();
+
+ return *this;
+ }
+
+ bool SdrLineEffectsTextAttribute::isDefault() const
+ {
+ return(SdrEffectsTextAttribute::isDefault()
+ && getLine().isDefault()
+ && getLineStartEnd().isDefault());
+ }
+
+ bool SdrLineEffectsTextAttribute::operator==(const SdrLineEffectsTextAttribute& rCandidate) const
+ {
+ return(SdrEffectsTextAttribute::operator==(rCandidate)
+ && getLine() == rCandidate.getLine()
+ && getLineStartEnd() == rCandidate.getLineStartEnd());
+ }
+
+} // end of namespace
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/sdr/attribute/sdrlinefilleffectstextattribute.cxx b/svx/source/sdr/attribute/sdrlinefilleffectstextattribute.cxx
new file mode 100644
index 000000000..442b51833
--- /dev/null
+++ b/svx/source/sdr/attribute/sdrlinefilleffectstextattribute.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 <sdr/attribute/sdrlinefilleffectstextattribute.hxx>
+
+
+namespace drawinglayer::attribute
+{
+ SdrLineFillEffectsTextAttribute::SdrLineFillEffectsTextAttribute(
+ const SdrLineAttribute& rLine,
+ const SdrFillAttribute& rFill,
+ const SdrLineStartEndAttribute& rLineStartEnd,
+ const SdrShadowAttribute& rShadow,
+ const FillGradientAttribute& rFillFloatTransGradient,
+ const SdrTextAttribute& rTextAttribute,
+ const SdrGlowAttribute& rGlow,
+ sal_Int32 nSoftEdgeRadius)
+ : SdrLineEffectsTextAttribute(rLine, rLineStartEnd, rShadow, rTextAttribute, rGlow, nSoftEdgeRadius),
+ maFill(rFill),
+ maFillFloatTransGradient(rFillFloatTransGradient)
+ {
+ }
+
+ SdrLineFillEffectsTextAttribute::SdrLineFillEffectsTextAttribute()
+ {
+ }
+
+ SdrLineFillEffectsTextAttribute::SdrLineFillEffectsTextAttribute(const SdrLineFillEffectsTextAttribute& rCandidate)
+ : SdrLineEffectsTextAttribute(rCandidate),
+ maFill(rCandidate.getFill()),
+ maFillFloatTransGradient(rCandidate.getFillFloatTransGradient())
+ {
+ }
+
+ SdrLineFillEffectsTextAttribute& SdrLineFillEffectsTextAttribute::operator=(const SdrLineFillEffectsTextAttribute& rCandidate)
+ {
+ SdrLineEffectsTextAttribute::operator=(rCandidate);
+ maFill = rCandidate.getFill();
+ maFillFloatTransGradient = rCandidate.getFillFloatTransGradient();
+
+ return *this;
+ }
+
+ bool SdrLineFillEffectsTextAttribute::isDefault() const
+ {
+ return (SdrLineEffectsTextAttribute::isDefault()
+ && getFill().isDefault()
+ && getFillFloatTransGradient().isDefault());
+ }
+
+ bool SdrLineFillEffectsTextAttribute::operator==(const SdrLineFillEffectsTextAttribute& rCandidate) const
+ {
+ return(SdrLineEffectsTextAttribute::operator==(rCandidate)
+ && getFill() == rCandidate.getFill()
+ && getFillFloatTransGradient() == rCandidate.getFillFloatTransGradient());
+ }
+
+} // end of namespace
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/sdr/attribute/sdrtextattribute.cxx b/svx/source/sdr/attribute/sdrtextattribute.cxx
new file mode 100644
index 000000000..5c9ecb34e
--- /dev/null
+++ b/svx/source/sdr/attribute/sdrtextattribute.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 <sdr/attribute/sdrtextattribute.hxx>
+#include <sdr/attribute/sdrformtextattribute.hxx>
+#include <svx/svdotext.hxx>
+#include <editeng/outlobj.hxx>
+#include <svx/sdr/properties/properties.hxx>
+
+
+namespace drawinglayer::attribute
+{
+ class ImpSdrTextAttribute
+ {
+ public:
+ // all-text attributes. The SdrText itself and a copy
+ // of the OPO
+ const SdrText* mpSdrText;
+ std::shared_ptr<OutlinerParaObject> mxOutlinerParaObject;
+
+ // Set when it's a FormText; contains all FormText attributes
+ SdrFormTextAttribute maSdrFormTextAttribute;
+
+ // text distances
+ sal_Int32 maTextLeftDistance;
+ sal_Int32 maTextUpperDistance;
+ sal_Int32 maTextRightDistance;
+ sal_Int32 maTextLowerDistance;
+
+ // #i101556# use versioning from text attributes to detect changes
+ sal_uInt32 maPropertiesVersion;
+
+ // text alignments
+ SdrTextHorzAdjust maSdrTextHorzAdjust;
+ SdrTextVertAdjust maSdrTextVertAdjust;
+
+ bool mbContour : 1;
+ bool mbFitToSize : 1;
+ bool mbAutoFit : 1;
+ bool mbHideContour : 1;
+ bool mbBlink : 1;
+ bool mbScroll : 1;
+ bool mbInEditMode : 1;
+ bool mbFixedCellHeight : 1;
+ bool mbWrongSpell : 1;
+
+ bool mbChainable : 1;
+
+
+ public:
+ ImpSdrTextAttribute(
+ const SdrText* pSdrText,
+ const OutlinerParaObject& rOutlinerParaObject,
+ XFormTextStyle eFormTextStyle,
+ sal_Int32 aTextLeftDistance,
+ sal_Int32 aTextUpperDistance,
+ sal_Int32 aTextRightDistance,
+ sal_Int32 aTextLowerDistance,
+ SdrTextHorzAdjust aSdrTextHorzAdjust,
+ SdrTextVertAdjust aSdrTextVertAdjust,
+ bool bContour,
+ bool bFitToSize,
+ bool bAutoFit,
+ bool bHideContour,
+ bool bBlink,
+ bool bScroll,
+ bool bInEditMode,
+ bool bFixedCellHeight,
+ bool bWrongSpell,
+ bool bChainable)
+ : mpSdrText(pSdrText),
+ mxOutlinerParaObject(std::make_shared<OutlinerParaObject>(rOutlinerParaObject)),
+ maTextLeftDistance(aTextLeftDistance),
+ maTextUpperDistance(aTextUpperDistance),
+ maTextRightDistance(aTextRightDistance),
+ maTextLowerDistance(aTextLowerDistance),
+ maPropertiesVersion(0),
+ maSdrTextHorzAdjust(aSdrTextHorzAdjust),
+ maSdrTextVertAdjust(aSdrTextVertAdjust),
+ mbContour(bContour),
+ mbFitToSize(bFitToSize),
+ mbAutoFit(bAutoFit),
+ mbHideContour(bHideContour),
+ mbBlink(bBlink),
+ mbScroll(bScroll),
+ mbInEditMode(bInEditMode),
+ mbFixedCellHeight(bFixedCellHeight),
+ mbWrongSpell(bWrongSpell),
+ mbChainable(bChainable)
+ {
+ if(!pSdrText)
+ return;
+
+ if(XFormTextStyle::NONE != eFormTextStyle)
+ {
+ // text on path. Create FormText attribute
+ const SfxItemSet& rSet = pSdrText->GetItemSet();
+ maSdrFormTextAttribute = SdrFormTextAttribute(rSet);
+ }
+
+ // #i101556# init with version number to detect changes of single text
+ // attribute and/or style sheets in primitive data without having to
+ // copy that data locally (which would be better from principle)
+ maPropertiesVersion = pSdrText->GetObject().GetProperties().getVersion();
+ }
+
+ ImpSdrTextAttribute()
+ : mpSdrText(nullptr),
+ maTextLeftDistance(0),
+ maTextUpperDistance(0),
+ maTextRightDistance(0),
+ maTextLowerDistance(0),
+ maPropertiesVersion(0),
+ maSdrTextHorzAdjust(SDRTEXTHORZADJUST_LEFT),
+ maSdrTextVertAdjust(SDRTEXTVERTADJUST_TOP),
+ mbContour(false),
+ mbFitToSize(false),
+ mbAutoFit(false),
+ mbHideContour(false),
+ mbBlink(false),
+ mbScroll(false),
+ mbInEditMode(false),
+ mbFixedCellHeight(false),
+ mbWrongSpell(false),
+ mbChainable(false)
+ {
+ }
+
+ // data read access
+ const SdrText& getSdrText() const
+ {
+ assert(mpSdrText && "Access to text of default version of ImpSdrTextAttribute (!)");
+ return *mpSdrText;
+ }
+
+ const OutlinerParaObject& getOutlinerParaObject() const
+ {
+ assert(mxOutlinerParaObject && "Access to OutlinerParaObject of default version of ImpSdrTextAttribute (!)");
+ return *mxOutlinerParaObject;
+ }
+
+ bool isContour() const { return mbContour; }
+ bool isFitToSize() const { return mbFitToSize; }
+ bool isAutoFit() const { return mbAutoFit; }
+ bool isHideContour() const { return mbHideContour; }
+ bool isBlink() const { return mbBlink; }
+ bool isScroll() const { return mbScroll; }
+ bool isInEditMode() const { return mbInEditMode; }
+ bool isFixedCellHeight() const { return mbFixedCellHeight; }
+ bool isChainable() const { return mbChainable; }
+ const SdrFormTextAttribute& getSdrFormTextAttribute() const { return maSdrFormTextAttribute; }
+ sal_Int32 getTextLeftDistance() const { return maTextLeftDistance; }
+ sal_Int32 getTextUpperDistance() const { return maTextUpperDistance; }
+ sal_Int32 getTextRightDistance() const { return maTextRightDistance; }
+ sal_Int32 getTextLowerDistance() const { return maTextLowerDistance; }
+ SdrTextHorzAdjust getSdrTextHorzAdjust() const { return maSdrTextHorzAdjust; }
+ SdrTextVertAdjust getSdrTextVertAdjust() const { return maSdrTextVertAdjust; }
+
+ // compare operator
+ bool operator==(const ImpSdrTextAttribute& rCandidate) const
+ {
+ if (mxOutlinerParaObject.get() != rCandidate.mxOutlinerParaObject.get())
+ {
+ if (mxOutlinerParaObject && rCandidate.mxOutlinerParaObject)
+ {
+ // compares OPO and it's contents, but traditionally not the RedLining
+ // which is not seen as model, but as temporary information
+ if(getOutlinerParaObject() != rCandidate.getOutlinerParaObject())
+ {
+ return false;
+ }
+
+ // #i102062# for primitive visualisation, the WrongList (SpellChecking)
+ // is important, too, so use isWrongListEqual since there is no WrongList
+ // comparison in the regular OutlinerParaObject compare (since it's
+ // not-persistent data)
+ if(!(getOutlinerParaObject().isWrongListEqual(rCandidate.getOutlinerParaObject())))
+ {
+ return false;
+ }
+ }
+ else
+ {
+ // only one is zero; not equal
+ return false;
+ }
+ }
+
+ return (
+ getSdrFormTextAttribute() == rCandidate.getSdrFormTextAttribute()
+ && getTextLeftDistance() == rCandidate.getTextLeftDistance()
+ && getTextUpperDistance() == rCandidate.getTextUpperDistance()
+ && getTextRightDistance() == rCandidate.getTextRightDistance()
+ && getTextLowerDistance() == rCandidate.getTextLowerDistance()
+ && maPropertiesVersion == rCandidate.maPropertiesVersion
+
+ && getSdrTextHorzAdjust() == rCandidate.getSdrTextHorzAdjust()
+ && getSdrTextVertAdjust() == rCandidate.getSdrTextVertAdjust()
+
+ && isContour() == rCandidate.isContour()
+ && isFitToSize() == rCandidate.isFitToSize()
+ && isAutoFit() == rCandidate.isAutoFit()
+ && isHideContour() == rCandidate.isHideContour()
+ && isBlink() == rCandidate.isBlink()
+ && isScroll() == rCandidate.isScroll()
+ && isInEditMode() == rCandidate.isInEditMode()
+ && isFixedCellHeight() == rCandidate.isFixedCellHeight()
+ && mbWrongSpell == rCandidate.mbWrongSpell );
+ }
+ };
+
+ namespace
+ {
+ SdrTextAttribute::ImplType& theGlobalDefault()
+ {
+ static SdrTextAttribute::ImplType SINGLETON;
+ return SINGLETON;
+ }
+ }
+
+ SdrTextAttribute::SdrTextAttribute(
+ const SdrText& rSdrText,
+ const OutlinerParaObject& rOutlinerParaObject,
+ XFormTextStyle eFormTextStyle,
+ sal_Int32 aTextLeftDistance,
+ sal_Int32 aTextUpperDistance,
+ sal_Int32 aTextRightDistance,
+ sal_Int32 aTextLowerDistance,
+ SdrTextHorzAdjust aSdrTextHorzAdjust,
+ SdrTextVertAdjust aSdrTextVertAdjust,
+ bool bContour,
+ bool bFitToSize,
+ bool bAutoFit,
+ bool bHideContour,
+ bool bBlink,
+ bool bScroll,
+ bool bInEditMode,
+ bool bFixedCellHeight,
+ bool bWrongSpell,
+ bool bChainable)
+ : mpSdrTextAttribute(
+ ImpSdrTextAttribute(
+ &rSdrText, rOutlinerParaObject, eFormTextStyle, aTextLeftDistance,
+ aTextUpperDistance, aTextRightDistance, aTextLowerDistance,
+ aSdrTextHorzAdjust, aSdrTextVertAdjust, bContour, bFitToSize, bAutoFit,
+ bHideContour, bBlink, bScroll, bInEditMode, bFixedCellHeight, bWrongSpell,
+ bChainable))
+ {
+ }
+
+ SdrTextAttribute::SdrTextAttribute()
+ : mpSdrTextAttribute(theGlobalDefault())
+ {
+ }
+
+ SdrTextAttribute::SdrTextAttribute(const SdrTextAttribute& rCandidate)
+ : mpSdrTextAttribute(rCandidate.mpSdrTextAttribute)
+ {
+ }
+
+ SdrTextAttribute::SdrTextAttribute(SdrTextAttribute&& rCandidate) noexcept
+ : mpSdrTextAttribute(std::move(rCandidate.mpSdrTextAttribute))
+ {
+ }
+
+ SdrTextAttribute::~SdrTextAttribute()
+ {
+ }
+
+ bool SdrTextAttribute::isDefault() const
+ {
+ return mpSdrTextAttribute.same_object(theGlobalDefault());
+ }
+
+ SdrTextAttribute& SdrTextAttribute::operator=(const SdrTextAttribute& rCandidate)
+ {
+ mpSdrTextAttribute = rCandidate.mpSdrTextAttribute;
+ return *this;
+ }
+
+ SdrTextAttribute& SdrTextAttribute::operator=(SdrTextAttribute&& rCandidate) noexcept
+ {
+ mpSdrTextAttribute = std::move(rCandidate.mpSdrTextAttribute);
+ return *this;
+ }
+
+ bool SdrTextAttribute::operator==(const SdrTextAttribute& rCandidate) const
+ {
+ // tdf#87509 default attr is always != non-default attr, even with same values
+ if(rCandidate.isDefault() != isDefault())
+ return false;
+
+ return rCandidate.mpSdrTextAttribute == mpSdrTextAttribute;
+ }
+
+ const SdrText& SdrTextAttribute::getSdrText() const
+ {
+ return mpSdrTextAttribute->getSdrText();
+ }
+
+ const OutlinerParaObject& SdrTextAttribute::getOutlinerParaObject() const
+ {
+ return mpSdrTextAttribute->getOutlinerParaObject();
+ }
+
+ bool SdrTextAttribute::isContour() const
+ {
+ return mpSdrTextAttribute->isContour();
+ }
+
+ bool SdrTextAttribute::isFitToSize() const
+ {
+ return mpSdrTextAttribute->isFitToSize();
+ }
+
+ bool SdrTextAttribute::isAutoFit() const
+ {
+ return mpSdrTextAttribute->isAutoFit();
+ }
+
+ bool SdrTextAttribute::isHideContour() const
+ {
+ return mpSdrTextAttribute->isHideContour();
+ }
+
+ bool SdrTextAttribute::isBlink() const
+ {
+ return mpSdrTextAttribute->isBlink();
+ }
+
+ bool SdrTextAttribute::isScroll() const
+ {
+ return mpSdrTextAttribute->isScroll();
+ }
+
+ bool SdrTextAttribute::isInEditMode() const
+ {
+ return mpSdrTextAttribute->isInEditMode();
+ }
+
+ bool SdrTextAttribute::isChainable() const
+ {
+ return mpSdrTextAttribute->isChainable();
+ }
+
+
+ bool SdrTextAttribute::isFixedCellHeight() const
+ {
+ return mpSdrTextAttribute->isFixedCellHeight();
+ }
+
+ const SdrFormTextAttribute& SdrTextAttribute::getSdrFormTextAttribute() const
+ {
+ return mpSdrTextAttribute->getSdrFormTextAttribute();
+ }
+
+ sal_Int32 SdrTextAttribute::getTextLeftDistance() const
+ {
+ return mpSdrTextAttribute->getTextLeftDistance();
+ }
+
+ sal_Int32 SdrTextAttribute::getTextUpperDistance() const
+ {
+ return mpSdrTextAttribute->getTextUpperDistance();
+ }
+
+ sal_Int32 SdrTextAttribute::getTextRightDistance() const
+ {
+ return mpSdrTextAttribute->getTextRightDistance();
+ }
+
+ sal_Int32 SdrTextAttribute::getTextLowerDistance() const
+ {
+ return mpSdrTextAttribute->getTextLowerDistance();
+ }
+
+ SdrTextHorzAdjust SdrTextAttribute::getSdrTextHorzAdjust() const
+ {
+ return mpSdrTextAttribute->getSdrTextHorzAdjust();
+ }
+
+ SdrTextVertAdjust SdrTextAttribute::getSdrTextVertAdjust() const
+ {
+ return mpSdrTextAttribute->getSdrTextVertAdjust();
+ }
+
+ void SdrTextAttribute::getBlinkTextTiming(drawinglayer::animation::AnimationEntryList& rAnimList) const
+ {
+ if(isBlink())
+ {
+ getSdrText().GetObject().impGetBlinkTextTiming(rAnimList);
+ }
+ }
+
+ void SdrTextAttribute::getScrollTextTiming(drawinglayer::animation::AnimationEntryList& rAnimList, double fFrameLength, double fTextLength) const
+ {
+ if(isScroll())
+ {
+ getSdrText().GetObject().impGetScrollTextTiming(rAnimList, fFrameLength, fTextLength);
+ }
+ }
+
+} // end of namespace
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/sdr/contact/displayinfo.cxx b/svx/source/sdr/contact/displayinfo.cxx
new file mode 100644
index 000000000..405fb41a0
--- /dev/null
+++ b/svx/source/sdr/contact/displayinfo.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 <svx/sdr/contact/displayinfo.hxx>
+
+
+namespace sdr::contact
+{
+ DisplayInfo::DisplayInfo()
+ : maProcessLayers(true), // init layer info with all bits set to draw everything on default
+ mbControlLayerProcessingActive(false),
+ mbPageProcessingActive(true),
+ mbGhostedDrawModeActive(false),
+ mbSubContentActive(false)
+ {
+ }
+
+ // Access to LayerInfos (which layers to process)
+ void DisplayInfo::SetProcessLayers(const SdrLayerIDSet& rSet)
+ {
+ maProcessLayers = rSet;
+ }
+
+ // access to RedrawArea
+ void DisplayInfo::SetRedrawArea(const vcl::Region& rRegion)
+ {
+ maRedrawArea = rRegion;
+ }
+
+ void DisplayInfo::SetWriterPageFrame(basegfx::B2IRectangle const& rPageFrame)
+ {
+ m_WriterPageFrame = rPageFrame;
+ }
+
+ void DisplayInfo::SetControlLayerProcessingActive(bool bDoProcess)
+ {
+ if(mbControlLayerProcessingActive != bDoProcess)
+ {
+ mbControlLayerProcessingActive = bDoProcess;
+ }
+ }
+
+ void DisplayInfo::SetPageProcessingActive(bool bDoProcess)
+ {
+ if(mbPageProcessingActive != bDoProcess)
+ {
+ mbPageProcessingActive = bDoProcess;
+ }
+ }
+
+ void DisplayInfo::ClearGhostedDrawMode()
+ {
+ mbGhostedDrawModeActive = false;
+ }
+
+ void DisplayInfo::SetGhostedDrawMode()
+ {
+ mbGhostedDrawModeActive = true;
+ }
+
+ void DisplayInfo::SetSubContentActive(bool bNew)
+ {
+ if(mbSubContentActive != bNew)
+ {
+ mbSubContentActive = bNew;
+ }
+ }
+
+} // end of namespace
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/sdr/contact/objectcontact.cxx b/svx/source/sdr/contact/objectcontact.cxx
new file mode 100644
index 000000000..d135a2a29
--- /dev/null
+++ b/svx/source/sdr/contact/objectcontact.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 <svx/sdr/contact/objectcontact.hxx>
+#include <tools/debug.hxx>
+#include <svx/sdr/contact/viewobjectcontact.hxx>
+#include <svx/sdr/contact/viewcontact.hxx>
+
+using namespace com::sun::star;
+
+namespace sdr::contact {
+
+bool ObjectContact::supportsGridOffsets() const
+{
+ // default does not support GridOffset
+ return false;
+}
+
+void ObjectContact::calculateGridOffsetForViewOjectContact(
+ basegfx::B2DVector& /*rTarget*/,
+ const ViewObjectContact& /*rClient*/) const
+{
+ // default does not on-demand calculate GridOffset
+}
+
+void ObjectContact::calculateGridOffsetForB2DRange(
+ basegfx::B2DVector& /*rTarget*/,
+ const basegfx::B2DRange& /*rB2DRange*/) const
+{
+ // default does not on-demand calculate GridOffset
+}
+
+ObjectContact::ObjectContact()
+: mpViewObjectContactRedirector(nullptr),
+ mbIsPreviewRenderer(false)
+{
+}
+
+ObjectContact::~ObjectContact() COVERITY_NOEXCEPT_FALSE
+{
+ // get rid of all registered contacts
+ // #i84257# To avoid that each 'delete pCandidate' again uses
+ // the local RemoveViewObjectContact with a search and removal in the
+ // vector, simply copy and clear local vector.
+ std::vector< ViewObjectContact* > aLocalVOCList;
+ aLocalVOCList.swap(maViewObjectContactVector);
+
+ for (const auto & pCandidate : aLocalVOCList)
+ // ViewObjectContacts only make sense with View and Object contacts.
+ // When the contact to the SdrObject is deleted like in this case,
+ // all ViewObjectContacts can be deleted, too.
+ delete pCandidate;
+
+ // assert when there were new entries added during deletion
+ DBG_ASSERT(maViewObjectContactVector.empty(), "Corrupted ViewObjectContactList (!)");
+}
+
+// LazyInvalidate request. Default implementation directly handles
+// this by calling back triggerLazyInvalidate() at the VOC
+void ObjectContact::setLazyInvalidate(ViewObjectContact& rVOC)
+{
+ rVOC.triggerLazyInvalidate();
+}
+
+// call this to support evtl. preparations for repaint. Default does nothing
+void ObjectContact::PrepareProcessDisplay()
+{
+}
+
+// A new ViewObjectContact was created and shall be remembered.
+void ObjectContact::AddViewObjectContact(ViewObjectContact& rVOContact)
+{
+ maViewObjectContactVector.push_back(&rVOContact);
+}
+
+// A ViewObjectContact was deleted and shall be forgotten.
+void ObjectContact::RemoveViewObjectContact(ViewObjectContact& rVOContact)
+{
+ std::vector< ViewObjectContact* >::iterator aFindResult = std::find(maViewObjectContactVector.begin(), maViewObjectContactVector.end(), &rVOContact);
+
+ if(aFindResult != maViewObjectContactVector.end())
+ {
+ maViewObjectContactVector.erase(aFindResult);
+ }
+}
+
+// Process the whole displaying
+void ObjectContact::ProcessDisplay(DisplayInfo& /*rDisplayInfo*/)
+{
+ // default does nothing
+}
+
+// test if visualizing of entered groups is switched on at all
+bool ObjectContact::DoVisualizeEnteredGroup() const
+{
+ // Do not do that as default
+ return false;
+}
+
+// get active group's (the entered group) ViewContact
+const ViewContact* ObjectContact::getActiveViewContact() const
+{
+ // default has no active VC
+ return nullptr;
+}
+
+// Invalidate given rectangle at the window/output which is represented by
+// this ObjectContact.
+void ObjectContact::InvalidatePartOfView(const basegfx::B2DRange& /*rRange*/) const
+{
+ // nothing to do here in the default version
+}
+
+// Get info about the need to visualize GluePoints
+bool ObjectContact::AreGluePointsVisible() const
+{
+ return false;
+}
+
+// check if text animation is allowed. Default is sal_true.
+bool ObjectContact::IsTextAnimationAllowed() const
+{
+ return true;
+}
+
+// check if graphic animation is allowed. Default is sal_true.
+bool ObjectContact::IsGraphicAnimationAllowed() const
+{
+ return true;
+}
+
+void ObjectContact::SetViewObjectContactRedirector(ViewObjectContactRedirector* pNew)
+{
+ if(mpViewObjectContactRedirector != pNew)
+ {
+ mpViewObjectContactRedirector = pNew;
+ }
+}
+
+// print? Default is false
+bool ObjectContact::isOutputToPrinter() const
+{
+ return false;
+}
+
+// recording MetaFile? Default is false
+bool ObjectContact::isOutputToRecordingMetaFile() const
+{
+ return false;
+}
+
+// pdf export? Default is false
+bool ObjectContact::isOutputToPDFFile() const
+{
+ return false;
+}
+
+bool ObjectContact::isExportTaggedPDF() const
+{
+ return false;
+}
+
+// gray display mode
+bool ObjectContact::isDrawModeGray() const
+{
+ return false;
+}
+
+// high contrast display mode
+bool ObjectContact::isDrawModeHighContrast() const
+{
+ return false;
+}
+
+// access to SdrPageView. Default implementation returns NULL
+SdrPageView* ObjectContact::TryToGetSdrPageView() const
+{
+ return nullptr;
+}
+
+// access to OutputDevice. Default implementation returns NULL
+OutputDevice* ObjectContact::TryToGetOutputDevice() const
+{
+ return nullptr;
+}
+
+void ObjectContact::resetAllGridOffsets()
+{
+ const sal_uInt32 nVOCCount(getViewObjectContactCount());
+
+ for(sal_uInt32 a(0); a < nVOCCount; a++)
+ {
+ ViewObjectContact* pVOC(getViewObjectContact(a));
+ assert(pVOC && "ObjectContact: ViewObjectContact list Corrupt (!)");
+ pVOC->resetGridOffset();
+ }
+}
+
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/sdr/contact/objectcontactofobjlistpainter.cxx b/svx/source/sdr/contact/objectcontactofobjlistpainter.cxx
new file mode 100644
index 000000000..f666eceb1
--- /dev/null
+++ b/svx/source/sdr/contact/objectcontactofobjlistpainter.cxx
@@ -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 .
+ */
+
+#include <sdr/contact/objectcontactofobjlistpainter.hxx>
+#include <svx/sdr/contact/displayinfo.hxx>
+#include <svx/sdr/contact/viewobjectcontact.hxx>
+#include <svx/svdpage.hxx>
+#include <svx/svdobj.hxx>
+#include <svx/sdr/contact/viewcontact.hxx>
+#include <basegfx/matrix/b2dhommatrix.hxx>
+#include <drawinglayer/processor2d/baseprocessor2d.hxx>
+#include <drawinglayer/processor2d/processor2dtools.hxx>
+#include <svx/unoapi.hxx>
+#include <tools/debug.hxx>
+#include <vcl/gdimtf.hxx>
+#include <vcl/pdfextoutdevdata.hxx>
+#include <memory>
+
+namespace sdr::contact {
+
+ObjectContactPainter::ObjectContactPainter()
+{
+}
+
+// The destructor.
+ObjectContactPainter::~ObjectContactPainter()
+{
+}
+
+sal_uInt32 ObjectContactOfObjListPainter::GetPaintObjectCount() const
+{
+ return maStartObjects.size();
+}
+
+ViewContact& ObjectContactOfObjListPainter::GetPaintObjectViewContact(sal_uInt32 nIndex)
+{
+ const SdrObject* pObj = maStartObjects[nIndex];
+ DBG_ASSERT(pObj, "ObjectContactOfObjListPainter: Corrupt SdrObjectVector (!)");
+ return pObj->GetViewContact();
+}
+
+ObjectContactOfObjListPainter::ObjectContactOfObjListPainter(
+ OutputDevice& rTargetDevice,
+ SdrObjectVector&& rObjects,
+ const SdrPage* pProcessedPage)
+: mrTargetOutputDevice(rTargetDevice),
+ maStartObjects(std::move(rObjects)),
+ mpProcessedPage(pProcessedPage)
+{
+}
+
+ObjectContactOfObjListPainter::~ObjectContactOfObjListPainter()
+{
+}
+
+// Process the whole displaying
+void ObjectContactOfObjListPainter::ProcessDisplay(DisplayInfo& rDisplayInfo)
+{
+ const sal_uInt32 nCount(GetPaintObjectCount());
+
+ if(!nCount)
+ return;
+
+ OutputDevice* pTargetDevice = TryToGetOutputDevice();
+
+ if(!pTargetDevice)
+ return;
+
+ // update current ViewInformation2D at the ObjectContact
+ const GDIMetaFile* pMetaFile = pTargetDevice->GetConnectMetaFile();
+ const bool bOutputToRecordingMetaFile(pMetaFile && pMetaFile->IsRecord() && !pMetaFile->IsPause());
+ basegfx::B2DRange aViewRange;
+
+ // create ViewRange
+ if(!bOutputToRecordingMetaFile)
+ {
+ // use visible pixels, but transform to world coordinates
+ const Size aOutputSizePixel(pTargetDevice->GetOutputSizePixel());
+ aViewRange = ::basegfx::B2DRange(0.0, 0.0, aOutputSizePixel.getWidth(), aOutputSizePixel.getHeight());
+ aViewRange.transform(pTargetDevice->GetInverseViewTransformation());
+ }
+
+ // update local ViewInformation2D
+ const drawinglayer::geometry::ViewInformation2D aNewViewInformation2D(
+ basegfx::B2DHomMatrix(),
+ pTargetDevice->GetViewTransformation(),
+ aViewRange,
+ GetXDrawPageForSdrPage(const_cast< SdrPage* >(mpProcessedPage)),
+ 0.0);
+ updateViewInformation2D(aNewViewInformation2D);
+
+ // collect primitive data in a sequence; this will already use the updated ViewInformation2D
+ drawinglayer::primitive2d::Primitive2DContainer xPrimitiveSequence;
+
+ for(sal_uInt32 a(0); a < nCount; a++)
+ {
+ const ViewObjectContact& rViewObjectContact = GetPaintObjectViewContact(a).GetViewObjectContact(*this);
+
+ rViewObjectContact.getPrimitive2DSequenceHierarchy(rDisplayInfo, xPrimitiveSequence);
+ }
+
+ // if there is something to show, use a vclProcessor to render it
+ if(!xPrimitiveSequence.empty())
+ {
+ std::unique_ptr<drawinglayer::processor2d::BaseProcessor2D> pProcessor2D(drawinglayer::processor2d::createProcessor2DFromOutputDevice(
+ *pTargetDevice,
+ getViewInformation2D()));
+
+ pProcessor2D->process(xPrimitiveSequence);
+ }
+}
+
+// recording MetaFile?
+bool ObjectContactOfObjListPainter::isOutputToRecordingMetaFile() const
+{
+ GDIMetaFile* pMetaFile = mrTargetOutputDevice.GetConnectMetaFile();
+ return (pMetaFile && pMetaFile->IsRecord() && !pMetaFile->IsPause());
+}
+
+// pdf export?
+bool ObjectContactOfObjListPainter::isOutputToPDFFile() const
+{
+ return OUTDEV_PDF == mrTargetOutputDevice.GetOutDevType();
+}
+
+bool ObjectContactOfObjListPainter::isExportTaggedPDF() const
+{
+ if (isOutputToPDFFile())
+ {
+ vcl::PDFExtOutDevData* pPDFExtOutDevData(dynamic_cast<vcl::PDFExtOutDevData*>(
+ mrTargetOutputDevice.GetExtOutDevData()));
+
+ if (nullptr != pPDFExtOutDevData)
+ {
+ return pPDFExtOutDevData->GetIsExportTaggedPDF();
+ }
+ }
+ return false;
+}
+
+OutputDevice* ObjectContactOfObjListPainter::TryToGetOutputDevice() const
+{
+ return &mrTargetOutputDevice;
+}
+
+sal_uInt32 ObjectContactOfPagePainter::GetPaintObjectCount() const
+{
+ return (GetStartPage() ? 1 : 0);
+}
+
+ViewContact& ObjectContactOfPagePainter::GetPaintObjectViewContact(sal_uInt32 /*nIndex*/)
+{
+ DBG_ASSERT(GetStartPage(), "ObjectContactOfPagePainter::GetPaintObjectViewContact: no StartPage set (!)");
+ return GetStartPage()->GetViewContact();
+}
+
+ObjectContactOfPagePainter::ObjectContactOfPagePainter(
+ ObjectContact& rOriginalObjectContact)
+: mrOriginalObjectContact(rOriginalObjectContact)
+{
+}
+
+ObjectContactOfPagePainter::~ObjectContactOfPagePainter()
+{
+}
+
+void ObjectContactOfPagePainter::SetStartPage(const SdrPage* pPage)
+{
+ if(pPage != GetStartPage())
+ {
+ mxStartPage = const_cast< SdrPage* >(pPage); // no tools::WeakReference<SdrPage> available to hold a const SdrPage*
+ }
+}
+
+OutputDevice* ObjectContactOfPagePainter::TryToGetOutputDevice() const
+{
+ return mrOriginalObjectContact.TryToGetOutputDevice();
+}
+
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/sdr/contact/objectcontactofpageview.cxx b/svx/source/sdr/contact/objectcontactofpageview.cxx
new file mode 100644
index 000000000..31b8b949e
--- /dev/null
+++ b/svx/source/sdr/contact/objectcontactofpageview.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 <config_feature_desktop.h>
+
+#include <svx/sdr/contact/objectcontactofpageview.hxx>
+#include <sdr/contact/viewobjectcontactofunocontrol.hxx>
+#include <svx/svdpagv.hxx>
+#include <svx/svdpage.hxx>
+#include <svx/sdr/contact/displayinfo.hxx>
+#include <svx/sdr/contact/viewobjectcontact.hxx>
+#include <svx/svdview.hxx>
+#include <svx/sdr/contact/viewcontact.hxx>
+#include <svx/sdr/animation/objectanimator.hxx>
+#include <svx/sdrpagewindow.hxx>
+#include <svx/sdrpaintwindow.hxx>
+#include <basegfx/matrix/b2dhommatrix.hxx>
+#include <drawinglayer/processor2d/baseprocessor2d.hxx>
+#include <drawinglayer/processor2d/processor2dtools.hxx>
+#include <osl/diagnose.h>
+#include <svx/unoapi.hxx>
+#include <unotools/configmgr.hxx>
+#include <vcl/canvastools.hxx>
+#include <vcl/pdfextoutdevdata.hxx>
+#include <comphelper/lok.hxx>
+
+#include <memory>
+
+using namespace com::sun::star;
+
+namespace sdr::contact
+{
+ // internal access to SdrPage of SdrPageView
+ SdrPage* ObjectContactOfPageView::GetSdrPage() const
+ {
+ return GetPageWindow().GetPageView().GetPage();
+ }
+
+ ObjectContactOfPageView::ObjectContactOfPageView(
+ SdrPageWindow& rPageWindow, const char *pDebugName)
+ : Idle(pDebugName)
+ , mrPageWindow(rPageWindow)
+ {
+ // init PreviewRenderer flag
+ setPreviewRenderer(static_cast<SdrPaintView&>(rPageWindow.GetPageView().GetView()).IsPreviewRenderer());
+
+ // init timer
+ SetPriority(TaskPriority::HIGH_IDLE);
+ Stop();
+ }
+
+ ObjectContactOfPageView::~ObjectContactOfPageView()
+ {
+ // execute missing LazyInvalidates and stop timer
+ Invoke();
+ }
+
+ // LazyInvalidate request. Take action.
+ void ObjectContactOfPageView::setLazyInvalidate(ViewObjectContact& /*rVOC*/)
+ {
+ // do NOT call parent, but remember that something is to do by
+ // starting the LazyInvalidateTimer
+ Start();
+ }
+
+ // call this to support evtl. preparations for repaint
+ void ObjectContactOfPageView::PrepareProcessDisplay()
+ {
+ if(IsActive())
+ // there are still non-triggered LazyInvalidate events, trigger these
+ Invoke();
+ }
+
+ // From baseclass Timer, the timeout call triggered by the LazyInvalidate mechanism
+ void ObjectContactOfPageView::Invoke()
+ {
+ // stop the timer
+ Stop();
+
+ // invalidate all LazyInvalidate VOCs new situations
+ const sal_uInt32 nVOCCount(getViewObjectContactCount());
+
+ for(sal_uInt32 a(0); a < nVOCCount; a++)
+ {
+ ViewObjectContact* pCandidate = getViewObjectContact(a);
+ pCandidate->triggerLazyInvalidate();
+ }
+ }
+
+ // Process the whole displaying
+ void ObjectContactOfPageView::ProcessDisplay(DisplayInfo& rDisplayInfo)
+ {
+ const SdrPage* pStartPage = GetSdrPage();
+
+ if(pStartPage && !rDisplayInfo.GetProcessLayers().IsEmpty())
+ {
+ const ViewContact& rDrawPageVC = pStartPage->GetViewContact();
+
+ if(rDrawPageVC.GetObjectCount())
+ {
+ DoProcessDisplay(rDisplayInfo);
+ }
+ }
+ }
+
+ // Process the whole displaying. Only use given DisplayInfo, do not access other
+ // OutputDevices then the given ones.
+ void ObjectContactOfPageView::DoProcessDisplay(DisplayInfo& rDisplayInfo)
+ {
+ OutputDevice& rTargetOutDev = GetPageWindow().GetPaintWindow().GetTargetOutputDevice();
+ const Size aOutputSizePixel(rTargetOutDev.GetOutputSizePixel());
+ if (!isOutputToRecordingMetaFile() // do those have outdev too?
+ && (0 == aOutputSizePixel.getWidth() ||
+ 0 == aOutputSizePixel.getHeight()))
+ {
+ return;
+ }
+
+ // visualize entered group when that feature is switched on and it's not
+ // a print output. #i29129# No ghosted display for printing.
+ bool bVisualizeEnteredGroup(DoVisualizeEnteredGroup() && !isOutputToPrinter());
+
+ // Visualize entered groups: Set to ghosted as default
+ // start. Do this only for the DrawPage, not for MasterPages
+ if(bVisualizeEnteredGroup)
+ {
+ rDisplayInfo.SetGhostedDrawMode();
+ }
+
+ // #114359# save old and set clip region
+ OutputDevice* pOutDev = TryToGetOutputDevice();
+ OSL_ENSURE(nullptr != pOutDev, "ObjectContactOfPageView without OutDev, someone has overridden TryToGetOutputDevice wrong (!)");
+ bool bClipRegionPushed(false);
+ const vcl::Region& rRedrawArea(rDisplayInfo.GetRedrawArea());
+
+ if(!rRedrawArea.IsEmpty() && !comphelper::LibreOfficeKit::isActive())
+ {
+ bClipRegionPushed = true;
+ pOutDev->Push(vcl::PushFlags::CLIPREGION);
+ pOutDev->IntersectClipRegion(rRedrawArea);
+ }
+
+ // Get start node and process DrawPage contents
+ const ViewObjectContact& rDrawPageVOContact = GetSdrPage()->GetViewContact().GetViewObjectContact(*this);
+
+ // update current ViewInformation2D at the ObjectContact
+ const double fCurrentTime(getPrimitiveAnimator().GetTime());
+ basegfx::B2DRange aViewRange;
+
+ // create ViewRange
+ if(isOutputToRecordingMetaFile())
+ {
+ if (!rDisplayInfo.GetRedrawArea().IsEmpty())
+ {
+ // #i98402# if it's a PDF export, set the ClipRegion as ViewRange. This is
+ // mainly because SW does not use DrawingLayer Page-Oriented and if not doing this,
+ // all existing objects will be collected as primitives and processed.
+ // OD 2009-03-05 #i99876# perform the same also for SW on printing.
+ // fdo#78149 same thing also needed for plain MetaFile
+ // export, so why not do it always
+ const tools::Rectangle aLogicClipRectangle(rDisplayInfo.GetRedrawArea().GetBoundRect());
+
+ aViewRange = vcl::unotools::b2DRectangleFromRectangle(aLogicClipRectangle);
+ }
+ }
+ else
+ {
+ // use visible pixels, but transform to world coordinates
+ aViewRange = basegfx::B2DRange(0.0, 0.0, aOutputSizePixel.getWidth(), aOutputSizePixel.getHeight());
+ // if a clip region is set, use it
+ if(!rDisplayInfo.GetRedrawArea().IsEmpty())
+ {
+ // get logic clip range and create discrete one
+ const tools::Rectangle aLogicClipRectangle(rDisplayInfo.GetRedrawArea().GetBoundRect());
+ basegfx::B2DRange aDiscreteClipRange = vcl::unotools::b2DRectangleFromRectangle(aLogicClipRectangle);
+ aDiscreteClipRange.transform(rTargetOutDev.GetViewTransformation());
+
+ // align the discrete one to discrete boundaries (pixel bounds). Also
+ // expand X and Y max by one due to Rectangle definition source
+ aDiscreteClipRange.expand(basegfx::B2DTuple(
+ floor(aDiscreteClipRange.getMinX()),
+ floor(aDiscreteClipRange.getMinY())));
+ aDiscreteClipRange.expand(basegfx::B2DTuple(
+ 1.0 + ceil(aDiscreteClipRange.getMaxX()),
+ 1.0 + ceil(aDiscreteClipRange.getMaxY())));
+
+ // intersect current ViewRange with ClipRange
+ aViewRange.intersect(aDiscreteClipRange);
+ }
+
+ // transform to world coordinates
+ aViewRange.transform(rTargetOutDev.GetInverseViewTransformation());
+ }
+
+ // update local ViewInformation2D
+ const drawinglayer::geometry::ViewInformation2D aNewViewInformation2D(
+ basegfx::B2DHomMatrix(),
+ rTargetOutDev.GetViewTransformation(),
+ aViewRange,
+ GetXDrawPageForSdrPage(GetSdrPage()),
+ fCurrentTime);
+ updateViewInformation2D(aNewViewInformation2D);
+
+ drawinglayer::primitive2d::Primitive2DContainer xPrimitiveSequence;
+
+#if HAVE_FEATURE_DESKTOP || defined( ANDROID )
+ // get whole Primitive2DContainer; this will already make use of updated ViewInformation2D
+ // and may use the MapMode from the Target OutDev in the DisplayInfo
+ rDrawPageVOContact.getPrimitive2DSequenceHierarchy(rDisplayInfo, xPrimitiveSequence);
+#else
+ // Hmm, !HAVE_FEATURE_DESKTOP && !ANDROID means iOS,
+ // right? But does it makes sense to use a different code
+ // path for iOS than for Android; both use tiled rendering
+ // etc now.
+
+ // HACK: this only works when we are drawing sdr shapes via
+ // drawinglayer; but it can happen that the hierarchy contains
+ // more than just the shapes, and then it fails.
+ //
+ // This is good enough for the tiled rendering for the moment, but
+ // we need to come up with the real solution shortly.
+
+ // Only get the expensive hierarchy if we can be sure that the
+ // returned sequence won't be empty anyway.
+ bool bGetHierarchy = rRedrawArea.IsEmpty();
+ if (!bGetHierarchy)
+ {
+ // Not empty? Then not doing a full redraw, check if
+ // getPrimitive2DSequenceHierarchy() is still needed.
+ sal_Int32 nObjCount = GetSdrPage()->GetObjCount();
+ for (sal_Int32 i = 0; i < nObjCount; ++i)
+ {
+ SdrObject* pObject = GetSdrPage()->GetObj(i);
+ if (rRedrawArea.Overlaps(pObject->GetCurrentBoundRect()))
+ {
+ bGetHierarchy = true;
+ break;
+ }
+ }
+ }
+
+ if (bGetHierarchy)
+ // get whole Primitive2DContainer; this will already make use of updated ViewInformation2D
+ // and may use the MapMode from the Target OutDev in the DisplayInfo
+ rDrawPageVOContact.getPrimitive2DSequenceHierarchy(rDisplayInfo, xPrimitiveSequence);
+#endif
+
+ // if there is something to show, use a primitive processor to render it. There
+ // is a choice between VCL and Canvas processors currently. The decision is made in
+ // createProcessor2DFromOutputDevice and takes into account things like the
+ // Target is a MetaFile, a VDev or something else. The Canvas renderer is triggered
+ // currently using the shown boolean. Canvas is not yet the default.
+ if(!xPrimitiveSequence.empty())
+ {
+ // prepare OutputDevice (historical stuff, maybe soon removed)
+ rDisplayInfo.ClearGhostedDrawMode(); // reset, else the VCL-paint with the processor will not do the right thing
+ pOutDev->SetLayoutMode(vcl::text::ComplexTextLayoutFlags::Default); // reset, default is no BiDi/RTL
+ // create renderer
+ std::unique_ptr<drawinglayer::processor2d::BaseProcessor2D> pProcessor2D(
+ drawinglayer::processor2d::createProcessor2DFromOutputDevice(
+ rTargetOutDev, getViewInformation2D()));
+ pProcessor2D->process(xPrimitiveSequence);
+ }
+
+ // #114359# restore old ClipReghion
+ if(bClipRegionPushed)
+ {
+ pOutDev->Pop();
+ }
+
+ // Visualize entered groups: Reset to original DrawMode
+ if(bVisualizeEnteredGroup)
+ {
+ rDisplayInfo.ClearGhostedDrawMode();
+ }
+ }
+
+ // test if visualizing of entered groups is switched on at all
+ bool ObjectContactOfPageView::DoVisualizeEnteredGroup() const
+ {
+ return true;
+ }
+
+ // get active group's (the entered group) ViewContact
+ const ViewContact* ObjectContactOfPageView::getActiveViewContact() const
+ {
+ SdrObjList* pActiveGroupList = GetPageWindow().GetPageView().GetObjList();
+
+ if(pActiveGroupList)
+ {
+ if(nullptr != pActiveGroupList->getSdrPageFromSdrObjList())
+ {
+ // It's a Page itself
+ return &(pActiveGroupList->getSdrPageFromSdrObjList()->GetViewContact());
+ }
+ else if(pActiveGroupList->getSdrObjectFromSdrObjList())
+ {
+ // Group object
+ return &(pActiveGroupList->getSdrObjectFromSdrObjList()->GetViewContact());
+ }
+ }
+ else if(GetSdrPage())
+ {
+ // use page of associated SdrPageView
+ return &(GetSdrPage()->GetViewContact());
+ }
+
+ return nullptr;
+ }
+
+ // Invalidate given rectangle at the window/output which is represented by
+ // this ObjectContact.
+ void ObjectContactOfPageView::InvalidatePartOfView(const basegfx::B2DRange& rRange) const
+ {
+ // invalidate at associated PageWindow
+ GetPageWindow().InvalidatePageWindow(rRange);
+ }
+
+ // Get info about the need to visualize GluePoints
+ bool ObjectContactOfPageView::AreGluePointsVisible() const
+ {
+ bool bTiledRendering = comphelper::LibreOfficeKit::isActive();
+ return !bTiledRendering && GetPageWindow().GetPageView().GetView().ImpIsGlueVisible();
+ }
+
+ // check if text animation is allowed.
+ bool ObjectContactOfPageView::IsTextAnimationAllowed() const
+ {
+ if (utl::ConfigManager::IsFuzzing())
+ return true;
+ SdrView& rView = GetPageWindow().GetPageView().GetView();
+ const SvtAccessibilityOptions& rOpt = rView.getAccessibilityOptions();
+ return rOpt.GetIsAllowAnimatedText();
+ }
+
+ // check if graphic animation is allowed.
+ bool ObjectContactOfPageView::IsGraphicAnimationAllowed() const
+ {
+ if (utl::ConfigManager::IsFuzzing())
+ return true;
+ SdrView& rView = GetPageWindow().GetPageView().GetView();
+ const SvtAccessibilityOptions& rOpt = rView.getAccessibilityOptions();
+ return rOpt.GetIsAllowAnimatedGraphics();
+ }
+
+ // print?
+ bool ObjectContactOfPageView::isOutputToPrinter() const
+ {
+ return (OUTDEV_PRINTER == mrPageWindow.GetPaintWindow().GetOutputDevice().GetOutDevType());
+ }
+
+ // recording MetaFile?
+ bool ObjectContactOfPageView::isOutputToRecordingMetaFile() const
+ {
+ GDIMetaFile* pMetaFile = mrPageWindow.GetPaintWindow().GetOutputDevice().GetConnectMetaFile();
+ return (pMetaFile && pMetaFile->IsRecord() && !pMetaFile->IsPause());
+ }
+
+ // pdf export?
+ bool ObjectContactOfPageView::isOutputToPDFFile() const
+ {
+ return OUTDEV_PDF == mrPageWindow.GetPaintWindow().GetOutputDevice().GetOutDevType();
+ }
+
+ bool ObjectContactOfPageView::isExportTaggedPDF() const
+ {
+ if (isOutputToPDFFile())
+ {
+ vcl::PDFExtOutDevData* pPDFExtOutDevData(dynamic_cast<vcl::PDFExtOutDevData*>(
+ mrPageWindow.GetPaintWindow().GetOutputDevice().GetExtOutDevData()));
+
+ if (nullptr != pPDFExtOutDevData)
+ {
+ return pPDFExtOutDevData->GetIsExportTaggedPDF();
+ }
+ }
+ return false;
+ }
+
+ // gray display mode
+ bool ObjectContactOfPageView::isDrawModeGray() const
+ {
+ const DrawModeFlags nDrawMode(mrPageWindow.GetPaintWindow().GetOutputDevice().GetDrawMode());
+ return (nDrawMode == (DrawModeFlags::GrayLine|DrawModeFlags::GrayFill|DrawModeFlags::BlackText|DrawModeFlags::GrayBitmap|DrawModeFlags::GrayGradient));
+ }
+
+ // high contrast display mode
+ bool ObjectContactOfPageView::isDrawModeHighContrast() const
+ {
+ const DrawModeFlags nDrawMode(mrPageWindow.GetPaintWindow().GetOutputDevice().GetDrawMode());
+ return (nDrawMode == (DrawModeFlags::SettingsLine|DrawModeFlags::SettingsFill|DrawModeFlags::SettingsText|DrawModeFlags::SettingsGradient));
+ }
+
+ // access to SdrPageView
+ SdrPageView* ObjectContactOfPageView::TryToGetSdrPageView() const
+ {
+ return &(mrPageWindow.GetPageView());
+ }
+
+
+ // access to OutputDevice
+ OutputDevice* ObjectContactOfPageView::TryToGetOutputDevice() const
+ {
+ SdrPreRenderDevice* pPreRenderDevice = mrPageWindow.GetPaintWindow().GetPreRenderDevice();
+
+ if(pPreRenderDevice)
+ {
+ return &(pPreRenderDevice->GetPreRenderDevice());
+ }
+ else
+ {
+ return &(mrPageWindow.GetPaintWindow().GetOutputDevice());
+ }
+ }
+
+ // set all UNO controls displayed in the view to design/alive mode
+ void ObjectContactOfPageView::SetUNOControlsDesignMode( bool _bDesignMode ) const
+ {
+ const sal_uInt32 nCount(getViewObjectContactCount());
+
+ for(sal_uInt32 a(0); a < nCount; a++)
+ {
+ const ViewObjectContact* pVOC = getViewObjectContact(a);
+ const ViewObjectContactOfUnoControl* pUnoObjectVOC = dynamic_cast< const ViewObjectContactOfUnoControl* >(pVOC);
+
+ if(pUnoObjectVOC)
+ {
+ pUnoObjectVOC->setControlDesignMode(_bDesignMode);
+ }
+ }
+ }
+
+} // end of namespace
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/sdr/contact/sdrmediawindow.cxx b/svx/source/sdr/contact/sdrmediawindow.cxx
new file mode 100644
index 000000000..086e12cee
--- /dev/null
+++ b/svx/source/sdr/contact/sdrmediawindow.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 "sdrmediawindow.hxx"
+#include <vcl/transfer.hxx>
+
+#include <sdr/contact/viewobjectcontactofsdrmediaobj.hxx>
+#include <vcl/window.hxx>
+#include <vcl/commandevent.hxx>
+#include <vcl/event.hxx>
+
+namespace sdr::contact {
+
+
+SdrMediaWindow::SdrMediaWindow( vcl::Window* pParent, ViewObjectContactOfSdrMediaObj& rViewObjContact ) :
+ ::avmedia::MediaWindow( pParent, false ),
+ mrViewObjectContactOfSdrMediaObj( rViewObjContact )
+{
+}
+
+
+SdrMediaWindow::~SdrMediaWindow()
+{
+}
+
+
+void SdrMediaWindow::MouseMove( const MouseEvent& rMEvt )
+{
+ vcl::Window* pWindow = mrViewObjectContactOfSdrMediaObj.getWindow();
+
+ if( pWindow && getWindow() )
+ {
+ const MouseEvent aTransformedEvent( pWindow->ScreenToOutputPixel( getWindow()->OutputToScreenPixel( rMEvt.GetPosPixel() ) ),
+ rMEvt.GetClicks(), rMEvt.GetMode(), rMEvt.GetButtons(), rMEvt.GetModifier() );
+
+ pWindow->MouseMove( aTransformedEvent );
+ setPointer( pWindow->GetPointer() );
+ }
+}
+
+
+void SdrMediaWindow::MouseButtonDown( const MouseEvent& rMEvt )
+{
+ vcl::Window* pWindow = mrViewObjectContactOfSdrMediaObj.getWindow();
+
+ if( pWindow && getWindow() )
+ {
+ const MouseEvent aTransformedEvent( pWindow->ScreenToOutputPixel( getWindow()->OutputToScreenPixel( rMEvt.GetPosPixel() ) ),
+ rMEvt.GetClicks(), rMEvt.GetMode(), rMEvt.GetButtons(), rMEvt.GetModifier() );
+
+ pWindow->MouseButtonDown( aTransformedEvent );
+ }
+}
+
+
+void SdrMediaWindow::MouseButtonUp( const MouseEvent& rMEvt )
+{
+ vcl::Window* pWindow = mrViewObjectContactOfSdrMediaObj.getWindow();
+
+ if( pWindow && getWindow() )
+ {
+ const MouseEvent aTransformedEvent( pWindow->ScreenToOutputPixel( getWindow()->OutputToScreenPixel( rMEvt.GetPosPixel() ) ),
+ rMEvt.GetClicks(), rMEvt.GetMode(), rMEvt.GetButtons(), rMEvt.GetModifier() );
+
+ pWindow->MouseButtonUp( aTransformedEvent );
+ }
+}
+
+
+void SdrMediaWindow::KeyInput( const KeyEvent& rKEvt )
+{
+ vcl::Window* pWindow = mrViewObjectContactOfSdrMediaObj.getWindow();
+
+ if( pWindow )
+ pWindow->KeyInput( rKEvt );
+}
+
+
+void SdrMediaWindow::KeyUp( const KeyEvent& rKEvt )
+{
+ vcl::Window* pWindow = mrViewObjectContactOfSdrMediaObj.getWindow();
+
+ if( pWindow )
+ pWindow->KeyUp( rKEvt );
+}
+
+
+void SdrMediaWindow::Command( const CommandEvent& rCEvt )
+{
+ vcl::Window* pWindow = mrViewObjectContactOfSdrMediaObj.getWindow();
+
+ if( pWindow && getWindow() )
+ {
+ const CommandEvent aTransformedEvent( pWindow->ScreenToOutputPixel( getWindow()->OutputToScreenPixel( rCEvt.GetMousePosPixel() ) ),
+ rCEvt.GetCommand(), rCEvt.IsMouseEvent(), rCEvt.GetEventData() );
+
+ pWindow->Command( aTransformedEvent );
+ }
+}
+
+
+sal_Int8 SdrMediaWindow::AcceptDrop( const AcceptDropEvent& rEvt )
+{
+ vcl::Window* pWindow = mrViewObjectContactOfSdrMediaObj.getWindow();
+ sal_Int8 nRet = DND_ACTION_NONE;
+
+ if( pWindow )
+ {
+ DropTargetHelper* pDropTargetHelper = dynamic_cast< DropTargetHelper* >( pWindow );
+
+ if( pDropTargetHelper )
+ {
+ nRet = pDropTargetHelper->AcceptDrop( rEvt );
+ }
+ }
+
+ return nRet;
+}
+
+
+sal_Int8 SdrMediaWindow::ExecuteDrop( const ExecuteDropEvent& rEvt )
+{
+ vcl::Window* pWindow = mrViewObjectContactOfSdrMediaObj.getWindow();
+ sal_Int8 nRet = DND_ACTION_NONE;
+
+ if( pWindow )
+ {
+ DropTargetHelper* pDropTargetHelper = dynamic_cast< DropTargetHelper* >( pWindow );
+
+ if( pDropTargetHelper )
+ {
+ nRet = pDropTargetHelper->ExecuteDrop( rEvt );
+ }
+ }
+
+ return nRet;
+}
+
+
+void SdrMediaWindow::StartDrag( sal_Int8 nAction, const Point& rPosPixel )
+{
+ vcl::Window* pWindow = mrViewObjectContactOfSdrMediaObj.getWindow();
+
+ if( pWindow )
+ {
+ DragSourceHelper* pDragSourceHelper = dynamic_cast< DragSourceHelper* >( pWindow );
+
+ if( pDragSourceHelper )
+ {
+ pDragSourceHelper->StartDrag( nAction, rPosPixel );
+ }
+ }
+}
+
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/sdr/contact/sdrmediawindow.hxx b/svx/source/sdr/contact/sdrmediawindow.hxx
new file mode 100644
index 000000000..fb3cda6fc
--- /dev/null
+++ b/svx/source/sdr/contact/sdrmediawindow.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_SVX_SOURCE_SDR_CONTACT_SDRMEDIAWINDOW_HXX
+#define INCLUDED_SVX_SOURCE_SDR_CONTACT_SDRMEDIAWINDOW_HXX
+
+#include <avmedia/mediawindow.hxx>
+
+namespace sdr::contact {
+
+
+class ViewObjectContactOfSdrMediaObj;
+
+class SdrMediaWindow : public ::avmedia::MediaWindow
+{
+public:
+
+ SdrMediaWindow( vcl::Window* pParent, ViewObjectContactOfSdrMediaObj& rViewObjContact );
+ virtual ~SdrMediaWindow() override;
+
+ virtual void MouseMove( const MouseEvent& rMEvt ) override;
+ virtual void MouseButtonDown( const MouseEvent& rMEvt ) override;
+ virtual void MouseButtonUp( const MouseEvent& rMEvt ) override;
+
+ virtual void KeyInput( const KeyEvent& rKEvt ) override;
+ virtual void KeyUp( const KeyEvent& rKEvt ) override;
+
+ virtual void Command( const CommandEvent& rCEvt ) override;
+
+ 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;
+
+private:
+
+ ViewObjectContactOfSdrMediaObj& mrViewObjectContactOfSdrMediaObj;
+};
+
+}
+
+#endif // INCLUDED_SVX_SOURCE_SDR_CONTACT_SDRMEDIAWINDOW_HXX
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/sdr/contact/viewcontact.cxx b/svx/source/sdr/contact/viewcontact.cxx
new file mode 100644
index 000000000..bd79bc5ed
--- /dev/null
+++ b/svx/source/sdr/contact/viewcontact.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 <svx/sdr/contact/viewcontact.hxx>
+#include <svx/sdr/contact/viewobjectcontact.hxx>
+#include <svx/sdr/contact/objectcontact.hxx>
+#include <basegfx/polygon/b2dpolygon.hxx>
+#include <basegfx/polygon/b2dpolygontools.hxx>
+#include <basegfx/color/bcolor.hxx>
+#include <drawinglayer/primitive2d/PolygonHairlinePrimitive2D.hxx>
+#include <osl/diagnose.h>
+#include <tools/debug.hxx>
+
+namespace sdr::contact
+{
+// 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
+ViewObjectContact& ViewContact::CreateObjectSpecificViewObjectContact(ObjectContact& rObjectContact)
+{
+ return *(new ViewObjectContact(rObjectContact, *this));
+}
+
+ViewContact::ViewContact() {}
+
+ViewContact::~ViewContact() { deleteAllVOCs(); }
+
+void ViewContact::deleteAllVOCs()
+{
+ // get rid of all VOCs
+ // #i84257# To avoid that each 'delete pCandidate' again uses
+ // the local RemoveViewObjectContact with a search and removal in the
+ // vector, simply copy and clear local vector.
+ std::vector<ViewObjectContact*> aLocalVOCList;
+ aLocalVOCList.swap(maViewObjectContactVector);
+
+ for (const auto& pCandidate : aLocalVOCList)
+ // ViewObjectContacts only make sense with View and Object contacts.
+ // When the contact to the SdrObject is deleted like in this case,
+ // all ViewObjectContacts can be deleted, too.
+ delete pCandidate;
+
+ // assert when there were new entries added during deletion
+ DBG_ASSERT(maViewObjectContactVector.empty(), "Corrupted ViewObjectContactList in VC (!)");
+}
+
+// get an Object-specific ViewObjectContact for a specific
+// ObjectContact (->View). Always needs to return something.
+ViewObjectContact& ViewContact::GetViewObjectContact(ObjectContact& rObjectContact)
+{
+ ViewObjectContact* pRetval = nullptr;
+ const sal_uInt32 nCount(maViewObjectContactVector.size());
+
+ // first search if there exists a VOC for the given OC
+ for (sal_uInt32 a(0); !pRetval && a < nCount; a++)
+ {
+ ViewObjectContact* pCandidate = maViewObjectContactVector[a];
+ DBG_ASSERT(pCandidate, "Corrupted ViewObjectContactList (!)");
+
+ if (&(pCandidate->GetObjectContact()) == &rObjectContact)
+ {
+ pRetval = pCandidate;
+ }
+ }
+
+ if (!pRetval)
+ {
+ // create a new one. It's inserted to the local list from the
+ // ViewObjectContact constructor via AddViewObjectContact()
+ pRetval = &CreateObjectSpecificViewObjectContact(rObjectContact);
+ }
+
+ return *pRetval;
+}
+
+// A new ViewObjectContact was created and shall be remembered.
+void ViewContact::AddViewObjectContact(ViewObjectContact& rVOContact)
+{
+ maViewObjectContactVector.push_back(&rVOContact);
+}
+
+// A ViewObjectContact was deleted and shall be forgotten.
+void ViewContact::RemoveViewObjectContact(ViewObjectContact& rVOContact)
+{
+ std::vector<ViewObjectContact*>::iterator aFindResult = std::find(
+ maViewObjectContactVector.begin(), maViewObjectContactVector.end(), &rVOContact);
+
+ if (aFindResult != maViewObjectContactVector.end())
+ {
+ maViewObjectContactVector.erase(aFindResult);
+ }
+}
+
+// Test if this ViewContact has ViewObjectContacts at all. This can
+// be used to test if this ViewContact is visualized ATM or not
+bool ViewContact::HasViewObjectContacts() const
+{
+ const sal_uInt32 nCount(maViewObjectContactVector.size());
+
+ for (sal_uInt32 a(0); a < nCount; a++)
+ {
+ if (!maViewObjectContactVector[a]->GetObjectContact().IsPreviewRenderer())
+ {
+ return true;
+ }
+ }
+ return false;
+}
+
+// Test if this ViewContact has ViewObjectContacts at all. This can
+// be used to test if this ViewContact is visualized ATM or not
+bool ViewContact::isAnimatedInAnyViewObjectContact() const
+{
+ const sal_uInt32 nCount(maViewObjectContactVector.size());
+
+ for (sal_uInt32 a(0); a < nCount; a++)
+ {
+ if (maViewObjectContactVector[a]->isAnimated())
+ {
+ return true;
+ }
+ }
+
+ return false;
+}
+
+// Access to possible sub-hierarchy and parent. GetObjectCount() default is 0L
+// and GetViewContact default pops up an assert since it's an error if
+// GetObjectCount has a result != 0 and it's not overridden.
+sal_uInt32 ViewContact::GetObjectCount() const
+{
+ // no sub-objects
+ return 0;
+}
+
+ViewContact& ViewContact::GetViewContact(sal_uInt32 /*nIndex*/) const
+{
+ // This is the default implementation; call would be an error
+ OSL_FAIL("ViewContact::GetViewContact: This call needs to be overridden when GetObjectCount() "
+ "can return results != 0 (!)");
+ return const_cast<ViewContact&>(*this);
+}
+
+ViewContact* ViewContact::GetParentContact() const
+{
+ // default has no parent
+ return nullptr;
+}
+
+void ViewContact::ActionChildInserted(ViewContact& rChild)
+{
+ // propagate change to all existing visualisations which
+ // will force a VOC for the new child and invalidate its range
+ const sal_uInt32 nCount(maViewObjectContactVector.size());
+
+ for (sal_uInt32 a(0); a < nCount; a++)
+ {
+ ViewObjectContact* pCandidate = maViewObjectContactVector[a];
+ DBG_ASSERT(pCandidate,
+ "ViewContact::GetViewObjectContact() invalid ViewObjectContactList (!)");
+
+ // take action at all VOCs. At the VOCs ObjectContact the initial
+ // rectangle will be invalidated at the associated OutputDevice.
+ pCandidate->ActionChildInserted(rChild);
+ }
+}
+
+// React on changes of the object of this ViewContact
+void ViewContact::ActionChanged()
+{
+ // propagate change to all existing VOCs. This will invalidate
+ // all drawn visualisations in all known views
+ const sal_uInt32 nCount(maViewObjectContactVector.size());
+
+ for (sal_uInt32 a(0); a < nCount; a++)
+ {
+ ViewObjectContact* pCandidate = maViewObjectContactVector[a];
+ DBG_ASSERT(pCandidate,
+ "ViewContact::GetViewObjectContact() invalid ViewObjectContactList (!)");
+
+ if (pCandidate)
+ {
+ pCandidate->ActionChanged();
+ }
+ }
+}
+
+// access to SdrObject and/or SdrPage. May return 0L like the default
+// implementations do. Override as needed.
+SdrObject* ViewContact::TryToGetSdrObject() const { return nullptr; }
+
+// primitive stuff
+
+void ViewContact::createViewIndependentPrimitive2DSequence(
+ drawinglayer::primitive2d::Primitive2DDecompositionVisitor& rVisitor) const
+{
+ // This is the default implementation and should never be called (see header). If this is called,
+ // someone implemented a ViewContact (VC) visualisation object without defining the visualisation by
+ // providing a sequence of primitives -> which cannot be correct.
+ // Since we have no access to any known model data here, the default implementation creates a yellow placeholder
+ // hairline polygon with a default size of (1000, 1000, 5000, 3000)
+ OSL_FAIL("ViewContact::createViewIndependentPrimitive2DSequence(): Never call the fallback "
+ "base implementation, this is always an error (!)");
+ const basegfx::B2DPolygon aOutline(
+ basegfx::utils::createPolygonFromRect(basegfx::B2DRange(1000.0, 1000.0, 5000.0, 3000.0)));
+ const basegfx::BColor aYellow(1.0, 1.0, 0.0);
+ const drawinglayer::primitive2d::Primitive2DReference xReference(
+ new drawinglayer::primitive2d::PolygonHairlinePrimitive2D(aOutline, aYellow));
+
+ rVisitor.visit(xReference);
+}
+
+void ViewContact::getViewIndependentPrimitive2DContainer(
+ drawinglayer::primitive2d::Primitive2DDecompositionVisitor& rVisitor) const
+{
+ /* Local up-to-date checks. Create new list and compare.
+ We cannot just always use the new data because the old data has cached bitmaps in it e.g. see the document in tdf#146108.
+ */
+ drawinglayer::primitive2d::Primitive2DContainer xNew;
+ createViewIndependentPrimitive2DSequence(xNew);
+
+ if (!xNew.empty())
+ {
+ // allow evtl. embedding in object-specific infos, e.g. Name, Title, Description
+ xNew = embedToObjectSpecificInformation(std::move(xNew));
+ }
+
+ if (mxViewIndependentPrimitive2DSequence != xNew)
+ {
+ // has changed, copy content
+ const_cast<ViewContact*>(this)->mxViewIndependentPrimitive2DSequence = std::move(xNew);
+ }
+
+ // return current Primitive2DContainer
+ rVisitor.visit(mxViewIndependentPrimitive2DSequence);
+}
+
+// add Gluepoints (if available)
+drawinglayer::primitive2d::Primitive2DContainer
+ViewContact::createGluePointPrimitive2DSequence() const
+{
+ // default returns empty reference
+ return drawinglayer::primitive2d::Primitive2DContainer();
+}
+
+drawinglayer::primitive2d::Primitive2DContainer ViewContact::embedToObjectSpecificInformation(
+ drawinglayer::primitive2d::Primitive2DContainer aSource) const
+{
+ // nothing to do for default
+ return aSource;
+}
+
+basegfx::B2DRange
+ViewContact::getRange(const drawinglayer::geometry::ViewInformation2D& /*rViewInfo2D*/) const
+{
+ // Return empty range.
+ return basegfx::B2DRange();
+}
+
+void ViewContact::flushViewObjectContacts(bool bWithHierarchy)
+{
+ if (bWithHierarchy)
+ {
+ // flush DrawingLayer hierarchy
+ const sal_uInt32 nCount(GetObjectCount());
+
+ for (sal_uInt32 a(0); a < nCount; a++)
+ {
+ ViewContact& rChild = GetViewContact(a);
+ rChild.flushViewObjectContacts(bWithHierarchy);
+ }
+ }
+
+ // delete local VOCs
+ deleteAllVOCs();
+}
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/sdr/contact/viewcontactofe3d.cxx b/svx/source/sdr/contact/viewcontactofe3d.cxx
new file mode 100644
index 000000000..a504038ce
--- /dev/null
+++ b/svx/source/sdr/contact/viewcontactofe3d.cxx
@@ -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 .
+ */
+
+#include <sdr/contact/viewcontactofe3d.hxx>
+#include <sdr/contact/viewobjectcontactofe3d.hxx>
+#include <svx/obj3d.hxx>
+#include <drawinglayer/primitive2d/embedded3dprimitive2d.hxx>
+#include <svx/sdr/contact/viewcontactofe3dscene.hxx>
+#include <svx/scene3d.hxx>
+#include <drawinglayer/primitive3d/transformprimitive3d.hxx>
+#include <drawinglayer/attribute/sdrsceneattribute3d.hxx>
+#include <drawinglayer/attribute/sdrlightingattribute3d.hxx>
+#include <drawinglayer/attribute/sdrlightattribute3d.hxx>
+
+namespace {
+
+const sdr::contact::ViewContactOfE3dScene* tryToFindVCOfE3DScene(
+ const sdr::contact::ViewContact& rCandidate,
+ basegfx::B3DHomMatrix& o_rInBetweenObjectTransform)
+{
+ const sdr::contact::ViewContactOfE3dScene* pSceneParent =
+ dynamic_cast< const sdr::contact::ViewContactOfE3dScene* >(rCandidate.GetParentContact());
+
+ if(pSceneParent)
+ {
+ // each 3d object (including in-between scenes) should have a scene as parent
+ const sdr::contact::ViewContactOfE3dScene* pSceneParentParent =
+ dynamic_cast< const sdr::contact::ViewContactOfE3dScene* >(pSceneParent->GetParentContact());
+
+ if(pSceneParentParent)
+ {
+ // the parent scene of rCandidate is an in-between scene, call recursively and collect
+ // the in-between scene's object transformation part in o_rInBetweenObjectTransform
+ const basegfx::B3DHomMatrix& rSceneParentTransform = pSceneParent->GetE3dScene().GetTransform();
+ o_rInBetweenObjectTransform = rSceneParentTransform * o_rInBetweenObjectTransform;
+ return tryToFindVCOfE3DScene(*pSceneParent, o_rInBetweenObjectTransform);
+ }
+ else
+ {
+ // the parent scene is the outmost scene
+ return pSceneParent;
+ }
+ }
+
+ // object hierarchy structure is incorrect; no result
+ return nullptr;
+}
+
+} // end of anonymous namespace
+
+namespace sdr::contact {
+
+drawinglayer::primitive2d::Primitive2DContainer ViewContactOfE3d::impCreateWithGivenPrimitive3DContainer(
+ const drawinglayer::primitive3d::Primitive3DContainer& rxContent3D) const
+{
+ drawinglayer::primitive2d::Primitive2DContainer xRetval;
+
+ if(!rxContent3D.empty())
+ {
+ // try to get the outmost ViewObjectContactOfE3dScene for this single 3d object,
+ // the ones on the way there are grouping scenes. Collect the in-between scene's
+ // transformations to build a correct object transformation for the embedded
+ // object
+ basegfx::B3DHomMatrix aInBetweenObjectTransform;
+ const ViewContactOfE3dScene* pVCOfE3DScene = tryToFindVCOfE3DScene(*this, aInBetweenObjectTransform);
+
+ if(pVCOfE3DScene)
+ {
+ basegfx::B3DVector aLightNormal;
+ const double fShadowSlant(pVCOfE3DScene->getSdrSceneAttribute().getShadowSlant());
+ const basegfx::B3DRange& rAllContentRange = pVCOfE3DScene->getAllContentRange3D();
+ drawinglayer::geometry::ViewInformation3D aViewInformation3D(pVCOfE3DScene->getViewInformation3D());
+
+ if(!pVCOfE3DScene->getSdrLightingAttribute().getLightVector().empty())
+ {
+ // get light normal from first light and normalize
+ aLightNormal = pVCOfE3DScene->getSdrLightingAttribute().getLightVector()[0].getDirection();
+ aLightNormal.normalize();
+ }
+
+ if(!aInBetweenObjectTransform.isIdentity())
+ {
+ // if aInBetweenObjectTransform is used, create combined ViewInformation3D which
+ // contains the correct object transformation for the embedded 3d object
+ aViewInformation3D = drawinglayer::geometry::ViewInformation3D(
+ aViewInformation3D.getObjectTransformation() * aInBetweenObjectTransform,
+ aViewInformation3D.getOrientation(),
+ aViewInformation3D.getProjection(),
+ aViewInformation3D.getDeviceToView(),
+ aViewInformation3D.getViewTime(),
+ aViewInformation3D.getExtendedInformationSequence());
+ }
+
+ // create embedded 2d primitive and add. LightNormal and ShadowSlant are needed for evtl.
+ // 3D shadow extraction for correct B2DRange calculation (shadow is part of the object)
+ const drawinglayer::primitive2d::Primitive2DReference xReference(
+ new drawinglayer::primitive2d::Embedded3DPrimitive2D(
+ rxContent3D,
+ pVCOfE3DScene->getObjectTransformation(),
+ aViewInformation3D,
+ aLightNormal,
+ fShadowSlant,
+ rAllContentRange));
+
+ xRetval = drawinglayer::primitive2d::Primitive2DContainer { xReference };
+ }
+ }
+
+ return xRetval;
+}
+
+ViewContactOfE3d::ViewContactOfE3d(E3dObject& rSdrObject)
+: ViewContactOfSdrObj(rSdrObject)
+{
+}
+
+ViewContactOfE3d::~ViewContactOfE3d()
+{
+}
+
+drawinglayer::primitive3d::Primitive3DContainer const & ViewContactOfE3d::getVIP3DSWithoutObjectTransform() const
+{
+ // local up-to-date checks. Create new list and compare.
+ drawinglayer::primitive3d::Primitive3DContainer xNew(createViewIndependentPrimitive3DContainer());
+
+ if(mxViewIndependentPrimitive3DContainer != xNew)
+ {
+ // has changed, copy content
+ const_cast< ViewContactOfE3d* >(this)->mxViewIndependentPrimitive3DContainer = xNew;
+ }
+
+ // return current Primitive2DContainer
+ return mxViewIndependentPrimitive3DContainer;
+}
+
+drawinglayer::primitive3d::Primitive3DContainer ViewContactOfE3d::getViewIndependentPrimitive3DContainer() const
+{
+ // get sequence without object transform
+ drawinglayer::primitive3d::Primitive3DContainer xRetval(getVIP3DSWithoutObjectTransform());
+
+ if(!xRetval.empty())
+ {
+ // add object transform if it's used
+ const basegfx::B3DHomMatrix& rObjectTransform(GetE3dObject().GetTransform());
+
+ if(!rObjectTransform.isIdentity())
+ {
+ const drawinglayer::primitive3d::Primitive3DReference xReference(
+ new drawinglayer::primitive3d::TransformPrimitive3D(
+ rObjectTransform,
+ xRetval));
+
+ xRetval = { xReference };
+ }
+ }
+
+ // return current Primitive2DContainer
+ return xRetval;
+}
+
+void ViewContactOfE3d::createViewIndependentPrimitive2DSequence(drawinglayer::primitive2d::Primitive2DDecompositionVisitor& rVisitor) const
+{
+ // also need to create a 2D embedding when the view-independent part is requested,
+ // see view-dependent part in ViewObjectContactOfE3d::createPrimitive2DSequence
+ // get 3d primitive vector, isPrimitiveVisible() is done in 3d creator
+ return rVisitor.visit(impCreateWithGivenPrimitive3DContainer(getViewIndependentPrimitive3DContainer()));
+}
+
+ViewObjectContact& ViewContactOfE3d::CreateObjectSpecificViewObjectContact(ObjectContact& rObjectContact)
+{
+ ViewObjectContact* pRetval = new ViewObjectContactOfE3d(rObjectContact, *this);
+ DBG_ASSERT(pRetval, "ViewContactOfE3d::CreateObjectSpecificViewObjectContact() failed (!)");
+
+ return *pRetval;
+}
+
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/sdr/contact/viewcontactofe3dcube.cxx b/svx/source/sdr/contact/viewcontactofe3dcube.cxx
new file mode 100644
index 000000000..2687aab32
--- /dev/null
+++ b/svx/source/sdr/contact/viewcontactofe3dcube.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 <sdr/contact/viewcontactofe3dcube.hxx>
+#include <svx/cube3d.hxx>
+#include <drawinglayer/primitive3d/sdrcubeprimitive3d.hxx>
+#include <sdr/primitive2d/sdrattributecreator.hxx>
+#include <sdr/primitive3d/sdrattributecreator3d.hxx>
+#include <basegfx/range/b3drange.hxx>
+
+
+namespace sdr::contact
+{
+ ViewContactOfE3dCube::ViewContactOfE3dCube(E3dCubeObj& rCubeObj)
+ : ViewContactOfE3d(rCubeObj)
+ {
+ }
+
+ ViewContactOfE3dCube::~ViewContactOfE3dCube()
+ {
+ }
+
+ drawinglayer::primitive3d::Primitive3DContainer ViewContactOfE3dCube::createViewIndependentPrimitive3DContainer() const
+ {
+ drawinglayer::primitive3d::Primitive3DContainer xRetval;
+ const SfxItemSet& rItemSet = GetE3dCubeObj().GetMergedItemSet();
+ const drawinglayer::attribute::SdrLineFillShadowAttribute3D aAttribute(
+ drawinglayer::primitive2d::createNewSdrLineFillShadowAttribute(rItemSet, false));
+
+ // get cube geometry and use as translation and scaling for unit cube
+ basegfx::B3DRange aCubeRange;
+ const basegfx::B3DVector aCubeSize(GetE3dCubeObj().GetCubeSize());
+ const basegfx::B3DPoint aCubePosition(GetE3dCubeObj().GetCubePos());
+ basegfx::B3DHomMatrix aWorldTransform;
+
+ if(GetE3dCubeObj().GetPosIsCenter())
+ {
+ const basegfx::B3DVector aHalfCubeSize(aCubeSize / 2.0);
+ aCubeRange.expand(aCubePosition - aHalfCubeSize);
+ aCubeRange.expand(aCubePosition + aHalfCubeSize);
+ }
+ else
+ {
+ aCubeRange.expand(aCubePosition);
+ aCubeRange.expand(aCubePosition + aCubeSize);
+ }
+
+ // add scale and translate to world transformation
+ const basegfx::B3DVector abjectRange(aCubeRange.getRange());
+ aWorldTransform.scale(abjectRange.getX(), abjectRange.getY(), abjectRange.getZ());
+ aWorldTransform.translate(aCubeRange.getMinX(), aCubeRange.getMinY(), aCubeRange.getMinZ());
+
+ // get 3D Object Attributes
+ drawinglayer::attribute::Sdr3DObjectAttribute aSdr3DObjectAttribute(drawinglayer::primitive2d::createNewSdr3DObjectAttribute(rItemSet));
+
+ // calculate texture size to get a perfect mapping for
+ // the front/back sides
+ const basegfx::B2DVector aTextureSize(aCubeSize.getX(), aCubeSize.getY());
+
+ // create primitive and add
+ const drawinglayer::primitive3d::Primitive3DReference xReference(
+ new drawinglayer::primitive3d::SdrCubePrimitive3D(
+ aWorldTransform, aTextureSize, aAttribute, aSdr3DObjectAttribute));
+ xRetval = { xReference };
+
+ return xRetval;
+ }
+
+} // end of namespace
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/sdr/contact/viewcontactofe3dextrude.cxx b/svx/source/sdr/contact/viewcontactofe3dextrude.cxx
new file mode 100644
index 000000000..71100ae34
--- /dev/null
+++ b/svx/source/sdr/contact/viewcontactofe3dextrude.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 <sdr/contact/viewcontactofe3dextrude.hxx>
+#include <extrud3d.hxx>
+#include <drawinglayer/primitive3d/sdrextrudeprimitive3d.hxx>
+#include <sdr/primitive2d/sdrattributecreator.hxx>
+#include <sdr/primitive3d/sdrattributecreator3d.hxx>
+#include <basegfx/polygon/b2dpolypolygontools.hxx>
+
+
+namespace sdr::contact
+{
+ ViewContactOfE3dExtrude::ViewContactOfE3dExtrude(E3dExtrudeObj& rExtrude)
+ : ViewContactOfE3d(rExtrude)
+ {
+ }
+
+ ViewContactOfE3dExtrude::~ViewContactOfE3dExtrude()
+ {
+ }
+
+ drawinglayer::primitive3d::Primitive3DContainer ViewContactOfE3dExtrude::createViewIndependentPrimitive3DContainer() const
+ {
+ drawinglayer::primitive3d::Primitive3DContainer xRetval;
+ const SfxItemSet& rItemSet = GetE3dExtrudeObj().GetMergedItemSet();
+ const drawinglayer::attribute::SdrLineFillShadowAttribute3D aAttribute(
+ drawinglayer::primitive2d::createNewSdrLineFillShadowAttribute(rItemSet, false));
+
+ // get extrude geometry
+ const basegfx::B2DPolyPolygon aPolyPolygon(GetE3dExtrudeObj().GetExtrudePolygon());
+
+ // get 3D Object Attributes
+ drawinglayer::attribute::Sdr3DObjectAttribute aSdr3DObjectAttribute(drawinglayer::primitive2d::createNewSdr3DObjectAttribute(rItemSet));
+
+ // calculate texture size; use size of top/bottom cap to get a perfect mapping
+ // for the caps. The in-between geometry will get a stretched size with a
+ // relative factor size of caps to extrude depth
+ const basegfx::B2DRange aRange(basegfx::utils::getRange(aPolyPolygon));
+ const basegfx::B2DVector aTextureSize(aRange.getWidth(), aRange.getHeight());
+
+ // get more data
+ const double fDepth(static_cast<double>(GetE3dExtrudeObj().GetExtrudeDepth()));
+ const double fDiagonal(static_cast<double>(GetE3dExtrudeObj().GetPercentDiagonal()) / 100.0);
+ const double fBackScale(static_cast<double>(GetE3dExtrudeObj().GetPercentBackScale()) / 100.0);
+ const bool bSmoothNormals(GetE3dExtrudeObj().GetSmoothNormals()); // Plane itself
+ const bool bSmoothLids(GetE3dExtrudeObj().GetSmoothLids()); // Front/back
+ const bool bCharacterMode(GetE3dExtrudeObj().GetCharacterMode());
+ const bool bCloseFront(GetE3dExtrudeObj().GetCloseFront());
+ const bool bCloseBack(GetE3dExtrudeObj().GetCloseBack());
+
+ // create primitive and add
+ const basegfx::B3DHomMatrix aWorldTransform;
+ const drawinglayer::primitive3d::Primitive3DReference xReference(
+ new drawinglayer::primitive3d::SdrExtrudePrimitive3D(
+ aWorldTransform, aTextureSize, aAttribute, aSdr3DObjectAttribute,
+ aPolyPolygon, fDepth, fDiagonal, fBackScale, bSmoothNormals, bSmoothLids,
+ bCharacterMode, bCloseFront, bCloseBack));
+ xRetval = { xReference };
+
+ return xRetval;
+ }
+
+} // end of namespace
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/sdr/contact/viewcontactofe3dlathe.cxx b/svx/source/sdr/contact/viewcontactofe3dlathe.cxx
new file mode 100644
index 000000000..a3e7086b8
--- /dev/null
+++ b/svx/source/sdr/contact/viewcontactofe3dlathe.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 <sdr/contact/viewcontactofe3dlathe.hxx>
+#include <svx/lathe3d.hxx>
+#include <drawinglayer/primitive3d/sdrlatheprimitive3d.hxx>
+#include <sdr/primitive2d/sdrattributecreator.hxx>
+#include <sdr/primitive3d/sdrattributecreator3d.hxx>
+#include <basegfx/polygon/b2dpolygontools.hxx>
+
+
+namespace sdr::contact
+{
+ ViewContactOfE3dLathe::ViewContactOfE3dLathe(E3dLatheObj& rLathe)
+ : ViewContactOfE3d(rLathe)
+ {
+ }
+
+ ViewContactOfE3dLathe::~ViewContactOfE3dLathe()
+ {
+ }
+
+ drawinglayer::primitive3d::Primitive3DContainer ViewContactOfE3dLathe::createViewIndependentPrimitive3DContainer() const
+ {
+ drawinglayer::primitive3d::Primitive3DContainer xRetval;
+ const SfxItemSet& rItemSet = GetE3dLatheObj().GetMergedItemSet();
+ const drawinglayer::attribute::SdrLineFillShadowAttribute3D aAttribute(
+ drawinglayer::primitive2d::createNewSdrLineFillShadowAttribute(rItemSet, false));
+
+ // get extrude geometry
+ const basegfx::B2DPolyPolygon aPolyPolygon(GetE3dLatheObj().GetPolyPoly2D());
+
+ // get 3D Object Attributes
+ drawinglayer::attribute::Sdr3DObjectAttribute aSdr3DObjectAttribute(drawinglayer::primitive2d::createNewSdr3DObjectAttribute(rItemSet));
+
+ // calculate texture size. Use the polygon length of the longest polygon for
+ // height and the rotated radius for width (using polygon center) to get a good
+ // texture mapping
+ double fPolygonMaxLength(0.0);
+
+ for(auto const& rCandidate : aPolyPolygon)
+ {
+ const double fPolygonLength(basegfx::utils::getLength(rCandidate));
+ fPolygonMaxLength = std::max(fPolygonMaxLength, fPolygonLength);
+ }
+
+ const basegfx::B2DRange aPolyPolygonRange(basegfx::utils::getRange(aPolyPolygon));
+ const basegfx::B2DVector aTextureSize(
+ M_PI * fabs(aPolyPolygonRange.getCenter().getX()), // PI * d
+ fPolygonMaxLength);
+
+ // get more data
+ const sal_uInt32 nHorizontalSegments(GetE3dLatheObj().GetHorizontalSegments());
+ const sal_uInt32 nVerticalSegments(GetE3dLatheObj().GetVerticalSegments());
+ const double fDiagonal(static_cast<double>(GetE3dLatheObj().GetPercentDiagonal()) / 100.0);
+ const double fBackScale(static_cast<double>(GetE3dLatheObj().GetBackScale()) / 100.0);
+ const double fRotation(basegfx::deg2rad<10>(GetE3dLatheObj().GetEndAngle()));
+ const bool bSmoothNormals(GetE3dLatheObj().GetSmoothNormals()); // Plane itself
+ const bool bSmoothLids(GetE3dLatheObj().GetSmoothLids()); // Front/back
+ const bool bCharacterMode(GetE3dLatheObj().GetCharacterMode());
+ const bool bCloseFront(GetE3dLatheObj().GetCloseFront());
+ const bool bCloseBack(GetE3dLatheObj().GetCloseBack());
+
+ // create primitive and add
+ const basegfx::B3DHomMatrix aWorldTransform;
+ const drawinglayer::primitive3d::Primitive3DReference xReference(
+ new drawinglayer::primitive3d::SdrLathePrimitive3D(
+ aWorldTransform, aTextureSize, aAttribute, aSdr3DObjectAttribute,
+ aPolyPolygon, nHorizontalSegments, nVerticalSegments,
+ fDiagonal, fBackScale, fRotation,
+ bSmoothNormals, bSmoothLids, bCharacterMode, bCloseFront, bCloseBack));
+ xRetval = { xReference };
+
+ return xRetval;
+ }
+
+} // end of namespace
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/sdr/contact/viewcontactofe3dpolygon.cxx b/svx/source/sdr/contact/viewcontactofe3dpolygon.cxx
new file mode 100644
index 000000000..2951ead56
--- /dev/null
+++ b/svx/source/sdr/contact/viewcontactofe3dpolygon.cxx
@@ -0,0 +1,169 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+
+#include <sdr/contact/viewcontactofe3dpolygon.hxx>
+#include <polygn3d.hxx>
+#include <drawinglayer/primitive3d/sdrpolypolygonprimitive3d.hxx>
+#include <sdr/primitive2d/sdrattributecreator.hxx>
+#include <sdr/primitive3d/sdrattributecreator3d.hxx>
+#include <basegfx/polygon/b3dpolygon.hxx>
+#include <basegfx/polygon/b3dpolypolygontools.hxx>
+
+
+namespace sdr::contact
+{
+ ViewContactOfE3dPolygon::ViewContactOfE3dPolygon(E3dPolygonObj& rPolygon)
+ : ViewContactOfE3d(rPolygon)
+ {
+ }
+
+ ViewContactOfE3dPolygon::~ViewContactOfE3dPolygon()
+ {
+ }
+
+ drawinglayer::primitive3d::Primitive3DContainer ViewContactOfE3dPolygon::createViewIndependentPrimitive3DContainer() const
+ {
+ drawinglayer::primitive3d::Primitive3DContainer xRetval;
+ const SfxItemSet& rItemSet = GetE3dPolygonObj().GetMergedItemSet();
+ const bool bSuppressFill(GetE3dPolygonObj().GetLineOnly());
+ const drawinglayer::attribute::SdrLineFillShadowAttribute3D aAttribute(
+ drawinglayer::primitive2d::createNewSdrLineFillShadowAttribute(rItemSet, bSuppressFill));
+
+ // get extrude geometry
+ basegfx::B3DPolyPolygon aPolyPolygon3D(GetE3dPolygonObj().GetPolyPolygon3D());
+ const basegfx::B3DPolyPolygon aPolyNormals3D(GetE3dPolygonObj().GetPolyNormals3D());
+ const basegfx::B2DPolyPolygon aPolyTexture2D(GetE3dPolygonObj().GetPolyTexture2D());
+ const bool bNormals(aPolyNormals3D.count() && aPolyNormals3D.count() == aPolyPolygon3D.count());
+ const bool bTexture(aPolyTexture2D.count() && aPolyTexture2D.count() == aPolyPolygon3D.count());
+
+ if(bNormals || bTexture)
+ {
+ for(sal_uInt32 a(0); a < aPolyPolygon3D.count(); a++)
+ {
+ basegfx::B3DPolygon aCandidate3D(aPolyPolygon3D.getB3DPolygon(a));
+ basegfx::B3DPolygon aNormals3D;
+ basegfx::B2DPolygon aTexture2D;
+
+ if(bNormals)
+ {
+ aNormals3D = aPolyNormals3D.getB3DPolygon(a);
+ }
+
+ if(bTexture)
+ {
+ aTexture2D = aPolyTexture2D.getB2DPolygon(a);
+ }
+
+ for(sal_uInt32 b(0); b < aCandidate3D.count(); b++)
+ {
+ if(bNormals)
+ {
+ sal_uInt32 nNormalCount = aNormals3D.count();
+ if( b < nNormalCount )
+ aCandidate3D.setNormal(b, aNormals3D.getB3DPoint(b));
+ else if( nNormalCount > 0 )
+ aCandidate3D.setNormal(b, aNormals3D.getB3DPoint(0));
+ }
+ if(bTexture)
+ {
+ sal_uInt32 nTextureCount = aTexture2D.count();
+ if( b < nTextureCount )
+ aCandidate3D.setTextureCoordinate(b, aTexture2D.getB2DPoint(b));
+ else if( nTextureCount > 0 )
+ aCandidate3D.setTextureCoordinate(b, aTexture2D.getB2DPoint(0));
+ }
+ }
+
+ aPolyPolygon3D.setB3DPolygon(a, aCandidate3D);
+ }
+ }
+
+ // get 3D Object Attributes
+ drawinglayer::attribute::Sdr3DObjectAttribute aSdr3DObjectAttribute(drawinglayer::primitive2d::createNewSdr3DObjectAttribute(rItemSet));
+
+ // calculate texture size
+ basegfx::B2DVector aTextureSize(1.0, 1.0);
+
+ if(bTexture)
+ {
+ // #i98314#
+ // create texture size from object's size
+ const basegfx::B3DRange aObjectRange(basegfx::utils::getRange(aPolyPolygon3D));
+
+ double fWidth(0.0);
+ double fHeight(0.0);
+
+ // this is a polygon object, so Width/Height and/or Depth may be zero (e.g. left
+ // wall of chart). Take this into account
+ if(basegfx::fTools::equalZero(aObjectRange.getWidth()))
+ {
+ // width is zero, use height and depth
+ fWidth = aObjectRange.getHeight();
+ fHeight = aObjectRange.getDepth();
+ }
+ else if(basegfx::fTools::equalZero(aObjectRange.getHeight()))
+ {
+ // height is zero, use width and depth
+ fWidth = aObjectRange.getWidth();
+ fHeight = aObjectRange.getDepth();
+ }
+ else
+ {
+ // use width and height
+ fWidth = aObjectRange.getWidth();
+ fHeight = aObjectRange.getHeight();
+ }
+
+ if(basegfx::fTools::lessOrEqual(fWidth, 0.0) ||basegfx::fTools::lessOrEqual(fHeight, 0.0))
+ {
+ // no texture; fallback to very small size
+ aTextureSize.setX(0.01);
+ aTextureSize.setY(0.01);
+ }
+ else
+ {
+ aTextureSize.setX(fWidth);
+ aTextureSize.setY(fHeight);
+ }
+ }
+
+ // #i98295#
+ // unfortunately, this SdrObject type which allows a free 3d geometry definition was defined
+ // wrong topologically in relation to its plane normal and 3D visibility when it was invented
+ // a long time ago. Since the API allows creation of this SDrObject type, it is not possible to
+ // simply change this definition. Only the chart should use it, and at least this object type
+ // only exists at Runtime (is not saved and/or loaded in any FileFormat). Still someone external
+ // may have used it in its API. To not risk wrong 3D lightings, I have to switch the orientation
+ // of the polygon here
+ aPolyPolygon3D.flip();
+
+ // create primitive and add
+ const basegfx::B3DHomMatrix aWorldTransform;
+ const drawinglayer::primitive3d::Primitive3DReference xReference(
+ new drawinglayer::primitive3d::SdrPolyPolygonPrimitive3D(
+ aPolyPolygon3D, aWorldTransform, aTextureSize, aAttribute, aSdr3DObjectAttribute));
+ xRetval = { xReference };
+
+ return xRetval;
+ }
+
+} // end of namespace
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/sdr/contact/viewcontactofe3dscene.cxx b/svx/source/sdr/contact/viewcontactofe3dscene.cxx
new file mode 100644
index 000000000..6bae55263
--- /dev/null
+++ b/svx/source/sdr/contact/viewcontactofe3dscene.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 <svx/sdr/contact/viewcontactofe3dscene.hxx>
+#include <svx/sdr/contact/viewobjectcontact.hxx>
+#include <sdr/primitive2d/sdrattributecreator.hxx>
+#include <sdr/contact/viewobjectcontactofe3dscene.hxx>
+#include <basegfx/matrix/b2dhommatrix.hxx>
+#include <basegfx/range/b3drange.hxx>
+#include <drawinglayer/primitive3d/baseprimitive3d.hxx>
+#include <sdr/contact/viewcontactofe3d.hxx>
+#include <drawinglayer/primitive2d/sceneprimitive2d.hxx>
+#include <drawinglayer/primitive3d/transformprimitive3d.hxx>
+#include <drawinglayer/primitive2d/sdrdecompositiontools2d.hxx>
+
+using namespace com::sun::star;
+
+namespace {
+
+// pActiveVC is only true if ghosted is still activated and maybe needs to be switched off in this path
+void createSubPrimitive3DVector(
+ const sdr::contact::ViewContact& rCandidate,
+ drawinglayer::primitive3d::Primitive3DContainer& o_rAllTarget,
+ drawinglayer::primitive3d::Primitive3DContainer* o_pVisibleTarget,
+ const SdrLayerIDSet* pVisibleSdrLayerIDSet,
+ const bool bTestSelectedVisibility)
+{
+ const sdr::contact::ViewContactOfE3dScene* pViewContactOfE3dScene = dynamic_cast< const sdr::contact::ViewContactOfE3dScene* >(&rCandidate);
+
+ if(pViewContactOfE3dScene)
+ {
+ const sal_uInt32 nChildrenCount(rCandidate.GetObjectCount());
+
+ if(nChildrenCount)
+ {
+ // provide new collection sequences
+ drawinglayer::primitive3d::Primitive3DContainer aNewAllTarget;
+ drawinglayer::primitive3d::Primitive3DContainer aNewVisibleTarget;
+
+ // add children recursively
+ for(sal_uInt32 a(0); a < nChildrenCount; a++)
+ {
+ createSubPrimitive3DVector(
+ rCandidate.GetViewContact(a),
+ aNewAllTarget,
+ o_pVisibleTarget ? &aNewVisibleTarget : nullptr,
+ pVisibleSdrLayerIDSet,
+ bTestSelectedVisibility);
+ }
+
+ // create transform primitive for the created content combining content and transformtion
+ const drawinglayer::primitive3d::Primitive3DReference xReference(new drawinglayer::primitive3d::TransformPrimitive3D(
+ pViewContactOfE3dScene->GetE3dScene().GetTransform(),
+ aNewAllTarget));
+
+ // add created content to all target
+ o_rAllTarget.push_back(xReference);
+
+ // add created content to visible target if exists
+ if(o_pVisibleTarget)
+ {
+ o_pVisibleTarget->push_back(xReference);
+ }
+ }
+ }
+ else
+ {
+ // access view independent representation of rCandidate
+ const sdr::contact::ViewContactOfE3d* pViewContactOfE3d = dynamic_cast< const sdr::contact::ViewContactOfE3d* >(&rCandidate);
+
+ if(pViewContactOfE3d)
+ {
+ drawinglayer::primitive3d::Primitive3DContainer xPrimitive3DSeq(pViewContactOfE3d->getViewIndependentPrimitive3DContainer());
+
+ if(!xPrimitive3DSeq.empty())
+ {
+ // add to all target vector
+ o_rAllTarget.append(xPrimitive3DSeq);
+
+ if(o_pVisibleTarget)
+ {
+ // test visibility. Primitive is visible when both tests are true (AND)
+ bool bVisible(true);
+
+ if(pVisibleSdrLayerIDSet)
+ {
+ // test layer visibility
+ const E3dObject& rE3dObject = pViewContactOfE3d->GetE3dObject();
+ const SdrLayerID aLayerID(rE3dObject.GetLayer());
+
+ bVisible = pVisibleSdrLayerIDSet->IsSet(aLayerID);
+ }
+
+ if(bVisible && bTestSelectedVisibility)
+ {
+ // test selected visibility (see 3D View's DrawMarkedObj implementation)
+ const E3dObject& rE3dObject = pViewContactOfE3d->GetE3dObject();
+
+ bVisible = rE3dObject.GetSelected();
+ }
+
+ if (bVisible)
+ {
+ // add to visible target vector
+ o_pVisibleTarget->append(xPrimitive3DSeq);
+ }
+ }
+ }
+ }
+ }
+}
+
+}
+
+namespace sdr::contact {
+
+// Create an Object-Specific ViewObjectContact, set ViewContact and
+// ObjectContact. Always needs to return something.
+ViewObjectContact& ViewContactOfE3dScene::CreateObjectSpecificViewObjectContact(ObjectContact& rObjectContact)
+{
+ ViewObjectContact* pRetval = new ViewObjectContactOfE3dScene(rObjectContact, *this);
+ DBG_ASSERT(pRetval, "ViewContactOfE3dScene::CreateObjectSpecificViewObjectContact() failed (!)");
+
+ return *pRetval;
+}
+
+ViewContactOfE3dScene::ViewContactOfE3dScene(E3dScene& rScene)
+: ViewContactOfSdrObj(rScene)
+{
+}
+
+void ViewContactOfE3dScene::createViewInformation3D(const basegfx::B3DRange& rContentRange)
+{
+ basegfx::B3DHomMatrix aTransformation;
+ basegfx::B3DHomMatrix aOrientation;
+ basegfx::B3DHomMatrix aProjection;
+ basegfx::B3DHomMatrix aDeviceToView;
+
+ // create transformation (scene as group's transformation)
+ // For historical reasons, the outmost scene's transformation is handles as part of the
+ // view transformation. This means that the BoundRect of the contained 3D Objects is
+ // without that transformation and makes it necessary to NOT add the first scene to the
+ // Primitive3DContainer of contained objects.
+ {
+ aTransformation = GetE3dScene().GetTransform();
+ }
+
+ // create orientation (world to camera coordinate system)
+ {
+ // calculate orientation from VRP, VPN and VUV
+ const B3dCamera& rSceneCamera = GetE3dScene().GetCameraSet();
+ const basegfx::B3DPoint& aVRP(rSceneCamera.GetVRP());
+ const basegfx::B3DVector& aVPN(rSceneCamera.GetVPN());
+ const basegfx::B3DVector& aVUV(rSceneCamera.GetVUV());
+
+ aOrientation.orientation(aVRP, aVPN, aVUV);
+ }
+
+ // create projection (camera coordinate system to relative 2d where X,Y and Z are [0.0 .. 1.0])
+ {
+ const basegfx::B3DHomMatrix aWorldToCamera(aOrientation * aTransformation);
+ basegfx::B3DRange aCameraRange(rContentRange);
+ aCameraRange.transform(aWorldToCamera);
+
+ // remember Z-Values, but change orientation
+ const double fMinZ(-aCameraRange.getMaxZ());
+ const double fMaxZ(-aCameraRange.getMinZ());
+
+ // construct temporary matrix from world to device. Use unit values here to measure expansion
+ basegfx::B3DHomMatrix aWorldToDevice(aWorldToCamera);
+ const drawinglayer::attribute::SdrSceneAttribute& rSdrSceneAttribute = getSdrSceneAttribute();
+
+ if(css::drawing::ProjectionMode_PERSPECTIVE == rSdrSceneAttribute.getProjectionMode())
+ {
+ aWorldToDevice.frustum(-1.0, 1.0, -1.0, 1.0, fMinZ, fMaxZ);
+ }
+ else
+ {
+ aWorldToDevice.ortho(-1.0, 1.0, -1.0, 1.0, fMinZ, fMaxZ);
+ }
+
+ // create B3DRange in device. This will create the real used ranges
+ // in camera space. Do not use the Z-Values, though.
+ basegfx::B3DRange aDeviceRange(rContentRange);
+ aDeviceRange.transform(aWorldToDevice);
+
+ // set projection
+ if(css::drawing::ProjectionMode_PERSPECTIVE == rSdrSceneAttribute.getProjectionMode())
+ {
+ aProjection.frustum(
+ aDeviceRange.getMinX(), aDeviceRange.getMaxX(),
+ aDeviceRange.getMinY(), aDeviceRange.getMaxY(),
+ fMinZ, fMaxZ);
+ }
+ else
+ {
+ aProjection.ortho(
+ aDeviceRange.getMinX(), aDeviceRange.getMaxX(),
+ aDeviceRange.getMinY(), aDeviceRange.getMaxY(),
+ fMinZ, fMaxZ);
+ }
+ }
+
+ // create device to view transform
+ {
+ // create standard deviceToView projection for geometry
+ // input is [-1.0 .. 1.0] in X,Y and Z. bring to [0.0 .. 1.0]. Also
+ // necessary to flip Y due to screen orientation
+ // Z is not needed, but will also be brought to [0.0 .. 1.0]
+ aDeviceToView.scale(0.5, -0.5, 0.5);
+ aDeviceToView.translate(0.5, 0.5, 0.5);
+ }
+
+ const uno::Sequence< beans::PropertyValue > aEmptyProperties;
+ maViewInformation3D = drawinglayer::geometry::ViewInformation3D(
+ aTransformation, aOrientation, aProjection,
+ aDeviceToView, 0.0, aEmptyProperties);
+}
+
+void ViewContactOfE3dScene::createObjectTransformation()
+{
+ // create 2d Object Transformation from relative point in 2d scene to world
+ const tools::Rectangle aRectangle(GetE3dScene().GetSnapRect());
+
+ maObjectTransformation.set(0, 0, aRectangle.getWidth());
+ maObjectTransformation.set(1, 1, aRectangle.getHeight());
+ maObjectTransformation.set(0, 2, aRectangle.Left());
+ maObjectTransformation.set(1, 2, aRectangle.Top());
+}
+
+void ViewContactOfE3dScene::createSdrSceneAttribute()
+{
+ const SfxItemSet& rItemSet = GetE3dScene().GetMergedItemSet();
+ maSdrSceneAttribute = drawinglayer::primitive2d::createNewSdrSceneAttribute(rItemSet);
+}
+
+void ViewContactOfE3dScene::createSdrLightingAttribute()
+{
+ const SfxItemSet& rItemSet = GetE3dScene().GetMergedItemSet();
+ maSdrLightingAttribute = drawinglayer::primitive2d::createNewSdrLightingAttribute(rItemSet);
+}
+
+drawinglayer::primitive2d::Primitive2DContainer ViewContactOfE3dScene::createScenePrimitive2DSequence(
+ const SdrLayerIDSet* pLayerVisibility) const
+{
+ drawinglayer::primitive2d::Primitive2DContainer xRetval;
+ const sal_uInt32 nChildrenCount(GetObjectCount());
+
+ if(nChildrenCount)
+ {
+ // create 3d scene primitive with visible content tested against rLayerVisibility
+ drawinglayer::primitive3d::Primitive3DContainer aAllSequence;
+ drawinglayer::primitive3d::Primitive3DContainer aVisibleSequence;
+ const bool bTestLayerVisibility(nullptr != pLayerVisibility);
+ const bool bTestSelectedVisibility(GetE3dScene().GetDrawOnlySelected());
+ const bool bTestVisibility(bTestLayerVisibility || bTestSelectedVisibility);
+
+ // add children recursively. Do NOT start with (*this), this would create
+ // a 3D transformPrimitive for the start scene. While this is theoretically not
+ // a bad thing, for historical reasons the transformation of the outmost scene
+ // is seen as part of the ViewTransformation (see text in createViewInformation3D)
+ for(sal_uInt32 a(0); a < nChildrenCount; a++)
+ {
+ createSubPrimitive3DVector(
+ GetViewContact(a),
+ aAllSequence,
+ bTestLayerVisibility ? &aVisibleSequence : nullptr,
+ bTestLayerVisibility ? pLayerVisibility : nullptr,
+ bTestSelectedVisibility);
+ }
+
+ const size_t nAllSize(!aAllSequence.empty() ? aAllSequence.size() : 0);
+ const size_t nVisibleSize(!aVisibleSequence.empty() ? aVisibleSequence.size() : 0);
+
+ if((bTestVisibility && nVisibleSize) || nAllSize)
+ {
+ // for getting the 3D range using getB3DRangeFromPrimitive3DContainer a ViewInformation3D
+ // needs to be given for evtl. decompositions. At the same time createViewInformation3D
+ // currently is based on creating the target-ViewInformation3D using a given range. To
+ // get the true range, use a neutral ViewInformation3D here. This leaves all matrices
+ // on identity and the time on 0.0.
+ const uno::Sequence< beans::PropertyValue > aEmptyProperties;
+ const drawinglayer::geometry::ViewInformation3D aNeutralViewInformation3D(aEmptyProperties);
+ const basegfx::B3DRange aContentRange(aAllSequence.getB3DRange(aNeutralViewInformation3D));
+
+ // create 2d primitive 3dscene with generated sub-list from collector
+ const drawinglayer::primitive2d::Primitive2DReference xReference(
+ new drawinglayer::primitive2d::ScenePrimitive2D(
+ bTestVisibility ? aVisibleSequence : aAllSequence,
+ getSdrSceneAttribute(),
+ getSdrLightingAttribute(),
+ getObjectTransformation(),
+ getViewInformation3D(aContentRange)));
+
+ xRetval = drawinglayer::primitive2d::Primitive2DContainer{ xReference };
+ }
+ }
+
+ // always append an invisible outline for the cases where no visible content exists
+ xRetval.push_back(
+ drawinglayer::primitive2d::createHiddenGeometryPrimitives2D(
+ getObjectTransformation()));
+
+ return xRetval;
+}
+
+void ViewContactOfE3dScene::createViewIndependentPrimitive2DSequence(drawinglayer::primitive2d::Primitive2DDecompositionVisitor& rVisitor) const
+{
+ if(GetObjectCount())
+ {
+ // create a default ScenePrimitive2D (without visibility test of members)
+ rVisitor.visit(createScenePrimitive2DSequence(nullptr));
+ }
+}
+
+void ViewContactOfE3dScene::ActionChanged()
+{
+ // call parent
+ ViewContactOfSdrObj::ActionChanged();
+
+ // mark locally cached values as invalid
+ maViewInformation3D = drawinglayer::geometry::ViewInformation3D();
+ maObjectTransformation.identity();
+ maSdrSceneAttribute = drawinglayer::attribute::SdrSceneAttribute();
+ maSdrLightingAttribute = drawinglayer::attribute::SdrLightingAttribute();
+}
+
+const drawinglayer::geometry::ViewInformation3D& ViewContactOfE3dScene::getViewInformation3D() const
+{
+ if(maViewInformation3D.isDefault())
+ {
+ // this version will create the content range on demand locally and thus is less
+ // performant than the other one. Since the information is buffered the planned
+ // behaviour is that the version with the given range is used initially.
+ basegfx::B3DRange aContentRange(getAllContentRange3D());
+
+ if(aContentRange.isEmpty())
+ {
+ // empty scene, no 3d action should be necessary. Prepare some
+ // fallback size
+ OSL_FAIL("No need to get ViewInformation3D from an empty scene (!)");
+ aContentRange.expand(basegfx::B3DPoint(-100.0, -100.0, -100.0));
+ aContentRange.expand(basegfx::B3DPoint( 100.0, 100.0, 100.0));
+ }
+
+ const_cast < ViewContactOfE3dScene* >(this)->createViewInformation3D(aContentRange);
+ }
+
+ return maViewInformation3D;
+}
+
+const drawinglayer::geometry::ViewInformation3D& ViewContactOfE3dScene::getViewInformation3D(const basegfx::B3DRange& rContentRange) const
+{
+ if(maViewInformation3D.isDefault())
+ {
+ const_cast < ViewContactOfE3dScene* >(this)->createViewInformation3D(rContentRange);
+ }
+
+ return maViewInformation3D;
+}
+
+const basegfx::B2DHomMatrix& ViewContactOfE3dScene::getObjectTransformation() const
+{
+ if(maObjectTransformation.isIdentity())
+ {
+ const_cast < ViewContactOfE3dScene* >(this)->createObjectTransformation();
+ }
+
+ return maObjectTransformation;
+}
+
+const drawinglayer::attribute::SdrSceneAttribute& ViewContactOfE3dScene::getSdrSceneAttribute() const
+{
+ if(maSdrSceneAttribute.isDefault())
+ {
+ const_cast < ViewContactOfE3dScene* >(this)->createSdrSceneAttribute();
+ }
+
+ return maSdrSceneAttribute;
+}
+
+const drawinglayer::attribute::SdrLightingAttribute& ViewContactOfE3dScene::getSdrLightingAttribute() const
+{
+ if(maSdrLightingAttribute.isDefault())
+ {
+ const_cast < ViewContactOfE3dScene* >(this)->createSdrLightingAttribute();
+ }
+
+ return maSdrLightingAttribute;
+}
+
+drawinglayer::primitive3d::Primitive3DContainer ViewContactOfE3dScene::getAllPrimitive3DContainer() const
+{
+ drawinglayer::primitive3d::Primitive3DContainer aAllPrimitive3DContainer;
+ const sal_uInt32 nChildrenCount(GetObjectCount());
+
+ // add children recursively. Do NOT start with (*this), this would create
+ // a 3D transformPrimitive for the start scene. While this is theoretically not
+ // a bad thing, for historical reasons the transformation of the outmost scene
+ // is seen as part of the ViewTransformation (see text in createViewInformation3D)
+ for(sal_uInt32 a(0); a < nChildrenCount; a++)
+ {
+ createSubPrimitive3DVector(GetViewContact(a), aAllPrimitive3DContainer, nullptr, nullptr, false);
+ }
+
+ return aAllPrimitive3DContainer;
+}
+
+basegfx::B3DRange ViewContactOfE3dScene::getAllContentRange3D() const
+{
+ const drawinglayer::primitive3d::Primitive3DContainer xAllSequence(getAllPrimitive3DContainer());
+ basegfx::B3DRange aAllContentRange3D;
+
+ if(!xAllSequence.empty())
+ {
+ // for getting the 3D range using getB3DRangeFromPrimitive3DContainer a ViewInformation3D
+ // needs to be given for evtl. decompositions. Use a neutral ViewInformation3D here. This
+ // leaves all matrices on identity and the time on 0.0.
+ const uno::Sequence< beans::PropertyValue > aEmptyProperties;
+ const drawinglayer::geometry::ViewInformation3D aNeutralViewInformation3D(aEmptyProperties);
+
+ aAllContentRange3D = xAllSequence.getB3DRange(aNeutralViewInformation3D);
+ }
+
+ return aAllContentRange3D;
+}
+
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/sdr/contact/viewcontactofe3dsphere.cxx b/svx/source/sdr/contact/viewcontactofe3dsphere.cxx
new file mode 100644
index 000000000..7a99719b8
--- /dev/null
+++ b/svx/source/sdr/contact/viewcontactofe3dsphere.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 <sdr/contact/viewcontactofe3dsphere.hxx>
+#include <svx/sphere3d.hxx>
+#include <drawinglayer/primitive3d/sdrsphereprimitive3d.hxx>
+#include <sdr/primitive2d/sdrattributecreator.hxx>
+#include <sdr/primitive3d/sdrattributecreator3d.hxx>
+
+
+namespace sdr::contact
+{
+ ViewContactOfE3dSphere::ViewContactOfE3dSphere(E3dSphereObj& rSphere)
+ : ViewContactOfE3d(rSphere)
+ {
+ }
+
+ ViewContactOfE3dSphere::~ViewContactOfE3dSphere()
+ {
+ }
+
+ drawinglayer::primitive3d::Primitive3DContainer ViewContactOfE3dSphere::createViewIndependentPrimitive3DContainer() const
+ {
+ drawinglayer::primitive3d::Primitive3DContainer xRetval;
+ const SfxItemSet& rItemSet = GetE3dSphereObj().GetMergedItemSet();
+ const drawinglayer::attribute::SdrLineFillShadowAttribute3D aAttribute(
+ drawinglayer::primitive2d::createNewSdrLineFillShadowAttribute(rItemSet, false));
+
+ // get sphere center and size for geometry
+ const basegfx::B3DPoint aSpherePosition(GetE3dSphereObj().Center());
+ const basegfx::B3DVector aSphereSize(GetE3dSphereObj().Size());
+ basegfx::B3DHomMatrix aWorldTransform;
+
+ aWorldTransform.translate(-0.5, -0.5, -0.5);
+ aWorldTransform.scale(aSphereSize.getX(), aSphereSize.getY(), aSphereSize.getZ());
+ aWorldTransform.translate(aSpherePosition.getX(), aSpherePosition.getY(), aSpherePosition.getZ());
+
+ // get 3D Object Attributes
+ drawinglayer::attribute::Sdr3DObjectAttribute aSdr3DObjectAttribute(drawinglayer::primitive2d::createNewSdr3DObjectAttribute(rItemSet));
+
+ // get segment count
+ const sal_uInt32 nHorizontalSegments(GetE3dSphereObj().GetHorizontalSegments());
+ const sal_uInt32 nVerticalSegments(GetE3dSphereObj().GetVerticalSegments());
+
+ // calculate texture size, use radii for (2 * PI * r) to get a perfect
+ // mapping on the sphere
+ const basegfx::B2DVector aTextureSize(
+ M_PI * ((aSphereSize.getX() + aSphereSize.getZ()) / 2.0), // PI * d
+ M_PI_2 * aSphereSize.getY()); // half outline, (PI * d)/2 -> PI/2 * d
+
+ // create primitive and add
+ const drawinglayer::primitive3d::Primitive3DReference xReference(
+ new drawinglayer::primitive3d::SdrSpherePrimitive3D(
+ aWorldTransform, aTextureSize, aAttribute, aSdr3DObjectAttribute,
+ nHorizontalSegments, nVerticalSegments));
+ xRetval = { xReference };
+
+ return xRetval;
+ }
+
+} // end of namespace
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/sdr/contact/viewcontactofgraphic.cxx b/svx/source/sdr/contact/viewcontactofgraphic.cxx
new file mode 100644
index 000000000..6428e5a66
--- /dev/null
+++ b/svx/source/sdr/contact/viewcontactofgraphic.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 <sdr/contact/viewcontactofgraphic.hxx>
+#include <sdr/contact/viewobjectcontactofgraphic.hxx>
+#include <svx/svdograf.hxx>
+#include <sdgtritm.hxx>
+#include <svx/sdgluitm.hxx>
+#include <sdgcoitm.hxx>
+#include <svx/sdggaitm.hxx>
+#include <sdginitm.hxx>
+#include <svx/sdgmoitm.hxx>
+#include <sdr/primitive2d/sdrattributecreator.hxx>
+#include <svl/itemset.hxx>
+#include <tools/debug.hxx>
+
+#include <svx/sdgcpitm.hxx>
+#include <svx/sdr/contact/viewobjectcontact.hxx>
+#include <svx/sdr/contact/objectcontact.hxx>
+#include <basegfx/matrix/b2dhommatrix.hxx>
+#include <sdr/primitive2d/sdrgrafprimitive2d.hxx>
+#include <vcl/canvastools.hxx>
+#include <vcl/svapp.hxx>
+#include <vcl/settings.hxx>
+#include <basegfx/polygon/b2dpolygontools.hxx>
+#include <drawinglayer/primitive2d/PolygonHairlinePrimitive2D.hxx>
+#include <drawinglayer/primitive2d/bitmapprimitive2d.hxx>
+#include <sdr/primitive2d/sdrtextprimitive2d.hxx>
+#include <editeng/eeitem.hxx>
+#include <editeng/colritem.hxx>
+#include <basegfx/matrix/b2dhommatrixtools.hxx>
+#include <drawinglayer/primitive2d/sdrdecompositiontools2d.hxx>
+#include <toolkit/helper/vclunohelper.hxx>
+
+#include <bitmaps.hlst>
+
+namespace sdr::contact
+{
+ // Create an Object-Specific ViewObjectContact, set ViewContact and
+ // ObjectContact. Always needs to return something.
+ ViewObjectContact& ViewContactOfGraphic::CreateObjectSpecificViewObjectContact(ObjectContact& rObjectContact)
+ {
+ ViewObjectContact* pRetval = new ViewObjectContactOfGraphic(rObjectContact, *this);
+ DBG_ASSERT(pRetval, "ViewContact::CreateObjectSpecificViewObjectContact() failed (!)");
+
+ return *pRetval;
+ }
+
+ ViewContactOfGraphic::ViewContactOfGraphic(SdrGrafObj& rGrafObj)
+ : ViewContactOfTextObj(rGrafObj)
+ {
+ }
+
+ ViewContactOfGraphic::~ViewContactOfGraphic()
+ {
+ }
+
+ drawinglayer::primitive2d::Primitive2DContainer ViewContactOfGraphic::createVIP2DSForPresObj(
+ const basegfx::B2DHomMatrix& rObjectMatrix,
+ const drawinglayer::attribute::SdrLineFillEffectsTextAttribute& rAttribute) const
+ {
+ drawinglayer::primitive2d::Primitive2DContainer xRetval;
+ GraphicObject aEmptyGraphicObject;
+ GraphicAttr aEmptyGraphicAttr;
+
+ // SdrGrafPrimitive2D without content in original size which carries all eventual attributes and texts
+ const drawinglayer::primitive2d::Primitive2DReference xReferenceA(new drawinglayer::primitive2d::SdrGrafPrimitive2D(
+ rObjectMatrix,
+ rAttribute,
+ aEmptyGraphicObject,
+ aEmptyGraphicAttr));
+ xRetval = drawinglayer::primitive2d::Primitive2DContainer { xReferenceA };
+
+ // SdrGrafPrimitive2D with content (which is the preview graphic) scaled to smaller size and
+ // without attributes
+ basegfx::B2DHomMatrix aSmallerMatrix;
+
+ // #i94431# for some reason, i forgot to take the PrefMapMode of the graphic
+ // into account. Since EmptyPresObj's are only used in Draw/Impress, it is
+ // safe to assume 100th mm as target.
+ Size aPrefSize(GetGrafObject().GetGrafPrefSize());
+
+ if(MapUnit::MapPixel == GetGrafObject().GetGrafPrefMapMode().GetMapUnit())
+ {
+ aPrefSize = Application::GetDefaultDevice()->PixelToLogic(aPrefSize, MapMode(MapUnit::Map100thMM));
+ }
+ else
+ {
+ aPrefSize = OutputDevice::LogicToLogic(aPrefSize, GetGrafObject().GetGrafPrefMapMode(), MapMode(MapUnit::Map100thMM));
+ }
+
+ // decompose object matrix to get single values
+ basegfx::B2DVector aScale, aTranslate;
+ double fRotate, fShearX;
+ rObjectMatrix.decompose(aScale, aTranslate, fRotate, fShearX);
+
+ const double fOffsetX((aScale.getX() - aPrefSize.getWidth()) / 2.0);
+ const double fOffsetY((aScale.getY() - aPrefSize.getHeight()) / 2.0);
+
+ if(basegfx::fTools::moreOrEqual(fOffsetX, 0.0) && basegfx::fTools::moreOrEqual(fOffsetY, 0.0))
+ {
+ // create the EmptyPresObj fallback visualisation. The fallback graphic
+ // is already provided in rGraphicObject in this case, use it
+ aSmallerMatrix = basegfx::utils::createScaleTranslateB2DHomMatrix(aPrefSize.getWidth(), aPrefSize.getHeight(), fOffsetX, fOffsetY);
+ aSmallerMatrix = basegfx::utils::createShearXRotateTranslateB2DHomMatrix(fShearX, fRotate, aTranslate)
+ * aSmallerMatrix;
+
+ const GraphicObject& rGraphicObject = GetGrafObject().GetGraphicObject();
+ const GraphicAttr aLocalGrafInfo;
+ const drawinglayer::primitive2d::Primitive2DReference xReferenceB(new drawinglayer::primitive2d::SdrGrafPrimitive2D(
+ aSmallerMatrix,
+ drawinglayer::attribute::SdrLineFillEffectsTextAttribute(),
+ rGraphicObject,
+ aLocalGrafInfo));
+
+ xRetval.push_back(xReferenceB);
+ }
+
+ return xRetval;
+ }
+
+ drawinglayer::primitive2d::Primitive2DContainer ViewContactOfGraphic::createVIP2DSForDraft(
+ const basegfx::B2DHomMatrix& rObjectMatrix,
+ const drawinglayer::attribute::SdrLineFillEffectsTextAttribute& rAttribute) const
+ {
+ drawinglayer::primitive2d::Primitive2DContainer xRetval;
+ GraphicObject aEmptyGraphicObject;
+ GraphicAttr aEmptyGraphicAttr;
+
+ // SdrGrafPrimitive2D without content in original size which carries all eventual attributes and texts
+ const drawinglayer::primitive2d::Primitive2DReference xReferenceA(new drawinglayer::primitive2d::SdrGrafPrimitive2D(
+ rObjectMatrix,
+ rAttribute,
+ aEmptyGraphicObject,
+ aEmptyGraphicAttr));
+ xRetval = drawinglayer::primitive2d::Primitive2DContainer { xReferenceA };
+
+ if(rAttribute.getLine().isDefault())
+ {
+ // create a surrounding frame when no linestyle given
+ const Color aColor(Application::GetSettings().GetStyleSettings().GetShadowColor());
+ const basegfx::BColor aBColor(aColor.getBColor());
+ basegfx::B2DPolygon aOutline(basegfx::utils::createUnitPolygon());
+ aOutline.transform(rObjectMatrix);
+
+ xRetval.push_back(
+ drawinglayer::primitive2d::Primitive2DReference(
+ new drawinglayer::primitive2d::PolygonHairlinePrimitive2D(
+ aOutline,
+ aBColor)));
+ }
+
+ // decompose object matrix to get single values
+ basegfx::B2DVector aScale, aTranslate;
+ double fRotate, fShearX;
+ rObjectMatrix.decompose(aScale, aTranslate, fRotate, fShearX);
+
+ // define a distance value, used for distance from bitmap to borders and from bitmap
+ // to text, too (2 mm)
+ const double fDistance(200.0);
+
+ // consume borders from values
+ aScale.setX(std::max(0.0, aScale.getX() - (2.0 * fDistance)));
+ aScale.setY(std::max(0.0, aScale.getY() - (2.0 * fDistance)));
+ aTranslate.setX(aTranslate.getX() + fDistance);
+ aTranslate.setY(aTranslate.getY() + fDistance);
+
+ // draw a draft bitmap
+ const BitmapEx aDraftBitmap(BMAP_GrafikEi);
+
+ if(!aDraftBitmap.IsEmpty())
+ {
+ Size aPrefSize(aDraftBitmap.GetPrefSize());
+
+ if(MapUnit::MapPixel == aDraftBitmap.GetPrefMapMode().GetMapUnit())
+ {
+ aPrefSize = Application::GetDefaultDevice()->PixelToLogic(aDraftBitmap.GetSizePixel(), MapMode(MapUnit::Map100thMM));
+ }
+ else
+ {
+ aPrefSize = OutputDevice::LogicToLogic(aPrefSize, aDraftBitmap.GetPrefMapMode(), MapMode(MapUnit::Map100thMM));
+ }
+
+ const double fBitmapScaling(2.0);
+ const double fWidth(aPrefSize.getWidth() * fBitmapScaling);
+ const double fHeight(aPrefSize.getHeight() * fBitmapScaling);
+
+ if(basegfx::fTools::more(fWidth, 1.0)
+ && basegfx::fTools::more(fHeight, 1.0)
+ && basegfx::fTools::lessOrEqual(fWidth, aScale.getX())
+ && basegfx::fTools::lessOrEqual(fHeight, aScale.getY()))
+ {
+ const basegfx::B2DHomMatrix aBitmapMatrix(basegfx::utils::createScaleShearXRotateTranslateB2DHomMatrix(
+ fWidth, fHeight, fShearX, fRotate, aTranslate.getX(), aTranslate.getY()));
+
+ xRetval.push_back(
+ drawinglayer::primitive2d::Primitive2DReference(
+ new drawinglayer::primitive2d::BitmapPrimitive2D(
+ VCLUnoHelper::CreateVCLXBitmap(aDraftBitmap),
+ aBitmapMatrix)));
+
+ // consume bitmap size in X
+ aScale.setX(std::max(0.0, aScale.getX() - (fWidth + fDistance)));
+ aTranslate.setX(aTranslate.getX() + fWidth + fDistance);
+ }
+ }
+
+ // Build the text for the draft object
+ OUString aDraftText = GetGrafObject().GetFileName();
+
+ if (aDraftText.isEmpty())
+ {
+ aDraftText = GetGrafObject().GetName() + " ...";
+ }
+
+ if (!aDraftText.isEmpty())
+ {
+ // #i103255# Goal is to produce TextPrimitives which hold the given text as
+ // BlockText in the available space. It would be very tricky to do
+ // an own word wrap/line layout here.
+ // Using SdrBlockTextPrimitive2D OTOH is critical since it internally
+ // uses the SdrObject it references. To solve this, create a temp
+ // SdrObject with Attributes and Text, generate a SdrBlockTextPrimitive2D
+ // directly and immediately decompose it. After that, it is no longer
+ // needed and can be deleted.
+
+ // create temp RectObj as TextObj and set needed attributes
+ SdrRectObj* pRectObj(new SdrRectObj(GetGrafObject().getSdrModelFromSdrObject(), SdrObjKind::Text));
+ pRectObj->NbcSetText(aDraftText);
+ pRectObj->SetMergedItem(SvxColorItem(COL_LIGHTRED, EE_CHAR_COLOR));
+
+ // get SdrText and OPO
+ SdrText* pSdrText(pRectObj->getText(0));
+ OutlinerParaObject* pOPO(pRectObj->GetOutlinerParaObject());
+
+ if(pSdrText && pOPO)
+ {
+ // directly use the remaining space as TextRangeTransform
+ const basegfx::B2DHomMatrix aTextRangeTransform(basegfx::utils::createScaleShearXRotateTranslateB2DHomMatrix(
+ aScale, fShearX, fRotate, aTranslate));
+
+ // directly create temp SdrBlockTextPrimitive2D
+ rtl::Reference< drawinglayer::primitive2d::SdrBlockTextPrimitive2D > xBlockTextPrimitive(new drawinglayer::primitive2d::SdrBlockTextPrimitive2D(
+ pSdrText,
+ *pOPO,
+ aTextRangeTransform,
+ SDRTEXTHORZADJUST_LEFT,
+ SDRTEXTVERTADJUST_TOP,
+ false,
+ false,
+ false,
+ false));
+
+ // decompose immediately with neutral ViewInformation. This will
+ // layout the text to more simple TextPrimitives from drawinglayer
+ const drawinglayer::geometry::ViewInformation2D aViewInformation2D;
+ xBlockTextPrimitive->get2DDecomposition(xRetval, aViewInformation2D);
+ }
+
+ // always use SdrObject::Free(...) for SdrObjects (!)
+ SdrObject* pTemp(pRectObj);
+ SdrObject::Free(pTemp);
+ }
+
+ return xRetval;
+ }
+
+ void ViewContactOfGraphic::createViewIndependentPrimitive2DSequence(drawinglayer::primitive2d::Primitive2DDecompositionVisitor& rVisitor) const
+ {
+ const SfxItemSet& rItemSet = GetGrafObject().GetMergedItemSet();
+
+ // create and fill GraphicAttr
+ GraphicAttr aLocalGrafInfo;
+ const sal_uInt16 nTrans(rItemSet.Get(SDRATTR_GRAFTRANSPARENCE).GetValue());
+ const SdrGrafCropItem& rCrop(rItemSet.Get(SDRATTR_GRAFCROP));
+ aLocalGrafInfo.SetLuminance(rItemSet.Get(SDRATTR_GRAFLUMINANCE).GetValue());
+ aLocalGrafInfo.SetContrast(rItemSet.Get(SDRATTR_GRAFCONTRAST).GetValue());
+ aLocalGrafInfo.SetChannelR(rItemSet.Get(SDRATTR_GRAFRED).GetValue());
+ aLocalGrafInfo.SetChannelG(rItemSet.Get(SDRATTR_GRAFGREEN).GetValue());
+ aLocalGrafInfo.SetChannelB(rItemSet.Get(SDRATTR_GRAFBLUE).GetValue());
+ aLocalGrafInfo.SetGamma(rItemSet.Get(SDRATTR_GRAFGAMMA).GetValue() * 0.01);
+ aLocalGrafInfo.SetAlpha(255 - static_cast<sal_uInt8>(::basegfx::fround(std::min(nTrans, sal_uInt16(100)) * 2.55)));
+ aLocalGrafInfo.SetInvert(rItemSet.Get(SDRATTR_GRAFINVERT).GetValue());
+ aLocalGrafInfo.SetDrawMode(rItemSet.Get(SDRATTR_GRAFMODE).GetValue());
+ aLocalGrafInfo.SetCrop(rCrop.GetLeft(), rCrop.GetTop(), rCrop.GetRight(), rCrop.GetBottom());
+
+ // we have content if graphic is not completely transparent
+ const bool bHasContent(0 != aLocalGrafInfo.GetAlpha());
+ drawinglayer::attribute::SdrLineFillEffectsTextAttribute aAttribute(
+ drawinglayer::primitive2d::createNewSdrLineFillEffectsTextAttribute(
+ rItemSet,
+ GetGrafObject().getText(0),
+ bHasContent));
+
+ // take unrotated snap rect for position and size. Directly use model data, not getBoundRect() or getSnapRect()
+ // which will use the primitive data we just create in the near future
+ const ::basegfx::B2DRange aObjectRange = vcl::unotools::b2DRectangleFromRectangle(GetGrafObject().GetGeoRect());
+
+ // look for mirroring
+ const GeoStat& rGeoStat(GetGrafObject().GetGeoStat());
+ const Degree100 nRotationAngle(rGeoStat.nRotationAngle);
+ const bool bMirrored(GetGrafObject().IsMirrored());
+
+ if (bMirrored)
+ aLocalGrafInfo.SetMirrorFlags(BmpMirrorFlags::Horizontal);
+
+ // fill object matrix
+ const double fShearX(-rGeoStat.mfTanShearAngle);
+ const double fRotate(nRotationAngle ? toRadians(36000_deg100 - nRotationAngle) : 0.0);
+ const basegfx::B2DHomMatrix aObjectMatrix(basegfx::utils::createScaleShearXRotateTranslateB2DHomMatrix(
+ aObjectRange.getWidth(), aObjectRange.getHeight(),
+ fShearX, fRotate,
+ aObjectRange.getMinX(), aObjectRange.getMinY()));
+
+ // get the current, unchanged graphic object from SdrGrafObj
+ const GraphicObject& rGraphicObject = GetGrafObject().GetGraphicObject();
+
+ if(visualisationUsesPresObj())
+ {
+ // it's an EmptyPresObj, create the SdrGrafPrimitive2D without content and another scaled one
+ // with the content which is the placeholder graphic
+ rVisitor.visit(createVIP2DSForPresObj(aObjectMatrix, aAttribute));
+ }
+#ifndef IOS // Enforce swap-in for tiled rendering for now, while we have no delayed updating mechanism
+ else if(visualisationUsesDraft())
+ {
+ // #i102380# The graphic is swapped out. To not force a swap-in here, there is a mechanism
+ // which shows a swapped-out-visualisation (which gets created here now) and an asynchronous
+ // visual update mechanism for swapped-out graphics when they were loaded (see AsynchGraphicLoadingEvent
+ // and ViewObjectContactOfGraphic implementation). Not forcing the swap-in here allows faster
+ // (non-blocking) processing here and thus in the effect e.g. fast scrolling through pages
+ rVisitor.visit(createVIP2DSForDraft(aObjectMatrix, aAttribute));
+ }
+#endif
+ else
+ {
+ // create primitive. Info: Calling the copy-constructor of GraphicObject in this
+ // SdrGrafPrimitive2D constructor will force a full swap-in of the graphic
+ const drawinglayer::primitive2d::Primitive2DReference xReference(
+ new drawinglayer::primitive2d::SdrGrafPrimitive2D(
+ aObjectMatrix,
+ aAttribute,
+ rGraphicObject,
+ aLocalGrafInfo));
+
+ rVisitor.visit(xReference);
+ }
+
+ // always append an invisible outline for the cases where no visible content exists
+ rVisitor.visit(
+ drawinglayer::primitive2d::createHiddenGeometryPrimitives2D(
+ aObjectMatrix));
+ }
+
+ bool ViewContactOfGraphic::visualisationUsesPresObj() const
+ {
+ return GetGrafObject().IsEmptyPresObj();
+ }
+
+ bool ViewContactOfGraphic::visualisationUsesDraft() const
+ {
+ // no draft when already PresObj
+ if(visualisationUsesPresObj())
+ return false;
+
+ // draft when swapped out
+ const GraphicObject& rGraphicObject = GetGrafObject().GetGraphicObject();
+
+ // draft when no graphic
+ return GraphicType::NONE == rGraphicObject.GetType() || GraphicType::Default == rGraphicObject.GetType();
+ }
+
+} // end of namespace
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/sdr/contact/viewcontactofgroup.cxx b/svx/source/sdr/contact/viewcontactofgroup.cxx
new file mode 100644
index 000000000..18e5e07aa
--- /dev/null
+++ b/svx/source/sdr/contact/viewcontactofgroup.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 <sdr/contact/viewcontactofgroup.hxx>
+#include <svx/svdogrp.hxx>
+#include <svx/sdr/contact/viewobjectcontact.hxx>
+#include <sdr/contact/viewobjectcontactofgroup.hxx>
+#include <drawinglayer/primitive2d/sdrdecompositiontools2d.hxx>
+#include <tools/debug.hxx>
+#include <vcl/canvastools.hxx>
+
+
+namespace sdr::contact
+{
+ // Create an Object-Specific ViewObjectContact, set ViewContact and
+ // ObjectContact. Always needs to return something.
+ ViewObjectContact& ViewContactOfGroup::CreateObjectSpecificViewObjectContact(ObjectContact& rObjectContact)
+ {
+ ViewObjectContact* pRetval = new ViewObjectContactOfGroup(rObjectContact, *this);
+ DBG_ASSERT(pRetval, "ViewContactOfGroup::CreateObjectSpecificViewObjectContact() failed (!)");
+
+ return *pRetval;
+ }
+
+ ViewContactOfGroup::ViewContactOfGroup(SdrObjGroup& rGroup)
+ : ViewContactOfSdrObj(rGroup)
+ {
+ }
+
+ ViewContactOfGroup::~ViewContactOfGroup()
+ {
+ }
+
+ void ViewContactOfGroup::createViewIndependentPrimitive2DSequence(drawinglayer::primitive2d::Primitive2DDecompositionVisitor& rVisitor) const
+ {
+ const sal_uInt32 nObjectCount(GetObjectCount());
+
+ if(nObjectCount)
+ {
+ // collect all sub-primitives
+ for(sal_uInt32 a(0); a < nObjectCount; a++)
+ {
+ const ViewContact& rCandidate(GetViewContact(a));
+ rCandidate.getViewIndependentPrimitive2DContainer(rVisitor);
+ }
+ }
+ else
+ {
+ // append an invisible outline for the cases where no visible content exists
+ const basegfx::B2DRange aCurrentRange = vcl::unotools::b2DRectangleFromRectangle(GetSdrObjGroup().GetLastBoundRect());
+
+ const drawinglayer::primitive2d::Primitive2DReference xReference(
+ drawinglayer::primitive2d::createHiddenGeometryPrimitives2D(
+ false, aCurrentRange));
+
+ rVisitor.visit(xReference);
+ }
+ }
+
+} // end of namespace
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/sdr/contact/viewcontactofmasterpagedescriptor.cxx b/svx/source/sdr/contact/viewcontactofmasterpagedescriptor.cxx
new file mode 100644
index 000000000..20463d0fc
--- /dev/null
+++ b/svx/source/sdr/contact/viewcontactofmasterpagedescriptor.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 <sdr/contact/viewcontactofmasterpagedescriptor.hxx>
+#include <svx/sdr/contact/viewobjectcontact.hxx>
+#include <svx/svdpage.hxx>
+#include <svx/svdobj.hxx>
+#include <sdr/contact/viewobjectcontactofmasterpagedescriptor.hxx>
+#include <sdr/primitive2d/sdrattributecreator.hxx>
+#include <basegfx/polygon/b2dpolygontools.hxx>
+#include <sdr/primitive2d/sdrdecompositiontools.hxx>
+#include <drawinglayer/attribute/sdrfillattribute.hxx>
+#include <basegfx/polygon/b2dpolygon.hxx>
+#include <drawinglayer/attribute/fillgradientattribute.hxx>
+
+
+namespace sdr::contact
+{
+ ViewObjectContact& ViewContactOfMasterPageDescriptor::CreateObjectSpecificViewObjectContact(ObjectContact& rObjectContact)
+ {
+ return *(new ViewObjectContactOfMasterPageDescriptor(rObjectContact, *this));
+ }
+
+ void ViewContactOfMasterPageDescriptor::createViewIndependentPrimitive2DSequence(drawinglayer::primitive2d::Primitive2DDecompositionVisitor& rVisitor) const
+ {
+ drawinglayer::attribute::SdrFillAttribute aFill;
+ const SdrPageProperties* pCorrectProperties = GetMasterPageDescriptor().getCorrectSdrPageProperties();
+
+ if(pCorrectProperties)
+ {
+ // create page fill attributes when correct properties were identified
+ aFill = drawinglayer::primitive2d::createNewSdrFillAttribute(pCorrectProperties->GetItemSet());
+ }
+
+ if(!aFill.isDefault())
+ {
+ // direct model data is the page size, get and use it
+ const SdrPage& rOwnerPage = GetMasterPageDescriptor().GetOwnerPage();
+ const basegfx::B2DRange aInnerRange(
+ rOwnerPage.GetLeftBorder(), rOwnerPage.GetUpperBorder(),
+ rOwnerPage.GetWidth() - rOwnerPage.GetRightBorder(),
+ rOwnerPage.GetHeight() - rOwnerPage.GetLowerBorder());
+ const basegfx::B2DRange aOuterRange(
+ 0, 0, rOwnerPage.GetWidth(), rOwnerPage.GetHeight());
+ // ??? somehow only the master page's bit is used
+ bool const isFullSize(GetMasterPageDescriptor().GetUsedPage().IsBackgroundFullSize());
+ const basegfx::B2DPolygon aFillPolygon(
+ basegfx::utils::createPolygonFromRect(isFullSize ? aOuterRange : aInnerRange));
+ const drawinglayer::primitive2d::Primitive2DReference xReference(
+ drawinglayer::primitive2d::createPolyPolygonFillPrimitive(
+ basegfx::B2DPolyPolygon(aFillPolygon),
+ aFill,
+ drawinglayer::attribute::FillGradientAttribute()));
+
+ rVisitor.visit(xReference);
+ }
+ }
+
+ // basic constructor
+ ViewContactOfMasterPageDescriptor::ViewContactOfMasterPageDescriptor(sdr::MasterPageDescriptor& rDescriptor)
+ : mrMasterPageDescriptor(rDescriptor)
+ {
+ }
+
+ // The destructor.
+ ViewContactOfMasterPageDescriptor::~ViewContactOfMasterPageDescriptor()
+ {
+ }
+
+ sal_uInt32 ViewContactOfMasterPageDescriptor::GetObjectCount() const
+ {
+ return GetMasterPageDescriptor().GetUsedPage().GetObjCount();
+ }
+
+ ViewContact& ViewContactOfMasterPageDescriptor::GetViewContact(sal_uInt32 nIndex) const
+ {
+ return GetMasterPageDescriptor().GetUsedPage().GetObj(nIndex)->GetViewContact();
+ }
+
+ ViewContact* ViewContactOfMasterPageDescriptor::GetParentContact() const
+ {
+ return &(GetMasterPageDescriptor().GetOwnerPage().GetViewContact());
+ }
+
+} // end of namespace
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/sdr/contact/viewcontactofpageobj.cxx b/svx/source/sdr/contact/viewcontactofpageobj.cxx
new file mode 100644
index 000000000..ffcc18fbc
--- /dev/null
+++ b/svx/source/sdr/contact/viewcontactofpageobj.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 <sdr/contact/viewcontactofpageobj.hxx>
+#include <svx/svdopage.hxx>
+#include <vcl/canvastools.hxx>
+#include <svx/sdr/contact/viewobjectcontact.hxx>
+#include <sdr/contact/viewobjectcontactofpageobj.hxx>
+#include <basegfx/polygon/b2dpolygontools.hxx>
+#include <drawinglayer/primitive2d/PolygonHairlinePrimitive2D.hxx>
+
+namespace sdr::contact
+{
+ViewObjectContact&
+ViewContactOfPageObj::CreateObjectSpecificViewObjectContact(ObjectContact& rObjectContact)
+{
+ ViewObjectContact* pRetval = new ViewObjectContactOfPageObj(rObjectContact, *this);
+ return *pRetval;
+}
+
+ViewContactOfPageObj::ViewContactOfPageObj(SdrPageObj& rPageObj)
+ : ViewContactOfSdrObj(rPageObj)
+{
+}
+
+ViewContactOfPageObj::~ViewContactOfPageObj() {}
+
+// #i35972# React on changes of the object of this ViewContact
+void ViewContactOfPageObj::ActionChanged()
+{
+ static bool bIsInActionChange(false);
+
+ if (!bIsInActionChange)
+ {
+ // set recursion flag, see description in *.hxx
+ bIsInActionChange = true;
+
+ // call parent
+ ViewContactOfSdrObj::ActionChanged();
+
+ // reset recursion flag, see description in *.hxx
+ bIsInActionChange = false;
+ }
+}
+
+void ViewContactOfPageObj::createViewIndependentPrimitive2DSequence(
+ drawinglayer::primitive2d::Primitive2DDecompositionVisitor& rVisitor) const
+{
+ // create graphical visualisation data. Since this is the view-independent version which should not be used,
+ // create a replacement graphic visualisation here. Use GetLastBoundRect to access the model data directly
+ // which is aOutRect for SdrPageObj.
+ const tools::Rectangle aModelRectangle(GetPageObj().GetLastBoundRect());
+ const basegfx::B2DRange aModelRange = vcl::unotools::b2DRectangleFromRectangle(aModelRectangle);
+ const basegfx::B2DPolygon aOutline(basegfx::utils::createPolygonFromRect(aModelRange));
+ const basegfx::BColor aYellow(1.0, 1.0, 0.0);
+ const drawinglayer::primitive2d::Primitive2DReference xReference(
+ new drawinglayer::primitive2d::PolygonHairlinePrimitive2D(aOutline, aYellow));
+
+ rVisitor.visit(xReference);
+}
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/sdr/contact/viewcontactofsdrcaptionobj.cxx b/svx/source/sdr/contact/viewcontactofsdrcaptionobj.cxx
new file mode 100644
index 000000000..2e668b4dd
--- /dev/null
+++ b/svx/source/sdr/contact/viewcontactofsdrcaptionobj.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 <sdr/contact/viewcontactofsdrcaptionobj.hxx>
+#include <svx/svdocapt.hxx>
+#include <sdr/primitive2d/sdrattributecreator.hxx>
+#include <sdr/primitive2d/sdrcaptionprimitive2d.hxx>
+#include <basegfx/matrix/b2dhommatrixtools.hxx>
+
+
+// includes for special text box shadow (SC)
+
+#include <svl/itemset.hxx>
+#include <svx/xhatch.hxx>
+#include <svx/xfillit0.hxx>
+#include <svx/xflhtit.hxx>
+#include <svx/xflclit.hxx>
+#include <svx/xfltrit.hxx>
+#include <svx/xlineit0.hxx>
+#include <svx/sdmetitm.hxx>
+#include <svx/sdprcitm.hxx>
+#include <basegfx/polygon/b2dpolygontools.hxx>
+#include <sdr/primitive2d/sdrdecompositiontools.hxx>
+#include <basegfx/polygon/b2dpolygonclipper.hxx>
+#include <vcl/canvastools.hxx>
+
+using namespace com::sun::star;
+
+namespace sdr::contact
+{
+ ViewContactOfSdrCaptionObj::ViewContactOfSdrCaptionObj(SdrCaptionObj& rCaptionObj)
+ : ViewContactOfSdrRectObj(rCaptionObj)
+ {
+ }
+
+ ViewContactOfSdrCaptionObj::~ViewContactOfSdrCaptionObj()
+ {
+ }
+
+ void ViewContactOfSdrCaptionObj::createViewIndependentPrimitive2DSequence(drawinglayer::primitive2d::Primitive2DDecompositionVisitor& rVisitor) const
+ {
+ const SdrCaptionObj& rCaptionObj(static_cast<const SdrCaptionObj&>(GetSdrObject()));
+ const SfxItemSet& rItemSet = rCaptionObj.GetMergedItemSet();
+ const drawinglayer::attribute::SdrLineFillEffectsTextAttribute aAttribute(
+ drawinglayer::primitive2d::createNewSdrLineFillEffectsTextAttribute(
+ rItemSet,
+ rCaptionObj.getText(0),
+ false));
+
+ // take unrotated snap rect (direct model data) for position and size
+ const tools::Rectangle aRectangle(rCaptionObj.GetGeoRect());
+ const ::basegfx::B2DRange aObjectRange = vcl::unotools::b2DRectangleFromRectangle(aRectangle);
+ const GeoStat& rGeoStat(rCaptionObj.GetGeoStat());
+
+ // fill object matrix
+ basegfx::B2DHomMatrix aObjectMatrix(basegfx::utils::createScaleShearXRotateTranslateB2DHomMatrix(
+ aObjectRange.getWidth(), aObjectRange.getHeight(),
+ -rGeoStat.mfTanShearAngle,
+ rGeoStat.nRotationAngle ? toRadians(36000_deg100 - rGeoStat.nRotationAngle) : 0.0,
+ aObjectRange.getMinX(), aObjectRange.getMinY()));
+
+ // calculate corner radius
+ double fCornerRadiusX;
+ double fCornerRadiusY;
+ drawinglayer::primitive2d::calculateRelativeCornerRadius(
+ rCaptionObj.GetEckenradius(), aObjectRange, fCornerRadiusX, fCornerRadiusY);
+ const basegfx::B2DPolygon aTail(rCaptionObj.getTailPolygon());
+
+ // create primitive. Always create one (even if invisible) to let the decomposition
+ // of SdrCaptionPrimitive2D create needed invisible elements for HitTest and BoundRect
+ drawinglayer::primitive2d::Primitive2DReference xReference(
+ new drawinglayer::primitive2d::SdrCaptionPrimitive2D(
+ aObjectMatrix,
+ aAttribute,
+ aTail,
+ fCornerRadiusX,
+ fCornerRadiusY));
+
+ if(!aAttribute.isDefault() && rCaptionObj.GetSpecialTextBoxShadow())
+ {
+ // for SC, the caption object may have a specialized shadow. The usual object shadow is off
+ // and a specialized shadow gets created here (see old paint)
+ const XColorItem& rShadColItem = rItemSet.Get(SDRATTR_SHADOWCOLOR);
+ const sal_uInt16 nShadowTransparence(rItemSet.Get(SDRATTR_SHADOWTRANSPARENCE).GetValue());
+ const Color aShadowColor(rShadColItem.GetColorValue());
+ const drawing::FillStyle eShadowStyle = rItemSet.Get(XATTR_FILLSTYLE).GetValue();
+
+ // Create own ItemSet and modify as needed
+ // Always hide lines for special calc shadow
+ SfxItemSet aSet(rItemSet);
+ aSet.Put(XLineStyleItem(drawing::LineStyle_NONE));
+
+ if(drawing::FillStyle_HATCH == eShadowStyle)
+ {
+ // #41666# Hatch color is set hard to shadow color
+ XHatch aHatch = rItemSet.Get(XATTR_FILLHATCH).GetHatchValue();
+ aHatch.SetColor(aShadowColor);
+ aSet.Put(XFillHatchItem(OUString(),aHatch));
+ }
+ else
+ {
+ if(drawing::FillStyle_SOLID != eShadowStyle)
+ {
+ // force fill to solid (for Gradient, Bitmap and *no* fill (#119750# not filled comments *have* shadow))
+ aSet.Put(XFillStyleItem(drawing::FillStyle_SOLID));
+ }
+
+ aSet.Put(XFillColorItem(OUString(),aShadowColor));
+ aSet.Put(XFillTransparenceItem(nShadowTransparence));
+ }
+
+ // create FillAttribute from modified ItemSet
+ const drawinglayer::attribute::SdrFillAttribute aFill(
+ drawinglayer::primitive2d::createNewSdrFillAttribute(aSet));
+ drawinglayer::primitive2d::Primitive2DReference xSpecialShadow;
+
+ if(!aFill.isDefault() && 1.0 != aFill.getTransparence())
+ {
+ // add shadow offset to object matrix
+ const sal_uInt32 nXDist(rItemSet.Get(SDRATTR_SHADOWXDIST).GetValue());
+ const sal_uInt32 nYDist(rItemSet.Get(SDRATTR_SHADOWYDIST).GetValue());
+
+ if(nXDist || nYDist)
+ {
+ // #119750# create object and shadow outline, clip shadow outline
+ // on object outline. If there is a rest, create shadow. Do this to
+ // emulate that shadow is *not* visible behind the object for
+ // transparent object fill for comments in excel
+ basegfx::B2DPolygon aObjectOutline(
+ basegfx::utils::createPolygonFromRect(
+ basegfx::B2DRange(0.0, 0.0, 1.0, 1.0),
+ fCornerRadiusX,
+ fCornerRadiusY));
+ aObjectOutline.transform(aObjectMatrix);
+
+ // create shadow outline
+ basegfx::B2DPolygon aShadowOutline(aObjectOutline);
+ aShadowOutline.transform(
+ basegfx::utils::createTranslateB2DHomMatrix(nXDist, nYDist));
+
+ // clip shadow outline against object outline
+ const basegfx::B2DPolyPolygon aClippedShadow(
+ basegfx::utils::clipPolygonOnPolyPolygon(
+ aShadowOutline,
+ basegfx::B2DPolyPolygon(aObjectOutline),
+ false, // take the outside
+ false));
+
+ if(aClippedShadow.count())
+ {
+ // if there is shadow, create the specialized shadow primitive
+ xSpecialShadow = drawinglayer::primitive2d::createPolyPolygonFillPrimitive(
+ aClippedShadow,
+ aFill,
+ drawinglayer::attribute::FillGradientAttribute());
+ }
+ }
+ }
+
+ if(xSpecialShadow.is())
+ {
+ // if we really got a special shadow, create a two-element retval with the shadow
+ // behind the standard object's geometry
+ rVisitor.visit(std::move(xSpecialShadow));
+ }
+ }
+
+ rVisitor.visit(std::move(xReference));
+ }
+
+} // end of namespace
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/sdr/contact/viewcontactofsdrcircobj.cxx b/svx/source/sdr/contact/viewcontactofsdrcircobj.cxx
new file mode 100644
index 000000000..042d2160a
--- /dev/null
+++ b/svx/source/sdr/contact/viewcontactofsdrcircobj.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 <sdr/contact/viewcontactofsdrcircobj.hxx>
+#include <svx/svdocirc.hxx>
+#include <svx/sdangitm.hxx>
+#include <sdr/primitive2d/sdrattributecreator.hxx>
+#include <sdr/primitive2d/sdrellipseprimitive2d.hxx>
+#include <svl/itemset.hxx>
+#include <basegfx/matrix/b2dhommatrixtools.hxx>
+#include <vcl/canvastools.hxx>
+
+
+namespace sdr::contact
+{
+ ViewContactOfSdrCircObj::ViewContactOfSdrCircObj(SdrCircObj& rCircObj)
+ : ViewContactOfSdrRectObj(rCircObj)
+ {
+ }
+
+ ViewContactOfSdrCircObj::~ViewContactOfSdrCircObj()
+ {
+ }
+
+ void ViewContactOfSdrCircObj::createViewIndependentPrimitive2DSequence(drawinglayer::primitive2d::Primitive2DDecompositionVisitor& rVisitor) const
+ {
+ const SfxItemSet& rItemSet = GetCircObj().GetMergedItemSet();
+ const drawinglayer::attribute::SdrLineFillEffectsTextAttribute aAttribute(
+ drawinglayer::primitive2d::createNewSdrLineFillEffectsTextAttribute(
+ rItemSet,
+ GetCircObj().getText(0),
+ false));
+
+ // take unrotated snap rect (direct model data) for position and size
+ const basegfx::B2DRange aObjectRange = vcl::unotools::b2DRectangleFromRectangle(GetCircObj().GetGeoRect());
+ const GeoStat& rGeoStat(GetCircObj().GetGeoStat());
+
+ // fill object matrix
+ const basegfx::B2DHomMatrix aObjectMatrix(
+ basegfx::utils::createScaleShearXRotateTranslateB2DHomMatrix(
+ aObjectRange.getWidth(), aObjectRange.getHeight(),
+ -rGeoStat.mfTanShearAngle,
+ rGeoStat.nRotationAngle ? toRadians(36000_deg100 - rGeoStat.nRotationAngle) : 0.0,
+ aObjectRange.getMinX(), aObjectRange.getMinY()));
+
+ // create primitive data
+ const SdrObjKind nIdentifier(GetCircObj().GetObjIdentifier());
+
+ // always create primitives to allow the decomposition of SdrEllipsePrimitive2D
+ // or SdrEllipseSegmentPrimitive2D to create needed invisible elements for HitTest
+ // and/or BoundRect
+ if(SdrObjKind::CircleOrEllipse == nIdentifier)
+ {
+ const drawinglayer::primitive2d::Primitive2DReference xReference(
+ new drawinglayer::primitive2d::SdrEllipsePrimitive2D(
+ aObjectMatrix,
+ aAttribute));
+
+ rVisitor.visit(xReference);
+ }
+ else
+ {
+ const auto nNewStart(rItemSet.Get(SDRATTR_CIRCSTARTANGLE).GetValue());
+ const auto nNewEnd(rItemSet.Get(SDRATTR_CIRCENDANGLE).GetValue());
+ const double fStart(toRadians((36000_deg100 - nNewEnd) % 36000_deg100));
+ const double fEnd(toRadians((36000_deg100 - nNewStart) % 36000_deg100));
+ const bool bCloseSegment(SdrObjKind::CircleArc != nIdentifier);
+ const bool bCloseUsingCenter(SdrObjKind::CircleSection == nIdentifier);
+
+ const drawinglayer::primitive2d::Primitive2DReference xReference(
+ new drawinglayer::primitive2d::SdrEllipseSegmentPrimitive2D(
+ aObjectMatrix,
+ aAttribute,
+ fStart,
+ fEnd,
+ bCloseSegment,
+ bCloseUsingCenter));
+
+ rVisitor.visit(xReference);
+ }
+ }
+
+} // end of namespace
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/sdr/contact/viewcontactofsdredgeobj.cxx b/svx/source/sdr/contact/viewcontactofsdredgeobj.cxx
new file mode 100644
index 000000000..ab9587856
--- /dev/null
+++ b/svx/source/sdr/contact/viewcontactofsdredgeobj.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 <sdr/contact/viewcontactofsdredgeobj.hxx>
+#include <svx/svdoedge.hxx>
+#include <sdr/primitive2d/sdrattributecreator.hxx>
+#include <sdr/primitive2d/sdrconnectorprimitive2d.hxx>
+#include <osl/diagnose.h>
+
+
+namespace sdr::contact
+{
+ ViewContactOfSdrEdgeObj::ViewContactOfSdrEdgeObj(SdrEdgeObj& rEdgeObj)
+ : ViewContactOfTextObj(rEdgeObj)
+ {
+ }
+
+ ViewContactOfSdrEdgeObj::~ViewContactOfSdrEdgeObj()
+ {
+ }
+
+ void ViewContactOfSdrEdgeObj::createViewIndependentPrimitive2DSequence(drawinglayer::primitive2d::Primitive2DDecompositionVisitor& rVisitor) const
+ {
+ const basegfx::B2DPolygon aEdgeTrack(GetEdgeObj().getEdgeTrack());
+
+ // what to do when no EdgeTrack is provided (HitTest and selectability) ?
+ OSL_ENSURE(0 != aEdgeTrack.count(), "Connectors with no geometry are not allowed (!)");
+
+ // ckeck attributes
+ const SfxItemSet& rItemSet = GetEdgeObj().GetMergedItemSet();
+ const drawinglayer::attribute::SdrLineEffectsTextAttribute aAttribute(
+ drawinglayer::primitive2d::createNewSdrLineEffectsTextAttribute(
+ rItemSet,
+ GetEdgeObj().getText(0)));
+
+ // create primitive. Always create primitives to allow the decomposition of
+ // SdrConnectorPrimitive2D to create needed invisible elements for HitTest
+ // and/or BoundRect
+ const drawinglayer::primitive2d::Primitive2DReference xReference(
+ new drawinglayer::primitive2d::SdrConnectorPrimitive2D(
+ aAttribute,
+ aEdgeTrack));
+
+ rVisitor.visit(xReference);
+ }
+
+} // end of namespace
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/sdr/contact/viewcontactofsdrmeasureobj.cxx b/svx/source/sdr/contact/viewcontactofsdrmeasureobj.cxx
new file mode 100644
index 000000000..eb04efd71
--- /dev/null
+++ b/svx/source/sdr/contact/viewcontactofsdrmeasureobj.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 <sdr/contact/viewcontactofsdrmeasureobj.hxx>
+#include <svx/svdomeas.hxx>
+#include <sdr/primitive2d/sdrattributecreator.hxx>
+#include <svl/itemset.hxx>
+#include <svx/sdmetitm.hxx>
+#include <svx/sxmbritm.hxx>
+#include <svx/sxmtritm.hxx>
+#include <sxmtaitm.hxx>
+#include <sdr/primitive2d/sdrmeasureprimitive2d.hxx>
+#include <svx/sxmtpitm.hxx>
+
+
+namespace sdr::contact
+{
+ ViewContactOfSdrMeasureObj::ViewContactOfSdrMeasureObj(SdrMeasureObj& rMeasureObj)
+ : ViewContactOfTextObj(rMeasureObj)
+ {
+ }
+
+ ViewContactOfSdrMeasureObj::~ViewContactOfSdrMeasureObj()
+ {
+ }
+
+ void ViewContactOfSdrMeasureObj::createViewIndependentPrimitive2DSequence(drawinglayer::primitive2d::Primitive2DDecompositionVisitor& rVisitor) const
+ {
+ const SfxItemSet& rItemSet = GetMeasureObj().GetMergedItemSet();
+ const drawinglayer::attribute::SdrLineEffectsTextAttribute aAttribute(
+ drawinglayer::primitive2d::createNewSdrLineEffectsTextAttribute(
+ rItemSet,
+ GetMeasureObj().getText(0)));
+
+ // take properties which are the model data.
+ const ::basegfx::B2DPoint aStart(GetMeasureObj().GetPoint(0).X(), GetMeasureObj().GetPoint(0).Y());
+ const ::basegfx::B2DPoint aEnd(GetMeasureObj().GetPoint(1).X(), GetMeasureObj().GetPoint(1).Y());
+ const double fDistance(rItemSet.Get(SDRATTR_MEASURELINEDIST).GetValue());
+ const double fUpperDistance(rItemSet.Get(SDRATTR_MEASUREHELPLINEOVERHANG).GetValue());
+ const double fLowerDistance(rItemSet.Get(SDRATTR_MEASUREHELPLINEDIST).GetValue());
+ const double fLeftDelta(rItemSet.Get(SDRATTR_MEASUREHELPLINE1LEN).GetValue());
+ const double fRightDelta(rItemSet.Get(SDRATTR_MEASUREHELPLINE2LEN).GetValue());
+ const bool bBelow(rItemSet.Get(SDRATTR_MEASUREBELOWREFEDGE).GetValue());
+ const bool bTextRotation(rItemSet.Get(SDRATTR_MEASURETEXTROTA90).GetValue());
+ const bool bTextAutoAngle(rItemSet.Get(SDRATTR_MEASURETEXTAUTOANGLE).GetValue());
+ drawinglayer::primitive2d::MeasureTextPosition aMTPHor(drawinglayer::primitive2d::MEASURETEXTPOSITION_AUTOMATIC);
+ drawinglayer::primitive2d::MeasureTextPosition aMTPVer(drawinglayer::primitive2d::MEASURETEXTPOSITION_AUTOMATIC);
+
+ switch(rItemSet.Get(SDRATTR_MEASURETEXTHPOS).GetValue())
+ {
+ case css::drawing::MeasureTextHorzPos::MeasureTextHorzPos_LEFTOUTSIDE:
+ {
+ aMTPHor = drawinglayer::primitive2d::MEASURETEXTPOSITION_NEGATIVE;
+ break;
+ }
+ case css::drawing::MeasureTextHorzPos::MeasureTextHorzPos_INSIDE:
+ {
+ aMTPHor = drawinglayer::primitive2d::MEASURETEXTPOSITION_CENTERED;
+ break;
+ }
+ case css::drawing::MeasureTextHorzPos::MeasureTextHorzPos_RIGHTOUTSIDE:
+ {
+ aMTPHor = drawinglayer::primitive2d::MEASURETEXTPOSITION_POSITIVE;
+ break;
+ }
+ default: // css::drawing::MeasureTextHorzPos::MeasureTextHorzPos_AUTO
+ {
+ break;
+ }
+ }
+
+ switch(rItemSet.Get(SDRATTR_MEASURETEXTVPOS).GetValue())
+ {
+ case css::drawing::MeasureTextVertPos_EAST:
+ {
+ aMTPVer = drawinglayer::primitive2d::MEASURETEXTPOSITION_NEGATIVE;
+ break;
+ }
+ case css::drawing::MeasureTextVertPos_CENTERED:
+ {
+ aMTPVer = drawinglayer::primitive2d::MEASURETEXTPOSITION_CENTERED;
+ break;
+ }
+ case css::drawing::MeasureTextVertPos_WEST:
+ {
+ aMTPVer = drawinglayer::primitive2d::MEASURETEXTPOSITION_POSITIVE;
+ break;
+ }
+ default : // css::drawing::MeasureTextVertPos_AUTO
+ {
+ break;
+ }
+ }
+
+ // create primitive with the model data. Always create primitives to allow the
+ // decomposition of SdrMeasurePrimitive2D to create needed invisible elements for HitTest
+ // and/or BoundRect
+ const drawinglayer::primitive2d::Primitive2DReference xReference(
+ new drawinglayer::primitive2d::SdrMeasurePrimitive2D(
+ aAttribute, aStart, aEnd,
+ aMTPHor, aMTPVer, fDistance,
+ fUpperDistance, fLowerDistance,
+ fLeftDelta, fRightDelta, bBelow,
+ bTextRotation, bTextAutoAngle));
+
+ rVisitor.visit(xReference);
+ }
+} // end of namespace
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/sdr/contact/viewcontactofsdrmediaobj.cxx b/svx/source/sdr/contact/viewcontactofsdrmediaobj.cxx
new file mode 100644
index 000000000..7c713bc4b
--- /dev/null
+++ b/svx/source/sdr/contact/viewcontactofsdrmediaobj.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 <svx/sdr/contact/viewcontactofsdrmediaobj.hxx>
+#include <svx/svdomedia.hxx>
+#include <sdr/contact/viewobjectcontactofsdrmediaobj.hxx>
+#include <drawinglayer/primitive2d/mediaprimitive2d.hxx>
+#include <vcl/canvastools.hxx>
+
+namespace sdr::contact {
+
+ViewContactOfSdrMediaObj::ViewContactOfSdrMediaObj( SdrMediaObj& rMediaObj ) :
+ ViewContactOfSdrObj( rMediaObj )
+{
+}
+
+ViewContactOfSdrMediaObj::~ViewContactOfSdrMediaObj()
+{
+}
+
+ViewObjectContact& ViewContactOfSdrMediaObj::CreateObjectSpecificViewObjectContact(ObjectContact& rObjectContact)
+{
+ return *( new ViewObjectContactOfSdrMediaObj( rObjectContact, *this, static_cast< SdrMediaObj& >( GetSdrObject() ).getMediaProperties() ) );
+}
+
+Size ViewContactOfSdrMediaObj::getPreferredSize() const
+{
+ // #i71805# Since we may have a whole bunch of VOCs here, make a loop
+ // return first useful size -> the size from the first which is visualized as a window
+ const sal_uInt32 nCount(getViewObjectContactCount());
+
+ for(sal_uInt32 a(0); a < nCount; a++)
+ {
+ ViewObjectContact* pCandidate = getViewObjectContact(a);
+ Size aSize(pCandidate ? static_cast< ViewObjectContactOfSdrMediaObj* >(pCandidate)->getPreferredSize() : Size());
+
+ if(0 != aSize.getWidth() || 0 != aSize.getHeight())
+ {
+ return aSize;
+ }
+ }
+
+ return Size();
+}
+
+void ViewContactOfSdrMediaObj::updateMediaItem( ::avmedia::MediaItem& rItem ) const
+{
+ // #i71805# Since we may have a whole bunch of VOCs here, make a loop
+ const sal_uInt32 nCount(getViewObjectContactCount());
+
+ for(sal_uInt32 a(0); a < nCount; a++)
+ {
+ ViewObjectContact* pCandidate = getViewObjectContact(a);
+
+ if(pCandidate)
+ {
+ static_cast< ViewObjectContactOfSdrMediaObj* >(pCandidate)->updateMediaItem(rItem);
+ }
+ }
+}
+
+void ViewContactOfSdrMediaObj::executeMediaItem( const ::avmedia::MediaItem& rItem )
+{
+ const sal_uInt32 nCount(getViewObjectContactCount());
+
+ for(sal_uInt32 a(0); a < nCount; a++)
+ {
+ ViewObjectContact* pCandidate = getViewObjectContact(a);
+
+ if(pCandidate)
+ {
+ static_cast< ViewObjectContactOfSdrMediaObj* >(pCandidate)->executeMediaItem(rItem);
+ }
+ }
+}
+
+void ViewContactOfSdrMediaObj::mediaPropertiesChanged( const ::avmedia::MediaItem& rNewState )
+{
+ static_cast< SdrMediaObj& >(GetSdrObject()).mediaPropertiesChanged(rNewState);
+}
+
+void ViewContactOfSdrMediaObj::createViewIndependentPrimitive2DSequence(drawinglayer::primitive2d::Primitive2DDecompositionVisitor& rVisitor) const
+{
+ // create range using the model data directly. This is in SdrTextObj::aRect which i will access using
+ // GetGeoRect() to not trigger any calculations. It's the unrotated geometry which is okay for MediaObjects ATM.
+ const tools::Rectangle aRectangle(GetSdrMediaObj().GetGeoRect());
+ const basegfx::B2DRange aRange = vcl::unotools::b2DRectangleFromRectangle(aRectangle);
+
+ // create object transform
+ basegfx::B2DHomMatrix aTransform;
+ aTransform.set(0, 0, aRange.getWidth());
+ aTransform.set(1, 1, aRange.getHeight());
+ aTransform.set(0, 2, aRange.getMinX());
+ aTransform.set(1, 2, aRange.getMinY());
+
+ // create media primitive. Always create primitives to allow the
+ // decomposition of MediaPrimitive2D to create needed invisible elements for HitTest
+ // and/or BoundRect
+ const basegfx::BColor aBackgroundColor(67.0 / 255.0, 67.0 / 255.0, 67.0 / 255.0);
+ const OUString& rURL(GetSdrMediaObj().getURL());
+ const sal_uInt32 nPixelBorder(4);
+ const drawinglayer::primitive2d::Primitive2DReference xRetval(
+ new drawinglayer::primitive2d::MediaPrimitive2D(
+ aTransform, rURL, aBackgroundColor, nPixelBorder,
+ GetSdrMediaObj().getSnapshot()));
+
+ rVisitor.visit(xRetval);
+}
+
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/sdr/contact/viewcontactofsdrobj.cxx b/svx/source/sdr/contact/viewcontactofsdrobj.cxx
new file mode 100644
index 000000000..1483b5764
--- /dev/null
+++ b/svx/source/sdr/contact/viewcontactofsdrobj.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 <svx/sdr/contact/viewcontactofsdrobj.hxx>
+#include <svx/sdr/contact/viewobjectcontactofsdrobj.hxx>
+#include <svx/sdr/contact/viewobjectcontact.hxx>
+#include <svx/svdobj.hxx>
+#include <tools/debug.hxx>
+#include <svx/svdpage.hxx>
+#include <svx/sdr/contact/objectcontact.hxx>
+#include <drawinglayer/primitive2d/markerarrayprimitive2d.hxx>
+#include <drawinglayer/primitive2d/objectinfoprimitive2d.hxx>
+#include <svx/svdhdl.hxx>
+
+namespace sdr::contact {
+
+// Create an Object-Specific ViewObjectContact, set ViewContact and
+// ObjectContact. Always needs to return something.
+ViewObjectContact& ViewContactOfSdrObj::CreateObjectSpecificViewObjectContact(ObjectContact& rObjectContact)
+{
+ ViewObjectContact* pRetval = new ViewObjectContactOfSdrObj(rObjectContact, *this);
+ DBG_ASSERT(pRetval, "ViewContactOfSdrObj::CreateObjectSpecificViewObjectContact() failed (!)");
+
+ return *pRetval;
+}
+
+ViewContactOfSdrObj::ViewContactOfSdrObj(SdrObject& rObj)
+: mrObject(rObj)
+{
+}
+
+ViewContactOfSdrObj::~ViewContactOfSdrObj()
+{
+}
+
+// Access to possible sub-hierarchy
+sal_uInt32 ViewContactOfSdrObj::GetObjectCount() const
+{
+ if(GetSdrObject().GetSubList())
+ {
+ return GetSdrObject().GetSubList()->GetObjCount();
+ }
+
+ return 0;
+}
+
+ViewContact& ViewContactOfSdrObj::GetViewContact(sal_uInt32 nIndex) const
+{
+ assert(GetSdrObject().GetSubList() &&
+ "ViewContactOfSdrObj::GetViewContact: Access to non-existent Sub-List (!)");
+ SdrObject* pObj = GetSdrObject().GetSubList()->GetObj(nIndex);
+ assert(pObj && "ViewContactOfSdrObj::GetViewContact: Corrupt SdrObjList (!)");
+ return pObj->GetViewContact();
+}
+
+ViewContact* ViewContactOfSdrObj::GetParentContact() const
+{
+ ViewContact* pRetval = nullptr;
+ SdrObjList* pObjList = GetSdrObject().getParentSdrObjListFromSdrObject();
+
+ if(pObjList)
+ {
+ if(auto pPage = dynamic_cast<SdrPage*>( pObjList))
+ {
+ // Is a page
+ pRetval = &(pPage->GetViewContact());
+ }
+ else
+ {
+ // Is a group?
+ if(pObjList->getSdrObjectFromSdrObjList())
+ {
+ pRetval = &(pObjList->getSdrObjectFromSdrObjList()->GetViewContact());
+ }
+ }
+ }
+
+ return pRetval;
+}
+
+// React on changes of the object of this ViewContact
+void ViewContactOfSdrObj::ActionChanged()
+{
+ // look for own changes
+ if (SdrTextObj* pTextObj = dynamic_cast<SdrTextObj*>(&GetSdrObject()))
+ {
+ // tdf#146860 no idea why, but calling this makes the text boxes render properly
+ pTextObj->GetTextAniKind();
+ }
+
+ // call parent
+ ViewContact::ActionChanged();
+}
+
+// override for accessing the SdrObject
+SdrObject* ViewContactOfSdrObj::TryToGetSdrObject() const
+{
+ return &GetSdrObject();
+}
+
+
+// primitive stuff
+
+// add Gluepoints (if available)
+drawinglayer::primitive2d::Primitive2DContainer ViewContactOfSdrObj::createGluePointPrimitive2DSequence() const
+{
+ drawinglayer::primitive2d::Primitive2DContainer xRetval;
+ const SdrGluePointList* pGluePointList = GetSdrObject().GetGluePointList();
+
+ if(pGluePointList)
+ {
+ const sal_uInt32 nCount(pGluePointList->GetCount());
+
+ if(nCount)
+ {
+ // prepare point vector
+ std::vector< basegfx::B2DPoint > aGluepointVector;
+
+ // create GluePoint primitives. ATM these are relative to the SnapRect
+ for(sal_uInt32 a(0); a < nCount; a++)
+ {
+ const SdrGluePoint& rCandidate = (*pGluePointList)[static_cast<sal_uInt16>(a)];
+ const Point aPosition(rCandidate.GetAbsolutePos(GetSdrObject()));
+
+ aGluepointVector.emplace_back(aPosition.X(), aPosition.Y());
+ }
+
+ if(!aGluepointVector.empty())
+ {
+ const drawinglayer::primitive2d::Primitive2DReference xReference(
+ new drawinglayer::primitive2d::MarkerArrayPrimitive2D(
+ std::move(aGluepointVector), SdrHdl::createGluePointBitmap()));
+ xRetval = drawinglayer::primitive2d::Primitive2DContainer{ xReference };
+ }
+ }
+ }
+
+ return xRetval;
+}
+
+drawinglayer::primitive2d::Primitive2DContainer ViewContactOfSdrObj::embedToObjectSpecificInformation(drawinglayer::primitive2d::Primitive2DContainer aSource) const
+{
+ if(!aSource.empty() &&
+ (!GetSdrObject().GetName().isEmpty() ||
+ !GetSdrObject().GetTitle().isEmpty() ||
+ !GetSdrObject().GetDescription().isEmpty()))
+ {
+ const drawinglayer::primitive2d::Primitive2DReference xRef(
+ new drawinglayer::primitive2d::ObjectInfoPrimitive2D(
+ std::move(aSource),
+ GetSdrObject().GetName(),
+ GetSdrObject().GetTitle(),
+ GetSdrObject().GetDescription()));
+
+ return drawinglayer::primitive2d::Primitive2DContainer { xRef };
+ }
+
+ return aSource;
+}
+
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/sdr/contact/viewcontactofsdrobjcustomshape.cxx b/svx/source/sdr/contact/viewcontactofsdrobjcustomshape.cxx
new file mode 100644
index 000000000..e03dfced8
--- /dev/null
+++ b/svx/source/sdr/contact/viewcontactofsdrobjcustomshape.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 <sdr/contact/viewcontactofsdrobjcustomshape.hxx>
+#include <svx/svdoashp.hxx>
+#include <svx/sdooitm.hxx>
+#include <sdr/primitive2d/sdrattributecreator.hxx>
+#include <sdr/primitive2d/sdrcustomshapeprimitive2d.hxx>
+#include <basegfx/matrix/b2dhommatrixtools.hxx>
+#include <svx/obj3d.hxx>
+#include <vcl/canvastools.hxx>
+
+
+namespace sdr::contact
+{
+ ViewContactOfSdrObjCustomShape::ViewContactOfSdrObjCustomShape(SdrObjCustomShape& rCustomShape)
+ : ViewContactOfTextObj(rCustomShape)
+ {
+ }
+
+ ViewContactOfSdrObjCustomShape::~ViewContactOfSdrObjCustomShape()
+ {
+ }
+
+ basegfx::B2DRange ViewContactOfSdrObjCustomShape::getCorrectedTextBoundRect() const
+ {
+ const tools::Rectangle aObjectBound(GetCustomShapeObj().GetGeoRect());
+ tools::Rectangle aTextBound(aObjectBound);
+ GetCustomShapeObj().GetTextBounds(aTextBound);
+ basegfx::B2DRange aTextRange = vcl::unotools::b2DRectangleFromRectangle(aTextBound);
+ const basegfx::B2DRange aObjectRange = vcl::unotools::b2DRectangleFromRectangle(aObjectBound);
+
+ // no need to correct if no extra text range
+ if(aTextRange != aObjectRange)
+ {
+ const GeoStat& rGeoStat(GetCustomShapeObj().GetGeoStat());
+
+ // only correct when rotation and/or shear is used
+ if(rGeoStat.nShearAngle || rGeoStat.nRotationAngle )
+ {
+ // text range needs to be corrected by
+ // aObjectRange.getCenter() - aRotObjectRange.getCenter() since it's
+ // defined differently by using rotation around object center. Start
+ // with positive part
+ basegfx::B2DVector aTranslation(aObjectRange.getCenter());
+
+ // get rotated and sheared object's range
+ basegfx::B2DRange aRotObjectRange(aObjectRange);
+ basegfx::B2DHomMatrix aRotMatrix;
+
+ aRotMatrix.translate(-aObjectRange.getMinimum().getX(), -aObjectRange.getMinimum().getY());
+
+ if(rGeoStat.nShearAngle)
+ {
+ aRotMatrix.shearX(-rGeoStat.mfTanShearAngle);
+ }
+
+ if(rGeoStat.nRotationAngle)
+ {
+ aRotMatrix.rotate(toRadians(36000_deg100 - rGeoStat.nRotationAngle));
+ }
+
+ aRotMatrix.translate(aObjectRange.getMinimum().getX(), aObjectRange.getMinimum().getY());
+ aRotObjectRange.transform(aRotMatrix);
+
+ // add negative translation part
+ aTranslation -= aRotObjectRange.getCenter();
+
+ // create new range
+ aTextRange = basegfx::B2DRange(
+ aTextRange.getMinX() + aTranslation.getX(), aTextRange.getMinY() + aTranslation.getY(),
+ aTextRange.getMaxX() + aTranslation.getX(), aTextRange.getMaxY() + aTranslation.getY());
+ }
+
+ // NbcMirror() of SdrTextObj (from which SdrObjCustomShape is derived), adds a
+ // 180deg rotation around the shape center to GeoStat.nRotationAngle. So remove here the
+ // 180° rotation, which was added by GetTextBounds().
+ if(GetCustomShapeObj().IsMirroredY())
+ {
+ basegfx::B2DHomMatrix aRotMatrix(basegfx::utils::createRotateAroundPoint(
+ aObjectRange.getCenterX(), aObjectRange.getCenterY(), M_PI));
+ aTextRange.transform(aRotMatrix);
+ }
+ }
+
+ return aTextRange;
+ }
+
+ void ViewContactOfSdrObjCustomShape::createViewIndependentPrimitive2DSequence(drawinglayer::primitive2d::Primitive2DDecompositionVisitor& rVisitor) const
+ {
+ const SfxItemSet& rItemSet = GetCustomShapeObj().GetMergedItemSet();
+
+ // #i98072# Get shadow and text; eventually suppress the text if it's
+ // a TextPath FontworkGallery object
+ const drawinglayer::attribute::SdrEffectsTextAttribute aAttribute(
+ drawinglayer::primitive2d::createNewSdrEffectsTextAttribute(
+ rItemSet,
+ GetCustomShapeObj().getText(0),
+ GetCustomShapeObj().IsTextPath()));
+ drawinglayer::primitive2d::Primitive2DContainer xGroup;
+ bool bHasText(!aAttribute.getText().isDefault());
+
+ // create Primitive2DContainer from sub-geometry
+ const SdrObject* pSdrObjRepresentation = GetCustomShapeObj().GetSdrObjectFromCustomShape();
+ bool b3DShape(false);
+
+ if(pSdrObjRepresentation)
+ {
+ // tdf#118498 The processing of SdrObjListIter for SdrIterMode::DeepNoGroups
+ // did change for 3D-Objects, it now correctly enters and iterates the
+ // SdrObjects in the E3dScene (same as for SdrObjGroup). This is more correct
+ // as the old version which just checked for dynamic_cast<const SdrObjGroup*>
+ // and *only* entered these, ignoring E3dScene as grouping-object.
+ // But how to fix that? Taking back the SdrObjListIter change would be easy, but
+ // not correct. After checking ViewContactOfE3dScene and ViewContactOfGroup
+ // I see that both traverse their children by themselves (on VC-Level,
+ // see createViewIndependentPrimitive2DSequence implementations and usage of
+ // GetObjectCount()). Thus in principle iterating here (esp. 'deep') seems to
+ // be wrong anyways, it might have even created wrong and double geometries
+ // (only with complex CustomShapes with multiple representation SdrObjects and
+ // only visible when transparency involved, but runtime-expensive).
+ // Thus: Just do not iterate, will check behaviour deeply.
+ b3DShape = (nullptr != dynamic_cast< const E3dObject* >(pSdrObjRepresentation));
+ pSdrObjRepresentation->GetViewContact().getViewIndependentPrimitive2DContainer(xGroup);
+ }
+
+ if(bHasText || !xGroup.empty())
+ {
+ // prepare text box geometry
+ basegfx::B2DHomMatrix aTextBoxMatrix;
+ bool bWordWrap(false);
+
+ // take unrotated snap rect as default, then get the
+ // unrotated text box. Rotation needs to be done centered
+ const tools::Rectangle aObjectBound(GetCustomShapeObj().GetGeoRect());
+ const basegfx::B2DRange aObjectRange = vcl::unotools::b2DRectangleFromRectangle(aObjectBound);
+
+ if(bHasText)
+ {
+ // #i101684# get the text range unrotated and absolute to the object range
+ const basegfx::B2DRange aTextRange(getCorrectedTextBoundRect());
+
+ // Rotation before scaling
+ if(!basegfx::fTools::equalZero(GetCustomShapeObj().GetExtraTextRotation(true)))
+ {
+ basegfx::B2DVector aTranslation(0.5, 0.5);
+ aTextBoxMatrix.translate( -aTranslation.getX(), -aTranslation.getY() );
+ aTextBoxMatrix.rotate(basegfx::deg2rad(
+ 360.0 - GetCustomShapeObj().GetExtraTextRotation(true)));
+ aTextBoxMatrix.translate( aTranslation.getX(), aTranslation.getY() );
+ }
+ // give text object a size
+ aTextBoxMatrix.scale(aTextRange.getWidth(), aTextRange.getHeight());
+
+ // check if we have a rotation/shear at all to take care of
+ const double fExtraTextRotation(GetCustomShapeObj().GetExtraTextRotation());
+ const GeoStat& rGeoStat(GetCustomShapeObj().GetGeoStat());
+
+ if(rGeoStat.nShearAngle || rGeoStat.nRotationAngle || !basegfx::fTools::equalZero(fExtraTextRotation))
+ {
+ if(aObjectRange != aTextRange)
+ {
+ // move relative to unrotated object range
+ aTextBoxMatrix.translate(
+ aTextRange.getMinX() - aObjectRange.getMinimum().getX(),
+ aTextRange.getMinY() - aObjectRange.getMinimum().getY());
+ }
+
+ if(!basegfx::fTools::equalZero(fExtraTextRotation))
+ {
+ basegfx::B2DVector aTranslation(
+ ( aTextRange.getWidth() / 2 ) + ( aTextRange.getMinX() - aObjectRange.getMinimum().getX() ),
+ ( aTextRange.getHeight() / 2 ) + ( aTextRange.getMinY() - aObjectRange.getMinimum().getY() ) );
+ aTextBoxMatrix.translate( -aTranslation.getX(), -aTranslation.getY() );
+ aTextBoxMatrix.rotate(basegfx::deg2rad(360.0 - fExtraTextRotation));
+ aTextBoxMatrix.translate( aTranslation.getX(), aTranslation.getY() );
+ }
+
+ if(rGeoStat.nShearAngle)
+ {
+ aTextBoxMatrix.shearX(-rGeoStat.mfTanShearAngle);
+ }
+
+ if(rGeoStat.nRotationAngle)
+ {
+ aTextBoxMatrix.rotate(toRadians(36000_deg100 - rGeoStat.nRotationAngle));
+ }
+
+ // give text it's target position
+ aTextBoxMatrix.translate(aObjectRange.getMinimum().getX(), aObjectRange.getMinimum().getY());
+ }
+ else
+ {
+ aTextBoxMatrix.translate(aTextRange.getMinX(), aTextRange.getMinY());
+ }
+
+ // check if SdrTextWordWrapItem is set
+ bWordWrap = GetCustomShapeObj().GetMergedItem(SDRATTR_TEXT_WORDWRAP).GetValue();
+ }
+
+ // fill object matrix
+ const basegfx::B2DHomMatrix aObjectMatrix(basegfx::utils::createScaleShearXRotateTranslateB2DHomMatrix(
+ aObjectRange.getWidth(), aObjectRange.getHeight(),
+ /*fShearX=*/0, /*fRotate=*/0,
+ aObjectRange.getMinX(), aObjectRange.getMinY()));
+
+ // create primitive
+ const drawinglayer::primitive2d::Primitive2DReference xReference(
+ new drawinglayer::primitive2d::SdrCustomShapePrimitive2D(
+ aAttribute,
+ std::move(xGroup),
+ aTextBoxMatrix,
+ bWordWrap,
+ b3DShape,
+ aObjectMatrix));
+ rVisitor.visit(xReference);
+ }
+ }
+
+} // end of namespace
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/sdr/contact/viewcontactofsdrole2obj.cxx b/svx/source/sdr/contact/viewcontactofsdrole2obj.cxx
new file mode 100644
index 000000000..c1a732786
--- /dev/null
+++ b/svx/source/sdr/contact/viewcontactofsdrole2obj.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 <drawinglayer/primitive2d/Tools.hxx>
+#include <sdr/contact/viewcontactofsdrole2obj.hxx>
+#include <svx/svdoole2.hxx>
+#include <sdr/contact/viewobjectcontactofsdrole2obj.hxx>
+#include <basegfx/matrix/b2dhommatrix.hxx>
+#include <sdr/primitive2d/sdrole2primitive2d.hxx>
+#include <sdr/primitive2d/sdrattributecreator.hxx>
+#include <vcl/canvastools.hxx>
+#include <tools/debug.hxx>
+#include <sdr/primitive2d/sdrolecontentprimitive2d.hxx>
+#include <basegfx/matrix/b2dhommatrixtools.hxx>
+#include <drawinglayer/primitive2d/transformprimitive2d.hxx>
+#include <svx/charthelper.hxx>
+#include <svtools/embedhlp.hxx>
+
+namespace sdr::contact {
+
+// Create an Object-Specific ViewObjectContact, set ViewContact and
+// ObjectContact. Always needs to return something.
+ViewObjectContact& ViewContactOfSdrOle2Obj::CreateObjectSpecificViewObjectContact(ObjectContact& rObjectContact)
+{
+ ViewObjectContact* pRetval = new ViewObjectContactOfSdrOle2Obj(rObjectContact, *this);
+ DBG_ASSERT(pRetval, "ViewContact::CreateObjectSpecificViewObjectContact() failed (!)");
+
+ return *pRetval;
+}
+
+ViewContactOfSdrOle2Obj::ViewContactOfSdrOle2Obj(SdrOle2Obj& rOle2Obj)
+: ViewContactOfSdrRectObj(rOle2Obj)
+{
+}
+
+ViewContactOfSdrOle2Obj::~ViewContactOfSdrOle2Obj()
+{
+}
+
+basegfx::B2DHomMatrix ViewContactOfSdrOle2Obj::createObjectTransform() const
+{
+ // take unrotated snap rect (direct model data) for position and size
+ const tools::Rectangle aRectangle(GetOle2Obj().GetGeoRect());
+ const basegfx::B2DRange aObjectRange = vcl::unotools::b2DRectangleFromRectangle(aRectangle);
+
+ // create object matrix
+ const GeoStat& rGeoStat(GetOle2Obj().GetGeoStat());
+ const double fShearX(-rGeoStat.mfTanShearAngle);
+ const double fRotate(rGeoStat.nRotationAngle ? toRadians(36000_deg100 - rGeoStat.nRotationAngle) : 0.0);
+
+ return basegfx::utils::createScaleShearXRotateTranslateB2DHomMatrix(
+ aObjectRange.getWidth(), aObjectRange.getHeight(),
+ fShearX,
+ fRotate,
+ aObjectRange.getMinX(), aObjectRange.getMinY());
+}
+
+void ViewContactOfSdrOle2Obj::createPrimitive2DSequenceWithParameters(drawinglayer::primitive2d::Primitive2DDecompositionVisitor& rVisitor) const
+{
+ // get object transformation
+ const basegfx::B2DHomMatrix aObjectMatrix(createObjectTransform());
+
+ // Prepare attribute settings, will be used soon anyways
+ const SfxItemSet& rItemSet = GetOle2Obj().GetMergedItemSet();
+
+ // this may be refined more granular; if no content, attributes may get simpler
+ const drawinglayer::attribute::SdrLineFillEffectsTextAttribute aAttribute(
+ drawinglayer::primitive2d::createNewSdrLineFillEffectsTextAttribute(
+ rItemSet,
+ GetOle2Obj().getText(0),
+ true));
+ drawinglayer::primitive2d::Primitive2DReference xContent;
+
+ if(GetOle2Obj().IsChart())
+ {
+ // try to get chart primitives and chart range directly from xChartModel
+ basegfx::B2DRange aChartContentRange;
+ drawinglayer::primitive2d::Primitive2DContainer aChartSequence(
+ ChartHelper::tryToGetChartContentAsPrimitive2DSequence(
+ GetOle2Obj().getXModel(),
+ aChartContentRange));
+ const double fWidth(aChartContentRange.getWidth());
+ const double fHeight(aChartContentRange.getHeight());
+
+ if(!aChartSequence.empty()
+ && basegfx::fTools::more(fWidth, 0.0)
+ && basegfx::fTools::more(fHeight, 0.0))
+ {
+ // create embedding transformation
+ basegfx::B2DHomMatrix aEmbed(
+ basegfx::utils::createTranslateB2DHomMatrix(
+ -aChartContentRange.getMinX(),
+ -aChartContentRange.getMinY()));
+
+ aEmbed.scale(1.0 / fWidth, 1.0 / fHeight);
+ aEmbed = aObjectMatrix * aEmbed;
+ xContent = new drawinglayer::primitive2d::TransformPrimitive2D(
+ aEmbed,
+ std::move(aChartSequence));
+ }
+ }
+
+ if(!xContent.is())
+ {
+ // #i102063# embed OLE content in an own primitive; this will be able to decompose accessing
+ // the weak SdrOle2 reference and will also implement getB2DRange() for fast BoundRect
+ // calculations without OLE Graphic access (which may trigger e.g. chart recalculation).
+ // It will also take care of HighContrast and ScaleContent
+ xContent = new drawinglayer::primitive2d::SdrOleContentPrimitive2D(
+ GetOle2Obj(),
+ aObjectMatrix,
+
+ // #i104867# add GraphicVersion number to be able to check for
+ // content change in the primitive later
+ GetOle2Obj().getEmbeddedObjectRef().getGraphicVersion() );
+ }
+
+ // create primitive. Use Ole2 primitive here. Prepare attribute settings, will
+ // be used soon anyways. Always create primitives to allow the decomposition of
+ // SdrOle2Primitive2D to create needed invisible elements for HitTest and/or BoundRect
+ const drawinglayer::primitive2d::Primitive2DReference xReference(
+ new drawinglayer::primitive2d::SdrOle2Primitive2D(
+ drawinglayer::primitive2d::Primitive2DContainer { xContent },
+ aObjectMatrix,
+ aAttribute));
+
+ rVisitor.visit(xReference);
+}
+
+basegfx::B2DRange ViewContactOfSdrOle2Obj::getRange( const drawinglayer::geometry::ViewInformation2D& rViewInfo2D ) const
+{
+ // this may be refined more granular; if no content, attributes may get simpler
+ const drawinglayer::attribute::SdrLineFillEffectsTextAttribute aAttribute =
+ drawinglayer::primitive2d::createNewSdrLineFillEffectsTextAttribute(
+ GetOle2Obj().GetMergedItemSet(),
+ GetOle2Obj().getText(0),
+ true);
+
+ basegfx::B2DHomMatrix aObjectMatrix = createObjectTransform();
+
+ drawinglayer::primitive2d::Primitive2DReference xContent =
+ new drawinglayer::primitive2d::SdrOleContentPrimitive2D(
+ GetOle2Obj(),
+ aObjectMatrix,
+ GetOle2Obj().getEmbeddedObjectRef().getGraphicVersion());
+
+ const drawinglayer::primitive2d::Primitive2DReference xReference(
+ new drawinglayer::primitive2d::SdrOle2Primitive2D(
+ drawinglayer::primitive2d::Primitive2DContainer { xContent },
+ aObjectMatrix,
+ aAttribute));
+
+ return drawinglayer::primitive2d::getB2DRangeFromPrimitive2DReference(xReference, rViewInfo2D);
+}
+
+void ViewContactOfSdrOle2Obj::createViewIndependentPrimitive2DSequence(drawinglayer::primitive2d::Primitive2DDecompositionVisitor& rVisitor) const
+{
+ createPrimitive2DSequenceWithParameters(rVisitor);
+}
+
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/sdr/contact/viewcontactofsdrpage.cxx b/svx/source/sdr/contact/viewcontactofsdrpage.cxx
new file mode 100644
index 000000000..44cccb319
--- /dev/null
+++ b/svx/source/sdr/contact/viewcontactofsdrpage.cxx
@@ -0,0 +1,598 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <sdr/contact/viewcontactofsdrpage.hxx>
+#include <svx/sdr/contact/viewobjectcontact.hxx>
+#include <svx/svdpage.hxx>
+#include <sdr/contact/viewobjectcontactofsdrpage.hxx>
+#include <basegfx/polygon/b2dpolygontools.hxx>
+#include <basegfx/matrix/b2dhommatrix.hxx>
+#include <svtools/colorcfg.hxx>
+#include <tools/debug.hxx>
+#include <vcl/svapp.hxx>
+#include <svx/sdr/contact/objectcontact.hxx>
+#include <drawinglayer/primitive2d/backgroundcolorprimitive2d.hxx>
+#include <drawinglayer/primitive2d/PolygonHairlinePrimitive2D.hxx>
+#include <drawinglayer/primitive2d/PolyPolygonColorPrimitive2D.hxx>
+#include <drawinglayer/attribute/fillgradientattribute.hxx>
+#include <basegfx/polygon/b2dpolygon.hxx>
+#include <sdr/primitive2d/sdrattributecreator.hxx>
+#include <sdr/primitive2d/sdrdecompositiontools.hxx>
+#include <vcl/lazydelete.hxx>
+#include <vcl/settings.hxx>
+#include <drawinglayer/primitive2d/discreteshadowprimitive2d.hxx>
+#include <drawinglayer/attribute/sdrfillattribute.hxx>
+#include <bitmaps.hlst>
+
+namespace sdr::contact {
+
+ViewContactOfPageSubObject::ViewContactOfPageSubObject(ViewContactOfSdrPage& rParentViewContactOfSdrPage)
+: mrParentViewContactOfSdrPage(rParentViewContactOfSdrPage)
+{
+}
+
+ViewContactOfPageSubObject::~ViewContactOfPageSubObject()
+{
+}
+
+ViewContact* ViewContactOfPageSubObject::GetParentContact() const
+{
+ return &mrParentViewContactOfSdrPage;
+}
+
+const SdrPage& ViewContactOfPageSubObject::getPage() const
+{
+ return mrParentViewContactOfSdrPage.GetSdrPage();
+}
+
+ViewObjectContact& ViewContactOfPageBackground::CreateObjectSpecificViewObjectContact(ObjectContact& rObjectContact)
+{
+ ViewObjectContact* pRetval = new ViewObjectContactOfPageBackground(rObjectContact, *this);
+ DBG_ASSERT(pRetval, "ViewContact::CreateObjectSpecificViewObjectContact() failed (!)");
+
+ return *pRetval;
+}
+
+void ViewContactOfPageBackground::createViewIndependentPrimitive2DSequence(drawinglayer::primitive2d::Primitive2DDecompositionVisitor& rVisitor) const
+{
+ // We have only the page information, not the view information. Use the
+ // svtools::DOCCOLOR color for initialisation
+ const svtools::ColorConfig aColorConfig;
+ const Color aInitColor(aColorConfig.GetColorValue(svtools::DOCCOLOR).nColor);
+ const basegfx::BColor aRGBColor(aInitColor.getBColor());
+ const drawinglayer::primitive2d::Primitive2DReference xReference(
+ new drawinglayer::primitive2d::BackgroundColorPrimitive2D(aRGBColor));
+
+ rVisitor.visit(xReference);
+}
+
+ViewContactOfPageBackground::ViewContactOfPageBackground(ViewContactOfSdrPage& rParentViewContactOfSdrPage)
+: ViewContactOfPageSubObject(rParentViewContactOfSdrPage)
+{
+}
+
+ViewContactOfPageBackground::~ViewContactOfPageBackground()
+{
+}
+
+ViewObjectContact& ViewContactOfPageShadow::CreateObjectSpecificViewObjectContact(ObjectContact& rObjectContact)
+{
+ ViewObjectContact* pRetval = new ViewObjectContactOfPageShadow(rObjectContact, *this);
+ DBG_ASSERT(pRetval, "ViewContact::CreateObjectSpecificViewObjectContact() failed (!)");
+
+ return *pRetval;
+}
+
+void ViewContactOfPageShadow::createViewIndependentPrimitive2DSequence(drawinglayer::primitive2d::Primitive2DDecompositionVisitor& rVisitor) const
+{
+ static bool bUseOldPageShadow(false); // loplugin:constvars:ignore
+ const SdrPage& rPage = getPage();
+ basegfx::B2DHomMatrix aPageMatrix;
+ aPageMatrix.set(0, 0, static_cast<double>(rPage.GetWidth()));
+ aPageMatrix.set(1, 1, static_cast<double>(rPage.GetHeight()));
+
+ if(bUseOldPageShadow)
+ {
+ // create page shadow polygon
+ const double fPageBorderFactor(1.0 / 256.0);
+ basegfx::B2DPolygon aPageShadowPolygon;
+ aPageShadowPolygon.append(basegfx::B2DPoint(1.0, fPageBorderFactor));
+ aPageShadowPolygon.append(basegfx::B2DPoint(1.0 + fPageBorderFactor, fPageBorderFactor));
+ aPageShadowPolygon.append(basegfx::B2DPoint(1.0 + fPageBorderFactor, 1.0 + fPageBorderFactor));
+ aPageShadowPolygon.append(basegfx::B2DPoint(fPageBorderFactor, 1.0 + fPageBorderFactor));
+ aPageShadowPolygon.append(basegfx::B2DPoint(fPageBorderFactor, 1.0));
+ aPageShadowPolygon.append(basegfx::B2DPoint(1.0, 1.0));
+ aPageShadowPolygon.setClosed(true);
+ aPageShadowPolygon.transform(aPageMatrix);
+
+ // We have only the page information, not the view information. Use the
+ // svtools::FONTCOLOR color for initialisation
+ const svtools::ColorConfig aColorConfig;
+ const Color aShadowColor(aColorConfig.GetColorValue(svtools::FONTCOLOR).nColor);
+ const basegfx::BColor aRGBShadowColor(aShadowColor.getBColor());
+ const drawinglayer::primitive2d::Primitive2DReference xReference(
+ new drawinglayer::primitive2d::PolyPolygonColorPrimitive2D(
+ basegfx::B2DPolyPolygon(aPageShadowPolygon),
+ aRGBShadowColor));
+
+ rVisitor.visit(xReference);
+ }
+ else
+ {
+ static vcl::DeleteOnDeinit< drawinglayer::primitive2d::DiscreteShadow > aDiscreteShadow((
+ BitmapEx(SIP_SA_PAGESHADOW35X35)));
+
+ if(aDiscreteShadow.get())
+ {
+ const drawinglayer::primitive2d::Primitive2DReference xReference(
+ new drawinglayer::primitive2d::DiscreteShadowPrimitive2D(
+ aPageMatrix,
+ *aDiscreteShadow.get()));
+
+ rVisitor.visit(xReference);
+ }
+ }
+}
+
+ViewContactOfPageShadow::ViewContactOfPageShadow(ViewContactOfSdrPage& rParentViewContactOfSdrPage)
+: ViewContactOfPageSubObject(rParentViewContactOfSdrPage)
+{
+}
+
+ViewContactOfPageShadow::~ViewContactOfPageShadow()
+{
+}
+
+ViewObjectContact& ViewContactOfMasterPage::CreateObjectSpecificViewObjectContact(ObjectContact& rObjectContact)
+{
+ ViewObjectContact* pRetval = new ViewObjectContactOfMasterPage(rObjectContact, *this);
+ DBG_ASSERT(pRetval, "ViewContact::CreateObjectSpecificViewObjectContact() failed (!)");
+
+ return *pRetval;
+}
+
+void ViewContactOfMasterPage::createViewIndependentPrimitive2DSequence(drawinglayer::primitive2d::Primitive2DDecompositionVisitor& rVisitor) const
+{
+ // this class is used when the page is a MasterPage and is responsible to
+ // create a visualisation for the MPBGO, if exists. This needs to be suppressed
+ // when a SdrPage which uses a MasterPage creates it's output. Suppression
+ // is done in the corresponding VOC since DisplayInfo data is needed
+ const SdrPage& rPage = getPage();
+
+ if(rPage.IsMasterPage())
+ {
+ if(0 == rPage.GetPageNum())
+ {
+ // #i98063#
+ // filter MasterPage 0 since it's the HandoutPage. Thus, it's a
+ // MasterPage, but has no MPBGO, so there is nothing to do here.
+ }
+ else
+ {
+ drawinglayer::attribute::SdrFillAttribute aFill;
+
+ // #i110846# Suppress SdrPage FillStyle for MasterPages without StyleSheets,
+ // else the PoolDefault (XFILL_COLOR and Blue8) will be used. Normally, all
+ // MasterPages should have a StyleSheet exactly for this reason, but historically
+ // e.g. the Notes MasterPage has no StyleSheet set (and there maybe others).
+ if(rPage.getSdrPageProperties().GetStyleSheet())
+ {
+ // create page fill attributes with correct properties
+ aFill = drawinglayer::primitive2d::createNewSdrFillAttribute(
+ rPage.getSdrPageProperties().GetItemSet());
+ }
+
+ if(!aFill.isDefault())
+ {
+ // direct model data is the page size, get and use it
+ const basegfx::B2DRange aOuterRange(
+ 0, 0, rPage.GetWidth(), rPage.GetHeight());
+ const basegfx::B2DRange aInnerRange(
+ rPage.GetLeftBorder(), rPage.GetUpperBorder(),
+ rPage.GetWidth() - rPage.GetRightBorder(), rPage.GetHeight() - rPage.GetLowerBorder());
+ bool const isFullSize(rPage.IsBackgroundFullSize());
+ const basegfx::B2DPolygon aFillPolygon(
+ basegfx::utils::createPolygonFromRect(isFullSize ? aOuterRange : aInnerRange));
+ const drawinglayer::primitive2d::Primitive2DReference xReference(
+ drawinglayer::primitive2d::createPolyPolygonFillPrimitive(
+ basegfx::B2DPolyPolygon(aFillPolygon),
+ aFill,
+ drawinglayer::attribute::FillGradientAttribute()));
+
+ rVisitor.visit(xReference);
+ }
+ }
+ }
+}
+
+ViewContactOfMasterPage::ViewContactOfMasterPage(ViewContactOfSdrPage& rParentViewContactOfSdrPage)
+: ViewContactOfPageSubObject(rParentViewContactOfSdrPage)
+{
+}
+
+ViewContactOfMasterPage::~ViewContactOfMasterPage()
+{
+}
+
+ViewObjectContact& ViewContactOfPageFill::CreateObjectSpecificViewObjectContact(ObjectContact& rObjectContact)
+{
+ ViewObjectContact* pRetval = new ViewObjectContactOfPageFill(rObjectContact, *this);
+ DBG_ASSERT(pRetval, "ViewContact::CreateObjectSpecificViewObjectContact() failed (!)");
+
+ return *pRetval;
+}
+
+void ViewContactOfPageFill::createViewIndependentPrimitive2DSequence(drawinglayer::primitive2d::Primitive2DDecompositionVisitor& rVisitor) const
+{
+ const SdrPage& rPage = getPage();
+ const basegfx::B2DRange aPageFillRange(0.0, 0.0, static_cast<double>(rPage.GetWidth()), static_cast<double>(rPage.GetHeight()));
+ const basegfx::B2DPolygon aPageFillPolygon(basegfx::utils::createPolygonFromRect(aPageFillRange));
+
+ // We have only the page information, not the view information. Use the
+ // svtools::DOCCOLOR color for initialisation
+ const svtools::ColorConfig aColorConfig;
+ const Color aPageFillColor(aColorConfig.GetColorValue(svtools::DOCCOLOR).nColor);
+
+ // create and add primitive
+ const basegfx::BColor aRGBColor(aPageFillColor.getBColor());
+ rVisitor.visit(drawinglayer::primitive2d::Primitive2DReference(new drawinglayer::primitive2d::PolyPolygonColorPrimitive2D(basegfx::B2DPolyPolygon(aPageFillPolygon), aRGBColor)));
+}
+
+ViewContactOfPageFill::ViewContactOfPageFill(ViewContactOfSdrPage& rParentViewContactOfSdrPage)
+: ViewContactOfPageSubObject(rParentViewContactOfSdrPage)
+{
+}
+
+ViewContactOfPageFill::~ViewContactOfPageFill()
+{
+}
+
+ViewObjectContact& ViewContactOfOuterPageBorder::CreateObjectSpecificViewObjectContact(ObjectContact& rObjectContact)
+{
+ ViewObjectContact* pRetval = new ViewObjectContactOfOuterPageBorder(rObjectContact, *this);
+ DBG_ASSERT(pRetval, "ViewContact::CreateObjectSpecificViewObjectContact() failed (!)");
+
+ return *pRetval;
+}
+
+void ViewContactOfOuterPageBorder::createViewIndependentPrimitive2DSequence(drawinglayer::primitive2d::Primitive2DDecompositionVisitor& rVisitor) const
+{
+ const SdrPage& rPage = getPage();
+ const basegfx::B2DRange aPageBorderRange(0.0, 0.0, static_cast<double>(rPage.GetWidth()), static_cast<double>(rPage.GetHeight()));
+
+ // Changed to 0x949599 for renaissance, before svtools::FONTCOLOR was used.
+ // Added old case as fallback for HighContrast.
+ basegfx::BColor aRGBBorderColor(0x94 / double(0xff), 0x95 / double(0xff), 0x99 / double(0xff));
+
+ if(Application::GetSettings().GetStyleSettings().GetHighContrastMode())
+ {
+ const svtools::ColorConfig aColorConfig;
+ const Color aBorderColor(aColorConfig.GetColorValue(svtools::FONTCOLOR).nColor);
+
+ aRGBBorderColor = aBorderColor.getBColor();
+ }
+
+ if(rPage.getPageBorderOnlyLeftRight())
+ {
+ // #i93597# for Report Designer, the page border shall be only displayed right and left,
+ // but not top and bottom. Create simplified geometry.
+ basegfx::B2DPolygon aLeft, aRight;
+
+ aLeft.append(basegfx::B2DPoint(aPageBorderRange.getMinX(), aPageBorderRange.getMinY()));
+ aLeft.append(basegfx::B2DPoint(aPageBorderRange.getMinX(), aPageBorderRange.getMaxY()));
+
+ aRight.append(basegfx::B2DPoint(aPageBorderRange.getMaxX(), aPageBorderRange.getMinY()));
+ aRight.append(basegfx::B2DPoint(aPageBorderRange.getMaxX(), aPageBorderRange.getMaxY()));
+
+ rVisitor.visit(drawinglayer::primitive2d::Primitive2DReference(new drawinglayer::primitive2d::PolygonHairlinePrimitive2D(aLeft, aRGBBorderColor)));
+ rVisitor.visit(drawinglayer::primitive2d::Primitive2DReference(new drawinglayer::primitive2d::PolygonHairlinePrimitive2D(aRight, aRGBBorderColor)));
+ }
+ else
+ {
+ const basegfx::B2DPolygon aPageBorderPolygon(basegfx::utils::createPolygonFromRect(aPageBorderRange));
+ rVisitor.visit(drawinglayer::primitive2d::Primitive2DReference(new drawinglayer::primitive2d::PolygonHairlinePrimitive2D(aPageBorderPolygon, aRGBBorderColor)));
+ }
+}
+
+ViewContactOfOuterPageBorder::ViewContactOfOuterPageBorder(ViewContactOfSdrPage& rParentViewContactOfSdrPage)
+: ViewContactOfPageSubObject(rParentViewContactOfSdrPage)
+{
+}
+
+ViewContactOfOuterPageBorder::~ViewContactOfOuterPageBorder()
+{
+}
+
+ViewObjectContact& ViewContactOfInnerPageBorder::CreateObjectSpecificViewObjectContact(ObjectContact& rObjectContact)
+{
+ ViewObjectContact* pRetval = new ViewObjectContactOfInnerPageBorder(rObjectContact, *this);
+ DBG_ASSERT(pRetval, "ViewContact::CreateObjectSpecificViewObjectContact() failed (!)");
+
+ return *pRetval;
+}
+
+void ViewContactOfInnerPageBorder::createViewIndependentPrimitive2DSequence(drawinglayer::primitive2d::Primitive2DDecompositionVisitor& rVisitor) const
+{
+ const SdrPage& rPage = getPage();
+ const basegfx::B2DRange aPageBorderRange(
+ static_cast<double>(rPage.GetLeftBorder()), static_cast<double>(rPage.GetUpperBorder()),
+ static_cast<double>(rPage.GetWidth() - rPage.GetRightBorder()), static_cast<double>(rPage.GetHeight() - rPage.GetLowerBorder()));
+ const basegfx::B2DPolygon aPageBorderPolygon(basegfx::utils::createPolygonFromRect(aPageBorderRange));
+
+ // We have only the page information, not the view information. Use the
+ // svtools::FONTCOLOR or svtools::DOCBOUNDARIES color for initialisation
+ const svtools::ColorConfig aColorConfig;
+ Color aBorderColor;
+
+ if(Application::GetSettings().GetStyleSettings().GetHighContrastMode())
+ {
+ aBorderColor = aColorConfig.GetColorValue(svtools::FONTCOLOR).nColor;
+ }
+ else
+ {
+ svtools::ColorConfigValue aBorderConfig = aColorConfig.GetColorValue(svtools::DOCBOUNDARIES);
+ aBorderColor = aBorderConfig.bIsVisible ? aBorderConfig.nColor :
+ aColorConfig.GetColorValue(svtools::DOCCOLOR).nColor;
+ }
+
+ // create page outer border primitive
+ const basegfx::BColor aRGBBorderColor(aBorderColor.getBColor());
+ rVisitor.visit(drawinglayer::primitive2d::Primitive2DReference(new drawinglayer::primitive2d::PolygonHairlinePrimitive2D(aPageBorderPolygon, aRGBBorderColor)));
+}
+
+ViewContactOfInnerPageBorder::ViewContactOfInnerPageBorder(ViewContactOfSdrPage& rParentViewContactOfSdrPage)
+: ViewContactOfPageSubObject(rParentViewContactOfSdrPage)
+{
+}
+
+ViewContactOfInnerPageBorder::~ViewContactOfInnerPageBorder()
+{
+}
+
+ViewObjectContact& ViewContactOfPageHierarchy::CreateObjectSpecificViewObjectContact(ObjectContact& rObjectContact)
+{
+ ViewObjectContact* pRetval = new ViewObjectContactOfPageHierarchy(rObjectContact, *this);
+ DBG_ASSERT(pRetval, "ViewContact::CreateObjectSpecificViewObjectContact() failed (!)");
+
+ return *pRetval;
+}
+
+void ViewContactOfPageHierarchy::createViewIndependentPrimitive2DSequence(drawinglayer::primitive2d::Primitive2DDecompositionVisitor& rVisitor) const
+{
+ // collect sub-hierarchy
+ const sal_uInt32 nObjectCount(GetObjectCount());
+
+ // collect all sub-primitives
+ for(sal_uInt32 a(0); a < nObjectCount; a++)
+ {
+ const ViewContact& rCandidate(GetViewContact(a));
+ rCandidate.getViewIndependentPrimitive2DContainer(rVisitor);
+ }
+}
+
+ViewContactOfPageHierarchy::ViewContactOfPageHierarchy(ViewContactOfSdrPage& rParentViewContactOfSdrPage)
+: ViewContactOfPageSubObject(rParentViewContactOfSdrPage)
+{
+}
+
+ViewContactOfPageHierarchy::~ViewContactOfPageHierarchy()
+{
+}
+
+sal_uInt32 ViewContactOfPageHierarchy::GetObjectCount() const
+{
+ return getPage().GetObjCount();
+}
+
+ViewContact& ViewContactOfPageHierarchy::GetViewContact(sal_uInt32 nIndex) const
+{
+ SdrObject* pObj = getPage().GetObj(nIndex);
+ assert(pObj && "ViewContactOfPageHierarchy::GetViewContact: Corrupt SdrObjList (!)");
+ return pObj->GetViewContact();
+}
+
+ViewObjectContact& ViewContactOfGrid::CreateObjectSpecificViewObjectContact(ObjectContact& rObjectContact)
+{
+ ViewObjectContact* pRetval = new ViewObjectContactOfPageGrid(rObjectContact, *this);
+ DBG_ASSERT(pRetval, "ViewContact::CreateObjectSpecificViewObjectContact() failed (!)");
+
+ return *pRetval;
+}
+
+void ViewContactOfGrid::createViewIndependentPrimitive2DSequence(drawinglayer::primitive2d::Primitive2DDecompositionVisitor&) const
+{
+ // We have only the page information, not the view information and thus no grid settings. Create empty
+ // default. For the view-dependent implementation, see ViewObjectContactOfPageGrid::createPrimitive2DSequence
+}
+
+ViewContactOfGrid::ViewContactOfGrid(ViewContactOfSdrPage& rParentViewContactOfSdrPage, bool bFront)
+: ViewContactOfPageSubObject(rParentViewContactOfSdrPage),
+ mbFront(bFront)
+{
+}
+
+ViewContactOfGrid::~ViewContactOfGrid()
+{
+}
+
+ViewObjectContact& ViewContactOfHelplines::CreateObjectSpecificViewObjectContact(ObjectContact& rObjectContact)
+{
+ ViewObjectContact* pRetval = new ViewObjectContactOfPageHelplines(rObjectContact, *this);
+ DBG_ASSERT(pRetval, "ViewContact::CreateObjectSpecificViewObjectContact() failed (!)");
+
+ return *pRetval;
+}
+
+void ViewContactOfHelplines::createViewIndependentPrimitive2DSequence(drawinglayer::primitive2d::Primitive2DDecompositionVisitor&) const
+{
+ // We have only the page information, not the view information and thus no helplines. Create empty
+ // default. For the view-dependent implementation, see ViewObjectContactOfPageHelplines::createPrimitive2DSequence
+}
+
+ViewContactOfHelplines::ViewContactOfHelplines(ViewContactOfSdrPage& rParentViewContactOfSdrPage, bool bFront)
+: ViewContactOfPageSubObject(rParentViewContactOfSdrPage),
+ mbFront(bFront)
+{
+}
+
+ViewContactOfHelplines::~ViewContactOfHelplines()
+{
+}
+
+// Create an Object-Specific ViewObjectContact, set ViewContact and
+// ObjectContact. Always needs to return something.
+ViewObjectContact& ViewContactOfSdrPage::CreateObjectSpecificViewObjectContact(ObjectContact& rObjectContact)
+{
+ ViewObjectContact* pRetval = new ViewObjectContactOfSdrPage(rObjectContact, *this);
+ DBG_ASSERT(pRetval, "ViewContact::CreateObjectSpecificViewObjectContact() failed (!)");
+
+ return *pRetval;
+}
+
+ViewContactOfSdrPage::ViewContactOfSdrPage(SdrPage& rPage)
+: mrPage(rPage),
+ maViewContactOfPageBackground(*this),
+ maViewContactOfPageShadow(*this),
+ maViewContactOfPageFill(*this),
+ maViewContactOfMasterPage(*this),
+ maViewContactOfOuterPageBorder(*this),
+ maViewContactOfInnerPageBorder(*this),
+ maViewContactOfGridBack(*this, false),
+ maViewContactOfHelplinesBack(*this, false),
+ maViewContactOfPageHierarchy(*this),
+ maViewContactOfGridFront(*this, true),
+ maViewContactOfHelplinesFront(*this, true)
+{
+}
+
+ViewContactOfSdrPage::~ViewContactOfSdrPage()
+{
+}
+
+// Access to possible sub-hierarchy
+sal_uInt32 ViewContactOfSdrPage::GetObjectCount() const
+{
+ // Fixed count of content. It contains PageBackground (Wiese), PageShadow, PageFill,
+ // then - depending on if the page has a MasterPage - either MasterPage Hierarchy
+ // or MPBGO. Also OuterPageBorder, InnerPageBorder and two pairs of Grid and Helplines
+ // (for front and back) which internally are visible or not depending on the current
+ // front/back setting for those.
+ return 11;
+}
+
+ViewContact& ViewContactOfSdrPage::GetViewContact(sal_uInt32 nIndex) const
+{
+ switch(nIndex)
+ {
+ case 0: return const_cast<ViewContactOfPageBackground&>(maViewContactOfPageBackground);
+ case 1: return const_cast<ViewContactOfPageShadow&>(maViewContactOfPageShadow);
+ case 2: return const_cast<ViewContactOfPageFill&>(maViewContactOfPageFill);
+ case 3:
+ {
+ const SdrPage& rPage = GetSdrPage();
+
+ if(rPage.TRG_HasMasterPage())
+ {
+ return rPage.TRG_GetMasterPageDescriptorViewContact();
+ }
+ else
+ {
+ return const_cast<ViewContactOfMasterPage&>(maViewContactOfMasterPage);
+ }
+ }
+ case 4: return const_cast<ViewContactOfOuterPageBorder&>(maViewContactOfOuterPageBorder);
+ case 5: return const_cast<ViewContactOfInnerPageBorder&>(maViewContactOfInnerPageBorder);
+ case 6: return const_cast<ViewContactOfGrid&>(maViewContactOfGridBack);
+ case 7: return const_cast<ViewContactOfHelplines&>(maViewContactOfHelplinesBack);
+ case 8: return const_cast<ViewContactOfPageHierarchy&>(maViewContactOfPageHierarchy);
+ case 9: return const_cast<ViewContactOfGrid&>(maViewContactOfGridFront);
+ case 10: case 11: return const_cast<ViewContactOfHelplines&>(maViewContactOfHelplinesFront);
+ default: assert(false);return const_cast<ViewContactOfHelplines&>(maViewContactOfHelplinesFront);
+ }
+}
+
+// React on changes of the object of this ViewContact
+void ViewContactOfSdrPage::ActionChanged()
+{
+ // call parent
+ ViewContact::ActionChanged();
+
+ // apply to local viewContacts, they all rely on page information. Exception
+ // is the sub hierarchy; this will not be influenced by the change
+ maViewContactOfPageBackground.ActionChanged();
+ maViewContactOfPageShadow.ActionChanged();
+ maViewContactOfPageFill.ActionChanged();
+
+ const SdrPage& rPage = GetSdrPage();
+
+ if(rPage.TRG_HasMasterPage())
+ {
+ rPage.TRG_GetMasterPageDescriptorViewContact().ActionChanged();
+ }
+ else if(rPage.IsMasterPage())
+ {
+ maViewContactOfMasterPage.ActionChanged();
+ }
+
+ maViewContactOfOuterPageBorder.ActionChanged();
+ maViewContactOfInnerPageBorder.ActionChanged();
+ maViewContactOfGridBack.ActionChanged();
+ maViewContactOfHelplinesBack.ActionChanged();
+ maViewContactOfGridFront.ActionChanged();
+ maViewContactOfHelplinesFront.ActionChanged();
+}
+
+void ViewContactOfSdrPage::createViewIndependentPrimitive2DSequence(drawinglayer::primitive2d::Primitive2DDecompositionVisitor& rVisitor) const
+{
+ // collect all sub-sequences including sub hierarchy.
+ maViewContactOfPageBackground.getViewIndependentPrimitive2DContainer(rVisitor);
+ maViewContactOfPageShadow.getViewIndependentPrimitive2DContainer(rVisitor);
+ maViewContactOfPageFill.getViewIndependentPrimitive2DContainer(rVisitor);
+
+ const SdrPage& rPage = GetSdrPage();
+
+ if(rPage.TRG_HasMasterPage())
+ {
+ rPage.TRG_GetMasterPageDescriptorViewContact().getViewIndependentPrimitive2DContainer(rVisitor);
+ }
+ else if(rPage.IsMasterPage())
+ {
+ maViewContactOfMasterPage.getViewIndependentPrimitive2DContainer(rVisitor);
+ }
+
+ maViewContactOfOuterPageBorder.getViewIndependentPrimitive2DContainer(rVisitor);
+ maViewContactOfInnerPageBorder.getViewIndependentPrimitive2DContainer(rVisitor);
+ maViewContactOfPageHierarchy.getViewIndependentPrimitive2DContainer(rVisitor);
+
+ // Only add front versions of grid and helplines since no visibility test is done,
+ // so adding the back incarnations is not necessary. This makes the Front
+ // visualisation the default when no visibility tests are done.
+
+ // Since we have no view here, no grid and helpline definitions are available currently. The used
+ // methods at ViewContactOfHelplines and ViewContactOfGrid return only empty sequences and
+ // do not need to be called ATM. This may change later if grid or helpline info gets
+ // model data (it should not). Keeping the lines commented to hold this hint.
+
+ // drawinglayer::primitive2d::appendPrimitive2DSequenceToPrimitive2DSequence(xRetval, maViewContactOfGridFront.getViewIndependentPrimitive2DContainer());
+ // drawinglayer::primitive2d::appendPrimitive2DSequenceToPrimitive2DSequence(xRetval, maViewContactOfHelplinesFront.getViewIndependentPrimitive2DContainer());
+}
+
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/sdr/contact/viewcontactofsdrpathobj.cxx b/svx/source/sdr/contact/viewcontactofsdrpathobj.cxx
new file mode 100644
index 000000000..8798d40fb
--- /dev/null
+++ b/svx/source/sdr/contact/viewcontactofsdrpathobj.cxx
@@ -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 .
+ */
+
+
+#include <sdr/contact/viewcontactofsdrpathobj.hxx>
+#include <svtools/optionsdrawinglayer.hxx>
+#include <svx/svdopath.hxx>
+#include <svx/svdpage.hxx>
+#include <sdr/primitive2d/sdrattributecreator.hxx>
+#include <basegfx/polygon/b2dpolypolygontools.hxx>
+#include <basegfx/polygon/b2dpolygonclipper.hxx>
+#include <sdr/primitive2d/sdrpathprimitive2d.hxx>
+#include <basegfx/matrix/b2dhommatrixtools.hxx>
+#include <basegfx/polygon/b2dpolygontools.hxx>
+#include <osl/diagnose.h>
+#include <unotools/configmgr.hxx>
+#include <vcl/canvastools.hxx>
+
+namespace sdr::contact
+{
+ ViewContactOfSdrPathObj::ViewContactOfSdrPathObj(SdrPathObj& rPathObj)
+ : ViewContactOfTextObj(rPathObj)
+ {
+ }
+
+ ViewContactOfSdrPathObj::~ViewContactOfSdrPathObj()
+ {
+ }
+
+ /// return true if polycount == 1
+ static bool ensureGeometry(basegfx::B2DPolyPolygon& rUnitPolyPolygon)
+ {
+ sal_uInt32 nPolyCount(rUnitPolyPolygon.count());
+ sal_uInt32 nPointCount(0);
+
+ for(auto const& rPolygon : std::as_const(rUnitPolyPolygon))
+ {
+ nPointCount += rPolygon.count();
+ if (nPointCount > 1)
+ return false;
+ }
+
+ if(!nPointCount)
+ {
+ OSL_FAIL("PolyPolygon object without geometry detected, this should not be created (!)");
+ basegfx::B2DPolygon aFallbackLine;
+ aFallbackLine.append(basegfx::B2DPoint(0.0, 0.0));
+ aFallbackLine.append(basegfx::B2DPoint(1000.0, 1000.0));
+ rUnitPolyPolygon = basegfx::B2DPolyPolygon(aFallbackLine);
+
+ nPolyCount = 1;
+ }
+
+ return nPolyCount == 1;
+ }
+
+ void ViewContactOfSdrPathObj::createViewIndependentPrimitive2DSequence(drawinglayer::primitive2d::Primitive2DDecompositionVisitor& rVisitor) const
+ {
+ const SfxItemSet& rItemSet = GetPathObj().GetMergedItemSet();
+ const drawinglayer::attribute::SdrLineFillEffectsTextAttribute aAttribute(
+ drawinglayer::primitive2d::createNewSdrLineFillEffectsTextAttribute(
+ rItemSet,
+ GetPathObj().getText(0),
+ false));
+ basegfx::B2DPolyPolygon aUnitPolyPolygon(GetPathObj().GetPathPoly());
+ bool bPolyCountIsOne(ensureGeometry(aUnitPolyPolygon));
+
+ // prepare object transformation and unit polygon (direct model data)
+ basegfx::B2DHomMatrix aObjectMatrix;
+ basegfx::B2DPolyPolygon aUnitDefinitionPolyPolygon;
+ bool bIsLine(
+ !aUnitPolyPolygon.areControlPointsUsed()
+ && bPolyCountIsOne
+ && 2 == aUnitPolyPolygon.getB2DPolygon(0).count());
+
+ if(bIsLine)
+ {
+ //tdf#63955 if we have an extremely long line then clip it to a
+ //very generous range of -1 page width/height vs +1 page
+ //width/height to avoid oom and massive churn generating a huge
+ //polygon chain to cover the length in applyLineDashing if this
+ //line is dashed
+ const SdrPage* pPage(GetPathObj().getSdrPageFromSdrObject());
+ sal_Int32 nPageWidth = pPage ? pPage->GetWidth() : 0;
+ sal_Int32 nPageHeight = pPage ? pPage->GetHeight() : 0;
+
+ //But, see tdf#101187, only do this if our generous clip region
+ //would not over flow into a tiny clip region
+ if (nPageWidth < SAL_MAX_INT32/2 && nPageHeight < SAL_MAX_INT32/2 && !utl::ConfigManager::IsFuzzing())
+ {
+ //But, see tdf#97276, tdf#126184 and tdf#98366. Don't clip too much if the
+ //underlying page dimension is unknown or a paste document
+ //where the page sizes use the odd default of 10x10
+ const sal_Int32 nMaxPaperWidth = SvtOptionsDrawinglayer::GetMaximumPaperWidth() * 1000;
+ const sal_Int32 nMaxPaperHeight = SvtOptionsDrawinglayer::GetMaximumPaperHeight() * 1000;
+ nPageWidth = std::max<sal_Int32>(nPageWidth, nMaxPaperWidth);
+ nPageHeight = std::max<sal_Int32>(nPageHeight, nMaxPaperHeight);
+ basegfx::B2DRange aClipRange(-nPageWidth, -nPageHeight,
+ nPageWidth*2, nPageHeight*2);
+
+ aUnitPolyPolygon = basegfx::utils::clipPolyPolygonOnRange(aUnitPolyPolygon,
+ aClipRange, true, true);
+ bPolyCountIsOne = ensureGeometry(aUnitPolyPolygon);
+
+ // re-check that we have't been clipped out to oblivion
+ bIsLine =
+ !aUnitPolyPolygon.areControlPointsUsed()
+ && bPolyCountIsOne
+ && 2 == aUnitPolyPolygon.getB2DPolygon(0).count();
+ }
+ }
+
+ if(bIsLine)
+ {
+
+ // special handling for single line mode (2 points)
+ const basegfx::B2DPolygon aSubPolygon(aUnitPolyPolygon.getB2DPolygon(0));
+ const basegfx::B2DPoint aStart(aSubPolygon.getB2DPoint(0));
+ const basegfx::B2DPoint aEnd(aSubPolygon.getB2DPoint(1));
+ const basegfx::B2DVector aLine(aEnd - aStart);
+
+ // #i102548# create new unit polygon for line (horizontal)
+ basegfx::B2DPolygon aNewPolygon;
+ aNewPolygon.append(basegfx::B2DPoint(0.0, 0.0));
+ aNewPolygon.append(basegfx::B2DPoint(1.0, 0.0));
+ aUnitPolyPolygon.setB2DPolygon(0, aNewPolygon);
+
+ // #i102548# fill objectMatrix with rotation and offset (no shear for lines)
+ aObjectMatrix = basegfx::utils::createScaleShearXRotateTranslateB2DHomMatrix(
+ aLine.getLength(), 1.0,
+ 0.0,
+ atan2(aLine.getY(), aLine.getX()),
+ aStart.getX(), aStart.getY());
+ }
+ else
+ {
+ // #i102548# create unscaled, unsheared, unrotated and untranslated polygon
+ // (unit polygon) by creating the object matrix and back-transforming the polygon
+ const basegfx::B2DRange aObjectRange(basegfx::utils::getRange(aUnitPolyPolygon));
+ const GeoStat& rGeoStat(GetPathObj().GetGeoStat());
+ const double fWidth(aObjectRange.getWidth());
+ const double fHeight(aObjectRange.getHeight());
+ const double fScaleX(basegfx::fTools::equalZero(fWidth) ? 1.0 : fWidth);
+ const double fScaleY(basegfx::fTools::equalZero(fHeight) ? 1.0 : fHeight);
+
+ aObjectMatrix = basegfx::utils::createScaleShearXRotateTranslateB2DHomMatrix(
+ fScaleX, fScaleY,
+ -rGeoStat.mfTanShearAngle,
+ rGeoStat.nRotationAngle ? toRadians(36000_deg100 - rGeoStat.nRotationAngle) : 0.0,
+ aObjectRange.getMinX(), aObjectRange.getMinY());
+
+ // create unit polygon from object's absolute path
+ basegfx::B2DHomMatrix aInverse(aObjectMatrix);
+ aInverse.invert();
+ aUnitPolyPolygon.transform(aInverse);
+
+ // OperationSmiley: Check if a FillGeometryDefiningShape is set
+ const SdrObject* pFillGeometryDefiningShape(GetPathObj().getFillGeometryDefiningShape());
+
+ if(nullptr != pFillGeometryDefiningShape)
+ {
+ // If yes, get it's BoundRange and use as defining Geometry for the FillStyle.
+ // If no, aUnitDefinitionPolyPolygon will just be empty and thus be interpreted
+ // as unused.
+ // Using SnapRect will make the FillDefinition to always be extended e.g.
+ // for rotated/sheared objects.
+ const tools::Rectangle& rSnapRect(pFillGeometryDefiningShape->GetSnapRect());
+
+ aUnitDefinitionPolyPolygon.append(
+ basegfx::utils::createPolygonFromRect(
+ vcl::unotools::b2DRectangleFromRectangle(rSnapRect)));
+
+ // use same coordinate system as the shape geometry -> this
+ // makes it relative to shape's unit geometry and thus freely
+ // transformable with the shape
+ aUnitDefinitionPolyPolygon.transform(aInverse);
+ }
+ }
+
+ // create primitive. Always create primitives to allow the decomposition of
+ // SdrPathPrimitive2D to create needed invisible elements for HitTest and/or BoundRect
+ const drawinglayer::primitive2d::Primitive2DReference xReference(
+ new drawinglayer::primitive2d::SdrPathPrimitive2D(
+ aObjectMatrix,
+ aAttribute,
+ aUnitPolyPolygon,
+ aUnitDefinitionPolyPolygon));
+
+ rVisitor.visit(xReference);
+ }
+
+} // end of namespace
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/sdr/contact/viewcontactofsdrrectobj.cxx b/svx/source/sdr/contact/viewcontactofsdrrectobj.cxx
new file mode 100644
index 000000000..6d9ed7bed
--- /dev/null
+++ b/svx/source/sdr/contact/viewcontactofsdrrectobj.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 <sdr/contact/viewcontactofsdrrectobj.hxx>
+#include <svx/svdorect.hxx>
+#include <sdr/primitive2d/sdrattributecreator.hxx>
+#include <sdr/primitive2d/sdrrectangleprimitive2d.hxx>
+#include <svl/itemset.hxx>
+#include <basegfx/matrix/b2dhommatrixtools.hxx>
+#include <svx/svdmodel.hxx>
+#include <vcl/canvastools.hxx>
+#include <svx/sdmetitm.hxx>
+
+namespace sdr::contact {
+
+ViewContactOfSdrRectObj::ViewContactOfSdrRectObj(SdrRectObj& rRectObj)
+: ViewContactOfTextObj(rRectObj)
+{
+}
+
+ViewContactOfSdrRectObj::~ViewContactOfSdrRectObj()
+{
+}
+
+void ViewContactOfSdrRectObj::createViewIndependentPrimitive2DSequence(drawinglayer::primitive2d::Primitive2DDecompositionVisitor& rVisitor) const
+{
+ const SfxItemSet& rItemSet = GetRectObj().GetMergedItemSet();
+ const drawinglayer::attribute::SdrLineFillEffectsTextAttribute aAttribute(
+ drawinglayer::primitive2d::createNewSdrLineFillEffectsTextAttribute(
+ rItemSet,
+ GetRectObj().getText(0),
+ false));
+
+ // take unrotated snap rect (direct model data) for position and size
+ const tools::Rectangle aRectangle(GetRectObj().GetGeoRect());
+ const ::basegfx::B2DRange aObjectRange = vcl::unotools::b2DRectangleFromRectangle(aRectangle);
+
+ const GeoStat& rGeoStat(GetRectObj().GetGeoStat());
+
+ // fill object matrix
+ basegfx::B2DHomMatrix aObjectMatrix(basegfx::utils::createScaleShearXRotateTranslateB2DHomMatrix(
+ aObjectRange.getWidth(), aObjectRange.getHeight(),
+ -rGeoStat.mfTanShearAngle,
+ rGeoStat.nRotationAngle ? toRadians(36000_deg100 - rGeoStat.nRotationAngle) : 0.0,
+ aObjectRange.getMinX(), aObjectRange.getMinY()));
+
+ // calculate corner radius
+ sal_uInt32 nCornerRadius(rItemSet.Get(SDRATTR_CORNER_RADIUS).GetValue());
+ double fCornerRadiusX;
+ double fCornerRadiusY;
+ drawinglayer::primitive2d::calculateRelativeCornerRadius(nCornerRadius, aObjectRange, fCornerRadiusX, fCornerRadiusY);
+
+ // #i105856# use knowledge about pickthrough from the model
+ const bool bPickThroughTransparentTextFrames(GetRectObj().getSdrModelFromSdrObject().IsPickThroughTransparentTextFrames());
+
+ // create primitive. Always create primitives to allow the decomposition of
+ // SdrRectanglePrimitive2D to create needed invisible elements for HitTest and/or BoundRect
+ const drawinglayer::primitive2d::Primitive2DReference xReference(
+ new drawinglayer::primitive2d::SdrRectanglePrimitive2D(
+ aObjectMatrix,
+ aAttribute,
+ fCornerRadiusX,
+ fCornerRadiusY,
+ // #i105856# use fill for HitTest when TextFrame and not PickThrough
+ GetRectObj().IsTextFrame() && !bPickThroughTransparentTextFrames));
+
+ rVisitor.visit(xReference);
+}
+
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/sdr/contact/viewcontactoftextobj.cxx b/svx/source/sdr/contact/viewcontactoftextobj.cxx
new file mode 100644
index 000000000..9e10d0130
--- /dev/null
+++ b/svx/source/sdr/contact/viewcontactoftextobj.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 <sdr/contact/viewcontactoftextobj.hxx>
+#include <svx/svdotext.hxx>
+
+namespace sdr::contact
+{
+ViewContactOfTextObj::ViewContactOfTextObj(SdrTextObj& rTextObj)
+ : ViewContactOfSdrObj(rTextObj)
+{
+}
+
+ViewContactOfTextObj::~ViewContactOfTextObj() {}
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/sdr/contact/viewcontactofunocontrol.cxx b/svx/source/sdr/contact/viewcontactofunocontrol.cxx
new file mode 100644
index 000000000..f99b24372
--- /dev/null
+++ b/svx/source/sdr/contact/viewcontactofunocontrol.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 <sal/config.h>
+
+#include <sdr/contact/viewcontactofunocontrol.hxx>
+#include <sdr/contact/viewobjectcontactofunocontrol.hxx>
+#include <svx/sdr/contact/objectcontactofpageview.hxx>
+#include <svx/svdouno.hxx>
+#include <svx/svdpagv.hxx>
+#include <svx/svdview.hxx>
+#include <svx/sdrpagewindow.hxx>
+
+#include <vcl/canvastools.hxx>
+#include <basegfx/matrix/b2dhommatrix.hxx>
+#include <drawinglayer/primitive2d/controlprimitive2d.hxx>
+#include <drawinglayer/primitive2d/sdrdecompositiontools2d.hxx>
+#include <osl/diagnose.h>
+
+
+namespace sdr::contact {
+
+
+ using ::com::sun::star::awt::XControl;
+ using ::com::sun::star::uno::Reference;
+ using ::com::sun::star::awt::XControlContainer;
+ using ::com::sun::star::awt::XControlModel;
+
+
+ //= ViewContactOfUnoControl
+
+ ViewContactOfUnoControl::ViewContactOfUnoControl( SdrUnoObj& _rUnoObject )
+ :ViewContactOfSdrObj( _rUnoObject )
+ {
+ }
+
+
+ ViewContactOfUnoControl::~ViewContactOfUnoControl()
+ {
+ }
+
+
+ Reference< XControl > ViewContactOfUnoControl::getTemporaryControlForWindow(
+ const vcl::Window& _rWindow, Reference< XControlContainer >& _inout_ControlContainer ) const
+ {
+ SdrUnoObj* pUnoObject = dynamic_cast< SdrUnoObj* >( TryToGetSdrObject() );
+ OSL_ENSURE( pUnoObject, "ViewContactOfUnoControl::getTemporaryControlForDevice: no SdrUnoObj!" );
+ if ( !pUnoObject )
+ return nullptr;
+ return ViewObjectContactOfUnoControl::getTemporaryControlForWindow( _rWindow, _inout_ControlContainer, *pUnoObject );
+ }
+
+
+ ViewObjectContact& ViewContactOfUnoControl::CreateObjectSpecificViewObjectContact( ObjectContact& _rObjectContact )
+ {
+ // print or print preview requires special handling
+ const OutputDevice* pDevice = _rObjectContact.TryToGetOutputDevice();
+ ObjectContactOfPageView* const pPageViewContact = dynamic_cast< ObjectContactOfPageView* >( &_rObjectContact );
+
+ const bool bPrintOrPreview = pPageViewContact
+ && ( ( ( pDevice != nullptr ) && ( pDevice->GetOutDevType() == OUTDEV_PRINTER ) )
+ || pPageViewContact->GetPageWindow().GetPageView().GetView().IsPrintPreview()
+ )
+ ;
+
+ if ( bPrintOrPreview )
+ return *new UnoControlPrintOrPreviewContact( *pPageViewContact, *this );
+
+ // all others are nowadays served by the same implementation
+ return *new ViewObjectContactOfUnoControl( _rObjectContact, *this );
+ }
+
+
+ void ViewContactOfUnoControl::createViewIndependentPrimitive2DSequence(drawinglayer::primitive2d::Primitive2DDecompositionVisitor& rVisitor) const
+ {
+ // create range. Use model data directly, not getBoundRect()/getSnapRect; these will use
+ // the primitive data themselves in the long run. Use SdrUnoObj's (which is a SdrRectObj)
+ // call to GetGeoRect() to access SdrTextObj::aRect directly and without executing anything
+ const basegfx::B2DRange aRange = vcl::unotools::b2DRectangleFromRectangle(GetSdrUnoObj().GetGeoRect());
+
+ // create object transform
+ basegfx::B2DHomMatrix aTransform;
+
+ aTransform.set(0, 0, aRange.getWidth());
+ aTransform.set(1, 1, aRange.getHeight());
+ aTransform.set(0, 2, aRange.getMinX());
+ aTransform.set(1, 2, aRange.getMinY());
+
+ Reference< XControlModel > xControlModel = GetSdrUnoObj().GetUnoControlModel();
+
+ if(xControlModel.is())
+ {
+ // create control primitive WITHOUT possibly existing XControl; this would be done in
+ // the VOC in createPrimitive2DSequence()
+ const drawinglayer::primitive2d::Primitive2DReference xRetval(
+ new drawinglayer::primitive2d::ControlPrimitive2D(
+ aTransform,
+ xControlModel));
+
+ rVisitor.visit(xRetval);
+ }
+ else
+ {
+ // always append an invisible outline for the cases where no visible content exists
+ const drawinglayer::primitive2d::Primitive2DReference xRetval(
+ drawinglayer::primitive2d::createHiddenGeometryPrimitives2D(
+ aTransform));
+
+ rVisitor.visit(xRetval);
+ }
+ }
+
+
+} // namespace sdr::contact
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/sdr/contact/viewcontactofvirtobj.cxx b/svx/source/sdr/contact/viewcontactofvirtobj.cxx
new file mode 100644
index 000000000..f4087d036
--- /dev/null
+++ b/svx/source/sdr/contact/viewcontactofvirtobj.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/sdr/contact/viewcontactofvirtobj.hxx>
+#include <svx/svdovirt.hxx>
+#include <basegfx/matrix/b2dhommatrix.hxx>
+#include <drawinglayer/primitive2d/transformprimitive2d.hxx>
+#include <drawinglayer/primitive2d/sdrdecompositiontools2d.hxx>
+
+namespace sdr::contact {
+
+ViewContactOfVirtObj::ViewContactOfVirtObj(SdrVirtObj& rObj)
+: ViewContactOfSdrObj(rObj)
+{
+}
+
+ViewContactOfVirtObj::~ViewContactOfVirtObj()
+{
+}
+
+SdrVirtObj& ViewContactOfVirtObj::GetVirtObj() const
+{
+ return static_cast<SdrVirtObj&>(mrObject);
+}
+
+// Access to possible sub-hierarchy
+sal_uInt32 ViewContactOfVirtObj::GetObjectCount() const
+{
+ // Here, SdrVirtObj's need to return 0L to show that they have no
+ // sub-hierarchy, even when they are group objects. This is necessary
+ // to avoid that the same VOCs will be added to the draw hierarchy
+ // twice which leads to problems.
+
+ // This solution is only a first solution to get things running. Later
+ // this needs to be replaced with creating real VOCs for the objects
+ // referenced by virtual objects to avoid the 'trick' of setting the
+ // offset for painting at the destination OutputDevice.
+
+ // As can be seen, with primitives, the problem will be solved using
+ // a transformPrimitive, so this solution can stay with primitives.
+ return 0;
+}
+
+void ViewContactOfVirtObj::createViewIndependentPrimitive2DSequence(drawinglayer::primitive2d::Primitive2DDecompositionVisitor& rVisitor) const
+{
+ // create displacement transformation if we have content
+ basegfx::B2DHomMatrix aObjectMatrix;
+ Point aAnchor(GetVirtObj().GetAnchorPos());
+
+ if(aAnchor.X() || aAnchor.Y())
+ {
+ aObjectMatrix.set(0, 2, aAnchor.X());
+ aObjectMatrix.set(1, 2, aAnchor.Y());
+ }
+
+ // use method from referenced object to get the Primitive2DContainer
+ drawinglayer::primitive2d::Primitive2DContainer xSequenceVirtual;
+ GetVirtObj().GetReferencedObj().GetViewContact().getViewIndependentPrimitive2DContainer(xSequenceVirtual);
+
+ if(!xSequenceVirtual.empty())
+ {
+ // create transform primitive
+ drawinglayer::primitive2d::Primitive2DReference xReference(
+ new drawinglayer::primitive2d::TransformPrimitive2D(
+ aObjectMatrix,
+ drawinglayer::primitive2d::Primitive2DContainer(xSequenceVirtual)));
+
+ rVisitor.visit(xReference);
+ }
+ else
+ {
+ // always append an invisible outline for the cases where no visible content exists
+ const drawinglayer::primitive2d::Primitive2DReference xReference(
+ drawinglayer::primitive2d::createHiddenGeometryPrimitives2D(
+ aObjectMatrix));
+
+ rVisitor.visit(xReference);
+ }
+}
+
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
+
diff --git a/svx/source/sdr/contact/viewobjectcontact.cxx b/svx/source/sdr/contact/viewobjectcontact.cxx
new file mode 100644
index 000000000..18acd5e52
--- /dev/null
+++ b/svx/source/sdr/contact/viewobjectcontact.cxx
@@ -0,0 +1,570 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <svx/sdr/contact/viewobjectcontact.hxx>
+#include <svx/sdr/contact/viewcontact.hxx>
+#include <svx/sdr/contact/objectcontact.hxx>
+#include <svx/sdr/contact/displayinfo.hxx>
+#include <svx/sdr/animation/animationstate.hxx>
+#include <svx/sdr/contact/viewobjectcontactredirector.hxx>
+#include <basegfx/color/bcolor.hxx>
+#include <drawinglayer/primitive2d/modifiedcolorprimitive2d.hxx>
+#include <drawinglayer/primitive2d/animatedprimitive2d.hxx>
+#include <drawinglayer/processor2d/baseprocessor2d.hxx>
+#include <svx/sdr/primitive2d/svx_primitivetypes2d.hxx>
+#include <drawinglayer/primitive2d/transformprimitive2d.hxx>
+#include <drawinglayer/primitive2d/structuretagprimitive2d.hxx>
+#include <svx/svdobj.hxx>
+#include <svx/svdmodel.hxx>
+#include <svx/svdpage.hxx>
+#include <svx/svdotext.hxx>
+#include <vcl/pdfwriter.hxx>
+
+using namespace com::sun::star;
+
+namespace {
+
+// animated extractor
+
+// Necessary to filter a sequence of animated primitives from
+// a sequence of primitives to find out if animated or not. The decision for
+// what to decompose is hard-coded and only done for knowingly animated primitives
+// to not decompose too deeply and unnecessarily. This implies that the list
+// which is view-specific needs to be expanded by hand when new animated objects
+// are added. This may eventually be changed to a dynamically configurable approach
+// if necessary.
+class AnimatedExtractingProcessor2D : public drawinglayer::processor2d::BaseProcessor2D
+{
+protected:
+ // the found animated primitives
+ drawinglayer::primitive2d::Primitive2DContainer maPrimitive2DSequence;
+
+ // text animation allowed?
+ bool mbTextAnimationAllowed : 1;
+
+ // graphic animation allowed?
+ bool mbGraphicAnimationAllowed : 1;
+
+ // as tooling, the process() implementation takes over API handling and calls this
+ // virtual render method when the primitive implementation is BasePrimitive2D-based.
+ virtual void processBasePrimitive2D(const drawinglayer::primitive2d::BasePrimitive2D& rCandidate) override;
+
+public:
+ AnimatedExtractingProcessor2D(
+ const drawinglayer::geometry::ViewInformation2D& rViewInformation,
+ bool bTextAnimationAllowed,
+ bool bGraphicAnimationAllowed);
+
+ // data access
+ const drawinglayer::primitive2d::Primitive2DContainer& getPrimitive2DSequence() const { return maPrimitive2DSequence; }
+ drawinglayer::primitive2d::Primitive2DContainer extractPrimitive2DSequence() const { return std::move(maPrimitive2DSequence); }
+};
+
+AnimatedExtractingProcessor2D::AnimatedExtractingProcessor2D(
+ const drawinglayer::geometry::ViewInformation2D& rViewInformation,
+ bool bTextAnimationAllowed,
+ bool bGraphicAnimationAllowed)
+: drawinglayer::processor2d::BaseProcessor2D(rViewInformation),
+ mbTextAnimationAllowed(bTextAnimationAllowed),
+ mbGraphicAnimationAllowed(bGraphicAnimationAllowed)
+{
+}
+
+void AnimatedExtractingProcessor2D::processBasePrimitive2D(const drawinglayer::primitive2d::BasePrimitive2D& rCandidate)
+{
+ // known implementation, access directly
+ switch(rCandidate.getPrimitive2DID())
+ {
+ // add and accept animated primitives directly, no need to decompose
+ case PRIMITIVE2D_ID_ANIMATEDSWITCHPRIMITIVE2D :
+ case PRIMITIVE2D_ID_ANIMATEDBLINKPRIMITIVE2D :
+ case PRIMITIVE2D_ID_ANIMATEDINTERPOLATEPRIMITIVE2D :
+ {
+ const drawinglayer::primitive2d::AnimatedSwitchPrimitive2D& rSwitchPrimitive = static_cast< const drawinglayer::primitive2d::AnimatedSwitchPrimitive2D& >(rCandidate);
+
+ if((rSwitchPrimitive.isTextAnimation() && mbTextAnimationAllowed)
+ || (rSwitchPrimitive.isGraphicAnimation() && mbGraphicAnimationAllowed))
+ {
+ const drawinglayer::primitive2d::Primitive2DReference xReference(const_cast< drawinglayer::primitive2d::BasePrimitive2D* >(&rCandidate));
+ maPrimitive2DSequence.push_back(xReference);
+ }
+ break;
+ }
+
+ // decompose animated gifs where SdrGrafPrimitive2D produces a GraphicPrimitive2D
+ // which then produces the animation infos (all when used/needed)
+ case PRIMITIVE2D_ID_SDRGRAFPRIMITIVE2D :
+ case PRIMITIVE2D_ID_GRAPHICPRIMITIVE2D :
+
+ // decompose SdrObjects with evtl. animated text
+ case PRIMITIVE2D_ID_SDRCAPTIONPRIMITIVE2D :
+ case PRIMITIVE2D_ID_SDRCONNECTORPRIMITIVE2D :
+ case PRIMITIVE2D_ID_SDRCUSTOMSHAPEPRIMITIVE2D :
+ case PRIMITIVE2D_ID_SDRELLIPSEPRIMITIVE2D :
+ case PRIMITIVE2D_ID_SDRELLIPSESEGMENTPRIMITIVE2D :
+ case PRIMITIVE2D_ID_SDRMEASUREPRIMITIVE2D :
+ case PRIMITIVE2D_ID_SDRPATHPRIMITIVE2D :
+ case PRIMITIVE2D_ID_SDRRECTANGLEPRIMITIVE2D :
+
+ // #121194# With Graphic as Bitmap FillStyle, also check
+ // for primitives filled with animated graphics
+ case PRIMITIVE2D_ID_POLYPOLYGONGRAPHICPRIMITIVE2D:
+ case PRIMITIVE2D_ID_FILLGRAPHICPRIMITIVE2D:
+ case PRIMITIVE2D_ID_TRANSFORMPRIMITIVE2D:
+
+ // decompose evtl. animated text contained in MaskPrimitive2D
+ // or group primitives
+ case PRIMITIVE2D_ID_MASKPRIMITIVE2D :
+ case PRIMITIVE2D_ID_GROUPPRIMITIVE2D :
+ {
+ process(rCandidate);
+ break;
+ }
+
+ default :
+ {
+ // nothing to do for the rest
+ break;
+ }
+ }
+}
+
+} // end of anonymous namespace
+
+namespace sdr::contact {
+
+ViewObjectContact::ViewObjectContact(ObjectContact& rObjectContact, ViewContact& rViewContact)
+: mrObjectContact(rObjectContact),
+ mrViewContact(rViewContact),
+ maGridOffset(0.0, 0.0),
+ mnActionChangedCount(0),
+ mbLazyInvalidate(false)
+{
+ // make the ViewContact remember me
+ mrViewContact.AddViewObjectContact(*this);
+
+ // make the ObjectContact remember me
+ mrObjectContact.AddViewObjectContact(*this);
+}
+
+ViewObjectContact::~ViewObjectContact()
+{
+ // invalidate in view
+ if(!getObjectRange().isEmpty())
+ {
+ GetObjectContact().InvalidatePartOfView(maObjectRange);
+ }
+
+ // delete PrimitiveAnimation
+ mpPrimitiveAnimation.reset();
+
+ // take care of remembered ObjectContact. Remove from
+ // OC first. The VC removal (below) CAN trigger a StopGettingViewed()
+ // which (depending of its implementation) may destroy other OCs. This
+ // can trigger the deletion of the helper OC of a page visualising object
+ // which IS the OC of this object. Eventually StopGettingViewed() needs
+ // to get asynchron later
+ GetObjectContact().RemoveViewObjectContact(*this);
+
+ // take care of remembered ViewContact
+ GetViewContact().RemoveViewObjectContact(*this);
+}
+
+const basegfx::B2DRange& ViewObjectContact::getObjectRange() const
+{
+ if(maObjectRange.isEmpty())
+ {
+ const drawinglayer::geometry::ViewInformation2D& rViewInfo2D = GetObjectContact().getViewInformation2D();
+ basegfx::B2DRange aTempRange = GetViewContact().getRange(rViewInfo2D);
+ if (!aTempRange.isEmpty())
+ {
+ const_cast< ViewObjectContact* >(this)->maObjectRange = aTempRange;
+ }
+ else
+ {
+ // if range is not computed (new or LazyInvalidate objects), force it
+ const DisplayInfo aDisplayInfo;
+ const drawinglayer::primitive2d::Primitive2DContainer& xSequence(getPrimitive2DSequence(aDisplayInfo));
+
+ if(!xSequence.empty())
+ {
+ const_cast< ViewObjectContact* >(this)->maObjectRange =
+ xSequence.getB2DRange(rViewInfo2D);
+ }
+ }
+ }
+
+ return maObjectRange;
+}
+
+void ViewObjectContact::ActionChanged()
+{
+ // clear cached primitives
+ mxPrimitive2DSequence.clear();
+ ++mnActionChangedCount;
+
+ if(mbLazyInvalidate)
+ return;
+
+ // set local flag
+ mbLazyInvalidate = true;
+
+ // force ObjectRange
+ getObjectRange();
+
+ if(!getObjectRange().isEmpty())
+ {
+ // invalidate current valid range
+ GetObjectContact().InvalidatePartOfView(maObjectRange);
+
+ // reset gridOffset, it needs to be recalculated
+ if (GetObjectContact().supportsGridOffsets())
+ resetGridOffset();
+ else
+ maObjectRange.reset();
+ }
+
+ // register at OC for lazy invalidate
+ GetObjectContact().setLazyInvalidate(*this);
+}
+
+void ViewObjectContact::triggerLazyInvalidate()
+{
+ if(!mbLazyInvalidate)
+ return;
+
+ // reset flag
+ mbLazyInvalidate = false;
+
+ // force ObjectRange
+ getObjectRange();
+
+ if(!getObjectRange().isEmpty())
+ {
+ // invalidate current valid range
+ GetObjectContact().InvalidatePartOfView(maObjectRange);
+ }
+}
+
+// Take some action when new objects are inserted
+void ViewObjectContact::ActionChildInserted(ViewContact& rChild)
+{
+ // force creation of the new VOC and trigger it's refresh, so it
+ // will take part in LazyInvalidate immediately
+ rChild.GetViewObjectContact(GetObjectContact()).ActionChanged();
+
+ // forward action to ObjectContact
+ // const ViewObjectContact& rChildVOC = rChild.GetViewObjectContact(GetObjectContact());
+ // GetObjectContact().InvalidatePartOfView(rChildVOC.getObjectRange());
+}
+
+void ViewObjectContact::checkForPrimitive2DAnimations()
+{
+ // remove old one
+ mpPrimitiveAnimation.reset();
+
+ // check for animated primitives
+ if(mxPrimitive2DSequence.empty())
+ return;
+
+ const bool bTextAnimationAllowed(GetObjectContact().IsTextAnimationAllowed());
+ const bool bGraphicAnimationAllowed(GetObjectContact().IsGraphicAnimationAllowed());
+
+ if(bTextAnimationAllowed || bGraphicAnimationAllowed)
+ {
+ AnimatedExtractingProcessor2D aAnimatedExtractor(GetObjectContact().getViewInformation2D(),
+ bTextAnimationAllowed, bGraphicAnimationAllowed);
+ aAnimatedExtractor.process(mxPrimitive2DSequence);
+
+ if(!aAnimatedExtractor.getPrimitive2DSequence().empty())
+ {
+ // derived primitiveList is animated, setup new PrimitiveAnimation
+ mpPrimitiveAnimation.reset( new sdr::animation::PrimitiveAnimation(*this, aAnimatedExtractor.extractPrimitive2DSequence()) );
+ }
+ }
+}
+
+void ViewObjectContact::createPrimitive2DSequence(const DisplayInfo& rDisplayInfo, drawinglayer::primitive2d::Primitive2DDecompositionVisitor& rVisitor) const
+{
+ // get the view-independent Primitive from the viewContact
+ drawinglayer::primitive2d::Primitive2DContainer xRetval;
+ GetViewContact().getViewIndependentPrimitive2DContainer(xRetval);
+
+ if(!xRetval.empty())
+ {
+ // handle GluePoint
+ if(!GetObjectContact().isOutputToPrinter() && GetObjectContact().AreGluePointsVisible())
+ {
+ const drawinglayer::primitive2d::Primitive2DContainer xGlue(GetViewContact().createGluePointPrimitive2DSequence());
+
+ if(!xGlue.empty())
+ {
+ xRetval.append(xGlue);
+ }
+ }
+
+ // handle ghosted
+ if(isPrimitiveGhosted(rDisplayInfo))
+ {
+ const basegfx::BColor aRGBWhite(1.0, 1.0, 1.0);
+ const basegfx::BColorModifierSharedPtr aBColorModifier =
+ std::make_shared<basegfx::BColorModifier_interpolate>(
+ aRGBWhite,
+ 0.5);
+ drawinglayer::primitive2d::Primitive2DReference xReference(
+ new drawinglayer::primitive2d::ModifiedColorPrimitive2D(
+ std::move(xRetval),
+ aBColorModifier));
+
+ xRetval = drawinglayer::primitive2d::Primitive2DContainer { xReference };
+ }
+ }
+
+ rVisitor.visit(xRetval);
+}
+
+drawinglayer::primitive2d::Primitive2DContainer const & ViewObjectContact::getPrimitive2DSequence(const DisplayInfo& rDisplayInfo) const
+{
+ // only some of the top-level apps are any good at reliably invalidating us (e.g. writer is not)
+ SdrObject* pSdrObj(mrViewContact.TryToGetSdrObject());
+
+ if (nullptr != pSdrObj && pSdrObj->getSdrModelFromSdrObject().IsVOCInvalidationIsReliable())
+ {
+ if (!mxPrimitive2DSequence.empty())
+ return mxPrimitive2DSequence;
+ }
+
+ // prepare new representation
+ drawinglayer::primitive2d::Primitive2DContainer xNewPrimitiveSequence;
+
+ // take care of redirectors and create new list
+ ViewObjectContactRedirector* pRedirector = GetObjectContact().GetViewObjectContactRedirector();
+
+ if(pRedirector)
+ {
+ pRedirector->createRedirectedPrimitive2DSequence(*this, rDisplayInfo, xNewPrimitiveSequence);
+ }
+ else
+ {
+ createPrimitive2DSequence(rDisplayInfo, xNewPrimitiveSequence);
+ }
+
+ // check and eventually embed to GridOffset transform primitive (calc only)
+ if(!xNewPrimitiveSequence.empty() && GetObjectContact().supportsGridOffsets())
+ {
+ const basegfx::B2DVector& rGridOffset(getGridOffset());
+
+ if(0.0 != rGridOffset.getX() || 0.0 != rGridOffset.getY())
+ {
+ const basegfx::B2DHomMatrix aTranslateGridOffset(
+ basegfx::utils::createTranslateB2DHomMatrix(
+ rGridOffset));
+ drawinglayer::primitive2d::Primitive2DReference aEmbed(
+ new drawinglayer::primitive2d::TransformPrimitive2D(
+ aTranslateGridOffset,
+ std::move(xNewPrimitiveSequence)));
+ xNewPrimitiveSequence = drawinglayer::primitive2d::Primitive2DContainer { aEmbed };
+ }
+ }
+
+ // Check if we need to embed to a StructureTagPrimitive2D, too. This
+ // was done at ImplRenderPaintProc::createRedirectedPrimitive2DSequence before
+ if (!xNewPrimitiveSequence.empty() && GetObjectContact().isExportTaggedPDF())
+ {
+ if (nullptr != pSdrObj)
+ {
+ vcl::PDFWriter::StructElement eElement(vcl::PDFWriter::NonStructElement);
+ const SdrInventor nInventor(pSdrObj->GetObjInventor());
+ const SdrObjKind nIdentifier(pSdrObj->GetObjIdentifier());
+ const bool bIsTextObj(nullptr != dynamic_cast<const SdrTextObj *>(pSdrObj));
+
+ // Note: SwFlyDrawObj/SwVirtFlyDrawObj have SdrInventor::Swg - these
+ // are *not* handled here because not all of them are painted
+ // completely with primitives, so a tag here does not encapsulate them.
+ // The tag must be created by SwTaggedPDFHelper until this is fixed.
+ if ( nInventor == SdrInventor::Default )
+ {
+ if ( nIdentifier == SdrObjKind::Group )
+ eElement = vcl::PDFWriter::Section;
+ else if (nIdentifier == SdrObjKind::Table)
+ eElement = vcl::PDFWriter::Table;
+ else if ( nIdentifier == SdrObjKind::TitleText )
+ eElement = vcl::PDFWriter::Heading;
+ else if ( nIdentifier == SdrObjKind::OutlineText )
+ eElement = vcl::PDFWriter::Division;
+ else if ( !bIsTextObj || !static_cast<const SdrTextObj&>(*pSdrObj).HasText() )
+ eElement = vcl::PDFWriter::Figure;
+ else
+ eElement = vcl::PDFWriter::Division;
+ }
+
+ if(vcl::PDFWriter::NonStructElement != eElement)
+ {
+ SdrPage* pSdrPage(pSdrObj->getSdrPageFromSdrObject());
+
+ if(pSdrPage)
+ {
+ const bool bBackground(pSdrPage->IsMasterPage());
+ const bool bImage(SdrObjKind::Graphic == pSdrObj->GetObjIdentifier());
+ // note: there must be output device here, in PDF export
+ sal_Int32 nAnchorId(-1);
+ if (auto const pUserCall = pSdrObj->GetUserCall())
+ {
+ nAnchorId = pUserCall->GetPDFAnchorStructureElementId(
+ *pSdrObj, *GetObjectContact().TryToGetOutputDevice());
+ }
+
+ drawinglayer::primitive2d::Primitive2DReference xReference(
+ new drawinglayer::primitive2d::StructureTagPrimitive2D(
+ eElement,
+ bBackground,
+ bImage,
+ std::move(xNewPrimitiveSequence),
+ nAnchorId));
+ xNewPrimitiveSequence = drawinglayer::primitive2d::Primitive2DContainer { xReference };
+ }
+ }
+ }
+ else
+ {
+ // page backgrounds etc should be tagged as artifacts:
+ xNewPrimitiveSequence = drawinglayer::primitive2d::Primitive2DContainer {
+ new drawinglayer::primitive2d::StructureTagPrimitive2D(
+ // lies to force silly VclMetafileProcessor2D to emit NonStructElement
+ vcl::PDFWriter::Division,
+ true,
+ true,
+ std::move(xNewPrimitiveSequence))
+ };
+ }
+ }
+
+ // Local up-to-date checks. New list different from local one?
+ // This is the important point where it gets decided if the current or the new
+ // representation gets used. This is important for performance, since the
+ // current representation contains possible precious decompositions. That
+ // comparisons triggers exactly if something in the object visualization
+ // has changed.
+ // Note: That is the main reason for BasePrimitive2D::operator== at all. I
+ // have alternatively tried to invalidate the local representation on object
+ // change, but that is simply not reliable.
+ // Note2: I did that once in aw080, the lost CWS, and it worked well enough
+ // so that I could remove *all* operator== from all derivations of
+ // BasePrimitive2D, so it can be done again (with the needed ressources)
+ if(mxPrimitive2DSequence != xNewPrimitiveSequence)
+ {
+ // has changed, copy content
+ const_cast< ViewObjectContact* >(this)->mxPrimitive2DSequence = std::move(xNewPrimitiveSequence);
+
+ // check for animated stuff
+ const_cast< ViewObjectContact* >(this)->checkForPrimitive2DAnimations();
+
+ // always update object range when PrimitiveSequence changes
+ const drawinglayer::geometry::ViewInformation2D& rViewInformation2D(GetObjectContact().getViewInformation2D());
+ const_cast< ViewObjectContact* >(this)->maObjectRange = mxPrimitive2DSequence.getB2DRange(rViewInformation2D);
+ }
+
+ // return current Primitive2DContainer
+ return mxPrimitive2DSequence;
+}
+
+bool ViewObjectContact::isPrimitiveVisible(const DisplayInfo& /*rDisplayInfo*/) const
+{
+ // default: always visible
+ return true;
+}
+
+bool ViewObjectContact::isPrimitiveGhosted(const DisplayInfo& rDisplayInfo) const
+{
+ // default: standard check
+ return (GetObjectContact().DoVisualizeEnteredGroup() && !GetObjectContact().isOutputToPrinter() && rDisplayInfo.IsGhostedDrawModeActive());
+}
+
+void ViewObjectContact::getPrimitive2DSequenceHierarchy(DisplayInfo& rDisplayInfo, drawinglayer::primitive2d::Primitive2DDecompositionVisitor& rVisitor) const
+{
+ // check model-view visibility
+ if(!isPrimitiveVisible(rDisplayInfo))
+ return;
+
+ getPrimitive2DSequence(rDisplayInfo);
+ if(mxPrimitive2DSequence.empty())
+ return;
+
+ // get ranges
+ const drawinglayer::geometry::ViewInformation2D& rViewInformation2D(GetObjectContact().getViewInformation2D());
+ // tdf#147164 cannot use maObjectRange here, it is unreliable
+ const basegfx::B2DRange aObjectRange(mxPrimitive2DSequence.getB2DRange(rViewInformation2D));
+ const basegfx::B2DRange& aViewRange(rViewInformation2D.getViewport());
+
+ // check geometrical visibility
+ bool bVisible = aViewRange.isEmpty() || aViewRange.overlaps(aObjectRange);
+ if(!bVisible)
+ return;
+
+ // temporarily take over the mxPrimitive2DSequence, in case it gets invalidated while we want to iterate over it
+ auto tmp = std::move(const_cast<ViewObjectContact*>(this)->mxPrimitive2DSequence);
+ int nPrevCount = mnActionChangedCount;
+
+ rVisitor.visit(tmp);
+
+ // if we received ActionChanged() calls while walking the primitives, then leave it empty, otherwise move it back
+ if (mnActionChangedCount == nPrevCount)
+ const_cast<ViewObjectContact*>(this)->mxPrimitive2DSequence = std::move(tmp);
+}
+
+void ViewObjectContact::getPrimitive2DSequenceSubHierarchy(DisplayInfo& rDisplayInfo, drawinglayer::primitive2d::Primitive2DDecompositionVisitor& rVisitor) const
+{
+ const sal_uInt32 nSubHierarchyCount(GetViewContact().GetObjectCount());
+
+ for(sal_uInt32 a(0); a < nSubHierarchyCount; a++)
+ {
+ const ViewObjectContact& rCandidate(GetViewContact().GetViewContact(a).GetViewObjectContact(GetObjectContact()));
+
+ rCandidate.getPrimitive2DSequenceHierarchy(rDisplayInfo, rVisitor);
+ }
+}
+
+// Support getting a GridOffset per object and view for non-linear ViewToDevice
+// transformation (calc). On-demand created by delegating to the ObjectContact
+// (->View) that has then all needed information
+const basegfx::B2DVector& ViewObjectContact::getGridOffset() const
+{
+ if(0.0 == maGridOffset.getX() && 0.0 == maGridOffset.getY() && GetObjectContact().supportsGridOffsets())
+ {
+ // create on-demand
+ GetObjectContact().calculateGridOffsetForViewOjectContact(const_cast<ViewObjectContact*>(this)->maGridOffset, *this);
+ }
+
+ return maGridOffset;
+}
+
+void ViewObjectContact::resetGridOffset()
+{
+ // reset buffered GridOffset itself
+ maGridOffset.setX(0.0);
+ maGridOffset.setY(0.0);
+
+ // also reset sequence to get a re-calculation when GridOffset changes
+ mxPrimitive2DSequence.clear();
+ maObjectRange.reset();
+}
+
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/sdr/contact/viewobjectcontactofe3d.cxx b/svx/source/sdr/contact/viewobjectcontactofe3d.cxx
new file mode 100644
index 000000000..c6d41bdc9
--- /dev/null
+++ b/svx/source/sdr/contact/viewobjectcontactofe3d.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 <sdr/contact/viewobjectcontactofe3d.hxx>
+#include <sdr/contact/viewcontactofe3d.hxx>
+#include <basegfx/color/bcolor.hxx>
+#include <drawinglayer/primitive3d/modifiedcolorprimitive3d.hxx>
+
+namespace sdr::contact
+{
+ ViewObjectContactOfE3d::ViewObjectContactOfE3d(ObjectContact& rObjectContact, ViewContact& rViewContact)
+ : ViewObjectContactOfSdrObj(rObjectContact, rViewContact)
+ {
+ }
+
+ ViewObjectContactOfE3d::~ViewObjectContactOfE3d()
+ {
+ }
+
+ drawinglayer::primitive3d::Primitive3DContainer ViewObjectContactOfE3d::getPrimitive3DContainer(const DisplayInfo& rDisplayInfo) const
+ {
+ // get the view-independent Primitive from the viewContact
+ const ViewContactOfE3d& rViewContactOfE3d(dynamic_cast< const ViewContactOfE3d& >(GetViewContact()));
+ drawinglayer::primitive3d::Primitive3DContainer xRetval(rViewContactOfE3d.getViewIndependentPrimitive3DContainer());
+
+ // handle ghosted
+ if(isPrimitiveGhosted(rDisplayInfo))
+ {
+ const ::basegfx::BColor aRGBWhite(1.0, 1.0, 1.0);
+ const ::basegfx::BColorModifierSharedPtr aBColorModifier =
+ std::make_shared<basegfx::BColorModifier_interpolate>(
+ aRGBWhite,
+ 0.5);
+ const drawinglayer::primitive3d::Primitive3DReference xReference(
+ new drawinglayer::primitive3d::ModifiedColorPrimitive3D(
+ xRetval,
+ aBColorModifier));
+
+ xRetval = { xReference };
+ }
+
+ return xRetval;
+ }
+
+ void ViewObjectContactOfE3d::createPrimitive2DSequence(const DisplayInfo& rDisplayInfo, drawinglayer::primitive2d::Primitive2DDecompositionVisitor& rVisitor) const
+ {
+ const ViewContactOfE3d& rViewContact = static_cast< const ViewContactOfE3d& >(GetViewContact());
+
+ // get 3d primitive vector, isPrimitiveVisible() is done in 3d creator
+ rVisitor.visit(rViewContact.impCreateWithGivenPrimitive3DContainer(getPrimitive3DContainer(rDisplayInfo)));
+ }
+
+
+} // end of namespace
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/sdr/contact/viewobjectcontactofe3dscene.cxx b/svx/source/sdr/contact/viewobjectcontactofe3dscene.cxx
new file mode 100644
index 000000000..ac7ad90f1
--- /dev/null
+++ b/svx/source/sdr/contact/viewobjectcontactofe3dscene.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 <sdr/contact/viewobjectcontactofe3dscene.hxx>
+#include <svx/sdr/contact/displayinfo.hxx>
+#include <svx/sdr/contact/objectcontact.hxx>
+#include <svx/sdr/contact/viewcontactofe3dscene.hxx>
+#include <basegfx/color/bcolormodifier.hxx>
+#include <drawinglayer/primitive2d/modifiedcolorprimitive2d.hxx>
+
+
+using namespace com::sun::star;
+
+
+namespace
+{
+ // Helper method to recursively travel the DrawHierarchy for 3D objects contained in
+ // the 2D Scene. This will create all VOCs for the current OC which are needed
+ // for ActionChanged() functionality
+ void impInternalSubHierarchyTraveller(const sdr::contact::ViewObjectContact& rVOC)
+ {
+ const sdr::contact::ViewContact& rVC = rVOC.GetViewContact();
+ const sal_uInt32 nSubHierarchyCount(rVC.GetObjectCount());
+
+ for(sal_uInt32 a(0); a < nSubHierarchyCount; a++)
+ {
+ const sdr::contact::ViewObjectContact& rCandidate(rVC.GetViewContact(a).GetViewObjectContact(rVOC.GetObjectContact()));
+ impInternalSubHierarchyTraveller(rCandidate);
+ }
+ }
+} // end of anonymous namespace
+
+
+namespace sdr::contact
+{
+ ViewObjectContactOfE3dScene::ViewObjectContactOfE3dScene(ObjectContact& rObjectContact, ViewContact& rViewContact)
+ : ViewObjectContactOfSdrObj(rObjectContact, rViewContact)
+ {
+ }
+
+ ViewObjectContactOfE3dScene::~ViewObjectContactOfE3dScene()
+ {
+ }
+
+ void ViewObjectContactOfE3dScene::createPrimitive2DSequence(const DisplayInfo& rDisplayInfo, drawinglayer::primitive2d::Primitive2DDecompositionVisitor& rVisitor) const
+ {
+ // handle ghosted, else the whole 3d group will be encapsulated to a ghosted primitive set (see below)
+ const bool bHandleGhostedDisplay(GetObjectContact().DoVisualizeEnteredGroup() && !GetObjectContact().isOutputToPrinter() && rDisplayInfo.IsGhostedDrawModeActive());
+ const bool bIsActiveVC(bHandleGhostedDisplay && GetObjectContact().getActiveViewContact() == &GetViewContact());
+
+ if(bIsActiveVC)
+ {
+ // switch off ghosted, display contents normal
+ const_cast< DisplayInfo& >(rDisplayInfo).ClearGhostedDrawMode();
+ }
+
+ // create 2d primitive with content, use layer visibility test
+ // this uses no ghosted mode, so scenes in scenes and entering them will not
+ // support ghosted for now. This is no problem currently but would need to be
+ // added when sub-groups in 3d will be added one day.
+ const ViewContactOfE3dScene& rViewContact = dynamic_cast< ViewContactOfE3dScene& >(GetViewContact());
+ const SdrLayerIDSet& rVisibleLayers = rDisplayInfo.GetProcessLayers();
+ drawinglayer::primitive2d::Primitive2DContainer xRetval(rViewContact.createScenePrimitive2DSequence(&rVisibleLayers));
+
+ if(!xRetval.empty())
+ {
+ // allow evtl. embedding in object-specific infos, e.g. Name, Title, Description
+ xRetval = rViewContact.embedToObjectSpecificInformation(std::move(xRetval));
+
+ // handle GluePoint
+ if(!GetObjectContact().isOutputToPrinter() && GetObjectContact().AreGluePointsVisible())
+ {
+ const drawinglayer::primitive2d::Primitive2DContainer xGlue(GetViewContact().createGluePointPrimitive2DSequence());
+
+ if(!xGlue.empty())
+ {
+ xRetval.append(xGlue);
+ }
+ }
+
+ // handle ghosted
+ if(isPrimitiveGhosted(rDisplayInfo))
+ {
+ const ::basegfx::BColor aRGBWhite(1.0, 1.0, 1.0);
+ const ::basegfx::BColorModifierSharedPtr aBColorModifier =
+ std::make_shared<basegfx::BColorModifier_interpolate>(
+ aRGBWhite,
+ 0.5);
+ const drawinglayer::primitive2d::Primitive2DReference xReference(
+ new drawinglayer::primitive2d::ModifiedColorPrimitive2D(
+ std::move(xRetval),
+ aBColorModifier));
+
+ xRetval = drawinglayer::primitive2d::Primitive2DContainer { xReference };
+ }
+ }
+
+ if(bIsActiveVC)
+ {
+ // set back, display ghosted again
+ const_cast< DisplayInfo& >(rDisplayInfo).SetGhostedDrawMode();
+ }
+
+ rVisitor.visit(xRetval);
+ }
+
+ void ViewObjectContactOfE3dScene::getPrimitive2DSequenceHierarchy(DisplayInfo& rDisplayInfo, drawinglayer::primitive2d::Primitive2DDecompositionVisitor& rVisitor) const
+ {
+ // To get the VOCs for the contained 3D objects created to get the correct
+ // Draw hierarchy and ActionChanged() working properly, travel the DrawHierarchy
+ // using a local tooling method
+ impInternalSubHierarchyTraveller(*this);
+
+ // call parent
+ ViewObjectContactOfSdrObj::getPrimitive2DSequenceHierarchy(rDisplayInfo, rVisitor);
+ }
+
+} // end of namespace
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/sdr/contact/viewobjectcontactofgraphic.cxx b/svx/source/sdr/contact/viewobjectcontactofgraphic.cxx
new file mode 100644
index 000000000..601ec28df
--- /dev/null
+++ b/svx/source/sdr/contact/viewobjectcontactofgraphic.cxx
@@ -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 .
+ */
+
+#include <sdr/contact/viewobjectcontactofgraphic.hxx>
+#include <sdr/contact/viewcontactofgraphic.hxx>
+#include <svx/sdr/contact/objectcontact.hxx>
+
+namespace sdr::contact
+{
+ void ViewObjectContactOfGraphic::createPrimitive2DSequence(const DisplayInfo& rDisplayInfo, drawinglayer::primitive2d::Primitive2DDecompositionVisitor& rVisitor) const
+ {
+ // #i103255# suppress when graphic needs draft visualisation and output
+ // is for PDF export/Printer
+ const ViewContactOfGraphic& rVCOfGraphic = static_cast< const ViewContactOfGraphic& >(GetViewContact());
+
+ if(rVCOfGraphic.visualisationUsesDraft())
+ {
+ const ObjectContact& rObjectContact = GetObjectContact();
+
+ if(rObjectContact.isOutputToPDFFile() || rObjectContact.isOutputToPrinter())
+ {
+ return;
+ }
+ }
+
+ // get return value by calling parent
+ ViewObjectContactOfSdrObj::createPrimitive2DSequence(rDisplayInfo, rVisitor);
+ }
+
+ ViewObjectContactOfGraphic::ViewObjectContactOfGraphic(ObjectContact& rObjectContact, ViewContact& rViewContact)
+ : ViewObjectContactOfSdrObj(rObjectContact, rViewContact)
+ {
+ }
+
+ ViewObjectContactOfGraphic::~ViewObjectContactOfGraphic()
+ {
+ }
+
+} // end of namespace
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/sdr/contact/viewobjectcontactofgroup.cxx b/svx/source/sdr/contact/viewobjectcontactofgroup.cxx
new file mode 100644
index 000000000..8469a00c1
--- /dev/null
+++ b/svx/source/sdr/contact/viewobjectcontactofgroup.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 <sdr/contact/viewobjectcontactofgroup.hxx>
+#include <svx/sdr/contact/displayinfo.hxx>
+#include <svx/sdr/contact/objectcontact.hxx>
+#include <svx/sdr/contact/viewcontact.hxx>
+#include <svx/svdobj.hxx>
+
+
+using namespace com::sun::star;
+
+
+namespace sdr::contact
+{
+ ViewObjectContactOfGroup::ViewObjectContactOfGroup(ObjectContact& rObjectContact, ViewContact& rViewContact)
+ : ViewObjectContactOfSdrObj(rObjectContact, rViewContact)
+ {
+ }
+
+ ViewObjectContactOfGroup::~ViewObjectContactOfGroup()
+ {
+ }
+
+ void ViewObjectContactOfGroup::getPrimitive2DSequenceHierarchy(DisplayInfo& rDisplayInfo, drawinglayer::primitive2d::Primitive2DDecompositionVisitor& rVisitor) const
+ {
+ // check model-view visibility
+ if(!isPrimitiveVisible(rDisplayInfo))
+ return;
+
+ const sal_uInt32 nSubHierarchyCount(GetViewContact().GetObjectCount());
+ if(nSubHierarchyCount)
+ {
+ const bool bDoGhostedDisplaying(
+ GetObjectContact().DoVisualizeEnteredGroup()
+ && !GetObjectContact().isOutputToPrinter()
+ && GetObjectContact().getActiveViewContact() == &GetViewContact());
+
+ if(bDoGhostedDisplaying)
+ {
+ rDisplayInfo.ClearGhostedDrawMode();
+ }
+
+ // visit object hierarchy
+ getPrimitive2DSequenceSubHierarchy(rDisplayInfo, rVisitor);
+
+ if(bDoGhostedDisplaying)
+ {
+ rDisplayInfo.SetGhostedDrawMode();
+ }
+ }
+ else
+ {
+ // draw replacement object for group. This will use ViewContactOfGroup::createViewIndependentPrimitive2DSequence
+ // which creates the replacement primitives for an empty group
+ ViewObjectContactOfSdrObj::getPrimitive2DSequenceHierarchy(rDisplayInfo, rVisitor);
+ }
+ }
+
+ bool ViewObjectContactOfGroup::isPrimitiveVisibleOnAnyLayer(const SdrLayerIDSet& aLayers) const
+ {
+ SdrLayerIDSet aObjectLayers;
+ getSdrObject().getMergedHierarchySdrLayerIDSet(aObjectLayers);
+ aObjectLayers &= aLayers;
+ return !aObjectLayers.IsEmpty();
+ }
+
+} // end of namespace
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/sdr/contact/viewobjectcontactofmasterpagedescriptor.cxx b/svx/source/sdr/contact/viewobjectcontactofmasterpagedescriptor.cxx
new file mode 100644
index 000000000..f96ed115d
--- /dev/null
+++ b/svx/source/sdr/contact/viewobjectcontactofmasterpagedescriptor.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 <sdr/contact/viewobjectcontactofmasterpagedescriptor.hxx>
+#include <sdr/contact/viewcontactofmasterpagedescriptor.hxx>
+#include <svx/sdr/contact/displayinfo.hxx>
+#include <svx/sdr/contact/objectcontact.hxx>
+#include <svx/svdpage.hxx>
+#include <drawinglayer/primitive2d/maskprimitive2d.hxx>
+#include <basegfx/polygon/b2dpolygontools.hxx>
+#include <basegfx/polygon/b2dpolygon.hxx>
+
+
+namespace sdr::contact
+{
+ ViewObjectContactOfMasterPageDescriptor::ViewObjectContactOfMasterPageDescriptor(ObjectContact& rObjectContact, ViewContact& rViewContact)
+ : ViewObjectContact(rObjectContact, rViewContact)
+ {
+ }
+
+ ViewObjectContactOfMasterPageDescriptor::~ViewObjectContactOfMasterPageDescriptor()
+ {
+ }
+
+ bool ViewObjectContactOfMasterPageDescriptor::isPrimitiveVisible(const DisplayInfo& rDisplayInfo) const
+ {
+ if(rDisplayInfo.GetControlLayerProcessingActive())
+ {
+ return false;
+ }
+
+ if(!rDisplayInfo.GetPageProcessingActive())
+ {
+ return false;
+ }
+
+ return true;
+ }
+
+ void ViewObjectContactOfMasterPageDescriptor::getPrimitive2DSequenceHierarchy(DisplayInfo& rDisplayInfo, drawinglayer::primitive2d::Primitive2DDecompositionVisitor& rVisitor) const
+ {
+ drawinglayer::primitive2d::Primitive2DContainer xMasterPageSequence;
+ const sdr::MasterPageDescriptor& rDescriptor = static_cast< ViewContactOfMasterPageDescriptor& >(GetViewContact()).GetMasterPageDescriptor();
+
+ // used range (retval) is fixed here, it's the MasterPage fill range
+ const SdrPage& rOwnerPage = rDescriptor.GetOwnerPage();
+ const basegfx::B2DRange aInnerRange(
+ rOwnerPage.GetLeftBorder(), rOwnerPage.GetUpperBorder(),
+ rOwnerPage.GetWidth() - rOwnerPage.GetRightBorder(), rOwnerPage.GetHeight() - rOwnerPage.GetLowerBorder());
+ const basegfx::B2DRange aOuterRange(
+ 0, 0, rOwnerPage.GetWidth(), rOwnerPage.GetHeight());
+ // ??? somehow only the master page's bit is used
+ bool const isFullSize(rDescriptor.GetUsedPage().IsBackgroundFullSize());
+ basegfx::B2DRange const& rPageFillRange(isFullSize ? aOuterRange : aInnerRange);
+
+ // Modify DisplayInfo for MasterPageContent collection; remember original layers and
+ // set combined SdrLayerIDSet; set MasterPagePaint flag
+ const SdrLayerIDSet aRememberedLayers(rDisplayInfo.GetProcessLayers());
+ SdrLayerIDSet aPreprocessedLayers(aRememberedLayers);
+ aPreprocessedLayers &= rDescriptor.GetVisibleLayers();
+ rDisplayInfo.SetProcessLayers(aPreprocessedLayers);
+ rDisplayInfo.SetSubContentActive(true);
+
+ // check layer visibility (traditionally was member of layer 1)
+ if(aPreprocessedLayers.IsSet(SdrLayerID(1)))
+ {
+ // hide PageBackground for special DrawModes; historical reasons
+ if(!GetObjectContact().isDrawModeGray() && !GetObjectContact().isDrawModeHighContrast())
+ {
+ // if visible, create the default background primitive sequence
+ static_cast< ViewContactOfMasterPageDescriptor& >(GetViewContact()).getViewIndependentPrimitive2DContainer(rVisitor);
+ }
+ }
+
+ // hide MasterPage content? Test self here for hierarchy
+ if(isPrimitiveVisible(rDisplayInfo))
+ {
+ // get the VOC of the Master-SdrPage and get its object hierarchy
+ ViewContact& rViewContactOfMasterPage(rDescriptor.GetUsedPage().GetViewContact());
+ ViewObjectContact& rVOCOfMasterPage(rViewContactOfMasterPage.GetViewObjectContact(GetObjectContact()));
+
+ rVOCOfMasterPage.getPrimitive2DSequenceHierarchy(rDisplayInfo, xMasterPageSequence);
+ }
+
+ // reset DisplayInfo changes for MasterPage paint
+ rDisplayInfo.SetProcessLayers(aRememberedLayers);
+ rDisplayInfo.SetSubContentActive(false);
+
+ if(!xMasterPageSequence.empty())
+ {
+ // get range of MasterPage sub hierarchy
+ const drawinglayer::geometry::ViewInformation2D& rViewInformation2D(GetObjectContact().getViewInformation2D());
+ basegfx::B2DRange aSubHierarchyRange(xMasterPageSequence.getB2DRange(rViewInformation2D));
+
+ if (rPageFillRange.isInside(aSubHierarchyRange))
+ {
+ // completely inside, just render MasterPage content. Add to target
+ rVisitor.visit(xMasterPageSequence);
+ }
+ else if (rPageFillRange.overlaps(aSubHierarchyRange))
+ {
+ // overlapping, compute common area
+ basegfx::B2DRange aCommonArea(rPageFillRange);
+ aCommonArea.intersect(aSubHierarchyRange);
+
+ // need to create a clip primitive, add clipped list to target
+ const drawinglayer::primitive2d::Primitive2DReference xReference(new drawinglayer::primitive2d::MaskPrimitive2D(
+ basegfx::B2DPolyPolygon(basegfx::utils::createPolygonFromRect(aCommonArea)), std::move(xMasterPageSequence)));
+ rVisitor.visit(xReference);
+ }
+ }
+ }
+} // end of namespace
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/sdr/contact/viewobjectcontactofpageobj.cxx b/svx/source/sdr/contact/viewobjectcontactofpageobj.cxx
new file mode 100644
index 000000000..e3431da91
--- /dev/null
+++ b/svx/source/sdr/contact/viewobjectcontactofpageobj.cxx
@@ -0,0 +1,315 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <vcl/idle.hxx>
+#include <sdr/contact/viewobjectcontactofpageobj.hxx>
+#include <sdr/contact/viewcontactofpageobj.hxx>
+#include <svx/svdopage.hxx>
+#include <svx/sdr/contact/displayinfo.hxx>
+#include <svtools/colorcfg.hxx>
+#include <basegfx/polygon/b2dpolygontools.hxx>
+#include <drawinglayer/primitive2d/PolygonHairlinePrimitive2D.hxx>
+#include <drawinglayer/primitive2d/PolyPolygonColorPrimitive2D.hxx>
+#include <sdr/contact/objectcontactofobjlistpainter.hxx>
+#include <basegfx/matrix/b2dhommatrix.hxx>
+#include <svx/svdpage.hxx>
+#include <svx/unoapi.hxx>
+#include <drawinglayer/primitive2d/pagepreviewprimitive2d.hxx>
+#include <drawinglayer/primitive2d/sdrdecompositiontools2d.hxx>
+#include <vcl/canvastools.hxx>
+
+using namespace com::sun::star;
+
+namespace sdr::contact {
+
+class PagePrimitiveExtractor : public ObjectContactOfPagePainter, public Idle
+{
+private:
+ // the ViewObjectContactOfPageObj using this painter
+ ViewObjectContactOfPageObj& mrViewObjectContactOfPageObj;
+
+public:
+ // basic constructor/destructor
+ explicit PagePrimitiveExtractor(ViewObjectContactOfPageObj& rVOC);
+ virtual ~PagePrimitiveExtractor() override;
+
+ // LazyInvalidate request. Supported here to not automatically
+ // invalidate the second interaction state all the time at the
+ // original OC
+ virtual void setLazyInvalidate(ViewObjectContact& rVOC) override;
+
+ // From baseclass Timer, the timeout call triggered by the LazyInvalidate mechanism
+ virtual void Invoke() final override;
+
+ // get primitive visualization
+ drawinglayer::primitive2d::Primitive2DContainer createPrimitive2DSequenceForPage();
+
+ // Own reaction on changes which will be forwarded to the OC of the owner-VOC
+ virtual void InvalidatePartOfView(const basegfx::B2DRange& rRange) const override;
+
+ // forward access to SdrPageView of ViewObjectContactOfPageObj
+ virtual bool isOutputToPrinter() const override;
+ virtual bool isOutputToRecordingMetaFile() const override;
+ virtual bool isOutputToPDFFile() const override;
+ virtual bool isDrawModeGray() const override;
+ virtual bool isDrawModeHighContrast() const override;
+ virtual SdrPageView* TryToGetSdrPageView() const override;
+ virtual OutputDevice* TryToGetOutputDevice() const override;
+};
+
+PagePrimitiveExtractor::PagePrimitiveExtractor(
+ ViewObjectContactOfPageObj& rVOC)
+: ObjectContactOfPagePainter(rVOC.GetObjectContact()), Idle("svx PagePrimitiveExtractor"),
+ mrViewObjectContactOfPageObj(rVOC)
+{
+ // make this renderer a preview renderer
+ setPreviewRenderer(true);
+
+ // init timer
+ SetPriority(TaskPriority::HIGH_IDLE);
+ Stop();
+}
+
+PagePrimitiveExtractor::~PagePrimitiveExtractor()
+{
+ // execute missing LazyInvalidates and stop timer
+ Invoke();
+}
+
+void PagePrimitiveExtractor::setLazyInvalidate(ViewObjectContact& /*rVOC*/)
+{
+ // do NOT call parent, but remember that something is to do by
+ // starting the LazyInvalidateTimer
+ Start();
+}
+
+// From baseclass Timer, the timeout call triggered by the LazyInvalidate mechanism
+void PagePrimitiveExtractor::Invoke()
+{
+ // stop the timer
+ Stop();
+
+ // invalidate all LazyInvalidate VOCs new situations
+ const sal_uInt32 nVOCCount(getViewObjectContactCount());
+
+ for(sal_uInt32 a(0); a < nVOCCount; a++)
+ {
+ ViewObjectContact* pCandidate = getViewObjectContact(a);
+ pCandidate->triggerLazyInvalidate();
+ }
+}
+
+drawinglayer::primitive2d::Primitive2DContainer PagePrimitiveExtractor::createPrimitive2DSequenceForPage()
+{
+ drawinglayer::primitive2d::Primitive2DContainer xRetval;
+ SdrPage* pStartPage = GetStartPage();
+
+ if(pStartPage)
+ {
+ // update own ViewInformation2D for visualized page
+ const drawinglayer::geometry::ViewInformation2D& rOriginalViewInformation = mrViewObjectContactOfPageObj.GetObjectContact().getViewInformation2D();
+ const drawinglayer::geometry::ViewInformation2D aNewViewInformation2D(
+ rOriginalViewInformation.getObjectTransformation(),
+ rOriginalViewInformation.getViewTransformation(),
+
+ // #i101075# use empty range for page content here to force
+ // the content not to be physically clipped in any way. This
+ // would be possible, but would require the internal transformation
+ // which maps between the page visualisation object and the page
+ // content, including the aspect ratios (for details see in
+ // PagePreviewPrimitive2D::create2DDecomposition)
+ basegfx::B2DRange(),
+
+ GetXDrawPageForSdrPage(pStartPage),
+ 0.0); // no time; page previews are not animated
+ updateViewInformation2D(aNewViewInformation2D);
+
+ // create copy of DisplayInfo to set PagePainting
+ DisplayInfo aDisplayInfo;
+
+ // get page's VOC
+ ViewObjectContact& rDrawPageVOContact = pStartPage->GetViewContact().GetViewObjectContact(*this);
+
+ // get whole Primitive2DContainer
+ rDrawPageVOContact.getPrimitive2DSequenceHierarchy(aDisplayInfo, xRetval);
+ }
+
+ return xRetval;
+}
+
+void PagePrimitiveExtractor::InvalidatePartOfView(const basegfx::B2DRange& rRange) const
+{
+ // an invalidate is called at this view, this needs to be translated to an invalidate
+ // for the using VOC. Coordinates are in page coordinate system.
+ const SdrPage* pStartPage = GetStartPage();
+
+ if(pStartPage && !rRange.isEmpty())
+ {
+ const basegfx::B2DRange aPageRange(0.0, 0.0, static_cast<double>(pStartPage->GetWidth()), static_cast<double>(pStartPage->GetHeight()));
+
+ if(rRange.overlaps(aPageRange))
+ {
+ // if object on the page is inside or overlapping with page, create ActionChanged() for
+ // involved VOC
+ mrViewObjectContactOfPageObj.ActionChanged();
+ }
+ }
+}
+
+// forward access to SdrPageView to VOCOfPageObj
+bool PagePrimitiveExtractor::isOutputToPrinter() const { return mrViewObjectContactOfPageObj.GetObjectContact().isOutputToPrinter(); }
+bool PagePrimitiveExtractor::isOutputToRecordingMetaFile() const { return mrViewObjectContactOfPageObj.GetObjectContact().isOutputToRecordingMetaFile(); }
+bool PagePrimitiveExtractor::isOutputToPDFFile() const { return mrViewObjectContactOfPageObj.GetObjectContact().isOutputToPDFFile(); }
+bool PagePrimitiveExtractor::isDrawModeGray() const { return mrViewObjectContactOfPageObj.GetObjectContact().isDrawModeGray(); }
+bool PagePrimitiveExtractor::isDrawModeHighContrast() const { return mrViewObjectContactOfPageObj.GetObjectContact().isDrawModeHighContrast(); }
+SdrPageView* PagePrimitiveExtractor::TryToGetSdrPageView() const { return mrViewObjectContactOfPageObj.GetObjectContact().TryToGetSdrPageView(); }
+OutputDevice* PagePrimitiveExtractor::TryToGetOutputDevice() const { return mrViewObjectContactOfPageObj.GetObjectContact().TryToGetOutputDevice(); }
+
+void ViewObjectContactOfPageObj::createPrimitive2DSequence(const DisplayInfo& /*rDisplayInfo*/, drawinglayer::primitive2d::Primitive2DDecompositionVisitor& rVisitor) const
+{
+ const SdrPageObj& rPageObject(static_cast< ViewContactOfPageObj& >(GetViewContact()).GetPageObj());
+ const SdrPage* pPage = rPageObject.GetReferencedPage();
+ const svtools::ColorConfig aColorConfig;
+
+ // get PageObject's geometry
+ basegfx::B2DHomMatrix aPageObjectTransform;
+ {
+ const tools::Rectangle aPageObjectModelData(rPageObject.GetLastBoundRect());
+ const basegfx::B2DRange aPageObjectBound = vcl::unotools::b2DRectangleFromRectangle(aPageObjectModelData);
+
+ aPageObjectTransform.set(0, 0, aPageObjectBound.getWidth());
+ aPageObjectTransform.set(1, 1, aPageObjectBound.getHeight());
+ aPageObjectTransform.set(0, 2, aPageObjectBound.getMinX());
+ aPageObjectTransform.set(1, 2, aPageObjectBound.getMinY());
+ }
+
+ // #i102637# add gray frame also when printing and page exists (handout pages)
+ const bool bCreateGrayFrame(!GetObjectContact().isOutputToPrinter() || pPage);
+
+ // get displayed page's content. This is the unscaled page content
+ if(mpExtractor && pPage)
+ {
+ // get displayed page's geometry
+ drawinglayer::primitive2d::Primitive2DContainer xPageContent;
+ const Size aPageSize(pPage->GetSize());
+ const double fPageWidth(aPageSize.getWidth());
+ const double fPageHeight(aPageSize.getHeight());
+
+ // The case that a PageObject contains another PageObject which visualizes the
+ // same page again would lead to a recursion. Limit that recursion depth to one
+ // by using a local static bool
+ static bool bInCreatePrimitive2D(false);
+
+ if(bInCreatePrimitive2D)
+ {
+ // Recursion is possible. Create a replacement primitive
+ xPageContent.resize(2);
+ const Color aDocColor(aColorConfig.GetColorValue(svtools::DOCCOLOR).nColor);
+ svtools::ColorConfigValue aBorderConfig = aColorConfig.GetColorValue(svtools::DOCBOUNDARIES);
+ const Color aBorderColor = aBorderConfig.bIsVisible ? aBorderConfig.nColor : aDocColor;
+ const basegfx::B2DRange aPageBound(0.0, 0.0, fPageWidth, fPageHeight);
+ const basegfx::B2DPolygon aOutline(basegfx::utils::createPolygonFromRect(aPageBound));
+
+ // add replacement fill
+ xPageContent[0] = drawinglayer::primitive2d::Primitive2DReference(
+ new drawinglayer::primitive2d::PolyPolygonColorPrimitive2D(basegfx::B2DPolyPolygon(aOutline), aDocColor.getBColor()));
+
+ // add replacement border
+ xPageContent[1] = drawinglayer::primitive2d::Primitive2DReference(
+ new drawinglayer::primitive2d::PolygonHairlinePrimitive2D(aOutline, aBorderColor.getBColor()));
+ }
+ else
+ {
+ // set recursion flag
+ bInCreatePrimitive2D = true;
+
+ // init extractor, guarantee existence, set page there
+ mpExtractor->SetStartPage(pPage);
+
+ // #i105548# also need to copy the VOCRedirector for sub-content creation
+ mpExtractor->SetViewObjectContactRedirector(GetObjectContact().GetViewObjectContactRedirector());
+
+ // create page content
+ xPageContent = mpExtractor->createPrimitive2DSequenceForPage();
+
+ // #i105548# reset VOCRedirector to not accidentally have a pointer to a
+ // temporary class, so calls to it are avoided safely
+ mpExtractor->SetViewObjectContactRedirector(nullptr);
+
+ // reset recursion flag
+ bInCreatePrimitive2D = false;
+ }
+
+ // prepare retval
+ if(!xPageContent.empty())
+ {
+ const uno::Reference< drawing::XDrawPage > xDrawPage(GetXDrawPageForSdrPage(const_cast< SdrPage*>(pPage)));
+ const drawinglayer::primitive2d::Primitive2DReference xPagePreview(new drawinglayer::primitive2d::PagePreviewPrimitive2D(
+ xDrawPage, aPageObjectTransform, fPageWidth, fPageHeight, std::move(xPageContent)));
+ rVisitor.visit(xPagePreview);
+ }
+ }
+ else if(bCreateGrayFrame)
+ {
+ // #i105146# no content, but frame display. To make hitting the page preview objects
+ // on the handout page more simple, add hidden fill geometry
+ const drawinglayer::primitive2d::Primitive2DReference xFrameHit(
+ drawinglayer::primitive2d::createHiddenGeometryPrimitives2D(
+ aPageObjectTransform));
+ rVisitor.visit(xFrameHit);
+ }
+
+ // add a gray outline frame, except not when printing
+ if(bCreateGrayFrame)
+ {
+ const Color aFrameColor(aColorConfig.GetColorValue(svtools::OBJECTBOUNDARIES).nColor);
+ basegfx::B2DPolygon aOwnOutline(basegfx::utils::createUnitPolygon());
+ aOwnOutline.transform(aPageObjectTransform);
+
+ const drawinglayer::primitive2d::Primitive2DReference xGrayFrame(
+ new drawinglayer::primitive2d::PolygonHairlinePrimitive2D(aOwnOutline, aFrameColor.getBColor()));
+
+ rVisitor.visit(xGrayFrame);
+ }
+}
+
+ViewObjectContactOfPageObj::ViewObjectContactOfPageObj(ObjectContact& rObjectContact, ViewContact& rViewContact)
+: ViewObjectContactOfSdrObj(rObjectContact, rViewContact),
+ mpExtractor(new PagePrimitiveExtractor(*this))
+{
+}
+
+ViewObjectContactOfPageObj::~ViewObjectContactOfPageObj()
+{
+ // delete the helper OC
+ if(mpExtractor)
+ {
+ // remember candidate and reset own pointer to avoid action when createPrimitive2DSequence()
+ // would be called for any reason
+ std::unique_ptr<PagePrimitiveExtractor> pCandidate = std::move(mpExtractor);
+
+ // also reset the StartPage to avoid ActionChanged() forwardings in the
+ // PagePrimitiveExtractor::InvalidatePartOfView() implementation
+ pCandidate->SetStartPage(nullptr);
+ }
+}
+
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/sdr/contact/viewobjectcontactofsdrmediaobj.cxx b/svx/source/sdr/contact/viewobjectcontactofsdrmediaobj.cxx
new file mode 100644
index 000000000..777017472
--- /dev/null
+++ b/svx/source/sdr/contact/viewobjectcontactofsdrmediaobj.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 <config_features.h>
+
+#include <sdr/contact/viewobjectcontactofsdrmediaobj.hxx>
+#include <svx/sdr/contact/viewcontactofsdrmediaobj.hxx>
+#include <svx/sdr/contact/objectcontact.hxx>
+#include <vcl/outdev.hxx>
+#include <vcl/window.hxx>
+#include <avmedia/mediaitem.hxx>
+#include "sdrmediawindow.hxx"
+
+namespace sdr::contact {
+
+ViewObjectContactOfSdrMediaObj::ViewObjectContactOfSdrMediaObj( ObjectContact& rObjectContact,
+ ViewContact& rViewContact,
+ const ::avmedia::MediaItem& rMediaItem ) :
+ ViewObjectContactOfSdrObj( rObjectContact, rViewContact )
+{
+#if HAVE_FEATURE_AVMEDIA
+ vcl::Window* pWindow = getWindow();
+
+ if( pWindow )
+ {
+ mpMediaWindow.reset( new SdrMediaWindow( pWindow, *this ) );
+ mpMediaWindow->hide();
+ executeMediaItem( rMediaItem );
+ }
+#else
+ (void) rMediaItem;
+#endif
+}
+
+ViewObjectContactOfSdrMediaObj::~ViewObjectContactOfSdrMediaObj()
+{
+}
+
+
+vcl::Window* ViewObjectContactOfSdrMediaObj::getWindow() const
+{
+ vcl::Window* pRetval = nullptr;
+
+ const OutputDevice* oPageOutputDev = getPageViewOutputDevice();
+ if( oPageOutputDev )
+ {
+ if(OUTDEV_WINDOW == oPageOutputDev->GetOutDevType())
+ {
+ pRetval = oPageOutputDev->GetOwnerWindow();
+ }
+ }
+
+ return pRetval;
+}
+
+
+Size ViewObjectContactOfSdrMediaObj::getPreferredSize() const
+{
+ Size aRet;
+
+#if HAVE_FEATURE_AVMEDIA
+ if( mpMediaWindow )
+ aRet = mpMediaWindow->getPreferredSize();
+#else
+ aRet = Size(0,0);
+#endif
+
+ return aRet;
+}
+
+void ViewObjectContactOfSdrMediaObj::ActionChanged()
+{
+ ViewObjectContactOfSdrObj::ActionChanged();
+ updateMediaWindow(false);
+}
+
+void ViewObjectContactOfSdrMediaObj::updateMediaWindow(bool bShow) const
+{
+#if HAVE_FEATURE_AVMEDIA
+ if (!mpMediaWindow || (!bShow && !mpMediaWindow->isVisible()))
+ return;
+
+ basegfx::B2DRange aViewRange(getObjectRange());
+ aViewRange.transform(GetObjectContact().getViewInformation2D().getViewTransformation());
+
+ const tools::Rectangle aViewRectangle(
+ static_cast<sal_Int32>(floor(aViewRange.getMinX())), static_cast<sal_Int32>(floor(aViewRange.getMinY())),
+ static_cast<sal_Int32>(ceil(aViewRange.getMaxX())), static_cast<sal_Int32>(ceil(aViewRange.getMaxY())));
+
+ // mpMediaWindow contains a SalObject window and gtk won't accept
+ // the size until after the SalObject widget is shown but if we
+ // show it before setting a size then vcl will detect that the
+ // vcl::Window has no size and make it invisible instead. If we
+ // call setPosSize twice with the same size before and after show
+ // then the second attempt is a no-op as vcl caches the size.
+
+ // so call it initially with a size arbitrarily 1 pixel wider than
+ // we want so we have an initial size to make vcl happy
+ tools::Rectangle aInitialRect(aViewRectangle);
+ aInitialRect.AdjustRight(1);
+ mpMediaWindow->setPosSize(aInitialRect);
+
+ // then make it visible
+ mpMediaWindow->show();
+
+ // set the final desired size which is different to let vcl send it
+ // through to gtk which will now accept it as the underlying
+ // m_pSocket of GtkSalObject::SetPosSize is now visible
+ mpMediaWindow->setPosSize(aViewRectangle);
+#else
+ (void) bShow;
+#endif
+}
+
+void ViewObjectContactOfSdrMediaObj::updateMediaItem( ::avmedia::MediaItem& rItem ) const
+{
+#if HAVE_FEATURE_AVMEDIA
+ if( !mpMediaWindow )
+ return;
+
+ mpMediaWindow->updateMediaItem( rItem );
+
+ // show/hide is now dependent of play state
+ if(avmedia::MediaState::Stop == rItem.getState())
+ {
+ mpMediaWindow->hide();
+ }
+ else
+ {
+ updateMediaWindow(true);
+ }
+#else
+ (void) rItem;
+#endif
+}
+
+
+void ViewObjectContactOfSdrMediaObj::executeMediaItem( const ::avmedia::MediaItem& rItem )
+{
+#if HAVE_FEATURE_AVMEDIA
+ if( mpMediaWindow )
+ {
+ ::avmedia::MediaItem aUpdatedItem;
+
+ mpMediaWindow->executeMediaItem( rItem );
+
+ // query new properties after trying to set the new properties
+ updateMediaItem( aUpdatedItem );
+ static_cast< ViewContactOfSdrMediaObj& >( GetViewContact() ).mediaPropertiesChanged( aUpdatedItem );
+ }
+#else
+ (void) rItem;
+#endif
+}
+
+
+} // end of namespace sdr::contact
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/sdr/contact/viewobjectcontactofsdrobj.cxx b/svx/source/sdr/contact/viewobjectcontactofsdrobj.cxx
new file mode 100644
index 000000000..f87906240
--- /dev/null
+++ b/svx/source/sdr/contact/viewobjectcontactofsdrobj.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 <svx/sdr/contact/viewobjectcontactofsdrobj.hxx>
+#include <svx/sdr/contact/viewcontactofsdrobj.hxx>
+#include <svx/sdr/contact/objectcontact.hxx>
+#include <svx/sdr/contact/displayinfo.hxx>
+#include <svx/sdr/contact/objectcontactofpageview.hxx>
+#include <svx/sdrpagewindow.hxx>
+#include <svx/sdrpaintwindow.hxx>
+#include <svx/svdobj.hxx>
+#include <svx/svdoole2.hxx>
+#include <svx/svdpagv.hxx>
+#include <svx/svdview.hxx>
+#include <vcl/outdev.hxx>
+#include <vcl/canvastools.hxx>
+
+#include <fmobj.hxx>
+
+namespace sdr::contact {
+
+const SdrObject& ViewObjectContactOfSdrObj::getSdrObject() const
+{
+ return static_cast< ViewContactOfSdrObj& >(GetViewContact()).GetSdrObject();
+}
+
+ViewObjectContactOfSdrObj::ViewObjectContactOfSdrObj(ObjectContact& rObjectContact, ViewContact& rViewContact)
+: ViewObjectContact(rObjectContact, rViewContact)
+{
+}
+
+ViewObjectContactOfSdrObj::~ViewObjectContactOfSdrObj()
+{
+}
+
+bool ViewObjectContactOfSdrObj::isPrimitiveVisibleOnAnyLayer(const SdrLayerIDSet& aLayers) const
+{
+ return aLayers.IsSet(getSdrObject().GetLayer());
+}
+
+bool ViewObjectContactOfSdrObj::isPrimitiveVisible(const DisplayInfo& rDisplayInfo) const
+{
+ const SdrObject& rObject = getSdrObject();
+
+ // Test layer visibility
+ if(!isPrimitiveVisibleOnAnyLayer(rDisplayInfo.GetProcessLayers()))
+ {
+ return false;
+ }
+
+ if(GetObjectContact().isOutputToPrinter() )
+ {
+ // Test if print output but not printable
+ if( !rObject.IsPrintable())
+ return false;
+ }
+ else
+ {
+ // test is object is not visible on screen
+ if( !rObject.IsVisible() )
+ return false;
+ }
+
+ // Test for hidden object on MasterPage
+ if(rDisplayInfo.GetSubContentActive() && rObject.IsNotVisibleAsMaster())
+ {
+ return false;
+ }
+
+ // Test for Calc object hiding (for OLE and Graphic it's extra, see there)
+ const SdrPageView* pSdrPageView = GetObjectContact().TryToGetSdrPageView();
+
+ if(pSdrPageView)
+ {
+ const SdrView& rSdrView = pSdrPageView->GetView();
+ const bool bHideOle(rSdrView.getHideOle());
+ const bool bHideChart(rSdrView.getHideChart());
+ const bool bHideDraw(rSdrView.getHideDraw());
+ const bool bHideFormControl(rSdrView.getHideFormControl());
+
+ if(bHideOle || bHideChart || bHideDraw || bHideFormControl)
+ {
+ if(SdrObjKind::OLE2 == rObject.GetObjIdentifier())
+ {
+ if(static_cast<const SdrOle2Obj&>(rObject).IsChart())
+ {
+ // chart
+ if(bHideChart)
+ {
+ return false;
+ }
+ }
+ else
+ {
+ // OLE
+ if(bHideOle)
+ {
+ return false;
+ }
+ }
+ }
+ else if(SdrObjKind::Graphic == rObject.GetObjIdentifier())
+ {
+ // graphic handled like OLE
+ if(bHideOle)
+ {
+ return false;
+ }
+ }
+ else
+ {
+ const bool bIsFormControl = dynamic_cast< const FmFormObj * >( &rObject ) != nullptr;
+ if(bIsFormControl && bHideFormControl)
+ {
+ return false;
+ }
+ // any other draw object
+ if(!bIsFormControl && bHideDraw)
+ {
+ return false;
+ }
+ }
+ }
+ }
+
+ // tdf#91260 check if the object is anchored on a different Writer page
+ // than the one being painted, and if so ignore it (Writer has only one
+ // SdrPage, so the part of the object that should be visible will be
+ // painted on the page where it is anchored)
+ // Note that we cannot check the ViewInformation2D ViewPort for this
+ // because it is only the part of the page that is currently visible.
+ basegfx::B2IPoint const& rAnchor(vcl::unotools::b2IPointFromPoint(getSdrObject().GetAnchorPos()));
+ if (rAnchor.getX() || rAnchor.getY()) // only Writer sets anchor position
+ {
+ if (!rDisplayInfo.GetWriterPageFrame().isEmpty() &&
+ !rDisplayInfo.GetWriterPageFrame().isInside(rAnchor))
+ {
+ return false;
+ }
+ }
+
+ // Check if this object is in the visible range.
+ const drawinglayer::geometry::ViewInformation2D& rViewInfo = GetObjectContact().getViewInformation2D();
+ basegfx::B2DRange aObjRange = GetViewContact().getRange(rViewInfo);
+ if (!aObjRange.isEmpty())
+ {
+ const basegfx::B2DRange& rViewRange = rViewInfo.getViewport();
+ bool bVisible = rViewRange.isEmpty() || rViewRange.overlaps(aObjRange);
+ if (!bVisible)
+ return false;
+ }
+
+ return true;
+}
+
+const OutputDevice* ViewObjectContactOfSdrObj::getPageViewOutputDevice() const
+{
+ ObjectContactOfPageView* pPageViewContact = dynamic_cast< ObjectContactOfPageView* >( &GetObjectContact() );
+ if ( pPageViewContact )
+ {
+ // if the PageWindow has a patched PaintWindow, use the original PaintWindow
+ // this ensures that our control is _not_ re-created just because somebody
+ // (temporarily) changed the window to paint onto.
+ // #i72429# / 2007-02-20 / frank.schoenheit (at) sun.com
+ SdrPageWindow& rPageWindow( pPageViewContact->GetPageWindow() );
+ if ( rPageWindow.GetOriginalPaintWindow() )
+ return &rPageWindow.GetOriginalPaintWindow()->GetOutputDevice();
+
+ return &rPageWindow.GetPaintWindow().GetOutputDevice();
+ }
+ return nullptr;
+}
+
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/sdr/contact/viewobjectcontactofsdrole2obj.cxx b/svx/source/sdr/contact/viewobjectcontactofsdrole2obj.cxx
new file mode 100644
index 000000000..66d1a6fb4
--- /dev/null
+++ b/svx/source/sdr/contact/viewobjectcontactofsdrole2obj.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 <sdr/contact/viewobjectcontactofsdrole2obj.hxx>
+#include <svx/sdr/contact/viewobjectcontactofsdrobj.hxx>
+#include <sdr/contact/viewcontactofsdrole2obj.hxx>
+#include <svx/svdoole2.hxx>
+#include <svx/sdr/contact/objectcontact.hxx>
+#include <svx/svdpagv.hxx>
+#include <svx/svdview.hxx>
+#include <drawinglayer/primitive2d/PolyPolygonHatchPrimitive2D.hxx>
+#include <basegfx/polygon/b2dpolygontools.hxx>
+#include <com/sun/star/embed/EmbedMisc.hpp>
+#include <com/sun/star/embed/EmbedStates.hpp>
+#include <com/sun/star/embed/XEmbeddedObject.hpp>
+#include <basegfx/polygon/b2dpolygon.hxx>
+#include <basegfx/matrix/b2dhommatrix.hxx>
+#include <svtools/embedhlp.hxx>
+
+using namespace com::sun::star;
+
+namespace sdr::contact {
+
+void ViewObjectContactOfSdrOle2Obj::createPrimitive2DSequence(
+ const DisplayInfo& /*rDisplayInfo*/, drawinglayer::primitive2d::Primitive2DDecompositionVisitor& rVisitor) const
+{
+ // override this method to do some things the old SdrOle2Obj::DoPaintObject did.
+ // In the future, some of these may be solved different, but ATM try to stay compatible
+ // with the old behaviour
+ const SdrOle2Obj& rSdrOle2 = static_cast< ViewContactOfSdrOle2Obj& >(GetViewContact()).GetOle2Obj();
+ sal_Int32 nState(-1);
+
+ {
+ const svt::EmbeddedObjectRef& xObjRef = rSdrOle2.getEmbeddedObjectRef();
+ if ( xObjRef.is() )
+ nState = xObjRef->getCurrentState();
+ }
+
+ const bool bIsOutplaceActive(nState == embed::EmbedStates::ACTIVE);
+ const bool bIsInplaceActive((nState == embed::EmbedStates::INPLACE_ACTIVE) || (nState == embed::EmbedStates::UI_ACTIVE));
+ bool bDone(false);
+
+ if (bIsInplaceActive)
+ {
+ if( !GetObjectContact().isOutputToPrinter() && !GetObjectContact().isOutputToRecordingMetaFile() )
+ {
+ //no need to create a primitive sequence here as the OLE object does render itself
+ //in case of charts the superfluous creation of a metafile is strongly performance relevant!
+ bDone = true;
+ }
+ }
+
+ if( !bDone )
+ {
+ //old stuff that should be reworked
+ {
+ //if no replacement image is available load the OLE object
+// if(!rSdrOle2.GetGraphic()) //try to fetch the metafile - this can lead to the actual creation of the metafile what can be extremely expensive (e.g. for big charts)!!! #i101925#
+// {
+// // try to create embedded object
+// rSdrOle2.GetObjRef(); //this loads the OLE object if it is not loaded already
+// }
+ const svt::EmbeddedObjectRef& xObjRef = rSdrOle2.getEmbeddedObjectRef();
+ if(xObjRef.is())
+ {
+ const sal_Int64 nMiscStatus(xObjRef->getStatus(rSdrOle2.GetAspect()));
+
+ // this hack (to change model data during PAINT argh(!)) should be reworked
+ if(!rSdrOle2.IsResizeProtect() && (nMiscStatus & embed::EmbedMisc::EMBED_NEVERRESIZE))
+ {
+ const_cast< SdrOle2Obj* >(&rSdrOle2)->SetResizeProtect(true);
+ }
+
+ SdrPageView* pPageView = GetObjectContact().TryToGetSdrPageView();
+ if(pPageView && (nMiscStatus & embed::EmbedMisc::MS_EMBED_ACTIVATEWHENVISIBLE))
+ {
+ // connect plugin object
+ pPageView->GetView().DoConnect(const_cast< SdrOle2Obj* >(&rSdrOle2));
+ }
+ }
+ }//end old stuff to rework
+
+ // create OLE primitive stuff directly at VC with HC as parameter
+ const ViewContactOfSdrOle2Obj& rVC = static_cast< const ViewContactOfSdrOle2Obj& >(GetViewContact());
+ rVC.createPrimitive2DSequenceWithParameters(rVisitor);
+
+ if(bIsOutplaceActive)
+ {
+ // do not shade when printing or PDF exporting
+ if(!GetObjectContact().isOutputToPrinter() && !GetObjectContact().isOutputToRecordingMetaFile())
+ {
+ // get object transformation
+ const basegfx::B2DHomMatrix aObjectMatrix(static_cast< ViewContactOfSdrOle2Obj& >(GetViewContact()).createObjectTransform());
+
+ // shade the representation if the object is activated outplace
+ basegfx::B2DPolygon aObjectOutline(basegfx::utils::createUnitPolygon());
+ aObjectOutline.transform(aObjectMatrix);
+
+ // Use a FillHatchPrimitive2D with necessary attributes
+ const drawinglayer::attribute::FillHatchAttribute aFillHatch(
+ drawinglayer::attribute::HatchStyle::Single, // single hatch
+ 125.0, // 1.25 mm
+ basegfx::deg2rad(45.0), // 45 degree diagonal
+ COL_BLACK.getBColor(), // black color
+ 3, // same default as VCL, a minimum of three discrete units (pixels) offset
+ false); // no filling
+
+ const drawinglayer::primitive2d::Primitive2DReference xReference(new drawinglayer::primitive2d::PolyPolygonHatchPrimitive2D(
+ basegfx::B2DPolyPolygon(aObjectOutline),
+ COL_BLACK.getBColor(),
+ aFillHatch));
+
+ rVisitor.visit(xReference);
+ }
+ }
+
+ }
+}
+
+ViewObjectContactOfSdrOle2Obj::ViewObjectContactOfSdrOle2Obj(ObjectContact& rObjectContact, ViewContact& rViewContact)
+: ViewObjectContactOfSdrObj(rObjectContact, rViewContact)
+{
+}
+
+ViewObjectContactOfSdrOle2Obj::~ViewObjectContactOfSdrOle2Obj()
+{
+}
+
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/sdr/contact/viewobjectcontactofsdrpage.cxx b/svx/source/sdr/contact/viewobjectcontactofsdrpage.cxx
new file mode 100644
index 000000000..fe9855f4c
--- /dev/null
+++ b/svx/source/sdr/contact/viewobjectcontactofsdrpage.cxx
@@ -0,0 +1,579 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+
+#include <sdr/contact/viewobjectcontactofsdrpage.hxx>
+#include <svx/sdr/contact/displayinfo.hxx>
+#include <sdr/contact/viewcontactofsdrpage.hxx>
+#include <svx/svdview.hxx>
+#include <svx/svdpage.hxx>
+#include <svx/svdpagv.hxx>
+#include <svx/sdr/contact/objectcontact.hxx>
+#include <drawinglayer/primitive2d/backgroundcolorprimitive2d.hxx>
+#include <basegfx/polygon/b2dpolygontools.hxx>
+#include <drawinglayer/primitive2d/PolyPolygonColorPrimitive2D.hxx>
+#include <drawinglayer/primitive2d/gridprimitive2d.hxx>
+#include <drawinglayer/primitive2d/helplineprimitive2d.hxx>
+#include <basegfx/polygon/b2dpolygon.hxx>
+#include <sdr/primitive2d/sdrprimitivetools.hxx>
+
+using namespace com::sun::star;
+
+namespace sdr::contact {
+
+const SdrPage& ViewObjectContactOfPageSubObject::getPage() const
+{
+ return static_cast< ViewContactOfPageSubObject& >(GetViewContact()).getPage();
+}
+
+ViewObjectContactOfPageSubObject::ViewObjectContactOfPageSubObject(ObjectContact& rObjectContact, ViewContact& rViewContact)
+: ViewObjectContact(rObjectContact, rViewContact)
+{
+}
+
+ViewObjectContactOfPageSubObject::~ViewObjectContactOfPageSubObject()
+{
+}
+
+bool ViewObjectContactOfPageSubObject::isPrimitiveVisible(const DisplayInfo& rDisplayInfo) const
+{
+ if(rDisplayInfo.GetSubContentActive())
+ {
+ return false;
+ }
+
+ if(rDisplayInfo.GetControlLayerProcessingActive())
+ {
+ return false;
+ }
+
+ if(!rDisplayInfo.GetPageProcessingActive())
+ {
+ return false;
+ }
+
+ if(GetObjectContact().isOutputToPrinter())
+ {
+ return false;
+ }
+
+ if(!GetObjectContact().TryToGetSdrPageView())
+ {
+ return false;
+ }
+
+ return true;
+}
+
+bool ViewObjectContactOfPageSubObject::isPrimitiveGhosted(const DisplayInfo& /*rDisplayInfo*/) const
+{
+ // suppress ghosted for page parts
+ return false;
+}
+
+ViewObjectContactOfPageBackground::ViewObjectContactOfPageBackground(ObjectContact& rObjectContact, ViewContact& rViewContact)
+: ViewObjectContactOfPageSubObject(rObjectContact, rViewContact)
+{
+}
+
+ViewObjectContactOfPageBackground::~ViewObjectContactOfPageBackground()
+{
+}
+
+bool ViewObjectContactOfPageBackground::isPrimitiveVisible(const DisplayInfo& rDisplayInfo) const
+{
+ if(!ViewObjectContactOfPageSubObject::isPrimitiveVisible(rDisplayInfo))
+ {
+ return false;
+ }
+
+ // no page background for preview renderers
+ if(GetObjectContact().IsPreviewRenderer())
+ {
+ return false;
+ }
+
+ return true;
+}
+
+void ViewObjectContactOfPageBackground::createPrimitive2DSequence(const DisplayInfo& /*rDisplayInfo*/, drawinglayer::primitive2d::Primitive2DDecompositionVisitor& rVisitor) const
+{
+ // Initialize background. Dependent of IsPageVisible, use ApplicationBackgroundColor or ApplicationDocumentColor. Most
+ // old renderers for export (html, pdf, gallery, ...) set the page to not visible (SetPageVisible(false)). They expect the
+ // given OutputDevice to be initialized with the ApplicationDocumentColor then.
+ const SdrPageView* pPageView = GetObjectContact().TryToGetSdrPageView();
+
+ if(pPageView)
+ {
+ const SdrView& rView = pPageView->GetView();
+ Color aInitColor;
+
+ if(rView.IsPageVisible())
+ {
+ aInitColor = pPageView->GetApplicationBackgroundColor();
+ }
+ else
+ {
+ aInitColor = pPageView->GetApplicationDocumentColor();
+
+ if(COL_AUTO == aInitColor)
+ {
+ const svtools::ColorConfig aColorConfig;
+ aInitColor = aColorConfig.GetColorValue(svtools::DOCCOLOR).nColor;
+ }
+ }
+
+ // init background with InitColor
+ const basegfx::BColor aRGBColor(aInitColor.getBColor());
+ rVisitor.visit(new drawinglayer::primitive2d::BackgroundColorPrimitive2D(aRGBColor, (255 - aInitColor.GetAlpha()) / 255.0));
+ }
+}
+
+ViewObjectContactOfMasterPage::ViewObjectContactOfMasterPage(ObjectContact& rObjectContact, ViewContact& rViewContact)
+: ViewObjectContactOfPageSubObject(rObjectContact, rViewContact)
+{
+}
+
+ViewObjectContactOfMasterPage::~ViewObjectContactOfMasterPage()
+{
+}
+
+bool ViewObjectContactOfMasterPage::isPrimitiveVisible(const DisplayInfo& rDisplayInfo) const
+{
+ if(!ViewObjectContactOfPageSubObject::isPrimitiveVisible(rDisplayInfo))
+ {
+ return false;
+ }
+
+ // this object is only used for MasterPages. When not the MasterPage is
+ // displayed as a page, but another page is using it as sub-object, the
+ // geometry needs to be hidden
+ if(rDisplayInfo.GetSubContentActive())
+ {
+ return false;
+ }
+
+ return true;
+}
+
+ViewObjectContactOfPageFill::ViewObjectContactOfPageFill(ObjectContact& rObjectContact, ViewContact& rViewContact)
+: ViewObjectContactOfPageSubObject(rObjectContact, rViewContact)
+{
+}
+
+ViewObjectContactOfPageFill::~ViewObjectContactOfPageFill()
+{
+}
+
+bool ViewObjectContactOfPageFill::isPrimitiveVisible(const DisplayInfo& rDisplayInfo) const
+{
+ if(!ViewObjectContactOfPageSubObject::isPrimitiveVisible(rDisplayInfo))
+ {
+ return false;
+ }
+
+ SdrPageView* pSdrPageView = GetObjectContact().TryToGetSdrPageView();
+
+ if(!pSdrPageView)
+ {
+ return false;
+ }
+
+ if(!pSdrPageView->GetView().IsPageVisible())
+ {
+ return false;
+ }
+
+ return true;
+}
+
+void ViewObjectContactOfPageFill::createPrimitive2DSequence(const DisplayInfo& /*rDisplayInfo*/, drawinglayer::primitive2d::Primitive2DDecompositionVisitor& rVisitor) const
+{
+ const SdrPageView* pPageView = GetObjectContact().TryToGetSdrPageView();
+
+ if(pPageView)
+ {
+ const SdrPage& rPage = getPage();
+
+ const basegfx::B2DRange aPageFillRange(0.0, 0.0, static_cast<double>(rPage.GetWidth()), static_cast<double>(rPage.GetHeight()));
+ const basegfx::B2DPolygon aPageFillPolygon(basegfx::utils::createPolygonFromRect(aPageFillRange));
+ Color aPageFillColor;
+
+ if(pPageView->GetApplicationDocumentColor() != COL_AUTO)
+ {
+ aPageFillColor = pPageView->GetApplicationDocumentColor();
+ }
+ else
+ {
+ const svtools::ColorConfig aColorConfig;
+ aPageFillColor = aColorConfig.GetColorValue(svtools::DOCCOLOR).nColor;
+ }
+
+ // create and add primitive
+ const basegfx::BColor aRGBColor(aPageFillColor.getBColor());
+ rVisitor.visit(new drawinglayer::primitive2d::PolyPolygonColorPrimitive2D(basegfx::B2DPolyPolygon(aPageFillPolygon), aRGBColor));
+ }
+}
+
+ViewObjectContactOfPageShadow::ViewObjectContactOfPageShadow(ObjectContact& rObjectContact, ViewContact& rViewContact)
+: ViewObjectContactOfPageSubObject(rObjectContact, rViewContact)
+{
+}
+
+ViewObjectContactOfPageShadow::~ViewObjectContactOfPageShadow()
+{
+}
+
+bool ViewObjectContactOfPageShadow::isPrimitiveVisible(const DisplayInfo& rDisplayInfo) const
+{
+ if(!ViewObjectContactOfPageSubObject::isPrimitiveVisible(rDisplayInfo))
+ {
+ return false;
+ }
+
+ SdrPageView* pSdrPageView = GetObjectContact().TryToGetSdrPageView();
+
+ if(!pSdrPageView)
+ {
+ return false;
+ }
+
+ if(!pSdrPageView->GetView().IsPageVisible())
+ {
+ return false;
+ }
+
+ if(!pSdrPageView->GetView().IsPageShadowVisible())
+ {
+ return false;
+ }
+
+ // no page shadow for preview renderers
+ if(GetObjectContact().IsPreviewRenderer())
+ {
+ return false;
+ }
+
+ // no page shadow for high contrast mode
+ if(GetObjectContact().isDrawModeHighContrast())
+ {
+ return false;
+ }
+
+ return true;
+}
+
+ViewObjectContactOfOuterPageBorder::ViewObjectContactOfOuterPageBorder(ObjectContact& rObjectContact, ViewContact& rViewContact)
+: ViewObjectContactOfPageSubObject(rObjectContact, rViewContact)
+{
+}
+
+ViewObjectContactOfOuterPageBorder::~ViewObjectContactOfOuterPageBorder()
+{
+}
+
+bool ViewObjectContactOfOuterPageBorder::isPrimitiveVisible(const DisplayInfo& rDisplayInfo) const
+{
+ if(!ViewObjectContactOfPageSubObject::isPrimitiveVisible(rDisplayInfo))
+ {
+ return false;
+ }
+
+ SdrPageView* pSdrPageView = GetObjectContact().TryToGetSdrPageView();
+
+ if(!pSdrPageView)
+ {
+ return false;
+ }
+
+ const SdrView& rView = pSdrPageView->GetView();
+
+ return rView.IsPageVisible() || !rView.IsPageBorderVisible();
+}
+
+ViewObjectContactOfInnerPageBorder::ViewObjectContactOfInnerPageBorder(ObjectContact& rObjectContact, ViewContact& rViewContact)
+: ViewObjectContactOfPageSubObject(rObjectContact, rViewContact)
+{
+}
+
+ViewObjectContactOfInnerPageBorder::~ViewObjectContactOfInnerPageBorder()
+{
+}
+
+bool ViewObjectContactOfInnerPageBorder::isPrimitiveVisible(const DisplayInfo& rDisplayInfo) const
+{
+ if(!ViewObjectContactOfPageSubObject::isPrimitiveVisible(rDisplayInfo))
+ {
+ return false;
+ }
+
+ SdrPageView* pSdrPageView = GetObjectContact().TryToGetSdrPageView();
+
+ if(!pSdrPageView)
+ {
+ return false;
+ }
+
+ if(!pSdrPageView->GetView().IsBordVisible())
+ {
+ return false;
+ }
+
+ const SdrPage& rPage = getPage();
+
+ if(!rPage.GetLeftBorder() && !rPage.GetUpperBorder() && !rPage.GetRightBorder() && !rPage.GetLowerBorder())
+ {
+ return false;
+ }
+
+ // no inner page border for preview renderers
+ if(GetObjectContact().IsPreviewRenderer())
+ {
+ return false;
+ }
+
+ return true;
+}
+
+ViewObjectContactOfPageHierarchy::ViewObjectContactOfPageHierarchy(ObjectContact& rObjectContact, ViewContact& rViewContact)
+: ViewObjectContactOfPageSubObject(rObjectContact, rViewContact)
+{
+}
+
+ViewObjectContactOfPageHierarchy::~ViewObjectContactOfPageHierarchy()
+{
+}
+
+void ViewObjectContactOfPageHierarchy::getPrimitive2DSequenceHierarchy(DisplayInfo& rDisplayInfo, drawinglayer::primitive2d::Primitive2DDecompositionVisitor& rVisitor) const
+{
+ // process local sub-hierarchy
+ const sal_uInt32 nSubHierarchyCount(GetViewContact().GetObjectCount());
+
+ if(!nSubHierarchyCount)
+ return;
+
+ getPrimitive2DSequenceSubHierarchy(rDisplayInfo, rVisitor);
+}
+
+ViewObjectContactOfPageGrid::ViewObjectContactOfPageGrid(ObjectContact& rObjectContact, ViewContact& rViewContact)
+: ViewObjectContactOfPageSubObject(rObjectContact, rViewContact)
+{
+}
+
+ViewObjectContactOfPageGrid::~ViewObjectContactOfPageGrid()
+{
+}
+
+bool ViewObjectContactOfPageGrid::isPrimitiveVisible(const DisplayInfo& rDisplayInfo) const
+{
+ if(!ViewObjectContactOfPageSubObject::isPrimitiveVisible(rDisplayInfo))
+ {
+ return false;
+ }
+
+ SdrPageView* pSdrPageView = GetObjectContact().TryToGetSdrPageView();
+
+ if(!pSdrPageView)
+ {
+ return false;
+ }
+
+ const SdrView& rView = pSdrPageView->GetView();
+
+ if(!rView.IsGridVisible())
+ {
+ return false;
+ }
+
+ // no page grid for preview renderers
+ if(GetObjectContact().IsPreviewRenderer())
+ {
+ return false;
+ }
+
+ if(static_cast< ViewContactOfGrid& >(GetViewContact()).getFront() != rView.IsGridFront())
+ {
+ return false;
+ }
+
+ return true;
+}
+
+void ViewObjectContactOfPageGrid::createPrimitive2DSequence(const DisplayInfo& /*rDisplayInfo*/, drawinglayer::primitive2d::Primitive2DDecompositionVisitor& rVisitor) const
+{
+ const SdrPageView* pPageView = GetObjectContact().TryToGetSdrPageView();
+
+ if(pPageView)
+ {
+ const SdrView& rView = pPageView->GetView();
+ const SdrPage& rPage = getPage();
+ const Color aGridColor(rView.GetGridColor());
+ const basegfx::BColor aRGBGridColor(aGridColor.getBColor());
+
+ basegfx::B2DHomMatrix aGridMatrix;
+ aGridMatrix.set(0, 0, static_cast<double>(rPage.GetWidth() - (rPage.GetRightBorder() + rPage.GetLeftBorder())));
+ aGridMatrix.set(1, 1, static_cast<double>(rPage.GetHeight() - (rPage.GetLowerBorder() + rPage.GetUpperBorder())));
+ aGridMatrix.set(0, 2, static_cast<double>(rPage.GetLeftBorder()));
+ aGridMatrix.set(1, 2, static_cast<double>(rPage.GetUpperBorder()));
+
+ const Size aRaw(rView.GetGridCoarse());
+ const Size aFine(rView.GetGridFine());
+ const double fWidthX(aRaw.getWidth());
+ const double fWidthY(aRaw.getHeight());
+ const sal_uInt32 nSubdivisionsX(aFine.getWidth() ? aRaw.getWidth() / aFine.getWidth() : 0);
+ const sal_uInt32 nSubdivisionsY(aFine.getHeight() ? aRaw.getHeight() / aFine.getHeight() : 0);
+
+ rVisitor.visit(new drawinglayer::primitive2d::GridPrimitive2D(
+ aGridMatrix, fWidthX, fWidthY, 10.0, 3.0, nSubdivisionsX, nSubdivisionsY, aRGBGridColor,
+ drawinglayer::primitive2d::createDefaultCross_3x3(aRGBGridColor)));
+ }
+}
+
+ViewObjectContactOfPageHelplines::ViewObjectContactOfPageHelplines(ObjectContact& rObjectContact, ViewContact& rViewContact)
+: ViewObjectContactOfPageSubObject(rObjectContact, rViewContact)
+{
+}
+
+ViewObjectContactOfPageHelplines::~ViewObjectContactOfPageHelplines()
+{
+}
+
+bool ViewObjectContactOfPageHelplines::isPrimitiveVisible(const DisplayInfo& rDisplayInfo) const
+{
+ if(!ViewObjectContactOfPageSubObject::isPrimitiveVisible(rDisplayInfo))
+ {
+ return false;
+ }
+
+ SdrPageView* pSdrPageView = GetObjectContact().TryToGetSdrPageView();
+
+ if(!pSdrPageView)
+ {
+ return false;
+ }
+
+ const SdrView& rView = pSdrPageView->GetView();
+
+ if(!rView.IsHlplVisible())
+ {
+ return false;
+ }
+
+ // no helplines for preview renderers
+ if(GetObjectContact().IsPreviewRenderer())
+ {
+ return false;
+ }
+
+ if(static_cast< ViewContactOfHelplines& >(GetViewContact()).getFront() != rView.IsHlplFront())
+ {
+ return false;
+ }
+
+ return true;
+}
+
+void ViewObjectContactOfPageHelplines::createPrimitive2DSequence(const DisplayInfo& /*rDisplayInfo*/, drawinglayer::primitive2d::Primitive2DDecompositionVisitor& rVisitor) const
+{
+ const SdrPageView* pPageView = GetObjectContact().TryToGetSdrPageView();
+
+ if(pPageView)
+ {
+ const SdrHelpLineList& rHelpLineList = pPageView->GetHelpLines();
+ const sal_uInt32 nCount(rHelpLineList.GetCount());
+
+ if(nCount)
+ {
+ const basegfx::BColor aRGBColorA(1.0, 1.0, 1.0);
+ const basegfx::BColor aRGBColorB(0.0, 0.0, 0.0);
+
+ for(sal_uInt32 a(0); a < nCount; a++)
+ {
+ const SdrHelpLine& rHelpLine = rHelpLineList[static_cast<sal_uInt16>(a)];
+ const basegfx::B2DPoint aPosition(static_cast<double>(rHelpLine.GetPos().X()), static_cast<double>(rHelpLine.GetPos().Y()));
+ const double fDiscreteDashLength(4.0);
+
+ switch(rHelpLine.GetKind())
+ {
+ default : // SdrHelpLineKind::Point
+ {
+ rVisitor.visit(new drawinglayer::primitive2d::HelplinePrimitive2D(
+ aPosition, basegfx::B2DVector(1.0, 0.0), drawinglayer::primitive2d::HelplineStyle2D::Point,
+ aRGBColorA, aRGBColorB, fDiscreteDashLength));
+ break;
+ }
+ case SdrHelpLineKind::Vertical :
+ {
+ rVisitor.visit(new drawinglayer::primitive2d::HelplinePrimitive2D(
+ aPosition, basegfx::B2DVector(0.0, 1.0), drawinglayer::primitive2d::HelplineStyle2D::Line,
+ aRGBColorA, aRGBColorB, fDiscreteDashLength));
+ break;
+ }
+ case SdrHelpLineKind::Horizontal :
+ {
+ rVisitor.visit(new drawinglayer::primitive2d::HelplinePrimitive2D(
+ aPosition, basegfx::B2DVector(1.0, 0.0), drawinglayer::primitive2d::HelplineStyle2D::Line,
+ aRGBColorA, aRGBColorB, fDiscreteDashLength));
+ break;
+ }
+ }
+ }
+ }
+ }
+}
+
+ViewObjectContactOfSdrPage::ViewObjectContactOfSdrPage(ObjectContact& rObjectContact, ViewContact& rViewContact)
+: ViewObjectContact(rObjectContact, rViewContact)
+{
+}
+
+ViewObjectContactOfSdrPage::~ViewObjectContactOfSdrPage()
+{
+}
+
+void ViewObjectContactOfSdrPage::getPrimitive2DSequenceHierarchy(DisplayInfo& rDisplayInfo, drawinglayer::primitive2d::Primitive2DDecompositionVisitor& rVisitor) const
+{
+ // process local sub-hierarchy
+ const sal_uInt32 nSubHierarchyCount(GetViewContact().GetObjectCount());
+
+ if(!nSubHierarchyCount)
+ return;
+
+ const bool bDoGhostedDisplaying(
+ GetObjectContact().DoVisualizeEnteredGroup()
+ && !GetObjectContact().isOutputToPrinter()
+ && GetObjectContact().getActiveViewContact() == &GetViewContact());
+
+ if(bDoGhostedDisplaying)
+ {
+ rDisplayInfo.ClearGhostedDrawMode();
+ }
+
+ // visit object hierarchy
+ getPrimitive2DSequenceSubHierarchy(rDisplayInfo, rVisitor);
+
+ if(bDoGhostedDisplaying)
+ {
+ rDisplayInfo.SetGhostedDrawMode();
+ }
+}
+
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/sdr/contact/viewobjectcontactofunocontrol.cxx b/svx/source/sdr/contact/viewobjectcontactofunocontrol.cxx
new file mode 100644
index 000000000..5ca5a32d2
--- /dev/null
+++ b/svx/source/sdr/contact/viewobjectcontactofunocontrol.cxx
@@ -0,0 +1,1777 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+
+#include <sdr/contact/viewobjectcontactofunocontrol.hxx>
+#include <sdr/contact/viewcontactofunocontrol.hxx>
+#include <svx/sdr/contact/displayinfo.hxx>
+#include <svx/sdr/contact/objectcontactofpageview.hxx>
+#include <svx/sdr/primitive2d/svx_primitivetypes2d.hxx>
+#include <svx/svdouno.hxx>
+#include <svx/svdpagv.hxx>
+#include <svx/svdview.hxx>
+#include <svx/sdrpagewindow.hxx>
+
+#include <com/sun/star/awt/XControl.hpp>
+#include <com/sun/star/awt/XControlModel.hpp>
+#include <com/sun/star/awt/XControlContainer.hpp>
+#include <com/sun/star/awt/XWindow2.hpp>
+#include <com/sun/star/awt/PosSize.hpp>
+#include <com/sun/star/awt/XView.hpp>
+#include <com/sun/star/beans/XPropertySet.hpp>
+#include <com/sun/star/lang/XComponent.hpp>
+#include <com/sun/star/awt/InvalidateStyle.hpp>
+#include <com/sun/star/util/XModeChangeListener.hpp>
+#include <com/sun/star/util/XModeChangeBroadcaster.hpp>
+#include <com/sun/star/uno/XComponentContext.hpp>
+#include <com/sun/star/container/XContainerListener.hpp>
+#include <com/sun/star/container/XContainer.hpp>
+
+#include <vcl/canvastools.hxx>
+#include <vcl/svapp.hxx>
+#include <vcl/window.hxx>
+#include <comphelper/processfactory.hxx>
+#include <comphelper/scopeguard.hxx>
+#include <cppuhelper/implbase.hxx>
+#include <toolkit/helper/vclunohelper.hxx>
+#include <tools/diagnose_ex.h>
+#include <tools/debug.hxx>
+#include <basegfx/matrix/b2dhommatrix.hxx>
+#include <basegfx/matrix/b2dhommatrixtools.hxx>
+#include <drawinglayer/primitive2d/controlprimitive2d.hxx>
+
+/*
+
+Form controls (more precise: UNO Controls) in the drawing layer are ... prone to breakage, since they have some
+specialities which the drawing layer currently doesn't capture too well. In particular, having a living VCL
+window as child of the document window, and coupling this Window to a drawing layer object, makes things
+difficult sometimes.
+
+Below is a list of issues which existed in the past. Whenever you change code here, you're encouraged to
+verify those issues are still fixed. (Whenever you have some additional time, you're encouraged to write
+an automatic test for one or more of those issues for which this is possible :)
+
+https://bz.apache.org/ooo/show_bug.cgi?id=105992
+zooming documents containing (alive) form controls improperly positions the controls
+
+https://bz.apache.org/ooo/show_bug.cgi?id=104362
+crash when copy a control
+
+https://bz.apache.org/ooo/show_bug.cgi?id=104544
+Gridcontrol duplicated after design view on/off
+
+https://bz.apache.org/ooo/show_bug.cgi?id=102089
+print preview shows control elements with property printable=false
+
+https://bz.apache.org/ooo/show_bug.cgi?id=102090
+problem with setVisible on TextControl
+
+https://bz.apache.org/ooo/show_bug.cgi?id=103138
+loop when insert a control in draw
+
+https://bz.apache.org/ooo/show_bug.cgi?id=101398
+initially-displaying a document with many controls is very slow
+
+https://bz.apache.org/ooo/show_bug.cgi?id=72429
+repaint error in form wizard in bugdoc database
+
+https://bz.apache.org/ooo/show_bug.cgi?id=72694
+form control artifacts when scrolling a text fast
+
+*/
+
+
+namespace sdr::contact {
+
+
+ using namespace ::com::sun::star::awt::InvalidateStyle;
+ using ::com::sun::star::uno::Reference;
+ using ::com::sun::star::uno::XInterface;
+ using ::com::sun::star::uno::UNO_QUERY;
+ using ::com::sun::star::uno::UNO_QUERY_THROW;
+ using ::com::sun::star::uno::Exception;
+ using ::com::sun::star::uno::RuntimeException;
+ using ::com::sun::star::awt::XControl;
+ using ::com::sun::star::awt::XControlModel;
+ using ::com::sun::star::awt::XControlContainer;
+ using ::com::sun::star::awt::XWindow2;
+ using ::com::sun::star::awt::XWindowListener;
+ using ::com::sun::star::awt::PosSize::POSSIZE;
+ using ::com::sun::star::awt::XView;
+ using ::com::sun::star::awt::WindowEvent;
+ using ::com::sun::star::beans::XPropertySet;
+ using ::com::sun::star::lang::XComponent;
+ using ::com::sun::star::awt::XWindowPeer;
+ using ::com::sun::star::beans::XPropertyChangeListener;
+ using ::com::sun::star::util::XModeChangeListener;
+ using ::com::sun::star::util::XModeChangeBroadcaster;
+ using ::com::sun::star::util::ModeChangeEvent;
+ using ::com::sun::star::lang::EventObject;
+ using ::com::sun::star::beans::PropertyChangeEvent;
+ using ::com::sun::star::container::XContainerListener;
+ using ::com::sun::star::container::XContainer;
+ using ::com::sun::star::container::ContainerEvent;
+ using ::com::sun::star::uno::Any;
+
+ namespace {
+
+ class ControlHolder
+ {
+ private:
+ Reference< XControl > m_xControl;
+ Reference< XWindow2 > m_xControlWindow;
+ Reference< XView > m_xControlView;
+
+ public:
+ ControlHolder()
+ {
+ }
+
+ explicit ControlHolder( const Reference< XControl >& _rxControl )
+ {
+ *this = _rxControl;
+ }
+
+ ControlHolder& operator=( const Reference< XControl >& _rxControl )
+ {
+ clear();
+
+ m_xControl = _rxControl;
+ if ( m_xControl.is() )
+ {
+ m_xControlWindow.set( m_xControl, UNO_QUERY );
+ m_xControlView.set( m_xControl, UNO_QUERY );
+ if ( !m_xControlWindow.is() || !m_xControlView.is() )
+ {
+ OSL_FAIL( "ControlHolder::operator=: invalid XControl, missing required interfaces!" );
+ clear();
+ }
+ }
+
+ return *this;
+ }
+
+ public:
+ bool is() const { return m_xControl.is() && m_xControlWindow.is() && m_xControlView.is(); }
+ void clear() { m_xControl.clear(); m_xControlWindow.clear(); m_xControlView.clear(); }
+
+ // delegators for the methods of the UNO interfaces
+ // Note all those will crash if called for a NULL object.
+ bool isDesignMode() const { return m_xControl->isDesignMode(); }
+ void setDesignMode( const bool _bDesign ) const { m_xControl->setDesignMode( _bDesign ); }
+ bool isVisible() const { return m_xControlWindow->isVisible(); }
+ void setVisible( const bool _bVisible ) const { m_xControlWindow->setVisible( _bVisible ); }
+ Reference< XControlModel >
+ getModel() const { return m_xControl->getModel(); }
+ void setModel( const Reference< XControlModel >& _m ) const { m_xControl->setModel( _m ); }
+
+ void addWindowListener( const Reference< XWindowListener >& _l ) const { m_xControlWindow->addWindowListener( _l ); }
+ void removeWindowListener( const Reference< XWindowListener >& _l ) const { m_xControlWindow->removeWindowListener( _l ); }
+ void setPosSize( const tools::Rectangle& _rPosSize ) const;
+ tools::Rectangle
+ getPosSize() const;
+ void setZoom( const ::basegfx::B2DVector& _rScale ) const;
+ ::basegfx::B2DVector
+ getZoom() const;
+
+ void invalidate() const;
+
+ public:
+ const Reference< XControl >& getControl() const { return m_xControl; }
+ };
+
+ bool operator==( const ControlHolder& _rControl, const Reference< XInterface >& _rxCompare )
+ {
+ return _rControl.getControl() == _rxCompare;
+ }
+
+ bool operator==( const ControlHolder& _rControl, const Any& _rxCompare )
+ {
+ return _rControl == Reference< XInterface >( _rxCompare, UNO_QUERY );
+ }
+
+ }
+
+ void ControlHolder::setPosSize( const tools::Rectangle& _rPosSize ) const
+ {
+ // no check whether we're valid, this is the responsibility of the caller
+
+ // don't call setPosSize when pos/size did not change #i104181#
+ ::tools::Rectangle aCurrentRect( getPosSize() );
+ if ( aCurrentRect != _rPosSize )
+ {
+ m_xControlWindow->setPosSize(
+ _rPosSize.Left(), _rPosSize.Top(), _rPosSize.GetWidth(), _rPosSize.GetHeight(),
+ POSSIZE
+ );
+ }
+ }
+
+
+ ::tools::Rectangle ControlHolder::getPosSize() const
+ {
+ // no check whether we're valid, this is the responsibility of the caller
+ return VCLUnoHelper::ConvertToVCLRect( m_xControlWindow->getPosSize() );
+ }
+
+
+ void ControlHolder::setZoom( const ::basegfx::B2DVector& _rScale ) const
+ {
+ // no check whether we're valid, this is the responsibility of the caller
+ m_xControlView->setZoom( static_cast<float>(_rScale.getX()), static_cast<float>(_rScale.getY()) );
+ }
+
+
+ void ControlHolder::invalidate() const
+ {
+ Reference< XWindowPeer > xPeer( m_xControl->getPeer() );
+ if ( xPeer.is() )
+ {
+ VclPtr<vcl::Window> pWindow = VCLUnoHelper::GetWindow( xPeer );
+ OSL_ENSURE( pWindow, "ControlHolder::invalidate: no implementation access!" );
+ if ( pWindow )
+ pWindow->Invalidate();
+ }
+ }
+
+
+ ::basegfx::B2DVector ControlHolder::getZoom() const
+ {
+ // no check whether we're valid, this is the responsibility of the caller
+
+ // Argh. Why does XView have a setZoom only, but not a getZoom?
+ VclPtr<vcl::Window> pWindow = VCLUnoHelper::GetWindow( m_xControl->getPeer() );
+ OSL_ENSURE( pWindow, "ControlHolder::getZoom: no implementation access!" );
+
+ ::basegfx::B2DVector aZoom( 1, 1 );
+ if ( pWindow )
+ {
+ const Fraction& rZoom( pWindow->GetZoom() );
+ aZoom.setX( static_cast<double>(rZoom) );
+ aZoom.setY( static_cast<double>(rZoom) );
+ }
+ return aZoom;
+ }
+
+ namespace UnoControlContactHelper {
+
+ /** positions a control, and sets its zoom mode, using a given transformation and output device
+ */
+ static void adjustControlGeometry_throw( const ControlHolder& _rControl, const tools::Rectangle& _rLogicBoundingRect,
+ const basegfx::B2DHomMatrix& _rViewTransformation, const ::basegfx::B2DHomMatrix& _rZoomLevelNormalization )
+ {
+ OSL_PRECOND( _rControl.is(), "UnoControlContactHelper::adjustControlGeometry_throw: illegal control!" );
+ if ( !_rControl.is() )
+ return;
+
+ #if OSL_DEBUG_LEVEL > 0
+ ::basegfx::B2DTuple aViewScale, aViewTranslate;
+ double nViewRotate(0), nViewShearX(0);
+ _rViewTransformation.decompose( aViewScale, aViewTranslate, nViewRotate, nViewShearX );
+
+ ::basegfx::B2DTuple aZoomScale, aZoomTranslate;
+ double nZoomRotate(0), nZoomShearX(0);
+ _rZoomLevelNormalization.decompose( aZoomScale, aZoomTranslate, nZoomRotate, nZoomShearX );
+ #endif
+
+ // transform the logic bound rect, using the view transformation, to pixel coordinates
+ ::basegfx::B2DPoint aTopLeft( _rLogicBoundingRect.Left(), _rLogicBoundingRect.Top() );
+ aTopLeft *= _rViewTransformation;
+ ::basegfx::B2DPoint aBottomRight( _rLogicBoundingRect.Right(), _rLogicBoundingRect.Bottom() );
+ aBottomRight *= _rViewTransformation;
+
+ const tools::Rectangle aPaintRectPixel(static_cast<tools::Long>(std::round(aTopLeft.getX())),
+ static_cast<tools::Long>(std::round(aTopLeft.getY())),
+ static_cast<tools::Long>(std::round(aBottomRight.getX())),
+ static_cast<tools::Long>(std::round(aBottomRight.getY())));
+ _rControl.setPosSize( aPaintRectPixel );
+
+ // determine the scale from the current view transformation, and the normalization matrix
+ ::basegfx::B2DHomMatrix aObtainResolutionDependentScale( _rViewTransformation * _rZoomLevelNormalization );
+ ::basegfx::B2DVector aScale, aTranslate;
+ double fRotate, fShearX;
+ aObtainResolutionDependentScale.decompose( aScale, aTranslate, fRotate, fShearX );
+ _rControl.setZoom( aScale );
+ }
+
+ /** disposes the given control
+ */
+ static void disposeAndClearControl_nothrow( ControlHolder& _rControl )
+ {
+ try
+ {
+ Reference< XComponent > xControlComp = _rControl.getControl();
+ if ( xControlComp.is() )
+ xControlComp->dispose();
+ }
+ catch( const Exception& )
+ {
+ DBG_UNHANDLED_EXCEPTION("svx");
+ }
+ _rControl.clear();
+ }
+
+ }
+
+ namespace {
+
+ /** interface encapsulating access to an SdrPageView, stripped down to the methods we really need
+ */
+ class IPageViewAccess
+ {
+ public:
+ /** determines whether the view is currently in design mode
+ */
+ virtual bool isDesignMode() const = 0;
+
+ /** retrieves the control container for a given output device
+ */
+ virtual Reference< XControlContainer >
+ getControlContainer( const OutputDevice& _rDevice ) const = 0;
+
+ /** determines whether a given layer is visible
+ */
+ virtual bool isLayerVisible( SdrLayerID _nLayerID ) const = 0;
+
+ protected:
+ ~IPageViewAccess() {}
+ };
+
+ /** is a ->IPageViewAccess implementation based on a real ->SdrPageView instance
+ */
+ class SdrPageViewAccess : public IPageViewAccess
+ {
+ const SdrPageView& m_rPageView;
+ public:
+ explicit SdrPageViewAccess( const SdrPageView& _rPageView ) : m_rPageView( _rPageView ) { }
+
+ virtual ~SdrPageViewAccess() {}
+
+ virtual bool isDesignMode() const override;
+ virtual Reference< XControlContainer >
+ getControlContainer( const OutputDevice& _rDevice ) const override;
+ virtual bool isLayerVisible( SdrLayerID _nLayerID ) const override;
+ };
+
+ }
+
+ bool SdrPageViewAccess::isDesignMode() const
+ {
+ return m_rPageView.GetView().IsDesignMode();
+ }
+
+
+ Reference< XControlContainer > SdrPageViewAccess::getControlContainer( const OutputDevice& _rDevice ) const
+ {
+ Reference< XControlContainer > xControlContainer = m_rPageView.GetControlContainer( _rDevice );
+ DBG_ASSERT( xControlContainer.is() || nullptr == m_rPageView.FindPageWindow( _rDevice ),
+ "SdrPageViewAccess::getControlContainer: the output device is known, but there is no control container for it?" );
+ return xControlContainer;
+ }
+
+
+ bool SdrPageViewAccess::isLayerVisible( SdrLayerID _nLayerID ) const
+ {
+ return m_rPageView.GetVisibleLayers().IsSet( _nLayerID );
+ }
+
+ namespace {
+
+ /** is a ->IPageViewAccess implementation which can be used to create an invisible control for
+ an arbitrary window
+ */
+ class InvisibleControlViewAccess : public IPageViewAccess
+ {
+ private:
+ Reference< XControlContainer >& m_rControlContainer;
+ public:
+ explicit InvisibleControlViewAccess( Reference< XControlContainer >& _inout_ControlContainer )
+ :m_rControlContainer( _inout_ControlContainer )
+ {
+ }
+
+ virtual ~InvisibleControlViewAccess() {}
+
+ virtual bool isDesignMode() const override;
+ virtual Reference< XControlContainer >
+ getControlContainer( const OutputDevice& _rDevice ) const override;
+ virtual bool isLayerVisible( SdrLayerID _nLayerID ) const override;
+ };
+
+ }
+
+ bool InvisibleControlViewAccess::isDesignMode() const
+ {
+ return true;
+ }
+
+
+ Reference< XControlContainer > InvisibleControlViewAccess::getControlContainer( const OutputDevice& _rDevice ) const
+ {
+ if ( !m_rControlContainer.is() )
+ {
+ const vcl::Window* pWindow = _rDevice.GetOwnerWindow();
+ OSL_ENSURE( pWindow, "InvisibleControlViewAccess::getControlContainer: expected to be called for a window only!" );
+ if ( pWindow )
+ m_rControlContainer = VCLUnoHelper::CreateControlContainer( const_cast< vcl::Window* >( pWindow ) );
+ }
+ return m_rControlContainer;
+ }
+
+
+ bool InvisibleControlViewAccess::isLayerVisible( SdrLayerID /*_nLayerID*/ ) const
+ {
+ return false;
+ }
+
+ namespace {
+
+ //= DummyPageViewAccess
+
+ /** is a ->IPageViewAccess implementation which can be used to create a control for an arbitrary
+ non-Window device
+
+ The implementation will report the "PageView" as being in design mode, all layers to be visible,
+ and will not return any ControlContainer, so all control container related features (notifications etc)
+ are not available.
+ */
+ class DummyPageViewAccess : public IPageViewAccess
+ {
+ public:
+ DummyPageViewAccess()
+ {
+ }
+
+ virtual ~DummyPageViewAccess() {}
+
+ virtual bool isDesignMode() const override;
+ virtual Reference< XControlContainer >
+ getControlContainer( const OutputDevice& _rDevice ) const override;
+ virtual bool isLayerVisible( SdrLayerID _nLayerID ) const override;
+ };
+
+ }
+
+ bool DummyPageViewAccess::isDesignMode() const
+ {
+ return true;
+ }
+
+
+ Reference< XControlContainer > DummyPageViewAccess::getControlContainer( const OutputDevice& /*_rDevice*/ ) const
+ {
+ return nullptr;
+ }
+
+
+ bool DummyPageViewAccess::isLayerVisible( SdrLayerID /*_nLayerID*/ ) const
+ {
+ return true;
+ }
+
+
+ //= ViewObjectContactOfUnoControl_Impl
+
+ typedef ::cppu::WeakImplHelper < XWindowListener
+ , XPropertyChangeListener
+ , XContainerListener
+ , XModeChangeListener
+ > ViewObjectContactOfUnoControl_Impl_Base;
+
+ class ViewObjectContactOfUnoControl_Impl:
+ public ViewObjectContactOfUnoControl_Impl_Base
+ {
+ private:
+ // tdf#41935 note that access to members is protected with SolarMutex;
+ // the class previously had its own mutex but that is prone to deadlock
+
+ /// the instance whose IMPL we are
+ ViewObjectContactOfUnoControl* m_pAntiImpl;
+
+ /// are we currently inside impl_ensureControl_nothrow?
+ bool m_bCreatingControl;
+
+ /// the control we're responsible for
+ ControlHolder m_aControl;
+
+ /// the ControlContainer where we inserted our control
+ Reference< XContainer > m_xContainer;
+
+ /// the output device for which the control was created
+ VclPtr<OutputDevice> m_pOutputDeviceForWindow;
+
+ /// flag indicating whether the control is currently visible
+ bool m_bControlIsVisible;
+
+ /// are we currently listening at a design mode control?
+ bool m_bIsDesignModeListening;
+
+ enum ViewControlMode
+ {
+ eDesign,
+ eAlive,
+ eUnknown
+ };
+ /// is the control currently in design mode?
+ mutable ViewControlMode m_eControlDesignMode;
+
+ ::basegfx::B2DHomMatrix m_aZoomLevelNormalization;
+
+ public:
+ explicit ViewObjectContactOfUnoControl_Impl( ViewObjectContactOfUnoControl* _pAntiImpl );
+ ViewObjectContactOfUnoControl_Impl(const ViewObjectContactOfUnoControl_Impl&) = delete;
+ ViewObjectContactOfUnoControl_Impl& operator=(const ViewObjectContactOfUnoControl_Impl&) = delete;
+
+ /** disposes the instance, which is nonfunctional afterwards
+ */
+ void dispose();
+
+ /** determines whether the instance is disposed
+ */
+ bool isDisposed() const { return impl_isDisposed_nofail(); }
+
+ /** returns the SdrUnoObject associated with the ViewContact
+
+ @precond
+ We're not disposed.
+ */
+ SdrUnoObj* getUnoObject() const;
+
+ /** ensures that we have an ->XControl
+
+ Must only be called if a control is needed when no DisplayInfo is present, yet.
+
+ For creating a control, an ->OutputDevice is needed, and an ->SdrPageView. Both will be obtained
+ from a ->ObjectContactOfPageView. So, if our (anti-impl's) object contact is not a ->ObjectContactOfPageView,
+ this method fill fail.
+
+ Failure of this method will be reported via an assertion in a non-product version.
+ */
+ void ensureControl( const basegfx::B2DHomMatrix* _pInitialViewTransformationOrNULL );
+
+ /** returns our XControl, if it already has been created
+
+ If you want to ensure that the control exists before accessing it, use ->ensureControl
+ */
+ const ControlHolder&
+ getExistentControl() const { return m_aControl; }
+
+ bool
+ hasControl() const { return m_aControl.is(); }
+
+ /** positions our XControl according to the geometry settings in the SdrUnoObj, modified by the given
+ transformation, and sets proper zoom settings according to our device
+
+ @precond
+ ->m_pOutputDeviceForWindow and ->m_aControl are not <NULL/>
+ */
+ void positionAndZoomControl( const basegfx::B2DHomMatrix& _rViewTransformation ) const;
+
+ /** determines whether or not our control is printable
+
+ Effectively, this method returns the value of the "Printable" property
+ of the control's model. If we have no control, <FALSE/> is returned.
+ */
+ bool isPrintableControl() const;
+
+ /** sets the design mode on the control, or at least remembers the flag for the
+ time the control is created
+ */
+ void setControlDesignMode( bool _bDesignMode ) const;
+
+ /** determines whether our control is currently visible
+ @nofail
+ */
+ bool isControlVisible() const { return m_bControlIsVisible; }
+
+ /// creates an XControl for the given device and SdrUnoObj
+ static bool
+ createControlForDevice(
+ IPageViewAccess const & _rPageView,
+ const OutputDevice& _rDevice,
+ const SdrUnoObj& _rUnoObject,
+ const basegfx::B2DHomMatrix& _rInitialViewTransformation,
+ const basegfx::B2DHomMatrix& _rInitialZoomNormalization,
+ ControlHolder& _out_rControl
+ );
+
+ const ViewContactOfUnoControl&
+ getViewContact() const
+ {
+ ENSURE_OR_THROW( !impl_isDisposed_nofail(), "already disposed" );
+ return static_cast< const ViewContactOfUnoControl& >( m_pAntiImpl->GetViewContact() );
+ }
+
+ protected:
+ virtual ~ViewObjectContactOfUnoControl_Impl() override;
+
+ // XEventListener
+ virtual void SAL_CALL disposing( const EventObject& Source ) override;
+
+ // XWindowListener
+ virtual void SAL_CALL windowResized( const WindowEvent& e ) override;
+ virtual void SAL_CALL windowMoved( const WindowEvent& e ) override;
+ virtual void SAL_CALL windowShown( const EventObject& e ) override;
+ virtual void SAL_CALL windowHidden( const EventObject& e ) override;
+
+ // XPropertyChangeListener
+ virtual void SAL_CALL propertyChange( const PropertyChangeEvent& evt ) override;
+
+ // XModeChangeListener
+ virtual void SAL_CALL modeChanged( const ModeChangeEvent& _rSource ) override;
+
+ // XContainerListener
+ virtual void SAL_CALL elementInserted( const css::container::ContainerEvent& Event ) override;
+ virtual void SAL_CALL elementRemoved( const css::container::ContainerEvent& Event ) override;
+ virtual void SAL_CALL elementReplaced( const css::container::ContainerEvent& Event ) override;
+
+ private:
+ /** retrieves the SdrPageView which our associated SdrPageViewWindow belongs to
+
+ @param out_rpPageView
+ a reference to a pointer holding, upon return, the desired SdrPageView
+
+ @return
+ <TRUE/> if and only if a ->SdrPageView could be obtained
+
+ @precond
+ We really belong to an SdrPageViewWindow. Perhaps (I'm not sure ATM :)
+ there are instance for which this might not be true, but those instances
+ should never have a need to call this method.
+
+ @precond
+ We're not disposed.
+
+ @postcond
+ The method expects success, if it returns with <FALSE/>, this will have been
+ asserted.
+
+ @nothrow
+ */
+ bool impl_getPageView_nothrow( SdrPageView*& _out_rpPageView );
+
+ /** adjusts the control visibility so it respects its layer's visibility
+
+ @precond
+ ->m_aControl is not <NULL/>
+
+ @precond
+ We're not disposed.
+
+ @precond
+ We really belong to an SdrPageViewWindow. There are instance for which this
+ might not be true, but those instances should never have a need to call
+ this method.
+ */
+ void impl_adjustControlVisibilityToLayerVisibility_throw();
+
+ /** adjusts the control visibility so it respects its layer's visibility
+
+ The control must never be visible if it's in design mode.
+ In alive mode, it must be visibility if and only it's on a visible layer.
+
+ @param _rxControl
+ the control whose visibility is to be adjusted
+
+ @param _rPageView
+ provides access to the attributes of the SdrPageView which the control finally belongs to
+
+ @param _rUnoObject
+ our SdrUnoObj
+
+ @param _bIsCurrentlyVisible
+ determines whether the control is currently visible. Note that this is only a shortcut for
+ querying _rxControl for the XWindow2 interface, and calling isVisible at this interface.
+ This shortcut has been chosen since the caller usually already has this information.
+ If _bForce is <TRUE/>, _bIsCurrentlyVisible is ignored.
+
+ @param _bForce
+ set to <TRUE/> if you want to force a ->XWindow::setVisible call,
+ no matter if the control visibility is already correct
+
+ @precond
+ We're not disposed.
+ */
+ static void impl_adjustControlVisibilityToLayerVisibility_throw( const ControlHolder& _rxControl, const SdrUnoObj& _rUnoObject,
+ IPageViewAccess const & _rPageView, bool _bIsCurrentlyVisible, bool _bForce );
+
+ /** starts or stops listening at various aspects of our control
+
+ @precond
+ ->m_aControl is not <NULL/>
+ */
+ void impl_switchControlListening_nothrow( bool _bStart );
+
+ /** starts or stops listening at our control container
+
+ @precond
+ ->m_xContainer is not <NULL/>
+ */
+ void impl_switchContainerListening_nothrow( bool _bStart );
+
+ /** starts or stops listening at the control for design-mode relevant facets
+ */
+ void impl_switchDesignModeListening_nothrow( bool _bStart );
+
+ /** starts or stops listening for all properties at our control
+
+ @param _bStart
+ determines whether to start or to stop listening
+
+ @precond
+ ->m_aControl is not <NULL/>
+ */
+ void impl_switchPropertyListening_nothrow( bool _bStart );
+
+ /** disposes the instance
+ @param _bAlsoDisposeControl
+ determines whether the XControl should be disposed, too
+ */
+ void impl_dispose_nothrow( bool _bAlsoDisposeControl );
+
+ /** determines whether the instance is disposed
+ @nofail
+ */
+ bool impl_isDisposed_nofail() const { return m_pAntiImpl == nullptr; }
+
+ /** determines whether the control currently is in design mode
+
+ @precond
+ The design mode must already be known. It is known when we first had access to
+ an SdrPageView (which carries this flag), or somebody explicitly set it from
+ outside.
+ */
+ bool impl_isControlDesignMode_nothrow() const
+ {
+ DBG_ASSERT( m_eControlDesignMode != eUnknown, "ViewObjectContactOfUnoControl_Impl::impl_isControlDesignMode_nothrow: mode is still unknown!" );
+ return m_eControlDesignMode == eDesign;
+ }
+
+ /** ensures that we have a control for the given PageView/OutputDevice
+ */
+ bool impl_ensureControl_nothrow(
+ IPageViewAccess const & _rPageView,
+ const OutputDevice& _rDevice,
+ const basegfx::B2DHomMatrix& _rInitialViewTransformation
+ );
+
+ const OutputDevice& impl_getOutputDevice_throw() const;
+ };
+
+ namespace {
+
+ class LazyControlCreationPrimitive2D : public ::drawinglayer::primitive2d::BufferedDecompositionPrimitive2D
+ {
+ private:
+ typedef ::drawinglayer::primitive2d::BufferedDecompositionPrimitive2D BufferedDecompositionPrimitive2D;
+
+ protected:
+ virtual void
+ get2DDecomposition(
+ ::drawinglayer::primitive2d::Primitive2DDecompositionVisitor& rVisitor,
+ const ::drawinglayer::geometry::ViewInformation2D& rViewInformation
+ ) const override;
+
+ virtual void create2DDecomposition(
+ ::drawinglayer::primitive2d::Primitive2DContainer& rContainer,
+ const ::drawinglayer::geometry::ViewInformation2D& rViewInformation
+ ) const override;
+
+ virtual ::basegfx::B2DRange
+ getB2DRange(
+ const ::drawinglayer::geometry::ViewInformation2D& rViewInformation
+ ) const override;
+
+ public:
+ explicit LazyControlCreationPrimitive2D( const ::rtl::Reference< ViewObjectContactOfUnoControl_Impl >& _pVOCImpl )
+ :m_pVOCImpl( _pVOCImpl )
+ {
+ ENSURE_OR_THROW( m_pVOCImpl.is(), "Illegal argument." );
+ getTransformation( m_pVOCImpl->getViewContact(), m_aTransformation );
+ }
+
+ virtual bool operator==(const BasePrimitive2D& rPrimitive) const override;
+
+ // declare unique ID for this primitive class
+ virtual sal_uInt32 getPrimitive2DID() const override;
+
+ static void getTransformation( const ViewContactOfUnoControl& _rVOC, ::basegfx::B2DHomMatrix& _out_Transformation );
+
+ private:
+ void impl_positionAndZoomControl( const ::drawinglayer::geometry::ViewInformation2D& _rViewInformation ) const
+ {
+ if ( !_rViewInformation.getViewport().isEmpty() )
+ m_pVOCImpl->positionAndZoomControl( _rViewInformation.getObjectToViewTransformation() );
+ }
+
+ private:
+ ::rtl::Reference< ViewObjectContactOfUnoControl_Impl > m_pVOCImpl;
+ /** The geometry is part of the identity of a primitive, so we cannot calculate it on demand
+ (since the data the calculation is based on might have changed then), but need to calc
+ it at construction time, and remember it.
+ */
+ ::basegfx::B2DHomMatrix m_aTransformation;
+ };
+
+ }
+
+ ViewObjectContactOfUnoControl_Impl::ViewObjectContactOfUnoControl_Impl( ViewObjectContactOfUnoControl* _pAntiImpl )
+ :m_pAntiImpl( _pAntiImpl )
+ ,m_bCreatingControl( false )
+ ,m_pOutputDeviceForWindow( nullptr )
+ ,m_bControlIsVisible( false )
+ ,m_bIsDesignModeListening( false )
+ ,m_eControlDesignMode( eUnknown )
+ {
+ DBG_ASSERT( m_pAntiImpl, "ViewObjectContactOfUnoControl_Impl::ViewObjectContactOfUnoControl_Impl: invalid AntiImpl!" );
+
+ const OutputDevice& rPageViewDevice( impl_getOutputDevice_throw() );
+ m_aZoomLevelNormalization = rPageViewDevice.GetInverseViewTransformation();
+
+ #if OSL_DEBUG_LEVEL > 0
+ ::basegfx::B2DVector aScale, aTranslate;
+ double fRotate, fShearX;
+ m_aZoomLevelNormalization.decompose( aScale, aTranslate, fRotate, fShearX );
+ #endif
+
+ ::basegfx::B2DHomMatrix aScaleNormalization;
+ const MapMode& aCurrentDeviceMapMode( rPageViewDevice.GetMapMode() );
+ aScaleNormalization.set( 0, 0, static_cast<double>(aCurrentDeviceMapMode.GetScaleX()) );
+ aScaleNormalization.set( 1, 1, static_cast<double>(aCurrentDeviceMapMode.GetScaleY()) );
+ m_aZoomLevelNormalization *= aScaleNormalization;
+
+ #if OSL_DEBUG_LEVEL > 0
+ m_aZoomLevelNormalization.decompose( aScale, aTranslate, fRotate, fShearX );
+ #endif
+ }
+
+
+ ViewObjectContactOfUnoControl_Impl::~ViewObjectContactOfUnoControl_Impl()
+ {
+ if ( !impl_isDisposed_nofail() )
+ {
+ acquire();
+ dispose();
+ }
+
+ }
+
+
+ void ViewObjectContactOfUnoControl_Impl::impl_dispose_nothrow( bool _bAlsoDisposeControl )
+ {
+ if ( impl_isDisposed_nofail() )
+ return;
+
+ if ( m_aControl.is() )
+ impl_switchControlListening_nothrow( false );
+
+ if ( m_xContainer.is() )
+ impl_switchContainerListening_nothrow( false );
+
+ // dispose the control
+ if ( _bAlsoDisposeControl )
+ UnoControlContactHelper::disposeAndClearControl_nothrow( m_aControl );
+
+ m_aControl.clear();
+ m_xContainer.clear();
+ m_pOutputDeviceForWindow = nullptr;
+ m_bControlIsVisible = false;
+
+ m_pAntiImpl = nullptr;
+ }
+
+
+ void ViewObjectContactOfUnoControl_Impl::dispose()
+ {
+ SolarMutexGuard aSolarGuard;
+ impl_dispose_nothrow( true );
+ }
+
+
+ SdrUnoObj* ViewObjectContactOfUnoControl_Impl::getUnoObject() const
+ {
+ OSL_PRECOND( !impl_isDisposed_nofail(), "ViewObjectContactOfUnoControl_Impl::getUnoObject: already disposed()" );
+ if ( impl_isDisposed_nofail() )
+ return nullptr;
+ auto pRet = dynamic_cast< SdrUnoObj* >( m_pAntiImpl->GetViewContact().TryToGetSdrObject() );
+ DBG_ASSERT( pRet || !m_pAntiImpl->GetViewContact().TryToGetSdrObject(),
+ "ViewObjectContactOfUnoControl_Impl::getUnoObject: invalid SdrObject!" );
+ return pRet;
+ }
+
+
+ void ViewObjectContactOfUnoControl_Impl::positionAndZoomControl( const basegfx::B2DHomMatrix& _rViewTransformation ) const
+ {
+ OSL_PRECOND( m_aControl.is(), "ViewObjectContactOfUnoControl_Impl::positionAndZoomControl: no output device or no control!" );
+ if ( !m_aControl.is() )
+ return;
+
+ try
+ {
+ SdrUnoObj* pUnoObject = getUnoObject();
+ if ( pUnoObject )
+ {
+ const tools::Rectangle aRect( pUnoObject->GetLogicRect() );
+ UnoControlContactHelper::adjustControlGeometry_throw( m_aControl, aRect, _rViewTransformation, m_aZoomLevelNormalization );
+ }
+ else
+ OSL_FAIL( "ViewObjectContactOfUnoControl_Impl::positionAndZoomControl: no SdrUnoObj!" );
+ }
+ catch( const Exception& )
+ {
+ DBG_UNHANDLED_EXCEPTION("svx");
+ }
+ }
+
+
+ void ViewObjectContactOfUnoControl_Impl::ensureControl( const basegfx::B2DHomMatrix* _pInitialViewTransformationOrNULL )
+ {
+ OSL_PRECOND( !impl_isDisposed_nofail(), "ViewObjectContactOfUnoControl_Impl::ensureControl: already disposed()" );
+ if ( impl_isDisposed_nofail() )
+ return;
+
+ ObjectContactOfPageView* pPageViewContact = dynamic_cast< ObjectContactOfPageView* >( &m_pAntiImpl->GetObjectContact() );
+ if ( pPageViewContact )
+ {
+ SdrPageViewAccess aPVAccess( pPageViewContact->GetPageWindow().GetPageView() );
+ const OutputDevice& rDevice( *m_pAntiImpl->getPageViewOutputDevice() );
+ impl_ensureControl_nothrow(
+ aPVAccess,
+ rDevice,
+ _pInitialViewTransformationOrNULL ? *_pInitialViewTransformationOrNULL : rDevice.GetViewTransformation()
+ );
+ return;
+ }
+
+ DummyPageViewAccess aNoPageView;
+ const OutputDevice& rDevice( impl_getOutputDevice_throw() );
+ impl_ensureControl_nothrow(
+ aNoPageView,
+ rDevice,
+ _pInitialViewTransformationOrNULL ? *_pInitialViewTransformationOrNULL : rDevice.GetViewTransformation()
+ );
+ }
+
+
+ const OutputDevice& ViewObjectContactOfUnoControl_Impl::impl_getOutputDevice_throw() const
+ {
+ // do not use ObjectContact::TryToGetOutputDevice, it would not care for the PageWindow's
+ // OriginalPaintWindow
+ const OutputDevice* oPageOutputDev = m_pAntiImpl->getPageViewOutputDevice();
+ if( oPageOutputDev )
+ return *oPageOutputDev;
+
+ const OutputDevice* pDevice = m_pAntiImpl->GetObjectContact().TryToGetOutputDevice();
+ ENSURE_OR_THROW( pDevice, "no output device -> no control" );
+ return *pDevice;
+ }
+
+
+ namespace
+ {
+ void lcl_resetFlag( bool& rbFlag )
+ {
+ rbFlag = false;
+ }
+ }
+
+
+ bool ViewObjectContactOfUnoControl_Impl::impl_ensureControl_nothrow( IPageViewAccess const & _rPageView, const OutputDevice& _rDevice,
+ const basegfx::B2DHomMatrix& _rInitialViewTransformation )
+ {
+ if ( m_bCreatingControl )
+ {
+ OSL_FAIL( "ViewObjectContactOfUnoControl_Impl::impl_ensureControl_nothrow: reentrance is not really good here!" );
+ // We once had a situation where this was called reentrantly, which lead to all kind of strange effects. All
+ // those affected the grid control, which is the only control so far which is visible in design mode (and
+ // not only in alive mode).
+ // Creating the control triggered a Window::Update on some of its child windows, which triggered a
+ // Paint on parent of the grid control (e.g. the SwEditWin), which triggered a reentrant call to this method,
+ // which it is not really prepared for.
+
+ // /me thinks that re-entrance should be caught on a higher level, i.e. the Drawing Layer should not allow
+ // reentrant paint requests. For the moment, until /me can discuss this with AW, catch it here. #i104544#
+ return false;
+ }
+
+ m_bCreatingControl = true;
+ ::comphelper::ScopeGuard aGuard([&] () { lcl_resetFlag(m_bCreatingControl); });
+
+ if ( m_aControl.is() )
+ {
+ if ( m_pOutputDeviceForWindow.get() == &_rDevice )
+ return true;
+
+ // Somebody requested a control for a new device, which means either of
+ // - our PageView's paint window changed since we were last here
+ // - we don't belong to a page view, and are simply painted onto different devices
+ // The first sounds strange (doesn't it?), the second means we could perhaps
+ // optimize this in the future - there is no need to re-create the control every time,
+ // is it? #i74523#
+ if ( m_xContainer.is() )
+ impl_switchContainerListening_nothrow( false );
+ impl_switchControlListening_nothrow( false );
+ UnoControlContactHelper::disposeAndClearControl_nothrow( m_aControl );
+ }
+
+ SdrUnoObj* pUnoObject = getUnoObject();
+ if ( !pUnoObject )
+ return false;
+
+ ControlHolder aControl;
+ if ( !createControlForDevice( _rPageView, _rDevice, *pUnoObject, _rInitialViewTransformation, m_aZoomLevelNormalization, aControl ) )
+ return false;
+
+ m_pOutputDeviceForWindow = const_cast< OutputDevice * >( &_rDevice );
+ m_aControl = aControl;
+ m_xContainer.set(_rPageView.getControlContainer( _rDevice ), css::uno::UNO_QUERY);
+ DBG_ASSERT( ( m_xContainer.is() // either have a XControlContainer
+ || ( ( !_rPageView.getControlContainer( _rDevice ).is() ) // or don't have any container,
+ && ( _rDevice.GetOwnerWindow() == nullptr ) // which is allowed for non-Window instances only
+ )
+ ),
+ "ViewObjectContactOfUnoControl_Impl::impl_ensureControl_nothrow: no XContainer at the ControlContainer!" );
+
+ try
+ {
+ m_eControlDesignMode = m_aControl.isDesignMode() ? eDesign : eAlive;
+ m_bControlIsVisible = m_aControl.isVisible();
+ }
+ catch( const Exception& )
+ {
+ DBG_UNHANDLED_EXCEPTION("svx");
+ }
+
+ // start listening at all aspects of the control which are interesting to us ...
+ impl_switchControlListening_nothrow( true );
+
+ // start listening at the control container, in case somebody tampers with our control
+ if ( m_xContainer.is() )
+ impl_switchContainerListening_nothrow( true );
+
+ return m_aControl.is();
+ }
+
+
+ bool ViewObjectContactOfUnoControl_Impl::createControlForDevice( IPageViewAccess const & _rPageView,
+ const OutputDevice& _rDevice, const SdrUnoObj& _rUnoObject, const basegfx::B2DHomMatrix& _rInitialViewTransformation,
+ const basegfx::B2DHomMatrix& _rInitialZoomNormalization, ControlHolder& _out_rControl )
+ {
+ _out_rControl.clear();
+
+ const Reference< XControlModel >& xControlModel( _rUnoObject.GetUnoControlModel() );
+ DBG_ASSERT( xControlModel.is(), "ViewObjectContactOfUnoControl_Impl::createControlForDevice: no control model at the SdrUnoObject!?" );
+ if ( !xControlModel.is() )
+ return false;
+
+ bool bSuccess = false;
+ try
+ {
+ const OUString& sControlServiceName( _rUnoObject.GetUnoControlTypeName() );
+
+ Reference< css::uno::XComponentContext > xContext = ::comphelper::getProcessComponentContext();
+ _out_rControl = Reference<XControl>( xContext->getServiceManager()->createInstanceWithContext(sControlServiceName, xContext), UNO_QUERY_THROW );
+
+ // knit the model and the control
+ _out_rControl.setModel( xControlModel );
+ const tools::Rectangle aRect( _rUnoObject.GetLogicRect() );
+
+ // proper geometry
+ UnoControlContactHelper::adjustControlGeometry_throw(
+ _out_rControl,
+ aRect,
+ _rInitialViewTransformation,
+ _rInitialZoomNormalization
+ );
+
+ // set design mode before peer is created,
+ // this is also needed for accessibility
+ _out_rControl.setDesignMode( _rPageView.isDesignMode() );
+
+ // adjust the initial visibility according to the visibility of the layer
+ impl_adjustControlVisibilityToLayerVisibility_throw( _out_rControl, _rUnoObject, _rPageView, false, true );
+
+ // add the control to the respective control container
+ // do this last
+ Reference< XControlContainer > xControlContainer( _rPageView.getControlContainer( _rDevice ) );
+ if ( xControlContainer.is() )
+ xControlContainer->addControl( sControlServiceName, _out_rControl.getControl() );
+
+ bSuccess = true;
+ }
+ catch( const Exception& )
+ {
+ DBG_UNHANDLED_EXCEPTION("svx");
+ }
+
+ if ( !bSuccess )
+ {
+ // delete the control which might have been created already
+ UnoControlContactHelper::disposeAndClearControl_nothrow( _out_rControl );
+ }
+
+ return _out_rControl.is();
+ }
+
+
+ bool ViewObjectContactOfUnoControl_Impl::impl_getPageView_nothrow( SdrPageView*& _out_rpPageView )
+ {
+ OSL_PRECOND( !impl_isDisposed_nofail(), "ViewObjectContactOfUnoControl_Impl::impl_getPageView_nothrow: already disposed!" );
+
+ _out_rpPageView = nullptr;
+ if ( impl_isDisposed_nofail() )
+ return false;
+
+ ObjectContactOfPageView* pPageViewContact = dynamic_cast< ObjectContactOfPageView* >( &m_pAntiImpl->GetObjectContact() );
+ if ( pPageViewContact )
+ _out_rpPageView = &pPageViewContact->GetPageWindow().GetPageView();
+
+ DBG_ASSERT( _out_rpPageView != nullptr, "ViewObjectContactOfUnoControl_Impl::impl_getPageView_nothrow: this method is expected to always have success!" );
+ return ( _out_rpPageView != nullptr );
+ }
+
+
+ void ViewObjectContactOfUnoControl_Impl::impl_adjustControlVisibilityToLayerVisibility_throw()
+ {
+ OSL_PRECOND( m_aControl.is(),
+ "ViewObjectContactOfUnoControl_Impl::impl_adjustControlVisibilityToLayerVisibility_throw: only valid if we have a control!" );
+
+ SdrPageView* pPageView( nullptr );
+ if ( !impl_getPageView_nothrow( pPageView ) )
+ return;
+
+ SdrUnoObj* pUnoObject = getUnoObject();
+ if ( !pUnoObject )
+ return;
+
+ SdrPageViewAccess aPVAccess( *pPageView );
+ impl_adjustControlVisibilityToLayerVisibility_throw( m_aControl, *pUnoObject, aPVAccess, m_bControlIsVisible, false/*_bForce*/ );
+ }
+
+
+ void ViewObjectContactOfUnoControl_Impl::impl_adjustControlVisibilityToLayerVisibility_throw( const ControlHolder& _rControl,
+ const SdrUnoObj& _rUnoObject, IPageViewAccess const & _rPageView, bool _bIsCurrentlyVisible, bool _bForce )
+ {
+ // in design mode, there is no problem with the visibility: The XControl is hidden by
+ // default, and the Drawing Layer will simply not call our paint routine, if we're in
+ // a hidden layer. So, only alive mode matters.
+ if ( !_rControl.isDesignMode() )
+ {
+ // the layer of our object
+ SdrLayerID nObjectLayer = _rUnoObject.GetLayer();
+ // is the object we're residing in visible in this view?
+ bool bIsObjectVisible = _rUnoObject.IsVisible() && _rPageView.isLayerVisible( nObjectLayer );
+
+ if ( _bForce || ( bIsObjectVisible != _bIsCurrentlyVisible ) )
+ {
+ _rControl.setVisible( bIsObjectVisible );
+ }
+ }
+ }
+
+
+ void ViewObjectContactOfUnoControl_Impl::impl_switchContainerListening_nothrow( bool _bStart )
+ {
+ OSL_PRECOND( m_xContainer.is(), "ViewObjectContactOfUnoControl_Impl::impl_switchContainerListening_nothrow: no control container!" );
+ if ( !m_xContainer.is() )
+ return;
+
+ try
+ {
+ if ( _bStart )
+ m_xContainer->addContainerListener( this );
+ else
+ m_xContainer->removeContainerListener( this );
+ }
+ catch( const Exception& )
+ {
+ DBG_UNHANDLED_EXCEPTION("svx");
+ }
+ }
+
+
+ void ViewObjectContactOfUnoControl_Impl::impl_switchControlListening_nothrow( bool _bStart )
+ {
+ OSL_PRECOND( m_aControl.is(), "ViewObjectContactOfUnoControl_Impl::impl_switchControlListening_nothrow: invalid control!" );
+ if ( !m_aControl.is() )
+ return;
+
+ try
+ {
+ // listen for visibility changes
+ if ( _bStart )
+ m_aControl.addWindowListener( this );
+ else
+ m_aControl.removeWindowListener( this );
+
+ // in design mode, listen for some more aspects
+ impl_switchDesignModeListening_nothrow( impl_isControlDesignMode_nothrow() && _bStart );
+
+ // listen for design mode changes
+ Reference< XModeChangeBroadcaster > xDesignModeChanges( m_aControl.getControl(), UNO_QUERY_THROW );
+ if ( _bStart )
+ xDesignModeChanges->addModeChangeListener( this );
+ else
+ xDesignModeChanges->removeModeChangeListener( this );
+ }
+ catch( const Exception& )
+ {
+ DBG_UNHANDLED_EXCEPTION("svx");
+ }
+ }
+
+
+ void ViewObjectContactOfUnoControl_Impl::impl_switchDesignModeListening_nothrow( bool _bStart )
+ {
+ if ( m_bIsDesignModeListening != _bStart )
+ {
+ m_bIsDesignModeListening = _bStart;
+ impl_switchPropertyListening_nothrow( _bStart );
+ }
+ }
+
+
+ void ViewObjectContactOfUnoControl_Impl::impl_switchPropertyListening_nothrow( bool _bStart )
+ {
+ OSL_PRECOND( m_aControl.is(), "ViewObjectContactOfUnoControl_Impl::impl_switchPropertyListening_nothrow: no control!" );
+ if ( !m_aControl.is() )
+ return;
+
+ try
+ {
+ Reference< XPropertySet > xModelProperties( m_aControl.getModel(), UNO_QUERY_THROW );
+ if ( _bStart )
+ xModelProperties->addPropertyChangeListener( OUString(), this );
+ else
+ xModelProperties->removePropertyChangeListener( OUString(), this );
+ }
+ catch( const Exception& )
+ {
+ DBG_UNHANDLED_EXCEPTION("svx");
+ }
+ }
+
+
+ bool ViewObjectContactOfUnoControl_Impl::isPrintableControl() const
+ {
+ SdrUnoObj* pUnoObject = getUnoObject();
+ if ( !pUnoObject )
+ return false;
+
+ bool bIsPrintable = false;
+ try
+ {
+ Reference< XPropertySet > xModelProperties( pUnoObject->GetUnoControlModel(), UNO_QUERY_THROW );
+ OSL_VERIFY( xModelProperties->getPropertyValue( "Printable" ) >>= bIsPrintable );
+ }
+ catch( const Exception& )
+ {
+ DBG_UNHANDLED_EXCEPTION("svx");
+ }
+ return bIsPrintable;
+ }
+
+
+ void SAL_CALL ViewObjectContactOfUnoControl_Impl::disposing( const EventObject& Source )
+ {
+ SolarMutexGuard aSolarGuard;
+ // some code below - in particular our disposal - might trigger actions which require the
+ // SolarMutex. In particular, in our disposal, we remove ourself as listener from the control,
+ // which alone needs the SolarMutex. Of course this - a removeFooListener needed the SolarMutex -
+ // is the real bug. Toolkit really is infested with solar mutex usage ... :( #i82169#
+
+ if ( !m_aControl.is() )
+ return;
+
+ if ( ( m_aControl == Source.Source )
+ || ( m_aControl.getModel() == Source.Source )
+ )
+ {
+ // the model or the control is dying ... hmm, not much sense in that we ourself continue
+ // living
+ impl_dispose_nothrow( false );
+ return;
+ }
+
+ DBG_ASSERT( Source.Source == m_xContainer, "ViewObjectContactOfUnoControl_Impl::disposing: Who's this?" );
+ }
+
+
+ void SAL_CALL ViewObjectContactOfUnoControl_Impl::windowResized( const WindowEvent& /*e*/ )
+ {
+ // not interested in
+ }
+
+
+ void SAL_CALL ViewObjectContactOfUnoControl_Impl::windowMoved( const WindowEvent& /*e*/ )
+ {
+ // not interested in
+ }
+
+
+ void SAL_CALL ViewObjectContactOfUnoControl_Impl::windowShown( const EventObject& /*e*/ )
+ {
+ SolarMutexGuard aSolarGuard;
+ m_bControlIsVisible = true;
+ }
+
+
+ void SAL_CALL ViewObjectContactOfUnoControl_Impl::windowHidden( const EventObject& /*e*/ )
+ {
+ SolarMutexGuard aSolarGuard;
+ m_bControlIsVisible = false;
+ }
+
+
+ void SAL_CALL ViewObjectContactOfUnoControl_Impl::propertyChange( const PropertyChangeEvent& /*_rEvent*/ )
+ {
+ SolarMutexGuard aSolarGuard;
+ // (re)painting might require VCL operations, which need the SolarMutex
+
+ OSL_PRECOND( !impl_isDisposed_nofail(), "ViewObjectContactOfUnoControl_Impl::propertyChange: already disposed()" );
+ if ( impl_isDisposed_nofail() )
+ return;
+
+ DBG_ASSERT( m_aControl.is(), "ViewObjectContactOfUnoControl_Impl::propertyChange: " );
+ if ( !m_aControl.is() )
+ return;
+
+ // a generic property changed. If we're in design mode, we need to repaint the control
+ if ( impl_isControlDesignMode_nothrow() )
+ {
+ m_pAntiImpl->propertyChange();
+ }
+ }
+
+
+ void SAL_CALL ViewObjectContactOfUnoControl_Impl::modeChanged( const ModeChangeEvent& _rSource )
+ {
+ SolarMutexGuard aSolarGuard;
+
+ DBG_ASSERT( _rSource.NewMode == "design" || _rSource.NewMode == "alive", "ViewObjectContactOfUnoControl_Impl::modeChanged: unexpected mode!" );
+
+ m_eControlDesignMode = _rSource.NewMode == "design" ? eDesign : eAlive;
+
+ impl_switchDesignModeListening_nothrow( impl_isControlDesignMode_nothrow() );
+
+ try
+ {
+ // if the control is part of an invisible layer, we need to explicitly hide it in alive mode
+ impl_adjustControlVisibilityToLayerVisibility_throw();
+ }
+ catch( const Exception& )
+ {
+ DBG_UNHANDLED_EXCEPTION("svx");
+ }
+ }
+
+
+ void SAL_CALL ViewObjectContactOfUnoControl_Impl::elementInserted( const ContainerEvent& /*_Event*/ )
+ {
+ // not interested in
+ }
+
+
+ void SAL_CALL ViewObjectContactOfUnoControl_Impl::elementRemoved( const ContainerEvent& Event )
+ {
+ SolarMutexGuard aSolarGuard;
+ // some code below - in particular our disposal - might trigger actions which require the
+ // SolarMutex. In particular, in our disposal, we remove ourself as listener from the control,
+ // which alone needs the SolarMutex. Of course this - a removeFooListener needed the SolarMutex -
+ // is the real bug. Toolkit really is infested with solar mutex usage ... :( #i82169#
+ DBG_ASSERT( Event.Source == m_xContainer, "ViewObjectContactOfUnoControl_Impl::elementRemoved: where did this come from?" );
+
+ if ( m_aControl == Event.Element )
+ impl_dispose_nothrow( false );
+ }
+
+
+ void SAL_CALL ViewObjectContactOfUnoControl_Impl::elementReplaced( const ContainerEvent& Event )
+ {
+ SolarMutexGuard aSolarGuard;
+ DBG_ASSERT( Event.Source == m_xContainer, "ViewObjectContactOfUnoControl_Impl::elementReplaced: where did this come from?" );
+
+ if ( ! ( m_aControl == Event.ReplacedElement ) )
+ return;
+
+ Reference< XControl > xNewControl( Event.Element, UNO_QUERY );
+ DBG_ASSERT( xNewControl.is(), "ViewObjectContactOfUnoControl_Impl::elementReplaced: invalid new control!" );
+ if ( !xNewControl.is() )
+ return;
+
+ ENSURE_OR_THROW( m_pOutputDeviceForWindow, "calling this without /me having an output device should be impossible." );
+
+ DBG_ASSERT( xNewControl->getModel() == m_aControl.getModel(), "ViewObjectContactOfUnoControl_Impl::elementReplaced: another model at the new control?" );
+ // another model should - in the drawing layer - also imply another SdrUnoObj, which
+ // should also result in new ViewContact, and thus in new ViewObjectContacts
+
+ impl_switchControlListening_nothrow( false );
+
+ ControlHolder aNewControl( xNewControl );
+ aNewControl.setZoom( m_aControl.getZoom() );
+ aNewControl.setPosSize( m_aControl.getPosSize() );
+ aNewControl.setDesignMode( impl_isControlDesignMode_nothrow() );
+
+ m_aControl = xNewControl;
+ m_bControlIsVisible = m_aControl.isVisible();
+
+ impl_switchControlListening_nothrow( true );
+
+ m_pAntiImpl->onControlChangedOrModified( ViewObjectContactOfUnoControl::ImplAccess() );
+ }
+
+
+ void ViewObjectContactOfUnoControl_Impl::setControlDesignMode( bool _bDesignMode ) const
+ {
+ if ( ( m_eControlDesignMode != eUnknown ) && ( _bDesignMode == impl_isControlDesignMode_nothrow() ) )
+ // nothing to do
+ return;
+ m_eControlDesignMode = _bDesignMode ? eDesign : eAlive;
+
+ if ( !m_aControl.is() )
+ // nothing to do, the setting will be respected as soon as the control
+ // is created
+ return;
+
+ try
+ {
+ m_aControl.setDesignMode( _bDesignMode );
+ }
+ catch( const Exception& )
+ {
+ DBG_UNHANDLED_EXCEPTION("svx");
+ }
+ }
+
+
+ //= LazyControlCreationPrimitive2D
+
+
+ bool LazyControlCreationPrimitive2D::operator==(const BasePrimitive2D& rPrimitive) const
+ {
+ if ( !BufferedDecompositionPrimitive2D::operator==( rPrimitive ) )
+ return false;
+
+ const LazyControlCreationPrimitive2D* pRHS = dynamic_cast< const LazyControlCreationPrimitive2D* >( &rPrimitive );
+ if ( !pRHS )
+ return false;
+
+ if ( m_pVOCImpl != pRHS->m_pVOCImpl )
+ return false;
+
+ if ( m_aTransformation != pRHS->m_aTransformation )
+ return false;
+
+ return true;
+ }
+
+
+ void LazyControlCreationPrimitive2D::getTransformation( const ViewContactOfUnoControl& _rVOC, ::basegfx::B2DHomMatrix& _out_Transformation )
+ {
+ // Do use model data directly to create the correct geometry. Do NOT
+ // use getBoundRect()/getSnapRect() here; these will use the sequence of
+ // primitives themselves in the long run.
+ const tools::Rectangle aSdrGeoData( _rVOC.GetSdrUnoObj().GetGeoRect() );
+ const basegfx::B2DRange aRange = vcl::unotools::b2DRectangleFromRectangle(aSdrGeoData);
+
+ _out_Transformation.identity();
+ _out_Transformation.set( 0, 0, aRange.getWidth() );
+ _out_Transformation.set( 1, 1, aRange.getHeight() );
+ _out_Transformation.set( 0, 2, aRange.getMinX() );
+ _out_Transformation.set( 1, 2, aRange.getMinY() );
+ }
+
+
+ ::basegfx::B2DRange LazyControlCreationPrimitive2D::getB2DRange( const ::drawinglayer::geometry::ViewInformation2D& /*rViewInformation*/ ) const
+ {
+ ::basegfx::B2DRange aRange( 0.0, 0.0, 1.0, 1.0 );
+ aRange.transform( m_aTransformation );
+ return aRange;
+ }
+
+
+ void LazyControlCreationPrimitive2D::get2DDecomposition( ::drawinglayer::primitive2d::Primitive2DDecompositionVisitor& rVisitor, const ::drawinglayer::geometry::ViewInformation2D& _rViewInformation ) const
+ {
+ #if OSL_DEBUG_LEVEL > 0
+ ::basegfx::B2DVector aScale, aTranslate;
+ double fRotate, fShearX;
+ _rViewInformation.getObjectToViewTransformation().decompose( aScale, aTranslate, fRotate, fShearX );
+ #endif
+ if ( m_pVOCImpl->hasControl() )
+ impl_positionAndZoomControl( _rViewInformation );
+ BufferedDecompositionPrimitive2D::get2DDecomposition( rVisitor, _rViewInformation );
+ }
+
+
+ void LazyControlCreationPrimitive2D::create2DDecomposition( ::drawinglayer::primitive2d::Primitive2DContainer& rContainer, const ::drawinglayer::geometry::ViewInformation2D& _rViewInformation ) const
+ {
+ #if OSL_DEBUG_LEVEL > 0
+ ::basegfx::B2DVector aScale, aTranslate;
+ double fRotate, fShearX;
+ _rViewInformation.getObjectToViewTransformation().decompose( aScale, aTranslate, fRotate, fShearX );
+ #endif
+ const bool bHadControl = m_pVOCImpl->getExistentControl().is();
+
+ // force control here to make it a VCL ChildWindow. Will be fetched
+ // and used below by getExistentControl()
+ m_pVOCImpl->ensureControl( &_rViewInformation.getObjectToViewTransformation() );
+ impl_positionAndZoomControl( _rViewInformation );
+
+ // get needed data
+ const ViewContactOfUnoControl& rViewContactOfUnoControl( m_pVOCImpl->getViewContact() );
+ Reference< XControlModel > xControlModel( rViewContactOfUnoControl.GetSdrUnoObj().GetUnoControlModel() );
+ const ControlHolder& rControl( m_pVOCImpl->getExistentControl() );
+
+ if ( !bHadControl && rControl.is() && rControl.isVisible() )
+ rControl.invalidate();
+
+ // check if we already have an XControl.
+ if ( !xControlModel.is() || !rControl.is() )
+ {
+ // use the default mechanism. This will create a ControlPrimitive2D without
+ // handing over a XControl. If not even a XControlModel exists, it will
+ // create the SdrObject fallback visualisation
+ rViewContactOfUnoControl.getViewIndependentPrimitive2DContainer(rContainer);
+ return;
+ }
+
+ // create a primitive and hand over the existing xControl. This will
+ // allow the primitive to not need to create another one on demand.
+ rContainer.push_back( new ::drawinglayer::primitive2d::ControlPrimitive2D(
+ m_aTransformation, xControlModel, rControl.getControl() ) );
+ }
+
+ sal_uInt32 LazyControlCreationPrimitive2D::getPrimitive2DID() const
+ {
+ return PRIMITIVE2D_ID_SDRCONTROLPRIMITIVE2D;
+ }
+
+ ViewObjectContactOfUnoControl::ViewObjectContactOfUnoControl( ObjectContact& _rObjectContact, ViewContactOfUnoControl& _rViewContact )
+ :ViewObjectContactOfSdrObj( _rObjectContact, _rViewContact )
+ ,m_pImpl( new ViewObjectContactOfUnoControl_Impl( this ) )
+ {
+ }
+
+
+ ViewObjectContactOfUnoControl::~ViewObjectContactOfUnoControl()
+ {
+ m_pImpl->dispose();
+ m_pImpl = nullptr;
+
+ }
+
+
+ Reference< XControl > ViewObjectContactOfUnoControl::getControl()
+ {
+ SolarMutexGuard aSolarGuard;
+ m_pImpl->ensureControl( nullptr );
+ return m_pImpl->getExistentControl().getControl();
+ }
+
+
+ Reference< XControl > ViewObjectContactOfUnoControl::getTemporaryControlForWindow(
+ const vcl::Window& _rWindow, Reference< XControlContainer >& _inout_ControlContainer, const SdrUnoObj& _rUnoObject )
+ {
+ ControlHolder aControl;
+
+ InvisibleControlViewAccess aSimulatePageView( _inout_ControlContainer );
+ OSL_VERIFY( ViewObjectContactOfUnoControl_Impl::createControlForDevice( aSimulatePageView, *_rWindow.GetOutDev(), _rUnoObject,
+ _rWindow.GetOutDev()->GetViewTransformation(), _rWindow.GetOutDev()->GetInverseViewTransformation(), aControl ) );
+ return aControl.getControl();
+ }
+
+
+ void ViewObjectContactOfUnoControl::ensureControlVisibility( bool _bVisible ) const
+ {
+ SolarMutexGuard aSolarGuard;
+
+ try
+ {
+ const ControlHolder& rControl( m_pImpl->getExistentControl() );
+ if ( !rControl.is() )
+ return;
+
+ // only need to care for alive mode
+ if ( rControl.isDesignMode() )
+ return;
+
+ // is the visibility correct?
+ if ( m_pImpl->isControlVisible() == _bVisible )
+ return;
+
+ // no -> adjust it
+ rControl.setVisible( _bVisible );
+ DBG_ASSERT( m_pImpl->isControlVisible() == _bVisible, "ViewObjectContactOfUnoControl::ensureControlVisibility: this didn't work!" );
+ // now this would mean that either isControlVisible is not reliable,
+ // or that showing/hiding the window did not work as intended.
+ }
+ catch( const Exception& )
+ {
+ DBG_UNHANDLED_EXCEPTION("svx");
+ }
+ }
+
+
+ void ViewObjectContactOfUnoControl::setControlDesignMode( bool _bDesignMode ) const
+ {
+ SolarMutexGuard aSolarGuard;
+ m_pImpl->setControlDesignMode( _bDesignMode );
+
+ if(!_bDesignMode)
+ {
+ // when live mode is switched on, a refresh is needed. The edit mode visualisation
+ // needs to be repainted and the now used VCL-Window needs to be positioned and
+ // sized. Both is done from the repaint refresh.
+ const_cast< ViewObjectContactOfUnoControl* >(this)->ActionChanged();
+ }
+ }
+
+
+ void ViewObjectContactOfUnoControl::createPrimitive2DSequence(const DisplayInfo& /*rDisplayInfo*/, drawinglayer::primitive2d::Primitive2DDecompositionVisitor& rVisitor) const
+ {
+ if ( m_pImpl->isDisposed() )
+ // our control already died.
+ // TODO: Is it worth re-creating the control? Finally, this is a pathological situation, it means some instance
+ // disposed the control though it doesn't own it. So, /me thinks we should not bother here.
+ return;
+
+ if ( GetObjectContact().getViewInformation2D().getViewTransformation().isIdentity() )
+ // remove this when #i115754# is fixed
+ return;
+
+ // ignore existing controls which are in alive mode and manually switched to "invisible" #i102090#
+ const ControlHolder& rControl( m_pImpl->getExistentControl() );
+ if ( rControl.is() && !rControl.isDesignMode() && !rControl.isVisible() )
+ return;
+
+ rVisitor.visit( new LazyControlCreationPrimitive2D( m_pImpl ) );
+ }
+
+
+ bool ViewObjectContactOfUnoControl::isPrimitiveVisible( const DisplayInfo& _rDisplayInfo ) const
+ {
+ SolarMutexGuard aSolarGuard;
+
+ if ( m_pImpl->hasControl() )
+ {
+ const ::drawinglayer::geometry::ViewInformation2D& rViewInformation( GetObjectContact().getViewInformation2D() );
+ #if OSL_DEBUG_LEVEL > 0
+ ::basegfx::B2DVector aScale, aTranslate;
+ double fRotate, fShearX;
+ rViewInformation.getObjectToViewTransformation().decompose( aScale, aTranslate, fRotate, fShearX );
+ #endif
+
+ if ( !rViewInformation.getViewport().isEmpty() )
+ {
+ // tdf#121963 check and eventually pre-multiply ViewTransformation
+ // with GridOffset transformation to avoid alternating positions of
+ // FormControls which are victims of the non-linear calc ViewTransformation
+ // aka GridOffset. For other paths (e.g. repaint) this is included already
+ // as part of the object's sequence of B2DPrimitive - representation
+ // (see ViewObjectContact::getPrimitive2DSequence and how getGridOffset is used there)
+ basegfx::B2DHomMatrix aViewTransformation(rViewInformation.getObjectToViewTransformation());
+
+ if(GetObjectContact().supportsGridOffsets())
+ {
+ const basegfx::B2DVector& rGridOffset(getGridOffset());
+
+ if(0.0 != rGridOffset.getX() || 0.0 != rGridOffset.getY())
+ {
+ // pre-multiply: GridOffset needs to be applied directly to logic model data
+ // of object coordinates, so multiply GridOffset from right to make it
+ // work as 1st change - these objects may still be part of groups/hierarchies
+ aViewTransformation = aViewTransformation * basegfx::utils::createTranslateB2DHomMatrix(rGridOffset);
+ }
+ }
+
+ m_pImpl->positionAndZoomControl(aViewTransformation);
+ }
+ }
+
+ return ViewObjectContactOfSdrObj::isPrimitiveVisible( _rDisplayInfo );
+ }
+
+
+ void ViewObjectContactOfUnoControl::propertyChange()
+ {
+ impl_onControlChangedOrModified();
+ }
+
+
+ void ViewObjectContactOfUnoControl::ActionChanged()
+ {
+ // call parent
+ ViewObjectContactOfSdrObj::ActionChanged();
+ const ControlHolder& rControl(m_pImpl->getExistentControl());
+
+ if(!rControl.is() || rControl.isDesignMode())
+ return;
+
+ // #i93180# if layer visibility has changed and control is in live mode, it is necessary
+ // to correct visibility to make those control vanish on SdrObject LayerID changes
+ const SdrPageView* pSdrPageView = GetObjectContact().TryToGetSdrPageView();
+
+ if(pSdrPageView)
+ {
+ const SdrObject& rObject = getSdrObject();
+ const bool bIsLayerVisible( rObject.IsVisible() && pSdrPageView->GetVisibleLayers().IsSet(rObject.GetLayer()));
+
+ if(rControl.isVisible() != bIsLayerVisible)
+ {
+ rControl.setVisible(bIsLayerVisible);
+ }
+ }
+ }
+
+
+ void ViewObjectContactOfUnoControl::impl_onControlChangedOrModified()
+ {
+ // graphical invalidate at all views
+ ActionChanged();
+
+ // #i93318# flush Primitive2DContainer to force recreation with updated XControlModel
+ // since e.g. background color has changed and existing decompositions are possibly no
+ // longer valid. Unfortunately this is not detected from ControlPrimitive2D::operator==
+ // since it only has a uno reference to the XControlModel
+ flushPrimitive2DSequence();
+ }
+
+ UnoControlPrintOrPreviewContact::UnoControlPrintOrPreviewContact( ObjectContactOfPageView& _rObjectContact, ViewContactOfUnoControl& _rViewContact )
+ :ViewObjectContactOfUnoControl( _rObjectContact, _rViewContact )
+ {
+ }
+
+
+ UnoControlPrintOrPreviewContact::~UnoControlPrintOrPreviewContact()
+ {
+ }
+
+
+ void UnoControlPrintOrPreviewContact::createPrimitive2DSequence(const DisplayInfo& rDisplayInfo, drawinglayer::primitive2d::Primitive2DDecompositionVisitor& rVisitor ) const
+ {
+ if ( !m_pImpl->isPrintableControl() )
+ return;
+ ViewObjectContactOfUnoControl::createPrimitive2DSequence( rDisplayInfo, rVisitor );
+ }
+
+
+} // namespace sdr::contact
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/sdr/contact/viewobjectcontactredirector.cxx b/svx/source/sdr/contact/viewobjectcontactredirector.cxx
new file mode 100644
index 000000000..c49909327
--- /dev/null
+++ b/svx/source/sdr/contact/viewobjectcontactredirector.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 <svx/sdr/contact/viewobjectcontactredirector.hxx>
+#include <svx/sdr/contact/viewobjectcontact.hxx>
+
+namespace sdr::contact
+{
+// basic constructor.
+ViewObjectContactRedirector::ViewObjectContactRedirector() {}
+
+// The destructor.
+ViewObjectContactRedirector::~ViewObjectContactRedirector() {}
+
+void ViewObjectContactRedirector::createRedirectedPrimitive2DSequence(
+ const sdr::contact::ViewObjectContact& rOriginal, const sdr::contact::DisplayInfo& rDisplayInfo,
+ drawinglayer::primitive2d::Primitive2DDecompositionVisitor& rVisitor)
+{
+ return rOriginal.createPrimitive2DSequence(rDisplayInfo, rVisitor);
+}
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/sdr/misc/ImageMapInfo.cxx b/svx/source/sdr/misc/ImageMapInfo.cxx
new file mode 100644
index 000000000..c50fb2fcc
--- /dev/null
+++ b/svx/source/sdr/misc/ImageMapInfo.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 <svx/ImageMapInfo.hxx>
+
+#include <svx/svdobj.hxx>
+#include <svx/svdograf.hxx>
+#include <svx/svdoole2.hxx>
+#include <vcl/imapobj.hxx>
+#include <vcl/svapp.hxx>
+#include <vcl/outdev.hxx>
+
+SvxIMapInfo* SvxIMapInfo::GetIMapInfo(SdrObject const* pObject)
+{
+ assert(pObject);
+
+ SvxIMapInfo* pIMapInfo = nullptr;
+ sal_uInt16 nCount = pObject->GetUserDataCount();
+
+ // Can we find IMap information within the user data?
+ for (sal_uInt16 i = 0; i < nCount; i++)
+ {
+ SdrObjUserData* pUserData = pObject->GetUserData(i);
+
+ if ((pUserData->GetInventor() == SdrInventor::StarDrawUserData)
+ && (pUserData->GetId() == SVX_IMAPINFO_ID))
+ pIMapInfo = static_cast<SvxIMapInfo*>(pUserData);
+ }
+
+ return pIMapInfo;
+}
+
+IMapObject* SvxIMapInfo::GetHitIMapObject(const SdrObject* pObj, const Point& rWinPoint,
+ const OutputDevice* pCmpWnd)
+{
+ SvxIMapInfo* pIMapInfo = GetIMapInfo(pObj);
+ IMapObject* pIMapObj = nullptr;
+
+ if (pIMapInfo)
+ {
+ const MapMode aMap100(MapUnit::Map100thMM);
+ Size aGraphSize;
+ Point aRelPoint(rWinPoint);
+ ImageMap& rImageMap = const_cast<ImageMap&>(pIMapInfo->GetImageMap());
+ tools::Rectangle& rRect = const_cast<tools::Rectangle&>(pObj->GetLogicRect());
+
+ if (pCmpWnd)
+ {
+ MapMode aWndMode = pCmpWnd->GetMapMode();
+ aRelPoint = pCmpWnd->LogicToLogic(rWinPoint, &aWndMode, &aMap100);
+ rRect = pCmpWnd->LogicToLogic(pObj->GetLogicRect(), &aWndMode, &aMap100);
+ }
+
+ bool bObjSupported = false;
+
+ // execute HitTest
+ if (auto pGrafObj = dynamic_cast<const SdrGrafObj*>(pObj)) // simple graphics object
+ {
+ const GeoStat& rGeo = pGrafObj->GetGeoStat();
+ std::unique_ptr<SdrGrafObjGeoData> pGeoData(
+ static_cast<SdrGrafObjGeoData*>(pGrafObj->GetGeoData().release()));
+
+ // Undo rotation
+ if (rGeo.nRotationAngle)
+ RotatePoint(aRelPoint, rRect.TopLeft(), -rGeo.mfSinRotationAngle,
+ rGeo.mfCosRotationAngle);
+
+ // Undo mirroring
+ if (pGeoData->bMirrored)
+ aRelPoint.setX(rRect.Right() + rRect.Left() - aRelPoint.X());
+
+ // Undo shearing
+ if (rGeo.nShearAngle)
+ ShearPoint(aRelPoint, rRect.TopLeft(), -rGeo.mfTanShearAngle);
+
+ if (pGrafObj->GetGrafPrefMapMode().GetMapUnit() == MapUnit::MapPixel)
+ aGraphSize = Application::GetDefaultDevice()->PixelToLogic(
+ pGrafObj->GetGrafPrefSize(), aMap100);
+ else
+ aGraphSize = OutputDevice::LogicToLogic(pGrafObj->GetGrafPrefSize(),
+ pGrafObj->GetGrafPrefMapMode(), aMap100);
+
+ bObjSupported = true;
+ }
+ else if (auto pOleObj = dynamic_cast<const SdrOle2Obj*>(pObj)) // OLE object
+ {
+ aGraphSize = pOleObj->GetOrigObjSize();
+ bObjSupported = true;
+ }
+
+ // Everything worked out well, thus execute HitTest
+ if (bObjSupported)
+ {
+ // Calculate relative position of mouse cursor
+ aRelPoint -= rRect.TopLeft();
+ pIMapObj = rImageMap.GetHitIMapObject(aGraphSize, rRect.GetSize(), aRelPoint);
+
+ // We don't care about deactivated objects
+ if (pIMapObj && !pIMapObj->IsActive())
+ pIMapObj = nullptr;
+ }
+ }
+
+ return pIMapObj;
+}
diff --git a/svx/source/sdr/overlay/overlayanimatedbitmapex.cxx b/svx/source/sdr/overlay/overlayanimatedbitmapex.cxx
new file mode 100644
index 000000000..967e9665b
--- /dev/null
+++ b/svx/source/sdr/overlay/overlayanimatedbitmapex.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 <svx/sdr/overlay/overlayanimatedbitmapex.hxx>
+#include <svx/sdr/overlay/overlaymanager.hxx>
+#include <sdr/overlay/overlaytools.hxx>
+
+
+namespace sdr::overlay
+{
+ drawinglayer::primitive2d::Primitive2DContainer OverlayAnimatedBitmapEx::createOverlayObjectPrimitive2DSequence()
+ {
+ if(mbOverlayState)
+ {
+ const drawinglayer::primitive2d::Primitive2DReference aPrimitive(
+ new drawinglayer::primitive2d::OverlayBitmapExPrimitive(
+ maBitmapEx1,
+ getBasePosition(),
+ mnCenterX1,
+ mnCenterY1,
+ getShearX(),
+ getRotation()));
+
+ return drawinglayer::primitive2d::Primitive2DContainer { aPrimitive };
+ }
+ else
+ {
+ const drawinglayer::primitive2d::Primitive2DReference aPrimitive(
+ new drawinglayer::primitive2d::OverlayBitmapExPrimitive(
+ maBitmapEx2,
+ getBasePosition(),
+ mnCenterX2,
+ mnCenterY2,
+ getShearX(),
+ getRotation()));
+
+ return drawinglayer::primitive2d::Primitive2DContainer { aPrimitive };
+ }
+ }
+
+ OverlayAnimatedBitmapEx::OverlayAnimatedBitmapEx(
+ const basegfx::B2DPoint& rBasePos,
+ const BitmapEx& rBitmapEx1,
+ const BitmapEx& rBitmapEx2,
+ sal_uInt64 nBlinkTime,
+ sal_uInt16 nCenX1,
+ sal_uInt16 nCenY1,
+ sal_uInt16 nCenX2,
+ sal_uInt16 nCenY2,
+ double fShearX,
+ double fRotation)
+ : OverlayObjectWithBasePosition(rBasePos, COL_WHITE),
+ maBitmapEx1(rBitmapEx1),
+ maBitmapEx2(rBitmapEx2),
+ mnCenterX1(nCenX1), mnCenterY1(nCenY1),
+ mnCenterX2(nCenX2), mnCenterY2(nCenY2),
+ mnBlinkTime(impCheckBlinkTimeValueRange(nBlinkTime)),
+ mfShearX(fShearX),
+ mfRotation(fRotation),
+ mbOverlayState(false)
+ {
+ // set AllowsAnimation flag to mark this object as animation capable
+ mbAllowsAnimation = true;
+ }
+
+ OverlayAnimatedBitmapEx::~OverlayAnimatedBitmapEx()
+ {
+ }
+
+ void OverlayAnimatedBitmapEx::Trigger(sal_uInt32 nTime)
+ {
+ if(!getOverlayManager())
+ return;
+
+ // #i53216# produce event after nTime + x
+ SetTime(nTime + mnBlinkTime);
+
+ // switch state
+ if(mbOverlayState)
+ {
+ mbOverlayState = false;
+ }
+ else
+ {
+ mbOverlayState = true;
+ }
+
+ // re-insert me as event
+ getOverlayManager()->InsertEvent(*this);
+
+ // register change (after change)
+ objectChange();
+ }
+
+} // end of namespace
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/sdr/overlay/overlaybitmapex.cxx b/svx/source/sdr/overlay/overlaybitmapex.cxx
new file mode 100644
index 000000000..54ccf788b
--- /dev/null
+++ b/svx/source/sdr/overlay/overlaybitmapex.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 <drawinglayer/primitive2d/unifiedtransparenceprimitive2d.hxx>
+#include <svx/sdr/overlay/overlaybitmapex.hxx>
+#include <sdr/overlay/overlaytools.hxx>
+
+
+namespace sdr::overlay
+{
+ drawinglayer::primitive2d::Primitive2DContainer OverlayBitmapEx::createOverlayObjectPrimitive2DSequence()
+ {
+ drawinglayer::primitive2d::Primitive2DReference aReference(
+ new drawinglayer::primitive2d::OverlayBitmapExPrimitive(
+ maBitmapEx,
+ getBasePosition(),
+ mnCenterX,
+ mnCenterY,
+ mfShearX,
+ mfRotation));
+
+ if(basegfx::fTools::more(mfAlpha, 0.0))
+ {
+ drawinglayer::primitive2d::Primitive2DContainer aNewTransPrimitiveVector { aReference };
+ aReference = drawinglayer::primitive2d::Primitive2DReference(
+ new drawinglayer::primitive2d::UnifiedTransparencePrimitive2D(std::move(aNewTransPrimitiveVector), mfAlpha));
+ }
+
+ return drawinglayer::primitive2d::Primitive2DContainer { aReference };
+ }
+
+ OverlayBitmapEx::OverlayBitmapEx(
+ const basegfx::B2DPoint& rBasePos,
+ const BitmapEx& rBitmapEx,
+ sal_uInt16 nCenX,
+ sal_uInt16 nCenY,
+ double fAlpha,
+ double fShearX,
+ double fRotation)
+ : OverlayObjectWithBasePosition(rBasePos, COL_WHITE),
+ maBitmapEx(rBitmapEx),
+ mnCenterX(nCenX),
+ mnCenterY(nCenY),
+ mfAlpha(fAlpha),
+ mfShearX(fShearX),
+ mfRotation(fRotation)
+ {
+ }
+
+ OverlayBitmapEx::~OverlayBitmapEx()
+ {
+ }
+
+} // end of namespace
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/sdr/overlay/overlaycrosshair.cxx b/svx/source/sdr/overlay/overlaycrosshair.cxx
new file mode 100644
index 000000000..942534d68
--- /dev/null
+++ b/svx/source/sdr/overlay/overlaycrosshair.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 <sdr/overlay/overlaycrosshair.hxx>
+#include <sdr/overlay/overlaytools.hxx>
+#include <svx/sdr/overlay/overlaymanager.hxx>
+
+
+namespace sdr::overlay
+{
+ drawinglayer::primitive2d::Primitive2DContainer OverlayCrosshairStriped::createOverlayObjectPrimitive2DSequence()
+ {
+ drawinglayer::primitive2d::Primitive2DContainer aRetval;
+
+ if(getOverlayManager())
+ {
+ const basegfx::BColor aRGBColorA(getOverlayManager()->getStripeColorA().getBColor());
+ const basegfx::BColor aRGBColorB(getOverlayManager()->getStripeColorB().getBColor());
+ const double fStripeLengthPixel(getOverlayManager()->getStripeLengthPixel());
+
+ const drawinglayer::primitive2d::Primitive2DReference aReference(
+ new drawinglayer::primitive2d::OverlayCrosshairPrimitive(
+ getBasePosition(),
+ aRGBColorA,
+ aRGBColorB,
+ fStripeLengthPixel));
+
+ aRetval = drawinglayer::primitive2d::Primitive2DContainer { aReference };
+ }
+
+ return aRetval;
+ }
+
+ void OverlayCrosshairStriped::stripeDefinitionHasChanged()
+ {
+ // react on OverlayManager's stripe definition change
+ objectChange();
+ }
+
+ OverlayCrosshairStriped::OverlayCrosshairStriped(const basegfx::B2DPoint& rBasePos)
+ : OverlayObjectWithBasePosition(rBasePos, COL_BLACK)
+ {
+ }
+
+ OverlayCrosshairStriped::~OverlayCrosshairStriped()
+ {
+ }
+
+} // end of namespace
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/sdr/overlay/overlayhandle.cxx b/svx/source/sdr/overlay/overlayhandle.cxx
new file mode 100644
index 000000000..c94da8e07
--- /dev/null
+++ b/svx/source/sdr/overlay/overlayhandle.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 <sdr/overlay/overlayhandle.hxx>
+#include <sdr/overlay/overlaytools.hxx>
+
+namespace sdr::overlay {
+
+using namespace drawinglayer;
+using namespace basegfx;
+
+primitive2d::Primitive2DContainer OverlayHandle::createOverlayObjectPrimitive2DSequence()
+{
+ basegfx::BColor aStrokeColor = maStrokeColor.getBColor();
+ basegfx::BColor aFillColor = getBaseColor().getBColor();
+
+ const primitive2d::Primitive2DReference aReference(
+ new primitive2d::OverlayStaticRectanglePrimitive(maBasePosition, maSize, aStrokeColor, aFillColor, 0.3f, 0.0f));
+
+ return primitive2d::Primitive2DContainer { aReference };
+}
+
+OverlayHandle::OverlayHandle(const B2DPoint& rBasePos,
+ const B2DSize& rSize,
+ Color const & rStrokeColor,
+ Color const & rFillColor)
+ : OverlayObjectWithBasePosition(rBasePos, rFillColor)
+ , maSize(rSize)
+ , maStrokeColor(rStrokeColor)
+{
+}
+
+OverlayHandle::~OverlayHandle()
+{
+}
+
+} // end of namespace sdr::overlay
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/sdr/overlay/overlayhelpline.cxx b/svx/source/sdr/overlay/overlayhelpline.cxx
new file mode 100644
index 000000000..9955122bb
--- /dev/null
+++ b/svx/source/sdr/overlay/overlayhelpline.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 <sdr/overlay/overlayhelpline.hxx>
+#include <sdr/overlay/overlaytools.hxx>
+#include <svx/sdr/overlay/overlaymanager.hxx>
+
+
+namespace sdr::overlay
+{
+ drawinglayer::primitive2d::Primitive2DContainer OverlayHelplineStriped::createOverlayObjectPrimitive2DSequence()
+ {
+ drawinglayer::primitive2d::Primitive2DContainer aRetval;
+
+ if(getOverlayManager())
+ {
+ const basegfx::BColor aRGBColorA(getOverlayManager()->getStripeColorA().getBColor());
+ const basegfx::BColor aRGBColorB(getOverlayManager()->getStripeColorB().getBColor());
+ const double fStripeLengthPixel(getOverlayManager()->getStripeLengthPixel());
+ const drawinglayer::primitive2d::HelplineStyle aStyle(
+ SdrHelpLineKind::Point == getKind() ? drawinglayer::primitive2d::HELPLINESTYLE_POINT :
+ SdrHelpLineKind::Vertical == getKind() ? drawinglayer::primitive2d::HELPLINESTYLE_VERTICAL :
+ drawinglayer::primitive2d::HELPLINESTYLE_HORIZONTAL);
+
+ const drawinglayer::primitive2d::Primitive2DReference aReference(
+ new drawinglayer::primitive2d::OverlayHelplineStripedPrimitive(
+ getBasePosition(),
+ aStyle,
+ aRGBColorA,
+ aRGBColorB,
+ fStripeLengthPixel));
+
+ aRetval = drawinglayer::primitive2d::Primitive2DContainer { aReference };
+ }
+
+ return aRetval;
+ }
+
+ void OverlayHelplineStriped::stripeDefinitionHasChanged()
+ {
+ // react on OverlayManager's stripe definition change
+ objectChange();
+ }
+
+ OverlayHelplineStriped::OverlayHelplineStriped(
+ const basegfx::B2DPoint& rBasePos,
+ SdrHelpLineKind eNewKind)
+ : OverlayObjectWithBasePosition(rBasePos, COL_BLACK),
+ meKind(eNewKind)
+ {
+ }
+
+ OverlayHelplineStriped::~OverlayHelplineStriped()
+ {
+ }
+} // end of namespace
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/sdr/overlay/overlayline.cxx b/svx/source/sdr/overlay/overlayline.cxx
new file mode 100644
index 000000000..30c08f026
--- /dev/null
+++ b/svx/source/sdr/overlay/overlayline.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 <sdr/overlay/overlayline.hxx>
+#include <svx/sdr/overlay/overlaymanager.hxx>
+#include <basegfx/polygon/b2dpolygon.hxx>
+#include <drawinglayer/primitive2d/PolygonMarkerPrimitive2D.hxx>
+
+
+namespace sdr::overlay
+{
+ drawinglayer::primitive2d::Primitive2DContainer OverlayLineStriped::createOverlayObjectPrimitive2DSequence()
+ {
+ drawinglayer::primitive2d::Primitive2DContainer aRetval;
+
+ if(getOverlayManager())
+ {
+ const basegfx::BColor aRGBColorA(getOverlayManager()->getStripeColorA().getBColor());
+ const basegfx::BColor aRGBColorB(getOverlayManager()->getStripeColorB().getBColor());
+ const double fStripeLengthPixel(getOverlayManager()->getStripeLengthPixel());
+ basegfx::B2DPolygon aLine;
+
+ aLine.append(getBasePosition());
+ aLine.append(getSecondPosition());
+
+ const drawinglayer::primitive2d::Primitive2DReference aReference(
+ new drawinglayer::primitive2d::PolygonMarkerPrimitive2D(
+ aLine,
+ aRGBColorA,
+ aRGBColorB,
+ fStripeLengthPixel));
+
+ aRetval = drawinglayer::primitive2d::Primitive2DContainer { aReference };
+ }
+
+ return aRetval;
+ }
+
+ void OverlayLineStriped::stripeDefinitionHasChanged()
+ {
+ // react on OverlayManager's stripe definition change
+ objectChange();
+ }
+
+ OverlayLineStriped::OverlayLineStriped(
+ const basegfx::B2DPoint& rBasePos,
+ const basegfx::B2DPoint& rSecondPos)
+ : OverlayObjectWithBasePosition(rBasePos, COL_BLACK),
+ maSecondPosition(rSecondPos)
+ {
+ }
+
+ OverlayLineStriped::~OverlayLineStriped()
+ {
+ }
+} // end of namespace
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/sdr/overlay/overlaymanager.cxx b/svx/source/sdr/overlay/overlaymanager.cxx
new file mode 100644
index 000000000..3cbd821fb
--- /dev/null
+++ b/svx/source/sdr/overlay/overlaymanager.cxx
@@ -0,0 +1,346 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * 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/sdr/overlay/overlaymanager.hxx>
+#include <basegfx/range/b2drange.hxx>
+#include <comphelper/propertyvalue.hxx>
+#include <tools/gen.hxx>
+#include <vcl/canvastools.hxx>
+#include <vcl/outdev.hxx>
+#include <vcl/window.hxx>
+#include <svx/sdr/overlay/overlayobject.hxx>
+#include <basegfx/matrix/b2dhommatrix.hxx>
+#include <drawinglayer/processor2d/baseprocessor2d.hxx>
+#include <drawinglayer/processor2d/processor2dtools.hxx>
+#include <svtools/optionsdrawinglayer.hxx>
+#include <osl/diagnose.h>
+#include <memory>
+
+
+using namespace com::sun::star;
+
+
+namespace sdr::overlay
+{
+ void OverlayManager::ImpDrawMembers(const basegfx::B2DRange& rRange, OutputDevice& rDestinationDevice) const
+ {
+ const sal_uInt32 nSize(maOverlayObjects.size());
+
+ if(!nSize)
+ return;
+
+ const AntialiasingFlags nOriginalAA(rDestinationDevice.GetAntialiasing());
+ const bool bIsAntiAliasing(SvtOptionsDrawinglayer::IsAntiAliasing());
+
+ // create processor
+ std::unique_ptr<drawinglayer::processor2d::BaseProcessor2D> pProcessor(drawinglayer::processor2d::createProcessor2DFromOutputDevice(
+ rDestinationDevice,
+ getCurrentViewInformation2D()));
+
+ for(const auto& rpOverlayObject : maOverlayObjects)
+ {
+ OSL_ENSURE(rpOverlayObject, "Corrupted OverlayObject List (!)");
+ const OverlayObject& rCandidate = *rpOverlayObject;
+
+ if(rCandidate.isVisible())
+ {
+ const drawinglayer::primitive2d::Primitive2DContainer& rSequence = rCandidate.getOverlayObjectPrimitive2DSequence();
+
+ if(!rSequence.empty())
+ {
+ if(rRange.overlaps(rCandidate.getBaseRange()))
+ {
+ if(bIsAntiAliasing && rCandidate.allowsAntiAliase())
+ {
+ rDestinationDevice.SetAntialiasing(nOriginalAA | AntialiasingFlags::Enable);
+ }
+ else
+ {
+ rDestinationDevice.SetAntialiasing(nOriginalAA & ~AntialiasingFlags::Enable);
+ }
+
+ pProcessor->process(rSequence);
+ }
+ }
+ }
+ }
+
+ pProcessor.reset();
+
+ // restore AA settings
+ rDestinationDevice.SetAntialiasing(nOriginalAA);
+ }
+
+ void OverlayManager::ImpStripeDefinitionChanged()
+ {
+ const sal_uInt32 nSize(maOverlayObjects.size());
+
+ if(nSize)
+ {
+ for(const auto& rpOverlayObject : maOverlayObjects)
+ {
+ OSL_ENSURE(rpOverlayObject, "Corrupted OverlayObject List (!)");
+ OverlayObject& rCandidate = *rpOverlayObject;
+ rCandidate.stripeDefinitionHasChanged();
+ }
+ }
+ }
+
+ double OverlayManager::getDiscreteOne() const
+ {
+ if(basegfx::fTools::equalZero(mfDiscreteOne))
+ {
+ const basegfx::B2DVector aDiscreteInLogic(getOutputDevice().GetInverseViewTransformation() * basegfx::B2DVector(1.0, 0.0));
+ const_cast< OverlayManager* >(this)->mfDiscreteOne = aDiscreteInLogic.getLength();
+ }
+
+ return mfDiscreteOne;
+ }
+
+ OverlayManager::OverlayManager(OutputDevice& rOutputDevice)
+ : mrOutputDevice(rOutputDevice),
+ maStripeColorA(COL_BLACK),
+ maStripeColorB(COL_WHITE),
+ mnStripeLengthPixel(5),
+ mfDiscreteOne(0.0)
+ {
+ // set Property 'ReducedDisplayQuality' to true to allow simpler interaction
+ // visualisations
+ uno::Sequence< beans::PropertyValue > xProperties{
+ comphelper::makePropertyValue("ReducedDisplayQuality", true)
+ };
+ maViewInformation2D = drawinglayer::geometry::createViewInformation2D(xProperties);
+ }
+
+ rtl::Reference<OverlayManager> OverlayManager::create(OutputDevice& rOutputDevice)
+ {
+ return rtl::Reference<OverlayManager>(new OverlayManager(rOutputDevice));
+ }
+
+ drawinglayer::geometry::ViewInformation2D const & OverlayManager::getCurrentViewInformation2D() const
+ {
+ if(getOutputDevice().GetViewTransformation() != maViewTransformation)
+ {
+ basegfx::B2DRange aViewRange(maViewInformation2D.getViewport());
+
+ if(OUTDEV_WINDOW == getOutputDevice().GetOutDevType())
+ {
+ const Size aOutputSizePixel(getOutputDevice().GetOutputSizePixel());
+
+ // only set when we *have* an output size, else let aViewRange
+ // stay on empty
+ if(aOutputSizePixel.Width() && aOutputSizePixel.Height())
+ {
+ aViewRange = basegfx::B2DRange(0.0, 0.0, aOutputSizePixel.getWidth(), aOutputSizePixel.getHeight());
+ aViewRange.transform(getOutputDevice().GetInverseViewTransformation());
+ }
+ }
+
+ OverlayManager* pThis = const_cast< OverlayManager* >(this);
+
+ pThis->maViewTransformation = getOutputDevice().GetViewTransformation();
+ pThis->maViewInformation2D = drawinglayer::geometry::ViewInformation2D(
+ maViewInformation2D.getObjectTransformation(),
+ maViewTransformation,
+ aViewRange,
+ maViewInformation2D.getVisualizedPage(),
+ maViewInformation2D.getViewTime());
+ pThis->mfDiscreteOne = 0.0;
+ }
+
+ return maViewInformation2D;
+ }
+
+ void OverlayManager::impApplyRemoveActions(OverlayObject& rTarget)
+ {
+ // handle evtl. animation
+ if(rTarget.allowsAnimation())
+ {
+ // remove from event chain
+ RemoveEvent(&rTarget);
+ }
+
+ // make invisible
+ invalidateRange(rTarget.getBaseRange());
+
+ // clear manager
+ rTarget.mpOverlayManager = nullptr;
+ }
+
+ void OverlayManager::impApplyAddActions(OverlayObject& rTarget)
+ {
+ // set manager
+ rTarget.mpOverlayManager = this;
+
+ // make visible
+ invalidateRange(rTarget.getBaseRange());
+
+ // handle evtl. animation
+ if(rTarget.allowsAnimation())
+ {
+ // Trigger at current time to get alive. This will do the
+ // object-specific next time calculation and hand over adding
+ // again to the scheduler to the animated object, too. This works for
+ // a paused or non-paused animator.
+ rTarget.Trigger(GetTime());
+ }
+ }
+
+ OverlayManager::~OverlayManager()
+ {
+ // The OverlayManager is not the owner of the OverlayObjects
+ // and thus will not delete them, but remove them. Profit here
+ // from knowing that all will be removed
+ const sal_uInt32 nSize(maOverlayObjects.size());
+
+ if(nSize)
+ {
+ for(const auto& rpOverlayObject : maOverlayObjects)
+ {
+ OSL_ENSURE(rpOverlayObject, "Corrupted OverlayObject List (!)");
+ OverlayObject& rCandidate = *rpOverlayObject;
+ impApplyRemoveActions(rCandidate);
+ }
+
+ // erase vector
+ maOverlayObjects.clear();
+ }
+ }
+
+ void OverlayManager::completeRedraw(const vcl::Region& rRegion, OutputDevice* pPreRenderDevice) const
+ {
+ if(rRegion.IsEmpty() || maOverlayObjects.empty())
+ return;
+
+ // check for changed MapModes. That may influence the
+ // logical size of pixel based OverlayObjects (like BitmapHandles)
+ //ImpCheckMapModeChange();
+
+ // paint members
+ const tools::Rectangle aRegionBoundRect(rRegion.GetBoundRect());
+ const basegfx::B2DRange aRegionRange = vcl::unotools::b2DRectangleFromRectangle(aRegionBoundRect);
+
+ OutputDevice& rTarget = pPreRenderDevice ? *pPreRenderDevice : getOutputDevice();
+ ImpDrawMembers(aRegionRange, rTarget);
+ }
+
+ void OverlayManager::flush()
+ {
+ // default has nothing to do
+ }
+
+ void OverlayManager::add(OverlayObject& rOverlayObject)
+ {
+ OSL_ENSURE(nullptr == rOverlayObject.mpOverlayManager, "OverlayObject is added twice to an OverlayManager (!)");
+
+ // add to the end of chain to preserve display order in paint
+ maOverlayObjects.push_back(&rOverlayObject);
+
+ // execute add actions
+ impApplyAddActions(rOverlayObject);
+ }
+
+ void OverlayManager::remove(OverlayObject& rOverlayObject)
+ {
+ OSL_ENSURE(rOverlayObject.mpOverlayManager == this, "OverlayObject is removed from wrong OverlayManager (!)");
+
+ // execute remove actions
+ impApplyRemoveActions(rOverlayObject);
+
+ // remove from vector
+ const OverlayObjectVector::iterator aFindResult = ::std::find(maOverlayObjects.begin(), maOverlayObjects.end(), &rOverlayObject);
+ const bool bFound(aFindResult != maOverlayObjects.end());
+ OSL_ENSURE(bFound, "OverlayObject NOT found at OverlayManager (!)");
+
+ if(bFound)
+ {
+ maOverlayObjects.erase(aFindResult);
+ }
+ }
+
+ tools::Rectangle OverlayManager::RangeToInvalidateRectangle(const basegfx::B2DRange& rRange) const
+ {
+ if (rRange.isEmpty()) {
+ return {};
+ }
+ if (SvtOptionsDrawinglayer::IsAntiAliasing())
+ {
+ // assume AA needs one pixel more and invalidate one pixel more
+ const double fDiscreteOne(getDiscreteOne());
+ const tools::Rectangle aInvalidateRectangle(
+ static_cast<tools::Long>(floor(rRange.getMinX() - fDiscreteOne)),
+ static_cast<tools::Long>(floor(rRange.getMinY() - fDiscreteOne)),
+ static_cast<tools::Long>(ceil(rRange.getMaxX() + fDiscreteOne)),
+ static_cast<tools::Long>(ceil(rRange.getMaxY() + fDiscreteOne)));
+ return aInvalidateRectangle;
+ }
+ else
+ {
+ // #i77674# transform to rectangle. Use floor/ceil to get all covered
+ // discrete pixels, see #i75163# and OverlayManagerBuffered::invalidateRange
+ const tools::Rectangle aInvalidateRectangle(
+ static_cast<sal_Int32>(floor(rRange.getMinX())), static_cast<sal_Int32>(floor(rRange.getMinY())),
+ static_cast<sal_Int32>(ceil(rRange.getMaxX())), static_cast<sal_Int32>(ceil(rRange.getMaxY())));
+ return aInvalidateRectangle;
+ }
+ }
+
+ void OverlayManager::invalidateRange(const basegfx::B2DRange& rRange)
+ {
+ if (OUTDEV_WINDOW == getOutputDevice().GetOutDevType())
+ {
+ tools::Rectangle aInvalidateRectangle(RangeToInvalidateRectangle(rRange));
+ // simply invalidate
+ getOutputDevice().GetOwnerWindow()->Invalidate(aInvalidateRectangle, InvalidateFlags::NoErase);
+ }
+ }
+
+ // stripe support ColA
+ void OverlayManager::setStripeColorA(Color aNew)
+ {
+ if(aNew != maStripeColorA)
+ {
+ maStripeColorA = aNew;
+ ImpStripeDefinitionChanged();
+ }
+ }
+
+ // stripe support ColB
+ void OverlayManager::setStripeColorB(Color aNew)
+ {
+ if(aNew != maStripeColorB)
+ {
+ maStripeColorB = aNew;
+ ImpStripeDefinitionChanged();
+ }
+ }
+
+ // stripe support StripeLengthPixel
+ void OverlayManager::setStripeLengthPixel(sal_uInt32 nNew)
+ {
+ if(nNew != mnStripeLengthPixel)
+ {
+ mnStripeLengthPixel = nNew;
+ ImpStripeDefinitionChanged();
+ }
+ }
+
+} // end of namespace
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/sdr/overlay/overlaymanagerbuffered.cxx b/svx/source/sdr/overlay/overlaymanagerbuffered.cxx
new file mode 100644
index 000000000..ae37c2ce4
--- /dev/null
+++ b/svx/source/sdr/overlay/overlaymanagerbuffered.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 <sdr/overlay/overlaymanagerbuffered.hxx>
+#include <svx/sdrpaintwindow.hxx>
+#include <vcl/outdev.hxx>
+#include <basegfx/range/b2drange.hxx>
+#include <vcl/window.hxx>
+#include <tools/fract.hxx>
+#include <vcl/cursor.hxx>
+#include <svtools/optionsdrawinglayer.hxx>
+
+
+namespace sdr::overlay
+{
+ void OverlayManagerBuffered::ImpPrepareBufferDevice()
+ {
+ // compare size of mpBufferDevice with size of visible area
+ if(mpBufferDevice->GetOutputSizePixel() != getOutputDevice().GetOutputSizePixel())
+ {
+ // set new buffer size, copy as much content as possible (use bool parameter for vcl).
+ // Newly uncovered regions will be repainted.
+ mpBufferDevice->SetOutputSizePixel(getOutputDevice().GetOutputSizePixel(), false);
+ }
+
+ // compare the MapModes for zoom/scroll changes
+ if(mpBufferDevice->GetMapMode() != getOutputDevice().GetMapMode())
+ {
+ const bool bZoomed(
+ mpBufferDevice->GetMapMode().GetScaleX() != getOutputDevice().GetMapMode().GetScaleX()
+ || mpBufferDevice->GetMapMode().GetScaleY() != getOutputDevice().GetMapMode().GetScaleY());
+
+ if(!bZoomed)
+ {
+ const Point& rOriginOld = mpBufferDevice->GetMapMode().GetOrigin();
+ const Point& rOriginNew = getOutputDevice().GetMapMode().GetOrigin();
+ const bool bScrolled(rOriginOld != rOriginNew);
+
+
+ if(bScrolled)
+ {
+ // get pixel bounds (tdf#149322 do subtraction in logic units before converting result back to pixel)
+ const Point aLogicOriginDiff(rOriginNew - rOriginOld);
+ const Size aPixelOriginDiff(mpBufferDevice->LogicToPixel(Size(aLogicOriginDiff.X(), aLogicOriginDiff.Y())));
+ const Point aDestinationOffsetPixel(aPixelOriginDiff.Width(), aPixelOriginDiff.Height());
+ const Size aOutputSizePixel(mpBufferDevice->GetOutputSizePixel());
+
+ // remember and switch off MapMode
+ const bool bMapModeWasEnabled(mpBufferDevice->IsMapModeEnabled());
+ mpBufferDevice->EnableMapMode(false);
+
+ // scroll internally buffered stuff
+ mpBufferDevice->DrawOutDev(
+ aDestinationOffsetPixel, aOutputSizePixel, // destination
+ Point(), aOutputSizePixel); // source
+
+ // restore MapMode
+ mpBufferDevice->EnableMapMode(bMapModeWasEnabled);
+
+ // scroll remembered region, too.
+ if(!maBufferRememberedRangePixel.isEmpty())
+ {
+ const basegfx::B2IPoint aIPointDestinationOffsetPixel(aDestinationOffsetPixel.X(), aDestinationOffsetPixel.Y());
+ const basegfx::B2IPoint aNewMinimum(maBufferRememberedRangePixel.getMinimum() + aIPointDestinationOffsetPixel);
+ const basegfx::B2IPoint aNewMaximum(maBufferRememberedRangePixel.getMaximum() + aIPointDestinationOffsetPixel);
+ maBufferRememberedRangePixel = basegfx::B2IRange(aNewMinimum, aNewMaximum);
+ }
+ }
+ }
+
+ // copy new MapMode
+ mpBufferDevice->SetMapMode(getOutputDevice().GetMapMode());
+ }
+
+ // #i29186#
+ mpBufferDevice->SetDrawMode(getOutputDevice().GetDrawMode());
+ mpBufferDevice->SetSettings(getOutputDevice().GetSettings());
+ mpBufferDevice->SetAntialiasing(getOutputDevice().GetAntialiasing());
+ }
+
+ void OverlayManagerBuffered::ImpRestoreBackground() const
+ {
+ const tools::Rectangle aRegionRectanglePixel(
+ maBufferRememberedRangePixel.getMinX(), maBufferRememberedRangePixel.getMinY(),
+ maBufferRememberedRangePixel.getMaxX(), maBufferRememberedRangePixel.getMaxY());
+ const vcl::Region aRegionPixel(aRegionRectanglePixel);
+
+ ImpRestoreBackground(aRegionPixel);
+ }
+
+ void OverlayManagerBuffered::ImpRestoreBackground(const vcl::Region& rRegionPixel) const
+ {
+ // MapModes off
+ const bool bMapModeWasEnabledDest(getOutputDevice().IsMapModeEnabled());
+ const bool bMapModeWasEnabledSource(mpBufferDevice->IsMapModeEnabled());
+ getOutputDevice().EnableMapMode(false);
+ const_cast<OverlayManagerBuffered*>(this)->mpBufferDevice->EnableMapMode(false);
+
+ // local region
+ RectangleVector aRectangles;
+ rRegionPixel.GetRegionRectangles(aRectangles);
+
+ for(const auto& rRect : aRectangles)
+ {
+ // restore the area
+ const Point aTopLeft(rRect.TopLeft());
+ const Size aSize(rRect.GetSize());
+
+ getOutputDevice().DrawOutDev(
+ aTopLeft, aSize, // destination
+ aTopLeft, aSize, // source
+ *mpBufferDevice);
+ }
+
+ // restore MapModes
+ getOutputDevice().EnableMapMode(bMapModeWasEnabledDest);
+ const_cast<OverlayManagerBuffered*>(this)->mpBufferDevice->EnableMapMode(bMapModeWasEnabledSource);
+ }
+
+ void OverlayManagerBuffered::ImpSaveBackground(const vcl::Region& rRegion, OutputDevice* pPreRenderDevice)
+ {
+ // prepare source
+ OutputDevice& rSource = pPreRenderDevice ? *pPreRenderDevice : getOutputDevice();
+
+ // Ensure buffer is valid
+ ImpPrepareBufferDevice();
+
+ // build region which needs to be copied
+ vcl::Region aRegion(rSource.LogicToPixel(rRegion));
+
+ // limit to PaintRegion if it's a window. This will be evtl. the expanded one,
+ // but always the exact redraw area
+ if(OUTDEV_WINDOW == rSource.GetOutDevType())
+ {
+ vcl::Window& rWindow = *rSource.GetOwnerWindow();
+ vcl::Region aPaintRegionPixel = rWindow.LogicToPixel(rWindow.GetPaintRegion());
+ aRegion.Intersect(aPaintRegionPixel);
+
+ // #i72754# Make sure content is completely rendered, the window
+ // will be used as source of a DrawOutDev soon
+ rWindow.GetOutDev()->Flush();
+ }
+
+ // also limit to buffer size
+ const tools::Rectangle aBufferDeviceRectanglePixel(Point(), mpBufferDevice->GetOutputSizePixel());
+ aRegion.Intersect(aBufferDeviceRectanglePixel);
+
+ // MapModes off
+ const bool bMapModeWasEnabledDest(rSource.IsMapModeEnabled());
+ const bool bMapModeWasEnabledSource(mpBufferDevice->IsMapModeEnabled());
+ rSource.EnableMapMode(false);
+ mpBufferDevice->EnableMapMode(false);
+
+ // prepare to iterate over the rectangles from the region in pixels
+ RectangleVector aRectangles;
+ aRegion.GetRegionRectangles(aRectangles);
+
+ for(const auto& rRect : aRectangles)
+ {
+ // for each rectangle, save the area
+ const Point aTopLeft(rRect.TopLeft());
+ const Size aSize(rRect.GetSize());
+
+ mpBufferDevice->DrawOutDev(
+ aTopLeft, aSize, // destination
+ aTopLeft, aSize, // source
+ rSource);
+ }
+
+ // restore MapModes
+ rSource.EnableMapMode(bMapModeWasEnabledDest);
+ mpBufferDevice->EnableMapMode(bMapModeWasEnabledSource);
+ }
+
+ IMPL_LINK_NOARG(OverlayManagerBuffered, ImpBufferTimerHandler, Timer*, void)
+ {
+ //Resolves: fdo#46728 ensure this exists until end of scope
+ rtl::Reference<OverlayManager> xKeepAlive(this);
+
+ // stop timer
+ maBufferIdle.Stop();
+
+ if(maBufferRememberedRangePixel.isEmpty())
+ return;
+
+ // logic size for impDrawMember call
+ basegfx::B2DRange aBufferRememberedRangeLogic(
+ maBufferRememberedRangePixel.getMinX(), maBufferRememberedRangePixel.getMinY(),
+ maBufferRememberedRangePixel.getMaxX(), maBufferRememberedRangePixel.getMaxY());
+ aBufferRememberedRangeLogic.transform(getOutputDevice().GetInverseViewTransformation());
+
+ // prepare cursor handling
+ const bool bTargetIsWindow(OUTDEV_WINDOW == mrOutputDevice.GetOutDevType());
+ bool bCursorWasEnabled(false);
+
+ // #i80730# switch off VCL cursor during overlay refresh
+ if(bTargetIsWindow)
+ {
+ vcl::Window& rWindow = *mrOutputDevice.GetOwnerWindow();
+ vcl::Cursor* pCursor = rWindow.GetCursor();
+
+ if(pCursor && pCursor->IsVisible())
+ {
+ pCursor->Hide();
+ bCursorWasEnabled = true;
+ }
+ }
+
+ // refresh with prerendering
+ {
+ // #i73602# ensure valid and sized mpOutputBufferDevice
+ const Size aDestinationSizePixel(mpBufferDevice->GetOutputSizePixel());
+ const Size aOutputBufferSizePixel(mpOutputBufferDevice->GetOutputSizePixel());
+
+ if(aDestinationSizePixel != aOutputBufferSizePixel)
+ {
+ mpOutputBufferDevice->SetOutputSizePixel(aDestinationSizePixel);
+ }
+
+ mpOutputBufferDevice->SetMapMode(getOutputDevice().GetMapMode());
+ mpOutputBufferDevice->EnableMapMode(false);
+ mpOutputBufferDevice->SetDrawMode(mpBufferDevice->GetDrawMode());
+ mpOutputBufferDevice->SetSettings(mpBufferDevice->GetSettings());
+ mpOutputBufferDevice->SetAntialiasing(mpBufferDevice->GetAntialiasing());
+
+ // calculate sizes
+ tools::Rectangle aRegionRectanglePixel(
+ maBufferRememberedRangePixel.getMinX(), maBufferRememberedRangePixel.getMinY(),
+ maBufferRememberedRangePixel.getMaxX(), maBufferRememberedRangePixel.getMaxY());
+
+ // truncate aRegionRectanglePixel to destination pixel size, more does
+ // not need to be prepared since destination is a buffer for a window. So,
+ // maximum size indirectly shall be limited to getOutputDevice().GetOutputSizePixel()
+ if(aRegionRectanglePixel.Left() < 0)
+ {
+ aRegionRectanglePixel.SetLeft( 0 );
+ }
+
+ if(aRegionRectanglePixel.Top() < 0)
+ {
+ aRegionRectanglePixel.SetTop( 0 );
+ }
+
+ if(aRegionRectanglePixel.Right() > aDestinationSizePixel.getWidth())
+ {
+ aRegionRectanglePixel.SetRight( aDestinationSizePixel.getWidth() );
+ }
+
+ if(aRegionRectanglePixel.Bottom() > aDestinationSizePixel.getHeight())
+ {
+ aRegionRectanglePixel.SetBottom( aDestinationSizePixel.getHeight() );
+ }
+
+ // get sizes
+ const Point aTopLeft(aRegionRectanglePixel.TopLeft());
+ const Size aSize(aRegionRectanglePixel.GetSize());
+
+ {
+ const bool bMapModeWasEnabledDest(mpBufferDevice->IsMapModeEnabled());
+ mpBufferDevice->EnableMapMode(false);
+
+ mpOutputBufferDevice->DrawOutDev(
+ aTopLeft, aSize, // destination
+ aTopLeft, aSize, // source
+ *mpBufferDevice);
+
+ // restore MapModes
+ mpBufferDevice->EnableMapMode(bMapModeWasEnabledDest);
+ }
+
+ // paint overlay content for remembered region, use
+ // method from base class directly
+ mpOutputBufferDevice->EnableMapMode();
+ OverlayManager::ImpDrawMembers(aBufferRememberedRangeLogic, *mpOutputBufferDevice);
+ mpOutputBufferDevice->EnableMapMode(false);
+
+ // copy to output
+ {
+ const bool bMapModeWasEnabledDest(getOutputDevice().IsMapModeEnabled());
+ getOutputDevice().EnableMapMode(false);
+
+ getOutputDevice().DrawOutDev(
+ aTopLeft, aSize, // destination
+ aTopLeft, aSize, // source
+ *mpOutputBufferDevice);
+
+ // debug
+ /*getOutputDevice().SetLineCOL_RED);
+ getOutputDevice().SetFillColor();
+ getOutputDevice().DrawRect(Rectangle(aTopLeft, aSize));*/
+
+ // restore MapModes
+ getOutputDevice().EnableMapMode(bMapModeWasEnabledDest);
+ }
+ }
+
+ // VCL hack for transparent child windows
+ // Problem is e.g. a radiobutton form control in life mode. The used window
+ // is a transparence vcl childwindow. This flag only allows the parent window to
+ // paint into the child windows area, but there is no mechanism which takes
+ // care for a repaint of the child window. A transparent child window is NOT
+ // a window which always keeps it's content consistent over the parent, but it's
+ // more like just a paint flag for the parent.
+ // To get the update, the windows in question are updated manually here.
+ if(bTargetIsWindow)
+ {
+ vcl::Window& rWindow = *mrOutputDevice.GetOwnerWindow();
+
+ const tools::Rectangle aRegionRectanglePixel(
+ maBufferRememberedRangePixel.getMinX(),
+ maBufferRememberedRangePixel.getMinY(),
+ maBufferRememberedRangePixel.getMaxX(),
+ maBufferRememberedRangePixel.getMaxY());
+ PaintTransparentChildren(rWindow, aRegionRectanglePixel);
+ }
+
+ // #i80730# restore visibility of VCL cursor
+ if(bCursorWasEnabled)
+ {
+ vcl::Window& rWindow = *mrOutputDevice.GetOwnerWindow();
+ vcl::Cursor* pCursor = rWindow.GetCursor();
+
+ if(pCursor)
+ {
+ // check if cursor still exists. It may have been deleted from someone
+ pCursor->Show();
+ }
+ }
+
+ // forget remembered Region
+ maBufferRememberedRangePixel.reset();
+ }
+
+ OverlayManagerBuffered::OverlayManagerBuffered(
+ OutputDevice& rOutputDevice)
+ : OverlayManager(rOutputDevice),
+ mpBufferDevice(VclPtr<VirtualDevice>::Create()),
+ mpOutputBufferDevice(VclPtr<VirtualDevice>::Create()),
+ maBufferIdle( "sdr::overlay::OverlayManagerBuffered maBufferIdle" )
+ {
+ // Init timer
+ maBufferIdle.SetPriority( TaskPriority::POST_PAINT );
+ maBufferIdle.SetInvokeHandler(LINK(this, OverlayManagerBuffered, ImpBufferTimerHandler));
+ }
+
+ rtl::Reference<OverlayManager> OverlayManagerBuffered::create(
+ OutputDevice& rOutputDevice)
+ {
+ return rtl::Reference<OverlayManager>(new OverlayManagerBuffered(rOutputDevice));
+ }
+
+ OverlayManagerBuffered::~OverlayManagerBuffered()
+ {
+ // Clear timer
+ maBufferIdle.Stop();
+
+ if(!maBufferRememberedRangePixel.isEmpty())
+ {
+ // Restore all rectangles for remembered region from buffer
+ ImpRestoreBackground();
+ }
+ }
+
+ void OverlayManagerBuffered::completeRedraw(const vcl::Region& rRegion, OutputDevice* pPreRenderDevice) const
+ {
+ if(!rRegion.IsEmpty())
+ {
+ // save new background
+ const_cast<OverlayManagerBuffered*>(this)->ImpSaveBackground(rRegion, pPreRenderDevice);
+ }
+
+ // call parent
+ OverlayManager::completeRedraw(rRegion, pPreRenderDevice);
+ }
+
+ void OverlayManagerBuffered::flush()
+ {
+ // call timer handler direct
+ ImpBufferTimerHandler(nullptr);
+ }
+
+ void OverlayManagerBuffered::invalidateRange(const basegfx::B2DRange& rRange)
+ {
+ if(rRange.isEmpty())
+ return;
+
+ // buffered output, do not invalidate but use the timer
+ // to trigger a timer event for refresh
+ maBufferIdle.Start();
+
+ // add the discrete range to the remembered region
+ // #i75163# use double precision and floor/ceil rounding to get overlapped pixel region, even
+ // when the given logic region has a width/height of 0.0. This does NOT work with LogicToPixel
+ // since it just transforms the top left and bottom right points equally without taking
+ // discrete pixel coverage into account. An empty B2DRange and thus empty logic Rectangle translated
+ // to an also empty discrete pixel rectangle - what is wrong.
+ basegfx::B2DRange aDiscreteRange(rRange);
+ aDiscreteRange.transform(getOutputDevice().GetViewTransformation());
+
+ if(SvtOptionsDrawinglayer::IsAntiAliasing())
+ {
+ // assume AA needs one pixel more and invalidate one pixel more
+ const double fDiscreteOne(getDiscreteOne());
+ const basegfx::B2IPoint aTopLeft(
+ static_cast<sal_Int32>(floor(aDiscreteRange.getMinX() - fDiscreteOne)),
+ static_cast<sal_Int32>(floor(aDiscreteRange.getMinY() - fDiscreteOne)));
+ const basegfx::B2IPoint aBottomRight(
+ static_cast<sal_Int32>(ceil(aDiscreteRange.getMaxX() + fDiscreteOne)),
+ static_cast<sal_Int32>(ceil(aDiscreteRange.getMaxY() + fDiscreteOne)));
+
+ maBufferRememberedRangePixel.expand(aTopLeft);
+ maBufferRememberedRangePixel.expand(aBottomRight);
+ }
+ else
+ {
+ const basegfx::B2IPoint aTopLeft(static_cast<sal_Int32>(floor(aDiscreteRange.getMinX())), static_cast<sal_Int32>(floor(aDiscreteRange.getMinY())));
+ const basegfx::B2IPoint aBottomRight(static_cast<sal_Int32>(ceil(aDiscreteRange.getMaxX())), static_cast<sal_Int32>(ceil(aDiscreteRange.getMaxY())));
+
+ maBufferRememberedRangePixel.expand(aTopLeft);
+ maBufferRememberedRangePixel.expand(aBottomRight);
+ }
+ }
+} // end of namespace
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/sdr/overlay/overlayobject.cxx b/svx/source/sdr/overlay/overlayobject.cxx
new file mode 100644
index 000000000..4fd227977
--- /dev/null
+++ b/svx/source/sdr/overlay/overlayobject.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 <svx/sdr/overlay/overlayobject.hxx>
+#include <svx/sdr/overlay/overlaymanager.hxx>
+#include <basegfx/matrix/b2dhommatrix.hxx>
+#include <basegfx/vector/b2dvector.hxx>
+#include <basegfx/matrix/b2dhommatrixtools.hxx>
+#include <drawinglayer/primitive2d/transformprimitive2d.hxx>
+#include <osl/diagnose.h>
+
+namespace sdr::overlay
+{
+ void OverlayObject::objectChange()
+ {
+ const basegfx::B2DRange aPreviousRange(maBaseRange);
+ maBaseRange.reset();
+ resetPrimitive2DSequence();
+// setPrimitive2DSequence(drawinglayer::primitive2d::Primitive2DContainer());
+
+ if(getOverlayManager() && !aPreviousRange.isEmpty())
+ {
+ getOverlayManager()->invalidateRange(aPreviousRange);
+ }
+
+ const basegfx::B2DRange& rCurrentRange = getBaseRange();
+
+ if(getOverlayManager() && rCurrentRange != aPreviousRange && !rCurrentRange.isEmpty())
+ {
+ getOverlayManager()->invalidateRange(rCurrentRange);
+ }
+ }
+
+ // OverlayObject implementations.
+ drawinglayer::primitive2d::Primitive2DContainer OverlayObject::createOverlayObjectPrimitive2DSequence()
+ {
+ // Default implementation has to assert a missing implementation. It cannot
+ // be useful to have overlay object derivations which have no visualisation
+ // at all
+ OSL_FAIL("OverlayObject derivation without visualisation definition (missing createOverlayObjectPrimitive2DSequence implementation) (!)");
+ return drawinglayer::primitive2d::Primitive2DContainer();
+ }
+
+ sal_uInt32 OverlayObject::impCheckBlinkTimeValueRange(sal_uInt64 nBlinkTime)
+ {
+ if(nBlinkTime < 25)
+ {
+ nBlinkTime = 25;
+ }
+ else if(nBlinkTime > 10000)
+ {
+ nBlinkTime = 10000;
+ }
+
+ return nBlinkTime;
+ }
+
+ void OverlayObject::allowAntiAliase(bool bNew)
+ {
+ if(bNew != mbAllowsAntiAliase)
+ {
+ // remember new value
+ mbAllowsAntiAliase = bNew;
+
+ // register change (after change)
+ objectChange();
+ }
+ }
+
+ OverlayObject::OverlayObject(Color aBaseColor)
+ : mpOverlayManager(nullptr),
+ maOffset(0.0, 0.0),
+ maBaseColor(aBaseColor),
+ mbIsVisible(true),
+ mbIsHittable(true),
+ mbAllowsAnimation(false),
+ mbAllowsAntiAliase(true)
+ {
+ }
+
+ OverlayObject::~OverlayObject()
+ {
+ OSL_ENSURE(nullptr == getOverlayManager(), "OverlayObject is destructed which is still registered at OverlayManager (!)");
+ }
+
+ drawinglayer::primitive2d::Primitive2DContainer OverlayObject::getOverlayObjectPrimitive2DSequence() const
+ {
+ if(getPrimitive2DSequence().empty())
+ {
+ // no existing sequence; create one
+ const_cast< OverlayObject* >(this)->maPrimitive2DSequence = const_cast< OverlayObject* >(this)->createOverlayObjectPrimitive2DSequence();
+
+ if(!getOffset().equalZero())
+ {
+ // embed to offset transformation
+ const basegfx::B2DHomMatrix aTranslateGridOffset(
+ basegfx::utils::createTranslateB2DHomMatrix(
+ getOffset()));
+ drawinglayer::primitive2d::Primitive2DReference aEmbed(
+ new drawinglayer::primitive2d::TransformPrimitive2D(
+ aTranslateGridOffset,
+ std::move(const_cast<drawinglayer::primitive2d::Primitive2DContainer&>(maPrimitive2DSequence))));
+
+ const_cast< OverlayObject* >(this)->maPrimitive2DSequence = drawinglayer::primitive2d::Primitive2DContainer { aEmbed };
+ }
+ }
+
+ return getPrimitive2DSequence();
+ }
+
+ const basegfx::B2DRange& OverlayObject::getBaseRange() const
+ {
+ if(getOverlayManager() && maBaseRange.isEmpty())
+ {
+ const drawinglayer::primitive2d::Primitive2DContainer& rSequence = getOverlayObjectPrimitive2DSequence();
+
+ if(!rSequence.empty())
+ {
+ const drawinglayer::geometry::ViewInformation2D & aViewInformation2D(getOverlayManager()->getCurrentViewInformation2D());
+
+ const_cast< sdr::overlay::OverlayObject* >(this)->maBaseRange =
+ rSequence.getB2DRange(aViewInformation2D);
+ }
+ }
+
+ return maBaseRange;
+ }
+
+ void OverlayObject::setVisible(bool bNew)
+ {
+ if(bNew != mbIsVisible)
+ {
+ // remember new value
+ mbIsVisible = bNew;
+
+ // register change (after change)
+ objectChange();
+ }
+ }
+
+ void OverlayObject::setHittable(bool bNew)
+ {
+ if(bNew != mbIsHittable)
+ {
+ // remember new value
+ mbIsHittable = bNew;
+
+ // register change (after change)
+ objectChange();
+ }
+ }
+
+ void OverlayObject::setBaseColor(Color aNew)
+ {
+ if(aNew != maBaseColor)
+ {
+ // remember new value
+ maBaseColor = aNew;
+
+ // register change (after change)
+ objectChange();
+ }
+ }
+
+ void OverlayObject::setOffset(const basegfx::B2DVector& rOffset)
+ {
+ if(rOffset != maOffset)
+ {
+ // remember new value
+ maOffset = rOffset;
+
+ // register change (after change)
+ objectChange();
+ }
+ }
+
+ void OverlayObject::Trigger(sal_uInt32 /*nTime*/)
+ {
+ // default does not register again
+ }
+
+ void OverlayObject::stripeDefinitionHasChanged()
+ {
+ // default does not need to do anything
+ }
+
+
+ OverlayObjectWithBasePosition::OverlayObjectWithBasePosition(const basegfx::B2DPoint& rBasePos, Color aBaseColor)
+ : OverlayObject(aBaseColor),
+ maBasePosition(rBasePos)
+ {
+ }
+
+ OverlayObjectWithBasePosition::~OverlayObjectWithBasePosition()
+ {
+ }
+
+ void OverlayObjectWithBasePosition::setBasePosition(const basegfx::B2DPoint& rNew)
+ {
+ if(rNew != maBasePosition)
+ {
+ // remember new value
+ maBasePosition = rNew;
+
+ // register change (after change)
+ objectChange();
+ }
+ }
+
+} // end of namespace
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/sdr/overlay/overlayobjectcell.cxx b/svx/source/sdr/overlay/overlayobjectcell.cxx
new file mode 100644
index 000000000..a7da6a299
--- /dev/null
+++ b/svx/source/sdr/overlay/overlayobjectcell.cxx
@@ -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 .
+ */
+
+
+#include <basegfx/numeric/ftools.hxx>
+#include <sdr/overlay/overlayobjectcell.hxx>
+#include <basegfx/polygon/b2dpolygontools.hxx>
+#include <basegfx/polygon/b2dpolygon.hxx>
+#include <drawinglayer/primitive2d/PolyPolygonColorPrimitive2D.hxx>
+#include <drawinglayer/primitive2d/unifiedtransparenceprimitive2d.hxx>
+
+using namespace ::basegfx;
+
+namespace sdr::overlay
+{
+ OverlayObjectCell::OverlayObjectCell( const Color& rColor, RangeVector&& rRects )
+ : OverlayObject( rColor ),
+ maRectangles( std::move(rRects) )
+ {
+ // no AA for selection overlays
+ allowAntiAliase(false);
+ }
+
+ OverlayObjectCell::~OverlayObjectCell()
+ {
+ }
+
+ drawinglayer::primitive2d::Primitive2DContainer OverlayObjectCell::createOverlayObjectPrimitive2DSequence()
+ {
+ drawinglayer::primitive2d::Primitive2DContainer aRetval;
+ const sal_uInt32 nCount(maRectangles.size());
+
+ if(nCount)
+ {
+ const basegfx::BColor aRGBColor(getBaseColor().getBColor());
+ aRetval.resize(nCount);
+
+ // create primitives for all ranges
+ for(sal_uInt32 a(0); a < nCount; a++)
+ {
+ const basegfx::B2DRange& rRange(maRectangles[a]);
+ const basegfx::B2DPolygon aPolygon(basegfx::utils::createPolygonFromRect(rRange));
+
+ aRetval[a] = drawinglayer::primitive2d::Primitive2DReference(
+ new drawinglayer::primitive2d::PolyPolygonColorPrimitive2D(
+ basegfx::B2DPolyPolygon(aPolygon),
+ aRGBColor));
+ }
+
+
+ // embed in 50% transparent paint
+ drawinglayer::primitive2d::Primitive2DReference aUnifiedTransparence(
+ new drawinglayer::primitive2d::UnifiedTransparencePrimitive2D(
+ std::move(aRetval),
+ 0.5));
+
+ aRetval = drawinglayer::primitive2d::Primitive2DContainer { aUnifiedTransparence };
+ }
+
+ return aRetval;
+ }
+
+} // end of namespace
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/sdr/overlay/overlayobjectlist.cxx b/svx/source/sdr/overlay/overlayobjectlist.cxx
new file mode 100644
index 000000000..d71f6c278
--- /dev/null
+++ b/svx/source/sdr/overlay/overlayobjectlist.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 <svx/sdr/overlay/overlayobjectlist.hxx>
+#include <svx/sdr/overlay/overlaymanager.hxx>
+#include <vcl/outdev.hxx>
+#include <tools/gen.hxx>
+
+#include <drawinglayer/processor2d/hittestprocessor2d.hxx>
+#include <comphelper/lok.hxx>
+
+#define DEFAULT_VALUE_FOR_HITTEST_PIXEL (2)
+#define DEFAULT_VALUE_FOR_HITTEST_TWIP (30)
+
+namespace sdr::overlay
+{
+ OverlayObjectList::~OverlayObjectList()
+ {
+ clear();
+ }
+
+ void OverlayObjectList::clear()
+ {
+ for(auto & pCandidate : maVector)
+ {
+ if(pCandidate->getOverlayManager())
+ pCandidate->getOverlayManager()->remove(*pCandidate);
+ }
+ maVector.clear();
+ }
+
+ void OverlayObjectList::append(std::unique_ptr<OverlayObject> pOverlayObject)
+ {
+ assert(pOverlayObject && "tried to add invalid OverlayObject to OverlayObjectList");
+ maVector.push_back(std::move(pOverlayObject));
+ }
+
+ bool OverlayObjectList::isHitLogic(const basegfx::B2DPoint& rLogicPosition, double fLogicTolerance) const
+ {
+ if(!maVector.empty())
+ {
+ OverlayObject* pFirst = maVector.front().get();
+ OverlayManager* pManager = pFirst->getOverlayManager();
+
+ if(pManager)
+ {
+ if(0.0 == fLogicTolerance)
+ {
+ Size aSizeLogic(pManager->getOutputDevice().PixelToLogic(
+ Size(DEFAULT_VALUE_FOR_HITTEST_PIXEL, DEFAULT_VALUE_FOR_HITTEST_PIXEL)));
+
+ // When tiled rendering, we always work in logic units, use the non-pixel default.
+ if (comphelper::LibreOfficeKit::isActive())
+ {
+ aSizeLogic = Size(DEFAULT_VALUE_FOR_HITTEST_TWIP, DEFAULT_VALUE_FOR_HITTEST_TWIP);
+ if (pManager->getOutputDevice().GetMapMode().GetMapUnit() == MapUnit::Map100thMM)
+ aSizeLogic = o3tl::convert(aSizeLogic, o3tl::Length::twip, o3tl::Length::mm100);
+ }
+
+ fLogicTolerance = aSizeLogic.Width();
+ }
+
+ const drawinglayer::geometry::ViewInformation2D& aViewInformation2D(pManager->getCurrentViewInformation2D());
+ drawinglayer::processor2d::HitTestProcessor2D aHitTestProcessor2D(
+ aViewInformation2D,
+ rLogicPosition,
+ fLogicTolerance,
+ false);
+
+ for(auto & pCandidate : maVector)
+ {
+ if(pCandidate->isHittable())
+ {
+ const drawinglayer::primitive2d::Primitive2DContainer& rSequence = pCandidate->getOverlayObjectPrimitive2DSequence();
+
+ if(!rSequence.empty())
+ {
+ aHitTestProcessor2D.process(rSequence);
+
+ if(aHitTestProcessor2D.getHit())
+ {
+ return true;
+ }
+ }
+ }
+ }
+ }
+ }
+
+ return false;
+ }
+
+ bool OverlayObjectList::isHitPixel(const Point& rDiscretePosition) const
+ {
+ constexpr sal_uInt32 nDiscreteTolerance = DEFAULT_VALUE_FOR_HITTEST_PIXEL;
+ if(!maVector.empty())
+ {
+ OverlayObject* pCandidate = maVector.front().get();
+ OverlayManager* pManager = pCandidate->getOverlayManager();
+
+ if(pManager)
+ {
+ const Point aPosLogic(pManager->getOutputDevice().PixelToLogic(rDiscretePosition));
+ const basegfx::B2DPoint aPosition(aPosLogic.X(), aPosLogic.Y());
+
+ const Size aSizeLogic(pManager->getOutputDevice().PixelToLogic(Size(nDiscreteTolerance, nDiscreteTolerance)));
+ return isHitLogic(aPosition, static_cast<double>(aSizeLogic.Width()));
+ }
+ }
+
+ return false;
+ }
+
+ basegfx::B2DRange OverlayObjectList::getBaseRange() const
+ {
+ basegfx::B2DRange aRetval;
+
+ for(auto & pCandidate : maVector)
+ {
+ aRetval.expand(pCandidate->getBaseRange());
+ }
+
+ return aRetval;
+ }
+
+} // end of namespace
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/sdr/overlay/overlaypolypolygon.cxx b/svx/source/sdr/overlay/overlaypolypolygon.cxx
new file mode 100644
index 000000000..69dacf375
--- /dev/null
+++ b/svx/source/sdr/overlay/overlaypolypolygon.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 <svx/sdr/overlay/overlaypolypolygon.hxx>
+#include <svx/sdr/overlay/overlaymanager.hxx>
+#include <drawinglayer/primitive2d/PolyPolygonMarkerPrimitive2D.hxx>
+#include <drawinglayer/primitive2d/PolyPolygonSelectionPrimitive2D.hxx>
+#include <drawinglayer/primitive2d/PolyPolygonStrokePrimitive2D.hxx>
+#include <drawinglayer/primitive2d/PolyPolygonColorPrimitive2D.hxx>
+#include <drawinglayer/primitive2d/unifiedtransparenceprimitive2d.hxx>
+#include <svtools/optionsdrawinglayer.hxx>
+
+
+namespace sdr::overlay
+{
+ OverlayPolyPolygon::OverlayPolyPolygon(
+ const basegfx::B2DPolyPolygon& rLinePolyPolygon,
+ Color const & rLineColor,
+ double fLineWidth,
+ Color const & rFillColor)
+ : OverlayObject(rLineColor)
+ , maLinePolyPolygon(rLinePolyPolygon)
+ , mfLineWidth(fLineWidth)
+ , maFillColor(rFillColor)
+ {
+ }
+
+ OverlayPolyPolygon::~OverlayPolyPolygon() = default;
+
+ drawinglayer::primitive2d::Primitive2DContainer OverlayPolyPolygon::createOverlayObjectPrimitive2DSequence()
+ {
+ drawinglayer::primitive2d::Primitive2DContainer aReturnContainer;
+
+ if (getOverlayManager())
+ {
+ const drawinglayer::attribute::LineAttribute aLineAttribute(getBaseColor().getBColor(), mfLineWidth);
+
+ aReturnContainer = drawinglayer::primitive2d::Primitive2DContainer {
+ new drawinglayer::primitive2d::PolyPolygonStrokePrimitive2D(maLinePolyPolygon, aLineAttribute) };
+
+ if (maFillColor.GetAlpha() != 0)
+ {
+ aReturnContainer.push_back(new drawinglayer::primitive2d::PolyPolygonColorPrimitive2D(maLinePolyPolygon, maFillColor.getBColor()));
+ }
+
+ sal_uInt8 nTransparency = 255 - getBaseColor().GetAlpha();
+ if (nTransparency > 0)
+ {
+ drawinglayer::primitive2d::Primitive2DReference aTransparencePrimitive(
+ new drawinglayer::primitive2d::UnifiedTransparencePrimitive2D(std::move(aReturnContainer), nTransparency / 255.0));
+ aReturnContainer = drawinglayer::primitive2d::Primitive2DContainer{ aTransparencePrimitive };
+ }
+ }
+
+ return aReturnContainer;
+ }
+
+ drawinglayer::primitive2d::Primitive2DContainer OverlayPolyPolygonStripedAndFilled::createOverlayObjectPrimitive2DSequence()
+ {
+ drawinglayer::primitive2d::Primitive2DContainer aRetval;
+
+ if(getOverlayManager())
+ {
+ const basegfx::BColor aRGBColorA(getOverlayManager()->getStripeColorA().getBColor());
+ const basegfx::BColor aRGBColorB(getOverlayManager()->getStripeColorB().getBColor());
+ const double fStripeLengthPixel(getOverlayManager()->getStripeLengthPixel());
+ const drawinglayer::primitive2d::Primitive2DReference aStriped(
+ new drawinglayer::primitive2d::PolyPolygonMarkerPrimitive2D(
+ getLinePolyPolygon(),
+ aRGBColorA,
+ aRGBColorB,
+ fStripeLengthPixel));
+
+ aRetval = drawinglayer::primitive2d::Primitive2DContainer { aStriped };
+
+ const basegfx::BColor aHilightColor(SvtOptionsDrawinglayer::getHilightColor().getBColor());
+ const double fTransparence(SvtOptionsDrawinglayer::GetTransparentSelectionPercent() * 0.01);
+
+ const drawinglayer::primitive2d::Primitive2DReference aFilled(
+ new drawinglayer::primitive2d::PolyPolygonSelectionPrimitive2D(
+ getLinePolyPolygon(),
+ aHilightColor,
+ fTransparence,
+ 3.0,
+ false));
+
+ aRetval.push_back(aFilled);
+ }
+
+ return aRetval;
+ }
+
+ void OverlayPolyPolygonStripedAndFilled::stripeDefinitionHasChanged()
+ {
+ // react on OverlayManager's stripe definition change
+ objectChange();
+ }
+
+ OverlayPolyPolygonStripedAndFilled::OverlayPolyPolygonStripedAndFilled(
+ const basegfx::B2DPolyPolygon& rLinePolyPolygon)
+ : OverlayObject(COL_BLACK),
+ maLinePolyPolygon(rLinePolyPolygon)
+ {
+ }
+
+ OverlayPolyPolygonStripedAndFilled::~OverlayPolyPolygonStripedAndFilled()
+ {
+ }
+
+} // end of namespace
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/sdr/overlay/overlayprimitive2dsequenceobject.cxx b/svx/source/sdr/overlay/overlayprimitive2dsequenceobject.cxx
new file mode 100644
index 000000000..eabd290f8
--- /dev/null
+++ b/svx/source/sdr/overlay/overlayprimitive2dsequenceobject.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 <svx/sdr/overlay/overlayprimitive2dsequenceobject.hxx>
+
+
+namespace sdr::overlay
+{
+ drawinglayer::primitive2d::Primitive2DContainer OverlayPrimitive2DSequenceObject::createOverlayObjectPrimitive2DSequence()
+ {
+ return maSequence;
+ }
+
+ OverlayPrimitive2DSequenceObject::OverlayPrimitive2DSequenceObject(drawinglayer::primitive2d::Primitive2DContainer&& rSequence)
+ : OverlayObjectWithBasePosition(basegfx::B2DPoint(), COL_BLACK),
+ maSequence(std::move(rSequence))
+ {
+ }
+
+ OverlayPrimitive2DSequenceObject::~OverlayPrimitive2DSequenceObject()
+ {
+ }
+} // end of namespace sdr::overlay
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/sdr/overlay/overlayrectangle.cxx b/svx/source/sdr/overlay/overlayrectangle.cxx
new file mode 100644
index 000000000..617c6d4dc
--- /dev/null
+++ b/svx/source/sdr/overlay/overlayrectangle.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 <sdr/overlay/overlayrectangle.hxx>
+#include <sdr/overlay/overlaytools.hxx>
+#include <svx/sdr/overlay/overlaymanager.hxx>
+#include <vcl/svapp.hxx>
+#include <vcl/settings.hxx>
+
+
+namespace sdr::overlay
+{
+ drawinglayer::primitive2d::Primitive2DContainer OverlayRectangle::createOverlayObjectPrimitive2DSequence()
+ {
+ const basegfx::B2DRange aHatchRange(getBasePosition(), maSecondPosition);
+ basegfx::BColor aColor(getBaseColor().getBColor());
+ static const double fChange(0.1); // just small optical change, do not make it annoying
+
+ if(mbOverlayState)
+ {
+ aColor += basegfx::B3DTuple(fChange, fChange, fChange);
+ aColor.clamp();
+ }
+ else
+ {
+ aColor -= basegfx::B3DTuple(fChange, fChange, fChange);
+ aColor.clamp();
+ }
+
+ const drawinglayer::primitive2d::Primitive2DReference aReference(
+ new drawinglayer::primitive2d::OverlayRectanglePrimitive(
+ aHatchRange,
+ aColor,
+ mfTransparence,
+ mfDiscreteGrow,
+ mfDiscreteShrink,
+ mfRotation));
+
+ return drawinglayer::primitive2d::Primitive2DContainer { aReference };
+ }
+
+ OverlayRectangle::OverlayRectangle(
+ const basegfx::B2DPoint& rBasePosition,
+ const basegfx::B2DPoint& rSecondPosition,
+ const Color& rHatchColor,
+ double fTransparence,
+ double fDiscreteGrow,
+ double fDiscreteShrink,
+ double fRotation,
+ bool bAnimate)
+ : OverlayObjectWithBasePosition(rBasePosition, rHatchColor),
+ maSecondPosition(rSecondPosition),
+ mfTransparence(fTransparence),
+ mfDiscreteGrow(fDiscreteGrow),
+ mfDiscreteShrink(fDiscreteShrink),
+ mfRotation(fRotation),
+ mnBlinkTime(impCheckBlinkTimeValueRange(500)),
+ mbOverlayState(false)
+ {
+ if(Application::GetSettings().GetStyleSettings().GetHighContrastMode())
+ {
+ // no animation in high contrast mode
+ bAnimate = false;
+ }
+
+ // set AllowsAnimation flag to mark this object as animation capable
+ mbAllowsAnimation = bAnimate;
+ }
+
+ void OverlayRectangle::Trigger(sal_uInt32 nTime)
+ {
+ if(!getOverlayManager())
+ return;
+
+ // #i53216# produce event after nTime + x
+ SetTime(nTime + mnBlinkTime);
+
+ // switch state
+ if(mbOverlayState)
+ {
+ mbOverlayState = false;
+ }
+ else
+ {
+ mbOverlayState = true;
+ }
+
+ // re-insert me as event
+ getOverlayManager()->InsertEvent(*this);
+
+ // register change (after change)
+ objectChange();
+ }
+} // end of namespace
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/sdr/overlay/overlayrollingrectangle.cxx b/svx/source/sdr/overlay/overlayrollingrectangle.cxx
new file mode 100644
index 000000000..f03380edd
--- /dev/null
+++ b/svx/source/sdr/overlay/overlayrollingrectangle.cxx
@@ -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 .
+ */
+
+#include <sdr/overlay/overlayrollingrectangle.hxx>
+#include <sdr/overlay/overlaytools.hxx>
+#include <svx/sdr/overlay/overlaymanager.hxx>
+#include <basegfx/polygon/b2dpolygontools.hxx>
+#include <basegfx/polygon/b2dpolygon.hxx>
+#include <drawinglayer/primitive2d/PolyPolygonMarkerPrimitive2D.hxx>
+#include <drawinglayer/primitive2d/PolyPolygonSelectionPrimitive2D.hxx>
+#include <svtools/optionsdrawinglayer.hxx>
+
+namespace sdr::overlay
+{
+ drawinglayer::primitive2d::Primitive2DContainer OverlayRollingRectangleStriped::createOverlayObjectPrimitive2DSequence()
+ {
+ drawinglayer::primitive2d::Primitive2DContainer aRetval;
+
+ if(getOverlayManager() && (mbShowBounds || mbExtendedLines))
+ {
+ const basegfx::BColor aRGBColorA(getOverlayManager()->getStripeColorA().getBColor());
+ const basegfx::BColor aRGBColorB(getOverlayManager()->getStripeColorB().getBColor());
+ const double fStripeLengthPixel(getOverlayManager()->getStripeLengthPixel());
+ const basegfx::B2DRange aRollingRectangle(getBasePosition(), getSecondPosition());
+
+ if(mbShowBounds)
+ {
+ // view-independent part, create directly
+ const basegfx::B2DPolygon aPolygon(basegfx::utils::createPolygonFromRect(aRollingRectangle));
+
+ aRetval.resize(2);
+ aRetval[0] = new drawinglayer::primitive2d::PolyPolygonMarkerPrimitive2D(
+ basegfx::B2DPolyPolygon(aPolygon),
+ aRGBColorA,
+ aRGBColorB,
+ fStripeLengthPixel);
+
+ const basegfx::BColor aHilightColor(SvtOptionsDrawinglayer::getHilightColor().getBColor());
+ const double fTransparence(SvtOptionsDrawinglayer::GetTransparentSelectionPercent() * 0.01);
+
+ aRetval[1] = new drawinglayer::primitive2d::PolyPolygonSelectionPrimitive2D(
+ basegfx::B2DPolyPolygon(aPolygon),
+ aHilightColor,
+ fTransparence,
+ 3.0,
+ false);
+ }
+
+ if(mbExtendedLines)
+ {
+ // view-dependent part, use helper primitive
+ const drawinglayer::primitive2d::Primitive2DReference aReference(
+ new drawinglayer::primitive2d::OverlayRollingRectanglePrimitive(
+ aRollingRectangle,
+ aRGBColorA,
+ aRGBColorB,
+ fStripeLengthPixel));
+
+ aRetval.push_back(aReference);
+ }
+ }
+
+ return aRetval;
+ }
+
+ void OverlayRollingRectangleStriped::stripeDefinitionHasChanged()
+ {
+ // react on OverlayManager's stripe definition change
+ objectChange();
+ }
+
+ OverlayRollingRectangleStriped::OverlayRollingRectangleStriped(
+ const basegfx::B2DPoint& rBasePos,
+ const basegfx::B2DPoint& rSecondPos,
+ bool bExtendedLines,
+ bool bShowBounds)
+ : OverlayObjectWithBasePosition(rBasePos, COL_BLACK),
+ maSecondPosition(rSecondPos),
+ mbExtendedLines(bExtendedLines),
+ mbShowBounds(bShowBounds)
+ {
+ }
+
+ OverlayRollingRectangleStriped::~OverlayRollingRectangleStriped()
+ {
+ }
+
+ void OverlayRollingRectangleStriped::setSecondPosition(const basegfx::B2DPoint& rNew)
+ {
+ if(rNew != maSecondPosition)
+ {
+ // remember new value
+ maSecondPosition = rNew;
+
+ // register change (after change)
+ objectChange();
+ }
+ }
+} // end of namespace
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/sdr/overlay/overlayselection.cxx b/svx/source/sdr/overlay/overlayselection.cxx
new file mode 100644
index 000000000..0463567c5
--- /dev/null
+++ b/svx/source/sdr/overlay/overlayselection.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 <svx/sdr/overlay/overlayselection.hxx>
+#include <basegfx/polygon/b2dpolygontools.hxx>
+#include <basegfx/polygon/b2dpolygon.hxx>
+#include <drawinglayer/primitive2d/PolyPolygonColorPrimitive2D.hxx>
+#include <drawinglayer/primitive2d/PolyPolygonHairlinePrimitive2D.hxx>
+#include <svtools/optionsdrawinglayer.hxx>
+#include <vcl/svapp.hxx>
+#include <vcl/outdev.hxx>
+#include <vcl/settings.hxx>
+#include <drawinglayer/primitive2d/invertprimitive2d.hxx>
+#include <drawinglayer/primitive2d/unifiedtransparenceprimitive2d.hxx>
+#include <basegfx/polygon/b2dpolypolygoncutter.hxx>
+#include <svx/sdr/overlay/overlaymanager.hxx>
+
+
+namespace sdr::overlay
+{
+ // combine rages geometrically to a single, ORed polygon
+ static 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;
+ }
+
+ // check if wanted type OverlayType::Transparent or OverlayType::Solid
+ // is possible. If not, fallback to invert mode (classic mode)
+ static OverlayType impCheckPossibleOverlayType(OverlayType aOverlayType)
+ {
+ if(OverlayType::Invert != aOverlayType)
+ {
+ if(!SvtOptionsDrawinglayer::IsTransparentSelection())
+ {
+ // not possible when switched off by user
+ return OverlayType::Invert;
+ }
+ else if (const OutputDevice* pOut = Application::GetDefaultDevice())
+ {
+
+ if(pOut->GetSettings().GetStyleSettings().GetHighContrastMode())
+ {
+ // not possible when in high contrast mode
+ return OverlayType::Invert;
+ }
+
+ if(!pOut->SupportsOperation(OutDevSupportType::TransparentRect))
+ {
+ // not possible when no fast transparence paint is supported on the system
+ return OverlayType::Invert;
+ }
+ }
+ }
+
+ return aOverlayType;
+ }
+
+ drawinglayer::primitive2d::Primitive2DContainer OverlaySelection::createOverlayObjectPrimitive2DSequence()
+ {
+ drawinglayer::primitive2d::Primitive2DContainer aRetval;
+ const sal_uInt32 nCount(getRanges().size());
+
+ if(nCount)
+ {
+ // create range primitives
+ const bool bInvert(OverlayType::Invert == maLastOverlayType);
+ basegfx::BColor aRGBColor(getBaseColor().getBColor());
+ aRetval.resize(nCount);
+
+ if(bInvert)
+ {
+ // force color to white for invert to get a full invert
+ aRGBColor = basegfx::BColor(1.0, 1.0, 1.0);
+ }
+
+ for(sal_uInt32 a(0);a < nCount; a++)
+ {
+ const basegfx::B2DPolygon aPolygon(basegfx::utils::createPolygonFromRect(maRanges[a]));
+ aRetval[a] = drawinglayer::primitive2d::Primitive2DReference(
+ new drawinglayer::primitive2d::PolyPolygonColorPrimitive2D(
+ basegfx::B2DPolyPolygon(aPolygon),
+ aRGBColor));
+ }
+
+ if(bInvert)
+ {
+ // embed all in invert primitive
+ drawinglayer::primitive2d::Primitive2DReference aInvert(
+ new drawinglayer::primitive2d::InvertPrimitive2D(
+ std::move(aRetval)));
+ aRetval = drawinglayer::primitive2d::Primitive2DContainer { aInvert };
+ }
+ else if(OverlayType::Transparent == maLastOverlayType)
+ {
+ // embed all rectangles in transparent paint
+ const double fTransparence(mnLastTransparence / 100.0);
+ const drawinglayer::primitive2d::Primitive2DReference aUnifiedTransparence(
+ new drawinglayer::primitive2d::UnifiedTransparencePrimitive2D(
+ std::move(aRetval),
+ fTransparence));
+
+ if(mbBorder)
+ {
+ const basegfx::B2DPolyPolygon aPolyPolygon(impCombineRangesToPolyPolygon(getRanges()));
+ const drawinglayer::primitive2d::Primitive2DReference aSelectionOutline(
+ new drawinglayer::primitive2d::PolyPolygonHairlinePrimitive2D(
+ aPolyPolygon,
+ aRGBColor));
+
+ // add both to result
+ aRetval = drawinglayer::primitive2d::Primitive2DContainer { aUnifiedTransparence, aSelectionOutline };
+ }
+ else
+ {
+ // just add transparent part
+ aRetval = drawinglayer::primitive2d::Primitive2DContainer { aUnifiedTransparence };
+ }
+ }
+ }
+
+ return aRetval;
+ }
+
+ OverlaySelection::OverlaySelection(
+ OverlayType eType,
+ const Color& rColor,
+ std::vector< basegfx::B2DRange >&& rRanges,
+ bool bBorder)
+ : OverlayObject(rColor),
+ meOverlayType(eType),
+ maRanges(std::move(rRanges)),
+ maLastOverlayType(eType),
+ mnLastTransparence(0),
+ mbBorder(bBorder)
+ {
+ // no AA for selection overlays
+ allowAntiAliase(false);
+ }
+
+ OverlaySelection::~OverlaySelection()
+ {
+ if(getOverlayManager())
+ {
+ getOverlayManager()->remove(*this);
+ }
+ }
+
+ drawinglayer::primitive2d::Primitive2DContainer OverlaySelection::getOverlayObjectPrimitive2DSequence() const
+ {
+ // get current values
+ const OverlayType aNewOverlayType(impCheckPossibleOverlayType(meOverlayType));
+ const sal_uInt16 nNewTransparence(SvtOptionsDrawinglayer::GetTransparentSelectionPercent());
+
+ if(!getPrimitive2DSequence().empty())
+ {
+ if(aNewOverlayType != maLastOverlayType
+ || nNewTransparence != mnLastTransparence)
+ {
+ // conditions of last local decomposition have changed, delete
+ const_cast< OverlaySelection* >(this)->resetPrimitive2DSequence();
+ }
+ }
+
+ if(getPrimitive2DSequence().empty())
+ {
+ // remember new values
+ const_cast< OverlaySelection* >(this)->maLastOverlayType = aNewOverlayType;
+ const_cast< OverlaySelection* >(this)->mnLastTransparence = nNewTransparence;
+ }
+
+ // call base implementation
+ return OverlayObject::getOverlayObjectPrimitive2DSequence();
+ }
+
+ void OverlaySelection::setRanges(std::vector< basegfx::B2DRange >&& rNew)
+ {
+ if(rNew != maRanges)
+ {
+ maRanges = std::move(rNew);
+ objectChange();
+ }
+ }
+} // end of namespace
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/sdr/overlay/overlaytools.cxx b/svx/source/sdr/overlay/overlaytools.cxx
new file mode 100644
index 000000000..ceeb32b3f
--- /dev/null
+++ b/svx/source/sdr/overlay/overlaytools.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 <sdr/overlay/overlaytools.hxx>
+#include <svx/sdr/primitive2d/svx_primitivetypes2d.hxx>
+#include <basegfx/matrix/b2dhommatrix.hxx>
+#include <drawinglayer/primitive2d/bitmapprimitive2d.hxx>
+#include <basegfx/polygon/b2dpolygon.hxx>
+#include <basegfx/polygon/b2dpolygontools.hxx>
+#include <drawinglayer/primitive2d/PolygonMarkerPrimitive2D.hxx>
+#include <drawinglayer/primitive2d/PolyPolygonColorPrimitive2D.hxx>
+#include <drawinglayer/primitive2d/PolyPolygonStrokePrimitive2D.hxx>
+#include <drawinglayer/primitive2d/PolyPolygonHatchPrimitive2D.hxx>
+#include <drawinglayer/geometry/viewinformation2d.hxx>
+#include <basegfx/matrix/b2dhommatrixtools.hxx>
+#include <drawinglayer/primitive2d/unifiedtransparenceprimitive2d.hxx>
+#include <vcl/svapp.hxx>
+#include <vcl/settings.hxx>
+#include <toolkit/helper/vclunohelper.hxx>
+
+
+namespace drawinglayer::primitive2d
+{
+
+OverlayStaticRectanglePrimitive::OverlayStaticRectanglePrimitive(
+ const basegfx::B2DPoint& rPosition,
+ const basegfx::B2DSize& rSize,
+ const basegfx::BColor& rStrokeColor,
+ const basegfx::BColor& rFillColor,
+ double fTransparence,
+ double fRotation)
+ : maPosition(rPosition)
+ , maSize(rSize)
+ , maStrokeColor(rStrokeColor)
+ , maFillColor(rFillColor)
+ , mfTransparence(fTransparence)
+ , mfRotation(fRotation)
+{}
+
+void OverlayStaticRectanglePrimitive::create2DDecomposition(Primitive2DContainer& rContainer, const geometry::ViewInformation2D& /*rViewInformation*/) const
+{
+ Primitive2DContainer aPrimitive2DSequence;
+ const double fHalfWidth = maSize.getX() * getDiscreteUnit() / 2.0;
+ const double fHalfHeight = maSize.getY() * getDiscreteUnit() / 2.0;
+
+ basegfx::B2DRange aRange(
+ maPosition.getX() - fHalfWidth, maPosition.getY() - fHalfHeight,
+ maPosition.getX() + fHalfWidth, maPosition.getY() + fHalfHeight);
+
+ if (basegfx::fTools::more(getDiscreteUnit(), 0.0) && mfTransparence <= 1.0)
+ {
+ basegfx::B2DPolygon aPolygon(
+ basegfx::utils::createPolygonFromRect(aRange));
+
+ // create filled primitive
+ basegfx::B2DPolyPolygon aPolyPolygon;
+ aPolyPolygon.append(aPolygon);
+
+ const attribute::LineAttribute aLineAttribute(maStrokeColor, 1.0);
+
+ // create data
+ const Primitive2DReference aStroke(
+ new PolyPolygonStrokePrimitive2D(aPolyPolygon, aLineAttribute));
+
+ // create fill primitive
+ const Primitive2DReference aFill(
+ new PolyPolygonColorPrimitive2D(aPolyPolygon, maFillColor));
+
+ aPrimitive2DSequence = Primitive2DContainer(2);
+ aPrimitive2DSequence[0] = aFill;
+ aPrimitive2DSequence[1] = aStroke;
+
+ // embed filled to transparency (if used)
+ if (mfTransparence > 0.0)
+ {
+ const Primitive2DReference aFillTransparent(
+ new UnifiedTransparencePrimitive2D(
+ std::move(aPrimitive2DSequence),
+ mfTransparence));
+
+ aPrimitive2DSequence = Primitive2DContainer { aFillTransparent };
+ }
+ }
+
+ rContainer.append(std::move(aPrimitive2DSequence));
+}
+
+bool OverlayStaticRectanglePrimitive::operator==(const BasePrimitive2D& rPrimitive) const
+{
+ if (DiscreteMetricDependentPrimitive2D::operator==(rPrimitive))
+ {
+ const OverlayStaticRectanglePrimitive& rCompare = static_cast<const OverlayStaticRectanglePrimitive&>(rPrimitive);
+
+ return (maPosition == rCompare.maPosition
+ && maSize == rCompare.maSize
+ && maStrokeColor == rCompare.maStrokeColor
+ && maFillColor == rCompare.maFillColor
+ && mfTransparence == rCompare.mfTransparence
+ && mfRotation == rCompare.mfRotation);
+ }
+
+ return false;
+}
+
+sal_uInt32 OverlayStaticRectanglePrimitive::getPrimitive2DID() const
+{
+ return PRIMITIVE2D_ID_OVERLAYRECTANGLEPRIMITIVE;
+}
+
+
+
+ OverlayBitmapExPrimitive::OverlayBitmapExPrimitive(
+ const BitmapEx& rBitmapEx,
+ const basegfx::B2DPoint& rBasePosition,
+ sal_uInt16 nCenterX,
+ sal_uInt16 nCenterY,
+ double fShearX,
+ double fRotation)
+ : maBitmapEx(rBitmapEx),
+ maBasePosition(rBasePosition),
+ mnCenterX(nCenterX),
+ mnCenterY(nCenterY),
+ mfShearX(fShearX),
+ mfRotation(fRotation)
+ {}
+
+ void OverlayBitmapExPrimitive::create2DDecomposition(Primitive2DContainer& rContainer, const geometry::ViewInformation2D& /*rViewInformation*/) const
+ {
+ const Size aBitmapSize(getBitmapEx().GetSizePixel());
+
+ if(!aBitmapSize.Width() || !aBitmapSize.Height() || !basegfx::fTools::more(getDiscreteUnit(), 0.0))
+ return;
+
+ // calculate back from internal bitmap's extreme coordinates (the edges)
+ // to logical coordinates. Only use a unified scaling value (getDiscreteUnit(),
+ // the prepared one which expresses how many logic units form a discrete unit)
+ // for this step. This primitive is to be displayed always unscaled (in its pixel size)
+ // and unrotated, more like a marker
+ const double fLeft((0.0 - getCenterX()) * getDiscreteUnit());
+ const double fTop((0.0 - getCenterY()) * getDiscreteUnit());
+ const double fRight((aBitmapSize.getWidth() - getCenterX()) * getDiscreteUnit());
+ const double fBottom((aBitmapSize.getHeight() - getCenterY()) * getDiscreteUnit());
+
+ // create a BitmapPrimitive2D using those positions
+ basegfx::B2DHomMatrix aTransform;
+
+ aTransform.set(0, 0, fRight - fLeft);
+ aTransform.set(1, 1, fBottom - fTop);
+ aTransform.set(0, 2, fLeft);
+ aTransform.set(1, 2, fTop);
+
+ // if shearX is used, apply it, too
+ if(!basegfx::fTools::equalZero(getShearX()))
+ {
+ aTransform.shearX(getShearX());
+ }
+
+ // if rotation is used, apply it, too
+ if(!basegfx::fTools::equalZero(getRotation()))
+ {
+ aTransform.rotate(getRotation());
+ }
+
+ // add BasePosition
+ aTransform.translate(getBasePosition().getX(), getBasePosition().getY());
+
+ rContainer.push_back(
+ new BitmapPrimitive2D(
+ VCLUnoHelper::CreateVCLXBitmap(getBitmapEx()),
+ aTransform));
+ }
+
+ bool OverlayBitmapExPrimitive::operator==( const BasePrimitive2D& rPrimitive ) const
+ {
+ if(DiscreteMetricDependentPrimitive2D::operator==(rPrimitive))
+ {
+ const OverlayBitmapExPrimitive& rCompare = static_cast< const OverlayBitmapExPrimitive& >(rPrimitive);
+
+ return (getBitmapEx() == rCompare.getBitmapEx()
+ && getBasePosition() == rCompare.getBasePosition()
+ && getCenterX() == rCompare.getCenterX()
+ && getCenterY() == rCompare.getCenterY()
+ && getShearX() == rCompare.getShearX()
+ && getRotation() == rCompare.getRotation());
+ }
+
+ return false;
+ }
+
+ sal_uInt32 OverlayBitmapExPrimitive::getPrimitive2DID() const
+ {
+ return PRIMITIVE2D_ID_OVERLAYBITMAPEXPRIMITIVE;
+ }
+
+
+
+ OverlayCrosshairPrimitive::OverlayCrosshairPrimitive(
+ const basegfx::B2DPoint& rBasePosition,
+ const basegfx::BColor& rRGBColorA,
+ const basegfx::BColor& rRGBColorB,
+ double fDiscreteDashLength)
+ : maBasePosition(rBasePosition),
+ maRGBColorA(rRGBColorA),
+ maRGBColorB(rRGBColorB),
+ mfDiscreteDashLength(fDiscreteDashLength)
+ {}
+
+ void OverlayCrosshairPrimitive::create2DDecomposition(Primitive2DContainer& rContainer, const geometry::ViewInformation2D& /*rViewInformation*/) const
+ {
+ // use the prepared Viewport information accessible using getViewport()
+
+ if(getViewport().isEmpty())
+ return;
+
+ basegfx::B2DPolygon aPolygon;
+
+ aPolygon.append(basegfx::B2DPoint(getViewport().getMinX(), getBasePosition().getY()));
+ aPolygon.append(basegfx::B2DPoint(getViewport().getMaxX(), getBasePosition().getY()));
+
+ rContainer.push_back(
+ new PolygonMarkerPrimitive2D(
+ aPolygon,
+ getRGBColorA(),
+ getRGBColorB(),
+ getDiscreteDashLength()));
+
+ aPolygon.clear();
+ aPolygon.append(basegfx::B2DPoint(getBasePosition().getX(), getViewport().getMinY()));
+ aPolygon.append(basegfx::B2DPoint(getBasePosition().getX(), getViewport().getMaxY()));
+
+ rContainer.push_back(
+ new PolygonMarkerPrimitive2D(
+ aPolygon,
+ getRGBColorA(),
+ getRGBColorB(),
+ getDiscreteDashLength()));
+ }
+
+ bool OverlayCrosshairPrimitive::operator==( const BasePrimitive2D& rPrimitive ) const
+ {
+ if(ViewportDependentPrimitive2D::operator==(rPrimitive))
+ {
+ const OverlayCrosshairPrimitive& rCompare = static_cast< const OverlayCrosshairPrimitive& >(rPrimitive);
+
+ return (getBasePosition() == rCompare.getBasePosition()
+ && getRGBColorA() == rCompare.getRGBColorA()
+ && getRGBColorB() == rCompare.getRGBColorB()
+ && getDiscreteDashLength() == rCompare.getDiscreteDashLength());
+ }
+
+ return false;
+ }
+
+ sal_uInt32 OverlayCrosshairPrimitive::getPrimitive2DID() const
+ {
+ return PRIMITIVE2D_ID_OVERLAYCROSSHAIRPRIMITIVE;
+ }
+
+
+
+ OverlayRectanglePrimitive::OverlayRectanglePrimitive(
+ const basegfx::B2DRange& rObjectRange,
+ const basegfx::BColor& rColor,
+ double fTransparence,
+ double fDiscreteGrow,
+ double fDiscreteShrink,
+ double fRotation)
+ : maObjectRange(rObjectRange),
+ maColor(rColor),
+ mfTransparence(fTransparence),
+ mfDiscreteGrow(fDiscreteGrow),
+ mfDiscreteShrink(fDiscreteShrink),
+ mfRotation(fRotation)
+ {}
+
+ void OverlayRectanglePrimitive::create2DDecomposition(Primitive2DContainer& rContainer, const geometry::ViewInformation2D& /*rViewInformation*/) const
+ {
+ Primitive2DContainer aRetval;
+ basegfx::B2DRange aInnerRange(getObjectRange());
+
+ if(!aInnerRange.isEmpty() && basegfx::fTools::more(getDiscreteUnit(), 0.0) && getTransparence() <= 1.0)
+ {
+ basegfx::B2DRange aOuterRange(getObjectRange());
+
+ // grow/shrink inner/outer polygons
+ aOuterRange.grow(getDiscreteUnit() * getDiscreteGrow());
+ aInnerRange.grow(getDiscreteUnit() * -getDiscreteShrink());
+
+ // convert to polygons
+ const double fFullGrow(getDiscreteGrow() + getDiscreteShrink());
+ const double fRelativeRadiusX(fFullGrow / aOuterRange.getWidth());
+ const double fRelativeRadiusY(fFullGrow / aOuterRange.getHeight());
+ basegfx::B2DPolygon aOuterPolygon(
+ basegfx::utils::createPolygonFromRect(
+ aOuterRange,
+ fRelativeRadiusX,
+ fRelativeRadiusY));
+ basegfx::B2DPolygon aInnerPolygon(
+ basegfx::utils::createPolygonFromRect(
+ aInnerRange));
+
+ // apply evtl. existing rotation
+ if(!basegfx::fTools::equalZero(getRotation()))
+ {
+ const basegfx::B2DHomMatrix aTransform(basegfx::utils::createRotateAroundPoint(
+ getObjectRange().getMinX(), getObjectRange().getMinY(), getRotation()));
+
+ aOuterPolygon.transform(aTransform);
+ aInnerPolygon.transform(aTransform);
+ }
+
+ // create filled primitive
+ basegfx::B2DPolyPolygon aPolyPolygon;
+
+ aPolyPolygon.append(aOuterPolygon);
+ aPolyPolygon.append(aInnerPolygon);
+
+ if(Application::GetSettings().GetStyleSettings().GetHighContrastMode())
+ {
+ // for high contrast, use hatch
+ const basegfx::BColor aHighContrastLineColor(Application::GetSettings().GetStyleSettings().GetFontColor().getBColor());
+ const basegfx::BColor aEmptyColor(0.0, 0.0, 0.0);
+ const double fHatchRotation(basegfx::deg2rad(45));
+ const double fDiscreteHatchDistance(3.0);
+ const drawinglayer::attribute::FillHatchAttribute aFillHatchAttribute(
+ drawinglayer::attribute::HatchStyle::Single,
+ fDiscreteHatchDistance * getDiscreteUnit(),
+ fHatchRotation - getRotation(),
+ aHighContrastLineColor,
+ 3, // same default as VCL, a minimum of three discrete units (pixels) offset
+ false);
+ const Primitive2DReference aHatch(
+ new PolyPolygonHatchPrimitive2D(
+ aPolyPolygon,
+ aEmptyColor,
+ aFillHatchAttribute));
+
+ aRetval = Primitive2DContainer { aHatch };
+ }
+ else
+ {
+ // create fill primitive
+ const Primitive2DReference aFill(
+ new PolyPolygonColorPrimitive2D(
+ aPolyPolygon,
+ getColor()));
+
+ aRetval = Primitive2DContainer { aFill };
+
+ // embed filled to transparency (if used)
+ if(getTransparence() > 0.0)
+ {
+ Primitive2DReference aFillTransparent(
+ new UnifiedTransparencePrimitive2D(
+ std::move(aRetval),
+ getTransparence()));
+
+ aRetval = Primitive2DContainer { aFillTransparent };
+ }
+ }
+ }
+
+ rContainer.append(std::move(aRetval));
+ }
+
+ bool OverlayRectanglePrimitive::operator==( const BasePrimitive2D& rPrimitive ) const
+ {
+ if(DiscreteMetricDependentPrimitive2D::operator==(rPrimitive))
+ {
+ const OverlayRectanglePrimitive& rCompare = static_cast< const OverlayRectanglePrimitive& >(rPrimitive);
+
+ return (getObjectRange() == rCompare.getObjectRange()
+ && getColor() == rCompare.getColor()
+ && getTransparence() == rCompare.getTransparence()
+ && getDiscreteGrow() == rCompare.getDiscreteGrow()
+ && getDiscreteShrink() == rCompare.getDiscreteShrink()
+ && getRotation() == rCompare.getRotation());
+ }
+
+ return false;
+ }
+
+ sal_uInt32 OverlayRectanglePrimitive::getPrimitive2DID() const
+ {
+ return PRIMITIVE2D_ID_OVERLAYRECTANGLEPRIMITIVE;
+ }
+
+
+
+ OverlayHelplineStripedPrimitive::OverlayHelplineStripedPrimitive(
+ const basegfx::B2DPoint& rBasePosition,
+ HelplineStyle eStyle,
+ const basegfx::BColor& rRGBColorA,
+ const basegfx::BColor& rRGBColorB,
+ double fDiscreteDashLength)
+ : maBasePosition(rBasePosition),
+ meStyle(eStyle),
+ maRGBColorA(rRGBColorA),
+ maRGBColorB(rRGBColorB),
+ mfDiscreteDashLength(fDiscreteDashLength)
+ {}
+
+ void OverlayHelplineStripedPrimitive::create2DDecomposition(Primitive2DContainer& rContainer, const geometry::ViewInformation2D& rViewInformation) const
+ {
+ // use the prepared Viewport information accessible using getViewport()
+
+ if(getViewport().isEmpty())
+ return;
+
+ switch(getStyle())
+ {
+ case HELPLINESTYLE_VERTICAL :
+ {
+ basegfx::B2DPolygon aLine;
+
+ aLine.append(basegfx::B2DPoint(getBasePosition().getX(), getViewport().getMinY()));
+ aLine.append(basegfx::B2DPoint(getBasePosition().getX(), getViewport().getMaxY()));
+
+ rContainer.push_back(
+ new PolygonMarkerPrimitive2D(
+ aLine,
+ getRGBColorA(),
+ getRGBColorB(),
+ getDiscreteDashLength()));
+ break;
+ }
+
+ case HELPLINESTYLE_HORIZONTAL :
+ {
+ basegfx::B2DPolygon aLine;
+
+ aLine.append(basegfx::B2DPoint(getViewport().getMinX(), getBasePosition().getY()));
+ aLine.append(basegfx::B2DPoint(getViewport().getMaxX(), getBasePosition().getY()));
+
+ rContainer.push_back(
+ new PolygonMarkerPrimitive2D(
+ aLine,
+ getRGBColorA(),
+ getRGBColorB(),
+ getDiscreteDashLength()));
+ break;
+ }
+
+ default: // case HELPLINESTYLE_POINT :
+ {
+ const double fDiscreteUnit((rViewInformation.getInverseObjectToViewTransformation() * basegfx::B2DVector(1.0, 0.0)).getLength());
+ basegfx::B2DPolygon aLineA, aLineB;
+
+ aLineA.append(basegfx::B2DPoint(getBasePosition().getX(), getBasePosition().getY() - fDiscreteUnit));
+ aLineA.append(basegfx::B2DPoint(getBasePosition().getX(), getBasePosition().getY() + fDiscreteUnit));
+
+ rContainer.push_back(
+ new PolygonMarkerPrimitive2D(
+ aLineA,
+ getRGBColorA(),
+ getRGBColorB(),
+ getDiscreteDashLength()));
+
+ aLineB.append(basegfx::B2DPoint(getBasePosition().getX() - fDiscreteUnit, getBasePosition().getY()));
+ aLineB.append(basegfx::B2DPoint(getBasePosition().getX() + fDiscreteUnit, getBasePosition().getY()));
+
+ rContainer.push_back(
+ new PolygonMarkerPrimitive2D(
+ aLineB,
+ getRGBColorA(),
+ getRGBColorB(),
+ getDiscreteDashLength()));
+
+ break;
+ }
+ }
+ }
+
+ bool OverlayHelplineStripedPrimitive::operator==( const BasePrimitive2D& rPrimitive ) const
+ {
+ if(ViewportDependentPrimitive2D::operator==(rPrimitive))
+ {
+ const OverlayHelplineStripedPrimitive& rCompare = static_cast< const OverlayHelplineStripedPrimitive& >(rPrimitive);
+
+ return (getBasePosition() == rCompare.getBasePosition()
+ && getStyle() == rCompare.getStyle()
+ && getRGBColorA() == rCompare.getRGBColorA()
+ && getRGBColorB() == rCompare.getRGBColorB()
+ && getDiscreteDashLength() == rCompare.getDiscreteDashLength());
+ }
+
+ return false;
+ }
+
+ sal_uInt32 OverlayHelplineStripedPrimitive::getPrimitive2DID() const
+ {
+ return PRIMITIVE2D_ID_OVERLAYHELPLINESTRIPEDPRIMITIVE;
+ }
+
+
+
+ OverlayRollingRectanglePrimitive::OverlayRollingRectanglePrimitive(
+ const basegfx::B2DRange& aRollingRectangle,
+ const basegfx::BColor& rRGBColorA,
+ const basegfx::BColor& rRGBColorB,
+ double fDiscreteDashLength)
+ : maRollingRectangle(aRollingRectangle),
+ maRGBColorA(rRGBColorA),
+ maRGBColorB(rRGBColorB),
+ mfDiscreteDashLength(fDiscreteDashLength)
+ {}
+
+ void OverlayRollingRectanglePrimitive::create2DDecomposition(Primitive2DContainer& rContainer, const geometry::ViewInformation2D& /*rViewInformation*/) const
+ {
+ // use the prepared Viewport information accessible using getViewport()
+
+ if(getViewport().isEmpty())
+ return;
+
+ basegfx::B2DPolygon aLine;
+
+ // Left lines
+ aLine.append(basegfx::B2DPoint(getViewport().getMinX(), getRollingRectangle().getMinY()));
+ aLine.append(basegfx::B2DPoint(getRollingRectangle().getMinX(), getRollingRectangle().getMinY()));
+ rContainer.push_back(new PolygonMarkerPrimitive2D(aLine, getRGBColorA(), getRGBColorB(), getDiscreteDashLength()));
+
+ aLine.clear();
+ aLine.append(basegfx::B2DPoint(getViewport().getMinX(), getRollingRectangle().getMaxY()));
+ aLine.append(basegfx::B2DPoint(getRollingRectangle().getMinX(), getRollingRectangle().getMaxY()));
+ rContainer.push_back(new PolygonMarkerPrimitive2D(aLine, getRGBColorA(), getRGBColorB(), getDiscreteDashLength()));
+
+ // Right lines
+ aLine.clear();
+ aLine.append(basegfx::B2DPoint(getRollingRectangle().getMaxX(), getRollingRectangle().getMinY()));
+ aLine.append(basegfx::B2DPoint(getViewport().getMaxX(), getRollingRectangle().getMinY()));
+ rContainer.push_back(new PolygonMarkerPrimitive2D(aLine, getRGBColorA(), getRGBColorB(), getDiscreteDashLength()));
+
+ aLine.clear();
+ aLine.append(basegfx::B2DPoint(getRollingRectangle().getMaxX(), getRollingRectangle().getMaxY()));
+ aLine.append(basegfx::B2DPoint(getViewport().getMaxX(), getRollingRectangle().getMaxY()));
+ rContainer.push_back(new PolygonMarkerPrimitive2D(aLine, getRGBColorA(), getRGBColorB(), getDiscreteDashLength()));
+
+ // Top lines
+ aLine.clear();
+ aLine.append(basegfx::B2DPoint(getRollingRectangle().getMinX(), getViewport().getMinY()));
+ aLine.append(basegfx::B2DPoint(getRollingRectangle().getMinX(), getRollingRectangle().getMinY()));
+ rContainer.push_back(new PolygonMarkerPrimitive2D(aLine, getRGBColorA(), getRGBColorB(), getDiscreteDashLength()));
+
+ aLine.clear();
+ aLine.append(basegfx::B2DPoint(getRollingRectangle().getMaxX(), getViewport().getMinY()));
+ aLine.append(basegfx::B2DPoint(getRollingRectangle().getMaxX(), getRollingRectangle().getMinY()));
+ rContainer.push_back(new PolygonMarkerPrimitive2D(aLine, getRGBColorA(), getRGBColorB(), getDiscreteDashLength()));
+
+ // Bottom lines
+ aLine.clear();
+ aLine.append(basegfx::B2DPoint(getRollingRectangle().getMinX(), getRollingRectangle().getMaxY()));
+ aLine.append(basegfx::B2DPoint(getRollingRectangle().getMinX(), getViewport().getMaxY()));
+ rContainer.push_back(new PolygonMarkerPrimitive2D(aLine, getRGBColorA(), getRGBColorB(), getDiscreteDashLength()));
+
+ aLine.clear();
+ aLine.append(basegfx::B2DPoint(getRollingRectangle().getMaxX(), getRollingRectangle().getMaxY()));
+ aLine.append(basegfx::B2DPoint(getRollingRectangle().getMaxX(), getViewport().getMaxY()));
+ rContainer.push_back(new PolygonMarkerPrimitive2D(aLine, getRGBColorA(), getRGBColorB(), getDiscreteDashLength()));
+
+ }
+
+ bool OverlayRollingRectanglePrimitive::operator==( const BasePrimitive2D& rPrimitive ) const
+ {
+ if(ViewportDependentPrimitive2D::operator==(rPrimitive))
+ {
+ const OverlayRollingRectanglePrimitive& rCompare = static_cast< const OverlayRollingRectanglePrimitive& >(rPrimitive);
+
+ return (getRollingRectangle() == rCompare.getRollingRectangle()
+ && getRGBColorA() == rCompare.getRGBColorA()
+ && getRGBColorB() == rCompare.getRGBColorB()
+ && getDiscreteDashLength() == rCompare.getDiscreteDashLength());
+ }
+
+ return false;
+ }
+
+ sal_uInt32 OverlayRollingRectanglePrimitive::getPrimitive2DID() const
+ {
+ return PRIMITIVE2D_ID_OVERLAYROLLINGRECTANGLEPRIMITIVE;
+ }
+
+} // end of namespace
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/sdr/overlay/overlaytriangle.cxx b/svx/source/sdr/overlay/overlaytriangle.cxx
new file mode 100644
index 000000000..f46fcf1e2
--- /dev/null
+++ b/svx/source/sdr/overlay/overlaytriangle.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 <sdr/overlay/overlaytriangle.hxx>
+#include <basegfx/polygon/b2dpolygon.hxx>
+#include <drawinglayer/primitive2d/PolyPolygonColorPrimitive2D.hxx>
+
+
+namespace sdr::overlay
+{
+ drawinglayer::primitive2d::Primitive2DContainer OverlayTriangle::createOverlayObjectPrimitive2DSequence()
+ {
+ basegfx::B2DPolygon aPolygon;
+
+ aPolygon.append(getBasePosition());
+ aPolygon.append(maSecondPosition);
+ aPolygon.append(maThirdPosition);
+ aPolygon.setClosed(true);
+
+ const drawinglayer::primitive2d::Primitive2DReference aReference(
+ new drawinglayer::primitive2d::PolyPolygonColorPrimitive2D(
+ basegfx::B2DPolyPolygon(aPolygon),
+ getBaseColor().getBColor()));
+
+ return drawinglayer::primitive2d::Primitive2DContainer { aReference };
+ }
+
+ OverlayTriangle::OverlayTriangle(
+ const basegfx::B2DPoint& rBasePos,
+ const basegfx::B2DPoint& rSecondPos,
+ const basegfx::B2DPoint& rThirdPos,
+ Color aTriangleColor)
+ : OverlayObjectWithBasePosition(rBasePos, aTriangleColor),
+ maSecondPosition(rSecondPos),
+ maThirdPosition(rThirdPos)
+ {
+ }
+
+ OverlayTriangle::~OverlayTriangle()
+ {
+ }
+
+} // end of namespace
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/sdr/primitive2d/primitivefactory2d.cxx b/svx/source/sdr/primitive2d/primitivefactory2d.cxx
new file mode 100644
index 000000000..c6530fd13
--- /dev/null
+++ b/svx/source/sdr/primitive2d/primitivefactory2d.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 <com/sun/star/uno/XComponentContext.hpp>
+#include <sdr/primitive2d/primitivefactory2d.hxx>
+#include <svx/sdr/contact/viewcontact.hxx>
+#include <svx/svdobj.hxx>
+#include <svx/svdpage.hxx>
+#include <svx/unoapi.hxx>
+
+using namespace com::sun::star;
+
+css::uno::Sequence< css::uno::Reference< css::graphic::XPrimitive2D > > SAL_CALL PrimitiveFactory2D::createPrimitivesFromXShape(
+ const uno::Reference< drawing::XShape >& xShape,
+ const uno::Sequence< beans::PropertyValue >& /*aParms*/ )
+{
+ css::uno::Sequence< css::uno::Reference< css::graphic::XPrimitive2D > > aRetval;
+
+ if(xShape.is())
+ {
+ SdrObject* pSource = SdrObject::getSdrObjectFromXShape(xShape);
+
+ if(pSource)
+ {
+ const sdr::contact::ViewContact& rSource(pSource->GetViewContact());
+ drawinglayer::primitive2d::Primitive2DContainer aSourceVal;
+ rSource.getViewIndependentPrimitive2DContainer(aSourceVal);
+ aRetval = aSourceVal.toSequence();
+ }
+ }
+
+ return aRetval;
+}
+
+void PrimitiveFactory2D::createPrimitivesFromXShape(
+ const uno::Reference< drawing::XShape >& xShape,
+ const uno::Sequence< beans::PropertyValue >& /*aParms*/,
+ drawinglayer::primitive2d::Primitive2DDecompositionVisitor& rVisitor)
+{
+ if(xShape.is())
+ {
+ SdrObject* pSource = SdrObject::getSdrObjectFromXShape(xShape);
+
+ if(pSource)
+ {
+ const sdr::contact::ViewContact& rSource(pSource->GetViewContact());
+ rSource.getViewIndependentPrimitive2DContainer(rVisitor);
+ }
+ }
+}
+
+css::uno::Sequence< css::uno::Reference< css::graphic::XPrimitive2D > > SAL_CALL PrimitiveFactory2D::createPrimitivesFromXDrawPage(
+ const uno::Reference< drawing::XDrawPage >& xDrawPage,
+ const uno::Sequence< beans::PropertyValue >& /*aParms*/ )
+{
+ css::uno::Sequence< css::uno::Reference< css::graphic::XPrimitive2D > > aRetval;
+
+ if(xDrawPage.is())
+ {
+ SdrPage* pSource = GetSdrPageFromXDrawPage(xDrawPage);
+
+ if(pSource)
+ {
+ const sdr::contact::ViewContact& rSource(pSource->GetViewContact());
+ drawinglayer::primitive2d::Primitive2DContainer aSourceRetval;
+ rSource.getViewIndependentPrimitive2DContainer(aSourceRetval);
+ aRetval = aSourceRetval.toSequence();
+ }
+ }
+
+ return aRetval;
+}
+
+extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface *
+com_sun_star_comp_graphic_PrimitiveFactory2D_get_implementation(
+ css::uno::XComponentContext *,
+ css::uno::Sequence<css::uno::Any> const &)
+{
+ return cppu::acquire(new PrimitiveFactory2D);
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/sdr/primitive2d/sdrattributecreator.cxx b/svx/source/sdr/primitive2d/sdrattributecreator.cxx
new file mode 100644
index 000000000..bafa96f95
--- /dev/null
+++ b/svx/source/sdr/primitive2d/sdrattributecreator.cxx
@@ -0,0 +1,1151 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <sdr/primitive2d/sdrattributecreator.hxx>
+#include <svl/itemset.hxx>
+#include <svx/sdmetitm.hxx>
+#include <svx/sdooitm.hxx>
+#include <svx/sdprcitm.hxx>
+#include <svx/xdef.hxx>
+#include <basegfx/polygon/b2dpolygon.hxx>
+#include <svx/xlineit0.hxx>
+#include <svx/xfillit0.hxx>
+#include <svx/xflbmpit.hxx>
+#include <svx/xlntrit.hxx>
+#include <svx/xlnwtit.hxx>
+#include <svx/xlinjoit.hxx>
+#include <svx/xlncapit.hxx>
+#include <svx/xlnclit.hxx>
+#include <svx/xlnstwit.hxx>
+#include <svx/xlnedwit.hxx>
+#include <svx/xlnstit.hxx>
+#include <svx/xlnstcit.hxx>
+#include <svx/xlnedit.hxx>
+#include <svx/xlnedcit.hxx>
+#include <svx/xdash.hxx>
+#include <svx/xlndsit.hxx>
+#include <svx/xfilluseslidebackgrounditem.hxx>
+#include <svx/xfltrit.hxx>
+#include <svx/xflftrit.hxx>
+#include <svx/xflclit.hxx>
+#include <svx/xgrscit.hxx>
+#include <svx/xflhtit.hxx>
+#include <svx/xflbckit.hxx>
+#include <svx/xflbmsxy.hxx>
+#include <svx/xflbtoxy.hxx>
+#include <svx/xflboxy.hxx>
+#include <svx/xflbmtit.hxx>
+#include <svx/xflbstit.hxx>
+#include <svx/xtextit0.hxx>
+#include <drawinglayer/attribute/sdrfillgraphicattribute.hxx>
+#include <svx/svdotext.hxx>
+#include <sdr/attribute/sdrtextattribute.hxx>
+#include <svx/xbtmpit.hxx>
+#include <svl/itempool.hxx>
+#include <vcl/svapp.hxx>
+#include <vcl/GraphicLoader.hxx>
+#include <basegfx/range/b2drange.hxx>
+#include <svx/svx3ditems.hxx>
+#include <com/sun/star/drawing/ProjectionMode.hpp>
+#include <com/sun/star/drawing/ShadeMode.hpp>
+#include <drawinglayer/attribute/sdrallattribute3d.hxx>
+#include <svx/rectenum.hxx>
+#include <svx/sdtfchim.hxx>
+#include <svx/svdoutl.hxx>
+#include <svx/svdmodel.hxx>
+#include <svx/xflbmsli.hxx>
+#include <editeng/editstat.hxx>
+#include <osl/diagnose.h>
+#include <drawinglayer/attribute/fillhatchattribute.hxx>
+#include <drawinglayer/attribute/fillgradientattribute.hxx>
+#include <sdr/attribute/sdreffectstextattribute.hxx>
+#include <sdr/attribute/sdrlineeffectstextattribute.hxx>
+#include <sdr/attribute/sdrformtextattribute.hxx>
+#include <sdr/attribute/sdrlinefilleffectstextattribute.hxx>
+#include <drawinglayer/attribute/sdrglowattribute.hxx>
+#include <drawinglayer/attribute/sdrsceneattribute3d.hxx>
+#include <drawinglayer/attribute/sdrlightingattribute3d.hxx>
+#include <drawinglayer/attribute/sdrlightattribute3d.hxx>
+#include <sdr/attribute/sdrfilltextattribute.hxx>
+#include <com/sun/star/drawing/LineCap.hpp>
+
+using namespace com::sun::star;
+
+namespace drawinglayer
+{
+ namespace
+ {
+ attribute::GradientStyle XGradientStyleToGradientStyle(css::awt::GradientStyle eStyle)
+ {
+ switch(eStyle)
+ {
+ case css::awt::GradientStyle_LINEAR :
+ {
+ return attribute::GradientStyle::Linear;
+ }
+ case css::awt::GradientStyle_AXIAL :
+ {
+ return attribute::GradientStyle::Axial;
+ }
+ case css::awt::GradientStyle_RADIAL :
+ {
+ return attribute::GradientStyle::Radial;
+ }
+ case css::awt::GradientStyle_ELLIPTICAL :
+ {
+ return attribute::GradientStyle::Elliptical;
+ }
+ case css::awt::GradientStyle_SQUARE :
+ {
+ return attribute::GradientStyle::Square;
+ }
+ default :
+ {
+ return attribute::GradientStyle::Rect; // css::awt::GradientStyle_RECT
+ }
+ }
+ }
+
+ attribute::HatchStyle XHatchStyleToHatchStyle(css::drawing::HatchStyle eStyle)
+ {
+ switch(eStyle)
+ {
+ case css::drawing::HatchStyle_SINGLE :
+ {
+ return attribute::HatchStyle::Single;
+ }
+ case css::drawing::HatchStyle_DOUBLE :
+ {
+ return attribute::HatchStyle::Double;
+ }
+ default :
+ {
+ return attribute::HatchStyle::Triple; // css::drawing::HatchStyle_TRIPLE
+ }
+ }
+ }
+
+ basegfx::B2DLineJoin LineJointToB2DLineJoin(css::drawing::LineJoint eLineJoint)
+ {
+ switch(eLineJoint)
+ {
+ case css::drawing::LineJoint_BEVEL :
+ {
+ return basegfx::B2DLineJoin::Bevel;
+ }
+ case css::drawing::LineJoint_MIDDLE :
+ case css::drawing::LineJoint_MITER :
+ {
+ return basegfx::B2DLineJoin::Miter;
+ }
+ case css::drawing::LineJoint_ROUND :
+ {
+ return basegfx::B2DLineJoin::Round;
+ }
+ default : // css::drawing::LineJoint_NONE
+ {
+ return basegfx::B2DLineJoin::NONE;
+ }
+ }
+ }
+
+ basegfx::B2DVector RectPointToB2DVector(RectPoint eRectPoint)
+ {
+ basegfx::B2DVector aRetval(0.0, 0.0);
+
+ // position changes X
+ switch(eRectPoint)
+ {
+ case RectPoint::LT: case RectPoint::LM: case RectPoint::LB:
+ {
+ aRetval.setX(-1.0);
+ break;
+ }
+
+ case RectPoint::RT: case RectPoint::RM: case RectPoint::RB:
+ {
+ aRetval.setX(1.0);
+ break;
+ }
+
+ default :
+ {
+ break;
+ }
+ }
+
+ // position changes Y
+ switch(eRectPoint)
+ {
+ case RectPoint::LT: case RectPoint::MT: case RectPoint::RT:
+ {
+ aRetval.setY(-1.0);
+ break;
+ }
+
+ case RectPoint::LB: case RectPoint::MB: case RectPoint::RB:
+ {
+ aRetval.setY(1.0);
+ break;
+ }
+
+ default :
+ {
+ break;
+ }
+ }
+
+ return aRetval;
+ }
+
+ attribute::SdrGlowAttribute createNewSdrGlowAttribute(const SfxItemSet& rSet)
+ {
+ sal_Int32 nRadius = rSet.Get(SDRATTR_GLOW_RADIUS).GetValue();
+ if (!nRadius)
+ return attribute::SdrGlowAttribute();
+ Color aColor(rSet.Get(SDRATTR_GLOW_COLOR).GetColorValue());
+ sal_uInt16 nTransparency(rSet.Get(SDRATTR_GLOW_TRANSPARENCY).GetValue());
+ if (nTransparency)
+ aColor.SetAlpha(255 - std::round(nTransparency / 100.0 * 255.0));
+
+ attribute::SdrGlowAttribute glowAttr{ nRadius, aColor };
+ return glowAttr;
+ }
+
+ sal_Int32 getSoftEdgeRadius(const SfxItemSet& rSet)
+ {
+ return rSet.Get(SDRATTR_SOFTEDGE_RADIUS).GetValue();
+ }
+ } // end of anonymous namespace
+} // end of namespace drawinglayer
+
+
+namespace drawinglayer::primitive2d
+{
+ attribute::SdrLineAttribute createNewSdrLineAttribute(const SfxItemSet& rSet)
+ {
+ const css::drawing::LineStyle eStyle(rSet.Get(XATTR_LINESTYLE).GetValue());
+
+ if(drawing::LineStyle_NONE != eStyle)
+ {
+ sal_uInt16 nTransparence(rSet.Get(XATTR_LINETRANSPARENCE).GetValue());
+
+ if(nTransparence > 100)
+ {
+ nTransparence = 100;
+ }
+
+ if(100 != nTransparence)
+ {
+ const sal_uInt32 nWidth(rSet.Get(XATTR_LINEWIDTH).GetValue());
+ const Color aColor(rSet.Get(XATTR_LINECOLOR).GetColorValue());
+ const css::drawing::LineJoint eJoint(rSet.Get(XATTR_LINEJOINT).GetValue());
+ const css::drawing::LineCap eCap(rSet.Get(XATTR_LINECAP).GetValue());
+ ::std::vector< double > aDotDashArray;
+ double fFullDotDashLen(0.0);
+
+ if(drawing::LineStyle_DASH == eStyle)
+ {
+ const XDash& rDash = rSet.Get(XATTR_LINEDASH).GetDashValue();
+
+ if(rDash.GetDots() || rDash.GetDashes())
+ {
+ fFullDotDashLen = rDash.CreateDotDashArray(aDotDashArray, static_cast<double>(nWidth));
+ }
+ }
+
+ return attribute::SdrLineAttribute(
+ LineJointToB2DLineJoin(eJoint),
+ static_cast<double>(nWidth),
+ static_cast<double>(nTransparence) * 0.01,
+ aColor.getBColor(),
+ eCap,
+ std::move(aDotDashArray),
+ fFullDotDashLen);
+ }
+ }
+
+ return attribute::SdrLineAttribute();
+ }
+
+ attribute::SdrLineStartEndAttribute createNewSdrLineStartEndAttribute(
+ const SfxItemSet& rSet,
+ double fWidth)
+ {
+ const sal_Int32 nTempStartWidth(rSet.Get(XATTR_LINESTARTWIDTH).GetValue());
+ const sal_Int32 nTempEndWidth(rSet.Get(XATTR_LINEENDWIDTH).GetValue());
+ basegfx::B2DPolyPolygon aStartPolyPolygon;
+ basegfx::B2DPolyPolygon aEndPolyPolygon;
+ double fStartWidth(0.0);
+ double fEndWidth(0.0);
+ bool bStartActive(false);
+ bool bEndActive(false);
+ bool bStartCentered(true);
+ bool bEndCentered(true);
+
+ if(nTempStartWidth)
+ {
+ if(nTempStartWidth < 0)
+ {
+ fStartWidth = (static_cast<double>(-nTempStartWidth) * fWidth) * 0.01;
+ }
+ else
+ {
+ fStartWidth = static_cast<double>(nTempStartWidth);
+ }
+
+ if(0.0 != fStartWidth)
+ {
+ aStartPolyPolygon = rSet.Get(XATTR_LINESTART).GetLineStartValue();
+
+ if(aStartPolyPolygon.count() && aStartPolyPolygon.getB2DPolygon(0).count())
+ {
+ bStartActive = true;
+ bStartCentered = rSet.Get(XATTR_LINESTARTCENTER).GetValue();
+ }
+ }
+ }
+
+ if(nTempEndWidth)
+ {
+ if(nTempEndWidth < 0)
+ {
+ fEndWidth = (static_cast<double>(-nTempEndWidth) * fWidth) * 0.01;
+ }
+ else
+ {
+ fEndWidth = static_cast<double>(nTempEndWidth);
+ }
+
+ if(0.0 != fEndWidth)
+ {
+ aEndPolyPolygon = rSet.Get(XATTR_LINEEND).GetLineEndValue();
+
+ if(aEndPolyPolygon.count() && aEndPolyPolygon.getB2DPolygon(0).count())
+ {
+ bEndActive = true;
+ bEndCentered = rSet.Get(XATTR_LINEENDCENTER).GetValue();
+ }
+ }
+ }
+
+ if(bStartActive || bEndActive)
+ {
+ return attribute::SdrLineStartEndAttribute(
+ aStartPolyPolygon, aEndPolyPolygon, fStartWidth, fEndWidth,
+ bStartActive, bEndActive, bStartCentered, bEndCentered);
+ }
+
+ return attribute::SdrLineStartEndAttribute();
+ }
+
+ attribute::SdrShadowAttribute createNewSdrShadowAttribute(const SfxItemSet& rSet)
+ {
+ const bool bShadow(rSet.Get(SDRATTR_SHADOW).GetValue());
+
+ if(bShadow)
+ {
+ sal_uInt16 nTransparence(rSet.Get(SDRATTR_SHADOWTRANSPARENCE).GetValue());
+
+ if(nTransparence > 100)
+ {
+ nTransparence = 100;
+ }
+
+ if(nTransparence)
+ {
+ sal_uInt16 nFillTransparence(rSet.Get(XATTR_FILLTRANSPARENCE).GetValue());
+
+ if(nFillTransparence > 100)
+ {
+ nFillTransparence = 100;
+ }
+
+ if(nTransparence == nFillTransparence)
+ {
+ // shadow does not really have an own transparence, but the application
+ // sets the shadow transparence equal to the object transparence for
+ // convenience. This is not useful for primitive creation, so take
+ // this as no shadow transparence
+ nTransparence = 0;
+ }
+ }
+
+ if(100 != nTransparence)
+ {
+ const basegfx::B2DVector aOffset(
+ static_cast<double>(rSet.Get(SDRATTR_SHADOWXDIST).GetValue()),
+ static_cast<double>(rSet.Get(SDRATTR_SHADOWYDIST).GetValue()));
+
+ const basegfx::B2DVector aSize(
+ static_cast<double>(rSet.Get(SDRATTR_SHADOWSIZEX).GetValue()),
+ static_cast<double>(rSet.Get(SDRATTR_SHADOWSIZEY).GetValue()));
+
+ const Color aColor(rSet.Get(SDRATTR_SHADOWCOLOR).GetColorValue());
+
+ sal_Int32 nBlur(rSet.Get(SDRATTR_SHADOWBLUR).GetValue());
+
+ return attribute::SdrShadowAttribute(aOffset, aSize, static_cast<double>(nTransparence) * 0.01,nBlur, aColor.getBColor());
+ }
+ }
+
+ return attribute::SdrShadowAttribute();
+ }
+
+ attribute::SdrFillAttribute createNewSdrFillAttribute(const SfxItemSet& rSet)
+ {
+ const drawing::FillStyle eStyle(rSet.Get(XATTR_FILLSTYLE).GetValue());
+
+ sal_uInt16 nTransparence(rSet.Get(XATTR_FILLTRANSPARENCE).GetValue());
+
+ if(nTransparence > 100)
+ {
+ nTransparence = 100;
+ }
+
+ if(drawing::FillStyle_NONE == eStyle)
+ {
+ XFillUseSlideBackgroundItem aBckItem(rSet.Get(XATTR_FILLUSESLIDEBACKGROUND));
+ const bool bSlideBackgroundFill(aBckItem.GetValue());
+
+ if(bSlideBackgroundFill)
+ {
+ // we have SlideBackgroundFill mode, create a
+ // SdrFillAttribute accordingly
+ return attribute::SdrFillAttribute(true);
+ }
+ }
+
+ if(drawing::FillStyle_NONE != eStyle)
+ {
+ if(100 != nTransparence)
+ {
+ // need to check XFillFloatTransparence, object fill may still be completely transparent
+ const XFillFloatTransparenceItem* pGradientItem;
+
+ if((pGradientItem = rSet.GetItemIfSet(XATTR_FILLFLOATTRANSPARENCE, true))
+ && pGradientItem->IsEnabled())
+ {
+ const XGradient& rGradient = pGradientItem->GetGradientValue();
+ const sal_uInt8 nStartLuminance(rGradient.GetStartColor().GetLuminance());
+ const sal_uInt8 nEndLuminance(rGradient.GetEndColor().GetLuminance());
+ const bool bCompletelyTransparent(0xff == nStartLuminance && 0xff == nEndLuminance);
+
+ if(bCompletelyTransparent)
+ {
+ nTransparence = 100;
+ }
+ }
+ }
+
+ if(100 != nTransparence)
+ {
+ const Color aColor(rSet.Get(XATTR_FILLCOLOR).GetColorValue());
+ attribute::FillGradientAttribute aGradient;
+ attribute::FillHatchAttribute aHatch;
+ attribute::SdrFillGraphicAttribute aFillGraphic;
+
+ switch(eStyle)
+ {
+ default:
+ {
+ // nothing to do, color is defined
+ break;
+ }
+ case drawing::FillStyle_GRADIENT :
+ {
+ XGradient aXGradient(rSet.Get(XATTR_FILLGRADIENT).GetGradientValue());
+
+ const Color aStartColor(aXGradient.GetStartColor());
+ const sal_uInt16 nStartIntens(aXGradient.GetStartIntens());
+ basegfx::BColor aStart(aStartColor.getBColor());
+
+ if(nStartIntens != 100)
+ {
+ const basegfx::BColor aBlack;
+ aStart = interpolate(aBlack, aStart, static_cast<double>(nStartIntens) * 0.01);
+ }
+
+ const Color aEndColor(aXGradient.GetEndColor());
+ const sal_uInt16 nEndIntens(aXGradient.GetEndIntens());
+ basegfx::BColor aEnd(aEndColor.getBColor());
+
+ if(nEndIntens != 100)
+ {
+ const basegfx::BColor aBlack;
+ aEnd = interpolate(aBlack, aEnd, static_cast<double>(nEndIntens) * 0.01);
+ }
+
+ aGradient = attribute::FillGradientAttribute(
+ XGradientStyleToGradientStyle(aXGradient.GetGradientStyle()),
+ static_cast<double>(aXGradient.GetBorder()) * 0.01,
+ static_cast<double>(aXGradient.GetXOffset()) * 0.01,
+ static_cast<double>(aXGradient.GetYOffset()) * 0.01,
+ toRadians(aXGradient.GetAngle()),
+ aStart,
+ aEnd,
+ rSet.Get(XATTR_GRADIENTSTEPCOUNT).GetValue());
+
+ break;
+ }
+ case drawing::FillStyle_HATCH :
+ {
+ const XHatch& rHatch(rSet.Get(XATTR_FILLHATCH).GetHatchValue());
+ const Color aColorB(rHatch.GetColor());
+
+ aHatch = attribute::FillHatchAttribute(
+ XHatchStyleToHatchStyle(rHatch.GetHatchStyle()),
+ static_cast<double>(rHatch.GetDistance()),
+ toRadians(rHatch.GetAngle()),
+ aColorB.getBColor(),
+ 3, // same default as VCL, a minimum of three discrete units (pixels) offset
+ rSet.Get(XATTR_FILLBACKGROUND).GetValue());
+
+ break;
+ }
+ case drawing::FillStyle_BITMAP :
+ {
+ aFillGraphic = createNewSdrFillGraphicAttribute(rSet);
+ break;
+ }
+ }
+
+ return attribute::SdrFillAttribute(
+ static_cast<double>(nTransparence) * 0.01,
+ aColor.getBColor(),
+ aGradient,
+ aHatch,
+ aFillGraphic);
+ }
+ }
+
+ if(nTransparence == 100)
+ {
+ attribute::FillGradientAttribute aGradient;
+ attribute::FillHatchAttribute aHatch;
+ attribute::SdrFillGraphicAttribute aFillGraphic;
+ return attribute::SdrFillAttribute(
+ 1,
+ basegfx::BColor( 0, 0, 0 ),
+ aGradient,
+ aHatch,
+ aFillGraphic);
+ }
+
+ return attribute::SdrFillAttribute();
+ }
+
+ // #i101508# Support handing over given text-to-border distances
+ attribute::SdrTextAttribute createNewSdrTextAttribute(
+ const SfxItemSet& rSet,
+ const SdrText& rText,
+ const sal_Int32* pLeft,
+ const sal_Int32* pUpper,
+ const sal_Int32* pRight,
+ const sal_Int32* pLower)
+ {
+ const SdrTextObj& rTextObj = rText.GetObject();
+
+ // Save chaining attributes
+ bool bChainable = rTextObj.IsChainable();
+
+
+ if(rText.GetOutlinerParaObject())
+ {
+ // added TextEdit text suppression
+ bool bInEditMode(false);
+
+ if(rText.GetObject().getTextCount() > 1)
+ {
+ bInEditMode = rTextObj.IsInEditMode() && rText.GetObject().getActiveText() == &rText;
+ }
+ else
+ {
+ bInEditMode = rTextObj.IsInEditMode();
+ }
+
+ OutlinerParaObject aOutlinerParaObject(*rText.GetOutlinerParaObject());
+
+ if(bInEditMode)
+ {
+ std::optional<OutlinerParaObject> pTempObj = rTextObj.CreateEditOutlinerParaObject();
+
+ if(pTempObj)
+ {
+ aOutlinerParaObject = *pTempObj;
+ }
+ else
+ {
+ // #i100537#
+ // CreateEditOutlinerParaObject() returning no object does not mean that
+ // text edit mode is not active. Do not reset the flag here
+ // bInEditMode = false;
+ }
+ }
+
+ const SdrTextAniKind eAniKind(rTextObj.GetTextAniKind());
+
+ // #i107346#
+ const SdrOutliner& rDrawTextOutliner(rText.GetObject().getSdrModelFromSdrObject().GetDrawOutliner(&rTextObj));
+ const bool bWrongSpell(rDrawTextOutliner.GetControlWord() & EEControlBits::ONLINESPELLING);
+
+ return attribute::SdrTextAttribute(
+ rText,
+ aOutlinerParaObject,
+ rSet.Get(XATTR_FORMTXTSTYLE).GetValue(),
+ pLeft ? *pLeft : rTextObj.GetTextLeftDistance(),
+ pUpper ? *pUpper : rTextObj.GetTextUpperDistance(),
+ pRight ? *pRight : rTextObj.GetTextRightDistance(),
+ pLower ? *pLower : rTextObj.GetTextLowerDistance(),
+ rTextObj.GetTextHorizontalAdjust(rSet),
+ rTextObj.GetTextVerticalAdjust(rSet),
+ rSet.Get(SDRATTR_TEXT_CONTOURFRAME).GetValue(),
+ rTextObj.IsFitToSize(),
+ rTextObj.IsAutoFit(),
+ rSet.Get(XATTR_FORMTXTHIDEFORM).GetValue(),
+ SdrTextAniKind::Blink == eAniKind,
+ SdrTextAniKind::Scroll == eAniKind || SdrTextAniKind::Alternate == eAniKind || SdrTextAniKind::Slide == eAniKind,
+ bInEditMode,
+ rSet.Get(SDRATTR_TEXT_USEFIXEDCELLHEIGHT).GetValue(),
+ bWrongSpell,
+ bChainable);
+ }
+
+ return attribute::SdrTextAttribute();
+ }
+
+ attribute::FillGradientAttribute createNewTransparenceGradientAttribute(const SfxItemSet& rSet)
+ {
+ const XFillFloatTransparenceItem* pGradientItem;
+
+ if((pGradientItem = rSet.GetItemIfSet(XATTR_FILLFLOATTRANSPARENCE))
+ && pGradientItem->IsEnabled())
+ {
+ // test if float transparence is completely transparent
+ const XGradient& rGradient = pGradientItem->GetGradientValue();
+ const sal_uInt8 nStartLuminance(rGradient.GetStartColor().GetLuminance());
+ const sal_uInt8 nEndLuminance(rGradient.GetEndColor().GetLuminance());
+ const bool bCompletelyTransparent(0xff == nStartLuminance && 0xff == nEndLuminance);
+ const bool bNotTransparent(0x00 == nStartLuminance && 0x00 == nEndLuminance);
+
+ // create nothing when completely transparent: This case is already checked for the
+ // normal fill attributes, XFILL_NONE will be used.
+ // create nothing when not transparent: use normal fill, no need t create a FillGradientAttribute.
+ // Both cases are optimizations, always creating FillGradientAttribute will work, too
+ if(!bNotTransparent && !bCompletelyTransparent)
+ {
+ const double fStartLum(nStartLuminance / 255.0);
+ const double fEndLum(nEndLuminance / 255.0);
+
+ return attribute::FillGradientAttribute(
+ XGradientStyleToGradientStyle(rGradient.GetGradientStyle()),
+ static_cast<double>(rGradient.GetBorder()) * 0.01,
+ static_cast<double>(rGradient.GetXOffset()) * 0.01,
+ static_cast<double>(rGradient.GetYOffset()) * 0.01,
+ toRadians(rGradient.GetAngle()),
+ basegfx::BColor(fStartLum, fStartLum, fStartLum),
+ basegfx::BColor(fEndLum, fEndLum, fEndLum));
+ }
+ }
+
+ return attribute::FillGradientAttribute();
+ }
+
+ attribute::SdrFillGraphicAttribute createNewSdrFillGraphicAttribute(const SfxItemSet& rSet)
+ {
+ Graphic aGraphic(rSet.Get(XATTR_FILLBITMAP).GetGraphicObject().GetGraphic());
+
+ OUString aOriginURL = aGraphic.getOriginURL();
+ if (aGraphic.GetType() == GraphicType::Default && !aOriginURL.isEmpty())
+ {
+ aGraphic = vcl::graphic::loadFromURL(aGraphic.getOriginURL());
+ aGraphic.setOriginURL(aOriginURL);
+ }
+
+ if(GraphicType::Bitmap != aGraphic.GetType() && GraphicType::GdiMetafile != aGraphic.GetType())
+ {
+ // no content if not bitmap or metafile
+ OSL_ENSURE(false, "No fill graphic in SfxItemSet (!)");
+ return attribute::SdrFillGraphicAttribute();
+ }
+
+ Size aPrefSize(aGraphic.GetPrefSize());
+
+ if(!aPrefSize.Width() || !aPrefSize.Height())
+ {
+ // if there is no logical size, create a size from pixel size and set MapMode accordingly
+ if(GraphicType::Bitmap == aGraphic.GetType())
+ {
+ aGraphic.SetPrefSize(aGraphic.GetBitmapEx().GetSizePixel());
+ aGraphic.SetPrefMapMode(MapMode(MapUnit::MapPixel));
+ aPrefSize = aGraphic.GetPrefSize();
+ }
+ }
+
+ if(!aPrefSize.Width() || !aPrefSize.Height())
+ {
+ // no content if no size
+ OSL_ENSURE(false, "Graphic has no size in SfxItemSet (!)");
+ return attribute::SdrFillGraphicAttribute();
+ }
+
+ // convert size and MapMode to destination logical size and MapMode
+ const MapUnit aDestinationMapUnit(rSet.GetPool()->GetMetric(0));
+ basegfx::B2DVector aGraphicLogicSize(aGraphic.GetPrefSize().Width(), aGraphic.GetPrefSize().Height());
+
+ if (aGraphic.GetPrefMapMode().GetMapUnit() != aDestinationMapUnit)
+ {
+ // #i100360# for MapUnit::MapPixel, LogicToLogic will not work properly,
+ // so fallback to Application::GetDefaultDevice()
+ Size aNewSize(0, 0);
+
+ if(MapUnit::MapPixel == aGraphic.GetPrefMapMode().GetMapUnit())
+ {
+ aNewSize = Application::GetDefaultDevice()->PixelToLogic(
+ aGraphic.GetPrefSize(),
+ MapMode(aDestinationMapUnit));
+ }
+ else
+ {
+ aNewSize = OutputDevice::LogicToLogic(
+ aGraphic.GetPrefSize(),
+ aGraphic.GetPrefMapMode(),
+ MapMode(aDestinationMapUnit));
+ }
+
+ // #i124002# do not set new size using SetPrefSize at the graphic, this will lead to problems.
+ // Instead, adapt the GraphicLogicSize which will be used for further decompositions
+ aGraphicLogicSize = basegfx::B2DVector(aNewSize.Width(), aNewSize.Height());
+ }
+
+ // get size
+ const basegfx::B2DVector aSize(
+ static_cast<double>(rSet.Get(XATTR_FILLBMP_SIZEX).GetValue()),
+ static_cast<double>(rSet.Get(XATTR_FILLBMP_SIZEY).GetValue()));
+ const basegfx::B2DVector aOffset(
+ static_cast<double>(rSet.Get(XATTR_FILLBMP_TILEOFFSETX).GetValue()),
+ static_cast<double>(rSet.Get(XATTR_FILLBMP_TILEOFFSETY).GetValue()));
+ const basegfx::B2DVector aOffsetPosition(
+ static_cast<double>(rSet.Get(XATTR_FILLBMP_POSOFFSETX).GetValue()),
+ static_cast<double>(rSet.Get(XATTR_FILLBMP_POSOFFSETY).GetValue()));
+
+ return attribute::SdrFillGraphicAttribute(
+ aGraphic,
+ aGraphicLogicSize,
+ aSize,
+ aOffset,
+ aOffsetPosition,
+ RectPointToB2DVector(rSet.GetItem<XFillBmpPosItem>(XATTR_FILLBMP_POS)->GetValue()),
+ rSet.Get(XATTR_FILLBMP_TILE).GetValue(),
+ rSet.Get(XATTR_FILLBMP_STRETCH).GetValue(),
+ rSet.Get(XATTR_FILLBMP_SIZELOG).GetValue());
+ }
+
+ attribute::SdrEffectsTextAttribute createNewSdrEffectsTextAttribute(
+ const SfxItemSet& rSet,
+ const SdrText* pText,
+ bool bSuppressText)
+ {
+ attribute::SdrTextAttribute aText;
+
+ // #i98072# added option to suppress text
+ // look for text first
+ if(!bSuppressText && pText)
+ {
+ aText = createNewSdrTextAttribute(rSet, *pText);
+ }
+
+ // try shadow
+ const attribute::SdrShadowAttribute aShadow(createNewSdrShadowAttribute(rSet));
+ const attribute::SdrGlowAttribute aGlow(createNewSdrGlowAttribute(rSet));
+ const sal_Int32 nSoftEdgeRadius(getSoftEdgeRadius(rSet));
+
+ return attribute::SdrEffectsTextAttribute(aShadow, aText, aGlow, nSoftEdgeRadius);
+ }
+
+ attribute::SdrLineEffectsTextAttribute createNewSdrLineEffectsTextAttribute(
+ const SfxItemSet& rSet,
+ const SdrText* pText)
+ {
+ attribute::SdrLineAttribute aLine;
+ attribute::SdrLineStartEndAttribute aLineStartEnd;
+ attribute::SdrTextAttribute aText;
+ bool bFontworkHideContour(false);
+
+ // look for text first
+ if(pText)
+ {
+ aText = createNewSdrTextAttribute(rSet, *pText);
+
+ // when object has text and text is fontwork and hide contour is set for fontwork, force
+ // line and fill style to empty
+ if(!aText.isDefault()
+ && !aText.getSdrFormTextAttribute().isDefault()
+ && aText.isHideContour())
+ {
+ bFontworkHideContour = true;
+ }
+ }
+
+ // try line style
+ if(!bFontworkHideContour)
+ {
+ aLine = createNewSdrLineAttribute(rSet);
+
+ if(!aLine.isDefault())
+ {
+ // try LineStartEnd
+ aLineStartEnd = createNewSdrLineStartEndAttribute(rSet, aLine.getWidth());
+ }
+ }
+
+ if(!aLine.isDefault() || !aText.isDefault())
+ {
+ // try shadow
+ const attribute::SdrShadowAttribute aShadow(createNewSdrShadowAttribute(rSet));
+ const attribute::SdrGlowAttribute aGlow = createNewSdrGlowAttribute(rSet);
+ const sal_Int32 nSoftEdgeRadius(getSoftEdgeRadius(rSet));
+
+ return attribute::SdrLineEffectsTextAttribute(aLine, aLineStartEnd, aShadow, aText,
+ aGlow, nSoftEdgeRadius);
+ }
+
+ return attribute::SdrLineEffectsTextAttribute();
+ }
+
+ attribute::SdrLineFillEffectsTextAttribute createNewSdrLineFillEffectsTextAttribute(
+ const SfxItemSet& rSet,
+ const SdrText* pText,
+ bool bHasContent)
+ {
+ attribute::SdrLineAttribute aLine;
+ attribute::SdrFillAttribute aFill;
+ attribute::SdrLineStartEndAttribute aLineStartEnd;
+ attribute::FillGradientAttribute aFillFloatTransGradient;
+ attribute::SdrTextAttribute aText;
+ bool bFontworkHideContour(false);
+
+ // look for text first
+ if(pText)
+ {
+ aText = createNewSdrTextAttribute(rSet, *pText);
+
+ // when object has text and text is fontwork and hide contour is set for fontwork, force
+ // line and fill style to empty
+ if(!aText.getSdrFormTextAttribute().isDefault() && aText.isHideContour())
+ {
+ bFontworkHideContour = true;
+ }
+ }
+
+ if(!bFontworkHideContour)
+ {
+ // try line style
+ aLine = createNewSdrLineAttribute(rSet);
+
+ if(!aLine.isDefault())
+ {
+ // try LineStartEnd
+ aLineStartEnd = createNewSdrLineStartEndAttribute(rSet, aLine.getWidth());
+ }
+
+ // try fill style
+ aFill = createNewSdrFillAttribute(rSet);
+
+ if(!aFill.isDefault())
+ {
+ // try fillfloattransparence
+ aFillFloatTransGradient = createNewTransparenceGradientAttribute(rSet);
+ }
+ }
+
+ // bHasContent is used from OLE and graphic objects. Normally a possible shadow
+ // depends on line, fill or text to be set, but for these objects it is possible
+ // to have none of these, but still content which needs to have a shadow (if set),
+ // so shadow needs to be tried
+ if(bHasContent || !aLine.isDefault() || !aFill.isDefault() || !aText.isDefault())
+ {
+ // try shadow
+ const attribute::SdrShadowAttribute aShadow = createNewSdrShadowAttribute(rSet);
+
+ // glow
+ const attribute::SdrGlowAttribute aGlow = createNewSdrGlowAttribute(rSet);
+
+ const sal_Int32 nSoftEdgeRadius(getSoftEdgeRadius(rSet));
+
+ return attribute::SdrLineFillEffectsTextAttribute(aLine, aFill, aLineStartEnd,
+ aShadow, aFillFloatTransGradient,
+ aText, aGlow, nSoftEdgeRadius);
+ }
+
+ return attribute::SdrLineFillEffectsTextAttribute();
+ }
+
+ attribute::SdrLineFillShadowAttribute3D createNewSdrLineFillShadowAttribute(const SfxItemSet& rSet, bool bSuppressFill)
+ {
+ attribute::SdrFillAttribute aFill;
+ attribute::SdrLineStartEndAttribute aLineStartEnd;
+ attribute::SdrShadowAttribute aShadow;
+ attribute::FillGradientAttribute aFillFloatTransGradient;
+
+ // try line style
+ const attribute::SdrLineAttribute aLine(createNewSdrLineAttribute(rSet));
+
+ if(!aLine.isDefault())
+ {
+ // try LineStartEnd
+ aLineStartEnd = createNewSdrLineStartEndAttribute(rSet, aLine.getWidth());
+ }
+
+ // try fill style
+ if(!bSuppressFill)
+ {
+ aFill = createNewSdrFillAttribute(rSet);
+
+ if(!aFill.isDefault())
+ {
+ // try fillfloattransparence
+ aFillFloatTransGradient = createNewTransparenceGradientAttribute(rSet);
+ }
+ }
+
+ if(!aLine.isDefault() || !aFill.isDefault())
+ {
+ // try shadow
+ aShadow = createNewSdrShadowAttribute(rSet);
+
+ return attribute::SdrLineFillShadowAttribute3D(
+ aLine, aFill, aLineStartEnd, aShadow, aFillFloatTransGradient);
+ }
+
+ return attribute::SdrLineFillShadowAttribute3D();
+ }
+
+ attribute::SdrSceneAttribute createNewSdrSceneAttribute(const SfxItemSet& rSet)
+ {
+ // get perspective
+ css::drawing::ProjectionMode aProjectionMode(css::drawing::ProjectionMode_PARALLEL);
+ const sal_uInt16 nProjectionValue(rSet.Get(SDRATTR_3DSCENE_PERSPECTIVE).GetValue());
+
+ if(1 == nProjectionValue)
+ {
+ aProjectionMode = css::drawing::ProjectionMode_PERSPECTIVE;
+ }
+
+ // get distance
+ const double fDistance(rSet.Get(SDRATTR_3DSCENE_DISTANCE).GetValue());
+
+ // get shadow slant
+ const double fShadowSlant(
+ basegfx::deg2rad(rSet.Get(SDRATTR_3DSCENE_SHADOW_SLANT).GetValue()));
+
+ // get shade mode
+ css::drawing::ShadeMode aShadeMode(css::drawing::ShadeMode_FLAT);
+ const sal_uInt16 nShadeValue(rSet.Get(SDRATTR_3DSCENE_SHADE_MODE).GetValue());
+
+ if(1 == nShadeValue)
+ {
+ aShadeMode = css::drawing::ShadeMode_PHONG;
+ }
+ else if(2 == nShadeValue)
+ {
+ aShadeMode = css::drawing::ShadeMode_SMOOTH;
+ }
+ else if(3 == nShadeValue)
+ {
+ aShadeMode = css::drawing::ShadeMode_DRAFT;
+ }
+
+ // get two sided lighting
+ const bool bTwoSidedLighting(rSet.Get(SDRATTR_3DSCENE_TWO_SIDED_LIGHTING).GetValue());
+
+ return attribute::SdrSceneAttribute(fDistance, fShadowSlant, aProjectionMode, aShadeMode, bTwoSidedLighting);
+ }
+
+ attribute::SdrLightingAttribute createNewSdrLightingAttribute(const SfxItemSet& rSet)
+ {
+ // extract lights from given SfxItemSet (from scene)
+ ::std::vector< attribute::Sdr3DLightAttribute > aLightVector;
+
+ if(rSet.Get(SDRATTR_3DSCENE_LIGHTON_1).GetValue())
+ {
+ const basegfx::BColor aColor(rSet.Get(SDRATTR_3DSCENE_LIGHTCOLOR_1).GetValue().getBColor());
+ const basegfx::B3DVector aDirection(rSet.Get(SDRATTR_3DSCENE_LIGHTDIRECTION_1).GetValue());
+ aLightVector.emplace_back(aColor, aDirection, true);
+ }
+
+ if(rSet.Get(SDRATTR_3DSCENE_LIGHTON_2).GetValue())
+ {
+ const basegfx::BColor aColor(rSet.Get(SDRATTR_3DSCENE_LIGHTCOLOR_2).GetValue().getBColor());
+ const basegfx::B3DVector aDirection(rSet.Get(SDRATTR_3DSCENE_LIGHTDIRECTION_2).GetValue());
+ aLightVector.emplace_back(aColor, aDirection, false);
+ }
+
+ if(rSet.Get(SDRATTR_3DSCENE_LIGHTON_3).GetValue())
+ {
+ const basegfx::BColor aColor(rSet.Get(SDRATTR_3DSCENE_LIGHTCOLOR_3).GetValue().getBColor());
+ const basegfx::B3DVector aDirection(rSet.Get(SDRATTR_3DSCENE_LIGHTDIRECTION_3).GetValue());
+ aLightVector.emplace_back(aColor, aDirection, false);
+ }
+
+ if(rSet.Get(SDRATTR_3DSCENE_LIGHTON_4).GetValue())
+ {
+ const basegfx::BColor aColor(rSet.Get(SDRATTR_3DSCENE_LIGHTCOLOR_4).GetValue().getBColor());
+ const basegfx::B3DVector aDirection(rSet.Get(SDRATTR_3DSCENE_LIGHTDIRECTION_4).GetValue());
+ aLightVector.emplace_back(aColor, aDirection, false);
+ }
+
+ if(rSet.Get(SDRATTR_3DSCENE_LIGHTON_5).GetValue())
+ {
+ const basegfx::BColor aColor(rSet.Get(SDRATTR_3DSCENE_LIGHTCOLOR_5).GetValue().getBColor());
+ const basegfx::B3DVector aDirection(rSet.Get(SDRATTR_3DSCENE_LIGHTDIRECTION_5).GetValue());
+ aLightVector.emplace_back(aColor, aDirection, false);
+ }
+
+ if(rSet.Get(SDRATTR_3DSCENE_LIGHTON_6).GetValue())
+ {
+ const basegfx::BColor aColor(rSet.Get(SDRATTR_3DSCENE_LIGHTCOLOR_6).GetValue().getBColor());
+ const basegfx::B3DVector aDirection(rSet.Get(SDRATTR_3DSCENE_LIGHTDIRECTION_6).GetValue());
+ aLightVector.emplace_back(aColor, aDirection, false);
+ }
+
+ if(rSet.Get(SDRATTR_3DSCENE_LIGHTON_7).GetValue())
+ {
+ const basegfx::BColor aColor(rSet.Get(SDRATTR_3DSCENE_LIGHTCOLOR_7).GetValue().getBColor());
+ const basegfx::B3DVector aDirection(rSet.Get(SDRATTR_3DSCENE_LIGHTDIRECTION_7).GetValue());
+ aLightVector.emplace_back(aColor, aDirection, false);
+ }
+
+ if(rSet.Get(SDRATTR_3DSCENE_LIGHTON_8).GetValue())
+ {
+ const basegfx::BColor aColor(rSet.Get(SDRATTR_3DSCENE_LIGHTCOLOR_8).GetValue().getBColor());
+ const basegfx::B3DVector aDirection(rSet.Get(SDRATTR_3DSCENE_LIGHTDIRECTION_8).GetValue());
+ aLightVector.emplace_back(aColor, aDirection, false);
+ }
+
+ // get ambient color
+ const Color aAmbientValue(rSet.Get(SDRATTR_3DSCENE_AMBIENTCOLOR).GetValue());
+ const basegfx::BColor aAmbientLight(aAmbientValue.getBColor());
+
+ return attribute::SdrLightingAttribute(aAmbientLight, std::move(aLightVector));
+ }
+
+ void calculateRelativeCornerRadius(sal_Int32 nRadius, const basegfx::B2DRange& rObjectRange, double& rfCornerRadiusX, double& rfCornerRadiusY)
+ {
+ rfCornerRadiusX = rfCornerRadiusY = static_cast<double>(nRadius);
+
+ if(0.0 != rfCornerRadiusX)
+ {
+ const double fHalfObjectWidth(rObjectRange.getWidth() * 0.5);
+
+ if(0.0 != fHalfObjectWidth)
+ {
+ if(rfCornerRadiusX < 0.0)
+ {
+ rfCornerRadiusX = 0.0;
+ }
+
+ if(rfCornerRadiusX > fHalfObjectWidth)
+ {
+ rfCornerRadiusX = fHalfObjectWidth;
+ }
+
+ rfCornerRadiusX /= fHalfObjectWidth;
+ }
+ else
+ {
+ rfCornerRadiusX = 0.0;
+ }
+ }
+
+ if(0.0 == rfCornerRadiusY)
+ return;
+
+ const double fHalfObjectHeight(rObjectRange.getHeight() * 0.5);
+
+ if(0.0 != fHalfObjectHeight)
+ {
+ if(rfCornerRadiusY < 0.0)
+ {
+ rfCornerRadiusY = 0.0;
+ }
+
+ if(rfCornerRadiusY > fHalfObjectHeight)
+ {
+ rfCornerRadiusY = fHalfObjectHeight;
+ }
+
+ rfCornerRadiusY /= fHalfObjectHeight;
+ }
+ else
+ {
+ rfCornerRadiusY = 0.0;
+ }
+ }
+
+ // #i101508# Support handing over given text-to-border distances
+ attribute::SdrFillTextAttribute createNewSdrFillTextAttribute(
+ const SfxItemSet& rSet,
+ const SdrText* pText,
+ const sal_Int32* pLeft,
+ const sal_Int32* pUpper,
+ const sal_Int32* pRight,
+ const sal_Int32* pLower)
+ {
+ attribute::SdrFillAttribute aFill;
+ attribute::FillGradientAttribute aFillFloatTransGradient;
+ attribute::SdrTextAttribute aText;
+ bool bFontworkHideContour(false);
+
+ // look for text first
+ if(pText)
+ {
+ aText = createNewSdrTextAttribute(rSet, *pText, pLeft, pUpper, pRight, pLower);
+
+ // when object has text and text is fontwork and hide contour is set for fontwork, force
+ // fill style to empty
+ if(!aText.getSdrFormTextAttribute().isDefault() && aText.isHideContour())
+ {
+ bFontworkHideContour = true;
+ }
+ }
+
+ if(!bFontworkHideContour)
+ {
+ // try fill style
+ aFill = createNewSdrFillAttribute(rSet);
+
+ if(!aFill.isDefault())
+ {
+ // try fillfloattransparence
+ aFillFloatTransGradient = createNewTransparenceGradientAttribute(rSet);
+ }
+ }
+
+ if(!aFill.isDefault() || !aText.isDefault())
+ {
+ return attribute::SdrFillTextAttribute(aFill, aFillFloatTransGradient, aText);
+ }
+
+ return attribute::SdrFillTextAttribute();
+ }
+
+} // end of namespace
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/sdr/primitive2d/sdrcaptionprimitive2d.cxx b/svx/source/sdr/primitive2d/sdrcaptionprimitive2d.cxx
new file mode 100644
index 000000000..50787ed39
--- /dev/null
+++ b/svx/source/sdr/primitive2d/sdrcaptionprimitive2d.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 <sdr/primitive2d/sdrcaptionprimitive2d.hxx>
+#include <basegfx/polygon/b2dpolypolygon.hxx>
+#include <basegfx/polygon/b2dpolygontools.hxx>
+#include <sdr/primitive2d/sdrdecompositiontools.hxx>
+#include <svx/sdr/primitive2d/svx_primitivetypes2d.hxx>
+#include <drawinglayer/primitive2d/sdrdecompositiontools2d.hxx>
+
+
+using namespace com::sun::star;
+
+
+namespace drawinglayer::primitive2d
+{
+ void SdrCaptionPrimitive2D::create2DDecomposition(Primitive2DContainer& rContainer, const geometry::ViewInformation2D& /*aViewInformation*/) const
+ {
+ Primitive2DContainer aRetval;
+
+ // create unit outline polygon
+ const basegfx::B2DPolygon aUnitOutline(basegfx::utils::createPolygonFromRect(
+ basegfx::B2DRange(0.0, 0.0, 1.0, 1.0),
+ getCornerRadiusX(),
+ getCornerRadiusY()));
+
+ // add fill
+ if(getSdrLFSTAttribute().getFill().isDefault())
+ {
+ // create invisible fill for HitTest
+ aRetval.push_back(
+ createHiddenGeometryPrimitives2D(
+ true,
+ basegfx::B2DPolyPolygon(aUnitOutline),
+ getTransform()));
+ }
+ else
+ {
+ basegfx::B2DPolyPolygon aTransformed(aUnitOutline);
+
+ aTransformed.transform(getTransform());
+ aRetval.push_back(
+ createPolyPolygonFillPrimitive(
+ aTransformed,
+ getSdrLFSTAttribute().getFill(),
+ getSdrLFSTAttribute().getFillFloatTransGradient()));
+ }
+
+ // add line
+ if(getSdrLFSTAttribute().getLine().isDefault())
+ {
+ // create invisible line for HitTest/BoundRect
+ aRetval.push_back(
+ createHiddenGeometryPrimitives2D(
+ false,
+ basegfx::B2DPolyPolygon(aUnitOutline),
+ getTransform()));
+
+ aRetval.push_back(
+ createHiddenGeometryPrimitives2D(
+ false,
+ basegfx::B2DPolyPolygon(getTail()),
+ {}));
+ }
+ else
+ {
+ basegfx::B2DPolygon aTransformed(aUnitOutline);
+
+ aTransformed.transform(getTransform());
+ aRetval.push_back(
+ createPolygonLinePrimitive(
+ aTransformed,
+ getSdrLFSTAttribute().getLine(),
+ attribute::SdrLineStartEndAttribute()));
+
+ aRetval.push_back(
+ createPolygonLinePrimitive(
+ getTail(),
+ getSdrLFSTAttribute().getLine(),
+ getSdrLFSTAttribute().getLineStartEnd()));
+ }
+
+ // add text
+ if(!getSdrLFSTAttribute().getText().isDefault())
+ {
+ aRetval.push_back(
+ createTextPrimitive(
+ basegfx::B2DPolyPolygon(aUnitOutline),
+ getTransform(),
+ getSdrLFSTAttribute().getText(),
+ getSdrLFSTAttribute().getLine(),
+ false,
+ false));
+ }
+
+ // add shadow
+ if(!getSdrLFSTAttribute().getShadow().isDefault())
+ {
+ aRetval = createEmbeddedShadowPrimitive(std::move(aRetval), getSdrLFSTAttribute().getShadow());
+ }
+
+ rContainer.append(std::move(aRetval));
+ }
+
+ SdrCaptionPrimitive2D::SdrCaptionPrimitive2D(
+ const basegfx::B2DHomMatrix& rTransform,
+ const attribute::SdrLineFillEffectsTextAttribute& rSdrLFSTAttribute,
+ const basegfx::B2DPolygon& rTail,
+ double fCornerRadiusX,
+ double fCornerRadiusY)
+ : maTransform(rTransform),
+ maSdrLFSTAttribute(rSdrLFSTAttribute),
+ maTail(rTail),
+ mfCornerRadiusX(fCornerRadiusX),
+ mfCornerRadiusY(fCornerRadiusY)
+ {
+ }
+
+ bool SdrCaptionPrimitive2D::operator==(const BasePrimitive2D& rPrimitive) const
+ {
+ if(BufferedDecompositionPrimitive2D::operator==(rPrimitive))
+ {
+ const SdrCaptionPrimitive2D& rCompare = static_cast<const SdrCaptionPrimitive2D&>(rPrimitive);
+
+ return (getCornerRadiusX() == rCompare.getCornerRadiusX()
+ && getCornerRadiusY() == rCompare.getCornerRadiusY()
+ && getTail() == rCompare.getTail()
+ && getTransform() == rCompare.getTransform()
+ && getSdrLFSTAttribute() == rCompare.getSdrLFSTAttribute());
+ }
+
+ return false;
+ }
+
+ // provide unique ID
+ sal_uInt32 SdrCaptionPrimitive2D::getPrimitive2DID() const
+ {
+ return PRIMITIVE2D_ID_SDRCAPTIONPRIMITIVE2D;
+ }
+
+} // end of namespace
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/sdr/primitive2d/sdrconnectorprimitive2d.cxx b/svx/source/sdr/primitive2d/sdrconnectorprimitive2d.cxx
new file mode 100644
index 000000000..aa663fb7a
--- /dev/null
+++ b/svx/source/sdr/primitive2d/sdrconnectorprimitive2d.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 <sdr/primitive2d/sdrconnectorprimitive2d.hxx>
+#include <sdr/primitive2d/sdrdecompositiontools.hxx>
+#include <basegfx/matrix/b2dhommatrix.hxx>
+#include <svx/sdr/primitive2d/svx_primitivetypes2d.hxx>
+#include <drawinglayer/primitive2d/sdrdecompositiontools2d.hxx>
+#include <basegfx/polygon/b2dpolypolygon.hxx>
+
+
+using namespace com::sun::star;
+
+
+namespace drawinglayer::primitive2d
+{
+ void SdrConnectorPrimitive2D::create2DDecomposition(Primitive2DContainer& rContainer, const geometry::ViewInformation2D& /*aViewInformation*/) const
+ {
+ Primitive2DContainer aRetval;
+
+ // add line
+ if(getSdrLSTAttribute().getLine().isDefault())
+ {
+ // create invisible line for HitTest/BoundRect
+ aRetval.push_back(
+ createHiddenGeometryPrimitives2D(
+ basegfx::B2DPolyPolygon(getUnitPolygon())));
+ }
+ else
+ {
+ aRetval.push_back(
+ createPolygonLinePrimitive(
+ getUnitPolygon(),
+ getSdrLSTAttribute().getLine(),
+ getSdrLSTAttribute().getLineStartEnd()));
+ }
+
+ // add text
+ if(!getSdrLSTAttribute().getText().isDefault())
+ {
+ aRetval.push_back(
+ createTextPrimitive(
+ basegfx::B2DPolyPolygon(getUnitPolygon()),
+ basegfx::B2DHomMatrix(),
+ getSdrLSTAttribute().getText(),
+ getSdrLSTAttribute().getLine(),
+ false,
+ false));
+ }
+
+ // add shadow
+ if(!getSdrLSTAttribute().getShadow().isDefault())
+ {
+ aRetval = createEmbeddedShadowPrimitive(
+ std::move(aRetval),
+ getSdrLSTAttribute().getShadow());
+ }
+
+ rContainer.append(std::move(aRetval));
+ }
+
+ SdrConnectorPrimitive2D::SdrConnectorPrimitive2D(
+ const attribute::SdrLineEffectsTextAttribute& rSdrLSTAttribute,
+ const ::basegfx::B2DPolygon& rUnitPolygon)
+ : maSdrLSTAttribute(rSdrLSTAttribute),
+ maUnitPolygon(rUnitPolygon)
+ {
+ }
+
+ bool SdrConnectorPrimitive2D::operator==(const BasePrimitive2D& rPrimitive) const
+ {
+ if(BufferedDecompositionPrimitive2D::operator==(rPrimitive))
+ {
+ const SdrConnectorPrimitive2D& rCompare = static_cast<const SdrConnectorPrimitive2D&>(rPrimitive);
+
+ return (getUnitPolygon() == rCompare.getUnitPolygon()
+ && getSdrLSTAttribute() == rCompare.getSdrLSTAttribute());
+ }
+
+ return false;
+ }
+
+ // provide unique ID
+ sal_uInt32 SdrConnectorPrimitive2D::getPrimitive2DID() const
+ {
+ return PRIMITIVE2D_ID_SDRCONNECTORPRIMITIVE2D;
+ }
+
+} // end of namespace
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/sdr/primitive2d/sdrcustomshapeprimitive2d.cxx b/svx/source/sdr/primitive2d/sdrcustomshapeprimitive2d.cxx
new file mode 100644
index 000000000..dddb8aa30
--- /dev/null
+++ b/svx/source/sdr/primitive2d/sdrcustomshapeprimitive2d.cxx
@@ -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 .
+ */
+
+#include <sdr/primitive2d/sdrcustomshapeprimitive2d.hxx>
+#include <basegfx/polygon/b2dpolygon.hxx>
+#include <basegfx/polygon/b2dpolygontools.hxx>
+#include <basegfx/polygon/b2dpolypolygon.hxx>
+#include <sdr/primitive2d/sdrdecompositiontools.hxx>
+#include <svx/sdr/primitive2d/svx_primitivetypes2d.hxx>
+#include <drawinglayer/attribute/sdrlineattribute.hxx>
+
+
+using namespace com::sun::star;
+
+
+namespace drawinglayer::primitive2d
+{
+ void SdrCustomShapePrimitive2D::create2DDecomposition(Primitive2DContainer& rContainer, const geometry::ViewInformation2D& /*aViewInformation*/) const
+ {
+ Primitive2DContainer aRetval(getSubPrimitives());
+
+ // Soft edges should be before text, since text is not affected by soft edges
+ if (!aRetval.empty() && getSdrSTAttribute().getSoftEdgeRadius())
+ {
+ aRetval = createEmbeddedSoftEdgePrimitive(std::move(aRetval),
+ getSdrSTAttribute().getSoftEdgeRadius());
+ }
+
+ // add text
+ if(!getSdrSTAttribute().getText().isDefault())
+ {
+ const basegfx::B2DPolygon& aUnitOutline(basegfx::utils::createUnitPolygon());
+
+ aRetval.push_back(
+ createTextPrimitive(
+ basegfx::B2DPolyPolygon(aUnitOutline),
+ getTextBox(),
+ getSdrSTAttribute().getText(),
+ attribute::SdrLineAttribute(),
+ false,
+ getWordWrap()));
+ }
+
+ // tdf#132199: put glow before shadow, to have shadow of the glow, not the opposite
+ if (!aRetval.empty() && !getSdrSTAttribute().getGlow().isDefault())
+ {
+ // glow
+ aRetval = createEmbeddedGlowPrimitive(std::move(aRetval), getSdrSTAttribute().getGlow());
+ }
+
+ // add shadow
+ if(!aRetval.empty() && !getSdrSTAttribute().getShadow().isDefault())
+ {
+ // #i105323# add generic shadow only for 2D shapes. For
+ // 3D shapes shadow will be set at the individual created
+ // visualisation objects and be visualized by the 3d renderer
+ // as a single shadow.
+
+ // The shadow for AutoShapes could be handled uniformly by not setting any
+ // shadow items at the helper model objects and only adding shadow here for
+ // 2D and 3D (and it works, too), but this would lead to two 3D scenes for
+ // the 3D object; one for the shadow and one for the content. The one for the
+ // shadow will be correct (using ColorModifierStack), but expensive.
+ if(!get3DShape())
+ {
+ aRetval = createEmbeddedShadowPrimitive(std::move(aRetval), getSdrSTAttribute().getShadow(),
+ maTransform);
+ }
+ }
+
+ rContainer.append(std::move(aRetval));
+ }
+
+ SdrCustomShapePrimitive2D::SdrCustomShapePrimitive2D(
+ const attribute::SdrEffectsTextAttribute& rSdrSTAttribute,
+ Primitive2DContainer&& rSubPrimitives,
+ const basegfx::B2DHomMatrix& rTextBox,
+ bool bWordWrap,
+ bool b3DShape,
+ const basegfx::B2DHomMatrix& rTransform)
+ : maSdrSTAttribute(rSdrSTAttribute),
+ maSubPrimitives(std::move(rSubPrimitives)),
+ maTextBox(rTextBox),
+ mbWordWrap(bWordWrap),
+ mb3DShape(b3DShape),
+ maTransform(rTransform)
+ {
+ }
+
+ bool SdrCustomShapePrimitive2D::operator==(const BasePrimitive2D& rPrimitive) const
+ {
+ if(BufferedDecompositionPrimitive2D::operator==(rPrimitive))
+ {
+ const SdrCustomShapePrimitive2D& rCompare = static_cast<const SdrCustomShapePrimitive2D&>(rPrimitive);
+
+ return (getSdrSTAttribute() == rCompare.getSdrSTAttribute()
+ && getSubPrimitives() == rCompare.getSubPrimitives()
+ && getTextBox() == rCompare.getTextBox()
+ && getWordWrap() == rCompare.getWordWrap()
+ && get3DShape() == rCompare.get3DShape());
+ }
+
+ return false;
+ }
+
+ // provide unique ID
+ sal_uInt32 SdrCustomShapePrimitive2D::getPrimitive2DID() const
+ {
+ return PRIMITIVE2D_ID_SDRCUSTOMSHAPEPRIMITIVE2D;
+ }
+
+} // end of namespace
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/sdr/primitive2d/sdrdecompositiontools.cxx b/svx/source/sdr/primitive2d/sdrdecompositiontools.cxx
new file mode 100644
index 000000000..a1838b03f
--- /dev/null
+++ b/svx/source/sdr/primitive2d/sdrdecompositiontools.cxx
@@ -0,0 +1,884 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <sdr/primitive2d/sdrdecompositiontools.hxx>
+#include <drawinglayer/primitive2d/baseprimitive2d.hxx>
+#include <drawinglayer/primitive2d/PolygonStrokeArrowPrimitive2D.hxx>
+#include <drawinglayer/primitive2d/PolyPolygonGradientPrimitive2D.hxx>
+#include <drawinglayer/primitive2d/PolyPolygonHatchPrimitive2D.hxx>
+#include <drawinglayer/primitive2d/PolyPolygonGraphicPrimitive2D.hxx>
+#include <drawinglayer/primitive2d/PolyPolygonColorPrimitive2D.hxx>
+#include <drawinglayer/primitive2d/softedgeprimitive2d.hxx>
+#include <drawinglayer/primitive2d/unifiedtransparenceprimitive2d.hxx>
+#include <drawinglayer/primitive2d/transparenceprimitive2d.hxx>
+#include <basegfx/polygon/b2dpolypolygontools.hxx>
+#include <drawinglayer/primitive2d/fillgradientprimitive2d.hxx>
+#include <drawinglayer/attribute/strokeattribute.hxx>
+#include <drawinglayer/attribute/linestartendattribute.hxx>
+#include <drawinglayer/attribute/sdrfillgraphicattribute.hxx>
+#include <basegfx/matrix/b2dhommatrix.hxx>
+#include <drawinglayer/primitive2d/shadowprimitive2d.hxx>
+#include <sdr/attribute/sdrtextattribute.hxx>
+#include <drawinglayer/primitive2d/glowprimitive2d.hxx>
+#include <sdr/primitive2d/sdrtextprimitive2d.hxx>
+#include <svx/svdotext.hxx>
+#include <basegfx/polygon/b2dpolygontools.hxx>
+#include <drawinglayer/primitive2d/animatedprimitive2d.hxx>
+#include <drawinglayer/animation/animationtiming.hxx>
+#include <drawinglayer/primitive2d/maskprimitive2d.hxx>
+#include <drawinglayer/geometry/viewinformation2d.hxx>
+#include <drawinglayer/primitive2d/texthierarchyprimitive2d.hxx>
+#include <drawinglayer/attribute/sdrfillattribute.hxx>
+#include <drawinglayer/attribute/sdrlineattribute.hxx>
+#include <drawinglayer/attribute/sdrlinestartendattribute.hxx>
+#include <drawinglayer/attribute/sdrshadowattribute.hxx>
+#include <drawinglayer/attribute/sdrglowattribute.hxx>
+#include <osl/diagnose.h>
+
+// for SlideBackgroundFillPrimitive2D
+#include <svx/sdr/primitive2d/svx_primitivetypes2d.hxx>
+#include <svx/unoapi.hxx>
+#include <svx/svdpage.hxx>
+#include <sdr/primitive2d/sdrattributecreator.hxx>
+#include <sdr/contact/viewcontactofmasterpagedescriptor.hxx>
+
+using namespace com::sun::star;
+
+
+namespace drawinglayer::primitive2d
+{
+namespace
+{
+// See also: SdrTextObj::AdjustRectToTextDistance
+basegfx::B2DRange getTextAnchorRange(const attribute::SdrTextAttribute& rText,
+ const basegfx::B2DRange& rSnapRange)
+{
+ // Take vertical text orientation into account when deciding
+ // which dimension is its width, and which is its height
+ const OutlinerParaObject& rOutlinerParaObj = rText.getOutlinerParaObject();
+ const bool bVerticalWriting(rOutlinerParaObj.IsEffectivelyVertical());
+ const double fWidthForText = bVerticalWriting ? rSnapRange.getHeight() : rSnapRange.getWidth();
+ // create a range describing the wanted text position and size (aTextAnchorRange). This
+ // means to use the text distance values here
+ // If the margin is larger than the entire width of the text area, then limit the
+ // margin.
+ const double fTextLeftDistance
+ = std::min(static_cast<double>(rText.getTextLeftDistance()), fWidthForText);
+ const double nTextRightDistance
+ = std::min(static_cast<double>(rText.getTextRightDistance()), fWidthForText);
+ double fDistanceForTextL, fDistanceForTextT, fDistanceForTextR, fDistanceForTextB;
+ if (!bVerticalWriting)
+ {
+ fDistanceForTextL = fTextLeftDistance;
+ fDistanceForTextT = rText.getTextUpperDistance();
+ fDistanceForTextR = nTextRightDistance;
+ fDistanceForTextB = rText.getTextLowerDistance();
+ }
+ else if (rOutlinerParaObj.IsTopToBottom())
+ {
+ fDistanceForTextL = rText.getTextLowerDistance();
+ fDistanceForTextT = fTextLeftDistance;
+ fDistanceForTextR = rText.getTextUpperDistance();
+ fDistanceForTextB = nTextRightDistance;
+ }
+ else
+ {
+ fDistanceForTextL = rText.getTextUpperDistance();
+ fDistanceForTextT = nTextRightDistance;
+ fDistanceForTextR = rText.getTextLowerDistance();
+ fDistanceForTextB = fTextLeftDistance;
+ }
+ const basegfx::B2DPoint aTopLeft(rSnapRange.getMinX() + fDistanceForTextL,
+ rSnapRange.getMinY() + fDistanceForTextT);
+ const basegfx::B2DPoint aBottomRight(rSnapRange.getMaxX() - fDistanceForTextR,
+ rSnapRange.getMaxY() - fDistanceForTextB);
+ basegfx::B2DRange aAnchorRange;
+ aAnchorRange.expand(aTopLeft);
+ aAnchorRange.expand(aBottomRight);
+
+ // If the shape has no width, then don't attempt to break the text into multiple
+ // lines, not a single character would satisfy a zero width requirement.
+ // SdrTextObj::impDecomposeBlockTextPrimitive() uses the same constant to
+ // effectively set no limits.
+ if (!bVerticalWriting && aAnchorRange.getWidth() == 0)
+ {
+ aAnchorRange.expand(basegfx::B2DPoint(aTopLeft.getX() - 1000000, aTopLeft.getY()));
+ aAnchorRange.expand(basegfx::B2DPoint(aBottomRight.getX() + 1000000, aBottomRight.getY()));
+ }
+ else if (bVerticalWriting && aAnchorRange.getHeight() == 0)
+ {
+ aAnchorRange.expand(basegfx::B2DPoint(aTopLeft.getX(), aTopLeft.getY() - 1000000));
+ aAnchorRange.expand(basegfx::B2DPoint(aBottomRight.getX(), aBottomRight.getY() + 1000000));
+ }
+ return aAnchorRange;
+}
+
+drawinglayer::attribute::SdrFillAttribute getMasterPageFillAttribute(
+ const geometry::ViewInformation2D& rViewInformation,
+ basegfx::B2DVector& rPageSize)
+{
+ drawinglayer::attribute::SdrFillAttribute aRetval;
+
+ // get SdrPage
+ const SdrPage* pVisualizedPage(GetSdrPageFromXDrawPage(rViewInformation.getVisualizedPage()));
+
+ if(nullptr != pVisualizedPage)
+ {
+ // copy needed values for further processing
+ rPageSize.setX(pVisualizedPage->GetWidth());
+ rPageSize.setY(pVisualizedPage->GetHeight());
+
+ if(pVisualizedPage->IsMasterPage())
+ {
+ // the page is a MasterPage, so we are in MasterPage view
+ // still need #i110846#, see ViewContactOfMasterPage
+ if(pVisualizedPage->getSdrPageProperties().GetStyleSheet())
+ {
+ // create page fill attributes with correct properties
+ aRetval = drawinglayer::primitive2d::createNewSdrFillAttribute(
+ pVisualizedPage->getSdrPageProperties().GetItemSet());
+ }
+ }
+ else
+ {
+ // the page is *no* MasterPage, we are in normal Page view, get the MasterPage
+ if(pVisualizedPage->TRG_HasMasterPage())
+ {
+ sdr::contact::ViewContact& rVC(pVisualizedPage->TRG_GetMasterPageDescriptorViewContact());
+ sdr::contact::ViewContactOfMasterPageDescriptor* pVCOMPD(
+ dynamic_cast<sdr::contact::ViewContactOfMasterPageDescriptor*>(&rVC));
+
+ if(nullptr != pVCOMPD)
+ {
+ // in this case the still needed #i110846# is part of
+ // getCorrectSdrPageProperties, that's the main reason to re-use
+ // that call/functionality here
+ const SdrPageProperties* pCorrectProperties(
+ pVCOMPD->GetMasterPageDescriptor().getCorrectSdrPageProperties());
+
+ if(pCorrectProperties)
+ {
+ // create page fill attributes when correct properties were identified
+ aRetval = drawinglayer::primitive2d::createNewSdrFillAttribute(
+ pCorrectProperties->GetItemSet());
+ }
+ }
+ }
+ }
+ }
+
+ return aRetval;
+}
+
+// provide a Primitive2D for the SlideBackgroundFill-mode. It is capable
+// of expressing that state of fill and it's decomposition implements all
+// needed preparation of the geometry in an isolated and controllable
+// space and way.
+// It is currently simple buffered (due to being derived from
+// BufferedDecompositionPrimitive2D) and detects if FillStyle changes
+class SlideBackgroundFillPrimitive2D final : public BufferedDecompositionPrimitive2D
+{
+private:
+ /// the basegfx::B2DPolyPolygon geometry
+ basegfx::B2DPolyPolygon maPolyPolygon;
+
+ /// the last SdrFillAttribute the geometry was created for
+ drawinglayer::attribute::SdrFillAttribute maLastFill;
+
+protected:
+ // create decomposition data
+ virtual void create2DDecomposition(
+ Primitive2DContainer& rContainer,
+ const geometry::ViewInformation2D& rViewInformation) const override;
+
+public:
+ /// constructor
+ SlideBackgroundFillPrimitive2D(
+ const basegfx::B2DPolyPolygon& rPolyPolygon);
+
+ /// check existing decomposition data, call parent
+ virtual void get2DDecomposition(
+ Primitive2DDecompositionVisitor& rVisitor,
+ const geometry::ViewInformation2D& rViewInformation) const override;
+
+ /// data read access
+ const basegfx::B2DPolyPolygon& getB2DPolyPolygon() const { return maPolyPolygon; }
+
+ /// 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;
+};
+
+SlideBackgroundFillPrimitive2D::SlideBackgroundFillPrimitive2D(
+ const basegfx::B2DPolyPolygon& rPolyPolygon)
+: BufferedDecompositionPrimitive2D()
+, maPolyPolygon(rPolyPolygon)
+, maLastFill()
+{
+}
+
+void SlideBackgroundFillPrimitive2D::create2DDecomposition(
+ Primitive2DContainer& rContainer,
+ const geometry::ViewInformation2D& rViewInformation) const
+{
+ basegfx::B2DVector aPageSize;
+
+ // get fill from target Page, this will check for all needed things
+ // like MasterPage/relationships, etc. (see getMasterPageFillAttribute impl above)
+ drawinglayer::attribute::SdrFillAttribute aFill(
+ getMasterPageFillAttribute(rViewInformation, aPageSize));
+
+ // if fill is on default (empty), nothing will be shown, we are done
+ if(aFill.isDefault())
+ return;
+
+ // Get PolygonRange of own local geometry
+ const basegfx::B2DRange aPolygonRange(getB2DPolyPolygon().getB2DRange());
+
+ // if local geometry is empty, nothing will be shown, we are done
+ if(aPolygonRange.isEmpty())
+ return;
+
+ // Get PageRange
+ const basegfx::B2DRange aPageRange(0.0, 0.0, aPageSize.getX(), aPageSize.getY());
+
+ // if local geometry does not overlap with PageRange, nothing will be shown, we are done
+ if(!aPageRange.overlaps(aPolygonRange))
+ return;
+
+ // create FillPrimitive2D with the geometry (the PolyPolygon) and
+ // the page's definitonRange to:
+ // - on one hand limit to geometry
+ // - on the other hand allow continutation of fill outside of
+ // MasterPage's range
+ const attribute::FillGradientAttribute aEmptyFillTransparenceGradient;
+ const Primitive2DReference aCreatedFill(
+ createPolyPolygonFillPrimitive(
+ getB2DPolyPolygon(), // geometry
+ aPageRange, // definition range
+ aFill,
+ aEmptyFillTransparenceGradient));
+
+ rContainer = Primitive2DContainer { aCreatedFill };
+}
+
+void SlideBackgroundFillPrimitive2D::get2DDecomposition(
+ Primitive2DDecompositionVisitor& rVisitor,
+ const geometry::ViewInformation2D& rViewInformation) const
+{
+ basegfx::B2DVector aPageSize;
+ drawinglayer::attribute::SdrFillAttribute aFill;
+
+ if(!getBuffered2DDecomposition().empty())
+ {
+ aFill = getMasterPageFillAttribute(rViewInformation, aPageSize);
+
+ if(!(aFill == maLastFill))
+ {
+ // conditions of last local decomposition have changed, delete
+ const_cast< SlideBackgroundFillPrimitive2D* >(this)->setBuffered2DDecomposition(Primitive2DContainer());
+ }
+ }
+
+ if(getBuffered2DDecomposition().empty())
+ {
+ // remember last Fill
+ const_cast< SlideBackgroundFillPrimitive2D* >(this)->maLastFill = aFill;
+ }
+
+ // use parent implementation
+ BufferedDecompositionPrimitive2D::get2DDecomposition(rVisitor, rViewInformation);
+}
+
+bool SlideBackgroundFillPrimitive2D::operator==(const BasePrimitive2D& rPrimitive) const
+{
+ if (BufferedDecompositionPrimitive2D::operator==(rPrimitive))
+ {
+ const SlideBackgroundFillPrimitive2D& rCompare
+ = static_cast<const SlideBackgroundFillPrimitive2D&>(rPrimitive);
+
+ return getB2DPolyPolygon() == rCompare.getB2DPolyPolygon();
+ }
+
+ return false;
+}
+
+basegfx::B2DRange SlideBackgroundFillPrimitive2D::getB2DRange(
+ const geometry::ViewInformation2D& /*rViewInformation*/) const
+{
+ // return range
+ return basegfx::utils::getRange(getB2DPolyPolygon());
+}
+
+// provide unique ID
+sal_uInt32 SlideBackgroundFillPrimitive2D::getPrimitive2DID() const
+{
+ return PRIMITIVE2D_ID_SLIDEBACKGROUNDFILLPRIMITIVE2D;
+}
+
+}; // end of anonymous namespace
+
+ class TransparencePrimitive2D;
+
+ Primitive2DReference createPolyPolygonFillPrimitive(
+ const basegfx::B2DPolyPolygon& rPolyPolygon,
+ const attribute::SdrFillAttribute& rFill,
+ const attribute::FillGradientAttribute& rFillGradient)
+ {
+ // when we have no given definition range, use the range of the given geometry
+ // also for definition (simplest case)
+ const basegfx::B2DRange aRange(basegfx::utils::getRange(rPolyPolygon));
+
+ return createPolyPolygonFillPrimitive(
+ rPolyPolygon,
+ aRange,
+ rFill,
+ rFillGradient);
+ }
+
+ Primitive2DReference createPolyPolygonFillPrimitive(
+ const basegfx::B2DPolyPolygon& rPolyPolygon,
+ const basegfx::B2DRange& rDefinitionRange,
+ const attribute::SdrFillAttribute& rFill,
+ const attribute::FillGradientAttribute& rFillGradient)
+ {
+ if(basegfx::fTools::moreOrEqual(rFill.getTransparence(), 1.0))
+ {
+ return Primitive2DReference();
+ }
+
+ // prepare fully scaled polygon
+ rtl::Reference<BasePrimitive2D> pNewFillPrimitive;
+
+ if(!rFill.getGradient().isDefault())
+ {
+ pNewFillPrimitive = new PolyPolygonGradientPrimitive2D(
+ rPolyPolygon,
+ rDefinitionRange,
+ rFill.getGradient());
+ }
+ else if(!rFill.getHatch().isDefault())
+ {
+ pNewFillPrimitive = new PolyPolygonHatchPrimitive2D(
+ rPolyPolygon,
+ rDefinitionRange,
+ rFill.getColor(),
+ rFill.getHatch());
+ }
+ else if(!rFill.getFillGraphic().isDefault())
+ {
+ pNewFillPrimitive = new PolyPolygonGraphicPrimitive2D(
+ rPolyPolygon,
+ rDefinitionRange,
+ rFill.getFillGraphic().createFillGraphicAttribute(rDefinitionRange));
+ }
+ else if(rFill.isSlideBackgroundFill())
+ {
+ // create needed Primitive2D representation for
+ // SlideBackgroundFill-mode
+ pNewFillPrimitive = new SlideBackgroundFillPrimitive2D(
+ rPolyPolygon);
+ }
+ else
+ {
+ pNewFillPrimitive = new PolyPolygonColorPrimitive2D(
+ rPolyPolygon,
+ rFill.getColor());
+ }
+
+ if(0.0 != rFill.getTransparence())
+ {
+ // create simpleTransparencePrimitive, add created fill primitive
+ Primitive2DContainer aContent { pNewFillPrimitive };
+ return Primitive2DReference(new UnifiedTransparencePrimitive2D(std::move(aContent), rFill.getTransparence()));
+ }
+ else if(!rFillGradient.isDefault())
+ {
+ // create sequence with created fill primitive
+ Primitive2DContainer aContent { pNewFillPrimitive };
+
+ // create FillGradientPrimitive2D for transparence and add to new sequence
+ // fillGradientPrimitive is enough here (compared to PolyPolygonGradientPrimitive2D) since float transparence will be masked anyways
+ const basegfx::B2DRange aRange(basegfx::utils::getRange(rPolyPolygon));
+ Primitive2DReference xRefB(
+ new FillGradientPrimitive2D(
+ aRange,
+ rDefinitionRange,
+ rFillGradient));
+ Primitive2DContainer aAlpha { xRefB };
+
+ // create TransparencePrimitive2D using alpha and content
+ return Primitive2DReference(new TransparencePrimitive2D(std::move(aContent), std::move(aAlpha)));
+ }
+ else
+ {
+ // add to decomposition
+ return pNewFillPrimitive;
+ }
+ }
+
+ Primitive2DReference createPolygonLinePrimitive(
+ const basegfx::B2DPolygon& rPolygon,
+ const attribute::SdrLineAttribute& rLine,
+ const attribute::SdrLineStartEndAttribute& rStroke)
+ {
+ // create line and stroke attribute
+ const attribute::LineAttribute aLineAttribute(rLine.getColor(), rLine.getWidth(), rLine.getJoin(), rLine.getCap());
+ const attribute::StrokeAttribute aStrokeAttribute(std::vector(rLine.getDotDashArray()), rLine.getFullDotDashLen());
+ rtl::Reference<BasePrimitive2D> pNewLinePrimitive;
+
+ if(!rPolygon.isClosed() && !rStroke.isDefault())
+ {
+ attribute::LineStartEndAttribute aStart(rStroke.getStartWidth(), rStroke.getStartPolyPolygon(), rStroke.isStartCentered());
+ attribute::LineStartEndAttribute aEnd(rStroke.getEndWidth(), rStroke.getEndPolyPolygon(), rStroke.isEndCentered());
+
+ // create data
+ pNewLinePrimitive = new PolygonStrokeArrowPrimitive2D(rPolygon, aLineAttribute, aStrokeAttribute, aStart, aEnd);
+ }
+ else
+ {
+ // create data
+ pNewLinePrimitive = new PolygonStrokePrimitive2D(rPolygon, aLineAttribute, aStrokeAttribute);
+ }
+
+ if(0.0 != rLine.getTransparence())
+ {
+ // create simpleTransparencePrimitive, add created fill primitive
+ Primitive2DContainer aContent { pNewLinePrimitive };
+ return Primitive2DReference(new UnifiedTransparencePrimitive2D(std::move(aContent), rLine.getTransparence()));
+ }
+ else
+ {
+ // add to decomposition
+ return pNewLinePrimitive;
+ }
+ }
+
+ Primitive2DReference createTextPrimitive(
+ const basegfx::B2DPolyPolygon& rUnitPolyPolygon,
+ const basegfx::B2DHomMatrix& rObjectTransform,
+ const attribute::SdrTextAttribute& rText,
+ const attribute::SdrLineAttribute& rStroke,
+ bool bCellText,
+ bool bWordWrap)
+ {
+ basegfx::B2DHomMatrix aAnchorTransform(rObjectTransform);
+ rtl::Reference<SdrTextPrimitive2D> pNew;
+
+ if(rText.isContour())
+ {
+ // contour text
+ if(!rStroke.isDefault() && 0.0 != rStroke.getWidth())
+ {
+ // take line width into account and shrink contour polygon accordingly
+ // decompose to get scale
+ basegfx::B2DVector aScale, aTranslate;
+ double fRotate, fShearX;
+ rObjectTransform.decompose(aScale, aTranslate, fRotate, fShearX);
+
+ // scale outline to object's size to allow growing with value relative to that size
+ // and also to keep aspect ratio
+ basegfx::B2DPolyPolygon aScaledUnitPolyPolygon(rUnitPolyPolygon);
+ aScaledUnitPolyPolygon.transform(basegfx::utils::createScaleB2DHomMatrix(
+ fabs(aScale.getX()), fabs(aScale.getY())));
+
+ // grow the polygon. To shrink, use negative value (half width)
+ aScaledUnitPolyPolygon = basegfx::utils::growInNormalDirection(aScaledUnitPolyPolygon, -(rStroke.getWidth() * 0.5));
+
+ // scale back to unit polygon
+ aScaledUnitPolyPolygon.transform(basegfx::utils::createScaleB2DHomMatrix(
+ 0.0 != aScale.getX() ? 1.0 / aScale.getX() : 1.0,
+ 0.0 != aScale.getY() ? 1.0 / aScale.getY() : 1.0));
+
+ // create with unit polygon
+ pNew = new SdrContourTextPrimitive2D(
+ &rText.getSdrText(),
+ rText.getOutlinerParaObject(),
+ aScaledUnitPolyPolygon,
+ rObjectTransform);
+ }
+ else
+ {
+ // create with unit polygon
+ pNew = new SdrContourTextPrimitive2D(
+ &rText.getSdrText(),
+ rText.getOutlinerParaObject(),
+ rUnitPolyPolygon,
+ rObjectTransform);
+ }
+ }
+ else if(!rText.getSdrFormTextAttribute().isDefault())
+ {
+ // text on path, use scaled polygon
+ basegfx::B2DPolyPolygon aScaledPolyPolygon(rUnitPolyPolygon);
+ aScaledPolyPolygon.transform(rObjectTransform);
+ pNew = new SdrPathTextPrimitive2D(
+ &rText.getSdrText(),
+ rText.getOutlinerParaObject(),
+ aScaledPolyPolygon,
+ rText.getSdrFormTextAttribute());
+ }
+ else
+ {
+ // rObjectTransform is the whole SdrObject transformation from unit rectangle
+ // to its size and position. Decompose to allow working with single values.
+ basegfx::B2DVector aScale, aTranslate;
+ double fRotate, fShearX;
+ rObjectTransform.decompose(aScale, aTranslate, fRotate, fShearX);
+
+ // extract mirroring
+ const bool bMirrorX(basegfx::fTools::less(aScale.getX(), 0.0));
+ const bool bMirrorY(basegfx::fTools::less(aScale.getY(), 0.0));
+ aScale = basegfx::absolute(aScale);
+
+ // Get the real size, since polygon outline and scale
+ // from the object transformation may vary (e.g. ellipse segments)
+ basegfx::B2DHomMatrix aJustScaleTransform;
+ aJustScaleTransform.set(0, 0, aScale.getX());
+ aJustScaleTransform.set(1, 1, aScale.getY());
+ basegfx::B2DPolyPolygon aScaledUnitPolyPolygon(rUnitPolyPolygon);
+ aScaledUnitPolyPolygon.transform(aJustScaleTransform);
+ const basegfx::B2DRange aTextAnchorRange
+ = getTextAnchorRange(rText, basegfx::utils::getRange(aScaledUnitPolyPolygon));
+
+ // now create a transformation from this basic range (aTextAnchorRange)
+ // #i121494# if we have no scale use at least 1.0 to have a carrier e.g. for
+ // mirror values, else these will get lost
+ aAnchorTransform = basegfx::utils::createScaleTranslateB2DHomMatrix(
+ basegfx::fTools::equalZero(aTextAnchorRange.getWidth()) ? 1.0 : aTextAnchorRange.getWidth(),
+ basegfx::fTools::equalZero(aTextAnchorRange.getHeight()) ? 1.0 : aTextAnchorRange.getHeight(),
+ aTextAnchorRange.getMinX(), aTextAnchorRange.getMinY());
+
+ // apply mirroring
+ aAnchorTransform.scale(bMirrorX ? -1.0 : 1.0, bMirrorY ? -1.0 : 1.0);
+
+ // apply object's other transforms
+ aAnchorTransform = basegfx::utils::createShearXRotateTranslateB2DHomMatrix(fShearX, fRotate, aTranslate)
+ * aAnchorTransform;
+
+ if(rText.isFitToSize())
+ {
+ // stretched text in range
+ pNew = new SdrStretchTextPrimitive2D(
+ &rText.getSdrText(),
+ rText.getOutlinerParaObject(),
+ aAnchorTransform,
+ rText.isFixedCellHeight());
+ }
+ else if(rText.isAutoFit())
+ {
+ // isotropically scaled text in range
+ pNew = new SdrAutoFitTextPrimitive2D(
+ &rText.getSdrText(),
+ rText.getOutlinerParaObject(),
+ aAnchorTransform,
+ bWordWrap);
+ }
+ else if( rText.isChainable() && !rText.isInEditMode() )
+ {
+ pNew = new SdrChainedTextPrimitive2D(
+ &rText.getSdrText(),
+ rText.getOutlinerParaObject(),
+ aAnchorTransform );
+ }
+ else // text in range
+ {
+ // build new primitive
+ pNew = new SdrBlockTextPrimitive2D(
+ &rText.getSdrText(),
+ rText.getOutlinerParaObject(),
+ aAnchorTransform,
+ rText.getSdrTextHorzAdjust(),
+ rText.getSdrTextVertAdjust(),
+ rText.isFixedCellHeight(),
+ rText.isScroll(),
+ bCellText,
+ bWordWrap);
+ }
+ }
+
+ OSL_ENSURE(pNew != nullptr, "createTextPrimitive: no text primitive created (!)");
+
+ if(rText.isBlink())
+ {
+ // prepare animation and primitive list
+ drawinglayer::animation::AnimationEntryList aAnimationList;
+ rText.getBlinkTextTiming(aAnimationList);
+
+ if(0.0 != aAnimationList.getDuration())
+ {
+ // create content sequence
+ Primitive2DReference xRefA(pNew);
+ Primitive2DContainer aContent { xRefA };
+
+ // create and add animated switch primitive
+ return Primitive2DReference(new AnimatedBlinkPrimitive2D(aAnimationList, std::move(aContent)));
+ }
+ else
+ {
+ // add to decomposition
+ return Primitive2DReference(pNew);
+ }
+ }
+
+ if(rText.isScroll())
+ {
+ // suppress scroll when FontWork
+ if(rText.getSdrFormTextAttribute().isDefault())
+ {
+ // get scroll direction
+ const SdrTextAniDirection eDirection(rText.getSdrText().GetObject().GetTextAniDirection());
+ const bool bHorizontal(SdrTextAniDirection::Left == eDirection || SdrTextAniDirection::Right == eDirection);
+
+ // decompose to get separated values for the scroll box
+ basegfx::B2DVector aScale, aTranslate;
+ double fRotate, fShearX;
+ aAnchorTransform.decompose(aScale, aTranslate, fRotate, fShearX);
+
+ // build transform from scaled only to full AnchorTransform and inverse
+ const basegfx::B2DHomMatrix aSRT(basegfx::utils::createShearXRotateTranslateB2DHomMatrix(
+ fShearX, fRotate, aTranslate));
+ basegfx::B2DHomMatrix aISRT(aSRT);
+ aISRT.invert();
+
+ // bring the primitive back to scaled only and get scaled range, create new clone for this
+ rtl::Reference<SdrTextPrimitive2D> pNew2 = pNew->createTransformedClone(aISRT);
+ OSL_ENSURE(pNew2, "createTextPrimitive: Could not create transformed clone of text primitive (!)");
+ pNew = pNew2.get();
+
+ // create neutral geometry::ViewInformation2D for local range and decompose calls. This is okay
+ // since the decompose is view-independent
+ geometry::ViewInformation2D aViewInformation2D;
+
+ // get range
+ const basegfx::B2DRange aScaledRange(pNew->getB2DRange(aViewInformation2D));
+
+ // create left outside and right outside transformations. Also take care
+ // of the clip rectangle
+ basegfx::B2DHomMatrix aLeft, aRight;
+ basegfx::B2DPoint aClipTopLeft(0.0, 0.0);
+ basegfx::B2DPoint aClipBottomRight(aScale.getX(), aScale.getY());
+
+ if(bHorizontal)
+ {
+ aClipTopLeft.setY(aScaledRange.getMinY());
+ aClipBottomRight.setY(aScaledRange.getMaxY());
+ aLeft.translate(-aScaledRange.getMaxX(), 0.0);
+ aRight.translate(aScale.getX() - aScaledRange.getMinX(), 0.0);
+ }
+ else
+ {
+ aClipTopLeft.setX(aScaledRange.getMinX());
+ aClipBottomRight.setX(aScaledRange.getMaxX());
+ aLeft.translate(0.0, -aScaledRange.getMaxY());
+ aRight.translate(0.0, aScale.getY() - aScaledRange.getMinY());
+ }
+
+ aLeft *= aSRT;
+ aRight *= aSRT;
+
+ // prepare animation list
+ drawinglayer::animation::AnimationEntryList aAnimationList;
+
+ if(bHorizontal)
+ {
+ rText.getScrollTextTiming(aAnimationList, aScale.getX(), aScaledRange.getWidth());
+ }
+ else
+ {
+ rText.getScrollTextTiming(aAnimationList, aScale.getY(), aScaledRange.getHeight());
+ }
+
+ if(0.0 != aAnimationList.getDuration())
+ {
+ // create a new Primitive2DContainer containing the animated text in its scaled only state.
+ // use the decomposition to force to simple text primitives, those will no longer
+ // need the outliner for formatting (alternatively it is also possible to just add
+ // pNew to aNewPrimitiveSequence)
+ Primitive2DContainer aAnimSequence;
+ pNew->get2DDecomposition(aAnimSequence, aViewInformation2D);
+ pNew.clear();
+
+ // create a new animatedInterpolatePrimitive and add it
+ Primitive2DReference xRefA(new AnimatedInterpolatePrimitive2D({ aLeft, aRight }, aAnimationList, std::move(aAnimSequence)));
+ Primitive2DContainer aContent { xRefA };
+
+ // scrolling needs an encapsulating clipping primitive
+ const basegfx::B2DRange aClipRange(aClipTopLeft, aClipBottomRight);
+ basegfx::B2DPolygon aClipPolygon(basegfx::utils::createPolygonFromRect(aClipRange));
+ aClipPolygon.transform(aSRT);
+ return Primitive2DReference(new MaskPrimitive2D(basegfx::B2DPolyPolygon(aClipPolygon), std::move(aContent)));
+ }
+ else
+ {
+ // add to decomposition
+ return Primitive2DReference(pNew);
+ }
+ }
+ }
+
+ if(rText.isInEditMode())
+ {
+ // #i97628#
+ // encapsulate with TextHierarchyEditPrimitive2D to allow renderers
+ // to suppress actively edited content if needed
+ Primitive2DReference xRefA(pNew);
+ Primitive2DContainer aContent { xRefA };
+
+ // create and add TextHierarchyEditPrimitive2D primitive
+ return Primitive2DReference(new TextHierarchyEditPrimitive2D(std::move(aContent)));
+ }
+ else
+ {
+ // add to decomposition
+ return pNew;
+ }
+ }
+
+ Primitive2DContainer createEmbeddedShadowPrimitive(
+ Primitive2DContainer&& rContent,
+ const attribute::SdrShadowAttribute& rShadow,
+ const basegfx::B2DHomMatrix& rObjectMatrix,
+ const Primitive2DContainer* pContentForShadow)
+ {
+ if(rContent.empty())
+ return std::move(rContent);
+
+ basegfx::B2DHomMatrix aShadowOffset;
+
+ if(rShadow.getSize().getX() != 100000)
+ {
+ basegfx::B2DTuple aScale;
+ basegfx::B2DTuple aTranslate;
+ double fRotate = 0;
+ double fShearX = 0;
+ rObjectMatrix.decompose(aScale, aTranslate, fRotate, fShearX);
+ // Scale the shadow
+ double nTranslateX = aTranslate.getX();
+ double nTranslateY = aTranslate.getY();
+
+ // The origin for scaling is the top left corner by default. A negative
+ // shadow offset changes the origin.
+ if (rShadow.getOffset().getX() < 0)
+ nTranslateX += aScale.getX();
+ if (rShadow.getOffset().getY() < 0)
+ nTranslateY += aScale.getY();
+
+ aShadowOffset.translate(-nTranslateX, -nTranslateY);
+ aShadowOffset.scale(rShadow.getSize().getX() * 0.00001, rShadow.getSize().getY() * 0.00001);
+ aShadowOffset.translate(nTranslateX, nTranslateY);
+ }
+
+ aShadowOffset.translate(rShadow.getOffset().getX(), rShadow.getOffset().getY());
+
+ // create shadow primitive and add content
+ const Primitive2DContainer& rContentForShadow
+ = pContentForShadow ? *pContentForShadow : rContent;
+ int nContentWithTransparence = std::count_if(
+ rContentForShadow.begin(), rContentForShadow.end(),
+ [](const Primitive2DReference& xChild) {
+ auto pChild = dynamic_cast<BufferedDecompositionPrimitive2D*>(xChild.get());
+ return pChild && pChild->getTransparenceForShadow() != 0;
+ });
+ if (nContentWithTransparence == 0)
+ {
+ Primitive2DContainer aRetval(2);
+ aRetval[0] = Primitive2DReference(
+ new ShadowPrimitive2D(
+ aShadowOffset,
+ rShadow.getColor(),
+ rShadow.getBlur(),
+ Primitive2DContainer(pContentForShadow ? *pContentForShadow : rContent)));
+
+ if (0.0 != rShadow.getTransparence())
+ {
+ // create SimpleTransparencePrimitive2D
+ Primitive2DContainer aTempContent{ aRetval[0] };
+
+ aRetval[0] = Primitive2DReference(
+ new UnifiedTransparencePrimitive2D(
+ std::move(aTempContent),
+ rShadow.getTransparence()));
+ }
+
+ aRetval[1] = Primitive2DReference(new GroupPrimitive2D(std::move(rContent)));
+ return aRetval;
+ }
+
+ Primitive2DContainer aRetval;
+ for (const auto& xChild : rContentForShadow)
+ {
+ double fChildTransparence = 0.0;
+ auto pChild = dynamic_cast<BufferedDecompositionPrimitive2D*>(xChild.get());
+ if (pChild)
+ {
+ fChildTransparence = pChild->getTransparenceForShadow();
+ fChildTransparence /= 100;
+ }
+ aRetval.push_back(Primitive2DReference(
+ new ShadowPrimitive2D(aShadowOffset, rShadow.getColor(), rShadow.getBlur(),
+ Primitive2DContainer({ xChild }))));
+ if (rShadow.getTransparence() != 0.0 || fChildTransparence != 0.0)
+ {
+ Primitive2DContainer aTempContent{ aRetval.back() };
+
+ double fChildAlpha = 1.0 - fChildTransparence;
+ double fShadowAlpha = 1.0 - rShadow.getTransparence();
+ double fTransparence = 1.0 - fChildAlpha * fShadowAlpha;
+ aRetval.back() = Primitive2DReference(new UnifiedTransparencePrimitive2D(
+ std::move(aTempContent), fTransparence));
+ }
+ }
+
+ aRetval.push_back(
+ Primitive2DReference(new GroupPrimitive2D(std::move(rContent))));
+ return aRetval;
+ }
+
+ Primitive2DContainer createEmbeddedGlowPrimitive(
+ Primitive2DContainer&& rContent,
+ const attribute::SdrGlowAttribute& rGlow)
+ {
+ if(rContent.empty())
+ return std::move(rContent);
+ Primitive2DContainer aRetval(2);
+ aRetval[0] = Primitive2DReference(
+ new GlowPrimitive2D(rGlow.getColor(), rGlow.getRadius(), Primitive2DContainer(rContent)));
+ aRetval[1] = Primitive2DReference(new GroupPrimitive2D(Primitive2DContainer(rContent)));
+ return aRetval;
+ }
+
+ Primitive2DContainer createEmbeddedSoftEdgePrimitive(Primitive2DContainer&& aContent,
+ sal_Int32 nRadius)
+ {
+ if (aContent.empty() || !nRadius)
+ return std::move(aContent);
+ Primitive2DContainer aRetval(1);
+ aRetval[0] = Primitive2DReference(new SoftEdgePrimitive2D(nRadius, std::move(aContent)));
+ return aRetval;
+ }
+
+} // end of namespace
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/sdr/primitive2d/sdrellipseprimitive2d.cxx b/svx/source/sdr/primitive2d/sdrellipseprimitive2d.cxx
new file mode 100644
index 000000000..89c348beb
--- /dev/null
+++ b/svx/source/sdr/primitive2d/sdrellipseprimitive2d.cxx
@@ -0,0 +1,266 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <sdr/primitive2d/sdrellipseprimitive2d.hxx>
+#include <basegfx/polygon/b2dpolygon.hxx>
+#include <basegfx/polygon/b2dpolygontools.hxx>
+#include <basegfx/polygon/b2dpolypolygon.hxx>
+#include <sdr/primitive2d/sdrdecompositiontools.hxx>
+#include <svx/sdr/primitive2d/svx_primitivetypes2d.hxx>
+#include <basegfx/matrix/b2dhommatrixtools.hxx>
+#include <drawinglayer/primitive2d/sdrdecompositiontools2d.hxx>
+
+
+using namespace com::sun::star;
+
+
+namespace drawinglayer::primitive2d
+{
+ void SdrEllipsePrimitive2D::create2DDecomposition(Primitive2DContainer& rContainer, const geometry::ViewInformation2D& /*aViewInformation*/) const
+ {
+ Primitive2DContainer aRetval;
+
+ // create unit outline polygon
+ // Do use createPolygonFromUnitCircle, but let create from first quadrant to mimic old geometry creation.
+ // This is needed to have the same look when stroke is used since the polygon start point defines the
+ // stroke start, too.
+ basegfx::B2DPolygon aUnitOutline(basegfx::utils::createPolygonFromUnitCircle(1));
+
+ // scale and move UnitEllipse to UnitObject (-1,-1 1,1) -> (0,0 1,1)
+ const basegfx::B2DHomMatrix aUnitCorrectionMatrix(
+ basegfx::utils::createScaleTranslateB2DHomMatrix(0.5, 0.5, 0.5, 0.5));
+
+ // apply to the geometry
+ aUnitOutline.transform(aUnitCorrectionMatrix);
+
+ // add fill
+ if(!getSdrLFSTAttribute().getFill().isDefault())
+ {
+ basegfx::B2DPolyPolygon aTransformed(aUnitOutline);
+
+ aTransformed.transform(getTransform());
+ aRetval.push_back(
+ createPolyPolygonFillPrimitive(
+ aTransformed,
+ getSdrLFSTAttribute().getFill(),
+ getSdrLFSTAttribute().getFillFloatTransGradient()));
+ }
+
+ // add line
+ if(getSdrLFSTAttribute().getLine().isDefault())
+ {
+ // create invisible line for HitTest/BoundRect
+ aRetval.push_back(
+ createHiddenGeometryPrimitives2D(
+ false,
+ basegfx::B2DPolyPolygon(aUnitOutline),
+ getTransform()));
+ }
+ else
+ {
+ basegfx::B2DPolygon aTransformed(aUnitOutline);
+
+ aTransformed.transform(getTransform());
+ aRetval.push_back(
+ createPolygonLinePrimitive(
+ aTransformed,
+ getSdrLFSTAttribute().getLine(),
+ attribute::SdrLineStartEndAttribute()));
+ }
+
+ // add text
+ if(!getSdrLFSTAttribute().getText().isDefault())
+ {
+ aRetval.push_back(
+ createTextPrimitive(
+ basegfx::B2DPolyPolygon(aUnitOutline),
+ getTransform(),
+ getSdrLFSTAttribute().getText(),
+ getSdrLFSTAttribute().getLine(),
+ false,
+ false));
+ }
+
+ // add shadow
+ if(!getSdrLFSTAttribute().getShadow().isDefault())
+ {
+ aRetval = createEmbeddedShadowPrimitive(
+ std::move(aRetval),
+ getSdrLFSTAttribute().getShadow());
+ }
+
+ rContainer.append(std::move(aRetval));
+ }
+
+ SdrEllipsePrimitive2D::SdrEllipsePrimitive2D(
+ const basegfx::B2DHomMatrix& rTransform,
+ const attribute::SdrLineFillEffectsTextAttribute& rSdrLFSTAttribute)
+ : maTransform(rTransform),
+ maSdrLFSTAttribute(rSdrLFSTAttribute)
+ {
+ }
+
+ bool SdrEllipsePrimitive2D::operator==(const BasePrimitive2D& rPrimitive) const
+ {
+ if(BufferedDecompositionPrimitive2D::operator==(rPrimitive))
+ {
+ const SdrEllipsePrimitive2D& rCompare = static_cast<const SdrEllipsePrimitive2D&>(rPrimitive);
+
+ return (getTransform() == rCompare.getTransform()
+ && getSdrLFSTAttribute() == rCompare.getSdrLFSTAttribute());
+ }
+
+ return false;
+ }
+
+ // provide unique ID
+ sal_uInt32 SdrEllipsePrimitive2D::getPrimitive2DID() const
+ {
+ return PRIMITIVE2D_ID_SDRELLIPSEPRIMITIVE2D;
+ }
+
+
+
+ void SdrEllipseSegmentPrimitive2D::create2DDecomposition(Primitive2DContainer& rContainer, const geometry::ViewInformation2D& /*aViewInformation*/) const
+ {
+ Primitive2DContainer aRetval;
+
+ // create unit outline polygon
+ basegfx::B2DPolygon aUnitOutline(basegfx::utils::createPolygonFromUnitEllipseSegment(mfStartAngle, mfEndAngle));
+
+ if(mbCloseSegment)
+ {
+ if(mbCloseUsingCenter)
+ {
+ // for compatibility, insert the center point at polygon start to get the same
+ // line stroking pattern as the old painting mechanisms.
+ aUnitOutline.insert(0, basegfx::B2DPoint(0.0, 0.0));
+ }
+
+ aUnitOutline.setClosed(true);
+ }
+
+ // move and scale UnitEllipse to UnitObject (-1,-1 1,1) -> (0,0 1,1)
+ const basegfx::B2DHomMatrix aUnitCorrectionMatrix(
+ basegfx::utils::createScaleTranslateB2DHomMatrix(0.5, 0.5, 0.5, 0.5));
+
+ // apply to the geometry
+ aUnitOutline.transform(aUnitCorrectionMatrix);
+
+ // add fill
+ if(!getSdrLFSTAttribute().getFill().isDefault() && aUnitOutline.isClosed())
+ {
+ basegfx::B2DPolyPolygon aTransformed(aUnitOutline);
+
+ aTransformed.transform(getTransform());
+ aRetval.push_back(
+ createPolyPolygonFillPrimitive(
+ aTransformed,
+ getSdrLFSTAttribute().getFill(),
+ getSdrLFSTAttribute().getFillFloatTransGradient()));
+ }
+
+ // add line
+ if(getSdrLFSTAttribute().getLine().isDefault())
+ {
+ // create invisible line for HitTest/BoundRect
+ aRetval.push_back(
+ createHiddenGeometryPrimitives2D(
+ false,
+ basegfx::B2DPolyPolygon(aUnitOutline),
+ getTransform()));
+ }
+ else
+ {
+ basegfx::B2DPolygon aTransformed(aUnitOutline);
+
+ aTransformed.transform(getTransform());
+ aRetval.push_back(
+ createPolygonLinePrimitive(
+ aTransformed,
+ getSdrLFSTAttribute().getLine(),
+ getSdrLFSTAttribute().getLineStartEnd()));
+ }
+
+ // add text
+ if(!getSdrLFSTAttribute().getText().isDefault())
+ {
+ aRetval.push_back(
+ createTextPrimitive(
+ basegfx::B2DPolyPolygon(aUnitOutline),
+ getTransform(),
+ getSdrLFSTAttribute().getText(),
+ getSdrLFSTAttribute().getLine(),
+ false,
+ false));
+ }
+
+ // add shadow
+ if(!getSdrLFSTAttribute().getShadow().isDefault())
+ {
+ aRetval = createEmbeddedShadowPrimitive(
+ std::move(aRetval),
+ getSdrLFSTAttribute().getShadow());
+ }
+
+ rContainer.append(std::move(aRetval));
+ }
+
+ SdrEllipseSegmentPrimitive2D::SdrEllipseSegmentPrimitive2D(
+ const basegfx::B2DHomMatrix& rTransform,
+ const attribute::SdrLineFillEffectsTextAttribute& rSdrLFSTAttribute,
+ double fStartAngle,
+ double fEndAngle,
+ bool bCloseSegment,
+ bool bCloseUsingCenter)
+ : SdrEllipsePrimitive2D(rTransform, rSdrLFSTAttribute),
+ mfStartAngle(fStartAngle),
+ mfEndAngle(fEndAngle),
+ mbCloseSegment(bCloseSegment),
+ mbCloseUsingCenter(bCloseUsingCenter)
+ {
+ }
+
+ bool SdrEllipseSegmentPrimitive2D::operator==(const BasePrimitive2D& rPrimitive) const
+ {
+ if(SdrEllipsePrimitive2D::operator==(rPrimitive))
+ {
+ const SdrEllipseSegmentPrimitive2D& rCompare = static_cast<const SdrEllipseSegmentPrimitive2D&>(rPrimitive);
+
+ if( mfStartAngle == rCompare.mfStartAngle
+ && mfEndAngle == rCompare.mfEndAngle
+ && mbCloseSegment == rCompare.mbCloseSegment
+ && mbCloseUsingCenter == rCompare.mbCloseUsingCenter)
+ {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ // provide unique ID
+ sal_uInt32 SdrEllipseSegmentPrimitive2D::getPrimitive2DID() const
+ {
+ return PRIMITIVE2D_ID_SDRELLIPSESEGMENTPRIMITIVE2D;
+ }
+
+} // end of namespace
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/sdr/primitive2d/sdrframeborderprimitive2d.cxx b/svx/source/sdr/primitive2d/sdrframeborderprimitive2d.cxx
new file mode 100644
index 000000000..0610f43b0
--- /dev/null
+++ b/svx/source/sdr/primitive2d/sdrframeborderprimitive2d.cxx
@@ -0,0 +1,928 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * 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/sdr/primitive2d/sdrframeborderprimitive2d.hxx>
+#include <drawinglayer/primitive2d/borderlineprimitive2d.hxx>
+#include <drawinglayer/geometry/viewinformation2d.hxx>
+#include <svx/sdr/primitive2d/svx_primitivetypes2d.hxx>
+#include <basegfx/polygon/b2dpolygontools.hxx>
+#include <svtools/borderhelper.hxx>
+
+namespace
+{
+ double snapToDiscreteUnit(
+ double fValue,
+ double fMinimalDiscreteUnit)
+ {
+ if(0.0 != fValue)
+ {
+ fValue = std::max(fValue, fMinimalDiscreteUnit);
+ }
+
+ return fValue;
+ }
+
+ class StyleVectorCombination
+ {
+ private:
+ struct OffsetAndHalfWidthAndColor
+ {
+ double mfOffset;
+ double mfHalfWidth;
+ Color maColor;
+
+ OffsetAndHalfWidthAndColor(double offset, double halfWidth, Color color) :
+ mfOffset(offset),
+ mfHalfWidth(halfWidth),
+ maColor(color)
+ {}
+ };
+
+ double mfRefModeOffset;
+ basegfx::B2DVector maB2DVector;
+ double mfAngle;
+ std::vector< OffsetAndHalfWidthAndColor > maOffsets;
+
+ public:
+ StyleVectorCombination(
+ const svx::frame::Style& rStyle,
+ const basegfx::B2DVector& rB2DVector,
+ double fAngle,
+ bool bMirrored,
+ const Color* pForceColor,
+ double fMinimalDiscreteUnit)
+ : mfRefModeOffset(0.0),
+ maB2DVector(rB2DVector),
+ mfAngle(fAngle)
+ {
+ if (!rStyle.IsUsed())
+ return;
+
+ svx::frame::RefMode aRefMode(rStyle.GetRefMode());
+ Color aPrim(rStyle.GetColorPrim());
+ Color aSecn(rStyle.GetColorSecn());
+ const bool bSecnUsed(0.0 != rStyle.Secn());
+
+ // Get the single segment line widths. This is the point where the
+ // minimal discrete unit will be used if given (fMinimalDiscreteUnit). If
+ // not given it's 0.0 and thus will have no influence.
+ double fPrim(snapToDiscreteUnit(rStyle.Prim(), fMinimalDiscreteUnit));
+ const double fDist(snapToDiscreteUnit(rStyle.Dist(), fMinimalDiscreteUnit));
+ double fSecn(snapToDiscreteUnit(rStyle.Secn(), fMinimalDiscreteUnit));
+
+ // Of course also do not use svx::frame::Style::GetWidth() for obvious
+ // reasons.
+ const double fStyleWidth(fPrim + fDist + fSecn);
+
+ if(bMirrored)
+ {
+ switch(aRefMode)
+ {
+ case svx::frame::RefMode::Begin: aRefMode = svx::frame::RefMode::End; break;
+ case svx::frame::RefMode::End: aRefMode = svx::frame::RefMode::Begin; break;
+ default: break;
+ }
+
+ if(bSecnUsed)
+ {
+ std::swap(aPrim, aSecn);
+ std::swap(fPrim, fSecn);
+ }
+ }
+
+ if (svx::frame::RefMode::Centered != aRefMode)
+ {
+ const double fHalfWidth(fStyleWidth * 0.5);
+
+ if (svx::frame::RefMode::Begin == aRefMode)
+ {
+ // move aligned below vector
+ mfRefModeOffset = fHalfWidth;
+ }
+ else if (svx::frame::RefMode::End == aRefMode)
+ {
+ // move aligned above vector
+ mfRefModeOffset = -fHalfWidth;
+ }
+ }
+
+ if (bSecnUsed)
+ {
+ // both or all three lines used
+ const bool bPrimTransparent(rStyle.GetColorPrim().IsFullyTransparent());
+ const bool bDistTransparent(!rStyle.UseGapColor() || rStyle.GetColorGap().IsFullyTransparent());
+ const bool bSecnTransparent(aSecn.IsFullyTransparent());
+
+ if(!bPrimTransparent || !bDistTransparent || !bSecnTransparent)
+ {
+ const double a(mfRefModeOffset - (fStyleWidth * 0.5));
+ const double b(a + fPrim);
+ const double c(b + fDist);
+ const double d(c + fSecn);
+
+ maOffsets.push_back(
+ OffsetAndHalfWidthAndColor(
+ (a + b) * 0.5,
+ fPrim * 0.5,
+ nullptr != pForceColor ? *pForceColor : aPrim));
+
+ maOffsets.push_back(
+ OffsetAndHalfWidthAndColor(
+ (b + c) * 0.5,
+ fDist * 0.5,
+ rStyle.UseGapColor()
+ ? (nullptr != pForceColor ? *pForceColor : rStyle.GetColorGap())
+ : COL_TRANSPARENT));
+
+ maOffsets.push_back(
+ OffsetAndHalfWidthAndColor(
+ (c + d) * 0.5,
+ fSecn * 0.5,
+ nullptr != pForceColor ? *pForceColor : aSecn));
+ }
+ }
+ else
+ {
+ // one line used, push two values, from outer to inner
+ if(!rStyle.GetColorPrim().IsFullyTransparent())
+ {
+ maOffsets.push_back(
+ OffsetAndHalfWidthAndColor(
+ mfRefModeOffset,
+ fPrim * 0.5,
+ nullptr != pForceColor ? *pForceColor : aPrim));
+ }
+ }
+ }
+
+ double getRefModeOffset() const { return mfRefModeOffset; }
+ const basegfx::B2DVector& getB2DVector() const { return maB2DVector; }
+ double getAngle() const { return mfAngle; }
+ bool empty() const { return maOffsets.empty(); }
+ size_t size() const { return maOffsets.size(); }
+
+ void getColorAndOffsetAndHalfWidth(size_t nIndex, Color& rColor, double& rfOffset, double& rfHalfWidth) const
+ {
+ if(nIndex >= maOffsets.size())
+ return;
+ const OffsetAndHalfWidthAndColor& rCandidate(maOffsets[nIndex]);
+ rfOffset = rCandidate.mfOffset;
+ rfHalfWidth = rCandidate.mfHalfWidth;
+ rColor = rCandidate.maColor;
+ }
+ };
+
+ class StyleVectorTable
+ {
+ private:
+ std::vector< StyleVectorCombination > maEntries;
+
+ public:
+ StyleVectorTable()
+ {
+ }
+
+ void add(
+ const svx::frame::Style& rStyle,
+ const basegfx::B2DVector& rMyVector,
+ const basegfx::B2DVector& rOtherVector,
+ bool bMirrored,
+ double fMinimalDiscreteUnit)
+ {
+ if(!rStyle.IsUsed() || basegfx::areParallel(rMyVector, rOtherVector))
+ return;
+
+ // create angle between both. angle() needs vectors pointing away from the same point,
+ // so take the mirrored one. Add M_PI to get from -pi..+pi to [0..M_PI_2] for sorting
+ const double fAngle(basegfx::B2DVector(-rMyVector.getX(), -rMyVector.getY()).angle(rOtherVector) + M_PI);
+ maEntries.emplace_back(
+ rStyle,
+ rOtherVector,
+ fAngle,
+ bMirrored,
+ nullptr,
+ fMinimalDiscreteUnit);
+ }
+
+ void sort()
+ {
+ // sort inverse from highest to lowest
+ std::sort(
+ maEntries.begin(),
+ maEntries.end(),
+ [](const StyleVectorCombination& a, const StyleVectorCombination& b)
+ { return a.getAngle() > b.getAngle(); });
+ }
+
+ bool empty() const { return maEntries.empty(); }
+ const std::vector< StyleVectorCombination >& getEntries() const{ return maEntries; }
+ };
+
+ struct CutSet
+ {
+ double mfOLML;
+ double mfORML;
+ double mfOLMR;
+ double mfORMR;
+
+ CutSet() : mfOLML(0.0), mfORML(0.0), mfOLMR(0.0), mfORMR(0.0)
+ {
+ }
+
+ bool operator<( const CutSet& rOther) const
+ {
+ const double fA(mfOLML + mfORML + mfOLMR + mfORMR);
+ const double fB(rOther.mfOLML + rOther.mfORML + rOther.mfOLMR + rOther.mfORMR);
+
+ return fA < fB;
+ }
+
+ double getSum() const { return mfOLML + mfORML + mfOLMR + mfORMR; }
+ };
+
+ void getCutSet(
+ CutSet& rCutSet,
+ const basegfx::B2DPoint& rLeft,
+ const basegfx::B2DPoint& rRight,
+ const basegfx::B2DVector& rX,
+ const basegfx::B2DPoint& rOtherLeft,
+ const basegfx::B2DPoint& rOtherRight,
+ const basegfx::B2DVector& rOtherX)
+ {
+ basegfx::utils::findCut(
+ rLeft,
+ rX,
+ rOtherLeft,
+ rOtherX,
+ CutFlagValue::LINE,
+ &rCutSet.mfOLML);
+
+ basegfx::utils::findCut(
+ rRight,
+ rX,
+ rOtherLeft,
+ rOtherX,
+ CutFlagValue::LINE,
+ &rCutSet.mfOLMR);
+
+ basegfx::utils::findCut(
+ rLeft,
+ rX,
+ rOtherRight,
+ rOtherX,
+ CutFlagValue::LINE,
+ &rCutSet.mfORML);
+
+ basegfx::utils::findCut(
+ rRight,
+ rX,
+ rOtherRight,
+ rOtherX,
+ CutFlagValue::LINE,
+ &rCutSet.mfORMR);
+ }
+
+ struct ExtendSet
+ {
+ double mfExtLeft;
+ double mfExtRight;
+
+ ExtendSet() : mfExtLeft(0.0), mfExtRight(0.0) {}
+ };
+
+ void getExtends(
+ std::vector<ExtendSet>& rExtendSet, // target Left/Right values to fill
+ const basegfx::B2DPoint& rOrigin, // own vector start
+ const StyleVectorCombination& rCombination, // own vector and offsets for lines
+ const basegfx::B2DVector& rPerpendX, // normalized perpendicular to own vector
+ const std::vector< StyleVectorCombination >& rStyleVector) // other vectors emerging in this point
+ {
+ if(!(!rCombination.empty() && !rStyleVector.empty() && rCombination.size() == rExtendSet.size()))
+ return;
+
+ const size_t nOffsetA(rCombination.size());
+
+ if(1 == nOffsetA)
+ {
+ Color aMyColor; double fMyOffset(0.0); double fMyHalfWidth(0.0);
+ rCombination.getColorAndOffsetAndHalfWidth(0, aMyColor, fMyOffset, fMyHalfWidth);
+
+ if(!aMyColor.IsFullyTransparent())
+ {
+ const basegfx::B2DPoint aLeft(rOrigin + (rPerpendX * (fMyOffset - fMyHalfWidth)));
+ const basegfx::B2DPoint aRight(rOrigin + (rPerpendX * (fMyOffset + fMyHalfWidth)));
+ std::vector< CutSet > aCutSets;
+
+ for(const auto& rStyleCandidate : rStyleVector)
+ {
+ const basegfx::B2DVector aOtherPerpend(basegfx::getNormalizedPerpendicular(rStyleCandidate.getB2DVector()));
+ const size_t nOffsetB(rStyleCandidate.size());
+
+ for(size_t other(0); other < nOffsetB; other++)
+ {
+ Color aOtherColor; double fOtherOffset(0.0); double fOtherHalfWidth(0.0);
+ rStyleCandidate.getColorAndOffsetAndHalfWidth(other, aOtherColor, fOtherOffset, fOtherHalfWidth);
+
+ if(!aOtherColor.IsFullyTransparent())
+ {
+ const basegfx::B2DPoint aOtherLeft(rOrigin + (aOtherPerpend * (fOtherOffset - fOtherHalfWidth)));
+ const basegfx::B2DPoint aOtherRight(rOrigin + (aOtherPerpend * (fOtherOffset + fOtherHalfWidth)));
+
+ CutSet aNewCutSet;
+ getCutSet(aNewCutSet, aLeft, aRight, rCombination.getB2DVector(), aOtherLeft, aOtherRight, rStyleCandidate.getB2DVector());
+ aCutSets.push_back(aNewCutSet);
+ }
+ }
+ }
+
+ if(!aCutSets.empty())
+ {
+ CutSet aCutSet(aCutSets[0]);
+ const size_t nNumCutSets(aCutSets.size());
+
+ if(1 != nNumCutSets)
+ {
+ double fCutSet(aCutSet.getSum());
+
+ for(size_t a(1); a < nNumCutSets; a++)
+ {
+ const CutSet& rCandidate(aCutSets[a]);
+ const double fCandidate(rCandidate.getSum());
+
+ if(basegfx::fTools::equalZero(fCandidate - fCutSet))
+ {
+ // both have equal center point, use medium cut
+ const double fNewOLML(std::max(std::min(rCandidate.mfOLML, rCandidate.mfORML), std::min(aCutSet.mfOLML, aCutSet.mfORML)));
+ const double fNewORML(std::min(std::max(rCandidate.mfOLML, rCandidate.mfORML), std::max(aCutSet.mfOLML, aCutSet.mfORML)));
+ const double fNewOLMR(std::max(std::min(rCandidate.mfOLMR, rCandidate.mfORMR), std::min(aCutSet.mfOLMR, aCutSet.mfORMR)));
+ const double fNewORMR(std::min(std::max(rCandidate.mfOLMR, rCandidate.mfORMR), std::max(aCutSet.mfOLMR, aCutSet.mfORMR)));
+ aCutSet.mfOLML = fNewOLML;
+ aCutSet.mfORML = fNewORML;
+ aCutSet.mfOLMR = fNewOLMR;
+ aCutSet.mfORMR = fNewORMR;
+ fCutSet = aCutSet.getSum();
+ }
+ else if(fCandidate < fCutSet)
+ {
+ // get minimum
+ fCutSet = fCandidate;
+ aCutSet = rCandidate;
+ }
+ }
+ }
+
+ ExtendSet& rExt(rExtendSet[0]);
+
+ rExt.mfExtLeft = std::min(aCutSet.mfOLML, aCutSet.mfORML);
+ rExt.mfExtRight = std::min(aCutSet.mfOLMR, aCutSet.mfORMR);
+ }
+ }
+ }
+ else
+ {
+ size_t nVisEdgeUp(0);
+ size_t nVisEdgeDn(0);
+
+ for(size_t my(0); my < nOffsetA; my++)
+ {
+ Color aMyColor; double fMyOffset(0.0); double fMyHalfWidth(0.0);
+ rCombination.getColorAndOffsetAndHalfWidth(my, aMyColor, fMyOffset, fMyHalfWidth);
+
+ if(!aMyColor.IsFullyTransparent())
+ {
+ const basegfx::B2DPoint aLeft(rOrigin + (rPerpendX * (fMyOffset - fMyHalfWidth)));
+ const basegfx::B2DPoint aRight(rOrigin + (rPerpendX * (fMyOffset + fMyHalfWidth)));
+ const bool bUpper(my <= (nOffsetA >> 1));
+ const StyleVectorCombination& rStyleCandidate(bUpper ? rStyleVector.front() : rStyleVector.back());
+ const basegfx::B2DVector aOtherPerpend(basegfx::getNormalizedPerpendicular(rStyleCandidate.getB2DVector()));
+ const size_t nOffsetB(rStyleCandidate.size());
+ std::vector< CutSet > aCutSets;
+
+ for(size_t other(0); other < nOffsetB; other++)
+ {
+ Color aOtherColor; double fOtherOffset(0.0); double fOtherHalfWidth(0.0);
+ rStyleCandidate.getColorAndOffsetAndHalfWidth(other, aOtherColor, fOtherOffset, fOtherHalfWidth);
+
+ if(!aOtherColor.IsFullyTransparent())
+ {
+ const basegfx::B2DPoint aOtherLeft(rOrigin + (aOtherPerpend * (fOtherOffset - fOtherHalfWidth)));
+ const basegfx::B2DPoint aOtherRight(rOrigin + (aOtherPerpend * (fOtherOffset + fOtherHalfWidth)));
+ CutSet aCutSet;
+ getCutSet(aCutSet, aLeft, aRight, rCombination.getB2DVector(), aOtherLeft, aOtherRight, rStyleCandidate.getB2DVector());
+ aCutSets.push_back(aCutSet);
+ }
+ }
+
+ if(!aCutSets.empty())
+ {
+ // sort: min to start, max to end
+ std::sort(aCutSets.begin(), aCutSets.end());
+ const bool bOtherUpper(rStyleCandidate.getAngle() > M_PI);
+
+ // check if we need min or max
+ // bUpper bOtherUpper MinMax
+ // t t max
+ // t f min
+ // f f max
+ // f t min
+ const bool bMax(bUpper == bOtherUpper);
+ size_t nBaseIndex(0);
+ const size_t nNumCutSets(aCutSets.size());
+
+ if(bMax)
+ {
+ // access at end
+ nBaseIndex = nNumCutSets - 1 - (bUpper ? nVisEdgeUp : nVisEdgeDn);
+ }
+ else
+ {
+ // access at start
+ nBaseIndex = bUpper ? nVisEdgeUp : nVisEdgeDn;
+ }
+
+ const size_t nSecuredIndex(std::clamp(nBaseIndex, size_t(0), size_t(nNumCutSets - 1)));
+ const CutSet& rCutSet(aCutSets[nSecuredIndex]);
+ ExtendSet& rExt(rExtendSet[my]);
+
+ rExt.mfExtLeft = std::min(rCutSet.mfOLML, rCutSet.mfORML);
+ rExt.mfExtRight = std::min(rCutSet.mfOLMR, rCutSet.mfORMR);
+ }
+
+ if(bUpper)
+ {
+ nVisEdgeUp++;
+ }
+ else
+ {
+ nVisEdgeDn++;
+ }
+ }
+ }
+ }
+ }
+
+ /**
+ * Helper method to create the correct drawinglayer::primitive2d::BorderLinePrimitive2D
+ * for the given data, especially the correct drawinglayer::primitive2d::BorderLine entries
+ * including the correctly solved/created LineStartEnd extends
+ *
+ * rTarget : Here the evtl. created BorderLinePrimitive2D will be appended
+ * rOrigin : StartPoint of the Borderline
+ * rX : Vector of the Borderline
+ * rBorder : svx::frame::Style of the of the Borderline
+ * rStartStyleVectorTable : All other Borderlines which have to be taken into account because
+ * they have the same StartPoint as the current Borderline. These will be used to calculate
+ * the correct LineStartEnd extends tor the BorderLinePrimitive2D. The definition should be
+ * built up using svx::frame::StyleVectorTable and StyleVectorTable::add and includes:
+ * rStyle : the svx::frame::Style of one other BorderLine
+ * rMyVector : the Vector of the *new* to-be-defined BorderLine, identical to rX
+ * rOtherVector: the Vector of one other BorderLine (may be, but does not need to be normalized),
+ * always *pointing away* from the common StartPoint rOrigin
+ * bMirrored : define if rStyle of one other BorderLine shall be mirrored (e.g. bottom-right edges)
+ * With multiple BorderLines the definitions have to be CounterClockWise. This will be
+ * ensured by StyleVectorTable sorting the entries, but knowing this may allow more efficient
+ * data creation.
+ * rEndStyleVectorTable: All other BorderLines that have the same EndPoint. There are differences to
+ * the Start definitions:
+ * - do not forget to consequently use -rX for rMyVector
+ * - definitions have to be ClockWise for the EndBorderLines, will be ensured by sorting
+ *
+ * If you take all this into account, you will get correctly extended BorderLinePrimitive2D
+ * representations for the new to be defined BorderLine. That extensions will overlap nicely
+ * with the corresponding BorderLines and take all multiple line definitions in the ::Style into
+ * account.
+ * The internal solver is *not limited* to ::Style(s) with three parts (Left/Gap/Right), this is
+ * just due to svx::frame::Style's definitions. A new solver based on this one can be created
+ * anytime using more mulötiple borders based on the more flexible
+ * std::vector< drawinglayer::primitive2d::BorderLine > if needed.
+ */
+ void CreateBorderPrimitives(
+ drawinglayer::primitive2d::Primitive2DContainer& rTarget, /// target for created primitives
+ const basegfx::B2DPoint& rOrigin, /// start point of borderline
+ const basegfx::B2DVector& rX, /// X-Axis of borderline with length
+ const svx::frame::Style& rBorder, /// Style of borderline
+ const StyleVectorTable& rStartStyleVectorTable, /// Styles and vectors (pointing away) at borderline start, ccw
+ const StyleVectorTable& rEndStyleVectorTable, /// Styles and vectors (pointing away) at borderline end, cw
+ const Color* pForceColor, /// If specified, overrides frame border color.
+ double fMinimalDiscreteUnit) /// minimal discrete unit to use for svx::frame::Style width values
+ {
+ // get offset color pairs for style, one per visible line
+ const StyleVectorCombination aCombination(
+ rBorder,
+ rX,
+ 0.0,
+ false,
+ pForceColor,
+ fMinimalDiscreteUnit);
+
+ if(aCombination.empty())
+ return;
+
+ const basegfx::B2DVector aPerpendX(basegfx::getNormalizedPerpendicular(rX));
+ const bool bHasStartStyles(!rStartStyleVectorTable.empty());
+ const bool bHasEndStyles(!rEndStyleVectorTable.empty());
+ const size_t nOffsets(aCombination.size());
+ std::vector<ExtendSet> aExtendSetStart(nOffsets);
+ std::vector<ExtendSet> aExtendSetEnd(nOffsets);
+
+ if(bHasStartStyles)
+ {
+ // create extends for line starts, use given point/vector and offsets
+ getExtends(aExtendSetStart, rOrigin, aCombination, aPerpendX, rStartStyleVectorTable.getEntries());
+ }
+
+ if(bHasEndStyles)
+ {
+ // Create extends for line ends, create inverse point/vector and inverse offsets.
+ const StyleVectorCombination aMirroredCombination(
+ rBorder,
+ -rX,
+ 0.0,
+ true,
+ pForceColor,
+ fMinimalDiscreteUnit);
+
+ getExtends(aExtendSetEnd, rOrigin + rX, aMirroredCombination, -aPerpendX, rEndStyleVectorTable.getEntries());
+
+ // also need to inverse the result to apply to the correct lines
+ std::reverse(aExtendSetEnd.begin(), aExtendSetEnd.end());
+ }
+
+ std::vector< drawinglayer::primitive2d::BorderLine > aBorderlines;
+ const double fNegLength(-rX.getLength());
+
+ for(size_t a(0); a < nOffsets; a++)
+ {
+ Color aMyColor;
+ double fMyOffset(0.0);
+ double fMyHalfWidth(0.0);
+ aCombination.getColorAndOffsetAndHalfWidth(a, aMyColor, fMyOffset, fMyHalfWidth);
+ const ExtendSet& rExtStart(aExtendSetStart[a]);
+ const ExtendSet& rExtEnd(aExtendSetEnd[a]);
+
+ if(aMyColor.IsFullyTransparent())
+ {
+ aBorderlines.push_back(
+ drawinglayer::primitive2d::BorderLine(
+ fMyHalfWidth * 2.0));
+ }
+ else
+ {
+ aBorderlines.push_back(
+ drawinglayer::primitive2d::BorderLine(
+ drawinglayer::attribute::LineAttribute(
+ aMyColor.getBColor(),
+ fMyHalfWidth * 2.0),
+ fNegLength * rExtStart.mfExtLeft,
+ fNegLength * rExtStart.mfExtRight,
+ fNegLength * rExtEnd.mfExtRight,
+ fNegLength * rExtEnd.mfExtLeft));
+ }
+ }
+
+ static const double fPatScFact(10.0); // 10.0 multiply, see old code
+ std::vector<double> aDashing(svtools::GetLineDashing(rBorder.Type(), rBorder.PatternScale() * fPatScFact));
+ const drawinglayer::attribute::StrokeAttribute aStrokeAttribute(std::move(aDashing));
+ const basegfx::B2DPoint aStart(rOrigin + (aPerpendX * aCombination.getRefModeOffset()));
+
+ rTarget.append(
+ drawinglayer::primitive2d::Primitive2DReference(
+ new drawinglayer::primitive2d::BorderLinePrimitive2D(
+ aStart,
+ aStart + rX,
+ std::move(aBorderlines),
+ aStrokeAttribute)));
+ }
+
+ double getMinimalNonZeroValue(double fCurrent, double fNew)
+ {
+ if(0.0 != fNew)
+ {
+ if(0.0 != fCurrent)
+ {
+ fCurrent = std::min(fNew, fCurrent);
+ }
+ else
+ {
+ fCurrent = fNew;
+ }
+ }
+
+ return fCurrent;
+ }
+
+ double getMinimalNonZeroBorderWidthFromStyle(double fCurrent, const svx::frame::Style& rStyle)
+ {
+ if(rStyle.IsUsed())
+ {
+ fCurrent = getMinimalNonZeroValue(fCurrent, rStyle.Prim());
+ fCurrent = getMinimalNonZeroValue(fCurrent, rStyle.Dist());
+ fCurrent = getMinimalNonZeroValue(fCurrent, rStyle.Secn());
+ }
+
+ return fCurrent;
+ }
+}
+
+namespace drawinglayer::primitive2d
+{
+ SdrFrameBorderData::SdrConnectStyleData::SdrConnectStyleData(
+ const svx::frame::Style& rStyle,
+ const basegfx::B2DVector& rNormalizedPerpendicular,
+ bool bStyleMirrored)
+ : maStyle(rStyle),
+ maNormalizedPerpendicular(rNormalizedPerpendicular),
+ mbStyleMirrored(bStyleMirrored)
+ {
+ }
+
+ bool SdrFrameBorderData::SdrConnectStyleData::operator==(const SdrFrameBorderData::SdrConnectStyleData& rCompare) const
+ {
+ return mbStyleMirrored == rCompare.mbStyleMirrored
+ && maStyle == rCompare.maStyle
+ && maNormalizedPerpendicular == rCompare.maNormalizedPerpendicular;
+ }
+
+ SdrFrameBorderData::SdrFrameBorderData(
+ const basegfx::B2DPoint& rOrigin,
+ const basegfx::B2DVector& rX,
+ const svx::frame::Style& rStyle,
+ const Color* pForceColor)
+ : maOrigin(rOrigin),
+ maX(rX),
+ maStyle(rStyle),
+ maColor(nullptr != pForceColor ? *pForceColor : Color()),
+ mbForceColor(nullptr != pForceColor)
+ {
+ }
+
+ void SdrFrameBorderData::addSdrConnectStyleData(
+ bool bStart,
+ const svx::frame::Style& rStyle,
+ const basegfx::B2DVector& rNormalizedPerpendicular,
+ bool bStyleMirrored)
+ {
+ if(rStyle.IsUsed())
+ {
+ if(bStart)
+ {
+ maStart.emplace_back(rStyle, rNormalizedPerpendicular, bStyleMirrored);
+ }
+ else
+ {
+ maEnd.emplace_back(rStyle, rNormalizedPerpendicular, bStyleMirrored);
+ }
+ }
+ }
+
+ void SdrFrameBorderData::create2DDecomposition(
+ Primitive2DContainer& rContainer,
+ double fMinimalDiscreteUnit) const
+ {
+ StyleVectorTable aStartVector;
+ StyleVectorTable aEndVector;
+ const basegfx::B2DVector aAxis(-maX);
+
+ for(const auto& rStart : maStart)
+ {
+ aStartVector.add(
+ rStart.getStyle(),
+ maX,
+ rStart.getNormalizedPerpendicular(),
+ rStart.getStyleMirrored(),
+ fMinimalDiscreteUnit);
+ }
+
+ for(const auto& rEnd : maEnd)
+ {
+ aEndVector.add(
+ rEnd.getStyle(),
+ aAxis,
+ rEnd.getNormalizedPerpendicular(),
+ rEnd.getStyleMirrored(),
+ fMinimalDiscreteUnit);
+ }
+
+ aStartVector.sort();
+ aEndVector.sort();
+
+ CreateBorderPrimitives(
+ rContainer,
+ maOrigin,
+ maX,
+ maStyle,
+ aStartVector,
+ aEndVector,
+ mbForceColor ? &maColor : nullptr,
+ fMinimalDiscreteUnit);
+ }
+
+ double SdrFrameBorderData::getMinimalNonZeroBorderWidth() const
+ {
+ double fRetval(getMinimalNonZeroBorderWidthFromStyle(0.0, maStyle));
+
+ for(const auto& rStart : maStart)
+ {
+ fRetval = getMinimalNonZeroBorderWidthFromStyle(fRetval, rStart.getStyle());
+ }
+
+ for(const auto& rEnd : maEnd)
+ {
+ fRetval = getMinimalNonZeroBorderWidthFromStyle(fRetval, rEnd.getStyle());
+ }
+
+ return fRetval;
+ }
+
+
+ bool SdrFrameBorderData::operator==(const SdrFrameBorderData& rCompare) const
+ {
+ return maOrigin == rCompare.maOrigin
+ && maX == rCompare.maX
+ && maStyle == rCompare.maStyle
+ && maColor == rCompare.maColor
+ && mbForceColor == rCompare.mbForceColor
+ && maStart == rCompare.maStart
+ && maEnd == rCompare.maEnd;
+ }
+
+
+ void SdrFrameBorderPrimitive2D::create2DDecomposition(
+ Primitive2DContainer& rContainer,
+ const geometry::ViewInformation2D& /*aViewInformation*/) const
+ {
+ if(!getFrameBorders())
+ {
+ return;
+ }
+
+ Primitive2DContainer aRetval;
+
+ // Check and use the minimal non-zero BorderWidth for decompose
+ // if that is set and wanted
+ const double fMinimalDiscreteUnit(doForceToSingleDiscreteUnit()
+ ? mfMinimalNonZeroBorderWidthUsedForDecompose
+ : 0.0);
+
+ {
+ // decompose all buffered SdrFrameBorderData entries and try to merge them
+ // to reduce existing number of BorderLinePrimitive2D(s)
+ for(const auto& rCandidate : *getFrameBorders())
+ {
+ // get decomposition on one SdrFrameBorderData entry
+ Primitive2DContainer aPartial;
+ rCandidate.create2DDecomposition(
+ aPartial,
+ fMinimalDiscreteUnit);
+
+ for(const auto& aCandidatePartial : aPartial)
+ {
+ if(aRetval.empty())
+ {
+ // no local data yet, just add as 1st entry, done
+ aRetval.append(aCandidatePartial);
+ }
+ else
+ {
+ bool bDidMerge(false);
+
+ for(auto& aCandidateRetval : aRetval)
+ {
+ // try to merge by appending new data to existing data
+ const drawinglayer::primitive2d::Primitive2DReference aMergeRetvalPartial(
+ drawinglayer::primitive2d::tryMergeBorderLinePrimitive2D(
+ static_cast<BorderLinePrimitive2D*>(aCandidateRetval.get()),
+ static_cast<BorderLinePrimitive2D*>(aCandidatePartial.get())));
+
+ if(aMergeRetvalPartial.is())
+ {
+ // could append, replace existing data with merged data, done
+ aCandidateRetval = aMergeRetvalPartial;
+ bDidMerge = true;
+ break;
+ }
+
+ // try to merge by appending existing data to new data
+ const drawinglayer::primitive2d::Primitive2DReference aMergePartialRetval(
+ drawinglayer::primitive2d::tryMergeBorderLinePrimitive2D(
+ static_cast<BorderLinePrimitive2D*>(aCandidatePartial.get()),
+ static_cast<BorderLinePrimitive2D*>(aCandidateRetval.get())));
+
+ if(aMergePartialRetval.is())
+ {
+ // could append, replace existing data with merged data, done
+ aCandidateRetval = aMergePartialRetval;
+ bDidMerge = true;
+ break;
+ }
+ }
+
+ if(!bDidMerge)
+ {
+ // no merge after checking all existing data, append as new segment
+ aRetval.append(aCandidatePartial);
+ }
+ }
+ }
+ }
+ }
+
+ rContainer.append(std::move(aRetval));
+ }
+
+ SdrFrameBorderPrimitive2D::SdrFrameBorderPrimitive2D(
+ std::shared_ptr<SdrFrameBorderDataVector>& rFrameBorders,
+ bool bForceToSingleDiscreteUnit)
+ : maFrameBorders(std::move(rFrameBorders)),
+ mfMinimalNonZeroBorderWidth(0.0),
+ mfMinimalNonZeroBorderWidthUsedForDecompose(0.0),
+ mbForceToSingleDiscreteUnit(bForceToSingleDiscreteUnit)
+ {
+ if(getFrameBorders() && doForceToSingleDiscreteUnit())
+ {
+ // detect used minimal non-zero partial border width
+ for(const auto& rCandidate : *getFrameBorders())
+ {
+ mfMinimalNonZeroBorderWidth = getMinimalNonZeroValue(
+ mfMinimalNonZeroBorderWidth,
+ rCandidate.getMinimalNonZeroBorderWidth());
+ }
+ }
+ }
+
+ bool SdrFrameBorderPrimitive2D::operator==(const BasePrimitive2D& rPrimitive) const
+ {
+ if(BufferedDecompositionPrimitive2D::operator==(rPrimitive))
+ {
+ const SdrFrameBorderPrimitive2D& rCompare = static_cast<const SdrFrameBorderPrimitive2D&>(rPrimitive);
+
+ return (getFrameBorders() == rCompare.getFrameBorders()
+ || (getFrameBorders() && rCompare.getFrameBorders()
+ && *getFrameBorders() == *rCompare.getFrameBorders()))
+ && doForceToSingleDiscreteUnit() == rCompare.doForceToSingleDiscreteUnit();
+ }
+
+ return false;
+ }
+
+ void SdrFrameBorderPrimitive2D::get2DDecomposition(
+ Primitive2DDecompositionVisitor& rVisitor,
+ const geometry::ViewInformation2D& rViewInformation) const
+ {
+ if(doForceToSingleDiscreteUnit())
+ {
+ // Get the current DiscreteUnit, look at X and Y and use the maximum
+ const basegfx::B2DVector aDiscreteVector(rViewInformation.getInverseObjectToViewTransformation() * basegfx::B2DVector(1.0, 1.0));
+ double fDiscreteUnit(std::min(fabs(aDiscreteVector.getX()), fabs(aDiscreteVector.getY())));
+
+ if(fDiscreteUnit <= mfMinimalNonZeroBorderWidth)
+ {
+ // no need to use it, reset
+ fDiscreteUnit = 0.0;
+ }
+
+ if(fDiscreteUnit != mfMinimalNonZeroBorderWidthUsedForDecompose)
+ {
+ // conditions of last local decomposition have changed, delete
+ // possible content
+ if(!getBuffered2DDecomposition().empty())
+ {
+ const_cast< SdrFrameBorderPrimitive2D* >(this)->setBuffered2DDecomposition(Primitive2DContainer());
+ }
+
+ // remember new conditions
+ const_cast< SdrFrameBorderPrimitive2D* >(this)->mfMinimalNonZeroBorderWidthUsedForDecompose = fDiscreteUnit;
+ }
+ }
+
+ // call parent. This will call back ::create2DDecomposition above
+ // where mfMinimalNonZeroBorderWidthUsedForDecompose will be used
+ // when doForceToSingleDiscreteUnit() is true
+ BufferedDecompositionPrimitive2D::get2DDecomposition(rVisitor, rViewInformation);
+ }
+
+ // provide unique ID
+ sal_uInt32 SdrFrameBorderPrimitive2D::getPrimitive2DID() const
+ {
+ return PRIMITIVE2D_ID_SDRFRAMEBORDERTPRIMITIVE2D;
+ }
+
+} // end of namespace
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/sdr/primitive2d/sdrgrafprimitive2d.cxx b/svx/source/sdr/primitive2d/sdrgrafprimitive2d.cxx
new file mode 100644
index 000000000..f5ac21cfc
--- /dev/null
+++ b/svx/source/sdr/primitive2d/sdrgrafprimitive2d.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 <sdr/primitive2d/sdrgrafprimitive2d.hxx>
+#include <drawinglayer/primitive2d/graphicprimitive2d.hxx>
+#include <basegfx/polygon/b2dpolygontools.hxx>
+#include <sdr/primitive2d/sdrdecompositiontools.hxx>
+#include <svx/sdr/primitive2d/svx_primitivetypes2d.hxx>
+#include <basegfx/polygon/b2dpolygon.hxx>
+
+namespace drawinglayer::primitive2d
+{
+void SdrGrafPrimitive2D::create2DDecomposition(
+ Primitive2DContainer& rContainer, const geometry::ViewInformation2D& /*aViewInformation*/) const
+{
+ Primitive2DContainer aRetval;
+
+ // create unit outline polygon
+ const basegfx::B2DPolygon& aUnitOutline(basegfx::utils::createUnitPolygon());
+
+ // add fill, but only when graphic is transparent
+ if (!getSdrLFSTAttribute().getFill().isDefault() && isTransparent())
+ {
+ basegfx::B2DPolyPolygon aTransformed(aUnitOutline);
+
+ aTransformed.transform(getTransform());
+ aRetval.push_back(
+ createPolyPolygonFillPrimitive(aTransformed, getSdrLFSTAttribute().getFill(),
+ getSdrLFSTAttribute().getFillFloatTransGradient()));
+ }
+
+ // add graphic content
+ if (0 != getGraphicAttr().GetAlpha())
+ {
+ // standard graphic fill
+ const Primitive2DReference xGraphicContentPrimitive(
+ new GraphicPrimitive2D(getTransform(), getGraphicObject(), getGraphicAttr()));
+ aRetval.push_back(xGraphicContentPrimitive);
+ }
+
+ // add line
+ if (!getSdrLFSTAttribute().getLine().isDefault())
+ {
+ // if line width is given, polygon needs to be grown by half of it to make the
+ // outline to be outside of the bitmap
+ if (0.0 != getSdrLFSTAttribute().getLine().getWidth())
+ {
+ // decompose to get scale
+ basegfx::B2DVector aScale, aTranslate;
+ double fRotate, fShearX;
+ getTransform().decompose(aScale, aTranslate, fRotate, fShearX);
+
+ // create expanded range (add relative half line width to unit rectangle)
+ double fHalfLineWidth(getSdrLFSTAttribute().getLine().getWidth() * 0.5);
+ double fScaleX(0.0 != aScale.getX() ? fHalfLineWidth / fabs(aScale.getX()) : 1.0);
+ double fScaleY(0.0 != aScale.getY() ? fHalfLineWidth / fabs(aScale.getY()) : 1.0);
+ const basegfx::B2DRange aExpandedRange(-fScaleX, -fScaleY, 1.0 + fScaleX,
+ 1.0 + fScaleY);
+ basegfx::B2DPolygon aExpandedUnitOutline(
+ basegfx::utils::createPolygonFromRect(aExpandedRange));
+
+ aExpandedUnitOutline.transform(getTransform());
+ aRetval.push_back(createPolygonLinePrimitive(aExpandedUnitOutline,
+ getSdrLFSTAttribute().getLine(),
+ attribute::SdrLineStartEndAttribute()));
+ }
+ else
+ {
+ basegfx::B2DPolygon aTransformed(aUnitOutline);
+
+ aTransformed.transform(getTransform());
+ aRetval.push_back(createPolygonLinePrimitive(aTransformed,
+ getSdrLFSTAttribute().getLine(),
+ attribute::SdrLineStartEndAttribute()));
+ }
+ }
+
+ // Soft edges should be before text, since text is not affected by soft edges
+ if (!aRetval.empty() && getSdrLFSTAttribute().getSoftEdgeRadius())
+ {
+ aRetval = createEmbeddedSoftEdgePrimitive(std::move(aRetval),
+ getSdrLFSTAttribute().getSoftEdgeRadius());
+ }
+
+ // add text
+ if (!getSdrLFSTAttribute().getText().isDefault())
+ {
+ aRetval.push_back(createTextPrimitive(basegfx::B2DPolyPolygon(aUnitOutline), getTransform(),
+ getSdrLFSTAttribute().getText(),
+ getSdrLFSTAttribute().getLine(), false, false));
+ }
+
+ // tdf#132199: put glow before shadow, to have shadow of the glow, not the opposite
+ if (!aRetval.empty() && !getSdrLFSTAttribute().getGlow().isDefault())
+ {
+ // glow
+ aRetval = createEmbeddedGlowPrimitive(std::move(aRetval), getSdrLFSTAttribute().getGlow());
+ }
+
+ // add shadow
+ if (!getSdrLFSTAttribute().getShadow().isDefault())
+ {
+ aRetval = createEmbeddedShadowPrimitive(std::move(aRetval),
+ getSdrLFSTAttribute().getShadow(), getTransform());
+ }
+
+ rContainer.append(std::move(aRetval));
+}
+
+SdrGrafPrimitive2D::SdrGrafPrimitive2D(
+ const basegfx::B2DHomMatrix& rTransform,
+ const attribute::SdrLineFillEffectsTextAttribute& rSdrLFSTAttribute,
+ const GraphicObject& rGraphicObject, const GraphicAttr& rGraphicAttr)
+ : maTransform(rTransform)
+ , maSdrLFSTAttribute(rSdrLFSTAttribute)
+ , maGraphicObject(rGraphicObject)
+ , maGraphicAttr(rGraphicAttr)
+{
+ // reset some values from GraphicAttr which are part of transformation already
+ maGraphicAttr.SetRotation(0_deg10);
+}
+
+bool SdrGrafPrimitive2D::operator==(const BasePrimitive2D& rPrimitive) const
+{
+ if (BufferedDecompositionPrimitive2D::operator==(rPrimitive))
+ {
+ const SdrGrafPrimitive2D& rCompare = static_cast<const SdrGrafPrimitive2D&>(rPrimitive);
+
+ return (getTransform() == rCompare.getTransform()
+ && getSdrLFSTAttribute() == rCompare.getSdrLFSTAttribute()
+ && getGraphicObject() == rCompare.getGraphicObject()
+ && getGraphicAttr() == rCompare.getGraphicAttr());
+ }
+
+ return false;
+}
+
+bool SdrGrafPrimitive2D::isTransparent() const
+{
+ return ((255 != getGraphicAttr().GetAlpha()) || (getGraphicObject().IsTransparent()));
+}
+
+// provide unique ID
+sal_uInt32 SdrGrafPrimitive2D::getPrimitive2DID() const
+{
+ return PRIMITIVE2D_ID_SDRGRAFPRIMITIVE2D;
+}
+
+} // end of namespace
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/sdr/primitive2d/sdrmeasureprimitive2d.cxx b/svx/source/sdr/primitive2d/sdrmeasureprimitive2d.cxx
new file mode 100644
index 000000000..aada2aee0
--- /dev/null
+++ b/svx/source/sdr/primitive2d/sdrmeasureprimitive2d.cxx
@@ -0,0 +1,498 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <sdr/primitive2d/sdrmeasureprimitive2d.hxx>
+#include <sdr/primitive2d/sdrdecompositiontools.hxx>
+#include <basegfx/matrix/b2dhommatrix.hxx>
+#include <sdr/primitive2d/sdrtextprimitive2d.hxx>
+#include <sdr/attribute/sdrtextattribute.hxx>
+#include <basegfx/polygon/b2dpolypolygontools.hxx>
+#include <rtl/ref.hxx>
+#include <svx/sdr/primitive2d/svx_primitivetypes2d.hxx>
+#include <basegfx/matrix/b2dhommatrixtools.hxx>
+#include <drawinglayer/primitive2d/hiddengeometryprimitive2d.hxx>
+#include <osl/diagnose.h>
+
+
+using namespace com::sun::star;
+
+
+namespace drawinglayer::primitive2d
+{
+ Primitive2DReference SdrMeasurePrimitive2D::impCreatePart(
+ const attribute::SdrLineAttribute& rLineAttribute,
+ const basegfx::B2DHomMatrix& rObjectMatrix,
+ const basegfx::B2DPoint& rStart,
+ const basegfx::B2DPoint& rEnd,
+ bool bLeftActive,
+ bool bRightActive) const
+ {
+ const attribute::SdrLineStartEndAttribute& rLineStartEnd = getSdrLSTAttribute().getLineStartEnd();
+ basegfx::B2DPolygon aPolygon;
+
+ aPolygon.append(rStart);
+ aPolygon.append(rEnd);
+ aPolygon.transform(rObjectMatrix);
+
+ if(rLineStartEnd.isDefault() || (!bLeftActive && !bRightActive))
+ {
+ return createPolygonLinePrimitive(
+ aPolygon,
+ rLineAttribute,
+ attribute::SdrLineStartEndAttribute());
+ }
+
+ if(bLeftActive && bRightActive)
+ {
+ return createPolygonLinePrimitive(
+ aPolygon,
+ rLineAttribute,
+ rLineStartEnd);
+ }
+
+ const basegfx::B2DPolyPolygon aEmpty;
+ const attribute::SdrLineStartEndAttribute aLineStartEnd(
+ bLeftActive ? rLineStartEnd.getStartPolyPolygon() : aEmpty, bRightActive ? rLineStartEnd.getEndPolyPolygon() : aEmpty,
+ bLeftActive ? rLineStartEnd.getStartWidth() : 0.0, bRightActive ? rLineStartEnd.getEndWidth() : 0.0,
+ bLeftActive && rLineStartEnd.isStartActive(), bRightActive && rLineStartEnd.isEndActive(),
+ bLeftActive && rLineStartEnd.isStartCentered(), bRightActive && rLineStartEnd.isEndCentered());
+
+ return createPolygonLinePrimitive(
+ aPolygon,
+ rLineAttribute,
+ aLineStartEnd);
+ }
+
+ void SdrMeasurePrimitive2D::create2DDecomposition(Primitive2DContainer& rContainer, const geometry::ViewInformation2D& aViewInformation) const
+ {
+ Primitive2DContainer aRetval;
+ rtl::Reference<SdrBlockTextPrimitive2D> xBlockText;
+ basegfx::B2DRange aTextRange;
+ const basegfx::B2DVector aLine(getEnd() - getStart());
+ const double fDistance(aLine.getLength());
+ const double fAngle(atan2(aLine.getY(), aLine.getX()));
+ bool bAutoUpsideDown(false);
+ const attribute::SdrTextAttribute rTextAttribute = getSdrLSTAttribute().getText();
+ const basegfx::B2DHomMatrix aObjectMatrix(
+ basegfx::utils::createShearXRotateTranslateB2DHomMatrix(0.0, fAngle, getStart()));
+
+ // prepare text, but do not add yet; it needs to be aligned to
+ // the line geometry
+ if(!rTextAttribute.isDefault())
+ {
+ basegfx::B2DHomMatrix aTextMatrix;
+ double fTestAngle(fAngle);
+
+ if(getTextRotation())
+ {
+ aTextMatrix.rotate(-M_PI_2);
+ fTestAngle -= (M_PI_2);
+
+ if(getTextAutoAngle() && fTestAngle < -M_PI)
+ {
+ fTestAngle += 2 * M_PI;
+ }
+ }
+
+ if(getTextAutoAngle())
+ {
+ if(fTestAngle > (M_PI / 4.0) || fTestAngle < (-M_PI * (3.0 / 4.0)))
+ {
+ bAutoUpsideDown = true;
+ }
+ }
+
+ // create primitive and get text range
+ xBlockText = new SdrBlockTextPrimitive2D(
+ &rTextAttribute.getSdrText(),
+ rTextAttribute.getOutlinerParaObject(),
+ aTextMatrix,
+ SDRTEXTHORZADJUST_CENTER,
+ SDRTEXTVERTADJUST_CENTER,
+ rTextAttribute.isScroll(),
+ false,
+ false,
+ false);
+
+ aTextRange = xBlockText->getB2DRange(aViewInformation);
+ }
+
+ // prepare line attribute and result
+ double fTextX;
+ double fTextY;
+ {
+ const attribute::SdrLineAttribute rLineAttribute(getSdrLSTAttribute().getLine());
+ bool bArrowsOutside(false);
+ bool bMainLineSplitted(false);
+ const attribute::SdrLineStartEndAttribute& rLineStartEnd = getSdrLSTAttribute().getLineStartEnd();
+ double fStartArrowW(0.0);
+ double fStartArrowH(0.0);
+ double fEndArrowW(0.0);
+ double fEndArrowH(0.0);
+
+ if(!rLineStartEnd.isDefault())
+ {
+ if(rLineStartEnd.isStartActive())
+ {
+ const basegfx::B2DRange aArrowRange(basegfx::utils::getRange(rLineStartEnd.getStartPolyPolygon()));
+ fStartArrowW = rLineStartEnd.getStartWidth();
+ fStartArrowH = aArrowRange.getHeight() * fStartArrowW / aArrowRange.getWidth();
+
+ if(rLineStartEnd.isStartCentered())
+ {
+ fStartArrowH *= 0.5;
+ }
+ }
+
+ if(rLineStartEnd.isEndActive())
+ {
+ const basegfx::B2DRange aArrowRange(basegfx::utils::getRange(rLineStartEnd.getEndPolyPolygon()));
+ fEndArrowW = rLineStartEnd.getEndWidth();
+ fEndArrowH = aArrowRange.getHeight() * fEndArrowW / aArrowRange.getWidth();
+
+ if(rLineStartEnd.isEndCentered())
+ {
+ fEndArrowH *= 0.5;
+ }
+ }
+ }
+
+ const double fSpaceNeededByArrows(fStartArrowH + fEndArrowH + ((fStartArrowW + fEndArrowW) * 0.5));
+ const double fArrowsOutsideLen((fStartArrowH + fEndArrowH + fStartArrowW + fEndArrowW) * 0.5);
+ const double fHalfLineWidth(rLineAttribute.getWidth() * 0.5);
+
+ if(fSpaceNeededByArrows > fDistance)
+ {
+ bArrowsOutside = true;
+ }
+
+ MeasureTextPosition eHorizontal(getHorizontal());
+ MeasureTextPosition eVertical(getVertical());
+
+ if(MEASURETEXTPOSITION_AUTOMATIC == eVertical)
+ {
+ eVertical = MEASURETEXTPOSITION_NEGATIVE;
+ }
+
+ if(MEASURETEXTPOSITION_CENTERED == eVertical)
+ {
+ bMainLineSplitted = true;
+ }
+
+ if(MEASURETEXTPOSITION_AUTOMATIC == eHorizontal)
+ {
+ if(aTextRange.getWidth() > fDistance)
+ {
+ eHorizontal = MEASURETEXTPOSITION_NEGATIVE;
+ }
+ else
+ {
+ eHorizontal = MEASURETEXTPOSITION_CENTERED;
+ }
+
+ if(bMainLineSplitted)
+ {
+ if(aTextRange.getWidth() + fSpaceNeededByArrows > fDistance)
+ {
+ bArrowsOutside = true;
+ }
+ }
+ else
+ {
+ const double fSmallArrowNeed(fStartArrowH + fEndArrowH + ((fStartArrowW + fEndArrowW) * 0.125));
+
+ if(aTextRange.getWidth() + fSmallArrowNeed > fDistance)
+ {
+ bArrowsOutside = true;
+ }
+ }
+ }
+
+ if(MEASURETEXTPOSITION_CENTERED != eHorizontal)
+ {
+ bArrowsOutside = true;
+ }
+
+ // switch text above/below?
+ if(getBelow() || (bAutoUpsideDown && !getTextRotation()))
+ {
+ if(MEASURETEXTPOSITION_NEGATIVE == eVertical)
+ {
+ eVertical = MEASURETEXTPOSITION_POSITIVE;
+ }
+ else if(MEASURETEXTPOSITION_POSITIVE == eVertical)
+ {
+ eVertical = MEASURETEXTPOSITION_NEGATIVE;
+ }
+ }
+
+ const double fMainLineOffset(getBelow() ? getDistance() : -getDistance());
+ const basegfx::B2DPoint aMainLeft(0.0, fMainLineOffset);
+ const basegfx::B2DPoint aMainRight(fDistance, fMainLineOffset);
+
+ // main line
+ if(bArrowsOutside)
+ {
+ double fLenLeft(fArrowsOutsideLen);
+ double fLenRight(fArrowsOutsideLen);
+
+ if(!bMainLineSplitted)
+ {
+ if(MEASURETEXTPOSITION_NEGATIVE == eHorizontal)
+ {
+ fLenLeft = fStartArrowH + aTextRange.getWidth();
+ }
+ else if(MEASURETEXTPOSITION_POSITIVE == eHorizontal)
+ {
+ fLenRight = fEndArrowH + aTextRange.getWidth();
+ }
+ }
+
+ const basegfx::B2DPoint aMainLeftLeft(aMainLeft.getX() - fLenLeft, aMainLeft.getY());
+ const basegfx::B2DPoint aMainRightRight(aMainRight.getX() + fLenRight, aMainRight.getY());
+
+ aRetval.push_back(impCreatePart(rLineAttribute, aObjectMatrix, aMainLeftLeft, aMainLeft, false, true));
+ aRetval.push_back(impCreatePart(rLineAttribute, aObjectMatrix, aMainRight, aMainRightRight, true, false));
+
+ if(!bMainLineSplitted || MEASURETEXTPOSITION_CENTERED != eHorizontal)
+ {
+ aRetval.push_back(impCreatePart(rLineAttribute, aObjectMatrix, aMainLeft, aMainRight, false, false));
+ }
+ }
+ else
+ {
+ if(bMainLineSplitted)
+ {
+ const double fHalfLength((fDistance - (aTextRange.getWidth() + (fStartArrowH + fEndArrowH) * 0.25)) * 0.5);
+ const basegfx::B2DPoint aMainInnerLeft(aMainLeft.getX() + fHalfLength, aMainLeft.getY());
+ const basegfx::B2DPoint aMainInnerRight(aMainRight.getX() - fHalfLength, aMainRight.getY());
+
+ aRetval.push_back(impCreatePart(rLineAttribute, aObjectMatrix, aMainLeft, aMainInnerLeft, true, false));
+ aRetval.push_back(impCreatePart(rLineAttribute, aObjectMatrix, aMainInnerRight, aMainRight, false, true));
+ }
+ else
+ {
+ aRetval.push_back(impCreatePart(rLineAttribute, aObjectMatrix, aMainLeft, aMainRight, true, true));
+ }
+ }
+
+ // left/right help line value preparation
+ const double fTopEdge(getBelow() ? getUpper() + getDistance() : -getUpper() - getDistance());
+ const double fBottomLeft(getBelow() ? getLower() - getLeftDelta() : getLeftDelta() - getLower());
+ const double fBottomRight(getBelow() ? getLower() - getRightDelta() : getRightDelta() - getLower());
+
+ // left help line
+ const basegfx::B2DPoint aLeftUp(0.0, fTopEdge);
+ const basegfx::B2DPoint aLeftDown(0.0, fBottomLeft);
+
+ aRetval.push_back(impCreatePart(rLineAttribute, aObjectMatrix, aLeftDown, aLeftUp, false, false));
+
+ // right help line
+ const basegfx::B2DPoint aRightUp(fDistance, fTopEdge);
+ const basegfx::B2DPoint aRightDown(fDistance, fBottomRight);
+
+ aRetval.push_back(impCreatePart(rLineAttribute, aObjectMatrix, aRightDown, aRightUp, false, false));
+
+ // text horizontal position
+ if(MEASURETEXTPOSITION_NEGATIVE == eHorizontal)
+ {
+ // left
+ const double fSmall(fArrowsOutsideLen * 0.18);
+ fTextX = aMainLeft.getX() - (fStartArrowH + aTextRange.getWidth() + fSmall + fHalfLineWidth);
+
+ if(bMainLineSplitted)
+ {
+ fTextX -= (fArrowsOutsideLen - fStartArrowH);
+ }
+
+ if(!rTextAttribute.isDefault())
+ {
+ fTextX -= rTextAttribute.getTextRightDistance();
+ }
+ }
+ else if(MEASURETEXTPOSITION_POSITIVE == eHorizontal)
+ {
+ // right
+ const double fSmall(fArrowsOutsideLen * 0.18);
+ fTextX = aMainRight.getX() + (fEndArrowH + fSmall + fHalfLineWidth);
+
+ if(bMainLineSplitted)
+ {
+ fTextX += (fArrowsOutsideLen - fEndArrowH);
+ }
+
+ if(!rTextAttribute.isDefault())
+ {
+ fTextX += rTextAttribute.getTextLeftDistance();
+ }
+ }
+ else // MEASURETEXTPOSITION_CENTERED
+ {
+ // centered
+ fTextX = aMainLeft.getX() + ((fDistance - aTextRange.getWidth()) * 0.5);
+
+ if(!rTextAttribute.isDefault())
+ {
+ fTextX += (rTextAttribute.getTextLeftDistance() - rTextAttribute.getTextRightDistance()) / 2L;
+ }
+ }
+
+ // text vertical position
+ if(MEASURETEXTPOSITION_NEGATIVE == eVertical)
+ {
+ // top
+ const double fSmall(fArrowsOutsideLen * 0.10);
+ fTextY = aMainLeft.getY() - (aTextRange.getHeight() + fSmall + fHalfLineWidth);
+
+ if(!rTextAttribute.isDefault())
+ {
+ fTextY -= rTextAttribute.getTextLowerDistance();
+ }
+ }
+ else if(MEASURETEXTPOSITION_POSITIVE == eVertical)
+ {
+ // bottom
+ const double fSmall(fArrowsOutsideLen * 0.10);
+ fTextY = aMainLeft.getY() + (fSmall + fHalfLineWidth);
+
+ if(!rTextAttribute.isDefault())
+ {
+ fTextY += rTextAttribute.getTextUpperDistance();
+ }
+ }
+ else // MEASURETEXTPOSITION_CENTERED
+ {
+ // centered
+ fTextY = aMainLeft.getY() - (aTextRange.getHeight() * 0.5);
+
+ if(!rTextAttribute.isDefault())
+ {
+ fTextY += (rTextAttribute.getTextUpperDistance() - rTextAttribute.getTextLowerDistance()) / 2L;
+ }
+ }
+ }
+
+ if(getSdrLSTAttribute().getLine().isDefault())
+ {
+ // embed line geometry to invisible (100% transparent) line group for HitTest
+ Primitive2DReference xHiddenLines(new HiddenGeometryPrimitive2D(std::move(aRetval)));
+
+ aRetval = Primitive2DContainer { xHiddenLines };
+ }
+
+ if(xBlockText.is())
+ {
+ // create transformation to text primitive end position
+ basegfx::B2DHomMatrix aChange;
+
+ // handle auto text rotation
+ if(bAutoUpsideDown)
+ {
+ aChange.rotate(M_PI);
+ }
+
+ // move from aTextRange.TopLeft to fTextX, fTextY
+ aChange.translate(fTextX - aTextRange.getMinX(), fTextY - aTextRange.getMinY());
+
+ // apply object matrix
+ aChange *= aObjectMatrix;
+
+ // apply to existing text primitive
+ rtl::Reference<SdrTextPrimitive2D> pNewBlockText = xBlockText->createTransformedClone(aChange);
+ OSL_ENSURE(pNewBlockText, "SdrMeasurePrimitive2D::create2DDecomposition: Could not create transformed clone of text primitive (!)");
+ xBlockText.clear();
+
+ // add to local primitives
+ aRetval.push_back(pNewBlockText);
+ }
+
+ // add shadow
+ if(!getSdrLSTAttribute().getShadow().isDefault())
+ {
+ aRetval = createEmbeddedShadowPrimitive(
+ std::move(aRetval),
+ getSdrLSTAttribute().getShadow());
+ }
+
+ rContainer.append(std::move(aRetval));
+ }
+
+ SdrMeasurePrimitive2D::SdrMeasurePrimitive2D(
+ const attribute::SdrLineEffectsTextAttribute& rSdrLSTAttribute,
+ const basegfx::B2DPoint& rStart,
+ const basegfx::B2DPoint& rEnd,
+ MeasureTextPosition eHorizontal,
+ MeasureTextPosition eVertical,
+ double fDistance,
+ double fUpper,
+ double fLower,
+ double fLeftDelta,
+ double fRightDelta,
+ bool bBelow,
+ bool bTextRotation,
+ bool bTextAutoAngle)
+ : maSdrLSTAttribute(rSdrLSTAttribute),
+ maStart(rStart),
+ maEnd(rEnd),
+ meHorizontal(eHorizontal),
+ meVertical(eVertical),
+ mfDistance(fDistance),
+ mfUpper(fUpper),
+ mfLower(fLower),
+ mfLeftDelta(fLeftDelta),
+ mfRightDelta(fRightDelta),
+ mbBelow(bBelow),
+ mbTextRotation(bTextRotation),
+ mbTextAutoAngle(bTextAutoAngle)
+ {
+ }
+
+ bool SdrMeasurePrimitive2D::operator==(const BasePrimitive2D& rPrimitive) const
+ {
+ if(BufferedDecompositionPrimitive2D::operator==(rPrimitive))
+ {
+ const SdrMeasurePrimitive2D& rCompare = static_cast<const SdrMeasurePrimitive2D&>(rPrimitive);
+
+ return (getStart() == rCompare.getStart()
+ && getEnd() == rCompare.getEnd()
+ && getHorizontal() == rCompare.getHorizontal()
+ && getVertical() == rCompare.getVertical()
+ && getDistance() == rCompare.getDistance()
+ && getUpper() == rCompare.getUpper()
+ && getLower() == rCompare.getLower()
+ && getLeftDelta() == rCompare.getLeftDelta()
+ && getRightDelta() == rCompare.getRightDelta()
+ && getBelow() == rCompare.getBelow()
+ && getTextRotation() == rCompare.getTextRotation()
+ && getTextAutoAngle() == rCompare.getTextAutoAngle()
+ && getSdrLSTAttribute() == rCompare.getSdrLSTAttribute());
+ }
+
+ return false;
+ }
+
+ // provide unique ID
+ sal_uInt32 SdrMeasurePrimitive2D::getPrimitive2DID() const
+ {
+ return PRIMITIVE2D_ID_SDRMEASUREPRIMITIVE2D;
+ }
+
+} // end of namespace
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/sdr/primitive2d/sdrole2primitive2d.cxx b/svx/source/sdr/primitive2d/sdrole2primitive2d.cxx
new file mode 100644
index 000000000..e7576b498
--- /dev/null
+++ b/svx/source/sdr/primitive2d/sdrole2primitive2d.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 <sdr/primitive2d/sdrole2primitive2d.hxx>
+#include <svx/sdr/primitive2d/svx_primitivetypes2d.hxx>
+#include <basegfx/polygon/b2dpolygontools.hxx>
+#include <sdr/primitive2d/sdrdecompositiontools.hxx>
+#include <drawinglayer/primitive2d/sdrdecompositiontools2d.hxx>
+#include <basegfx/polygon/b2dpolygon.hxx>
+#include <basegfx/polygon/b2dpolypolygon.hxx>
+
+
+using namespace com::sun::star;
+
+
+namespace drawinglayer::primitive2d
+{
+ SdrOle2Primitive2D::SdrOle2Primitive2D(
+ Primitive2DContainer&& rOLEContent,
+ const basegfx::B2DHomMatrix& rTransform,
+ const attribute::SdrLineFillEffectsTextAttribute& rSdrLFSTAttribute)
+ : maOLEContent(std::move(rOLEContent)),
+ maTransform(rTransform),
+ maSdrLFSTAttribute(rSdrLFSTAttribute)
+ {
+ }
+
+ bool SdrOle2Primitive2D::operator==(const BasePrimitive2D& rPrimitive) const
+ {
+ if(BasePrimitive2D::operator==(rPrimitive))
+ {
+ const SdrOle2Primitive2D& rCompare = static_cast<const SdrOle2Primitive2D&>(rPrimitive);
+
+ // #i108636# The standard operator== on two UNO sequences did not work as i
+ // would have expected; it just checks the .is() states and the data type
+ // of the sequence. What i need here is detection of equality of the whole
+ // sequence content, thus i need to use the arePrimitive2DSequencesEqual helper
+ // here instead of the operator== which lead to always returning false and thus
+ // always re-decompositions of the subcontent.
+ if(getOLEContent() == rCompare.getOLEContent()
+ && getTransform() == rCompare.getTransform()
+ && getSdrLFSTAttribute() == rCompare.getSdrLFSTAttribute())
+ {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ void SdrOle2Primitive2D::get2DDecomposition(Primitive2DDecompositionVisitor& rVisitor, const geometry::ViewInformation2D& /*aViewInformation*/) const
+ {
+ // to take care of getSdrLFSTAttribute() later, the same as in SdrGrafPrimitive2D::create2DDecomposition
+ // should happen. For the moment we only need the OLE itself
+ // Added complete primitive preparation using getSdrLFSTAttribute() now.
+ Primitive2DContainer aRetval;
+
+ // create unit outline polygon
+ const basegfx::B2DPolygon& aUnitOutline(basegfx::utils::createUnitPolygon());
+
+ // add fill
+ if(!getSdrLFSTAttribute().getFill().isDefault())
+ {
+ basegfx::B2DPolyPolygon aTransformed(aUnitOutline);
+
+ aTransformed.transform(getTransform());
+ aRetval.push_back(
+ createPolyPolygonFillPrimitive(
+ aTransformed,
+ getSdrLFSTAttribute().getFill(),
+ getSdrLFSTAttribute().getFillFloatTransGradient()));
+ }
+
+ // add line
+ // #i97981# condition was inverse to purpose. When being compatible to paint version,
+ // border needs to be suppressed
+ if(!getSdrLFSTAttribute().getLine().isDefault())
+ {
+ // if line width is given, polygon needs to be grown by half of it to make the
+ // outline to be outside of the bitmap
+ if(0.0 != getSdrLFSTAttribute().getLine().getWidth())
+ {
+ // decompose to get scale
+ basegfx::B2DVector aScale, aTranslate;
+ double fRotate, fShearX;
+ getTransform().decompose(aScale, aTranslate, fRotate, fShearX);
+
+ // create expanded range (add relative half line width to unit rectangle)
+ double fHalfLineWidth(getSdrLFSTAttribute().getLine().getWidth() * 0.5);
+ double fScaleX(0.0 != aScale.getX() ? fHalfLineWidth / fabs(aScale.getX()) : 1.0);
+ double fScaleY(0.0 != aScale.getY() ? fHalfLineWidth / fabs(aScale.getY()) : 1.0);
+ const basegfx::B2DRange aExpandedRange(-fScaleX, -fScaleY, 1.0 + fScaleX, 1.0 + fScaleY);
+ basegfx::B2DPolygon aExpandedUnitOutline(basegfx::utils::createPolygonFromRect(aExpandedRange));
+
+ aExpandedUnitOutline.transform(getTransform());
+ aRetval.push_back(
+ createPolygonLinePrimitive(
+ aExpandedUnitOutline,
+ getSdrLFSTAttribute().getLine(),
+ attribute::SdrLineStartEndAttribute()));
+ }
+ else
+ {
+ basegfx::B2DPolygon aTransformed(aUnitOutline);
+
+ aTransformed.transform(getTransform());
+ aRetval.push_back(
+ createPolygonLinePrimitive(
+ aTransformed,
+ getSdrLFSTAttribute().getLine(),
+ attribute::SdrLineStartEndAttribute()));
+ }
+ }
+ else
+ {
+ // if initially no line is defined, create one for HitTest and BoundRect
+ aRetval.push_back(
+ createHiddenGeometryPrimitives2D(
+ false,
+ basegfx::B2DPolyPolygon(aUnitOutline),
+ getTransform()));
+ }
+
+ // add graphic content
+ aRetval.append(getOLEContent());
+
+ // add text, no need to suppress to stay compatible since text was
+ // always supported by the old paints, too
+ if(!getSdrLFSTAttribute().getText().isDefault())
+ {
+ aRetval.push_back(
+ createTextPrimitive(
+ basegfx::B2DPolyPolygon(aUnitOutline),
+ getTransform(),
+ getSdrLFSTAttribute().getText(),
+ getSdrLFSTAttribute().getLine(),
+ false,
+ false));
+ }
+
+ // add shadow
+ if(!getSdrLFSTAttribute().getShadow().isDefault())
+ {
+ aRetval = createEmbeddedShadowPrimitive(
+ std::move(aRetval),
+ getSdrLFSTAttribute().getShadow());
+ }
+
+ rVisitor.visit(std::move(aRetval));
+ }
+
+ // provide unique ID
+ sal_uInt32 SdrOle2Primitive2D::getPrimitive2DID() const
+ {
+ return PRIMITIVE2D_ID_SDROLE2PRIMITIVE2D;
+ }
+
+} // end of namespace
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/sdr/primitive2d/sdrolecontentprimitive2d.cxx b/svx/source/sdr/primitive2d/sdrolecontentprimitive2d.cxx
new file mode 100644
index 000000000..7edd0e601
--- /dev/null
+++ b/svx/source/sdr/primitive2d/sdrolecontentprimitive2d.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 <sdr/primitive2d/sdrolecontentprimitive2d.hxx>
+#include <svx/sdr/primitive2d/svx_primitivetypes2d.hxx>
+#include <svx/svdoole2.hxx>
+#include <vcl/svapp.hxx>
+#include <drawinglayer/primitive2d/graphicprimitive2d.hxx>
+#include <drawinglayer/primitive2d/PolygonHairlinePrimitive2D.hxx>
+#include <svtools/colorcfg.hxx>
+#include <basegfx/polygon/b2dpolygontools.hxx>
+#include <basegfx/polygon/b2dpolygon.hxx>
+#include <basegfx/matrix/b2dhommatrixtools.hxx>
+
+
+namespace drawinglayer::primitive2d
+{
+ void SdrOleContentPrimitive2D::create2DDecomposition(Primitive2DContainer& rContainer, const geometry::ViewInformation2D& /*aViewInformation*/) const
+ {
+ const SdrOle2Obj* pSource = mpSdrOle2Obj.get();
+ bool bScaleContent(false);
+ Graphic aGraphic;
+
+ if(pSource)
+ {
+ const Graphic* pOLEGraphic = pSource->GetGraphic();
+
+ if(pOLEGraphic)
+ {
+ aGraphic = *pOLEGraphic;
+ bScaleContent = pSource->IsEmptyPresObj();
+ }
+ }
+#ifdef _WIN32 // Little point in displaying the "broken OLE" graphic on OSes that don't have real OLE, maybe?
+ if(GraphicType::NONE == aGraphic.GetType())
+ {
+ // no source, use fallback resource empty OLE graphic
+ aGraphic = SdrOle2Obj::GetEmptyOLEReplacementGraphic();
+ bScaleContent = true;
+ }
+#endif
+ if(GraphicType::NONE == aGraphic.GetType())
+ return;
+
+ const GraphicObject aGraphicObject(aGraphic);
+ const GraphicAttr aGraphicAttr;
+
+ if(bScaleContent)
+ {
+ // get transformation atoms
+ basegfx::B2DVector aScale, aTranslate;
+ double fRotate, fShearX;
+ getObjectTransform().decompose(aScale, aTranslate, fRotate, fShearX);
+
+ // get PrefSize from the graphic in 100th mm
+ Size aPrefSize(aGraphic.GetPrefSize());
+
+ if(MapUnit::MapPixel == aGraphic.GetPrefMapMode().GetMapUnit())
+ {
+ aPrefSize = Application::GetDefaultDevice()->PixelToLogic(aPrefSize, MapMode(MapUnit::Map100thMM));
+ }
+ else
+ {
+ aPrefSize = OutputDevice::LogicToLogic(aPrefSize, aGraphic.GetPrefMapMode(), MapMode(MapUnit::Map100thMM));
+ }
+
+ const double fOffsetX((aScale.getX() - aPrefSize.getWidth()) / 2.0);
+ const double fOffsetY((aScale.getY() - aPrefSize.getHeight()) / 2.0);
+
+ if(basegfx::fTools::moreOrEqual(fOffsetX, 0.0) && basegfx::fTools::moreOrEqual(fOffsetY, 0.0))
+ {
+ // if content fits into frame, create it
+ basegfx::B2DHomMatrix aInnerObjectMatrix(basegfx::utils::createScaleTranslateB2DHomMatrix(
+ aPrefSize.getWidth(), aPrefSize.getHeight(), fOffsetX, fOffsetY));
+ aInnerObjectMatrix = basegfx::utils::createShearXRotateTranslateB2DHomMatrix(fShearX, fRotate, aTranslate)
+ * aInnerObjectMatrix;
+
+ const drawinglayer::primitive2d::Primitive2DReference aGraphicPrimitive(
+ new drawinglayer::primitive2d::GraphicPrimitive2D(
+ aInnerObjectMatrix,
+ aGraphicObject,
+ aGraphicAttr));
+ rContainer.push_back(aGraphicPrimitive);
+ }
+ }
+ else
+ {
+ // create graphic primitive for content
+ const drawinglayer::primitive2d::Primitive2DReference aGraphicPrimitive(
+ new drawinglayer::primitive2d::GraphicPrimitive2D(
+ getObjectTransform(),
+ aGraphicObject,
+ aGraphicAttr));
+ rContainer.push_back(aGraphicPrimitive);
+ }
+
+ // a standard gray outline is created for scaled content
+ if(!bScaleContent)
+ return;
+
+ const svtools::ColorConfig aColorConfig;
+ const svtools::ColorConfigValue aColor(aColorConfig.GetColorValue(svtools::OBJECTBOUNDARIES));
+
+ if(aColor.bIsVisible)
+ {
+ basegfx::B2DPolygon aOutline(basegfx::utils::createUnitPolygon());
+ const Color aVclColor(aColor.nColor);
+ aOutline.transform(getObjectTransform());
+ const drawinglayer::primitive2d::Primitive2DReference xOutline(
+ new drawinglayer::primitive2d::PolygonHairlinePrimitive2D(aOutline, aVclColor.getBColor()));
+ rContainer.push_back(xOutline);
+ }
+ }
+
+ SdrOleContentPrimitive2D::SdrOleContentPrimitive2D(
+ const SdrOle2Obj& rSdrOle2Obj,
+ const basegfx::B2DHomMatrix& rObjectTransform,
+ sal_uInt32 nGraphicVersion
+ )
+ : mpSdrOle2Obj(const_cast< SdrOle2Obj* >(&rSdrOle2Obj)),
+ maObjectTransform(rObjectTransform),
+ mnGraphicVersion(nGraphicVersion)
+ {
+ }
+
+ bool SdrOleContentPrimitive2D::operator==(const BasePrimitive2D& rPrimitive) const
+ {
+ if( BufferedDecompositionPrimitive2D::operator==(rPrimitive) )
+ {
+ const SdrOleContentPrimitive2D& rCompare = static_cast<const SdrOleContentPrimitive2D&>(rPrimitive);
+ const bool bBothNot(!mpSdrOle2Obj.is() && !rCompare.mpSdrOle2Obj.is());
+ const bool bBothAndEqual(mpSdrOle2Obj.is() && rCompare.mpSdrOle2Obj.is()
+ && mpSdrOle2Obj.get() == rCompare.mpSdrOle2Obj.get());
+
+ return ((bBothNot || bBothAndEqual)
+ && getObjectTransform() == rCompare.getObjectTransform()
+
+ // #i104867# to find out if the Graphic content of the
+ // OLE has changed, use GraphicVersion number
+ && mnGraphicVersion == rCompare.mnGraphicVersion
+ );
+ }
+
+ return false;
+ }
+
+ basegfx::B2DRange SdrOleContentPrimitive2D::getB2DRange(const geometry::ViewInformation2D& /*rViewInformation*/) const
+ {
+ basegfx::B2DRange aRange(0.0, 0.0, 1.0, 1.0);
+ aRange.transform(getObjectTransform());
+
+ return aRange;
+ }
+
+ // provide unique ID
+ sal_uInt32 SdrOleContentPrimitive2D::getPrimitive2DID() const
+ {
+ return PRIMITIVE2D_ID_SDROLECONTENTPRIMITIVE2D;
+ }
+
+} // end of namespace
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/sdr/primitive2d/sdrpathprimitive2d.cxx b/svx/source/sdr/primitive2d/sdrpathprimitive2d.cxx
new file mode 100644
index 000000000..829525e2b
--- /dev/null
+++ b/svx/source/sdr/primitive2d/sdrpathprimitive2d.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 <sdr/primitive2d/sdrpathprimitive2d.hxx>
+#include <sdr/primitive2d/sdrdecompositiontools.hxx>
+#include <svx/sdr/primitive2d/svx_primitivetypes2d.hxx>
+#include <drawinglayer/primitive2d/sdrdecompositiontools2d.hxx>
+
+
+using namespace com::sun::star;
+
+
+namespace drawinglayer::primitive2d
+{
+ void SdrPathPrimitive2D::create2DDecomposition(Primitive2DContainer& rContainer, const geometry::ViewInformation2D& /*aViewInformation*/) const
+ {
+ Primitive2DContainer aRetval;
+
+ // add fill
+ if(!getSdrLFSTAttribute().getFill().isDefault()
+ && getUnitPolyPolygon().isClosed())
+ {
+ // #i108255# no need to use correctOrientations here; target is
+ // straight visualisation
+ basegfx::B2DPolyPolygon aTransformed(getUnitPolyPolygon());
+ aTransformed.transform(getTransform());
+
+ // OperationSmiley: Check if a UnitDefinitionPolyPolygon is set
+ if(getUnitDefinitionPolyPolygon().count()
+ && getUnitDefinitionPolyPolygon() != getUnitPolyPolygon())
+ {
+ // if yes, use the B2DRange of it's transformed form
+ basegfx::B2DPolyPolygon aTransformedDefinition(getUnitDefinitionPolyPolygon());
+ aTransformedDefinition.transform(getTransform());
+
+ aRetval.push_back(
+ createPolyPolygonFillPrimitive(
+ aTransformed,
+ aTransformedDefinition.getB2DRange(),
+ getSdrLFSTAttribute().getFill(),
+ getSdrLFSTAttribute().getFillFloatTransGradient()));
+ }
+ else
+ {
+ aRetval.push_back(
+ createPolyPolygonFillPrimitive(
+ aTransformed,
+ getSdrLFSTAttribute().getFill(),
+ getSdrLFSTAttribute().getFillFloatTransGradient()));
+ }
+ }
+
+ // add line
+ if(getSdrLFSTAttribute().getLine().isDefault())
+ {
+ // if initially no line is defined, create one for HitTest and BoundRect
+ aRetval.push_back(
+ createHiddenGeometryPrimitives2D(
+ false,
+ getUnitPolyPolygon(),
+ getTransform()));
+ }
+ else
+ {
+ Primitive2DContainer aTemp(getUnitPolyPolygon().count());
+
+ for(sal_uInt32 a(0); a < getUnitPolyPolygon().count(); a++)
+ {
+ basegfx::B2DPolygon aTransformed(getUnitPolyPolygon().getB2DPolygon(a));
+
+ aTransformed.transform(getTransform());
+ aTemp[a] = createPolygonLinePrimitive(
+ aTransformed,
+ getSdrLFSTAttribute().getLine(),
+ getSdrLFSTAttribute().getLineStartEnd());
+ }
+
+ aRetval.append(aTemp);
+ }
+
+ // add text
+ if(!getSdrLFSTAttribute().getText().isDefault())
+ {
+ aRetval.push_back(
+ createTextPrimitive(
+ getUnitPolyPolygon(),
+ getTransform(),
+ getSdrLFSTAttribute().getText(),
+ getSdrLFSTAttribute().getLine(),
+ false,
+ false));
+ }
+
+ // add shadow
+ if(!getSdrLFSTAttribute().getShadow().isDefault())
+ {
+ aRetval = createEmbeddedShadowPrimitive(
+ std::move(aRetval),
+ getSdrLFSTAttribute().getShadow());
+ }
+
+ rContainer.append(std::move(aRetval));
+ }
+
+ SdrPathPrimitive2D::SdrPathPrimitive2D(
+ const basegfx::B2DHomMatrix& rTransform,
+ const attribute::SdrLineFillEffectsTextAttribute& rSdrLFSTAttribute,
+ const basegfx::B2DPolyPolygon& rUnitPolyPolygon,
+ const basegfx::B2DPolyPolygon& rUnitDefinitionPolyPolygon)
+ : maTransform(rTransform),
+ maSdrLFSTAttribute(rSdrLFSTAttribute),
+ maUnitPolyPolygon(rUnitPolyPolygon),
+ maUnitDefinitionPolyPolygon(rUnitDefinitionPolyPolygon)
+ {
+ }
+
+ bool SdrPathPrimitive2D::operator==(const BasePrimitive2D& rPrimitive) const
+ {
+ if(BufferedDecompositionPrimitive2D::operator==(rPrimitive))
+ {
+ const SdrPathPrimitive2D& rCompare = static_cast<const SdrPathPrimitive2D&>(rPrimitive);
+
+ return (getUnitPolyPolygon() == rCompare.getUnitPolyPolygon()
+ && getUnitDefinitionPolyPolygon() == rCompare.getUnitDefinitionPolyPolygon()
+ && getTransform() == rCompare.getTransform()
+ && getSdrLFSTAttribute() == rCompare.getSdrLFSTAttribute());
+ }
+
+ return false;
+ }
+
+ // provide unique ID
+ sal_uInt32 SdrPathPrimitive2D::getPrimitive2DID() const
+ {
+ return PRIMITIVE2D_ID_SDRPATHPRIMITIVE2D;
+ }
+
+} // end of namespace
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/sdr/primitive2d/sdrprimitivetools.cxx b/svx/source/sdr/primitive2d/sdrprimitivetools.cxx
new file mode 100644
index 000000000..6376fa796
--- /dev/null
+++ b/svx/source/sdr/primitive2d/sdrprimitivetools.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 <sdr/primitive2d/sdrprimitivetools.hxx>
+#include <vcl/lazydelete.hxx>
+#include <vcl/BitmapTools.hxx>
+#include <mutex>
+
+
+// helper methods
+
+namespace drawinglayer::primitive2d
+{
+ BitmapEx createDefaultCross_3x3(const basegfx::BColor& rBColor)
+ {
+ static vcl::DeleteOnDeinit< BitmapEx > aRetVal(vcl::DeleteOnDeinitFlag::Empty);
+ static basegfx::BColor aBColor;
+ static std::mutex aMutex;
+
+ std::scoped_lock aGuard(aMutex);
+
+ if(!aRetVal.get() || rBColor != aBColor)
+ {
+ // copy values
+ aBColor = rBColor;
+
+ // create bitmap
+ Color c(aBColor);
+ sal_uInt8 r = c.GetRed();
+ sal_uInt8 g = c.GetGreen();
+ sal_uInt8 b = c.GetBlue();
+ sal_uInt8 a = 255;
+ const sal_uInt8 cross[] = {
+ 0, 0, 0, a, r, g, b, 0, 0, 0, 0, a,
+ r, g, b, 0, r, g, b, 0, r, g, b, 0,
+ 0, 0, 0, a, r, g, b, 0, 0, 0, 0, a
+ };
+ BitmapEx aBitmap = vcl::bitmap::CreateFromData(cross, 3, 3, 12, vcl::PixelFormat::N32_BPP);
+
+ // create and exchange at aRetVal
+ aRetVal.set(aBitmap);
+ }
+
+ return aRetVal.get() ? *aRetVal.get() : BitmapEx();
+ }
+} // end of namespace
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/sdr/primitive2d/sdrrectangleprimitive2d.cxx b/svx/source/sdr/primitive2d/sdrrectangleprimitive2d.cxx
new file mode 100644
index 000000000..f289ea6bc
--- /dev/null
+++ b/svx/source/sdr/primitive2d/sdrrectangleprimitive2d.cxx
@@ -0,0 +1,152 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <sdr/primitive2d/sdrrectangleprimitive2d.hxx>
+#include <basegfx/polygon/b2dpolygontools.hxx>
+#include <sdr/primitive2d/sdrdecompositiontools.hxx>
+#include <svx/sdr/primitive2d/svx_primitivetypes2d.hxx>
+#include <drawinglayer/primitive2d/sdrdecompositiontools2d.hxx>
+#include <basegfx/polygon/b2dpolygon.hxx>
+#include <basegfx/polygon/b2dpolypolygon.hxx>
+
+
+using namespace com::sun::star;
+
+
+namespace drawinglayer::primitive2d
+{
+ void SdrRectanglePrimitive2D::create2DDecomposition(Primitive2DContainer& rContainer, const geometry::ViewInformation2D& /*aViewInformation*/) const
+ {
+ Primitive2DContainer aRetval;
+
+ // create unit outline polygon
+ const basegfx::B2DPolygon aUnitOutline(basegfx::utils::createPolygonFromRect(
+ basegfx::B2DRange(0.0, 0.0, 1.0, 1.0),
+ getCornerRadiusX(),
+ getCornerRadiusY()));
+
+ // add fill
+ if(!getSdrLFSTAttribute().getFill().isDefault())
+ {
+ basegfx::B2DPolyPolygon aTransformed(aUnitOutline);
+
+ aTransformed.transform(getTransform());
+ aRetval.push_back(
+ createPolyPolygonFillPrimitive(
+ aTransformed,
+ getSdrLFSTAttribute().getFill(),
+ getSdrLFSTAttribute().getFillFloatTransGradient()));
+ }
+ else if(getForceFillForHitTest())
+ {
+ // if no fill and it's a text frame, create a fill for HitTest and
+ // BoundRect fallback
+ aRetval.push_back(
+ createHiddenGeometryPrimitives2D(
+ true,
+ basegfx::B2DPolyPolygon(aUnitOutline),
+ getTransform()));
+ }
+
+ // add line
+ if(!getSdrLFSTAttribute().getLine().isDefault())
+ {
+ basegfx::B2DPolygon aTransformed(aUnitOutline);
+
+ aTransformed.transform(getTransform());
+ aRetval.push_back(
+ createPolygonLinePrimitive(
+ aTransformed,
+ getSdrLFSTAttribute().getLine(),
+ attribute::SdrLineStartEndAttribute()));
+ }
+ else if(!getForceFillForHitTest())
+ {
+ // if initially no line is defined and it's not a text frame, create
+ // a line for HitTest and BoundRect
+ aRetval.push_back(
+ createHiddenGeometryPrimitives2D(
+ false,
+ basegfx::B2DPolyPolygon(aUnitOutline),
+ getTransform()));
+ }
+
+ // add text
+ if(!getSdrLFSTAttribute().getText().isDefault())
+ {
+ aRetval.push_back(
+ createTextPrimitive(
+ basegfx::B2DPolyPolygon(aUnitOutline),
+ getTransform(),
+ getSdrLFSTAttribute().getText(),
+ getSdrLFSTAttribute().getLine(),
+ false,
+ false));
+ }
+
+ // add shadow
+ if(!getSdrLFSTAttribute().getShadow().isDefault())
+ {
+ aRetval = createEmbeddedShadowPrimitive(
+ std::move(aRetval),
+ getSdrLFSTAttribute().getShadow());
+ }
+
+ rContainer.append(std::move(aRetval));
+ }
+
+ SdrRectanglePrimitive2D::SdrRectanglePrimitive2D(
+ const basegfx::B2DHomMatrix& rTransform,
+ const attribute::SdrLineFillEffectsTextAttribute& rSdrLFSTAttribute,
+ double fCornerRadiusX,
+ double fCornerRadiusY,
+ bool bForceFillForHitTest)
+ : maTransform(rTransform),
+ maSdrLFSTAttribute(rSdrLFSTAttribute),
+ mfCornerRadiusX(fCornerRadiusX),
+ mfCornerRadiusY(fCornerRadiusY),
+ mbForceFillForHitTest(bForceFillForHitTest)
+ {
+ }
+
+ bool SdrRectanglePrimitive2D::operator==(const BasePrimitive2D& rPrimitive) const
+ {
+ if(BufferedDecompositionPrimitive2D::operator==(rPrimitive))
+ {
+ const SdrRectanglePrimitive2D& rCompare = static_cast<const SdrRectanglePrimitive2D&>(rPrimitive);
+
+ return (getCornerRadiusX() == rCompare.getCornerRadiusX()
+ && getCornerRadiusY() == rCompare.getCornerRadiusY()
+ && getTransform() == rCompare.getTransform()
+ && getSdrLFSTAttribute() == rCompare.getSdrLFSTAttribute()
+ && getForceFillForHitTest() == rCompare.getForceFillForHitTest());
+ }
+
+ return false;
+ }
+
+ // provide unique ID
+ sal_uInt32 SdrRectanglePrimitive2D::getPrimitive2DID() const
+ {
+ return PRIMITIVE2D_ID_SDRRECTANGLEPRIMITIVE2D;
+ }
+
+} // end of namespace
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/sdr/primitive2d/sdrtextprimitive2d.cxx b/svx/source/sdr/primitive2d/sdrtextprimitive2d.cxx
new file mode 100644
index 000000000..6311c123d
--- /dev/null
+++ b/svx/source/sdr/primitive2d/sdrtextprimitive2d.cxx
@@ -0,0 +1,545 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <sdr/primitive2d/sdrtextprimitive2d.hxx>
+#include <svx/svdotext.hxx>
+#include <svx/sdr/primitive2d/svx_primitivetypes2d.hxx>
+#include <drawinglayer/primitive2d/texthierarchyprimitive2d.hxx>
+#include <editeng/outlobj.hxx>
+#include <editeng/editobj.hxx>
+#include <editeng/flditem.hxx>
+#include <drawinglayer/geometry/viewinformation2d.hxx>
+#include <svx/unoapi.hxx>
+#include <svx/svdpage.hxx>
+#include <svx/svdmodel.hxx>
+#include <svx/svdoutl.hxx>
+#include <com/sun/star/beans/XPropertySet.hpp>
+#include <osl/diagnose.h>
+
+
+using namespace com::sun::star;
+
+
+namespace
+{
+ sal_Int16 getPageNumber(const uno::Reference< drawing::XDrawPage >& rxDrawPage)
+ {
+ sal_Int16 nRetval(0);
+ uno::Reference< beans::XPropertySet > xSet(rxDrawPage, uno::UNO_QUERY);
+
+ if (xSet.is())
+ {
+ try
+ {
+ const uno::Any aNumber(xSet->getPropertyValue("Number"));
+ aNumber >>= nRetval;
+ }
+ catch(const uno::Exception&)
+ {
+ OSL_ASSERT(false);
+ }
+ }
+
+ return nRetval;
+ }
+
+ sal_Int16 getPageCount(const uno::Reference< drawing::XDrawPage >& rxDrawPage)
+ {
+ sal_Int16 nRetval(0);
+ SdrPage* pPage = GetSdrPageFromXDrawPage(rxDrawPage);
+
+ if(pPage)
+ {
+ if( (pPage->GetPageNum() == 0) && !pPage->IsMasterPage() )
+ {
+ // handout page!
+ return pPage->getSdrModelFromSdrPage().getHandoutPageCount();
+ }
+ else
+ {
+ const sal_uInt16 nPageCount(pPage->getSdrModelFromSdrPage().GetPageCount());
+ nRetval = (static_cast<sal_Int16>(nPageCount) - 1) / 2;
+ }
+ }
+
+ return nRetval;
+ }
+} // end of anonymous namespace
+
+
+namespace drawinglayer::primitive2d
+{
+ // support for XTEXT_PAINTSHAPE_BEGIN/XTEXT_PAINTSHAPE_END Metafile comments
+ // for slideshow. This uses TextHierarchyBlockPrimitive2D to mark a text block.
+ // ATM there is only one text block per SdrObject, this may get more in the future
+ void SdrTextPrimitive2D::encapsulateWithTextHierarchyBlockPrimitive2D(Primitive2DContainer& rContainer, Primitive2DContainer&& aCandidate)
+ {
+ rContainer.push_back(new TextHierarchyBlockPrimitive2D(drawinglayer::primitive2d::Primitive2DContainer(aCandidate)));
+ }
+
+ SdrTextPrimitive2D::SdrTextPrimitive2D(
+ const SdrText* pSdrText,
+ const OutlinerParaObject& rOutlinerParaObject)
+ : mrSdrText(const_cast< SdrText* >(pSdrText)),
+ maOutlinerParaObject(rOutlinerParaObject),
+ mnLastPageNumber(0),
+ mnLastPageCount(0),
+ mbContainsPageField(false),
+ mbContainsPageCountField(false),
+ mbContainsOtherFields(false)
+ {
+ const EditTextObject& rETO = maOutlinerParaObject.GetTextObject();
+
+ mbContainsPageField = rETO.HasField(SvxPageField::CLASS_ID);
+ mbContainsPageCountField = rETO.HasField(SvxPagesField::CLASS_ID);
+ mbContainsOtherFields = rETO.HasField(SvxHeaderField::CLASS_ID)
+ || rETO.HasField(SvxFooterField::CLASS_ID)
+ || rETO.HasField(SvxDateTimeField::CLASS_ID)
+ || rETO.HasField(SvxAuthorField::CLASS_ID);
+ }
+
+ const SdrText* SdrTextPrimitive2D::getSdrText() const { return mrSdrText.get(); }
+
+ bool SdrTextPrimitive2D::operator==(const BasePrimitive2D& rPrimitive) const
+ {
+ if(BufferedDecompositionPrimitive2D::operator==(rPrimitive))
+ {
+ const SdrTextPrimitive2D& rCompare = static_cast<const SdrTextPrimitive2D&>(rPrimitive);
+
+ return (
+
+ // compare OPO and content, but not WrongList
+ getOutlinerParaObject() == rCompare.getOutlinerParaObject()
+
+ // also compare WrongList (not-persistent data, but visualized)
+ && getOutlinerParaObject().isWrongListEqual(rCompare.getOutlinerParaObject()));
+ }
+
+ return false;
+ }
+
+ void SdrTextPrimitive2D::get2DDecomposition(Primitive2DDecompositionVisitor& rVisitor, const geometry::ViewInformation2D& rViewInformation) const
+ {
+ uno::Reference< drawing::XDrawPage > xCurrentlyVisualizingPage;
+ bool bCurrentlyVisualizingPageIsSet(false);
+ Color aNewTextBackgroundColor;
+ bool bNewTextBackgroundColorIsSet(false);
+ sal_Int16 nCurrentlyValidPageNumber(0);
+ sal_Int16 nCurrentlyValidPageCount(0);
+
+ if(!getBuffered2DDecomposition().empty())
+ {
+ bool bDoDelete(false);
+
+ // check visualized page
+ if(mbContainsPageField || mbContainsPageCountField || mbContainsOtherFields)
+ {
+ // get visualized page and remember
+ xCurrentlyVisualizingPage = rViewInformation.getVisualizedPage();
+ bCurrentlyVisualizingPageIsSet = true;
+
+ if(xCurrentlyVisualizingPage != mxLastVisualizingPage)
+ {
+ bDoDelete = true;
+ }
+
+ // #i98870# check visualized PageNumber
+ if(!bDoDelete && mbContainsPageField)
+ {
+ nCurrentlyValidPageNumber = getPageNumber(xCurrentlyVisualizingPage);
+
+ if(nCurrentlyValidPageNumber != mnLastPageNumber)
+ {
+ bDoDelete = true;
+ }
+ }
+
+ // #i98870# check visualized PageCount, too
+ if(!bDoDelete && mbContainsPageCountField)
+ {
+ nCurrentlyValidPageCount = getPageCount(xCurrentlyVisualizingPage);
+
+ if(nCurrentlyValidPageCount != mnLastPageCount)
+ {
+ bDoDelete = true;
+ }
+ }
+ }
+
+ // #i101443# check change of TextBackgroundolor
+ if(!bDoDelete && getSdrText())
+ {
+ SdrOutliner& rDrawOutliner = getSdrText()->GetObject().getSdrModelFromSdrObject().GetDrawOutliner();
+ aNewTextBackgroundColor = rDrawOutliner.GetBackgroundColor();
+ bNewTextBackgroundColorIsSet = true;
+
+ if(aNewTextBackgroundColor != maLastTextBackgroundColor)
+ {
+ bDoDelete = true;
+ }
+ }
+
+ if(bDoDelete)
+ {
+ const_cast< SdrTextPrimitive2D* >(this)->setBuffered2DDecomposition(Primitive2DContainer());
+ }
+ }
+
+ if(getBuffered2DDecomposition().empty())
+ {
+ if(!bCurrentlyVisualizingPageIsSet && mbContainsPageField)
+ {
+ xCurrentlyVisualizingPage = rViewInformation.getVisualizedPage();
+ }
+
+ if(!nCurrentlyValidPageNumber && mbContainsPageField)
+ {
+ nCurrentlyValidPageNumber = getPageNumber(xCurrentlyVisualizingPage);
+ }
+
+ if(!nCurrentlyValidPageCount && mbContainsPageCountField)
+ {
+ nCurrentlyValidPageCount = getPageCount(xCurrentlyVisualizingPage);
+ }
+
+ if(!bNewTextBackgroundColorIsSet && getSdrText())
+ {
+ SdrOutliner& rDrawOutliner = getSdrText()->GetObject().getSdrModelFromSdrObject().GetDrawOutliner();
+ aNewTextBackgroundColor = rDrawOutliner.GetBackgroundColor();
+ }
+
+ const_cast< SdrTextPrimitive2D* >(this)->mxLastVisualizingPage = xCurrentlyVisualizingPage;
+ const_cast< SdrTextPrimitive2D* >(this)->mnLastPageNumber = nCurrentlyValidPageNumber;
+ const_cast< SdrTextPrimitive2D* >(this)->mnLastPageCount = nCurrentlyValidPageCount;
+ const_cast< SdrTextPrimitive2D* >(this)->maLastTextBackgroundColor = aNewTextBackgroundColor;
+ }
+
+ // call parent
+ BufferedDecompositionPrimitive2D::get2DDecomposition(rVisitor, rViewInformation);
+ }
+
+
+
+
+ void SdrContourTextPrimitive2D::create2DDecomposition(Primitive2DContainer& rContainer, const geometry::ViewInformation2D& aViewInformation) const
+ {
+ Primitive2DContainer aRetval;
+ getSdrText()->GetObject().impDecomposeContourTextPrimitive(aRetval, *this, aViewInformation);
+
+ encapsulateWithTextHierarchyBlockPrimitive2D(rContainer, std::move(aRetval));
+ }
+
+ SdrContourTextPrimitive2D::SdrContourTextPrimitive2D(
+ const SdrText* pSdrText,
+ const OutlinerParaObject& rOutlinerParaObject,
+ const basegfx::B2DPolyPolygon& rUnitPolyPolygon,
+ const basegfx::B2DHomMatrix& rObjectTransform)
+ : SdrTextPrimitive2D(pSdrText, rOutlinerParaObject),
+ maUnitPolyPolygon(rUnitPolyPolygon),
+ maObjectTransform(rObjectTransform)
+ {
+ }
+
+ bool SdrContourTextPrimitive2D::operator==(const BasePrimitive2D& rPrimitive) const
+ {
+ if(SdrTextPrimitive2D::operator==(rPrimitive))
+ {
+ const SdrContourTextPrimitive2D& rCompare = static_cast<const SdrContourTextPrimitive2D&>(rPrimitive);
+
+ return (getUnitPolyPolygon() == rCompare.getUnitPolyPolygon()
+ && getObjectTransform() == rCompare.getObjectTransform());
+ }
+
+ return false;
+ }
+
+ rtl::Reference<SdrTextPrimitive2D> SdrContourTextPrimitive2D::createTransformedClone(const basegfx::B2DHomMatrix& rTransform) const
+ {
+ return new SdrContourTextPrimitive2D(
+ getSdrText(),
+ getOutlinerParaObject(),
+ getUnitPolyPolygon(),
+ rTransform * getObjectTransform());
+ }
+
+ // provide unique ID
+ sal_uInt32 SdrContourTextPrimitive2D::getPrimitive2DID() const
+ {
+ return PRIMITIVE2D_ID_SDRCONTOURTEXTPRIMITIVE2D;
+ }
+
+
+
+ void SdrPathTextPrimitive2D::create2DDecomposition(Primitive2DContainer& rContainer, const geometry::ViewInformation2D& aViewInformation) const
+ {
+ Primitive2DContainer aRetval;
+ getSdrText()->GetObject().impDecomposePathTextPrimitive(aRetval, *this, aViewInformation);
+
+ encapsulateWithTextHierarchyBlockPrimitive2D(rContainer, std::move(aRetval));
+ }
+
+ SdrPathTextPrimitive2D::SdrPathTextPrimitive2D(
+ const SdrText* pSdrText,
+ const OutlinerParaObject& rOutlinerParaObject,
+ const basegfx::B2DPolyPolygon& rPathPolyPolygon,
+ const attribute::SdrFormTextAttribute& rSdrFormTextAttribute)
+ : SdrTextPrimitive2D(pSdrText, rOutlinerParaObject),
+ maPathPolyPolygon(rPathPolyPolygon),
+ maSdrFormTextAttribute(rSdrFormTextAttribute)
+ {
+ }
+
+ bool SdrPathTextPrimitive2D::operator==(const BasePrimitive2D& rPrimitive) const
+ {
+ if(SdrTextPrimitive2D::operator==(rPrimitive))
+ {
+ const SdrPathTextPrimitive2D& rCompare = static_cast<const SdrPathTextPrimitive2D&>(rPrimitive);
+
+ return (getPathPolyPolygon() == rCompare.getPathPolyPolygon()
+ && getSdrFormTextAttribute() == rCompare.getSdrFormTextAttribute());
+ }
+
+ return false;
+ }
+
+ rtl::Reference<SdrTextPrimitive2D> SdrPathTextPrimitive2D::createTransformedClone(const basegfx::B2DHomMatrix& rTransform) const
+ {
+ basegfx::B2DPolyPolygon aNewPolyPolygon(getPathPolyPolygon());
+ aNewPolyPolygon.transform(rTransform);
+
+ return new SdrPathTextPrimitive2D(
+ getSdrText(),
+ getOutlinerParaObject(),
+ aNewPolyPolygon,
+ getSdrFormTextAttribute());
+ }
+
+ // provide unique ID
+ sal_uInt32 SdrPathTextPrimitive2D::getPrimitive2DID() const
+ {
+ return PRIMITIVE2D_ID_SDRPATHTEXTPRIMITIVE2D;
+ }
+
+
+
+ void SdrBlockTextPrimitive2D::create2DDecomposition(Primitive2DContainer& rContainer, const geometry::ViewInformation2D& aViewInformation) const
+ {
+ Primitive2DContainer aRetval;
+ getSdrText()->GetObject().impDecomposeBlockTextPrimitive(aRetval, *this, aViewInformation);
+
+ encapsulateWithTextHierarchyBlockPrimitive2D(rContainer, std::move(aRetval));
+ }
+
+ SdrBlockTextPrimitive2D::SdrBlockTextPrimitive2D(
+ const SdrText* pSdrText,
+ const OutlinerParaObject& rOutlinerParaObject,
+ const basegfx::B2DHomMatrix& rTextRangeTransform,
+ SdrTextHorzAdjust aSdrTextHorzAdjust,
+ SdrTextVertAdjust aSdrTextVertAdjust,
+ bool bFixedCellHeight,
+ bool bUnlimitedPage,
+ bool bCellText,
+ bool bWordWrap)
+ : SdrTextPrimitive2D(pSdrText, rOutlinerParaObject),
+ maTextRangeTransform(rTextRangeTransform),
+ maSdrTextHorzAdjust(aSdrTextHorzAdjust),
+ maSdrTextVertAdjust(aSdrTextVertAdjust),
+ mbFixedCellHeight(bFixedCellHeight),
+ mbUnlimitedPage(bUnlimitedPage),
+ mbCellText(bCellText),
+ mbWordWrap(bWordWrap)
+ {
+ }
+
+ bool SdrBlockTextPrimitive2D::operator==(const BasePrimitive2D& rPrimitive) const
+ {
+ if(SdrTextPrimitive2D::operator==(rPrimitive))
+ {
+ const SdrBlockTextPrimitive2D& rCompare = static_cast<const SdrBlockTextPrimitive2D&>(rPrimitive);
+
+ return (getTextRangeTransform() == rCompare.getTextRangeTransform()
+ && getSdrTextHorzAdjust() == rCompare.getSdrTextHorzAdjust()
+ && getSdrTextVertAdjust() == rCompare.getSdrTextVertAdjust()
+ && isFixedCellHeight() == rCompare.isFixedCellHeight()
+ && getUnlimitedPage() == rCompare.getUnlimitedPage()
+ && getCellText() == rCompare.getCellText()
+ && getWordWrap() == rCompare.getWordWrap());
+ }
+
+ return false;
+ }
+
+ rtl::Reference<SdrTextPrimitive2D> SdrBlockTextPrimitive2D::createTransformedClone(const basegfx::B2DHomMatrix& rTransform) const
+ {
+ return new SdrBlockTextPrimitive2D(
+ getSdrText(),
+ getOutlinerParaObject(),
+ rTransform * getTextRangeTransform(),
+ getSdrTextHorzAdjust(),
+ getSdrTextVertAdjust(),
+ isFixedCellHeight(),
+ getUnlimitedPage(),
+ getCellText(),
+ getWordWrap());
+ }
+
+ // provide unique ID
+ sal_uInt32 SdrBlockTextPrimitive2D::getPrimitive2DID() const
+ {
+ return PRIMITIVE2D_ID_SDRBLOCKTEXTPRIMITIVE2D;
+ }
+
+
+
+ void SdrAutoFitTextPrimitive2D::create2DDecomposition(Primitive2DContainer& rContainer, const geometry::ViewInformation2D& aViewInformation) const
+ {
+ Primitive2DContainer aRetval;
+ getSdrText()->GetObject().impDecomposeAutoFitTextPrimitive(aRetval, *this, aViewInformation);
+
+ encapsulateWithTextHierarchyBlockPrimitive2D(rContainer, std::move(aRetval));
+ }
+
+ SdrAutoFitTextPrimitive2D::SdrAutoFitTextPrimitive2D(
+ const SdrText* pSdrText,
+ const OutlinerParaObject& rParaObj,
+ const ::basegfx::B2DHomMatrix& rTextRangeTransform,
+ bool bWordWrap)
+ : SdrTextPrimitive2D(pSdrText, rParaObj),
+ maTextRangeTransform(rTextRangeTransform),
+ mbWordWrap(bWordWrap)
+ {
+ }
+
+ bool SdrAutoFitTextPrimitive2D::operator==(const BasePrimitive2D& rPrimitive) const
+ {
+ if(SdrTextPrimitive2D::operator==(rPrimitive))
+ {
+ const SdrBlockTextPrimitive2D& rCompare = static_cast<const SdrBlockTextPrimitive2D&>(rPrimitive);
+
+ return (getTextRangeTransform() == rCompare.getTextRangeTransform()
+ && getWordWrap() == rCompare.getWordWrap());
+ }
+
+ return false;
+ }
+
+ rtl::Reference<SdrTextPrimitive2D> SdrAutoFitTextPrimitive2D::createTransformedClone(const ::basegfx::B2DHomMatrix& rTransform) const
+ {
+ return new SdrAutoFitTextPrimitive2D(getSdrText(), getOutlinerParaObject(), rTransform * getTextRangeTransform(), getWordWrap());
+ }
+
+ // provide unique ID
+ sal_uInt32 SdrAutoFitTextPrimitive2D::getPrimitive2DID() const
+ {
+ return PRIMITIVE2D_ID_SDRAUTOFITTEXTPRIMITIVE2D;
+ }
+
+
+
+
+ SdrChainedTextPrimitive2D::SdrChainedTextPrimitive2D(
+ const SdrText* pSdrText,
+ const OutlinerParaObject& rOutlinerParaObject,
+ const basegfx::B2DHomMatrix& rTextRangeTransform)
+ : SdrTextPrimitive2D(pSdrText, rOutlinerParaObject),
+ maTextRangeTransform(rTextRangeTransform)
+ { }
+
+ void SdrChainedTextPrimitive2D::create2DDecomposition(Primitive2DContainer& rContainer, const geometry::ViewInformation2D& aViewInformation) const
+ {
+ Primitive2DContainer aRetval;
+ getSdrText()->GetObject().impDecomposeChainedTextPrimitive(aRetval, *this, aViewInformation);
+
+ encapsulateWithTextHierarchyBlockPrimitive2D(rContainer, std::move(aRetval));
+ }
+
+ bool SdrChainedTextPrimitive2D::operator==(const BasePrimitive2D& rPrimitive) const
+ {
+ if(SdrTextPrimitive2D::operator==(rPrimitive))
+ {
+ const SdrBlockTextPrimitive2D& rCompare = static_cast<const SdrBlockTextPrimitive2D&>(rPrimitive);
+
+ return (getTextRangeTransform() == rCompare.getTextRangeTransform());
+ }
+
+ return false;
+ }
+
+ rtl::Reference<SdrTextPrimitive2D> SdrChainedTextPrimitive2D::createTransformedClone(const basegfx::B2DHomMatrix& rTransform) const
+ {
+ return new SdrChainedTextPrimitive2D(getSdrText(), getOutlinerParaObject(), rTransform * getTextRangeTransform());
+ }
+
+ // provide unique ID
+ sal_uInt32 SdrChainedTextPrimitive2D::getPrimitive2DID() const
+ {
+ return PRIMITIVE2D_ID_SDRCHAINEDTEXTPRIMITIVE2D;
+ }
+
+
+ void SdrStretchTextPrimitive2D::create2DDecomposition(Primitive2DContainer& rContainer, const geometry::ViewInformation2D& aViewInformation) const
+ {
+ Primitive2DContainer aRetval;
+ getSdrText()->GetObject().impDecomposeStretchTextPrimitive(aRetval, *this, aViewInformation);
+
+ encapsulateWithTextHierarchyBlockPrimitive2D(rContainer, std::move(aRetval));
+ }
+
+ SdrStretchTextPrimitive2D::SdrStretchTextPrimitive2D(
+ const SdrText* pSdrText,
+ const OutlinerParaObject& rOutlinerParaObject,
+ const basegfx::B2DHomMatrix& rTextRangeTransform,
+ bool bFixedCellHeight)
+ : SdrTextPrimitive2D(pSdrText, rOutlinerParaObject),
+ maTextRangeTransform(rTextRangeTransform),
+ mbFixedCellHeight(bFixedCellHeight)
+ {
+ }
+
+ bool SdrStretchTextPrimitive2D::operator==(const BasePrimitive2D& rPrimitive) const
+ {
+ if(SdrTextPrimitive2D::operator==(rPrimitive))
+ {
+ const SdrStretchTextPrimitive2D& rCompare = static_cast<const SdrStretchTextPrimitive2D&>(rPrimitive);
+
+ return (getTextRangeTransform() == rCompare.getTextRangeTransform()
+ && isFixedCellHeight() == rCompare.isFixedCellHeight());
+ }
+
+ return false;
+ }
+
+ rtl::Reference<SdrTextPrimitive2D> SdrStretchTextPrimitive2D::createTransformedClone(const basegfx::B2DHomMatrix& rTransform) const
+ {
+ return new SdrStretchTextPrimitive2D(
+ getSdrText(),
+ getOutlinerParaObject(),
+ rTransform * getTextRangeTransform(),
+ isFixedCellHeight());
+ }
+
+ // provide unique ID
+ sal_uInt32 SdrStretchTextPrimitive2D::getPrimitive2DID() const
+ {
+ return PRIMITIVE2D_ID_SDRSTRETCHTEXTPRIMITIVE2D;
+ }
+
+} // end of namespace
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/sdr/primitive3d/sdrattributecreator3d.cxx b/svx/source/sdr/primitive3d/sdrattributecreator3d.cxx
new file mode 100644
index 000000000..6bf8f8c6e
--- /dev/null
+++ b/svx/source/sdr/primitive3d/sdrattributecreator3d.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 <sdr/primitive3d/sdrattributecreator3d.hxx>
+#include <svx/svx3ditems.hxx>
+#include <svl/itemset.hxx>
+#include <com/sun/star/drawing/NormalsKind.hpp>
+#include <com/sun/star/drawing/TextureProjectionMode.hpp>
+#include <com/sun/star/drawing/TextureKind2.hpp>
+#include <com/sun/star/drawing/TextureMode.hpp>
+#include <svx/xflclit.hxx>
+#include <drawinglayer/attribute/materialattribute3d.hxx>
+#include <drawinglayer/attribute/sdrobjectattribute3d.hxx>
+
+
+namespace drawinglayer::primitive2d
+{
+ attribute::Sdr3DObjectAttribute createNewSdr3DObjectAttribute(const SfxItemSet& rSet)
+ {
+ // get NormalsKind
+ css::drawing::NormalsKind aNormalsKind(css::drawing::NormalsKind_SPECIFIC);
+ const sal_uInt16 nNormalsValue(rSet.Get(SDRATTR_3DOBJ_NORMALS_KIND).GetValue());
+
+ if(1 == nNormalsValue)
+ {
+ aNormalsKind = css::drawing::NormalsKind_FLAT;
+ }
+ else if(2 == nNormalsValue)
+ {
+ aNormalsKind = css::drawing::NormalsKind_SPHERE;
+ }
+
+ // get NormalsInvert flag
+ const bool bInvertNormals(rSet.Get(SDRATTR_3DOBJ_NORMALS_INVERT).GetValue());
+
+ // get TextureProjectionX
+ css::drawing::TextureProjectionMode aTextureProjectionX(css::drawing::TextureProjectionMode_OBJECTSPECIFIC);
+ const sal_uInt16 nTextureValueX(rSet.Get(SDRATTR_3DOBJ_TEXTURE_PROJ_X).GetValue());
+
+ if(1 == nTextureValueX)
+ {
+ aTextureProjectionX = css::drawing::TextureProjectionMode_PARALLEL;
+ }
+ else if(2 == nTextureValueX)
+ {
+ aTextureProjectionX = css::drawing::TextureProjectionMode_SPHERE;
+ }
+
+ // get TextureProjectionY
+ css::drawing::TextureProjectionMode aTextureProjectionY(css::drawing::TextureProjectionMode_OBJECTSPECIFIC);
+ const sal_uInt16 nTextureValueY(rSet.Get(SDRATTR_3DOBJ_TEXTURE_PROJ_Y).GetValue());
+
+ if(1 == nTextureValueY)
+ {
+ aTextureProjectionY = css::drawing::TextureProjectionMode_PARALLEL;
+ }
+ else if(2 == nTextureValueY)
+ {
+ aTextureProjectionY = css::drawing::TextureProjectionMode_SPHERE;
+ }
+
+ // get DoubleSided flag
+ const bool bDoubleSided(rSet.Get(SDRATTR_3DOBJ_DOUBLE_SIDED).GetValue());
+
+ // get Shadow3D flag
+ const bool bShadow3D(rSet.Get(SDRATTR_3DOBJ_SHADOW_3D).GetValue());
+
+ // get TextureFilter flag
+ const bool bTextureFilter(rSet.Get(SDRATTR_3DOBJ_TEXTURE_FILTER).GetValue());
+
+ // get texture kind
+ // TextureKind: 1 == Base3DTextureLuminance, 2 == Base3DTextureIntensity, 3 == Base3DTextureColor
+ css::drawing::TextureKind2 aTextureKind(css::drawing::TextureKind2_LUMINANCE);
+ const sal_uInt16 nTextureKind(rSet.Get(SDRATTR_3DOBJ_TEXTURE_KIND).GetValue());
+
+ if(2 == nTextureKind)
+ {
+ aTextureKind = css::drawing::TextureKind2_INTENSITY;
+ }
+ else if(3 == nTextureKind)
+ {
+ aTextureKind = css::drawing::TextureKind2_COLOR;
+ }
+
+ // get texture mode
+ // TextureMode: 1 == Base3DTextureReplace, 2 == Base3DTextureModulate, 3 == Base3DTextureBlend
+ css::drawing::TextureMode aTextureMode(css::drawing::TextureMode_REPLACE);
+ const sal_uInt16 nTextureMode(rSet.Get(SDRATTR_3DOBJ_TEXTURE_MODE).GetValue());
+
+ if(2 == nTextureMode)
+ {
+ aTextureMode = css::drawing::TextureMode_MODULATE;
+ }
+ else if(3 == nTextureMode)
+ {
+ aTextureMode = css::drawing::TextureMode_BLEND;
+ }
+
+ // get object color
+ const ::basegfx::BColor aObjectColor(rSet.Get(XATTR_FILLCOLOR).GetColorValue().getBColor());
+
+ // get specular color
+ const ::basegfx::BColor aSpecular(rSet.Get(SDRATTR_3DOBJ_MAT_SPECULAR).GetValue().getBColor());
+
+ // get emissive color
+ const ::basegfx::BColor aEmission(rSet.Get(SDRATTR_3DOBJ_MAT_EMISSION).GetValue().getBColor());
+
+ // get specular intensity
+ sal_uInt16 nSpecularIntensity(rSet.Get(SDRATTR_3DOBJ_MAT_SPECULAR_INTENSITY).GetValue());
+
+ if(nSpecularIntensity > 128)
+ {
+ nSpecularIntensity = 128;
+ }
+
+ // get reduced line geometry
+ const bool bReducedLineGeometry(rSet.Get(SDRATTR_3DOBJ_REDUCED_LINE_GEOMETRY).GetValue());
+
+ // prepare material
+ attribute::MaterialAttribute3D aMaterial(aObjectColor, aSpecular, aEmission, nSpecularIntensity);
+
+ return attribute::Sdr3DObjectAttribute(
+ aNormalsKind, aTextureProjectionX, aTextureProjectionY,
+ aTextureKind, aTextureMode, aMaterial,
+ bInvertNormals, bDoubleSided, bShadow3D, bTextureFilter, bReducedLineGeometry);
+ }
+} // end of namespace
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/sdr/properties/attributeproperties.cxx b/svx/source/sdr/properties/attributeproperties.cxx
new file mode 100644
index 000000000..7f141b8f6
--- /dev/null
+++ b/svx/source/sdr/properties/attributeproperties.cxx
@@ -0,0 +1,541 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * 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 <sdr/properties/attributeproperties.hxx>
+#include <tools/debug.hxx>
+#include <svl/itemset.hxx>
+#include <svl/style.hxx>
+#include <svl/whiter.hxx>
+#include <svl/poolitem.hxx>
+#include <svx/svdobj.hxx>
+#include <svx/svddef.hxx>
+#include <svx/xbtmpit.hxx>
+#include <svx/xlndsit.hxx>
+#include <svx/xlnstit.hxx>
+#include <svx/xlnedit.hxx>
+#include <svx/xflgrit.hxx>
+#include <svx/xflftrit.hxx>
+#include <svx/xflhtit.hxx>
+#include <svx/svdmodel.hxx>
+#include <svx/svdpage.hxx>
+#include <osl/diagnose.h>
+
+namespace sdr::properties
+{
+ void AttributeProperties::ImpSetParentAtSfxItemSet(bool bDontRemoveHardAttr)
+ {
+ if(HasSfxItemSet() && mpStyleSheet)
+ {
+ // Delete hard attributes where items are set in the style sheet
+ if(!bDontRemoveHardAttr)
+ {
+ const SfxItemSet& rStyle = mpStyleSheet->GetItemSet();
+ SfxWhichIter aIter(rStyle);
+ sal_uInt16 nWhich = aIter.FirstWhich();
+
+ while(nWhich)
+ {
+ if(SfxItemState::SET == aIter.GetItemState())
+ {
+ mxItemSet->ClearItem(nWhich);
+ }
+
+ nWhich = aIter.NextWhich();
+ }
+ }
+
+ // set new stylesheet as parent
+ mxItemSet->SetParent(&mpStyleSheet->GetItemSet());
+ }
+ else
+ {
+ OSL_ENSURE(false, "ImpSetParentAtSfxItemSet called without SfxItemSet/SfxStyleSheet (!)");
+ }
+ }
+
+ void AttributeProperties::ImpAddStyleSheet(SfxStyleSheet* pNewStyleSheet, bool bDontRemoveHardAttr)
+ {
+ // test if old StyleSheet is cleared, else it would be lost
+ // after this method -> memory leak (!)
+ DBG_ASSERT(!mpStyleSheet, "Old style sheet not deleted before setting new one (!)");
+
+ if(!pNewStyleSheet)
+ return;
+
+ // local remember
+ mpStyleSheet = pNewStyleSheet;
+
+ if(HasSfxItemSet())
+ {
+ // register as listener
+ StartListening(*pNewStyleSheet->GetPool());
+ StartListening(*pNewStyleSheet);
+
+ // only apply the following when we have an SfxItemSet already, else
+ if(GetStyleSheet())
+ {
+ ImpSetParentAtSfxItemSet(bDontRemoveHardAttr);
+ }
+ }
+ }
+
+ void AttributeProperties::ImpRemoveStyleSheet()
+ {
+ // Check type since it is destroyed when the type is deleted
+ if(GetStyleSheet() && mpStyleSheet)
+ {
+ EndListening(*mpStyleSheet);
+ if (auto const pool = mpStyleSheet->GetPool()) { // TTTT
+ EndListening(*pool);
+ }
+
+ // reset parent of ItemSet
+ if(HasSfxItemSet())
+ {
+ mxItemSet->SetParent(nullptr);
+ }
+
+ SdrObject& rObj = GetSdrObject();
+ rObj.SetBoundRectDirty();
+ rObj.SetBoundAndSnapRectsDirty(/*bNotMyself*/true);
+ }
+
+ mpStyleSheet = nullptr;
+ }
+
+ // create a new itemset
+ SfxItemSet AttributeProperties::CreateObjectSpecificItemSet(SfxItemPool& rPool)
+ {
+ return SfxItemSet(rPool,
+
+ // ranges from SdrAttrObj
+ svl::Items<SDRATTR_START, SDRATTR_SHADOW_LAST,
+ SDRATTR_MISC_FIRST, SDRATTR_MISC_LAST,
+ SDRATTR_TEXTDIRECTION, SDRATTR_TEXTDIRECTION,
+ SDRATTR_TEXTCOLUMNS_FIRST, SDRATTR_TEXTCOLUMNS_LAST>);
+ }
+
+ AttributeProperties::AttributeProperties(SdrObject& rObj)
+ : DefaultProperties(rObj),
+ mpStyleSheet(nullptr)
+ {
+ // Do nothing else, esp. do *not* try to get and set
+ // a default SfxStyle sheet. Nothing is allowed to be done
+ // that may lead to calls to virtual functions like
+ // CreateObjectSpecificItemSet - these would go *wrong*.
+ // Thus the rest is lazy-init from here.
+ }
+
+ AttributeProperties::AttributeProperties(const AttributeProperties& rProps, SdrObject& rObj)
+ : DefaultProperties(rProps, rObj),
+ mpStyleSheet(nullptr)
+ {
+ SfxStyleSheet* pTargetStyleSheet(rProps.GetStyleSheet());
+
+ if(pTargetStyleSheet)
+ {
+ const bool bModelChange(&rObj.getSdrModelFromSdrObject() != &rProps.GetSdrObject().getSdrModelFromSdrObject());
+
+ if(bModelChange)
+ {
+ // tdf#117506
+ // The error shows that it is definitely necessary to solve this problem.
+ // Interestingly I already had a note here for 'work needed'.
+ // Checked in libreoffice-6-0 what happened there. In principle, the whole
+ // ::Clone of SdrPage and SdrObject happened in the same SdrModel, only
+ // afterwards a ::SetModel was used at the cloned SdrPage which went through
+ // all layers. The StyleSheet-problem was solved in
+ // AttributeProperties::MoveToItemPool at the end. There, a StyleSheet with the
+ // same name was searched for in the target-SdrModel.
+ // Start by resetting the current TargetStyleSheet so that nothing goes wrong
+ // when we do not find a fitting TargetStyleSheet.
+ // Note: The test for SdrModelChange above was wrong (compared the already set
+ // new SdrObject), so this never triggered and pTargetStyleSheet was never set to
+ // nullptr before. This means that a StyleSheet from another SdrModel was used
+ // what of course is very dangerous. Interestingly did not crash since when that
+ // other SdrModel was destroyed the ::Notify mechanism still worked reliably
+ // and de-connected this Properties successfully from the alien-StyleSheet.
+ pTargetStyleSheet = nullptr;
+
+ // Check if we have a TargetStyleSheetPool at the target-SdrModel. This *should*
+ // be the case already (SdrModel::Merge and SdDrawDocument::InsertBookmarkAsPage
+ // have already cloned the StyleSheets to the target-SdrModel when used in Draw/impress).
+ // If none is found, ImpGetDefaultStyleSheet will be used to set a 'default'
+ // StyleSheet as StyleSheet implicitly later (that's what happened in the task,
+ // thus the FillStyle changed to the 'default' Blue).
+ // Note: It *may* be necessary to do more for StyleSheets, e.g. clone/copy the
+ // StyleSheet Hierarchy from the source SdrModel and/or add the Items from there
+ // as hard attributes. If needed, have a look at the older AttributeProperties::SetModel
+ // implementation from e.g. libreoffice-6-0.
+ SfxStyleSheetBasePool* pTargetStyleSheetPool(rObj.getSdrModelFromSdrObject().GetStyleSheetPool());
+
+ if(nullptr != pTargetStyleSheetPool)
+ {
+ // If we have a TargetStyleSheetPool, search for the used StyleSheet
+ // in the target SdrModel using the Name from the original StyleSheet
+ // in the source-SdrModel.
+ pTargetStyleSheet = dynamic_cast< SfxStyleSheet* >(
+ pTargetStyleSheetPool->Find(
+ rProps.GetStyleSheet()->GetName(),
+ SfxStyleFamily::All));
+ }
+ }
+ }
+
+ if(!pTargetStyleSheet)
+ return;
+
+ if(HasSfxItemSet())
+ {
+ // The SfxItemSet has been cloned and exists,
+ // we can directly set the SfxStyleSheet at it
+ ImpAddStyleSheet(pTargetStyleSheet, true);
+ }
+ else
+ {
+ // No SfxItemSet exists yet (there is none in
+ // the source, so none was cloned). Remember the
+ // SfxStyleSheet to set it when the SfxItemSet
+ // got constructed on-demand
+ mpStyleSheet = pTargetStyleSheet;
+ }
+ }
+
+ AttributeProperties::~AttributeProperties()
+ {
+ ImpRemoveStyleSheet();
+ }
+
+ std::unique_ptr<BaseProperties> AttributeProperties::Clone(SdrObject& rObj) const
+ {
+ return std::unique_ptr<BaseProperties>(new AttributeProperties(*this, rObj));
+ }
+
+ const SfxItemSet& AttributeProperties::GetObjectItemSet() const
+ {
+ // remember if we had a SfxItemSet already
+ const bool bHadSfxItemSet(HasSfxItemSet());
+
+ // call parent - this will guarantee SfxItemSet existence
+ DefaultProperties::GetObjectItemSet();
+
+ if(!bHadSfxItemSet)
+ {
+ // need to take care for SfxStyleSheet for newly
+ // created SfxItemSet
+ if(nullptr == mpStyleSheet)
+ {
+ // Set missing defaults without removal of hard attributes.
+ // This is more complicated historically than I first thought:
+ // Originally for GetDefaultStyleSheetForSdrGrafObjAndSdrOle2Obj
+ // SetStyleSheet(..., false) was used, while for GetDefaultStyleSheet
+ // SetStyleSheet(..., true) was used. Thus, for SdrGrafObj and SdrOle2Obj
+ // bDontRemoveHardAttr == false -> *do* delete hard attributes was used.
+ // This was probably not done by purpose, adding the method
+ // GetDefaultStyleSheetForSdrGrafObjAndSdrOle2Obj additionally to
+ // GetDefaultStyleSheet was an enhancement to allow for SdrGrafObj/SdrOle2Obj
+ // with full AttributeSet (adding e.g. FillAttributes). To stay as compatible
+ // as possible these SdrObjects got a new default-StyleSheet.
+ // There is no reason to delete the HardAttributes and it anyways has only
+ // AFAIK effects on a single Item - the SdrTextHorzAdjustItem. To get things
+ // unified I will stay with not deleting the HardAttributes and adapt the
+ // UnitTests in CppunitTest_sd_import_tests accordingly.
+ const_cast< AttributeProperties* >(this)->applyDefaultStyleSheetFromSdrModel();
+ }
+ else
+ {
+ // Late-Init of setting parent to SfxStyleSheet after
+ // it's creation. Can only happen from copy-constructor
+ // (where creation of SfxItemSet is avoided due to the
+ // problem with constructors and virtual functions in C++),
+ // thus DontRemoveHardAttr is not needed.
+ const_cast< AttributeProperties* >(this)->SetStyleSheet(
+ mpStyleSheet, true, true);
+ }
+ }
+
+ return *mxItemSet;
+ }
+
+ void AttributeProperties::ItemSetChanged(o3tl::span< const SfxPoolItem* const > /*aChangedItems*/, sal_uInt16 /*nDeletedWhich*/)
+ {
+ // own modifications
+ SdrObject& rObj = GetSdrObject();
+
+ rObj.SetBoundRectDirty();
+ rObj.SetBoundAndSnapRectsDirty(/*bNotMyself*/true);
+ rObj.SetChanged();
+ }
+
+ void AttributeProperties::ItemChange(const sal_uInt16 nWhich, const SfxPoolItem* pNewItem)
+ {
+ if(pNewItem)
+ {
+ std::unique_ptr<SfxPoolItem> pResultItem;
+ SdrModel& rModel(GetSdrObject().getSdrModelFromSdrObject());
+
+ switch( nWhich )
+ {
+ case XATTR_FILLBITMAP:
+ {
+ // TTTT checkForUniqueItem should use SdrModel&
+ pResultItem = static_cast<const XFillBitmapItem*>(pNewItem)->checkForUniqueItem( &rModel );
+ break;
+ }
+ case XATTR_LINEDASH:
+ {
+ pResultItem = static_cast<const XLineDashItem*>(pNewItem)->checkForUniqueItem( &rModel );
+ break;
+ }
+ case XATTR_LINESTART:
+ {
+ pResultItem = static_cast<const XLineStartItem*>(pNewItem)->checkForUniqueItem( &rModel );
+ break;
+ }
+ case XATTR_LINEEND:
+ {
+ pResultItem = static_cast<const XLineEndItem*>(pNewItem)->checkForUniqueItem( &rModel );
+ break;
+ }
+ case XATTR_FILLGRADIENT:
+ {
+ pResultItem = static_cast<const XFillGradientItem*>(pNewItem)->checkForUniqueItem( &rModel );
+ break;
+ }
+ case XATTR_FILLFLOATTRANSPARENCE:
+ {
+ // #85953# allow all kinds of XFillFloatTransparenceItem to be set
+ pResultItem = static_cast<const XFillFloatTransparenceItem*>(pNewItem)->checkForUniqueItem( &rModel );
+ break;
+ }
+ case XATTR_FILLHATCH:
+ {
+ pResultItem = static_cast<const XFillHatchItem*>(pNewItem)->checkForUniqueItem( &rModel );
+ break;
+ }
+ }
+
+ // guarantee SfxItemSet existence
+ GetObjectItemSet();
+
+ if(pResultItem)
+ {
+ // force ItemSet
+ mxItemSet->Put(std::move(pResultItem));
+ }
+ else
+ {
+ mxItemSet->Put(*pNewItem);
+ }
+ }
+ else
+ {
+ // clear item if ItemSet exists
+ if(HasSfxItemSet())
+ {
+ mxItemSet->ClearItem(nWhich);
+ }
+ }
+ }
+
+ void AttributeProperties::SetStyleSheet(SfxStyleSheet* pNewStyleSheet, bool bDontRemoveHardAttr,
+ bool /*bBroadcast*/)
+ {
+ // guarantee SfxItemSet existence
+ GetObjectItemSet();
+
+ ImpRemoveStyleSheet();
+ ImpAddStyleSheet(pNewStyleSheet, bDontRemoveHardAttr);
+
+ SdrObject& rObj = GetSdrObject();
+ rObj.SetBoundRectDirty();
+ rObj.SetBoundAndSnapRectsDirty(true);
+ }
+
+ SfxStyleSheet* AttributeProperties::GetStyleSheet() const
+ {
+ return mpStyleSheet;
+ }
+
+ void AttributeProperties::ForceStyleToHardAttributes()
+ {
+ if(!GetStyleSheet() || mpStyleSheet == nullptr)
+ return;
+
+ // guarantee SfxItemSet existence
+ GetObjectItemSet();
+
+ // prepare copied, new itemset, but WITHOUT parent
+ SfxItemSet aDestItemSet(*mxItemSet);
+ aDestItemSet.SetParent(nullptr);
+
+ // prepare forgetting the current stylesheet like in RemoveStyleSheet()
+ EndListening(*mpStyleSheet);
+ EndListening(*mpStyleSheet->GetPool());
+
+ // prepare the iter; use the mpObjectItemSet which may have less
+ // WhichIDs than the style.
+ SfxWhichIter aIter(aDestItemSet);
+ sal_uInt16 nWhich(aIter.FirstWhich());
+ const SfxPoolItem *pItem = nullptr;
+
+ // now set all hard attributes of the current at the new itemset
+ while(nWhich)
+ {
+ // #i61284# use mpItemSet with parents, makes things easier and reduces to
+ // one loop
+ if(SfxItemState::SET == mxItemSet->GetItemState(nWhich, true, &pItem))
+ {
+ aDestItemSet.Put(*pItem);
+ }
+
+ nWhich = aIter.NextWhich();
+ }
+
+ // replace itemsets
+ mxItemSet.emplace(std::move(aDestItemSet));
+
+ // set necessary changes like in RemoveStyleSheet()
+ GetSdrObject().SetBoundRectDirty();
+ GetSdrObject().SetBoundAndSnapRectsDirty(/*bNotMyself*/true);
+
+ mpStyleSheet = nullptr;
+ }
+
+ void AttributeProperties::Notify(SfxBroadcaster& rBC, const SfxHint& rHint)
+ {
+ bool bHintUsed(false);
+
+ const SfxStyleSheetHint* pStyleHint = dynamic_cast<const SfxStyleSheetHint*>(&rHint);
+
+ if(pStyleHint && pStyleHint->GetStyleSheet() == GetStyleSheet())
+ {
+ SdrObject& rObj = GetSdrObject();
+ //SdrPage* pPage = rObj.GetPage();
+
+ switch(pStyleHint->GetId())
+ {
+ case SfxHintId::StyleSheetCreated :
+ {
+ // cannot happen, nothing to do
+ break;
+ }
+ case SfxHintId::StyleSheetModified :
+ case SfxHintId::StyleSheetChanged :
+ {
+ // notify change
+ break;
+ }
+ case SfxHintId::StyleSheetErased :
+ case SfxHintId::StyleSheetInDestruction :
+ {
+ // Style needs to be exchanged
+ SfxStyleSheet* pNewStSh = nullptr;
+ SdrModel& rModel(rObj.getSdrModelFromSdrObject());
+
+ // Do nothing if object is in destruction, else a StyleSheet may be found from
+ // a StyleSheetPool which is just being deleted itself. and thus it would be fatal
+ // to register as listener to that new StyleSheet.
+ if(!rObj.IsInDestruction())
+ {
+ if(SfxStyleSheet* pStyleSheet = GetStyleSheet())
+ {
+ pNewStSh = static_cast<SfxStyleSheet*>(rModel.GetStyleSheetPool()->Find(
+ pStyleSheet->GetParent(), pStyleSheet->GetFamily()));
+ }
+
+ if(!pNewStSh)
+ {
+ pNewStSh = rModel.GetDefaultStyleSheet();
+ }
+ }
+
+ // remove used style, it's erased or in destruction
+ ImpRemoveStyleSheet();
+
+ if(pNewStSh)
+ {
+ ImpAddStyleSheet(pNewStSh, true);
+ }
+
+ break;
+ }
+ default: break;
+ }
+
+ // Get old BoundRect. Do this after the style change is handled
+ // in the ItemSet parts because GetBoundRect() may calculate a new
+ tools::Rectangle aBoundRect = rObj.GetLastBoundRect();
+
+ rObj.SetBoundAndSnapRectsDirty(/*bNotMyself*/true);
+
+ // tell the object about the change
+ rObj.SetChanged();
+ rObj.BroadcastObjectChange();
+
+ //if(pPage && pPage->IsInserted())
+ //{
+ // rObj.BroadcastObjectChange();
+ //}
+
+ rObj.SendUserCall(SdrUserCallType::ChangeAttr, aBoundRect);
+
+ bHintUsed = true;
+ }
+
+ if(!bHintUsed)
+ {
+ // forward to SdrObject ATM. Not sure if this will be necessary
+ // in the future.
+ GetSdrObject().Notify(rBC, rHint);
+ }
+ }
+
+ bool AttributeProperties::isUsedByModel() const
+ {
+ const SdrObject& rObj(GetSdrObject());
+ if (rObj.IsInserted())
+ {
+ const SdrPage* const pPage(rObj.getSdrPageFromSdrObject());
+ if (pPage && pPage->IsInserted())
+ return true;
+ }
+ return false;
+ }
+
+ void AttributeProperties::applyDefaultStyleSheetFromSdrModel()
+ {
+ SfxStyleSheet* pDefaultStyleSheet(GetSdrObject().getSdrModelFromSdrObject().GetDefaultStyleSheet());
+
+ // tdf#118139 Only do this when StyleSheet really differs. It may e.g.
+ // be the case that nullptr == pDefaultStyleSheet and there is none set yet,
+ // so indeed no need to set it (needed for some strange old MSWord2003
+ // documents with CustomShape-'Group' and added Text-Frames, see task description)
+ if(pDefaultStyleSheet != GetStyleSheet())
+ {
+ // do not delete hard attributes when setting dsefault Style
+ SetStyleSheet(pDefaultStyleSheet, true, true);
+ }
+ }
+
+} // end of namespace
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/sdr/properties/captionproperties.cxx b/svx/source/sdr/properties/captionproperties.cxx
new file mode 100644
index 000000000..2f7a2c8a7
--- /dev/null
+++ b/svx/source/sdr/properties/captionproperties.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 <sal/config.h>
+
+#include <sdr/properties/captionproperties.hxx>
+#include <svl/itemset.hxx>
+#include <svl/style.hxx>
+#include <svx/svddef.hxx>
+#include <editeng/eeitem.hxx>
+#include <svx/svdocapt.hxx>
+
+
+namespace sdr::properties
+{
+ // create a new itemset
+ SfxItemSet CaptionProperties::CreateObjectSpecificItemSet(SfxItemPool& rPool)
+ {
+ return SfxItemSet(
+ rPool,
+ svl::Items<
+ // Ranges from SdrAttrObj, SdrCaptionObj:
+ SDRATTR_START, SDRATTR_MISC_LAST,
+ SDRATTR_TEXTDIRECTION, SDRATTR_TEXTDIRECTION,
+ SDRATTR_TEXTCOLUMNS_FIRST, SDRATTR_TEXTCOLUMNS_LAST,
+ // Range from SdrTextObj:
+ EE_ITEMS_START, EE_ITEMS_END>);
+ }
+
+ CaptionProperties::CaptionProperties(SdrObject& rObj)
+ : RectangleProperties(rObj)
+ {
+ }
+
+ CaptionProperties::CaptionProperties(const CaptionProperties& rProps, SdrObject& rObj)
+ : RectangleProperties(rProps, rObj)
+ {
+ }
+
+ CaptionProperties::~CaptionProperties()
+ {
+ }
+
+ std::unique_ptr<BaseProperties> CaptionProperties::Clone(SdrObject& rObj) const
+ {
+ return std::unique_ptr<BaseProperties>(new CaptionProperties(*this, rObj));
+ }
+
+ void CaptionProperties::ItemSetChanged(o3tl::span< const SfxPoolItem* const > aChangedItems, sal_uInt16 nDeletedWhich)
+ {
+ SdrCaptionObj& rObj = static_cast<SdrCaptionObj&>(GetSdrObject());
+
+ // local changes
+ rObj.ImpRecalcTail();
+
+ // call parent
+ RectangleProperties::ItemSetChanged(aChangedItems, nDeletedWhich);
+ }
+
+ void CaptionProperties::SetStyleSheet(SfxStyleSheet* pNewStyleSheet, bool bDontRemoveHardAttr,
+ bool bBroadcast)
+ {
+ // call parent (always first thing to do, may create the SfxItemSet)
+ RectangleProperties::SetStyleSheet(pNewStyleSheet, bDontRemoveHardAttr, bBroadcast);
+
+ // local changes
+ SdrCaptionObj& rObj = static_cast<SdrCaptionObj&>(GetSdrObject());
+ rObj.ImpRecalcTail();
+ }
+
+ void CaptionProperties::ForceDefaultAttributes()
+ {
+ // call parent
+ RectangleProperties::ForceDefaultAttributes();
+
+ // force ItemSet
+ GetObjectItemSet();
+
+ // this was set by TextProperties::ForceDefaultAttributes(),
+ // reset to default
+ mxItemSet->ClearItem(XATTR_LINESTYLE);
+ }
+} // end of namespace
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/sdr/properties/circleproperties.cxx b/svx/source/sdr/properties/circleproperties.cxx
new file mode 100644
index 000000000..0c2a5bdc1
--- /dev/null
+++ b/svx/source/sdr/properties/circleproperties.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 <sal/config.h>
+
+#include <sdr/properties/circleproperties.hxx>
+#include <svl/itemset.hxx>
+#include <svl/style.hxx>
+#include <svx/svddef.hxx>
+#include <editeng/eeitem.hxx>
+#include <svx/svdocirc.hxx>
+#include <sxcikitm.hxx>
+#include <svx/sxciaitm.hxx>
+
+
+namespace sdr::properties
+{
+ // create a new itemset
+ SfxItemSet CircleProperties::CreateObjectSpecificItemSet(SfxItemPool& rPool)
+ {
+ return SfxItemSet(
+ rPool,
+ svl::Items<
+ // Ranges from SdrAttrObj, SdrCircObj
+ SDRATTR_START, SDRATTR_SHADOW_LAST,
+ SDRATTR_MISC_FIRST, SDRATTR_MISC_LAST,
+ SDRATTR_CIRC_FIRST, SDRATTR_CIRC_LAST,
+ SDRATTR_TEXTDIRECTION, SDRATTR_TEXTDIRECTION,
+ SDRATTR_TEXTCOLUMNS_FIRST, SDRATTR_TEXTCOLUMNS_LAST,
+ // Range from SdrTextObj:
+ EE_ITEMS_START, EE_ITEMS_END>);
+ }
+
+ CircleProperties::CircleProperties(SdrObject& rObj)
+ : RectangleProperties(rObj)
+ {
+ }
+
+ CircleProperties::CircleProperties(const CircleProperties& rProps, SdrObject& rObj)
+ : RectangleProperties(rProps, rObj)
+ {
+ }
+
+ CircleProperties::~CircleProperties()
+ {
+ }
+
+ std::unique_ptr<BaseProperties> CircleProperties::Clone(SdrObject& rObj) const
+ {
+ return std::unique_ptr<BaseProperties>(new CircleProperties(*this, rObj));
+ }
+
+ void CircleProperties::ItemSetChanged(o3tl::span< const SfxPoolItem* const > aChangedItems, sal_uInt16 nDeletedWhich)
+ {
+ SdrCircObj& rObj = static_cast<SdrCircObj&>(GetSdrObject());
+
+ // call parent
+ RectangleProperties::ItemSetChanged(aChangedItems, nDeletedWhich);
+
+ // local changes
+ rObj.ImpSetAttrToCircInfo();
+ }
+
+ void CircleProperties::SetStyleSheet(SfxStyleSheet* pNewStyleSheet, bool bDontRemoveHardAttr,
+ bool bBroadcast)
+ {
+ // call parent (always first thing to do, may create the SfxItemSet)
+ RectangleProperties::SetStyleSheet(pNewStyleSheet, bDontRemoveHardAttr, bBroadcast);
+
+ // local changes
+ SdrCircObj& rObj = static_cast<SdrCircObj&>(GetSdrObject());
+ rObj.SetXPolyDirty();
+
+ // local changes
+ rObj.ImpSetAttrToCircInfo();
+ }
+
+ void CircleProperties::ForceDefaultAttributes()
+ {
+ SdrCircObj& rObj = static_cast<SdrCircObj&>(GetSdrObject());
+ SdrCircKind eKind = rObj.GetCircleKind();
+
+ if(eKind != SdrCircKind::Full)
+ {
+ // force ItemSet
+ GetObjectItemSet();
+
+ mxItemSet->Put(SdrCircKindItem(eKind));
+
+ if(rObj.GetStartAngle())
+ {
+ mxItemSet->Put(makeSdrCircStartAngleItem(rObj.GetStartAngle()));
+ }
+
+ if(rObj.GetEndAngle() != 36000_deg100)
+ {
+ mxItemSet->Put(makeSdrCircEndAngleItem(rObj.GetEndAngle()));
+ }
+ }
+
+ // call parent after SetObjectItem(SdrCircKindItem())
+ // because ForceDefaultAttr() will call
+ // ImpSetAttrToCircInfo() which needs a correct
+ // SdrCircKindItem
+ RectangleProperties::ForceDefaultAttributes();
+ }
+} // end of namespace
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/sdr/properties/connectorproperties.cxx b/svx/source/sdr/properties/connectorproperties.cxx
new file mode 100644
index 000000000..375a98190
--- /dev/null
+++ b/svx/source/sdr/properties/connectorproperties.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 <sal/config.h>
+
+#include <sdr/properties/connectorproperties.hxx>
+#include <svl/itemset.hxx>
+#include <svl/style.hxx>
+#include <svx/svddef.hxx>
+#include <editeng/eeitem.hxx>
+#include <svx/svdoedge.hxx>
+
+
+namespace sdr::properties
+{
+ // create a new itemset
+ SfxItemSet ConnectorProperties::CreateObjectSpecificItemSet(SfxItemPool& rPool)
+ {
+ return SfxItemSet(
+ rPool,
+ svl::Items<
+ // Ranges from SdrAttrObj, SdrEdgeObj:
+ SDRATTR_START, SDRATTR_SHADOW_LAST,
+ SDRATTR_MISC_FIRST, SDRATTR_EDGE_LAST,
+ SDRATTR_TEXTDIRECTION, SDRATTR_TEXTDIRECTION,
+ SDRATTR_TEXTCOLUMNS_FIRST, SDRATTR_TEXTCOLUMNS_LAST,
+ // Range from SdrTextObj:
+ EE_ITEMS_START, EE_ITEMS_END>);
+ }
+
+ ConnectorProperties::ConnectorProperties(SdrObject& rObj)
+ : TextProperties(rObj)
+ {
+ }
+
+ ConnectorProperties::ConnectorProperties(const ConnectorProperties& rProps, SdrObject& rObj)
+ : TextProperties(rProps, rObj)
+ {
+ }
+
+ ConnectorProperties::~ConnectorProperties()
+ {
+ }
+
+ std::unique_ptr<BaseProperties> ConnectorProperties::Clone(SdrObject& rObj) const
+ {
+ return std::unique_ptr<BaseProperties>(new ConnectorProperties(*this, rObj));
+ }
+
+ void ConnectorProperties::ItemSetChanged(o3tl::span< const SfxPoolItem* const > aChangedItems, sal_uInt16 nDeletedWhich)
+ {
+ SdrEdgeObj& rObj = static_cast<SdrEdgeObj&>(GetSdrObject());
+
+ // call parent
+ TextProperties::ItemSetChanged(aChangedItems, nDeletedWhich);
+
+ // local changes
+ rObj.ImpSetAttrToEdgeInfo();
+ }
+
+ void ConnectorProperties::SetStyleSheet(SfxStyleSheet* pNewStyleSheet, bool bDontRemoveHardAttr,
+ bool bBroadcast)
+ {
+ // call parent (always first thing to do, may create the SfxItemSet)
+ TextProperties::SetStyleSheet(pNewStyleSheet, bDontRemoveHardAttr, bBroadcast);
+
+ // local changes
+ SdrEdgeObj& rObj = static_cast<SdrEdgeObj&>(GetSdrObject());
+ rObj.ImpSetAttrToEdgeInfo();
+ }
+} // end of namespace
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/sdr/properties/customshapeproperties.cxx b/svx/source/sdr/properties/customshapeproperties.cxx
new file mode 100644
index 000000000..59135cde1
--- /dev/null
+++ b/svx/source/sdr/properties/customshapeproperties.cxx
@@ -0,0 +1,230 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * 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 <sdr/properties/customshapeproperties.hxx>
+#include <svl/itemset.hxx>
+#include <svl/style.hxx>
+#include <svx/svdoashp.hxx>
+#include <svx/sdooitm.hxx>
+#include <editeng/eeitem.hxx>
+#include <svl/whiter.hxx>
+#include <svl/hint.hxx>
+
+
+namespace sdr::properties
+{
+ void CustomShapeProperties::UpdateTextFrameStatus(bool bInvalidateRenderGeometry)
+ {
+ SdrObjCustomShape& rObj = static_cast< SdrObjCustomShape& >(GetSdrObject());
+ const bool bOld(rObj.mbTextFrame);
+
+ // change TextFrame flag when bResizeShapeToFitText changes (which is mapped
+ // on the item SDRATTR_TEXT_AUTOGROWHEIGHT for custom shapes, argh)
+ rObj.mbTextFrame = GetObjectItemSet().Get(SDRATTR_TEXT_AUTOGROWHEIGHT).GetValue();
+
+ // check if it did change
+ if(rObj.mbTextFrame != bOld)
+ {
+ // on change also invalidate render geometry
+ bInvalidateRenderGeometry = true;
+
+ // #115391# Potential recursion, since it calls SetObjectItemSet again, but rObj.bTextFrame
+ // will not change again. Thus it will be only one level and terminate safely
+ rObj.AdaptTextMinSize();
+ }
+
+ if(bInvalidateRenderGeometry)
+ {
+ // if asked for or bResizeShapeToFitText changed, make sure that
+ // the render geometry is reconstructed using changed parameters
+ rObj.InvalidateRenderGeometry();
+ }
+ }
+
+ SfxItemSet CustomShapeProperties::CreateObjectSpecificItemSet(SfxItemPool& rPool)
+ {
+ return SfxItemSet(
+ rPool,
+ 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,
+ SDRATTR_GLOW_FIRST, SDRATTR_SOFTEDGE_LAST,
+ SDRATTR_TEXTCOLUMNS_FIRST, SDRATTR_TEXTCOLUMNS_LAST,
+ // Range from SdrTextObj:
+ EE_ITEMS_START, EE_ITEMS_END>);
+ }
+
+ bool CustomShapeProperties::AllowItemChange(const sal_uInt16 nWhich, const SfxPoolItem* pNewItem ) const
+ {
+ bool bAllowItemChange = true;
+ if ( !pNewItem )
+ {
+ if ( ( nWhich >= SDRATTR_CUSTOMSHAPE_FIRST ) && ( nWhich <= SDRATTR_CUSTOMSHAPE_LAST ) )
+ bAllowItemChange = false;
+ }
+ if ( bAllowItemChange )
+ bAllowItemChange = TextProperties::AllowItemChange( nWhich, pNewItem );
+ return bAllowItemChange;
+ }
+
+ void CustomShapeProperties::ClearObjectItem(const sal_uInt16 nWhich)
+ {
+ if ( !nWhich )
+ {
+ SfxWhichIter aIter( *mxItemSet );
+ sal_uInt16 nWhich2 = aIter.FirstWhich();
+ while( nWhich2 )
+ {
+ TextProperties::ClearObjectItemDirect( nWhich2 );
+ nWhich2 = aIter.NextWhich();
+ }
+ ItemSetChanged({}, 0);
+ }
+ else
+ TextProperties::ClearObjectItem( nWhich );
+ }
+
+ void CustomShapeProperties::ClearObjectItemDirect(const sal_uInt16 nWhich)
+ {
+ if ( !nWhich )
+ {
+ SfxWhichIter aIter( *mxItemSet );
+ sal_uInt16 nWhich2 = aIter.FirstWhich();
+ while( nWhich2 )
+ {
+ TextProperties::ClearObjectItemDirect( nWhich2 );
+ nWhich2 = aIter.NextWhich();
+ }
+ }
+ else
+ TextProperties::ClearObjectItemDirect( nWhich );
+ }
+
+ void CustomShapeProperties::ItemSetChanged(o3tl::span< const SfxPoolItem* const > aChangedItems, sal_uInt16 nDeletedWhich)
+ {
+ // call parent
+ TextProperties::ItemSetChanged(aChangedItems, nDeletedWhich);
+
+ // update bTextFrame and RenderGeometry
+ UpdateTextFrameStatus(true);
+ }
+
+ void CustomShapeProperties::PostItemChange(const sal_uInt16 nWhich)
+ {
+ switch(nWhich)
+ {
+ case SDRATTR_TEXT_AUTOGROWHEIGHT:
+ {
+ // #115391# update bTextFrame and RenderGeometry using AdaptTextMinSize()
+ UpdateTextFrameStatus(false);
+ break;
+ }
+ default:
+ {
+ break;
+ }
+ }
+
+ // call parent
+ TextProperties::PostItemChange(nWhich);
+ }
+
+ void CustomShapeProperties::ItemChange(const sal_uInt16 nWhich, const SfxPoolItem* pNewItem)
+ {
+ // call parent
+ TextProperties::ItemChange( nWhich, pNewItem );
+
+ // update bTextFrame and RenderGeometry
+ UpdateTextFrameStatus(true);
+ }
+
+ void CustomShapeProperties::SetStyleSheet(SfxStyleSheet* pNewStyleSheet, bool bDontRemoveHardAttr,
+ bool bBroadcast)
+ {
+ // call parent (always first thing to do, may create the SfxItemSet)
+ TextProperties::SetStyleSheet( pNewStyleSheet, bDontRemoveHardAttr, bBroadcast );
+
+ // update bTextFrame and RenderGeometry
+ UpdateTextFrameStatus(true);
+ }
+
+ void CustomShapeProperties::ForceDefaultAttributes()
+ {
+ // update bTextFrame and RenderGeometry
+ UpdateTextFrameStatus(true);
+
+ // SJ: Following is no good if creating customshapes, leading to objects that are white after loading via xml
+ // This means: Do *not* call parent here is by purpose...
+ }
+
+ CustomShapeProperties::CustomShapeProperties(SdrObject& rObj)
+ : TextProperties(rObj)
+ {
+ }
+
+ CustomShapeProperties::CustomShapeProperties(const CustomShapeProperties& rProps, SdrObject& rObj)
+ : TextProperties(rProps, rObj)
+ {
+ }
+
+ CustomShapeProperties::~CustomShapeProperties()
+ {
+ }
+
+ std::unique_ptr<BaseProperties> CustomShapeProperties::Clone(SdrObject& rObj) const
+ {
+ return std::unique_ptr<BaseProperties>(new CustomShapeProperties(*this, rObj));
+ }
+
+ void CustomShapeProperties::Notify( SfxBroadcaster& rBC, const SfxHint& rHint )
+ {
+ TextProperties::Notify( rBC, rHint );
+
+ bool bRemoveRenderGeometry = false;
+ const SfxStyleSheetHint* pStyleHint = dynamic_cast<const SfxStyleSheetHint*>(&rHint);
+
+ if ( pStyleHint && pStyleHint->GetStyleSheet() == GetStyleSheet() )
+ {
+ switch( pStyleHint->GetId() )
+ {
+ case SfxHintId::StyleSheetModified :
+ case SfxHintId::StyleSheetChanged :
+ bRemoveRenderGeometry = true;
+ break;
+ default: break;
+ }
+ }
+ else if ( rHint.GetId() == SfxHintId::DataChanged )
+ {
+ bRemoveRenderGeometry = true;
+ }
+
+ // update bTextFrame and RenderGeometry
+ UpdateTextFrameStatus(bRemoveRenderGeometry);
+ }
+} // end of namespace
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/sdr/properties/defaultproperties.cxx b/svx/source/sdr/properties/defaultproperties.cxx
new file mode 100644
index 000000000..fa5292458
--- /dev/null
+++ b/svx/source/sdr/properties/defaultproperties.cxx
@@ -0,0 +1,251 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * 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 <svx/sdr/properties/defaultproperties.hxx>
+#include <sdr/properties/itemsettools.hxx>
+#include <svl/itemset.hxx>
+#include <svl/whiter.hxx>
+#include <vector>
+#include <svx/svdobj.hxx>
+#include <svx/svddef.hxx>
+#include <editeng/eeitem.hxx>
+#include <libxml/xmlwriter.h>
+#include <svx/svdmodel.hxx>
+#include <svx/svdtrans.hxx>
+#include <svx/xbtmpit.hxx>
+
+namespace sdr::properties
+{
+ SfxItemSet DefaultProperties::CreateObjectSpecificItemSet(SfxItemPool& rPool)
+ {
+ // Basic implementation; Basic object has NO attributes
+ return SfxItemSet(rPool);
+ }
+
+ DefaultProperties::DefaultProperties(SdrObject& rObj)
+ : BaseProperties(rObj)
+ {
+ }
+
+ DefaultProperties::DefaultProperties(const DefaultProperties& rProps, SdrObject& rObj)
+ : BaseProperties(rObj)
+ {
+ if(!rProps.mxItemSet)
+ return;
+
+ // Clone may be to another model and thus another ItemPool.
+ // SfxItemSet supports that thus we are able to Clone all
+ // SfxItemState::SET items to the target pool.
+ mxItemSet.emplace(rProps.mxItemSet->CloneAsValue(
+ true,
+ &rObj.getSdrModelFromSdrObject().GetItemPool()));
+
+ // React on ModelChange: If metric has changed, scale items.
+ // As seen above, clone is supported, but scale is not included,
+ // thus: TTTT maybe add scale to SfxItemSet::Clone() (?)
+ // tdf#117707 correct ModelChange detection
+ const bool bModelChange(&rObj.getSdrModelFromSdrObject() != &rProps.GetSdrObject().getSdrModelFromSdrObject());
+
+ if(bModelChange)
+ {
+ const MapUnit aOldUnit(rProps.GetSdrObject().getSdrModelFromSdrObject().GetScaleUnit());
+ const MapUnit aNewUnit(rObj.getSdrModelFromSdrObject().GetScaleUnit());
+ const bool bScaleUnitChanged(aNewUnit != aOldUnit);
+
+ if(bScaleUnitChanged)
+ {
+ const Fraction aMetricFactor(GetMapFactor(aOldUnit, aNewUnit).X());
+
+ ScaleItemSet(*mxItemSet, aMetricFactor);
+ }
+ }
+
+ // do not keep parent info, this may be changed by later constructors.
+ // This class just copies the ItemSet, ignore parent.
+ if(mxItemSet && mxItemSet->GetParent())
+ {
+ mxItemSet->SetParent(nullptr);
+ }
+ }
+
+ std::unique_ptr<BaseProperties> DefaultProperties::Clone(SdrObject& rObj) const
+ {
+ return std::unique_ptr<BaseProperties>(new DefaultProperties(*this, rObj));
+ }
+
+ DefaultProperties::~DefaultProperties() {}
+
+ const SfxItemSet& DefaultProperties::GetObjectItemSet() const
+ {
+ if(!mxItemSet)
+ {
+ mxItemSet.emplace(const_cast<DefaultProperties*>(this)->CreateObjectSpecificItemSet(GetSdrObject().GetObjectItemPool()));
+ const_cast<DefaultProperties*>(this)->ForceDefaultAttributes();
+ }
+
+ assert(mxItemSet && "Could not create an SfxItemSet(!)");
+
+ return *mxItemSet;
+ }
+
+ void DefaultProperties::SetObjectItem(const SfxPoolItem& rItem)
+ {
+ const sal_uInt16 nWhichID(rItem.Which());
+
+ if(!AllowItemChange(nWhichID, &rItem))
+ return;
+
+ ItemChange(nWhichID, &rItem);
+ PostItemChange(nWhichID);
+
+ const SfxPoolItem* pItem = &rItem;
+ ItemSetChanged( {&pItem, 1}, 0);
+ }
+
+ void DefaultProperties::SetObjectItemDirect(const SfxPoolItem& rItem)
+ {
+ const sal_uInt16 nWhichID(rItem.Which());
+
+ if(AllowItemChange(nWhichID, &rItem))
+ {
+ ItemChange(nWhichID, &rItem);
+ }
+ }
+
+ void DefaultProperties::ClearObjectItem(const sal_uInt16 nWhich)
+ {
+ if(!AllowItemChange(nWhich))
+ return;
+
+ ItemChange(nWhich);
+ PostItemChange(nWhich);
+
+ if(nWhich)
+ {
+ ItemSetChanged({}, nWhich);
+ }
+ }
+
+ void DefaultProperties::ClearObjectItemDirect(const sal_uInt16 nWhich)
+ {
+ if(AllowItemChange(nWhich))
+ {
+ ItemChange(nWhich);
+ }
+ }
+
+ void DefaultProperties::SetObjectItemSet(const SfxItemSet& rSet)
+ {
+ if (rSet.HasItem(XATTR_FILLBITMAP))
+ {
+ const XFillBitmapItem* pItem = rSet.GetItem(XATTR_FILLBITMAP);
+ const std::shared_ptr<VectorGraphicData>& pVectorData
+ = pItem->GetGraphicObject().GetGraphic().getVectorGraphicData();
+ if (pVectorData)
+ {
+ // Shape is filled by a vector graphic: tell it our size as a hint.
+ basegfx::B2DTuple aSizeHint;
+ aSizeHint.setX(GetSdrObject().GetSnapRect().getWidth());
+ aSizeHint.setY(GetSdrObject().GetSnapRect().getHeight());
+ pVectorData->setSizeHint(aSizeHint);
+ }
+ }
+
+ SfxWhichIter aWhichIter(rSet);
+ sal_uInt16 nWhich(aWhichIter.FirstWhich());
+ std::vector< const SfxPoolItem * > aPostItemChangeList;
+ // give a hint to STL_Vector
+ aPostItemChangeList.reserve(rSet.Count());
+
+ while(nWhich)
+ {
+ const SfxPoolItem* pPoolItem;
+ if(SfxItemState::SET == aWhichIter.GetItemState(false, &pPoolItem))
+ {
+ if(AllowItemChange(nWhich, pPoolItem))
+ {
+ ItemChange(nWhich, pPoolItem);
+ aPostItemChangeList.emplace_back( pPoolItem );
+ }
+ }
+
+ nWhich = aWhichIter.NextWhich();
+ }
+
+ if(!aPostItemChangeList.empty())
+ {
+ for (const auto& rItem : aPostItemChangeList)
+ {
+ PostItemChange(rItem->Which());
+ }
+
+ ItemSetChanged(aPostItemChangeList, 0);
+ }
+ }
+
+ void DefaultProperties::ItemSetChanged(o3tl::span< const SfxPoolItem* const > /*aChangedItems*/, sal_uInt16 /*nDeletedWhich*/)
+ {
+ }
+
+ bool DefaultProperties::AllowItemChange(const sal_uInt16 /*nWhich*/, const SfxPoolItem* /*pNewItem*/) const
+ {
+ return true;
+ }
+
+ void DefaultProperties::ItemChange(const sal_uInt16 /*nWhich*/, const SfxPoolItem* /*pNewItem*/)
+ {
+ }
+
+ void DefaultProperties::PostItemChange(const sal_uInt16 nWhich )
+ {
+ if( (nWhich == XATTR_FILLSTYLE) && mxItemSet )
+ CleanupFillProperties(*mxItemSet);
+ }
+
+ void DefaultProperties::SetStyleSheet(SfxStyleSheet* /*pNewStyleSheet*/, bool /*bDontRemoveHardAttr*/,
+ bool /*bBroadcast*/)
+ {
+ // no StyleSheet in DefaultProperties
+ }
+
+ SfxStyleSheet* DefaultProperties::GetStyleSheet() const
+ {
+ // no StyleSheet in DefaultProperties
+ return nullptr;
+ }
+
+ void DefaultProperties::ForceDefaultAttributes()
+ {
+ }
+
+ void DefaultProperties::dumpAsXml(xmlTextWriterPtr pWriter) const
+ {
+ (void)xmlTextWriterStartElement(pWriter, BAD_CAST("DefaultProperties"));
+ BaseProperties::dumpAsXml(pWriter);
+ if (mxItemSet)
+ {
+ mxItemSet->dumpAsXml(pWriter);
+ }
+ (void)xmlTextWriterEndElement(pWriter);
+ }
+} // end of namespace
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/sdr/properties/e3dcompoundproperties.cxx b/svx/source/sdr/properties/e3dcompoundproperties.cxx
new file mode 100644
index 000000000..c31d85fdc
--- /dev/null
+++ b/svx/source/sdr/properties/e3dcompoundproperties.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 <sdr/properties/e3dcompoundproperties.hxx>
+#include <svl/itemset.hxx>
+#include <svx/obj3d.hxx>
+#include <svx/scene3d.hxx>
+
+
+namespace sdr::properties
+{
+ E3dCompoundProperties::E3dCompoundProperties(SdrObject& rObj)
+ : E3dProperties(rObj)
+ {
+ }
+
+ E3dCompoundProperties::E3dCompoundProperties(const E3dCompoundProperties& rProps, SdrObject& rObj)
+ : E3dProperties(rProps, rObj)
+ {
+ }
+
+ E3dCompoundProperties::~E3dCompoundProperties()
+ {
+ }
+
+ std::unique_ptr<BaseProperties> E3dCompoundProperties::Clone(SdrObject& rObj) const
+ {
+ return std::unique_ptr<BaseProperties>(new E3dCompoundProperties(*this, rObj));
+ }
+
+ const SfxItemSet& E3dCompoundProperties::GetMergedItemSet() const
+ {
+ // include Items of scene this object belongs to
+ const E3dCompoundObject& rObj = static_cast<const E3dCompoundObject&>(GetSdrObject());
+ E3dScene* pScene(rObj.getRootE3dSceneFromE3dObject());
+
+ if(nullptr != pScene)
+ {
+ // force ItemSet
+ GetObjectItemSet();
+
+ // add filtered scene properties (SDRATTR_3DSCENE_) to local itemset
+ SfxItemSetFixed<SDRATTR_3DSCENE_FIRST, SDRATTR_3DSCENE_LAST> aSet(*mxItemSet->GetPool() );
+ aSet.Put(pScene->GetProperties().GetObjectItemSet());
+ mxItemSet->Put(aSet);
+ }
+
+ // call parent
+ return E3dProperties::GetMergedItemSet();
+ }
+
+ void E3dCompoundProperties::SetMergedItemSet(const SfxItemSet& rSet, bool bClearAllItems)
+ {
+ // Set scene specific items at scene
+ E3dCompoundObject& rObj = static_cast<E3dCompoundObject&>(GetSdrObject());
+ E3dScene* pScene(rObj.getRootE3dSceneFromE3dObject());
+
+ if(nullptr != pScene)
+ {
+ // force ItemSet
+ GetObjectItemSet();
+
+ // Generate filtered scene properties (SDRATTR_3DSCENE_) itemset
+ SfxItemSetFixed<SDRATTR_3DSCENE_FIRST, SDRATTR_3DSCENE_LAST> aSet(*mxItemSet->GetPool());
+ aSet.Put(rSet);
+
+ if(bClearAllItems)
+ {
+ pScene->GetProperties().ClearObjectItem();
+ }
+
+ if(aSet.Count())
+ {
+ pScene->GetProperties().SetObjectItemSet(aSet);
+ }
+ }
+
+ // call parent. This will set items on local object, too.
+ E3dProperties::SetMergedItemSet(rSet, bClearAllItems);
+ }
+
+ void E3dCompoundProperties::PostItemChange(const sal_uInt16 nWhich)
+ {
+ // call parent
+ E3dProperties::PostItemChange(nWhich);
+
+ // handle value change
+ E3dCompoundObject& rObj = static_cast<E3dCompoundObject&>(GetSdrObject());
+
+ switch(nWhich)
+ {
+ // #i28528#
+ // Added extra Item (Bool) for chart2 to be able to show reduced line geometry
+ case SDRATTR_3DOBJ_REDUCED_LINE_GEOMETRY:
+ {
+ rObj.ActionChanged();
+ break;
+ }
+ case SDRATTR_3DOBJ_DOUBLE_SIDED:
+ {
+ rObj.ActionChanged();
+ break;
+ }
+ case SDRATTR_3DOBJ_NORMALS_KIND:
+ {
+ rObj.ActionChanged();
+ break;
+ }
+ case SDRATTR_3DOBJ_NORMALS_INVERT:
+ {
+ rObj.ActionChanged();
+ break;
+ }
+ case SDRATTR_3DOBJ_TEXTURE_PROJ_X:
+ {
+ rObj.ActionChanged();
+ break;
+ }
+ case SDRATTR_3DOBJ_TEXTURE_PROJ_Y:
+ {
+ rObj.ActionChanged();
+ break;
+ }
+ }
+ }
+} // end of namespace
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/sdr/properties/e3dextrudeproperties.cxx b/svx/source/sdr/properties/e3dextrudeproperties.cxx
new file mode 100644
index 000000000..9b9ffd8be
--- /dev/null
+++ b/svx/source/sdr/properties/e3dextrudeproperties.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 <sdr/properties/e3dextrudeproperties.hxx>
+#include <extrud3d.hxx>
+
+
+namespace sdr::properties
+{
+ E3dExtrudeProperties::E3dExtrudeProperties(SdrObject& rObj)
+ : E3dCompoundProperties(rObj)
+ {
+ }
+
+ E3dExtrudeProperties::E3dExtrudeProperties(const E3dExtrudeProperties& rProps, SdrObject& rObj)
+ : E3dCompoundProperties(rProps, rObj)
+ {
+ }
+
+ E3dExtrudeProperties::~E3dExtrudeProperties()
+ {
+ }
+
+ std::unique_ptr<BaseProperties> E3dExtrudeProperties::Clone(SdrObject& rObj) const
+ {
+ return std::unique_ptr<BaseProperties>(new E3dExtrudeProperties(*this, rObj));
+ }
+
+ void E3dExtrudeProperties::PostItemChange(const sal_uInt16 nWhich)
+ {
+ // call parent
+ E3dCompoundProperties::PostItemChange(nWhich);
+
+ // handle value change
+ E3dExtrudeObj& rObj = static_cast<E3dExtrudeObj&>(GetSdrObject());
+
+ switch(nWhich)
+ {
+ case SDRATTR_3DOBJ_PERCENT_DIAGONAL:
+ {
+ rObj.ActionChanged();
+ break;
+ }
+ case SDRATTR_3DOBJ_BACKSCALE:
+ {
+ rObj.ActionChanged();
+ break;
+ }
+ case SDRATTR_3DOBJ_DEPTH:
+ {
+ rObj.ActionChanged();
+ break;
+ }
+ }
+ }
+} // end of namespace
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/sdr/properties/e3dlatheproperties.cxx b/svx/source/sdr/properties/e3dlatheproperties.cxx
new file mode 100644
index 000000000..feb546534
--- /dev/null
+++ b/svx/source/sdr/properties/e3dlatheproperties.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 <sdr/properties/e3dlatheproperties.hxx>
+#include <svx/lathe3d.hxx>
+
+
+namespace sdr::properties
+{
+ E3dLatheProperties::E3dLatheProperties(SdrObject& rObj)
+ : E3dCompoundProperties(rObj)
+ {
+ }
+
+ E3dLatheProperties::E3dLatheProperties(const E3dLatheProperties& rProps, SdrObject& rObj)
+ : E3dCompoundProperties(rProps, rObj)
+ {
+ }
+
+ E3dLatheProperties::~E3dLatheProperties()
+ {
+ }
+
+ std::unique_ptr<BaseProperties> E3dLatheProperties::Clone(SdrObject& rObj) const
+ {
+ return std::unique_ptr<BaseProperties>(new E3dLatheProperties(*this, rObj));
+ }
+
+ void E3dLatheProperties::PostItemChange(const sal_uInt16 nWhich)
+ {
+ // call parent
+ E3dCompoundProperties::PostItemChange(nWhich);
+
+ // handle value change
+ E3dLatheObj& rObj = static_cast<E3dLatheObj&>(GetSdrObject());
+
+ switch(nWhich)
+ {
+ case SDRATTR_3DOBJ_HORZ_SEGS:
+ {
+ rObj.ActionChanged();
+ break;
+ }
+ case SDRATTR_3DOBJ_VERT_SEGS:
+ {
+ rObj.ActionChanged();
+ break;
+ }
+ case SDRATTR_3DOBJ_PERCENT_DIAGONAL:
+ {
+ rObj.ActionChanged();
+ break;
+ }
+ case SDRATTR_3DOBJ_BACKSCALE:
+ {
+ rObj.ActionChanged();
+ break;
+ }
+ case SDRATTR_3DOBJ_END_ANGLE:
+ {
+ rObj.ActionChanged();
+ break;
+ }
+ }
+ }
+} // end of namespace
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/sdr/properties/e3dproperties.cxx b/svx/source/sdr/properties/e3dproperties.cxx
new file mode 100644
index 000000000..946f879de
--- /dev/null
+++ b/svx/source/sdr/properties/e3dproperties.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 <sdr/properties/e3dproperties.hxx>
+#include <svl/itemset.hxx>
+#include <svx/svddef.hxx>
+#include <svx/obj3d.hxx>
+
+
+namespace sdr::properties
+{
+ // create a new itemset
+ SfxItemSet E3dProperties::CreateObjectSpecificItemSet(SfxItemPool& rPool)
+ {
+ return SfxItemSet(rPool,
+
+ // ranges from SdrAttrObj
+ svl::Items<SDRATTR_START, SDRATTR_SHADOW_LAST,
+ SDRATTR_MISC_FIRST, SDRATTR_MISC_LAST,
+ SDRATTR_TEXTDIRECTION, SDRATTR_TEXTDIRECTION,
+
+ // ranges from E3dObject, contains object and scene because of GetMergedItemSet()
+ SDRATTR_3D_FIRST, SDRATTR_3D_LAST>);
+ }
+
+ E3dProperties::E3dProperties(SdrObject& rObj)
+ : AttributeProperties(rObj)
+ {
+ }
+
+ E3dProperties::E3dProperties(const E3dProperties& rProps, SdrObject& rObj)
+ : AttributeProperties(rProps, rObj)
+ {
+ }
+
+ E3dProperties::~E3dProperties()
+ {
+ }
+
+ std::unique_ptr<BaseProperties> E3dProperties::Clone(SdrObject& rObj) const
+ {
+ return std::unique_ptr<BaseProperties>(new E3dProperties(*this, rObj));
+ }
+
+ void E3dProperties::ItemSetChanged(o3tl::span< const SfxPoolItem* const > aChangedItems, sal_uInt16 nDeletedWhich)
+ {
+ E3dObject& rObj = static_cast<E3dObject&>(GetSdrObject());
+
+ // call parent
+ AttributeProperties::ItemSetChanged(aChangedItems, nDeletedWhich);
+
+ // local changes
+ rObj.StructureChanged();
+ }
+} // end of namespace
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/sdr/properties/e3dsceneproperties.cxx b/svx/source/sdr/properties/e3dsceneproperties.cxx
new file mode 100644
index 000000000..c156db33e
--- /dev/null
+++ b/svx/source/sdr/properties/e3dsceneproperties.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 <sdr/properties/e3dsceneproperties.hxx>
+#include <svl/itemset.hxx>
+#include <svl/whiter.hxx>
+#include <svx/svddef.hxx>
+#include <svx/scene3d.hxx>
+#include <osl/diagnose.h>
+#include <memory>
+
+
+namespace sdr::properties
+{
+ E3dSceneProperties::E3dSceneProperties(SdrObject& rObj)
+ : E3dProperties(rObj)
+ {
+ }
+
+ E3dSceneProperties::E3dSceneProperties(const E3dSceneProperties& rProps, SdrObject& rObj)
+ : E3dProperties(rProps, rObj)
+ {
+ }
+
+ E3dSceneProperties::~E3dSceneProperties()
+ {
+ }
+
+ std::unique_ptr<BaseProperties> E3dSceneProperties::Clone(SdrObject& rObj) const
+ {
+ return std::unique_ptr<BaseProperties>(new E3dSceneProperties(*this, rObj));
+ }
+
+ const SfxItemSet& E3dSceneProperties::GetMergedItemSet() const
+ {
+ // prepare ItemSet
+ if(mxItemSet)
+ {
+ // filter for SDRATTR_3DSCENE_ items, only keep those items
+ SfxItemSetFixed<SDRATTR_3DSCENE_FIRST, SDRATTR_3DSCENE_LAST> aNew(*mxItemSet->GetPool());
+ aNew.Put(*mxItemSet);
+ mxItemSet->ClearItem();
+ mxItemSet->Put(aNew);
+ }
+ else
+ {
+ // No ItemSet yet, force local ItemSet
+ GetObjectItemSet();
+ }
+
+ // collect all ItemSets of contained 3d objects
+ const SdrObjList* pSub(static_cast<const E3dScene&>(GetSdrObject()).GetSubList());
+ OSL_ENSURE(nullptr != pSub, "Children of SdrObject expected (!)");
+ const size_t nCount(nullptr == pSub ? 0 : pSub->GetObjCount());
+
+ for(size_t a = 0; a < nCount; ++a)
+ {
+ SdrObject* pObj = pSub->GetObj(a);
+
+ if(dynamic_cast<const E3dCompoundObject* >(pObj))
+ {
+ const SfxItemSet& rSet = pObj->GetMergedItemSet();
+ SfxWhichIter aIter(rSet);
+ sal_uInt16 nWhich(aIter.FirstWhich());
+
+ while(nWhich)
+ {
+ // Leave out the SDRATTR_3DSCENE_ range, this would only be double
+ // and always equal.
+ if(nWhich <= SDRATTR_3DSCENE_FIRST || nWhich >= SDRATTR_3DSCENE_LAST)
+ {
+ if(SfxItemState::DONTCARE == aIter.GetItemState(false))
+ {
+ mxItemSet->InvalidateItem(nWhich);
+ }
+ else
+ {
+ mxItemSet->MergeValue(rSet.Get(nWhich), true);
+ }
+ }
+
+ nWhich = aIter.NextWhich();
+ }
+ }
+ }
+
+ // call parent
+ return E3dProperties::GetMergedItemSet();
+ }
+
+ void E3dSceneProperties::SetMergedItemSet(const SfxItemSet& rSet, bool bClearAllItems)
+ {
+ // Set SDRATTR_3DOBJ_ range at contained objects.
+ const SdrObjList* pSub(static_cast<const E3dScene&>(GetSdrObject()).GetSubList());
+ OSL_ENSURE(nullptr != pSub, "Children of SdrObject expected (!)");
+ const size_t nCount(nullptr == pSub ? 0 : pSub->GetObjCount());
+
+ if(nCount)
+ {
+ // Generate filtered ItemSet which contains all but the SDRATTR_3DSCENE items.
+ // #i50808# Leak fix, Clone produces a new instance and we get ownership here
+ SfxItemSet aNewSet(rSet.CloneAsValue());
+
+ for(sal_uInt16 b(SDRATTR_3DSCENE_FIRST); b <= SDRATTR_3DSCENE_LAST; b++)
+ {
+ aNewSet.ClearItem(b);
+ }
+
+ if(aNewSet.Count())
+ {
+ for(size_t a = 0; a < nCount; ++a)
+ {
+ SdrObject* pObj = pSub->GetObj(a);
+
+ if(dynamic_cast<const E3dCompoundObject* >(pObj))
+ {
+ // set merged ItemSet at contained 3d object.
+ pObj->SetMergedItemSet(aNewSet, bClearAllItems);
+ }
+ }
+ }
+ }
+
+ // call parent. This will set items on local object, too.
+ E3dProperties::SetMergedItemSet(rSet, bClearAllItems);
+ }
+
+ void E3dSceneProperties::SetMergedItem(const SfxPoolItem& rItem)
+ {
+ const SdrObjList* pSub(static_cast<const E3dScene&>(GetSdrObject()).GetSubList());
+ OSL_ENSURE(nullptr != pSub, "Children of SdrObject expected (!)");
+ const size_t nCount(nullptr == pSub ? 0 : pSub->GetObjCount());
+
+ for(size_t a = 0; a < nCount; ++a)
+ {
+ pSub->GetObj(a)->SetMergedItem(rItem);
+ }
+
+ // #i43809# call parent. This will set items on local object, too.
+ E3dProperties::SetMergedItem(rItem);
+ }
+
+ void E3dSceneProperties::ClearMergedItem(const sal_uInt16 nWhich)
+ {
+ const SdrObjList* pSub(static_cast<const E3dScene&>(GetSdrObject()).GetSubList());
+ OSL_ENSURE(nullptr != pSub, "Children of SdrObject expected (!)");
+ const size_t nCount(nullptr == pSub ? 0 : pSub->GetObjCount());
+
+ for(size_t a = 0; a < nCount; ++a)
+ {
+ pSub->GetObj(a)->ClearMergedItem(nWhich);
+ }
+
+ // #i43809# call parent. This will clear items on local object, too.
+ E3dProperties::ClearMergedItem(nWhich);
+ }
+
+ void E3dSceneProperties::PostItemChange(const sal_uInt16 nWhich)
+ {
+ // call parent
+ E3dProperties::PostItemChange(nWhich);
+
+ // local changes
+ E3dScene& rObj = static_cast<E3dScene&>(GetSdrObject());
+ rObj.StructureChanged();
+
+ switch(nWhich)
+ {
+ case SDRATTR_3DSCENE_PERSPECTIVE :
+ case SDRATTR_3DSCENE_DISTANCE :
+ case SDRATTR_3DSCENE_FOCAL_LENGTH :
+ {
+ // #83387#, #83391#
+ // one common function for the camera attributes
+ // since SetCamera() sets all three back to the ItemSet
+ Camera3D aSceneCam(rObj.GetCamera());
+ bool bChange(false);
+
+ // for SDRATTR_3DSCENE_PERSPECTIVE:
+ if(aSceneCam.GetProjection() != rObj.GetPerspective())
+ {
+ aSceneCam.SetProjection(rObj.GetPerspective());
+ bChange = true;
+ }
+
+ // for SDRATTR_3DSCENE_DISTANCE:
+ basegfx::B3DPoint aActualPosition(aSceneCam.GetPosition());
+ double fNew = rObj.GetDistance();
+
+ if(fNew != aActualPosition.getZ())
+ {
+ aSceneCam.SetPosition(basegfx::B3DPoint(aActualPosition.getX(), aActualPosition.getY(), fNew));
+ bChange = true;
+ }
+
+ // for SDRATTR_3DSCENE_FOCAL_LENGTH:
+ fNew = rObj.GetFocalLength() / 100.0;
+
+ if(aSceneCam.GetFocalLength() != fNew)
+ {
+ aSceneCam.SetFocalLength(fNew);
+ bChange = true;
+ }
+
+ // for all
+ if(bChange)
+ {
+ rObj.SetCamera(aSceneCam);
+ }
+
+ break;
+ }
+ }
+ }
+
+ void E3dSceneProperties::SetStyleSheet(SfxStyleSheet* pNewStyleSheet, bool bDontRemoveHardAttr,
+ bool bBroadcast)
+ {
+ const SdrObjList* pSub(static_cast<const E3dScene&>(GetSdrObject()).GetSubList());
+ OSL_ENSURE(nullptr != pSub, "Children of SdrObject expected (!)");
+ const size_t nCount(nullptr == pSub ? 0 : pSub->GetObjCount());
+
+ for(size_t a = 0; a < nCount; ++a)
+ {
+ if(bBroadcast)
+ pSub->GetObj(a)->SetStyleSheet(pNewStyleSheet, bDontRemoveHardAttr);
+ else
+ pSub->GetObj(a)->NbcSetStyleSheet(pNewStyleSheet, bDontRemoveHardAttr);
+ }
+ }
+
+ SfxStyleSheet* E3dSceneProperties::GetStyleSheet() const
+ {
+ SfxStyleSheet* pRetval = nullptr;
+
+ const SdrObjList* pSub(static_cast<const E3dScene&>(GetSdrObject()).GetSubList());
+ OSL_ENSURE(nullptr != pSub, "Children of SdrObject expected (!)");
+ const size_t nCount(nullptr == pSub ? 0 : pSub->GetObjCount());
+
+ for(size_t a = 0; a < nCount; ++a)
+ {
+ SfxStyleSheet* pCandidate = pSub->GetObj(a)->GetStyleSheet();
+
+ if(pRetval)
+ {
+ if(pCandidate != pRetval)
+ {
+ // different StyleSheelts, return none
+ return nullptr;
+ }
+ }
+ else
+ {
+ pRetval = pCandidate;
+ }
+ }
+
+ return pRetval;
+ }
+
+ void E3dSceneProperties::SetSceneItemsFromCamera()
+ {
+ // force ItemSet
+ GetObjectItemSet();
+
+ E3dScene& rObj = static_cast<E3dScene&>(GetSdrObject());
+ const Camera3D& aSceneCam(rObj.GetCamera());
+
+ // ProjectionType
+ mxItemSet->Put(Svx3DPerspectiveItem(aSceneCam.GetProjection()));
+
+ // CamPos
+ mxItemSet->Put(makeSvx3DDistanceItem(static_cast<sal_uInt32>(aSceneCam.GetPosition().getZ() + 0.5)));
+
+ // FocalLength
+ mxItemSet->Put(makeSvx3DFocalLengthItem(static_cast<sal_uInt32>((aSceneCam.GetFocalLength() * 100.0) + 0.5)));
+ }
+} // end of namespace
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/sdr/properties/e3dsphereproperties.cxx b/svx/source/sdr/properties/e3dsphereproperties.cxx
new file mode 100644
index 000000000..66f01748f
--- /dev/null
+++ b/svx/source/sdr/properties/e3dsphereproperties.cxx
@@ -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 .
+ */
+
+#include <sdr/properties/e3dsphereproperties.hxx>
+#include <svx/sphere3d.hxx>
+
+
+namespace sdr::properties
+{
+ E3dSphereProperties::E3dSphereProperties(SdrObject& rObj)
+ : E3dCompoundProperties(rObj)
+ {
+ }
+
+ E3dSphereProperties::E3dSphereProperties(const E3dSphereProperties& rProps, SdrObject& rObj)
+ : E3dCompoundProperties(rProps, rObj)
+ {
+ }
+
+ E3dSphereProperties::~E3dSphereProperties()
+ {
+ }
+
+ std::unique_ptr<BaseProperties> E3dSphereProperties::Clone(SdrObject& rObj) const
+ {
+ return std::unique_ptr<BaseProperties>(new E3dSphereProperties(*this, rObj));
+ }
+
+ void E3dSphereProperties::PostItemChange(const sal_uInt16 nWhich)
+ {
+ // call parent
+ E3dCompoundProperties::PostItemChange(nWhich);
+
+ // handle value change
+ E3dSphereObj& rObj = static_cast<E3dSphereObj&>(GetSdrObject());
+
+ switch(nWhich)
+ {
+ case SDRATTR_3DOBJ_HORZ_SEGS:
+ {
+ rObj.ActionChanged();
+ break;
+ }
+ case SDRATTR_3DOBJ_VERT_SEGS:
+ {
+ rObj.ActionChanged();
+ break;
+ }
+ }
+ }
+} // end of namespace
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/sdr/properties/emptyproperties.cxx b/svx/source/sdr/properties/emptyproperties.cxx
new file mode 100644
index 000000000..1cad150b2
--- /dev/null
+++ b/svx/source/sdr/properties/emptyproperties.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 <sal/config.h>
+
+#include <sdr/properties/emptyproperties.hxx>
+#include <svl/itemset.hxx>
+#include <svx/svdobj.hxx>
+
+
+namespace sdr::properties
+{
+ EmptyProperties::EmptyProperties(SdrObject& rObj)
+ : BaseProperties(rObj)
+ {
+ }
+
+ std::unique_ptr<BaseProperties> EmptyProperties::Clone(SdrObject& rObj) const
+ {
+ return std::unique_ptr<BaseProperties>(new EmptyProperties(rObj));
+ }
+
+ const SfxItemSet& EmptyProperties::GetObjectItemSet() const
+ {
+ assert(!"EmptyProperties::GetObjectItemSet() should never be called");
+ abort();
+ }
+
+ SfxItemSet EmptyProperties::CreateObjectSpecificItemSet(SfxItemPool&)
+ {
+ assert(!"EmptyProperties::CreateObjectSpecificItemSet() should never be called");
+ abort();
+ }
+
+ void EmptyProperties::SetObjectItem(const SfxPoolItem& /*rItem*/)
+ {
+ assert(!"EmptyProperties::SetObjectItem() should never be called");
+ }
+
+ void EmptyProperties::SetObjectItemDirect(const SfxPoolItem& /*rItem*/)
+ {
+ assert(!"EmptyProperties::SetObjectItemDirect() should never be called");
+ }
+
+ void EmptyProperties::ClearObjectItem(const sal_uInt16 /*nWhich*/)
+ {
+ assert(!"EmptyProperties::ClearObjectItem() should never be called");
+ }
+
+ void EmptyProperties::ClearObjectItemDirect(const sal_uInt16 /*nWhich*/)
+ {
+ assert(!"EmptyProperties::ClearObjectItemDirect() should never be called");
+ }
+
+ void EmptyProperties::SetObjectItemSet(const SfxItemSet& /*rSet*/)
+ {
+ assert(!"EmptyProperties::SetObjectItemSet() should never be called");
+ }
+
+ void EmptyProperties::SetStyleSheet(SfxStyleSheet* /*pNewStyleSheet*/, bool /*bDontRemoveHardAttr*/,
+ bool /*bBroadcast*/)
+ {
+ assert(!"EmptyProperties::SetStyleSheet() should never be called");
+ }
+
+ SfxStyleSheet* EmptyProperties::GetStyleSheet() const
+ {
+ assert(!"EmptyProperties::GetStyleSheet() should never be called");
+ return nullptr;
+ }
+} // end of namespace
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/sdr/properties/graphicproperties.cxx b/svx/source/sdr/properties/graphicproperties.cxx
new file mode 100644
index 000000000..be9b87800
--- /dev/null
+++ b/svx/source/sdr/properties/graphicproperties.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 <sal/config.h>
+
+#include <sdr/properties/graphicproperties.hxx>
+#include <svl/itemset.hxx>
+#include <svl/style.hxx>
+#include <svx/svddef.hxx>
+#include <editeng/eeitem.hxx>
+#include <svx/svdograf.hxx>
+#include <svx/sdgcpitm.hxx>
+#include <svx/svdmodel.hxx>
+#include <svx/sdgluitm.hxx>
+#include <sdgcoitm.hxx>
+#include <svx/sdggaitm.hxx>
+#include <sdgtritm.hxx>
+#include <sdginitm.hxx>
+#include <svx/sdgmoitm.hxx>
+#include <svx/xfillit0.hxx>
+#include <svx/xlineit0.hxx>
+
+namespace sdr::properties
+{
+ void GraphicProperties::applyDefaultStyleSheetFromSdrModel()
+ {
+ SfxStyleSheet* pStyleSheet(GetSdrObject().getSdrModelFromSdrObject().GetDefaultStyleSheetForSdrGrafObjAndSdrOle2Obj());
+
+ if(pStyleSheet)
+ {
+ // do not delete hard attributes when setting dsefault Style
+ SetStyleSheet(pStyleSheet, true, true);
+ }
+ else
+ {
+ SetMergedItem(XFillStyleItem(com::sun::star::drawing::FillStyle_NONE));
+ SetMergedItem(XLineStyleItem(com::sun::star::drawing::LineStyle_NONE));
+ }
+ }
+
+ // create a new itemset
+ SfxItemSet GraphicProperties::CreateObjectSpecificItemSet(SfxItemPool& rPool)
+ {
+ return SfxItemSet(rPool,
+
+ // range from SdrAttrObj
+ svl::Items<SDRATTR_START, SDRATTR_SHADOW_LAST,
+ SDRATTR_MISC_FIRST, SDRATTR_MISC_LAST,
+ SDRATTR_TEXTDIRECTION, SDRATTR_TEXTDIRECTION,
+
+ // range from SdrGrafObj
+ SDRATTR_GRAF_FIRST, SDRATTR_GRAF_LAST,
+
+ SDRATTR_GLOW_FIRST, SDRATTR_SOFTEDGE_LAST,
+ SDRATTR_TEXTCOLUMNS_FIRST, SDRATTR_TEXTCOLUMNS_LAST,
+
+ // range from SdrTextObj
+ EE_ITEMS_START, EE_ITEMS_END>);
+ }
+
+ GraphicProperties::GraphicProperties(SdrObject& rObj)
+ : RectangleProperties(rObj)
+ {
+ }
+
+ GraphicProperties::GraphicProperties(const GraphicProperties& rProps, SdrObject& rObj)
+ : RectangleProperties(rProps, rObj)
+ {
+ }
+
+ GraphicProperties::~GraphicProperties()
+ {
+ }
+
+ std::unique_ptr<BaseProperties> GraphicProperties::Clone(SdrObject& rObj) const
+ {
+ return std::unique_ptr<BaseProperties>(new GraphicProperties(*this, rObj));
+ }
+
+ void GraphicProperties::ItemSetChanged(o3tl::span< const SfxPoolItem* const > aChangedItems, sal_uInt16 nDeletedWhich)
+ {
+ SdrGrafObj& rObj = static_cast<SdrGrafObj&>(GetSdrObject());
+
+ // local changes
+ rObj.SetXPolyDirty();
+
+ // #i29367# Update GraphicAttr, too. This was formerly
+ // triggered by SdrGrafObj::Notify, which is no longer
+ // called nowadays. BTW: strictly speaking, the whole
+ // ImpSetAttrToGrafInfostuff could
+ // be dumped, when SdrGrafObj::aGrafInfo is removed and
+ // always created on the fly for repaint.
+ rObj.ImpSetAttrToGrafInfo();
+
+ // call parent
+ RectangleProperties::ItemSetChanged(aChangedItems, nDeletedWhich);
+ }
+
+ void GraphicProperties::SetStyleSheet(SfxStyleSheet* pNewStyleSheet, bool bDontRemoveHardAttr,
+ bool bBroadcast)
+ {
+ // call parent (always first thing to do, may create the SfxItemSet)
+ RectangleProperties::SetStyleSheet(pNewStyleSheet, bDontRemoveHardAttr, bBroadcast);
+
+ // local changes
+ SdrGrafObj& rObj = static_cast<SdrGrafObj&>(GetSdrObject());
+ rObj.SetXPolyDirty();
+
+ // local changes
+ rObj.ImpSetAttrToGrafInfo();
+ }
+
+ void GraphicProperties::ForceDefaultAttributes()
+ {
+ // call parent
+ RectangleProperties::ForceDefaultAttributes();
+
+ // force ItemSet
+ GetObjectItemSet();
+
+ mxItemSet->Put( SdrGrafLuminanceItem( 0 ) );
+ mxItemSet->Put( SdrGrafContrastItem( 0 ) );
+ mxItemSet->Put( SdrGrafRedItem( 0 ) );
+ mxItemSet->Put( SdrGrafGreenItem( 0 ) );
+ mxItemSet->Put( SdrGrafBlueItem( 0 ) );
+ mxItemSet->Put( SdrGrafGamma100Item( 100 ) );
+ mxItemSet->Put( SdrGrafTransparenceItem( 0 ) );
+ mxItemSet->Put( SdrGrafInvertItem( false ) );
+ mxItemSet->Put( SdrGrafModeItem( GraphicDrawMode::Standard ) );
+ mxItemSet->Put( SdrGrafCropItem( 0, 0, 0, 0 ) );
+ }
+} // end of namespace
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/sdr/properties/groupproperties.cxx b/svx/source/sdr/properties/groupproperties.cxx
new file mode 100644
index 000000000..6ec522aa3
--- /dev/null
+++ b/svx/source/sdr/properties/groupproperties.cxx
@@ -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 .
+ */
+
+#include <sal/config.h>
+
+#include <sdr/properties/groupproperties.hxx>
+#include <svl/itemset.hxx>
+#include <svl/whiter.hxx>
+#include <svx/svdogrp.hxx>
+#include <svx/svdpage.hxx>
+#include <osl/diagnose.h>
+
+
+namespace sdr::properties
+{
+ GroupProperties::GroupProperties(SdrObject& rObj)
+ : BaseProperties(rObj)
+ {
+ }
+
+ GroupProperties::~GroupProperties()
+ {
+ }
+
+ std::unique_ptr<BaseProperties> GroupProperties::Clone(SdrObject& rObj) const
+ {
+ return std::unique_ptr<BaseProperties>(new GroupProperties(rObj));
+ }
+
+ SfxItemSet GroupProperties::CreateObjectSpecificItemSet(SfxItemPool& rPool)
+ {
+ return SfxItemSet(rPool);
+ }
+
+ const SfxItemSet& GroupProperties::GetObjectItemSet() const
+ {
+ assert(!"GroupProperties::GetObjectItemSet() should never be called");
+ abort();
+ }
+
+ const SfxItemSet& GroupProperties::GetMergedItemSet() const
+ {
+ // prepare ItemSet
+ if(mxItemSet)
+ // clear local itemset for merge
+ mxItemSet->ClearItem();
+ else if(!mxItemSet)
+ // force local itemset
+ mxItemSet.emplace(GetSdrObject().GetObjectItemPool());
+
+ // collect all ItemSets in mpItemSet
+ const SdrObjList* pSub(static_cast<const SdrObjGroup&>(GetSdrObject()).GetSubList());
+ OSL_ENSURE(nullptr != pSub, "Children of SdrObject expected (!)");
+ const size_t nCount(nullptr == pSub ? 0 : pSub->GetObjCount());
+
+ for(size_t a = 0; a < nCount; ++a)
+ {
+ const SfxItemSet& rSet = pSub->GetObj(a)->GetMergedItemSet();
+ SfxWhichIter aIter(rSet);
+ sal_uInt16 nWhich(aIter.FirstWhich());
+
+ while(nWhich)
+ {
+ if(SfxItemState::DONTCARE == aIter.GetItemState(false))
+ {
+ mxItemSet->InvalidateItem(nWhich);
+ }
+ else
+ {
+ mxItemSet->MergeValue(rSet.Get(nWhich), true);
+ }
+
+ nWhich = aIter.NextWhich();
+ }
+ }
+
+ // For group properties, do not call parent since groups do
+ // not have local ItemSets.
+ return *mxItemSet;
+ }
+
+ void GroupProperties::SetMergedItemSet(const SfxItemSet& rSet, bool bClearAllItems)
+ {
+ // iterate over contained SdrObjects
+ const SdrObjList* pSub(static_cast<const SdrObjGroup&>(GetSdrObject()).GetSubList());
+ OSL_ENSURE(nullptr != pSub, "Children of SdrObject expected (!)");
+ const size_t nCount(nullptr == pSub ? 0 : pSub->GetObjCount());
+
+ for(size_t a = 0; a < nCount; ++a)
+ {
+ SdrObject* pObj = pSub->GetObj(a);
+
+ if(pObj)
+ {
+ // Set merged ItemSet at contained object
+ pObj->SetMergedItemSet(rSet, bClearAllItems);
+ }
+ }
+
+ // Do not call parent here. Group objects do not have local ItemSets
+ // where items need to be set.
+ }
+
+ void GroupProperties::SetObjectItem(const SfxPoolItem& /*rItem*/)
+ {
+ assert(!"GroupProperties::SetObjectItem() should never be called");
+ }
+
+ void GroupProperties::SetObjectItemDirect(const SfxPoolItem& /*rItem*/)
+ {
+ assert(!"GroupProperties::SetObjectItemDirect() should never be called");
+ }
+
+ void GroupProperties::ClearObjectItem(const sal_uInt16 nWhich)
+ {
+ // iterate over contained SdrObjects
+ const SdrObjList* pSub(static_cast<const SdrObjGroup&>(GetSdrObject()).GetSubList());
+ OSL_ENSURE(nullptr != pSub, "Children of SdrObject expected (!)");
+ const size_t nCount(nullptr == pSub ? 0 : pSub->GetObjCount());
+
+ for(size_t a = 0; a < nCount; ++a)
+ {
+ SdrObject* pObj = pSub->GetObj(a);
+
+ if(pObj)
+ {
+ pObj->GetProperties().ClearObjectItem(nWhich);
+ }
+ }
+ }
+
+ void GroupProperties::ClearObjectItemDirect(const sal_uInt16 /*nWhich*/)
+ {
+ assert(!"GroupProperties::ClearObjectItemDirect() should never be called");
+ }
+
+ void GroupProperties::SetMergedItem(const SfxPoolItem& rItem)
+ {
+ const SdrObjList* pSub(static_cast<const SdrObjGroup&>(GetSdrObject()).GetSubList());
+ OSL_ENSURE(nullptr != pSub, "Children of SdrObject expected (!)");
+ const size_t nCount(nullptr == pSub ? 0 : pSub->GetObjCount());
+
+ for(size_t a = 0; a < nCount; ++a)
+ {
+ pSub->GetObj(a)->GetProperties().SetMergedItem(rItem);
+ }
+ }
+
+ void GroupProperties::ClearMergedItem(const sal_uInt16 nWhich)
+ {
+ const SdrObjList* pSub(static_cast<const SdrObjGroup&>(GetSdrObject()).GetSubList());
+ OSL_ENSURE(nullptr != pSub, "Children of SdrObject expected (!)");
+ const size_t nCount(nullptr == pSub ? 0 : pSub->GetObjCount());
+
+ for(size_t a = 0; a < nCount; ++a)
+ {
+ pSub->GetObj(a)->GetProperties().ClearMergedItem(nWhich);
+ }
+ }
+
+ void GroupProperties::SetObjectItemSet(const SfxItemSet& /*rSet*/)
+ {
+ assert(!"GroupProperties::SetObjectItemSet() should never be called");
+ }
+
+ SfxStyleSheet* GroupProperties::GetStyleSheet() const
+ {
+ SfxStyleSheet* pRetval = nullptr;
+
+ const SdrObjList* pSub(static_cast<const SdrObjGroup&>(GetSdrObject()).GetSubList());
+ OSL_ENSURE(nullptr != pSub, "Children of SdrObject expected (!)");
+ const size_t nCount(nullptr == pSub ? 0 : pSub->GetObjCount());
+
+ for(size_t a = 0; a < nCount; ++a)
+ {
+ SfxStyleSheet* pCandidate = pSub->GetObj(a)->GetStyleSheet();
+
+ if(pRetval)
+ {
+ if(pCandidate != pRetval)
+ {
+ // different StyleSheelts, return none
+ return nullptr;
+ }
+ }
+ else
+ {
+ pRetval = pCandidate;
+ }
+ }
+
+ return pRetval;
+ }
+
+ void GroupProperties::SetStyleSheet(SfxStyleSheet* pNewStyleSheet, bool bDontRemoveHardAttr,
+ bool bBroadcast)
+ {
+ const SdrObjList* pSub(static_cast<const SdrObjGroup&>(GetSdrObject()).GetSubList());
+ OSL_ENSURE(nullptr != pSub, "Children of SdrObject expected (!)");
+ const size_t nCount(nullptr == pSub ? 0 : pSub->GetObjCount());
+
+ for(size_t a = 0; a < nCount; ++a)
+ {
+ if(bBroadcast)
+ pSub->GetObj(a)->SetStyleSheet(pNewStyleSheet, bDontRemoveHardAttr);
+ else
+ pSub->GetObj(a)->NbcSetStyleSheet(pNewStyleSheet, bDontRemoveHardAttr);
+ }
+ }
+
+ void GroupProperties::ForceStyleToHardAttributes()
+ {
+ const SdrObjList* pSub(static_cast<const SdrObjGroup&>(GetSdrObject()).GetSubList());
+ OSL_ENSURE(nullptr != pSub, "Children of SdrObject expected (!)");
+ const size_t nCount(nullptr == pSub ? 0 : pSub->GetObjCount());
+
+ for(size_t a = 0; a < nCount; ++a)
+ {
+ pSub->GetObj(a)->GetProperties().ForceStyleToHardAttributes();
+ }
+ }
+} // end of namespace
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/sdr/properties/itemsettools.cxx b/svx/source/sdr/properties/itemsettools.cxx
new file mode 100644
index 000000000..11431d367
--- /dev/null
+++ b/svx/source/sdr/properties/itemsettools.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 <sdr/properties/itemsettools.hxx>
+#include <tools/fract.hxx>
+#include <svl/itemset.hxx>
+#include <svl/whiter.hxx>
+#include <svx/svdogrp.hxx>
+#include <svx/svditer.hxx>
+#include <memory>
+
+// class to remember broadcast start positions
+
+namespace sdr::properties
+{
+ ItemChangeBroadcaster::ItemChangeBroadcaster(const SdrObject& rObj)
+ {
+ if (rObj.GetObjIdentifier() == SdrObjKind::Group)
+ {
+ const SdrObjGroup* pGroupObj = static_cast<const SdrObjGroup*>(&rObj);
+ SdrObjListIter aIter(pGroupObj->GetSubList(), SdrIterMode::DeepNoGroups);
+ maRectangles.reserve(aIter.Count());
+
+ while(aIter.IsMore())
+ {
+ SdrObject* pObj = aIter.Next();
+
+ if(pObj)
+ {
+ maRectangles.push_back(pObj->GetLastBoundRect());
+ }
+ }
+ }
+ else
+ {
+ maRectangles.push_back(rObj.GetLastBoundRect());
+ }
+ }
+
+
+ void ScaleItemSet(SfxItemSet& rSet, const Fraction& rScale)
+ {
+ sal_Int32 nMul(rScale.GetNumerator());
+ sal_Int32 nDiv(rScale.GetDenominator());
+
+ if(!rScale.IsValid() || !nDiv)
+ {
+ return;
+ }
+
+ SfxWhichIter aIter(rSet);
+ sal_uInt16 nWhich(aIter.FirstWhich());
+ const SfxPoolItem *pItem = nullptr;
+
+ while(nWhich)
+ {
+ if(SfxItemState::SET == aIter.GetItemState(false, &pItem))
+ {
+ if(pItem->HasMetrics())
+ {
+ std::unique_ptr<SfxPoolItem> pNewItem(pItem->Clone());
+ pNewItem->ScaleMetrics(nMul, nDiv);
+ rSet.Put(std::move(pNewItem));
+ }
+ }
+ nWhich = aIter.NextWhich();
+ }
+ }
+} // end of namespace
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/sdr/properties/measureproperties.cxx b/svx/source/sdr/properties/measureproperties.cxx
new file mode 100644
index 000000000..07441c385
--- /dev/null
+++ b/svx/source/sdr/properties/measureproperties.cxx
@@ -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 .
+ */
+
+#include <sal/config.h>
+
+#include <sdr/properties/measureproperties.hxx>
+#include <svl/itemset.hxx>
+#include <svl/style.hxx>
+#include <svx/svddef.hxx>
+#include <editeng/eeitem.hxx>
+#include <svx/svdomeas.hxx>
+#include <svx/xlineit0.hxx>
+#include <svx/xlnstit.hxx>
+#include <svx/xlnstwit.hxx>
+#include <svx/xlnedit.hxx>
+#include <svx/xlnedwit.hxx>
+#include <svx/sdynitm.hxx>
+#include <basegfx/point/b2dpoint.hxx>
+#include <basegfx/polygon/b2dpolygon.hxx>
+
+
+namespace sdr::properties
+{
+ // create a new itemset
+ SfxItemSet MeasureProperties::CreateObjectSpecificItemSet(SfxItemPool& rPool)
+ {
+ return SfxItemSet(
+ rPool,
+ svl::Items<
+ // Ranges from SdrAttrObj, SdrMeasureObj:
+ SDRATTR_START, SDRATTR_SHADOW_LAST,
+ SDRATTR_MISC_FIRST, SDRATTR_MISC_LAST,
+ SDRATTR_MEASURE_FIRST, SDRATTR_MEASURE_LAST,
+ SDRATTR_TEXTDIRECTION, SDRATTR_TEXTDIRECTION,
+ SDRATTR_TEXTCOLUMNS_FIRST, SDRATTR_TEXTCOLUMNS_LAST,
+ // Range from SdrTextObj:
+ EE_ITEMS_START, EE_ITEMS_END>);
+ }
+
+ MeasureProperties::MeasureProperties(SdrObject& rObj)
+ : TextProperties(rObj)
+ {
+ }
+
+ MeasureProperties::MeasureProperties(const MeasureProperties& rProps, SdrObject& rObj)
+ : TextProperties(rProps, rObj)
+ {
+ }
+
+ MeasureProperties::~MeasureProperties()
+ {
+ }
+
+ std::unique_ptr<BaseProperties> MeasureProperties::Clone(SdrObject& rObj) const
+ {
+ return std::unique_ptr<BaseProperties>(new MeasureProperties(*this, rObj));
+ }
+
+ void MeasureProperties::ItemSetChanged(o3tl::span< const SfxPoolItem* const > aChangedItems, sal_uInt16 nDeletedWhich)
+ {
+ SdrMeasureObj& rObj = static_cast<SdrMeasureObj&>(GetSdrObject());
+
+ // call parent
+ TextProperties::ItemSetChanged(aChangedItems, nDeletedWhich);
+
+ // local changes
+ rObj.SetTextDirty();
+ }
+
+ void MeasureProperties::SetStyleSheet(SfxStyleSheet* pNewStyleSheet, bool bDontRemoveHardAttr,
+ bool bBroadcast)
+ {
+ // call parent (always first thing to do, may create the SfxItemSet)
+ TextProperties::SetStyleSheet(pNewStyleSheet, bDontRemoveHardAttr, bBroadcast);
+
+ // local changes
+ // get access to dimension line object
+ SdrMeasureObj& rObj = static_cast<SdrMeasureObj&>(GetSdrObject());
+
+ // mark dimension line text as changed (dirty) in the dimension line object
+ rObj.SetTextDirty();
+
+ // tdf#98525 ask the dimension line object to redraw the changed text
+ rObj.UndirtyText();
+ }
+
+ void MeasureProperties::ForceDefaultAttributes()
+ {
+ // call parent
+ TextProperties::ForceDefaultAttributes();
+
+ // force ItemSet
+ GetObjectItemSet();
+
+ //#71958# by default, the show units Bool-Item is set as hard
+ // attribute to sal_True to avoid confusion when copying SdrMeasureObj's
+ // from one application to another
+ mxItemSet->Put(SdrYesNoItem(SDRATTR_MEASURESHOWUNIT, true));
+
+ basegfx::B2DPolygon aNewPolygon;
+ aNewPolygon.append(basegfx::B2DPoint(100.0, 0.0));
+ aNewPolygon.append(basegfx::B2DPoint(200.0, 400.0));
+ aNewPolygon.append(basegfx::B2DPoint(0.0, 400.0));
+ aNewPolygon.setClosed(true);
+
+ mxItemSet->Put(XLineStartItem(OUString(), basegfx::B2DPolyPolygon(aNewPolygon)));
+ mxItemSet->Put(XLineStartWidthItem(200));
+ mxItemSet->Put(XLineEndItem(OUString(), basegfx::B2DPolyPolygon(aNewPolygon)));
+ mxItemSet->Put(XLineEndWidthItem(200));
+ mxItemSet->Put(XLineStyleItem(css::drawing::LineStyle_SOLID));
+ }
+} // end of namespace
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/sdr/properties/oleproperties.cxx b/svx/source/sdr/properties/oleproperties.cxx
new file mode 100644
index 000000000..587ff1d3f
--- /dev/null
+++ b/svx/source/sdr/properties/oleproperties.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 <sdr/properties/oleproperties.hxx>
+#include <svx/xfillit0.hxx>
+#include <svx/xlineit0.hxx>
+#include <svx/svdobj.hxx>
+#include <svx/svdmodel.hxx>
+
+namespace sdr::properties
+{
+ void OleProperties::applyDefaultStyleSheetFromSdrModel()
+ {
+ SfxStyleSheet* pStyleSheet(GetSdrObject().getSdrModelFromSdrObject().GetDefaultStyleSheetForSdrGrafObjAndSdrOle2Obj());
+
+ if(pStyleSheet)
+ {
+ // do not delete hard attributes when setting dsefault Style
+ SetStyleSheet(pStyleSheet, true, true);
+ }
+ else
+ {
+ SetMergedItem(XFillStyleItem(com::sun::star::drawing::FillStyle_NONE));
+ SetMergedItem(XLineStyleItem(com::sun::star::drawing::LineStyle_NONE));
+ }
+ }
+
+ OleProperties::OleProperties(SdrObject& rObj)
+ : RectangleProperties(rObj)
+ {
+ }
+
+ OleProperties::OleProperties(const OleProperties& rProps, SdrObject& rObj)
+ : RectangleProperties(rProps, rObj)
+ {
+ }
+
+ OleProperties::~OleProperties()
+ {
+ }
+
+ std::unique_ptr<BaseProperties> OleProperties::Clone(SdrObject& rObj) const
+ {
+ return std::unique_ptr<BaseProperties>(new OleProperties(*this, rObj));
+ }
+
+ void OleProperties::ForceDefaultAttributes()
+ {
+ // call parent
+ RectangleProperties::ForceDefaultAttributes();
+
+ // force ItemSet
+ GetObjectItemSet();
+ }
+} // end of namespace
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/sdr/properties/pageproperties.cxx b/svx/source/sdr/properties/pageproperties.cxx
new file mode 100644
index 000000000..011ab2192
--- /dev/null
+++ b/svx/source/sdr/properties/pageproperties.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 <sal/config.h>
+
+#include <sdr/properties/pageproperties.hxx>
+#include <svl/itemset.hxx>
+#include <svx/svdobj.hxx>
+#include <tools/debug.hxx>
+
+
+namespace sdr::properties
+{
+ PageProperties::PageProperties(SdrObject& rObj)
+ : BaseProperties(rObj)
+ {
+ }
+
+ PageProperties::PageProperties(const PageProperties& /*rProps*/, SdrObject& rObj)
+ : BaseProperties(rObj)
+ {
+ }
+
+ PageProperties::~PageProperties()
+ {
+ }
+
+ std::unique_ptr<BaseProperties> PageProperties::Clone(SdrObject& rObj) const
+ {
+ return std::unique_ptr<BaseProperties>(new PageProperties(*this, rObj));
+ }
+
+ SfxItemSet PageProperties::CreateObjectSpecificItemSet(SfxItemPool& rPool)
+ {
+ return SfxItemSet(rPool);
+ }
+
+ // get itemset. Override here to allow creating the empty itemset
+ // without asserting
+ const SfxItemSet& PageProperties::GetObjectItemSet() const
+ {
+ if(!mxEmptyItemSet)
+ {
+ mxEmptyItemSet.emplace(GetSdrObject().GetObjectItemPool());
+ }
+
+ DBG_ASSERT(mxEmptyItemSet, "Could not create an SfxItemSet(!)");
+
+ return *mxEmptyItemSet;
+ }
+
+ SfxStyleSheet* PageProperties::GetStyleSheet() const
+ {
+ // override to legally return a 0L pointer here
+ return nullptr;
+ }
+
+ void PageProperties::SetStyleSheet(SfxStyleSheet* /*pStyleSheet*/, bool /*bDontRemoveHardAttr*/,
+ bool /*bBroadcast*/)
+ {
+ // override to legally ignore the StyleSheet here
+ }
+
+ void PageProperties::ClearObjectItem(const sal_uInt16 /*nWhich*/)
+ {
+ // simply ignore item clearing on page objects
+ }
+
+ void PageProperties::SetObjectItem(const SfxPoolItem& /*rItem*/)
+ {
+ assert(!"PageProperties::SetObjectItem() should never be called");
+ }
+
+ void PageProperties::SetObjectItemDirect(const SfxPoolItem& /*rItem*/)
+ {
+ assert(!"PageProperties::SetObjectItemDirect() should never be called");
+ }
+
+ void PageProperties::ClearObjectItemDirect(const sal_uInt16 /*nWhich*/)
+ {
+ assert(!"PageProperties::ClearObjectItemDirect() should never be called");
+ }
+
+ void PageProperties::SetObjectItemSet(const SfxItemSet& /*rSet*/)
+ {
+ assert(!"PageProperties::SetObjectItemSet() should never be called");
+ }
+} // end of namespace
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/sdr/properties/properties.cxx b/svx/source/sdr/properties/properties.cxx
new file mode 100644
index 000000000..97a264712
--- /dev/null
+++ b/svx/source/sdr/properties/properties.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 <libxml/xmlwriter.h>
+
+#include <svx/sdr/properties/properties.hxx>
+#include <sdr/properties/itemsettools.hxx>
+#include <svl/itemset.hxx>
+#include <svx/svdogrp.hxx>
+#include <svx/svditer.hxx>
+#include <svx/xdef.hxx>
+#include <svx/xfillit0.hxx>
+
+using namespace com::sun::star;
+
+namespace sdr::properties
+{
+ BaseProperties::BaseProperties(SdrObject& rObj)
+ : mrObject(rObj)
+ {
+ }
+
+ BaseProperties::~BaseProperties()
+ {
+ }
+
+ const SdrObject& BaseProperties::GetSdrObject() const
+ {
+ return mrObject;
+ }
+
+ SdrObject& BaseProperties::GetSdrObject()
+ {
+ return mrObject;
+ }
+
+ const SfxItemSet& BaseProperties::GetMergedItemSet() const
+ {
+ // default implementation falls back to GetObjectItemSet()
+ return GetObjectItemSet();
+ }
+
+ void BaseProperties::SetMergedItemSet(const SfxItemSet& rSet, bool bClearAllItems)
+ {
+ // clear items if requested
+ if(bClearAllItems)
+ {
+ ClearObjectItem();
+ }
+
+ // default implementation falls back to SetObjectItemSet()
+ SetObjectItemSet(rSet);
+ }
+
+ void BaseProperties::SetMergedItem(const SfxPoolItem& rItem)
+ {
+ // default implementation falls back to SetObjectItem()
+ SetObjectItem(rItem);
+ }
+
+ void BaseProperties::ClearMergedItem(const sal_uInt16 nWhich)
+ {
+ // default implementation falls back to ClearObjectItem()
+ ClearObjectItem(nWhich);
+ }
+
+ void BaseProperties::ForceStyleToHardAttributes()
+ {
+ // force all attributes which come from styles to hard attributes
+ // to be able to live without the style. Default implementation does nothing.
+ // Override where an ItemSet is implemented.
+ }
+
+ void BaseProperties::SetMergedItemSetAndBroadcast(const SfxItemSet& rSet, bool bClearAllItems)
+ {
+ ItemChangeBroadcaster aC(GetSdrObject());
+
+ if(bClearAllItems)
+ {
+ ClearObjectItem();
+ }
+
+ SetMergedItemSet(rSet);
+ BroadcastItemChange(aC);
+ }
+
+ const SfxPoolItem& BaseProperties::GetItem(const sal_uInt16 nWhich) const
+ {
+ return GetObjectItemSet().Get(nWhich);
+ }
+
+ void BaseProperties::BroadcastItemChange(const ItemChangeBroadcaster& rChange)
+ {
+ const sal_uInt32 nCount(rChange.GetRectangleCount());
+
+ // invalidate all new rectangles
+ SdrObject* pObj = &GetSdrObject();
+ if (pObj->GetObjIdentifier() == SdrObjKind::Group)
+ {
+ SdrObjGroup* pObjGroup = static_cast<SdrObjGroup*>(pObj);
+ SdrObjListIter aIter(pObjGroup, SdrIterMode::DeepNoGroups);
+
+ while(aIter.IsMore())
+ {
+ SdrObject* pChildObj = aIter.Next();
+ pChildObj->BroadcastObjectChange();
+ }
+ }
+ else
+ {
+ pObj->BroadcastObjectChange();
+ }
+
+ // also send the user calls
+ for(sal_uInt32 a(0); a < nCount; a++)
+ {
+ pObj->SendUserCall(SdrUserCallType::ChangeAttr, rChange.GetRectangle(a));
+ }
+ }
+
+ sal_uInt32 BaseProperties::getVersion() const
+ {
+ return 0;
+ }
+
+ void BaseProperties::dumpAsXml(xmlTextWriterPtr pWriter) const
+ {
+ (void)xmlTextWriterStartElement(pWriter, BAD_CAST("BaseProperties"));
+ (void)xmlTextWriterEndElement(pWriter);
+ }
+
+ void CleanupFillProperties( SfxItemSet& rItemSet )
+ {
+ const bool bFillBitmap = rItemSet.GetItemState(XATTR_FILLBITMAP, false) == SfxItemState::SET;
+ const bool bFillGradient = rItemSet.GetItemState(XATTR_FILLGRADIENT, false) == SfxItemState::SET;
+ const bool bFillHatch = rItemSet.GetItemState(XATTR_FILLHATCH, false) == SfxItemState::SET;
+ if( !(bFillBitmap || bFillGradient || bFillHatch) )
+ return;
+
+ const XFillStyleItem* pFillStyleItem = rItemSet.GetItem(XATTR_FILLSTYLE);
+ if( !pFillStyleItem )
+ return;
+
+ if( bFillBitmap && (pFillStyleItem->GetValue() != drawing::FillStyle_BITMAP) )
+ {
+ rItemSet.ClearItem( XATTR_FILLBITMAP );
+ }
+
+ if( bFillGradient && (pFillStyleItem->GetValue() != drawing::FillStyle_GRADIENT) )
+ {
+ rItemSet.ClearItem( XATTR_FILLGRADIENT );
+ }
+
+ if( bFillHatch && (pFillStyleItem->GetValue() != drawing::FillStyle_HATCH) )
+ {
+ rItemSet.ClearItem( XATTR_FILLHATCH );
+ }
+ }
+
+} // end of namespace
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/sdr/properties/rectangleproperties.cxx b/svx/source/sdr/properties/rectangleproperties.cxx
new file mode 100644
index 000000000..4c3a72a2f
--- /dev/null
+++ b/svx/source/sdr/properties/rectangleproperties.cxx
@@ -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 .
+ */
+
+#include <sdr/properties/rectangleproperties.hxx>
+#include <svx/svdorect.hxx>
+
+
+namespace sdr::properties
+{
+ RectangleProperties::RectangleProperties(SdrObject& rObj)
+ : TextProperties(rObj)
+ {
+ }
+
+ RectangleProperties::RectangleProperties(const RectangleProperties& rProps, SdrObject& rObj)
+ : TextProperties(rProps, rObj)
+ {
+ }
+
+ RectangleProperties::~RectangleProperties()
+ {
+ }
+
+ std::unique_ptr<BaseProperties> RectangleProperties::Clone(SdrObject& rObj) const
+ {
+ return std::unique_ptr<BaseProperties>(new RectangleProperties(*this, rObj));
+ }
+
+ void RectangleProperties::ItemSetChanged(o3tl::span< const SfxPoolItem* const > aChangedItems, sal_uInt16 nDeletedWhich)
+ {
+ SdrRectObj& rObj = static_cast<SdrRectObj&>(GetSdrObject());
+
+ // call parent
+ TextProperties::ItemSetChanged(aChangedItems, nDeletedWhich);
+
+ // local changes
+ rObj.SetXPolyDirty();
+ }
+
+ // set a new StyleSheet and broadcast
+ void RectangleProperties::SetStyleSheet(SfxStyleSheet* pNewStyleSheet, bool bDontRemoveHardAttr,
+ bool bBroadcast)
+ {
+ // call parent (always first thing to do, may create the SfxItemSet)
+ TextProperties::SetStyleSheet(pNewStyleSheet, bDontRemoveHardAttr, bBroadcast);
+
+ // local changes
+ SdrRectObj& rObj = static_cast<SdrRectObj&>(GetSdrObject());
+ rObj.SetXPolyDirty();
+ }
+} // end of namespace
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/sdr/properties/textproperties.cxx b/svx/source/sdr/properties/textproperties.cxx
new file mode 100644
index 000000000..d8cdf80df
--- /dev/null
+++ b/svx/source/sdr/properties/textproperties.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 <sal/config.h>
+
+#include <sdr/properties/textproperties.hxx>
+#include <svl/itemset.hxx>
+#include <svl/style.hxx>
+#include <svl/itemiter.hxx>
+#include <svl/hint.hxx>
+#include <svx/svddef.hxx>
+#include <svx/svdotext.hxx>
+#include <svx/svdoutl.hxx>
+#include <svx/sdmetitm.hxx>
+#include <svx/sdtditm.hxx>
+#include <editeng/writingmodeitem.hxx>
+#include <svx/svdmodel.hxx>
+#include <editeng/eeitem.hxx>
+#include <editeng/outlobj.hxx>
+#include <svx/xfillit0.hxx>
+#include <svx/xflclit.hxx>
+#include <editeng/adjustitem.hxx>
+#include <svx/svdetc.hxx>
+#include <editeng/editeng.hxx>
+#include <editeng/flditem.hxx>
+#include <svx/xlineit0.hxx>
+#include <svx/xlnwtit.hxx>
+
+using namespace com::sun::star;
+
+namespace sdr::properties
+{
+ SfxItemSet TextProperties::CreateObjectSpecificItemSet(SfxItemPool& rPool)
+ {
+ return SfxItemSet(rPool,
+
+ // range from SdrAttrObj
+ svl::Items<SDRATTR_START, SDRATTR_SHADOW_LAST,
+ SDRATTR_MISC_FIRST, SDRATTR_MISC_LAST,
+ SDRATTR_TEXTDIRECTION, SDRATTR_TEXTDIRECTION,
+ SDRATTR_TEXTCOLUMNS_FIRST, SDRATTR_TEXTCOLUMNS_LAST,
+
+ // range from SdrTextObj
+ EE_ITEMS_START, EE_ITEMS_END>);
+ }
+
+ TextProperties::TextProperties(SdrObject& rObj)
+ : AttributeProperties(rObj),
+ maVersion(0)
+ {
+ }
+
+ TextProperties::TextProperties(const TextProperties& rProps, SdrObject& rObj)
+ : AttributeProperties(rProps, rObj),
+ maVersion(rProps.getVersion())
+ {
+ }
+
+ TextProperties::~TextProperties()
+ {
+ }
+
+ std::unique_ptr<BaseProperties> TextProperties::Clone(SdrObject& rObj) const
+ {
+ return std::unique_ptr<BaseProperties>(new TextProperties(*this, rObj));
+ }
+
+ void TextProperties::ItemSetChanged(o3tl::span< const SfxPoolItem* const > aChangedItems, sal_uInt16 nDeletedWhich)
+ {
+ SdrTextObj& rObj = static_cast<SdrTextObj&>(GetSdrObject());
+
+ // #i101556# ItemSet has changed -> new version
+ maVersion++;
+
+ if (auto pOutliner = rObj.GetTextEditOutliner())
+ {
+ pOutliner->SetTextColumns(rObj.GetTextColumnsNumber(),
+ rObj.GetTextColumnsSpacing());
+ }
+
+ const svx::ITextProvider& rTextProvider(getTextProvider());
+ sal_Int32 nText = rTextProvider.getTextCount();
+ while (nText--)
+ {
+ SdrText* pText = rTextProvider.getText( nText );
+
+ OutlinerParaObject* pParaObj = pText ? pText->GetOutlinerParaObject() : nullptr;
+
+ if(pParaObj)
+ {
+ const bool bTextEdit = rObj.IsTextEditActive() && (rObj.getActiveText() == pText);
+
+ // handle outliner attributes
+ GetObjectItemSet();
+ Outliner* pOutliner = rObj.GetTextEditOutliner();
+
+ if(!bTextEdit)
+ {
+ pOutliner = &rObj.ImpGetDrawOutliner();
+ pOutliner->SetText(*pParaObj);
+ }
+
+ sal_Int32 nParaCount(pOutliner->GetParagraphCount());
+
+ for(sal_Int32 nPara = 0; nPara < nParaCount; nPara++)
+ {
+ SfxItemSet aSet(pOutliner->GetParaAttribs(nPara));
+ for (const SfxPoolItem* pItem : aChangedItems)
+ aSet.Put(*pItem);
+ if (nDeletedWhich)
+ aSet.ClearItem(nDeletedWhich);
+ pOutliner->SetParaAttribs(nPara, aSet);
+ }
+
+ if(!bTextEdit)
+ {
+ if(nParaCount)
+ {
+ // force ItemSet
+ GetObjectItemSet();
+
+ SfxItemSet aNewSet(pOutliner->GetParaAttribs(0));
+ mxItemSet->Put(aNewSet);
+ }
+
+ std::optional<OutlinerParaObject> pTemp = pOutliner->CreateParaObject(0, nParaCount);
+ pOutliner->Clear();
+
+ rObj.NbcSetOutlinerParaObjectForText(std::move(pTemp),pText);
+ }
+ }
+ }
+
+ // Extra-Repaint for radical layout changes (#43139#)
+ for (const SfxPoolItem* pItem : aChangedItems)
+ if (pItem->Which() == SDRATTR_TEXT_CONTOURFRAME)
+ {
+ // Here only repaint wanted
+ rObj.ActionChanged();
+ //rObj.BroadcastObjectChange();
+ break;
+ }
+
+ // call parent
+ AttributeProperties::ItemSetChanged(aChangedItems, nDeletedWhich);
+ }
+
+ void TextProperties::ItemChange(const sal_uInt16 nWhich, const SfxPoolItem* pNewItem)
+ {
+ SdrTextObj& rObj = static_cast<SdrTextObj&>(GetSdrObject());
+
+ // #i25616#
+ sal_Int32 nOldLineWidth(0);
+
+ if(XATTR_LINEWIDTH == nWhich && rObj.DoesSupportTextIndentingOnLineWidthChange())
+ {
+ nOldLineWidth = GetItem(XATTR_LINEWIDTH).GetValue();
+ }
+
+ if(pNewItem && (SDRATTR_TEXTDIRECTION == nWhich))
+ {
+ bool bVertical(css::text::WritingMode_TB_RL == static_cast<const SvxWritingModeItem*>(pNewItem)->GetValue());
+ rObj.SetVerticalWriting(bVertical);
+ }
+
+ // #95501# reset to default
+ if(!pNewItem && !nWhich && rObj.HasText() )
+ {
+ SdrOutliner& rOutliner = rObj.ImpGetDrawOutliner();
+
+ const svx::ITextProvider& rTextProvider(getTextProvider());
+ sal_Int32 nCount = rTextProvider.getTextCount();
+ while (nCount--)
+ {
+ SdrText* pText = rTextProvider.getText( nCount );
+ OutlinerParaObject* pParaObj = pText->GetOutlinerParaObject();
+ if( pParaObj )
+ {
+ rOutliner.SetText(*pParaObj);
+ sal_Int32 nParaCount(rOutliner.GetParagraphCount());
+
+ if(nParaCount)
+ {
+ ESelection aSelection( 0, 0, EE_PARA_ALL, EE_TEXTPOS_ALL);
+ rOutliner.RemoveAttribs(aSelection, true, 0);
+
+ std::optional<OutlinerParaObject> pTemp = rOutliner.CreateParaObject(0, nParaCount);
+ rOutliner.Clear();
+
+ rObj.NbcSetOutlinerParaObjectForText( std::move(pTemp), pText );
+ }
+ }
+ }
+ }
+
+ // call parent
+ AttributeProperties::ItemChange( nWhich, pNewItem );
+
+ // #i25616#
+ if(!(XATTR_LINEWIDTH == nWhich && rObj.DoesSupportTextIndentingOnLineWidthChange()))
+ return;
+
+ const sal_Int32 nNewLineWidth(GetItem(XATTR_LINEWIDTH).GetValue());
+ const sal_Int32 nDifference((nNewLineWidth - nOldLineWidth) / 2);
+
+ if(!nDifference)
+ return;
+
+ const bool bLineVisible(drawing::LineStyle_NONE != GetItem(XATTR_LINESTYLE).GetValue());
+
+ if(bLineVisible)
+ {
+ const sal_Int32 nLeftDist(GetItem(SDRATTR_TEXT_LEFTDIST).GetValue());
+ const sal_Int32 nRightDist(GetItem(SDRATTR_TEXT_RIGHTDIST).GetValue());
+ const sal_Int32 nUpperDist(GetItem(SDRATTR_TEXT_UPPERDIST).GetValue());
+ const sal_Int32 nLowerDist(GetItem(SDRATTR_TEXT_LOWERDIST).GetValue());
+
+ SetObjectItemDirect(makeSdrTextLeftDistItem(nLeftDist + nDifference));
+ SetObjectItemDirect(makeSdrTextRightDistItem(nRightDist + nDifference));
+ SetObjectItemDirect(makeSdrTextUpperDistItem(nUpperDist + nDifference));
+ SetObjectItemDirect(makeSdrTextLowerDistItem(nLowerDist + nDifference));
+ }
+ }
+
+ const svx::ITextProvider& TextProperties::getTextProvider() const
+ {
+ return static_cast<const SdrTextObj&>(GetSdrObject());
+ }
+
+ void TextProperties::SetStyleSheet(SfxStyleSheet* pNewStyleSheet, bool bDontRemoveHardAttr,
+ bool bBroadcast)
+ {
+ // call parent (always first thing to do, may create the SfxItemSet)
+ AttributeProperties::SetStyleSheet(pNewStyleSheet, bDontRemoveHardAttr, bBroadcast);
+
+ // #i101556# StyleSheet has changed -> new version
+ SdrTextObj& rObj = static_cast<SdrTextObj&>(GetSdrObject());
+ maVersion++;
+
+ if(!rObj.IsLinkedText() )
+ {
+ SdrOutliner& rOutliner = rObj.ImpGetDrawOutliner();
+
+ const svx::ITextProvider& rTextProvider(getTextProvider());
+ sal_Int32 nText = rTextProvider.getTextCount();
+ while (nText--)
+ {
+ SdrText* pText = rTextProvider.getText( nText );
+
+ OutlinerParaObject* pParaObj = pText ? pText->GetOutlinerParaObject() : nullptr;
+ if( !pParaObj )
+ continue;
+
+ // apply StyleSheet to all paragraphs
+ rOutliner.SetText(*pParaObj);
+ sal_Int32 nParaCount(rOutliner.GetParagraphCount());
+
+ if(nParaCount)
+ {
+ for(sal_Int32 nPara = 0; nPara < nParaCount; nPara++)
+ {
+ std::optional<SfxItemSet> pTempSet;
+
+ // since setting the stylesheet removes all para attributes
+ if(bDontRemoveHardAttr)
+ {
+ // we need to remember them if we want to keep them
+ pTempSet.emplace(rOutliner.GetParaAttribs(nPara));
+ }
+
+ if(GetStyleSheet())
+ {
+ if((SdrObjKind::OutlineText == rObj.GetTextKind()) && (SdrInventor::Default == rObj.GetObjInventor()))
+ {
+ OUString aNewStyleSheetName(GetStyleSheet()->GetName());
+ aNewStyleSheetName = aNewStyleSheetName.copy(0, aNewStyleSheetName.getLength() - 1);
+ sal_Int16 nDepth = rOutliner.GetDepth(nPara);
+ aNewStyleSheetName += OUString::number( nDepth <= 0 ? 1 : nDepth + 1);
+ SfxStyleSheetBasePool* pStylePool(rObj.getSdrModelFromSdrObject().GetStyleSheetPool());
+ SfxStyleSheet* pNewStyle = nullptr;
+ if(pStylePool)
+ pNewStyle = static_cast<SfxStyleSheet*>(pStylePool->Find(aNewStyleSheetName, GetStyleSheet()->GetFamily()));
+ DBG_ASSERT( pNewStyle, "AutoStyleSheetName - Style not found!" );
+
+ if(pNewStyle)
+ {
+ rOutliner.SetStyleSheet(nPara, pNewStyle);
+ }
+ }
+ else
+ {
+ rOutliner.SetStyleSheet(nPara, GetStyleSheet());
+ }
+ }
+ else
+ {
+ // remove StyleSheet
+ rOutliner.SetStyleSheet(nPara, nullptr);
+ }
+
+ if(bDontRemoveHardAttr)
+ {
+ if(pTempSet)
+ {
+ // restore para attributes
+ rOutliner.SetParaAttribs(nPara, *pTempSet);
+ }
+ }
+ else
+ {
+ if(pNewStyleSheet)
+ {
+ // remove all hard paragraph attributes
+ // which occur in StyleSheet, take care of
+ // parents (!)
+ SfxItemIter aIter(pNewStyleSheet->GetItemSet());
+
+ for (const SfxPoolItem* pItem = aIter.GetCurItem(); pItem;
+ pItem = aIter.NextItem())
+ {
+ if(!IsInvalidItem(pItem))
+ {
+ sal_uInt16 nW(pItem->Which());
+
+ if(nW >= EE_ITEMS_START && nW <= EE_ITEMS_END)
+ {
+ rOutliner.RemoveCharAttribs(nPara, nW);
+ }
+ }
+ }
+ }
+ }
+ }
+
+ std::optional<OutlinerParaObject> pTemp = rOutliner.CreateParaObject(0, nParaCount);
+ rOutliner.Clear();
+ rObj.NbcSetOutlinerParaObjectForText(std::move(pTemp), pText);
+ }
+ }
+ }
+
+ if(rObj.IsTextFrame())
+ {
+ rObj.NbcAdjustTextFrameWidthAndHeight();
+ }
+ }
+
+ void TextProperties::ForceDefaultAttributes()
+ {
+ SdrTextObj& rObj = static_cast<SdrTextObj&>(GetSdrObject());
+
+ if( rObj.GetObjInventor() == SdrInventor::Default )
+ {
+ const SdrObjKind nSdrObjKind = rObj.GetObjIdentifier();
+
+ if( nSdrObjKind == SdrObjKind::TitleText || nSdrObjKind == SdrObjKind::OutlineText )
+ return; // no defaults for presentation objects
+ }
+
+ bool bTextFrame(rObj.IsTextFrame());
+
+ // force ItemSet
+ GetObjectItemSet();
+
+ if(bTextFrame)
+ {
+ mxItemSet->Put(XLineStyleItem(drawing::LineStyle_NONE));
+ mxItemSet->Put(XFillColorItem(OUString(), COL_WHITE));
+ mxItemSet->Put(XFillStyleItem(drawing::FillStyle_NONE));
+ }
+ else
+ {
+ mxItemSet->Put(SvxAdjustItem(SvxAdjust::Center, EE_PARA_JUST));
+ mxItemSet->Put(SdrTextHorzAdjustItem(SDRTEXTHORZADJUST_CENTER));
+ mxItemSet->Put(SdrTextVertAdjustItem(SDRTEXTVERTADJUST_CENTER));
+ }
+ }
+
+ void TextProperties::ForceStyleToHardAttributes()
+ {
+ // #i61284# call parent first to get the hard ObjectItemSet
+ AttributeProperties::ForceStyleToHardAttributes();
+
+ // #i61284# push hard ObjectItemSet to OutlinerParaObject attributes
+ // using existing functionality
+ GetObjectItemSet(); // force ItemSet
+ std::vector<const SfxPoolItem*> aChangedItems;
+ SfxItemIter aIter(*mxItemSet);
+ for (const SfxPoolItem* pItem = aIter.GetCurItem(); pItem; pItem = aIter.NextItem())
+ {
+ if(!IsInvalidItem(pItem))
+ aChangedItems.push_back(pItem);
+ }
+ ItemSetChanged(aChangedItems, 0);
+
+ // now the standard TextProperties stuff
+ SdrTextObj& rObj = static_cast<SdrTextObj&>(GetSdrObject());
+
+ if(rObj.IsTextEditActive() || rObj.IsLinkedText())
+ return;
+
+ std::unique_ptr<Outliner> pOutliner = SdrMakeOutliner(OutlinerMode::OutlineObject, rObj.getSdrModelFromSdrObject());
+ const svx::ITextProvider& rTextProvider(getTextProvider());
+ sal_Int32 nText = rTextProvider.getTextCount();
+ while (nText--)
+ {
+ SdrText* pText = rTextProvider.getText( nText );
+
+ OutlinerParaObject* pParaObj = pText ? pText->GetOutlinerParaObject() : nullptr;
+ if( !pParaObj )
+ continue;
+
+ pOutliner->SetText(*pParaObj);
+
+ sal_Int32 nParaCount(pOutliner->GetParagraphCount());
+
+ if(nParaCount)
+ {
+ bool bBurnIn(false);
+
+ for(sal_Int32 nPara = 0; nPara < nParaCount; nPara++)
+ {
+ SfxStyleSheet* pSheet = pOutliner->GetStyleSheet(nPara);
+
+ if(pSheet)
+ {
+ SfxItemSet aParaSet(pOutliner->GetParaAttribs(nPara));
+ SfxItemSet aSet(*aParaSet.GetPool());
+ aSet.Put(pSheet->GetItemSet());
+
+ /** the next code handles a special case for paragraphs that contain a
+ url field. The color for URL fields is either the system color for
+ urls or the char color attribute that formats the portion in which the
+ url field is contained.
+ When we set a char color attribute to the paragraphs item set from the
+ styles item set, we would have this char color attribute as an attribute
+ that is spanned over the complete paragraph after xml import due to some
+ problems in the xml import (using a XCursor on import so it does not know
+ the paragraphs and can't set char attributes to paragraphs ).
+
+ To avoid this, as soon as we try to set a char color attribute from the style
+ we
+ 1. check if we have at least one url field in this paragraph
+ 2. if we found at least one url field, we span the char color attribute over
+ all portions that are not url fields and remove the char color attribute
+ from the paragraphs item set
+ */
+
+ bool bHasURL(false);
+
+ if(aSet.GetItemState(EE_CHAR_COLOR) == SfxItemState::SET)
+ {
+ EditEngine* pEditEngine = const_cast<EditEngine*>(&(pOutliner->GetEditEngine()));
+ std::vector<EECharAttrib> aAttribs;
+ pEditEngine->GetCharAttribs(nPara, aAttribs);
+
+ for(const auto& rAttrib : aAttribs)
+ {
+ if(rAttrib.pAttr && EE_FEATURE_FIELD == rAttrib.pAttr->Which())
+ {
+ const SvxFieldItem* pFieldItem = static_cast<const SvxFieldItem*>(rAttrib.pAttr);
+
+ if(pFieldItem)
+ {
+ const SvxFieldData* pData = pFieldItem->GetField();
+
+ if(dynamic_cast<const SvxURLField*>( pData))
+ {
+ bHasURL = true;
+ break;
+ }
+ }
+ }
+ }
+
+ if(bHasURL)
+ {
+ SfxItemSetFixed<EE_CHAR_COLOR, EE_CHAR_COLOR> aColorSet(*aSet.GetPool());
+ aColorSet.Put(aSet, false);
+
+ ESelection aSel(nPara, 0);
+
+ for(const auto& rAttrib : aAttribs)
+ {
+ if(EE_FEATURE_FIELD == rAttrib.pAttr->Which())
+ {
+ aSel.nEndPos = rAttrib.nStart;
+
+ if(aSel.nStartPos != aSel.nEndPos)
+ pEditEngine->QuickSetAttribs(aColorSet, aSel);
+
+ aSel.nStartPos = rAttrib.nEnd;
+ }
+ }
+
+ aSel.nEndPos = pEditEngine->GetTextLen(nPara);
+
+ if(aSel.nStartPos != aSel.nEndPos)
+ {
+ pEditEngine->QuickSetAttribs( aColorSet, aSel );
+ }
+ }
+
+ }
+
+ aSet.Put(aParaSet, false);
+
+ if(bHasURL)
+ {
+ aSet.ClearItem(EE_CHAR_COLOR);
+ }
+
+ pOutliner->SetParaAttribs(nPara, aSet);
+ bBurnIn = true; // #i51163# Flag was set wrong
+ }
+ }
+
+ if(bBurnIn)
+ {
+ std::optional<OutlinerParaObject> pTemp = pOutliner->CreateParaObject(0, nParaCount);
+ rObj.NbcSetOutlinerParaObjectForText(std::move(pTemp),pText);
+ }
+ }
+
+ pOutliner->Clear();
+ }
+ }
+
+ void TextProperties::SetObjectItemNoBroadcast(const SfxPoolItem& rItem)
+ {
+ GetObjectItemSet();
+ mxItemSet->Put(rItem);
+ }
+
+
+ void TextProperties::Notify(SfxBroadcaster& rBC, const SfxHint& rHint)
+ {
+ // call parent
+ AttributeProperties::Notify(rBC, rHint);
+
+ SdrTextObj& rObj = static_cast<SdrTextObj&>(GetSdrObject());
+ if(!rObj.HasText())
+ return;
+
+ const svx::ITextProvider& rTextProvider(getTextProvider());
+ if(dynamic_cast<const SfxStyleSheet *>(&rBC) != nullptr)
+ {
+ SfxHintId nId(rHint.GetId());
+
+ if(SfxHintId::DataChanged == nId)
+ {
+ sal_Int32 nText = rTextProvider.getTextCount();
+ while (nText--)
+ {
+ OutlinerParaObject* pParaObj = rTextProvider.getText( nText )->GetOutlinerParaObject();
+ if( pParaObj )
+ pParaObj->ClearPortionInfo();
+ }
+ rObj.SetTextSizeDirty();
+
+ if(rObj.IsTextFrame() && rObj.NbcAdjustTextFrameWidthAndHeight())
+ {
+ // here only repaint wanted
+ rObj.ActionChanged();
+ //rObj.BroadcastObjectChange();
+ }
+
+ // #i101556# content of StyleSheet has changed -> new version
+ maVersion++;
+ }
+
+ if(SfxHintId::Dying == nId)
+ {
+ sal_Int32 nText = rTextProvider.getTextCount();
+ while (nText--)
+ {
+ OutlinerParaObject* pParaObj = rTextProvider.getText( nText )->GetOutlinerParaObject();
+ if( pParaObj )
+ pParaObj->ClearPortionInfo();
+ }
+ }
+ }
+ else if(dynamic_cast<const SfxStyleSheetBasePool *>(&rBC) != nullptr)
+ {
+ const SfxStyleSheetModifiedHint* pExtendedHint = dynamic_cast<const SfxStyleSheetModifiedHint*>(&rHint);
+
+ if(pExtendedHint
+ && SfxHintId::StyleSheetModified == pExtendedHint->GetId())
+ {
+ const OUString& aOldName(pExtendedHint->GetOldName());
+ OUString aNewName(pExtendedHint->GetStyleSheet()->GetName());
+ SfxStyleFamily eFamily = pExtendedHint->GetStyleSheet()->GetFamily();
+
+ if(aOldName != aNewName)
+ {
+ sal_Int32 nText = rTextProvider.getTextCount();
+ while (nText--)
+ {
+ OutlinerParaObject* pParaObj = rTextProvider.getText( nText )->GetOutlinerParaObject();
+ if( pParaObj )
+ pParaObj->ChangeStyleSheetName(eFamily, aOldName, aNewName);
+ }
+ }
+ }
+ }
+ }
+
+ // #i101556# Handout version information
+ sal_uInt32 TextProperties::getVersion() const
+ {
+ return maVersion;
+ }
+} // end of namespace
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/sidebar/ContextChangeEventMultiplexer.cxx b/svx/source/sidebar/ContextChangeEventMultiplexer.cxx
new file mode 100644
index 000000000..ce552b0ac
--- /dev/null
+++ b/svx/source/sidebar/ContextChangeEventMultiplexer.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 <svx/sidebar/ContextChangeEventMultiplexer.hxx>
+
+#include <com/sun/star/ui/ContextChangeEventObject.hpp>
+#include <com/sun/star/ui/XContextChangeEventMultiplexer.hpp>
+#include <com/sun/star/ui/ContextChangeEventMultiplexer.hpp>
+#include <com/sun/star/frame/ModuleManager.hpp>
+#include <comphelper/lok.hxx>
+#include <comphelper/processfactory.hxx>
+#include <sfx2/lokhelper.hxx>
+#include <sfx2/viewsh.hxx>
+
+using namespace css;
+using namespace css::uno;
+
+
+void ContextChangeEventMultiplexer::NotifyContextChange (
+ const css::uno::Reference<css::frame::XController>& rxController,
+ const vcl::EnumContext::Context eContext)
+{
+ if (!(rxController.is() && rxController->getFrame().is()))
+ return;
+
+ const css::ui::ContextChangeEventObject aEvent(
+ rxController,
+ GetModuleName(rxController->getFrame()),
+ vcl::EnumContext::GetContextName(eContext));
+
+ css::uno::Reference<css::ui::XContextChangeEventMultiplexer> xMultiplexer (
+ css::ui::ContextChangeEventMultiplexer::get(
+ ::comphelper::getProcessComponentContext()));
+ if (xMultiplexer.is())
+ xMultiplexer->broadcastContextChangeEvent(aEvent, rxController);
+
+ // notify the LOK too after all the change have taken effect.
+ if (comphelper::LibreOfficeKit::isActive())
+ {
+ if (SfxViewShell* pViewShell = SfxViewShell::Get(rxController))
+ SfxLokHelper::notifyContextChange(pViewShell, GetModuleName(rxController->getFrame()), vcl::EnumContext::GetContextName(eContext));
+ }
+}
+
+
+void ContextChangeEventMultiplexer::NotifyContextChange (
+ const SfxViewShell* pViewShell,
+ const vcl::EnumContext::Context eContext)
+{
+ if (pViewShell != nullptr)
+ NotifyContextChange(pViewShell->GetController(), eContext);
+}
+
+
+OUString ContextChangeEventMultiplexer::GetModuleName (
+ const css::uno::Reference<css::frame::XFrame>& rxFrame)
+{
+ try
+ {
+ const Reference<frame::XModuleManager> xModuleManager =
+ frame::ModuleManager::create( comphelper::getProcessComponentContext() );
+ return xModuleManager->identify(rxFrame);
+ }
+ catch (const Exception&)
+ {
+ // An exception typically means that a context change is notified
+ // during initialization or destruction of a view.
+ // Ignore it.
+ }
+ return vcl::EnumContext::GetApplicationName(
+ vcl::EnumContext::Application::NONE);
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/sidebar/EmptyPanel.cxx b/svx/source/sidebar/EmptyPanel.cxx
new file mode 100644
index 000000000..3bc21636b
--- /dev/null
+++ b/svx/source/sidebar/EmptyPanel.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 "EmptyPanel.hxx"
+
+namespace svx::sidebar
+{
+EmptyPanel::EmptyPanel(weld::Widget* pParent)
+ : PanelLayout(pParent, "EmptyPanel", "svx/ui/sidebarempty.ui")
+ , mxMessageControl(m_xBuilder->weld_label("message"))
+{
+}
+
+EmptyPanel::~EmptyPanel() {}
+
+} // end of namespace svx::sidebar
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/sidebar/EmptyPanel.hxx b/svx/source/sidebar/EmptyPanel.hxx
new file mode 100644
index 000000000..26007a530
--- /dev/null
+++ b/svx/source/sidebar/EmptyPanel.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_SVX_SOURCE_SIDEBAR_EMPTYPANEL_HXX
+#define INCLUDED_SVX_SOURCE_SIDEBAR_EMPTYPANEL_HXX
+
+#include <sfx2/sidebar/PanelLayout.hxx>
+
+namespace svx::sidebar
+{
+/** Display a panel that tells the user that the current deck is
+ intentionally empty.
+*/
+class EmptyPanel final : public PanelLayout
+{
+public:
+ explicit EmptyPanel(weld::Widget* pParent);
+ virtual ~EmptyPanel() override;
+
+private:
+ std::unique_ptr<weld::Label> mxMessageControl;
+};
+
+} // end of namespace svx::sidebar
+
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/sidebar/PanelFactory.cxx b/svx/source/sidebar/PanelFactory.cxx
new file mode 100644
index 000000000..f55575d1a
--- /dev/null
+++ b/svx/source/sidebar/PanelFactory.cxx
@@ -0,0 +1,220 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * 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 "text/TextPropertyPanel.hxx"
+#include "styles/StylesPropertyPanel.hxx"
+#include "paragraph/ParaPropertyPanel.hxx"
+#include "lists/ListsPropertyPanel.hxx"
+#include "area/AreaPropertyPanel.hxx"
+#include "fontwork/FontworkPropertyPanel.hxx"
+#include "shadow/ShadowPropertyPanel.hxx"
+#include "effect/EffectPropertyPanel.hxx"
+#include "graphic/GraphicPropertyPanel.hxx"
+#include "line/LinePropertyPanel.hxx"
+#include "possize/PosSizePropertyPanel.hxx"
+#include "textcolumns/TextColumnsPropertyPanel.hxx"
+#include <DefaultShapesPanel.hxx>
+#if HAVE_FEATURE_AVMEDIA
+#include "media/MediaPlaybackPanel.hxx"
+#endif
+#include <GalleryControl.hxx>
+#include "EmptyPanel.hxx"
+#include <sfx2/sidebar/SidebarPanelBase.hxx>
+#include <sfx2/templdlg.hxx>
+#include <vcl/weldutils.hxx>
+#include <comphelper/namedvaluecollection.hxx>
+#include <comphelper/compbase.hxx>
+#include <cppuhelper/supportsservice.hxx>
+#include <com/sun/star/lang/XServiceInfo.hpp>
+#include <com/sun/star/ui/XSidebar.hpp>
+#include <com/sun/star/ui/XUIElementFactory.hpp>
+#include <com/sun/star/uno/XComponentContext.hpp>
+
+using namespace css;
+using namespace css::uno;
+using namespace svx::sidebar;
+
+
+namespace {
+
+/* Why this is not used ? Doesn't it need to inherit from XServiceInfo ?
+constexpr OUStringLiteral IMPLEMENTATION_NAME = u"org.apache.openoffice.comp.svx.sidebar.PanelFactory";
+constexpr OUStringLiteral SERVICE_NAME = u"com.sun.star.ui.UIElementFactory";
+*/
+
+typedef comphelper::WeakComponentImplHelper< css::ui::XUIElementFactory, css::lang::XServiceInfo >
+ PanelFactoryInterfaceBase;
+
+class PanelFactory
+ : public PanelFactoryInterfaceBase
+{
+public:
+ PanelFactory();
+ PanelFactory(const PanelFactory&) = delete;
+ PanelFactory& operator=(const PanelFactory&) = delete;
+
+ // 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.svx.sidebar.PanelFactory"; }
+
+ sal_Bool SAL_CALL supportsService(OUString const & ServiceName) override
+ { return cppu::supportsService(this, ServiceName); }
+
+ css::uno::Sequence<OUString> SAL_CALL getSupportedServiceNames() override
+ { return {"com.sun.star.ui.UIElementFactory"}; }
+};
+
+PanelFactory::PanelFactory()
+{
+}
+
+Reference<ui::XUIElement> SAL_CALL PanelFactory::createUIElement (
+ const OUString& rsResourceURL,
+ const ::css::uno::Sequence<css::beans::PropertyValue>& rArguments)
+{
+ 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>()));
+ Reference<ui::XSidebar> xSidebar (aArguments.getOrDefault("Sidebar", Reference<ui::XSidebar>()));
+ const sal_uInt64 nBindingsValue (aArguments.getOrDefault("SfxBindings", sal_uInt64(0)));
+ SfxBindings* pBindings = reinterpret_cast<SfxBindings*>(nBindingsValue);
+
+ weld::Widget* pParent(nullptr);
+ if (weld::TransportAsXWindow* pTunnel = dynamic_cast<weld::TransportAsXWindow*>(xParentWindow.get()))
+ pParent = pTunnel->getWidget();
+
+ if (!pParent)
+ 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);
+
+ std::unique_ptr<PanelLayout> xControl;
+ ui::LayoutSize aLayoutSize (-1,-1,-1);
+
+ if (rsResourceURL.endsWith("/TextPropertyPanel"))
+ {
+ xControl = TextPropertyPanel::Create(pParent, xFrame);
+ }
+ else if (rsResourceURL.endsWith("/StylesPropertyPanel"))
+ {
+ xControl = StylesPropertyPanel::Create(pParent, xFrame);
+ }
+ else if (rsResourceURL.endsWith("/ParaPropertyPanel"))
+ {
+ xControl = ParaPropertyPanel::Create(pParent, xFrame, pBindings, xSidebar);
+ }
+ else if (rsResourceURL.endsWith("/ListsPropertyPanel"))
+ {
+ xControl = ListsPropertyPanel::Create(pParent, xFrame);
+ }
+ else if (rsResourceURL.endsWith("/AreaPropertyPanel"))
+ {
+ xControl = AreaPropertyPanel::Create(pParent, xFrame, pBindings);
+ }
+ else if (rsResourceURL.endsWith("/FontworkPropertyPanel"))
+ {
+ xControl = FontworkPropertyPanel::Create(pParent, xFrame);
+ }
+ else if (rsResourceURL.endsWith("/ShadowPropertyPanel"))
+ {
+ xControl = ShadowPropertyPanel::Create(pParent, pBindings);
+ }
+ else if (rsResourceURL.endsWith("/EffectPropertyPanel"))
+ {
+ xControl = EffectPropertyPanel::Create(pParent, pBindings);
+ }
+ else if (rsResourceURL.endsWith("/GraphicPropertyPanel"))
+ {
+ xControl = GraphicPropertyPanel::Create(pParent, pBindings);
+ }
+ else if (rsResourceURL.endsWith("/LinePropertyPanel"))
+ {
+ xControl = LinePropertyPanel::Create(pParent, xFrame, pBindings);
+ }
+ else if (rsResourceURL.endsWith("/PosSizePropertyPanel"))
+ {
+ xControl = PosSizePropertyPanel::Create(pParent, xFrame, pBindings, xSidebar);
+ }
+ else if (rsResourceURL.endsWith("/DefaultShapesPanel"))
+ {
+ xControl = DefaultShapesPanel::Create(pParent, xFrame);
+ }
+#if HAVE_FEATURE_AVMEDIA
+ else if (rsResourceURL.endsWith("/MediaPlaybackPanel"))
+ {
+ xControl = MediaPlaybackPanel::Create(pParent, pBindings);
+ }
+#endif
+ else if (rsResourceURL.endsWith("/GalleryPanel"))
+ {
+ xControl = std::make_unique<GalleryControl>(pParent);
+ aLayoutSize = ui::LayoutSize(300,-1,400);
+ }
+ else if (rsResourceURL.endsWith("/StyleListPanel"))
+ {
+ xControl = std::make_unique<SfxTemplatePanelControl>(pBindings, pParent);
+ aLayoutSize = ui::LayoutSize(0,-1,-1);
+ }
+ else if (rsResourceURL.endsWith("/EmptyPanel"))
+ {
+ xControl = std::make_unique<EmptyPanel>(pParent);
+ aLayoutSize = ui::LayoutSize(20,-1, 50);
+ }
+ else if (rsResourceURL.endsWith("/TextColumnsPropertyPanel"))
+ {
+ xControl = TextColumnsPropertyPanel::Create(pParent, pBindings);
+ }
+
+ if (xControl)
+ {
+ return sfx2::sidebar::SidebarPanelBase::Create(
+ rsResourceURL,
+ xFrame,
+ std::move(xControl),
+ aLayoutSize);
+ }
+ else
+ return Reference<ui::XUIElement>();
+}
+
+}
+
+extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface *
+org_apache_openoffice_comp_svx_sidebar_PanelFactory_get_implementation(
+ css::uno::XComponentContext *,
+ css::uno::Sequence<css::uno::Any> const &)
+{
+ return cppu::acquire(new PanelFactory);
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/sidebar/SelectionAnalyzer.cxx b/svx/source/sidebar/SelectionAnalyzer.cxx
new file mode 100644
index 000000000..193b6b6b7
--- /dev/null
+++ b/svx/source/sidebar/SelectionAnalyzer.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 <svx/sidebar/SelectionAnalyzer.hxx>
+#include <svx/svdmark.hxx>
+#include <svx/svdobj.hxx>
+#include <svx/svdotext.hxx>
+#include <svx/svdpage.hxx>
+#include <svx/fontworkbar.hxx>
+
+using vcl::EnumContext;
+
+namespace svx::sidebar
+{
+EnumContext::Context SelectionAnalyzer::GetContextForSelection_SC(const SdrMarkList& rMarkList)
+{
+ EnumContext::Context eContext = EnumContext::Context::Unknown;
+
+ switch (rMarkList.GetMarkCount())
+ {
+ case 0:
+ // Empty selection. Return Context::Unknown to let the caller
+ // substitute it with the default context.
+ break;
+
+ case 1:
+ {
+ SdrObject* pObj = rMarkList.GetMark(0)->GetMarkedSdrObj();
+ auto pTextObj = dynamic_cast<SdrTextObj*>(pObj);
+ if (pTextObj && pTextObj->IsInEditMode())
+ {
+ eContext = EnumContext::Context::DrawText;
+ }
+ else if (svx::checkForFontWork(pObj))
+ {
+ eContext = EnumContext::Context::DrawFontwork;
+ }
+ else
+ {
+ const SdrInventor nInv = pObj->GetObjInventor();
+ const SdrObjKind nObjId = pObj->GetObjIdentifier();
+ if (nInv == SdrInventor::Default)
+ eContext = GetContextForObjectId_SC(nObjId);
+ else if (nInv == SdrInventor::FmForm)
+ eContext = EnumContext::Context::Form;
+ }
+ break;
+ }
+
+ default:
+ {
+ // Multi selection.
+ switch (GetInventorTypeFromMark(rMarkList))
+ {
+ case SdrInventor::Default:
+ {
+ const SdrObjKind nObjId(GetObjectTypeFromMark(rMarkList));
+ if (nObjId == SdrObjKind::NONE)
+ eContext = EnumContext::Context::MultiObject;
+ else
+ eContext = GetContextForObjectId_SC(nObjId);
+ break;
+ }
+
+ case SdrInventor::FmForm:
+ eContext = EnumContext::Context::Form;
+ break;
+
+ case SdrInventor::Unknown:
+ eContext = EnumContext::Context::MultiObject;
+ break;
+
+ default:
+ break;
+ }
+ }
+ }
+
+ return eContext;
+}
+
+EnumContext::Context SelectionAnalyzer::GetContextForSelection_SD(const SdrMarkList& rMarkList,
+ const ViewType eViewType)
+{
+ EnumContext::Context eContext = EnumContext::Context::Unknown;
+
+ // Note that some cases are handled by the caller. They rely on
+ // sd specific data.
+ switch (rMarkList.GetMarkCount())
+ {
+ case 0:
+ switch (eViewType)
+ {
+ case ViewType::Standard:
+ eContext = EnumContext::Context::DrawPage;
+ break;
+ case ViewType::Master:
+ eContext = EnumContext::Context::MasterPage;
+ break;
+ case ViewType::Handout:
+ eContext = EnumContext::Context::HandoutPage;
+ break;
+ case ViewType::Notes:
+ eContext = EnumContext::Context::NotesPage;
+ break;
+ }
+ break;
+
+ case 1:
+ {
+ SdrObject* pObj = rMarkList.GetMark(0)->GetMarkedSdrObj();
+ auto pTextObj = dynamic_cast<SdrTextObj*>(pObj);
+ if (pTextObj && pTextObj->IsInEditMode())
+ {
+ if (pObj->GetObjIdentifier() == SdrObjKind::Table)
+ {
+ // Let a table object take precedence over text
+ // edit mode. The panels for text editing are
+ // present for table context as well, anyway.
+ eContext = EnumContext::Context::Table;
+ }
+ else
+ eContext = EnumContext::Context::DrawText;
+ }
+ else if (svx::checkForFontWork(pObj))
+ {
+ eContext = EnumContext::Context::DrawFontwork;
+ }
+ else
+ {
+ const SdrInventor nInv = pObj->GetObjInventor();
+ SdrObjKind nObjId = pObj->GetObjIdentifier();
+ if (nInv == SdrInventor::Default)
+ {
+ if (nObjId == SdrObjKind::Group)
+ {
+ nObjId = GetObjectTypeFromGroup(pObj);
+ if (nObjId == SdrObjKind::NONE)
+ nObjId = SdrObjKind::Group;
+ }
+ eContext = GetContextForObjectId_SD(nObjId, eViewType);
+ }
+ else if (nInv == SdrInventor::E3d)
+ {
+ eContext = EnumContext::Context::ThreeDObject;
+ }
+ else if (nInv == SdrInventor::FmForm)
+ {
+ eContext = EnumContext::Context::Form;
+ }
+ }
+ break;
+ }
+
+ default:
+ {
+ switch (GetInventorTypeFromMark(rMarkList))
+ {
+ case SdrInventor::Default:
+ {
+ const SdrObjKind nObjId = GetObjectTypeFromMark(rMarkList);
+ if (nObjId == SdrObjKind::NONE)
+ eContext = EnumContext::Context::MultiObject;
+ else
+ eContext = GetContextForObjectId_SD(nObjId, eViewType);
+ break;
+ }
+
+ case SdrInventor::E3d:
+ eContext = EnumContext::Context::ThreeDObject;
+ break;
+
+ case SdrInventor::FmForm:
+ eContext = EnumContext::Context::Form;
+ break;
+
+ case SdrInventor::Unknown:
+ eContext = EnumContext::Context::MultiObject;
+ break;
+
+ default:
+ break;
+ }
+ break;
+ }
+ }
+
+ return eContext;
+}
+
+EnumContext::Context SelectionAnalyzer::GetContextForObjectId_SC(const SdrObjKind nObjectId)
+{
+ switch (nObjectId)
+ {
+ case SdrObjKind::Caption:
+ case SdrObjKind::TitleText:
+ case SdrObjKind::OutlineText:
+ case SdrObjKind::Text:
+ case SdrObjKind::Measure:
+ case SdrObjKind::Rectangle:
+ case SdrObjKind::CircleOrEllipse:
+ case SdrObjKind::FreehandFill:
+ case SdrObjKind::PathFill:
+ case SdrObjKind::Polygon:
+ case SdrObjKind::CircleSection:
+ case SdrObjKind::CircleArc:
+ case SdrObjKind::CircleCut:
+ case SdrObjKind::CustomShape:
+ case SdrObjKind::Group:
+ return EnumContext::Context::Draw;
+
+ case SdrObjKind::PolyLine:
+ case SdrObjKind::PathLine:
+ case SdrObjKind::FreehandLine:
+ case SdrObjKind::Line:
+ case SdrObjKind::Edge:
+ return EnumContext::Context::DrawLine;
+
+ case SdrObjKind::Graphic:
+ return EnumContext::Context::Graphic;
+
+ case SdrObjKind::OLE2:
+ return EnumContext::Context::OLE;
+
+ case SdrObjKind::Media:
+ return EnumContext::Context::Media;
+
+ default:
+ return EnumContext::Context::Unknown;
+ }
+}
+
+EnumContext::Context SelectionAnalyzer::GetContextForObjectId_SD(const SdrObjKind nObjectId,
+ const ViewType eViewType)
+{
+ switch (nObjectId)
+ {
+ case SdrObjKind::Caption:
+ case SdrObjKind::Measure:
+ case SdrObjKind::Rectangle:
+ case SdrObjKind::CircleOrEllipse:
+ case SdrObjKind::FreehandFill:
+ case SdrObjKind::PathFill:
+ case SdrObjKind::Polygon:
+ case SdrObjKind::CircleSection:
+ case SdrObjKind::CircleArc:
+ case SdrObjKind::CircleCut:
+ case SdrObjKind::CustomShape:
+ case SdrObjKind::Group:
+ return EnumContext::Context::Draw;
+
+ case SdrObjKind::Edge:
+ case SdrObjKind::PathLine:
+ case SdrObjKind::FreehandLine:
+ case SdrObjKind::PolyLine:
+ case SdrObjKind::Line:
+ return EnumContext::Context::DrawLine;
+
+ case SdrObjKind::TitleText:
+ case SdrObjKind::OutlineText:
+ case SdrObjKind::Text:
+ return EnumContext::Context::TextObject;
+
+ case SdrObjKind::Graphic:
+ return EnumContext::Context::Graphic;
+
+ case SdrObjKind::OLE2:
+ return EnumContext::Context::OLE;
+
+ case SdrObjKind::Media:
+ return EnumContext::Context::Media;
+
+ case SdrObjKind::Table:
+ return EnumContext::Context::Table;
+
+ case SdrObjKind::Page:
+ switch (eViewType)
+ {
+ case ViewType::Handout:
+ return EnumContext::Context::HandoutPage;
+ case ViewType::Notes:
+ return EnumContext::Context::NotesPage;
+ default:
+ return EnumContext::Context::Unknown;
+ }
+
+ default:
+ return EnumContext::Context::Unknown;
+ }
+}
+
+SdrInventor SelectionAnalyzer::GetInventorTypeFromMark(const SdrMarkList& rMarkList)
+{
+ const size_t nMarkCount(rMarkList.GetMarkCount());
+
+ if (nMarkCount < 1)
+ return SdrInventor::Unknown;
+
+ SdrMark* pMark = rMarkList.GetMark(0);
+ SdrObject* pObj = pMark->GetMarkedSdrObj();
+ const SdrInventor nFirstInv = pObj->GetObjInventor();
+
+ for (size_t nIndex = 1; nIndex < nMarkCount; ++nIndex)
+ {
+ pMark = rMarkList.GetMark(nIndex);
+ pObj = pMark->GetMarkedSdrObj();
+ const SdrInventor nInv(pObj->GetObjInventor());
+
+ if (nInv != nFirstInv)
+ return SdrInventor::Unknown;
+ }
+
+ return nFirstInv;
+}
+
+SdrObjKind SelectionAnalyzer::GetObjectTypeFromGroup(const SdrObject* pObj)
+{
+ SdrObjList* pObjList = pObj->GetSubList();
+ if (pObjList)
+ {
+ const size_t nSubObjCount(pObjList->GetObjCount());
+
+ if (nSubObjCount > 0)
+ {
+ SdrObject* pSubObj = pObjList->GetObj(0);
+ SdrObjKind nResultType = pSubObj->GetObjIdentifier();
+
+ if (nResultType == SdrObjKind::Group)
+ nResultType = GetObjectTypeFromGroup(pSubObj);
+
+ if (IsShapeType(nResultType))
+ nResultType = SdrObjKind::CustomShape;
+
+ if (IsTextObjType(nResultType))
+ nResultType = SdrObjKind::Text;
+
+ for (size_t nIndex = 1; nIndex < nSubObjCount; ++nIndex)
+ {
+ pSubObj = pObjList->GetObj(nIndex);
+ SdrObjKind nType(pSubObj->GetObjIdentifier());
+
+ if (nType == SdrObjKind::Group)
+ nType = GetObjectTypeFromGroup(pSubObj);
+
+ if (IsShapeType(nType))
+ nType = SdrObjKind::CustomShape;
+
+ if ((nType == SdrObjKind::CustomShape) && (nResultType == SdrObjKind::Text))
+ nType = SdrObjKind::Text;
+
+ if (IsTextObjType(nType))
+ nType = SdrObjKind::Text;
+
+ if ((nType == SdrObjKind::Text) && (nResultType == SdrObjKind::CustomShape))
+ nResultType = SdrObjKind::Text;
+
+ if (nType != nResultType)
+ return SdrObjKind::NONE;
+ }
+
+ return nResultType;
+ }
+ }
+
+ return SdrObjKind::NONE;
+}
+
+SdrObjKind SelectionAnalyzer::GetObjectTypeFromMark(const SdrMarkList& rMarkList)
+{
+ const size_t nMarkCount(rMarkList.GetMarkCount());
+
+ if (nMarkCount < 1)
+ return SdrObjKind::NONE;
+
+ SdrMark* pMark = rMarkList.GetMark(0);
+ SdrObject* pObj = pMark->GetMarkedSdrObj();
+ SdrObjKind nResultType = pObj->GetObjIdentifier();
+
+ if (nResultType == SdrObjKind::Group)
+ nResultType = GetObjectTypeFromGroup(pObj);
+
+ if (IsShapeType(nResultType))
+ nResultType = SdrObjKind::CustomShape;
+
+ if (IsTextObjType(nResultType))
+ nResultType = SdrObjKind::Text;
+
+ for (size_t nIndex = 1; nIndex < nMarkCount; ++nIndex)
+ {
+ pMark = rMarkList.GetMark(nIndex);
+ pObj = pMark->GetMarkedSdrObj();
+ SdrObjKind nType = pObj->GetObjIdentifier();
+
+ if (nType == SdrObjKind::Group)
+ nType = GetObjectTypeFromGroup(pObj);
+
+ if (IsShapeType(nType))
+ nType = SdrObjKind::CustomShape;
+
+ if ((nType == SdrObjKind::CustomShape) && (nResultType == SdrObjKind::Text))
+ nType = SdrObjKind::Text;
+
+ if (IsTextObjType(nType))
+ nType = SdrObjKind::Text;
+
+ if ((nType == SdrObjKind::Text) && (nResultType == SdrObjKind::CustomShape))
+ nResultType = SdrObjKind::Text;
+
+ if (nType != nResultType)
+ return SdrObjKind::NONE;
+ }
+
+ return nResultType;
+}
+
+bool SelectionAnalyzer::IsShapeType(const SdrObjKind nType)
+{
+ switch (nType)
+ {
+ case SdrObjKind::Line:
+ case SdrObjKind::CircleArc:
+ case SdrObjKind::PolyLine:
+ case SdrObjKind::PathLine:
+ case SdrObjKind::Rectangle:
+ case SdrObjKind::CircleOrEllipse:
+ case SdrObjKind::CircleSection:
+ case SdrObjKind::CircleCut:
+ case SdrObjKind::PathFill:
+ case SdrObjKind::CustomShape:
+ case SdrObjKind::Caption:
+ case SdrObjKind::Measure:
+ case SdrObjKind::Edge:
+ case SdrObjKind::Polygon:
+ case SdrObjKind::FreehandLine:
+ case SdrObjKind::FreehandFill:
+
+ // #122145# adding SdrObjKind::OLE2 since these also allow line/fill style and may
+ // be multiselected/grouped with normal draw objects, e.g. math OLE objects
+ case SdrObjKind::OLE2:
+ return true;
+
+ default:
+ return false;
+ }
+}
+
+bool SelectionAnalyzer::IsTextObjType(const SdrObjKind nType)
+{
+ switch (nType)
+ {
+ case SdrObjKind::Text:
+ case SdrObjKind::TitleText:
+ case SdrObjKind::OutlineText:
+ return true;
+
+ default:
+ return false;
+ }
+}
+
+} // end of namespace svx::sidebar
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/sidebar/SelectionChangeHandler.cxx b/svx/source/sidebar/SelectionChangeHandler.cxx
new file mode 100644
index 000000000..f4ef4d893
--- /dev/null
+++ b/svx/source/sidebar/SelectionChangeHandler.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/sidebar/SelectionChangeHandler.hxx>
+#include <svx/sidebar/ContextChangeEventMultiplexer.hxx>
+
+#include <com/sun/star/view/XSelectionSupplier.hpp>
+
+#include <vcl/EnumContext.hxx>
+
+
+using namespace css;
+using namespace css::uno;
+
+namespace svx::sidebar {
+
+SelectionChangeHandler::SelectionChangeHandler (
+ const std::function<OUString()>& rSelectionChangeCallback,
+ const Reference<css::frame::XController>& rxController,
+ const vcl::EnumContext::Context eDefaultContext)
+ : maSelectionChangeCallback(rSelectionChangeCallback),
+ mxController(rxController),
+ meDefaultContext(eDefaultContext),
+ mbIsConnected(false)
+{
+}
+
+
+SelectionChangeHandler::~SelectionChangeHandler()
+{
+}
+
+
+void SAL_CALL SelectionChangeHandler::selectionChanged (const lang::EventObject&)
+{
+ if (maSelectionChangeCallback)
+ {
+ const vcl::EnumContext::Context eContext (
+ vcl::EnumContext::GetContextEnum(maSelectionChangeCallback()));
+ ContextChangeEventMultiplexer::NotifyContextChange(
+ mxController,
+ eContext==vcl::EnumContext::Context::Unknown
+ ? meDefaultContext
+ : eContext);
+ }
+}
+
+
+void SAL_CALL SelectionChangeHandler::disposing (const lang::EventObject&)
+{
+}
+
+
+void SelectionChangeHandler::disposing(std::unique_lock<std::mutex>&)
+{
+ if (mbIsConnected)
+ Disconnect();
+}
+
+
+void SelectionChangeHandler::Connect()
+{
+ uno::Reference<view::XSelectionSupplier> xSupplier (mxController, uno::UNO_QUERY);
+ if (xSupplier.is())
+ {
+ mbIsConnected = true;
+ xSupplier->addSelectionChangeListener(this);
+ }
+}
+
+
+void SelectionChangeHandler::Disconnect()
+{
+ uno::Reference<view::XSelectionSupplier> xSupplier (mxController, uno::UNO_QUERY);
+ if (xSupplier.is())
+ {
+ mbIsConnected = false;
+ xSupplier->removeSelectionChangeListener(this);
+ }
+}
+
+
+} // end of namespace svx::sidebar
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/sidebar/area/AreaPropertyPanel.cxx b/svx/source/sidebar/area/AreaPropertyPanel.cxx
new file mode 100644
index 000000000..fa634ee44
--- /dev/null
+++ b/svx/source/sidebar/area/AreaPropertyPanel.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 "AreaPropertyPanel.hxx"
+#include <svx/svxids.hrc>
+#include <svx/xfltrit.hxx>
+#include <svx/xflftrit.hxx>
+#include <sfx2/dispatch.hxx>
+#include <sfx2/bindings.hxx>
+
+
+using namespace css;
+using namespace css::uno;
+
+namespace svx::sidebar {
+
+AreaPropertyPanel::AreaPropertyPanel(
+ weld::Widget* pParent,
+ const css::uno::Reference<css::frame::XFrame>& rxFrame,
+ SfxBindings* pBindings)
+ : AreaPropertyPanelBase(pParent, rxFrame),
+ maStyleControl(SID_ATTR_FILL_STYLE, *pBindings, *this),
+ maColorControl(SID_ATTR_FILL_COLOR, *pBindings, *this),
+ maGradientControl(SID_ATTR_FILL_GRADIENT, *pBindings, *this),
+ maHatchControl(SID_ATTR_FILL_HATCH, *pBindings, *this),
+ maBitmapControl(SID_ATTR_FILL_BITMAP, *pBindings, *this),
+ maGradientListControl(SID_GRADIENT_LIST, *pBindings, *this),
+ maHatchListControl(SID_HATCH_LIST, *pBindings, *this),
+ maBitmapListControl(SID_BITMAP_LIST, *pBindings, *this),
+ maPatternListControl(SID_PATTERN_LIST, *pBindings, *this),
+ maFillTransparenceController(SID_ATTR_FILL_TRANSPARENCE, *pBindings, *this),
+ maFillFloatTransparenceController(SID_ATTR_FILL_FLOATTRANSPARENCE, *pBindings, *this),
+ maFillUseSlideBackgroundController(SID_ATTR_FILL_USE_SLIDE_BACKGROUND, *pBindings, *this),
+ mpBindings(pBindings)
+{
+}
+
+AreaPropertyPanel::~AreaPropertyPanel()
+{
+ maStyleControl.dispose();
+ maColorControl.dispose();
+ maGradientControl.dispose();
+ maHatchControl.dispose();
+ maBitmapControl.dispose();
+ maGradientListControl.dispose();
+ maHatchListControl.dispose();
+ maBitmapListControl.dispose();
+ maPatternListControl.dispose();
+ maFillTransparenceController.dispose();
+ maFillFloatTransparenceController.dispose();
+}
+
+std::unique_ptr<PanelLayout> AreaPropertyPanel::Create (
+ weld::Widget* pParent,
+ const css::uno::Reference<css::frame::XFrame>& rxFrame,
+ SfxBindings* pBindings)
+{
+ if (pParent == nullptr)
+ throw lang::IllegalArgumentException("no parent Window given to AreaPropertyPanel::Create", nullptr, 0);
+ if ( ! rxFrame.is())
+ throw lang::IllegalArgumentException("no XFrame given to AreaPropertyPanel::Create", nullptr, 1);
+ if (pBindings == nullptr)
+ throw lang::IllegalArgumentException("no SfxBindings given to AreaPropertyPanel::Create", nullptr, 2);
+
+ return std::make_unique<AreaPropertyPanel>(pParent, rxFrame, pBindings);
+}
+
+void AreaPropertyPanel::setFillTransparence(const XFillTransparenceItem& rItem)
+{
+ GetBindings()->GetDispatcher()->ExecuteList(SID_ATTR_FILL_TRANSPARENCE,
+ SfxCallMode::RECORD, { &rItem });
+}
+
+void AreaPropertyPanel::setFillUseBackground(const XFillStyleItem* pStyleItem,
+ const XFillUseSlideBackgroundItem& rItem)
+{
+ const SfxPoolItem* pItem = nullptr;
+ auto pDispatcher = GetBindings()->GetDispatcher();
+ auto state = pDispatcher->QueryState(SID_ATTR_FILL_USE_SLIDE_BACKGROUND, pItem);
+ // FillUseSlideBackground is only available in Impress
+ if (state == SfxItemState::DISABLED)
+ {
+ setFillStyle(*pStyleItem);
+ }
+ else
+ {
+ pDispatcher->ExecuteList(SID_ATTR_FILL_USE_SLIDE_BACKGROUND, SfxCallMode::RECORD,
+ std::initializer_list<SfxPoolItem const*>{ &rItem, pStyleItem });
+ }
+}
+
+void AreaPropertyPanel::setFillFloatTransparence(const XFillFloatTransparenceItem& rItem)
+{
+ GetBindings()->GetDispatcher()->ExecuteList(SID_ATTR_FILL_FLOATTRANSPARENCE,
+ SfxCallMode::RECORD, { &rItem });
+}
+
+void AreaPropertyPanel::setFillStyle(const XFillStyleItem& rItem)
+{
+ GetBindings()->GetDispatcher()->ExecuteList(SID_ATTR_FILL_STYLE,
+ SfxCallMode::RECORD, { &rItem });
+}
+
+void AreaPropertyPanel::setFillStyleAndColor(const XFillStyleItem* pStyleItem,
+ const XFillColorItem& rColorItem)
+{
+ GetBindings()->GetDispatcher()->ExecuteList(SID_ATTR_FILL_COLOR,
+ SfxCallMode::RECORD, pStyleItem
+ ? std::initializer_list<SfxPoolItem const*>{ &rColorItem, pStyleItem }
+ : std::initializer_list<SfxPoolItem const*>{ &rColorItem });
+}
+
+void AreaPropertyPanel::setFillStyleAndGradient(const XFillStyleItem* pStyleItem,
+ const XFillGradientItem& rGradientItem)
+{
+ GetBindings()->GetDispatcher()->ExecuteList(SID_ATTR_FILL_GRADIENT,
+ SfxCallMode::RECORD, pStyleItem
+ ? std::initializer_list<SfxPoolItem const*>{ &rGradientItem, pStyleItem }
+ : std::initializer_list<SfxPoolItem const*>{ &rGradientItem });
+}
+
+void AreaPropertyPanel::setFillStyleAndHatch(const XFillStyleItem* pStyleItem,
+ const XFillHatchItem& rHatchItem)
+{
+ GetBindings()->GetDispatcher()->ExecuteList(SID_ATTR_FILL_HATCH,
+ SfxCallMode::RECORD, pStyleItem
+ ? std::initializer_list<SfxPoolItem const*>{ &rHatchItem, pStyleItem }
+ : std::initializer_list<SfxPoolItem const*>{ &rHatchItem });
+}
+
+void AreaPropertyPanel::setFillStyleAndBitmap(const XFillStyleItem* pStyleItem,
+ const XFillBitmapItem& rBitmapItem)
+{
+ GetBindings()->GetDispatcher()->ExecuteList(SID_ATTR_FILL_BITMAP,
+ SfxCallMode::RECORD, pStyleItem
+ ? std::initializer_list<SfxPoolItem const*>{ &rBitmapItem, pStyleItem }
+ : std::initializer_list<SfxPoolItem const*>{ &rBitmapItem });
+}
+
+} // end of namespace svx::sidebar
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/sidebar/area/AreaPropertyPanel.hxx b/svx/source/sidebar/area/AreaPropertyPanel.hxx
new file mode 100644
index 000000000..6c398e6ac
--- /dev/null
+++ b/svx/source/sidebar/area/AreaPropertyPanel.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_SVX_SOURCE_SIDEBAR_AREA_AREAPROPERTYPANEL_HXX
+#define INCLUDED_SVX_SOURCE_SIDEBAR_AREA_AREAPROPERTYPANEL_HXX
+
+#include <sfx2/sidebar/ControllerItem.hxx>
+#include <svx/xfillit0.hxx>
+#include <svx/xflclit.hxx>
+#include <svx/xflgrit.hxx>
+#include <svx/xflhtit.hxx>
+#include <svx/xbtmpit.hxx>
+
+#include <svx/sidebar/AreaPropertyPanelBase.hxx>
+
+class XFillFloatTransparenceItem;
+class XFillTransparenceItem;
+class XFillUseSlideBackgroundItem;
+class XFillStyleItem;
+class XFillGradientItem;
+class XFillColorItem;
+class XFillHatchItem;
+class XFillBitmapItem;
+
+namespace svx::sidebar {
+
+class AreaTransparencyGradientControl;
+
+class AreaPropertyPanel : public AreaPropertyPanelBase
+{
+public:
+ static std::unique_ptr<PanelLayout> Create(
+ weld::Widget* pParent,
+ const css::uno::Reference<css::frame::XFrame>& rxFrame,
+ SfxBindings* pBindings);
+
+ SfxBindings* GetBindings() { return mpBindings;}
+
+ // constructor/destructor
+ AreaPropertyPanel(
+ weld::Widget* pParent,
+ const css::uno::Reference<css::frame::XFrame>& rxFrame,
+ SfxBindings* pBindings);
+
+ virtual ~AreaPropertyPanel() override;
+
+ virtual void setFillTransparence(const XFillTransparenceItem& rItem) override;
+ virtual void setFillUseBackground(const XFillStyleItem* pStyleItem, const XFillUseSlideBackgroundItem& rItem) override;
+ virtual void setFillFloatTransparence(const XFillFloatTransparenceItem& rItem) override;
+ virtual void setFillStyle(const XFillStyleItem& rItem) override;
+ virtual void setFillStyleAndColor(const XFillStyleItem* pStyleItem, const XFillColorItem& aColorItem) override;
+ virtual void setFillStyleAndGradient(const XFillStyleItem* pStyleItem, const XFillGradientItem& aGradientItem) override;
+ virtual void setFillStyleAndHatch(const XFillStyleItem* pStyleItem, const XFillHatchItem& aHatchItem) override;
+ virtual void setFillStyleAndBitmap(const XFillStyleItem* pStyleItem, const XFillBitmapItem& aHatchItem) override;
+
+private:
+ ::sfx2::sidebar::ControllerItem maStyleControl;
+ ::sfx2::sidebar::ControllerItem maColorControl;
+ ::sfx2::sidebar::ControllerItem maGradientControl;
+ ::sfx2::sidebar::ControllerItem maHatchControl;
+ ::sfx2::sidebar::ControllerItem maBitmapControl;
+ ::sfx2::sidebar::ControllerItem maGradientListControl;
+ ::sfx2::sidebar::ControllerItem maHatchListControl;
+ ::sfx2::sidebar::ControllerItem maBitmapListControl;
+ ::sfx2::sidebar::ControllerItem maPatternListControl;
+ ::sfx2::sidebar::ControllerItem maFillTransparenceController;
+ ::sfx2::sidebar::ControllerItem maFillFloatTransparenceController;
+ ::sfx2::sidebar::ControllerItem maFillUseSlideBackgroundController;
+
+ SfxBindings* mpBindings;
+};
+
+
+} // end of namespace svx::sidebar
+
+
+#endif // INCLUDED_SVX_SOURCE_SIDEBAR_AREA_AREAPROPERTYPANEL_HXX
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/sidebar/area/AreaPropertyPanelBase.cxx b/svx/source/sidebar/area/AreaPropertyPanelBase.cxx
new file mode 100644
index 000000000..672ab145f
--- /dev/null
+++ b/svx/source/sidebar/area/AreaPropertyPanelBase.cxx
@@ -0,0 +1,1371 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * 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 <svx/sidebar/AreaPropertyPanelBase.hxx>
+#include <svx/drawitem.hxx>
+#include <svx/itemwin.hxx>
+#include <svx/svxids.hrc>
+#include <sfx2/objsh.hxx>
+#include <svx/xfltrit.hxx>
+#include <svx/xflftrit.hxx>
+#include <svx/xfilluseslidebackgrounditem.hxx>
+#include <svx/xtable.hxx>
+#include <sfx2/sidebar/Panel.hxx>
+#include <sfx2/opengrf.hxx>
+#include <sfx2/weldutils.hxx>
+#include <tools/urlobj.hxx>
+#include <bitmaps.hlst>
+#include <comphelper/lok.hxx>
+
+using namespace css;
+using namespace css::uno;
+
+constexpr OStringLiteral SIDEBARGRADIENT = "sidebargradient";
+
+namespace svx::sidebar {
+
+namespace {
+
+enum eFillStyle
+{
+ NONE,
+ SOLID,
+ GRADIENT,
+ HATCH,
+ BITMAP,
+ PATTERN,
+ USE_BACKGROUND
+};
+
+}
+
+const sal_Int32 AreaPropertyPanelBase::DEFAULT_CENTERX = 50;
+const sal_Int32 AreaPropertyPanelBase::DEFAULT_CENTERY = 50;
+const sal_Int32 AreaPropertyPanelBase::DEFAULT_ANGLE = 0;
+const sal_Int32 AreaPropertyPanelBase::DEFAULT_STARTVALUE = 0;
+const sal_Int32 AreaPropertyPanelBase::DEFAULT_ENDVALUE = 16777215;
+const sal_Int32 AreaPropertyPanelBase::DEFAULT_BORDER = 0;
+
+AreaPropertyPanelBase::AreaPropertyPanelBase(
+ weld::Widget* pParent,
+ const css::uno::Reference<css::frame::XFrame>& rxFrame)
+ : PanelLayout(pParent, "AreaPropertyPanel", "svx/ui/sidebararea.ui"),
+ mxFrame(rxFrame),
+ meLastXFS(static_cast<sal_uInt16>(-1)),
+ mnLastPosHatch(0),
+ mnLastPosBitmap(0),
+ mnLastPosPattern(0),
+ mnLastTransSolid(50),
+ mxColorTextFT(m_xBuilder->weld_label("filllabel")),
+ mxLbFillType(m_xBuilder->weld_combo_box("fillstylearea")),
+ mxLbFillAttr(m_xBuilder->weld_combo_box("fillattrhb")),
+ mxLbFillGradFrom(new ColorListBox(m_xBuilder->weld_menu_button("fillgrad1"), [this]{ return GetFrameWeld(); })),
+ mxLbFillGradTo(new ColorListBox(m_xBuilder->weld_menu_button("fillgrad2"), [this]{ return GetFrameWeld(); })),
+ mxToolBoxColor(m_xBuilder->weld_toolbar("selectcolor")),
+ mxColorDispatch(new ToolbarUnoDispatcher(*mxToolBoxColor, *m_xBuilder, rxFrame)),
+ mxTrspTextFT(m_xBuilder->weld_label("transparencylabel")),
+ mxLBTransType(m_xBuilder->weld_combo_box("transtype")),
+ mxMTRTransparent(m_xBuilder->weld_metric_spin_button("settransparency", FieldUnit::PERCENT)),
+ mxSldTransparent(m_xBuilder->weld_scale("transparencyslider")),
+ mxBTNGradient(m_xBuilder->weld_toolbar("selectgradient")),
+ mxMTRAngle(m_xBuilder->weld_metric_spin_button("gradangle", FieldUnit::DEGREE)),
+ mxGradientStyle(m_xBuilder->weld_combo_box("gradientstyle")),
+ mxBmpImport(m_xBuilder->weld_button("bmpimport")),
+ maImgAxial(BMP_AXIAL),
+ maImgElli(BMP_ELLI),
+ maImgQuad(BMP_QUAD),
+ maImgRadial(BMP_RADIAL),
+ maImgSquare(BMP_SQUARE),
+ maImgLinear(BMP_LINEAR)
+{
+ Initialize();
+}
+
+AreaPropertyPanelBase::~AreaPropertyPanelBase()
+{
+ mxTrGrPopup.reset();
+ mxColorTextFT.reset();
+ mxLbFillType.reset();
+ mxLbFillAttr.reset();
+ mxColorDispatch.reset();
+ mxToolBoxColor.reset();
+ mxTrspTextFT.reset();
+ mxLBTransType.reset();
+ mxMTRTransparent.reset();
+ mxSldTransparent.reset();
+ mxBTNGradient.reset();
+ mxMTRAngle.reset();
+ mxLbFillGradFrom.reset();
+ mxLbFillGradTo.reset();
+ mxGradientStyle.reset();
+ mxBmpImport.reset();
+}
+
+void AreaPropertyPanelBase::Initialize()
+{
+ SvxFillTypeBox::Fill(*mxLbFillType);
+
+ mxLbFillAttr->set_size_request(42, -1);
+
+ maGradientLinear.SetXOffset(DEFAULT_CENTERX);
+ maGradientLinear.SetYOffset(DEFAULT_CENTERY);
+ maGradientLinear.SetAngle(Degree10(DEFAULT_ANGLE));
+ maGradientLinear.SetStartColor(Color(DEFAULT_STARTVALUE));
+ maGradientLinear.SetEndColor(Color(DEFAULT_ENDVALUE));
+ maGradientLinear.SetBorder(DEFAULT_BORDER);
+ maGradientLinear.SetGradientStyle(css::awt::GradientStyle_LINEAR);
+
+ maGradientAxial = maGradientLinear;
+ maGradientAxial.SetGradientStyle(css::awt::GradientStyle_AXIAL);
+
+ maGradientRadial = maGradientLinear;
+ maGradientRadial.SetGradientStyle(css::awt::GradientStyle_RADIAL);
+
+ maGradientElliptical = maGradientLinear;
+ maGradientElliptical.SetGradientStyle(css::awt::GradientStyle_ELLIPTICAL);
+
+ maGradientSquare = maGradientLinear;
+ maGradientSquare.SetGradientStyle(css::awt::GradientStyle_SQUARE);
+
+ maGradientRect = maGradientLinear;
+ maGradientRect.SetGradientStyle(css::awt::GradientStyle_RECT);
+
+
+ mxLbFillType->connect_changed( LINK( this, AreaPropertyPanelBase, SelectFillTypeHdl ) );
+
+ Link<weld::ComboBox&,void> aLink = LINK( this, AreaPropertyPanelBase, SelectFillAttrHdl );
+ mxLbFillAttr->connect_changed( aLink );
+ mxGradientStyle->connect_changed( aLink );
+ Link<ColorListBox&,void> aLink3 = LINK( this, AreaPropertyPanelBase, SelectFillColorHdl );
+ mxLbFillGradFrom->SetSelectHdl( aLink3 );
+ mxLbFillGradTo->SetSelectHdl( aLink3 );
+ mxMTRAngle->connect_value_changed(LINK(this,AreaPropertyPanelBase, ChangeGradientAngle));
+
+ // https://gerrit.libreoffice.org/c/core/+/87313 set a small width to force widgets to
+ // take their final width from other widgets in the grid
+ mxLbFillGradFrom->get_widget().set_size_request(42, -1);
+ mxLbFillGradTo->get_widget().set_size_request(42, -1);
+
+ mxLBTransType->connect_changed(LINK(this, AreaPropertyPanelBase, ChangeTrgrTypeHdl_Impl));
+
+ SetTransparency( 50 );
+ mxMTRTransparent->connect_value_changed(LINK(this, AreaPropertyPanelBase, ModifyTransparentHdl_Impl));
+ mxSldTransparent->connect_value_changed(LINK(this, AreaPropertyPanelBase, ModifyTransSliderHdl));
+
+ mxTrGrPopup = std::make_unique<AreaTransparencyGradientPopup>(mxFrame, *this, mxBTNGradient.get());
+
+ mxBTNGradient->set_item_popover(SIDEBARGRADIENT, mxTrGrPopup->getTopLevel());
+ mxBTNGradient->connect_clicked(LINK(this, AreaPropertyPanelBase, ToolbarHdl_Impl));
+
+ mxBTNGradient->set_item_icon_name(SIDEBARGRADIENT, maImgLinear);
+ mxBTNGradient->hide();
+ mxBmpImport->connect_clicked( LINK(this, AreaPropertyPanelBase, ClickImportBitmapHdl));
+}
+
+IMPL_LINK_NOARG(AreaPropertyPanelBase, ToolbarHdl_Impl, const OString&, void)
+{
+ mxBTNGradient->set_menu_item_active(SIDEBARGRADIENT, !mxBTNGradient->get_menu_item_active(SIDEBARGRADIENT));
+}
+
+void AreaPropertyPanelBase::SetTransparency(sal_uInt16 nVal)
+{
+ mxSldTransparent->set_value(nVal);
+ mxMTRTransparent->set_value(nVal, FieldUnit::PERCENT);
+}
+
+IMPL_LINK_NOARG(AreaPropertyPanelBase, ClickImportBitmapHdl, weld::Button&, void)
+{
+ SvxOpenGraphicDialog aDlg("Import", GetFrameWeld());
+ aDlg.EnableLink(false);
+ if( aDlg.Execute() != ERRCODE_NONE )
+ return;
+
+ Graphic aGraphic;
+ auto xWait = std::make_unique<weld::WaitObject>(m_xContainer.get());
+ ErrCode nError = aDlg.GetGraphic( aGraphic );
+ xWait.reset();
+ if( nError != ERRCODE_NONE )
+ return;
+
+ mxLbFillAttr->clear();
+
+ if (SfxObjectShell* pSh = SfxObjectShell::Current())
+ {
+ INetURLObject aURL(aDlg.GetPath());
+ OUString aFileName = aURL.GetLastName().getToken(0, '.');
+ OUString aName = aFileName;
+
+ XBitmapListRef pList = pSh->GetItem(SID_BITMAP_LIST)->GetBitmapList();
+
+ tools::Long j = 1;
+ bool bValidBitmapName = false;
+ while( !bValidBitmapName )
+ {
+ bValidBitmapName = true;
+ for( tools::Long i = 0; i < pList->Count() && bValidBitmapName; i++ )
+ {
+ if( aName == pList->GetBitmap(i)->GetName() )
+ {
+ bValidBitmapName = false;
+ aName = aFileName + OUString::number(j++);
+ }
+ }
+ }
+
+ pList->Insert(std::make_unique<XBitmapEntry>(aGraphic, aName));
+ pList->Save();
+
+ SvxFillAttrBox::Fill(*mxLbFillAttr, pList);
+
+ mxLbFillAttr->set_active_text(aName);
+ SelectFillAttrHdl(*mxLbFillAttr);
+ }
+}
+
+IMPL_LINK_NOARG(AreaPropertyPanelBase, SelectFillTypeHdl, weld::ComboBox&, void)
+{
+ FillStyleChanged(true);
+}
+
+IMPL_LINK_NOARG(AreaPropertyPanelBase, SelectFillColorHdl, ColorListBox&, void)
+{
+ SelectFillAttrHdl_Impl();
+}
+
+IMPL_LINK_NOARG(AreaPropertyPanelBase, SelectFillAttrHdl, weld::ComboBox&, void)
+{
+ SelectFillAttrHdl_Impl();
+}
+
+IMPL_LINK_NOARG(AreaPropertyPanelBase, ChangeGradientAngle, weld::MetricSpinButton&, void)
+{
+ SelectFillAttrHdl_Impl();
+}
+
+void AreaPropertyPanelBase::SelectFillAttrHdl_Impl()
+{
+ sal_Int32 nPosFillStyle = static_cast<eFillStyle>(mxLbFillType->get_active());
+ SfxObjectShell* pSh = SfxObjectShell::Current();
+
+ // #i122676# dependent from bFillStyleChange, do execute a single or two
+ // changes in one Execute call
+ const bool bFillStyleChange(static_cast<eFillStyle>(meLastXFS) != static_cast<eFillStyle>(nPosFillStyle));
+
+ switch(nPosFillStyle)
+ {
+ case eFillStyle::NONE:
+ {
+ if(bFillStyleChange)
+ {
+ const XFillStyleItem aXFillStyleItem(drawing::FillStyle_NONE);
+ // Need to disable the XFillUseSlideBackgroundItem
+ const XFillUseSlideBackgroundItem aXFillUseSlideBackgroundItem(false);
+ setFillUseBackground(&aXFillStyleItem, aXFillUseSlideBackgroundItem);
+ }
+ break;
+ }
+ case eFillStyle::SOLID:
+ {
+ if(bFillStyleChange)
+ {
+ // #i122676# Single FillStyle change call needed here
+ XFillStyleItem aXFillStyleItem(drawing::FillStyle_SOLID);
+ setFillStyle(aXFillStyleItem);
+ }
+ break;
+ }
+ case eFillStyle::GRADIENT:
+ {
+
+ if (pSh && pSh->GetItem(SID_COLOR_TABLE))
+ {
+ XGradient aGradient;
+ aGradient.SetAngle(Degree10(mxMTRAngle->get_value(FieldUnit::DEGREE) * 10));
+ aGradient.SetGradientStyle(static_cast<css::awt::GradientStyle>(mxGradientStyle->get_active()));
+ aGradient.SetStartColor(mxLbFillGradFrom->GetSelectEntryColor());
+ aGradient.SetEndColor(mxLbFillGradTo->GetSelectEntryColor());
+
+ const XFillGradientItem aXFillGradientItem(mxLbFillAttr->get_active_text(), aGradient);
+
+ // #i122676# Change FillStyle and Gradient in one call
+ XFillStyleItem aXFillStyleItem(drawing::FillStyle_GRADIENT);
+ setFillStyleAndGradient(bFillStyleChange ? &aXFillStyleItem : nullptr, aXFillGradientItem);
+ }
+ break;
+ }
+ case eFillStyle::HATCH:
+ {
+ sal_Int32 nPos = mxLbFillAttr->get_active();
+
+ if (nPos == -1)
+ {
+ nPos = mnLastPosHatch;
+ }
+
+ if (nPos != -1 && pSh && pSh->GetItem(SID_HATCH_LIST))
+ {
+ const SvxHatchListItem * pItem = pSh->GetItem(SID_HATCH_LIST);
+
+ if(nPos < pItem->GetHatchList()->Count())
+ {
+ const XHatch aHatch = pItem->GetHatchList()->GetHatch(nPos)->GetHatch();
+ const XFillHatchItem aXFillHatchItem( mxLbFillAttr->get_active_text(), aHatch);
+
+ // #i122676# Change FillStyle and Hatch in one call
+ XFillStyleItem aXFillStyleItem(drawing::FillStyle_HATCH);
+ setFillStyleAndHatch(bFillStyleChange ? &aXFillStyleItem : nullptr, aXFillHatchItem);
+ }
+ }
+
+ if (nPos != -1)
+ {
+ mnLastPosHatch = nPos;
+ }
+ break;
+ }
+ case eFillStyle::BITMAP:
+ {
+ sal_Int32 nPos = mxLbFillAttr->get_active();
+
+ if (nPos == -1)
+ {
+ nPos = mnLastPosBitmap;
+ }
+
+ if (nPos != -1 && pSh && pSh->GetItem(SID_BITMAP_LIST))
+ {
+ const SvxBitmapListItem * pItem = pSh->GetItem(SID_BITMAP_LIST);
+
+ if(nPos < pItem->GetBitmapList()->Count())
+ {
+ const XBitmapEntry* pXBitmapEntry = pItem->GetBitmapList()->GetBitmap(nPos);
+ const XFillBitmapItem aXFillBitmapItem(mxLbFillAttr->get_active_text(), pXBitmapEntry->GetGraphicObject());
+
+ // #i122676# Change FillStyle and Bitmap in one call
+ XFillStyleItem aXFillStyleItem(drawing::FillStyle_BITMAP);
+ setFillStyleAndBitmap(bFillStyleChange ? &aXFillStyleItem : nullptr, aXFillBitmapItem);
+ }
+ }
+
+ if (nPos != -1)
+ {
+ mnLastPosBitmap = nPos;
+ }
+ break;
+ }
+ case eFillStyle::PATTERN:
+ {
+ sal_Int32 nPos = mxLbFillAttr->get_active();
+
+ if (nPos == -1)
+ {
+ nPos = mnLastPosPattern;
+ }
+
+ if (nPos != -1 && pSh && pSh->GetItem(SID_PATTERN_LIST))
+ {
+ const SvxPatternListItem * pItem = pSh->GetItem(SID_PATTERN_LIST);
+
+ if(nPos < pItem->GetPatternList()->Count())
+ {
+ const XBitmapEntry* pXPatternEntry = pItem->GetPatternList()->GetBitmap(nPos);
+ const XFillBitmapItem aXFillBitmapItem(mxLbFillAttr->get_active_text(), pXPatternEntry->GetGraphicObject());
+
+ // #i122676# Change FillStyle and Bitmap in one call
+ XFillStyleItem aXFillStyleItem(drawing::FillStyle_BITMAP);
+ setFillStyleAndBitmap(bFillStyleChange ? &aXFillStyleItem : nullptr, aXFillBitmapItem);
+ }
+ }
+
+ if (nPos != -1)
+ {
+ mnLastPosPattern = nPos;
+ }
+ break;
+ }
+ }
+ if (m_pPanel && !comphelper::LibreOfficeKit::isActive())
+ m_pPanel->TriggerDeckLayouting();
+}
+
+void AreaPropertyPanelBase::FillStyleChanged(bool bUpdateModel)
+{
+ sal_Int32 nPos = static_cast<eFillStyle>(mxLbFillType->get_active());
+ mxLbFillAttr->clear();
+ SfxObjectShell* pSh = SfxObjectShell::Current();
+ if (!pSh)
+ return;
+
+ bool bShowLbFillAttr = false;
+ bool bShowLbFillGradFrom = false;
+ bool bShowLbFillGradTo = false;
+ bool bShowGradientStyle = false;
+ bool bShowMTRAngle = false;
+ bool bShowToolBoxColor = false;
+ bool bShowBmpImport = false;
+
+ // #i122676# Do no longer trigger two Execute calls, one for SID_ATTR_FILL_STYLE
+ // and one for setting the fill attribute itself, but add two SfxPoolItems to the
+ // call to get just one action at the SdrObject and to create only one Undo action, too.
+ // Checked that this works in all apps.
+ switch (nPos)
+ {
+ default:
+ case NONE:
+ {
+ if (bUpdateModel)
+ {
+ const XFillStyleItem aXFillStyleItem(drawing::FillStyle_NONE);
+ // Need to disable the XFillUseSlideBackgroundItem
+ const XFillUseSlideBackgroundItem aXFillUseSlideBackgroundItem(false);
+ setFillUseBackground(&aXFillStyleItem, aXFillUseSlideBackgroundItem);
+ }
+
+ break;
+ }
+ case SOLID:
+ {
+ bShowToolBoxColor = true;
+
+ if (bUpdateModel)
+ {
+ const Color aColor = mpColorItem ? mpColorItem->GetColorValue() : COL_AUTO;
+ const XFillColorItem aXFillColorItem("", aColor);
+
+ // #i122676# change FillStyle and Color in one call
+ XFillStyleItem aXFillStyleItem(drawing::FillStyle_SOLID);
+ setFillStyleAndColor(&aXFillStyleItem, aXFillColorItem);
+ }
+ break;
+ }
+ case GRADIENT:
+ {
+ bShowLbFillGradFrom = true;
+ bShowLbFillGradTo = true;
+ bShowGradientStyle = true;
+ bShowMTRAngle = true;
+
+ mxLbFillAttr->set_sensitive(true);
+ mxLbFillGradTo->set_sensitive(true);
+ mxLbFillGradFrom->set_sensitive(true);
+ mxGradientStyle->set_sensitive(true);
+ mxMTRAngle->set_sensitive(true);
+ mxLbFillAttr->clear();
+
+ if (bUpdateModel)
+ {
+ mxLbFillAttr->hide();
+ mxToolBoxColor->hide();
+ mxBmpImport->hide();
+
+ const SvxGradientListItem* pItem = pSh->GetItem(SID_GRADIENT_LIST);
+ if (pItem->GetGradientList()->Count() > 0)
+ {
+ const XGradient aGradient
+ = pItem->GetGradientList()->GetGradient(0)->GetGradient();
+ const OUString aName = pItem->GetGradientList()->GetGradient(0)->GetName();
+ const XFillGradientItem aXFillGradientItem(aName, aGradient);
+
+ // #i122676# change FillStyle and Gradient in one call
+ XFillStyleItem aXFillStyleItem(drawing::FillStyle_GRADIENT);
+ setFillStyleAndGradient(&aXFillStyleItem, aXFillGradientItem);
+ mxLbFillGradFrom->SelectEntry(aGradient.GetStartColor());
+ mxLbFillGradTo->SelectEntry(aGradient.GetEndColor());
+
+ mxMTRAngle->set_value(toDegrees(aGradient.GetAngle()), FieldUnit::DEGREE);
+ css::awt::GradientStyle eXGS = aGradient.GetGradientStyle();
+ mxGradientStyle->set_active(sal::static_int_cast<sal_Int32>(eXGS));
+ }
+ }
+ else
+ {
+ if (pSh && pSh->GetItem(SID_GRADIENT_LIST))
+ {
+ SvxFillAttrBox::Fill(*mxLbFillAttr,
+ pSh->GetItem(SID_GRADIENT_LIST)->GetGradientList());
+ mxLbFillGradTo->SetNoSelection();
+ mxLbFillGradFrom->SetNoSelection();
+ if (mpFillGradientItem)
+ {
+ const OUString aString(mpFillGradientItem->GetName());
+ mxLbFillAttr->set_active_text(aString);
+ const XGradient aGradient = mpFillGradientItem->GetGradientValue();
+ mxLbFillGradFrom->SelectEntry(aGradient.GetStartColor());
+ mxLbFillGradTo->SelectEntry(aGradient.GetEndColor());
+ mxGradientStyle->set_active(
+ sal::static_int_cast<sal_Int32>(aGradient.GetGradientStyle()));
+ if (mxGradientStyle->get_active() == sal_Int32(GradientStyle::Radial))
+ mxMTRAngle->set_sensitive(false);
+ else
+ mxMTRAngle->set_value(toDegrees(aGradient.GetAngle()),
+ FieldUnit::DEGREE);
+ }
+ else
+ {
+ mxLbFillAttr->set_active(-1);
+ }
+ }
+ else
+ {
+ mxLbFillAttr->set_active(-1);
+ }
+ }
+ break;
+ }
+ case HATCH:
+ {
+ bShowLbFillAttr = true;
+
+ const SvxHatchListItem* pItem(pSh->GetItem(SID_HATCH_LIST));
+ if (pItem)
+ {
+ const XHatchListRef& pXHatchList(pItem->GetHatchList());
+ mxLbFillAttr->set_sensitive(true);
+ mxLbFillAttr->clear();
+ SvxFillAttrBox::Fill(*mxLbFillAttr, pXHatchList);
+
+ if (mnLastPosHatch != -1)
+ {
+ if (mnLastPosHatch < pXHatchList->Count())
+ {
+ const XHatch aHatch = pXHatchList->GetHatch(mnLastPosHatch)->GetHatch();
+ const OUString aName = pXHatchList->GetHatch(mnLastPosHatch)->GetName();
+ const XFillHatchItem aXFillHatchItem(aName, aHatch);
+
+ // #i122676# change FillStyle and Hatch in one call
+ if (bUpdateModel)
+ {
+ XFillStyleItem aXFillStyleItem(drawing::FillStyle_HATCH);
+ setFillStyleAndHatch(&aXFillStyleItem, aXFillHatchItem);
+ }
+ mxLbFillAttr->set_active(mnLastPosHatch);
+ }
+ }
+ }
+ else
+ {
+ mxLbFillAttr->set_sensitive(false);
+ }
+ break;
+ }
+ case BITMAP:
+ case PATTERN:
+ {
+ bShowLbFillAttr = true;
+ mxLbFillAttr->set_sensitive(true);
+ mxLbFillAttr->clear();
+
+ OUString aName;
+ GraphicObject aBitmap;
+ if (nPos == static_cast<sal_Int32>(BITMAP))
+ {
+ if (!comphelper::LibreOfficeKit::isActive())
+ bShowBmpImport = true;
+ const SvxBitmapListItem* pItem = pSh->GetItem(SID_BITMAP_LIST);
+ if (pItem)
+ {
+ const XBitmapListRef& pXBitmapList(pItem->GetBitmapList());
+ SvxFillAttrBox::Fill(*mxLbFillAttr, pXBitmapList);
+
+ if (mnLastPosBitmap != -1)
+ {
+ if (mnLastPosBitmap < pXBitmapList->Count())
+ {
+ const XBitmapEntry* pXBitmapEntry
+ = pXBitmapList->GetBitmap(mnLastPosBitmap);
+ aBitmap = pXBitmapEntry->GetGraphicObject();
+ aName = pXBitmapEntry->GetName();
+ mxLbFillAttr->set_active(mnLastPosBitmap);
+ }
+ }
+ }
+ else
+ {
+ bShowBmpImport = false;
+ }
+ }
+ else if (nPos == static_cast<sal_Int32>(PATTERN))
+ {
+ const SvxPatternListItem* pItem = pSh->GetItem(SID_PATTERN_LIST);
+ if (pItem)
+ {
+ const XPatternListRef& pXPatternList(pItem->GetPatternList());
+ SvxFillAttrBox::Fill(*mxLbFillAttr, pXPatternList);
+
+ if (mnLastPosPattern != -1)
+ {
+ if (mnLastPosPattern < pXPatternList->Count())
+ {
+ const XBitmapEntry* pXPatternEntry
+ = pXPatternList->GetBitmap(mnLastPosPattern);
+ aBitmap = pXPatternEntry->GetGraphicObject();
+ aName = pXPatternEntry->GetName();
+ mxLbFillAttr->set_active(mnLastPosPattern);
+ }
+ }
+ }
+ else
+ {
+ bShowLbFillAttr = false;
+ }
+ }
+ if (bUpdateModel)
+ {
+ const XFillBitmapItem aXFillBitmapItem(aName, aBitmap);
+ const XFillStyleItem aXFillStyleItem(drawing::FillStyle_BITMAP);
+ setFillStyleAndBitmap(&aXFillStyleItem, aXFillBitmapItem);
+ }
+ break;
+ }
+ case USE_BACKGROUND:
+ {
+ // No transparencies here
+ mxLBTransType->hide();
+ mxTrspTextFT->hide();
+ mxMTRTransparent->hide();
+ mxSldTransparent->hide();
+ mxBTNGradient->hide();
+ if (bUpdateModel)
+ {
+ const XFillStyleItem aXFillStyleItem(drawing::FillStyle_NONE);
+ const XFillUseSlideBackgroundItem aXFillUseSlideBackgroundItem(true);
+ setFillUseBackground(&aXFillStyleItem, aXFillUseSlideBackgroundItem);
+ break;
+ }
+ }
+ }
+
+ mxLbFillAttr->set_visible(bShowLbFillAttr);
+ mxLbFillGradFrom->set_visible(bShowLbFillGradFrom);
+ mxLbFillGradTo->set_visible(bShowLbFillGradTo);
+ mxGradientStyle->set_visible(bShowGradientStyle);
+ mxMTRAngle->set_visible(bShowMTRAngle);
+ mxToolBoxColor->set_visible(bShowToolBoxColor);
+ mxBmpImport->set_visible(bShowBmpImport);
+
+ meLastXFS = static_cast<sal_uInt16>(nPos);
+
+ if (m_pPanel && !comphelper::LibreOfficeKit::isActive())
+ m_pPanel->TriggerDeckLayouting();
+}
+
+void AreaPropertyPanelBase::ImpUpdateTransparencies()
+{
+ if(mpTransparenceItem || mpFloatTransparenceItem)
+ {
+ bool bZeroValue(false);
+
+ if (mpTransparenceItem)
+ {
+ const sal_uInt16 nValue(mpTransparenceItem->GetValue());
+
+ if(!nValue)
+ {
+ bZeroValue = true;
+ }
+ else if(nValue <= 100)
+ {
+ mxLBTransType->set_sensitive(true);
+ mxTrspTextFT->set_sensitive(true);
+ mxLBTransType->set_active(1);
+ mxBTNGradient->hide();
+ mxMTRTransparent->show();
+ mxSldTransparent->show();
+ mxMTRTransparent->set_sensitive(true);
+ mxSldTransparent->set_sensitive(true);
+ SetTransparency(nValue);
+ }
+
+ if (!bZeroValue && mxTrGrPopup)
+ {
+ mxBTNGradient->set_menu_item_active(SIDEBARGRADIENT, false);
+ }
+ }
+
+ if(bZeroValue && mpFloatTransparenceItem)
+ {
+ if(mpFloatTransparenceItem->IsEnabled())
+ {
+ const XGradient& rGradient = mpFloatTransparenceItem->GetGradientValue();
+ sal_Int32 nEntryPos(0);
+ OUString* pImage = nullptr;
+
+ mxLBTransType->set_sensitive(true);
+ mxTrspTextFT->set_sensitive(true);
+ mxMTRTransparent->hide();
+ mxSldTransparent->hide();
+ mxBTNGradient->set_sensitive(true);
+ mxBTNGradient->show();
+
+ switch(rGradient.GetGradientStyle())
+ {
+ default:
+ case css::awt::GradientStyle_LINEAR:
+ {
+ nEntryPos = 2;
+ pImage = &maImgLinear;
+ break;
+ }
+ case css::awt::GradientStyle_AXIAL:
+ {
+ nEntryPos = 3;
+ pImage = &maImgAxial;
+ break;
+ }
+ case css::awt::GradientStyle_RADIAL:
+ {
+ nEntryPos = 4;
+ pImage = &maImgRadial;
+ break;
+ }
+ case css::awt::GradientStyle_ELLIPTICAL:
+ {
+ nEntryPos = 5;
+ pImage = &maImgElli;
+ break;
+ }
+ case css::awt::GradientStyle_SQUARE:
+ {
+ nEntryPos = 6;
+ pImage = &maImgQuad;
+ break;
+ }
+ case css::awt::GradientStyle_RECT:
+ {
+ nEntryPos = 7;
+ pImage = &maImgSquare;
+ break;
+ }
+ }
+ mxLBTransType->set_active(nEntryPos);
+ mxBTNGradient->set_item_icon_name(SIDEBARGRADIENT, *pImage);
+ mxTrGrPopup->Rearrange(mpFloatTransparenceItem.get());
+ bZeroValue = false;
+ }
+ else
+ {
+ bZeroValue = true;
+ }
+ }
+
+ if(bZeroValue)
+ {
+ mxLBTransType->set_sensitive(true);
+ mxTrspTextFT->set_sensitive(true);
+ mxLBTransType->set_active(0);
+ mxBTNGradient->hide();
+ mxMTRTransparent->set_sensitive(true);
+ mxSldTransparent->set_sensitive(true);
+ mxMTRTransparent->show();
+ mxSldTransparent->show();
+ SetTransparency(0);
+ }
+ }
+ else
+ {
+ // no transparency at all
+ mxLBTransType->set_active(-1);
+ mxLBTransType->set_sensitive(false);
+ mxTrspTextFT->set_sensitive(false);
+ mxMTRTransparent->set_sensitive(false);
+ mxSldTransparent->set_sensitive(false);
+ mxMTRTransparent->show();
+ mxSldTransparent->show();
+ mxBTNGradient->set_sensitive(false);
+ mxBTNGradient->hide();
+ }
+}
+
+void AreaPropertyPanelBase::updateFillTransparence(bool bDisabled, bool bDefaultOrSet, const SfxPoolItem* pState)
+{
+ if (bDisabled)
+ {
+ mpTransparenceItem.reset();
+ return;
+ }
+ else if (bDefaultOrSet)
+ {
+ if (pState)
+ {
+ const SfxUInt16Item* pItem = static_cast<const SfxUInt16Item*>(pState);
+ mpTransparenceItem.reset(pItem->Clone());
+ }
+ else
+ {
+ mpTransparenceItem.reset();
+ }
+ }
+ else
+ {
+ mpTransparenceItem.reset();
+ }
+
+ // update transparency settings dependent of mpTransparenceItem and mpFloatTransparenceItem
+ ImpUpdateTransparencies();
+}
+
+void AreaPropertyPanelBase::updateFillFloatTransparence(bool bDisabled, bool bDefaultOrSet, const SfxPoolItem* pState)
+{
+ if (bDisabled)
+ {
+ mpFloatTransparenceItem.reset();
+ return;
+ }
+
+ if (bDefaultOrSet)
+ {
+ if (pState)
+ {
+ const XFillFloatTransparenceItem* pItem = static_cast<const XFillFloatTransparenceItem*>(pState);
+ mpFloatTransparenceItem.reset(pItem->Clone());
+ }
+ else
+ {
+ mpFloatTransparenceItem.reset();
+ }
+ }
+ else
+ {
+ mpFloatTransparenceItem.reset();
+ }
+
+ // update transparency settings dependent of mpTransparenceItem and mpFloatTransparenceItem
+ ImpUpdateTransparencies();
+}
+
+void AreaPropertyPanelBase::updateFillStyle(bool bDisabled, bool bDefaultOrSet, const SfxPoolItem* pState)
+{
+ if(bDisabled)
+ {
+ mxLbFillType->set_sensitive(false);
+ mxColorTextFT->set_sensitive(false);
+ mxLbFillType->set_active(-1);
+ mxLbFillAttr->show();
+ mxLbFillAttr->set_sensitive(false);
+ mxLbFillAttr->set_active(-1);
+ mxToolBoxColor->hide();
+ meLastXFS = static_cast<sal_uInt16>(-1);
+ mpStyleItem.reset();
+ }
+ else if (bDefaultOrSet && pState)
+ {
+ const XFillStyleItem* pItem = static_cast<const XFillStyleItem*>(pState);
+ mpStyleItem.reset(pItem->Clone());
+ mxLbFillType->set_sensitive(true);
+ mxColorTextFT->set_sensitive(true);
+ drawing::FillStyle eXFS = mpStyleItem->GetValue();
+ eFillStyle nPos = NONE;
+ switch(eXFS)
+ {
+ default:
+ case drawing::FillStyle_NONE:
+ {
+ mxLbFillAttr->set_active(-1);
+ mxLbFillAttr->set_sensitive(false);
+ // "Use slide background" also uses FillStyle_NONE internally,
+ // don't switch listbox in that case (will be handled by updateFillUseBackground)
+ nPos = meLastXFS == USE_BACKGROUND ? USE_BACKGROUND : NONE;
+ break;
+ }
+ case drawing::FillStyle_SOLID:
+ nPos = SOLID;
+ break;
+ case drawing::FillStyle_GRADIENT:
+ nPos = GRADIENT;
+ break;
+ case drawing::FillStyle_HATCH:
+ nPos = HATCH;
+ break;
+ case drawing::FillStyle_BITMAP:
+ {
+ if(mpBitmapItem)
+ {
+ if(!mpBitmapItem->isPattern())
+ nPos = BITMAP;
+ else
+ nPos = PATTERN;
+ }
+ else
+ nPos = BITMAP;
+ break;
+ }
+ }
+ meLastXFS = static_cast< sal_uInt16 >(mxLbFillType->get_active());
+ mxLbFillType->set_active(static_cast< sal_Int32 >(nPos));
+ FillStyleChanged(false);
+ return;
+ }
+
+ mxLbFillType->set_active(-1);
+ mxLbFillAttr->show();
+ mxLbFillAttr->set_sensitive(false);
+ mxLbFillAttr->set_active(-1);
+ mxToolBoxColor->hide();
+ meLastXFS = static_cast<sal_uInt16>(-1);
+ mpStyleItem.reset();
+}
+
+void AreaPropertyPanelBase::updateFillGradient(bool bDisabled, bool bDefaultOrSet, const SfxPoolItem* pState)
+{
+ if (bDefaultOrSet)
+ {
+ const XFillGradientItem* pItem = static_cast<const XFillGradientItem*>(pState);
+ mpFillGradientItem.reset(pItem ? pItem->Clone() : nullptr);
+ }
+
+ if(mpStyleItem && drawing::FillStyle_GRADIENT == mpStyleItem->GetValue())
+ {
+ mxLbFillAttr->hide();
+ mxLbFillGradFrom->show();
+ mxLbFillGradTo->show();
+ mxMTRAngle->show();
+ mxGradientStyle->show();
+ mxToolBoxColor->hide();
+
+ if (bDefaultOrSet)
+ {
+ mxLbFillType->set_active(GRADIENT);
+ FillStyleChanged(false);
+ }
+ else if(bDisabled)
+ {
+ mxLbFillGradFrom->SetNoSelection();
+ mxLbFillGradTo->SetNoSelection();
+ mxLbFillGradFrom->set_sensitive(false);
+ mxLbFillGradTo->set_sensitive(false);
+ mxMTRAngle->set_sensitive(false);
+ mxGradientStyle->set_sensitive(false);
+ }
+ else
+ {
+ mxLbFillGradFrom->SetNoSelection();
+ mxLbFillGradTo->SetNoSelection();
+ }
+ }
+}
+
+void AreaPropertyPanelBase::updateFillHatch(bool bDisabled, bool bDefaultOrSet, const SfxPoolItem* pState)
+{
+ if (bDefaultOrSet)
+ {
+ const XFillHatchItem* pItem = static_cast<const XFillHatchItem*>(pState);
+ mpHatchItem.reset(pItem ? pItem->Clone() : nullptr);
+ }
+
+ if(mpStyleItem && drawing::FillStyle_HATCH == mpStyleItem->GetValue())
+ {
+ mxLbFillAttr->show();
+ mxToolBoxColor->hide();
+
+ if (bDefaultOrSet)
+ {
+ mxLbFillAttr->set_sensitive(true);
+ mxLbFillType->set_active(HATCH);
+ FillStyleChanged(false);
+ }
+ else if(bDisabled)
+ {
+ mxLbFillAttr->set_sensitive(false);
+ mxLbFillAttr->set_active(-1);
+ }
+ else
+ {
+ mxLbFillAttr->set_active(-1);
+ }
+ }
+ FillStyleChanged(false);
+}
+
+void AreaPropertyPanelBase::updateFillColor(bool bDefaultOrSet, const SfxPoolItem* pState)
+{
+ if (bDefaultOrSet)
+ {
+ const XFillColorItem* pItem = static_cast<const XFillColorItem*>(pState);
+ mpColorItem.reset(pItem ? pItem->Clone() : nullptr);
+ }
+
+ if(mpStyleItem && drawing::FillStyle_SOLID == mpStyleItem->GetValue())
+ {
+ mxLbFillAttr->hide();
+ mxToolBoxColor->show();
+ mxLbFillType->set_active(SOLID);
+ FillStyleChanged(false);
+ }
+}
+
+void AreaPropertyPanelBase::updateFillBitmap(bool bDisabled, bool bDefaultOrSet, const SfxPoolItem* pState)
+{
+ if (bDefaultOrSet)
+ {
+ const XFillBitmapItem* pItem = static_cast<const XFillBitmapItem*>(pState);
+ mpBitmapItem.reset(pItem ? pItem->Clone() : nullptr);
+ }
+
+ if(mpStyleItem && drawing::FillStyle_BITMAP == mpStyleItem->GetValue())
+ {
+ mxLbFillAttr->show();
+ mxToolBoxColor->hide();
+
+ if (bDefaultOrSet)
+ {
+ if(mpBitmapItem->isPattern())
+ mxLbFillType->set_active(PATTERN);
+ else
+ mxLbFillType->set_active(BITMAP);
+ FillStyleChanged(false);
+ }
+ else if(bDisabled)
+ {
+ mxLbFillAttr->hide();
+ mxLbFillAttr->set_active(-1);
+ }
+ else
+ {
+ mxLbFillAttr->set_active(-1);
+ }
+ }
+}
+
+void AreaPropertyPanelBase::updateFillUseBackground(bool bDisabled, bool bDefaultOrSet, const SfxPoolItem* pState)
+{
+ if (bDisabled)
+ {
+ mpUseSlideBackgroundItem.reset();
+ return;
+ }
+
+ if (bDefaultOrSet)
+ {
+ if (pState)
+ {
+ const XFillUseSlideBackgroundItem* pItem = static_cast<const XFillUseSlideBackgroundItem*>(pState);
+ // When XFillUseSlideBackgroundItem is true, select "Use Background Fill". When false, select "None"
+ int nPos = pItem->GetValue() ? USE_BACKGROUND : NONE;
+ mxLbFillType->set_active(nPos);
+ mpUseSlideBackgroundItem.reset(pItem->Clone());
+ FillStyleChanged(false);
+ }
+ else
+ {
+ mpUseSlideBackgroundItem.reset();
+ }
+ }
+ else
+ {
+ mpUseSlideBackgroundItem.reset();
+ }
+}
+
+void AreaPropertyPanelBase::NotifyItemUpdate(
+ sal_uInt16 nSID,
+ SfxItemState eState,
+ const SfxPoolItem* pState)
+{
+ const bool bDisabled(SfxItemState::DISABLED == eState);
+ const bool bDefaultOrSet(SfxItemState::DEFAULT <= eState);
+ const bool bDefault(SfxItemState::DEFAULT == eState);
+
+ switch(nSID)
+ {
+ case SID_ATTR_FILL_TRANSPARENCE:
+ updateFillTransparence(bDisabled, bDefaultOrSet, pState);
+ break;
+ case SID_ATTR_FILL_FLOATTRANSPARENCE:
+ updateFillFloatTransparence(bDisabled, bDefaultOrSet, pState);
+ break;
+ case SID_ATTR_FILL_STYLE:
+ updateFillStyle(bDisabled, bDefaultOrSet, pState);
+ break;
+ case SID_ATTR_FILL_COLOR:
+ updateFillColor(bDefaultOrSet, pState);
+ break;
+ case SID_ATTR_FILL_GRADIENT:
+ updateFillGradient(bDisabled, bDefaultOrSet, pState);
+ break;
+ case SID_ATTR_FILL_HATCH:
+ updateFillHatch(bDisabled, bDefaultOrSet, pState);
+ break;
+ case SID_ATTR_FILL_BITMAP:
+ updateFillBitmap(bDisabled, bDefaultOrSet, pState);
+ break;
+ case SID_ATTR_FILL_USE_SLIDE_BACKGROUND:
+ updateFillUseBackground(bDisabled, bDefaultOrSet, pState);
+ break;
+ case SID_GRADIENT_LIST:
+ {
+ if(bDefault)
+ {
+ if(mpStyleItem && drawing::FillStyle_GRADIENT == mpStyleItem->GetValue())
+ {
+ if(mpFillGradientItem)
+ {
+ const OUString aString( mpFillGradientItem->GetName() );
+ const SfxObjectShell* pSh = SfxObjectShell::Current();
+ mxLbFillAttr->clear();
+ if (pSh)
+ {
+ mxLbFillAttr->set_sensitive(true);
+ SvxFillAttrBox::Fill(*mxLbFillAttr, pSh->GetItem(SID_GRADIENT_LIST)->GetGradientList());
+ }
+ mxLbFillAttr->set_active_text(aString);
+ }
+ else
+ {
+ mxLbFillAttr->set_active(-1);
+ }
+ }
+ }
+ break;
+ }
+ case SID_HATCH_LIST:
+ {
+ if(bDefault)
+ {
+ if(mpStyleItem && drawing::FillStyle_HATCH == mpStyleItem->GetValue())
+ {
+ if(mpHatchItem)
+ {
+ const OUString aString( mpHatchItem->GetName() );
+ const SfxObjectShell* pSh = SfxObjectShell::Current();
+ mxLbFillAttr->clear();
+ if (pSh)
+ {
+ mxLbFillAttr->set_sensitive(true);
+ SvxFillAttrBox::Fill(*mxLbFillAttr, pSh->GetItem(SID_HATCH_LIST)->GetHatchList());
+ }
+ mxLbFillAttr->set_active_text(aString);
+ }
+ else
+ {
+ mxLbFillAttr->set_active(-1);
+ }
+ }
+ }
+ break;
+ }
+ case SID_BITMAP_LIST:
+ case SID_PATTERN_LIST:
+ {
+ if(bDefault)
+ {
+ if(mpStyleItem && drawing::FillStyle_BITMAP == mpStyleItem->GetValue())
+ {
+ if(mpBitmapItem)
+ {
+ const OUString aString( mpBitmapItem->GetName() );
+ const SfxObjectShell* pSh = SfxObjectShell::Current();
+ mxLbFillAttr->clear();
+ mxLbFillAttr->show();
+ if (pSh)
+ {
+ if(nSID == SID_BITMAP_LIST)
+ SvxFillAttrBox::Fill(*mxLbFillAttr, pSh->GetItem(SID_BITMAP_LIST)->GetBitmapList());
+ else if(nSID == SID_PATTERN_LIST)
+ SvxFillAttrBox::Fill(*mxLbFillAttr, pSh->GetItem(SID_PATTERN_LIST)->GetPatternList());
+ }
+ mxLbFillAttr->set_active_text(aString);
+ }
+ else
+ {
+ mxLbFillAttr->set_active(-1);
+ }
+ }
+ }
+ break;
+ }
+ }
+ FillStyleChanged(false);
+}
+
+IMPL_LINK_NOARG(AreaPropertyPanelBase, ModifyTransSliderHdl, weld::Scale&, void)
+{
+ const sal_uInt16 nVal = mxSldTransparent->get_value();
+ SetTransparency(nVal);
+ const XFillTransparenceItem aLinearItem(nVal);
+ setFillTransparence(aLinearItem);
+}
+
+IMPL_LINK_NOARG(AreaPropertyPanelBase, ChangeTrgrTypeHdl_Impl, weld::ComboBox&, void)
+{
+ sal_Int32 nSelectType = mxLBTransType->get_active();
+ bool bGradient = false;
+ sal_uInt16 nTrans = 0;
+
+ if(!nSelectType)
+ {
+ mxBTNGradient->hide();
+ mxMTRTransparent->show();
+ mxSldTransparent->show();
+ mxMTRTransparent->set_sensitive(true);
+ mxSldTransparent->set_sensitive(true);
+ SetTransparency(0);
+ }
+ else if(1 == nSelectType)
+ {
+ mxBTNGradient->hide();
+ mxMTRTransparent->show();
+ mxSldTransparent->show();
+ nTrans = mnLastTransSolid;
+ mxMTRTransparent->set_value(nTrans, FieldUnit::PERCENT);
+ mxLBTransType->set_active(1);
+ mxMTRTransparent->set_sensitive(true);
+ mxSldTransparent->set_sensitive(true);
+ }
+ else
+ {
+ mxBTNGradient->show();
+
+ switch (nSelectType)
+ {
+ case 2:
+ mxBTNGradient->set_item_icon_name(SIDEBARGRADIENT, maImgLinear);
+ break;
+ case 3:
+ mxBTNGradient->set_item_icon_name(SIDEBARGRADIENT, maImgAxial);
+ break;
+ case 4:
+ mxBTNGradient->set_item_icon_name(SIDEBARGRADIENT, maImgRadial);
+ break;
+ case 5:
+ mxBTNGradient->set_item_icon_name(SIDEBARGRADIENT, maImgElli);
+ break;
+ case 6:
+ mxBTNGradient->set_item_icon_name(SIDEBARGRADIENT, maImgQuad);
+ break;
+ case 7:
+ mxBTNGradient->set_item_icon_name(SIDEBARGRADIENT, maImgSquare);
+ break;
+ }
+
+ mxMTRTransparent->hide();
+ mxSldTransparent->hide();
+ mxBTNGradient->set_sensitive(true);
+ bGradient = true;
+ }
+
+ const XFillTransparenceItem aLinearItem(nTrans);
+ setFillTransparence(aLinearItem);
+
+ if(nSelectType > 1)
+ {
+ nSelectType -= 2;
+ }
+
+ XGradient aTmpGradient;
+
+ switch(static_cast<css::awt::GradientStyle>(nSelectType))
+ {
+ case css::awt::GradientStyle_LINEAR:
+ aTmpGradient = maGradientLinear;
+ break;
+ case css::awt::GradientStyle_AXIAL:
+ aTmpGradient = maGradientAxial;
+ break;
+ case css::awt::GradientStyle_RADIAL:
+ aTmpGradient = maGradientRadial;
+ break;
+ case css::awt::GradientStyle_ELLIPTICAL:
+ aTmpGradient = maGradientElliptical;
+ break;
+ case css::awt::GradientStyle_SQUARE:
+ aTmpGradient = maGradientSquare;
+ break;
+ case css::awt::GradientStyle_RECT:
+ aTmpGradient = maGradientRect;
+ break;
+ default:
+ break;
+ }
+
+ const XFillFloatTransparenceItem aGradientItem(aTmpGradient, bGradient);
+ setFillFloatTransparence(aGradientItem);
+}
+
+IMPL_LINK_NOARG(AreaPropertyPanelBase, ModifyTransparentHdl_Impl, weld::MetricSpinButton&, void)
+{
+ const sal_uInt16 nTrans = static_cast<sal_uInt16>(mxMTRTransparent->get_value(FieldUnit::PERCENT));
+ mnLastTransSolid = nTrans;
+ SetTransparency(nTrans);
+ const sal_Int32 nSelectType = mxLBTransType->get_active();
+
+ if(nTrans && !nSelectType)
+ {
+ mxLBTransType->set_active(1);
+ }
+
+ const XFillTransparenceItem aLinearItem(nTrans);
+ setFillTransparence(aLinearItem);
+}
+
+const XGradient& AreaPropertyPanelBase::GetGradient (const css::awt::GradientStyle eStyle) const
+{
+ switch (eStyle)
+ {
+ default:
+ case css::awt::GradientStyle_LINEAR:
+ return maGradientLinear;
+ case css::awt::GradientStyle_AXIAL:
+ return maGradientAxial;
+ case css::awt::GradientStyle_RADIAL:
+ return maGradientRadial;
+ case css::awt::GradientStyle_ELLIPTICAL:
+ return maGradientElliptical;
+ case css::awt::GradientStyle_SQUARE:
+ return maGradientSquare;
+ case css::awt::GradientStyle_RECT:
+ return maGradientRect;
+ }
+}
+
+void AreaPropertyPanelBase::SetGradient (const XGradient& rGradient)
+{
+ switch (rGradient.GetGradientStyle())
+ {
+ case css::awt::GradientStyle_LINEAR:
+ maGradientLinear = rGradient;
+ break;
+ case css::awt::GradientStyle_AXIAL:
+ maGradientAxial = rGradient;
+ break;
+ case css::awt::GradientStyle_RADIAL:
+ maGradientRadial = rGradient;
+ break;
+ case css::awt::GradientStyle_ELLIPTICAL:
+ maGradientElliptical = rGradient;
+ break;
+ case css::awt::GradientStyle_SQUARE:
+ maGradientSquare = rGradient;
+ break;
+ case css::awt::GradientStyle_RECT:
+ maGradientRect = rGradient;
+ break;
+ default:
+ break;
+ }
+}
+
+sal_Int32 AreaPropertyPanelBase::GetSelectedTransparencyTypeIndex() const
+{
+ return mxLBTransType->get_active();
+}
+
+} // end of namespace svx::sidebar
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/sidebar/area/AreaTransparencyGradientPopup.cxx b/svx/source/sidebar/area/AreaTransparencyGradientPopup.cxx
new file mode 100644
index 000000000..2d92b6b4b
--- /dev/null
+++ b/svx/source/sidebar/area/AreaTransparencyGradientPopup.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 <svx/sidebar/AreaTransparencyGradientPopup.hxx>
+#include <svx/sidebar/AreaPropertyPanelBase.hxx>
+#include <svx/xflftrit.hxx>
+#include <svx/xgrad.hxx>
+
+namespace svx::sidebar {
+
+AreaTransparencyGradientPopup::AreaTransparencyGradientPopup(const css::uno::Reference<css::frame::XFrame>& rFrame,
+ AreaPropertyPanelBase& rPanel, weld::Widget* pParent)
+ : WeldToolbarPopup(rFrame, pParent, "svx/ui/floatingareastyle.ui", "FloatingAreaStyle")
+ , mrAreaPropertyPanel(rPanel)
+ , mxCenterGrid(m_xBuilder->weld_widget("centergrid"))
+ , mxAngleGrid(m_xBuilder->weld_widget("anglegrid"))
+ , mxMtrTrgrCenterX(m_xBuilder->weld_metric_spin_button("centerx", FieldUnit::PERCENT))
+ , mxMtrTrgrCenterY(m_xBuilder->weld_metric_spin_button("centery", FieldUnit::PERCENT))
+ , mxMtrTrgrAngle(m_xBuilder->weld_metric_spin_button("angle", FieldUnit::DEGREE))
+ , mxBtnLeft45(m_xBuilder->weld_toolbar("lefttoolbox"))
+ , mxBtnRight45(m_xBuilder->weld_toolbar("righttoolbox"))
+ , mxMtrTrgrStartValue(m_xBuilder->weld_metric_spin_button("start", FieldUnit::PERCENT))
+ , mxMtrTrgrEndValue(m_xBuilder->weld_metric_spin_button("end", FieldUnit::PERCENT))
+ , mxMtrTrgrBorder(m_xBuilder->weld_metric_spin_button("border", FieldUnit::PERCENT))
+{
+ Link<weld::MetricSpinButton&,void> aLink = LINK(this, AreaTransparencyGradientPopup, ModifiedTrgrHdl_Impl);
+ mxMtrTrgrCenterX->connect_value_changed(aLink);
+ mxMtrTrgrCenterY->connect_value_changed(aLink);
+ mxMtrTrgrAngle->connect_value_changed(aLink);
+ mxMtrTrgrBorder->connect_value_changed(aLink);
+ mxMtrTrgrStartValue->connect_value_changed(aLink);
+ mxMtrTrgrEndValue->connect_value_changed(aLink);
+ mxBtnLeft45->connect_clicked(LINK(this, AreaTransparencyGradientPopup, Left_Click45_Impl));
+ mxBtnRight45->connect_clicked(LINK(this, AreaTransparencyGradientPopup, Right_Click45_Impl));
+}
+
+AreaTransparencyGradientPopup::~AreaTransparencyGradientPopup()
+{
+}
+
+void AreaTransparencyGradientPopup::InitStatus(XFillFloatTransparenceItem const * pGradientItem)
+{
+ const XGradient& rGradient = pGradientItem->GetGradientValue();
+
+ XGradient aGradient;
+
+ if (rGradient.GetXOffset() == AreaPropertyPanelBase::DEFAULT_CENTERX
+ && rGradient.GetYOffset() == AreaPropertyPanelBase::DEFAULT_CENTERY
+ && static_cast<sal_Int32>(toDegrees(rGradient.GetAngle())) == AreaPropertyPanelBase::DEFAULT_ANGLE
+ && static_cast<sal_uInt16>(((static_cast<sal_uInt16>(rGradient.GetStartColor().GetRed()) + 1) * 100) / 255)
+ == AreaPropertyPanelBase::DEFAULT_STARTVALUE
+ && static_cast<sal_uInt16>(((static_cast<sal_uInt16>(rGradient.GetEndColor().GetRed()) + 1) * 100) / 255)
+ == AreaPropertyPanelBase::DEFAULT_ENDVALUE
+ && rGradient.GetBorder() == AreaPropertyPanelBase::DEFAULT_BORDER)
+ {
+ aGradient = mrAreaPropertyPanel.GetGradient(rGradient.GetGradientStyle());
+ }
+ else
+ {
+ aGradient = rGradient;
+ }
+ mxMtrTrgrCenterX->set_value(aGradient.GetXOffset(), FieldUnit::PERCENT);
+ mxMtrTrgrCenterY->set_value(aGradient.GetYOffset(), FieldUnit::PERCENT);
+ mxMtrTrgrAngle->set_value(toDegrees(aGradient.GetAngle()), FieldUnit::DEGREE);
+ mxMtrTrgrStartValue->set_value(static_cast<sal_uInt16>(((static_cast<sal_uInt16>(aGradient.GetStartColor().GetRed()) + 1) * 100) / 255), FieldUnit::PERCENT);
+ mxMtrTrgrEndValue->set_value(static_cast<sal_uInt16>(((static_cast<sal_uInt16>(aGradient.GetEndColor().GetRed()) + 1) * 100) / 255), FieldUnit::PERCENT);
+ mxMtrTrgrBorder->set_value(aGradient.GetBorder(), FieldUnit::PERCENT);
+}
+
+void AreaTransparencyGradientPopup::Rearrange(XFillFloatTransparenceItem const * pGradientItem)
+{
+ InitStatus(pGradientItem);
+ const XGradient& rGradient = pGradientItem->GetGradientValue();
+ css::awt::GradientStyle eXGS(rGradient.GetGradientStyle());
+
+ switch(eXGS)
+ {
+ case css::awt::GradientStyle_LINEAR:
+ case css::awt::GradientStyle_AXIAL:
+ mxCenterGrid->hide();
+ mxAngleGrid->show();
+ break;
+ case css::awt::GradientStyle_RADIAL:
+ mxCenterGrid->show();
+ mxAngleGrid->hide();
+ break;
+ case css::awt::GradientStyle_ELLIPTICAL:
+ case css::awt::GradientStyle_SQUARE:
+ case css::awt::GradientStyle_RECT:
+ mxCenterGrid->show();
+ mxAngleGrid->show();
+ break;
+ default:
+ break;
+ }
+}
+
+void AreaTransparencyGradientPopup::ExecuteValueModify(sal_uInt8 nStartCol, sal_uInt8 nEndCol)
+{
+ //Added
+ sal_Int16 aMtrValue = static_cast<sal_Int16>(mxMtrTrgrAngle->get_value(FieldUnit::DEGREE));
+ while(aMtrValue<0)
+ aMtrValue += 360;
+ sal_uInt16 nVal = aMtrValue/360;
+ nVal = aMtrValue - nVal*360;
+ mxMtrTrgrAngle->set_value(nVal, FieldUnit::DEGREE);
+ //End of new code
+ XGradient aTmpGradient(
+ Color(nStartCol, nStartCol, nStartCol),
+ Color(nEndCol, nEndCol, nEndCol),
+ static_cast<css::awt::GradientStyle>(mrAreaPropertyPanel.GetSelectedTransparencyTypeIndex()-2),
+ Degree10(static_cast<sal_Int16>(mxMtrTrgrAngle->get_value(FieldUnit::DEGREE)) * 10),
+ static_cast<sal_uInt16>(mxMtrTrgrCenterX->get_value(FieldUnit::PERCENT)),
+ static_cast<sal_uInt16>(mxMtrTrgrCenterY->get_value(FieldUnit::PERCENT)),
+ static_cast<sal_uInt16>(mxMtrTrgrBorder->get_value(FieldUnit::PERCENT)),
+ 100, 100);
+
+ mrAreaPropertyPanel.SetGradient(aTmpGradient);
+
+ XFillFloatTransparenceItem aGradientItem(aTmpGradient, true );
+
+ mrAreaPropertyPanel.setFillFloatTransparence(aGradientItem);
+}
+
+IMPL_LINK_NOARG(AreaTransparencyGradientPopup, ModifiedTrgrHdl_Impl, weld::MetricSpinButton&, void)
+{
+ sal_uInt8 nStartCol = static_cast<sal_uInt8>((static_cast<sal_uInt16>(mxMtrTrgrStartValue->get_value(FieldUnit::PERCENT)) * 255) / 100);
+ sal_uInt8 nEndCol = static_cast<sal_uInt8>((static_cast<sal_uInt16>(mxMtrTrgrEndValue->get_value(FieldUnit::PERCENT)) * 255) / 100);
+ ExecuteValueModify( nStartCol, nEndCol );
+}
+
+IMPL_LINK_NOARG(AreaTransparencyGradientPopup, Left_Click45_Impl, const OString&, void)
+{
+ sal_uInt8 nStartCol = static_cast<sal_uInt8>((static_cast<sal_uInt16>(mxMtrTrgrStartValue->get_value(FieldUnit::PERCENT)) * 255) / 100);
+ sal_uInt8 nEndCol = static_cast<sal_uInt8>((static_cast<sal_uInt16>(mxMtrTrgrEndValue->get_value(FieldUnit::PERCENT)) * 255) / 100);
+ sal_uInt16 nTemp = static_cast<sal_uInt16>(mxMtrTrgrAngle->get_value(FieldUnit::DEGREE));
+ if (nTemp>=315)
+ nTemp -= 360;
+ nTemp += 45;
+ mxMtrTrgrAngle->set_value(nTemp, FieldUnit::DEGREE);
+ ExecuteValueModify(nStartCol, nEndCol);
+}
+
+IMPL_LINK_NOARG(AreaTransparencyGradientPopup, Right_Click45_Impl, const OString&, void)
+{
+ sal_uInt8 nStartCol = static_cast<sal_uInt8>((static_cast<sal_uInt16>(mxMtrTrgrStartValue->get_value(FieldUnit::PERCENT)) * 255) / 100);
+ sal_uInt8 nEndCol = static_cast<sal_uInt8>((static_cast<sal_uInt16>(mxMtrTrgrEndValue->get_value(FieldUnit::PERCENT)) * 255) / 100);
+ sal_uInt16 nTemp = static_cast<sal_uInt16>(mxMtrTrgrAngle->get_value(FieldUnit::DEGREE));
+ if (nTemp<45)
+ nTemp += 360;
+ nTemp -= 45;
+ mxMtrTrgrAngle->set_value(nTemp, FieldUnit::DEGREE);
+ ExecuteValueModify(nStartCol, nEndCol);
+}
+
+void AreaTransparencyGradientPopup::GrabFocus()
+{
+ mxMtrTrgrCenterX->grab_focus();
+}
+
+} // end of namespace svx::sidebar
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/sidebar/effect/EffectPropertyPanel.cxx b/svx/source/sidebar/effect/EffectPropertyPanel.cxx
new file mode 100644
index 000000000..77e2dac11
--- /dev/null
+++ b/svx/source/sidebar/effect/EffectPropertyPanel.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/.
+ */
+
+#include <sal/config.h>
+
+#include "EffectPropertyPanel.hxx"
+
+#include <sfx2/dispatch.hxx>
+#include <svx/colorbox.hxx>
+#include <svx/sdmetitm.hxx>
+#include <svx/sdprcitm.hxx>
+#include <svx/svddef.hxx>
+#include <svx/svxids.hrc>
+#include <svx/xcolit.hxx>
+
+namespace svx::sidebar
+{
+EffectPropertyPanel::EffectPropertyPanel(weld::Widget* pParent, SfxBindings* pBindings)
+ : PanelLayout(pParent, "EffectPropertyPanel", "svx/ui/sidebareffect.ui")
+ , maGlowColorController(SID_ATTR_GLOW_COLOR, *pBindings, *this)
+ , maGlowRadiusController(SID_ATTR_GLOW_RADIUS, *pBindings, *this)
+ , maGlowTransparencyController(SID_ATTR_GLOW_TRANSPARENCY, *pBindings, *this)
+ , mxFTTransparency(m_xBuilder->weld_label("transparency"))
+ , maSoftEdgeRadiusController(SID_ATTR_SOFTEDGE_RADIUS, *pBindings, *this)
+ , mpBindings(pBindings)
+ , mxGlowRadius(m_xBuilder->weld_metric_spin_button("LB_GLOW_RADIUS", FieldUnit::POINT))
+ , mxLBGlowColor(new ColorListBox(m_xBuilder->weld_menu_button("LB_GLOW_COLOR"),
+ [this] { return GetFrameWeld(); }))
+ , mxGlowTransparency(
+ m_xBuilder->weld_metric_spin_button("LB_GLOW_TRANSPARENCY", FieldUnit::PERCENT))
+ , mxFTRadiusSoftEdge(m_xBuilder->weld_label("radiussoftedge"))
+ , mxFTRadiusGlow(m_xBuilder->weld_label("radiusglow"))
+ , mxFTColor(m_xBuilder->weld_label("glowcolorlabel"))
+ , mxSoftEdgeRadius(m_xBuilder->weld_metric_spin_button("SB_SOFTEDGE_RADIUS", FieldUnit::POINT))
+{
+ Initialize();
+}
+
+EffectPropertyPanel::~EffectPropertyPanel()
+{
+ mxGlowRadius.reset();
+ mxLBGlowColor.reset();
+ mxGlowTransparency.reset();
+ mxFTRadiusSoftEdge.reset();
+ mxFTColor.reset();
+ mxFTTransparency.reset();
+ mxSoftEdgeRadius.reset();
+ mxFTRadiusGlow.reset();
+
+ maGlowColorController.dispose();
+ maGlowRadiusController.dispose();
+ maGlowTransparencyController.dispose();
+ maSoftEdgeRadiusController.dispose();
+}
+
+void EffectPropertyPanel::Initialize()
+{
+ mxGlowRadius->connect_value_changed(LINK(this, EffectPropertyPanel, ModifyGlowRadiusHdl));
+ mxLBGlowColor->SetSelectHdl(LINK(this, EffectPropertyPanel, ModifyGlowColorHdl));
+ mxGlowTransparency->connect_value_changed(
+ LINK(this, EffectPropertyPanel, ModifyGlowTransparencyHdl));
+ mxSoftEdgeRadius->connect_value_changed(
+ LINK(this, EffectPropertyPanel, ModifySoftEdgeRadiusHdl));
+}
+
+IMPL_LINK_NOARG(EffectPropertyPanel, ModifySoftEdgeRadiusHdl, weld::MetricSpinButton&, void)
+{
+ SdrMetricItem aItem(SDRATTR_SOFTEDGE_RADIUS, mxSoftEdgeRadius->get_value(FieldUnit::MM_100TH));
+ mpBindings->GetDispatcher()->ExecuteList(SID_ATTR_SOFTEDGE_RADIUS, SfxCallMode::RECORD,
+ { &aItem });
+}
+
+IMPL_LINK_NOARG(EffectPropertyPanel, ModifyGlowColorHdl, ColorListBox&, void)
+{
+ XColorItem aItem(SDRATTR_GLOW_COLOR, mxLBGlowColor->GetSelectEntryColor());
+ mpBindings->GetDispatcher()->ExecuteList(SID_ATTR_GLOW_COLOR, SfxCallMode::RECORD, { &aItem });
+}
+
+IMPL_LINK_NOARG(EffectPropertyPanel, ModifyGlowRadiusHdl, weld::MetricSpinButton&, void)
+{
+ SdrMetricItem aItem(SDRATTR_GLOW_RADIUS, mxGlowRadius->get_value(FieldUnit::MM_100TH));
+ mpBindings->GetDispatcher()->ExecuteList(SID_ATTR_GLOW_RADIUS, SfxCallMode::RECORD, { &aItem });
+}
+
+IMPL_LINK_NOARG(EffectPropertyPanel, ModifyGlowTransparencyHdl, weld::MetricSpinButton&, void)
+{
+ SdrPercentItem aItem(SDRATTR_GLOW_TRANSPARENCY,
+ mxGlowTransparency->get_value(FieldUnit::PERCENT));
+ mpBindings->GetDispatcher()->ExecuteList(SID_ATTR_GLOW_TRANSPARENCY, SfxCallMode::RECORD,
+ { &aItem });
+}
+
+void EffectPropertyPanel::UpdateControls()
+{
+ const bool bEnabled = mxGlowRadius->get_value(FieldUnit::MM_100TH) != 0;
+ mxLBGlowColor->set_sensitive(bEnabled);
+ mxGlowTransparency->set_sensitive(bEnabled);
+ mxFTColor->set_sensitive(bEnabled);
+ mxFTTransparency->set_sensitive(bEnabled);
+}
+
+void EffectPropertyPanel::NotifyItemUpdate(sal_uInt16 nSID, SfxItemState eState,
+ const SfxPoolItem* pState)
+{
+ switch (nSID)
+ {
+ case SID_ATTR_SOFTEDGE_RADIUS:
+ {
+ if (eState >= SfxItemState::DEFAULT)
+ {
+ const SdrMetricItem* pRadiusItem = dynamic_cast<const SdrMetricItem*>(pState);
+ if (pRadiusItem)
+ {
+ mxSoftEdgeRadius->set_value(pRadiusItem->GetValue(), FieldUnit::MM_100TH);
+ }
+ }
+ }
+ break;
+ case SID_ATTR_GLOW_COLOR:
+ {
+ if (eState >= SfxItemState::DEFAULT)
+ {
+ const XColorItem* pColorItem = dynamic_cast<const XColorItem*>(pState);
+ if (pColorItem)
+ {
+ mxLBGlowColor->SelectEntry(pColorItem->GetColorValue());
+ }
+ }
+ }
+ break;
+ case SID_ATTR_GLOW_RADIUS:
+ {
+ if (eState >= SfxItemState::DEFAULT)
+ {
+ const SdrMetricItem* pRadiusItem = dynamic_cast<const SdrMetricItem*>(pState);
+ if (pRadiusItem)
+ {
+ mxGlowRadius->set_value(pRadiusItem->GetValue(), FieldUnit::MM_100TH);
+ }
+ }
+ }
+ break;
+ case SID_ATTR_GLOW_TRANSPARENCY:
+ {
+ if (eState >= SfxItemState::DEFAULT)
+ {
+ if (auto pItem = dynamic_cast<const SdrPercentItem*>(pState))
+ {
+ mxGlowTransparency->set_value(pItem->GetValue(), FieldUnit::PERCENT);
+ }
+ }
+ }
+ break;
+ }
+ UpdateControls();
+}
+
+std::unique_ptr<PanelLayout> EffectPropertyPanel::Create(weld::Widget* pParent,
+ SfxBindings* pBindings)
+{
+ if (pParent == nullptr)
+ throw css::lang::IllegalArgumentException(
+ "no parent Window given to EffectPropertyPanel::Create", nullptr, 0);
+ if (pBindings == nullptr)
+ throw css::lang::IllegalArgumentException(
+ "no SfxBindings given to EffectPropertyPanel::Create", nullptr, 2);
+
+ return std::make_unique<EffectPropertyPanel>(pParent, pBindings);
+}
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/sidebar/effect/EffectPropertyPanel.hxx b/svx/source/sidebar/effect/EffectPropertyPanel.hxx
new file mode 100644
index 000000000..d657d83e6
--- /dev/null
+++ b/svx/source/sidebar/effect/EffectPropertyPanel.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_SVX_SOURCE_SIDEBAR_EFFECT_EFFECTPROPERTYPANEL_HXX
+#define INCLUDED_SVX_SOURCE_SIDEBAR_EFFECT_EFFECTPROPERTYPANEL_HXX
+
+#include <sfx2/sidebar/ControllerItem.hxx>
+#include <sfx2/sidebar/PanelLayout.hxx>
+
+class ColorListBox;
+
+namespace svx::sidebar
+{
+class EffectPropertyPanel : public PanelLayout,
+ public ::sfx2::sidebar::ControllerItem::ItemUpdateReceiverInterface
+{
+public:
+ EffectPropertyPanel(weld::Widget* pParent, SfxBindings* pBindings);
+ virtual ~EffectPropertyPanel() override;
+
+ static std::unique_ptr<PanelLayout> Create(weld::Widget* pParent, 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:
+ sfx2::sidebar::ControllerItem maGlowColorController;
+ sfx2::sidebar::ControllerItem maGlowRadiusController;
+ sfx2::sidebar::ControllerItem maGlowTransparencyController;
+ std::unique_ptr<weld::Label> mxFTTransparency;
+ sfx2::sidebar::ControllerItem maSoftEdgeRadiusController;
+
+ SfxBindings* mpBindings;
+
+ std::unique_ptr<weld::MetricSpinButton> mxGlowRadius;
+ std::unique_ptr<ColorListBox> mxLBGlowColor;
+ std::unique_ptr<weld::MetricSpinButton> mxGlowTransparency;
+ std::unique_ptr<weld::Label> mxFTRadiusSoftEdge;
+ std::unique_ptr<weld::Label> mxFTRadiusGlow;
+ std::unique_ptr<weld::Label> mxFTColor;
+ std::unique_ptr<weld::MetricSpinButton> mxSoftEdgeRadius;
+
+ void Initialize();
+ void UpdateControls();
+
+ DECL_LINK(ModifyGlowColorHdl, ColorListBox&, void);
+ DECL_LINK(ModifyGlowRadiusHdl, weld::MetricSpinButton&, void);
+ DECL_LINK(ModifyGlowTransparencyHdl, weld::MetricSpinButton&, void);
+ DECL_LINK(ModifySoftEdgeRadiusHdl, weld::MetricSpinButton&, void);
+};
+}
+
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/sidebar/fontwork/FontworkPropertyPanel.cxx b/svx/source/sidebar/fontwork/FontworkPropertyPanel.cxx
new file mode 100644
index 000000000..972141b7b
--- /dev/null
+++ b/svx/source/sidebar/fontwork/FontworkPropertyPanel.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 "FontworkPropertyPanel.hxx"
+
+#include <com/sun/star/lang/IllegalArgumentException.hpp>
+
+#include <comphelper/lok.hxx>
+
+using namespace css;
+using namespace css::uno;
+
+namespace svx
+{
+namespace sidebar
+{
+FontworkPropertyPanel::FontworkPropertyPanel(weld::Widget* pParent,
+ const css::uno::Reference<css::frame::XFrame>& rxFrame)
+ : PanelLayout(pParent, "FontworkPropertyPanel", "svx/ui/sidebarfontwork.ui")
+ , m_pToolbar(m_xBuilder->weld_toolbar("fontwork-toolbox"))
+ , m_xToolbar(new ToolbarUnoDispatcher(*m_pToolbar, *m_xBuilder, rxFrame))
+{
+ if (comphelper::LibreOfficeKit::isActive())
+ m_pToolbar->set_item_visible(".uno:ExtrusionToggle", false);
+}
+
+std::unique_ptr<PanelLayout>
+FontworkPropertyPanel::Create(weld::Widget* pParent,
+ const css::uno::Reference<css::frame::XFrame>& rxFrame)
+{
+ if (pParent == nullptr)
+ throw lang::IllegalArgumentException(
+ "no parent Window given to FontworkPropertyPanel::Create", nullptr, 0);
+ if (!rxFrame.is())
+ throw lang::IllegalArgumentException("no XFrame given to FontworkPropertyPanel::Create",
+ nullptr, 1);
+
+ return std::make_unique<FontworkPropertyPanel>(pParent, rxFrame);
+}
+}
+} // end of namespace svx::sidebar
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/sidebar/fontwork/FontworkPropertyPanel.hxx b/svx/source/sidebar/fontwork/FontworkPropertyPanel.hxx
new file mode 100644
index 000000000..cded09b95
--- /dev/null
+++ b/svx/source/sidebar/fontwork/FontworkPropertyPanel.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_SVX_SOURCE_SIDEBAR_AREA_FONTWORKPROPERTYPANEL_HXX
+#define INCLUDED_SVX_SOURCE_SIDEBAR_AREA_FONTWORKPROPERTYPANEL_HXX
+
+#include <sfx2/sidebar/PanelLayout.hxx>
+#include <sfx2/weldutils.hxx>
+
+namespace svx
+{
+namespace sidebar
+{
+class FontworkPropertyPanel : public PanelLayout
+{
+public:
+ static std::unique_ptr<PanelLayout>
+ Create(weld::Widget* pParent, const css::uno::Reference<css::frame::XFrame>& rxFrame);
+
+ // constructor/destructor
+ FontworkPropertyPanel(weld::Widget* pParent,
+ const css::uno::Reference<css::frame::XFrame>& rxFrame);
+
+private:
+ std::unique_ptr<weld::Toolbar> m_pToolbar;
+ std::unique_ptr<ToolbarUnoDispatcher> m_xToolbar;
+};
+}
+} // end of namespace svx::sidebar
+
+#endif // INCLUDED_SVX_SOURCE_SIDEBAR_AREA_FONTWORKPROPERTYPANEL_HXX
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/sidebar/graphic/GraphicPropertyPanel.cxx b/svx/source/sidebar/graphic/GraphicPropertyPanel.cxx
new file mode 100644
index 000000000..550364955
--- /dev/null
+++ b/svx/source/sidebar/graphic/GraphicPropertyPanel.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 "GraphicPropertyPanel.hxx"
+#include <svx/strings.hrc>
+#include <svx/svxids.hrc>
+#include <svx/dialmgr.hxx>
+#include <svl/intitem.hxx>
+#include <sfx2/bindings.hxx>
+#include <sfx2/dispatch.hxx>
+#include <com/sun/star/lang/IllegalArgumentException.hpp>
+
+using namespace css;
+using namespace css::uno;
+
+
+// namespace open
+
+namespace svx::sidebar {
+
+
+GraphicPropertyPanel::GraphicPropertyPanel(
+ weld::Widget* pParent,
+ SfxBindings* pBindings)
+: PanelLayout(pParent, "GraphicPropertyPanel", "svx/ui/sidebargraphic.ui"),
+ maBrightControl(SID_ATTR_GRAF_LUMINANCE, *pBindings, *this),
+ maContrastControl(SID_ATTR_GRAF_CONTRAST, *pBindings, *this),
+ maTransparenceControl(SID_ATTR_GRAF_TRANSPARENCE, *pBindings, *this),
+ maRedControl(SID_ATTR_GRAF_RED, *pBindings, *this),
+ maGreenControl(SID_ATTR_GRAF_GREEN, *pBindings, *this),
+ maBlueControl(SID_ATTR_GRAF_BLUE, *pBindings, *this),
+ maGammaControl(SID_ATTR_GRAF_GAMMA, *pBindings, *this),
+ maModeControl(SID_ATTR_GRAF_MODE, *pBindings, *this),
+ mpBindings(pBindings),
+ mxMtrBrightness(m_xBuilder->weld_metric_spin_button("setbrightness", FieldUnit::PERCENT)),
+ mxMtrContrast(m_xBuilder->weld_metric_spin_button("setcontrast", FieldUnit::PERCENT)),
+ mxLBColorMode(m_xBuilder->weld_combo_box("setcolormode")),
+ mxMtrTrans(m_xBuilder->weld_metric_spin_button("setgraphtransparency", FieldUnit::PERCENT))
+{
+ mxLBColorMode->set_size_request(mxLBColorMode->get_preferred_size().Width(), -1);
+ Initialize();
+}
+
+GraphicPropertyPanel::~GraphicPropertyPanel()
+{
+ mxMtrBrightness.reset();
+ mxMtrContrast.reset();
+ mxLBColorMode.reset();
+ mxMtrTrans.reset();
+
+ maBrightControl.dispose();
+ maContrastControl.dispose();
+ maTransparenceControl.dispose();
+ maRedControl.dispose();
+ maGreenControl.dispose();
+ maBlueControl.dispose();
+ maGammaControl.dispose();
+ maModeControl.dispose();
+}
+
+void GraphicPropertyPanel::Initialize()
+{
+ mxMtrBrightness->connect_value_changed( LINK( this, GraphicPropertyPanel, ModifyBrightnessHdl ) );
+ mxMtrContrast->connect_value_changed( LINK( this, GraphicPropertyPanel, ModifyContrastHdl ) );
+ mxMtrTrans->connect_value_changed( LINK( this, GraphicPropertyPanel, ModifyTransHdl ) );
+
+ mxLBColorMode->append_text(SvxResId(RID_SVXSTR_GRAFMODE_STANDARD));
+ mxLBColorMode->append_text(SvxResId(RID_SVXSTR_GRAFMODE_GREYS));
+ mxLBColorMode->append_text(SvxResId(RID_SVXSTR_GRAFMODE_MONO));
+ mxLBColorMode->append_text(SvxResId(RID_SVXSTR_GRAFMODE_WATERMARK));
+ mxLBColorMode->connect_changed( LINK( this, GraphicPropertyPanel, ClickColorModeHdl ));
+}
+
+IMPL_LINK_NOARG( GraphicPropertyPanel, ModifyBrightnessHdl, weld::MetricSpinButton&, void )
+{
+ const sal_Int16 nBright = mxMtrBrightness->get_value(FieldUnit::PERCENT);
+ const SfxInt16Item aBrightItem( SID_ATTR_GRAF_LUMINANCE, nBright );
+ GetBindings()->GetDispatcher()->ExecuteList(SID_ATTR_GRAF_LUMINANCE,
+ SfxCallMode::RECORD, { &aBrightItem });
+}
+
+
+IMPL_LINK_NOARG( GraphicPropertyPanel, ModifyContrastHdl, weld::MetricSpinButton&, void )
+{
+ const sal_Int16 nContrast = mxMtrContrast->get_value(FieldUnit::PERCENT);
+ const SfxInt16Item aContrastItem( SID_ATTR_GRAF_CONTRAST, nContrast );
+ GetBindings()->GetDispatcher()->ExecuteList(SID_ATTR_GRAF_CONTRAST,
+ SfxCallMode::RECORD, { &aContrastItem });
+}
+
+
+IMPL_LINK_NOARG( GraphicPropertyPanel, ModifyTransHdl, weld::MetricSpinButton&, void )
+{
+ const sal_Int16 nTrans = mxMtrTrans->get_value(FieldUnit::PERCENT);
+ const SfxUInt16Item aTransItem( SID_ATTR_GRAF_TRANSPARENCE, nTrans );
+ GetBindings()->GetDispatcher()->ExecuteList(SID_ATTR_GRAF_TRANSPARENCE,
+ SfxCallMode::RECORD, { &aTransItem });
+}
+
+
+IMPL_LINK_NOARG( GraphicPropertyPanel, ClickColorModeHdl, weld::ComboBox&, void )
+{
+ const sal_Int16 nTrans = mxLBColorMode->get_active();
+ const SfxUInt16Item aTransItem( SID_ATTR_GRAF_MODE, nTrans );
+ GetBindings()->GetDispatcher()->ExecuteList(SID_ATTR_GRAF_MODE,
+ SfxCallMode::RECORD, { &aTransItem });
+}
+
+std::unique_ptr<PanelLayout> GraphicPropertyPanel::Create (
+ weld::Widget* pParent,
+ SfxBindings* pBindings)
+{
+ if (pParent == nullptr)
+ throw lang::IllegalArgumentException("no parent Window given to GraphicPropertyPanel::Create", nullptr, 0);
+ if (pBindings == nullptr)
+ throw lang::IllegalArgumentException("no SfxBindings given to GraphicPropertyPanel::Create", nullptr, 2);
+
+ return std::make_unique<GraphicPropertyPanel>(pParent, pBindings);
+}
+
+void GraphicPropertyPanel::NotifyItemUpdate(
+ sal_uInt16 nSID,
+ SfxItemState eState,
+ const SfxPoolItem* pState)
+{
+ switch( nSID )
+ {
+ case SID_ATTR_GRAF_LUMINANCE:
+ {
+ if(eState >= SfxItemState::DEFAULT)
+ {
+ mxMtrBrightness->set_sensitive(true);
+ const SfxInt16Item* pItem = dynamic_cast< const SfxInt16Item* >(pState);
+
+ if(pItem)
+ {
+ const sal_Int64 nBright = pItem->GetValue();
+ mxMtrBrightness->set_value(nBright, FieldUnit::PERCENT);
+ }
+ }
+ else if(SfxItemState::DISABLED == eState)
+ {
+ mxMtrBrightness->set_sensitive(false);
+ }
+ else
+ {
+ mxMtrBrightness->set_sensitive(true);
+ mxMtrBrightness->set_text(OUString());
+ }
+ break;
+ }
+ case SID_ATTR_GRAF_CONTRAST:
+ {
+ if(eState >= SfxItemState::DEFAULT)
+ {
+ mxMtrContrast->set_sensitive(true);
+ const SfxInt16Item* pItem = dynamic_cast< const SfxInt16Item* >(pState);
+
+ if(pItem)
+ {
+ const sal_Int64 nContrast = pItem->GetValue();
+ mxMtrContrast->set_value(nContrast, FieldUnit::PERCENT);
+ }
+ }
+ else if(SfxItemState::DISABLED == eState)
+ {
+ mxMtrContrast->set_sensitive(false);
+ }
+ else
+ {
+ mxMtrContrast->set_sensitive(true);
+ mxMtrContrast->set_text(OUString());
+ }
+ break;
+ }
+ case SID_ATTR_GRAF_TRANSPARENCE:
+ {
+ if(eState >= SfxItemState::DEFAULT)
+ {
+ mxMtrTrans->set_sensitive(true);
+ const SfxUInt16Item* pItem = dynamic_cast< const SfxUInt16Item* >(pState);
+
+ if(pItem)
+ {
+ const sal_Int64 nTrans = pItem->GetValue();
+ mxMtrTrans->set_value(nTrans, FieldUnit::PERCENT);
+ }
+ }
+ else if(SfxItemState::DISABLED == eState)
+ {
+ mxMtrTrans->set_sensitive(false);
+ }
+ else
+ {
+ mxMtrTrans->set_sensitive(true);
+ mxMtrTrans->set_text(OUString());
+ }
+ break;
+ }
+ case SID_ATTR_GRAF_MODE:
+ {
+ if(eState >= SfxItemState::DEFAULT)
+ {
+ mxLBColorMode->set_sensitive(true);
+
+ if(pState)
+ {
+ const sal_uInt16 nTrans = static_cast< const SfxUInt16Item* >(pState)->GetValue();
+ mxLBColorMode->set_active(nTrans);
+ }
+ }
+ else if(SfxItemState::DISABLED == eState)
+ {
+ mxLBColorMode->set_sensitive(false);
+ }
+ else
+ {
+ mxLBColorMode->set_sensitive(true);
+ mxLBColorMode->set_active(-1);
+ }
+ break;
+ }
+ }
+}
+
+// namespace close
+
+} // end of namespace svx::sidebar
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/sidebar/graphic/GraphicPropertyPanel.hxx b/svx/source/sidebar/graphic/GraphicPropertyPanel.hxx
new file mode 100644
index 000000000..e7fd4914b
--- /dev/null
+++ b/svx/source/sidebar/graphic/GraphicPropertyPanel.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_SVX_SOURCE_SIDEBAR_GRAPHIC_GRAPHICPROPERTYPANEL_HXX
+#define INCLUDED_SVX_SOURCE_SIDEBAR_GRAPHIC_GRAPHICPROPERTYPANEL_HXX
+
+#include <sfx2/sidebar/ControllerItem.hxx>
+#include <sfx2/sidebar/PanelLayout.hxx>
+#include <vcl/weld.hxx>
+
+namespace svx::sidebar {
+
+class GraphicPropertyPanel
+: public PanelLayout,
+ public ::sfx2::sidebar::ControllerItem::ItemUpdateReceiverInterface
+{
+public:
+ virtual ~GraphicPropertyPanel() override;
+
+ static std::unique_ptr<PanelLayout> Create(
+ weld::Widget* pParent,
+ 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() { return mpBindings;}
+
+ // constructor/destructor
+ GraphicPropertyPanel(
+ weld::Widget* pParent,
+ SfxBindings* pBindings);
+
+private:
+ ::sfx2::sidebar::ControllerItem maBrightControl;
+ ::sfx2::sidebar::ControllerItem maContrastControl;
+ ::sfx2::sidebar::ControllerItem maTransparenceControl;
+ ::sfx2::sidebar::ControllerItem maRedControl;
+ ::sfx2::sidebar::ControllerItem maGreenControl;
+ ::sfx2::sidebar::ControllerItem maBlueControl;
+ ::sfx2::sidebar::ControllerItem maGammaControl;
+ ::sfx2::sidebar::ControllerItem maModeControl;
+
+ SfxBindings* mpBindings;
+
+ //ui controls
+ std::unique_ptr<weld::MetricSpinButton> mxMtrBrightness;
+ std::unique_ptr<weld::MetricSpinButton> mxMtrContrast;
+ std::unique_ptr<weld::ComboBox> mxLBColorMode;
+ std::unique_ptr<weld::MetricSpinButton> mxMtrTrans;
+ std::unique_ptr<weld::MetricSpinButton> mxMtrRed;
+ std::unique_ptr<weld::MetricSpinButton> mxMtrGreen;
+ std::unique_ptr<weld::MetricSpinButton> mxMtrBlue;
+ std::unique_ptr<weld::SpinButton> mxMtrGamma;
+
+ DECL_LINK( ModifyBrightnessHdl, weld::MetricSpinButton&, void );
+ DECL_LINK( ModifyContrastHdl, weld::MetricSpinButton&, void );
+ DECL_LINK( ModifyTransHdl, weld::MetricSpinButton&, void );
+ DECL_LINK( ClickColorModeHdl, weld::ComboBox&, void );
+
+ void Initialize();
+};
+
+} // end of namespace svx::sidebar
+
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/sidebar/inspector/InspectorTextPanel.cxx b/svx/source/sidebar/inspector/InspectorTextPanel.cxx
new file mode 100644
index 000000000..0b5da3d2d
--- /dev/null
+++ b/svx/source/sidebar/inspector/InspectorTextPanel.cxx
@@ -0,0 +1,173 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <o3tl/safeint.hxx>
+#include <sal/config.h>
+
+#include <svx/dialmgr.hxx>
+
+#include <svx/sidebar/InspectorTextPanel.hxx>
+
+#include <svl/cjkoptions.hxx>
+#include <svl/ctloptions.hxx>
+#include <com/sun/star/awt/FontSlant.hpp>
+#include <com/sun/star/lang/IllegalArgumentException.hpp>
+#include <inspectorvalues.hrc>
+
+using namespace css;
+
+const int MinimumPanelWidth = 250;
+
+namespace svx::sidebar
+{
+std::unique_ptr<PanelLayout> InspectorTextPanel::Create(weld::Widget* pParent)
+{
+ if (pParent == nullptr)
+ throw lang::IllegalArgumentException("no parent Window given to InspectorTextPanel::Create",
+ nullptr, 0);
+ return std::make_unique<InspectorTextPanel>(pParent);
+}
+
+InspectorTextPanel::InspectorTextPanel(weld::Widget* pParent)
+ : PanelLayout(pParent, "InspectorTextPanel", "svx/ui/inspectortextpanel.ui")
+ , mpListBoxStyles(m_xBuilder->weld_tree_view("listbox_fonts"))
+{
+ mpListBoxStyles->set_size_request(MinimumPanelWidth, -1);
+ float fWidth = mpListBoxStyles->get_approximate_digit_width();
+ std::vector<int> aWidths{ o3tl::narrowing<int>(fWidth * 29) };
+ // 2nd column will fill remaining space
+ mpListBoxStyles->set_column_fixed_widths(aWidths);
+}
+
+static bool GetPropertyValues(std::u16string_view rPropName, const uno::Any& rAny,
+ OUString& rString)
+{
+ // Hide Asian and Complex properties
+ if (!SvtCJKOptions::IsCJKFontEnabled() && rPropName.find(u"Asian") != std::u16string_view::npos)
+ return false;
+ if (!SvtCTLOptions().IsCTLFontEnabled()
+ && rPropName.find(u"Complex") != std::u16string_view::npos)
+ return false;
+
+ if (bool bValue; rAny >>= bValue)
+ {
+ rString = SvxResId(bValue ? RID_TRUE : RID_FALSE); // tdf#139136
+ }
+ else if (OUString aValue; (rAny >>= aValue) && !(aValue.isEmpty()))
+ {
+ rString = aValue;
+ }
+ else if (awt::FontSlant eValue; rAny >>= eValue)
+ {
+ rString = SvxResId(eValue == awt::FontSlant_ITALIC ? RID_ITALIC : RID_NORMAL);
+ }
+ else if (tools::Long nValueLong; rAny >>= nValueLong)
+ {
+ if (rPropName.find(u"Color") != std::u16string_view::npos)
+ rString = "0x" + OUString::number(nValueLong, 16);
+ else
+ rString = OUString::number(nValueLong);
+ }
+ else if (double fValue; rAny >>= fValue)
+ {
+ if (rPropName.find(u"Weight") != std::u16string_view::npos)
+ rString = SvxResId(fValue > 100 ? RID_BOLD : RID_NORMAL);
+ else
+ rString = OUString::number((round(fValue * 100)) / 100.00);
+ }
+ else if (short nValueShort; rAny >>= nValueShort)
+ {
+ rString = OUString::number(nValueShort);
+ }
+ else
+ return false;
+
+ return true;
+}
+
+static void FillBox_Impl(weld::TreeView& rListBoxStyles, const TreeNode& rCurrent,
+ const weld::TreeIter* pParent)
+{
+ std::unique_ptr<weld::TreeIter> pResult = rListBoxStyles.make_iterator();
+ const OUString& rName = rCurrent.sNodeName;
+ OUString sPairValue;
+
+ if (!(rCurrent.NodeType != TreeNode::SimpleProperty
+ || GetPropertyValues(rName, rCurrent.aValue, sPairValue)))
+ return;
+
+ rListBoxStyles.insert(pParent, -1, &rName, nullptr, nullptr, nullptr, false, pResult.get());
+ rListBoxStyles.set_sensitive(*pResult, !rCurrent.isGrey, 0);
+ rListBoxStyles.set_text_emphasis(*pResult, rCurrent.NodeType == TreeNode::Category, 0);
+
+ if (rCurrent.NodeType == TreeNode::SimpleProperty)
+ {
+ rListBoxStyles.set_text(*pResult, sPairValue, 1);
+ rListBoxStyles.set_sensitive(*pResult, !rCurrent.isGrey, 1);
+ rListBoxStyles.set_text_emphasis(*pResult, false, 1);
+ }
+ else
+ {
+ // Necessary, without this the selection line will be truncated.
+ rListBoxStyles.set_text(*pResult, "", 1);
+ }
+
+ for (const TreeNode& rChildNode : rCurrent.children)
+ FillBox_Impl(rListBoxStyles, rChildNode, pResult.get());
+}
+
+void InspectorTextPanel::updateEntries(const std::vector<TreeNode>& rStore, const sal_Int32 nParIdx)
+{
+ mpListBoxStyles->freeze();
+ mpListBoxStyles->clear();
+ for (const TreeNode& rChildNode : rStore)
+ {
+ FillBox_Impl(*mpListBoxStyles, rChildNode, nullptr);
+ }
+
+ mpListBoxStyles->thaw();
+
+ weld::TreeView* pTreeDiagram = mpListBoxStyles.get();
+ pTreeDiagram->all_foreach([pTreeDiagram](weld::TreeIter& rEntry) {
+ pTreeDiagram->expand_row(rEntry);
+ return false;
+ });
+
+ // Collapse "Default Paragraph Style"
+
+ std::unique_ptr<weld::TreeIter> pEntry = mpListBoxStyles->make_iterator();
+ if (!mpListBoxStyles->get_iter_first(*pEntry))
+ return;
+ // skip the optional metadata items before "Default Paragraph Style"
+ for (sal_Int32 i = 0; i < nParIdx; ++i)
+ {
+ if (!mpListBoxStyles->iter_next_sibling(*pEntry))
+ return;
+ }
+ if (!mpListBoxStyles->iter_next(*pEntry))
+ return;
+
+ mpListBoxStyles->collapse_row(*pEntry);
+}
+
+InspectorTextPanel::~InspectorTextPanel() {}
+
+} // end of namespace svx::sidebar
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/sidebar/line/LinePropertyPanel.cxx b/svx/source/sidebar/line/LinePropertyPanel.cxx
new file mode 100644
index 000000000..02dd597bd
--- /dev/null
+++ b/svx/source/sidebar/line/LinePropertyPanel.cxx
@@ -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 .
+ */
+
+#include "LinePropertyPanel.hxx"
+#include <svx/svxids.hrc>
+#include <sfx2/bindings.hxx>
+#include <sfx2/dispatch.hxx>
+#include <svx/xlnwtit.hxx>
+#include <svx/xlntrit.hxx>
+#include <svx/xlncapit.hxx>
+#include <svx/xlinjoit.hxx>
+#include <com/sun/star/lang/IllegalArgumentException.hpp>
+
+using namespace css;
+using namespace css::uno;
+
+namespace svx::sidebar {
+
+LinePropertyPanel::LinePropertyPanel(
+ weld::Widget* pParent,
+ const uno::Reference<css::frame::XFrame>& rxFrame,
+ SfxBindings* pBindings)
+: LinePropertyPanelBase(pParent, rxFrame),
+ maStyleControl(SID_ATTR_LINE_STYLE, *pBindings, *this),
+ maDashControl (SID_ATTR_LINE_DASH, *pBindings, *this),
+ maWidthControl(SID_ATTR_LINE_WIDTH, *pBindings, *this),
+ maTransControl(SID_ATTR_LINE_TRANSPARENCE, *pBindings, *this),
+ maEdgeStyle(SID_ATTR_LINE_JOINT, *pBindings, *this),
+ maCapStyle(SID_ATTR_LINE_CAP, *pBindings, *this),
+ mpBindings(pBindings)
+{
+ setMapUnit(maWidthControl.GetCoreMetric());
+}
+
+LinePropertyPanel::~LinePropertyPanel()
+{
+ maStyleControl.dispose();
+ maDashControl.dispose();
+ maWidthControl.dispose();
+ maTransControl.dispose();
+ maEdgeStyle.dispose();
+ maCapStyle.dispose();
+}
+
+std::unique_ptr<PanelLayout> LinePropertyPanel::Create (
+ weld::Widget* pParent,
+ const uno::Reference<css::frame::XFrame>& rxFrame,
+ SfxBindings* pBindings)
+{
+ if (pParent == nullptr)
+ throw lang::IllegalArgumentException("no parent Window given to LinePropertyPanel::Create", nullptr, 0);
+ if ( ! rxFrame.is())
+ throw lang::IllegalArgumentException("no XFrame given to LinePropertyPanel::Create", nullptr, 1);
+ if (pBindings == nullptr)
+ throw lang::IllegalArgumentException("no SfxBindings given to LinePropertyPanel::Create", nullptr, 2);
+
+ return std::make_unique<LinePropertyPanel>(pParent, rxFrame, pBindings);
+}
+
+void LinePropertyPanel::NotifyItemUpdate(
+ sal_uInt16 nSID,
+ SfxItemState eState,
+ const SfxPoolItem* pState)
+{
+ const bool bDisabled(SfxItemState::DISABLED == eState);
+ const bool bSetOrDefault = eState >= SfxItemState::DEFAULT;
+
+ switch(nSID)
+ {
+ case SID_ATTR_LINE_TRANSPARENCE:
+ {
+ updateLineTransparence(bDisabled, bSetOrDefault, pState);
+ break;
+ }
+ case SID_ATTR_LINE_WIDTH:
+ {
+ updateLineWidth(bDisabled, bSetOrDefault, pState);
+ break;
+ }
+ case SID_ATTR_LINE_JOINT:
+ {
+ updateLineJoint(bDisabled, bSetOrDefault, pState);
+ break;
+ }
+ case SID_ATTR_LINE_CAP:
+ {
+ updateLineCap(bDisabled, bSetOrDefault, pState);
+ break;
+ }
+ }
+ ActivateControls();
+}
+
+void LinePropertyPanel::HandleContextChange(
+ const vcl::EnumContext& rContext)
+{
+ if(maContext == rContext)
+ {
+ // Nothing to do
+ return;
+ }
+
+ maContext = rContext;
+ bool bShowArrows = false;
+
+ switch(maContext.GetCombinedContext_DI())
+ {
+ case CombinedEnumContext(Application::Calc, Context::DrawLine):
+ case CombinedEnumContext(Application::DrawImpress, Context::DrawLine):
+ case CombinedEnumContext(Application::DrawImpress, Context::Draw):
+ case CombinedEnumContext(Application::WriterVariants, Context::Draw):
+ // TODO : Implement DrawLine context in Writer
+ bShowArrows = true;
+ break;
+ }
+
+ if (!bShowArrows)
+ disableArrowHead();
+ else
+ enableArrowHead();
+}
+
+void LinePropertyPanel::setLineJoint(const XLineJointItem* pItem)
+{
+ GetBindings()->GetDispatcher()->ExecuteList(SID_ATTR_LINE_JOINT,
+ SfxCallMode::RECORD, { pItem });
+}
+
+void LinePropertyPanel::setLineCap(const XLineCapItem* pItem)
+{
+ GetBindings()->GetDispatcher()->ExecuteList(SID_ATTR_LINE_CAP,
+ SfxCallMode::RECORD, { pItem });
+}
+
+void LinePropertyPanel::setLineTransparency(const XLineTransparenceItem& rItem)
+{
+ GetBindings()->GetDispatcher()->ExecuteList(SID_ATTR_LINE_STYLE,
+ SfxCallMode::RECORD, { &rItem });
+}
+
+void LinePropertyPanel::setLineWidth(const XLineWidthItem& rItem)
+{
+ GetBindings()->GetDispatcher()->ExecuteList(SID_ATTR_LINE_WIDTH,
+ SfxCallMode::RECORD, { &rItem });
+}
+
+} // end of namespace svx::sidebar
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/sidebar/line/LinePropertyPanel.hxx b/svx/source/sidebar/line/LinePropertyPanel.hxx
new file mode 100644
index 000000000..7223cd9f1
--- /dev/null
+++ b/svx/source/sidebar/line/LinePropertyPanel.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_SVX_SOURCE_SIDEBAR_LINE_LINEPROPERTYPANEL_HXX
+#define INCLUDED_SVX_SOURCE_SIDEBAR_LINE_LINEPROPERTYPANEL_HXX
+
+#include <sfx2/sidebar/ControllerItem.hxx>
+#include <sfx2/sidebar/IContextChangeReceiver.hxx>
+#include <svx/sidebar/LinePropertyPanelBase.hxx>
+#include <vcl/EnumContext.hxx>
+
+class XLineStyleItem;
+class XLineDashItem;
+class XLineStartItem;
+class XLineEndItem;
+class XLineEndList;
+class XDashList;
+class ListBox;
+class ToolBox;
+
+namespace svx::sidebar
+{
+
+class LinePropertyPanel : public LinePropertyPanelBase,
+ public sfx2::sidebar::IContextChangeReceiver,
+ public sfx2::sidebar::ControllerItem::ItemUpdateReceiverInterface
+{
+public:
+ virtual ~LinePropertyPanel() override;
+
+ static std::unique_ptr<PanelLayout> Create(
+ weld::Widget* pParent,
+ const css::uno::Reference<css::frame::XFrame>& rxFrame,
+ SfxBindings* pBindings);
+
+ virtual void HandleContextChange(
+ const vcl::EnumContext& rContext) override;
+
+ 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() { return mpBindings;}
+
+ // constructor/destructor
+ LinePropertyPanel(
+ weld::Widget* pParent,
+ const css::uno::Reference<css::frame::XFrame>& rxFrame,
+ SfxBindings* pBindings);
+
+ virtual void setLineWidth(const XLineWidthItem& rItem) override;
+
+protected:
+
+ virtual void setLineTransparency(const XLineTransparenceItem& rItem) override;
+ virtual void setLineJoint(const XLineJointItem* pItem) override;
+ virtual void setLineCap(const XLineCapItem* pItem) override;
+
+private:
+ //ControllerItem
+ sfx2::sidebar::ControllerItem maStyleControl;
+ sfx2::sidebar::ControllerItem maDashControl;
+ sfx2::sidebar::ControllerItem maWidthControl;
+ sfx2::sidebar::ControllerItem maTransControl;
+ sfx2::sidebar::ControllerItem maEdgeStyle;
+ sfx2::sidebar::ControllerItem maCapStyle;
+
+ SfxBindings* mpBindings;
+ vcl::EnumContext maContext;
+};
+
+} // end of namespace svx::sidebar
+
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/sidebar/line/LinePropertyPanelBase.cxx b/svx/source/sidebar/line/LinePropertyPanelBase.cxx
new file mode 100644
index 000000000..454e16a33
--- /dev/null
+++ b/svx/source/sidebar/line/LinePropertyPanelBase.cxx
@@ -0,0 +1,470 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * 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/sidebar/LinePropertyPanelBase.hxx>
+#include <sfx2/weldutils.hxx>
+#include <svx/linectrl.hxx>
+#include <svx/xlnwtit.hxx>
+#include <svx/xlntrit.hxx>
+#include <svx/xlncapit.hxx>
+#include <svx/xlinjoit.hxx>
+#include <bitmaps.hlst>
+
+using namespace css;
+using namespace css::uno;
+
+constexpr OStringLiteral SELECTWIDTH = "SelectWidth";
+
+namespace svx::sidebar {
+
+// trigger disabling the arrows if the none line style is selected
+class LineStyleNoneChange
+{
+private:
+ LinePropertyPanelBase& m_rPanel;
+
+public:
+ LineStyleNoneChange(LinePropertyPanelBase& rPanel)
+ : m_rPanel(rPanel)
+ {
+ }
+
+ void operator()(bool bLineStyleNone)
+ {
+ m_rPanel.SetNoneLineStyle(bLineStyleNone);
+ }
+};
+
+namespace
+{
+ SvxLineStyleToolBoxControl* getLineStyleToolBoxControl(const ToolbarUnoDispatcher& rToolBoxColor)
+ {
+ css::uno::Reference<css::frame::XToolbarController> xController = rToolBoxColor.GetControllerForCommand(".uno:XLineStyle");
+ SvxLineStyleToolBoxControl* pToolBoxLineStyleControl = dynamic_cast<SvxLineStyleToolBoxControl*>(xController.get());
+ return pToolBoxLineStyleControl;
+ }
+}
+
+
+LinePropertyPanelBase::LinePropertyPanelBase(
+ weld::Widget* pParent,
+ const uno::Reference<css::frame::XFrame>& rxFrame)
+: PanelLayout(pParent, "LinePropertyPanel", "svx/ui/sidebarline.ui"),
+ mxTBColor(m_xBuilder->weld_toolbar("color")),
+ mxColorDispatch(new ToolbarUnoDispatcher(*mxTBColor, *m_xBuilder, rxFrame)),
+ mxLineStyleTB(m_xBuilder->weld_toolbar("linestyle")),
+ mxLineStyleDispatch(new ToolbarUnoDispatcher(*mxLineStyleTB, *m_xBuilder, rxFrame)),
+ mnWidthCoreValue(0),
+ mxFTWidth(m_xBuilder->weld_label("widthlabel")),
+ mxTBWidth(m_xBuilder->weld_toolbar("width")),
+ mxFTTransparency(m_xBuilder->weld_label("translabel")),
+ mxMFTransparent(m_xBuilder->weld_metric_spin_button("linetransparency", FieldUnit::PERCENT)),
+ mxFTEdgeStyle(m_xBuilder->weld_label("cornerlabel")),
+ mxLBEdgeStyle(m_xBuilder->weld_combo_box("edgestyle")),
+ mxFTCapStyle(m_xBuilder->weld_label("caplabel")),
+ mxLBCapStyle(m_xBuilder->weld_combo_box("linecapstyle")),
+ mxGridLineProps(m_xBuilder->weld_widget("lineproperties")),
+ mxBoxArrowProps(m_xBuilder->weld_widget("arrowproperties")),
+ mxLineWidthPopup(new LineWidthPopup(mxTBWidth.get(), *this)),
+ mxLineStyleNoneChange(new LineStyleNoneChange(*this)),
+ mnTrans(0),
+ meMapUnit(MapUnit::MapMM),
+ maIMGNone(BMP_NONE_ICON),
+ mbWidthValuable(true),
+ mbArrowSupported(true),
+ mbNoneLineStyle(false)
+{
+ Initialize();
+}
+
+LinePropertyPanelBase::~LinePropertyPanelBase()
+{
+ mxLineWidthPopup.reset();
+ mxFTWidth.reset();
+ mxTBWidth.reset();
+ mxColorDispatch.reset();
+ mxTBColor.reset();
+ mxFTTransparency.reset();
+ mxMFTransparent.reset();
+ mxLineStyleDispatch.reset();
+ mxLineStyleTB.reset();
+ mxFTEdgeStyle.reset();
+ mxLBEdgeStyle.reset();
+ mxFTCapStyle.reset();
+ mxLBCapStyle.reset();
+ mxGridLineProps.reset();
+ mxBoxArrowProps.reset();
+}
+
+void LinePropertyPanelBase::Initialize()
+{
+ mxTBWidth->set_item_popover(SELECTWIDTH, mxLineWidthPopup->getTopLevel());
+
+ maIMGWidthIcon[0] = BMP_WIDTH1_ICON;
+ maIMGWidthIcon[1] = BMP_WIDTH2_ICON;
+ maIMGWidthIcon[2] = BMP_WIDTH3_ICON;
+ maIMGWidthIcon[3] = BMP_WIDTH4_ICON;
+ maIMGWidthIcon[4] = BMP_WIDTH5_ICON;
+ maIMGWidthIcon[5] = BMP_WIDTH6_ICON;
+ maIMGWidthIcon[6] = BMP_WIDTH7_ICON;
+ maIMGWidthIcon[7] = BMP_WIDTH8_ICON;
+
+ mxTBWidth->set_item_icon_name(SELECTWIDTH, maIMGWidthIcon[0]);
+ mxTBWidth->connect_clicked(LINK(this, LinePropertyPanelBase, ToolboxWidthSelectHdl));
+
+ mxMFTransparent->connect_value_changed(LINK(this, LinePropertyPanelBase, ChangeTransparentHdl));
+
+ mxLBEdgeStyle->connect_changed( LINK( this, LinePropertyPanelBase, ChangeEdgeStyleHdl ) );
+
+ mxLBCapStyle->connect_changed( LINK( this, LinePropertyPanelBase, ChangeCapStyleHdl ) );
+
+ SvxLineStyleToolBoxControl* pLineStyleControl = getLineStyleToolBoxControl(*mxLineStyleDispatch);
+ pLineStyleControl->setLineStyleIsNoneFunction(*mxLineStyleNoneChange);
+}
+
+void LinePropertyPanelBase::updateLineTransparence(bool bDisabled, bool bSetOrDefault,
+ const SfxPoolItem* pState)
+{
+ if(bDisabled)
+ {
+ mxFTTransparency->set_sensitive(false);
+ mxMFTransparent->set_sensitive(false);
+ }
+ else
+ {
+ mxFTTransparency->set_sensitive(true);
+ mxMFTransparent->set_sensitive(true);
+ }
+
+ if(bSetOrDefault)
+ {
+ if (const XLineTransparenceItem* pItem = dynamic_cast<const XLineTransparenceItem*>(pState))
+ {
+ mnTrans = pItem->GetValue();
+ mxMFTransparent->set_value(mnTrans, FieldUnit::PERCENT);
+ return;
+ }
+ }
+
+ mxMFTransparent->set_value(0, FieldUnit::PERCENT);//add
+ mxMFTransparent->set_text(OUString());
+}
+
+void LinePropertyPanelBase::updateLineWidth(bool bDisabled, bool bSetOrDefault,
+ const SfxPoolItem* pState)
+{
+ if(bDisabled)
+ {
+ mxTBWidth->set_sensitive(false);
+ mxFTWidth->set_sensitive(false);
+ }
+ else
+ {
+ mxTBWidth->set_sensitive(true);
+ mxFTWidth->set_sensitive(true);
+ }
+
+ if(bSetOrDefault)
+ {
+ if (const XLineWidthItem* pItem = dynamic_cast<const XLineWidthItem*>(pState))
+ {
+ mnWidthCoreValue = pItem->GetValue();
+ mbWidthValuable = true;
+ SetWidthIcon();
+ return;
+ }
+ }
+
+ mbWidthValuable = false;
+ SetWidthIcon();
+}
+
+void LinePropertyPanelBase::updateLineJoint(bool bDisabled, bool bSetOrDefault,
+ const SfxPoolItem* pState)
+{
+ if(bDisabled)
+ {
+ mxLBEdgeStyle->set_sensitive(false);
+ mxFTEdgeStyle->set_sensitive(false);
+ }
+ else
+ {
+ mxLBEdgeStyle->set_sensitive(true);
+ mxFTEdgeStyle->set_sensitive(true);
+ }
+
+ if(bSetOrDefault)
+ {
+ if (const XLineJointItem* pItem = dynamic_cast<const XLineJointItem*>(pState))
+ {
+ sal_Int32 nEntryPos(0);
+
+ switch(pItem->GetValue())
+ {
+ case drawing::LineJoint_ROUND:
+ {
+ nEntryPos = 1;
+ break;
+ }
+ case drawing::LineJoint_NONE:
+ {
+ nEntryPos = 2;
+ break;
+ }
+ case drawing::LineJoint_MIDDLE:
+ case drawing::LineJoint_MITER:
+ {
+ nEntryPos = 3;
+ break;
+ }
+ case drawing::LineJoint_BEVEL:
+ {
+ nEntryPos = 4;
+ break;
+ }
+
+ default:
+ break;
+ }
+
+ if(nEntryPos)
+ {
+ mxLBEdgeStyle->set_active(nEntryPos - 1);
+ return;
+ }
+ }
+ }
+
+ mxLBEdgeStyle->set_active(-1);
+}
+
+void LinePropertyPanelBase::updateLineCap(bool bDisabled, bool bSetOrDefault,
+ const SfxPoolItem* pState)
+{
+ if(bDisabled)
+ {
+ mxLBCapStyle->set_sensitive(false);
+ mxFTCapStyle->set_sensitive(false);
+ }
+ else
+ {
+ mxLBCapStyle->set_sensitive(true);
+ mxLBCapStyle->set_sensitive(true);
+ }
+
+ if(bSetOrDefault)
+ {
+ if (const XLineCapItem* pItem = dynamic_cast<const XLineCapItem*>(pState))
+ {
+ sal_Int32 nEntryPos(0);
+
+ switch(pItem->GetValue())
+ {
+ case drawing::LineCap_BUTT:
+ {
+ nEntryPos = 1;
+ break;
+ }
+ case drawing::LineCap_ROUND:
+ {
+ nEntryPos = 2;
+ break;
+ }
+ case drawing::LineCap_SQUARE:
+ {
+ nEntryPos = 3;
+ break;
+ }
+
+ default:
+ break;
+ }
+
+ if(nEntryPos)
+ {
+ mxLBCapStyle->set_active(nEntryPos - 1);
+ return;
+ }
+ }
+ }
+
+ mxLBCapStyle->set_active(-1);
+}
+
+IMPL_LINK_NOARG(LinePropertyPanelBase, ChangeEdgeStyleHdl, weld::ComboBox&, void)
+{
+ const sal_Int32 nPos(mxLBEdgeStyle->get_active());
+
+ if (nPos == -1 || !mxLBEdgeStyle->get_value_changed_from_saved())
+ return;
+
+ std::unique_ptr<XLineJointItem> pItem;
+
+ switch(nPos)
+ {
+ case 0: // rounded
+ {
+ pItem.reset(new XLineJointItem(drawing::LineJoint_ROUND));
+ break;
+ }
+ case 1: // none
+ {
+ pItem.reset(new XLineJointItem(drawing::LineJoint_NONE));
+ break;
+ }
+ case 2: // mitered
+ {
+ pItem.reset(new XLineJointItem(drawing::LineJoint_MITER));
+ break;
+ }
+ case 3: // beveled
+ {
+ pItem.reset(new XLineJointItem(drawing::LineJoint_BEVEL));
+ break;
+ }
+ }
+
+ setLineJoint(pItem.get());
+}
+
+IMPL_LINK_NOARG(LinePropertyPanelBase, ChangeCapStyleHdl, weld::ComboBox&, void)
+{
+ const sal_Int32 nPos(mxLBCapStyle->get_active());
+
+ if (!(nPos != -1 && mxLBCapStyle->get_value_changed_from_saved()))
+ return;
+
+ std::unique_ptr<XLineCapItem> pItem;
+
+ switch(nPos)
+ {
+ case 0: // flat
+ {
+ pItem.reset(new XLineCapItem(drawing::LineCap_BUTT));
+ break;
+ }
+ case 1: // round
+ {
+ pItem.reset(new XLineCapItem(drawing::LineCap_ROUND));
+ break;
+ }
+ case 2: // square
+ {
+ pItem.reset(new XLineCapItem(drawing::LineCap_SQUARE));
+ break;
+ }
+ }
+
+ setLineCap(pItem.get());
+}
+
+IMPL_LINK_NOARG(LinePropertyPanelBase, ToolboxWidthSelectHdl, const OString&, void)
+{
+ mxTBWidth->set_menu_item_active(SELECTWIDTH, !mxTBWidth->get_menu_item_active(SELECTWIDTH));
+}
+
+void LinePropertyPanelBase::EndLineWidthPopup()
+{
+ mxTBWidth->set_menu_item_active(SELECTWIDTH, false);
+}
+
+IMPL_LINK_NOARG( LinePropertyPanelBase, ChangeTransparentHdl, weld::MetricSpinButton&, void )
+{
+ sal_uInt16 nVal = static_cast<sal_uInt16>(mxMFTransparent->get_value(FieldUnit::PERCENT));
+ XLineTransparenceItem aItem( nVal );
+
+ setLineTransparency(aItem);
+}
+
+void LinePropertyPanelBase::SetWidthIcon(int n)
+{
+ if (n == 0)
+ mxTBWidth->set_item_icon_name(SELECTWIDTH, maIMGNone);
+ else
+ mxTBWidth->set_item_icon_name(SELECTWIDTH, maIMGWidthIcon[n-1]);
+}
+
+void LinePropertyPanelBase::SetWidthIcon()
+{
+ if(!mbWidthValuable)
+ {
+ mxTBWidth->set_item_icon_name(SELECTWIDTH, maIMGNone);
+ return;
+ }
+
+ tools::Long nVal = OutputDevice::LogicToLogic(mnWidthCoreValue * 10, meMapUnit, MapUnit::MapPoint);
+
+ if(nVal <= 6)
+ mxTBWidth->set_item_icon_name(SELECTWIDTH, maIMGWidthIcon[0]);
+ else if (nVal <= 9)
+ mxTBWidth->set_item_icon_name(SELECTWIDTH, maIMGWidthIcon[1]);
+ else if (nVal <= 12)
+ mxTBWidth->set_item_icon_name(SELECTWIDTH, maIMGWidthIcon[2]);
+ else if (nVal <= 19)
+ mxTBWidth->set_item_icon_name(SELECTWIDTH, maIMGWidthIcon[3]);
+ else if (nVal <= 26)
+ mxTBWidth->set_item_icon_name(SELECTWIDTH, maIMGWidthIcon[4]);
+ else if (nVal <= 37)
+ mxTBWidth->set_item_icon_name(SELECTWIDTH, maIMGWidthIcon[5]);
+ else if (nVal <= 52)
+ mxTBWidth->set_item_icon_name(SELECTWIDTH, maIMGWidthIcon[6]);
+ else
+ mxTBWidth->set_item_icon_name(SELECTWIDTH, maIMGWidthIcon[7]);
+
+}
+
+void LinePropertyPanelBase::SetWidth(tools::Long nWidth)
+{
+ mnWidthCoreValue = nWidth;
+ mbWidthValuable = true;
+ mxLineWidthPopup->SetWidthSelect(mnWidthCoreValue, mbWidthValuable, meMapUnit);
+}
+
+void LinePropertyPanelBase::ActivateControls()
+{
+ mxGridLineProps->set_sensitive(!mbNoneLineStyle);
+ mxBoxArrowProps->set_sensitive(!mbNoneLineStyle);
+ mxLineStyleTB->set_item_sensitive(".uno:LineEndStyle", !mbNoneLineStyle);
+
+ mxBoxArrowProps->set_visible(mbArrowSupported);
+ mxLineStyleTB->set_item_visible(".uno:LineEndStyle", mbArrowSupported);
+}
+
+void LinePropertyPanelBase::setMapUnit(MapUnit eMapUnit)
+{
+ meMapUnit = eMapUnit;
+ mxLineWidthPopup->SetWidthSelect(mnWidthCoreValue, mbWidthValuable, meMapUnit);
+}
+
+void LinePropertyPanelBase::disableArrowHead()
+{
+ mbArrowSupported = false;
+ ActivateControls();
+}
+
+void LinePropertyPanelBase::enableArrowHead()
+{
+ mbArrowSupported = true;
+ ActivateControls();
+}
+
+} // end of namespace svx::sidebar
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/sidebar/line/LineWidthPopup.cxx b/svx/source/sidebar/line/LineWidthPopup.cxx
new file mode 100644
index 000000000..347bb2a3b
--- /dev/null
+++ b/svx/source/sidebar/line/LineWidthPopup.cxx
@@ -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 .
+ */
+#include <svx/sidebar/LineWidthPopup.hxx>
+#include <svx/sidebar/LinePropertyPanelBase.hxx>
+#include <com/sun/star/beans/NamedValue.hpp>
+#include <svx/dialmgr.hxx>
+#include <svx/strings.hrc>
+#include <svx/xlnwtit.hxx>
+#include <unotools/localedatawrapper.hxx>
+#include <unotools/viewoptions.hxx>
+#include <vcl/settings.hxx>
+#include <vcl/svapp.hxx>
+#include "LineWidthValueSet.hxx"
+#include <bitmaps.hlst>
+
+namespace svx::sidebar
+{
+LineWidthPopup::LineWidthPopup(weld::Widget* pParent, LinePropertyPanelBase& rParent)
+ : WeldToolbarPopup(nullptr, pParent, "svx/ui/floatinglineproperty.ui", "FloatingLineProperty")
+ , m_rParent(rParent)
+ , m_sPt(SvxResId(RID_SVXSTR_PT))
+ , m_eMapUnit(MapUnit::MapTwip)
+ , m_bVSFocus(true)
+ , m_bCustom(false)
+ , m_nCustomWidth(0)
+ , m_aIMGCus(StockImage::Yes, RID_SVXBMP_WIDTH_CUSTOM)
+ , m_aIMGCusGray(StockImage::Yes, RID_SVXBMP_WIDTH_CUSTOM_GRAY)
+ , m_xMFWidth(m_xBuilder->weld_metric_spin_button("spin", FieldUnit::POINT))
+ , m_xVSWidth(new LineWidthValueSet())
+ , m_xVSWidthWin(new weld::CustomWeld(*m_xBuilder, "lineset", *m_xVSWidth))
+{
+ m_xVSWidth->SetStyle(m_xVSWidth->GetStyle() | WB_3DLOOK | WB_NO_DIRECTSELECT);
+
+ maStrUnits[0] = "0.5";
+ maStrUnits[1] = "0.8";
+ maStrUnits[2] = "1.0";
+ maStrUnits[3] = "1.5";
+ maStrUnits[4] = "2.3";
+ maStrUnits[5] = "3.0";
+ maStrUnits[6] = "4.5";
+ maStrUnits[7] = "6.0";
+ maStrUnits[8] = SvxResId(RID_SVXSTR_WIDTH_LAST_CUSTOM);
+
+ const LocaleDataWrapper& rLocaleWrapper(Application::GetSettings().GetLocaleDataWrapper());
+ const sal_Unicode cSep = rLocaleWrapper.getNumDecimalSep()[0];
+
+ for (int i = 0; i <= 7; i++)
+ {
+ maStrUnits[i] = maStrUnits[i].replace('.', cSep); //Modify
+ maStrUnits[i] += " ";
+ maStrUnits[i] += m_sPt;
+ }
+
+ for (sal_uInt16 i = 1; i <= 9; ++i)
+ {
+ m_xVSWidth->InsertItem(i);
+ m_xVSWidth->SetItemText(i, maStrUnits[i - 1]);
+ }
+
+ m_xVSWidth->SetUnit(maStrUnits);
+ m_xVSWidth->SetItemData(1, reinterpret_cast<void*>(5));
+ m_xVSWidth->SetItemData(2, reinterpret_cast<void*>(8));
+ m_xVSWidth->SetItemData(3, reinterpret_cast<void*>(10));
+ m_xVSWidth->SetItemData(4, reinterpret_cast<void*>(15));
+ m_xVSWidth->SetItemData(5, reinterpret_cast<void*>(23));
+ m_xVSWidth->SetItemData(6, reinterpret_cast<void*>(30));
+ m_xVSWidth->SetItemData(7, reinterpret_cast<void*>(45));
+ m_xVSWidth->SetItemData(8, reinterpret_cast<void*>(60));
+ m_xVSWidth->SetImage(m_aIMGCusGray);
+
+ m_xVSWidth->SetSelItem(0);
+
+ m_xVSWidth->SetSelectHdl(LINK(this, LineWidthPopup, VSSelectHdl));
+ m_xMFWidth->connect_value_changed(LINK(this, LineWidthPopup, MFModifyHdl));
+}
+
+LineWidthPopup::~LineWidthPopup() {}
+
+IMPL_LINK_NOARG(LineWidthPopup, VSSelectHdl, ValueSet*, void)
+{
+ sal_uInt16 iPos = m_xVSWidth->GetSelectedItemId();
+ if (iPos >= 1 && iPos <= 8)
+ {
+ sal_IntPtr nVal = OutputDevice::LogicToLogic(
+ reinterpret_cast<sal_IntPtr>(m_xVSWidth->GetItemData(iPos)), MapUnit::MapPoint,
+ m_eMapUnit);
+ nVal = m_xMFWidth->denormalize(nVal);
+ XLineWidthItem aWidthItem(nVal);
+ m_rParent.setLineWidth(aWidthItem);
+ m_rParent.SetWidthIcon(iPos);
+ m_rParent.SetWidth(nVal);
+ }
+ else if (iPos == 9)
+ { //last custom
+ //modified
+ if (m_bCustom)
+ {
+ tools::Long nVal
+ = OutputDevice::LogicToLogic(m_nCustomWidth, MapUnit::MapPoint, m_eMapUnit);
+ nVal = m_xMFWidth->denormalize(nVal);
+ XLineWidthItem aWidthItem(nVal);
+ m_rParent.setLineWidth(aWidthItem);
+ m_rParent.SetWidth(nVal);
+ }
+ else
+ {
+ m_xVSWidth->SetNoSelection(); //add , set no selection and keep the last select item
+ m_xVSWidth->SetFormat();
+ m_xVSWidth->Invalidate();
+ }
+ //modify end
+ }
+
+ if ((iPos >= 1 && iPos <= 8) || (iPos == 9 && m_bCustom)) //add
+ {
+ m_rParent.EndLineWidthPopup();
+ }
+}
+
+IMPL_LINK_NOARG(LineWidthPopup, MFModifyHdl, weld::MetricSpinButton&, void)
+{
+ if (m_xVSWidth->GetSelItem())
+ {
+ m_xVSWidth->SetSelItem(0);
+ m_xVSWidth->SetFormat();
+ m_xVSWidth->Invalidate();
+ }
+ tools::Long nTmp = static_cast<tools::Long>(m_xMFWidth->get_value(FieldUnit::NONE));
+ tools::Long nVal = OutputDevice::LogicToLogic(nTmp, MapUnit::MapPoint, m_eMapUnit);
+ sal_Int32 nNewWidth = static_cast<short>(m_xMFWidth->denormalize(nVal));
+ XLineWidthItem aWidthItem(nNewWidth);
+ m_rParent.setLineWidth(aWidthItem);
+}
+
+void LineWidthPopup::SetWidthSelect(tools::Long lValue, bool bValuable, MapUnit eMapUnit)
+{
+ m_bVSFocus = true;
+ m_xVSWidth->SetSelItem(0);
+ m_eMapUnit = eMapUnit;
+ SvtViewOptions aWinOpt(EViewType::Window, "PopupPanel_LineWidth");
+ 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_nCustomWidth = aWinData.toInt32();
+ m_bCustom = true;
+ m_xVSWidth->SetImage(m_aIMGCus);
+ m_xVSWidth->SetCusEnable(true);
+
+ OUString aStrTip = OUString::number(static_cast<double>(m_nCustomWidth) / 10) + m_sPt;
+ m_xVSWidth->SetItemText(9, aStrTip);
+ }
+ else
+ {
+ m_bCustom = false;
+ m_xVSWidth->SetImage(m_aIMGCusGray);
+ m_xVSWidth->SetCusEnable(false);
+ m_xVSWidth->SetItemText(9, maStrUnits[8]);
+ }
+
+ if (bValuable)
+ {
+ sal_Int64 nVal = OutputDevice::LogicToLogic(lValue, eMapUnit, MapUnit::Map100thMM);
+ nVal = m_xMFWidth->normalize(nVal);
+ m_xMFWidth->set_value(nVal, FieldUnit::MM_100TH);
+ }
+ else
+ {
+ m_xMFWidth->set_text("");
+ }
+
+ OUString strCurrValue = m_xMFWidth->get_text();
+ sal_uInt16 i = 0;
+ for (; i < 8; i++)
+ {
+ if (strCurrValue == maStrUnits[i])
+ {
+ m_xVSWidth->SetSelItem(i + 1);
+ break;
+ }
+ }
+
+ if (i >= 8)
+ {
+ m_bVSFocus = false;
+ m_xVSWidth->SetSelItem(0);
+ }
+
+ m_xVSWidth->SetFormat();
+ m_xVSWidth->Invalidate();
+}
+
+void LineWidthPopup::GrabFocus()
+{
+ if (m_bVSFocus)
+ m_xVSWidth->GrabFocus();
+ else
+ m_xMFWidth->grab_focus();
+}
+
+} // end of namespace svx::sidebar
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/sidebar/line/LineWidthValueSet.cxx b/svx/source/sidebar/line/LineWidthValueSet.cxx
new file mode 100644
index 000000000..be518fe4f
--- /dev/null
+++ b/svx/source/sidebar/line/LineWidthValueSet.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 "LineWidthValueSet.hxx"
+
+#include <i18nlangtag/mslangid.hxx>
+#include <vcl/event.hxx>
+#include <vcl/settings.hxx>
+#include <vcl/svapp.hxx>
+
+namespace svx::sidebar {
+
+LineWidthValueSet::LineWidthValueSet()
+ : ValueSet(nullptr)
+ , nSelItem(0)
+ , bCusEnable(false)
+{
+}
+
+void LineWidthValueSet::Resize()
+{
+ SetColCount();
+ SetLineCount(9);
+ ValueSet::Resize();
+}
+
+LineWidthValueSet::~LineWidthValueSet()
+{
+}
+
+void LineWidthValueSet::SetUnit(std::array<OUString,9> const & strUnits)
+{
+ maStrUnits = strUnits;
+}
+
+void LineWidthValueSet::SetSelItem(sal_uInt16 nSel)
+{
+ nSelItem = nSel;
+ if(nSel == 0)
+ {
+ SelectItem(1); // ,false); // 'false' nut supported by AOO
+ SetNoSelection();
+ }
+ else
+ {
+ SelectItem(nSelItem);
+ GrabFocus();
+ }
+}
+
+void LineWidthValueSet::SetImage(const Image& img)
+{
+ imgCus = img;
+}
+
+void LineWidthValueSet::SetCusEnable(bool bEnable)
+{
+ bCusEnable = bEnable;
+}
+
+void LineWidthValueSet::UserDraw( const UserDrawEvent& rUDEvt )
+{
+ tools::Rectangle aRect = rUDEvt.GetRect();
+ vcl::RenderContext* pDev = rUDEvt.GetRenderContext();
+ sal_uInt16 nItemId = rUDEvt.GetItemId();
+
+ tools::Long nRectHeight = aRect.GetHeight();
+ tools::Long nRectWidth = aRect.GetWidth();
+ Point aBLPos = aRect.TopLeft();
+
+ //const StyleSettings& rStyleSettings = GetSettings().GetStyleSettings();
+ //Color aBackColor(0,0,200);
+ //const Color aTextColor = rStyleSettings.GetFieldTextColor();
+ vcl::Font aOldFont = pDev->GetFont();
+ Color aOldColor = pDev->GetLineColor();
+ Color aOldFillColor = pDev->GetFillColor();
+
+ vcl::Font aFont(OutputDevice::GetDefaultFont(DefaultFontType::UI_SANS, MsLangId::getConfiguredSystemLanguage(), GetDefaultFontFlags::OnlyOne));
+ Size aSize = aFont.GetFontSize();
+ aSize.setHeight( nRectHeight*3/5 );
+ aFont.SetFontSize( aSize );
+
+ Point aLineStart(aBLPos.X() + 5, aBLPos.Y() + ( nRectHeight - nItemId )/2);
+ Point aLineEnd(aBLPos.X() + nRectWidth * 7 / 9 - 10, aBLPos.Y() + ( nRectHeight - nItemId )/2);
+ if (nItemId == 9)
+ {
+ Point aImgStart(aBLPos.X() + 5, aBLPos.Y() + ( nRectHeight - 23 ) / 2);
+ pDev->DrawImage(aImgStart, imgCus);
+
+ tools::Rectangle aStrRect = aRect;
+ aStrRect.AdjustTop(nRectHeight/6 );
+ aStrRect.AdjustBottom( -(nRectHeight/6) );
+ aStrRect.AdjustLeft(imgCus.GetSizePixel().Width() + 20 );
+ if(bCusEnable)
+ aFont.SetColor(Application::GetSettings().GetStyleSettings().GetFieldTextColor());
+ else
+ aFont.SetColor(Application::GetSettings().GetStyleSettings().GetDisableColor());
+
+ pDev->SetFont(aFont);
+ pDev->DrawText(aStrRect, maStrUnits[ nItemId - 1 ], DrawTextFlags::EndEllipsis);
+ }
+ else
+ {
+ if( nSelItem == nItemId )
+ {
+ tools::Rectangle aBackRect = aRect;
+ aBackRect.AdjustTop(3 );
+ aBackRect.AdjustBottom( -2 );
+ pDev->SetFillColor(Color(50,107,197));
+ pDev->DrawRect(aBackRect);
+ }
+ else
+ {
+ pDev->SetFillColor( COL_TRANSPARENT );
+ pDev->DrawRect(aRect);
+ }
+
+ //draw text
+ if(nSelItem == nItemId )
+ aFont.SetColor(COL_WHITE);
+ else
+ aFont.SetColor(Application::GetSettings().GetStyleSettings().GetFieldTextColor());
+ pDev->SetFont(aFont);
+ Point aStart(aBLPos.X() + nRectWidth * 7 / 9 , aBLPos.Y() + nRectHeight/6);
+ pDev->DrawText(aStart, maStrUnits[ nItemId - 1 ]); //can't set DrawTextFlags::EndEllipsis here ,or the text will disappear
+
+ //draw line
+ if( nSelItem == nItemId )
+ pDev->SetLineColor(COL_WHITE);
+ else
+ pDev->SetLineColor(Application::GetSettings().GetStyleSettings().GetFieldTextColor());
+
+ for(sal_uInt16 i = 1; i <= nItemId; i++)
+ {
+ pDev->DrawLine(aLineStart,aLineEnd );
+ aLineStart.setY(aLineStart.getY() + 1);
+ aLineEnd.setY (aLineEnd.getY() + 1);
+ }
+ }
+
+ Invalidate( aRect );
+ pDev->SetLineColor(aOldColor);
+ pDev->SetFillColor(aOldFillColor);
+ pDev->SetFont(aOldFont);
+}
+
+void LineWidthValueSet::SetDrawingArea(weld::DrawingArea* pDrawingArea)
+{
+ ValueSet::SetDrawingArea(pDrawingArea);
+ Size aSize(pDrawingArea->get_ref_device().LogicToPixel(Size(80, 12 * 9), MapMode(MapUnit::MapAppFont)));
+ pDrawingArea->set_size_request(aSize.Width(), aSize.Height());
+ SetOutputSizePixel(aSize);
+}
+
+} // end of namespace svx::sidebar
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/sidebar/line/LineWidthValueSet.hxx b/svx/source/sidebar/line/LineWidthValueSet.hxx
new file mode 100644
index 000000000..225e706a6
--- /dev/null
+++ b/svx/source/sidebar/line/LineWidthValueSet.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_SVX_SOURCE_SIDEBAR_LINE_LINEWIDTHVALUESET_HXX
+#define INCLUDED_SVX_SOURCE_SIDEBAR_LINE_LINEWIDTHVALUESET_HXX
+
+#include <svtools/valueset.hxx>
+#include <vcl/image.hxx>
+#include <array>
+
+namespace svx::sidebar {
+
+class LineWidthValueSet final : public ValueSet
+{
+public:
+ explicit LineWidthValueSet();
+ virtual ~LineWidthValueSet() override;
+
+ void SetUnit(std::array<OUString,9> const & strUnits);
+ void SetSelItem(sal_uInt16 nSel);
+ sal_uInt16 GetSelItem() const { return nSelItem;}
+ void SetImage(const Image& img);
+ void SetCusEnable(bool bEnable);
+
+ virtual void UserDraw( const UserDrawEvent& rUDEvt ) override;
+ virtual void Resize() override;
+ virtual void SetDrawingArea(weld::DrawingArea* pDrawingArea) override;
+
+private:
+ sal_uInt16 nSelItem;
+ std::array<OUString,9> maStrUnits;
+ Image imgCus;
+ bool bCusEnable;
+};
+
+} // end of namespace svx::sidebar
+
+#endif // INCLUDED_SVX_SOURCE_SIDEBAR_LINE_LINEWIDTHVALUESET_HXX
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/sidebar/lists/ListsPropertyPanel.cxx b/svx/source/sidebar/lists/ListsPropertyPanel.cxx
new file mode 100644
index 000000000..58badf679
--- /dev/null
+++ b/svx/source/sidebar/lists/ListsPropertyPanel.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 "ListsPropertyPanel.hxx"
+#include <com/sun/star/lang/IllegalArgumentException.hpp>
+
+using namespace css;
+using namespace css::uno;
+
+namespace svx::sidebar
+{
+std::unique_ptr<PanelLayout>
+ListsPropertyPanel::Create(weld::Widget* pParent,
+ const css::uno::Reference<css::frame::XFrame>& rxFrame)
+{
+ if (pParent == nullptr)
+ throw lang::IllegalArgumentException("no parent Window given to ListsPropertyPanel::Create",
+ nullptr, 0);
+ if (!rxFrame.is())
+ throw lang::IllegalArgumentException("no XFrame given to ListsPropertyPanel::Create",
+ nullptr, 1);
+
+ return std::make_unique<ListsPropertyPanel>(pParent, rxFrame);
+}
+
+ListsPropertyPanel::ListsPropertyPanel(weld::Widget* pParent,
+ const css::uno::Reference<css::frame::XFrame>& rxFrame)
+ : PanelLayout(pParent, "ListsPropertyPanel", "svx/ui/sidebarlists.ui")
+ , mxTBxNumBullet(m_xBuilder->weld_toolbar("numberbullet"))
+ , mxNumBulletDispatcher(new ToolbarUnoDispatcher(*mxTBxNumBullet, *m_xBuilder, rxFrame))
+ , mxTBxOutline(m_xBuilder->weld_toolbar("outline"))
+ , mxOutlineDispatcher(new ToolbarUnoDispatcher(*mxTBxOutline, *m_xBuilder, rxFrame))
+{
+}
+
+ListsPropertyPanel::~ListsPropertyPanel()
+{
+ mxOutlineDispatcher.reset();
+ mxTBxOutline.reset();
+ mxNumBulletDispatcher.reset();
+ mxTBxNumBullet.reset();
+}
+
+} // end of namespace svx::sidebar
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/sidebar/lists/ListsPropertyPanel.hxx b/svx/source/sidebar/lists/ListsPropertyPanel.hxx
new file mode 100644
index 000000000..ec20bb430
--- /dev/null
+++ b/svx/source/sidebar/lists/ListsPropertyPanel.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_SVX_SOURCE_SIDEBAR_LISTS_LISTSPROPERTYPANEL_HXX
+#define INCLUDED_SVX_SOURCE_SIDEBAR_LISTS_LISTSPROPERTYPANEL_HXX
+
+#include <sfx2/weldutils.hxx>
+#include <sfx2/sidebar/PanelLayout.hxx>
+#include <com/sun/star/frame/XFrame.hpp>
+
+namespace svx::sidebar
+{
+class ListsPropertyPanel : public PanelLayout
+{
+public:
+ virtual ~ListsPropertyPanel() override;
+
+ static std::unique_ptr<PanelLayout>
+ Create(weld::Widget* pParent, const css::uno::Reference<css::frame::XFrame>& rxFrame);
+
+ ListsPropertyPanel(weld::Widget* pParent,
+ const css::uno::Reference<css::frame::XFrame>& rxFrame);
+
+private:
+ std::unique_ptr<weld::Toolbar> mxTBxNumBullet;
+ std::unique_ptr<ToolbarUnoDispatcher> mxNumBulletDispatcher;
+ std::unique_ptr<weld::Toolbar> mxTBxOutline;
+ std::unique_ptr<ToolbarUnoDispatcher> mxOutlineDispatcher;
+};
+
+} // end of namespace svx::sidebar
+
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/sidebar/media/MediaPlaybackPanel.cxx b/svx/source/sidebar/media/MediaPlaybackPanel.cxx
new file mode 100644
index 000000000..b23c612ff
--- /dev/null
+++ b/svx/source/sidebar/media/MediaPlaybackPanel.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 "MediaPlaybackPanel.hxx"
+#include <avmedia/mediaitem.hxx>
+#include <sfx2/sfxsids.hrc>
+#include <sfx2/dispatch.hxx>
+#include <avmedia/MediaControlBase.hxx>
+
+#include <com/sun/star/lang/IllegalArgumentException.hpp>
+
+using namespace avmedia;
+
+namespace svx::sidebar {
+
+MediaPlaybackPanel::MediaPlaybackPanel (
+ weld::Widget* pParent,
+ SfxBindings* pBindings)
+ : PanelLayout(pParent, "MediaPlaybackPanel", "svx/ui/mediaplayback.ui"),
+ maMediaController(SID_AVMEDIA_TOOLBOX, *pBindings, *this),
+ maIdle("MediaPlaybackPanel"),
+ mpBindings(pBindings)
+{
+ mxTimeEdit = m_xBuilder->weld_entry("timeedit");
+ mxPlayToolBox = m_xBuilder->weld_toolbar("playtoolbox");
+ mxMuteToolBox = m_xBuilder->weld_toolbar("mutetoolbox");
+ mxTimeSlider = m_xBuilder->weld_scale("timeslider");
+ mxVolumeSlider = m_xBuilder->weld_scale("volumeslider");
+ mxZoomListBox = m_xBuilder->weld_combo_box("zoombox");
+
+ Initialize();
+}
+
+std::unique_ptr<PanelLayout> MediaPlaybackPanel::Create(
+ weld::Widget* pParent,
+ SfxBindings* pBindings)
+{
+ if (pParent == nullptr)
+ throw lang::IllegalArgumentException("no parent Window given to MediaPlaybackPanel::Create", nullptr, 0);
+ if (pBindings == nullptr)
+ throw lang::IllegalArgumentException("no SfxBindings given to MediaPlaybackPanel::Create", nullptr, 2);
+
+ return std::make_unique<MediaPlaybackPanel>(pParent, pBindings);
+}
+
+void MediaPlaybackPanel::Initialize()
+{
+ InitializeWidgets();
+ mxVolumeSlider->connect_value_changed(LINK(this, MediaPlaybackPanel, VolumeSlideHdl));
+ mxPlayToolBox->connect_clicked(LINK(this, MediaPlaybackPanel, PlayToolBoxSelectHdl));
+ mxMuteToolBox->connect_clicked(LINK(this, MediaPlaybackPanel, PlayToolBoxSelectHdl));
+ mxTimeSlider->connect_value_changed(LINK(this, MediaPlaybackPanel, SeekHdl));
+
+ maIdle.SetPriority( TaskPriority::HIGHEST );
+ maIdle.SetInvokeHandler( LINK( this, MediaPlaybackPanel, TimeoutHdl ) );
+ maIdle.Start();
+ mpBindings->Invalidate(SID_AVMEDIA_TOOLBOX);
+}
+
+MediaPlaybackPanel::~MediaPlaybackPanel()
+{
+ disposeWidgets();
+}
+
+void MediaPlaybackPanel::NotifyItemUpdate(
+ const sal_uInt16 nSID,
+ const SfxItemState eState,
+ const SfxPoolItem* pState)
+{
+ if( nSID == SID_AVMEDIA_TOOLBOX )
+ {
+ if(eState >= SfxItemState::DEFAULT)
+ {
+ mpMediaItem.reset(pState ? static_cast< MediaItem* >(pState->Clone()) : nullptr);
+ Update();
+ }
+ }
+}
+
+void MediaPlaybackPanel::UpdateToolBoxes(const MediaItem& rMediaItem)
+{
+ mxPlayToolBox->set_sensitive(false);
+ avmedia::MediaControlBase::UpdateToolBoxes(rMediaItem);
+}
+
+void MediaPlaybackPanel::Update()
+{
+ if (mpMediaItem)
+ {
+ UpdateToolBoxes( *mpMediaItem );
+ UpdateTimeSlider( *mpMediaItem );
+ UpdateVolumeSlider( *mpMediaItem );
+ UpdateTimeField( *mpMediaItem, mpMediaItem->getTime() );
+ }
+}
+
+IMPL_LINK_NOARG( MediaPlaybackPanel, VolumeSlideHdl, weld::Scale&, void)
+{
+ MediaItem aItem(SID_AVMEDIA_TOOLBOX);
+ aItem.setVolumeDB(mxVolumeSlider->get_value());
+ mpBindings->GetDispatcher()->ExecuteList(SID_AVMEDIA_TOOLBOX, SfxCallMode::RECORD, { &aItem });
+}
+
+IMPL_LINK_NOARG( MediaPlaybackPanel, SeekHdl, weld::Scale&, void)
+{
+ MediaItem aItem(SID_AVMEDIA_TOOLBOX);
+ double nTime = 0;
+ if (mpMediaItem)
+ nTime = mxTimeSlider->get_value() * mpMediaItem->getDuration() / AVMEDIA_TIME_RANGE;
+ aItem.setTime(nTime);
+ mpBindings->GetDispatcher()->ExecuteList(SID_AVMEDIA_TOOLBOX, SfxCallMode::RECORD, { &aItem });
+ mpBindings->Invalidate(SID_AVMEDIA_TOOLBOX);
+}
+
+IMPL_LINK_NOARG( MediaPlaybackPanel, TimeoutHdl, Timer*, void)
+{
+ mpBindings->Invalidate(SID_AVMEDIA_TOOLBOX);
+}
+
+IMPL_LINK( MediaPlaybackPanel, PlayToolBoxSelectHdl, const OString&, rId, void)
+{
+ MediaItem aItem(SID_AVMEDIA_TOOLBOX);
+
+ if (rId == "play")
+ {
+ aItem.setState( MediaState::Play );
+
+ if( !mpMediaItem || (mpMediaItem->getTime() == mpMediaItem->getDuration() ))
+ aItem.setTime( 0.0 );
+ else
+ aItem.setTime( mpMediaItem->getTime());
+ }
+ else if (rId == "pause")
+ {
+ aItem.setState( MediaState::Pause );
+ }
+ else if (rId == "stop")
+ {
+ aItem.setState( MediaState::Stop );
+ aItem.setTime( 0.0 );
+ }
+ else if (rId == "mute")
+ {
+ aItem.setMute( mxMuteToolBox->get_item_active("mute") );
+ }
+ else if (rId == "loop")
+ {
+ aItem.setLoop( mxPlayToolBox->get_item_active("loop") );
+ }
+
+ if(aItem.getMaskSet() != AVMediaSetMask::NONE)
+ {
+ mpBindings->GetDispatcher()->ExecuteList(SID_AVMEDIA_TOOLBOX, SfxCallMode::RECORD, { &aItem } );
+ mpBindings->Invalidate(SID_AVMEDIA_TOOLBOX);
+ }
+}
+
+} // end of namespace svx::sidebar
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/sidebar/media/MediaPlaybackPanel.hxx b/svx/source/sidebar/media/MediaPlaybackPanel.hxx
new file mode 100644
index 000000000..95f3d6eaa
--- /dev/null
+++ b/svx/source/sidebar/media/MediaPlaybackPanel.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 .
+ */
+#pragma once
+
+#include <memory>
+
+#include <sfx2/sidebar/PanelLayout.hxx>
+#include <avmedia/mediaitem.hxx>
+#include <sfx2/bindings.hxx>
+#include <sfx2/sidebar/ControllerItem.hxx>
+#include <avmedia/MediaControlBase.hxx>
+#include <vcl/idle.hxx>
+
+using namespace css;
+using namespace ::com::sun::star::uno;
+using namespace ::com::sun::star::frame;
+
+namespace svx::sidebar {
+
+/** This panel provides media playback control in document
+*/
+class MediaPlaybackPanel
+ : public PanelLayout,
+ public ::sfx2::sidebar::ControllerItem::ItemUpdateReceiverInterface,
+ public ::avmedia::MediaControlBase
+{
+public:
+ MediaPlaybackPanel (
+ weld::Widget* pParent,
+ SfxBindings* pBindings);
+ static std::unique_ptr<PanelLayout> Create(
+ weld::Widget* pParent,
+ SfxBindings* pBindings);
+ virtual ~MediaPlaybackPanel() override;
+
+protected:
+ virtual void UpdateToolBoxes(const avmedia::MediaItem& rMediaItem) override;
+
+private:
+ std::unique_ptr< ::avmedia::MediaItem > mpMediaItem;
+ ::sfx2::sidebar::ControllerItem maMediaController;
+ Idle maIdle;
+ SfxBindings* mpBindings;
+ void Initialize();
+ void Update();
+ 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 {};
+
+ DECL_LINK(PlayToolBoxSelectHdl, const OString&, void);
+ DECL_LINK(VolumeSlideHdl, weld::Scale&, void);
+ DECL_LINK(SeekHdl, weld::Scale&, void);
+
+ DECL_LINK(TimeoutHdl, Timer*, void);
+};
+
+
+} // end of namespace svx::sidebar
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/sidebar/nbdtmg.cxx b/svx/source/sidebar/nbdtmg.cxx
new file mode 100644
index 000000000..241b91673
--- /dev/null
+++ b/svx/source/sidebar/nbdtmg.cxx
@@ -0,0 +1,905 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+#include <svx/nbdtmg.hxx>
+#include <svx/svxids.hrc>
+#include <vcl/svapp.hxx>
+#include <svl/itemset.hxx>
+#include <sfx2/request.hxx>
+#include <svl/stritem.hxx>
+#include <svtools/ctrltool.hxx>
+#include <sfx2/objsh.hxx>
+#include <editeng/flstitem.hxx>
+#include <svl/itempool.hxx>
+#include <vcl/outdev.hxx>
+#include <editeng/brushitem.hxx>
+#include <svx/dialmgr.hxx>
+#include <svx/strings.hrc>
+#include <vcl/graph.hxx>
+#include <vcl/settings.hxx>
+
+#include <i18nlangtag/languagetag.hxx>
+#include <o3tl/temporary.hxx>
+#include <tools/debug.hxx>
+#include <tools/urlobj.hxx>
+#include <unotools/ucbstreamhelper.hxx>
+#include <unotools/pathoptions.hxx>
+#include <editeng/eeitem.hxx>
+
+#include <com/sun/star/text/VertOrientation.hpp>
+#include <com/sun/star/style/NumberingType.hpp>
+#include <com/sun/star/container/XIndexAccess.hpp>
+#include <com/sun/star/text/DefaultNumberingProvider.hpp>
+#include <com/sun/star/beans/PropertyValue.hpp>
+#include <comphelper/processfactory.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::lang;
+using namespace com::sun::star::text;
+using namespace com::sun::star::container;
+using namespace com::sun::star::style;
+
+namespace svx::sidebar {
+
+namespace {
+
+const vcl::Font& lcl_GetDefaultBulletFont()
+{
+ static vcl::Font aDefBulletFont = []()
+ {
+ static vcl::Font tmp("OpenSymbol", "", Size(0, 14));
+ tmp.SetCharSet( RTL_TEXTENCODING_SYMBOL );
+ tmp.SetFamily( FAMILY_DONTKNOW );
+ tmp.SetPitch( PITCH_DONTKNOW );
+ tmp.SetWeight( WEIGHT_DONTKNOW );
+ tmp.SetTransparent( true );
+ return tmp;
+ }();
+ return aDefBulletFont;
+}
+
+const sal_Unicode aDefaultBulletTypes[] =
+{
+ 0x2022,
+ 0x25cf,
+ 0xe00c,
+ 0xe00a,
+ 0x2794,
+ 0x27a2,
+ 0x2717,
+ 0x2714
+};
+
+NumSettings_Impl* lcl_CreateNumberingSettingsPtr(const Sequence<PropertyValue>& rLevelProps)
+{
+ NumSettings_Impl* pNew = new NumSettings_Impl;
+ for(const PropertyValue& rValue : rLevelProps)
+ {
+ if(rValue.Name == "NumberingType")
+ {
+ sal_Int16 nTmp;
+ if (rValue.Value >>= nTmp)
+ pNew->nNumberType = static_cast<SvxNumType>(nTmp);
+ }
+ else if(rValue.Name == "Prefix")
+ rValue.Value >>= pNew->sPrefix;
+ else if(rValue.Name == "Suffix")
+ rValue.Value >>= pNew->sSuffix;
+ else if(rValue.Name == "ParentNumbering")
+ rValue.Value >>= pNew->nParentNumbering;
+ else if(rValue.Name == "BulletChar")
+ rValue.Value >>= pNew->sBulletChar;
+ else if(rValue.Name == "BulletFontName")
+ rValue.Value >>= pNew->sBulletFont;
+ }
+ const sal_Unicode cLocalPrefix = pNew->sPrefix.getLength() ? pNew->sPrefix[0] : 0;
+ const sal_Unicode cLocalSuffix = pNew->sSuffix.getLength() ? pNew->sSuffix[0] : 0;
+ if( cLocalPrefix == ' ') pNew->sPrefix.clear();
+ if( cLocalSuffix == ' ') pNew->sSuffix.clear();
+ return pNew;
+}
+
+}
+
+sal_uInt16 NBOTypeMgrBase:: IsSingleLevel(sal_uInt16 nCurLevel)
+{
+ sal_uInt16 nLv = sal_uInt16(0xFFFF);
+ sal_uInt16 nCount = 0;
+ sal_uInt16 nMask = 1;
+ for( sal_uInt16 i = 0; i < SVX_MAX_NUM; i++ )
+ {
+ if(nCurLevel & nMask)
+ {
+ nCount++;
+ nLv=i;
+ }
+ nMask <<= 1 ;
+ }
+
+ if ( nCount == 1)
+ return nLv;
+ else
+ return sal_uInt16(0xFFFF);
+}
+
+void NBOTypeMgrBase::SetItems(const SfxItemSet* pArg) {
+ pSet = pArg;
+ if ( !pSet )
+ return;
+
+ SfxAllItemSet aSet(*pSet);
+
+ const SfxStringItem* pBulletCharFmt = aSet.GetItem<SfxStringItem>(SID_BULLET_CHAR_FMT, false);
+ if (pBulletCharFmt)
+ aBulletCharFmtName = pBulletCharFmt->GetValue();
+
+ const SfxStringItem* pNumCharFmt = aSet.GetItem<SfxStringItem>(SID_NUM_CHAR_FMT, false);
+ if (pNumCharFmt)
+ aNumCharFmtName = pNumCharFmt->GetValue();
+
+ const SfxPoolItem* pItem;
+ SfxItemState eState = pSet->GetItemState(SID_ATTR_NUMBERING_RULE, false, &pItem);
+ if(eState == SfxItemState::SET)
+ {
+ eCoreUnit = pSet->GetPool()->GetMetric(pSet->GetPool()->GetWhich(SID_ATTR_NUMBERING_RULE));
+ } else {
+ //sd use different sid for numbering rule
+ eState = pSet->GetItemState(EE_PARA_NUMBULLET, false, &pItem);
+ if(eState == SfxItemState::SET)
+ {
+ eCoreUnit = pSet->GetPool()->GetMetric(pSet->GetPool()->GetWhich(EE_PARA_NUMBULLET));
+ }
+ }
+}
+
+void NBOTypeMgrBase::ImplLoad(std::u16string_view filename)
+{
+ bIsLoading = true;
+ MapUnit eOldCoreUnit=eCoreUnit;
+ eCoreUnit = MapUnit::Map100thMM;
+ INetURLObject aFile( SvtPathOptions().GetUserConfigPath() );
+ aFile.Append( filename);
+ std::unique_ptr<SvStream> xIStm(::utl::UcbStreamHelper::CreateStream( aFile.GetMainURL( INetURLObject::DecodeMechanism::NONE ), StreamMode::READ ));
+ if( xIStm ) {
+ sal_uInt32 nVersion = 0;
+ sal_Int32 nNumIndex = 0;
+ xIStm->ReadUInt32( nVersion );
+ if (nVersion==DEFAULT_NUMBERING_CACHE_FORMAT_VERSION) //first version
+ {
+ xIStm->ReadInt32( nNumIndex );
+ while (nNumIndex>=0 && nNumIndex<DEFAULT_NUM_VALUSET_COUNT) {
+ SvxNumRule aNum(*xIStm);
+ //bullet color in font properties is not stored correctly. Need set transparency bits manually
+ for(sal_uInt16 i = 0; i < aNum.GetLevelCount(); i++)
+ {
+ SvxNumberFormat aFmt(aNum.GetLevel(i));
+ if (aFmt.GetBulletFont()) {
+ vcl::Font aFont(*aFmt.GetBulletFont());
+ Color c=aFont.GetColor();
+ c.SetAlpha(0);
+ aFont.SetColor(c);
+ aFmt.SetBulletFont(&aFont);
+ aNum.SetLevel(i, aFmt);
+ }
+ }
+ RelplaceNumRule(aNum,nNumIndex,0x1/*nLevel*/);
+ xIStm->ReadInt32( nNumIndex );
+ }
+ }
+ }
+ eCoreUnit = eOldCoreUnit;
+ bIsLoading = false;
+}
+void NBOTypeMgrBase::ImplStore(std::u16string_view filename)
+{
+ if (bIsLoading) return;
+ MapUnit eOldCoreUnit=eCoreUnit;
+ eCoreUnit = MapUnit::Map100thMM;
+ INetURLObject aFile( SvtPathOptions().GetUserConfigPath() );
+ aFile.Append( filename);
+ std::unique_ptr<SvStream> xOStm(::utl::UcbStreamHelper::CreateStream( aFile.GetMainURL( INetURLObject::DecodeMechanism::NONE ), StreamMode::WRITE ));
+ if( xOStm ) {
+ sal_uInt32 nVersion;
+ sal_Int32 nNumIndex;
+ nVersion = DEFAULT_NUMBERING_CACHE_FORMAT_VERSION;
+ xOStm->WriteUInt32( nVersion );
+ for(sal_Int32 nItem = 0; nItem < DEFAULT_NUM_VALUSET_COUNT; nItem++ ) {
+ if (IsCustomized(nItem)) {
+ SvxNumRule aDefNumRule( SvxNumRuleFlags::BULLET_REL_SIZE | SvxNumRuleFlags::CONTINUOUS | SvxNumRuleFlags::BULLET_COLOR,
+ 10, false,
+ SvxNumRuleType::NUMBERING, SvxNumberFormat::LABEL_ALIGNMENT);
+ xOStm->WriteInt32( nItem );
+ ApplyNumRule(aDefNumRule,nItem,0x1/*nLevel*/,false,true);
+ aDefNumRule.Store(*xOStm);
+ }
+ }
+ nNumIndex = -1;
+ xOStm->WriteInt32( nNumIndex ); //write end flag
+ }
+ eCoreUnit = eOldCoreUnit;
+}
+
+// Character Bullet Type lib
+BulletsSettings* BulletsTypeMgr::pActualBullets[] ={nullptr,nullptr,nullptr,nullptr,nullptr,nullptr,nullptr,nullptr};
+sal_Unicode BulletsTypeMgr::aDynamicBulletTypes[]={' ',' ',' ',' ',' ',' ',' ',' '};
+sal_Unicode BulletsTypeMgr::aDynamicRTLBulletTypes[]={' ',' ',' ',' ',' ',' ',' ',' '};
+
+BulletsTypeMgr::BulletsTypeMgr()
+{
+ Init();
+}
+
+BulletsTypeMgr& BulletsTypeMgr::GetInstance()
+{
+ static BulletsTypeMgr theBulletsTypeMgr;
+ return theBulletsTypeMgr;
+}
+
+void BulletsTypeMgr::Init()
+{
+ const vcl::Font& rActBulletFont = lcl_GetDefaultBulletFont();
+
+ for (sal_uInt16 i=0;i<DEFAULT_BULLET_TYPES;i++)
+ {
+ pActualBullets[i] = new BulletsSettings;
+ pActualBullets[i]->cBulletChar = aDefaultBulletTypes[i];
+ pActualBullets[i]->aFont = rActBulletFont;
+ OString id = OString::Concat(RID_SVXSTR_BULLET_DESCRIPTION_0.mpId) + OString::number(i);
+ pActualBullets[i]->sDescription = SvxResId( TranslateId(RID_SVXSTR_BULLET_DESCRIPTION_0.mpContext, id.getStr()) );
+ }
+}
+sal_uInt16 BulletsTypeMgr::GetNBOIndexForNumRule(SvxNumRule& aNum,sal_uInt16 mLevel,sal_uInt16 nFromIndex)
+{
+ if ( mLevel == sal_uInt16(0xFFFF) || mLevel == 0)
+ return sal_uInt16(0xFFFF);
+ //if ( !lcl_IsNumFmtSet(pNR, mLevel) ) return (sal_uInt16)0xFFFF;
+
+ sal_uInt16 nActLv = IsSingleLevel(mLevel);
+
+ if ( nActLv == sal_uInt16(0xFFFF) )
+ return sal_uInt16(0xFFFF);
+
+ const SvxNumberFormat& aFmt(aNum.GetLevel(nActLv));
+ sal_UCS4 cChar = aFmt.GetBulletChar();
+ for(sal_uInt16 i = nFromIndex; i < DEFAULT_BULLET_TYPES; i++)
+ {
+ if ( (cChar == pActualBullets[i]->cBulletChar) ||
+ (cChar == 9830 && 57356 == pActualBullets[i]->cBulletChar) ||
+ (cChar == 9632 && 57354 == pActualBullets[i]->cBulletChar) )
+ {
+ return i+1;
+ }
+ }
+
+ return sal_uInt16(0xFFFF);
+}
+
+void BulletsTypeMgr::RelplaceNumRule(SvxNumRule& aNum, sal_uInt16 nIndex, sal_uInt16 mLevel)
+{
+ if ( mLevel == sal_uInt16(0xFFFF) || mLevel == 0)
+ return;
+
+ if ( GetNBOIndexForNumRule(aNum,mLevel) != sal_uInt16(0xFFFF) )
+ return;
+
+ sal_uInt16 nActLv = IsSingleLevel(mLevel);
+
+ if ( nActLv == sal_uInt16(0xFFFF) )
+ return;
+
+ SvxNumberFormat aFmt(aNum.GetLevel(nActLv));
+ sal_UCS4 cChar = aFmt.GetBulletChar();
+ std::optional<vcl::Font> pFont = aFmt.GetBulletFont();
+ if ( nIndex >= DEFAULT_BULLET_TYPES )
+ return;
+
+ pActualBullets[nIndex]->cBulletChar = cChar;
+ if ( pFont )
+ pActualBullets[nIndex]->aFont = *pFont;
+ pActualBullets[nIndex]->bIsCustomized = true;
+}
+
+void BulletsTypeMgr::ApplyNumRule(SvxNumRule& aNum, sal_uInt16 nIndex, sal_uInt16 mLevel, bool /*isDefault*/, bool isResetSize)
+{
+ if ( nIndex >= DEFAULT_BULLET_TYPES )
+ return;
+ sal_UCS4 cChar = pActualBullets[nIndex]->cBulletChar;
+ const vcl::Font& rActBulletFont = pActualBullets[nIndex]->aFont;
+
+ sal_uInt16 nMask = 1;
+ OUString sBulletCharFormatName = GetBulletCharFmtName();
+ for(sal_uInt16 i = 0; i < aNum.GetLevelCount(); i++)
+ {
+ if(mLevel & nMask)
+ {
+ SvxNumberFormat aFmt(aNum.GetLevel(i));
+ aFmt.SetNumberingType( SVX_NUM_CHAR_SPECIAL );
+ aFmt.SetBulletFont(&rActBulletFont);
+ aFmt.SetBulletChar(cChar);
+ aFmt.SetCharFormatName(sBulletCharFormatName);
+ aFmt.SetListFormat( "" );
+ if (isResetSize) aFmt.SetBulletRelSize(45);
+ aNum.SetLevel(i, aFmt);
+ }
+ nMask <<= 1;
+ }
+}
+
+OUString BulletsTypeMgr::GetDescription(sal_uInt16 nIndex, bool /*isDefault*/)
+{
+ OUString sRet;
+
+ if ( nIndex >= DEFAULT_BULLET_TYPES )
+ return sRet;
+ else
+ sRet = pActualBullets[nIndex]->sDescription;
+
+ return sRet;
+}
+
+bool BulletsTypeMgr::IsCustomized(sal_uInt16 nIndex)
+{
+ bool bRet = false;
+
+ if ( nIndex >= DEFAULT_BULLET_TYPES )
+ bRet = false;
+ else
+ bRet = pActualBullets[nIndex]->bIsCustomized;
+
+ return bRet;
+}
+
+// Numbering Type lib
+NumberingTypeMgr::NumberingTypeMgr()
+{
+ Init();
+ maDefaultNumberSettingsArr = maNumberSettingsArr;
+ ImplLoad(u"standard.syb");
+}
+
+NumberingTypeMgr::~NumberingTypeMgr()
+{
+}
+
+const TranslateId RID_SVXSTR_SINGLENUM_DESCRIPTIONS[] =
+{
+ RID_SVXSTR_SINGLENUM_DESCRIPTION_0,
+ RID_SVXSTR_SINGLENUM_DESCRIPTION_1,
+ RID_SVXSTR_SINGLENUM_DESCRIPTION_2,
+ RID_SVXSTR_SINGLENUM_DESCRIPTION_3,
+ RID_SVXSTR_SINGLENUM_DESCRIPTION_4,
+ RID_SVXSTR_SINGLENUM_DESCRIPTION_5,
+ RID_SVXSTR_SINGLENUM_DESCRIPTION_6,
+ RID_SVXSTR_SINGLENUM_DESCRIPTION_7
+};
+
+NumberingTypeMgr& NumberingTypeMgr::GetInstance()
+{
+ static NumberingTypeMgr theNumberingTypeMgr;
+ return theNumberingTypeMgr;
+}
+
+void NumberingTypeMgr::Init()
+{
+ Reference< XComponentContext > xContext = ::comphelper::getProcessComponentContext();
+ Reference<XDefaultNumberingProvider> xDefNum = DefaultNumberingProvider::create( xContext );
+
+ Sequence< Sequence< PropertyValue > > aNumberings;
+ Locale aLocale(Application::GetSettings().GetLanguageTag().getLocale());
+ try
+ {
+ aNumberings = xDefNum->getDefaultContinuousNumberingLevels( aLocale );
+
+ sal_Int32 nLength = aNumberings.getLength();
+
+ const Sequence<PropertyValue>* pValuesArr = aNumberings.getConstArray();
+ for(sal_Int32 i = 0; i < nLength; i++)
+ {
+ NumSettings_Impl* pNew = lcl_CreateNumberingSettingsPtr(pValuesArr[i]);
+ std::shared_ptr<NumberSettings_Impl> pNumEntry = std::make_shared<NumberSettings_Impl>();
+ pNumEntry->pNumSetting = pNew;
+ if ( i < 8 )
+ pNumEntry->sDescription = SvxResId(RID_SVXSTR_SINGLENUM_DESCRIPTIONS[i]);
+ maNumberSettingsArr.push_back(pNumEntry);
+ }
+ }
+ catch(Exception&)
+ {
+ }
+}
+
+sal_uInt16 NumberingTypeMgr::GetNBOIndexForNumRule(SvxNumRule& aNum,sal_uInt16 mLevel,sal_uInt16 nFromIndex)
+{
+ if ( mLevel == sal_uInt16(0xFFFF) || mLevel > aNum.GetLevelCount() || mLevel == 0)
+ return sal_uInt16(0xFFFF);
+
+ sal_uInt16 nActLv = IsSingleLevel(mLevel);
+
+ if ( nActLv == sal_uInt16(0xFFFF) )
+ return sal_uInt16(0xFFFF);
+
+ const SvxNumberFormat& aFmt(aNum.GetLevel(nActLv));
+ //sal_Unicode cPrefix = OUString(aFmt.GetPrefix())[0];
+ //sal_Unicode cSuffix = :OUString(aFmt.GetSuffix())[0];
+ const OUString& sPrefix = aFmt.GetPrefix();
+ const OUString& sLclSuffix = aFmt.GetSuffix();
+ sal_Int16 eNumType = aFmt.GetNumberingType();
+
+ sal_uInt16 nCount = maNumberSettingsArr.size();
+ for(sal_uInt16 i = nFromIndex; i < nCount; ++i)
+ {
+ NumberSettings_Impl* _pSet = maNumberSettingsArr[i].get();
+ sal_Int16 eNType = _pSet->pNumSetting->nNumberType;
+ OUString sLocalPrefix = _pSet->pNumSetting->sPrefix;
+ OUString sLocalSuffix = _pSet->pNumSetting->sSuffix;
+ if (sPrefix == sLocalPrefix &&
+ sLclSuffix == sLocalSuffix &&
+ eNumType == eNType )
+ {
+ return i+1;
+ }
+ }
+
+
+ return sal_uInt16(0xFFFF);
+}
+
+void NumberingTypeMgr::RelplaceNumRule(SvxNumRule& aNum, sal_uInt16 nIndex, sal_uInt16 mLevel)
+{
+ sal_uInt16 nActLv = IsSingleLevel(mLevel);
+
+ if ( nActLv == sal_uInt16(0xFFFF) )
+ return;
+
+ const SvxNumberFormat& aFmt(aNum.GetLevel(nActLv));
+ SvxNumType eNumType = aFmt.GetNumberingType();
+
+ sal_uInt16 nCount = maNumberSettingsArr.size();
+ if ( nIndex >= nCount )
+ return;
+
+ NumberSettings_Impl* _pSet = maNumberSettingsArr[nIndex].get();
+
+ _pSet->pNumSetting->sPrefix = aFmt.GetPrefix();
+ _pSet->pNumSetting->sSuffix = aFmt.GetSuffix();
+ _pSet->pNumSetting->nNumberType = eNumType;
+ _pSet->bIsCustomized = true;
+
+ SvxNumRule aTmpRule1(aNum);
+ SvxNumRule aTmpRule2(aNum);
+ ApplyNumRule(aTmpRule1,nIndex,mLevel,true);
+ ApplyNumRule(aTmpRule2,nIndex,mLevel);
+ if (aTmpRule1==aTmpRule2) _pSet->bIsCustomized=false;
+ if (!_pSet->bIsCustomized) {
+ _pSet->sDescription = GetDescription(nIndex,true);
+ }
+ ImplStore(u"standard.syb");
+}
+
+void NumberingTypeMgr::ApplyNumRule(SvxNumRule& aNum, sal_uInt16 nIndex, sal_uInt16 mLevel, bool isDefault, bool isResetSize)
+{
+ if(maNumberSettingsArr.size() <= nIndex)
+ return;
+ NumberSettingsArr_Impl* pCurrentNumberSettingsArr = &maNumberSettingsArr;
+ if (isDefault) pCurrentNumberSettingsArr = &maDefaultNumberSettingsArr;
+ NumberSettings_Impl* _pSet = (*pCurrentNumberSettingsArr)[nIndex].get();
+ SvxNumType eNewType = _pSet->pNumSetting->nNumberType;
+
+ sal_uInt16 nMask = 1;
+ OUString sNumCharFmtName = GetNumCharFmtName();
+ for(sal_uInt16 i = 0; i < aNum.GetLevelCount(); i++)
+ {
+ if(mLevel & nMask)
+ {
+ SvxNumberFormat aFmt(aNum.GetLevel(i));
+ if (eNewType!=aFmt.GetNumberingType()) isResetSize=true;
+ aFmt.SetNumberingType(eNewType);
+ aFmt.SetListFormat(_pSet->pNumSetting->sPrefix, _pSet->pNumSetting->sSuffix, i);
+ aFmt.SetCharFormatName(sNumCharFmtName);
+ if (isResetSize) aFmt.SetBulletRelSize(100);
+ aNum.SetLevel(i, aFmt);
+ }
+ nMask <<= 1 ;
+ }
+}
+
+OUString NumberingTypeMgr::GetDescription(sal_uInt16 nIndex, bool isDefault)
+{
+ OUString sRet;
+ sal_uInt16 nLength = maNumberSettingsArr.size();
+
+ if ( nIndex >= nLength )
+ return sRet;
+ else
+ sRet = maNumberSettingsArr[nIndex]->sDescription;
+ if (isDefault) sRet = maDefaultNumberSettingsArr[nIndex]->sDescription;
+
+ return sRet;
+}
+
+bool NumberingTypeMgr::IsCustomized(sal_uInt16 nIndex)
+{
+ bool bRet = false;
+ sal_uInt16 nLength = maNumberSettingsArr.size();
+
+ if ( nIndex >= nLength )
+ bRet = false;
+ else
+ bRet = maNumberSettingsArr[nIndex]->bIsCustomized;
+
+ return bRet;
+}
+// Multi-level /Outline Type lib
+OutlineTypeMgr::OutlineTypeMgr()
+{
+ Init();
+ for(sal_Int32 nItem = 0; nItem < DEFAULT_NUM_VALUSET_COUNT; nItem++ )
+ {
+ pDefaultOutlineSettingsArrs[nItem] = pOutlineSettingsArrs[nItem];
+ }
+ //Initial the first time to store the default value. Then do it again for customized value
+ Init();
+ ImplLoad(u"standard.syc");
+}
+
+OutlineTypeMgr& OutlineTypeMgr::GetInstance()
+{
+ static OutlineTypeMgr theOutlineTypeMgr;
+ return theOutlineTypeMgr;
+}
+
+void OutlineTypeMgr::Init()
+{
+ Reference< XComponentContext > xContext = ::comphelper::getProcessComponentContext();
+ Reference<XDefaultNumberingProvider> xDefNum = DefaultNumberingProvider::create( xContext );
+
+ Sequence<Reference<XIndexAccess> > aOutlineAccess;
+ Locale aLocale(Application::GetSettings().GetLanguageTag().getLocale());
+ try
+ {
+ aOutlineAccess = xDefNum->getDefaultOutlineNumberings( aLocale );
+
+ SvxNumRule aDefNumRule( SvxNumRuleFlags::BULLET_REL_SIZE | SvxNumRuleFlags::CONTINUOUS | SvxNumRuleFlags::BULLET_COLOR,
+ 10, false,
+ SvxNumRuleType::NUMBERING, SvxNumberFormat::LABEL_ALIGNMENT);
+
+ auto nSize = std::min<sal_Int32>(aOutlineAccess.getLength(), DEFAULT_NUM_VALUSET_COUNT);
+ for(sal_Int32 nItem = 0; nItem < nSize; nItem++ )
+ {
+ pOutlineSettingsArrs[ nItem ] = new OutlineSettings_Impl;
+ OutlineSettings_Impl* pItemArr = pOutlineSettingsArrs[ nItem ];
+ OString id = OString::Concat(RID_SVXSTR_OUTLINENUM_DESCRIPTION_0.mpId) + OString::number(nItem);
+ pItemArr->sDescription = SvxResId( TranslateId(RID_SVXSTR_OUTLINENUM_DESCRIPTION_0.mpContext, id.getStr()) );
+ pItemArr->pNumSettingsArr = new NumSettingsArr_Impl;
+ Reference<XIndexAccess> xLevel = aOutlineAccess.getConstArray()[nItem];
+ for(sal_Int32 nLevel = 0; nLevel < xLevel->getCount() && nLevel < 5; nLevel++)
+ {
+ Any aValueAny = xLevel->getByIndex(nLevel);
+ Sequence<PropertyValue> aLevelProps;
+ aValueAny >>= aLevelProps;
+ NumSettings_Impl* pNew = lcl_CreateNumberingSettingsPtr(aLevelProps);
+ const SvxNumberFormat& aNumFmt( aDefNumRule.GetLevel( nLevel) );
+ pNew->eLabelFollowedBy = aNumFmt.GetLabelFollowedBy();
+ pNew->nTabValue = aNumFmt.GetListtabPos();
+ pNew->eNumAlign = aNumFmt.GetNumAdjust();
+ pNew->nNumAlignAt = aNumFmt.GetFirstLineIndent();
+ pNew->nNumIndentAt = aNumFmt.GetIndentAt();
+ pItemArr->pNumSettingsArr->push_back(std::shared_ptr<NumSettings_Impl>(pNew));
+ }
+ }
+ }
+ catch(Exception&)
+ {
+ }
+}
+
+sal_uInt16 OutlineTypeMgr::GetNBOIndexForNumRule(SvxNumRule& aNum,sal_uInt16 /*mLevel*/,sal_uInt16 nFromIndex)
+{
+ sal_uInt16 const nLength = SAL_N_ELEMENTS(pOutlineSettingsArrs);
+ for(sal_uInt16 iDex = nFromIndex; iDex < nLength; iDex++)
+ {
+ bool bNotMatch = false;
+ OutlineSettings_Impl* pItemArr = pOutlineSettingsArrs[iDex];
+ sal_uInt16 nCount = pItemArr->pNumSettingsArr->size();
+ for (sal_uInt16 iLevel=0;iLevel < nCount;iLevel++)
+ {
+ NumSettings_Impl* _pSet = (*pItemArr->pNumSettingsArr)[iLevel].get();
+ sal_Int16 eNType = _pSet->nNumberType;
+
+ const SvxNumberFormat& aFmt(aNum.GetLevel(iLevel));
+ const OUString& sPrefix = aFmt.GetPrefix();
+ const OUString& sLclSuffix = aFmt.GetSuffix();
+ sal_Int16 eNumType = aFmt.GetNumberingType();
+ if( eNumType == SVX_NUM_CHAR_SPECIAL)
+ {
+ sal_UCS4 cChar = aFmt.GetBulletChar();
+
+ sal_UCS4 ccChar
+ = _pSet->sBulletChar.iterateCodePoints(&o3tl::temporary(sal_Int32(0)));
+
+ if ( !((cChar == ccChar) &&
+ _pSet->eLabelFollowedBy == aFmt.GetLabelFollowedBy() &&
+ _pSet->nTabValue == aFmt.GetListtabPos() &&
+ _pSet->eNumAlign == aFmt.GetNumAdjust() &&
+ _pSet->nNumAlignAt == aFmt.GetFirstLineIndent() &&
+ _pSet->nNumIndentAt == aFmt.GetIndentAt()))
+ {
+ bNotMatch = true;
+ break;
+ }
+ }
+ else if ((eNumType&(~LINK_TOKEN)) == SVX_NUM_BITMAP )
+ {
+ const SvxBrushItem* pBrsh1 = aFmt.GetBrush();
+ const SvxBrushItem* pBrsh2 = _pSet->pBrushItem;
+ bool bIsMatch = false;
+ if (pBrsh1==pBrsh2) bIsMatch = true;
+ if (pBrsh1 && pBrsh2) {
+ const Graphic* pGrf1 = pBrsh1->GetGraphic();
+ const Graphic* pGrf2 = pBrsh2->GetGraphic();
+ if (pGrf1==pGrf2) bIsMatch = true;
+ if (pGrf1 && pGrf2) {
+ if ( pGrf1->GetBitmapEx() == pGrf2->GetBitmapEx() &&
+ _pSet->aSize == aFmt.GetGraphicSize())
+ bIsMatch = true;
+ }
+ }
+ if (!bIsMatch) {
+ bNotMatch = true;
+ break;
+ }
+ }
+ else
+ {
+ if (!(sPrefix == _pSet->sPrefix &&
+ sLclSuffix == _pSet->sSuffix &&
+ eNumType == eNType &&
+ _pSet->eLabelFollowedBy == aFmt.GetLabelFollowedBy() &&
+ _pSet->nTabValue == aFmt.GetListtabPos() &&
+ _pSet->eNumAlign == aFmt.GetNumAdjust() &&
+ _pSet->nNumAlignAt == aFmt.GetFirstLineIndent() &&
+ _pSet->nNumIndentAt == aFmt.GetIndentAt()))
+ {
+ bNotMatch = true;
+ break;
+ }
+ }
+ }
+ if ( !bNotMatch )
+ return iDex+1;
+ }
+
+
+ return sal_uInt16(0xFFFF);
+}
+
+void OutlineTypeMgr::RelplaceNumRule(SvxNumRule& aNum, sal_uInt16 nIndex, sal_uInt16 mLevel)
+{
+ sal_uInt16 const nLength = SAL_N_ELEMENTS(pOutlineSettingsArrs);
+ if ( nIndex >= nLength )
+ return;
+
+ OutlineSettings_Impl* pItemArr = pOutlineSettingsArrs[nIndex];
+ sal_uInt16 nCount = pItemArr->pNumSettingsArr->size();
+ for (sal_uInt16 iLevel=0;iLevel < nCount;iLevel++)
+ {
+ const SvxNumberFormat& aFmt(aNum.GetLevel(iLevel));
+ SvxNumType eNumType = aFmt.GetNumberingType();
+
+ NumSettings_Impl* _pSet = (*pItemArr->pNumSettingsArr)[iLevel].get();
+
+ _pSet->eLabelFollowedBy = aFmt.GetLabelFollowedBy();
+ _pSet->nTabValue = aFmt.GetListtabPos();
+ _pSet->eNumAlign = aFmt.GetNumAdjust();
+ _pSet->nNumAlignAt = aFmt.GetFirstLineIndent();
+ _pSet->nNumIndentAt = aFmt.GetIndentAt();
+
+ if( eNumType == SVX_NUM_CHAR_SPECIAL)
+ {
+ sal_UCS4 cChar = aFmt.GetBulletChar();
+ OUString sChar(&cChar, 1);
+ _pSet->sBulletChar = sChar;
+ if ( aFmt.GetBulletFont() )
+ _pSet->sBulletFont = aFmt.GetBulletFont()->GetFamilyName();
+ _pSet->nNumberType = eNumType;
+ pItemArr->bIsCustomized = true;
+ }else if ((eNumType&(~LINK_TOKEN)) == SVX_NUM_BITMAP ) {
+ if (_pSet->pBrushItem) {
+ delete _pSet->pBrushItem;
+ _pSet->pBrushItem=nullptr;
+ }
+ if (aFmt.GetBrush())
+ _pSet->pBrushItem = new SvxBrushItem(*aFmt.GetBrush());
+ _pSet->aSize = aFmt.GetGraphicSize();
+ _pSet->nNumberType = eNumType;
+ } else
+ {
+ _pSet->sPrefix = aFmt.GetPrefix();
+ _pSet->sSuffix = aFmt.GetSuffix();
+ _pSet->nNumberType = eNumType;
+ if ( aFmt.GetBulletFont() )
+ _pSet->sBulletFont = aFmt.GetBulletFont()->GetFamilyName();
+ pItemArr->bIsCustomized = true;
+ }
+ }
+ SvxNumRule aTmpRule1(aNum);
+ SvxNumRule aTmpRule2(aNum);
+ ApplyNumRule(aTmpRule1,nIndex,mLevel,true);
+ ApplyNumRule(aTmpRule2,nIndex,mLevel);
+ if (aTmpRule1==aTmpRule2) pItemArr->bIsCustomized=false;
+ if (!pItemArr->bIsCustomized) {
+ pItemArr->sDescription = GetDescription(nIndex,true);
+ }
+ ImplStore(u"standard.syc");
+}
+
+void OutlineTypeMgr::ApplyNumRule(SvxNumRule& aNum, sal_uInt16 nIndex, sal_uInt16 /*mLevel*/, bool isDefault, bool isResetSize)
+{
+ DBG_ASSERT(DEFAULT_NUM_VALUSET_COUNT > nIndex, "wrong index");
+ if(DEFAULT_NUM_VALUSET_COUNT <= nIndex)
+ return;
+
+ const FontList* pList = nullptr;
+
+ OutlineSettings_Impl* pItemArr = pOutlineSettingsArrs[nIndex];
+ if (isDefault) pItemArr=pDefaultOutlineSettingsArrs[nIndex];
+
+ NumSettingsArr_Impl *pNumSettingsArr=pItemArr->pNumSettingsArr;
+
+ NumSettings_Impl* pLevelSettings = nullptr;
+ for(sal_uInt16 i = 0; i < aNum.GetLevelCount(); i++)
+ {
+ if(pNumSettingsArr->size() > i)
+ pLevelSettings = (*pNumSettingsArr)[i].get();
+
+ if(!pLevelSettings)
+ break;
+
+ SvxNumberFormat aFmt(aNum.GetLevel(i));
+ const vcl::Font& rActBulletFont = lcl_GetDefaultBulletFont();
+ if (pLevelSettings->nNumberType !=aFmt.GetNumberingType()) isResetSize=true;
+ aFmt.SetNumberingType( pLevelSettings->nNumberType );
+ sal_uInt16 nUpperLevelOrChar = static_cast<sal_uInt16>(pLevelSettings->nParentNumbering);
+ if(aFmt.GetNumberingType() == SVX_NUM_CHAR_SPECIAL)
+ {
+ if( pLevelSettings->sBulletFont.getLength() &&
+ pLevelSettings->sBulletFont != rActBulletFont.GetFamilyName() )
+ {
+ //search for the font
+ if(!pList)
+ {
+ if (SfxObjectShell* pCurDocShell = SfxObjectShell::Current())
+ {
+ const SvxFontListItem* pFontListItem = static_cast<const SvxFontListItem*>(pCurDocShell->GetItem(SID_ATTR_CHAR_FONTLIST));
+ pList = pFontListItem ? pFontListItem->GetFontList() : nullptr;
+ }
+ }
+ if(pList && pList->IsAvailable( pLevelSettings->sBulletFont ) )
+ {
+ FontMetric aFontMetric = pList->Get(pLevelSettings->sBulletFont,WEIGHT_NORMAL, ITALIC_NONE);
+ vcl::Font aFont(aFontMetric);
+ aFmt.SetBulletFont(&aFont);
+ }
+ else
+ {
+ //if it cannot be found then create a new one
+ vcl::Font aCreateFont( pLevelSettings->sBulletFont, OUString(), Size( 0, 14 ) );
+ aCreateFont.SetCharSet( RTL_TEXTENCODING_DONTKNOW );
+ aCreateFont.SetFamily( FAMILY_DONTKNOW );
+ aCreateFont.SetPitch( PITCH_DONTKNOW );
+ aCreateFont.SetWeight( WEIGHT_DONTKNOW );
+ aCreateFont.SetTransparent( true );
+ aFmt.SetBulletFont( &aCreateFont );
+ }
+ }else
+ aFmt.SetBulletFont( &rActBulletFont );
+
+ sal_UCS4 cChar = 0;
+ if( !pLevelSettings->sBulletChar.isEmpty() )
+ {
+ cChar
+ = pLevelSettings->sBulletChar.iterateCodePoints(&o3tl::temporary(sal_Int32(0)));
+ }
+ if( AllSettings::GetLayoutRTL() )
+ {
+ if( 0 == i && cChar == BulletsTypeMgr::aDynamicBulletTypes[5] )
+ cChar = BulletsTypeMgr::aDynamicRTLBulletTypes[5];
+ else if( 1 == i )
+ {
+ const SvxNumberFormat& numberFmt = aNum.GetLevel(0);
+ if( numberFmt.GetBulletChar() == BulletsTypeMgr::aDynamicRTLBulletTypes[5] )
+ cChar = BulletsTypeMgr::aDynamicRTLBulletTypes[4];
+ }
+ }
+
+ aFmt.SetBulletChar(cChar);
+ aFmt.SetCharFormatName( GetBulletCharFmtName() );
+ if (isResetSize) aFmt.SetBulletRelSize(45);
+ }else if ((aFmt.GetNumberingType()&(~LINK_TOKEN)) == SVX_NUM_BITMAP ) {
+ if (pLevelSettings->pBrushItem) {
+ const Graphic* pGrf = pLevelSettings->pBrushItem->GetGraphic();
+ Size aSize = pLevelSettings->aSize;
+ sal_Int16 eOrient = text::VertOrientation::LINE_CENTER;
+ if (!isResetSize && aFmt.GetGraphicSize()!=Size(0,0))
+ aSize = aFmt.GetGraphicSize();
+ else if (aSize.IsEmpty() && pGrf)
+ aSize = SvxNumberFormat::GetGraphicSizeMM100( pGrf );
+ aSize = OutputDevice::LogicToLogic(aSize, MapMode(MapUnit::Map100thMM), MapMode(GetMapUnit()));
+ aFmt.SetGraphicBrush( pLevelSettings->pBrushItem, &aSize, &eOrient );
+ }
+ } else
+ {
+ aFmt.SetIncludeUpperLevels(sal::static_int_cast< sal_uInt8 >(0 != nUpperLevelOrChar ? aNum.GetLevelCount() : 1));
+ aFmt.SetCharFormatName(GetNumCharFmtName());
+ if (isResetSize) aFmt.SetBulletRelSize(100);
+ }
+ if(pNumSettingsArr->size() > i) {
+ aFmt.SetLabelFollowedBy(pLevelSettings->eLabelFollowedBy);
+ aFmt.SetListtabPos(pLevelSettings->nTabValue);
+ aFmt.SetNumAdjust(pLevelSettings->eNumAlign);
+ aFmt.SetFirstLineIndent(pLevelSettings->nNumAlignAt);
+ aFmt.SetIndentAt(pLevelSettings->nNumIndentAt);
+ }
+ aFmt.SetListFormat(pLevelSettings->sPrefix, pLevelSettings->sSuffix, i);
+ aNum.SetLevel(i, aFmt);
+ }
+}
+
+OUString OutlineTypeMgr::GetDescription(sal_uInt16 nIndex, bool isDefault)
+{
+ OUString sRet;
+
+ if ( nIndex >= SAL_N_ELEMENTS(pOutlineSettingsArrs) )
+ return sRet;
+ else
+ {
+ OutlineSettings_Impl* pItemArr = pOutlineSettingsArrs[nIndex];
+ if (isDefault) pItemArr = pDefaultOutlineSettingsArrs[nIndex];
+ if ( pItemArr )
+ {
+ sRet = pItemArr->sDescription;
+ }
+ }
+ return sRet;
+}
+
+bool OutlineTypeMgr::IsCustomized(sal_uInt16 nIndex)
+{
+ bool bRet = false;
+
+ if ( nIndex >= SAL_N_ELEMENTS(pOutlineSettingsArrs) )
+ return bRet;
+ else
+ {
+ OutlineSettings_Impl* pItemArr = pOutlineSettingsArrs[nIndex];
+ if ( pItemArr )
+ {
+ bRet = pItemArr->bIsCustomized;
+ }
+ }
+
+ return bRet;
+}
+
+
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/sidebar/nbdtmgfact.cxx b/svx/source/sidebar/nbdtmgfact.cxx
new file mode 100644
index 000000000..14dcd64c4
--- /dev/null
+++ b/svx/source/sidebar/nbdtmgfact.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 <svx/nbdtmgfact.hxx>
+
+namespace svx::sidebar::NBOutlineTypeMgrFact
+{
+NBOTypeMgrBase* CreateInstance(const NBOType aType)
+{
+ if (aType == NBOType::Bullets)
+ {
+ return &BulletsTypeMgr::GetInstance();
+ }
+ else if (aType == NBOType::Numbering)
+ {
+ return &NumberingTypeMgr::GetInstance();
+ }
+ else if (aType == NBOType::Outline)
+ {
+ return &OutlineTypeMgr::GetInstance();
+ }
+ return nullptr;
+}
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/sidebar/paragraph/ParaLineSpacingControl.cxx b/svx/source/sidebar/paragraph/ParaLineSpacingControl.cxx
new file mode 100644
index 000000000..fee659817
--- /dev/null
+++ b/svx/source/sidebar/paragraph/ParaLineSpacingControl.cxx
@@ -0,0 +1,439 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include "ParaLineSpacingControl.hxx"
+
+#include <editeng/editids.hrc>
+#include <editeng/lspcitem.hxx>
+#include <sfx2/dispatch.hxx>
+#include <sfx2/module.hxx>
+#include <sfx2/sfxsids.hrc>
+#include <sfx2/viewfrm.hxx>
+#include <svtools/unitconv.hxx>
+
+#include <svl/intitem.hxx>
+#include <svl/itempool.hxx>
+
+#include <ParaLineSpacingPopup.hxx>
+
+#define DEFAULT_LINE_SPACING 200
+#define FIX_DIST_DEF 283
+#define LINESPACE_1 100
+#define LINESPACE_15 150
+#define LINESPACE_2 200
+#define LINESPACE_115 115
+
+// values of the mxLineDist listbox
+#define LLINESPACE_1 0
+#define LLINESPACE_115 1
+#define LLINESPACE_15 2
+#define LLINESPACE_2 3
+#define LLINESPACE_PROP 4
+#define LLINESPACE_MIN 5
+#define LLINESPACE_DURCH 6
+#define LLINESPACE_FIX 7
+
+#define MIN_FIXED_DISTANCE 28
+
+using namespace svx;
+
+ParaLineSpacingControl::ParaLineSpacingControl(SvxLineSpacingToolBoxControl* pControl, weld::Widget* pParent)
+ : WeldToolbarPopup(pControl->getFrameInterface(), pParent, "svx/ui/paralinespacingcontrol.ui", "ParaLineSpacingControl")
+ , mxControl(pControl)
+ , meLNSpaceUnit(MapUnit::Map100thMM)
+ , mxSpacing1Button(m_xBuilder->weld_button("spacing_1"))
+ , mxSpacing115Button(m_xBuilder->weld_button("spacing_115"))
+ , mxSpacing15Button(m_xBuilder->weld_button("spacing_15"))
+ , mxSpacing2Button(m_xBuilder->weld_button("spacing_2"))
+ , mxLineDist(m_xBuilder->weld_combo_box("line_dist"))
+ , mxLineDistLabel(m_xBuilder->weld_label("value_label"))
+ , mxLineDistAtPercentBox(m_xBuilder->weld_metric_spin_button("percent_box", FieldUnit::PERCENT))
+ , mxLineDistAtMetricBox(m_xBuilder->weld_metric_spin_button("metric_box", FieldUnit::CM))
+ , mpActLineDistFld(mxLineDistAtPercentBox.get())
+{
+ Link<weld::Button&,void> aLink = LINK(this, ParaLineSpacingControl, PredefinedValuesHandler);
+ mxSpacing1Button->connect_clicked(aLink);
+ mxSpacing115Button->connect_clicked(aLink);
+ mxSpacing15Button->connect_clicked(aLink);
+ mxSpacing2Button->connect_clicked(aLink);
+
+ Link<weld::ComboBox&,void> aLink3 = LINK( this, ParaLineSpacingControl, LineSPDistHdl_Impl );
+ mxLineDist->connect_changed(aLink3);
+ SelectEntryPos(LLINESPACE_1);
+
+ Link<weld::MetricSpinButton&,void> aLink2 = LINK( this, ParaLineSpacingControl, LineSPDistAtHdl_Impl );
+ mxLineDistAtPercentBox->connect_value_changed( aLink2 );
+ mxLineDistAtMetricBox->connect_value_changed( aLink2 );
+
+ FieldUnit eUnit = FieldUnit::INCH;
+ const SfxUInt16Item* pItem = nullptr;
+ SfxViewFrame* pCurrent = SfxViewFrame::Current();
+ if (pCurrent && pCurrent->GetBindings().GetDispatcher()->QueryState(SID_ATTR_METRIC, pItem) >= SfxItemState::DEFAULT)
+ eUnit = static_cast<FieldUnit>(pItem->GetValue());
+ else
+ eUnit = SfxModule::GetCurrentFieldUnit();
+
+ SetFieldUnit(*mxLineDistAtMetricBox, eUnit);
+
+ Initialize();
+}
+
+void ParaLineSpacingControl::GrabFocus()
+{
+ switch (mxLineDist->get_active())
+ {
+ case LLINESPACE_1:
+ mxSpacing1Button->grab_focus();
+ break;
+ case LLINESPACE_115:
+ mxSpacing115Button->grab_focus();
+ break;
+ case LLINESPACE_15:
+ mxSpacing15Button->grab_focus();
+ break;
+ case LLINESPACE_2:
+ mxSpacing2Button->grab_focus();
+ break;
+ default:
+ mxLineDist->grab_focus();
+ break;
+ }
+}
+
+ParaLineSpacingControl::~ParaLineSpacingControl()
+{
+}
+
+void ParaLineSpacingControl::Initialize()
+{
+ const SvxLineSpacingItem* pItem(nullptr);
+ SfxViewFrame* pCurrent = SfxViewFrame::Current();
+ const bool bItemStateSet(nullptr != pCurrent);
+ const SfxItemState eState(bItemStateSet
+ ? pCurrent->GetBindings().GetDispatcher()->QueryState(SID_ATTR_PARA_LINESPACE, pItem)
+ : SfxItemState::DEFAULT);
+
+ mxLineDist->set_sensitive(true);
+
+ if( bItemStateSet && (eState == SfxItemState::DEFAULT || eState == SfxItemState::SET) )
+ {
+ const SvxLineSpacingItem* currSPItem = pItem;
+ MapUnit eUnit = pCurrent->GetPool().GetMetric(currSPItem->Which());
+ meLNSpaceUnit = eUnit;
+
+ switch( currSPItem->GetLineSpaceRule() )
+ {
+ case SvxLineSpaceRule::Auto:
+ {
+ SvxInterLineSpaceRule eInter = currSPItem->GetInterLineSpaceRule();
+
+ switch( eInter )
+ {
+ case SvxInterLineSpaceRule::Off:
+ SelectEntryPos(LLINESPACE_1);
+ break;
+
+ case SvxInterLineSpaceRule::Prop:
+ {
+ if ( LINESPACE_1 == currSPItem->GetPropLineSpace() )
+ {
+ SelectEntryPos(LLINESPACE_1);
+ }
+ else if ( LINESPACE_115 == currSPItem->GetPropLineSpace() )
+ {
+ SelectEntryPos(LLINESPACE_115);
+ }
+ else if ( LINESPACE_15 == currSPItem->GetPropLineSpace() )
+ {
+ SelectEntryPos(LLINESPACE_15);
+ }
+ else if ( LINESPACE_2 == currSPItem->GetPropLineSpace() )
+ {
+ SelectEntryPos(LLINESPACE_2);
+ }
+ else
+ {
+ SelectEntryPos(LLINESPACE_PROP);
+ mxLineDistAtPercentBox->set_value(mxLineDistAtPercentBox->normalize(currSPItem->GetPropLineSpace()), FieldUnit::PERCENT);
+ }
+ }
+ break;
+
+ case SvxInterLineSpaceRule::Fix:
+ {
+ SelectEntryPos(LLINESPACE_DURCH);
+ SetMetricValue(*mxLineDistAtMetricBox, currSPItem->GetInterLineSpace(), eUnit);
+ }
+ break;
+ default:
+ break;
+ }
+ }
+ break;
+ case SvxLineSpaceRule::Fix:
+ {
+ SelectEntryPos(LLINESPACE_FIX);
+ SetMetricValue(*mxLineDistAtMetricBox, currSPItem->GetLineHeight(), eUnit);
+ }
+ break;
+
+ case SvxLineSpaceRule::Min:
+ {
+ SelectEntryPos(LLINESPACE_MIN);
+ SetMetricValue(*mxLineDistAtMetricBox, currSPItem->GetLineHeight(), eUnit);
+ }
+ break;
+ default:
+ break;
+ }
+ }
+ else if( bItemStateSet && eState == SfxItemState::DISABLED )
+ {
+ mxLineDist->set_sensitive(false);
+ mxLineDistLabel->set_sensitive(false);
+ mpActLineDistFld->set_sensitive(false);
+ mpActLineDistFld->set_text("");
+
+ }
+ else // !bItemStateSet || eState == SfxItemState::DONTCARE || eState == SfxItemState::UNKNOWN
+ {
+ mxLineDistLabel->set_sensitive(false);
+ mpActLineDistFld->set_sensitive(false);
+ mpActLineDistFld->set_text("");
+ mxLineDist->set_active(-1);
+ }
+
+ mxLineDist->save_value();
+}
+
+void ParaLineSpacingControl::UpdateMetricFields()
+{
+ switch (mxLineDist->get_active())
+ {
+ case LLINESPACE_1:
+ case LLINESPACE_115:
+ case LLINESPACE_15:
+ case LLINESPACE_2:
+ if (mpActLineDistFld == mxLineDistAtPercentBox.get())
+ mxLineDistAtMetricBox->hide();
+ else
+ mxLineDistAtPercentBox->hide();
+
+ mxLineDistLabel->set_sensitive(false);
+ mpActLineDistFld->show();
+ mpActLineDistFld->set_sensitive(false);
+ mpActLineDistFld->set_text("");
+ break;
+
+ case LLINESPACE_DURCH:
+ mxLineDistAtPercentBox->hide();
+
+ mpActLineDistFld = mxLineDistAtMetricBox.get();
+ mxLineDistAtMetricBox->set_min(0, FieldUnit::NONE);
+
+ if (mxLineDistAtMetricBox->get_text().isEmpty())
+ mxLineDistAtMetricBox->set_value(mxLineDistAtMetricBox->normalize(0), FieldUnit::NONE);
+
+ mxLineDistLabel->set_sensitive(true);
+ mpActLineDistFld->show();
+ mpActLineDistFld->set_sensitive(true);
+ break;
+
+ case LLINESPACE_MIN:
+ mxLineDistAtPercentBox->hide();
+
+ mpActLineDistFld = mxLineDistAtMetricBox.get();
+ mxLineDistAtMetricBox->set_min(0, FieldUnit::NONE);
+
+ if (mxLineDistAtMetricBox->get_text().isEmpty())
+ mxLineDistAtMetricBox->set_value(mxLineDistAtMetricBox->normalize(0), FieldUnit::TWIP);
+
+ mxLineDistLabel->set_sensitive(true);
+ mpActLineDistFld->show();
+ mpActLineDistFld->set_sensitive(true);
+ break;
+
+ case LLINESPACE_PROP:
+ mxLineDistAtMetricBox->hide();
+
+ mpActLineDistFld = mxLineDistAtPercentBox.get();
+
+ if (mxLineDistAtPercentBox->get_text().isEmpty())
+ mxLineDistAtPercentBox->set_value(mxLineDistAtPercentBox->normalize(100), FieldUnit::TWIP);
+
+ mxLineDistLabel->set_sensitive(true);
+ mpActLineDistFld->show();
+ mpActLineDistFld->set_sensitive(true);
+ break;
+
+ case LLINESPACE_FIX:
+ mxLineDistAtPercentBox->hide();
+
+ mpActLineDistFld = mxLineDistAtMetricBox.get();
+ sal_Int64 nTemp = mxLineDistAtMetricBox->get_value(FieldUnit::NONE);
+ mxLineDistAtMetricBox->set_min(mxLineDistAtMetricBox->normalize(MIN_FIXED_DISTANCE), FieldUnit::TWIP);
+
+ if (mxLineDistAtMetricBox->get_value(FieldUnit::NONE) != nTemp)
+ SetMetricValue(*mxLineDistAtMetricBox, FIX_DIST_DEF, MapUnit::MapTwip);
+
+ mxLineDistLabel->set_sensitive(true);
+ mpActLineDistFld->show();
+ mpActLineDistFld->set_sensitive(true);
+ break;
+ }
+}
+
+void ParaLineSpacingControl::SelectEntryPos(sal_Int32 nPos)
+{
+ mxLineDist->set_active(nPos);
+ UpdateMetricFields();
+}
+
+IMPL_LINK_NOARG(ParaLineSpacingControl, LineSPDistHdl_Impl, weld::ComboBox&, void)
+{
+ UpdateMetricFields();
+ ExecuteLineSpace();
+}
+
+IMPL_LINK_NOARG( ParaLineSpacingControl, LineSPDistAtHdl_Impl, weld::MetricSpinButton&, void )
+{
+ ExecuteLineSpace();
+}
+
+void ParaLineSpacingControl::ExecuteLineSpace()
+{
+ mxLineDist->save_value();
+
+ SvxLineSpacingItem aSpacing(DEFAULT_LINE_SPACING, SID_ATTR_PARA_LINESPACE);
+ const sal_Int32 nPos = mxLineDist->get_active();
+
+ switch ( nPos )
+ {
+ case LLINESPACE_1:
+ case LLINESPACE_115:
+ case LLINESPACE_15:
+ case LLINESPACE_2:
+ SetLineSpace(aSpacing, nPos);
+ break;
+
+ case LLINESPACE_PROP:
+ SetLineSpace(aSpacing, nPos, mxLineDistAtPercentBox->denormalize(static_cast<tools::Long>(mxLineDistAtPercentBox->get_value(FieldUnit::PERCENT))));
+ break;
+
+ case LLINESPACE_MIN:
+ case LLINESPACE_DURCH:
+ case LLINESPACE_FIX:
+ SetLineSpace(aSpacing, nPos, GetCoreValue(*mxLineDistAtMetricBox, meLNSpaceUnit));
+ break;
+
+ default:
+ break;
+ }
+
+ if (SfxViewFrame* pViewFrm = SfxViewFrame::Current())
+ {
+ pViewFrm->GetBindings().GetDispatcher()->ExecuteList(
+ SID_ATTR_PARA_LINESPACE, SfxCallMode::RECORD, { &aSpacing });
+ }
+}
+
+void ParaLineSpacingControl::SetLineSpace(SvxLineSpacingItem& rLineSpace, sal_Int32 eSpace, tools::Long lValue)
+{
+ switch ( eSpace )
+ {
+ case LLINESPACE_1:
+ rLineSpace.SetLineSpaceRule( SvxLineSpaceRule::Auto );
+ rLineSpace.SetInterLineSpaceRule( SvxInterLineSpaceRule::Off );
+ break;
+
+ case LLINESPACE_115:
+ rLineSpace.SetLineSpaceRule( SvxLineSpaceRule::Auto );
+ rLineSpace.SetPropLineSpace( LINESPACE_115 );
+ break;
+
+ case LLINESPACE_15:
+ rLineSpace.SetLineSpaceRule( SvxLineSpaceRule::Auto );
+ rLineSpace.SetPropLineSpace( LINESPACE_15 );
+ break;
+
+ case LLINESPACE_2:
+ rLineSpace.SetLineSpaceRule( SvxLineSpaceRule::Auto );
+ rLineSpace.SetPropLineSpace( LINESPACE_2 );
+ break;
+
+ case LLINESPACE_PROP:
+ rLineSpace.SetLineSpaceRule( SvxLineSpaceRule::Auto );
+ rLineSpace.SetPropLineSpace( static_cast<sal_uInt16>(lValue) );
+ break;
+
+ case LLINESPACE_MIN:
+ rLineSpace.SetLineHeight( static_cast<sal_uInt16>(lValue) );
+ rLineSpace.SetInterLineSpaceRule( SvxInterLineSpaceRule::Off );
+ break;
+
+ case LLINESPACE_DURCH:
+ rLineSpace.SetLineSpaceRule( SvxLineSpaceRule::Auto );
+ rLineSpace.SetInterLineSpace( static_cast<sal_uInt16>(lValue) );
+ break;
+
+ case LLINESPACE_FIX:
+ rLineSpace.SetLineHeight(static_cast<sal_uInt16>(lValue));
+ rLineSpace.SetLineSpaceRule( SvxLineSpaceRule::Fix );
+ rLineSpace.SetInterLineSpaceRule( SvxInterLineSpaceRule::Off );
+ break;
+ }
+}
+
+IMPL_LINK(ParaLineSpacingControl, PredefinedValuesHandler, weld::Button&, rControl, void)
+{
+ if (&rControl == mxSpacing1Button.get())
+ {
+ ExecuteLineSpacing(LLINESPACE_1);
+ }
+ else if (&rControl == mxSpacing115Button.get())
+ {
+ ExecuteLineSpacing(LLINESPACE_115);
+ }
+ else if (&rControl == mxSpacing15Button.get())
+ {
+ ExecuteLineSpacing(LLINESPACE_15);
+ }
+ else if (&rControl == mxSpacing2Button.get())
+ {
+ ExecuteLineSpacing(LLINESPACE_2);
+ }
+}
+
+void ParaLineSpacingControl::ExecuteLineSpacing(sal_Int32 nEntry)
+{
+ SvxLineSpacingItem aSpacing(DEFAULT_LINE_SPACING, SID_ATTR_PARA_LINESPACE);
+
+ SetLineSpace(aSpacing, nEntry);
+
+ if (SfxViewFrame* pViewFrm = SfxViewFrame::Current())
+ {
+ pViewFrm->GetBindings().GetDispatcher()->ExecuteList(
+ SID_ATTR_PARA_LINESPACE, SfxCallMode::RECORD, { &aSpacing });
+ }
+
+ // close when the user used the buttons
+ mxControl->EndPopupMode();
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/sidebar/paragraph/ParaLineSpacingControl.hxx b/svx/source/sidebar/paragraph/ParaLineSpacingControl.hxx
new file mode 100644
index 000000000..e49556f16
--- /dev/null
+++ b/svx/source/sidebar/paragraph/ParaLineSpacingControl.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 .
+ */
+#pragma once
+
+#include <svtools/toolbarmenu.hxx>
+
+class SvxLineSpacingItem;
+
+namespace svx
+{
+class SvxLineSpacingToolBoxControl;
+
+class ParaLineSpacingControl : public WeldToolbarPopup
+{
+public:
+ explicit ParaLineSpacingControl(SvxLineSpacingToolBoxControl* pControl, weld::Widget* pParent);
+ virtual ~ParaLineSpacingControl() override;
+
+ /// Setup the widgets with values from the document.
+ void Initialize();
+
+ virtual void GrabFocus() override;
+
+private:
+ rtl::Reference<SvxLineSpacingToolBoxControl> mxControl;
+
+ MapUnit meLNSpaceUnit;
+
+ std::unique_ptr<weld::Button> mxSpacing1Button;
+ std::unique_ptr<weld::Button> mxSpacing115Button;
+ std::unique_ptr<weld::Button> mxSpacing15Button;
+ std::unique_ptr<weld::Button> mxSpacing2Button;
+
+ std::unique_ptr<weld::ComboBox> mxLineDist;
+
+ std::unique_ptr<weld::Label> mxLineDistLabel;
+ std::unique_ptr<weld::MetricSpinButton> mxLineDistAtPercentBox;
+ std::unique_ptr<weld::MetricSpinButton> mxLineDistAtMetricBox;
+ weld::MetricSpinButton* mpActLineDistFld;
+
+private:
+ /// Take the values from the widgets, and update the paragraph accordingly.
+ void ExecuteLineSpace();
+
+ /// Set one particular value.
+ static void SetLineSpace(SvxLineSpacingItem& rLineSpace, sal_Int32 eSpace,
+ tools::Long lValue = 0);
+
+ /// For the buttons - set the values, and close the popup.
+ void ExecuteLineSpacing(sal_Int32 aEntry);
+
+ /// Set mpActlineDistFld and visibility of mpLineDist* fields according to what is just selected.
+ void UpdateMetricFields();
+
+ /// Set the entry and update the metric fields.
+ void SelectEntryPos(sal_Int32 nPos);
+
+ DECL_LINK(LineSPDistHdl_Impl, weld::ComboBox&, void);
+ DECL_LINK(LineSPDistAtHdl_Impl, weld::MetricSpinButton&, void);
+ DECL_LINK(PredefinedValuesHandler, weld::Button&, void);
+};
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/sidebar/paragraph/ParaLineSpacingPopup.cxx b/svx/source/sidebar/paragraph/ParaLineSpacingPopup.cxx
new file mode 100644
index 000000000..d8e427840
--- /dev/null
+++ b/svx/source/sidebar/paragraph/ParaLineSpacingPopup.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 "ParaLineSpacingControl.hxx"
+
+#include <ParaLineSpacingPopup.hxx>
+#include <vcl/toolbox.hxx>
+
+using namespace svx;
+
+SvxLineSpacingToolBoxControl::SvxLineSpacingToolBoxControl(
+ const css::uno::Reference<css::uno::XComponentContext>& rContext)
+ : PopupWindowController(rContext, nullptr, OUString())
+{
+}
+
+SvxLineSpacingToolBoxControl::~SvxLineSpacingToolBoxControl() {}
+
+void SvxLineSpacingToolBoxControl::initialize(const css::uno::Sequence<css::uno::Any>& rArguments)
+{
+ PopupWindowController::initialize(rArguments);
+
+ if (m_pToolbar)
+ {
+ mxPopoverContainer.reset(new ToolbarPopupContainer(m_pToolbar));
+ m_pToolbar->set_item_popover(m_aCommandURL.toUtf8(), mxPopoverContainer->getTopLevel());
+ }
+
+ ToolBox* pToolBox = nullptr;
+ ToolBoxItemId nId;
+ if (getToolboxId(nId, &pToolBox))
+ pToolBox->SetItemBits(nId, ToolBoxItemBits::DROPDOWNONLY | pToolBox->GetItemBits(nId));
+}
+
+void SAL_CALL SvxLineSpacingToolBoxControl::execute(sal_Int16 /*KeyModifier*/)
+{
+ if (m_pToolbar)
+ {
+ // Toggle the popup also when toolbutton is activated
+ const OString aId(m_aCommandURL.toUtf8());
+ m_pToolbar->set_menu_item_active(aId, !m_pToolbar->get_menu_item_active(aId));
+ }
+ else
+ {
+ // Open the popup also when Enter key is pressed.
+ createPopupWindow();
+ }
+}
+
+std::unique_ptr<WeldToolbarPopup> SvxLineSpacingToolBoxControl::weldPopupWindow()
+{
+ return std::make_unique<ParaLineSpacingControl>(this, m_pToolbar);
+}
+
+VclPtr<vcl::Window> SvxLineSpacingToolBoxControl::createVclPopupWindow(vcl::Window* pParent)
+{
+ mxInterimPopover = VclPtr<InterimToolbarPopup>::Create(
+ getFrameInterface(), pParent,
+ std::make_unique<ParaLineSpacingControl>(this, pParent->GetFrameWeld()));
+
+ mxInterimPopover->Show();
+
+ return mxInterimPopover;
+}
+
+OUString SvxLineSpacingToolBoxControl::getImplementationName()
+{
+ return "com.sun.star.comp.svx.LineSpacingToolBoxControl";
+}
+
+css::uno::Sequence<OUString> SvxLineSpacingToolBoxControl::getSupportedServiceNames()
+{
+ return { "com.sun.star.frame.ToolbarController" };
+}
+
+extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface*
+com_sun_star_comp_svx_LineSpacingToolBoxControl_get_implementation(
+ css::uno::XComponentContext* rContext, css::uno::Sequence<css::uno::Any> const&)
+{
+ return cppu::acquire(new SvxLineSpacingToolBoxControl(rContext));
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/sidebar/paragraph/ParaPropertyPanel.cxx b/svx/source/sidebar/paragraph/ParaPropertyPanel.cxx
new file mode 100644
index 000000000..f4e2fd83b
--- /dev/null
+++ b/svx/source/sidebar/paragraph/ParaPropertyPanel.cxx
@@ -0,0 +1,491 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+#include "ParaPropertyPanel.hxx"
+#include <sfx2/dispatch.hxx>
+#include <sfx2/module.hxx>
+#include <sfx2/viewfrm.hxx>
+#include <sfx2/weldutils.hxx>
+#include <editeng/lrspitem.hxx>
+#include <editeng/ulspitem.hxx>
+#include <svx/dlgctrl.hxx>
+#include <svx/svxids.hrc>
+#include <svl/intitem.hxx>
+#include <sfx2/objsh.hxx>
+#include <svtools/unitconv.hxx>
+#include <sal/log.hxx>
+
+#include <com/sun/star/lang/IllegalArgumentException.hpp>
+
+using namespace css;
+using namespace css::uno;
+
+namespace svx::sidebar {
+#define DEFAULT_VALUE 0
+
+#define MAX_DURCH 31680 // tdf#68335: 1584 pt for UX interoperability with Word
+
+#define MAX_SW 1709400
+#define MAX_SC_SD 116220200
+#define NEGA_MAXVALUE -10000000
+
+std::unique_ptr<PanelLayout> ParaPropertyPanel::Create (
+ weld::Widget* pParent,
+ const css::uno::Reference<css::frame::XFrame>& rxFrame,
+ SfxBindings* pBindings,
+ const css::uno::Reference<css::ui::XSidebar>& rxSidebar)
+{
+ if (pParent == nullptr)
+ throw lang::IllegalArgumentException("no parent Window given to ParaPropertyPanel::Create", nullptr, 0);
+ if ( ! rxFrame.is())
+ throw lang::IllegalArgumentException("no XFrame given to ParaPropertyPanel::Create", nullptr, 1);
+ if (pBindings == nullptr)
+ throw lang::IllegalArgumentException("no SfxBindings given to ParaPropertyPanel::Create", nullptr, 2);
+
+ return std::make_unique<ParaPropertyPanel>(pParent, rxFrame, pBindings, rxSidebar);
+}
+
+void ParaPropertyPanel::HandleContextChange (
+ const vcl::EnumContext& rContext)
+{
+ if (maContext == rContext)
+ {
+ // Nothing to do.
+ return;
+ }
+
+ maContext = rContext;
+ switch (maContext.GetCombinedContext_DI())
+ {
+ case CombinedEnumContext(Application::Calc, Context::DrawText):
+ case CombinedEnumContext(Application::WriterVariants, Context::DrawText):
+ mxTBxVertAlign->show();
+ mxTBxBackColor->hide();
+ mxTBxNumBullet->hide();
+ ReSize();
+ break;
+
+ case CombinedEnumContext(Application::DrawImpress, Context::Draw):
+ case CombinedEnumContext(Application::DrawImpress, Context::TextObject):
+ case CombinedEnumContext(Application::DrawImpress, Context::Graphic):
+ case CombinedEnumContext(Application::DrawImpress, Context::DrawText):
+ case CombinedEnumContext(Application::DrawImpress, Context::Table):
+ mxTBxVertAlign->show();
+ mxTBxBackColor->hide();
+ mxTBxNumBullet->hide();
+ ReSize();
+ break;
+
+ case CombinedEnumContext(Application::WriterVariants, Context::Default):
+ case CombinedEnumContext(Application::WriterVariants, Context::Text):
+ mxTBxVertAlign->hide();
+ mxTBxBackColor->show();
+ mxTBxNumBullet->show();
+ ReSize();
+ break;
+
+ case CombinedEnumContext(Application::WriterVariants, Context::Table):
+ mxTBxVertAlign->show();
+ mxTBxBackColor->show();
+ mxTBxNumBullet->show();
+ ReSize();
+ break;
+
+ case CombinedEnumContext(Application::WriterVariants, Context::Annotation):
+ mxTBxVertAlign->hide();
+ mxTBxBackColor->hide();
+ mxTBxNumBullet->hide();
+ ReSize();
+ break;
+
+ case CombinedEnumContext(Application::Calc, Context::EditCell):
+ case CombinedEnumContext(Application::Calc, Context::Cell):
+ case CombinedEnumContext(Application::Calc, Context::Pivot):
+ case CombinedEnumContext(Application::Calc, Context::Sparkline):
+ case CombinedEnumContext(Application::DrawImpress, Context::Text):
+ case CombinedEnumContext(Application::DrawImpress, Context::OutlineText):
+ break;
+
+ default:
+ break;
+ }
+}
+
+void ParaPropertyPanel::ReSize()
+{
+ if (mxSidebar.is())
+ mxSidebar->requestLayout();
+}
+
+void ParaPropertyPanel::InitToolBoxIndent()
+{
+ Link<weld::MetricSpinButton&,void> aLink = LINK( this, ParaPropertyPanel, ModifyIndentHdl_Impl );
+ mxLeftIndent->connect_value_changed( aLink );
+ mxRightIndent->connect_value_changed( aLink );
+ mxFLineIndent->connect_value_changed( aLink );
+
+ m_eLRSpaceUnit = maLRSpaceControl.GetCoreMetric();
+}
+
+void ParaPropertyPanel::InitToolBoxSpacing()
+{
+ Link<weld::MetricSpinButton&,void> aLink = LINK( this, ParaPropertyPanel, ULSpaceHdl_Impl );
+ mxTopDist->connect_value_changed(aLink);
+ mxBottomDist->connect_value_changed( aLink );
+
+ m_eULSpaceUnit = maULSpaceControl.GetCoreMetric();
+}
+
+void ParaPropertyPanel::initial()
+{
+ limitMetricWidths();
+
+ //toolbox
+ InitToolBoxIndent();
+ InitToolBoxSpacing();
+}
+
+// for Paragraph Indent
+IMPL_LINK_NOARG( ParaPropertyPanel, ModifyIndentHdl_Impl, weld::MetricSpinButton&, void)
+{
+ SvxLRSpaceItem aMargin( SID_ATTR_PARA_LRSPACE );
+ aMargin.SetTextLeft(mxLeftIndent->GetCoreValue(m_eLRSpaceUnit));
+ aMargin.SetRight(mxRightIndent->GetCoreValue(m_eLRSpaceUnit));
+ aMargin.SetTextFirstLineOffset(static_cast<short>(mxFLineIndent->GetCoreValue(m_eLRSpaceUnit)));
+
+ GetBindings()->GetDispatcher()->ExecuteList(
+ SID_ATTR_PARA_LRSPACE, SfxCallMode::RECORD, { &aMargin });
+}
+
+
+// for Paragraph Spacing
+IMPL_LINK_NOARG( ParaPropertyPanel, ULSpaceHdl_Impl, weld::MetricSpinButton&, void)
+{
+ SvxULSpaceItem aMargin( SID_ATTR_PARA_ULSPACE );
+ aMargin.SetUpper( static_cast<sal_uInt16>(mxTopDist->GetCoreValue(m_eULSpaceUnit)));
+ aMargin.SetLower( static_cast<sal_uInt16>(mxBottomDist->GetCoreValue(m_eULSpaceUnit)));
+
+ GetBindings()->GetDispatcher()->ExecuteList(
+ SID_ATTR_PARA_ULSPACE, SfxCallMode::RECORD, { &aMargin });
+}
+
+// for Paragraph State change
+void ParaPropertyPanel::NotifyItemUpdate(
+ sal_uInt16 nSID,
+ SfxItemState eState,
+ const SfxPoolItem* pState)
+{
+ switch (nSID)
+ {
+ case SID_ATTR_METRIC:
+ {
+ m_eMetricUnit = GetCurrentUnit(eState,pState);
+ if( m_eMetricUnit!=m_last_eMetricUnit )
+ {
+ mxLeftIndent->SetFieldUnit(m_eMetricUnit);
+ mxRightIndent->SetFieldUnit(m_eMetricUnit);
+ mxFLineIndent->SetFieldUnit(m_eMetricUnit);
+ mxTopDist->SetFieldUnit(m_eMetricUnit);
+ mxBottomDist->SetFieldUnit(m_eMetricUnit);
+
+ limitMetricWidths();
+ }
+ m_last_eMetricUnit = m_eMetricUnit;
+ }
+ break;
+
+ case SID_ATTR_PARA_LRSPACE:
+ StateChangedIndentImpl( eState, pState );
+ break;
+
+ case SID_ATTR_PARA_ULSPACE:
+ StateChangedULImpl( eState, pState );
+ break;
+ }
+}
+
+void ParaPropertyPanel::StateChangedIndentImpl( SfxItemState eState, const SfxPoolItem* pState )
+{
+ switch (maContext.GetCombinedContext_DI())
+ {
+
+ case CombinedEnumContext(Application::WriterVariants, Context::DrawText):
+ case CombinedEnumContext(Application::WriterVariants, Context::Annotation):
+ case CombinedEnumContext(Application::Calc, Context::DrawText):
+ case CombinedEnumContext(Application::DrawImpress, Context::DrawText):
+ case CombinedEnumContext(Application::DrawImpress, Context::Draw):
+ case CombinedEnumContext(Application::DrawImpress, Context::TextObject):
+ case CombinedEnumContext(Application::DrawImpress, Context::Graphic):
+ case CombinedEnumContext(Application::DrawImpress, Context::Table):
+ {
+ mxLeftIndent->set_min( DEFAULT_VALUE, FieldUnit::NONE );
+ mxRightIndent->set_min( DEFAULT_VALUE, FieldUnit::NONE );
+ mxFLineIndent->set_min( DEFAULT_VALUE, FieldUnit::NONE );
+ }
+ break;
+ case CombinedEnumContext(Application::WriterVariants, Context::Default):
+ case CombinedEnumContext(Application::WriterVariants, Context::Text):
+ case CombinedEnumContext(Application::WriterVariants, Context::Table):
+ {
+ mxLeftIndent->set_min( NEGA_MAXVALUE, FieldUnit::MM_100TH );
+ mxRightIndent->set_min( NEGA_MAXVALUE, FieldUnit::MM_100TH );
+ mxFLineIndent->set_min( NEGA_MAXVALUE, FieldUnit::MM_100TH );
+ }
+ break;
+ }
+
+ bool bDisabled = eState == SfxItemState::DISABLED;
+ mxLeftIndent->set_sensitive(!bDisabled);
+ mxRightIndent->set_sensitive(!bDisabled);
+ mxFLineIndent->set_sensitive(!bDisabled);
+
+ if (pState && eState >= SfxItemState::DEFAULT)
+ {
+ const SvxLRSpaceItem* pSpace = static_cast<const SvxLRSpaceItem*>(pState);
+ maTxtLeft = pSpace->GetTextLeft();
+ maTxtLeft = OutputDevice::LogicToLogic(maTxtLeft, m_eLRSpaceUnit, MapUnit::MapTwip);
+
+ tools::Long aTxtRight = pSpace->GetRight();
+ aTxtRight = OutputDevice::LogicToLogic(aTxtRight, m_eLRSpaceUnit, MapUnit::MapTwip);
+
+ tools::Long aTxtFirstLineOfst = pSpace->GetTextFirstLineOffset();
+ aTxtFirstLineOfst = OutputDevice::LogicToLogic( aTxtFirstLineOfst, m_eLRSpaceUnit, MapUnit::MapTwip );
+
+ tools::Long nVal = o3tl::convert(maTxtLeft, o3tl::Length::twip, o3tl::Length::mm100);
+ nVal = static_cast<tools::Long>(mxLeftIndent->normalize( nVal ));
+
+ if ( maContext.GetCombinedContext_DI() != CombinedEnumContext(Application::WriterVariants, Context::Text)
+ && maContext.GetCombinedContext_DI() != CombinedEnumContext(Application::WriterVariants, Context::Default)
+ && maContext.GetCombinedContext_DI() != CombinedEnumContext(Application::WriterVariants, Context::Table))
+ {
+ mxFLineIndent->set_min( nVal*-1, FieldUnit::MM_100TH );
+ }
+
+ tools::Long nrVal = o3tl::convert(aTxtRight, o3tl::Length::twip, o3tl::Length::mm100);
+ nrVal = static_cast<tools::Long>(mxRightIndent->normalize( nrVal ));
+
+ switch (maContext.GetCombinedContext_DI())
+ {
+ case CombinedEnumContext(Application::WriterVariants, Context::DrawText):
+ case CombinedEnumContext(Application::WriterVariants, Context::Text):
+ case CombinedEnumContext(Application::WriterVariants, Context::Default):
+ case CombinedEnumContext(Application::WriterVariants, Context::Table):
+ case CombinedEnumContext(Application::WriterVariants, Context::Annotation):
+ {
+ mxLeftIndent->set_max( MAX_SW - nrVal, FieldUnit::MM_100TH );
+ mxRightIndent->set_max( MAX_SW - nVal, FieldUnit::MM_100TH );
+ mxFLineIndent->set_max( MAX_SW - nVal - nrVal, FieldUnit::MM_100TH );
+ }
+ break;
+ case CombinedEnumContext(Application::DrawImpress, Context::DrawText):
+ case CombinedEnumContext(Application::DrawImpress, Context::Draw):
+ case CombinedEnumContext(Application::DrawImpress, Context::Table):
+ case CombinedEnumContext(Application::DrawImpress, Context::TextObject):
+ case CombinedEnumContext(Application::DrawImpress, Context::Graphic):
+ {
+ mxLeftIndent->set_max( MAX_SC_SD - nrVal, FieldUnit::MM_100TH );
+ mxRightIndent->set_max( MAX_SC_SD - nVal, FieldUnit::MM_100TH );
+ mxFLineIndent->set_max( MAX_SC_SD - nVal - nrVal, FieldUnit::MM_100TH );
+ }
+ }
+
+ mxLeftIndent->set_value( nVal, FieldUnit::MM_100TH );
+ mxRightIndent->set_value( nrVal, FieldUnit::MM_100TH );
+
+ tools::Long nfVal = o3tl::convert(aTxtFirstLineOfst, o3tl::Length::twip, o3tl::Length::mm100);
+ nfVal = static_cast<tools::Long>(mxFLineIndent->normalize( nfVal ));
+ mxFLineIndent->set_value( nfVal, FieldUnit::MM_100TH );
+ }
+ else if (eState != SfxItemState::DISABLED )
+ {
+ mxLeftIndent->set_text("");
+ mxRightIndent->set_text("");
+ mxFLineIndent->set_text("");
+ }
+
+ limitMetricWidths();
+}
+
+void ParaPropertyPanel::StateChangedULImpl( SfxItemState eState, const SfxPoolItem* pState )
+{
+ mxTopDist->set_max( mxTopDist->normalize( MAX_DURCH ), MapToFieldUnit(m_eULSpaceUnit) );
+ mxBottomDist->set_max( mxBottomDist->normalize( MAX_DURCH ), MapToFieldUnit(m_eULSpaceUnit) );
+
+ bool bDisabled = eState == SfxItemState::DISABLED;
+ mxTopDist->set_sensitive(!bDisabled);
+ mxBottomDist->set_sensitive(!bDisabled);
+
+ if( pState && eState >= SfxItemState::DEFAULT )
+ {
+ const SvxULSpaceItem* pOldItem = static_cast<const SvxULSpaceItem*>(pState);
+
+ maUpper = pOldItem->GetUpper();
+ maUpper = OutputDevice::LogicToLogic(maUpper, m_eULSpaceUnit, MapUnit::MapTwip);
+
+ maLower = pOldItem->GetLower();
+ maLower = OutputDevice::LogicToLogic(maLower, m_eULSpaceUnit, MapUnit::MapTwip);
+
+ sal_Int64 nVal = o3tl::convert(maUpper, o3tl::Length::twip, o3tl::Length::mm100);
+ nVal = mxTopDist->normalize( nVal );
+ mxTopDist->set_value( nVal, FieldUnit::MM_100TH );
+
+ nVal = o3tl::convert(maLower, o3tl::Length::twip, o3tl::Length::mm100);
+ nVal = mxBottomDist->normalize( nVal );
+ mxBottomDist->set_value( nVal, FieldUnit::MM_100TH );
+ }
+ else if (eState != SfxItemState::DISABLED )
+ {
+ mxTopDist->set_text("");
+ mxBottomDist->set_text("");
+ }
+ limitMetricWidths();
+}
+
+FieldUnit ParaPropertyPanel::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 ) //the object shell is not always available during reload
+ {
+ 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("svx.sidebar", "GetModuleFieldUnit(): no module found");
+ }
+ }
+ }
+
+ return eUnit;
+}
+
+ParaPropertyPanel::ParaPropertyPanel(weld::Widget* pParent,
+ const css::uno::Reference<css::frame::XFrame>& rxFrame,
+ SfxBindings* pBindings,
+ const css::uno::Reference<css::ui::XSidebar>& rxSidebar)
+ : PanelLayout(pParent, "ParaPropertyPanel", "svx/ui/sidebarparagraph.ui"),
+ //Alignment
+ mxTBxHorzAlign(m_xBuilder->weld_toolbar("horizontalalignment")),
+ mxHorzAlignDispatch(new ToolbarUnoDispatcher(*mxTBxHorzAlign, *m_xBuilder, rxFrame)),
+ mxTBxVertAlign(m_xBuilder->weld_toolbar("verticalalignment")),
+ mxVertAlignDispatch(new ToolbarUnoDispatcher(*mxTBxVertAlign, *m_xBuilder, rxFrame)),
+ //NumBullet&Backcolor
+ mxTBxNumBullet(m_xBuilder->weld_toolbar("numberbullet")),
+ mxNumBulletDispatch(new ToolbarUnoDispatcher(*mxTBxNumBullet, *m_xBuilder, rxFrame)),
+ mxTBxBackColor(m_xBuilder->weld_toolbar("backgroundcolor")),
+ mxBackColorDispatch(new ToolbarUnoDispatcher(*mxTBxBackColor, *m_xBuilder, rxFrame)),
+ mxTBxWriteDirection(m_xBuilder->weld_toolbar("writedirection")),
+ mxWriteDirectionDispatch(new ToolbarUnoDispatcher(*mxTBxWriteDirection, *m_xBuilder, rxFrame)),
+ mxTBxParaSpacing(m_xBuilder->weld_toolbar("paraspacing")),
+ mxParaSpacingDispatch(new ToolbarUnoDispatcher(*mxTBxParaSpacing, *m_xBuilder, rxFrame)),
+ mxTBxLineSpacing(m_xBuilder->weld_toolbar("linespacing")),
+ mxLineSpacingDispatch(new ToolbarUnoDispatcher(*mxTBxLineSpacing, *m_xBuilder, rxFrame)),
+ mxTBxIndent(m_xBuilder->weld_toolbar("indent")),
+ mxIndentDispatch(new ToolbarUnoDispatcher(*mxTBxIndent, *m_xBuilder, rxFrame)),
+ //Paragraph spacing
+ mxTopDist(new SvxRelativeField(m_xBuilder->weld_metric_spin_button("aboveparaspacing", FieldUnit::CM))),
+ mxBottomDist(new SvxRelativeField(m_xBuilder->weld_metric_spin_button("belowparaspacing", FieldUnit::CM))),
+ mxLeftIndent(new SvxRelativeField(m_xBuilder->weld_metric_spin_button("beforetextindent", FieldUnit::CM))),
+ mxRightIndent(new SvxRelativeField(m_xBuilder->weld_metric_spin_button("aftertextindent", FieldUnit::CM))),
+ mxFLineIndent(new SvxRelativeField(m_xBuilder->weld_metric_spin_button("firstlineindent", FieldUnit::CM))),
+ maTxtLeft (0),
+ maUpper (0),
+ maLower (0),
+ m_eMetricUnit(FieldUnit::NONE),
+ m_last_eMetricUnit(FieldUnit::NONE),
+ m_eLRSpaceUnit(),
+ m_eULSpaceUnit(),
+ maLRSpaceControl (SID_ATTR_PARA_LRSPACE,*pBindings,*this),
+ maULSpaceControl (SID_ATTR_PARA_ULSPACE, *pBindings,*this),
+ m_aMetricCtl (SID_ATTR_METRIC, *pBindings,*this),
+ mpBindings(pBindings),
+ mxSidebar(rxSidebar)
+{
+ // tdf#130197 We want to 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 ParaPropertyPanel is a default panel in writer, so
+ // subsequent panels, e.g. the TableEditPanel panel can have up to 5
+ // entries in each of its column and remain in alignment with this panel
+ padWidthForSidebar(*mxTBxIndent, rxFrame);
+
+ initial();
+ m_aMetricCtl.RequestUpdate();
+}
+
+void ParaPropertyPanel::limitMetricWidths()
+{
+ limitWidthForSidebar(*mxTopDist);
+ limitWidthForSidebar(*mxBottomDist);
+ limitWidthForSidebar(*mxLeftIndent);
+ limitWidthForSidebar(*mxRightIndent);
+ limitWidthForSidebar(*mxFLineIndent);
+}
+
+ParaPropertyPanel::~ParaPropertyPanel()
+{
+ mxHorzAlignDispatch.reset();
+ mxTBxHorzAlign.reset();
+
+ mxVertAlignDispatch.reset();
+ mxTBxVertAlign.reset();
+
+ mxNumBulletDispatch.reset();
+ mxTBxNumBullet.reset();
+
+ mxBackColorDispatch.reset();
+ mxTBxBackColor.reset();
+
+ mxWriteDirectionDispatch.reset();
+ mxTBxWriteDirection.reset();
+
+ mxParaSpacingDispatch.reset();
+ mxTBxParaSpacing.reset();
+
+ mxLineSpacingDispatch.reset();
+ mxTBxLineSpacing.reset();
+
+ mxIndentDispatch.reset();
+ mxTBxIndent.reset();
+
+ mxTopDist.reset();
+ mxBottomDist.reset();
+ mxLeftIndent.reset();
+ mxRightIndent.reset();
+ mxFLineIndent.reset();
+
+ maLRSpaceControl.dispose();
+ maULSpaceControl.dispose();
+ m_aMetricCtl.dispose();
+}
+
+} // end of namespace svx::sidebar
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/sidebar/paragraph/ParaPropertyPanel.hxx b/svx/source/sidebar/paragraph/ParaPropertyPanel.hxx
new file mode 100644
index 000000000..bd252eb1a
--- /dev/null
+++ b/svx/source/sidebar/paragraph/ParaPropertyPanel.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 <sfx2/sidebar/ControllerItem.hxx>
+#include <sfx2/sidebar/IContextChangeReceiver.hxx>
+#include <sfx2/sidebar/PanelLayout.hxx>
+#include <svx/relfld.hxx>
+
+#include <com/sun/star/frame/XFrame.hpp>
+#include <com/sun/star/ui/XSidebar.hpp>
+
+#include <svl/poolitem.hxx>
+#include <tools/fldunit.hxx>
+#include <vcl/EnumContext.hxx>
+
+class ToolbarUnoDispatcher;
+
+namespace svx::sidebar {
+
+class ParaPropertyPanel
+ : public PanelLayout,
+ public ::sfx2::sidebar::IContextChangeReceiver,
+ public ::sfx2::sidebar::ControllerItem::ItemUpdateReceiverInterface
+{
+public:
+ virtual ~ParaPropertyPanel() override;
+
+ static std::unique_ptr<PanelLayout> Create (
+ weld::Widget* pParent,
+ const css::uno::Reference<css::frame::XFrame>& rxFrame,
+ SfxBindings* pBindings,
+ const css::uno::Reference<css::ui::XSidebar>& rxSidebar);
+
+ SfxBindings* GetBindings() { return mpBindings;}
+
+ virtual void HandleContextChange (
+ const vcl::EnumContext& rContext) override;
+
+ 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 {};
+
+ static FieldUnit GetCurrentUnit( SfxItemState eState, const SfxPoolItem* pState );
+
+ ParaPropertyPanel (
+ weld::Widget* pParent,
+ const css::uno::Reference<css::frame::XFrame>& rxFrame,
+ SfxBindings* pBindings,
+ const css::uno::Reference<css::ui::XSidebar>& rxSidebar);
+
+private:
+ // UI controls
+ //Alignment
+ std::unique_ptr<weld::Toolbar> mxTBxHorzAlign;
+ std::unique_ptr<ToolbarUnoDispatcher> mxHorzAlignDispatch;
+ std::unique_ptr<weld::Toolbar> mxTBxVertAlign;
+ std::unique_ptr<ToolbarUnoDispatcher> mxVertAlignDispatch;
+ //NumBullet&Backcolor
+ std::unique_ptr<weld::Toolbar> mxTBxNumBullet;
+ std::unique_ptr<ToolbarUnoDispatcher> mxNumBulletDispatch;
+ std::unique_ptr<weld::Toolbar> mxTBxBackColor;
+ std::unique_ptr<ToolbarUnoDispatcher> mxBackColorDispatch;
+
+ std::unique_ptr<weld::Toolbar> mxTBxWriteDirection;
+ std::unique_ptr<ToolbarUnoDispatcher> mxWriteDirectionDispatch;
+ std::unique_ptr<weld::Toolbar> mxTBxParaSpacing;
+ std::unique_ptr<ToolbarUnoDispatcher> mxParaSpacingDispatch;
+ std::unique_ptr<weld::Toolbar> mxTBxLineSpacing;
+ std::unique_ptr<ToolbarUnoDispatcher> mxLineSpacingDispatch;
+ std::unique_ptr<weld::Toolbar> mxTBxIndent;
+ std::unique_ptr<ToolbarUnoDispatcher> mxIndentDispatch;
+
+ //Paragraph spacing
+ std::unique_ptr<SvxRelativeField> mxTopDist;
+ std::unique_ptr<SvxRelativeField> mxBottomDist;
+ std::unique_ptr<SvxRelativeField> mxLeftIndent;
+ std::unique_ptr<SvxRelativeField> mxRightIndent;
+ std::unique_ptr<SvxRelativeField> mxFLineIndent;
+
+ // Data Member
+ tools::Long maTxtLeft;
+ tools::Long maUpper;
+ tools::Long maLower;
+
+ FieldUnit m_eMetricUnit;
+ FieldUnit m_last_eMetricUnit;
+ MapUnit m_eLRSpaceUnit;
+ MapUnit m_eULSpaceUnit;
+ // Control Items
+ ::sfx2::sidebar::ControllerItem maLRSpaceControl;
+ ::sfx2::sidebar::ControllerItem maULSpaceControl;
+ ::sfx2::sidebar::ControllerItem m_aMetricCtl;
+
+ vcl::EnumContext maContext;
+ SfxBindings* mpBindings;
+ css::uno::Reference<css::ui::XSidebar> mxSidebar;
+
+ DECL_LINK(ModifyIndentHdl_Impl, weld::MetricSpinButton&, void);
+ DECL_LINK(ULSpaceHdl_Impl, weld::MetricSpinButton&, void);
+
+ void StateChangedIndentImpl( SfxItemState eState, const SfxPoolItem* pState );
+ void StateChangedULImpl( SfxItemState eState, const SfxPoolItem* pState );
+
+ void initial();
+ void ReSize();
+ void InitToolBoxIndent();
+ void InitToolBoxSpacing();
+ void limitMetricWidths();
+};
+
+} // end of namespace svx::sidebar
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/sidebar/paragraph/ParaSpacingControl.cxx b/svx/source/sidebar/paragraph/ParaSpacingControl.cxx
new file mode 100644
index 000000000..5b4c40969
--- /dev/null
+++ b/svx/source/sidebar/paragraph/ParaSpacingControl.cxx
@@ -0,0 +1,257 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include "ParaSpacingWindow.hxx"
+
+#include <cppuhelper/queryinterface.hxx>
+#include <editeng/ulspitem.hxx>
+#include <editeng/lrspitem.hxx>
+#include <editeng/editids.hrc>
+#include <svx/ParaSpacingControl.hxx>
+#include <vcl/toolbox.hxx>
+#include <sfx2/sfxsids.hrc>
+#include <svl/intitem.hxx>
+#include <comphelper/processfactory.hxx>
+
+#include <com/sun/star/frame/XFrame.hpp>
+#include <com/sun/star/ui/ContextChangeEventMultiplexer.hpp>
+#include <com/sun/star/ui/XContextChangeEventMultiplexer.hpp>
+
+using namespace svx;
+
+SFX_IMPL_TOOLBOX_CONTROL(ParaAboveSpacingControl, SvxULSpaceItem);
+SFX_IMPL_TOOLBOX_CONTROL(ParaBelowSpacingControl, SvxULSpaceItem);
+
+SFX_IMPL_TOOLBOX_CONTROL(ParaLeftSpacingControl, SvxLRSpaceItem);
+SFX_IMPL_TOOLBOX_CONTROL(ParaRightSpacingControl, SvxLRSpaceItem);
+SFX_IMPL_TOOLBOX_CONTROL(ParaFirstLineSpacingControl, SvxLRSpaceItem);
+
+ParaULSpacingControl::ParaULSpacingControl(sal_uInt16 nSlotId, ToolBoxItemId nId, ToolBox& rTbx)
+ : SfxToolBoxControl(nSlotId, nId, rTbx)
+{
+ addStatusListener(".uno:MetricUnit");
+}
+
+ParaULSpacingControl::~ParaULSpacingControl()
+{
+}
+
+void ParaULSpacingControl::StateChangedAtToolBoxControl(sal_uInt16 nSID, SfxItemState eState,
+ const SfxPoolItem* pState)
+{
+ ToolBoxItemId nId = GetId();
+ ToolBox& rTbx = GetToolBox();
+ ParaULSpacingWindow* pWindow = static_cast<ParaULSpacingWindow*>(rTbx.GetItemWindow(nId));
+
+ DBG_ASSERT( pWindow, "Control not found!" );
+
+ if(SfxItemState::DISABLED == eState)
+ pWindow->Disable();
+ else
+ pWindow->Enable();
+
+ rTbx.EnableItem(nId, SfxItemState::DISABLED != eState);
+
+ if(nSID == SID_ATTR_METRIC && pState && eState >= SfxItemState::DEFAULT)
+ {
+ const SfxUInt16Item* pMetricItem = static_cast<const SfxUInt16Item*>(pState);
+ pWindow->SetUnit(static_cast<FieldUnit>(pMetricItem->GetValue()));
+ }
+ else if((nSID == SID_ATTR_PARA_ULSPACE
+ || nSID == SID_ATTR_PARA_ABOVESPACE
+ || nSID == SID_ATTR_PARA_BELOWSPACE )
+ && pState && eState >= SfxItemState::DEFAULT)
+ pWindow->SetValue(static_cast<const SvxULSpaceItem*>(pState));
+}
+
+// ParaAboveSpacingControl
+
+ParaAboveSpacingControl::ParaAboveSpacingControl(sal_uInt16 nSlotId, ToolBoxItemId nId, ToolBox& rTbx)
+ : ParaULSpacingControl(nSlotId, nId, rTbx)
+{
+}
+
+VclPtr<InterimItemWindow> ParaAboveSpacingControl::CreateItemWindow(vcl::Window* pParent)
+{
+ VclPtr<ParaAboveSpacingWindow> pWindow = VclPtr<ParaAboveSpacingWindow>::Create(pParent);
+ pWindow->Show();
+
+ return pWindow;
+}
+
+// ParaBelowSpacingControl
+
+ParaBelowSpacingControl::ParaBelowSpacingControl(sal_uInt16 nSlotId, ToolBoxItemId nId, ToolBox& rTbx)
+ : ParaULSpacingControl(nSlotId, nId, rTbx)
+{
+}
+
+VclPtr<InterimItemWindow> ParaBelowSpacingControl::CreateItemWindow(vcl::Window* pParent)
+{
+ VclPtr<ParaBelowSpacingWindow> pWindow = VclPtr<ParaBelowSpacingWindow>::Create(pParent);
+ pWindow->Show();
+
+ return pWindow;
+}
+
+// ParaLRSpacingControl
+
+ParaLRSpacingControl::ParaLRSpacingControl(sal_uInt16 nSlotId, ToolBoxItemId nId, ToolBox& rTbx)
+ : SfxToolBoxControl(nSlotId, nId, rTbx)
+{
+ addStatusListener(".uno:MetricUnit");
+}
+
+ParaLRSpacingControl::~ParaLRSpacingControl()
+{
+}
+
+void SAL_CALL ParaLRSpacingControl::dispose()
+{
+ if(m_xMultiplexer.is())
+ {
+ m_xMultiplexer->removeAllContextChangeEventListeners(this);
+ m_xMultiplexer.clear();
+ }
+
+ SfxToolBoxControl::dispose();
+}
+
+void ParaLRSpacingControl::StateChangedAtToolBoxControl(sal_uInt16 nSID, SfxItemState eState,
+ const SfxPoolItem* pState)
+{
+ ToolBoxItemId nId = GetId();
+ ToolBox& rTbx = GetToolBox();
+ ParaLRSpacingWindow* pWindow = static_cast<ParaLRSpacingWindow*>(rTbx.GetItemWindow(nId));
+
+ DBG_ASSERT( pWindow, "Control not found!" );
+
+ if(SfxItemState::DISABLED == eState)
+ pWindow->Disable();
+ else
+ pWindow->Enable();
+
+ if(!m_xMultiplexer.is() && m_xFrame.is())
+ {
+ m_xMultiplexer = css::ui::ContextChangeEventMultiplexer::get(
+ ::comphelper::getProcessComponentContext());
+
+ m_xMultiplexer->addContextChangeEventListener(this, m_xFrame->getController());
+ }
+
+ if(nSID == SID_ATTR_METRIC && pState && eState >= SfxItemState::DEFAULT)
+ {
+ const SfxUInt16Item* pMetricItem = static_cast<const SfxUInt16Item*>(pState);
+ pWindow->SetUnit(static_cast<FieldUnit>(pMetricItem->GetValue()));
+ }
+ else if(nSID == SID_ATTR_PARA_LRSPACE
+ || nSID == SID_ATTR_PARA_LEFTSPACE
+ || nSID == SID_ATTR_PARA_RIGHTSPACE
+ || nSID == SID_ATTR_PARA_FIRSTLINESPACE
+ )
+ {
+ pWindow->SetValue(eState, pState);
+ }
+}
+
+void SAL_CALL ParaLRSpacingControl::notifyContextChangeEvent(const css::ui::ContextChangeEventObject& rEvent)
+{
+ ToolBoxItemId nId = GetId();
+ ToolBox& rTbx = GetToolBox();
+ ParaLRSpacingWindow* pWindow = static_cast<ParaLRSpacingWindow*>(rTbx.GetItemWindow(nId));
+
+ if(pWindow)
+ {
+ vcl::EnumContext eContext(
+ vcl::EnumContext::GetApplicationEnum(rEvent.ApplicationName),
+ vcl::EnumContext::GetContextEnum(rEvent.ContextName));
+ pWindow->SetContext(eContext);
+ }
+}
+
+::css::uno::Any SAL_CALL ParaLRSpacingControl::queryInterface(const ::css::uno::Type& aType)
+{
+ ::css::uno::Any a(SfxToolBoxControl::queryInterface(aType));
+ if (a.hasValue())
+ return a;
+
+ return ::cppu::queryInterface(aType, static_cast<css::ui::XContextChangeEventListener*>(this));
+}
+
+void SAL_CALL ParaLRSpacingControl::acquire() noexcept
+{
+ SfxToolBoxControl::acquire();
+}
+
+void SAL_CALL ParaLRSpacingControl::disposing(const ::css::lang::EventObject&)
+{
+ SfxToolBoxControl::disposing();
+}
+
+void SAL_CALL ParaLRSpacingControl::release() noexcept
+{
+ SfxToolBoxControl::release();
+}
+
+// ParaLeftSpacingControl
+
+ParaLeftSpacingControl::ParaLeftSpacingControl(sal_uInt16 nSlotId, ToolBoxItemId nId, ToolBox& rTbx)
+: ParaLRSpacingControl(nSlotId, nId, rTbx)
+{
+}
+
+VclPtr<InterimItemWindow> ParaLeftSpacingControl::CreateItemWindow(vcl::Window* pParent)
+{
+ VclPtr<ParaLeftSpacingWindow> pWindow = VclPtr<ParaLeftSpacingWindow>::Create(pParent);
+ pWindow->Show();
+
+ return pWindow;
+}
+
+// ParaRightSpacingControl
+
+ParaRightSpacingControl::ParaRightSpacingControl(sal_uInt16 nSlotId, ToolBoxItemId nId, ToolBox& rTbx)
+: ParaLRSpacingControl(nSlotId, nId, rTbx)
+{
+}
+
+VclPtr<InterimItemWindow> ParaRightSpacingControl::CreateItemWindow(vcl::Window* pParent)
+{
+ VclPtr<ParaRightSpacingWindow> pWindow = VclPtr<ParaRightSpacingWindow>::Create(pParent);
+ pWindow->Show();
+
+ return pWindow;
+}
+
+// ParaFirstLineSpacingControl
+
+ParaFirstLineSpacingControl::ParaFirstLineSpacingControl(sal_uInt16 nSlotId, ToolBoxItemId nId, ToolBox& rTbx)
+: ParaLRSpacingControl(nSlotId, nId, rTbx)
+{
+}
+
+VclPtr<InterimItemWindow> ParaFirstLineSpacingControl::CreateItemWindow(vcl::Window* pParent)
+{
+ VclPtr<ParaFirstLineSpacingWindow> pWindow = VclPtr<ParaFirstLineSpacingWindow>::Create(pParent);
+ pWindow->Show();
+
+ return pWindow;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/sidebar/paragraph/ParaSpacingWindow.cxx b/svx/source/sidebar/paragraph/ParaSpacingWindow.cxx
new file mode 100644
index 000000000..fa164dd02
--- /dev/null
+++ b/svx/source/sidebar/paragraph/ParaSpacingWindow.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 "ParaSpacingWindow.hxx"
+#include <editeng/editids.hrc>
+#include <editeng/lrspitem.hxx>
+#include <sfx2/dispatch.hxx>
+#include <sfx2/app.hxx>
+#include <sfx2/viewfrm.hxx>
+#include <svl/itempool.hxx>
+
+using namespace svx;
+
+#define DEFAULT_VALUE 0
+#define MAX_DURCH 31680 // tdf#68335: 1584 pt for UX interoperability with Word
+#define MAX_SW 1709400
+#define MAX_SC_SD 116220200
+#define NEGA_MAXVALUE -10000000
+
+// ParaULSpacingWindow
+
+ParaULSpacingWindow::ParaULSpacingWindow(vcl::Window* pParent)
+ : InterimItemWindow(pParent, "svx/ui/paraulspacing.ui", "ParaULSpacingWindow")
+ , m_eUnit(MapUnit::MapTwip)
+{
+ m_xAboveSpacing = std::make_unique<SvxRelativeField>(m_xBuilder->weld_metric_spin_button("aboveparaspacing", FieldUnit::CM));
+ m_xBelowSpacing = std::make_unique<SvxRelativeField>(m_xBuilder->weld_metric_spin_button("belowparaspacing", FieldUnit::CM));
+ m_xAboveContainer = m_xBuilder->weld_container("above");
+ m_xBelowContainer = m_xBuilder->weld_container("below");
+
+ Link<weld::MetricSpinButton&,void> aLink = LINK(this, ParaULSpacingWindow, ModifySpacingHdl);
+ m_xAboveSpacing->connect_value_changed(aLink);
+ m_xBelowSpacing->connect_value_changed(aLink);
+
+ /// set the initial values of max width
+ m_xAboveSpacing->set_max(m_xAboveSpacing->normalize(MAX_DURCH), FieldUnit::CM);
+ m_xBelowSpacing->set_max(m_xBelowSpacing->normalize(MAX_DURCH), FieldUnit::CM);
+}
+
+ParaULSpacingWindow::~ParaULSpacingWindow()
+{
+ disposeOnce();
+}
+
+void ParaULSpacingWindow::dispose()
+{
+ m_xAboveSpacing.reset();
+ m_xBelowSpacing.reset();
+ m_xAboveContainer.reset();
+ m_xBelowContainer.reset();
+
+ InterimItemWindow::dispose();
+}
+
+void ParaULSpacingWindow::SetUnit(FieldUnit eUnit)
+{
+ m_xAboveSpacing->SetFieldUnit(eUnit);
+ m_xBelowSpacing->SetFieldUnit(eUnit);
+
+ SfxItemPool &rPool = SfxGetpApp()->GetPool();
+ m_eUnit = rPool.GetMetric(SID_ATTR_PARA_ULSPACE);
+
+ m_xAboveSpacing->set_max(m_xAboveSpacing->normalize(MAX_DURCH), MapToFieldUnit(m_eUnit));
+ m_xBelowSpacing->set_max(m_xBelowSpacing->normalize(MAX_DURCH), MapToFieldUnit(m_eUnit));
+}
+
+void ParaULSpacingWindow::SetValue(const SvxULSpaceItem* pItem)
+{
+ sal_Int64 nVal = pItem->GetUpper();
+ nVal = m_xAboveSpacing->normalize(nVal);
+ m_xAboveSpacing->set_value(nVal, FieldUnit::MM_100TH);
+
+ nVal = pItem->GetLower();
+ nVal = m_xBelowSpacing->normalize(nVal);
+ m_xBelowSpacing->set_value(nVal, FieldUnit::MM_100TH);
+}
+
+IMPL_LINK_NOARG(ParaULSpacingWindow, ModifySpacingHdl, weld::MetricSpinButton&, void)
+{
+ SfxViewFrame* pFrame = SfxViewFrame::Current();
+ if (!pFrame)
+ return;
+ SfxDispatcher* pDisp = pFrame->GetBindings().GetDispatcher();
+ if(pDisp)
+ {
+ SvxULSpaceItem aMargin(SID_ATTR_PARA_ULSPACE);
+ aMargin.SetUpper(m_xAboveSpacing->GetCoreValue(m_eUnit));
+ aMargin.SetLower(m_xBelowSpacing->GetCoreValue(m_eUnit));
+ pDisp->ExecuteList(SID_ATTR_PARA_ULSPACE, SfxCallMode::RECORD, {&aMargin});
+ }
+}
+
+// ParaAboveSpacingWindow
+ParaAboveSpacingWindow::ParaAboveSpacingWindow(vcl::Window* pParent)
+ : ParaULSpacingWindow(pParent)
+{
+ InitControlBase(&m_xAboveSpacing->get_widget());
+
+ m_xAboveContainer->show();
+ m_xBelowContainer->hide();
+
+ SetSizePixel(get_preferred_size());
+}
+
+// ParaBelowSpacingWindow
+ParaBelowSpacingWindow::ParaBelowSpacingWindow(vcl::Window* pParent)
+ : ParaULSpacingWindow(pParent)
+{
+ InitControlBase(&m_xBelowSpacing->get_widget());
+
+ m_xAboveContainer->hide();
+ m_xBelowContainer->show();
+
+ SetSizePixel(get_preferred_size());
+}
+
+// ParaLRSpacingWindow
+ParaLRSpacingWindow::ParaLRSpacingWindow(vcl::Window* pParent)
+ : InterimItemWindow(pParent, "svx/ui/paralrspacing.ui", "ParaLRSpacingWindow")
+ , m_eUnit(MapUnit::MapTwip)
+{
+ m_xBeforeSpacing = std::make_unique<SvxRelativeField>(m_xBuilder->weld_metric_spin_button("beforetextindent", FieldUnit::CM));
+ m_xAfterSpacing = std::make_unique<SvxRelativeField>(m_xBuilder->weld_metric_spin_button("aftertextindent", FieldUnit::CM));
+ m_xFLSpacing = std::make_unique<SvxRelativeField>(m_xBuilder->weld_metric_spin_button("firstlineindent", FieldUnit::CM));
+ m_xBeforeContainer = m_xBuilder->weld_container("before");
+ m_xAfterContainer = m_xBuilder->weld_container("after");
+ m_xFirstLineContainer = m_xBuilder->weld_container("firstline");
+
+ Link<weld::MetricSpinButton&,void> aLink = LINK(this, ParaLRSpacingWindow, ModifySpacingHdl);
+ m_xBeforeSpacing->connect_value_changed(aLink);
+ m_xAfterSpacing->connect_value_changed(aLink);
+ m_xFLSpacing->connect_value_changed(aLink);
+
+ /// set the initial values of max width
+ m_xBeforeSpacing->set_min(NEGA_MAXVALUE, FieldUnit::MM_100TH);
+ m_xAfterSpacing->set_min(NEGA_MAXVALUE, FieldUnit::MM_100TH);
+ m_xFLSpacing->set_min(NEGA_MAXVALUE, FieldUnit::MM_100TH);
+}
+
+ParaLRSpacingWindow::~ParaLRSpacingWindow()
+{
+ disposeOnce();
+}
+
+void ParaLRSpacingWindow::dispose()
+{
+ m_xBeforeSpacing.reset();
+ m_xAfterSpacing.reset();
+ m_xFLSpacing.reset();
+ m_xBeforeContainer.reset();
+ m_xAfterContainer.reset();
+ m_xFirstLineContainer.reset();
+
+ InterimItemWindow::dispose();
+}
+
+void ParaLRSpacingWindow::SetContext(const vcl::EnumContext& eContext)
+{
+ m_aContext = eContext;
+}
+
+void ParaLRSpacingWindow::SetValue(SfxItemState eState, const SfxPoolItem* pState)
+{
+ switch(m_aContext.GetCombinedContext_DI())
+ {
+
+ case CombinedEnumContext(Application::WriterVariants, Context::DrawText):
+ case CombinedEnumContext(Application::WriterVariants, Context::Annotation):
+ case CombinedEnumContext(Application::Calc, Context::DrawText):
+ case CombinedEnumContext(Application::DrawImpress, Context::DrawText):
+ case CombinedEnumContext(Application::DrawImpress, Context::Draw):
+ case CombinedEnumContext(Application::DrawImpress, Context::TextObject):
+ case CombinedEnumContext(Application::DrawImpress, Context::Graphic):
+ case CombinedEnumContext(Application::DrawImpress, Context::Table):
+ {
+ m_xBeforeSpacing->set_min(DEFAULT_VALUE, FieldUnit::NONE);
+ m_xAfterSpacing->set_min(DEFAULT_VALUE, FieldUnit::NONE);
+ m_xFLSpacing->set_min(DEFAULT_VALUE, FieldUnit::NONE);
+ }
+ break;
+ case CombinedEnumContext(Application::WriterVariants, Context::Default):
+ case CombinedEnumContext(Application::WriterVariants, Context::Text):
+ case CombinedEnumContext(Application::WriterVariants, Context::Table):
+ {
+ m_xBeforeSpacing->set_min(NEGA_MAXVALUE, FieldUnit::MM_100TH);
+ m_xAfterSpacing->set_min(NEGA_MAXVALUE, FieldUnit::MM_100TH);
+ m_xFLSpacing->set_min(NEGA_MAXVALUE, FieldUnit::MM_100TH);
+ }
+ break;
+ }
+
+ if(pState && eState >= SfxItemState::DEFAULT)
+ {
+ m_xBeforeSpacing->set_sensitive(true);
+ m_xAfterSpacing->set_sensitive(true);
+ m_xFLSpacing->set_sensitive(true);
+
+ const SvxLRSpaceItem* pSpace = static_cast<const SvxLRSpaceItem*>(pState);
+ tools::Long aTxtLeft = pSpace->GetTextLeft();
+ tools::Long aTxtRight = pSpace->GetRight();
+ tools::Long aTxtFirstLineOfst = pSpace->GetTextFirstLineOffset();
+
+ aTxtLeft = m_xBeforeSpacing->normalize(aTxtLeft);
+
+ if(m_aContext.GetCombinedContext_DI() != CombinedEnumContext(Application::WriterVariants, Context::Text)
+ && m_aContext.GetCombinedContext_DI() != CombinedEnumContext(Application::WriterVariants, Context::Default)
+ && m_aContext.GetCombinedContext_DI() != CombinedEnumContext(Application::WriterVariants, Context::Table))
+ {
+ m_xFLSpacing->set_min(aTxtLeft*-1, FieldUnit::MM_100TH);
+ }
+
+ aTxtRight = m_xAfterSpacing->normalize(aTxtRight);
+
+ switch(m_aContext.GetCombinedContext_DI())
+ {
+ case CombinedEnumContext(Application::WriterVariants, Context::DrawText):
+ case CombinedEnumContext(Application::WriterVariants, Context::Text):
+ case CombinedEnumContext(Application::WriterVariants, Context::Default):
+ case CombinedEnumContext(Application::WriterVariants, Context::Table):
+ case CombinedEnumContext(Application::WriterVariants, Context::Annotation):
+ {
+ m_xBeforeSpacing->set_max(MAX_SW - aTxtRight, FieldUnit::MM_100TH);
+ m_xAfterSpacing->set_max(MAX_SW - aTxtLeft, FieldUnit::MM_100TH);
+ m_xFLSpacing->set_max(MAX_SW - aTxtLeft - aTxtRight, FieldUnit::MM_100TH);
+ }
+ break;
+ case CombinedEnumContext(Application::DrawImpress, Context::DrawText):
+ case CombinedEnumContext(Application::DrawImpress, Context::Draw):
+ case CombinedEnumContext(Application::DrawImpress, Context::Table):
+ case CombinedEnumContext(Application::DrawImpress, Context::TextObject):
+ case CombinedEnumContext(Application::DrawImpress, Context::Graphic):
+ {
+ m_xBeforeSpacing->set_max(MAX_SC_SD - aTxtRight, FieldUnit::MM_100TH);
+ m_xAfterSpacing->set_max(MAX_SC_SD - aTxtLeft, FieldUnit::MM_100TH);
+ m_xFLSpacing->set_max(MAX_SC_SD - aTxtLeft - aTxtRight, FieldUnit::MM_100TH);
+ }
+ }
+
+ m_xBeforeSpacing->set_value(aTxtLeft, FieldUnit::MM_100TH);
+ m_xAfterSpacing->set_value(aTxtRight, FieldUnit::MM_100TH);
+
+ aTxtFirstLineOfst = m_xFLSpacing->normalize(aTxtFirstLineOfst);
+ m_xFLSpacing->set_value(aTxtFirstLineOfst, FieldUnit::MM_100TH);
+ }
+ else if(eState == SfxItemState::DISABLED)
+ {
+ m_xBeforeSpacing->set_sensitive(false);
+ m_xAfterSpacing->set_sensitive(false);
+ m_xFLSpacing->set_sensitive(false);
+ }
+ else
+ {
+ m_xBeforeSpacing->set_text("");
+ m_xAfterSpacing->set_text("");
+ m_xFLSpacing->set_text("");
+ }
+}
+
+void ParaLRSpacingWindow::SetUnit(FieldUnit eUnit)
+{
+ m_xBeforeSpacing->SetFieldUnit(eUnit);
+ m_xAfterSpacing->SetFieldUnit(eUnit);
+ m_xFLSpacing->SetFieldUnit(eUnit);
+
+ SfxItemPool &rPool = SfxGetpApp()->GetPool();
+ m_eUnit = rPool.GetMetric(SID_ATTR_PARA_LRSPACE);
+}
+
+IMPL_LINK_NOARG(ParaLRSpacingWindow, ModifySpacingHdl, weld::MetricSpinButton&, void)
+{
+ SfxViewFrame* pFrame = SfxViewFrame::Current();
+ if (!pFrame)
+ return;
+ SfxDispatcher* pDisp = pFrame->GetBindings().GetDispatcher();
+ if(pDisp)
+ {
+ SvxLRSpaceItem aMargin(SID_ATTR_PARA_LRSPACE);
+ aMargin.SetTextLeft(m_xBeforeSpacing->GetCoreValue(m_eUnit));
+ aMargin.SetRight(m_xAfterSpacing->GetCoreValue(m_eUnit));
+ aMargin.SetTextFirstLineOffset(m_xFLSpacing->GetCoreValue(m_eUnit));
+
+ pDisp->ExecuteList(SID_ATTR_PARA_LRSPACE, SfxCallMode::RECORD, {&aMargin});
+ }
+}
+
+// ParaLeftSpacingWindow
+ParaLeftSpacingWindow::ParaLeftSpacingWindow(vcl::Window* pParent)
+ : ParaLRSpacingWindow(pParent)
+{
+ InitControlBase(&m_xBeforeSpacing->get_widget());
+
+ m_xBeforeContainer->show();
+ m_xAfterContainer->hide();
+ m_xFirstLineContainer->hide();
+
+ SetSizePixel(get_preferred_size());
+}
+
+// ParaRightSpacingWindow
+ParaRightSpacingWindow::ParaRightSpacingWindow(vcl::Window* pParent)
+ : ParaLRSpacingWindow(pParent)
+{
+ InitControlBase(&m_xAfterSpacing->get_widget());
+
+ m_xBeforeContainer->hide();
+ m_xAfterContainer->show();
+ m_xFirstLineContainer->hide();
+
+ SetSizePixel(get_preferred_size());
+}
+
+// ParaFirstLineSpacingWindow
+ParaFirstLineSpacingWindow::ParaFirstLineSpacingWindow(vcl::Window* pParent)
+ : ParaLRSpacingWindow(pParent)
+{
+ InitControlBase(&m_xFLSpacing->get_widget());
+
+ m_xBeforeContainer->hide();
+ m_xAfterContainer->hide();
+ m_xFirstLineContainer->show();
+
+ SetSizePixel(get_preferred_size());
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/sidebar/paragraph/ParaSpacingWindow.hxx b/svx/source/sidebar/paragraph/ParaSpacingWindow.hxx
new file mode 100644
index 000000000..f1730f933
--- /dev/null
+++ b/svx/source/sidebar/paragraph/ParaSpacingWindow.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 .
+ */
+#pragma once
+
+#include <editeng/ulspitem.hxx>
+#include <vcl/EnumContext.hxx>
+#include <vcl/InterimItemWindow.hxx>
+#include <svx/relfld.hxx>
+
+using namespace com::sun::star;
+
+namespace svx
+{
+class ParaULSpacingWindow : public InterimItemWindow
+{
+public:
+ virtual ~ParaULSpacingWindow() override;
+ virtual void dispose() override;
+
+ void SetValue(const SvxULSpaceItem* pItem);
+ void SetUnit(FieldUnit eUnit);
+
+protected:
+ ParaULSpacingWindow(vcl::Window* pParent);
+
+ std::unique_ptr<SvxRelativeField> m_xAboveSpacing;
+ std::unique_ptr<SvxRelativeField> m_xBelowSpacing;
+ std::unique_ptr<weld::Container> m_xAboveContainer;
+ std::unique_ptr<weld::Container> m_xBelowContainer;
+
+ MapUnit m_eUnit;
+
+ DECL_LINK(ModifySpacingHdl, weld::MetricSpinButton&, void);
+};
+
+class ParaAboveSpacingWindow : public ParaULSpacingWindow
+{
+public:
+ explicit ParaAboveSpacingWindow(vcl::Window* pParent);
+};
+
+class ParaBelowSpacingWindow : public ParaULSpacingWindow
+{
+public:
+ explicit ParaBelowSpacingWindow(vcl::Window* pParent);
+};
+
+class ParaLRSpacingWindow : public InterimItemWindow
+{
+public:
+ virtual ~ParaLRSpacingWindow() override;
+ virtual void dispose() override;
+
+ void SetValue(SfxItemState eState, const SfxPoolItem* pState);
+ void SetUnit(FieldUnit eUnit);
+ void SetContext(const vcl::EnumContext& eContext);
+
+protected:
+ ParaLRSpacingWindow(vcl::Window* pParent);
+
+ std::unique_ptr<SvxRelativeField> m_xBeforeSpacing;
+ std::unique_ptr<SvxRelativeField> m_xAfterSpacing;
+ std::unique_ptr<SvxRelativeField> m_xFLSpacing;
+ std::unique_ptr<weld::Container> m_xBeforeContainer;
+ std::unique_ptr<weld::Container> m_xAfterContainer;
+ std::unique_ptr<weld::Container> m_xFirstLineContainer;
+
+ MapUnit m_eUnit;
+
+ vcl::EnumContext m_aContext;
+
+ DECL_LINK(ModifySpacingHdl, weld::MetricSpinButton&, void);
+};
+
+class ParaLeftSpacingWindow : public ParaLRSpacingWindow
+{
+public:
+ explicit ParaLeftSpacingWindow(vcl::Window* pParent);
+};
+
+class ParaRightSpacingWindow : public ParaLRSpacingWindow
+{
+public:
+ explicit ParaRightSpacingWindow(vcl::Window* pParent);
+};
+
+class ParaFirstLineSpacingWindow : public ParaLRSpacingWindow
+{
+public:
+ explicit ParaFirstLineSpacingWindow(vcl::Window* pParent);
+};
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/sidebar/possize/PosSizePropertyPanel.cxx b/svx/source/sidebar/possize/PosSizePropertyPanel.cxx
new file mode 100644
index 000000000..da9e067b2
--- /dev/null
+++ b/svx/source/sidebar/possize/PosSizePropertyPanel.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 <sal/config.h>
+
+#include <algorithm>
+
+#include "PosSizePropertyPanel.hxx"
+#include <sal/log.hxx>
+#include <svx/svxids.hrc>
+#include <sfx2/dispatch.hxx>
+#include <sfx2/bindings.hxx>
+#include <sfx2/module.hxx>
+#include <sfx2/viewsh.hxx>
+#include <sfx2/objsh.hxx>
+#include <sfx2/viewfrm.hxx>
+#include <sfx2/weldutils.hxx>
+#include <svx/dialcontrol.hxx>
+#include <svx/dialmgr.hxx>
+#include <svx/rectenum.hxx>
+#include <svx/sdangitm.hxx>
+#include <unotools/viewoptions.hxx>
+#include <unotools/localedatawrapper.hxx>
+#include <vcl/canvastools.hxx>
+#include <vcl/fieldvalues.hxx>
+#include <svl/intitem.hxx>
+#include <svx/strings.hrc>
+#include <svx/svdpagv.hxx>
+#include <svx/svdview.hxx>
+#include <svx/transfrmhelper.hxx>
+#include <boost/property_tree/ptree.hpp>
+
+#include <svtools/unitconv.hxx>
+
+using namespace css;
+using namespace css::uno;
+
+constexpr OUStringLiteral USERITEM_NAME = u"FitItem";
+
+namespace svx::sidebar {
+
+PosSizePropertyPanel::PosSizePropertyPanel(
+ weld::Widget* pParent,
+ const css::uno::Reference<css::frame::XFrame>& rxFrame,
+ SfxBindings* pBindings,
+ const css::uno::Reference<css::ui::XSidebar>& rxSidebar)
+: PanelLayout(pParent, "PosSizePropertyPanel", "svx/ui/sidebarpossize.ui"),
+ mxFtPosX(m_xBuilder->weld_label("horizontallabel")),
+ mxMtrPosX(m_xBuilder->weld_metric_spin_button("horizontalpos", FieldUnit::CM)),
+ mxFtPosY(m_xBuilder->weld_label("verticallabel")),
+ mxMtrPosY(m_xBuilder->weld_metric_spin_button("verticalpos", FieldUnit::CM)),
+ mxFtWidth(m_xBuilder->weld_label("widthlabel")),
+ mxMtrWidth(m_xBuilder->weld_metric_spin_button("selectwidth", FieldUnit::CM)),
+ mxFtHeight(m_xBuilder->weld_label("heightlabel")),
+ mxMtrHeight(m_xBuilder->weld_metric_spin_button("selectheight", FieldUnit::CM)),
+ mxCbxScale(m_xBuilder->weld_check_button("ratio")),
+ mxFtAngle(m_xBuilder->weld_label("rotationlabel")),
+ mxMtrAngle(m_xBuilder->weld_metric_spin_button("rotation", FieldUnit::DEGREE)),
+ mxCtrlDial(new DialControl),
+ mxDial(new weld::CustomWeld(*m_xBuilder, "orientationcontrol", *mxCtrlDial)),
+ mxFtFlip(m_xBuilder->weld_label("fliplabel")),
+ mxFlipTbx(m_xBuilder->weld_toolbar("selectrotationtype")),
+ mxFlipDispatch(new ToolbarUnoDispatcher(*mxFlipTbx, *m_xBuilder, rxFrame)),
+ mxArrangeTbx(m_xBuilder->weld_toolbar("arrangetoolbar")),
+ mxArrangeDispatch(new ToolbarUnoDispatcher(*mxArrangeTbx, *m_xBuilder, rxFrame)),
+ mxArrangeTbx2(m_xBuilder->weld_toolbar("arrangetoolbar2")),
+ mxArrangeDispatch2(new ToolbarUnoDispatcher(*mxArrangeTbx2, *m_xBuilder, rxFrame)),
+ mxAlignTbx(m_xBuilder->weld_toolbar("aligntoolbar")),
+ mxAlignDispatch(new ToolbarUnoDispatcher(*mxAlignTbx, *m_xBuilder, rxFrame)),
+ mxAlignTbx2(m_xBuilder->weld_toolbar("aligntoolbar2")),
+ mxAlignDispatch2(new ToolbarUnoDispatcher(*mxAlignTbx2, *m_xBuilder, rxFrame)),
+ mxBtnEditOLEObject(m_xBuilder->weld_button("btnEditObject")),
+ mpView(nullptr),
+ mlOldWidth(1),
+ mlOldHeight(1),
+ mlRotX(0),
+ mlRotY(0),
+ mePoolUnit(),
+ meDlgUnit(FieldUnit::INCH), // #i124409# init with fallback default
+ maTransfPosXControl(SID_ATTR_TRANSFORM_POS_X, *pBindings, *this),
+ maTransfPosYControl(SID_ATTR_TRANSFORM_POS_Y, *pBindings, *this),
+ maTransfWidthControl(SID_ATTR_TRANSFORM_WIDTH, *pBindings, *this),
+ maTransfHeightControl(SID_ATTR_TRANSFORM_HEIGHT, *pBindings, *this),
+ maSvxAngleControl( SID_ATTR_TRANSFORM_ANGLE, *pBindings, *this),
+ maRotXControl(SID_ATTR_TRANSFORM_ROT_X, *pBindings, *this),
+ maRotYControl(SID_ATTR_TRANSFORM_ROT_Y, *pBindings, *this),
+ maProPosControl(SID_ATTR_TRANSFORM_PROTECT_POS, *pBindings, *this),
+ maProSizeControl(SID_ATTR_TRANSFORM_PROTECT_SIZE, *pBindings, *this),
+ maAutoWidthControl(SID_ATTR_TRANSFORM_AUTOWIDTH, *pBindings, *this),
+ maAutoHeightControl(SID_ATTR_TRANSFORM_AUTOHEIGHT, *pBindings, *this),
+ m_aMetricCtl(SID_ATTR_METRIC, *pBindings, *this),
+ mpBindings(pBindings),
+ mbSizeProtected(false),
+ mbPositionProtected(false),
+ mbAutoWidth(false),
+ mbAutoHeight(false),
+ mbAdjustEnabled(false),
+ mxSidebar(rxSidebar)
+{
+ Initialize();
+
+ // A guesstimate of the longest label in the various sidebar panes to use
+ // to get this pane's contents to align with them, for lack of a better
+ // solution
+ auto nWidth = mxFtWidth->get_preferred_size().Width();
+ OUString sLabel = mxFtWidth->get_label();
+ mxFtWidth->set_label(SvxResId(RID_SVXSTR_TRANSPARENCY));
+ nWidth = std::max(nWidth, mxFtWidth->get_preferred_size().Width());;
+ mxFtWidth->set_label(sLabel);
+ mxFtWidth->set_size_request(nWidth, -1);
+
+ mpBindings->Update( SID_ATTR_METRIC );
+ mpBindings->Update( SID_ATTR_TRANSFORM_WIDTH );
+ mpBindings->Update( SID_ATTR_TRANSFORM_HEIGHT );
+ mpBindings->Update( SID_ATTR_TRANSFORM_PROTECT_SIZE );
+}
+
+PosSizePropertyPanel::~PosSizePropertyPanel()
+{
+ mxFtPosX.reset();
+ mxMtrPosX.reset();
+ mxFtPosY.reset();
+ mxMtrPosY.reset();
+ mxFtWidth.reset();
+ mxMtrWidth.reset();
+ mxFtHeight.reset();
+ mxMtrHeight.reset();
+ mxCbxScale.reset();
+ mxFtAngle.reset();
+ mxMtrAngle.reset();
+ mxDial.reset();
+ mxCtrlDial.reset();
+ mxFtFlip.reset();
+ mxFlipDispatch.reset();
+ mxFlipTbx.reset();
+ mxAlignDispatch.reset();
+ mxAlignDispatch2.reset();
+ mxAlignTbx.reset();
+ mxAlignTbx2.reset();
+ mxArrangeDispatch.reset();
+ mxArrangeDispatch2.reset();
+ mxArrangeTbx.reset();
+ mxArrangeTbx2.reset();
+ mxBtnEditOLEObject.reset();
+
+ maTransfPosXControl.dispose();
+ maTransfPosYControl.dispose();
+ maTransfWidthControl.dispose();
+ maTransfHeightControl.dispose();
+
+ maSvxAngleControl.dispose();
+ maRotXControl.dispose();
+ maRotYControl.dispose();
+ maProPosControl.dispose();
+ maProSizeControl.dispose();
+ maAutoWidthControl.dispose();
+ maAutoHeightControl.dispose();
+ m_aMetricCtl.dispose();
+}
+
+namespace
+{
+ bool hasText(const SdrView& rSdrView)
+ {
+ const SdrMarkList& rMarkList = rSdrView.GetMarkedObjectList();
+
+ if(1 == rMarkList.GetMarkCount())
+ {
+ const SdrObject* pObj = rMarkList.GetMark(0)->GetMarkedSdrObj();
+ const SdrObjKind eKind(pObj->GetObjIdentifier());
+
+ if((pObj->GetObjInventor() == SdrInventor::Default) && (SdrObjKind::Text == eKind || SdrObjKind::TitleText == eKind || SdrObjKind::OutlineText == eKind))
+ {
+ const SdrTextObj* pSdrTextObj = dynamic_cast< const SdrTextObj* >(pObj);
+
+ if(pSdrTextObj && pSdrTextObj->HasText())
+ {
+ return true;
+ }
+ }
+ }
+
+ return false;
+ }
+} // end of anonymous namespace
+
+
+void PosSizePropertyPanel::Initialize()
+{
+ //Position : Horizontal / Vertical
+ mxMtrPosX->connect_value_changed( LINK( this, PosSizePropertyPanel, ChangePosXHdl ) );
+ mxMtrPosY->connect_value_changed( LINK( this, PosSizePropertyPanel, ChangePosYHdl ) );
+
+ //Size : Width / Height
+ mxMtrWidth->connect_value_changed( LINK( this, PosSizePropertyPanel, ChangeWidthHdl ) );
+ mxMtrHeight->connect_value_changed( LINK( this, PosSizePropertyPanel, ChangeHeightHdl ) );
+
+ //Size : Keep ratio
+ mxCbxScale->connect_toggled( LINK( this, PosSizePropertyPanel, ClickAutoHdl ) );
+
+ //rotation control
+ mxCtrlDial->SetLinkedField(mxMtrAngle.get(), 2);
+ mxCtrlDial->SetModifyHdl(LINK( this, PosSizePropertyPanel, RotationHdl));
+
+ //use same logic as DialControl_Impl::SetSize
+ weld::DrawingArea* pDrawingArea = mxCtrlDial->GetDrawingArea();
+ int nDim = (std::min<int>(pDrawingArea->get_approximate_digit_width() * 6,
+ pDrawingArea->get_text_height() * 3) - 1) | 1;
+ Size aSize(nDim, nDim);
+ pDrawingArea->set_size_request(aSize.Width(), aSize.Height());
+ mxCtrlDial->Init(aSize);
+
+ mxBtnEditOLEObject->connect_clicked( LINK( this, PosSizePropertyPanel, ClickObjectEditHdl ) );
+
+ SfxViewShell* pCurSh = SfxViewShell::Current();
+ if ( pCurSh )
+ mpView = pCurSh->GetDrawView();
+ else
+ mpView = nullptr;
+
+ if ( mpView != nullptr )
+ {
+ maUIScale = mpView->GetModel()->GetUIScale();
+ mbAdjustEnabled = hasText(*mpView);
+ }
+
+ mePoolUnit = maTransfWidthControl.GetCoreMetric();
+}
+
+std::unique_ptr<PanelLayout> PosSizePropertyPanel::Create (
+ weld::Widget* pParent,
+ const css::uno::Reference<css::frame::XFrame>& rxFrame,
+ SfxBindings* pBindings,
+ const css::uno::Reference<css::ui::XSidebar>& rxSidebar)
+{
+ if (pParent == nullptr)
+ throw lang::IllegalArgumentException("no parent Window given to PosSizePropertyPanel::Create", nullptr, 0);
+ if ( ! rxFrame.is())
+ throw lang::IllegalArgumentException("no XFrame given to PosSizePropertyPanel::Create", nullptr, 1);
+ if (pBindings == nullptr)
+ throw lang::IllegalArgumentException("no SfxBindings given to PosSizePropertyPanel::Create", nullptr, 2);
+
+ return std::make_unique<PosSizePropertyPanel>(pParent, rxFrame, pBindings, rxSidebar);
+}
+
+void PosSizePropertyPanel::HandleContextChange(
+ const vcl::EnumContext& rContext)
+{
+ if (maContext == rContext)
+ {
+ // Nothing to do.
+ return;
+ }
+
+ maContext = rContext;
+
+ bool bShowPosition = false;
+ bool bShowAngle = false;
+ bool bShowFlip = false;
+ bool bShowEditObject = false;
+ bool bShowArrangeTbx2 = false;
+
+ switch (maContext.GetCombinedContext_DI())
+ {
+ case CombinedEnumContext(Application::WriterVariants, Context::Draw):
+ bShowAngle = true;
+ bShowFlip = true;
+ bShowArrangeTbx2 = true;
+ break;
+
+ case CombinedEnumContext(Application::WriterVariants, Context::Graphic):
+ bShowFlip = true;
+ bShowAngle = true; // RotGrfFlyFrame: Writer FlyFrames for Graphics now support angle
+ break;
+
+ case CombinedEnumContext(Application::Calc, Context::Draw):
+ case CombinedEnumContext(Application::Calc, Context::DrawLine):
+ case CombinedEnumContext(Application::Calc, Context::Graphic):
+ case CombinedEnumContext(Application::DrawImpress, Context::Draw):
+ case CombinedEnumContext(Application::DrawImpress, Context::DrawLine):
+ case CombinedEnumContext(Application::DrawImpress, Context::TextObject):
+ case CombinedEnumContext(Application::DrawImpress, Context::Graphic):
+ bShowPosition = true;
+ bShowAngle = true;
+ bShowFlip = true;
+ break;
+
+ case CombinedEnumContext(Application::WriterVariants, Context::OLE):
+ bShowEditObject = true;
+ break;
+
+ case CombinedEnumContext(Application::Calc, Context::OLE):
+ case CombinedEnumContext(Application::DrawImpress, Context::OLE):
+ bShowPosition = true;
+ bShowEditObject = true;
+ break;
+
+ case CombinedEnumContext(Application::Calc, Context::Chart):
+ case CombinedEnumContext(Application::Calc, Context::Form):
+ case CombinedEnumContext(Application::Calc, Context::Media):
+ case CombinedEnumContext(Application::Calc, Context::MultiObject):
+ case CombinedEnumContext(Application::DrawImpress, Context::Media):
+ case CombinedEnumContext(Application::DrawImpress, Context::Form):
+ case CombinedEnumContext(Application::DrawImpress, Context::ThreeDObject):
+ case CombinedEnumContext(Application::DrawImpress, Context::MultiObject):
+ bShowPosition = true;
+ break;
+ }
+
+ // Position
+ mxFtPosX->set_visible(bShowPosition);
+ mxMtrPosX->set_visible(bShowPosition);
+ mxFtPosY->set_visible(bShowPosition);
+ mxMtrPosY->set_visible(bShowPosition);
+
+ // Rotation
+ mxFtAngle->set_visible(bShowAngle);
+ mxMtrAngle->set_visible(bShowAngle);
+ mxCtrlDial->set_visible(bShowAngle);
+
+ // Flip
+ mxFtFlip->set_visible(bShowFlip);
+ mxFlipTbx->set_visible(bShowFlip);
+
+ // Edit Object
+ mxBtnEditOLEObject->set_visible(bShowEditObject);
+
+ // Arrange tool bar 2
+ mxArrangeTbx2->set_visible(bShowArrangeTbx2);
+
+ if (mxSidebar.is())
+ mxSidebar->requestLayout();
+}
+
+
+IMPL_LINK_NOARG( PosSizePropertyPanel, ChangeWidthHdl, weld::MetricSpinButton&, void )
+{
+ if( mxCbxScale->get_active() &&
+ mxCbxScale->get_sensitive() )
+ {
+ tools::Long nHeight = static_cast<tools::Long>( (static_cast<double>(mlOldHeight) * static_cast<double>(mxMtrWidth->get_value(FieldUnit::NONE))) / static_cast<double>(mlOldWidth) );
+ if( nHeight <= mxMtrHeight->get_max( FieldUnit::NONE ) )
+ {
+ mxMtrHeight->set_value( nHeight, FieldUnit::NONE );
+ }
+ else
+ {
+ nHeight = static_cast<tools::Long>(mxMtrHeight->get_max( FieldUnit::NONE ));
+ mxMtrHeight->set_value(nHeight, FieldUnit::NONE);
+ const tools::Long nWidth = static_cast<tools::Long>( (static_cast<double>(mlOldWidth) * static_cast<double>(nHeight)) / static_cast<double>(mlOldHeight) );
+ mxMtrWidth->set_value( nWidth, FieldUnit::NONE );
+ }
+ }
+ executeSize();
+}
+
+
+IMPL_LINK_NOARG( PosSizePropertyPanel, ChangeHeightHdl, weld::MetricSpinButton&, void )
+{
+ if( mxCbxScale->get_active() &&
+ mxCbxScale->get_sensitive() )
+ {
+ tools::Long nWidth = static_cast<tools::Long>( (static_cast<double>(mlOldWidth) * static_cast<double>(mxMtrHeight->get_value(FieldUnit::NONE))) / static_cast<double>(mlOldHeight) );
+ if( nWidth <= mxMtrWidth->get_max( FieldUnit::NONE ) )
+ {
+ mxMtrWidth->set_value( nWidth, FieldUnit::NONE );
+ }
+ else
+ {
+ nWidth = static_cast<tools::Long>(mxMtrWidth->get_max( FieldUnit::NONE ));
+ mxMtrWidth->set_value( nWidth, FieldUnit::NONE );
+ const tools::Long nHeight = static_cast<tools::Long>( (static_cast<double>(mlOldHeight) * static_cast<double>(nWidth)) / static_cast<double>(mlOldWidth) );
+ mxMtrHeight->set_value( nHeight, FieldUnit::NONE );
+ }
+ }
+ executeSize();
+}
+
+
+IMPL_LINK_NOARG( PosSizePropertyPanel, ChangePosXHdl, weld::MetricSpinButton&, void )
+{
+ if ( mxMtrPosX->get_value_changed_from_saved())
+ {
+ tools::Long lX = GetCoreValue( *mxMtrPosX, mePoolUnit );
+
+ Fraction aUIScale = mpView->GetModel()->GetUIScale();
+ lX = tools::Long( lX * aUIScale );
+
+ SfxInt32Item aPosXItem( SID_ATTR_TRANSFORM_POS_X,static_cast<sal_uInt32>(lX));
+
+ GetBindings()->GetDispatcher()->ExecuteList(
+ SID_ATTR_TRANSFORM, SfxCallMode::RECORD, { &aPosXItem });
+ }
+}
+
+IMPL_LINK_NOARG( PosSizePropertyPanel, ChangePosYHdl, weld::MetricSpinButton&, void )
+{
+ if ( mxMtrPosY->get_value_changed_from_saved() )
+ {
+ tools::Long lY = GetCoreValue( *mxMtrPosY, mePoolUnit );
+
+ Fraction aUIScale = mpView->GetModel()->GetUIScale();
+ lY = tools::Long( lY * aUIScale );
+
+ SfxInt32Item aPosYItem( SID_ATTR_TRANSFORM_POS_Y,static_cast<sal_uInt32>(lY));
+
+ GetBindings()->GetDispatcher()->ExecuteList(
+ SID_ATTR_TRANSFORM, SfxCallMode::RECORD, { &aPosYItem });
+ }
+}
+
+IMPL_LINK_NOARG( PosSizePropertyPanel, ClickAutoHdl, weld::Toggleable&, void )
+{
+ if ( mxCbxScale->get_active() )
+ {
+ mlOldWidth = std::max(GetCoreValue(*mxMtrWidth, mePoolUnit), SAL_CONST_INT64(1));
+ mlOldHeight = std::max(GetCoreValue(*mxMtrHeight, mePoolUnit), SAL_CONST_INT64(1));
+ }
+
+ // mxCbxScale must synchronized with that on Position and Size tabpage on Shape Properties dialog
+ SvtViewOptions aPageOpt(EViewType::TabPage, "cui/ui/possizetabpage/PositionAndSize");
+ aPageOpt.SetUserItem( USERITEM_NAME, css::uno::Any( OUString::number( int(mxCbxScale->get_active()) ) ) );
+}
+
+IMPL_LINK_NOARG( PosSizePropertyPanel, RotationHdl, DialControl&, void )
+{
+ Degree100 nTmp = mxCtrlDial->GetRotation();
+
+ // #i123993# Need to take UIScale into account when executing rotations
+ const double fUIScale(mpView && mpView->GetModel() ? double(mpView->GetModel()->GetUIScale()) : 1.0);
+ SdrAngleItem aAngleItem( SID_ATTR_TRANSFORM_ANGLE, nTmp);
+ SfxInt32Item aRotXItem( SID_ATTR_TRANSFORM_ROT_X, basegfx::fround(mlRotX * fUIScale));
+ SfxInt32Item aRotYItem( SID_ATTR_TRANSFORM_ROT_Y, basegfx::fround(mlRotY * fUIScale));
+
+ GetBindings()->GetDispatcher()->ExecuteList(SID_ATTR_TRANSFORM,
+ SfxCallMode::RECORD, { &aAngleItem, &aRotXItem, &aRotYItem });
+}
+
+IMPL_STATIC_LINK_NOARG( PosSizePropertyPanel, ClickObjectEditHdl, weld::Button&, void )
+{
+ SfxViewShell* pCurSh = SfxViewShell::Current();
+ if ( pCurSh)
+ {
+ pCurSh->DoVerb( -1 );
+ }
+}
+
+namespace
+{
+ void limitWidth(weld::MetricSpinButton& rMetricSpinButton)
+ {
+ // space is limited in the sidebar, so limit MetricSpinButtons to a width of 7 digits
+ const int nMaxDigits = 7;
+
+ weld::SpinButton& rSpinButton = rMetricSpinButton.get_widget();
+ rSpinButton.set_width_chars(std::min(rSpinButton.get_width_chars(), nMaxDigits));
+ }
+}
+
+void PosSizePropertyPanel::NotifyItemUpdate(
+ sal_uInt16 nSID,
+ SfxItemState eState,
+ const SfxPoolItem* pState)
+{
+ mxFtAngle->set_sensitive(true);
+ mxMtrAngle->set_sensitive(true);
+ mxDial->set_sensitive(true);
+ mxFtFlip->set_sensitive(true);
+ mxFlipTbx->set_sensitive(true);
+
+ const SfxUInt32Item* pWidthItem;
+ const SfxUInt32Item* pHeightItem;
+
+ SfxViewShell* pCurSh = SfxViewShell::Current();
+ if ( pCurSh )
+ mpView = pCurSh->GetDrawView();
+ else
+ mpView = nullptr;
+
+ if ( mpView == nullptr )
+ return;
+
+ mbAdjustEnabled = hasText(*mpView);
+
+ // Pool unit and dialog unit may have changed, make sure that we
+ // have the current values.
+ mePoolUnit = maTransfWidthControl.GetCoreMetric();
+
+ switch (nSID)
+ {
+ case SID_ATTR_TRANSFORM_WIDTH:
+ if ( SfxItemState::DEFAULT == eState )
+ {
+ pWidthItem = dynamic_cast< const SfxUInt32Item* >(pState);
+
+ if(pWidthItem)
+ {
+ tools::Long lOldWidth1 = tools::Long( pWidthItem->GetValue() / maUIScale );
+ SetFieldUnit( *mxMtrWidth, meDlgUnit, true );
+ SetMetricValue( *mxMtrWidth, lOldWidth1, mePoolUnit );
+ limitWidth(*mxMtrWidth);
+ mlOldWidth = lOldWidth1;
+ mxMtrWidth->save_value();
+ break;
+ }
+ }
+
+ mxMtrWidth->set_text( "" );
+ break;
+
+ case SID_ATTR_TRANSFORM_HEIGHT:
+ if ( SfxItemState::DEFAULT == eState )
+ {
+ pHeightItem = dynamic_cast< const SfxUInt32Item* >(pState);
+
+ if(pHeightItem)
+ {
+ tools::Long nTmp = tools::Long( pHeightItem->GetValue() / maUIScale);
+ SetFieldUnit( *mxMtrHeight, meDlgUnit, true );
+ SetMetricValue( *mxMtrHeight, nTmp, mePoolUnit );
+ limitWidth(*mxMtrHeight);
+ mlOldHeight = nTmp;
+ mxMtrHeight->save_value();
+ break;
+ }
+ }
+
+ mxMtrHeight->set_text( "");
+ break;
+
+ case SID_ATTR_TRANSFORM_POS_X:
+ if(SfxItemState::DEFAULT == eState)
+ {
+ const SfxInt32Item* pItem = dynamic_cast< const SfxInt32Item* >(pState);
+
+ if(pItem)
+ {
+ tools::Long nTmp = tools::Long(pItem->GetValue() / maUIScale);
+ SetFieldUnit( *mxMtrPosX, meDlgUnit, true );
+ SetMetricValue( *mxMtrPosX, nTmp, mePoolUnit );
+ limitWidth(*mxMtrPosX);
+ mxMtrPosX->save_value();
+ break;
+ }
+ }
+
+ mxMtrPosX->set_text( "" );
+ break;
+
+ case SID_ATTR_TRANSFORM_POS_Y:
+ if(SfxItemState::DEFAULT == eState)
+ {
+ const SfxInt32Item* pItem = dynamic_cast< const SfxInt32Item* >(pState);
+
+ if(pItem)
+ {
+ tools::Long nTmp = tools::Long(pItem->GetValue() / maUIScale);
+ SetFieldUnit( *mxMtrPosY, meDlgUnit, true );
+ SetMetricValue( *mxMtrPosY, nTmp, mePoolUnit );
+ limitWidth(*mxMtrPosY);
+ mxMtrPosY->save_value();
+ break;
+ }
+ }
+
+ mxMtrPosY->set_text( "" );
+ break;
+
+ case SID_ATTR_TRANSFORM_ROT_X:
+ if (SfxItemState::DEFAULT == eState)
+ {
+ const SfxInt32Item* pItem = dynamic_cast< const SfxInt32Item* >(pState);
+
+ if(pItem)
+ {
+ mlRotX = pItem->GetValue();
+ mlRotX = tools::Long( mlRotX / maUIScale );
+ }
+ }
+ break;
+
+ case SID_ATTR_TRANSFORM_ROT_Y:
+ if (SfxItemState::DEFAULT == eState)
+ {
+ const SfxInt32Item* pItem = dynamic_cast< const SfxInt32Item* >(pState);
+
+ if(pItem)
+ {
+ mlRotY = pItem->GetValue();
+ mlRotY = tools::Long( mlRotY / maUIScale );
+ }
+ }
+ break;
+
+ case SID_ATTR_TRANSFORM_PROTECT_POS:
+ if(SfxItemState::DEFAULT == eState)
+ {
+ const SfxBoolItem* pItem = dynamic_cast< const SfxBoolItem* >(pState);
+
+ if(pItem)
+ {
+ // record the state of position protect
+ mbPositionProtected = pItem->GetValue();
+ break;
+ }
+ }
+
+ mbPositionProtected = false;
+ break;
+
+ case SID_ATTR_TRANSFORM_PROTECT_SIZE:
+ if(SfxItemState::DEFAULT == eState)
+ {
+ const SfxBoolItem* pItem = dynamic_cast< const SfxBoolItem* >(pState);
+
+ if(pItem)
+ {
+ // record the state of size protect
+ mbSizeProtected = pItem->GetValue();
+ break;
+ }
+ }
+
+ mbSizeProtected = false;
+ break;
+
+ case SID_ATTR_TRANSFORM_AUTOWIDTH:
+ if(SfxItemState::DEFAULT == eState)
+ {
+ const SfxBoolItem* pItem = dynamic_cast< const SfxBoolItem* >(pState);
+
+ if(pItem)
+ {
+ mbAutoWidth = pItem->GetValue();
+ }
+ }
+ break;
+
+ case SID_ATTR_TRANSFORM_AUTOHEIGHT:
+ if(SfxItemState::DEFAULT == eState)
+ {
+ const SfxBoolItem* pItem = dynamic_cast< const SfxBoolItem* >(pState);
+
+ if(pItem)
+ {
+ mbAutoHeight = pItem->GetValue();
+ }
+ }
+ break;
+
+ case SID_ATTR_TRANSFORM_ANGLE:
+ if (eState >= SfxItemState::DEFAULT)
+ {
+ const SdrAngleItem* pItem = dynamic_cast< const SdrAngleItem* >(pState);
+
+ if(pItem)
+ {
+ Degree100 nTmp = NormAngle36000(pItem->GetValue());
+
+ mxMtrAngle->set_value(nTmp.get(), FieldUnit::DEGREE);
+ mxCtrlDial->SetRotation(nTmp);
+
+ break;
+ }
+ }
+
+ mxMtrAngle->set_text( "" );
+ mxCtrlDial->SetRotation( 0_deg100 );
+ break;
+
+ case SID_ATTR_METRIC:
+ MetricState( eState, pState );
+ UpdateUIScale();
+ break;
+
+ default:
+ break;
+ }
+
+ const sal_Int32 nCombinedContext(maContext.GetCombinedContext_DI());
+ const SdrMarkList& rMarkList = mpView->GetMarkedObjectList();
+
+ switch (rMarkList.GetMarkCount())
+ {
+ case 0:
+ break;
+
+ case 1:
+ {
+ const SdrObject* pObj = rMarkList.GetMark(0)->GetMarkedSdrObj();
+ const SdrObjKind eKind(pObj->GetObjIdentifier());
+
+ if(((nCombinedContext == CombinedEnumContext(Application::DrawImpress, Context::Draw)
+ || nCombinedContext == CombinedEnumContext(Application::DrawImpress, Context::TextObject)
+ ) && SdrObjKind::Edge == eKind)
+ || SdrObjKind::Caption == eKind)
+ {
+ mxFtAngle->set_sensitive(false);
+ mxMtrAngle->set_sensitive(false);
+ mxDial->set_sensitive(false);
+ mxFlipTbx->set_sensitive(false);
+ mxFtFlip->set_sensitive(false);
+ }
+ break;
+ }
+
+ default:
+ {
+ sal_uInt16 nMarkObj = 0;
+ bool isNoEdge = true;
+
+ while(isNoEdge && rMarkList.GetMark(nMarkObj))
+ {
+ const SdrObject* pObj = rMarkList.GetMark(nMarkObj)->GetMarkedSdrObj();
+ const SdrObjKind eKind(pObj->GetObjIdentifier());
+
+ if(((nCombinedContext == CombinedEnumContext(Application::DrawImpress, Context::Draw)
+ || nCombinedContext == CombinedEnumContext(Application::DrawImpress, Context::TextObject)
+ ) && SdrObjKind::Edge == eKind)
+ || SdrObjKind::Caption == eKind)
+ {
+ isNoEdge = false;
+ break;
+ }
+ nMarkObj++;
+ }
+
+ if(!isNoEdge)
+ {
+ mxFtAngle->set_sensitive(false);
+ mxMtrAngle->set_sensitive(false);
+ mxDial->set_sensitive(false);
+ mxFlipTbx->set_sensitive(false);
+ mxFtFlip->set_sensitive(false);
+ }
+ break;
+ }
+ }
+
+ if(nCombinedContext == CombinedEnumContext(Application::DrawImpress, Context::TextObject))
+ {
+ mxFlipTbx->set_sensitive(false);
+ mxFtFlip->set_sensitive(false);
+ }
+
+ DisableControls();
+
+ // mxCbxScale must synchronized with that on Position and Size tabpage on Shape Properties dialog
+ SvtViewOptions aPageOpt(EViewType::TabPage, "cui/ui/possizetabpage/PositionAndSize");
+ OUString sUserData;
+ css::uno::Any aUserItem = aPageOpt.GetUserItem( USERITEM_NAME );
+ OUString aTemp;
+ if ( aUserItem >>= aTemp )
+ sUserData = aTemp;
+ mxCbxScale->set_active(static_cast<bool>(sUserData.toInt32()));
+}
+
+void PosSizePropertyPanel::GetControlState(const sal_uInt16 nSID, boost::property_tree::ptree& rState)
+{
+ weld::MetricSpinButton* pControl = nullptr;
+ switch (nSID)
+ {
+ case SID_ATTR_TRANSFORM_POS_X:
+ pControl = mxMtrPosX.get();
+ break;
+ case SID_ATTR_TRANSFORM_POS_Y:
+ pControl = mxMtrPosY.get();
+ break;
+ case SID_ATTR_TRANSFORM_WIDTH:
+ pControl = mxMtrWidth.get();
+ break;
+ case SID_ATTR_TRANSFORM_HEIGHT:
+ pControl = mxMtrHeight.get();
+ break;
+ }
+
+ if (pControl && !pControl->get_text().isEmpty())
+ {
+ OUString sValue = Application::GetSettings().GetNeutralLocaleDataWrapper().
+ getNum(pControl->get_value(pControl->get_unit()), pControl->get_digits(), false, false);
+ rState.put(pControl->get_buildable_name().getStr(), sValue.toUtf8().getStr());
+ }
+}
+
+void PosSizePropertyPanel::executeSize()
+{
+ if ( !mxMtrWidth->get_value_changed_from_saved() && !mxMtrHeight->get_value_changed_from_saved())
+ return;
+
+ Fraction aUIScale = mpView->GetModel()->GetUIScale();
+
+ // get Width
+ double nWidth = static_cast<double>(mxMtrWidth->get_value(FieldUnit::MM_100TH));
+ tools::Long lWidth = tools::Long(nWidth * static_cast<double>(aUIScale));
+ lWidth = OutputDevice::LogicToLogic( lWidth, MapUnit::Map100thMM, mePoolUnit );
+ lWidth = static_cast<tools::Long>(mxMtrWidth->denormalize( lWidth ));
+
+ // get Height
+ double nHeight = static_cast<double>(mxMtrHeight->get_value(FieldUnit::MM_100TH));
+ tools::Long lHeight = tools::Long(nHeight * static_cast<double>(aUIScale));
+ lHeight = OutputDevice::LogicToLogic( lHeight, MapUnit::Map100thMM, mePoolUnit );
+ lHeight = static_cast<tools::Long>(mxMtrHeight->denormalize( lHeight ));
+
+ // put Width & Height to itemset
+ SfxUInt32Item aWidthItem( SID_ATTR_TRANSFORM_WIDTH, static_cast<sal_uInt32>(lWidth));
+ SfxUInt32Item aHeightItem( SID_ATTR_TRANSFORM_HEIGHT, static_cast<sal_uInt32>(lHeight));
+ SfxUInt16Item aPointItem (SID_ATTR_TRANSFORM_SIZE_POINT, sal_uInt16(RectPoint::LT));
+ const sal_Int32 nCombinedContext(maContext.GetCombinedContext_DI());
+
+ if( nCombinedContext == CombinedEnumContext(Application::WriterVariants, Context::Graphic)
+ || nCombinedContext == CombinedEnumContext(Application::WriterVariants, Context::OLE)
+ )
+ {
+ GetBindings()->GetDispatcher()->ExecuteList(SID_ATTR_TRANSFORM,
+ SfxCallMode::RECORD, { &aWidthItem, &aHeightItem, &aPointItem });
+ }
+ else
+ {
+ if ( (mxMtrWidth->get_value_changed_from_saved()) && (mxMtrHeight->get_value_changed_from_saved()))
+ GetBindings()->GetDispatcher()->ExecuteList(SID_ATTR_TRANSFORM,
+ SfxCallMode::RECORD, { &aWidthItem, &aHeightItem, &aPointItem });
+ else if( mxMtrWidth->get_value_changed_from_saved())
+ GetBindings()->GetDispatcher()->ExecuteList(SID_ATTR_TRANSFORM,
+ SfxCallMode::RECORD, { &aWidthItem, &aPointItem });
+ else if ( mxMtrHeight->get_value_changed_from_saved())
+ GetBindings()->GetDispatcher()->ExecuteList(SID_ATTR_TRANSFORM,
+ SfxCallMode::RECORD, { &aHeightItem, &aPointItem });
+ }
+}
+
+void PosSizePropertyPanel::DumpAsPropertyTree(tools::JsonWriter& rJsonWriter)
+{
+ if (meDlgUnit != GetCurrentUnit(SfxItemState::DEFAULT, nullptr))
+ {
+ mpBindings->Update( SID_ATTR_METRIC );
+ }
+
+ PanelLayout::DumpAsPropertyTree(rJsonWriter);
+}
+
+void PosSizePropertyPanel::MetricState( SfxItemState eState, const SfxPoolItem* pState )
+{
+ bool bPosXBlank = false;
+ bool bPosYBlank = false;
+ bool bWidthBlank = false;
+ bool bHeightBlank = false;
+
+ // #i124409# use the given Item to get the correct UI unit and initialize it
+ // and the Fields using it
+ meDlgUnit = GetCurrentUnit(eState,pState);
+
+ if (mxMtrPosX->get_text().isEmpty())
+ bPosXBlank = true;
+ SetFieldUnit( *mxMtrPosX, meDlgUnit, true );
+ if(bPosXBlank)
+ mxMtrPosX->set_text(OUString());
+
+ if (mxMtrPosY->get_text().isEmpty())
+ bPosYBlank = true;
+ SetFieldUnit( *mxMtrPosY, meDlgUnit, true );
+ if(bPosYBlank)
+ mxMtrPosY->set_text(OUString());
+ SetPosSizeMinMax();
+
+ if (mxMtrWidth->get_text().isEmpty())
+ bWidthBlank = true;
+ SetFieldUnit( *mxMtrWidth, meDlgUnit, true );
+ if(bWidthBlank)
+ mxMtrWidth->set_text(OUString());
+
+ if (mxMtrHeight->get_text().isEmpty())
+ bHeightBlank = true;
+ SetFieldUnit( *mxMtrHeight, meDlgUnit, true );
+ if(bHeightBlank)
+ mxMtrHeight->set_text(OUString());
+}
+
+
+FieldUnit PosSizePropertyPanel::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("svx.sidebar", "GetModuleFieldUnit(): no module found");
+ }
+ }
+ }
+
+ return eUnit;
+}
+
+
+void PosSizePropertyPanel::DisableControls()
+{
+ if( mbPositionProtected )
+ {
+ // the position is protected("Position protect" option in modal dialog is checked),
+ // disable all the Position controls in sidebar
+ mxFtPosX->set_sensitive(false);
+ mxMtrPosX->set_sensitive(false);
+ mxFtPosY->set_sensitive(false);
+ mxMtrPosY->set_sensitive(false);
+ mxFtAngle->set_sensitive(false);
+ mxMtrAngle->set_sensitive(false);
+ mxDial->set_sensitive(false);
+ mxFtFlip->set_sensitive(false);
+ mxFlipTbx->set_sensitive(false);
+
+ mxFtWidth->set_sensitive(false);
+ mxMtrWidth->set_sensitive(false);
+ mxFtHeight->set_sensitive(false);
+ mxMtrHeight->set_sensitive(false);
+ mxCbxScale->set_sensitive(false);
+ }
+ else
+ {
+ mxFtPosX->set_sensitive(true);
+ mxMtrPosX->set_sensitive(true);
+ mxFtPosY->set_sensitive(true);
+ mxMtrPosY->set_sensitive(true);
+
+ if( mbSizeProtected )
+ {
+ mxFtWidth->set_sensitive(false);
+ mxMtrWidth->set_sensitive(false);
+ mxFtHeight->set_sensitive(false);
+ mxMtrHeight->set_sensitive(false);
+ mxCbxScale->set_sensitive(false);
+ }
+ else
+ {
+ if( mbAdjustEnabled )
+ {
+ if( mbAutoWidth )
+ {
+ mxFtWidth->set_sensitive(false);
+ mxMtrWidth->set_sensitive(false);
+ mxCbxScale->set_sensitive(false);
+ }
+ else
+ {
+ mxFtWidth->set_sensitive(true);
+ mxMtrWidth->set_sensitive(true);
+ }
+ if( mbAutoHeight )
+ {
+ mxFtHeight->set_sensitive(false);
+ mxMtrHeight->set_sensitive(false);
+ mxCbxScale->set_sensitive(false);
+ }
+ else
+ {
+ mxFtHeight->set_sensitive(true);
+ mxMtrHeight->set_sensitive(true);
+ }
+ if( !mbAutoWidth && !mbAutoHeight )
+ mxCbxScale->set_sensitive(true);
+ }
+ else
+ {
+ mxFtWidth->set_sensitive(true);
+ mxMtrWidth->set_sensitive(true);
+ mxFtHeight->set_sensitive(true);
+ mxMtrHeight->set_sensitive(true);
+ mxCbxScale->set_sensitive(true);
+ }
+ }
+ }
+}
+
+void PosSizePropertyPanel::SetPosSizeMinMax()
+{
+ SdrPageView* pPV = mpView->GetSdrPageView();
+ if (!pPV)
+ return;
+ tools::Rectangle aTmpRect(mpView->GetAllMarkedRect());
+ pPV->LogicToPagePos(aTmpRect);
+ maRect = vcl::unotools::b2DRectangleFromRectangle(aTmpRect);
+
+ tools::Rectangle aTmpRect2(mpView->GetWorkArea());
+ pPV->LogicToPagePos(aTmpRect2);
+ maWorkArea = vcl::unotools::b2DRectangleFromRectangle(aTmpRect2);
+
+ const Fraction aUIScale(mpView->GetModel()->GetUIScale());
+ TransfrmHelper::ScaleRect( maWorkArea, aUIScale );
+ TransfrmHelper::ScaleRect( maRect, aUIScale );
+
+ const sal_uInt16 nDigits(mxMtrPosX->get_digits());
+ TransfrmHelper::ConvertRect( maWorkArea, nDigits, mePoolUnit, meDlgUnit );
+ TransfrmHelper::ConvertRect( maRect, nDigits, mePoolUnit, meDlgUnit );
+
+ double fLeft(maWorkArea.getMinX());
+ double fTop(maWorkArea.getMinY());
+ double fRight(maWorkArea.getMaxX());
+ double fBottom(maWorkArea.getMaxY());
+
+ // seems that sidebar defaults to top left reference point
+ // and there's no way to set it to something else
+ fRight -= maRect.getWidth();
+ fBottom -= maRect.getHeight();
+
+ const double fMaxLong(static_cast<double>(vcl::ConvertValue( LONG_MAX, 0, MapUnit::Map100thMM, meDlgUnit ) - 1));
+ fLeft = std::clamp(fLeft, -fMaxLong, fMaxLong);
+ fRight = std::clamp(fRight, -fMaxLong, fMaxLong);
+ fTop = std::clamp(fTop, - fMaxLong, fMaxLong);
+ fBottom = std::clamp(fBottom, -fMaxLong, fMaxLong);
+
+ mxMtrPosX->set_range(basegfx::fround64(fLeft), basegfx::fround64(fRight), FieldUnit::NONE);
+ limitWidth(*mxMtrPosX);
+ mxMtrPosY->set_range(basegfx::fround64(fTop), basegfx::fround64(fBottom), FieldUnit::NONE);
+ limitWidth(*mxMtrPosY);
+
+ double fMaxWidth = maWorkArea.getWidth() - (maRect.getWidth() - fLeft);
+ double fMaxHeight = maWorkArea.getHeight() - (maRect.getHeight() - fTop);
+ mxMtrWidth->set_max(std::min<sal_Int64>(INT_MAX, basegfx::fround64(fMaxWidth*100)), FieldUnit::NONE);
+ limitWidth(*mxMtrWidth);
+ mxMtrHeight->set_max(std::min<sal_Int64>(INT_MAX, basegfx::fround64(fMaxHeight*100)), FieldUnit::NONE);
+ limitWidth(*mxMtrHeight);
+}
+
+void PosSizePropertyPanel::UpdateUIScale()
+{
+ const Fraction aUIScale (mpView->GetModel()->GetUIScale());
+ if (maUIScale == aUIScale)
+ return;
+
+ // UI scale has changed.
+
+ // Remember the new UI scale.
+ maUIScale = aUIScale;
+
+ // The content of the position and size boxes is only updated when item changes are notified.
+ // Request such notifications without changing the actual item values.
+ GetBindings()->Invalidate(SID_ATTR_TRANSFORM_POS_X, true);
+ GetBindings()->Invalidate(SID_ATTR_TRANSFORM_POS_Y, true);
+ GetBindings()->Invalidate(SID_ATTR_TRANSFORM_WIDTH, true);
+ GetBindings()->Invalidate(SID_ATTR_TRANSFORM_HEIGHT, true);
+}
+
+
+} // end of namespace svx::sidebar
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/sidebar/possize/PosSizePropertyPanel.hxx b/svx/source/sidebar/possize/PosSizePropertyPanel.hxx
new file mode 100644
index 000000000..ede3d3e44
--- /dev/null
+++ b/svx/source/sidebar/possize/PosSizePropertyPanel.hxx
@@ -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 .
+ */
+#ifndef INCLUDED_SVX_SOURCE_SIDEBAR_POSSIZE_POSSIZEPROPERTYPANEL_HXX
+#define INCLUDED_SVX_SOURCE_SIDEBAR_POSSIZE_POSSIZEPROPERTYPANEL_HXX
+
+#include <sfx2/sidebar/ControllerItem.hxx>
+#include <sfx2/sidebar/IContextChangeReceiver.hxx>
+#include <sfx2/weldutils.hxx>
+#include <sfx2/sidebar/PanelLayout.hxx>
+#include <svl/poolitem.hxx>
+#include <tools/fldunit.hxx>
+#include <tools/fract.hxx>
+#include <com/sun/star/ui/XSidebar.hpp>
+#include <basegfx/range/b2drange.hxx>
+#include <vcl/EnumContext.hxx>
+#include <vcl/customweld.hxx>
+#include <vcl/weld.hxx>
+
+class SdrView;
+
+namespace svx {
+class DialControl;
+};
+
+namespace svx::sidebar {
+
+class PosSizePropertyPanel
+: public PanelLayout,
+ public ::sfx2::sidebar::IContextChangeReceiver,
+ public ::sfx2::sidebar::ControllerItem::ItemUpdateReceiverInterface
+{
+public:
+ virtual ~PosSizePropertyPanel() override;
+
+ static std::unique_ptr<PanelLayout> Create(
+ weld::Widget* pParent,
+ const css::uno::Reference<css::frame::XFrame>& rxFrame,
+ SfxBindings* pBindings,
+ const css::uno::Reference<css::ui::XSidebar>& rxSidebar);
+
+ virtual void HandleContextChange(
+ const vcl::EnumContext& rContext) override;
+
+ virtual void NotifyItemUpdate(
+ const sal_uInt16 nSId,
+ const SfxItemState eState,
+ const SfxPoolItem* pState) override;
+
+ virtual void DumpAsPropertyTree(tools::JsonWriter&) override;
+
+ SfxBindings* GetBindings() { return mpBindings;}
+
+ // constructor/destructor
+ PosSizePropertyPanel(
+ weld::Widget* pParent,
+ const css::uno::Reference<css::frame::XFrame>& rxFrame,
+ SfxBindings* pBindings,
+ const css::uno::Reference<css::ui::XSidebar>& rxSidebar);
+
+ virtual void GetControlState(
+ const sal_uInt16 nSId,
+ boost::property_tree::ptree& rState) override;
+
+private:
+ //Position
+ std::unique_ptr<weld::Label> mxFtPosX;
+ std::unique_ptr<weld::MetricSpinButton> mxMtrPosX;
+ std::unique_ptr<weld::Label> mxFtPosY;
+ std::unique_ptr<weld::MetricSpinButton> mxMtrPosY;
+
+ // size
+ std::unique_ptr<weld::Label> mxFtWidth;
+ std::unique_ptr<weld::MetricSpinButton> mxMtrWidth;
+ std::unique_ptr<weld::Label> mxFtHeight;
+ std::unique_ptr<weld::MetricSpinButton> mxMtrHeight;
+ std::unique_ptr<weld::CheckButton> mxCbxScale;
+
+ //rotation
+ std::unique_ptr<weld::Label> mxFtAngle;
+ std::unique_ptr<weld::MetricSpinButton> mxMtrAngle;
+
+ //rotation control
+ std::unique_ptr<svx::DialControl> mxCtrlDial;
+ std::unique_ptr<weld::CustomWeld> mxDial;
+
+ //flip
+ std::unique_ptr<weld::Label> mxFtFlip;
+ std::unique_ptr<weld::Toolbar> mxFlipTbx;
+ std::unique_ptr<ToolbarUnoDispatcher> mxFlipDispatch;
+
+ std::unique_ptr<weld::Toolbar> mxArrangeTbx;
+ std::unique_ptr<ToolbarUnoDispatcher> mxArrangeDispatch;
+ std::unique_ptr<weld::Toolbar> mxArrangeTbx2;
+ std::unique_ptr<ToolbarUnoDispatcher> mxArrangeDispatch2;
+
+ std::unique_ptr<weld::Toolbar> mxAlignTbx;
+ std::unique_ptr<ToolbarUnoDispatcher> mxAlignDispatch;
+ std::unique_ptr<weld::Toolbar> mxAlignTbx2;
+ std::unique_ptr<ToolbarUnoDispatcher> mxAlignDispatch2;
+
+ //edit objects button for online's mobile view
+ std::unique_ptr<weld::Button> mxBtnEditOLEObject;
+
+ // Internal variables
+ basegfx::B2DRange maRect;
+ basegfx::B2DRange maWorkArea;
+ const SdrView* mpView;
+ sal_uInt32 mlOldWidth;
+ sal_uInt32 mlOldHeight;
+ tools::Long mlRotX;
+ tools::Long mlRotY;
+ Fraction maUIScale;
+ MapUnit mePoolUnit;
+ FieldUnit meDlgUnit;
+
+ // Controller Items
+ ::sfx2::sidebar::ControllerItem maTransfPosXControl;
+ ::sfx2::sidebar::ControllerItem maTransfPosYControl;
+ ::sfx2::sidebar::ControllerItem maTransfWidthControl;
+ ::sfx2::sidebar::ControllerItem maTransfHeightControl;
+
+ ::sfx2::sidebar::ControllerItem maSvxAngleControl;
+ ::sfx2::sidebar::ControllerItem maRotXControl;
+ ::sfx2::sidebar::ControllerItem maRotYControl;
+ ::sfx2::sidebar::ControllerItem maProPosControl;
+ ::sfx2::sidebar::ControllerItem maProSizeControl;
+ ::sfx2::sidebar::ControllerItem maAutoWidthControl;
+ ::sfx2::sidebar::ControllerItem maAutoHeightControl;
+ ::sfx2::sidebar::ControllerItem m_aMetricCtl;
+
+ vcl::EnumContext maContext;
+ SfxBindings* mpBindings;
+
+ bool mbSizeProtected : 1;
+ bool mbPositionProtected : 1;
+ bool mbAutoWidth : 1;
+ bool mbAutoHeight : 1;
+ bool mbAdjustEnabled : 1;
+
+ css::uno::Reference<css::ui::XSidebar> mxSidebar;
+
+ DECL_LINK( ChangePosXHdl, weld::MetricSpinButton&, void );
+ DECL_LINK( ChangePosYHdl, weld::MetricSpinButton&, void );
+ DECL_LINK( ChangeWidthHdl, weld::MetricSpinButton&, void );
+ DECL_LINK( ChangeHeightHdl, weld::MetricSpinButton&, void );
+ DECL_LINK( ClickAutoHdl, weld::Toggleable&, void );
+ DECL_LINK( RotationHdl, svx::DialControl&, void );
+ DECL_STATIC_LINK( PosSizePropertyPanel, ClickObjectEditHdl, weld::Button&, void );
+
+ void Initialize();
+ void executeSize();
+
+ void MetricState( SfxItemState eState, const SfxPoolItem* pState );
+ static FieldUnit GetCurrentUnit( SfxItemState eState, const SfxPoolItem* pState );
+ void DisableControls();
+ void SetPosSizeMinMax();
+
+ /** Check if the UI scale has changed and handle such a change.
+ UI scale is an SD only feature. The UI scale is represented by items
+ ATTR_OPTIONS_SCALE_X and
+ ATTR_OPTIONS_SCALE_Y.
+ As we have no direct access (there is no dependency of svx on sd) we have to
+ use a small trick (aka hack):
+ a) call this method whenever a change of the metric item is notified,
+ b) check if the UI scale has changed (strangely, the UI scale value is available at the SdrModel.
+ c) invalidate the items for position and size to trigger notifications of their current values.
+ */
+ void UpdateUIScale();
+};
+
+
+} // end of namespace svx::sidebar
+
+
+#endif // INCLUDED_SVX_SOURCE_SIDEBAR_POSSIZE_POSSIZEPROPERTYPANEL_HXX
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/sidebar/shadow/ShadowPropertyPanel.cxx b/svx/source/sidebar/shadow/ShadowPropertyPanel.cxx
new file mode 100644
index 000000000..93259adc2
--- /dev/null
+++ b/svx/source/sidebar/shadow/ShadowPropertyPanel.cxx
@@ -0,0 +1,362 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+#include "ShadowPropertyPanel.hxx"
+#include <vcl/settings.hxx>
+#include <vcl/svapp.hxx>
+#include <svx/colorbox.hxx>
+#include <svx/svxids.hrc>
+#include <sfx2/bindings.hxx>
+#include <sfx2/dispatch.hxx>
+#include <unotools/localedatawrapper.hxx>
+#include <svx/sdooitm.hxx>
+#include <svx/sdshitm.hxx>
+#include <svx/sdshtitm.hxx>
+#include <svx/sdprcitm.hxx>
+#include <svx/sdsxyitm.hxx>
+#include <svx/sdshcitm.hxx>
+#include <comphelper/lok.hxx>
+
+using namespace css;
+using namespace css::uno;
+
+namespace svx::sidebar {
+
+ShadowPropertyPanel::ShadowPropertyPanel(
+ weld::Widget* pParent,
+ SfxBindings* pBindings)
+: PanelLayout(pParent, "ShadowPropertyPanel", "svx/ui/sidebarshadow.ui"),
+ maShadowController(SID_ATTR_FILL_SHADOW, *pBindings, *this),
+ maShadowTransController(SID_ATTR_SHADOW_TRANSPARENCE, *pBindings, *this),
+ maShadowBlurController(SID_ATTR_SHADOW_BLUR, *pBindings, *this),
+ maShadowColorController(SID_ATTR_SHADOW_COLOR, *pBindings, *this),
+ maShadowXDistanceController(SID_ATTR_SHADOW_XDISTANCE, *pBindings, *this),
+ maShadowYDistanceController(SID_ATTR_SHADOW_YDISTANCE, *pBindings, *this),
+ mpBindings(pBindings),
+ nX(0),
+ nY(0),
+ nXY(0),
+ mxShowShadow(m_xBuilder->weld_check_button("SHOW_SHADOW")),
+ mxShadowDistance(m_xBuilder->weld_metric_spin_button("LB_DISTANCE", FieldUnit::POINT)),
+ mxLBShadowColor(new ColorListBox(m_xBuilder->weld_menu_button("LB_SHADOW_COLOR"), [this]{ return GetFrameWeld(); })),
+ mxShadowAngle(m_xBuilder->weld_combo_box("LB_ANGLE")),
+ mxFTAngle(m_xBuilder->weld_label("angle")),
+ mxFTDistance(m_xBuilder->weld_label("distance")),
+ mxFTTransparency(m_xBuilder->weld_label("transparency_label")),
+ mxFTBlur(m_xBuilder->weld_label("blur_label")),
+ mxFTColor(m_xBuilder->weld_label("color")),
+ mxShadowTransSlider(m_xBuilder->weld_scale("transparency_slider")),
+ mxShadowTransMetric(m_xBuilder->weld_metric_spin_button("FIELD_TRANSPARENCY", FieldUnit::PERCENT)),
+ mxShadowBlurMetric(m_xBuilder->weld_metric_spin_button("LB_SHADOW_BLUR", FieldUnit::POINT))
+{
+ Initialize();
+}
+
+ShadowPropertyPanel::~ShadowPropertyPanel()
+{
+ mxShowShadow.reset();
+ mxFTAngle.reset();
+ mxShadowAngle.reset();
+ mxFTDistance.reset();
+ mxShadowDistance.reset();
+ mxFTTransparency.reset();
+ mxShadowTransSlider.reset();
+ mxShadowTransMetric.reset();
+ mxShadowBlurMetric.reset();
+ mxFTBlur.reset();
+ mxFTColor.reset();
+ mxLBShadowColor.reset();
+
+ maShadowController.dispose();
+ maShadowTransController.dispose();
+ maShadowBlurController.dispose();
+ maShadowColorController.dispose();
+ maShadowXDistanceController.dispose();
+ maShadowYDistanceController.dispose();
+}
+
+void ShadowPropertyPanel::Initialize()
+{
+ mxShowShadow->set_state( TRISTATE_FALSE );
+ mxShowShadow->connect_toggled( LINK(this, ShadowPropertyPanel, ClickShadowHdl ) );
+ mxShadowTransMetric->connect_value_changed( LINK(this, ShadowPropertyPanel, ModifyShadowTransMetricHdl) );
+ mxLBShadowColor->SetSelectHdl( LINK( this, ShadowPropertyPanel, ModifyShadowColorHdl ) );
+ mxShadowAngle->connect_changed( LINK(this, ShadowPropertyPanel, ModifyShadowAngleHdl) );
+ mxShadowDistance->connect_value_changed( LINK(this, ShadowPropertyPanel, ModifyShadowDistanceHdl) );
+ mxShadowTransSlider->set_range(0, 100);
+ mxShadowTransSlider->connect_value_changed( LINK(this, ShadowPropertyPanel, ModifyShadowTransSliderHdl) );
+ mxShadowBlurMetric->set_range(0, 150, FieldUnit::POINT);
+ mxShadowBlurMetric->connect_value_changed(LINK(this, ShadowPropertyPanel, ModifyShadowBlurMetricHdl));
+ InsertAngleValues();
+}
+
+IMPL_LINK_NOARG(ShadowPropertyPanel, ClickShadowHdl, weld::Toggleable&, void)
+{
+ if( mxShowShadow->get_state() == TRISTATE_FALSE )
+ {
+ SdrOnOffItem aItem(makeSdrShadowItem(false));
+ GetBindings()->GetDispatcher()->ExecuteList(SID_ATTR_FILL_SHADOW,
+ SfxCallMode::RECORD, { &aItem });
+
+ if (comphelper::LibreOfficeKit::isActive())
+ {
+ mxShowShadow->set_state( TRISTATE_FALSE );
+ UpdateControls();
+ }
+ }
+ else
+ {
+ SdrOnOffItem aItem(makeSdrShadowItem(true));
+ GetBindings()->GetDispatcher()->ExecuteList(SID_ATTR_FILL_SHADOW,
+ SfxCallMode::RECORD, { &aItem });
+
+ if (mxShadowDistance->get_value(FieldUnit::POINT) == 0)
+ mxShadowDistance->set_value( 8, FieldUnit::POINT );
+
+ if (comphelper::LibreOfficeKit::isActive())
+ {
+ mxShowShadow->set_state( TRISTATE_TRUE );
+ UpdateControls();
+ }
+ }
+}
+
+IMPL_LINK_NOARG(ShadowPropertyPanel, ModifyShadowColorHdl, ColorListBox&, void)
+{
+ XColorItem aItem(makeSdrShadowColorItem(mxLBShadowColor->GetSelectEntryColor()));
+ GetBindings()->GetDispatcher()->ExecuteList(SID_ATTR_SHADOW_COLOR,
+ SfxCallMode::RECORD, { &aItem });
+}
+
+IMPL_LINK_NOARG(ShadowPropertyPanel, ModifyShadowTransMetricHdl, weld::MetricSpinButton&, void)
+{
+ sal_uInt16 nVal = mxShadowTransMetric->get_value(FieldUnit::PERCENT);
+ SetTransparencyValue(nVal);
+ SdrPercentItem aItem( makeSdrShadowTransparenceItem(nVal) );
+ GetBindings()->GetDispatcher()->ExecuteList(SID_ATTR_SHADOW_TRANSPARENCE,
+ SfxCallMode::RECORD, { &aItem });
+}
+
+IMPL_LINK_NOARG(ShadowPropertyPanel, ModifyShadowBlurMetricHdl, weld::MetricSpinButton&, void)
+{
+ SdrMetricItem aItem(SDRATTR_SHADOWBLUR, mxShadowBlurMetric->get_value(FieldUnit::MM_100TH));
+
+ mpBindings->GetDispatcher()->ExecuteList(SID_ATTR_SHADOW_BLUR, SfxCallMode::RECORD, { &aItem });
+}
+
+IMPL_LINK_NOARG(ShadowPropertyPanel, ModifyShadowTransSliderHdl, weld::Scale&, void)
+{
+ sal_uInt16 nVal = mxShadowTransSlider->get_value();
+ SetTransparencyValue(nVal);
+ SdrPercentItem aItem( makeSdrShadowTransparenceItem(nVal) );
+ GetBindings()->GetDispatcher()->ExecuteList(SID_ATTR_SHADOW_TRANSPARENCE,
+ SfxCallMode::RECORD, { &aItem });
+}
+
+IMPL_LINK_NOARG(ShadowPropertyPanel, ModifyShadowAngleHdl, weld::ComboBox&, void)
+{
+ ModifyShadowDistance();
+}
+
+IMPL_LINK_NOARG(ShadowPropertyPanel, ModifyShadowDistanceHdl, weld::MetricSpinButton&, void)
+{
+ ModifyShadowDistance();
+}
+
+void ShadowPropertyPanel::ModifyShadowDistance()
+{
+ auto nAngle = mxShadowAngle->get_active_id().toInt32();
+ nXY = mxShadowDistance->get_value(FieldUnit::MM_100TH);
+ switch (nAngle)
+ {
+ case 0: nX = nXY; nY = 0; break;
+ case 45: nX = nXY; nY = -nXY; break;
+ case 90: nX = 0; nY = - nXY; break;
+ case 135: nX = nY = -nXY; break;
+ case 180: nX = -nXY; nY = 0; break;
+ case 225: nX = -nXY; nY = nXY; break;
+ case 270: nX = 0; nY = nXY; break;
+ case 315: nX = nY = nXY; break;
+ }
+ SdrMetricItem aXItem(makeSdrShadowXDistItem(nX));
+ SdrMetricItem aYItem(makeSdrShadowYDistItem(nY));
+ GetBindings()->GetDispatcher()->ExecuteList(SID_ATTR_SHADOW_XDISTANCE,
+ SfxCallMode::RECORD, { &aXItem });
+ GetBindings()->GetDispatcher()->ExecuteList(SID_ATTR_SHADOW_YDISTANCE,
+ SfxCallMode::RECORD, { &aYItem });
+}
+
+void ShadowPropertyPanel::UpdateControls()
+{
+ if (mxShowShadow->get_state() == TRISTATE_FALSE)
+ {
+ mxShadowDistance->set_sensitive(false);
+ mxLBShadowColor->set_sensitive(false);
+ mxShadowAngle->set_sensitive(false);
+ mxFTAngle->set_sensitive(false);
+ mxFTDistance->set_sensitive(false);
+ mxFTTransparency->set_sensitive(false);
+ mxFTBlur->set_sensitive(false);
+ mxFTColor->set_sensitive(false);
+ mxShadowTransSlider->set_sensitive(false);
+ mxShadowTransMetric->set_sensitive(false);
+ mxShadowBlurMetric->set_sensitive(false);
+
+ return;
+ }
+ else
+ {
+ mxShadowDistance->set_sensitive(true);
+ mxLBShadowColor->set_sensitive(true);
+ mxShadowAngle->set_sensitive(true);
+ mxFTAngle->set_sensitive(true);
+ mxFTDistance->set_sensitive(true);
+ mxFTTransparency->set_sensitive(true);
+ mxFTBlur->set_sensitive(true);
+ mxFTColor->set_sensitive(true);
+ mxShadowTransSlider->set_sensitive(true);
+ mxShadowTransMetric->set_sensitive(true);
+ mxShadowBlurMetric->set_sensitive(true);
+ }
+
+ if(nX > 0 && nY == 0) { mxShadowAngle->set_active(0); nXY = nX; }
+ else if( nX > 0 && nY < 0 ) { mxShadowAngle->set_active(1); nXY = nX; }
+ else if( nX == 0 && nY < 0 ) { mxShadowAngle->set_active(2); nXY = -nY; }
+ else if( nX < 0 && nY < 0 ) { mxShadowAngle->set_active(3); nXY = -nY; }
+ else if( nX < 0 && nY == 0 ) { mxShadowAngle->set_active(4); nXY = -nX; }
+ else if( nX < 0 && nY > 0 ) { mxShadowAngle->set_active(5); nXY = nY; }
+ else if( nX == 0 && nY > 0 ) { mxShadowAngle->set_active(6); nXY = nY; }
+ else if( nX > 0 && nY > 0 ) { mxShadowAngle->set_active(7); nXY = nX; }
+ else { nXY = 0; }
+ mxShadowDistance->set_value(nXY, FieldUnit::MM_100TH);
+}
+
+void ShadowPropertyPanel::SetTransparencyValue(tools::Long nVal)
+{
+ mxShadowTransSlider->set_value(nVal);
+ mxShadowTransMetric->set_value(nVal, FieldUnit::PERCENT);
+}
+
+void ShadowPropertyPanel::InsertAngleValues()
+{
+ OUString sSuffix = weld::MetricSpinButton::MetricToString(FieldUnit::DEGREE);
+
+ const LocaleDataWrapper& rLocaleData = Application::GetSettings().GetLocaleDataWrapper();
+
+ mxShadowAngle->append(OUString::number(0), rLocaleData.getNum(0, 0, true, true) + sSuffix);
+ mxShadowAngle->append(OUString::number(45), rLocaleData.getNum(45, 0, true, true) + sSuffix);
+ mxShadowAngle->append(OUString::number(90), rLocaleData.getNum(90, 0, true, true) + sSuffix);
+ mxShadowAngle->append(OUString::number(135), rLocaleData.getNum(135, 0, true, true) + sSuffix);
+ mxShadowAngle->append(OUString::number(180), rLocaleData.getNum(180, 0, true, true) + sSuffix);
+ mxShadowAngle->append(OUString::number(225), rLocaleData.getNum(225, 0, true, true) + sSuffix);
+ mxShadowAngle->append(OUString::number(270), rLocaleData.getNum(270, 0, true, true) + sSuffix);
+ mxShadowAngle->append(OUString::number(315), rLocaleData.getNum(315, 0, true, true) + sSuffix);
+}
+
+void ShadowPropertyPanel::NotifyItemUpdate(
+ sal_uInt16 nSID,
+ SfxItemState eState,
+ const SfxPoolItem* pState)
+{
+ switch(nSID)
+ {
+ case SID_ATTR_FILL_SHADOW:
+ {
+ if(eState >= SfxItemState::DEFAULT)
+ {
+ const SdrOnOffItem* pItem = dynamic_cast< const SdrOnOffItem* >(pState);
+ if (pItem && pItem->GetValue())
+ mxShowShadow->set_state(TRISTATE_TRUE);
+ else
+ mxShowShadow->set_state(TRISTATE_FALSE);
+ }
+ }
+ break;
+
+ case SID_ATTR_SHADOW_TRANSPARENCE:
+ {
+ if(eState >= SfxItemState::DEFAULT)
+ {
+ const SdrPercentItem* pTransparencyItem = dynamic_cast< const SdrPercentItem* >(pState);
+ if(pTransparencyItem)
+ {
+ const sal_uInt16 nVal = pTransparencyItem->GetValue();
+ SetTransparencyValue(nVal);
+ }
+ else
+ SetTransparencyValue(0);
+ }
+ }
+ break;
+ case SID_ATTR_SHADOW_BLUR:
+ {
+ if (eState >= SfxItemState::DEFAULT)
+ {
+ const SdrMetricItem* pRadiusItem = dynamic_cast<const SdrMetricItem*>(pState);
+ if (pRadiusItem)
+ {
+ mxShadowBlurMetric->set_value(pRadiusItem->GetValue(), FieldUnit::MM_100TH);
+ }
+ }
+ }
+ break;
+ case SID_ATTR_SHADOW_COLOR:
+ {
+ if(eState >= SfxItemState::DEFAULT)
+ {
+ const XColorItem* pColorItem = dynamic_cast< const XColorItem* >(pState);
+ if(pColorItem)
+ {
+ mxLBShadowColor->SelectEntry(pColorItem->GetColorValue());
+ }
+ }
+ }
+ break;
+ case SID_ATTR_SHADOW_XDISTANCE:
+ {
+ if(eState >= SfxItemState::DEFAULT)
+ {
+ const SdrMetricItem* pXDistItem = dynamic_cast< const SdrMetricItem* >(pState);
+ if(pXDistItem)
+ {
+ nX = pXDistItem->GetValue();
+ }
+ }
+ }
+ break;
+ case SID_ATTR_SHADOW_YDISTANCE:
+ {
+ if(eState >= SfxItemState::DEFAULT)
+ {
+ const SdrMetricItem* pYDistItem = dynamic_cast< const SdrMetricItem* >(pState);
+ if(pYDistItem)
+ {
+ nY = pYDistItem->GetValue();
+ }
+ }
+ }
+ break;
+ }
+ UpdateControls();
+}
+
+std::unique_ptr<PanelLayout> ShadowPropertyPanel::Create (
+ weld::Widget* pParent,
+ SfxBindings* pBindings)
+{
+ if(pParent == nullptr)
+ throw lang::IllegalArgumentException("no parent Window given to ShadowPropertyPanel::Create", nullptr, 0);
+ if(pBindings == nullptr)
+ throw lang::IllegalArgumentException("no SfxBindings given to ShadowPropertyPanel::Create", nullptr, 2);
+
+ return std::make_unique<ShadowPropertyPanel>(pParent, pBindings);
+}
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/sidebar/shadow/ShadowPropertyPanel.hxx b/svx/source/sidebar/shadow/ShadowPropertyPanel.hxx
new file mode 100644
index 000000000..a68405eb0
--- /dev/null
+++ b/svx/source/sidebar/shadow/ShadowPropertyPanel.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/.
+ */
+#ifndef INCLUDED_SVX_SOURCE_SIDEBAR_AREA_SHADOWPROPERTYPANEL_HXX
+#define INCLUDED_SVX_SOURCE_SIDEBAR_AREA_SHADOWPROPERTYPANEL_HXX
+
+#include <sfx2/sidebar/ControllerItem.hxx>
+#include <sfx2/sidebar/PanelLayout.hxx>
+
+class ColorListBox;
+
+namespace svx::sidebar {
+
+class ShadowPropertyPanel
+: public PanelLayout,
+ public ::sfx2::sidebar::ControllerItem::ItemUpdateReceiverInterface
+{
+public:
+ virtual ~ShadowPropertyPanel() override;
+
+ static std::unique_ptr<PanelLayout> Create(
+ weld::Widget* pParent,
+ 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() { return mpBindings;}
+
+ void Initialize();
+
+ ShadowPropertyPanel(
+ weld::Widget* pParent,
+ SfxBindings* pBindings);
+
+private:
+ ::sfx2::sidebar::ControllerItem maShadowController;
+ ::sfx2::sidebar::ControllerItem maShadowTransController;
+ ::sfx2::sidebar::ControllerItem maShadowBlurController;
+ ::sfx2::sidebar::ControllerItem maShadowColorController;
+ ::sfx2::sidebar::ControllerItem maShadowXDistanceController;
+ ::sfx2::sidebar::ControllerItem maShadowYDistanceController;
+
+ SfxBindings* mpBindings;
+ tools::Long nX,nY,nXY;
+
+ std::unique_ptr<weld::CheckButton> mxShowShadow;
+ std::unique_ptr<weld::MetricSpinButton> mxShadowDistance;
+ std::unique_ptr<ColorListBox> mxLBShadowColor;
+ std::unique_ptr<weld::ComboBox> mxShadowAngle;
+ std::unique_ptr<weld::Label> mxFTAngle;
+ std::unique_ptr<weld::Label> mxFTDistance;
+ std::unique_ptr<weld::Label> mxFTTransparency;
+ std::unique_ptr<weld::Label> mxFTBlur;
+ std::unique_ptr<weld::Label> mxFTColor;
+ std::unique_ptr<weld::Scale> mxShadowTransSlider;
+ std::unique_ptr<weld::MetricSpinButton> mxShadowTransMetric;
+ std::unique_ptr<weld::MetricSpinButton> mxShadowBlurMetric;
+
+ void InsertAngleValues();
+ void SetTransparencyValue(tools::Long);
+ void UpdateControls();
+ void ModifyShadowDistance();
+
+ DECL_LINK(ClickShadowHdl, weld::Toggleable&, void);
+ DECL_LINK(ModifyShadowColorHdl, ColorListBox&, void);
+ DECL_LINK(ModifyShadowTransMetricHdl, weld::MetricSpinButton&, void);
+ DECL_LINK(ModifyShadowAngleHdl, weld::ComboBox&, void);
+ DECL_LINK(ModifyShadowDistanceHdl, weld::MetricSpinButton&, void);
+ DECL_LINK(ModifyShadowTransSliderHdl, weld::Scale&, void);
+ DECL_LINK(ModifyShadowBlurMetricHdl, weld::MetricSpinButton&, void);
+};
+
+}
+
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/sidebar/shapes/DefaultShapesPanel.cxx b/svx/source/sidebar/shapes/DefaultShapesPanel.cxx
new file mode 100644
index 000000000..2dc8092a5
--- /dev/null
+++ b/svx/source/sidebar/shapes/DefaultShapesPanel.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 <DefaultShapesPanel.hxx>
+
+#include <com/sun/star/lang/IllegalArgumentException.hpp>
+#include <comphelper/dispatchcommand.hxx>
+#include <vcl/commandinfoprovider.hxx>
+#include <vcl/settings.hxx>
+#include <vcl/svapp.hxx>
+
+namespace svx::sidebar {
+
+DefaultShapesPanel::DefaultShapesPanel (
+ weld::Widget* pParent,
+ const css::uno::Reference<css::frame::XFrame>& rxFrame)
+ : PanelLayout(pParent, "DefaultShapesPanel", "svx/ui/defaultshapespanel.ui")
+ , mxLineArrowSet(new ValueSet(nullptr))
+ , mxLineArrowSetWin(new weld::CustomWeld(*m_xBuilder, "LinesArrows", *mxLineArrowSet))
+ , mxCurveSet(new ValueSet(nullptr))
+ , mxCurveSetWin(new weld::CustomWeld(*m_xBuilder, "Curves", *mxCurveSet))
+ , mxConnectorSet(new ValueSet(nullptr))
+ , mxConnectorSetWin(new weld::CustomWeld(*m_xBuilder, "Connectors", *mxConnectorSet))
+ , mxBasicShapeSet(new ValueSet(nullptr))
+ , mxBasicShapeSetWin(new weld::CustomWeld(*m_xBuilder, "BasicShapes", *mxBasicShapeSet))
+ , mxSymbolShapeSet(new ValueSet(nullptr))
+ , mxSymbolShapeSetWin(new weld::CustomWeld(*m_xBuilder, "SymbolShapes", *mxSymbolShapeSet))
+ , mxBlockArrowSet(new ValueSet(nullptr))
+ , mxBlockArrowSetWin(new weld::CustomWeld(*m_xBuilder, "BlockArrows", *mxBlockArrowSet))
+ , mxFlowchartSet(new ValueSet(nullptr))
+ , mxFlowchartSetWin(new weld::CustomWeld(*m_xBuilder, "Flowcharts", *mxFlowchartSet))
+ , mxCalloutSet(new ValueSet(nullptr))
+ , mxCalloutSetWin(new weld::CustomWeld(*m_xBuilder, "Callouts", *mxCalloutSet))
+ , mxStarSet(new ValueSet(nullptr))
+ , mxStarSetWin(new weld::CustomWeld(*m_xBuilder, "Stars", *mxStarSet))
+ , mx3DObjectSet(new ValueSet(nullptr))
+ , mx3DObjectSetWin(new weld::CustomWeld(*m_xBuilder, "3DObjects", *mx3DObjectSet))
+ , mxFrame(rxFrame)
+{
+ Initialize();
+ pParent->set_size_request(pParent->get_approximate_digit_width() * 20, -1);
+ m_xContainer->set_size_request(m_xContainer->get_approximate_digit_width() * 25, -1);
+}
+
+std::unique_ptr<PanelLayout> DefaultShapesPanel::Create(
+ weld::Widget* pParent,
+ const Reference< XFrame >& rxFrame)
+{
+ if (pParent == nullptr)
+ throw lang::IllegalArgumentException("no parent Window given to DefaultShapesPanel::Create", nullptr, 0);
+ if ( ! rxFrame.is())
+ throw lang::IllegalArgumentException("no XFrame given to DefaultShapesPanel::Create", nullptr, 1);
+
+ return std::make_unique<DefaultShapesPanel>(pParent, rxFrame);
+}
+
+void DefaultShapesPanel::Initialize()
+{
+ mpShapesSetMap = decltype(mpShapesSetMap){
+ { mxLineArrowSet.get(), mpLineShapes },
+ { mxCurveSet.get(), mpCurveShapes },
+ { mxConnectorSet.get(), mpConnectorShapes },
+ { mxBasicShapeSet.get(), mpBasicShapes },
+ { mxSymbolShapeSet.get(), mpSymbolShapes },
+ { mxBlockArrowSet.get(), mpBlockArrowShapes },
+ { mxFlowchartSet.get(), mpFlowchartShapes },
+ { mxCalloutSet.get(), mpCalloutShapes },
+ { mxStarSet.get(), mpStarShapes },
+ { mx3DObjectSet.get(), mp3DShapes }
+ };
+ populateShapes();
+ for(auto& aSetMap: mpShapesSetMap)
+ {
+ aSetMap.first->SetColor(Application::GetSettings().GetStyleSettings().GetDialogColor());
+ aSetMap.first->SetSelectHdl(LINK(this, DefaultShapesPanel, ShapeSelectHdl));
+ }
+}
+
+DefaultShapesPanel::~DefaultShapesPanel()
+{
+ mpShapesSetMap.clear();
+ mxLineArrowSetWin.reset();
+ mxLineArrowSet.reset();
+ mxCurveSetWin.reset();
+ mxCurveSet.reset();
+ mxConnectorSetWin.reset();
+ mxConnectorSet.reset();
+ mxBasicShapeSetWin.reset();
+ mxBasicShapeSet.reset();
+ mxSymbolShapeSetWin.reset();
+ mxSymbolShapeSet.reset();
+ mxBlockArrowSetWin.reset();
+ mxBlockArrowSet.reset();
+ mxFlowchartSetWin.reset();
+ mxFlowchartSet.reset();
+ mxCalloutSetWin.reset();
+ mxCalloutSet.reset();
+ mxStarSetWin.reset();
+ mxStarSet.reset();
+ mx3DObjectSetWin.reset();
+ mx3DObjectSet.reset();
+}
+
+IMPL_LINK(DefaultShapesPanel, ShapeSelectHdl, ValueSet*, rValueSet, void)
+{
+ for(auto& aSetMap : mpShapesSetMap)
+ {
+ if(rValueSet == aSetMap.first)
+ {
+ sal_uInt16 nSelectionId = aSetMap.first->GetSelectedItemId();
+ comphelper::dispatchCommand(aSetMap.second[nSelectionId - 1], {});
+ }
+ else
+ aSetMap.first->SetNoSelection();
+ }
+}
+
+void DefaultShapesPanel::populateShapes()
+{
+ OUString sSlotStr, sLabel;
+ Image aSlotImage;
+ for(auto& aSet : mpShapesSetMap)
+ {
+ aSet.first->SetColCount(6);
+ for(std::map<sal_uInt16, OUString>::size_type i = 0; i < aSet.second.size(); i++)
+ {
+ sSlotStr = aSet.second[i];
+ aSlotImage = vcl::CommandInfoProvider::GetImageForCommand(sSlotStr, mxFrame);
+ auto aProperties = vcl::CommandInfoProvider::GetCommandProperties(sSlotStr,
+ vcl::CommandInfoProvider::GetModuleIdentifier(mxFrame));
+ sLabel = vcl::CommandInfoProvider::GetTooltipForCommand(sSlotStr, aProperties, mxFrame);
+ sal_uInt16 nSelectionId = i + 1; // tdf#142767 id 0 is reserved for nothing-selected
+ aSet.first->InsertItem(nSelectionId, aSlotImage, sLabel);
+ }
+ }
+}
+
+} // end of namespace svx::sidebar
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/sidebar/shapes/ShapesUtil.cxx b/svx/source/sidebar/shapes/ShapesUtil.cxx
new file mode 100644
index 000000000..ffd1acb62
--- /dev/null
+++ b/svx/source/sidebar/shapes/ShapesUtil.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 <ShapesUtil.hxx>
+#include <map>
+#include <rtl/ustring.hxx>
+
+namespace svx::sidebar{
+SvxShapeCommandsMap::SvxShapeCommandsMap()
+{
+ mpLineShapes = decltype(mpLineShapes){
+ {0, ".uno:Line"},
+ {1, ".uno:LineArrowEnd"},
+ {2, ".uno:LineCircleArrow"},
+ {3, ".uno:LineSquareArrow"},
+ {4, ".uno:LineArrows"},
+ {5, ".uno:LineArrowStart"},
+ {6, ".uno:LineArrowCircle"},
+ {7, ".uno:LineArrowSquare"},
+ {8, ".uno:MeasureLine"},
+ {9, ".uno:Line_Diagonal"}
+ };
+
+ mpCurveShapes = decltype(mpCurveShapes){
+ {0, ".uno:Freeline_Unfilled"},
+ {1, ".uno:Bezier_Unfilled"},
+ {2, ".uno:Polygon_Unfilled"},
+ {3, ".uno:Polygon_Diagonal_Unfilled"},
+ {4, ".uno:Freeline"},
+ {5, ".uno:BezierFill"},
+ {6, ".uno:Polygon"},
+ {7, ".uno:Polygon_Diagonal"}
+ };
+
+ mpConnectorShapes = decltype(mpConnectorShapes){
+ {0, ".uno:ConnectorArrowEnd"},
+ {1, ".uno:ConnectorLineArrowEnd"},
+ {2, ".uno:ConnectorCurveArrowEnd"},
+ {3, ".uno:ConnectorLinesArrowEnd"},
+ {4, ".uno:Connector"},
+ {5, ".uno:ConnectorLine"},
+ {6, ".uno:ConnectorCurve"},
+ {7, ".uno:ConnectorLines"},
+ {8, ".uno:ConnectorArrows"},
+ {9, ".uno:ConnectorLineArrows"},
+ {10, ".uno:ConnectorCurveArrows"},
+ {11, ".uno:ConnectorLinesArrows"}
+ };
+
+ mpBasicShapes = decltype(mpBasicShapes){
+ {0, ".uno:BasicShapes.rectangle"},
+ {1, ".uno:BasicShapes.round-rectangle"},
+ {2, ".uno:BasicShapes.quadrat"},
+ {3, ".uno:BasicShapes.round-quadrat"},
+ {4, ".uno:BasicShapes.parallelogram"},
+ {5, ".uno:BasicShapes.trapezoid"},
+ {6, ".uno:BasicShapes.ellipse"},
+ {7, ".uno:BasicShapes.circle"},
+ {8, ".uno:BasicShapes.circle-pie"},
+ {9, ".uno:CircleCut"},
+ {10, ".uno:Arc"},
+ {11, ".uno:BasicShapes.block-arc"},
+ {12, ".uno:BasicShapes.isosceles-triangle"},
+ {13, ".uno:BasicShapes.right-triangle"},
+ {14, ".uno:BasicShapes.diamond"},
+ {15, ".uno:BasicShapes.pentagon"},
+ {16, ".uno:BasicShapes.hexagon"},
+ {17, ".uno:BasicShapes.octagon"},
+ {18, ".uno:BasicShapes.cross"},
+ {19, ".uno:BasicShapes.can"},
+ {20, ".uno:BasicShapes.cube"},
+ {21, ".uno:BasicShapes.paper"},
+ {22, ".uno:BasicShapes.frame"},
+ {23, ".uno:BasicShapes.ring"}
+ };
+
+ mpSymbolShapes = decltype(mpSymbolShapes){
+ {0, ".uno:SymbolShapes.smiley"},
+ {1, ".uno:SymbolShapes.sun"},
+ {2, ".uno:SymbolShapes.moon"},
+ {3, ".uno:SymbolShapes.lightning"},
+ {4, ".uno:SymbolShapes.heart"},
+ {5, ".uno:SymbolShapes.flower"},
+ {6, ".uno:SymbolShapes.cloud"},
+ {7, ".uno:SymbolShapes.forbidden"},
+ {8, ".uno:SymbolShapes.puzzle"},
+ {9, ".uno:SymbolShapes.bracket-pair"},
+ {10, ".uno:SymbolShapes.left-bracket"},
+ {11, ".uno:SymbolShapes.right-bracket"},
+ {12, ".uno:SymbolShapes.brace-pair"},
+ {13, ".uno:SymbolShapes.left-brace"},
+ {14, ".uno:SymbolShapes.right-brace"},
+ {15, ".uno:SymbolShapes.quad-bevel"},
+ {16, ".uno:SymbolShapes.octagon-bevel"},
+ {17, ".uno:SymbolShapes.diamond-bevel"}
+ };
+
+ mpBlockArrowShapes = decltype(mpBlockArrowShapes){
+ {0, ".uno:ArrowShapes.left-arrow"},
+ {1, ".uno:ArrowShapes.right-arrow"},
+ {2, ".uno:ArrowShapes.up-arrow"},
+ {3, ".uno:ArrowShapes.down-arrow"},
+ {4, ".uno:ArrowShapes.left-right-arrow"},
+ {5, ".uno:ArrowShapes.up-down-arrow"},
+ {6, ".uno:ArrowShapes.up-right-arrow"},
+ {7, ".uno:ArrowShapes.up-right-down-arrow"},
+ {8, ".uno:ArrowShapes.quad-arrow"},
+ {9, ".uno:ArrowShapes.corner-right-arrow"},
+ {10, ".uno:ArrowShapes.split-arrow"},
+ {11, ".uno:ArrowShapes.striped-right-arrow"},
+ {12, ".uno:ArrowShapes.notched-right-arrow"},
+ {13, ".uno:ArrowShapes.pentagon-right"},
+ {14, ".uno:ArrowShapes.chevron"},
+ {15, ".uno:ArrowShapes.right-arrow-callout"},
+ {16, ".uno:ArrowShapes.left-arrow-callout"},
+ {17, ".uno:ArrowShapes.up-arrow-callout"},
+ {18, ".uno:ArrowShapes.left-right-arrow-callout"},
+ {19, ".uno:ArrowShapes.up-down-arrow-callout"},
+ {20, ".uno:ArrowShapes.up-right-arrow-callout"},
+ {21, ".uno:ArrowShapes.quad-arrow-callout"},
+ {22, ".uno:ArrowShapes.circular-arrow"},
+ {23, ".uno:ArrowShapes.down-arrow-callout"},
+ {24, ".uno:ArrowShapes.split-round-arrow"},
+ {25, ".uno:ArrowShapes.s-sharped-arrow"}
+ };
+
+ mpFlowchartShapes = decltype(mpFlowchartShapes){
+ {0, ".uno:FlowChartShapes.flowchart-process"},
+ {1, ".uno:FlowChartShapes.flowchart-alternate-process"},
+ {2, ".uno:FlowChartShapes.flowchart-decision"},
+ {3, ".uno:FlowChartShapes.flowchart-data"},
+ {4, ".uno:FlowChartShapes.flowchart-predefined-process"},
+ {5, ".uno:FlowChartShapes.flowchart-internal-storage"},
+ {6, ".uno:FlowChartShapes.flowchart-document"},
+ {7, ".uno:FlowChartShapes.flowchart-multidocument"},
+ {8, ".uno:FlowChartShapes.flowchart-terminator"},
+ {9, ".uno:FlowChartShapes.flowchart-preparation"},
+ {10, ".uno:FlowChartShapes.flowchart-manual-input"},
+ {11, ".uno:FlowChartShapes.flowchart-manual-operation"},
+ {12, ".uno:FlowChartShapes.flowchart-connector"},
+ {13, ".uno:FlowChartShapes.flowchart-off-page-connector"},
+ {14, ".uno:FlowChartShapes.flowchart-card"},
+ {15, ".uno:FlowChartShapes.flowchart-punched-tape"},
+ {16, ".uno:FlowChartShapes.flowchart-summing-junction"},
+ {17, ".uno:FlowChartShapes.flowchart-or"},
+ {18, ".uno:FlowChartShapes.flowchart-collate"},
+ {19, ".uno:FlowChartShapes.flowchart-sort"},
+ {20, ".uno:FlowChartShapes.flowchart-extract"},
+ {21, ".uno:FlowChartShapes.flowchart-merge"},
+ {22, ".uno:FlowChartShapes.flowchart-stored-data"},
+ {23, ".uno:FlowChartShapes.flowchart-delay"},
+ {24, ".uno:FlowChartShapes.flowchart-sequential-access"},
+ {25, ".uno:FlowChartShapes.flowchart-magnetic-disk"},
+ {26, ".uno:FlowChartShapes.flowchart-direct-access-storage"},
+ {27, ".uno:FlowChartShapes.flowchart-display"}
+ };
+
+ mpCalloutShapes = decltype(mpCalloutShapes){
+ {0, ".uno:CalloutShapes.rectangular-callout"},
+ {1, ".uno:CalloutShapes.round-rectangular-callout"},
+ {2, ".uno:CalloutShapes.round-callout"},
+ {3, ".uno:CalloutShapes.cloud-callout"},
+ {4, ".uno:CalloutShapes.line-callout-1"},
+ {5, ".uno:CalloutShapes.line-callout-2"},
+ {6, ".uno:CalloutShapes.line-callout-3"}
+ };
+
+ mpStarShapes = decltype(mpStarShapes){
+ {0, ".uno:StarShapes.star4"},
+ {1, ".uno:StarShapes.star5"},
+ {2, ".uno:StarShapes.star6"},
+ {3, ".uno:StarShapes.star8"},
+ {4, ".uno:StarShapes.star12"},
+ {5, ".uno:StarShapes.star24"},
+ {6, ".uno:StarShapes.bang"},
+ {7, ".uno:StarShapes.vertical-scroll"},
+ {8, ".uno:StarShapes.horizontal-scroll"},
+ {9, ".uno:StarShapes.signet"},
+ {10, ".uno:StarShapes.doorplate"},
+ {11, ".uno:StarShapes.concave-star6"}
+ };
+
+ mp3DShapes = decltype(mp3DShapes){
+ {0, ".uno:Cube"},
+ {1, ".uno:Sphere"},
+ {2, ".uno:Cylinder"},
+ {3, ".uno:Cone"},
+ {4, ".uno:Cyramid"},
+ {5, ".uno:Torus"},
+ {6, ".uno:Shell3D"},
+ {7, ".uno:HalfSphere"}
+ };
+}
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ \ No newline at end of file
diff --git a/svx/source/sidebar/styles/StylesPropertyPanel.cxx b/svx/source/sidebar/styles/StylesPropertyPanel.cxx
new file mode 100644
index 000000000..4e6b2235d
--- /dev/null
+++ b/svx/source/sidebar/styles/StylesPropertyPanel.cxx
@@ -0,0 +1,50 @@
+/* -*- 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 <sal/config.h>
+
+#include <com/sun/star/lang/IllegalArgumentException.hpp>
+
+#include "StylesPropertyPanel.hxx"
+
+using namespace css;
+using namespace css::uno;
+
+namespace svx::sidebar {
+
+std::unique_ptr<PanelLayout> StylesPropertyPanel::Create (
+ weld::Widget* pParent,
+ const css::uno::Reference<css::frame::XFrame>& rxFrame)
+{
+ if (pParent == nullptr)
+ throw lang::IllegalArgumentException("no parent Window given to StylesPropertyPanel::Create", nullptr, 0);
+ if ( ! rxFrame.is())
+ throw lang::IllegalArgumentException("no XFrame given to StylesPropertyPanel::Create", nullptr, 1);
+
+ return std::make_unique<StylesPropertyPanel>(pParent, rxFrame);
+}
+
+StylesPropertyPanel::StylesPropertyPanel(weld::Widget* pParent, const css::uno::Reference<css::frame::XFrame>& rxFrame)
+ : PanelLayout(pParent, "SidebarStylesPanel", "svx/ui/sidebarstylespanel.ui")
+ , m_xFontStyle(m_xBuilder->weld_toolbar("fontstyletoolbox"))
+ , m_xFontStyleDispatch(new ToolbarUnoDispatcher(*m_xFontStyle, *m_xBuilder, rxFrame))
+ , m_xStyle(m_xBuilder->weld_toolbar("style"))
+ , m_xStyleDispatch(new ToolbarUnoDispatcher(*m_xStyle, *m_xBuilder, rxFrame))
+{
+}
+
+StylesPropertyPanel::~StylesPropertyPanel()
+{
+ m_xStyleDispatch.reset();
+ m_xStyle.reset();
+ m_xFontStyleDispatch.reset();
+ m_xFontStyle.reset();
+}
+
+}
+/* vim:set shiftwidth=4 softtabstop=4 expandtab cinoptions=b1,g0,N-s cinkeys+=0=break: */
diff --git a/svx/source/sidebar/styles/StylesPropertyPanel.hxx b/svx/source/sidebar/styles/StylesPropertyPanel.hxx
new file mode 100644
index 000000000..66f773ae9
--- /dev/null
+++ b/svx/source/sidebar/styles/StylesPropertyPanel.hxx
@@ -0,0 +1,38 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4; fill-column: 100 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+#pragma once
+
+#include <sfx2/sidebar/PanelLayout.hxx>
+#include <sfx2/weldutils.hxx>
+
+namespace svx::sidebar{
+
+class StylesPropertyPanel : public PanelLayout
+{
+private:
+ std::unique_ptr<weld::Toolbar> m_xFontStyle;
+ std::unique_ptr<ToolbarUnoDispatcher> m_xFontStyleDispatch;
+
+ std::unique_ptr<weld::Toolbar> m_xStyle;
+ std::unique_ptr<ToolbarUnoDispatcher> m_xStyleDispatch;
+
+public:
+ virtual ~StylesPropertyPanel() override;
+
+ static std::unique_ptr<PanelLayout> Create (
+ weld::Widget* pParent,
+ const css::uno::Reference<css::frame::XFrame>& rxFrame);
+
+ StylesPropertyPanel(
+ weld::Widget* pParent,
+ const css::uno::Reference<css::frame::XFrame>& rxFrame);
+};
+
+} //end of namespace svx::sidebar
+/* vim:set shiftwidth=4 softtabstop=4 expandtab cinoptions=b1,g0,N-s cinkeys+=0=break: */
diff --git a/svx/source/sidebar/text/TextCharacterSpacingControl.cxx b/svx/source/sidebar/text/TextCharacterSpacingControl.cxx
new file mode 100644
index 000000000..ad9650477
--- /dev/null
+++ b/svx/source/sidebar/text/TextCharacterSpacingControl.cxx
@@ -0,0 +1,224 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+#include <sfx2/bindings.hxx>
+#include "TextCharacterSpacingControl.hxx"
+#include <unotools/viewoptions.hxx>
+#include <editeng/editids.hrc>
+#include <editeng/kernitem.hxx>
+#include <sfx2/app.hxx>
+#include <sfx2/dispatch.hxx>
+#include <sfx2/viewfrm.hxx>
+#include <TextCharacterSpacingPopup.hxx>
+#include <svl/itempool.hxx>
+#include <helpids.h>
+
+#include <com/sun/star/beans/NamedValue.hpp>
+
+#define SPACING_VERY_TIGHT -30
+#define SPACING_TIGHT -15
+#define SPACING_NORMAL 0
+#define SPACING_LOOSE 30
+#define SPACING_VERY_LOOSE 60
+
+namespace svx {
+
+TextCharacterSpacingControl::TextCharacterSpacingControl(TextCharacterSpacingPopup* pControl, weld::Widget* pParent)
+ : WeldToolbarPopup(pControl->getFrameInterface(), pParent, "svx/ui/textcharacterspacingcontrol.ui", "TextCharacterSpacingControl")
+ , mnCustomKern(0)
+ , mnLastCus(SPACING_NOCUSTOM)
+ , mxEditKerning(m_xBuilder->weld_metric_spin_button("kerning", FieldUnit::POINT))
+ , mxTight(m_xBuilder->weld_button("tight"))
+ , mxVeryTight(m_xBuilder->weld_button("very_tight"))
+ , mxNormal(m_xBuilder->weld_button("normal"))
+ , mxLoose(m_xBuilder->weld_button("loose"))
+ , mxVeryLoose(m_xBuilder->weld_button("very_loose"))
+ , mxLastCustom(m_xBuilder->weld_button("last_custom"))
+ , mxControl(pControl)
+{
+ mxEditKerning->connect_value_changed(LINK(this, TextCharacterSpacingControl, KerningModifyHdl));
+ mxEditKerning->set_help_id(HID_SPACING_MB_KERN);
+
+ Link<weld::Button&,void> aLink = LINK(this, TextCharacterSpacingControl, PredefinedValuesHdl);
+ mxNormal->connect_clicked(aLink);
+ mxVeryTight->connect_clicked(aLink);
+ mxTight->connect_clicked(aLink);
+ mxVeryLoose->connect_clicked(aLink);
+ mxLoose->connect_clicked(aLink);
+ mxLastCustom->connect_clicked(aLink);
+
+ Initialize();
+}
+
+void TextCharacterSpacingControl::GrabFocus()
+{
+ tools::Long nKerning = mxEditKerning->get_value(FieldUnit::NONE);
+ switch (nKerning)
+ {
+ case SPACING_VERY_TIGHT:
+ mxVeryTight->grab_focus();
+ break;
+ case SPACING_TIGHT:
+ mxTight->grab_focus();
+ break;
+ case SPACING_NORMAL:
+ mxNormal->grab_focus();
+ break;
+ case SPACING_LOOSE:
+ mxLoose->grab_focus();
+ break;
+ case SPACING_VERY_LOOSE:
+ mxVeryLoose->grab_focus();
+ break;
+ default:
+ if (nKerning == mnCustomKern)
+ mxLastCustom->grab_focus();
+ else
+ mxEditKerning->grab_focus();
+ }
+}
+
+TextCharacterSpacingControl::~TextCharacterSpacingControl()
+{
+ if (mnLastCus == SPACING_CLOSE_BY_CUS_EDIT)
+ {
+ SvtViewOptions aWinOpt(EViewType::Window, SIDEBAR_SPACING_GLOBAL_VALUE);
+ css::uno::Sequence<css::beans::NamedValue> aSeq
+ { { "Spacing", css::uno::Any(OUString::number(mnCustomKern)) } };
+ aWinOpt.SetUserData(aSeq);
+ }
+}
+
+void TextCharacterSpacingControl::Initialize()
+{
+ const SvxKerningItem* pKerningItem(nullptr);
+ SfxViewFrame* pViewFrm = SfxViewFrame::Current();
+ SfxItemState eState = pViewFrm ? pViewFrm->GetBindings().GetDispatcher()->QueryState(SID_ATTR_CHAR_KERNING, pKerningItem) : SfxItemState::UNKNOWN;
+
+ tools::Long nKerning = 0;
+
+ if(pKerningItem)
+ nKerning = pKerningItem->GetValue();
+
+ SvtViewOptions aWinOpt(EViewType::Window, SIDEBAR_SPACING_GLOBAL_VALUE);
+ if(aWinOpt.Exists())
+ {
+ css::uno::Sequence<css::beans::NamedValue> aSeq = aWinOpt.GetUserData();
+ OUString aTmp;
+ if(aSeq.hasElements())
+ aSeq[0].Value >>= aTmp;
+
+ OUString aWinData(aTmp);
+ mnCustomKern = aWinData.toInt32();
+ mnLastCus = SPACING_CLOSE_BY_CUS_EDIT;
+ }
+ else
+ {
+ mnLastCus = SPACING_NOCUSTOM;
+ }
+
+ if(eState >= SfxItemState::DEFAULT)
+ {
+ MapUnit eUnit = GetCoreMetric();
+ MapUnit eOrgUnit = eUnit;
+ tools::Long nBig = mxEditKerning->normalize(nKerning);
+ nKerning = OutputDevice::LogicToLogic(nBig, eOrgUnit, MapUnit::MapPoint);
+ mxEditKerning->set_value(nKerning, FieldUnit::NONE);
+ }
+ else if(SfxItemState::DISABLED == eState)
+ {
+ mxEditKerning->set_text(OUString());
+ mxEditKerning->set_sensitive(false);
+ }
+ else
+ {
+ mxEditKerning->set_text(OUString());
+ mxEditKerning->set_sensitive(false);
+ }
+}
+
+void TextCharacterSpacingControl::ExecuteCharacterSpacing(tools::Long nValue, bool bClose)
+{
+ MapUnit eUnit = GetCoreMetric();
+
+ tools::Long nSign = (nValue < 0) ? -1 : 1;
+ nValue = nValue * nSign;
+
+ tools::Long nVal = OutputDevice::LogicToLogic(nValue, MapUnit::MapPoint, eUnit);
+ short nKern = (nValue == 0) ? 0 : static_cast<short>(mxEditKerning->denormalize(nVal));
+
+ SvxKerningItem aKernItem(nSign * nKern, SID_ATTR_CHAR_KERNING);
+
+ if (SfxViewFrame* pViewFrm = SfxViewFrame::Current())
+ {
+ pViewFrm->GetBindings().GetDispatcher()->ExecuteList(SID_ATTR_CHAR_KERNING,
+ SfxCallMode::RECORD, { &aKernItem });
+ }
+
+ if (bClose)
+ mxControl->EndPopupMode();
+}
+
+IMPL_LINK(TextCharacterSpacingControl, PredefinedValuesHdl, weld::Button&, rControl, void)
+{
+ mnLastCus = SPACING_CLOSE_BY_CLICK_ICON;
+
+ if (&rControl == mxNormal.get())
+ {
+ ExecuteCharacterSpacing(SPACING_NORMAL);
+ }
+ else if (&rControl == mxVeryTight.get())
+ {
+ ExecuteCharacterSpacing(SPACING_VERY_TIGHT);
+ }
+ else if (&rControl == mxTight.get())
+ {
+ ExecuteCharacterSpacing(SPACING_TIGHT);
+ }
+ else if (&rControl == mxVeryLoose.get())
+ {
+ ExecuteCharacterSpacing(SPACING_VERY_LOOSE);
+ }
+ else if (&rControl == mxLoose.get())
+ {
+ ExecuteCharacterSpacing(SPACING_LOOSE);
+ }
+ else if (&rControl == mxLastCustom.get())
+ {
+ ExecuteCharacterSpacing(mnCustomKern);
+ }
+}
+
+IMPL_LINK_NOARG(TextCharacterSpacingControl, KerningModifyHdl, weld::MetricSpinButton&, void)
+{
+ mnLastCus = SPACING_CLOSE_BY_CUS_EDIT;
+ mnCustomKern = mxEditKerning->get_value(FieldUnit::NONE);
+
+ ExecuteCharacterSpacing(mnCustomKern, false);
+}
+
+MapUnit TextCharacterSpacingControl::GetCoreMetric()
+{
+ SfxItemPool &rPool = SfxGetpApp()->GetPool();
+ sal_uInt16 nWhich = rPool.GetWhich(SID_ATTR_CHAR_KERNING);
+ return rPool.GetMetric(nWhich);
+}
+
+} // end of namespace svx
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/sidebar/text/TextCharacterSpacingControl.hxx b/svx/source/sidebar/text/TextCharacterSpacingControl.hxx
new file mode 100644
index 000000000..a8ce8b7fb
--- /dev/null
+++ b/svx/source/sidebar/text/TextCharacterSpacingControl.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_SVX_SOURCE_SIDEBAR_TEXT_TEXTCHARACTERSPACINGCONTROL_HXX
+#define INCLUDED_SVX_SOURCE_SIDEBAR_TEXT_TEXTCHARACTERSPACINGCONTROL_HXX
+
+#include <svtools/toolbarmenu.hxx>
+
+namespace svx {
+#define SPACING_NOCUSTOM 0
+#define SPACING_CLOSE_BY_CLICK_ICON -1
+#define SPACING_CLOSE_BY_CUS_EDIT 1
+
+inline constexpr OUStringLiteral SIDEBAR_SPACING_GLOBAL_VALUE = u"PopupPanel_Spacing";
+
+class TextCharacterSpacingPopup;
+
+class TextCharacterSpacingControl final : public WeldToolbarPopup
+{
+public:
+ explicit TextCharacterSpacingControl(TextCharacterSpacingPopup* pControl, weld::Widget* pParent);
+
+ virtual void GrabFocus() override;
+
+ virtual ~TextCharacterSpacingControl() override;
+
+private:
+ tools::Long mnCustomKern;
+ short mnLastCus;
+
+ std::unique_ptr<weld::MetricSpinButton> mxEditKerning;
+ std::unique_ptr<weld::Button> mxTight;
+ std::unique_ptr<weld::Button> mxVeryTight;
+ std::unique_ptr<weld::Button> mxNormal;
+ std::unique_ptr<weld::Button> mxLoose;
+ std::unique_ptr<weld::Button> mxVeryLoose;
+ std::unique_ptr<weld::Button> mxLastCustom;
+
+ rtl::Reference<TextCharacterSpacingPopup> mxControl;
+
+ void Initialize();
+ void ExecuteCharacterSpacing(tools::Long nValue, bool bClose = true);
+
+ DECL_LINK(PredefinedValuesHdl, weld::Button&, void);
+ DECL_LINK(KerningModifyHdl, weld::MetricSpinButton&, void);
+
+ static MapUnit GetCoreMetric();
+};
+
+}
+
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/sidebar/text/TextCharacterSpacingPopup.cxx b/svx/source/sidebar/text/TextCharacterSpacingPopup.cxx
new file mode 100644
index 000000000..a8eef9000
--- /dev/null
+++ b/svx/source/sidebar/text/TextCharacterSpacingPopup.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 <TextCharacterSpacingPopup.hxx>
+#include "TextCharacterSpacingControl.hxx"
+#include <vcl/toolbox.hxx>
+
+using namespace svx;
+
+TextCharacterSpacingPopup::TextCharacterSpacingPopup(
+ const css::uno::Reference<css::uno::XComponentContext>& rContext)
+ : PopupWindowController(rContext, nullptr, OUString())
+{
+}
+
+void TextCharacterSpacingPopup::initialize(const css::uno::Sequence<css::uno::Any>& rArguments)
+{
+ PopupWindowController::initialize(rArguments);
+
+ if (m_pToolbar)
+ {
+ mxPopoverContainer.reset(new ToolbarPopupContainer(m_pToolbar));
+ m_pToolbar->set_item_popover(m_aCommandURL.toUtf8(), mxPopoverContainer->getTopLevel());
+ }
+
+ ToolBox* pToolBox = nullptr;
+ ToolBoxItemId nId;
+ if (getToolboxId(nId, &pToolBox))
+ pToolBox->SetItemBits(nId, ToolBoxItemBits::DROPDOWNONLY | pToolBox->GetItemBits(nId));
+}
+
+TextCharacterSpacingPopup::~TextCharacterSpacingPopup() {}
+
+std::unique_ptr<WeldToolbarPopup> TextCharacterSpacingPopup::weldPopupWindow()
+{
+ return std::make_unique<TextCharacterSpacingControl>(this, m_pToolbar);
+}
+
+VclPtr<vcl::Window> TextCharacterSpacingPopup::createVclPopupWindow(vcl::Window* pParent)
+{
+ mxInterimPopover = VclPtr<InterimToolbarPopup>::Create(
+ getFrameInterface(), pParent,
+ std::make_unique<TextCharacterSpacingControl>(this, pParent->GetFrameWeld()));
+
+ mxInterimPopover->Show();
+
+ return mxInterimPopover;
+}
+
+OUString TextCharacterSpacingPopup::getImplementationName()
+{
+ return "com.sun.star.comp.svx.CharacterSpacingToolBoxControl";
+}
+
+css::uno::Sequence<OUString> TextCharacterSpacingPopup::getSupportedServiceNames()
+{
+ return { "com.sun.star.frame.ToolbarController" };
+}
+
+extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface*
+com_sun_star_comp_svx_CharacterSpacingToolBoxControl_get_implementation(
+ css::uno::XComponentContext* rContext, css::uno::Sequence<css::uno::Any> const&)
+{
+ return cppu::acquire(new TextCharacterSpacingPopup(rContext));
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/sidebar/text/TextPropertyPanel.cxx b/svx/source/sidebar/text/TextPropertyPanel.cxx
new file mode 100644
index 000000000..1b59a79aa
--- /dev/null
+++ b/svx/source/sidebar/text/TextPropertyPanel.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 "TextPropertyPanel.hxx"
+
+#include <com/sun/star/lang/IllegalArgumentException.hpp>
+#include <comphelper/lok.hxx>
+#include <sfx2/viewsh.hxx>
+
+using namespace css;
+
+namespace svx::sidebar {
+
+std::unique_ptr<PanelLayout> TextPropertyPanel::Create (
+ weld::Widget* pParent,
+ const css::uno::Reference<css::frame::XFrame>& rxFrame)
+{
+ if (pParent == nullptr)
+ throw lang::IllegalArgumentException("no parent Window given to TextPropertyPanel::Create", nullptr, 0);
+ if ( ! rxFrame.is())
+ throw lang::IllegalArgumentException("no XFrame given to TextPropertyPanel::Create", nullptr, 1);
+
+ return std::make_unique<TextPropertyPanel>(pParent, rxFrame);
+}
+
+TextPropertyPanel::TextPropertyPanel(weld::Widget* pParent, const css::uno::Reference<css::frame::XFrame>& rxFrame)
+ : PanelLayout(pParent, "SidebarTextPanel", "svx/ui/sidebartextpanel.ui")
+ , mxFont(m_xBuilder->weld_toolbar("font"))
+ , mxFontDispatch(new ToolbarUnoDispatcher(*mxFont, *m_xBuilder, rxFrame))
+ , mxFontHeight(m_xBuilder->weld_toolbar("fontheight"))
+ , mxFontHeightDispatch(new ToolbarUnoDispatcher(*mxFontHeight, *m_xBuilder, rxFrame))
+ , mxFontEffects(m_xBuilder->weld_toolbar("fonteffects"))
+ , mxFontEffectsDispatch(new ToolbarUnoDispatcher(*mxFontEffects, *m_xBuilder, rxFrame))
+ , mxFontAdjust(m_xBuilder->weld_toolbar("fontadjust"))
+ , mxFontAdjustDispatch(new ToolbarUnoDispatcher(*mxFontAdjust, *m_xBuilder, rxFrame))
+ , mxToolBoxFontColorSw(m_xBuilder->weld_toolbar("colorbar_writer"))
+ , mxToolBoxFontColorSwDispatch(new ToolbarUnoDispatcher(*mxToolBoxFontColorSw, *m_xBuilder, rxFrame))
+ , mxToolBoxFontColor(m_xBuilder->weld_toolbar("colorbar_others"))
+ , mxToolBoxFontColorDispatch(new ToolbarUnoDispatcher(*mxToolBoxFontColor, *m_xBuilder, rxFrame))
+ , mxToolBoxBackgroundColor(m_xBuilder->weld_toolbar("colorbar_background"))
+ , mxToolBoxBackgroundColorDispatch(new ToolbarUnoDispatcher(*mxToolBoxBackgroundColor, *m_xBuilder, rxFrame))
+ , mxResetBar(m_xBuilder->weld_toolbar("resetattr"))
+ , mxResetBarDispatch(new ToolbarUnoDispatcher(*mxResetBar, *m_xBuilder, rxFrame))
+ , mxDefaultBar(m_xBuilder->weld_toolbar("defaultattr"))
+ , mxDefaultBarDispatch(new ToolbarUnoDispatcher(*mxDefaultBar, *m_xBuilder, rxFrame))
+ , mxPositionBar(m_xBuilder->weld_toolbar("position"))
+ , mxPositionBarDispatch(new ToolbarUnoDispatcher(*mxPositionBar, *m_xBuilder, rxFrame))
+ , mxSpacingBar(m_xBuilder->weld_toolbar("spacingbar"))
+ , mxSpacingBarDispatch(new ToolbarUnoDispatcher(*mxSpacingBar, *m_xBuilder, rxFrame))
+{
+ bool isMobilePhone = false;
+ const SfxViewShell* pViewShell = SfxViewShell::Current();
+ if (comphelper::LibreOfficeKit::isActive() &&
+ pViewShell && pViewShell->isLOKMobilePhone())
+ isMobilePhone = true;
+ mxSpacingBar->set_visible(!isMobilePhone);
+}
+
+TextPropertyPanel::~TextPropertyPanel()
+{
+ mxResetBarDispatch.reset();
+ mxDefaultBarDispatch.reset();
+ mxPositionBarDispatch.reset();
+ mxSpacingBarDispatch.reset();
+ mxToolBoxFontColorSwDispatch.reset();
+ mxToolBoxFontColorDispatch.reset();
+ mxToolBoxBackgroundColorDispatch.reset();
+ mxFontAdjustDispatch.reset();
+ mxFontEffectsDispatch.reset();
+ mxFontHeightDispatch.reset();
+ mxFontDispatch.reset();
+
+ mxResetBar.reset();
+ mxDefaultBar.reset();
+ mxPositionBar.reset();
+ mxSpacingBar.reset();
+ mxToolBoxFontColorSw.reset();
+ mxToolBoxFontColor.reset();
+ mxToolBoxBackgroundColor.reset();
+ mxFontAdjust.reset();
+ mxFontEffects.reset();
+ mxFontHeight.reset();
+ mxFont.reset();
+}
+
+void TextPropertyPanel::HandleContextChange (
+ const vcl::EnumContext& rContext)
+{
+ if (maContext == rContext)
+ return;
+
+ maContext = rContext;
+
+ bool bWriterText = false;
+ bool bDrawText = false;
+ bool bCalcText = false;
+
+ switch (maContext.GetCombinedContext_DI())
+ {
+ case CombinedEnumContext(Application::Calc, Context::DrawText):
+ case CombinedEnumContext(Application::WriterVariants, Context::DrawText):
+ case CombinedEnumContext(Application::WriterVariants, Context::Annotation):
+ case CombinedEnumContext(Application::DrawImpress, Context::DrawText):
+ case CombinedEnumContext(Application::DrawImpress, Context::Text):
+ case CombinedEnumContext(Application::DrawImpress, Context::Table):
+ case CombinedEnumContext(Application::DrawImpress, Context::OutlineText):
+ case CombinedEnumContext(Application::DrawImpress, Context::Draw):
+ case CombinedEnumContext(Application::DrawImpress, Context::TextObject):
+ case CombinedEnumContext(Application::DrawImpress, Context::Graphic):
+ bDrawText = true;
+ break;
+
+ case CombinedEnumContext(Application::WriterVariants, Context::Text):
+ case CombinedEnumContext(Application::WriterVariants, Context::Table):
+ bWriterText = true;
+ break;
+
+ case CombinedEnumContext(Application::Calc, Context::Text):
+ case CombinedEnumContext(Application::Calc, Context::Table):
+ case CombinedEnumContext(Application::Calc, Context::Cell):
+ case CombinedEnumContext(Application::Calc, Context::EditCell):
+ case CombinedEnumContext(Application::Calc, Context::Grid):
+ bCalcText = true;
+ break;
+
+ default:
+ break;
+ }
+
+ mxToolBoxFontColor->set_visible(!bWriterText);
+ mxToolBoxFontColorSw->set_visible(bWriterText);
+ mxToolBoxBackgroundColor->set_visible(bDrawText);
+ mxResetBar->set_visible(bWriterText || bCalcText);
+ mxDefaultBar->set_visible(bDrawText);
+}
+
+} // end of namespace svx::sidebar
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/sidebar/text/TextPropertyPanel.hxx b/svx/source/sidebar/text/TextPropertyPanel.hxx
new file mode 100644
index 000000000..3ad44bde7
--- /dev/null
+++ b/svx/source/sidebar/text/TextPropertyPanel.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_SVX_SOURCE_SIDEBAR_TEXT_TEXTPROPERTYPANEL_HXX
+#define INCLUDED_SVX_SOURCE_SIDEBAR_TEXT_TEXTPROPERTYPANEL_HXX
+
+#include <sfx2/sidebar/IContextChangeReceiver.hxx>
+#include <sfx2/weldutils.hxx>
+#include <vcl/EnumContext.hxx>
+#include <sfx2/sidebar/PanelLayout.hxx>
+
+namespace svx::sidebar {
+
+class TextPropertyPanel
+ : public PanelLayout,
+ public ::sfx2::sidebar::IContextChangeReceiver
+{
+public:
+ virtual ~TextPropertyPanel() override;
+
+ static std::unique_ptr<PanelLayout> Create (
+ weld::Widget* pParent,
+ const css::uno::Reference<css::frame::XFrame>& rxFrame);
+
+ virtual void HandleContextChange (
+ const vcl::EnumContext& rContext) override;
+
+ TextPropertyPanel (
+ weld::Widget* pParent,
+ const css::uno::Reference<css::frame::XFrame>& rxFrame);
+
+private:
+ std::unique_ptr<weld::Toolbar> mxFont;
+ std::unique_ptr<ToolbarUnoDispatcher> mxFontDispatch;
+ std::unique_ptr<weld::Toolbar> mxFontHeight;
+ std::unique_ptr<ToolbarUnoDispatcher> mxFontHeightDispatch;
+ std::unique_ptr<weld::Toolbar> mxFontEffects;
+ std::unique_ptr<ToolbarUnoDispatcher> mxFontEffectsDispatch;
+ std::unique_ptr<weld::Toolbar> mxFontAdjust;
+ std::unique_ptr<ToolbarUnoDispatcher> mxFontAdjustDispatch;
+ std::unique_ptr<weld::Toolbar> mxToolBoxFontColorSw;
+ std::unique_ptr<ToolbarUnoDispatcher> mxToolBoxFontColorSwDispatch;
+ std::unique_ptr<weld::Toolbar> mxToolBoxFontColor;
+ std::unique_ptr<ToolbarUnoDispatcher> mxToolBoxFontColorDispatch;
+ std::unique_ptr<weld::Toolbar> mxToolBoxBackgroundColor;
+ std::unique_ptr<ToolbarUnoDispatcher> mxToolBoxBackgroundColorDispatch;
+ std::unique_ptr<weld::Toolbar> mxResetBar;
+ std::unique_ptr<ToolbarUnoDispatcher> mxResetBarDispatch;
+ std::unique_ptr<weld::Toolbar> mxDefaultBar;
+ std::unique_ptr<ToolbarUnoDispatcher> mxDefaultBarDispatch;
+ std::unique_ptr<weld::Toolbar> mxPositionBar;
+ std::unique_ptr<ToolbarUnoDispatcher> mxPositionBarDispatch;
+ std::unique_ptr<weld::Toolbar> mxSpacingBar;
+ std::unique_ptr<ToolbarUnoDispatcher> mxSpacingBarDispatch;
+
+ vcl::EnumContext maContext;
+};
+
+} // end of namespace svx::sidebar
+
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/sidebar/text/TextUnderlineControl.cxx b/svx/source/sidebar/text/TextUnderlineControl.cxx
new file mode 100644
index 000000000..ac65b8271
--- /dev/null
+++ b/svx/source/sidebar/text/TextUnderlineControl.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 "TextUnderlineControl.hxx"
+#include <svx/svxids.hrc>
+#include <sfx2/dispatch.hxx>
+#include <sfx2/viewfrm.hxx>
+#include <TextUnderlinePopup.hxx>
+#include <editeng/editids.hrc>
+#include <editeng/udlnitem.hxx>
+#include <helpids.h>
+
+namespace svx {
+
+TextUnderlineControl::TextUnderlineControl(TextUnderlinePopup* pControl, weld::Widget* pParent)
+ : WeldToolbarPopup(pControl->getFrameInterface(), pParent, "svx/ui/textunderlinecontrol.ui", "TextUnderlineControl")
+ , mxNone(m_xBuilder->weld_button("none"))
+ , mxSingle(m_xBuilder->weld_button("single"))
+ , mxDouble(m_xBuilder->weld_button("double"))
+ , mxBold(m_xBuilder->weld_button("bold"))
+ , mxDot(m_xBuilder->weld_button("dot"))
+ , mxDotBold(m_xBuilder->weld_button("dotbold"))
+ , mxDash(m_xBuilder->weld_button("dash"))
+ , mxDashLong(m_xBuilder->weld_button("dashlong"))
+ , mxDashDot(m_xBuilder->weld_button("dashdot"))
+ , mxDashDotDot(m_xBuilder->weld_button("dashdotdot"))
+ , mxWave(m_xBuilder->weld_button("wave"))
+ , mxMoreOptions(m_xBuilder->weld_button("moreoptions"))
+ , mxControl(pControl)
+{
+ mxMoreOptions->set_help_id(HID_UNDERLINE_BTN);
+
+ Link<weld::Button&,void> aLink = LINK(this, TextUnderlineControl, PBClickHdl);
+ mxNone->connect_clicked(aLink);
+ mxSingle->connect_clicked(aLink);
+ mxDouble->connect_clicked(aLink);
+ mxBold->connect_clicked(aLink);
+ mxDot->connect_clicked(aLink);
+ mxDotBold->connect_clicked(aLink);
+ mxDash->connect_clicked(aLink);
+ mxDashLong->connect_clicked(aLink);
+ mxDashDot->connect_clicked(aLink);
+ mxDashDotDot->connect_clicked(aLink);
+ mxWave->connect_clicked(aLink);
+ mxMoreOptions->connect_clicked(aLink);
+}
+
+void TextUnderlineControl::GrabFocus()
+{
+ mxNone->grab_focus();
+}
+
+TextUnderlineControl::~TextUnderlineControl()
+{
+}
+
+FontLineStyle TextUnderlineControl::getLineStyle(const weld::Button& rButton) const
+{
+ if (&rButton == mxSingle.get())
+ return LINESTYLE_SINGLE;
+ else if (&rButton == mxDouble.get())
+ return LINESTYLE_DOUBLE;
+ else if (&rButton == mxBold.get())
+ return LINESTYLE_BOLD;
+ else if (&rButton == mxDot.get())
+ return LINESTYLE_DOTTED;
+ else if (&rButton == mxDotBold.get())
+ return LINESTYLE_BOLDDOTTED;
+ else if (&rButton == mxDash.get())
+ return LINESTYLE_DASH;
+ else if (&rButton == mxDashLong.get())
+ return LINESTYLE_LONGDASH;
+ else if (&rButton == mxDashDot.get())
+ return LINESTYLE_DASHDOT;
+ else if (&rButton == mxDashDotDot.get())
+ return LINESTYLE_DASHDOTDOT;
+ else if (&rButton == mxWave.get())
+ return LINESTYLE_WAVE;
+
+ return LINESTYLE_NONE;
+}
+
+namespace {
+
+Color GetUnderlineColor()
+{
+ if (SfxViewFrame* pViewFrm = SfxViewFrame::Current())
+ {
+ const SvxUnderlineItem* pUnderlineItem;
+ pViewFrm->GetBindings().GetDispatcher()->QueryState(SID_ATTR_CHAR_UNDERLINE, pUnderlineItem);
+
+ if (pUnderlineItem)
+ return pUnderlineItem->GetColor();
+ }
+
+ return COL_AUTO;
+}
+
+}
+
+IMPL_LINK(TextUnderlineControl, PBClickHdl, weld::Button&, rButton, void)
+{
+ if (SfxViewFrame* pViewFrm = SfxViewFrame::Current())
+ {
+ if (&rButton == mxMoreOptions.get())
+ {
+ SfxDispatcher* pDisp = pViewFrm->GetBindings().GetDispatcher();
+ pDisp->Execute(SID_CHAR_DLG_EFFECT, SfxCallMode::ASYNCHRON);
+ }
+ else
+ {
+ const FontLineStyle eUnderline = getLineStyle(rButton);
+
+ SvxUnderlineItem aLineItem(eUnderline, SID_ATTR_CHAR_UNDERLINE);
+ aLineItem.SetColor(GetUnderlineColor());
+
+ pViewFrm->GetBindings().GetDispatcher()->ExecuteList(SID_ATTR_CHAR_UNDERLINE,
+ SfxCallMode::RECORD, { &aLineItem });
+ }
+ }
+ mxControl->EndPopupMode();
+}
+
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/sidebar/text/TextUnderlineControl.hxx b/svx/source/sidebar/text/TextUnderlineControl.hxx
new file mode 100644
index 000000000..cc1a19c67
--- /dev/null
+++ b/svx/source/sidebar/text/TextUnderlineControl.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_SVX_SOURCE_SIDEBAR_TEXT_TEXTUNDERLINECONTROL_HXX
+#define INCLUDED_SVX_SOURCE_SIDEBAR_TEXT_TEXTUNDERLINECONTROL_HXX
+
+#include <svtools/toolbarmenu.hxx>
+
+namespace svx
+{
+class TextUnderlinePopup;
+
+class TextUnderlineControl final : public WeldToolbarPopup
+{
+public:
+ explicit TextUnderlineControl(TextUnderlinePopup* pControl, weld::Widget* pParent);
+ virtual void GrabFocus() override;
+ virtual ~TextUnderlineControl() override;
+
+private:
+ std::unique_ptr<weld::Button> mxNone;
+ std::unique_ptr<weld::Button> mxSingle;
+ std::unique_ptr<weld::Button> mxDouble;
+ std::unique_ptr<weld::Button> mxBold;
+ std::unique_ptr<weld::Button> mxDot;
+ std::unique_ptr<weld::Button> mxDotBold;
+ std::unique_ptr<weld::Button> mxDash;
+ std::unique_ptr<weld::Button> mxDashLong;
+ std::unique_ptr<weld::Button> mxDashDot;
+ std::unique_ptr<weld::Button> mxDashDotDot;
+ std::unique_ptr<weld::Button> mxWave;
+ std::unique_ptr<weld::Button> mxMoreOptions;
+
+ rtl::Reference<TextUnderlinePopup> mxControl;
+
+ FontLineStyle getLineStyle(const weld::Button& rButton) const;
+
+ DECL_LINK(PBClickHdl, weld::Button&, void);
+};
+}
+
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/sidebar/text/TextUnderlinePopup.cxx b/svx/source/sidebar/text/TextUnderlinePopup.cxx
new file mode 100644
index 000000000..75529f297
--- /dev/null
+++ b/svx/source/sidebar/text/TextUnderlinePopup.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 <TextUnderlinePopup.hxx>
+#include "TextUnderlineControl.hxx"
+#include <vcl/toolbox.hxx>
+
+using namespace svx;
+
+TextUnderlinePopup::TextUnderlinePopup(
+ const css::uno::Reference<css::uno::XComponentContext>& rContext)
+ : PopupWindowController(rContext, nullptr, OUString())
+{
+}
+
+TextUnderlinePopup::~TextUnderlinePopup() {}
+
+void TextUnderlinePopup::initialize(const css::uno::Sequence<css::uno::Any>& rArguments)
+{
+ PopupWindowController::initialize(rArguments);
+
+ if (m_pToolbar)
+ {
+ mxPopoverContainer.reset(new ToolbarPopupContainer(m_pToolbar));
+ m_pToolbar->set_item_popover(m_aCommandURL.toUtf8(), mxPopoverContainer->getTopLevel());
+ }
+
+ ToolBox* pToolBox = nullptr;
+ ToolBoxItemId nId;
+ if (getToolboxId(nId, &pToolBox) && pToolBox->GetItemCommand(nId) == m_aCommandURL)
+ pToolBox->SetItemBits(nId, ToolBoxItemBits::DROPDOWN | pToolBox->GetItemBits(nId));
+}
+
+std::unique_ptr<WeldToolbarPopup> TextUnderlinePopup::weldPopupWindow()
+{
+ return std::make_unique<TextUnderlineControl>(this, m_pToolbar);
+}
+
+VclPtr<vcl::Window> TextUnderlinePopup::createVclPopupWindow(vcl::Window* pParent)
+{
+ mxInterimPopover = VclPtr<InterimToolbarPopup>::Create(
+ getFrameInterface(), pParent,
+ std::make_unique<TextUnderlineControl>(this, pParent->GetFrameWeld()));
+
+ mxInterimPopover->Show();
+
+ return mxInterimPopover;
+}
+
+OUString TextUnderlinePopup::getImplementationName()
+{
+ return "com.sun.star.comp.svx.UnderlineToolBoxControl";
+}
+
+css::uno::Sequence<OUString> TextUnderlinePopup::getSupportedServiceNames()
+{
+ return { "com.sun.star.frame.ToolbarController" };
+}
+
+extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface*
+com_sun_star_comp_svx_UnderlineToolBoxControl_get_implementation(
+ css::uno::XComponentContext* rContext, css::uno::Sequence<css::uno::Any> const&)
+{
+ return cppu::acquire(new TextUnderlinePopup(rContext));
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/sidebar/textcolumns/TextColumnsPropertyPanel.cxx b/svx/source/sidebar/textcolumns/TextColumnsPropertyPanel.cxx
new file mode 100644
index 000000000..2a06c5705
--- /dev/null
+++ b/svx/source/sidebar/textcolumns/TextColumnsPropertyPanel.cxx
@@ -0,0 +1,118 @@
+/* -*- 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 <sal/config.h>
+
+#include "TextColumnsPropertyPanel.hxx"
+
+#include <sfx2/app.hxx>
+#include <sfx2/dispatch.hxx>
+#include <sfx2/objsh.hxx>
+#include <svl/itempool.hxx>
+#include <svtools/unitconv.hxx>
+#include <svx/sdmetitm.hxx>
+#include <svx/svddef.hxx>
+#include <svx/svxids.hrc>
+
+#include <com/sun/star/lang/IllegalArgumentException.hpp>
+
+namespace
+{
+MapUnit GetUnit(const SfxBindings* pBindings, sal_uInt16 nWhich)
+{
+ assert(pBindings);
+
+ SfxObjectShell* pSh = nullptr;
+ if (auto pShell = pBindings->GetDispatcher()->GetShell(0))
+ pSh = pShell->GetObjectShell();
+ if (!pSh)
+ pSh = SfxObjectShell::Current();
+ SfxItemPool& rPool = pSh ? pSh->GetPool() : SfxGetpApp()->GetPool();
+ return rPool.GetMetric(nWhich);
+}
+}
+
+namespace svx::sidebar
+{
+TextColumnsPropertyPanel::TextColumnsPropertyPanel(weld::Widget* pParent, SfxBindings* pBindings)
+ : PanelLayout(pParent, "TextColumnsPropertyPanel", "svx/ui/sidebartextcolumnspanel.ui")
+ , mpBindings(pBindings)
+ , m_xColumnsNumber(m_xBuilder->weld_spin_button("FLD_COL_NUMBER"))
+ , m_xColumnsSpacing(m_xBuilder->weld_metric_spin_button("MTR_FLD_COL_SPACING", FieldUnit::CM))
+ , maColumnsNumberController(SID_ATTR_TEXTCOLUMNS_NUMBER, *pBindings, *this)
+ , maColumnsSpacingController(SID_ATTR_TEXTCOLUMNS_SPACING, *pBindings, *this)
+{
+ m_xColumnsNumber->connect_value_changed(
+ LINK(this, TextColumnsPropertyPanel, ModifyColumnsNumberHdl));
+ m_xColumnsSpacing->connect_value_changed(
+ LINK(this, TextColumnsPropertyPanel, ModifyColumnsSpacingHdl));
+}
+
+TextColumnsPropertyPanel::~TextColumnsPropertyPanel()
+{
+ maColumnsSpacingController.dispose();
+ maColumnsNumberController.dispose();
+
+ m_xColumnsSpacing.reset();
+ m_xColumnsNumber.reset();
+}
+
+IMPL_LINK_NOARG(TextColumnsPropertyPanel, ModifyColumnsNumberHdl, weld::SpinButton&, void)
+{
+ SfxInt16Item aItem(SDRATTR_TEXTCOLUMNS_NUMBER, m_xColumnsNumber->get_value());
+ mpBindings->GetDispatcher()->ExecuteList(SID_ATTR_TEXTCOLUMNS_NUMBER, SfxCallMode::RECORD,
+ { &aItem });
+}
+
+IMPL_LINK_NOARG(TextColumnsPropertyPanel, ModifyColumnsSpacingHdl, weld::MetricSpinButton&, void)
+{
+ const MapUnit aUnit = GetUnit(mpBindings, SDRATTR_TEXTCOLUMNS_SPACING);
+ SdrMetricItem aItem(SDRATTR_TEXTCOLUMNS_SPACING, GetCoreValue(*m_xColumnsSpacing, aUnit));
+ mpBindings->GetDispatcher()->ExecuteList(SID_ATTR_TEXTCOLUMNS_SPACING, SfxCallMode::RECORD,
+ { &aItem });
+}
+
+void TextColumnsPropertyPanel::NotifyItemUpdate(sal_uInt16 nSID, SfxItemState eState,
+ const SfxPoolItem* pState)
+{
+ switch (nSID)
+ {
+ case SID_ATTR_TEXTCOLUMNS_NUMBER:
+ if (eState >= SfxItemState::DEFAULT)
+ {
+ if (const auto pItem = dynamic_cast<const SfxInt16Item*>(pState))
+ m_xColumnsNumber->set_value(pItem->GetValue());
+ }
+ break;
+ case SID_ATTR_TEXTCOLUMNS_SPACING:
+ if (eState >= SfxItemState::DEFAULT)
+ {
+ const MapUnit aUnit = GetUnit(mpBindings, SDRATTR_TEXTCOLUMNS_SPACING);
+ if (const auto pItem = dynamic_cast<const SdrMetricItem*>(pState))
+ SetMetricValue(*m_xColumnsSpacing, pItem->GetValue(), aUnit);
+ }
+ break;
+ }
+}
+
+std::unique_ptr<PanelLayout> TextColumnsPropertyPanel::Create(weld::Widget* pParent,
+ SfxBindings* pBindings)
+{
+ if (pParent == nullptr)
+ throw css::lang::IllegalArgumentException(
+ "no parent Window given to TextColumnsPropertyPanel::Create", nullptr, 0);
+ if (pBindings == nullptr)
+ throw css::lang::IllegalArgumentException(
+ "no SfxBindings given to TextColumnsPropertyPanel::Create", nullptr, 2);
+
+ return std::make_unique<TextColumnsPropertyPanel>(pParent, pBindings);
+}
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab cinoptions=b1,g0,N-s cinkeys+=0=break: */
diff --git a/svx/source/sidebar/textcolumns/TextColumnsPropertyPanel.hxx b/svx/source/sidebar/textcolumns/TextColumnsPropertyPanel.hxx
new file mode 100644
index 000000000..e9c27fcc6
--- /dev/null
+++ b/svx/source/sidebar/textcolumns/TextColumnsPropertyPanel.hxx
@@ -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/.
+ */
+
+#pragma once
+
+#include <sfx2/sidebar/ControllerItem.hxx>
+#include <sfx2/sidebar/PanelLayout.hxx>
+
+class ColorListBox;
+
+namespace svx::sidebar
+{
+class TextColumnsPropertyPanel : public PanelLayout,
+ public ::sfx2::sidebar::ControllerItem::ItemUpdateReceiverInterface
+{
+public:
+ TextColumnsPropertyPanel(weld::Widget* pParent, SfxBindings* pBindings);
+ virtual ~TextColumnsPropertyPanel() override;
+
+ static std::unique_ptr<PanelLayout> Create(weld::Widget* pParent, 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:
+ SfxBindings* mpBindings;
+
+ std::unique_ptr<weld::SpinButton> m_xColumnsNumber;
+ std::unique_ptr<weld::MetricSpinButton> m_xColumnsSpacing;
+
+ sfx2::sidebar::ControllerItem maColumnsNumberController;
+ sfx2::sidebar::ControllerItem maColumnsSpacingController;
+
+ DECL_LINK(ModifyColumnsNumberHdl, weld::SpinButton&, void);
+ DECL_LINK(ModifyColumnsSpacingHdl, weld::MetricSpinButton&, void);
+};
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab cinoptions=b1,g0,N-s cinkeys+=0=break: */
diff --git a/svx/source/sidebar/tools/ValueSetWithTextControl.cxx b/svx/source/sidebar/tools/ValueSetWithTextControl.cxx
new file mode 100644
index 000000000..1b6ca2d42
--- /dev/null
+++ b/svx/source/sidebar/tools/ValueSetWithTextControl.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 <svx/sidebar/ValueSetWithTextControl.hxx>
+#include <sfx2/sidebar/Theme.hxx>
+
+#include <i18nlangtag/mslangid.hxx>
+#include <svtools/valueset.hxx>
+#include <vcl/event.hxx>
+#include <vcl/settings.hxx>
+#include <vcl/svapp.hxx>
+
+namespace svx::sidebar {
+
+ValueSetWithTextControl::ValueSetWithTextControl()
+ : ValueSet(nullptr)
+{
+}
+
+void ValueSetWithTextControl::SetDrawingArea(weld::DrawingArea* pDrawingArea)
+{
+ ValueSet::SetDrawingArea(pDrawingArea);
+
+ Size aSize(250, 300);
+ pDrawingArea->set_size_request(aSize.Width(), aSize.Height());
+ SetOutputSizePixel(aSize);
+
+ SetColCount();
+}
+
+void ValueSetWithTextControl::AddItem(
+ const OUString& rItemText,
+ const OUString& rItemText2 )
+{
+ ValueSetWithTextItem aItem;
+ aItem.maItemText = rItemText;
+ aItem.maItemText2 = rItemText2;
+
+ maItems.push_back( aItem );
+
+ InsertItem( maItems.size() );
+ SetItemText( maItems.size(), rItemText );
+}
+
+void ValueSetWithTextControl::UserDraw( const UserDrawEvent& rUDEvt )
+{
+ const tools::Rectangle aRect = rUDEvt.GetRect();
+ vcl::RenderContext* pDev = rUDEvt.GetRenderContext();
+ pDev->Push();
+ const sal_uInt16 nItemId = rUDEvt.GetItemId();
+
+ const tools::Long nRectHeight = aRect.GetHeight();
+
+ vcl::Font aFont(OutputDevice::GetDefaultFont(DefaultFontType::UI_SANS, MsLangId::getConfiguredSystemLanguage(), GetDefaultFontFlags::OnlyOne));
+ {
+ Size aSize = aFont.GetFontSize();
+ aSize.setHeight( (nRectHeight*4)/9 );
+ aFont.SetFontSize( aSize );
+ }
+
+ {
+ //draw background
+ if ( GetSelectedItemId() == nItemId )
+ {
+ tools::Rectangle aBackRect = aRect;
+ aBackRect.AdjustTop(3 );
+ aBackRect.AdjustBottom( -2 );
+ pDev->SetFillColor( sfx2::sidebar::Theme::GetColor( sfx2::sidebar::Theme::Color_Highlight ) );
+ pDev->DrawRect(aBackRect);
+ }
+ else
+ {
+ pDev->SetFillColor( COL_TRANSPARENT );
+ pDev->DrawRect(aRect);
+ }
+
+ if ( GetSelectedItemId() == nItemId )
+ {
+ aFont.SetColor( sfx2::sidebar::Theme::GetColor( sfx2::sidebar::Theme::Color_HighlightText ) );
+ }
+ else
+ {
+ aFont.SetColor( Application::GetSettings().GetStyleSettings().GetFieldTextColor() );
+ }
+
+ tools::Rectangle aStrRect = aRect;
+ aStrRect.AdjustTop(nRectHeight/4 );
+ aStrRect.AdjustBottom( -(nRectHeight/4) );
+
+ const tools::Long nRectWidth = aRect.GetWidth();
+ aStrRect.AdjustLeft(8 );
+ aStrRect.AdjustRight( -((nRectWidth*2)/3) );
+ pDev->SetFont(aFont);
+ pDev->DrawText(aStrRect, maItems[nItemId-1].maItemText, DrawTextFlags::EndEllipsis);
+ aStrRect.AdjustLeft(nRectWidth/3 );
+ aStrRect.AdjustRight((nRectWidth*2)/3 );
+ pDev->DrawText(aStrRect, maItems[nItemId-1].maItemText2, DrawTextFlags::EndEllipsis);
+ }
+
+ Invalidate( aRect );
+ pDev->Pop();
+}
+
+} // end of namespace svx::sidebar
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/smarttags/SmartTagMgr.cxx b/svx/source/smarttags/SmartTagMgr.cxx
new file mode 100644
index 000000000..875d4557d
--- /dev/null
+++ b/svx/source/smarttags/SmartTagMgr.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 .
+ */
+
+// SMARTTAGS
+
+#include <svx/SmartTagMgr.hxx>
+
+#include <vcl/svapp.hxx>
+#include <com/sun/star/smarttags/XSmartTagRecognizer.hpp>
+#include <com/sun/star/smarttags/XRangeBasedSmartTagRecognizer.hpp>
+#include <com/sun/star/smarttags/XSmartTagAction.hpp>
+#include <com/sun/star/deployment/ExtensionManager.hpp>
+#include <com/sun/star/smarttags/SmartTagRecognizerMode.hpp>
+#include <com/sun/star/i18n/BreakIterator.hpp>
+#include <com/sun/star/lang/XMultiServiceFactory.hpp>
+#include <com/sun/star/lang/XSingleComponentFactory.hpp>
+#include <com/sun/star/lang/XServiceInfo.hpp>
+#include <com/sun/star/configuration/theDefaultProvider.hpp>
+#include <com/sun/star/container/XContentEnumerationAccess.hpp>
+#include <com/sun/star/beans/XPropertySet.hpp>
+#include <com/sun/star/beans/PropertyValue.hpp>
+#include <com/sun/star/util/XChangesBatch.hpp>
+#include <com/sun/star/util/XChangesNotifier.hpp>
+#include <comphelper/processfactory.hxx>
+#include <comphelper/sequence.hxx>
+#include <rtl/ustring.hxx>
+
+using namespace com::sun::star;
+using namespace com::sun::star::uno;
+using namespace com::sun::star::i18n;
+
+
+SmartTagMgr::SmartTagMgr( const OUString& rApplicationName )
+ : maApplicationName( rApplicationName ),
+ mxContext( ::comphelper::getProcessComponentContext() ),
+ mbLabelTextWithSmartTags(true)
+{
+}
+
+SmartTagMgr::~SmartTagMgr()
+{
+}
+
+void SmartTagMgr::Init( std::u16string_view rConfigurationGroupName )
+{
+ PrepareConfiguration( rConfigurationGroupName );
+ ReadConfiguration( true, true );
+ RegisterListener();
+ LoadLibraries();
+}
+
+/** Dispatches the recognize call to all installed smart tag recognizers
+*/
+void SmartTagMgr::RecognizeString( const OUString& rText,
+ const Reference< text::XTextMarkup >& xMarkup,
+ const Reference< frame::XController >& xController,
+ const lang::Locale& rLocale,
+ sal_uInt32 nStart, sal_uInt32 nLen ) const
+{
+ for (const Reference < smarttags::XSmartTagRecognizer >& xRecognizer : maRecognizerList)
+ {
+ // if all smart tag types supported by this recognizer have been
+ // disabled, we do not have to call the recognizer:
+ bool bCallRecognizer = false;
+ const sal_uInt32 nSmartTagCount = xRecognizer->getSmartTagCount();
+ for ( sal_uInt32 j = 0; j < nSmartTagCount && !bCallRecognizer; ++j )
+ {
+ const OUString aSmartTagName = xRecognizer->getSmartTagName(j);
+ if ( IsSmartTagTypeEnabled( aSmartTagName ) )
+ bCallRecognizer = true;
+ }
+
+ if ( bCallRecognizer )
+ {
+ // get the break iterator
+ if ( !mxBreakIter.is() )
+ {
+ mxBreakIter.set( BreakIterator::create(mxContext) );
+ }
+ xRecognizer->recognize( rText, nStart, nLen,
+ smarttags::SmartTagRecognizerMode_PARAGRAPH,
+ rLocale, xMarkup, maApplicationName, xController,
+ mxBreakIter );
+ }
+ }
+}
+
+void SmartTagMgr::RecognizeTextRange(const Reference< text::XTextRange>& xRange,
+ const Reference< text::XTextMarkup >& xMarkup,
+ const Reference< frame::XController >& xController) const
+{
+ for (const Reference<smarttags::XSmartTagRecognizer>& xRecognizer : maRecognizerList)
+ {
+ Reference< smarttags::XRangeBasedSmartTagRecognizer > xRangeBasedRecognizer( xRecognizer, UNO_QUERY);
+
+ if (!xRangeBasedRecognizer.is()) continue;
+
+ // if all smart tag types supported by this recognizer have been
+ // disabled, we do not have to call the recognizer:
+ bool bCallRecognizer = false;
+ const sal_uInt32 nSmartTagCount = xRecognizer->getSmartTagCount();
+ for ( sal_uInt32 j = 0; j < nSmartTagCount && !bCallRecognizer; ++j )
+ {
+ const OUString aSmartTagName = xRecognizer->getSmartTagName(j);
+ if ( IsSmartTagTypeEnabled( aSmartTagName ) )
+ bCallRecognizer = true;
+ }
+
+ if ( bCallRecognizer )
+ {
+ xRangeBasedRecognizer->recognizeTextRange( xRange,
+ smarttags::SmartTagRecognizerMode_PARAGRAPH,
+ xMarkup, maApplicationName, xController);
+ }
+ }
+
+}
+
+void SmartTagMgr::GetActionSequences( std::vector< OUString >& rSmartTagTypes,
+ Sequence < Sequence< Reference< smarttags::XSmartTagAction > > >& rActionComponentsSequence,
+ Sequence < Sequence< sal_Int32 > >& rActionIndicesSequence ) const
+{
+ rActionComponentsSequence.realloc( rSmartTagTypes.size() );
+ auto pActionComponentsSequence = rActionComponentsSequence.getArray();
+ rActionIndicesSequence.realloc( rSmartTagTypes.size() );
+ auto pActionIndicesSequence = rActionIndicesSequence.getArray();
+
+ for ( size_t j = 0; j < rSmartTagTypes.size(); ++j )
+ {
+ const OUString& rSmartTagType = rSmartTagTypes[j];
+
+ const sal_Int32 nNumberOfActionRefs = maSmartTagMap.count( rSmartTagType );
+
+ Sequence< Reference< smarttags::XSmartTagAction > > aActions( nNumberOfActionRefs );
+ auto aActionsRange = asNonConstRange(aActions);
+ Sequence< sal_Int32 > aIndices( nNumberOfActionRefs );
+ auto aIndicesRange = asNonConstRange(aIndices);
+
+ sal_uInt16 i = 0;
+ auto iters = maSmartTagMap.equal_range( rSmartTagType );
+
+ for ( auto aActionsIter = iters.first; aActionsIter != iters.second; ++aActionsIter )
+ {
+ aActionsRange[ i ] = (*aActionsIter).second.mxSmartTagAction;
+ aIndicesRange[ i++ ] = (*aActionsIter).second.mnSmartTagIndex;
+ }
+
+ pActionComponentsSequence[ j ] = aActions;
+ pActionIndicesSequence[ j ] = aIndices;
+ }
+}
+
+/** Returns the caption for a smart tag type.
+*/
+OUString SmartTagMgr::GetSmartTagCaption( const OUString& rSmartTagType, const css::lang::Locale& rLocale ) const
+{
+ OUString aRet;
+
+ auto aLower = maSmartTagMap.find( rSmartTagType );
+
+ if ( aLower != maSmartTagMap.end() )
+ {
+ const ActionReference& rActionRef = (*aLower).second;
+ Reference< smarttags::XSmartTagAction > xAction = rActionRef.mxSmartTagAction;
+
+ if ( xAction.is() )
+ {
+ const sal_Int32 nSmartTagIndex = rActionRef.mnSmartTagIndex;
+ aRet = xAction->getSmartTagCaption( nSmartTagIndex, rLocale );
+ }
+ }
+
+ return aRet;
+}
+
+
+/** Returns true if the given smart tag type is enabled.
+*/
+bool SmartTagMgr::IsSmartTagTypeEnabled( const OUString& rSmartTagType ) const
+{
+ return maDisabledSmartTagTypes.end() == maDisabledSmartTagTypes.find( rSmartTagType );
+}
+
+/** Writes currently disabled smart tag types to configuration.
+*/
+void SmartTagMgr::WriteConfiguration( const bool* pIsLabelTextWithSmartTags,
+ const std::vector< OUString >* pDisabledTypes ) const
+{
+ if ( !mxConfigurationSettings.is() )
+ return;
+
+ bool bCommit = false;
+
+ if ( pIsLabelTextWithSmartTags )
+ {
+ const Any aEnabled( *pIsLabelTextWithSmartTags );
+
+ try
+ {
+ mxConfigurationSettings->setPropertyValue( "RecognizeSmartTags", aEnabled );
+ bCommit = true;
+ }
+ catch ( css::uno::Exception& )
+ {
+ }
+ }
+
+ if ( pDisabledTypes )
+ {
+ Sequence< OUString > aTypes = comphelper::containerToSequence(*pDisabledTypes);
+
+ const Any aNewTypes( aTypes );
+
+ try
+ {
+ mxConfigurationSettings->setPropertyValue( "ExcludedSmartTagTypes", aNewTypes );
+ bCommit = true;
+ }
+ catch ( css::uno::Exception& )
+ {
+ }
+ }
+
+ if ( bCommit )
+ {
+ try
+ {
+ Reference< util::XChangesBatch >( mxConfigurationSettings, UNO_QUERY_THROW )->commitChanges();
+ }
+ catch ( css::uno::Exception& )
+ {
+ }
+ }
+}
+
+// css::util::XModifyListener
+void SmartTagMgr::modified( const lang::EventObject& )
+{
+ SolarMutexGuard aGuard;
+
+ maRecognizerList.clear();
+ maActionList.clear();
+ maSmartTagMap.clear();
+
+ LoadLibraries();
+}
+
+// css::lang::XEventListener
+void SmartTagMgr::disposing( const lang::EventObject& rEvent )
+{
+ SolarMutexGuard aGuard;
+
+ uno::Reference< frame::XModel > xModel( rEvent.Source, uno::UNO_QUERY );
+ uno::Reference< util::XModifyBroadcaster > xMB(xModel, uno::UNO_QUERY);
+ uno::Reference< util::XChangesNotifier > xCN(xModel, uno::UNO_QUERY);
+
+ try
+ {
+ if( xMB.is() )
+ {
+ uno::Reference< util::XModifyListener > xListener( this );
+ xMB->removeModifyListener( xListener );
+ }
+ else if ( xCN.is() )
+ {
+ uno::Reference< util::XChangesListener > xListener( this );
+ xCN->removeChangesListener( xListener );
+ }
+ }
+ catch(Exception& )
+ {
+ }
+}
+
+// css::util::XChangesListener
+void SmartTagMgr::changesOccurred( const util::ChangesEvent& rEvent )
+{
+ SolarMutexGuard aGuard;
+
+ bool bExcludedTypes = false;
+ bool bRecognize = false;
+
+ for( const util::ElementChange& rElementChange : rEvent.Changes)
+ {
+ OUString sTemp;
+ rElementChange.Accessor >>= sTemp;
+
+ if ( sTemp == "ExcludedSmartTagTypes" )
+ bExcludedTypes = true;
+ else if ( sTemp == "RecognizeSmartTags" )
+ bRecognize = true;
+ }
+
+ ReadConfiguration( bExcludedTypes, bRecognize );
+}
+
+void SmartTagMgr::LoadLibraries()
+{
+ Reference< container::XContentEnumerationAccess > rContent( mxContext->getServiceManager(), UNO_QUERY_THROW );
+
+ // load recognizers: No recognizers -> nothing to do.
+ Reference < container::XEnumeration > rEnum = rContent->createContentEnumeration( "com.sun.star.smarttags.SmartTagRecognizer");
+ if ( !rEnum.is() || !rEnum->hasMoreElements() )
+ return;
+
+ // iterate over all implementations of the smart tag recognizer service:
+ while( rEnum->hasMoreElements())
+ {
+ const Any a = rEnum->nextElement();
+ Reference< lang::XSingleComponentFactory > xSCF;
+ Reference< lang::XServiceInfo > xsInfo;
+
+ if (a >>= xsInfo)
+ xSCF.set(xsInfo, UNO_QUERY);
+ else
+ continue;
+
+ Reference< smarttags::XSmartTagRecognizer > xLib ( xSCF->
+ createInstanceWithContext(mxContext), UNO_QUERY );
+
+ if (!xLib.is())
+ continue;
+
+ xLib->initialize( Sequence< Any >() );
+ maRecognizerList.push_back(xLib);
+ }
+
+ // load actions: No actions -> nothing to do.
+ rEnum = rContent->createContentEnumeration( "com.sun.star.smarttags.SmartTagAction");
+ if ( !rEnum.is() )
+ return;
+
+ // iterate over all implementations of the smart tag action service:
+ while( rEnum->hasMoreElements())
+ {
+ const Any a = rEnum->nextElement();
+ Reference< lang::XServiceInfo > xsInfo;
+ Reference< lang::XSingleComponentFactory > xSCF;
+
+ if (a >>= xsInfo)
+ xSCF.set(xsInfo, UNO_QUERY);
+ else
+ continue;
+
+ Reference< smarttags::XSmartTagAction > xLib ( xSCF->
+ createInstanceWithContext(mxContext), UNO_QUERY );
+
+ if (!xLib.is())
+ continue;
+
+ xLib->initialize( Sequence< Any >() );
+ maActionList.push_back(xLib);
+ }
+
+ AssociateActionsWithRecognizers();
+
+}
+
+void SmartTagMgr::PrepareConfiguration( std::u16string_view rConfigurationGroupName )
+{
+ Any aAny(
+ OUString::Concat("/org.openoffice.Office.Common/SmartTags/") + rConfigurationGroupName );
+ beans::PropertyValue aPathArgument;
+ aPathArgument.Name = "nodepath";
+ aPathArgument.Value = aAny;
+ Sequence< Any > aArguments{ Any(aPathArgument) };
+ Reference< lang::XMultiServiceFactory > xConfProv = configuration::theDefaultProvider::get( mxContext );
+
+ // try to get read-write access to configuration:
+ Reference< XInterface > xConfigurationAccess;
+ try
+ {
+ xConfigurationAccess = xConfProv->createInstanceWithArguments(
+ "com.sun.star.configuration.ConfigurationUpdateAccess", aArguments );
+ }
+ catch ( uno::Exception& )
+ {
+ }
+
+ // fallback: try read-only access to configuration:
+ if ( !xConfigurationAccess.is() )
+ {
+ try
+ {
+ xConfigurationAccess = xConfProv->createInstanceWithArguments(
+ "com.sun.star.configuration.ConfigurationAccess", aArguments );
+ }
+ catch ( uno::Exception& )
+ {
+ }
+ }
+
+ if ( xConfigurationAccess.is() )
+ {
+ mxConfigurationSettings.set( xConfigurationAccess, UNO_QUERY );
+ }
+}
+
+
+void SmartTagMgr::ReadConfiguration( bool bExcludedTypes, bool bRecognize )
+{
+ if ( !mxConfigurationSettings.is() )
+ return;
+
+ if ( bExcludedTypes )
+ {
+ maDisabledSmartTagTypes.clear();
+
+ Any aAny = mxConfigurationSettings->getPropertyValue( "ExcludedSmartTagTypes" );
+ Sequence< OUString > aValues;
+ aAny >>= aValues;
+
+ for ( const auto& rValue : std::as_const(aValues) )
+ maDisabledSmartTagTypes.insert( rValue );
+ }
+
+ if ( bRecognize )
+ {
+ Any aAny = mxConfigurationSettings->getPropertyValue( "RecognizeSmartTags" );
+ bool bValue = true;
+ aAny >>= bValue;
+
+ mbLabelTextWithSmartTags = bValue;
+ }
+}
+
+void SmartTagMgr::RegisterListener()
+{
+ // register as listener at package manager
+ try
+ {
+ Reference<deployment::XExtensionManager> xExtensionManager(
+ deployment::ExtensionManager::get( mxContext ) );
+ Reference< util::XModifyBroadcaster > xMB ( xExtensionManager, UNO_QUERY_THROW );
+
+ Reference< util::XModifyListener > xListener( this );
+ xMB->addModifyListener( xListener );
+ }
+ catch ( uno::Exception& )
+ {
+ }
+
+ // register as listener at configuration
+ try
+ {
+ Reference<util::XChangesNotifier> xCN( mxConfigurationSettings, UNO_QUERY_THROW );
+ Reference< util::XChangesListener > xListener( this );
+ xCN->addChangesListener( xListener );
+ }
+ catch ( uno::Exception& )
+ {
+ }
+}
+
+typedef std::pair < const OUString, ActionReference > SmartTagMapElement;
+
+/** Sets up a map that maps smart tag type names to actions references.
+*/
+void SmartTagMgr::AssociateActionsWithRecognizers()
+{
+ const sal_uInt32 nActionLibCount = maActionList.size();
+ const sal_uInt32 nRecognizerCount = maRecognizerList.size();
+
+ for ( sal_uInt32 i = 0; i < nRecognizerCount; ++i )
+ {
+ Reference < smarttags::XSmartTagRecognizer > xRecognizer = maRecognizerList[i];
+ const sal_uInt32 nSmartTagCount = xRecognizer->getSmartTagCount();
+ for ( sal_uInt32 j = 0; j < nSmartTagCount; ++j )
+ {
+ const OUString aSmartTagName = xRecognizer->getSmartTagName(j);
+
+ // check if smart tag type has already been processed:
+ if ( maSmartTagMap.find( aSmartTagName ) != maSmartTagMap.end() )
+ continue;
+
+ bool bFound = false;
+ for ( sal_uInt32 k = 0; k < nActionLibCount; ++k )
+ {
+ Reference< smarttags::XSmartTagAction > xActionLib = maActionList[k];
+ const sal_uInt32 nSmartTagCountInActionLib = xActionLib->getSmartTagCount();
+ for ( sal_uInt32 l = 0; l < nSmartTagCountInActionLib; ++l )
+ {
+ const OUString aSmartTagNameInActionLib = xActionLib->getSmartTagName(l);
+ if ( aSmartTagName == aSmartTagNameInActionLib )
+ {
+ // found actions and recognizer for same smarttag
+ ActionReference aActionRef( xActionLib, l );
+
+ // add recognizer/action pair to map
+ maSmartTagMap.insert( SmartTagMapElement( aSmartTagName, aActionRef ));
+
+ bFound = true;
+ }
+ }
+ }
+
+ if ( !bFound )
+ {
+ // insert 'empty' action reference if there is no action associated with
+ // the current smart tag type:
+ Reference< smarttags::XSmartTagAction > xActionLib;
+ ActionReference aActionRef( xActionLib, 0 );
+
+ // add recognizer/action pair to map
+ maSmartTagMap.insert( SmartTagMapElement( aSmartTagName, aActionRef ));
+ }
+ }
+ }
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/stbctrls/insctrl.cxx b/svx/source/stbctrls/insctrl.cxx
new file mode 100644
index 000000000..3ad0fdc1e
--- /dev/null
+++ b/svx/source/stbctrls/insctrl.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 <vcl/status.hxx>
+#include <svl/eitem.hxx>
+#include <tools/debug.hxx>
+
+#include <svx/strings.hrc>
+
+#include <svx/insctrl.hxx>
+#include <svx/dialmgr.hxx>
+
+SFX_IMPL_STATUSBAR_CONTROL(SvxInsertStatusBarControl, SfxBoolItem);
+
+SvxInsertStatusBarControl::SvxInsertStatusBarControl( sal_uInt16 _nSlotId,
+ sal_uInt16 _nId,
+ StatusBar& rStb ) :
+
+ SfxStatusBarControl( _nSlotId, _nId, rStb ),
+ bInsert( true )
+{
+}
+
+SvxInsertStatusBarControl::~SvxInsertStatusBarControl()
+{
+}
+
+void SvxInsertStatusBarControl::StateChangedAtStatusBarControl( sal_uInt16 , SfxItemState eState,
+ const SfxPoolItem* pState )
+{
+ if ( SfxItemState::DEFAULT != eState )
+ GetStatusBar().SetItemText( GetId(), "" );
+ else
+ {
+ DBG_ASSERT( dynamic_cast<const SfxBoolItem*>( pState) != nullptr, "invalid item type" );
+ const SfxBoolItem* pItem = static_cast<const SfxBoolItem*>(pState);
+ bInsert = pItem->GetValue();
+
+ if ( bInsert )
+ GetStatusBar().SetQuickHelpText( GetId(), SvxResId( RID_SVXSTR_INSERT_HELPTEXT ) );
+ else
+ GetStatusBar().SetQuickHelpText( GetId(), SvxResId( RID_SVXSTR_OVERWRITE_HELPTEXT ) );
+
+ DrawItemText_Impl();
+ }
+}
+
+void SvxInsertStatusBarControl::Paint( const UserDrawEvent& )
+{
+}
+
+void SvxInsertStatusBarControl::DrawItemText_Impl()
+{
+ OUString aText = "";
+ // tdf#107918 on macOS without an Insert key it's hard to figure out how to switch modes
+ // so we show both Insert and Overwrite
+#ifdef MACOSX
+ if ( bInsert )
+ aText = SvxResId( RID_SVXSTR_INSERT_TEXT );
+#endif
+ if ( !bInsert )
+ aText = SvxResId( RID_SVXSTR_OVERWRITE_TEXT );
+
+ GetStatusBar().SetItemText( GetId(), aText );
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/stbctrls/modctrl.cxx b/svx/source/stbctrls/modctrl.cxx
new file mode 100644
index 000000000..d65817526
--- /dev/null
+++ b/svx/source/stbctrls/modctrl.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 <vcl/status.hxx>
+#include <vcl/image.hxx>
+#include <vcl/timer.hxx>
+#include <vcl/idle.hxx>
+#include <vcl/event.hxx>
+#include <svl/eitem.hxx>
+#include <tools/debug.hxx>
+
+#include <svx/strings.hrc>
+#include <svx/modctrl.hxx>
+#include <svx/dialmgr.hxx>
+
+#include <com/sun/star/beans/PropertyValue.hpp>
+#include "modctrl_internal.hxx"
+#include <bitmaps.hlst>
+
+using ::com::sun::star::uno::Sequence;
+using ::com::sun::star::beans::PropertyValue;
+
+SFX_IMPL_STATUSBAR_CONTROL(SvxModifyControl, SfxBoolItem);
+
+struct SvxModifyControl::ImplData
+{
+ enum ModificationState
+ {
+ MODIFICATION_STATE_NO = 0,
+ MODIFICATION_STATE_YES,
+ MODIFICATION_STATE_FEEDBACK,
+ MODIFICATION_STATE_SIZE
+ };
+
+ Idle maIdle { "svx::SvxModifyControl maIdle" };
+ Image maImages[MODIFICATION_STATE_SIZE];
+
+ ModificationState mnModState;
+
+ ImplData():
+ mnModState(MODIFICATION_STATE_NO)
+ {
+ maImages[MODIFICATION_STATE_NO] = Image(StockImage::Yes, RID_SVXBMP_DOC_MODIFIED_NO);
+ maImages[MODIFICATION_STATE_YES] = Image(StockImage::Yes, RID_SVXBMP_DOC_MODIFIED_YES);
+ maImages[MODIFICATION_STATE_FEEDBACK] = Image(StockImage::Yes, RID_SVXBMP_DOC_MODIFIED_FEEDBACK);
+
+ maIdle.SetPriority(TaskPriority::LOWEST);
+ }
+};
+
+SvxModifyControl::SvxModifyControl( sal_uInt16 _nSlotId, sal_uInt16 _nId, StatusBar& rStb ) :
+ SfxStatusBarControl( _nSlotId, _nId, rStb ),
+ mxImpl(std::make_shared<ImplData>())
+{
+ mxImpl->maIdle.SetInvokeHandler( LINK(this, SvxModifyControl, OnTimer) );
+}
+
+
+void SvxModifyControl::StateChangedAtStatusBarControl( sal_uInt16, SfxItemState eState,
+ const SfxPoolItem* pState )
+{
+ if ( SfxItemState::DEFAULT != eState )
+ return;
+
+ DBG_ASSERT( dynamic_cast<const SfxBoolItem*>( pState) != nullptr, "invalid item type" );
+ const SfxBoolItem* pItem = static_cast<const SfxBoolItem*>(pState);
+ mxImpl->maIdle.Stop();
+
+ bool modified = pItem->GetValue();
+ bool start = ( !modified && mxImpl->mnModState == ImplData::MODIFICATION_STATE_YES); // should timer be started and feedback image displayed ?
+
+ mxImpl->mnModState = (start ? ImplData::MODIFICATION_STATE_FEEDBACK : (modified ? ImplData::MODIFICATION_STATE_YES : ImplData::MODIFICATION_STATE_NO));
+
+ _repaint();
+
+ TranslateId pResId = modified ? RID_SVXSTR_DOC_MODIFIED_YES : RID_SVXSTR_DOC_MODIFIED_NO;
+ GetStatusBar().SetQuickHelpText(GetId(), SvxResId(pResId));
+
+ if ( start )
+ mxImpl->maIdle.Start();
+}
+
+
+IMPL_LINK( SvxModifyControl, OnTimer, Timer *, pTimer, void )
+{
+ if (pTimer == nullptr)
+ return;
+
+ pTimer->Stop();
+ mxImpl->mnModState = ImplData::MODIFICATION_STATE_NO;
+
+ _repaint();
+}
+
+
+void SvxModifyControl::_repaint()
+{
+ GetStatusBar().SetItemData( GetId(), nullptr ); // force repaint
+}
+
+/**
+ * Given a bounding rectangle and an image, determine the top-left position
+ * of the image so that the image would look centered both horizontally and
+ * vertically.
+ *
+ * @param rBoundingRect bounding rectangle
+ * @param rImg image
+ *
+ * @return Point top-left corner of the centered image position
+ */
+Point centerImage(const tools::Rectangle& rBoundingRect, const Image& rImg)
+{
+ Size aImgSize = rImg.GetSizePixel();
+ Size aRectSize = rBoundingRect.GetSize();
+ tools::Long nXOffset = (aRectSize.getWidth() - aImgSize.getWidth())/2;
+ tools::Long nYOffset = (aRectSize.getHeight() - aImgSize.getHeight())/2;
+ Point aPt = rBoundingRect.TopLeft();
+ aPt += Point(nXOffset, nYOffset);
+ return aPt;
+}
+
+void SvxModifyControl::Paint( const UserDrawEvent& rUsrEvt )
+{
+ vcl::RenderContext* pDev = rUsrEvt.GetRenderContext();
+ tools::Rectangle aRect(rUsrEvt.GetRect());
+
+ ImplData::ModificationState state = mxImpl->mnModState;
+ Point aPt = centerImage(aRect, mxImpl->maImages[state]);
+ pDev->DrawImage(aPt, mxImpl->maImages[state]);
+}
+
+void SvxModifyControl::Click()
+{
+ if (mxImpl->mnModState != ImplData::MODIFICATION_STATE_YES)
+ // document not modified. nothing to do here.
+ return;
+
+ Sequence<PropertyValue> aArgs;
+ execute(".uno:Save", aArgs);
+}
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/stbctrls/modctrl_internal.hxx b/svx/source/stbctrls/modctrl_internal.hxx
new file mode 100644
index 000000000..d6d3d6834
--- /dev/null
+++ b/svx/source/stbctrls/modctrl_internal.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_SVX_SOURCE_STBCTRLS_MODCTRL_INTERNAL_HXX
+#define INCLUDED_SVX_SOURCE_STBCTRLS_MODCTRL_INTERNAL_HXX
+
+#include <tools/gen.hxx>
+
+class Image;
+
+Point centerImage(const tools::Rectangle& rBoundingRect, const Image& rImg);
+
+#endif // INCLUDED_SVX_SOURCE_STBCTRLS_MODCTRL_INTERNAL_HXX
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/stbctrls/pszctrl.cxx b/svx/source/stbctrls/pszctrl.cxx
new file mode 100644
index 000000000..129ac67eb
--- /dev/null
+++ b/svx/source/stbctrls/pszctrl.cxx
@@ -0,0 +1,486 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * 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/propertyvalue.hxx>
+#include <vcl/commandevent.hxx>
+#include <vcl/event.hxx>
+#include <vcl/fieldvalues.hxx>
+#include <vcl/status.hxx>
+#include <vcl/image.hxx>
+#include <vcl/settings.hxx>
+#include <vcl/svapp.hxx>
+#include <vcl/weld.hxx>
+#include <vcl/weldutils.hxx>
+#include <svl/stritem.hxx>
+#include <svl/ptitem.hxx>
+#include <sfx2/module.hxx>
+#include <svl/intitem.hxx>
+#include <sal/log.hxx>
+
+#include <svx/pszctrl.hxx>
+
+#define PAINT_OFFSET 5
+
+#include <editeng/sizeitem.hxx>
+#include "stbctrls.h"
+
+#include <svx/svxids.hrc>
+#include <bitmaps.hlst>
+#include <unotools/localedatawrapper.hxx>
+
+#include <com/sun/star/beans/PropertyValue.hpp>
+
+/* [Description]
+
+ Function used to create a text representation of
+ a metric value
+
+ nVal is the metric value in the unit eUnit.
+
+ [cross reference]
+
+ <SvxPosSizeStatusBarControl::Paint(const UserDrawEvent&)>
+*/
+
+OUString SvxPosSizeStatusBarControl::GetMetricStr_Impl( tools::Long nVal ) const
+{
+ // deliver and set the Metric of the application
+ FieldUnit eOutUnit = SfxModule::GetModuleFieldUnit( getFrameInterface() );
+
+ OUString sMetric;
+ const sal_Unicode cSep = Application::GetSettings().GetLocaleDataWrapper().getNumDecimalSep()[0];
+ sal_Int64 nConvVal = vcl::ConvertValue( nVal * 100, 0, 0, FieldUnit::MM_100TH, eOutUnit );
+
+ if ( nConvVal < 0 && ( nConvVal / 100 == 0 ) )
+ sMetric += "-";
+ sMetric += OUString::number(nConvVal / 100);
+
+ if( FieldUnit::NONE != eOutUnit )
+ {
+ sMetric += OUStringChar(cSep);
+ sal_Int64 nFract = nConvVal % 100;
+
+ if ( nFract < 0 )
+ nFract *= -1;
+ if ( nFract < 10 )
+ sMetric += "0";
+ sMetric += OUString::number(nFract);
+ }
+
+ return sMetric;
+}
+
+
+SFX_IMPL_STATUSBAR_CONTROL(SvxPosSizeStatusBarControl, SvxSizeItem);
+
+namespace {
+
+class FunctionPopup_Impl
+{
+ std::unique_ptr<weld::Builder> m_xBuilder;
+ std::unique_ptr<weld::Menu> m_xMenu;
+ sal_uInt32 m_nSelected;
+ static sal_uInt16 id_to_function(std::string_view rIdent);
+ static OString function_to_id(sal_uInt16 nFunc);
+public:
+ explicit FunctionPopup_Impl(sal_uInt32 nCheckEncoded);
+ OString Execute(weld::Window* pParent, const tools::Rectangle& rRect)
+ {
+ return m_xMenu->popup_at_rect(pParent, rRect);
+ }
+ sal_uInt32 GetSelected(std::string_view curident) const;
+};
+
+}
+
+sal_uInt16 FunctionPopup_Impl::id_to_function(std::string_view rIdent)
+{
+ if (rIdent == "avg")
+ return PSZ_FUNC_AVG;
+ else if (rIdent == "counta")
+ return PSZ_FUNC_COUNT2;
+ else if (rIdent == "count")
+ return PSZ_FUNC_COUNT;
+ else if (rIdent == "max")
+ return PSZ_FUNC_MAX;
+ else if (rIdent == "min")
+ return PSZ_FUNC_MIN;
+ else if (rIdent == "sum")
+ return PSZ_FUNC_SUM;
+ else if (rIdent == "selection")
+ return PSZ_FUNC_SELECTION_COUNT;
+ else if (rIdent == "none")
+ return PSZ_FUNC_NONE;
+ return 0;
+}
+
+OString FunctionPopup_Impl::function_to_id(sal_uInt16 nFunc)
+{
+ switch (nFunc)
+ {
+ case PSZ_FUNC_AVG:
+ return "avg";
+ case PSZ_FUNC_COUNT2:
+ return "counta";
+ case PSZ_FUNC_COUNT:
+ return "count";
+ case PSZ_FUNC_MAX:
+ return "max";
+ case PSZ_FUNC_MIN:
+ return "min";
+ case PSZ_FUNC_SUM:
+ return "sum";
+ case PSZ_FUNC_SELECTION_COUNT:
+ return "selection";
+ case PSZ_FUNC_NONE:
+ return "none";
+ }
+ return OString();
+}
+
+FunctionPopup_Impl::FunctionPopup_Impl(sal_uInt32 nCheckEncoded)
+ : m_xBuilder(Application::CreateBuilder(nullptr, "svx/ui/functionmenu.ui"))
+ , m_xMenu(m_xBuilder->weld_menu("menu"))
+ , m_nSelected(nCheckEncoded)
+{
+ for ( sal_uInt16 nCheck = 1; nCheck < 32; ++nCheck )
+ if ( nCheckEncoded & (1u << nCheck) )
+ m_xMenu->set_active(function_to_id(nCheck), true);
+}
+
+sal_uInt32 FunctionPopup_Impl::GetSelected(std::string_view curident) const
+{
+ sal_uInt32 nSelected = m_nSelected;
+ sal_uInt16 nCurItemId = id_to_function(curident);
+ if ( nCurItemId == PSZ_FUNC_NONE )
+ nSelected = ( 1 << PSZ_FUNC_NONE );
+ else
+ {
+ nSelected &= (~( 1u << PSZ_FUNC_NONE )); // Clear the "None" bit
+ nSelected ^= ( 1u << nCurItemId ); // Toggle the bit corresponding to nCurItemId
+ if ( !nSelected )
+ nSelected = ( 1u << PSZ_FUNC_NONE );
+ }
+ return nSelected;
+}
+
+struct SvxPosSizeStatusBarControl_Impl
+
+/* [Description]
+
+ This implementation-structure of the class SvxPosSizeStatusBarControl
+ is done for the un-linking of the changes of the exported interface such as
+ the toning down of symbols that are visible externally.
+
+ One instance exists for each SvxPosSizeStatusBarControl-instance
+ during its life time
+*/
+
+{
+ Point aPos; // valid when a position is shown
+ Size aSize; // valid when a size is shown
+ OUString aStr; // valid when a text is shown
+ bool bPos; // show position ?
+ bool bSize; // set size ?
+ bool bTable; // set table index ?
+ bool bHasMenu; // set StarCalc popup menu ?
+ sal_uInt32 nFunctionSet; // the selected StarCalc functions encoded in 32 bits
+ Image aPosImage; // Image to show the position
+ Image aSizeImage; // Image to show the size
+};
+
+/* [Description]
+
+ Ctor():
+ Create an instance of the implementation class,
+ load the images for the position and size
+*/
+
+#define STR_POSITION ".uno:Position"
+#define STR_TABLECELL ".uno:StateTableCell"
+#define STR_FUNC ".uno:StatusBarFunc"
+
+SvxPosSizeStatusBarControl::SvxPosSizeStatusBarControl( sal_uInt16 _nSlotId,
+ sal_uInt16 _nId,
+ StatusBar& rStb ) :
+ SfxStatusBarControl( _nSlotId, _nId, rStb ),
+ pImpl( new SvxPosSizeStatusBarControl_Impl )
+{
+ pImpl->bPos = false;
+ pImpl->bSize = false;
+ pImpl->bTable = false;
+ pImpl->bHasMenu = false;
+ pImpl->nFunctionSet = 0;
+ pImpl->aPosImage = Image(StockImage::Yes, RID_SVXBMP_POSITION);
+ pImpl->aSizeImage = Image(StockImage::Yes, RID_SVXBMP_SIZE);
+
+ addStatusListener( STR_POSITION); // SID_ATTR_POSITION
+ addStatusListener( STR_TABLECELL); // SID_TABLE_CELL
+ addStatusListener( STR_FUNC); // SID_PSZ_FUNCTION
+ ImplUpdateItemText();
+}
+
+/* [Description]
+
+ Dtor():
+ remove the pointer to the implementation class, so that the timer is stopped
+
+*/
+
+SvxPosSizeStatusBarControl::~SvxPosSizeStatusBarControl()
+{
+}
+
+
+/* [Description]
+
+ SID_PSZ_FUNCTION activates the popup menu for Calc:
+
+ Status overview
+ Depending on the type of the item, a special setting is enabled, the others disabled.
+
+ NULL/Void SfxPointItem SvxSizeItem SfxStringItem
+ ------------------------------------------------------------------------
+ Position sal_False FALSE
+ Size FALSE TRUE FALSE
+ Text sal_False sal_False TRUE
+
+*/
+
+void SvxPosSizeStatusBarControl::StateChangedAtStatusBarControl( sal_uInt16 nSID, SfxItemState eState,
+ const SfxPoolItem* pState )
+{
+ // Because the combi-controller, always sets the current Id as HelpId
+ // first clean the cached HelpText
+ GetStatusBar().SetHelpText( GetId(), "" );
+
+ switch ( nSID )
+ {
+ case SID_ATTR_POSITION : GetStatusBar().SetHelpId( GetId(), STR_POSITION ); break;
+ case SID_TABLE_CELL: GetStatusBar().SetHelpId( GetId(), STR_TABLECELL ); break;
+ case SID_PSZ_FUNCTION: GetStatusBar().SetHelpId( GetId(), STR_FUNC ); break;
+ default: break;
+ }
+
+ if ( nSID == SID_PSZ_FUNCTION )
+ {
+ if ( eState == SfxItemState::DEFAULT )
+ {
+ pImpl->bHasMenu = true;
+ if ( auto pUInt32Item = dynamic_cast< const SfxUInt32Item* >(pState) )
+ pImpl->nFunctionSet = pUInt32Item->GetValue();
+ }
+ else
+ pImpl->bHasMenu = false;
+ }
+ else if ( SfxItemState::DEFAULT != eState )
+ {
+ // don't switch to empty display before an empty state was
+ // notified for all display types
+
+ if ( nSID == SID_TABLE_CELL )
+ pImpl->bTable = false;
+ else if ( nSID == SID_ATTR_POSITION )
+ pImpl->bPos = false;
+ else if ( nSID == GetSlotId() ) // controller is registered for SID_ATTR_SIZE
+ pImpl->bSize = false;
+ else
+ {
+ SAL_WARN( "svx.stbcrtls","unknown slot id");
+ }
+ }
+ else if ( auto pPointItem = dynamic_cast<const SfxPointItem*>( pState) )
+ {
+ // show position
+ pImpl->aPos = pPointItem->GetValue();
+ pImpl->bPos = true;
+ pImpl->bTable = false;
+ }
+ else if ( auto pSizeItem = dynamic_cast<const SvxSizeItem*>( pState) )
+ {
+ // show size
+ pImpl->aSize = pSizeItem->GetSize();
+ pImpl->bSize = true;
+ pImpl->bTable = false;
+ }
+ else if ( auto pStringItem = dynamic_cast<const SfxStringItem*>( pState) )
+ {
+ // show string (table cel or different)
+ pImpl->aStr = pStringItem->GetValue();
+ pImpl->bTable = true;
+ pImpl->bPos = false;
+ pImpl->bSize = false;
+ }
+ else
+ {
+ SAL_WARN( "svx.stbcrtls", "invalid item type" );
+ pImpl->bPos = false;
+ pImpl->bSize = false;
+ pImpl->bTable = false;
+ }
+
+ GetStatusBar().SetItemData( GetId(), nullptr );
+
+ ImplUpdateItemText();
+}
+
+
+/* [Description]
+
+ execute popup menu, when the status enables this
+*/
+
+void SvxPosSizeStatusBarControl::Command( const CommandEvent& rCEvt )
+{
+ if ( rCEvt.GetCommand() == CommandEventId::ContextMenu && pImpl->bHasMenu )
+ {
+ sal_uInt32 nSelect = pImpl->nFunctionSet;
+ if (!nSelect)
+ nSelect = ( 1 << PSZ_FUNC_NONE );
+ tools::Rectangle aRect(rCEvt.GetMousePosPixel(), Size(1,1));
+ weld::Window* pParent = weld::GetPopupParent(GetStatusBar(), aRect);
+ FunctionPopup_Impl aMenu(nSelect);
+ OString sIdent = aMenu.Execute(pParent, aRect);
+ if (!sIdent.isEmpty())
+ {
+ nSelect = aMenu.GetSelected(sIdent);
+ if (nSelect)
+ {
+ if (nSelect == (1 << PSZ_FUNC_NONE))
+ nSelect = 0;
+
+ css::uno::Any a;
+ SfxUInt32Item aItem( SID_PSZ_FUNCTION, nSelect );
+ aItem.QueryValue( a );
+ css::uno::Sequence< css::beans::PropertyValue > aArgs{ comphelper::makePropertyValue(
+ "StatusBarFunc", a) };
+ execute( ".uno:StatusBarFunc", aArgs );
+ }
+ }
+ }
+ else
+ SfxStatusBarControl::Command( rCEvt );
+}
+
+
+/* [Description]
+
+ Depending on the type to be shown, the value us shown. First the
+ rectangle is repainted (removed).
+*/
+
+void SvxPosSizeStatusBarControl::Paint( const UserDrawEvent& rUsrEvt )
+{
+ vcl::RenderContext* pDev = rUsrEvt.GetRenderContext();
+
+ const tools::Rectangle& rRect = rUsrEvt.GetRect();
+ StatusBar& rBar = GetStatusBar();
+ Point aItemPos = rBar.GetItemTextPos( GetId() );
+ Color aOldLineColor = pDev->GetLineColor();
+ Color aOldFillColor = pDev->GetFillColor();
+ pDev->SetLineColor();
+ pDev->SetFillColor( pDev->GetBackground().GetColor() );
+
+ if ( pImpl->bPos || pImpl->bSize )
+ {
+ // count the position for showing the size
+ tools::Long nSizePosX =
+ rRect.Left() + rRect.GetWidth() / 2 + PAINT_OFFSET;
+ // draw position
+ Point aPnt = rRect.TopLeft();
+ aPnt.setY( aItemPos.Y() );
+ aPnt.AdjustX(PAINT_OFFSET );
+ pDev->DrawImage( aPnt, pImpl->aPosImage );
+ aPnt.AdjustX(pImpl->aPosImage.GetSizePixel().Width() );
+ aPnt.AdjustX(PAINT_OFFSET );
+ OUString aStr = GetMetricStr_Impl( pImpl->aPos.X()) + " / " +
+ GetMetricStr_Impl( pImpl->aPos.Y());
+ tools::Rectangle aRect(aPnt, Point(nSizePosX, rRect.Bottom()));
+ pDev->DrawRect(aRect);
+ vcl::Region aOrigRegion(pDev->GetClipRegion());
+ pDev->SetClipRegion(vcl::Region(aRect));
+ pDev->DrawText(aPnt, aStr);
+ pDev->SetClipRegion(aOrigRegion);
+
+ // draw the size, when available
+ aPnt.setX( nSizePosX );
+
+ if ( pImpl->bSize )
+ {
+ pDev->DrawImage( aPnt, pImpl->aSizeImage );
+ aPnt.AdjustX(pImpl->aSizeImage.GetSizePixel().Width() );
+ Point aDrwPnt = aPnt;
+ aPnt.AdjustX(PAINT_OFFSET );
+ aStr = GetMetricStr_Impl( pImpl->aSize.Width() ) + " x " +
+ GetMetricStr_Impl( pImpl->aSize.Height() );
+ aRect = tools::Rectangle(aDrwPnt, rRect.BottomRight());
+ pDev->DrawRect(aRect);
+ aOrigRegion = pDev->GetClipRegion();
+ pDev->SetClipRegion(vcl::Region(aRect));
+ pDev->DrawText(aPnt, aStr);
+ pDev->SetClipRegion(aOrigRegion);
+ }
+ else
+ pDev->DrawRect( tools::Rectangle( aPnt, rRect.BottomRight() ) );
+ }
+ else if ( pImpl->bTable )
+ {
+ pDev->DrawRect( rRect );
+ pDev->DrawText( Point(
+ rRect.Left() + rRect.GetWidth() / 2 - pDev->GetTextWidth( pImpl->aStr ) / 2,
+ aItemPos.Y() ), pImpl->aStr );
+ }
+ else
+ {
+ // Empty display if neither size nor table position are available.
+ // Date/Time are no longer used (#65302#).
+ pDev->DrawRect( rRect );
+ }
+
+ pDev->SetLineColor( aOldLineColor );
+ pDev->SetFillColor( aOldFillColor );
+}
+
+void SvxPosSizeStatusBarControl::ImplUpdateItemText()
+{
+ // set only strings as text at the statusBar, so that the Help-Tips
+ // can work with the text, when it is too long for the statusBar
+ OUString aText;
+ int nCharsWidth = -1;
+ if ( pImpl->bPos || pImpl->bSize )
+ {
+ aText = GetMetricStr_Impl( pImpl->aPos.X()) + " / " +
+ GetMetricStr_Impl( pImpl->aPos.Y());
+ // widest X/Y string looks like "-999,99"
+ nCharsWidth = 1 + 6 + 3 + 6; // icon + x + slash + y
+ if ( pImpl->bSize )
+ {
+ aText += " " + GetMetricStr_Impl( pImpl->aSize.Width() ) + " x " +
+ GetMetricStr_Impl( pImpl->aSize.Height() );
+ nCharsWidth += 1 + 1 + 4 + 3 + 4; // icon + space + w + x + h
+ }
+ }
+ else if ( pImpl->bTable )
+ aText = pImpl->aStr;
+
+ GetStatusBar().SetItemText( GetId(), aText, nCharsWidth );
+}
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/stbctrls/selctrl.cxx b/svx/source/stbctrls/selctrl.cxx
new file mode 100644
index 000000000..8c32f3ed8
--- /dev/null
+++ b/svx/source/stbctrls/selctrl.cxx
@@ -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 .
+ */
+
+#include <sal/config.h>
+
+#include <string_view>
+
+#include <comphelper/propertyvalue.hxx>
+#include <vcl/event.hxx>
+#include <vcl/status.hxx>
+#include <vcl/svapp.hxx>
+#include <vcl/weldutils.hxx>
+#include <svl/intitem.hxx>
+#include <tools/urlobj.hxx>
+
+#include <svx/selctrl.hxx>
+
+#include <bitmaps.hlst>
+
+#include <com/sun/star/beans/PropertyValue.hpp>
+#include <com/sun/star/frame/XFrame.hpp>
+#include <com/sun/star/lang/XServiceInfo.hpp>
+
+#include <svx/strings.hrc>
+#include <svx/dialmgr.hxx>
+
+SFX_IMPL_STATUSBAR_CONTROL(SvxSelectionModeControl, SfxUInt16Item);
+
+namespace {
+
+/// Popup menu to select the selection type
+class SelectionTypePopup
+{
+ weld::Window* m_pPopupParent;
+ std::unique_ptr<weld::Builder> m_xBuilder;
+ std::unique_ptr<weld::Menu> m_xMenu;
+ static OString state_to_id(sal_uInt16 nState);
+public:
+ SelectionTypePopup(weld::Window* pPopupParent, sal_uInt16 nCurrent);
+ OUString GetItemTextForState(sal_uInt16 nState) { return m_xMenu->get_label(state_to_id(nState)); }
+ OString popup_at_rect(const tools::Rectangle& rRect)
+ {
+ return m_xMenu->popup_at_rect(m_pPopupParent, rRect);
+ }
+ void HideSelectionType(const OString& rIdent)
+ {
+ m_xMenu->remove(rIdent);
+ }
+ static sal_uInt16 id_to_state(std::string_view ident);
+};
+
+}
+
+sal_uInt16 SelectionTypePopup::id_to_state(std::string_view ident)
+{
+ if (ident == "block")
+ return 3;
+ else if (ident == "adding")
+ return 2;
+ else if (ident == "extending")
+ return 1;
+ else // fall through
+ return 0;
+}
+
+OString SelectionTypePopup::state_to_id(sal_uInt16 nState)
+{
+ switch (nState)
+ {
+ default: // fall through
+ case 0: return "standard";
+ case 1: return "extending";
+ case 2: return "adding";
+ case 3: return "block";
+ }
+}
+
+SelectionTypePopup::SelectionTypePopup(weld::Window* pPopupParent, sal_uInt16 nCurrent)
+ : m_pPopupParent(pPopupParent)
+ , m_xBuilder(Application::CreateBuilder(m_pPopupParent, "svx/ui/selectionmenu.ui"))
+ , m_xMenu(m_xBuilder->weld_menu("menu"))
+{
+ m_xMenu->set_active(state_to_id(nCurrent), true);
+}
+
+SvxSelectionModeControl::SvxSelectionModeControl( sal_uInt16 _nSlotId,
+ sal_uInt16 _nId,
+ StatusBar& rStb ) :
+ SfxStatusBarControl( _nSlotId, _nId, rStb ),
+ mnState( 0 ),
+ maImages{Image(StockImage::Yes, RID_SVXBMP_STANDARD_SELECTION),
+ Image(StockImage::Yes, RID_SVXBMP_EXTENDING_SELECTION),
+ Image(StockImage::Yes, RID_SVXBMP_ADDING_SELECTION),
+ Image(StockImage::Yes, RID_SVXBMP_BLOCK_SELECTION)},
+ mbFeatureEnabled(false)
+{
+ GetStatusBar().SetQuickHelpText(GetId(), u"");
+}
+
+void SvxSelectionModeControl::StateChangedAtStatusBarControl( sal_uInt16, SfxItemState eState,
+ const SfxPoolItem* pState )
+{
+ mbFeatureEnabled = SfxItemState::DEFAULT == eState;
+ if (mbFeatureEnabled)
+ {
+ DBG_ASSERT( dynamic_cast< const SfxUInt16Item* >(pState) != nullptr, "invalid item type" );
+ const SfxUInt16Item* pItem = static_cast<const SfxUInt16Item*>(pState);
+ mnState = pItem->GetValue();
+ SelectionTypePopup aPop(GetStatusBar().GetFrameWeld(), mnState);
+ GetStatusBar().SetQuickHelpText(GetId(),
+ SvxResId(RID_SVXSTR_SELECTIONMODE_HELPTEXT).
+ replaceFirst("%1", aPop.GetItemTextForState(mnState)));
+ GetStatusBar().Invalidate();
+ }
+}
+
+bool SvxSelectionModeControl::MouseButtonDown( const MouseEvent& rEvt )
+{
+ if (!mbFeatureEnabled)
+ return true;
+
+ ::tools::Rectangle aRect(rEvt.GetPosPixel(), Size(1, 1));
+ weld::Window* pPopupParent = weld::GetPopupParent(GetStatusBar(), aRect);
+ SelectionTypePopup aPop(pPopupParent, mnState);
+
+ // Check if Calc is opened; if true, hide block selection mode tdf#122280
+ const css::uno::Reference < css::frame::XModel > xModel = m_xFrame->getController()->getModel();
+ css::uno::Reference< css::lang::XServiceInfo > xServices( xModel, css::uno::UNO_QUERY );
+ if ( xServices.is() )
+ {
+ bool bSpecModeCalc = xServices->supportsService("com.sun.star.sheet.SpreadsheetDocument");
+ if (bSpecModeCalc)
+ aPop.HideSelectionType("block");
+ }
+
+ OString sIdent = aPop.popup_at_rect(aRect);
+ if (!sIdent.isEmpty())
+ {
+ sal_uInt16 nNewState = SelectionTypePopup::id_to_state(sIdent);
+ if ( nNewState != mnState )
+ {
+ mnState = nNewState;
+
+ css::uno::Any a;
+ SfxUInt16Item aState( GetSlotId(), mnState );
+ aState.QueryValue( a );
+ INetURLObject aObj( m_aCommandURL );
+
+ css::uno::Sequence< css::beans::PropertyValue > aArgs{ comphelper::makePropertyValue(
+ aObj.GetURLPath(), a) };
+ execute( aArgs );
+ }
+ }
+
+ return true;
+}
+
+void SvxSelectionModeControl::Click()
+{
+}
+
+void SvxSelectionModeControl::Paint( const UserDrawEvent& rUsrEvt )
+{
+ const tools::Rectangle aControlRect = getControlRect();
+ vcl::RenderContext* pDev = rUsrEvt.GetRenderContext();
+ tools::Rectangle aRect = rUsrEvt.GetRect();
+
+ Size aImgSize( maImages[mnState].GetSizePixel() );
+
+ Point aPos( aRect.Left() + ( aControlRect.GetWidth() - aImgSize.Width() ) / 2,
+ aRect.Top() + ( aControlRect.GetHeight() - aImgSize.Height() ) / 2 );
+
+ if (mbFeatureEnabled)
+ pDev->DrawImage(aPos, maImages[mnState]);
+ else
+ pDev->DrawImage(aPos, Image());
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/stbctrls/stbctrls.h b/svx/source/stbctrls/stbctrls.h
new file mode 100644
index 000000000..3b12d6f95
--- /dev/null
+++ b/svx/source/stbctrls/stbctrls.h
@@ -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_SVX_SOURCE_STBCTRLS_STBCTRLS_H
+#define INCLUDED_SVX_SOURCE_STBCTRLS_STBCTRLS_H
+
+// IDs as in SUBTOTAL_FUNC of Calc
+
+#define PSZ_FUNC_AVG 1
+#define PSZ_FUNC_COUNT2 3
+#define PSZ_FUNC_COUNT 2
+#define PSZ_FUNC_MAX 4
+#define PSZ_FUNC_MIN 5
+#define PSZ_FUNC_SUM 9
+#define PSZ_FUNC_SELECTION_COUNT 13
+#define PSZ_FUNC_NONE 16
+
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/stbctrls/xmlsecctrl.cxx b/svx/source/stbctrls/xmlsecctrl.cxx
new file mode 100644
index 000000000..0a26128d9
--- /dev/null
+++ b/svx/source/stbctrls/xmlsecctrl.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 <sal/config.h>
+
+#include <comphelper/propertyvalue.hxx>
+#include <vcl/commandevent.hxx>
+#include <vcl/image.hxx>
+#include <vcl/event.hxx>
+#include <vcl/status.hxx>
+#include <vcl/svapp.hxx>
+#include <vcl/weldutils.hxx>
+#include <sfx2/signaturestate.hxx>
+#include <sfx2/module.hxx>
+
+#include <svl/intitem.hxx>
+
+#include <svx/strings.hrc>
+#include <svx/dialmgr.hxx>
+#include <svx/xmlsecctrl.hxx>
+#include <tools/urlobj.hxx>
+#include <bitmaps.hlst>
+#include <sal/log.hxx>
+
+#include <com/sun/star/beans/PropertyValue.hpp>
+
+SFX_IMPL_STATUSBAR_CONTROL( XmlSecStatusBarControl, SfxUInt16Item );
+
+struct XmlSecStatusBarControl::XmlSecStatusBarControl_Impl
+{
+ SignatureState mnState;
+ Image maImage;
+ Image maImageBroken;
+ Image maImageNotValidated;
+};
+
+
+XmlSecStatusBarControl::XmlSecStatusBarControl( sal_uInt16 _nSlotId, sal_uInt16 _nId, StatusBar& _rStb )
+ :SfxStatusBarControl( _nSlotId, _nId, _rStb )
+ ,mpImpl( new XmlSecStatusBarControl_Impl )
+{
+ mpImpl->mnState = SignatureState::UNKNOWN;
+
+ mpImpl->maImage = Image(StockImage::Yes, RID_SVXBMP_SIGNET);
+ mpImpl->maImageBroken = Image(StockImage::Yes, RID_SVXBMP_SIGNET_BROKEN);
+ mpImpl->maImageNotValidated = Image(StockImage::Yes, RID_SVXBMP_SIGNET_NOTVALIDATED);
+}
+
+XmlSecStatusBarControl::~XmlSecStatusBarControl()
+{
+}
+
+void XmlSecStatusBarControl::StateChangedAtStatusBarControl( sal_uInt16, SfxItemState eState, const SfxPoolItem* pState )
+{
+ if( SfxItemState::DEFAULT != eState )
+ {
+ mpImpl->mnState = SignatureState::UNKNOWN;
+ }
+ else if( auto pUint16Item = dynamic_cast< const SfxUInt16Item* >(pState) )
+ {
+ mpImpl->mnState = static_cast<SignatureState>(pUint16Item->GetValue());
+ }
+ else
+ {
+ SAL_WARN( "svx.stbcrtls", "+XmlSecStatusBarControl::StateChangedAtStatusBarControl(): invalid item type" );
+ mpImpl->mnState = SignatureState::UNKNOWN;
+ }
+
+ GetStatusBar().SetItemData( GetId(), nullptr ); // necessary ?
+
+ GetStatusBar().SetItemText( GetId(), "" ); // necessary ?
+
+ TranslateId pResId = RID_SVXSTR_XMLSEC_NO_SIG;
+ if ( mpImpl->mnState == SignatureState::OK )
+ pResId = RID_SVXSTR_XMLSEC_SIG_OK;
+ else if ( mpImpl->mnState == SignatureState::BROKEN )
+ pResId = RID_SVXSTR_XMLSEC_SIG_NOT_OK;
+ else if ( mpImpl->mnState == SignatureState::NOTVALIDATED )
+ pResId = RID_SVXSTR_XMLSEC_SIG_OK_NO_VERIFY;
+ else if ( mpImpl->mnState == SignatureState::PARTIAL_OK )
+ pResId = RID_SVXSTR_XMLSEC_SIG_CERT_OK_PARTIAL_SIG;
+
+ GetStatusBar().SetQuickHelpText(GetId(), SvxResId(pResId));
+}
+
+void XmlSecStatusBarControl::Command( const CommandEvent& rCEvt )
+{
+ if( rCEvt.GetCommand() == CommandEventId::ContextMenu )
+ {
+ tools::Rectangle aRect(rCEvt.GetMousePosPixel(), Size(1, 1));
+ weld::Window* pPopupParent = weld::GetPopupParent(GetStatusBar(), aRect);
+ std::unique_ptr<weld::Builder> xBuilder(Application::CreateBuilder(pPopupParent, "svx/ui/xmlsecstatmenu.ui"));
+ std::unique_ptr<weld::Menu> xPopMenu(xBuilder->weld_menu("menu"));
+ if (!xPopMenu->popup_at_rect(pPopupParent, aRect).isEmpty())
+ {
+ css::uno::Any a;
+ SfxUInt16Item aState( GetSlotId(), 0 );
+ aState.QueryValue( a );
+ INetURLObject aObj( m_aCommandURL );
+
+ css::uno::Sequence< css::beans::PropertyValue > aArgs{ comphelper::makePropertyValue(
+ aObj.GetURLPath(), a) };
+ execute( aArgs );
+ }
+ }
+ else
+ SfxStatusBarControl::Command( rCEvt );
+}
+
+void XmlSecStatusBarControl::Paint( const UserDrawEvent& rUsrEvt )
+{
+ vcl::RenderContext* pDev = rUsrEvt.GetRenderContext();
+
+ tools::Rectangle aRect = rUsrEvt.GetRect();
+ Color aOldLineColor = pDev->GetLineColor();
+ Color aOldFillColor = pDev->GetFillColor();
+
+ pDev->SetLineColor();
+ pDev->SetFillColor( pDev->GetBackground().GetColor() );
+
+ tools::Long yOffset = (aRect.GetHeight() - mpImpl->maImage.GetSizePixel().Height()) / 2;
+
+ if( mpImpl->mnState == SignatureState::OK )
+ {
+ aRect.AdjustTop(yOffset );
+ pDev->DrawImage( aRect.TopLeft(), mpImpl->maImage );
+ }
+ else if( mpImpl->mnState == SignatureState::BROKEN )
+ {
+ aRect.AdjustTop(yOffset );
+ pDev->DrawImage( aRect.TopLeft(), mpImpl->maImageBroken );
+ }
+ else if( mpImpl->mnState == SignatureState::NOTVALIDATED
+ || mpImpl->mnState == SignatureState::PARTIAL_OK)
+ {
+ aRect.AdjustTop(yOffset );
+ pDev->DrawImage( aRect.TopLeft(), mpImpl->maImageNotValidated );
+ }
+ else
+ pDev->DrawRect( aRect );
+
+ pDev->SetLineColor( aOldLineColor );
+ pDev->SetFillColor( aOldFillColor );
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/stbctrls/zoomctrl.cxx b/svx/source/stbctrls/zoomctrl.cxx
new file mode 100644
index 000000000..57cf99bbd
--- /dev/null
+++ b/svx/source/stbctrls/zoomctrl.cxx
@@ -0,0 +1,229 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <sal/config.h>
+
+#include <comphelper/propertyvalue.hxx>
+#include <i18nutil/unicode.hxx>
+#include <vcl/commandevent.hxx>
+#include <vcl/event.hxx>
+#include <vcl/svapp.hxx>
+#include <vcl/status.hxx>
+#include <vcl/weldutils.hxx>
+#include <vcl/settings.hxx>
+#include <tools/urlobj.hxx>
+#include <sal/log.hxx>
+
+#include <svx/strings.hrc>
+
+#include <svx/zoomctrl.hxx>
+#include <sfx2/zoomitem.hxx>
+#include <svx/dialmgr.hxx>
+#include "modctrl_internal.hxx"
+#include <bitmaps.hlst>
+
+#include <com/sun/star/beans/PropertyValue.hpp>
+
+SFX_IMPL_STATUSBAR_CONTROL(SvxZoomStatusBarControl,SvxZoomItem);
+
+namespace {
+
+class ZoomPopup_Impl
+{
+public:
+ ZoomPopup_Impl(weld::Window* pPopupParent, sal_uInt16 nZ, SvxZoomEnableFlags nValueSet);
+
+ sal_uInt16 GetZoom(std::string_view ident) const;
+
+ OString popup_at_rect(const tools::Rectangle& rRect)
+ {
+ return m_xMenu->popup_at_rect(m_pPopupParent, rRect);
+ }
+
+private:
+ weld::Window* m_pPopupParent;
+ std::unique_ptr<weld::Builder> m_xBuilder;
+ std::unique_ptr<weld::Menu> m_xMenu;
+ sal_uInt16 nZoom;
+};
+
+}
+
+ZoomPopup_Impl::ZoomPopup_Impl(weld::Window* pPopupParent, sal_uInt16 nZ, SvxZoomEnableFlags nValueSet)
+ : m_pPopupParent(pPopupParent)
+ , m_xBuilder(Application::CreateBuilder(m_pPopupParent, "svx/ui/zoommenu.ui"))
+ , m_xMenu(m_xBuilder->weld_menu("menu"))
+ , nZoom(nZ)
+{
+ if ( !(SvxZoomEnableFlags::N50 & nValueSet) )
+ m_xMenu->set_sensitive("50", false);
+ if ( !(SvxZoomEnableFlags::N100 & nValueSet) )
+ m_xMenu->set_sensitive("100", false);
+ if ( !(SvxZoomEnableFlags::N150 & nValueSet) )
+ m_xMenu->set_sensitive("150", false);
+ if ( !(SvxZoomEnableFlags::N200 & nValueSet) )
+ m_xMenu->set_sensitive("200", false);
+ if ( !(SvxZoomEnableFlags::OPTIMAL & nValueSet) )
+ m_xMenu->set_sensitive("optimal", false);
+ if ( !(SvxZoomEnableFlags::WHOLEPAGE & nValueSet) )
+ m_xMenu->set_sensitive("page", false);
+ if ( !(SvxZoomEnableFlags::PAGEWIDTH & nValueSet) )
+ m_xMenu->set_sensitive("width", false);
+}
+
+sal_uInt16 ZoomPopup_Impl::GetZoom(std::string_view ident) const
+{
+ sal_uInt16 nRet = nZoom;
+
+ if (ident == "200")
+ nRet = 200;
+ else if (ident == "150")
+ nRet = 150;
+ else if (ident == "100")
+ nRet = 100;
+ else if (ident == "75")
+ nRet = 75;
+ else if (ident == "50")
+ nRet = 50;
+ else if (ident == "optimal" || ident == "width" || ident == "page")
+ nRet = 0;
+
+ return nRet;
+}
+
+SvxZoomStatusBarControl::SvxZoomStatusBarControl( sal_uInt16 _nSlotId,
+ sal_uInt16 _nId,
+ StatusBar& rStb ) :
+
+ SfxStatusBarControl( _nSlotId, _nId, rStb ),
+ nZoom( 100 ),
+ nValueSet( SvxZoomEnableFlags::ALL )
+{
+ GetStatusBar().SetQuickHelpText(GetId(), SvxResId(RID_SVXSTR_ZOOMTOOL_HINT));
+ ImplUpdateItemText();
+}
+
+void SvxZoomStatusBarControl::StateChangedAtStatusBarControl( sal_uInt16, SfxItemState eState,
+ const SfxPoolItem* pState )
+{
+ if( SfxItemState::DEFAULT != eState )
+ {
+ GetStatusBar().SetItemText( GetId(), "" );
+ nValueSet = SvxZoomEnableFlags::NONE;
+ }
+ else if ( auto pItem = dynamic_cast< const SfxUInt16Item* >(pState) )
+ {
+ nZoom = pItem->GetValue();
+ ImplUpdateItemText();
+
+ if ( auto pZoomItem = dynamic_cast<const SvxZoomItem*>(pState) )
+ {
+ nValueSet = pZoomItem->GetValueSet();
+ }
+ else
+ {
+ SAL_INFO( "svx", "use SfxZoomItem for SID_ATTR_ZOOM" );
+ nValueSet = SvxZoomEnableFlags::ALL;
+ }
+ }
+}
+
+void SvxZoomStatusBarControl::ImplUpdateItemText()
+{
+ // workaround - don't bother updating when we don't have a real zoom value
+ if (nZoom)
+ {
+ OUString aStr(unicode::formatPercent(nZoom, Application::GetSettings().GetUILanguageTag()));
+ GetStatusBar().SetItemText( GetId(), aStr );
+ }
+}
+
+void SvxZoomStatusBarControl::Paint( const UserDrawEvent& )
+{
+}
+
+void SvxZoomStatusBarControl::Command( const CommandEvent& rCEvt )
+{
+ if ( CommandEventId::ContextMenu == rCEvt.GetCommand() && bool(nValueSet) )
+ {
+ ::tools::Rectangle aRect(rCEvt.GetMousePosPixel(), Size(1, 1));
+ weld::Window* pPopupParent = weld::GetPopupParent(GetStatusBar(), aRect);
+ ZoomPopup_Impl aPop(pPopupParent, nZoom, nValueSet);
+
+ OString sIdent = aPop.popup_at_rect(aRect);
+ if (!sIdent.isEmpty() && (nZoom != aPop.GetZoom(sIdent) || !nZoom))
+ {
+ nZoom = aPop.GetZoom(sIdent);
+ ImplUpdateItemText();
+ SvxZoomItem aZoom(SvxZoomType::PERCENT, nZoom, GetId());
+
+ if (sIdent == "optimal")
+ aZoom.SetType(SvxZoomType::OPTIMAL);
+ else if (sIdent == "width")
+ aZoom.SetType(SvxZoomType::PAGEWIDTH);
+ else if (sIdent == "page")
+ aZoom.SetType(SvxZoomType::WHOLEPAGE);
+
+ css::uno::Any a;
+ aZoom.QueryValue( a );
+ INetURLObject aObj( m_aCommandURL );
+
+ css::uno::Sequence< css::beans::PropertyValue > aArgs{ comphelper::makePropertyValue(
+ aObj.GetURLPath(), a) };
+ execute( aArgs );
+ }
+ }
+ else
+ SfxStatusBarControl::Command( rCEvt );
+}
+
+SFX_IMPL_STATUSBAR_CONTROL(SvxZoomPageStatusBarControl,SfxVoidItem);
+
+SvxZoomPageStatusBarControl::SvxZoomPageStatusBarControl(sal_uInt16 _nSlotId,
+ sal_uInt16 _nId, StatusBar& rStb)
+ : SfxStatusBarControl(_nSlotId, _nId, rStb)
+ , maImage(StockImage::Yes, RID_SVXBMP_ZOOM_PAGE)
+{
+ GetStatusBar().SetQuickHelpText(GetId(), SvxResId(RID_SVXSTR_FIT_SLIDE));
+}
+
+void SvxZoomPageStatusBarControl::Paint(const UserDrawEvent& rUsrEvt)
+{
+ vcl::RenderContext* pDev = rUsrEvt.GetRenderContext();
+ tools::Rectangle aRect = rUsrEvt.GetRect();
+ Point aPt = centerImage(aRect, maImage);
+ pDev->DrawImage(aPt, maImage);
+}
+
+bool SvxZoomPageStatusBarControl::MouseButtonDown(const MouseEvent&)
+{
+ SvxZoomItem aZoom( SvxZoomType::WHOLEPAGE, 0, GetId() );
+
+ css::uno::Any a;
+ aZoom.QueryValue( a );
+ INetURLObject aObj( m_aCommandURL );
+
+ css::uno::Sequence< css::beans::PropertyValue > aArgs{ comphelper::makePropertyValue(
+ aObj.GetURLPath(), a) };
+ execute( aArgs );
+
+ return true;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/stbctrls/zoomsliderctrl.cxx b/svx/source/stbctrls/zoomsliderctrl.cxx
new file mode 100644
index 000000000..c0790b074
--- /dev/null
+++ b/svx/source/stbctrls/zoomsliderctrl.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 <svx/zoomsliderctrl.hxx>
+
+#include <comphelper/propertyvalue.hxx>
+#include <vcl/status.hxx>
+#include <vcl/image.hxx>
+#include <vcl/svapp.hxx>
+#include <vcl/settings.hxx>
+#include <vcl/event.hxx>
+#include <svx/zoomslideritem.hxx>
+#include <svx/dialmgr.hxx>
+#include <svx/strings.hrc>
+#include <basegfx/utils/zoomtools.hxx>
+#include <bitmaps.hlst>
+#include <com/sun/star/beans/PropertyValue.hpp>
+
+#include <set>
+
+SFX_IMPL_STATUSBAR_CONTROL( SvxZoomSliderControl, SvxZoomSliderItem );
+
+struct SvxZoomSliderControl::SvxZoomSliderControl_Impl
+{
+ sal_uInt16 mnCurrentZoom;
+ sal_uInt16 mnMinZoom;
+ sal_uInt16 mnMaxZoom;
+ sal_uInt16 mnSliderCenter;
+ std::vector< tools::Long > maSnappingPointOffsets;
+ std::vector< sal_uInt16 > maSnappingPointZooms;
+ Image maSliderButton;
+ Image maIncreaseButton;
+ Image maDecreaseButton;
+ bool mbValuesSet;
+ bool mbDraggingStarted;
+
+ SvxZoomSliderControl_Impl() :
+ mnCurrentZoom( 0 ),
+ mnMinZoom( 0 ),
+ mnMaxZoom( 0 ),
+ mnSliderCenter( 0 ),
+ mbValuesSet( false ),
+ mbDraggingStarted( false ) {}
+};
+
+const tools::Long nSliderXOffset = 20;
+const tools::Long nSnappingEpsilon = 5; // snapping epsilon in pixels
+const tools::Long nSnappingPointsMinDist = nSnappingEpsilon; // minimum distance of two adjacent snapping points
+
+// nOffset refers to the origin of the control:
+// + ----------- -
+sal_uInt16 SvxZoomSliderControl::Offset2Zoom( tools::Long nOffset ) const
+{
+ const tools::Long nControlWidth = getControlRect().GetWidth();
+ sal_uInt16 nRet = 0;
+
+ if ( nOffset < nSliderXOffset )
+ return mxImpl->mnMinZoom;
+
+ if ( nOffset > nControlWidth - nSliderXOffset )
+ return mxImpl->mnMaxZoom;
+
+ // check for snapping points:
+ sal_uInt16 nCount = 0;
+ for ( const tools::Long nCurrent : mxImpl->maSnappingPointOffsets )
+ {
+ if ( std::abs(nCurrent - nOffset) < nSnappingEpsilon )
+ {
+ nOffset = nCurrent;
+ nRet = mxImpl->maSnappingPointZooms[ nCount ];
+ break;
+ }
+ ++nCount;
+ }
+
+ if ( 0 == nRet )
+ {
+ if ( nOffset < nControlWidth / 2 )
+ {
+ // first half of slider
+ const tools::Long nFirstHalfRange = mxImpl->mnSliderCenter - mxImpl->mnMinZoom;
+ const tools::Long nHalfSliderWidth = nControlWidth/2 - nSliderXOffset;
+ const tools::Long nZoomPerSliderPixel = (1000 * nFirstHalfRange) / nHalfSliderWidth;
+ const tools::Long nOffsetToSliderLeft = nOffset - nSliderXOffset;
+ nRet = mxImpl->mnMinZoom + sal_uInt16( nOffsetToSliderLeft * nZoomPerSliderPixel / 1000 );
+ }
+ else
+ {
+ // second half of slider
+ const tools::Long nSecondHalfRange = mxImpl->mnMaxZoom - mxImpl->mnSliderCenter;
+ const tools::Long nHalfSliderWidth = nControlWidth/2 - nSliderXOffset;
+ const tools::Long nZoomPerSliderPixel = 1000 * nSecondHalfRange / nHalfSliderWidth;
+ const tools::Long nOffsetToSliderCenter = nOffset - nControlWidth/2;
+ nRet = mxImpl->mnSliderCenter + sal_uInt16( nOffsetToSliderCenter * nZoomPerSliderPixel / 1000 );
+ }
+ }
+
+ if ( nRet < mxImpl->mnMinZoom )
+ nRet = mxImpl->mnMinZoom;
+ else if ( nRet > mxImpl->mnMaxZoom )
+ nRet = mxImpl->mnMaxZoom;
+
+ return nRet;
+}
+
+// returns the offset to the left control border
+tools::Long SvxZoomSliderControl::Zoom2Offset( sal_uInt16 nCurrentZoom ) const
+{
+ const tools::Long nControlWidth = getControlRect().GetWidth();
+ tools::Long nRet = nSliderXOffset;
+
+ const tools::Long nHalfSliderWidth = nControlWidth/2 - nSliderXOffset;
+
+ if ( nCurrentZoom <= mxImpl->mnSliderCenter )
+ {
+ nCurrentZoom = nCurrentZoom - mxImpl->mnMinZoom;
+ const tools::Long nFirstHalfRange = mxImpl->mnSliderCenter - mxImpl->mnMinZoom;
+ const tools::Long nSliderPixelPerZoomPercent = 1000 * nHalfSliderWidth / nFirstHalfRange;
+ const tools::Long nOffset = (nSliderPixelPerZoomPercent * nCurrentZoom) / 1000;
+ nRet += nOffset;
+ }
+ else
+ {
+ nCurrentZoom = nCurrentZoom - mxImpl->mnSliderCenter;
+ const tools::Long nSecondHalfRange = mxImpl->mnMaxZoom - mxImpl->mnSliderCenter;
+ const tools::Long nSliderPixelPerZoomPercent = 1000 * nHalfSliderWidth / nSecondHalfRange;
+ const tools::Long nOffset = (nSliderPixelPerZoomPercent * nCurrentZoom) / 1000;
+ nRet += nHalfSliderWidth + nOffset;
+ }
+
+ return nRet;
+}
+
+SvxZoomSliderControl::SvxZoomSliderControl( sal_uInt16 _nSlotId, sal_uInt16 _nId, StatusBar& rStatusBar ) :
+ SfxStatusBarControl( _nSlotId, _nId, rStatusBar ),
+ mxImpl( new SvxZoomSliderControl_Impl )
+{
+ mxImpl->maSliderButton = Image(StockImage::Yes, RID_SVXBMP_SLIDERBUTTON);
+ mxImpl->maIncreaseButton = Image(StockImage::Yes, RID_SVXBMP_SLIDERINCREASE);
+ mxImpl->maDecreaseButton = Image(StockImage::Yes, RID_SVXBMP_SLIDERDECREASE);
+}
+
+SvxZoomSliderControl::~SvxZoomSliderControl()
+{
+}
+
+void SvxZoomSliderControl::StateChangedAtStatusBarControl( sal_uInt16 /*nSID*/, SfxItemState eState, const SfxPoolItem* pState )
+{
+ if ( (SfxItemState::DEFAULT != eState) || pState->IsVoidItem() )
+ {
+ GetStatusBar().SetItemText( GetId(), "" );
+ mxImpl->mbValuesSet = false;
+ }
+ else
+ {
+ assert( dynamic_cast<const SvxZoomSliderItem*>( pState) && "invalid item type: should be a SvxZoomSliderItem" );
+ mxImpl->mnCurrentZoom = static_cast<const SvxZoomSliderItem*>( pState )->GetValue();
+ mxImpl->mnMinZoom = static_cast<const SvxZoomSliderItem*>( pState )->GetMinZoom();
+ mxImpl->mnMaxZoom = static_cast<const SvxZoomSliderItem*>( pState )->GetMaxZoom();
+ mxImpl->mnSliderCenter= 100;
+ mxImpl->mbValuesSet = true;
+
+ if ( mxImpl->mnSliderCenter == mxImpl->mnMaxZoom )
+ mxImpl->mnSliderCenter = mxImpl->mnMinZoom + static_cast<sal_uInt16>((mxImpl->mnMaxZoom - mxImpl->mnMinZoom) * 0.5);
+
+
+ DBG_ASSERT( mxImpl->mnMinZoom <= mxImpl->mnCurrentZoom &&
+ mxImpl->mnMinZoom < mxImpl->mnSliderCenter &&
+ mxImpl->mnMaxZoom >= mxImpl->mnCurrentZoom &&
+ mxImpl->mnMaxZoom > mxImpl->mnSliderCenter,
+ "Looks like the zoom slider item is corrupted" );
+
+ const css::uno::Sequence < sal_Int32 > rSnappingPoints = static_cast<const SvxZoomSliderItem*>( pState )->GetSnappingPoints();
+ mxImpl->maSnappingPointOffsets.clear();
+ mxImpl->maSnappingPointZooms.clear();
+
+ // get all snapping points:
+ std::set< sal_uInt16 > aTmpSnappingPoints;
+ for ( const sal_Int32 nSnappingPoint : rSnappingPoints )
+ {
+ aTmpSnappingPoints.insert( static_cast<sal_uInt16>(nSnappingPoint) );
+ }
+
+ // remove snapping points that are too close to each other:
+ tools::Long nLastOffset = 0;
+
+ for ( const sal_uInt16 nCurrent : aTmpSnappingPoints )
+ {
+ const tools::Long nCurrentOffset = Zoom2Offset( nCurrent );
+
+ if ( nCurrentOffset - nLastOffset >= nSnappingPointsMinDist )
+ {
+ mxImpl->maSnappingPointOffsets.push_back( nCurrentOffset );
+ mxImpl->maSnappingPointZooms.push_back( nCurrent );
+ nLastOffset = nCurrentOffset;
+ }
+ }
+ }
+
+ forceRepaint();
+}
+
+void SvxZoomSliderControl::Paint( const UserDrawEvent& rUsrEvt )
+{
+ if ( !mxImpl->mbValuesSet )
+ return;
+
+ const tools::Rectangle aControlRect = getControlRect();
+ vcl::RenderContext* pDev = rUsrEvt.GetRenderContext();
+ tools::Rectangle aRect = rUsrEvt.GetRect();
+ tools::Rectangle aSlider = aRect;
+
+ tools::Long nSliderHeight = 1 * pDev->GetDPIScaleFactor();
+ tools::Long nSnappingHeight = 2 * pDev->GetDPIScaleFactor();
+
+ aSlider.AdjustTop((aControlRect.GetHeight() - nSliderHeight)/2 );
+ aSlider.SetBottom( aSlider.Top() + nSliderHeight - 1 );
+ aSlider.AdjustLeft(nSliderXOffset );
+ aSlider.AdjustRight( -nSliderXOffset );
+
+ Color aOldLineColor = pDev->GetLineColor();
+ Color aOldFillColor = pDev->GetFillColor();
+
+ const StyleSettings& rStyleSettings = Application::GetSettings().GetStyleSettings();
+ pDev->SetLineColor( rStyleSettings.GetDarkShadowColor() );
+ pDev->SetFillColor( rStyleSettings.GetDarkShadowColor() );
+
+ // draw slider
+ pDev->DrawRect( aSlider );
+ // shadow
+ pDev->SetLineColor( rStyleSettings.GetShadowColor() );
+ pDev->DrawLine(Point(aSlider.Left()+1,aSlider.Bottom()+1), Point(aSlider.Right()+1,aSlider.Bottom()+1));
+ pDev->SetLineColor( rStyleSettings.GetDarkShadowColor() );
+
+ // draw snapping points:
+ for ( const auto& rSnappingPoint : mxImpl->maSnappingPointOffsets )
+ {
+ tools::Long nSnapPosX = aRect.Left() + rSnappingPoint;
+
+ pDev->DrawRect( tools::Rectangle( nSnapPosX - 1, aSlider.Top() - nSnappingHeight,
+ nSnapPosX, aSlider.Bottom() + nSnappingHeight ) );
+ }
+
+ // draw slider button
+ Point aImagePoint = aRect.TopLeft();
+ aImagePoint.AdjustX(Zoom2Offset( mxImpl->mnCurrentZoom ) );
+ aImagePoint.AdjustX( -(mxImpl->maSliderButton.GetSizePixel().Width()/2) );
+ aImagePoint.AdjustY((aControlRect.GetHeight() - mxImpl->maSliderButton.GetSizePixel().Height())/2 );
+ pDev->DrawImage( aImagePoint, mxImpl->maSliderButton );
+
+ // draw decrease button
+ aImagePoint = aRect.TopLeft();
+ aImagePoint.AdjustX((nSliderXOffset - mxImpl->maDecreaseButton.GetSizePixel().Width())/2 );
+ aImagePoint.AdjustY((aControlRect.GetHeight() - mxImpl->maDecreaseButton.GetSizePixel().Height())/2 );
+ pDev->DrawImage( aImagePoint, mxImpl->maDecreaseButton );
+
+ // draw increase button
+ aImagePoint.setX( aRect.Left() + aControlRect.GetWidth() - mxImpl->maIncreaseButton.GetSizePixel().Width() - (nSliderXOffset - mxImpl->maIncreaseButton.GetSizePixel().Height())/2 );
+ pDev->DrawImage( aImagePoint, mxImpl->maIncreaseButton );
+
+ pDev->SetLineColor( aOldLineColor );
+ pDev->SetFillColor( aOldFillColor );
+}
+
+bool SvxZoomSliderControl::MouseButtonDown( const MouseEvent & rEvt )
+{
+ if ( !mxImpl->mbValuesSet )
+ return true;
+
+ const tools::Rectangle aControlRect = getControlRect();
+ const Point aPoint = rEvt.GetPosPixel();
+ const sal_Int32 nXDiff = aPoint.X() - aControlRect.Left();
+
+ tools::Long nIncDecWidth = mxImpl->maIncreaseButton.GetSizePixel().Width();
+
+ const tools::Long nButtonLeftOffset = (nSliderXOffset - nIncDecWidth)/2;
+ const tools::Long nButtonRightOffset = (nSliderXOffset + nIncDecWidth)/2;
+
+ const tools::Long nOldZoom = mxImpl->mnCurrentZoom;
+
+ // click to - button
+ if ( nXDiff >= nButtonLeftOffset && nXDiff <= nButtonRightOffset )
+ mxImpl->mnCurrentZoom = basegfx::zoomtools::zoomOut( static_cast<int>(mxImpl->mnCurrentZoom) );
+ // click to + button
+ else if ( nXDiff >= aControlRect.GetWidth() - nSliderXOffset + nButtonLeftOffset &&
+ nXDiff <= aControlRect.GetWidth() - nSliderXOffset + nButtonRightOffset )
+ mxImpl->mnCurrentZoom = basegfx::zoomtools::zoomIn( static_cast<int>(mxImpl->mnCurrentZoom) );
+ // click to slider
+ else if( nXDiff >= nSliderXOffset && nXDiff <= aControlRect.GetWidth() - nSliderXOffset )
+ {
+ mxImpl->mnCurrentZoom = Offset2Zoom( nXDiff );
+ mxImpl->mbDraggingStarted = true;
+ }
+
+ if ( mxImpl->mnCurrentZoom < mxImpl->mnMinZoom )
+ mxImpl->mnCurrentZoom = mxImpl->mnMinZoom;
+ else if ( mxImpl->mnCurrentZoom > mxImpl->mnMaxZoom )
+ mxImpl->mnCurrentZoom = mxImpl->mnMaxZoom;
+
+ if ( nOldZoom == mxImpl->mnCurrentZoom )
+ return true;
+
+ repaintAndExecute();
+
+ return true;
+}
+
+bool SvxZoomSliderControl::MouseButtonUp( const MouseEvent & )
+{
+ mxImpl->mbDraggingStarted = false;
+ return true;
+}
+
+bool SvxZoomSliderControl::MouseMove( const MouseEvent & rEvt )
+{
+ if ( !mxImpl->mbValuesSet )
+ return true;
+
+ const short nButtons = rEvt.GetButtons();
+ const tools::Rectangle aControlRect = getControlRect();
+ const Point aPoint = rEvt.GetPosPixel();
+ const sal_Int32 nXDiff = aPoint.X() - aControlRect.Left();
+
+ // check mouse move with button pressed
+ if ( 1 == nButtons && mxImpl->mbDraggingStarted )
+ {
+ if ( nXDiff >= nSliderXOffset && nXDiff <= aControlRect.GetWidth() - nSliderXOffset )
+ {
+ mxImpl->mnCurrentZoom = Offset2Zoom( nXDiff );
+
+ repaintAndExecute();
+ }
+ }
+
+ // Tooltips
+
+ tools::Long nIncDecWidth = mxImpl->maIncreaseButton.GetSizePixel().Width();
+
+ const tools::Long nButtonLeftOffset = (nSliderXOffset - nIncDecWidth)/2;
+ const tools::Long nButtonRightOffset = (nSliderXOffset + nIncDecWidth)/2;
+
+ // click to - button
+ if ( nXDiff >= nButtonLeftOffset && nXDiff <= nButtonRightOffset )
+ GetStatusBar().SetQuickHelpText(GetId(), SvxResId(RID_SVXSTR_ZOOM_OUT));
+ // click to + button
+ else if ( nXDiff >= aControlRect.GetWidth() - nSliderXOffset + nButtonLeftOffset &&
+ nXDiff <= aControlRect.GetWidth() - nSliderXOffset + nButtonRightOffset )
+ GetStatusBar().SetQuickHelpText(GetId(), SvxResId(RID_SVXSTR_ZOOM_IN));
+ else
+ // don't hide the slider and its handle with a tooltip during zooming
+ GetStatusBar().SetQuickHelpText(GetId(), "");
+
+ return true;
+}
+
+void SvxZoomSliderControl::forceRepaint() const
+{
+ GetStatusBar().SetItemData(GetId(), nullptr);
+}
+
+void SvxZoomSliderControl::repaintAndExecute()
+{
+ forceRepaint();
+
+ // commit state change
+ SvxZoomSliderItem aZoomSliderItem(mxImpl->mnCurrentZoom);
+
+ css::uno::Any any;
+ aZoomSliderItem.QueryValue(any);
+
+ css::uno::Sequence<css::beans::PropertyValue> aArgs{ comphelper::makePropertyValue("ZoomSlider",
+ any) };
+ execute(aArgs);
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/styles/ColorSets.cxx b/svx/source/styles/ColorSets.cxx
new file mode 100644
index 000000000..240b65e2b
--- /dev/null
+++ b/svx/source/styles/ColorSets.cxx
@@ -0,0 +1,375 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.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 <svx/ColorSets.hxx>
+
+#include <sstream>
+
+#include <libxml/xmlwriter.h>
+
+#include <com/sun/star/util/Color.hpp>
+#include <com/sun/star/text/XTextRange.hpp>
+#include <com/sun/star/container/XEnumerationAccess.hpp>
+#include <com/sun/star/container/XEnumeration.hpp>
+#include <com/sun/star/beans/XPropertySet.hpp>
+
+#include <comphelper/sequenceashashmap.hxx>
+#include <comphelper/sequence.hxx>
+#include <sal/log.hxx>
+#include <svx/svdpage.hxx>
+#include <svx/svditer.hxx>
+#include <editeng/unoprnms.hxx>
+
+using namespace com::sun::star;
+
+namespace
+{
+/// Updates a text portion to match a new color set, in case it already uses theme colors.
+void UpdateTextPortionColorSet(const uno::Reference<beans::XPropertySet>& xPortion,
+ const svx::ColorSet& rColorSet)
+{
+ sal_Int16 nCharColorTheme = -1;
+ xPortion->getPropertyValue(UNO_NAME_EDIT_CHAR_COLOR_THEME) >>= nCharColorTheme;
+ if (nCharColorTheme < 0 || nCharColorTheme > 11)
+ {
+ return;
+ }
+
+ Color aColor = rColorSet.getColor(nCharColorTheme);
+ sal_Int32 nCharColorLumMod{};
+ xPortion->getPropertyValue(UNO_NAME_EDIT_CHAR_COLOR_LUM_MOD) >>= nCharColorLumMod;
+ sal_Int32 nCharColorLumOff{};
+ xPortion->getPropertyValue(UNO_NAME_EDIT_CHAR_COLOR_LUM_OFF) >>= nCharColorLumOff;
+ if (nCharColorLumMod != 10000 || nCharColorLumOff != 0)
+ {
+ aColor.ApplyLumModOff(nCharColorLumMod, nCharColorLumOff);
+ }
+
+ sal_Int32 nCharColorTintOrShade{};
+ xPortion->getPropertyValue(UNO_NAME_EDIT_CHAR_COLOR_TINT_OR_SHADE) >>= nCharColorTintOrShade;
+ if (nCharColorTintOrShade != 0)
+ {
+ aColor.ApplyTintOrShade(nCharColorTintOrShade);
+ }
+
+ xPortion->setPropertyValue(UNO_NAME_EDIT_CHAR_COLOR,
+ uno::Any(static_cast<sal_Int32>(aColor)));
+}
+
+void UpdateFillColorSet(const uno::Reference<beans::XPropertySet>& xShape, const svx::ColorSet& rColorSet)
+{
+ if (!xShape->getPropertySetInfo()->hasPropertyByName(UNO_NAME_FILLCOLOR_THEME))
+ {
+ return;
+ }
+
+ sal_Int16 nFillColorTheme = -1;
+ xShape->getPropertyValue(UNO_NAME_FILLCOLOR_THEME) >>= nFillColorTheme;
+ if (nFillColorTheme < 0 || nFillColorTheme > 11)
+ {
+ return;
+ }
+
+ Color aColor = rColorSet.getColor(nFillColorTheme);
+ sal_Int32 nFillColorLumMod{};
+ xShape->getPropertyValue(UNO_NAME_FILLCOLOR_LUM_MOD) >>= nFillColorLumMod;
+ sal_Int32 nFillColorLumOff{};
+ xShape->getPropertyValue(UNO_NAME_FILLCOLOR_LUM_OFF) >>= nFillColorLumOff;
+ if (nFillColorLumMod != 10000 || nFillColorLumOff != 0)
+ {
+ aColor.ApplyLumModOff(nFillColorLumMod, nFillColorLumOff);
+ }
+
+ xShape->setPropertyValue(UNO_NAME_FILLCOLOR, uno::Any(static_cast<sal_Int32>(aColor)));
+}
+
+void UpdateSdrObject(svx::Theme* pTheme, SdrObject* pObject)
+{
+ svx::ColorSet* pColorSet = pTheme->GetColorSet();
+ if (!pColorSet)
+ {
+ return;
+ }
+
+ uno::Reference<drawing::XShape> xShape = pObject->getUnoShape();
+ uno::Reference<text::XTextRange> xShapeText(xShape, uno::UNO_QUERY);
+ if (xShapeText.is())
+ {
+ // E.g. group shapes have no text.
+ uno::Reference<container::XEnumerationAccess> xText(xShapeText->getText(), uno::UNO_QUERY);
+ uno::Reference<container::XEnumeration> xParagraphs = xText->createEnumeration();
+ while (xParagraphs->hasMoreElements())
+ {
+ uno::Reference<container::XEnumerationAccess> xParagraph(xParagraphs->nextElement(), uno::UNO_QUERY);
+ uno::Reference<container::XEnumeration> xPortions = xParagraph->createEnumeration();
+ while (xPortions->hasMoreElements())
+ {
+ uno::Reference<beans::XPropertySet> xPortion(xPortions->nextElement(), uno::UNO_QUERY);
+ UpdateTextPortionColorSet(xPortion, *pColorSet);
+ }
+ }
+ }
+
+ uno::Reference<beans::XPropertySet> xShapeProps(xShape, uno::UNO_QUERY);
+ UpdateFillColorSet(xShapeProps, *pColorSet);
+}
+}
+
+namespace svx
+{
+
+ColorSet::ColorSet(OUString const & aColorSetName)
+ : maColorSetName(aColorSetName)
+ , maColors(12)
+{}
+
+ColorSets::ColorSets()
+{}
+
+void ColorSet::dumpAsXml(xmlTextWriterPtr pWriter) const
+{
+ (void)xmlTextWriterStartElement(pWriter, BAD_CAST("ColorSet"));
+ (void)xmlTextWriterWriteFormatAttribute(pWriter, BAD_CAST("ptr"), "%p", this);
+ (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("maColorSetName"),
+ BAD_CAST(maColorSetName.toUtf8().getStr()));
+
+ for (const auto& rColor : maColors)
+ {
+ (void)xmlTextWriterStartElement(pWriter, BAD_CAST("Color"));
+ std::stringstream ss;
+ ss << rColor;
+ (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("value"), BAD_CAST(ss.str().c_str()));
+ (void)xmlTextWriterEndElement(pWriter);
+ }
+
+ (void)xmlTextWriterEndElement(pWriter);
+}
+
+ColorSets::~ColorSets()
+{}
+
+void ColorSets::init()
+{
+ {
+ ColorSet aColorSet("Breeze");
+ aColorSet.add(0, 0xFCFCFC);
+ aColorSet.add(1, 0x232629);
+ aColorSet.add(2, 0xEFF0F1);
+ aColorSet.add(3, 0x31363B);
+ aColorSet.add(4, 0xDA4453);
+ aColorSet.add(5, 0xF47750);
+ aColorSet.add(6, 0xFDBC4B);
+ aColorSet.add(7, 0xC9CE3B);
+ aColorSet.add(8, 0x1CDC9A);
+ aColorSet.add(9, 0x2ECC71);
+ aColorSet.add(10, 0x1D99F3);
+ aColorSet.add(11, 0x3DAEE9);
+ maColorSets.push_back(aColorSet);
+ }
+ {
+ ColorSet aColorSet("Material Blue");
+ aColorSet.add(0, 0xFFFFFF);
+ aColorSet.add(1, 0x212121);
+ aColorSet.add(2, 0xECEFF1);
+ aColorSet.add(3, 0x37474F);
+ aColorSet.add(4, 0x7986CB);
+ aColorSet.add(5, 0x303F9F);
+ aColorSet.add(6, 0x64B5F6);
+ aColorSet.add(7, 0x1976D2);
+ aColorSet.add(8, 0x4FC3F7);
+ aColorSet.add(9, 0x0277BD);
+ aColorSet.add(10, 0x4DD0E1);
+ aColorSet.add(11, 0x0097A7);
+ maColorSets.push_back(aColorSet);
+ }
+ {
+ ColorSet aColorSet("Material Red");
+ aColorSet.add(0, 0xFFFFFF);
+ aColorSet.add(1, 0x212121);
+ aColorSet.add(2, 0xF5F5F5);
+ aColorSet.add(3, 0x424242);
+ aColorSet.add(4, 0xFF9800);
+ aColorSet.add(5, 0xFF6D00);
+ aColorSet.add(6, 0xFF5722);
+ aColorSet.add(7, 0xDD2C00);
+ aColorSet.add(8, 0xF44336);
+ aColorSet.add(9, 0xD50000);
+ aColorSet.add(10, 0xE91E63);
+ aColorSet.add(11, 0xC51162);
+ maColorSets.push_back(aColorSet);
+ }
+ {
+ ColorSet aColorSet("Material Green");
+ aColorSet.add(0, 0xFFFFFF);
+ aColorSet.add(1, 0x212121);
+ aColorSet.add(2, 0xF5F5F5);
+ aColorSet.add(3, 0x424242);
+ aColorSet.add(4, 0x009688);
+ aColorSet.add(5, 0x00bfa5);
+ aColorSet.add(6, 0x4caf50);
+ aColorSet.add(7, 0x00c853);
+ aColorSet.add(8, 0x8bc34a);
+ aColorSet.add(9, 0x64dd17);
+ aColorSet.add(10, 0xcddc39);
+ aColorSet.add(11, 0xaeea00);
+ maColorSets.push_back(aColorSet);
+ }
+}
+
+const ColorSet& ColorSets::getColorSet(std::u16string_view rName)
+{
+ for (const ColorSet & rColorSet : maColorSets)
+ {
+ if (rColorSet.getName() == rName)
+ return rColorSet;
+ }
+ return maColorSets[0];
+}
+
+Theme::Theme(const OUString& rName)
+ : maName(rName)
+{
+}
+
+Theme::~Theme() {}
+
+void Theme::SetColorSet(std::unique_ptr<ColorSet> pColorSet) { mpColorSet = std::move(pColorSet); }
+
+ColorSet* Theme::GetColorSet() { return mpColorSet.get(); }
+
+void Theme::SetName(const OUString& rName) { maName = rName; }
+
+const OUString& Theme::GetName() const { return maName; }
+
+void Theme::dumpAsXml(xmlTextWriterPtr pWriter) const
+{
+ (void)xmlTextWriterStartElement(pWriter, BAD_CAST("Theme"));
+ (void)xmlTextWriterWriteFormatAttribute(pWriter, BAD_CAST("ptr"), "%p", this);
+ (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("maName"),
+ BAD_CAST(maName.toUtf8().getStr()));
+
+ if (mpColorSet)
+ {
+ mpColorSet->dumpAsXml(pWriter);
+ }
+
+ (void)xmlTextWriterEndElement(pWriter);
+}
+
+void Theme::ToAny(css::uno::Any& rVal) const
+{
+ comphelper::SequenceAsHashMap aMap;
+ aMap["Name"] <<= maName;
+
+ if (mpColorSet)
+ {
+ std::vector<util::Color> aColorScheme;
+ for (size_t i = 0; i < 12; ++i)
+ {
+ aColorScheme.push_back(static_cast<sal_Int32>(mpColorSet->getColor(i)));
+ }
+
+ aMap["ColorSchemeName"] <<= mpColorSet->getName();
+ aMap["ColorScheme"] <<= comphelper::containerToSequence(aColorScheme);
+ }
+
+ rVal <<= aMap.getAsConstPropertyValueList();
+}
+
+std::unique_ptr<Theme> Theme::FromAny(const css::uno::Any& rVal)
+{
+ comphelper::SequenceAsHashMap aMap(rVal);
+ std::unique_ptr<Theme> pTheme;
+ ColorSet* pColorSet = nullptr;
+
+ auto it = aMap.find("Name");
+ if (it != aMap.end())
+ {
+ OUString aName;
+ it->second >>= aName;
+ pTheme = std::make_unique<Theme>(aName);
+ }
+
+ it = aMap.find("ColorSchemeName");
+ if (it != aMap.end() && pTheme)
+ {
+ OUString aName;
+ it->second >>= aName;
+ auto pSet = std::make_unique<ColorSet>(aName);
+ pTheme->SetColorSet(std::move(pSet));
+ pColorSet = pTheme->GetColorSet();
+ }
+
+ it = aMap.find("ColorScheme");
+ if (it != aMap.end() && pColorSet)
+ {
+ uno::Sequence<util::Color> aColors;
+ it->second >>= aColors;
+ for (size_t i = 0; i < aColors.size(); ++i)
+ {
+ if (i >= 12)
+ {
+ SAL_WARN("svx", "Theme::FromAny: too many colors in the color set");
+ break;
+ }
+
+ pColorSet->add(i, Color(ColorTransparency, aColors[i]));
+ }
+ }
+
+ return pTheme;
+}
+
+void Theme::UpdateSdrPage(const SdrPage* pPage)
+{
+ for (size_t nObject = 0; nObject < pPage->GetObjCount(); ++nObject)
+ {
+ SdrObject* pObject = pPage->GetObj(nObject);
+ UpdateSdrObject(this, pObject);
+ SdrObjList* pList = pObject->GetSubList();
+ if (pList)
+ {
+ SdrObjListIter aIter(pList, SdrIterMode::DeepWithGroups);
+ while (aIter.IsMore())
+ {
+ UpdateSdrObject(this, aIter.Next());
+ }
+ }
+ }
+}
+
+std::vector<Color> Theme::GetColors() const
+{
+ if (!mpColorSet)
+ {
+ return {};
+ }
+
+ std::vector<Color> aColors;
+ for (size_t i = 0; i < 12; ++i)
+ {
+ aColors.push_back(mpColorSet->getColor(i));
+ }
+ return aColors;
+}
+
+Color Theme::GetColor(ThemeColorType eType) const
+{
+ if (!mpColorSet)
+ {
+ return {};
+ }
+
+ return mpColorSet->getColor(static_cast<size_t>(eType));
+}
+
+} // end of namespace svx
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/styles/CommonStyleManager.cxx b/svx/source/styles/CommonStyleManager.cxx
new file mode 100644
index 000000000..7a4279716
--- /dev/null
+++ b/svx/source/styles/CommonStyleManager.cxx
@@ -0,0 +1,26 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.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 <svx/CommonStyleManager.hxx>
+#include <CommonStylePreviewRenderer.hxx>
+
+namespace svx
+{
+
+std::unique_ptr<sfx2::StylePreviewRenderer> CommonStyleManager::CreateStylePreviewRenderer(
+ OutputDevice& rOutputDev, SfxStyleSheetBase* pStyle,
+ tools::Long nMaxHeight)
+{
+ return std::unique_ptr<sfx2::StylePreviewRenderer>(new CommonStylePreviewRenderer(mrShell, rOutputDev, pStyle, nMaxHeight));
+}
+
+} // end svx namespace
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/styles/CommonStylePreviewRenderer.cxx b/svx/source/styles/CommonStylePreviewRenderer.cxx
new file mode 100644
index 000000000..df0e3539e
--- /dev/null
+++ b/svx/source/styles/CommonStylePreviewRenderer.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/.
+ *
+ */
+
+#include <memory>
+#include <CommonStylePreviewRenderer.hxx>
+
+#include <sfx2/objsh.hxx>
+#include <svl/style.hxx>
+#include <svl/itemset.hxx>
+#include <vcl/outdev.hxx>
+
+#include <com/sun/star/drawing/FillStyle.hpp>
+#include <svx/xdef.hxx>
+#include <svx/xfillit0.hxx>
+#include <svx/xflclit.hxx>
+#include <editeng/brushitem.hxx>
+#include <editeng/fontitem.hxx>
+#include <editeng/fhgtitem.hxx>
+#include <editeng/charreliefitem.hxx>
+#include <editeng/contouritem.hxx>
+#include <editeng/colritem.hxx>
+#include <editeng/crossedoutitem.hxx>
+#include <editeng/emphasismarkitem.hxx>
+#include <editeng/postitem.hxx>
+#include <editeng/shdditem.hxx>
+#include <editeng/udlnitem.hxx>
+#include <editeng/wghtitem.hxx>
+#include <editeng/svxfont.hxx>
+#include <editeng/cmapitem.hxx>
+
+#include <editeng/editids.hrc>
+
+using namespace css;
+
+namespace svx
+{
+
+CommonStylePreviewRenderer::CommonStylePreviewRenderer(
+ const SfxObjectShell& rShell, OutputDevice& rOutputDev,
+ SfxStyleSheetBase* pStyle, tools::Long nMaxHeight)
+ : StylePreviewRenderer(rShell, rOutputDev, pStyle, nMaxHeight)
+ , maFontColor(COL_AUTO)
+ , maHighlightColor(COL_AUTO)
+ , maBackgroundColor(COL_AUTO)
+ , maStyleName(mpStyle->GetName())
+{
+}
+
+CommonStylePreviewRenderer::~CommonStylePreviewRenderer()
+{}
+
+bool CommonStylePreviewRenderer::recalculate()
+{
+ m_oFont.reset();
+
+ std::optional<SfxItemSet> pItemSet(mpStyle->GetItemSetForPreview());
+
+ if (!pItemSet) return false;
+
+ SvxFont aFont;
+
+ const SfxPoolItem* pItem;
+
+ if ((pItem = pItemSet->GetItem(SID_ATTR_CHAR_WEIGHT)) != nullptr)
+ {
+ aFont.SetWeight(static_cast<const SvxWeightItem*>(pItem)->GetWeight());
+ }
+ if ((pItem = pItemSet->GetItem(SID_ATTR_CHAR_POSTURE)) != nullptr)
+ {
+ aFont.SetItalic(static_cast<const SvxPostureItem*>(pItem)->GetPosture());
+ }
+ if ((pItem = pItemSet->GetItem(SID_ATTR_CHAR_CONTOUR)) != nullptr)
+ {
+ aFont.SetOutline(static_cast< const SvxContourItem*>(pItem)->GetValue());
+ }
+ if ((pItem = pItemSet->GetItem(SID_ATTR_CHAR_SHADOWED)) != nullptr)
+ {
+ aFont.SetShadow(static_cast<const SvxShadowedItem*>(pItem)->GetValue());
+ }
+ if ((pItem = pItemSet->GetItem(SID_ATTR_CHAR_RELIEF)) != nullptr)
+ {
+ aFont.SetRelief(static_cast<const SvxCharReliefItem*>(pItem)->GetValue());
+ }
+ if ((pItem = pItemSet->GetItem(SID_ATTR_CHAR_UNDERLINE)) != nullptr)
+ {
+ aFont.SetUnderline(static_cast< const SvxUnderlineItem*>(pItem)->GetLineStyle());
+ }
+ if ((pItem = pItemSet->GetItem(SID_ATTR_CHAR_OVERLINE)) != nullptr)
+ {
+ aFont.SetOverline(static_cast<const SvxOverlineItem*>(pItem)->GetValue());
+ }
+ if ((pItem = pItemSet->GetItem(SID_ATTR_CHAR_STRIKEOUT)) != nullptr)
+ {
+ aFont.SetStrikeout(static_cast<const SvxCrossedOutItem*>(pItem)->GetStrikeout());
+ }
+ if ((pItem = pItemSet->GetItem(SID_ATTR_CHAR_CASEMAP)) != nullptr)
+ {
+ aFont.SetCaseMap(static_cast<const SvxCaseMapItem*>(pItem)->GetCaseMap());
+ }
+ if ((pItem = pItemSet->GetItem(SID_ATTR_CHAR_EMPHASISMARK)) != nullptr)
+ {
+ aFont.SetEmphasisMark(static_cast<const SvxEmphasisMarkItem*>(pItem)->GetEmphasisMark());
+ }
+ if ((pItem = pItemSet->GetItem(SID_ATTR_CHAR_COLOR)) != nullptr)
+ {
+ maFontColor = static_cast<const SvxColorItem*>(pItem)->GetValue();
+ }
+ if ((pItem = pItemSet->GetItem(SID_ATTR_BRUSH_CHAR)) != nullptr)
+ {
+ maHighlightColor = static_cast<const SvxBrushItem*>(pItem)->GetColor();
+ }
+
+ if (mpStyle->GetFamily() == SfxStyleFamily::Para)
+ {
+ if ((pItem = pItemSet->GetItem(XATTR_FILLSTYLE)) != nullptr)
+ {
+ css::drawing::FillStyle aFillStyle = static_cast<const XFillStyleItem*>(pItem)->GetValue();
+ if (aFillStyle == drawing::FillStyle_SOLID)
+ {
+ if ((pItem = pItemSet->GetItem(XATTR_FILLCOLOR)) != nullptr)
+ {
+ maBackgroundColor = static_cast<const XFillColorItem*>(pItem)->GetColorValue();
+ }
+ }
+ }
+ }
+
+ if ((pItem = pItemSet->GetItem(SID_ATTR_CHAR_FONT)) != nullptr)
+ {
+ const SvxFontItem* pFontItem = static_cast<const SvxFontItem*>(pItem);
+ if (IsStarSymbol(pFontItem->GetFamilyName()))
+ return false;
+ aFont.SetFamilyName(pFontItem->GetFamilyName());
+ aFont.SetStyleName(pFontItem->GetStyleName());
+ }
+ else
+ {
+ return false;
+ }
+
+ if ((pItem = pItemSet->GetItem(SID_ATTR_CHAR_FONTHEIGHT)) != nullptr)
+ {
+ const SvxFontHeightItem* pFontHeightItem = static_cast<const SvxFontHeightItem*>(pItem);
+ Size aFontSize(0, pFontHeightItem->GetHeight());
+ maPixelSize = mrOutputDev.LogicToPixel(aFontSize, MapMode(mrShell.GetMapUnit()));
+ aFont.SetFontSize(maPixelSize);
+
+ vcl::Font aOldFont(mrOutputDev.GetFont());
+
+ mrOutputDev.SetFont(aFont);
+ tools::Rectangle aTextRect;
+ mrOutputDev.GetTextBoundRect(aTextRect, mpStyle->GetName());
+ if (aTextRect.Bottom() > mnMaxHeight)
+ {
+ double ratio = double(mnMaxHeight) / aTextRect.Bottom();
+ maPixelSize.setWidth( maPixelSize.Width() * ratio );
+ maPixelSize.setHeight( maPixelSize.Height() * ratio );
+ aFont.SetFontSize(maPixelSize);
+ }
+ mrOutputDev.SetFont(aOldFont);
+ }
+ else
+ {
+ return false;
+ }
+
+ m_oFont = aFont;
+ maPixelSize = getRenderSize();
+ return true;
+}
+
+Size CommonStylePreviewRenderer::getRenderSize() const
+{
+ assert(m_oFont);
+ Size aPixelSize = m_oFont->GetTextSize(mrOutputDev, maStyleName);
+
+ if (aPixelSize.Height() > mnMaxHeight)
+ aPixelSize.setHeight( mnMaxHeight );
+
+ return aPixelSize;
+}
+
+bool CommonStylePreviewRenderer::render(const tools::Rectangle& aRectangle, RenderAlign eRenderAlign)
+{
+ const OUString& rText = maStyleName;
+
+ // setup the device & draw
+ mrOutputDev.Push(vcl::PushFlags::FONT | vcl::PushFlags::TEXTCOLOR | vcl::PushFlags::FILLCOLOR | vcl::PushFlags::TEXTFILLCOLOR);
+
+ if (maBackgroundColor != COL_AUTO)
+ {
+ mrOutputDev.SetFillColor(maBackgroundColor);
+ mrOutputDev.DrawRect(aRectangle);
+ }
+
+ if (m_oFont)
+ mrOutputDev.SetFont(*m_oFont);
+
+ if (maFontColor != COL_AUTO)
+ mrOutputDev.SetTextColor(maFontColor);
+
+ if (maHighlightColor != COL_AUTO)
+ mrOutputDev.SetTextFillColor(maHighlightColor);
+
+ Size aPixelSize(m_oFont ? maPixelSize : mrOutputDev.GetFont().GetFontSize());
+
+ Point aFontDrawPosition = aRectangle.TopLeft();
+ if (eRenderAlign == RenderAlign::CENTER)
+ {
+ if (aRectangle.GetHeight() > aPixelSize.Height())
+ aFontDrawPosition.AdjustY((aRectangle.GetHeight() - aPixelSize.Height()) / 2 );
+ }
+
+ if (m_oFont)
+ m_oFont->QuickDrawText( &mrOutputDev, aFontDrawPosition, rText, 0, rText.getLength(), {} );
+ else
+ mrOutputDev.DrawText(aFontDrawPosition, rText);
+
+ mrOutputDev.Pop();
+
+ return true;
+}
+
+} // end svx namespace
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/svdraw/ActionDescriptionProvider.cxx b/svx/source/svdraw/ActionDescriptionProvider.cxx
new file mode 100644
index 000000000..0dc5e895c
--- /dev/null
+++ b/svx/source/svdraw/ActionDescriptionProvider.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 <svx/ActionDescriptionProvider.hxx>
+#include <svx/dialmgr.hxx>
+#include <svx/strings.hrc>
+
+OUString ActionDescriptionProvider::createDescription( ActionType eActionType
+ , std::u16string_view rObjectName )
+{
+ TranslateId pResID;
+ switch( eActionType )
+ {
+ case ActionType::Insert:
+ pResID=STR_UndoInsertObj;
+ break;
+ case ActionType::Delete:
+ pResID= STR_EditDelete;
+ break;
+ case ActionType::Move:
+ pResID= STR_EditMove;
+ break;
+ case ActionType::Resize:
+ pResID= STR_EditResize;
+ break;
+ case ActionType::Rotate:
+ pResID= STR_EditRotate;
+ break;
+ case ActionType::Format:
+ pResID= STR_EditSetAttributes;
+ break;
+ case ActionType::MoveToTop:
+ pResID= STR_EditMovToTop;
+ break;
+ case ActionType::MoveToBottom:
+ pResID= STR_EditMovToBtm;
+ break;
+ case ActionType::PosSize:
+ pResID = STR_EditPosSize;
+ break;
+ }
+ if (!pResID)
+ return OUString();
+
+ OUString aStr(SvxResId(pResID));
+ return aStr.replaceAll("%1", rObjectName);
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/svdraw/MediaShellHelpers.cxx b/svx/source/svdraw/MediaShellHelpers.cxx
new file mode 100644
index 000000000..8cf760a7b
--- /dev/null
+++ b/svx/source/svdraw/MediaShellHelpers.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 <config_features.h>
+#include <svx/MediaShellHelpers.hxx>
+#include <avmedia/mediaitem.hxx>
+#include <sfx2/request.hxx>
+#include <sfx2/sfxsids.hrc>
+#include <svl/whiter.hxx>
+#include <svx/sdr/contact/viewcontactofsdrmediaobj.hxx>
+#include <svx/svdmrkv.hxx>
+
+namespace svx::MediaShellHelpers
+{
+void GetState(const SdrMarkView* pSdrView, SfxItemSet& rSet)
+{
+ if (!pSdrView)
+ return;
+
+ SfxWhichIter aIter(rSet);
+
+ for (sal_uInt16 nWhich = aIter.FirstWhich(); nWhich; nWhich = aIter.NextWhich())
+ {
+ if (SID_AVMEDIA_TOOLBOX != nWhich)
+ continue;
+
+#if HAVE_FEATURE_AVMEDIA
+ const SdrMarkList& rMarkList = pSdrView->GetMarkedObjectList();
+ bool bDisable = true;
+
+ if (1 == rMarkList.GetMarkCount())
+ {
+ SdrObject* pObj = rMarkList.GetMark(0)->GetMarkedSdrObj();
+
+ if (dynamic_cast<SdrMediaObj*>(pObj))
+ {
+ ::avmedia::MediaItem aItem(SID_AVMEDIA_TOOLBOX);
+
+ static_cast<sdr::contact::ViewContactOfSdrMediaObj&>(pObj->GetViewContact())
+ .updateMediaItem(aItem);
+ rSet.Put(aItem);
+ bDisable = false;
+ }
+ }
+
+ if (bDisable)
+#endif
+ rSet.DisableItem(SID_AVMEDIA_TOOLBOX);
+ }
+}
+
+const ::avmedia::MediaItem* Execute(const SdrMarkView* pSdrView, SfxRequest const& rReq)
+{
+#if !HAVE_FEATURE_AVMEDIA
+ (void)pSdrView;
+ (void)rReq;
+ return nullptr;
+#else
+ if (!pSdrView)
+ return nullptr;
+
+ if (SID_AVMEDIA_TOOLBOX != rReq.GetSlot())
+ return nullptr;
+
+ const SfxItemSet* pArgs = rReq.GetArgs();
+ if (!pArgs)
+ return nullptr;
+
+ const ::avmedia::MediaItem* pMediaItem = pArgs->GetItemIfSet(SID_AVMEDIA_TOOLBOX, false);
+ if (!pMediaItem)
+ return nullptr;
+
+ const SdrMarkList& rMarkList = pSdrView->GetMarkedObjectList();
+
+ if (1 != rMarkList.GetMarkCount())
+ return nullptr;
+
+ SdrObject* pObj = rMarkList.GetMark(0)->GetMarkedSdrObj();
+
+ if (!dynamic_cast<SdrMediaObj*>(pObj))
+ return nullptr;
+
+ static_cast<sdr::contact::ViewContactOfSdrMediaObj&>(pObj->GetViewContact())
+ .executeMediaItem(*pMediaItem);
+
+ return pMediaItem;
+#endif
+}
+
+} // end of namespace svx::MediaShellHelpers
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/svdraw/charthelper.cxx b/svx/source/svdraw/charthelper.cxx
new file mode 100644
index 000000000..cae747228
--- /dev/null
+++ b/svx/source/svdraw/charthelper.cxx
@@ -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 .
+ */
+
+#include <svx/charthelper.hxx>
+#include <tools/diagnose_ex.h>
+#include <com/sun/star/embed/XEmbeddedObject.hpp>
+#include <com/sun/star/lang/XUnoTunnel.hpp>
+#include <com/sun/star/util/XUpdatable2.hpp>
+#include <com/sun/star/drawing/XDrawPageSupplier.hpp>
+#include <com/sun/star/lang/XMultiServiceFactory.hpp>
+#include <drawinglayer/geometry/viewinformation2d.hxx>
+#include <com/sun/star/chart2/XChartDocument.hpp>
+#include <com/sun/star/drawing/FillStyle.hpp>
+#include <com/sun/star/drawing/LineStyle.hpp>
+#include <sdr/primitive2d/primitivefactory2d.hxx>
+
+using namespace ::com::sun::star;
+
+void ChartHelper::updateChart( const uno::Reference< ::frame::XModel >& rXModel )
+{
+ if (!rXModel.is())
+ return;
+
+ try
+ {
+ const uno::Reference< lang::XMultiServiceFactory > xChartFact(rXModel, uno::UNO_QUERY_THROW);
+ const uno::Reference< lang::XUnoTunnel > xChartView(xChartFact->createInstance("com.sun.star.chart2.ChartView"), uno::UNO_QUERY_THROW);
+ const uno::Reference<util::XUpdatable2> xUpdatable(xChartView, uno::UNO_QUERY_THROW);
+
+ xUpdatable->updateHard();
+ }
+ catch(uno::Exception&)
+ {
+ OSL_ENSURE(false, "Unexpected exception!");
+ }
+}
+
+drawinglayer::primitive2d::Primitive2DContainer ChartHelper::tryToGetChartContentAsPrimitive2DSequence(
+ const uno::Reference< ::frame::XModel >& rXModel,
+ basegfx::B2DRange& rRange)
+{
+ drawinglayer::primitive2d::Primitive2DContainer aRetval;
+
+ if (!rXModel.is())
+ return aRetval;
+
+ // don't broadcast until we're finished building, more efficient
+ rXModel->lockControllers();
+ updateChart(rXModel);
+ rXModel->unlockControllers();
+
+ try
+ {
+ const uno::Reference< drawing::XDrawPageSupplier > xDrawPageSupplier(rXModel, uno::UNO_QUERY_THROW);
+ const uno::Reference< container::XIndexAccess > xShapeAccess(xDrawPageSupplier->getDrawPage(), uno::UNO_QUERY_THROW);
+
+ if(xShapeAccess->getCount())
+ {
+ const sal_Int32 nShapeCount(xShapeAccess->getCount());
+ const uno::Sequence< beans::PropertyValue > aParams;
+ uno::Reference< drawing::XShape > xShape;
+
+ for(sal_Int32 a(0); a < nShapeCount; a++)
+ {
+ xShapeAccess->getByIndex(a) >>= xShape;
+
+ if(xShape.is())
+ {
+ PrimitiveFactory2D::createPrimitivesFromXShape(
+ xShape,
+ aParams,
+ aRetval);
+ }
+ }
+ }
+ }
+ catch(uno::Exception&)
+ {
+ OSL_ENSURE(false, "Unexpected exception!");
+ }
+
+ if(!aRetval.empty())
+ {
+ const drawinglayer::geometry::ViewInformation2D aViewInformation2D;
+
+ rRange = aRetval.getB2DRange(aViewInformation2D);
+ }
+
+ return aRetval;
+}
+
+void ChartHelper::AdaptDefaultsForChart(
+ const uno::Reference < embed::XEmbeddedObject > & xEmbObj)
+{
+ if( !xEmbObj.is())
+ return;
+
+ uno::Reference< chart2::XChartDocument > xChartDoc( xEmbObj->getComponent(), uno::UNO_QUERY );
+ OSL_ENSURE( xChartDoc.is(), "Trying to set chart property to non-chart OLE" );
+ if( !xChartDoc.is())
+ return;
+
+ try
+ {
+ if (uno::Reference< beans::XPropertySet > xPageProp = xChartDoc->getPageBackground())
+ {
+ // set background to transparent (none)
+ xPageProp->setPropertyValue("FillStyle", uno::Any(drawing::FillStyle_NONE));
+ // set no border
+ xPageProp->setPropertyValue("LineStyle", uno::Any(drawing::LineStyle_NONE));
+ }
+ }
+ catch( const uno::Exception & )
+ {
+ TOOLS_WARN_EXCEPTION("svx.svdraw", "");
+ }
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/svdraw/clonelist.cxx b/svx/source/svdraw/clonelist.cxx
new file mode 100644
index 000000000..12f395ac1
--- /dev/null
+++ b/svx/source/svdraw/clonelist.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 .
+ */
+
+
+// #i13033#
+// New mechanism to hold a list of all original and cloned objects for later
+// re-creating the connections for contained connectors
+#include <clonelist.hxx>
+#include <svx/svdoedge.hxx>
+#include <svx/scene3d.hxx>
+
+void CloneList::AddPair(const SdrObject* pOriginal, SdrObject* pClone)
+{
+ maOriginalList.push_back(pOriginal);
+ maCloneList.push_back(pClone);
+
+ // look for subobjects, too.
+ bool bOriginalIsGroup(pOriginal->IsGroupObject());
+ bool bCloneIsGroup(pClone->IsGroupObject());
+
+ if(bOriginalIsGroup && dynamic_cast<const E3dObject* >(pOriginal) != nullptr && dynamic_cast<const E3dScene* >(pOriginal) == nullptr )
+ bOriginalIsGroup = false;
+
+ if(bCloneIsGroup && dynamic_cast<const E3dObject* >(pClone) != nullptr && dynamic_cast<const E3dScene* >(pClone) == nullptr)
+ bCloneIsGroup = false;
+
+ if(!(bOriginalIsGroup && bCloneIsGroup))
+ return;
+
+ const SdrObjList* pOriginalList = pOriginal->GetSubList();
+ SdrObjList* pCloneList = pClone->GetSubList();
+
+ if(pOriginalList && pCloneList
+ && pOriginalList->GetObjCount() == pCloneList->GetObjCount())
+ {
+ for(size_t a = 0; a < pOriginalList->GetObjCount(); ++a)
+ {
+ // recursive call
+ AddPair(pOriginalList->GetObj(a), pCloneList->GetObj(a));
+ }
+ }
+}
+
+const SdrObject* CloneList::GetOriginal(sal_uInt32 nIndex) const
+{
+ return maOriginalList[nIndex];
+}
+
+SdrObject* CloneList::GetClone(sal_uInt32 nIndex) const
+{
+ return maCloneList[nIndex];
+}
+
+void CloneList::CopyConnections() const
+{
+ sal_uInt32 cloneCount = maCloneList.size();
+
+ for(size_t a = 0; a < maOriginalList.size(); a++)
+ {
+ const SdrEdgeObj* pOriginalEdge = dynamic_cast<const SdrEdgeObj*>( GetOriginal(a) );
+ SdrEdgeObj* pCloneEdge = dynamic_cast<SdrEdgeObj*>( GetClone(a) );
+
+ if(pOriginalEdge && pCloneEdge)
+ {
+ SdrObject* pOriginalNode1 = pOriginalEdge->GetConnectedNode(true);
+ SdrObject* pOriginalNode2 = pOriginalEdge->GetConnectedNode(false);
+
+ if(pOriginalNode1)
+ {
+ std::vector<const SdrObject*>::const_iterator it = std::find(maOriginalList.begin(),
+ maOriginalList.end(),
+ pOriginalNode1);
+
+ sal_uInt32 nPos = it - maOriginalList.begin();
+
+ if(it != maOriginalList.end())
+ {
+ SdrObject *cObj = nullptr;
+
+ if (nPos < cloneCount)
+ cObj = GetClone(nPos);
+
+ if(pOriginalEdge->GetConnectedNode(true) != cObj)
+ pCloneEdge->ConnectToNode(true, cObj);
+ }
+ }
+
+ if(pOriginalNode2)
+ {
+ std::vector<const SdrObject*>::const_iterator it = std::find(maOriginalList.begin(),
+ maOriginalList.end(),
+ pOriginalNode2);
+
+ sal_uInt32 nPos = it - maOriginalList.begin();
+
+ if(it != maOriginalList.end())
+ {
+ SdrObject *cObj = nullptr;
+
+ if (nPos < cloneCount)
+ cObj = GetClone(nPos);
+
+ if(pOriginalEdge->GetConnectedNode(false) != cObj)
+ pCloneEdge->ConnectToNode(false, cObj);
+ }
+ }
+ }
+ }
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/svdraw/gradtrns.cxx b/svx/source/svdraw/gradtrns.cxx
new file mode 100644
index 000000000..d665cd48c
--- /dev/null
+++ b/svx/source/svdraw/gradtrns.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 "gradtrns.hxx"
+#include <svx/svdobj.hxx>
+#include <basegfx/range/b2drange.hxx>
+#include <basegfx/matrix/b2dhommatrix.hxx>
+#include <basegfx/matrix/b2dhommatrixtools.hxx>
+#include <tools/helpers.hxx>
+#include <vcl/canvastools.hxx>
+
+
+void GradTransformer::GradToVec(GradTransGradient const & rG, GradTransVector& rV, const SdrObject* pObj)
+{
+ // handle start color
+ rV.aCol1 = rG.aGradient.GetStartColor();
+ if(100 != rG.aGradient.GetStartIntens())
+ {
+ const double fFact(static_cast<double>(rG.aGradient.GetStartIntens()) / 100.0);
+ rV.aCol1 = Color(rV.aCol1.getBColor() * fFact);
+ }
+
+ // handle end color
+ rV.aCol2 = rG.aGradient.GetEndColor();
+ if(100 != rG.aGradient.GetEndIntens())
+ {
+ const double fFact(static_cast<double>(rG.aGradient.GetEndIntens()) / 100.0);
+ rV.aCol2 = Color(rV.aCol2.getBColor() * fFact);
+ }
+
+ // calc the basic positions
+ const tools::Rectangle aObjectSnapRectangle(pObj->GetSnapRect());
+ const basegfx::B2DRange aRange = vcl::unotools::b2DRectangleFromRectangle(aObjectSnapRectangle);
+ const basegfx::B2DPoint aCenter(aRange.getCenter());
+ basegfx::B2DPoint aStartPos, aEndPos;
+
+ switch(rG.aGradient.GetGradientStyle())
+ {
+ case css::awt::GradientStyle_LINEAR :
+ {
+ aStartPos = basegfx::B2DPoint(aCenter.getX(), aRange.getMinY());
+ aEndPos = basegfx::B2DPoint(aCenter.getX(), aRange.getMaximum().getY());
+
+ if(rG.aGradient.GetBorder())
+ {
+ basegfx::B2DVector aFullVec(aStartPos - aEndPos);
+ const double fLen = (aFullVec.getLength() * (100.0 - static_cast<double>(rG.aGradient.GetBorder()))) / 100.0;
+ aFullVec.normalize();
+ aStartPos = aEndPos + (aFullVec * fLen);
+ }
+
+ if(rG.aGradient.GetAngle())
+ {
+ const double fAngle = toRadians(rG.aGradient.GetAngle());
+ const basegfx::B2DHomMatrix aTransformation(basegfx::utils::createRotateAroundPoint(aCenter, -fAngle));
+
+ aStartPos *= aTransformation;
+ aEndPos *= aTransformation;
+ }
+ break;
+ }
+ case css::awt::GradientStyle_AXIAL :
+ {
+ aStartPos = aCenter;
+ aEndPos = basegfx::B2DPoint(aCenter.getX(), aRange.getMaximum().getY());
+
+ if(rG.aGradient.GetBorder())
+ {
+ basegfx::B2DVector aFullVec(aEndPos - aStartPos);
+ const double fLen = (aFullVec.getLength() * (100.0 - static_cast<double>(rG.aGradient.GetBorder()))) / 100.0;
+ aFullVec.normalize();
+ aEndPos = aStartPos + (aFullVec * fLen);
+ }
+
+ if(rG.aGradient.GetAngle())
+ {
+ const double fAngle = toRadians(rG.aGradient.GetAngle());
+ const basegfx::B2DHomMatrix aTransformation(basegfx::utils::createRotateAroundPoint(aCenter, -fAngle));
+
+ aStartPos *= aTransformation;
+ aEndPos *= aTransformation;
+ }
+ break;
+ }
+ case css::awt::GradientStyle_RADIAL :
+ case css::awt::GradientStyle_SQUARE :
+ {
+ aStartPos = basegfx::B2DPoint(aRange.getMinX(), aRange.getMaximum().getY());
+ aEndPos = basegfx::B2DPoint(aRange.getMinX(), aRange.getMinY());
+
+ if(rG.aGradient.GetBorder())
+ {
+ basegfx::B2DVector aFullVec(aStartPos - aEndPos);
+ const double fLen = (aFullVec.getLength() * (100.0 - static_cast<double>(rG.aGradient.GetBorder()))) / 100.0;
+ aFullVec.normalize();
+ aStartPos = aEndPos + (aFullVec * fLen);
+ }
+
+ if(rG.aGradient.GetAngle())
+ {
+ const double fAngle = toRadians(rG.aGradient.GetAngle());
+ const basegfx::B2DHomMatrix aTransformation(basegfx::utils::createRotateAroundPoint(aEndPos, -fAngle));
+
+ aStartPos *= aTransformation;
+ aEndPos *= aTransformation;
+ }
+
+ if(rG.aGradient.GetXOffset() || rG.aGradient.GetYOffset())
+ {
+ basegfx::B2DPoint aOffset(
+ (aRange.getWidth() * rG.aGradient.GetXOffset()) / 100.0,
+ (aRange.getHeight() * rG.aGradient.GetYOffset()) / 100.0);
+
+ aStartPos += aOffset;
+ aEndPos += aOffset;
+ }
+
+ break;
+ }
+ case css::awt::GradientStyle_ELLIPTICAL :
+ case css::awt::GradientStyle_RECT :
+ {
+ aStartPos = basegfx::B2DPoint(aRange.getMinX(), aCenter.getY());
+ aEndPos = basegfx::B2DPoint(aRange.getMinX(), aRange.getMinY());
+
+ if(rG.aGradient.GetBorder())
+ {
+ basegfx::B2DVector aFullVec(aStartPos - aEndPos);
+ const double fLen = (aFullVec.getLength() * (100.0 - static_cast<double>(rG.aGradient.GetBorder()))) / 100.0;
+ aFullVec.normalize();
+ aStartPos = aEndPos + (aFullVec * fLen);
+ }
+
+ if(rG.aGradient.GetAngle())
+ {
+ const double fAngle = toRadians(rG.aGradient.GetAngle());
+ const basegfx::B2DHomMatrix aTransformation(basegfx::utils::createRotateAroundPoint(aEndPos, -fAngle));
+
+ aStartPos *= aTransformation;
+ aEndPos *= aTransformation;
+ }
+
+ if(rG.aGradient.GetXOffset() || rG.aGradient.GetYOffset())
+ {
+ basegfx::B2DPoint aOffset(
+ (aRange.getWidth() * rG.aGradient.GetXOffset()) / 100.0,
+ (aRange.getHeight() * rG.aGradient.GetYOffset()) / 100.0);
+
+ aStartPos += aOffset;
+ aEndPos += aOffset;
+ }
+
+ break;
+ }
+ default:
+ break;
+ }
+
+ // set values for vector positions now
+ rV.maPositionA = aStartPos;
+ rV.maPositionB = aEndPos;
+}
+
+
+void GradTransformer::VecToGrad(GradTransVector const & rV, GradTransGradient& rG, GradTransGradient const & rGOld, const SdrObject* pObj,
+ bool bMoveSingle, bool bMoveFirst)
+{
+ // fill old gradient to new gradient to have a base
+ rG = rGOld;
+
+ // handle color changes
+ if(rV.aCol1 != rGOld.aGradient.GetStartColor())
+ {
+ rG.aGradient.SetStartColor(rV.aCol1);
+ rG.aGradient.SetStartIntens(100);
+ }
+ if(rV.aCol2 != rGOld.aGradient.GetEndColor())
+ {
+ rG.aGradient.SetEndColor(rV.aCol2);
+ rG.aGradient.SetEndIntens(100);
+ }
+
+ // calc the basic positions
+ const tools::Rectangle aObjectSnapRectangle(pObj->GetSnapRect());
+ const basegfx::B2DRange aRange = vcl::unotools::b2DRectangleFromRectangle(aObjectSnapRectangle);
+ const basegfx::B2DPoint aCenter(aRange.getCenter());
+ basegfx::B2DPoint aStartPos(rV.maPositionA);
+ basegfx::B2DPoint aEndPos(rV.maPositionB);
+
+ switch(rG.aGradient.GetGradientStyle())
+ {
+ case css::awt::GradientStyle_LINEAR :
+ {
+ if(!bMoveSingle || !bMoveFirst)
+ {
+ basegfx::B2DVector aFullVec(aEndPos - aStartPos);
+
+ if(bMoveSingle)
+ {
+ aFullVec = aEndPos - aCenter;
+ }
+
+ aFullVec.normalize();
+
+ double fNewFullAngle(basegfx::rad2deg(atan2(aFullVec.getY(), aFullVec.getX())));
+ fNewFullAngle *= -10.0;
+ fNewFullAngle += 900.0;
+
+ // clip
+ while(fNewFullAngle < 0.0)
+ {
+ fNewFullAngle += 3600.0;
+ }
+
+ while(fNewFullAngle >= 3600.0)
+ {
+ fNewFullAngle -= 3600.0;
+ }
+
+ // to int and set
+ Degree10 nNewAngle( FRound(fNewFullAngle));
+
+ if(nNewAngle != rGOld.aGradient.GetAngle())
+ {
+ rG.aGradient.SetAngle(nNewAngle);
+ }
+ }
+
+ if(!bMoveSingle || bMoveFirst)
+ {
+ const basegfx::B2DVector aFullVec(aEndPos - aStartPos);
+ const basegfx::B2DPoint aBottomLeft(aRange.getMinX(), aRange.getMaximum().getY());
+ const basegfx::B2DPoint aTopLeft(aRange.getMinX(), aRange.getMinY());
+ const basegfx::B2DVector aOldVec(aBottomLeft - aTopLeft);
+ const double fFullLen(aFullVec.getLength());
+ const double fOldLen(aOldVec.getLength());
+ const double fNewBorder((fFullLen * 100.0) / fOldLen);
+ sal_Int32 nNewBorder(100 - FRound(fNewBorder));
+
+ // clip
+ if(nNewBorder < 0)
+ {
+ nNewBorder = 0;
+ }
+
+ if(nNewBorder > 100)
+ {
+ nNewBorder = 100;
+ }
+
+ // set
+ if(nNewBorder != rG.aGradient.GetBorder())
+ {
+ rG.aGradient.SetBorder(static_cast<sal_uInt16>(nNewBorder));
+ }
+ }
+
+ break;
+ }
+ case css::awt::GradientStyle_AXIAL :
+ {
+ if(!bMoveSingle || !bMoveFirst)
+ {
+ basegfx::B2DVector aFullVec(aEndPos - aCenter);
+ const basegfx::B2DVector aOldVec(basegfx::B2DPoint(aCenter.getX(), aRange.getMaximum().getY()) - aCenter);
+ const double fFullLen(aFullVec.getLength());
+ const double fOldLen(aOldVec.getLength());
+ const double fNewBorder((fFullLen * 100.0) / fOldLen);
+ sal_Int32 nNewBorder = 100 - FRound(fNewBorder);
+
+ // clip
+ if(nNewBorder < 0)
+ {
+ nNewBorder = 0;
+ }
+
+ if(nNewBorder > 100)
+ {
+ nNewBorder = 100;
+ }
+
+ // set
+ if(nNewBorder != rG.aGradient.GetBorder())
+ {
+ rG.aGradient.SetBorder(static_cast<sal_uInt16>(nNewBorder));
+ }
+
+ aFullVec.normalize();
+ double fNewFullAngle(basegfx::rad2deg(atan2(aFullVec.getY(), aFullVec.getX())));
+ fNewFullAngle *= -10.0;
+ fNewFullAngle += 900.0;
+
+ // clip
+ while(fNewFullAngle < 0.0)
+ {
+ fNewFullAngle += 3600.0;
+ }
+
+ while(fNewFullAngle >= 3600.0)
+ {
+ fNewFullAngle -= 3600.0;
+ }
+
+ // to int and set
+ const Degree10 nNewAngle(FRound(fNewFullAngle));
+
+ if(nNewAngle != rGOld.aGradient.GetAngle())
+ {
+ rG.aGradient.SetAngle(nNewAngle);
+ }
+ }
+
+ break;
+ }
+ case css::awt::GradientStyle_RADIAL :
+ case css::awt::GradientStyle_SQUARE :
+ {
+ if(!bMoveSingle || !bMoveFirst)
+ {
+ const basegfx::B2DPoint aTopLeft(aRange.getMinX(), aRange.getMinY());
+ const basegfx::B2DPoint aOffset(aEndPos - aTopLeft);
+ sal_Int32 nNewXOffset(FRound((aOffset.getX() * 100.0) / aRange.getWidth()));
+ sal_Int32 nNewYOffset(FRound((aOffset.getY() * 100.0) / aRange.getHeight()));
+
+ // clip
+ if(nNewXOffset < 0)
+ {
+ nNewXOffset = 0;
+ }
+
+ if(nNewXOffset > 100)
+ {
+ nNewXOffset = 100;
+ }
+
+ if(nNewYOffset < 0)
+ {
+ nNewYOffset = 0;
+ }
+
+ if(nNewYOffset > 100)
+ {
+ nNewYOffset = 100;
+ }
+
+ rG.aGradient.SetXOffset(static_cast<sal_uInt16>(nNewXOffset));
+ rG.aGradient.SetYOffset(static_cast<sal_uInt16>(nNewYOffset));
+
+ aStartPos -= aOffset;
+ aEndPos -= aOffset;
+ }
+
+ if(!bMoveSingle || bMoveFirst)
+ {
+ basegfx::B2DVector aFullVec(aStartPos - aEndPos);
+ const basegfx::B2DPoint aBottomLeft(aRange.getMinX(), aRange.getMaximum().getY());
+ const basegfx::B2DPoint aTopLeft(aRange.getMinX(), aRange.getMinY());
+ const basegfx::B2DVector aOldVec(aBottomLeft - aTopLeft);
+ const double fFullLen(aFullVec.getLength());
+ const double fOldLen(aOldVec.getLength());
+ const double fNewBorder((fFullLen * 100.0) / fOldLen);
+ sal_Int32 nNewBorder(100 - FRound(fNewBorder));
+
+ // clip
+ if(nNewBorder < 0)
+ {
+ nNewBorder = 0;
+ }
+
+ if(nNewBorder > 100)
+ {
+ nNewBorder = 100;
+ }
+
+ // set
+ if(nNewBorder != rG.aGradient.GetBorder())
+ {
+ rG.aGradient.SetBorder(static_cast<sal_uInt16>(nNewBorder));
+ }
+
+ // angle is not definitely necessary for these modes, but it makes
+ // controlling more fun for the user
+ aFullVec.normalize();
+ double fNewFullAngle(basegfx::rad2deg(atan2(aFullVec.getY(), aFullVec.getX())));
+ fNewFullAngle *= -10.0;
+ fNewFullAngle += 900.0;
+
+ // clip
+ while(fNewFullAngle < 0.0)
+ {
+ fNewFullAngle += 3600.0;
+ }
+
+ while(fNewFullAngle >= 3600.0)
+ {
+ fNewFullAngle -= 3600.0;
+ }
+
+ // to int and set
+ const Degree10 nNewAngle(FRound(fNewFullAngle));
+
+ if(nNewAngle != rGOld.aGradient.GetAngle())
+ {
+ rG.aGradient.SetAngle(nNewAngle);
+ }
+ }
+
+ break;
+ }
+ case css::awt::GradientStyle_ELLIPTICAL :
+ case css::awt::GradientStyle_RECT :
+ {
+ if(!bMoveSingle || !bMoveFirst)
+ {
+ const basegfx::B2DPoint aTopLeft(aRange.getMinX(), aRange.getMinY());
+ const basegfx::B2DPoint aOffset(aEndPos - aTopLeft);
+ sal_Int32 nNewXOffset(FRound((aOffset.getX() * 100.0) / aRange.getWidth()));
+ sal_Int32 nNewYOffset(FRound((aOffset.getY() * 100.0) / aRange.getHeight()));
+
+ // clip
+ if(nNewXOffset < 0)
+ {
+ nNewXOffset = 0;
+ }
+
+ if(nNewXOffset > 100)
+ {
+ nNewXOffset = 100;
+ }
+
+ if(nNewYOffset < 0)
+ {
+ nNewYOffset = 0;
+ }
+
+ if(nNewYOffset > 100)
+ {
+ nNewYOffset = 100;
+ }
+
+ rG.aGradient.SetXOffset(static_cast<sal_uInt16>(nNewXOffset));
+ rG.aGradient.SetYOffset(static_cast<sal_uInt16>(nNewYOffset));
+
+ aStartPos -= aOffset;
+ aEndPos -= aOffset;
+ }
+
+ if(!bMoveSingle || bMoveFirst)
+ {
+ basegfx::B2DVector aFullVec(aStartPos - aEndPos);
+ const basegfx::B2DPoint aTopLeft(aRange.getMinX(), aRange.getMinY());
+ const basegfx::B2DPoint aCenterLeft(aRange.getMinX(), aCenter.getY());
+ const basegfx::B2DVector aOldVec(aCenterLeft - aTopLeft);
+ const double fFullLen(aFullVec.getLength());
+ const double fOldLen(aOldVec.getLength());
+ const double fNewBorder((fFullLen * 100.0) / fOldLen);
+ sal_Int32 nNewBorder(100 - FRound(fNewBorder));
+
+ // clip
+ if(nNewBorder < 0)
+ {
+ nNewBorder = 0;
+ }
+
+ if(nNewBorder > 100)
+ {
+ nNewBorder = 100;
+ }
+
+ // set
+ if(nNewBorder != rG.aGradient.GetBorder())
+ {
+ rG.aGradient.SetBorder(static_cast<sal_uInt16>(nNewBorder));
+ }
+
+ // angle is not definitely necessary for these modes, but it makes
+ // controlling more fun for the user
+ aFullVec.normalize();
+ double fNewFullAngle(basegfx::rad2deg(atan2(aFullVec.getY(), aFullVec.getX())));
+ fNewFullAngle *= -10.0;
+ fNewFullAngle += 900.0;
+
+ // clip
+ while(fNewFullAngle < 0.0)
+ {
+ fNewFullAngle += 3600.0;
+ }
+
+ while(fNewFullAngle >= 3600.0)
+ {
+ fNewFullAngle -= 3600.0;
+ }
+
+ // to int and set
+ const Degree10 nNewAngle(FRound(fNewFullAngle));
+
+ if(nNewAngle != rGOld.aGradient.GetAngle())
+ {
+ rG.aGradient.SetAngle(nNewAngle);
+ }
+ }
+
+ break;
+ }
+ default:
+ break;
+ }
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/svdraw/gradtrns.hxx b/svx/source/svdraw/gradtrns.hxx
new file mode 100644
index 000000000..eee35582a
--- /dev/null
+++ b/svx/source/svdraw/gradtrns.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_SVX_SOURCE_SVDRAW_GRADTRNS_HXX
+#define INCLUDED_SVX_SOURCE_SVDRAW_GRADTRNS_HXX
+
+#include <svx/xgrad.hxx>
+#include <basegfx/point/b2dpoint.hxx>
+
+class SdrObject;
+
+class GradTransVector
+{
+public:
+ basegfx::B2DPoint maPositionA;
+ basegfx::B2DPoint maPositionB;
+ Color aCol1;
+ Color aCol2;
+};
+
+class GradTransGradient
+{
+public:
+ XGradient aGradient;
+};
+
+struct GradTransformer
+{
+ GradTransformer() = delete;
+
+ static void GradToVec(GradTransGradient const & rG, GradTransVector& rV,
+ const SdrObject* pObj);
+ static void VecToGrad(GradTransVector const & rV, GradTransGradient& rG,
+ GradTransGradient const & rGOld, const SdrObject* pObj, bool bMoveSingle, bool bMoveFirst);
+};
+
+#endif // INCLUDED_SVX_SOURCE_SVDRAW_GRADTRNS_HXX
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/svdraw/polypolygoneditor.cxx b/svx/source/svdraw/polypolygoneditor.cxx
new file mode 100644
index 000000000..b3f6d3bc5
--- /dev/null
+++ b/svx/source/svdraw/polypolygoneditor.cxx
@@ -0,0 +1,182 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+
+#include <basegfx/polygon/b2dpolygon.hxx>
+#include <basegfx/polygon/b2dpolygontools.hxx>
+
+#include <svx/polypolygoneditor.hxx>
+
+namespace sdr {
+
+PolyPolygonEditor::PolyPolygonEditor( const basegfx::B2DPolyPolygon& rPolyPolygon)
+: maPolyPolygon( rPolyPolygon )
+{
+}
+
+bool PolyPolygonEditor::DeletePoints( const o3tl::sorted_vector< sal_uInt16 >& rAbsPoints )
+{
+ bool bPolyPolyChanged = false;
+
+ auto aIter( rAbsPoints.rbegin() );
+ for( ; aIter != rAbsPoints.rend(); ++aIter )
+ {
+ sal_uInt32 nPoly, nPnt;
+ if( GetRelativePolyPoint(maPolyPolygon,(*aIter), nPoly, nPnt) )
+ {
+ // remove point
+ basegfx::B2DPolygon aCandidate(maPolyPolygon.getB2DPolygon(nPoly));
+
+ aCandidate.remove(nPnt);
+
+
+ if( aCandidate.count() < 2 )
+ {
+ maPolyPolygon.remove(nPoly);
+ }
+ else
+ {
+ maPolyPolygon.setB2DPolygon(nPoly, aCandidate);
+ }
+
+ bPolyPolyChanged = true;
+ }
+ }
+
+ return bPolyPolyChanged;
+}
+
+bool PolyPolygonEditor::SetSegmentsKind(SdrPathSegmentKind eKind, const o3tl::sorted_vector< sal_uInt16 >& rAbsPoints )
+{
+ bool bPolyPolyChanged = false;
+
+ auto aIter( rAbsPoints.rbegin() );
+ for( ; aIter != rAbsPoints.rend(); ++aIter )
+ {
+ sal_uInt32 nPolyNum, nPntNum;
+
+ if(PolyPolygonEditor::GetRelativePolyPoint(maPolyPolygon, (*aIter), nPolyNum, nPntNum))
+ {
+ // do change at aNewPolyPolygon. Take a look at edge.
+ basegfx::B2DPolygon aCandidate(maPolyPolygon.getB2DPolygon(nPolyNum));
+ const sal_uInt32 nCount(aCandidate.count());
+
+ if(nCount && (nPntNum + 1 < nCount || aCandidate.isClosed()))
+ {
+ bool bCandidateChanged(false);
+
+ // it's a valid edge, check control point usage
+ const sal_uInt32 nNextIndex((nPntNum + 1) % nCount);
+ const bool bContolUsed(aCandidate.areControlPointsUsed()
+ && (aCandidate.isNextControlPointUsed(nPntNum) || aCandidate.isPrevControlPointUsed(nNextIndex)));
+
+ if(bContolUsed)
+ {
+ if(SdrPathSegmentKind::Toggle == eKind || SdrPathSegmentKind::Line == eKind)
+ {
+ // remove control
+ aCandidate.resetNextControlPoint(nPntNum);
+ aCandidate.resetPrevControlPoint(nNextIndex);
+ bCandidateChanged = true;
+ }
+ }
+ else
+ {
+ if(SdrPathSegmentKind::Toggle == eKind || SdrPathSegmentKind::Curve == eKind)
+ {
+ // add control
+ const basegfx::B2DPoint aStart(aCandidate.getB2DPoint(nPntNum));
+ const basegfx::B2DPoint aEnd(aCandidate.getB2DPoint(nNextIndex));
+
+ aCandidate.setNextControlPoint(nPntNum, interpolate(aStart, aEnd, (1.0 / 3.0)));
+ aCandidate.setPrevControlPoint(nNextIndex, interpolate(aStart, aEnd, (2.0 / 3.0)));
+ bCandidateChanged = true;
+ }
+ }
+
+ if(bCandidateChanged)
+ {
+ maPolyPolygon.setB2DPolygon(nPolyNum, aCandidate);
+ bPolyPolyChanged = true;
+ }
+ }
+ }
+ }
+
+ return bPolyPolyChanged;
+}
+
+bool PolyPolygonEditor::SetPointsSmooth( basegfx::B2VectorContinuity eFlags, const o3tl::sorted_vector< sal_uInt16 >& rAbsPoints)
+{
+ bool bPolyPolygonChanged(false);
+
+ auto aIter( rAbsPoints.rbegin() );
+ for( ; aIter != rAbsPoints.rend(); ++aIter )
+ {
+ sal_uInt32 nPolyNum, nPntNum;
+
+ if(PolyPolygonEditor::GetRelativePolyPoint(maPolyPolygon, (*aIter), nPolyNum, nPntNum))
+ {
+ // do change at aNewPolyPolygon...
+ basegfx::B2DPolygon aCandidate(maPolyPolygon.getB2DPolygon(nPolyNum));
+
+ // set continuity in point, make sure there is a curve
+ bool bPolygonChanged = basegfx::utils::expandToCurveInPoint(aCandidate, nPntNum);
+ bPolygonChanged |= basegfx::utils::setContinuityInPoint(aCandidate, nPntNum, eFlags);
+
+ if(bPolygonChanged)
+ {
+ maPolyPolygon.setB2DPolygon(nPolyNum, aCandidate);
+ bPolyPolygonChanged = true;
+ }
+ }
+ }
+
+ return bPolyPolygonChanged;
+}
+
+bool PolyPolygonEditor::GetRelativePolyPoint( const basegfx::B2DPolyPolygon& rPoly, sal_uInt32 nAbsPnt, sal_uInt32& rPolyNum, sal_uInt32& rPointNum )
+{
+ const sal_uInt32 nPolyCount(rPoly.count());
+ sal_uInt32 nPolyNum(0);
+
+ while(nPolyNum < nPolyCount)
+ {
+ const sal_uInt32 nPointCount(rPoly.getB2DPolygon(nPolyNum).count());
+
+ if(nAbsPnt < nPointCount)
+ {
+ rPolyNum = nPolyNum;
+ rPointNum = nAbsPnt;
+
+ return true;
+ }
+ else
+ {
+ nPolyNum++;
+ nAbsPnt -= nPointCount;
+ }
+ }
+
+ return false;
+}
+
+} // end of namespace sdr
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/svdraw/presetooxhandleadjustmentrelations.cxx b/svx/source/svdraw/presetooxhandleadjustmentrelations.cxx
new file mode 100644
index 000000000..528c8b35c
--- /dev/null
+++ b/svx/source/svdraw/presetooxhandleadjustmentrelations.cxx
@@ -0,0 +1,343 @@
+/* -*- 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 <sal/config.h>
+
+#include <string_view>
+
+#include <o3tl/string_view.hxx>
+#include <rtl/ustring.hxx>
+#include <unordered_map>
+#include "presetooxhandleadjustmentrelations.hxx"
+
+namespace
+{
+typedef std::unordered_map<OUString, OUString> HandleAdjRelHashMap;
+
+struct HandleAdjRel
+{
+ // Shape name without leading "ooxml-", underscore, zero based handle index
+ // e.g. The third handle in shape of type "ooxml-circularArrow" will be
+ // identified by key "circularArrow_2"
+ const char* sShape_Handle;
+
+ // 4 tokens with separator "|"
+ // first: RefX or RefR, na if not exists
+ // second: adj, or adj1 or adj2, etc. as in preset, na if not exists
+ // third: RefY or RefAngle, na if not exists
+ // forth: adj, or adj1 or adj2, etc. as in preset, na if not exists
+ // e.g. The third handle in shape <circularArrow> has in the preset
+ // the tag <ahPolar gdRefR="adj5" minR="0" maxR="25000"> .
+ // The resulting value in the map here is "RefR|adj5|na|na"
+ const char* sAdjReferences;
+};
+
+// The array initializer has been extracted from
+// oox/source/drawingml/customshapes/presetShapeDefinitions.xml
+// by using an XSLT file. That file is attached to tdf#126512.
+constexpr HandleAdjRel aHandleAdjRelArray[]
+ = { { "accentBorderCallout1_0", "RefX|adj2|RefY|adj1" },
+ { "accentBorderCallout1_1", "RefX|adj4|RefY|adj3" },
+ { "accentBorderCallout2_0", "RefX|adj2|RefY|adj1" },
+ { "accentBorderCallout2_1", "RefX|adj4|RefY|adj3" },
+ { "accentBorderCallout2_2", "RefX|adj6|RefY|adj5" },
+ { "accentBorderCallout3_0", "RefX|adj2|RefY|adj1" },
+ { "accentBorderCallout3_1", "RefX|adj4|RefY|adj3" },
+ { "accentBorderCallout3_2", "RefX|adj6|RefY|adj5" },
+ { "accentBorderCallout3_3", "RefX|adj8|RefY|adj7" },
+ { "accentCallout1_0", "RefX|adj2|RefY|adj1" },
+ { "accentCallout1_1", "RefX|adj4|RefY|adj3" },
+ { "accentCallout2_0", "RefX|adj2|RefY|adj1" },
+ { "accentCallout2_1", "RefX|adj4|RefY|adj3" },
+ { "accentCallout2_2", "RefX|adj6|RefY|adj5" },
+ { "accentCallout3_0", "RefX|adj2|RefY|adj1" },
+ { "accentCallout3_1", "RefX|adj4|RefY|adj3" },
+ { "accentCallout3_2", "RefX|adj6|RefY|adj5" },
+ { "accentCallout3_3", "RefX|adj8|RefY|adj7" },
+ { "arc_0", "na|na|RefAngle|adj1" },
+ { "arc_1", "na|na|RefAngle|adj2" },
+ { "bentArrow_0", "RefX|adj1|na|na" },
+ { "bentArrow_1", "na|na|RefY|adj2" },
+ { "bentArrow_2", "RefX|adj3|na|na" },
+ { "bentArrow_3", "RefX|adj4|na|na" },
+ { "bentConnector3_0", "RefX|adj1|na|na" },
+ { "bentConnector4_0", "RefX|adj1|na|na" },
+ { "bentConnector4_1", "na|na|RefY|adj2" },
+ { "bentConnector5_0", "RefX|adj1|na|na" },
+ { "bentConnector5_1", "na|na|RefY|adj2" },
+ { "bentConnector5_2", "RefX|adj3|na|na" },
+ { "bentUpArrow_0", "na|na|RefY|adj1" },
+ { "bentUpArrow_1", "RefX|adj2|na|na" },
+ { "bentUpArrow_2", "na|na|RefY|adj3" },
+ { "bevel_0", "RefX|adj|na|na" },
+ { "blockArc_0", "na|na|RefAngle|adj1" },
+ { "blockArc_1", "RefR|adj3|RefAngle|adj2" },
+ { "borderCallout1_0", "RefX|adj2|RefY|adj1" },
+ { "borderCallout1_1", "RefX|adj4|RefY|adj3" },
+ { "borderCallout2_0", "RefX|adj2|RefY|adj1" },
+ { "borderCallout2_1", "RefX|adj4|RefY|adj3" },
+ { "borderCallout2_2", "RefX|adj6|RefY|adj5" },
+ { "borderCallout3_0", "RefX|adj2|RefY|adj1" },
+ { "borderCallout3_1", "RefX|adj4|RefY|adj3" },
+ { "borderCallout3_2", "RefX|adj6|RefY|adj5" },
+ { "borderCallout3_3", "RefX|adj8|RefY|adj7" },
+ { "bracePair_0", "na|na|RefY|adj" },
+ { "bracketPair_0", "na|na|RefY|adj" },
+ { "callout1_0", "RefX|adj2|RefY|adj1" },
+ { "callout1_1", "RefX|adj4|RefY|adj3" },
+ { "callout2_0", "RefX|adj2|RefY|adj1" },
+ { "callout2_1", "RefX|adj4|RefY|adj3" },
+ { "callout2_2", "RefX|adj6|RefY|adj5" },
+ { "callout3_0", "RefX|adj2|RefY|adj1" },
+ { "callout3_1", "RefX|adj4|RefY|adj3" },
+ { "callout3_2", "RefX|adj6|RefY|adj5" },
+ { "callout3_3", "RefX|adj8|RefY|adj7" },
+ { "can_0", "na|na|RefY|adj" },
+ { "chevron_0", "RefX|adj|na|na" },
+ { "chord_0", "na|na|RefAngle|adj1" },
+ { "chord_1", "na|na|RefAngle|adj2" },
+ { "circularArrow_0", "na|na|RefAngle|adj2" },
+ { "circularArrow_1", "na|na|RefAngle|adj4" },
+ { "circularArrow_2", "RefR|adj1|RefAngle|adj3" },
+ { "circularArrow_3", "RefR|adj5|na|na" },
+ { "cloudCallout_0", "RefX|adj1|RefY|adj2" },
+ { "corner_0", "na|na|RefY|adj1" },
+ { "corner_1", "RefX|adj2|na|na" },
+ { "cube_0", "na|na|RefY|adj" },
+ { "curvedConnector3_0", "RefX|adj1|na|na" },
+ { "curvedConnector4_0", "RefX|adj1|na|na" },
+ { "curvedConnector4_1", "na|na|RefY|adj2" },
+ { "curvedConnector5_0", "RefX|adj1|na|na" },
+ { "curvedConnector5_1", "na|na|RefY|adj2" },
+ { "curvedConnector5_2", "RefX|adj3|na|na" },
+ { "curvedDownArrow_0", "RefX|adj1|na|na" },
+ { "curvedDownArrow_1", "RefX|adj2|na|na" },
+ { "curvedDownArrow_2", "na|na|RefY|adj3" },
+ { "curvedLeftArrow_0", "na|na|RefY|adj1" },
+ { "curvedLeftArrow_1", "na|na|RefY|adj2" },
+ { "curvedLeftArrow_2", "RefX|adj3|na|na" },
+ { "curvedRightArrow_0", "na|na|RefY|adj1" },
+ { "curvedRightArrow_1", "na|na|RefY|adj2" },
+ { "curvedRightArrow_2", "RefX|adj3|na|na" },
+ { "curvedUpArrow_0", "RefX|adj1|na|na" },
+ { "curvedUpArrow_1", "RefX|adj2|na|na" },
+ { "curvedUpArrow_2", "na|na|RefY|adj3" },
+ { "diagStripe_0", "na|na|RefY|adj" },
+ { "donut_0", "RefR|adj|na|na" },
+ { "doubleWave_0", "na|na|RefY|adj1" },
+ { "doubleWave_1", "RefX|adj2|na|na" },
+ { "downArrow_0", "RefX|adj1|na|na" },
+ { "downArrow_1", "na|na|RefY|adj2" },
+ { "downArrowCallout_0", "RefX|adj1|na|na" },
+ { "downArrowCallout_1", "RefX|adj2|na|na" },
+ { "downArrowCallout_2", "na|na|RefY|adj3" },
+ { "downArrowCallout_3", "na|na|RefY|adj4" },
+ { "ellipseRibbon_0", "na|na|RefY|adj1" },
+ { "ellipseRibbon_1", "RefX|adj2|na|na" },
+ { "ellipseRibbon_2", "na|na|RefY|adj3" },
+ { "ellipseRibbon2_0", "na|na|RefY|adj1" },
+ { "ellipseRibbon2_1", "RefX|adj2|na|na" },
+ { "ellipseRibbon2_2", "na|na|RefY|adj3" },
+ { "foldedCorner_0", "RefX|adj|na|na" },
+ { "frame_0", "RefX|adj1|na|na" },
+ { "gear6_0", "na|na|RefY|adj1" },
+ { "gear6_1", "RefX|adj2|na|na" },
+ { "gear9_0", "na|na|RefY|adj1" },
+ { "gear9_1", "RefX|adj2|na|na" },
+ { "halfFrame_0", "na|na|RefY|adj1" },
+ { "halfFrame_1", "RefX|adj2|na|na" },
+ { "hexagon_0", "RefX|adj|na|na" },
+ { "homePlate_0", "RefX|adj|na|na" },
+ { "horizontalScroll_0", "RefX|adj|na|na" },
+ { "leftArrow_0", "na|na|RefY|adj1" },
+ { "leftArrow_1", "RefX|adj2|na|na" },
+ { "leftArrowCallout_0", "na|na|RefY|adj1" },
+ { "leftArrowCallout_1", "na|na|RefY|adj2" },
+ { "leftArrowCallout_2", "RefX|adj3|na|na" },
+ { "leftArrowCallout_3", "RefX|adj4|na|na" },
+ { "leftBrace_0", "na|na|RefY|adj1" },
+ { "leftBrace_1", "na|na|RefY|adj2" },
+ { "leftBracket_0", "na|na|RefY|adj" },
+ { "leftCircularArrow_0", "na|na|RefAngle|adj2" },
+ { "leftCircularArrow_1", "na|na|RefAngle|adj4" },
+ { "leftCircularArrow_2", "RefR|adj1|RefAngle|adj3" },
+ { "leftCircularArrow_3", "RefR|adj5|na|na" },
+ { "leftRightArrow_0", "na|na|RefY|adj1" },
+ { "leftRightArrow_1", "RefX|adj2|na|na" },
+ { "leftRightArrowCallout_0", "na|na|RefY|adj1" },
+ { "leftRightArrowCallout_1", "na|na|RefY|adj2" },
+ { "leftRightArrowCallout_2", "RefX|adj3|na|na" },
+ { "leftRightArrowCallout_3", "RefX|adj4|na|na" },
+ { "leftRightCircularArrow_0", "na|na|RefAngle|adj2" },
+ { "leftRightCircularArrow_1", "na|na|RefAngle|adj4" },
+ { "leftRightCircularArrow_2", "RefR|adj1|RefAngle|adj3" },
+ { "leftRightCircularArrow_3", "RefR|adj5|na|na" },
+ { "leftRightRibbon_0", "na|na|RefY|adj1" },
+ { "leftRightRibbon_1", "RefX|adj2|na|na" },
+ { "leftRightRibbon_2", "na|na|RefY|adj3" },
+ { "leftRightUpArrow_0", "RefX|adj1|na|na" },
+ { "leftRightUpArrow_1", "RefX|adj2|na|na" },
+ { "leftRightUpArrow_2", "na|na|RefY|adj3" },
+ { "leftUpArrow_0", "na|na|RefY|adj1" },
+ { "leftUpArrow_1", "RefX|adj2|na|na" },
+ { "leftUpArrow_2", "na|na|RefY|adj3" },
+ { "mathDivide_0", "na|na|RefY|adj1" },
+ { "mathDivide_1", "na|na|RefY|adj2" },
+ { "mathDivide_2", "RefX|adj3|na|na" },
+ { "mathEqual_0", "na|na|RefY|adj1" },
+ { "mathEqual_1", "na|na|RefY|adj2" },
+ { "mathMinus_0", "na|na|RefY|adj1" },
+ { "mathMultiply_0", "na|na|RefY|adj1" },
+ { "mathNotEqual_0", "na|na|RefY|adj1" },
+ { "mathNotEqual_1", "na|na|RefAngle|adj2" },
+ { "mathNotEqual_2", "na|na|RefY|adj3" },
+ { "mathPlus_0", "na|na|RefY|adj1" },
+ { "moon_0", "RefX|adj|na|na" },
+ { "nonIsoscelesTrapezoid_0", "RefX|adj1|na|na" },
+ { "nonIsoscelesTrapezoid_1", "RefX|adj2|na|na" },
+ { "noSmoking_0", "RefR|adj|na|na" },
+ { "notchedRightArrow_0", "na|na|RefY|adj1" },
+ { "notchedRightArrow_1", "RefX|adj2|na|na" },
+ { "octagon_0", "RefX|adj|na|na" },
+ { "parallelogram_0", "RefX|adj|na|na" },
+ { "pie_0", "na|na|RefAngle|adj1" },
+ { "pie_1", "na|na|RefAngle|adj2" },
+ { "plaque_0", "RefX|adj|na|na" },
+ { "plus_0", "RefX|adj|na|na" },
+ { "quadArrow_0", "RefX|adj1|na|na" },
+ { "quadArrow_1", "RefX|adj2|na|na" },
+ { "quadArrow_2", "na|na|RefY|adj3" },
+ { "quadArrowCallout_0", "RefX|adj1|na|na" },
+ { "quadArrowCallout_1", "RefX|adj2|na|na" },
+ { "quadArrowCallout_2", "na|na|RefY|adj3" },
+ { "quadArrowCallout_3", "na|na|RefY|adj4" },
+ { "ribbon_0", "na|na|RefY|adj1" },
+ { "ribbon_1", "RefX|adj2|na|na" },
+ { "ribbon2_0", "na|na|RefY|adj1" },
+ { "ribbon2_1", "RefX|adj2|na|na" },
+ { "rightArrow_0", "na|na|RefY|adj1" },
+ { "rightArrow_1", "RefX|adj2|na|na" },
+ { "rightArrowCallout_0", "na|na|RefY|adj1" },
+ { "rightArrowCallout_1", "na|na|RefY|adj2" },
+ { "rightArrowCallout_2", "RefX|adj3|na|na" },
+ { "rightArrowCallout_3", "RefX|adj4|na|na" },
+ { "rightBrace_0", "na|na|RefY|adj1" },
+ { "rightBrace_1", "na|na|RefY|adj2" },
+ { "rightBracket_0", "na|na|RefY|adj" },
+ { "round1Rect_0", "RefX|adj|na|na" },
+ { "round2DiagRect_0", "RefX|adj1|na|na" },
+ { "round2DiagRect_1", "RefX|adj2|na|na" },
+ { "round2SameRect_0", "RefX|adj1|na|na" },
+ { "round2SameRect_1", "RefX|adj2|na|na" },
+ { "roundRect_0", "RefX|adj|na|na" },
+ { "smileyFace_0", "na|na|RefY|adj" },
+ { "snip1Rect_0", "RefX|adj|na|na" },
+ { "snip2DiagRect_0", "RefX|adj1|na|na" },
+ { "snip2DiagRect_1", "RefX|adj2|na|na" },
+ { "snip2SameRect_0", "RefX|adj1|na|na" },
+ { "snip2SameRect_1", "RefX|adj2|na|na" },
+ { "snipRoundRect_0", "RefX|adj1|na|na" },
+ { "snipRoundRect_1", "RefX|adj2|na|na" },
+ { "star10_0", "na|na|RefY|adj" },
+ { "star12_0", "na|na|RefY|adj" },
+ { "star16_0", "na|na|RefY|adj" },
+ { "star24_0", "na|na|RefY|adj" },
+ { "star32_0", "na|na|RefY|adj" },
+ { "star4_0", "na|na|RefY|adj" },
+ { "star5_0", "na|na|RefY|adj" },
+ { "star6_0", "na|na|RefY|adj" },
+ { "star7_0", "na|na|RefY|adj" },
+ { "star8_0", "na|na|RefY|adj" },
+ { "stripedRightArrow_0", "na|na|RefY|adj1" },
+ { "stripedRightArrow_1", "RefX|adj2|na|na" },
+ { "sun_0", "RefX|adj|na|na" },
+ { "swooshArrow_0", "na|na|RefY|adj1" },
+ { "swooshArrow_1", "RefX|adj2|na|na" },
+ { "teardrop_0", "RefX|adj|na|na" },
+ { "trapezoid_0", "RefX|adj|na|na" },
+ { "triangle_0", "RefX|adj|na|na" },
+ { "upArrowCallout_0", "RefX|adj1|na|na" },
+ { "upArrowCallout_1", "RefX|adj2|na|na" },
+ { "upArrowCallout_2", "na|na|RefY|adj3" },
+ { "upArrowCallout_3", "na|na|RefY|adj4" },
+ { "upDownArrow_0", "RefX|adj1|na|na" },
+ { "upDownArrow_1", "na|na|RefY|adj2" },
+ { "upArrow_0", "RefX|adj1|na|na" },
+ { "upArrow_1", "na|na|RefY|adj2" },
+ { "upDownArrowCallout_0", "RefX|adj1|na|na" },
+ { "upDownArrowCallout_1", "RefX|adj2|na|na" },
+ { "upDownArrowCallout_2", "na|na|RefY|adj3" },
+ { "upDownArrowCallout_3", "na|na|RefY|adj4" },
+ { "uturnArrow_0", "RefX|adj1|na|na" },
+ { "uturnArrow_1", "RefX|adj2|na|na" },
+ { "uturnArrow_2", "na|na|RefY|adj3" },
+ { "uturnArrow_3", "RefX|adj4|na|na" },
+ { "uturnArrow_4", "na|na|RefY|adj5" },
+ { "verticalScroll_0", "na|na|RefY|adj" },
+ { "wave_0", "na|na|RefY|adj1" },
+ { "wave_1", "RefX|adj2|na|na" },
+ { "wedgeEllipseCallout_0", "RefX|adj1|RefY|adj2" },
+ { "wedgeRectCallout_0", "RefX|adj1|RefY|adj2" },
+ { "wedgeRoundRectCallout_0", "RefX|adj1|RefY|adj2" } };
+}
+
+static sal_Int32 lcl_getAdjIndexFromToken(const sal_Int32 nTokenPos, std::u16string_view rMapValue)
+{
+ std::u16string_view sAdjRef = o3tl::getToken(rMapValue, nTokenPos, '|');
+ std::u16string_view sNumber; // number part from "adj1", "adj2" etc.
+ if (o3tl::starts_with(sAdjRef, u"adj", &sNumber))
+ {
+ if (sNumber.empty() || sNumber == u"1")
+ return 0;
+ else
+ return o3tl::toInt32(sNumber) - 1;
+ }
+ else
+ return -1;
+}
+
+void PresetOOXHandleAdj::GetOOXHandleAdjRelation(
+ std::u16string_view sFullOOXShapeName, const sal_Int32 nHandleIndex, OUString& rFirstRefType,
+ sal_Int32& rFirstAdjValueIndex, OUString& rSecondRefType, sal_Int32& rSecondAdjValueIndex)
+{
+ static const HandleAdjRelHashMap s_HashMap = []() {
+ HandleAdjRelHashMap aH;
+ aH.reserve(std::size(aHandleAdjRelArray));
+ for (const auto& item : aHandleAdjRelArray)
+ aH.emplace(OUString::createFromAscii(item.sShape_Handle),
+ OUString::createFromAscii(item.sAdjReferences));
+ return aH;
+ }();
+
+ std::u16string_view sKey;
+ OUString sValue;
+ rFirstRefType = "na";
+ rFirstAdjValueIndex = -1;
+ rSecondRefType = "na";
+ rSecondAdjValueIndex = -1;
+ if (o3tl::starts_with(sFullOOXShapeName, u"ooxml-", &sKey))
+ {
+ HandleAdjRelHashMap::const_iterator aHashIter(
+ s_HashMap.find(OUString::Concat(sKey) + "_" + OUString::number(nHandleIndex)));
+ if (aHashIter != s_HashMap.end())
+ sValue = (*aHashIter).second;
+ else
+ return;
+ }
+ else
+ return;
+
+ rFirstRefType = sValue.getToken(0, '|');
+ rFirstAdjValueIndex = lcl_getAdjIndexFromToken(1, sValue);
+ rSecondRefType = sValue.getToken(2, '|');
+ rSecondAdjValueIndex = lcl_getAdjIndexFromToken(3, sValue);
+ return;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab cinoptions=b1,g0,N-s cinkeys+=0=break: */
diff --git a/svx/source/svdraw/presetooxhandleadjustmentrelations.hxx b/svx/source/svdraw/presetooxhandleadjustmentrelations.hxx
new file mode 100644
index 000000000..b9404e598
--- /dev/null
+++ b/svx/source/svdraw/presetooxhandleadjustmentrelations.hxx
@@ -0,0 +1,34 @@
+/* -*- 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_SVX_SOURCE_SVDRAW_PRESETOOXHANDLEADJUSTMENTRELATIONS_HXX
+#define INCLUDED_SVX_SOURCE_SVDRAW_PRESETOOXHANDLEADJUSTMENTRELATIONS_HXX
+
+#include <sal/config.h>
+
+#include <string_view>
+
+#include <rtl/ustring.hxx>
+
+namespace PresetOOXHandleAdj
+{
+/* This method is used in SdrObjCustomShape::MergeDefaultAttributes() */
+void GetOOXHandleAdjRelation(
+ std::u16string_view sFullOOXShapeName, /* e.g. "ooxml-circularArrow" */
+ const sal_Int32 nHandleIndex, /* index in sequence from property "Handles" */
+ OUString& rFirstRefType, /* Propertyname, same as by pptx import, e.g. "RefX" */
+ sal_Int32& rFirstAdjValueIndex, /* index in sequence from property "AdjustmentValues" */
+ OUString& rSecondRefType, /* Propertyname, same as by pptx import, e.g. "RefY" */
+ sal_Int32& rSecondAdjValueIndex /* index in sequence from property "AdjustmentValues" */
+);
+}
+
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab cinoptions=b1,g0,N-s cinkeys+=0=break: */
diff --git a/svx/source/svdraw/sdrhittesthelper.cxx b/svx/source/svdraw/sdrhittesthelper.cxx
new file mode 100644
index 000000000..62ebde956
--- /dev/null
+++ b/svx/source/svdraw/sdrhittesthelper.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 <svx/sdrhittesthelper.hxx>
+#include <svx/obj3d.hxx>
+#include <svx/helperhittest3d.hxx>
+#include <svx/svdpage.hxx>
+#include <svx/sdrpagewindow.hxx>
+#include <svx/sdr/contact/viewobjectcontact.hxx>
+#include <svx/sdr/contact/displayinfo.hxx>
+#include <svx/sdr/contact/objectcontact.hxx>
+#include <drawinglayer/processor2d/hittestprocessor2d.hxx>
+#include <svx/svdpagv.hxx>
+#include <svx/sdr/contact/viewcontact.hxx>
+
+
+// #i101872# new Object HitTest as View-tooling
+
+SdrObject* SdrObjectPrimitiveHit(
+ const SdrObject& rObject,
+ const Point& rPnt,
+ sal_uInt16 nTol,
+ const SdrPageView& rSdrPageView,
+ const SdrLayerIDSet* pVisiLayer,
+ bool bTextOnly,
+ drawinglayer::primitive2d::Primitive2DContainer* pHitContainer)
+{
+ SdrObject* pResult = nullptr;
+
+ if(rObject.GetSubList() && rObject.GetSubList()->GetObjCount())
+ {
+ // group or scene with content. Single 3D objects also have a
+ // true == rObject.GetSubList(), but no content
+ pResult = SdrObjListPrimitiveHit(*rObject.GetSubList(), rPnt, nTol, rSdrPageView, pVisiLayer, bTextOnly);
+ }
+ else
+ {
+ if( rObject.IsVisible() && (!pVisiLayer || pVisiLayer->IsSet(rObject.GetLayer())))
+ {
+ // single object, 3d object, empty scene or empty group. Check if
+ // it's a single 3D object
+ const E3dCompoundObject* pE3dCompoundObject = dynamic_cast< const E3dCompoundObject* >(&rObject);
+
+ if(pE3dCompoundObject)
+ {
+ const basegfx::B2DPoint aHitPosition(rPnt.X(), rPnt.Y());
+
+ if(checkHitSingle3DObject(aHitPosition, *pE3dCompoundObject))
+ {
+ pResult = const_cast< E3dCompoundObject* >(pE3dCompoundObject);
+ }
+ }
+ else
+ {
+ // not a single 3D object; Check in first PageWindow using primitives (only SC
+ // with split views uses multiple PageWindows nowadays)
+ if(rSdrPageView.PageWindowCount())
+ {
+ const double fLogicTolerance(nTol);
+ const basegfx::B2DPoint aHitPosition(rPnt.X(), rPnt.Y());
+ const sdr::contact::ViewObjectContact& rVOC = rObject.GetViewContact().GetViewObjectContact(
+ rSdrPageView.GetPageWindow(0)->GetObjectContact());
+
+ if(ViewObjectContactPrimitiveHit(rVOC, aHitPosition, fLogicTolerance, bTextOnly, pHitContainer))
+ {
+ pResult = const_cast< SdrObject* >(&rObject);
+ }
+ }
+ }
+ }
+ }
+
+ return pResult;
+}
+
+
+SdrObject* SdrObjListPrimitiveHit(
+ const SdrObjList& rList,
+ const Point& rPnt,
+ sal_uInt16 nTol,
+ const SdrPageView& rSdrPageView,
+ const SdrLayerIDSet* pVisiLayer,
+ bool bTextOnly)
+{
+ size_t nObjNum(rList.GetObjCount());
+ SdrObject* pRetval = nullptr;
+
+ while(!pRetval && nObjNum > 0)
+ {
+ nObjNum--;
+ SdrObject* pObj = rList.GetObj(nObjNum);
+
+ pRetval = SdrObjectPrimitiveHit(*pObj, rPnt, nTol, rSdrPageView, pVisiLayer, bTextOnly);
+ }
+
+ return pRetval;
+}
+
+
+bool ViewObjectContactPrimitiveHit(
+ const sdr::contact::ViewObjectContact& rVOC,
+ const basegfx::B2DPoint& rHitPosition,
+ double fLogicHitTolerance,
+ bool bTextOnly,
+ drawinglayer::primitive2d::Primitive2DContainer* pHitContainer)
+{
+ basegfx::B2DRange aObjectRange(rVOC.getObjectRange());
+
+ if(!aObjectRange.isEmpty())
+ {
+ // first do a rough B2DRange based HitTest; do not forget to
+ // include the HitTolerance if given
+ if(basegfx::fTools::more(fLogicHitTolerance, 0.0))
+ {
+ aObjectRange.grow(fLogicHitTolerance);
+ }
+
+ if(aObjectRange.isInside(rHitPosition))
+ {
+ // get primitive sequence
+ sdr::contact::DisplayInfo aDisplayInfo;
+ // have to make a copy of this container here, because it might be changed underneath us
+ const drawinglayer::primitive2d::Primitive2DContainer aSequence(rVOC.getPrimitive2DSequence(aDisplayInfo));
+
+ if(!aSequence.empty())
+ {
+ // create a HitTest processor
+ const drawinglayer::geometry::ViewInformation2D& rViewInformation2D = rVOC.GetObjectContact().getViewInformation2D();
+ drawinglayer::processor2d::HitTestProcessor2D aHitTestProcessor2D(
+ rViewInformation2D,
+ rHitPosition,
+ fLogicHitTolerance,
+ bTextOnly);
+
+ // ask for HitStack
+ aHitTestProcessor2D.collectHitStack(pHitContainer != nullptr);
+
+ // feed it with the primitives
+ aHitTestProcessor2D.process(aSequence);
+
+ // deliver result
+ if (aHitTestProcessor2D.getHit())
+ {
+ if (pHitContainer)
+ {
+ // fetch HitStack primitives if requested
+ *pHitContainer = aHitTestProcessor2D.getHitStack();
+ }
+
+ return true;
+ }
+ }
+ }
+ }
+
+ return false;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/svdraw/sdrmasterpagedescriptor.cxx b/svx/source/svdraw/sdrmasterpagedescriptor.cxx
new file mode 100644
index 000000000..1d0843918
--- /dev/null
+++ b/svx/source/svdraw/sdrmasterpagedescriptor.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 <svx/sdrmasterpagedescriptor.hxx>
+#include <sdr/contact/viewcontactofmasterpagedescriptor.hxx>
+#include <svx/svdpage.hxx>
+#include <svx/xdef.hxx>
+#include <svx/xfillit0.hxx>
+#include <svl/itemset.hxx>
+
+using namespace com::sun::star;
+
+namespace sdr
+{
+ MasterPageDescriptor::MasterPageDescriptor(SdrPage& aOwnerPage, SdrPage& aUsedPage)
+ : maOwnerPage(aOwnerPage),
+ maUsedPage(aUsedPage)
+ {
+ // all layers visible
+ maVisibleLayers.SetAll();
+
+ // register at used page
+ maUsedPage.AddPageUser(*this);
+ }
+
+ MasterPageDescriptor::~MasterPageDescriptor()
+ {
+ // de-register at used page
+ maUsedPage.RemovePageUser(*this);
+
+ mpViewContact.reset();
+ }
+
+ // ViewContact part
+ sdr::contact::ViewContact& MasterPageDescriptor::GetViewContact() const
+ {
+ if(!mpViewContact)
+ {
+ mpViewContact.reset(
+ new sdr::contact::ViewContactOfMasterPageDescriptor(*const_cast< MasterPageDescriptor* >(this)) );
+ }
+
+ return *mpViewContact;
+ }
+
+ // this method is called from the destructor of the referenced page.
+ // do all necessary action to forget the page. It is not necessary to call
+ // RemovePageUser(), that is done from the destructor.
+ void MasterPageDescriptor::PageInDestruction(const SdrPage& /*rPage*/)
+ {
+ maOwnerPage.TRG_ClearMasterPage();
+ }
+
+ void MasterPageDescriptor::SetVisibleLayers(const SdrLayerIDSet& rNew)
+ {
+ if(rNew != maVisibleLayers)
+ {
+ maVisibleLayers = rNew;
+ GetViewContact().ActionChanged();
+ }
+ }
+
+
+ const SdrPageProperties* MasterPageDescriptor::getCorrectSdrPageProperties() const
+ {
+ const SdrPage* pCorrectPage = &GetOwnerPage();
+ const SdrPageProperties* pCorrectProperties = &pCorrectPage->getSdrPageProperties();
+
+ if(drawing::FillStyle_NONE == pCorrectProperties->GetItemSet().Get(XATTR_FILLSTYLE).GetValue())
+ {
+ pCorrectPage = &GetUsedPage();
+ pCorrectProperties = &pCorrectPage->getSdrPageProperties();
+ }
+
+ if(pCorrectPage->IsMasterPage() && !pCorrectProperties->GetStyleSheet())
+ {
+ // #i110846# Suppress SdrPage FillStyle for MasterPages without StyleSheets,
+ // else the PoolDefault (XFILL_COLOR and Blue8) will be used. Normally, all
+ // MasterPages should have a StyleSheet exactly for this reason, but historically
+ // e.g. the Notes MasterPage has no StyleSheet set (and there maybe others).
+ pCorrectProperties = nullptr;
+ }
+
+ return pCorrectProperties;
+ }
+} // end of namespace sdr
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/svdraw/sdrpagewindow.cxx b/svx/source/svdraw/sdrpagewindow.cxx
new file mode 100644
index 000000000..01be77135
--- /dev/null
+++ b/svx/source/svdraw/sdrpagewindow.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 <svx/sdrpagewindow.hxx>
+#include <com/sun/star/awt/XWindow.hpp>
+#include <com/sun/star/awt/PosSize.hpp>
+#include <com/sun/star/lang/XMultiServiceFactory.hpp>
+#include <comphelper/lok.hxx>
+#include <comphelper/processfactory.hxx>
+#include <toolkit/helper/vclunohelper.hxx>
+#include <svx/svdview.hxx>
+#include <svx/svdpagv.hxx>
+#include <svx/sdrpaintwindow.hxx>
+#include <svx/sdr/contact/objectcontactofpageview.hxx>
+#include <svx/sdr/contact/displayinfo.hxx>
+#include <svx/fmview.hxx>
+#include <sfx2/lokhelper.hxx>
+#include <svtools/optionsdrawinglayer.hxx>
+#include <tools/debug.hxx>
+#include <vcl/window.hxx>
+
+using namespace ::com::sun::star;
+
+struct SdrPageWindow::Impl
+{
+ // #110094# ObjectContact section
+ mutable sdr::contact::ObjectContact* mpObjectContact;
+
+ // the SdrPageView this window belongs to
+ SdrPageView& mrPageView;
+
+ // the PaintWindow to paint on. Here is access to OutDev etc.
+ // #i72752# change to pointer to allow patcing it in DrawLayer() if necessary
+ SdrPaintWindow* mpPaintWindow;
+ SdrPaintWindow* mpOriginalPaintWindow;
+
+ // UNO stuff for xControls
+ uno::Reference<awt::XControlContainer> mxControlContainer;
+
+ Impl( SdrPageView& rPageView, SdrPaintWindow& rPaintWindow ) :
+ mpObjectContact(nullptr),
+ mrPageView(rPageView),
+ mpPaintWindow(&rPaintWindow),
+ mpOriginalPaintWindow(nullptr)
+ {
+ }
+};
+
+
+uno::Reference<awt::XControlContainer> const & SdrPageWindow::GetControlContainer( bool _bCreateIfNecessary ) const
+{
+ if (!mpImpl->mxControlContainer.is() && _bCreateIfNecessary)
+ {
+ SdrView& rView = GetPageView().GetView();
+
+ const SdrPaintWindow& rPaintWindow( GetOriginalPaintWindow() ? *GetOriginalPaintWindow() : GetPaintWindow() );
+ if ( rPaintWindow.OutputToWindow() && !rView.IsPrintPreview() )
+ {
+ vcl::Window* pWindow = rPaintWindow.GetOutputDevice().GetOwnerWindow();
+ const_cast< SdrPageWindow* >( this )->mpImpl->mxControlContainer = VCLUnoHelper::CreateControlContainer( pWindow );
+
+ // #100394# xC->setVisible triggers window->Show() and this has
+ // problems when the view is not completely constructed which may
+ // happen when loading. This leads to accessibility broadcasts which
+ // throw asserts due to the not finished view. All this chain can be avoided
+ // since xC->setVisible is here called only for the side effect in
+ // UnoControlContainer::setVisible(...) which calls createPeer(...).
+ // This will now be called directly from here.
+
+ uno::Reference< awt::XControl > xControl(mpImpl->mxControlContainer, uno::UNO_QUERY);
+ if(xControl.is())
+ {
+ uno::Reference< uno::XInterface > xContext = xControl->getContext();
+ if(!xContext.is())
+ {
+ xControl->createPeer( uno::Reference<awt::XToolkit>(), uno::Reference<awt::XWindowPeer>() );
+ }
+ }
+ }
+ else
+ {
+ // Printer and VirtualDevice, or rather: no OutDev
+ uno::Reference< lang::XMultiServiceFactory > xFactory( ::comphelper::getProcessServiceFactory() );
+ const_cast< SdrPageWindow* >( this )->mpImpl->mxControlContainer.set(xFactory->createInstance("com.sun.star.awt.UnoControlContainer"), uno::UNO_QUERY);
+ uno::Reference< awt::XControlModel > xModel(xFactory->createInstance("com.sun.star.awt.UnoControlContainerModel"), uno::UNO_QUERY);
+ uno::Reference< awt::XControl > xControl(mpImpl->mxControlContainer, uno::UNO_QUERY);
+ if (xControl.is())
+ xControl->setModel(xModel);
+
+ OutputDevice& rOutDev = rPaintWindow.GetOutputDevice();
+ Point aPosPix = rOutDev.GetMapMode().GetOrigin();
+ Size aSizePix = rOutDev.GetOutputSizePixel();
+
+ uno::Reference< awt::XWindow > xContComp(mpImpl->mxControlContainer, uno::UNO_QUERY);
+ if( xContComp.is() )
+ xContComp->setPosSize(aPosPix.X(), aPosPix.Y(), aSizePix.Width(), aSizePix.Height(), awt::PosSize::POSSIZE);
+ }
+
+ FmFormView* pViewAsFormView = dynamic_cast< FmFormView* >( &rView );
+ if ( pViewAsFormView )
+ pViewAsFormView->InsertControlContainer(mpImpl->mxControlContainer);
+ }
+ return mpImpl->mxControlContainer;
+}
+
+SdrPageWindow::SdrPageWindow(SdrPageView& rPageView, SdrPaintWindow& rPaintWindow) :
+ mpImpl(new Impl(rPageView, rPaintWindow))
+{
+}
+
+SdrPageWindow::~SdrPageWindow()
+{
+ // #i26631#
+ ResetObjectContact();
+
+ if (!mpImpl->mxControlContainer.is())
+ return;
+
+ auto & rView = static_cast<SdrPaintView &>(GetPageView().GetView());
+
+ // notify derived views
+ FmFormView* pViewAsFormView = dynamic_cast< FmFormView* >( &rView );
+ if ( pViewAsFormView )
+ pViewAsFormView->RemoveControlContainer(mpImpl->mxControlContainer);
+
+ // dispose the control container
+ uno::Reference< lang::XComponent > xComponent(mpImpl->mxControlContainer, uno::UNO_QUERY);
+ xComponent->dispose();
+}
+
+SdrPageView& SdrPageWindow::GetPageView() const
+{
+ return mpImpl->mrPageView;
+}
+
+SdrPaintWindow& SdrPageWindow::GetPaintWindow() const
+{
+ return *mpImpl->mpPaintWindow;
+}
+
+const SdrPaintWindow* SdrPageWindow::GetOriginalPaintWindow() const
+{
+ return mpImpl->mpOriginalPaintWindow;
+}
+
+// OVERLAY MANAGER
+rtl::Reference< sdr::overlay::OverlayManager > const & SdrPageWindow::GetOverlayManager() const
+{
+ return GetPaintWindow().GetOverlayManager();
+}
+
+SdrPaintWindow* SdrPageWindow::patchPaintWindow(SdrPaintWindow& rPaintWindow)
+{
+ if (!mpImpl)
+ return nullptr;
+
+ if (!mpImpl->mpOriginalPaintWindow)
+ {
+ // first patch
+ mpImpl->mpOriginalPaintWindow = mpImpl->mpPaintWindow;
+ mpImpl->mpPaintWindow = &rPaintWindow;
+ mpImpl->mpOriginalPaintWindow->setPatched(&rPaintWindow);
+ return mpImpl->mpOriginalPaintWindow;
+ }
+ else
+ {
+ // second or more patch
+ auto pPreviousPaintWindow = mpImpl->mpPaintWindow;
+ mpImpl->mpPaintWindow = &rPaintWindow;
+ mpImpl->mpOriginalPaintWindow->setPatched(&rPaintWindow);
+ return pPreviousPaintWindow;
+ }
+}
+
+void SdrPageWindow::unpatchPaintWindow(SdrPaintWindow* pPreviousPaintWindow)
+{
+ if (pPreviousPaintWindow == mpImpl->mpOriginalPaintWindow)
+ {
+ // first patch
+ mpImpl->mpPaintWindow = mpImpl->mpOriginalPaintWindow;
+ mpImpl->mpOriginalPaintWindow->setPatched(nullptr);
+ mpImpl->mpOriginalPaintWindow = nullptr;
+ }
+ else
+ {
+ // second or more patch
+ mpImpl->mpPaintWindow = pPreviousPaintWindow;
+ mpImpl->mpOriginalPaintWindow->setPatched(pPreviousPaintWindow);
+ }
+}
+
+void SdrPageWindow::PrePaint()
+{
+ // give OC the chance to do ProcessDisplay preparations
+ if(HasObjectContact())
+ {
+ GetObjectContact().PrepareProcessDisplay();
+ }
+}
+
+void SdrPageWindow::PrepareRedraw(const vcl::Region& rReg)
+{
+ // give OC the chance to do ProcessDisplay preparations
+ if(HasObjectContact())
+ {
+ GetObjectContact().PrepareProcessDisplay();
+ }
+
+ // if necessary, remember changed RedrawArea at PaintWindow for usage with
+ // overlay and PreRenderDevice stuff
+ GetPaintWindow().SetRedrawRegion(rReg);
+}
+
+
+// clip test
+#ifdef CLIPPER_TEST
+#include <svx/svdopath.hxx>
+#include <basegfx/polygon/b2dpolygon.hxx>
+#include <tools/helpers.hxx>
+#include <basegfx/polygon/b2dpolygoncutandtouch.hxx>
+#include <basegfx/polygon/b2dpolypolygontools.hxx>
+#include <basegfx/polygon/b2dpolygontools.hxx>
+#include <basegfx/polygon/b2dpolygonclipper.hxx>
+
+// for ::std::sort
+#include <algorithm>
+
+namespace
+{
+ void impPaintStrokePolygon(const basegfx::B2DPolygon& rCandidate, OutputDevice& rOutDev, Color aColor)
+ {
+ basegfx::B2DPolygon aCandidate(rCandidate);
+
+ if(aCandidate.areControlPointsUsed())
+ {
+ aCandidate = basegfx::utils::adaptiveSubdivideByAngle(rCandidate);
+ }
+
+ if(aCandidate.count())
+ {
+ const sal_uInt32 nLoopCount(aCandidate.isClosed() ? aCandidate.count() : aCandidate.count() - 1);
+ rOutDev.SetFillColor();
+ rOutDev.SetLineColor(aColor);
+
+ for(sal_uInt32 a(0); a < nLoopCount; a++)
+ {
+ const basegfx::B2DPoint aBStart(aCandidate.getB2DPoint(a));
+ const basegfx::B2DPoint aBEnd(aCandidate.getB2DPoint((a + 1) % aCandidate.count()));
+ const Point aStart(FRound(aBStart.getX()), FRound(aBStart.getY()));
+ const Point aEnd(FRound(aBEnd.getX()), FRound(aBEnd.getY()));
+ rOutDev.DrawLine(aStart, aEnd);
+ }
+ }
+ }
+
+ void impTryTest(const SdrPageView& rPageView, OutputDevice& rOutDev)
+ {
+ if(rPageView.GetPage() && rPageView.GetPage()->GetObjCount() >= 2)
+ {
+ SdrPage* pPage = rPageView.GetPage();
+ SdrObject* pObjA = pPage->GetObj(0);
+
+ if(dynamic_cast<const SdrPathObj*>( pObjA))
+ {
+ basegfx::B2DPolyPolygon aPolyA(pObjA->GetPathPoly());
+ aPolyA = basegfx::utils::correctOrientations(aPolyA);
+
+ basegfx::B2DPolyPolygon aPolyB;
+
+ for(sal_uInt32 a(1); a < rPageView.GetPage()->GetObjCount(); a++)
+ {
+ SdrObject* pObjB = pPage->GetObj(a);
+
+ if(dynamic_cast<const SdrPathObj*>( pObjB))
+ {
+ basegfx::B2DPolyPolygon aCandidate(pObjB->GetPathPoly());
+ aCandidate = basegfx::utils::correctOrientations(aCandidate);
+ aPolyB.append(aCandidate);
+ }
+ }
+
+ if(aPolyA.count() && aPolyA.isClosed() && aPolyB.count())
+ {
+ // poly A is the clipregion, clip poly b against it. Algo depends on
+ // poly b being closed.
+ basegfx::B2DPolyPolygon aResult(basegfx::utils::clipPolyPolygonOnPolyPolygon(aPolyB, aPolyA));
+
+ for(auto const& rPolygon : aResult)
+ {
+ int nR = comphelper::rng::uniform_int_distribution(0, 254);
+ int nG = comphelper::rng::uniform_int_distribution(0, 254);
+ int nB = comphelper::rng::uniform_int_distribution(0, 254);
+ Color aColor(nR, nG, nB);
+ impPaintStrokePolygon(rPolygon, rOutDev, aColor);
+ }
+ }
+ }
+ }
+ }
+} // end of anonymous namespace
+#endif // CLIPPER_TEST
+
+
+void SdrPageWindow::RedrawAll( sdr::contact::ViewObjectContactRedirector* pRedirector )
+{
+ // set Redirector
+ GetObjectContact().SetViewObjectContactRedirector(pRedirector);
+
+ // set PaintingPageView
+ const SdrView& rView = mpImpl->mrPageView.GetView();
+ SdrModel& rModel = *(rView.GetModel());
+
+ // get to be processed layers
+ const bool bPrinter(GetPaintWindow().OutputToPrinter());
+ SdrLayerIDSet aProcessLayers = bPrinter ? mpImpl->mrPageView.GetPrintableLayers() : mpImpl->mrPageView.GetVisibleLayers();
+
+ // create PaintInfoRec; use Rectangle only temporarily
+ const vcl::Region& rRegion = GetPaintWindow().GetRedrawRegion();
+
+ // create processing data
+ sdr::contact::DisplayInfo aDisplayInfo;
+
+ // Draw all layers. do NOT draw form layer from CompleteRedraw, this is done separately
+ // as a single layer paint
+ const SdrLayerAdmin& rLayerAdmin = rModel.GetLayerAdmin();
+ const SdrLayerID nControlLayerId = rLayerAdmin.GetLayerID(rLayerAdmin.GetControlLayerName());
+ aProcessLayers.Clear(nControlLayerId);
+
+ // still something to paint?
+ if(!aProcessLayers.IsEmpty())
+ {
+ aDisplayInfo.SetProcessLayers(aProcessLayers);
+
+ // Set region as redraw area
+ aDisplayInfo.SetRedrawArea(rRegion);
+
+ // Draw/Impress
+ aDisplayInfo.SetPageProcessingActive(rView.IsPagePaintingAllowed()); // #i72889#
+
+ // paint page
+ GetObjectContact().ProcessDisplay(aDisplayInfo);
+ }
+
+ // reset redirector
+ GetObjectContact().SetViewObjectContactRedirector(nullptr);
+
+ // LineClip test
+#ifdef CLIPPER_TEST
+ if(true)
+ {
+ impTryTest(GetPageView(), GetPaintWindow().GetOutputDevice());
+ }
+#endif // CLIPPER_TEST
+}
+
+void SdrPageWindow::RedrawLayer(const SdrLayerID* pId,
+ sdr::contact::ViewObjectContactRedirector* pRedirector,
+ basegfx::B2IRectangle const*const pPageFrame)
+{
+ // set redirector
+ GetObjectContact().SetViewObjectContactRedirector(pRedirector);
+
+ // set PaintingPageView
+ const SdrView& rView = mpImpl->mrPageView.GetView();
+ SdrModel& rModel = *(rView.GetModel());
+
+ // get the layers to process
+ const bool bPrinter(GetPaintWindow().OutputToPrinter());
+ SdrLayerIDSet aProcessLayers = bPrinter ? mpImpl->mrPageView.GetPrintableLayers() : mpImpl->mrPageView.GetVisibleLayers();
+
+ // is the given layer visible at all?
+ if(aProcessLayers.IsSet(*pId))
+ {
+ // find out if we are painting the ControlLayer
+ const SdrLayerAdmin& rLayerAdmin = rModel.GetLayerAdmin();
+ const SdrLayerID nControlLayerId = rLayerAdmin.GetLayerID(rLayerAdmin.GetControlLayerName());
+ const bool bControlLayerProcessingActive(nControlLayerId == *pId);
+
+ // create PaintInfoRec, use Rectangle only temporarily
+ const vcl::Region& rRegion = GetPaintWindow().GetRedrawRegion();
+
+ // create processing data
+ sdr::contact::DisplayInfo aDisplayInfo;
+
+ // is it the control layer? If Yes, set flag
+ aDisplayInfo.SetControlLayerProcessingActive(bControlLayerProcessingActive);
+
+ // Draw just the one given layer
+ aProcessLayers.ClearAll();
+ aProcessLayers.Set(*pId);
+
+ aDisplayInfo.SetProcessLayers(aProcessLayers);
+
+ // Set region as redraw area
+ aDisplayInfo.SetRedrawArea(rRegion);
+
+ // Writer or calc, coming from original RedrawOneLayer.
+ // #i72889# no page painting for layer painting
+ aDisplayInfo.SetPageProcessingActive(false);
+
+ if (pPageFrame) // Writer page frame for anchor based clipping
+ {
+ aDisplayInfo.SetWriterPageFrame(*pPageFrame);
+ }
+
+ // paint page
+ GetObjectContact().ProcessDisplay(aDisplayInfo);
+ }
+
+ // reset redirector
+ GetObjectContact().SetViewObjectContactRedirector(nullptr);
+}
+
+// Invalidate call, used from ObjectContact(OfPageView) in InvalidatePartOfView(...)
+void SdrPageWindow::InvalidatePageWindow(const basegfx::B2DRange& rRange)
+{
+ if (GetPageView().IsVisible() && GetPaintWindow().OutputToWindow())
+ {
+ OutputDevice& rWindow(GetPaintWindow().GetOutputDevice());
+ basegfx::B2DRange aDiscreteRange(rRange);
+ aDiscreteRange.transform(rWindow.GetViewTransformation());
+
+ if (SvtOptionsDrawinglayer::IsAntiAliasing())
+ {
+ // invalidate one discrete unit more under the assumption that AA
+ // needs one pixel more
+ aDiscreteRange.grow(1.0);
+ }
+
+ // If the shapes use negative X coordinates, make them positive before sending
+ // the invalidation rectangle.
+ bool bNegativeX = mpImpl->mrPageView.GetView().IsNegativeX();
+
+ const tools::Rectangle aVCLDiscreteRectangle(
+ static_cast<tools::Long>(bNegativeX ? std::max(0.0, ceil(-aDiscreteRange.getMaxX())) : floor(aDiscreteRange.getMinX())),
+ static_cast<tools::Long>(floor(aDiscreteRange.getMinY())),
+ static_cast<tools::Long>(bNegativeX ? std::max(0.0, floor(-aDiscreteRange.getMinX())) : ceil(aDiscreteRange.getMaxX())),
+ static_cast<tools::Long>(ceil(aDiscreteRange.getMaxY())));
+
+ const bool bWasMapModeEnabled(rWindow.IsMapModeEnabled());
+ rWindow.EnableMapMode(false);
+ GetPageView().GetView().InvalidateOneWin(rWindow, aVCLDiscreteRectangle);
+ rWindow.EnableMapMode(bWasMapModeEnabled);
+ }
+ else if (comphelper::LibreOfficeKit::isActive())
+ {
+ // we don't really have to have a paint window with LOK; OTOH we know
+ // that the drawinglayer units are 100ths of mm, so they are easy to
+ // convert to twips
+
+ // If the shapes use negative X coordinates, make them positive before sending
+ // the invalidation rectangle.
+ bool bNegativeX = mpImpl->mrPageView.GetView().IsNegativeX();
+ const tools::Rectangle aRect100thMM(
+ static_cast<tools::Long>(bNegativeX ? std::max(0.0, ceil(-rRange.getMaxX())) : floor(rRange.getMinX())),
+ static_cast<tools::Long>(floor(rRange.getMinY())),
+ static_cast<tools::Long>(bNegativeX ? std::max(0.0, floor(-rRange.getMinX())) : ceil(rRange.getMaxX())),
+ static_cast<tools::Long>(ceil(rRange.getMaxY())));
+
+ const tools::Rectangle aRectTwips = o3tl::convert(aRect100thMM, o3tl::Length::mm100, o3tl::Length::twip);
+
+ if (SfxViewShell* pViewShell = SfxViewShell::Current())
+ SfxLokHelper::notifyInvalidation(pViewShell, &aRectTwips);
+ }
+}
+
+// ObjectContact section
+const sdr::contact::ObjectContact& SdrPageWindow::GetObjectContact() const
+{
+ if (!mpImpl->mpObjectContact)
+ {
+ mpImpl->mpObjectContact = GetPageView().GetView().createViewSpecificObjectContact(
+ const_cast<SdrPageWindow&>(*this),
+ "svx::svdraw::SdrPageWindow mpObjectContact");
+ }
+
+ return *mpImpl->mpObjectContact;
+}
+
+sdr::contact::ObjectContact& SdrPageWindow::GetObjectContact()
+{
+ if (!mpImpl->mpObjectContact)
+ {
+ mpImpl->mpObjectContact = GetPageView().GetView().createViewSpecificObjectContact(
+ *this,
+ "svx::svdraw::SdrPageWindow mpObjectContact" );
+ }
+
+ return *mpImpl->mpObjectContact;
+}
+
+bool SdrPageWindow::HasObjectContact() const
+{
+ return mpImpl->mpObjectContact != nullptr;
+}
+
+// #i26631#
+void SdrPageWindow::ResetObjectContact()
+{
+ if (mpImpl->mpObjectContact)
+ {
+ delete mpImpl->mpObjectContact;
+ mpImpl->mpObjectContact = nullptr;
+ }
+}
+
+void SdrPageWindow::SetDesignMode( bool _bDesignMode ) const
+{
+ const sdr::contact::ObjectContactOfPageView* pOC = dynamic_cast< const sdr::contact::ObjectContactOfPageView* >( &GetObjectContact() );
+ DBG_ASSERT( pOC, "SdrPageWindow::SetDesignMode: invalid object contact!" );
+ if ( pOC )
+ pOC->SetUNOControlsDesignMode( _bDesignMode );
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/svdraw/sdrpaintwindow.cxx b/svx/source/svdraw/sdrpaintwindow.cxx
new file mode 100644
index 000000000..6c3b328ba
--- /dev/null
+++ b/svx/source/svdraw/sdrpaintwindow.cxx
@@ -0,0 +1,346 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * 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 <osl/diagnose.h>
+#include <svx/sdrpaintwindow.hxx>
+#include <sdr/overlay/overlaymanagerbuffered.hxx>
+#include <svx/svdpntv.hxx>
+#include <vcl/gdimtf.hxx>
+#include <vcl/svapp.hxx>
+#include <vcl/settings.hxx>
+#include <vcl/window.hxx>
+#include <svtools/optionsdrawinglayer.hxx>
+#include <set>
+#include <vector>
+
+namespace {
+
+//rhbz#1007697 do this in two loops, one to collect the candidates
+//and another to update them because updating a candidate can
+//trigger the candidate to be deleted, so asking for its
+//sibling after that is going to fail hard
+class CandidateMgr
+{
+ std::vector<VclPtr<vcl::Window> > m_aCandidates;
+ std::set<VclPtr<vcl::Window> > m_aDeletedCandidates;
+ DECL_LINK(WindowEventListener, VclWindowEvent&, void);
+public:
+ void PaintTransparentChildren(vcl::Window const & rWindow, tools::Rectangle const& rPixelRect);
+ ~CandidateMgr();
+};
+
+}
+
+IMPL_LINK(CandidateMgr, WindowEventListener, VclWindowEvent&, rEvent, void)
+{
+ vcl::Window* pWindow = rEvent.GetWindow();
+ if (rEvent.GetId() == VclEventId::ObjectDying)
+ {
+ m_aDeletedCandidates.insert(pWindow);
+ }
+}
+
+CandidateMgr::~CandidateMgr()
+{
+ for (VclPtr<vcl::Window>& pCandidate : m_aCandidates)
+ {
+ if (m_aDeletedCandidates.find(pCandidate) != m_aDeletedCandidates.end())
+ continue;
+ pCandidate->RemoveEventListener(LINK(this, CandidateMgr, WindowEventListener));
+ }
+}
+
+void PaintTransparentChildren(vcl::Window const & rWindow, tools::Rectangle const& rPixelRect)
+{
+ if (!rWindow.IsChildTransparentModeEnabled())
+ return;
+
+ CandidateMgr aManager;
+ aManager.PaintTransparentChildren(rWindow, rPixelRect);
+}
+
+void CandidateMgr::PaintTransparentChildren(vcl::Window const & rWindow, tools::Rectangle const& rPixelRect)
+{
+ vcl::Window * pCandidate = rWindow.GetWindow( GetWindowType::FirstChild );
+ while (pCandidate)
+ {
+ if (pCandidate->IsPaintTransparent())
+ {
+ const tools::Rectangle aCandidatePosSizePixel(
+ pCandidate->GetPosPixel(),
+ pCandidate->GetSizePixel());
+
+ if (aCandidatePosSizePixel.Overlaps(rPixelRect))
+ {
+ m_aCandidates.emplace_back(pCandidate);
+ pCandidate->AddEventListener(LINK(this, CandidateMgr, WindowEventListener));
+ }
+ }
+ pCandidate = pCandidate->GetWindow( GetWindowType::Next );
+ }
+
+ for (const auto& rpCandidate : m_aCandidates)
+ {
+ pCandidate = rpCandidate.get();
+ if (m_aDeletedCandidates.find(pCandidate) != m_aDeletedCandidates.end())
+ continue;
+ //rhbz#1007697 this can cause the window itself to be
+ //deleted. So we are listening to see if that happens
+ //and if so, then skip the update
+ pCandidate->Invalidate(InvalidateFlags::NoTransparent|InvalidateFlags::Children);
+ // important: actually paint the child here!
+ if (m_aDeletedCandidates.find(pCandidate) != m_aDeletedCandidates.end())
+ continue;
+ pCandidate->PaintImmediately();
+ }
+}
+
+SdrPreRenderDevice::SdrPreRenderDevice(OutputDevice& rOriginal)
+: mpOutputDevice(&rOriginal),
+ mpPreRenderDevice(VclPtr<VirtualDevice>::Create())
+{
+}
+
+SdrPreRenderDevice::~SdrPreRenderDevice()
+{
+ mpPreRenderDevice.disposeAndClear();
+}
+
+void SdrPreRenderDevice::PreparePreRenderDevice()
+{
+ // compare size of mpPreRenderDevice with size of visible area
+ if(mpPreRenderDevice->GetOutputSizePixel() != mpOutputDevice->GetOutputSizePixel())
+ {
+ mpPreRenderDevice->SetOutputSizePixel(mpOutputDevice->GetOutputSizePixel());
+ }
+
+ // Also compare the MapModes for zoom/scroll changes
+ if(mpPreRenderDevice->GetMapMode() != mpOutputDevice->GetMapMode())
+ {
+ mpPreRenderDevice->SetMapMode(mpOutputDevice->GetMapMode());
+ }
+
+ // #i29186#
+ mpPreRenderDevice->SetDrawMode(mpOutputDevice->GetDrawMode());
+ mpPreRenderDevice->SetSettings(mpOutputDevice->GetSettings());
+}
+
+void SdrPreRenderDevice::OutputPreRenderDevice(const vcl::Region& rExpandedRegion)
+{
+ // region to pixels
+ const vcl::Region aRegionPixel(mpOutputDevice->LogicToPixel(rExpandedRegion));
+ //RegionHandle aRegionHandle(aRegionPixel.BeginEnumRects());
+ //Rectangle aRegionRectanglePixel;
+
+ // MapModes off
+ bool bMapModeWasEnabledDest(mpOutputDevice->IsMapModeEnabled());
+ bool bMapModeWasEnabledSource(mpPreRenderDevice->IsMapModeEnabled());
+ mpOutputDevice->EnableMapMode(false);
+ mpPreRenderDevice->EnableMapMode(false);
+
+ RectangleVector aRectangles;
+ aRegionPixel.GetRegionRectangles(aRectangles);
+
+ for(const auto& rRect : aRectangles)
+ {
+ // for each rectangle, copy the area
+ const Point aTopLeft(rRect.TopLeft());
+ const Size aSize(rRect.GetSize());
+
+ mpOutputDevice->DrawOutDev(
+ aTopLeft, aSize,
+ aTopLeft, aSize,
+ *mpPreRenderDevice);
+ }
+
+ mpOutputDevice->EnableMapMode(bMapModeWasEnabledDest);
+ mpPreRenderDevice->EnableMapMode(bMapModeWasEnabledSource);
+}
+
+void SdrPaintView::InitOverlayManager(rtl::Reference<sdr::overlay::OverlayManager> xOverlayManager)
+{
+ Color aColA(SvtOptionsDrawinglayer::GetStripeColorA());
+ Color aColB(SvtOptionsDrawinglayer::GetStripeColorB());
+
+ if (Application::GetSettings().GetStyleSettings().GetHighContrastMode())
+ {
+ aColA = aColB = Application::GetSettings().GetStyleSettings().GetHighlightColor();
+ aColB.Invert();
+ }
+
+ xOverlayManager->setStripeColorA(aColA);
+ xOverlayManager->setStripeColorB(aColB);
+ xOverlayManager->setStripeLengthPixel(SvtOptionsDrawinglayer::GetStripeLength());
+}
+
+rtl::Reference<sdr::overlay::OverlayManager> SdrPaintView::CreateOverlayManager(OutputDevice& rOutputDevice) const
+{
+ rtl::Reference<sdr::overlay::OverlayManager> xOverlayManager;
+ // is it a window?
+ if (OUTDEV_WINDOW == rOutputDevice.GetOutDevType())
+ {
+ vcl::Window* pWindow = rOutputDevice.GetOwnerWindow();
+ // decide which OverlayManager to use
+ if (IsBufferedOverlayAllowed() && !pWindow->SupportsDoubleBuffering())
+ {
+ // buffered OverlayManager, buffers its background and refreshes from there
+ // for pure overlay changes (no system redraw). The 3rd parameter specifies
+ // whether that refresh itself will use a 2nd vdev to avoid flickering.
+ // Also hand over the old OverlayManager if existent; this means to take over
+ // the registered OverlayObjects from it
+ xOverlayManager = sdr::overlay::OverlayManagerBuffered::create(rOutputDevice);
+ }
+ else
+ {
+ // unbuffered OverlayManager, just invalidates places where changes
+ // take place
+ // Also hand over the old OverlayManager if existent; this means to take over
+ // the registered OverlayObjects from it
+ xOverlayManager = sdr::overlay::OverlayManager::create(rOutputDevice);
+ }
+
+ OSL_ENSURE(xOverlayManager.is(), "SdrPaintWindow::SdrPaintWindow: Could not allocate an overlayManager (!)");
+
+ // Request a repaint so that the buffered overlay manager fills
+ // its buffer properly. This is a workaround for missing buffer
+ // updates.
+ if (!comphelper::LibreOfficeKit::isActive())
+ {
+ pWindow->Invalidate();
+ }
+
+ InitOverlayManager(xOverlayManager);
+ }
+ return xOverlayManager;
+}
+
+void SdrPaintWindow::impCreateOverlayManager()
+{
+ // not yet one created?
+ if(!mxOverlayManager.is())
+ mxOverlayManager = mrPaintView.CreateOverlayManager(GetOutputDevice());
+}
+
+SdrPaintWindow::SdrPaintWindow(SdrPaintView& rNewPaintView, OutputDevice& rOut, vcl::Window* pWindow)
+: mpOutputDevice(&rOut),
+ mpWindow(pWindow),
+ mrPaintView(rNewPaintView),
+ mbTemporaryTarget(false), // #i72889#
+ mbOutputToWindow(OUTDEV_WINDOW == mpOutputDevice->GetOutDevType()),
+ mpPatched(nullptr)
+{
+}
+
+SdrPaintWindow::~SdrPaintWindow()
+{
+ mxOverlayManager.clear();
+
+ DestroyPreRenderDevice();
+}
+
+rtl::Reference< sdr::overlay::OverlayManager > const & SdrPaintWindow::GetOverlayManager() const
+{
+ if(!mxOverlayManager.is())
+ {
+ // Create buffered overlay manager by default.
+ const_cast< SdrPaintWindow* >(this)->impCreateOverlayManager();
+ }
+
+ return mxOverlayManager;
+}
+
+tools::Rectangle SdrPaintWindow::GetVisibleArea() const
+{
+ Size aVisSizePixel(GetOutputDevice().GetOutputSizePixel());
+ return GetOutputDevice().PixelToLogic(tools::Rectangle(Point(0,0), aVisSizePixel));
+}
+
+bool SdrPaintWindow::OutputToRecordingMetaFile() const
+{
+ GDIMetaFile* pMetaFile = mpOutputDevice->GetConnectMetaFile();
+ return (pMetaFile && pMetaFile->IsRecord() && !pMetaFile->IsPause());
+}
+
+void SdrPaintWindow::PreparePreRenderDevice()
+{
+ const bool bPrepareBufferedOutput(
+ mrPaintView.IsBufferedOutputAllowed()
+ && !OutputToPrinter()
+ && !mpOutputDevice->IsVirtual()
+ && !OutputToRecordingMetaFile());
+
+ if(bPrepareBufferedOutput)
+ {
+ if(!mpPreRenderDevice)
+ {
+ mpPreRenderDevice.reset(new SdrPreRenderDevice(*mpOutputDevice));
+ }
+ }
+ else
+ {
+ DestroyPreRenderDevice();
+ }
+
+ if(mpPreRenderDevice)
+ {
+ mpPreRenderDevice->PreparePreRenderDevice();
+ }
+}
+
+void SdrPaintWindow::DestroyPreRenderDevice()
+{
+ mpPreRenderDevice.reset();
+}
+
+void SdrPaintWindow::OutputPreRenderDevice(const vcl::Region& rExpandedRegion)
+{
+ if(mpPreRenderDevice)
+ {
+ mpPreRenderDevice->OutputPreRenderDevice(rExpandedRegion);
+ }
+}
+
+// #i73602# add flag if buffer shall be used
+void SdrPaintWindow::DrawOverlay(const vcl::Region& rRegion)
+{
+ // ## force creation of OverlayManager since the first repaint needs to
+ // save the background to get a controlled start into overlay mechanism
+ impCreateOverlayManager();
+
+ if(mxOverlayManager.is() && !OutputToPrinter())
+ {
+ if(mpPreRenderDevice)
+ {
+ mxOverlayManager->completeRedraw(rRegion, &mpPreRenderDevice->GetPreRenderDevice());
+ }
+ else
+ {
+ mxOverlayManager->completeRedraw(rRegion);
+ }
+ }
+}
+
+
+void SdrPaintWindow::SetRedrawRegion(const vcl::Region& rNew)
+{
+ maRedrawRegion = rNew;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/svdraw/sdrundomanager.cxx b/svx/source/svdraw/sdrundomanager.cxx
new file mode 100644
index 000000000..3d5fde475
--- /dev/null
+++ b/svx/source/svdraw/sdrundomanager.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 <svx/sdrundomanager.hxx>
+#include <svx/svdundo.hxx>
+#include <sfx2/objsh.hxx>
+#include <svl/hint.hxx>
+
+SdrUndoManager::SdrUndoManager()
+ : EditUndoManager(20 /*nMaxUndoActionCount*/)
+ , mpLastUndoActionBeforeTextEdit(nullptr)
+ , mnRedoActionCountBeforeTextEdit(0)
+ , mbEndTextEditTriggeredFromUndo(false)
+ , m_pDocSh(nullptr)
+{
+}
+
+SdrUndoManager::~SdrUndoManager() {}
+
+bool SdrUndoManager::Undo()
+{
+ if (isTextEditActive())
+ {
+ bool bRetval(false);
+
+ // we are in text edit mode
+ if (GetUndoActionCount() && mpLastUndoActionBeforeTextEdit != GetUndoAction())
+ {
+ // there is an undo action for text edit, trigger it
+ bRetval = EditUndoManager::Undo();
+ }
+ else
+ {
+ // no more text edit undo, end text edit
+ mbEndTextEditTriggeredFromUndo = true;
+ maEndTextEditHdl.Call(this);
+ mbEndTextEditTriggeredFromUndo = false;
+ }
+
+ return bRetval;
+ }
+ else
+ {
+ // no undo triggered up to now, trigger local one
+ return SfxUndoManager::Undo();
+ }
+}
+
+bool SdrUndoManager::Redo()
+{
+ bool bRetval(false);
+ bool bClearRedoStack(false);
+
+ if (isTextEditActive())
+ {
+ // we are in text edit mode
+ bRetval = EditUndoManager::Redo();
+ }
+
+ if (!bRetval)
+ {
+ // Check if the current and thus to-be undone UndoAction is a SdrUndoDiagramModelData action
+ const bool bCurrentIsDiagramChange(
+ GetRedoActionCount()
+ && nullptr != dynamic_cast<SdrUndoDiagramModelData*>(GetRedoAction()));
+
+ // no redo triggered up to now, trigger local one
+ bRetval = SfxUndoManager::Redo();
+
+ // it was a SdrUndoDiagramModelData action and we have more Redo actions
+ if (bCurrentIsDiagramChange && GetRedoActionCount())
+ {
+ const bool bNextIsDiagramChange(
+ nullptr != dynamic_cast<SdrUndoDiagramModelData*>(GetRedoAction()));
+
+ // We have more Redo-actions and the 'next' one to be executed is *not* a
+ // SdrUndoDiagramModelData-action. This means that the already executed
+ // one had done a re-Layout/Re-create of the Diagram XShape/SdrObject
+ // representation based on the restored Diagram ModelData. When the next
+ // Redo action is something else (and thus will not itself re-create
+ // XShapes/SdrShapes) it may be that it is an UnGroup/Delete where a former
+ // as-content-of-Diagram created XShape/SdrShape is referenced, an action
+ // that references a XShape/SdrShape by pointer/reference. That
+ // pointer/reference *cannot* be valid anymore (now).
+
+ // The problem here is that Undo/Redo actions historically reference
+ // XShapes/SdrShapes by pointer/reference, e.g. deleting means: remove
+ // from an SdrObjList and add to an Undo action. I is *not*
+ // address/incarnation-invariant in the sense to remember e.g. to
+ // remove the Nth object in the list (that would work).
+
+ // It might be possible to solve/correct this better, but since it's
+ // a rare corner case just avoid the possible crash when continuing Redos
+ // by clearing the Redo-Stack here as a consequence
+ bClearRedoStack = !bNextIsDiagramChange;
+ }
+ }
+
+ if (bClearRedoStack)
+ {
+ // clear Redo-Stack (explanation see above)
+ ClearRedo();
+ }
+
+ return bRetval;
+}
+
+void SdrUndoManager::Clear()
+{
+ if (isTextEditActive())
+ {
+ while (GetUndoActionCount() && mpLastUndoActionBeforeTextEdit != GetUndoAction())
+ {
+ RemoveLastUndoAction();
+ }
+
+ // urgently needed: RemoveLastUndoAction does NOT correct the Redo stack by itself (!)
+ ClearRedo();
+ }
+ else
+ {
+ // call parent
+ EditUndoManager::Clear();
+ }
+}
+
+void SdrUndoManager::SetEndTextEditHdl(const Link<SdrUndoManager*, void>& rLink)
+{
+ maEndTextEditHdl = rLink;
+
+ if (isTextEditActive())
+ {
+ // text edit start, remember last non-textedit action for later cleanup
+ mpLastUndoActionBeforeTextEdit = GetUndoActionCount() ? GetUndoAction() : nullptr;
+ mnRedoActionCountBeforeTextEdit = GetRedoActionCount();
+ }
+ else
+ {
+ // text edit ends, pop all textedit actions up to the remembered non-textedit action from the start
+ // to set back the UndoManager to the state before text edit started. If that action is already gone
+ // (due to being removed from the undo stack in the meantime), all need to be removed anyways
+ while (GetUndoActionCount() && mpLastUndoActionBeforeTextEdit != GetUndoAction())
+ {
+ RemoveLastUndoAction();
+ }
+
+ // urgently needed: RemoveLastUndoAction does NOT correct the Redo stack by itself (!)
+ ClearRedo();
+
+ // forget marker again
+ mpLastUndoActionBeforeTextEdit = nullptr;
+ mnRedoActionCountBeforeTextEdit = 0;
+ }
+}
+
+bool SdrUndoManager::isTextEditActive() const { return maEndTextEditHdl.IsSet(); }
+
+void SdrUndoManager::SetDocShell(SfxObjectShell* pDocShell) { m_pDocSh = pDocShell; }
+
+void SdrUndoManager::EmptyActionsChanged()
+{
+ if (m_pDocSh)
+ {
+ m_pDocSh->Broadcast(SfxHint(SfxHintId::DocumentRepair));
+ }
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/svdraw/selectioncontroller.cxx b/svx/source/svdraw/selectioncontroller.cxx
new file mode 100644
index 000000000..5f6f51312
--- /dev/null
+++ b/svx/source/svdraw/selectioncontroller.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 <svx/selectioncontroller.hxx>
+
+namespace sdr
+{
+
+bool SelectionController::onKeyInput(const KeyEvent& /*rKEvt*/, vcl::Window* /*pWin*/)
+{
+ return false;
+}
+
+bool SelectionController::onMouseButtonDown(const MouseEvent& /*rMEvt*/, vcl::Window* /*pWin*/)
+{
+ return false;
+}
+
+bool SelectionController::onMouseButtonUp(const MouseEvent& /*rMEvt*/, vcl::Window* /*pWin*/)
+{
+ return false;
+}
+
+bool SelectionController::onMouseMove(const MouseEvent& /*rMEvt*/, vcl::Window* /*pWin*/)
+{
+ return false;
+}
+
+void SelectionController::onSelectionHasChanged()
+{
+}
+
+void SelectionController::onSelectAll()
+{
+}
+
+void SelectionController::GetState( SfxItemSet& /*rSet*/ )
+{
+}
+
+void SelectionController::Execute( SfxRequest& /*rReq*/ )
+{
+}
+
+bool SelectionController::DeleteMarked()
+{
+ return false;
+}
+
+bool SelectionController::GetAttributes(SfxItemSet& /*rTargetSet*/, bool /*bOnlyHardAttr*/) const
+{
+ return false;
+}
+
+bool SelectionController::SetAttributes(const SfxItemSet& /*rSet*/, bool /*bReplaceAll*/)
+{
+ return false;
+}
+
+bool SelectionController::GetStyleSheet( SfxStyleSheet* &/*rpStyleSheet*/ ) const
+{
+ return false;
+}
+
+bool SelectionController::SetStyleSheet( SfxStyleSheet* /*pStyleSheet*/, bool /*bDontRemoveHardAttr*/ )
+{
+ return false;
+}
+
+SdrObject* SelectionController::GetMarkedSdrObjClone( SdrModel& /*rTargetModel*/ )
+{
+ return nullptr;
+}
+
+bool SelectionController::PasteObjModel( const SdrModel& /*rModel*/ )
+{
+ return false;
+}
+
+bool SelectionController::ApplyFormatPaintBrush( SfxItemSet& /*rFormatSet*/, bool /*bNoCharacterFormats*/, bool /*bNoParagraphFormats*/ )
+{
+ return false;
+}
+
+bool SelectionController::hasSelectedCells() const
+{
+ return false;
+}
+
+void SelectionController::getSelectedCells(table::CellPos& /*rFirstPos*/, table::CellPos& /*rLastPos*/)
+{
+}
+
+bool SelectionController::setCursorLogicPosition(const Point& /*rPosition*/, bool /*bPoint*/)
+{
+ return false;
+}
+
+
+bool SelectionController::ChangeFontSize(bool /*bGrow*/, const FontList* /*pFontList*/)
+{
+ return false;
+}
+
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/svdraw/svdattr.cxx b/svx/source/svdraw/svdattr.cxx
new file mode 100644
index 000000000..c0057b6ae
--- /dev/null
+++ b/svx/source/svdraw/svdattr.cxx
@@ -0,0 +1,2042 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * 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/TextFitToSizeType.hpp>
+#include <com/sun/star/drawing/TextHorizontalAdjust.hpp>
+#include <com/sun/star/drawing/TextVerticalAdjust.hpp>
+#include <com/sun/star/drawing/TextAnimationKind.hpp>
+#include <com/sun/star/uno/Any.hxx>
+#include <com/sun/star/drawing/TextAnimationDirection.hpp>
+#include <com/sun/star/drawing/ConnectorType.hpp>
+#include <com/sun/star/drawing/MeasureKind.hpp>
+#include <com/sun/star/drawing/MeasureTextHorzPos.hpp>
+#include <com/sun/star/drawing/MeasureTextVertPos.hpp>
+#include <com/sun/star/drawing/CircleKind.hpp>
+
+#include <editeng/boxitem.hxx>
+#include <editeng/eeitem.hxx>
+#include <editeng/lineitem.hxx>
+#include <editeng/xmlcnitm.hxx>
+#include <editeng/writingmodeitem.hxx>
+#include <editeng/charrotateitem.hxx>
+#include <osl/diagnose.h>
+#include <i18nutil/unicode.hxx>
+#include <tools/bigint.hxx>
+#include <unotools/intlwrapper.hxx>
+#include <unotools/localedatawrapper.hxx>
+#include <vcl/svapp.hxx>
+#include <vcl/settings.hxx>
+
+#include <svx/strings.hrc>
+#include <svx/dialmgr.hxx>
+#include <svx/sdgcpitm.hxx>
+#include <svx/sdtfchim.hxx>
+#include <svx/sdasitm.hxx>
+#include <sdgcoitm.hxx>
+#include <svx/sdggaitm.hxx>
+#include <sdginitm.hxx>
+#include <svx/sdgluitm.hxx>
+#include <svx/sdgmoitm.hxx>
+#include <sdgtritm.hxx>
+#include <svx/sdprcitm.hxx>
+#include <svx/sdtaaitm.hxx>
+#include <svx/sdtacitm.hxx>
+#include <svx/sdtaditm.hxx>
+#include <svx/sdtaiitm.hxx>
+#include <svx/sdtaitm.hxx>
+#include <svx/sdtakitm.hxx>
+#include <svx/sdtayitm.hxx>
+#include <svx/sdtfsitm.hxx>
+#include <svx/svdmodel.hxx>
+#include <svx/svdpool.hxx>
+#include <svx/svdtrans.hxx>
+#include <svx/svx3ditems.hxx>
+#include <svx/svxids.hrc>
+#include <sxallitm.hxx>
+#include <sxcaitm.hxx>
+#include <svx/sxcecitm.hxx>
+#include <svx/sxcgitm.hxx>
+#include <sxcikitm.hxx>
+#include <svx/sxcllitm.hxx>
+#include <svx/sxctitm.hxx>
+#include <svx/sxekitm.hxx>
+#include <svx/sxelditm.hxx>
+#include <svx/sxenditm.hxx>
+#include <sxfiitm.hxx>
+#include <sxlayitm.hxx>
+#include <sxlogitm.hxx>
+#include <svx/sxmbritm.hxx>
+#include <sxmfsitm.hxx>
+#include <sxmkitm.hxx>
+#include <sxmoitm.hxx>
+#include <sxmovitm.hxx>
+#include <sxmsitm.hxx>
+#include <sxmtaitm.hxx>
+#include <svx/sxmtfitm.hxx>
+#include <svx/sxmtpitm.hxx>
+#include <svx/sxmtritm.hxx>
+#include <svx/sxmuitm.hxx>
+#include <svx/xcolit.hxx>
+#include <sxoneitm.hxx>
+#include <sxopitm.hxx>
+#include <sxreaitm.hxx>
+#include <sxreoitm.hxx>
+#include <sxroaitm.hxx>
+#include <sxrooitm.hxx>
+#include <sxsaitm.hxx>
+#include <sxsalitm.hxx>
+#include <sxsiitm.hxx>
+#include <sxsoitm.hxx>
+#include <sxtraitm.hxx>
+#include <libxml/xmlwriter.h>
+
+using namespace ::com::sun::star;
+
+SdrItemPool::SdrItemPool(
+ SfxItemPool* _pMaster)
+: XOutdevItemPool(_pMaster)
+{
+ // prepare some constants
+ const Color aNullCol(COL_BLACK);
+ const sal_Int32 nDefEdgeDist(500); // Defaulting hard for Draw (100TH_MM) currently. MapMode will have to be taken into account in the future.
+
+ // init the non-persistent items
+ for(sal_uInt16 i(SDRATTR_NOTPERSIST_FIRST); i <= SDRATTR_NOTPERSIST_LAST; i++)
+ {
+ mpLocalItemInfos[i - SDRATTR_START]._bPoolable = false;
+ }
+
+ // init own PoolDefaults
+ std::vector<SfxPoolItem*>& rPoolDefaults = *mpLocalPoolDefaults;
+ rPoolDefaults[SDRATTR_SHADOW -SDRATTR_START]=new SdrOnOffItem(SDRATTR_SHADOW, false);
+ rPoolDefaults[SDRATTR_SHADOWCOLOR -SDRATTR_START]=new XColorItem(SDRATTR_SHADOWCOLOR, aNullCol);
+ rPoolDefaults[SDRATTR_SHADOWXDIST -SDRATTR_START]=new SdrMetricItem(SDRATTR_SHADOWXDIST, 0);
+ rPoolDefaults[SDRATTR_SHADOWYDIST -SDRATTR_START]=new SdrMetricItem(SDRATTR_SHADOWYDIST, 0);
+ rPoolDefaults[SDRATTR_SHADOWSIZEX -SDRATTR_START]=new SdrMetricItem(SDRATTR_SHADOWSIZEX, 100000);
+ rPoolDefaults[SDRATTR_SHADOWSIZEY -SDRATTR_START]=new SdrMetricItem(SDRATTR_SHADOWSIZEY, 100000);
+ rPoolDefaults[SDRATTR_SHADOWTRANSPARENCE-SDRATTR_START]=new SdrPercentItem(SDRATTR_SHADOWTRANSPARENCE, 0);
+ rPoolDefaults[SDRATTR_SHADOWBLUR -SDRATTR_START]=new SdrMetricItem(SDRATTR_SHADOWBLUR, 0);
+ rPoolDefaults[SDRATTR_SHADOW3D -SDRATTR_START]=new SfxVoidItem(SDRATTR_SHADOW3D );
+ rPoolDefaults[SDRATTR_SHADOWPERSP -SDRATTR_START]=new SfxVoidItem(SDRATTR_SHADOWPERSP );
+ rPoolDefaults[SDRATTR_CAPTIONTYPE -SDRATTR_START]=new SdrCaptionTypeItem ;
+ rPoolDefaults[SDRATTR_CAPTIONFIXEDANGLE-SDRATTR_START]=new SdrOnOffItem(SDRATTR_CAPTIONFIXEDANGLE, true);
+ rPoolDefaults[SDRATTR_CAPTIONANGLE -SDRATTR_START]=new SdrCaptionAngleItem ;
+ rPoolDefaults[SDRATTR_CAPTIONGAP -SDRATTR_START]=new SdrCaptionGapItem ;
+ rPoolDefaults[SDRATTR_CAPTIONESCDIR -SDRATTR_START]=new SdrCaptionEscDirItem ;
+ rPoolDefaults[SDRATTR_CAPTIONESCISREL -SDRATTR_START]=new SdrCaptionEscIsRelItem ;
+ rPoolDefaults[SDRATTR_CAPTIONESCREL -SDRATTR_START]=new SdrCaptionEscRelItem ;
+ rPoolDefaults[SDRATTR_CAPTIONESCABS -SDRATTR_START]=new SdrCaptionEscAbsItem ;
+ rPoolDefaults[SDRATTR_CAPTIONLINELEN -SDRATTR_START]=new SdrCaptionLineLenItem ;
+ rPoolDefaults[SDRATTR_CAPTIONFITLINELEN-SDRATTR_START]=new SdrCaptionFitLineLenItem;
+ rPoolDefaults[SDRATTR_CORNER_RADIUS -SDRATTR_START]=new SdrMetricItem(SDRATTR_CORNER_RADIUS, 0);
+ rPoolDefaults[SDRATTR_TEXT_MINFRAMEHEIGHT -SDRATTR_START]=new SdrMetricItem(SDRATTR_TEXT_MINFRAMEHEIGHT, 0);
+ rPoolDefaults[SDRATTR_TEXT_AUTOGROWHEIGHT -SDRATTR_START]=new SdrOnOffItem(SDRATTR_TEXT_AUTOGROWHEIGHT, true);
+ rPoolDefaults[SDRATTR_TEXT_FITTOSIZE -SDRATTR_START]=new SdrTextFitToSizeTypeItem;
+ rPoolDefaults[SDRATTR_TEXT_LEFTDIST -SDRATTR_START]=new SdrMetricItem(SDRATTR_TEXT_LEFTDIST, 0);
+ rPoolDefaults[SDRATTR_TEXT_RIGHTDIST -SDRATTR_START]=new SdrMetricItem(SDRATTR_TEXT_RIGHTDIST, 0);
+ rPoolDefaults[SDRATTR_TEXT_UPPERDIST -SDRATTR_START]=new SdrMetricItem(SDRATTR_TEXT_UPPERDIST, 0);
+ rPoolDefaults[SDRATTR_TEXT_LOWERDIST -SDRATTR_START]=new SdrMetricItem(SDRATTR_TEXT_LOWERDIST, 0);
+ rPoolDefaults[SDRATTR_TEXT_VERTADJUST -SDRATTR_START]=new SdrTextVertAdjustItem;
+ rPoolDefaults[SDRATTR_TEXT_MAXFRAMEHEIGHT -SDRATTR_START]=new SdrMetricItem(SDRATTR_TEXT_MAXFRAMEHEIGHT, 0);
+ rPoolDefaults[SDRATTR_TEXT_MINFRAMEWIDTH -SDRATTR_START]=new SdrMetricItem(SDRATTR_TEXT_MINFRAMEWIDTH, 0);
+ rPoolDefaults[SDRATTR_TEXT_MAXFRAMEWIDTH -SDRATTR_START]=new SdrMetricItem(SDRATTR_TEXT_MAXFRAMEWIDTH, 0);
+ rPoolDefaults[SDRATTR_TEXT_AUTOGROWWIDTH -SDRATTR_START]=new SdrOnOffItem(SDRATTR_TEXT_AUTOGROWWIDTH, false);
+ rPoolDefaults[SDRATTR_TEXT_HORZADJUST -SDRATTR_START]=new SdrTextHorzAdjustItem;
+ rPoolDefaults[SDRATTR_TEXT_ANIKIND -SDRATTR_START]=new SdrTextAniKindItem;
+ rPoolDefaults[SDRATTR_TEXT_ANIDIRECTION -SDRATTR_START]=new SdrTextAniDirectionItem;
+ rPoolDefaults[SDRATTR_TEXT_ANISTARTINSIDE -SDRATTR_START]=new SdrTextAniStartInsideItem;
+ rPoolDefaults[SDRATTR_TEXT_ANISTOPINSIDE -SDRATTR_START]=new SdrTextAniStopInsideItem;
+ rPoolDefaults[SDRATTR_TEXT_ANICOUNT -SDRATTR_START]=new SdrTextAniCountItem;
+ rPoolDefaults[SDRATTR_TEXT_ANIDELAY -SDRATTR_START]=new SdrTextAniDelayItem;
+ rPoolDefaults[SDRATTR_TEXT_ANIAMOUNT -SDRATTR_START]=new SdrTextAniAmountItem;
+ rPoolDefaults[SDRATTR_TEXT_CONTOURFRAME -SDRATTR_START]=new SdrOnOffItem(SDRATTR_TEXT_CONTOURFRAME, false);
+ rPoolDefaults[SDRATTR_XMLATTRIBUTES -SDRATTR_START]=new SvXMLAttrContainerItem( SDRATTR_XMLATTRIBUTES );
+ rPoolDefaults[SDRATTR_TEXT_CHAINNEXTNAME -SDRATTR_START]=new SfxStringItem(SDRATTR_TEXT_CHAINNEXTNAME, "");
+ rPoolDefaults[SDRATTR_TEXT_USEFIXEDCELLHEIGHT -SDRATTR_START]=new SdrTextFixedCellHeightItem;
+ rPoolDefaults[SDRATTR_TEXT_WORDWRAP -SDRATTR_START]=new SdrOnOffItem(SDRATTR_TEXT_WORDWRAP, true);
+ rPoolDefaults[SDRATTR_EDGEKIND -SDRATTR_START]=new SdrEdgeKindItem;
+ rPoolDefaults[SDRATTR_EDGENODE1HORZDIST-SDRATTR_START]=new SdrEdgeNode1HorzDistItem(nDefEdgeDist);
+ rPoolDefaults[SDRATTR_EDGENODE1VERTDIST-SDRATTR_START]=new SdrEdgeNode1VertDistItem(nDefEdgeDist);
+ rPoolDefaults[SDRATTR_EDGENODE2HORZDIST-SDRATTR_START]=new SdrEdgeNode2HorzDistItem(nDefEdgeDist);
+ rPoolDefaults[SDRATTR_EDGENODE2VERTDIST-SDRATTR_START]=new SdrEdgeNode2VertDistItem(nDefEdgeDist);
+ rPoolDefaults[SDRATTR_EDGENODE1GLUEDIST-SDRATTR_START]=new SdrEdgeNode1GlueDistItem;
+ rPoolDefaults[SDRATTR_EDGENODE2GLUEDIST-SDRATTR_START]=new SdrEdgeNode2GlueDistItem;
+ rPoolDefaults[SDRATTR_EDGELINEDELTACOUNT-SDRATTR_START]=new SdrEdgeLineDeltaCountItem;
+ rPoolDefaults[SDRATTR_EDGELINE1DELTA -SDRATTR_START]=new SdrMetricItem(SDRATTR_EDGELINE1DELTA, 0);
+ rPoolDefaults[SDRATTR_EDGELINE2DELTA -SDRATTR_START]=new SdrMetricItem(SDRATTR_EDGELINE2DELTA, 0);
+ rPoolDefaults[SDRATTR_EDGELINE3DELTA -SDRATTR_START]=new SdrMetricItem(SDRATTR_EDGELINE3DELTA, 0);
+ rPoolDefaults[SDRATTR_MEASUREKIND -SDRATTR_START]=new SdrMeasureKindItem;
+ rPoolDefaults[SDRATTR_MEASURETEXTHPOS -SDRATTR_START]=new SdrMeasureTextHPosItem;
+ rPoolDefaults[SDRATTR_MEASURETEXTVPOS -SDRATTR_START]=new SdrMeasureTextVPosItem;
+ rPoolDefaults[SDRATTR_MEASURELINEDIST -SDRATTR_START]=new SdrMetricItem(SDRATTR_MEASURELINEDIST, 800);
+ rPoolDefaults[SDRATTR_MEASUREHELPLINEOVERHANG -SDRATTR_START]=new SdrMetricItem(SDRATTR_MEASUREHELPLINEOVERHANG, 200);
+ rPoolDefaults[SDRATTR_MEASUREHELPLINEDIST -SDRATTR_START]=new SdrMetricItem(SDRATTR_MEASUREHELPLINEDIST, 100);
+ rPoolDefaults[SDRATTR_MEASUREHELPLINE1LEN -SDRATTR_START]=new SdrMetricItem(SDRATTR_MEASUREHELPLINE1LEN, 0);
+ rPoolDefaults[SDRATTR_MEASUREHELPLINE2LEN -SDRATTR_START]=new SdrMetricItem(SDRATTR_MEASUREHELPLINE2LEN, 0);
+ rPoolDefaults[SDRATTR_MEASUREBELOWREFEDGE -SDRATTR_START]=new SdrMeasureBelowRefEdgeItem;
+ rPoolDefaults[SDRATTR_MEASURETEXTROTA90 -SDRATTR_START]=new SdrMeasureTextRota90Item;
+ rPoolDefaults[SDRATTR_MEASURETEXTUPSIDEDOWN -SDRATTR_START]=new SdrMeasureTextUpsideDownItem;
+ rPoolDefaults[SDRATTR_MEASUREOVERHANG -SDRATTR_START]=new SdrMeasureOverhangItem(600);
+ rPoolDefaults[SDRATTR_MEASUREUNIT -SDRATTR_START]=new SdrMeasureUnitItem;
+ rPoolDefaults[SDRATTR_MEASURESCALE -SDRATTR_START]=new SdrMeasureScaleItem;
+ rPoolDefaults[SDRATTR_MEASURESHOWUNIT -SDRATTR_START]=new SdrYesNoItem(SDRATTR_MEASURESHOWUNIT, false);
+ rPoolDefaults[SDRATTR_MEASUREFORMATSTRING -SDRATTR_START]=new SdrMeasureFormatStringItem();
+ rPoolDefaults[SDRATTR_MEASURETEXTAUTOANGLE -SDRATTR_START]=new SdrMeasureTextAutoAngleItem();
+ rPoolDefaults[SDRATTR_MEASURETEXTAUTOANGLEVIEW-SDRATTR_START]=new SdrMeasureTextAutoAngleViewItem();
+ rPoolDefaults[SDRATTR_MEASURETEXTISFIXEDANGLE -SDRATTR_START]=new SdrMeasureTextIsFixedAngleItem();
+ rPoolDefaults[SDRATTR_MEASURETEXTFIXEDANGLE -SDRATTR_START]=new SdrMeasureTextFixedAngleItem();
+ rPoolDefaults[SDRATTR_MEASUREDECIMALPLACES -SDRATTR_START]=new SdrMeasureDecimalPlacesItem();
+ rPoolDefaults[SDRATTR_CIRCKIND -SDRATTR_START]=new SdrCircKindItem;
+ rPoolDefaults[SDRATTR_CIRCSTARTANGLE-SDRATTR_START]=new SdrAngleItem(SDRATTR_CIRCSTARTANGLE, 0_deg100);
+ rPoolDefaults[SDRATTR_CIRCENDANGLE -SDRATTR_START]=new SdrAngleItem(SDRATTR_CIRCENDANGLE, 36000_deg100);
+ rPoolDefaults[SDRATTR_OBJMOVEPROTECT -SDRATTR_START]=new SdrYesNoItem(SDRATTR_OBJMOVEPROTECT, false);
+ rPoolDefaults[SDRATTR_OBJSIZEPROTECT -SDRATTR_START]=new SdrYesNoItem(SDRATTR_OBJSIZEPROTECT, false);
+ rPoolDefaults[SDRATTR_OBJPRINTABLE -SDRATTR_START]=new SdrObjPrintableItem;
+ rPoolDefaults[SDRATTR_OBJVISIBLE -SDRATTR_START]=new SdrObjVisibleItem;
+ rPoolDefaults[SDRATTR_LAYERID -SDRATTR_START]=new SdrLayerIdItem(SdrLayerID(0));
+ rPoolDefaults[SDRATTR_LAYERNAME -SDRATTR_START]=new SdrLayerNameItem;
+ rPoolDefaults[SDRATTR_OBJECTNAME -SDRATTR_START]=new SfxStringItem(SDRATTR_OBJECTNAME);
+ rPoolDefaults[SDRATTR_ALLPOSITIONX -SDRATTR_START]=new SdrAllPositionXItem;
+ rPoolDefaults[SDRATTR_ALLPOSITIONY -SDRATTR_START]=new SdrAllPositionYItem;
+ rPoolDefaults[SDRATTR_ALLSIZEWIDTH -SDRATTR_START]=new SdrAllSizeWidthItem;
+ rPoolDefaults[SDRATTR_ALLSIZEHEIGHT -SDRATTR_START]=new SdrAllSizeHeightItem;
+ rPoolDefaults[SDRATTR_ONEPOSITIONX -SDRATTR_START]=new SdrOnePositionXItem;
+ rPoolDefaults[SDRATTR_ONEPOSITIONY -SDRATTR_START]=new SdrOnePositionYItem;
+ rPoolDefaults[SDRATTR_ONESIZEWIDTH -SDRATTR_START]=new SdrOneSizeWidthItem;
+ rPoolDefaults[SDRATTR_ONESIZEHEIGHT -SDRATTR_START]=new SdrOneSizeHeightItem;
+ rPoolDefaults[SDRATTR_LOGICSIZEWIDTH -SDRATTR_START]=new SdrLogicSizeWidthItem;
+ rPoolDefaults[SDRATTR_LOGICSIZEHEIGHT-SDRATTR_START]=new SdrLogicSizeHeightItem;
+ rPoolDefaults[SDRATTR_ROTATEANGLE -SDRATTR_START]=new SdrAngleItem(SDRATTR_ROTATEANGLE, 0_deg100);
+ rPoolDefaults[SDRATTR_SHEARANGLE -SDRATTR_START]=new SdrShearAngleItem;
+ rPoolDefaults[SDRATTR_MOVEX -SDRATTR_START]=new SdrMoveXItem;
+ rPoolDefaults[SDRATTR_MOVEY -SDRATTR_START]=new SdrMoveYItem;
+ rPoolDefaults[SDRATTR_RESIZEXONE -SDRATTR_START]=new SdrResizeXOneItem;
+ rPoolDefaults[SDRATTR_RESIZEYONE -SDRATTR_START]=new SdrResizeYOneItem;
+ rPoolDefaults[SDRATTR_ROTATEONE -SDRATTR_START]=new SdrRotateOneItem;
+ rPoolDefaults[SDRATTR_HORZSHEARONE -SDRATTR_START]=new SdrHorzShearOneItem;
+ rPoolDefaults[SDRATTR_VERTSHEARONE -SDRATTR_START]=new SdrVertShearOneItem;
+ rPoolDefaults[SDRATTR_RESIZEXALL -SDRATTR_START]=new SdrResizeXAllItem;
+ rPoolDefaults[SDRATTR_RESIZEYALL -SDRATTR_START]=new SdrResizeYAllItem;
+ rPoolDefaults[SDRATTR_ROTATEALL -SDRATTR_START]=new SdrRotateAllItem;
+ rPoolDefaults[SDRATTR_HORZSHEARALL -SDRATTR_START]=new SdrHorzShearAllItem;
+ rPoolDefaults[SDRATTR_VERTSHEARALL -SDRATTR_START]=new SdrVertShearAllItem;
+ rPoolDefaults[SDRATTR_TRANSFORMREF1X -SDRATTR_START]=new SdrTransformRef1XItem;
+ rPoolDefaults[SDRATTR_TRANSFORMREF1Y -SDRATTR_START]=new SdrTransformRef1YItem;
+ rPoolDefaults[SDRATTR_TRANSFORMREF2X -SDRATTR_START]=new SdrTransformRef2XItem;
+ rPoolDefaults[SDRATTR_TRANSFORMREF2Y -SDRATTR_START]=new SdrTransformRef2YItem;
+ rPoolDefaults[SDRATTR_TEXTDIRECTION -SDRATTR_START]=new SvxWritingModeItem(css::text::WritingMode_LR_TB, SDRATTR_TEXTDIRECTION);
+ rPoolDefaults[ SDRATTR_GRAFRED - SDRATTR_START] = new SdrGrafRedItem;
+ rPoolDefaults[ SDRATTR_GRAFGREEN - SDRATTR_START] = new SdrGrafGreenItem;
+ rPoolDefaults[ SDRATTR_GRAFBLUE - SDRATTR_START] = new SdrGrafBlueItem;
+ rPoolDefaults[ SDRATTR_GRAFLUMINANCE - SDRATTR_START] = new SdrGrafLuminanceItem;
+ rPoolDefaults[ SDRATTR_GRAFCONTRAST - SDRATTR_START] = new SdrGrafContrastItem;
+ rPoolDefaults[ SDRATTR_GRAFGAMMA - SDRATTR_START] = new SdrGrafGamma100Item;
+ rPoolDefaults[ SDRATTR_GRAFTRANSPARENCE - SDRATTR_START] = new SdrGrafTransparenceItem;
+ rPoolDefaults[ SDRATTR_GRAFINVERT - SDRATTR_START] = new SdrGrafInvertItem;
+ rPoolDefaults[ SDRATTR_GRAFMODE - SDRATTR_START] = new SdrGrafModeItem;
+ rPoolDefaults[ SDRATTR_GRAFCROP - SDRATTR_START] = new SdrGrafCropItem;
+ rPoolDefaults[ SDRATTR_3DOBJ_PERCENT_DIAGONAL - SDRATTR_START ] = new SfxUInt16Item(SDRATTR_3DOBJ_PERCENT_DIAGONAL, 10);
+ rPoolDefaults[ SDRATTR_3DOBJ_BACKSCALE - SDRATTR_START ] = new SfxUInt16Item(SDRATTR_3DOBJ_BACKSCALE, 100);
+ rPoolDefaults[ SDRATTR_3DOBJ_DEPTH - SDRATTR_START ] = new SfxUInt32Item(SDRATTR_3DOBJ_DEPTH, 1000);
+ rPoolDefaults[ SDRATTR_3DOBJ_HORZ_SEGS - SDRATTR_START ] = new SfxUInt32Item(SDRATTR_3DOBJ_HORZ_SEGS, 24);
+ rPoolDefaults[ SDRATTR_3DOBJ_VERT_SEGS - SDRATTR_START ] = new SfxUInt32Item(SDRATTR_3DOBJ_VERT_SEGS, 24);
+ rPoolDefaults[ SDRATTR_3DOBJ_END_ANGLE - SDRATTR_START ] = new SfxUInt32Item(SDRATTR_3DOBJ_END_ANGLE, 3600);
+ rPoolDefaults[ SDRATTR_3DOBJ_DOUBLE_SIDED - SDRATTR_START ] = new SfxBoolItem(SDRATTR_3DOBJ_DOUBLE_SIDED, false);
+ rPoolDefaults[ SDRATTR_3DOBJ_NORMALS_KIND - SDRATTR_START ] = new Svx3DNormalsKindItem;
+ rPoolDefaults[ SDRATTR_3DOBJ_NORMALS_INVERT - SDRATTR_START ] = new SfxBoolItem(SDRATTR_3DOBJ_NORMALS_INVERT, false);
+ rPoolDefaults[ SDRATTR_3DOBJ_TEXTURE_PROJ_X - SDRATTR_START ] = new Svx3DTextureProjectionXItem;
+ rPoolDefaults[ SDRATTR_3DOBJ_TEXTURE_PROJ_Y - SDRATTR_START ] = new Svx3DTextureProjectionYItem;
+ rPoolDefaults[ SDRATTR_3DOBJ_SHADOW_3D - SDRATTR_START ] = new SfxBoolItem(SDRATTR_3DOBJ_SHADOW_3D, false);
+ rPoolDefaults[ SDRATTR_3DOBJ_MAT_COLOR - SDRATTR_START ] = new SvxColorItem(Color(0x0000b8ff), SDRATTR_3DOBJ_MAT_COLOR);
+ rPoolDefaults[ SDRATTR_3DOBJ_MAT_EMISSION - SDRATTR_START ] = new SvxColorItem(Color(0x00000000), SDRATTR_3DOBJ_MAT_EMISSION);
+ rPoolDefaults[ SDRATTR_3DOBJ_MAT_SPECULAR - SDRATTR_START ] = new SvxColorItem(Color(0x00ffffff), SDRATTR_3DOBJ_MAT_SPECULAR);
+ rPoolDefaults[ SDRATTR_3DOBJ_MAT_SPECULAR_INTENSITY - SDRATTR_START ] = new SfxUInt16Item(SDRATTR_3DOBJ_MAT_SPECULAR_INTENSITY, 15);
+ rPoolDefaults[ SDRATTR_3DOBJ_TEXTURE_KIND - SDRATTR_START ] = new Svx3DTextureKindItem;
+ rPoolDefaults[ SDRATTR_3DOBJ_TEXTURE_MODE - SDRATTR_START ] = new Svx3DTextureModeItem;
+ rPoolDefaults[ SDRATTR_3DOBJ_TEXTURE_FILTER - SDRATTR_START ] = new SfxBoolItem(SDRATTR_3DOBJ_TEXTURE_FILTER, false);
+ rPoolDefaults[ SDRATTR_3DOBJ_SMOOTH_NORMALS - SDRATTR_START ] = new Svx3DSmoothNormalsItem;
+ rPoolDefaults[ SDRATTR_3DOBJ_SMOOTH_LIDS - SDRATTR_START ] = new Svx3DSmoothLidsItem;
+ rPoolDefaults[ SDRATTR_3DOBJ_CHARACTER_MODE - SDRATTR_START ] = new Svx3DCharacterModeItem;
+ rPoolDefaults[ SDRATTR_3DOBJ_CLOSE_FRONT - SDRATTR_START ] = new Svx3DCloseFrontItem;
+ rPoolDefaults[ SDRATTR_3DOBJ_CLOSE_BACK - SDRATTR_START ] = new Svx3DCloseBackItem;
+ rPoolDefaults[ SDRATTR_3DOBJ_REDUCED_LINE_GEOMETRY - SDRATTR_START ] = new Svx3DReducedLineGeometryItem;
+ rPoolDefaults[ SDRATTR_3DSCENE_PERSPECTIVE - SDRATTR_START ] = new Svx3DPerspectiveItem;
+ rPoolDefaults[ SDRATTR_3DSCENE_DISTANCE - SDRATTR_START ] = new SfxUInt32Item(SDRATTR_3DSCENE_DISTANCE, 100);
+ rPoolDefaults[ SDRATTR_3DSCENE_FOCAL_LENGTH - SDRATTR_START ] = new SfxUInt32Item(SDRATTR_3DSCENE_FOCAL_LENGTH, 100);
+ rPoolDefaults[ SDRATTR_3DSCENE_TWO_SIDED_LIGHTING - SDRATTR_START ] = new SfxBoolItem(SDRATTR_3DSCENE_TWO_SIDED_LIGHTING, false);
+ rPoolDefaults[ SDRATTR_3DSCENE_LIGHTCOLOR_1 - SDRATTR_START ] = new SvxColorItem(Color(ColorTransparency, 0xffcccccc), SDRATTR_3DSCENE_LIGHTCOLOR_1);
+ rPoolDefaults[ SDRATTR_3DSCENE_LIGHTCOLOR_2 - SDRATTR_START ] = new SvxColorItem(Color(0x00000000), SDRATTR_3DSCENE_LIGHTCOLOR_2);
+ rPoolDefaults[ SDRATTR_3DSCENE_LIGHTCOLOR_3 - SDRATTR_START ] = new SvxColorItem(Color(0x00000000), SDRATTR_3DSCENE_LIGHTCOLOR_3);
+ rPoolDefaults[ SDRATTR_3DSCENE_LIGHTCOLOR_4 - SDRATTR_START ] = new SvxColorItem(Color(0x00000000), SDRATTR_3DSCENE_LIGHTCOLOR_4);
+ rPoolDefaults[ SDRATTR_3DSCENE_LIGHTCOLOR_5 - SDRATTR_START ] = new SvxColorItem(Color(0x00000000), SDRATTR_3DSCENE_LIGHTCOLOR_5);
+ rPoolDefaults[ SDRATTR_3DSCENE_LIGHTCOLOR_6 - SDRATTR_START ] = new SvxColorItem(Color(0x00000000), SDRATTR_3DSCENE_LIGHTCOLOR_6);
+ rPoolDefaults[ SDRATTR_3DSCENE_LIGHTCOLOR_7 - SDRATTR_START ] = new SvxColorItem(Color(0x00000000), SDRATTR_3DSCENE_LIGHTCOLOR_7);
+ rPoolDefaults[ SDRATTR_3DSCENE_LIGHTCOLOR_8 - SDRATTR_START ] = new SvxColorItem(Color(0x00000000), SDRATTR_3DSCENE_LIGHTCOLOR_8);
+ rPoolDefaults[ SDRATTR_3DSCENE_AMBIENTCOLOR - SDRATTR_START ] = new SvxColorItem(Color(0x00666666), SDRATTR_3DSCENE_AMBIENTCOLOR);
+ rPoolDefaults[ SDRATTR_3DSCENE_LIGHTON_1 - SDRATTR_START ] = new SfxBoolItem(SDRATTR_3DSCENE_LIGHTON_1, true);
+ rPoolDefaults[ SDRATTR_3DSCENE_LIGHTON_2 - SDRATTR_START ] = new SfxBoolItem(SDRATTR_3DSCENE_LIGHTON_2, false);
+ rPoolDefaults[ SDRATTR_3DSCENE_LIGHTON_3 - SDRATTR_START ] = new SfxBoolItem(SDRATTR_3DSCENE_LIGHTON_3, false);
+ rPoolDefaults[ SDRATTR_3DSCENE_LIGHTON_4 - SDRATTR_START ] = new SfxBoolItem(SDRATTR_3DSCENE_LIGHTON_4, false);
+ rPoolDefaults[ SDRATTR_3DSCENE_LIGHTON_5 - SDRATTR_START ] = new SfxBoolItem(SDRATTR_3DSCENE_LIGHTON_5, false);
+ rPoolDefaults[ SDRATTR_3DSCENE_LIGHTON_6 - SDRATTR_START ] = new SfxBoolItem(SDRATTR_3DSCENE_LIGHTON_6, false);
+ rPoolDefaults[ SDRATTR_3DSCENE_LIGHTON_7 - SDRATTR_START ] = new SfxBoolItem(SDRATTR_3DSCENE_LIGHTON_7, false);
+ rPoolDefaults[ SDRATTR_3DSCENE_LIGHTON_8 - SDRATTR_START ] = new SfxBoolItem(SDRATTR_3DSCENE_LIGHTON_8, false);
+ rPoolDefaults[ SDRATTR_3DSCENE_LIGHTDIRECTION_1 - SDRATTR_START ] = new SvxB3DVectorItem(SDRATTR_3DSCENE_LIGHTDIRECTION_1, basegfx::B3DVector(0.57735026918963, 0.57735026918963, 0.57735026918963));
+ rPoolDefaults[ SDRATTR_3DSCENE_LIGHTDIRECTION_2 - SDRATTR_START ] = new SvxB3DVectorItem(SDRATTR_3DSCENE_LIGHTDIRECTION_2, basegfx::B3DVector(0.0,0.0,1.0));
+ rPoolDefaults[ SDRATTR_3DSCENE_LIGHTDIRECTION_3 - SDRATTR_START ] = new SvxB3DVectorItem(SDRATTR_3DSCENE_LIGHTDIRECTION_3, basegfx::B3DVector(0.0,0.0,1.0));
+ rPoolDefaults[ SDRATTR_3DSCENE_LIGHTDIRECTION_4 - SDRATTR_START ] = new SvxB3DVectorItem(SDRATTR_3DSCENE_LIGHTDIRECTION_4, basegfx::B3DVector(0.0,0.0,1.0));
+ rPoolDefaults[ SDRATTR_3DSCENE_LIGHTDIRECTION_5 - SDRATTR_START ] = new SvxB3DVectorItem(SDRATTR_3DSCENE_LIGHTDIRECTION_5, basegfx::B3DVector(0.0,0.0,1.0));
+ rPoolDefaults[ SDRATTR_3DSCENE_LIGHTDIRECTION_6 - SDRATTR_START ] = new SvxB3DVectorItem(SDRATTR_3DSCENE_LIGHTDIRECTION_6, basegfx::B3DVector(0.0,0.0,1.0));
+ rPoolDefaults[ SDRATTR_3DSCENE_LIGHTDIRECTION_7 - SDRATTR_START ] = new SvxB3DVectorItem(SDRATTR_3DSCENE_LIGHTDIRECTION_7, basegfx::B3DVector(0.0,0.0,1.0));
+ rPoolDefaults[ SDRATTR_3DSCENE_LIGHTDIRECTION_8 - SDRATTR_START ] = new SvxB3DVectorItem(SDRATTR_3DSCENE_LIGHTDIRECTION_8, basegfx::B3DVector(0.0,0.0,1.0));
+ rPoolDefaults[ SDRATTR_3DSCENE_SHADOW_SLANT - SDRATTR_START ] = new SfxUInt16Item(SDRATTR_3DSCENE_SHADOW_SLANT, 0);
+ rPoolDefaults[ SDRATTR_3DSCENE_SHADE_MODE - SDRATTR_START ] = new Svx3DShadeModeItem;
+ rPoolDefaults[ SDRATTR_CUSTOMSHAPE_ENGINE - SDRATTR_START ] = new SfxStringItem(SDRATTR_CUSTOMSHAPE_ENGINE, "");
+ rPoolDefaults[ SDRATTR_CUSTOMSHAPE_DATA - SDRATTR_START ] = new SfxStringItem(SDRATTR_CUSTOMSHAPE_DATA, "");
+ rPoolDefaults[ SDRATTR_CUSTOMSHAPE_GEOMETRY - SDRATTR_START ] = new SdrCustomShapeGeometryItem;
+
+ SvxBoxItem* pboxItem = new SvxBoxItem( SDRATTR_TABLE_BORDER );
+ pboxItem->SetAllDistances( 100 );
+ rPoolDefaults[ SDRATTR_TABLE_BORDER - SDRATTR_START ] = pboxItem;
+
+ SvxBoxInfoItem* pBoxInfoItem = new SvxBoxInfoItem( SDRATTR_TABLE_BORDER_INNER );
+
+ pBoxInfoItem->SetTable( true );
+ pBoxInfoItem->SetDist( true); // always show margin field
+ pBoxInfoItem->SetValid( SvxBoxInfoItemValidFlags::DISABLE ); // some lines may have DontCare state only in tables
+
+ rPoolDefaults[ SDRATTR_TABLE_BORDER_INNER - SDRATTR_START ] = pBoxInfoItem;
+ rPoolDefaults[ SDRATTR_TABLE_BORDER_TLBR - SDRATTR_START ] = new SvxLineItem( SDRATTR_TABLE_BORDER_TLBR );
+ rPoolDefaults[ SDRATTR_TABLE_BORDER_BLTR - SDRATTR_START ] = new SvxLineItem( SDRATTR_TABLE_BORDER_BLTR );
+ rPoolDefaults[ SDRATTR_TABLE_TEXT_ROTATION - SDRATTR_START ] = new SvxTextRotateItem(0_deg10, SDRATTR_TABLE_TEXT_ROTATION);
+
+ rPoolDefaults[ SDRATTR_GLOW_RADIUS - SDRATTR_START ] = new SdrMetricItem(SDRATTR_GLOW_RADIUS, 0);
+ rPoolDefaults[ SDRATTR_GLOW_COLOR - SDRATTR_START ] = new XColorItem(SDRATTR_GLOW_COLOR, aNullCol);
+ rPoolDefaults[ SDRATTR_GLOW_TRANSPARENCY - SDRATTR_START ] = new SdrPercentItem(SDRATTR_GLOW_TRANSPARENCY, 0);
+
+ rPoolDefaults[SDRATTR_SOFTEDGE_RADIUS - SDRATTR_START] = new SdrMetricItem(SDRATTR_SOFTEDGE_RADIUS, 0);
+
+ rPoolDefaults[SDRATTR_TEXTCOLUMNS_NUMBER - SDRATTR_START] = new SfxInt16Item(SDRATTR_TEXTCOLUMNS_NUMBER, 1);
+ rPoolDefaults[SDRATTR_TEXTCOLUMNS_SPACING - SDRATTR_START] = new SdrMetricItem(SDRATTR_TEXTCOLUMNS_SPACING, 0);
+
+ // set own ItemInfos
+ mpLocalItemInfos[SDRATTR_SHADOW-SDRATTR_START]._nSID=SID_ATTR_FILL_SHADOW;
+ mpLocalItemInfos[SDRATTR_SHADOWCOLOR-SDRATTR_START]._nSID=SID_ATTR_SHADOW_COLOR;
+ mpLocalItemInfos[SDRATTR_SHADOWTRANSPARENCE-SDRATTR_START]._nSID=SID_ATTR_SHADOW_TRANSPARENCE;
+ mpLocalItemInfos[SDRATTR_SHADOWBLUR-SDRATTR_START]._nSID=SID_ATTR_SHADOW_BLUR;
+ mpLocalItemInfos[SDRATTR_SHADOWXDIST-SDRATTR_START]._nSID=SID_ATTR_SHADOW_XDISTANCE;
+ mpLocalItemInfos[SDRATTR_SHADOWYDIST-SDRATTR_START]._nSID=SID_ATTR_SHADOW_YDISTANCE;
+ mpLocalItemInfos[SDRATTR_TEXT_FITTOSIZE-SDRATTR_START]._nSID=SID_ATTR_TEXT_FITTOSIZE;
+ mpLocalItemInfos[SDRATTR_GRAFCROP-SDRATTR_START]._nSID=SID_ATTR_GRAF_CROP;
+
+ mpLocalItemInfos[SDRATTR_TABLE_BORDER - SDRATTR_START ]._nSID = SID_ATTR_BORDER_OUTER;
+ mpLocalItemInfos[SDRATTR_TABLE_BORDER_INNER - SDRATTR_START ]._nSID = SID_ATTR_BORDER_INNER;
+ mpLocalItemInfos[SDRATTR_TABLE_BORDER_TLBR - SDRATTR_START ]._nSID = SID_ATTR_BORDER_DIAG_TLBR;
+ mpLocalItemInfos[SDRATTR_TABLE_BORDER_BLTR - SDRATTR_START ]._nSID = SID_ATTR_BORDER_DIAG_BLTR;
+
+ mpLocalItemInfos[SDRATTR_GLOW_RADIUS - SDRATTR_START]._nSID = SID_ATTR_GLOW_RADIUS;
+ mpLocalItemInfos[SDRATTR_GLOW_COLOR - SDRATTR_START]._nSID = SID_ATTR_GLOW_COLOR;
+ mpLocalItemInfos[SDRATTR_GLOW_TRANSPARENCY - SDRATTR_START]._nSID = SID_ATTR_GLOW_TRANSPARENCY;
+
+ mpLocalItemInfos[SDRATTR_SOFTEDGE_RADIUS - SDRATTR_START]._nSID = SID_ATTR_SOFTEDGE_RADIUS;
+
+ mpLocalItemInfos[SDRATTR_TEXTCOLUMNS_NUMBER - SDRATTR_START]._nSID = 0 /*TODO*/;
+ mpLocalItemInfos[SDRATTR_TEXTCOLUMNS_SPACING - SDRATTR_START]._nSID = 0 /*TODO*/;
+
+ // it's my own creation level, set Defaults and ItemInfos
+ SetDefaults(mpLocalPoolDefaults);
+ SetItemInfos(mpLocalItemInfos.get());
+}
+
+// copy ctor, so that static defaults are cloned
+// (Parameter 2 = sal_True)
+SdrItemPool::SdrItemPool(const SdrItemPool& rPool)
+: XOutdevItemPool(rPool)
+{
+}
+
+rtl::Reference<SfxItemPool> SdrItemPool::Clone() const
+{
+ return new SdrItemPool(*this);
+}
+
+SdrItemPool::~SdrItemPool()
+{
+ // split pools before destroying
+ SetSecondaryPool(nullptr);
+}
+
+bool SdrItemPool::GetPresentation(
+ const SfxPoolItem& rItem,
+ MapUnit ePresentationMetric, OUString& rText,
+ const IntlWrapper& rIntlWrapper) const
+{
+ if (!IsInvalidItem(&rItem)) {
+ sal_uInt16 nWhich=rItem.Which();
+ if (nWhich>=SDRATTR_SHADOW_FIRST && nWhich<=SDRATTR_END) {
+ rItem.GetPresentation(SfxItemPresentation::Nameless,
+ GetMetric(nWhich),ePresentationMetric,rText,
+ rIntlWrapper);
+ rText = GetItemName(nWhich) + " " + rText;
+
+ return true;
+ }
+ }
+ return XOutdevItemPool::GetPresentation(rItem,ePresentationMetric,rText,rIntlWrapper);
+}
+
+OUString SdrItemPool::GetItemName(sal_uInt16 nWhich)
+{
+ TranslateId pResId = SIP_UNKNOWN_ATTR;
+
+ switch (nWhich)
+ {
+ case XATTR_LINESTYLE : pResId = SIP_XA_LINESTYLE;break;
+ case XATTR_LINEDASH : pResId = SIP_XA_LINEDASH;break;
+ case XATTR_LINEWIDTH : pResId = SIP_XA_LINEWIDTH;break;
+ case XATTR_LINECOLOR : pResId = SIP_XA_LINECOLOR;break;
+ case XATTR_LINESTART : pResId = SIP_XA_LINESTART;break;
+ case XATTR_LINEEND : pResId = SIP_XA_LINEEND;break;
+ case XATTR_LINESTARTWIDTH : pResId = SIP_XA_LINESTARTWIDTH;break;
+ case XATTR_LINEENDWIDTH : pResId = SIP_XA_LINEENDWIDTH;break;
+ case XATTR_LINESTARTCENTER : pResId = SIP_XA_LINESTARTCENTER;break;
+ case XATTR_LINEENDCENTER : pResId = SIP_XA_LINEENDCENTER;break;
+ case XATTR_LINETRANSPARENCE : pResId = SIP_XA_LINETRANSPARENCE;break;
+ case XATTR_LINEJOINT : pResId = SIP_XA_LINEJOINT;break;
+ case XATTRSET_LINE : pResId = SIP_XATTRSET_LINE;break;
+
+ case XATTR_FILLSTYLE : pResId = SIP_XA_FILLSTYLE;break;
+ case XATTR_FILLCOLOR : pResId = SIP_XA_FILLCOLOR;break;
+ case XATTR_FILLGRADIENT : pResId = SIP_XA_FILLGRADIENT;break;
+ case XATTR_FILLHATCH : pResId = SIP_XA_FILLHATCH;break;
+ case XATTR_FILLBITMAP : pResId = SIP_XA_FILLBITMAP;break;
+ case XATTR_FILLTRANSPARENCE : pResId = SIP_XA_FILLTRANSPARENCE;break;
+ case XATTR_GRADIENTSTEPCOUNT : pResId = SIP_XA_GRADIENTSTEPCOUNT;break;
+ case XATTR_FILLBMP_TILE : pResId = SIP_XA_FILLBMP_TILE;break;
+ case XATTR_FILLBMP_POS : pResId = SIP_XA_FILLBMP_POS;break;
+ case XATTR_FILLBMP_SIZEX : pResId = SIP_XA_FILLBMP_SIZEX;break;
+ case XATTR_FILLBMP_SIZEY : pResId = SIP_XA_FILLBMP_SIZEY;break;
+ case XATTR_FILLFLOATTRANSPARENCE: pResId = SIP_XA_FILLFLOATTRANSPARENCE;break;
+ case XATTR_SECONDARYFILLCOLOR : pResId = SIP_XA_SECONDARYFILLCOLOR;break;
+ case XATTR_FILLBMP_SIZELOG : pResId = SIP_XA_FILLBMP_SIZELOG;break;
+ case XATTR_FILLBMP_TILEOFFSETX : pResId = SIP_XA_FILLBMP_TILEOFFSETX;break;
+ case XATTR_FILLBMP_TILEOFFSETY : pResId = SIP_XA_FILLBMP_TILEOFFSETY;break;
+ case XATTR_FILLBMP_STRETCH : pResId = SIP_XA_FILLBMP_STRETCH;break;
+ case XATTR_FILLBMP_POSOFFSETX : pResId = SIP_XA_FILLBMP_POSOFFSETX;break;
+ case XATTR_FILLBMP_POSOFFSETY : pResId = SIP_XA_FILLBMP_POSOFFSETY;break;
+ case XATTR_FILLBACKGROUND : pResId = SIP_XA_FILLBACKGROUND;break;
+ case XATTR_FILLUSESLIDEBACKGROUND: pResId = SIP_XA_FILLUSESLIDEBACKGROUND;break;
+
+ case XATTRSET_FILL : pResId = SIP_XATTRSET_FILL;break;
+
+ case XATTR_FORMTXTSTYLE : pResId = SIP_XA_FORMTXTSTYLE;break;
+ case XATTR_FORMTXTADJUST : pResId = SIP_XA_FORMTXTADJUST;break;
+ case XATTR_FORMTXTDISTANCE : pResId = SIP_XA_FORMTXTDISTANCE;break;
+ case XATTR_FORMTXTSTART : pResId = SIP_XA_FORMTXTSTART;break;
+ case XATTR_FORMTXTMIRROR : pResId = SIP_XA_FORMTXTMIRROR;break;
+ case XATTR_FORMTXTOUTLINE : pResId = SIP_XA_FORMTXTOUTLINE;break;
+ case XATTR_FORMTXTSHADOW : pResId = SIP_XA_FORMTXTSHADOW;break;
+ case XATTR_FORMTXTSHDWCOLOR : pResId = SIP_XA_FORMTXTSHDWCOLOR;break;
+ case XATTR_FORMTXTSHDWXVAL : pResId = SIP_XA_FORMTXTSHDWXVAL;break;
+ case XATTR_FORMTXTSHDWYVAL : pResId = SIP_XA_FORMTXTSHDWYVAL;break;
+ case XATTR_FORMTXTHIDEFORM : pResId = SIP_XA_FORMTXTHIDEFORM;break;
+ case XATTR_FORMTXTSHDWTRANSP: pResId = SIP_XA_FORMTXTSHDWTRANSP;break;
+
+ case SDRATTR_SHADOW : pResId = SIP_SA_SHADOW;break;
+ case SDRATTR_SHADOWCOLOR : pResId = SIP_SA_SHADOWCOLOR;break;
+ case SDRATTR_SHADOWXDIST : pResId = SIP_SA_SHADOWXDIST;break;
+ case SDRATTR_SHADOWYDIST : pResId = SIP_SA_SHADOWYDIST;break;
+ case SDRATTR_SHADOWTRANSPARENCE: pResId = SIP_SA_SHADOWTRANSPARENCE;break;
+ case SDRATTR_SHADOWBLUR : pResId = SIP_SA_SHADOWBLUR;break;
+ case SDRATTR_SHADOW3D : pResId = SIP_SA_SHADOW3D;break;
+ case SDRATTR_SHADOWPERSP : pResId = SIP_SA_SHADOWPERSP;break;
+
+ case SDRATTR_GLOW_RADIUS : pResId = SIP_SA_GLOW_RADIUS;break;
+ case SDRATTR_GLOW_COLOR : pResId = SIP_SA_GLOW_COLOR;break;
+ case SDRATTR_GLOW_TRANSPARENCY : pResId = SIP_SA_GLOW_TRANSPARENCY;break;
+
+ case SDRATTR_SOFTEDGE_RADIUS : pResId = SIP_SA_SOFTEDGE_RADIUS; break;
+
+ case SDRATTR_CAPTIONTYPE : pResId = SIP_SA_CAPTIONTYPE;break;
+ case SDRATTR_CAPTIONFIXEDANGLE: pResId = SIP_SA_CAPTIONFIXEDANGLE;break;
+ case SDRATTR_CAPTIONANGLE : pResId = SIP_SA_CAPTIONANGLE;break;
+ case SDRATTR_CAPTIONGAP : pResId = SIP_SA_CAPTIONGAP;break;
+ case SDRATTR_CAPTIONESCDIR : pResId = SIP_SA_CAPTIONESCDIR;break;
+ case SDRATTR_CAPTIONESCISREL : pResId = SIP_SA_CAPTIONESCISREL;break;
+ case SDRATTR_CAPTIONESCREL : pResId = SIP_SA_CAPTIONESCREL;break;
+ case SDRATTR_CAPTIONESCABS : pResId = SIP_SA_CAPTIONESCABS;break;
+ case SDRATTR_CAPTIONLINELEN : pResId = SIP_SA_CAPTIONLINELEN;break;
+ case SDRATTR_CAPTIONFITLINELEN: pResId = SIP_SA_CAPTIONFITLINELEN;break;
+
+ case SDRATTR_CORNER_RADIUS : pResId = SIP_SA_CORNER_RADIUS;break;
+ case SDRATTR_TEXT_MINFRAMEHEIGHT : pResId = SIP_SA_TEXT_MINFRAMEHEIGHT;break;
+ case SDRATTR_TEXT_AUTOGROWHEIGHT : pResId = SIP_SA_TEXT_AUTOGROWHEIGHT;break;
+ case SDRATTR_TEXT_FITTOSIZE : pResId = SIP_SA_TEXT_FITTOSIZE;break;
+ case SDRATTR_TEXT_LEFTDIST : pResId = SIP_SA_TEXT_LEFTDIST;break;
+ case SDRATTR_TEXT_RIGHTDIST : pResId = SIP_SA_TEXT_RIGHTDIST;break;
+ case SDRATTR_TEXT_UPPERDIST : pResId = SIP_SA_TEXT_UPPERDIST;break;
+ case SDRATTR_TEXT_LOWERDIST : pResId = SIP_SA_TEXT_LOWERDIST;break;
+ case SDRATTR_TEXT_VERTADJUST : pResId = SIP_SA_TEXT_VERTADJUST;break;
+ case SDRATTR_TEXT_MAXFRAMEHEIGHT : pResId = SIP_SA_TEXT_MAXFRAMEHEIGHT;break;
+ case SDRATTR_TEXT_MINFRAMEWIDTH : pResId = SIP_SA_TEXT_MINFRAMEWIDTH;break;
+ case SDRATTR_TEXT_MAXFRAMEWIDTH : pResId = SIP_SA_TEXT_MAXFRAMEWIDTH;break;
+ case SDRATTR_TEXT_AUTOGROWWIDTH : pResId = SIP_SA_TEXT_AUTOGROWWIDTH;break;
+ case SDRATTR_TEXT_HORZADJUST : pResId = SIP_SA_TEXT_HORZADJUST;break;
+ case SDRATTR_TEXT_ANIKIND : pResId = SIP_SA_TEXT_ANIKIND;break;
+ case SDRATTR_TEXT_ANIDIRECTION : pResId = SIP_SA_TEXT_ANIDIRECTION;break;
+ case SDRATTR_TEXT_ANISTARTINSIDE : pResId = SIP_SA_TEXT_ANISTARTINSIDE;break;
+ case SDRATTR_TEXT_ANISTOPINSIDE : pResId = SIP_SA_TEXT_ANISTOPINSIDE;break;
+ case SDRATTR_TEXT_ANICOUNT : pResId = SIP_SA_TEXT_ANICOUNT;break;
+ case SDRATTR_TEXT_ANIDELAY : pResId = SIP_SA_TEXT_ANIDELAY;break;
+ case SDRATTR_TEXT_ANIAMOUNT : pResId = SIP_SA_TEXT_ANIAMOUNT;break;
+ case SDRATTR_TEXT_CONTOURFRAME : pResId = SIP_SA_TEXT_CONTOURFRAME;break;
+ case SDRATTR_XMLATTRIBUTES : pResId = SIP_SA_XMLATTRIBUTES;break;
+ case SDRATTR_TEXT_USEFIXEDCELLHEIGHT: pResId = SIP_SA_TEXT_USEFIXEDCELLHEIGHT;break;
+ case SDRATTR_TEXT_WORDWRAP : pResId = SIP_SA_WORDWRAP;break;
+ case SDRATTR_TEXT_CHAINNEXTNAME : pResId = SIP_SA_CHAINNEXTNAME;break;
+
+ case SDRATTR_EDGEKIND : pResId = SIP_SA_EDGEKIND;break;
+ case SDRATTR_EDGENODE1HORZDIST : pResId = SIP_SA_EDGENODE1HORZDIST;break;
+ case SDRATTR_EDGENODE1VERTDIST : pResId = SIP_SA_EDGENODE1VERTDIST;break;
+ case SDRATTR_EDGENODE2HORZDIST : pResId = SIP_SA_EDGENODE2HORZDIST;break;
+ case SDRATTR_EDGENODE2VERTDIST : pResId = SIP_SA_EDGENODE2VERTDIST;break;
+ case SDRATTR_EDGENODE1GLUEDIST : pResId = SIP_SA_EDGENODE1GLUEDIST;break;
+ case SDRATTR_EDGENODE2GLUEDIST : pResId = SIP_SA_EDGENODE2GLUEDIST;break;
+ case SDRATTR_EDGELINEDELTACOUNT : pResId = SIP_SA_EDGELINEDELTACOUNT;break;
+ case SDRATTR_EDGELINE1DELTA : pResId = SIP_SA_EDGELINE1DELTA;break;
+ case SDRATTR_EDGELINE2DELTA : pResId = SIP_SA_EDGELINE2DELTA;break;
+ case SDRATTR_EDGELINE3DELTA : pResId = SIP_SA_EDGELINE3DELTA;break;
+
+ case SDRATTR_MEASUREKIND : pResId = SIP_SA_MEASUREKIND;break;
+ case SDRATTR_MEASURETEXTHPOS : pResId = SIP_SA_MEASURETEXTHPOS;break;
+ case SDRATTR_MEASURETEXTVPOS : pResId = SIP_SA_MEASURETEXTVPOS;break;
+ case SDRATTR_MEASURELINEDIST : pResId = SIP_SA_MEASURELINEDIST;break;
+ case SDRATTR_MEASUREHELPLINEOVERHANG : pResId = SIP_SA_MEASUREHELPLINEOVERHANG;break;
+ case SDRATTR_MEASUREHELPLINEDIST : pResId = SIP_SA_MEASUREHELPLINEDIST;break;
+ case SDRATTR_MEASUREHELPLINE1LEN : pResId = SIP_SA_MEASUREHELPLINE1LEN;break;
+ case SDRATTR_MEASUREHELPLINE2LEN : pResId = SIP_SA_MEASUREHELPLINE2LEN;break;
+ case SDRATTR_MEASUREBELOWREFEDGE : pResId = SIP_SA_MEASUREBELOWREFEDGE;break;
+ case SDRATTR_MEASURETEXTROTA90 : pResId = SIP_SA_MEASURETEXTROTA90;break;
+ case SDRATTR_MEASURETEXTUPSIDEDOWN : pResId = SIP_SA_MEASURETEXTUPSIDEDOWN;break;
+ case SDRATTR_MEASUREOVERHANG : pResId = SIP_SA_MEASUREOVERHANG;break;
+ case SDRATTR_MEASUREUNIT : pResId = SIP_SA_MEASUREUNIT;break;
+ case SDRATTR_MEASURESCALE : pResId = SIP_SA_MEASURESCALE;break;
+ case SDRATTR_MEASURESHOWUNIT : pResId = SIP_SA_MEASURESHOWUNIT;break;
+ case SDRATTR_MEASUREFORMATSTRING : pResId = SIP_SA_MEASUREFORMATSTRING;break;
+ case SDRATTR_MEASURETEXTAUTOANGLE : pResId = SIP_SA_MEASURETEXTAUTOANGLE;break;
+ case SDRATTR_MEASURETEXTAUTOANGLEVIEW: pResId = SIP_SA_MEASURETEXTAUTOANGLEVIEW;break;
+ case SDRATTR_MEASURETEXTISFIXEDANGLE : pResId = SIP_SA_MEASURETEXTISFIXEDANGLE;break;
+ case SDRATTR_MEASURETEXTFIXEDANGLE : pResId = SIP_SA_MEASURETEXTFIXEDANGLE;break;
+ case SDRATTR_MEASUREDECIMALPLACES : pResId = SIP_SA_MEASUREDECIMALPLACES;break;
+
+ case SDRATTR_CIRCKIND : pResId = SIP_SA_CIRCKIND;break;
+ case SDRATTR_CIRCSTARTANGLE: pResId = SIP_SA_CIRCSTARTANGLE;break;
+ case SDRATTR_CIRCENDANGLE : pResId = SIP_SA_CIRCENDANGLE;break;
+
+ case SDRATTR_OBJMOVEPROTECT : pResId = SIP_SA_OBJMOVEPROTECT;break;
+ case SDRATTR_OBJSIZEPROTECT : pResId = SIP_SA_OBJSIZEPROTECT;break;
+ case SDRATTR_OBJPRINTABLE : pResId = SIP_SA_OBJPRINTABLE;break;
+ case SDRATTR_OBJVISIBLE : pResId = SIP_SA_OBJVISIBLE;break;
+ case SDRATTR_LAYERID : pResId = SIP_SA_LAYERID;break;
+ case SDRATTR_LAYERNAME : pResId = SIP_SA_LAYERNAME;break;
+ case SDRATTR_OBJECTNAME : pResId = SIP_SA_OBJECTNAME;break;
+ case SDRATTR_ALLPOSITIONX : pResId = SIP_SA_ALLPOSITIONX;break;
+ case SDRATTR_ALLPOSITIONY : pResId = SIP_SA_ALLPOSITIONY;break;
+ case SDRATTR_ALLSIZEWIDTH : pResId = SIP_SA_ALLSIZEWIDTH;break;
+ case SDRATTR_ALLSIZEHEIGHT : pResId = SIP_SA_ALLSIZEHEIGHT;break;
+ case SDRATTR_ONEPOSITIONX : pResId = SIP_SA_ONEPOSITIONX;break;
+ case SDRATTR_ONEPOSITIONY : pResId = SIP_SA_ONEPOSITIONY;break;
+ case SDRATTR_ONESIZEWIDTH : pResId = SIP_SA_ONESIZEWIDTH;break;
+ case SDRATTR_ONESIZEHEIGHT : pResId = SIP_SA_ONESIZEHEIGHT;break;
+ case SDRATTR_LOGICSIZEWIDTH : pResId = SIP_SA_LOGICSIZEWIDTH;break;
+ case SDRATTR_LOGICSIZEHEIGHT: pResId = SIP_SA_LOGICSIZEHEIGHT;break;
+ case SDRATTR_ROTATEANGLE : pResId = SIP_SA_ROTATEANGLE;break;
+ case SDRATTR_SHEARANGLE : pResId = SIP_SA_SHEARANGLE;break;
+ case SDRATTR_MOVEX : pResId = SIP_SA_MOVEX;break;
+ case SDRATTR_MOVEY : pResId = SIP_SA_MOVEY;break;
+ case SDRATTR_RESIZEXONE : pResId = SIP_SA_RESIZEXONE;break;
+ case SDRATTR_RESIZEYONE : pResId = SIP_SA_RESIZEYONE;break;
+ case SDRATTR_ROTATEONE : pResId = SIP_SA_ROTATEONE;break;
+ case SDRATTR_HORZSHEARONE : pResId = SIP_SA_HORZSHEARONE;break;
+ case SDRATTR_VERTSHEARONE : pResId = SIP_SA_VERTSHEARONE;break;
+ case SDRATTR_RESIZEXALL : pResId = SIP_SA_RESIZEXALL;break;
+ case SDRATTR_RESIZEYALL : pResId = SIP_SA_RESIZEYALL;break;
+ case SDRATTR_ROTATEALL : pResId = SIP_SA_ROTATEALL;break;
+ case SDRATTR_HORZSHEARALL : pResId = SIP_SA_HORZSHEARALL;break;
+ case SDRATTR_VERTSHEARALL : pResId = SIP_SA_VERTSHEARALL;break;
+ case SDRATTR_TRANSFORMREF1X : pResId = SIP_SA_TRANSFORMREF1X;break;
+ case SDRATTR_TRANSFORMREF1Y : pResId = SIP_SA_TRANSFORMREF1Y;break;
+ case SDRATTR_TRANSFORMREF2X : pResId = SIP_SA_TRANSFORMREF2X;break;
+ case SDRATTR_TRANSFORMREF2Y : pResId = SIP_SA_TRANSFORMREF2Y;break;
+
+ case SDRATTR_GRAFRED : pResId = SIP_SA_GRAFRED;break;
+ case SDRATTR_GRAFGREEN : pResId = SIP_SA_GRAFGREEN;break;
+ case SDRATTR_GRAFBLUE : pResId = SIP_SA_GRAFBLUE;break;
+ case SDRATTR_GRAFLUMINANCE : pResId = SIP_SA_GRAFLUMINANCE;break;
+ case SDRATTR_GRAFCONTRAST : pResId = SIP_SA_GRAFCONTRAST;break;
+ case SDRATTR_GRAFGAMMA : pResId = SIP_SA_GRAFGAMMA;break;
+ case SDRATTR_GRAFTRANSPARENCE : pResId = SIP_SA_GRAFTRANSPARENCE;break;
+ case SDRATTR_GRAFINVERT : pResId = SIP_SA_GRAFINVERT;break;
+ case SDRATTR_GRAFMODE : pResId = SIP_SA_GRAFMODE;break;
+ case SDRATTR_GRAFCROP : pResId = SIP_SA_GRAFCROP;break;
+
+ case EE_PARA_HYPHENATE : pResId = SIP_EE_PARA_HYPHENATE;break;
+ case EE_PARA_BULLETSTATE: pResId = SIP_EE_PARA_BULLETSTATE;break;
+ case EE_PARA_OUTLLRSPACE: pResId = SIP_EE_PARA_OUTLLRSPACE;break;
+ case EE_PARA_OUTLLEVEL : pResId = SIP_EE_PARA_OUTLLEVEL;break;
+ case EE_PARA_BULLET : pResId = SIP_EE_PARA_BULLET;break;
+ case EE_PARA_LRSPACE : pResId = SIP_EE_PARA_LRSPACE;break;
+ case EE_PARA_ULSPACE : pResId = SIP_EE_PARA_ULSPACE;break;
+ case EE_PARA_SBL : pResId = SIP_EE_PARA_SBL;break;
+ case EE_PARA_JUST : pResId = SIP_EE_PARA_JUST;break;
+ case EE_PARA_TABS : pResId = SIP_EE_PARA_TABS;break;
+
+ case EE_CHAR_COLOR : pResId = SIP_EE_CHAR_COLOR;break;
+ case EE_CHAR_FONTINFO : pResId = SIP_EE_CHAR_FONTINFO;break;
+ case EE_CHAR_FONTHEIGHT : pResId = SIP_EE_CHAR_FONTHEIGHT;break;
+ case EE_CHAR_FONTWIDTH : pResId = SIP_EE_CHAR_FONTWIDTH;break;
+ case EE_CHAR_WEIGHT : pResId = SIP_EE_CHAR_WEIGHT;break;
+ case EE_CHAR_UNDERLINE : pResId = SIP_EE_CHAR_UNDERLINE;break;
+ case EE_CHAR_OVERLINE : pResId = SIP_EE_CHAR_OVERLINE;break;
+ case EE_CHAR_STRIKEOUT : pResId = SIP_EE_CHAR_STRIKEOUT;break;
+ case EE_CHAR_ITALIC : pResId = SIP_EE_CHAR_ITALIC;break;
+ case EE_CHAR_OUTLINE : pResId = SIP_EE_CHAR_OUTLINE;break;
+ case EE_CHAR_SHADOW : pResId = SIP_EE_CHAR_SHADOW;break;
+ case EE_CHAR_ESCAPEMENT : pResId = SIP_EE_CHAR_ESCAPEMENT;break;
+ case EE_CHAR_PAIRKERNING: pResId = SIP_EE_CHAR_PAIRKERNING;break;
+ case EE_CHAR_KERNING : pResId = SIP_EE_CHAR_KERNING;break;
+ case EE_CHAR_WLM : pResId = SIP_EE_CHAR_WLM;break;
+ case EE_FEATURE_TAB : pResId = SIP_EE_FEATURE_TAB;break;
+ case EE_FEATURE_LINEBR : pResId = SIP_EE_FEATURE_LINEBR;break;
+ case EE_FEATURE_NOTCONV : pResId = SIP_EE_FEATURE_NOTCONV;break;
+ case EE_FEATURE_FIELD : pResId = SIP_EE_FEATURE_FIELD;break;
+
+ case SDRATTR_TEXTCOLUMNS_NUMBER: pResId = SIP_SA_TEXTCOLUMNS_NUMBER; break;
+ case SDRATTR_TEXTCOLUMNS_SPACING: pResId = SIP_SA_TEXTCOLUMNS_SPACING; break;
+ } // switch
+
+ return SvxResId(pResId);
+}
+
+
+// FractionItem
+
+
+bool SdrFractionItem::operator==(const SfxPoolItem& rCmp) const
+{
+ return SfxPoolItem::operator==(rCmp) &&
+ static_cast<const SdrFractionItem&>(rCmp).GetValue()==nValue;
+}
+
+bool SdrFractionItem::GetPresentation(
+ SfxItemPresentation ePresentation, MapUnit /*eCoreMetric*/,
+ MapUnit /*ePresentationMetric*/, OUString &rText, const IntlWrapper&) const
+{
+ if(nValue.IsValid())
+ {
+ sal_Int32 nDiv = nValue.GetDenominator();
+ rText = OUString::number(nValue.GetNumerator());
+
+ if(nDiv != 1)
+ {
+ rText += "/" + OUString::number(nDiv);
+ }
+ }
+ else
+ {
+ rText = "?";
+ }
+
+ if(ePresentation == SfxItemPresentation::Complete)
+ {
+ rText = SdrItemPool::GetItemName(Which()) + " " + rText;
+ return true;
+ }
+ else if(ePresentation == SfxItemPresentation::Nameless)
+ return true;
+
+ return false;
+}
+
+SdrFractionItem* SdrFractionItem::Clone(SfxItemPool * /*pPool*/) const
+{
+ return new SdrFractionItem(Which(),GetValue());
+}
+
+
+// ScaleItem
+
+
+bool SdrScaleItem::GetPresentation(
+ SfxItemPresentation ePresentation, MapUnit /*eCoreMetric*/,
+ MapUnit /*ePresentationMetric*/, OUString &rText, const IntlWrapper&) const
+{
+ if(GetValue().IsValid())
+ {
+ sal_Int32 nDiv = GetValue().GetDenominator();
+
+ rText = OUString::number(GetValue().GetNumerator()) + ":" + OUString::number(nDiv);
+ }
+ else
+ {
+ rText = "?";
+ }
+
+ if(ePresentation == SfxItemPresentation::Complete)
+ {
+ rText = SdrItemPool::GetItemName(Which()) + " " + rText;
+ }
+
+ return true;
+}
+
+SdrScaleItem* SdrScaleItem::Clone(SfxItemPool * /*pPool*/) const
+{
+ return new SdrScaleItem(Which(),GetValue());
+}
+
+
+// OnOffItem
+
+
+SdrOnOffItem* SdrOnOffItem::Clone(SfxItemPool* /*pPool*/) const
+{
+ return new SdrOnOffItem(TypedWhichId<SdrOnOffItem>(Which()),GetValue());
+}
+
+OUString SdrOnOffItem::GetValueTextByVal(bool bVal) const
+{
+ if (bVal)
+ return SvxResId(STR_ItemValON);
+ return SvxResId(STR_ItemValOFF);
+}
+
+bool SdrOnOffItem::GetPresentation(SfxItemPresentation ePres,
+ MapUnit /*eCoreMetric*/, MapUnit /*ePresMetric*/, OUString& rText, const IntlWrapper&) const
+{
+ rText=GetValueTextByVal(GetValue());
+ if (ePres==SfxItemPresentation::Complete) {
+ rText = SdrItemPool::GetItemName(Which()) + " " + rText;
+ }
+ return true;
+}
+
+void SdrOnOffItem::dumpAsXml(xmlTextWriterPtr pWriter) const
+{
+ (void)xmlTextWriterStartElement(pWriter, BAD_CAST("SdrOnOffItem"));
+ if (Which() == SDRATTR_SHADOW)
+ {
+ (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("whichId"), BAD_CAST("SDRATTR_SHADOW"));
+ }
+
+ SfxBoolItem::dumpAsXml(pWriter);
+
+ (void)xmlTextWriterEndElement(pWriter);
+}
+
+SdrYesNoItem* SdrYesNoItem::Clone(SfxItemPool* /*pPool*/) const
+{
+ return new SdrYesNoItem(TypedWhichId<SdrYesNoItem>(Which()),GetValue());
+}
+
+OUString SdrYesNoItem::GetValueTextByVal(bool bVal) const
+{
+ if (bVal)
+ return SvxResId(STR_ItemValYES);
+ return SvxResId(STR_ItemValNO);
+}
+
+bool SdrYesNoItem::GetPresentation(SfxItemPresentation ePres,
+ MapUnit /*eCoreMetric*/, MapUnit /*ePresMetric*/, OUString& rText, const IntlWrapper&) const
+{
+ rText=GetValueTextByVal(GetValue());
+ if (ePres==SfxItemPresentation::Complete) {
+ rText = SdrItemPool::GetItemName(Which()) + " " + rText;
+ }
+ return true;
+}
+
+SdrPercentItem* SdrPercentItem::Clone(SfxItemPool* /*pPool*/) const
+{
+ return new SdrPercentItem(TypedWhichId<SdrPercentItem>(Which()),GetValue());
+}
+
+bool SdrPercentItem::GetPresentation(
+ SfxItemPresentation ePres, MapUnit /*eCoreMetric*/,
+ MapUnit /*ePresMetric*/, OUString& rText, const IntlWrapper&) const
+{
+ rText = unicode::formatPercent(GetValue(),
+ Application::GetSettings().GetUILanguageTag());
+
+ if(ePres == SfxItemPresentation::Complete)
+ {
+ rText = SdrItemPool::GetItemName(Which()) + " " + rText;
+ }
+
+ return true;
+}
+
+void SdrPercentItem::dumpAsXml(xmlTextWriterPtr pWriter) const
+{
+ (void)xmlTextWriterStartElement(pWriter, BAD_CAST("SdrPercentItem"));
+ if (Which() == SDRATTR_SHADOWTRANSPARENCE)
+ {
+ (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("whichId"),
+ BAD_CAST("SDRATTR_SHADOWTRANSPARENCE"));
+ }
+
+ SfxUInt16Item::dumpAsXml(pWriter);
+
+ (void)xmlTextWriterEndElement(pWriter);
+}
+
+SdrAngleItem* SdrAngleItem::Clone(SfxItemPool* /*pPool*/) const
+{
+ return new SdrAngleItem(TypedWhichId<SdrAngleItem>(Which()),GetValue());
+}
+
+bool SdrAngleItem::GetPresentation(
+ SfxItemPresentation ePres, MapUnit /*eCoreMetric*/, MapUnit /*ePresMetric*/,
+ OUString& rText, const IntlWrapper& rIntlWrapper) const
+{
+ sal_Int32 nValue(GetValue());
+ bool bNeg(nValue < 0);
+
+ if(bNeg)
+ nValue = -nValue;
+
+ OUStringBuffer aText = OUString::number(nValue);
+
+ if(nValue)
+ {
+ sal_Unicode aUnicodeNull('0');
+ sal_Int32 nCount(2);
+
+ if(LocaleDataWrapper::isNumLeadingZero())
+ nCount++;
+
+ while(aText.getLength() < nCount)
+ aText.insert(0, aUnicodeNull);
+
+ sal_Int32 nLen = aText.getLength();
+ bool bNull1(aText[nLen-1] == aUnicodeNull);
+ bool bNull2(bNull1 && aText[nLen-2] == aUnicodeNull);
+
+ if(bNull2)
+ {
+ // no decimal place(s)
+ sal_Int32 idx = nLen-2;
+ aText.remove(idx, aText.getLength()-idx);
+ }
+ else
+ {
+ sal_Unicode cDec =
+ rIntlWrapper.getLocaleData()->getNumDecimalSep()[0];
+ aText.insert(nLen-2, cDec);
+
+ if(bNull1)
+ aText.remove(nLen, aText.getLength()-nLen);
+ }
+
+ if(bNeg)
+ aText.insert(0, '-');
+ }
+
+ aText.append(sal_Unicode(DEGREE_CHAR));
+
+ if(ePres == SfxItemPresentation::Complete)
+ {
+ OUString aStr = SdrItemPool::GetItemName(Which());
+ aText.insert(0, ' ');
+ aText.insert(0, aStr);
+ }
+
+ rText = aText.makeStringAndClear();
+ return true;
+}
+
+SdrMetricItem* SdrMetricItem::Clone(SfxItemPool* /*pPool*/) const
+{
+ return new SdrMetricItem(TypedWhichId<SdrMetricItem>(Which()),GetValue());
+}
+
+bool SdrMetricItem::HasMetrics() const
+{
+ return true;
+}
+
+void SdrMetricItem::ScaleMetrics(tools::Long nMul, tools::Long nDiv)
+{
+ if (GetValue()!=0) {
+ SetValue(BigInt::Scale(GetValue(), nMul, nDiv));
+ }
+}
+
+bool SdrMetricItem::GetPresentation(SfxItemPresentation ePres,
+ MapUnit eCoreMetric, MapUnit ePresMetric, OUString& rText, const IntlWrapper&) const
+{
+ tools::Long nValue=GetValue();
+ SdrFormatter aFmt(eCoreMetric,ePresMetric);
+ rText = aFmt.GetStr(nValue);
+ rText += " " + SdrFormatter::GetUnitStr(ePresMetric);
+ if (ePres==SfxItemPresentation::Complete) {
+ rText = SdrItemPool::GetItemName(Which()) + " " + rText;
+ }
+ return true;
+}
+
+void SdrMetricItem::dumpAsXml(xmlTextWriterPtr pWriter) const
+{
+ (void)xmlTextWriterStartElement(pWriter, BAD_CAST("SdrMetricItem"));
+ if (Which() == SDRATTR_SHADOWXDIST)
+ {
+ (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("whichId"), BAD_CAST("SDRATTR_SHADOWXDIST"));
+ }
+ else if (Which() == SDRATTR_SHADOWYDIST)
+ {
+ (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("whichId"), BAD_CAST("SDRATTR_SHADOWYDIST"));
+ }
+ else if (Which() == SDRATTR_SHADOWSIZEX)
+ {
+ (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("whichId"), BAD_CAST("SDRATTR_SHADOWSIZEX"));
+ }
+ else if (Which() == SDRATTR_SHADOWSIZEY)
+ {
+ (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("whichId"), BAD_CAST("SDRATTR_SHADOWSIZEY"));
+ }
+ else if (Which() == SDRATTR_SHADOWBLUR)
+ {
+ (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("whichId"), BAD_CAST("SDRATTR_SHADOWBLUR"));
+ }
+
+ SfxInt32Item::dumpAsXml(pWriter);
+
+ (void)xmlTextWriterEndElement(pWriter);
+}
+
+// items of the legend object
+
+
+SdrCaptionTypeItem* SdrCaptionTypeItem::Clone(SfxItemPool* /*pPool*/) const { return new SdrCaptionTypeItem(*this); }
+
+sal_uInt16 SdrCaptionTypeItem::GetValueCount() const { return 4; }
+
+OUString SdrCaptionTypeItem::GetValueTextByPos(sal_uInt16 nPos)
+{
+ static TranslateId ITEMVALCAPTIONTYPES[] =
+ {
+ STR_ItemValCAPTIONTYPE1,
+ STR_ItemValCAPTIONTYPE2,
+ STR_ItemValCAPTIONTYPE3,
+ STR_ItemValCAPTIONTYPE4
+ };
+ assert(nPos < SAL_N_ELEMENTS(ITEMVALCAPTIONTYPES) && "wrong pos!");
+ return SvxResId(ITEMVALCAPTIONTYPES[nPos]);
+}
+
+bool SdrCaptionTypeItem::GetPresentation(SfxItemPresentation ePres,
+ MapUnit /*eCoreMetric*/, MapUnit /*ePresMetric*/, OUString& rText, const IntlWrapper&) const
+{
+ rText=GetValueTextByPos(sal::static_int_cast< sal_uInt16 >(GetValue()));
+ if (ePres==SfxItemPresentation::Complete) {
+ rText = SdrItemPool::GetItemName(Which()) + " " + rText;
+ }
+ return true;
+}
+
+
+SdrCaptionEscDirItem* SdrCaptionEscDirItem::Clone(SfxItemPool* /*pPool*/) const { return new SdrCaptionEscDirItem(*this); }
+
+sal_uInt16 SdrCaptionEscDirItem::GetValueCount() const { return 3; }
+
+OUString SdrCaptionEscDirItem::GetValueTextByPos(sal_uInt16 nPos)
+{
+ static TranslateId ITEMVALCAPTIONTYPES[] =
+ {
+ STR_ItemValCAPTIONESCHORI,
+ STR_ItemValCAPTIONESCVERT,
+ STR_ItemValCAPTIONESCBESTFIT
+ };
+ assert(nPos < SAL_N_ELEMENTS(ITEMVALCAPTIONTYPES) && "wrong pos!");
+ return SvxResId(ITEMVALCAPTIONTYPES[nPos]);
+}
+
+bool SdrCaptionEscDirItem::GetPresentation(SfxItemPresentation ePres,
+ MapUnit /*eCoreMetric*/, MapUnit /*ePresMetric*/, OUString& rText, const IntlWrapper&) const
+{
+ rText=GetValueTextByPos(sal::static_int_cast< sal_uInt16 >(GetValue()));
+ if (ePres==SfxItemPresentation::Complete) {
+ rText = SdrItemPool::GetItemName(Which()) + " " + rText;
+ }
+ return true;
+}
+
+
+// MiscItems
+
+
+// FitToSize
+
+SfxPoolItem* SdrTextFitToSizeTypeItem::CreateDefault() { return new SdrTextFitToSizeTypeItem; }
+
+SdrTextFitToSizeTypeItem* SdrTextFitToSizeTypeItem::Clone(SfxItemPool* /*pPool*/) const { return new SdrTextFitToSizeTypeItem(*this); }
+
+bool SdrTextFitToSizeTypeItem::operator==(const SfxPoolItem& rItem) const
+{
+ if (!SfxEnumItem<css::drawing::TextFitToSizeType>::operator==(rItem))
+ {
+ return false;
+ }
+
+ return m_nMaxScale == static_cast<const SdrTextFitToSizeTypeItem&>(rItem).m_nMaxScale;
+}
+
+sal_uInt16 SdrTextFitToSizeTypeItem::GetValueCount() const { return 4; }
+
+OUString SdrTextFitToSizeTypeItem::GetValueTextByPos(sal_uInt16 nPos)
+{
+ static TranslateId ITEMVALFITTISIZETYPES[] =
+ {
+ STR_ItemValFITTOSIZENONE,
+ STR_ItemValFITTOSIZEPROP,
+ STR_ItemValFITTOSIZEALLLINES,
+ STR_ItemValFITTOSIZERESIZEAT
+ };
+ assert(nPos < SAL_N_ELEMENTS(ITEMVALFITTISIZETYPES) && "wrong pos!");
+ return SvxResId(ITEMVALFITTISIZETYPES[nPos]);
+}
+
+bool SdrTextFitToSizeTypeItem::GetPresentation(SfxItemPresentation ePres,
+ MapUnit /*eCoreMetric*/, MapUnit /*ePresMetric*/, OUString& rText, const IntlWrapper&) const
+{
+ rText=GetValueTextByPos(sal::static_int_cast< sal_uInt16 >(GetValue()));
+ if (ePres==SfxItemPresentation::Complete) {
+ rText = SdrItemPool::GetItemName(Which()) + " " + rText;
+ }
+ return true;
+}
+
+bool SdrTextFitToSizeTypeItem::HasBoolValue() const { return true; }
+
+bool SdrTextFitToSizeTypeItem::GetBoolValue() const { return GetValue() != drawing::TextFitToSizeType_NONE; }
+
+void SdrTextFitToSizeTypeItem::SetBoolValue(bool bVal)
+{
+ SetValue(bVal ? drawing::TextFitToSizeType_PROPORTIONAL : drawing::TextFitToSizeType_NONE);
+}
+
+bool SdrTextFitToSizeTypeItem::QueryValue( uno::Any& rVal, sal_uInt8 /*nMemberId*/) const
+{
+ drawing::TextFitToSizeType eFS = GetValue();
+ rVal <<= eFS;
+
+ return true;
+}
+
+bool SdrTextFitToSizeTypeItem::PutValue( const uno::Any& rVal, sal_uInt8 /*nMemberId*/)
+{
+ drawing::TextFitToSizeType eFS;
+ if(!(rVal >>= eFS))
+ {
+ sal_Int32 nEnum = 0;
+ if(!(rVal >>= nEnum))
+ return false;
+
+ eFS = static_cast<drawing::TextFitToSizeType>(nEnum);
+ }
+
+ SetValue(eFS);
+
+ return true;
+}
+
+
+SdrTextVertAdjustItem* SdrTextVertAdjustItem::Clone(SfxItemPool* /*pPool*/) const { return new SdrTextVertAdjustItem(*this); }
+
+sal_uInt16 SdrTextVertAdjustItem::GetValueCount() const { return 5; }
+
+OUString SdrTextVertAdjustItem::GetValueTextByPos(sal_uInt16 nPos)
+{
+ static TranslateId ITEMVALTEXTVADJTYPES[] =
+ {
+ STR_ItemValTEXTVADJTOP,
+ STR_ItemValTEXTVADJCENTER,
+ STR_ItemValTEXTVADJBOTTOM,
+ STR_ItemValTEXTVADJBLOCK,
+ STR_ItemValTEXTVADJSTRETCH
+ };
+ assert(nPos < SAL_N_ELEMENTS(ITEMVALTEXTVADJTYPES) && "wrong pos!");
+ return SvxResId(ITEMVALTEXTVADJTYPES[nPos]);
+}
+
+bool SdrTextVertAdjustItem::GetPresentation(SfxItemPresentation ePres,
+ MapUnit /*eCoreMetric*/, MapUnit /*ePresMetric*/, OUString& rText, const IntlWrapper&) const
+{
+ rText=GetValueTextByPos(sal::static_int_cast< sal_uInt16 >(GetValue()));
+ if (ePres==SfxItemPresentation::Complete) {
+ rText = SdrItemPool::GetItemName(Which()) + " " + rText;
+ }
+ return true;
+}
+
+bool SdrTextVertAdjustItem::QueryValue( uno::Any& rVal, sal_uInt8 /*nMemberId*/) const
+{
+ rVal <<= static_cast<drawing::TextVerticalAdjust>(GetValue());
+ return true;
+}
+
+bool SdrTextVertAdjustItem::PutValue( const uno::Any& rVal, sal_uInt8 /*nMemberId*/)
+{
+ drawing::TextVerticalAdjust eAdj;
+ if(!(rVal >>= eAdj))
+ {
+ sal_Int32 nEnum = 0;
+ if(!(rVal >>= nEnum))
+ return false;
+
+ eAdj = static_cast<drawing::TextVerticalAdjust>(nEnum);
+ }
+
+ SetValue( static_cast<SdrTextVertAdjust>(eAdj) );
+
+ return true;
+}
+
+void SdrTextVertAdjustItem::dumpAsXml(xmlTextWriterPtr pWriter) const
+{
+ (void)xmlTextWriterStartElement(pWriter, BAD_CAST("SdrTextVertAdjustItem"));
+ (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("whichId"), BAD_CAST(OString::number(Which()).getStr()));
+ (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("value"), BAD_CAST(OString::number(GetValue()).getStr()));
+ (void)xmlTextWriterEndElement(pWriter);
+}
+
+SdrTextHorzAdjustItem* SdrTextHorzAdjustItem::Clone(SfxItemPool* /*pPool*/) const { return new SdrTextHorzAdjustItem(*this); }
+
+sal_uInt16 SdrTextHorzAdjustItem::GetValueCount() const { return 5; }
+
+OUString SdrTextHorzAdjustItem::GetValueTextByPos(sal_uInt16 nPos)
+{
+ static TranslateId ITEMVALTEXTHADJTYPES[] =
+ {
+ STR_ItemValTEXTHADJLEFT,
+ STR_ItemValTEXTHADJCENTER,
+ STR_ItemValTEXTHADJRIGHT,
+ STR_ItemValTEXTHADJBLOCK,
+ STR_ItemValTEXTHADJSTRETCH
+ };
+ assert(nPos < SAL_N_ELEMENTS(ITEMVALTEXTHADJTYPES) && "wrong pos!");
+ return SvxResId(ITEMVALTEXTHADJTYPES[nPos]);
+}
+
+bool SdrTextHorzAdjustItem::GetPresentation(SfxItemPresentation ePres,
+ MapUnit /*eCoreMetric*/, MapUnit /*ePresMetric*/, OUString& rText, const IntlWrapper&) const
+{
+ rText=GetValueTextByPos(sal::static_int_cast< sal_uInt16 >(GetValue()));
+ if (ePres==SfxItemPresentation::Complete) {
+ rText = SdrItemPool::GetItemName(Which()) + " " + rText;
+ }
+ return true;
+}
+
+bool SdrTextHorzAdjustItem::QueryValue( uno::Any& rVal, sal_uInt8 /*nMemberId*/) const
+{
+ rVal <<= static_cast<drawing::TextHorizontalAdjust>(GetValue());
+ return true;
+}
+
+bool SdrTextHorzAdjustItem::PutValue( const uno::Any& rVal, sal_uInt8 /*nMemberId*/)
+{
+ drawing::TextHorizontalAdjust eAdj;
+ if(!(rVal >>= eAdj))
+ {
+ sal_Int32 nEnum = 0;
+ if(!(rVal >>= nEnum))
+ return false;
+
+ eAdj = static_cast<drawing::TextHorizontalAdjust>(nEnum);
+ }
+
+ SetValue( static_cast<SdrTextHorzAdjust>(eAdj) );
+
+ return true;
+}
+
+
+SdrTextAniKindItem* SdrTextAniKindItem::Clone(SfxItemPool* /*pPool*/) const { return new SdrTextAniKindItem(*this); }
+
+sal_uInt16 SdrTextAniKindItem::GetValueCount() const { return 5; }
+
+OUString SdrTextAniKindItem::GetValueTextByPos(sal_uInt16 nPos)
+{
+ static TranslateId ITEMVALTEXTANITYPES[] =
+ {
+ STR_ItemValTEXTANI_NONE,
+ STR_ItemValTEXTANI_BLINK,
+ STR_ItemValTEXTANI_SCROLL,
+ STR_ItemValTEXTANI_ALTERNATE,
+ STR_ItemValTEXTANI_SLIDE
+ };
+ assert(nPos < SAL_N_ELEMENTS(ITEMVALTEXTANITYPES) && "wrong pos!");
+ return SvxResId(ITEMVALTEXTANITYPES[nPos]);
+}
+
+bool SdrTextAniKindItem::GetPresentation(SfxItemPresentation ePres,
+ MapUnit /*eCoreMetric*/, MapUnit /*ePresMetric*/, OUString& rText, const IntlWrapper&) const
+{
+ rText=GetValueTextByPos(sal::static_int_cast< sal_uInt16 >(GetValue()));
+ if (ePres==SfxItemPresentation::Complete) {
+ rText = SdrItemPool::GetItemName(Which()) + " " + rText;
+ }
+ return true;
+}
+
+bool SdrTextAniKindItem::QueryValue( uno::Any& rVal, sal_uInt8 /*nMemberId*/) const
+{
+ rVal <<= static_cast<drawing::TextAnimationKind>(GetValue());
+ return true;
+}
+
+bool SdrTextAniKindItem::PutValue( const uno::Any& rVal, sal_uInt8 /*nMemberId*/)
+{
+ drawing::TextAnimationKind eKind;
+ if(!(rVal >>= eKind))
+ {
+ sal_Int32 nEnum = 0;
+ if(!(rVal >>= nEnum))
+ return false;
+ eKind = static_cast<drawing::TextAnimationKind>(nEnum);
+ }
+
+ SetValue( static_cast<SdrTextAniKind>(eKind) );
+
+ return true;
+}
+
+
+SdrTextAniDirectionItem* SdrTextAniDirectionItem::Clone(SfxItemPool* /*pPool*/) const { return new SdrTextAniDirectionItem(*this); }
+
+sal_uInt16 SdrTextAniDirectionItem::GetValueCount() const { return 4; }
+
+OUString SdrTextAniDirectionItem::GetValueTextByPos(sal_uInt16 nPos)
+{
+ static TranslateId ITEMVALTEXTANITYPES[] =
+ {
+ STR_ItemValTEXTANI_LEFT,
+ STR_ItemValTEXTANI_UP,
+ STR_ItemValTEXTANI_RIGHT,
+ STR_ItemValTEXTANI_DOWN
+ };
+ assert(nPos < SAL_N_ELEMENTS(ITEMVALTEXTANITYPES) && "wrong pos!");
+ return SvxResId(ITEMVALTEXTANITYPES[nPos]);
+}
+
+bool SdrTextAniDirectionItem::GetPresentation(SfxItemPresentation ePres,
+ MapUnit /*eCoreMetric*/, MapUnit /*ePresMetric*/, OUString& rText, const IntlWrapper&) const
+{
+ rText=GetValueTextByPos(sal::static_int_cast< sal_uInt16 >(GetValue()));
+ if (ePres==SfxItemPresentation::Complete) {
+ rText = SdrItemPool::GetItemName(Which()) + " " + rText;
+ }
+ return true;
+}
+
+bool SdrTextAniDirectionItem::QueryValue( uno::Any& rVal, sal_uInt8 /*nMemberId*/) const
+{
+ rVal <<= static_cast<drawing::TextAnimationDirection>(GetValue());
+ return true;
+}
+
+bool SdrTextAniDirectionItem::PutValue( const uno::Any& rVal, sal_uInt8 /*nMemberId*/)
+{
+ drawing::TextAnimationDirection eDir;
+ if(!(rVal >>= eDir))
+ {
+ sal_Int32 nEnum = 0;
+ if(!(rVal >>= nEnum))
+ return false;
+
+ eDir = static_cast<drawing::TextAnimationDirection>(nEnum);
+ }
+
+ SetValue( static_cast<SdrTextAniDirection>(eDir) );
+
+ return true;
+}
+
+
+SdrTextAniDelayItem* SdrTextAniDelayItem::Clone(SfxItemPool* /*pPool*/) const { return new SdrTextAniDelayItem(*this); }
+
+bool SdrTextAniDelayItem::GetPresentation(
+ SfxItemPresentation ePres, MapUnit /*eCoreMetric*/, MapUnit /*ePresMetric*/,
+ OUString& rText, const IntlWrapper&) const
+{
+ rText = OUString::number(GetValue()) + "ms";
+
+ if(ePres == SfxItemPresentation::Complete)
+ {
+ rText = SdrItemPool::GetItemName(Which()) + " " + rText;
+ }
+
+ return true;
+}
+
+
+SdrTextAniAmountItem* SdrTextAniAmountItem::Clone(SfxItemPool* /*pPool*/) const { return new SdrTextAniAmountItem(*this); }
+
+bool SdrTextAniAmountItem::HasMetrics() const
+{
+ return GetValue()>0;
+}
+
+void SdrTextAniAmountItem::ScaleMetrics(tools::Long nMul, tools::Long nDiv)
+{
+ if (GetValue()>0) {
+ BigInt aVal(GetValue());
+ aVal*=nMul;
+ aVal+=nDiv/2; // to round accurately
+ aVal/=nDiv;
+ SetValue(short(aVal));
+ }
+}
+
+bool SdrTextAniAmountItem::GetPresentation(
+ SfxItemPresentation ePres, MapUnit eCoreMetric, MapUnit ePresMetric,
+ OUString& rText, const IntlWrapper&) const
+{
+ sal_Int32 nValue(GetValue());
+
+ if(!nValue)
+ nValue = -1;
+
+ if(nValue < 0)
+ {
+ rText = OUString::number(-nValue) + "pixel";
+ }
+ else
+ {
+ SdrFormatter aFmt(eCoreMetric, ePresMetric);
+ rText = aFmt.GetStr(nValue) +
+ SdrFormatter::GetUnitStr(ePresMetric);
+ }
+
+ if(ePres == SfxItemPresentation::Complete)
+ {
+ rText = SdrItemPool::GetItemName(Which()) + " " + rText;
+ }
+
+ return true;
+}
+
+
+SdrTextFixedCellHeightItem::SdrTextFixedCellHeightItem( bool bUseFixedCellHeight )
+ : SfxBoolItem( SDRATTR_TEXT_USEFIXEDCELLHEIGHT, bUseFixedCellHeight )
+{
+}
+bool SdrTextFixedCellHeightItem::GetPresentation( SfxItemPresentation ePres,
+ MapUnit /*eCoreMetric*/, MapUnit /*ePresentationMetric*/,
+ OUString &rText, const IntlWrapper& ) const
+{
+ rText = GetValueTextByVal( GetValue() );
+ if (ePres==SfxItemPresentation::Complete)
+ {
+ rText = SdrItemPool::GetItemName(Which()) + " " + rText;
+ }
+ return true;
+}
+
+SdrTextFixedCellHeightItem* SdrTextFixedCellHeightItem::Clone( SfxItemPool * /*pPool*/) const
+{
+ return new SdrTextFixedCellHeightItem( GetValue() );
+}
+
+bool SdrTextFixedCellHeightItem::QueryValue( uno::Any& rVal, sal_uInt8 /*nMemberId*/) const
+{
+ bool bValue = GetValue();
+ rVal <<= bValue;
+ return true;
+}
+bool SdrTextFixedCellHeightItem::PutValue( const uno::Any& rVal, sal_uInt8 /*nMemberId*/)
+{
+ bool bValue;
+ if( !( rVal >>= bValue ) )
+ return false;
+ SetValue( bValue );
+ return true;
+}
+
+// EdgeKind
+
+SdrEdgeKindItem* SdrEdgeKindItem::Clone(SfxItemPool* /*pPool*/) const { return new SdrEdgeKindItem(*this); }
+
+sal_uInt16 SdrEdgeKindItem::GetValueCount() const { return 4; }
+
+OUString SdrEdgeKindItem::GetValueTextByPos(sal_uInt16 nPos)
+{
+ static TranslateId ITEMVALEDGES[] =
+ {
+ STR_ItemValEDGE_ORTHOLINES,
+ STR_ItemValEDGE_THREELINES,
+ STR_ItemValEDGE_ONELINE,
+ STR_ItemValEDGE_BEZIER
+ };
+ assert(nPos < SAL_N_ELEMENTS(ITEMVALEDGES) && "wrong pos!");
+ return SvxResId(ITEMVALEDGES[nPos]);
+}
+
+bool SdrEdgeKindItem::GetPresentation(SfxItemPresentation ePres,
+ MapUnit /*eCoreMetric*/, MapUnit /*ePresMetric*/, OUString& rText, const IntlWrapper&) const
+{
+ rText=GetValueTextByPos(sal::static_int_cast< sal_uInt16 >(GetValue()));
+ if (ePres==SfxItemPresentation::Complete) {
+ rText = SdrItemPool::GetItemName(Which()) + " " + rText;
+ }
+ return true;
+}
+
+bool SdrEdgeKindItem::QueryValue( uno::Any& rVal, sal_uInt8 /*nMemberId*/) const
+{
+ drawing::ConnectorType eCT = drawing::ConnectorType_STANDARD;
+
+ switch( GetValue() )
+ {
+ case SdrEdgeKind::OrthoLines : eCT = drawing::ConnectorType_STANDARD; break;
+ case SdrEdgeKind::ThreeLines : eCT = drawing::ConnectorType_LINES; break;
+ case SdrEdgeKind::OneLine : eCT = drawing::ConnectorType_LINE; break;
+ case SdrEdgeKind::Bezier : eCT = drawing::ConnectorType_CURVE; break;
+ case SdrEdgeKind::Arc : eCT = drawing::ConnectorType_CURVE; break;
+ default:
+ OSL_FAIL( "SdrEdgeKindItem::QueryValue : unknown enum" );
+ }
+
+ rVal <<= eCT;
+
+ return true;
+}
+
+bool SdrEdgeKindItem::PutValue( const uno::Any& rVal, sal_uInt8 /*nMemberId*/)
+{
+ drawing::ConnectorType eCT;
+ if(!(rVal >>= eCT))
+ {
+ sal_Int32 nEnum = 0;
+ if(!(rVal >>= nEnum))
+ return false;
+
+ eCT = static_cast<drawing::ConnectorType>(nEnum);
+ }
+
+ SdrEdgeKind eEK = SdrEdgeKind::OrthoLines;
+ switch( eCT )
+ {
+ case drawing::ConnectorType_STANDARD : eEK = SdrEdgeKind::OrthoLines; break;
+ case drawing::ConnectorType_CURVE : eEK = SdrEdgeKind::Bezier; break;
+ case drawing::ConnectorType_LINE : eEK = SdrEdgeKind::OneLine; break;
+ case drawing::ConnectorType_LINES : eEK = SdrEdgeKind::ThreeLines; break;
+ default:
+ OSL_FAIL( "SdrEdgeKindItem::PuValue : unknown enum" );
+ }
+ SetValue( eEK );
+
+ return true;
+}
+
+bool SdrEdgeNode1HorzDistItem::QueryValue( uno::Any& rVal, sal_uInt8 /*nMemberId*/) const
+{
+ rVal <<= GetValue();
+ return true;
+}
+
+bool SdrEdgeNode1HorzDistItem::PutValue( const uno::Any& rVal, sal_uInt8 /*nMemberId*/)
+{
+ sal_Int32 nValue = 0;
+ if(!(rVal >>= nValue))
+ return false;
+
+ SetValue( nValue );
+ return true;
+}
+
+SdrEdgeNode1HorzDistItem* SdrEdgeNode1HorzDistItem::Clone(SfxItemPool* /*pPool*/) const
+{
+ return new SdrEdgeNode1HorzDistItem(*this);
+}
+
+bool SdrEdgeNode1VertDistItem::QueryValue( uno::Any& rVal, sal_uInt8 /*nMemberId*/) const
+{
+ rVal <<= GetValue();
+ return true;
+}
+
+bool SdrEdgeNode1VertDistItem::PutValue( const uno::Any& rVal, sal_uInt8 /*nMemberId*/)
+{
+ sal_Int32 nValue = 0;
+ if(!(rVal >>= nValue))
+ return false;
+
+ SetValue( nValue );
+ return true;
+}
+
+SdrEdgeNode1VertDistItem* SdrEdgeNode1VertDistItem::Clone(SfxItemPool* /*pPool*/) const
+{
+ return new SdrEdgeNode1VertDistItem(*this);
+}
+
+bool SdrEdgeNode2HorzDistItem::QueryValue( uno::Any& rVal, sal_uInt8 /*nMemberId*/) const
+{
+ rVal <<= GetValue();
+ return true;
+}
+
+bool SdrEdgeNode2HorzDistItem::PutValue( const uno::Any& rVal, sal_uInt8 /*nMemberId*/)
+{
+ sal_Int32 nValue = 0;
+ if(!(rVal >>= nValue))
+ return false;
+
+ SetValue( nValue );
+ return true;
+}
+
+SdrEdgeNode2HorzDistItem* SdrEdgeNode2HorzDistItem::Clone(SfxItemPool* /*pPool*/) const
+{
+ return new SdrEdgeNode2HorzDistItem(*this);
+}
+
+bool SdrEdgeNode2VertDistItem::QueryValue( uno::Any& rVal, sal_uInt8 /*nMemberId*/) const
+{
+ rVal <<= GetValue();
+ return true;
+}
+
+bool SdrEdgeNode2VertDistItem::PutValue( const uno::Any& rVal, sal_uInt8 /*nMemberId*/)
+{
+ sal_Int32 nValue = 0;
+ if(!(rVal >>= nValue))
+ return false;
+
+ SetValue( nValue );
+ return true;
+}
+
+SdrEdgeNode2VertDistItem* SdrEdgeNode2VertDistItem::Clone(SfxItemPool* /*pPool*/) const
+{
+ return new SdrEdgeNode2VertDistItem(*this);
+}
+
+SdrEdgeNode1GlueDistItem* SdrEdgeNode1GlueDistItem::Clone(SfxItemPool* /*pPool*/) const
+{
+ return new SdrEdgeNode1GlueDistItem(*this);
+}
+
+SdrEdgeNode2GlueDistItem* SdrEdgeNode2GlueDistItem::Clone(SfxItemPool* /*pPool*/) const
+{
+ return new SdrEdgeNode2GlueDistItem(*this);
+}
+
+SdrMeasureKindItem* SdrMeasureKindItem::Clone(SfxItemPool* /*pPool*/) const { return new SdrMeasureKindItem(*this); }
+
+sal_uInt16 SdrMeasureKindItem::GetValueCount() const { return 2; }
+
+OUString SdrMeasureKindItem::GetValueTextByPos(sal_uInt16 nPos)
+{
+ static TranslateId ITEMVALMEASURETYPES[] =
+ {
+ STR_ItemValMEASURE_STD,
+ STR_ItemValMEASURE_RADIUS
+ };
+ assert(nPos < SAL_N_ELEMENTS(ITEMVALMEASURETYPES) && "wrong pos!");
+ return SvxResId(ITEMVALMEASURETYPES[nPos]);
+}
+
+bool SdrMeasureKindItem::GetPresentation(SfxItemPresentation ePres,
+ MapUnit /*eCoreMetric*/, MapUnit /*ePresMetric*/, OUString& rText, const IntlWrapper&) const
+{
+ rText=GetValueTextByPos(sal::static_int_cast< sal_uInt16 >(GetValue()));
+ if (ePres==SfxItemPresentation::Complete) {
+ rText = SdrItemPool::GetItemName(Which()) + " " + rText;
+ }
+ return true;
+}
+
+bool SdrMeasureKindItem::QueryValue( uno::Any& rVal, sal_uInt8 /*nMemberId*/) const
+{
+ rVal <<= static_cast<drawing::MeasureKind>(GetValue());
+ return true;
+}
+
+bool SdrMeasureKindItem::PutValue( const uno::Any& rVal, sal_uInt8 /*nMemberId*/)
+{
+ drawing::MeasureKind eKind;
+ if(!(rVal >>= eKind))
+ {
+ sal_Int32 nEnum = 0;
+ if(!(rVal >>= nEnum))
+ return false;
+
+ eKind = static_cast<drawing::MeasureKind>(nEnum);
+ }
+
+ SetValue( static_cast<SdrMeasureKind>(eKind) );
+ return true;
+}
+
+
+SdrMeasureTextHPosItem* SdrMeasureTextHPosItem::Clone(SfxItemPool* /*pPool*/) const { return new SdrMeasureTextHPosItem(*this); }
+
+sal_uInt16 SdrMeasureTextHPosItem::GetValueCount() const { return 4; }
+
+const OUString & SdrMeasureTextHPosItem::GetValueTextByPos(sal_uInt16 nPos)
+{
+ static std::array<OUString, 4> aMeasureTextHPosItem
+ {
+ "automatic",
+ "left outside",
+ "inside (centered)",
+ "right outside"
+ };
+ assert(nPos < aMeasureTextHPosItem.size() && "wrong pos!");
+ return aMeasureTextHPosItem[nPos];
+}
+
+bool SdrMeasureTextHPosItem::GetPresentation(SfxItemPresentation ePres,
+ MapUnit /*eCoreMetric*/, MapUnit /*ePresMetric*/, OUString& rText, const IntlWrapper&) const
+{
+ rText=GetValueTextByPos(sal::static_int_cast< sal_uInt16 >(GetValue()));
+ if (ePres==SfxItemPresentation::Complete) {
+ rText = SdrItemPool::GetItemName(Which()) + " " + rText;
+ }
+ return true;
+}
+
+bool SdrMeasureTextHPosItem::QueryValue( uno::Any& rVal, sal_uInt8 /*nMemberId*/) const
+{
+ rVal <<= GetValue();
+ return true;
+}
+
+bool SdrMeasureTextHPosItem::PutValue( const uno::Any& rVal, sal_uInt8 /*nMemberId*/)
+{
+ drawing::MeasureTextHorzPos ePos;
+ if(!(rVal >>= ePos))
+ {
+ sal_Int32 nEnum = 0;
+ if(!(rVal >>= nEnum))
+ return false;
+
+ ePos = static_cast<drawing::MeasureTextHorzPos>(nEnum);
+ }
+
+ SetValue(ePos);
+ return true;
+}
+
+SdrMeasureTextVPosItem* SdrMeasureTextVPosItem::Clone(SfxItemPool* /*pPool*/) const { return new SdrMeasureTextVPosItem(*this); }
+
+sal_uInt16 SdrMeasureTextVPosItem::GetValueCount() const { return 5; }
+
+OUString SdrMeasureTextVPosItem::GetValueTextByPos(sal_uInt16 nPos)
+{
+ static TranslateId ITEMVALMEASURETEXTTYPES[] =
+ {
+ STR_ItemValMEASURE_TEXTVAUTO,
+ STR_ItemValMEASURE_ABOVE,
+ STR_ItemValMEASURETEXT_BREAKEDLINE,
+ STR_ItemValMEASURE_BELOW,
+ STR_ItemValMEASURETEXT_VERTICALCEN
+ };
+ assert(nPos < SAL_N_ELEMENTS(ITEMVALMEASURETEXTTYPES) && "wrong pos!");
+ return SvxResId(ITEMVALMEASURETEXTTYPES[nPos]);
+}
+
+bool SdrMeasureTextVPosItem::GetPresentation(SfxItemPresentation ePres,
+ MapUnit /*eCoreMetric*/, MapUnit /*ePresMetric*/, OUString& rText, const IntlWrapper&) const
+{
+ rText=GetValueTextByPos(sal::static_int_cast< sal_uInt16 >(GetValue()));
+ if (ePres==SfxItemPresentation::Complete) {
+ rText = SdrItemPool::GetItemName(Which()) + " " + rText;
+ }
+ return true;
+}
+
+bool SdrMeasureTextVPosItem::QueryValue( uno::Any& rVal, sal_uInt8 /*nMemberId*/) const
+{
+ rVal <<= GetValue();
+ return true;
+}
+
+bool SdrMeasureTextVPosItem::PutValue( const uno::Any& rVal, sal_uInt8 /*nMemberId*/)
+{
+ drawing::MeasureTextVertPos ePos;
+ if(!(rVal >>= ePos))
+ {
+ sal_Int32 nEnum = 0;
+ if(!(rVal >>= nEnum))
+ return false;
+
+ ePos = static_cast<drawing::MeasureTextVertPos>(nEnum);
+ }
+
+ SetValue(ePos);
+ return true;
+}
+
+SdrMeasureUnitItem* SdrMeasureUnitItem::Clone(SfxItemPool* /*pPool*/) const { return new SdrMeasureUnitItem(*this); }
+
+sal_uInt16 SdrMeasureUnitItem::GetValueCount() const { return 14; }
+
+OUString SdrMeasureUnitItem::GetValueTextByPos(sal_uInt16 nPos)
+{
+ if(static_cast<FieldUnit>(nPos) == FieldUnit::NONE)
+ return "default";
+ else
+ return SdrFormatter::GetUnitStr(static_cast<FieldUnit>(nPos));
+}
+
+bool SdrMeasureUnitItem::GetPresentation(SfxItemPresentation ePres,
+ MapUnit /*eCoreMetric*/, MapUnit /*ePresMetric*/, OUString& rText, const IntlWrapper&) const
+{
+ rText=GetValueTextByPos(sal::static_int_cast< sal_uInt16 >(GetValue()));
+ if (ePres==SfxItemPresentation::Complete) {
+ rText = SdrItemPool::GetItemName(Which()) + " " + rText;
+ }
+ return true;
+}
+
+bool SdrMeasureUnitItem::QueryValue( uno::Any& rVal, sal_uInt8 /*nMemberId*/) const
+{
+ rVal <<= static_cast<sal_Int32>(GetValue());
+ return true;
+}
+
+bool SdrMeasureUnitItem::PutValue( const uno::Any& rVal, sal_uInt8 /*nMemberId*/)
+{
+ sal_Int32 nMeasure = 0;
+ if(!(rVal >>= nMeasure))
+ return false;
+
+ SetValue( static_cast<FieldUnit>(nMeasure) );
+ return true;
+}
+
+
+SdrCircKindItem* SdrCircKindItem::Clone(SfxItemPool* /*pPool*/) const { return new SdrCircKindItem(*this); }
+
+sal_uInt16 SdrCircKindItem::GetValueCount() const { return 4; }
+
+OUString SdrCircKindItem::GetValueTextByPos(sal_uInt16 nPos)
+{
+ static TranslateId ITEMVALCIRCTYPES[] =
+ {
+ STR_ItemValCIRC_FULL,
+ STR_ItemValCIRC_SECT,
+ STR_ItemValCIRC_CUT,
+ STR_ItemValCIRC_ARC
+ };
+ assert(nPos < SAL_N_ELEMENTS(ITEMVALCIRCTYPES) && "wrong pos!");
+ return SvxResId(ITEMVALCIRCTYPES[nPos]);
+}
+
+bool SdrCircKindItem::GetPresentation(SfxItemPresentation ePres,
+ MapUnit /*eCoreMetric*/, MapUnit /*ePresMetric*/, OUString& rText, const IntlWrapper&) const
+{
+ rText=GetValueTextByPos(sal::static_int_cast< sal_uInt16 >(GetValue()));
+ if (ePres==SfxItemPresentation::Complete) {
+ rText = SdrItemPool::GetItemName(Which()) + " " + rText;
+ }
+ return true;
+}
+
+bool SdrCircKindItem::QueryValue( uno::Any& rVal, sal_uInt8 /*nMemberId*/) const
+{
+ rVal <<= static_cast<drawing::CircleKind>(GetValue());
+ return true;
+}
+
+bool SdrCircKindItem::PutValue( const uno::Any& rVal, sal_uInt8 /*nMemberId*/)
+{
+ drawing::CircleKind eKind;
+ if(!(rVal >>= eKind))
+ {
+ sal_Int32 nEnum = 0;
+ if(!(rVal >>= nEnum))
+ return false;
+
+ eKind = static_cast<drawing::CircleKind>(nEnum);
+ }
+
+ SetValue( static_cast<SdrCircKind>(eKind) );
+ return true;
+}
+
+SdrSignedPercentItem* SdrSignedPercentItem::Clone(SfxItemPool* /*pPool*/) const
+{
+ return new SdrSignedPercentItem( Which(), GetValue() );
+}
+
+bool SdrSignedPercentItem::GetPresentation(
+ SfxItemPresentation ePres, MapUnit /*eCoreMetric*/, MapUnit /*ePresMetric*/,
+ OUString& rText, const IntlWrapper&) const
+{
+ rText = unicode::formatPercent(GetValue(),
+ Application::GetSettings().GetUILanguageTag());
+
+ if(ePres == SfxItemPresentation::Complete)
+ {
+ rText = SdrItemPool::GetItemName(Which()) + " " + rText;
+ }
+
+ return true;
+}
+
+SdrGrafRedItem* SdrGrafRedItem::Clone( SfxItemPool* /*pPool*/) const
+{
+ return new SdrGrafRedItem( *this );
+}
+
+SdrGrafGreenItem* SdrGrafGreenItem::Clone( SfxItemPool* /*pPool*/) const
+{
+ return new SdrGrafGreenItem( *this );
+}
+
+SdrGrafBlueItem* SdrGrafBlueItem::Clone( SfxItemPool* /*pPool*/) const
+{
+ return new SdrGrafBlueItem( *this );
+}
+
+SdrGrafLuminanceItem* SdrGrafLuminanceItem::Clone( SfxItemPool* /*pPool*/) const
+{
+ return new SdrGrafLuminanceItem( *this );
+}
+
+SdrGrafContrastItem* SdrGrafContrastItem::Clone( SfxItemPool* /*pPool*/) const
+{
+ return new SdrGrafContrastItem( *this );
+}
+
+SdrGrafGamma100Item* SdrGrafGamma100Item::Clone( SfxItemPool* /*pPool */) const
+{
+ return new SdrGrafGamma100Item( *this );
+}
+
+bool SdrGrafGamma100Item::QueryValue( uno::Any& rVal, sal_uInt8 /*nMemberId*/) const
+{
+ rVal <<= static_cast<double>(GetValue()) / 100.0;
+ return true;
+}
+
+bool SdrGrafGamma100Item::PutValue( const uno::Any& rVal, sal_uInt8 /*nMemberId*/)
+{
+ double nGamma = 0;
+ if(!(rVal >>= nGamma))
+ return false;
+
+ SetValue( static_cast<sal_uInt32>(nGamma * 100.0 ) );
+ return true;
+}
+
+SdrGrafInvertItem* SdrGrafInvertItem::Clone( SfxItemPool* /*pPool*/) const
+{
+ return new SdrGrafInvertItem( *this );
+}
+
+SdrGrafTransparenceItem* SdrGrafTransparenceItem::Clone( SfxItemPool* /*pPool*/) const
+{
+ return new SdrGrafTransparenceItem( *this );
+}
+
+SdrGrafModeItem* SdrGrafModeItem::Clone(SfxItemPool* /*pPool*/) const
+{
+ return new SdrGrafModeItem( *this );
+}
+
+sal_uInt16 SdrGrafModeItem::GetValueCount() const
+{
+ return 4;
+}
+
+OUString SdrGrafModeItem::GetValueTextByPos(sal_uInt16 nPos)
+{
+ OUString aStr;
+
+ switch(nPos)
+ {
+ case 1:
+ {
+ aStr = "Greys";
+ break;
+ }
+ case 2:
+ {
+ aStr = "Black/White";
+ break;
+ }
+ case 3:
+ {
+ aStr = "Watermark";
+ break;
+ }
+ default:
+ {
+ aStr = "Standard";
+ break;
+ }
+ }
+
+ return aStr;
+}
+
+bool SdrGrafModeItem::GetPresentation( SfxItemPresentation ePres,
+ MapUnit /*eCoreMetric*/, MapUnit /*ePresMetric*/,
+ OUString& rText, const IntlWrapper&) const
+{
+ rText = GetValueTextByPos( sal::static_int_cast< sal_uInt16 >( GetValue() ) );
+
+ if( ePres == SfxItemPresentation::Complete )
+ {
+ rText = SdrItemPool::GetItemName( Which() ) + " " + rText;
+ }
+
+ return true;
+}
+
+SdrGrafCropItem* SdrGrafCropItem::Clone( SfxItemPool* /*pPool*/) const
+{
+ return new SdrGrafCropItem( *this );
+}
+
+SdrTextAniStartInsideItem::~SdrTextAniStartInsideItem()
+{
+}
+
+SdrTextAniStartInsideItem* SdrTextAniStartInsideItem::Clone(SfxItemPool* ) const
+{
+ return new SdrTextAniStartInsideItem(*this);
+}
+
+SdrTextAniStopInsideItem::~SdrTextAniStopInsideItem()
+{
+}
+
+SdrTextAniStopInsideItem* SdrTextAniStopInsideItem::Clone(SfxItemPool* ) const
+{
+ return new SdrTextAniStopInsideItem(*this);
+}
+
+SdrCaptionEscIsRelItem::~SdrCaptionEscIsRelItem()
+{
+}
+
+SdrCaptionEscIsRelItem* SdrCaptionEscIsRelItem::Clone(SfxItemPool* ) const
+{
+ return new SdrCaptionEscIsRelItem(*this);
+}
+
+SdrCaptionEscRelItem::~SdrCaptionEscRelItem()
+{
+}
+
+SdrCaptionEscRelItem* SdrCaptionEscRelItem::Clone(SfxItemPool*) const
+{
+ return new SdrCaptionEscRelItem(*this);
+}
+
+SdrCaptionFitLineLenItem::~SdrCaptionFitLineLenItem()
+{
+}
+
+SdrCaptionFitLineLenItem* SdrCaptionFitLineLenItem::Clone(SfxItemPool* ) const
+{
+ return new SdrCaptionFitLineLenItem(*this);
+}
+
+SdrCaptionLineLenItem::~SdrCaptionLineLenItem()
+{
+}
+
+SdrCaptionLineLenItem* SdrCaptionLineLenItem::Clone(SfxItemPool*) const
+{
+ return new SdrCaptionLineLenItem(*this);
+}
+
+SdrMeasureBelowRefEdgeItem::~SdrMeasureBelowRefEdgeItem()
+{
+}
+
+SdrMeasureBelowRefEdgeItem* SdrMeasureBelowRefEdgeItem::Clone(SfxItemPool* ) const
+{
+ return new SdrMeasureBelowRefEdgeItem(*this);
+}
+
+SdrMeasureTextIsFixedAngleItem::~SdrMeasureTextIsFixedAngleItem()
+{
+}
+
+SdrMeasureTextIsFixedAngleItem* SdrMeasureTextIsFixedAngleItem::Clone(SfxItemPool* ) const
+{
+ return new SdrMeasureTextIsFixedAngleItem(*this);
+}
+
+SdrMeasureTextFixedAngleItem::~SdrMeasureTextFixedAngleItem()
+{
+}
+
+SdrMeasureTextFixedAngleItem* SdrMeasureTextFixedAngleItem::Clone(SfxItemPool* ) const
+{
+ return new SdrMeasureTextFixedAngleItem(*this);
+}
+
+SdrMeasureDecimalPlacesItem::~SdrMeasureDecimalPlacesItem()
+{
+}
+
+SdrMeasureDecimalPlacesItem* SdrMeasureDecimalPlacesItem::Clone(SfxItemPool* ) const
+{
+ return new SdrMeasureDecimalPlacesItem(*this);
+}
+
+SdrMeasureTextRota90Item::~SdrMeasureTextRota90Item()
+{
+}
+
+SdrMeasureTextRota90Item* SdrMeasureTextRota90Item::Clone(SfxItemPool* ) const
+{
+ return new SdrMeasureTextRota90Item(*this);
+}
+
+SdrMeasureTextUpsideDownItem::~SdrMeasureTextUpsideDownItem()
+{
+}
+
+SdrMeasureTextUpsideDownItem* SdrMeasureTextUpsideDownItem::Clone(SfxItemPool* ) const
+{
+ return new SdrMeasureTextUpsideDownItem(*this);
+}
+
+SdrLayerIdItem* SdrLayerIdItem::Clone(SfxItemPool* /*pPool*/) const
+{
+ return new SdrLayerIdItem(*this);
+}
+
+SdrLayerNameItem* SdrLayerNameItem::Clone(SfxItemPool* /*pPool*/) const
+{
+ return new SdrLayerNameItem(*this);
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/svdraw/svdcrtv.cxx b/svx/source/svdraw/svdcrtv.cxx
new file mode 100644
index 000000000..9791df7ae
--- /dev/null
+++ b/svx/source/svdraw/svdcrtv.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 <svx/svdcrtv.hxx>
+#include <svx/xlnclit.hxx>
+#include <svx/svdocapt.hxx>
+#include <svx/svdoedge.hxx>
+#include <svx/svdpagv.hxx>
+#include <svx/svdpage.hxx>
+#include <svx/scene3d.hxx>
+#include <svx/view3d.hxx>
+#include <svx/xfillit0.hxx>
+#include <svx/xflclit.hxx>
+#include <svx/xlineit0.hxx>
+#include <svx/svdouno.hxx>
+#include <svx/svdopath.hxx>
+#include <svx/sdr/overlay/overlaypolypolygon.hxx>
+#include <svx/sdr/overlay/overlaymanager.hxx>
+#include <svx/sdrpaintwindow.hxx>
+#include <fmobj.hxx>
+#include <svx/svdocirc.hxx>
+#include <svx/sdr/contact/viewcontact.hxx>
+#include <svx/sdr/overlay/overlayprimitive2dsequenceobject.hxx>
+#include <vcl/ptrstyle.hxx>
+
+using namespace com::sun::star;
+
+class ImplConnectMarkerOverlay
+{
+ // The OverlayObjects
+ sdr::overlay::OverlayObjectList maObjects;
+
+ // The remembered target object
+ const SdrObject& mrObject;
+
+public:
+ ImplConnectMarkerOverlay(const SdrCreateView& rView, SdrObject const & rObject);
+
+ // The OverlayObjects are cleared using the destructor of OverlayObjectList.
+ // That destructor calls clear() at the list which removes all objects from the
+ // OverlayManager and deletes them.
+
+ const SdrObject& GetTargetObject() const { return mrObject; }
+};
+
+ImplConnectMarkerOverlay::ImplConnectMarkerOverlay(const SdrCreateView& rView, SdrObject const & rObject)
+: mrObject(rObject)
+{
+ basegfx::B2DPolyPolygon aB2DPolyPolygon(rObject.TakeXorPoly());
+
+ for(sal_uInt32 a(0); a < rView.PaintWindowCount(); a++)
+ {
+ SdrPaintWindow* pCandidate = rView.GetPaintWindow(a);
+ const rtl::Reference< sdr::overlay::OverlayManager >& xTargetOverlay = pCandidate->GetOverlayManager();
+
+ if(xTargetOverlay.is())
+ {
+ float fScalingFactor = xTargetOverlay->getOutputDevice().GetDPIScaleFactor();
+ Size aHalfLogicSize(xTargetOverlay->getOutputDevice().PixelToLogic(Size(4 * fScalingFactor, 4 * fScalingFactor)));
+
+ // object
+ std::unique_ptr<sdr::overlay::OverlayPolyPolygonStripedAndFilled> pNew(new sdr::overlay::OverlayPolyPolygonStripedAndFilled(
+ aB2DPolyPolygon));
+ xTargetOverlay->add(*pNew);
+ maObjects.append(std::move(pNew));
+
+ // gluepoints
+ for(sal_uInt16 i(0); i < 4; i++)
+ {
+ SdrGluePoint aGluePoint(rObject.GetVertexGluePoint(i));
+ const Point& rPosition = aGluePoint.GetAbsolutePos(rObject);
+
+ basegfx::B2DPoint aTopLeft(rPosition.X() - aHalfLogicSize.Width(), rPosition.Y() - aHalfLogicSize.Height());
+ basegfx::B2DPoint aBottomRight(rPosition.X() + aHalfLogicSize.Width(), rPosition.Y() + aHalfLogicSize.Height());
+
+ basegfx::B2DPolygon aTempPoly;
+ aTempPoly.append(aTopLeft);
+ aTempPoly.append(basegfx::B2DPoint(aBottomRight.getX(), aTopLeft.getY()));
+ aTempPoly.append(aBottomRight);
+ aTempPoly.append(basegfx::B2DPoint(aTopLeft.getX(), aBottomRight.getY()));
+ aTempPoly.setClosed(true);
+
+ basegfx::B2DPolyPolygon aTempPolyPoly;
+ aTempPolyPoly.append(aTempPoly);
+
+ std::unique_ptr<sdr::overlay::OverlayPolyPolygonStripedAndFilled> pNew2(new sdr::overlay::OverlayPolyPolygonStripedAndFilled(
+ aTempPolyPoly));
+ xTargetOverlay->add(*pNew2);
+ maObjects.append(std::move(pNew2));
+ }
+ }
+ }
+}
+
+class ImpSdrCreateViewExtraData
+{
+ // The OverlayObjects for XOR replacement
+ sdr::overlay::OverlayObjectList maObjects;
+
+public:
+ ImpSdrCreateViewExtraData();
+ ~ImpSdrCreateViewExtraData();
+
+ void CreateAndShowOverlay(const SdrCreateView& rView, const SdrObject* pObject, const basegfx::B2DPolyPolygon& rPolyPoly);
+ void HideOverlay();
+};
+
+ImpSdrCreateViewExtraData::ImpSdrCreateViewExtraData()
+{
+}
+
+ImpSdrCreateViewExtraData::~ImpSdrCreateViewExtraData()
+{
+ HideOverlay();
+}
+
+void ImpSdrCreateViewExtraData::CreateAndShowOverlay(const SdrCreateView& rView, const SdrObject* pObject, const basegfx::B2DPolyPolygon& rPolyPoly)
+{
+ for(sal_uInt32 a(0); a < rView.PaintWindowCount(); a++)
+ {
+ SdrPaintWindow* pCandidate = rView.GetPaintWindow(a);
+ const rtl::Reference<sdr::overlay::OverlayManager>& xOverlayManager = pCandidate->GetOverlayManager();
+
+ if (xOverlayManager.is())
+ {
+ if(pObject)
+ {
+ const sdr::contact::ViewContact& rVC = pObject->GetViewContact();
+ drawinglayer::primitive2d::Primitive2DContainer aSequence;
+ rVC.getViewIndependentPrimitive2DContainer(aSequence);
+ std::unique_ptr<sdr::overlay::OverlayObject> pNew(new sdr::overlay::OverlayPrimitive2DSequenceObject(std::move(aSequence)));
+
+ xOverlayManager->add(*pNew);
+ maObjects.append(std::move(pNew));
+ }
+
+ if(rPolyPoly.count())
+ {
+ std::unique_ptr<sdr::overlay::OverlayPolyPolygonStripedAndFilled> pNew(new sdr::overlay::OverlayPolyPolygonStripedAndFilled(
+ rPolyPoly));
+ xOverlayManager->add(*pNew);
+ maObjects.append(std::move(pNew));
+ }
+ }
+ }
+}
+
+void ImpSdrCreateViewExtraData::HideOverlay()
+{
+ // the clear() call of the list removes all objects from the
+ // OverlayManager and deletes them.
+ maObjects.clear();
+}
+
+
+// CreateView
+
+
+void SdrCreateView::ImpClearConnectMarker()
+{
+ mpCoMaOverlay.reset();
+}
+
+SdrCreateView::SdrCreateView(SdrModel& rSdrModel, OutputDevice* pOut)
+ : SdrDragView(rSdrModel, pOut)
+ , mpCurrentCreate(nullptr)
+ , mpCreatePV(nullptr)
+ , mpCreateViewExtraData(new ImpSdrCreateViewExtraData())
+ , maCurrentCreatePointer(PointerStyle::Cross)
+ , mnAutoCloseDistPix(5)
+ , mnFreeHandMinDistPix(10)
+ , mnCurrentInvent(SdrInventor::Default)
+ , mnCurrentIdent(SdrObjKind::NONE)
+ , mb1stPointAsCenter(false)
+ , mbUseIncompatiblePathCreateInterface(false)
+{
+}
+
+SdrCreateView::~SdrCreateView()
+{
+ ImpClearConnectMarker();
+ mpCreateViewExtraData.reset();
+ SdrObject::Free(mpCurrentCreate);
+}
+
+bool SdrCreateView::IsAction() const
+{
+ return SdrDragView::IsAction() || mpCurrentCreate!=nullptr;
+}
+
+void SdrCreateView::MovAction(const Point& rPnt)
+{
+ SdrDragView::MovAction(rPnt);
+ if (mpCurrentCreate != nullptr) {
+ MovCreateObj(rPnt);
+ }
+}
+
+void SdrCreateView::EndAction()
+{
+ if (mpCurrentCreate != nullptr) EndCreateObj(SdrCreateCmd::ForceEnd);
+ SdrDragView::EndAction();
+}
+
+void SdrCreateView::BckAction()
+{
+ if (mpCurrentCreate != nullptr) BckCreateObj();
+ SdrDragView::BckAction();
+}
+
+void SdrCreateView::BrkAction()
+{
+ SdrDragView::BrkAction();
+ BrkCreateObj();
+}
+
+void SdrCreateView::TakeActionRect(tools::Rectangle& rRect) const
+{
+ if (mpCurrentCreate != nullptr)
+ {
+ rRect=maDragStat.GetActionRect();
+ if (rRect.IsEmpty())
+ {
+ rRect=tools::Rectangle(maDragStat.GetPrev(),maDragStat.GetNow());
+ }
+ }
+ else
+ {
+ SdrDragView::TakeActionRect(rRect);
+ }
+}
+
+bool SdrCreateView::CheckEdgeMode()
+{
+ if (mpCurrentCreate != nullptr)
+ {
+ // is managed by EdgeObj
+ if (mnCurrentInvent==SdrInventor::Default && mnCurrentIdent==SdrObjKind::Edge) return false;
+ }
+
+ if (!IsCreateMode() || mnCurrentInvent!=SdrInventor::Default || mnCurrentIdent!=SdrObjKind::Edge)
+ {
+ ImpClearConnectMarker();
+ return false;
+ }
+ else
+ {
+ // sal_True, if MouseMove should check Connect
+ return !IsAction();
+ }
+}
+
+void SdrCreateView::SetConnectMarker(const SdrObjConnection& rCon)
+{
+ SdrObject* pTargetObject = rCon.pObj;
+
+ if(pTargetObject)
+ {
+ // if target object changes, throw away overlay object to make room for changes
+ if(mpCoMaOverlay && pTargetObject != &mpCoMaOverlay->GetTargetObject())
+ {
+ ImpClearConnectMarker();
+ }
+
+ if(!mpCoMaOverlay)
+ {
+ mpCoMaOverlay.reset(new ImplConnectMarkerOverlay(*this, *pTargetObject));
+ }
+ }
+ else
+ {
+ ImpClearConnectMarker();
+ }
+}
+
+void SdrCreateView::HideConnectMarker()
+{
+ ImpClearConnectMarker();
+}
+
+bool SdrCreateView::MouseMove(const MouseEvent& rMEvt, OutputDevice* pWin)
+{
+ if(CheckEdgeMode() && pWin)
+ {
+ SdrPageView* pPV = GetSdrPageView();
+
+ if(pPV)
+ {
+ // TODO: Change default hit tolerance at IsMarkedHit() some time!
+ Point aPos(pWin->PixelToLogic(rMEvt.GetPosPixel()));
+ bool bMarkHit=PickHandle(aPos)!=nullptr || IsMarkedObjHit(aPos);
+ SdrObjConnection aCon;
+ if (!bMarkHit) SdrEdgeObj::ImpFindConnector(aPos,*pPV,aCon,nullptr,pWin);
+ SetConnectMarker(aCon);
+ }
+ }
+ return SdrDragView::MouseMove(rMEvt,pWin);
+}
+
+bool SdrCreateView::IsTextTool() const
+{
+ return meEditMode==SdrViewEditMode::Create
+ && mnCurrentInvent==SdrInventor::Default
+ && (mnCurrentIdent==SdrObjKind::Text
+ || mnCurrentIdent==SdrObjKind::TitleText
+ || mnCurrentIdent==SdrObjKind::OutlineText);
+}
+
+bool SdrCreateView::IsEdgeTool() const
+{
+ return meEditMode==SdrViewEditMode::Create && mnCurrentInvent==SdrInventor::Default && (mnCurrentIdent==SdrObjKind::Edge);
+}
+
+bool SdrCreateView::IsMeasureTool() const
+{
+ return meEditMode==SdrViewEditMode::Create && mnCurrentInvent==SdrInventor::Default && (mnCurrentIdent==SdrObjKind::Measure);
+}
+
+void SdrCreateView::SetCurrentObj(SdrObjKind nIdent, SdrInventor nInvent)
+{
+ if (mnCurrentInvent!=nInvent || mnCurrentIdent!=nIdent)
+ {
+ mnCurrentInvent=nInvent;
+ mnCurrentIdent=nIdent;
+ SdrObject * pObj = (nIdent == SdrObjKind::NONE) ? nullptr :
+ SdrObjFactory::MakeNewObject(
+ *GetModel(),
+ nInvent,
+ nIdent);
+
+ if(pObj)
+ {
+ // Using text tool, mouse cursor is usually I-Beam,
+ // crosshairs with tiny I-Beam appears only on MouseButtonDown.
+ if(IsTextTool())
+ {
+ // Here the correct pointer needs to be used
+ // if the default is set to vertical writing
+ maCurrentCreatePointer = PointerStyle::Text;
+ }
+ else
+ maCurrentCreatePointer = pObj->GetCreatePointer();
+
+ SdrObject::Free( pObj );
+ }
+ else
+ {
+ maCurrentCreatePointer = PointerStyle::Cross;
+ }
+ }
+
+ CheckEdgeMode();
+ ImpSetGlueVisible3(IsEdgeTool());
+}
+
+bool SdrCreateView::ImpBegCreateObj(SdrInventor nInvent, SdrObjKind nIdent, const Point& rPnt, OutputDevice* pOut,
+ sal_Int16 nMinMov, const tools::Rectangle& rLogRect, SdrObject* pPreparedFactoryObject)
+{
+ bool bRet=false;
+ UnmarkAllObj();
+ BrkAction();
+
+ ImpClearConnectMarker();
+
+ mpCreatePV = GetSdrPageView();
+
+ if (mpCreatePV != nullptr)
+ { // otherwise no side registered!
+ OUString aLay(maActualLayer);
+
+ if(nInvent == SdrInventor::Default && nIdent == SdrObjKind::Measure && !maMeasureLayer.isEmpty())
+ {
+ aLay = maMeasureLayer;
+ }
+
+ SdrLayerID nLayer = mpCreatePV->GetPage()->GetLayerAdmin().GetLayerID(aLay);
+ if (nLayer==SDRLAYER_NOTFOUND) nLayer = SdrLayerID(0);
+ if (!mpCreatePV->GetLockedLayers().IsSet(nLayer) && mpCreatePV->GetVisibleLayers().IsSet(nLayer))
+ {
+ if(pPreparedFactoryObject)
+ {
+ mpCurrentCreate = pPreparedFactoryObject;
+ }
+ else
+ {
+ mpCurrentCreate = SdrObjFactory::MakeNewObject(
+ *mpModel,
+ nInvent,
+ nIdent);
+ }
+
+ Point aPnt(rPnt);
+ if (mnCurrentInvent != SdrInventor::Default || (mnCurrentIdent != SdrObjKind::Edge &&
+ mnCurrentIdent != SdrObjKind::FreehandLine &&
+ mnCurrentIdent != SdrObjKind::FreehandFill )) { // no snapping for Edge and Freehand
+ aPnt=GetSnapPos(aPnt, mpCreatePV);
+ }
+ if (mpCurrentCreate!=nullptr)
+ {
+ if (mpDefaultStyleSheet!=nullptr) mpCurrentCreate->NbcSetStyleSheet(mpDefaultStyleSheet, false);
+
+ // SW uses a naked SdrObject for frame construction. Normally, such an
+ // object should not be created. Since it is possible to use it as a helper
+ // object (e.g. in letting the user define an area with the interactive
+ // construction) at least no items should be set at that object.
+ if(nInvent != SdrInventor::Default || nIdent != SdrObjKind::NONE)
+ {
+ mpCurrentCreate->SetMergedItemSet(maDefaultAttr);
+ }
+
+ if (mpModel && dynamic_cast<const SdrCaptionObj *>(mpCurrentCreate) != nullptr)
+ {
+ SfxItemSet aSet(mpModel->GetItemPool());
+ aSet.Put(XFillColorItem(OUString(),COL_WHITE)); // in case someone turns on Solid
+ aSet.Put(XFillStyleItem(drawing::FillStyle_NONE));
+
+ mpCurrentCreate->SetMergedItemSet(aSet);
+ }
+ if (mpModel && nInvent==SdrInventor::Default && (nIdent==SdrObjKind::Text ||
+ nIdent==SdrObjKind::TitleText || nIdent==SdrObjKind::OutlineText))
+ {
+ // default for all text frames: no background, no border
+ SfxItemSet aSet(mpModel->GetItemPool());
+ aSet.Put(XFillColorItem(OUString(),COL_WHITE)); // in case someone turns on Solid
+ aSet.Put(XFillStyleItem(drawing::FillStyle_NONE));
+ aSet.Put(XLineColorItem(OUString(),COL_BLACK)); // in case someone turns on Solid
+ aSet.Put(XLineStyleItem(drawing::LineStyle_NONE));
+
+ mpCurrentCreate->SetMergedItemSet(aSet);
+ }
+ if (!rLogRect.IsEmpty()) mpCurrentCreate->NbcSetLogicRect(rLogRect);
+
+ // make sure drag start point is inside WorkArea
+ const tools::Rectangle& rWorkArea = GetWorkArea();
+
+ if(!rWorkArea.IsEmpty())
+ {
+ if(aPnt.X() < rWorkArea.Left())
+ {
+ aPnt.setX( rWorkArea.Left() );
+ }
+
+ if(aPnt.X() > rWorkArea.Right())
+ {
+ aPnt.setX( rWorkArea.Right() );
+ }
+
+ if(aPnt.Y() < rWorkArea.Top())
+ {
+ aPnt.setY( rWorkArea.Top() );
+ }
+
+ if(aPnt.Y() > rWorkArea.Bottom())
+ {
+ aPnt.setY( rWorkArea.Bottom() );
+ }
+ }
+
+ maDragStat.Reset(aPnt);
+ maDragStat.SetView(static_cast<SdrView*>(this));
+ maDragStat.SetPageView(mpCreatePV);
+ maDragStat.SetMinMove(ImpGetMinMovLogic(nMinMov,pOut));
+ mpDragWin=pOut;
+ if (mpCurrentCreate->BegCreate(maDragStat))
+ {
+ ShowCreateObj(/*pOut,sal_True*/);
+ bRet=true;
+ }
+ else
+ {
+ SdrObject::Free(mpCurrentCreate);
+ mpCurrentCreate = nullptr;
+ mpCreatePV = nullptr;
+ }
+ }
+ }
+ }
+ return bRet;
+}
+
+bool SdrCreateView::BegCreateObj(const Point& rPnt, OutputDevice* pOut, short nMinMov)
+{
+ return ImpBegCreateObj(mnCurrentInvent,mnCurrentIdent,rPnt,pOut,nMinMov,tools::Rectangle(), nullptr);
+}
+
+bool SdrCreateView::BegCreatePreparedObject(const Point& rPnt, sal_Int16 nMinMov, SdrObject* pPreparedFactoryObject)
+{
+ SdrInventor nInvent(mnCurrentInvent);
+ SdrObjKind nIdent(mnCurrentIdent);
+
+ if(pPreparedFactoryObject)
+ {
+ nInvent = pPreparedFactoryObject->GetObjInventor();
+ nIdent = pPreparedFactoryObject->GetObjIdentifier();
+ }
+
+ return ImpBegCreateObj(nInvent, nIdent, rPnt, nullptr, nMinMov, tools::Rectangle(), pPreparedFactoryObject);
+}
+
+bool SdrCreateView::BegCreateCaptionObj(const Point& rPnt, const Size& rObjSiz,
+ OutputDevice* pOut, short nMinMov)
+{
+ return ImpBegCreateObj(SdrInventor::Default,SdrObjKind::Caption,rPnt,pOut,nMinMov,
+ tools::Rectangle(rPnt,Size(rObjSiz.Width()+1,rObjSiz.Height()+1)), nullptr);
+}
+
+void SdrCreateView::MovCreateObj(const Point& rPnt)
+{
+ if (mpCurrentCreate==nullptr)
+ return;
+
+ Point aPnt(rPnt);
+ if (!maDragStat.IsNoSnap())
+ {
+ aPnt=GetSnapPos(aPnt, mpCreatePV);
+ }
+ if (IsOrtho())
+ {
+ if (maDragStat.IsOrtho8Possible()) OrthoDistance8(maDragStat.GetPrev(),aPnt,IsBigOrtho());
+ else if (maDragStat.IsOrtho4Possible()) OrthoDistance4(maDragStat.GetPrev(),aPnt,IsBigOrtho());
+ }
+
+ // If the drag point was limited and Ortho is active, do
+ // the small ortho correction (reduction) -> last parameter to FALSE.
+ bool bDidLimit(ImpLimitToWorkArea(aPnt));
+ if(bDidLimit && IsOrtho())
+ {
+ if(maDragStat.IsOrtho8Possible())
+ OrthoDistance8(maDragStat.GetPrev(), aPnt, false);
+ else if(maDragStat.IsOrtho4Possible())
+ OrthoDistance4(maDragStat.GetPrev(), aPnt, false);
+ }
+
+ if (aPnt==maDragStat.GetNow()) return;
+ bool bIsMinMoved(maDragStat.IsMinMoved());
+ if (!maDragStat.CheckMinMoved(aPnt))
+ return;
+
+ if (!bIsMinMoved) maDragStat.NextPoint();
+ maDragStat.NextMove(aPnt);
+ mpCurrentCreate->MovCreate(maDragStat);
+
+ // MovCreate changes the object, so use ActionChanged() on it
+ mpCurrentCreate->ActionChanged();
+
+ // replace for DrawCreateObjDiff
+ HideCreateObj();
+ ShowCreateObj();
+}
+
+void SdrCreateView::SetupObjLayer(const SdrPageView* pPageView, const OUString& aActiveLayer, SdrObject* pObj)
+{
+ const SdrLayerAdmin& rAd = pPageView->GetPage()->GetLayerAdmin();
+ SdrLayerID nLayer(0);
+
+ // #i72535#
+ if(dynamic_cast<const FmFormObj*>( pObj) != nullptr)
+ {
+ // for FormControls, force to form layer
+ nLayer = rAd.GetLayerID(rAd.GetControlLayerName());
+ }
+ else
+ {
+ nLayer = rAd.GetLayerID(aActiveLayer);
+ }
+
+ if(SDRLAYER_NOTFOUND == nLayer)
+ {
+ nLayer = SdrLayerID(0);
+ }
+
+ pObj->SetLayer(nLayer);
+}
+
+bool SdrCreateView::EndCreateObj(SdrCreateCmd eCmd)
+{
+ bool bRet=false;
+ SdrObject* pObjCreated=mpCurrentCreate;
+
+ if (mpCurrentCreate!=nullptr)
+ {
+ sal_uInt32 nCount=maDragStat.GetPointCount();
+
+ if (nCount<=1 && eCmd==SdrCreateCmd::ForceEnd)
+ {
+ BrkCreateObj(); // objects with only a single point don't exist (at least today)
+ return false; // sal_False = event not interpreted
+ }
+
+ bool bPntsEq=nCount>1;
+ sal_uInt32 i=1;
+ Point aP0=maDragStat.GetPoint(0);
+ while (bPntsEq && i<nCount) { bPntsEq=aP0==maDragStat.GetPoint(i); i++; }
+
+ if (mpCurrentCreate->EndCreate(maDragStat,eCmd))
+ {
+ HideCreateObj();
+
+ if (!bPntsEq)
+ {
+ // otherwise Brk, because all points are equal
+ SdrObject* pObj=mpCurrentCreate;
+ mpCurrentCreate=nullptr;
+
+ SetupObjLayer(mpCreatePV, maActualLayer, pObj);
+
+ // recognize creation of a new 3D object inside a 3D scene
+ bool bSceneIntoScene(false);
+
+ E3dScene* pObjScene = dynamic_cast<E3dScene*>(pObjCreated);
+ E3dScene* pCurrentScene = pObjScene ? dynamic_cast<E3dScene*>(mpCreatePV->GetCurrentGroup()) : nullptr;
+ if (pCurrentScene)
+ {
+ bool bDidInsert = static_cast<E3dView*>(this)->ImpCloneAll3DObjectsToDestScene(
+ pObjScene, pCurrentScene, Point(0, 0));
+
+ if(bDidInsert)
+ {
+ // delete object, its content is cloned and inserted
+ SdrObject::Free( pObjCreated );
+ pObjCreated = nullptr;
+ bSceneIntoScene = true;
+ }
+ }
+
+ if(!bSceneIntoScene)
+ {
+ // Here an interactively created SdrObject gets added, so
+ // take into account that interaction created an object in
+ // model coordinates. If we have e.g. a GirdOffset, this is a
+ // little bit tricky - we have an object in model coordinates,
+ // so the fetched offset is at the wrong point in principle
+ // since we need to 'substract' the offset here to get to
+ // 'real' model coordinates. But we have nothing better here,
+ // so go for it.
+ // The 2nd a little tricky thing is that this will early-create
+ // a ViewObjectContact for the new SdrObject, but these VOCs
+ // are anyways layouted for being create-on-demand. This will
+ // be adapted/replaced correctly later on.
+ // This *should* be the right place for getting all interactively
+ // created objects, see InsertObjectAtView below that calls
+ // CreateUndoNewObject.
+ basegfx::B2DVector aGridOffset(0.0, 0.0);
+ if(getPossibleGridOffsetForSdrObject(aGridOffset, pObj, mpCreatePV))
+ {
+ const Size aOffset(
+ basegfx::fround(-aGridOffset.getX()),
+ basegfx::fround(-aGridOffset.getY()));
+
+ pObj->NbcMove(aOffset);
+ }
+
+ // do the same as before
+ InsertObjectAtView(pObj, *mpCreatePV);
+ }
+
+ mpCreatePV = nullptr;
+ bRet=true; // sal_True = event interpreted
+ }
+ else
+ {
+ BrkCreateObj();
+ }
+ }
+ else
+ { // more points
+ if (eCmd==SdrCreateCmd::ForceEnd || // nothing there -- force ending
+ nCount==0 || // no existing points (should never happen)
+ (nCount<=1 && !maDragStat.IsMinMoved())) { // MinMove not met
+ BrkCreateObj();
+ }
+ else
+ {
+ // replace for DrawCreateObjDiff
+ HideCreateObj();
+ ShowCreateObj();
+ maDragStat.ResetMinMoved(); // NextPoint is at MovCreateObj()
+ bRet=true;
+ }
+ }
+ }
+ return bRet;
+}
+
+void SdrCreateView::BckCreateObj()
+{
+ if (mpCurrentCreate==nullptr)
+ return;
+
+ if (maDragStat.GetPointCount()<=2 )
+ {
+ BrkCreateObj();
+ }
+ else
+ {
+ HideCreateObj();
+ maDragStat.PrevPoint();
+ if (mpCurrentCreate->BckCreate(maDragStat))
+ {
+ ShowCreateObj();
+ }
+ else
+ {
+ BrkCreateObj();
+ }
+ }
+}
+
+void SdrCreateView::BrkCreateObj()
+{
+ if (mpCurrentCreate!=nullptr)
+ {
+ HideCreateObj();
+ mpCurrentCreate->BrkCreate(maDragStat);
+ SdrObject::Free( mpCurrentCreate );
+ mpCurrentCreate = nullptr;
+ mpCreatePV = nullptr;
+ }
+}
+
+void SdrCreateView::ShowCreateObj(/*OutputDevice* pOut, sal_Bool bFull*/)
+{
+ if(!IsCreateObj() || maDragStat.IsShown())
+ return;
+
+ if (mpCurrentCreate)
+ {
+ // for migration from XOR, replace DrawDragObj here to create
+ // overlay objects instead.
+ bool bUseSolidDragging(IsSolidDragging());
+
+ // #i101648# check if dragged object is a naked SdrObject (not
+ // a derivation). This is e.g. used in SW Frame construction
+ // as placeholder. Do not use SolidDragging for naked SdrObjects,
+ // they cannot have a valid optical representation
+ if(bUseSolidDragging && SdrObjKind::NONE == mpCurrentCreate->GetObjIdentifier())
+ {
+ bUseSolidDragging = false;
+ }
+
+ // check for objects with no fill and no line
+ if(bUseSolidDragging)
+ {
+ const SfxItemSet& rSet = mpCurrentCreate->GetMergedItemSet();
+ const drawing::FillStyle eFill(rSet.Get(XATTR_FILLSTYLE).GetValue());
+ const drawing::LineStyle eLine(rSet.Get(XATTR_LINESTYLE).GetValue());
+
+ if(drawing::LineStyle_NONE == eLine && drawing::FillStyle_NONE == eFill)
+ {
+ bUseSolidDragging = false;
+ }
+ }
+
+ // check for form controls
+ if(bUseSolidDragging)
+ {
+ if (dynamic_cast<const SdrUnoObj*>(mpCurrentCreate) != nullptr)
+ {
+ bUseSolidDragging = false;
+ }
+ }
+
+ // #i101781# force to non-solid dragging when not creating a full circle
+ if(bUseSolidDragging)
+ {
+ SdrCircObj* pCircObj = dynamic_cast<SdrCircObj*>(mpCurrentCreate);
+
+ if(pCircObj && SdrObjKind::CircleOrEllipse != pCircObj->GetObjIdentifier())
+ {
+ // #i103058# Allow SolidDragging with four points
+ if(maDragStat.GetPointCount() < 4)
+ {
+ bUseSolidDragging = false;
+ }
+ }
+ }
+
+ if(bUseSolidDragging)
+ {
+ basegfx::B2DPolyPolygon aDragPolyPolygon;
+
+ if (dynamic_cast<const SdrRectObj*>(mpCurrentCreate) != nullptr)
+ {
+ // ensure object has some size, necessary for SdrTextObj because
+ // there are still untested divisions by that sizes
+ tools::Rectangle aCurrentSnapRect(mpCurrentCreate->GetSnapRect());
+
+ if(aCurrentSnapRect.GetWidth() <= 1 || aCurrentSnapRect.GetHeight() <= 1)
+ {
+ tools::Rectangle aNewRect(maDragStat.GetStart(), maDragStat.GetStart() + Point(2, 2));
+ mpCurrentCreate->NbcSetSnapRect(aNewRect);
+ }
+ }
+
+ if (auto pPathObj = dynamic_cast<SdrPathObj*>(mpCurrentCreate))
+ {
+ // The up-to-now created path needs to be set at the object to have something
+ // that can be visualized
+ const basegfx::B2DPolyPolygon aCurrentPolyPolygon(pPathObj->getObjectPolyPolygon(maDragStat));
+
+ if(aCurrentPolyPolygon.count())
+ {
+ pPathObj->NbcSetPathPoly(aCurrentPolyPolygon);
+ }
+
+ aDragPolyPolygon = pPathObj->getDragPolyPolygon(maDragStat);
+ }
+
+ // use the SdrObject directly for overlay
+ mpCreateViewExtraData->CreateAndShowOverlay(*this, mpCurrentCreate, aDragPolyPolygon);
+ }
+ else
+ {
+ const ::basegfx::B2DPolyPolygon aPoly(mpCurrentCreate->TakeCreatePoly(maDragStat));
+
+ mpCreateViewExtraData->CreateAndShowOverlay(*this, nullptr, aPoly);
+ }
+
+ // #i101679# Force changed overlay to be shown
+ for(sal_uInt32 a(0); a < PaintWindowCount(); a++)
+ {
+ SdrPaintWindow* pCandidate = GetPaintWindow(a);
+ const rtl::Reference<sdr::overlay::OverlayManager>& xOverlayManager = pCandidate->GetOverlayManager();
+
+ if (xOverlayManager.is())
+ {
+ xOverlayManager->flush();
+ }
+ }
+ }
+
+ maDragStat.SetShown(true);
+}
+
+void SdrCreateView::HideCreateObj()
+{
+ if(IsCreateObj() && maDragStat.IsShown())
+ {
+ // for migration from XOR, replace DrawDragObj here to create
+ // overlay objects instead.
+ mpCreateViewExtraData->HideOverlay();
+
+ //DrawCreateObj(pOut,bFull);
+ maDragStat.SetShown(false);
+ }
+}
+
+
+void SdrCreateView::GetAttributes(SfxItemSet& rTargetSet, bool bOnlyHardAttr) const
+{
+ if (mpCurrentCreate)
+ {
+ rTargetSet.Put(mpCurrentCreate->GetMergedItemSet());
+ }
+ else
+ {
+ SdrDragView::GetAttributes(rTargetSet, bOnlyHardAttr);
+ }
+}
+
+bool SdrCreateView::SetAttributes(const SfxItemSet& rSet, bool bReplaceAll)
+{
+ if (mpCurrentCreate)
+ {
+ mpCurrentCreate->SetMergedItemSetAndBroadcast(rSet, bReplaceAll);
+
+ return true;
+ }
+ else
+ {
+ return SdrDragView::SetAttributes(rSet,bReplaceAll);
+ }
+}
+
+SfxStyleSheet* SdrCreateView::GetStyleSheet() const
+{
+ if (mpCurrentCreate != nullptr)
+ {
+ return mpCurrentCreate->GetStyleSheet();
+ }
+ else
+ {
+ return SdrDragView::GetStyleSheet();
+ }
+}
+
+void SdrCreateView::SetStyleSheet(SfxStyleSheet* pStyleSheet, bool bDontRemoveHardAttr)
+{
+ if (mpCurrentCreate != nullptr)
+ {
+ mpCurrentCreate->SetStyleSheet(pStyleSheet,bDontRemoveHardAttr);
+ }
+ else
+ {
+ SdrDragView::SetStyleSheet(pStyleSheet,bDontRemoveHardAttr);
+ }
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/svdraw/svddrag.cxx b/svx/source/svdraw/svddrag.cxx
new file mode 100644
index 000000000..0c785dc67
--- /dev/null
+++ b/svx/source/svdraw/svddrag.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 <svx/svdview.hxx>
+#include <svx/svddrag.hxx>
+
+SdrDragStatUserData::~SdrDragStatUserData() = default;
+
+SdrDragStat::~SdrDragStat()
+{
+}
+
+void SdrDragStat::Clear()
+{
+ mpUserData.reset();
+ mvPnts.clear();
+ mvPnts.emplace_back();
+}
+
+void SdrDragStat::Reset()
+{
+ pView=nullptr;
+ pPageView=nullptr;
+ bShown=false;
+ nMinMov=1;
+ bMinMoved=false;
+ bHorFixed=false;
+ bVerFixed=false;
+ bWantNoSnap=false;
+ pHdl=nullptr;
+ bOrtho4=false;
+ bOrtho8=false;
+ pDragMethod=nullptr;
+ bEndDragChangesAttributes=false;
+ bEndDragChangesGeoAndAttributes=false;
+ mbEndDragChangesLayout=false;
+ bMouseIsUp=false;
+ Clear();
+ aActionRect=tools::Rectangle();
+}
+
+void SdrDragStat::Reset(const Point& rPnt)
+{
+ Reset();
+ mvPnts[0]=rPnt;
+ aPos0=rPnt;
+ aRealNow=rPnt;
+}
+
+void SdrDragStat::NextMove(const Point& rPnt)
+{
+ aPos0=mvPnts.back();
+ aRealNow=rPnt;
+ mvPnts.back()=rPnt;
+}
+
+void SdrDragStat::NextPoint()
+{
+ mvPnts.emplace_back(aRealNow);
+}
+
+void SdrDragStat::PrevPoint()
+{
+ if (mvPnts.size()>1) { // one has to remain at all times
+ mvPnts.erase(mvPnts.begin()+mvPnts.size()-2);
+ mvPnts.back() = aRealNow;
+ }
+}
+
+bool SdrDragStat::CheckMinMoved(const Point& rPnt)
+{
+ if (!bMinMoved) {
+ tools::Long dx=rPnt.X()-GetPrev().X(); if (dx<0) dx=-dx;
+ tools::Long dy=rPnt.Y()-GetPrev().Y(); if (dy<0) dy=-dy;
+ if (dx>=tools::Long(nMinMov) || dy>=tools::Long(nMinMov))
+ bMinMoved=true;
+ }
+ return bMinMoved;
+}
+
+Fraction SdrDragStat::GetXFact() const
+{
+ tools::Long nMul=mvPnts.back().X()-aRef1.X();
+ tools::Long nDiv=GetPrev().X()-aRef1.X();
+ if (nDiv==0) nDiv=1;
+ if (bHorFixed) { nMul=1; nDiv=1; }
+ return Fraction(nMul,nDiv);
+}
+
+Fraction SdrDragStat::GetYFact() const
+{
+ tools::Long nMul=mvPnts.back().Y()-aRef1.Y();
+ tools::Long nDiv=GetPrev().Y()-aRef1.Y();
+ if (nDiv==0) nDiv=1;
+ if (bVerFixed) { nMul=1; nDiv=1; }
+ return Fraction(nMul,nDiv);
+}
+
+void SdrDragStat::TakeCreateRect(tools::Rectangle& rRect) const
+{
+ rRect=tools::Rectangle(mvPnts[0], mvPnts.back());
+ if (mvPnts.size()>1) {
+ Point aBtmRgt(mvPnts[1]);
+ rRect.SetRight(aBtmRgt.X() );
+ rRect.SetBottom(aBtmRgt.Y() );
+ }
+ if (pView!=nullptr && pView->IsCreate1stPointAsCenter()) {
+ rRect.AdjustTop(rRect.Top()-rRect.Bottom() );
+ rRect.AdjustLeft(rRect.Left()-rRect.Right() );
+ }
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/svdraw/svddrgm1.hxx b/svx/source/svdraw/svddrgm1.hxx
new file mode 100644
index 000000000..54214b956
--- /dev/null
+++ b/svx/source/svdraw/svddrgm1.hxx
@@ -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 .
+ */
+
+#ifndef INCLUDED_SVX_SOURCE_SVDRAW_SVDDRGM1_HXX
+#define INCLUDED_SVX_SOURCE_SVDRAW_SVDDRGM1_HXX
+
+#include <svx/xpoly.hxx>
+#include <svx/svdhdl.hxx>
+#include <svx/svddrgv.hxx>
+#include <svx/svddrgmt.hxx>
+
+class SdrDragView;
+
+class SdrDragMovHdl : public SdrDragMethod
+{
+protected:
+ // define nothing, override to do so
+ virtual void createSdrDragEntries() override;
+
+public:
+ explicit SdrDragMovHdl(SdrDragView& rNewView);
+
+ virtual OUString GetSdrDragComment() const override;
+ virtual bool BeginSdrDrag() override;
+ virtual void MoveSdrDrag(const Point& rPnt) override;
+ virtual bool EndSdrDrag(bool bCopy) override;
+ virtual void CancelSdrDrag() override;
+ virtual PointerStyle GetSdrDragPointer() const override;
+};
+
+class SdrDragRotate : public SdrDragMethod
+{
+private:
+ double nSin;
+ double nCos;
+ Degree100 nAngle0;
+ Degree100 nAngle;
+ bool bRight;
+
+public:
+ explicit SdrDragRotate(SdrDragView& rNewView);
+
+ virtual OUString GetSdrDragComment() const override;
+ virtual bool BeginSdrDrag() override;
+ virtual void MoveSdrDrag(const Point& rPnt) override;
+ virtual bool EndSdrDrag(bool bCopy) override;
+ virtual PointerStyle GetSdrDragPointer() const override;
+
+ virtual basegfx::B2DHomMatrix getCurrentTransformation() const override;
+ virtual void applyCurrentTransformationToSdrObject(SdrObject& rTarget) override;
+};
+
+class SdrDragShear : public SdrDragMethod
+{
+private:
+ Fraction aFact;
+ Degree100 nAngle0;
+ Degree100 nAngle;
+ double nTan;
+ bool bVertical; // contort vertically
+ bool bResize; // shear and resize
+ bool bUpSideDown; // mirror and shear/slant
+ bool bSlant;
+
+public:
+ SdrDragShear(SdrDragView& rNewView,bool bSlant1);
+
+ virtual OUString GetSdrDragComment() const override;
+ virtual bool BeginSdrDrag() override;
+ virtual void MoveSdrDrag(const Point& rPnt) override;
+ virtual bool EndSdrDrag(bool bCopy) override;
+ virtual PointerStyle GetSdrDragPointer() const override;
+
+ virtual basegfx::B2DHomMatrix getCurrentTransformation() const override;
+ virtual void applyCurrentTransformationToSdrObject(SdrObject& rTarget) override;
+};
+
+class SdrDragMirror : public SdrDragMethod
+{
+private:
+ Point aDif;
+ Degree100 nAngle;
+ bool bMirrored;
+ bool bSide0;
+
+ bool ImpCheckSide(const Point& rPnt) const;
+
+public:
+ explicit SdrDragMirror(SdrDragView& rNewView);
+
+ virtual OUString GetSdrDragComment() const override;
+ virtual bool BeginSdrDrag() override;
+ virtual void MoveSdrDrag(const Point& rPnt) override;
+ virtual bool EndSdrDrag(bool bCopy) override;
+ virtual PointerStyle GetSdrDragPointer() const override;
+
+ virtual basegfx::B2DHomMatrix getCurrentTransformation() const override;
+ virtual void applyCurrentTransformationToSdrObject(SdrObject& rTarget) override;
+};
+
+class SdrDragGradient : public SdrDragMethod
+{
+private:
+ // Handles to work on
+ SdrHdlGradient* pIAOHandle;
+
+ // is this for gradient (or for transparency)?
+ bool bIsGradient : 1;
+
+public:
+ SdrDragGradient(SdrDragView& rNewView, bool bGrad = true);
+
+ bool IsGradient() const { return bIsGradient; }
+
+ virtual OUString GetSdrDragComment() const override;
+ virtual bool BeginSdrDrag() override;
+ virtual void MoveSdrDrag(const Point& rPnt) override;
+ virtual bool EndSdrDrag(bool bCopy) override;
+ virtual PointerStyle GetSdrDragPointer() const override;
+ virtual void CancelSdrDrag() override;
+};
+
+class SdrDragCrook : public SdrDragMethod
+{
+private:
+ tools::Rectangle aMarkRect;
+ Point aMarkCenter;
+ Point aCenter;
+ Point aStart;
+ Fraction aFact;
+ Point aRad;
+ bool bContortionAllowed;
+ bool bNoContortionAllowed;
+ bool bContortion;
+ bool bResizeAllowed;
+ bool bResize;
+ bool bRotateAllowed;
+ bool bRotate;
+ bool bVertical;
+ bool bValid;
+ bool bLft;
+ bool bRgt;
+ bool bUpr;
+ bool bLwr;
+ bool bAtCenter;
+ Degree100 nAngle;
+ tools::Long nMarkSize;
+ SdrCrookMode eMode;
+
+ // helpers for applyCurrentTransformationToPolyPolygon
+ void MovAllPoints(basegfx::B2DPolyPolygon& rTarget);
+ void MovCrookPoint(Point& rPnt, Point* pC1, Point* pC2);
+
+protected:
+ // needs to add drag geometry to the default
+ virtual void createSdrDragEntries() override;
+
+public:
+ explicit SdrDragCrook(SdrDragView& rNewView);
+
+ virtual OUString GetSdrDragComment() const override;
+ virtual bool BeginSdrDrag() override;
+ virtual void MoveSdrDrag(const Point& rPnt) override;
+ virtual bool EndSdrDrag(bool bCopy) override;
+ virtual PointerStyle GetSdrDragPointer() const override;
+
+ virtual void applyCurrentTransformationToSdrObject(SdrObject& rTarget) override;
+ virtual void applyCurrentTransformationToPolyPolygon(basegfx::B2DPolyPolygon& rTarget) override;
+};
+
+class SdrDragDistort : public SdrDragMethod
+{
+private:
+ tools::Rectangle aMarkRect;
+ XPolygon aDistortedRect;
+ sal_uInt16 nPolyPt;
+ bool bContortionAllowed;
+ bool bNoContortionAllowed;
+ bool bContortion;
+
+ // helper for applyCurrentTransformationToPolyPolygon
+ void MovAllPoints(basegfx::B2DPolyPolygon& rTarget);
+
+protected:
+ // needs to add drag geometry to the default
+ virtual void createSdrDragEntries() override;
+
+public:
+ explicit SdrDragDistort(SdrDragView& rNewView);
+
+ virtual OUString GetSdrDragComment() const override;
+ virtual bool BeginSdrDrag() override;
+ virtual void MoveSdrDrag(const Point& rPnt) override;
+ virtual bool EndSdrDrag(bool bCopy) override;
+ virtual PointerStyle GetSdrDragPointer() const override;
+
+ virtual void applyCurrentTransformationToSdrObject(SdrObject& rTarget) override;
+ virtual void applyCurrentTransformationToPolyPolygon(basegfx::B2DPolyPolygon& rTarget) override;
+};
+
+// derive from SdrDragObjOwn to have handles aligned to object when it
+// is sheared or rotated
+class SdrDragCrop : public SdrDragObjOwn
+{
+public:
+ explicit SdrDragCrop(SdrDragView& rNewView);
+
+ virtual OUString GetSdrDragComment() const override;
+ virtual bool BeginSdrDrag() override;
+ virtual bool EndSdrDrag(bool bCopy) override;
+ virtual PointerStyle GetSdrDragPointer() const override;
+};
+
+#endif // INCLUDED_SVX_SOURCE_SVDRAW_SVDDRGM1_HXX
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/svdraw/svddrgmt.cxx b/svx/source/svdraw/svddrgmt.cxx
new file mode 100644
index 000000000..40fd8df27
--- /dev/null
+++ b/svx/source/svdraw/svddrgmt.cxx
@@ -0,0 +1,3860 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include "svddrgm1.hxx"
+#include <math.h>
+
+#include <o3tl/numeric.hxx>
+#include <osl/diagnose.h>
+#include <vcl/canvastools.hxx>
+#include <vcl/svapp.hxx>
+#include <vcl/settings.hxx>
+#include <vcl/ptrstyle.hxx>
+#include <svx/xpoly.hxx>
+#include <svx/svdtrans.hxx>
+#include <svx/svdundo.hxx>
+#include <svx/svdmark.hxx>
+#include <svx/svdpagv.hxx>
+#include <svx/svddrgv.hxx>
+#include <svx/svdograf.hxx>
+#include <svx/strings.hrc>
+#include <svx/dialmgr.hxx>
+#include <svx/sdgcpitm.hxx>
+#include <svx/sdooitm.hxx>
+#include <svx/sdtagitm.hxx>
+#include <basegfx/polygon/b2dpolygon.hxx>
+#include <basegfx/polygon/b2dpolygontools.hxx>
+#include <svx/sdr/overlay/overlaymanager.hxx>
+#include <sdr/overlay/overlayrollingrectangle.hxx>
+#include <svx/sdrpagewindow.hxx>
+#include <svx/sdrpaintwindow.hxx>
+#include <basegfx/matrix/b2dhommatrix.hxx>
+#include <basegfx/polygon/b2dpolypolygontools.hxx>
+#include <svx/sdr/contact/viewcontact.hxx>
+#include <svx/sdr/overlay/overlayprimitive2dsequenceobject.hxx>
+#include <drawinglayer/primitive2d/unifiedtransparenceprimitive2d.hxx>
+#include <svx/sdr/contact/objectcontact.hxx>
+#include <svx/svditer.hxx>
+#include <svx/svdopath.hxx>
+#include <svx/polypolygoneditor.hxx>
+#include <drawinglayer/primitive2d/PolygonMarkerPrimitive2D.hxx>
+#include <drawinglayer/primitive2d/PolyPolygonSelectionPrimitive2D.hxx>
+#include <drawinglayer/primitive2d/PolyPolygonMarkerPrimitive2D.hxx>
+#include <drawinglayer/primitive2d/transformprimitive2d.hxx>
+#include <drawinglayer/primitive2d/markerarrayprimitive2d.hxx>
+#include <sdr/primitive2d/sdrattributecreator.hxx>
+#include <sdr/primitive2d/sdrdecompositiontools.hxx>
+#include <sdr/primitive2d/sdrprimitivetools.hxx>
+#include <basegfx/matrix/b2dhommatrixtools.hxx>
+#include <drawinglayer/attribute/sdrlineattribute.hxx>
+#include <drawinglayer/attribute/sdrlinestartendattribute.hxx>
+#include <svl/itempool.hxx>
+#include <svtools/optionsdrawinglayer.hxx>
+#include <comphelper/lok.hxx>
+#include <map>
+#include <vector>
+
+
+SdrDragEntry::SdrDragEntry()
+: mbAddToTransparent(false)
+{
+}
+
+SdrDragEntry::~SdrDragEntry()
+{
+}
+
+
+SdrDragEntryPolyPolygon::SdrDragEntryPolyPolygon(const basegfx::B2DPolyPolygon& rOriginalPolyPolygon)
+: maOriginalPolyPolygon(rOriginalPolyPolygon)
+{
+}
+
+SdrDragEntryPolyPolygon::~SdrDragEntryPolyPolygon()
+{
+}
+
+drawinglayer::primitive2d::Primitive2DContainer SdrDragEntryPolyPolygon::createPrimitive2DSequenceInCurrentState(SdrDragMethod& rDragMethod)
+{
+ drawinglayer::primitive2d::Primitive2DContainer aRetval;
+
+ if(maOriginalPolyPolygon.count())
+ {
+ basegfx::B2DPolyPolygon aCopy(maOriginalPolyPolygon);
+
+ rDragMethod.applyCurrentTransformationToPolyPolygon(aCopy);
+ basegfx::BColor aColA(SvtOptionsDrawinglayer::GetStripeColorA().getBColor());
+ basegfx::BColor aColB(SvtOptionsDrawinglayer::GetStripeColorB().getBColor());
+ const double fStripeLength(SvtOptionsDrawinglayer::GetStripeLength());
+
+ if(Application::GetSettings().GetStyleSettings().GetHighContrastMode())
+ {
+ aColA = aColB = Application::GetSettings().GetStyleSettings().GetHighlightColor().getBColor();
+ aColB.invert();
+ }
+
+ aRetval.resize(2);
+ aRetval[0] = new drawinglayer::primitive2d::PolyPolygonMarkerPrimitive2D(
+ aCopy,
+ aColA,
+ aColB,
+ fStripeLength);
+
+ const basegfx::BColor aHilightColor(SvtOptionsDrawinglayer::getHilightColor().getBColor());
+ const double fTransparence(SvtOptionsDrawinglayer::GetTransparentSelectionPercent() * 0.01);
+
+ aRetval[1] = new drawinglayer::primitive2d::PolyPolygonSelectionPrimitive2D(
+ aCopy,
+ aHilightColor,
+ fTransparence,
+ 3.0,
+ false);
+ }
+
+ return aRetval;
+}
+
+
+SdrDragEntrySdrObject::SdrDragEntrySdrObject(
+ const SdrObject& rOriginal,
+ bool bModify)
+: maOriginal(rOriginal),
+ mbModify(bModify)
+{
+ // add SdrObject parts to transparent overlay stuff
+ setAddToTransparent(true);
+}
+
+SdrDragEntrySdrObject::~SdrDragEntrySdrObject()
+{
+}
+
+void SdrDragEntrySdrObject::prepareCurrentState(SdrDragMethod& rDragMethod)
+{
+ // for the moment, i need to re-create the clone in all cases. I need to figure
+ // out when clone and original have the same class, so that i can use operator=
+ // in those cases
+
+ mxClone.reset();
+
+ if(mbModify)
+ {
+ mxClone = maOriginal.getFullDragClone();
+
+ // apply original transformation, implemented at the DragMethods
+ rDragMethod.applyCurrentTransformationToSdrObject(*mxClone);
+ }
+}
+
+drawinglayer::primitive2d::Primitive2DContainer SdrDragEntrySdrObject::createPrimitive2DSequenceInCurrentState(SdrDragMethod&)
+{
+ const SdrObject* pSource = &maOriginal;
+
+ if(mbModify && mxClone)
+ {
+ // choose source for geometry data
+ pSource = mxClone.get();
+ }
+
+ // use the view-independent primitive representation (without
+ // evtl. GridOffset, that may be applied to the DragEntry individually)
+ drawinglayer::primitive2d::Primitive2DContainer xRetval;
+ pSource->GetViewContact().getViewIndependentPrimitive2DContainer(xRetval);
+ return xRetval;
+}
+
+
+SdrDragEntryPrimitive2DSequence::SdrDragEntryPrimitive2DSequence(
+ drawinglayer::primitive2d::Primitive2DContainer&& rSequence)
+: maPrimitive2DSequence(std::move(rSequence))
+{
+ // add parts to transparent overlay stuff if necessary
+ setAddToTransparent(true);
+}
+
+SdrDragEntryPrimitive2DSequence::~SdrDragEntryPrimitive2DSequence()
+{
+}
+
+drawinglayer::primitive2d::Primitive2DContainer SdrDragEntryPrimitive2DSequence::createPrimitive2DSequenceInCurrentState(SdrDragMethod& rDragMethod)
+{
+ drawinglayer::primitive2d::Primitive2DReference aTransformPrimitive2D(
+ new drawinglayer::primitive2d::TransformPrimitive2D(
+ rDragMethod.getCurrentTransformation(),
+ drawinglayer::primitive2d::Primitive2DContainer(maPrimitive2DSequence)));
+
+ return drawinglayer::primitive2d::Primitive2DContainer { aTransformPrimitive2D };
+}
+
+
+SdrDragEntryPointGlueDrag::SdrDragEntryPointGlueDrag(std::vector< basegfx::B2DPoint >&& rPositions, bool bIsPointDrag)
+: maPositions(std::move(rPositions)),
+ mbIsPointDrag(bIsPointDrag)
+{
+ // add SdrObject parts to transparent overlay stuff
+ setAddToTransparent(true);
+}
+
+SdrDragEntryPointGlueDrag::~SdrDragEntryPointGlueDrag()
+{
+}
+
+drawinglayer::primitive2d::Primitive2DContainer SdrDragEntryPointGlueDrag::createPrimitive2DSequenceInCurrentState(SdrDragMethod& rDragMethod)
+{
+ drawinglayer::primitive2d::Primitive2DContainer aRetval;
+
+ if(!maPositions.empty())
+ {
+ basegfx::B2DPolygon aPolygon;
+
+ for(auto const & a: maPositions)
+ {
+ aPolygon.append(a);
+ }
+
+ basegfx::B2DPolyPolygon aPolyPolygon(aPolygon);
+
+ rDragMethod.applyCurrentTransformationToPolyPolygon(aPolyPolygon);
+
+ const basegfx::B2DPolygon aTransformed(aPolyPolygon.getB2DPolygon(0));
+ std::vector< basegfx::B2DPoint > aTransformedPositions;
+
+ aTransformedPositions.reserve(aTransformed.count());
+
+ for(sal_uInt32 a = 0; a < aTransformed.count(); a++)
+ {
+ aTransformedPositions.push_back(aTransformed.getB2DPoint(a));
+ }
+
+ if(mbIsPointDrag)
+ {
+ basegfx::BColor aColor(SvtOptionsDrawinglayer::GetStripeColorA().getBColor());
+
+ if(Application::GetSettings().GetStyleSettings().GetHighContrastMode())
+ {
+ aColor = Application::GetSettings().GetStyleSettings().GetHighlightColor().getBColor();
+ }
+
+ drawinglayer::primitive2d::Primitive2DReference aMarkerArrayPrimitive2D(
+ new drawinglayer::primitive2d::MarkerArrayPrimitive2D(std::move(aTransformedPositions),
+ drawinglayer::primitive2d::createDefaultCross_3x3(aColor)));
+
+ aRetval = drawinglayer::primitive2d::Primitive2DContainer { aMarkerArrayPrimitive2D };
+ }
+ else
+ {
+ drawinglayer::primitive2d::Primitive2DReference aMarkerArrayPrimitive2D(
+ new drawinglayer::primitive2d::MarkerArrayPrimitive2D(std::move(aTransformedPositions),
+ SdrHdl::createGluePointBitmap()));
+ aRetval = drawinglayer::primitive2d::Primitive2DContainer { aMarkerArrayPrimitive2D };
+ }
+ }
+
+ return aRetval;
+}
+
+
+void SdrDragMethod::resetSdrDragEntries()
+{
+ // clear entries; creation is on demand
+ clearSdrDragEntries();
+}
+
+basegfx::B2DRange SdrDragMethod::getCurrentRange() const
+{
+ return maOverlayObjectList.getBaseRange();
+}
+
+void SdrDragMethod::clearSdrDragEntries()
+{
+ maSdrDragEntries.clear();
+}
+
+void SdrDragMethod::addSdrDragEntry(std::unique_ptr<SdrDragEntry> pNew)
+{
+ assert(pNew);
+ maSdrDragEntries.push_back(std::move(pNew));
+}
+
+void SdrDragMethod::createSdrDragEntries()
+{
+ if(!(getSdrDragView().GetSdrPageView() && getSdrDragView().GetSdrPageView()->HasMarkedObjPageView()))
+ return;
+
+ if(getSdrDragView().IsDraggingPoints())
+ {
+ createSdrDragEntries_PointDrag();
+ }
+ else if(getSdrDragView().IsDraggingGluePoints())
+ {
+ createSdrDragEntries_GlueDrag();
+ }
+ else
+ {
+ if(getSolidDraggingActive())
+ {
+ createSdrDragEntries_SolidDrag();
+ }
+ else
+ {
+ createSdrDragEntries_PolygonDrag();
+ }
+ }
+}
+
+void SdrDragMethod::createSdrDragEntryForSdrObject(const SdrObject& rOriginal)
+{
+ // add full object drag; Clone() at the object has to work
+ // for this
+ addSdrDragEntry(std::unique_ptr<SdrDragEntry>(new SdrDragEntrySdrObject(rOriginal, true/*bModify*/)));
+}
+
+void SdrDragMethod::insertNewlyCreatedOverlayObjectForSdrDragMethod(
+ std::unique_ptr<sdr::overlay::OverlayObject> pOverlayObject,
+ const sdr::contact::ObjectContact& rObjectContact,
+ sdr::overlay::OverlayManager& rOverlayManager)
+{
+ // check if we have an OverlayObject
+ if(!pOverlayObject)
+ {
+ return;
+ }
+
+ // add to OverlayManager
+ rOverlayManager.add(*pOverlayObject);
+
+ // Add GridOffset for non-linear ViewToDevice transformation (calc)
+ if(rObjectContact.supportsGridOffsets())
+ {
+ const basegfx::B2DRange& rNewRange(pOverlayObject->getBaseRange());
+
+ if(!rNewRange.isEmpty())
+ {
+ basegfx::B2DVector aOffset(0.0, 0.0);
+ rObjectContact.calculateGridOffsetForB2DRange(aOffset, rNewRange);
+
+ if(!aOffset.equalZero())
+ {
+ pOverlayObject->setOffset(aOffset);
+ }
+ }
+ }
+
+ // add to local OverlayObjectList - ownership change (!)
+ maOverlayObjectList.append(std::move(pOverlayObject));
+}
+
+void SdrDragMethod::createSdrDragEntries_SolidDrag()
+{
+ const size_t nMarkCount(getSdrDragView().GetMarkedObjectCount());
+ SdrPageView* pPV = getSdrDragView().GetSdrPageView();
+
+ if(!pPV)
+ return;
+
+ for(size_t a = 0; a < nMarkCount; ++a)
+ {
+ SdrMark* pM = getSdrDragView().GetSdrMarkByIndex(a);
+
+ if(pM->GetPageView() == pPV)
+ {
+ const SdrObject* pObject = pM->GetMarkedSdrObj();
+
+ if(pObject)
+ {
+ if(pPV->PageWindowCount())
+ {
+ SdrObjListIter aIter(*pObject);
+
+ while(aIter.IsMore())
+ {
+ SdrObject* pCandidate = aIter.Next();
+
+ if(pCandidate)
+ {
+ const bool bSuppressFullDrag(!pCandidate->supportsFullDrag());
+ bool bAddWireframe(bSuppressFullDrag);
+
+ if(!bAddWireframe && !pCandidate->HasLineStyle())
+ {
+ // add wireframe for objects without outline
+ bAddWireframe = true;
+ }
+
+ if(!bSuppressFullDrag)
+ {
+ // add full object drag; Clone() at the object has to work
+ // for this
+ createSdrDragEntryForSdrObject(*pCandidate);
+ }
+
+ if(bAddWireframe)
+ {
+ // when dragging a 50% transparent copy of a filled or not filled object without
+ // outline, this is normally hard to see. Add extra wireframe in that case. This
+ // works nice e.g. with text frames etc.
+ addSdrDragEntry(std::unique_ptr<SdrDragEntry>(new SdrDragEntryPolyPolygon(pCandidate->TakeXorPoly())));
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+}
+
+void SdrDragMethod::createSdrDragEntries_PolygonDrag()
+{
+ const size_t nMarkCount(getSdrDragView().GetMarkedObjectCount());
+ bool bNoPolygons(getSdrDragView().IsNoDragXorPolys() || nMarkCount > SdrDragView::GetDragXorPolyLimit());
+ basegfx::B2DPolyPolygon aResult;
+ sal_uInt32 nPointCount(0);
+
+ for(size_t a = 0; !bNoPolygons && a < nMarkCount; ++a)
+ {
+ SdrMark* pM = getSdrDragView().GetSdrMarkByIndex(a);
+
+ if(pM->GetPageView() == getSdrDragView().GetSdrPageView())
+ {
+ const basegfx::B2DPolyPolygon aNewPolyPolygon(pM->GetMarkedSdrObj()->TakeXorPoly());
+
+ for(auto const& rPolygon : aNewPolyPolygon)
+ {
+ nPointCount += rPolygon.count();
+ }
+
+ if(nPointCount > SdrDragView::GetDragXorPointLimit())
+ {
+ bNoPolygons = true;
+ }
+
+ if(!bNoPolygons)
+ {
+ aResult.append(aNewPolyPolygon);
+ }
+ }
+ }
+
+ if(bNoPolygons)
+ {
+ const tools::Rectangle aR(getSdrDragView().GetSdrPageView()->MarkSnap());
+ const basegfx::B2DRange aNewRectangle = vcl::unotools::b2DRectangleFromRectangle(aR);
+ basegfx::B2DPolygon aNewPolygon(basegfx::utils::createPolygonFromRect(aNewRectangle));
+
+ aResult = basegfx::B2DPolyPolygon(basegfx::utils::expandToCurve(aNewPolygon));
+ }
+
+ if(aResult.count())
+ {
+ addSdrDragEntry(std::unique_ptr<SdrDragEntry>(new SdrDragEntryPolyPolygon(aResult)));
+ }
+}
+
+void SdrDragMethod::createSdrDragEntries_PointDrag()
+{
+ const size_t nMarkCount(getSdrDragView().GetMarkedObjectCount());
+ std::vector< basegfx::B2DPoint > aPositions;
+
+ for(size_t nm = 0; nm < nMarkCount; ++nm)
+ {
+ SdrMark* pM = getSdrDragView().GetSdrMarkByIndex(nm);
+
+ if(pM->GetPageView() == getSdrDragView().GetSdrPageView())
+ {
+ const SdrUShortCont& rPts = pM->GetMarkedPoints();
+
+ if (!rPts.empty())
+ {
+ const SdrObject* pObj = pM->GetMarkedSdrObj();
+ const SdrPathObj* pPath = dynamic_cast< const SdrPathObj* >(pObj);
+
+ if(pPath)
+ {
+ const basegfx::B2DPolyPolygon& aPathXPP = pPath->GetPathPoly();
+
+ if(aPathXPP.count())
+ {
+ for(const sal_uInt16 nObjPt : rPts)
+ {
+ sal_uInt32 nPolyNum, nPointNum;
+
+ if(sdr::PolyPolygonEditor::GetRelativePolyPoint(aPathXPP, nObjPt, nPolyNum, nPointNum))
+ {
+ aPositions.push_back(aPathXPP.getB2DPolygon(nPolyNum).getB2DPoint(nPointNum));
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+
+ if(!aPositions.empty())
+ {
+ addSdrDragEntry(std::unique_ptr<SdrDragEntry>(new SdrDragEntryPointGlueDrag(std::move(aPositions), true)));
+ }
+}
+
+void SdrDragMethod::createSdrDragEntries_GlueDrag()
+{
+ const size_t nMarkCount(getSdrDragView().GetMarkedObjectCount());
+ std::vector< basegfx::B2DPoint > aPositions;
+
+ for(size_t nm = 0; nm < nMarkCount; ++nm)
+ {
+ SdrMark* pM = getSdrDragView().GetSdrMarkByIndex(nm);
+
+ if(pM->GetPageView() == getSdrDragView().GetSdrPageView())
+ {
+ const SdrUShortCont& rPts = pM->GetMarkedGluePoints();
+
+ if (!rPts.empty())
+ {
+ const SdrObject* pObj = pM->GetMarkedSdrObj();
+ const SdrGluePointList* pGPL = pObj->GetGluePointList();
+
+ if (pGPL)
+ {
+ for(const sal_uInt16 nObjPt : rPts)
+ {
+ const sal_uInt16 nGlueNum(pGPL->FindGluePoint(nObjPt));
+
+ if(SDRGLUEPOINT_NOTFOUND != nGlueNum)
+ {
+ const Point aPoint((*pGPL)[nGlueNum].GetAbsolutePos(*pObj));
+ aPositions.emplace_back(aPoint.X(), aPoint.Y());
+ }
+ }
+ }
+ }
+ }
+ }
+
+ if(!aPositions.empty())
+ {
+ addSdrDragEntry(std::unique_ptr<SdrDragEntry>(new SdrDragEntryPointGlueDrag(std::move(aPositions), false)));
+ }
+}
+
+OUString SdrDragMethod::ImpGetDescriptionStr(TranslateId pStrCacheID) const
+{
+ ImpGetDescriptionOptions nOpt=ImpGetDescriptionOptions::NONE;
+ if (IsDraggingPoints()) {
+ nOpt=ImpGetDescriptionOptions::POINTS;
+ } else if (IsDraggingGluePoints()) {
+ nOpt=ImpGetDescriptionOptions::GLUEPOINTS;
+ }
+ return getSdrDragView().ImpGetDescriptionString(pStrCacheID, nOpt);
+}
+
+SdrObject* SdrDragMethod::GetDragObj() const
+{
+ SdrObject* pObj=nullptr;
+ if (getSdrDragView().mpDragHdl!=nullptr) pObj=getSdrDragView().mpDragHdl->GetObj();
+ if (pObj==nullptr) pObj=getSdrDragView().mpMarkedObj;
+ return pObj;
+}
+
+SdrPageView* SdrDragMethod::GetDragPV() const
+{
+ SdrPageView* pPV=nullptr;
+ if (getSdrDragView().mpDragHdl!=nullptr) pPV=getSdrDragView().mpDragHdl->GetPageView();
+ if (pPV==nullptr) pPV=getSdrDragView().mpMarkedPV;
+ return pPV;
+}
+
+void SdrDragMethod::applyCurrentTransformationToSdrObject(SdrObject& rTarget)
+{
+ // the original applies the transformation using TRGetBaseGeometry/TRSetBaseGeometry.
+ // Later this should be the only needed one for linear transforms (not for SdrDragCrook and
+ // SdrDragDistort, those are NOT linear). Currently, this can not yet be used since the
+ // special handling of rotate/mirror due to the not-being-able to handle it in the old
+ // drawinglayer stuff. Text would currently not correctly be mirrored in the preview.
+ basegfx::B2DHomMatrix aObjectTransform;
+ basegfx::B2DPolyPolygon aObjectPolyPolygon;
+ bool bPolyUsed(rTarget.TRGetBaseGeometry(aObjectTransform, aObjectPolyPolygon));
+
+ // apply transform to object transform
+ aObjectTransform *= getCurrentTransformation();
+
+ if(bPolyUsed)
+ {
+ // do something special since the object size is in the polygon
+ // break up matrix to get the scale
+ const basegfx::utils::B2DHomMatrixBufferedDecompose aTmpDecomp(aObjectTransform);
+
+ // get polygon's position and size
+ const basegfx::B2DRange aPolyRange(aObjectPolyPolygon.getB2DRange());
+
+ // get the scaling factors (do not mirror, this is in the object transformation)
+ const double fScaleX(fabs(aTmpDecomp.getScale().getX()) / (basegfx::fTools::equalZero(aPolyRange.getWidth()) ? 1.0 : aPolyRange.getWidth()));
+ const double fScaleY(fabs(aTmpDecomp.getScale().getY()) / (basegfx::fTools::equalZero(aPolyRange.getHeight()) ? 1.0 : aPolyRange.getHeight()));
+
+ // prepare transform matrix for polygon
+ basegfx::B2DHomMatrix aPolyTransform(
+ basegfx::utils::createTranslateB2DHomMatrix(
+ -aPolyRange.getMinX(),
+ -aPolyRange.getMinY()));
+ aPolyTransform.scale(fScaleX, fScaleY);
+
+ // transform the polygon
+ aObjectPolyPolygon.transform(aPolyTransform);
+ }
+
+ rTarget.TRSetBaseGeometry(getCurrentTransformation() * aObjectTransform, aObjectPolyPolygon);
+}
+
+void SdrDragMethod::applyCurrentTransformationToPolyPolygon(basegfx::B2DPolyPolygon& rTarget)
+{
+ // original uses CurrentTransformation
+ rTarget.transform(getCurrentTransformation());
+}
+
+SdrDragMethod::SdrDragMethod(SdrDragView& rNewView)
+: mrSdrDragView(rNewView),
+ mbMoveOnly(false),
+ mbSolidDraggingActive(getSdrDragView().IsSolidDragging()),
+ mbShiftPressed(false)
+{
+ if(mbSolidDraggingActive && Application::GetSettings().GetStyleSettings().GetHighContrastMode())
+ {
+ // fallback to wireframe when high contrast is used
+ mbSolidDraggingActive = false;
+ }
+}
+
+SdrDragMethod::~SdrDragMethod()
+{
+ clearSdrDragEntries();
+}
+
+void SdrDragMethod::Show()
+{
+ getSdrDragView().ShowDragObj();
+}
+
+void SdrDragMethod::Hide()
+{
+ getSdrDragView().HideDragObj();
+}
+
+basegfx::B2DHomMatrix SdrDragMethod::getCurrentTransformation() const
+{
+ return basegfx::B2DHomMatrix();
+}
+
+void SdrDragMethod::CancelSdrDrag()
+{
+ Hide();
+}
+
+typedef std::map< const SdrObject*, SdrObject* > SdrObjectAndCloneMap;
+
+void SdrDragMethod::CreateOverlayGeometry(
+ sdr::overlay::OverlayManager& rOverlayManager,
+ const sdr::contact::ObjectContact& rObjectContact)
+{
+ // We do client-side object manipulation with the Kit API
+ if (comphelper::LibreOfficeKit::isActive())
+ return;
+
+ // create SdrDragEntries on demand
+ if(maSdrDragEntries.empty())
+ {
+ createSdrDragEntries();
+ }
+
+ // if there are entries, derive OverlayObjects from the entries, including
+ // modification from current interactive state
+ if(!maSdrDragEntries.empty())
+ {
+ // #i54102# SdrDragEntrySdrObject creates clones of SdrObjects as base for creating the needed
+ // primitives, holding the original and the clone. If connectors (Edges) are involved,
+ // the cloned connectors need to be connected to the cloned SdrObjects (after cloning
+ // they are connected to the original SdrObjects). To do so, trigger the preparation
+ // steps for SdrDragEntrySdrObject, save an association of (orig, clone) in a helper
+ // and evtl. remember if it was an edge
+ SdrObjectAndCloneMap aOriginalAndClones;
+ std::vector< SdrEdgeObj* > aEdges;
+
+ // #i54102# execute prepareCurrentState for all SdrDragEntrySdrObject, register pair of original and
+ // clone, remember edges
+ for(auto const & a: maSdrDragEntries)
+ {
+ SdrDragEntrySdrObject* pSdrDragEntrySdrObject = dynamic_cast< SdrDragEntrySdrObject*>(a.get());
+
+ if(pSdrDragEntrySdrObject)
+ {
+ pSdrDragEntrySdrObject->prepareCurrentState(*this);
+
+ SdrEdgeObj* pSdrEdgeObj = dynamic_cast< SdrEdgeObj* >(pSdrDragEntrySdrObject->getClone());
+
+ if(pSdrEdgeObj)
+ {
+ aEdges.push_back(pSdrEdgeObj);
+ }
+
+ if(pSdrDragEntrySdrObject->getClone())
+ {
+ aOriginalAndClones[&pSdrDragEntrySdrObject->getOriginal()] = pSdrDragEntrySdrObject->getClone();
+ }
+ }
+ }
+
+ // #i54102# if there are edges, reconnect their ends to the corresponding clones (if found)
+ for(SdrEdgeObj* pSdrEdgeObj: aEdges)
+ {
+ SdrObject* pConnectedTo = pSdrEdgeObj->GetConnectedNode(true);
+
+ if(pConnectedTo)
+ {
+ SdrObjectAndCloneMap::iterator aEntry = aOriginalAndClones.find(pConnectedTo);
+
+ if(aEntry != aOriginalAndClones.end())
+ {
+ pSdrEdgeObj->ConnectToNode(true, aEntry->second);
+ }
+ }
+
+ pConnectedTo = pSdrEdgeObj->GetConnectedNode(false);
+
+ if(pConnectedTo)
+ {
+ SdrObjectAndCloneMap::iterator aEntry = aOriginalAndClones.find(pConnectedTo);
+
+ if(aEntry != aOriginalAndClones.end())
+ {
+ pSdrEdgeObj->ConnectToNode(false, aEntry->second);
+ }
+ }
+ }
+
+ // collect primitives for visualisation
+ drawinglayer::primitive2d::Primitive2DContainer aResult;
+ drawinglayer::primitive2d::Primitive2DContainer aResultTransparent;
+
+ for(auto & pCandidate: maSdrDragEntries)
+ {
+ const drawinglayer::primitive2d::Primitive2DContainer aCandidateResult(pCandidate->createPrimitive2DSequenceInCurrentState(*this));
+
+ if(!aCandidateResult.empty())
+ {
+ if(pCandidate->getAddToTransparent())
+ {
+ aResultTransparent.append(aCandidateResult);
+ }
+ else
+ {
+ aResult.append(aCandidateResult);
+ }
+ }
+ }
+
+ if(DoAddConnectorOverlays())
+ {
+ const drawinglayer::primitive2d::Primitive2DContainer aConnectorOverlays(AddConnectorOverlays());
+
+ if(!aConnectorOverlays.empty())
+ {
+ // add connector overlays to transparent part
+ aResultTransparent.append(aConnectorOverlays);
+ }
+ }
+
+ if(!aResult.empty())
+ {
+ std::unique_ptr<sdr::overlay::OverlayObject> pNewOverlayObject(
+ new sdr::overlay::OverlayPrimitive2DSequenceObject(
+ std::move(aResult)));
+
+ insertNewlyCreatedOverlayObjectForSdrDragMethod(
+ std::move(pNewOverlayObject),
+ rObjectContact,
+ rOverlayManager);
+ }
+
+ if(!aResultTransparent.empty())
+ {
+ drawinglayer::primitive2d::Primitive2DReference aUnifiedTransparencePrimitive2D(new drawinglayer::primitive2d::UnifiedTransparencePrimitive2D(std::move(aResultTransparent), 0.5));
+ aResultTransparent = drawinglayer::primitive2d::Primitive2DContainer { aUnifiedTransparencePrimitive2D };
+
+ std::unique_ptr<sdr::overlay::OverlayObject> pNewOverlayObject(
+ new sdr::overlay::OverlayPrimitive2DSequenceObject(
+ std::move(aResultTransparent)));
+
+ insertNewlyCreatedOverlayObjectForSdrDragMethod(
+ std::move(pNewOverlayObject),
+ rObjectContact,
+ rOverlayManager);
+ }
+ }
+
+ // add DragStripes if necessary (help lines cross the page when dragging)
+ if(!getSdrDragView().IsDragStripes())
+ return;
+
+ tools::Rectangle aActionRectangle;
+ getSdrDragView().TakeActionRect(aActionRectangle);
+
+ const basegfx::B2DPoint aTopLeft(aActionRectangle.Left(), aActionRectangle.Top());
+ const basegfx::B2DPoint aBottomRight(aActionRectangle.Right(), aActionRectangle.Bottom());
+ std::unique_ptr<sdr::overlay::OverlayRollingRectangleStriped> pNew(
+ new sdr::overlay::OverlayRollingRectangleStriped(
+ aTopLeft,
+ aBottomRight,
+ true,
+ false));
+
+ insertNewlyCreatedOverlayObjectForSdrDragMethod(
+ std::move(pNew),
+ rObjectContact,
+ rOverlayManager);
+}
+
+void SdrDragMethod::destroyOverlayGeometry()
+{
+ maOverlayObjectList.clear();
+}
+
+bool SdrDragMethod::DoAddConnectorOverlays()
+{
+ // these conditions are translated from SdrDragView::ImpDrawEdgeXor
+ const SdrMarkList& rMarkedNodes = getSdrDragView().GetEdgesOfMarkedNodes();
+
+ if(!rMarkedNodes.GetMarkCount())
+ {
+ return false;
+ }
+
+ if(getSdrDragView().IsDraggingPoints() || getSdrDragView().IsDraggingGluePoints())
+ {
+ return false;
+ }
+
+ if(!getMoveOnly() && !(
+ dynamic_cast<const SdrDragMove*>(this) != nullptr || dynamic_cast<const SdrDragResize*>(this) != nullptr ||
+ dynamic_cast<const SdrDragRotate*>(this) != nullptr || dynamic_cast<const SdrDragMirror*>(this) != nullptr ))
+ {
+ return false;
+ }
+
+ // one more migrated from SdrEdgeObj::NspToggleEdgeXor
+ if( dynamic_cast< const SdrDragObjOwn* >(this) != nullptr || dynamic_cast< const SdrDragMovHdl* >(this) != nullptr )
+ {
+ return false;
+ }
+
+ return true;
+}
+
+drawinglayer::primitive2d::Primitive2DContainer SdrDragMethod::AddConnectorOverlays()
+{
+ drawinglayer::primitive2d::Primitive2DContainer aRetval;
+ const bool bDetail(getMoveOnly());
+ const SdrMarkList& rMarkedNodes = getSdrDragView().GetEdgesOfMarkedNodes();
+
+ for(size_t a = 0; a < rMarkedNodes.GetMarkCount(); ++a)
+ {
+ SdrMark* pEM = rMarkedNodes.GetMark(a);
+
+ if(pEM && pEM->GetMarkedSdrObj())
+ {
+ SdrEdgeObj* pEdge = dynamic_cast< SdrEdgeObj* >(pEM->GetMarkedSdrObj());
+
+ if(pEdge)
+ {
+ const basegfx::B2DPolygon aEdgePolygon(pEdge->ImplAddConnectorOverlay(*this, pEM->IsCon1(), pEM->IsCon2(), bDetail));
+
+ if(aEdgePolygon.count())
+ {
+ // this polygon is a temporary calculated connector path, so it is not possible to fetch
+ // the needed primitives directly from the pEdge object which does not get changed. If full
+ // drag is on, use the SdrObjects ItemSet to create an adequate representation
+ bool bUseSolidDragging(getSolidDraggingActive());
+
+ if(bUseSolidDragging)
+ {
+ // switch off solid dragging if connector is not visible
+ if(!pEdge->HasLineStyle())
+ {
+ bUseSolidDragging = false;
+ }
+ }
+
+ if(bUseSolidDragging)
+ {
+ const SfxItemSet& rItemSet = pEdge->GetMergedItemSet();
+ const drawinglayer::attribute::SdrLineAttribute aLine(
+ drawinglayer::primitive2d::createNewSdrLineAttribute(rItemSet));
+
+ if(!aLine.isDefault())
+ {
+ const drawinglayer::attribute::SdrLineStartEndAttribute aLineStartEnd(
+ drawinglayer::primitive2d::createNewSdrLineStartEndAttribute(
+ rItemSet,
+ aLine.getWidth()));
+
+ aRetval.push_back(drawinglayer::primitive2d::createPolygonLinePrimitive(
+ aEdgePolygon,
+ aLine,
+ aLineStartEnd));
+ }
+ }
+ else
+ {
+ basegfx::BColor aColA(SvtOptionsDrawinglayer::GetStripeColorA().getBColor());
+ basegfx::BColor aColB(SvtOptionsDrawinglayer::GetStripeColorB().getBColor());
+ const double fStripeLength(SvtOptionsDrawinglayer::GetStripeLength());
+
+ if(Application::GetSettings().GetStyleSettings().GetHighContrastMode())
+ {
+ aColA = aColB = Application::GetSettings().GetStyleSettings().GetHighlightColor().getBColor();
+ aColB.invert();
+ }
+
+ drawinglayer::primitive2d::Primitive2DReference aPolyPolygonMarkerPrimitive2D(
+ new drawinglayer::primitive2d::PolygonMarkerPrimitive2D(
+ aEdgePolygon, aColA, aColB, fStripeLength));
+ aRetval.push_back(aPolyPolygonMarkerPrimitive2D);
+ }
+ }
+ }
+ }
+ }
+
+ return aRetval;
+}
+
+
+SdrDragMovHdl::SdrDragMovHdl(SdrDragView& rNewView)
+: SdrDragMethod(rNewView)
+{
+}
+
+void SdrDragMovHdl::createSdrDragEntries()
+{
+ // SdrDragMovHdl does not use the default drags,
+ // but creates nothing
+}
+
+OUString SdrDragMovHdl::GetSdrDragComment() const
+{
+ OUString aStr=SvxResId(STR_DragMethMovHdl);
+ if (getSdrDragView().IsDragWithCopy()) aStr+=SvxResId(STR_EditWithCopy);
+ return aStr;
+}
+
+bool SdrDragMovHdl::BeginSdrDrag()
+{
+ if( !GetDragHdl() )
+ return false;
+
+ DragStat().SetRef1(GetDragHdl()->GetPos());
+ DragStat().SetShown(!DragStat().IsShown());
+ SdrHdlKind eKind=GetDragHdl()->GetKind();
+ SdrHdl* pH1=GetHdlList().GetHdl(SdrHdlKind::Ref1);
+ SdrHdl* pH2=GetHdlList().GetHdl(SdrHdlKind::Ref2);
+
+ if (eKind==SdrHdlKind::MirrorAxis)
+ {
+ if (pH1==nullptr || pH2==nullptr)
+ {
+ OSL_FAIL("SdrDragMovHdl::BeginSdrDrag(): Moving the axis of reflection: reference handles not found.");
+ return false;
+ }
+
+ DragStat().SetActionRect(tools::Rectangle(pH1->GetPos(),pH2->GetPos()));
+ }
+ else
+ {
+ Point aPt(GetDragHdl()->GetPos());
+ DragStat().SetActionRect(tools::Rectangle(aPt,aPt));
+ }
+
+ return true;
+}
+
+void SdrDragMovHdl::MoveSdrDrag(const Point& rNoSnapPnt)
+{
+ Point aPnt(rNoSnapPnt);
+
+ if ( !(GetDragHdl() && DragStat().CheckMinMoved(rNoSnapPnt)))
+ return;
+
+ if (GetDragHdl()->GetKind()==SdrHdlKind::MirrorAxis)
+ {
+ SdrHdl* pH1=GetHdlList().GetHdl(SdrHdlKind::Ref1);
+ SdrHdl* pH2=GetHdlList().GetHdl(SdrHdlKind::Ref2);
+
+ if (pH1==nullptr || pH2==nullptr)
+ return;
+
+ if (!DragStat().IsNoSnap())
+ {
+ tools::Long nBestXSnap=0;
+ tools::Long nBestYSnap=0;
+ bool bXSnapped=false;
+ bool bYSnapped=false;
+ Point aDif(aPnt-DragStat().GetStart());
+ getSdrDragView().CheckSnap(Ref1()+aDif,nBestXSnap,nBestYSnap,bXSnapped,bYSnapped);
+ getSdrDragView().CheckSnap(Ref2()+aDif,nBestXSnap,nBestYSnap,bXSnapped,bYSnapped);
+ aPnt.AdjustX(nBestXSnap );
+ aPnt.AdjustY(nBestYSnap );
+ }
+
+ if (aPnt!=DragStat().GetNow())
+ {
+ Hide();
+ DragStat().NextMove(aPnt);
+ Point aDif(DragStat().GetNow()-DragStat().GetStart());
+ pH1->SetPos(Ref1()+aDif);
+ pH2->SetPos(Ref2()+aDif);
+
+ SdrHdl* pHM = GetHdlList().GetHdl(SdrHdlKind::MirrorAxis);
+
+ if(pHM)
+ pHM->Touch();
+
+ Show();
+ DragStat().SetActionRect(tools::Rectangle(pH1->GetPos(),pH2->GetPos()));
+ }
+ }
+ else
+ {
+ if (!DragStat().IsNoSnap()) SnapPos(aPnt);
+ Degree100 nSA(0);
+
+ if (getSdrDragView().IsAngleSnapEnabled())
+ nSA=getSdrDragView().GetSnapAngle();
+
+ if (getSdrDragView().IsMirrorAllowed(true,true))
+ { // limited
+ if (!getSdrDragView().IsMirrorAllowed()) nSA=4500_deg100;
+ if (!getSdrDragView().IsMirrorAllowed(true)) nSA=9000_deg100;
+ }
+
+ if (getSdrDragView().IsOrtho() && nSA!=9000_deg100)
+ nSA=4500_deg100;
+
+ if (nSA)
+ { // angle snapping
+ SdrHdlKind eRef=SdrHdlKind::Ref1;
+
+ if (GetDragHdl()->GetKind()==SdrHdlKind::Ref1)
+ eRef=SdrHdlKind::Ref2;
+
+ SdrHdl* pH=GetHdlList().GetHdl(eRef);
+
+ if (pH!=nullptr)
+ {
+ Point aRef(pH->GetPos());
+ Degree100 nAngle=NormAngle36000(GetAngle(aPnt-aRef));
+ Degree100 nNewAngle=nAngle;
+ nNewAngle+=nSA/2_deg100;
+ nNewAngle/=nSA;
+ nNewAngle*=nSA;
+ nNewAngle=NormAngle36000(nNewAngle);
+ double a=toRadians(nNewAngle-nAngle);
+ double nSin=sin(a);
+ double nCos=cos(a);
+ RotatePoint(aPnt,aRef,nSin,nCos);
+
+ // eliminate rounding errors for certain values
+ if (nSA==9000_deg100)
+ {
+ if (nNewAngle==0_deg100 || nNewAngle==18000_deg100) aPnt.setY(aRef.Y() );
+ if (nNewAngle==9000_deg100 || nNewAngle==27000_deg100) aPnt.setX(aRef.X() );
+ }
+
+ if (nSA==4500_deg100)
+ OrthoDistance8(aRef,aPnt,true);
+ }
+ }
+
+ if (aPnt!=DragStat().GetNow())
+ {
+ Hide();
+ DragStat().NextMove(aPnt);
+ GetDragHdl()->SetPos(DragStat().GetNow());
+ SdrHdl* pHM = GetHdlList().GetHdl(SdrHdlKind::MirrorAxis);
+
+ if(pHM)
+ pHM->Touch();
+
+ Show();
+ DragStat().SetActionRect(tools::Rectangle(aPnt,aPnt));
+ }
+ }
+}
+
+bool SdrDragMovHdl::EndSdrDrag(bool /*bCopy*/)
+{
+ if( GetDragHdl() )
+ {
+ switch (GetDragHdl()->GetKind())
+ {
+ case SdrHdlKind::Ref1:
+ Ref1()=DragStat().GetNow();
+ break;
+
+ case SdrHdlKind::Ref2:
+ Ref2()=DragStat().GetNow();
+ break;
+
+ case SdrHdlKind::MirrorAxis:
+ Ref1()+=DragStat().GetNow()-DragStat().GetStart();
+ Ref2()+=DragStat().GetNow()-DragStat().GetStart();
+ break;
+
+ default: break;
+ }
+ }
+
+ return true;
+}
+
+void SdrDragMovHdl::CancelSdrDrag()
+{
+ Hide();
+
+ SdrHdl* pHdl = GetDragHdl();
+ if( pHdl )
+ pHdl->SetPos(DragStat().GetRef1());
+
+ SdrHdl* pHM = GetHdlList().GetHdl(SdrHdlKind::MirrorAxis);
+
+ if(pHM)
+ pHM->Touch();
+}
+
+PointerStyle SdrDragMovHdl::GetSdrDragPointer() const
+{
+ const SdrHdl* pHdl = GetDragHdl();
+
+ if (pHdl!=nullptr)
+ {
+ return pHdl->GetPointer();
+ }
+
+ return PointerStyle::RefHand;
+}
+
+
+SdrDragObjOwn::SdrDragObjOwn(SdrDragView& rNewView)
+: SdrDragMethod(rNewView)
+{
+ const SdrObject* pObj = GetDragObj();
+
+ if(pObj)
+ {
+ // suppress full drag for some object types
+ setSolidDraggingActive(pObj->supportsFullDrag());
+ }
+}
+
+SdrDragObjOwn::~SdrDragObjOwn()
+{
+}
+
+void SdrDragObjOwn::createSdrDragEntries()
+{
+ if(!mxClone)
+ return;
+
+ basegfx::B2DPolyPolygon aDragPolyPolygon;
+ bool bAddWireframe(true);
+
+ if(getSolidDraggingActive())
+ {
+ SdrPageView* pPV = getSdrDragView().GetSdrPageView();
+
+ if(pPV && pPV->PageWindowCount())
+ {
+ addSdrDragEntry(std::unique_ptr<SdrDragEntry>(new SdrDragEntrySdrObject(*mxClone, false)));
+
+ // potentially no wireframe needed, full drag works
+ bAddWireframe = false;
+ }
+ }
+
+ if(!bAddWireframe)
+ {
+ // check for extra conditions for wireframe, e.g. no border at
+ // objects
+ if(!mxClone->HasLineStyle())
+ {
+ bAddWireframe = true;
+ }
+ }
+
+ if(bAddWireframe)
+ {
+ // use wireframe poly when full drag is off or did not work
+ aDragPolyPolygon = mxClone->TakeXorPoly();
+ }
+
+ // add evtl. extra DragPolyPolygon
+ const basegfx::B2DPolyPolygon aSpecialDragPolyPolygon(mxClone->getSpecialDragPoly(DragStat()));
+
+ if(aSpecialDragPolyPolygon.count())
+ {
+ aDragPolyPolygon.append(aSpecialDragPolyPolygon);
+ }
+
+ if(aDragPolyPolygon.count())
+ {
+ addSdrDragEntry(std::unique_ptr<SdrDragEntry>(new SdrDragEntryPolyPolygon(aDragPolyPolygon)));
+ }
+}
+
+OUString SdrDragObjOwn::GetSdrDragComment() const
+{
+ OUString aStr;
+ // #i103058# get info string from the clone preferred, the original will
+ // not be changed. For security, use original as fallback
+ if(mxClone)
+ {
+ aStr = mxClone->getSpecialDragComment(DragStat());
+ }
+ else
+ {
+ const SdrObject* pObj = GetDragObj();
+
+ if(pObj)
+ {
+ aStr = pObj->getSpecialDragComment(DragStat());
+ }
+ }
+ return aStr;
+}
+
+bool SdrDragObjOwn::BeginSdrDrag()
+{
+ if(!mxClone)
+ {
+ const SdrObject* pObj = GetDragObj();
+
+ if(pObj && !pObj->IsResizeProtect())
+ {
+ if(pObj->beginSpecialDrag(DragStat()))
+ {
+ // create initial clone to have a start visualization
+ mxClone = pObj->getFullDragClone();
+ mxClone->applySpecialDrag(DragStat());
+
+ return true;
+ }
+ }
+ }
+
+ return false;
+}
+
+void SdrDragObjOwn::MoveSdrDrag(const Point& rNoSnapPnt)
+{
+ const SdrObject* pObj = GetDragObj();
+
+ if (!pObj)
+ // No object to drag. Bail out.
+ return;
+
+ Point aPnt(rNoSnapPnt);
+ SdrPageView* pPV = GetDragPV();
+
+ if (!pPV)
+ // No page view available. Bail out.
+ return;
+
+ if(!DragStat().IsNoSnap())
+ {
+ SnapPos(aPnt);
+ }
+ if(getSdrDragView().IsOrtho())
+ {
+ if (DragStat().IsOrtho8Possible())
+ {
+ OrthoDistance8(DragStat().GetStart(),aPnt,getSdrDragView().IsBigOrtho());
+ }
+ else if (DragStat().IsOrtho4Possible())
+ {
+ OrthoDistance4(DragStat().GetStart(),aPnt,getSdrDragView().IsBigOrtho());
+ }
+ }
+
+ if (!DragStat().CheckMinMoved(rNoSnapPnt))
+ // Not moved by the minimum threshold. Nothing to do.
+ return;
+
+ Hide();
+ DragStat().NextMove(aPnt);
+
+ // since SdrDragObjOwn currently supports no transformation of
+ // existing SdrDragEntries but only their recreation, a recreation
+ // after every move is needed in this mode. Delete existing
+ // SdrDragEntries here to force their recreation in the following Show().
+ clearSdrDragEntries();
+
+ // delete current clone (after the last reference to it is deleted above)
+ mxClone.reset();
+
+ // create a new clone and modify to current drag state
+ mxClone = pObj->getFullDragClone();
+ mxClone->applySpecialDrag(DragStat());
+
+ // AutoGrowWidth may change for SdrTextObj due to the automatism used
+ // with bDisableAutoWidthOnDragging, so not only geometry changes but
+ // also this (pretty indirect) property change is possible. If it gets
+ // changed, it needs to be copied to the original since nothing will
+ // happen when it only changes in the drag clone
+ const bool bOldAutoGrowWidth(pObj->GetMergedItem(SDRATTR_TEXT_AUTOGROWWIDTH).GetValue());
+ const bool bNewAutoGrowWidth(mxClone->GetMergedItem(SDRATTR_TEXT_AUTOGROWWIDTH).GetValue());
+
+ if (bOldAutoGrowWidth != bNewAutoGrowWidth)
+ {
+ GetDragObj()->SetMergedItem(makeSdrTextAutoGrowWidthItem(bNewAutoGrowWidth));
+ }
+
+ Show();
+}
+
+bool SdrDragObjOwn::EndSdrDrag(bool /*bCopy*/)
+{
+ Hide();
+ std::vector< std::unique_ptr<SdrUndoAction> > vConnectorUndoActions;
+ bool bRet = false;
+ SdrObject* pObj = GetDragObj();
+
+ if(pObj)
+ {
+ std::unique_ptr<SdrUndoAction> pUndo;
+ std::unique_ptr<SdrUndoAction> pUndo2;
+ const bool bUndo = getSdrDragView().IsUndoEnabled();
+
+ if( bUndo )
+ {
+ getSdrDragView().EndTextEditCurrentView();
+ if(!getSdrDragView().IsInsObjPoint() && pObj->IsInserted() )
+ {
+ if (DragStat().IsEndDragChangesAttributes())
+ {
+ pUndo=getSdrDragView().GetModel()->GetSdrUndoFactory().CreateUndoAttrObject(*pObj);
+
+ if (DragStat().IsEndDragChangesGeoAndAttributes())
+ {
+ vConnectorUndoActions = getSdrDragView().CreateConnectorUndo( *pObj );
+ pUndo2 = getSdrDragView().GetModel()->GetSdrUndoFactory().CreateUndoGeoObject(*pObj);
+ }
+ }
+ else
+ {
+ vConnectorUndoActions = getSdrDragView().CreateConnectorUndo( *pObj );
+ pUndo= getSdrDragView().GetModel()->GetSdrUndoFactory().CreateUndoGeoObject(*pObj);
+ }
+ }
+
+ if( pUndo )
+ {
+ getSdrDragView().BegUndo( pUndo->GetComment() );
+ }
+ else
+ {
+ getSdrDragView().BegUndo();
+ }
+ }
+
+ // Maybe use operator = for setting changed object data (do not change selection in
+ // view, this will destroy the interactor). This is possible since a clone is now
+ // directly modified by the modifiers. Only SdrTableObj is adding own UNDOs
+ // in its SdrTableObj::endSpecialDrag, so currently not possible. OTOH it uses
+ // a CreateUndoGeoObject(), so maybe setting SetEndDragChangesAttributes is okay. I
+ // will test this now
+ tools::Rectangle aBoundRect0;
+
+ if(pObj->GetUserCall())
+ {
+ aBoundRect0 = pObj->GetLastBoundRect();
+ }
+
+ bRet = pObj->applySpecialDrag(DragStat());
+ if (DragStat().IsEndDragChangesLayout())
+ {
+ auto pGeoUndo = dynamic_cast<SdrUndoGeoObj*>(pUndo.get());
+ if (pGeoUndo)
+ pGeoUndo->SetSkipChangeLayout(true);
+ }
+
+ if(bRet)
+ {
+ pObj->SetChanged();
+ pObj->BroadcastObjectChange();
+ pObj->SendUserCall( SdrUserCallType::Resize, aBoundRect0 );
+ }
+
+ if(bRet && bUndo )
+ {
+ getSdrDragView().AddUndoActions( std::move(vConnectorUndoActions) );
+
+ if ( pUndo )
+ {
+ getSdrDragView().AddUndo(std::move(pUndo));
+ }
+
+ if ( pUndo2 )
+ {
+ getSdrDragView().AddUndo(std::move(pUndo2));
+ }
+ }
+
+ if( bUndo )
+ getSdrDragView().EndUndo();
+ }
+
+ return bRet;
+}
+
+PointerStyle SdrDragObjOwn::GetSdrDragPointer() const
+{
+ const SdrHdl* pHdl=GetDragHdl();
+
+ if (pHdl)
+ {
+ return pHdl->GetPointer();
+ }
+
+ return PointerStyle::Move;
+}
+
+
+void SdrDragMove::createSdrDragEntryForSdrObject(const SdrObject& rOriginal)
+{
+ // use the view-independent primitive representation (without
+ // evtl. GridOffset, that may be applied to the DragEntry individually)
+ drawinglayer::primitive2d::Primitive2DContainer xRetval;
+ rOriginal.GetViewContact().getViewIndependentPrimitive2DContainer(xRetval);
+ addSdrDragEntry(
+ std::unique_ptr<SdrDragEntry>(
+ new SdrDragEntryPrimitive2DSequence(
+ std::move(xRetval))));
+
+}
+
+void SdrDragMove::applyCurrentTransformationToSdrObject(SdrObject& rTarget)
+{
+ rTarget.Move(Size(DragStat().GetDX(), DragStat().GetDY()));
+}
+
+SdrDragMove::SdrDragMove(SdrDragView& rNewView)
+ : SdrDragMethod(rNewView)
+ , nBestXSnap(0)
+ , nBestYSnap(0)
+ , bXSnapped(false)
+ , bYSnapped(false)
+{
+ setMoveOnly(true);
+}
+
+OUString SdrDragMove::GetSdrDragComment() const
+{
+ OUString aStr = ImpGetDescriptionStr(STR_DragMethMove)
+ + " (x="
+ + getSdrDragView().GetModel()->GetMetricString(DragStat().GetDX())
+ + " y="
+ + getSdrDragView().GetModel()->GetMetricString(DragStat().GetDY())
+ + ")";
+
+ if(getSdrDragView().IsDragWithCopy())
+ {
+ if(!getSdrDragView().IsInsObjPoint() && !getSdrDragView().IsInsGluePoint())
+ {
+ aStr += SvxResId(STR_EditWithCopy);
+ }
+ }
+ return aStr;
+}
+
+bool SdrDragMove::BeginSdrDrag()
+{
+ DragStat().SetActionRect(GetMarkedRect());
+ Show();
+
+ return true;
+}
+
+basegfx::B2DHomMatrix SdrDragMove::getCurrentTransformation() const
+{
+ return basegfx::utils::createTranslateB2DHomMatrix(DragStat().GetDX(), DragStat().GetDY());
+}
+
+void SdrDragMove::ImpCheckSnap(const Point& rPt)
+{
+ Point aPt(rPt);
+ SdrSnap nRet=SnapPos(aPt);
+ aPt-=rPt;
+
+ if (nRet & SdrSnap::XSNAPPED)
+ {
+ if (bXSnapped)
+ {
+ if (std::abs(aPt.X())<std::abs(nBestXSnap))
+ {
+ nBestXSnap=aPt.X();
+ }
+ }
+ else
+ {
+ nBestXSnap=aPt.X();
+ bXSnapped=true;
+ }
+ }
+
+ if (!(nRet & SdrSnap::YSNAPPED))
+ return;
+
+ if (bYSnapped)
+ {
+ if (std::abs(aPt.Y())<std::abs(nBestYSnap))
+ {
+ nBestYSnap=aPt.Y();
+ }
+ }
+ else
+ {
+ nBestYSnap=aPt.Y();
+ bYSnapped=true;
+ }
+}
+
+void SdrDragMove::MoveSdrDrag(const Point& rNoSnapPnt_)
+{
+ nBestXSnap=0;
+ nBestYSnap=0;
+ bXSnapped=false;
+ bYSnapped=false;
+ Point aNoSnapPnt(rNoSnapPnt_);
+ const tools::Rectangle& aSR=GetMarkedRect();
+ tools::Long nMovedx=aNoSnapPnt.X()-DragStat().GetStart().X();
+ tools::Long nMovedy=aNoSnapPnt.Y()-DragStat().GetStart().Y();
+ Point aLO(aSR.TopLeft()); aLO.AdjustX(nMovedx ); aLO.AdjustY(nMovedy );
+ Point aRU(aSR.BottomRight()); aRU.AdjustX(nMovedx ); aRU.AdjustY(nMovedy );
+ Point aLU(aLO.X(),aRU.Y());
+ Point aRO(aRU.X(),aLO.Y());
+ ImpCheckSnap(aLO);
+
+ if (!getSdrDragView().IsMoveSnapOnlyTopLeft())
+ {
+ ImpCheckSnap(aRO);
+ ImpCheckSnap(aLU);
+ ImpCheckSnap(aRU);
+ }
+
+ Point aPnt(aNoSnapPnt.X()+nBestXSnap,aNoSnapPnt.Y()+nBestYSnap);
+ bool bOrtho=getSdrDragView().IsOrtho();
+
+ if (bOrtho)
+ OrthoDistance8(DragStat().GetStart(),aPnt,getSdrDragView().IsBigOrtho());
+
+ if (!DragStat().CheckMinMoved(aNoSnapPnt))
+ return;
+
+ Point aPt1(aPnt);
+ tools::Rectangle aLR(getSdrDragView().GetWorkArea());
+ bool bWorkArea=!aLR.IsEmpty();
+ bool bDragLimit=IsDragLimit();
+
+ if (bDragLimit || bWorkArea)
+ {
+ tools::Rectangle aSR2(GetMarkedRect());
+ Point aD(aPt1-DragStat().GetStart());
+
+ if (bDragLimit)
+ {
+ tools::Rectangle aR2(GetDragLimitRect());
+
+ if (bWorkArea)
+ aLR.Intersection(aR2);
+ else
+ aLR=aR2;
+ }
+
+ if (aSR2.Left()>aLR.Left() || aSR2.Right()<aLR.Right())
+ { // any space to move to?
+ aSR2.Move(aD.X(),0);
+
+ if (aSR2.Left()<aLR.Left())
+ {
+ aPt1.AdjustX( -(aSR2.Left()-aLR.Left()) );
+ }
+ else if (aSR2.Right()>aLR.Right())
+ {
+ aPt1.AdjustX( -(aSR2.Right()-aLR.Right()) );
+ }
+ }
+ else
+ aPt1.setX(DragStat().GetStart().X() ); // no space to move to
+
+ if (aSR2.Top()>aLR.Top() || aSR2.Bottom()<aLR.Bottom())
+ { // any space to move to?
+ aSR2.Move(0,aD.Y());
+
+ if (aSR2.Top()<aLR.Top())
+ {
+ aPt1.AdjustY( -(aSR2.Top()-aLR.Top()) );
+ }
+ else if (aSR2.Bottom()>aLR.Bottom())
+ {
+ aPt1.AdjustY( -(aSR2.Bottom()-aLR.Bottom()) );
+ }
+ }
+ else
+ aPt1.setY(DragStat().GetStart().Y() ); // no space to move to
+ }
+
+ if (getSdrDragView().IsDraggingGluePoints())
+ { // restrict gluepoints to the BoundRect of the Obj
+ aPt1-=DragStat().GetStart();
+ const SdrMarkList& rML=GetMarkedObjectList();
+ const size_t nMarkCount=rML.GetMarkCount();
+
+ for (size_t nMarkNum=0; nMarkNum<nMarkCount; ++nMarkNum)
+ {
+ const SdrMark* pM=rML.GetMark(nMarkNum);
+ const SdrUShortCont& rPts = pM->GetMarkedGluePoints();
+
+ if (!rPts.empty())
+ {
+ const SdrObject* pObj=pM->GetMarkedSdrObj();
+ const SdrGluePointList* pGPL=pObj->GetGluePointList();
+ tools::Rectangle aBound(pObj->GetCurrentBoundRect());
+
+ for (sal_uInt16 nId : rPts)
+ {
+ sal_uInt16 nGlueNum=pGPL->FindGluePoint(nId);
+
+ if (nGlueNum!=SDRGLUEPOINT_NOTFOUND)
+ {
+ Point aPt((*pGPL)[nGlueNum].GetAbsolutePos(*pObj));
+ aPt+=aPt1; // move by this much
+ if (aPt.X()<aBound.Left() ) aPt1.AdjustX( -(aPt.X()-aBound.Left()) ) ;
+ if (aPt.X()>aBound.Right() ) aPt1.AdjustX( -(aPt.X()-aBound.Right()) ) ;
+ if (aPt.Y()<aBound.Top() ) aPt1.AdjustY( -(aPt.Y()-aBound.Top()) ) ;
+ if (aPt.Y()>aBound.Bottom()) aPt1.AdjustY( -(aPt.Y()-aBound.Bottom()) );
+ }
+ }
+ }
+ }
+
+ aPt1+=DragStat().GetStart();
+ }
+
+ if (bOrtho)
+ OrthoDistance8(DragStat().GetStart(),aPt1,false);
+
+ if (aPt1!=DragStat().GetNow())
+ {
+ Hide();
+ DragStat().NextMove(aPt1);
+ tools::Rectangle aAction(GetMarkedRect());
+ aAction.Move(DragStat().GetDX(),DragStat().GetDY());
+ DragStat().SetActionRect(aAction);
+ Show();
+ }
+}
+
+bool SdrDragMove::EndSdrDrag(bool bCopy)
+{
+ Hide();
+
+ if (getSdrDragView().IsInsObjPoint() || getSdrDragView().IsInsGluePoint())
+ bCopy=false;
+
+ if (IsDraggingPoints())
+ {
+ getSdrDragView().MoveMarkedPoints(Size(DragStat().GetDX(),DragStat().GetDY()));
+ }
+ else if (IsDraggingGluePoints())
+ {
+ getSdrDragView().MoveMarkedGluePoints(Size(DragStat().GetDX(),DragStat().GetDY()),bCopy);
+ }
+ else
+ {
+ getSdrDragView().MoveMarkedObj(Size(DragStat().GetDX(),DragStat().GetDY()),bCopy);
+ }
+
+ return true;
+}
+
+PointerStyle SdrDragMove::GetSdrDragPointer() const
+{
+ if (IsDraggingPoints() || IsDraggingGluePoints())
+ {
+ return PointerStyle::MovePoint;
+ }
+ else
+ {
+ return PointerStyle::Move;
+ }
+}
+
+
+SdrDragResize::SdrDragResize(SdrDragView& rNewView)
+: SdrDragMethod(rNewView),
+ aXFact(1,1),
+ aYFact(1,1)
+{
+}
+
+OUString SdrDragResize::GetSdrDragComment() const
+{
+ OUString aStr = ImpGetDescriptionStr(STR_DragMethResize);
+ Fraction aFact1(1,1);
+ Point aStart(DragStat().GetStart());
+ Point aRef(DragStat().GetRef1());
+ sal_Int32 nXDiv(aStart.X() - aRef.X());
+
+ if(!nXDiv)
+ nXDiv = 1;
+
+ sal_Int32 nYDiv(aStart.Y() - aRef.Y());
+
+ if(!nYDiv)
+ nYDiv = 1;
+
+ bool bX(aXFact != aFact1 && std::abs(nXDiv) > 1);
+ bool bY(aYFact != aFact1 && std::abs(nYDiv) > 1);
+
+ if(bX || bY)
+ {
+ aStr += " (";
+
+ bool bEqual(aXFact == aYFact);
+ if(bX)
+ {
+ if(!bEqual)
+ aStr += "x=";
+
+ aStr += SdrModel::GetPercentString(aXFact);
+ }
+
+ if(bY && !bEqual)
+ {
+ if(bX)
+ aStr += " ";
+
+ aStr += "y=" + SdrModel::GetPercentString(aYFact);
+ }
+
+ aStr += ")";
+ }
+
+ if(getSdrDragView().IsDragWithCopy())
+ aStr += SvxResId(STR_EditWithCopy);
+ return aStr;
+}
+
+bool SdrDragResize::BeginSdrDrag()
+{
+ SdrHdlKind eRefHdl=SdrHdlKind::Move;
+ SdrHdl* pRefHdl=nullptr;
+
+ switch (GetDragHdlKind())
+ {
+ case SdrHdlKind::UpperLeft: eRefHdl=SdrHdlKind::LowerRight; break;
+ case SdrHdlKind::Upper: eRefHdl=SdrHdlKind::Lower; DragStat().SetHorFixed(true); break;
+ case SdrHdlKind::UpperRight: eRefHdl=SdrHdlKind::LowerLeft; break;
+ case SdrHdlKind::Left : eRefHdl=SdrHdlKind::Right; DragStat().SetVerFixed(true); break;
+ case SdrHdlKind::Right: eRefHdl=SdrHdlKind::Left ; DragStat().SetVerFixed(true); break;
+ case SdrHdlKind::LowerLeft: eRefHdl=SdrHdlKind::UpperRight; break;
+ case SdrHdlKind::Lower: eRefHdl=SdrHdlKind::Upper; DragStat().SetHorFixed(true); break;
+ case SdrHdlKind::LowerRight: eRefHdl=SdrHdlKind::UpperLeft; break;
+ default: break;
+ }
+
+ if (eRefHdl!=SdrHdlKind::Move)
+ pRefHdl=GetHdlList().GetHdl(eRefHdl);
+
+ if (pRefHdl!=nullptr && !getSdrDragView().IsResizeAtCenter())
+ {
+ DragStat().SetRef1(pRefHdl->GetPos());
+ }
+ else
+ {
+ SdrHdl* pRef1=GetHdlList().GetHdl(SdrHdlKind::UpperLeft);
+ SdrHdl* pRef2=GetHdlList().GetHdl(SdrHdlKind::LowerRight);
+
+ if (pRef1!=nullptr && pRef2!=nullptr)
+ {
+ DragStat().SetRef1(tools::Rectangle(pRef1->GetPos(),pRef2->GetPos()).Center());
+ }
+ else
+ {
+ DragStat().SetRef1(GetMarkedRect().Center());
+ }
+ }
+
+ Show();
+
+ return true;
+}
+
+basegfx::B2DHomMatrix SdrDragResize::getCurrentTransformation() const
+{
+ basegfx::B2DHomMatrix aRetval(basegfx::utils::createTranslateB2DHomMatrix(
+ -DragStat().GetRef1().X(), -DragStat().GetRef1().Y()));
+ aRetval.scale(double(aXFact), double(aYFact));
+ aRetval.translate(DragStat().GetRef1().X(), DragStat().GetRef1().Y());
+
+ return aRetval;
+}
+
+void SdrDragResize::MoveSdrDrag(const Point& rNoSnapPnt)
+{
+ Point aPnt(GetSnapPos(rNoSnapPnt));
+ Point aStart(DragStat().GetStart());
+ Point aRef(DragStat().GetRef1());
+ Fraction aMaxFact(0x7FFFFFFF,1);
+ tools::Rectangle aLR(getSdrDragView().GetWorkArea());
+ bool bWorkArea=!aLR.IsEmpty();
+ bool bDragLimit=IsDragLimit();
+
+ if (bDragLimit || bWorkArea)
+ {
+ tools::Rectangle aSR(GetMarkedRect());
+
+ if (bDragLimit)
+ {
+ tools::Rectangle aR2(GetDragLimitRect());
+
+ if (bWorkArea)
+ aLR.Intersection(aR2);
+ else
+ aLR=aR2;
+ }
+
+ if (aPnt.X()<aLR.Left())
+ aPnt.setX(aLR.Left() );
+ else if (aPnt.X()>aLR.Right())
+ aPnt.setX(aLR.Right() );
+
+ if (aPnt.Y()<aLR.Top())
+ aPnt.setY(aLR.Top() );
+ else if (aPnt.Y()>aLR.Bottom())
+ aPnt.setY(aLR.Bottom() );
+
+ if (aRef.X()>aSR.Left())
+ {
+ Fraction aMax(aRef.X()-aLR.Left(),aRef.X()-aSR.Left());
+
+ if (aMax<aMaxFact)
+ aMaxFact=aMax;
+ }
+
+ if (aRef.X()<aSR.Right())
+ {
+ Fraction aMax(aLR.Right()-aRef.X(),aSR.Right()-aRef.X());
+
+ if (aMax<aMaxFact)
+ aMaxFact=aMax;
+ }
+
+ if (aRef.Y()>aSR.Top())
+ {
+ Fraction aMax(aRef.Y()-aLR.Top(),aRef.Y()-aSR.Top());
+
+ if (aMax<aMaxFact)
+ aMaxFact=aMax;
+ }
+
+ if (aRef.Y()<aSR.Bottom())
+ {
+ Fraction aMax(aLR.Bottom()-aRef.Y(),aSR.Bottom()-aRef.Y());
+
+ if (aMax<aMaxFact)
+ aMaxFact=aMax;
+ }
+ }
+
+ tools::Long nXDiv=aStart.X()-aRef.X(); if (nXDiv==0) nXDiv=1;
+ tools::Long nYDiv=aStart.Y()-aRef.Y(); if (nYDiv==0) nYDiv=1;
+ tools::Long nXMul=aPnt.X()-aRef.X();
+ tools::Long nYMul=aPnt.Y()-aRef.Y();
+
+ if (nXDiv<0)
+ {
+ nXDiv=-nXDiv;
+ nXMul=-nXMul;
+ }
+
+ if (nYDiv<0)
+ {
+ nYDiv=-nYDiv;
+ nYMul=-nYMul;
+ }
+
+ bool bXNeg=nXMul<0; if (bXNeg) nXMul=-nXMul;
+ bool bYNeg=nYMul<0; if (bYNeg) nYMul=-nYMul;
+ bool bOrtho=getSdrDragView().IsOrtho() || !getSdrDragView().IsResizeAllowed();
+
+ if (!DragStat().IsHorFixed() && !DragStat().IsVerFixed())
+ {
+ if (std::abs(nXDiv)<=1 || std::abs(nYDiv)<=1)
+ bOrtho=false;
+
+ if (bOrtho)
+ {
+ if ((Fraction(nXMul,nXDiv)>Fraction(nYMul,nYDiv)) !=getSdrDragView().IsBigOrtho())
+ {
+ nXMul=nYMul;
+ nXDiv=nYDiv;
+ }
+ else
+ {
+ nYMul=nXMul;
+ nYDiv=nXDiv;
+ }
+ }
+ }
+ else
+ {
+ if (bOrtho)
+ {
+ if (DragStat().IsHorFixed())
+ {
+ bXNeg=false;
+ nXMul=nYMul;
+ nXDiv=nYDiv;
+ }
+
+ if (DragStat().IsVerFixed())
+ {
+ bYNeg=false;
+ nYMul=nXMul;
+ nYDiv=nXDiv;
+ }
+ }
+ else
+ {
+ if (DragStat().IsHorFixed())
+ {
+ bXNeg=false;
+ nXMul=1;
+ nXDiv=1;
+ }
+
+ if (DragStat().IsVerFixed())
+ {
+ bYNeg=false;
+ nYMul=1;
+ nYDiv=1;
+ }
+ }
+ }
+
+ Fraction aNewXFact(nXMul,nXDiv);
+ Fraction aNewYFact(nYMul,nYDiv);
+
+ if (bOrtho)
+ {
+ if (aNewXFact>aMaxFact)
+ {
+ aNewXFact=aMaxFact;
+ aNewYFact=aMaxFact;
+ }
+
+ if (aNewYFact>aMaxFact)
+ {
+ aNewXFact=aMaxFact;
+ aNewYFact=aMaxFact;
+ }
+ }
+
+ if (bXNeg)
+ aNewXFact=Fraction(-aNewXFact.GetNumerator(),aNewXFact.GetDenominator());
+
+ if (bYNeg)
+ aNewYFact=Fraction(-aNewYFact.GetNumerator(),aNewYFact.GetDenominator());
+
+ if (DragStat().CheckMinMoved(aPnt))
+ {
+ if ((!DragStat().IsHorFixed() && aPnt.X()!=DragStat().GetNow().X()) ||
+ (!DragStat().IsVerFixed() && aPnt.Y()!=DragStat().GetNow().Y()))
+ {
+ Hide();
+ DragStat().NextMove(aPnt);
+ aXFact=aNewXFact;
+ aYFact=aNewYFact;
+ Show();
+ }
+ }
+}
+
+void SdrDragResize::applyCurrentTransformationToSdrObject(SdrObject& rTarget)
+{
+ rTarget.Resize(DragStat().GetRef1(),aXFact,aYFact);
+}
+
+bool SdrDragResize::EndSdrDrag(bool bCopy)
+{
+ Hide();
+
+ if (IsDraggingPoints())
+ {
+ getSdrDragView().ResizeMarkedPoints(DragStat().GetRef1(),aXFact,aYFact);
+ }
+ else if (IsDraggingGluePoints())
+ {
+ getSdrDragView().ResizeMarkedGluePoints(DragStat().GetRef1(),aXFact,aYFact,bCopy);
+ }
+ else
+ {
+ getSdrDragView().ResizeMarkedObj(DragStat().GetRef1(),aXFact,aYFact,bCopy);
+ }
+
+ return true;
+}
+
+PointerStyle SdrDragResize::GetSdrDragPointer() const
+{
+ const SdrHdl* pHdl=GetDragHdl();
+
+ if (pHdl!=nullptr)
+ {
+ return pHdl->GetPointer();
+ }
+
+ return PointerStyle::Move;
+}
+
+
+void SdrDragRotate::applyCurrentTransformationToSdrObject(SdrObject& rTarget)
+{
+ rTarget.Rotate(DragStat().GetRef1(), nAngle, nSin, nCos);
+}
+
+SdrDragRotate::SdrDragRotate(SdrDragView& rNewView)
+: SdrDragMethod(rNewView),
+ nSin(0.0),
+ nCos(1.0),
+ nAngle0(0),
+ nAngle(0),
+ bRight(false)
+{
+}
+
+OUString SdrDragRotate::GetSdrDragComment() const
+{
+ OUString aStr = ImpGetDescriptionStr(STR_DragMethRotate) +
+ " (";
+ Degree100 nTmpAngle(NormAngle36000(nAngle));
+
+ if(bRight && nAngle)
+ {
+ nTmpAngle -= 36000_deg100;
+ }
+
+ aStr += SdrModel::GetAngleString(nTmpAngle) + ")";
+
+ if(getSdrDragView().IsDragWithCopy())
+ aStr += SvxResId(STR_EditWithCopy);
+ return aStr;
+}
+
+bool SdrDragRotate::BeginSdrDrag()
+{
+ SdrHdl* pH=GetHdlList().GetHdl(SdrHdlKind::Ref1);
+
+ if (nullptr != pH)
+ {
+ Show();
+ DragStat().SetRef1(pH->GetPos());
+ nAngle0=GetAngle(DragStat().GetStart()-DragStat().GetRef1());
+ return true;
+ }
+
+ // RotGrfFlyFrame: Support rotation around center *without* Ref1 (normally
+ // the rotation point)
+ const tools::Rectangle aLocalMarkRect(getSdrDragView().GetMarkedObjRect());
+
+ if(!aLocalMarkRect.IsEmpty())
+ {
+ Show();
+ DragStat().SetRef1(aLocalMarkRect.Center());
+ nAngle0=GetAngle(DragStat().GetStart()-DragStat().GetRef1());
+ return true;
+ }
+
+ OSL_FAIL("SdrDragRotate::BeginSdrDrag(): No reference point handle found.");
+ return false;
+}
+
+basegfx::B2DHomMatrix SdrDragRotate::getCurrentTransformation() const
+{
+ return basegfx::utils::createRotateAroundPoint(
+ DragStat().GetRef1().X(), DragStat().GetRef1().Y(),
+ -atan2(nSin, nCos));
+}
+
+void SdrDragRotate::MoveSdrDrag(const Point& rPnt_)
+{
+ Point aPnt(rPnt_);
+ if (!DragStat().CheckMinMoved(aPnt))
+ return;
+
+ Degree100 nNewAngle=NormAngle36000(GetAngle(aPnt-DragStat().GetRef1())-nAngle0);
+ Degree100 nSA(0);
+
+ if (getSdrDragView().IsAngleSnapEnabled())
+ nSA=getSdrDragView().GetSnapAngle();
+
+ if (!getSdrDragView().IsRotateAllowed())
+ nSA=9000_deg100;
+
+ if (nSA)
+ { // angle snapping
+ nNewAngle += nSA / 2_deg100;
+ nNewAngle /= nSA;
+ nNewAngle *= nSA;
+ }
+
+ nNewAngle=NormAngle18000(nNewAngle);
+
+ if (nAngle==nNewAngle)
+ return;
+
+ sal_uInt16 nSekt0=GetAngleSector(nAngle);
+ sal_uInt16 nSekt1=GetAngleSector(nNewAngle);
+
+ if (nSekt0==0 && nSekt1==3)
+ bRight=true;
+
+ if (nSekt0==3 && nSekt1==0)
+ bRight=false;
+
+ nAngle=nNewAngle;
+ double a = toRadians(nAngle);
+ double nSin1=sin(a); // calculate now, so as little time as possible
+ double nCos1=cos(a); // passes between Hide() and Show()
+ Hide();
+ nSin=nSin1;
+ nCos=nCos1;
+ DragStat().NextMove(aPnt);
+ Show();
+}
+
+bool SdrDragRotate::EndSdrDrag(bool bCopy)
+{
+ Hide();
+
+ if (nAngle!=0_deg100)
+ {
+ if (IsDraggingPoints())
+ {
+ getSdrDragView().RotateMarkedPoints(DragStat().GetRef1(),nAngle);
+ }
+ else if (IsDraggingGluePoints())
+ {
+ getSdrDragView().RotateMarkedGluePoints(DragStat().GetRef1(),nAngle,bCopy);
+ }
+ else
+ {
+ getSdrDragView().RotateMarkedObj(DragStat().GetRef1(),nAngle,bCopy);
+ }
+ }
+ return true;
+}
+
+PointerStyle SdrDragRotate::GetSdrDragPointer() const
+{
+ return PointerStyle::Rotate;
+}
+
+
+SdrDragShear::SdrDragShear(SdrDragView& rNewView, bool bSlant1)
+: SdrDragMethod(rNewView),
+ aFact(1,1),
+ nAngle0(0),
+ nAngle(0),
+ nTan(0.0),
+ bVertical(false),
+ bResize(false),
+ bUpSideDown(false),
+ bSlant(bSlant1)
+{
+}
+
+OUString SdrDragShear::GetSdrDragComment() const
+{
+ OUString aStr = ImpGetDescriptionStr(STR_DragMethShear) +
+ " (";
+
+ Degree100 nTmpAngle(nAngle);
+
+ if(bUpSideDown)
+ nTmpAngle += 18000_deg100;
+
+ nTmpAngle = NormAngle18000(nTmpAngle);
+
+ aStr += SdrModel::GetAngleString(nTmpAngle) + ")";
+
+ if(getSdrDragView().IsDragWithCopy())
+ aStr += SvxResId(STR_EditWithCopy);
+ return aStr;
+}
+
+bool SdrDragShear::BeginSdrDrag()
+{
+ SdrHdlKind eRefHdl=SdrHdlKind::Move;
+ SdrHdl* pRefHdl=nullptr;
+
+ switch (GetDragHdlKind())
+ {
+ case SdrHdlKind::Upper: eRefHdl=SdrHdlKind::Lower; break;
+ case SdrHdlKind::Lower: eRefHdl=SdrHdlKind::Upper; break;
+ case SdrHdlKind::Left : eRefHdl=SdrHdlKind::Right; bVertical=true; break;
+ case SdrHdlKind::Right: eRefHdl=SdrHdlKind::Left ; bVertical=true; break;
+ default: break;
+ }
+
+ if (eRefHdl!=SdrHdlKind::Move)
+ pRefHdl=GetHdlList().GetHdl(eRefHdl);
+
+ if (pRefHdl!=nullptr)
+ {
+ DragStat().SetRef1(pRefHdl->GetPos());
+ nAngle0=GetAngle(DragStat().GetStart()-DragStat().GetRef1());
+ }
+ else
+ {
+ OSL_FAIL("SdrDragShear::BeginSdrDrag(): No reference point handle for shearing found.");
+ return false;
+ }
+
+ Show();
+ return true;
+}
+
+basegfx::B2DHomMatrix SdrDragShear::getCurrentTransformation() const
+{
+ basegfx::B2DHomMatrix aRetval(basegfx::utils::createTranslateB2DHomMatrix(
+ -DragStat().GetRef1().X(), -DragStat().GetRef1().Y()));
+
+ if (bResize)
+ {
+ if (bVertical)
+ {
+ aRetval.scale(double(aFact), 1.0);
+ aRetval.shearY(-nTan);
+ }
+ else
+ {
+ aRetval.scale(1.0, double(aFact));
+ aRetval.shearX(-nTan);
+ }
+ }
+
+ aRetval.translate(DragStat().GetRef1().X(), DragStat().GetRef1().Y());
+
+ return aRetval;
+}
+
+void SdrDragShear::MoveSdrDrag(const Point& rPnt)
+{
+ if (!DragStat().CheckMinMoved(rPnt))
+ return;
+
+ bResize=!getSdrDragView().IsOrtho();
+ Degree100 nSA(0);
+
+ if (getSdrDragView().IsAngleSnapEnabled())
+ nSA=getSdrDragView().GetSnapAngle();
+
+ Point aP0(DragStat().GetStart());
+ Point aPnt(rPnt);
+ Fraction aNewFract(1,1);
+
+ // if angle snapping not activated, snap to raster (except when using slant)
+ if (nSA==0_deg100 && !bSlant)
+ aPnt=GetSnapPos(aPnt);
+
+ if (!bSlant && !bResize)
+ { // shear, but no resize
+ if (bVertical)
+ aPnt.setX(aP0.X() );
+ else
+ aPnt.setY(aP0.Y() );
+ }
+
+ Point aRef(DragStat().GetRef1());
+ Point aDif(aPnt-aRef);
+
+ Degree100 nNewAngle(0);
+
+ if (bSlant)
+ {
+ nNewAngle=NormAngle18000(-(GetAngle(aDif)-nAngle0));
+
+ if (bVertical)
+ nNewAngle=NormAngle18000(-nNewAngle);
+ }
+ else
+ {
+ if (bVertical)
+ nNewAngle=NormAngle18000(GetAngle(aDif));
+ else
+ nNewAngle=NormAngle18000(-(GetAngle(aDif)-9000_deg100));
+
+ if (nNewAngle<Degree100(-9000) || nNewAngle>9000_deg100)
+ nNewAngle=NormAngle18000(nNewAngle+18000_deg100);
+
+ if (bResize)
+ {
+ Point aPt2(aPnt);
+
+ if (nSA!=0_deg100)
+ aPt2=GetSnapPos(aPnt); // snap this one in any case
+
+ if (bVertical)
+ {
+ aNewFract=Fraction(aPt2.X()-aRef.X(),aP0.X()-aRef.X());
+ }
+ else
+ {
+ aNewFract=Fraction(aPt2.Y()-aRef.Y(),aP0.Y()-aRef.Y());
+ }
+ }
+ }
+
+ bool bNeg=nNewAngle<0_deg100;
+
+ if (bNeg)
+ nNewAngle=-nNewAngle;
+
+ if (nSA)
+ { // angle snapping
+ nNewAngle += nSA / 2_deg100;
+ nNewAngle /= nSA;
+ nNewAngle *= nSA;
+ }
+
+ nNewAngle=NormAngle36000(nNewAngle);
+ bUpSideDown=nNewAngle>9000_deg100 && nNewAngle<27000_deg100;
+
+ if (bSlant)
+ { // calculate resize for slant
+ // when angle snapping is activated, disable 89 degree limit
+ Degree100 nTmpAngle=nNewAngle;
+ if (bUpSideDown) nNewAngle -= 18000_deg100;
+ if (bNeg) nTmpAngle=-nTmpAngle;
+ bResize=true;
+ aNewFract = cos(toRadians(nTmpAngle));
+ aFact.ReduceInaccurate(10); // three decimals should be enough
+ }
+
+ if (nNewAngle > 8900_deg100)
+ nNewAngle = 8900_deg100;
+
+ if (bNeg)
+ nNewAngle=-nNewAngle;
+
+ if (nAngle!=nNewAngle || aFact!=aNewFract)
+ {
+ nAngle=nNewAngle;
+ aFact=aNewFract;
+ double a = toRadians(nAngle);
+ double nTan1=tan(a); // calculate now, so as little time as possible passes between Hide() and Show()
+ Hide();
+ nTan=nTan1;
+ DragStat().NextMove(rPnt);
+ Show();
+ }
+}
+
+void SdrDragShear::applyCurrentTransformationToSdrObject(SdrObject& rTarget)
+{
+ if (bResize)
+ {
+ if (bVertical)
+ {
+ rTarget.Resize(DragStat().GetRef1(),aFact,Fraction(1,1));
+ }
+ else
+ {
+ rTarget.Resize(DragStat().GetRef1(),Fraction(1,1),aFact);
+ }
+ }
+
+ if (nAngle)
+ {
+ rTarget.Shear(DragStat().GetRef1(), nAngle, nTan, bVertical);
+ }
+}
+
+bool SdrDragShear::EndSdrDrag(bool bCopy)
+{
+ Hide();
+
+ if (bResize && aFact==Fraction(1,1))
+ bResize=false;
+
+ if (nAngle || bResize)
+ {
+ if (nAngle && bResize)
+ {
+ OUString aStr = ImpGetDescriptionStr(STR_EditShear);
+
+ if (bCopy)
+ aStr += SvxResId(STR_EditWithCopy);
+
+ getSdrDragView().BegUndo(aStr);
+ }
+
+ if (bResize)
+ {
+ if (bVertical)
+ {
+ getSdrDragView().ResizeMarkedObj(DragStat().GetRef1(),aFact,Fraction(1,1),bCopy);
+ }
+ else
+ {
+ getSdrDragView().ResizeMarkedObj(DragStat().GetRef1(),Fraction(1,1),aFact,bCopy);
+ }
+
+ bCopy=false;
+ }
+
+ if (nAngle)
+ {
+ getSdrDragView().ShearMarkedObj(DragStat().GetRef1(),nAngle,bVertical,bCopy);
+ }
+
+ if (nAngle && bResize)
+ getSdrDragView().EndUndo();
+
+ return true;
+ }
+
+ return false;
+}
+
+PointerStyle SdrDragShear::GetSdrDragPointer() const
+{
+ if (bVertical)
+ return PointerStyle::VShear;
+ else
+ return PointerStyle::HShear;
+}
+
+
+void SdrDragMirror::applyCurrentTransformationToSdrObject(SdrObject& rTarget)
+{
+ if(bMirrored)
+ {
+ rTarget.Mirror(DragStat().GetRef1(), DragStat().GetRef2());
+ }
+}
+
+SdrDragMirror::SdrDragMirror(SdrDragView& rNewView)
+: SdrDragMethod(rNewView),
+ nAngle(0),
+ bMirrored(false),
+ bSide0(false)
+{
+}
+
+bool SdrDragMirror::ImpCheckSide(const Point& rPnt) const
+{
+ Degree100 nAngle1=GetAngle(rPnt-DragStat().GetRef1());
+ nAngle1-=nAngle;
+ nAngle1=NormAngle36000(nAngle1);
+
+ return nAngle1<18000_deg100;
+}
+
+OUString SdrDragMirror::GetSdrDragComment() const
+{
+ OUString aStr;
+ if (aDif.X()==0)
+ aStr = ImpGetDescriptionStr(STR_DragMethMirrorHori);
+ else if (aDif.Y()==0)
+ aStr = ImpGetDescriptionStr(STR_DragMethMirrorVert);
+ else if (std::abs(aDif.X()) == std::abs(aDif.Y()))
+ aStr = ImpGetDescriptionStr(STR_DragMethMirrorDiag);
+ else
+ aStr = ImpGetDescriptionStr(STR_DragMethMirrorFree);
+
+ if (getSdrDragView().IsDragWithCopy())
+ aStr+=SvxResId(STR_EditWithCopy);
+ return aStr;
+}
+
+bool SdrDragMirror::BeginSdrDrag()
+{
+ SdrHdl* pH1=GetHdlList().GetHdl(SdrHdlKind::Ref1);
+ SdrHdl* pH2=GetHdlList().GetHdl(SdrHdlKind::Ref2);
+
+ if (pH1!=nullptr && pH2!=nullptr)
+ {
+ DragStat().SetRef1(pH1->GetPos());
+ DragStat().SetRef2(pH2->GetPos());
+ Ref1()=pH1->GetPos();
+ Ref2()=pH2->GetPos();
+ aDif=pH2->GetPos()-pH1->GetPos();
+ bool b90=(aDif.X()==0) || aDif.Y()==0;
+ bool b45=b90 || (std::abs(aDif.X()) == std::abs(aDif.Y()));
+ nAngle=NormAngle36000(GetAngle(aDif));
+
+ if (!getSdrDragView().IsMirrorAllowed() && !b45)
+ return false; // free choice of axis angle not allowed
+
+ if (!getSdrDragView().IsMirrorAllowed() && !b90)
+ return false; // 45 degrees not allowed either
+
+ bSide0=ImpCheckSide(DragStat().GetStart());
+ Show();
+ return true;
+ }
+ else
+ {
+ OSL_FAIL("SdrDragMirror::BeginSdrDrag(): Axis of reflection not found.");
+ return false;
+ }
+}
+
+basegfx::B2DHomMatrix SdrDragMirror::getCurrentTransformation() const
+{
+ basegfx::B2DHomMatrix aRetval;
+
+ if (bMirrored)
+ {
+ const double fDeltaX(DragStat().GetRef2().X() - DragStat().GetRef1().X());
+ const double fDeltaY(DragStat().GetRef2().Y() - DragStat().GetRef1().Y());
+ const double fRotation(atan2(fDeltaY, fDeltaX));
+
+ aRetval = basegfx::utils::createTranslateB2DHomMatrix(-DragStat().GetRef1().X(), -DragStat().GetRef1().Y());
+ aRetval.rotate(-fRotation);
+ aRetval.scale(1.0, -1.0);
+ aRetval.rotate(fRotation);
+ aRetval.translate(DragStat().GetRef1().X(), DragStat().GetRef1().Y());
+ }
+
+ return aRetval;
+}
+
+void SdrDragMirror::MoveSdrDrag(const Point& rPnt)
+{
+ if (!DragStat().CheckMinMoved(rPnt))
+ return;
+
+ bool bNewSide=ImpCheckSide(rPnt);
+ bool bNewMirrored=bSide0!=bNewSide;
+
+ if (bMirrored!=bNewMirrored)
+ {
+ Hide();
+ bMirrored=bNewMirrored;
+ DragStat().NextMove(rPnt);
+ Show();
+ }
+}
+
+bool SdrDragMirror::EndSdrDrag(bool bCopy)
+{
+ Hide();
+
+ if (bMirrored)
+ {
+ getSdrDragView().MirrorMarkedObj(DragStat().GetRef1(),DragStat().GetRef2(),bCopy);
+ }
+
+ return true;
+}
+
+PointerStyle SdrDragMirror::GetSdrDragPointer() const
+{
+ return PointerStyle::Mirror;
+}
+
+
+SdrDragGradient::SdrDragGradient(SdrDragView& rNewView, bool bGrad)
+: SdrDragMethod(rNewView),
+ pIAOHandle(nullptr),
+ bIsGradient(bGrad)
+{
+}
+
+OUString SdrDragGradient::GetSdrDragComment() const
+{
+ if(IsGradient())
+ return ImpGetDescriptionStr(STR_DragMethGradient);
+ else
+ return ImpGetDescriptionStr(STR_DragMethTransparence);
+}
+
+bool SdrDragGradient::BeginSdrDrag()
+{
+ bool bRetval(false);
+
+ pIAOHandle = static_cast<SdrHdlGradient*>(GetHdlList().GetHdl(IsGradient() ? SdrHdlKind::Gradient : SdrHdlKind::Transparence));
+
+ if(pIAOHandle)
+ {
+ // save old values
+ DragStat().SetRef1( pIAOHandle->GetPos() );
+ DragStat().SetRef2( pIAOHandle->Get2ndPos() );
+
+ // what was hit?
+ bool bHit(false);
+ SdrHdlColor* pColHdl = pIAOHandle->GetColorHdl1();
+
+ // init handling flags
+ pIAOHandle->SetMoveSingleHandle(false);
+ pIAOHandle->SetMoveFirstHandle(false);
+
+ // test first color handle
+ if(pColHdl)
+ {
+ basegfx::B2DPoint aPosition(DragStat().GetStart().X(), DragStat().GetStart().Y());
+
+ if(pColHdl->getOverlayObjectList().isHitLogic(aPosition))
+ {
+ bHit = true;
+ pIAOHandle->SetMoveSingleHandle(true);
+ pIAOHandle->SetMoveFirstHandle(true);
+ }
+ }
+
+ // test second color handle
+ pColHdl = pIAOHandle->GetColorHdl2();
+
+ if(!bHit && pColHdl)
+ {
+ basegfx::B2DPoint aPosition(DragStat().GetStart().X(), DragStat().GetStart().Y());
+
+ if(pColHdl->getOverlayObjectList().isHitLogic(aPosition))
+ {
+ bHit = true;
+ pIAOHandle->SetMoveSingleHandle(true);
+ }
+ }
+
+ // test gradient handle itself
+ if(!bHit)
+ {
+ basegfx::B2DPoint aPosition(DragStat().GetStart().X(), DragStat().GetStart().Y());
+
+ if(pIAOHandle->getOverlayObjectList().isHitLogic(aPosition))
+ {
+ bHit = true;
+ }
+ }
+
+ // everything up and running :o}
+ bRetval = bHit;
+ }
+ else
+ {
+ OSL_FAIL("SdrDragGradient::BeginSdrDrag(): IAOGradient not found.");
+ }
+
+ return bRetval;
+}
+
+void SdrDragGradient::MoveSdrDrag(const Point& rPnt)
+{
+ if(!(pIAOHandle && DragStat().CheckMinMoved(rPnt)))
+ return;
+
+ DragStat().NextMove(rPnt);
+
+ // Do the Move here!!! DragStat().GetStart()
+ Point aMoveDiff = rPnt - DragStat().GetStart();
+
+ if(pIAOHandle->IsMoveSingleHandle())
+ {
+ if(pIAOHandle->IsMoveFirstHandle())
+ {
+ pIAOHandle->SetPos(DragStat().GetRef1() + aMoveDiff);
+ if(pIAOHandle->GetColorHdl1())
+ pIAOHandle->GetColorHdl1()->SetPos(DragStat().GetRef1() + aMoveDiff);
+ }
+ else
+ {
+ pIAOHandle->Set2ndPos(DragStat().GetRef2() + aMoveDiff);
+ if(pIAOHandle->GetColorHdl2())
+ pIAOHandle->GetColorHdl2()->SetPos(DragStat().GetRef2() + aMoveDiff);
+ }
+ }
+ else
+ {
+ pIAOHandle->SetPos(DragStat().GetRef1() + aMoveDiff);
+ pIAOHandle->Set2ndPos(DragStat().GetRef2() + aMoveDiff);
+
+ if(pIAOHandle->GetColorHdl1())
+ pIAOHandle->GetColorHdl1()->SetPos(DragStat().GetRef1() + aMoveDiff);
+
+ if(pIAOHandle->GetColorHdl2())
+ pIAOHandle->GetColorHdl2()->SetPos(DragStat().GetRef2() + aMoveDiff);
+ }
+
+ // new state
+ pIAOHandle->FromIAOToItem(getSdrDragView().GetMarkedObjectList().GetMark(0)->GetMarkedSdrObj(), false, false);
+}
+
+bool SdrDragGradient::EndSdrDrag(bool /*bCopy*/)
+{
+ Ref1() = pIAOHandle->GetPos();
+ Ref2() = pIAOHandle->Get2ndPos();
+
+ // new state
+ pIAOHandle->FromIAOToItem(getSdrDragView().GetMarkedObjectList().GetMark(0)->GetMarkedSdrObj(), true, true);
+
+ return true;
+}
+
+void SdrDragGradient::CancelSdrDrag()
+{
+ // restore old values
+ pIAOHandle->SetPos(DragStat().GetRef1());
+ pIAOHandle->Set2ndPos(DragStat().GetRef2());
+
+ if(pIAOHandle->GetColorHdl1())
+ pIAOHandle->GetColorHdl1()->SetPos(DragStat().GetRef1());
+
+ if(pIAOHandle->GetColorHdl2())
+ pIAOHandle->GetColorHdl2()->SetPos(DragStat().GetRef2());
+
+ // new state
+ pIAOHandle->FromIAOToItem(getSdrDragView().GetMarkedObjectList().GetMark(0)->GetMarkedSdrObj(), true, false);
+}
+
+PointerStyle SdrDragGradient::GetSdrDragPointer() const
+{
+ return PointerStyle::RefHand;
+}
+
+
+SdrDragCrook::SdrDragCrook(SdrDragView& rNewView)
+: SdrDragMethod(rNewView),
+ aFact(1,1),
+ bContortionAllowed(false),
+ bNoContortionAllowed(false),
+ bContortion(false),
+ bResizeAllowed(false),
+ bResize(false),
+ bRotateAllowed(false),
+ bRotate(false),
+ bVertical(false),
+ bValid(false),
+ bLft(false),
+ bRgt(false),
+ bUpr(false),
+ bLwr(false),
+ bAtCenter(false),
+ nAngle(0),
+ nMarkSize(0),
+ eMode(SdrCrookMode::Rotate)
+{
+}
+
+OUString SdrDragCrook::GetSdrDragComment() const
+{
+ OUString aStr = ImpGetDescriptionStr(!bContortion ? STR_DragMethCrook : STR_DragMethCrookContortion);
+
+ if(bValid)
+ {
+ aStr += " (";
+
+ sal_Int32 nVal(nAngle);
+
+ if(bAtCenter)
+ nVal *= 2;
+
+ nVal = std::abs(nVal);
+ aStr += SdrModel::GetAngleString(Degree100(nVal)) + ")";
+ }
+
+ if(getSdrDragView().IsDragWithCopy())
+ aStr += SvxResId(STR_EditWithCopy);
+ return aStr;
+}
+
+// These defines parametrize the created raster
+// for interactions
+#define DRAG_CROOK_RASTER_MINIMUM (4)
+#define DRAG_CROOK_RASTER_MAXIMUM (15)
+#define DRAG_CROOK_RASTER_DISTANCE (30)
+
+static basegfx::B2DPolyPolygon impCreateDragRaster(SdrPageView const & rPageView, const tools::Rectangle& rMarkRect)
+{
+ basegfx::B2DPolyPolygon aRetval;
+
+ if(rPageView.PageWindowCount())
+ {
+ OutputDevice& rOut = rPageView.GetPageWindow(0)->GetPaintWindow().GetOutputDevice();
+ tools::Rectangle aPixelSize = rOut.LogicToPixel(rMarkRect);
+ sal_uInt32 nHorDiv(aPixelSize.GetWidth() / DRAG_CROOK_RASTER_DISTANCE);
+ sal_uInt32 nVerDiv(aPixelSize.GetHeight() / DRAG_CROOK_RASTER_DISTANCE);
+
+ if(nHorDiv > DRAG_CROOK_RASTER_MAXIMUM)
+ nHorDiv = DRAG_CROOK_RASTER_MAXIMUM;
+ if(nHorDiv < DRAG_CROOK_RASTER_MINIMUM)
+ nHorDiv = DRAG_CROOK_RASTER_MINIMUM;
+
+ if(nVerDiv > DRAG_CROOK_RASTER_MAXIMUM)
+ nVerDiv = DRAG_CROOK_RASTER_MAXIMUM;
+ if(nVerDiv < DRAG_CROOK_RASTER_MINIMUM)
+ nVerDiv = DRAG_CROOK_RASTER_MINIMUM;
+
+ const double fXLen(rMarkRect.GetWidth() / static_cast<double>(nHorDiv));
+ const double fYLen(rMarkRect.GetHeight() / static_cast<double>(nVerDiv));
+ double fYPos(rMarkRect.Top());
+ sal_uInt32 a, b;
+
+ for(a = 0; a <= nVerDiv; a++)
+ {
+ // horizontal lines
+ for(b = 0; b < nHorDiv; b++)
+ {
+ basegfx::B2DPolygon aHorLineSegment;
+
+ const double fNewX(rMarkRect.Left() + (b * fXLen));
+ aHorLineSegment.append(basegfx::B2DPoint(fNewX, fYPos));
+ aHorLineSegment.appendBezierSegment(
+ basegfx::B2DPoint(fNewX + (fXLen * (1.0 / 3.0)), fYPos),
+ basegfx::B2DPoint(fNewX + (fXLen * (2.0 / 3.0)), fYPos),
+ basegfx::B2DPoint(fNewX + fXLen, fYPos));
+ aRetval.append(aHorLineSegment);
+ }
+
+ // increments
+ fYPos += fYLen;
+ }
+
+ double fXPos(rMarkRect.Left());
+
+ for(a = 0; a <= nHorDiv; a++)
+ {
+ // vertical lines
+ for(b = 0; b < nVerDiv; b++)
+ {
+ basegfx::B2DPolygon aVerLineSegment;
+
+ const double fNewY(rMarkRect.Top() + (b * fYLen));
+ aVerLineSegment.append(basegfx::B2DPoint(fXPos, fNewY));
+ aVerLineSegment.appendBezierSegment(
+ basegfx::B2DPoint(fXPos, fNewY + (fYLen * (1.0 / 3.0))),
+ basegfx::B2DPoint(fXPos, fNewY + (fYLen * (2.0 / 3.0))),
+ basegfx::B2DPoint(fXPos, fNewY + fYLen));
+ aRetval.append(aVerLineSegment);
+ }
+
+ // increments
+ fXPos += fXLen;
+ }
+ }
+
+ return aRetval;
+}
+
+void SdrDragCrook::createSdrDragEntries()
+{
+ // Add extended frame raster first, so it will be behind objects
+ if(getSdrDragView().GetSdrPageView())
+ {
+ const basegfx::B2DPolyPolygon aDragRaster(impCreateDragRaster(*getSdrDragView().GetSdrPageView(), GetMarkedRect()));
+
+ if(aDragRaster.count())
+ {
+ addSdrDragEntry(std::unique_ptr<SdrDragEntry>(new SdrDragEntryPolyPolygon(aDragRaster)));
+ }
+ }
+
+ // call parent
+ SdrDragMethod::createSdrDragEntries();
+}
+
+bool SdrDragCrook::BeginSdrDrag()
+{
+ bContortionAllowed=getSdrDragView().IsCrookAllowed();
+ bNoContortionAllowed=getSdrDragView().IsCrookAllowed(true);
+ bResizeAllowed=getSdrDragView().IsResizeAllowed();
+ bRotateAllowed=getSdrDragView().IsRotateAllowed();
+
+ if (bContortionAllowed || bNoContortionAllowed)
+ {
+ bVertical=(GetDragHdlKind()==SdrHdlKind::Lower || GetDragHdlKind()==SdrHdlKind::Upper);
+ aMarkRect=GetMarkedRect();
+ aMarkCenter=aMarkRect.Center();
+ nMarkSize=bVertical ? (aMarkRect.GetHeight()-1) : (aMarkRect.GetWidth()-1);
+ aCenter=aMarkCenter;
+ aStart=DragStat().GetStart();
+ Show();
+ return true;
+ }
+ else
+ {
+ return false;
+ }
+}
+
+void SdrDragCrook::MovAllPoints(basegfx::B2DPolyPolygon& rTarget)
+{
+ SdrPageView* pPV = getSdrDragView().GetSdrPageView();
+
+ if(!pPV)
+ return;
+
+ XPolyPolygon aTempPolyPoly(rTarget);
+
+ if (pPV->HasMarkedObjPageView())
+ {
+ sal_uInt16 nPolyCount=aTempPolyPoly.Count();
+
+ if (!bContortion && !getSdrDragView().IsNoDragXorPolys())
+ {
+ sal_uInt16 n1st=0,nLast=0;
+ Point aC(aCenter);
+
+ while (n1st<nPolyCount)
+ {
+ nLast=n1st;
+ while (nLast<nPolyCount && aTempPolyPoly[nLast].GetPointCount()!=0) nLast++;
+ tools::Rectangle aBound(aTempPolyPoly[n1st].GetBoundRect());
+ sal_uInt16 i;
+
+ for (i=n1st+1; i<nLast; i++)
+ {
+ aBound.Union(aTempPolyPoly[n1st].GetBoundRect());
+ }
+
+ Point aCtr0(aBound.Center());
+ Point aCtr1(aCtr0);
+
+ if (bResize)
+ {
+ Fraction aFact1(1,1);
+
+ if (bVertical)
+ {
+ ResizePoint(aCtr1,aC,aFact1,aFact);
+ }
+ else
+ {
+ ResizePoint(aCtr1,aC,aFact,aFact1);
+ }
+ }
+
+ bool bRotOk=false;
+ double nSin=0,nCos=0;
+
+ if (aRad.X()!=0 && aRad.Y()!=0)
+ {
+ bRotOk=bRotate;
+
+ switch (eMode)
+ {
+ case SdrCrookMode::Rotate : CrookRotateXPoint (aCtr1,nullptr,nullptr,aC,aRad,nSin,nCos,bVertical); break;
+ case SdrCrookMode::Slant : CrookSlantXPoint (aCtr1,nullptr,nullptr,aC,aRad,nSin,nCos,bVertical); break;
+ case SdrCrookMode::Stretch: CrookStretchXPoint(aCtr1,nullptr,nullptr,aC,aRad,nSin,nCos,bVertical,aMarkRect); break;
+ } // switch
+ }
+
+ aCtr1-=aCtr0;
+
+ for (i=n1st; i<nLast; i++)
+ {
+ if (bRotOk)
+ {
+ RotateXPoly(aTempPolyPoly[i],aCtr0,nSin,nCos);
+ }
+
+ aTempPolyPoly[i].Move(aCtr1.X(),aCtr1.Y());
+ }
+
+ n1st=nLast+1;
+ }
+ }
+ else
+ {
+ sal_uInt16 i,j;
+
+ for (j=0; j<nPolyCount; j++)
+ {
+ XPolygon& aPol=aTempPolyPoly[j];
+ sal_uInt16 nPointCount=aPol.GetPointCount();
+ i=0;
+
+ while (i<nPointCount)
+ {
+ Point* pPnt=&aPol[i];
+ Point* pC1=nullptr;
+ Point* pC2=nullptr;
+
+ if (i+1<nPointCount && aPol.IsControl(i))
+ { // control point on the left
+ pC1=pPnt;
+ i++;
+ pPnt=&aPol[i];
+ }
+
+ i++;
+
+ if (i<nPointCount && aPol.IsControl(i))
+ { // control point on the right
+ pC2=&aPol[i];
+ i++;
+ }
+
+ MovCrookPoint(*pPnt,pC1,pC2);
+ }
+ }
+ }
+ }
+
+ rTarget = aTempPolyPoly.getB2DPolyPolygon();
+}
+
+void SdrDragCrook::MovCrookPoint(Point& rPnt, Point* pC1, Point* pC2)
+{
+ bool bVert=bVertical;
+ bool bC1=pC1!=nullptr;
+ bool bC2=pC2!=nullptr;
+ Point aC(aCenter);
+
+ if (bResize)
+ {
+ Fraction aFact1(1,1);
+
+ if (bVert)
+ {
+ ResizePoint(rPnt,aC,aFact1,aFact);
+
+ if (bC1)
+ ResizePoint(*pC1,aC,aFact1,aFact);
+
+ if (bC2)
+ ResizePoint(*pC2,aC,aFact1,aFact);
+ }
+ else
+ {
+ ResizePoint(rPnt,aC,aFact,aFact1);
+
+ if (bC1)
+ ResizePoint(*pC1,aC,aFact,aFact1);
+
+ if (bC2)
+ ResizePoint(*pC2,aC,aFact,aFact1);
+ }
+ }
+
+ if (aRad.X()!=0 && aRad.Y()!=0)
+ {
+ double nSin,nCos;
+
+ switch (eMode)
+ {
+ case SdrCrookMode::Rotate : CrookRotateXPoint (rPnt,pC1,pC2,aC,aRad,nSin,nCos,bVert); break;
+ case SdrCrookMode::Slant : CrookSlantXPoint (rPnt,pC1,pC2,aC,aRad,nSin,nCos,bVert); break;
+ case SdrCrookMode::Stretch: CrookStretchXPoint(rPnt,pC1,pC2,aC,aRad,nSin,nCos,bVert,aMarkRect); break;
+ } // switch
+ }
+}
+
+void SdrDragCrook::MoveSdrDrag(const Point& rPnt)
+{
+ if (!DragStat().CheckMinMoved(rPnt))
+ return;
+
+ bool bNewMoveOnly=getSdrDragView().IsMoveOnlyDragging();
+ bAtCenter=false;
+ SdrCrookMode eNewMode=getSdrDragView().GetCrookMode();
+ bool bNewContortion=!bNewMoveOnly && ((bContortionAllowed && !getSdrDragView().IsCrookNoContortion()) || !bNoContortionAllowed);
+ bResize=!getSdrDragView().IsOrtho() && bResizeAllowed && !bNewMoveOnly;
+ bool bNewRotate=bRotateAllowed && !bNewContortion && !bNewMoveOnly && eNewMode==SdrCrookMode::Rotate;
+
+ Point aPnt(GetSnapPos(rPnt));
+
+ Point aNewCenter(aMarkCenter.X(),aStart.Y());
+
+ if (bVertical)
+ {
+ aNewCenter.setX(aStart.X() );
+ aNewCenter.setY(aMarkCenter.Y() );
+ }
+
+ if (!getSdrDragView().IsCrookAtCenter())
+ {
+ switch (GetDragHdlKind())
+ {
+ case SdrHdlKind::UpperLeft: aNewCenter.setX(aMarkRect.Right() ); bLft=true; break;
+ case SdrHdlKind::Upper: aNewCenter.setY(aMarkRect.Bottom() ); bUpr=true; break;
+ case SdrHdlKind::UpperRight: aNewCenter.setX(aMarkRect.Left() ); bRgt=true; break;
+ case SdrHdlKind::Left : aNewCenter.setX(aMarkRect.Right() ); bLft=true; break;
+ case SdrHdlKind::Right: aNewCenter.setX(aMarkRect.Left() ); bRgt=true; break;
+ case SdrHdlKind::LowerLeft: aNewCenter.setX(aMarkRect.Right() ); bLft=true; break;
+ case SdrHdlKind::Lower: aNewCenter.setY(aMarkRect.Top() ); bLwr=true; break;
+ case SdrHdlKind::LowerRight: aNewCenter.setX(aMarkRect.Left() ); bRgt=true; break;
+ default: bAtCenter=true;
+ }
+ }
+ else
+ bAtCenter=true;
+
+ Fraction aNewFract(1,1);
+ tools::Long dx1=aPnt.X()-aNewCenter.X();
+ tools::Long dy1=aPnt.Y()-aNewCenter.Y();
+ bValid=bVertical ? dx1!=0 : dy1!=0;
+
+ if (bValid)
+ {
+ if (bVertical)
+ bValid = std::abs(dx1)*100>std::abs(dy1);
+ else
+ bValid = std::abs(dy1)*100>std::abs(dx1);
+ }
+
+ tools::Long nNewRad=0;
+ nAngle=0_deg100;
+
+ if (bValid)
+ {
+ double a=0; // slope of the radius
+ Degree100 nPntAngle(0);
+
+ if (bVertical)
+ {
+ a=static_cast<double>(dy1)/static_cast<double>(dx1); // slope of the radius
+ nNewRad=(static_cast<tools::Long>(dy1*a)+dx1) /2;
+ aNewCenter.AdjustX(nNewRad );
+ nPntAngle=GetAngle(aPnt-aNewCenter);
+ }
+ else
+ {
+ a=static_cast<double>(dx1)/static_cast<double>(dy1); // slope of the radius
+ nNewRad=(static_cast<tools::Long>(dx1*a)+dy1) /2;
+ aNewCenter.AdjustY(nNewRad );
+ nPntAngle=GetAngle(aPnt-aNewCenter)-9000_deg100;
+ }
+
+ if (!bAtCenter)
+ {
+ if (nNewRad<0)
+ {
+ if (bRgt) nPntAngle += 18000_deg100;
+ if (bLft) nPntAngle = 18000_deg100 - nPntAngle;
+ if (bLwr) nPntAngle =- nPntAngle;
+ }
+ else
+ {
+ if (bRgt) nPntAngle = -nPntAngle;
+ if (bUpr) nPntAngle = 18000_deg100 - nPntAngle;
+ if (bLwr) nPntAngle += 18000_deg100;
+ }
+
+ nPntAngle=NormAngle36000(nPntAngle);
+ }
+ else
+ {
+ if (nNewRad<0) nPntAngle += 18000_deg100;
+ if (bVertical) nPntAngle = 18000_deg100 - nPntAngle;
+ nPntAngle = NormAngle18000(nPntAngle);
+ nPntAngle = abs(nPntAngle);
+ }
+
+ double nCircumference = 2 * std::abs(nNewRad) * M_PI;
+
+ if (bResize)
+ {
+ tools::Long nMul=static_cast<tools::Long>(nCircumference * NormAngle36000(nPntAngle).get() / 36000.0);
+
+ if (bAtCenter)
+ nMul*=2;
+
+ aNewFract=Fraction(nMul,nMarkSize);
+ nAngle=nPntAngle;
+ }
+ else
+ {
+ nAngle = Degree100(static_cast<tools::Long>((nMarkSize*360/nCircumference)*100)/2);
+
+ if (nAngle==0_deg100)
+ bValid=false;
+ }
+ }
+
+ if (nAngle==0_deg100 || nNewRad==0)
+ bValid=false;
+
+ if (!bValid)
+ nNewRad=0;
+
+ if (!bValid && bResize)
+ {
+ tools::Long nMul=bVertical ? dy1 : dx1;
+
+ if (bLft || bUpr)
+ nMul=-nMul;
+
+ tools::Long nDiv=nMarkSize;
+
+ if (bAtCenter)
+ {
+ nMul*=2;
+ nMul = std::abs(nMul);
+ }
+
+ aNewFract=Fraction(nMul,nDiv);
+ }
+
+ if (aNewCenter==aCenter && bNewContortion==bContortion && aNewFract==aFact &&
+ bNewMoveOnly == getMoveOnly() && bNewRotate==bRotate && eNewMode==eMode)
+ return;
+
+ Hide();
+ setMoveOnly(bNewMoveOnly);
+ bRotate=bNewRotate;
+ eMode=eNewMode;
+ bContortion=bNewContortion;
+ aCenter=aNewCenter;
+ aFact=aNewFract;
+ aRad=Point(nNewRad,nNewRad);
+ bResize=aFact!=Fraction(1,1) && aFact.GetDenominator()!=0 && aFact.IsValid();
+ DragStat().NextMove(aPnt);
+ Show();
+}
+
+void SdrDragCrook::applyCurrentTransformationToSdrObject(SdrObject& rTarget)
+{
+ const bool bDoResize(aFact!=Fraction(1,1));
+ const bool bDoCrook(aCenter!=aMarkCenter && aRad.X()!=0 && aRad.Y()!=0);
+
+ if (!(bDoCrook || bDoResize))
+ return;
+
+ if (bDoResize)
+ {
+ Fraction aFact1(1,1);
+
+ if (bContortion)
+ {
+ if (bVertical)
+ {
+ rTarget.Resize(aCenter,aFact1,aFact);
+ }
+ else
+ {
+ rTarget.Resize(aCenter,aFact,aFact1);
+ }
+ }
+ else
+ {
+ Point aCtr0(rTarget.GetSnapRect().Center());
+ Point aCtr1(aCtr0);
+
+ if (bVertical)
+ {
+ ResizePoint(aCtr1,aCenter,aFact1,aFact);
+ }
+ else
+ {
+ ResizePoint(aCtr1,aCenter,aFact,aFact1);
+ }
+
+ Size aSiz(aCtr1.X()-aCtr0.X(),aCtr1.Y()-aCtr0.Y());
+
+ rTarget.Move(aSiz);
+ }
+ }
+
+ if (bDoCrook)
+ {
+ const tools::Rectangle aLocalMarkRect(getSdrDragView().GetMarkedObjRect());
+ const bool bLocalRotate(!bContortion && eMode == SdrCrookMode::Rotate && getSdrDragView().IsRotateAllowed());
+
+ SdrEditView::ImpCrookObj(&rTarget,aCenter,aRad,eMode,bVertical,!bContortion,bLocalRotate,aLocalMarkRect);
+ }
+}
+
+void SdrDragCrook::applyCurrentTransformationToPolyPolygon(basegfx::B2DPolyPolygon& rTarget)
+{
+ // use helper derived from old stuff
+ MovAllPoints(rTarget);
+}
+
+bool SdrDragCrook::EndSdrDrag(bool bCopy)
+{
+ Hide();
+
+ if (bResize && aFact==Fraction(1,1))
+ bResize=false;
+
+ const bool bUndo = getSdrDragView().IsUndoEnabled();
+
+ bool bDoCrook=aCenter!=aMarkCenter && aRad.X()!=0 && aRad.Y()!=0;
+
+ if (bDoCrook || bResize)
+ {
+ if (bResize && bUndo)
+ {
+ OUString aStr = ImpGetDescriptionStr(!bContortion?STR_EditCrook:STR_EditCrookContortion);
+
+ if (bCopy)
+ aStr += SvxResId(STR_EditWithCopy);
+
+ getSdrDragView().BegUndo(aStr);
+ }
+
+ if (bResize)
+ {
+ Fraction aFact1(1,1);
+
+ if (bContortion)
+ {
+ if (bVertical)
+ getSdrDragView().ResizeMarkedObj(aCenter,aFact1,aFact,bCopy);
+ else
+ getSdrDragView().ResizeMarkedObj(aCenter,aFact,aFact1,bCopy);
+ }
+ else
+ {
+ if (bCopy)
+ getSdrDragView().CopyMarkedObj();
+
+ const size_t nMarkCount=getSdrDragView().GetMarkedObjectList().GetMarkCount();
+
+ for (size_t nm=0; nm<nMarkCount; ++nm)
+ {
+ SdrMark* pM=getSdrDragView().GetMarkedObjectList().GetMark(nm);
+ SdrObject* pO=pM->GetMarkedSdrObj();
+ Point aCtr0(pO->GetSnapRect().Center());
+ Point aCtr1(aCtr0);
+
+ if (bVertical)
+ ResizePoint(aCtr1,aCenter,aFact1,aFact);
+ else
+ ResizePoint(aCtr1,aCenter,aFact,aFact1);
+
+ Size aSiz(aCtr1.X()-aCtr0.X(),aCtr1.Y()-aCtr0.Y());
+ if( bUndo )
+ AddUndo(getSdrDragView().GetModel()->GetSdrUndoFactory().CreateUndoMoveObject(*pO,aSiz));
+ pO->Move(aSiz);
+ }
+ }
+
+ bCopy=false;
+ }
+
+ if (bDoCrook)
+ {
+ getSdrDragView().CrookMarkedObj(aCenter,aRad,eMode,bVertical,!bContortion,bCopy);
+ }
+
+ if (bResize && bUndo)
+ getSdrDragView().EndUndo();
+
+ return true;
+ }
+
+ return false;
+}
+
+PointerStyle SdrDragCrook::GetSdrDragPointer() const
+{
+ return PointerStyle::Crook;
+}
+
+
+SdrDragDistort::SdrDragDistort(SdrDragView& rNewView)
+: SdrDragMethod(rNewView),
+ nPolyPt(0),
+ bContortionAllowed(false),
+ bNoContortionAllowed(false),
+ bContortion(false)
+{
+}
+
+OUString SdrDragDistort::GetSdrDragComment() const
+{
+ OUString aStr = ImpGetDescriptionStr(STR_DragMethDistort)
+ + " (x="
+ + getSdrDragView().GetModel()->GetMetricString(DragStat().GetDX())
+ + " y="
+ + getSdrDragView().GetModel()->GetMetricString(DragStat().GetDY())
+ + ")";
+
+ if(getSdrDragView().IsDragWithCopy())
+ aStr += SvxResId(STR_EditWithCopy);
+ return aStr;
+}
+
+void SdrDragDistort::createSdrDragEntries()
+{
+ // Add extended frame raster first, so it will be behind objects
+ if(getSdrDragView().GetSdrPageView())
+ {
+ const basegfx::B2DPolyPolygon aDragRaster(impCreateDragRaster(*getSdrDragView().GetSdrPageView(), GetMarkedRect()));
+
+ if(aDragRaster.count())
+ {
+ addSdrDragEntry(std::unique_ptr<SdrDragEntry>(new SdrDragEntryPolyPolygon(aDragRaster)));
+ }
+ }
+
+ // call parent
+ SdrDragMethod::createSdrDragEntries();
+}
+
+bool SdrDragDistort::BeginSdrDrag()
+{
+ bContortionAllowed=getSdrDragView().IsDistortAllowed();
+ bNoContortionAllowed=getSdrDragView().IsDistortAllowed(true);
+
+ if (bContortionAllowed || bNoContortionAllowed)
+ {
+ SdrHdlKind eKind=GetDragHdlKind();
+ nPolyPt=0xFFFF;
+
+ if (eKind==SdrHdlKind::UpperLeft) nPolyPt=0;
+ if (eKind==SdrHdlKind::UpperRight) nPolyPt=1;
+ if (eKind==SdrHdlKind::LowerRight) nPolyPt=2;
+ if (eKind==SdrHdlKind::LowerLeft) nPolyPt=3;
+ if (nPolyPt>3) return false;
+
+ aMarkRect=GetMarkedRect();
+ aDistortedRect=XPolygon(aMarkRect);
+ Show();
+ return true;
+ }
+ else
+ {
+ return false;
+ }
+}
+
+void SdrDragDistort::MovAllPoints(basegfx::B2DPolyPolygon& rTarget)
+{
+ if (!bContortion)
+ return;
+
+ SdrPageView* pPV = getSdrDragView().GetSdrPageView();
+
+ if(pPV && pPV->HasMarkedObjPageView())
+ {
+ basegfx::B2DPolyPolygon aDragPolygon(rTarget);
+ const basegfx::B2DRange aOriginalRange = vcl::unotools::b2DRectangleFromRectangle(aMarkRect);
+ const basegfx::B2DPoint aTopLeft(aDistortedRect[0].X(), aDistortedRect[0].Y());
+ const basegfx::B2DPoint aTopRight(aDistortedRect[1].X(), aDistortedRect[1].Y());
+ const basegfx::B2DPoint aBottomLeft(aDistortedRect[3].X(), aDistortedRect[3].Y());
+ const basegfx::B2DPoint aBottomRight(aDistortedRect[2].X(), aDistortedRect[2].Y());
+
+ aDragPolygon = basegfx::utils::distort(aDragPolygon, aOriginalRange, aTopLeft, aTopRight, aBottomLeft, aBottomRight);
+ rTarget = aDragPolygon;
+ }
+}
+
+void SdrDragDistort::MoveSdrDrag(const Point& rPnt)
+{
+ if (!DragStat().CheckMinMoved(rPnt))
+ return;
+
+ Point aPnt(GetSnapPos(rPnt));
+
+ if (getSdrDragView().IsOrtho())
+ OrthoDistance8(DragStat().GetStart(),aPnt,getSdrDragView().IsBigOrtho());
+
+ bool bNewContortion=(bContortionAllowed && !getSdrDragView().IsCrookNoContortion()) || !bNoContortionAllowed;
+
+ if (bNewContortion!=bContortion || aDistortedRect[nPolyPt]!=aPnt)
+ {
+ Hide();
+ aDistortedRect[nPolyPt]=aPnt;
+ bContortion=bNewContortion;
+ DragStat().NextMove(aPnt);
+ Show();
+ }
+}
+
+bool SdrDragDistort::EndSdrDrag(bool bCopy)
+{
+ Hide();
+ bool bDoDistort=DragStat().GetDX()!=0 || DragStat().GetDY()!=0;
+
+ if (bDoDistort)
+ {
+ getSdrDragView().DistortMarkedObj(aMarkRect,aDistortedRect,!bContortion,bCopy);
+ return true;
+ }
+
+ return false;
+}
+
+PointerStyle SdrDragDistort::GetSdrDragPointer() const
+{
+ return PointerStyle::RefHand;
+}
+
+void SdrDragDistort::applyCurrentTransformationToSdrObject(SdrObject& rTarget)
+{
+ const bool bDoDistort(DragStat().GetDX()!=0 || DragStat().GetDY()!=0);
+
+ if (bDoDistort)
+ {
+ SdrEditView::ImpDistortObj(&rTarget, aMarkRect, aDistortedRect, !bContortion);
+ }
+}
+
+void SdrDragDistort::applyCurrentTransformationToPolyPolygon(basegfx::B2DPolyPolygon& rTarget)
+{
+ // use helper derived from old stuff
+ MovAllPoints(rTarget);
+}
+
+
+SdrDragCrop::SdrDragCrop(SdrDragView& rNewView)
+: SdrDragObjOwn(rNewView)
+{
+ // switch off solid dragging for crop; it just makes no sense since showing
+ // a 50% transparent object above the original will not be visible
+ setSolidDraggingActive(false);
+}
+
+OUString SdrDragCrop::GetSdrDragComment() const
+{
+ OUString aStr = ImpGetDescriptionStr(STR_DragMethCrop)
+ + " (x="
+ + getSdrDragView().GetModel()->GetMetricString(DragStat().GetDX())
+ + " y="
+ + getSdrDragView().GetModel()->GetMetricString(DragStat().GetDY())
+ + ")";
+
+ if(getSdrDragView().IsDragWithCopy())
+ aStr += SvxResId(STR_EditWithCopy);
+ return aStr;
+}
+
+bool SdrDragCrop::BeginSdrDrag()
+{
+ // call parent
+ bool bRetval(SdrDragObjOwn::BeginSdrDrag());
+
+ if(!GetDragHdl())
+ {
+ // we need the DragHdl, break if not there
+ bRetval = false;
+ }
+
+ return bRetval;
+}
+
+bool SdrDragCrop::EndSdrDrag(bool /*bCopy*/)
+{
+ Hide();
+
+ if(0 == DragStat().GetDX() && 0 == DragStat().GetDY())
+ {
+ // no change, done
+ return false;
+ }
+
+ const SdrMarkList& rMarkList = getSdrDragView().GetMarkedObjectList();
+
+ if(1 != rMarkList.GetMarkCount())
+ {
+ // Crop only with single Object selected
+ return false;
+ }
+
+ // prepare for SdrGrafObj or others. This code has to work with usual
+ // SdrGrafObj's from Draw/Impress/Calc, but also with SdrObjects from
+ // Writer. It would be better to handle this in Writer directly, but
+ // there are currently no easy mechanisms to plug an alternative interaction
+ // from there
+ SdrObject* pSdrObject = rMarkList.GetMark(0)->GetMarkedSdrObj();
+ SdrObjectUniquePtr pFullDragClone;
+ bool bExternal(false);
+ SdrObject* pExternalSdrObject(nullptr);
+
+ // RotGrfFlyFrame: Crop decision for DrawingLayer/Writer now
+ // locally, no two-in-one methods any more
+ if (nullptr != pSdrObject && dynamic_cast< const SdrGrafObj* >(pSdrObject) == nullptr)
+ {
+ // If Writer, get the already offered for interaction SdrGrafObj
+ // and set up for using that replacement object that contains the
+ // real transformation. That SdrObject is owned and has to be deleted,
+ // so use a std::unique_ptr with special handling for the protected
+ // SDrObject destructor
+ pFullDragClone = pSdrObject->getFullDragClone();
+
+ if(dynamic_cast< SdrGrafObj* >(pFullDragClone.get()))
+ {
+ bExternal = true;
+ pExternalSdrObject = pSdrObject;
+ pSdrObject = pFullDragClone.get();
+ }
+ }
+
+ // get and check for SdrGrafObj now
+ SdrGrafObj* pObj = dynamic_cast<SdrGrafObj*>( pSdrObject );
+
+ if(!pObj)
+ {
+ return false;
+ }
+
+ // no undo for external needed, done there
+ const bool bUndo(!bExternal && getSdrDragView().IsUndoEnabled());
+
+ if(bUndo)
+ {
+ OUString aUndoStr = ImpGetDescriptionStr(STR_DragMethCrop);
+
+ getSdrDragView().BegUndo( aUndoStr );
+ getSdrDragView().AddUndo( getSdrDragView().GetModel()->GetSdrUndoFactory().CreateUndoGeoObject(*pObj));
+ // also need attr undo, the SdrGrafCropItem will be changed
+ getSdrDragView().AddUndo( getSdrDragView().GetModel()->GetSdrUndoFactory().CreateUndoAttrObject(*pObj));
+ }
+
+ // get the original objects transformation
+ basegfx::B2DHomMatrix aOriginalMatrix;
+ basegfx::B2DPolyPolygon aPolyPolygon;
+ bool bShearCorrected(false);
+ pObj->TRGetBaseGeometry(aOriginalMatrix, aPolyPolygon);
+
+ { // correct shear, it comes currently mirrored from TRGetBaseGeometry, can be removed with aw080
+ const basegfx::utils::B2DHomMatrixBufferedDecompose aTmpDecomp(aOriginalMatrix);
+
+ if(!basegfx::fTools::equalZero(aTmpDecomp.getShearX()))
+ {
+ bShearCorrected = true;
+ aOriginalMatrix = basegfx::utils::createScaleShearXRotateTranslateB2DHomMatrix(
+ aTmpDecomp.getScale(),
+ -aTmpDecomp.getShearX(),
+ aTmpDecomp.getRotate(),
+ aTmpDecomp.getTranslate());
+ }
+ }
+
+ // generate start point of original drag vector in unit coordinates (the
+ // vis-a-vis of the drag point)
+ basegfx::B2DPoint aLocalStart(0.0, 0.0);
+ bool bOnAxis(false);
+
+ switch(GetDragHdlKind())
+ {
+ case SdrHdlKind::UpperLeft: aLocalStart.setX(1.0); aLocalStart.setY(1.0); break;
+ case SdrHdlKind::Upper: aLocalStart.setX(0.5); aLocalStart.setY(1.0); bOnAxis = true; break;
+ case SdrHdlKind::UpperRight: aLocalStart.setX(0.0); aLocalStart.setY(1.0); break;
+ case SdrHdlKind::Left : aLocalStart.setX(1.0); aLocalStart.setY(0.5); bOnAxis = true; break;
+ case SdrHdlKind::Right: aLocalStart.setX(0.0); aLocalStart.setY(0.5); bOnAxis = true; break;
+ case SdrHdlKind::LowerLeft: aLocalStart.setX(1.0); aLocalStart.setY(0.0); break;
+ case SdrHdlKind::Lower: aLocalStart.setX(0.5); aLocalStart.setY(0.0); bOnAxis = true; break;
+ case SdrHdlKind::LowerRight: aLocalStart.setX(0.0); aLocalStart.setY(0.0); break;
+ default: break;
+ }
+
+ // create the current drag position in unit coordinates. To get there,
+ // transform back the DragPoint to UnitCoordinates
+ basegfx::B2DHomMatrix aInverse(aOriginalMatrix);
+ aInverse.invert();
+ basegfx::B2DPoint aLocalCurrent(aInverse * basegfx::B2DPoint(DragStat().GetNow().X(), DragStat().GetNow().Y()));
+
+ // if one of the edge handles is used, limit to X or Y drag only
+ if(bOnAxis)
+ {
+ if(basegfx::fTools::equal(aLocalStart.getX(), 0.5))
+ {
+ aLocalCurrent.setX(aLocalStart.getX());
+ }
+ else
+ {
+ aLocalCurrent.setY(aLocalStart.getY());
+ }
+ }
+
+ // create internal change in unit coordinates
+ basegfx::B2DHomMatrix aDiscreteChangeMatrix;
+
+ if(!basegfx::fTools::equal(aLocalCurrent.getX(), aLocalStart.getX()))
+ {
+ if(aLocalStart.getX() < 0.5)
+ {
+ aDiscreteChangeMatrix.scale(aLocalCurrent.getX(), 1.0);
+ }
+ else
+ {
+ aDiscreteChangeMatrix.scale(1.0 - aLocalCurrent.getX(), 1.0);
+ aDiscreteChangeMatrix.translate(aLocalCurrent.getX(), 0.0);
+ }
+ }
+
+ if(!basegfx::fTools::equal(aLocalCurrent.getY(), aLocalStart.getY()))
+ {
+ if(aLocalStart.getY() < 0.5)
+ {
+ aDiscreteChangeMatrix.scale(1.0, aLocalCurrent.getY());
+ }
+ else
+ {
+ aDiscreteChangeMatrix.scale(1.0, 1.0 - aLocalCurrent.getY());
+ aDiscreteChangeMatrix.translate(0.0, aLocalCurrent.getY());
+ }
+ }
+
+ // We now have the whole executed Crop in UnitCoordinates in
+ // aDiscreteChangeMatrix, go to concrete sizes now.
+ // Create the unrotated original rectangle and the unrotated modified
+ // rectangle as Ranges
+ const basegfx::utils::B2DHomMatrixBufferedDecompose aOriginalMatrixDecomp(aOriginalMatrix);
+
+ // prepare unsheared/unrotated versions of the old and new transformation
+ const basegfx::B2DHomMatrix aOriginalMatrixNoShearNoRotate(
+ basegfx::utils::createScaleTranslateB2DHomMatrix(
+ basegfx::absolute(aOriginalMatrixDecomp.getScale()),
+ aOriginalMatrixDecomp.getTranslate()));
+
+ // create the ranges for these
+ basegfx::B2DRange aRangeOriginalNoShearNoRotate(0.0, 0.0, 1.0, 1.0);
+ basegfx::B2DRange aRangeNewNoShearNoRotate(0.0, 0.0, 1.0, 1.0);
+ aRangeOriginalNoShearNoRotate.transform(aOriginalMatrixNoShearNoRotate);
+ aRangeNewNoShearNoRotate.transform(aOriginalMatrixNoShearNoRotate * aDiscreteChangeMatrix);
+
+ if(bExternal)
+ {
+ // With aLocalStart point (opposed to dragged point), X scale and Y scale,
+ // we call crop (virtual method) on pSdrObject which calls VirtFlyDrawObj
+ // crop. Use aLocalStart unchanged, so being relative to the Crop-Action,
+ // the called instance knows best how to use it
+ const double fScaleX(aRangeNewNoShearNoRotate.getWidth() / aRangeOriginalNoShearNoRotate.getWidth());
+ const double fScaleY(aRangeNewNoShearNoRotate.getHeight() / aRangeOriginalNoShearNoRotate.getHeight());
+
+ pExternalSdrObject->Crop(
+ aLocalStart,
+ fScaleX,
+ fScaleY);
+ }
+ else
+ {
+ // prepare matrix to apply to object; evtl. back-correct shear
+ basegfx::B2DHomMatrix aNewObjectMatrix(aOriginalMatrix * aDiscreteChangeMatrix);
+
+ if(bShearCorrected)
+ {
+ // back-correct shear
+ const basegfx::utils::B2DHomMatrixBufferedDecompose aTmpDecomp(aNewObjectMatrix);
+
+ aNewObjectMatrix = basegfx::utils::createScaleShearXRotateTranslateB2DHomMatrix(
+ aTmpDecomp.getScale(),
+ -aTmpDecomp.getShearX(),
+ aTmpDecomp.getRotate(),
+ aTmpDecomp.getTranslate());
+ }
+
+ // apply change to object by applying the unit coordinate change followed
+ // by the original change
+ pObj->TRSetBaseGeometry(aNewObjectMatrix, aPolyPolygon);
+
+ // extract the old Rectangle structures
+ tools::Rectangle aOldRect(
+ basegfx::fround(aRangeOriginalNoShearNoRotate.getMinX()),
+ basegfx::fround(aRangeOriginalNoShearNoRotate.getMinY()),
+ basegfx::fround(aRangeOriginalNoShearNoRotate.getMaxX()),
+ basegfx::fround(aRangeOriginalNoShearNoRotate.getMaxY()));
+ tools::Rectangle aNewRect(
+ basegfx::fround(aRangeNewNoShearNoRotate.getMinX()),
+ basegfx::fround(aRangeNewNoShearNoRotate.getMinY()),
+ basegfx::fround(aRangeNewNoShearNoRotate.getMaxX()),
+ basegfx::fround(aRangeNewNoShearNoRotate.getMaxY()));
+
+ // continue with the old original stuff
+ if (!aOldRect.GetWidth() || !aOldRect.GetHeight())
+ {
+ throw o3tl::divide_by_zero();
+ }
+
+ if((pObj->GetGraphicType() == GraphicType::NONE) || (pObj->GetGraphicType() == GraphicType::Default))
+ {
+ return false;
+ }
+
+ const GraphicObject& rGraphicObject(pObj->GetGraphicObject());
+ // tdf#117145 Usually Writer will go the bExternal path (see above), but more correct for
+ // the future is to use the MapMode from the SdrModel/SfxItemPool if the Writer's current
+ // special handling should be unified to this path in the future. Usually it *should* be
+ // MapUnit::Map100thMM, but better do not mix up Units.
+ // Checked now what SwVirtFlyDrawObj::NbcCrop is doing - it calculates everything forced
+ // to MapUnit::Map100thMM, but extracts/packs Twips to the used SdrGrafCropItem in Writer.
+ const MapMode aMapModePool(pObj->getSdrModelFromSdrObject().GetItemPool().GetMetric(0));
+ Size aGraphicSize(rGraphicObject.GetPrefSize());
+
+ if(MapUnit::MapPixel == rGraphicObject.GetPrefMapMode().GetMapUnit())
+ {
+ aGraphicSize = Application::GetDefaultDevice()->PixelToLogic(aGraphicSize, aMapModePool);
+ }
+ else
+ {
+ aGraphicSize = OutputDevice::LogicToLogic(aGraphicSize, rGraphicObject.GetPrefMapMode(), aMapModePool);
+ }
+
+ if(0 == aGraphicSize.Width() || 0 == aGraphicSize.Height())
+ {
+ return false;
+ }
+
+ const SdrGrafCropItem& rOldCrop = pObj->GetMergedItem(SDRATTR_GRAFCROP);
+ double fScaleX = ( aGraphicSize.Width() - rOldCrop.GetLeft() - rOldCrop.GetRight() ) / static_cast<double>(aOldRect.GetWidth());
+ double fScaleY = ( aGraphicSize.Height() - rOldCrop.GetTop() - rOldCrop.GetBottom() ) / static_cast<double>(aOldRect.GetHeight());
+
+ 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();
+
+ if(pObj->IsMirrored())
+ {
+ // mirrored X or Y, for old stuff, exchange X
+ // check for aw080
+ sal_Int32 nTmp(nDiffLeft);
+ nDiffLeft = -nDiffRight;
+ nDiffRight = -nTmp;
+ }
+
+ sal_Int32 nLeftCrop = static_cast<sal_Int32>( rOldCrop.GetLeft() + nDiffLeft * fScaleX );
+ sal_Int32 nTopCrop = static_cast<sal_Int32>( rOldCrop.GetTop() + nDiffTop * fScaleY );
+ sal_Int32 nRightCrop = static_cast<sal_Int32>( rOldCrop.GetRight() - nDiffRight * fScaleX );
+ sal_Int32 nBottomCrop = static_cast<sal_Int32>( rOldCrop.GetBottom() - nDiffBottom * fScaleY );
+
+ SfxItemPool& rPool = getSdrDragView().GetModel()->GetItemPool();
+ SfxItemSetFixed<SDRATTR_GRAFCROP, SDRATTR_GRAFCROP> aSet( rPool );
+ aSet.Put( SdrGrafCropItem( nLeftCrop, nTopCrop, nRightCrop, nBottomCrop ) );
+ getSdrDragView().SetAttributes( aSet, false );
+ }
+
+ if(bUndo)
+ {
+ getSdrDragView().EndUndo();
+ }
+
+ return true;
+}
+
+PointerStyle SdrDragCrop::GetSdrDragPointer() const
+{
+ return PointerStyle::Crop;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/svdraw/svddrgv.cxx b/svx/source/svdraw/svddrgv.cxx
new file mode 100644
index 000000000..b6ac05cef
--- /dev/null
+++ b/svx/source/svdraw/svddrgv.cxx
@@ -0,0 +1,919 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * 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 <tools/debug.hxx>
+#include <svx/svddrgv.hxx>
+#include <svx/svdview.hxx>
+#include <svx/svdundo.hxx>
+#include <svx/svdocapt.hxx>
+#include <svx/svdpagv.hxx>
+#include <svx/svdopath.hxx>
+#include <svx/svdoedge.hxx>
+#include <svx/strings.hrc>
+#include <svx/dialmgr.hxx>
+#include "svddrgm1.hxx"
+#include <svx/obj3d.hxx>
+#include <svx/svdoashp.hxx>
+#include <svx/sdrpaintwindow.hxx>
+#include <basegfx/matrix/b2dhommatrix.hxx>
+#include <svtools/optionsdrawinglayer.hxx>
+#include <svx/sdr/overlay/overlaymanager.hxx>
+#include <svx/sdrpagewindow.hxx>
+#include <unotools/configmgr.hxx>
+#include <comphelper/lok.hxx>
+
+using namespace sdr;
+
+SdrDragView::SdrDragView(SdrModel& rSdrModel, OutputDevice* pOut)
+ : SdrExchangeView(rSdrModel, pOut)
+ , mpDragHdl(nullptr)
+ , mpInsPointUndo(nullptr)
+ , meDragHdl(SdrHdlKind::Move)
+ , mbFramDrag(false)
+ , mbMarkedHitMovesAlways(false)
+ , mbDragLimit(false)
+ , mbDragHdl(false)
+ , mbDragStripes(false)
+ , mbSolidDragging(utl::ConfigManager::IsFuzzing() || SvtOptionsDrawinglayer::IsSolidDragCreate())
+ , mbResizeAtCenter(false)
+ , mbCrookAtCenter(false)
+ , mbDragWithCopy(false)
+ , mbInsGluePoint(false)
+ , mbInsObjPointMode(false)
+ , mbInsGluePointMode(false)
+ , mbNoDragXorPolys(false)
+{
+ meDragMode = SdrDragMode::Move;
+}
+
+SdrDragView::~SdrDragView()
+{
+}
+
+bool SdrDragView::IsAction() const
+{
+ return (mpCurrentSdrDragMethod || SdrExchangeView::IsAction());
+}
+
+void SdrDragView::MovAction(const Point& rPnt)
+{
+ SdrExchangeView::MovAction(rPnt);
+ if (mpCurrentSdrDragMethod)
+ {
+ MovDragObj(rPnt);
+ }
+}
+
+void SdrDragView::EndAction()
+{
+ if (mpCurrentSdrDragMethod)
+ {
+ EndDragObj();
+ }
+ SdrExchangeView::EndAction();
+}
+
+void SdrDragView::BckAction()
+{
+ SdrExchangeView::BckAction();
+ BrkDragObj();
+}
+
+void SdrDragView::BrkAction()
+{
+ SdrExchangeView::BrkAction();
+ BrkDragObj();
+}
+
+void SdrDragView::TakeActionRect(tools::Rectangle& rRect) const
+{
+ if (mpCurrentSdrDragMethod)
+ {
+ rRect=maDragStat.GetActionRect();
+ if (rRect.IsEmpty())
+ {
+ SdrPageView* pPV = GetSdrPageView();
+
+ if(pPV&& pPV->HasMarkedObjPageView())
+ {
+ // #i95646# is this used..?
+ const basegfx::B2DRange aBoundRange(mpCurrentSdrDragMethod->getCurrentRange());
+ if (aBoundRange.isEmpty())
+ {
+ rRect.SetEmpty();
+ }
+ else
+ {
+ rRect = tools::Rectangle(
+ basegfx::fround(aBoundRange.getMinX()), basegfx::fround(aBoundRange.getMinY()),
+ basegfx::fround(aBoundRange.getMaxX()), basegfx::fround(aBoundRange.getMaxY()));
+ }
+ }
+ }
+ if (rRect.IsEmpty())
+ {
+ rRect=tools::Rectangle(maDragStat.GetNow(),maDragStat.GetNow());
+ }
+ }
+ else
+ {
+ SdrExchangeView::TakeActionRect(rRect);
+ }
+}
+
+bool SdrDragView::TakeDragObjAnchorPos(Point& rPos, bool bTR ) const
+{
+ tools::Rectangle aR;
+ TakeActionRect(aR);
+ rPos = bTR ? aR.TopRight() : aR.TopLeft();
+ if (GetMarkedObjectCount()==1 && IsDragObj() && // only on single selection
+ !IsDraggingPoints() && !IsDraggingGluePoints() && // not when moving points
+ dynamic_cast<const SdrDragMovHdl*>( mpCurrentSdrDragMethod.get() ) == nullptr) // not when moving handles
+ {
+ SdrObject* pObj=GetMarkedObjectByIndex(0);
+ if (auto pCaptionObj = dynamic_cast<SdrCaptionObj*>(pObj))
+ {
+ Point aPt(pCaptionObj->GetTailPos());
+ bool bTail=meDragHdl==SdrHdlKind::Poly; // drag tail
+ bool bOwn=dynamic_cast<const SdrDragObjOwn*>( mpCurrentSdrDragMethod.get() ) != nullptr; // specific to object
+ if (!bTail)
+ { // for bTail, TakeActionRect already does the right thing
+ if (bOwn)
+ { // bOwn may be MoveTextFrame, ResizeTextFrame, but may not (any more) be DragTail
+ rPos=aPt;
+ }
+ else
+ {
+ // drag the whole Object (Move, Resize, ...)
+ const basegfx::B2DPoint aTransformed(mpCurrentSdrDragMethod->getCurrentTransformation() * basegfx::B2DPoint(aPt.X(), aPt.Y()));
+ rPos.setX( basegfx::fround(aTransformed.getX()) );
+ rPos.setY( basegfx::fround(aTransformed.getY()) );
+ }
+ }
+ }
+ return true;
+ }
+ return false;
+}
+
+
+bool SdrDragView::TakeDragLimit(SdrDragMode /*eMode*/, tools::Rectangle& /*rRect*/) const
+{
+ return false;
+}
+
+bool SdrDragView::BegDragObj(const Point& rPnt, OutputDevice* pOut, SdrHdl* pHdl, short nMinMov, SdrDragMethod* _pForcedMeth)
+{
+ BrkAction();
+
+ // so we don't leak the object on early return
+ std::unique_ptr<SdrDragMethod> pForcedMeth(_pForcedMeth);
+
+ bool bRet=false;
+ {
+ SetDragWithCopy(false);
+ //TODO: aAni.Reset();
+ mpCurrentSdrDragMethod=nullptr;
+ SdrDragMode eTmpMode=meDragMode;
+ if (eTmpMode==SdrDragMode::Move && pHdl!=nullptr && pHdl->GetKind()!=SdrHdlKind::Move) {
+ eTmpMode=SdrDragMode::Resize;
+ }
+ mbDragLimit=TakeDragLimit(eTmpMode,maDragLimit);
+ mbFramDrag=ImpIsFrameHandles();
+ if (!mbFramDrag &&
+ (mpMarkedObj==nullptr || !mpMarkedObj->hasSpecialDrag()) &&
+ (pHdl==nullptr || pHdl->GetObj()==nullptr)) {
+ mbFramDrag=true;
+ }
+
+ Point aPnt(rPnt);
+ basegfx::B2DVector aGridOffset(0.0, 0.0);
+
+ // Coordinate maybe affected by GridOffset, so we may need to
+ // adapt to Model-coordinates here
+ if((comphelper::LibreOfficeKit::isActive() && mpMarkedObj
+ && getPossibleGridOffsetForSdrObject(aGridOffset, GetMarkedObjectByIndex(0), GetSdrPageView()))
+ || (getPossibleGridOffsetForPosition(
+ aGridOffset,
+ basegfx::B2DPoint(aPnt.X(), aPnt.Y()),
+ GetSdrPageView())))
+ {
+ aPnt.AdjustX(basegfx::fround(-aGridOffset.getX()));
+ aPnt.AdjustY(basegfx::fround(-aGridOffset.getY()));
+ }
+
+ if(pHdl == nullptr
+ || pHdl->GetKind() == SdrHdlKind::Move
+ || pHdl->GetKind() == SdrHdlKind::MirrorAxis
+ || pHdl->GetKind() == SdrHdlKind::Transparence
+ || pHdl->GetKind() == SdrHdlKind::Gradient)
+ {
+ maDragStat.Reset(aPnt);
+ }
+ else
+ {
+ maDragStat.Reset(pHdl->GetPos());
+ }
+
+ maDragStat.SetView(static_cast<SdrView*>(this));
+ maDragStat.SetPageView(mpMarkedPV); // <<-- DragPV has to go here!!!
+ maDragStat.SetMinMove(ImpGetMinMovLogic(nMinMov,pOut));
+ maDragStat.SetHdl(pHdl);
+ maDragStat.NextPoint();
+ mpDragWin=pOut;
+ mpDragHdl=pHdl;
+ meDragHdl= pHdl==nullptr ? SdrHdlKind::Move : pHdl->GetKind();
+ mbDragHdl=meDragHdl==SdrHdlKind::Ref1 || meDragHdl==SdrHdlKind::Ref2 || meDragHdl==SdrHdlKind::MirrorAxis;
+
+ // Expand test for SdrHdlKind::Anchor_TR
+ bool bNotDraggable = (SdrHdlKind::Anchor == meDragHdl || SdrHdlKind::Anchor_TR == meDragHdl);
+
+ if(pHdl && (pHdl->GetKind() == SdrHdlKind::SmartTag) && pForcedMeth )
+ {
+ // just use the forced method for smart tags
+ }
+ else if(mbDragHdl)
+ {
+ mpCurrentSdrDragMethod.reset(new SdrDragMovHdl(*this));
+ }
+ else if(!bNotDraggable)
+ {
+ switch (meDragMode)
+ {
+ case SdrDragMode::Rotate: case SdrDragMode::Shear:
+ {
+ switch (meDragHdl)
+ {
+ case SdrHdlKind::Left: case SdrHdlKind::Right:
+ case SdrHdlKind::Upper: case SdrHdlKind::Lower:
+ {
+ // are 3D objects selected?
+ bool b3DObjSelected = false;
+ for(size_t a=0; !b3DObjSelected && a<GetMarkedObjectCount(); ++a)
+ {
+ SdrObject* pObj = GetMarkedObjectByIndex(a);
+ if(dynamic_cast< const E3dObject* >(pObj))
+ b3DObjSelected = true;
+ }
+ // If yes, allow shear even when !IsShearAllowed,
+ // because 3D objects are limited rotations
+ if (!b3DObjSelected && !IsShearAllowed())
+ return false;
+ mpCurrentSdrDragMethod.reset(new SdrDragShear(*this,meDragMode==SdrDragMode::Rotate));
+ } break;
+ case SdrHdlKind::UpperLeft: case SdrHdlKind::UpperRight:
+ case SdrHdlKind::LowerLeft: case SdrHdlKind::LowerRight:
+ {
+ if (meDragMode==SdrDragMode::Shear)
+ {
+ if (!IsDistortAllowed(true) && !IsDistortAllowed()) return false;
+ mpCurrentSdrDragMethod.reset(new SdrDragDistort(*this));
+ }
+ else
+ {
+ if (!IsRotateAllowed(true)) return false;
+ mpCurrentSdrDragMethod.reset(new SdrDragRotate(*this));
+ }
+ } break;
+ default:
+ {
+ if (IsMarkedHitMovesAlways() && meDragHdl==SdrHdlKind::Move)
+ { // SdrHdlKind::Move is true, even if Obj is hit directly
+ if (!IsMoveAllowed()) return false;
+ mpCurrentSdrDragMethod.reset(new SdrDragMove(*this));
+ }
+ else
+ {
+ if (!IsRotateAllowed(true)) return false;
+ mpCurrentSdrDragMethod.reset(new SdrDragRotate(*this));
+ }
+ }
+ }
+ } break;
+ case SdrDragMode::Mirror:
+ {
+ if (meDragHdl==SdrHdlKind::Move && IsMarkedHitMovesAlways())
+ {
+ if (!IsMoveAllowed()) return false;
+ mpCurrentSdrDragMethod.reset(new SdrDragMove(*this));
+ }
+ else
+ {
+ if (!IsMirrorAllowed(true,true)) return false;
+ mpCurrentSdrDragMethod.reset(new SdrDragMirror(*this));
+ }
+ } break;
+
+ case SdrDragMode::Crop:
+ {
+ if (meDragHdl==SdrHdlKind::Move && IsMarkedHitMovesAlways())
+ {
+ if (!IsMoveAllowed())
+ return false;
+ mpCurrentSdrDragMethod.reset(new SdrDragMove(*this));
+ }
+ else
+ {
+ if (!IsCropAllowed())
+ return false;
+ mpCurrentSdrDragMethod.reset(new SdrDragCrop(*this));
+ }
+ }
+ break;
+
+ case SdrDragMode::Transparence:
+ {
+ if(meDragHdl == SdrHdlKind::Move && IsMarkedHitMovesAlways())
+ {
+ if(!IsMoveAllowed())
+ return false;
+ mpCurrentSdrDragMethod.reset(new SdrDragMove(*this));
+ }
+ else
+ {
+ if(!IsTransparenceAllowed())
+ return false;
+
+ mpCurrentSdrDragMethod.reset(new SdrDragGradient(*this, false));
+ }
+ break;
+ }
+ case SdrDragMode::Gradient:
+ {
+ if(meDragHdl == SdrHdlKind::Move && IsMarkedHitMovesAlways())
+ {
+ if(!IsMoveAllowed())
+ return false;
+ mpCurrentSdrDragMethod.reset(new SdrDragMove(*this));
+ }
+ else
+ {
+ if(!IsGradientAllowed())
+ return false;
+
+ mpCurrentSdrDragMethod.reset(new SdrDragGradient(*this));
+ }
+ break;
+ }
+
+ case SdrDragMode::Crook :
+ {
+ if (meDragHdl==SdrHdlKind::Move && IsMarkedHitMovesAlways())
+ {
+ if (!IsMoveAllowed()) return false;
+ mpCurrentSdrDragMethod.reset( new SdrDragMove(*this) );
+ }
+ else
+ {
+ if (!IsCrookAllowed(true) && !IsCrookAllowed()) return false;
+ mpCurrentSdrDragMethod.reset( new SdrDragCrook(*this) );
+ }
+ } break;
+
+ default:
+ {
+ // SdrDragMode::Move
+ if((meDragHdl == SdrHdlKind::Move) && !IsMoveAllowed())
+ {
+ return false;
+ }
+ else if(meDragHdl == SdrHdlKind::Glue)
+ {
+ mpCurrentSdrDragMethod.reset( new SdrDragMove(*this) );
+ }
+ else
+ {
+ if(mbFramDrag)
+ {
+ if(meDragHdl == SdrHdlKind::Move)
+ {
+ mpCurrentSdrDragMethod.reset( new SdrDragMove(*this) );
+ }
+ else
+ {
+ if(!IsResizeAllowed(true))
+ {
+ return false;
+ }
+
+ bool bSingleTextObjMark = false; // SJ: #i100490#
+ if ( GetMarkedObjectCount() == 1 )
+ {
+ mpMarkedObj=GetMarkedObjectByIndex(0);
+ if ( mpMarkedObj &&
+ dynamic_cast<const SdrTextObj*>( mpMarkedObj) != nullptr &&
+ static_cast<SdrTextObj*>(mpMarkedObj)->IsTextFrame() )
+ bSingleTextObjMark = true;
+ }
+ if ( bSingleTextObjMark )
+ mpCurrentSdrDragMethod.reset( new SdrDragObjOwn(*this) );
+ else
+ mpCurrentSdrDragMethod.reset( new SdrDragResize(*this) );
+ }
+ }
+ else
+ {
+ if(SdrHdlKind::Move == meDragHdl)
+ {
+ const bool bCustomShapeSelected(1 == GetMarkedObjectCount() && dynamic_cast<const SdrObjCustomShape*>(GetMarkedObjectByIndex(0)) != nullptr);
+
+ if(bCustomShapeSelected)
+ {
+ mpCurrentSdrDragMethod.reset( new SdrDragMove( *this ) );
+ }
+ }
+ else if(SdrHdlKind::Poly == meDragHdl)
+ {
+ const bool bConnectorSelected(1 == GetMarkedObjectCount() && dynamic_cast<const SdrEdgeObj*>(GetMarkedObjectByIndex(0)) != nullptr);
+
+ if(bConnectorSelected)
+ {
+ // #i97784#
+ // fallback to old behaviour for connectors (see
+ // text in task description for more details)
+ }
+ else if(!IsMoveAllowed() || !IsResizeAllowed())
+ {
+ // #i77187#
+ // do not allow move of polygon points if object is move or size protected
+ return false;
+ }
+ }
+
+ if(!mpCurrentSdrDragMethod)
+ {
+ // fallback to DragSpecial if no interaction defined
+ mpCurrentSdrDragMethod.reset( new SdrDragObjOwn(*this) );
+ }
+ }
+ }
+ }
+ }
+ }
+ if (pForcedMeth)
+ {
+ mpCurrentSdrDragMethod = std::move(pForcedMeth);
+ }
+ maDragStat.SetDragMethod(mpCurrentSdrDragMethod.get());
+ if (mpCurrentSdrDragMethod)
+ {
+ bRet = mpCurrentSdrDragMethod->BeginSdrDrag();
+ if (!bRet)
+ {
+ if (pHdl==nullptr && dynamic_cast< const SdrDragObjOwn* >(mpCurrentSdrDragMethod.get()) != nullptr)
+ {
+ // Obj may not Move SpecialDrag, so try with MoveFrameDrag
+ mpCurrentSdrDragMethod.reset();
+
+ if (!IsMoveAllowed())
+ return false;
+
+ mbFramDrag=true;
+ mpCurrentSdrDragMethod.reset( new SdrDragMove(*this) );
+ maDragStat.SetDragMethod(mpCurrentSdrDragMethod.get());
+ bRet = mpCurrentSdrDragMethod->BeginSdrDrag();
+ }
+ }
+ if (!bRet)
+ {
+ mpCurrentSdrDragMethod.reset();
+ maDragStat.SetDragMethod(mpCurrentSdrDragMethod.get());
+ }
+ }
+ }
+
+ return bRet;
+}
+
+void SdrDragView::MovDragObj(const Point& rPnt)
+{
+ if (!mpCurrentSdrDragMethod)
+ return;
+
+ Point aPnt(rPnt);
+ basegfx::B2DVector aGridOffset(0.0, 0.0);
+
+ // Coordinate maybe affected by GridOffset, so we may need to
+ // adapt to Model-coordinates here
+ if((comphelper::LibreOfficeKit::isActive() && mpMarkedObj
+ && getPossibleGridOffsetForSdrObject(aGridOffset, GetMarkedObjectByIndex(0), GetSdrPageView()))
+ || (getPossibleGridOffsetForPosition(
+ aGridOffset,
+ basegfx::B2DPoint(aPnt.X(), aPnt.Y()),
+ GetSdrPageView())))
+ {
+ aPnt.AdjustX(basegfx::fround(-aGridOffset.getX()));
+ aPnt.AdjustY(basegfx::fround(-aGridOffset.getY()));
+ }
+
+ ImpLimitToWorkArea(aPnt);
+ mpCurrentSdrDragMethod->MoveSdrDrag(aPnt); // this call already makes a Hide()/Show combination
+}
+
+bool SdrDragView::EndDragObj(bool bCopy)
+{
+ bool bRet(false);
+
+ // #i73341# If inserting GluePoint, do not insist on last points being different
+ if(mpCurrentSdrDragMethod && maDragStat.IsMinMoved() && (IsInsertGluePoint() || maDragStat.GetNow() != maDragStat.GetPrev()))
+ {
+ sal_Int32 nSavedHdlCount=0;
+
+ if (bEliminatePolyPoints)
+ {
+ nSavedHdlCount=GetMarkablePointCount();
+ }
+
+ const bool bUndo = IsUndoEnabled();
+ if (IsInsertGluePoint() && bUndo)
+ {
+ BegUndo(maInsPointUndoStr);
+ AddUndo(std::unique_ptr<SdrUndoAction>(mpInsPointUndo));
+ }
+
+ bRet = mpCurrentSdrDragMethod->EndSdrDrag(bCopy);
+
+ if( IsInsertGluePoint() && bUndo)
+ EndUndo();
+
+ mpCurrentSdrDragMethod.reset();
+
+ if (bEliminatePolyPoints)
+ {
+ if (nSavedHdlCount!=GetMarkablePointCount())
+ {
+ UnmarkAllPoints();
+ }
+ }
+
+ if (mbInsPolyPoint)
+ {
+ SetMarkHandles(nullptr);
+ mbInsPolyPoint=false;
+ if( bUndo )
+ {
+ BegUndo(maInsPointUndoStr);
+ AddUndo(std::unique_ptr<SdrUndoAction>(mpInsPointUndo));
+ EndUndo();
+ }
+ }
+
+ meDragHdl=SdrHdlKind::Move;
+ mpDragHdl=nullptr;
+
+ if (!mbSomeObjChgdFlag)
+ {
+ // Obj did not broadcast (e. g. Writer FlyFrames)
+ if(!mbDragHdl)
+ {
+ AdjustMarkHdl();
+ }
+ }
+ }
+ else
+ {
+ BrkDragObj();
+ }
+
+ mbInsPolyPoint=false;
+ SetInsertGluePoint(false);
+
+ return bRet;
+}
+
+void SdrDragView::BrkDragObj()
+{
+ if (!mpCurrentSdrDragMethod)
+ return;
+
+ mpCurrentSdrDragMethod->CancelSdrDrag();
+
+ mpCurrentSdrDragMethod.reset();
+
+ if (mbInsPolyPoint)
+ {
+ mpInsPointUndo->Undo(); // delete inserted point again
+ delete mpInsPointUndo;
+ mpInsPointUndo=nullptr;
+ SetMarkHandles(nullptr);
+ mbInsPolyPoint=false;
+ }
+
+ if (IsInsertGluePoint())
+ {
+ mpInsPointUndo->Undo(); // delete inserted gluepoint again
+ delete mpInsPointUndo;
+ mpInsPointUndo=nullptr;
+ SetInsertGluePoint(false);
+ }
+
+ meDragHdl=SdrHdlKind::Move;
+ mpDragHdl=nullptr;
+}
+
+bool SdrDragView::IsInsObjPointPossible() const
+{
+ return mpMarkedObj!=nullptr && mpMarkedObj->IsPolyObj();
+}
+
+bool SdrDragView::ImpBegInsObjPoint(bool bIdxZwang, const Point& rPnt, bool bNewObj, OutputDevice* pOut)
+{
+ bool bRet(false);
+
+ if(auto pMarkedPath = dynamic_cast<SdrPathObj*>( mpMarkedObj))
+ {
+ BrkAction();
+ mpInsPointUndo = dynamic_cast< SdrUndoGeoObj* >( GetModel()->GetSdrUndoFactory().CreateUndoGeoObject(*mpMarkedObj).release() );
+ DBG_ASSERT( mpInsPointUndo, "svx::SdrDragView::BegInsObjPoint(), could not create correct undo object!" );
+
+ OUString aStr(SvxResId(STR_DragInsertPoint));
+
+ maInsPointUndoStr = aStr.replaceFirst("%1", mpMarkedObj->TakeObjNameSingul() );
+
+ Point aPt(rPnt);
+
+ if(bNewObj)
+ aPt = GetSnapPos(aPt,mpMarkedPV);
+
+ bool bClosed0 = pMarkedPath->IsClosedObj();
+
+ const sal_uInt32 nInsPointNum { bIdxZwang
+ ? pMarkedPath->NbcInsPoint(aPt, bNewObj)
+ : pMarkedPath->NbcInsPointOld(aPt, bNewObj)
+ };
+
+ if(bClosed0 != pMarkedPath->IsClosedObj())
+ {
+ // Obj was closed implicitly
+ // object changed
+ pMarkedPath->SetChanged();
+ pMarkedPath->BroadcastObjectChange();
+ }
+
+ if (nInsPointNum != SAL_MAX_UINT32)
+ {
+ mbInsPolyPoint = true;
+ UnmarkAllPoints();
+ AdjustMarkHdl();
+
+ bRet = BegDragObj(rPnt, pOut, maHdlList.GetHdl(nInsPointNum), 0);
+
+ if (bRet)
+ {
+ maDragStat.SetMinMoved();
+ MovDragObj(rPnt);
+ }
+ }
+ else
+ {
+ delete mpInsPointUndo;
+ mpInsPointUndo = nullptr;
+ }
+ }
+
+ return bRet;
+}
+
+bool SdrDragView::EndInsObjPoint(SdrCreateCmd eCmd)
+{
+ if(IsInsObjPoint())
+ {
+ Point aPnt(maDragStat.GetNow());
+ bool bOk=EndDragObj();
+ if (bOk && eCmd!=SdrCreateCmd::ForceEnd)
+ {
+ // Ret=True means: Action is over.
+ bOk = ! ImpBegInsObjPoint(true, aPnt, eCmd == SdrCreateCmd::NextObject, mpDragWin);
+ }
+
+ return bOk;
+ } else return false;
+}
+
+bool SdrDragView::IsInsGluePointPossible() const
+{
+ bool bRet=false;
+ if (IsInsGluePointMode() && AreObjectsMarked())
+ {
+ if (GetMarkedObjectCount()==1)
+ {
+ // return sal_False, if only 1 object which is a connector.
+ const SdrObject* pObj=GetMarkedObjectByIndex(0);
+ if (dynamic_cast<const SdrEdgeObj *>(pObj) == nullptr)
+ {
+ bRet=true;
+ }
+ }
+ else
+ {
+ bRet=true;
+ }
+ }
+ return bRet;
+}
+
+bool SdrDragView::BegInsGluePoint(const Point& rPnt)
+{
+ bool bRet=false;
+ SdrObject* pObj;
+ SdrPageView* pPV;
+ if (PickMarkedObj(rPnt,pObj,pPV,SdrSearchOptions::PASS2BOUND))
+ {
+ BrkAction();
+ UnmarkAllGluePoints();
+ mpInsPointUndo= dynamic_cast< SdrUndoGeoObj* >( GetModel()->GetSdrUndoFactory().CreateUndoGeoObject(*pObj).release() );
+ DBG_ASSERT( mpInsPointUndo, "svx::SdrDragView::BegInsObjPoint(), could not create correct undo object!" );
+ OUString aStr(SvxResId(STR_DragInsertGluePoint));
+
+ maInsPointUndoStr = aStr.replaceFirst("%1", pObj->TakeObjNameSingul() );
+
+ SdrGluePointList* pGPL=pObj->ForceGluePointList();
+ if (pGPL!=nullptr)
+ {
+ sal_uInt16 nGlueIdx=pGPL->Insert(SdrGluePoint());
+ SdrGluePoint& rGP=(*pGPL)[nGlueIdx];
+ sal_uInt16 nGlueId=rGP.GetId();
+ rGP.SetAbsolutePos(rPnt,*pObj);
+
+ SdrHdl* pHdl=nullptr;
+ if (MarkGluePoint(pObj,nGlueId,false))
+ {
+ pHdl=GetGluePointHdl(pObj,nGlueId);
+ }
+ if (pHdl!=nullptr && pHdl->GetKind()==SdrHdlKind::Glue && pHdl->GetObj()==pObj && pHdl->GetObjHdlNum()==nGlueId)
+ {
+ SetInsertGluePoint(true);
+ bRet=BegDragObj(rPnt,nullptr,pHdl,0);
+ if (bRet)
+ {
+ maDragStat.SetMinMoved();
+ MovDragObj(rPnt);
+ }
+ else
+ {
+ SetInsertGluePoint(false);
+ delete mpInsPointUndo;
+ mpInsPointUndo=nullptr;
+ }
+ }
+ else
+ {
+ OSL_FAIL("BegInsGluePoint(): GluePoint handle not found.");
+ }
+ }
+ else
+ {
+ // no gluepoints possible for this object (e. g. Edge)
+ SetInsertGluePoint(false);
+ delete mpInsPointUndo;
+ mpInsPointUndo=nullptr;
+ }
+ }
+
+ return bRet;
+}
+
+void SdrDragView::ShowDragObj()
+{
+ if(!mpCurrentSdrDragMethod || maDragStat.IsShown())
+ return;
+
+ // Changed for the GridOffset stuff: No longer iterate over
+ // SdrPaintWindow(s), but now over SdrPageWindow(s), so doing the
+ // same as the SdrHdl visualizations (see ::CreateB2dIAObject) do.
+ // This is needed to get access to an ObjectContact which is needed
+ // to evtl. process that GridOffset in CreateOverlayGeometry
+ SdrPageView* pPageView(GetSdrPageView());
+
+ if(nullptr != pPageView)
+ {
+ for(sal_uInt32 a(0); a < pPageView->PageWindowCount(); a++)
+ {
+ const SdrPageWindow& rPageWindow(*pPageView->GetPageWindow(a));
+ const SdrPaintWindow& rPaintWindow(rPageWindow.GetPaintWindow());
+
+ if(rPaintWindow.OutputToWindow())
+ {
+ const rtl::Reference<sdr::overlay::OverlayManager>& xOverlayManager(
+ rPaintWindow.GetOverlayManager());
+
+ if(xOverlayManager.is())
+ {
+ mpCurrentSdrDragMethod->CreateOverlayGeometry(
+ *xOverlayManager,
+ rPageWindow.GetObjectContact());
+
+ // #i101679# Force changed overlay to be shown
+ xOverlayManager->flush();
+ }
+ }
+ }
+ }
+
+ maDragStat.SetShown(true);
+}
+
+void SdrDragView::HideDragObj()
+{
+ if(mpCurrentSdrDragMethod && maDragStat.IsShown())
+ {
+ mpCurrentSdrDragMethod->destroyOverlayGeometry();
+ maDragStat.SetShown(false);
+ }
+}
+
+
+void SdrDragView::SetNoDragXorPolys(bool bOn)
+{
+ if (IsNoDragXorPolys()==bOn)
+ return;
+
+ const bool bDragging(mpCurrentSdrDragMethod);
+ const bool bShown(bDragging && maDragStat.IsShown());
+
+ if(bShown)
+ {
+ HideDragObj();
+ }
+
+ mbNoDragXorPolys = bOn;
+
+ if(bDragging)
+ {
+ // force recreation of drag content
+ mpCurrentSdrDragMethod->resetSdrDragEntries();
+ }
+
+ if(bShown)
+ {
+ ShowDragObj();
+ }
+}
+
+void SdrDragView::SetDragStripes(bool bOn)
+{
+ if (mpCurrentSdrDragMethod && maDragStat.IsShown())
+ {
+ HideDragObj();
+ mbDragStripes=bOn;
+ ShowDragObj();
+ }
+ else
+ {
+ mbDragStripes=bOn;
+ }
+}
+
+bool SdrDragView::IsOrthoDesired() const
+{
+ if( dynamic_cast< const SdrDragObjOwn* >( mpCurrentSdrDragMethod.get() )
+ || dynamic_cast< const SdrDragResize* >(mpCurrentSdrDragMethod.get() ))
+ {
+ return m_bOrthoDesiredOnMarked;
+ }
+
+ return false;
+}
+
+void SdrDragView::SetMarkHandles(SfxViewShell* pOtherShell)
+{
+ if( mpDragHdl )
+ mpDragHdl = nullptr;
+
+ SdrExchangeView::SetMarkHandles(pOtherShell);
+}
+
+void SdrDragView::SetSolidDragging(bool bOn)
+{
+ if(mbSolidDragging != bOn)
+ {
+ mbSolidDragging = bOn;
+ }
+}
+
+bool SdrDragView::IsSolidDragging() const
+{
+ // allow each user to disable by having a local setting, but using AND for
+ // checking allowance
+ return mbSolidDragging && SvtOptionsDrawinglayer::IsSolidDragCreate();
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/svdraw/svdedtv.cxx b/svx/source/svdraw/svdedtv.cxx
new file mode 100644
index 000000000..b019f26ae
--- /dev/null
+++ b/svx/source/svdraw/svdedtv.cxx
@@ -0,0 +1,1103 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * 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/svdedtv.hxx>
+#include <svx/svdundo.hxx>
+#include <svx/svdograf.hxx>
+#include <svx/svdoole2.hxx>
+#include <svx/svdoedge.hxx>
+#include <svx/svdlayer.hxx>
+#include <svx/svdpagv.hxx>
+#include <svx/svdpage.hxx>
+#include <svx/svdpoev.hxx>
+#include <svx/strings.hrc>
+#include <svx/dialmgr.hxx>
+#include <svx/e3dsceneupdater.hxx>
+#include <rtl/strbuf.hxx>
+#include <svx/svdview.hxx>
+#include <clonelist.hxx>
+#include <svx/svdogrp.hxx>
+#include <svx/scene3d.hxx>
+#include <svx/xfillit0.hxx>
+#include <osl/diagnose.h>
+
+#include <com/sun/star/lang/XServiceInfo.hpp>
+
+using namespace com::sun::star;
+
+void SdrEditView::ImpResetPossibilityFlags()
+{
+ m_bReadOnly =false;
+
+ m_bGroupPossible =false;
+ m_bUnGroupPossible =false;
+ m_bGrpEnterPossible =false;
+ m_bToTopPossible =false;
+ m_bToBtmPossible =false;
+ m_bReverseOrderPossible =false;
+
+ m_bImportMtfPossible =false;
+ m_bCombinePossible =false;
+ m_bDismantlePossible =false;
+ m_bCombineNoPolyPolyPossible =false;
+ m_bDismantleMakeLinesPossible=false;
+ m_bOrthoDesiredOnMarked =false;
+
+ m_bOneOrMoreMovable =false;
+ m_bMoreThanOneNoMovRot =false;
+ m_bContortionPossible =false;
+ m_bMoveAllowed =false;
+ m_bResizeFreeAllowed =false;
+ m_bResizePropAllowed =false;
+ m_bRotateFreeAllowed =false;
+ m_bRotate90Allowed =false;
+ m_bMirrorFreeAllowed =false;
+ m_bMirror45Allowed =false;
+ m_bMirror90Allowed =false;
+ m_bTransparenceAllowed =false;
+ m_bCropAllowed =false;
+ m_bGradientAllowed =false;
+ m_bShearAllowed =false;
+ m_bEdgeRadiusAllowed =false;
+ m_bCanConvToPath =false;
+ m_bCanConvToPoly =false;
+ m_bCanConvToContour =false;
+ m_bMoveProtect =false;
+ m_bResizeProtect =false;
+}
+
+SdrEditView::SdrEditView(SdrModel& rSdrModel, OutputDevice* pOut)
+ : SdrMarkView(rSdrModel, pOut)
+ , m_bPossibilitiesDirty(true)
+ , m_bReadOnly(false)
+ , m_bGroupPossible(false)
+ , m_bUnGroupPossible(false)
+ , m_bGrpEnterPossible(false)
+ , m_bToTopPossible(false)
+ , m_bToBtmPossible(false)
+ , m_bReverseOrderPossible(false)
+ , m_bImportMtfPossible(false)
+ , m_bCombinePossible(false)
+ , m_bDismantlePossible(false)
+ , m_bCombineNoPolyPolyPossible(false)
+ , m_bDismantleMakeLinesPossible(false)
+ , m_bOrthoDesiredOnMarked(false)
+ , m_bOneOrMoreMovable(false)
+ , m_bMoreThanOneNoMovRot(false)
+ , m_bContortionPossible(false)
+ , m_bMoveAllowed(false)
+ , m_bResizeFreeAllowed(false)
+ , m_bResizePropAllowed(false)
+ , m_bRotateFreeAllowed(false)
+ , m_bRotate90Allowed(false)
+ , m_bMirrorFreeAllowed(false)
+ , m_bMirror45Allowed(false)
+ , m_bMirror90Allowed(false)
+ , m_bShearAllowed(false)
+ , m_bEdgeRadiusAllowed(false)
+ , m_bTransparenceAllowed(false)
+ , m_bCropAllowed(false)
+ , m_bGradientAllowed(false)
+ , m_bCanConvToPath(false)
+ , m_bCanConvToPoly(false)
+ , m_bCanConvToContour(false)
+ , m_bMoveProtect(false)
+ , m_bResizeProtect(false)
+{
+}
+
+SdrEditView::~SdrEditView()
+{
+}
+
+void SdrEditView::InsertNewLayer(const OUString& rName, sal_uInt16 nPos)
+{
+ SdrLayerAdmin& rLA=mpModel->GetLayerAdmin();
+ sal_uInt16 nMax=rLA.GetLayerCount();
+ if (nPos>nMax) nPos=nMax;
+ rLA.NewLayer(rName,nPos);
+
+ if( GetModel()->IsUndoEnabled() )
+ AddUndo(GetModel()->GetSdrUndoFactory().CreateUndoNewLayer(nPos,rLA,*mpModel));
+
+ mpModel->SetChanged();
+}
+
+bool SdrEditView::ImpDelLayerCheck(SdrObjList const * pOL, SdrLayerID nDelID) const
+{
+ bool bDelAll(true);
+
+ for(size_t nObjNum = pOL->GetObjCount(); nObjNum > 0 && bDelAll;)
+ {
+ nObjNum--;
+ SdrObject* pObj = pOL->GetObj(nObjNum);
+ SdrObjList* pSubOL = pObj->GetSubList();
+
+ // explicitly test for group objects and 3d scenes
+ if(pSubOL && (dynamic_cast<const SdrObjGroup*>(pObj) != nullptr || dynamic_cast< const E3dScene* >(pObj) != nullptr))
+ {
+ if(!ImpDelLayerCheck(pSubOL, nDelID))
+ {
+ bDelAll = false;
+ }
+ }
+ else
+ {
+ if(pObj->GetLayer() != nDelID)
+ {
+ bDelAll = false;
+ }
+ }
+ }
+
+ return bDelAll;
+}
+
+void SdrEditView::ImpDelLayerDelObjs(SdrObjList* pOL, SdrLayerID nDelID)
+{
+ const size_t nObjCount(pOL->GetObjCount());
+ // make sure OrdNums are correct
+ pOL->GetObj(0)->GetOrdNum();
+
+ const bool bUndo = GetModel()->IsUndoEnabled();
+
+ for(size_t nObjNum = nObjCount; nObjNum > 0;)
+ {
+ nObjNum--;
+ SdrObject* pObj = pOL->GetObj(nObjNum);
+ SdrObjList* pSubOL = pObj->GetSubList();
+
+
+ // explicitly test for group objects and 3d scenes
+ if(pSubOL && (dynamic_cast<const SdrObjGroup*>( pObj) != nullptr || dynamic_cast<const E3dScene* >(pObj) != nullptr))
+ {
+ if(ImpDelLayerCheck(pSubOL, nDelID))
+ {
+ if( bUndo )
+ AddUndo(GetModel()->GetSdrUndoFactory().CreateUndoDeleteObject(*pObj, true));
+ pOL->RemoveObject(nObjNum);
+
+ if( !bUndo )
+ SdrObject::Free( pObj );
+ }
+ else
+ {
+ ImpDelLayerDelObjs(pSubOL, nDelID);
+ }
+ }
+ else
+ {
+ if(pObj->GetLayer() == nDelID)
+ {
+ if( bUndo )
+ AddUndo(GetModel()->GetSdrUndoFactory().CreateUndoDeleteObject(*pObj, true));
+ pOL->RemoveObject(nObjNum);
+ if( !bUndo )
+ SdrObject::Free( pObj );
+ }
+ }
+ }
+}
+
+void SdrEditView::DeleteLayer(const OUString& rName)
+{
+ SdrLayerAdmin& rLA = mpModel->GetLayerAdmin();
+ SdrLayer* pLayer = rLA.GetLayer(rName);
+
+ if(!pLayer)
+ return;
+
+ sal_uInt16 nLayerNum(rLA.GetLayerPos(pLayer));
+ SdrLayerID nDelID = pLayer->GetID();
+
+ const bool bUndo = IsUndoEnabled();
+ if( bUndo )
+ BegUndo(SvxResId(STR_UndoDelLayer));
+
+ bool bMaPg(true);
+
+ for(sal_uInt16 nPageKind(0); nPageKind < 2; nPageKind++)
+ {
+ // MasterPages and DrawPages
+ sal_uInt16 nPgCount(bMaPg ? mpModel->GetMasterPageCount() : mpModel->GetPageCount());
+
+ for(sal_uInt16 nPgNum(0); nPgNum < nPgCount; nPgNum++)
+ {
+ // over all pages
+ SdrPage* pPage = bMaPg ? mpModel->GetMasterPage(nPgNum) : mpModel->GetPage(nPgNum);
+ const size_t nObjCount(pPage->GetObjCount());
+
+ // make sure OrdNums are correct
+ if(nObjCount)
+ pPage->GetObj(0)->GetOrdNum();
+
+ for(size_t nObjNum(nObjCount); nObjNum > 0;)
+ {
+ nObjNum--;
+ SdrObject* pObj = pPage->GetObj(nObjNum);
+ SdrObjList* pSubOL = pObj->GetSubList();
+
+ // explicitly test for group objects and 3d scenes
+ if(pSubOL && (dynamic_cast<const SdrObjGroup*>(pObj) != nullptr || dynamic_cast<const E3dScene* >(pObj) != nullptr))
+ {
+ if(ImpDelLayerCheck(pSubOL, nDelID))
+ {
+ if( bUndo )
+ AddUndo(GetModel()->GetSdrUndoFactory().CreateUndoDeleteObject(*pObj, true));
+ pPage->RemoveObject(nObjNum);
+ if( !bUndo )
+ SdrObject::Free(pObj);
+ }
+ else
+ {
+ ImpDelLayerDelObjs(pSubOL, nDelID);
+ }
+ }
+ else
+ {
+ if(pObj->GetLayer() == nDelID)
+ {
+ if( bUndo )
+ AddUndo(GetModel()->GetSdrUndoFactory().CreateUndoDeleteObject(*pObj, true));
+ pPage->RemoveObject(nObjNum);
+ if( !bUndo )
+ SdrObject::Free(pObj);
+ }
+ }
+ }
+ }
+ bMaPg = false;
+ }
+
+ if( bUndo )
+ {
+ AddUndo(GetModel()->GetSdrUndoFactory().CreateUndoDeleteLayer(nLayerNum, rLA, *mpModel));
+ rLA.RemoveLayer(nLayerNum).release();
+ EndUndo();
+ }
+ else
+ {
+ rLA.RemoveLayer(nLayerNum);
+ }
+
+ mpModel->SetChanged();
+}
+
+
+void SdrEditView::EndUndo()
+{
+ // #i13033#
+ // Comparison changed to 1L since EndUndo() is called later now
+ // and EndUndo WILL change count to count-1
+ if(1 == mpModel->GetUndoBracketLevel())
+ {
+ ImpBroadcastEdgesOfMarkedNodes();
+ }
+
+ // #i13033#
+ // moved to bottom to still have access to UNDOs inside of
+ // ImpBroadcastEdgesOfMarkedNodes()
+ mpModel->EndUndo();
+}
+
+void SdrEditView::ImpBroadcastEdgesOfMarkedNodes()
+{
+ std::vector<SdrObject*>::const_iterator iterPos;
+ const std::vector<SdrObject*>& rAllMarkedObjects = GetTransitiveHullOfMarkedObjects();
+
+ // #i13033#
+ // New mechanism to search for necessary disconnections for
+ // changed connectors inside the transitive hull of all at
+ // the beginning of UNDO selected objects
+ for(size_t a(0); a < rAllMarkedObjects.size(); a++)
+ {
+ SdrEdgeObj* pEdge = dynamic_cast<SdrEdgeObj*>( rAllMarkedObjects[a] );
+
+ if(pEdge)
+ {
+ SdrObject* pObj1 = pEdge->GetConnectedNode(false);
+ SdrObject* pObj2 = pEdge->GetConnectedNode(true);
+
+ if(pObj1 && !pEdge->CheckNodeConnection(false))
+ {
+ iterPos = std::find(rAllMarkedObjects.begin(),rAllMarkedObjects.end(),pObj1);
+
+ if (iterPos == rAllMarkedObjects.end())
+ {
+ if( IsUndoEnabled() )
+ AddUndo( GetModel()->GetSdrUndoFactory().CreateUndoGeoObject(*pEdge));
+ pEdge->DisconnectFromNode(false);
+ }
+ }
+
+ if(pObj2 && !pEdge->CheckNodeConnection(true))
+ {
+ iterPos = std::find(rAllMarkedObjects.begin(),rAllMarkedObjects.end(),pObj2);
+
+ if (iterPos == rAllMarkedObjects.end())
+ {
+ if( IsUndoEnabled() )
+ AddUndo( GetModel()->GetSdrUndoFactory().CreateUndoGeoObject(*pEdge));
+ pEdge->DisconnectFromNode(true);
+ }
+ }
+ }
+ }
+
+ const size_t nMarkedEdgeCnt = GetMarkedEdgesOfMarkedNodes().GetMarkCount();
+
+ for (size_t i=0; i<nMarkedEdgeCnt; ++i) {
+ SdrMark* pEM = GetMarkedEdgesOfMarkedNodes().GetMark(i);
+ SdrObject* pEdgeTmp=pEM->GetMarkedSdrObj();
+ SdrEdgeObj* pEdge=dynamic_cast<SdrEdgeObj*>( pEdgeTmp );
+ if (pEdge!=nullptr) {
+ pEdge->SetEdgeTrackDirty();
+ }
+ }
+}
+
+
+// Possibilities
+
+
+void SdrEditView::MarkListHasChanged()
+{
+ SdrMarkView::MarkListHasChanged();
+ m_bPossibilitiesDirty=true;
+}
+
+void SdrEditView::ModelHasChanged()
+{
+ SdrMarkView::ModelHasChanged();
+ m_bPossibilitiesDirty=true;
+}
+
+bool SdrEditView::IsResizeAllowed(bool bProp) const
+{
+ ForcePossibilities();
+ if (m_bResizeProtect) return false;
+ if (bProp) return m_bResizePropAllowed;
+ return m_bResizeFreeAllowed;
+}
+
+bool SdrEditView::IsRotateAllowed(bool b90Deg) const
+{
+ ForcePossibilities();
+ if (m_bMoveProtect) return false;
+ if (b90Deg) return m_bRotate90Allowed;
+ return m_bRotateFreeAllowed;
+}
+
+bool SdrEditView::IsMirrorAllowed(bool b45Deg, bool b90Deg) const
+{
+ ForcePossibilities();
+ if (m_bMoveProtect) return false;
+ if (b90Deg) return m_bMirror90Allowed;
+ if (b45Deg) return m_bMirror45Allowed;
+ return m_bMirrorFreeAllowed;
+}
+
+bool SdrEditView::IsTransparenceAllowed() const
+{
+ ForcePossibilities();
+ return m_bTransparenceAllowed;
+}
+
+bool SdrEditView::IsCropAllowed() const
+{
+ ForcePossibilities();
+ return m_bCropAllowed;
+}
+
+bool SdrEditView::IsGradientAllowed() const
+{
+ ForcePossibilities();
+ return m_bGradientAllowed;
+}
+
+bool SdrEditView::IsShearAllowed() const
+{
+ ForcePossibilities();
+ if (m_bResizeProtect) return false;
+ return m_bShearAllowed;
+}
+
+bool SdrEditView::IsEdgeRadiusAllowed() const
+{
+ ForcePossibilities();
+ return m_bEdgeRadiusAllowed;
+}
+
+bool SdrEditView::IsCrookAllowed(bool bNoContortion) const
+{
+ // CrookMode missing here (no rotations allowed when shearing ...)
+ ForcePossibilities();
+ if (bNoContortion) {
+ if (!m_bRotateFreeAllowed) return false;
+ return !m_bMoveProtect && m_bMoveAllowed;
+ } else {
+ return !m_bResizeProtect && m_bContortionPossible;
+ }
+}
+
+bool SdrEditView::IsDistortAllowed(bool bNoContortion) const
+{
+ ForcePossibilities();
+ if (bNoContortion) {
+ return false;
+ } else {
+ return !m_bResizeProtect && m_bContortionPossible;
+ }
+}
+
+bool SdrEditView::IsCombinePossible(bool bNoPolyPoly) const
+{
+ ForcePossibilities();
+ if (bNoPolyPoly) return m_bCombineNoPolyPolyPossible;
+ else return m_bCombinePossible;
+}
+
+bool SdrEditView::IsDismantlePossible(bool bMakeLines) const
+{
+ ForcePossibilities();
+ if (bMakeLines) return m_bDismantleMakeLinesPossible;
+ else return m_bDismantlePossible;
+}
+
+void SdrEditView::CheckPossibilities()
+{
+ if (mbSomeObjChgdFlag)
+ {
+ m_bPossibilitiesDirty = true;
+
+ // This call IS necessary to correct the MarkList, in which
+ // no longer to the model belonging objects still can reside.
+ // These ones need to be removed.
+ CheckMarked();
+ }
+
+ if (!m_bPossibilitiesDirty)
+ return;
+
+ ImpResetPossibilityFlags();
+ SortMarkedObjects();
+ const size_t nMarkCount = GetMarkedObjectCount();
+ if (nMarkCount != 0)
+ {
+ m_bReverseOrderPossible = (nMarkCount >= 2);
+
+ size_t nMovableCount=0;
+ m_bGroupPossible=nMarkCount>=2;
+ m_bCombinePossible=nMarkCount>=2;
+ if (nMarkCount==1)
+ {
+ // check bCombinePossible more thoroughly
+ // still missing ...
+ const SdrObject* pObj=GetMarkedObjectByIndex(0);
+ //const SdrPathObj* pPath=dynamic_cast<SdrPathObj*>( pObj );
+ bool bGroup=pObj->GetSubList()!=nullptr;
+ bool bHasText=pObj->GetOutlinerParaObject()!=nullptr;
+ if (bGroup || bHasText) {
+ m_bCombinePossible=true;
+ }
+ }
+ m_bCombineNoPolyPolyPossible=m_bCombinePossible;
+ // accept transformations for now
+ m_bMoveAllowed =true;
+ m_bResizeFreeAllowed=true;
+ m_bResizePropAllowed=true;
+ m_bRotateFreeAllowed=true;
+ m_bRotate90Allowed =true;
+ m_bMirrorFreeAllowed=true;
+ m_bMirror45Allowed =true;
+ m_bMirror90Allowed =true;
+ m_bShearAllowed =true;
+ m_bEdgeRadiusAllowed=false;
+ m_bContortionPossible=true;
+ m_bCanConvToContour = true;
+
+ // these ones are only allowed when single object is selected
+ m_bTransparenceAllowed = (nMarkCount == 1);
+ m_bGradientAllowed = (nMarkCount == 1);
+ m_bCropAllowed = (nMarkCount == 1);
+ if(m_bGradientAllowed)
+ {
+ // gradient depends on fill style
+ const SdrMark* pM = GetSdrMarkByIndex(0);
+ const SdrObject* pObj = pM->GetMarkedSdrObj();
+
+ // may be group object, so get merged ItemSet
+ const SfxItemSet& rSet = pObj->GetMergedItemSet();
+ SfxItemState eState = rSet.GetItemState(XATTR_FILLSTYLE, false);
+
+ if(SfxItemState::DONTCARE != eState)
+ {
+ // If state is not DONTCARE, test the item
+ drawing::FillStyle eFillStyle = rSet.Get(XATTR_FILLSTYLE).GetValue();
+
+ if(eFillStyle != drawing::FillStyle_GRADIENT)
+ {
+ m_bGradientAllowed = false;
+ }
+ }
+ }
+
+ bool bNoMovRotFound=false;
+ const SdrPageView* pPV0=nullptr;
+
+ for (size_t nm=0; nm<nMarkCount; ++nm) {
+ const SdrMark* pM=GetSdrMarkByIndex(nm);
+ const SdrObject* pObj=pM->GetMarkedSdrObj();
+ const SdrPageView* pPV=pM->GetPageView();
+ if (pPV!=pPV0) {
+ if (pPV->IsReadOnly()) m_bReadOnly=true;
+ pPV0=pPV;
+ }
+
+ SdrObjTransformInfoRec aInfo;
+ pObj->TakeObjInfo(aInfo);
+ bool bMovPrt=pObj->IsMoveProtect();
+ bool bSizPrt=pObj->IsResizeProtect();
+ if (!bMovPrt && aInfo.bMoveAllowed) nMovableCount++; // count MovableObjs
+ if (bMovPrt) m_bMoveProtect=true;
+ if (bSizPrt) m_bResizeProtect=true;
+
+ // not allowed when not allowed at one object
+ if(!aInfo.bTransparenceAllowed)
+ m_bTransparenceAllowed = false;
+
+ // If one of these can't do something, none can
+ if (!aInfo.bMoveAllowed ) m_bMoveAllowed =false;
+ if (!aInfo.bResizeFreeAllowed) m_bResizeFreeAllowed=false;
+ if (!aInfo.bResizePropAllowed) m_bResizePropAllowed=false;
+ if (!aInfo.bRotateFreeAllowed) m_bRotateFreeAllowed=false;
+ if (!aInfo.bRotate90Allowed ) m_bRotate90Allowed =false;
+ if (!aInfo.bMirrorFreeAllowed) m_bMirrorFreeAllowed=false;
+ if (!aInfo.bMirror45Allowed ) m_bMirror45Allowed =false;
+ if (!aInfo.bMirror90Allowed ) m_bMirror90Allowed =false;
+ if (!aInfo.bShearAllowed ) m_bShearAllowed =false;
+ if (aInfo.bEdgeRadiusAllowed) m_bEdgeRadiusAllowed=true;
+ if (aInfo.bNoContortion ) m_bContortionPossible=false;
+ // For Crook with Contortion: all objects have to be
+ // Movable and Rotatable, except for a maximum of 1 of them
+ if (!m_bMoreThanOneNoMovRot) {
+ if (!aInfo.bMoveAllowed || !aInfo.bResizeFreeAllowed) {
+ m_bMoreThanOneNoMovRot=bNoMovRotFound;
+ bNoMovRotFound=true;
+ }
+ }
+
+ // Must be resizable to allow cropping
+ if (!aInfo.bResizeFreeAllowed && !aInfo.bResizePropAllowed)
+ m_bCropAllowed = false;
+
+ // if one member cannot be converted, no conversion is possible
+ if(!aInfo.bCanConvToContour)
+ m_bCanConvToContour = false;
+
+ // Ungroup
+ if (!m_bUnGroupPossible) m_bUnGroupPossible=pObj->GetSubList()!=nullptr;
+ // ConvertToCurve: If at least one can be converted, that is fine.
+ if (aInfo.bCanConvToPath ) m_bCanConvToPath =true;
+ if (aInfo.bCanConvToPoly ) m_bCanConvToPoly =true;
+
+ // Combine/Dismantle
+ if(m_bCombinePossible)
+ {
+ m_bCombinePossible = ImpCanConvertForCombine(pObj);
+ m_bCombineNoPolyPolyPossible = m_bCombinePossible;
+ }
+
+ if (!m_bDismantlePossible) m_bDismantlePossible = ImpCanDismantle(pObj, false);
+ if (!m_bDismantleMakeLinesPossible) m_bDismantleMakeLinesPossible = ImpCanDismantle(pObj, true);
+ // check OrthoDesiredOnMarked
+ if (!m_bOrthoDesiredOnMarked && !aInfo.bNoOrthoDesired) m_bOrthoDesiredOnMarked=true;
+ // check ImportMtf
+
+ if (!m_bImportMtfPossible)
+ {
+ const SdrGrafObj* pSdrGrafObj = dynamic_cast< const SdrGrafObj* >(pObj);
+ if (pSdrGrafObj != nullptr)
+ {
+ if ((pSdrGrafObj->HasGDIMetaFile() && !pSdrGrafObj->IsEPS()) ||
+ pSdrGrafObj->isEmbeddedVectorGraphicData())
+ {
+ m_bImportMtfPossible = true;
+ }
+ }
+
+ const SdrOle2Obj* pSdrOle2Obj = dynamic_cast< const SdrOle2Obj* >(pObj);
+ if (pSdrOle2Obj)
+ {
+ m_bImportMtfPossible = pSdrOle2Obj->GetObjRef().is();
+ }
+ }
+ }
+
+ m_bOneOrMoreMovable=nMovableCount!=0;
+ m_bGrpEnterPossible=m_bUnGroupPossible;
+ }
+ ImpCheckToTopBtmPossible();
+ static_cast<SdrPolyEditView*>(this)->ImpCheckPolyPossibilities();
+ m_bPossibilitiesDirty=false;
+
+ if (m_bReadOnly) {
+ bool bTemp=m_bGrpEnterPossible;
+ ImpResetPossibilityFlags();
+ m_bReadOnly=true;
+ m_bGrpEnterPossible=bTemp;
+ }
+ if (!m_bMoveAllowed) return;
+
+ // Don't allow moving glued connectors.
+ // Currently only implemented for single selection.
+ if (nMarkCount==1) {
+ SdrObject* pObj=GetMarkedObjectByIndex(0);
+ SdrEdgeObj* pEdge=dynamic_cast<SdrEdgeObj*>( pObj );
+ if (pEdge!=nullptr) {
+ SdrObject* pNode1=pEdge->GetConnectedNode(true);
+ SdrObject* pNode2=pEdge->GetConnectedNode(false);
+ if (pNode1!=nullptr || pNode2!=nullptr) m_bMoveAllowed=false;
+ }
+ }
+
+ // Don't allow enter Diagrams
+ if (1 == nMarkCount && m_bGrpEnterPossible)
+ {
+ SdrObject* pCandidate(GetMarkedObjectByIndex(0));
+
+ if(nullptr != pCandidate && pCandidate->isDiagram())
+ m_bGrpEnterPossible = false;
+ }
+}
+
+
+void SdrEditView::ForceMarkedObjToAnotherPage()
+{
+ bool bFlg=false;
+ for (size_t nm=0; nm<GetMarkedObjectCount(); ++nm) {
+ SdrMark* pM=GetSdrMarkByIndex(nm);
+ SdrObject* pObj=pM->GetMarkedSdrObj();
+ tools::Rectangle aObjRect(pObj->GetCurrentBoundRect());
+ tools::Rectangle aPgRect(pM->GetPageView()->GetPageRect());
+ if (!aObjRect.Overlaps(aPgRect)) {
+ bool bFnd=false;
+ SdrPageView* pPV = GetSdrPageView();
+
+ if(pPV)
+ {
+ bFnd = aObjRect.Overlaps(pPV->GetPageRect());
+ }
+
+ if(bFnd)
+ {
+ pM->GetPageView()->GetObjList()->RemoveObject(pObj->GetOrdNum());
+ pPV->GetObjList()->InsertObject(pObj, SAL_MAX_SIZE);
+ pM->SetPageView(pPV);
+ InvalidateAllWin(aObjRect);
+ bFlg=true;
+ }
+ }
+ }
+ if (bFlg) {
+ MarkListHasChanged();
+ }
+}
+
+std::vector<SdrObject*> SdrEditView::DeleteMarkedList(SdrMarkList const& rMark)
+{
+ std::vector<SdrObject*> ret;
+ if (rMark.GetMarkCount()!=0)
+ {
+ rMark.ForceSort();
+
+ const bool bUndo = IsUndoEnabled();
+ if( bUndo )
+ BegUndo();
+ const size_t nMarkCount(rMark.GetMarkCount());
+
+ if(nMarkCount)
+ {
+ std::vector< E3DModifySceneSnapRectUpdater* > aUpdaters;
+
+ if( bUndo )
+ {
+ for(size_t nm = nMarkCount; nm > 0;)
+ {
+ --nm;
+ SdrMark* pM = rMark.GetMark(nm);
+ SdrObject* pObj = pM->GetMarkedSdrObj();
+
+ // extra undo actions for changed connector which now may hold its laid out path (SJ)
+ AddUndoActions(CreateConnectorUndo( *pObj ));
+
+ AddUndo(GetModel()->GetSdrUndoFactory().CreateUndoDeleteObject(*pObj));
+ }
+ }
+
+ // make sure, OrderNums are correct:
+ rMark.GetMark(0)->GetMarkedSdrObj()->GetOrdNum();
+
+ for(size_t nm = nMarkCount; nm > 0;)
+ {
+ --nm;
+ SdrMark* pM = rMark.GetMark(nm);
+ SdrObject* pObj = pM->GetMarkedSdrObj();
+ SdrObjList* pOL = pObj->getParentSdrObjListFromSdrObject();
+ const size_t nOrdNum(pObj->GetOrdNumDirect());
+
+ bool bIs3D = dynamic_cast< E3dObject* >(pObj);
+ // set up a scene updater if object is a 3d object
+ if(bIs3D)
+ {
+ aUpdaters.push_back(new E3DModifySceneSnapRectUpdater(pObj));
+ }
+
+ pOL->RemoveObject(nOrdNum);
+
+ if( !bUndo )
+ {
+ // tdf#108863 don't delete objects before EndUndo()
+ ret.push_back(pObj);
+ }
+ }
+
+ // fire scene updaters
+ while(!aUpdaters.empty())
+ {
+ delete aUpdaters.back();
+ aUpdaters.pop_back();
+ }
+ }
+
+ if( bUndo )
+ EndUndo();
+ }
+ return ret;
+}
+
+static void lcl_LazyDelete(std::vector<SdrObject*> & rLazyDelete)
+{
+ // now delete removed scene objects
+ while (!rLazyDelete.empty())
+ {
+ SdrObject::Free( rLazyDelete.back() );
+ rLazyDelete.pop_back();
+ }
+}
+
+void SdrEditView::DeleteMarkedObj()
+{
+ // #i110981# return when nothing is to be done at all
+ if(!GetMarkedObjectCount())
+ {
+ return;
+ }
+
+ // moved breaking action and undo start outside loop
+ BrkAction();
+ BegUndo(SvxResId(STR_EditDelete),GetDescriptionOfMarkedObjects(),SdrRepeatFunc::Delete);
+
+ std::vector<SdrObject*> lazyDeleteObjects;
+ // remove as long as something is selected. This allows to schedule objects for
+ // removal for a next run as needed
+ while(GetMarkedObjectCount())
+ {
+ // vector to remember the parents which may be empty after object removal
+ std::vector< SdrObject* > aParents;
+
+ {
+ const SdrMarkList& rMarkList = GetMarkedObjectList();
+ const size_t nCount(rMarkList.GetMarkCount());
+
+ for(size_t a = 0; a < nCount; ++a)
+ {
+ // in the first run, add all found parents, but only once
+ SdrMark* pMark(rMarkList.GetMark(a));
+ SdrObject* pObject(pMark->GetMarkedSdrObj());
+ SdrObject* pParent(pObject->getParentSdrObjectFromSdrObject());
+
+ if(pParent)
+ {
+ if(!aParents.empty())
+ {
+ std::vector< SdrObject* >::iterator aFindResult =
+ std::find(aParents.begin(), aParents.end(), pParent);
+
+ if(aFindResult == aParents.end())
+ {
+ aParents.push_back(pParent);
+ }
+ }
+ else
+ {
+ aParents.push_back(pParent);
+ }
+ }
+ }
+
+ if(!aParents.empty())
+ {
+ // in a 2nd run, remove all objects which may already be scheduled for
+ // removal. I am not sure if this can happen, but theoretically
+ // a to-be-removed object may already be the group/3DScene itself
+ for(size_t a = 0; a < nCount; ++a)
+ {
+ SdrMark* pMark = rMarkList.GetMark(a);
+ SdrObject* pObject = pMark->GetMarkedSdrObj();
+
+ std::vector< SdrObject* >::iterator aFindResult =
+ std::find(aParents.begin(), aParents.end(), pObject);
+
+ if(aFindResult != aParents.end())
+ {
+ aParents.erase(aFindResult);
+ }
+ }
+ }
+ }
+
+ // original stuff: remove selected objects. Handle clear will
+ // do something only once
+ auto temp(DeleteMarkedList(GetMarkedObjectList()));
+ for (auto p : temp)
+ {
+ lazyDeleteObjects.push_back(p);
+ }
+ GetMarkedObjectListWriteAccess().Clear();
+ maHdlList.Clear();
+
+ while(!aParents.empty() && !GetMarkedObjectCount())
+ {
+ // iterate over remembered parents
+ SdrObject* pParent = aParents.back();
+ aParents.pop_back();
+
+ if(pParent->GetSubList() && 0 == pParent->GetSubList()->GetObjCount())
+ {
+ // we detected an empty parent, a candidate to leave group/3DScene
+ // if entered
+ if(GetSdrPageView()->GetCurrentGroup()
+ && GetSdrPageView()->GetCurrentGroup() == pParent)
+ {
+ GetSdrPageView()->LeaveOneGroup();
+ }
+
+ // schedule empty parent for removal
+ GetMarkedObjectListWriteAccess().InsertEntry(
+ SdrMark(pParent, GetSdrPageView()));
+ }
+ }
+ }
+
+ // end undo and change messaging moved at the end
+ EndUndo();
+ MarkListHasChanged();
+
+ lcl_LazyDelete(lazyDeleteObjects);
+}
+
+void SdrEditView::CopyMarkedObj()
+{
+ SortMarkedObjects();
+
+ SdrMarkList aSourceObjectsForCopy(GetMarkedObjectList());
+ // The following loop is used instead of MarkList::Merge(), to be
+ // able to flag the MarkEntries.
+ const size_t nEdgeCnt = GetEdgesOfMarkedNodes().GetMarkCount();
+ for (size_t nEdgeNum=0; nEdgeNum<nEdgeCnt; ++nEdgeNum) {
+ SdrMark aM(*GetEdgesOfMarkedNodes().GetMark(nEdgeNum));
+ aM.SetUser(1);
+ aSourceObjectsForCopy.InsertEntry(aM);
+ }
+ aSourceObjectsForCopy.ForceSort();
+
+ // #i13033#
+ // New mechanism to re-create the connections of cloned connectors
+ CloneList aCloneList;
+
+ const bool bUndo = IsUndoEnabled();
+
+ GetMarkedObjectListWriteAccess().Clear();
+ size_t nCloneErrCnt=0;
+ std::unordered_set<rtl::OUString> aNameSet;
+ const size_t nMarkCount=aSourceObjectsForCopy.GetMarkCount();
+ for (size_t nm=0; nm<nMarkCount; ++nm) {
+ SdrMark* pM=aSourceObjectsForCopy.GetMark(nm);
+ SdrObject* pSource(pM->GetMarkedSdrObj());
+ SdrObject* pO(pSource->CloneSdrObject(pSource->getSdrModelFromSdrObject()));
+ if (pO!=nullptr) {
+ pM->GetPageView()->GetObjList()->InsertObjectThenMakeNameUnique(pO, aNameSet);
+
+ if( bUndo )
+ AddUndo(GetModel()->GetSdrUndoFactory().CreateUndoCopyObject(*pO));
+
+ SdrMark aME(*pM);
+ aME.SetMarkedSdrObj(pO);
+ aCloneList.AddPair(pM->GetMarkedSdrObj(), pO);
+
+ if (pM->GetUser()==0)
+ {
+ // otherwise it is only an Edge we have to copy as well
+ GetMarkedObjectListWriteAccess().InsertEntry(aME);
+ }
+ } else {
+ nCloneErrCnt++;
+ }
+ }
+
+ // #i13033#
+ // New mechanism to re-create the connections of cloned connectors
+ aCloneList.CopyConnections();
+
+ if(nCloneErrCnt)
+ {
+#ifdef DBG_UTIL
+ OStringBuffer aStr("SdrEditView::CopyMarkedObj(): Error when cloning ");
+
+ if(nCloneErrCnt == 1)
+ {
+ aStr.append("a drawing object.");
+ }
+ else
+ {
+ aStr.append(static_cast<sal_Int32>(nCloneErrCnt));
+ aStr.append(" drawing objects.");
+ }
+
+ aStr.append(" This object's/These objects's connections will not be copied.");
+ OSL_FAIL(aStr.getStr());
+#endif
+ }
+ MarkListHasChanged();
+}
+
+
+bool SdrEditView::InsertObjectAtView(SdrObject* pObj, SdrPageView& rPV, SdrInsertFlags nOptions)
+{
+ if (nOptions & SdrInsertFlags::SETDEFLAYER) {
+ SdrLayerID nLayer=rPV.GetPage()->GetLayerAdmin().GetLayerID(maActualLayer);
+ if (nLayer==SDRLAYER_NOTFOUND) nLayer=SdrLayerID(0);
+ if (rPV.GetLockedLayers().IsSet(nLayer) || !rPV.GetVisibleLayers().IsSet(nLayer)) {
+ SdrObject::Free( pObj ); // Layer locked or invisible
+ return false;
+ }
+ pObj->NbcSetLayer(nLayer);
+ }
+ if (nOptions & SdrInsertFlags::SETDEFATTR) {
+ if (mpDefaultStyleSheet!=nullptr) pObj->NbcSetStyleSheet(mpDefaultStyleSheet, false);
+ pObj->SetMergedItemSet(maDefaultAttr);
+ }
+ if (!pObj->IsInserted()) {
+ rPV.GetObjList()->InsertObject(pObj, SAL_MAX_SIZE);
+ }
+ if( IsUndoEnabled())
+ {
+ bool bDontDeleteReally = true;
+ EndTextEditCurrentView(bDontDeleteReally);
+ AddUndo(GetModel()->GetSdrUndoFactory().CreateUndoNewObject(*pObj));
+ }
+
+ css::uno::Reference<lang::XServiceInfo> xServices(GetModel()->getUnoModel(),
+ css::uno::UNO_QUERY);
+ if (xServices.is() && (xServices->supportsService("com.sun.star.sheet.SpreadsheetDocument") ||
+ xServices->supportsService("com.sun.star.text.TextDocument")))
+ {
+ const bool bUndo(IsUndoEnabled());
+ GetModel()->EnableUndo(false);
+ pObj->MakeNameUnique();
+ GetModel()->EnableUndo(bUndo);
+ }
+
+ if (!(nOptions & SdrInsertFlags::DONTMARK)) {
+ if (!(nOptions & SdrInsertFlags::ADDMARK)) UnmarkAllObj();
+ MarkObj(pObj,&rPV);
+ }
+ return true;
+}
+
+void SdrEditView::ReplaceObjectAtView(SdrObject* pOldObj, SdrPageView& rPV, SdrObject* pNewObj, bool bMark)
+{
+ if(IsTextEdit())
+ {
+#ifdef DBG_UTIL
+ if(auto pTextObj = dynamic_cast< SdrTextObj* >(pOldObj))
+ if (pTextObj->IsTextEditActive())
+ OSL_ENSURE(false, "OldObject is in TextEdit mode, this has to be ended before replacing it using SdrEndTextEdit (!)");
+
+ if(auto pTextObj = dynamic_cast< SdrTextObj* >(pNewObj))
+ if (pTextObj->IsTextEditActive())
+ OSL_ENSURE(false, "NewObject is in TextEdit mode, this has to be ended before replacing it using SdrEndTextEdit (!)");
+#endif
+
+ // #i123468# emergency repair situation, needs to cast up to a class derived from
+ // this one; (aw080 has a mechanism for that and the view hierarchy is secured to
+ // always be a SdrView)
+ SdrView *pSdrView = dynamic_cast<SdrView*>(this);
+ if (pSdrView)
+ pSdrView->SdrEndTextEdit();
+ }
+
+ SdrObjList* pOL=pOldObj->getParentSdrObjListFromSdrObject();
+ const bool bUndo = IsUndoEnabled();
+ if( bUndo )
+ AddUndo(GetModel()->GetSdrUndoFactory().CreateUndoReplaceObject(*pOldObj,*pNewObj));
+
+ if( IsObjMarked( pOldObj ) )
+ MarkObj( pOldObj, &rPV, true /*unmark!*/ );
+
+ pOL->ReplaceObject(pNewObj,pOldObj->GetOrdNum());
+
+ if( !bUndo )
+ SdrObject::Free( pOldObj );
+
+ if (bMark) MarkObj(pNewObj,&rPV);
+}
+
+
+bool SdrEditView::IsUndoEnabled() const
+{
+ return mpModel->IsUndoEnabled();
+}
+
+void SdrEditView::EndTextEditAllViews() const
+{
+ size_t nViews = mpModel->GetListenerCount();
+ for (size_t nView = 0; nView < nViews; ++nView)
+ {
+ SdrObjEditView* pView = dynamic_cast<SdrObjEditView*>(mpModel->GetListener(nView));
+ if (!pView)
+ continue;
+
+ if (pView->IsTextEdit())
+ pView->SdrEndTextEdit();
+ }
+}
+
+void SdrEditView::EndTextEditCurrentView(bool bDontDeleteReally)
+{
+ if (IsTextEdit())
+ {
+ SdrView* pSdrView = dynamic_cast<SdrView*>(this);
+ if (pSdrView)
+ pSdrView->SdrEndTextEdit(bDontDeleteReally);
+ }
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/svdraw/svdedtv1.cxx b/svx/source/svdraw/svdedtv1.cxx
new file mode 100644
index 000000000..371584bd8
--- /dev/null
+++ b/svx/source/svdraw/svdedtv1.cxx
@@ -0,0 +1,2019 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <basegfx/matrix/b2dhommatrix.hxx>
+#include <basegfx/matrix/b2dhommatrixtools.hxx>
+#include <editeng/eeitem.hxx>
+#include <math.h>
+#include <svl/itemiter.hxx>
+#include <svl/whiter.hxx>
+#include <tools/bigint.hxx>
+#include <vcl/svapp.hxx>
+#include <vcl/weld.hxx>
+
+#include <getallcharpropids.hxx>
+#include <svx/dialmgr.hxx>
+#include <svx/svditer.hxx>
+#include <svx/strings.hrc>
+
+#include <AffineMatrixItem.hxx>
+#include <svx/e3dsceneupdater.hxx>
+#include <svx/obj3d.hxx>
+#include <svx/rectenum.hxx>
+#include <svx/sdr/contact/viewcontact.hxx>
+#include <svx/sdooitm.hxx>
+#include <svx/sderitm.hxx>
+#include <svx/sdtagitm.hxx>
+#include <svx/svdedtv.hxx>
+#include <svx/svdetc.hxx>
+#include <svx/svdopath.hxx>
+#include <svx/svdpage.hxx>
+#include <svx/svdpagv.hxx>
+#include <svx/svdtrans.hxx>
+#include <svx/svdundo.hxx>
+#include <svx/svxids.hrc>
+#include <sxallitm.hxx>
+#include <sxmovitm.hxx>
+#include <sxreaitm.hxx>
+#include <sxreoitm.hxx>
+#include <sxroaitm.hxx>
+#include <sxrooitm.hxx>
+#include <sxsalitm.hxx>
+#include <sxsoitm.hxx>
+#include <sxtraitm.hxx>
+#include <svx/xlnedwit.hxx>
+#include <svx/xlnstwit.hxx>
+#include <svx/xlnwtit.hxx>
+#include <svx/xlnclit.hxx>
+#include <svx/xflclit.hxx>
+#include <svx/xlntrit.hxx>
+#include <svx/xfltrit.hxx>
+#include <svx/sdprcitm.hxx>
+#include <LibreOfficeKit/LibreOfficeKitEnums.h>
+#include <rtl/ustring.hxx>
+#include <sfx2/viewsh.hxx>
+#include <comphelper/lok.hxx>
+#include <osl/diagnose.h>
+
+// EditView
+
+
+void SdrEditView::SetMarkedObjRect(const tools::Rectangle& rRect)
+{
+ DBG_ASSERT(!rRect.IsEmpty(),"SetMarkedObjRect() with an empty Rect does not make sense.");
+ if (rRect.IsEmpty()) return;
+ const size_t nCount=GetMarkedObjectCount();
+ if (nCount==0) return;
+ tools::Rectangle aR0(GetMarkedObjRect());
+ DBG_ASSERT(!aR0.IsEmpty(),"SetMarkedObjRect(): GetMarkedObjRect() is empty.");
+ if (aR0.IsEmpty()) return;
+ tools::Long x0=aR0.Left();
+ tools::Long y0=aR0.Top();
+ tools::Long w0=aR0.Right()-x0;
+ tools::Long h0=aR0.Bottom()-y0;
+ tools::Long x1=rRect.Left();
+ tools::Long y1=rRect.Top();
+ tools::Long w1=rRect.Right()-x1;
+ tools::Long h1=rRect.Bottom()-y1;
+
+ const bool bUndo = IsUndoEnabled();
+ if( bUndo )
+ {
+ EndTextEditCurrentView();
+ BegUndo(ImpGetDescriptionString(STR_EditPosSize));
+ }
+
+ for (size_t nm=0; nm<nCount; ++nm)
+ {
+ SdrMark* pM=GetSdrMarkByIndex(nm);
+ SdrObject* pO=pM->GetMarkedSdrObj();
+ if( bUndo )
+ AddUndo( GetModel()->GetSdrUndoFactory().CreateUndoGeoObject(*pO));
+
+ tools::Rectangle aR1(pO->GetSnapRect());
+ if (!aR1.IsEmpty())
+ {
+ if (aR1==aR0)
+ {
+ aR1=rRect;
+ }
+ else
+ { // transform aR1 to aR0 after rRect
+ aR1.Move(-x0,-y0);
+ BigInt l(aR1.Left());
+ BigInt r(aR1.Right());
+ BigInt t(aR1.Top());
+ BigInt b(aR1.Bottom());
+ if (w0!=0) {
+ l*=w1; l/=w0;
+ r*=w1; r/=w0;
+ } else {
+ l=0; r=w1;
+ }
+ if (h0!=0) {
+ t*=h1; t/=h0;
+ b*=h1; b/=h0;
+ } else {
+ t=0; b=h1;
+ }
+ aR1.SetLeft(tools::Long(l) );
+ aR1.SetRight(tools::Long(r) );
+ aR1.SetTop(tools::Long(t) );
+ aR1.SetBottom(tools::Long(b) );
+ aR1.Move(x1,y1);
+ }
+ pO->SetSnapRect(aR1);
+ } else {
+ OSL_FAIL("SetMarkedObjRect(): pObj->GetSnapRect() returns empty Rect");
+ }
+ }
+ if( bUndo )
+ EndUndo();
+}
+
+std::vector< std::unique_ptr<SdrUndoAction> > SdrEditView::CreateConnectorUndo( const SdrObject& rO )
+{
+ std::vector< std::unique_ptr<SdrUndoAction> > vUndoActions;
+
+ if ( rO.GetBroadcaster() )
+ {
+ const SdrPage* pPage = rO.getSdrPageFromSdrObject();
+ if ( pPage )
+ {
+ SdrObjListIter aIter(pPage, SdrIterMode::DeepWithGroups);
+ while( aIter.IsMore() )
+ {
+ SdrObject* pPartObj = aIter.Next();
+ if ( dynamic_cast<const SdrEdgeObj*>( pPartObj) != nullptr )
+ {
+ if ( ( pPartObj->GetConnectedNode( false ) == &rO ) ||
+ ( pPartObj->GetConnectedNode( true ) == &rO ) )
+ {
+ vUndoActions.push_back( GetModel()->GetSdrUndoFactory().CreateUndoGeoObject( *pPartObj ) );
+ }
+ }
+ }
+ }
+ }
+ return vUndoActions;
+}
+
+void SdrEditView::AddUndoActions( std::vector< std::unique_ptr<SdrUndoAction> > aUndoActions )
+{
+ for (auto & rAction : aUndoActions)
+ AddUndo( std::move(rAction) );
+}
+
+void SdrEditView::MoveMarkedObj(const Size& rSiz, bool bCopy)
+{
+ const bool bUndo = IsUndoEnabled();
+
+ if( bUndo )
+ {
+ EndTextEditCurrentView();
+ OUString aStr(SvxResId(STR_EditMove));
+ if (bCopy)
+ aStr += SvxResId(STR_EditWithCopy);
+ // needs its own UndoGroup because of its parameters
+ BegUndo(aStr,GetDescriptionOfMarkedObjects(),SdrRepeatFunc::Move);
+ }
+
+ if (bCopy)
+ CopyMarkedObj();
+
+ const size_t nMarkCount=GetMarkedObjectCount();
+ for (size_t nm=0; nm<nMarkCount; ++nm)
+ {
+ SdrMark* pM=GetSdrMarkByIndex(nm);
+ SdrObject* pO=pM->GetMarkedSdrObj();
+ if( bUndo )
+ {
+ AddUndoActions( CreateConnectorUndo( *pO ) );
+ AddUndo(GetModel()->GetSdrUndoFactory().CreateUndoMoveObject(*pO,rSiz));
+ }
+ pO->Move(rSiz);
+ }
+
+ if( bUndo )
+ EndUndo();
+}
+
+void SdrEditView::ResizeMarkedObj(const Point& rRef, const Fraction& xFact, const Fraction& yFact, bool bCopy)
+{
+ const bool bUndo = IsUndoEnabled();
+ if( bUndo )
+ {
+ EndTextEditCurrentView();
+ OUString aStr {ImpGetDescriptionString(STR_EditResize)};
+ if (bCopy)
+ aStr+=SvxResId(STR_EditWithCopy);
+ BegUndo(aStr);
+ }
+
+ if (bCopy)
+ CopyMarkedObj();
+
+ const size_t nMarkCount=GetMarkedObjectCount();
+ for (size_t nm=0; nm<nMarkCount; ++nm)
+ {
+ SdrMark* pM=GetSdrMarkByIndex(nm);
+ SdrObject* pO=pM->GetMarkedSdrObj();
+ if( bUndo )
+ {
+ AddUndoActions( CreateConnectorUndo( *pO ) );
+ AddUndo( GetModel()->GetSdrUndoFactory().CreateUndoGeoObject(*pO));
+ }
+ pO->Resize(rRef,xFact,yFact);
+ }
+
+ if( bUndo )
+ EndUndo();
+}
+void SdrEditView::ResizeMultMarkedObj(const Point& rRef,
+ const Fraction& xFact,
+ const Fraction& yFact,
+ const bool bWdh,
+ const bool bHgt)
+{
+ const bool bUndo = IsUndoEnabled();
+ if( bUndo )
+ {
+ EndTextEditCurrentView();
+ BegUndo(ImpGetDescriptionString(STR_EditResize));
+ }
+
+ const size_t nMarkCount=GetMarkedObjectCount();
+ for (size_t nm=0; nm<nMarkCount; ++nm)
+ {
+ SdrMark* pM=GetSdrMarkByIndex(nm);
+ SdrObject* pO=pM->GetMarkedSdrObj();
+ if( bUndo )
+ {
+ AddUndoActions( CreateConnectorUndo( *pO ) );
+ AddUndo( GetModel()->GetSdrUndoFactory().CreateUndoGeoObject(*pO));
+ }
+
+ Fraction aFrac(1,1);
+ if (bWdh && xFact.IsValid() && bHgt && yFact.IsValid())
+ pO->Resize(rRef, xFact, yFact);
+ else if (bWdh && xFact.IsValid())
+ pO->Resize(rRef, xFact, aFrac);
+ else if (bHgt && yFact.IsValid())
+ pO->Resize(rRef, aFrac, yFact);
+ }
+ if( bUndo )
+ EndUndo();
+}
+
+Degree100 SdrEditView::GetMarkedObjRotate() const
+{
+ Degree100 nRetval(0);
+
+ if(GetMarkedObjectCount())
+ {
+ SdrMark* pM = GetSdrMarkByIndex(0);
+ SdrObject* pO = pM->GetMarkedSdrObj();
+
+ nRetval = pO->GetRotateAngle();
+ }
+
+ return nRetval;
+}
+
+void SdrEditView::RotateMarkedObj(const Point& rRef, Degree100 nAngle, bool bCopy)
+{
+ const bool bUndo = IsUndoEnabled();
+ if( bUndo )
+ {
+ EndTextEditCurrentView();
+ OUString aStr {ImpGetDescriptionString(STR_EditRotate)};
+ if (bCopy) aStr+=SvxResId(STR_EditWithCopy);
+ BegUndo(aStr);
+ }
+
+ if (bCopy)
+ CopyMarkedObj();
+
+ double nSin = sin(toRadians(nAngle));
+ double nCos = cos(toRadians(nAngle));
+ const size_t nMarkCount(GetMarkedObjectCount());
+
+ if(nMarkCount)
+ {
+ std::vector< E3DModifySceneSnapRectUpdater* > aUpdaters;
+
+ for(size_t nm = 0; nm < nMarkCount; ++nm)
+ {
+ SdrMark* pM = GetSdrMarkByIndex(nm);
+ SdrObject* pO = pM->GetMarkedSdrObj();
+
+ if( bUndo )
+ {
+ // extra undo actions for changed connector which now may hold its laid out path (SJ)
+ AddUndoActions( CreateConnectorUndo( *pO ) );
+
+ AddUndo(GetModel()->GetSdrUndoFactory().CreateUndoGeoObject(*pO));
+ }
+
+ // set up a scene updater if object is a 3d object
+ if(dynamic_cast< E3dObject* >(pO))
+ {
+ aUpdaters.push_back(new E3DModifySceneSnapRectUpdater(pO));
+ }
+
+ pO->Rotate(rRef,nAngle,nSin,nCos);
+ }
+
+ // fire scene updaters
+ while(!aUpdaters.empty())
+ {
+ delete aUpdaters.back();
+ aUpdaters.pop_back();
+ }
+ }
+
+ if( bUndo )
+ EndUndo();
+}
+
+void SdrEditView::MirrorMarkedObj(const Point& rRef1, const Point& rRef2, bool bCopy)
+{
+ const bool bUndo = IsUndoEnabled();
+
+ if( bUndo )
+ {
+ EndTextEditCurrentView();
+ OUString aStr;
+ Point aDif(rRef2-rRef1);
+ if (aDif.X()==0)
+ aStr = ImpGetDescriptionString(STR_EditMirrorHori);
+ else if (aDif.Y()==0)
+ aStr = ImpGetDescriptionString(STR_EditMirrorVert);
+ else if (std::abs(aDif.X()) == std::abs(aDif.Y()))
+ aStr = ImpGetDescriptionString(STR_EditMirrorDiag);
+ else
+ aStr = ImpGetDescriptionString(STR_EditMirrorFree);
+ if (bCopy) aStr+=SvxResId(STR_EditWithCopy);
+ BegUndo(aStr);
+ }
+
+ if (bCopy)
+ CopyMarkedObj();
+
+ const size_t nMarkCount(GetMarkedObjectCount());
+
+ if(nMarkCount)
+ {
+ std::vector< E3DModifySceneSnapRectUpdater* > aUpdaters;
+
+ for(size_t nm = 0; nm < nMarkCount; ++nm)
+ {
+ SdrMark* pM = GetSdrMarkByIndex(nm);
+ SdrObject* pO = pM->GetMarkedSdrObj();
+
+ if( bUndo )
+ {
+ // extra undo actions for changed connector which now may hold its laid out path (SJ)
+ AddUndoActions( CreateConnectorUndo( *pO ) );
+
+ AddUndo( GetModel()->GetSdrUndoFactory().CreateUndoGeoObject(*pO));
+ }
+
+ // set up a scene updater if object is a 3d object
+ if(dynamic_cast< E3dObject* >(pO))
+ {
+ aUpdaters.push_back(new E3DModifySceneSnapRectUpdater(pO));
+ }
+
+ pO->Mirror(rRef1,rRef2);
+ }
+
+ // fire scene updaters
+ while(!aUpdaters.empty())
+ {
+ delete aUpdaters.back();
+ aUpdaters.pop_back();
+ }
+ }
+
+ if( bUndo )
+ EndUndo();
+}
+
+void SdrEditView::MirrorMarkedObjHorizontal()
+{
+ Point aCenter(GetMarkedObjRect().Center());
+ Point aPt2(aCenter);
+ aPt2.AdjustY( 1 );
+ MirrorMarkedObj(aCenter,aPt2);
+}
+
+void SdrEditView::MirrorMarkedObjVertical()
+{
+ Point aCenter(GetMarkedObjRect().Center());
+ Point aPt2(aCenter);
+ aPt2.AdjustX( 1 );
+ MirrorMarkedObj(aCenter,aPt2);
+}
+
+Degree100 SdrEditView::GetMarkedObjShear() const
+{
+ bool b1st=true;
+ bool bOk=true;
+ Degree100 nAngle(0);
+ const size_t nMarkCount=GetMarkedObjectCount();
+ for (size_t nm=0; nm<nMarkCount && bOk; ++nm) {
+ SdrMark* pM=GetSdrMarkByIndex(nm);
+ SdrObject* pO=pM->GetMarkedSdrObj();
+ Degree100 nAngle2=pO->GetShearAngle();
+ if (b1st) nAngle=nAngle2;
+ else if (nAngle2!=nAngle) bOk=false;
+ b1st=false;
+ }
+ if (nAngle>SDRMAXSHEAR) nAngle=SDRMAXSHEAR;
+ if (nAngle<-SDRMAXSHEAR) nAngle=-SDRMAXSHEAR;
+ if (!bOk) nAngle=0_deg100;
+ return nAngle;
+}
+
+void SdrEditView::ShearMarkedObj(const Point& rRef, Degree100 nAngle, bool bVShear, bool bCopy)
+{
+ const bool bUndo = IsUndoEnabled();
+
+ if( bUndo )
+ {
+ EndTextEditCurrentView();
+ OUString aStr {ImpGetDescriptionString(STR_EditShear)};
+ if (bCopy)
+ aStr+=SvxResId(STR_EditWithCopy);
+ BegUndo(aStr);
+ }
+
+ if (bCopy)
+ CopyMarkedObj();
+
+ double nTan = tan(toRadians(nAngle));
+ const size_t nMarkCount=GetMarkedObjectCount();
+ for (size_t nm=0; nm<nMarkCount; ++nm)
+ {
+ SdrMark* pM=GetSdrMarkByIndex(nm);
+ SdrObject* pO=pM->GetMarkedSdrObj();
+ if( bUndo )
+ {
+ AddUndoActions( CreateConnectorUndo( *pO ) );
+ AddUndo( GetModel()->GetSdrUndoFactory().CreateUndoGeoObject(*pO));
+ }
+ pO->Shear(rRef,nAngle,nTan,bVShear);
+ }
+
+ if( bUndo )
+ EndUndo();
+}
+
+void SdrEditView::ImpCrookObj(SdrObject* pO, const Point& rRef, const Point& rRad,
+ SdrCrookMode eMode, bool bVertical, bool bNoContortion, bool bRotate, const tools::Rectangle& rMarkRect)
+{
+ SdrPathObj* pPath=dynamic_cast<SdrPathObj*>( pO );
+ bool bDone = false;
+
+ if(pPath!=nullptr && !bNoContortion)
+ {
+ XPolyPolygon aXPP(pPath->GetPathPoly());
+ switch (eMode) {
+ case SdrCrookMode::Rotate : CrookRotatePoly (aXPP,rRef,rRad,bVertical); break;
+ case SdrCrookMode::Slant : CrookSlantPoly (aXPP,rRef,rRad,bVertical); break;
+ case SdrCrookMode::Stretch: CrookStretchPoly(aXPP,rRef,rRad,bVertical,rMarkRect); break;
+ } // switch
+ pPath->SetPathPoly(aXPP.getB2DPolyPolygon());
+ bDone = true;
+ }
+
+ if(!bDone && !pPath && pO->IsPolyObj() && 0 != pO->GetPointCount())
+ {
+ // for PolyObj's, but NOT for SdrPathObj's, e.g. the measurement object
+ sal_uInt32 nPointCount(pO->GetPointCount());
+ XPolygon aXP(static_cast<sal_uInt16>(nPointCount));
+ sal_uInt32 nPtNum;
+
+ for(nPtNum = 0; nPtNum < nPointCount; nPtNum++)
+ {
+ Point aPt(pO->GetPoint(nPtNum));
+ aXP[static_cast<sal_uInt16>(nPtNum)]=aPt;
+ }
+
+ switch (eMode)
+ {
+ case SdrCrookMode::Rotate : CrookRotatePoly (aXP,rRef,rRad,bVertical); break;
+ case SdrCrookMode::Slant : CrookSlantPoly (aXP,rRef,rRad,bVertical); break;
+ case SdrCrookMode::Stretch: CrookStretchPoly(aXP,rRef,rRad,bVertical,rMarkRect); break;
+ }
+
+ for(nPtNum = 0; nPtNum < nPointCount; nPtNum++)
+ {
+ // broadcasting could be optimized here, but for the
+ // current two points of the measurement object, it's fine
+ pO->SetPoint(aXP[static_cast<sal_uInt16>(nPtNum)],nPtNum);
+ }
+
+ bDone = true;
+ }
+
+ if(bDone)
+ return;
+
+ // for all others or if bNoContortion
+ Point aCtr0(pO->GetSnapRect().Center());
+ Point aCtr1(aCtr0);
+ bool bRotOk(false);
+ double nSin(0.0), nCos(1.0);
+ double nAngle(0.0);
+
+ if(0 != rRad.X() && 0 != rRad.Y())
+ {
+ bRotOk = bRotate;
+
+ switch (eMode)
+ {
+ case SdrCrookMode::Rotate : nAngle=CrookRotateXPoint (aCtr1,nullptr,nullptr,rRef,rRad,nSin,nCos,bVertical); bRotOk=bRotate; break;
+ case SdrCrookMode::Slant : nAngle=CrookSlantXPoint (aCtr1,nullptr,nullptr,rRef,rRad,nSin,nCos,bVertical); break;
+ case SdrCrookMode::Stretch: nAngle=CrookStretchXPoint(aCtr1,nullptr,nullptr,rRef,rRad,nSin,nCos,bVertical,rMarkRect); break;
+ }
+ }
+
+ aCtr1 -= aCtr0;
+
+ if(bRotOk)
+ pO->Rotate(aCtr0, Degree100(FRound(basegfx::rad2deg<100>(nAngle))), nSin, nCos);
+
+ pO->Move(Size(aCtr1.X(),aCtr1.Y()));
+}
+
+void SdrEditView::CrookMarkedObj(const Point& rRef, const Point& rRad, SdrCrookMode eMode,
+ bool bVertical, bool bNoContortion, bool bCopy)
+{
+ tools::Rectangle aMarkRect(GetMarkedObjRect());
+ const bool bUndo = IsUndoEnabled();
+
+ bool bRotate=bNoContortion && eMode==SdrCrookMode::Rotate && IsRotateAllowed();
+
+ if( bUndo )
+ {
+ EndTextEditCurrentView();
+ OUString aStr {ImpGetDescriptionString(bNoContortion ? STR_EditCrook : STR_EditCrookContortion)};
+ if (bCopy)
+ aStr+=SvxResId(STR_EditWithCopy);
+ BegUndo(aStr);
+ }
+
+ if (bCopy)
+ CopyMarkedObj();
+
+ const size_t nMarkCount=GetMarkedObjectCount();
+ for (size_t nm=0; nm<nMarkCount; ++nm)
+ {
+ SdrMark* pM=GetSdrMarkByIndex(nm);
+ SdrObject* pO=pM->GetMarkedSdrObj();
+ if( bUndo )
+ AddUndo( GetModel()->GetSdrUndoFactory().CreateUndoGeoObject(*pO));
+
+ const SdrObjList* pOL=pO->GetSubList();
+ if (bNoContortion || pOL==nullptr) {
+ ImpCrookObj(pO,rRef,rRad,eMode,bVertical,bNoContortion,bRotate,aMarkRect);
+ } else {
+ SdrObjListIter aIter(pOL,SdrIterMode::DeepNoGroups);
+ while (aIter.IsMore()) {
+ SdrObject* pO1=aIter.Next();
+ ImpCrookObj(pO1,rRef,rRad,eMode,bVertical,bNoContortion,bRotate,aMarkRect);
+ }
+ }
+ }
+
+ if( bUndo )
+ EndUndo();
+}
+
+void SdrEditView::ImpDistortObj(SdrObject* pO, const tools::Rectangle& rRef, const XPolygon& rDistortedRect, bool bNoContortion)
+{
+ SdrPathObj* pPath = dynamic_cast<SdrPathObj*>( pO );
+
+ if(!bNoContortion && pPath)
+ {
+ XPolyPolygon aXPP(pPath->GetPathPoly());
+ aXPP.Distort(rRef, rDistortedRect);
+ pPath->SetPathPoly(aXPP.getB2DPolyPolygon());
+ }
+ else if(pO->IsPolyObj())
+ {
+ // e. g. for the measurement object
+ sal_uInt32 nPointCount(pO->GetPointCount());
+ XPolygon aXP(static_cast<sal_uInt16>(nPointCount));
+ sal_uInt32 nPtNum;
+
+ for(nPtNum = 0; nPtNum < nPointCount; nPtNum++)
+ {
+ Point aPt(pO->GetPoint(nPtNum));
+ aXP[static_cast<sal_uInt16>(nPtNum)]=aPt;
+ }
+
+ aXP.Distort(rRef, rDistortedRect);
+
+ for(nPtNum = 0; nPtNum < nPointCount; nPtNum++)
+ {
+ // broadcasting could be optimized here, but for the
+ // current two points of the measurement object it's fine
+ pO->SetPoint(aXP[static_cast<sal_uInt16>(nPtNum)],nPtNum);
+ }
+ }
+}
+
+void SdrEditView::DistortMarkedObj(const tools::Rectangle& rRef, const XPolygon& rDistortedRect, bool bNoContortion, bool bCopy)
+{
+ const bool bUndo = IsUndoEnabled();
+
+ if( bUndo )
+ {
+ EndTextEditCurrentView();
+ OUString aStr {ImpGetDescriptionString(STR_EditDistort)};
+ if (bCopy)
+ aStr+=SvxResId(STR_EditWithCopy);
+ BegUndo(aStr);
+ }
+
+ if (bCopy)
+ CopyMarkedObj();
+
+ const size_t nMarkCount=GetMarkedObjectCount();
+ for (size_t nm=0; nm<nMarkCount; ++nm)
+ {
+ SdrMark* pM=GetSdrMarkByIndex(nm);
+ SdrObject* pO=pM->GetMarkedSdrObj();
+ if( bUndo )
+ AddUndo( GetModel()->GetSdrUndoFactory().CreateUndoGeoObject(*pO));
+
+ tools::Rectangle aRefRect(rRef);
+ const SdrObjList* pOL=pO->GetSubList();
+ if (bNoContortion || pOL==nullptr) {
+ ImpDistortObj(pO,aRefRect,rDistortedRect,bNoContortion);
+ } else {
+ SdrObjListIter aIter(pOL,SdrIterMode::DeepNoGroups);
+ while (aIter.IsMore()) {
+ SdrObject* pO1=aIter.Next();
+ ImpDistortObj(pO1,aRefRect,rDistortedRect,bNoContortion);
+ }
+ }
+ }
+ if( bUndo )
+ EndUndo();
+}
+
+
+void SdrEditView::SetNotPersistAttrToMarked(const SfxItemSet& rAttr)
+{
+ // bReplaceAll has no effect here
+ tools::Rectangle aAllSnapRect(GetMarkedObjRect());
+ if (const SdrTransformRef1XItem *pPoolItem = rAttr.GetItemIfSet(SDRATTR_TRANSFORMREF1X))
+ {
+ tools::Long n = pPoolItem->GetValue();
+ SetRef1(Point(n,GetRef1().Y()));
+ }
+ if (const SdrTransformRef1YItem *pPoolItem = rAttr.GetItemIfSet(SDRATTR_TRANSFORMREF1Y))
+ {
+ tools::Long n = pPoolItem->GetValue();
+ SetRef1(Point(GetRef1().X(),n));
+ }
+ if (const SdrTransformRef2XItem *pPoolItem = rAttr.GetItemIfSet(SDRATTR_TRANSFORMREF2X))
+ {
+ tools::Long n = pPoolItem->GetValue();
+ SetRef2(Point(n,GetRef2().Y()));
+ }
+ if (const SdrTransformRef2YItem *pPoolItem = rAttr.GetItemIfSet(SDRATTR_TRANSFORMREF2Y))
+ {
+ tools::Long n = pPoolItem->GetValue();
+ SetRef2(Point(GetRef2().X(),n));
+ }
+ tools::Long nAllPosX=0; bool bAllPosX=false;
+ tools::Long nAllPosY=0; bool bAllPosY=false;
+ tools::Long nAllWdt=0; bool bAllWdt=false;
+ tools::Long nAllHgt=0; bool bAllHgt=false;
+ bool bDoIt=false;
+ if (const SdrAllPositionXItem *pPoolItem = rAttr.GetItemIfSet(SDRATTR_ALLPOSITIONX))
+ {
+ nAllPosX = pPoolItem->GetValue();
+ bAllPosX=true; bDoIt=true;
+ }
+ if (const SdrAllPositionYItem *pPoolItem = rAttr.GetItemIfSet(SDRATTR_ALLPOSITIONY))
+ {
+ nAllPosY = pPoolItem->GetValue();
+ bAllPosY=true; bDoIt=true;
+ }
+ if (const SdrAllSizeWidthItem *pPoolItem = rAttr.GetItemIfSet(SDRATTR_ALLSIZEWIDTH))
+ {
+ nAllWdt = pPoolItem->GetValue();
+ bAllWdt=true; bDoIt=true;
+ }
+ if (const SdrAllSizeHeightItem *pPoolItem = rAttr.GetItemIfSet(SDRATTR_ALLSIZEHEIGHT))
+ {
+ nAllHgt = pPoolItem->GetValue();
+ bAllHgt=true; bDoIt=true;
+ }
+ if (bDoIt) {
+ tools::Rectangle aRect(aAllSnapRect); // TODO: change this for PolyPt's and GluePt's!!!
+ if (bAllPosX) aRect.Move(nAllPosX-aRect.Left(),0);
+ if (bAllPosY) aRect.Move(0,nAllPosY-aRect.Top());
+ if (bAllWdt) aRect.SetRight(aAllSnapRect.Left()+nAllWdt );
+ if (bAllHgt) aRect.SetBottom(aAllSnapRect.Top()+nAllHgt );
+ SetMarkedObjRect(aRect);
+ }
+ if (const SdrResizeXAllItem *pPoolItem = rAttr.GetItemIfSet(SDRATTR_RESIZEXALL))
+ {
+ Fraction aXFact = pPoolItem->GetValue();
+ ResizeMarkedObj(aAllSnapRect.TopLeft(),aXFact,Fraction(1,1));
+ }
+ if (const SdrResizeYAllItem *pPoolItem = rAttr.GetItemIfSet(SDRATTR_RESIZEYALL))
+ {
+ Fraction aYFact = pPoolItem->GetValue();
+ ResizeMarkedObj(aAllSnapRect.TopLeft(),Fraction(1,1),aYFact);
+ }
+ if (const SdrRotateAllItem *pPoolItem = rAttr.GetItemIfSet(SDRATTR_ROTATEALL))
+ {
+ Degree100 nAngle = pPoolItem->GetValue();
+ RotateMarkedObj(aAllSnapRect.Center(),nAngle);
+ }
+ if (const SdrHorzShearAllItem *pPoolItem = rAttr.GetItemIfSet(SDRATTR_HORZSHEARALL))
+ {
+ Degree100 nAngle = pPoolItem->GetValue();
+ ShearMarkedObj(aAllSnapRect.Center(),nAngle);
+ }
+ if (const SdrVertShearAllItem *pPoolItem = rAttr.GetItemIfSet(SDRATTR_VERTSHEARALL))
+ {
+ Degree100 nAngle = pPoolItem->GetValue();
+ ShearMarkedObj(aAllSnapRect.Center(),nAngle,true);
+ }
+
+ const bool bUndo = IsUndoEnabled();
+
+ // TODO: check if WhichRange is necessary.
+ const size_t nMarkCount=GetMarkedObjectCount();
+ for (size_t nm=0; nm<nMarkCount; ++nm)
+ {
+ const SdrMark* pM=GetSdrMarkByIndex(nm);
+ SdrObject* pObj=pM->GetMarkedSdrObj();
+ if( bUndo )
+ AddUndo( GetModel()->GetSdrUndoFactory().CreateUndoGeoObject(*pObj));
+
+ pObj->ApplyNotPersistAttr(rAttr);
+ }
+}
+
+void SdrEditView::MergeNotPersistAttrFromMarked(SfxItemSet& rAttr) const
+{
+ // TODO: Take into account the origin and PvPos.
+ tools::Rectangle aAllSnapRect(GetMarkedObjRect()); // TODO: change this for PolyPt's and GluePt's!!!
+ tools::Long nAllSnapPosX=aAllSnapRect.Left();
+ tools::Long nAllSnapPosY=aAllSnapRect.Top();
+ tools::Long nAllSnapWdt=aAllSnapRect.GetWidth()-1;
+ tools::Long nAllSnapHgt=aAllSnapRect.GetHeight()-1;
+ // TODO: could go into CheckPossibilities
+ bool bMovProtect = false, bMovProtectDC = false;
+ bool bSizProtect = false, bSizProtectDC = false;
+ bool bPrintable = true, bPrintableDC = false;
+ bool bVisible = true, bVisibleDC = false;
+ SdrLayerID nLayerId(0);
+ bool bLayerDC=false;
+ tools::Long nSnapPosX=0; bool bSnapPosXDC=false;
+ tools::Long nSnapPosY=0; bool bSnapPosYDC=false;
+ tools::Long nSnapWdt=0; bool bSnapWdtDC=false;
+ tools::Long nSnapHgt=0; bool bSnapHgtDC=false;
+ tools::Long nLogicWdt=0; bool bLogicWdtDC=false,bLogicWdtDiff=false;
+ tools::Long nLogicHgt=0; bool bLogicHgtDC=false,bLogicHgtDiff=false;
+ Degree100 nRotAngle(0); bool bRotAngleDC=false;
+ Degree100 nShrAngle(0); bool bShrAngleDC=false;
+ tools::Rectangle aSnapRect;
+ tools::Rectangle aLogicRect;
+ const size_t nMarkCount=GetMarkedObjectCount();
+ for (size_t nm=0; nm<nMarkCount; ++nm) {
+ const SdrMark* pM=GetSdrMarkByIndex(nm);
+ const SdrObject* pObj=pM->GetMarkedSdrObj();
+ if (nm==0) {
+ nLayerId=pObj->GetLayer();
+ bMovProtect=pObj->IsMoveProtect();
+ bSizProtect=pObj->IsResizeProtect();
+ bPrintable =pObj->IsPrintable();
+ bVisible = pObj->IsVisible();
+ tools::Rectangle aSnapRect2(pObj->GetSnapRect());
+ tools::Rectangle aLogicRect2(pObj->GetLogicRect());
+ nSnapPosX=aSnapRect2.Left();
+ nSnapPosY=aSnapRect2.Top();
+ nSnapWdt=aSnapRect2.GetWidth()-1;
+ nSnapHgt=aSnapRect2.GetHeight()-1;
+ nLogicWdt=aLogicRect2.GetWidth()-1;
+ nLogicHgt=aLogicRect2.GetHeight()-1;
+ bLogicWdtDiff=nLogicWdt!=nSnapWdt;
+ bLogicHgtDiff=nLogicHgt!=nSnapHgt;
+ nRotAngle=pObj->GetRotateAngle();
+ nShrAngle=pObj->GetShearAngle();
+ } else {
+ if (!bLayerDC && nLayerId !=pObj->GetLayer()) bLayerDC = true;
+ if (!bMovProtectDC && bMovProtect!=pObj->IsMoveProtect()) bMovProtectDC = true;
+ if (!bSizProtectDC && bSizProtect!=pObj->IsResizeProtect()) bSizProtectDC = true;
+ if (!bPrintableDC && bPrintable !=pObj->IsPrintable()) bPrintableDC = true;
+ if (!bVisibleDC && bVisible !=pObj->IsVisible()) bVisibleDC=true;
+ if (!bRotAngleDC && nRotAngle !=pObj->GetRotateAngle()) bRotAngleDC=true;
+ if (!bShrAngleDC && nShrAngle !=pObj->GetShearAngle()) bShrAngleDC=true;
+ if (!bSnapWdtDC || !bSnapHgtDC || !bSnapPosXDC || !bSnapPosYDC || !bLogicWdtDiff || !bLogicHgtDiff) {
+ aSnapRect=pObj->GetSnapRect();
+ if (nSnapPosX!=aSnapRect.Left()) bSnapPosXDC=true;
+ if (nSnapPosY!=aSnapRect.Top()) bSnapPosYDC=true;
+ if (nSnapWdt!=aSnapRect.GetWidth()-1) bSnapWdtDC=true;
+ if (nSnapHgt!=aSnapRect.GetHeight()-1) bSnapHgtDC=true;
+ }
+ if (!bLogicWdtDC || !bLogicHgtDC || !bLogicWdtDiff || !bLogicHgtDiff) {
+ aLogicRect=pObj->GetLogicRect();
+ if (nLogicWdt!=aLogicRect.GetWidth()-1) bLogicWdtDC=true;
+ if (nLogicHgt!=aLogicRect.GetHeight()-1) bLogicHgtDC=true;
+ if (!bLogicWdtDiff && aSnapRect.GetWidth()!=aLogicRect.GetWidth()) bLogicWdtDiff=true;
+ if (!bLogicHgtDiff && aSnapRect.GetHeight()!=aLogicRect.GetHeight()) bLogicHgtDiff=true;
+ }
+ }
+ }
+
+ if (bSnapPosXDC || nAllSnapPosX!=nSnapPosX) rAttr.Put(SdrAllPositionXItem(nAllSnapPosX));
+ if (bSnapPosYDC || nAllSnapPosY!=nSnapPosY) rAttr.Put(SdrAllPositionYItem(nAllSnapPosY));
+ if (bSnapWdtDC || nAllSnapWdt !=nSnapWdt ) rAttr.Put(SdrAllSizeWidthItem(nAllSnapWdt));
+ if (bSnapHgtDC || nAllSnapHgt !=nSnapHgt ) rAttr.Put(SdrAllSizeHeightItem(nAllSnapHgt));
+
+ // items for pure transformations
+ rAttr.Put(SdrMoveXItem());
+ rAttr.Put(SdrMoveYItem());
+ rAttr.Put(SdrResizeXOneItem());
+ rAttr.Put(SdrResizeYOneItem());
+ rAttr.Put(SdrRotateOneItem());
+ rAttr.Put(SdrHorzShearOneItem());
+ rAttr.Put(SdrVertShearOneItem());
+
+ if (nMarkCount>1) {
+ rAttr.Put(SdrResizeXAllItem());
+ rAttr.Put(SdrResizeYAllItem());
+ rAttr.Put(SdrRotateAllItem());
+ rAttr.Put(SdrHorzShearAllItem());
+ rAttr.Put(SdrVertShearAllItem());
+ }
+
+ if(meDragMode == SdrDragMode::Rotate || meDragMode == SdrDragMode::Mirror)
+ {
+ rAttr.Put(SdrTransformRef1XItem(GetRef1().X()));
+ rAttr.Put(SdrTransformRef1YItem(GetRef1().Y()));
+ }
+
+ if(meDragMode == SdrDragMode::Mirror)
+ {
+ rAttr.Put(SdrTransformRef2XItem(GetRef2().X()));
+ rAttr.Put(SdrTransformRef2YItem(GetRef2().Y()));
+ }
+}
+
+SfxItemSet SdrEditView::GetAttrFromMarked(bool bOnlyHardAttr) const
+{
+ SfxItemSet aSet(mpModel->GetItemPool());
+ MergeAttrFromMarked(aSet,bOnlyHardAttr);
+ //the EE_FEATURE items should not be set with SetAttrToMarked (see error message there)
+ //so we do not set them here
+ // #i32448#
+ // Do not disable, but clear the items.
+ aSet.ClearItem(EE_FEATURE_TAB);
+ aSet.ClearItem(EE_FEATURE_LINEBR);
+ aSet.ClearItem(EE_FEATURE_NOTCONV);
+ aSet.ClearItem(EE_FEATURE_FIELD);
+
+ return aSet;
+}
+
+void SdrEditView::MergeAttrFromMarked(SfxItemSet& rAttr, bool bOnlyHardAttr) const
+{
+ const size_t nMarkCount(GetMarkedObjectCount());
+
+ for(size_t a = 0; a < nMarkCount; ++a)
+ {
+ // #80277# merging was done wrong in the prev version
+ SdrObject *pObj = GetMarkedObjectByIndex(a);
+ if (!pObj)
+ {
+ continue;
+ }
+
+ const SfxItemSet& rSet = pObj->GetMergedItemSet();
+ SfxWhichIter aIter(rSet);
+ sal_uInt16 nWhich(aIter.FirstWhich());
+
+ while(nWhich)
+ {
+ if(!bOnlyHardAttr)
+ {
+ if(SfxItemState::DONTCARE == aIter.GetItemState(false))
+ rAttr.InvalidateItem(nWhich);
+ else
+ rAttr.MergeValue(rSet.Get(nWhich), true);
+ }
+ else if(SfxItemState::SET == aIter.GetItemState(false))
+ {
+ const SfxPoolItem& rItem = rSet.Get(nWhich);
+ rAttr.MergeValue(rItem, true);
+ }
+
+ if (comphelper::LibreOfficeKit::isActive())
+ {
+ OUString sPayload;
+ switch(nWhich)
+ {
+ case XATTR_LINECOLOR:
+ {
+ const SfxPoolItem* pItem = rSet.GetItem(XATTR_LINECOLOR);
+ if (pItem)
+ {
+ Color aColor = static_cast<const XLineColorItem*>(pItem)->GetColorValue();
+ sPayload = OUString::number(static_cast<sal_uInt32>(aColor));
+
+ sPayload = ".uno:XLineColor=" + sPayload;
+ }
+ break;
+ }
+
+ case XATTR_FILLCOLOR:
+ {
+ const SfxPoolItem* pItem = rSet.GetItem(XATTR_FILLCOLOR);
+ if (pItem)
+ {
+ Color aColor = static_cast<const XFillColorItem*>(pItem)->GetColorValue();
+ sPayload = OUString::number(static_cast<sal_uInt32>(aColor));
+
+ sPayload = ".uno:FillColor=" + sPayload;
+ }
+ break;
+ }
+
+ case XATTR_FILLTRANSPARENCE:
+ {
+ const SfxPoolItem* pItem = rSet.GetItem(XATTR_FILLTRANSPARENCE);
+ if (pItem)
+ {
+ sal_uInt16 nTransparency = static_cast<const SfxUInt16Item*>(pItem)->GetValue();
+ sPayload = OUString::number(nTransparency);
+
+ sPayload = ".uno:FillTransparence=" + sPayload;
+ }
+ break;
+ }
+
+ case XATTR_LINETRANSPARENCE:
+ {
+ const SfxPoolItem* pItem = rSet.GetItem(XATTR_LINETRANSPARENCE);
+ if (pItem)
+ {
+ sal_uInt16 nTransparency = static_cast<const SfxUInt16Item*>(pItem)->GetValue();
+ sPayload = OUString::number(nTransparency);
+
+ sPayload = ".uno:LineTransparence=" + sPayload;
+ }
+ break;
+ }
+
+ case XATTR_LINEWIDTH:
+ {
+ const SfxPoolItem* pItem = rSet.GetItem(XATTR_LINEWIDTH);
+ if (pItem)
+ {
+ sal_uInt32 nWidth = static_cast<const XLineWidthItem*>(pItem)->GetValue();
+ sPayload = OUString::number(nWidth);
+
+ sPayload = ".uno:LineWidth=" + sPayload;
+ }
+ break;
+ }
+
+ case SDRATTR_SHADOWTRANSPARENCE:
+ {
+ const SfxPoolItem* pItem = rSet.GetItem(SDRATTR_SHADOWTRANSPARENCE);
+ if (pItem)
+ {
+ sal_uInt16 nWidth = static_cast<const SfxUInt16Item*>(pItem)->GetValue();
+ sPayload = OUString::number(nWidth);
+
+ sPayload = ".uno:FillShadowTransparency=" + sPayload;
+ }
+ break;
+ }
+ }
+
+ if (!sPayload.isEmpty())
+ GetSfxViewShell()->libreOfficeKitViewCallback(LOK_CALLBACK_STATE_CHANGED,
+ OUStringToOString(sPayload, RTL_TEXTENCODING_ASCII_US).getStr());
+ }
+
+ nWhich = aIter.NextWhich();
+ }
+ }
+}
+
+std::vector<sal_uInt16> GetAllCharPropIds(const SfxItemSet& rSet)
+{
+ std::vector<sal_uInt16> aCharWhichIds;
+ {
+ SfxItemIter aIter(rSet);
+ for (const SfxPoolItem* pItem = aIter.GetCurItem(); pItem; pItem = aIter.NextItem())
+ {
+ if (!IsInvalidItem(pItem))
+ {
+ sal_uInt16 nWhich = pItem->Which();
+ if (nWhich>=EE_CHAR_START && nWhich<=EE_CHAR_END)
+ aCharWhichIds.push_back( nWhich );
+ }
+ }
+ }
+ return aCharWhichIds;
+}
+
+std::vector<sal_uInt16> GetAllCharPropIds(o3tl::span< const SfxPoolItem* const > aChangedItems)
+{
+ std::vector<sal_uInt16> aCharWhichIds;
+ for (const SfxPoolItem* pItem : aChangedItems)
+ {
+ sal_uInt16 nWhich = pItem->Which();
+ if (nWhich>=EE_CHAR_START && nWhich<=EE_CHAR_END)
+ aCharWhichIds.push_back( nWhich );
+ }
+ return aCharWhichIds;
+}
+
+void SdrEditView::SetAttrToMarked(const SfxItemSet& rAttr, bool bReplaceAll)
+{
+ if (!AreObjectsMarked())
+ return;
+
+#ifdef DBG_UTIL
+ {
+ bool bHasEEFeatureItems=false;
+ SfxItemIter aIter(rAttr);
+ for (const SfxPoolItem* pItem = aIter.GetCurItem(); !bHasEEFeatureItems && pItem;
+ pItem = aIter.NextItem())
+ {
+ if (!IsInvalidItem(pItem)) {
+ sal_uInt16 nW=pItem->Which();
+ if (nW>=EE_FEATURE_START && nW<=EE_FEATURE_END) bHasEEFeatureItems=true;
+ }
+ }
+ if(bHasEEFeatureItems)
+ {
+ std::unique_ptr<weld::MessageDialog> xInfoBox(Application::CreateMessageDialog(nullptr,
+ VclMessageType::Info, VclButtonsType::Ok,
+ "SdrEditView::SetAttrToMarked(): Setting EE_FEATURE items at the SdrView does not make sense! It only leads to overhead and unreadable documents."));
+ xInfoBox->run();
+ }
+ }
+#endif
+
+ // #103836# if the user sets character attributes to the complete shape,
+ // we want to remove all hard set character attributes with same
+ // which ids from the text. We do that later but here we remember
+ // all character attribute which id's that are set.
+ std::vector<sal_uInt16> aCharWhichIds(GetAllCharPropIds(rAttr));
+
+ // To make Undo reconstruct text attributes correctly after Format.Standard
+ bool bHasEEItems=SearchOutlinerItems(rAttr,bReplaceAll);
+
+ // save additional geometry information when paragraph or character attributes
+ // are changed and the geometrical shape of the text object might be changed
+ bool bPossibleGeomChange(false);
+ SfxWhichIter aIter(rAttr);
+ sal_uInt16 nWhich = aIter.FirstWhich();
+ while(!bPossibleGeomChange && nWhich)
+ {
+ SfxItemState eState = aIter.GetItemState();
+ if(eState == SfxItemState::SET)
+ {
+ if((nWhich >= SDRATTR_TEXT_MINFRAMEHEIGHT && nWhich <= SDRATTR_TEXT_CONTOURFRAME)
+ || nWhich == SDRATTR_3DOBJ_PERCENT_DIAGONAL
+ || nWhich == SDRATTR_3DOBJ_BACKSCALE
+ || nWhich == SDRATTR_3DOBJ_DEPTH
+ || nWhich == SDRATTR_3DOBJ_END_ANGLE
+ || nWhich == SDRATTR_3DSCENE_DISTANCE)
+ {
+ bPossibleGeomChange = true;
+ }
+ }
+ nWhich = aIter.NextWhich();
+ }
+
+ const bool bUndo = IsUndoEnabled();
+ if( bUndo )
+ {
+ EndTextEditCurrentView();
+ BegUndo(ImpGetDescriptionString(STR_EditSetAttributes));
+ }
+
+ const size_t nMarkCount(GetMarkedObjectCount());
+ std::vector< E3DModifySceneSnapRectUpdater* > aUpdaters;
+
+ // create ItemSet without SfxItemState::DONTCARE. Put()
+ // uses its second parameter (bInvalidAsDefault) to
+ // remove all such items to set them to default.
+ SfxItemSet aAttr(*rAttr.GetPool(), rAttr.GetRanges());
+ aAttr.Put(rAttr);
+
+ // #i38135#
+ bool bResetAnimationTimer(false);
+
+ const bool bLineStartWidthExplicitChange(SfxItemState::SET
+ == aAttr.GetItemState(XATTR_LINESTARTWIDTH));
+ const bool bLineEndWidthExplicitChange(SfxItemState::SET
+ == aAttr.GetItemState(XATTR_LINEENDWIDTH));
+ // check if LineWidth is part of the change
+ const bool bAdaptStartEndWidths(!(bLineStartWidthExplicitChange && bLineEndWidthExplicitChange)
+ && SfxItemState::SET == aAttr.GetItemState(XATTR_LINEWIDTH));
+ sal_Int32 nNewLineWidth(0);
+
+ if(bAdaptStartEndWidths)
+ {
+ nNewLineWidth = aAttr.Get(XATTR_LINEWIDTH).GetValue();
+ }
+
+ for (size_t nm=0; nm<nMarkCount; ++nm)
+ {
+ SdrMark* pM=GetSdrMarkByIndex(nm);
+ SdrObject* pObj = pM->GetMarkedSdrObj();
+
+ if( bUndo )
+ {
+ SdrEdgeObj* pEdgeObj = dynamic_cast< SdrEdgeObj* >( pObj );
+ if ( pEdgeObj )
+ bPossibleGeomChange = true;
+ else
+ AddUndoActions( CreateConnectorUndo( *pObj ) );
+ }
+
+ // new geometry undo
+ if(bPossibleGeomChange && bUndo)
+ {
+ // save position and size of object, too
+ AddUndo( GetModel()->GetSdrUndoFactory().CreateUndoGeoObject(*pObj));
+ }
+
+ if( bUndo )
+ {
+ // #i8508#
+ // If this is a text object also rescue the OutlinerParaObject since
+ // applying attributes to the object may change text layout when
+ // multiple portions exist with multiple formats. If an OutlinerParaObject
+ // really exists and needs to be rescued is evaluated in the undo
+ // implementation itself.
+ const bool bRescueText = dynamic_cast< SdrTextObj* >(pObj) != nullptr;
+
+ // add attribute undo
+ AddUndo(GetModel()->GetSdrUndoFactory().CreateUndoAttrObject(*pObj,false,bHasEEItems || bPossibleGeomChange || bRescueText));
+ }
+
+ // set up a scene updater if object is a 3d object
+ if(dynamic_cast< E3dObject* >(pObj))
+ {
+ aUpdaters.push_back(new E3DModifySceneSnapRectUpdater(pObj));
+ }
+
+ sal_Int32 nOldLineWidth(0);
+ if (bAdaptStartEndWidths)
+ {
+ nOldLineWidth = pObj->GetMergedItem(XATTR_LINEWIDTH).GetValue();
+ }
+
+ // set attributes at object
+ pObj->SetMergedItemSetAndBroadcast(aAttr, bReplaceAll);
+
+ if(bAdaptStartEndWidths)
+ {
+ const SfxItemSet& rSet = pObj->GetMergedItemSet();
+
+ if(nOldLineWidth != nNewLineWidth)
+ {
+ if(SfxItemState::DONTCARE != rSet.GetItemState(XATTR_LINESTARTWIDTH))
+ {
+ const sal_Int32 nValAct(rSet.Get(XATTR_LINESTARTWIDTH).GetValue());
+ const sal_Int32 nValNewStart(std::max(sal_Int32(0), nValAct + (((nNewLineWidth - nOldLineWidth) * 15) / 10)));
+
+ pObj->SetMergedItem(XLineStartWidthItem(nValNewStart));
+ }
+
+ if(SfxItemState::DONTCARE != rSet.GetItemState(XATTR_LINEENDWIDTH))
+ {
+ const sal_Int32 nValAct(rSet.Get(XATTR_LINEENDWIDTH).GetValue());
+ const sal_Int32 nValNewEnd(std::max(sal_Int32(0), nValAct + (((nNewLineWidth - nOldLineWidth) * 15) / 10)));
+
+ pObj->SetMergedItem(XLineEndWidthItem(nValNewEnd));
+ }
+ }
+ }
+
+ if(auto pTextObj = dynamic_cast<SdrTextObj*>( pObj))
+ {
+ if(!aCharWhichIds.empty())
+ {
+ tools::Rectangle aOldBoundRect = pTextObj->GetLastBoundRect();
+
+ // #110094#-14 pTextObj->SendRepaintBroadcast(pTextObj->GetBoundRect());
+ pTextObj->RemoveOutlinerCharacterAttribs( aCharWhichIds );
+
+ // object has changed, should be called from
+ // RemoveOutlinerCharacterAttribs. This will change when the text
+ // object implementation changes.
+ pTextObj->SetChanged();
+
+ pTextObj->BroadcastObjectChange();
+ pTextObj->SendUserCall(SdrUserCallType::ChangeAttr, aOldBoundRect);
+ }
+ }
+
+ // #i38495#
+ if(!bResetAnimationTimer)
+ {
+ if(pObj->GetViewContact().isAnimatedInAnyViewObjectContact())
+ {
+ bResetAnimationTimer = true;
+ }
+ }
+ }
+
+ // fire scene updaters
+ while(!aUpdaters.empty())
+ {
+ delete aUpdaters.back();
+ aUpdaters.pop_back();
+ }
+
+ // #i38135#
+ if(bResetAnimationTimer)
+ {
+ SetAnimationTimer(0);
+ }
+
+ // better check before what to do:
+ // pObj->SetAttr() or SetNotPersistAttr()
+ // TODO: missing implementation!
+ SetNotPersistAttrToMarked(rAttr);
+
+ if( bUndo )
+ EndUndo();
+}
+
+SfxStyleSheet* SdrEditView::GetStyleSheetFromMarked() const
+{
+ SfxStyleSheet* pRet=nullptr;
+ bool b1st=true;
+ const size_t nMarkCount=GetMarkedObjectCount();
+ for (size_t nm=0; nm<nMarkCount; ++nm) {
+ SdrMark* pM=GetSdrMarkByIndex(nm);
+ SfxStyleSheet* pSS=pM->GetMarkedSdrObj()->GetStyleSheet();
+ if (b1st) pRet=pSS;
+ else if (pRet!=pSS) return nullptr; // different stylesheets
+ b1st=false;
+ }
+ return pRet;
+}
+
+void SdrEditView::SetStyleSheetToMarked(SfxStyleSheet* pStyleSheet, bool bDontRemoveHardAttr)
+{
+ if (!AreObjectsMarked())
+ return;
+
+ const bool bUndo = IsUndoEnabled();
+
+ if( bUndo )
+ {
+ EndTextEditCurrentView();
+ OUString aStr;
+ if (pStyleSheet!=nullptr)
+ aStr = ImpGetDescriptionString(STR_EditSetStylesheet);
+ else
+ aStr = ImpGetDescriptionString(STR_EditDelStylesheet);
+ BegUndo(aStr);
+ }
+
+ const size_t nMarkCount=GetMarkedObjectCount();
+ for (size_t nm=0; nm<nMarkCount; ++nm)
+ {
+ SdrMark* pM=GetSdrMarkByIndex(nm);
+ if( bUndo )
+ {
+ AddUndo(GetModel()->GetSdrUndoFactory().CreateUndoGeoObject(*pM->GetMarkedSdrObj()));
+ AddUndo(GetModel()->GetSdrUndoFactory().CreateUndoAttrObject(*pM->GetMarkedSdrObj(),true,true));
+ }
+ pM->GetMarkedSdrObj()->SetStyleSheet(pStyleSheet,bDontRemoveHardAttr);
+ }
+
+ if( bUndo )
+ EndUndo();
+}
+
+
+void SdrEditView::GetAttributes(SfxItemSet& rTargetSet, bool bOnlyHardAttr) const
+{
+ if(GetMarkedObjectCount())
+ {
+ rTargetSet.Put(GetAttrFromMarked(bOnlyHardAttr), false);
+ }
+ else
+ {
+ SdrMarkView::GetAttributes(rTargetSet, bOnlyHardAttr);
+ }
+}
+
+void SdrEditView::SetAttributes(const SfxItemSet& rSet, bool bReplaceAll)
+{
+ if (GetMarkedObjectCount()!=0) {
+ SetAttrToMarked(rSet,bReplaceAll);
+ } else {
+ SdrMarkView::SetAttributes(rSet,bReplaceAll);
+ }
+}
+
+SfxStyleSheet* SdrEditView::GetStyleSheet() const
+{
+ if (GetMarkedObjectCount()!=0) {
+ return GetStyleSheetFromMarked();
+ } else {
+ return SdrMarkView::GetStyleSheet();
+ }
+}
+
+void SdrEditView::SetStyleSheet(SfxStyleSheet* pStyleSheet, bool bDontRemoveHardAttr)
+{
+ if (GetMarkedObjectCount()!=0) {
+ SetStyleSheetToMarked(pStyleSheet,bDontRemoveHardAttr);
+ } else {
+ SdrMarkView::SetStyleSheet(pStyleSheet,bDontRemoveHardAttr);
+ }
+}
+
+
+SfxItemSet SdrEditView::GetGeoAttrFromMarked() const
+{
+ SfxItemSet aRetSet(
+ mpModel->GetItemPool(),
+ svl::Items< // SID_ATTR_TRANSFORM_... from s:svxids.hrc
+ SDRATTR_CORNER_RADIUS, SDRATTR_CORNER_RADIUS,
+ SID_ATTR_TRANSFORM_POS_X, SID_ATTR_TRANSFORM_ANGLE,
+ SID_ATTR_TRANSFORM_PROTECT_POS, SID_ATTR_TRANSFORM_AUTOHEIGHT>);
+
+ if (AreObjectsMarked())
+ {
+ SfxItemSet aMarkAttr(GetAttrFromMarked(false)); // because of AutoGrowHeight and corner radius
+ tools::Rectangle aRect(GetMarkedObjRect());
+
+ if(GetSdrPageView())
+ {
+ GetSdrPageView()->LogicToPagePos(aRect);
+ }
+
+ // position
+ aRetSet.Put(SfxInt32Item(SID_ATTR_TRANSFORM_POS_X,aRect.Left()));
+ aRetSet.Put(SfxInt32Item(SID_ATTR_TRANSFORM_POS_Y,aRect.Top()));
+
+ // size
+ tools::Long nResizeRefX=aRect.Left();
+ tools::Long nResizeRefY=aRect.Top();
+ if (meDragMode==SdrDragMode::Rotate) { // use rotation axis as a reference for resizing, too
+ nResizeRefX=maRef1.X();
+ nResizeRefY=maRef1.Y();
+ }
+ aRetSet.Put(SfxUInt32Item(SID_ATTR_TRANSFORM_WIDTH,aRect.Right()-aRect.Left()));
+ aRetSet.Put(SfxUInt32Item(SID_ATTR_TRANSFORM_HEIGHT,aRect.Bottom()-aRect.Top()));
+ aRetSet.Put(SfxInt32Item(SID_ATTR_TRANSFORM_RESIZE_REF_X,nResizeRefX));
+ aRetSet.Put(SfxInt32Item(SID_ATTR_TRANSFORM_RESIZE_REF_Y,nResizeRefY));
+
+ Point aRotateAxe(maRef1);
+
+ if(GetSdrPageView())
+ {
+ GetSdrPageView()->LogicToPagePos(aRotateAxe);
+ }
+
+ // rotation
+ tools::Long nRotateRefX=aRect.Center().X();
+ tools::Long nRotateRefY=aRect.Center().Y();
+ if (meDragMode==SdrDragMode::Rotate) {
+ nRotateRefX=aRotateAxe.X();
+ nRotateRefY=aRotateAxe.Y();
+ }
+ aRetSet.Put(SdrAngleItem(SID_ATTR_TRANSFORM_ANGLE,GetMarkedObjRotate()));
+ aRetSet.Put(SfxInt32Item(SID_ATTR_TRANSFORM_ROT_X,nRotateRefX));
+ aRetSet.Put(SfxInt32Item(SID_ATTR_TRANSFORM_ROT_Y,nRotateRefY));
+
+ // shearing
+ tools::Long nShearRefX=aRect.Left();
+ tools::Long nShearRefY=aRect.Bottom();
+ if (meDragMode==SdrDragMode::Rotate) { // use rotation axis as a reference for shearing, too
+ nShearRefX=aRotateAxe.X();
+ nShearRefY=aRotateAxe.Y();
+ }
+ aRetSet.Put(SdrAngleItem(SID_ATTR_TRANSFORM_SHEAR,GetMarkedObjShear()));
+ aRetSet.Put(SfxInt32Item(SID_ATTR_TRANSFORM_SHEAR_X,nShearRefX));
+ aRetSet.Put(SfxInt32Item(SID_ATTR_TRANSFORM_SHEAR_Y,nShearRefY));
+
+ // check every object whether it is protected
+ const SdrMarkList& rMarkList=GetMarkedObjectList();
+ const size_t nMarkCount=rMarkList.GetMarkCount();
+ SdrObject* pObj=rMarkList.GetMark(0)->GetMarkedSdrObj();
+ bool bPosProt=pObj->IsMoveProtect();
+ bool bSizProt=pObj->IsResizeProtect();
+ bool bPosProtDontCare=false;
+ bool bSizProtDontCare=false;
+ for (size_t i=1; i<nMarkCount && (!bPosProtDontCare || !bSizProtDontCare); ++i)
+ {
+ pObj=rMarkList.GetMark(i)->GetMarkedSdrObj();
+ if (bPosProt!=pObj->IsMoveProtect()) bPosProtDontCare=true;
+ if (bSizProt!=pObj->IsResizeProtect()) bSizProtDontCare=true;
+ }
+
+ // InvalidateItem sets item to DONT_CARE
+ if (bPosProtDontCare) {
+ aRetSet.InvalidateItem(SID_ATTR_TRANSFORM_PROTECT_POS);
+ } else {
+ aRetSet.Put(SfxBoolItem(SID_ATTR_TRANSFORM_PROTECT_POS,bPosProt));
+ }
+ if (bSizProtDontCare) {
+ aRetSet.InvalidateItem(SID_ATTR_TRANSFORM_PROTECT_SIZE);
+ } else {
+ aRetSet.Put(SfxBoolItem(SID_ATTR_TRANSFORM_PROTECT_SIZE,bSizProt));
+ }
+
+ SfxItemState eState=aMarkAttr.GetItemState(SDRATTR_TEXT_AUTOGROWWIDTH);
+ bool bAutoGrow=aMarkAttr.Get(SDRATTR_TEXT_AUTOGROWWIDTH).GetValue();
+ if (eState==SfxItemState::DONTCARE) {
+ aRetSet.InvalidateItem(SID_ATTR_TRANSFORM_AUTOWIDTH);
+ } else if (eState==SfxItemState::SET) {
+ aRetSet.Put(SfxBoolItem(SID_ATTR_TRANSFORM_AUTOWIDTH,bAutoGrow));
+ }
+
+ eState=aMarkAttr.GetItemState(SDRATTR_TEXT_AUTOGROWHEIGHT);
+ bAutoGrow=aMarkAttr.Get(SDRATTR_TEXT_AUTOGROWHEIGHT).GetValue();
+ if (eState==SfxItemState::DONTCARE) {
+ aRetSet.InvalidateItem(SID_ATTR_TRANSFORM_AUTOHEIGHT);
+ } else if (eState==SfxItemState::SET) {
+ aRetSet.Put(SfxBoolItem(SID_ATTR_TRANSFORM_AUTOHEIGHT,bAutoGrow));
+ }
+
+ eState=aMarkAttr.GetItemState(SDRATTR_CORNER_RADIUS);
+ tools::Long nRadius=aMarkAttr.Get(SDRATTR_CORNER_RADIUS).GetValue();
+ if (eState==SfxItemState::DONTCARE) {
+ aRetSet.InvalidateItem(SDRATTR_CORNER_RADIUS);
+ } else if (eState==SfxItemState::SET) {
+ aRetSet.Put(makeSdrEckenradiusItem(nRadius));
+ }
+
+ basegfx::B2DHomMatrix aTransformation;
+
+ if(nMarkCount > 1)
+ {
+ // multiple objects, range is collected in aRect
+ aTransformation = basegfx::utils::createScaleTranslateB2DHomMatrix(
+ aRect.Left(), aRect.Top(),
+ aRect.getWidth(), aRect.getHeight());
+ }
+ else
+ {
+ // single object, get homogen transformation
+ basegfx::B2DPolyPolygon aPolyPolygon;
+
+ pObj->TRGetBaseGeometry(aTransformation, aPolyPolygon);
+ }
+
+ if(aTransformation.isIdentity())
+ {
+ aRetSet.InvalidateItem(SID_ATTR_TRANSFORM_MATRIX);
+ }
+ else
+ {
+ css::geometry::AffineMatrix2D aAffineMatrix2D;
+ Point aPageOffset(0, 0);
+
+ if(GetSdrPageView())
+ {
+ aPageOffset = GetSdrPageView()->GetPageOrigin();
+ }
+
+ aAffineMatrix2D.m00 = aTransformation.get(0, 0);
+ aAffineMatrix2D.m01 = aTransformation.get(0, 1);
+ aAffineMatrix2D.m02 = aTransformation.get(0, 2) - aPageOffset.X();
+ aAffineMatrix2D.m10 = aTransformation.get(1, 0);
+ aAffineMatrix2D.m11 = aTransformation.get(1, 1);
+ aAffineMatrix2D.m12 = aTransformation.get(1, 2) - aPageOffset.Y();
+
+ aRetSet.Put(AffineMatrixItem(&aAffineMatrix2D));
+ }
+ }
+
+ return aRetSet;
+}
+
+static Point ImpGetPoint(const tools::Rectangle& rRect, RectPoint eRP)
+{
+ switch(eRP) {
+ case RectPoint::LT: return rRect.TopLeft();
+ case RectPoint::MT: return rRect.TopCenter();
+ case RectPoint::RT: return rRect.TopRight();
+ case RectPoint::LM: return rRect.LeftCenter();
+ case RectPoint::MM: return rRect.Center();
+ case RectPoint::RM: return rRect.RightCenter();
+ case RectPoint::LB: return rRect.BottomLeft();
+ case RectPoint::MB: return rRect.BottomCenter();
+ case RectPoint::RB: return rRect.BottomRight();
+ }
+ return Point(); // Should not happen!
+}
+
+void SdrEditView::SetGeoAttrToMarked(const SfxItemSet& rAttr, bool addPageMargin)
+{
+ const bool bTiledRendering = comphelper::LibreOfficeKit::isActive();
+
+ tools::Rectangle aRect(GetMarkedObjRect());
+
+ if(GetSdrPageView())
+ {
+ if (addPageMargin)
+ {
+ SdrPage * pPage = GetSdrPageView()->GetPage();
+ Point upperLeft(pPage->GetLeftBorder(), pPage->GetUpperBorder());
+ aRect.Move(upperLeft.getX(), upperLeft.getY());
+ }
+ GetSdrPageView()->LogicToPagePos(aRect);
+ }
+
+ Degree100 nOldRotateAngle=GetMarkedObjRotate();
+ Degree100 nOldShearAngle=GetMarkedObjShear();
+ const SdrMarkList& rMarkList=GetMarkedObjectList();
+ SdrObject* pObj=nullptr;
+
+ RectPoint eSizePoint=RectPoint::MM;
+ tools::Long nPosDX=0;
+ tools::Long nPosDY=0;
+ tools::Long nSizX=0;
+ tools::Long nSizY=0;
+ Degree100 nRotateAngle(0);
+
+ bool bModeIsRotate(meDragMode == SdrDragMode::Rotate);
+ tools::Long nRotateX(0);
+ tools::Long nRotateY(0);
+ tools::Long nOldRotateX(0);
+ tools::Long nOldRotateY(0);
+ if(bModeIsRotate)
+ {
+ Point aRotateAxe(maRef1);
+
+ if(GetSdrPageView())
+ {
+ GetSdrPageView()->LogicToPagePos(aRotateAxe);
+ }
+
+ nRotateX = nOldRotateX = aRotateAxe.X();
+ nRotateY = nOldRotateY = aRotateAxe.Y();
+ }
+
+ Degree100 nShearAngle(0);
+ tools::Long nShearX=0;
+ tools::Long nShearY=0;
+ bool bShearVert=false;
+
+ bool bChgPos=false;
+ bool bChgSiz=false;
+ bool bChgWdh=false;
+ bool bChgHgt=false;
+ bool bRotate=false;
+ bool bShear =false;
+
+ bool bSetAttr=false;
+ SfxItemSet aSetAttr(mpModel->GetItemPool());
+
+ // position
+ if (const SfxInt32Item *pPoolItem = rAttr.GetItemIfSet(SID_ATTR_TRANSFORM_POS_X))
+ {
+ nPosDX = pPoolItem->GetValue() - aRect.Left();
+ bChgPos=true;
+ }
+ if (const SfxInt32Item *pPoolItem = rAttr.GetItemIfSet(SID_ATTR_TRANSFORM_POS_Y))
+ {
+ nPosDY = pPoolItem->GetValue() - aRect.Top();
+ bChgPos=true;
+ }
+ // size
+ if (const SfxUInt32Item *pPoolItem = rAttr.GetItemIfSet(SID_ATTR_TRANSFORM_WIDTH))
+ {
+ nSizX = pPoolItem->GetValue();
+ bChgSiz=true;
+ bChgWdh=true;
+ }
+ if (const SfxUInt32Item *pPoolItem = rAttr.GetItemIfSet(SID_ATTR_TRANSFORM_HEIGHT))
+ {
+ nSizY = pPoolItem->GetValue();
+ bChgSiz=true;
+ bChgHgt=true;
+ }
+ if (bChgSiz) {
+ if (bTiledRendering && SfxItemState::SET != rAttr.GetItemState(SID_ATTR_TRANSFORM_SIZE_POINT))
+ eSizePoint = RectPoint::LT;
+ else
+ eSizePoint = static_cast<RectPoint>(rAttr.Get(SID_ATTR_TRANSFORM_SIZE_POINT).GetValue());
+ }
+
+ // rotation
+ if (const SdrAngleItem *pPoolItem = rAttr.GetItemIfSet(SID_ATTR_TRANSFORM_DELTA_ANGLE))
+ {
+ nRotateAngle = pPoolItem->GetValue();
+ bRotate = (nRotateAngle != 0_deg100);
+ }
+
+ // rotation
+ if (const SdrAngleItem *pPoolItem = rAttr.GetItemIfSet(SID_ATTR_TRANSFORM_ANGLE))
+ {
+ nRotateAngle = pPoolItem->GetValue() - nOldRotateAngle;
+ bRotate = (nRotateAngle != 0_deg100);
+ }
+
+ // position rotation point x
+ if(bRotate || rAttr.GetItemIfSet(SID_ATTR_TRANSFORM_ROT_X))
+ nRotateX = rAttr.Get(SID_ATTR_TRANSFORM_ROT_X).GetValue();
+
+ // position rotation point y
+ if(bRotate || rAttr.GetItemIfSet(SID_ATTR_TRANSFORM_ROT_Y))
+ nRotateY = rAttr.Get(SID_ATTR_TRANSFORM_ROT_Y).GetValue();
+
+ // shearing
+ if (const SdrAngleItem *pPoolItem = rAttr.GetItemIfSet(SID_ATTR_TRANSFORM_SHEAR))
+ {
+ Degree100 nNewShearAngle=pPoolItem->GetValue();
+ if (nNewShearAngle>SDRMAXSHEAR) nNewShearAngle=SDRMAXSHEAR;
+ if (nNewShearAngle<-SDRMAXSHEAR) nNewShearAngle=-SDRMAXSHEAR;
+ if (nNewShearAngle!=nOldShearAngle) {
+ bShearVert = rAttr.Get(SID_ATTR_TRANSFORM_SHEAR_VERTICAL).GetValue();
+ if (bShearVert) {
+ nShearAngle=nNewShearAngle;
+ } else {
+ if (nNewShearAngle!=0_deg100 && nOldShearAngle!=0_deg100) {
+ // bug fix
+ double nOld = tan(toRadians(nOldShearAngle));
+ double nNew = tan(toRadians(nNewShearAngle));
+ nNew-=nOld;
+ nNew = basegfx::rad2deg<100>(atan(nNew));
+ nShearAngle=Degree100(FRound(nNew));
+ } else {
+ nShearAngle=nNewShearAngle-nOldShearAngle;
+ }
+ }
+ bShear=nShearAngle!=0_deg100;
+ if (bShear) {
+ nShearX=static_cast<const SfxInt32Item&>(rAttr.Get(SID_ATTR_TRANSFORM_SHEAR_X)).GetValue();
+ nShearY=static_cast<const SfxInt32Item&>(rAttr.Get(SID_ATTR_TRANSFORM_SHEAR_Y)).GetValue();
+ }
+ }
+ }
+
+ // AutoGrow
+ if (const SfxBoolItem *pPoolItem = rAttr.GetItemIfSet(SID_ATTR_TRANSFORM_AUTOWIDTH))
+ {
+ bool bAutoGrow = pPoolItem->GetValue();
+ aSetAttr.Put(makeSdrTextAutoGrowWidthItem(bAutoGrow));
+ bSetAttr=true;
+ }
+
+ if (const SfxBoolItem *pPoolItem = rAttr.GetItemIfSet(SID_ATTR_TRANSFORM_AUTOHEIGHT))
+ {
+ bool bAutoGrow = pPoolItem->GetValue();
+ aSetAttr.Put(makeSdrTextAutoGrowHeightItem(bAutoGrow));
+ bSetAttr=true;
+ }
+
+ // corner radius
+ if (m_bEdgeRadiusAllowed)
+ if (const SdrMetricItem *pPoolItem = rAttr.GetItemIfSet(SDRATTR_CORNER_RADIUS))
+ {
+ tools::Long nRadius = pPoolItem->GetValue();
+ aSetAttr.Put(makeSdrEckenradiusItem(nRadius));
+ bSetAttr=true;
+ }
+
+ ForcePossibilities();
+
+ BegUndo(SvxResId(STR_EditTransform),GetDescriptionOfMarkedObjects());
+
+ if (bSetAttr) {
+ SetAttrToMarked(aSetAttr,false);
+ }
+
+ // change size and height
+ if (bChgSiz && (m_bResizeFreeAllowed || m_bResizePropAllowed)) {
+ Fraction aWdt(nSizX,aRect.Right()-aRect.Left());
+ Fraction aHgt(nSizY,aRect.Bottom()-aRect.Top());
+ Point aRef(ImpGetPoint(aRect,eSizePoint));
+
+ if(GetSdrPageView())
+ {
+ GetSdrPageView()->PagePosToLogic(aRef);
+ }
+
+ ResizeMultMarkedObj(aRef, aWdt, aHgt, bChgWdh, bChgHgt);
+ }
+
+ // rotate
+ if (bRotate && (m_bRotateFreeAllowed || m_bRotate90Allowed)) {
+ Point aRef(nRotateX,nRotateY);
+
+ if(GetSdrPageView())
+ {
+ GetSdrPageView()->PagePosToLogic(aRef);
+ }
+
+ RotateMarkedObj(aRef,nRotateAngle);
+ }
+
+ // set rotation point position
+ if(bModeIsRotate && (nRotateX != nOldRotateX || nRotateY != nOldRotateY))
+ {
+ Point aNewRef1(nRotateX, nRotateY);
+
+ if(GetSdrPageView())
+ {
+ GetSdrPageView()->PagePosToLogic(aNewRef1);
+ }
+
+ SetRef1(aNewRef1);
+ }
+
+ // shear
+ if (bShear && m_bShearAllowed) {
+ Point aRef(nShearX,nShearY);
+
+ if(GetSdrPageView())
+ {
+ GetSdrPageView()->PagePosToLogic(aRef);
+ }
+
+ ShearMarkedObj(aRef,nShearAngle,bShearVert);
+
+ // #i74358#
+ // ShearMarkedObj creates a linear combination of the existing transformation and
+ // the new shear to apply. If the object is already transformed (e.g. rotated) the
+ // linear combination will not decompose to the same start values again, but to a
+ // new combination. Thus it makes no sense to check if the wanted shear is reached
+ // or not. Taking out.
+ }
+
+ // change position
+ if (bChgPos && m_bMoveAllowed) {
+ MoveMarkedObj(Size(nPosDX,nPosDY));
+ }
+
+ const size_t nMarkCount=rMarkList.GetMarkCount();
+ // protect position
+ if(const SfxBoolItem *pPoolItem = rAttr.GetItemIfSet(SID_ATTR_TRANSFORM_PROTECT_POS))
+ {
+ const bool bProtPos(pPoolItem->GetValue());
+ bool bChanged(false);
+
+ for(size_t i = 0; i < nMarkCount; ++i)
+ {
+ pObj = rMarkList.GetMark(i)->GetMarkedSdrObj();
+
+ if(pObj->IsMoveProtect() != bProtPos)
+ {
+ bChanged = true;
+ pObj->SetMoveProtect(bProtPos);
+
+ if(bProtPos)
+ {
+ pObj->SetResizeProtect(true);
+ }
+ }
+ }
+
+ if(bChanged)
+ {
+ m_bMoveProtect = bProtPos;
+
+ if(bProtPos)
+ {
+ m_bResizeProtect = true;
+ }
+
+ // #i77187# there is no simple method to get the toolbars updated
+ // in the application. The App is listening to selection change and i
+ // will use it here (even if not true). It's acceptable since changing
+ // this model data is pretty rare and only possible using the F4 dialog
+ MarkListHasChanged();
+ }
+ }
+
+ if(!m_bMoveProtect)
+ {
+ // protect size
+ if(const SfxBoolItem *pPoolItem = rAttr.GetItemIfSet(SID_ATTR_TRANSFORM_PROTECT_SIZE))
+ {
+ const bool bProtSize(pPoolItem->GetValue());
+ bool bChanged(false);
+
+ for(size_t i = 0; i < nMarkCount; ++i)
+ {
+ pObj = rMarkList.GetMark(i)->GetMarkedSdrObj();
+
+ if(pObj->IsResizeProtect() != bProtSize)
+ {
+ bChanged = true;
+ pObj->SetResizeProtect(bProtSize);
+ }
+ }
+
+ if(bChanged)
+ {
+ m_bResizeProtect = bProtSize;
+
+ // #i77187# see above
+ MarkListHasChanged();
+ }
+ }
+ }
+
+ EndUndo();
+}
+
+
+bool SdrEditView::IsAlignPossible() const
+{ // at least two selected objects, at least one of them movable
+ ForcePossibilities();
+ const size_t nCount=GetMarkedObjectCount();
+ if (nCount==0) return false; // nothing selected!
+ if (nCount==1) return m_bMoveAllowed; // align single object to page
+ return m_bOneOrMoreMovable; // otherwise: MarkCount>=2
+}
+
+void SdrEditView::AlignMarkedObjects(SdrHorAlign eHor, SdrVertAlign eVert)
+{
+ if (eHor==SdrHorAlign::NONE && eVert==SdrVertAlign::NONE)
+ return;
+
+ SortMarkedObjects();
+ if (!GetMarkedObjectCount())
+ return;
+
+ const bool bUndo = IsUndoEnabled();
+ if( bUndo )
+ {
+ EndTextEditCurrentView();
+ OUString aStr(GetDescriptionOfMarkedObjects());
+ if (eHor==SdrHorAlign::NONE)
+ {
+ switch (eVert)
+ {
+ case SdrVertAlign::Top:
+ aStr = ImpGetDescriptionString(STR_EditAlignVTop);
+ break;
+ case SdrVertAlign::Bottom:
+ aStr = ImpGetDescriptionString(STR_EditAlignVBottom);
+ break;
+ case SdrVertAlign::Center:
+ aStr = ImpGetDescriptionString(STR_EditAlignVCenter);
+ break;
+ default: break;
+ }
+ }
+ else if (eVert==SdrVertAlign::NONE)
+ {
+ switch (eHor)
+ {
+ case SdrHorAlign::Left:
+ aStr = ImpGetDescriptionString(STR_EditAlignHLeft);
+ break;
+ case SdrHorAlign::Right:
+ aStr = ImpGetDescriptionString(STR_EditAlignHRight);
+ break;
+ case SdrHorAlign::Center:
+ aStr = ImpGetDescriptionString(STR_EditAlignHCenter);
+ break;
+ default: break;
+ }
+ }
+ else if (eHor==SdrHorAlign::Center && eVert==SdrVertAlign::Center)
+ {
+ aStr = ImpGetDescriptionString(STR_EditAlignCenter);
+ }
+ else
+ {
+ aStr = ImpGetDescriptionString(STR_EditAlign);
+ }
+ BegUndo(aStr);
+ }
+
+ tools::Rectangle aBound;
+ const size_t nMarkCount=GetMarkedObjectCount();
+ bool bHasFixed=false;
+ for (size_t nm=0; nm<nMarkCount; ++nm)
+ {
+ SdrMark* pM=GetSdrMarkByIndex(nm);
+ SdrObject* pObj=pM->GetMarkedSdrObj();
+ SdrObjTransformInfoRec aInfo;
+ pObj->TakeObjInfo(aInfo);
+ if (!aInfo.bMoveAllowed || pObj->IsMoveProtect())
+ {
+ tools::Rectangle aObjRect(pObj->GetSnapRect());
+ aBound.Union(aObjRect);
+ bHasFixed=true;
+ }
+ }
+ if (!bHasFixed)
+ {
+ if (nMarkCount==1)
+ { // align single object to page
+ const SdrObject* pObj=GetMarkedObjectByIndex(0);
+ const SdrPage* pPage=pObj->getSdrPageFromSdrObject();
+ const SdrPageGridFrameList* pGFL=pPage->GetGridFrameList(GetSdrPageViewOfMarkedByIndex(0),&(pObj->GetSnapRect()));
+ const SdrPageGridFrame* pFrame=nullptr;
+ if (pGFL!=nullptr && pGFL->GetCount()!=0)
+ { // Writer
+ pFrame=&((*pGFL)[0]);
+ }
+
+ if (pFrame!=nullptr)
+ { // Writer
+ aBound=pFrame->GetUserArea();
+ }
+ else
+ {
+ aBound=tools::Rectangle(pPage->GetLeftBorder(),pPage->GetUpperBorder(),
+ pPage->GetWidth()-pPage->GetRightBorder(),
+ pPage->GetHeight()-pPage->GetLowerBorder());
+ }
+ }
+ else
+ {
+ aBound=GetMarkedObjRect();
+ }
+ }
+ Point aCenter(aBound.Center());
+ for (size_t nm=0; nm<nMarkCount; ++nm)
+ {
+ SdrMark* pM=GetSdrMarkByIndex(nm);
+ SdrObject* pObj=pM->GetMarkedSdrObj();
+ SdrObjTransformInfoRec aInfo;
+ pObj->TakeObjInfo(aInfo);
+ if (aInfo.bMoveAllowed && !pObj->IsMoveProtect())
+ {
+ tools::Long nXMov=0;
+ tools::Long nYMov=0;
+ tools::Rectangle aObjRect(pObj->GetSnapRect());
+ switch (eVert)
+ {
+ case SdrVertAlign::Top : nYMov=aBound.Top() -aObjRect.Top() ; break;
+ case SdrVertAlign::Bottom: nYMov=aBound.Bottom()-aObjRect.Bottom() ; break;
+ case SdrVertAlign::Center: nYMov=aCenter.Y() -aObjRect.Center().Y(); break;
+ default: break;
+ }
+ switch (eHor)
+ {
+ case SdrHorAlign::Left : nXMov=aBound.Left() -aObjRect.Left() ; break;
+ case SdrHorAlign::Right : nXMov=aBound.Right() -aObjRect.Right() ; break;
+ case SdrHorAlign::Center: nXMov=aCenter.X() -aObjRect.Center().X(); break;
+ default: break;
+ }
+ if (nXMov!=0 || nYMov!=0)
+ {
+ // SdrEdgeObj needs an extra SdrUndoGeoObj since the
+ // connections may need to be saved
+ if( bUndo )
+ {
+ if( dynamic_cast<SdrEdgeObj*>(pObj) )
+ {
+ AddUndo(GetModel()->GetSdrUndoFactory().CreateUndoGeoObject(*pObj));
+ }
+
+ AddUndo(GetModel()->GetSdrUndoFactory().CreateUndoMoveObject(*pObj,Size(nXMov,nYMov)));
+ }
+
+ pObj->Move(Size(nXMov,nYMov));
+ }
+ }
+ }
+
+ if( bUndo )
+ EndUndo();
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/svdraw/svdedtv2.cxx b/svx/source/svdraw/svdedtv2.cxx
new file mode 100644
index 000000000..50a7940ca
--- /dev/null
+++ b/svx/source/svdraw/svdedtv2.cxx
@@ -0,0 +1,2250 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * 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/svdedtv.hxx>
+#include <svx/svdundo.hxx>
+#include <svx/svdogrp.hxx>
+#include <svx/svdoutl.hxx>
+#include <svx/svdopath.hxx>
+#include <svx/svdpage.hxx>
+#include <svx/svdpagv.hxx>
+#include <svx/svditer.hxx>
+#include <svx/svdograf.hxx>
+#include <svx/svdoole2.hxx>
+#include <svx/dialmgr.hxx>
+#include <svx/sdooitm.hxx>
+#include <svx/sdshitm.hxx>
+#include <svx/xfillit0.hxx>
+#include <svx/xlineit0.hxx>
+#include <svx/xtextit0.hxx>
+#include "svdfmtf.hxx"
+#include <svdpdf.hxx>
+#include <svx/svdetc.hxx>
+#include <editeng/outlobj.hxx>
+#include <editeng/eeitem.hxx>
+#include <basegfx/polygon/b2dpolypolygon.hxx>
+#include <basegfx/polygon/b2dpolypolygontools.hxx>
+#include <svx/strings.hrc>
+#include <svx/svdoashp.hxx>
+#include <basegfx/polygon/b2dpolypolygoncutter.hxx>
+#include <i18nutil/unicode.hxx>
+#include <sal/log.hxx>
+#include <tools/debug.hxx>
+#include <memory>
+#include <vector>
+#include <vcl/graph.hxx>
+#include <svx/svxids.hrc>
+#include <dstribut_enum.hxx>
+#include <osl/diagnose.h>
+
+using namespace com::sun::star;
+
+SdrObject* SdrEditView::GetMaxToTopObj(SdrObject* /*pObj*/) const
+{
+ return nullptr;
+}
+
+SdrObject* SdrEditView::GetMaxToBtmObj(SdrObject* /*pObj*/) const
+{
+ return nullptr;
+}
+
+void SdrEditView::ObjOrderChanged(SdrObject* /*pObj*/, size_t /*nOldPos*/, size_t /*nNewPos*/)
+{
+}
+
+void SdrEditView::MovMarkedToTop()
+{
+ const size_t nCount=GetMarkedObjectCount();
+ if (nCount==0)
+ return;
+
+ const bool bUndo = IsUndoEnabled();
+
+ if( bUndo )
+ BegUndo(SvxResId(STR_EditMovToTop),GetDescriptionOfMarkedObjects(),SdrRepeatFunc::MoveToTop);
+
+ SortMarkedObjects();
+ for (size_t nm=0; nm<nCount; ++nm)
+ { // All Ordnums have to be correct!
+ GetMarkedObjectByIndex(nm)->GetOrdNum();
+ }
+ bool bChg=false;
+ SdrObjList* pOL0=nullptr;
+ size_t nNewPos=0;
+ for (size_t nm=nCount; nm>0;)
+ {
+ --nm;
+ SdrMark* pM=GetSdrMarkByIndex(nm);
+ SdrObject* pObj=pM->GetMarkedSdrObj();
+ SdrObjList* pOL=pObj->getParentSdrObjListFromSdrObject();
+ if (pOL!=pOL0)
+ {
+ nNewPos = pOL->GetObjCount()-1;
+ pOL0=pOL;
+ }
+ const size_t nNowPos = pObj->GetOrdNumDirect();
+ const tools::Rectangle& rBR=pObj->GetCurrentBoundRect();
+ size_t nCmpPos = nNowPos+1;
+ SdrObject* pMaxObj=GetMaxToTopObj(pObj);
+ if (pMaxObj!=nullptr)
+ {
+ size_t nMaxPos=pMaxObj->GetOrdNum();
+ if (nMaxPos!=0)
+ nMaxPos--;
+ if (nNewPos>nMaxPos)
+ nNewPos=nMaxPos; // neither go faster...
+ if (nNewPos<nNowPos)
+ nNewPos=nNowPos; // nor go in the other direction
+ }
+ bool bEnd=false;
+ while (nCmpPos<nNewPos && !bEnd)
+ {
+ SdrObject* pCmpObj=pOL->GetObj(nCmpPos);
+ if (pCmpObj==nullptr)
+ {
+ OSL_FAIL("MovMarkedToTop(): Reference object not found.");
+ bEnd=true;
+ }
+ else if (pCmpObj==pMaxObj)
+ {
+ nNewPos=nCmpPos;
+ nNewPos--;
+ bEnd=true;
+ }
+ else if (rBR.Overlaps(pCmpObj->GetCurrentBoundRect()))
+ {
+ nNewPos=nCmpPos;
+ bEnd=true;
+ }
+ else
+ {
+ nCmpPos++;
+ }
+ }
+ if (nNowPos!=nNewPos)
+ {
+ bChg=true;
+ pOL->SetObjectOrdNum(nNowPos,nNewPos);
+ if( bUndo )
+ AddUndo(GetModel()->GetSdrUndoFactory().CreateUndoObjectOrdNum(*pObj,nNowPos,nNewPos));
+ ObjOrderChanged(pObj,nNowPos,nNewPos);
+ }
+ nNewPos--;
+ }
+
+ if( bUndo )
+ EndUndo();
+
+ if (bChg)
+ MarkListHasChanged();
+}
+
+void SdrEditView::MovMarkedToBtm()
+{
+ const size_t nCount=GetMarkedObjectCount();
+ if (nCount==0)
+ return;
+
+ const bool bUndo = IsUndoEnabled();
+
+ if( bUndo )
+ BegUndo(SvxResId(STR_EditMovToBtm),GetDescriptionOfMarkedObjects(),SdrRepeatFunc::MoveToBottom);
+
+ SortMarkedObjects();
+ for (size_t nm=0; nm<nCount; ++nm)
+ { // All Ordnums have to be correct!
+ GetMarkedObjectByIndex(nm)->GetOrdNum();
+ }
+
+ bool bChg=false;
+ SdrObjList* pOL0=nullptr;
+ size_t nNewPos=0;
+ for (size_t nm=0; nm<nCount; ++nm)
+ {
+ SdrMark* pM=GetSdrMarkByIndex(nm);
+ SdrObject* pObj=pM->GetMarkedSdrObj();
+ SdrObjList* pOL=pObj->getParentSdrObjListFromSdrObject();
+ if (pOL!=pOL0)
+ {
+ nNewPos=0;
+ pOL0=pOL;
+ }
+ const size_t nNowPos = pObj->GetOrdNumDirect();
+ const tools::Rectangle& rBR=pObj->GetCurrentBoundRect();
+ size_t nCmpPos = nNowPos;
+ if (nCmpPos>0)
+ --nCmpPos;
+ SdrObject* pMaxObj=GetMaxToBtmObj(pObj);
+ if (pMaxObj!=nullptr)
+ {
+ const size_t nMinPos=pMaxObj->GetOrdNum()+1;
+ if (nNewPos<nMinPos)
+ nNewPos=nMinPos; // neither go faster...
+ if (nNewPos>nNowPos)
+ nNewPos=nNowPos; // nor go in the other direction
+ }
+ bool bEnd=false;
+ // nNewPos in this case is the "maximum" position
+ // the object may reach without going faster than the object before
+ // it (multiple selection).
+ while (nCmpPos>nNewPos && !bEnd)
+ {
+ SdrObject* pCmpObj=pOL->GetObj(nCmpPos);
+ if (pCmpObj==nullptr)
+ {
+ OSL_FAIL("MovMarkedToBtm(): Reference object not found.");
+ bEnd=true;
+ }
+ else if (pCmpObj==pMaxObj)
+ {
+ nNewPos=nCmpPos;
+ nNewPos++;
+ bEnd=true;
+ }
+ else if (rBR.Overlaps(pCmpObj->GetCurrentBoundRect()))
+ {
+ nNewPos=nCmpPos;
+ bEnd=true;
+ }
+ else
+ {
+ nCmpPos--;
+ }
+ }
+ if (nNowPos!=nNewPos)
+ {
+ bChg=true;
+ pOL->SetObjectOrdNum(nNowPos,nNewPos);
+ if( bUndo )
+ AddUndo(GetModel()->GetSdrUndoFactory().CreateUndoObjectOrdNum(*pObj,nNowPos,nNewPos));
+ ObjOrderChanged(pObj,nNowPos,nNewPos);
+ }
+ nNewPos++;
+ }
+
+ if(bUndo)
+ EndUndo();
+
+ if(bChg)
+ MarkListHasChanged();
+}
+
+void SdrEditView::PutMarkedToTop()
+{
+ PutMarkedInFrontOfObj(nullptr);
+}
+
+void SdrEditView::PutMarkedInFrontOfObj(const SdrObject* pRefObj)
+{
+ const size_t nCount=GetMarkedObjectCount();
+ if (nCount==0)
+ return;
+
+ const bool bUndo = IsUndoEnabled();
+ if( bUndo )
+ BegUndo(SvxResId(STR_EditPutToTop),GetDescriptionOfMarkedObjects(),SdrRepeatFunc::PutToTop);
+
+ SortMarkedObjects();
+
+ if (pRefObj!=nullptr)
+ {
+ // Make "in front of the object" work, even if the
+ // selected objects are already in front of the other object
+ const size_t nRefMark=TryToFindMarkedObject(pRefObj);
+ SdrMark aRefMark;
+ if (nRefMark!=SAL_MAX_SIZE)
+ {
+ aRefMark=*GetSdrMarkByIndex(nRefMark);
+ GetMarkedObjectListWriteAccess().DeleteMark(nRefMark);
+ }
+ PutMarkedToBtm();
+ if (nRefMark!=SAL_MAX_SIZE)
+ {
+ GetMarkedObjectListWriteAccess().InsertEntry(aRefMark);
+ SortMarkedObjects();
+ }
+ }
+ for (size_t nm=0; nm<nCount; ++nm)
+ { // All Ordnums have to be correct!
+ GetMarkedObjectByIndex(nm)->GetOrdNum();
+ }
+ bool bChg=false;
+ SdrObjList* pOL0=nullptr;
+ size_t nNewPos=0;
+ for (size_t nm=nCount; nm>0;)
+ {
+ --nm;
+ SdrMark* pM=GetSdrMarkByIndex(nm);
+ SdrObject* pObj=pM->GetMarkedSdrObj();
+ if (pObj!=pRefObj)
+ {
+ SdrObjList* pOL=pObj->getParentSdrObjListFromSdrObject();
+ if (pOL!=pOL0)
+ {
+ nNewPos=pOL->GetObjCount()-1;
+ pOL0=pOL;
+ }
+ const size_t nNowPos=pObj->GetOrdNumDirect();
+ SdrObject* pMaxObj=GetMaxToTopObj(pObj);
+ if (pMaxObj!=nullptr)
+ {
+ size_t nMaxOrd=pMaxObj->GetOrdNum(); // sadly doesn't work any other way
+ if (nMaxOrd>0)
+ nMaxOrd--;
+ if (nNewPos>nMaxOrd)
+ nNewPos=nMaxOrd; // neither go faster...
+ if (nNewPos<nNowPos)
+ nNewPos=nNowPos; // nor go into the other direction
+ }
+ if (pRefObj!=nullptr)
+ {
+ if (pRefObj->getParentSdrObjListFromSdrObject()==pObj->getParentSdrObjListFromSdrObject())
+ {
+ const size_t nMaxOrd=pRefObj->GetOrdNum(); // sadly doesn't work any other way
+ if (nNewPos>nMaxOrd)
+ nNewPos=nMaxOrd; // neither go faster...
+ if (nNewPos<nNowPos)
+ nNewPos=nNowPos; // nor go into the other direction
+ }
+ else
+ {
+ nNewPos=nNowPos; // different PageView, so don't change
+ }
+ }
+ if (nNowPos!=nNewPos)
+ {
+ bChg=true;
+ pOL->SetObjectOrdNum(nNowPos,nNewPos);
+ if( bUndo )
+ AddUndo(GetModel()->GetSdrUndoFactory().CreateUndoObjectOrdNum(*pObj,nNowPos,nNewPos));
+ ObjOrderChanged(pObj,nNowPos,nNewPos);
+ }
+ nNewPos--;
+ } // if (pObj!=pRefObj)
+ } // for loop over all selected objects
+
+ if( bUndo )
+ EndUndo();
+
+ if(bChg)
+ MarkListHasChanged();
+}
+
+void SdrEditView::PutMarkedToBtm()
+{
+ PutMarkedBehindObj(nullptr);
+}
+
+void SdrEditView::PutMarkedBehindObj(const SdrObject* pRefObj)
+{
+ const size_t nCount=GetMarkedObjectCount();
+ if (nCount==0)
+ return;
+
+ const bool bUndo = IsUndoEnabled();
+
+ if( bUndo )
+ BegUndo(SvxResId(STR_EditPutToBtm),GetDescriptionOfMarkedObjects(),SdrRepeatFunc::PutToBottom);
+
+ SortMarkedObjects();
+ if (pRefObj!=nullptr)
+ {
+ // Make "behind the object" work, even if the
+ // selected objects are already behind the other object
+ const size_t nRefMark=TryToFindMarkedObject(pRefObj);
+ SdrMark aRefMark;
+ if (nRefMark!=SAL_MAX_SIZE)
+ {
+ aRefMark=*GetSdrMarkByIndex(nRefMark);
+ GetMarkedObjectListWriteAccess().DeleteMark(nRefMark);
+ }
+ PutMarkedToTop();
+ if (nRefMark!=SAL_MAX_SIZE)
+ {
+ GetMarkedObjectListWriteAccess().InsertEntry(aRefMark);
+ SortMarkedObjects();
+ }
+ }
+ for (size_t nm=0; nm<nCount; ++nm) { // All Ordnums have to be correct!
+ GetMarkedObjectByIndex(nm)->GetOrdNum();
+ }
+ bool bChg=false;
+ SdrObjList* pOL0=nullptr;
+ size_t nNewPos=0;
+ for (size_t nm=0; nm<nCount; ++nm) {
+ SdrMark* pM=GetSdrMarkByIndex(nm);
+ SdrObject* pObj=pM->GetMarkedSdrObj();
+ if (pObj!=pRefObj) {
+ SdrObjList* pOL=pObj->getParentSdrObjListFromSdrObject();
+ if (pOL!=pOL0) {
+ nNewPos=0;
+ pOL0=pOL;
+ }
+ const size_t nNowPos=pObj->GetOrdNumDirect();
+ SdrObject* pMinObj=GetMaxToBtmObj(pObj);
+ if (pMinObj!=nullptr) {
+ const size_t nMinOrd=pMinObj->GetOrdNum()+1; // sadly doesn't work any differently
+ if (nNewPos<nMinOrd) nNewPos=nMinOrd; // neither go faster...
+ if (nNewPos>nNowPos) nNewPos=nNowPos; // nor go into the other direction
+ }
+ if (pRefObj!=nullptr) {
+ if (pRefObj->getParentSdrObjListFromSdrObject()==pObj->getParentSdrObjListFromSdrObject()) {
+ const size_t nMinOrd=pRefObj->GetOrdNum(); // sadly doesn't work any differently
+ if (nNewPos<nMinOrd) nNewPos=nMinOrd; // neither go faster...
+ if (nNewPos>nNowPos) nNewPos=nNowPos; // nor go into the other direction
+ } else {
+ nNewPos=nNowPos; // different PageView, so don't change
+ }
+ }
+ if (nNowPos!=nNewPos) {
+ bChg=true;
+ pOL->SetObjectOrdNum(nNowPos,nNewPos);
+ if( bUndo )
+ AddUndo(GetModel()->GetSdrUndoFactory().CreateUndoObjectOrdNum(*pObj,nNowPos,nNewPos));
+ ObjOrderChanged(pObj,nNowPos,nNewPos);
+ }
+ nNewPos++;
+ } // if (pObj!=pRefObj)
+ } // for loop over all selected objects
+
+ if(bUndo)
+ EndUndo();
+
+ if(bChg)
+ MarkListHasChanged();
+
+}
+
+void SdrEditView::ReverseOrderOfMarked()
+{
+ SortMarkedObjects();
+ const size_t nMarkCount=GetMarkedObjectCount();
+ if (nMarkCount<=0)
+ return;
+
+ bool bChg=false;
+
+ bool bUndo = IsUndoEnabled();
+ if( bUndo )
+ BegUndo(SvxResId(STR_EditRevOrder),GetDescriptionOfMarkedObjects(),SdrRepeatFunc::ReverseOrder);
+
+ size_t a=0;
+ do {
+ // take into account selection across multiple PageViews
+ size_t b=a+1;
+ while (b<nMarkCount && GetSdrPageViewOfMarkedByIndex(b) == GetSdrPageViewOfMarkedByIndex(a)) ++b;
+ --b;
+ SdrObjList* pOL=GetSdrPageViewOfMarkedByIndex(a)->GetObjList();
+ size_t c=b;
+ if (a<c) { // make sure OrdNums aren't dirty
+ GetMarkedObjectByIndex(a)->GetOrdNum();
+ }
+ while (a<c) {
+ SdrObject* pObj1=GetMarkedObjectByIndex(a);
+ SdrObject* pObj2=GetMarkedObjectByIndex(c);
+ const size_t nOrd1=pObj1->GetOrdNumDirect();
+ const size_t nOrd2=pObj2->GetOrdNumDirect();
+ if( bUndo )
+ {
+ AddUndo(GetModel()->GetSdrUndoFactory().CreateUndoObjectOrdNum(*pObj1,nOrd1,nOrd2));
+ AddUndo(GetModel()->GetSdrUndoFactory().CreateUndoObjectOrdNum(*pObj2,nOrd2-1,nOrd1));
+ }
+ pOL->SetObjectOrdNum(nOrd1,nOrd2);
+ // Obj 2 has moved forward by one position, so now nOrd2-1
+ pOL->SetObjectOrdNum(nOrd2-1,nOrd1);
+ // use Replace instead of SetOrdNum for performance reasons (recalculation of Ordnums)
+ ++a;
+ --c;
+ bChg=true;
+ }
+ a=b+1;
+ } while (a<nMarkCount);
+
+ if(bUndo)
+ EndUndo();
+
+ if(bChg)
+ MarkListHasChanged();
+}
+
+void SdrEditView::ImpCheckToTopBtmPossible()
+{
+ const size_t nCount=GetMarkedObjectCount();
+ if (nCount==0)
+ return;
+ if (nCount==1)
+ { // special-casing for single selection
+ SdrObject* pObj=GetMarkedObjectByIndex(0);
+ SdrObjList* pOL=pObj->getParentSdrObjListFromSdrObject();
+ SAL_WARN_IF(!pOL, "svx", "Object somehow has no ObjList");
+ size_t nMax = pOL ? pOL->GetObjCount() : 0;
+ size_t nMin = 0;
+ const size_t nObjNum=pObj->GetOrdNum();
+ SdrObject* pRestrict=GetMaxToTopObj(pObj);
+ if (pRestrict!=nullptr) {
+ const size_t nRestrict=pRestrict->GetOrdNum();
+ if (nRestrict<nMax) nMax=nRestrict;
+ }
+ pRestrict=GetMaxToBtmObj(pObj);
+ if (pRestrict!=nullptr) {
+ const size_t nRestrict=pRestrict->GetOrdNum();
+ if (nRestrict>nMin) nMin=nRestrict;
+ }
+ m_bToTopPossible=nObjNum<nMax-1;
+ m_bToBtmPossible=nObjNum>nMin;
+ } else { // multiple selection
+ SdrObjList* pOL0=nullptr;
+ size_t nPos0 = 0;
+ for (size_t nm = 0; !m_bToBtmPossible && nm<nCount; ++nm) { // check 'send to background'
+ SdrObject* pObj=GetMarkedObjectByIndex(nm);
+ SdrObjList* pOL=pObj->getParentSdrObjListFromSdrObject();
+ if (pOL!=pOL0) {
+ nPos0 = 0;
+ pOL0=pOL;
+ }
+ const size_t nPos = pObj->GetOrdNum();
+ m_bToBtmPossible = nPos && (nPos-1 > nPos0);
+ nPos0 = nPos;
+ }
+
+ pOL0=nullptr;
+ nPos0 = SAL_MAX_SIZE;
+ for (size_t nm=nCount; !m_bToTopPossible && nm>0; ) { // check 'bring to front'
+ --nm;
+ SdrObject* pObj=GetMarkedObjectByIndex(nm);
+ SdrObjList* pOL=pObj->getParentSdrObjListFromSdrObject();
+ if (pOL!=pOL0) {
+ nPos0=pOL->GetObjCount();
+ pOL0=pOL;
+ }
+ const size_t nPos = pObj->GetOrdNum();
+ m_bToTopPossible = nPos+1 < nPos0;
+ nPos0=nPos;
+ }
+ }
+}
+
+
+// Combine
+
+
+void SdrEditView::ImpCopyAttributes(const SdrObject* pSource, SdrObject* pDest) const
+{
+ if (pSource!=nullptr) {
+ SdrObjList* pOL=pSource->GetSubList();
+ if (pOL!=nullptr && !pSource->Is3DObj()) { // get first non-group object from group
+ SdrObjListIter aIter(pOL,SdrIterMode::DeepNoGroups);
+ pSource=aIter.Next();
+ }
+ }
+
+ if(!(pSource && pDest))
+ return;
+
+ SfxItemSetFixed<SDRATTR_START, SDRATTR_NOTPERSIST_FIRST-1,
+ SDRATTR_NOTPERSIST_LAST+1, SDRATTR_END,
+ EE_ITEMS_START, EE_ITEMS_END> aSet(mpModel->GetItemPool());
+
+ aSet.Put(pSource->GetMergedItemSet());
+
+ pDest->ClearMergedItem();
+ pDest->SetMergedItemSet(aSet);
+
+ pDest->NbcSetLayer(pSource->GetLayer());
+ pDest->NbcSetStyleSheet(pSource->GetStyleSheet(), true);
+}
+
+bool SdrEditView::ImpCanConvertForCombine1(const SdrObject* pObj)
+{
+ // new condition IsLine() to be able to combine simple Lines
+ bool bIsLine(false);
+
+ const SdrPathObj* pPath = dynamic_cast< const SdrPathObj*>( pObj );
+
+ if(pPath)
+ {
+ bIsLine = pPath->IsLine();
+ }
+
+ SdrObjTransformInfoRec aInfo;
+ pObj->TakeObjInfo(aInfo);
+
+ return (aInfo.bCanConvToPath || aInfo.bCanConvToPoly || bIsLine);
+}
+
+bool SdrEditView::ImpCanConvertForCombine(const SdrObject* pObj)
+{
+ SdrObjList* pOL = pObj->GetSubList();
+
+ if(pOL && !pObj->Is3DObj())
+ {
+ SdrObjListIter aIter(pOL, SdrIterMode::DeepNoGroups);
+
+ while(aIter.IsMore())
+ {
+ SdrObject* pObj1 = aIter.Next();
+
+ // all members of a group have to be convertible
+ if(!ImpCanConvertForCombine1(pObj1))
+ {
+ return false;
+ }
+ }
+ }
+ else
+ {
+ if(!ImpCanConvertForCombine1(pObj))
+ {
+ return false;
+ }
+ }
+
+ return true;
+}
+
+basegfx::B2DPolyPolygon SdrEditView::ImpGetPolyPolygon1(const SdrObject* pObj)
+{
+ basegfx::B2DPolyPolygon aRetval;
+ const SdrPathObj* pPath = dynamic_cast<const SdrPathObj*>( pObj );
+
+ if(pPath && !pObj->GetOutlinerParaObject())
+ {
+ aRetval = pPath->GetPathPoly();
+ }
+ else
+ {
+ SdrObjectUniquePtr pConvObj = pObj->ConvertToPolyObj(true/*bCombine*/, false);
+
+ if(pConvObj)
+ {
+ SdrObjList* pOL = pConvObj->GetSubList();
+
+ if(pOL)
+ {
+ SdrObjListIter aIter(pOL, SdrIterMode::DeepNoGroups);
+
+ while(aIter.IsMore())
+ {
+ SdrObject* pObj1 = aIter.Next();
+ pPath = dynamic_cast<SdrPathObj*>( pObj1 );
+
+ if(pPath)
+ {
+ aRetval.append(pPath->GetPathPoly());
+ }
+ }
+ }
+ else
+ {
+ pPath = dynamic_cast<SdrPathObj*>( pConvObj.get() );
+
+ if(pPath)
+ {
+ aRetval = pPath->GetPathPoly();
+ }
+ }
+ }
+ }
+
+ return aRetval;
+}
+
+basegfx::B2DPolyPolygon SdrEditView::ImpGetPolyPolygon(const SdrObject* pObj)
+{
+ SdrObjList* pOL = pObj->GetSubList();
+
+ if(pOL && !pObj->Is3DObj())
+ {
+ basegfx::B2DPolyPolygon aRetval;
+ SdrObjListIter aIter(pOL, SdrIterMode::DeepNoGroups);
+
+ while(aIter.IsMore())
+ {
+ SdrObject* pObj1 = aIter.Next();
+ aRetval.append(ImpGetPolyPolygon1(pObj1));
+ }
+
+ return aRetval;
+ }
+ else
+ {
+ return ImpGetPolyPolygon1(pObj);
+ }
+}
+
+basegfx::B2DPolygon SdrEditView::ImpCombineToSinglePolygon(const basegfx::B2DPolyPolygon& rPolyPolygon)
+{
+ const sal_uInt32 nPolyCount(rPolyPolygon.count());
+
+ if(0 == nPolyCount)
+ {
+ return basegfx::B2DPolygon();
+ }
+ else if(1 == nPolyCount)
+ {
+ return rPolyPolygon.getB2DPolygon(0);
+ }
+ else
+ {
+ basegfx::B2DPolygon aRetval(rPolyPolygon.getB2DPolygon(0));
+
+ for(sal_uInt32 a(1); a < nPolyCount; a++)
+ {
+ basegfx::B2DPolygon aCandidate(rPolyPolygon.getB2DPolygon(a));
+
+ if(aRetval.count())
+ {
+ if(aCandidate.count())
+ {
+ const basegfx::B2DPoint aCA(aCandidate.getB2DPoint(0));
+ const basegfx::B2DPoint aCB(aCandidate.getB2DPoint(aCandidate.count() - 1));
+ const basegfx::B2DPoint aRA(aRetval.getB2DPoint(0));
+ const basegfx::B2DPoint aRB(aRetval.getB2DPoint(aRetval.count() - 1));
+
+ const double fRACA(basegfx::B2DVector(aCA - aRA).getLength());
+ const double fRACB(basegfx::B2DVector(aCB - aRA).getLength());
+ const double fRBCA(basegfx::B2DVector(aCA - aRB).getLength());
+ const double fRBCB(basegfx::B2DVector(aCB - aRB).getLength());
+
+ const double fSmallestRA(std::min(fRACA, fRACB));
+ const double fSmallestRB(std::min(fRBCA, fRBCB));
+
+ if(fSmallestRA < fSmallestRB)
+ {
+ // flip result
+ aRetval.flip();
+ }
+
+ const double fSmallestCA(std::min(fRACA, fRBCA));
+ const double fSmallestCB(std::min(fRACB, fRBCB));
+
+ if(fSmallestCB < fSmallestCA)
+ {
+ // flip candidate
+ aCandidate.flip();
+ }
+
+ // append candidate to retval
+ aRetval.append(aCandidate);
+ }
+ }
+ else
+ {
+ aRetval = aCandidate;
+ }
+ }
+
+ return aRetval;
+ }
+}
+
+namespace {
+
+// for distribution dialog function
+struct ImpDistributeEntry
+{
+ SdrObject* mpObj;
+ sal_Int32 mnPos;
+ sal_Int32 mnLength;
+};
+
+}
+
+typedef std::vector<ImpDistributeEntry> ImpDistributeEntryList;
+
+void SdrEditView::DistributeMarkedObjects(sal_uInt16 SlotID)
+{
+ const size_t nMark(GetMarkedObjectCount());
+
+ if(nMark <= 2)
+ return;
+
+ SvxDistributeHorizontal eHor = SvxDistributeHorizontal::NONE;
+ SvxDistributeVertical eVer = SvxDistributeVertical::NONE;
+
+ switch (SlotID)
+ {
+ case SID_DISTRIBUTE_HLEFT: eHor = SvxDistributeHorizontal::Left; break;
+ case SID_DISTRIBUTE_HCENTER: eHor = SvxDistributeHorizontal::Center; break;
+ case SID_DISTRIBUTE_HDISTANCE: eHor = SvxDistributeHorizontal::Distance; break;
+ case SID_DISTRIBUTE_HRIGHT: eHor = SvxDistributeHorizontal::Right; break;
+ case SID_DISTRIBUTE_VTOP: eVer = SvxDistributeVertical::Top; break;
+ case SID_DISTRIBUTE_VCENTER: eVer = SvxDistributeVertical::Center; break;
+ case SID_DISTRIBUTE_VDISTANCE: eVer = SvxDistributeVertical::Distance; break;
+ case SID_DISTRIBUTE_VBOTTOM: eVer = SvxDistributeVertical::Bottom; break;
+ }
+
+ ImpDistributeEntryList aEntryList;
+ ImpDistributeEntryList::iterator itEntryList;
+ sal_uInt32 nFullLength;
+
+ const bool bUndo = IsUndoEnabled();
+ if( bUndo )
+ BegUndo();
+
+ if(eHor != SvxDistributeHorizontal::NONE)
+ {
+ // build sorted entry list
+ nFullLength = 0;
+
+ for( size_t a = 0; a < nMark; ++a )
+ {
+ SdrMark* pMark = GetSdrMarkByIndex(a);
+ ImpDistributeEntry aNew;
+
+ aNew.mpObj = pMark->GetMarkedSdrObj();
+
+ switch(eHor)
+ {
+ case SvxDistributeHorizontal::Left:
+ {
+ aNew.mnPos = aNew.mpObj->GetSnapRect().Left();
+ break;
+ }
+ case SvxDistributeHorizontal::Center:
+ {
+ aNew.mnPos = (aNew.mpObj->GetSnapRect().Right() + aNew.mpObj->GetSnapRect().Left()) / 2;
+ break;
+ }
+ case SvxDistributeHorizontal::Distance:
+ {
+ aNew.mnLength = aNew.mpObj->GetSnapRect().GetWidth() + 1;
+ nFullLength += aNew.mnLength;
+ aNew.mnPos = (aNew.mpObj->GetSnapRect().Right() + aNew.mpObj->GetSnapRect().Left()) / 2;
+ break;
+ }
+ case SvxDistributeHorizontal::Right:
+ {
+ aNew.mnPos = aNew.mpObj->GetSnapRect().Right();
+ break;
+ }
+ default: break;
+ }
+
+ itEntryList = std::find_if(aEntryList.begin(), aEntryList.end(),
+ [&aNew](const ImpDistributeEntry& rEntry) { return rEntry.mnPos >= aNew.mnPos; });
+ if ( itEntryList < aEntryList.end() )
+ aEntryList.insert( itEntryList, aNew );
+ else
+ aEntryList.push_back( aNew );
+ }
+
+ if(eHor == SvxDistributeHorizontal::Distance)
+ {
+ // calculate room in-between
+ sal_Int32 nWidth = GetAllMarkedBoundRect().GetWidth() + 1;
+ double fStepWidth = (static_cast<double>(nWidth) - static_cast<double>(nFullLength)) / static_cast<double>(aEntryList.size() - 1);
+ double fStepStart = static_cast<double>(aEntryList[ 0 ].mnPos);
+ fStepStart += fStepWidth + static_cast<double>((aEntryList[ 0 ].mnLength + aEntryList[ 1 ].mnLength) / 2);
+
+ // move entries 1..n-1
+ for( size_t i = 1, n = aEntryList.size()-1; i < n; ++i )
+ {
+ ImpDistributeEntry& rCurr = aEntryList[ i ];
+ ImpDistributeEntry& rNext = aEntryList[ i + 1];
+ sal_Int32 nDelta = static_cast<sal_Int32>(fStepStart + 0.5) - rCurr.mnPos;
+ if( bUndo )
+ AddUndo(GetModel()->GetSdrUndoFactory().CreateUndoGeoObject(*rCurr.mpObj));
+ rCurr.mpObj->Move(Size(nDelta, 0));
+ fStepStart += fStepWidth + static_cast<double>((rCurr.mnLength + rNext.mnLength) / 2);
+ }
+ }
+ else
+ {
+ // calculate distances
+ sal_Int32 nWidth = aEntryList[ aEntryList.size() - 1 ].mnPos - aEntryList[ 0 ].mnPos;
+ double fStepWidth = static_cast<double>(nWidth) / static_cast<double>(aEntryList.size() - 1);
+ double fStepStart = static_cast<double>(aEntryList[ 0 ].mnPos);
+ fStepStart += fStepWidth;
+
+ // move entries 1..n-1
+ for( size_t i = 1 ; i < aEntryList.size()-1 ; ++i )
+ {
+ ImpDistributeEntry& rCurr = aEntryList[ i ];
+ sal_Int32 nDelta = static_cast<sal_Int32>(fStepStart + 0.5) - rCurr.mnPos;
+ if( bUndo )
+ AddUndo(GetModel()->GetSdrUndoFactory().CreateUndoGeoObject(*rCurr.mpObj));
+ rCurr.mpObj->Move(Size(nDelta, 0));
+ fStepStart += fStepWidth;
+ }
+ }
+
+ // clear list
+ aEntryList.clear();
+ }
+
+ if(eVer != SvxDistributeVertical::NONE)
+ {
+ // build sorted entry list
+ nFullLength = 0;
+
+ for( size_t a = 0; a < nMark; ++a )
+ {
+ SdrMark* pMark = GetSdrMarkByIndex(a);
+ ImpDistributeEntry aNew;
+
+ aNew.mpObj = pMark->GetMarkedSdrObj();
+
+ switch(eVer)
+ {
+ case SvxDistributeVertical::Top:
+ {
+ aNew.mnPos = aNew.mpObj->GetSnapRect().Top();
+ break;
+ }
+ case SvxDistributeVertical::Center:
+ {
+ aNew.mnPos = (aNew.mpObj->GetSnapRect().Bottom() + aNew.mpObj->GetSnapRect().Top()) / 2;
+ break;
+ }
+ case SvxDistributeVertical::Distance:
+ {
+ aNew.mnLength = aNew.mpObj->GetSnapRect().GetHeight() + 1;
+ nFullLength += aNew.mnLength;
+ aNew.mnPos = (aNew.mpObj->GetSnapRect().Bottom() + aNew.mpObj->GetSnapRect().Top()) / 2;
+ break;
+ }
+ case SvxDistributeVertical::Bottom:
+ {
+ aNew.mnPos = aNew.mpObj->GetSnapRect().Bottom();
+ break;
+ }
+ default: break;
+ }
+
+ itEntryList = std::find_if(aEntryList.begin(), aEntryList.end(),
+ [&aNew](const ImpDistributeEntry& rEntry) { return rEntry.mnPos >= aNew.mnPos; });
+ if ( itEntryList < aEntryList.end() )
+ aEntryList.insert( itEntryList, aNew );
+ else
+ aEntryList.push_back( aNew );
+ }
+
+ if(eVer == SvxDistributeVertical::Distance)
+ {
+ // calculate room in-between
+ sal_Int32 nHeight = GetAllMarkedBoundRect().GetHeight() + 1;
+ double fStepWidth = (static_cast<double>(nHeight) - static_cast<double>(nFullLength)) / static_cast<double>(aEntryList.size() - 1);
+ double fStepStart = static_cast<double>(aEntryList[ 0 ].mnPos);
+ fStepStart += fStepWidth + static_cast<double>((aEntryList[ 0 ].mnLength + aEntryList[ 1 ].mnLength) / 2);
+
+ // move entries 1..n-1
+ for( size_t i = 1, n = aEntryList.size()-1; i < n; ++i)
+ {
+ ImpDistributeEntry& rCurr = aEntryList[ i ];
+ ImpDistributeEntry& rNext = aEntryList[ i + 1 ];
+ sal_Int32 nDelta = static_cast<sal_Int32>(fStepStart + 0.5) - rCurr.mnPos;
+ if( bUndo )
+ AddUndo(GetModel()->GetSdrUndoFactory().CreateUndoGeoObject(*rCurr.mpObj));
+ rCurr.mpObj->Move(Size(0, nDelta));
+ fStepStart += fStepWidth + static_cast<double>((rCurr.mnLength + rNext.mnLength) / 2);
+ }
+ }
+ else
+ {
+ // calculate distances
+ sal_Int32 nHeight = aEntryList[ aEntryList.size() - 1 ].mnPos - aEntryList[ 0 ].mnPos;
+ double fStepWidth = static_cast<double>(nHeight) / static_cast<double>(aEntryList.size() - 1);
+ double fStepStart = static_cast<double>(aEntryList[ 0 ].mnPos);
+ fStepStart += fStepWidth;
+
+ // move entries 1..n-1
+ for(size_t i = 1, n = aEntryList.size()-1; i < n; ++i)
+ {
+ ImpDistributeEntry& rCurr = aEntryList[ i ];
+ sal_Int32 nDelta = static_cast<sal_Int32>(fStepStart + 0.5) - rCurr.mnPos;
+ if( bUndo )
+ AddUndo(GetModel()->GetSdrUndoFactory().CreateUndoGeoObject(*rCurr.mpObj));
+ rCurr.mpObj->Move(Size(0, nDelta));
+ fStepStart += fStepWidth;
+ }
+ }
+
+ // clear list
+ aEntryList.clear();
+ }
+
+ // UNDO-Comment and end of UNDO
+ mpModel->SetUndoComment(SvxResId(STR_DistributeMarkedObjects));
+
+ if( bUndo )
+ EndUndo();
+}
+
+void SdrEditView::MergeMarkedObjects(SdrMergeMode eMode)
+{
+ // #i73441# check content
+ if(!AreObjectsMarked())
+ return;
+
+ SdrMarkList aRemove;
+ SortMarkedObjects();
+
+ const bool bUndo = IsUndoEnabled();
+
+ if( bUndo )
+ BegUndo();
+
+ size_t nInsPos = SAL_MAX_SIZE;
+ const SdrObject* pAttrObj = nullptr;
+ basegfx::B2DPolyPolygon aMergePolyPolygonA;
+ basegfx::B2DPolyPolygon aMergePolyPolygonB;
+
+ SdrObjList* pInsOL = nullptr;
+ SdrPageView* pInsPV = nullptr;
+ bool bFirstObjectComplete(false);
+
+ // make sure selected objects are contour objects
+ // since now basegfx::utils::adaptiveSubdivide() is used, it is no longer
+ // necessary to use ConvertMarkedToPolyObj which will subdivide curves using the old
+ // mechanisms. In a next step the polygon clipper will even be able to clip curves...
+ // ConvertMarkedToPolyObj(true);
+ ConvertMarkedToPathObj(true);
+ OSL_ENSURE(AreObjectsMarked(), "no more objects selected after preparations (!)");
+
+ for(size_t a=0; a<GetMarkedObjectCount(); ++a)
+ {
+ SdrMark* pM = GetSdrMarkByIndex(a);
+ SdrObject* pObj = pM->GetMarkedSdrObj();
+
+ if(ImpCanConvertForCombine(pObj))
+ {
+ if(!pAttrObj)
+ pAttrObj = pObj;
+
+ nInsPos = pObj->GetOrdNum() + 1;
+ pInsPV = pM->GetPageView();
+ pInsOL = pObj->getParentSdrObjListFromSdrObject();
+
+ // #i76891# use single iteration from SJ here which works on SdrObjects and takes
+ // groups into account by itself
+ SdrObjListIter aIter(*pObj, SdrIterMode::DeepWithGroups);
+
+ while(aIter.IsMore())
+ {
+ SdrObject* pCandidate = aIter.Next();
+ SdrPathObj* pPathObj = dynamic_cast<SdrPathObj*>( pCandidate );
+ if(pPathObj)
+ {
+ basegfx::B2DPolyPolygon aTmpPoly(pPathObj->GetPathPoly());
+
+ // #i76891# unfortunately ConvertMarkedToPathObj has converted all
+ // involved polygon data to curve segments, even if not necessary.
+ // It is better to try to reduce to more simple polygons.
+ aTmpPoly = basegfx::utils::simplifyCurveSegments(aTmpPoly);
+
+ // for each part polygon as preparation, remove self-intersections
+ // correct orientations and get rid of possible neutral polygons.
+ aTmpPoly = basegfx::utils::prepareForPolygonOperation(aTmpPoly);
+
+ if(!bFirstObjectComplete)
+ {
+ // #i111987# Also need to collect ORed source shape when more than
+ // a single polygon is involved
+ if(aMergePolyPolygonA.count())
+ {
+ aMergePolyPolygonA = basegfx::utils::solvePolygonOperationOr(aMergePolyPolygonA, aTmpPoly);
+ }
+ else
+ {
+ aMergePolyPolygonA = aTmpPoly;
+ }
+ }
+ else
+ {
+ if(aMergePolyPolygonB.count())
+ {
+ // to topologically correctly collect the 2nd polygon
+ // group it is necessary to OR the parts (each is seen as
+ // XOR-FillRule polygon and they are drawn over each-other)
+ aMergePolyPolygonB = basegfx::utils::solvePolygonOperationOr(aMergePolyPolygonB, aTmpPoly);
+ }
+ else
+ {
+ aMergePolyPolygonB = aTmpPoly;
+ }
+ }
+ }
+ }
+
+ // was there something added to the first polygon?
+ if(!bFirstObjectComplete && aMergePolyPolygonA.count())
+ {
+ bFirstObjectComplete = true;
+ }
+
+ // move object to temporary delete list
+ aRemove.InsertEntry(SdrMark(pObj, pM->GetPageView()));
+ }
+ }
+
+ switch(eMode)
+ {
+ case SdrMergeMode::Merge:
+ {
+ // merge all contained parts (OR)
+ aMergePolyPolygonA = basegfx::utils::solvePolygonOperationOr(aMergePolyPolygonA, aMergePolyPolygonB);
+ break;
+ }
+ case SdrMergeMode::Subtract:
+ {
+ // Subtract B from A
+ aMergePolyPolygonA = basegfx::utils::solvePolygonOperationDiff(aMergePolyPolygonA, aMergePolyPolygonB);
+ break;
+ }
+ case SdrMergeMode::Intersect:
+ {
+ // AND B and A
+ aMergePolyPolygonA = basegfx::utils::solvePolygonOperationAnd(aMergePolyPolygonA, aMergePolyPolygonB);
+ break;
+ }
+ }
+
+ // #i73441# check insert list before taking actions
+ if(pInsOL)
+ {
+ SdrPathObj* pPath = new SdrPathObj(pAttrObj->getSdrModelFromSdrObject(), SdrObjKind::PathFill, aMergePolyPolygonA);
+ ImpCopyAttributes(pAttrObj, pPath);
+ pInsOL->InsertObject(pPath, nInsPos);
+ if( bUndo )
+ AddUndo(GetModel()->GetSdrUndoFactory().CreateUndoNewObject(*pPath));
+
+ // #i124760# To have a correct selection with only the new object it is necessary to
+ // unmark all objects first. If not doing so, there may remain invalid pointers to objects
+ // TTTT:Not needed for aw080 (!)
+ UnmarkAllObj(pInsPV);
+
+ MarkObj(pPath, pInsPV, false, true);
+ }
+
+ aRemove.ForceSort();
+ switch(eMode)
+ {
+ case SdrMergeMode::Merge:
+ {
+ SetUndoComment(
+ SvxResId(STR_EditMergeMergePoly),
+ aRemove.GetMarkDescription());
+ break;
+ }
+ case SdrMergeMode::Subtract:
+ {
+ SetUndoComment(
+ SvxResId(STR_EditMergeSubstractPoly),
+ aRemove.GetMarkDescription());
+ break;
+ }
+ case SdrMergeMode::Intersect:
+ {
+ SetUndoComment(
+ SvxResId(STR_EditMergeIntersectPoly),
+ aRemove.GetMarkDescription());
+ break;
+ }
+ }
+ DeleteMarkedList(aRemove);
+
+ if( bUndo )
+ EndUndo();
+}
+
+void SdrEditView::EqualizeMarkedObjects(bool bWidth)
+{
+ const SdrMarkList& rMarkList = GetMarkedObjectList();
+ size_t nMarked = rMarkList.GetMarkCount();
+
+ if (nMarked < 2)
+ return;
+
+ size_t nLastSelected = 0;
+ sal_Int64 nLastSelectedTime = rMarkList.GetMark(0)->getTimeStamp();
+ for (size_t a = 1; a < nMarked; ++a)
+ {
+ sal_Int64 nCandidateTime = rMarkList.GetMark(a)->getTimeStamp();
+ if (nCandidateTime > nLastSelectedTime)
+ {
+ nLastSelectedTime = nCandidateTime;
+ nLastSelected = a;
+ }
+ }
+
+ SdrObject* pLastSelectedObj = rMarkList.GetMark(nLastSelected)->GetMarkedSdrObj();
+ Size aLastRectSize(pLastSelectedObj->GetLogicRect().GetSize());
+
+ const bool bUndo = IsUndoEnabled();
+
+ if (bUndo)
+ BegUndo();
+
+ for (size_t a = 0; a < nMarked; ++a)
+ {
+ if (a == nLastSelected)
+ continue;
+ SdrMark* pM = rMarkList.GetMark(a);
+ SdrObject* pObj = pM->GetMarkedSdrObj();
+ tools::Rectangle aLogicRect(pObj->GetLogicRect());
+ Size aLogicRectSize(aLogicRect.GetSize());
+ if (bWidth)
+ aLogicRectSize.setWidth( aLastRectSize.Width() );
+ else
+ aLogicRectSize.setHeight( aLastRectSize.Height() );
+ aLogicRect.SetSize(aLogicRectSize);
+ if (bUndo)
+ AddUndo(GetModel()->GetSdrUndoFactory().CreateUndoGeoObject(*pObj));
+ pObj->SetLogicRect(aLogicRect);
+ }
+
+ SetUndoComment(
+ SvxResId(bWidth ? STR_EqualizeWidthMarkedObjects : STR_EqualizeHeightMarkedObjects),
+ rMarkList.GetMarkDescription());
+
+ if (bUndo)
+ EndUndo();
+}
+
+void SdrEditView::CombineMarkedTextObjects()
+{
+ SdrPageView* pPageView = GetSdrPageView();
+ if ( !pPageView || pPageView->IsLayerLocked( GetActiveLayer() ) )
+ return;
+
+ bool bUndo = IsUndoEnabled();
+
+ // Undo-String will be set later
+ if ( bUndo )
+ BegUndo();
+
+ SdrOutliner& rDrawOutliner = getSdrModelFromSdrView().GetDrawOutliner();
+
+ SdrObjListIter aIter( GetMarkedObjectList(), SdrIterMode::Flat);
+ while ( aIter.IsMore() )
+ {
+ SdrObject* pObj = aIter.Next();
+ SdrTextObj* pTextObj = dynamic_cast<SdrTextObj*>( pObj );
+ const OutlinerParaObject* pOPO = pTextObj ? pTextObj->GetOutlinerParaObject() : nullptr;
+ if ( pOPO && pTextObj->IsTextFrame()
+ && pTextObj->GetObjIdentifier() == SdrObjKind::Text // not callouts (OBJ_CAPTION)
+ && !pTextObj->IsOutlText() // not impress presentation objects
+ && pTextObj->GetMergedItem(XATTR_FORMTXTSTYLE).GetValue() == XFormTextStyle::NONE // not Fontwork
+ )
+ {
+ // if the last paragraph does not end in paragraph-end punctuation (ignoring whitespace),
+ // assume this text should be added to the end of the last paragraph, instead of starting a new paragraph.
+ const sal_Int32 nPara = rDrawOutliner.GetParagraphCount();
+ const OUString sLastPara = nPara ? rDrawOutliner.GetText( rDrawOutliner.GetParagraph( nPara - 1 ) ) : "";
+ sal_Int32 n = sLastPara.getLength();
+ while ( n && unicode::isWhiteSpace( sLastPara[--n] ) )
+ ;
+ //TODO: find way to use Locale to identify sentence final punctuation. Copied IsSentenceAtEnd() from autofmt.cxx
+ const bool bAppend = !n || ( sLastPara[n] != '.' && sLastPara[n] != '?' && sLastPara[n] != '!' );
+ rDrawOutliner.AddText( *pOPO, bAppend );
+ }
+ else
+ {
+ // Unmark non-textboxes, because all marked objects are deleted at the end. AdjustMarkHdl later.
+ MarkObj(pObj, pPageView, /*bUnmark=*/true, /*bImpNoSetMarkHdl=*/true);
+ }
+ }
+
+ MarkListHasChanged();
+ AdjustMarkHdl();
+
+ if ( GetMarkedObjectCount() > 1 )
+ {
+ SdrRectObj* pReplacement = new SdrRectObj( getSdrModelFromSdrView(), SdrObjKind::Text );
+ pReplacement->SetOutlinerParaObject( rDrawOutliner.CreateParaObject() );
+ pReplacement->SetSnapRect( GetMarkedObjRect() );
+
+ const SdrInsertFlags nFlags = SdrInsertFlags::DONTMARK | SdrInsertFlags::SETDEFLAYER;
+ if ( InsertObjectAtView( pReplacement, *pPageView, nFlags ) )
+ DeleteMarkedObj();
+ }
+
+ if ( bUndo )
+ EndUndo();
+
+ return;
+}
+
+void SdrEditView::CombineMarkedObjects(bool bNoPolyPoly)
+{
+ // #105899# Start of Combine-Undo put to front, else ConvertMarkedToPolyObj would
+ // create a 2nd Undo-action and Undo-Comment.
+
+ bool bUndo = IsUndoEnabled();
+
+ // Undo-String will be set later
+ if( bUndo )
+ BegUndo("", "", bNoPolyPoly ? SdrRepeatFunc::CombineOnePoly : SdrRepeatFunc::CombinePolyPoly);
+
+ // #105899# First, guarantee that all objects are converted to polyobjects,
+ // especially for SdrGrafObj with bitmap filling this is necessary to not
+ // lose the bitmap filling.
+
+ // #i12392#
+ // ConvertMarkedToPolyObj was too strong here, it will lose quality and
+ // information when curve objects are combined. This can be replaced by
+ // using ConvertMarkedToPathObj without changing the previous fix.
+
+ // #i21250#
+ // Instead of simply passing true as LineToArea, use bNoPolyPoly as info
+ // if this command is a 'Combine' or a 'Connect' command. On Connect it's true.
+ // To not concert line segments with a set line width to polygons in that case,
+ // use this info. Do not convert LineToArea on Connect commands.
+ // ConvertMarkedToPathObj(!bNoPolyPoly);
+
+ // This is used for Combine and Connect. In no case it is necessary to force
+ // the content to curve, but it is also not good to force to polygons. Thus,
+ // curve is the less information losing one. Remember: This place is not
+ // used for merge.
+ // LineToArea is never necessary, both commands are able to take over the
+ // set line style and to display it correctly. Thus, i will use a
+ // ConvertMarkedToPathObj with a false in any case. Only drawback is that
+ // simple polygons will be changed to curves, but with no information loss.
+ ConvertMarkedToPathObj(false /* bLineToArea */);
+
+ // continue as before
+ basegfx::B2DPolyPolygon aPolyPolygon;
+ SdrObjList* pCurrentOL = nullptr;
+ SdrMarkList aRemoveBuffer;
+
+ SortMarkedObjects();
+ size_t nInsPos = SAL_MAX_SIZE;
+ SdrObjList* pInsOL = nullptr;
+ SdrPageView* pInsPV = nullptr;
+ const SdrObject* pAttrObj = nullptr;
+
+ for(size_t a = GetMarkedObjectCount(); a; )
+ {
+ --a;
+ SdrMark* pM = GetSdrMarkByIndex(a);
+ SdrObject* pObj = pM->GetMarkedSdrObj();
+ SdrObjList* pThisOL = pObj->getParentSdrObjListFromSdrObject();
+
+ if(pCurrentOL != pThisOL)
+ {
+ pCurrentOL = pThisOL;
+ }
+
+ if(ImpCanConvertForCombine(pObj))
+ {
+ // remember objects to be able to copy attributes
+ pAttrObj = pObj;
+
+ // unfortunately ConvertMarkedToPathObj has converted all
+ // involved polygon data to curve segments, even if not necessary.
+ // It is better to try to reduce to more simple polygons.
+ basegfx::B2DPolyPolygon aTmpPoly(basegfx::utils::simplifyCurveSegments(ImpGetPolyPolygon(pObj)));
+ aPolyPolygon.insert(0, aTmpPoly);
+
+ if(!pInsOL)
+ {
+ nInsPos = pObj->GetOrdNum() + 1;
+ pInsPV = pM->GetPageView();
+ pInsOL = pObj->getParentSdrObjListFromSdrObject();
+ }
+
+ aRemoveBuffer.InsertEntry(SdrMark(pObj, pM->GetPageView()));
+ }
+ }
+
+ if(bNoPolyPoly)
+ {
+ basegfx::B2DPolygon aCombinedPolygon(ImpCombineToSinglePolygon(aPolyPolygon));
+ aPolyPolygon.clear();
+ aPolyPolygon.append(aCombinedPolygon);
+ }
+
+ const sal_uInt32 nPolyCount(aPolyPolygon.count());
+
+ if (nPolyCount && pAttrObj)
+ {
+ SdrObjKind eKind = SdrObjKind::PathFill;
+
+ if(nPolyCount > 1)
+ {
+ aPolyPolygon.setClosed(true);
+ }
+ else
+ {
+ // check for Polyline
+ const basegfx::B2DPolygon aPolygon(aPolyPolygon.getB2DPolygon(0));
+ const sal_uInt32 nPointCount(aPolygon.count());
+
+ if(nPointCount <= 2)
+ {
+ eKind = SdrObjKind::PathLine;
+ }
+ else
+ {
+ if(!aPolygon.isClosed())
+ {
+ const basegfx::B2DPoint aPointA(aPolygon.getB2DPoint(0));
+ const basegfx::B2DPoint aPointB(aPolygon.getB2DPoint(nPointCount - 1));
+ const double fDistance(basegfx::B2DVector(aPointB - aPointA).getLength());
+ const double fJoinTolerance(10.0);
+
+ if(fDistance < fJoinTolerance)
+ {
+ aPolyPolygon.setClosed(true);
+ }
+ else
+ {
+ eKind = SdrObjKind::PathLine;
+ }
+ }
+ }
+ }
+
+ SdrPathObj* pPath = new SdrPathObj(pAttrObj->getSdrModelFromSdrObject(), eKind, aPolyPolygon);
+
+ // attributes of the lowest object
+ ImpCopyAttributes(pAttrObj, pPath);
+
+ // If LineStyle of pAttrObj is drawing::LineStyle_NONE force to drawing::LineStyle_SOLID to make visible.
+ const drawing::LineStyle eLineStyle = pAttrObj->GetMergedItem(XATTR_LINESTYLE).GetValue();
+ const drawing::FillStyle eFillStyle = pAttrObj->GetMergedItem(XATTR_FILLSTYLE).GetValue();
+
+ // Take fill style/closed state of pAttrObj in account when deciding to change the line style
+ bool bIsClosedPathObj = false;
+ if (auto pPathObj = dynamic_cast<const SdrPathObj*>(pAttrObj))
+ if (pPathObj->IsClosed())
+ bIsClosedPathObj = true;
+
+ if(drawing::LineStyle_NONE == eLineStyle && (drawing::FillStyle_NONE == eFillStyle || !bIsClosedPathObj))
+ {
+ pPath->SetMergedItem(XLineStyleItem(drawing::LineStyle_SOLID));
+ }
+
+ pInsOL->InsertObject(pPath,nInsPos);
+ if( bUndo )
+ AddUndo(GetModel()->GetSdrUndoFactory().CreateUndoNewObject(*pPath));
+
+ // Here was a severe error: Without UnmarkAllObj, the new object was marked
+ // additionally to the two ones which are deleted below. As long as those are
+ // in the UNDO there is no problem, but as soon as they get deleted, the
+ // MarkList will contain deleted objects -> GPF.
+ UnmarkAllObj(pInsPV);
+ MarkObj(pPath, pInsPV, false, true);
+ }
+
+ // build an UndoComment from the objects actually used
+ aRemoveBuffer.ForceSort(); // important for remove (see below)
+ if( bUndo )
+ SetUndoComment(SvxResId(bNoPolyPoly?STR_EditCombine_OnePoly:STR_EditCombine_PolyPoly),aRemoveBuffer.GetMarkDescription());
+
+ // remove objects actually used from the list
+ DeleteMarkedList(aRemoveBuffer);
+ if( bUndo )
+ EndUndo();
+}
+
+
+// Dismantle
+
+
+bool SdrEditView::ImpCanDismantle(const basegfx::B2DPolyPolygon& rPpolyPolygon, bool bMakeLines)
+{
+ bool bCan(false);
+ const sal_uInt32 nPolygonCount(rPpolyPolygon.count());
+
+ if(nPolygonCount >= 2)
+ {
+ // #i69172# dismantle makes sense with 2 or more polygons in a polyPolygon
+ bCan = true;
+ }
+ else if(bMakeLines && 1 == nPolygonCount)
+ {
+ // #i69172# ..or with at least 2 edges (curves or lines)
+ const basegfx::B2DPolygon& aPolygon(rPpolyPolygon.getB2DPolygon(0));
+ const sal_uInt32 nPointCount(aPolygon.count());
+
+ if(nPointCount > 2)
+ {
+ bCan = true;
+ }
+ }
+
+ return bCan;
+}
+
+bool SdrEditView::ImpCanDismantle(const SdrObject* pObj, bool bMakeLines)
+{
+ bool bOtherObjs(false); // true=objects other than PathObj's existent
+ bool bMin1PolyPoly(false); // true=at least 1 tools::PolyPolygon with more than one Polygon existent
+ SdrObjList* pOL = pObj->GetSubList();
+
+ if(pOL)
+ {
+ // group object -- check all members if they're PathObjs
+ SdrObjListIter aIter(pOL, SdrIterMode::DeepNoGroups);
+
+ while(aIter.IsMore() && !bOtherObjs)
+ {
+ const SdrObject* pObj1 = aIter.Next();
+ const SdrPathObj* pPath = dynamic_cast<const SdrPathObj*>( pObj1 );
+
+ if(pPath)
+ {
+ if(ImpCanDismantle(pPath->GetPathPoly(), bMakeLines))
+ {
+ bMin1PolyPoly = true;
+ }
+
+ SdrObjTransformInfoRec aInfo;
+ pObj1->TakeObjInfo(aInfo);
+
+ if(!aInfo.bCanConvToPath)
+ {
+ // happens e. g. in the case of FontWork
+ bOtherObjs = true;
+ }
+ }
+ else
+ {
+ bOtherObjs = true;
+ }
+ }
+ }
+ else
+ {
+ const SdrPathObj* pPath = dynamic_cast<const SdrPathObj*>(pObj);
+ const SdrObjCustomShape* pCustomShape = dynamic_cast<const SdrObjCustomShape*>(pObj);
+
+ // #i37011#
+ if(pPath)
+ {
+ if(ImpCanDismantle(pPath->GetPathPoly(),bMakeLines))
+ {
+ bMin1PolyPoly = true;
+ }
+
+ SdrObjTransformInfoRec aInfo;
+ pObj->TakeObjInfo(aInfo);
+
+ // new condition IsLine() to be able to break simple Lines
+ if(!(aInfo.bCanConvToPath || aInfo.bCanConvToPoly) && !pPath->IsLine())
+ {
+ // happens e. g. in the case of FontWork
+ bOtherObjs = true;
+ }
+ }
+ else if(pCustomShape)
+ {
+ if(bMakeLines)
+ {
+ // allow break command
+ bMin1PolyPoly = true;
+ }
+ }
+ else
+ {
+ bOtherObjs = true;
+ }
+ }
+ return bMin1PolyPoly && !bOtherObjs;
+}
+
+void SdrEditView::ImpDismantleOneObject(const SdrObject* pObj, SdrObjList& rOL, size_t& rPos, SdrPageView* pPV, bool bMakeLines)
+{
+ const SdrPathObj* pSrcPath = dynamic_cast<const SdrPathObj*>( pObj );
+ const SdrObjCustomShape* pCustomShape = dynamic_cast<const SdrObjCustomShape*>( pObj );
+
+ const bool bUndo = IsUndoEnabled();
+
+ if(pSrcPath)
+ {
+ // #i74631# redesigned due to XpolyPolygon removal and explicit constructors
+ SdrObject* pLast = nullptr; // to be able to apply OutlinerParaObject
+ const basegfx::B2DPolyPolygon& rPolyPolygon(pSrcPath->GetPathPoly());
+ const sal_uInt32 nPolyCount(rPolyPolygon.count());
+
+ for(sal_uInt32 a(0); a < nPolyCount; a++)
+ {
+ const basegfx::B2DPolygon& rCandidate(rPolyPolygon.getB2DPolygon(a));
+ const sal_uInt32 nPointCount(rCandidate.count());
+
+ if(!bMakeLines || nPointCount < 2)
+ {
+ SdrPathObj* pPath = new SdrPathObj(
+ pSrcPath->getSdrModelFromSdrObject(),
+ pSrcPath->GetObjIdentifier(),
+ basegfx::B2DPolyPolygon(rCandidate));
+ ImpCopyAttributes(pSrcPath, pPath);
+ pLast = pPath;
+ rOL.InsertObject(pPath, rPos);
+ if( bUndo )
+ AddUndo(GetModel()->GetSdrUndoFactory().CreateUndoNewObject(*pPath, true));
+ MarkObj(pPath, pPV, false, true);
+ rPos++;
+ }
+ else
+ {
+ const sal_uInt32 nLoopCount(rCandidate.isClosed() ? nPointCount : nPointCount - 1);
+
+ for(sal_uInt32 b(0); b < nLoopCount; b++)
+ {
+ SdrObjKind eKind(SdrObjKind::PolyLine);
+ basegfx::B2DPolygon aNewPolygon;
+ const sal_uInt32 nNextIndex((b + 1) % nPointCount);
+
+ aNewPolygon.append(rCandidate.getB2DPoint(b));
+
+ if(rCandidate.areControlPointsUsed())
+ {
+ aNewPolygon.appendBezierSegment(
+ rCandidate.getNextControlPoint(b),
+ rCandidate.getPrevControlPoint(nNextIndex),
+ rCandidate.getB2DPoint(nNextIndex));
+ eKind = SdrObjKind::PathLine;
+ }
+ else
+ {
+ aNewPolygon.append(rCandidate.getB2DPoint(nNextIndex));
+ }
+
+ SdrPathObj* pPath = new SdrPathObj(
+ pSrcPath->getSdrModelFromSdrObject(),
+ eKind,
+ basegfx::B2DPolyPolygon(aNewPolygon));
+ ImpCopyAttributes(pSrcPath, pPath);
+ pLast = pPath;
+ rOL.InsertObject(pPath, rPos);
+ if( bUndo )
+ AddUndo(GetModel()->GetSdrUndoFactory().CreateUndoNewObject(*pPath, true));
+ MarkObj(pPath, pPV, false, true);
+ rPos++;
+ }
+ }
+ }
+
+ if(pLast && pSrcPath->GetOutlinerParaObject())
+ {
+ pLast->SetOutlinerParaObject(*pSrcPath->GetOutlinerParaObject());
+ }
+ }
+ else if(pCustomShape)
+ {
+ if(bMakeLines)
+ {
+ // break up custom shape
+ const SdrObject* pReplacement = pCustomShape->GetSdrObjectFromCustomShape();
+
+ if(pReplacement)
+ {
+ SdrObject* pCandidate(pReplacement->CloneSdrObject(pReplacement->getSdrModelFromSdrObject()));
+ DBG_ASSERT(pCandidate, "SdrEditView::ImpDismantleOneObject: Could not clone SdrObject (!)");
+
+ if(pCustomShape->GetMergedItem(SDRATTR_SHADOW).GetValue())
+ {
+ if(dynamic_cast<const SdrObjGroup*>( pReplacement) != nullptr)
+ {
+ pCandidate->SetMergedItem(makeSdrShadowItem(true));
+ }
+ }
+
+ rOL.InsertObject(pCandidate, rPos);
+ if( bUndo )
+ AddUndo(GetModel()->GetSdrUndoFactory().CreateUndoNewObject(*pCandidate, true));
+ MarkObj(pCandidate, pPV, false, true);
+
+ if(pCustomShape->HasText() && !pCustomShape->IsTextPath())
+ {
+ // #i37011# also create a text object and add at rPos + 1
+ SdrObject* pTextObj = SdrObjFactory::MakeNewObject(
+ pCustomShape->getSdrModelFromSdrObject(),
+ pCustomShape->GetObjInventor(),
+ SdrObjKind::Text);
+
+ // Copy text content
+ OutlinerParaObject* pParaObj = pCustomShape->GetOutlinerParaObject();
+ if(pParaObj)
+ {
+ pTextObj->NbcSetOutlinerParaObject(*pParaObj);
+ }
+
+ // copy all attributes
+ SfxItemSet aTargetItemSet(pCustomShape->GetMergedItemSet());
+
+ // clear fill and line style
+ aTargetItemSet.Put(XLineStyleItem(drawing::LineStyle_NONE));
+ aTargetItemSet.Put(XFillStyleItem(drawing::FillStyle_NONE));
+
+ // get the text bounds and set at text object
+ tools::Rectangle aTextBounds = pCustomShape->GetSnapRect();
+ if(pCustomShape->GetTextBounds(aTextBounds))
+ {
+ pTextObj->SetSnapRect(aTextBounds);
+ }
+
+ // if rotated, copy GeoStat, too.
+ const GeoStat& rSourceGeo = pCustomShape->GetGeoStat();
+ if(rSourceGeo.nRotationAngle)
+ {
+ pTextObj->NbcRotate(
+ pCustomShape->GetSnapRect().Center(), rSourceGeo.nRotationAngle,
+ rSourceGeo.mfSinRotationAngle, rSourceGeo.mfCosRotationAngle);
+ }
+
+ // set modified ItemSet at text object
+ pTextObj->SetMergedItemSet(aTargetItemSet);
+
+ // insert object
+ rOL.InsertObject(pTextObj, rPos + 1);
+ if( bUndo )
+ AddUndo(GetModel()->GetSdrUndoFactory().CreateUndoNewObject(*pTextObj, true));
+ MarkObj(pTextObj, pPV, false, true);
+ }
+ }
+ }
+ }
+}
+
+void SdrEditView::DismantleMarkedObjects(bool bMakeLines)
+{
+ // temporary MarkList
+ SdrMarkList aRemoveBuffer;
+
+ SortMarkedObjects();
+
+ const bool bUndo = IsUndoEnabled();
+
+ if( bUndo )
+ {
+ // comment is constructed later
+ BegUndo("", "", bMakeLines ? SdrRepeatFunc::DismantleLines : SdrRepeatFunc::DismantlePolys);
+ }
+
+ SdrObjList* pOL0=nullptr;
+ for (size_t nm=GetMarkedObjectCount(); nm>0;) {
+ --nm;
+ SdrMark* pM=GetSdrMarkByIndex(nm);
+ SdrObject* pObj=pM->GetMarkedSdrObj();
+ SdrPageView* pPV=pM->GetPageView();
+ SdrObjList* pOL=pObj->getParentSdrObjListFromSdrObject();
+ if (pOL!=pOL0) { pOL0=pOL; pObj->GetOrdNum(); } // make sure OrdNums are correct!
+ if (ImpCanDismantle(pObj,bMakeLines)) {
+ aRemoveBuffer.InsertEntry(SdrMark(pObj,pM->GetPageView()));
+ const size_t nPos0=pObj->GetOrdNumDirect();
+ size_t nPos=nPos0+1;
+ SdrObjList* pSubList=pObj->GetSubList();
+ if (pSubList!=nullptr && !pObj->Is3DObj()) {
+ SdrObjListIter aIter(pSubList,SdrIterMode::DeepNoGroups);
+ while (aIter.IsMore()) {
+ const SdrObject* pObj1=aIter.Next();
+ ImpDismantleOneObject(pObj1,*pOL,nPos,pPV,bMakeLines);
+ }
+ } else {
+ ImpDismantleOneObject(pObj,*pOL,nPos,pPV,bMakeLines);
+ }
+ if( bUndo )
+ AddUndo(GetModel()->GetSdrUndoFactory().CreateUndoDeleteObject(*pObj,true));
+ pOL->RemoveObject(nPos0);
+
+ if( !bUndo )
+ SdrObject::Free(pObj);
+ }
+ }
+
+ if( bUndo )
+ {
+ // construct UndoComment from objects actually used
+ SetUndoComment(SvxResId(bMakeLines?STR_EditDismantle_Lines:STR_EditDismantle_Polys),aRemoveBuffer.GetMarkDescription());
+ // remove objects actually used from the list
+ EndUndo();
+ }
+}
+
+
+// Group
+
+
+void SdrEditView::GroupMarked()
+{
+ if (!AreObjectsMarked())
+ return;
+
+ SortMarkedObjects();
+
+ const bool bUndo = IsUndoEnabled();
+ if( bUndo )
+ {
+ BegUndo(SvxResId(STR_EditGroup),GetDescriptionOfMarkedObjects(),SdrRepeatFunc::Group);
+
+ for(size_t nm = GetMarkedObjectCount(); nm>0; )
+ {
+ // add UndoActions for all affected objects
+ --nm;
+ SdrMark* pM=GetSdrMarkByIndex(nm);
+ SdrObject* pObj = pM->GetMarkedSdrObj();
+ AddUndoActions( CreateConnectorUndo( *pObj ) );
+ AddUndo(GetModel()->GetSdrUndoFactory().CreateUndoRemoveObject( *pObj ));
+ }
+ }
+
+ SdrMarkList aNewMark;
+ SdrPageView* pPV = GetSdrPageView();
+
+ if(pPV)
+ {
+ SdrObjList* pCurrentLst=pPV->GetObjList();
+ SdrObjList* pSrcLst=pCurrentLst;
+ SdrObjList* pSrcLst0=pSrcLst;
+ // make sure OrdNums are correct
+ if (pSrcLst->IsObjOrdNumsDirty())
+ pSrcLst->RecalcObjOrdNums();
+ SdrObject* pGrp=nullptr;
+ SdrObjList* pDstLst=nullptr;
+ // if all selected objects come from foreign object lists.
+ // the group object is the last one in the list.
+ size_t nInsPos=pSrcLst->GetObjCount();
+ bool bNeedInsPos=true;
+ for (size_t nm=GetMarkedObjectCount(); nm>0;)
+ {
+ --nm;
+ SdrMark* pM=GetSdrMarkByIndex(nm);
+ if (pM->GetPageView()==pPV)
+ {
+ SdrObject* pObj=pM->GetMarkedSdrObj();
+ if (nullptr==pGrp)
+ {
+ pGrp = new SdrObjGroup(pObj->getSdrModelFromSdrObject());
+ pDstLst=pGrp->GetSubList();
+ DBG_ASSERT(pDstLst!=nullptr,"Alleged group object doesn't return object list.");
+ }
+ pSrcLst=pObj->getParentSdrObjListFromSdrObject();
+ if (pSrcLst!=pSrcLst0)
+ {
+ if (pSrcLst->IsObjOrdNumsDirty())
+ pSrcLst->RecalcObjOrdNums();
+ }
+ bool bForeignList=pSrcLst!=pCurrentLst;
+ if (!bForeignList && bNeedInsPos)
+ {
+ nInsPos=pObj->GetOrdNum(); // this way, all ObjOrdNum of the page are set
+ nInsPos++;
+ bNeedInsPos=false;
+ }
+ pSrcLst->RemoveObject(pObj->GetOrdNumDirect());
+ if (!bForeignList)
+ nInsPos--; // correct InsertPos
+ pDstLst->InsertObject(pObj,0);
+ GetMarkedObjectListWriteAccess().DeleteMark(nm);
+ pSrcLst0=pSrcLst;
+ }
+ }
+ if (pGrp!=nullptr)
+ {
+ aNewMark.InsertEntry(SdrMark(pGrp,pPV));
+ const size_t nCount=pDstLst->GetObjCount();
+ pCurrentLst->InsertObject(pGrp,nInsPos);
+ if( bUndo )
+ {
+ AddUndo(GetModel()->GetSdrUndoFactory().CreateUndoNewObject(*pGrp,true)); // no recalculation!
+ for (size_t no=0; no<nCount; ++no)
+ {
+ AddUndo(GetModel()->GetSdrUndoFactory().CreateUndoInsertObject(*pDstLst->GetObj(no)));
+ }
+ }
+ }
+ }
+ GetMarkedObjectListWriteAccess().Merge(aNewMark);
+ MarkListHasChanged();
+
+ if( bUndo )
+ EndUndo();
+}
+
+
+// Ungroup
+
+
+void SdrEditView::UnGroupMarked()
+{
+ SdrMarkList aNewMark;
+
+ const bool bUndo = IsUndoEnabled();
+ if( bUndo )
+ BegUndo("", "", SdrRepeatFunc::Ungroup);
+
+ size_t nCount=0;
+ OUString aName1;
+ OUString aName;
+ bool bNameOk=false;
+ for (size_t nm=GetMarkedObjectCount(); nm>0;) {
+ --nm;
+ SdrMark* pM=GetSdrMarkByIndex(nm);
+ SdrObject* pGrp=pM->GetMarkedSdrObj();
+ SdrObjList* pSrcLst=pGrp->GetSubList();
+ if (pSrcLst!=nullptr) {
+ nCount++;
+ if (nCount==1) {
+ aName = pGrp->TakeObjNameSingul(); // retrieve name of group
+ aName1 = pGrp->TakeObjNamePlural(); // retrieve name of group
+ bNameOk=true;
+ } else {
+ if (nCount==2) aName=aName1; // set plural name
+ if (bNameOk) {
+ OUString aStr(pGrp->TakeObjNamePlural()); // retrieve name of group
+
+ if (aStr != aName)
+ bNameOk = false;
+ }
+ }
+ size_t nDstCnt=pGrp->GetOrdNum();
+ SdrObjList* pDstLst=pM->GetPageView()->GetObjList();
+ size_t nObjCount=pSrcLst->GetObjCount();
+ const bool bIsDiagram(pGrp->isDiagram());
+
+ // If the Group is a Diagram, it has a filler BG object to guarantee
+ // the Diagam's dimensions. Identify that shape
+ if(bIsDiagram && nObjCount)
+ {
+ SdrObject* pObj(pSrcLst->GetObj(0));
+
+ if(nullptr != pObj && !pObj->IsGroupObject() &&
+ !pObj->HasLineStyle() &&
+ pObj->IsMoveProtect() && pObj->IsResizeProtect())
+ {
+ if(pObj->HasFillStyle())
+ {
+ // If it has FillStyle it is a useful object representing that possible
+ // defined fill from oox import. In this case, we should remove the
+ // Move/Resize protection to allow seamless further processing.
+
+ // Undo of these is handled by SdrUndoGeoObj which holds a SdrObjGeoData,
+ // create one
+ if( bUndo )
+ AddUndo(GetModel()->GetSdrUndoFactory().CreateUndoGeoObject(*pObj));
+
+ pObj->SetMoveProtect(false);
+ pObj->SetResizeProtect(false);
+ }
+ else
+ {
+ // If it has no FillStyle it is not useful for any further processing
+ // but only was used as a placeholder, get directly rid of it
+ if( bUndo )
+ AddUndo(GetModel()->GetSdrUndoFactory().CreateUndoDeleteObject(*pObj));
+
+ pObj = pSrcLst->RemoveObject(0);
+
+ if( !bUndo )
+ SdrObject::Free(pObj);
+
+ nObjCount = pSrcLst->GetObjCount();
+ }
+ }
+ }
+
+ // FIRST move contained objects to parent of group, so that
+ // the contained objects are NOT migrated to the UNDO-ItemPool
+ // when AddUndo(new SdrUndoDelObj(*pGrp)) is called.
+ if( bUndo )
+ {
+ for (size_t no=nObjCount; no>0;)
+ {
+ no--;
+ SdrObject* pObj=pSrcLst->GetObj(no);
+ AddUndo(GetModel()->GetSdrUndoFactory().CreateUndoRemoveObject(*pObj));
+ }
+ }
+
+ for (size_t no=0; no<nObjCount; ++no)
+ {
+ SdrObject* pObj=pSrcLst->RemoveObject(0);
+ pDstLst->InsertObject(pObj,nDstCnt);
+ if( bUndo )
+ AddUndo( GetModel()->GetSdrUndoFactory().CreateUndoInsertObject(*pObj,true));
+ nDstCnt++;
+ // No SortCheck when inserting into MarkList, because that would
+ // provoke a RecalcOrdNums() each time because of pObj->GetOrdNum():
+ aNewMark.InsertEntry(SdrMark(pObj,pM->GetPageView()),false);
+ }
+
+ if( bUndo )
+ {
+ // Now it is safe to add the delete-UNDO which triggers the
+ // MigrateItemPool now only for itself, not for the sub-objects.
+ // nDstCnt is right, because previous inserts move group
+ // object deeper and increase nDstCnt.
+ AddUndo(GetModel()->GetSdrUndoFactory().CreateUndoDeleteObject(*pGrp));
+ }
+ pDstLst->RemoveObject(nDstCnt);
+
+ if( !bUndo )
+ SdrObject::Free(pGrp);
+
+ GetMarkedObjectListWriteAccess().DeleteMark(nm);
+ }
+ }
+ if (nCount!=0)
+ {
+ if (!bNameOk)
+ aName=SvxResId(STR_ObjNamePluralGRUP); // Use the term "Group Objects," if different objects are grouped.
+ SetUndoComment(SvxResId(STR_EditUngroup),aName);
+ }
+
+ if( bUndo )
+ EndUndo();
+
+ if (nCount!=0)
+ {
+ GetMarkedObjectListWriteAccess().Merge(aNewMark,true); // Because of the sorting above, aNewMark is reversed
+ MarkListHasChanged();
+ }
+}
+
+
+// ConvertToPoly
+
+
+SdrObject* SdrEditView::ImpConvertOneObj(SdrObject* pObj, bool bPath, bool bLineToArea)
+{
+ SdrObjectUniquePtr pNewObj = pObj->ConvertToPolyObj(bPath, bLineToArea);
+ if (pNewObj)
+ {
+ SdrObjList* pOL = pObj->getParentSdrObjListFromSdrObject();
+ const bool bUndo = IsUndoEnabled();
+ if( bUndo )
+ AddUndo(GetModel()->GetSdrUndoFactory().CreateUndoReplaceObject(*pObj,*pNewObj));
+
+ // ownership passed into here (despite the UniquePtr indicating that we are returning it)
+ pOL->ReplaceObject(pNewObj.get(), pObj->GetOrdNum());
+
+ if( !bUndo )
+ SdrObject::Free(pObj);
+ }
+ return pNewObj.release();
+}
+
+void SdrEditView::ImpConvertTo(bool bPath, bool bLineToArea)
+{
+ if (!AreObjectsMarked()) return;
+
+ bool bMrkChg = false;
+ const size_t nMarkCount=GetMarkedObjectCount();
+ TranslateId pDscrID;
+ if(bLineToArea)
+ {
+ if(nMarkCount == 1)
+ pDscrID = STR_EditConvToContour;
+ else
+ pDscrID = STR_EditConvToContours;
+
+ BegUndo(SvxResId(pDscrID), GetDescriptionOfMarkedObjects());
+ }
+ else
+ {
+ if (bPath) {
+ if (nMarkCount==1) pDscrID=STR_EditConvToCurve;
+ else pDscrID=STR_EditConvToCurves;
+ BegUndo(SvxResId(pDscrID),GetDescriptionOfMarkedObjects(),SdrRepeatFunc::ConvertToPath);
+ } else {
+ if (nMarkCount==1) pDscrID=STR_EditConvToPoly;
+ else pDscrID=STR_EditConvToPolys;
+ BegUndo(SvxResId(pDscrID),GetDescriptionOfMarkedObjects(),SdrRepeatFunc::ConvertToPoly);
+ }
+ }
+ for (size_t nm=nMarkCount; nm>0;) {
+ --nm;
+ SdrMark* pM=GetSdrMarkByIndex(nm);
+ SdrObject* pObj=pM->GetMarkedSdrObj();
+ SdrPageView* pPV=pM->GetPageView();
+ if (pObj->IsGroupObject() && !pObj->Is3DObj()) {
+ SdrObject* pGrp=pObj;
+ SdrObjListIter aIter(*pGrp, SdrIterMode::DeepNoGroups);
+ while (aIter.IsMore()) {
+ pObj=aIter.Next();
+ ImpConvertOneObj(pObj,bPath,bLineToArea);
+ }
+ } else {
+ SdrObject* pNewObj=ImpConvertOneObj(pObj,bPath,bLineToArea);
+ if (pNewObj!=nullptr) {
+ bMrkChg=true;
+ GetMarkedObjectListWriteAccess().ReplaceMark(SdrMark(pNewObj,pPV),nm);
+ }
+ }
+ }
+ EndUndo();
+ if (bMrkChg)
+ {
+ AdjustMarkHdl();
+ MarkListHasChanged();
+ }
+}
+
+void SdrEditView::ConvertMarkedToPathObj(bool bLineToArea)
+{
+ ImpConvertTo(true, bLineToArea);
+}
+
+void SdrEditView::ConvertMarkedToPolyObj()
+{
+ ImpConvertTo(false, false/*bLineToArea*/);
+}
+
+namespace
+{
+ GDIMetaFile GetMetaFile(SdrGrafObj const * pGraf)
+ {
+ if (pGraf->HasGDIMetaFile())
+ return pGraf->GetTransformedGraphic(SdrGrafObjTransformsAttrs::MIRROR).GetGDIMetaFile();
+ assert(pGraf->isEmbeddedVectorGraphicData());
+ return pGraf->getMetafileFromEmbeddedVectorGraphicData();
+ }
+}
+
+// Metafile Import
+void SdrEditView::DoImportMarkedMtf(SvdProgressInfo *pProgrInfo)
+{
+ const bool bUndo = IsUndoEnabled();
+
+ if( bUndo )
+ BegUndo("", "", SdrRepeatFunc::ImportMtf);
+
+ SortMarkedObjects();
+ SdrMarkList aForTheDescription;
+ SdrMarkList aNewMarked;
+ for (size_t nm =GetMarkedObjectCount(); nm > 0; )
+ {
+ // create Undo objects for all new objects
+ // check for cancellation between the metafiles
+ if (pProgrInfo != nullptr)
+ {
+ pProgrInfo->SetNextObject();
+ if (!pProgrInfo->ReportActions(0))
+ break;
+ }
+
+ --nm;
+ SdrMark* pM=GetSdrMarkByIndex(nm);
+ SdrObject* pObj=pM->GetMarkedSdrObj();
+ SdrPageView* pPV=pM->GetPageView();
+ SdrObjList* pOL=pObj->getParentSdrObjListFromSdrObject();
+ const size_t nInsPos=pObj->GetOrdNum()+1;
+ size_t nInsCnt=0;
+ tools::Rectangle aLogicRect;
+
+ SdrGrafObj* pGraf = dynamic_cast<SdrGrafObj*>( pObj );
+ if (pGraf != nullptr)
+ {
+ Graphic aGraphic = pGraf->GetGraphic();
+ auto const & pVectorGraphicData = aGraphic.getVectorGraphicData();
+
+ if (pVectorGraphicData && pVectorGraphicData->getType() == VectorGraphicDataType::Pdf)
+ {
+ auto pPdfium = vcl::pdf::PDFiumLibrary::get();
+ if (pPdfium)
+ {
+ aLogicRect = pGraf->GetLogicRect();
+ ImpSdrPdfImport aFilter(*mpModel, pObj->GetLayer(), aLogicRect, aGraphic);
+ if (aGraphic.getPageNumber() < aFilter.GetPageCount())
+ {
+ nInsCnt = aFilter.DoImport(*pOL, nInsPos, aGraphic.getPageNumber(), pProgrInfo);
+ }
+ }
+ }
+ else if (pGraf->HasGDIMetaFile() || pGraf->isEmbeddedVectorGraphicData() )
+ {
+ GDIMetaFile aMetaFile(GetMetaFile(pGraf));
+ if (aMetaFile.GetActionSize())
+ {
+ aLogicRect = pGraf->GetLogicRect();
+ ImpSdrGDIMetaFileImport aFilter(*mpModel, pObj->GetLayer(), aLogicRect);
+ nInsCnt = aFilter.DoImport(aMetaFile, *pOL, nInsPos, pProgrInfo);
+ }
+ }
+ }
+
+ SdrOle2Obj* pOle2 = dynamic_cast<SdrOle2Obj*>(pObj);
+ if (pOle2 != nullptr && pOle2->GetGraphic())
+ {
+ aLogicRect = pOle2->GetLogicRect();
+ ImpSdrGDIMetaFileImport aFilter(*mpModel, pObj->GetLayer(), aLogicRect);
+ nInsCnt = aFilter.DoImport(pOle2->GetGraphic()->GetGDIMetaFile(), *pOL, nInsPos, pProgrInfo);
+ }
+
+ if (nInsCnt != 0)
+ {
+ // transformation
+ GeoStat aGeoStat(pGraf ? pGraf->GetGeoStat() : pOle2->GetGeoStat());
+ size_t nObj = nInsPos;
+
+ if (aGeoStat.nShearAngle)
+ aGeoStat.RecalcTan();
+
+ if (aGeoStat.nRotationAngle)
+ aGeoStat.RecalcSinCos();
+
+ for (size_t i = 0; i < nInsCnt; i++)
+ {
+ if (bUndo)
+ AddUndo(GetModel()->GetSdrUndoFactory().CreateUndoNewObject(*pOL->GetObj(nObj)));
+
+ // update new MarkList
+ SdrObject* pCandidate = pOL->GetObj(nObj);
+
+ // apply original transformation
+ if (aGeoStat.nShearAngle)
+ pCandidate->NbcShear(aLogicRect.TopLeft(), aGeoStat.nShearAngle, aGeoStat.mfTanShearAngle, false);
+
+ if (aGeoStat.nRotationAngle)
+ pCandidate->NbcRotate(aLogicRect.TopLeft(), aGeoStat.nRotationAngle, aGeoStat.mfSinRotationAngle, aGeoStat.mfCosRotationAngle);
+
+ SdrMark aNewMark(pCandidate, pPV);
+ aNewMarked.InsertEntry(aNewMark);
+
+ nObj++;
+ }
+
+ aForTheDescription.InsertEntry(*pM);
+
+ if (bUndo)
+ AddUndo(GetModel()->GetSdrUndoFactory().CreateUndoDeleteObject(*pObj));
+
+ // remove object from selection and delete
+ GetMarkedObjectListWriteAccess().DeleteMark(TryToFindMarkedObject(pObj));
+ pOL->RemoveObject(nInsPos-1);
+
+ if (!bUndo)
+ SdrObject::Free(pObj);
+ }
+ }
+
+ if (aNewMarked.GetMarkCount())
+ {
+ // create new selection
+ for (size_t a = 0; a < aNewMarked.GetMarkCount(); ++a)
+ {
+ GetMarkedObjectListWriteAccess().InsertEntry(*aNewMarked.GetMark(a));
+ }
+
+ SortMarkedObjects();
+ }
+
+ if (bUndo)
+ {
+ SetUndoComment(SvxResId(STR_EditImportMtf),aForTheDescription.GetMarkDescription());
+ EndUndo();
+ }
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/svdraw/svdedxv.cxx b/svx/source/svdraw/svdedxv.cxx
new file mode 100644
index 000000000..c1c248b21
--- /dev/null
+++ b/svx/source/svdraw/svdedxv.cxx
@@ -0,0 +1,2871 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * 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 <editeng/editdata.hxx>
+#include <editeng/editeng.hxx>
+#include <editeng/editobj.hxx>
+#include <editeng/editstat.hxx>
+#include <editeng/outlobj.hxx>
+#include <editeng/unotext.hxx>
+#include <o3tl/deleter.hxx>
+#include <svl/itemiter.hxx>
+#include <svl/style.hxx>
+#include <svl/whiter.hxx>
+#include <svtools/accessibilityoptions.hxx>
+#include <svx/sdtfchim.hxx>
+#include <svx/selectioncontroller.hxx>
+#include <svx/svdedxv.hxx>
+#include <svx/svdetc.hxx>
+#include <svx/svdotable.hxx>
+#include <svx/svdotext.hxx>
+#include <svx/svdoutl.hxx>
+#include <svx/svdpage.hxx>
+#include <svx/svdpagv.hxx>
+#include <svx/svdundo.hxx>
+#include <vcl/canvastools.hxx>
+#include <vcl/commandevent.hxx>
+#include <vcl/cursor.hxx>
+#include <vcl/weld.hxx>
+#include <vcl/window.hxx>
+#include <comphelper/lok.hxx>
+#include <drawinglayer/processor2d/baseprocessor2d.hxx>
+#include <drawinglayer/processor2d/processor2dtools.hxx>
+#include <editeng/outliner.hxx>
+#include <sal/log.hxx>
+#include <sdr/overlay/overlaytools.hxx>
+#include <sfx2/viewsh.hxx>
+#include <svx/dialmgr.hxx>
+#include <svx/sdr/overlay/overlaymanager.hxx>
+#include <svx/sdr/overlay/overlayselection.hxx>
+#include <svx/sdr/table/tablecontroller.hxx>
+#include <svx/sdrpagewindow.hxx>
+#include <svx/sdrpaintwindow.hxx>
+#include <svx/sdrundomanager.hxx>
+#include <svx/strings.hrc>
+#include <svx/svdviter.hxx>
+#include <svtools/optionsdrawinglayer.hxx>
+#include <textchain.hxx>
+#include <textchaincursor.hxx>
+#include <tools/debug.hxx>
+#include <vcl/svapp.hxx>
+
+#include <memory>
+
+SdrObjEditView::SdrObjEditView(SdrModel& rSdrModel, OutputDevice* pOut)
+ : SdrGlueEditView(rSdrModel, pOut)
+ , mpTextEditPV(nullptr)
+ , mpTextEditOutlinerView(nullptr)
+ , mpTextEditWin(nullptr)
+ , pTextEditCursorBuffer(nullptr)
+ , pMacroObj(nullptr)
+ , pMacroPV(nullptr)
+ , pMacroWin(nullptr)
+ , nMacroTol(0)
+ , mbTextEditDontDelete(false)
+ , mbTextEditOnlyOneView(false)
+ , mbTextEditNewObj(false)
+ , mbQuickTextEditMode(true)
+ , mbMacroDown(false)
+ , mpOldTextEditUndoManager(nullptr)
+{
+}
+
+SdrObjEditView::~SdrObjEditView()
+{
+ mpTextEditWin = nullptr; // so there's no ShowCursor in SdrEndTextEdit
+ assert(!IsTextEdit());
+ if (IsTextEdit())
+ suppress_fun_call_w_exception(SdrEndTextEdit());
+ mpTextEditOutliner.reset();
+ assert(nullptr == mpOldTextEditUndoManager); // should have been reset
+}
+
+bool SdrObjEditView::IsAction() const { return IsMacroObj() || SdrGlueEditView::IsAction(); }
+
+void SdrObjEditView::MovAction(const Point& rPnt)
+{
+ if (IsMacroObj())
+ MovMacroObj(rPnt);
+ SdrGlueEditView::MovAction(rPnt);
+}
+
+void SdrObjEditView::EndAction()
+{
+ if (IsMacroObj())
+ EndMacroObj();
+ SdrGlueEditView::EndAction();
+}
+
+void SdrObjEditView::BckAction()
+{
+ BrkMacroObj();
+ SdrGlueEditView::BckAction();
+}
+
+void SdrObjEditView::BrkAction()
+{
+ BrkMacroObj();
+ SdrGlueEditView::BrkAction();
+}
+
+SdrPageView* SdrObjEditView::ShowSdrPage(SdrPage* pPage)
+{
+ SdrPageView* pPageView = SdrGlueEditView::ShowSdrPage(pPage);
+
+ if (comphelper::LibreOfficeKit::isActive() && pPageView)
+ {
+ // Check if other views have an active text edit on the same page as
+ // this one.
+ SdrViewIter aIter(pPageView->GetPage());
+ for (SdrView* pView = aIter.FirstView(); pView; pView = aIter.NextView())
+ {
+ if (pView == this || !pView->IsTextEdit())
+ continue;
+
+ OutputDevice* pOutDev = GetFirstOutputDevice();
+ if (!pOutDev || pOutDev->GetOutDevType() != OUTDEV_WINDOW)
+ continue;
+
+ // Found one, so create an outliner view, to get invalidations when
+ // the text edit changes.
+ // Call GetSfxViewShell() to make sure ImpMakeOutlinerView()
+ // registers the view shell of this draw view, and not the view
+ // shell of pView.
+ OutlinerView* pOutlinerView
+ = pView->ImpMakeOutlinerView(pOutDev->GetOwnerWindow(), nullptr, GetSfxViewShell());
+ pOutlinerView->HideCursor();
+ pView->GetTextEditOutliner()->InsertView(pOutlinerView);
+ }
+ }
+
+ return pPageView;
+}
+
+namespace
+{
+/// Removes outliner views registered in other draw views that use pOutputDevice.
+void lcl_RemoveTextEditOutlinerViews(SdrObjEditView const* pThis, SdrPageView const* pPageView,
+ OutputDevice const* pOutputDevice)
+{
+ if (!comphelper::LibreOfficeKit::isActive())
+ return;
+
+ if (!pPageView)
+ return;
+
+ if (!pOutputDevice || pOutputDevice->GetOutDevType() != OUTDEV_WINDOW)
+ return;
+
+ SdrViewIter aIter(pPageView->GetPage());
+ for (SdrView* pView = aIter.FirstView(); pView; pView = aIter.NextView())
+ {
+ if (pView == pThis || !pView->IsTextEdit())
+ continue;
+
+ SdrOutliner* pOutliner = pView->GetTextEditOutliner();
+ for (size_t nView = 0; nView < pOutliner->GetViewCount(); ++nView)
+ {
+ OutlinerView* pOutlinerView = pOutliner->GetView(nView);
+ if (pOutlinerView->GetWindow()->GetOutDev() != pOutputDevice)
+ continue;
+
+ pOutliner->RemoveView(pOutlinerView);
+ delete pOutlinerView;
+ }
+ }
+}
+}
+
+void SdrObjEditView::HideSdrPage()
+{
+ lcl_RemoveTextEditOutlinerViews(this, GetSdrPageView(), GetFirstOutputDevice());
+
+ if (mpTextEditPV == GetSdrPageView())
+ {
+ // HideSdrPage() will clear mpPageView, avoid a dangling pointer.
+ mpTextEditPV = nullptr;
+ }
+
+ SdrGlueEditView::HideSdrPage();
+}
+
+void SdrObjEditView::TakeActionRect(tools::Rectangle& rRect) const
+{
+ if (IsMacroObj())
+ {
+ rRect = pMacroObj->GetCurrentBoundRect();
+ }
+ else
+ {
+ SdrGlueEditView::TakeActionRect(rRect);
+ }
+}
+
+void SdrObjEditView::Notify(SfxBroadcaster& rBC, const SfxHint& rHint)
+{
+ SdrGlueEditView::Notify(rBC, rHint);
+ if (mpTextEditOutliner == nullptr)
+ return;
+
+ // change of printer while editing
+ if (rHint.GetId() != SfxHintId::ThisIsAnSdrHint)
+ return;
+
+ const SdrHint* pSdrHint = static_cast<const SdrHint*>(&rHint);
+ SdrHintKind eKind = pSdrHint->GetKind();
+ if (eKind == SdrHintKind::RefDeviceChange)
+ {
+ mpTextEditOutliner->SetRefDevice(mpModel->GetRefDevice());
+ }
+ if (eKind == SdrHintKind::DefaultTabChange)
+ {
+ mpTextEditOutliner->SetDefTab(mpModel->GetDefaultTabulator());
+ }
+}
+
+void SdrObjEditView::ModelHasChanged()
+{
+ SdrGlueEditView::ModelHasChanged();
+ if (mxWeakTextEditObj.is() && !mxWeakTextEditObj->IsInserted())
+ SdrEndTextEdit(); // object deleted
+ // TextEditObj changed?
+ if (!IsTextEdit())
+ return;
+
+ SdrTextObj* pTextObj = mxWeakTextEditObj.get();
+ if (pTextObj != nullptr)
+ {
+ size_t nOutlViewCnt = mpTextEditOutliner->GetViewCount();
+ bool bAreaChg = false;
+ bool bAnchorChg = false;
+ bool bColorChg = false;
+ bool bContourFrame = pTextObj->IsContourTextFrame();
+ EEAnchorMode eNewAnchor(EEAnchorMode::VCenterHCenter);
+ tools::Rectangle aOldArea(aMinTextEditArea);
+ aOldArea.Union(aTextEditArea);
+ Color aNewColor;
+ { // check area
+ Size aPaperMin1;
+ Size aPaperMax1;
+ tools::Rectangle aEditArea1;
+ tools::Rectangle aMinArea1;
+ pTextObj->TakeTextEditArea(&aPaperMin1, &aPaperMax1, &aEditArea1, &aMinArea1);
+ Point aPvOfs(pTextObj->GetTextEditOffset());
+
+ // add possible GridOffset to up-to-now view-independent EditAreas
+ basegfx::B2DVector aGridOffset(0.0, 0.0);
+ if (getPossibleGridOffsetForSdrObject(aGridOffset, pTextObj, GetSdrPageView()))
+ {
+ const Point aOffset(basegfx::fround(aGridOffset.getX()),
+ basegfx::fround(aGridOffset.getY()));
+
+ aEditArea1 += aOffset;
+ aMinArea1 += aOffset;
+ }
+
+ aEditArea1.Move(aPvOfs.X(), aPvOfs.Y());
+ aMinArea1.Move(aPvOfs.X(), aPvOfs.Y());
+ tools::Rectangle aNewArea(aMinArea1);
+ aNewArea.Union(aEditArea1);
+
+ if (aNewArea != aOldArea || aEditArea1 != aTextEditArea || aMinArea1 != aMinTextEditArea
+ || mpTextEditOutliner->GetMinAutoPaperSize() != aPaperMin1
+ || mpTextEditOutliner->GetMaxAutoPaperSize() != aPaperMax1)
+ {
+ aTextEditArea = aEditArea1;
+ aMinTextEditArea = aMinArea1;
+
+ const bool bPrevUpdateLayout = mpTextEditOutliner->SetUpdateLayout(false);
+ mpTextEditOutliner->SetMinAutoPaperSize(aPaperMin1);
+ mpTextEditOutliner->SetMaxAutoPaperSize(aPaperMax1);
+ mpTextEditOutliner->SetPaperSize(Size(0, 0)); // re-format Outliner
+
+ if (!bContourFrame)
+ {
+ mpTextEditOutliner->ClearPolygon();
+ EEControlBits nStat = mpTextEditOutliner->GetControlWord();
+ nStat |= EEControlBits::AUTOPAGESIZE;
+ mpTextEditOutliner->SetControlWord(nStat);
+ }
+ else
+ {
+ EEControlBits nStat = mpTextEditOutliner->GetControlWord();
+ nStat &= ~EEControlBits::AUTOPAGESIZE;
+ mpTextEditOutliner->SetControlWord(nStat);
+ tools::Rectangle aAnchorRect;
+ pTextObj->TakeTextAnchorRect(aAnchorRect);
+ pTextObj->ImpSetContourPolygon(*mpTextEditOutliner, aAnchorRect, true);
+ }
+ for (size_t nOV = 0; nOV < nOutlViewCnt; nOV++)
+ {
+ OutlinerView* pOLV = mpTextEditOutliner->GetView(nOV);
+ EVControlBits nStat0 = pOLV->GetControlWord();
+ EVControlBits nStat = nStat0;
+ // AutoViewSize only if not ContourFrame.
+ if (!bContourFrame)
+ nStat |= EVControlBits::AUTOSIZE;
+ else
+ nStat &= ~EVControlBits::AUTOSIZE;
+ if (nStat != nStat0)
+ pOLV->SetControlWord(nStat);
+ }
+
+ mpTextEditOutliner->SetUpdateLayout(bPrevUpdateLayout);
+ bAreaChg = true;
+ }
+ }
+ if (mpTextEditOutlinerView != nullptr)
+ { // check fill and anchor
+ EEAnchorMode eOldAnchor = mpTextEditOutlinerView->GetAnchorMode();
+ eNewAnchor = pTextObj->GetOutlinerViewAnchorMode();
+ bAnchorChg = eOldAnchor != eNewAnchor;
+ Color aOldColor(mpTextEditOutlinerView->GetBackgroundColor());
+ aNewColor = GetTextEditBackgroundColor(*this);
+ bColorChg = aOldColor != aNewColor;
+ }
+ // refresh always when it's a contour frame. That
+ // refresh is necessary since it triggers the repaint
+ // which makes the Handles visible. Changes at TakeTextRect()
+ // seem to have resulted in a case where no refresh is executed.
+ // Before that, a refresh must have been always executed
+ // (else this error would have happened earlier), thus I
+ // even think here a refresh should be done always.
+ // Since follow-up problems cannot even be guessed I only
+ // add this one more case to the if below.
+ // BTW: It's VERY bad style that here, inside ModelHasChanged()
+ // the outliner is again massively changed for the text object
+ // in text edit mode. Normally, all necessary data should be
+ // set at SdrBeginTextEdit(). Some changes and value assigns in
+ // SdrBeginTextEdit() are completely useless since they are set here
+ // again on ModelHasChanged().
+ if (bContourFrame || bAreaChg || bAnchorChg || bColorChg)
+ {
+ for (size_t nOV = 0; nOV < nOutlViewCnt; nOV++)
+ {
+ OutlinerView* pOLV = mpTextEditOutliner->GetView(nOV);
+ { // invalidate old OutlinerView area
+ vcl::Window* pWin = pOLV->GetWindow();
+ tools::Rectangle aTmpRect(aOldArea);
+ sal_uInt16 nPixSiz = pOLV->GetInvalidateMore() + 1;
+ Size aMore(pWin->PixelToLogic(Size(nPixSiz, nPixSiz)));
+ aTmpRect.AdjustLeft(-(aMore.Width()));
+ aTmpRect.AdjustRight(aMore.Width());
+ aTmpRect.AdjustTop(-(aMore.Height()));
+ aTmpRect.AdjustBottom(aMore.Height());
+ InvalidateOneWin(*pWin->GetOutDev(), aTmpRect);
+ }
+ if (bAnchorChg)
+ pOLV->SetAnchorMode(eNewAnchor);
+ if (bColorChg)
+ pOLV->SetBackgroundColor(aNewColor);
+
+ pOLV->SetOutputArea(
+ aTextEditArea); // because otherwise, we're not re-anchoring correctly
+ ImpInvalidateOutlinerView(*pOLV);
+ }
+ mpTextEditOutlinerView->ShowCursor();
+ }
+ }
+ ImpMakeTextCursorAreaVisible();
+}
+
+namespace
+{
+/**
+ Helper class to visualize the content of an active EditView as an
+ OverlayObject. These objects work with Primitives and are handled
+ from the OverlayManager(s) in place as needed.
+
+ It allows complete visualization of the content of the active
+ EditView without the need of Invalidates triggered by the EditView
+ and thus avoiding potentially expensive repaints by using the
+ automatically buffered Overlay mechanism.
+
+ It buffers as much as possible locally and *only* triggers a real
+ change (see call to objectChange()) when really needed.
+ */
+class TextEditOverlayObject : public sdr::overlay::OverlayObject
+{
+protected:
+ /// local access to associated sdr::overlay::OverlaySelection
+ std::unique_ptr<sdr::overlay::OverlaySelection> mxOverlaySelection;
+
+ /// local definition depends on active OutlinerView
+ OutlinerView& mrOutlinerView;
+
+ /// geometry definitions with buffering
+ basegfx::B2DRange maLastRange;
+ basegfx::B2DRange maRange;
+
+ /// text content definitions with buffering
+ drawinglayer::primitive2d::Primitive2DContainer maTextPrimitives;
+ drawinglayer::primitive2d::Primitive2DContainer maLastTextPrimitives;
+
+ /// bitfield
+ bool mbVisualizeSurroundingFrame : 1;
+
+ // geometry creation for OverlayObject, can use local *Last* values
+ virtual drawinglayer::primitive2d::Primitive2DContainer
+ createOverlayObjectPrimitive2DSequence() override;
+
+public:
+ TextEditOverlayObject(const Color& rColor, OutlinerView& rOutlinerView,
+ bool bVisualizeSurroundingFrame);
+ virtual ~TextEditOverlayObject() override;
+
+ // data read access
+ const sdr::overlay::OverlaySelection* getOverlaySelection() const
+ {
+ return mxOverlaySelection.get();
+ }
+ const OutlinerView& getOutlinerView() const { return mrOutlinerView; }
+
+ /// override to check conditions for last createOverlayObjectPrimitive2DSequence
+ virtual drawinglayer::primitive2d::Primitive2DContainer
+ getOverlayObjectPrimitive2DSequence() const override;
+
+ // data write access. In this OverlayObject we only have the
+ // callback that triggers detecting if something *has* changed
+ void checkDataChange(const basegfx::B2DRange& rMinTextEditArea);
+ void checkSelectionChange();
+};
+
+drawinglayer::primitive2d::Primitive2DContainer
+TextEditOverlayObject::createOverlayObjectPrimitive2DSequence()
+{
+ drawinglayer::primitive2d::Primitive2DContainer aRetval;
+
+ /// outer frame visualization
+ if (mbVisualizeSurroundingFrame)
+ {
+ const double fTransparence(SvtOptionsDrawinglayer::GetTransparentSelectionPercent() * 0.01);
+ const sal_uInt16 nPixSiz(getOutlinerView().GetInvalidateMore() - 1);
+
+ aRetval.push_back(new drawinglayer::primitive2d::OverlayRectanglePrimitive(
+ maRange, getBaseColor().getBColor(), fTransparence, std::max(6, nPixSiz - 2), // grow
+ 0.0, // shrink
+ 0.0));
+ }
+
+ // add buffered TextPrimitives
+ aRetval.append(maTextPrimitives);
+
+ return aRetval;
+}
+
+TextEditOverlayObject::TextEditOverlayObject(const Color& rColor, OutlinerView& rOutlinerView,
+ bool bVisualizeSurroundingFrame)
+ : OverlayObject(rColor)
+ , mrOutlinerView(rOutlinerView)
+ , mbVisualizeSurroundingFrame(bVisualizeSurroundingFrame)
+{
+ // no AA for TextEdit overlay
+ allowAntiAliase(false);
+
+ // create local OverlaySelection - this is an integral part of EditText
+ // visualization
+ std::vector<basegfx::B2DRange> aEmptySelection{};
+ mxOverlaySelection.reset(new sdr::overlay::OverlaySelection(
+ sdr::overlay::OverlayType::Transparent, rColor, std::move(aEmptySelection), true));
+}
+
+TextEditOverlayObject::~TextEditOverlayObject()
+{
+ mxOverlaySelection.reset();
+
+ if (getOverlayManager())
+ {
+ getOverlayManager()->remove(*this);
+ }
+}
+
+drawinglayer::primitive2d::Primitive2DContainer
+TextEditOverlayObject::getOverlayObjectPrimitive2DSequence() const
+{
+ if (!getPrimitive2DSequence().empty())
+ {
+ if (!maRange.equal(maLastRange) || maLastTextPrimitives != maTextPrimitives)
+ {
+ // conditions of last local decomposition have changed, delete to force new evaluation
+ const_cast<TextEditOverlayObject*>(this)->resetPrimitive2DSequence();
+ }
+ }
+
+ if (getPrimitive2DSequence().empty())
+ {
+ // remember new buffered values
+ const_cast<TextEditOverlayObject*>(this)->maLastRange = maRange;
+ const_cast<TextEditOverlayObject*>(this)->maLastTextPrimitives = maTextPrimitives;
+ }
+
+ // call base implementation
+ return OverlayObject::getOverlayObjectPrimitive2DSequence();
+}
+
+void TextEditOverlayObject::checkDataChange(const basegfx::B2DRange& rMinTextEditArea)
+{
+ bool bObjectChange(false);
+
+ // check current range
+ const tools::Rectangle aOutArea(mrOutlinerView.GetOutputArea());
+ basegfx::B2DRange aNewRange = vcl::unotools::b2DRectangleFromRectangle(aOutArea);
+ aNewRange.expand(rMinTextEditArea);
+
+ if (aNewRange != maRange)
+ {
+ maRange = aNewRange;
+ bObjectChange = true;
+ }
+
+ // check if text primitives did change
+ SdrOutliner* pSdrOutliner = dynamic_cast<SdrOutliner*>(getOutlinerView().GetOutliner());
+
+ if (pSdrOutliner)
+ {
+ // get TextPrimitives directly from active Outliner
+ basegfx::B2DHomMatrix aNewTransformA;
+ basegfx::B2DHomMatrix aNewTransformB;
+ basegfx::B2DRange aClipRange;
+ drawinglayer::primitive2d::Primitive2DContainer aNewTextPrimitives;
+
+ // active Outliner is always in unified oriented coordinate system (currently)
+ // so just translate to TopLeft of visible Range. Keep in mind that top-left
+ // depends on vertical text and top-to-bottom text attributes
+ const tools::Rectangle aVisArea(mrOutlinerView.GetVisArea());
+ const bool bVerticalWriting(pSdrOutliner->IsVertical());
+ const bool bTopToBottom(pSdrOutliner->IsTopToBottom());
+ const double fStartInX(bVerticalWriting && bTopToBottom
+ ? aOutArea.Right() - aVisArea.Left()
+ : aOutArea.Left() - aVisArea.Left());
+ const double fStartInY(bVerticalWriting && !bTopToBottom
+ ? aOutArea.Bottom() - aVisArea.Top()
+ : aOutArea.Top() - aVisArea.Top());
+
+ aNewTransformB.translate(fStartInX, fStartInY);
+
+ // get the current TextPrimitives. This is the most expensive part
+ // of this mechanism, it *may* be possible to buffer layouted
+ // primitives per ParaPortion with/in/dependent on the EditEngine
+ // content if needed. For now, get and compare
+ SdrTextObj::impDecomposeBlockTextPrimitiveDirect(
+ aNewTextPrimitives, *pSdrOutliner, aNewTransformA, aNewTransformB, aClipRange);
+
+ if (aNewTextPrimitives != maTextPrimitives)
+ {
+ maTextPrimitives = std::move(aNewTextPrimitives);
+ bObjectChange = true;
+ }
+ }
+
+ if (bObjectChange)
+ {
+ // if there really *was* a change signal the OverlayManager to
+ // refresh this object's visualization
+ objectChange();
+
+ // on data change, always do a SelectionChange, too
+ // since the selection is an integral part of text visualization
+ checkSelectionChange();
+ }
+}
+
+void TextEditOverlayObject::checkSelectionChange()
+{
+ if (!(getOverlaySelection() && getOverlayManager()))
+ return;
+
+ std::vector<tools::Rectangle> aLogicRects;
+ std::vector<basegfx::B2DRange> aLogicRanges;
+ const Size aLogicPixel(getOverlayManager()->getOutputDevice().PixelToLogic(Size(1, 1)));
+
+ // get logic selection
+ getOutlinerView().GetSelectionRectangles(aLogicRects);
+
+ aLogicRanges.reserve(aLogicRects.size());
+ for (const auto& aRect : aLogicRects)
+ {
+ // convert from logic Rectangles to logic Ranges, do not forget to add
+ // one Unit (in this case logical units for one pixel, pre-calculated)
+ aLogicRanges.emplace_back(
+ aRect.Left() - aLogicPixel.Width(), aRect.Top() - aLogicPixel.Height(),
+ aRect.Right() + aLogicPixel.Width(), aRect.Bottom() + aLogicPixel.Height());
+ }
+
+ mxOverlaySelection->setRanges(std::move(aLogicRanges));
+}
+} // end of anonymous namespace
+
+// TextEdit
+
+// callback from the active EditView, forward to evtl. existing instances of the
+// TextEditOverlayObject(s). This will additionally update the selection which
+// is an integral part of the text visualization
+void SdrObjEditView::EditViewInvalidate(const tools::Rectangle&)
+{
+ if (!IsTextEdit())
+ return;
+
+ // MinTextRange may have changed. Forward it, too
+ const basegfx::B2DRange aMinTextRange
+ = vcl::unotools::b2DRectangleFromRectangle(aMinTextEditArea);
+
+ for (sal_uInt32 a(0); a < maTEOverlayGroup.count(); a++)
+ {
+ TextEditOverlayObject* pCandidate
+ = dynamic_cast<TextEditOverlayObject*>(&maTEOverlayGroup.getOverlayObject(a));
+
+ if (pCandidate)
+ {
+ pCandidate->checkDataChange(aMinTextRange);
+ }
+ }
+}
+
+// callback from the active EditView, forward to evtl. existing instances of the
+// TextEditOverlayObject(s). This cvall *only* updates the selection visualization
+// which is e.g. used when only the selection is changed, but not the text
+void SdrObjEditView::EditViewSelectionChange()
+{
+ if (!IsTextEdit())
+ return;
+
+ for (sal_uInt32 a(0); a < maTEOverlayGroup.count(); a++)
+ {
+ TextEditOverlayObject* pCandidate
+ = dynamic_cast<TextEditOverlayObject*>(&maTEOverlayGroup.getOverlayObject(a));
+
+ if (pCandidate)
+ {
+ pCandidate->checkSelectionChange();
+ }
+ }
+}
+
+OutputDevice& SdrObjEditView::EditViewOutputDevice() const { return *mpTextEditWin->GetOutDev(); }
+
+Point SdrObjEditView::EditViewPointerPosPixel() const
+{
+ return mpTextEditWin->GetPointerPosPixel();
+}
+
+css::uno::Reference<css::datatransfer::clipboard::XClipboard> SdrObjEditView::GetClipboard() const
+{
+ if (!mpTextEditWin)
+ return nullptr;
+ return mpTextEditWin->GetClipboard();
+}
+
+css::uno::Reference<css::datatransfer::dnd::XDropTarget> SdrObjEditView::GetDropTarget()
+{
+ if (!mpTextEditWin)
+ return nullptr;
+ return mpTextEditWin->GetDropTarget();
+}
+
+void SdrObjEditView::EditViewInputContext(const InputContext& rInputContext)
+{
+ if (!mpTextEditWin)
+ return;
+ mpTextEditWin->SetInputContext(rInputContext);
+}
+
+void SdrObjEditView::EditViewCursorRect(const tools::Rectangle& rRect, int nExtTextInputWidth)
+{
+ if (!mpTextEditWin)
+ return;
+ mpTextEditWin->SetCursorRect(&rRect, nExtTextInputWidth);
+}
+
+void SdrObjEditView::TextEditDrawing(SdrPaintWindow& rPaintWindow)
+{
+ if (!comphelper::LibreOfficeKit::isActive())
+ {
+ // adapt all TextEditOverlayObject(s), so call EditViewInvalidate()
+ // to update accordingly (will update selection, too). Suppress new
+ // stuff when LibreOfficeKit is active
+ EditViewInvalidate(tools::Rectangle());
+ }
+ else
+ {
+ // draw old text edit stuff
+ if (IsTextEdit())
+ {
+ const SdrOutliner* pActiveOutliner = GetTextEditOutliner();
+
+ if (pActiveOutliner)
+ {
+ const sal_uInt32 nViewCount(pActiveOutliner->GetViewCount());
+
+ if (nViewCount)
+ {
+ const vcl::Region& rRedrawRegion = rPaintWindow.GetRedrawRegion();
+ const tools::Rectangle aCheckRect(rRedrawRegion.GetBoundRect());
+
+ for (sal_uInt32 i(0); i < nViewCount; i++)
+ {
+ OutlinerView* pOLV = pActiveOutliner->GetView(i);
+
+ // If rPaintWindow knows that the output device is a render
+ // context and is aware of the underlying vcl::Window,
+ // compare against that; that's how double-buffering can
+ // still find the matching OutlinerView.
+ OutputDevice* pOutputDevice = rPaintWindow.GetWindow()
+ ? rPaintWindow.GetWindow()->GetOutDev()
+ : &rPaintWindow.GetOutputDevice();
+ if (pOLV->GetWindow()->GetOutDev() == pOutputDevice
+ || comphelper::LibreOfficeKit::isActive())
+ {
+ ImpPaintOutlinerView(*pOLV, aCheckRect,
+ rPaintWindow.GetTargetOutputDevice());
+ return;
+ }
+ }
+ }
+ }
+ }
+ }
+}
+
+void SdrObjEditView::ImpPaintOutlinerView(OutlinerView& rOutlView, const tools::Rectangle& rRect,
+ OutputDevice& rTargetDevice) const
+{
+ const SdrTextObj* pText = GetTextEditObject();
+ bool bTextFrame(pText && pText->IsTextFrame());
+ bool bFitToSize(mpTextEditOutliner->GetControlWord() & EEControlBits::STRETCHING);
+ bool bModified(mpTextEditOutliner->IsModified());
+ tools::Rectangle aBlankRect(rOutlView.GetOutputArea());
+ aBlankRect.Union(aMinTextEditArea);
+ tools::Rectangle aPixRect(rTargetDevice.LogicToPixel(aBlankRect));
+
+ // in the tiled rendering case, the setup is incomplete, and we very
+ // easily get an empty rRect on input - that will cause that everything is
+ // clipped; happens in case of editing text inside a shape in Calc.
+ // FIXME would be better to complete the setup so that we don't get an
+ // empty rRect here
+ if (!comphelper::LibreOfficeKit::isActive() || !rRect.IsEmpty())
+ aBlankRect.Intersection(rRect);
+
+ rOutlView.GetOutliner()->SetUpdateLayout(true); // Bugfix #22596#
+ rOutlView.Paint(aBlankRect, &rTargetDevice);
+
+ if (!bModified)
+ {
+ mpTextEditOutliner->ClearModifyFlag();
+ }
+
+ if (bTextFrame && !bFitToSize)
+ {
+ // completely reworked to use primitives; this ensures same look and functionality
+ const drawinglayer::geometry::ViewInformation2D aViewInformation2D;
+ std::unique_ptr<drawinglayer::processor2d::BaseProcessor2D> xProcessor(
+ drawinglayer::processor2d::createProcessor2DFromOutputDevice(rTargetDevice,
+ aViewInformation2D));
+
+ const bool bMapModeEnabled(rTargetDevice.IsMapModeEnabled());
+ const basegfx::B2DRange aRange = vcl::unotools::b2DRectangleFromRectangle(aPixRect);
+ const Color aHilightColor(SvtOptionsDrawinglayer::getHilightColor());
+ const double fTransparence(SvtOptionsDrawinglayer::GetTransparentSelectionPercent() * 0.01);
+ const sal_uInt16 nPixSiz(rOutlView.GetInvalidateMore() - 1);
+ const drawinglayer::primitive2d::Primitive2DReference xReference(
+ new drawinglayer::primitive2d::OverlayRectanglePrimitive(
+ aRange, aHilightColor.getBColor(), fTransparence, std::max(6, nPixSiz - 2), // grow
+ 0.0, // shrink
+ 0.0));
+ const drawinglayer::primitive2d::Primitive2DContainer aSequence{ xReference };
+
+ rTargetDevice.EnableMapMode(false);
+ xProcessor->process(aSequence);
+ rTargetDevice.EnableMapMode(bMapModeEnabled);
+ }
+
+ rOutlView.ShowCursor(/*bGotoCursor=*/true, /*bActivate=*/true);
+}
+
+void SdrObjEditView::ImpInvalidateOutlinerView(OutlinerView const& rOutlView) const
+{
+ vcl::Window* pWin = rOutlView.GetWindow();
+
+ if (!pWin)
+ return;
+
+ const SdrTextObj* pText = GetTextEditObject();
+ bool bTextFrame(pText && pText->IsTextFrame());
+ bool bFitToSize(pText && pText->IsFitToSize());
+
+ if (!bTextFrame || bFitToSize)
+ return;
+
+ tools::Rectangle aBlankRect(rOutlView.GetOutputArea());
+ aBlankRect.Union(aMinTextEditArea);
+ tools::Rectangle aPixRect(pWin->LogicToPixel(aBlankRect));
+ sal_uInt16 nPixSiz(rOutlView.GetInvalidateMore() - 1);
+
+ aPixRect.AdjustLeft(-1);
+ aPixRect.AdjustTop(-1);
+ aPixRect.AdjustRight(1);
+ aPixRect.AdjustBottom(1);
+
+ {
+ // limit xPixRect because of driver problems when pixel coordinates are too far out
+ Size aMaxXY(pWin->GetOutputSizePixel());
+ tools::Long a(2 * nPixSiz);
+ tools::Long nMaxX(aMaxXY.Width() + a);
+ tools::Long nMaxY(aMaxXY.Height() + a);
+
+ if (aPixRect.Left() < -a)
+ aPixRect.SetLeft(-a);
+ if (aPixRect.Top() < -a)
+ aPixRect.SetTop(-a);
+ if (aPixRect.Right() > nMaxX)
+ aPixRect.SetRight(nMaxX);
+ if (aPixRect.Bottom() > nMaxY)
+ aPixRect.SetBottom(nMaxY);
+ }
+
+ tools::Rectangle aOuterPix(aPixRect);
+ aOuterPix.AdjustLeft(-nPixSiz);
+ aOuterPix.AdjustTop(-nPixSiz);
+ aOuterPix.AdjustRight(nPixSiz);
+ aOuterPix.AdjustBottom(nPixSiz);
+
+ bool bMapModeEnabled(pWin->IsMapModeEnabled());
+ pWin->EnableMapMode(false);
+ pWin->Invalidate(aOuterPix);
+ pWin->EnableMapMode(bMapModeEnabled);
+}
+
+OutlinerView* SdrObjEditView::ImpMakeOutlinerView(vcl::Window* pWin, OutlinerView* pGivenView,
+ SfxViewShell* pViewShell) const
+{
+ // background
+ Color aBackground(GetTextEditBackgroundColor(*this));
+ SdrTextObj* pText = mxWeakTextEditObj.get();
+ bool bTextFrame = pText != nullptr && pText->IsTextFrame();
+ bool bContourFrame = pText != nullptr && pText->IsContourTextFrame();
+ // create OutlinerView
+ OutlinerView* pOutlView = pGivenView;
+ mpTextEditOutliner->SetUpdateLayout(false);
+
+ if (pOutlView == nullptr)
+ {
+ pOutlView = new OutlinerView(mpTextEditOutliner.get(), pWin);
+ }
+ else
+ {
+ pOutlView->SetWindow(pWin);
+ }
+
+ if (mbNegativeX)
+ pOutlView->GetEditView().SetNegativeX(mbNegativeX);
+
+ // disallow scrolling
+ EVControlBits nStat = pOutlView->GetControlWord();
+ nStat &= ~EVControlBits::AUTOSCROLL;
+ // AutoViewSize only if not ContourFrame.
+ if (!bContourFrame)
+ nStat |= EVControlBits::AUTOSIZE;
+ if (bTextFrame)
+ {
+ sal_uInt16 nPixSiz = maHdlList.GetHdlSize() * 2 + 1;
+ nStat |= EVControlBits::INVONEMORE;
+ pOutlView->SetInvalidateMore(nPixSiz);
+ }
+ pOutlView->SetControlWord(nStat);
+ pOutlView->SetBackgroundColor(aBackground);
+
+ // In case we're in the process of constructing a new view shell,
+ // SfxViewShell::Current() may still point to the old one. So if possible,
+ // depend on the application owning this draw view to provide the view
+ // shell.
+ SfxViewShell* pSfxViewShell = pViewShell ? pViewShell : GetSfxViewShell();
+ pOutlView->RegisterViewShell(pSfxViewShell ? pSfxViewShell : SfxViewShell::Current());
+
+ if (pText != nullptr)
+ {
+ pOutlView->SetAnchorMode(pText->GetOutlinerViewAnchorMode());
+ mpTextEditOutliner->SetFixedCellHeight(
+ pText->GetMergedItem(SDRATTR_TEXT_USEFIXEDCELLHEIGHT).GetValue());
+ }
+ // do update before setting output area so that aTextEditArea can be recalculated
+ mpTextEditOutliner->SetUpdateLayout(true);
+ pOutlView->SetOutputArea(aTextEditArea);
+ ImpInvalidateOutlinerView(*pOutlView);
+ return pOutlView;
+}
+
+IMPL_LINK(SdrObjEditView, ImpOutlinerStatusEventHdl, EditStatus&, rEditStat, void)
+{
+ if (mpTextEditOutliner)
+ {
+ SdrTextObj* pTextObj = mxWeakTextEditObj.get();
+ if (pTextObj)
+ {
+ pTextObj->onEditOutlinerStatusEvent(&rEditStat);
+ }
+ }
+}
+
+void SdrObjEditView::ImpChainingEventHdl()
+{
+ if (!mpTextEditOutliner)
+ return;
+
+ SdrTextObj* pTextObj = mxWeakTextEditObj.get();
+ OutlinerView* pOLV = GetTextEditOutlinerView();
+ if (pTextObj && pOLV)
+ {
+ TextChain* pTextChain = pTextObj->GetTextChain();
+
+ // XXX: IsChainable and GetNilChainingEvent are a bit mixed up atm
+ if (!pTextObj->IsChainable())
+ {
+ return;
+ }
+ // This is true during an underflow-caused overflow (with pEdtOutl->SetText())
+ if (pTextChain->GetNilChainingEvent(pTextObj))
+ {
+ return;
+ }
+
+ // We prevent to trigger further handling of overflow/underflow for pTextObj
+ pTextChain->SetNilChainingEvent(pTextObj, true); // XXX
+
+ // Save previous selection pos // NOTE: It must be done to have the right CursorEvent in KeyInput
+ pTextChain->SetPreChainingSel(pTextObj, pOLV->GetSelection());
+ //maPreChainingSel = new ESelection(pOLV->GetSelection());
+
+ // Handling Undo
+ const int nText = 0; // XXX: hardcoded index (SdrTextObj::getText handles only 0)
+
+ const bool bUndoEnabled = GetModel() && IsUndoEnabled();
+ std::unique_ptr<SdrUndoObjSetText> pTxtUndo;
+ if (bUndoEnabled)
+ pTxtUndo.reset(
+ dynamic_cast<SdrUndoObjSetText*>(GetModel()
+ ->GetSdrUndoFactory()
+ .CreateUndoObjectSetText(*pTextObj, nText)
+ .release()));
+
+ // trigger actual chaining
+ pTextObj->onChainingEvent();
+
+ if (pTxtUndo)
+ {
+ pTxtUndo->AfterSetText();
+ if (!pTxtUndo->IsDifferent())
+ {
+ pTxtUndo.reset();
+ }
+ }
+
+ if (pTxtUndo)
+ AddUndo(std::move(pTxtUndo));
+
+ //maCursorEvent = new CursorChainingEvent(pTextChain->GetCursorEvent(pTextObj));
+ //SdrTextObj *pNextLink = pTextObj->GetNextLinkInChain();
+
+ // NOTE: Must be called. Don't let the function return if you set it to true and not reset it
+ pTextChain->SetNilChainingEvent(pTextObj, false);
+ }
+ else
+ {
+ // XXX
+ SAL_INFO("svx.chaining", "[OnChaining] No Edit Outliner View");
+ }
+}
+
+IMPL_LINK_NOARG(SdrObjEditView, ImpAfterCutOrPasteChainingEventHdl, LinkParamNone*, void)
+{
+ SdrTextObj* pTextObj = GetTextEditObject();
+ if (!pTextObj)
+ return;
+ ImpChainingEventHdl();
+ TextChainCursorManager aCursorManager(this, pTextObj);
+ ImpMoveCursorAfterChainingEvent(&aCursorManager);
+}
+
+void SdrObjEditView::ImpMoveCursorAfterChainingEvent(TextChainCursorManager* pCursorManager)
+{
+ if (!mxWeakTextEditObj.is() || !pCursorManager)
+ return;
+
+ SdrTextObj* pTextObj = mxWeakTextEditObj.get();
+
+ // Check if it has links to move it to
+ if (!pTextObj || !pTextObj->IsChainable())
+ return;
+
+ TextChain* pTextChain = pTextObj->GetTextChain();
+ ESelection aNewSel = pTextChain->GetPostChainingSel(pTextObj);
+
+ pCursorManager->HandleCursorEventAfterChaining(pTextChain->GetCursorEvent(pTextObj), aNewSel);
+
+ // Reset event
+ pTextChain->SetCursorEvent(pTextObj, CursorChainingEvent::NULL_EVENT);
+}
+
+IMPL_LINK(SdrObjEditView, ImpOutlinerCalcFieldValueHdl, EditFieldInfo*, pFI, void)
+{
+ bool bOk = false;
+ OUString& rStr = pFI->GetRepresentation();
+ rStr.clear();
+ SdrTextObj* pTextObj = mxWeakTextEditObj.get();
+ if (pTextObj != nullptr)
+ {
+ std::optional<Color> pTxtCol;
+ std::optional<Color> pFldCol;
+ bOk = pTextObj->CalcFieldValue(pFI->GetField(), pFI->GetPara(), pFI->GetPos(), true,
+ pTxtCol, pFldCol, rStr);
+ if (bOk)
+ {
+ if (pTxtCol)
+ {
+ pFI->SetTextColor(*pTxtCol);
+ }
+ if (pFldCol)
+ {
+ pFI->SetFieldColor(*pFldCol);
+ }
+ else
+ {
+ pFI->SetFieldColor(COL_LIGHTGRAY); // TODO: remove this later on (357)
+ }
+ }
+ }
+ Outliner& rDrawOutl = mpModel->GetDrawOutliner(pTextObj);
+ Link<EditFieldInfo*, void> aDrawOutlLink = rDrawOutl.GetCalcFieldValueHdl();
+ if (!bOk && aDrawOutlLink.IsSet())
+ {
+ aDrawOutlLink.Call(pFI);
+ bOk = !rStr.isEmpty();
+ }
+ if (!bOk)
+ {
+ aOldCalcFieldValueLink.Call(pFI);
+ }
+}
+
+IMPL_LINK_NOARG(SdrObjEditView, EndTextEditHdl, SdrUndoManager*, void) { SdrEndTextEdit(); }
+
+// Default implementation - null UndoManager
+std::unique_ptr<SdrUndoManager> SdrObjEditView::createLocalTextUndoManager()
+{
+ SAL_WARN("svx", "SdrObjEditView::createLocalTextUndoManager needs to be overridden");
+ return std::unique_ptr<SdrUndoManager>();
+}
+
+bool SdrObjEditView::SdrBeginTextEdit(SdrObject* pObj_, SdrPageView* pPV, vcl::Window* pWin,
+ bool bIsNewObj, SdrOutliner* pGivenOutliner,
+ OutlinerView* pGivenOutlinerView, bool bDontDeleteOutliner,
+ bool bOnlyOneView, bool bGrabFocus)
+{
+ // FIXME cannot be an assert() yet, the code is not ready for that;
+ // eg. press F7 in Impress when you are inside a text object with spelling
+ // mistakes => boom; and it is unclear how to avoid that
+ SAL_WARN_IF(IsTextEdit(), "svx", "SdrBeginTextEdit called when IsTextEdit() is already true.");
+ // FIXME this encourages all sorts of bad habits and should be removed
+ SdrEndTextEdit();
+
+ SdrTextObj* pObj = dynamic_cast<SdrTextObj*>(pObj_);
+ if (!pObj)
+ return false; // currently only possible with text objects
+
+ if (bGrabFocus && pWin)
+ {
+ // attention, this call may cause an EndTextEdit() call to this view
+ pWin->GrabFocus(); // to force the cursor into the edit view
+ }
+
+ mbTextEditDontDelete = bDontDeleteOutliner && pGivenOutliner != nullptr;
+ mbTextEditOnlyOneView = bOnlyOneView;
+ mbTextEditNewObj = bIsNewObj;
+ const sal_uInt32 nWinCount(PaintWindowCount());
+
+ bool bBrk(false);
+
+ if (!pWin)
+ {
+ for (sal_uInt32 i = 0; i < nWinCount && !pWin; i++)
+ {
+ SdrPaintWindow* pPaintWindow = GetPaintWindow(i);
+
+ if (OUTDEV_WINDOW == pPaintWindow->GetOutputDevice().GetOutDevType())
+ {
+ pWin = pPaintWindow->GetOutputDevice().GetOwnerWindow();
+ }
+ }
+
+ // break, when no window exists
+ if (!pWin)
+ {
+ bBrk = true;
+ }
+ }
+
+ if (!bBrk && !pPV)
+ {
+ pPV = GetSdrPageView();
+
+ // break, when no PageView for the object exists
+ if (!pPV)
+ {
+ bBrk = true;
+ }
+ }
+
+ // no TextEdit on objects in locked Layer
+ if (pPV && pPV->GetLockedLayers().IsSet(pObj->GetLayer()))
+ {
+ bBrk = true;
+ }
+
+ if (mpTextEditOutliner)
+ {
+ OSL_FAIL("SdrObjEditView::SdrBeginTextEdit(): Old Outliner still exists.");
+ mpTextEditOutliner.reset();
+ }
+
+ if (!bBrk)
+ {
+ mpTextEditWin = pWin;
+ mpTextEditPV = pPV;
+ mxWeakTextEditObj.reset(pObj);
+ if (pGivenOutliner)
+ {
+ mpTextEditOutliner.reset(pGivenOutliner);
+ pGivenOutliner = nullptr; // so we don't delete it on the error path
+ }
+ else
+ mpTextEditOutliner = SdrMakeOutliner(OutlinerMode::TextObject,
+ mxWeakTextEditObj->getSdrModelFromSdrObject());
+
+ {
+ SvtAccessibilityOptions aOptions;
+ mpTextEditOutliner->ForceAutoColor(aOptions.GetIsAutomaticFontColor());
+ }
+
+ aOldCalcFieldValueLink = mpTextEditOutliner->GetCalcFieldValueHdl();
+ // FieldHdl has to be set by SdrBeginTextEdit, because this call an UpdateFields
+ mpTextEditOutliner->SetCalcFieldValueHdl(
+ LINK(this, SdrObjEditView, ImpOutlinerCalcFieldValueHdl));
+ mpTextEditOutliner->SetBeginPasteOrDropHdl(LINK(this, SdrObjEditView, BeginPasteOrDropHdl));
+ mpTextEditOutliner->SetEndPasteOrDropHdl(LINK(this, SdrObjEditView, EndPasteOrDropHdl));
+
+ // It is just necessary to make the visualized page known. Set it.
+ mpTextEditOutliner->setVisualizedPage(pPV->GetPage());
+
+ mpTextEditOutliner->SetTextObjNoInit(mxWeakTextEditObj.get());
+
+ if (mxWeakTextEditObj->BegTextEdit(*mpTextEditOutliner))
+ {
+ SdrTextObj* pTextObj = mxWeakTextEditObj.get();
+ DBG_ASSERT(pTextObj, "svx::SdrObjEditView::BegTextEdit(), no text object?");
+ if (!pTextObj)
+ return false;
+
+ // switch off any running TextAnimations
+ pTextObj->SetTextAnimationAllowed(false);
+
+ // remember old cursor
+ if (mpTextEditOutliner->GetViewCount() != 0)
+ {
+ mpTextEditOutliner->RemoveView(static_cast<size_t>(0));
+ }
+
+ // Determine EditArea via TakeTextEditArea.
+ // TODO: This could theoretically be left out, because TakeTextRect() calculates the aTextEditArea,
+ // but aMinTextEditArea has to happen, too (therefore leaving this in right now)
+ pTextObj->TakeTextEditArea(nullptr, nullptr, &aTextEditArea, &aMinTextEditArea);
+
+ tools::Rectangle aTextRect;
+ tools::Rectangle aAnchorRect;
+ pTextObj->TakeTextRect(*mpTextEditOutliner, aTextRect, true,
+ &aAnchorRect /* Give true here, not false */);
+
+ if (!pTextObj->IsContourTextFrame())
+ {
+ // FitToSize not together with ContourFrame, for now
+ if (pTextObj->IsFitToSize())
+ aTextRect = aAnchorRect;
+ }
+
+ aTextEditArea = aTextRect;
+
+ // add possible GridOffset to up-to-now view-independent EditAreas
+ basegfx::B2DVector aGridOffset(0.0, 0.0);
+ if (getPossibleGridOffsetForSdrObject(aGridOffset, pTextObj, pPV))
+ {
+ const Point aOffset(basegfx::fround(aGridOffset.getX()),
+ basegfx::fround(aGridOffset.getY()));
+
+ aTextEditArea += aOffset;
+ aMinTextEditArea += aOffset;
+ }
+
+ Point aPvOfs(pTextObj->GetTextEditOffset());
+ aTextEditArea.Move(aPvOfs.X(), aPvOfs.Y());
+ aMinTextEditArea.Move(aPvOfs.X(), aPvOfs.Y());
+ pTextEditCursorBuffer = pWin->GetCursor();
+
+ maHdlList.SetMoveOutside(true);
+
+ // Since IsMarkHdlWhenTextEdit() is ignored, it is necessary
+ // to call AdjustMarkHdl() always.
+ AdjustMarkHdl();
+
+ mpTextEditOutlinerView = ImpMakeOutlinerView(pWin, pGivenOutlinerView);
+
+ if (!comphelper::LibreOfficeKit::isActive() && mpTextEditOutlinerView)
+ {
+ // activate visualization of EditView on Overlay, suppress when
+ // LibreOfficeKit is active
+ mpTextEditOutlinerView->GetEditView().setEditViewCallbacks(this);
+
+ const Color aHilightColor(SvtOptionsDrawinglayer::getHilightColor());
+ const SdrTextObj* pText = GetTextEditObject();
+ const bool bTextFrame(pText && pText->IsTextFrame());
+ const bool bFitToSize(mpTextEditOutliner->GetControlWord()
+ & EEControlBits::STRETCHING);
+ const bool bVisualizeSurroundingFrame(bTextFrame && !bFitToSize);
+ SdrPageView* pPageView = GetSdrPageView();
+
+ if (pPageView)
+ {
+ for (sal_uInt32 b(0); b < pPageView->PageWindowCount(); b++)
+ {
+ const SdrPageWindow& rPageWindow = *pPageView->GetPageWindow(b);
+
+ if (rPageWindow.GetPaintWindow().OutputToWindow())
+ {
+ const rtl::Reference<sdr::overlay::OverlayManager>& xManager
+ = rPageWindow.GetOverlayManager();
+ if (xManager.is())
+ {
+ std::unique_ptr<TextEditOverlayObject> pNewTextEditOverlayObject(
+ new TextEditOverlayObject(aHilightColor,
+ *mpTextEditOutlinerView,
+ bVisualizeSurroundingFrame));
+
+ xManager->add(*pNewTextEditOverlayObject);
+ xManager->add(const_cast<sdr::overlay::OverlaySelection&>(
+ *pNewTextEditOverlayObject->getOverlaySelection()));
+
+ maTEOverlayGroup.append(std::move(pNewTextEditOverlayObject));
+ }
+ }
+ }
+ }
+ }
+
+ // check if this view is already inserted
+ size_t i2, nCount = mpTextEditOutliner->GetViewCount();
+ for (i2 = 0; i2 < nCount; i2++)
+ {
+ if (mpTextEditOutliner->GetView(i2) == mpTextEditOutlinerView)
+ break;
+ }
+
+ if (i2 == nCount)
+ mpTextEditOutliner->InsertView(mpTextEditOutlinerView, 0);
+
+ maHdlList.SetMoveOutside(false);
+ maHdlList.SetMoveOutside(true);
+
+ // register all windows as OutlinerViews with the Outliner
+ if (!bOnlyOneView)
+ {
+ for (sal_uInt32 i = 0; i < nWinCount; i++)
+ {
+ SdrPaintWindow* pPaintWindow = GetPaintWindow(i);
+ OutputDevice& rOutDev = pPaintWindow->GetOutputDevice();
+
+ if (&rOutDev != pWin->GetOutDev() && OUTDEV_WINDOW == rOutDev.GetOutDevType())
+ {
+ OutlinerView* pOutlView
+ = ImpMakeOutlinerView(rOutDev.GetOwnerWindow(), nullptr);
+ mpTextEditOutliner->InsertView(pOutlView, static_cast<sal_uInt16>(i));
+ }
+ }
+
+ if (comphelper::LibreOfficeKit::isActive())
+ {
+ // Register an outliner view for all other sdr views that
+ // show the same page, so that when the text edit changes,
+ // all interested windows get an invalidation.
+ SdrViewIter aIter(pObj->getSdrPageFromSdrObject());
+ for (SdrView* pView = aIter.FirstView(); pView; pView = aIter.NextView())
+ {
+ if (pView == this)
+ continue;
+
+ for (sal_uInt32 nViewPaintWindow = 0;
+ nViewPaintWindow < pView->PaintWindowCount(); ++nViewPaintWindow)
+ {
+ SdrPaintWindow* pPaintWindow = pView->GetPaintWindow(nViewPaintWindow);
+ OutputDevice& rOutDev = pPaintWindow->GetOutputDevice();
+
+ if (&rOutDev != pWin->GetOutDev()
+ && OUTDEV_WINDOW == rOutDev.GetOutDevType())
+ {
+ OutlinerView* pOutlView
+ = ImpMakeOutlinerView(rOutDev.GetOwnerWindow(), nullptr);
+ pOutlView->HideCursor();
+ rOutDev.GetOwnerWindow()->SetCursor(nullptr);
+ mpTextEditOutliner->InsertView(pOutlView);
+ }
+ }
+ }
+ }
+ }
+
+ mpTextEditOutlinerView->ShowCursor();
+ mpTextEditOutliner->SetStatusEventHdl(
+ LINK(this, SdrObjEditView, ImpOutlinerStatusEventHdl));
+ if (pTextObj->IsChainable())
+ {
+ mpTextEditOutlinerView->SetEndCutPasteLinkHdl(
+ LINK(this, SdrObjEditView, ImpAfterCutOrPasteChainingEventHdl));
+ }
+
+ mpTextEditOutliner->ClearModifyFlag();
+
+ if (pTextObj->IsFitToSize())
+ {
+ pWin->Invalidate(aTextEditArea);
+ }
+
+ if (GetModel())
+ {
+ SdrHint aHint(SdrHintKind::BeginEdit, *pTextObj);
+ GetModel()->Broadcast(aHint);
+ }
+
+ mpTextEditOutliner->setVisualizedPage(nullptr);
+
+ if (mxSelectionController.is())
+ mxSelectionController->onSelectionHasChanged();
+
+ if (GetModel() && IsUndoEnabled()
+ && !GetModel()->GetDisableTextEditUsesCommonUndoManager())
+ {
+ SdrUndoManager* pSdrUndoManager = nullptr;
+ mpLocalTextEditUndoManager = createLocalTextUndoManager();
+
+ if (mpLocalTextEditUndoManager)
+ pSdrUndoManager = mpLocalTextEditUndoManager.get();
+
+ if (pSdrUndoManager)
+ {
+ // we have an outliner, undo manager and it's an EditUndoManager, exchange
+ // the document undo manager and the default one from the outliner and tell
+ // it that text edit starts by setting a callback if it needs to end text edit mode.
+ assert(nullptr == mpOldTextEditUndoManager);
+
+ mpOldTextEditUndoManager = mpTextEditOutliner->SetUndoManager(pSdrUndoManager);
+ pSdrUndoManager->SetEndTextEditHdl(LINK(this, SdrObjEditView, EndTextEditHdl));
+ }
+ else
+ {
+ OSL_ENSURE(false,
+ "The document undo manager is not derived from SdrUndoManager (!)");
+ }
+ }
+
+ return true; // ran fine, let TextEdit run now
+ }
+ else
+ {
+ mpTextEditOutliner->SetCalcFieldValueHdl(aOldCalcFieldValueLink);
+ mpTextEditOutliner->SetBeginPasteOrDropHdl(Link<PasteOrDropInfos*, void>());
+ mpTextEditOutliner->SetEndPasteOrDropHdl(Link<PasteOrDropInfos*, void>());
+ }
+ }
+ if (mpTextEditOutliner != nullptr)
+ {
+ mpTextEditOutliner->setVisualizedPage(nullptr);
+ }
+
+ // something went wrong...
+ if (!bDontDeleteOutliner)
+ {
+ delete pGivenOutliner;
+ if (pGivenOutlinerView != nullptr)
+ {
+ delete pGivenOutlinerView;
+ pGivenOutlinerView = nullptr;
+ }
+ }
+ mpTextEditOutliner.reset();
+
+ mpTextEditOutlinerView = nullptr;
+ mxWeakTextEditObj.reset(nullptr);
+ mpTextEditPV = nullptr;
+ mpTextEditWin = nullptr;
+ maHdlList.SetMoveOutside(false);
+
+ return false;
+}
+
+SdrEndTextEditKind SdrObjEditView::SdrEndTextEdit(bool bDontDeleteReally)
+{
+ SdrEndTextEditKind eRet = SdrEndTextEditKind::Unchanged;
+ SdrTextObj* pTEObj = mxWeakTextEditObj.get();
+ vcl::Window* pTEWin = mpTextEditWin;
+ OutlinerView* pTEOutlinerView = mpTextEditOutlinerView;
+ vcl::Cursor* pTECursorBuffer = pTextEditCursorBuffer;
+ SdrUndoManager* pUndoEditUndoManager = nullptr;
+ bool bNeedToUndoSavedRedoTextEdit(false);
+
+ if (GetModel() && IsUndoEnabled() && pTEObj && mpTextEditOutliner
+ && !GetModel()->GetDisableTextEditUsesCommonUndoManager())
+ {
+ // change back the UndoManager to the remembered original one
+ SfxUndoManager* pOriginal = mpTextEditOutliner->SetUndoManager(mpOldTextEditUndoManager);
+ mpOldTextEditUndoManager = nullptr;
+
+ if (pOriginal)
+ {
+ // check if we got back our document undo manager
+ SdrUndoManager* pSdrUndoManager = mpLocalTextEditUndoManager.get();
+
+ if (pSdrUndoManager && dynamic_cast<SdrUndoManager*>(pOriginal) == pSdrUndoManager)
+ {
+ if (pSdrUndoManager->isEndTextEditTriggeredFromUndo())
+ {
+ // remember the UndoManager where missing Undos have to be triggered after end
+ // text edit. When the undo had triggered the end text edit, the original action
+ // which had to be undone originally is not yet undone.
+ pUndoEditUndoManager = pSdrUndoManager;
+
+ // We are ending text edit; if text edit was triggered from undo, execute all redos
+ // to create a complete text change undo action for the redo buffer. Also mark this
+ // state when at least one redo was executed; the created extra TextChange needs to
+ // be undone in addition to the first real undo outside the text edit changes
+ while (pSdrUndoManager->GetRedoActionCount()
+ > pSdrUndoManager->GetRedoActionCountBeforeTextEdit())
+ {
+ bNeedToUndoSavedRedoTextEdit = true;
+ pSdrUndoManager->Redo();
+ }
+ }
+
+ // reset the callback link and let the undo manager cleanup all text edit
+ // undo actions to get the stack back to the form before the text edit
+ pSdrUndoManager->SetEndTextEditHdl(Link<SdrUndoManager*, void>());
+ }
+ else
+ {
+ OSL_ENSURE(false, "Got UndoManager back in SdrEndTextEdit which is NOT the "
+ "expected document UndoManager (!)");
+ delete pOriginal;
+ }
+
+ // cid#1493241 - Wrapper object use after free
+ if (pUndoEditUndoManager == mpLocalTextEditUndoManager.get())
+ pUndoEditUndoManager = nullptr;
+ mpLocalTextEditUndoManager.reset();
+ }
+ }
+ else
+ {
+ assert(nullptr == mpOldTextEditUndoManager); // cannot be restored!
+ }
+
+ if (GetModel() && mxWeakTextEditObj.is())
+ {
+ SdrHint aHint(SdrHintKind::EndEdit, *mxWeakTextEditObj);
+ GetModel()->Broadcast(aHint);
+ }
+
+ // if new mechanism was used, clean it up. At cleanup no need to check
+ // for LibreOfficeKit
+ if (mpTextEditOutlinerView)
+ {
+ mpTextEditOutlinerView->GetEditView().setEditViewCallbacks(nullptr);
+ maTEOverlayGroup.clear();
+ }
+
+ mxWeakTextEditObj.reset(nullptr);
+ mpTextEditPV = nullptr;
+ mpTextEditWin = nullptr;
+ SdrOutliner* pTEOutliner = mpTextEditOutliner.release();
+ mpTextEditOutlinerView = nullptr;
+ pTextEditCursorBuffer = nullptr;
+ aTextEditArea = tools::Rectangle();
+
+ if (pTEOutliner != nullptr)
+ {
+ bool bModified = pTEOutliner->IsModified();
+ if (pTEOutlinerView != nullptr)
+ {
+ pTEOutlinerView->HideCursor();
+ }
+ if (pTEObj != nullptr)
+ {
+ pTEOutliner->CompleteOnlineSpelling();
+
+ std::unique_ptr<SdrUndoObjSetText> pTxtUndo;
+
+ if (bModified)
+ {
+ sal_Int32 nText;
+ for (nText = 0; nText < pTEObj->getTextCount(); ++nText)
+ if (pTEObj->getText(nText) == pTEObj->getActiveText())
+ break;
+
+ pTxtUndo.reset(
+ dynamic_cast<SdrUndoObjSetText*>(GetModel()
+ ->GetSdrUndoFactory()
+ .CreateUndoObjectSetText(*pTEObj, nText)
+ .release()));
+ }
+ DBG_ASSERT(!bModified || pTxtUndo,
+ "svx::SdrObjEditView::EndTextEdit(), could not create undo action!");
+ // Set old CalcFieldValue-Handler again, this
+ // has to happen before Obj::EndTextEdit(), as this does UpdateFields().
+ pTEOutliner->SetCalcFieldValueHdl(aOldCalcFieldValueLink);
+ pTEOutliner->SetBeginPasteOrDropHdl(Link<PasteOrDropInfos*, void>());
+ pTEOutliner->SetEndPasteOrDropHdl(Link<PasteOrDropInfos*, void>());
+
+ const bool bUndo = IsUndoEnabled();
+ if (bUndo)
+ {
+ OUString aObjName(pTEObj->TakeObjNameSingul());
+ BegUndo(SvxResId(STR_UndoObjSetText), aObjName);
+ }
+
+ pTEObj->EndTextEdit(*pTEOutliner);
+
+ if ((pTEObj->GetRotateAngle() != 0_deg100) || (pTEObj && pTEObj->IsFontwork()))
+ {
+ pTEObj->ActionChanged();
+ }
+
+ if (pTxtUndo != nullptr)
+ {
+ pTxtUndo->AfterSetText();
+ if (!pTxtUndo->IsDifferent())
+ {
+ pTxtUndo.reset();
+ }
+ }
+ // check deletion of entire TextObj
+ std::unique_ptr<SdrUndoAction> pDelUndo;
+ bool bDelObj = false;
+ if (mbTextEditNewObj)
+ {
+ bDelObj = pTEObj->IsTextFrame() && !pTEObj->HasText() && !pTEObj->IsEmptyPresObj()
+ && !pTEObj->HasFill() && !pTEObj->HasLine();
+
+ if (pTEObj->IsInserted() && bDelObj
+ && pTEObj->GetObjInventor() == SdrInventor::Default && !bDontDeleteReally)
+ {
+ SdrObjKind eIdent = pTEObj->GetObjIdentifier();
+ if (eIdent == SdrObjKind::Text)
+ {
+ pDelUndo = GetModel()->GetSdrUndoFactory().CreateUndoDeleteObject(*pTEObj);
+ }
+ }
+ }
+ if (pTxtUndo)
+ {
+ if (bUndo)
+ AddUndo(std::move(pTxtUndo));
+ eRet = SdrEndTextEditKind::Changed;
+ }
+ if (pDelUndo != nullptr)
+ {
+ if (bUndo)
+ {
+ AddUndo(std::move(pDelUndo));
+ }
+ eRet = SdrEndTextEditKind::Deleted;
+ DBG_ASSERT(pTEObj->getParentSdrObjListFromSdrObject() != nullptr,
+ "SdrObjEditView::SdrEndTextEdit(): Fatal: Object edited doesn't have an "
+ "ObjList!");
+ if (pTEObj->getParentSdrObjListFromSdrObject() != nullptr)
+ {
+ pTEObj->getParentSdrObjListFromSdrObject()->RemoveObject(pTEObj->GetOrdNum());
+ CheckMarked(); // remove selection immediately...
+ }
+ }
+ else if (bDelObj)
+ { // for Writer: the app has to do the deletion itself.
+ eRet = SdrEndTextEditKind::ShouldBeDeleted;
+ }
+
+ if (bUndo)
+ EndUndo(); // EndUndo after Remove, in case UndoStack is deleted immediately
+
+ // Switch on any TextAnimation again after TextEdit
+ if (pTEObj)
+ {
+ pTEObj->SetTextAnimationAllowed(true);
+ }
+
+ // Since IsMarkHdlWhenTextEdit() is ignored, it is necessary
+ // to call AdjustMarkHdl() always.
+ AdjustMarkHdl();
+ }
+ // delete all OutlinerViews
+ for (size_t i = pTEOutliner->GetViewCount(); i > 0;)
+ {
+ i--;
+ OutlinerView* pOLV = pTEOutliner->GetView(i);
+ sal_uInt16 nMorePix = pOLV->GetInvalidateMore() + 10;
+ vcl::Window* pWin = pOLV->GetWindow();
+ tools::Rectangle aRect(pOLV->GetOutputArea());
+ pTEOutliner->RemoveView(i);
+ if (!mbTextEditDontDelete || i != 0)
+ {
+ // may not own the zeroth one
+ delete pOLV;
+ }
+ aRect.Union(aTextEditArea);
+ aRect.Union(aMinTextEditArea);
+ aRect = pWin->LogicToPixel(aRect);
+ aRect.AdjustLeft(-nMorePix);
+ aRect.AdjustTop(-nMorePix);
+ aRect.AdjustRight(nMorePix);
+ aRect.AdjustBottom(nMorePix);
+ aRect = pWin->PixelToLogic(aRect);
+ InvalidateOneWin(*pWin->GetOutDev(), aRect);
+ pWin->GetOutDev()->SetFillColor();
+ pWin->GetOutDev()->SetLineColor(COL_BLACK);
+ }
+ // and now the Outliner itself
+ if (!mbTextEditDontDelete)
+ delete pTEOutliner;
+ else
+ pTEOutliner->Clear();
+ if (pTEWin != nullptr)
+ {
+ pTEWin->SetCursor(pTECursorBuffer);
+ }
+ maHdlList.SetMoveOutside(false);
+ if (eRet != SdrEndTextEditKind::Unchanged)
+ {
+ GetMarkedObjectListWriteAccess().SetNameDirty();
+ }
+ }
+
+ if (pTEObj && !pTEObj->getSdrModelFromSdrObject().isLocked() && pTEObj->GetBroadcaster())
+ {
+ SdrHint aHint(SdrHintKind::EndEdit, *pTEObj);
+ const_cast<SfxBroadcaster*>(pTEObj->GetBroadcaster())->Broadcast(aHint);
+ }
+
+ if (pUndoEditUndoManager)
+ {
+ if (bNeedToUndoSavedRedoTextEdit)
+ {
+ // undo the text edit action since it was created as part of an EndTextEdit
+ // callback from undo itself. This needs to be done after the call to
+ // FmFormView::SdrEndTextEdit since it gets created there
+ pUndoEditUndoManager->Undo();
+ }
+
+ // trigger the Undo which was not executed, but lead to this
+ // end text edit
+ pUndoEditUndoManager->Undo();
+ }
+
+ return eRet;
+}
+
+// info about TextEdit. Default is false.
+bool SdrObjEditView::IsTextEdit() const { return mxWeakTextEditObj.is(); }
+
+// info about TextEditPageView. Default is 0L.
+SdrPageView* SdrObjEditView::GetTextEditPageView() const { return mpTextEditPV; }
+
+OutlinerView* SdrObjEditView::ImpFindOutlinerView(vcl::Window const* pWin) const
+{
+ if (pWin == nullptr)
+ return nullptr;
+ if (mpTextEditOutliner == nullptr)
+ return nullptr;
+ OutlinerView* pNewView = nullptr;
+ size_t nWinCount = mpTextEditOutliner->GetViewCount();
+ for (size_t i = 0; i < nWinCount && pNewView == nullptr; i++)
+ {
+ OutlinerView* pView = mpTextEditOutliner->GetView(i);
+ if (pView->GetWindow() == pWin)
+ pNewView = pView;
+ }
+ return pNewView;
+}
+
+void SdrObjEditView::SetTextEditWin(vcl::Window* pWin)
+{
+ if (!(mxWeakTextEditObj.is() && pWin != nullptr && pWin != mpTextEditWin))
+ return;
+
+ OutlinerView* pNewView = ImpFindOutlinerView(pWin);
+ if (pNewView != nullptr && pNewView != mpTextEditOutlinerView)
+ {
+ if (mpTextEditOutlinerView != nullptr)
+ {
+ mpTextEditOutlinerView->HideCursor();
+ }
+ mpTextEditOutlinerView = pNewView;
+ mpTextEditWin = pWin;
+ pWin->GrabFocus(); // Make the cursor blink here as well
+ pNewView->ShowCursor();
+ ImpMakeTextCursorAreaVisible();
+ }
+}
+
+bool SdrObjEditView::IsTextEditHit(const Point& rHit) const
+{
+ bool bOk = false;
+ if (mxWeakTextEditObj.is())
+ {
+ tools::Rectangle aEditArea;
+ if (OutlinerView* pOLV = mpTextEditOutliner->GetView(0))
+ aEditArea.Union(pOLV->GetOutputArea());
+
+ if (aEditArea.Contains(rHit))
+ { // check if any characters were actually hit
+ const Point aPnt(rHit - aEditArea.TopLeft());
+ tools::Long nHitTol = 2000;
+ if (OutputDevice* pRef = mpTextEditOutliner->GetRefDevice())
+ nHitTol = OutputDevice::LogicToLogic(nHitTol, MapUnit::Map100thMM,
+ pRef->GetMapMode().GetMapUnit());
+
+ bOk = mpTextEditOutliner->IsTextPos(aPnt, static_cast<sal_uInt16>(nHitTol));
+ }
+ }
+ return bOk;
+}
+
+bool SdrObjEditView::IsTextEditFrameHit(const Point& rHit) const
+{
+ bool bOk = false;
+ if (mxWeakTextEditObj.is())
+ {
+ SdrTextObj* pText = mxWeakTextEditObj.get();
+ OutlinerView* pOLV = mpTextEditOutliner->GetView(0);
+ if (pOLV)
+ {
+ vcl::Window* pWin = pOLV->GetWindow();
+ if (pText != nullptr && pText->IsTextFrame() && pWin != nullptr)
+ {
+ sal_uInt16 nPixSiz = pOLV->GetInvalidateMore();
+ tools::Rectangle aEditArea(aMinTextEditArea);
+ aEditArea.Union(pOLV->GetOutputArea());
+ if (!aEditArea.Contains(rHit))
+ {
+ Size aSiz(pWin->PixelToLogic(Size(nPixSiz, nPixSiz)));
+ aEditArea.AdjustLeft(-(aSiz.Width()));
+ aEditArea.AdjustTop(-(aSiz.Height()));
+ aEditArea.AdjustRight(aSiz.Width());
+ aEditArea.AdjustBottom(aSiz.Height());
+ bOk = aEditArea.Contains(rHit);
+ }
+ }
+ }
+ }
+ return bOk;
+}
+
+std::unique_ptr<TextChainCursorManager>
+SdrObjEditView::ImpHandleMotionThroughBoxesKeyInput(const KeyEvent& rKEvt, bool* bOutHandled)
+{
+ *bOutHandled = false;
+
+ SdrTextObj* pTextObj = mxWeakTextEditObj.get();
+ if (!pTextObj)
+ return nullptr;
+
+ if (!pTextObj->GetNextLinkInChain() && !pTextObj->GetPrevLinkInChain())
+ return nullptr;
+
+ std::unique_ptr<TextChainCursorManager> pCursorManager(
+ new TextChainCursorManager(this, pTextObj));
+ if (pCursorManager->HandleKeyEvent(rKEvt))
+ {
+ // Possibly do other stuff here if necessary...
+ // XXX: Careful with the checks below (in KeyInput) for pWin and co. You should do them here I guess.
+ *bOutHandled = true;
+ }
+
+ return pCursorManager;
+}
+
+bool SdrObjEditView::KeyInput(const KeyEvent& rKEvt, vcl::Window* pWin)
+{
+ if (mpTextEditOutlinerView)
+ {
+ /* Start special handling of keys within a chain */
+ // We possibly move to another box before any handling
+ bool bHandled = false;
+ std::unique_ptr<TextChainCursorManager> xCursorManager(
+ ImpHandleMotionThroughBoxesKeyInput(rKEvt, &bHandled));
+ if (bHandled)
+ return true;
+ /* End special handling of keys within a chain */
+
+ if (mpTextEditOutlinerView->PostKeyEvent(rKEvt, pWin))
+ {
+ if (mpModel)
+ {
+ if (mpTextEditOutliner && mpTextEditOutliner->IsModified())
+ mpModel->SetChanged();
+ }
+
+ /* Start chaining processing */
+ ImpChainingEventHdl();
+ ImpMoveCursorAfterChainingEvent(xCursorManager.get());
+ /* End chaining processing */
+
+ if (pWin != nullptr && pWin != mpTextEditWin)
+ SetTextEditWin(pWin);
+ ImpMakeTextCursorAreaVisible();
+ return true;
+ }
+ }
+ return SdrGlueEditView::KeyInput(rKEvt, pWin);
+}
+
+bool SdrObjEditView::MouseButtonDown(const MouseEvent& rMEvt, OutputDevice* pWin)
+{
+ if (mpTextEditOutlinerView != nullptr)
+ {
+ bool bPostIt = mpTextEditOutliner->IsInSelectionMode();
+ if (!bPostIt)
+ {
+ Point aPt(rMEvt.GetPosPixel());
+ if (pWin != nullptr)
+ aPt = pWin->PixelToLogic(aPt);
+ else if (mpTextEditWin != nullptr)
+ aPt = mpTextEditWin->PixelToLogic(aPt);
+ bPostIt = IsTextEditHit(aPt);
+ }
+ if (bPostIt)
+ {
+ Point aPixPos(rMEvt.GetPosPixel());
+ if (pWin)
+ {
+ tools::Rectangle aR(pWin->LogicToPixel(mpTextEditOutlinerView->GetOutputArea()));
+ if (aPixPos.X() < aR.Left())
+ aPixPos.setX(aR.Left());
+ if (aPixPos.X() > aR.Right())
+ aPixPos.setX(aR.Right());
+ if (aPixPos.Y() < aR.Top())
+ aPixPos.setY(aR.Top());
+ if (aPixPos.Y() > aR.Bottom())
+ aPixPos.setY(aR.Bottom());
+ }
+ MouseEvent aMEvt(aPixPos, rMEvt.GetClicks(), rMEvt.GetMode(), rMEvt.GetButtons(),
+ rMEvt.GetModifier());
+ if (mpTextEditOutlinerView->MouseButtonDown(aMEvt))
+ {
+ if (pWin != nullptr && pWin != mpTextEditWin->GetOutDev()
+ && pWin->GetOutDevType() == OUTDEV_WINDOW)
+ SetTextEditWin(pWin->GetOwnerWindow());
+ ImpMakeTextCursorAreaVisible();
+ return true;
+ }
+ }
+ }
+ return SdrGlueEditView::MouseButtonDown(rMEvt, pWin);
+}
+
+bool SdrObjEditView::MouseButtonUp(const MouseEvent& rMEvt, OutputDevice* pWin)
+{
+ if (mpTextEditOutlinerView != nullptr)
+ {
+ bool bPostIt = mpTextEditOutliner->IsInSelectionMode();
+ if (!bPostIt)
+ {
+ Point aPt(rMEvt.GetPosPixel());
+ if (pWin != nullptr)
+ aPt = pWin->PixelToLogic(aPt);
+ else if (mpTextEditWin != nullptr)
+ aPt = mpTextEditWin->PixelToLogic(aPt);
+ bPostIt = IsTextEditHit(aPt);
+ }
+ if (bPostIt && pWin)
+ {
+ Point aPixPos(rMEvt.GetPosPixel());
+ tools::Rectangle aR(pWin->LogicToPixel(mpTextEditOutlinerView->GetOutputArea()));
+ if (aPixPos.X() < aR.Left())
+ aPixPos.setX(aR.Left());
+ if (aPixPos.X() > aR.Right())
+ aPixPos.setX(aR.Right());
+ if (aPixPos.Y() < aR.Top())
+ aPixPos.setY(aR.Top());
+ if (aPixPos.Y() > aR.Bottom())
+ aPixPos.setY(aR.Bottom());
+ MouseEvent aMEvt(aPixPos, rMEvt.GetClicks(), rMEvt.GetMode(), rMEvt.GetButtons(),
+ rMEvt.GetModifier());
+ if (mpTextEditOutlinerView->MouseButtonUp(aMEvt))
+ {
+ ImpMakeTextCursorAreaVisible();
+ return true;
+ }
+ }
+ }
+ return SdrGlueEditView::MouseButtonUp(rMEvt, pWin);
+}
+
+bool SdrObjEditView::MouseMove(const MouseEvent& rMEvt, OutputDevice* pWin)
+{
+ if (mpTextEditOutlinerView != nullptr)
+ {
+ bool bSelMode = mpTextEditOutliner->IsInSelectionMode();
+ bool bPostIt = bSelMode;
+ if (!bPostIt)
+ {
+ Point aPt(rMEvt.GetPosPixel());
+ if (pWin)
+ aPt = pWin->PixelToLogic(aPt);
+ else if (mpTextEditWin)
+ aPt = mpTextEditWin->PixelToLogic(aPt);
+ bPostIt = IsTextEditHit(aPt);
+ }
+ if (bPostIt)
+ {
+ Point aPixPos(rMEvt.GetPosPixel());
+ tools::Rectangle aR(mpTextEditOutlinerView->GetOutputArea());
+ if (pWin)
+ aR = pWin->LogicToPixel(aR);
+ else if (mpTextEditWin)
+ aR = mpTextEditWin->LogicToPixel(aR);
+ if (aPixPos.X() < aR.Left())
+ aPixPos.setX(aR.Left());
+ if (aPixPos.X() > aR.Right())
+ aPixPos.setX(aR.Right());
+ if (aPixPos.Y() < aR.Top())
+ aPixPos.setY(aR.Top());
+ if (aPixPos.Y() > aR.Bottom())
+ aPixPos.setY(aR.Bottom());
+ MouseEvent aMEvt(aPixPos, rMEvt.GetClicks(), rMEvt.GetMode(), rMEvt.GetButtons(),
+ rMEvt.GetModifier());
+ if (mpTextEditOutlinerView->MouseMove(aMEvt) && bSelMode)
+ {
+ ImpMakeTextCursorAreaVisible();
+ return true;
+ }
+ }
+ }
+ return SdrGlueEditView::MouseMove(rMEvt, pWin);
+}
+
+bool SdrObjEditView::Command(const CommandEvent& rCEvt, vcl::Window* pWin)
+{
+ // as long as OutlinerView returns a sal_Bool, it only gets CommandEventId::StartDrag
+ if (mpTextEditOutlinerView != nullptr)
+ {
+ if (rCEvt.GetCommand() == CommandEventId::StartDrag)
+ {
+ bool bPostIt = mpTextEditOutliner->IsInSelectionMode() || !rCEvt.IsMouseEvent();
+ if (!bPostIt && rCEvt.IsMouseEvent())
+ {
+ Point aPt(rCEvt.GetMousePosPixel());
+ if (pWin != nullptr)
+ aPt = pWin->PixelToLogic(aPt);
+ else if (mpTextEditWin != nullptr)
+ aPt = mpTextEditWin->PixelToLogic(aPt);
+ bPostIt = IsTextEditHit(aPt);
+ }
+ if (bPostIt)
+ {
+ Point aPixPos(rCEvt.GetMousePosPixel());
+ if (rCEvt.IsMouseEvent() && pWin)
+ {
+ tools::Rectangle aR(
+ pWin->LogicToPixel(mpTextEditOutlinerView->GetOutputArea()));
+ if (aPixPos.X() < aR.Left())
+ aPixPos.setX(aR.Left());
+ if (aPixPos.X() > aR.Right())
+ aPixPos.setX(aR.Right());
+ if (aPixPos.Y() < aR.Top())
+ aPixPos.setY(aR.Top());
+ if (aPixPos.Y() > aR.Bottom())
+ aPixPos.setY(aR.Bottom());
+ }
+ CommandEvent aCEvt(aPixPos, rCEvt.GetCommand(), rCEvt.IsMouseEvent());
+ // Command is void at the OutlinerView, sadly
+ mpTextEditOutlinerView->Command(aCEvt);
+ if (pWin != nullptr && pWin != mpTextEditWin)
+ SetTextEditWin(pWin);
+ ImpMakeTextCursorAreaVisible();
+ return true;
+ }
+ }
+ else
+ {
+ mpTextEditOutlinerView->Command(rCEvt);
+ if (mpModel && comphelper::LibreOfficeKit::isActive())
+ {
+ // It could execute CommandEventId::ExtTextInput, while SdrObjEditView::KeyInput
+ // isn't called
+ if (mpTextEditOutliner && mpTextEditOutliner->IsModified())
+ mpModel->SetChanged();
+ }
+ return true;
+ }
+ }
+ return SdrGlueEditView::Command(rCEvt, pWin);
+}
+
+bool SdrObjEditView::ImpIsTextEditAllSelected() const
+{
+ bool bRet = false;
+ if (mpTextEditOutliner != nullptr && mpTextEditOutlinerView != nullptr)
+ {
+ if (SdrTextObj::HasTextImpl(mpTextEditOutliner.get()))
+ {
+ const sal_Int32 nParaCnt = mpTextEditOutliner->GetParagraphCount();
+ Paragraph* pLastPara
+ = mpTextEditOutliner->GetParagraph(nParaCnt > 1 ? nParaCnt - 1 : 0);
+
+ ESelection aESel(mpTextEditOutlinerView->GetSelection());
+ if (aESel.nStartPara == 0 && aESel.nStartPos == 0 && aESel.nEndPara == (nParaCnt - 1))
+ {
+ if (mpTextEditOutliner->GetText(pLastPara).getLength() == aESel.nEndPos)
+ bRet = true;
+ }
+ // in case the selection was done backwards
+ if (!bRet && aESel.nEndPara == 0 && aESel.nEndPos == 0
+ && aESel.nStartPara == (nParaCnt - 1))
+ {
+ if (mpTextEditOutliner->GetText(pLastPara).getLength() == aESel.nStartPos)
+ bRet = true;
+ }
+ }
+ else
+ {
+ bRet = true;
+ }
+ }
+ return bRet;
+}
+
+void SdrObjEditView::ImpMakeTextCursorAreaVisible()
+{
+ if (mpTextEditOutlinerView != nullptr && mpTextEditWin != nullptr)
+ {
+ vcl::Cursor* pCsr = mpTextEditWin->GetCursor();
+ if (pCsr != nullptr)
+ {
+ Size aSiz(pCsr->GetSize());
+ if (!aSiz.IsEmpty())
+ {
+ MakeVisible(tools::Rectangle(pCsr->GetPos(), aSiz), *mpTextEditWin);
+ }
+ }
+ }
+}
+
+SvtScriptType SdrObjEditView::GetScriptType() const
+{
+ SvtScriptType nScriptType = SvtScriptType::NONE;
+
+ if (IsTextEdit())
+ {
+ if (mxWeakTextEditObj->GetOutlinerParaObject())
+ nScriptType
+ = mxWeakTextEditObj->GetOutlinerParaObject()->GetTextObject().GetScriptType();
+
+ if (mpTextEditOutlinerView)
+ nScriptType = mpTextEditOutlinerView->GetSelectedScriptType();
+ }
+ else
+ {
+ const size_t nMarkCount(GetMarkedObjectCount());
+
+ for (size_t i = 0; i < nMarkCount; ++i)
+ {
+ OutlinerParaObject* pParaObj = GetMarkedObjectByIndex(i)->GetOutlinerParaObject();
+
+ if (pParaObj)
+ {
+ nScriptType |= pParaObj->GetTextObject().GetScriptType();
+ }
+ }
+ }
+
+ if (nScriptType == SvtScriptType::NONE)
+ nScriptType = SvtScriptType::LATIN;
+
+ return nScriptType;
+}
+
+void SdrObjEditView::GetAttributes(SfxItemSet& rTargetSet, bool bOnlyHardAttr) const
+{
+ if (mxSelectionController.is())
+ if (mxSelectionController->GetAttributes(rTargetSet, bOnlyHardAttr))
+ return;
+
+ if (IsTextEdit())
+ {
+ DBG_ASSERT(mpTextEditOutlinerView != nullptr,
+ "SdrObjEditView::GetAttributes(): mpTextEditOutlinerView=NULL");
+ DBG_ASSERT(mpTextEditOutliner != nullptr,
+ "SdrObjEditView::GetAttributes(): mpTextEditOutliner=NULL");
+
+ // take care of bOnlyHardAttr(!)
+ if (!bOnlyHardAttr && mxWeakTextEditObj->GetStyleSheet())
+ rTargetSet.Put(mxWeakTextEditObj->GetStyleSheet()->GetItemSet());
+
+ // add object attributes
+ rTargetSet.Put(mxWeakTextEditObj->GetMergedItemSet());
+
+ if (mpTextEditOutlinerView)
+ {
+ // FALSE= regard InvalidItems as "holes," not as Default
+ rTargetSet.Put(mpTextEditOutlinerView->GetAttribs(), false);
+ }
+
+ if (GetMarkedObjectCount() == 1 && GetMarkedObjectByIndex(0) == mxWeakTextEditObj.get())
+ {
+ MergeNotPersistAttrFromMarked(rTargetSet);
+ }
+ }
+ else
+ {
+ SdrGlueEditView::GetAttributes(rTargetSet, bOnlyHardAttr);
+ }
+}
+
+bool SdrObjEditView::SetAttributes(const SfxItemSet& rSet, bool bReplaceAll)
+{
+ bool bRet = false;
+ bool bTextEdit = mpTextEditOutlinerView != nullptr && mxWeakTextEditObj.is();
+ bool bAllTextSelected = ImpIsTextEditAllSelected();
+ const SfxItemSet* pSet = &rSet;
+
+ if (!bTextEdit)
+ {
+ // no TextEdit active -> all Items to drawing object
+ if (mxSelectionController.is())
+ bRet = mxSelectionController->SetAttributes(*pSet, bReplaceAll);
+
+ if (!bRet)
+ {
+ SdrGlueEditView::SetAttributes(*pSet, bReplaceAll);
+ bRet = true;
+ }
+ }
+ else
+ {
+#ifdef DBG_UTIL
+ {
+ bool bHasEEFeatureItems = false;
+ SfxItemIter aIter(rSet);
+ for (const SfxPoolItem* pItem = aIter.GetCurItem(); !bHasEEFeatureItems && pItem;
+ pItem = aIter.NextItem())
+ {
+ if (!IsInvalidItem(pItem))
+ {
+ sal_uInt16 nW = pItem->Which();
+ if (nW >= EE_FEATURE_START && nW <= EE_FEATURE_END)
+ bHasEEFeatureItems = true;
+ }
+ }
+
+ if (bHasEEFeatureItems)
+ {
+ std::unique_ptr<weld::MessageDialog> xInfoBox(Application::CreateMessageDialog(
+ nullptr, VclMessageType::Info, VclButtonsType::Ok,
+ "SdrObjEditView::SetAttributes(): Setting EE_FEATURE items "
+ "at the SdrView does not make sense! It only leads to "
+ "overhead and unreadable documents."));
+ xInfoBox->run();
+ }
+ }
+#endif
+
+ bool bOnlyEEItems;
+ bool bNoEEItems = !SearchOutlinerItems(*pSet, bReplaceAll, &bOnlyEEItems);
+ // everything selected? -> attributes to the border, too
+ // if no EEItems, attributes to the border only
+ if (bAllTextSelected || bNoEEItems)
+ {
+ if (mxSelectionController.is())
+ bRet = mxSelectionController->SetAttributes(*pSet, bReplaceAll);
+
+ if (!bRet)
+ {
+ const bool bUndo = IsUndoEnabled();
+
+ if (bUndo)
+ {
+ BegUndo(ImpGetDescriptionString(STR_EditSetAttributes));
+ AddUndo(
+ GetModel()->GetSdrUndoFactory().CreateUndoGeoObject(*mxWeakTextEditObj));
+
+ // If this is a text object also rescue the OutlinerParaObject since
+ // applying attributes to the object may change text layout when
+ // multiple portions exist with multiple formats. If an OutlinerParaObject
+ // really exists and needs to be rescued is evaluated in the undo
+ // implementation itself.
+ bool bRescueText = mxWeakTextEditObj;
+
+ AddUndo(GetModel()->GetSdrUndoFactory().CreateUndoAttrObject(
+ *mxWeakTextEditObj, false, !bNoEEItems || bRescueText));
+ EndUndo();
+ }
+
+ mxWeakTextEditObj->SetMergedItemSetAndBroadcast(*pSet, bReplaceAll);
+
+ FlushComeBackTimer(); // to set ModeHasChanged immediately
+ }
+ }
+ else if (!bOnlyEEItems)
+ {
+ // Otherwise split Set, if necessary.
+ // Now we build an ItemSet aSet that doesn't contain EE_Items from
+ // *pSet (otherwise it would be a copy).
+ WhichRangesContainer pNewWhichTable
+ = RemoveWhichRange(pSet->GetRanges(), EE_ITEMS_START, EE_ITEMS_END);
+ SfxItemSet aSet(mpModel->GetItemPool(), std::move(pNewWhichTable));
+ SfxWhichIter aIter(aSet);
+ sal_uInt16 nWhich = aIter.FirstWhich();
+ while (nWhich != 0)
+ {
+ const SfxPoolItem* pItem;
+ SfxItemState eState = pSet->GetItemState(nWhich, false, &pItem);
+ if (eState == SfxItemState::SET)
+ aSet.Put(*pItem);
+ nWhich = aIter.NextWhich();
+ }
+
+ if (mxSelectionController.is())
+ bRet = mxSelectionController->SetAttributes(aSet, bReplaceAll);
+
+ if (!bRet)
+ {
+ if (IsUndoEnabled())
+ {
+ BegUndo(ImpGetDescriptionString(STR_EditSetAttributes));
+ AddUndo(
+ GetModel()->GetSdrUndoFactory().CreateUndoGeoObject(*mxWeakTextEditObj));
+ AddUndo(
+ GetModel()->GetSdrUndoFactory().CreateUndoAttrObject(*mxWeakTextEditObj));
+ EndUndo();
+ }
+
+ mxWeakTextEditObj->SetMergedItemSetAndBroadcast(aSet, bReplaceAll);
+
+ if (GetMarkedObjectCount() == 1
+ && GetMarkedObjectByIndex(0) == mxWeakTextEditObj.get())
+ {
+ SetNotPersistAttrToMarked(aSet);
+ }
+ }
+ FlushComeBackTimer();
+ }
+ if (!bNoEEItems)
+ {
+ // and now the attributes to the EditEngine
+ if (bReplaceAll)
+ {
+ mpTextEditOutlinerView->RemoveAttribs(true);
+ }
+ mpTextEditOutlinerView->SetAttribs(rSet);
+
+ Outliner* pTEOutliner = mpTextEditOutlinerView->GetOutliner();
+ if (mpModel && pTEOutliner && pTEOutliner->IsModified())
+ mpModel->SetChanged();
+
+ ImpMakeTextCursorAreaVisible();
+ }
+ bRet = true;
+ }
+ return bRet;
+}
+
+SfxStyleSheet* SdrObjEditView::GetStyleSheet() const
+{
+ SfxStyleSheet* pSheet = nullptr;
+
+ if (mxSelectionController.is())
+ {
+ if (mxSelectionController->GetStyleSheet(pSheet))
+ return pSheet;
+ }
+
+ if (mpTextEditOutlinerView)
+ {
+ pSheet = mpTextEditOutlinerView->GetStyleSheet();
+ }
+ else
+ {
+ pSheet = SdrGlueEditView::GetStyleSheet();
+ }
+ return pSheet;
+}
+
+void SdrObjEditView::SetStyleSheet(SfxStyleSheet* pStyleSheet, bool bDontRemoveHardAttr)
+{
+ if (mxSelectionController.is())
+ {
+ if (mxSelectionController->SetStyleSheet(pStyleSheet, bDontRemoveHardAttr))
+ return;
+ }
+
+ // if we are currently in edit mode we must also set the stylesheet
+ // on all paragraphs in the Outliner for the edit view
+ if (nullptr != mpTextEditOutlinerView)
+ {
+ Outliner* pOutliner = mpTextEditOutlinerView->GetOutliner();
+
+ const sal_Int32 nParaCount = pOutliner->GetParagraphCount();
+ for (sal_Int32 nPara = 0; nPara < nParaCount; nPara++)
+ {
+ pOutliner->SetStyleSheet(nPara, pStyleSheet);
+ }
+ }
+
+ SdrGlueEditView::SetStyleSheet(pStyleSheet, bDontRemoveHardAttr);
+}
+
+void SdrObjEditView::AddWindowToPaintView(OutputDevice* pNewWin, vcl::Window* pWindow)
+{
+ SdrGlueEditView::AddWindowToPaintView(pNewWin, pWindow);
+
+ if (mxWeakTextEditObj.is() && !mbTextEditOnlyOneView
+ && pNewWin->GetOutDevType() == OUTDEV_WINDOW)
+ {
+ OutlinerView* pOutlView = ImpMakeOutlinerView(pNewWin->GetOwnerWindow(), nullptr);
+ mpTextEditOutliner->InsertView(pOutlView);
+ }
+}
+
+void SdrObjEditView::DeleteWindowFromPaintView(OutputDevice* pOldWin)
+{
+ SdrGlueEditView::DeleteWindowFromPaintView(pOldWin);
+
+ if (mxWeakTextEditObj.is() && !mbTextEditOnlyOneView
+ && pOldWin->GetOutDevType() == OUTDEV_WINDOW)
+ {
+ for (size_t i = mpTextEditOutliner->GetViewCount(); i > 0;)
+ {
+ i--;
+ OutlinerView* pOLV = mpTextEditOutliner->GetView(i);
+ if (pOLV && pOLV->GetWindow() == pOldWin->GetOwnerWindow())
+ {
+ mpTextEditOutliner->RemoveView(i);
+ }
+ }
+ }
+
+ lcl_RemoveTextEditOutlinerViews(this, GetSdrPageView(), pOldWin);
+}
+
+bool SdrObjEditView::IsTextEditInSelectionMode() const
+{
+ return mpTextEditOutliner != nullptr && mpTextEditOutliner->IsInSelectionMode();
+}
+
+// MacroMode
+
+void SdrObjEditView::BegMacroObj(const Point& rPnt, short nTol, SdrObject* pObj, SdrPageView* pPV,
+ vcl::Window* pWin)
+{
+ BrkMacroObj();
+ if (pObj != nullptr && pPV != nullptr && pWin != nullptr && pObj->HasMacro())
+ {
+ nTol = ImpGetHitTolLogic(nTol, nullptr);
+ pMacroObj = pObj;
+ pMacroPV = pPV;
+ pMacroWin = pWin;
+ mbMacroDown = false;
+ nMacroTol = sal_uInt16(nTol);
+ aMacroDownPos = rPnt;
+ MovMacroObj(rPnt);
+ }
+}
+
+void SdrObjEditView::ImpMacroUp(const Point& rUpPos)
+{
+ if (pMacroObj != nullptr && mbMacroDown)
+ {
+ SdrObjMacroHitRec aHitRec;
+ aHitRec.aPos = rUpPos;
+ aHitRec.nTol = nMacroTol;
+ aHitRec.pVisiLayer = &pMacroPV->GetVisibleLayers();
+ aHitRec.pPageView = pMacroPV;
+ pMacroObj->PaintMacro(*pMacroWin->GetOutDev(), tools::Rectangle(), aHitRec);
+ mbMacroDown = false;
+ }
+}
+
+void SdrObjEditView::ImpMacroDown(const Point& rDownPos)
+{
+ if (pMacroObj != nullptr && !mbMacroDown)
+ {
+ SdrObjMacroHitRec aHitRec;
+ aHitRec.aPos = rDownPos;
+ aHitRec.nTol = nMacroTol;
+ aHitRec.pVisiLayer = &pMacroPV->GetVisibleLayers();
+ aHitRec.pPageView = pMacroPV;
+ pMacroObj->PaintMacro(*pMacroWin->GetOutDev(), tools::Rectangle(), aHitRec);
+ mbMacroDown = true;
+ }
+}
+
+void SdrObjEditView::MovMacroObj(const Point& rPnt)
+{
+ if (pMacroObj == nullptr)
+ return;
+
+ SdrObjMacroHitRec aHitRec;
+ aHitRec.aPos = rPnt;
+ aHitRec.nTol = nMacroTol;
+ aHitRec.pVisiLayer = &pMacroPV->GetVisibleLayers();
+ aHitRec.pPageView = pMacroPV;
+ bool bDown = pMacroObj->IsMacroHit(aHitRec);
+ if (bDown)
+ ImpMacroDown(rPnt);
+ else
+ ImpMacroUp(rPnt);
+}
+
+void SdrObjEditView::BrkMacroObj()
+{
+ if (pMacroObj != nullptr)
+ {
+ ImpMacroUp(aMacroDownPos);
+ pMacroObj = nullptr;
+ pMacroPV = nullptr;
+ pMacroWin = nullptr;
+ }
+}
+
+bool SdrObjEditView::EndMacroObj()
+{
+ if (pMacroObj != nullptr && mbMacroDown)
+ {
+ ImpMacroUp(aMacroDownPos);
+ SdrObjMacroHitRec aHitRec;
+ aHitRec.aPos = aMacroDownPos;
+ aHitRec.nTol = nMacroTol;
+ aHitRec.pVisiLayer = &pMacroPV->GetVisibleLayers();
+ aHitRec.pPageView = pMacroPV;
+ bool bRet = pMacroObj->DoMacro(aHitRec);
+ pMacroObj = nullptr;
+ pMacroPV = nullptr;
+ pMacroWin = nullptr;
+ return bRet;
+ }
+ else
+ {
+ BrkMacroObj();
+ return false;
+ }
+}
+
+/** fills the given any with a XTextCursor for the current text selection.
+ Leaves the any untouched if there currently is no text selected */
+void SdrObjEditView::getTextSelection(css::uno::Any& rSelection)
+{
+ if (!IsTextEdit())
+ return;
+
+ OutlinerView* pOutlinerView = GetTextEditOutlinerView();
+ if (!(pOutlinerView && pOutlinerView->HasSelection()))
+ return;
+
+ SdrObject* pObj = GetTextEditObject();
+
+ if (!pObj)
+ return;
+
+ css::uno::Reference<css::text::XText> xText(pObj->getUnoShape(), css::uno::UNO_QUERY);
+ if (xText.is())
+ {
+ SvxUnoTextBase* pRange = comphelper::getFromUnoTunnel<SvxUnoTextBase>(xText);
+ if (pRange)
+ {
+ rSelection <<= pRange->createTextCursorBySelection(pOutlinerView->GetSelection());
+ }
+ }
+}
+
+/* check if we have a single selection and that single object likes
+ to handle the mouse and keyboard events itself
+
+ TODO: the selection controller should be queried from the
+ object specific view contact. Currently this method only
+ works for tables.
+*/
+void SdrObjEditView::MarkListHasChanged()
+{
+ SdrGlueEditView::MarkListHasChanged();
+
+ if (mxSelectionController.is())
+ {
+ mxLastSelectionController = mxSelectionController;
+ mxSelectionController->onSelectionHasChanged();
+ }
+
+ mxSelectionController.clear();
+
+ const SdrMarkList& rMarkList = GetMarkedObjectList();
+ if (rMarkList.GetMarkCount() != 1)
+ return;
+
+ const SdrObject* pObj(rMarkList.GetMark(0)->GetMarkedSdrObj());
+ SdrView* pView(dynamic_cast<SdrView*>(this));
+
+ // check for table
+ if (pObj && pView && (pObj->GetObjInventor() == SdrInventor::Default)
+ && (pObj->GetObjIdentifier() == SdrObjKind::Table))
+ {
+ mxSelectionController = sdr::table::CreateTableController(
+ *pView, static_cast<const sdr::table::SdrTableObj&>(*pObj), mxLastSelectionController);
+
+ if (mxSelectionController.is())
+ {
+ mxLastSelectionController.clear();
+ mxSelectionController->onSelectionHasChanged();
+ }
+ }
+}
+
+IMPL_LINK(SdrObjEditView, EndPasteOrDropHdl, PasteOrDropInfos*, pInfo, void)
+{
+ OnEndPasteOrDrop(pInfo);
+}
+
+IMPL_LINK(SdrObjEditView, BeginPasteOrDropHdl, PasteOrDropInfos*, pInfo, void)
+{
+ OnBeginPasteOrDrop(pInfo);
+}
+
+void SdrObjEditView::OnBeginPasteOrDrop(PasteOrDropInfos*)
+{
+ // applications can derive from these virtual methods to do something before a drop or paste operation
+}
+
+void SdrObjEditView::OnEndPasteOrDrop(PasteOrDropInfos*)
+{
+ // applications can derive from these virtual methods to do something before a drop or paste operation
+}
+
+sal_uInt16 SdrObjEditView::GetSelectionLevel() const
+{
+ sal_uInt16 nLevel = 0xFFFF;
+ if (IsTextEdit())
+ {
+ DBG_ASSERT(mpTextEditOutlinerView != nullptr,
+ "SdrObjEditView::GetAttributes(): mpTextEditOutlinerView=NULL");
+ DBG_ASSERT(mpTextEditOutliner != nullptr,
+ "SdrObjEditView::GetAttributes(): mpTextEditOutliner=NULL");
+ if (mpTextEditOutlinerView)
+ {
+ //start and end position
+ ESelection aSelect = mpTextEditOutlinerView->GetSelection();
+ sal_uInt16 nStartPara = ::std::min(aSelect.nStartPara, aSelect.nEndPara);
+ sal_uInt16 nEndPara = ::std::max(aSelect.nStartPara, aSelect.nEndPara);
+ //get level from each paragraph
+ nLevel = 0;
+ for (sal_uInt16 nPara = nStartPara; nPara <= nEndPara; nPara++)
+ {
+ sal_uInt16 nParaDepth
+ = 1 << static_cast<sal_uInt16>(mpTextEditOutliner->GetDepth(nPara));
+ if (!(nLevel & nParaDepth))
+ nLevel += nParaDepth;
+ }
+ //reduce one level for Outliner Object
+ //if( nLevel > 0 && GetTextEditObject()->GetObjIdentifier() == OBJ_OUTLINETEXT )
+ // nLevel = nLevel >> 1;
+ //no bullet paragraph selected
+ if (nLevel == 0)
+ nLevel = 0xFFFF;
+ }
+ }
+ return nLevel;
+}
+
+bool SdrObjEditView::SupportsFormatPaintbrush(SdrInventor nObjectInventor,
+ SdrObjKind nObjectIdentifier)
+{
+ if (nObjectInventor != SdrInventor::Default && nObjectInventor != SdrInventor::E3d)
+ return false;
+ switch (nObjectIdentifier)
+ {
+ case SdrObjKind::NONE:
+ case SdrObjKind::Group:
+ return false;
+ case SdrObjKind::Line:
+ case SdrObjKind::Rectangle:
+ case SdrObjKind::CircleOrEllipse:
+ case SdrObjKind::CircleSection:
+ case SdrObjKind::CircleArc:
+ case SdrObjKind::CircleCut:
+ case SdrObjKind::Polygon:
+ case SdrObjKind::PolyLine:
+ case SdrObjKind::PathLine:
+ case SdrObjKind::PathFill:
+ case SdrObjKind::FreehandLine:
+ case SdrObjKind::FreehandFill:
+ case SdrObjKind::SplineLine:
+ case SdrObjKind::SplineFill:
+ case SdrObjKind::Text:
+ case SdrObjKind::TitleText:
+ case SdrObjKind::OutlineText:
+ case SdrObjKind::Graphic:
+ case SdrObjKind::OLE2:
+ case SdrObjKind::Table:
+ return true;
+ case SdrObjKind::Caption:
+ return false;
+ case SdrObjKind::Edge:
+ case SdrObjKind::PathPoly:
+ case SdrObjKind::PathPolyLine:
+ return true;
+ case SdrObjKind::Page:
+ case SdrObjKind::Measure:
+ case SdrObjKind::OLEPluginFrame:
+ case SdrObjKind::UNO:
+ return false;
+ case SdrObjKind::CustomShape:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static const WhichRangesContainer& GetFormatRangeImpl(bool bTextOnly)
+{
+ static const WhichRangesContainer gFull(
+ svl::Items<XATTR_LINE_FIRST, XATTR_LINE_LAST, XATTR_FILL_FIRST, XATTRSET_FILL,
+ SDRATTR_SHADOW_FIRST, SDRATTR_SHADOW_LAST, SDRATTR_MISC_FIRST,
+ SDRATTR_MISC_LAST, // table cell formats
+ SDRATTR_GRAF_FIRST, SDRATTR_GRAF_LAST, SDRATTR_TABLE_FIRST, SDRATTR_TABLE_LAST,
+ SDRATTR_GLOW_FIRST, SDRATTR_GLOW_LAST, SDRATTR_SOFTEDGE_FIRST,
+ SDRATTR_SOFTEDGE_LAST, EE_PARA_START, EE_PARA_END, EE_CHAR_START, EE_CHAR_END>);
+
+ static const WhichRangesContainer gTextOnly(
+ svl::Items<SDRATTR_MISC_FIRST, SDRATTR_MISC_LAST, EE_PARA_START, EE_PARA_END, EE_CHAR_START,
+ EE_CHAR_END>);
+
+ return bTextOnly ? gTextOnly : gFull;
+}
+
+void SdrObjEditView::TakeFormatPaintBrush(std::shared_ptr<SfxItemSet>& rFormatSet)
+{
+ const SdrMarkList& rMarkList = GetMarkedObjectList();
+ if (rMarkList.GetMarkCount() <= 0)
+ return;
+
+ OutlinerView* pOLV = GetTextEditOutlinerView();
+
+ rFormatSet = std::make_shared<SfxItemSet>(GetModel()->GetItemPool(),
+ GetFormatRangeImpl(pOLV != nullptr));
+ if (pOLV)
+ {
+ rFormatSet->Put(pOLV->GetAttribs());
+ }
+ else
+ {
+ const bool bOnlyHardAttr = false;
+ rFormatSet->Put(GetAttrFromMarked(bOnlyHardAttr));
+ }
+
+ // check for cloning from table cell, in which case we need to copy cell-specific formatting attributes
+ const SdrObject* pObj = rMarkList.GetMark(0)->GetMarkedSdrObj();
+ if (pObj && (pObj->GetObjInventor() == SdrInventor::Default)
+ && (pObj->GetObjIdentifier() == SdrObjKind::Table))
+ {
+ auto pTable = static_cast<const sdr::table::SdrTableObj*>(pObj);
+ if (mxSelectionController.is() && pTable->getActiveCell().is())
+ {
+ mxSelectionController->GetAttributes(*rFormatSet, false);
+ }
+ }
+}
+
+static SfxItemSet CreatePaintSet(const WhichRangesContainer& pRanges, SfxItemPool& rPool,
+ const SfxItemSet& rSourceSet, const SfxItemSet& rTargetSet,
+ bool bNoCharacterFormats, bool bNoParagraphFormats)
+{
+ SfxItemSet aPaintSet(rPool, pRanges);
+
+ for (const auto& pRange : pRanges)
+ {
+ sal_uInt16 nWhich = pRange.first;
+ const sal_uInt16 nLastWhich = pRange.second;
+
+ if (bNoCharacterFormats && (nWhich == EE_CHAR_START))
+ continue;
+
+ if (bNoParagraphFormats && (nWhich == EE_PARA_START))
+ continue;
+
+ for (; nWhich <= nLastWhich; nWhich++)
+ {
+ const SfxPoolItem* pSourceItem = rSourceSet.GetItem(nWhich);
+ const SfxPoolItem* pTargetItem = rTargetSet.GetItem(nWhich);
+
+ if ((pSourceItem && !pTargetItem)
+ || (pSourceItem && pTargetItem && *pSourceItem != *pTargetItem))
+ {
+ aPaintSet.Put(*pSourceItem);
+ }
+ }
+ }
+ return aPaintSet;
+}
+
+void SdrObjEditView::ApplyFormatPaintBrushToText(SfxItemSet const& rFormatSet, SdrTextObj& rTextObj,
+ SdrText* pText, bool bNoCharacterFormats,
+ bool bNoParagraphFormats)
+{
+ OutlinerParaObject* pParaObj = pText ? pText->GetOutlinerParaObject() : nullptr;
+ if (!pParaObj)
+ return;
+
+ SdrOutliner& rOutliner = rTextObj.ImpGetDrawOutliner();
+ rOutliner.SetText(*pParaObj);
+
+ sal_Int32 nParaCount(rOutliner.GetParagraphCount());
+
+ if (!nParaCount)
+ return;
+
+ for (sal_Int32 nPara = 0; nPara < nParaCount; nPara++)
+ {
+ if (!bNoCharacterFormats)
+ rOutliner.RemoveCharAttribs(nPara);
+
+ SfxItemSet aSet(rOutliner.GetParaAttribs(nPara));
+ aSet.Put(CreatePaintSet(GetFormatRangeImpl(true), *aSet.GetPool(), rFormatSet, aSet,
+ bNoCharacterFormats, bNoParagraphFormats));
+ rOutliner.SetParaAttribs(nPara, aSet);
+ }
+
+ std::optional<OutlinerParaObject> pTemp = rOutliner.CreateParaObject(0, nParaCount);
+ rOutliner.Clear();
+
+ rTextObj.NbcSetOutlinerParaObjectForText(std::move(pTemp), pText);
+}
+
+void SdrObjEditView::DisposeUndoManager()
+{
+ if (mpTextEditOutliner)
+ {
+ if (typeid(mpTextEditOutliner->GetUndoManager()) != typeid(EditUndoManager))
+ {
+ // Non-owning pointer, clear it.
+ mpTextEditOutliner->SetUndoManager(nullptr);
+ }
+ }
+
+ mpOldTextEditUndoManager = nullptr;
+}
+
+void SdrObjEditView::ApplyFormatPaintBrush(SfxItemSet& rFormatSet, bool bNoCharacterFormats,
+ bool bNoParagraphFormats)
+{
+ if (mxSelectionController.is()
+ && mxSelectionController->ApplyFormatPaintBrush(rFormatSet, bNoCharacterFormats,
+ bNoParagraphFormats))
+ {
+ return;
+ }
+
+ OutlinerView* pOLV = GetTextEditOutlinerView();
+ const SdrMarkList& rMarkList = GetMarkedObjectList();
+ if (!pOLV)
+ {
+ SdrObject* pObj = rMarkList.GetMark(0)->GetMarkedSdrObj();
+ const SfxItemSet& rShapeSet = pObj->GetMergedItemSet();
+
+ // if not in text edit mode (aka the user selected text or clicked on a word)
+ // apply formatting attributes to selected shape
+ // All formatting items (see ranges above) that are unequal in selected shape and
+ // the format paintbrush are hard set on the selected shape.
+
+ const WhichRangesContainer& pRanges = rFormatSet.GetRanges();
+ bool bTextOnly = true;
+
+ for (const auto& pRange : pRanges)
+ {
+ if ((pRange.first != EE_PARA_START) && (pRange.first != EE_CHAR_START))
+ {
+ bTextOnly = false;
+ break;
+ }
+ }
+
+ if (!bTextOnly)
+ {
+ SfxItemSet aPaintSet(CreatePaintSet(GetFormatRangeImpl(false), *rShapeSet.GetPool(),
+ rFormatSet, rShapeSet, bNoCharacterFormats,
+ bNoParagraphFormats));
+ SetAttrToMarked(aPaintSet, false /*bReplaceAll*/);
+ }
+
+ // now apply character and paragraph formatting to text, if the shape has any
+ SdrTextObj* pTextObj = dynamic_cast<SdrTextObj*>(pObj);
+ if (pTextObj)
+ {
+ sal_Int32 nText = pTextObj->getTextCount();
+
+ while (--nText >= 0)
+ {
+ SdrText* pText = pTextObj->getText(nText);
+ ApplyFormatPaintBrushToText(rFormatSet, *pTextObj, pText, bNoCharacterFormats,
+ bNoParagraphFormats);
+ }
+ }
+ }
+ else
+ {
+ ::Outliner* pOutliner = pOLV->GetOutliner();
+ if (pOutliner)
+ {
+ const EditEngine& rEditEngine = pOutliner->GetEditEngine();
+
+ ESelection aSel(pOLV->GetSelection());
+ if (!aSel.HasRange())
+ pOLV->SetSelection(rEditEngine.GetWord(aSel, css::i18n::WordType::DICTIONARY_WORD));
+
+ const bool bRemoveParaAttribs = !bNoParagraphFormats;
+ pOLV->RemoveAttribsKeepLanguages(bRemoveParaAttribs);
+ SfxItemSet aSet(pOLV->GetAttribs());
+ SfxItemSet aPaintSet(CreatePaintSet(GetFormatRangeImpl(true), *aSet.GetPool(),
+ rFormatSet, aSet, bNoCharacterFormats,
+ bNoParagraphFormats));
+ pOLV->SetAttribs(aPaintSet);
+ }
+ }
+
+ // check for cloning to table cell, in which case we need to copy cell-specific formatting attributes
+ SdrObject* pObj = rMarkList.GetMark(0)->GetMarkedSdrObj();
+ if (pObj && (pObj->GetObjInventor() == SdrInventor::Default)
+ && (pObj->GetObjIdentifier() == SdrObjKind::Table))
+ {
+ auto pTable = static_cast<sdr::table::SdrTableObj*>(pObj);
+ if (pTable->getActiveCell().is() && mxSelectionController.is())
+ {
+ mxSelectionController->SetAttributes(rFormatSet, false);
+ }
+ }
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/svdraw/svdetc.cxx b/svx/source/svdraw/svdetc.cxx
new file mode 100644
index 000000000..154bd3c7b
--- /dev/null
+++ b/svx/source/svdraw/svdetc.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 <sal/config.h>
+
+#include <algorithm>
+
+#include <officecfg/Office/Common.hxx>
+#include <svtools/colorcfg.hxx>
+#include <svx/svdetc.hxx>
+#include <svx/svdedxv.hxx>
+#include <svx/svdmodel.hxx>
+#include <svx/svdoutl.hxx>
+#include <vcl/BitmapReadAccess.hxx>
+#include <editeng/eeitem.hxx>
+#include <svl/itemset.hxx>
+#include <svl/whiter.hxx>
+#include <svx/xgrad.hxx>
+#include <svx/xfillit0.hxx>
+#include <svx/xflclit.hxx>
+#include <svx/xflhtit.hxx>
+#include <svx/xbtmpit.hxx>
+#include <svx/xflgrit.hxx>
+#include <svx/svdoole2.hxx>
+#include <svl/itempool.hxx>
+#include <unotools/configmgr.hxx>
+#include <unotools/localedatawrapper.hxx>
+#include <unotools/syslocale.hxx>
+#include <svx/xflbckit.hxx>
+#include <svx/extrusionbar.hxx>
+#include <svx/fontworkbar.hxx>
+#include <vcl/svapp.hxx>
+#include <vcl/settings.hxx>
+#include <svx/sdr/contact/viewcontact.hxx>
+#include <svx/svdpage.hxx>
+#include <svx/svdpagv.hxx>
+#include <svx/svdotable.hxx>
+#include <svx/sdrhittesthelper.hxx>
+
+#include <com/sun/star/frame/XModel.hpp>
+#include <com/sun/star/embed/XEmbeddedObject.hpp>
+
+using namespace ::com::sun::star;
+
+// Global data of the DrawingEngine
+SdrGlobalData::SdrGlobalData()
+ : pSysLocale(nullptr)
+{
+ if (!utl::ConfigManager::IsFuzzing())
+ {
+ svx::ExtrusionBar::RegisterInterface();
+ svx::FontworkBar::RegisterInterface();
+ }
+}
+
+const SvtSysLocale* SdrGlobalData::GetSysLocale()
+{
+ if ( !pSysLocale )
+ pSysLocale = new SvtSysLocale;
+ return pSysLocale;
+}
+const LocaleDataWrapper* SdrGlobalData::GetLocaleData()
+{
+ return &GetSysLocale()->GetLocaleData();
+}
+
+namespace {
+
+struct TheSdrGlobalData: public rtl::Static<SdrGlobalData, TheSdrGlobalData> {};
+
+}
+
+SdrGlobalData & GetSdrGlobalData() {
+ return TheSdrGlobalData::get();
+}
+
+OLEObjCache::OLEObjCache()
+{
+ if (!utl::ConfigManager::IsFuzzing())
+ {
+// This limit is only useful on 32-bit windows, where we can run out of virtual memory (see tdf#95579)
+// For everything else, we are better off keeping it in main memory rather than using our hacky page-out thing
+#if defined _WIN32 && !defined _WIN64
+ nSize = officecfg::Office::Common::Cache::DrawingEngine::OLE_Objects::get();
+#else
+ nSize = SAL_MAX_INT32; // effectively disable the page-out mechanism
+#endif
+ }
+ else
+ nSize = 100;
+ pTimer.reset( new AutoTimer( "svx OLEObjCache pTimer UnloadCheck" ) );
+ pTimer->SetInvokeHandler( LINK(this, OLEObjCache, UnloadCheckHdl) );
+ pTimer->SetTimeout(20000);
+ pTimer->SetStatic();
+}
+
+OLEObjCache::~OLEObjCache()
+{
+ pTimer->Stop();
+}
+
+IMPL_LINK_NOARG(OLEObjCache, UnloadCheckHdl, Timer*, void)
+{
+ if (nSize >= maObjs.size())
+ return;
+
+ // more objects than configured cache size try to remove objects
+ // of course not the freshly inserted one at nIndex=0
+ size_t nCount2 = maObjs.size();
+ size_t nIndex = nCount2-1;
+ while( nIndex && nCount2 > nSize )
+ {
+ SdrOle2Obj* pUnloadObj = maObjs[nIndex--];
+ if (!pUnloadObj)
+ continue;
+
+ try
+ {
+ // it is important to get object without reinitialization to avoid reentrance
+ const uno::Reference< embed::XEmbeddedObject > & xUnloadObj = pUnloadObj->GetObjRef_NoInit();
+
+ bool bUnload = !xUnloadObj || SdrOle2Obj::CanUnloadRunningObj( xUnloadObj, pUnloadObj->GetAspect() );
+
+ // check whether the object can be unloaded before looking for the parent objects
+ if ( xUnloadObj.is() && bUnload )
+ {
+ uno::Reference< frame::XModel > xUnloadModel( xUnloadObj->getComponent(), uno::UNO_QUERY );
+ if ( xUnloadModel.is() )
+ {
+ for (SdrOle2Obj* pCacheObj : maObjs)
+ {
+ if ( pCacheObj && pCacheObj != pUnloadObj )
+ {
+ uno::Reference< frame::XModel > xParentModel = pCacheObj->GetParentXModel();
+ if ( xUnloadModel == xParentModel )
+ {
+ bUnload = false; // the object has running embedded objects
+ break;
+ }
+ }
+ }
+ }
+ }
+
+ if (bUnload && UnloadObj(*pUnloadObj))
+ {
+ // object was successfully unloaded
+ RemoveObj(pUnloadObj);
+ nCount2 = std::min(nCount2 - 1, maObjs.size());
+ if (nIndex >= nCount2)
+ nIndex = nCount2 - 1;
+ }
+ }
+ catch( uno::Exception& )
+ {}
+ }
+}
+
+void OLEObjCache::InsertObj(SdrOle2Obj* pObj)
+{
+ if (!maObjs.empty())
+ {
+ SdrOle2Obj* pExistingObj = maObjs.front();
+ if ( pObj == pExistingObj )
+ // the object is already on the top, nothing has to be changed
+ return;
+ }
+
+ // get the old position of the object to know whether it is already in container
+ std::vector<SdrOle2Obj*>::iterator it = std::find(maObjs.begin(), maObjs.end(), pObj);
+ bool bFound = it != maObjs.end();
+
+ if (bFound)
+ maObjs.erase(it);
+ // insert object into first position
+ maObjs.insert(maObjs.begin(), pObj);
+
+ // if a new object was inserted, recalculate the cache
+ if (!bFound)
+ pTimer->Invoke();
+
+ if (!bFound || !pTimer->IsActive())
+ pTimer->Start();
+}
+
+void OLEObjCache::RemoveObj(SdrOle2Obj* pObj)
+{
+ std::vector<SdrOle2Obj*>::iterator it = std::find(maObjs.begin(), maObjs.end(), pObj);
+ if (it != maObjs.end())
+ maObjs.erase(it);
+ if (maObjs.empty())
+ pTimer->Stop();
+}
+
+size_t OLEObjCache::size() const
+{
+ return maObjs.size();
+}
+
+SdrOle2Obj* OLEObjCache::operator[](size_t nPos)
+{
+ return maObjs[nPos];
+}
+
+const SdrOle2Obj* OLEObjCache::operator[](size_t nPos) const
+{
+ return maObjs[nPos];
+}
+
+bool OLEObjCache::UnloadObj(SdrOle2Obj& rObj)
+{
+ bool bUnloaded = false;
+
+ //#i80528# The old mechanism is completely useless, only taking into account if
+ // in all views the GrafDraft feature is used. This will nearly never have been the
+ // case since no one ever used this option.
+
+ // A much better (and working) criteria would be the VOC contact count.
+ // The question is what will happen when i make it work now suddenly? I
+ // will try it for 2.4.
+ const sdr::contact::ViewContact& rViewContact = rObj.GetViewContact();
+ const bool bVisible(rViewContact.HasViewObjectContacts());
+
+ if(!bVisible)
+ {
+ bUnloaded = rObj.Unload();
+ }
+
+ return bUnloaded;
+}
+
+bool GetDraftFillColor(const SfxItemSet& rSet, Color& rCol)
+{
+ drawing::FillStyle eFill=rSet.Get(XATTR_FILLSTYLE).GetValue();
+ bool bRetval = false;
+
+ switch(eFill)
+ {
+ case drawing::FillStyle_SOLID:
+ {
+ rCol = rSet.Get(XATTR_FILLCOLOR).GetColorValue();
+ bRetval = true;
+
+ break;
+ }
+ case drawing::FillStyle_HATCH:
+ {
+ Color aCol1(rSet.Get(XATTR_FILLHATCH).GetHatchValue().GetColor());
+ Color aCol2(COL_WHITE);
+
+ // when hatched background is activated, use object fill color as hatch color
+ bool bFillHatchBackground = rSet.Get(XATTR_FILLBACKGROUND).GetValue();
+ if(bFillHatchBackground)
+ {
+ aCol2 = rSet.Get(XATTR_FILLCOLOR).GetColorValue();
+ }
+
+ const basegfx::BColor aAverageColor(basegfx::average(aCol1.getBColor(), aCol2.getBColor()));
+ rCol = Color(aAverageColor);
+ bRetval = true;
+
+ break;
+ }
+ case drawing::FillStyle_GRADIENT: {
+ const XGradient& rGrad=rSet.Get(XATTR_FILLGRADIENT).GetGradientValue();
+ Color aCol1(rGrad.GetStartColor());
+ Color aCol2(rGrad.GetEndColor());
+ const basegfx::BColor aAverageColor(basegfx::average(aCol1.getBColor(), aCol2.getBColor()));
+ rCol = Color(aAverageColor);
+ bRetval = true;
+
+ break;
+ }
+ case drawing::FillStyle_BITMAP:
+ {
+ Bitmap aBitmap(rSet.Get(XATTR_FILLBITMAP).GetGraphicObject().GetGraphic().GetBitmapEx().GetBitmap());
+ const Size aSize(aBitmap.GetSizePixel());
+ const sal_uInt32 nWidth = aSize.Width();
+ const sal_uInt32 nHeight = aSize.Height();
+ if (nWidth <= 0 || nHeight <= 0)
+ return bRetval;
+
+ Bitmap::ScopedReadAccess pAccess(aBitmap);
+
+ if (pAccess)
+ {
+ sal_uInt32 nRt(0);
+ sal_uInt32 nGn(0);
+ sal_uInt32 nBl(0);
+ const sal_uInt32 nMaxSteps(8);
+ const sal_uInt32 nXStep((nWidth > nMaxSteps) ? nWidth / nMaxSteps : 1);
+ const sal_uInt32 nYStep((nHeight > nMaxSteps) ? nHeight / nMaxSteps : 1);
+ sal_uInt32 nCount(0);
+
+ for(sal_uInt32 nY(0); nY < nHeight; nY += nYStep)
+ {
+ for(sal_uInt32 nX(0); nX < nWidth; nX += nXStep)
+ {
+ const BitmapColor& rCol2 = pAccess->GetColor(nY, nX);
+
+ nRt += rCol2.GetRed();
+ nGn += rCol2.GetGreen();
+ nBl += rCol2.GetBlue();
+ nCount++;
+ }
+ }
+
+ nRt /= nCount;
+ nGn /= nCount;
+ nBl /= nCount;
+
+ rCol = Color(sal_uInt8(nRt), sal_uInt8(nGn), sal_uInt8(nBl));
+
+ bRetval = true;
+ }
+ break;
+ }
+ default: break;
+ }
+
+ return bRetval;
+}
+
+std::unique_ptr<SdrOutliner> SdrMakeOutliner(OutlinerMode nOutlinerMode, SdrModel& rModel)
+{
+ SfxItemPool* pPool = &rModel.GetItemPool();
+ std::unique_ptr<SdrOutliner> pOutl(new SdrOutliner( pPool, nOutlinerMode ));
+ pOutl->SetEditTextObjectPool( pPool );
+ pOutl->SetStyleSheetPool( static_cast<SfxStyleSheetPool*>(rModel.GetStyleSheetPool()));
+ pOutl->SetDefTab(rModel.GetDefaultTabulator());
+ Outliner::SetForbiddenCharsTable(rModel.GetForbiddenCharsTable());
+ pOutl->SetAsianCompressionMode(rModel.GetCharCompressType());
+ pOutl->SetKernAsianPunctuation(rModel.IsKernAsianPunctuation());
+ pOutl->SetAddExtLeading(rModel.IsAddExtLeading());
+ return pOutl;
+}
+
+std::vector<Link<SdrObjCreatorParams, SdrObject*>>& ImpGetUserMakeObjHdl()
+{
+ SdrGlobalData& rGlobalData=GetSdrGlobalData();
+ return rGlobalData.aUserMakeObjHdl;
+}
+
+bool SearchOutlinerItems(const SfxItemSet& rSet, bool bInklDefaults, bool* pbOnlyEE)
+{
+ bool bHas=false;
+ bool bOnly=true;
+ bool bLookOnly=pbOnlyEE!=nullptr;
+ SfxWhichIter aIter(rSet);
+ sal_uInt16 nWhich=aIter.FirstWhich();
+ while (((bLookOnly && bOnly) || !bHas) && nWhich!=0) {
+ // For bInklDefaults, the entire Which range is decisive,
+ // in other cases only the set items are.
+ // Disabled and DontCare are regarded as holes in the Which range.
+ SfxItemState eState=aIter.GetItemState();
+ if ((eState==SfxItemState::DEFAULT && bInklDefaults) || eState==SfxItemState::SET) {
+ if (nWhich<EE_ITEMS_START || nWhich>EE_ITEMS_END) bOnly=false;
+ else bHas=true;
+ }
+ nWhich=aIter.NextWhich();
+ }
+ if (!bHas) bOnly=false;
+ if (pbOnlyEE!=nullptr) *pbOnlyEE=bOnly;
+ return bHas;
+}
+
+WhichRangesContainer RemoveWhichRange(const WhichRangesContainer& pOldWhichTable, sal_uInt16 nRangeBeg, sal_uInt16 nRangeEnd)
+{
+ // Six possible cases (per range):
+ // [Beg..End] [nRangeBeg, nRangeEnd], to delete
+ // [b..e] [b..e] [b..e] Cases 1,3,2: doesn't matter, delete, doesn't matter + Ranges
+ // [b........e] [b........e] Cases 4,5 : shrink range | in
+ // [b......................e] Case 6 : splitting + pOldWhichTable
+ std::vector<WhichPair> buf;
+ for (const auto & rPair : pOldWhichTable) {
+ auto const begin = rPair.first;
+ auto const end = rPair.second;
+ if (end < nRangeBeg || begin > nRangeEnd) { // cases 1, 2
+ buf.push_back({begin, end});
+ } else if (begin >= nRangeBeg && end <= nRangeEnd) { // case 3
+ // drop
+ } else if (end <= nRangeEnd) { // case 4
+ buf.push_back({begin, nRangeBeg - 1});
+ } else if (begin >= nRangeBeg) { // case 5
+ buf.push_back({nRangeEnd + 1, end});
+ } else { // case 6
+ buf.push_back({begin, nRangeBeg - 1});
+ buf.push_back({nRangeEnd + 1, end});
+ }
+ }
+ std::unique_ptr<WhichPair[]> pNewWhichTable(new WhichPair[buf.size()]);
+ std::copy(buf.begin(), buf.end(), pNewWhichTable.get());
+ return WhichRangesContainer(std::move(pNewWhichTable), buf.size());
+}
+
+
+SvdProgressInfo::SvdProgressInfo( const Link<void*,bool>&_rLink )
+{
+ maLink = _rLink;
+ m_nSumCurAction = 0;
+
+ m_nObjCount = 0;
+ m_nCurObj = 0;
+
+ m_nActionCount = 0;
+ m_nCurAction = 0;
+
+ m_nInsertCount = 0;
+ m_nCurInsert = 0;
+}
+
+void SvdProgressInfo::Init( size_t nObjCount )
+{
+ m_nObjCount = nObjCount;
+}
+
+bool SvdProgressInfo::ReportActions( size_t nActionCount )
+{
+ m_nSumCurAction += nActionCount;
+ m_nCurAction += nActionCount;
+ if(m_nCurAction > m_nActionCount)
+ m_nCurAction = m_nActionCount;
+
+ return maLink.Call(nullptr);
+}
+
+void SvdProgressInfo::ReportInserts( size_t nInsertCount )
+{
+ m_nSumCurAction += nInsertCount;
+ m_nCurInsert += nInsertCount;
+
+ maLink.Call(nullptr);
+}
+
+void SvdProgressInfo::ReportRescales( size_t nRescaleCount )
+{
+ m_nSumCurAction += nRescaleCount;
+ maLink.Call(nullptr);
+}
+
+void SvdProgressInfo::SetActionCount( size_t nActionCount )
+{
+ m_nActionCount = nActionCount;
+}
+
+void SvdProgressInfo::SetInsertCount( size_t nInsertCount )
+{
+ m_nInsertCount = nInsertCount;
+}
+
+void SvdProgressInfo::SetNextObject()
+{
+ m_nActionCount = 0;
+ m_nCurAction = 0;
+
+ m_nInsertCount = 0;
+ m_nCurInsert = 0;
+
+ m_nCurObj++;
+ ReportActions(0);
+}
+
+// #i101872# isolate GetTextEditBackgroundColor to tooling; it will anyways only be used as long
+// as text edit is not running on overlay
+
+namespace
+{
+ bool impGetSdrObjListFillColor(
+ const SdrObjList& rList,
+ const Point& rPnt,
+ const SdrPageView& rTextEditPV,
+ const SdrLayerIDSet& rVisLayers,
+ Color& rCol)
+ {
+ bool bRet(false);
+ bool bMaster(rList.getSdrPageFromSdrObjList() && rList.getSdrPageFromSdrObjList()->IsMasterPage());
+
+ for(size_t no(rList.GetObjCount()); !bRet && no > 0; )
+ {
+ no--;
+ SdrObject* pObj = rList.GetObj(no);
+ SdrObjList* pOL = pObj->GetSubList();
+
+ if(pOL)
+ {
+ // group object
+ bRet = impGetSdrObjListFillColor(*pOL, rPnt, rTextEditPV, rVisLayers, rCol);
+ }
+ else
+ {
+ SdrTextObj* pText = dynamic_cast< SdrTextObj * >(pObj);
+
+ // Exclude zero master page object (i.e. background shape) from color query
+ if(pText
+ && pObj->IsClosedObj()
+ && (!bMaster || (!pObj->IsNotVisibleAsMaster() && 0 != no))
+ && pObj->GetCurrentBoundRect().Contains(rPnt)
+ && !pText->IsHideContour()
+ && SdrObjectPrimitiveHit(*pObj, rPnt, 0, rTextEditPV, &rVisLayers, false))
+ {
+ bRet = GetDraftFillColor(pObj->GetMergedItemSet(), rCol);
+ }
+ }
+ }
+
+ return bRet;
+ }
+
+ bool impGetSdrPageFillColor(
+ const SdrPage& rPage,
+ const Point& rPnt,
+ const SdrPageView& rTextEditPV,
+ const SdrLayerIDSet& rVisLayers,
+ Color& rCol,
+ bool bSkipBackgroundShape)
+ {
+ bool bRet(impGetSdrObjListFillColor(rPage, rPnt, rTextEditPV, rVisLayers, rCol));
+
+ if(!bRet && !rPage.IsMasterPage())
+ {
+ if(rPage.TRG_HasMasterPage())
+ {
+ SdrLayerIDSet aSet(rVisLayers);
+ aSet &= rPage.TRG_GetMasterPageVisibleLayers();
+ SdrPage& rMasterPage = rPage.TRG_GetMasterPage();
+
+ // Don't fall back to background shape on
+ // master pages. This is later handled by
+ // GetBackgroundColor, and is necessary to cater for
+ // the silly ordering: 1. shapes, 2. master page
+ // shapes, 3. page background, 4. master page
+ // background.
+ bRet = impGetSdrPageFillColor(rMasterPage, rPnt, rTextEditPV, aSet, rCol, true);
+ }
+ }
+
+ // Only now determine background color from background shapes
+ if(!bRet && !bSkipBackgroundShape)
+ {
+ rCol = rPage.GetPageBackgroundColor();
+ return true;
+ }
+
+ return bRet;
+ }
+
+ Color impCalcBackgroundColor(
+ const tools::Rectangle& rArea,
+ const SdrPageView& rTextEditPV,
+ const SdrPage& rPage)
+ {
+ svtools::ColorConfig aColorConfig;
+ Color aBackground(aColorConfig.GetColorValue(svtools::DOCCOLOR).nColor);
+ const StyleSettings& rStyleSettings = Application::GetSettings().GetStyleSettings();
+
+ if(!rStyleSettings.GetHighContrastMode())
+ {
+ // search in page
+ const sal_uInt16 SPOTCOUNT(5);
+ Point aSpotPos[SPOTCOUNT];
+ Color aSpotColor[SPOTCOUNT];
+ sal_uInt32 nHeight( rArea.GetSize().Height() );
+ sal_uInt32 nWidth( rArea.GetSize().Width() );
+ sal_uInt32 nWidth14 = nWidth / 4;
+ sal_uInt32 nHeight14 = nHeight / 4;
+ sal_uInt32 nWidth34 = ( 3 * nWidth ) / 4;
+ sal_uInt32 nHeight34 = ( 3 * nHeight ) / 4;
+
+ sal_uInt16 i;
+ for ( i = 0; i < SPOTCOUNT; i++ )
+ {
+ // five spots are used
+ switch ( i )
+ {
+ case 0 :
+ {
+ // Center-Spot
+ aSpotPos[i] = rArea.Center();
+ }
+ break;
+
+ case 1 :
+ {
+ // TopLeft-Spot
+ aSpotPos[i] = rArea.TopLeft();
+ aSpotPos[i].AdjustX(nWidth14 );
+ aSpotPos[i].AdjustY(nHeight14 );
+ }
+ break;
+
+ case 2 :
+ {
+ // TopRight-Spot
+ aSpotPos[i] = rArea.TopLeft();
+ aSpotPos[i].AdjustX(nWidth34 );
+ aSpotPos[i].AdjustY(nHeight14 );
+ }
+ break;
+
+ case 3 :
+ {
+ // BottomLeft-Spot
+ aSpotPos[i] = rArea.TopLeft();
+ aSpotPos[i].AdjustX(nWidth14 );
+ aSpotPos[i].AdjustY(nHeight34 );
+ }
+ break;
+
+ case 4 :
+ {
+ // BottomRight-Spot
+ aSpotPos[i] = rArea.TopLeft();
+ aSpotPos[i].AdjustX(nWidth34 );
+ aSpotPos[i].AdjustY(nHeight34 );
+ }
+ break;
+
+ }
+
+ aSpotColor[i] = COL_WHITE;
+ impGetSdrPageFillColor(rPage, aSpotPos[i], rTextEditPV, rTextEditPV.GetVisibleLayers(), aSpotColor[i], false);
+ }
+
+ sal_uInt16 aMatch[SPOTCOUNT];
+
+ for ( i = 0; i < SPOTCOUNT; i++ )
+ {
+ // were same spot colors found?
+ aMatch[i] = 0;
+
+ for ( sal_uInt16 j = 0; j < SPOTCOUNT; j++ )
+ {
+ if( j != i )
+ {
+ if( aSpotColor[i] == aSpotColor[j] )
+ {
+ aMatch[i]++;
+ }
+ }
+ }
+ }
+
+ // highest weight to center spot
+ aBackground = aSpotColor[0];
+
+ for ( sal_uInt16 nMatchCount = SPOTCOUNT - 1; nMatchCount > 1; nMatchCount-- )
+ {
+ // which spot color was found most?
+ for ( i = 0; i < SPOTCOUNT; i++ )
+ {
+ if( aMatch[i] == nMatchCount )
+ {
+ aBackground = aSpotColor[i];
+ nMatchCount = 1; // break outer for-loop
+ break;
+ }
+ }
+ }
+ }
+
+ return aBackground;
+ }
+} // end of anonymous namespace
+
+Color GetTextEditBackgroundColor(const SdrObjEditView& rView)
+{
+ svtools::ColorConfig aColorConfig;
+ Color aBackground(aColorConfig.GetColorValue(svtools::DOCCOLOR).nColor);
+ const StyleSettings& rStyleSettings = Application::GetSettings().GetStyleSettings();
+
+ if(!rStyleSettings.GetHighContrastMode())
+ {
+ bool bFound(false);
+ SdrTextObj* pText = rView.GetTextEditObject();
+
+ if(pText && pText->IsClosedObj())
+ {
+ sdr::table::SdrTableObj* pTable = dynamic_cast< sdr::table::SdrTableObj * >( pText );
+
+ if( pTable )
+ bFound = GetDraftFillColor(pTable->GetActiveCellItemSet(), aBackground );
+
+ if( !bFound )
+ bFound=GetDraftFillColor(pText->GetMergedItemSet(), aBackground);
+ }
+
+ if(!bFound && pText)
+ {
+ SdrPageView* pTextEditPV = rView.GetTextEditPageView();
+
+ if(pTextEditPV)
+ {
+ Point aPvOfs(pText->GetTextEditOffset());
+ const SdrPage* pPg = pTextEditPV->GetPage();
+
+ if(pPg)
+ {
+ tools::Rectangle aSnapRect( pText->GetSnapRect() );
+ aSnapRect.Move(aPvOfs.X(), aPvOfs.Y());
+
+ return impCalcBackgroundColor(aSnapRect, *pTextEditPV, *pPg);
+ }
+ }
+ }
+ }
+
+ return aBackground;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/svdraw/svdfmtf.cxx b/svx/source/svdraw/svdfmtf.cxx
new file mode 100644
index 000000000..659fae090
--- /dev/null
+++ b/svx/source/svdraw/svdfmtf.cxx
@@ -0,0 +1,1641 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include "svdfmtf.hxx"
+#include <math.h>
+#include <editeng/eeitem.hxx>
+#include <editeng/fhgtitem.hxx>
+#include <editeng/wghtitem.hxx>
+#include <editeng/postitem.hxx>
+#include <editeng/udlnitem.hxx>
+#include <editeng/crossedoutitem.hxx>
+#include <editeng/shdditem.hxx>
+#include <svx/xlineit0.hxx>
+#include <svx/xlnclit.hxx>
+#include <svx/xlncapit.hxx>
+#include <svx/xlnwtit.hxx>
+#include <svx/xfillit0.hxx>
+#include <svx/xflclit.hxx>
+#include <svx/xgrad.hxx>
+#include <svx/xflgrit.hxx>
+#include <editeng/fontitem.hxx>
+#include <editeng/wrlmitem.hxx>
+#include <editeng/contouritem.hxx>
+#include <editeng/colritem.hxx>
+#include <vcl/canvastools.hxx>
+#include <vcl/metric.hxx>
+#include <editeng/charscaleitem.hxx>
+#include <svx/xflhtit.hxx>
+#include <svx/sdmetitm.hxx>
+#include <svx/sdtagitm.hxx>
+#include <svx/sdtaitm.hxx>
+#include <svx/sdtditm.hxx>
+#include <svx/sdtfsitm.hxx>
+#include <svx/svdmodel.hxx>
+#include <svx/svdpage.hxx>
+#include <svx/svdobj.hxx>
+#include <svx/svdotext.hxx>
+#include <svx/svdorect.hxx>
+#include <svx/svdocirc.hxx>
+#include <svx/svdograf.hxx>
+#include <svx/svdopath.hxx>
+#include <svx/svdetc.hxx>
+#include <svl/itemset.hxx>
+#include <basegfx/polygon/b2dpolygon.hxx>
+#include <tools/helpers.hxx>
+#include <basegfx/matrix/b2dhommatrix.hxx>
+#include <basegfx/matrix/b2dhommatrixtools.hxx>
+#include <svx/xlinjoit.hxx>
+#include <svx/xlndsit.hxx>
+#include <basegfx/polygon/b2dpolygonclipper.hxx>
+#include <svx/xbtmpit.hxx>
+#include <svx/xfltrit.hxx>
+#include <svx/xflbmtit.hxx>
+#include <svx/xflbstit.hxx>
+#include <svx/svdpntv.hxx>
+#include <basegfx/polygon/b2dpolypolygontools.hxx>
+#include <svx/svditer.hxx>
+#include <svx/svdogrp.hxx>
+#include <vcl/BitmapTools.hxx>
+#include <osl/diagnose.h>
+
+using namespace com::sun::star;
+
+ImpSdrGDIMetaFileImport::ImpSdrGDIMetaFileImport(
+ SdrModel& rModel,
+ SdrLayerID nLay,
+ const tools::Rectangle& rRect)
+: mpVD(VclPtr<VirtualDevice>::Create()),
+ maScaleRect(rRect),
+ mnMapScalingOfs(0),
+ mpModel(&rModel),
+ mnLayer(nLay),
+ mnLineWidth(0),
+ maLineJoin(basegfx::B2DLineJoin::NONE),
+ maLineCap(css::drawing::LineCap_BUTT),
+ maDash(css::drawing::DashStyle_RECT, 0, 0, 0, 0, 0),
+ mbMov(false),
+ mbSize(false),
+ maOfs(0, 0),
+ mfScaleX(1.0),
+ mfScaleY(1.0),
+ maScaleX(1.0),
+ maScaleY(1.0),
+ mbFntDirty(true),
+ mbLastObjWasPolyWithoutLine(false),
+ mbNoLine(false),
+ mbNoFill(false),
+ mbLastObjWasLine(false)
+{
+ mpVD->EnableOutput(false);
+ mpVD->SetLineColor();
+ mpVD->SetFillColor();
+ maOldLineColor.SetRed( mpVD->GetLineColor().GetRed() + 1 );
+ mpLineAttr = std::make_unique<SfxItemSetFixed<XATTR_LINE_FIRST, XATTR_LINE_LAST>>(rModel.GetItemPool());
+ mpFillAttr = std::make_unique<SfxItemSetFixed<XATTR_FILL_FIRST, XATTR_FILL_LAST>>(rModel.GetItemPool());
+ mpTextAttr = std::make_unique<SfxItemSetFixed<EE_ITEMS_START, EE_ITEMS_END>>(rModel.GetItemPool());
+ checkClip();
+}
+
+void ImpSdrGDIMetaFileImport::DoLoopActions(GDIMetaFile const & rMtf, SvdProgressInfo* pProgrInfo, sal_uInt32* pActionsToReport)
+{
+ const sal_uLong nCount(rMtf.GetActionSize());
+
+ for(sal_uLong a(0); a < nCount; a++)
+ {
+ MetaAction* pAct = rMtf.GetAction(a);
+
+ if(!pAct)
+ {
+ OSL_ENSURE(false, "OOps, no action at valid position (!)");
+ pAct = rMtf.GetAction(0);
+ }
+
+ switch (pAct->GetType())
+ {
+ case MetaActionType::PIXEL : break;
+ case MetaActionType::POINT : break;
+ case MetaActionType::LINE : DoAction(static_cast<MetaLineAction &>(*pAct)); break;
+ case MetaActionType::RECT : DoAction(static_cast<MetaRectAction &>(*pAct)); break;
+ case MetaActionType::ROUNDRECT : DoAction(static_cast<MetaRoundRectAction &>(*pAct)); break;
+ case MetaActionType::ELLIPSE : DoAction(static_cast<MetaEllipseAction &>(*pAct)); break;
+ case MetaActionType::ARC : DoAction(static_cast<MetaArcAction &>(*pAct)); break;
+ case MetaActionType::PIE : DoAction(static_cast<MetaPieAction &>(*pAct)); break;
+ case MetaActionType::CHORD : DoAction(static_cast<MetaChordAction &>(*pAct)); break;
+ case MetaActionType::POLYLINE : DoAction(static_cast<MetaPolyLineAction &>(*pAct)); break;
+ case MetaActionType::POLYGON : DoAction(static_cast<MetaPolygonAction &>(*pAct)); break;
+ case MetaActionType::POLYPOLYGON : DoAction(static_cast<MetaPolyPolygonAction &>(*pAct)); break;
+ case MetaActionType::TEXT : DoAction(static_cast<MetaTextAction &>(*pAct)); break;
+ case MetaActionType::TEXTARRAY : DoAction(static_cast<MetaTextArrayAction &>(*pAct)); break;
+ case MetaActionType::STRETCHTEXT : DoAction(static_cast<MetaStretchTextAction &>(*pAct)); break;
+ case MetaActionType::BMP : DoAction(static_cast<MetaBmpAction &>(*pAct)); break;
+ case MetaActionType::BMPSCALE : DoAction(static_cast<MetaBmpScaleAction &>(*pAct)); break;
+ case MetaActionType::BMPEX : DoAction(static_cast<MetaBmpExAction &>(*pAct)); break;
+ case MetaActionType::BMPEXSCALE : DoAction(static_cast<MetaBmpExScaleAction &>(*pAct)); break;
+ case MetaActionType::LINECOLOR : DoAction(static_cast<MetaLineColorAction &>(*pAct)); break;
+ case MetaActionType::FILLCOLOR : DoAction(static_cast<MetaFillColorAction &>(*pAct)); break;
+ case MetaActionType::TEXTCOLOR : DoAction(static_cast<MetaTextColorAction &>(*pAct)); break;
+ case MetaActionType::TEXTFILLCOLOR : DoAction(static_cast<MetaTextFillColorAction &>(*pAct)); break;
+ case MetaActionType::FONT : DoAction(static_cast<MetaFontAction &>(*pAct)); break;
+ case MetaActionType::TEXTALIGN : DoAction(static_cast<MetaTextAlignAction &>(*pAct)); break;
+ case MetaActionType::MAPMODE : DoAction(static_cast<MetaMapModeAction &>(*pAct)); break;
+ case MetaActionType::CLIPREGION : DoAction(static_cast<MetaClipRegionAction &>(*pAct)); break;
+ case MetaActionType::MOVECLIPREGION : DoAction(static_cast<MetaMoveClipRegionAction &>(*pAct)); break;
+ case MetaActionType::ISECTRECTCLIPREGION: DoAction(static_cast<MetaISectRectClipRegionAction&>(*pAct)); break;
+ case MetaActionType::ISECTREGIONCLIPREGION: DoAction(static_cast<MetaISectRegionClipRegionAction&>(*pAct)); break;
+ case MetaActionType::RASTEROP : DoAction(static_cast<MetaRasterOpAction &>(*pAct)); break;
+ case MetaActionType::PUSH : DoAction(static_cast<MetaPushAction &>(*pAct)); break;
+ case MetaActionType::POP : DoAction(static_cast<MetaPopAction &>(*pAct)); break;
+ case MetaActionType::HATCH : DoAction(static_cast<MetaHatchAction &>(*pAct)); break;
+
+ // #i125211# MetaCommentAction may change index, thus hand it over
+ case MetaActionType::COMMENT : DoAction(static_cast<MetaCommentAction&>(*pAct), rMtf, a);
+ break;
+
+ // missing actions added
+ case MetaActionType::TEXTRECT : DoAction(static_cast<MetaTextRectAction&>(*pAct)); break;
+ case MetaActionType::BMPSCALEPART : DoAction(static_cast<MetaBmpScalePartAction&>(*pAct)); break;
+ case MetaActionType::BMPEXSCALEPART : DoAction(static_cast<MetaBmpExScalePartAction&>(*pAct)); break;
+ case MetaActionType::MASK : DoAction(static_cast<MetaMaskAction&>(*pAct)); break;
+ case MetaActionType::MASKSCALE : DoAction(static_cast<MetaMaskScaleAction&>(*pAct)); break;
+ case MetaActionType::MASKSCALEPART : DoAction(static_cast<MetaMaskScalePartAction&>(*pAct)); break;
+ case MetaActionType::GRADIENT : DoAction(static_cast<MetaGradientAction&>(*pAct)); break;
+ case MetaActionType::WALLPAPER : OSL_ENSURE(false, "Tried to construct SdrObject from MetaWallpaperAction: not supported (!)"); break;
+ case MetaActionType::Transparent : DoAction(static_cast<MetaTransparentAction&>(*pAct)); break;
+ case MetaActionType::EPS : OSL_ENSURE(false, "Tried to construct SdrObject from MetaEPSAction: not supported (!)"); break;
+ case MetaActionType::REFPOINT : DoAction(static_cast<MetaRefPointAction&>(*pAct)); break;
+ case MetaActionType::TEXTLINECOLOR : DoAction(static_cast<MetaTextLineColorAction&>(*pAct)); break;
+ case MetaActionType::TEXTLINE : OSL_ENSURE(false, "Tried to construct SdrObject from MetaTextLineAction: not supported (!)"); break;
+ case MetaActionType::FLOATTRANSPARENT : DoAction(static_cast<MetaFloatTransparentAction&>(*pAct)); break;
+ case MetaActionType::GRADIENTEX : DoAction(static_cast<MetaGradientExAction&>(*pAct)); break;
+ case MetaActionType::LAYOUTMODE : DoAction(static_cast<MetaLayoutModeAction&>(*pAct)); break;
+ case MetaActionType::TEXTLANGUAGE : DoAction(static_cast<MetaTextLanguageAction&>(*pAct)); break;
+ case MetaActionType::OVERLINECOLOR : DoAction(static_cast<MetaOverlineColorAction&>(*pAct)); break;
+ default: break;
+ }
+
+ if(pProgrInfo && pActionsToReport)
+ {
+ (*pActionsToReport)++;
+
+ if(*pActionsToReport >= 16) // update all 16 actions
+ {
+ if(!pProgrInfo->ReportActions(*pActionsToReport))
+ break;
+
+ *pActionsToReport = 0;
+ }
+ }
+ }
+}
+
+size_t ImpSdrGDIMetaFileImport::DoImport(
+ const GDIMetaFile& rMtf,
+ SdrObjList& rOL,
+ size_t nInsPos,
+ SvdProgressInfo* pProgrInfo)
+{
+ // setup some global scale parameter
+ // mfScaleX, mfScaleY, maScaleX, maScaleY, mbMov, mbSize
+ mfScaleX = mfScaleY = 1.0;
+ const Size aMtfSize(rMtf.GetPrefSize());
+
+ if(aMtfSize.Width() & aMtfSize.Height() && (!maScaleRect.IsEmpty()))
+ {
+ maOfs = maScaleRect.TopLeft();
+
+ if(aMtfSize.Width() != (maScaleRect.GetWidth() - 1))
+ {
+ mfScaleX = static_cast<double>( maScaleRect.GetWidth() - 1 ) / static_cast<double>(aMtfSize.Width());
+ }
+
+ if(aMtfSize.Height() != (maScaleRect.GetHeight() - 1))
+ {
+ mfScaleY = static_cast<double>( maScaleRect.GetHeight() - 1 ) / static_cast<double>(aMtfSize.Height());
+ }
+ }
+
+ mbMov = maOfs.X()!=0 || maOfs.Y()!=0;
+ mbSize = false;
+ maScaleX = Fraction( 1, 1 );
+ maScaleY = Fraction( 1, 1 );
+
+ if(aMtfSize.Width() != (maScaleRect.GetWidth() - 1))
+ {
+ maScaleX = Fraction(maScaleRect.GetWidth() - 1, aMtfSize.Width());
+ mbSize = true;
+ }
+
+ if(aMtfSize.Height() != (maScaleRect.GetHeight() - 1))
+ {
+ maScaleY = Fraction(maScaleRect.GetHeight() - 1, aMtfSize.Height());
+ mbSize = true;
+ }
+
+ if(pProgrInfo)
+ {
+ pProgrInfo->SetActionCount(rMtf.GetActionSize());
+ }
+
+ sal_uInt32 nActionsToReport(0);
+
+ // execute
+ DoLoopActions(rMtf, pProgrInfo, &nActionsToReport);
+
+ if(pProgrInfo)
+ {
+ pProgrInfo->ReportActions(nActionsToReport);
+ nActionsToReport = 0;
+ }
+
+ // MapMode scaling
+ MapScaling();
+
+ // To calculate the progress meter, we use GetActionSize()*3.
+ // However, maTmpList has a lower entry count limit than GetActionSize(),
+ // so the actions that were assumed were too much have to be re-added.
+ nActionsToReport = (rMtf.GetActionSize() - maTmpList.size()) * 2;
+
+ // announce all currently unannounced rescales
+ if(pProgrInfo)
+ {
+ pProgrInfo->ReportRescales(nActionsToReport);
+ pProgrInfo->SetInsertCount(maTmpList.size());
+ }
+
+ nActionsToReport = 0;
+
+ // insert all objects cached in aTmpList now into rOL from nInsPos
+ nInsPos = std::min(nInsPos, rOL.GetObjCount());
+
+ for(SdrObject* pObj : maTmpList)
+ {
+ rOL.NbcInsertObject(pObj, nInsPos);
+ nInsPos++;
+
+ if(pProgrInfo)
+ {
+ nActionsToReport++;
+
+ if(nActionsToReport >= 32) // update all 32 actions
+ {
+ pProgrInfo->ReportInserts(nActionsToReport);
+ nActionsToReport = 0;
+ }
+ }
+ }
+
+ // report all remaining inserts for the last time
+ if(pProgrInfo)
+ {
+ pProgrInfo->ReportInserts(nActionsToReport);
+ }
+
+ return maTmpList.size();
+}
+
+void ImpSdrGDIMetaFileImport::SetAttributes(SdrObject* pObj, bool bForceTextAttr)
+{
+ mbNoLine = false;
+ mbNoFill = false;
+ bool bLine(!bForceTextAttr);
+ bool bFill(!pObj || (pObj->IsClosedObj() && !bForceTextAttr));
+ bool bText(bForceTextAttr || (pObj && pObj->GetOutlinerParaObject()));
+
+ if(bLine)
+ {
+ if(mnLineWidth)
+ {
+ mpLineAttr->Put(XLineWidthItem(mnLineWidth));
+ }
+ else
+ {
+ mpLineAttr->Put(XLineWidthItem(0));
+ }
+
+ maOldLineColor = mpVD->GetLineColor();
+
+ if(mpVD->IsLineColor())
+ {
+ mpLineAttr->Put(XLineStyleItem(drawing::LineStyle_SOLID));
+ mpLineAttr->Put(XLineColorItem(OUString(), mpVD->GetLineColor()));
+ }
+ else
+ {
+ mpLineAttr->Put(XLineStyleItem(drawing::LineStyle_NONE));
+ }
+
+ switch(maLineJoin)
+ {
+ case basegfx::B2DLineJoin::NONE:
+ mpLineAttr->Put(XLineJointItem(css::drawing::LineJoint_NONE));
+ break;
+ case basegfx::B2DLineJoin::Bevel:
+ mpLineAttr->Put(XLineJointItem(css::drawing::LineJoint_BEVEL));
+ break;
+ case basegfx::B2DLineJoin::Miter:
+ mpLineAttr->Put(XLineJointItem(css::drawing::LineJoint_MITER));
+ break;
+ case basegfx::B2DLineJoin::Round:
+ mpLineAttr->Put(XLineJointItem(css::drawing::LineJoint_ROUND));
+ break;
+ }
+
+ // Add LineCap support
+ mpLineAttr->Put(XLineCapItem(maLineCap));
+
+ if(((maDash.GetDots() && maDash.GetDotLen()) || (maDash.GetDashes() && maDash.GetDashLen())) && maDash.GetDistance())
+ {
+ mpLineAttr->Put(XLineDashItem(OUString(), maDash));
+ }
+ else
+ {
+ mpLineAttr->Put(XLineDashItem(OUString(), XDash(css::drawing::DashStyle_RECT)));
+ }
+ }
+ else
+ {
+ mbNoLine = true;
+ }
+
+ if(bFill)
+ {
+ if(mpVD->IsFillColor())
+ {
+ mpFillAttr->Put(XFillStyleItem(drawing::FillStyle_SOLID));
+ mpFillAttr->Put(XFillColorItem(OUString(), mpVD->GetFillColor()));
+ }
+ else
+ {
+ mpFillAttr->Put(XFillStyleItem(drawing::FillStyle_NONE));
+ }
+ }
+ else
+ {
+ mbNoFill = true;
+ }
+
+ if(bText && mbFntDirty)
+ {
+ vcl::Font aFnt(mpVD->GetFont());
+ const sal_uInt32 nHeight(FRound(aFnt.GetFontSize().Height() * mfScaleY));
+
+ mpTextAttr->Put( SvxFontItem( aFnt.GetFamilyType(), aFnt.GetFamilyName(), aFnt.GetStyleName(), aFnt.GetPitch(), aFnt.GetCharSet(), EE_CHAR_FONTINFO ) );
+ mpTextAttr->Put( SvxFontItem( aFnt.GetFamilyType(), aFnt.GetFamilyName(), aFnt.GetStyleName(), aFnt.GetPitch(), aFnt.GetCharSet(), EE_CHAR_FONTINFO_CJK ) );
+ mpTextAttr->Put( SvxFontItem( aFnt.GetFamilyType(), aFnt.GetFamilyName(), aFnt.GetStyleName(), aFnt.GetPitch(), aFnt.GetCharSet(), EE_CHAR_FONTINFO_CTL ) );
+ mpTextAttr->Put(SvxPostureItem(aFnt.GetItalic(), EE_CHAR_ITALIC));
+ mpTextAttr->Put(SvxWeightItem(aFnt.GetWeight(), EE_CHAR_WEIGHT));
+ mpTextAttr->Put( SvxFontHeightItem( nHeight, 100, EE_CHAR_FONTHEIGHT ) );
+ mpTextAttr->Put( SvxFontHeightItem( nHeight, 100, EE_CHAR_FONTHEIGHT_CJK ) );
+ mpTextAttr->Put( SvxFontHeightItem( nHeight, 100, EE_CHAR_FONTHEIGHT_CTL ) );
+ mpTextAttr->Put(SvxCharScaleWidthItem(100, EE_CHAR_FONTWIDTH));
+ mpTextAttr->Put(SvxUnderlineItem(aFnt.GetUnderline(), EE_CHAR_UNDERLINE));
+ mpTextAttr->Put(SvxOverlineItem(aFnt.GetOverline(), EE_CHAR_OVERLINE));
+ mpTextAttr->Put(SvxCrossedOutItem(aFnt.GetStrikeout(), EE_CHAR_STRIKEOUT));
+ mpTextAttr->Put(SvxShadowedItem(aFnt.IsShadow(), EE_CHAR_SHADOW));
+
+ // #i118485# Setting this item leads to problems (written #i118498# for this)
+ // mpTextAttr->Put(SvxAutoKernItem(aFnt.IsKerning(), EE_CHAR_KERNING));
+
+ mpTextAttr->Put(SvxWordLineModeItem(aFnt.IsWordLineMode(), EE_CHAR_WLM));
+ mpTextAttr->Put(SvxContourItem(aFnt.IsOutline(), EE_CHAR_OUTLINE));
+ mpTextAttr->Put(SvxColorItem(mpVD->GetTextColor(), EE_CHAR_COLOR));
+ //... svxfont textitem svditext
+ mbFntDirty = false;
+ }
+
+ if(!pObj)
+ return;
+
+ pObj->SetLayer(mnLayer);
+
+ if(bLine)
+ {
+ pObj->SetMergedItemSet(*mpLineAttr);
+ }
+
+ if(bFill)
+ {
+ pObj->SetMergedItemSet(*mpFillAttr);
+ }
+
+ if(bText)
+ {
+ pObj->SetMergedItemSet(*mpTextAttr);
+ pObj->SetMergedItem(SdrTextHorzAdjustItem(SDRTEXTHORZADJUST_LEFT));
+ }
+}
+
+void ImpSdrGDIMetaFileImport::InsertObj(SdrObject* pObj, bool bScale)
+{
+ if(bScale && !maScaleRect.IsEmpty())
+ {
+ if(mbSize)
+ {
+ pObj->NbcResize(Point(), maScaleX, maScaleY);
+ }
+
+ if(mbMov)
+ {
+ pObj->NbcMove(Size(maOfs.X(), maOfs.Y()));
+ }
+ }
+
+ if(isClip())
+ {
+ const basegfx::B2DPolyPolygon aPoly(pObj->TakeXorPoly());
+ const basegfx::B2DRange aOldRange(aPoly.getB2DRange());
+ const SdrLayerID aOldLayer(pObj->GetLayer());
+ const SfxItemSet aOldItemSet(pObj->GetMergedItemSet());
+ const SdrGrafObj* pSdrGrafObj = dynamic_cast< SdrGrafObj* >(pObj);
+ const SdrTextObj* pSdrTextObj = dynamic_cast< SdrTextObj* >(pObj);
+
+ if(pSdrTextObj && pSdrTextObj->HasText())
+ {
+ // all text objects are created from ImportText and have no line or fill attributes, so
+ // it is okay to concentrate on the text itself
+ while(true)
+ {
+ const basegfx::B2DPolyPolygon aTextContour(pSdrTextObj->TakeContour());
+ const basegfx::B2DRange aTextRange(aTextContour.getB2DRange());
+ const basegfx::B2DRange aClipRange(maClip.getB2DRange());
+
+ // no overlap -> completely outside
+ if(!aClipRange.overlaps(aTextRange))
+ {
+ SdrObject::Free(pObj);
+ break;
+ }
+
+ // when the clip is a rectangle fast check for inside is possible
+ if(basegfx::utils::isRectangle(maClip) && aClipRange.isInside(aTextRange))
+ {
+ // completely inside ClipRect
+ break;
+ }
+
+ // here text needs to be clipped; to do so, convert to SdrObjects with polygons
+ // and add these recursively. Delete original object, do not add in this run
+ SdrObject* pConverted = pSdrTextObj->ConvertToPolyObj(true, true).release();
+ SdrObject::Free(pObj);
+
+ if(pConverted)
+ {
+ // recursively add created conversion; per definition this shall not
+ // contain further SdrTextObjs. Visit only non-group objects
+ SdrObjListIter aIter(*pConverted, SdrIterMode::DeepNoGroups);
+
+ // work with clones; the created conversion may contain group objects
+ // and when working with the original objects the loop itself could
+ // break and the cleanup later would be pretty complicated (only delete group
+ // objects, are these empty, ...?)
+ while(aIter.IsMore())
+ {
+ SdrObject* pCandidate = aIter.Next();
+ OSL_ENSURE(pCandidate && dynamic_cast< SdrObjGroup* >(pCandidate) == nullptr, "SdrObjListIter with SdrIterMode::DeepNoGroups error (!)");
+ SdrObject* pNewClone(pCandidate->CloneSdrObject(pCandidate->getSdrModelFromSdrObject()));
+
+ if(pNewClone)
+ {
+ InsertObj(pNewClone, false);
+ }
+ else
+ {
+ OSL_ENSURE(false, "SdrObject::Clone() failed (!)");
+ }
+ }
+
+ // cleanup temporary conversion objects
+ SdrObject::Free(pConverted);
+ }
+
+ break;
+ }
+ }
+ else
+ {
+ BitmapEx aBitmapEx;
+
+ if(pSdrGrafObj)
+ {
+ aBitmapEx = pSdrGrafObj->GetGraphic().GetBitmapEx();
+ }
+
+ SdrObject::Free(pObj);
+
+ if(!aOldRange.isEmpty())
+ {
+ // clip against ClipRegion
+ const basegfx::B2DPolyPolygon aNewPoly(
+ basegfx::utils::clipPolyPolygonOnPolyPolygon(
+ aPoly,
+ maClip,
+ true,
+ !aPoly.isClosed()));
+ const basegfx::B2DRange aNewRange(aNewPoly.getB2DRange());
+
+ if(!aNewRange.isEmpty())
+ {
+ pObj = new SdrPathObj(
+ *mpModel,
+ aNewPoly.isClosed() ? SdrObjKind::Polygon : SdrObjKind::PolyLine,
+ aNewPoly);
+
+ pObj->SetLayer(aOldLayer);
+ pObj->SetMergedItemSet(aOldItemSet);
+
+ if(!aBitmapEx.IsEmpty())
+ {
+ // aNewRange is inside of aOldRange and defines which part of aBitmapEx is used
+ const double fScaleX(aBitmapEx.GetSizePixel().Width() / (aOldRange.getWidth() ? aOldRange.getWidth() : 1.0));
+ const double fScaleY(aBitmapEx.GetSizePixel().Height() / (aOldRange.getHeight() ? aOldRange.getHeight() : 1.0));
+ basegfx::B2DRange aPixel(aNewRange);
+ basegfx::B2DHomMatrix aTrans;
+
+ aTrans.translate(-aOldRange.getMinX(), -aOldRange.getMinY());
+ aTrans.scale(fScaleX, fScaleY);
+ aPixel.transform(aTrans);
+
+ const Size aOrigSizePixel(aBitmapEx.GetSizePixel());
+ const Point aClipTopLeft(
+ basegfx::fround(floor(std::max(0.0, aPixel.getMinX()))),
+ basegfx::fround(floor(std::max(0.0, aPixel.getMinY()))));
+ const Size aClipSize(
+ basegfx::fround(ceil(std::min(static_cast<double>(aOrigSizePixel.Width()), aPixel.getWidth()))),
+ basegfx::fround(ceil(std::min(static_cast<double>(aOrigSizePixel.Height()), aPixel.getHeight()))));
+ const BitmapEx aClippedBitmap(
+ aBitmapEx,
+ aClipTopLeft,
+ aClipSize);
+
+ pObj->SetMergedItem(XFillStyleItem(drawing::FillStyle_BITMAP));
+ pObj->SetMergedItem(XFillBitmapItem(OUString(), Graphic(aClippedBitmap)));
+ pObj->SetMergedItem(XFillBmpTileItem(false));
+ pObj->SetMergedItem(XFillBmpStretchItem(true));
+ }
+ }
+ }
+ }
+ }
+
+ if(!pObj)
+ return;
+
+ // #i111954# check object for visibility
+ // used are SdrPathObj, SdrRectObj, SdrCircObj, SdrGrafObj
+ bool bVisible(false);
+
+ if(pObj->HasLineStyle())
+ {
+ bVisible = true;
+ }
+
+ if(!bVisible && pObj->HasFillStyle())
+ {
+ bVisible = true;
+ }
+
+ if(!bVisible)
+ {
+ SdrTextObj* pTextObj = dynamic_cast< SdrTextObj* >(pObj);
+
+ if(pTextObj && pTextObj->HasText())
+ {
+ bVisible = true;
+ }
+ }
+
+ if(!bVisible)
+ {
+ SdrGrafObj* pGrafObj = dynamic_cast< SdrGrafObj* >(pObj);
+
+ if(pGrafObj)
+ {
+ // this may be refined to check if the graphic really is visible. It
+ // is here to ensure that graphic objects without fill, line and text
+ // get created
+ bVisible = true;
+ }
+ }
+
+ if(!bVisible)
+ {
+ SdrObject::Free(pObj);
+ }
+ else
+ {
+ maTmpList.push_back(pObj);
+
+ if(dynamic_cast< SdrPathObj* >(pObj))
+ {
+ const bool bClosed(pObj->IsClosedObj());
+
+ mbLastObjWasPolyWithoutLine = mbNoLine && bClosed;
+ mbLastObjWasLine = !bClosed;
+ }
+ else
+ {
+ mbLastObjWasPolyWithoutLine = false;
+ mbLastObjWasLine = false;
+ }
+ }
+}
+
+void ImpSdrGDIMetaFileImport::DoAction(MetaLineAction const & rAct)
+{
+ // #i73407# reformulation to use new B2DPolygon classes
+ const basegfx::B2DPoint aStart(rAct.GetStartPoint().X(), rAct.GetStartPoint().Y());
+ const basegfx::B2DPoint aEnd(rAct.GetEndPoint().X(), rAct.GetEndPoint().Y());
+
+ if(aStart.equal(aEnd))
+ return;
+
+ basegfx::B2DPolygon aLine;
+ const basegfx::B2DHomMatrix aTransform(basegfx::utils::createScaleTranslateB2DHomMatrix(mfScaleX, mfScaleY, maOfs.X(), maOfs.Y()));
+
+ aLine.append(aStart);
+ aLine.append(aEnd);
+ aLine.transform(aTransform);
+
+ const LineInfo& rLineInfo = rAct.GetLineInfo();
+ const sal_Int32 nNewLineWidth(rLineInfo.GetWidth());
+ bool bCreateLineObject(true);
+
+ if(mbLastObjWasLine && (nNewLineWidth == mnLineWidth) && CheckLastLineMerge(aLine))
+ {
+ bCreateLineObject = false;
+ }
+
+ if(!bCreateLineObject)
+ return;
+
+ SdrPathObj* pPath = new SdrPathObj(
+ *mpModel,
+ SdrObjKind::Line,
+ basegfx::B2DPolyPolygon(aLine));
+ mnLineWidth = nNewLineWidth;
+ maLineJoin = rLineInfo.GetLineJoin();
+ maLineCap = rLineInfo.GetLineCap();
+ maDash = XDash(css::drawing::DashStyle_RECT,
+ rLineInfo.GetDotCount(), rLineInfo.GetDotLen(),
+ rLineInfo.GetDashCount(), rLineInfo.GetDashLen(),
+ rLineInfo.GetDistance());
+ SetAttributes(pPath);
+ mnLineWidth = 0;
+ maLineJoin = basegfx::B2DLineJoin::NONE;
+ maDash = XDash();
+ InsertObj(pPath, false);
+}
+
+void ImpSdrGDIMetaFileImport::DoAction(MetaRectAction const & rAct)
+{
+ SdrRectObj* pRect = new SdrRectObj(
+ *mpModel,
+ rAct.GetRect());
+ SetAttributes(pRect);
+ InsertObj(pRect);
+}
+
+void ImpSdrGDIMetaFileImport::DoAction(MetaRoundRectAction const & rAct)
+{
+ SdrRectObj* pRect = new SdrRectObj(
+ *mpModel,
+ rAct.GetRect());
+ SetAttributes(pRect);
+ tools::Long nRad=(rAct.GetHorzRound()+rAct.GetVertRound())/2;
+ if (nRad!=0) {
+ SfxItemSetFixed<SDRATTR_CORNER_RADIUS, SDRATTR_CORNER_RADIUS> aSet(*mpLineAttr->GetPool());
+ aSet.Put(SdrMetricItem(SDRATTR_CORNER_RADIUS, nRad));
+ pRect->SetMergedItemSet(aSet);
+ }
+ InsertObj(pRect);
+}
+
+void ImpSdrGDIMetaFileImport::DoAction(MetaEllipseAction const & rAct)
+{
+ SdrCircObj* pCirc=new SdrCircObj(
+ *mpModel,
+ SdrCircKind::Full,
+ rAct.GetRect());
+ SetAttributes(pCirc);
+ InsertObj(pCirc);
+}
+
+void ImpSdrGDIMetaFileImport::DoAction(MetaArcAction const & rAct)
+{
+ Point aCenter(rAct.GetRect().Center());
+ Degree100 nStart=GetAngle(rAct.GetStartPoint()-aCenter);
+ Degree100 nEnd=GetAngle(rAct.GetEndPoint()-aCenter);
+ SdrCircObj* pCirc = new SdrCircObj(
+ *mpModel,
+ SdrCircKind::Arc,
+ rAct.GetRect(),nStart,nEnd);
+ SetAttributes(pCirc);
+ InsertObj(pCirc);
+}
+
+void ImpSdrGDIMetaFileImport::DoAction(MetaPieAction const & rAct)
+{
+ Point aCenter(rAct.GetRect().Center());
+ Degree100 nStart=GetAngle(rAct.GetStartPoint()-aCenter);
+ Degree100 nEnd=GetAngle(rAct.GetEndPoint()-aCenter);
+ SdrCircObj* pCirc = new SdrCircObj(
+ *mpModel,
+ SdrCircKind::Section,
+ rAct.GetRect(),
+ nStart,
+ nEnd);
+ SetAttributes(pCirc);
+ InsertObj(pCirc);
+}
+
+void ImpSdrGDIMetaFileImport::DoAction(MetaChordAction const & rAct)
+{
+ Point aCenter(rAct.GetRect().Center());
+ Degree100 nStart=GetAngle(rAct.GetStartPoint()-aCenter);
+ Degree100 nEnd=GetAngle(rAct.GetEndPoint()-aCenter);
+ SdrCircObj* pCirc = new SdrCircObj(
+ *mpModel,
+ SdrCircKind::Cut,
+ rAct.GetRect(),
+ nStart,
+ nEnd);
+ SetAttributes(pCirc);
+ InsertObj(pCirc);
+}
+
+bool ImpSdrGDIMetaFileImport::CheckLastLineMerge(const basegfx::B2DPolygon& rSrcPoly)
+{
+ // #i102706# Do not merge closed polygons
+ if(rSrcPoly.isClosed())
+ {
+ return false;
+ }
+
+ // #i73407# reformulation to use new B2DPolygon classes
+ if(mbLastObjWasLine && (maOldLineColor == mpVD->GetLineColor()) && rSrcPoly.count())
+ {
+ SdrObject* pTmpObj = !maTmpList.empty() ? maTmpList[maTmpList.size() - 1] : nullptr;
+ SdrPathObj* pLastPoly = dynamic_cast< SdrPathObj* >(pTmpObj);
+
+ if(pLastPoly)
+ {
+ if(1 == pLastPoly->GetPathPoly().count())
+ {
+ bool bOk(false);
+ basegfx::B2DPolygon aDstPoly(pLastPoly->GetPathPoly().getB2DPolygon(0));
+
+ // #i102706# Do not merge closed polygons
+ if(aDstPoly.isClosed())
+ {
+ return false;
+ }
+
+ if(aDstPoly.count())
+ {
+ const sal_uInt32 nMaxDstPnt(aDstPoly.count() - 1);
+ const sal_uInt32 nMaxSrcPnt(rSrcPoly.count() - 1);
+
+ if(aDstPoly.getB2DPoint(nMaxDstPnt) == rSrcPoly.getB2DPoint(0))
+ {
+ aDstPoly.append(rSrcPoly, 1, rSrcPoly.count() - 1);
+ bOk = true;
+ }
+ else if(aDstPoly.getB2DPoint(0) == rSrcPoly.getB2DPoint(nMaxSrcPnt))
+ {
+ basegfx::B2DPolygon aNew(rSrcPoly);
+ aNew.append(aDstPoly, 1, aDstPoly.count() - 1);
+ aDstPoly = aNew;
+ bOk = true;
+ }
+ else if(aDstPoly.getB2DPoint(0) == rSrcPoly.getB2DPoint(0))
+ {
+ aDstPoly.flip();
+ aDstPoly.append(rSrcPoly, 1, rSrcPoly.count() - 1);
+ bOk = true;
+ }
+ else if(aDstPoly.getB2DPoint(nMaxDstPnt) == rSrcPoly.getB2DPoint(nMaxSrcPnt))
+ {
+ basegfx::B2DPolygon aNew(rSrcPoly);
+ aNew.flip();
+ aDstPoly.append(aNew, 1, aNew.count() - 1);
+ bOk = true;
+ }
+ }
+
+ if(bOk)
+ {
+ pLastPoly->NbcSetPathPoly(basegfx::B2DPolyPolygon(aDstPoly));
+ }
+
+ return bOk;
+ }
+ }
+ }
+
+ return false;
+}
+
+bool ImpSdrGDIMetaFileImport::CheckLastPolyLineAndFillMerge(const basegfx::B2DPolyPolygon & rPolyPolygon)
+{
+ // #i73407# reformulation to use new B2DPolygon classes
+ if(mbLastObjWasPolyWithoutLine)
+ {
+ SdrObject* pTmpObj = !maTmpList.empty() ? maTmpList[maTmpList.size() - 1] : nullptr;
+ SdrPathObj* pLastPoly = dynamic_cast< SdrPathObj* >(pTmpObj);
+
+ if(pLastPoly)
+ {
+ if(pLastPoly->GetPathPoly() == rPolyPolygon)
+ {
+ SetAttributes(nullptr);
+
+ if(!mbNoLine && mbNoFill)
+ {
+ pLastPoly->SetMergedItemSet(*mpLineAttr);
+
+ return true;
+ }
+ }
+ }
+ }
+
+ return false;
+}
+
+void ImpSdrGDIMetaFileImport::checkClip()
+{
+ if(!mpVD->IsClipRegion())
+ return;
+
+ maClip = mpVD->GetClipRegion().GetAsB2DPolyPolygon();
+
+ if(isClip())
+ {
+ const basegfx::B2DHomMatrix aTransform(
+ basegfx::utils::createScaleTranslateB2DHomMatrix(
+ mfScaleX,
+ mfScaleY,
+ maOfs.X(),
+ maOfs.Y()));
+
+ maClip.transform(aTransform);
+ }
+}
+
+bool ImpSdrGDIMetaFileImport::isClip() const
+{
+ return !maClip.getB2DRange().isEmpty();
+}
+
+void ImpSdrGDIMetaFileImport::DoAction( MetaPolyLineAction const & rAct )
+{
+ // #i73407# reformulation to use new B2DPolygon classes
+ basegfx::B2DPolygon aSource(rAct.GetPolygon().getB2DPolygon());
+
+ if(aSource.count())
+ {
+ const basegfx::B2DHomMatrix aTransform(basegfx::utils::createScaleTranslateB2DHomMatrix(mfScaleX, mfScaleY, maOfs.X(), maOfs.Y()));
+ aSource.transform(aTransform);
+ }
+
+ const LineInfo& rLineInfo = rAct.GetLineInfo();
+ const sal_Int32 nNewLineWidth(rLineInfo.GetWidth());
+ bool bCreateLineObject(true);
+
+ if(mbLastObjWasLine && (nNewLineWidth == mnLineWidth) && CheckLastLineMerge(aSource))
+ {
+ bCreateLineObject = false;
+ }
+ else if(mbLastObjWasPolyWithoutLine && CheckLastPolyLineAndFillMerge(basegfx::B2DPolyPolygon(aSource)))
+ {
+ bCreateLineObject = false;
+ }
+
+ if(!bCreateLineObject)
+ return;
+
+ SdrPathObj* pPath = new SdrPathObj(
+ *mpModel,
+ aSource.isClosed() ? SdrObjKind::Polygon : SdrObjKind::PolyLine,
+ basegfx::B2DPolyPolygon(aSource));
+ mnLineWidth = nNewLineWidth;
+ maLineJoin = rLineInfo.GetLineJoin();
+ maLineCap = rLineInfo.GetLineCap();
+ maDash = XDash(css::drawing::DashStyle_RECT,
+ rLineInfo.GetDotCount(), rLineInfo.GetDotLen(),
+ rLineInfo.GetDashCount(), rLineInfo.GetDashLen(),
+ rLineInfo.GetDistance());
+ SetAttributes(pPath);
+ mnLineWidth = 0;
+ maLineJoin = basegfx::B2DLineJoin::NONE;
+ maDash = XDash();
+ InsertObj(pPath, false);
+}
+
+void ImpSdrGDIMetaFileImport::DoAction( MetaPolygonAction const & rAct )
+{
+ // #i73407# reformulation to use new B2DPolygon classes
+ basegfx::B2DPolygon aSource(rAct.GetPolygon().getB2DPolygon());
+
+ if(!aSource.count())
+ return;
+
+ const basegfx::B2DHomMatrix aTransform(basegfx::utils::createScaleTranslateB2DHomMatrix(mfScaleX, mfScaleY, maOfs.X(), maOfs.Y()));
+ aSource.transform(aTransform);
+
+ if(!mbLastObjWasPolyWithoutLine || !CheckLastPolyLineAndFillMerge(basegfx::B2DPolyPolygon(aSource)))
+ {
+ // #i73407# make sure polygon is closed, it's a filled primitive
+ aSource.setClosed(true);
+ SdrPathObj* pPath = new SdrPathObj(
+ *mpModel,
+ SdrObjKind::Polygon,
+ basegfx::B2DPolyPolygon(aSource));
+ SetAttributes(pPath);
+ InsertObj(pPath, false);
+ }
+}
+
+void ImpSdrGDIMetaFileImport::DoAction(MetaPolyPolygonAction const & rAct)
+{
+ // #i73407# reformulation to use new B2DPolygon classes
+ basegfx::B2DPolyPolygon aSource(rAct.GetPolyPolygon().getB2DPolyPolygon());
+
+ if(!aSource.count())
+ return;
+
+ const basegfx::B2DHomMatrix aTransform(basegfx::utils::createScaleTranslateB2DHomMatrix(mfScaleX, mfScaleY, maOfs.X(), maOfs.Y()));
+ aSource.transform(aTransform);
+
+ if(!mbLastObjWasPolyWithoutLine || !CheckLastPolyLineAndFillMerge(aSource))
+ {
+ // #i73407# make sure polygon is closed, it's a filled primitive
+ aSource.setClosed(true);
+ SdrPathObj* pPath = new SdrPathObj(
+ *mpModel,
+ SdrObjKind::Polygon,
+ aSource);
+ SetAttributes(pPath);
+ InsertObj(pPath, false);
+ }
+}
+
+void ImpSdrGDIMetaFileImport::ImportText( const Point& rPos, const OUString& rStr, const MetaAction& rAct )
+{
+ // calc text box size, add 5% to make it fit safely
+
+ FontMetric aFontMetric( mpVD->GetFontMetric() );
+ vcl::Font aFnt( mpVD->GetFont() );
+ TextAlign eAlg( aFnt.GetAlignment() );
+
+ sal_Int32 nTextWidth = static_cast<sal_Int32>( mpVD->GetTextWidth( rStr ) * mfScaleX );
+ sal_Int32 nTextHeight = static_cast<sal_Int32>( mpVD->GetTextHeight() * mfScaleY );
+
+ Point aPos( FRound(rPos.X() * mfScaleX + maOfs.X()), FRound(rPos.Y() * mfScaleY + maOfs.Y()) );
+ Size aSize( nTextWidth, nTextHeight );
+
+ if ( eAlg == ALIGN_BASELINE )
+ aPos.AdjustY( -(FRound(aFontMetric.GetAscent() * mfScaleY)) );
+ else if ( eAlg == ALIGN_BOTTOM )
+ aPos.AdjustY( -nTextHeight );
+
+ tools::Rectangle aTextRect( aPos, aSize );
+ SdrRectObj* pText = new SdrRectObj(
+ *mpModel,
+ SdrObjKind::Text,
+ aTextRect);
+
+ pText->SetMergedItem ( makeSdrTextUpperDistItem (0));
+ pText->SetMergedItem ( makeSdrTextLowerDistItem (0));
+ pText->SetMergedItem ( makeSdrTextRightDistItem (0));
+ pText->SetMergedItem ( makeSdrTextLeftDistItem (0));
+
+ if ( aFnt.GetAverageFontWidth() || ( rAct.GetType() == MetaActionType::STRETCHTEXT ) )
+ {
+ pText->ClearMergedItem( SDRATTR_TEXT_AUTOGROWWIDTH );
+ pText->SetMergedItem( makeSdrTextAutoGrowHeightItem( false ) );
+ // don't let the margins eat the space needed for the text
+ pText->SetMergedItem( SdrTextFitToSizeTypeItem(drawing::TextFitToSizeType_ALLLINES) );
+ }
+ else
+ {
+ pText->SetMergedItem( makeSdrTextAutoGrowWidthItem( true ) );
+ }
+
+ pText->SetLayer(mnLayer);
+ pText->NbcSetText( rStr );
+ SetAttributes( pText, true );
+ pText->SetSnapRect( aTextRect );
+
+ if (!aFnt.IsTransparent())
+ {
+ SfxItemSetFixed<XATTR_FILL_FIRST, XATTR_FILL_LAST> aAttr(*mpFillAttr->GetPool());
+ aAttr.Put(XFillStyleItem(drawing::FillStyle_SOLID));
+ aAttr.Put(XFillColorItem(OUString(), aFnt.GetFillColor()));
+ pText->SetMergedItemSet(aAttr);
+ }
+ Degree100 nAngle = to<Degree100>(aFnt.GetOrientation());
+ if ( nAngle )
+ pText->SdrAttrObj::NbcRotate(aPos,nAngle);
+ InsertObj( pText, false );
+}
+
+void ImpSdrGDIMetaFileImport::DoAction(MetaTextAction const & rAct)
+{
+ OUString aStr(rAct.GetText());
+ aStr = aStr.copy(rAct.GetIndex(), rAct.GetLen());
+ ImportText( rAct.GetPoint(), aStr, rAct );
+}
+
+void ImpSdrGDIMetaFileImport::DoAction(MetaTextArrayAction const & rAct)
+{
+ OUString aStr(rAct.GetText());
+ aStr = aStr.copy(rAct.GetIndex(), rAct.GetLen());
+ ImportText( rAct.GetPoint(), aStr, rAct );
+}
+
+void ImpSdrGDIMetaFileImport::DoAction(MetaStretchTextAction const & rAct)
+{
+ OUString aStr(rAct.GetText());
+ aStr = aStr.copy(rAct.GetIndex(), rAct.GetLen());
+ ImportText( rAct.GetPoint(), aStr, rAct );
+}
+
+void ImpSdrGDIMetaFileImport::DoAction(MetaBmpAction const & rAct)
+{
+ tools::Rectangle aRect(rAct.GetPoint(),rAct.GetBitmap().GetSizePixel());
+ aRect.AdjustRight( 1 ); aRect.AdjustBottom( 1 );
+ SdrGrafObj* pGraf = new SdrGrafObj(
+ *mpModel,
+ Graphic(BitmapEx(rAct.GetBitmap())),
+ aRect);
+
+ // This action is not creating line and fill, set directly, do not use SetAttributes(..)
+ pGraf->SetMergedItem(XLineStyleItem(drawing::LineStyle_NONE));
+ pGraf->SetMergedItem(XFillStyleItem(drawing::FillStyle_NONE));
+ InsertObj(pGraf);
+}
+
+void ImpSdrGDIMetaFileImport::DoAction(MetaBmpScaleAction const & rAct)
+{
+ tools::Rectangle aRect(rAct.GetPoint(),rAct.GetSize());
+ aRect.AdjustRight( 1 ); aRect.AdjustBottom( 1 );
+ SdrGrafObj* pGraf = new SdrGrafObj(
+ *mpModel,
+ Graphic(BitmapEx(rAct.GetBitmap())),
+ aRect);
+
+ // This action is not creating line and fill, set directly, do not use SetAttributes(..)
+ pGraf->SetMergedItem(XLineStyleItem(drawing::LineStyle_NONE));
+ pGraf->SetMergedItem(XFillStyleItem(drawing::FillStyle_NONE));
+ InsertObj(pGraf);
+}
+
+void ImpSdrGDIMetaFileImport::DoAction(MetaBmpExAction const & rAct)
+{
+ tools::Rectangle aRect(rAct.GetPoint(),rAct.GetBitmapEx().GetSizePixel());
+ aRect.AdjustRight( 1 ); aRect.AdjustBottom( 1 );
+ SdrGrafObj* pGraf = new SdrGrafObj(
+ *mpModel,
+ rAct.GetBitmapEx(),
+ aRect);
+
+ // This action is not creating line and fill, set directly, do not use SetAttributes(..)
+ pGraf->SetMergedItem(XLineStyleItem(drawing::LineStyle_NONE));
+ pGraf->SetMergedItem(XFillStyleItem(drawing::FillStyle_NONE));
+ InsertObj(pGraf);
+}
+
+void ImpSdrGDIMetaFileImport::DoAction(MetaBmpExScaleAction const & rAct)
+{
+ tools::Rectangle aRect(rAct.GetPoint(),rAct.GetSize());
+ aRect.AdjustRight( 1 ); aRect.AdjustBottom( 1 );
+ SdrGrafObj* pGraf = new SdrGrafObj(
+ *mpModel,
+ rAct.GetBitmapEx(),
+ aRect);
+
+ // This action is not creating line and fill, set directly, do not use SetAttributes(..)
+ pGraf->SetMergedItem(XLineStyleItem(drawing::LineStyle_NONE));
+ pGraf->SetMergedItem(XFillStyleItem(drawing::FillStyle_NONE));
+ InsertObj(pGraf);
+}
+
+
+void ImpSdrGDIMetaFileImport::DoAction( MetaHatchAction const & rAct )
+{
+ // #i73407# reformulation to use new B2DPolygon classes
+ basegfx::B2DPolyPolygon aSource(rAct.GetPolyPolygon().getB2DPolyPolygon());
+
+ if(!aSource.count())
+ return;
+
+ const basegfx::B2DHomMatrix aTransform(basegfx::utils::createScaleTranslateB2DHomMatrix(mfScaleX, mfScaleY, maOfs.X(), maOfs.Y()));
+ aSource.transform(aTransform);
+
+ if(mbLastObjWasPolyWithoutLine && CheckLastPolyLineAndFillMerge(aSource))
+ return;
+
+ const Hatch& rHatch = rAct.GetHatch();
+ SdrPathObj* pPath = new SdrPathObj(
+ *mpModel,
+ SdrObjKind::Polygon,
+ aSource);
+ // #i125211# Use the ranges from the SdrObject to create a new empty SfxItemSet
+ SfxItemSet aHatchAttr(mpModel->GetItemPool(), pPath->GetMergedItemSet().GetRanges());
+ css::drawing::HatchStyle eStyle;
+
+ switch(rHatch.GetStyle())
+ {
+ case HatchStyle::Triple :
+ {
+ eStyle = css::drawing::HatchStyle_TRIPLE;
+ break;
+ }
+
+ case HatchStyle::Double :
+ {
+ eStyle = css::drawing::HatchStyle_DOUBLE;
+ break;
+ }
+
+ default:
+ {
+ eStyle = css::drawing::HatchStyle_SINGLE;
+ break;
+ }
+ }
+
+ SetAttributes(pPath);
+ aHatchAttr.Put(XFillStyleItem(drawing::FillStyle_HATCH));
+ aHatchAttr.Put(XFillHatchItem(XHatch(rHatch.GetColor(), eStyle, rHatch.GetDistance(), rHatch.GetAngle())));
+ pPath->SetMergedItemSet(aHatchAttr);
+
+ InsertObj(pPath, false);
+}
+
+
+void ImpSdrGDIMetaFileImport::DoAction(MetaLineColorAction& rAct)
+{
+ rAct.Execute(mpVD);
+}
+
+void ImpSdrGDIMetaFileImport::DoAction(MetaMapModeAction& rAct)
+{
+ MapScaling();
+ rAct.Execute(mpVD);
+ mbLastObjWasPolyWithoutLine = false;
+ mbLastObjWasLine = false;
+}
+
+void ImpSdrGDIMetaFileImport::MapScaling()
+{
+ const size_t nCount(maTmpList.size());
+ const MapMode& rMap = mpVD->GetMapMode();
+ Point aMapOrg( rMap.GetOrigin() );
+ bool bMov2(aMapOrg.X() != 0 || aMapOrg.Y() != 0);
+
+ if(bMov2)
+ {
+ for(size_t i = mnMapScalingOfs; i < nCount; i++)
+ {
+ SdrObject* pObj = maTmpList[i];
+
+ pObj->NbcMove(Size(aMapOrg.X(), aMapOrg.Y()));
+ }
+ }
+
+ mnMapScalingOfs = nCount;
+}
+
+
+void ImpSdrGDIMetaFileImport::DoAction( MetaCommentAction const & rAct, GDIMetaFile const & rMtf, sal_uLong& a) // GDIMetaFile* pMtf )
+{
+ bool aSkipComment = false;
+
+ if (a < rMtf.GetActionSize() && rAct.GetComment().equalsIgnoreAsciiCase("XGRAD_SEQ_BEGIN"))
+ {
+ // #i125211# Check if next action is a MetaGradientExAction
+ MetaGradientExAction* pAct = dynamic_cast< MetaGradientExAction* >(rMtf.GetAction(a + 1));
+
+ if( pAct && pAct->GetType() == MetaActionType::GRADIENTEX )
+ {
+ // #i73407# reformulation to use new B2DPolygon classes
+ basegfx::B2DPolyPolygon aSource(pAct->GetPolyPolygon().getB2DPolyPolygon());
+
+ if(aSource.count())
+ {
+ if(!mbLastObjWasPolyWithoutLine || !CheckLastPolyLineAndFillMerge(aSource))
+ {
+ const Gradient& rGrad = pAct->GetGradient();
+ SdrPathObj* pPath = new SdrPathObj(
+ *mpModel,
+ SdrObjKind::Polygon,
+ aSource);
+ // #i125211# Use the ranges from the SdrObject to create a new empty SfxItemSet
+ SfxItemSet aGradAttr(mpModel->GetItemPool(), pPath->GetMergedItemSet().GetRanges());
+ XGradient aXGradient;
+
+ aXGradient.SetGradientStyle(static_cast<css::awt::GradientStyle>(rGrad.GetStyle()));
+ aXGradient.SetStartColor(rGrad.GetStartColor());
+ aXGradient.SetEndColor(rGrad.GetEndColor());
+ aXGradient.SetAngle(rGrad.GetAngle());
+ aXGradient.SetBorder(rGrad.GetBorder());
+ aXGradient.SetXOffset(rGrad.GetOfsX());
+ aXGradient.SetYOffset(rGrad.GetOfsY());
+ aXGradient.SetStartIntens(rGrad.GetStartIntensity());
+ aXGradient.SetEndIntens(rGrad.GetEndIntensity());
+ aXGradient.SetSteps(rGrad.GetSteps());
+
+ // no need to use SetAttributes(..) here since line and fill style
+ // need to be set individually
+ // SetAttributes(pPath);
+
+ // switch line off; if there was one there will be a
+ // MetaActionType::POLYLINE following creating another object
+ aGradAttr.Put(XLineStyleItem(drawing::LineStyle_NONE));
+
+ // add detected gradient fillstyle
+ aGradAttr.Put(XFillStyleItem(drawing::FillStyle_GRADIENT));
+ aGradAttr.Put(XFillGradientItem(aXGradient));
+
+ pPath->SetMergedItemSet(aGradAttr);
+
+ InsertObj(pPath);
+ }
+ }
+
+ aSkipComment = true;
+ }
+ }
+
+ if(aSkipComment)
+ {
+ // #i125211# forward until closing MetaCommentAction
+ MetaAction* pSkipAct = rMtf.GetAction(++a);
+
+ while( pSkipAct
+ && ((pSkipAct->GetType() != MetaActionType::COMMENT )
+ || !(static_cast<MetaCommentAction*>(pSkipAct)->GetComment().equalsIgnoreAsciiCase("XGRAD_SEQ_END"))))
+ {
+ pSkipAct = rMtf.GetAction(++a);
+ }
+ }
+}
+
+void ImpSdrGDIMetaFileImport::DoAction(MetaTextRectAction const & rAct)
+{
+ GDIMetaFile aTemp;
+
+ mpVD->AddTextRectActions(rAct.GetRect(), rAct.GetText(), rAct.GetStyle(), aTemp);
+ DoLoopActions(aTemp, nullptr, nullptr);
+}
+
+void ImpSdrGDIMetaFileImport::DoAction(MetaBmpScalePartAction const & rAct)
+{
+ tools::Rectangle aRect(rAct.GetDestPoint(), rAct.GetDestSize());
+ BitmapEx aBitmapEx(rAct.GetBitmap());
+
+ aRect.AdjustRight( 1 );
+ aRect.AdjustBottom( 1 );
+ aBitmapEx.Crop(tools::Rectangle(rAct.GetSrcPoint(), rAct.GetSrcSize()));
+ SdrGrafObj* pGraf = new SdrGrafObj(
+ *mpModel,
+ aBitmapEx,
+ aRect);
+
+ // This action is not creating line and fill, set directly, do not use SetAttributes(..)
+ pGraf->SetMergedItem(XLineStyleItem(drawing::LineStyle_NONE));
+ pGraf->SetMergedItem(XFillStyleItem(drawing::FillStyle_NONE));
+ InsertObj(pGraf);
+}
+
+void ImpSdrGDIMetaFileImport::DoAction(MetaBmpExScalePartAction const & rAct)
+{
+ tools::Rectangle aRect(rAct.GetDestPoint(),rAct.GetDestSize());
+ BitmapEx aBitmapEx(rAct.GetBitmapEx());
+
+ aRect.AdjustRight( 1 );
+ aRect.AdjustBottom( 1 );
+ aBitmapEx.Crop(tools::Rectangle(rAct.GetSrcPoint(), rAct.GetSrcSize()));
+ SdrGrafObj* pGraf = new SdrGrafObj(
+ *mpModel,
+ aBitmapEx,
+ aRect);
+
+ // This action is not creating line and fill, set directly, do not use SetAttributes(..)
+ pGraf->SetMergedItem(XLineStyleItem(drawing::LineStyle_NONE));
+ pGraf->SetMergedItem(XFillStyleItem(drawing::FillStyle_NONE));
+ InsertObj(pGraf);
+}
+
+void ImpSdrGDIMetaFileImport::DoAction(MetaMaskAction const & rAct)
+{
+ tools::Rectangle aRect(rAct.GetPoint(), rAct.GetBitmap().GetSizePixel());
+ BitmapEx aBitmapEx(rAct.GetBitmap(), rAct.GetColor());
+
+ aRect.AdjustRight( 1 ); aRect.AdjustBottom( 1 );
+ SdrGrafObj* pGraf = new SdrGrafObj(
+ *mpModel,
+ aBitmapEx,
+ aRect);
+
+ // This action is not creating line and fill, set directly, do not use SetAttributes(..)
+ pGraf->SetMergedItem(XLineStyleItem(drawing::LineStyle_NONE));
+ pGraf->SetMergedItem(XFillStyleItem(drawing::FillStyle_NONE));
+ InsertObj(pGraf);
+}
+
+void ImpSdrGDIMetaFileImport::DoAction(MetaMaskScaleAction const & rAct)
+{
+ tools::Rectangle aRect(rAct.GetPoint(), rAct.GetSize());
+ BitmapEx aBitmapEx(rAct.GetBitmap(), rAct.GetColor());
+
+ aRect.AdjustRight( 1 ); aRect.AdjustBottom( 1 );
+ SdrGrafObj* pGraf = new SdrGrafObj(
+ *mpModel,
+ aBitmapEx,
+ aRect);
+
+ // This action is not creating line and fill, set directly, do not use SetAttributes(..)
+ pGraf->SetMergedItem(XLineStyleItem(drawing::LineStyle_NONE));
+ pGraf->SetMergedItem(XFillStyleItem(drawing::FillStyle_NONE));
+ InsertObj(pGraf);
+}
+
+void ImpSdrGDIMetaFileImport::DoAction(MetaMaskScalePartAction const & rAct)
+{
+ tools::Rectangle aRect(rAct.GetDestPoint(), rAct.GetDestSize());
+ BitmapEx aBitmapEx(rAct.GetBitmap(), rAct.GetColor());
+
+ aRect.AdjustRight( 1 ); aRect.AdjustBottom( 1 );
+ aBitmapEx.Crop(tools::Rectangle(rAct.GetSrcPoint(), rAct.GetSrcSize()));
+ SdrGrafObj* pGraf = new SdrGrafObj(
+ *mpModel,
+ aBitmapEx,
+ aRect);
+
+ // This action is not creating line and fill, set directly, do not use SetAttributes(..)
+ pGraf->SetMergedItem(XLineStyleItem(drawing::LineStyle_NONE));
+ pGraf->SetMergedItem(XFillStyleItem(drawing::FillStyle_NONE));
+ InsertObj(pGraf);
+}
+
+namespace
+{
+ css::awt::GradientStyle getXGradientStyleFromGradientStyle(const GradientStyle& rGradientStyle)
+ {
+ css::awt::GradientStyle aXGradientStyle(css::awt::GradientStyle_LINEAR);
+
+ switch(rGradientStyle)
+ {
+ case GradientStyle::Linear: aXGradientStyle = css::awt::GradientStyle_LINEAR; break;
+ case GradientStyle::Axial: aXGradientStyle = css::awt::GradientStyle_AXIAL; break;
+ case GradientStyle::Radial: aXGradientStyle = css::awt::GradientStyle_RADIAL; break;
+ case GradientStyle::Elliptical: aXGradientStyle = css::awt::GradientStyle_ELLIPTICAL; break;
+ case GradientStyle::Square: aXGradientStyle = css::awt::GradientStyle_SQUARE; break;
+ case GradientStyle::Rect: aXGradientStyle = css::awt::GradientStyle_RECT; break;
+
+ // Needed due to GradientStyle::FORCE_EQUAL_SIZE; this again is needed
+ // to force the enum defines in VCL to a defined size for the compilers,
+ // so despite it is never used it cannot be removed (would break the
+ // API implementation probably).
+ case GradientStyle::FORCE_EQUAL_SIZE: break;
+ default:
+ break;
+ }
+
+ return aXGradientStyle;
+ }
+}
+
+void ImpSdrGDIMetaFileImport::DoAction(MetaGradientAction const & rAct)
+{
+ basegfx::B2DRange aRange = vcl::unotools::b2DRectangleFromRectangle(rAct.GetRect());
+
+ if(aRange.isEmpty())
+ return;
+
+ const basegfx::B2DHomMatrix aTransform(basegfx::utils::createScaleTranslateB2DHomMatrix(mfScaleX, mfScaleY, maOfs.X(), maOfs.Y()));
+ aRange.transform(aTransform);
+ const Gradient& rGradient = rAct.GetGradient();
+ SdrRectObj* pRect = new SdrRectObj(
+ *mpModel,
+ tools::Rectangle(
+ floor(aRange.getMinX()),
+ floor(aRange.getMinY()),
+ ceil(aRange.getMaxX()),
+ ceil(aRange.getMaxY())));
+ // #i125211# Use the ranges from the SdrObject to create a new empty SfxItemSet
+ SfxItemSet aGradientAttr(mpModel->GetItemPool(), pRect->GetMergedItemSet().GetRanges());
+ const css::awt::GradientStyle aXGradientStyle(getXGradientStyleFromGradientStyle(rGradient.GetStyle()));
+ const XFillGradientItem aXFillGradientItem(
+ XGradient(
+ rGradient.GetStartColor(),
+ rGradient.GetEndColor(),
+ aXGradientStyle,
+ rGradient.GetAngle(),
+ rGradient.GetOfsX(),
+ rGradient.GetOfsY(),
+ rGradient.GetBorder(),
+ rGradient.GetStartIntensity(),
+ rGradient.GetEndIntensity(),
+ rGradient.GetSteps()));
+
+ SetAttributes(pRect);
+ aGradientAttr.Put(XFillStyleItem(drawing::FillStyle_GRADIENT)); // #i125211#
+ aGradientAttr.Put(aXFillGradientItem);
+ pRect->SetMergedItemSet(aGradientAttr);
+
+ InsertObj(pRect, false);
+}
+
+void ImpSdrGDIMetaFileImport::DoAction(MetaTransparentAction const & rAct)
+{
+ basegfx::B2DPolyPolygon aSource(rAct.GetPolyPolygon().getB2DPolyPolygon());
+
+ if(!aSource.count())
+ return;
+
+ const basegfx::B2DHomMatrix aTransform(basegfx::utils::createScaleTranslateB2DHomMatrix(mfScaleX, mfScaleY, maOfs.X(), maOfs.Y()));
+ aSource.transform(aTransform);
+ aSource.setClosed(true);
+
+ SdrPathObj* pPath = new SdrPathObj(
+ *mpModel,
+ SdrObjKind::Polygon,
+ aSource);
+ SetAttributes(pPath);
+ pPath->SetMergedItem(XFillTransparenceItem(rAct.GetTransparence()));
+ InsertObj(pPath, false);
+}
+
+void ImpSdrGDIMetaFileImport::DoAction(MetaGradientExAction const & rAct)
+{
+ basegfx::B2DPolyPolygon aSource(rAct.GetPolyPolygon().getB2DPolyPolygon());
+
+ if(!aSource.count())
+ return;
+
+ const basegfx::B2DHomMatrix aTransform(basegfx::utils::createScaleTranslateB2DHomMatrix(mfScaleX, mfScaleY, maOfs.X(), maOfs.Y()));
+ aSource.transform(aTransform);
+
+ if(mbLastObjWasPolyWithoutLine && CheckLastPolyLineAndFillMerge(aSource))
+ return;
+
+ const Gradient& rGradient = rAct.GetGradient();
+ SdrPathObj* pPath = new SdrPathObj(
+ *mpModel,
+ SdrObjKind::Polygon,
+ aSource);
+ // #i125211# Use the ranges from the SdrObject to create a new empty SfxItemSet
+ SfxItemSet aGradientAttr(mpModel->GetItemPool(), pPath->GetMergedItemSet().GetRanges());
+ const css::awt::GradientStyle aXGradientStyle(getXGradientStyleFromGradientStyle(rGradient.GetStyle()));
+ const XFillGradientItem aXFillGradientItem(
+ XGradient(
+ rGradient.GetStartColor(),
+ rGradient.GetEndColor(),
+ aXGradientStyle,
+ rGradient.GetAngle(),
+ rGradient.GetOfsX(),
+ rGradient.GetOfsY(),
+ rGradient.GetBorder(),
+ rGradient.GetStartIntensity(),
+ rGradient.GetEndIntensity(),
+ rGradient.GetSteps()));
+
+ SetAttributes(pPath);
+ aGradientAttr.Put(XFillStyleItem(drawing::FillStyle_GRADIENT)); // #i125211#
+ aGradientAttr.Put(aXFillGradientItem);
+ pPath->SetMergedItemSet(aGradientAttr);
+
+ InsertObj(pPath, false);
+}
+
+void ImpSdrGDIMetaFileImport::DoAction(MetaFloatTransparentAction const & rAct)
+{
+ const GDIMetaFile& rMtf = rAct.GetGDIMetaFile();
+
+ if(!rMtf.GetActionSize())
+ return;
+
+ const tools::Rectangle aRect(rAct.GetPoint(),rAct.GetSize());
+
+ // convert metafile sub-content to BitmapEx
+ BitmapEx aBitmapEx(
+ convertMetafileToBitmapEx(
+ rMtf,
+ vcl::unotools::b2DRectangleFromRectangle(aRect),
+ 125000));
+
+ // handle colors
+ const Gradient& rGradient = rAct.GetGradient();
+ basegfx::BColor aStart(rGradient.GetStartColor().getBColor());
+ basegfx::BColor aEnd(rGradient.GetEndColor().getBColor());
+
+ if(100 != rGradient.GetStartIntensity())
+ {
+ aStart *= static_cast<double>(rGradient.GetStartIntensity()) / 100.0;
+ }
+
+ if(100 != rGradient.GetEndIntensity())
+ {
+ aEnd *= static_cast<double>(rGradient.GetEndIntensity()) / 100.0;
+ }
+
+ const bool bEqualColors(aStart == aEnd);
+ const bool bNoSteps(1 == rGradient.GetSteps());
+ bool bCreateObject(true);
+ bool bHasNewMask(false);
+ AlphaMask aNewMask;
+ double fTransparence(0.0);
+ bool bFixedTransparence(false);
+
+ if(bEqualColors || bNoSteps)
+ {
+ // single transparence
+ const basegfx::BColor aMedium(basegfx::average(aStart, aEnd));
+ fTransparence = aMedium.luminance();
+
+ if(basegfx::fTools::lessOrEqual(fTransparence, 0.0))
+ {
+ // no transparence needed, all done
+ }
+ else if(basegfx::fTools::moreOrEqual(fTransparence, 1.0))
+ {
+ // all transparent, no object
+ bCreateObject = false;
+ }
+ else
+ {
+ // 0.0 < transparence < 1.0, apply fixed transparence
+ bFixedTransparence = true;
+ }
+ }
+ else
+ {
+ // gradient transparence
+ ScopedVclPtrInstance< VirtualDevice > pVDev;
+
+ pVDev->SetOutputSizePixel(aBitmapEx.GetBitmap().GetSizePixel());
+ pVDev->DrawGradient(tools::Rectangle(Point(0, 0), pVDev->GetOutputSizePixel()), rGradient);
+
+ aNewMask = AlphaMask(pVDev->GetBitmap(Point(0, 0), pVDev->GetOutputSizePixel()));
+ bHasNewMask = true;
+ }
+
+ if(!bCreateObject)
+ return;
+
+ if(bHasNewMask || bFixedTransparence)
+ {
+ if(!aBitmapEx.IsAlpha())
+ {
+ // no transparence yet, apply new one
+ if(bFixedTransparence)
+ {
+ sal_uInt8 aAlpha(basegfx::fround(fTransparence * 255.0));
+
+ aNewMask = AlphaMask(aBitmapEx.GetBitmap().GetSizePixel(), &aAlpha);
+ }
+
+ aBitmapEx = BitmapEx(aBitmapEx.GetBitmap(), aNewMask);
+ }
+ else
+ {
+ vcl::bitmap::DrawAlphaBitmapAndAlphaGradient(aBitmapEx, bFixedTransparence, fTransparence, aNewMask);
+ }
+ }
+
+ // create and add object
+ SdrGrafObj* pGraf = new SdrGrafObj(
+ *mpModel,
+ aBitmapEx,
+ aRect);
+
+ // for MetaFloatTransparentAction, do not use SetAttributes(...)
+ // since these metafile content is not used to draw line/fill
+ // dependent of these setting at the device content
+ pGraf->SetMergedItem(XLineStyleItem(drawing::LineStyle_NONE));
+ pGraf->SetMergedItem(XFillStyleItem(drawing::FillStyle_NONE));
+ InsertObj(pGraf);
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/svdraw/svdfmtf.hxx b/svx/source/svdraw/svdfmtf.hxx
new file mode 100644
index 000000000..0e788bcd5
--- /dev/null
+++ b/svx/source/svdraw/svdfmtf.hxx
@@ -0,0 +1,173 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#ifndef INCLUDED_SVX_SOURCE_SVDRAW_SVDFMTF_HXX
+#define INCLUDED_SVX_SOURCE_SVDRAW_SVDFMTF_HXX
+
+#include <sal/config.h>
+
+#include <memory>
+
+#include <svl/itemset.hxx>
+#include <tools/fract.hxx>
+#include <vcl/metaact.hxx>
+#include <vcl/virdev.hxx>
+#include <svx/svdobj.hxx>
+#include <svx/xdash.hxx>
+
+// Forward Declarations
+
+
+class SfxItemSet;
+class SdrObjList;
+class SdrModel;
+class SdrPage;
+class SdrObject;
+class SvdProgressInfo;
+
+
+// Helper Class ImpSdrGDIMetaFileImport
+class ImpSdrGDIMetaFileImport final
+{
+ ::std::vector< SdrObject* > maTmpList;
+ ScopedVclPtr<VirtualDevice> mpVD;
+ tools::Rectangle maScaleRect;
+ size_t mnMapScalingOfs; // from here on, not edited with MapScaling
+ std::unique_ptr<SfxItemSet> mpLineAttr;
+ std::unique_ptr<SfxItemSet> mpFillAttr;
+ std::unique_ptr<SfxItemSet> mpTextAttr;
+ SdrModel* mpModel;
+ SdrLayerID mnLayer;
+ Color maOldLineColor;
+ sal_Int32 mnLineWidth;
+ basegfx::B2DLineJoin maLineJoin;
+ css::drawing::LineCap maLineCap;
+ XDash maDash;
+
+ bool mbMov;
+ bool mbSize;
+ Point maOfs;
+ double mfScaleX;
+ double mfScaleY;
+ Fraction maScaleX;
+ Fraction maScaleY;
+
+ bool mbFntDirty;
+
+ // to optimize (PenNULL,Brush,DrawPoly),(Pen,BrushNULL,DrawPoly) -> two-in-one
+ bool mbLastObjWasPolyWithoutLine;
+ bool mbNoLine;
+ bool mbNoFill;
+
+ // to optimize multiple lines into a Polyline
+ bool mbLastObjWasLine;
+
+ // clipregion
+ basegfx::B2DPolyPolygon maClip;
+
+ // check for clip and evtl. fill maClip
+ void checkClip();
+ bool isClip() const;
+
+ // actions
+ void DoAction(MetaLineAction const & rAct);
+ void DoAction(MetaRectAction const & rAct);
+ void DoAction(MetaRoundRectAction const & rAct);
+ void DoAction(MetaEllipseAction const & rAct);
+ void DoAction(MetaArcAction const & rAct);
+ void DoAction(MetaPieAction const & rAct);
+ void DoAction(MetaChordAction const & rAct);
+ void DoAction(MetaPolyLineAction const & rAct);
+ void DoAction(MetaPolygonAction const & rAct);
+ void DoAction(MetaPolyPolygonAction const & rAct);
+ void DoAction(MetaTextAction const & rAct);
+ void DoAction(MetaTextArrayAction const & rAct);
+ void DoAction(MetaStretchTextAction const & rAct);
+ void DoAction(MetaBmpAction const & rAct);
+ void DoAction(MetaBmpScaleAction const & rAct);
+ void DoAction(MetaBmpExAction const & rAct);
+ void DoAction(MetaBmpExScaleAction const & rAct);
+ void DoAction(MetaHatchAction const & rAct);
+ void DoAction(MetaLineColorAction & rAct);
+ void DoAction(MetaMapModeAction & rAct);
+ void DoAction(MetaFillColorAction & rAct) { rAct.Execute(mpVD); }
+ void DoAction(MetaTextColorAction & rAct) { rAct.Execute(mpVD); }
+ void DoAction(MetaTextFillColorAction & rAct) { rAct.Execute(mpVD); }
+ void DoAction(MetaFontAction & rAct) { rAct.Execute(mpVD); mbFntDirty = true; }
+ void DoAction(MetaTextAlignAction & rAct) { rAct.Execute(mpVD); mbFntDirty = true; }
+ void DoAction(MetaClipRegionAction & rAct) { rAct.Execute(mpVD); checkClip(); }
+ void DoAction(MetaRasterOpAction & rAct) { rAct.Execute(mpVD); }
+ void DoAction(MetaPushAction & rAct) { rAct.Execute(mpVD); checkClip(); }
+ void DoAction(MetaPopAction & rAct) { rAct.Execute(mpVD); mbFntDirty = true; checkClip(); }
+ void DoAction(MetaMoveClipRegionAction & rAct) { rAct.Execute(mpVD); checkClip(); }
+ void DoAction(MetaISectRectClipRegionAction& rAct) { rAct.Execute(mpVD); checkClip(); }
+ void DoAction(MetaISectRegionClipRegionAction& rAct) { rAct.Execute(mpVD); checkClip(); }
+
+ // #i125211# The MetaCommentAction needs to advance (if used), thus
+ // give current metafile and index which may be changed
+ void DoAction(MetaCommentAction const & rAct, GDIMetaFile const & rMtf, sal_uLong& a);
+
+ // missing actions added
+ void DoAction(MetaTextRectAction const & rAct);
+ void DoAction(MetaBmpScalePartAction const & rAct);
+ void DoAction(MetaBmpExScalePartAction const & rAct);
+ void DoAction(MetaMaskAction const & rAct);
+ void DoAction(MetaMaskScaleAction const & rAct);
+ void DoAction(MetaMaskScalePartAction const & rAct);
+ void DoAction(MetaGradientAction const & rAct);
+ void DoAction(MetaTransparentAction const & rAct);
+ void DoAction(MetaRefPointAction& rAct) { rAct.Execute(mpVD); }
+ void DoAction(MetaTextLineColorAction& rAct) { rAct.Execute(mpVD); mbFntDirty = true; }
+ void DoAction(MetaFloatTransparentAction const & rAct);
+ void DoAction(MetaGradientExAction const & rAct);
+ void DoAction(MetaLayoutModeAction& rAct) { rAct.Execute(mpVD); mbFntDirty = true; }
+ void DoAction(MetaTextLanguageAction& rAct) { rAct.Execute(mpVD); mbFntDirty = true; }
+ void DoAction(MetaOverlineColorAction& rAct) { rAct.Execute(mpVD); mbFntDirty = true; }
+
+ void ImportText(const Point& rPos, const OUString& rStr, const MetaAction& rAct);
+ void SetAttributes(SdrObject* pObj, bool bForceTextAttr = false);
+ void InsertObj(SdrObject* pObj, bool bScale = true);
+ void MapScaling();
+
+ // #i73407# reformulation to use new B2DPolygon classes
+ bool CheckLastLineMerge(const basegfx::B2DPolygon& rSrcPoly);
+ bool CheckLastPolyLineAndFillMerge(const basegfx::B2DPolyPolygon& rPolyPolygon);
+
+ void DoLoopActions(GDIMetaFile const & rMtf, SvdProgressInfo* pProgrInfo, sal_uInt32* pActionsToReport);
+
+ // Copy assignment is forbidden and not implemented.
+ ImpSdrGDIMetaFileImport (const ImpSdrGDIMetaFileImport &) = delete;
+ ImpSdrGDIMetaFileImport & operator= (const ImpSdrGDIMetaFileImport &) = delete;
+
+public:
+ ImpSdrGDIMetaFileImport(
+ SdrModel& rModel,
+ SdrLayerID nLay,
+ const tools::Rectangle& rRect);
+
+ size_t DoImport(
+ const GDIMetaFile& rMtf,
+ SdrObjList& rDestList,
+ size_t nInsPos,
+ SvdProgressInfo* pProgrInfo = nullptr);
+};
+
+#endif // INCLUDED_SVX_SOURCE_SVDRAW_SVDFMTF_HXX
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/svdraw/svdglev.cxx b/svx/source/svdraw/svdglev.cxx
new file mode 100644
index 000000000..67c64eb1a
--- /dev/null
+++ b/svx/source/svdraw/svdglev.cxx
@@ -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 .
+ */
+
+
+#include <svx/svdglev.hxx>
+#include <math.h>
+
+#include <svx/svdundo.hxx>
+#include <svx/strings.hrc>
+#include <svx/dialmgr.hxx>
+#include <svx/svdglue.hxx>
+#include <svx/svdtrans.hxx>
+#include <svx/svdobj.hxx>
+
+SdrGlueEditView::SdrGlueEditView(
+ SdrModel& rSdrModel,
+ OutputDevice* pOut)
+: SdrPolyEditView(rSdrModel, pOut)
+{
+}
+
+SdrGlueEditView::~SdrGlueEditView()
+{
+}
+
+void SdrGlueEditView::ImpDoMarkedGluePoints(PGlueDoFunc pDoFunc, bool bConst, const void* p1, const void* p2, const void* p3, const void* p4)
+{
+ const size_t nMarkCount=GetMarkedObjectCount();
+ for (size_t nm=0; nm<nMarkCount; ++nm) {
+ SdrMark* pM=GetSdrMarkByIndex(nm);
+ SdrObject* pObj=pM->GetMarkedSdrObj();
+ const SdrUShortCont& rPts = pM->GetMarkedGluePoints();
+ if (!rPts.empty())
+ {
+ SdrGluePointList* pGPL=nullptr;
+ if (bConst) {
+ const SdrGluePointList* pConstGPL=pObj->GetGluePointList();
+ pGPL=const_cast<SdrGluePointList*>(pConstGPL);
+ } else {
+ pGPL=pObj->ForceGluePointList();
+ }
+ if (pGPL!=nullptr)
+ {
+ if(!bConst && IsUndoEnabled() )
+ AddUndo(GetModel()->GetSdrUndoFactory().CreateUndoGeoObject(*pObj));
+
+ for(sal_uInt16 nPtId : rPts)
+ {
+ sal_uInt16 nGlueIdx=pGPL->FindGluePoint(nPtId);
+ if (nGlueIdx!=SDRGLUEPOINT_NOTFOUND)
+ {
+ SdrGluePoint& rGP=(*pGPL)[nGlueIdx];
+ (*pDoFunc)(rGP,pObj,p1,p2,p3,p4);
+ }
+ }
+ if (!bConst)
+ {
+ pObj->SetChanged();
+ pObj->BroadcastObjectChange();
+ }
+ }
+ }
+ }
+ if (!bConst && nMarkCount!=0) mpModel->SetChanged();
+}
+
+
+static void ImpGetEscDir(SdrGluePoint & rGP, const SdrObject* /*pObj*/, const void* pbFirst, const void* pnThisEsc, const void* pnRet, const void*)
+{
+ TriState& nRet=*const_cast<TriState *>(static_cast<TriState const *>(pnRet));
+ if (nRet!=TRISTATE_INDET) {
+ SdrEscapeDirection nEsc = rGP.GetEscDir();
+ bool bOn = bool(nEsc & *static_cast<SdrEscapeDirection const *>(pnThisEsc));
+ bool& bFirst=*const_cast<bool *>(static_cast<bool const *>(pbFirst));
+ if (bFirst) {
+ nRet = bOn ? TRISTATE_TRUE : TRISTATE_FALSE;
+ bFirst = false;
+ }
+ else if (nRet != (bOn ? TRISTATE_TRUE : TRISTATE_FALSE)) nRet=TRISTATE_INDET;
+ }
+}
+
+TriState SdrGlueEditView::IsMarkedGluePointsEscDir(SdrEscapeDirection nThisEsc) const
+{
+ ForceUndirtyMrkPnt();
+ bool bFirst=true;
+ TriState nRet=TRISTATE_FALSE;
+ const_cast<SdrGlueEditView*>(this)->ImpDoMarkedGluePoints(ImpGetEscDir,true,&bFirst,&nThisEsc,&nRet);
+ return nRet;
+}
+
+static void ImpSetEscDir(SdrGluePoint& rGP, const SdrObject* /*pObj*/, const void* pnThisEsc, const void* pbOn, const void*, const void*)
+{
+ SdrEscapeDirection nEsc=rGP.GetEscDir();
+ if (*static_cast<bool const *>(pbOn))
+ nEsc |= *static_cast<SdrEscapeDirection const *>(pnThisEsc);
+ else
+ nEsc &= ~*static_cast<SdrEscapeDirection const *>(pnThisEsc);
+ rGP.SetEscDir(nEsc);
+}
+
+void SdrGlueEditView::SetMarkedGluePointsEscDir(SdrEscapeDirection nThisEsc, bool bOn)
+{
+ ForceUndirtyMrkPnt();
+ BegUndo(SvxResId(STR_EditSetGlueEscDir),GetDescriptionOfMarkedGluePoints());
+ ImpDoMarkedGluePoints(ImpSetEscDir,false,&nThisEsc,&bOn);
+ EndUndo();
+}
+
+
+static void ImpGetPercent(SdrGluePoint & rGP, const SdrObject* /*pObj*/, const void* pbFirst, const void* pnRet, const void*, const void*)
+{
+ TriState& nRet=*const_cast<TriState *>(static_cast<TriState const *>(pnRet));
+ if (nRet!=TRISTATE_INDET) {
+ bool bOn=rGP.IsPercent();
+ bool& bFirst=*const_cast<bool *>(static_cast<bool const *>(pbFirst));
+ if (bFirst) {
+ nRet = bOn ? TRISTATE_TRUE : TRISTATE_FALSE;
+ bFirst = false;
+ }
+ else if ((nRet!=TRISTATE_FALSE)!=bOn)
+ nRet=TRISTATE_INDET;
+ }
+}
+
+TriState SdrGlueEditView::IsMarkedGluePointsPercent() const
+{
+ ForceUndirtyMrkPnt();
+ bool bFirst=true;
+ TriState nRet = TRISTATE_TRUE;
+ const_cast<SdrGlueEditView*>(this)->ImpDoMarkedGluePoints(ImpGetPercent,true,&bFirst,&nRet);
+ return nRet;
+}
+
+static void ImpSetPercent(SdrGluePoint& rGP, const SdrObject* pObj, const void* pbOn, const void*, const void*, const void*)
+{
+ Point aPos(rGP.GetAbsolutePos(*pObj));
+ rGP.SetPercent(*static_cast<bool const *>(pbOn));
+ rGP.SetAbsolutePos(aPos,*pObj);
+}
+
+void SdrGlueEditView::SetMarkedGluePointsPercent(bool bOn)
+{
+ ForceUndirtyMrkPnt();
+ BegUndo(SvxResId(STR_EditSetGluePercent),GetDescriptionOfMarkedGluePoints());
+ ImpDoMarkedGluePoints(ImpSetPercent,false,&bOn);
+ EndUndo();
+}
+
+
+static void ImpGetAlign(SdrGluePoint & rGP, const SdrObject* /*pObj*/, const void* pbFirst, const void* pbDontCare, const void* pbVert, const void* pnRet)
+{
+ SdrAlign& nRet=*const_cast<SdrAlign *>(static_cast<SdrAlign const *>(pnRet));
+ bool& bDontCare=*const_cast<bool *>(static_cast<bool const *>(pbDontCare));
+ bool bVert=*static_cast<bool const *>(pbVert);
+ if (bDontCare)
+ return;
+
+ SdrAlign nAlg=SdrAlign::NONE;
+ if (bVert) {
+ nAlg=rGP.GetVertAlign();
+ } else {
+ nAlg=rGP.GetHorzAlign();
+ }
+ bool& bFirst=*const_cast<bool *>(static_cast<bool const *>(pbFirst));
+ if (bFirst) { nRet=nAlg; bFirst=false; }
+ else if (nRet!=nAlg) {
+ if (bVert) {
+ nRet=SdrAlign::VERT_DONTCARE;
+ } else {
+ nRet=SdrAlign::HORZ_DONTCARE;
+ }
+ bDontCare=true;
+ }
+}
+
+SdrAlign SdrGlueEditView::GetMarkedGluePointsAlign(bool bVert) const
+{
+ ForceUndirtyMrkPnt();
+ bool bFirst=true;
+ bool bDontCare=false;
+ SdrAlign nRet=SdrAlign::NONE;
+ const_cast<SdrGlueEditView*>(this)->ImpDoMarkedGluePoints(ImpGetAlign,true,&bFirst,&bDontCare,&bVert,&nRet);
+ return nRet;
+}
+
+static void ImpSetAlign(SdrGluePoint& rGP, const SdrObject* pObj, const void* pbVert, const void* pnAlign, const void*, const void*)
+{
+ Point aPos(rGP.GetAbsolutePos(*pObj));
+ if (*static_cast<bool const *>(pbVert)) { // bVert?
+ rGP.SetVertAlign(*static_cast<SdrAlign const *>(pnAlign));
+ } else {
+ rGP.SetHorzAlign(*static_cast<SdrAlign const *>(pnAlign));
+ }
+ rGP.SetAbsolutePos(aPos,*pObj);
+}
+
+void SdrGlueEditView::SetMarkedGluePointsAlign(bool bVert, SdrAlign nAlign)
+{
+ ForceUndirtyMrkPnt();
+ BegUndo(SvxResId(STR_EditSetGlueAlign),GetDescriptionOfMarkedGluePoints());
+ ImpDoMarkedGluePoints(ImpSetAlign,false,&bVert,&nAlign);
+ EndUndo();
+}
+
+void SdrGlueEditView::DeleteMarkedGluePoints()
+{
+ BrkAction();
+ ForceUndirtyMrkPnt();
+ const bool bUndo = IsUndoEnabled();
+ if( bUndo )
+ BegUndo(SvxResId(STR_EditDelete),GetDescriptionOfMarkedGluePoints(),SdrRepeatFunc::Delete);
+
+ const size_t nMarkCount=GetMarkedObjectCount();
+ for (size_t nm=0; nm<nMarkCount; ++nm)
+ {
+ SdrMark* pM=GetSdrMarkByIndex(nm);
+ SdrObject* pObj=pM->GetMarkedSdrObj();
+ const SdrUShortCont& rPts = pM->GetMarkedGluePoints();
+ if (!rPts.empty())
+ {
+ SdrGluePointList* pGPL=pObj->ForceGluePointList();
+ if (pGPL!=nullptr)
+ {
+ if( bUndo )
+ AddUndo(GetModel()->GetSdrUndoFactory().CreateUndoGeoObject(*pObj));
+
+ for(sal_uInt16 nPtId : rPts)
+ {
+ sal_uInt16 nGlueIdx=pGPL->FindGluePoint(nPtId);
+ if (nGlueIdx!=SDRGLUEPOINT_NOTFOUND)
+ {
+ pGPL->Delete(nGlueIdx);
+ }
+ }
+ pObj->SetChanged();
+ pObj->BroadcastObjectChange();
+ }
+ }
+ }
+ if( bUndo )
+ EndUndo();
+ UnmarkAllGluePoints();
+ if (nMarkCount!=0)
+ mpModel->SetChanged();
+}
+
+
+void SdrGlueEditView::ImpCopyMarkedGluePoints()
+{
+ const bool bUndo = IsUndoEnabled();
+
+ if( bUndo )
+ BegUndo();
+
+ const size_t nMarkCount=GetMarkedObjectCount();
+ for (size_t nm=0; nm<nMarkCount; ++nm)
+ {
+ SdrMark* pM=GetSdrMarkByIndex(nm);
+ SdrObject* pObj=pM->GetMarkedSdrObj();
+ SdrUShortCont& rPts = pM->GetMarkedGluePoints();
+ SdrGluePointList* pGPL=pObj->ForceGluePointList();
+ if (!rPts.empty() && pGPL!=nullptr)
+ {
+ if( bUndo )
+ AddUndo(GetModel()->GetSdrUndoFactory().CreateUndoGeoObject(*pObj));
+
+ SdrUShortCont aIdsToErase;
+ SdrUShortCont aIdsToInsert;
+ for(sal_uInt16 nPtId : rPts)
+ {
+ sal_uInt16 nGlueIdx=pGPL->FindGluePoint(nPtId);
+ if (nGlueIdx!=SDRGLUEPOINT_NOTFOUND)
+ {
+ SdrGluePoint aNewGP((*pGPL)[nGlueIdx]); // clone GluePoint
+ sal_uInt16 nNewIdx=pGPL->Insert(aNewGP); // and insert it
+ sal_uInt16 nNewId=(*pGPL)[nNewIdx].GetId(); // retrieve ID of new GluePoints
+ aIdsToErase.insert(nPtId); // select it (instead of the old one)
+ aIdsToInsert.insert(nNewId);
+ }
+ }
+ for(const auto& rId : aIdsToErase)
+ rPts.erase(rId);
+ rPts.insert(aIdsToInsert);
+ }
+ }
+ if( bUndo )
+ EndUndo();
+
+ if (nMarkCount!=0)
+ mpModel->SetChanged();
+}
+
+
+void SdrGlueEditView::ImpTransformMarkedGluePoints(PGlueTrFunc pTrFunc, const void* p1, const void* p2, const void* p3, const void* p4)
+{
+ const size_t nMarkCount=GetMarkedObjectCount();
+ for (size_t nm=0; nm<nMarkCount; ++nm) {
+ SdrMark* pM=GetSdrMarkByIndex(nm);
+ SdrObject* pObj=pM->GetMarkedSdrObj();
+ const SdrUShortCont& rPts = pM->GetMarkedGluePoints();
+ if (!rPts.empty()) {
+ SdrGluePointList* pGPL=pObj->ForceGluePointList();
+ if (pGPL!=nullptr)
+ {
+ if( IsUndoEnabled() )
+ AddUndo(GetModel()->GetSdrUndoFactory().CreateUndoGeoObject(*pObj));
+
+ for(sal_uInt16 nPtId : rPts)
+ {
+ sal_uInt16 nGlueIdx=pGPL->FindGluePoint(nPtId);
+ if (nGlueIdx!=SDRGLUEPOINT_NOTFOUND) {
+ SdrGluePoint& rGP=(*pGPL)[nGlueIdx];
+ Point aPos(rGP.GetAbsolutePos(*pObj));
+ (*pTrFunc)(aPos,p1,p2,p3,p4);
+ rGP.SetAbsolutePos(aPos,*pObj);
+ }
+ }
+ pObj->SetChanged();
+ pObj->BroadcastObjectChange();
+ }
+ }
+ }
+ if (nMarkCount!=0) mpModel->SetChanged();
+}
+
+
+static void ImpMove(Point& rPt, const void* p1, const void* /*p2*/, const void* /*p3*/, const void* /*p4*/)
+{
+ rPt.AdjustX(static_cast<const Size*>(p1)->Width() );
+ rPt.AdjustY(static_cast<const Size*>(p1)->Height() );
+}
+
+void SdrGlueEditView::MoveMarkedGluePoints(const Size& rSiz, bool bCopy)
+{
+ ForceUndirtyMrkPnt();
+ OUString aStr(SvxResId(STR_EditMove));
+ if (bCopy) aStr += SvxResId(STR_EditWithCopy);
+ BegUndo(aStr,GetDescriptionOfMarkedGluePoints(),SdrRepeatFunc::Move);
+ if (bCopy) ImpCopyMarkedGluePoints();
+ ImpTransformMarkedGluePoints(ImpMove,&rSiz);
+ EndUndo();
+ AdjustMarkHdl();
+}
+
+
+static void ImpResize(Point& rPt, const void* p1, const void* p2, const void* p3, const void* /*p4*/)
+{
+ ResizePoint(rPt,*static_cast<const Point*>(p1),*static_cast<const Fraction*>(p2),*static_cast<const Fraction*>(p3));
+}
+
+void SdrGlueEditView::ResizeMarkedGluePoints(const Point& rRef, const Fraction& xFact, const Fraction& yFact, bool bCopy)
+{
+ ForceUndirtyMrkPnt();
+ OUString aStr(SvxResId(STR_EditResize));
+ if (bCopy) aStr+=SvxResId(STR_EditWithCopy);
+ BegUndo(aStr,GetDescriptionOfMarkedGluePoints(),SdrRepeatFunc::Resize);
+ if (bCopy) ImpCopyMarkedGluePoints();
+ ImpTransformMarkedGluePoints(ImpResize,&rRef,&xFact,&yFact);
+ EndUndo();
+ AdjustMarkHdl();
+}
+
+
+static void ImpRotate(Point& rPt, const void* p1, const void* /*p2*/, const void* p3, const void* p4)
+{
+ RotatePoint(rPt,*static_cast<const Point*>(p1),*static_cast<const double*>(p3),*static_cast<const double*>(p4));
+}
+
+void SdrGlueEditView::RotateMarkedGluePoints(const Point& rRef, Degree100 nAngle, bool bCopy)
+{
+ ForceUndirtyMrkPnt();
+ OUString aStr(SvxResId(STR_EditRotate));
+ if (bCopy) aStr+=SvxResId(STR_EditWithCopy);
+ BegUndo(aStr,GetDescriptionOfMarkedGluePoints(),SdrRepeatFunc::Rotate);
+ if (bCopy) ImpCopyMarkedGluePoints();
+ double nSin = sin(toRadians(nAngle));
+ double nCos = cos(toRadians(nAngle));
+ ImpTransformMarkedGluePoints(ImpRotate,&rRef,&nAngle,&nSin,&nCos);
+ EndUndo();
+ AdjustMarkHdl();
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/svdraw/svdglue.cxx b/svx/source/svdraw/svdglue.cxx
new file mode 100644
index 000000000..9808372f8
--- /dev/null
+++ b/svx/source/svdraw/svdglue.cxx
@@ -0,0 +1,394 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * 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/debug.hxx>
+#include <vcl/window.hxx>
+
+#include <svx/svdglue.hxx>
+#include <svx/svdobj.hxx>
+#include <svx/svdtrans.hxx>
+#include <comphelper/lok.hxx>
+
+const Size aGlueHalfSize(4,4);
+
+void SdrGluePoint::SetReallyAbsolute(bool bOn, const SdrObject& rObj)
+{
+ if ( bReallyAbsolute == bOn )
+ return;
+
+ if ( bOn )
+ {
+ aPos=GetAbsolutePos(rObj);
+ bReallyAbsolute=bOn;
+ }
+ else
+ {
+ bReallyAbsolute=bOn;
+ Point aPt(aPos);
+ SetAbsolutePos(aPt,rObj);
+ }
+}
+
+Point SdrGluePoint::GetAbsolutePos(const SdrObject& rObj) const
+{
+ if (bReallyAbsolute) return aPos;
+ tools::Rectangle aSnap(rObj.GetSnapRect());
+ tools::Rectangle aBound(rObj.GetSnapRect());
+ Point aPt(aPos);
+
+ Point aOfs(aSnap.Center());
+ switch (GetHorzAlign()) {
+ case SdrAlign::HORZ_LEFT : aOfs.setX(aSnap.Left() ); break;
+ case SdrAlign::HORZ_RIGHT : aOfs.setX(aSnap.Right() ); break;
+ default: break;
+ }
+ switch (GetVertAlign()) {
+ case SdrAlign::VERT_TOP : aOfs.setY(aSnap.Top() ); break;
+ case SdrAlign::VERT_BOTTOM: aOfs.setY(aSnap.Bottom() ); break;
+ default: break;
+ }
+ if (!bNoPercent) {
+ tools::Long nXMul=aSnap.Right()-aSnap.Left();
+ tools::Long nYMul=aSnap.Bottom()-aSnap.Top();
+ tools::Long nXDiv=10000;
+ tools::Long nYDiv=10000;
+ if (nXMul!=nXDiv) {
+ aPt.setX( aPt.X() * nXMul );
+ aPt.setX( aPt.X() / nXDiv );
+ }
+ if (nYMul!=nYDiv) {
+ aPt.setY( aPt.Y() * nYMul );
+ aPt.setY( aPt.Y() / nYDiv );
+ }
+ }
+ aPt+=aOfs;
+ // Now limit to the BoundRect of the object
+ if (aPt.X()<aBound.Left ()) aPt.setX(aBound.Left () );
+ if (aPt.X()>aBound.Right ()) aPt.setX(aBound.Right () );
+ if (aPt.Y()<aBound.Top ()) aPt.setY(aBound.Top () );
+ if (aPt.Y()>aBound.Bottom()) aPt.setY(aBound.Bottom() );
+ return aPt;
+}
+
+void SdrGluePoint::SetAbsolutePos(const Point& rNewPos, const SdrObject& rObj)
+{
+ if (bReallyAbsolute) {
+ aPos=rNewPos;
+ return;
+ }
+ tools::Rectangle aSnap(rObj.GetSnapRect());
+ Point aPt(rNewPos);
+
+ Point aOfs(aSnap.Center());
+ switch (GetHorzAlign()) {
+ case SdrAlign::HORZ_LEFT : aOfs.setX(aSnap.Left() ); break;
+ case SdrAlign::HORZ_RIGHT : aOfs.setX(aSnap.Right() ); break;
+ default: break;
+ }
+ switch (GetVertAlign()) {
+ case SdrAlign::VERT_TOP : aOfs.setY(aSnap.Top() ); break;
+ case SdrAlign::VERT_BOTTOM: aOfs.setY(aSnap.Bottom() ); break;
+ default: break;
+ }
+ aPt-=aOfs;
+ if (!bNoPercent) {
+ tools::Long nXMul=aSnap.Right()-aSnap.Left();
+ tools::Long nYMul=aSnap.Bottom()-aSnap.Top();
+ if (nXMul==0) nXMul=1;
+ if (nYMul==0) nYMul=1;
+ tools::Long nXDiv=10000;
+ tools::Long nYDiv=10000;
+ if (nXMul!=nXDiv) {
+ aPt.setX( aPt.X() * nXDiv );
+ aPt.setX( aPt.X() / nXMul );
+ }
+ if (nYMul!=nYDiv) {
+ aPt.setY( aPt.Y() * nYDiv );
+ aPt.setY( aPt.Y() / nYMul );
+ }
+ }
+ aPos=aPt;
+}
+
+Degree100 SdrGluePoint::GetAlignAngle() const
+{
+ if (nAlign == (SdrAlign::HORZ_CENTER|SdrAlign::VERT_CENTER))
+ return 0_deg100; // Invalid!
+ else if (nAlign == (SdrAlign::HORZ_RIGHT |SdrAlign::VERT_CENTER))
+ return 0_deg100;
+ else if (nAlign == (SdrAlign::HORZ_RIGHT |SdrAlign::VERT_TOP))
+ return 4500_deg100;
+ else if (nAlign == (SdrAlign::HORZ_CENTER|SdrAlign::VERT_TOP))
+ return 9000_deg100;
+ else if (nAlign == (SdrAlign::HORZ_LEFT |SdrAlign::VERT_TOP))
+ return 13500_deg100;
+ else if (nAlign == (SdrAlign::HORZ_LEFT |SdrAlign::VERT_CENTER))
+ return 18000_deg100;
+ else if (nAlign == (SdrAlign::HORZ_LEFT |SdrAlign::VERT_BOTTOM))
+ return 22500_deg100;
+ else if (nAlign == (SdrAlign::HORZ_CENTER|SdrAlign::VERT_BOTTOM))
+ return 27000_deg100;
+ else if (nAlign == (SdrAlign::HORZ_RIGHT |SdrAlign::VERT_BOTTOM))
+ return 31500_deg100;
+ return 0_deg100;
+}
+
+void SdrGluePoint::SetAlignAngle(Degree100 nAngle)
+{
+ nAngle=NormAngle36000(nAngle);
+ if (nAngle>=33750_deg100 || nAngle<2250_deg100) nAlign=SdrAlign::HORZ_RIGHT |SdrAlign::VERT_CENTER;
+ else if (nAngle< 6750_deg100) nAlign=SdrAlign::HORZ_RIGHT |SdrAlign::VERT_TOP ;
+ else if (nAngle<11250_deg100) nAlign=SdrAlign::HORZ_CENTER|SdrAlign::VERT_TOP ;
+ else if (nAngle<15750_deg100) nAlign=SdrAlign::HORZ_LEFT |SdrAlign::VERT_TOP ;
+ else if (nAngle<20250_deg100) nAlign=SdrAlign::HORZ_LEFT |SdrAlign::VERT_CENTER;
+ else if (nAngle<24750_deg100) nAlign=SdrAlign::HORZ_LEFT |SdrAlign::VERT_BOTTOM;
+ else if (nAngle<29250_deg100) nAlign=SdrAlign::HORZ_CENTER|SdrAlign::VERT_BOTTOM;
+ else if (nAngle<33750_deg100) nAlign=SdrAlign::HORZ_RIGHT |SdrAlign::VERT_BOTTOM;
+}
+
+Degree100 SdrGluePoint::EscDirToAngle(SdrEscapeDirection nEsc)
+{
+ switch (nEsc) {
+ case SdrEscapeDirection::RIGHT : return 0_deg100;
+ case SdrEscapeDirection::TOP : return 9000_deg100;
+ case SdrEscapeDirection::LEFT : return 18000_deg100;
+ case SdrEscapeDirection::BOTTOM: return 27000_deg100;
+ default: break;
+ } // switch
+ return 0_deg100;
+}
+
+SdrEscapeDirection SdrGluePoint::EscAngleToDir(Degree100 nAngle)
+{
+ nAngle=NormAngle36000(nAngle);
+ if (nAngle>=31500_deg100 || nAngle<4500_deg100)
+ return SdrEscapeDirection::RIGHT;
+ if (nAngle<13500_deg100)
+ return SdrEscapeDirection::TOP;
+ if (nAngle<22500_deg100)
+ return SdrEscapeDirection::LEFT;
+ /* (nAngle<31500)*/
+ return SdrEscapeDirection::BOTTOM;
+}
+
+void SdrGluePoint::Rotate(const Point& rRef, Degree100 nAngle, double sn, double cs, const SdrObject* pObj)
+{
+ Point aPt(pObj!=nullptr ? GetAbsolutePos(*pObj) : GetPos());
+ RotatePoint(aPt,rRef,sn,cs);
+ // rotate reference edge
+ if(nAlign != (SdrAlign::HORZ_CENTER|SdrAlign::VERT_CENTER))
+ {
+ SetAlignAngle(GetAlignAngle()+nAngle);
+ }
+ // rotate exit directions
+ SdrEscapeDirection nEscDir0=nEscDir;
+ SdrEscapeDirection nEscDir1=SdrEscapeDirection::SMART;
+ if (nEscDir0&SdrEscapeDirection::LEFT ) nEscDir1 |= EscAngleToDir(EscDirToAngle(SdrEscapeDirection::LEFT )+nAngle);
+ if (nEscDir0&SdrEscapeDirection::TOP ) nEscDir1 |= EscAngleToDir(EscDirToAngle(SdrEscapeDirection::TOP )+nAngle);
+ if (nEscDir0&SdrEscapeDirection::RIGHT ) nEscDir1 |= EscAngleToDir(EscDirToAngle(SdrEscapeDirection::RIGHT )+nAngle);
+ if (nEscDir0&SdrEscapeDirection::BOTTOM) nEscDir1 |= EscAngleToDir(EscDirToAngle(SdrEscapeDirection::BOTTOM)+nAngle);
+ nEscDir=nEscDir1;
+ if (pObj!=nullptr) SetAbsolutePos(aPt,*pObj); else SetPos(aPt);
+}
+
+void SdrGluePoint::Mirror(const Point& rRef1, const Point& rRef2, Degree100 nAngle, const SdrObject* pObj)
+{
+ Point aPt(pObj!=nullptr ? GetAbsolutePos(*pObj) : GetPos());
+ MirrorPoint(aPt,rRef1,rRef2);
+ // mirror reference edge
+ if(nAlign != (SdrAlign::HORZ_CENTER|SdrAlign::VERT_CENTER))
+ {
+ Degree100 nAW=GetAlignAngle();
+ nAW+=2_deg100*(nAngle-nAW);
+ SetAlignAngle(nAW);
+ }
+ // mirror exit directions
+ SdrEscapeDirection nEscDir0=nEscDir;
+ SdrEscapeDirection nEscDir1=SdrEscapeDirection::SMART;
+ if (nEscDir0&SdrEscapeDirection::LEFT) {
+ Degree100 nEW=EscDirToAngle(SdrEscapeDirection::LEFT);
+ nEW+=2_deg100*(nAngle-nEW);
+ nEscDir1|=EscAngleToDir(nEW);
+ }
+ if (nEscDir0&SdrEscapeDirection::TOP) {
+ Degree100 nEW=EscDirToAngle(SdrEscapeDirection::TOP);
+ nEW+=2_deg100*(nAngle-nEW);
+ nEscDir1|=EscAngleToDir(nEW);
+ }
+ if (nEscDir0&SdrEscapeDirection::RIGHT) {
+ Degree100 nEW=EscDirToAngle(SdrEscapeDirection::RIGHT);
+ nEW+=2_deg100*(nAngle-nEW);
+ nEscDir1|=EscAngleToDir(nEW);
+ }
+ if (nEscDir0&SdrEscapeDirection::BOTTOM) {
+ Degree100 nEW=EscDirToAngle(SdrEscapeDirection::BOTTOM);
+ nEW+=2_deg100*(nAngle-nEW);
+ nEscDir1|=EscAngleToDir(nEW);
+ }
+ nEscDir=nEscDir1;
+ if (pObj!=nullptr) SetAbsolutePos(aPt,*pObj); else SetPos(aPt);
+}
+
+void SdrGluePoint::Shear(const Point& rRef, double tn, bool bVShear, const SdrObject* pObj)
+{
+ Point aPt(pObj!=nullptr ? GetAbsolutePos(*pObj) : GetPos());
+ ShearPoint(aPt,rRef,tn,bVShear);
+ if (pObj!=nullptr) SetAbsolutePos(aPt,*pObj); else SetPos(aPt);
+}
+
+void SdrGluePoint::Invalidate(vcl::Window& rWin, const SdrObject* pObj) const
+{
+ if (comphelper::LibreOfficeKit::isActive())
+ return;
+ bool bMapMode=rWin.IsMapModeEnabled();
+ Point aPt(pObj!=nullptr ? GetAbsolutePos(*pObj) : GetPos());
+ aPt=rWin.LogicToPixel(aPt);
+ rWin.EnableMapMode(false);
+
+ Size aSiz( aGlueHalfSize );
+ tools::Rectangle aRect(aPt.X()-aSiz.Width(),aPt.Y()-aSiz.Height(),
+ aPt.X()+aSiz.Width(),aPt.Y()+aSiz.Height());
+
+ // do not erase background, that causes flicker (!)
+ rWin.Invalidate(aRect, InvalidateFlags::NoErase);
+
+ rWin.EnableMapMode(bMapMode);
+}
+
+bool SdrGluePoint::IsHit(const Point& rPnt, const OutputDevice& rOut, const SdrObject* pObj) const
+{
+ Point aPt(pObj!=nullptr ? GetAbsolutePos(*pObj) : GetPos());
+ Size aSiz=rOut.PixelToLogic(aGlueHalfSize);
+ tools::Rectangle aRect(aPt.X()-aSiz.Width(),aPt.Y()-aSiz.Height(),aPt.X()+aSiz.Width(),aPt.Y()+aSiz.Height());
+ return aRect.Contains(rPnt);
+}
+
+
+SdrGluePointList& SdrGluePointList::operator=(const SdrGluePointList& rSrcList)
+{
+ if (GetCount()!=0) aList.clear();
+ sal_uInt16 nCount=rSrcList.GetCount();
+ for (sal_uInt16 i=0; i<nCount; i++) {
+ Insert(rSrcList[i]);
+ }
+ return *this;
+}
+
+// The ID's of the gluepoints always increase monotonously!
+// If an ID is taken already, the new gluepoint gets a new ID. ID 0 is reserved.
+sal_uInt16 SdrGluePointList::Insert(const SdrGluePoint& rGP)
+{
+ SdrGluePoint aGP(rGP);
+ sal_uInt16 nId=aGP.GetId();
+ sal_uInt16 nCount=GetCount();
+ sal_uInt16 nInsPos=nCount;
+ sal_uInt16 nLastId=nCount!=0 ? aList[nCount-1].GetId() : 0;
+ DBG_ASSERT(nLastId>=nCount,"SdrGluePointList::Insert(): nLastId<nCount");
+ bool bHole = nLastId>nCount;
+ if (nId<=nLastId) {
+ if (!bHole || nId==0) {
+ nId=nLastId+1;
+ } else {
+ bool bBrk = false;
+ for (sal_uInt16 nNum=0; nNum<nCount && !bBrk; nNum++) {
+ const auto& pGP2=aList[nNum];
+ sal_uInt16 nTmpId=pGP2.GetId();
+ if (nTmpId==nId) {
+ nId=nLastId+1; // already in use
+ bBrk = true;
+ }
+ if (nTmpId>nId) {
+ nInsPos=nNum; // insert here (sort)
+ bBrk = true;
+ }
+ }
+ }
+ aGP.SetId(nId);
+ }
+ aList.emplace(aList.begin()+nInsPos, aGP);
+ return nInsPos;
+}
+
+void SdrGluePointList::Invalidate(vcl::Window& rWin, const SdrObject* pObj) const
+{
+ if (comphelper::LibreOfficeKit::isActive())
+ return;
+ for (auto& xGP : aList)
+ xGP.Invalidate(rWin,pObj);
+}
+
+sal_uInt16 SdrGluePointList::FindGluePoint(sal_uInt16 nId) const
+{
+ // TODO: Implement a better search algorithm
+ // List should be sorted at all times!
+ sal_uInt16 nCount=GetCount();
+ sal_uInt16 nRet=SDRGLUEPOINT_NOTFOUND;
+ for (sal_uInt16 nNum=0; nNum<nCount && nRet==SDRGLUEPOINT_NOTFOUND; nNum++) {
+ const auto& pGP=aList[nNum];
+ if (pGP.GetId()==nId) nRet=nNum;
+ }
+ return nRet;
+}
+
+sal_uInt16 SdrGluePointList::HitTest(const Point& rPnt, const OutputDevice& rOut, const SdrObject* pObj) const
+{
+ sal_uInt16 nCount = GetCount();
+ sal_uInt16 nRet = SDRGLUEPOINT_NOTFOUND;
+ sal_uInt16 nNum = nCount;
+ while ((nNum>0) && nRet==SDRGLUEPOINT_NOTFOUND) {
+ nNum--;
+ const auto& pGP = aList[nNum];
+ if (pGP.IsHit(rPnt,rOut,pObj))
+ nRet = nNum;
+ }
+ return nRet;
+}
+
+void SdrGluePointList::SetReallyAbsolute(bool bOn, const SdrObject& rObj)
+{
+ for (auto& xGP : aList)
+ xGP.SetReallyAbsolute(bOn,rObj);
+}
+
+void SdrGluePointList::Rotate(const Point& rRef, Degree100 nAngle, double sn, double cs, const SdrObject* pObj)
+{
+ for (auto& xGP : aList)
+ xGP.Rotate(rRef,nAngle,sn,cs,pObj);
+}
+
+void SdrGluePointList::Mirror(const Point& rRef1, const Point& rRef2, const SdrObject* pObj)
+{
+ Point aPt(rRef2); aPt-=rRef1;
+ Degree100 nAngle=GetAngle(aPt);
+ Mirror(rRef1,rRef2,nAngle,pObj);
+}
+
+void SdrGluePointList::Mirror(const Point& rRef1, const Point& rRef2, Degree100 nAngle, const SdrObject* pObj)
+{
+ for (auto& xGP : aList)
+ xGP.Mirror(rRef1,rRef2,nAngle,pObj);
+}
+
+void SdrGluePointList::Shear(const Point& rRef, double tn, bool bVShear, const SdrObject* pObj)
+{
+ for (auto& xGP : aList)
+ xGP.Shear(rRef,tn,bVShear,pObj);
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/svdraw/svdhdl.cxx b/svx/source/svdraw/svdhdl.cxx
new file mode 100644
index 000000000..31aa07578
--- /dev/null
+++ b/svx/source/svdraw/svdhdl.cxx
@@ -0,0 +1,2675 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * 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 <cassert>
+
+#include <svx/svdhdl.hxx>
+#include <svx/svdpagv.hxx>
+#include <svx/svdmrkv.hxx>
+#include <vcl/settings.hxx>
+#include <vcl/virdev.hxx>
+#include <vcl/ptrstyle.hxx>
+
+#include <svx/sxekitm.hxx>
+#include <svx/strings.hrc>
+#include <svx/svdmodel.hxx>
+#include "gradtrns.hxx"
+#include <svx/xflgrit.hxx>
+#include <svx/svdundo.hxx>
+#include <svx/dialmgr.hxx>
+#include <svx/xflftrit.hxx>
+
+#include <svx/svdopath.hxx>
+#include <basegfx/vector/b2dvector.hxx>
+#include <basegfx/polygon/b2dpolygon.hxx>
+#include <svx/sdr/overlay/overlaymanager.hxx>
+#include <svx/sdr/overlay/overlayanimatedbitmapex.hxx>
+#include <svx/sdr/overlay/overlaybitmapex.hxx>
+#include <sdr/overlay/overlayline.hxx>
+#include <sdr/overlay/overlaytriangle.hxx>
+#include <sdr/overlay/overlayhandle.hxx>
+#include <sdr/overlay/overlayrectangle.hxx>
+#include <svx/sdrpagewindow.hxx>
+#include <svx/sdrpaintwindow.hxx>
+#include <vcl/svapp.hxx>
+#include <svx/sdr/overlay/overlaypolypolygon.hxx>
+#include <vcl/lazydelete.hxx>
+#include <vcl/BitmapTools.hxx>
+#include <svx/sdr/contact/objectcontact.hxx>
+#include <svx/sdr/contact/viewcontact.hxx>
+#include <osl/diagnose.h>
+
+#include <basegfx/polygon/b2dpolygontools.hxx>
+#include <svx/sdr/overlay/overlayprimitive2dsequenceobject.hxx>
+#include <drawinglayer/primitive2d/PolygonHairlinePrimitive2D.hxx>
+#include <drawinglayer/primitive2d/graphicprimitive2d.hxx>
+#include <drawinglayer/primitive2d/maskprimitive2d.hxx>
+#include <drawinglayer/primitive2d/unifiedtransparenceprimitive2d.hxx>
+#include <svtools/optionsdrawinglayer.hxx>
+#include <memory>
+#include <bitmaps.hlst>
+
+namespace {
+
+// #i15222#
+// Due to the resource problems in Win95/98 with bitmap resources I
+// will change this handle bitmap providing class. Old version was splitting
+// and preparing all small handle bitmaps in device bitmap format, now this will
+// be done on the fly. Thus, there is only one big bitmap in memory. With
+// three source bitmaps, this will be 3 system bitmap resources instead of hundreds.
+// The price for that needs to be evaluated. Maybe we will need another change here
+// if this is too expensive.
+class SdrHdlBitmapSet
+{
+ // the bitmap holding all information
+ BitmapEx maMarkersBitmap;
+
+ // the cropped Bitmaps for reusage
+ ::std::vector< BitmapEx > maRealMarkers;
+
+ // helpers
+ BitmapEx& impGetOrCreateTargetBitmap(sal_uInt16 nIndex, const tools::Rectangle& rRectangle);
+
+public:
+ explicit SdrHdlBitmapSet();
+
+ const BitmapEx& GetBitmapEx(BitmapMarkerKind eKindOfMarker, sal_uInt16 nInd);
+};
+
+}
+
+#define KIND_COUNT (14)
+#define INDEX_COUNT (6)
+#define INDIVIDUAL_COUNT (5)
+
+SdrHdlBitmapSet::SdrHdlBitmapSet()
+ : maMarkersBitmap(SIP_SA_MARKERS),
+ // 15 kinds (BitmapMarkerKind) use index [0..5] + 5 extra
+ maRealMarkers((KIND_COUNT * INDEX_COUNT) + INDIVIDUAL_COUNT)
+{
+}
+
+BitmapEx& SdrHdlBitmapSet::impGetOrCreateTargetBitmap(sal_uInt16 nIndex, const tools::Rectangle& rRectangle)
+{
+ BitmapEx& rTargetBitmap = maRealMarkers[nIndex];
+
+ if(rTargetBitmap.IsEmpty())
+ {
+ rTargetBitmap = maMarkersBitmap;
+ rTargetBitmap.Crop(rRectangle);
+ }
+
+ return rTargetBitmap;
+}
+
+// change getting of bitmap to use the big resource bitmap
+const BitmapEx& SdrHdlBitmapSet::GetBitmapEx(BitmapMarkerKind eKindOfMarker, sal_uInt16 nInd)
+{
+ // fill in size and source position in maMarkersBitmap
+ const sal_uInt16 nYPos(nInd * 11);
+
+ switch(eKindOfMarker)
+ {
+ default:
+ {
+ OSL_FAIL( "Unknown kind of marker." );
+ [[fallthrough]]; // return Rect_9x9 as default
+ }
+ case BitmapMarkerKind::Rect_9x9:
+ {
+ return impGetOrCreateTargetBitmap((1 * INDEX_COUNT) + nInd, tools::Rectangle(Point(7, nYPos), Size(9, 9)));
+ }
+
+ case BitmapMarkerKind::Rect_7x7:
+ {
+ return impGetOrCreateTargetBitmap((0 * INDEX_COUNT) + nInd, tools::Rectangle(Point(0, nYPos), Size(7, 7)));
+ }
+
+ case BitmapMarkerKind::Rect_11x11:
+ {
+ return impGetOrCreateTargetBitmap((2 * INDEX_COUNT) + nInd, tools::Rectangle(Point(16, nYPos), Size(11, 11)));
+ }
+
+ case BitmapMarkerKind::Rect_13x13:
+ {
+ const sal_uInt16 nIndex((3 * INDEX_COUNT) + nInd);
+
+ switch(nInd)
+ {
+ case 0:
+ {
+ return impGetOrCreateTargetBitmap(nIndex, tools::Rectangle(Point(72, 66), Size(13, 13)));
+ }
+ case 1:
+ {
+ return impGetOrCreateTargetBitmap(nIndex, tools::Rectangle(Point(85, 66), Size(13, 13)));
+ }
+ case 2:
+ {
+ return impGetOrCreateTargetBitmap(nIndex, tools::Rectangle(Point(72, 79), Size(13, 13)));
+ }
+ case 3:
+ {
+ return impGetOrCreateTargetBitmap(nIndex, tools::Rectangle(Point(85, 79), Size(13, 13)));
+ }
+ case 4:
+ {
+ return impGetOrCreateTargetBitmap(nIndex, tools::Rectangle(Point(98, 79), Size(13, 13)));
+ }
+ default: // case 5:
+ {
+ return impGetOrCreateTargetBitmap(nIndex, tools::Rectangle(Point(98, 66), Size(13, 13)));
+ }
+ }
+ }
+
+ case BitmapMarkerKind::Circ_7x7:
+ case BitmapMarkerKind::Customshape_7x7:
+ {
+ return impGetOrCreateTargetBitmap((4 * INDEX_COUNT) + nInd, tools::Rectangle(Point(27, nYPos), Size(7, 7)));
+ }
+
+ case BitmapMarkerKind::Circ_9x9:
+ case BitmapMarkerKind::Customshape_9x9:
+ {
+ return impGetOrCreateTargetBitmap((5 * INDEX_COUNT) + nInd, tools::Rectangle(Point(34, nYPos), Size(9, 9)));
+ }
+
+ case BitmapMarkerKind::Circ_11x11:
+ case BitmapMarkerKind::Customshape_11x11:
+ {
+ return impGetOrCreateTargetBitmap((6 * INDEX_COUNT) + nInd, tools::Rectangle(Point(43, nYPos), Size(11, 11)));
+ }
+
+ case BitmapMarkerKind::Elli_7x9:
+ {
+ return impGetOrCreateTargetBitmap((7 * INDEX_COUNT) + nInd, tools::Rectangle(Point(54, nYPos), Size(7, 9)));
+ }
+
+ case BitmapMarkerKind::Elli_9x11:
+ {
+ return impGetOrCreateTargetBitmap((8 * INDEX_COUNT) + nInd, tools::Rectangle(Point(61, nYPos), Size(9, 11)));
+ }
+
+ case BitmapMarkerKind::Elli_9x7:
+ {
+ return impGetOrCreateTargetBitmap((9 * INDEX_COUNT) + nInd, tools::Rectangle(Point(70, nYPos), Size(9, 7)));
+ }
+
+ case BitmapMarkerKind::Elli_11x9:
+ {
+ return impGetOrCreateTargetBitmap((10 * INDEX_COUNT) + nInd, tools::Rectangle(Point(79, nYPos), Size(11, 9)));
+ }
+
+ case BitmapMarkerKind::RectPlus_7x7:
+ {
+ return impGetOrCreateTargetBitmap((11 * INDEX_COUNT) + nInd, tools::Rectangle(Point(90, nYPos), Size(7, 7)));
+ }
+
+ case BitmapMarkerKind::RectPlus_9x9:
+ {
+ return impGetOrCreateTargetBitmap((12 * INDEX_COUNT) + nInd, tools::Rectangle(Point(97, nYPos), Size(9, 9)));
+ }
+
+ case BitmapMarkerKind::RectPlus_11x11:
+ {
+ return impGetOrCreateTargetBitmap((13 * INDEX_COUNT) + nInd, tools::Rectangle(Point(106, nYPos), Size(11, 11)));
+ }
+
+ case BitmapMarkerKind::Crosshair:
+ {
+ return impGetOrCreateTargetBitmap((KIND_COUNT * INDEX_COUNT) + 0, tools::Rectangle(Point(0, 68), Size(15, 15)));
+ }
+
+ case BitmapMarkerKind::Glue:
+ {
+ return impGetOrCreateTargetBitmap((KIND_COUNT * INDEX_COUNT) + 1, tools::Rectangle(Point(15, 76), Size(9, 9)));
+ }
+
+ case BitmapMarkerKind::Glue_Deselected:
+ {
+ return impGetOrCreateTargetBitmap((KIND_COUNT * INDEX_COUNT) + 2, tools::Rectangle(Point(15, 67), Size(9, 9)));
+ }
+
+ case BitmapMarkerKind::Anchor: // AnchorTR for SW
+ case BitmapMarkerKind::AnchorTR:
+ {
+ return impGetOrCreateTargetBitmap((KIND_COUNT * INDEX_COUNT) + 3, tools::Rectangle(Point(24, 67), Size(24, 24)));
+ }
+
+ // add AnchorPressed to be able to animate anchor control
+ case BitmapMarkerKind::AnchorPressed:
+ case BitmapMarkerKind::AnchorPressedTR:
+ {
+ return impGetOrCreateTargetBitmap((KIND_COUNT * INDEX_COUNT) + 4, tools::Rectangle(Point(48, 67), Size(24, 24)));
+ }
+ }
+}
+
+
+SdrHdl::SdrHdl():
+ pObj(nullptr),
+ pPV(nullptr),
+ pHdlList(nullptr),
+ eKind(SdrHdlKind::Move),
+ nRotationAngle(0),
+ nObjHdlNum(0),
+ nPolyNum(0),
+ nPPntNum(0),
+ nSourceHdlNum(0),
+ bSelect(false),
+ b1PixMore(false),
+ bPlusHdl(false),
+ mbMoveOutside(false),
+ mbMouseOver(false)
+{
+}
+
+SdrHdl::SdrHdl(const Point& rPnt, SdrHdlKind eNewKind):
+ pObj(nullptr),
+ pPV(nullptr),
+ pHdlList(nullptr),
+ aPos(rPnt),
+ eKind(eNewKind),
+ nRotationAngle(0),
+ nObjHdlNum(0),
+ nPolyNum(0),
+ nPPntNum(0),
+ nSourceHdlNum(0),
+ bSelect(false),
+ b1PixMore(false),
+ bPlusHdl(false),
+ mbMoveOutside(false),
+ mbMouseOver(false)
+{
+}
+
+SdrHdl::~SdrHdl()
+{
+ GetRidOfIAObject();
+}
+
+void SdrHdl::Set1PixMore(bool bJa)
+{
+ if(b1PixMore != bJa)
+ {
+ b1PixMore = bJa;
+
+ // create new display
+ Touch();
+ }
+}
+
+void SdrHdl::SetMoveOutside( bool bMoveOutside )
+{
+ if(mbMoveOutside != bMoveOutside)
+ {
+ mbMoveOutside = bMoveOutside;
+
+ // create new display
+ Touch();
+ }
+}
+
+void SdrHdl::SetRotationAngle(Degree100 n)
+{
+ if(nRotationAngle != n)
+ {
+ nRotationAngle = n;
+
+ // create new display
+ Touch();
+ }
+}
+
+void SdrHdl::SetPos(const Point& rPnt)
+{
+ if(aPos != rPnt)
+ {
+ // remember new position
+ aPos = rPnt;
+
+ // create new display
+ Touch();
+ }
+}
+
+void SdrHdl::SetSelected(bool bJa)
+{
+ if(bSelect != bJa)
+ {
+ // remember new value
+ bSelect = bJa;
+
+ // create new display
+ Touch();
+ }
+}
+
+void SdrHdl::SetHdlList(SdrHdlList* pList)
+{
+ if(pHdlList != pList)
+ {
+ // remember list
+ pHdlList = pList;
+
+ // now it's possible to create graphic representation
+ Touch();
+ }
+}
+
+void SdrHdl::SetObj(SdrObject* pNewObj)
+{
+ if(pObj != pNewObj)
+ {
+ // remember new object
+ pObj = pNewObj;
+
+ // graphic representation may have changed
+ Touch();
+ }
+}
+
+void SdrHdl::Touch()
+{
+ // force update of graphic representation
+ CreateB2dIAObject();
+}
+
+void SdrHdl::GetRidOfIAObject()
+{
+
+ // OVERLAYMANAGER
+ maOverlayGroup.clear();
+}
+
+void SdrHdl::CreateB2dIAObject()
+{
+ // first throw away old one
+ GetRidOfIAObject();
+
+ if(!pHdlList || !pHdlList->GetView() || pHdlList->GetView()->areMarkHandlesHidden())
+ return;
+
+ BitmapColorIndex eColIndex = BitmapColorIndex::LightGreen;
+ BitmapMarkerKind eKindOfMarker = BitmapMarkerKind::Rect_7x7;
+
+ bool bRot = pHdlList->IsRotateShear();
+ if(pObj)
+ eColIndex = bSelect ? BitmapColorIndex::Cyan : BitmapColorIndex::LightCyan;
+ if(bRot)
+ {
+ // red rotation handles
+ if(pObj && bSelect)
+ eColIndex = BitmapColorIndex::Red;
+ else
+ eColIndex = BitmapColorIndex::LightRed;
+ }
+
+ switch(eKind)
+ {
+ case SdrHdlKind::Move:
+ {
+ eKindOfMarker = b1PixMore ? BitmapMarkerKind::Rect_9x9 : BitmapMarkerKind::Rect_7x7;
+ break;
+ }
+ case SdrHdlKind::UpperLeft:
+ case SdrHdlKind::UpperRight:
+ case SdrHdlKind::LowerLeft:
+ case SdrHdlKind::LowerRight:
+ {
+ // corner handles
+ if(bRot)
+ {
+ eKindOfMarker = BitmapMarkerKind::Circ_7x7;
+ }
+ else
+ {
+ eKindOfMarker = BitmapMarkerKind::Rect_7x7;
+ }
+ break;
+ }
+ case SdrHdlKind::Upper:
+ case SdrHdlKind::Lower:
+ {
+ // Upper/Lower handles
+ if(bRot)
+ {
+ eKindOfMarker = BitmapMarkerKind::Elli_9x7;
+ }
+ else
+ {
+ eKindOfMarker = BitmapMarkerKind::Rect_7x7;
+ }
+ break;
+ }
+ case SdrHdlKind::Left:
+ case SdrHdlKind::Right:
+ {
+ // Left/Right handles
+ if(bRot)
+ {
+ eKindOfMarker = BitmapMarkerKind::Elli_7x9;
+ }
+ else
+ {
+ eKindOfMarker = BitmapMarkerKind::Rect_7x7;
+ }
+ break;
+ }
+ case SdrHdlKind::Poly:
+ {
+ if(bRot)
+ {
+ eKindOfMarker = b1PixMore ? BitmapMarkerKind::Circ_9x9 : BitmapMarkerKind::Circ_7x7;
+ }
+ else
+ {
+ eKindOfMarker = b1PixMore ? BitmapMarkerKind::Rect_9x9 : BitmapMarkerKind::Rect_7x7;
+ }
+ break;
+ }
+ case SdrHdlKind::BezierWeight: // weight at poly
+ {
+ eKindOfMarker = BitmapMarkerKind::Circ_7x7;
+ break;
+ }
+ case SdrHdlKind::Circle:
+ {
+ eKindOfMarker = BitmapMarkerKind::Rect_11x11;
+ break;
+ }
+ case SdrHdlKind::Ref1:
+ case SdrHdlKind::Ref2:
+ {
+ eKindOfMarker = BitmapMarkerKind::Crosshair;
+ break;
+ }
+ case SdrHdlKind::Glue:
+ {
+ eKindOfMarker = BitmapMarkerKind::Glue;
+ break;
+ }
+ case SdrHdlKind::Anchor:
+ {
+ eKindOfMarker = BitmapMarkerKind::Anchor;
+ break;
+ }
+ case SdrHdlKind::User:
+ {
+ break;
+ }
+ // top right anchor for SW
+ case SdrHdlKind::Anchor_TR:
+ {
+ eKindOfMarker = BitmapMarkerKind::AnchorTR;
+ break;
+ }
+
+ // for SJ and the CustomShapeHandles:
+ case SdrHdlKind::CustomShape1:
+ {
+ eKindOfMarker = b1PixMore ? BitmapMarkerKind::Customshape_9x9 : BitmapMarkerKind::Customshape_7x7;
+ eColIndex = BitmapColorIndex::Yellow;
+ break;
+ }
+ default:
+ break;
+ }
+
+ SdrMarkView* pView = pHdlList->GetView();
+ SdrPageView* pPageView = pView->GetSdrPageView();
+
+ if(!pPageView)
+ return;
+
+ for(sal_uInt32 b(0); b < pPageView->PageWindowCount(); b++)
+ {
+ // const SdrPageViewWinRec& rPageViewWinRec = rPageViewWinList[b];
+ const SdrPageWindow& rPageWindow = *pPageView->GetPageWindow(b);
+
+ if(rPageWindow.GetPaintWindow().OutputToWindow())
+ {
+ Point aMoveOutsideOffset(0, 0);
+ OutputDevice& rOutDev = rPageWindow.GetPaintWindow().GetOutputDevice();
+
+ // add offset if necessary
+ if(pHdlList->IsMoveOutside() || mbMoveOutside)
+ {
+ Size aOffset = rOutDev.PixelToLogic(Size(4, 4));
+
+ if(eKind == SdrHdlKind::UpperLeft || eKind == SdrHdlKind::Upper || eKind == SdrHdlKind::UpperRight)
+ aMoveOutsideOffset.AdjustY( -(aOffset.Width()) );
+ if(eKind == SdrHdlKind::LowerLeft || eKind == SdrHdlKind::Lower || eKind == SdrHdlKind::LowerRight)
+ aMoveOutsideOffset.AdjustY(aOffset.Height() );
+ if(eKind == SdrHdlKind::UpperLeft || eKind == SdrHdlKind::Left || eKind == SdrHdlKind::LowerLeft)
+ aMoveOutsideOffset.AdjustX( -(aOffset.Width()) );
+ if(eKind == SdrHdlKind::UpperRight || eKind == SdrHdlKind::Right || eKind == SdrHdlKind::LowerRight)
+ aMoveOutsideOffset.AdjustX(aOffset.Height() );
+ }
+
+ const rtl::Reference< sdr::overlay::OverlayManager >& xManager = rPageWindow.GetOverlayManager();
+ if (xManager.is())
+ {
+ basegfx::B2DPoint aPosition(aPos.X(), aPos.Y());
+ std::unique_ptr<sdr::overlay::OverlayObject> pNewOverlayObject;
+ if (getenv ("SVX_DRAW_HANDLES") && (eKindOfMarker == BitmapMarkerKind::Rect_7x7 || eKindOfMarker == BitmapMarkerKind::Rect_9x9 || eKindOfMarker == BitmapMarkerKind::Rect_11x11))
+ {
+ double fSize = 7.0;
+ switch (eKindOfMarker)
+ {
+ case BitmapMarkerKind::Rect_9x9:
+ fSize = 9.0;
+ break;
+ case BitmapMarkerKind::Rect_11x11:
+ fSize = 11.0;
+ break;
+ default:
+ break;
+ }
+ float fScalingFactor = rOutDev.GetDPIScaleFactor();
+ basegfx::B2DSize aB2DSize(fSize * fScalingFactor, fSize * fScalingFactor);
+
+ Color aHandleFillColor(COL_LIGHTGREEN);
+ switch (eColIndex)
+ {
+ case BitmapColorIndex::Cyan:
+ aHandleFillColor = COL_CYAN;
+ break;
+ case BitmapColorIndex::LightCyan:
+ aHandleFillColor = COL_LIGHTCYAN;
+ break;
+ case BitmapColorIndex::Red:
+ aHandleFillColor = COL_RED;
+ break;
+ case BitmapColorIndex::LightRed:
+ aHandleFillColor = COL_LIGHTRED;
+ break;
+ case BitmapColorIndex::Yellow:
+ aHandleFillColor = COL_YELLOW;
+ break;
+ default:
+ break;
+ }
+ pNewOverlayObject.reset(new sdr::overlay::OverlayHandle(aPosition, aB2DSize, /*HandleStrokeColor*/COL_BLACK, aHandleFillColor));
+ }
+ else
+ {
+ pNewOverlayObject = CreateOverlayObject(
+ aPosition, eColIndex, eKindOfMarker,
+ aMoveOutsideOffset);
+ }
+
+ // OVERLAYMANAGER
+ insertNewlyCreatedOverlayObjectForSdrHdl(
+ std::move(pNewOverlayObject),
+ rPageWindow.GetObjectContact(),
+ *xManager);
+ }
+ }
+ }
+}
+
+BitmapMarkerKind SdrHdl::GetNextBigger(BitmapMarkerKind eKnd)
+{
+ BitmapMarkerKind eRetval(eKnd);
+
+ switch(eKnd)
+ {
+ case BitmapMarkerKind::Rect_7x7: eRetval = BitmapMarkerKind::Rect_9x9; break;
+ case BitmapMarkerKind::Rect_9x9: eRetval = BitmapMarkerKind::Rect_11x11; break;
+ case BitmapMarkerKind::Rect_11x11: eRetval = BitmapMarkerKind::Rect_13x13; break;
+
+ case BitmapMarkerKind::Circ_7x7: eRetval = BitmapMarkerKind::Circ_9x9; break;
+ case BitmapMarkerKind::Circ_9x9: eRetval = BitmapMarkerKind::Circ_11x11; break;
+
+ case BitmapMarkerKind::Customshape_7x7: eRetval = BitmapMarkerKind::Customshape_9x9; break;
+ case BitmapMarkerKind::Customshape_9x9: eRetval = BitmapMarkerKind::Customshape_11x11; break;
+ //case BitmapMarkerKind::Customshape_11x11: eRetval = ; break;
+
+ case BitmapMarkerKind::Elli_7x9: eRetval = BitmapMarkerKind::Elli_9x11; break;
+
+ case BitmapMarkerKind::Elli_9x7: eRetval = BitmapMarkerKind::Elli_11x9; break;
+
+ case BitmapMarkerKind::RectPlus_7x7: eRetval = BitmapMarkerKind::RectPlus_9x9; break;
+ case BitmapMarkerKind::RectPlus_9x9: eRetval = BitmapMarkerKind::RectPlus_11x11; break;
+
+ // let anchor blink with its pressed state
+ case BitmapMarkerKind::Anchor: eRetval = BitmapMarkerKind::AnchorPressed; break;
+
+ // same for AnchorTR
+ case BitmapMarkerKind::AnchorTR: eRetval = BitmapMarkerKind::AnchorPressedTR; break;
+ default:
+ break;
+ }
+
+ return eRetval;
+}
+
+namespace
+{
+
+OUString appendMarkerName(BitmapMarkerKind eKindOfMarker)
+{
+ switch(eKindOfMarker)
+ {
+ case BitmapMarkerKind::Rect_7x7:
+ return "rect7";
+ case BitmapMarkerKind::Rect_9x9:
+ return "rect9";
+ case BitmapMarkerKind::Rect_11x11:
+ return "rect11";
+ case BitmapMarkerKind::Rect_13x13:
+ return "rect13";
+ case BitmapMarkerKind::Circ_7x7:
+ case BitmapMarkerKind::Customshape_7x7:
+ return "circ7";
+ case BitmapMarkerKind::Circ_9x9:
+ case BitmapMarkerKind::Customshape_9x9:
+ return "circ9";
+ case BitmapMarkerKind::Circ_11x11:
+ case BitmapMarkerKind::Customshape_11x11:
+ return "circ11";
+ case BitmapMarkerKind::Elli_7x9:
+ return "elli7x9";
+ case BitmapMarkerKind::Elli_9x11:
+ return "elli9x11";
+ case BitmapMarkerKind::Elli_9x7:
+ return "elli9x7";
+ case BitmapMarkerKind::Elli_11x9:
+ return "elli11x9";
+ case BitmapMarkerKind::RectPlus_7x7:
+ return "rectplus7";
+ case BitmapMarkerKind::RectPlus_9x9:
+ return "rectplus9";
+ case BitmapMarkerKind::RectPlus_11x11:
+ return "rectplus11";
+ case BitmapMarkerKind::Crosshair:
+ return "cross";
+ case BitmapMarkerKind::Anchor:
+ case BitmapMarkerKind::AnchorTR:
+ return "anchor";
+ case BitmapMarkerKind::AnchorPressed:
+ case BitmapMarkerKind::AnchorPressedTR:
+ return "anchor-pressed";
+ case BitmapMarkerKind::Glue:
+ return "glue-selected";
+ case BitmapMarkerKind::Glue_Deselected:
+ return "glue-unselected";
+ default:
+ break;
+ }
+ return OUString();
+}
+
+OUString appendMarkerColor(BitmapColorIndex eIndex)
+{
+ switch(eIndex)
+ {
+ case BitmapColorIndex::LightGreen:
+ return "1";
+ case BitmapColorIndex::Cyan:
+ return "2";
+ case BitmapColorIndex::LightCyan:
+ return "3";
+ case BitmapColorIndex::Red:
+ return "4";
+ case BitmapColorIndex::LightRed:
+ return "5";
+ case BitmapColorIndex::Yellow:
+ return "6";
+ default:
+ break;
+ }
+ return OUString();
+}
+
+BitmapEx ImpGetBitmapEx(BitmapMarkerKind eKindOfMarker, BitmapColorIndex eIndex)
+{
+ // use this code path only when we use HiDPI (for now)
+ if (Application::GetDefaultDevice()->GetDPIScalePercentage() > 100)
+ {
+ OUString sMarkerName = appendMarkerName(eKindOfMarker);
+ if (!sMarkerName.isEmpty())
+ {
+ OUString sMarkerPrefix("svx/res/marker-");
+ BitmapEx aBitmapEx;
+
+ if (eKindOfMarker == BitmapMarkerKind::Crosshair
+ || eKindOfMarker == BitmapMarkerKind::Anchor
+ || eKindOfMarker == BitmapMarkerKind::AnchorTR
+ || eKindOfMarker == BitmapMarkerKind::AnchorPressed
+ || eKindOfMarker == BitmapMarkerKind::AnchorPressedTR
+ || eKindOfMarker == BitmapMarkerKind::Glue
+ || eKindOfMarker == BitmapMarkerKind::Glue_Deselected)
+ {
+ aBitmapEx = vcl::bitmap::loadFromName(sMarkerPrefix + sMarkerName + ".png");
+ }
+ else
+ {
+ aBitmapEx = vcl::bitmap::loadFromName(sMarkerPrefix + sMarkerName + "-" + appendMarkerColor(eIndex) + ".png");
+ }
+
+ if (!aBitmapEx.IsEmpty())
+ return aBitmapEx;
+ }
+ }
+
+ // if we can't load the marker...
+
+ static vcl::DeleteOnDeinit< SdrHdlBitmapSet > aModernSet {};
+ return aModernSet.get()->GetBitmapEx(eKindOfMarker, sal_uInt16(eIndex));
+}
+
+} // end anonymous namespace
+
+std::unique_ptr<sdr::overlay::OverlayObject> SdrHdl::CreateOverlayObject(
+ const basegfx::B2DPoint& rPos,
+ BitmapColorIndex eColIndex, BitmapMarkerKind eKindOfMarker, Point aMoveOutsideOffset)
+{
+ std::unique_ptr<sdr::overlay::OverlayObject> pRetval;
+
+ // support bigger sizes
+ bool bForceBiggerSize(false);
+
+ if (pHdlList && pHdlList->GetHdlSize() > 3)
+ {
+ switch(eKindOfMarker)
+ {
+ case BitmapMarkerKind::Anchor:
+ case BitmapMarkerKind::AnchorPressed:
+ case BitmapMarkerKind::AnchorTR:
+ case BitmapMarkerKind::AnchorPressedTR:
+ {
+ // #i121463# For anchor, do not simply make bigger because of HdlSize,
+ // do it dependent of IsSelected() which Writer can set in drag mode
+ if(IsSelected())
+ {
+ bForceBiggerSize = true;
+ }
+ break;
+ }
+ default:
+ {
+ bForceBiggerSize = true;
+ break;
+ }
+ }
+ }
+
+ if(bForceBiggerSize)
+ {
+ eKindOfMarker = GetNextBigger(eKindOfMarker);
+ }
+
+ // This handle has the focus, visualize it
+ if(IsFocusHdl() && pHdlList && pHdlList->GetFocusHdl() == this)
+ {
+ // create animated handle
+ BitmapMarkerKind eNextBigger = GetNextBigger(eKindOfMarker);
+
+ if(eNextBigger == eKindOfMarker)
+ {
+ // this may happen for the not supported getting-bigger types.
+ // Choose an alternative here
+ switch(eKindOfMarker)
+ {
+ case BitmapMarkerKind::Rect_13x13: eNextBigger = BitmapMarkerKind::Rect_11x11; break;
+ case BitmapMarkerKind::Circ_11x11: eNextBigger = BitmapMarkerKind::Elli_11x9; break;
+ case BitmapMarkerKind::Elli_9x11: eNextBigger = BitmapMarkerKind::Elli_11x9; break;
+ case BitmapMarkerKind::Elli_11x9: eNextBigger = BitmapMarkerKind::Elli_9x11; break;
+ case BitmapMarkerKind::RectPlus_11x11: eNextBigger = BitmapMarkerKind::Rect_13x13; break;
+
+ case BitmapMarkerKind::Crosshair:
+ eNextBigger = BitmapMarkerKind::Glue;
+ break;
+
+ case BitmapMarkerKind::Glue:
+ eNextBigger = BitmapMarkerKind::Crosshair;
+ break;
+ case BitmapMarkerKind::Glue_Deselected:
+ eNextBigger = BitmapMarkerKind::Glue;
+ break;
+ default:
+ break;
+ }
+ }
+
+ // create animated handle
+ BitmapEx aBmpEx1 = ImpGetBitmapEx(eKindOfMarker, eColIndex);
+ BitmapEx aBmpEx2 = ImpGetBitmapEx(eNextBigger, eColIndex);
+
+ // #i53216# Use system cursor blink time. Use the unsigned value.
+ const StyleSettings& rStyleSettings = Application::GetSettings().GetStyleSettings();
+ const sal_uInt64 nBlinkTime(rStyleSettings.GetCursorBlinkTime());
+
+ if(eKindOfMarker == BitmapMarkerKind::Anchor || eKindOfMarker == BitmapMarkerKind::AnchorPressed)
+ {
+ // when anchor is used take upper left as reference point inside the handle
+ pRetval.reset(new sdr::overlay::OverlayAnimatedBitmapEx(rPos, aBmpEx1, aBmpEx2, nBlinkTime));
+ }
+ else if(eKindOfMarker == BitmapMarkerKind::AnchorTR || eKindOfMarker == BitmapMarkerKind::AnchorPressedTR)
+ {
+ // AnchorTR for SW, take top right as (0,0)
+ pRetval.reset(new sdr::overlay::OverlayAnimatedBitmapEx(rPos, aBmpEx1, aBmpEx2, nBlinkTime,
+ static_cast<sal_uInt16>(aBmpEx1.GetSizePixel().Width() - 1), 0,
+ static_cast<sal_uInt16>(aBmpEx2.GetSizePixel().Width() - 1), 0));
+ }
+ else
+ {
+ // create centered handle as default
+ pRetval.reset(new sdr::overlay::OverlayAnimatedBitmapEx(rPos, aBmpEx1, aBmpEx2, nBlinkTime,
+ static_cast<sal_uInt16>(aBmpEx1.GetSizePixel().Width() - 1) >> 1,
+ static_cast<sal_uInt16>(aBmpEx1.GetSizePixel().Height() - 1) >> 1,
+ static_cast<sal_uInt16>(aBmpEx2.GetSizePixel().Width() - 1) >> 1,
+ static_cast<sal_uInt16>(aBmpEx2.GetSizePixel().Height() - 1) >> 1));
+ }
+ }
+ else
+ {
+ // create normal handle: use ImpGetBitmapEx(...) now
+ BitmapEx aBmpEx = ImpGetBitmapEx(eKindOfMarker, eColIndex);
+
+ // When the image with handles is not found, the bitmap returned is
+ // empty. This is a problem when we use LibreOffice as a library
+ // (through LOKit - for example on Android) even when we don't show
+ // the handles, because the hit test would always return false.
+ //
+ // This HACK replaces the empty bitmap with a black 13x13 bitmap handle
+ // so that the hit test works for this case.
+ if (aBmpEx.IsEmpty())
+ {
+ aBmpEx = BitmapEx(Size(13, 13), vcl::PixelFormat::N24_BPP);
+ aBmpEx.Erase(COL_BLACK);
+ }
+
+ if(eKindOfMarker == BitmapMarkerKind::Anchor || eKindOfMarker == BitmapMarkerKind::AnchorPressed)
+ {
+ // upper left as reference point inside the handle for AnchorPressed, too
+ pRetval.reset(new sdr::overlay::OverlayBitmapEx(rPos, aBmpEx));
+ }
+ else if(eKindOfMarker == BitmapMarkerKind::AnchorTR || eKindOfMarker == BitmapMarkerKind::AnchorPressedTR)
+ {
+ // AnchorTR for SW, take top right as (0,0)
+ pRetval.reset(new sdr::overlay::OverlayBitmapEx(rPos, aBmpEx,
+ static_cast<sal_uInt16>(aBmpEx.GetSizePixel().Width() - 1), 0));
+ }
+ else
+ {
+ sal_uInt16 nCenX(static_cast<sal_uInt16>(aBmpEx.GetSizePixel().Width() - 1) >> 1);
+ sal_uInt16 nCenY(static_cast<sal_uInt16>(aBmpEx.GetSizePixel().Height() - 1) >> 1);
+
+ if(aMoveOutsideOffset.X() > 0)
+ {
+ nCenX = 0;
+ }
+ else if(aMoveOutsideOffset.X() < 0)
+ {
+ nCenX = static_cast<sal_uInt16>(aBmpEx.GetSizePixel().Width() - 1);
+ }
+
+ if(aMoveOutsideOffset.Y() > 0)
+ {
+ nCenY = 0;
+ }
+ else if(aMoveOutsideOffset.Y() < 0)
+ {
+ nCenY = static_cast<sal_uInt16>(aBmpEx.GetSizePixel().Height() - 1);
+ }
+
+ // create centered handle as default
+ pRetval.reset(new sdr::overlay::OverlayBitmapEx(rPos, aBmpEx, nCenX, nCenY));
+ }
+ }
+
+ return pRetval;
+}
+
+bool SdrHdl::IsHdlHit(const Point& rPnt) const
+{
+ // OVERLAYMANAGER
+ basegfx::B2DPoint aPosition(rPnt.X(), rPnt.Y());
+ return maOverlayGroup.isHitLogic(aPosition);
+}
+
+PointerStyle SdrHdl::GetPointer() const
+{
+ PointerStyle ePtr=PointerStyle::Move;
+ const bool bSize=eKind>=SdrHdlKind::UpperLeft && eKind<=SdrHdlKind::LowerRight;
+ const bool bRot=pHdlList!=nullptr && pHdlList->IsRotateShear();
+ const bool bDis=pHdlList!=nullptr && pHdlList->IsDistortShear();
+ if (bSize && pHdlList!=nullptr && (bRot || bDis)) {
+ switch (eKind) {
+ case SdrHdlKind::UpperLeft: case SdrHdlKind::UpperRight:
+ case SdrHdlKind::LowerLeft: case SdrHdlKind::LowerRight: ePtr=bRot ? PointerStyle::Rotate : PointerStyle::RefHand; break;
+ case SdrHdlKind::Left : case SdrHdlKind::Right: ePtr=PointerStyle::VShear; break;
+ case SdrHdlKind::Upper: case SdrHdlKind::Lower: ePtr=PointerStyle::HShear; break;
+ default:
+ break;
+ }
+ } else {
+ // When resizing rotated rectangles, rotate the mouse cursor slightly, too
+ if (bSize && nRotationAngle!=0_deg100) {
+ Degree100 nHdlAngle(0);
+ switch (eKind) {
+ case SdrHdlKind::LowerRight: nHdlAngle=31500_deg100; break;
+ case SdrHdlKind::Lower: nHdlAngle=27000_deg100; break;
+ case SdrHdlKind::LowerLeft: nHdlAngle=22500_deg100; break;
+ case SdrHdlKind::Left : nHdlAngle=18000_deg100; break;
+ case SdrHdlKind::UpperLeft: nHdlAngle=13500_deg100; break;
+ case SdrHdlKind::Upper: nHdlAngle=9000_deg100; break;
+ case SdrHdlKind::UpperRight: nHdlAngle=4500_deg100; break;
+ case SdrHdlKind::Right: nHdlAngle=0_deg100; break;
+ default:
+ break;
+ }
+ // a little bit more (for rounding)
+ nHdlAngle = NormAngle36000(nHdlAngle + nRotationAngle + 2249_deg100);
+ nHdlAngle/=4500_deg100;
+ switch (static_cast<sal_uInt8>(nHdlAngle.get())) {
+ case 0: ePtr=PointerStyle::ESize; break;
+ case 1: ePtr=PointerStyle::NESize; break;
+ case 2: ePtr=PointerStyle::NSize; break;
+ case 3: ePtr=PointerStyle::NWSize; break;
+ case 4: ePtr=PointerStyle::WSize; break;
+ case 5: ePtr=PointerStyle::SWSize; break;
+ case 6: ePtr=PointerStyle::SSize; break;
+ case 7: ePtr=PointerStyle::SESize; break;
+ } // switch
+ } else {
+ switch (eKind) {
+ case SdrHdlKind::UpperLeft: ePtr=PointerStyle::NWSize; break;
+ case SdrHdlKind::Upper: ePtr=PointerStyle::NSize; break;
+ case SdrHdlKind::UpperRight: ePtr=PointerStyle::NESize; break;
+ case SdrHdlKind::Left : ePtr=PointerStyle::WSize; break;
+ case SdrHdlKind::Right: ePtr=PointerStyle::ESize; break;
+ case SdrHdlKind::LowerLeft: ePtr=PointerStyle::SWSize; break;
+ case SdrHdlKind::Lower: ePtr=PointerStyle::SSize; break;
+ case SdrHdlKind::LowerRight: ePtr=PointerStyle::SESize; break;
+ case SdrHdlKind::Poly : ePtr=PointerStyle::MovePoint; break;
+ case SdrHdlKind::Circle : ePtr=PointerStyle::Hand; break;
+ case SdrHdlKind::Ref1 : ePtr=PointerStyle::RefHand; break;
+ case SdrHdlKind::Ref2 : ePtr=PointerStyle::RefHand; break;
+ case SdrHdlKind::BezierWeight : ePtr=PointerStyle::MoveBezierWeight; break;
+ case SdrHdlKind::Glue : ePtr=PointerStyle::MovePoint; break;
+ case SdrHdlKind::CustomShape1 : ePtr=PointerStyle::Hand; break;
+ default:
+ break;
+ }
+ }
+ }
+ return ePtr;
+}
+
+bool SdrHdl::IsFocusHdl() const
+{
+ switch(eKind)
+ {
+ case SdrHdlKind::UpperLeft:
+ case SdrHdlKind::Upper:
+ case SdrHdlKind::UpperRight:
+ case SdrHdlKind::Left:
+ case SdrHdlKind::Right:
+ case SdrHdlKind::LowerLeft:
+ case SdrHdlKind::Lower:
+ case SdrHdlKind::LowerRight:
+ {
+ // if it's an activated TextEdit, it's moved to extended points
+ return !pHdlList || !pHdlList->IsMoveOutside();
+ }
+
+ case SdrHdlKind::Move: // handle to move object
+ case SdrHdlKind::Poly: // selected point of polygon or curve
+ case SdrHdlKind::BezierWeight: // weight at a curve
+ case SdrHdlKind::Circle: // angle of circle segments, corner radius of rectangles
+ case SdrHdlKind::Ref1: // reference point 1, e. g. center of rotation
+ case SdrHdlKind::Ref2: // reference point 2, e. g. endpoint of reflection axis
+ case SdrHdlKind::Glue: // gluepoint
+
+ // for SJ and the CustomShapeHandles:
+ case SdrHdlKind::CustomShape1:
+
+ case SdrHdlKind::User:
+ {
+ return true;
+ }
+
+ default:
+ {
+ return false;
+ }
+ }
+}
+
+void SdrHdl::onMouseEnter(const MouseEvent& /*rMEvt*/)
+{
+}
+
+void SdrHdl::onHelpRequest()
+{
+}
+
+void SdrHdl::onMouseLeave()
+{
+}
+
+BitmapEx SdrHdl::createGluePointBitmap()
+{
+ return ImpGetBitmapEx(BitmapMarkerKind::Glue_Deselected, BitmapColorIndex::LightGreen);
+}
+
+void SdrHdl::insertNewlyCreatedOverlayObjectForSdrHdl(
+ std::unique_ptr<sdr::overlay::OverlayObject> pOverlayObject,
+ const sdr::contact::ObjectContact& rObjectContact,
+ sdr::overlay::OverlayManager& rOverlayManager)
+{
+ // check if we have an OverlayObject
+ if(!pOverlayObject)
+ {
+ return;
+ }
+
+ // Add GridOffset for non-linear ViewToDevice transformation (calc)
+ if(nullptr != GetObj() && rObjectContact.supportsGridOffsets())
+ {
+ basegfx::B2DVector aOffset(0.0, 0.0);
+ const sdr::contact::ViewObjectContact& rVOC(GetObj()->GetViewContact().GetViewObjectContact(
+ const_cast<sdr::contact::ObjectContact&>(rObjectContact)));
+
+ rObjectContact.calculateGridOffsetForViewOjectContact(aOffset, rVOC);
+
+ if(!aOffset.equalZero())
+ {
+ pOverlayObject->setOffset(aOffset);
+ }
+ }
+
+ // add to OverlayManager
+ rOverlayManager.add(*pOverlayObject);
+
+ // add to local OverlayObjectList - ownership change (!)
+ maOverlayGroup.append(std::move(pOverlayObject));
+}
+
+SdrHdlColor::SdrHdlColor(const Point& rRef, Color aCol, const Size& rSize, bool bLum)
+: SdrHdl(rRef, SdrHdlKind::Color),
+ aMarkerSize(rSize),
+ bUseLuminance(bLum)
+{
+ if(IsUseLuminance())
+ aCol = GetLuminance(aCol);
+
+ // remember color
+ aMarkerColor = aCol;
+}
+
+SdrHdlColor::~SdrHdlColor()
+{
+}
+
+void SdrHdlColor::CreateB2dIAObject()
+{
+ // first throw away old one
+ GetRidOfIAObject();
+
+ if(!pHdlList)
+ return;
+
+ SdrMarkView* pView = pHdlList->GetView();
+
+ if(!pView || pView->areMarkHandlesHidden())
+ return;
+
+ SdrPageView* pPageView = pView->GetSdrPageView();
+
+ if(!pPageView)
+ return;
+
+ for(sal_uInt32 b(0); b < pPageView->PageWindowCount(); b++)
+ {
+ const SdrPageWindow& rPageWindow = *pPageView->GetPageWindow(b);
+
+ if(rPageWindow.GetPaintWindow().OutputToWindow())
+ {
+ const rtl::Reference< sdr::overlay::OverlayManager >& xManager = rPageWindow.GetOverlayManager();
+ if (xManager.is())
+ {
+ BitmapEx aBmpCol(CreateColorDropper(aMarkerColor));
+ basegfx::B2DPoint aPosition(aPos.X(), aPos.Y());
+ std::unique_ptr<sdr::overlay::OverlayObject> pNewOverlayObject(new
+ sdr::overlay::OverlayBitmapEx(
+ aPosition,
+ aBmpCol,
+ static_cast<sal_uInt16>(aBmpCol.GetSizePixel().Width() - 1) >> 1,
+ static_cast<sal_uInt16>(aBmpCol.GetSizePixel().Height() - 1) >> 1
+ ));
+
+ // OVERLAYMANAGER
+ insertNewlyCreatedOverlayObjectForSdrHdl(
+ std::move(pNewOverlayObject),
+ rPageWindow.GetObjectContact(),
+ *xManager);
+ }
+ }
+ }
+}
+
+BitmapEx SdrHdlColor::CreateColorDropper(Color aCol)
+{
+ // get the Bitmap
+ VclPtr<VirtualDevice> pWrite(VclPtr<VirtualDevice>::Create());
+ pWrite->SetOutputSizePixel(aMarkerSize);
+ pWrite->SetBackground(aCol);
+ pWrite->Erase();
+
+ // draw outer border
+ sal_Int32 nWidth = aMarkerSize.Width();
+ sal_Int32 nHeight = aMarkerSize.Height();
+
+ pWrite->SetLineColor(COL_LIGHTGRAY);
+ pWrite->DrawLine(Point(0, 0), Point(0, nHeight - 1));
+ pWrite->DrawLine(Point(1, 0), Point(nWidth - 1, 0));
+ pWrite->SetLineColor(COL_GRAY);
+ pWrite->DrawLine(Point(1, nHeight - 1), Point(nWidth - 1, nHeight - 1));
+ pWrite->DrawLine(Point(nWidth - 1, 1), Point(nWidth - 1, nHeight - 2));
+
+ // draw lighter UpperLeft
+ const Color aLightColor(
+ static_cast<sal_uInt8>(::std::min(static_cast<sal_Int16>(static_cast<sal_Int16>(aCol.GetRed()) + sal_Int16(0x0040)), sal_Int16(0x00ff))),
+ static_cast<sal_uInt8>(::std::min(static_cast<sal_Int16>(static_cast<sal_Int16>(aCol.GetGreen()) + sal_Int16(0x0040)), sal_Int16(0x00ff))),
+ static_cast<sal_uInt8>(::std::min(static_cast<sal_Int16>(static_cast<sal_Int16>(aCol.GetBlue()) + sal_Int16(0x0040)), sal_Int16(0x00ff))));
+ pWrite->SetLineColor(aLightColor);
+ pWrite->DrawLine(Point(1, 1), Point(1, nHeight - 2));
+ pWrite->DrawLine(Point(2, 1), Point(nWidth - 2, 1));
+
+ // draw darker LowerRight
+ const Color aDarkColor(
+ static_cast<sal_uInt8>(::std::max(static_cast<sal_Int16>(static_cast<sal_Int16>(aCol.GetRed()) - sal_Int16(0x0040)), sal_Int16(0x0000))),
+ static_cast<sal_uInt8>(::std::max(static_cast<sal_Int16>(static_cast<sal_Int16>(aCol.GetGreen()) - sal_Int16(0x0040)), sal_Int16(0x0000))),
+ static_cast<sal_uInt8>(::std::max(static_cast<sal_Int16>(static_cast<sal_Int16>(aCol.GetBlue()) - sal_Int16(0x0040)), sal_Int16(0x0000))));
+ pWrite->SetLineColor(aDarkColor);
+ pWrite->DrawLine(Point(2, nHeight - 2), Point(nWidth - 2, nHeight - 2));
+ pWrite->DrawLine(Point(nWidth - 2, 2), Point(nWidth - 2, nHeight - 3));
+
+ return pWrite->GetBitmapEx(Point(0,0), aMarkerSize);
+}
+
+Color SdrHdlColor::GetLuminance(const Color& rCol)
+{
+ sal_uInt8 aLum = rCol.GetLuminance();
+ Color aRetval(aLum, aLum, aLum);
+ return aRetval;
+}
+
+void SdrHdlColor::SetColor(Color aNew, bool bCallLink)
+{
+ if(IsUseLuminance())
+ aNew = GetLuminance(aNew);
+
+ if(aMarkerColor != aNew)
+ {
+ // remember new color
+ aMarkerColor = aNew;
+
+ // create new display
+ Touch();
+
+ // tell about change
+ if(bCallLink)
+ aColorChangeHdl.Call(this);
+ }
+}
+
+void SdrHdlColor::SetSize(const Size& rNew)
+{
+ if(rNew != aMarkerSize)
+ {
+ // remember new size
+ aMarkerSize = rNew;
+
+ // create new display
+ Touch();
+ }
+}
+
+SdrHdlGradient::SdrHdlGradient(const Point& rRef1, const Point& rRef2, bool bGrad)
+ : SdrHdl(rRef1, bGrad ? SdrHdlKind::Gradient : SdrHdlKind::Transparence)
+ , pColHdl1(nullptr)
+ , pColHdl2(nullptr)
+ , a2ndPos(rRef2)
+ , bGradient(bGrad)
+ , bMoveSingleHandle(false)
+ , bMoveFirstHandle(false)
+{
+}
+
+SdrHdlGradient::~SdrHdlGradient()
+{
+}
+
+void SdrHdlGradient::Set2ndPos(const Point& rPnt)
+{
+ if(a2ndPos != rPnt)
+ {
+ // remember new position
+ a2ndPos = rPnt;
+
+ // create new display
+ Touch();
+ }
+}
+
+void SdrHdlGradient::CreateB2dIAObject()
+{
+ // first throw away old one
+ GetRidOfIAObject();
+
+ if(!pHdlList)
+ return;
+
+ SdrMarkView* pView = pHdlList->GetView();
+
+ if(!pView || pView->areMarkHandlesHidden())
+ return;
+
+ SdrPageView* pPageView = pView->GetSdrPageView();
+
+ if(!pPageView)
+ return;
+
+ for(sal_uInt32 b(0); b < pPageView->PageWindowCount(); b++)
+ {
+ const SdrPageWindow& rPageWindow = *pPageView->GetPageWindow(b);
+
+ if(rPageWindow.GetPaintWindow().OutputToWindow())
+ {
+ const rtl::Reference< sdr::overlay::OverlayManager >& xManager = rPageWindow.GetOverlayManager();
+ if (xManager.is())
+ {
+ // striped line in between
+ basegfx::B2DVector aVec(a2ndPos.X() - aPos.X(), a2ndPos.Y() - aPos.Y());
+ double fVecLen = aVec.getLength();
+ double fLongPercentArrow = (1.0 - 0.05) * fVecLen;
+ double fHalfArrowWidth = (0.05 * 0.5) * fVecLen;
+ aVec.normalize();
+ basegfx::B2DVector aPerpend(-aVec.getY(), aVec.getX());
+ sal_Int32 nMidX = static_cast<sal_Int32>(aPos.X() + aVec.getX() * fLongPercentArrow);
+ sal_Int32 nMidY = static_cast<sal_Int32>(aPos.Y() + aVec.getY() * fLongPercentArrow);
+ Point aMidPoint(nMidX, nMidY);
+
+ basegfx::B2DPoint aPosition(aPos.X(), aPos.Y());
+ basegfx::B2DPoint aMidPos(aMidPoint.X(), aMidPoint.Y());
+
+ std::unique_ptr<sdr::overlay::OverlayObject> pNewOverlayObject(new
+ sdr::overlay::OverlayLineStriped(
+ aPosition, aMidPos
+ ));
+
+ pNewOverlayObject->setBaseColor(IsGradient() ? COL_BLACK : COL_BLUE);
+
+ // OVERLAYMANAGER
+ insertNewlyCreatedOverlayObjectForSdrHdl(
+ std::move(pNewOverlayObject),
+ rPageWindow.GetObjectContact(),
+ *xManager);
+
+ // arrowhead
+ Point aLeft(aMidPoint.X() + static_cast<sal_Int32>(aPerpend.getX() * fHalfArrowWidth),
+ aMidPoint.Y() + static_cast<sal_Int32>(aPerpend.getY() * fHalfArrowWidth));
+ Point aRight(aMidPoint.X() - static_cast<sal_Int32>(aPerpend.getX() * fHalfArrowWidth),
+ aMidPoint.Y() - static_cast<sal_Int32>(aPerpend.getY() * fHalfArrowWidth));
+
+ basegfx::B2DPoint aPositionLeft(aLeft.X(), aLeft.Y());
+ basegfx::B2DPoint aPositionRight(aRight.X(), aRight.Y());
+ basegfx::B2DPoint aPosition2(a2ndPos.X(), a2ndPos.Y());
+
+ pNewOverlayObject.reset(new
+ sdr::overlay::OverlayTriangle(
+ aPositionLeft,
+ aPosition2,
+ aPositionRight,
+ IsGradient() ? COL_BLACK : COL_BLUE
+ ));
+
+ // OVERLAYMANAGER
+ insertNewlyCreatedOverlayObjectForSdrHdl(
+ std::move(pNewOverlayObject),
+ rPageWindow.GetObjectContact(),
+ *xManager);
+ }
+ }
+ }
+}
+
+IMPL_LINK_NOARG(SdrHdlGradient, ColorChangeHdl, SdrHdlColor*, void)
+{
+ if(GetObj())
+ FromIAOToItem(GetObj(), true, true);
+}
+
+void SdrHdlGradient::FromIAOToItem(SdrObject* _pObj, bool bSetItemOnObject, bool bUndo)
+{
+ // from IAO positions and colors to gradient
+ const SfxItemSet& rSet = _pObj->GetMergedItemSet();
+
+ GradTransGradient aOldGradTransGradient;
+ GradTransGradient aGradTransGradient;
+ GradTransVector aGradTransVector;
+
+ aGradTransVector.maPositionA = basegfx::B2DPoint(GetPos().X(), GetPos().Y());
+ aGradTransVector.maPositionB = basegfx::B2DPoint(Get2ndPos().X(), Get2ndPos().Y());
+ if(pColHdl1)
+ aGradTransVector.aCol1 = pColHdl1->GetColor();
+ if(pColHdl2)
+ aGradTransVector.aCol2 = pColHdl2->GetColor();
+
+ if(IsGradient())
+ aOldGradTransGradient.aGradient = rSet.Get(XATTR_FILLGRADIENT).GetGradientValue();
+ else
+ aOldGradTransGradient.aGradient = rSet.Get(XATTR_FILLFLOATTRANSPARENCE).GetGradientValue();
+
+ // transform vector data to gradient
+ GradTransformer::VecToGrad(aGradTransVector, aGradTransGradient, aOldGradTransGradient, _pObj, bMoveSingleHandle, bMoveFirstHandle);
+
+ if(bSetItemOnObject)
+ {
+ SdrModel& rModel(_pObj->getSdrModelFromSdrObject());
+ SfxItemSet aNewSet(rModel.GetItemPool());
+ const OUString aString;
+
+ if(IsGradient())
+ {
+ XFillGradientItem aNewGradItem(aString, aGradTransGradient.aGradient);
+ aNewSet.Put(aNewGradItem);
+ }
+ else
+ {
+ XFillFloatTransparenceItem aNewTransItem(aString, aGradTransGradient.aGradient);
+ aNewSet.Put(aNewTransItem);
+ }
+
+ if(bUndo && rModel.IsUndoEnabled())
+ {
+ rModel.BegUndo(SvxResId(IsGradient() ? SIP_XA_FILLGRADIENT : SIP_XA_FILLTRANSPARENCE));
+ rModel.AddUndo(rModel.GetSdrUndoFactory().CreateUndoAttrObject(*_pObj));
+ rModel.EndUndo();
+ }
+
+ pObj->SetMergedItemSetAndBroadcast(aNewSet);
+ }
+
+ // back transformation, set values on pIAOHandle
+ GradTransformer::GradToVec(aGradTransGradient, aGradTransVector, _pObj);
+
+ SetPos(Point(FRound(aGradTransVector.maPositionA.getX()), FRound(aGradTransVector.maPositionA.getY())));
+ Set2ndPos(Point(FRound(aGradTransVector.maPositionB.getX()), FRound(aGradTransVector.maPositionB.getY())));
+ if(pColHdl1)
+ {
+ pColHdl1->SetPos(Point(FRound(aGradTransVector.maPositionA.getX()), FRound(aGradTransVector.maPositionA.getY())));
+ pColHdl1->SetColor(aGradTransVector.aCol1);
+ }
+ if(pColHdl2)
+ {
+ pColHdl2->SetPos(Point(FRound(aGradTransVector.maPositionB.getX()), FRound(aGradTransVector.maPositionB.getY())));
+ pColHdl2->SetColor(aGradTransVector.aCol2);
+ }
+}
+
+
+SdrHdlLine::~SdrHdlLine() {}
+
+void SdrHdlLine::CreateB2dIAObject()
+{
+ // first throw away old one
+ GetRidOfIAObject();
+
+ if(!pHdlList)
+ return;
+
+ SdrMarkView* pView = pHdlList->GetView();
+
+ if(!(pView && !pView->areMarkHandlesHidden() && pHdl1 && pHdl2))
+ return;
+
+ SdrPageView* pPageView = pView->GetSdrPageView();
+
+ if(!pPageView)
+ return;
+
+ for(sal_uInt32 b(0); b < pPageView->PageWindowCount(); b++)
+ {
+ const SdrPageWindow& rPageWindow = *pPageView->GetPageWindow(b);
+
+ if(rPageWindow.GetPaintWindow().OutputToWindow())
+ {
+ const rtl::Reference< sdr::overlay::OverlayManager >& xManager = rPageWindow.GetOverlayManager();
+ if (xManager.is())
+ {
+ basegfx::B2DPoint aPosition1(pHdl1->GetPos().X(), pHdl1->GetPos().Y());
+ basegfx::B2DPoint aPosition2(pHdl2->GetPos().X(), pHdl2->GetPos().Y());
+
+ std::unique_ptr<sdr::overlay::OverlayObject> pNewOverlayObject(new
+ sdr::overlay::OverlayLineStriped(
+ aPosition1,
+ aPosition2
+ ));
+
+ // color(?)
+ pNewOverlayObject->setBaseColor(COL_LIGHTRED);
+
+ // OVERLAYMANAGER
+ insertNewlyCreatedOverlayObjectForSdrHdl(
+ std::move(pNewOverlayObject),
+ rPageWindow.GetObjectContact(),
+ *xManager);
+ }
+ }
+ }
+}
+
+PointerStyle SdrHdlLine::GetPointer() const
+{
+ return PointerStyle::RefHand;
+}
+
+
+SdrHdlBezWgt::~SdrHdlBezWgt() {}
+
+void SdrHdlBezWgt::CreateB2dIAObject()
+{
+ // call parent
+ SdrHdl::CreateB2dIAObject();
+
+ // create lines
+ if(!pHdlList)
+ return;
+
+ SdrMarkView* pView = pHdlList->GetView();
+
+ if(!pView || pView->areMarkHandlesHidden())
+ return;
+
+ SdrPageView* pPageView = pView->GetSdrPageView();
+
+ if(!pPageView)
+ return;
+
+ for(sal_uInt32 b(0); b < pPageView->PageWindowCount(); b++)
+ {
+ const SdrPageWindow& rPageWindow = *pPageView->GetPageWindow(b);
+
+ if(rPageWindow.GetPaintWindow().OutputToWindow())
+ {
+ const rtl::Reference< sdr::overlay::OverlayManager >& xManager = rPageWindow.GetOverlayManager();
+ if (xManager.is())
+ {
+ basegfx::B2DPoint aPosition1(pHdl1->GetPos().X(), pHdl1->GetPos().Y());
+ basegfx::B2DPoint aPosition2(aPos.X(), aPos.Y());
+
+ if(!aPosition1.equal(aPosition2))
+ {
+ std::unique_ptr<sdr::overlay::OverlayObject> pNewOverlayObject(new
+ sdr::overlay::OverlayLineStriped(
+ aPosition1,
+ aPosition2
+ ));
+
+ // line part is not hittable
+ pNewOverlayObject->setHittable(false);
+
+ // color(?)
+ pNewOverlayObject->setBaseColor(COL_LIGHTBLUE);
+
+ // OVERLAYMANAGER
+ insertNewlyCreatedOverlayObjectForSdrHdl(
+ std::move(pNewOverlayObject),
+ rPageWindow.GetObjectContact(),
+ *xManager);
+ }
+ }
+ }
+ }
+}
+
+
+E3dVolumeMarker::E3dVolumeMarker(const basegfx::B2DPolyPolygon& rWireframePoly)
+{
+ aWireframePoly = rWireframePoly;
+}
+
+void E3dVolumeMarker::CreateB2dIAObject()
+{
+ // create lines
+ if(!pHdlList)
+ return;
+
+ SdrMarkView* pView = pHdlList->GetView();
+
+ if(!pView || pView->areMarkHandlesHidden())
+ return;
+
+ SdrPageView* pPageView = pView->GetSdrPageView();
+
+ if(!pPageView)
+ return;
+
+ for(sal_uInt32 b(0); b < pPageView->PageWindowCount(); b++)
+ {
+ const SdrPageWindow& rPageWindow = *pPageView->GetPageWindow(b);
+
+ if(rPageWindow.GetPaintWindow().OutputToWindow())
+ {
+ const rtl::Reference< sdr::overlay::OverlayManager >& xManager = rPageWindow.GetOverlayManager();
+ if (xManager.is() && aWireframePoly.count())
+ {
+ std::unique_ptr<sdr::overlay::OverlayObject> pNewOverlayObject(new
+ sdr::overlay::OverlayPolyPolygonStripedAndFilled(
+ aWireframePoly));
+
+ pNewOverlayObject->setBaseColor(COL_BLACK);
+
+ // OVERLAYMANAGER
+ insertNewlyCreatedOverlayObjectForSdrHdl(
+ std::move(pNewOverlayObject),
+ rPageWindow.GetObjectContact(),
+ *xManager);
+ }
+ }
+ }
+}
+
+
+ImpEdgeHdl::~ImpEdgeHdl()
+{
+}
+
+void ImpEdgeHdl::CreateB2dIAObject()
+{
+ if(nObjHdlNum <= 1 && pObj)
+ {
+ // first throw away old one
+ GetRidOfIAObject();
+
+ BitmapColorIndex eColIndex = BitmapColorIndex::LightCyan;
+ BitmapMarkerKind eKindOfMarker = BitmapMarkerKind::Rect_7x7;
+
+ if(pHdlList)
+ {
+ SdrMarkView* pView = pHdlList->GetView();
+
+ if(pView && !pView->areMarkHandlesHidden())
+ {
+ const SdrEdgeObj* pEdge = static_cast<SdrEdgeObj*>(pObj);
+
+ if(pEdge->GetConnectedNode(nObjHdlNum == 0) != nullptr)
+ eColIndex = BitmapColorIndex::LightRed;
+
+ if(nPPntNum < 2)
+ {
+ // Handle with plus sign inside
+ eKindOfMarker = BitmapMarkerKind::Circ_7x7;
+ }
+
+ SdrPageView* pPageView = pView->GetSdrPageView();
+
+ if(pPageView)
+ {
+ for(sal_uInt32 b(0); b < pPageView->PageWindowCount(); b++)
+ {
+ const SdrPageWindow& rPageWindow = *pPageView->GetPageWindow(b);
+
+ if(rPageWindow.GetPaintWindow().OutputToWindow())
+ {
+ const rtl::Reference< sdr::overlay::OverlayManager >& xManager = rPageWindow.GetOverlayManager();
+ if (xManager.is())
+ {
+ basegfx::B2DPoint aPosition(aPos.X(), aPos.Y());
+ std::unique_ptr<sdr::overlay::OverlayObject> pNewOverlayObject(CreateOverlayObject(
+ aPosition,
+ eColIndex,
+ eKindOfMarker));
+
+ // OVERLAYMANAGER
+ insertNewlyCreatedOverlayObjectForSdrHdl(
+ std::move(pNewOverlayObject),
+ rPageWindow.GetObjectContact(),
+ *xManager);
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ else
+ {
+ // call parent
+ SdrHdl::CreateB2dIAObject();
+ }
+}
+
+void ImpEdgeHdl::SetLineCode(SdrEdgeLineCode eCode)
+{
+ if(eLineCode != eCode)
+ {
+ // remember new value
+ eLineCode = eCode;
+
+ // create new display
+ Touch();
+ }
+}
+
+PointerStyle ImpEdgeHdl::GetPointer() const
+{
+ SdrEdgeObj* pEdge=dynamic_cast<SdrEdgeObj*>( pObj );
+ if (pEdge==nullptr)
+ return SdrHdl::GetPointer();
+ if (nObjHdlNum<=1)
+ return PointerStyle::MovePoint;
+ if (IsHorzDrag())
+ return PointerStyle::ESize;
+ else
+ return PointerStyle::SSize;
+}
+
+bool ImpEdgeHdl::IsHorzDrag() const
+{
+ SdrEdgeObj* pEdge=dynamic_cast<SdrEdgeObj*>( pObj );
+ if (pEdge==nullptr)
+ return false;
+ if (nObjHdlNum<=1)
+ return false;
+
+ SdrEdgeKind eEdgeKind = pEdge->GetObjectItem(SDRATTR_EDGEKIND).GetValue();
+
+ const SdrEdgeInfoRec& rInfo=pEdge->aEdgeInfo;
+ if (eEdgeKind==SdrEdgeKind::OrthoLines || eEdgeKind==SdrEdgeKind::Bezier)
+ {
+ return !rInfo.ImpIsHorzLine(eLineCode,*pEdge->pEdgeTrack);
+ }
+ else if (eEdgeKind==SdrEdgeKind::ThreeLines)
+ {
+ tools::Long nAngle=nObjHdlNum==2 ? rInfo.nAngle1 : rInfo.nAngle2;
+ return nAngle==0 || nAngle==18000;
+ }
+ return false;
+}
+
+
+ImpMeasureHdl::~ImpMeasureHdl()
+{
+}
+
+void ImpMeasureHdl::CreateB2dIAObject()
+{
+ // first throw away old one
+ GetRidOfIAObject();
+
+ if(!pHdlList)
+ return;
+
+ SdrMarkView* pView = pHdlList->GetView();
+
+ if(!pView || pView->areMarkHandlesHidden())
+ return;
+
+ BitmapColorIndex eColIndex = BitmapColorIndex::LightCyan;
+ BitmapMarkerKind eKindOfMarker = BitmapMarkerKind::Rect_9x9;
+
+ if(nObjHdlNum > 1)
+ {
+ eKindOfMarker = BitmapMarkerKind::Rect_7x7;
+ }
+
+ if(bSelect)
+ {
+ eColIndex = BitmapColorIndex::Cyan;
+ }
+
+ SdrPageView* pPageView = pView->GetSdrPageView();
+
+ if(!pPageView)
+ return;
+
+ for(sal_uInt32 b(0); b < pPageView->PageWindowCount(); b++)
+ {
+ const SdrPageWindow& rPageWindow = *pPageView->GetPageWindow(b);
+
+ if(rPageWindow.GetPaintWindow().OutputToWindow())
+ {
+ const rtl::Reference< sdr::overlay::OverlayManager >& xManager = rPageWindow.GetOverlayManager();
+ if (xManager.is())
+ {
+ basegfx::B2DPoint aPosition(aPos.X(), aPos.Y());
+ std::unique_ptr<sdr::overlay::OverlayObject> pNewOverlayObject(CreateOverlayObject(
+ aPosition,
+ eColIndex,
+ eKindOfMarker));
+
+ // OVERLAYMANAGER
+ insertNewlyCreatedOverlayObjectForSdrHdl(
+ std::move(pNewOverlayObject),
+ rPageWindow.GetObjectContact(),
+ *xManager);
+ }
+ }
+ }
+}
+
+PointerStyle ImpMeasureHdl::GetPointer() const
+{
+ switch (nObjHdlNum)
+ {
+ case 0: case 1: return PointerStyle::Hand;
+ case 2: case 3: return PointerStyle::MovePoint;
+ case 4: case 5: return SdrHdl::GetPointer(); // will then be rotated appropriately
+ } // switch
+ return PointerStyle::NotAllowed;
+}
+
+
+ImpTextframeHdl::ImpTextframeHdl(const tools::Rectangle& rRect) :
+ SdrHdl(rRect.TopLeft(),SdrHdlKind::Move),
+ maRect(rRect)
+{
+}
+
+void ImpTextframeHdl::CreateB2dIAObject()
+{
+ // first throw away old one
+ GetRidOfIAObject();
+
+ if(!pHdlList)
+ return;
+
+ SdrMarkView* pView = pHdlList->GetView();
+
+ if(!pView || pView->areMarkHandlesHidden())
+ return;
+
+ SdrPageView* pPageView = pView->GetSdrPageView();
+
+ if(!pPageView)
+ return;
+
+ for(sal_uInt32 b(0); b < pPageView->PageWindowCount(); b++)
+ {
+ const SdrPageWindow& rPageWindow = *pPageView->GetPageWindow(b);
+
+ if(rPageWindow.GetPaintWindow().OutputToWindow())
+ {
+ const rtl::Reference< sdr::overlay::OverlayManager >& xManager = rPageWindow.GetOverlayManager();
+ if (xManager.is())
+ {
+ const basegfx::B2DPoint aTopLeft(maRect.Left(), maRect.Top());
+ const basegfx::B2DPoint aBottomRight(maRect.Right(), maRect.Bottom());
+ const Color aHilightColor(SvtOptionsDrawinglayer::getHilightColor());
+ const double fTransparence(SvtOptionsDrawinglayer::GetTransparentSelectionPercent() * 0.01);
+
+ std::unique_ptr<sdr::overlay::OverlayRectangle> pNewOverlayObject(new sdr::overlay::OverlayRectangle(
+ aTopLeft,
+ aBottomRight,
+ aHilightColor,
+ fTransparence,
+ 3.0,
+ 3.0,
+ -toRadians(nRotationAngle),
+ true)); // allow animation; the Handle is not shown at text edit time
+
+ pNewOverlayObject->setHittable(false);
+
+ // OVERLAYMANAGER
+ insertNewlyCreatedOverlayObjectForSdrHdl(
+ std::move(pNewOverlayObject),
+ rPageWindow.GetObjectContact(),
+ *xManager);
+ }
+ }
+ }
+}
+
+
+static bool ImpSdrHdlListSorter(std::unique_ptr<SdrHdl> const& lhs, std::unique_ptr<SdrHdl> const& rhs)
+{
+ SdrHdlKind eKind1=lhs->GetKind();
+ SdrHdlKind eKind2=rhs->GetKind();
+ // Level 1: first normal handles, then Glue, then User, then Plus handles, then reference point handles
+ unsigned n1=1;
+ unsigned n2=1;
+ if (eKind1!=eKind2)
+ {
+ if (eKind1==SdrHdlKind::Ref1 || eKind1==SdrHdlKind::Ref2 || eKind1==SdrHdlKind::MirrorAxis) n1=5;
+ else if (eKind1==SdrHdlKind::Glue) n1=2;
+ else if (eKind1==SdrHdlKind::User) n1=3;
+ else if (eKind1==SdrHdlKind::SmartTag) n1=0;
+ if (eKind2==SdrHdlKind::Ref1 || eKind2==SdrHdlKind::Ref2 || eKind2==SdrHdlKind::MirrorAxis) n2=5;
+ else if (eKind2==SdrHdlKind::Glue) n2=2;
+ else if (eKind2==SdrHdlKind::User) n2=3;
+ else if (eKind2==SdrHdlKind::SmartTag) n2=0;
+ }
+ if (lhs->IsPlusHdl()) n1=4;
+ if (rhs->IsPlusHdl()) n2=4;
+ if (n1==n2)
+ {
+ // Level 2: PageView (Pointer)
+ SdrPageView* pPV1=lhs->GetPageView();
+ SdrPageView* pPV2=rhs->GetPageView();
+ if (pPV1==pPV2)
+ {
+ // Level 3: Position (x+y)
+ SdrObject* pObj1=lhs->GetObj();
+ SdrObject* pObj2=rhs->GetObj();
+ if (pObj1==pObj2)
+ {
+ sal_uInt32 nNum1=lhs->GetObjHdlNum();
+ sal_uInt32 nNum2=rhs->GetObjHdlNum();
+ if (nNum1==nNum2)
+ {
+ if (eKind1==eKind2)
+ return lhs<rhs; // Hack, to always get to the same sorting
+ return static_cast<sal_uInt16>(eKind1)<static_cast<sal_uInt16>(eKind2);
+ }
+ else
+ return nNum1<nNum2;
+ }
+ else
+ {
+ return pObj1<pObj2;
+ }
+ }
+ else
+ {
+ return pPV1<pPV2;
+ }
+ }
+ else
+ {
+ return n1<n2;
+ }
+}
+
+namespace {
+
+// Helper struct for re-sorting handles
+struct ImplHdlAndIndex
+{
+ SdrHdl* mpHdl;
+ sal_uInt32 mnIndex;
+};
+
+}
+
+extern "C" {
+
+// Helper method for sorting handles taking care of OrdNums, keeping order in
+// single objects and re-sorting polygon handles intuitively
+static int ImplSortHdlFunc( const void* pVoid1, const void* pVoid2 )
+{
+ const ImplHdlAndIndex* p1 = static_cast<ImplHdlAndIndex const *>(pVoid1);
+ const ImplHdlAndIndex* p2 = static_cast<ImplHdlAndIndex const *>(pVoid2);
+
+ if(p1->mpHdl->GetObj() == p2->mpHdl->GetObj())
+ {
+ if(p1->mpHdl->GetObj() && dynamic_cast<const SdrPathObj*>(p1->mpHdl->GetObj()) != nullptr)
+ {
+ // same object and a path object
+ if((p1->mpHdl->GetKind() == SdrHdlKind::Poly || p1->mpHdl->GetKind() == SdrHdlKind::BezierWeight)
+ && (p2->mpHdl->GetKind() == SdrHdlKind::Poly || p2->mpHdl->GetKind() == SdrHdlKind::BezierWeight))
+ {
+ // both handles are point or control handles
+ if(p1->mpHdl->GetPolyNum() == p2->mpHdl->GetPolyNum())
+ {
+ if(p1->mpHdl->GetPointNum() < p2->mpHdl->GetPointNum())
+ {
+ return -1;
+ }
+ else
+ {
+ return 1;
+ }
+ }
+ else if(p1->mpHdl->GetPolyNum() < p2->mpHdl->GetPolyNum())
+ {
+ return -1;
+ }
+ else
+ {
+ return 1;
+ }
+ }
+ }
+ }
+ else
+ {
+ if(!p1->mpHdl->GetObj())
+ {
+ return -1;
+ }
+ else if(!p2->mpHdl->GetObj())
+ {
+ return 1;
+ }
+ else
+ {
+ // different objects, use OrdNum for sort
+ const sal_uInt32 nOrdNum1 = p1->mpHdl->GetObj()->GetOrdNum();
+ const sal_uInt32 nOrdNum2 = p2->mpHdl->GetObj()->GetOrdNum();
+
+ if(nOrdNum1 < nOrdNum2)
+ {
+ return -1;
+ }
+ else
+ {
+ return 1;
+ }
+ }
+ }
+
+ // fallback to indices
+ if(p1->mnIndex < p2->mnIndex)
+ {
+ return -1;
+ }
+ else
+ {
+ return 1;
+ }
+}
+
+}
+
+void SdrHdlList::TravelFocusHdl(bool bForward)
+{
+ // security correction
+ if (mnFocusIndex >= GetHdlCount())
+ mnFocusIndex = SAL_MAX_SIZE;
+
+ if(maList.empty())
+ return;
+
+ // take care of old handle
+ const size_t nOldHdlNum(mnFocusIndex);
+ SdrHdl* pOld = nullptr;
+ if (nOldHdlNum < GetHdlCount())
+ pOld = GetHdl(nOldHdlNum);
+
+ if(pOld)
+ {
+ // switch off old handle
+ mnFocusIndex = SAL_MAX_SIZE;
+ pOld->Touch();
+ }
+
+ // allocate pointer array for sorted handle list
+ std::unique_ptr<ImplHdlAndIndex[]> pHdlAndIndex(new ImplHdlAndIndex[maList.size()]);
+
+ // build sorted handle list
+ for( size_t a = 0; a < maList.size(); ++a)
+ {
+ pHdlAndIndex[a].mpHdl = maList[a].get();
+ pHdlAndIndex[a].mnIndex = a;
+ }
+
+ qsort(pHdlAndIndex.get(), maList.size(), sizeof(ImplHdlAndIndex), ImplSortHdlFunc);
+
+ // look for old num in sorted array
+ size_t nOldHdl(nOldHdlNum);
+
+ if(nOldHdlNum != SAL_MAX_SIZE)
+ {
+ for(size_t a = 0; a < maList.size(); ++a)
+ {
+ if(pHdlAndIndex[a].mpHdl == pOld)
+ {
+ nOldHdl = a;
+ break;
+ }
+ }
+ }
+
+ // build new HdlNum
+ size_t nNewHdl(nOldHdl);
+
+ // do the focus travel
+ if(bForward)
+ {
+ if(nOldHdl != SAL_MAX_SIZE)
+ {
+ if(nOldHdl == maList.size() - 1)
+ {
+ // end forward run
+ nNewHdl = SAL_MAX_SIZE;
+ }
+ else
+ {
+ // simply the next handle
+ nNewHdl++;
+ }
+ }
+ else
+ {
+ // start forward run at first entry
+ nNewHdl = 0;
+ }
+ }
+ else
+ {
+ if(nOldHdl == SAL_MAX_SIZE)
+ {
+ // start backward run at last entry
+ nNewHdl = maList.size() - 1;
+
+ }
+ else
+ {
+ if(nOldHdl == 0)
+ {
+ // end backward run
+ nNewHdl = SAL_MAX_SIZE;
+ }
+ else
+ {
+ // simply the previous handle
+ nNewHdl--;
+ }
+ }
+ }
+
+ // build new HdlNum
+ sal_uIntPtr nNewHdlNum(nNewHdl);
+
+ // look for old num in sorted array
+ if(nNewHdl != SAL_MAX_SIZE)
+ {
+ SdrHdl* pNew = pHdlAndIndex[nNewHdl].mpHdl;
+
+ for(size_t a = 0; a < maList.size(); ++a)
+ {
+ if(maList[a].get() == pNew)
+ {
+ nNewHdlNum = a;
+ break;
+ }
+ }
+ }
+
+ // take care of next handle
+ if(nOldHdlNum != nNewHdlNum)
+ {
+ mnFocusIndex = nNewHdlNum;
+ if (mnFocusIndex < GetHdlCount())
+ {
+ SdrHdl* pNew = GetHdl(mnFocusIndex);
+ pNew->Touch();
+ }
+ }
+}
+
+SdrHdl* SdrHdlList::GetFocusHdl() const
+{
+ if(mnFocusIndex < GetHdlCount())
+ return GetHdl(mnFocusIndex);
+ else
+ return nullptr;
+}
+
+void SdrHdlList::SetFocusHdl(SdrHdl* pNew)
+{
+ if(!pNew)
+ return;
+
+ SdrHdl* pActual = GetFocusHdl();
+
+ if(pActual && pActual == pNew)
+ return;
+
+ const size_t nNewHdlNum = GetHdlNum(pNew);
+
+ if(nNewHdlNum != SAL_MAX_SIZE)
+ {
+ mnFocusIndex = nNewHdlNum;
+
+ if(pActual)
+ {
+ pActual->Touch();
+ }
+
+ pNew->Touch();
+ }
+}
+
+void SdrHdlList::ResetFocusHdl()
+{
+ SdrHdl* pHdl = GetFocusHdl();
+
+ mnFocusIndex = SAL_MAX_SIZE;
+
+ if(pHdl)
+ {
+ pHdl->Touch();
+ }
+}
+
+
+SdrHdlList::SdrHdlList(SdrMarkView* pV)
+: mnFocusIndex(SAL_MAX_SIZE),
+ pView(pV)
+{
+ nHdlSize = 3;
+ bRotateShear = false;
+ bMoveOutside = false;
+ bDistortShear = false;
+}
+
+SdrHdlList::~SdrHdlList()
+{
+ Clear();
+}
+
+void SdrHdlList::SetHdlSize(sal_uInt16 nSiz)
+{
+ if(nHdlSize != nSiz)
+ {
+ // remember new value
+ nHdlSize = nSiz;
+
+ // propagate change to IAOs
+ for(size_t i=0; i<GetHdlCount(); ++i)
+ {
+ SdrHdl* pHdl = GetHdl(i);
+ pHdl->Touch();
+ }
+ }
+}
+
+void SdrHdlList::SetMoveOutside(bool bOn)
+{
+ if(bMoveOutside != bOn)
+ {
+ // remember new value
+ bMoveOutside = bOn;
+
+ // propagate change to IAOs
+ for(size_t i=0; i<GetHdlCount(); ++i)
+ {
+ SdrHdl* pHdl = GetHdl(i);
+ pHdl->Touch();
+ }
+ }
+}
+
+void SdrHdlList::SetRotateShear(bool bOn)
+{
+ bRotateShear = bOn;
+}
+
+void SdrHdlList::SetDistortShear(bool bOn)
+{
+ bDistortShear = bOn;
+}
+
+std::unique_ptr<SdrHdl> SdrHdlList::RemoveHdl(size_t nNum)
+{
+ std::unique_ptr<SdrHdl> pRetval = std::move(maList[nNum]);
+ maList.erase(maList.begin() + nNum);
+
+ return pRetval;
+}
+
+void SdrHdlList::RemoveAllByKind(SdrHdlKind eKind)
+{
+ maList.erase(std::remove_if(maList.begin(), maList.end(),
+ [&eKind](std::unique_ptr<SdrHdl>& rItem) { return rItem->GetKind() == eKind; }),
+ maList.end());
+}
+
+void SdrHdlList::Clear()
+{
+ maList.clear();
+
+ bRotateShear=false;
+ bDistortShear=false;
+}
+
+void SdrHdlList::Sort()
+{
+ // remember currently focused handle
+ SdrHdl* pPrev = GetFocusHdl();
+
+ std::sort( maList.begin(), maList.end(), ImpSdrHdlListSorter );
+
+ // get now and compare
+ SdrHdl* pNow = GetFocusHdl();
+
+ if(pPrev == pNow)
+ return;
+
+ if(pPrev)
+ {
+ pPrev->Touch();
+ }
+
+ if(pNow)
+ {
+ pNow->Touch();
+ }
+}
+
+size_t SdrHdlList::GetHdlNum(const SdrHdl* pHdl) const
+{
+ if (pHdl==nullptr)
+ return SAL_MAX_SIZE;
+ auto it = std::find_if( maList.begin(), maList.end(),
+ [&](const std::unique_ptr<SdrHdl> & p) { return p.get() == pHdl; });
+ assert(it != maList.end());
+ if( it == maList.end() )
+ return SAL_MAX_SIZE;
+ return it - maList.begin();
+}
+
+void SdrHdlList::AddHdl(std::unique_ptr<SdrHdl> pHdl)
+{
+ assert(pHdl);
+ pHdl->SetHdlList(this);
+ maList.push_back(std::move(pHdl));
+}
+
+SdrHdl* SdrHdlList::IsHdlListHit(const Point& rPnt) const
+{
+ SdrHdl* pRet=nullptr;
+ const size_t nCount=GetHdlCount();
+ size_t nNum=nCount;
+ while (nNum>0 && pRet==nullptr)
+ {
+ nNum--;
+ SdrHdl* pHdl=GetHdl(nNum);
+ if (pHdl->IsHdlHit(rPnt))
+ pRet=pHdl;
+ }
+ return pRet;
+}
+
+SdrHdl* SdrHdlList::GetHdl(SdrHdlKind eKind1) const
+{
+ SdrHdl* pRet=nullptr;
+ for (size_t i=0; i<GetHdlCount() && pRet==nullptr; ++i)
+ {
+ SdrHdl* pHdl=GetHdl(i);
+ if (pHdl->GetKind()==eKind1)
+ pRet=pHdl;
+ }
+ return pRet;
+}
+
+void SdrHdlList::MoveTo(SdrHdlList& rOther)
+{
+ for (auto & pHdl : maList)
+ pHdl->SetHdlList(&rOther);
+ rOther.maList.insert(rOther.maList.end(),
+ std::make_move_iterator(maList.begin()), std::make_move_iterator(maList.end()));
+ maList.clear();
+}
+
+SdrCropHdl::SdrCropHdl(
+ const Point& rPnt,
+ SdrHdlKind eNewKind,
+ double fShearX,
+ double fRotation)
+: SdrHdl(rPnt, eNewKind),
+ mfShearX(fShearX),
+ mfRotation(fRotation)
+{
+}
+
+
+BitmapEx SdrCropHdl::GetBitmapForHandle( const BitmapEx& rBitmap, int nSize )
+{
+ int nPixelSize = 0, nX = 0, nY = 0, nOffset = 0;
+
+ if( nSize <= 3 )
+ {
+ nPixelSize = 13;
+ nOffset = 0;
+ }
+ else if( nSize <=4 )
+ {
+ nPixelSize = 17;
+ nOffset = 39;
+ }
+ else
+ {
+ nPixelSize = 21;
+ nOffset = 90;
+ }
+
+ switch( eKind )
+ {
+ case SdrHdlKind::UpperLeft: nX = 0; nY = 0; break;
+ case SdrHdlKind::Upper: nX = 1; nY = 0; break;
+ case SdrHdlKind::UpperRight: nX = 2; nY = 0; break;
+ case SdrHdlKind::Left: nX = 0; nY = 1; break;
+ case SdrHdlKind::Right: nX = 2; nY = 1; break;
+ case SdrHdlKind::LowerLeft: nX = 0; nY = 2; break;
+ case SdrHdlKind::Lower: nX = 1; nY = 2; break;
+ case SdrHdlKind::LowerRight: nX = 2; nY = 2; break;
+ default: break;
+ }
+
+ tools::Rectangle aSourceRect( Point( nX * nPixelSize + nOffset, nY * nPixelSize), Size(nPixelSize, nPixelSize) );
+
+ BitmapEx aRetval(rBitmap);
+ aRetval.Crop(aSourceRect);
+ return aRetval;
+}
+
+
+void SdrCropHdl::CreateB2dIAObject()
+{
+ // first throw away old one
+ GetRidOfIAObject();
+
+ SdrMarkView* pView = pHdlList ? pHdlList->GetView() : nullptr;
+ SdrPageView* pPageView = pView ? pView->GetSdrPageView() : nullptr;
+
+ if( !pPageView || pView->areMarkHandlesHidden() )
+ return;
+
+ const StyleSettings& rStyleSettings = Application::GetSettings().GetStyleSettings();
+ int nHdlSize = pHdlList->GetHdlSize();
+
+ const BitmapEx aHandlesBitmap(SIP_SA_CROP_MARKERS);
+ BitmapEx aBmpEx1( GetBitmapForHandle( aHandlesBitmap, nHdlSize ) );
+
+ for(sal_uInt32 b(0); b < pPageView->PageWindowCount(); b++)
+ {
+ const SdrPageWindow& rPageWindow = *pPageView->GetPageWindow(b);
+
+ if(rPageWindow.GetPaintWindow().OutputToWindow())
+ {
+ const rtl::Reference< sdr::overlay::OverlayManager >& xManager = rPageWindow.GetOverlayManager();
+ if (xManager.is())
+ {
+ basegfx::B2DPoint aPosition(aPos.X(), aPos.Y());
+
+ std::unique_ptr<sdr::overlay::OverlayObject> pOverlayObject;
+
+ // animate focused handles
+ if(IsFocusHdl() && (pHdlList->GetFocusHdl() == this))
+ {
+ if( nHdlSize >= 2 )
+ nHdlSize = 1;
+
+ BitmapEx aBmpEx2( GetBitmapForHandle( aHandlesBitmap, nHdlSize + 1 ) );
+
+ const sal_uInt64 nBlinkTime = rStyleSettings.GetCursorBlinkTime();
+
+ pOverlayObject.reset(new sdr::overlay::OverlayAnimatedBitmapEx(
+ aPosition,
+ aBmpEx1,
+ aBmpEx2,
+ nBlinkTime,
+ static_cast<sal_uInt16>(aBmpEx1.GetSizePixel().Width() - 1) >> 1,
+ static_cast<sal_uInt16>(aBmpEx1.GetSizePixel().Height() - 1) >> 1,
+ static_cast<sal_uInt16>(aBmpEx2.GetSizePixel().Width() - 1) >> 1,
+ static_cast<sal_uInt16>(aBmpEx2.GetSizePixel().Height() - 1) >> 1,
+ mfShearX,
+ mfRotation));
+ }
+ else
+ {
+ // create centered handle as default
+ pOverlayObject.reset(new sdr::overlay::OverlayBitmapEx(
+ aPosition,
+ aBmpEx1,
+ static_cast<sal_uInt16>(aBmpEx1.GetSizePixel().Width() - 1) >> 1,
+ static_cast<sal_uInt16>(aBmpEx1.GetSizePixel().Height() - 1) >> 1,
+ 0.0,
+ mfShearX,
+ mfRotation));
+ }
+
+ // OVERLAYMANAGER
+ insertNewlyCreatedOverlayObjectForSdrHdl(
+ std::move(pOverlayObject),
+ rPageWindow.GetObjectContact(),
+ *xManager);
+ }
+ }
+ }
+}
+
+
+// with the correction of crop handling I could get rid of the extra mirroring flag, adapted stuff
+// accordingly
+
+SdrCropViewHdl::SdrCropViewHdl(
+ const basegfx::B2DHomMatrix& rObjectTransform,
+ const Graphic& rGraphic,
+ double fCropLeft,
+ double fCropTop,
+ double fCropRight,
+ double fCropBottom)
+: SdrHdl(Point(), SdrHdlKind::User),
+ maObjectTransform(rObjectTransform),
+ maGraphic(rGraphic),
+ mfCropLeft(fCropLeft),
+ mfCropTop(fCropTop),
+ mfCropRight(fCropRight),
+ mfCropBottom(fCropBottom)
+{
+}
+
+namespace {
+
+void translateRotationToMirroring(basegfx::B2DVector & scale, double * rotate) {
+ assert(rotate != nullptr);
+
+ // detect 180 degree rotation, this is the same as mirrored in X and Y,
+ // thus change to mirroring. Prefer mirroring here. Use the equal call
+ // with getSmallValue here, the original which uses rtl::math::approxEqual
+ // is too correct here. Maybe this changes with enhanced precision in aw080
+ // to the better so that this can be reduced to the more precise call again
+ if(basegfx::fTools::equal(fabs(*rotate), M_PI, 0.000000001))
+ {
+ scale.setX(scale.getX() * -1.0);
+ scale.setY(scale.getY() * -1.0);
+ *rotate = 0.0;
+ }
+}
+
+}
+
+void SdrCropViewHdl::CreateB2dIAObject()
+{
+ GetRidOfIAObject();
+ SdrMarkView* pView = pHdlList ? pHdlList->GetView() : nullptr;
+ SdrPageView* pPageView = pView ? pView->GetSdrPageView() : nullptr;
+
+ if(!pPageView || pView->areMarkHandlesHidden())
+ {
+ return;
+ }
+
+ // decompose to have current translate and scale
+ basegfx::B2DVector aScale, aTranslate;
+ double fRotate, fShearX;
+
+ maObjectTransform.decompose(aScale, aTranslate, fRotate, fShearX);
+
+ if(aScale.equalZero())
+ {
+ return;
+ }
+
+ translateRotationToMirroring(aScale, &fRotate);
+
+ // remember mirroring, reset at Scale and adapt crop values for usage;
+ // mirroring can stay in the object transformation, so do not have to
+ // cope with it here (except later for the CroppedImage transformation,
+ // see below)
+ const bool bMirroredX(aScale.getX() < 0.0);
+ const bool bMirroredY(aScale.getY() < 0.0);
+ double fCropLeft(mfCropLeft);
+ double fCropTop(mfCropTop);
+ double fCropRight(mfCropRight);
+ double fCropBottom(mfCropBottom);
+
+ if(bMirroredX)
+ {
+ aScale.setX(-aScale.getX());
+ }
+
+ if(bMirroredY)
+ {
+ aScale.setY(-aScale.getY());
+ }
+
+ // create target translate and scale
+ const basegfx::B2DVector aTargetScale(
+ aScale.getX() + fCropRight + fCropLeft,
+ aScale.getY() + fCropBottom + fCropTop);
+ const basegfx::B2DVector aTargetTranslate(
+ aTranslate.getX() - fCropLeft,
+ aTranslate.getY() - fCropTop);
+
+ // create ranges to make comparisons
+ const basegfx::B2DRange aCurrentForCompare(
+ aTranslate.getX(), aTranslate.getY(),
+ aTranslate.getX() + aScale.getX(), aTranslate.getY() + aScale.getY());
+ basegfx::B2DRange aCropped(
+ aTargetTranslate.getX(), aTargetTranslate.getY(),
+ aTargetTranslate.getX() + aTargetScale.getX(), aTargetTranslate.getY() + aTargetScale.getY());
+
+ if(aCropped.isEmpty())
+ {
+ // nothing to return since cropped content is completely empty
+ return;
+ }
+
+ if(aCurrentForCompare.equal(aCropped))
+ {
+ // no crop at all
+ return;
+ }
+
+ // back-transform to have values in unit coordinates
+ basegfx::B2DHomMatrix aBackToUnit;
+ aBackToUnit.translate(-aTranslate.getX(), -aTranslate.getY());
+ aBackToUnit.scale(
+ basegfx::fTools::equalZero(aScale.getX()) ? 1.0 : 1.0 / aScale.getX(),
+ basegfx::fTools::equalZero(aScale.getY()) ? 1.0 : 1.0 / aScale.getY());
+
+ // transform cropped back to unit coordinates
+ aCropped.transform(aBackToUnit);
+
+ // prepare crop PolyPolygon
+ basegfx::B2DPolygon aGraphicOutlinePolygon(
+ basegfx::utils::createPolygonFromRect(
+ aCropped));
+ basegfx::B2DPolyPolygon aCropPolyPolygon(aGraphicOutlinePolygon);
+
+ // current range is unit range
+ basegfx::B2DRange aOverlap(0.0, 0.0, 1.0, 1.0);
+
+ aOverlap.intersect(aCropped);
+
+ if(!aOverlap.isEmpty())
+ {
+ aCropPolyPolygon.append(
+ basegfx::utils::createPolygonFromRect(
+ aOverlap));
+ }
+
+ // transform to object coordinates to prepare for clip
+ aCropPolyPolygon.transform(maObjectTransform);
+ aGraphicOutlinePolygon.transform(maObjectTransform);
+
+ // create cropped transformation
+ basegfx::B2DHomMatrix aCroppedTransform;
+
+ aCroppedTransform.scale(
+ aCropped.getWidth(),
+ aCropped.getHeight());
+ aCroppedTransform.translate(
+ aCropped.getMinX(),
+ aCropped.getMinY());
+ aCroppedTransform = maObjectTransform * aCroppedTransform;
+
+ // prepare graphic primitive (transformed)
+ const drawinglayer::primitive2d::Primitive2DReference aGraphic(
+ new drawinglayer::primitive2d::GraphicPrimitive2D(
+ aCroppedTransform,
+ maGraphic));
+
+ // prepare outline polygon for whole graphic
+ const basegfx::BColor aHilightColor(SvtOptionsDrawinglayer::getHilightColor().getBColor());
+ const drawinglayer::primitive2d::Primitive2DReference aGraphicOutline(
+ new drawinglayer::primitive2d::PolygonHairlinePrimitive2D(
+ aGraphicOutlinePolygon,
+ aHilightColor));
+
+ // combine these
+ drawinglayer::primitive2d::Primitive2DContainer aCombination(2);
+ aCombination[0] = aGraphic;
+ aCombination[1] = aGraphicOutline;
+
+ // embed to MaskPrimitive2D
+ const drawinglayer::primitive2d::Primitive2DReference aMaskedGraphic(
+ new drawinglayer::primitive2d::MaskPrimitive2D(
+ aCropPolyPolygon,
+ std::move(aCombination)));
+
+ // embed to UnifiedTransparencePrimitive2D
+ const drawinglayer::primitive2d::Primitive2DReference aTransparenceMaskedGraphic(
+ new drawinglayer::primitive2d::UnifiedTransparencePrimitive2D(
+ drawinglayer::primitive2d::Primitive2DContainer { aMaskedGraphic },
+ 0.8));
+
+ const drawinglayer::primitive2d::Primitive2DContainer aSequence { aTransparenceMaskedGraphic };
+
+ for(sal_uInt32 b(0); b < pPageView->PageWindowCount(); b++)
+ {
+ // const SdrPageViewWinRec& rPageViewWinRec = rPageViewWinList[b];
+ const SdrPageWindow& rPageWindow = *(pPageView->GetPageWindow(b));
+
+ if(rPageWindow.GetPaintWindow().OutputToWindow())
+ {
+ const rtl::Reference< sdr::overlay::OverlayManager >& xManager = rPageWindow.GetOverlayManager();
+ if(xManager.is())
+ {
+ std::unique_ptr<sdr::overlay::OverlayObject> pNew(new sdr::overlay::OverlayPrimitive2DSequenceObject(drawinglayer::primitive2d::Primitive2DContainer(aSequence)));
+
+ // only informative object, no hit
+ pNew->setHittable(false);
+
+ // OVERLAYMANAGER
+ insertNewlyCreatedOverlayObjectForSdrHdl(
+ std::move(pNew),
+ rPageWindow.GetObjectContact(),
+ *xManager);
+ }
+ }
+ }
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/svdraw/svdhlpln.cxx b/svx/source/svdraw/svdhlpln.cxx
new file mode 100644
index 000000000..0d515191e
--- /dev/null
+++ b/svx/source/svdraw/svdhlpln.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 <svx/svdhlpln.hxx>
+
+#include <vcl/outdev.hxx>
+#include <vcl/ptrstyle.hxx>
+
+
+PointerStyle SdrHelpLine::GetPointer() const
+{
+ switch (eKind) {
+ case SdrHelpLineKind::Vertical : return PointerStyle::ESize;
+ case SdrHelpLineKind::Horizontal: return PointerStyle::SSize;
+ default : return PointerStyle::Move;
+ } // switch
+}
+
+bool SdrHelpLine::IsHit(const Point& rPnt, sal_uInt16 nTolLog, const OutputDevice& rOut) const
+{
+ Size a1Pix(rOut.PixelToLogic(Size(1,1)));
+ bool bXHit=rPnt.X()>=aPos.X()-nTolLog && rPnt.X()<=aPos.X()+nTolLog+a1Pix.Width();
+ bool bYHit=rPnt.Y()>=aPos.Y()-nTolLog && rPnt.Y()<=aPos.Y()+nTolLog+a1Pix.Height();
+ switch (eKind) {
+ case SdrHelpLineKind::Vertical : return bXHit;
+ case SdrHelpLineKind::Horizontal: return bYHit;
+ case SdrHelpLineKind::Point: {
+ if (bXHit || bYHit) {
+ Size aRad(rOut.PixelToLogic(Size(SDRHELPLINE_POINT_PIXELSIZE,SDRHELPLINE_POINT_PIXELSIZE)));
+ return rPnt.X()>=aPos.X()-aRad.Width() && rPnt.X()<=aPos.X()+aRad.Width()+a1Pix.Width() &&
+ rPnt.Y()>=aPos.Y()-aRad.Height() && rPnt.Y()<=aPos.Y()+aRad.Height()+a1Pix.Height();
+ }
+ } break;
+ } // switch
+ return false;
+}
+
+tools::Rectangle SdrHelpLine::GetBoundRect(const OutputDevice& rOut) const
+{
+ tools::Rectangle aRet(aPos,aPos);
+ Point aOfs(rOut.GetMapMode().GetOrigin());
+ Size aSiz(rOut.GetOutputSize());
+ switch (eKind) {
+ case SdrHelpLineKind::Vertical : aRet.SetTop(-aOfs.Y() ); aRet.SetBottom(-aOfs.Y()+aSiz.Height() ); break;
+ case SdrHelpLineKind::Horizontal: aRet.SetLeft(-aOfs.X() ); aRet.SetRight(-aOfs.X()+aSiz.Width() ); break;
+ case SdrHelpLineKind::Point : {
+ Size aRad(rOut.PixelToLogic(Size(SDRHELPLINE_POINT_PIXELSIZE,SDRHELPLINE_POINT_PIXELSIZE)));
+ aRet.AdjustLeft( -(aRad.Width()) );
+ aRet.AdjustRight(aRad.Width() );
+ aRet.AdjustTop( -(aRad.Height()) );
+ aRet.AdjustBottom(aRad.Height() );
+ } break;
+ } // switch
+ return aRet;
+}
+
+SdrHelpLineList& SdrHelpLineList::operator=(const SdrHelpLineList& rSrcList)
+{
+ aList.clear();
+ sal_uInt16 nCount=rSrcList.GetCount();
+ for (sal_uInt16 i=0; i<nCount; i++) {
+ Insert(rSrcList[i]);
+ }
+ return *this;
+}
+
+bool SdrHelpLineList::operator==(const SdrHelpLineList& rSrcList) const
+{
+ bool bEqual = false;
+ sal_uInt16 nCount=GetCount();
+ if (nCount==rSrcList.GetCount()) {
+ bEqual = true;
+ for (sal_uInt16 i=0; i<nCount && bEqual; i++) {
+ if (*aList[i]!=*rSrcList.aList[i]) {
+ bEqual = false;
+ }
+ }
+ }
+ return bEqual;
+}
+
+sal_uInt16 SdrHelpLineList::HitTest(const Point& rPnt, sal_uInt16 nTolLog, const OutputDevice& rOut) const
+{
+ sal_uInt16 nCount=GetCount();
+ for (sal_uInt16 i=nCount; i>0;) {
+ i--;
+ if (aList[i]->IsHit(rPnt,nTolLog,rOut)) return i;
+ }
+ return SDRHELPLINE_NOTFOUND;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/svdraw/svditer.cxx b/svx/source/svdraw/svditer.cxx
new file mode 100644
index 000000000..eaca2b335
--- /dev/null
+++ b/svx/source/svdraw/svditer.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 <svx/svditer.hxx>
+#include <svx/svdpage.hxx>
+#include <svx/svdobj.hxx>
+#include <svx/svdmark.hxx>
+#include <osl/diagnose.h>
+
+SdrObjListIter::SdrObjListIter(const SdrObjList* pObjList, SdrIterMode eMode, bool bReverse)
+: mnIndex(0),
+ mbReverse(bReverse),
+ mbUseZOrder(true)
+{
+ if(nullptr != pObjList)
+ {
+ ImpProcessObjectList(*pObjList, eMode);
+ }
+
+ Reset();
+}
+
+SdrObjListIter::SdrObjListIter(const SdrObjList* pObjList, bool bUseZOrder, SdrIterMode eMode, bool bReverse)
+: mnIndex(0),
+ mbReverse(bReverse),
+ mbUseZOrder(bUseZOrder)
+{
+ if(nullptr != pObjList)
+ {
+ // correct when we have no ObjectNavigationOrder
+ if(!mbUseZOrder && !pObjList->HasObjectNavigationOrder())
+ {
+ mbUseZOrder = false;
+ }
+
+ ImpProcessObjectList(*pObjList, eMode);
+ }
+
+ Reset();
+}
+
+SdrObjListIter::SdrObjListIter(const SdrObject& rSdrObject, SdrIterMode eMode, bool bReverse)
+: mnIndex(0),
+ mbReverse(bReverse),
+ mbUseZOrder(true)
+{
+ ImpProcessObj(rSdrObject, eMode);
+ Reset();
+}
+
+SdrObjListIter::SdrObjListIter(const SdrPage* pSdrPage, SdrIterMode eMode, bool bReverse)
+: mnIndex(0),
+ mbReverse(bReverse),
+ mbUseZOrder(true)
+{
+ if (pSdrPage)
+ ImpProcessObjectList(*pSdrPage, eMode);
+ Reset();
+}
+
+SdrObjListIter::SdrObjListIter( const SdrMarkList& rMarkList, SdrIterMode eMode )
+: mnIndex(0),
+ mbReverse(false),
+ mbUseZOrder(true)
+{
+ ImpProcessMarkList(rMarkList, eMode);
+ Reset();
+}
+
+void SdrObjListIter::ImpProcessObjectList(const SdrObjList& rObjList, SdrIterMode eMode)
+{
+ for(size_t nIdx(0), nCount(rObjList.GetObjCount()); nIdx < nCount; ++nIdx)
+ {
+ const SdrObject* pSdrObject(mbUseZOrder
+ ? rObjList.GetObj(nIdx)
+ : rObjList.GetObjectForNavigationPosition(nIdx));
+
+ if(nullptr == pSdrObject)
+ {
+ OSL_ENSURE(false, "SdrObjListIter: corrupted SdrObjList (!)");
+ }
+ else
+ {
+ ImpProcessObj(*pSdrObject, eMode);
+ }
+ }
+}
+
+void SdrObjListIter::ImpProcessMarkList(const SdrMarkList& rMarkList, SdrIterMode eMode)
+{
+ for( size_t nIdx = 0, nCount = rMarkList.GetMarkCount(); nIdx < nCount; ++nIdx )
+ {
+ if( SdrObject* pObj = rMarkList.GetMark( nIdx )->GetMarkedSdrObj() )
+ {
+ ImpProcessObj(*pObj, eMode);
+ }
+ }
+}
+
+void SdrObjListIter::ImpProcessObj(const SdrObject& rSdrObject, SdrIterMode eMode)
+{
+ // TTTT: Note: The behaviour has changed here, it will now deep-iterate
+ // for SdrObjGroup and E3dScene. Old version only deep-dived for SdrObjGroup,
+ // E3dScene was just added flat. This is now more correct, but potentially
+ // there will exist code in the 3D area that *self-iterates* with local
+ // functions/methods due to this iterator was not doing the expected thing.
+ // These will be difficult to find, but in most cases should do no harm,
+ // but cost runtime. Will need to have an eye on this aspect on continued
+ // changes...
+ const SdrObjList* pChildren(rSdrObject.getChildrenOfSdrObject());
+ const bool bIsGroup(nullptr != pChildren);
+
+ if(!bIsGroup || (SdrIterMode::DeepNoGroups != eMode))
+ {
+ maObjList.push_back(&rSdrObject);
+ }
+
+ if(bIsGroup && (SdrIterMode::Flat != eMode))
+ {
+ ImpProcessObjectList(*pChildren, eMode);
+ }
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/svdraw/svdlayer.cxx b/svx/source/svdraw/svdlayer.cxx
new file mode 100644
index 000000000..3146d62f7
--- /dev/null
+++ b/svx/source/svdraw/svdlayer.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 <com/sun/star/uno/Sequence.hxx>
+
+#include <svx/svdlayer.hxx>
+#include <svx/svdmodel.hxx>
+
+#include <algorithm>
+
+bool SdrLayerIDSet::IsEmpty() const
+{
+ for(sal_uInt8 i : aData)
+ {
+ if(i != 0)
+ return false;
+ }
+
+ return true;
+}
+
+void SdrLayerIDSet::operator&=(const SdrLayerIDSet& r2ndSet)
+{
+ for(sal_uInt16 i(0); i < 32; i++)
+ {
+ aData[i] &= r2ndSet.aData[i];
+ }
+}
+
+/** initialize this set with a UNO sequence of sal_Int8 (e.g. as stored in settings.xml)
+*/
+void SdrLayerIDSet::PutValue( const css::uno::Any & rAny )
+{
+ css::uno::Sequence< sal_Int8 > aSeq;
+ if( !(rAny >>= aSeq) )
+ return;
+
+ sal_Int16 nCount = static_cast<sal_Int16>(aSeq.getLength());
+ if( nCount > 32 )
+ nCount = 32;
+
+ sal_Int16 nIndex;
+ for( nIndex = 0; nIndex < nCount; nIndex++ )
+ {
+ aData[nIndex] = static_cast<sal_uInt8>(aSeq[nIndex]);
+ }
+
+ for( ; nIndex < 32; nIndex++ )
+ {
+ aData[nIndex] = 0;
+ }
+}
+
+SdrLayer::SdrLayer(SdrLayerID nNewID, const OUString& rNewName) :
+ maName(rNewName), pModel(nullptr), nID(nNewID)
+{
+ // ODF default values
+ mbVisibleODF = true;
+ mbPrintableODF = true;
+ mbLockedODF = false;
+}
+
+void SdrLayer::SetName(const OUString& rNewName)
+{
+ if (rNewName == maName)
+ return;
+
+ maName = rNewName;
+
+ if (pModel)
+ {
+ SdrHint aHint(SdrHintKind::LayerChange);
+ pModel->Broadcast(aHint);
+ pModel->SetChanged();
+ }
+}
+
+bool SdrLayer::operator==(const SdrLayer& rCmpLayer) const
+{
+ return (nID == rCmpLayer.nID
+ && maName == rCmpLayer.maName);
+}
+
+SdrLayerAdmin::SdrLayerAdmin(SdrLayerAdmin* pNewParent):
+ pParent(pNewParent),
+ pModel(nullptr),
+ maControlLayerName("controls")
+{
+}
+
+SdrLayerAdmin::SdrLayerAdmin(const SdrLayerAdmin& rSrcLayerAdmin):
+ pParent(nullptr),
+ pModel(nullptr),
+ maControlLayerName("controls")
+{
+ *this = rSrcLayerAdmin;
+}
+
+SdrLayerAdmin::~SdrLayerAdmin()
+{
+}
+
+void SdrLayerAdmin::ClearLayers()
+{
+ maLayers.clear();
+}
+
+SdrLayerAdmin& SdrLayerAdmin::operator=(const SdrLayerAdmin& rSrcLayerAdmin)
+{
+ if (this != &rSrcLayerAdmin)
+ {
+ maLayers.clear();
+ pParent=rSrcLayerAdmin.pParent;
+ sal_uInt16 i;
+ sal_uInt16 nCount=rSrcLayerAdmin.GetLayerCount();
+ for (i=0; i<nCount; i++) {
+ maLayers.emplace_back(new SdrLayer(*rSrcLayerAdmin.GetLayer(i)));
+ }
+ }
+ return *this;
+}
+
+void SdrLayerAdmin::SetModel(SdrModel* pNewModelel)
+{
+ if (pNewModelel!=pModel) {
+ pModel=pNewModelel;
+ sal_uInt16 nCount=GetLayerCount();
+ sal_uInt16 i;
+ for (i=0; i<nCount; i++) {
+ GetLayer(i)->SetModel(pNewModelel);
+ }
+ }
+}
+
+void SdrLayerAdmin::Broadcast() const
+{
+ if (pModel!=nullptr) {
+ SdrHint aHint(SdrHintKind::LayerOrderChange);
+ pModel->Broadcast(aHint);
+ pModel->SetChanged();
+ }
+}
+
+void SdrLayerAdmin::InsertLayer(std::unique_ptr<SdrLayer> pLayer, sal_uInt16 nPos)
+{
+ pLayer->SetModel(pModel);
+ if(nPos==0xFFFF)
+ maLayers.push_back(std::move(pLayer));
+ else
+ maLayers.insert(maLayers.begin() + nPos, std::move(pLayer));
+ Broadcast();
+}
+
+std::unique_ptr<SdrLayer> SdrLayerAdmin::RemoveLayer(sal_uInt16 nPos)
+{
+ std::unique_ptr<SdrLayer> pRetLayer = std::move(maLayers[nPos]);
+ maLayers.erase(maLayers.begin()+nPos);
+ Broadcast();
+ return pRetLayer;
+}
+
+SdrLayer* SdrLayerAdmin::NewLayer(const OUString& rName, sal_uInt16 nPos)
+{
+ SdrLayerID nID=GetUniqueLayerID();
+ SdrLayer* pLay=new SdrLayer(nID,rName);
+ pLay->SetModel(pModel);
+ if(nPos==0xFFFF)
+ maLayers.push_back(std::unique_ptr<SdrLayer>(pLay));
+ else
+ maLayers.insert(maLayers.begin() + nPos, std::unique_ptr<SdrLayer>(pLay));
+ Broadcast();
+ return pLay;
+}
+
+sal_uInt16 SdrLayerAdmin::GetLayerPos(const SdrLayer* pLayer) const
+{
+ sal_uInt16 nRet=SDRLAYERPOS_NOTFOUND;
+ if (pLayer!=nullptr) {
+ auto it = std::find_if(maLayers.begin(), maLayers.end(),
+ [&](const std::unique_ptr<SdrLayer> & p) { return p.get() == pLayer; });
+ if (it!=maLayers.end()) {
+ nRet=it - maLayers.begin();
+ }
+ }
+ return nRet;
+}
+
+SdrLayer* SdrLayerAdmin::GetLayer(const OUString& rName)
+{
+ return const_cast<SdrLayer*>(const_cast<const SdrLayerAdmin*>(this)->GetLayer(rName));
+}
+
+const SdrLayer* SdrLayerAdmin::GetLayer(const OUString& rName) const
+{
+ sal_uInt16 i(0);
+ const SdrLayer* pLay = nullptr;
+
+ while(i < GetLayerCount() && !pLay)
+ {
+ if (rName == GetLayer(i)->GetName())
+ pLay = GetLayer(i);
+ else
+ i++;
+ }
+
+ if(!pLay && pParent)
+ {
+ pLay = pParent->GetLayer(rName);
+ }
+
+ return pLay;
+}
+
+SdrLayerID SdrLayerAdmin::GetLayerID(const OUString& rName) const
+{
+ SdrLayerID nRet=SDRLAYER_NOTFOUND;
+ const SdrLayer* pLay=GetLayer(rName);
+ if (pLay!=nullptr) nRet=pLay->GetID();
+ return nRet;
+}
+
+const SdrLayer* SdrLayerAdmin::GetLayerPerID(SdrLayerID nID) const
+{
+ for (auto const & pLayer : maLayers)
+ if (pLayer->GetID() == nID)
+ return pLayer.get();
+ return nullptr;
+}
+
+// Global LayerIDs begin at 0 and increase,
+// local LayerIDs begin at 254 and decrease;
+// 255 is reserved for SDRLAYER_NOTFOUND.
+
+SdrLayerID SdrLayerAdmin::GetUniqueLayerID() const
+{
+ SdrLayerIDSet aSet;
+ for (sal_uInt16 j=0; j<GetLayerCount(); j++)
+ {
+ aSet.Set(GetLayer(j)->GetID());
+ }
+ sal_uInt8 i;
+ if (pParent != nullptr)
+ {
+ i = 254;
+ while (i && aSet.IsSet(SdrLayerID(i)))
+ --i;
+ assert(i != 0);
+ if (i == 0)
+ i = 254;
+ }
+ else
+ {
+ i = 0;
+ while (i<=254 && aSet.IsSet(SdrLayerID(i)))
+ i++;
+ assert(i <= 254);
+ if (i>254)
+ i = 0;
+ }
+ return SdrLayerID(i);
+}
+
+void SdrLayerAdmin::SetControlLayerName(const OUString& rNewName)
+{
+ maControlLayerName = rNewName;
+}
+
+void SdrLayerAdmin::getVisibleLayersODF( SdrLayerIDSet& rOutSet) const
+{
+ rOutSet.ClearAll();
+ for( auto & pCurrentLayer : maLayers )
+ {
+ if ( pCurrentLayer->IsVisibleODF() )
+ rOutSet.Set( pCurrentLayer->GetID() );
+ }
+}
+
+void SdrLayerAdmin::getPrintableLayersODF( SdrLayerIDSet& rOutSet) const
+{
+ rOutSet.ClearAll();
+ for( auto & pCurrentLayer : maLayers )
+ {
+ if ( pCurrentLayer->IsPrintableODF() )
+ rOutSet.Set( pCurrentLayer->GetID() );
+ }
+}
+
+void SdrLayerAdmin::getLockedLayersODF( SdrLayerIDSet& rOutSet) const
+{
+ rOutSet.ClearAll();
+ for( auto& pCurrentLayer : maLayers )
+ {
+ if ( pCurrentLayer->IsLockedODF() )
+ rOutSet.Set( pCurrentLayer->GetID() );
+ }
+}
+
+ // Generates a bitfield for settings.xml from the SdrLayerIDSet.
+ // Output is a UNO sequence of BYTE (which is 'short' in API).
+void SdrLayerAdmin::QueryValue(const SdrLayerIDSet& rViewLayerSet, css::uno::Any& rAny)
+{
+ // tdf#119392 The SdrLayerIDSet in a view is ordered according LayerID, but in file
+ // the bitfield is interpreted in order of layers in <draw:layer-set>.
+ // First generate a new bitfield based on rViewLayerSet in the needed order.
+ sal_uInt8 aTmp[32]; // 256 bits in settings.xml makes byte 0 to 31
+ for (auto nIndex = 0; nIndex <32; nIndex++)
+ {
+ aTmp[nIndex] = 0;
+ }
+ sal_uInt8 nByteIndex = 0;
+ sal_uInt8 nBitpos = 0;
+ sal_uInt16 nLayerPos = 0; // Position of the layer in member aLayer and in <draw:layer-set> in file
+ sal_uInt16 nLayerIndex = 0;
+ for( const auto& pCurrentLayer : maLayers )
+ {
+ SdrLayerID nCurrentID = pCurrentLayer->GetID();
+ if ( rViewLayerSet.IsSet(nCurrentID) )
+ {
+ nLayerPos = nLayerIndex;
+ nByteIndex = nLayerPos / 8;
+ if (nByteIndex > 31)
+ continue; // skip position, if too large for bitfield
+ nBitpos = nLayerPos % 8;
+ aTmp[nByteIndex] |= (1 << nBitpos);
+ }
+ ++nLayerIndex;
+ }
+
+ // Second transform the bitfield to byte sequence, same as in previous version of QueryValue
+ sal_uInt8 nNumBytesSet = 0;
+ for( auto nIndex = 31; nIndex >= 0; nIndex--)
+ {
+ if( 0 != aTmp[nIndex] )
+ {
+ nNumBytesSet = nIndex + 1;
+ break;
+ }
+ }
+ css::uno::Sequence< sal_Int8 > aSeq( nNumBytesSet );
+ std::transform(aTmp, aTmp + nNumBytesSet, aSeq.getArray(),
+ [](const sal_uInt8 b) { return static_cast<sal_Int8>(b); });
+ rAny <<= aSeq;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/svdraw/svdmark.cxx b/svx/source/svdraw/svdmark.cxx
new file mode 100644
index 000000000..e3c77bda1
--- /dev/null
+++ b/svx/source/svdraw/svdmark.cxx
@@ -0,0 +1,791 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * 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 <osl/time.h>
+#include <svx/svdmark.hxx>
+#include <svx/svdobj.hxx>
+#include <svx/svdpage.hxx>
+#include <svx/svdpagv.hxx>
+#include <svx/strings.hrc>
+#include <svx/dialmgr.hxx>
+
+
+#include <svx/obj3d.hxx>
+#include <svx/scene3d.hxx>
+#include <svl/SfxBroadcaster.hxx>
+#include <svx/svdoedge.hxx>
+#include <osl/diagnose.h>
+
+#include <cassert>
+
+void SdrMark::setTime()
+{
+ TimeValue aNow;
+ osl_getSystemTime(&aNow);
+ mnTimeStamp = sal_Int64(aNow.Seconds) * 1000000000 + aNow.Nanosec;
+}
+
+SdrMark::SdrMark(SdrObject* pNewObj, SdrPageView* pNewPageView)
+: mpSelectedSdrObject(pNewObj),
+ mpPageView(pNewPageView),
+ mbCon1(false),
+ mbCon2(false),
+ mnUser(0)
+{
+ if(mpSelectedSdrObject)
+ {
+ mpSelectedSdrObject->AddObjectUser( *this );
+ }
+ setTime();
+}
+
+SdrMark::SdrMark(const SdrMark& rMark)
+: ObjectUser(),
+ mnTimeStamp(0),
+ mpSelectedSdrObject(nullptr),
+ mpPageView(nullptr),
+ mbCon1(false),
+ mbCon2(false),
+ mnUser(0)
+{
+ *this = rMark;
+}
+
+SdrMark::~SdrMark()
+{
+ if (mpSelectedSdrObject)
+ {
+ mpSelectedSdrObject->RemoveObjectUser( *this );
+ }
+}
+
+void SdrMark::ObjectInDestruction(const SdrObject& rObject)
+{
+ (void) rObject; // avoid warnings
+ OSL_ENSURE(mpSelectedSdrObject && mpSelectedSdrObject == &rObject, "SdrMark::ObjectInDestruction: called from object different from hosted one (!)");
+ OSL_ENSURE(mpSelectedSdrObject, "SdrMark::ObjectInDestruction: still selected SdrObject is deleted, deselect first (!)");
+ mpSelectedSdrObject = nullptr;
+}
+
+void SdrMark::SetMarkedSdrObj(SdrObject* pNewObj)
+{
+ if(mpSelectedSdrObject)
+ {
+ mpSelectedSdrObject->RemoveObjectUser( *this );
+ }
+
+ mpSelectedSdrObject = pNewObj;
+
+ if(mpSelectedSdrObject)
+ {
+ mpSelectedSdrObject->AddObjectUser( *this );
+ }
+}
+
+SdrMark& SdrMark::operator=(const SdrMark& rMark)
+{
+ SetMarkedSdrObj(rMark.mpSelectedSdrObject);
+
+ mnTimeStamp = rMark.mnTimeStamp;
+ mpPageView = rMark.mpPageView;
+ mbCon1 = rMark.mbCon1;
+ mbCon2 = rMark.mbCon2;
+ mnUser = rMark.mnUser;
+ maPoints = rMark.maPoints;
+ maGluePoints = rMark.maGluePoints;
+
+ return *this;
+}
+
+static bool ImpSdrMarkListSorter(std::unique_ptr<SdrMark> const& lhs, std::unique_ptr<SdrMark> const& rhs)
+{
+ SdrObject* pObj1 = lhs->GetMarkedSdrObj();
+ SdrObject* pObj2 = rhs->GetMarkedSdrObj();
+ SdrObjList* pOL1 = pObj1 ? pObj1->getParentSdrObjListFromSdrObject() : nullptr;
+ SdrObjList* pOL2 = pObj2 ? pObj2->getParentSdrObjListFromSdrObject() : nullptr;
+
+ if (pOL1 == pOL2)
+ {
+ // AF: Note that I reverted a change from sal_uInt32 to sal_uLong (made
+ // for 64bit compliance, #i78198#) because internally in SdrObject
+ // both nOrdNum and mnNavigationPosition are stored as sal_uInt32.
+ sal_uInt32 nObjOrd1(pObj1 ? pObj1->GetNavigationPosition() : 0);
+ sal_uInt32 nObjOrd2(pObj2 ? pObj2->GetNavigationPosition() : 0);
+
+ return nObjOrd1 < nObjOrd2;
+ }
+ else
+ {
+ return pOL1 < pOL2;
+ }
+}
+
+
+void SdrMarkList::ForceSort() const
+{
+ if(!mbSorted)
+ {
+ const_cast<SdrMarkList*>(this)->ImpForceSort();
+ }
+}
+
+void SdrMarkList::ImpForceSort()
+{
+ if(mbSorted)
+ return;
+
+ mbSorted = true;
+ size_t nCount = maList.size();
+
+ // remove invalid
+ if(nCount > 0 )
+ {
+ maList.erase(std::remove_if(maList.begin(), maList.end(),
+ [](std::unique_ptr<SdrMark>& rItem) { return rItem->GetMarkedSdrObj() == nullptr; }),
+ maList.end());
+ nCount = maList.size();
+ }
+
+ if(nCount <= 1)
+ return;
+
+ std::sort(maList.begin(), maList.end(), ImpSdrMarkListSorter);
+
+ // remove duplicates
+ if(maList.size() <= 1)
+ return;
+
+ SdrMark* pCurrent = maList.back().get();
+ for (size_t count = maList.size() - 1; count; --count)
+ {
+ size_t i = count - 1;
+ SdrMark* pCmp = maList[i].get();
+ assert(pCurrent->GetMarkedSdrObj());
+ if(pCurrent->GetMarkedSdrObj() == pCmp->GetMarkedSdrObj())
+ {
+ // Con1/Con2 Merging
+ if(pCmp->IsCon1())
+ pCurrent->SetCon1(true);
+
+ if(pCmp->IsCon2())
+ pCurrent->SetCon2(true);
+
+ // delete pCmp
+ maList.erase(maList.begin() + i);
+ }
+ else
+ {
+ pCurrent = pCmp;
+ }
+ }
+}
+
+void SdrMarkList::Clear()
+{
+ maList.clear();
+ mbSorted = true; //we're empty, so can be considered sorted
+ SetNameDirty();
+}
+
+SdrMarkList& SdrMarkList::operator=(const SdrMarkList& rLst)
+{
+ if (this != &rLst)
+ {
+ Clear();
+
+ for(size_t i = 0; i < rLst.GetMarkCount(); ++i)
+ {
+ SdrMark* pMark = rLst.GetMark(i);
+ maList.emplace_back(new SdrMark(*pMark));
+ }
+
+ maMarkName = rLst.maMarkName;
+ mbNameOk = rLst.mbNameOk;
+ maPointName = rLst.maPointName;
+ mbPointNameOk = rLst.mbPointNameOk;
+ maGluePointName = rLst.maGluePointName;
+ mbSorted = rLst.mbSorted;
+ }
+ return *this;
+}
+
+SdrMark* SdrMarkList::GetMark(size_t nNum) const
+{
+ return (nNum < maList.size()) ? maList[nNum].get() : nullptr;
+}
+
+size_t SdrMarkList::FindObject(const SdrObject* pObj) const
+{
+ // Since relying on OrdNums is not allowed for the selection because objects in the
+ // selection may not be inserted in a list if they are e.g. modified ATM, i changed
+ // this loop to just look if the object pointer is in the selection.
+
+ // Problem is that GetOrdNum() which is const, internally casts to non-const and
+ // hardly sets the OrdNum member of the object (nOrdNum) to 0 (ZERO) if the object
+ // is not inserted in an object list.
+ // Since this may be by purpose and necessary somewhere else i decided that it is
+ // less dangerous to change this method then changing SdrObject::GetOrdNum().
+ if(pObj)
+ {
+ for(size_t a = 0; a < maList.size(); ++a)
+ {
+ if(maList[a]->GetMarkedSdrObj() == pObj)
+ {
+ return a;
+ }
+ }
+ }
+
+ return SAL_MAX_SIZE;
+}
+
+void SdrMarkList::InsertEntry(const SdrMark& rMark, bool bChkSort)
+{
+ SetNameDirty();
+ const size_t nCount(maList.size());
+
+ if(!bChkSort || !mbSorted || nCount == 0)
+ {
+ if(!bChkSort)
+ mbSorted = false;
+
+ maList.emplace_back(new SdrMark(rMark));
+ }
+ else
+ {
+ SdrMark* pLast = GetMark(nCount - 1);
+ const SdrObject* pLastObj = pLast->GetMarkedSdrObj();
+ const SdrObject* pNewObj = rMark.GetMarkedSdrObj();
+
+ if(pLastObj == pNewObj)
+ {
+ // This one already exists.
+ // Con1/Con2 Merging
+ if(rMark.IsCon1())
+ pLast->SetCon1(true);
+
+ if(rMark.IsCon2())
+ pLast->SetCon2(true);
+ }
+ else
+ {
+ maList.emplace_back(new SdrMark(rMark));
+
+ // now check if the sort is ok
+ const SdrObjList* pLastOL = pLastObj!=nullptr ? pLastObj->getParentSdrObjListFromSdrObject() : nullptr;
+ const SdrObjList* pNewOL = pNewObj !=nullptr ? pNewObj->getParentSdrObjListFromSdrObject() : nullptr;
+
+ if(pLastOL == pNewOL)
+ {
+ const sal_uLong nLastNum(pLastObj!=nullptr ? pLastObj->GetOrdNum() : 0);
+ const sal_uLong nNewNum(pNewObj !=nullptr ? pNewObj ->GetOrdNum() : 0);
+
+ if(nNewNum < nLastNum)
+ {
+ // at some point, we have to sort
+ mbSorted = false;
+ }
+ }
+ else
+ {
+ // at some point, we have to sort
+ mbSorted = false;
+ }
+ }
+ }
+}
+
+void SdrMarkList::DeleteMark(size_t nNum)
+{
+ SdrMark* pMark = GetMark(nNum);
+ DBG_ASSERT(pMark!=nullptr,"DeleteMark: MarkEntry not found.");
+
+ if(pMark)
+ {
+ maList.erase(maList.begin() + nNum);
+ if (maList.empty())
+ mbSorted = true; //we're empty, so can be considered sorted
+ SetNameDirty();
+ }
+}
+
+void SdrMarkList::ReplaceMark(const SdrMark& rNewMark, size_t nNum)
+{
+ SdrMark* pMark = GetMark(nNum);
+ DBG_ASSERT(pMark!=nullptr,"ReplaceMark: MarkEntry not found.");
+
+ if(pMark)
+ {
+ SetNameDirty();
+ maList[nNum].reset(new SdrMark(rNewMark));
+ mbSorted = false;
+ }
+}
+
+void SdrMarkList::Merge(const SdrMarkList& rSrcList, bool bReverse)
+{
+ const size_t nCount(rSrcList.maList.size());
+
+ if(rSrcList.mbSorted)
+ {
+ // merge without forcing a Sort in rSrcList
+ bReverse = false;
+ }
+
+ if(!bReverse)
+ {
+ for(size_t i = 0; i < nCount; ++i)
+ {
+ SdrMark* pM = rSrcList.maList[i].get();
+ InsertEntry(*pM);
+ }
+ }
+ else
+ {
+ for(size_t i = nCount; i > 0;)
+ {
+ --i;
+ SdrMark* pM = rSrcList.maList[i].get();
+ InsertEntry(*pM);
+ }
+ }
+}
+
+bool SdrMarkList::DeletePageView(const SdrPageView& rPV)
+{
+ bool bChgd(false);
+
+ for(auto it = maList.begin(); it != maList.end(); )
+ {
+ SdrMark* pMark = it->get();
+
+ if(pMark->GetPageView()==&rPV)
+ {
+ it = maList.erase(it);
+ SetNameDirty();
+ bChgd = true;
+ }
+ else
+ ++it;
+ }
+
+ return bChgd;
+}
+
+bool SdrMarkList::InsertPageView(const SdrPageView& rPV)
+{
+ bool bChgd(false);
+ DeletePageView(rPV); // delete all of them, then append the entire page
+ const SdrObjList* pOL = rPV.GetObjList();
+ const size_t nObjCount(pOL->GetObjCount());
+
+ for(size_t nO = 0; nO < nObjCount; ++nO)
+ {
+ SdrObject* pObj = pOL->GetObj(nO);
+ bool bDoIt(rPV.IsObjMarkable(pObj));
+
+ if(bDoIt)
+ {
+ maList.emplace_back(new SdrMark(pObj, const_cast<SdrPageView*>(&rPV)));
+ SetNameDirty();
+ bChgd = true;
+ }
+ }
+
+ return bChgd;
+}
+
+const OUString& SdrMarkList::GetMarkDescription() const
+{
+ const size_t nCount(GetMarkCount());
+
+ if(mbNameOk && 1 == nCount)
+ {
+ // if it's a single selection, cache only text frame
+ const SdrObject* pObj = GetMark(0)->GetMarkedSdrObj();
+ const SdrTextObj* pTextObj = dynamic_cast<const SdrTextObj*>( pObj );
+
+ if(!pTextObj || !pTextObj->IsTextFrame())
+ {
+ const_cast<SdrMarkList*>(this)->mbNameOk = false;
+ }
+ }
+
+ if(!mbNameOk)
+ {
+ SdrMark* pMark = GetMark(0);
+ OUString aNam;
+
+ if(!nCount)
+ {
+ const_cast<SdrMarkList*>(this)->maMarkName = SvxResId(STR_ObjNameNoObj);
+ }
+ else if(1 == nCount)
+ {
+ if(pMark->GetMarkedSdrObj())
+ {
+ aNam = pMark->GetMarkedSdrObj()->TakeObjNameSingul();
+ }
+ }
+ else
+ {
+ if(pMark->GetMarkedSdrObj())
+ {
+ aNam = pMark->GetMarkedSdrObj()->TakeObjNamePlural();
+ bool bEq(true);
+
+ for(size_t i = 1; i < GetMarkCount() && bEq; ++i)
+ {
+ SdrMark* pMark2 = GetMark(i);
+ OUString aStr1(pMark2->GetMarkedSdrObj()->TakeObjNamePlural());
+ bEq = aNam == aStr1;
+ }
+
+ if(!bEq)
+ {
+ aNam = SvxResId(STR_ObjNamePlural);
+ }
+ }
+
+ aNam = OUString::number( nCount ) + " " + aNam;
+ }
+
+ const_cast<SdrMarkList*>(this)->maMarkName = aNam;
+ const_cast<SdrMarkList*>(this)->mbNameOk = true;
+ }
+
+ return maMarkName;
+}
+
+const OUString& SdrMarkList::GetPointMarkDescription(bool bGlue) const
+{
+ bool& rNameOk = const_cast<bool&>(bGlue ? mbGluePointNameOk : mbPointNameOk);
+ OUString& rName = const_cast<OUString&>(bGlue ? maGluePointName : maPointName);
+ const size_t nMarkCount(GetMarkCount());
+ size_t nMarkPtCnt(0);
+ size_t nMarkPtObjCnt(0);
+ size_t n1stMarkNum(SAL_MAX_SIZE);
+
+ for(size_t nMarkNum = 0; nMarkNum < nMarkCount; ++nMarkNum)
+ {
+ const SdrMark* pMark = GetMark(nMarkNum);
+ const SdrUShortCont& rPts = bGlue ? pMark->GetMarkedGluePoints() : pMark->GetMarkedPoints();
+
+ if (!rPts.empty())
+ {
+ if(n1stMarkNum == SAL_MAX_SIZE)
+ {
+ n1stMarkNum = nMarkNum;
+ }
+
+ nMarkPtCnt += rPts.size();
+ nMarkPtObjCnt++;
+ }
+
+ if(nMarkPtObjCnt > 1 && rNameOk)
+ {
+ // preliminary decision
+ return rName;
+ }
+ }
+
+ if(rNameOk && 1 == nMarkPtObjCnt)
+ {
+ // if it's a single selection, cache only text frame
+ const SdrObject* pObj = GetMark(0)->GetMarkedSdrObj();
+ const SdrTextObj* pTextObj = dynamic_cast<const SdrTextObj*>( pObj );
+
+ if(!pTextObj || !pTextObj->IsTextFrame())
+ {
+ rNameOk = false;
+ }
+ }
+
+ if(!nMarkPtObjCnt)
+ {
+ rName.clear();
+ rNameOk = true;
+ }
+ else if(!rNameOk)
+ {
+ const SdrMark* pMark = GetMark(n1stMarkNum);
+ OUString aNam;
+
+ if(1 == nMarkPtObjCnt)
+ {
+ if(pMark->GetMarkedSdrObj())
+ {
+ aNam = pMark->GetMarkedSdrObj()->TakeObjNameSingul();
+ }
+ }
+ else
+ {
+ if(pMark->GetMarkedSdrObj())
+ {
+ aNam = pMark->GetMarkedSdrObj()->TakeObjNamePlural();
+ }
+
+ bool bEq(true);
+
+ for(size_t i = n1stMarkNum + 1; i < GetMarkCount() && bEq; ++i)
+ {
+ const SdrMark* pMark2 = GetMark(i);
+ const SdrUShortCont& rPts = bGlue ? pMark2->GetMarkedGluePoints() : pMark2->GetMarkedPoints();
+
+ if (!rPts.empty() && pMark2->GetMarkedSdrObj())
+ {
+ OUString aStr1(pMark2->GetMarkedSdrObj()->TakeObjNamePlural());
+ bEq = aNam == aStr1;
+ }
+ }
+
+ if(!bEq)
+ {
+ aNam = SvxResId(STR_ObjNamePlural);
+ }
+
+ aNam = OUString::number( nMarkPtObjCnt ) + " " + aNam;
+ }
+
+ OUString aStr1;
+
+ if(1 == nMarkPtCnt)
+ {
+ aStr1 = SvxResId(bGlue ? STR_ViewMarkedGluePoint : STR_ViewMarkedPoint);
+ }
+ else
+ {
+ aStr1 = SvxResId(bGlue ? STR_ViewMarkedGluePoints : STR_ViewMarkedPoints);
+ aStr1 = aStr1.replaceFirst("%2", OUString::number( nMarkPtCnt ));
+ }
+
+ aStr1 = aStr1.replaceFirst("%1", aNam);
+ rName = aStr1;
+ rNameOk = true;
+ }
+
+ return rName;
+}
+
+bool SdrMarkList::TakeBoundRect(SdrPageView const * pPV, tools::Rectangle& rRect) const
+{
+ bool bFnd(false);
+ tools::Rectangle aR;
+
+ for(size_t i = 0; i < GetMarkCount(); ++i)
+ {
+ SdrMark* pMark = GetMark(i);
+
+ if(!pPV || pMark->GetPageView() == pPV)
+ {
+ if(pMark->GetMarkedSdrObj())
+ {
+ aR = pMark->GetMarkedSdrObj()->GetCurrentBoundRect();
+
+ if(bFnd)
+ {
+ rRect.Union(aR);
+ }
+ else
+ {
+ rRect = aR;
+ bFnd = true;
+ }
+ }
+ }
+ }
+
+ return bFnd;
+}
+
+bool SdrMarkList::TakeSnapRect(SdrPageView const * pPV, tools::Rectangle& rRect) const
+{
+ bool bFnd(false);
+
+ for(size_t i = 0; i < GetMarkCount(); ++i)
+ {
+ SdrMark* pMark = GetMark(i);
+
+ if(!pPV || pMark->GetPageView() == pPV)
+ {
+ if(pMark->GetMarkedSdrObj())
+ {
+ tools::Rectangle aR(pMark->GetMarkedSdrObj()->GetSnapRect());
+
+ if(bFnd)
+ {
+ rRect.Union(aR);
+ }
+ else
+ {
+ rRect = aR;
+ bFnd = true;
+ }
+ }
+ }
+ }
+
+ return bFnd;
+}
+
+
+namespace sdr
+{
+ ViewSelection::ViewSelection()
+ : mbEdgesOfMarkedNodesDirty(false)
+ {
+ }
+
+ void ViewSelection::SetEdgesOfMarkedNodesDirty()
+ {
+ if(!mbEdgesOfMarkedNodesDirty)
+ {
+ mbEdgesOfMarkedNodesDirty = true;
+ maEdgesOfMarkedNodes.Clear();
+ maMarkedEdgesOfMarkedNodes.Clear();
+ maAllMarkedObjects.clear();
+ }
+ }
+
+ const SdrMarkList& ViewSelection::GetEdgesOfMarkedNodes() const
+ {
+ if(mbEdgesOfMarkedNodesDirty)
+ {
+ const_cast<ViewSelection*>(this)->ImpForceEdgesOfMarkedNodes();
+ }
+
+ return maEdgesOfMarkedNodes;
+ }
+
+ const SdrMarkList& ViewSelection::GetMarkedEdgesOfMarkedNodes() const
+ {
+ if(mbEdgesOfMarkedNodesDirty)
+ {
+ const_cast<ViewSelection*>(this)->ImpForceEdgesOfMarkedNodes();
+ }
+
+ return maMarkedEdgesOfMarkedNodes;
+ }
+
+ const std::vector<SdrObject*>& ViewSelection::GetAllMarkedObjects() const
+ {
+ if(mbEdgesOfMarkedNodesDirty)
+ const_cast<ViewSelection*>(this)->ImpForceEdgesOfMarkedNodes();
+
+ return maAllMarkedObjects;
+ }
+
+ void ViewSelection::ImplCollectCompleteSelection(SdrObject* pObj)
+ {
+ if(!pObj)
+ return;
+
+ bool bIsGroup(pObj->IsGroupObject());
+
+ if(bIsGroup && dynamic_cast< const E3dObject* >(pObj) != nullptr && dynamic_cast< const E3dScene* >(pObj) == nullptr)
+ {
+ bIsGroup = false;
+ }
+
+ if(bIsGroup)
+ {
+ SdrObjList* pList = pObj->GetSubList();
+
+ for(size_t a = 0; a < pList->GetObjCount(); ++a)
+ {
+ SdrObject* pObj2 = pList->GetObj(a);
+ ImplCollectCompleteSelection(pObj2);
+ }
+ }
+
+ maAllMarkedObjects.push_back(pObj);
+ }
+
+ void ViewSelection::ImpForceEdgesOfMarkedNodes()
+ {
+ if(!mbEdgesOfMarkedNodesDirty)
+ return;
+
+ mbEdgesOfMarkedNodesDirty = false;
+ maMarkedObjectList.ForceSort();
+ maEdgesOfMarkedNodes.Clear();
+ maMarkedEdgesOfMarkedNodes.Clear();
+ maAllMarkedObjects.clear();
+
+ // GetMarkCount after ForceSort
+ const size_t nMarkCount(maMarkedObjectList.GetMarkCount());
+
+ for(size_t a = 0; a < nMarkCount; ++a)
+ {
+ SdrObject* pCandidate = maMarkedObjectList.GetMark(a)->GetMarkedSdrObj();
+
+ if(pCandidate)
+ {
+ // build transitive hull
+ ImplCollectCompleteSelection(pCandidate);
+
+ // travel over broadcaster/listener to access edges connected to the selected object
+ const SfxBroadcaster* pBC = pCandidate->GetBroadcaster();
+
+ if(pBC)
+ {
+ const size_t nLstCnt(pBC->GetSizeOfVector());
+
+ for(size_t nl=0; nl < nLstCnt; ++nl)
+ {
+ SfxListener* pLst = pBC->GetListener(nl);
+ SdrEdgeObj* pEdge = dynamic_cast<SdrEdgeObj*>( pLst );
+
+ if(pEdge && pEdge->IsInserted() && pEdge->getSdrPageFromSdrObject() == pCandidate->getSdrPageFromSdrObject())
+ {
+ SdrMark aM(pEdge, maMarkedObjectList.GetMark(a)->GetPageView());
+
+ if(pEdge->GetConnectedNode(true) == pCandidate)
+ {
+ aM.SetCon1(true);
+ }
+
+ if(pEdge->GetConnectedNode(false) == pCandidate)
+ {
+ aM.SetCon2(true);
+ }
+
+ if(SAL_MAX_SIZE == maMarkedObjectList.FindObject(pEdge))
+ {
+ // check if it itself is selected
+ maEdgesOfMarkedNodes.InsertEntry(aM);
+ }
+ else
+ {
+ maMarkedEdgesOfMarkedNodes.InsertEntry(aM);
+ }
+ }
+ }
+ }
+ }
+ }
+
+ maEdgesOfMarkedNodes.ForceSort();
+ maMarkedEdgesOfMarkedNodes.ForceSort();
+ }
+} // end of namespace sdr
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/svdraw/svdmodel.cxx b/svx/source/svdraw/svdmodel.cxx
new file mode 100644
index 000000000..acf05bd94
--- /dev/null
+++ b/svx/source/svdraw/svdmodel.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 <svx/svdmodel.hxx>
+#include <cassert>
+#include <math.h>
+#include <sal/log.hxx>
+#include <rtl/ustrbuf.hxx>
+#include <com/sun/star/lang/XComponent.hpp>
+#include <com/sun/star/document/XStorageBasedDocument.hpp>
+#include <com/sun/star/embed/ElementModes.hpp>
+#include <unotools/configmgr.hxx>
+#include <unotools/pathoptions.hxx>
+#include <svl/whiter.hxx>
+#include <svl/asiancfg.hxx>
+#include <svx/xbtmpit.hxx>
+#include <svx/xlndsit.hxx>
+#include <svx/xlnedit.hxx>
+#include <svx/xflgrit.hxx>
+#include <svx/xflftrit.hxx>
+#include <svx/xflhtit.hxx>
+#include <svx/xlnstit.hxx>
+#include <editeng/editeng.hxx>
+#include <svx/xtable.hxx>
+#include <svx/svdpage.hxx>
+#include <svx/svdlayer.hxx>
+#include <svx/svdundo.hxx>
+#include <svx/svdpool.hxx>
+#include <svx/svdobj.hxx>
+#include <svx/svdotext.hxx>
+#include <textchain.hxx>
+#include <svx/svdetc.hxx>
+#include <svx/svdoutl.hxx>
+#include <svx/dialmgr.hxx>
+#include <svx/strings.hrc>
+#include <svdoutlinercache.hxx>
+#include <svx/sdasitm.hxx>
+#include <officecfg/Office/Common.hxx>
+#include <editeng/fontitem.hxx>
+#include <editeng/colritem.hxx>
+#include <editeng/fhgtitem.hxx>
+#include <svl/style.hxx>
+#include <editeng/forbiddencharacterstable.hxx>
+#include <comphelper/servicehelper.hxx>
+#include <comphelper/storagehelper.hxx>
+#include <unotools/localedatawrapper.hxx>
+#include <unotools/syslocale.hxx>
+#include <editeng/eeitem.hxx>
+#include <svl/itemset.hxx>
+#include <vcl/settings.hxx>
+#include <vcl/svapp.hxx>
+#include <memory>
+#include <libxml/xmlwriter.h>
+#include <sfx2/viewsh.hxx>
+#include <o3tl/enumrange.hxx>
+#include <tools/diagnose_ex.h>
+#include <tools/UnitConversion.hxx>
+#include <svx/ColorSets.hxx>
+
+using namespace ::com::sun::star;
+using namespace ::com::sun::star::uno;
+using namespace ::com::sun::star::lang;
+
+
+struct SdrModelImpl
+{
+ SfxUndoManager* mpUndoManager;
+ SdrUndoFactory* mpUndoFactory;
+ bool mbAnchoredTextOverflowLegacy; // tdf#99729 compatibility flag
+ std::unique_ptr<svx::Theme> mpTheme;
+
+ SdrModelImpl()
+ : mpUndoManager(nullptr)
+ , mpUndoFactory(nullptr)
+ , mbAnchoredTextOverflowLegacy(false)
+ {}
+};
+
+
+SdrModel::SdrModel(SfxItemPool* pPool, comphelper::IEmbeddedHelper* pEmbeddedHelper, bool bDisablePropertyFiles)
+ : m_aObjUnit(SdrEngineDefaults::GetMapFraction())
+ , m_eObjUnit(SdrEngineDefaults::GetMapUnit())
+ , m_eUIUnit(FieldUnit::MM)
+ , m_aUIScale(Fraction(1,1))
+ , m_nUIUnitDecimalMark(0)
+ , m_pLayerAdmin(new SdrLayerAdmin)
+ , m_pItemPool(pPool)
+ , m_pEmbeddedHelper(pEmbeddedHelper)
+ , mnDefTextHgt(SdrEngineDefaults::GetFontHeight())
+ , m_pRefOutDev(nullptr)
+ , m_pDefaultStyleSheet(nullptr)
+ , mpDefaultStyleSheetForSdrGrafObjAndSdrOle2Obj(nullptr)
+ , m_pLinkManager(nullptr)
+ , m_nUndoLevel(0)
+ , m_bIsWriter(true)
+ , mbUndoEnabled(true)
+ , mbChanged(false)
+ , m_bPagNumsDirty(false)
+ , m_bMPgNumsDirty(false)
+ , m_bTransportContainer(false)
+ , m_bReadOnly(false)
+ , m_bTransparentTextFrames(false)
+ , m_bSwapGraphics(false)
+ , m_bPasteResize(false)
+ , m_bStarDrawPreviewMode(false)
+ , mbDisableTextEditUsesCommonUndoManager(false)
+ , mbVOCInvalidationIsReliable(false)
+ , m_nDefaultTabulator(0)
+ , m_nMaxUndoCount(16)
+ , m_pTextChain(new TextChain)
+ , mpImpl(new SdrModelImpl)
+ , mnCharCompressType(CharCompressType::NONE)
+ , mnHandoutPageCount(0)
+ , mbModelLocked(false)
+ , mbKernAsianPunctuation(false)
+ , mbAddExtLeading(false)
+ , mbInDestruction(false)
+{
+ if (!utl::ConfigManager::IsFuzzing())
+ {
+ mnCharCompressType = static_cast<CharCompressType>(
+ officecfg::Office::Common::AsianLayout::CompressCharacterDistance::get());
+ }
+
+ if (m_pItemPool == nullptr)
+ {
+ m_pItemPool = new SdrItemPool(nullptr);
+ // Outliner doesn't have its own Pool, so use the EditEngine's
+ rtl::Reference<SfxItemPool> pOutlPool=EditEngine::CreatePool();
+ // OutlinerPool as SecondaryPool of SdrPool
+ m_pItemPool->SetSecondaryPool(pOutlPool.get());
+ // remember that I created both pools myself
+ m_bIsWriter = false;
+ }
+ m_pItemPool->SetDefaultMetric(m_eObjUnit);
+
+// using static SdrEngineDefaults only if default SvxFontHeight item is not available
+ const SfxPoolItem* pPoolItem = m_pItemPool->GetPoolDefaultItem( EE_CHAR_FONTHEIGHT );
+ if (pPoolItem)
+ mnDefTextHgt = static_cast<const SvxFontHeightItem*>(pPoolItem)->GetHeight();
+
+ m_pItemPool->SetPoolDefaultItem( makeSdrTextWordWrapItem( false ) );
+
+ SetTextDefaults();
+ m_pLayerAdmin->SetModel(this);
+ ImpSetUIUnit();
+
+ // can't create DrawOutliner OnDemand, because I can't get the Pool,
+ // then (only from 302 onwards!)
+ m_pDrawOutliner = SdrMakeOutliner(OutlinerMode::TextObject, *this);
+ ImpSetOutlinerDefaults(m_pDrawOutliner.get(), true);
+
+ m_pHitTestOutliner = SdrMakeOutliner(OutlinerMode::TextObject, *this);
+ ImpSetOutlinerDefaults(m_pHitTestOutliner.get(), true);
+
+ /* Start Text Chaining related code */
+ // Initialize Chaining Outliner
+ m_pChainingOutliner = SdrMakeOutliner( OutlinerMode::TextObject, *this );
+ ImpSetOutlinerDefaults(m_pChainingOutliner.get(), true);
+
+ ImpCreateTables(bDisablePropertyFiles || utl::ConfigManager::IsFuzzing());
+}
+
+SdrModel::~SdrModel()
+{
+ mbInDestruction = true;
+
+ Broadcast(SdrHint(SdrHintKind::ModelCleared));
+
+ mpOutlinerCache.reset();
+
+ ClearUndoBuffer();
+#ifdef DBG_UTIL
+ SAL_WARN_IF(m_pCurrentUndoGroup, "svx", "In the Dtor of the SdrModel there is an open Undo left: \""
+ << m_pCurrentUndoGroup->GetComment() << '\"');
+#endif
+ m_pCurrentUndoGroup.reset();
+
+ ClearModel(true);
+
+#ifdef DBG_UTIL
+ // SdrObjectLifetimeWatchDog:
+ if(!maAllIncarnatedObjects.empty())
+ {
+ SAL_WARN("svx","SdrModel::~SdrModel: Not all incarnations of SdrObjects deleted, possible memory leak (!)");
+ const std::vector<const SdrObject*> maRemainingObjects(maAllIncarnatedObjects.begin(),
+ maAllIncarnatedObjects.end());
+ for (auto pSdrObject : maRemainingObjects)
+ {
+ SdrObject* pCandidate(const_cast<SdrObject*>(pSdrObject));
+ // calling SdrObject::Free will change maAllIncarnatedObjects, and potentially remove
+ // more than one, so check if the candidate is still in the updated list before Free
+ if (maAllIncarnatedObjects.find(pSdrObject) != maAllIncarnatedObjects.end())
+ SdrObject::Free(pCandidate);
+ }
+ }
+#endif
+
+ m_pLayerAdmin.reset();
+
+ m_pTextChain.reset();
+ // Delete DrawOutliner only after deleting ItemPool, because ItemPool
+ // references Items of the DrawOutliner!
+ m_pChainingOutliner.reset();
+ m_pHitTestOutliner.reset();
+ m_pDrawOutliner.reset();
+
+ // delete StyleSheetPool, derived classes should not do this since
+ // the DrawingEngine may need it in its destructor
+ if( mxStyleSheetPool.is() )
+ {
+ Reference< XComponent > xComponent( static_cast< cppu::OWeakObject* >( mxStyleSheetPool.get() ), UNO_QUERY );
+ if( xComponent.is() ) try
+ {
+ xComponent->dispose();
+ }
+ catch( RuntimeException& )
+ {
+ }
+ mxStyleSheetPool.clear();
+ }
+
+ mpForbiddenCharactersTable.reset();
+
+ delete mpImpl->mpUndoFactory;
+}
+
+void SdrModel::SetSwapGraphics()
+{
+ m_bSwapGraphics = true;
+}
+
+bool SdrModel::IsReadOnly() const
+{
+ return m_bReadOnly;
+}
+
+void SdrModel::SetReadOnly(bool bYes)
+{
+ m_bReadOnly=bYes;
+}
+
+
+void SdrModel::SetMaxUndoActionCount(sal_uInt32 nCount)
+{
+ if (nCount<1) nCount=1;
+ m_nMaxUndoCount=nCount;
+ while (m_aUndoStack.size()>m_nMaxUndoCount)
+ m_aUndoStack.pop_back();
+}
+
+void SdrModel::ClearUndoBuffer()
+{
+ m_aUndoStack.clear();
+ m_aRedoStack.clear();
+}
+
+bool SdrModel::HasUndoActions() const
+{
+ return !m_aUndoStack.empty();
+}
+
+bool SdrModel::HasRedoActions() const
+{
+ return !m_aRedoStack.empty();
+}
+
+void SdrModel::Undo()
+{
+ if( mpImpl->mpUndoManager )
+ {
+ OSL_FAIL("svx::SdrModel::Undo(), method not supported with application undo manager!");
+ }
+ else
+ {
+ if(HasUndoActions())
+ {
+ SfxUndoAction* pDo = m_aUndoStack.front().get();
+ const bool bWasUndoEnabled = mbUndoEnabled;
+ mbUndoEnabled = false;
+ pDo->Undo();
+ std::unique_ptr<SfxUndoAction> p = std::move(m_aUndoStack.front());
+ m_aUndoStack.pop_front();
+ m_aRedoStack.emplace_front(std::move(p));
+ mbUndoEnabled = bWasUndoEnabled;
+ }
+ }
+}
+
+void SdrModel::Redo()
+{
+ if( mpImpl->mpUndoManager )
+ {
+ OSL_FAIL("svx::SdrModel::Redo(), method not supported with application undo manager!");
+ }
+ else
+ {
+ if(HasRedoActions())
+ {
+ SfxUndoAction* pDo = m_aRedoStack.front().get();
+ const bool bWasUndoEnabled = mbUndoEnabled;
+ mbUndoEnabled = false;
+ pDo->Redo();
+ std::unique_ptr<SfxUndoAction> p = std::move(m_aRedoStack.front());
+ m_aRedoStack.pop_front();
+ m_aUndoStack.emplace_front(std::move(p));
+ mbUndoEnabled = bWasUndoEnabled;
+ }
+ }
+}
+
+void SdrModel::Repeat(SfxRepeatTarget& rView)
+{
+ if( mpImpl->mpUndoManager )
+ {
+ OSL_FAIL("svx::SdrModel::Redo(), method not supported with application undo manager!");
+ }
+ else
+ {
+ if(HasUndoActions())
+ {
+ SfxUndoAction* pDo = m_aUndoStack.front().get();
+ if(pDo->CanRepeat(rView))
+ {
+ pDo->Repeat(rView);
+ }
+ }
+ }
+}
+
+void SdrModel::ImpPostUndoAction(std::unique_ptr<SdrUndoAction> pUndo)
+{
+ DBG_ASSERT( mpImpl->mpUndoManager == nullptr, "svx::SdrModel::ImpPostUndoAction(), method not supported with application undo manager!" );
+ if( !IsUndoEnabled() )
+ return;
+
+ if (m_aUndoLink)
+ {
+ m_aUndoLink(std::move(pUndo));
+ }
+ else
+ {
+ m_aUndoStack.emplace_front(std::move(pUndo));
+ while (m_aUndoStack.size()>m_nMaxUndoCount)
+ {
+ m_aUndoStack.pop_back();
+ }
+ m_aRedoStack.clear();
+ }
+}
+
+void SdrModel::BegUndo()
+{
+ if( mpImpl->mpUndoManager )
+ {
+ ViewShellId nViewShellId(-1);
+ if (SfxViewShell* pViewShell = SfxViewShell::Current())
+ nViewShellId = pViewShell->GetViewShellId();
+ mpImpl->mpUndoManager->EnterListAction("","",0,nViewShellId);
+ m_nUndoLevel++;
+ }
+ else if( IsUndoEnabled() )
+ {
+ if(!m_pCurrentUndoGroup)
+ {
+ m_pCurrentUndoGroup.reset(new SdrUndoGroup(*this));
+ m_nUndoLevel=1;
+ }
+ else
+ {
+ m_nUndoLevel++;
+ }
+ }
+}
+
+void SdrModel::BegUndo(const OUString& rComment)
+{
+ if( mpImpl->mpUndoManager )
+ {
+ ViewShellId nViewShellId(-1);
+ if (SfxViewShell* pViewShell = SfxViewShell::Current())
+ nViewShellId = pViewShell->GetViewShellId();
+ mpImpl->mpUndoManager->EnterListAction( rComment, "", 0, nViewShellId );
+ m_nUndoLevel++;
+ }
+ else if( IsUndoEnabled() )
+ {
+ BegUndo();
+ if (m_nUndoLevel==1)
+ {
+ m_pCurrentUndoGroup->SetComment(rComment);
+ }
+ }
+}
+
+void SdrModel::BegUndo(const OUString& rComment, const OUString& rObjDescr, SdrRepeatFunc eFunc)
+{
+ if( mpImpl->mpUndoManager )
+ {
+ OUString aComment(rComment);
+ if( !aComment.isEmpty() && !rObjDescr.isEmpty() )
+ {
+ aComment = aComment.replaceFirst("%1", rObjDescr);
+ }
+ ViewShellId nViewShellId(-1);
+ if (SfxViewShell* pViewShell = SfxViewShell::Current())
+ nViewShellId = pViewShell->GetViewShellId();
+ mpImpl->mpUndoManager->EnterListAction( aComment,"",0,nViewShellId );
+ m_nUndoLevel++;
+ }
+ else if( IsUndoEnabled() )
+ {
+ BegUndo();
+ if (m_nUndoLevel==1)
+ {
+ m_pCurrentUndoGroup->SetComment(rComment);
+ m_pCurrentUndoGroup->SetObjDescription(rObjDescr);
+ m_pCurrentUndoGroup->SetRepeatFunction(eFunc);
+ }
+ }
+}
+
+void SdrModel::EndUndo()
+{
+ DBG_ASSERT(m_nUndoLevel!=0,"SdrModel::EndUndo(): UndoLevel is already 0!");
+ if( mpImpl->mpUndoManager )
+ {
+ if( m_nUndoLevel )
+ {
+ m_nUndoLevel--;
+ mpImpl->mpUndoManager->LeaveListAction();
+ }
+ }
+ else
+ {
+ if(m_pCurrentUndoGroup!=nullptr && IsUndoEnabled())
+ {
+ m_nUndoLevel--;
+ if(m_nUndoLevel==0)
+ {
+ if(m_pCurrentUndoGroup->GetActionCount()!=0)
+ {
+ ImpPostUndoAction(std::move(m_pCurrentUndoGroup));
+ }
+ else
+ {
+ // was empty
+ m_pCurrentUndoGroup.reset();
+ }
+ }
+ }
+ }
+}
+
+void SdrModel::SetUndoComment(const OUString& rComment)
+{
+ DBG_ASSERT(m_nUndoLevel!=0,"SdrModel::SetUndoComment(): UndoLevel is already 0!");
+
+ if( mpImpl->mpUndoManager )
+ {
+ OSL_FAIL("svx::SdrModel::SetUndoComment(), method not supported with application undo manager!" );
+ }
+ else if( IsUndoEnabled() && m_nUndoLevel==1)
+ {
+ m_pCurrentUndoGroup->SetComment(rComment);
+ }
+}
+
+void SdrModel::SetUndoComment(const OUString& rComment, const OUString& rObjDescr)
+{
+ DBG_ASSERT(m_nUndoLevel!=0,"SdrModel::SetUndoComment(): UndoLevel is already 0!");
+ if( mpImpl->mpUndoManager )
+ {
+ OSL_FAIL("svx::SdrModel::SetUndoComment(), method not supported with application undo manager!" );
+ }
+ else
+ {
+ if (m_nUndoLevel==1)
+ {
+ m_pCurrentUndoGroup->SetComment(rComment);
+ m_pCurrentUndoGroup->SetObjDescription(rObjDescr);
+ }
+ }
+}
+
+void SdrModel::AddUndo(std::unique_ptr<SdrUndoAction> pUndo)
+{
+ if( mpImpl->mpUndoManager )
+ {
+ mpImpl->mpUndoManager->AddUndoAction( std::move(pUndo) );
+ }
+ else if( IsUndoEnabled() )
+ {
+ if (m_pCurrentUndoGroup)
+ {
+ m_pCurrentUndoGroup->AddAction(std::move(pUndo));
+ }
+ else
+ {
+ ImpPostUndoAction(std::move(pUndo));
+ }
+ }
+}
+
+void SdrModel::EnableUndo( bool bEnable )
+{
+ if( mpImpl->mpUndoManager )
+ {
+ mpImpl->mpUndoManager->EnableUndo( bEnable );
+ }
+ else
+ {
+ mbUndoEnabled = bEnable;
+ }
+}
+
+bool SdrModel::IsUndoEnabled() const
+{
+ if( mpImpl->mpUndoManager )
+ {
+ return mpImpl->mpUndoManager->IsUndoEnabled();
+ }
+ else
+ {
+ return mbUndoEnabled;
+ }
+}
+
+void SdrModel::ImpCreateTables(bool bDisablePropertyFiles)
+{
+ // use standard path for initial construction
+ const OUString aTablePath(!bDisablePropertyFiles ? SvtPathOptions().GetPalettePath() : "");
+
+ for( auto i : o3tl::enumrange<XPropertyListType>() )
+ {
+ maProperties[i] = XPropertyList::CreatePropertyList(i, aTablePath, ""/*TODO?*/ );
+ }
+}
+
+void SdrModel::ClearModel(bool bCalledFromDestructor)
+{
+ if(bCalledFromDestructor)
+ {
+ mbInDestruction = true;
+ }
+
+ sal_Int32 i;
+ // delete all drawing pages
+ sal_Int32 nCount=GetPageCount();
+ for (i=nCount-1; i>=0; i--)
+ {
+ DeletePage( static_cast<sal_uInt16>(i) );
+ }
+ maPages.clear();
+ PageListChanged();
+
+ // delete all Masterpages
+ nCount=GetMasterPageCount();
+ for(i=nCount-1; i>=0; i--)
+ {
+ DeleteMasterPage( static_cast<sal_uInt16>(i) );
+ }
+ maMasterPages.clear();
+ MasterPageListChanged();
+
+ m_pLayerAdmin->ClearLayers();
+}
+
+SdrModel* SdrModel::AllocModel() const
+{
+ SdrModel* pModel=new SdrModel();
+ pModel->SetScaleUnit(m_eObjUnit,m_aObjUnit);
+ return pModel;
+}
+
+rtl::Reference<SdrPage> SdrModel::AllocPage(bool bMasterPage)
+{
+ return new SdrPage(*this,bMasterPage);
+}
+
+void SdrModel::SetTextDefaults() const
+{
+ SetTextDefaults( m_pItemPool.get(), mnDefTextHgt );
+}
+
+void SdrModel::SetTextDefaults( SfxItemPool* pItemPool, sal_Int32 nDefTextHgt )
+{
+ // set application-language specific dynamic pool language defaults
+ SvxFontItem aSvxFontItem( EE_CHAR_FONTINFO) ;
+ SvxFontItem aSvxFontItemCJK(EE_CHAR_FONTINFO_CJK);
+ SvxFontItem aSvxFontItemCTL(EE_CHAR_FONTINFO_CTL);
+ LanguageType nLanguage;
+ if (!utl::ConfigManager::IsFuzzing())
+ nLanguage = Application::GetSettings().GetLanguageTag().getLanguageType();
+ else
+ nLanguage = LANGUAGE_ENGLISH_US;
+
+ // get DEFAULTFONT_LATIN_TEXT and set at pool as dynamic default
+ vcl::Font aFont(OutputDevice::GetDefaultFont(DefaultFontType::LATIN_TEXT, nLanguage, GetDefaultFontFlags::OnlyOne));
+ aSvxFontItem.SetFamily(aFont.GetFamilyType());
+ aSvxFontItem.SetFamilyName(aFont.GetFamilyName());
+ aSvxFontItem.SetStyleName(OUString());
+ aSvxFontItem.SetPitch( aFont.GetPitch());
+ aSvxFontItem.SetCharSet( aFont.GetCharSet() );
+ pItemPool->SetPoolDefaultItem(aSvxFontItem);
+
+ // get DEFAULTFONT_CJK_TEXT and set at pool as dynamic default
+ vcl::Font aFontCJK(OutputDevice::GetDefaultFont(DefaultFontType::CJK_TEXT, nLanguage, GetDefaultFontFlags::OnlyOne));
+ aSvxFontItemCJK.SetFamily( aFontCJK.GetFamilyType());
+ aSvxFontItemCJK.SetFamilyName(aFontCJK.GetFamilyName());
+ aSvxFontItemCJK.SetStyleName(OUString());
+ aSvxFontItemCJK.SetPitch( aFontCJK.GetPitch());
+ aSvxFontItemCJK.SetCharSet( aFontCJK.GetCharSet());
+ pItemPool->SetPoolDefaultItem(aSvxFontItemCJK);
+
+ // get DEFAULTFONT_CTL_TEXT and set at pool as dynamic default
+ vcl::Font aFontCTL(OutputDevice::GetDefaultFont(DefaultFontType::CTL_TEXT, nLanguage, GetDefaultFontFlags::OnlyOne));
+ aSvxFontItemCTL.SetFamily(aFontCTL.GetFamilyType());
+ aSvxFontItemCTL.SetFamilyName(aFontCTL.GetFamilyName());
+ aSvxFontItemCTL.SetStyleName(OUString());
+ aSvxFontItemCTL.SetPitch( aFontCTL.GetPitch() );
+ aSvxFontItemCTL.SetCharSet( aFontCTL.GetCharSet());
+ pItemPool->SetPoolDefaultItem(aSvxFontItemCTL);
+
+ // set dynamic FontHeight defaults
+ pItemPool->SetPoolDefaultItem( SvxFontHeightItem(nDefTextHgt, 100, EE_CHAR_FONTHEIGHT ) );
+ pItemPool->SetPoolDefaultItem( SvxFontHeightItem(nDefTextHgt, 100, EE_CHAR_FONTHEIGHT_CJK ) );
+ pItemPool->SetPoolDefaultItem( SvxFontHeightItem(nDefTextHgt, 100, EE_CHAR_FONTHEIGHT_CTL ) );
+
+ // set FontColor defaults
+ pItemPool->SetPoolDefaultItem( SvxColorItem(SdrEngineDefaults::GetFontColor(), EE_CHAR_COLOR) );
+}
+
+SdrOutliner& SdrModel::GetDrawOutliner(const SdrTextObj* pObj) const
+{
+ m_pDrawOutliner->SetTextObj(pObj);
+ return *m_pDrawOutliner;
+}
+
+SdrOutliner& SdrModel::GetChainingOutliner(const SdrTextObj* pObj) const
+{
+ m_pChainingOutliner->SetTextObj(pObj);
+ return *m_pChainingOutliner;
+}
+
+const SdrTextObj* SdrModel::GetFormattingTextObj() const
+{
+ if (m_pDrawOutliner!=nullptr) {
+ return m_pDrawOutliner->GetTextObj();
+ }
+ return nullptr;
+}
+
+void SdrModel::ImpSetOutlinerDefaults( SdrOutliner* pOutliner, bool bInit )
+{
+ // Initialization of the Outliners for drawing text and HitTest
+ if( bInit )
+ {
+ pOutliner->EraseVirtualDevice();
+ pOutliner->SetUpdateLayout(false);
+ pOutliner->SetEditTextObjectPool(m_pItemPool.get());
+ pOutliner->SetDefTab(m_nDefaultTabulator);
+ }
+
+ pOutliner->SetRefDevice(GetRefDevice());
+ Outliner::SetForbiddenCharsTable(GetForbiddenCharsTable());
+ pOutliner->SetAsianCompressionMode( mnCharCompressType );
+ pOutliner->SetKernAsianPunctuation( IsKernAsianPunctuation() );
+ pOutliner->SetAddExtLeading( IsAddExtLeading() );
+
+ if ( !GetRefDevice() )
+ {
+ MapMode aMapMode(m_eObjUnit, Point(0,0), m_aObjUnit, m_aObjUnit);
+ pOutliner->SetRefMapMode(aMapMode);
+ }
+}
+
+void SdrModel::SetRefDevice(OutputDevice* pDev)
+{
+ m_pRefOutDev=pDev;
+ ImpSetOutlinerDefaults( m_pDrawOutliner.get() );
+ ImpSetOutlinerDefaults( m_pHitTestOutliner.get() );
+ RefDeviceChanged();
+}
+
+void SdrModel::ImpReformatAllTextObjects()
+{
+ if( isLocked() )
+ return;
+
+ sal_uInt16 nCount=GetMasterPageCount();
+ sal_uInt16 nNum;
+ for (nNum=0; nNum<nCount; nNum++) {
+ GetMasterPage(nNum)->ReformatAllTextObjects();
+ }
+ nCount=GetPageCount();
+ for (nNum=0; nNum<nCount; nNum++) {
+ GetPage(nNum)->ReformatAllTextObjects();
+ }
+}
+
+/* steps over all available pages and sends notify messages to
+ all edge objects that are connected to other objects so that
+ they may reposition themselves
+*/
+void SdrModel::ImpReformatAllEdgeObjects()
+{
+ if( isLocked() )
+ return;
+
+ sal_uInt16 nCount=GetMasterPageCount();
+ sal_uInt16 nNum;
+ for (nNum=0; nNum<nCount; nNum++)
+ {
+ GetMasterPage(nNum)->ReformatAllEdgeObjects();
+ }
+ nCount=GetPageCount();
+ for (nNum=0; nNum<nCount; nNum++)
+ {
+ GetPage(nNum)->ReformatAllEdgeObjects();
+ }
+}
+
+uno::Reference<embed::XStorage> SdrModel::GetDocumentStorage() const
+{
+ uno::Reference<document::XStorageBasedDocument> const xSBD(
+ const_cast<SdrModel*>(this)->getUnoModel(), uno::UNO_QUERY);
+ if (!xSBD.is())
+ {
+ SAL_WARN("svx", "no UNO model");
+ return nullptr;
+ }
+ return xSBD->getDocumentStorage();
+}
+
+uno::Reference<io::XInputStream>
+SdrModel::GetDocumentStream( OUString const& rURL,
+ ::comphelper::LifecycleProxy const & rProxy) const
+{
+ uno::Reference<embed::XStorage> const xStorage(GetDocumentStorage());
+ if (!xStorage.is())
+ {
+ SAL_WARN("svx", "no storage?");
+ return nullptr;
+ }
+ try {
+ uno::Reference<io::XStream> const xStream(
+ ::comphelper::OStorageHelper::GetStreamAtPackageURL(
+ xStorage, rURL, embed::ElementModes::READ, rProxy));
+ return (xStream.is()) ? xStream->getInputStream() : nullptr;
+ }
+ catch (container::NoSuchElementException const&)
+ {
+ SAL_INFO("svx", "not found");
+ }
+ catch (uno::Exception const&)
+ {
+ TOOLS_WARN_EXCEPTION("svx", "");
+ }
+ return nullptr;
+}
+
+// convert template attributes from the string into "hard" attributes
+void SdrModel::BurnInStyleSheetAttributes()
+{
+ sal_uInt16 nCount=GetMasterPageCount();
+ sal_uInt16 nNum;
+ for (nNum=0; nNum<nCount; nNum++) {
+ GetMasterPage(nNum)->BurnInStyleSheetAttributes();
+ }
+ nCount=GetPageCount();
+ for (nNum=0; nNum<nCount; nNum++) {
+ GetPage(nNum)->BurnInStyleSheetAttributes();
+ }
+}
+
+void SdrModel::RefDeviceChanged()
+{
+ Broadcast(SdrHint(SdrHintKind::RefDeviceChange));
+ ImpReformatAllTextObjects();
+}
+
+void SdrModel::SetDefaultFontHeight(sal_Int32 nVal)
+{
+ if (nVal!=mnDefTextHgt) {
+ mnDefTextHgt=nVal;
+ ImpReformatAllTextObjects();
+ }
+}
+
+void SdrModel::SetDefaultTabulator(sal_uInt16 nVal)
+{
+ if (m_nDefaultTabulator!=nVal) {
+ m_nDefaultTabulator=nVal;
+ Outliner& rOutliner=GetDrawOutliner();
+ rOutliner.SetDefTab(nVal);
+ Broadcast(SdrHint(SdrHintKind::DefaultTabChange));
+ ImpReformatAllTextObjects();
+ }
+}
+
+void SdrModel::ImpSetUIUnit()
+{
+ if(0 == m_aUIScale.GetNumerator() || 0 == m_aUIScale.GetDenominator())
+ {
+ m_aUIScale = Fraction(1,1);
+ }
+
+ m_nUIUnitDecimalMark = 0;
+
+ o3tl::Length eFrom = MapToO3tlLength(m_eObjUnit, o3tl::Length::invalid);
+ o3tl::Length eTo;
+
+ switch (m_eUIUnit)
+ {
+ case FieldUnit::CHAR:
+ case FieldUnit::LINE:
+ eTo = o3tl::Length::invalid;
+ break;
+ case FieldUnit::PERCENT:
+ m_nUIUnitDecimalMark += 2;
+ [[fallthrough]];
+ default:
+ eTo = FieldToO3tlLength(m_eUIUnit, o3tl::Length::invalid);
+ } // switch
+
+ sal_Int32 nMul = 1, nDiv = 1;
+ if (eFrom != o3tl::Length::invalid && eTo != o3tl::Length::invalid)
+ {
+ const auto& [mul, div] = o3tl::getConversionMulDiv(eFrom, eTo);
+ nMul = mul;
+ nDiv = div;
+ }
+ // #i89872# take Unit of Measurement into account
+ if(1 != m_aUIScale.GetDenominator() || 1 != m_aUIScale.GetNumerator())
+ {
+ // divide by UIScale
+ nMul *= m_aUIScale.GetDenominator();
+ nDiv *= m_aUIScale.GetNumerator();
+ }
+
+ // shorten trailing zeros for dividend
+ while(0 == (nMul % 10))
+ {
+ m_nUIUnitDecimalMark--;
+ nMul /= 10;
+ }
+
+ // shorten trailing zeros for divisor
+ while(0 == (nDiv % 10))
+ {
+ m_nUIUnitDecimalMark++;
+ nDiv /= 10;
+ }
+
+ // end preparations, set member values
+ m_aUIUnitFact = Fraction(sal_Int32(nMul), sal_Int32(nDiv));
+ m_aUIUnitStr = GetUnitString(m_eUIUnit);
+}
+
+void SdrModel::SetScaleUnit(MapUnit eMap, const Fraction& rFrac)
+{
+ if (m_eObjUnit!=eMap || m_aObjUnit!=rFrac) {
+ m_eObjUnit=eMap;
+ m_aObjUnit=rFrac;
+ m_pItemPool->SetDefaultMetric(m_eObjUnit);
+ ImpSetUIUnit();
+ ImpSetOutlinerDefaults( m_pDrawOutliner.get() );
+ ImpSetOutlinerDefaults( m_pHitTestOutliner.get() );
+ ImpReformatAllTextObjects();
+ }
+}
+
+void SdrModel::SetScaleUnit(MapUnit eMap)
+{
+ if (m_eObjUnit!=eMap) {
+ m_eObjUnit=eMap;
+ m_pItemPool->SetDefaultMetric(m_eObjUnit);
+ ImpSetUIUnit();
+ ImpSetOutlinerDefaults( m_pDrawOutliner.get() );
+ ImpSetOutlinerDefaults( m_pHitTestOutliner.get() );
+ ImpReformatAllTextObjects();
+ }
+}
+
+void SdrModel::SetScaleFraction(const Fraction& rFrac)
+{
+ if (m_aObjUnit!=rFrac) {
+ m_aObjUnit=rFrac;
+ ImpSetUIUnit();
+ ImpSetOutlinerDefaults( m_pDrawOutliner.get() );
+ ImpSetOutlinerDefaults( m_pHitTestOutliner.get() );
+ ImpReformatAllTextObjects();
+ }
+}
+
+void SdrModel::SetUIUnit(FieldUnit eUnit)
+{
+ if (m_eUIUnit!=eUnit) {
+ m_eUIUnit=eUnit;
+ ImpSetUIUnit();
+ ImpReformatAllTextObjects();
+ }
+}
+
+void SdrModel::SetUIScale(const Fraction& rScale)
+{
+ if (m_aUIScale!=rScale) {
+ m_aUIScale=rScale;
+ ImpSetUIUnit();
+ ImpReformatAllTextObjects();
+ }
+}
+
+void SdrModel::SetUIUnit(FieldUnit eUnit, const Fraction& rScale)
+{
+ if (m_eUIUnit!=eUnit || m_aUIScale!=rScale) {
+ m_eUIUnit=eUnit;
+ m_aUIScale=rScale;
+ ImpSetUIUnit();
+ ImpReformatAllTextObjects();
+ }
+}
+
+OUString SdrModel::GetUnitString(FieldUnit eUnit)
+{
+ switch(eUnit)
+ {
+ default:
+ case FieldUnit::NONE :
+ case FieldUnit::CUSTOM :
+ return OUString();
+ case FieldUnit::MM_100TH:
+ return OUString{"/100mm"};
+ case FieldUnit::MM :
+ return OUString{"mm"};
+ case FieldUnit::CM :
+ return OUString{"cm"};
+ case FieldUnit::M :
+ return OUString{"m"};
+ case FieldUnit::KM :
+ return OUString{"km"};
+ case FieldUnit::TWIP :
+ return OUString{"twip"};
+ case FieldUnit::POINT :
+ return OUString{"pt"};
+ case FieldUnit::PICA :
+ return OUString{"pica"};
+ case FieldUnit::INCH :
+ return OUString{"\""};
+ case FieldUnit::FOOT :
+ return OUString{"ft"};
+ case FieldUnit::MILE :
+ return OUString{"mile(s)"};
+ case FieldUnit::PERCENT:
+ return OUString{"%"};
+ }
+}
+
+OUString SdrModel::GetMetricString(tools::Long nVal, bool bNoUnitChars, sal_Int32 nNumDigits) const
+{
+ // #i22167#
+ // change to double precision usage to not lose decimal places
+ const bool bNegative(nVal < 0);
+ SvtSysLocale aSysLoc;
+ const LocaleDataWrapper& rLoc(aSysLoc.GetLocaleData());
+ double fLocalValue(double(nVal) * double(m_aUIUnitFact));
+
+ if(bNegative)
+ {
+ fLocalValue = -fLocalValue;
+ }
+
+ if( -1 == nNumDigits )
+ {
+ nNumDigits = LocaleDataWrapper::getNumDigits();
+ }
+
+ sal_Int32 nDecimalMark(m_nUIUnitDecimalMark);
+
+ if(nDecimalMark > nNumDigits)
+ {
+ const sal_Int32 nDiff(nDecimalMark - nNumDigits);
+ const double fFactor(pow(10.0, static_cast<int>(nDiff)));
+
+ fLocalValue /= fFactor;
+ nDecimalMark = nNumDigits;
+ }
+ else if(nDecimalMark < nNumDigits)
+ {
+ const sal_Int32 nDiff(nNumDigits - nDecimalMark);
+ const double fFactor(pow(10.0, static_cast<int>(nDiff)));
+
+ fLocalValue *= fFactor;
+ nDecimalMark = nNumDigits;
+ }
+
+ OUStringBuffer aBuf;
+ aBuf.append(static_cast<sal_Int32>(fLocalValue + 0.5));
+
+ if(nDecimalMark < 0)
+ {
+ // negative nDecimalMark (decimal point) means: add zeros
+ sal_Int32 nCount(-nDecimalMark);
+
+ for(sal_Int32 i=0; i<nCount; i++)
+ aBuf.append('0');
+
+ nDecimalMark = 0;
+ }
+
+ // the second condition needs to be <= since inside this loop
+ // also the leading zero is inserted.
+ if (nDecimalMark > 0 && aBuf.getLength() <= nDecimalMark)
+ {
+ // if necessary, add zeros before the decimal point
+ sal_Int32 nCount = nDecimalMark - aBuf.getLength();
+
+ if(nCount >= 0 && LocaleDataWrapper::isNumLeadingZero())
+ nCount++;
+
+ for(sal_Int32 i=0; i<nCount; i++)
+ aBuf.insert(0, '0');
+ }
+
+ const sal_Unicode cDec( rLoc.getNumDecimalSep()[0] );
+
+ // insert the decimal mark character
+ sal_Int32 nBeforeDecimalMark = aBuf.getLength() - nDecimalMark;
+
+ if(nDecimalMark > 0)
+ aBuf.insert(nBeforeDecimalMark, cDec);
+
+ if(!LocaleDataWrapper::isNumTrailingZeros())
+ {
+ sal_Int32 aPos=aBuf.getLength()-1;
+
+ // Remove all trailing zeros.
+ while (aPos>=0 && aBuf[aPos]=='0')
+ --aPos;
+
+ // Remove decimal if it's the last character.
+ if (aPos>=0 && aBuf[aPos]==cDec)
+ --aPos;
+
+ // Adjust aPos to index first char to be truncated, if any
+ if (++aPos<aBuf.getLength())
+ aBuf.truncate(aPos);
+ }
+
+ // if necessary, add separators before every third digit
+ if( nBeforeDecimalMark > 3 )
+ {
+ const OUString& aThoSep( rLoc.getNumThousandSep() );
+ if ( !aThoSep.isEmpty() )
+ {
+ sal_Unicode cTho( aThoSep[0] );
+ sal_Int32 i(nBeforeDecimalMark - 3);
+
+ while(i > 0)
+ {
+ aBuf.insert(i, cTho);
+ i -= 3;
+ }
+ }
+ }
+
+ if (aBuf.isEmpty())
+ aBuf.append("0");
+
+ if(bNegative)
+ {
+ aBuf.insert(0, "-");
+ }
+
+ if(!bNoUnitChars)
+ aBuf.append(m_aUIUnitStr);
+
+ return aBuf.makeStringAndClear();
+}
+
+OUString SdrModel::GetAngleString(Degree100 nAngle)
+{
+ bool bNeg = nAngle < 0_deg100;
+
+ if(bNeg)
+ nAngle = -nAngle;
+
+ OUStringBuffer aBuf;
+ aBuf.append(static_cast<sal_Int32>(nAngle));
+
+ SvtSysLocale aSysLoc;
+ const LocaleDataWrapper& rLoc = aSysLoc.GetLocaleData();
+ sal_Int32 nCount = 2;
+
+ if(LocaleDataWrapper::isNumLeadingZero())
+ nCount++;
+
+ while(aBuf.getLength() < nCount)
+ aBuf.insert(0, '0');
+
+ aBuf.insert(aBuf.getLength()-2, rLoc.getNumDecimalSep()[0]);
+
+ if(bNeg)
+ aBuf.insert(0, '-');
+
+ aBuf.append(DEGREE_CHAR);
+
+ return aBuf.makeStringAndClear();
+}
+
+OUString SdrModel::GetPercentString(const Fraction& rVal)
+{
+ sal_Int32 nMul(rVal.GetNumerator());
+ sal_Int32 nDiv(rVal.GetDenominator());
+ bool bNeg {false};
+
+ if (nDiv < 0)
+ {
+ bNeg = !bNeg;
+ nDiv = -nDiv;
+ }
+
+ if (nMul < 0)
+ {
+ bNeg = !bNeg;
+ nMul = -nMul;
+ }
+
+ sal_Int32 nPct = ((nMul*100) + nDiv/2)/nDiv;
+
+ if (bNeg)
+ nPct = -nPct;
+
+ return OUString::number(nPct) + "%";
+}
+
+void SdrModel::SetChanged(bool bFlg)
+{
+ mbChanged = bFlg;
+}
+
+void SdrModel::RecalcPageNums(bool bMaster)
+{
+ if(bMaster)
+ {
+ sal_uInt16 nCount=sal_uInt16(maMasterPages.size());
+ sal_uInt16 i;
+ for (i=0; i<nCount; i++) {
+ SdrPage* pPg = maMasterPages[i].get();
+ pPg->SetPageNum(i);
+ }
+ m_bMPgNumsDirty=false;
+ }
+ else
+ {
+ sal_uInt16 nCount=sal_uInt16(maPages.size());
+ sal_uInt16 i;
+ for (i=0; i<nCount; i++) {
+ SdrPage* pPg = maPages[i].get();
+ pPg->SetPageNum(i);
+ }
+ m_bPagNumsDirty=false;
+ }
+}
+
+void SdrModel::InsertPage(SdrPage* pPage, sal_uInt16 nPos)
+{
+ sal_uInt16 nCount = GetPageCount();
+ if (nPos > nCount)
+ nPos = nCount;
+
+ maPages.insert(maPages.begin() + nPos, pPage);
+ PageListChanged();
+ pPage->SetInserted();
+ pPage->SetPageNum(nPos);
+
+ if (mbMakePageObjectsNamesUnique)
+ pPage->MakePageObjectsNamesUnique();
+
+ if (nPos<nCount) m_bPagNumsDirty=true;
+ SetChanged();
+ SdrHint aHint(SdrHintKind::PageOrderChange, pPage);
+ Broadcast(aHint);
+}
+
+void SdrModel::DeletePage(sal_uInt16 nPgNum)
+{
+ RemovePage(nPgNum);
+}
+
+rtl::Reference<SdrPage> SdrModel::RemovePage(sal_uInt16 nPgNum)
+{
+ rtl::Reference<SdrPage> pPg = maPages[nPgNum];
+ maPages.erase(maPages.begin()+nPgNum);
+ PageListChanged();
+ if (pPg) {
+ pPg->SetInserted(false);
+ }
+ m_bPagNumsDirty=true;
+ SetChanged();
+ SdrHint aHint(SdrHintKind::PageOrderChange, pPg.get());
+ Broadcast(aHint);
+ return pPg;
+}
+
+void SdrModel::MovePage(sal_uInt16 nPgNum, sal_uInt16 nNewPos)
+{
+ rtl::Reference<SdrPage> pPg = std::move(maPages[nPgNum]);
+ if (pPg) {
+ maPages.erase(maPages.begin()+nPgNum); // shortcut to avoid two broadcasts
+ PageListChanged();
+ pPg->SetInserted(false);
+ InsertPage(pPg.get(), nNewPos);
+ }
+ else
+ RemovePage(nPgNum);
+}
+
+void SdrModel::InsertMasterPage(SdrPage* pPage, sal_uInt16 nPos)
+{
+ sal_uInt16 nCount=GetMasterPageCount();
+ if (nPos>nCount) nPos=nCount;
+ maMasterPages.insert(maMasterPages.begin()+nPos,pPage);
+ MasterPageListChanged();
+ pPage->SetInserted();
+ pPage->SetPageNum(nPos);
+
+ if (nPos<nCount) {
+ m_bMPgNumsDirty=true;
+ }
+
+ SetChanged();
+ SdrHint aHint(SdrHintKind::PageOrderChange, pPage);
+ Broadcast(aHint);
+}
+
+void SdrModel::DeleteMasterPage(sal_uInt16 nPgNum)
+{
+ RemoveMasterPage(nPgNum);
+}
+
+rtl::Reference<SdrPage> SdrModel::RemoveMasterPage(sal_uInt16 nPgNum)
+{
+ rtl::Reference<SdrPage> pRetPg = std::move(maMasterPages[nPgNum]);
+ maMasterPages.erase(maMasterPages.begin()+nPgNum);
+ MasterPageListChanged();
+
+ if(pRetPg)
+ {
+ // Now delete the links from the normal drawing pages to the deleted master page.
+ sal_uInt16 nPageCnt(GetPageCount());
+
+ for(sal_uInt16 np(0); np < nPageCnt; np++)
+ {
+ GetPage(np)->TRG_ImpMasterPageRemoved(*pRetPg);
+ }
+
+ pRetPg->SetInserted(false);
+ }
+
+ m_bMPgNumsDirty=true;
+ SetChanged();
+ SdrHint aHint(SdrHintKind::PageOrderChange, pRetPg.get());
+ Broadcast(aHint);
+ return pRetPg;
+}
+
+void SdrModel::MoveMasterPage(sal_uInt16 nPgNum, sal_uInt16 nNewPos)
+{
+ rtl::Reference<SdrPage> pPg = std::move(maMasterPages[nPgNum]);
+ maMasterPages.erase(maMasterPages.begin()+nPgNum);
+ MasterPageListChanged();
+ if (pPg) {
+ pPg->SetInserted(false);
+ maMasterPages.insert(maMasterPages.begin()+nNewPos,pPg);
+ MasterPageListChanged();
+ }
+ m_bMPgNumsDirty=true;
+ SetChanged();
+ SdrHint aHint(SdrHintKind::PageOrderChange, pPg.get());
+ Broadcast(aHint);
+}
+
+
+void SdrModel::CopyPages(sal_uInt16 nFirstPageNum, sal_uInt16 nLastPageNum,
+ sal_uInt16 nDestPos,
+ bool bUndo, bool bMoveNoCopy)
+{
+ if( bUndo && !IsUndoEnabled() )
+ bUndo = false;
+
+ if( bUndo )
+ BegUndo(SvxResId(STR_UndoMergeModel));
+
+ sal_uInt16 nPageCnt=GetPageCount();
+ sal_uInt16 nMaxPage=nPageCnt;
+
+ if (nMaxPage!=0)
+ nMaxPage--;
+ if (nFirstPageNum>nMaxPage)
+ nFirstPageNum=nMaxPage;
+ if (nLastPageNum>nMaxPage)
+ nLastPageNum =nMaxPage;
+ bool bReverse=nLastPageNum<nFirstPageNum;
+ if (nDestPos>nPageCnt)
+ nDestPos=nPageCnt;
+
+ // at first, save the pointers of the affected pages in an array
+ sal_uInt16 nPageNum=nFirstPageNum;
+ sal_uInt16 nCopyCnt=((!bReverse)?(nLastPageNum-nFirstPageNum):(nFirstPageNum-nLastPageNum))+1;
+ std::unique_ptr<SdrPage*[]> pPagePtrs(new SdrPage*[nCopyCnt]);
+ sal_uInt16 nCopyNum;
+ for(nCopyNum=0; nCopyNum<nCopyCnt; nCopyNum++)
+ {
+ pPagePtrs[nCopyNum]=GetPage(nPageNum);
+ if (bReverse)
+ nPageNum--;
+ else
+ nPageNum++;
+ }
+
+ // now copy the pages
+ sal_uInt16 nDestNum=nDestPos;
+ for (nCopyNum=0; nCopyNum<nCopyCnt; nCopyNum++)
+ {
+ rtl::Reference<SdrPage> pPg = pPagePtrs[nCopyNum];
+ sal_uInt16 nPageNum2=pPg->GetPageNum();
+ if (!bMoveNoCopy)
+ {
+ const SdrPage* pPg1=GetPage(nPageNum2);
+
+ // Clone to local model
+ pPg = pPg1->CloneSdrPage(*this);
+
+ InsertPage(pPg.get(), nDestNum);
+ if (bUndo)
+ AddUndo(GetSdrUndoFactory().CreateUndoCopyPage(*pPg));
+ nDestNum++;
+ }
+ else
+ {
+ // TODO: Move is untested!
+ if (nDestNum>nPageNum2)
+ nDestNum--;
+
+ if(bUndo)
+ AddUndo(GetSdrUndoFactory().CreateUndoSetPageNum(*GetPage(nPageNum2),nPageNum2,nDestNum));
+
+ pPg=RemovePage(nPageNum2);
+ InsertPage(pPg.get(), nDestNum);
+ nDestNum++;
+ }
+
+ if(bReverse)
+ nPageNum2--;
+ else
+ nPageNum2++;
+ }
+
+ pPagePtrs.reset();
+ if(bUndo)
+ EndUndo();
+}
+
+void SdrModel::Merge(SdrModel& rSourceModel,
+ sal_uInt16 nFirstPageNum, sal_uInt16 nLastPageNum,
+ sal_uInt16 nDestPos,
+ bool bMergeMasterPages, bool bAllMasterPages,
+ bool bUndo, bool bTreadSourceAsConst)
+{
+ if (&rSourceModel==this)
+ {
+ CopyPages(nFirstPageNum,nLastPageNum,nDestPos,bUndo,!bTreadSourceAsConst);
+ return;
+ }
+
+ if( bUndo && !IsUndoEnabled() )
+ bUndo = false;
+
+ if (bUndo)
+ BegUndo(SvxResId(STR_UndoMergeModel));
+
+ sal_uInt16 nSrcPageCnt=rSourceModel.GetPageCount();
+ sal_uInt16 nSrcMasterPageCnt=rSourceModel.GetMasterPageCount();
+ sal_uInt16 nDstMasterPageCnt=GetMasterPageCount();
+ bool bInsPages=(nFirstPageNum<nSrcPageCnt || nLastPageNum<nSrcPageCnt);
+ sal_uInt16 nMaxSrcPage=nSrcPageCnt; if (nMaxSrcPage!=0) nMaxSrcPage--;
+ if (nFirstPageNum>nMaxSrcPage) nFirstPageNum=nMaxSrcPage;
+ if (nLastPageNum>nMaxSrcPage) nLastPageNum =nMaxSrcPage;
+ bool bReverse=nLastPageNum<nFirstPageNum;
+
+ std::unique_ptr<sal_uInt16[]> pMasterMap;
+ std::unique_ptr<bool[]> pMasterNeed;
+ sal_uInt16 nMasterNeed=0;
+ if (bMergeMasterPages && nSrcMasterPageCnt!=0) {
+ // determine which MasterPages from rSrcModel we need
+ pMasterMap.reset(new sal_uInt16[nSrcMasterPageCnt]);
+ pMasterNeed.reset(new bool[nSrcMasterPageCnt]);
+ memset(pMasterMap.get(),0xFF,nSrcMasterPageCnt*sizeof(sal_uInt16));
+ if (bAllMasterPages) {
+ memset(pMasterNeed.get(), true, nSrcMasterPageCnt * sizeof(bool));
+ } else {
+ memset(pMasterNeed.get(), false, nSrcMasterPageCnt * sizeof(bool));
+ sal_uInt16 nStart= bReverse ? nLastPageNum : nFirstPageNum;
+ sal_uInt16 nEnd= bReverse ? nFirstPageNum : nLastPageNum;
+ for (sal_uInt16 i=nStart; i<=nEnd; i++) {
+ const SdrPage* pPg=rSourceModel.GetPage(i);
+ if(pPg->TRG_HasMasterPage())
+ {
+ SdrPage& rMasterPage = pPg->TRG_GetMasterPage();
+ sal_uInt16 nMPgNum(rMasterPage.GetPageNum());
+
+ if(nMPgNum < nSrcMasterPageCnt)
+ {
+ pMasterNeed[nMPgNum] = true;
+ }
+ }
+ }
+ }
+ // now determine the Mapping of the MasterPages
+ sal_uInt16 nCurrentMaPagNum=nDstMasterPageCnt;
+ for (sal_uInt16 i=0; i<nSrcMasterPageCnt; i++) {
+ if (pMasterNeed[i]) {
+ pMasterMap[i]=nCurrentMaPagNum;
+ nCurrentMaPagNum++;
+ nMasterNeed++;
+ }
+ }
+ }
+
+ // get the MasterPages
+ if (pMasterMap && pMasterNeed && nMasterNeed!=0) {
+ for (sal_uInt16 i=nSrcMasterPageCnt; i>0;) {
+ i--;
+ if (pMasterNeed[i])
+ {
+ // Always Clone to new model
+ const SdrPage* pPg1(rSourceModel.GetMasterPage(i));
+ rtl::Reference<SdrPage> pPg = pPg1->CloneSdrPage(*this);
+
+ if(!bTreadSourceAsConst)
+ {
+ // if requested, delete original/modify original model
+ rSourceModel.RemoveMasterPage(i);
+ }
+
+ if (pPg!=nullptr) {
+ // Now append all of them to the end of the DstModel.
+ // Don't use InsertMasterPage(), because everything is
+ // inconsistent until all are in.
+ maMasterPages.insert(maMasterPages.begin()+nDstMasterPageCnt, pPg);
+ MasterPageListChanged();
+ pPg->SetInserted();
+ m_bMPgNumsDirty=true;
+ if (bUndo) AddUndo(GetSdrUndoFactory().CreateUndoNewPage(*pPg));
+ } else {
+ OSL_FAIL("SdrModel::Merge(): MasterPage not found in SourceModel.");
+ }
+ }
+ }
+ }
+
+ // get the drawing pages
+ if (bInsPages) {
+ sal_uInt16 nSourcePos=nFirstPageNum;
+ sal_uInt16 nMergeCount=sal_uInt16(std::abs(static_cast<tools::Long>(static_cast<tools::Long>(nFirstPageNum)-nLastPageNum))+1);
+ if (nDestPos>GetPageCount()) nDestPos=GetPageCount();
+ while (nMergeCount>0)
+ {
+ // Always Clone to new model
+ const SdrPage* pPg1(rSourceModel.GetPage(nSourcePos));
+ rtl::Reference<SdrPage> pPg = pPg1->CloneSdrPage(*this);
+
+ if(!bTreadSourceAsConst)
+ {
+ // if requested, delete original/modify original model
+ rSourceModel.RemovePage(nSourcePos);
+ }
+
+ if (pPg!=nullptr) {
+ InsertPage(pPg.get(),nDestPos);
+ if (bUndo) AddUndo(GetSdrUndoFactory().CreateUndoNewPage(*pPg));
+
+ if(pPg->TRG_HasMasterPage())
+ {
+ SdrPage& rMasterPage = pPg->TRG_GetMasterPage();
+ sal_uInt16 nMaPgNum(rMasterPage.GetPageNum());
+
+ if (bMergeMasterPages)
+ {
+ sal_uInt16 nNewNum(0xFFFF);
+
+ if(pMasterMap)
+ {
+ nNewNum = pMasterMap[nMaPgNum];
+ }
+
+ if(nNewNum != 0xFFFF)
+ {
+ // tdf#90357 here pPg and the to-be-set new masterpage are parts of the new model
+ // already, but the currently set masterpage is part of the old model. Remove master
+ // page from already cloned page to prevent creating wrong undo action that can
+ // eventually crash the app.
+ // Do *not* remove it directly after cloning - the old masterpage is still needed
+ // later to find the new to-be-set masterpage.
+ pPg->TRG_ClearMasterPage();
+
+ if(bUndo)
+ {
+ AddUndo(GetSdrUndoFactory().CreateUndoPageChangeMasterPage(*pPg));
+ }
+
+ pPg->TRG_SetMasterPage(*GetMasterPage(nNewNum));
+ }
+ DBG_ASSERT(nNewNum!=0xFFFF,"SdrModel::Merge(): Something is crooked with the mapping of the MasterPages.");
+ } else {
+ if (nMaPgNum>=nDstMasterPageCnt) {
+ // This is outside of the original area of the MasterPage of the DstModel.
+ pPg->TRG_ClearMasterPage();
+ }
+ }
+ }
+
+ } else {
+ OSL_FAIL("SdrModel::Merge(): Drawing page not found in SourceModel.");
+ }
+ nDestPos++;
+ if (bReverse) nSourcePos--;
+ else if (bTreadSourceAsConst) nSourcePos++;
+ nMergeCount--;
+ }
+ }
+
+ pMasterMap.reset();
+ pMasterNeed.reset();
+
+ m_bMPgNumsDirty=true;
+ m_bPagNumsDirty=true;
+
+ SetChanged();
+ // TODO: Missing: merging and mapping of layers
+ // at the objects as well as at the MasterPageDescriptors
+ if (bUndo) EndUndo();
+}
+
+void SdrModel::SetStarDrawPreviewMode(bool bPreview)
+{
+ if (!bPreview && m_bStarDrawPreviewMode && GetPageCount())
+ {
+ // Resetting is not allowed, because the Model might not be loaded completely
+ SAL_WARN("svx", "SdrModel::SetStarDrawPreviewMode(): Resetting not allowed, because Model might not be complete.");
+ }
+ else
+ {
+ m_bStarDrawPreviewMode = bPreview;
+ }
+}
+
+void SdrModel::SetTheme(std::unique_ptr<svx::Theme> pTheme) { mpImpl->mpTheme = std::move(pTheme); }
+
+svx::Theme* SdrModel::GetTheme() { return mpImpl->mpTheme.get(); }
+
+uno::Reference< uno::XInterface > const & SdrModel::getUnoModel()
+{
+ if( !mxUnoModel.is() )
+ mxUnoModel = createUnoModel();
+
+ return mxUnoModel;
+}
+
+void SdrModel::setUnoModel( const css::uno::Reference< css::uno::XInterface >& xModel )
+{
+ mxUnoModel = xModel;
+}
+
+void SdrModel::adaptSizeAndBorderForAllPages(
+ const Size& /*rNewSize*/,
+ tools::Long /*nLeft*/,
+ tools::Long /*nRight*/,
+ tools::Long /*nUpper*/,
+ tools::Long /*nLower*/)
+{
+ // base implementation does currently nothing. It may be added if needed,
+ // but we are on SdrModel level here, thus probably have not enough information
+ // to do this for higher-level (derived) Models (e.g. Draw/Impress)
+}
+
+uno::Reference< uno::XInterface > SdrModel::createUnoModel()
+{
+ OSL_FAIL( "SdrModel::createUnoModel() - base implementation should not be called!" );
+ css::uno::Reference< css::uno::XInterface > xInt;
+ return xInt;
+}
+
+void SdrModel::setLock( bool bLock )
+{
+ if( mbModelLocked != bLock )
+ {
+ // #i120437# need to set first, else ImpReformatAllEdgeObjects will do nothing
+ mbModelLocked = bLock;
+
+ if( !bLock )
+ {
+ ImpReformatAllEdgeObjects();
+ }
+ }
+}
+
+
+void SdrModel::MigrateItemSet( const SfxItemSet* pSourceSet, SfxItemSet* pDestSet, SdrModel* pNewModelel )
+{
+ assert(pNewModelel != nullptr);
+ if( !(pSourceSet && pDestSet && (pSourceSet != pDestSet )) )
+ return;
+
+ SfxWhichIter aWhichIter(*pSourceSet);
+ sal_uInt16 nWhich(aWhichIter.FirstWhich());
+ const SfxPoolItem *pPoolItem;
+
+ while(nWhich)
+ {
+ if(SfxItemState::SET == aWhichIter.GetItemState(false, &pPoolItem))
+ {
+ std::unique_ptr<SfxPoolItem> pResultItem;
+
+ switch( nWhich )
+ {
+ case XATTR_FILLBITMAP:
+ pResultItem = static_cast<const XFillBitmapItem*>(pPoolItem)->checkForUniqueItem( pNewModelel );
+ break;
+ case XATTR_LINEDASH:
+ pResultItem = static_cast<const XLineDashItem*>(pPoolItem)->checkForUniqueItem( pNewModelel );
+ break;
+ case XATTR_LINESTART:
+ pResultItem = static_cast<const XLineStartItem*>(pPoolItem)->checkForUniqueItem( pNewModelel );
+ break;
+ case XATTR_LINEEND:
+ pResultItem = static_cast<const XLineEndItem*>(pPoolItem)->checkForUniqueItem( pNewModelel );
+ break;
+ case XATTR_FILLGRADIENT:
+ pResultItem = static_cast<const XFillGradientItem*>(pPoolItem)->checkForUniqueItem( pNewModelel );
+ break;
+ case XATTR_FILLFLOATTRANSPARENCE:
+ // allow all kinds of XFillFloatTransparenceItem to be set
+ pResultItem = static_cast<const XFillFloatTransparenceItem*>(pPoolItem)->checkForUniqueItem( pNewModelel );
+ break;
+ case XATTR_FILLHATCH:
+ pResultItem = static_cast<const XFillHatchItem*>(pPoolItem)->checkForUniqueItem( pNewModelel );
+ break;
+ }
+
+ // set item
+ if( pResultItem )
+ pDestSet->Put(std::move(pResultItem));
+ else
+ pDestSet->Put(*pPoolItem);
+ }
+ nWhich = aWhichIter.NextWhich();
+ }
+}
+
+
+void SdrModel::SetForbiddenCharsTable(const std::shared_ptr<SvxForbiddenCharactersTable>& xForbiddenChars)
+{
+ mpForbiddenCharactersTable = xForbiddenChars;
+
+ ImpSetOutlinerDefaults( m_pDrawOutliner.get() );
+ ImpSetOutlinerDefaults( m_pHitTestOutliner.get() );
+}
+
+
+void SdrModel::SetCharCompressType( CharCompressType nType )
+{
+ if( nType != mnCharCompressType )
+ {
+ mnCharCompressType = nType;
+ ImpSetOutlinerDefaults( m_pDrawOutliner.get() );
+ ImpSetOutlinerDefaults( m_pHitTestOutliner.get() );
+ }
+}
+
+void SdrModel::SetKernAsianPunctuation( bool bEnabled )
+{
+ if( mbKernAsianPunctuation != bEnabled )
+ {
+ mbKernAsianPunctuation = bEnabled;
+ ImpSetOutlinerDefaults( m_pDrawOutliner.get() );
+ ImpSetOutlinerDefaults( m_pHitTestOutliner.get() );
+ }
+}
+
+void SdrModel::SetAddExtLeading( bool bEnabled )
+{
+ if( mbAddExtLeading != bEnabled )
+ {
+ mbAddExtLeading = bEnabled;
+ ImpSetOutlinerDefaults( m_pDrawOutliner.get() );
+ ImpSetOutlinerDefaults( m_pHitTestOutliner.get() );
+ }
+}
+
+void SdrModel::SetAnchoredTextOverflowLegacy(bool bEnabled)
+{
+ mpImpl->mbAnchoredTextOverflowLegacy = bEnabled;
+}
+
+bool SdrModel::IsAnchoredTextOverflowLegacy() const
+{
+ return mpImpl->mbAnchoredTextOverflowLegacy;
+}
+
+void SdrModel::ReformatAllTextObjects()
+{
+ ImpReformatAllTextObjects();
+}
+
+std::unique_ptr<SdrOutliner> SdrModel::createOutliner( OutlinerMode nOutlinerMode )
+{
+ if( !mpOutlinerCache )
+ mpOutlinerCache.reset(new SdrOutlinerCache(this));
+
+ return mpOutlinerCache->createOutliner( nOutlinerMode );
+}
+
+std::vector<SdrOutliner*> SdrModel::GetActiveOutliners() const
+{
+ std::vector< SdrOutliner* > aRet(mpOutlinerCache ? mpOutlinerCache->GetActiveOutliners() : std::vector< SdrOutliner* >());
+ aRet.push_back(m_pDrawOutliner.get());
+ aRet.push_back(m_pHitTestOutliner.get());
+
+ return aRet;
+}
+
+void SdrModel::disposeOutliner( std::unique_ptr<SdrOutliner> pOutliner )
+{
+ if( mpOutlinerCache )
+ mpOutlinerCache->disposeOutliner( std::move(pOutliner) );
+}
+
+SvxNumType SdrModel::GetPageNumType() const
+{
+ return SVX_NUM_ARABIC;
+}
+
+void SdrModel::ReadUserDataSequenceValue(const css::beans::PropertyValue* pValue)
+{
+ if (pValue->Name == "AnchoredTextOverflowLegacy")
+ {
+ bool bBool = false;
+ if (pValue->Value >>= bBool)
+ {
+ mpImpl->mbAnchoredTextOverflowLegacy = bBool;
+ }
+ }
+}
+
+template <typename T>
+static void addPair(std::vector< std::pair< OUString, Any > >& aUserData, const OUString& name, const T val)
+{
+ aUserData.push_back(std::pair< OUString, Any >(name, css::uno::Any(val)));
+}
+
+void SdrModel::WriteUserDataSequence(css::uno::Sequence < css::beans::PropertyValue >& rValues)
+{
+ std::vector< std::pair< OUString, Any > > aUserData;
+ addPair(aUserData, "AnchoredTextOverflowLegacy", IsAnchoredTextOverflowLegacy());
+
+ const sal_Int32 nOldLength = rValues.getLength();
+ rValues.realloc(nOldLength + aUserData.size());
+
+ css::beans::PropertyValue* pValue = &(rValues.getArray()[nOldLength]);
+
+ for (const auto &aIter : aUserData)
+ {
+ pValue->Name = aIter.first;
+ pValue->Value = aIter.second;
+ ++pValue;
+ }
+}
+
+const SdrPage* SdrModel::GetPage(sal_uInt16 nPgNum) const
+{
+ return nPgNum < maPages.size() ? maPages[nPgNum].get() : nullptr;
+}
+
+SdrPage* SdrModel::GetPage(sal_uInt16 nPgNum)
+{
+ return nPgNum < maPages.size() ? maPages[nPgNum].get() : nullptr;
+}
+
+sal_uInt16 SdrModel::GetPageCount() const
+{
+ return sal_uInt16(maPages.size());
+}
+
+void SdrModel::PageListChanged()
+{
+}
+
+TextChain *SdrModel::GetTextChain() const
+{
+ return m_pTextChain.get();
+}
+
+const SdrPage* SdrModel::GetMasterPage(sal_uInt16 nPgNum) const
+{
+ DBG_ASSERT(nPgNum < maMasterPages.size(), "SdrModel::GetMasterPage: Access out of range (!)");
+ return maMasterPages[nPgNum].get();
+}
+
+SdrPage* SdrModel::GetMasterPage(sal_uInt16 nPgNum)
+{
+ DBG_ASSERT(nPgNum < maMasterPages.size(), "SdrModel::GetMasterPage: Access out of range (!)");
+ return maMasterPages[nPgNum].get();
+}
+
+sal_uInt16 SdrModel::GetMasterPageCount() const
+{
+ return sal_uInt16(maMasterPages.size());
+}
+
+void SdrModel::MasterPageListChanged()
+{
+}
+
+void SdrModel::SetSdrUndoManager( SfxUndoManager* pUndoManager )
+{
+ mpImpl->mpUndoManager = pUndoManager;
+}
+
+SfxUndoManager* SdrModel::GetSdrUndoManager() const
+{
+ return mpImpl->mpUndoManager;
+}
+
+SdrUndoFactory& SdrModel::GetSdrUndoFactory() const
+{
+ if( !mpImpl->mpUndoFactory )
+ mpImpl->mpUndoFactory = new SdrUndoFactory;
+ return *mpImpl->mpUndoFactory;
+}
+
+void SdrModel::SetSdrUndoFactory( SdrUndoFactory* pUndoFactory )
+{
+ if( pUndoFactory && (pUndoFactory != mpImpl->mpUndoFactory) )
+ {
+ delete mpImpl->mpUndoFactory;
+ mpImpl->mpUndoFactory = pUndoFactory;
+ }
+}
+
+void SdrModel::dumpAsXml(xmlTextWriterPtr pWriter) const
+{
+ (void)xmlTextWriterStartElement(pWriter, BAD_CAST("SdrModel"));
+ (void)xmlTextWriterWriteFormatAttribute(pWriter, BAD_CAST("ptr"), "%p", this);
+
+ (void)xmlTextWriterStartElement(pWriter, BAD_CAST("maMasterPages"));
+ for (size_t i = 0; i < maMasterPages.size(); ++i)
+ {
+ if (const SdrPage* pPage = maMasterPages[i].get())
+ {
+ pPage->dumpAsXml(pWriter);
+ }
+ }
+ (void)xmlTextWriterEndElement(pWriter);
+
+ (void)xmlTextWriterStartElement(pWriter, BAD_CAST("maPages"));
+ for (size_t i = 0; i < maPages.size(); ++i)
+ {
+ if (const SdrPage* pPage = maPages[i].get())
+ {
+ pPage->dumpAsXml(pWriter);
+ }
+ }
+ (void)xmlTextWriterEndElement(pWriter);
+
+ if (mpImpl->mpTheme)
+ {
+ mpImpl->mpTheme->dumpAsXml(pWriter);
+ }
+
+ (void)xmlTextWriterEndElement(pWriter);
+}
+
+const css::uno::Sequence< sal_Int8 >& SdrModel::getUnoTunnelId()
+{
+ static const comphelper::UnoIdInit theSdrModelUnoTunnelImplementationId;
+ return theSdrModelUnoTunnelImplementationId.getSeq();
+}
+
+
+SdrHint::SdrHint(SdrHintKind eNewHint)
+: SfxHint(SfxHintId::ThisIsAnSdrHint),
+ meHint(eNewHint),
+ mpObj(nullptr),
+ mpPage(nullptr)
+{
+}
+
+SdrHint::SdrHint(SdrHintKind eNewHint, const SdrObject& rNewObj)
+: SfxHint(SfxHintId::ThisIsAnSdrHint),
+ meHint(eNewHint),
+ mpObj(&rNewObj),
+ mpPage(rNewObj.getSdrPageFromSdrObject())
+{
+}
+
+SdrHint::SdrHint(SdrHintKind eNewHint, const SdrPage* pPage)
+: SfxHint(SfxHintId::ThisIsAnSdrHint),
+ meHint(eNewHint),
+ mpObj(nullptr),
+ mpPage(pPage)
+{
+}
+
+SdrHint::SdrHint(SdrHintKind eNewHint, const SdrObject& rNewObj, const SdrPage* pPage)
+: SfxHint(SfxHintId::ThisIsAnSdrHint),
+ meHint(eNewHint),
+ mpObj(&rNewObj),
+ mpPage(pPage)
+{
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/svdraw/svdmrkv.cxx b/svx/source/svdraw/svdmrkv.cxx
new file mode 100644
index 000000000..c298e1925
--- /dev/null
+++ b/svx/source/svdraw/svdmrkv.cxx
@@ -0,0 +1,2733 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * 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/svdmrkv.hxx>
+#include <svx/svdview.hxx>
+#include <svx/svdpagv.hxx>
+#include <svx/svdpage.hxx>
+#include <svx/svdotable.hxx>
+
+#include <osl/diagnose.h>
+#include <osl/thread.h>
+#include <rtl/strbuf.hxx>
+#include <svx/svdoole2.hxx>
+#include <svx/xgrad.hxx>
+#include <svx/xfillit0.hxx>
+#include <svx/xflgrit.hxx>
+#include "gradtrns.hxx"
+#include <svx/xflftrit.hxx>
+#include <svx/dialmgr.hxx>
+#include <svx/strings.hrc>
+#include <svx/svdundo.hxx>
+#include <svx/svdopath.hxx>
+#include <svx/scene3d.hxx>
+#include <svx/svdovirt.hxx>
+#include <sdr/overlay/overlayrollingrectangle.hxx>
+#include <svx/sdr/contact/objectcontact.hxx>
+#include <svx/sdr/overlay/overlaymanager.hxx>
+#include <svx/sdr/overlay/overlayselection.hxx>
+#include <svx/sdr/contact/viewcontact.hxx>
+#include <svx/sdr/contact/viewobjectcontact.hxx>
+#include <svx/sdrpaintwindow.hxx>
+#include <svx/sdrpagewindow.hxx>
+#include <svx/sdrhittesthelper.hxx>
+#include <vcl/uitest/logger.hxx>
+#include <vcl/uitest/eventdescription.hxx>
+#include <vcl/window.hxx>
+#include <o3tl/string_view.hxx>
+
+#include <LibreOfficeKit/LibreOfficeKitEnums.h>
+#include <comphelper/lok.hxx>
+#include <sfx2/lokhelper.hxx>
+#include <sfx2/lokcomponenthelpers.hxx>
+#include <sfx2/viewsh.hxx>
+#include <svtools/optionsdrawinglayer.hxx>
+
+#include <array>
+
+#include <com/sun/star/frame/XController.hpp>
+#include <com/sun/star/view/XSelectionSupplier.hpp>
+
+#include <boost/property_tree/json_parser.hpp>
+
+using namespace com::sun::star;
+
+// Migrate Marking of Objects, Points and GluePoints
+
+class ImplMarkingOverlay
+{
+ // The OverlayObjects
+ sdr::overlay::OverlayObjectList maObjects;
+
+ // The remembered second position in logical coordinates
+ basegfx::B2DPoint maSecondPosition;
+
+ // A flag to remember if the action is for unmarking.
+ bool mbUnmarking : 1;
+
+public:
+ ImplMarkingOverlay(const SdrPaintView& rView, const basegfx::B2DPoint& rStartPos, bool bUnmarking);
+
+ // The OverlayObjects are cleared using the destructor of OverlayObjectList.
+ // That destructor calls clear() at the list which removes all objects from the
+ // OverlayManager and deletes them.
+
+ void SetSecondPosition(const basegfx::B2DPoint& rNewPosition);
+ bool IsUnmarking() const { return mbUnmarking; }
+};
+
+ImplMarkingOverlay::ImplMarkingOverlay(const SdrPaintView& rView, const basegfx::B2DPoint& rStartPos, bool bUnmarking)
+: maSecondPosition(rStartPos),
+ mbUnmarking(bUnmarking)
+{
+ if (comphelper::LibreOfficeKit::isActive())
+ return; // We do client-side object manipulation with the Kit API
+
+ for(sal_uInt32 a(0); a < rView.PaintWindowCount(); a++)
+ {
+ SdrPaintWindow* pCandidate = rView.GetPaintWindow(a);
+ const rtl::Reference< sdr::overlay::OverlayManager >& xTargetOverlay = pCandidate->GetOverlayManager();
+
+ if (xTargetOverlay.is())
+ {
+ std::unique_ptr<sdr::overlay::OverlayRollingRectangleStriped> pNew(new sdr::overlay::OverlayRollingRectangleStriped(
+ rStartPos, rStartPos, false));
+ xTargetOverlay->add(*pNew);
+ maObjects.append(std::move(pNew));
+ }
+ }
+}
+
+void ImplMarkingOverlay::SetSecondPosition(const basegfx::B2DPoint& rNewPosition)
+{
+ if(rNewPosition != maSecondPosition)
+ {
+ // apply to OverlayObjects
+ for(sal_uInt32 a(0); a < maObjects.count(); a++)
+ {
+ sdr::overlay::OverlayRollingRectangleStriped& rCandidate = static_cast< sdr::overlay::OverlayRollingRectangleStriped&>(maObjects.getOverlayObject(a));
+ rCandidate.setSecondPosition(rNewPosition);
+ }
+
+ // remember new position
+ maSecondPosition = rNewPosition;
+ }
+}
+
+class MarkingSubSelectionOverlay
+{
+ sdr::overlay::OverlayObjectList maObjects;
+
+public:
+ MarkingSubSelectionOverlay(const SdrPaintView& rView, std::vector<basegfx::B2DRectangle> const & rSelections)
+ {
+ if (comphelper::LibreOfficeKit::isActive())
+ return; // We do client-side object manipulation with the Kit API
+
+ for (sal_uInt32 a(0); a < rView.PaintWindowCount(); a++)
+ {
+ SdrPaintWindow* pCandidate = rView.GetPaintWindow(a);
+ const rtl::Reference<sdr::overlay::OverlayManager>& xTargetOverlay = pCandidate->GetOverlayManager();
+
+ if (xTargetOverlay.is())
+ {
+ const Color aHighlightColor = SvtOptionsDrawinglayer::getHilightColor();
+
+ std::unique_ptr<sdr::overlay::OverlaySelection> pNew =
+ std::make_unique<sdr::overlay::OverlaySelection>(
+ sdr::overlay::OverlayType::Transparent,
+ aHighlightColor, std::vector(rSelections), false);
+
+ xTargetOverlay->add(*pNew);
+ maObjects.append(std::move(pNew));
+ }
+ }
+ }
+};
+
+SdrMarkView::SdrMarkView(SdrModel& rSdrModel, OutputDevice* pOut)
+ : SdrSnapView(rSdrModel, pOut)
+ , mpMarkedObj(nullptr)
+ , mpMarkedPV(nullptr)
+ , maHdlList(this)
+ , meDragMode(SdrDragMode::Move)
+ , meEditMode(SdrViewEditMode::Edit)
+ , meEditMode0(SdrViewEditMode::Edit)
+ , mbDesignMode(false)
+ , mbForceFrameHandles(false)
+ , mbPlusHdlAlways(false)
+ , mbInsPolyPoint(false)
+ , mbMarkedObjRectDirty(false)
+ , mbMrkPntDirty(false)
+ , mbMarkedPointsRectsDirty(false)
+ , mbMarkHandlesHidden(false)
+ , mbNegativeX(false)
+{
+
+ BrkMarkObj();
+ BrkMarkPoints();
+ BrkMarkGluePoints();
+
+ StartListening(rSdrModel);
+}
+
+SdrMarkView::~SdrMarkView()
+{
+ // Migrate selections
+ BrkMarkObj();
+ BrkMarkPoints();
+ BrkMarkGluePoints();
+}
+
+void SdrMarkView::Notify(SfxBroadcaster& rBC, const SfxHint& rHint)
+{
+ if (rHint.GetId() == SfxHintId::ThisIsAnSdrHint)
+ {
+ const SdrHint* pSdrHint = static_cast<const SdrHint*>(&rHint);
+ SdrHintKind eKind=pSdrHint->GetKind();
+ if (eKind==SdrHintKind::ObjectChange || eKind==SdrHintKind::ObjectInserted || eKind==SdrHintKind::ObjectRemoved)
+ {
+ mbMarkedObjRectDirty=true;
+ mbMarkedPointsRectsDirty=true;
+ }
+ }
+ SdrSnapView::Notify(rBC,rHint);
+}
+
+void SdrMarkView::ModelHasChanged()
+{
+ SdrPaintView::ModelHasChanged();
+ GetMarkedObjectListWriteAccess().SetNameDirty();
+ mbMarkedObjRectDirty=true;
+ mbMarkedPointsRectsDirty=true;
+ // Example: Obj is selected and maMarkedObjectList is sorted.
+ // In another View 2, the ObjOrder is changed (e. g. MovToTop())
+ // Then we need to re-sort MarkList.
+ GetMarkedObjectListWriteAccess().SetUnsorted();
+ SortMarkedObjects();
+ mbMrkPntDirty=true;
+ UndirtyMrkPnt();
+ SdrView* pV=static_cast<SdrView*>(this);
+ if (pV!=nullptr && !pV->IsDragObj() && !pV->IsInsObjPoint()) {
+ AdjustMarkHdl();
+ }
+
+ if (comphelper::LibreOfficeKit::isActive())
+ modelHasChangedLOKit();
+}
+
+void SdrMarkView::modelHasChangedLOKit()
+{
+ if (GetMarkedObjectCount() <= 0)
+ return;
+
+ //TODO: Is MarkedObjRect valid at this point?
+ tools::Rectangle aSelection(GetMarkedObjRect());
+ tools::Rectangle* pResultSelection;
+ if (aSelection.IsEmpty())
+ pResultSelection = nullptr;
+ else
+ {
+ sal_uInt32 nTotalPaintWindows = this->PaintWindowCount();
+ if (nTotalPaintWindows == 1)
+ {
+ const OutputDevice* pOut = this->GetFirstOutputDevice();
+ const vcl::Window* pWin = pOut ? pOut->GetOwnerWindow() : nullptr;
+ if (pWin && pWin->IsChart())
+ {
+ const vcl::Window* pViewShellWindow = GetSfxViewShell()->GetEditWindowForActiveOLEObj();
+ if (pViewShellWindow && pViewShellWindow->IsAncestorOf(*pWin))
+ {
+ Point aOffsetPx = pWin->GetOffsetPixelFrom(*pViewShellWindow);
+ Point aLogicOffset = pWin->PixelToLogic(aOffsetPx);
+ aSelection.Move(aLogicOffset.getX(), aLogicOffset.getY());
+ }
+ }
+ }
+
+ // In case the map mode is in 100th MM, then need to convert the coordinates over to twips for LOK.
+ if (mpMarkedPV)
+ {
+ if (OutputDevice* pOutputDevice = mpMarkedPV->GetView().GetFirstOutputDevice())
+ {
+ if (pOutputDevice->GetMapMode().GetMapUnit() == MapUnit::Map100thMM)
+ aSelection = o3tl::convert(aSelection, o3tl::Length::mm100, o3tl::Length::twip);
+ }
+ }
+
+ pResultSelection = &aSelection;
+
+ if (mbNegativeX)
+ {
+ // Convert to positive X doc-coordinates
+ tools::Long nTmp = aSelection.Left();
+ aSelection.SetLeft(-aSelection.Right());
+ aSelection.SetRight(-nTmp);
+ }
+ }
+
+ if (SfxViewShell* pViewShell = GetSfxViewShell())
+ SfxLokHelper::notifyInvalidation(pViewShell, pResultSelection);
+}
+
+bool SdrMarkView::IsAction() const
+{
+ return SdrSnapView::IsAction() || IsMarkObj() || IsMarkPoints() || IsMarkGluePoints();
+}
+
+void SdrMarkView::MovAction(const Point& rPnt)
+{
+ SdrSnapView::MovAction(rPnt);
+
+ if(IsMarkObj())
+ {
+ MovMarkObj(rPnt);
+ }
+ else if(IsMarkPoints())
+ {
+ MovMarkPoints(rPnt);
+ }
+ else if(IsMarkGluePoints())
+ {
+ MovMarkGluePoints(rPnt);
+ }
+}
+
+void SdrMarkView::EndAction()
+{
+ if(IsMarkObj())
+ {
+ EndMarkObj();
+ }
+ else if(IsMarkPoints())
+ {
+ EndMarkPoints();
+ }
+ else if(IsMarkGluePoints())
+ {
+ EndMarkGluePoints();
+ }
+
+ SdrSnapView::EndAction();
+}
+
+void SdrMarkView::BckAction()
+{
+ SdrSnapView::BckAction();
+ BrkMarkObj();
+ BrkMarkPoints();
+ BrkMarkGluePoints();
+}
+
+void SdrMarkView::BrkAction()
+{
+ SdrSnapView::BrkAction();
+ BrkMarkObj();
+ BrkMarkPoints();
+ BrkMarkGluePoints();
+}
+
+void SdrMarkView::TakeActionRect(tools::Rectangle& rRect) const
+{
+ if(IsMarkObj() || IsMarkPoints() || IsMarkGluePoints())
+ {
+ rRect = tools::Rectangle(maDragStat.GetStart(), maDragStat.GetNow());
+ }
+ else
+ {
+ SdrSnapView::TakeActionRect(rRect);
+ }
+}
+
+
+void SdrMarkView::ClearPageView()
+{
+ UnmarkAllObj();
+ SdrSnapView::ClearPageView();
+}
+
+void SdrMarkView::HideSdrPage()
+{
+ bool bMrkChg(false);
+
+ SdrPageView* pPageView = GetSdrPageView();
+ if (pPageView)
+ {
+ // break all creation actions when hiding page (#75081#)
+ BrkAction();
+
+ // Discard all selections on this page
+ bMrkChg = GetMarkedObjectListWriteAccess().DeletePageView(*pPageView);
+ }
+
+ SdrSnapView::HideSdrPage();
+
+ if(bMrkChg)
+ {
+ MarkListHasChanged();
+ AdjustMarkHdl();
+ }
+}
+
+
+void SdrMarkView::BegMarkObj(const Point& rPnt, bool bUnmark)
+{
+ BrkAction();
+
+ DBG_ASSERT(!mpMarkObjOverlay, "SdrMarkView::BegMarkObj: There exists a mpMarkObjOverlay (!)");
+
+ basegfx::B2DPoint aStartPos(rPnt.X(), rPnt.Y());
+ mpMarkObjOverlay.reset(new ImplMarkingOverlay(*this, aStartPos, bUnmark));
+
+ maDragStat.Reset(rPnt);
+ maDragStat.NextPoint();
+ maDragStat.SetMinMove(mnMinMovLog);
+}
+
+void SdrMarkView::MovMarkObj(const Point& rPnt)
+{
+ if(IsMarkObj() && maDragStat.CheckMinMoved(rPnt))
+ {
+ maDragStat.NextMove(rPnt);
+ DBG_ASSERT(mpMarkObjOverlay, "SdrSnapView::MovSetPageOrg: no ImplPageOriginOverlay (!)");
+ basegfx::B2DPoint aNewPos(rPnt.X(), rPnt.Y());
+ mpMarkObjOverlay->SetSecondPosition(aNewPos);
+ }
+}
+
+bool SdrMarkView::EndMarkObj()
+{
+ bool bRetval(false);
+
+ if(IsMarkObj())
+ {
+ if(maDragStat.IsMinMoved())
+ {
+ tools::Rectangle aRect(maDragStat.GetStart(), maDragStat.GetNow());
+ aRect.Justify();
+ MarkObj(aRect, mpMarkObjOverlay->IsUnmarking());
+ bRetval = true;
+ }
+
+ // cleanup
+ BrkMarkObj();
+ }
+
+ return bRetval;
+}
+
+void SdrMarkView::BrkMarkObj()
+{
+ if(IsMarkObj())
+ {
+ DBG_ASSERT(mpMarkObjOverlay, "SdrSnapView::MovSetPageOrg: no ImplPageOriginOverlay (!)");
+ mpMarkObjOverlay.reset();
+ }
+}
+
+
+bool SdrMarkView::BegMarkPoints(const Point& rPnt, bool bUnmark)
+{
+ if(HasMarkablePoints())
+ {
+ BrkAction();
+
+ DBG_ASSERT(!mpMarkPointsOverlay, "SdrMarkView::BegMarkObj: There exists a mpMarkPointsOverlay (!)");
+ basegfx::B2DPoint aStartPos(rPnt.X(), rPnt.Y());
+ mpMarkPointsOverlay.reset(new ImplMarkingOverlay(*this, aStartPos, bUnmark));
+
+ maDragStat.Reset(rPnt);
+ maDragStat.NextPoint();
+ maDragStat.SetMinMove(mnMinMovLog);
+
+ return true;
+ }
+
+ return false;
+}
+
+void SdrMarkView::MovMarkPoints(const Point& rPnt)
+{
+ if(IsMarkPoints() && maDragStat.CheckMinMoved(rPnt))
+ {
+ maDragStat.NextMove(rPnt);
+
+ DBG_ASSERT(mpMarkPointsOverlay, "SdrSnapView::MovSetPageOrg: no ImplPageOriginOverlay (!)");
+ basegfx::B2DPoint aNewPos(rPnt.X(), rPnt.Y());
+ mpMarkPointsOverlay->SetSecondPosition(aNewPos);
+ }
+}
+
+bool SdrMarkView::EndMarkPoints()
+{
+ bool bRetval(false);
+
+ if(IsMarkPoints())
+ {
+ if(maDragStat.IsMinMoved())
+ {
+ tools::Rectangle aRect(maDragStat.GetStart(), maDragStat.GetNow());
+ aRect.Justify();
+ MarkPoints(&aRect, mpMarkPointsOverlay->IsUnmarking());
+
+ bRetval = true;
+ }
+
+ // cleanup
+ BrkMarkPoints();
+ }
+
+ return bRetval;
+}
+
+void SdrMarkView::BrkMarkPoints()
+{
+ if(IsMarkPoints())
+ {
+ DBG_ASSERT(mpMarkPointsOverlay, "SdrSnapView::MovSetPageOrg: no ImplPageOriginOverlay (!)");
+ mpMarkPointsOverlay.reset();
+ }
+}
+
+
+bool SdrMarkView::BegMarkGluePoints(const Point& rPnt, bool bUnmark)
+{
+ if(HasMarkableGluePoints())
+ {
+ BrkAction();
+
+ DBG_ASSERT(!mpMarkGluePointsOverlay, "SdrMarkView::BegMarkObj: There exists a mpMarkGluePointsOverlay (!)");
+
+ basegfx::B2DPoint aStartPos(rPnt.X(), rPnt.Y());
+ mpMarkGluePointsOverlay.reset(new ImplMarkingOverlay(*this, aStartPos, bUnmark));
+ maDragStat.Reset(rPnt);
+ maDragStat.NextPoint();
+ maDragStat.SetMinMove(mnMinMovLog);
+
+ return true;
+ }
+
+ return false;
+}
+
+void SdrMarkView::MovMarkGluePoints(const Point& rPnt)
+{
+ if(IsMarkGluePoints() && maDragStat.CheckMinMoved(rPnt))
+ {
+ maDragStat.NextMove(rPnt);
+
+ DBG_ASSERT(mpMarkGluePointsOverlay, "SdrSnapView::MovSetPageOrg: no ImplPageOriginOverlay (!)");
+ basegfx::B2DPoint aNewPos(rPnt.X(), rPnt.Y());
+ mpMarkGluePointsOverlay->SetSecondPosition(aNewPos);
+ }
+}
+
+void SdrMarkView::EndMarkGluePoints()
+{
+ if(IsMarkGluePoints())
+ {
+ if(maDragStat.IsMinMoved())
+ {
+ tools::Rectangle aRect(maDragStat.GetStart(),maDragStat.GetNow());
+ aRect.Justify();
+ MarkGluePoints(&aRect, mpMarkGluePointsOverlay->IsUnmarking());
+ }
+
+ // cleanup
+ BrkMarkGluePoints();
+ }
+}
+
+void SdrMarkView::BrkMarkGluePoints()
+{
+ if(IsMarkGluePoints())
+ {
+ DBG_ASSERT(mpMarkGluePointsOverlay, "SdrSnapView::MovSetPageOrg: no ImplPageOriginOverlay (!)");
+ mpMarkGluePointsOverlay.reset();
+ }
+}
+
+bool SdrMarkView::MarkableObjectsExceed( int n ) const
+{
+ SdrPageView* pPV = GetSdrPageView();
+ if (!pPV)
+ return false;
+
+ SdrObjList* pOL=pPV->GetObjList();
+ const size_t nObjCount = pOL->GetObjCount();
+ for (size_t nObjNum=0; nObjNum<nObjCount; ++nObjNum) {
+ SdrObject* pObj=pOL->GetObj(nObjNum);
+ if (IsObjMarkable(pObj,pPV) && --n<0)
+ return true;
+ }
+
+ return false;
+}
+
+void SdrMarkView::hideMarkHandles()
+{
+ if(!mbMarkHandlesHidden)
+ {
+ mbMarkHandlesHidden = true;
+ AdjustMarkHdl();
+ }
+}
+
+void SdrMarkView::showMarkHandles()
+{
+ if(mbMarkHandlesHidden)
+ {
+ mbMarkHandlesHidden = false;
+ AdjustMarkHdl();
+ }
+}
+
+bool SdrMarkView::ImpIsFrameHandles() const
+{
+ const size_t nMarkCount=GetMarkedObjectCount();
+ bool bFrmHdl=nMarkCount>static_cast<size_t>(mnFrameHandlesLimit) || mbForceFrameHandles;
+ bool bStdDrag=meDragMode==SdrDragMode::Move;
+ if (nMarkCount==1 && bStdDrag && bFrmHdl)
+ {
+ const SdrObject* pObj=GetMarkedObjectByIndex(0);
+ if (pObj && pObj->GetObjInventor()==SdrInventor::Default)
+ {
+ SdrObjKind nIdent=pObj->GetObjIdentifier();
+ if (nIdent==SdrObjKind::Line || nIdent==SdrObjKind::Edge || nIdent==SdrObjKind::Caption || nIdent==SdrObjKind::Measure || nIdent==SdrObjKind::CustomShape || nIdent==SdrObjKind::Table )
+ {
+ bFrmHdl=false;
+ }
+ }
+ }
+ if (!bStdDrag && !bFrmHdl) {
+ // all other drag modes only with FrameHandles
+ bFrmHdl=true;
+ if (meDragMode==SdrDragMode::Rotate) {
+ // when rotating, use ObjOwn drag, if there's at least 1 PolyObj
+ for (size_t nMarkNum=0; nMarkNum<nMarkCount && bFrmHdl; ++nMarkNum) {
+ const SdrMark* pM=GetSdrMarkByIndex(nMarkNum);
+ const SdrObject* pObj=pM->GetMarkedSdrObj();
+ bFrmHdl=!pObj->IsPolyObj();
+ }
+ }
+ }
+ if (!bFrmHdl) {
+ // FrameHandles, if at least 1 Obj can't do SpecialDrag
+ for (size_t nMarkNum=0; nMarkNum<nMarkCount && !bFrmHdl; ++nMarkNum) {
+ const SdrMark* pM=GetSdrMarkByIndex(nMarkNum);
+ const SdrObject* pObj=pM->GetMarkedSdrObj();
+ bFrmHdl=!pObj->hasSpecialDrag();
+ }
+ }
+
+ // no FrameHdl for crop
+ if(bFrmHdl && SdrDragMode::Crop == meDragMode)
+ {
+ bFrmHdl = false;
+ }
+
+ return bFrmHdl;
+}
+
+namespace
+{
+std::u16string_view lcl_getDragMethodServiceName( std::u16string_view rCID )
+{
+ std::u16string_view aRet;
+
+ size_t nIndexStart = rCID.find( u"DragMethod=" );
+ if( nIndexStart != std::u16string_view::npos )
+ {
+ nIndexStart = rCID.find( '=', nIndexStart );
+ if( nIndexStart != std::u16string_view::npos )
+ {
+ nIndexStart++;
+ size_t nNextSlash = rCID.find( '/', nIndexStart );
+ if( nNextSlash != std::u16string_view::npos )
+ {
+ sal_Int32 nIndexEnd = nNextSlash;
+ size_t nNextColon = rCID.find( ':', nIndexStart );
+ if( nNextColon == std::u16string_view::npos || nNextColon < nNextSlash )
+ nIndexEnd = nNextColon;
+ aRet = rCID.substr(nIndexStart,nIndexEnd-nIndexStart);
+ }
+ }
+ }
+ return aRet;
+}
+
+std::u16string_view lcl_getDragParameterString( std::u16string_view rCID )
+{
+ std::u16string_view aRet;
+
+ size_t nIndexStart = rCID.find( u"DragParameter=" );
+ if( nIndexStart != std::u16string_view::npos )
+ {
+ nIndexStart = rCID.find( '=', nIndexStart );
+ if( nIndexStart != std::u16string_view::npos )
+ {
+ nIndexStart++;
+ size_t nNextSlash = rCID.find( '/', nIndexStart );
+ if( nNextSlash != std::u16string_view::npos )
+ {
+ sal_Int32 nIndexEnd = nNextSlash;
+ size_t nNextColon = rCID.find( ':', nIndexStart );
+ if( nNextColon == std::u16string_view::npos || nNextColon < nNextSlash )
+ nIndexEnd = nNextColon;
+ aRet = rCID.substr(nIndexStart,nIndexEnd-nIndexStart);
+ }
+ }
+ }
+ return aRet;
+}
+} // anonymous namespace
+
+bool SdrMarkView::dumpGluePointsToJSON(boost::property_tree::ptree& rTree)
+{
+ bool result = false;
+ tools::Long nSignX = mbNegativeX ? -1 : 1;
+ if (OutputDevice* pOutDev = mpMarkedPV ? mpMarkedPV->GetView().GetFirstOutputDevice() : nullptr)
+ {
+ bool bConvertUnit = false;
+ if (pOutDev->GetMapMode().GetMapUnit() == MapUnit::Map100thMM)
+ bConvertUnit = true;
+ const SdrObjList* pOL = mpMarkedPV->GetObjList();
+ if (!pOL)
+ return false;
+ const size_t nObjCount = pOL->GetObjCount();
+ boost::property_tree::ptree elements;
+ for (size_t nObjNum = 0; nObjNum < nObjCount; ++nObjNum)
+ {
+ SdrObject* pObj = pOL->GetObj(nObjNum);
+ if (!pObj)
+ continue;
+ if (pObj == GetMarkedObjectByIndex(0))
+ continue;
+ const SdrGluePointList* pGPL = pObj->GetGluePointList();
+ bool VertexObject = !(pGPL && pGPL->GetCount());
+ const size_t count = !VertexObject ? pGPL->GetCount() : 4;
+ boost::property_tree::ptree object;
+ boost::property_tree::ptree points;
+ for (size_t i = 0; i < count; ++i)
+ {
+ boost::property_tree::ptree node;
+ boost::property_tree::ptree point;
+ const SdrGluePoint& rGP = !VertexObject ? (*pGPL)[i] : pObj->GetVertexGluePoint(i);
+ Point rPoint = rGP.GetAbsolutePos(*pObj);
+ if (bConvertUnit)
+ {
+ rPoint = o3tl::convert(rPoint, o3tl::Length::mm100, o3tl::Length::twip);
+ }
+ point.put("x", nSignX * rPoint.getX());
+ point.put("y", rPoint.getY());
+ node.add_child("point", point);
+ points.push_back(std::make_pair("", node));
+ }
+ basegfx::B2DVector aGridOffset(0.0, 0.0);
+ Point objLogicRectTopLeft = pObj->GetLogicRect().TopLeft();
+ if(getPossibleGridOffsetForPosition(aGridOffset, basegfx::B2DPoint(objLogicRectTopLeft.X(), objLogicRectTopLeft.Y()), GetSdrPageView()))
+ {
+ Point p(aGridOffset.getX(), aGridOffset.getY());
+ if (bConvertUnit)
+ {
+ p = o3tl::convert(p, o3tl::Length::mm100, o3tl::Length::twip);
+ }
+ boost::property_tree::ptree gridOffset;
+ gridOffset.put("x", nSignX * p.getX());
+ gridOffset.put("y", p.getY());
+ object.add_child("gridoffset", gridOffset);
+ }
+ object.put("ordnum", pObj->GetOrdNum());
+ object.add_child("gluepoints", points);
+ elements.push_back(std::make_pair("", object));
+ result = true;
+ }
+ rTree.add_child("shapes", elements);
+ }
+ return result;
+}
+
+void SdrMarkView::SetMarkHandlesForLOKit(tools::Rectangle const & rRect, const SfxViewShell* pOtherShell)
+{
+ SfxViewShell* pViewShell = GetSfxViewShell();
+
+ tools::Rectangle aSelection(rRect);
+ tools::Long nSignX = mbNegativeX ? -1 : 1;
+ bool bIsChart = false;
+ Point addLogicOffset(0, 0);
+ bool convertMapMode = false;
+ if (!rRect.IsEmpty())
+ {
+ sal_uInt32 nTotalPaintWindows = this->PaintWindowCount();
+ if (nTotalPaintWindows == 1)
+ {
+ const OutputDevice* pOut = this->GetFirstOutputDevice();
+ const vcl::Window* pWin = pOut ? pOut->GetOwnerWindow() : nullptr;
+ if (pWin && pWin->IsChart())
+ {
+ bIsChart = true;
+ const vcl::Window* pViewShellWindow = GetSfxViewShell()->GetEditWindowForActiveOLEObj();
+ if (pViewShellWindow && pViewShellWindow->IsAncestorOf(*pWin))
+ {
+ Point aOffsetPx = pWin->GetOffsetPixelFrom(*pViewShellWindow);
+ if (mbNegativeX && AllSettings::GetLayoutRTL())
+ {
+ // mbNegativeX is set only for Calc in RTL mode.
+ // If global RTL flag is set, vcl-window X offset of chart window is
+ // mirrored w.r.t parent window rectangle. This needs to be reverted.
+ aOffsetPx.setX(pViewShellWindow->GetOutOffXPixel() + pViewShellWindow->GetSizePixel().Width()
+ - pWin->GetOutOffXPixel() - pWin->GetSizePixel().Width());
+ }
+ Point aLogicOffset = pWin->PixelToLogic(aOffsetPx);
+ addLogicOffset = aLogicOffset;
+ aSelection.Move(aLogicOffset.getX(), aLogicOffset.getY());
+ }
+ }
+ }
+ }
+
+ if (!aSelection.IsEmpty())
+ {
+ // In case the map mode is in 100th MM, then need to convert the coordinates over to twips for LOK.
+ if (mpMarkedPV)
+ {
+ if (OutputDevice* pOutputDevice = mpMarkedPV->GetView().GetFirstOutputDevice())
+ {
+ if (pOutputDevice->GetMapMode().GetMapUnit() == MapUnit::Map100thMM)
+ {
+ aSelection = o3tl::convert(aSelection, o3tl::Length::mm100, o3tl::Length::twip);
+ convertMapMode = true;
+ }
+ }
+ }
+
+ // hide the text selection too
+ pViewShell->libreOfficeKitViewCallback(LOK_CALLBACK_TEXT_SELECTION, "");
+ }
+
+ {
+ OString sSelectionText;
+ OString sSelectionTextView;
+ boost::property_tree::ptree aTableJsonTree;
+ boost::property_tree::ptree aGluePointsTree;
+ bool bTableSelection = false;
+ bool bConnectorSelection = false;
+
+ if (mpMarkedObj && mpMarkedObj->GetObjIdentifier() == SdrObjKind::Table)
+ {
+ auto& rTableObject = dynamic_cast<sdr::table::SdrTableObj&>(*mpMarkedObj);
+ bTableSelection = rTableObject.createTableEdgesJson(aTableJsonTree);
+ }
+ if (mpMarkedObj && mpMarkedObj->GetObjIdentifier() == SdrObjKind::Edge)
+ {
+ bConnectorSelection = dumpGluePointsToJSON(aGluePointsTree);
+ }
+ if (GetMarkedObjectCount())
+ {
+ SdrMark* pM = GetSdrMarkByIndex(0);
+ SdrObject* pO = pM->GetMarkedSdrObj();
+ Degree100 nRotAngle = pO->GetRotateAngle();
+ // true if we are dealing with a RotGrfFlyFrame
+ // (SwVirtFlyDrawObj with a SwGrfNode)
+ bool bWriterGraphic = pO->HasLimitedRotation();
+
+ OStringBuffer aExtraInfo;
+ OString handleArrayStr;
+
+ aExtraInfo.append("{\"id\":\"");
+ aExtraInfo.append(reinterpret_cast<sal_IntPtr>(pO));
+ aExtraInfo.append("\",\"type\":");
+ aExtraInfo.append(static_cast<sal_Int32>(pO->GetObjIdentifier()));
+
+ // In core, the gridOffset is calculated based on the LogicRect's TopLeft coordinate
+ // In online, we have the SnapRect and we calculate it based on its TopLeft coordinate
+ // SnapRect's TopLeft and LogicRect's TopLeft match unless there is rotation
+ // but the rotation is not applied to the LogicRect. Therefore,
+ // what we calculate in online does not match with the core in case of the rotation.
+ // Here we can send the correct gridOffset in the selection callback, this way
+ // whether the shape is rotated or not, we will always have the correct gridOffset
+ // Note that the gridOffset is calculated from the first selected obj
+ basegfx::B2DVector aGridOffset(0.0, 0.0);
+ if(getPossibleGridOffsetForSdrObject(aGridOffset, GetMarkedObjectByIndex(0), GetSdrPageView()))
+ {
+ Point p(aGridOffset.getX(), aGridOffset.getY());
+ if (convertMapMode)
+ p = o3tl::convert(p, o3tl::Length::mm100, o3tl::Length::twip);
+ aExtraInfo.append(",\"gridOffsetX\":");
+ aExtraInfo.append(nSignX * p.getX());
+ aExtraInfo.append(",\"gridOffsetY\":");
+ aExtraInfo.append(p.getY());
+ }
+
+ if (bWriterGraphic)
+ {
+ aExtraInfo.append(", \"isWriterGraphic\": true");
+ }
+ else if (bIsChart)
+ {
+ LokChartHelper aChartHelper(pViewShell);
+ css::uno::Reference<css::frame::XController>& xChartController = aChartHelper.GetXController();
+ css::uno::Reference<css::view::XSelectionSupplier> xSelectionSupplier( xChartController, uno::UNO_QUERY);
+ if (xSelectionSupplier.is())
+ {
+ uno::Any aSel = xSelectionSupplier->getSelection();
+ OUString aValue;
+ if (aSel >>= aValue)
+ {
+ OString aObjectCID(aValue.getStr(), aValue.getLength(), osl_getThreadTextEncoding());
+ const std::vector<OString> aProps{"Draggable", "Resizable", "Rotatable"};
+ for (const auto& rProp: aProps)
+ {
+ sal_Int32 nPos = aObjectCID.indexOf(rProp);
+ if (nPos == -1) continue;
+ nPos += rProp.getLength() + 1; // '='
+ if (aExtraInfo.getLength() > 2) // != "{ "
+ aExtraInfo.append(", ");
+ aExtraInfo.append("\"is");
+ aExtraInfo.append(rProp);
+ aExtraInfo.append("\": ");
+ aExtraInfo.append(OString::boolean(aObjectCID[nPos] == '1'));
+ }
+
+ std::u16string_view sDragMethod = lcl_getDragMethodServiceName(aValue);
+ if (sDragMethod == u"PieSegmentDragging")
+ {
+ // old initial offset inside the CID returned by xSelectionSupplier->getSelection()
+ // after a pie segment dragging; using SdrObject::GetName for getting a CID with the updated offset
+ aValue = pO->GetName();
+ std::u16string_view sDragParameters = lcl_getDragParameterString(aValue);
+ if (!sDragParameters.empty())
+ {
+ aExtraInfo.append(", \"dragInfo\": { ");
+ aExtraInfo.append("\"dragMethod\": \"");
+ aExtraInfo.append(OUString(sDragMethod).toUtf8());
+ aExtraInfo.append("\"");
+
+ sal_Int32 nStartIndex = 0;
+ std::array<int, 5> aDragParameters;
+ for (auto& rParam : aDragParameters)
+ {
+ std::u16string_view sParam = o3tl::getToken(sDragParameters, 0, ',', nStartIndex);
+ if (sParam.empty())
+ break;
+ rParam = o3tl::toInt32(sParam);
+ }
+
+ // initial offset in %
+ if (aDragParameters[0] < 0)
+ aDragParameters[0] = 0;
+ else if (aDragParameters[0] > 100)
+ aDragParameters[0] = 100;
+
+ aExtraInfo.append(", \"initialOffset\": ");
+ aExtraInfo.append(static_cast<sal_Int32>(aDragParameters[0]));
+
+ // drag direction constraint
+ Point aMinPos(aDragParameters[1], aDragParameters[2]);
+ Point aMaxPos(aDragParameters[3], aDragParameters[4]);
+ Point aDragDirection = aMaxPos - aMinPos;
+ aDragDirection = o3tl::convert(aDragDirection, o3tl::Length::mm100, o3tl::Length::twip);
+
+ aExtraInfo.append(", \"dragDirection\": [");
+ aExtraInfo.append(aDragDirection.toString());
+ aExtraInfo.append("]");
+
+ // polygon approximating the pie segment or donut segment
+ if (pO->GetObjIdentifier() == SdrObjKind::PathFill)
+ {
+ const basegfx::B2DPolyPolygon aPolyPolygon(pO->TakeXorPoly());
+ if (aPolyPolygon.count() == 1)
+ {
+ const basegfx::B2DPolygon aPolygon = aPolyPolygon.getB2DPolygon(0);
+ if (sal_uInt32 nPolySize = aPolygon.count())
+ {
+ const OutputDevice* pOut = this->GetFirstOutputDevice();
+ const vcl::Window* pWin = pOut ? pOut->GetOwnerWindow() : nullptr;
+ const vcl::Window* pViewShellWindow = pViewShell->GetEditWindowForActiveOLEObj();
+ if (pWin && pViewShellWindow && pViewShellWindow->IsAncestorOf(*pWin))
+ {
+ // in the following code escaping sequences used inside raw literal strings
+ // are for making them understandable by the JSON parser
+
+ Point aOffsetPx = pWin->GetOffsetPixelFrom(*pViewShellWindow);
+ Point aLogicOffset = pWin->PixelToLogic(aOffsetPx);
+ OString sPolygonElem("<polygon points=\\\"");
+ for (sal_uInt32 nIndex = 0; nIndex < nPolySize; ++nIndex)
+ {
+ const basegfx::B2DPoint aB2Point = aPolygon.getB2DPoint(nIndex);
+ Point aPoint(aB2Point.getX(), aB2Point.getY());
+ aPoint.Move(aLogicOffset.getX(), aLogicOffset.getY());
+ if (mbNegativeX)
+ aPoint.setX(-aPoint.X());
+ if (nIndex > 0)
+ sPolygonElem += " ";
+ sPolygonElem += aPoint.toString();
+ }
+ sPolygonElem += R"elem(\" style=\"stroke: none; fill: rgb(114,159,207); fill-opacity: 0.8\"/>)elem";
+
+ OString sSVGElem = R"elem(<svg version=\"1.2\" width=\")elem" +
+ OString::number(aSelection.GetWidth() / 100.0) +
+ R"elem(mm\" height=\")elem" +
+ OString::number(aSelection.GetHeight() / 100.0) +
+ R"elem(mm\" viewBox=\")elem" +
+ aSelection.toString() +
+ R"elem(\" preserveAspectRatio=\"xMidYMid\" xmlns=\"http://www.w3.org/2000/svg\">)elem";
+
+ aExtraInfo.append(", \"svg\": \"");
+ aExtraInfo.append(sSVGElem);
+ aExtraInfo.append("\\n ");
+ aExtraInfo.append(sPolygonElem);
+ aExtraInfo.append("\\n</svg>");
+ aExtraInfo.append("\""); // svg
+ }
+ }
+ }
+ }
+ aExtraInfo.append("}"); // dragInfo
+ }
+ }
+ }
+ }
+ }
+ if (!bTableSelection && !pOtherShell && maHdlList.GetHdlCount())
+ {
+ boost::property_tree::ptree responseJSON;
+ boost::property_tree::ptree others;
+ boost::property_tree::ptree anchor;
+ boost::property_tree::ptree rectangle;
+ boost::property_tree::ptree poly;
+ boost::property_tree::ptree custom;
+ boost::property_tree::ptree nodes;
+ for (size_t i = 0; i < maHdlList.GetHdlCount(); i++)
+ {
+ SdrHdl *pHdl = maHdlList.GetHdl(i);
+ boost::property_tree::ptree child;
+ boost::property_tree::ptree point;
+ sal_Int32 kind = static_cast<sal_Int32>(pHdl->GetKind());
+ child.put("id", pHdl->GetObjHdlNum());
+ child.put("kind", kind);
+ child.put("pointer", static_cast<sal_Int32>(pHdl->GetPointer()));
+ Point pHdlPos = pHdl->GetPos();
+ pHdlPos.Move(addLogicOffset.getX(), addLogicOffset.getY());
+ if (convertMapMode)
+ {
+ pHdlPos = o3tl::convert(pHdlPos, o3tl::Length::mm100, o3tl::Length::twip);
+ }
+ point.put("x", pHdlPos.getX());
+ point.put("y", pHdlPos.getY());
+ child.add_child("point", point);
+ const auto node = std::make_pair("", child);
+ boost::property_tree::ptree* selectedNode = nullptr;
+ if (kind >= static_cast<sal_Int32>(SdrHdlKind::UpperLeft) && kind <= static_cast<sal_Int32>(SdrHdlKind::LowerRight))
+ {
+ selectedNode = &rectangle;
+ }
+ else if (kind == static_cast<sal_Int32>(SdrHdlKind::Poly))
+ {
+ selectedNode = &poly;
+ }
+ else if (kind == static_cast<sal_Int32>(SdrHdlKind::CustomShape1))
+ {
+ selectedNode = &custom;
+ }
+ else if (kind == static_cast<sal_Int32>(SdrHdlKind::Anchor) || kind == static_cast<sal_Int32>(SdrHdlKind::Anchor_TR))
+ {
+ if (getSdrModelFromSdrView().IsWriter())
+ selectedNode = &anchor;
+ else
+ // put it to others as we don't render them except in writer
+ selectedNode = &others;
+ }
+ else
+ {
+ selectedNode = &others;
+ }
+ std::string sKind = std::to_string(kind);
+ boost::optional< boost::property_tree::ptree& > kindNode = selectedNode->get_child_optional(sKind.c_str());
+ if (!kindNode)
+ {
+ boost::property_tree::ptree newChild;
+ newChild.push_back(node);
+ selectedNode->add_child(sKind.c_str(), newChild);
+ }
+ else
+ kindNode.get().push_back(node);
+ }
+ nodes.add_child("rectangle", rectangle);
+ nodes.add_child("poly", poly);
+ nodes.add_child("custom", custom);
+ nodes.add_child("anchor", anchor);
+ nodes.add_child("others", others);
+ responseJSON.add_child("kinds", nodes);
+ std::stringstream aStream;
+ boost::property_tree::write_json(aStream, responseJSON, /*pretty=*/ false);
+ handleArrayStr = ", \"handles\":";
+ handleArrayStr = handleArrayStr + aStream.str().c_str();
+ if (bConnectorSelection)
+ {
+ aStream.str("");
+ boost::property_tree::write_json(aStream, aGluePointsTree, /*pretty=*/ false);
+ handleArrayStr = handleArrayStr + ", \"GluePoints\":";
+ handleArrayStr = handleArrayStr + aStream.str().c_str();
+ }
+ }
+
+ if (mbNegativeX)
+ {
+ tools::Rectangle aNegatedRect(aSelection);
+ aNegatedRect.SetLeft(-aNegatedRect.Left());
+ aNegatedRect.SetRight(-aNegatedRect.Right());
+ aNegatedRect.Justify();
+ sSelectionText = aNegatedRect.toString() +
+ ", " + OString::number(nRotAngle.get());
+ }
+ else
+ {
+ sSelectionText = aSelection.toString() +
+ ", " + OString::number(nRotAngle.get());
+ }
+ if (!aExtraInfo.isEmpty())
+ {
+ sSelectionTextView = sSelectionText + ", " + aExtraInfo + "}";
+ aExtraInfo.append(handleArrayStr);
+ aExtraInfo.append("}");
+ sSelectionText += ", " + aExtraInfo;
+ aExtraInfo.setLength(0);
+ }
+ }
+
+ if (sSelectionText.isEmpty())
+ {
+ sSelectionText = "EMPTY";
+ sSelectionTextView = "EMPTY";
+ }
+
+ if (bTableSelection)
+ {
+ boost::property_tree::ptree aTableRectangle;
+ aTableRectangle.put("x", aSelection.Left());
+ aTableRectangle.put("y", aSelection.Top());
+ aTableRectangle.put("width", aSelection.GetWidth());
+ aTableRectangle.put("height", aSelection.GetHeight());
+ aTableJsonTree.push_back(std::make_pair("rectangle", aTableRectangle));
+
+ std::stringstream aStream;
+ boost::property_tree::write_json(aStream, aTableJsonTree);
+ pViewShell->libreOfficeKitViewCallback(LOK_CALLBACK_TABLE_SELECTED, aStream.str().c_str());
+ }
+ else if (!getSdrModelFromSdrView().IsWriter())
+ {
+ pViewShell->libreOfficeKitViewCallback(LOK_CALLBACK_TABLE_SELECTED, "{}");
+ }
+
+ if (pOtherShell)
+ {
+ // Another shell wants to know about our existing
+ // selection.
+ if (pViewShell != pOtherShell)
+ SfxLokHelper::notifyOtherView(pViewShell, pOtherShell, LOK_CALLBACK_GRAPHIC_VIEW_SELECTION, "selection", sSelectionTextView);
+ }
+ else
+ {
+ // We have a new selection, so both pViewShell and the
+ // other views want to know about it.
+ pViewShell->libreOfficeKitViewCallback(LOK_CALLBACK_GRAPHIC_SELECTION, sSelectionText.getStr());
+ SfxLokHelper::notifyOtherViews(pViewShell, LOK_CALLBACK_GRAPHIC_VIEW_SELECTION, "selection", sSelectionTextView);
+ }
+ }
+}
+
+void SdrMarkView::SetMarkHandles(SfxViewShell* pOtherShell)
+{
+ // remember old focus handle values to search for it again
+ const SdrHdl* pSaveOldFocusHdl = maHdlList.GetFocusHdl();
+ bool bSaveOldFocus(false);
+ sal_uInt32 nSavePolyNum(0), nSavePointNum(0);
+ SdrHdlKind eSaveKind(SdrHdlKind::Move);
+ SdrObject* pSaveObj = nullptr;
+
+ mpMarkingSubSelectionOverlay.reset();
+
+ if(pSaveOldFocusHdl
+ && pSaveOldFocusHdl->GetObj()
+ && dynamic_cast<const SdrPathObj*>(pSaveOldFocusHdl->GetObj()) != nullptr
+ && (pSaveOldFocusHdl->GetKind() == SdrHdlKind::Poly || pSaveOldFocusHdl->GetKind() == SdrHdlKind::BezierWeight))
+ {
+ bSaveOldFocus = true;
+ nSavePolyNum = pSaveOldFocusHdl->GetPolyNum();
+ nSavePointNum = pSaveOldFocusHdl->GetPointNum();
+ pSaveObj = pSaveOldFocusHdl->GetObj();
+ eSaveKind = pSaveOldFocusHdl->GetKind();
+ }
+
+ // delete/clear all handles. This will always be done, even with areMarkHandlesHidden()
+ maHdlList.Clear();
+ maHdlList.SetRotateShear(meDragMode==SdrDragMode::Rotate);
+ maHdlList.SetDistortShear(meDragMode==SdrDragMode::Shear);
+ mpMarkedObj=nullptr;
+ mpMarkedPV=nullptr;
+
+ // are handles enabled at all? Create only then
+ if(areMarkHandlesHidden())
+ return;
+
+ // There can be multiple mark views, but we're only interested in the one that has a window associated.
+ const bool bTiledRendering = comphelper::LibreOfficeKit::isActive() && GetFirstOutputDevice() && GetFirstOutputDevice()->GetOutDevType() == OUTDEV_WINDOW;
+
+ const size_t nMarkCount=GetMarkedObjectCount();
+ bool bStdDrag=meDragMode==SdrDragMode::Move;
+ bool bSingleTextObjMark=false;
+ bool bLimitedRotation(false);
+
+ if (nMarkCount==1)
+ {
+ mpMarkedObj=GetMarkedObjectByIndex(0);
+
+ if(nullptr != mpMarkedObj)
+ {
+ bSingleTextObjMark =
+ dynamic_cast<const SdrTextObj*>( mpMarkedObj) != nullptr &&
+ static_cast<SdrTextObj*>(mpMarkedObj)->IsTextFrame();
+
+ // RotGrfFlyFrame: we may have limited rotation
+ bLimitedRotation = SdrDragMode::Rotate == meDragMode && mpMarkedObj->HasLimitedRotation();
+ }
+ }
+
+ bool bFrmHdl=ImpIsFrameHandles();
+
+ if (nMarkCount>0)
+ {
+ mpMarkedPV=GetSdrPageViewOfMarkedByIndex(0);
+
+ for (size_t nMarkNum=0; nMarkNum<nMarkCount && (mpMarkedPV!=nullptr || !bFrmHdl); ++nMarkNum)
+ {
+ const SdrMark* pM=GetSdrMarkByIndex(nMarkNum);
+
+ if (mpMarkedPV!=pM->GetPageView())
+ {
+ mpMarkedPV=nullptr;
+ }
+ }
+ }
+
+ SfxViewShell* pViewShell = GetSfxViewShell();
+
+ // check if text edit or ole is active and handles need to be suppressed. This may be the case
+ // when a single object is selected
+ // Using a strict return statement is okay here; no handles means *no* handles.
+ if(mpMarkedObj)
+ {
+ // formerly #i33755#: If TextEdit is active the EditEngine will directly paint
+ // to the window, so suppress Overlay and handles completely; a text frame for
+ // the active text edit will be painted by the repaint mechanism in
+ // SdrObjEditView::ImpPaintOutlinerView in this case. This needs to be reworked
+ // in the future
+ // Also formerly #122142#: Pretty much the same for SdrCaptionObj's in calc.
+ if(static_cast<SdrView*>(this)->IsTextEdit())
+ {
+ const SdrTextObj* pSdrTextObj = dynamic_cast< const SdrTextObj* >(mpMarkedObj);
+
+ if (pSdrTextObj && pSdrTextObj->IsInEditMode())
+ {
+ if (!bTiledRendering)
+ return;
+ }
+ }
+
+ // formerly #i118524#: if inplace activated OLE is selected, suppress handles
+ const SdrOle2Obj* pSdrOle2Obj = dynamic_cast< const SdrOle2Obj* >(mpMarkedObj);
+
+ if(pSdrOle2Obj && (pSdrOle2Obj->isInplaceActive() || pSdrOle2Obj->isUiActive()))
+ {
+ return;
+ }
+
+ if (!maSubSelectionList.empty())
+ {
+ mpMarkingSubSelectionOverlay = std::make_unique<MarkingSubSelectionOverlay>(*this, maSubSelectionList);
+ }
+ }
+
+ tools::Rectangle aRect(GetMarkedObjRect());
+
+ if (bFrmHdl)
+ {
+ if(!aRect.IsEmpty())
+ {
+ // otherwise nothing is found
+ const size_t nSiz0(maHdlList.GetHdlCount());
+
+ if( bSingleTextObjMark )
+ {
+ mpMarkedObj->AddToHdlList(maHdlList);
+ }
+ else
+ {
+ const bool bWdt0(aRect.Left() == aRect.Right());
+ const bool bHgt0(aRect.Top() == aRect.Bottom());
+
+ if (bWdt0 && bHgt0)
+ {
+ maHdlList.AddHdl(std::make_unique<SdrHdl>(aRect.TopLeft(), SdrHdlKind::UpperLeft));
+ }
+ else if (!bStdDrag && (bWdt0 || bHgt0))
+ {
+ maHdlList.AddHdl(std::make_unique<SdrHdl>(aRect.TopLeft(), SdrHdlKind::UpperLeft));
+ maHdlList.AddHdl(std::make_unique<SdrHdl>(aRect.BottomRight(), SdrHdlKind::LowerRight));
+ }
+ else
+ {
+ if (!bWdt0 && !bHgt0)
+ {
+ maHdlList.AddHdl(std::make_unique<SdrHdl>(aRect.TopLeft(), SdrHdlKind::UpperLeft));
+ }
+
+ if (!bLimitedRotation && !bHgt0)
+ {
+ maHdlList.AddHdl(std::make_unique<SdrHdl>(aRect.TopCenter(), SdrHdlKind::Upper));
+ }
+
+ if (!bWdt0 && !bHgt0)
+ {
+ maHdlList.AddHdl(std::make_unique<SdrHdl>(aRect.TopRight(), SdrHdlKind::UpperRight));
+ }
+
+ if (!bLimitedRotation && !bWdt0)
+ {
+ maHdlList.AddHdl(std::make_unique<SdrHdl>(aRect.LeftCenter(), SdrHdlKind::Left ));
+ maHdlList.AddHdl(std::make_unique<SdrHdl>(aRect.RightCenter(), SdrHdlKind::Right));
+ }
+
+ if (!bWdt0 && !bHgt0)
+ {
+ maHdlList.AddHdl(std::make_unique<SdrHdl>(aRect.BottomLeft(), SdrHdlKind::LowerLeft));
+ }
+
+ if (!bLimitedRotation && !bHgt0)
+ {
+ maHdlList.AddHdl(std::make_unique<SdrHdl>(aRect.BottomCenter(), SdrHdlKind::Lower));
+ }
+
+ if (!bWdt0 && !bHgt0)
+ {
+ maHdlList.AddHdl(std::make_unique<SdrHdl>(aRect.BottomRight(), SdrHdlKind::LowerRight));
+ }
+ }
+ }
+
+ // Diagram selection visualization support
+ // Caution: CppunitTest_sd_tiledrendering shows that mpMarkedObj *can* actually be nullptr (!)
+ if(nullptr != mpMarkedObj && mpMarkedObj->isDiagram())
+ {
+ mpMarkedObj->AddToHdlList(maHdlList);
+ }
+
+ const size_t nSiz1(maHdlList.GetHdlCount());
+
+ // moved setting the missing parameters at SdrHdl here from the
+ // single loop above (bSingleTextObjMark), this was missing all
+ // the time. Setting SdrObject is now required to correctly get
+ // the View-Dependent evtl. GridOffset adapted
+ for (size_t i=nSiz0; i<nSiz1; ++i)
+ {
+ SdrHdl* pHdl=maHdlList.GetHdl(i);
+ pHdl->SetObj(mpMarkedObj);
+ pHdl->SetPageView(mpMarkedPV);
+ pHdl->SetObjHdlNum(sal_uInt16(i-nSiz0));
+ }
+ }
+ }
+ else
+ {
+ bool bDone(false);
+
+ // moved crop handling to non-frame part and the handle creation to SdrGrafObj
+ if(1 == nMarkCount && mpMarkedObj && SdrDragMode::Crop == meDragMode)
+ {
+ // Default addCropHandles from SdrObject does nothing. When pMarkedObj is SdrGrafObj, previous
+ // behaviour occurs (code in svx/source/svdraw/svdograf.cxx). When pMarkedObj is SwVirtFlyDrawObj
+ // writer takes the responsibility of adding handles (code in sw/source/core/draw/dflyobj.cxx)
+ const size_t nSiz0(maHdlList.GetHdlCount());
+ mpMarkedObj->addCropHandles(maHdlList);
+ const size_t nSiz1(maHdlList.GetHdlCount());
+
+ // Was missing: Set infos at SdrCropHdl
+ for (size_t i=nSiz0; i<nSiz1; ++i)
+ {
+ SdrHdl* pHdl=maHdlList.GetHdl(i);
+ pHdl->SetObj(mpMarkedObj);
+ pHdl->SetPageView(mpMarkedPV);
+ pHdl->SetObjHdlNum(sal_uInt16(i-nSiz0));
+ }
+
+ bDone = true;
+ }
+
+ if(!bDone)
+ {
+ for (size_t nMarkNum=0; nMarkNum<nMarkCount; ++nMarkNum)
+ {
+ const SdrMark* pM=GetSdrMarkByIndex(nMarkNum);
+ SdrObject* pObj=pM->GetMarkedSdrObj();
+ SdrPageView* pPV=pM->GetPageView();
+ const size_t nSiz0=maHdlList.GetHdlCount();
+ pObj->AddToHdlList(maHdlList);
+ const size_t nSiz1=maHdlList.GetHdlCount();
+ bool bPoly=pObj->IsPolyObj();
+ const SdrUShortCont& rMrkPnts = pM->GetMarkedPoints();
+ for (size_t i=nSiz0; i<nSiz1; ++i)
+ {
+ SdrHdl* pHdl=maHdlList.GetHdl(i);
+ pHdl->SetObj(pObj);
+ pHdl->SetPageView(pPV);
+ pHdl->SetObjHdlNum(sal_uInt16(i-nSiz0));
+
+ if (bPoly)
+ {
+ bool bSelected= rMrkPnts.find( sal_uInt16(i-nSiz0) ) != rMrkPnts.end();
+ pHdl->SetSelected(bSelected);
+ if (mbPlusHdlAlways || bSelected)
+ {
+ SdrHdlList plusList(nullptr);
+ pObj->AddToPlusHdlList(plusList, *pHdl);
+ sal_uInt32 nPlusHdlCnt=plusList.GetHdlCount();
+ for (sal_uInt32 nPlusNum=0; nPlusNum<nPlusHdlCnt; nPlusNum++)
+ {
+ SdrHdl* pPlusHdl=plusList.GetHdl(nPlusNum);
+ pPlusHdl->SetObj(pObj);
+ pPlusHdl->SetPageView(pPV);
+ pPlusHdl->SetPlusHdl(true);
+ }
+ plusList.MoveTo(maHdlList);
+ }
+ }
+ }
+ }
+ }
+ }
+
+ // GluePoint handles
+ for (size_t nMarkNum=0; nMarkNum<nMarkCount; ++nMarkNum)
+ {
+ const SdrMark* pM=GetSdrMarkByIndex(nMarkNum);
+ SdrObject* pObj=pM->GetMarkedSdrObj();
+ const SdrGluePointList* pGPL=pObj->GetGluePointList();
+ if (!pGPL)
+ continue;
+
+ SdrPageView* pPV=pM->GetPageView();
+ const SdrUShortCont& rMrkGlue=pM->GetMarkedGluePoints();
+ for (sal_uInt16 nId : rMrkGlue)
+ {
+ //nNum changed to nNumGP because already used in for loop
+ sal_uInt16 nNumGP=pGPL->FindGluePoint(nId);
+ if (nNumGP!=SDRGLUEPOINT_NOTFOUND)
+ {
+ const SdrGluePoint& rGP=(*pGPL)[nNumGP];
+ Point aPos(rGP.GetAbsolutePos(*pObj));
+ std::unique_ptr<SdrHdl> pGlueHdl(new SdrHdl(aPos,SdrHdlKind::Glue));
+ pGlueHdl->SetObj(pObj);
+ pGlueHdl->SetPageView(pPV);
+ pGlueHdl->SetObjHdlNum(nId);
+ maHdlList.AddHdl(std::move(pGlueHdl));
+ }
+ }
+ }
+
+ // rotation point/axis of reflection
+ if(!bLimitedRotation)
+ {
+ AddDragModeHdl(meDragMode);
+ }
+
+ // sort handles
+ maHdlList.Sort();
+
+ // add custom handles (used by other apps, e.g. AnchorPos)
+ AddCustomHdl();
+
+ // moved it here to access all the handles for callback.
+ if (bTiledRendering && pViewShell)
+ {
+ SetMarkHandlesForLOKit(aRect, pOtherShell);
+ }
+
+ // try to restore focus handle index from remembered values
+ if(!bSaveOldFocus)
+ return;
+
+ for(size_t a = 0; a < maHdlList.GetHdlCount(); ++a)
+ {
+ SdrHdl* pCandidate = maHdlList.GetHdl(a);
+
+ if(pCandidate->GetObj()
+ && pCandidate->GetObj() == pSaveObj
+ && pCandidate->GetKind() == eSaveKind
+ && pCandidate->GetPolyNum() == nSavePolyNum
+ && pCandidate->GetPointNum() == nSavePointNum)
+ {
+ maHdlList.SetFocusHdl(pCandidate);
+ break;
+ }
+ }
+}
+
+void SdrMarkView::AddCustomHdl()
+{
+ // add custom handles (used by other apps, e.g. AnchorPos)
+}
+
+void SdrMarkView::SetDragMode(SdrDragMode eMode)
+{
+ SdrDragMode eMode0=meDragMode;
+ meDragMode=eMode;
+ if (meDragMode==SdrDragMode::Resize) meDragMode=SdrDragMode::Move;
+ if (meDragMode!=eMode0) {
+ ForceRefToMarked();
+ SetMarkHandles(nullptr);
+ {
+ if (AreObjectsMarked()) MarkListHasChanged();
+ }
+ }
+}
+
+void SdrMarkView::AddDragModeHdl(SdrDragMode eMode)
+{
+ switch(eMode)
+ {
+ case SdrDragMode::Rotate:
+ {
+ // add rotation center
+ maHdlList.AddHdl(std::make_unique<SdrHdl>(maRef1, SdrHdlKind::Ref1));
+ break;
+ }
+ case SdrDragMode::Mirror:
+ {
+ // add axis of reflection
+ std::unique_ptr<SdrHdl> pHdl3(new SdrHdl(maRef2, SdrHdlKind::Ref2));
+ std::unique_ptr<SdrHdl> pHdl2(new SdrHdl(maRef1, SdrHdlKind::Ref1));
+ std::unique_ptr<SdrHdl> pHdl1(new SdrHdlLine(*pHdl2, *pHdl3, SdrHdlKind::MirrorAxis));
+
+ pHdl1->SetObjHdlNum(1); // for sorting
+ pHdl2->SetObjHdlNum(2); // for sorting
+ pHdl3->SetObjHdlNum(3); // for sorting
+
+ maHdlList.AddHdl(std::move(pHdl1)); // line comes first, so it is the last in HitTest
+ maHdlList.AddHdl(std::move(pHdl2));
+ maHdlList.AddHdl(std::move(pHdl3));
+
+ break;
+ }
+ case SdrDragMode::Transparence:
+ {
+ // add interactive transparency handle
+ const size_t nMarkCount = GetMarkedObjectCount();
+ if(nMarkCount == 1)
+ {
+ SdrObject* pObj = GetMarkedObjectByIndex(0);
+ SdrModel* pModel = GetModel();
+ const SfxItemSet& rSet = pObj->GetMergedItemSet();
+
+ if(SfxItemState::SET != rSet.GetItemState(XATTR_FILLFLOATTRANSPARENCE, false))
+ {
+ // add this item, it's not yet there
+ XFillFloatTransparenceItem aNewItem(rSet.Get(XATTR_FILLFLOATTRANSPARENCE));
+ XGradient aGrad = aNewItem.GetGradientValue();
+
+ aNewItem.SetEnabled(true);
+ aGrad.SetStartIntens(100);
+ aGrad.SetEndIntens(100);
+ aNewItem.SetGradientValue(aGrad);
+
+ // add undo to allow user to take back this step
+ if( pModel->IsUndoEnabled() )
+ {
+ pModel->BegUndo(SvxResId(SIP_XA_FILLTRANSPARENCE));
+ pModel->AddUndo(pModel->GetSdrUndoFactory().CreateUndoAttrObject(*pObj));
+ pModel->EndUndo();
+ }
+
+ SfxItemSet aNewSet(pModel->GetItemPool());
+ aNewSet.Put(aNewItem);
+ pObj->SetMergedItemSetAndBroadcast(aNewSet);
+ }
+
+ // set values and transform to vector set
+ GradTransVector aGradTransVector;
+ GradTransGradient aGradTransGradient;
+
+ aGradTransGradient.aGradient = rSet.Get(XATTR_FILLFLOATTRANSPARENCE).GetGradientValue();
+ GradTransformer::GradToVec(aGradTransGradient, aGradTransVector, pObj);
+
+ // build handles
+ const Point aTmpPos1(basegfx::fround(aGradTransVector.maPositionA.getX()), basegfx::fround(aGradTransVector.maPositionA.getY()));
+ const Point aTmpPos2(basegfx::fround(aGradTransVector.maPositionB.getX()), basegfx::fround(aGradTransVector.maPositionB.getY()));
+ std::unique_ptr<SdrHdlColor> pColHdl1(new SdrHdlColor(aTmpPos1, aGradTransVector.aCol1, SDR_HANDLE_COLOR_SIZE_NORMAL, true));
+ std::unique_ptr<SdrHdlColor> pColHdl2(new SdrHdlColor(aTmpPos2, aGradTransVector.aCol2, SDR_HANDLE_COLOR_SIZE_NORMAL, true));
+ std::unique_ptr<SdrHdlGradient> pGradHdl(new SdrHdlGradient(aTmpPos1, aTmpPos2, false));
+ DBG_ASSERT(pColHdl1 && pColHdl2 && pGradHdl, "Could not get all necessary handles!");
+
+ // link them
+ pGradHdl->SetColorHandles(pColHdl1.get(), pColHdl2.get());
+ pGradHdl->SetObj(pObj);
+ pColHdl1->SetColorChangeHdl(LINK(pGradHdl.get(), SdrHdlGradient, ColorChangeHdl));
+ pColHdl2->SetColorChangeHdl(LINK(pGradHdl.get(), SdrHdlGradient, ColorChangeHdl));
+
+ // insert them
+ maHdlList.AddHdl(std::move(pColHdl1));
+ maHdlList.AddHdl(std::move(pColHdl2));
+ maHdlList.AddHdl(std::move(pGradHdl));
+ }
+ break;
+ }
+ case SdrDragMode::Gradient:
+ {
+ // add interactive gradient handle
+ const size_t nMarkCount = GetMarkedObjectCount();
+ if(nMarkCount == 1)
+ {
+ SdrObject* pObj = GetMarkedObjectByIndex(0);
+ const SfxItemSet& rSet = pObj->GetMergedItemSet();
+ drawing::FillStyle eFillStyle = rSet.Get(XATTR_FILLSTYLE).GetValue();
+
+ if(eFillStyle == drawing::FillStyle_GRADIENT)
+ {
+ // set values and transform to vector set
+ GradTransVector aGradTransVector;
+ GradTransGradient aGradTransGradient;
+ Size aHdlSize(15, 15);
+
+ aGradTransGradient.aGradient = rSet.Get(XATTR_FILLGRADIENT).GetGradientValue();
+ GradTransformer::GradToVec(aGradTransGradient, aGradTransVector, pObj);
+
+ // build handles
+ const Point aTmpPos1(basegfx::fround(aGradTransVector.maPositionA.getX()), basegfx::fround(aGradTransVector.maPositionA.getY()));
+ const Point aTmpPos2(basegfx::fround(aGradTransVector.maPositionB.getX()), basegfx::fround(aGradTransVector.maPositionB.getY()));
+ std::unique_ptr<SdrHdlColor> pColHdl1(new SdrHdlColor(aTmpPos1, aGradTransVector.aCol1, aHdlSize, false));
+ std::unique_ptr<SdrHdlColor> pColHdl2(new SdrHdlColor(aTmpPos2, aGradTransVector.aCol2, aHdlSize, false));
+ std::unique_ptr<SdrHdlGradient> pGradHdl(new SdrHdlGradient(aTmpPos1, aTmpPos2, true));
+ DBG_ASSERT(pColHdl1 && pColHdl2 && pGradHdl, "Could not get all necessary handles!");
+
+ // link them
+ pGradHdl->SetColorHandles(pColHdl1.get(), pColHdl2.get());
+ pGradHdl->SetObj(pObj);
+ pColHdl1->SetColorChangeHdl(LINK(pGradHdl.get(), SdrHdlGradient, ColorChangeHdl));
+ pColHdl2->SetColorChangeHdl(LINK(pGradHdl.get(), SdrHdlGradient, ColorChangeHdl));
+
+ // insert them
+ maHdlList.AddHdl(std::move(pColHdl1));
+ maHdlList.AddHdl(std::move(pColHdl2));
+ maHdlList.AddHdl(std::move(pGradHdl));
+ }
+ }
+ break;
+ }
+ case SdrDragMode::Crop:
+ {
+ // TODO
+ break;
+ }
+ default: break;
+ }
+}
+
+/** handle mouse over effects for handles */
+bool SdrMarkView::MouseMove(const MouseEvent& rMEvt, OutputDevice* pWin)
+{
+ if(maHdlList.GetHdlCount())
+ {
+ SdrHdl* pMouseOverHdl = nullptr;
+ if( !rMEvt.IsLeaveWindow() && pWin )
+ {
+ Point aMDPos( pWin->PixelToLogic( rMEvt.GetPosPixel() ) );
+ pMouseOverHdl = PickHandle(aMDPos);
+ }
+
+ // notify last mouse over handle that he lost the mouse
+ const size_t nHdlCount = maHdlList.GetHdlCount();
+
+ for(size_t nHdl = 0; nHdl < nHdlCount; ++nHdl)
+ {
+ SdrHdl* pCurrentHdl = GetHdl(nHdl);
+ if( pCurrentHdl->mbMouseOver )
+ {
+ if( pCurrentHdl != pMouseOverHdl )
+ {
+ pCurrentHdl->mbMouseOver = false;
+ pCurrentHdl->onMouseLeave();
+ }
+ break;
+ }
+ }
+
+ // notify current mouse over handle
+ if( pMouseOverHdl )
+ {
+ pMouseOverHdl->mbMouseOver = true;
+ pMouseOverHdl->onMouseEnter(rMEvt);
+ }
+ }
+ return SdrSnapView::MouseMove(rMEvt, pWin);
+}
+
+bool SdrMarkView::RequestHelp(const HelpEvent& rHEvt)
+{
+ if (maHdlList.GetHdlCount())
+ {
+ const size_t nHdlCount = maHdlList.GetHdlCount();
+
+ for (size_t nHdl = 0; nHdl < nHdlCount; ++nHdl)
+ {
+ SdrHdl* pCurrentHdl = GetHdl(nHdl);
+ if (pCurrentHdl->mbMouseOver)
+ {
+ pCurrentHdl->onHelpRequest();
+ return true;
+ }
+ }
+ }
+ return SdrSnapView::RequestHelp(rHEvt);
+}
+
+void SdrMarkView::ForceRefToMarked()
+{
+ switch(meDragMode)
+ {
+ case SdrDragMode::Rotate:
+ {
+ tools::Rectangle aR(GetMarkedObjRect());
+ maRef1 = aR.Center();
+
+ break;
+ }
+
+ case SdrDragMode::Mirror:
+ {
+ // first calculate the length of the axis of reflection
+ tools::Long nOutMin=0;
+ tools::Long nOutMax=0;
+ tools::Long nMinLen=0;
+ tools::Long nObjDst=0;
+ tools::Long nOutHgt=0;
+ OutputDevice* pOut=GetFirstOutputDevice();
+ if (pOut!=nullptr) {
+ // minimum length: 50 pixels
+ nMinLen=pOut->PixelToLogic(Size(0,50)).Height();
+ // 20 pixels distance to the Obj for the reference point
+ nObjDst=pOut->PixelToLogic(Size(0,20)).Height();
+ // MinY/MaxY
+ // margin = minimum length = 10 pixels
+ tools::Long nDst=pOut->PixelToLogic(Size(0,10)).Height();
+ nOutMin=-pOut->GetMapMode().GetOrigin().Y();
+ nOutMax=pOut->GetOutputSize().Height()-1+nOutMin;
+ nOutMin+=nDst;
+ nOutMax-=nDst;
+ // absolute minimum length, however, is 10 pixels
+ if (nOutMax-nOutMin<nDst) {
+ nOutMin+=nOutMax+1;
+ nOutMin/=2;
+ nOutMin-=(nDst+1)/2;
+ nOutMax=nOutMin+nDst;
+ }
+ nOutHgt=nOutMax-nOutMin;
+ // otherwise minimum length = 1/4 OutHgt
+ tools::Long nTemp=nOutHgt/4;
+ if (nTemp>nMinLen) nMinLen=nTemp;
+ }
+
+ tools::Rectangle aR(GetMarkedObjBoundRect());
+ Point aCenter(aR.Center());
+ tools::Long nMarkHgt=aR.GetHeight()-1;
+ tools::Long nHgt=nMarkHgt+nObjDst*2; // 20 pixels overlapping above and below
+ if (nHgt<nMinLen) nHgt=nMinLen; // minimum length 50 pixels or 1/4 OutHgt, respectively
+
+ tools::Long nY1=aCenter.Y()-(nHgt+1)/2;
+ tools::Long nY2=nY1+nHgt;
+
+ if (pOut!=nullptr && nMinLen>nOutHgt) nMinLen=nOutHgt; // TODO: maybe shorten this a little
+
+ if (pOut!=nullptr) { // now move completely into the visible area
+ if (nY1<nOutMin) {
+ nY1=nOutMin;
+ if (nY2<nY1+nMinLen) nY2=nY1+nMinLen;
+ }
+ if (nY2>nOutMax) {
+ nY2=nOutMax;
+ if (nY1>nY2-nMinLen) nY1=nY2-nMinLen;
+ }
+ }
+
+ maRef1.setX(aCenter.X() );
+ maRef1.setY(nY1 );
+ maRef2.setX(aCenter.X() );
+ maRef2.setY(nY2 );
+
+ break;
+ }
+
+ case SdrDragMode::Transparence:
+ case SdrDragMode::Gradient:
+ case SdrDragMode::Crop:
+ {
+ tools::Rectangle aRect(GetMarkedObjBoundRect());
+ maRef1 = aRect.TopLeft();
+ maRef2 = aRect.BottomRight();
+ break;
+ }
+ default: break;
+ }
+}
+
+void SdrMarkView::SetRef1(const Point& rPt)
+{
+ if(meDragMode == SdrDragMode::Rotate || meDragMode == SdrDragMode::Mirror)
+ {
+ maRef1 = rPt;
+ SdrHdl* pH = maHdlList.GetHdl(SdrHdlKind::Ref1);
+ if(pH)
+ pH->SetPos(rPt);
+ }
+}
+
+void SdrMarkView::SetRef2(const Point& rPt)
+{
+ if(meDragMode == SdrDragMode::Mirror)
+ {
+ maRef2 = rPt;
+ SdrHdl* pH = maHdlList.GetHdl(SdrHdlKind::Ref2);
+ if(pH)
+ pH->SetPos(rPt);
+ }
+}
+
+SfxViewShell* SdrMarkView::GetSfxViewShell() const
+{
+ return SfxViewShell::Current();
+}
+
+void SdrMarkView::CheckMarked()
+{
+ for (size_t nm=GetMarkedObjectCount(); nm>0;) {
+ --nm;
+ SdrMark* pM = GetSdrMarkByIndex(nm);
+ SdrObject* pObj = pM->GetMarkedSdrObj();
+ SdrPageView* pPV = pM->GetPageView();
+ bool bRaus = !pObj || !pPV->IsObjMarkable(pObj);
+ if (bRaus)
+ {
+ GetMarkedObjectListWriteAccess().DeleteMark(nm);
+ }
+ else
+ {
+ if (!IsGluePointEditMode()) { // selected gluepoints only in GlueEditMode
+ SdrUShortCont& rPts = pM->GetMarkedGluePoints();
+ rPts.clear();
+ }
+ }
+ }
+
+ // at least reset the remembered BoundRect to prevent handle
+ // generation if bForceFrameHandles is TRUE.
+ mbMarkedObjRectDirty = true;
+}
+
+void SdrMarkView::SetMarkRects()
+{
+ SdrPageView* pPV = GetSdrPageView();
+
+ if(pPV)
+ {
+ pPV->SetHasMarkedObj(GetMarkedObjectList().TakeSnapRect(pPV, pPV->MarkSnap()));
+ GetMarkedObjectList().TakeBoundRect(pPV, pPV->MarkBound());
+ }
+}
+
+void SdrMarkView::SetFrameHandles(bool bOn)
+{
+ if (bOn!=mbForceFrameHandles) {
+ bool bOld=ImpIsFrameHandles();
+ mbForceFrameHandles=bOn;
+ bool bNew=ImpIsFrameHandles();
+ if (bNew!=bOld) {
+ AdjustMarkHdl();
+ MarkListHasChanged();
+ }
+ }
+}
+
+void SdrMarkView::SetEditMode(SdrViewEditMode eMode)
+{
+ if (eMode==meEditMode) return;
+
+ bool bGlue0=meEditMode==SdrViewEditMode::GluePointEdit;
+ bool bEdge0=static_cast<SdrCreateView*>(this)->IsEdgeTool();
+ meEditMode0=meEditMode;
+ meEditMode=eMode;
+ bool bGlue1=meEditMode==SdrViewEditMode::GluePointEdit;
+ bool bEdge1=static_cast<SdrCreateView*>(this)->IsEdgeTool();
+ // avoid flickering when switching between GlueEdit and EdgeTool
+ if (bGlue1 && !bGlue0) ImpSetGlueVisible2(bGlue1);
+ if (bEdge1!=bEdge0) ImpSetGlueVisible3(bEdge1);
+ if (!bGlue1 && bGlue0) ImpSetGlueVisible2(bGlue1);
+ if (bGlue0 && !bGlue1) UnmarkAllGluePoints();
+}
+
+
+bool SdrMarkView::IsObjMarkable(SdrObject const * pObj, SdrPageView const * pPV) const
+{
+ if (pObj)
+ {
+ if (pObj->IsMarkProtect() ||
+ (!mbDesignMode && pObj->IsUnoObj()))
+ {
+ // object not selectable or
+ // SdrUnoObj not in DesignMode
+ return false;
+ }
+ }
+ return pPV==nullptr || pPV->IsObjMarkable(pObj);
+}
+
+bool SdrMarkView::IsMarkedObjHit(const Point& rPnt, short nTol) const
+{
+ bool bRet=false;
+ nTol=ImpGetHitTolLogic(nTol,nullptr);
+ for (size_t nm=0; nm<GetMarkedObjectCount() && !bRet; ++nm) {
+ SdrMark* pM=GetSdrMarkByIndex(nm);
+ bRet = nullptr != CheckSingleSdrObjectHit(rPnt,sal_uInt16(nTol),pM->GetMarkedSdrObj(),pM->GetPageView(),SdrSearchOptions::NONE,nullptr);
+ }
+ return bRet;
+}
+
+SdrHdl* SdrMarkView::PickHandle(const Point& rPnt) const
+{
+ if (mbSomeObjChgdFlag) { // recalculate handles, if necessary
+ FlushComeBackTimer();
+ }
+ return maHdlList.IsHdlListHit(rPnt);
+}
+
+bool SdrMarkView::MarkObj(const Point& rPnt, short nTol, bool bToggle, bool bDeep)
+{
+ SdrPageView* pPV;
+ nTol=ImpGetHitTolLogic(nTol,nullptr);
+ SdrSearchOptions nOptions=SdrSearchOptions::PICKMARKABLE;
+ if (bDeep) nOptions=nOptions|SdrSearchOptions::DEEP;
+ SdrObject* pObj = PickObj(rPnt, static_cast<sal_uInt16>(nTol), pPV, nOptions);
+ if (pObj) {
+ bool bUnmark=bToggle && IsObjMarked(pObj);
+ MarkObj(pObj,pPV,bUnmark);
+ }
+ return pObj != nullptr;
+}
+
+bool SdrMarkView::MarkNextObj(bool bPrev)
+{
+ SdrPageView* pPageView = GetSdrPageView();
+
+ if(!pPageView)
+ {
+ return false;
+ }
+
+ SortMarkedObjects();
+ const size_t nMarkCount=GetMarkedObjectCount();
+ size_t nChgMarkNum = SAL_MAX_SIZE; // number of the MarkEntry we want to replace
+ size_t nSearchObjNum = bPrev ? 0 : SAL_MAX_SIZE;
+ if (nMarkCount!=0) {
+ nChgMarkNum=bPrev ? 0 : nMarkCount-1;
+ SdrMark* pM=GetSdrMarkByIndex(nChgMarkNum);
+ OSL_ASSERT(pM!=nullptr);
+ if (pM->GetMarkedSdrObj() != nullptr)
+ nSearchObjNum = pM->GetMarkedSdrObj()->GetNavigationPosition();
+ }
+
+ SdrObject* pMarkObj=nullptr;
+ SdrObjList* pSearchObjList=pPageView->GetObjList();
+ const size_t nObjCount = pSearchObjList->GetObjCount();
+ if (nObjCount!=0) {
+ if (nSearchObjNum>nObjCount) nSearchObjNum=nObjCount;
+ while (pMarkObj==nullptr && ((!bPrev && nSearchObjNum>0) || (bPrev && nSearchObjNum<nObjCount)))
+ {
+ if (!bPrev)
+ nSearchObjNum--;
+ SdrObject* pSearchObj = pSearchObjList->GetObjectForNavigationPosition(nSearchObjNum);
+ if (IsObjMarkable(pSearchObj,pPageView))
+ {
+ if (TryToFindMarkedObject(pSearchObj)==SAL_MAX_SIZE)
+ {
+ pMarkObj=pSearchObj;
+ }
+ }
+ if (bPrev) nSearchObjNum++;
+ }
+ }
+
+ if(!pMarkObj)
+ {
+ return false;
+ }
+
+ if (nChgMarkNum!=SAL_MAX_SIZE)
+ {
+ GetMarkedObjectListWriteAccess().DeleteMark(nChgMarkNum);
+ }
+ MarkObj(pMarkObj,pPageView); // also calls MarkListHasChanged(), AdjustMarkHdl()
+ return true;
+}
+
+bool SdrMarkView::MarkNextObj(const Point& rPnt, short nTol, bool bPrev)
+{
+ SortMarkedObjects();
+ nTol=ImpGetHitTolLogic(nTol,nullptr);
+ SdrMark* pTopMarkHit=nullptr;
+ SdrMark* pBtmMarkHit=nullptr;
+ size_t nTopMarkHit=0;
+ size_t nBtmMarkHit=0;
+ // find topmost of the selected objects that is hit by rPnt
+ const size_t nMarkCount=GetMarkedObjectCount();
+ for (size_t nm=nMarkCount; nm>0 && pTopMarkHit==nullptr;) {
+ --nm;
+ SdrMark* pM=GetSdrMarkByIndex(nm);
+ if(CheckSingleSdrObjectHit(rPnt,sal_uInt16(nTol),pM->GetMarkedSdrObj(),pM->GetPageView(),SdrSearchOptions::NONE,nullptr))
+ {
+ pTopMarkHit=pM;
+ nTopMarkHit=nm;
+ }
+ }
+ // nothing found, in this case, just select an object
+ if (pTopMarkHit==nullptr) return MarkObj(rPnt,sal_uInt16(nTol));
+
+ SdrObject* pTopObjHit=pTopMarkHit->GetMarkedSdrObj();
+ SdrObjList* pObjList=pTopObjHit->getParentSdrObjListFromSdrObject();
+ SdrPageView* pPV=pTopMarkHit->GetPageView();
+ // find lowermost of the selected objects that is hit by rPnt
+ // and is placed on the same PageView as pTopMarkHit
+ for (size_t nm=0; nm<nMarkCount && pBtmMarkHit==nullptr; ++nm) {
+ SdrMark* pM=GetSdrMarkByIndex(nm);
+ SdrPageView* pPV2=pM->GetPageView();
+ if (pPV2==pPV && CheckSingleSdrObjectHit(rPnt,sal_uInt16(nTol),pM->GetMarkedSdrObj(),pPV2,SdrSearchOptions::NONE,nullptr))
+ {
+ pBtmMarkHit=pM;
+ nBtmMarkHit=nm;
+ }
+ }
+ if (pBtmMarkHit==nullptr) { pBtmMarkHit=pTopMarkHit; nBtmMarkHit=nTopMarkHit; }
+ SdrObject* pBtmObjHit=pBtmMarkHit->GetMarkedSdrObj();
+ const size_t nObjCount = pObjList->GetObjCount();
+
+ size_t nSearchBeg(0);
+ E3dScene* pScene(nullptr);
+ SdrObject* pObjHit(bPrev ? pBtmObjHit : pTopObjHit);
+ bool bRemap =
+ nullptr != dynamic_cast< const E3dCompoundObject* >(pObjHit);
+ if (bRemap)
+ {
+ pScene = dynamic_cast< E3dScene* >(pObjHit->getParentSdrObjectFromSdrObject());
+ bRemap = nullptr != pScene;
+ }
+
+ if(bPrev)
+ {
+ sal_uInt32 nOrdNumBtm(pBtmObjHit->GetOrdNum());
+
+ if(bRemap)
+ {
+ nOrdNumBtm = pScene->RemapOrdNum(nOrdNumBtm);
+ }
+
+ nSearchBeg = nOrdNumBtm + 1;
+ }
+ else
+ {
+ sal_uInt32 nOrdNumTop(pTopObjHit->GetOrdNum());
+
+ if(bRemap)
+ {
+ nOrdNumTop = pScene->RemapOrdNum(nOrdNumTop);
+ }
+
+ nSearchBeg = nOrdNumTop;
+ }
+
+ size_t no=nSearchBeg;
+ SdrObject* pFndObj=nullptr;
+ while (pFndObj==nullptr && ((!bPrev && no>0) || (bPrev && no<nObjCount))) {
+ if (!bPrev) no--;
+ SdrObject* pObj;
+
+ if(bRemap)
+ {
+ pObj = pObjList->GetObj(pScene->RemapOrdNum(no));
+ }
+ else
+ {
+ pObj = pObjList->GetObj(no);
+ }
+
+ if (CheckSingleSdrObjectHit(rPnt,sal_uInt16(nTol),pObj,pPV,SdrSearchOptions::TESTMARKABLE,nullptr))
+ {
+ if (TryToFindMarkedObject(pObj)==SAL_MAX_SIZE) {
+ pFndObj=pObj;
+ } else {
+ // TODO: for performance reasons set on to Top or Btm, if necessary
+ }
+ }
+ if (bPrev) no++;
+ }
+ if (pFndObj!=nullptr)
+ {
+ GetMarkedObjectListWriteAccess().DeleteMark(bPrev?nBtmMarkHit:nTopMarkHit);
+ GetMarkedObjectListWriteAccess().InsertEntry(SdrMark(pFndObj,pPV));
+ MarkListHasChanged();
+ AdjustMarkHdl();
+ }
+ return pFndObj!=nullptr;
+}
+
+void SdrMarkView::MarkObj(const tools::Rectangle& rRect, bool bUnmark)
+{
+ bool bFnd=false;
+ tools::Rectangle aR(rRect);
+ SdrObjList* pObjList;
+ BrkAction();
+ SdrPageView* pPV = GetSdrPageView();
+
+ if(pPV)
+ {
+ pObjList=pPV->GetObjList();
+ tools::Rectangle aFrm1(aR);
+ const size_t nObjCount = pObjList->GetObjCount();
+ for (size_t nO=0; nO<nObjCount; ++nO) {
+ SdrObject* pObj=pObjList->GetObj(nO);
+ tools::Rectangle aRect(pObj->GetCurrentBoundRect());
+ if (aFrm1.Contains(aRect)) {
+ if (!bUnmark) {
+ if (IsObjMarkable(pObj,pPV))
+ {
+ GetMarkedObjectListWriteAccess().InsertEntry(SdrMark(pObj,pPV));
+ bFnd=true;
+ }
+ } else {
+ const size_t nPos=TryToFindMarkedObject(pObj);
+ if (nPos!=SAL_MAX_SIZE)
+ {
+ GetMarkedObjectListWriteAccess().DeleteMark(nPos);
+ bFnd=true;
+ }
+ }
+ }
+ }
+ }
+ if (bFnd) {
+ SortMarkedObjects();
+ MarkListHasChanged();
+ AdjustMarkHdl();
+ }
+}
+
+namespace {
+
+void collectUIInformation(const SdrObject* pObj)
+{
+ EventDescription aDescription;
+ aDescription.aAction = "SELECT";
+ aDescription.aParent = "MainWindow";
+ aDescription.aKeyWord = "CurrentApp";
+
+ if (!pObj->GetName().isEmpty())
+ aDescription.aParameters = {{"OBJECT", pObj->GetName()}};
+ else
+ aDescription.aParameters = {{"OBJECT", "Unnamed_Obj_" + OUString::number(pObj->GetOrdNum())}};
+
+ UITestLogger::getInstance().logEvent(aDescription);
+}
+
+}
+
+ void SdrMarkView::MarkObj(SdrObject* pObj, SdrPageView* pPV, bool bUnmark, bool bDoNoSetMarkHdl,
+ std::vector<basegfx::B2DRectangle> && rSubSelections)
+{
+ if (!(pObj!=nullptr && pPV!=nullptr && IsObjMarkable(pObj, pPV)))
+ return;
+
+ BrkAction();
+ if (!bUnmark)
+ {
+ GetMarkedObjectListWriteAccess().InsertEntry(SdrMark(pObj,pPV));
+ collectUIInformation(pObj);
+ }
+ else
+ {
+ const size_t nPos=TryToFindMarkedObject(pObj);
+ if (nPos!=SAL_MAX_SIZE)
+ {
+ GetMarkedObjectListWriteAccess().DeleteMark(nPos);
+ }
+ }
+
+ maSubSelectionList = std::move(rSubSelections);
+
+ if (!bDoNoSetMarkHdl) {
+ MarkListHasChanged();
+ AdjustMarkHdl();
+ }
+}
+
+bool SdrMarkView::IsObjMarked(SdrObject const * pObj) const
+{
+ return TryToFindMarkedObject(pObj)!=SAL_MAX_SIZE;
+}
+
+sal_uInt16 SdrMarkView::GetMarkHdlSizePixel() const
+{
+ return maHdlList.GetHdlSize()*2+1;
+}
+
+void SdrMarkView::SetMarkHdlSizePixel(sal_uInt16 nSiz)
+{
+ if (nSiz<3) nSiz=3;
+ nSiz/=2;
+ if (nSiz!=maHdlList.GetHdlSize()) {
+ maHdlList.SetHdlSize(nSiz);
+ }
+}
+
+bool SdrMarkView::getPossibleGridOffsetForSdrObject(
+ basegfx::B2DVector& rOffset,
+ const SdrObject* pObj,
+ const SdrPageView* pPV) const
+{
+ if(nullptr == pObj || nullptr == pPV)
+ {
+ return false;
+ }
+
+ const OutputDevice* pOutputDevice(GetFirstOutputDevice());
+
+ if(nullptr == pOutputDevice)
+ {
+ return false;
+ }
+
+ const SdrPageWindow* pSdrPageWindow(pPV->FindPageWindow(*pOutputDevice));
+
+ if(nullptr == pSdrPageWindow)
+ {
+ return false;
+ }
+
+ const sdr::contact::ObjectContact& rObjectContact(pSdrPageWindow->GetObjectContact());
+
+ if(!rObjectContact.supportsGridOffsets())
+ {
+ return false;
+ }
+
+ const sdr::contact::ViewObjectContact& rVOC(pObj->GetViewContact().GetViewObjectContact(
+ const_cast<sdr::contact::ObjectContact&>(rObjectContact)));
+
+ rOffset = rVOC.getGridOffset();
+
+ return !rOffset.equalZero();
+}
+
+bool SdrMarkView::getPossibleGridOffsetForPosition(
+ basegfx::B2DVector& rOffset,
+ const basegfx::B2DPoint& rPoint,
+ const SdrPageView* pPV) const
+{
+ if(nullptr == pPV)
+ {
+ return false;
+ }
+
+ const OutputDevice* pOutputDevice(GetFirstOutputDevice());
+
+ if(nullptr == pOutputDevice)
+ {
+ return false;
+ }
+
+ const SdrPageWindow* pSdrPageWindow(pPV->FindPageWindow(*pOutputDevice));
+
+ if(nullptr == pSdrPageWindow)
+ {
+ return false;
+ }
+
+ const sdr::contact::ObjectContact& rObjectContact(pSdrPageWindow->GetObjectContact());
+
+ if(!rObjectContact.supportsGridOffsets())
+ {
+ return false;
+ }
+
+ rObjectContact.calculateGridOffsetForB2DRange(rOffset, basegfx::B2DRange(rPoint));
+
+ return !rOffset.equalZero();
+}
+
+SdrObject* SdrMarkView::CheckSingleSdrObjectHit(const Point& rPnt, sal_uInt16 nTol, SdrObject* pObj, SdrPageView* pPV, SdrSearchOptions nOptions, const SdrLayerIDSet* pMVisLay) const
+{
+ if(((nOptions & SdrSearchOptions::IMPISMASTER) && pObj->IsNotVisibleAsMaster()) || (!pObj->IsVisible()))
+ {
+ return nullptr;
+ }
+
+ const bool bCheckIfMarkable(nOptions & SdrSearchOptions::TESTMARKABLE);
+ const bool bDeep(nOptions & SdrSearchOptions::DEEP);
+ const bool bOLE(dynamic_cast< const SdrOle2Obj* >(pObj) != nullptr);
+ auto pTextObj = dynamic_cast<const SdrTextObj*>( pObj);
+ const bool bTXT(pTextObj && pTextObj->IsTextFrame());
+ SdrObject* pRet=nullptr;
+ tools::Rectangle aRect(pObj->GetCurrentBoundRect());
+
+ // add possible GridOffset to up-to-now view-independent BoundRect data
+ basegfx::B2DVector aGridOffset(0.0, 0.0);
+ if(getPossibleGridOffsetForSdrObject(aGridOffset, pObj, pPV))
+ {
+ aRect += Point(
+ basegfx::fround(aGridOffset.getX()),
+ basegfx::fround(aGridOffset.getY()));
+ }
+
+ sal_uInt16 nTol2(nTol);
+
+ // double tolerance for OLE, text frames and objects in
+ // active text edit
+ if(bOLE || bTXT || pObj==static_cast<const SdrObjEditView*>(this)->GetTextEditObject())
+ {
+ nTol2*=2;
+ }
+
+ aRect.AdjustLeft( -nTol2 ); // add 1 tolerance for all objects
+ aRect.AdjustTop( -nTol2 );
+ aRect.AdjustRight(nTol2 );
+ aRect.AdjustBottom(nTol2 );
+
+ if (aRect.Contains(rPnt))
+ {
+ if (!bCheckIfMarkable || IsObjMarkable(pObj,pPV))
+ {
+ SdrObjList* pOL=pObj->GetSubList();
+
+ if (pOL!=nullptr && pOL->GetObjCount()!=0)
+ {
+ SdrObject* pTmpObj;
+ // adjustment hit point for virtual objects
+ Point aPnt( rPnt );
+
+ if ( auto pVirtObj = dynamic_cast<const SdrVirtObj*>( pObj) )
+ {
+ Point aOffset = pVirtObj->GetOffset();
+ aPnt.Move( -aOffset.X(), -aOffset.Y() );
+ }
+
+ pRet=CheckSingleSdrObjectHit(aPnt,nTol,pOL,pPV,nOptions,pMVisLay,pTmpObj);
+ }
+ else
+ {
+ if(!pMVisLay || pMVisLay->IsSet(pObj->GetLayer()))
+ {
+ pRet = SdrObjectPrimitiveHit(*pObj, rPnt, nTol2, *pPV, &pPV->GetVisibleLayers(), false);
+ }
+ }
+ }
+ }
+
+ if (!bDeep && pRet!=nullptr)
+ {
+ pRet=pObj;
+ }
+
+ return pRet;
+}
+
+SdrObject* SdrMarkView::CheckSingleSdrObjectHit(const Point& rPnt, sal_uInt16 nTol, SdrObjList const * pOL, SdrPageView* pPV, SdrSearchOptions nOptions, const SdrLayerIDSet* pMVisLay, SdrObject*& rpRootObj) const
+{
+ return (*this).CheckSingleSdrObjectHit(rPnt,nTol,pOL,pPV,nOptions,pMVisLay,rpRootObj,nullptr);
+}
+SdrObject* SdrMarkView::CheckSingleSdrObjectHit(const Point& rPnt, sal_uInt16 nTol, SdrObjList const * pOL, SdrPageView* pPV, SdrSearchOptions nOptions, const SdrLayerIDSet* pMVisLay, SdrObject*& rpRootObj,const SdrMarkList * pMarkList) const
+{
+ SdrObject* pRet=nullptr;
+ rpRootObj=nullptr;
+ if (pOL!=nullptr)
+ {
+ const bool bRemap(
+ nullptr != pOL->getSdrObjectFromSdrObjList()
+ && nullptr != dynamic_cast< const E3dScene* >(pOL->getSdrObjectFromSdrObjList()));
+ const E3dScene* pRemapScene(bRemap ? static_cast< E3dScene* >(pOL->getSdrObjectFromSdrObjList()) : nullptr);
+ const size_t nObjCount(pOL->GetObjCount());
+ size_t nObjNum(nObjCount);
+
+ while (pRet==nullptr && nObjNum>0)
+ {
+ nObjNum--;
+ SdrObject* pObj;
+
+ if(bRemap)
+ {
+ pObj = pOL->GetObj(pRemapScene->RemapOrdNum(nObjNum));
+ }
+ else
+ {
+ pObj = pOL->GetObj(nObjNum);
+ }
+ if (nOptions & SdrSearchOptions::BEFOREMARK)
+ {
+ if (pMarkList!=nullptr)
+ {
+ if ((*pMarkList).FindObject(pObj)!=SAL_MAX_SIZE)
+ {
+ return nullptr;
+ }
+ }
+ }
+ pRet=CheckSingleSdrObjectHit(rPnt,nTol,pObj,pPV,nOptions,pMVisLay);
+ if (pRet!=nullptr) rpRootObj=pObj;
+ }
+ }
+ return pRet;
+}
+
+SdrObject* SdrMarkView::PickObj(const Point& rPnt, short nTol, SdrPageView*& rpPV, SdrSearchOptions nOptions) const
+{
+ return PickObj(rPnt, nTol, rpPV, nOptions, nullptr);
+}
+
+SdrObject* SdrMarkView::PickObj(const Point& rPnt, short nTol, SdrPageView*& rpPV, SdrSearchOptions nOptions, SdrObject** ppRootObj, bool* pbHitPassDirect) const
+{ // TODO: lacks a Pass2,Pass3
+ SortMarkedObjects();
+ if (ppRootObj!=nullptr) *ppRootObj=nullptr;
+ if (pbHitPassDirect!=nullptr) *pbHitPassDirect=true;
+ SdrObject* pRet = nullptr;
+ rpPV=nullptr;
+ bool bMarked(nOptions & SdrSearchOptions::MARKED);
+ bool bMasters=!bMarked && bool(nOptions & SdrSearchOptions::ALSOONMASTER);
+ // nOptions & SdrSearchOptions::NEXT: n.i.
+ // nOptions & SdrSearchOptions::PASS2BOUND: n.i.
+ // nOptions & SdrSearchOptions::PASS3NEAREST// n.i.
+ if (nTol<0) nTol=ImpGetHitTolLogic(nTol,nullptr);
+ SdrObject* pObj=nullptr;
+ SdrObject* pHitObj=nullptr;
+ SdrPageView* pPV=nullptr;
+ if (static_cast<const SdrObjEditView*>(this)->IsTextEditFrameHit(rPnt)) {
+ pObj=static_cast<const SdrObjEditView*>(this)->GetTextEditObject();
+ pHitObj=pObj;
+ pPV=static_cast<const SdrObjEditView*>(this)->GetTextEditPageView();
+ }
+ if (bMarked) {
+ const size_t nMrkCnt=GetMarkedObjectCount();
+ size_t nMrkNum=nMrkCnt;
+ while (pHitObj==nullptr && nMrkNum>0) {
+ nMrkNum--;
+ SdrMark* pM=GetSdrMarkByIndex(nMrkNum);
+ pObj=pM->GetMarkedSdrObj();
+ pPV=pM->GetPageView();
+ pHitObj=CheckSingleSdrObjectHit(rPnt,nTol,pObj,pPV,nOptions,nullptr);
+ }
+ }
+ else
+ {
+ pPV = GetSdrPageView();
+
+ if(pPV)
+ {
+ SdrPage* pPage=pPV->GetPage();
+ sal_uInt16 nPgCount=1;
+
+ if(bMasters && pPage->TRG_HasMasterPage())
+ {
+ nPgCount++;
+ }
+ bool bWholePage(nOptions & SdrSearchOptions::WHOLEPAGE);
+ bool bExtraPassForWholePage=bWholePage && pPage!=pPV->GetObjList();
+ if (bExtraPassForWholePage) nPgCount++; // First search in AktObjList, then on the entire page
+ sal_uInt16 nPgNum=nPgCount;
+ while (pHitObj==nullptr && nPgNum>0) {
+ SdrSearchOptions nTmpOptions=nOptions;
+ nPgNum--;
+ const SdrLayerIDSet* pMVisLay=nullptr;
+ SdrObjList* pObjList=nullptr;
+ if (pbHitPassDirect!=nullptr) *pbHitPassDirect = true;
+ if (nPgNum>=nPgCount-1 || (bExtraPassForWholePage && nPgNum>=nPgCount-2))
+ {
+ pObjList=pPV->GetObjList();
+ if (bExtraPassForWholePage && nPgNum==nPgCount-2) {
+ pObjList=pPage;
+ if (pbHitPassDirect!=nullptr) *pbHitPassDirect = false;
+ }
+ }
+ else
+ {
+ // otherwise MasterPage
+ SdrPage& rMasterPage = pPage->TRG_GetMasterPage();
+ pMVisLay = &pPage->TRG_GetMasterPageVisibleLayers();
+ pObjList = &rMasterPage;
+
+ if (pbHitPassDirect!=nullptr) *pbHitPassDirect = false;
+ nTmpOptions=nTmpOptions | SdrSearchOptions::IMPISMASTER;
+ }
+ pHitObj=CheckSingleSdrObjectHit(rPnt,nTol,pObjList,pPV,nTmpOptions,pMVisLay,pObj,&(GetMarkedObjectList()));
+ }
+ }
+ }
+ if (pHitObj!=nullptr) {
+ if (ppRootObj!=nullptr) *ppRootObj=pObj;
+ if (nOptions & SdrSearchOptions::DEEP) pObj=pHitObj;
+ if (nOptions & SdrSearchOptions::TESTTEXTEDIT) {
+ if (!pObj->HasTextEdit() || pPV->GetLockedLayers().IsSet(pObj->GetLayer())) {
+ pObj=nullptr;
+ }
+ }
+ if (pObj!=nullptr && (nOptions & SdrSearchOptions::TESTMACRO)) {
+ SdrObjMacroHitRec aHitRec;
+ aHitRec.aPos=rPnt;
+ aHitRec.nTol=nTol;
+ aHitRec.pVisiLayer=&pPV->GetVisibleLayers();
+ aHitRec.pPageView=pPV;
+ if (!pObj->HasMacro() || !pObj->IsMacroHit(aHitRec)) pObj=nullptr;
+ }
+ if (pObj!=nullptr) {
+ pRet=pObj;
+ rpPV=pPV;
+ }
+ }
+ return pRet;
+}
+
+bool SdrMarkView::PickMarkedObj(const Point& rPnt, SdrObject*& rpObj, SdrPageView*& rpPV, SdrSearchOptions nOptions) const
+{
+ SortMarkedObjects();
+ const bool bBoundCheckOn2ndPass(nOptions & SdrSearchOptions::PASS2BOUND);
+ rpObj=nullptr;
+ rpPV=nullptr;
+ const size_t nMarkCount=GetMarkedObjectCount();
+ for (size_t nMarkNum=nMarkCount; nMarkNum>0;) {
+ --nMarkNum;
+ SdrMark* pM=GetSdrMarkByIndex(nMarkNum);
+ SdrPageView* pPV=pM->GetPageView();
+ SdrObject* pObj=pM->GetMarkedSdrObj();
+ if (CheckSingleSdrObjectHit(rPnt,mnHitTolLog,pObj,pPV,SdrSearchOptions::TESTMARKABLE,nullptr)) {
+ rpObj=pObj;
+ rpPV=pPV;
+ return true;
+ }
+ }
+ if (bBoundCheckOn2ndPass) {
+ for (size_t nMarkNum=nMarkCount; nMarkNum>0;) {
+ --nMarkNum;
+ SdrMark* pM=GetSdrMarkByIndex(nMarkNum);
+ SdrPageView* pPV=pM->GetPageView();
+ SdrObject* pObj=pM->GetMarkedSdrObj();
+ tools::Rectangle aRect(pObj->GetCurrentBoundRect());
+ aRect.AdjustLeft( -mnHitTolLog );
+ aRect.AdjustTop( -mnHitTolLog );
+ aRect.AdjustRight(mnHitTolLog );
+ aRect.AdjustBottom(mnHitTolLog );
+ if (aRect.Contains(rPnt)) {
+ rpObj=pObj;
+ rpPV=pPV;
+ return true;
+ }
+ }
+ }
+ return false;
+}
+
+
+void SdrMarkView::UnmarkAllObj(SdrPageView const * pPV)
+{
+ if (GetMarkedObjectCount()==0)
+ return;
+
+ BrkAction();
+ if (pPV!=nullptr)
+ {
+ GetMarkedObjectListWriteAccess().DeletePageView(*pPV);
+ }
+ else
+ {
+ GetMarkedObjectListWriteAccess().Clear();
+ }
+ mpMarkedObj=nullptr;
+ mpMarkedPV=nullptr;
+ MarkListHasChanged();
+ AdjustMarkHdl();
+}
+
+void SdrMarkView::MarkAllObj(SdrPageView* pPV)
+{
+ BrkAction();
+
+ if(!pPV)
+ {
+ pPV = GetSdrPageView();
+ }
+
+ // #i69171# pPV may still be NULL if there is no SDrPageView (!), e.g. when inserting
+ // other files
+ if(pPV)
+ {
+ const bool bMarkChg(GetMarkedObjectListWriteAccess().InsertPageView(*pPV));
+
+ if(bMarkChg)
+ {
+ MarkListHasChanged();
+ }
+ }
+
+ if(GetMarkedObjectCount())
+ {
+ AdjustMarkHdl();
+ }
+}
+
+void SdrMarkView::AdjustMarkHdl(SfxViewShell* pOtherShell)
+{
+ CheckMarked();
+ SetMarkRects();
+ SetMarkHandles(pOtherShell);
+}
+
+// BoundRect in model coordinates, no GridOffset added
+tools::Rectangle SdrMarkView::GetMarkedObjBoundRect() const
+{
+ tools::Rectangle aRect;
+ for (size_t nm=0; nm<GetMarkedObjectCount(); ++nm) {
+ SdrMark* pM=GetSdrMarkByIndex(nm);
+ SdrObject* pO=pM->GetMarkedSdrObj();
+ tools::Rectangle aR1(pO->GetCurrentBoundRect());
+ if (aRect.IsEmpty()) aRect=aR1;
+ else aRect.Union(aR1);
+ }
+ return aRect;
+}
+
+// ObjRect in model coordinates, no GridOffset added
+const tools::Rectangle& SdrMarkView::GetMarkedObjRect() const
+{
+ if (mbMarkedObjRectDirty) {
+ const_cast<SdrMarkView*>(this)->mbMarkedObjRectDirty=false;
+ tools::Rectangle aRect;
+ for (size_t nm=0; nm<GetMarkedObjectCount(); ++nm) {
+ SdrMark* pM=GetSdrMarkByIndex(nm);
+ SdrObject* pO = pM->GetMarkedSdrObj();
+ if (!pO)
+ continue;
+ tools::Rectangle aR1(pO->GetSnapRect());
+ if (aRect.IsEmpty()) aRect=aR1;
+ else aRect.Union(aR1);
+ }
+ const_cast<SdrMarkView*>(this)->maMarkedObjRect=aRect;
+ }
+ return maMarkedObjRect;
+}
+
+
+OUString SdrMarkView::ImpGetDescriptionString(TranslateId pStrCacheID, ImpGetDescriptionOptions nOpt) const
+{
+ OUString sStr = SvxResId(pStrCacheID);
+ const sal_Int32 nPos = sStr.indexOf("%1");
+
+ if(nPos != -1)
+ {
+ if(nOpt == ImpGetDescriptionOptions::POINTS)
+ {
+ sStr = sStr.replaceAt(nPos, 2, GetDescriptionOfMarkedPoints());
+ }
+ else if(nOpt == ImpGetDescriptionOptions::GLUEPOINTS)
+ {
+ sStr = sStr.replaceAt(nPos, 2, GetDescriptionOfMarkedGluePoints());
+ }
+ else
+ {
+ sStr = sStr.replaceAt(nPos, 2, GetDescriptionOfMarkedObjects());
+ }
+ }
+
+ return sStr.replaceFirst("%2", "0");
+}
+
+
+void SdrMarkView::EnterMarkedGroup()
+{
+ // We enter only the first group found (in only one PageView), because
+ // PageView::EnterGroup calls an AdjustMarkHdl.
+ // TODO: I'll have to prevent that via a flag.
+ SdrPageView* pPV = GetSdrPageView();
+
+ if(!pPV)
+ return;
+
+ bool bEnter=false;
+ for (size_t nm = GetMarkedObjectCount(); nm > 0 && !bEnter;)
+ {
+ --nm;
+ SdrMark* pM=GetSdrMarkByIndex(nm);
+ if (pM->GetPageView()==pPV) {
+ SdrObject* pObj=pM->GetMarkedSdrObj();
+ if (pObj->IsGroupObject()) {
+ if (pPV->EnterGroup(pObj)) {
+ bEnter=true;
+ }
+ }
+ }
+ }
+}
+
+
+void SdrMarkView::MarkListHasChanged()
+{
+ GetMarkedObjectListWriteAccess().SetNameDirty();
+ maSdrViewSelection.SetEdgesOfMarkedNodesDirty();
+
+ mbMarkedObjRectDirty=true;
+ mbMarkedPointsRectsDirty=true;
+ bool bOneEdgeMarked=false;
+ if (GetMarkedObjectCount()==1) {
+ const SdrObject* pObj=GetMarkedObjectByIndex(0);
+ if (pObj->GetObjInventor()==SdrInventor::Default) {
+ bOneEdgeMarked = pObj->GetObjIdentifier() == SdrObjKind::Edge;
+ }
+ }
+ ImpSetGlueVisible4(bOneEdgeMarked);
+}
+
+
+void SdrMarkView::SetMoveOutside(bool bOn)
+{
+ maHdlList.SetMoveOutside(bOn);
+}
+
+void SdrMarkView::SetDesignMode( bool bOn )
+{
+ if ( mbDesignMode != bOn )
+ {
+ mbDesignMode = bOn;
+ SdrPageView* pPageView = GetSdrPageView();
+ if ( pPageView )
+ pPageView->SetDesignMode( bOn );
+ }
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/svdraw/svdmrkv1.cxx b/svx/source/svdraw/svdmrkv1.cxx
new file mode 100644
index 000000000..9c732262b
--- /dev/null
+++ b/svx/source/svdraw/svdmrkv1.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 <svx/svdmrkv.hxx>
+#include <svx/svdpagv.hxx>
+#include <osl/diagnose.h>
+
+
+// Point Selection
+
+
+bool SdrMarkView::HasMarkablePoints() const
+{
+ ForceUndirtyMrkPnt();
+ bool bRet=false;
+ if (!ImpIsFrameHandles()) {
+ const size_t nMarkCount=GetMarkedObjectCount();
+ if (nMarkCount<=static_cast<size_t>(mnFrameHandlesLimit)) {
+ for (size_t nMarkNum=0; nMarkNum<nMarkCount && !bRet; ++nMarkNum) {
+ const SdrMark* pM=GetSdrMarkByIndex(nMarkNum);
+ const SdrObject* pObj=pM->GetMarkedSdrObj();
+ bRet=pObj->IsPolyObj();
+ }
+ }
+ }
+ return bRet;
+}
+
+sal_Int32 SdrMarkView::GetMarkablePointCount() const
+{
+ ForceUndirtyMrkPnt();
+ sal_Int32 nCount=0;
+ if (!ImpIsFrameHandles()) {
+ const size_t nMarkCount=GetMarkedObjectCount();
+ if (nMarkCount<=static_cast<size_t>(mnFrameHandlesLimit)) {
+ for (size_t nMarkNum=0; nMarkNum<nMarkCount; ++nMarkNum) {
+ const SdrMark* pM=GetSdrMarkByIndex(nMarkNum);
+ const SdrObject* pObj=pM->GetMarkedSdrObj();
+ if (pObj->IsPolyObj()) {
+ nCount+=pObj->GetPointCount();
+ }
+ }
+ }
+ }
+ return nCount;
+}
+
+bool SdrMarkView::HasMarkedPoints() const
+{
+ ForceUndirtyMrkPnt();
+ bool bRet=false;
+ if (!ImpIsFrameHandles()) {
+ const size_t nMarkCount=GetMarkedObjectCount();
+ if (nMarkCount<=static_cast<size_t>(mnFrameHandlesLimit)) {
+ for (size_t nMarkNum=0; nMarkNum<nMarkCount && !bRet; ++nMarkNum) {
+ const SdrMark* pM=GetSdrMarkByIndex(nMarkNum);
+ const SdrUShortCont& rPts = pM->GetMarkedPoints();
+ bRet = !rPts.empty();
+ }
+ }
+ }
+ return bRet;
+}
+
+bool SdrMarkView::IsPointMarkable(const SdrHdl& rHdl) const
+{
+ return !ImpIsFrameHandles() && !rHdl.IsPlusHdl() && rHdl.GetKind()!=SdrHdlKind::Glue && rHdl.GetKind()!=SdrHdlKind::SmartTag && rHdl.GetObj()!=nullptr && rHdl.GetObj()->IsPolyObj();
+}
+
+bool SdrMarkView::MarkPointHelper(SdrHdl* pHdl, SdrMark* pMark, bool bUnmark)
+{
+ return ImpMarkPoint( pHdl, pMark, bUnmark );
+}
+
+bool SdrMarkView::ImpMarkPoint(SdrHdl* pHdl, SdrMark* pMark, bool bUnmark)
+{
+ if (pHdl==nullptr || pHdl->IsPlusHdl() || pHdl->GetKind()==SdrHdlKind::Glue)
+ return false;
+
+ if (pHdl->IsSelected() != bUnmark)
+ return false;
+
+ SdrObject* pObj=pHdl->GetObj();
+ if (pObj==nullptr || !pObj->IsPolyObj())
+ return false;
+
+ if (pMark==nullptr)
+ {
+ const size_t nMarkNum=TryToFindMarkedObject(pObj);
+ if (nMarkNum==SAL_MAX_SIZE)
+ return false;
+ pMark=GetSdrMarkByIndex(nMarkNum);
+ }
+ const sal_uInt32 nHdlNum(pHdl->GetObjHdlNum());
+ SdrUShortCont& rPts=pMark->GetMarkedPoints();
+ if (!bUnmark)
+ {
+ rPts.insert(static_cast<sal_uInt16>(nHdlNum));
+ }
+ else
+ {
+ SdrUShortCont::const_iterator it = rPts.find( static_cast<sal_uInt16>(nHdlNum) );
+ if (it != rPts.end())
+ {
+ rPts.erase(it);
+ }
+ else
+ {
+ return false; // error case!
+ }
+ }
+
+ pHdl->SetSelected(!bUnmark);
+ if (!mbPlusHdlAlways)
+ {
+ if (!bUnmark)
+ {
+ SdrHdlList plusList(nullptr);
+ pObj->AddToPlusHdlList(plusList, *pHdl);
+ sal_uInt32 nCount(plusList.GetHdlCount());
+ for (sal_uInt32 i=0; i<nCount; i++)
+ {
+ SdrHdl* pPlusHdl=plusList.GetHdl(i);
+ pPlusHdl->SetObj(pObj);
+ pPlusHdl->SetPageView(pMark->GetPageView());
+ pPlusHdl->SetPlusHdl(true);
+ }
+ plusList.MoveTo(maHdlList);
+ }
+ else
+ {
+ for (size_t i = maHdlList.GetHdlCount(); i>0;)
+ {
+ --i;
+ SdrHdl* pPlusHdl=maHdlList.GetHdl(i);
+ if (pPlusHdl->IsPlusHdl() && pPlusHdl->GetSourceHdlNum()==nHdlNum)
+ {
+ maHdlList.RemoveHdl(i);
+ }
+ }
+ }
+ }
+
+ maHdlList.Sort();
+
+ return true;
+}
+
+
+bool SdrMarkView::MarkPoint(SdrHdl& rHdl, bool bUnmark)
+{
+ ForceUndirtyMrkPnt();
+ bool bRet=false;
+ const SdrObject* pObj=rHdl.GetObj();
+ if (IsPointMarkable(rHdl) && rHdl.IsSelected()==bUnmark) {
+ const size_t nMarkNum=TryToFindMarkedObject(pObj);
+ if (nMarkNum!=SAL_MAX_SIZE) {
+ SdrMark* pM=GetSdrMarkByIndex(nMarkNum);
+ if (ImpMarkPoint(&rHdl,pM,bUnmark)) {
+ MarkListHasChanged();
+ bRet=true;
+ }
+ }
+ }
+
+ return bRet;
+}
+
+bool SdrMarkView::MarkPoints(const tools::Rectangle* pRect, bool bUnmark)
+{
+ ForceUndirtyMrkPnt();
+ bool bChgd=false;
+ SortMarkedObjects();
+ const SdrObject* pObj0=nullptr;
+ const SdrPageView* pPV0=nullptr;
+ SdrMark* pM=nullptr;
+ maHdlList.Sort();
+ const size_t nHdlCnt=maHdlList.GetHdlCount();
+ for (size_t nHdlNum=nHdlCnt; nHdlNum>0;) {
+ --nHdlNum;
+ SdrHdl* pHdl=maHdlList.GetHdl(nHdlNum);
+ if (IsPointMarkable(*pHdl) && pHdl->IsSelected()==bUnmark) {
+ const SdrObject* pObj=pHdl->GetObj();
+ const SdrPageView* pPV=pHdl->GetPageView();
+ if (pObj!=pObj0 || pPV!=pPV0 || pM==nullptr) { // This section is for optimization,
+ const size_t nMarkNum=TryToFindMarkedObject(pObj); // so ImpMarkPoint() doesn't always
+ if (nMarkNum!=SAL_MAX_SIZE) { // have to search the object in the MarkList.
+ pM=GetSdrMarkByIndex(nMarkNum);
+ pObj0=pObj;
+ pPV0=pPV;
+ } else {
+#ifdef DBG_UTIL
+ if (pObj->IsInserted()) {
+ OSL_FAIL("SdrMarkView::MarkPoints(const Rectangle* pRect): Selected object not found.");
+ }
+#endif
+ pM=nullptr;
+ }
+ }
+ Point aPos(pHdl->GetPos());
+ if (pM!=nullptr && (pRect==nullptr || pRect->Contains(aPos))) {
+ if (ImpMarkPoint(pHdl,pM,bUnmark)) bChgd=true;
+ }
+ }
+ }
+ if (bChgd) {
+ MarkListHasChanged();
+ }
+
+ return bChgd;
+}
+
+void SdrMarkView::MarkNextPoint()
+{
+ ForceUndirtyMrkPnt();
+ SortMarkedObjects();
+}
+
+const tools::Rectangle& SdrMarkView::GetMarkedPointsRect() const
+{
+ ForceUndirtyMrkPnt();
+ if (mbMarkedPointsRectsDirty) ImpSetPointsRects();
+ return maMarkedPointsRect;
+}
+
+void SdrMarkView::SetPlusHandlesAlwaysVisible(bool bOn)
+{ // TODO: Optimize HandlePaint!
+ ForceUndirtyMrkPnt();
+ if (bOn!=mbPlusHdlAlways) {
+ mbPlusHdlAlways=bOn;
+ SetMarkHandles(nullptr);
+ MarkListHasChanged();
+ }
+}
+
+
+// ImpSetPointsRects() is for PolyPoints and GluePoints!
+
+
+void SdrMarkView::ImpSetPointsRects() const
+{
+ tools::Rectangle aPnts;
+ tools::Rectangle aGlue;
+ const size_t nHdlCnt=maHdlList.GetHdlCount();
+ for (size_t nHdlNum=0; nHdlNum<nHdlCnt; ++nHdlNum) {
+ const SdrHdl* pHdl=maHdlList.GetHdl(nHdlNum);
+ SdrHdlKind eKind=pHdl->GetKind();
+ if ((eKind==SdrHdlKind::Poly && pHdl->IsSelected()) || eKind==SdrHdlKind::Glue) {
+ Point aPt(pHdl->GetPos());
+ tools::Rectangle& rR=eKind==SdrHdlKind::Glue ? aGlue : aPnts;
+ if (rR.IsEmpty()) {
+ rR=tools::Rectangle(aPt,aPt);
+ } else {
+ if (aPt.X()<rR.Left ()) rR.SetLeft(aPt.X() );
+ if (aPt.X()>rR.Right ()) rR.SetRight(aPt.X() );
+ if (aPt.Y()<rR.Top ()) rR.SetTop(aPt.Y() );
+ if (aPt.Y()>rR.Bottom()) rR.SetBottom(aPt.Y() );
+ }
+ }
+ }
+ const_cast<SdrMarkView*>(this)->maMarkedPointsRect=aPnts;
+ const_cast<SdrMarkView*>(this)->maMarkedGluePointsRect=aGlue;
+ const_cast<SdrMarkView*>(this)->mbMarkedPointsRectsDirty=false;
+}
+
+
+// UndirtyMrkPnt() is for PolyPoints and GluePoints!
+
+
+void SdrMarkView::UndirtyMrkPnt() const
+{
+ bool bChg=false;
+ const size_t nMarkCount=GetMarkedObjectCount();
+ for (size_t nMarkNum=0; nMarkNum<nMarkCount; ++nMarkNum) {
+ SdrMark* pM=GetSdrMarkByIndex(nMarkNum);
+ const SdrObject* pObj=pM->GetMarkedSdrObj();
+ // PolyPoints
+ {
+ SdrUShortCont& rPts = pM->GetMarkedPoints();
+ if (pObj->IsPolyObj()) {
+ // Remove invalid selected points, that is, all
+ // entries above the number of points in the object.
+ sal_uInt32 nMax(pObj->GetPointCount());
+
+ SdrUShortCont::const_iterator it = rPts.lower_bound(nMax);
+ if( it != rPts.end() )
+ {
+ rPts.erase(it, rPts.end());
+ bChg = true;
+ }
+ }
+ else
+ {
+ if (!rPts.empty())
+ {
+ // only fail *if* there are marked points
+ OSL_FAIL("SdrMarkView::UndirtyMrkPnt(): Selected points on an object that is not a PolyObj!");
+ rPts.clear();
+ bChg = true;
+ }
+ }
+ }
+
+ // GluePoints
+ {
+ SdrUShortCont& rPts = pM->GetMarkedGluePoints();
+ const SdrGluePointList* pGPL=pObj->GetGluePointList();
+ if (pGPL!=nullptr) {
+ // Remove invalid selected gluepoints, that is, all entries
+ // (IDs) that aren't contained in the GluePointList of the
+ // object
+ for(SdrUShortCont::const_iterator it = rPts.begin(); it != rPts.end(); )
+ {
+ sal_uInt16 nId=*it;
+ if (pGPL->FindGluePoint(nId)==SDRGLUEPOINT_NOTFOUND) {
+ it = rPts.erase(it);
+ bChg=true;
+ }
+ else
+ ++it;
+ }
+ } else {
+ if (!rPts.empty()) {
+ rPts.clear(); // object doesn't have any gluepoints (any more)
+ bChg=true;
+ }
+ }
+ }
+ }
+ if (bChg) const_cast<SdrMarkView*>(this)->mbMarkedPointsRectsDirty=true;
+ const_cast<SdrMarkView*>(this)->mbMrkPntDirty=false;
+}
+
+
+bool SdrMarkView::HasMarkableGluePoints() const
+{
+ bool bRet=false;
+ if (IsGluePointEditMode()) {
+ ForceUndirtyMrkPnt();
+ const size_t nMarkCount=GetMarkedObjectCount();
+ for (size_t nMarkNum=0; nMarkNum<nMarkCount && !bRet; ++nMarkNum) {
+ const SdrMark* pM=GetSdrMarkByIndex(nMarkNum);
+ const SdrObject* pObj=pM->GetMarkedSdrObj();
+ const SdrGluePointList* pGPL=pObj->GetGluePointList();
+
+ // #i38892#
+ if(pGPL && pGPL->GetCount())
+ {
+ for(sal_uInt16 a(0); !bRet && a < pGPL->GetCount(); a++)
+ {
+ if((*pGPL)[a].IsUserDefined())
+ {
+ bRet = true;
+ }
+ }
+ }
+ }
+ }
+ return bRet;
+}
+
+bool SdrMarkView::HasMarkedGluePoints() const
+{
+ ForceUndirtyMrkPnt();
+ bool bRet=false;
+ const size_t nMarkCount=GetMarkedObjectCount();
+ for (size_t nMarkNum=0; nMarkNum<nMarkCount && !bRet; ++nMarkNum) {
+ const SdrMark* pM=GetSdrMarkByIndex(nMarkNum);
+ const SdrUShortCont& rPts = pM->GetMarkedGluePoints();
+ bRet = !rPts.empty();
+ }
+ return bRet;
+}
+
+bool SdrMarkView::MarkGluePoints(const tools::Rectangle* pRect, bool bUnmark)
+{
+ if (!IsGluePointEditMode() && !bUnmark) return false;
+ ForceUndirtyMrkPnt();
+ bool bChgd=false;
+ SortMarkedObjects();
+ const size_t nMarkCount=GetMarkedObjectCount();
+ for (size_t nMarkNum=0; nMarkNum<nMarkCount; ++nMarkNum) {
+ SdrMark* pM=GetSdrMarkByIndex(nMarkNum);
+ const SdrObject* pObj=pM->GetMarkedSdrObj();
+ const SdrGluePointList* pGPL=pObj->GetGluePointList();
+ SdrUShortCont& rPts = pM->GetMarkedGluePoints();
+ if (bUnmark && pRect==nullptr) { // UnmarkAll
+ if (!rPts.empty()) {
+ rPts.clear();
+ bChgd=true;
+ }
+ } else {
+ if (pGPL!=nullptr) {
+ sal_uInt16 nGluePointCnt=pGPL->GetCount();
+ for (sal_uInt16 nGPNum=0; nGPNum<nGluePointCnt; nGPNum++) {
+ const SdrGluePoint& rGP=(*pGPL)[nGPNum];
+
+ // #i38892#
+ if(rGP.IsUserDefined())
+ {
+ Point aPos(rGP.GetAbsolutePos(*pObj));
+ if (pRect==nullptr || pRect->Contains(aPos)) {
+ bool bContains = rPts.find( rGP.GetId() ) != rPts.end();
+ if (!bUnmark && !bContains) {
+ bChgd=true;
+ rPts.insert(rGP.GetId());
+ }
+ if (bUnmark && bContains) {
+ bChgd=true;
+ rPts.erase(rGP.GetId());
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ if (bChgd) {
+ AdjustMarkHdl();
+ MarkListHasChanged();
+ }
+ return bChgd;
+}
+
+bool SdrMarkView::PickGluePoint(const Point& rPnt, SdrObject*& rpObj, sal_uInt16& rnId, SdrPageView*& rpPV) const
+{
+ rpObj=nullptr; rpPV=nullptr; rnId=0;
+ if (!IsGluePointEditMode()) return false;
+ OutputDevice* pOut=mpActualOutDev.get();
+ if (pOut==nullptr) pOut=GetFirstOutputDevice();
+ if (pOut==nullptr) return false;
+ SortMarkedObjects();
+ const size_t nMarkCount=GetMarkedObjectCount();
+ size_t nMarkNum=nMarkCount;
+ while (nMarkNum>0) {
+ nMarkNum--;
+ const SdrMark* pM=GetSdrMarkByIndex(nMarkNum);
+ SdrObject* pObj=pM->GetMarkedSdrObj();
+ SdrPageView* pPV=pM->GetPageView();
+ const SdrGluePointList* pGPL=pObj->GetGluePointList();
+ if (pGPL!=nullptr) {
+ sal_uInt16 nNum=pGPL->HitTest(rPnt,*pOut,pObj);
+ if (nNum!=SDRGLUEPOINT_NOTFOUND)
+ {
+ // #i38892#
+ const SdrGluePoint& rCandidate = (*pGPL)[nNum];
+
+ if(rCandidate.IsUserDefined())
+ {
+ rpObj=pObj;
+ rnId=(*pGPL)[nNum].GetId();
+ rpPV=pPV;
+ return true;
+ }
+ }
+ }
+ }
+ return false;
+}
+
+bool SdrMarkView::MarkGluePoint(const SdrObject* pObj, sal_uInt16 nId, bool bUnmark)
+{
+ if (!IsGluePointEditMode()) return false;
+ ForceUndirtyMrkPnt();
+ bool bChgd=false;
+ if (pObj!=nullptr) {
+ const size_t nMarkPos=TryToFindMarkedObject(pObj);
+ if (nMarkPos!=SAL_MAX_SIZE) {
+ SdrMark* pM=GetSdrMarkByIndex(nMarkPos);
+ SdrUShortCont& rPts = pM->GetMarkedGluePoints();
+ bool bContains = rPts.find( nId ) != rPts.end();
+ if (!bUnmark && !bContains) {
+ bChgd=true;
+ rPts.insert(nId);
+ }
+ if (bUnmark && bContains) {
+ bChgd=true;
+ rPts.erase(nId);
+ }
+ } else {
+ // TODO: implement implicit selection of objects
+ }
+ }
+ if (bChgd) {
+ AdjustMarkHdl();
+ MarkListHasChanged();
+ }
+ return bChgd;
+}
+
+bool SdrMarkView::IsGluePointMarked(const SdrObject* pObj, sal_uInt16 nId) const
+{
+ ForceUndirtyMrkPnt();
+ bool bRet=false;
+ const size_t nPos=TryToFindMarkedObject(pObj); // casting to NonConst
+ if (nPos!=SAL_MAX_SIZE) {
+ const SdrMark* pM=GetSdrMarkByIndex(nPos);
+ const SdrUShortCont& rPts = pM->GetMarkedGluePoints();
+ bRet = rPts.find( nId ) != rPts.end();
+ }
+ return bRet;
+}
+
+SdrHdl* SdrMarkView::GetGluePointHdl(const SdrObject* pObj, sal_uInt16 nId) const
+{
+ ForceUndirtyMrkPnt();
+ const size_t nHdlCnt=maHdlList.GetHdlCount();
+ for (size_t nHdlNum=0; nHdlNum<nHdlCnt; ++nHdlNum) {
+ SdrHdl* pHdl=maHdlList.GetHdl(nHdlNum);
+ if (pHdl->GetObj()==pObj &&
+ pHdl->GetKind()==SdrHdlKind::Glue &&
+ pHdl->GetObjHdlNum()==nId ) return pHdl;
+ }
+ return nullptr;
+}
+
+void SdrMarkView::MarkNextGluePoint()
+{
+ ForceUndirtyMrkPnt();
+ SortMarkedObjects();
+}
+
+const tools::Rectangle& SdrMarkView::GetMarkedGluePointsRect() const
+{
+ ForceUndirtyMrkPnt();
+ if (mbMarkedPointsRectsDirty) ImpSetPointsRects();
+ return maMarkedGluePointsRect;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/svdraw/svdoashp.cxx b/svx/source/svdraw/svdoashp.cxx
new file mode 100644
index 000000000..a21c59415
--- /dev/null
+++ b/svx/source/svdraw/svdoashp.cxx
@@ -0,0 +1,3245 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * 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/BitmapShadowFilter.hxx>
+#include <svx/svdoashp.hxx>
+#include <svx/unoapi.hxx>
+#include <com/sun/star/loader/CannotActivateFactoryException.hpp>
+#include <com/sun/star/drawing/XShape.hpp>
+#include <com/sun/star/drawing/XCustomShapeEngine.hpp>
+#include <com/sun/star/drawing/PolyPolygonBezierCoords.hpp>
+#include <com/sun/star/beans/PropertyValue.hpp>
+#include <com/sun/star/awt/Rectangle.hpp>
+#include <com/sun/star/uno/XComponentContext.hpp>
+#include <comphelper/processfactory.hxx>
+#include <comphelper/propertyvalue.hxx>
+#include <comphelper/sequenceashashmap.hxx>
+#include <com/sun/star/uno/Sequence.h>
+#include <tools/helpers.hxx>
+#include <svx/svddrag.hxx>
+#include <svx/svddrgmt.hxx>
+#include <svx/svdmodel.hxx>
+#include <svx/svdpage.hxx>
+#include <svx/svditer.hxx>
+#include <svx/svdobj.hxx>
+#include <svx/svdtrans.hxx>
+#include <svx/dialmgr.hxx>
+#include <svx/strings.hrc>
+#include <editeng/eeitem.hxx>
+#include <editeng/editstat.hxx>
+#include <editeng/adjustitem.hxx>
+#include <svx/svdoutl.hxx>
+#include <editeng/outlobj.hxx>
+#include <svx/sdtfchim.hxx>
+#include <svx/EnhancedCustomShapeGeometry.hxx>
+#include <svx/EnhancedCustomShapeTypeNames.hxx>
+#include <svx/EnhancedCustomShape2d.hxx>
+#include <com/sun/star/beans/PropertyValues.hpp>
+#include <com/sun/star/drawing/EnhancedCustomShapeAdjustmentValue.hpp>
+#include <com/sun/star/drawing/EnhancedCustomShapeParameterPair.hpp>
+#include <com/sun/star/drawing/EnhancedCustomShapeTextFrame.hpp>
+#include <com/sun/star/drawing/EnhancedCustomShapeSegment.hpp>
+#include <com/sun/star/drawing/EnhancedCustomShapeSegmentCommand.hpp>
+#include <editeng/writingmodeitem.hxx>
+#include <svx/xlineit0.hxx>
+#include <svx/xlnclit.hxx>
+#include <sdr/properties/customshapeproperties.hxx>
+#include <sdr/contact/viewcontactofsdrobjcustomshape.hxx>
+#include <svx/xlntrit.hxx>
+#include <svx/xfillit0.hxx>
+#include <svx/xfltrit.hxx>
+#include <svx/xflclit.hxx>
+#include <svx/xflgrit.hxx>
+#include <svx/xflhtit.hxx>
+#include <svx/xbtmpit.hxx>
+#include <vcl/virdev.hxx>
+#include <svx/svdview.hxx>
+#include <svx/sdmetitm.hxx>
+#include <svx/sdprcitm.hxx>
+#include <svx/sdshitm.hxx>
+#include <svx/sdsxyitm.hxx>
+#include <svx/sdtmfitm.hxx>
+#include <svx/sdasitm.hxx>
+#include <basegfx/polygon/b2dpolypolygontools.hxx>
+#include <basegfx/matrix/b2dhommatrix.hxx>
+#include <basegfx/matrix/b2dhommatrixtools.hxx>
+#include <basegfx/polygon/b2dpolygon.hxx>
+#include <basegfx/polygon/b2dpolygontools.hxx>
+#include <basegfx/range/b2drange.hxx>
+#include <svdobjplusdata.hxx>
+#include <sal/log.hxx>
+#include <o3tl/string_view.hxx>
+#include "presetooxhandleadjustmentrelations.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::drawing;
+
+static void lcl_ShapeSegmentFromBinary( EnhancedCustomShapeSegment& rSegInfo, sal_uInt16 nSDat )
+{
+ switch( nSDat >> 8 )
+ {
+ case 0x00 :
+ rSegInfo.Command = EnhancedCustomShapeSegmentCommand::LINETO;
+ rSegInfo.Count = nSDat & 0xff;
+ if ( !rSegInfo.Count )
+ rSegInfo.Count = 1;
+ break;
+ case 0x20 :
+ rSegInfo.Command = EnhancedCustomShapeSegmentCommand::CURVETO;
+ rSegInfo.Count = nSDat & 0xff;
+ if ( !rSegInfo.Count )
+ rSegInfo.Count = 1;
+ break;
+ case 0x40 :
+ rSegInfo.Command = EnhancedCustomShapeSegmentCommand::MOVETO;
+ rSegInfo.Count = nSDat & 0xff;
+ if ( !rSegInfo.Count )
+ rSegInfo.Count = 1;
+ break;
+ case 0x60 :
+ rSegInfo.Command = EnhancedCustomShapeSegmentCommand::CLOSESUBPATH;
+ rSegInfo.Count = 0;
+ break;
+ case 0x80 :
+ rSegInfo.Command = EnhancedCustomShapeSegmentCommand::ENDSUBPATH;
+ rSegInfo.Count = 0;
+ break;
+ case 0xa1 :
+ rSegInfo.Command = EnhancedCustomShapeSegmentCommand::ANGLEELLIPSETO;
+ rSegInfo.Count = ( nSDat & 0xff ) / 3;
+ break;
+ case 0xa2 :
+ rSegInfo.Command = EnhancedCustomShapeSegmentCommand::ANGLEELLIPSE;
+ rSegInfo.Count = ( nSDat & 0xff ) / 3;
+ break;
+ case 0xa3 :
+ rSegInfo.Command = EnhancedCustomShapeSegmentCommand::ARCTO;
+ rSegInfo.Count = ( nSDat & 0xff ) >> 2;
+ break;
+ case 0xa4 :
+ rSegInfo.Command = EnhancedCustomShapeSegmentCommand::ARC;
+ rSegInfo.Count = ( nSDat & 0xff ) >> 2;
+ break;
+ case 0xa5 :
+ rSegInfo.Command = EnhancedCustomShapeSegmentCommand::CLOCKWISEARCTO;
+ rSegInfo.Count = ( nSDat & 0xff ) >> 2;
+ break;
+ case 0xa6 :
+ rSegInfo.Command = EnhancedCustomShapeSegmentCommand::CLOCKWISEARC;
+ rSegInfo.Count = ( nSDat & 0xff ) >> 2;
+ break;
+ case 0xa7 :
+ rSegInfo.Command = EnhancedCustomShapeSegmentCommand::ELLIPTICALQUADRANTX;
+ rSegInfo.Count = nSDat & 0xff;
+ break;
+ case 0xa8 :
+ rSegInfo.Command = EnhancedCustomShapeSegmentCommand::ELLIPTICALQUADRANTY;
+ rSegInfo.Count = nSDat & 0xff;
+ break;
+ case 0xaa :
+ rSegInfo.Command = EnhancedCustomShapeSegmentCommand::NOFILL;
+ rSegInfo.Count = 0;
+ break;
+ case 0xab :
+ rSegInfo.Command = EnhancedCustomShapeSegmentCommand::NOSTROKE;
+ rSegInfo.Count = 0;
+ break;
+ default:
+ case 0xf8 :
+ rSegInfo.Command = EnhancedCustomShapeSegmentCommand::UNKNOWN;
+ rSegInfo.Count = nSDat;
+ break;
+ }
+}
+
+static MSO_SPT ImpGetCustomShapeType( const SdrObjCustomShape& rCustoShape )
+{
+ MSO_SPT eRetValue = mso_sptNil;
+
+ OUString aEngine( rCustoShape.GetMergedItem( SDRATTR_CUSTOMSHAPE_ENGINE ).GetValue() );
+ if ( aEngine.isEmpty() || aEngine == "com.sun.star.drawing.EnhancedCustomShapeEngine" )
+ {
+ OUString sShapeType;
+ const SdrCustomShapeGeometryItem& rGeometryItem( rCustoShape.GetMergedItem( SDRATTR_CUSTOMSHAPE_GEOMETRY ) );
+ const Any* pAny = rGeometryItem.GetPropertyValueByName( "Type" );
+ if ( pAny && ( *pAny >>= sShapeType ) )
+ eRetValue = EnhancedCustomShapeTypeNames::Get( sShapeType );
+ }
+ return eRetValue;
+};
+
+static bool ImpVerticalSwitch( const SdrObjCustomShape& rCustoShape )
+{
+ bool bRet = false;
+ MSO_SPT eShapeType( ImpGetCustomShapeType( rCustoShape ) );
+ switch( eShapeType )
+ {
+ case mso_sptAccentBorderCallout90 : // 2 ortho
+ case mso_sptBorderCallout1 : // 2 diag
+ case mso_sptBorderCallout2 : // 3
+ {
+ bRet = true;
+ }
+ break;
+ default: break;
+ }
+ return bRet;
+}
+
+// #i37011# create a clone with all attributes changed to shadow attributes
+// and translation executed, too.
+static SdrObject* ImpCreateShadowObjectClone(const SdrObject& rOriginal, const SfxItemSet& rOriginalSet)
+{
+ SdrObject* pRetval = nullptr;
+ const bool bShadow(rOriginalSet.Get(SDRATTR_SHADOW).GetValue());
+
+ if(bShadow)
+ {
+ // create a shadow representing object
+ const sal_Int32 nXDist(rOriginalSet.Get(SDRATTR_SHADOWXDIST).GetValue());
+ const sal_Int32 nYDist(rOriginalSet.Get(SDRATTR_SHADOWYDIST).GetValue());
+ const ::Color aShadowColor(rOriginalSet.Get(SDRATTR_SHADOWCOLOR).GetColorValue());
+ const sal_uInt16 nShadowTransparence(rOriginalSet.Get(SDRATTR_SHADOWTRANSPARENCE).GetValue());
+ pRetval = rOriginal.CloneSdrObject(rOriginal.getSdrModelFromSdrObject());
+ DBG_ASSERT(pRetval, "ImpCreateShadowObjectClone: Could not clone object (!)");
+
+ // look for used stuff
+ SdrObjListIter aIterator(rOriginal);
+ bool bLineUsed(false);
+ bool bAllFillUsed(false);
+ bool bSolidFillUsed(false);
+ bool bGradientFillUsed(false);
+ bool bHatchFillUsed(false);
+ bool bBitmapFillUsed(false);
+
+ while(aIterator.IsMore())
+ {
+ SdrObject* pObj = aIterator.Next();
+ drawing::FillStyle eFillStyle = pObj->GetMergedItem(XATTR_FILLSTYLE).GetValue();
+
+ if(!bLineUsed)
+ {
+ drawing::LineStyle eLineStyle = pObj->GetMergedItem(XATTR_LINESTYLE).GetValue();
+
+ if(drawing::LineStyle_NONE != eLineStyle)
+ {
+ bLineUsed = true;
+ }
+ }
+
+ if(!bAllFillUsed)
+ {
+ if(!bSolidFillUsed && drawing::FillStyle_SOLID == eFillStyle)
+ {
+ bSolidFillUsed = true;
+ bAllFillUsed = (bSolidFillUsed && bGradientFillUsed && bHatchFillUsed && bBitmapFillUsed);
+ }
+ if(!bGradientFillUsed && drawing::FillStyle_GRADIENT == eFillStyle)
+ {
+ bGradientFillUsed = true;
+ bAllFillUsed = (bSolidFillUsed && bGradientFillUsed && bHatchFillUsed && bBitmapFillUsed);
+ }
+ if(!bHatchFillUsed && drawing::FillStyle_HATCH == eFillStyle)
+ {
+ bHatchFillUsed = true;
+ bAllFillUsed = (bSolidFillUsed && bGradientFillUsed && bHatchFillUsed && bBitmapFillUsed);
+ }
+ if(!bBitmapFillUsed && drawing::FillStyle_BITMAP == eFillStyle)
+ {
+ bBitmapFillUsed = true;
+ bAllFillUsed = (bSolidFillUsed && bGradientFillUsed && bHatchFillUsed && bBitmapFillUsed);
+ }
+ }
+ }
+
+ // translate to shadow coordinates
+ pRetval->NbcMove(Size(nXDist, nYDist));
+
+ // set items as needed
+ SfxItemSet aTempSet(rOriginalSet);
+
+ // if a SvxWritingModeItem (Top->Bottom) is set the text object
+ // is creating a paraobject, but paraobjects can not be created without model. So
+ // we are preventing the crash by setting the writing mode always left to right,
+ // this is not bad since our shadow geometry does not contain text.
+ aTempSet.Put( SvxWritingModeItem( css::text::WritingMode_LR_TB, SDRATTR_TEXTDIRECTION ) );
+
+ // no shadow
+ aTempSet.Put(makeSdrShadowItem(false));
+ aTempSet.Put(makeSdrShadowXDistItem(0));
+ aTempSet.Put(makeSdrShadowYDistItem(0));
+
+ // line color and transparency like shadow
+ if(bLineUsed)
+ {
+ aTempSet.Put(XLineColorItem(OUString(), aShadowColor));
+ aTempSet.Put(XLineTransparenceItem(nShadowTransparence));
+ }
+
+ // fill color and transparency like shadow
+ if(bSolidFillUsed)
+ {
+ aTempSet.Put(XFillColorItem(OUString(), aShadowColor));
+ aTempSet.Put(XFillTransparenceItem(nShadowTransparence));
+ }
+
+ // gradient and transparency like shadow
+ if(bGradientFillUsed)
+ {
+ XGradient aGradient(rOriginalSet.Get(XATTR_FILLGRADIENT).GetGradientValue());
+ sal_uInt8 nStartLuminance(aGradient.GetStartColor().GetLuminance());
+ sal_uInt8 nEndLuminance(aGradient.GetEndColor().GetLuminance());
+
+ if(aGradient.GetStartIntens() != 100)
+ {
+ nStartLuminance = static_cast<sal_uInt8>(nStartLuminance * (static_cast<double>(aGradient.GetStartIntens()) / 100.0));
+ }
+
+ if(aGradient.GetEndIntens() != 100)
+ {
+ nEndLuminance = static_cast<sal_uInt8>(nEndLuminance * (static_cast<double>(aGradient.GetEndIntens()) / 100.0));
+ }
+
+ ::Color aStartColor(
+ static_cast<sal_uInt8>((nStartLuminance * aShadowColor.GetRed()) / 256),
+ static_cast<sal_uInt8>((nStartLuminance * aShadowColor.GetGreen()) / 256),
+ static_cast<sal_uInt8>((nStartLuminance * aShadowColor.GetBlue()) / 256));
+
+ ::Color aEndColor(
+ static_cast<sal_uInt8>((nEndLuminance * aShadowColor.GetRed()) / 256),
+ static_cast<sal_uInt8>((nEndLuminance * aShadowColor.GetGreen()) / 256),
+ static_cast<sal_uInt8>((nEndLuminance * aShadowColor.GetBlue()) / 256));
+
+ aGradient.SetStartColor(aStartColor);
+ aGradient.SetEndColor(aEndColor);
+ aTempSet.Put(XFillGradientItem(aGradient));
+ aTempSet.Put(XFillTransparenceItem(nShadowTransparence));
+ }
+
+ // hatch and transparency like shadow
+ if(bHatchFillUsed)
+ {
+ XHatch aHatch(rOriginalSet.Get(XATTR_FILLHATCH).GetHatchValue());
+ aHatch.SetColor(aShadowColor);
+ aTempSet.Put(XFillHatchItem(aHatch));
+ aTempSet.Put(XFillTransparenceItem(nShadowTransparence));
+ }
+
+ // bitmap and transparency like shadow
+ if(bBitmapFillUsed)
+ {
+ GraphicObject aGraphicObject(rOriginalSet.Get(XATTR_FILLBITMAP).GetGraphicObject());
+ BitmapEx aBitmapEx(aGraphicObject.GetGraphic().GetBitmapEx());
+
+ if(!aBitmapEx.IsEmpty())
+ {
+ ScopedVclPtr<VirtualDevice> pVirDev(VclPtr<VirtualDevice>::Create());
+ pVirDev->SetOutputSizePixel(aBitmapEx.GetSizePixel());
+ BitmapFilter::Filter(aBitmapEx, BitmapShadowFilter(aShadowColor));
+ pVirDev->DrawBitmapEx(Point(), aBitmapEx);
+ aGraphicObject.SetGraphic(Graphic(pVirDev->GetBitmapEx(Point(0,0), aBitmapEx.GetSizePixel())));
+ }
+
+ aTempSet.Put(XFillBitmapItem(aGraphicObject));
+ aTempSet.Put(XFillTransparenceItem(nShadowTransparence));
+ }
+
+ // set attributes and paint shadow object
+ pRetval->SetMergedItemSet( aTempSet );
+ }
+ return pRetval;
+}
+
+
+Reference< XCustomShapeEngine > const & SdrObjCustomShape::GetCustomShapeEngine() const
+{
+ if (mxCustomShapeEngine.is())
+ return mxCustomShapeEngine;
+
+ Reference< XShape > aXShape = GetXShapeForSdrObject(const_cast<SdrObjCustomShape*>(this));
+ if ( !aXShape )
+ return mxCustomShapeEngine;
+
+ Reference< XComponentContext > xContext( ::comphelper::getProcessComponentContext() );
+
+ OUString aEngine(GetMergedItem( SDRATTR_CUSTOMSHAPE_ENGINE ).GetValue());
+ static constexpr OUStringLiteral sEnhancedCustomShapeEngine = u"com.sun.star.drawing.EnhancedCustomShapeEngine";
+ if ( aEngine.isEmpty() )
+ aEngine = sEnhancedCustomShapeEngine;
+
+ {
+ static constexpr OUStringLiteral sCustomShape = u"CustomShape";
+ Sequence< PropertyValue > aPropValues{ comphelper::makePropertyValue(sCustomShape,
+ aXShape) };
+ Sequence< Any > aArgument{ Any(aPropValues) };
+ try
+ {
+ Reference<XInterface> xInterface(xContext->getServiceManager()->createInstanceWithArgumentsAndContext(aEngine, aArgument, xContext));
+ if (xInterface.is())
+ mxCustomShapeEngine.set( xInterface, UNO_QUERY );
+ }
+ catch (const css::loader::CannotActivateFactoryException&)
+ {
+ }
+ }
+
+ return mxCustomShapeEngine;
+}
+
+const SdrObject* SdrObjCustomShape::GetSdrObjectFromCustomShape() const
+{
+ if ( !mXRenderedCustomShape.is() )
+ {
+ Reference< XCustomShapeEngine > xCustomShapeEngine( GetCustomShapeEngine() );
+ if ( xCustomShapeEngine.is() )
+ const_cast<SdrObjCustomShape*>(this)->mXRenderedCustomShape = xCustomShapeEngine->render();
+ }
+ SdrObject* pRenderedCustomShape = mXRenderedCustomShape.is()
+ ? SdrObject::getSdrObjectFromXShape( mXRenderedCustomShape )
+ : nullptr;
+ return pRenderedCustomShape;
+}
+
+// #i37011# Shadow geometry creation
+const SdrObject* SdrObjCustomShape::GetSdrObjectShadowFromCustomShape() const
+{
+ if(!mpLastShadowGeometry)
+ {
+ const SdrObject* pSdrObject = GetSdrObjectFromCustomShape();
+ if(pSdrObject)
+ {
+ const SfxItemSet& rOriginalSet = GetObjectItemSet();
+ const bool bShadow(rOriginalSet.Get( SDRATTR_SHADOW ).GetValue());
+
+ if(bShadow)
+ {
+ // create a clone with all attributes changed to shadow attributes
+ // and translation executed, too.
+ const_cast<SdrObjCustomShape*>(this)->mpLastShadowGeometry =
+ ImpCreateShadowObjectClone(*pSdrObject, rOriginalSet);
+ }
+ }
+ }
+
+ return mpLastShadowGeometry;
+}
+
+bool SdrObjCustomShape::IsTextPath() const
+{
+ static const OUStringLiteral sTextPath( u"TextPath" );
+ bool bTextPathOn = false;
+ const SdrCustomShapeGeometryItem& rGeometryItem = GetMergedItem( SDRATTR_CUSTOMSHAPE_GEOMETRY );
+ const Any* pAny = rGeometryItem.GetPropertyValueByName( sTextPath, sTextPath );
+ if ( pAny )
+ *pAny >>= bTextPathOn;
+ return bTextPathOn;
+}
+
+bool SdrObjCustomShape::UseNoFillStyle() const
+{
+ bool bRet = false;
+ OUString sShapeType;
+ static const OUStringLiteral sType( u"Type" );
+ const SdrCustomShapeGeometryItem& rGeometryItem( GetMergedItem( SDRATTR_CUSTOMSHAPE_GEOMETRY ) );
+ const Any* pAny = rGeometryItem.GetPropertyValueByName( sType );
+ if ( pAny )
+ *pAny >>= sShapeType;
+ bRet = !IsCustomShapeFilledByDefault( EnhancedCustomShapeTypeNames::Get( sType ) );
+
+ return bRet;
+}
+
+bool SdrObjCustomShape::IsMirroredX() const
+{
+ bool bMirroredX = false;
+ const SdrCustomShapeGeometryItem & rGeometryItem( GetMergedItem( SDRATTR_CUSTOMSHAPE_GEOMETRY ) );
+ const css::uno::Any* pAny = rGeometryItem.GetPropertyValueByName( "MirroredX" );
+ if ( pAny )
+ *pAny >>= bMirroredX;
+ return bMirroredX;
+}
+bool SdrObjCustomShape::IsMirroredY() const
+{
+ bool bMirroredY = false;
+ const SdrCustomShapeGeometryItem & rGeometryItem( GetMergedItem( SDRATTR_CUSTOMSHAPE_GEOMETRY ) );
+ const css::uno::Any* pAny = rGeometryItem.GetPropertyValueByName( "MirroredY" );
+ if ( pAny )
+ *pAny >>= bMirroredY;
+ return bMirroredY;
+}
+void SdrObjCustomShape::SetMirroredX( const bool bMirrorX )
+{
+ SdrCustomShapeGeometryItem aGeometryItem( GetMergedItem( SDRATTR_CUSTOMSHAPE_GEOMETRY ) );
+ PropertyValue aPropVal;
+ aPropVal.Name = "MirroredX";
+ aPropVal.Value <<= bMirrorX;
+ aGeometryItem.SetPropertyValue( aPropVal );
+ SetMergedItem( aGeometryItem );
+}
+void SdrObjCustomShape::SetMirroredY( const bool bMirrorY )
+{
+ SdrCustomShapeGeometryItem aGeometryItem( GetMergedItem( SDRATTR_CUSTOMSHAPE_GEOMETRY ) );
+ PropertyValue aPropVal;
+ aPropVal.Name = "MirroredY";
+ aPropVal.Value <<= bMirrorY;
+ aGeometryItem.SetPropertyValue( aPropVal );
+ SetMergedItem( aGeometryItem );
+}
+
+double SdrObjCustomShape::GetExtraTextRotation( const bool bPreRotation ) const
+{
+ const css::uno::Any* pAny;
+ const SdrCustomShapeGeometryItem& rGeometryItem = GetMergedItem( SDRATTR_CUSTOMSHAPE_GEOMETRY );
+ pAny = rGeometryItem.GetPropertyValueByName( bPreRotation ? OUString( "TextPreRotateAngle" ) : OUString( "TextRotateAngle" ) );
+ double fExtraTextRotateAngle = 0.0;
+ if ( pAny )
+ *pAny >>= fExtraTextRotateAngle;
+ return fExtraTextRotateAngle;
+}
+
+bool SdrObjCustomShape::GetTextBounds( tools::Rectangle& rTextBound ) const
+{
+ bool bRet = false;
+
+ Reference< XCustomShapeEngine > xCustomShapeEngine( GetCustomShapeEngine() );
+ if ( xCustomShapeEngine.is() )
+ {
+ awt::Rectangle aR( xCustomShapeEngine->getTextBounds() );
+ if ( aR.Width > 1 && aR.Height > 1 )
+ {
+ rTextBound = tools::Rectangle( Point( aR.X, aR.Y ), Size( aR.Width, aR.Height ) );
+ bRet = true;
+ }
+ }
+ return bRet;
+}
+basegfx::B2DPolyPolygon SdrObjCustomShape::GetLineGeometry( const bool bBezierAllowed ) const
+{
+ basegfx::B2DPolyPolygon aRetval;
+ Reference< XCustomShapeEngine > xCustomShapeEngine( GetCustomShapeEngine() );
+ if ( xCustomShapeEngine.is() )
+ {
+ css::drawing::PolyPolygonBezierCoords aBezierCoords = xCustomShapeEngine->getLineGeometry();
+ try
+ {
+ aRetval = basegfx::utils::UnoPolyPolygonBezierCoordsToB2DPolyPolygon( aBezierCoords );
+ if ( !bBezierAllowed && aRetval.areControlPointsUsed())
+ {
+ aRetval = basegfx::utils::adaptiveSubdivideByAngle(aRetval);
+ }
+ }
+ catch ( const css::lang::IllegalArgumentException & )
+ {
+ }
+ }
+ return aRetval;
+}
+
+std::vector< SdrCustomShapeInteraction > SdrObjCustomShape::GetInteractionHandles() const
+{
+ std::vector< SdrCustomShapeInteraction > aRet;
+ try
+ {
+ Reference< XCustomShapeEngine > xCustomShapeEngine( GetCustomShapeEngine() );
+ if ( xCustomShapeEngine.is() )
+ {
+ int i;
+ Sequence< Reference< XCustomShapeHandle > > xInteractionHandles( xCustomShapeEngine->getInteraction() );
+ for ( i = 0; i < xInteractionHandles.getLength(); i++ )
+ {
+ if ( xInteractionHandles[ i ].is() )
+ {
+ SdrCustomShapeInteraction aSdrCustomShapeInteraction;
+ aSdrCustomShapeInteraction.xInteraction = xInteractionHandles[ i ];
+ aSdrCustomShapeInteraction.aPosition = xInteractionHandles[ i ]->getPosition();
+
+ CustomShapeHandleModes nMode = CustomShapeHandleModes::NONE;
+ switch( ImpGetCustomShapeType( *this ) )
+ {
+ case mso_sptAccentBorderCallout90 : // 2 ortho
+ {
+ if (i == 0)
+ nMode |= CustomShapeHandleModes::RESIZE_FIXED | CustomShapeHandleModes::CREATE_FIXED;
+ else if (i == 1)
+ nMode |= CustomShapeHandleModes::RESIZE_ABSOLUTE_X | CustomShapeHandleModes::RESIZE_ABSOLUTE_Y | CustomShapeHandleModes::MOVE_SHAPE | CustomShapeHandleModes::ORTHO4;
+ }
+ break;
+
+ case mso_sptChevron :
+ case mso_sptHomePlate :
+ nMode |= CustomShapeHandleModes::RESIZE_ABSOLUTE_NEGX;
+ break;
+
+ case mso_sptWedgeRectCallout :
+ case mso_sptWedgeRRectCallout :
+ case mso_sptCloudCallout :
+ case mso_sptWedgeEllipseCallout :
+ {
+ if (i == 0)
+ nMode |= CustomShapeHandleModes::RESIZE_FIXED;
+ }
+ break;
+
+ case mso_sptBorderCallout1 : // 2 diag
+ {
+ if (i == 0)
+ nMode |= CustomShapeHandleModes::RESIZE_FIXED | CustomShapeHandleModes::CREATE_FIXED;
+ else if (i == 1)
+ nMode |= CustomShapeHandleModes::RESIZE_ABSOLUTE_X | CustomShapeHandleModes::RESIZE_ABSOLUTE_Y | CustomShapeHandleModes::MOVE_SHAPE;
+ }
+ break;
+ case mso_sptBorderCallout2 : // 3
+ {
+ if (i == 0)
+ nMode |= CustomShapeHandleModes::RESIZE_FIXED | CustomShapeHandleModes::CREATE_FIXED;
+ else if (i == 2)
+ nMode |= CustomShapeHandleModes::RESIZE_ABSOLUTE_X | CustomShapeHandleModes::RESIZE_ABSOLUTE_Y | CustomShapeHandleModes::MOVE_SHAPE;
+ }
+ break;
+ case mso_sptCallout90 :
+ case mso_sptAccentCallout90 :
+ case mso_sptBorderCallout90 :
+ case mso_sptCallout1 :
+ case mso_sptCallout2 :
+ case mso_sptCallout3 :
+ case mso_sptAccentCallout1 :
+ case mso_sptAccentCallout2 :
+ case mso_sptAccentCallout3 :
+ case mso_sptBorderCallout3 :
+ case mso_sptAccentBorderCallout1 :
+ case mso_sptAccentBorderCallout2 :
+ case mso_sptAccentBorderCallout3 :
+ {
+ if (i == 0)
+ nMode |= CustomShapeHandleModes::RESIZE_FIXED | CustomShapeHandleModes::CREATE_FIXED;
+ }
+ break;
+ default: break;
+ }
+ aSdrCustomShapeInteraction.nMode = nMode;
+ aRet.push_back( aSdrCustomShapeInteraction );
+ }
+ }
+ }
+ }
+ catch( const uno::RuntimeException& )
+ {
+ }
+ return aRet;
+}
+
+
+// BaseProperties section
+#define DEFAULT_MINIMUM_SIGNED_COMPARE (sal_Int32(0x80000000))
+#define DEFAULT_MAXIMUM_SIGNED_COMPARE (sal_Int32(0x7fffffff))
+
+static sal_Int32 GetNumberOfProperties ( const SvxMSDffHandle* pData )
+{
+ sal_Int32 nPropertiesNeeded=1; // position is always needed
+ SvxMSDffHandleFlags nFlags = pData->nFlags;
+
+ if ( nFlags & SvxMSDffHandleFlags::MIRRORED_X )
+ nPropertiesNeeded++;
+ if ( nFlags & SvxMSDffHandleFlags::MIRRORED_Y )
+ nPropertiesNeeded++;
+ if ( nFlags & SvxMSDffHandleFlags::SWITCHED )
+ nPropertiesNeeded++;
+ if ( nFlags & SvxMSDffHandleFlags::POLAR )
+ {
+ nPropertiesNeeded++;
+ if ( nFlags & SvxMSDffHandleFlags::RADIUS_RANGE )
+ {
+ if ( pData->nRangeXMin != DEFAULT_MINIMUM_SIGNED_COMPARE )
+ nPropertiesNeeded++;
+ if ( pData->nRangeXMax != DEFAULT_MAXIMUM_SIGNED_COMPARE )
+ nPropertiesNeeded++;
+ }
+ }
+ else if ( nFlags & SvxMSDffHandleFlags::RANGE )
+ {
+ if ( pData->nRangeXMin != DEFAULT_MINIMUM_SIGNED_COMPARE )
+ nPropertiesNeeded++;
+ if ( pData->nRangeXMax != DEFAULT_MAXIMUM_SIGNED_COMPARE )
+ nPropertiesNeeded++;
+ if ( pData->nRangeYMin != DEFAULT_MINIMUM_SIGNED_COMPARE )
+ nPropertiesNeeded++;
+ if ( pData->nRangeYMax != DEFAULT_MAXIMUM_SIGNED_COMPARE )
+ nPropertiesNeeded++;
+ }
+
+ return nPropertiesNeeded;
+}
+
+static void lcl_ShapePropertiesFromDFF( const SvxMSDffHandle* pData, css::beans::PropertyValues& rPropValues )
+{
+ SvxMSDffHandleFlags nFlags = pData->nFlags;
+ sal_Int32 n=0;
+ auto pPropValues = rPropValues.getArray();
+
+ // POSITION
+ {
+ css::drawing::EnhancedCustomShapeParameterPair aPosition;
+ EnhancedCustomShape2d::SetEnhancedCustomShapeHandleParameter( aPosition.First, pData->nPositionX, true, true );
+ EnhancedCustomShape2d::SetEnhancedCustomShapeHandleParameter( aPosition.Second, pData->nPositionY, true, false );
+ pPropValues[ n ].Name = "Position";
+ pPropValues[ n++ ].Value <<= aPosition;
+ }
+ if ( nFlags & SvxMSDffHandleFlags::MIRRORED_X )
+ {
+ pPropValues[ n ].Name = "MirroredX";
+ pPropValues[ n++ ].Value <<= true;
+ }
+ if ( nFlags & SvxMSDffHandleFlags::MIRRORED_Y )
+ {
+ pPropValues[ n ].Name = "MirroredY";
+ pPropValues[ n++ ].Value <<= true;
+ }
+ if ( nFlags & SvxMSDffHandleFlags::SWITCHED )
+ {
+ pPropValues[ n ].Name = "Switched";
+ pPropValues[ n++ ].Value <<= true;
+ }
+ if ( nFlags & SvxMSDffHandleFlags::POLAR )
+ {
+ css::drawing::EnhancedCustomShapeParameterPair aCenter;
+ EnhancedCustomShape2d::SetEnhancedCustomShapeHandleParameter( aCenter.First, pData->nCenterX,
+ bool( nFlags & SvxMSDffHandleFlags::CENTER_X_IS_SPECIAL ), true );
+ EnhancedCustomShape2d::SetEnhancedCustomShapeHandleParameter( aCenter.Second, pData->nCenterY,
+ bool( nFlags & SvxMSDffHandleFlags::CENTER_Y_IS_SPECIAL ), false );
+ pPropValues[ n ].Name = "Polar";
+ pPropValues[ n++ ].Value <<= aCenter;
+ if ( nFlags & SvxMSDffHandleFlags::RADIUS_RANGE )
+ {
+ if ( pData->nRangeXMin != DEFAULT_MINIMUM_SIGNED_COMPARE )
+ {
+ css::drawing::EnhancedCustomShapeParameter aRadiusRangeMinimum;
+ EnhancedCustomShape2d::SetEnhancedCustomShapeHandleParameter( aRadiusRangeMinimum, pData->nRangeXMin,
+ bool( nFlags & SvxMSDffHandleFlags::RANGE_X_MIN_IS_SPECIAL ), true );
+ pPropValues[ n ].Name = "RadiusRangeMinimum";
+ pPropValues[ n++ ].Value <<= aRadiusRangeMinimum;
+ }
+ if ( pData->nRangeXMax != DEFAULT_MAXIMUM_SIGNED_COMPARE )
+ {
+ css::drawing::EnhancedCustomShapeParameter aRadiusRangeMaximum;
+ EnhancedCustomShape2d::SetEnhancedCustomShapeHandleParameter( aRadiusRangeMaximum, pData->nRangeXMax,
+ bool( nFlags & SvxMSDffHandleFlags::RANGE_X_MAX_IS_SPECIAL ), false );
+ pPropValues[ n ].Name = "RadiusRangeMaximum";
+ pPropValues[ n++ ].Value <<= aRadiusRangeMaximum;
+ }
+ }
+ }
+ else if ( nFlags & SvxMSDffHandleFlags::RANGE )
+ {
+ if ( pData->nRangeXMin != DEFAULT_MINIMUM_SIGNED_COMPARE )
+ {
+ css::drawing::EnhancedCustomShapeParameter aRangeXMinimum;
+ EnhancedCustomShape2d::SetEnhancedCustomShapeHandleParameter( aRangeXMinimum, pData->nRangeXMin,
+ bool( nFlags & SvxMSDffHandleFlags::RANGE_X_MIN_IS_SPECIAL ), true );
+ pPropValues[ n ].Name = "RangeXMinimum";
+ pPropValues[ n++ ].Value <<= aRangeXMinimum;
+ }
+ if ( pData->nRangeXMax != DEFAULT_MAXIMUM_SIGNED_COMPARE )
+ {
+ css::drawing::EnhancedCustomShapeParameter aRangeXMaximum;
+ EnhancedCustomShape2d::SetEnhancedCustomShapeHandleParameter( aRangeXMaximum, pData->nRangeXMax,
+ bool( nFlags & SvxMSDffHandleFlags::RANGE_X_MAX_IS_SPECIAL ), false );
+ pPropValues[ n ].Name = "RangeXMaximum";
+ pPropValues[ n++ ].Value <<= aRangeXMaximum;
+ }
+ if ( pData->nRangeYMin != DEFAULT_MINIMUM_SIGNED_COMPARE )
+ {
+ css::drawing::EnhancedCustomShapeParameter aRangeYMinimum;
+ EnhancedCustomShape2d::SetEnhancedCustomShapeHandleParameter( aRangeYMinimum, pData->nRangeYMin,
+ bool( nFlags & SvxMSDffHandleFlags::RANGE_Y_MIN_IS_SPECIAL ), true );
+ pPropValues[ n ].Name = "RangeYMinimum";
+ pPropValues[ n++ ].Value <<= aRangeYMinimum;
+ }
+ if ( pData->nRangeYMax != DEFAULT_MAXIMUM_SIGNED_COMPARE )
+ {
+ css::drawing::EnhancedCustomShapeParameter aRangeYMaximum;
+ EnhancedCustomShape2d::SetEnhancedCustomShapeHandleParameter( aRangeYMaximum, pData->nRangeYMax,
+ bool( nFlags & SvxMSDffHandleFlags::RANGE_Y_MAX_IS_SPECIAL ), false );
+ pPropValues[ n ].Name = "RangeYMaximum";
+ pPropValues[ n++ ].Value <<= aRangeYMaximum;
+ }
+ }
+}
+
+std::unique_ptr<sdr::properties::BaseProperties> SdrObjCustomShape::CreateObjectSpecificProperties()
+{
+ return std::make_unique<sdr::properties::CustomShapeProperties>(*this);
+}
+
+SdrObjCustomShape::SdrObjCustomShape(SdrModel& rSdrModel)
+: SdrTextObj(rSdrModel)
+ , fObjectRotation(0.0)
+ , mbAdjustingTextFrameWidthAndHeight(false)
+ , mpLastShadowGeometry(nullptr)
+{
+ m_bClosedObj = true; // custom shapes may be filled
+ mbTextFrame = true;
+}
+
+SdrObjCustomShape::SdrObjCustomShape(SdrModel& rSdrModel, SdrObjCustomShape const & rSource)
+: SdrTextObj(rSdrModel, rSource)
+ , fObjectRotation(0.0)
+ , mbAdjustingTextFrameWidthAndHeight(false)
+ , mpLastShadowGeometry(nullptr)
+{
+ m_bClosedObj = true; // custom shapes may be filled
+ mbTextFrame = true;
+
+ fObjectRotation = rSource.fObjectRotation;
+ mbAdjustingTextFrameWidthAndHeight = rSource.mbAdjustingTextFrameWidthAndHeight;
+ assert(!mbAdjustingTextFrameWidthAndHeight);
+ InvalidateRenderGeometry();
+}
+
+SdrObjCustomShape::~SdrObjCustomShape()
+{
+ // delete buffered display geometry
+ InvalidateRenderGeometry();
+}
+
+void SdrObjCustomShape::MergeDefaultAttributes( const OUString* pType )
+{
+ PropertyValue aPropVal;
+ OUString sShapeType;
+ static const OUStringLiteral sType( u"Type" );
+ SdrCustomShapeGeometryItem aGeometryItem( GetMergedItem( SDRATTR_CUSTOMSHAPE_GEOMETRY ) );
+ if ( pType && !pType->isEmpty() )
+ {
+ sal_Int32 nType = pType->toInt32();
+ if ( nType )
+ sShapeType = EnhancedCustomShapeTypeNames::Get( static_cast< MSO_SPT >( nType ) );
+ else
+ sShapeType = *pType;
+
+ aPropVal.Name = sType;
+ aPropVal.Value <<= sShapeType;
+ aGeometryItem.SetPropertyValue( aPropVal );
+ }
+ else
+ {
+ Any *pAny = aGeometryItem.GetPropertyValueByName( sType );
+ if ( pAny )
+ *pAny >>= sShapeType;
+ }
+ MSO_SPT eSpType = EnhancedCustomShapeTypeNames::Get( sShapeType );
+
+ const sal_Int32* pDefData = nullptr;
+ const mso_CustomShape* pDefCustomShape = GetCustomShapeContent( eSpType );
+ if ( pDefCustomShape )
+ pDefData = pDefCustomShape->pDefData;
+
+ css::uno::Sequence< css::drawing::EnhancedCustomShapeAdjustmentValue > seqAdjustmentValues;
+
+
+ // AdjustmentValues
+
+ static const OUStringLiteral sAdjustmentValues( u"AdjustmentValues" );
+ const Any* pAny = aGeometryItem.GetPropertyValueByName( sAdjustmentValues );
+ if ( pAny )
+ *pAny >>= seqAdjustmentValues;
+ if ( pDefCustomShape && pDefData ) // now check if we have to default some adjustment values
+ {
+ // first check if there are adjustment values are to be appended
+ sal_Int32 i, nAdjustmentValues = seqAdjustmentValues.getLength();
+ sal_Int32 nAdjustmentDefaults = *pDefData++;
+ if ( nAdjustmentDefaults > nAdjustmentValues )
+ seqAdjustmentValues.realloc( nAdjustmentDefaults );
+ auto pseqAdjustmentValues = seqAdjustmentValues.getArray();
+ for ( i = nAdjustmentValues; i < nAdjustmentDefaults; i++ )
+ {
+ pseqAdjustmentValues[ i ].Value <<= pDefData[ i ];
+ pseqAdjustmentValues[ i ].State = css::beans::PropertyState_DIRECT_VALUE;
+ }
+ // check if there are defaulted adjustment values that should be filled the hard coded defaults (pDefValue)
+ sal_Int32 nCount = std::min(nAdjustmentValues, nAdjustmentDefaults);
+ for ( i = 0; i < nCount; i++ )
+ {
+ if ( seqAdjustmentValues[ i ].State != css::beans::PropertyState_DIRECT_VALUE )
+ {
+ pseqAdjustmentValues[ i ].Value <<= pDefData[ i ];
+ pseqAdjustmentValues[ i ].State = css::beans::PropertyState_DIRECT_VALUE;
+ }
+ }
+ }
+ aPropVal.Name = sAdjustmentValues;
+ aPropVal.Value <<= seqAdjustmentValues;
+ aGeometryItem.SetPropertyValue( aPropVal );
+
+
+ // Coordsize
+
+ static const OUStringLiteral sViewBox( u"ViewBox" );
+ const Any* pViewBox = aGeometryItem.GetPropertyValueByName( sViewBox );
+ css::awt::Rectangle aViewBox;
+ if ( !pViewBox || !(*pViewBox >>= aViewBox ) )
+ {
+ if ( pDefCustomShape )
+ {
+ aViewBox.X = 0;
+ aViewBox.Y = 0;
+ aViewBox.Width = pDefCustomShape->nCoordWidth;
+ aViewBox.Height= pDefCustomShape->nCoordHeight;
+ aPropVal.Name = sViewBox;
+ aPropVal.Value <<= aViewBox;
+ aGeometryItem.SetPropertyValue( aPropVal );
+ }
+ }
+
+ static const OUStringLiteral sPath( u"Path" );
+
+
+ // Path/Coordinates
+
+ static const OUStringLiteral sCoordinates( u"Coordinates" );
+ pAny = aGeometryItem.GetPropertyValueByName( sPath, sCoordinates );
+ if ( !pAny && pDefCustomShape && pDefCustomShape->nVertices && pDefCustomShape->pVertices )
+ {
+ sal_Int32 i, nCount = pDefCustomShape->nVertices;
+ css::uno::Sequence< css::drawing::EnhancedCustomShapeParameterPair> seqCoordinates( nCount );
+ auto pseqCoordinates = seqCoordinates.getArray();
+ for ( i = 0; i < nCount; i++ )
+ {
+ EnhancedCustomShape2d::SetEnhancedCustomShapeParameter( pseqCoordinates[ i ].First, pDefCustomShape->pVertices[ i ].nValA );
+ EnhancedCustomShape2d::SetEnhancedCustomShapeParameter( pseqCoordinates[ i ].Second, pDefCustomShape->pVertices[ i ].nValB );
+ }
+ aPropVal.Name = sCoordinates;
+ aPropVal.Value <<= seqCoordinates;
+ aGeometryItem.SetPropertyValue( sPath, aPropVal );
+ }
+
+ // Path/GluePoints
+ static const OUStringLiteral sGluePoints( u"GluePoints" );
+ pAny = aGeometryItem.GetPropertyValueByName( sPath, sGluePoints );
+ if ( !pAny && pDefCustomShape && pDefCustomShape->nGluePoints && pDefCustomShape->pGluePoints )
+ {
+ sal_Int32 i, nCount = pDefCustomShape->nGluePoints;
+ css::uno::Sequence< css::drawing::EnhancedCustomShapeParameterPair> seqGluePoints( nCount );
+ auto pseqGluePoints = seqGluePoints.getArray();
+ for ( i = 0; i < nCount; i++ )
+ {
+ EnhancedCustomShape2d::SetEnhancedCustomShapeParameter( pseqGluePoints[ i ].First, pDefCustomShape->pGluePoints[ i ].nValA );
+ EnhancedCustomShape2d::SetEnhancedCustomShapeParameter( pseqGluePoints[ i ].Second, pDefCustomShape->pGluePoints[ i ].nValB );
+ }
+ aPropVal.Name = sGluePoints;
+ aPropVal.Value <<= seqGluePoints;
+ aGeometryItem.SetPropertyValue( sPath, aPropVal );
+ }
+
+ // Path/Segments
+ static const OUStringLiteral sSegments( u"Segments" );
+ pAny = aGeometryItem.GetPropertyValueByName( sPath, sSegments );
+ if ( !pAny && pDefCustomShape && pDefCustomShape->nElements && pDefCustomShape->pElements )
+ {
+ sal_Int32 i, nCount = pDefCustomShape->nElements;
+ css::uno::Sequence< css::drawing::EnhancedCustomShapeSegment > seqSegments( nCount );
+ auto pseqSegments = seqSegments.getArray();
+ for ( i = 0; i < nCount; i++ )
+ {
+ EnhancedCustomShapeSegment& rSegInfo = pseqSegments[ i ];
+ sal_uInt16 nSDat = pDefCustomShape->pElements[ i ];
+ lcl_ShapeSegmentFromBinary( rSegInfo, nSDat );
+ }
+ aPropVal.Name = sSegments;
+ aPropVal.Value <<= seqSegments;
+ aGeometryItem.SetPropertyValue( sPath, aPropVal );
+ }
+
+ // Path/StretchX
+ static const OUStringLiteral sStretchX( u"StretchX" );
+ pAny = aGeometryItem.GetPropertyValueByName( sPath, sStretchX );
+ if ( !pAny && pDefCustomShape )
+ {
+ sal_Int32 nXRef = pDefCustomShape->nXRef;
+ if ( nXRef != DEFAULT_MINIMUM_SIGNED_COMPARE )
+ {
+ aPropVal.Name = sStretchX;
+ aPropVal.Value <<= nXRef;
+ aGeometryItem.SetPropertyValue( sPath, aPropVal );
+ }
+ }
+
+ // Path/StretchY
+ static const OUStringLiteral sStretchY( u"StretchY" );
+ pAny = aGeometryItem.GetPropertyValueByName( sPath, sStretchY );
+ if ( !pAny && pDefCustomShape )
+ {
+ sal_Int32 nYRef = pDefCustomShape->nYRef;
+ if ( nYRef != DEFAULT_MINIMUM_SIGNED_COMPARE )
+ {
+ aPropVal.Name = sStretchY;
+ aPropVal.Value <<= nYRef;
+ aGeometryItem.SetPropertyValue( sPath, aPropVal );
+ }
+ }
+
+ // Path/TextFrames
+ static const OUStringLiteral sTextFrames( u"TextFrames" );
+ pAny = aGeometryItem.GetPropertyValueByName( sPath, sTextFrames );
+ if ( !pAny && pDefCustomShape && pDefCustomShape->nTextRect && pDefCustomShape->pTextRect )
+ {
+ sal_Int32 i, nCount = pDefCustomShape->nTextRect;
+ css::uno::Sequence< css::drawing::EnhancedCustomShapeTextFrame > seqTextFrames( nCount );
+ auto pseqTextFrames = seqTextFrames.getArray();
+ const SvxMSDffTextRectangles* pRectangles = pDefCustomShape->pTextRect;
+ for ( i = 0; i < nCount; i++, pRectangles++ )
+ {
+ EnhancedCustomShape2d::SetEnhancedCustomShapeParameter( pseqTextFrames[ i ].TopLeft.First, pRectangles->nPairA.nValA );
+ EnhancedCustomShape2d::SetEnhancedCustomShapeParameter( pseqTextFrames[ i ].TopLeft.Second, pRectangles->nPairA.nValB );
+ EnhancedCustomShape2d::SetEnhancedCustomShapeParameter( pseqTextFrames[ i ].BottomRight.First, pRectangles->nPairB.nValA );
+ EnhancedCustomShape2d::SetEnhancedCustomShapeParameter( pseqTextFrames[ i ].BottomRight.Second, pRectangles->nPairB.nValB );
+ }
+ aPropVal.Name = sTextFrames;
+ aPropVal.Value <<= seqTextFrames;
+ aGeometryItem.SetPropertyValue( sPath, aPropVal );
+ }
+
+ // Equations
+ static const OUStringLiteral sEquations( u"Equations" );
+ pAny = aGeometryItem.GetPropertyValueByName( sEquations );
+ if ( !pAny && pDefCustomShape && pDefCustomShape->nCalculation && pDefCustomShape->pCalculation )
+ {
+ sal_Int32 i, nCount = pDefCustomShape->nCalculation;
+ css::uno::Sequence< OUString > seqEquations( nCount );
+ auto pseqEquations = seqEquations.getArray();
+ const SvxMSDffCalculationData* pData = pDefCustomShape->pCalculation;
+ for ( i = 0; i < nCount; i++, pData++ )
+ pseqEquations[ i ] = EnhancedCustomShape2d::GetEquation( pData->nFlags, pData->nVal[ 0 ], pData->nVal[ 1 ], pData->nVal[ 2 ] );
+ aPropVal.Name = sEquations;
+ aPropVal.Value <<= seqEquations;
+ aGeometryItem.SetPropertyValue( aPropVal );
+ }
+
+ // Handles
+ static const OUStringLiteral sHandles( u"Handles" );
+ pAny = aGeometryItem.GetPropertyValueByName( sHandles );
+ if ( !pAny && pDefCustomShape && pDefCustomShape->nHandles && pDefCustomShape->pHandles )
+ {
+ sal_Int32 i, nCount = pDefCustomShape->nHandles;
+ const SvxMSDffHandle* pData = pDefCustomShape->pHandles;
+ css::uno::Sequence< css::beans::PropertyValues > seqHandles( nCount );
+ auto pseqHandles = seqHandles.getArray();
+ for ( i = 0; i < nCount; i++, pData++ )
+ {
+ sal_Int32 nPropertiesNeeded;
+ css::beans::PropertyValues& rPropValues = pseqHandles[ i ];
+ nPropertiesNeeded = GetNumberOfProperties( pData );
+ rPropValues.realloc( nPropertiesNeeded );
+ lcl_ShapePropertiesFromDFF( pData, rPropValues );
+ }
+ aPropVal.Name = sHandles;
+ aPropVal.Value <<= seqHandles;
+ aGeometryItem.SetPropertyValue( aPropVal );
+ }
+ else if (pAny && sShapeType.startsWith("ooxml-") && sShapeType != "ooxml-non-primitive")
+ {
+ // ODF is not able to store the ooxml way of connecting handle to an adjustment
+ // value by name, e.g. attribute RefX="adj". So the information is lost, when exporting
+ // a pptx to odp, for example. This part reconstructs this information for the
+ // ooxml preset shapes from their definition.
+ css::uno::Sequence<css::beans::PropertyValues> seqHandles;
+ *pAny >>= seqHandles;
+ auto seqHandlesRange = asNonConstRange(seqHandles);
+ bool bChanged(false);
+ for (sal_Int32 i = 0; i < seqHandles.getLength(); i++)
+ {
+ comphelper::SequenceAsHashMap aHandleProps(seqHandles[i]);
+ OUString sFirstRefType;
+ sal_Int32 nFirstAdjRef;
+ OUString sSecondRefType;
+ sal_Int32 nSecondAdjRef;
+ PresetOOXHandleAdj::GetOOXHandleAdjRelation(sShapeType, i, sFirstRefType, nFirstAdjRef,
+ sSecondRefType, nSecondAdjRef);
+ if (sFirstRefType != "na" && 0 <= nFirstAdjRef
+ && nFirstAdjRef < seqAdjustmentValues.getLength())
+ {
+ bChanged |= aHandleProps.createItemIfMissing(sFirstRefType, nFirstAdjRef);
+ }
+ if (sSecondRefType != "na" && 0 <= nSecondAdjRef
+ && nSecondAdjRef < seqAdjustmentValues.getLength())
+ {
+ bChanged |= aHandleProps.createItemIfMissing(sSecondRefType, nSecondAdjRef);
+ }
+ aHandleProps >> seqHandlesRange[i];
+ }
+ if (bChanged)
+ {
+ aPropVal.Name = sHandles;
+ aPropVal.Value <<= seqHandles;
+ aGeometryItem.SetPropertyValue(aPropVal);
+ }
+ }
+
+ SetMergedItem( aGeometryItem );
+}
+
+bool SdrObjCustomShape::IsDefaultGeometry( const DefaultType eDefaultType ) const
+{
+ bool bIsDefaultGeometry = false;
+
+ OUString sShapeType;
+ const SdrCustomShapeGeometryItem & rGeometryItem( GetMergedItem( SDRATTR_CUSTOMSHAPE_GEOMETRY ) );
+
+ const Any *pAny = rGeometryItem.GetPropertyValueByName( "Type" );
+ if ( pAny )
+ *pAny >>= sShapeType;
+
+ MSO_SPT eSpType = EnhancedCustomShapeTypeNames::Get( sShapeType );
+
+ const mso_CustomShape* pDefCustomShape = GetCustomShapeContent( eSpType );
+ static const OUStringLiteral sPath( u"Path" );
+ switch( eDefaultType )
+ {
+ case DefaultType::Viewbox :
+ {
+ const Any* pViewBox = rGeometryItem.GetPropertyValueByName( "ViewBox" );
+ css::awt::Rectangle aViewBox;
+ if (pViewBox && (*pViewBox >>= aViewBox) && pDefCustomShape)
+ {
+ if ( ( aViewBox.Width == pDefCustomShape->nCoordWidth )
+ && ( aViewBox.Height == pDefCustomShape->nCoordHeight ) )
+ bIsDefaultGeometry = true;
+ }
+ }
+ break;
+
+ case DefaultType::Path :
+ {
+ pAny = rGeometryItem.GetPropertyValueByName( sPath, "Coordinates" );
+ if ( pAny && pDefCustomShape && pDefCustomShape->nVertices && pDefCustomShape->pVertices )
+ {
+ css::uno::Sequence< css::drawing::EnhancedCustomShapeParameterPair> seqCoordinates1;
+ if ( *pAny >>= seqCoordinates1 )
+ {
+ sal_Int32 i, nCount = pDefCustomShape->nVertices;
+ css::uno::Sequence< css::drawing::EnhancedCustomShapeParameterPair> seqCoordinates2( nCount );
+ auto pseqCoordinates2 = seqCoordinates2.getArray();
+ for ( i = 0; i < nCount; i++ )
+ {
+ EnhancedCustomShape2d::SetEnhancedCustomShapeParameter( pseqCoordinates2[ i ].First, pDefCustomShape->pVertices[ i ].nValA );
+ EnhancedCustomShape2d::SetEnhancedCustomShapeParameter( pseqCoordinates2[ i ].Second, pDefCustomShape->pVertices[ i ].nValB );
+ }
+ if ( seqCoordinates1 == seqCoordinates2 )
+ bIsDefaultGeometry = true;
+ }
+ }
+ else if ( pDefCustomShape && ( ( pDefCustomShape->nVertices == 0 ) || ( pDefCustomShape->pVertices == nullptr ) ) )
+ bIsDefaultGeometry = true;
+ }
+ break;
+
+ case DefaultType::Gluepoints :
+ {
+ pAny = rGeometryItem.GetPropertyValueByName( sPath, "GluePoints" );
+ if ( pAny && pDefCustomShape && pDefCustomShape->nGluePoints && pDefCustomShape->pGluePoints )
+ {
+ css::uno::Sequence< css::drawing::EnhancedCustomShapeParameterPair> seqGluePoints1;
+ if ( *pAny >>= seqGluePoints1 )
+ {
+ sal_Int32 i, nCount = pDefCustomShape->nGluePoints;
+ css::uno::Sequence< css::drawing::EnhancedCustomShapeParameterPair> seqGluePoints2( nCount );
+ auto pseqGluePoints2 = seqGluePoints2.getArray();
+ for ( i = 0; i < nCount; i++ )
+ {
+ EnhancedCustomShape2d::SetEnhancedCustomShapeParameter( pseqGluePoints2[ i ].First, pDefCustomShape->pGluePoints[ i ].nValA );
+ EnhancedCustomShape2d::SetEnhancedCustomShapeParameter( pseqGluePoints2[ i ].Second, pDefCustomShape->pGluePoints[ i ].nValB );
+ }
+ if ( seqGluePoints1 == seqGluePoints2 )
+ bIsDefaultGeometry = true;
+ }
+ }
+ else if ( pDefCustomShape && ( pDefCustomShape->nGluePoints == 0 ) )
+ bIsDefaultGeometry = true;
+ }
+ break;
+
+ case DefaultType::Segments :
+ {
+ // Path/Segments
+ pAny = rGeometryItem.GetPropertyValueByName( sPath, "Segments" );
+ if ( pAny )
+ {
+ css::uno::Sequence< css::drawing::EnhancedCustomShapeSegment > seqSegments1;
+ if ( *pAny >>= seqSegments1 )
+ {
+ if ( pDefCustomShape && pDefCustomShape->nElements && pDefCustomShape->pElements )
+ {
+ sal_Int32 i, nCount = pDefCustomShape->nElements;
+ if ( nCount )
+ {
+ css::uno::Sequence< css::drawing::EnhancedCustomShapeSegment > seqSegments2( nCount );
+ auto pseqSegments2 = seqSegments2.getArray();
+ for ( i = 0; i < nCount; i++ )
+ {
+ EnhancedCustomShapeSegment& rSegInfo = pseqSegments2[ i ];
+ sal_uInt16 nSDat = pDefCustomShape->pElements[ i ];
+ lcl_ShapeSegmentFromBinary( rSegInfo, nSDat );
+ }
+ if ( seqSegments1 == seqSegments2 )
+ bIsDefaultGeometry = true;
+ }
+ }
+ else
+ {
+ // check if it's the default segment description ( M L Z N )
+ if ( seqSegments1.getLength() == 4 )
+ {
+ if ( ( seqSegments1[ 0 ].Command == EnhancedCustomShapeSegmentCommand::MOVETO )
+ && ( seqSegments1[ 1 ].Command == EnhancedCustomShapeSegmentCommand::LINETO )
+ && ( seqSegments1[ 2 ].Command == EnhancedCustomShapeSegmentCommand::CLOSESUBPATH )
+ && ( seqSegments1[ 3 ].Command == EnhancedCustomShapeSegmentCommand::ENDSUBPATH ) )
+ bIsDefaultGeometry = true;
+ }
+ }
+ }
+ }
+ else if ( pDefCustomShape && ( ( pDefCustomShape->nElements == 0 ) || ( pDefCustomShape->pElements == nullptr ) ) )
+ bIsDefaultGeometry = true;
+ }
+ break;
+
+ case DefaultType::StretchX :
+ {
+ pAny = rGeometryItem.GetPropertyValueByName( sPath, "StretchX" );
+ if ( pAny && pDefCustomShape )
+ {
+ sal_Int32 nStretchX = 0;
+ if ( *pAny >>= nStretchX )
+ {
+ if ( pDefCustomShape->nXRef == nStretchX )
+ bIsDefaultGeometry = true;
+ }
+ }
+ else if ( pDefCustomShape && ( pDefCustomShape->nXRef == DEFAULT_MINIMUM_SIGNED_COMPARE ) )
+ bIsDefaultGeometry = true;
+ }
+ break;
+
+ case DefaultType::StretchY :
+ {
+ pAny = rGeometryItem.GetPropertyValueByName( sPath, "StretchY" );
+ if ( pAny && pDefCustomShape )
+ {
+ sal_Int32 nStretchY = 0;
+ if ( *pAny >>= nStretchY )
+ {
+ if ( pDefCustomShape->nYRef == nStretchY )
+ bIsDefaultGeometry = true;
+ }
+ }
+ else if ( pDefCustomShape && ( pDefCustomShape->nYRef == DEFAULT_MINIMUM_SIGNED_COMPARE ) )
+ bIsDefaultGeometry = true;
+ }
+ break;
+
+ case DefaultType::Equations :
+ {
+ pAny = rGeometryItem.GetPropertyValueByName( "Equations" );
+ if ( pAny && pDefCustomShape && pDefCustomShape->nCalculation && pDefCustomShape->pCalculation )
+ {
+ css::uno::Sequence< OUString > seqEquations1;
+ if ( *pAny >>= seqEquations1 )
+ {
+ sal_Int32 i, nCount = pDefCustomShape->nCalculation;
+ css::uno::Sequence< OUString > seqEquations2( nCount );
+ auto pseqEquations2 = seqEquations2.getArray();
+
+ const SvxMSDffCalculationData* pData = pDefCustomShape->pCalculation;
+ for ( i = 0; i < nCount; i++, pData++ )
+ pseqEquations2[ i ] = EnhancedCustomShape2d::GetEquation( pData->nFlags, pData->nVal[ 0 ], pData->nVal[ 1 ], pData->nVal[ 2 ] );
+
+ if ( seqEquations1 == seqEquations2 )
+ bIsDefaultGeometry = true;
+ }
+ }
+ else if ( pDefCustomShape && ( ( pDefCustomShape->nCalculation == 0 ) || ( pDefCustomShape->pCalculation == nullptr ) ) )
+ bIsDefaultGeometry = true;
+ }
+ break;
+
+ case DefaultType::TextFrames :
+ {
+ pAny = rGeometryItem.GetPropertyValueByName( sPath, "TextFrames" );
+ if ( pAny && pDefCustomShape && pDefCustomShape->nTextRect && pDefCustomShape->pTextRect )
+ {
+ css::uno::Sequence< css::drawing::EnhancedCustomShapeTextFrame > seqTextFrames1;
+ if ( *pAny >>= seqTextFrames1 )
+ {
+ sal_Int32 i, nCount = pDefCustomShape->nTextRect;
+ css::uno::Sequence< css::drawing::EnhancedCustomShapeTextFrame > seqTextFrames2( nCount );
+ auto pseqTextFrames2 = seqTextFrames2.getArray();
+ const SvxMSDffTextRectangles* pRectangles = pDefCustomShape->pTextRect;
+ for ( i = 0; i < nCount; i++, pRectangles++ )
+ {
+ EnhancedCustomShape2d::SetEnhancedCustomShapeParameter( pseqTextFrames2[ i ].TopLeft.First, pRectangles->nPairA.nValA );
+ EnhancedCustomShape2d::SetEnhancedCustomShapeParameter( pseqTextFrames2[ i ].TopLeft.Second, pRectangles->nPairA.nValB );
+ EnhancedCustomShape2d::SetEnhancedCustomShapeParameter( pseqTextFrames2[ i ].BottomRight.First, pRectangles->nPairB.nValA );
+ EnhancedCustomShape2d::SetEnhancedCustomShapeParameter( pseqTextFrames2[ i ].BottomRight.Second, pRectangles->nPairB.nValB );
+ }
+ if ( seqTextFrames1 == seqTextFrames2 )
+ bIsDefaultGeometry = true;
+ }
+ }
+ else if ( pDefCustomShape && ( ( pDefCustomShape->nTextRect == 0 ) || ( pDefCustomShape->pTextRect == nullptr ) ) )
+ bIsDefaultGeometry = true;
+ }
+ break;
+ }
+ return bIsDefaultGeometry;
+}
+
+void SdrObjCustomShape::TakeObjInfo(SdrObjTransformInfoRec& rInfo) const
+{
+ rInfo.bResizeFreeAllowed=fObjectRotation == 0.0;
+ rInfo.bResizePropAllowed=true;
+ rInfo.bRotateFreeAllowed=true;
+ rInfo.bRotate90Allowed =true;
+ rInfo.bMirrorFreeAllowed=true;
+ rInfo.bMirror45Allowed =true;
+ rInfo.bMirror90Allowed =true;
+ rInfo.bTransparenceAllowed = false;
+ rInfo.bShearAllowed =true;
+ rInfo.bEdgeRadiusAllowed=false;
+ rInfo.bNoContortion =true;
+
+ // #i37011#
+ if ( !mXRenderedCustomShape.is() )
+ return;
+
+ const SdrObject* pRenderedCustomShape = SdrObject::getSdrObjectFromXShape( mXRenderedCustomShape );
+ if ( !pRenderedCustomShape )
+ return;
+
+ // #i37262#
+ // Iterate self over the contained objects, since there are combinations of
+ // polygon and curve objects. In that case, aInfo.bCanConvToPath and
+ // aInfo.bCanConvToPoly would be false. What is needed here is an or, not an and.
+ SdrObjListIter aIterator(*pRenderedCustomShape);
+ while(aIterator.IsMore())
+ {
+ SdrObject* pCandidate = aIterator.Next();
+ SdrObjTransformInfoRec aInfo;
+ pCandidate->TakeObjInfo(aInfo);
+
+ // set path and poly conversion if one is possible since
+ // this object will first be broken
+ const bool bCanConvToPathOrPoly(aInfo.bCanConvToPath || aInfo.bCanConvToPoly);
+ if(rInfo.bCanConvToPath != bCanConvToPathOrPoly)
+ {
+ rInfo.bCanConvToPath = bCanConvToPathOrPoly;
+ }
+
+ if(rInfo.bCanConvToPoly != bCanConvToPathOrPoly)
+ {
+ rInfo.bCanConvToPoly = bCanConvToPathOrPoly;
+ }
+
+ if(rInfo.bCanConvToContour != aInfo.bCanConvToContour)
+ {
+ rInfo.bCanConvToContour = aInfo.bCanConvToContour;
+ }
+
+ if(rInfo.bShearAllowed != aInfo.bShearAllowed)
+ {
+ rInfo.bShearAllowed = aInfo.bShearAllowed;
+ }
+ }
+}
+
+SdrObjKind SdrObjCustomShape::GetObjIdentifier() const
+{
+ return SdrObjKind::CustomShape;
+}
+
+// #115391# This implementation is based on the TextFrame size of the CustomShape and the
+// state of the ResizeShapeToFitText flag to correctly set TextMinFrameWidth/Height
+void SdrObjCustomShape::AdaptTextMinSize()
+{
+ if (getSdrModelFromSdrObject().IsCreatingDataObj() || getSdrModelFromSdrObject().IsPasteResize())
+ return;
+
+ // check if we need to change anything before creating an SfxItemSet, because that is expensive
+ const bool bResizeShapeToFitText(GetObjectItem(SDRATTR_TEXT_AUTOGROWHEIGHT).GetValue());
+ tools::Rectangle aTextBound(maRect);
+ bool bChanged(false);
+ if(bResizeShapeToFitText)
+ bChanged = true;
+ else if(GetTextBounds(aTextBound))
+ bChanged = true;
+ if (!bChanged)
+ return;
+
+ SfxItemSetFixed<SDRATTR_TEXT_MINFRAMEHEIGHT, SDRATTR_TEXT_AUTOGROWHEIGHT,
+ SDRATTR_TEXT_MINFRAMEWIDTH, SDRATTR_TEXT_AUTOGROWWIDTH> // contains SDRATTR_TEXT_MAXFRAMEWIDTH
+ aSet(*GetObjectItemSet().GetPool());
+
+ if(bResizeShapeToFitText)
+ {
+ // always reset MinWidthHeight to zero to only rely on text size and frame size
+ // to allow resizing being completely dependent on text size only
+ aSet.Put(makeSdrTextMinFrameWidthItem(0));
+ aSet.Put(makeSdrTextMinFrameHeightItem(0));
+ }
+ else
+ {
+ // recreate from CustomShape-specific TextBounds
+ const tools::Long nHDist(GetTextLeftDistance() + GetTextRightDistance());
+ const tools::Long nVDist(GetTextUpperDistance() + GetTextLowerDistance());
+ const tools::Long nTWdt(std::max(tools::Long(0), static_cast<tools::Long>(aTextBound.GetWidth() - 1 - nHDist)));
+ const tools::Long nTHgt(std::max(tools::Long(0), static_cast<tools::Long>(aTextBound.GetHeight() - 1 - nVDist)));
+
+ aSet.Put(makeSdrTextMinFrameWidthItem(nTWdt));
+ aSet.Put(makeSdrTextMinFrameHeightItem(nTHgt));
+ }
+
+ SetObjectItemSet(aSet);
+}
+
+void SdrObjCustomShape::NbcSetSnapRect( const tools::Rectangle& rRect )
+{
+ maRect = rRect;
+ ImpJustifyRect(maRect);
+ InvalidateRenderGeometry();
+
+ AdaptTextMinSize();
+
+ ImpCheckShear();
+ SetBoundAndSnapRectsDirty();
+ SetChanged();
+}
+
+void SdrObjCustomShape::SetSnapRect( const tools::Rectangle& rRect )
+{
+ tools::Rectangle aBoundRect0;
+ if ( m_pUserCall )
+ aBoundRect0 = GetLastBoundRect();
+ NbcSetSnapRect( rRect );
+ BroadcastObjectChange();
+ SendUserCall(SdrUserCallType::Resize,aBoundRect0);
+}
+
+void SdrObjCustomShape::NbcSetLogicRect( const tools::Rectangle& rRect )
+{
+ maRect = rRect;
+ ImpJustifyRect(maRect);
+ InvalidateRenderGeometry();
+
+ AdaptTextMinSize();
+
+ SetBoundAndSnapRectsDirty();
+ SetChanged();
+}
+
+void SdrObjCustomShape::SetLogicRect( const tools::Rectangle& rRect )
+{
+ tools::Rectangle aBoundRect0;
+ if ( m_pUserCall )
+ aBoundRect0 = GetLastBoundRect();
+ NbcSetLogicRect(rRect);
+ BroadcastObjectChange();
+ SendUserCall(SdrUserCallType::Resize,aBoundRect0);
+}
+
+void SdrObjCustomShape::Move( const Size& rSiz )
+{
+ if ( rSiz.Width() || rSiz.Height() )
+ {
+ tools::Rectangle aBoundRect0;
+ if ( m_pUserCall )
+ aBoundRect0 = GetLastBoundRect();
+ NbcMove(rSiz);
+ SetChanged();
+ BroadcastObjectChange();
+ SendUserCall(SdrUserCallType::MoveOnly,aBoundRect0);
+ }
+}
+void SdrObjCustomShape::NbcMove( const Size& rSiz )
+{
+ SdrTextObj::NbcMove( rSiz );
+ if ( mXRenderedCustomShape.is() )
+ {
+ SdrObject* pRenderedCustomShape = SdrObject::getSdrObjectFromXShape(mXRenderedCustomShape);
+ if ( pRenderedCustomShape )
+ {
+ // #i97149# the visualisation shape needs to be informed
+ // about change, too
+ pRenderedCustomShape->ActionChanged();
+ pRenderedCustomShape->NbcMove( rSiz );
+ }
+ }
+
+ // #i37011# adapt geometry shadow
+ if(mpLastShadowGeometry)
+ {
+ mpLastShadowGeometry->NbcMove( rSiz );
+ }
+}
+
+void SdrObjCustomShape::NbcResize( const Point& rRef, const Fraction& rxFact, const Fraction& ryFact )
+{
+ // taking care of handles that should not been changed
+ tools::Rectangle aOld( maRect );
+ std::vector< SdrCustomShapeInteraction > aInteractionHandles( GetInteractionHandles() );
+
+ SdrTextObj::NbcResize( rRef, rxFact, ryFact );
+
+ if ( ( rxFact.GetNumerator() != rxFact.GetDenominator() )
+ || ( ryFact.GetNumerator()!= ryFact.GetDenominator() ) )
+ {
+ if ( ( ( rxFact.GetNumerator() < 0 ) && ( rxFact.GetDenominator() > 0 ) ) ||
+ ( ( rxFact.GetNumerator() > 0 ) && ( rxFact.GetDenominator() < 0 ) ) )
+ {
+ SetMirroredX( !IsMirroredX() );
+ }
+ if ( ( ( ryFact.GetNumerator() < 0 ) && ( ryFact.GetDenominator() > 0 ) ) ||
+ ( ( ryFact.GetNumerator() > 0 ) && ( ryFact.GetDenominator() < 0 ) ) )
+ {
+ SetMirroredY( !IsMirroredY() );
+ }
+ }
+
+ for (const auto& rInteraction : aInteractionHandles)
+ {
+ try
+ {
+ if ( rInteraction.nMode & CustomShapeHandleModes::RESIZE_FIXED )
+ rInteraction.xInteraction->setControllerPosition( rInteraction.aPosition );
+ if ( rInteraction.nMode & CustomShapeHandleModes::RESIZE_ABSOLUTE_X )
+ {
+ sal_Int32 nX = ( rInteraction.aPosition.X - aOld.Left() ) + maRect.Left();
+ rInteraction.xInteraction->setControllerPosition( css::awt::Point( nX, rInteraction.xInteraction->getPosition().Y ) );
+ }
+ else if ( rInteraction.nMode & CustomShapeHandleModes::RESIZE_ABSOLUTE_NEGX )
+ {
+ sal_Int32 nX = maRect.Right() - (aOld.Right() - rInteraction.aPosition.X);
+ rInteraction.xInteraction->setControllerPosition( css::awt::Point( nX, rInteraction.xInteraction->getPosition().Y ) );
+ }
+ if ( rInteraction.nMode & CustomShapeHandleModes::RESIZE_ABSOLUTE_Y )
+ {
+ sal_Int32 nY = ( rInteraction.aPosition.Y - aOld.Top() ) + maRect.Top();
+ rInteraction.xInteraction->setControllerPosition( css::awt::Point( rInteraction.xInteraction->getPosition().X, nY ) );
+ }
+ }
+ catch ( const uno::RuntimeException& )
+ {
+ }
+ }
+
+ // updating fObjectRotation
+ Degree100 nTextObjRotation = maGeo.nRotationAngle;
+ double fAngle = toDegrees(nTextObjRotation);
+ if (IsMirroredX())
+ {
+ if (IsMirroredY())
+ fObjectRotation = fAngle - 180.0;
+ else
+ fObjectRotation = -fAngle;
+ }
+ else
+ {
+ if (IsMirroredY())
+ fObjectRotation = 180.0 - fAngle;
+ else
+ fObjectRotation = fAngle;
+ }
+ while (fObjectRotation < 0)
+ fObjectRotation += 360.0;
+ while (fObjectRotation >= 360.0)
+ fObjectRotation -= 360.0;
+
+ InvalidateRenderGeometry();
+}
+
+void SdrObjCustomShape::NbcRotate( const Point& rRef, Degree100 nAngle, double sn, double cs )
+{
+ bool bMirroredX = IsMirroredX();
+ bool bMirroredY = IsMirroredY();
+
+ fObjectRotation = fmod( fObjectRotation, 360.0 );
+ if ( fObjectRotation < 0 )
+ fObjectRotation = 360 + fObjectRotation;
+
+ // the rotation angle for ashapes is stored in fObjectRotation, this rotation
+ // has to be applied to the text object (which is internally using maGeo.nAngle).
+ SdrTextObj::NbcRotate( maRect.TopLeft(), -maGeo.nRotationAngle, // retrieving the unrotated text object
+ -maGeo.mfSinRotationAngle,
+ maGeo.mfCosRotationAngle );
+ maGeo.nRotationAngle = 0_deg100; // resetting aGeo data
+ maGeo.RecalcSinCos();
+
+ Degree100 nW(static_cast<sal_Int32>( fObjectRotation * 100 )); // applying our object rotation
+ if ( bMirroredX )
+ nW = 36000_deg100 - nW;
+ if ( bMirroredY )
+ nW = 18000_deg100 - nW;
+ nW = nW % 36000_deg100;
+ if ( nW < 0_deg100 )
+ nW = 36000_deg100 + nW;
+ SdrTextObj::NbcRotate( maRect.TopLeft(), nW, // applying text rotation
+ sin( toRadians(nW) ),
+ cos( toRadians(nW) ) );
+
+ int nSwap = 0;
+ if ( bMirroredX )
+ nSwap ^= 1;
+ if ( bMirroredY )
+ nSwap ^= 1;
+
+ double fAngle = toDegrees(nAngle); // updating to our new object rotation
+ fObjectRotation = fmod( nSwap ? fObjectRotation - fAngle : fObjectRotation + fAngle, 360.0 );
+ if ( fObjectRotation < 0 )
+ fObjectRotation = 360 + fObjectRotation;
+
+ SdrTextObj::NbcRotate( rRef, nAngle, sn, cs ); // applying text rotation
+ InvalidateRenderGeometry();
+}
+
+void SdrObjCustomShape::NbcMirror( const Point& rRef1, const Point& rRef2 )
+{
+ // TTTT: Fix for old mirroring, can be removed again in aw080
+ // storing horizontal and vertical flipping without modifying the rotate angle
+ // decompose other flipping to rotation and MirrorX.
+ tools::Long ndx = rRef2.X()-rRef1.X();
+ tools::Long ndy = rRef2.Y()-rRef1.Y();
+
+ if(!ndx) // MirroredX
+ {
+ SetMirroredX(!IsMirroredX());
+ SdrTextObj::NbcMirror( rRef1, rRef2 );
+ }
+ else
+ {
+ if(!ndy) // MirroredY
+ {
+ SetMirroredY(!IsMirroredY());
+ SdrTextObj::NbcMirror( rRef1, rRef2 );
+ }
+ else // neither horizontal nor vertical
+ {
+ SetMirroredX(!IsMirroredX());
+
+ // call parent
+ SdrTextObj::NbcMirror( rRef1, rRef2 );
+
+ // update fObjectRotation
+ Degree100 nTextObjRotation = maGeo.nRotationAngle;
+ double fAngle = toDegrees(nTextObjRotation);
+
+ bool bSingleFlip = (IsMirroredX()!= IsMirroredY());
+
+ fObjectRotation = fmod( bSingleFlip ? -fAngle : fAngle, 360.0 );
+
+ if ( fObjectRotation < 0 )
+ {
+ fObjectRotation = 360.0 + fObjectRotation;
+ }
+ }
+ }
+
+ InvalidateRenderGeometry();
+}
+
+void SdrObjCustomShape::Shear( const Point& rRef, Degree100 nAngle, double tn, bool bVShear )
+{
+ SdrTextObj::Shear( rRef, nAngle, tn, bVShear );
+ InvalidateRenderGeometry();
+}
+void SdrObjCustomShape::NbcShear( const Point& rRef, Degree100 nAngle, double tn, bool bVShear )
+{
+ // TTTT: Fix for old mirroring, can be removed again in aw080
+ SdrTextObj::NbcShear(rRef,nAngle,tn,bVShear);
+
+ // updating fObjectRotation
+ Degree100 nTextObjRotation = maGeo.nRotationAngle;
+ double fAngle = toDegrees(nTextObjRotation);
+ if (IsMirroredX())
+ {
+ if (IsMirroredY())
+ fObjectRotation = fAngle - 180.0;
+ else
+ fObjectRotation = -fAngle;
+ }
+ else
+ {
+ if (IsMirroredY())
+ fObjectRotation = 180.0 - fAngle;
+ else
+ fObjectRotation = fAngle;
+ }
+ while (fObjectRotation < 0)
+ fObjectRotation += 360.0;
+ while (fObjectRotation >= 360.0)
+ fObjectRotation -= 360.0;
+
+ InvalidateRenderGeometry();
+}
+
+SdrGluePoint SdrObjCustomShape::GetVertexGluePoint(sal_uInt16 nPosNum) const
+{
+ sal_Int32 nWdt = ImpGetLineWdt(); // #i25616#
+
+ // #i25616#
+ if(!LineIsOutsideGeometry())
+ {
+ nWdt++;
+ nWdt /= 2;
+ }
+
+ Point aPt;
+ switch (nPosNum) {
+ case 0: aPt=maRect.TopCenter(); aPt.AdjustY( -nWdt ); break;
+ case 1: aPt=maRect.RightCenter(); aPt.AdjustX(nWdt ); break;
+ case 2: aPt=maRect.BottomCenter(); aPt.AdjustY(nWdt ); break;
+ case 3: aPt=maRect.LeftCenter(); aPt.AdjustX( -nWdt ); break;
+ }
+ if (maGeo.nShearAngle != 0_deg100) ShearPoint(aPt, maRect.TopLeft(), maGeo.mfTanShearAngle);
+ if (maGeo.nRotationAngle != 0_deg100) RotatePoint(aPt, maRect.TopLeft(), maGeo.mfSinRotationAngle, maGeo.mfCosRotationAngle);
+ aPt-=GetSnapRect().Center();
+ SdrGluePoint aGP(aPt);
+ aGP.SetPercent(false);
+ return aGP;
+}
+
+
+// #i38892#
+void SdrObjCustomShape::ImpCheckCustomGluePointsAreAdded()
+{
+ const SdrObject* pSdrObject = GetSdrObjectFromCustomShape();
+
+ if(!pSdrObject)
+ return;
+
+ const SdrGluePointList* pSource = pSdrObject->GetGluePointList();
+
+ if(!(pSource && pSource->GetCount()))
+ return;
+
+ if(!SdrTextObj::GetGluePointList())
+ {
+ SdrTextObj::ForceGluePointList();
+ }
+
+ const SdrGluePointList* pList = SdrTextObj::GetGluePointList();
+
+ if(!pList)
+ return;
+
+ SdrGluePointList aNewList;
+ sal_uInt16 a;
+
+ for(a = 0; a < pSource->GetCount(); a++)
+ {
+ SdrGluePoint aCopy((*pSource)[a]);
+ aCopy.SetUserDefined(false);
+ aNewList.Insert(aCopy);
+ }
+
+ bool bMirroredX = IsMirroredX();
+ bool bMirroredY = IsMirroredY();
+
+ Degree100 nShearAngle = maGeo.nShearAngle;
+ double fTan = maGeo.mfTanShearAngle;
+
+ if (maGeo.nRotationAngle || nShearAngle || bMirroredX || bMirroredY)
+ {
+ tools::Polygon aPoly( maRect );
+ if( nShearAngle )
+ {
+ sal_uInt16 nPointCount=aPoly.GetSize();
+ for (sal_uInt16 i=0; i<nPointCount; i++)
+ ShearPoint(aPoly[i],maRect.Center(), fTan );
+ }
+ if (maGeo.nRotationAngle)
+ aPoly.Rotate( maRect.Center(), to<Degree10>(maGeo.nRotationAngle) );
+
+ tools::Rectangle aBoundRect( aPoly.GetBoundRect() );
+ sal_Int32 nXDiff = aBoundRect.Left() - maRect.Left();
+ sal_Int32 nYDiff = aBoundRect.Top() - maRect.Top();
+
+ if (nShearAngle && bMirroredX != bMirroredY)
+ {
+ nShearAngle = -nShearAngle;
+ fTan = -fTan;
+ }
+
+ Point aRef( maRect.GetWidth() / 2, maRect.GetHeight() / 2 );
+ for ( a = 0; a < aNewList.GetCount(); a++ )
+ {
+ SdrGluePoint& rPoint = aNewList[ a ];
+ Point aGlue( rPoint.GetPos() );
+ if ( nShearAngle )
+ ShearPoint( aGlue, aRef, fTan );
+
+ RotatePoint(aGlue, aRef, sin(basegfx::deg2rad(fObjectRotation)),
+ cos(basegfx::deg2rad(fObjectRotation)));
+ if ( bMirroredX )
+ aGlue.setX( maRect.GetWidth() - aGlue.X() );
+ if ( bMirroredY )
+ aGlue.setY( maRect.GetHeight() - aGlue.Y() );
+ aGlue.AdjustX( -nXDiff );
+ aGlue.AdjustY( -nYDiff );
+ rPoint.SetPos( aGlue );
+ }
+ }
+
+ for(a = 0; a < pList->GetCount(); a++)
+ {
+ const SdrGluePoint& rCandidate = (*pList)[a];
+
+ if(rCandidate.IsUserDefined())
+ {
+ aNewList.Insert(rCandidate);
+ }
+ }
+
+ // copy new list to local. This is NOT very convenient behavior, the local
+ // GluePointList should not be set, but we delivered by using GetGluePointList(),
+ // maybe on demand. Since the local object is changed here, this is assumed to
+ // be a result of GetGluePointList and thus the list is copied
+ if(m_pPlusData)
+ {
+ m_pPlusData->SetGluePoints(aNewList);
+ }
+}
+
+// #i38892#
+const SdrGluePointList* SdrObjCustomShape::GetGluePointList() const
+{
+ const_cast<SdrObjCustomShape*>(this)->ImpCheckCustomGluePointsAreAdded();
+ return SdrTextObj::GetGluePointList();
+}
+
+// #i38892#
+SdrGluePointList* SdrObjCustomShape::ForceGluePointList()
+{
+ if(SdrTextObj::ForceGluePointList())
+ {
+ ImpCheckCustomGluePointsAreAdded();
+ return SdrTextObj::ForceGluePointList();
+ }
+ else
+ {
+ return nullptr;
+ }
+}
+
+
+sal_uInt32 SdrObjCustomShape::GetHdlCount() const
+{
+ const sal_uInt32 nBasicHdlCount(SdrTextObj::GetHdlCount());
+ return ( GetInteractionHandles().size() + nBasicHdlCount );
+}
+
+void SdrObjCustomShape::AddToHdlList(SdrHdlList& rHdlList) const
+{
+ SdrTextObj::AddToHdlList(rHdlList);
+
+ int nCustomShapeHdlNum = 0;
+ for (SdrCustomShapeInteraction const & rInteraction : GetInteractionHandles())
+ {
+ if ( rInteraction.xInteraction.is() )
+ {
+ try
+ {
+ css::awt::Point aPosition( rInteraction.xInteraction->getPosition() );
+ std::unique_ptr<SdrHdl> pH(new SdrHdl( Point( aPosition.X, aPosition.Y ), SdrHdlKind::CustomShape1 ));
+ pH->SetPointNum( nCustomShapeHdlNum );
+ pH->SetObj( const_cast<SdrObjCustomShape*>(this) );
+ rHdlList.AddHdl(std::move(pH));
+ }
+ catch ( const uno::RuntimeException& )
+ {
+ }
+ }
+ ++nCustomShapeHdlNum;
+ }
+}
+
+bool SdrObjCustomShape::hasSpecialDrag() const
+{
+ return true;
+}
+
+bool SdrObjCustomShape::beginSpecialDrag(SdrDragStat& rDrag) const
+{
+ const SdrHdl* pHdl = rDrag.GetHdl();
+
+ if(pHdl && SdrHdlKind::CustomShape1 == pHdl->GetKind())
+ {
+ rDrag.SetEndDragChangesAttributes(true);
+ rDrag.SetNoSnap();
+ }
+ else
+ {
+ const SdrHdl* pHdl2 = rDrag.GetHdl();
+ const SdrHdlKind eHdl((pHdl2 == nullptr) ? SdrHdlKind::Move : pHdl2->GetKind());
+
+ switch( eHdl )
+ {
+ case SdrHdlKind::UpperLeft :
+ case SdrHdlKind::Upper :
+ case SdrHdlKind::UpperRight :
+ case SdrHdlKind::Left :
+ case SdrHdlKind::Right :
+ case SdrHdlKind::LowerLeft :
+ case SdrHdlKind::Lower :
+ case SdrHdlKind::LowerRight :
+ case SdrHdlKind::Move :
+ {
+ break;
+ }
+ default:
+ {
+ return false;
+ }
+ }
+ }
+
+ return true;
+}
+
+void SdrObjCustomShape::DragResizeCustomShape( const tools::Rectangle& rNewRect )
+{
+ tools::Rectangle aOld( maRect );
+ bool bOldMirroredX( IsMirroredX() );
+ bool bOldMirroredY( IsMirroredY() );
+
+ tools::Rectangle aNewRect( rNewRect );
+ aNewRect.Justify();
+
+ std::vector< SdrCustomShapeInteraction > aInteractionHandles( GetInteractionHandles() );
+
+ GeoStat aGeoStat( GetGeoStat() );
+ if ( aNewRect.TopLeft()!= maRect.TopLeft() &&
+ ( maGeo.nRotationAngle || maGeo.nShearAngle ) )
+ {
+ Point aNewPos( aNewRect.TopLeft() );
+ if ( maGeo.nShearAngle ) ShearPoint( aNewPos, aOld.TopLeft(), aGeoStat.mfTanShearAngle );
+ if ( maGeo.nRotationAngle ) RotatePoint(aNewPos, aOld.TopLeft(), aGeoStat.mfSinRotationAngle, aGeoStat.mfCosRotationAngle );
+ aNewRect.SetPos( aNewPos );
+ }
+ if ( aNewRect == maRect )
+ return;
+
+ SetLogicRect( aNewRect );
+ InvalidateRenderGeometry();
+
+ if ( rNewRect.Left() > rNewRect.Right() )
+ {
+ Point aTop( ( GetSnapRect().Left() + GetSnapRect().Right() ) >> 1, GetSnapRect().Top() );
+ Point aBottom( aTop.X(), aTop.Y() + 1000 );
+ NbcMirror( aTop, aBottom );
+ }
+ if ( rNewRect.Top() > rNewRect.Bottom() )
+ {
+ Point aLeft( GetSnapRect().Left(), ( GetSnapRect().Top() + GetSnapRect().Bottom() ) >> 1 );
+ Point aRight( aLeft.X() + 1000, aLeft.Y() );
+ NbcMirror( aLeft, aRight );
+ }
+
+ for (const auto& rInteraction : aInteractionHandles)
+ {
+ try
+ {
+ if ( rInteraction.nMode & CustomShapeHandleModes::RESIZE_FIXED )
+ rInteraction.xInteraction->setControllerPosition( rInteraction.aPosition );
+ if ( rInteraction.nMode & CustomShapeHandleModes::RESIZE_ABSOLUTE_X ||
+ rInteraction.nMode & CustomShapeHandleModes::RESIZE_ABSOLUTE_NEGX )
+ {
+ if (rInteraction.nMode & CustomShapeHandleModes::RESIZE_ABSOLUTE_NEGX)
+ bOldMirroredX = !bOldMirroredX;
+
+ sal_Int32 nX;
+ if ( bOldMirroredX )
+ {
+ nX = ( rInteraction.aPosition.X - aOld.Right() );
+ if ( rNewRect.Left() > rNewRect.Right() )
+ nX = maRect.Left() - nX;
+ else
+ nX += maRect.Right();
+ }
+ else
+ {
+ nX = ( rInteraction.aPosition.X - aOld.Left() );
+ if ( rNewRect.Left() > rNewRect.Right() )
+ nX = maRect.Right() - nX;
+ else
+ nX += maRect.Left();
+ }
+ rInteraction.xInteraction->setControllerPosition( css::awt::Point( nX, rInteraction.xInteraction->getPosition().Y ) );
+ }
+ if ( rInteraction.nMode & CustomShapeHandleModes::RESIZE_ABSOLUTE_Y )
+ {
+ sal_Int32 nY;
+ if ( bOldMirroredY )
+ {
+ nY = ( rInteraction.aPosition.Y - aOld.Bottom() );
+ if ( rNewRect.Top() > rNewRect.Bottom() )
+ nY = maRect.Top() - nY;
+ else
+ nY += maRect.Bottom();
+ }
+ else
+ {
+ nY = ( rInteraction.aPosition.Y - aOld.Top() );
+ if ( rNewRect.Top() > rNewRect.Bottom() )
+ nY = maRect.Bottom() - nY;
+ else
+ nY += maRect.Top();
+ }
+ rInteraction.xInteraction->setControllerPosition( css::awt::Point( rInteraction.xInteraction->getPosition().X, nY ) );
+ }
+ }
+ catch ( const uno::RuntimeException& )
+ {
+ }
+ }
+}
+
+void SdrObjCustomShape::DragMoveCustomShapeHdl( const Point& rDestination,
+ const sal_uInt16 nCustomShapeHdlNum, bool bMoveCalloutRectangle )
+{
+ std::vector< SdrCustomShapeInteraction > aInteractionHandles( GetInteractionHandles() );
+ if ( nCustomShapeHdlNum >= aInteractionHandles.size() )
+ return;
+
+ SdrCustomShapeInteraction aInteractionHandle( aInteractionHandles[ nCustomShapeHdlNum ] );
+ if ( !aInteractionHandle.xInteraction.is() )
+ return;
+
+ try
+ {
+ css::awt::Point aPt( rDestination.X(), rDestination.Y() );
+ if ( aInteractionHandle.nMode & CustomShapeHandleModes::MOVE_SHAPE && bMoveCalloutRectangle )
+ {
+ sal_Int32 nXDiff = aPt.X - aInteractionHandle.aPosition.X;
+ sal_Int32 nYDiff = aPt.Y - aInteractionHandle.aPosition.Y;
+
+ maRect.Move( nXDiff, nYDiff );
+ m_aOutRect.Move( nXDiff, nYDiff );
+ maSnapRect.Move( nXDiff, nYDiff );
+ SetBoundAndSnapRectsDirty(/*bNotMyself*/true);
+ InvalidateRenderGeometry();
+
+ for (const auto& rInteraction : aInteractionHandles)
+ {
+ if ( rInteraction.nMode & CustomShapeHandleModes::RESIZE_FIXED )
+ {
+ if ( rInteraction.xInteraction.is() )
+ rInteraction.xInteraction->setControllerPosition( rInteraction.aPosition );
+ }
+ }
+ }
+ aInteractionHandle.xInteraction->setControllerPosition( aPt );
+ }
+ catch ( const uno::RuntimeException& )
+ {
+ }
+}
+
+bool SdrObjCustomShape::applySpecialDrag(SdrDragStat& rDrag)
+{
+ const SdrHdl* pHdl = rDrag.GetHdl();
+ const SdrHdlKind eHdl((pHdl == nullptr) ? SdrHdlKind::Move : pHdl->GetKind());
+
+ switch(eHdl)
+ {
+ case SdrHdlKind::CustomShape1 :
+ {
+ rDrag.SetEndDragChangesGeoAndAttributes(true);
+ DragMoveCustomShapeHdl( rDrag.GetNow(), static_cast<sal_uInt16>(pHdl->GetPointNum()), !rDrag.GetDragMethod()->IsShiftPressed() );
+ SetBoundAndSnapRectsDirty();
+ InvalidateRenderGeometry();
+ SetChanged();
+ break;
+ }
+
+ case SdrHdlKind::UpperLeft :
+ case SdrHdlKind::Upper :
+ case SdrHdlKind::UpperRight :
+ case SdrHdlKind::Left :
+ case SdrHdlKind::Right :
+ case SdrHdlKind::LowerLeft :
+ case SdrHdlKind::Lower :
+ case SdrHdlKind::LowerRight :
+ {
+ DragResizeCustomShape( ImpDragCalcRect(rDrag) );
+ break;
+ }
+ case SdrHdlKind::Move :
+ {
+ Move(Size(rDrag.GetDX(), rDrag.GetDY()));
+ break;
+ }
+ default: break;
+ }
+
+ return true;
+}
+
+
+void SdrObjCustomShape::DragCreateObject( SdrDragStat& rStat )
+{
+ tools::Rectangle aRect1;
+ rStat.TakeCreateRect( aRect1 );
+
+ std::vector< SdrCustomShapeInteraction > aInteractionHandles( GetInteractionHandles() );
+
+ constexpr sal_uInt32 nDefaultObjectSizeWidth = 3000; // default width from SDOptions ?
+ constexpr sal_uInt32 nDefaultObjectSizeHeight= 3000;
+
+ if ( ImpVerticalSwitch( *this ) )
+ {
+ SetMirroredX( aRect1.Left() > aRect1.Right() );
+
+ aRect1 = tools::Rectangle( rStat.GetNow(), Size( nDefaultObjectSizeWidth, nDefaultObjectSizeHeight ) );
+ // subtracting the horizontal difference of the latest handle from shape position
+ if ( !aInteractionHandles.empty() )
+ {
+ sal_Int32 nHandlePos = aInteractionHandles[ aInteractionHandles.size() - 1 ].xInteraction->getPosition().X;
+ aRect1.Move( maRect.Left() - nHandlePos, 0 );
+ }
+ }
+ ImpJustifyRect( aRect1 );
+ rStat.SetActionRect( aRect1 );
+ maRect = aRect1;
+ SetBoundAndSnapRectsDirty();
+
+ for (const auto& rInteraction : aInteractionHandles)
+ {
+ try
+ {
+ if ( rInteraction.nMode & CustomShapeHandleModes::CREATE_FIXED )
+ rInteraction.xInteraction->setControllerPosition( awt::Point( rStat.GetStart().X(), rStat.GetStart().Y() ) );
+ }
+ catch ( const uno::RuntimeException& )
+ {
+ }
+ }
+
+ SetBoundRectDirty();
+ m_bSnapRectDirty=true;
+}
+
+bool SdrObjCustomShape::MovCreate(SdrDragStat& rStat)
+{
+ SdrView* pView = rStat.GetView(); // #i37448#
+ if( pView && pView->IsSolidDragging() )
+ {
+ InvalidateRenderGeometry();
+ }
+ DragCreateObject( rStat );
+ SetBoundAndSnapRectsDirty();
+ return true;
+}
+
+bool SdrObjCustomShape::EndCreate( SdrDragStat& rStat, SdrCreateCmd eCmd )
+{
+ DragCreateObject( rStat );
+
+ AdaptTextMinSize();
+
+ SetBoundAndSnapRectsDirty();
+ return ( eCmd == SdrCreateCmd::ForceEnd || rStat.GetPointCount() >= 2 );
+}
+
+basegfx::B2DPolyPolygon SdrObjCustomShape::TakeCreatePoly(const SdrDragStat& /*rDrag*/) const
+{
+ return GetLineGeometry( false );
+}
+
+
+// in context with the SdrObjCustomShape the SdrTextAutoGrowHeightItem == true -> Resize Shape to fit text,
+// the SdrTextAutoGrowWidthItem == true -> Word wrap text in Shape
+bool SdrObjCustomShape::IsAutoGrowHeight() const
+{
+ const SfxItemSet& rSet = GetMergedItemSet();
+ bool bIsAutoGrowHeight = rSet.Get(SDRATTR_TEXT_AUTOGROWHEIGHT).GetValue();
+ if ( bIsAutoGrowHeight && IsVerticalWriting() )
+ bIsAutoGrowHeight = !rSet.Get(SDRATTR_TEXT_WORDWRAP).GetValue();
+ return bIsAutoGrowHeight;
+}
+bool SdrObjCustomShape::IsAutoGrowWidth() const
+{
+ const SfxItemSet& rSet = GetMergedItemSet();
+ bool bIsAutoGrowWidth = rSet.Get(SDRATTR_TEXT_AUTOGROWHEIGHT).GetValue();
+ if ( bIsAutoGrowWidth && !IsVerticalWriting() )
+ bIsAutoGrowWidth = !rSet.Get(SDRATTR_TEXT_WORDWRAP).GetValue();
+ return bIsAutoGrowWidth;
+}
+
+/* The following method is identical to the SdrTextObj::SetVerticalWriting method, the only difference
+ is that the SdrAutoGrowWidthItem and SdrAutoGrowHeightItem are not exchanged if the vertical writing
+ mode has been changed */
+
+void SdrObjCustomShape::SetVerticalWriting( bool bVertical )
+{
+ ForceOutlinerParaObject();
+
+ OutlinerParaObject* pOutlinerParaObject = GetOutlinerParaObject();
+
+ DBG_ASSERT( pOutlinerParaObject, "SdrTextObj::SetVerticalWriting() without OutlinerParaObject!" );
+
+ if( !pOutlinerParaObject ||
+ (pOutlinerParaObject->IsEffectivelyVertical() == bVertical) )
+ return;
+
+ // get item settings
+ const SfxItemSet& rSet = GetObjectItemSet();
+
+ // Also exchange horizontal and vertical adjust items
+ SdrTextHorzAdjust eHorz = rSet.Get(SDRATTR_TEXT_HORZADJUST).GetValue();
+ SdrTextVertAdjust eVert = rSet.Get(SDRATTR_TEXT_VERTADJUST).GetValue();
+
+ // rescue object size, SetSnapRect below expects logic rect,
+ // not snap rect.
+ tools::Rectangle aObjectRect = GetLogicRect();
+
+ // prepare ItemSet to set exchanged width and height items
+ SfxItemSetFixed<SDRATTR_TEXT_AUTOGROWHEIGHT, SDRATTR_TEXT_AUTOGROWHEIGHT,
+ // Expanded item ranges to also support horizontal and vertical adjust.
+ SDRATTR_TEXT_VERTADJUST, SDRATTR_TEXT_VERTADJUST,
+ SDRATTR_TEXT_AUTOGROWWIDTH, SDRATTR_TEXT_HORZADJUST> aNewSet(*rSet.GetPool());
+
+ aNewSet.Put(rSet);
+
+ // Exchange horizontal and vertical adjusts
+ switch(eVert)
+ {
+ case SDRTEXTVERTADJUST_TOP: aNewSet.Put(SdrTextHorzAdjustItem(SDRTEXTHORZADJUST_RIGHT)); break;
+ case SDRTEXTVERTADJUST_CENTER: aNewSet.Put(SdrTextHorzAdjustItem(SDRTEXTHORZADJUST_CENTER)); break;
+ case SDRTEXTVERTADJUST_BOTTOM: aNewSet.Put(SdrTextHorzAdjustItem(SDRTEXTHORZADJUST_LEFT)); break;
+ case SDRTEXTVERTADJUST_BLOCK: aNewSet.Put(SdrTextHorzAdjustItem(SDRTEXTHORZADJUST_BLOCK)); break;
+ }
+ switch(eHorz)
+ {
+ case SDRTEXTHORZADJUST_LEFT: aNewSet.Put(SdrTextVertAdjustItem(SDRTEXTVERTADJUST_BOTTOM)); break;
+ case SDRTEXTHORZADJUST_CENTER: aNewSet.Put(SdrTextVertAdjustItem(SDRTEXTVERTADJUST_CENTER)); break;
+ case SDRTEXTHORZADJUST_RIGHT: aNewSet.Put(SdrTextVertAdjustItem(SDRTEXTVERTADJUST_TOP)); break;
+ case SDRTEXTHORZADJUST_BLOCK: aNewSet.Put(SdrTextVertAdjustItem(SDRTEXTVERTADJUST_BLOCK)); break;
+ }
+
+ pOutlinerParaObject = GetOutlinerParaObject();
+ if ( pOutlinerParaObject )
+ pOutlinerParaObject->SetVertical(bVertical);
+ SetObjectItemSet( aNewSet );
+
+ // restore object size
+ SetSnapRect(aObjectRect);
+}
+
+void SdrObjCustomShape::SuggestTextFrameSize(Size aSuggestedTextFrameSize)
+{
+ m_aSuggestedTextFrameSize = aSuggestedTextFrameSize;
+}
+
+bool SdrObjCustomShape::AdjustTextFrameWidthAndHeight(tools::Rectangle& rR, bool bHgt, bool bWdt) const
+{
+ // Either we have text or the application has native text and suggested its size to us.
+ bool bHasText = HasText() || !m_aSuggestedTextFrameSize.IsEmpty();
+ if ( bHasText && !rR.IsEmpty() )
+ {
+ bool bWdtGrow=bWdt && IsAutoGrowWidth();
+ bool bHgtGrow=bHgt && IsAutoGrowHeight();
+ if ( bWdtGrow || bHgtGrow )
+ {
+ tools::Rectangle aR0(rR);
+ tools::Long nHgt=0,nMinHgt=0,nMaxHgt=0;
+ tools::Long nWdt=0,nMinWdt=0,nMaxWdt=0;
+ Size aSiz(rR.GetSize()); aSiz.AdjustWidth( -1 ); aSiz.AdjustHeight( -1 );
+ Size aMaxSiz(100000,100000);
+ Size aTmpSiz(getSdrModelFromSdrObject().GetMaxObjSize());
+ if (aTmpSiz.Width()!=0) aMaxSiz.setWidth(aTmpSiz.Width() );
+ if (aTmpSiz.Height()!=0) aMaxSiz.setHeight(aTmpSiz.Height() );
+ if (bWdtGrow)
+ {
+ nMinWdt=GetMinTextFrameWidth();
+ nMaxWdt=GetMaxTextFrameWidth();
+ if (nMaxWdt==0 || nMaxWdt>aMaxSiz.Width()) nMaxWdt=aMaxSiz.Width();
+ if (nMinWdt<=0) nMinWdt=1;
+ aSiz.setWidth(nMaxWdt );
+ }
+ if (bHgtGrow)
+ {
+ nMinHgt=GetMinTextFrameHeight();
+ nMaxHgt=GetMaxTextFrameHeight();
+ if (nMaxHgt==0 || nMaxHgt>aMaxSiz.Height()) nMaxHgt=aMaxSiz.Height();
+ if (nMinHgt<=0) nMinHgt=1;
+ aSiz.setHeight(nMaxHgt );
+ }
+ tools::Long nHDist=GetTextLeftDistance()+GetTextRightDistance();
+ tools::Long nVDist=GetTextUpperDistance()+GetTextLowerDistance();
+ aSiz.AdjustWidth( -nHDist );
+ aSiz.AdjustHeight( -nVDist );
+ if ( aSiz.Width() < 2 )
+ aSiz.setWidth( 2 ); // minimum size=2
+ if ( aSiz.Height() < 2 )
+ aSiz.setHeight( 2 ); // minimum size=2
+
+ if (HasText())
+ {
+ if(mpEditingOutliner)
+ {
+ mpEditingOutliner->SetMaxAutoPaperSize( aSiz );
+ if (bWdtGrow)
+ {
+ Size aSiz2(mpEditingOutliner->CalcTextSize());
+ nWdt=aSiz2.Width()+1; // a little more tolerance
+ if (bHgtGrow) nHgt=aSiz2.Height()+1; // a little more tolerance
+ } else
+ {
+ nHgt=mpEditingOutliner->GetTextHeight()+1; // a little more tolerance
+ }
+ }
+ else
+ {
+ Outliner& rOutliner=ImpGetDrawOutliner();
+ rOutliner.SetPaperSize(aSiz);
+ rOutliner.SetUpdateLayout(true);
+ // TODO: add the optimization with bPortionInfoChecked again.
+ OutlinerParaObject* pOutlinerParaObject = GetOutlinerParaObject();
+ if( pOutlinerParaObject != nullptr )
+ {
+ rOutliner.SetText(*pOutlinerParaObject);
+ rOutliner.SetFixedCellHeight(GetMergedItem(SDRATTR_TEXT_USEFIXEDCELLHEIGHT).GetValue());
+ }
+ if ( bWdtGrow )
+ {
+ Size aSiz2(rOutliner.CalcTextSize());
+ nWdt=aSiz2.Width()+1; // a little more tolerance
+ if ( bHgtGrow )
+ nHgt=aSiz2.Height()+1; // a little more tolerance
+ }
+ else
+ {
+ nHgt = rOutliner.GetTextHeight()+1; // a little more tolerance
+
+ sal_Int16 nColumns = GetMergedItem(SDRATTR_TEXTCOLUMNS_NUMBER).GetValue();
+ if (bHgtGrow && nColumns > 1)
+ {
+ // Both 'resize shape to fix text' and multiple columns are enabled. The
+ // first means a dynamic height, the second expects a fixed height.
+ // Resolve this conflict by going with the original height.
+ nHgt = rR.getHeight();
+ }
+ }
+ rOutliner.Clear();
+ }
+ }
+ else
+ {
+ nHgt = m_aSuggestedTextFrameSize.Height();
+ nWdt = m_aSuggestedTextFrameSize.Width();
+ }
+ if ( nWdt < nMinWdt )
+ nWdt = nMinWdt;
+ if ( nWdt > nMaxWdt )
+ nWdt = nMaxWdt;
+ nWdt += nHDist;
+ if ( nWdt < 1 )
+ nWdt = 1; // nHDist may also be negative
+ if ( nHgt < nMinHgt )
+ nHgt = nMinHgt;
+ if ( nHgt > nMaxHgt )
+ nHgt = nMaxHgt;
+ nHgt+=nVDist;
+ if ( nHgt < 1 )
+ nHgt = 1; // nVDist may also be negative
+ tools::Long nWdtGrow = nWdt-(rR.Right()-rR.Left());
+ tools::Long nHgtGrow = nHgt-(rR.Bottom()-rR.Top());
+ if ( nWdtGrow == 0 )
+ bWdtGrow = false;
+ if ( nHgtGrow == 0 )
+ bHgtGrow=false;
+ if ( bWdtGrow || bHgtGrow || !m_aSuggestedTextFrameSize.IsEmpty())
+ {
+ if ( bWdtGrow || m_aSuggestedTextFrameSize.Width() )
+ {
+ SdrTextHorzAdjust eHAdj=GetTextHorizontalAdjust();
+ if (m_aSuggestedTextFrameSize.Width())
+ {
+ rR.SetRight(rR.Left() + m_aSuggestedTextFrameSize.Width());
+ }
+ else if ( eHAdj == SDRTEXTHORZADJUST_LEFT )
+ rR.AdjustRight(nWdtGrow );
+ else if ( eHAdj == SDRTEXTHORZADJUST_RIGHT )
+ rR.AdjustLeft( -nWdtGrow );
+ else
+ {
+ tools::Long nWdtGrow2=nWdtGrow/2;
+ rR.AdjustLeft( -nWdtGrow2 );
+ rR.SetRight(rR.Left()+nWdt );
+ }
+ }
+ if ( bHgtGrow || m_aSuggestedTextFrameSize.Height() )
+ {
+ SdrTextVertAdjust eVAdj=GetTextVerticalAdjust();
+ if (m_aSuggestedTextFrameSize.Height())
+ {
+ rR.SetBottom(rR.Top() + m_aSuggestedTextFrameSize.Height());
+ }
+ else if ( eVAdj == SDRTEXTVERTADJUST_TOP )
+ rR.AdjustBottom(nHgtGrow );
+ else if ( eVAdj == SDRTEXTVERTADJUST_BOTTOM )
+ rR.AdjustTop( -nHgtGrow );
+ else
+ {
+ tools::Long nHgtGrow2=nHgtGrow/2;
+ rR.AdjustTop( -nHgtGrow2 );
+ rR.SetBottom(rR.Top()+nHgt );
+ }
+ }
+ if ( maGeo.nRotationAngle )
+ {
+ Point aD1(rR.TopLeft());
+ aD1-=aR0.TopLeft();
+ Point aD2(aD1);
+ RotatePoint(aD2,Point(), maGeo.mfSinRotationAngle, maGeo.mfCosRotationAngle);
+ aD2-=aD1;
+ rR.Move(aD2.X(),aD2.Y());
+ }
+ return true;
+ }
+ }
+ }
+ return false;
+}
+
+tools::Rectangle SdrObjCustomShape::ImpCalculateTextFrame( const bool bHgt, const bool bWdt )
+{
+ tools::Rectangle aReturnValue;
+
+ tools::Rectangle aOldTextRect( maRect ); // <- initial text rectangle
+
+ tools::Rectangle aNewTextRect( maRect ); // <- new text rectangle returned from the custom shape renderer,
+ GetTextBounds( aNewTextRect ); // it depends to the current logical shape size
+
+ tools::Rectangle aAdjustedTextRect( aNewTextRect ); // <- new text rectangle is being tested by AdjustTextFrameWidthAndHeight to ensure
+ if ( AdjustTextFrameWidthAndHeight( aAdjustedTextRect, bHgt, bWdt ) ) // that the new text rectangle is matching the current text size from the outliner
+ {
+ if (aAdjustedTextRect != aNewTextRect && aOldTextRect != aAdjustedTextRect &&
+ aNewTextRect.GetWidth() && aNewTextRect.GetHeight())
+ {
+ aReturnValue = maRect;
+ double fXScale = static_cast<double>(aOldTextRect.GetWidth()) / static_cast<double>(aNewTextRect.GetWidth());
+ double fYScale = static_cast<double>(aOldTextRect.GetHeight()) / static_cast<double>(aNewTextRect.GetHeight());
+ double fRightDiff = static_cast<double>( aAdjustedTextRect.Right() - aNewTextRect.Right() ) * fXScale;
+ double fLeftDiff = static_cast<double>( aAdjustedTextRect.Left() - aNewTextRect.Left() ) * fXScale;
+ double fTopDiff = static_cast<double>( aAdjustedTextRect.Top() - aNewTextRect.Top() ) * fYScale;
+ double fBottomDiff= static_cast<double>( aAdjustedTextRect.Bottom()- aNewTextRect.Bottom()) * fYScale;
+ aReturnValue.AdjustLeft(static_cast<sal_Int32>(fLeftDiff) );
+ aReturnValue.AdjustRight(static_cast<sal_Int32>(fRightDiff) );
+ aReturnValue.AdjustTop(static_cast<sal_Int32>(fTopDiff) );
+ aReturnValue.AdjustBottom(static_cast<sal_Int32>(fBottomDiff) );
+ }
+ }
+ return aReturnValue;
+}
+
+bool SdrObjCustomShape::NbcAdjustTextFrameWidthAndHeight(bool bHgt, bool bWdt)
+{
+ tools::Rectangle aNewTextRect = ImpCalculateTextFrame(bHgt, bWdt);
+ const bool bRet = !aNewTextRect.IsEmpty() && aNewTextRect != maRect;
+ if (bRet && !mbAdjustingTextFrameWidthAndHeight)
+ {
+ mbAdjustingTextFrameWidthAndHeight = true;
+
+ // taking care of handles that should not been changed
+ std::vector< SdrCustomShapeInteraction > aInteractionHandles( GetInteractionHandles() );
+
+ maRect = aNewTextRect;
+ SetBoundAndSnapRectsDirty();
+ SetChanged();
+
+ for (const auto& rInteraction : aInteractionHandles)
+ {
+ try
+ {
+ if ( rInteraction.nMode & CustomShapeHandleModes::RESIZE_FIXED )
+ rInteraction.xInteraction->setControllerPosition( rInteraction.aPosition );
+ }
+ catch ( const uno::RuntimeException& )
+ {
+ }
+ }
+ InvalidateRenderGeometry();
+
+ mbAdjustingTextFrameWidthAndHeight = false;
+ }
+ return bRet;
+}
+
+bool SdrObjCustomShape::AdjustTextFrameWidthAndHeight()
+{
+ tools::Rectangle aNewTextRect = ImpCalculateTextFrame( true/*bHgt*/, true/*bWdt*/ );
+ bool bRet = !aNewTextRect.IsEmpty() && ( aNewTextRect != maRect );
+ if ( bRet )
+ {
+ tools::Rectangle aBoundRect0;
+ if ( m_pUserCall )
+ aBoundRect0 = GetCurrentBoundRect();
+
+ // taking care of handles that should not been changed
+ std::vector< SdrCustomShapeInteraction > aInteractionHandles( GetInteractionHandles() );
+
+ maRect = aNewTextRect;
+ SetBoundAndSnapRectsDirty();
+
+ for (const auto& rInteraction : aInteractionHandles)
+ {
+ try
+ {
+ if ( rInteraction.nMode & CustomShapeHandleModes::RESIZE_FIXED )
+ rInteraction.xInteraction->setControllerPosition( rInteraction.aPosition );
+ }
+ catch ( const uno::RuntimeException& )
+ {
+ }
+ }
+
+ InvalidateRenderGeometry();
+ SetChanged();
+ BroadcastObjectChange();
+ SendUserCall(SdrUserCallType::Resize,aBoundRect0);
+ }
+ return bRet;
+}
+void SdrObjCustomShape::TakeTextEditArea(Size* pPaperMin, Size* pPaperMax, tools::Rectangle* pViewInit, tools::Rectangle* pViewMin) const
+{
+ tools::Rectangle aViewInit;
+ TakeTextAnchorRect( aViewInit );
+ if (maGeo.nRotationAngle)
+ {
+ Point aCenter(aViewInit.Center());
+ aCenter-=aViewInit.TopLeft();
+ Point aCenter0(aCenter);
+ RotatePoint(aCenter, Point(), maGeo.mfSinRotationAngle, maGeo.mfCosRotationAngle);
+ aCenter-=aCenter0;
+ aViewInit.Move(aCenter.X(),aCenter.Y());
+ }
+ Size aAnkSiz(aViewInit.GetSize());
+ aAnkSiz.AdjustWidth( -1 ); aAnkSiz.AdjustHeight( -1 ); // because GetSize() adds 1
+ Size aMaxSiz(1000000,1000000);
+ {
+ Size aTmpSiz(getSdrModelFromSdrObject().GetMaxObjSize());
+ if (aTmpSiz.Width()!=0) aMaxSiz.setWidth(aTmpSiz.Width() );
+ if (aTmpSiz.Height()!=0) aMaxSiz.setHeight(aTmpSiz.Height() );
+ }
+ SdrTextHorzAdjust eHAdj(GetTextHorizontalAdjust());
+ SdrTextVertAdjust eVAdj(GetTextVerticalAdjust());
+
+ tools::Long nMinWdt = GetMinTextFrameWidth();
+ tools::Long nMinHgt = GetMinTextFrameHeight();
+ tools::Long nMaxWdt = GetMaxTextFrameWidth();
+ tools::Long nMaxHgt = GetMaxTextFrameHeight();
+ if (nMinWdt<1) nMinWdt=1;
+ if (nMinHgt<1) nMinHgt=1;
+ if ( nMaxWdt == 0 || nMaxWdt > aMaxSiz.Width() )
+ nMaxWdt = aMaxSiz.Width();
+ if ( nMaxHgt == 0 || nMaxHgt > aMaxSiz.Height() )
+ nMaxHgt=aMaxSiz.Height();
+
+ if (GetMergedItem(SDRATTR_TEXT_WORDWRAP).GetValue())
+ {
+ if ( IsVerticalWriting() )
+ {
+ nMaxHgt = aAnkSiz.Height();
+ nMinHgt = nMaxHgt;
+ }
+ else
+ {
+ nMaxWdt = aAnkSiz.Width();
+ nMinWdt = nMaxWdt;
+ }
+ }
+ Size aPaperMax(nMaxWdt, nMaxHgt);
+ Size aPaperMin(nMinWdt, nMinHgt);
+
+ if ( pViewMin )
+ {
+ *pViewMin = aViewInit;
+
+ tools::Long nXFree = aAnkSiz.Width() - aPaperMin.Width();
+ if ( eHAdj == SDRTEXTHORZADJUST_LEFT )
+ pViewMin->AdjustRight( -nXFree );
+ else if ( eHAdj == SDRTEXTHORZADJUST_RIGHT )
+ pViewMin->AdjustLeft(nXFree );
+ else { pViewMin->AdjustLeft(nXFree / 2 ); pViewMin->SetRight( pViewMin->Left() + aPaperMin.Width() ); }
+
+ tools::Long nYFree = aAnkSiz.Height() - aPaperMin.Height();
+ if ( eVAdj == SDRTEXTVERTADJUST_TOP )
+ pViewMin->AdjustBottom( -nYFree );
+ else if ( eVAdj == SDRTEXTVERTADJUST_BOTTOM )
+ pViewMin->AdjustTop(nYFree );
+ else { pViewMin->AdjustTop(nYFree / 2 ); pViewMin->SetBottom( pViewMin->Top() + aPaperMin.Height() ); }
+ }
+
+ if( IsVerticalWriting() )
+ aPaperMin.setWidth( 0 );
+ else
+ aPaperMin.setHeight( 0 );
+
+ if( eHAdj != SDRTEXTHORZADJUST_BLOCK )
+ aPaperMin.setWidth(0 );
+
+ // For complete vertical adjust support, set paper min height to 0, here.
+ if(SDRTEXTVERTADJUST_BLOCK != eVAdj )
+ aPaperMin.setHeight( 0 );
+
+ if (pPaperMin!=nullptr) *pPaperMin=aPaperMin;
+ if (pPaperMax!=nullptr) *pPaperMax=aPaperMax;
+ if (pViewInit!=nullptr) *pViewInit=aViewInit;
+}
+void SdrObjCustomShape::EndTextEdit( SdrOutliner& rOutl )
+{
+ SdrTextObj::EndTextEdit( rOutl );
+ InvalidateRenderGeometry();
+}
+void SdrObjCustomShape::TakeTextAnchorRect( tools::Rectangle& rAnchorRect ) const
+{
+ if ( GetTextBounds( rAnchorRect ) )
+ {
+ Point aRotateRef( maSnapRect.Center() );
+ AdjustRectToTextDistance(rAnchorRect);
+
+ if ( rAnchorRect.GetWidth() < 2 )
+ rAnchorRect.SetRight( rAnchorRect.Left() + 1 ); // minimal width is 2
+ if ( rAnchorRect.GetHeight() < 2 )
+ rAnchorRect.SetBottom( rAnchorRect.Top() + 1 ); // minimal height is 2
+ if (maGeo.nRotationAngle)
+ {
+ Point aP( rAnchorRect.TopLeft() );
+ RotatePoint(aP, aRotateRef, maGeo.mfSinRotationAngle, maGeo.mfCosRotationAngle);
+ rAnchorRect.SetPos( aP );
+ }
+ }
+ else
+ SdrTextObj::TakeTextAnchorRect( rAnchorRect );
+}
+void SdrObjCustomShape::TakeTextRect( SdrOutliner& rOutliner, tools::Rectangle& rTextRect, bool bNoEditText,
+ tools::Rectangle* pAnchorRect, bool /*bLineWidth*/) const
+{
+ tools::Rectangle aAnkRect; // Rect in which we anchor
+ TakeTextAnchorRect(aAnkRect);
+ SdrTextVertAdjust eVAdj=GetTextVerticalAdjust();
+ SdrTextHorzAdjust eHAdj=GetTextHorizontalAdjust();
+ EEControlBits nStat0=rOutliner.GetControlWord();
+ Size aNullSize;
+
+ rOutliner.SetControlWord(nStat0|EEControlBits::AUTOPAGESIZE);
+ rOutliner.SetMinAutoPaperSize(aNullSize);
+ sal_Int32 nMaxAutoPaperWidth = 1000000;
+ sal_Int32 nMaxAutoPaperHeight= 1000000;
+
+ tools::Long nAnkWdt=aAnkRect.GetWidth();
+ tools::Long nAnkHgt=aAnkRect.GetHeight();
+
+ if (GetMergedItem(SDRATTR_TEXT_WORDWRAP).GetValue())
+ {
+ if ( IsVerticalWriting() )
+ nMaxAutoPaperHeight = nAnkHgt;
+ else
+ nMaxAutoPaperWidth = nAnkWdt;
+ }
+ if(SDRTEXTHORZADJUST_BLOCK == eHAdj && !IsVerticalWriting())
+ {
+ rOutliner.SetMinAutoPaperSize(Size(nAnkWdt, 0));
+ }
+
+ if(SDRTEXTVERTADJUST_BLOCK == eVAdj && IsVerticalWriting())
+ {
+ rOutliner.SetMinAutoPaperSize(Size(0, nAnkHgt));
+ }
+ rOutliner.SetMaxAutoPaperSize( Size( nMaxAutoPaperWidth, nMaxAutoPaperHeight ) );
+ rOutliner.SetPaperSize( aNullSize );
+
+ // put text into the Outliner - if necessary the use the text from the EditOutliner
+ std::optional<OutlinerParaObject> pPara;
+ if (GetOutlinerParaObject())
+ pPara = *GetOutlinerParaObject();
+ if (mpEditingOutliner && !bNoEditText)
+ pPara=mpEditingOutliner->CreateParaObject();
+
+ if (pPara)
+ {
+ bool bHitTest(&getSdrModelFromSdrObject().GetHitTestOutliner() == &rOutliner);
+ const SdrTextObj* pTestObj = rOutliner.GetTextObj();
+
+ if( !pTestObj || !bHitTest || pTestObj != this ||
+ pTestObj->GetOutlinerParaObject() != GetOutlinerParaObject() )
+ {
+ if( bHitTest )
+ rOutliner.SetTextObj( this );
+
+ rOutliner.SetUpdateLayout(true);
+ rOutliner.SetText(*pPara);
+ }
+ }
+ else
+ {
+ rOutliner.SetTextObj( nullptr );
+ }
+
+ rOutliner.SetUpdateLayout(true);
+ rOutliner.SetControlWord(nStat0);
+
+ SdrText* pText = getActiveText();
+ if( pText )
+ pText->CheckPortionInfo( rOutliner );
+
+ Point aTextPos(aAnkRect.TopLeft());
+ Size aTextSiz(rOutliner.GetPaperSize()); // GetPaperSize() has a little added tolerance, no?
+
+ // For draw objects containing text correct horizontal/vertical alignment if text is bigger
+ // than the object itself. Without that correction, the text would always be
+ // formatted to the left edge (or top edge when vertical) of the draw object.
+
+ if( !IsTextFrame() )
+ {
+ if(aAnkRect.GetWidth() < aTextSiz.Width() && !IsVerticalWriting())
+ {
+ // Horizontal case here. Correct only if eHAdj == SDRTEXTHORZADJUST_BLOCK,
+ // else the alignment is wanted.
+ if(SDRTEXTHORZADJUST_BLOCK == eHAdj)
+ {
+ SvxAdjust eAdjust = GetObjectItemSet().Get(EE_PARA_JUST).GetAdjust();
+ switch (eAdjust)
+ {
+ case SvxAdjust::Left: eHAdj = SDRTEXTHORZADJUST_LEFT; break;
+ case SvxAdjust::Right: eHAdj = SDRTEXTHORZADJUST_RIGHT; break;
+ case SvxAdjust::Center: eHAdj = SDRTEXTHORZADJUST_CENTER; break;
+ default: break;
+ }
+ }
+ }
+
+ if(aAnkRect.GetHeight() < aTextSiz.Height() && IsVerticalWriting())
+ {
+ // Vertical case here. Correct only if eHAdj == SDRTEXTVERTADJUST_BLOCK,
+ // else the alignment is wanted.
+ if(SDRTEXTVERTADJUST_BLOCK == eVAdj)
+ {
+ eVAdj = SDRTEXTVERTADJUST_CENTER;
+ }
+ }
+ }
+
+ if (eHAdj==SDRTEXTHORZADJUST_CENTER || eHAdj==SDRTEXTHORZADJUST_RIGHT)
+ {
+ tools::Long nFreeWdt=aAnkRect.GetWidth()-aTextSiz.Width();
+ if (eHAdj==SDRTEXTHORZADJUST_CENTER)
+ aTextPos.AdjustX(nFreeWdt/2 );
+ if (eHAdj==SDRTEXTHORZADJUST_RIGHT)
+ aTextPos.AdjustX(nFreeWdt );
+ }
+ if (eVAdj==SDRTEXTVERTADJUST_CENTER || eVAdj==SDRTEXTVERTADJUST_BOTTOM)
+ {
+ tools::Long nFreeHgt=aAnkRect.GetHeight()-aTextSiz.Height();
+ if (eVAdj==SDRTEXTVERTADJUST_CENTER)
+ aTextPos.AdjustY(nFreeHgt/2 );
+ if (eVAdj==SDRTEXTVERTADJUST_BOTTOM)
+ aTextPos.AdjustY(nFreeHgt );
+ }
+ if (maGeo.nRotationAngle != 0_deg100)
+ RotatePoint(aTextPos,aAnkRect.TopLeft(), maGeo.mfSinRotationAngle, maGeo.mfCosRotationAngle);
+
+ if (pAnchorRect)
+ *pAnchorRect=aAnkRect;
+
+ // using rTextRect together with ContourFrame doesn't always work correctly
+ rTextRect=tools::Rectangle(aTextPos,aTextSiz);
+}
+
+void SdrObjCustomShape::NbcSetOutlinerParaObject(std::optional<OutlinerParaObject> pTextObject)
+{
+ SdrTextObj::NbcSetOutlinerParaObject( std::move(pTextObject) );
+ SetBoundRectDirty();
+ SetBoundAndSnapRectsDirty(true);
+ InvalidateRenderGeometry();
+}
+
+SdrObjCustomShape* SdrObjCustomShape::CloneSdrObject(SdrModel& rTargetModel) const
+{
+ return new SdrObjCustomShape(rTargetModel, *this);
+}
+
+OUString SdrObjCustomShape::TakeObjNameSingul() const
+{
+ OUString sName(SvxResId(STR_ObjNameSingulCUSTOMSHAPE));
+ OUString aNm(GetName());
+ if (!aNm.isEmpty())
+ sName += " '" + aNm + "'";
+ return sName;
+}
+
+OUString SdrObjCustomShape::TakeObjNamePlural() const
+{
+ return SvxResId(STR_ObjNamePluralCUSTOMSHAPE);
+}
+
+basegfx::B2DPolyPolygon SdrObjCustomShape::TakeXorPoly() const
+{
+ return GetLineGeometry( false );
+}
+
+basegfx::B2DPolyPolygon SdrObjCustomShape::TakeContour() const
+{
+ const SdrObject* pSdrObject = GetSdrObjectFromCustomShape();
+ if ( pSdrObject )
+ return pSdrObject->TakeContour();
+ return basegfx::B2DPolyPolygon();
+}
+
+SdrObjectUniquePtr SdrObjCustomShape::DoConvertToPolyObj(bool bBezier, bool bAddText) const
+{
+ // #i37011#
+ SdrObjectUniquePtr pRetval;
+ SdrObject* pRenderedCustomShape = nullptr;
+
+ if ( !mXRenderedCustomShape.is() )
+ {
+ // force CustomShape
+ GetSdrObjectFromCustomShape();
+ }
+
+ if ( mXRenderedCustomShape.is() )
+ {
+ pRenderedCustomShape = SdrObject::getSdrObjectFromXShape(mXRenderedCustomShape);
+ }
+
+ if ( pRenderedCustomShape )
+ {
+ // Clone to same SdrModel
+ SdrObject* pCandidate(pRenderedCustomShape->CloneSdrObject(pRenderedCustomShape->getSdrModelFromSdrObject()));
+ DBG_ASSERT(pCandidate, "SdrObjCustomShape::DoConvertToPolyObj: Could not clone SdrObject (!)");
+ pRetval = pCandidate->DoConvertToPolyObj(bBezier, bAddText);
+ SdrObject::Free( pCandidate );
+
+ if(pRetval)
+ {
+ const bool bShadow(GetMergedItem(SDRATTR_SHADOW).GetValue());
+ if(bShadow)
+ {
+ pRetval->SetMergedItem(makeSdrShadowItem(true));
+ }
+ }
+
+ if(bAddText && HasText() && !IsTextPath())
+ {
+ pRetval = ImpConvertAddText(std::move(pRetval), bBezier);
+ }
+ }
+
+ return pRetval;
+}
+
+void SdrObjCustomShape::InternalSetStyleSheet( SfxStyleSheet* pNewStyleSheet, bool bDontRemoveHardAttr, bool bBroadcast )
+{
+ // #i40944#
+ InvalidateRenderGeometry();
+ SdrObject::InternalSetStyleSheet( pNewStyleSheet, bDontRemoveHardAttr, bBroadcast );
+}
+
+void SdrObjCustomShape::handlePageChange(SdrPage* pOldPage, SdrPage* pNewPage)
+{
+ // call parent
+ SdrTextObj::handlePageChange(pOldPage, pNewPage);
+
+ if(nullptr != pNewPage)
+ {
+ // invalidating rectangles by SetRectsDirty is not sufficient,
+ // AdjustTextFrameWidthAndHeight() also has to be made, both
+ // actions are done by NbcSetSnapRect
+ tools::Rectangle aTmp( maRect ); //creating temporary rectangle #i61108#
+ NbcSetSnapRect( aTmp );
+ }
+}
+
+std::unique_ptr<SdrObjGeoData> SdrObjCustomShape::NewGeoData() const
+{
+ return std::make_unique<SdrAShapeObjGeoData>();
+}
+
+void SdrObjCustomShape::SaveGeoData(SdrObjGeoData& rGeo) const
+{
+ SdrTextObj::SaveGeoData( rGeo );
+ SdrAShapeObjGeoData& rAGeo=static_cast<SdrAShapeObjGeoData&>(rGeo);
+ rAGeo.fObjectRotation = fObjectRotation;
+ rAGeo.bMirroredX = IsMirroredX();
+ rAGeo.bMirroredY = IsMirroredY();
+
+ const Any* pAny = GetMergedItem( SDRATTR_CUSTOMSHAPE_GEOMETRY ).GetPropertyValueByName( "AdjustmentValues" );
+ if ( pAny )
+ *pAny >>= rAGeo.aAdjustmentSeq;
+}
+
+void SdrObjCustomShape::RestoreGeoData(const SdrObjGeoData& rGeo)
+{
+ SdrTextObj::RestoreGeoData( rGeo );
+ const SdrAShapeObjGeoData& rAGeo=static_cast<const SdrAShapeObjGeoData&>(rGeo);
+ fObjectRotation = rAGeo.fObjectRotation;
+ SetMirroredX( rAGeo.bMirroredX );
+ SetMirroredY( rAGeo.bMirroredY );
+
+ SdrCustomShapeGeometryItem rGeometryItem = GetMergedItem( SDRATTR_CUSTOMSHAPE_GEOMETRY );
+ PropertyValue aPropVal;
+ aPropVal.Name = "AdjustmentValues";
+ aPropVal.Value <<= rAGeo.aAdjustmentSeq;
+ rGeometryItem.SetPropertyValue( aPropVal );
+ SetMergedItem( rGeometryItem );
+
+ InvalidateRenderGeometry();
+}
+
+void SdrObjCustomShape::AdjustToMaxRect(const tools::Rectangle& rMaxRect, bool bShrinkOnly /* = false */)
+{
+ SAL_INFO_IF(bShrinkOnly, "svx", "Case bShrinkOnly == true is not implemented yet.");
+
+ if (rMaxRect.IsEmpty() || rMaxRect == GetSnapRect())
+ return;
+
+ // Get a matrix, that would produce the existing shape, when applied to a unit square
+ basegfx::B2DPolyPolygon aPolyPolygon; //not used, but formal needed
+ basegfx::B2DHomMatrix aMatrix;
+ TRGetBaseGeometry(aMatrix, aPolyPolygon);
+ // Using TRSetBaseGeometry(aMatrix, aPolyPolygon) would regenerate the current shape. But
+ // applying aMatrix to a unit square will not generate the current shape. Scaling,
+ // rotation and translation are correct, but shear angle has wrong sign. So break up
+ // matrix and create a mathematically correct new one.
+ basegfx::B2DTuple aScale;
+ basegfx::B2DTuple aTranslate;
+ double fRotate, fShearX;
+ aMatrix.decompose(aScale, aTranslate, fRotate, fShearX);
+ basegfx::B2DHomMatrix aMathMatrix;
+ aMathMatrix = basegfx::utils::createScaleShearXRotateTranslateB2DHomMatrix(
+ aScale,
+ basegfx::fTools::equalZero(fShearX) ? 0.0 : -fShearX,
+ basegfx::fTools::equalZero(fRotate) ? 0.0 : fRotate,
+ aTranslate);
+
+ // Calculate scaling factors from size of the transformed unit polygon as ersatz for the not
+ // usable current snap rectangle.
+ basegfx::B2DPolygon aB2DPolygon(basegfx::utils::createUnitPolygon());
+ aB2DPolygon.transform(aMathMatrix);
+ basegfx::B2DRange aB2DRange(aB2DPolygon.getB2DRange());
+ double fPolygonWidth = aB2DRange.getWidth();
+ if (fPolygonWidth == 0)
+ fPolygonWidth = 1;
+ double fPolygonHeight = aB2DRange.getHeight();
+ if (fPolygonHeight == 0)
+ fPolygonHeight = 1;
+ const double aFactorX = static_cast<double>(rMaxRect.GetWidth()) / fPolygonWidth;
+ const double aFactorY = static_cast<double>(rMaxRect.GetHeight()) / fPolygonHeight;
+
+ // Generate matrix, that would produce the desired rMaxRect when applied to unit square
+ aMathMatrix.scale(aFactorX, aFactorY);
+ aB2DPolygon = basegfx::utils::createUnitPolygon();
+ aB2DPolygon.transform(aMathMatrix);
+ aB2DRange = aB2DPolygon.getB2DRange();
+ const double fPolygonLeft = aB2DRange.getMinX();
+ const double fPolygonTop = aB2DRange.getMinY();
+ aMathMatrix.translate(rMaxRect.Left() - fPolygonLeft, rMaxRect.Top() - fPolygonTop);
+
+ // Create a Matrix from aMathMatrix, which is usable with TRSetBaseGeometry
+ aMathMatrix.decompose(aScale, aTranslate, fRotate, fShearX);
+ aMatrix = basegfx::utils::createScaleShearXRotateTranslateB2DHomMatrix(
+ aScale,
+ basegfx::fTools::equalZero(fShearX) ? 0.0 : -fShearX,
+ basegfx::fTools::equalZero(fRotate) ? 0.0 : fRotate,
+ aTranslate);
+
+ // Now use TRSetBaseGeometry to actually perform scale, shear, rotate and translate
+ // on the shape. That considers gluepoints, interaction handles and text area, and includes
+ // setting rectangles dirty and broadcast.
+ TRSetBaseGeometry(aMatrix, aPolyPolygon);
+}
+
+void SdrObjCustomShape::TRSetBaseGeometry(const basegfx::B2DHomMatrix& rMatrix, const basegfx::B2DPolyPolygon& /*rPolyPolygon*/)
+{
+ // The shape might have already flipping in its enhanced geometry. LibreOffice applies
+ // such after all transformations. We remove it, but remember it to apply them later.
+ bool bIsMirroredX = IsMirroredX();
+ bool bIsMirroredY = IsMirroredY();
+ if (bIsMirroredX || bIsMirroredY)
+ {
+ Point aCurrentCenter = GetSnapRect().Center();
+ if (bIsMirroredX) // mirror on the y-axis
+ {
+ Mirror(aCurrentCenter, Point(aCurrentCenter.X(), aCurrentCenter.Y() + 1000));
+ }
+ if (bIsMirroredY) // mirror on the x-axis
+ {
+ Mirror(aCurrentCenter, Point(aCurrentCenter.X() + 1000, aCurrentCenter.Y()));
+ }
+ }
+
+ // break up matrix
+ basegfx::B2DTuple aScale;
+ basegfx::B2DTuple aTranslate;
+ double fRotate, fShearX;
+ rMatrix.decompose(aScale, aTranslate, fRotate, fShearX);
+
+ // reset object shear and rotations
+ fObjectRotation = 0.0;
+ maGeo.nRotationAngle = 0_deg100;
+ maGeo.RecalcSinCos();
+ maGeo.nShearAngle = 0_deg100;
+ maGeo.RecalcTan();
+
+ // if anchor is used, make position relative to it
+ if(getSdrModelFromSdrObject().IsWriter())
+ {
+ if(GetAnchorPos().X() || GetAnchorPos().Y())
+ {
+ aTranslate += basegfx::B2DTuple(GetAnchorPos().X(), GetAnchorPos().Y());
+ }
+ }
+
+ // scale
+ Size aSize(FRound(fabs(aScale.getX())), FRound(fabs(aScale.getY())));
+ // fdo#47434 We need a valid rectangle here
+ if( !aSize.Height() ) aSize.setHeight( 1 );
+ if( !aSize.Width() ) aSize.setWidth( 1 );
+ tools::Rectangle aBaseRect(Point(), aSize);
+ SetLogicRect(aBaseRect);
+
+ // Apply flipping from Matrix, which is a transformation relative to origin
+ if (basegfx::fTools::less(aScale.getX(), 0.0))
+ Mirror(Point(0, 0), Point(0, 1000)); // mirror on the y-axis
+ if (basegfx::fTools::less(aScale.getY(), 0.0))
+ Mirror(Point(0, 0), Point(1000, 0)); // mirror on the x-axis
+
+ // shear?
+ if(!basegfx::fTools::equalZero(fShearX))
+ {
+ GeoStat aGeoStat;
+ // #i123181# The fix for #121932# here was wrong, the trunk version does not correct the
+ // mirrored shear values, neither at the object level, nor on the API or XML level. Taking
+ // back the mirroring of the shear angle
+ aGeoStat.nShearAngle = Degree100(FRound(basegfx::rad2deg<100>(atan(fShearX))));
+ aGeoStat.RecalcTan();
+ Shear(Point(), aGeoStat.nShearAngle, aGeoStat.mfTanShearAngle, false);
+ }
+
+ // rotation?
+ if(!basegfx::fTools::equalZero(fRotate))
+ {
+ GeoStat aGeoStat;
+
+ // #i78696#
+ // fRotate is mathematically correct, but aGeoStat.nRotationAngle is
+ // mirrored -> mirror value here
+ aGeoStat.nRotationAngle = NormAngle36000(Degree100(FRound(-basegfx::rad2deg<100>(fRotate))));
+ aGeoStat.RecalcSinCos();
+ Rotate(Point(), aGeoStat.nRotationAngle, aGeoStat.mfSinRotationAngle, aGeoStat.mfCosRotationAngle);
+ }
+
+ // translate?
+ if(!aTranslate.equalZero())
+ {
+ Move(Size(FRound(aTranslate.getX()), FRound(aTranslate.getY())));
+ }
+
+ // Apply flipping from enhanced geometry at center of the shape.
+ if (!(bIsMirroredX || bIsMirroredY))
+ return;
+
+ // create mathematically matrix for the applied transformations
+ // aScale was in most cases built from a rectangle including edge
+ // and is therefore mathematically too large by 1
+ if (aScale.getX() > 2.0 && aScale.getY() > 2.0)
+ aScale -= basegfx::B2DTuple(1.0, 1.0);
+ basegfx::B2DHomMatrix aMathMat = basegfx::utils::createScaleShearXRotateTranslateB2DHomMatrix(
+ aScale, -fShearX, basegfx::fTools::equalZero(fRotate) ? 0.0 : fRotate,
+ aTranslate);
+ // Use matrix to get current center
+ basegfx::B2DPoint aCenter(0.5,0.5);
+ aCenter = aMathMat * aCenter;
+ double fCenterX = aCenter.getX();
+ double fCenterY = aCenter.getY();
+ if (bIsMirroredX) // vertical axis
+ Mirror(Point(FRound(fCenterX),FRound(fCenterY)),
+ Point(FRound(fCenterX), FRound(fCenterY + 1000.0)));
+ if (bIsMirroredY) // horizontal axis
+ Mirror(Point(FRound(fCenterX),FRound(fCenterY)),
+ Point(FRound(fCenterX + 1000.0), FRound(fCenterY)));
+}
+
+// taking fObjectRotation instead of aGeo.nAngle
+bool SdrObjCustomShape::TRGetBaseGeometry(basegfx::B2DHomMatrix& rMatrix, basegfx::B2DPolyPolygon& /*rPolyPolygon*/) const
+{
+ // get turn and shear
+ double fRotate = basegfx::deg2rad(fObjectRotation);
+ double fShearX = toRadians(maGeo.nShearAngle);
+
+ // get aRect, this is the unrotated snaprect
+ tools::Rectangle aRectangle(maRect);
+
+ bool bMirroredX = IsMirroredX();
+ bool bMirroredY = IsMirroredY();
+ if ( bMirroredX || bMirroredY )
+ { // we have to retrieve the unmirrored rect
+
+ GeoStat aNewGeo(maGeo);
+
+ if ( bMirroredX )
+ {
+ fShearX = -fShearX;
+ tools::Polygon aPol = Rect2Poly(maRect, aNewGeo);
+ tools::Rectangle aBoundRect( aPol.GetBoundRect() );
+
+ Point aRef1( ( aBoundRect.Left() + aBoundRect.Right() ) >> 1, aBoundRect.Top() );
+ Point aRef2( aRef1.X(), aRef1.Y() + 1000 );
+ sal_uInt16 i;
+ sal_uInt16 nPointCount=aPol.GetSize();
+ for (i=0; i<nPointCount; i++)
+ {
+ MirrorPoint(aPol[i],aRef1,aRef2);
+ }
+ // mirror polygon and move it a bit
+ tools::Polygon aPol0(aPol);
+ aPol[0]=aPol0[1];
+ aPol[1]=aPol0[0];
+ aPol[2]=aPol0[3];
+ aPol[3]=aPol0[2];
+ aPol[4]=aPol0[1];
+ Poly2Rect(aPol,aRectangle,aNewGeo);
+ }
+ if ( bMirroredY )
+ {
+ fShearX = -fShearX;
+ tools::Polygon aPol( Rect2Poly( aRectangle, aNewGeo ) );
+ tools::Rectangle aBoundRect( aPol.GetBoundRect() );
+
+ Point aRef1( aBoundRect.Left(), ( aBoundRect.Top() + aBoundRect.Bottom() ) >> 1 );
+ Point aRef2( aRef1.X() + 1000, aRef1.Y() );
+ sal_uInt16 i;
+ sal_uInt16 nPointCount=aPol.GetSize();
+ for (i=0; i<nPointCount; i++)
+ {
+ MirrorPoint(aPol[i],aRef1,aRef2);
+ }
+ // mirror polygon and move it a bit
+ tools::Polygon aPol0(aPol);
+ aPol[0]=aPol0[1]; // This was WRONG for vertical (!)
+ aPol[1]=aPol0[0]; // #i121932# Despite my own comment above
+ aPol[2]=aPol0[3]; // it was *not* wrong even when the reordering
+ aPol[3]=aPol0[2]; // *seems* to be specific for X-Mirrorings. Oh
+ aPol[4]=aPol0[1]; // will I be happy when this old stuff is |gone| with aw080 (!)
+ Poly2Rect(aPol,aRectangle,aNewGeo);
+ }
+ }
+
+ // fill other values
+ basegfx::B2DTuple aScale(aRectangle.GetWidth(), aRectangle.GetHeight());
+ basegfx::B2DTuple aTranslate(aRectangle.Left(), aRectangle.Top());
+
+ // position may be relative to anchorpos, convert
+ if(getSdrModelFromSdrObject().IsWriter())
+ {
+ if(GetAnchorPos().X() || GetAnchorPos().Y())
+ {
+ aTranslate -= basegfx::B2DTuple(GetAnchorPos().X(), GetAnchorPos().Y());
+ }
+ }
+
+ // build matrix
+ rMatrix = basegfx::utils::createScaleShearXRotateTranslateB2DHomMatrix(
+ aScale,
+ basegfx::fTools::equalZero(fShearX) ? 0.0 : tan(fShearX),
+ basegfx::fTools::equalZero(fRotate) ? 0.0 : -fRotate,
+ aTranslate);
+
+ return false;
+}
+
+std::unique_ptr<sdr::contact::ViewContact> SdrObjCustomShape::CreateObjectSpecificViewContact()
+{
+ return std::make_unique<sdr::contact::ViewContactOfSdrObjCustomShape>(*this);
+}
+
+// #i33136#
+bool SdrObjCustomShape::doConstructOrthogonal(std::u16string_view rName)
+{
+ bool bRetval(false);
+
+ if(o3tl::equalsIgnoreAsciiCase(rName, u"quadrat"))
+ {
+ bRetval = true;
+ }
+ else if(o3tl::equalsIgnoreAsciiCase(rName, u"round-quadrat"))
+ {
+ bRetval = true;
+ }
+ else if(o3tl::equalsIgnoreAsciiCase(rName, u"circle"))
+ {
+ bRetval = true;
+ }
+ else if(o3tl::equalsIgnoreAsciiCase(rName, u"circle-pie"))
+ {
+ bRetval = true;
+ }
+ else if(o3tl::equalsIgnoreAsciiCase(rName, u"ring"))
+ {
+ bRetval = true;
+ }
+
+ return bRetval;
+}
+
+// #i37011# centralize throw-away of render geometry
+void SdrObjCustomShape::InvalidateRenderGeometry()
+{
+ mXRenderedCustomShape = nullptr;
+ SdrObject::Free( mpLastShadowGeometry );
+ mpLastShadowGeometry = nullptr;
+}
+
+void SdrObjCustomShape::setUnoShape(const uno::Reference<drawing::XShape>& rxUnoShape)
+{
+ SdrTextObj::setUnoShape(rxUnoShape);
+
+ // The shape engine is created with _current_ shape. This means we
+ // _must_ reset it when the shape changes.
+ mxCustomShapeEngine.set(nullptr);
+}
+
+OUString SdrObjCustomShape::GetCustomShapeName() const
+{
+ OUString sShapeName;
+ OUString aEngine( GetMergedItem( SDRATTR_CUSTOMSHAPE_ENGINE ).GetValue() );
+ if ( aEngine.isEmpty()
+ || aEngine == "com.sun.star.drawing.EnhancedCustomShapeEngine" )
+ {
+ OUString sShapeType;
+ const SdrCustomShapeGeometryItem& rGeometryItem( GetMergedItem( SDRATTR_CUSTOMSHAPE_GEOMETRY ) );
+ const Any* pAny = rGeometryItem.GetPropertyValueByName( "Type" );
+ if ( pAny && ( *pAny >>= sShapeType ) )
+ sShapeName = EnhancedCustomShapeTypeNames::GetAccName( sShapeType );
+ }
+ return sShapeName;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/svdraw/svdoattr.cxx b/svx/source/svdraw/svdoattr.cxx
new file mode 100644
index 000000000..2c1b208d3
--- /dev/null
+++ b/svx/source/svdraw/svdoattr.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/svdoattr.hxx>
+#include <svx/svdmodel.hxx>
+#include <svl/hint.hxx>
+#include <svl/itemset.hxx>
+#include <svx/xdef.hxx>
+#include <svx/xlineit0.hxx>
+#include <svx/xfillit0.hxx>
+#include <svx/xlnwtit.hxx>
+#include <sdr/properties/attributeproperties.hxx>
+
+using namespace com::sun::star;
+
+std::unique_ptr<sdr::properties::BaseProperties> SdrAttrObj::CreateObjectSpecificProperties()
+{
+ return std::make_unique<sdr::properties::AttributeProperties>(*this);
+}
+
+SdrAttrObj::SdrAttrObj(SdrModel& rSdrModel)
+ : SdrObject(rSdrModel)
+{
+}
+
+SdrAttrObj::SdrAttrObj(SdrModel& rSdrModel, SdrAttrObj const& rSource)
+ : SdrObject(rSdrModel, rSource)
+{
+}
+
+SdrAttrObj::~SdrAttrObj() {}
+
+const tools::Rectangle& SdrAttrObj::GetSnapRect() const
+{
+ if (m_bSnapRectDirty)
+ {
+ const_cast<SdrAttrObj*>(this)->RecalcSnapRect();
+ const_cast<SdrAttrObj*>(this)->m_bSnapRectDirty = false;
+ }
+
+ return maSnapRect;
+}
+
+// syntactical sugar for ItemSet accesses
+void SdrAttrObj::Notify(SfxBroadcaster& /*rBC*/, const SfxHint& rHint)
+{
+ bool bDataChg(SfxHintId::DataChanged == rHint.GetId());
+
+ if (bDataChg)
+ {
+ tools::Rectangle aBoundRect = GetLastBoundRect();
+ SetBoundRectDirty();
+ SetBoundAndSnapRectsDirty(/*bNotMyself*/ true);
+
+ // This may have led to object change
+ SetChanged();
+ BroadcastObjectChange();
+ SendUserCall(SdrUserCallType::ChangeAttr, aBoundRect);
+ }
+}
+
+sal_Int32 SdrAttrObj::ImpGetLineWdt() const
+{
+ sal_Int32 nRetval(0);
+
+ if (drawing::LineStyle_NONE != GetObjectItem(XATTR_LINESTYLE).GetValue())
+ {
+ nRetval = GetObjectItem(XATTR_LINEWIDTH).GetValue();
+ }
+
+ return nRetval;
+}
+
+bool SdrAttrObj::HasFill() const
+{
+ return m_bClosedObj
+ && GetProperties().GetObjectItemSet().Get(XATTR_FILLSTYLE).GetValue()
+ != drawing::FillStyle_NONE;
+}
+
+bool SdrAttrObj::HasLine() const
+{
+ return GetProperties().GetObjectItemSet().Get(XATTR_LINESTYLE).GetValue()
+ != drawing::LineStyle_NONE;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/svdraw/svdobj.cxx b/svx/source/svdraw/svdobj.cxx
new file mode 100644
index 000000000..4f13059a7
--- /dev/null
+++ b/svx/source/svdraw/svdobj.cxx
@@ -0,0 +1,3375 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * 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 <config_features.h>
+
+#include <sal/config.h>
+#include <sal/log.hxx>
+
+#include <com/sun/star/lang/XComponent.hpp>
+#include <com/sun/star/text/RelOrientation.hpp>
+#include <com/sun/star/frame/XTerminateListener.hpp>
+#include <com/sun/star/frame/Desktop.hpp>
+
+#include <basegfx/matrix/b2dhommatrix.hxx>
+#include <basegfx/matrix/b2dhommatrixtools.hxx>
+#include <basegfx/polygon/b2dpolygon.hxx>
+#include <basegfx/polygon/b2dpolygontools.hxx>
+#include <basegfx/polygon/b2dpolypolygoncutter.hxx>
+#include <basegfx/polygon/b2dpolypolygontools.hxx>
+#include <basegfx/range/b2drange.hxx>
+#include <drawinglayer/processor2d/contourextractor2d.hxx>
+#include <drawinglayer/processor2d/linegeometryextractor2d.hxx>
+#include <comphelper/processfactory.hxx>
+#include <editeng/editeng.hxx>
+#include <editeng/outlobj.hxx>
+#include <o3tl/deleter.hxx>
+#include <math.h>
+#include <svl/grabbagitem.hxx>
+#include <tools/bigint.hxx>
+#include <tools/diagnose_ex.h>
+#include <tools/helpers.hxx>
+#include <unotools/configmgr.hxx>
+#include <vcl/canvastools.hxx>
+#include <vcl/ptrstyle.hxx>
+#include <vector>
+
+#include <svx/shapepropertynotifier.hxx>
+#include <svx/svdotable.hxx>
+
+#include <svx/sdr/contact/displayinfo.hxx>
+#include <sdr/contact/objectcontactofobjlistpainter.hxx>
+#include <svx/sdr/contact/viewcontactofsdrobj.hxx>
+#include <sdr/properties/emptyproperties.hxx>
+#include <svx/sdrhittesthelper.hxx>
+#include <svx/sdrobjectuser.hxx>
+#include <svx/sdrobjectfilter.hxx>
+#include <svx/svddrag.hxx>
+#include <svx/svdetc.hxx>
+#include <svx/svdhdl.hxx>
+#include <svx/svditer.hxx>
+#include <svx/svdmodel.hxx>
+#include <svx/svdoashp.hxx>
+#include <svx/svdocapt.hxx>
+#include <svx/svdocirc.hxx>
+#include <svx/svdoedge.hxx>
+#include <svx/svdograf.hxx>
+#include <svx/svdogrp.hxx>
+#include <svx/svdomeas.hxx>
+#include <svx/svdomedia.hxx>
+#include <svx/svdoole2.hxx>
+#include <svx/svdopage.hxx>
+#include <svx/svdopath.hxx>
+#include <svx/svdorect.hxx>
+#include <svx/svdotext.hxx>
+#include <svx/svdouno.hxx>
+#include <svx/svdovirt.hxx>
+#include <svx/svdpage.hxx>
+#include <svx/svdpool.hxx>
+#include <svx/strings.hrc>
+#include <svx/dialmgr.hxx>
+#include <svx/svdtrans.hxx>
+#include <svx/svdundo.hxx>
+#include <svx/svdview.hxx>
+#include <sxlayitm.hxx>
+#include <sxlogitm.hxx>
+#include <sxmovitm.hxx>
+#include <sxoneitm.hxx>
+#include <sxopitm.hxx>
+#include <sxreoitm.hxx>
+#include <sxrooitm.hxx>
+#include <sxsaitm.hxx>
+#include <sxsoitm.hxx>
+#include <sxtraitm.hxx>
+#include <svx/unopage.hxx>
+#include <svx/unoshape.hxx>
+#include <svx/xfillit0.hxx>
+#include <svx/xflclit.hxx>
+#include <svx/xfltrit.hxx>
+#include <svx/xlineit0.hxx>
+#include <svx/xlnclit.hxx>
+#include <svx/xlnedwit.hxx>
+#include <svx/xlnstwit.hxx>
+#include <svx/xlntrit.hxx>
+#include <svx/xlnwtit.hxx>
+#include <svx/svdglue.hxx>
+#include <svx/svdsob.hxx>
+#include <svdobjplusdata.hxx>
+#include <svdobjuserdatalist.hxx>
+
+#include <unordered_set>
+
+#include <optional>
+#include <libxml/xmlwriter.h>
+#include <memory>
+
+#include <svx/scene3d.hxx>
+#include <rtl/character.hxx>
+#include <tools/UnitConversion.hxx>
+#include <o3tl/string_view.hxx>
+
+using namespace ::com::sun::star;
+
+
+SdrObjUserCall::~SdrObjUserCall()
+{
+}
+
+void SdrObjUserCall::Changed(const SdrObject& /*rObj*/, SdrUserCallType /*eType*/, const tools::Rectangle& /*rOldBoundRect*/)
+{
+}
+
+sal_Int32 SdrObjUserCall::GetPDFAnchorStructureElementId(SdrObject const&, OutputDevice const&)
+{
+ return -1;
+}
+
+SdrObjMacroHitRec::SdrObjMacroHitRec() :
+ pVisiLayer(nullptr),
+ pPageView(nullptr),
+ nTol(0) {}
+
+
+SdrObjUserData::SdrObjUserData(SdrInventor nInv, sal_uInt16 nId) :
+ nInventor(nInv),
+ nIdentifier(nId) {}
+
+SdrObjUserData::SdrObjUserData(const SdrObjUserData& rData) :
+ nInventor(rData.nInventor),
+ nIdentifier(rData.nIdentifier) {}
+
+SdrObjUserData::~SdrObjUserData() {}
+
+SdrObjGeoData::SdrObjGeoData():
+ bMovProt(false),
+ bSizProt(false),
+ bNoPrint(false),
+ bClosedObj(false),
+ mbVisible(true),
+ mnLayerID(0)
+{
+}
+
+SdrObjGeoData::~SdrObjGeoData()
+{
+}
+
+SdrObjTransformInfoRec::SdrObjTransformInfoRec() :
+ bMoveAllowed(true),
+ bResizeFreeAllowed(true),
+ bResizePropAllowed(true),
+ bRotateFreeAllowed(true),
+ bRotate90Allowed(true),
+ bMirrorFreeAllowed(true),
+ bMirror45Allowed(true),
+ bMirror90Allowed(true),
+ bTransparenceAllowed(true),
+ bShearAllowed(true),
+ bEdgeRadiusAllowed(true),
+ bNoOrthoDesired(true),
+ bNoContortion(true),
+ bCanConvToPath(true),
+ bCanConvToPoly(true),
+ bCanConvToContour(false),
+ bCanConvToPathLineToArea(true),
+ bCanConvToPolyLineToArea(true) {}
+
+struct SdrObject::Impl
+{
+ sdr::ObjectUserVector maObjectUsers;
+ std::optional<double> mnRelativeWidth;
+ std::optional<double> mnRelativeHeight;
+ sal_Int16 meRelativeWidthRelation;
+ sal_Int16 meRelativeHeightRelation;
+
+ Impl() :
+ meRelativeWidthRelation(text::RelOrientation::PAGE_FRAME),
+ meRelativeHeightRelation(text::RelOrientation::PAGE_FRAME) {}
+};
+
+const std::shared_ptr< svx::diagram::IDiagramHelper >& SdrObject::getDiagramHelper() const
+{
+ static std::shared_ptr< svx::diagram::IDiagramHelper > aEmpty;
+ return aEmpty;
+}
+
+// BaseProperties section
+
+std::unique_ptr<sdr::properties::BaseProperties> SdrObject::CreateObjectSpecificProperties()
+{
+ return std::make_unique<sdr::properties::EmptyProperties>(*this);
+}
+
+sdr::properties::BaseProperties& SdrObject::GetProperties() const
+{
+ if(!mpProperties)
+ {
+ // CAUTION(!) Do *not* call this during SdrObject construction,
+ // that will lead to wrong type-casts (dependent on constructor-level)
+ // and thus eventually create the wrong sdr::properties (!). Is there
+ // a way to check if on the stack is a SdrObject-constructor (?)
+ const_cast< SdrObject* >(this)->mpProperties =
+ const_cast< SdrObject* >(this)->CreateObjectSpecificProperties();
+ }
+
+ return *mpProperties;
+}
+
+
+// ObjectUser section
+
+void SdrObject::AddObjectUser(sdr::ObjectUser& rNewUser)
+{
+ mpImpl->maObjectUsers.push_back(&rNewUser);
+}
+
+void SdrObject::RemoveObjectUser(sdr::ObjectUser& rOldUser)
+{
+ const sdr::ObjectUserVector::iterator aFindResult =
+ std::find(mpImpl->maObjectUsers.begin(), mpImpl->maObjectUsers.end(), &rOldUser);
+ if (aFindResult != mpImpl->maObjectUsers.end())
+ {
+ mpImpl->maObjectUsers.erase(aFindResult);
+ }
+}
+
+
+// DrawContact section
+
+std::unique_ptr<sdr::contact::ViewContact> SdrObject::CreateObjectSpecificViewContact()
+{
+ return std::make_unique<sdr::contact::ViewContactOfSdrObj>(*this);
+}
+
+sdr::contact::ViewContact& SdrObject::GetViewContact() const
+{
+ if(!mpViewContact)
+ {
+ const_cast< SdrObject* >(this)->mpViewContact =
+ const_cast< SdrObject* >(this)->CreateObjectSpecificViewContact();
+ }
+
+ return *mpViewContact;
+}
+
+// DrawContact support: Methods for handling Object changes
+void SdrObject::ActionChanged() const
+{
+ // Do necessary ViewContact actions
+ GetViewContact().ActionChanged();
+}
+
+SdrPage* SdrObject::getSdrPageFromSdrObject() const
+{
+ if (SdrObjList* pParentList = getParentSdrObjListFromSdrObject())
+ {
+ return pParentList->getSdrPageFromSdrObjList();
+ }
+
+ return nullptr;
+}
+
+SdrModel& SdrObject::getSdrModelFromSdrObject() const
+{
+ return mrSdrModelFromSdrObject;
+}
+
+void SdrObject::setParentOfSdrObject(SdrObjList* pNewObjList)
+{
+ if(getParentSdrObjListFromSdrObject() == pNewObjList)
+ return;
+
+ // remember current page
+ SdrPage* pOldPage(getSdrPageFromSdrObject());
+
+ // set new parent
+ mpParentOfSdrObject = pNewObjList;
+
+ // get new page
+ SdrPage* pNewPage(getSdrPageFromSdrObject());
+
+ // broadcast page change over objects if needed
+ if(pOldPage != pNewPage)
+ {
+ handlePageChange(pOldPage, pNewPage);
+ }
+}
+
+SdrObjList* SdrObject::getParentSdrObjListFromSdrObject() const
+{
+ return mpParentOfSdrObject;
+}
+
+SdrObjList* SdrObject::getChildrenOfSdrObject() const
+{
+ // default has no children
+ return nullptr;
+}
+
+void SdrObject::SetBoundRectDirty()
+{
+ m_aOutRect = tools::Rectangle();
+}
+
+#ifdef DBG_UTIL
+// SdrObjectLifetimeWatchDog:
+void impAddIncarnatedSdrObjectToSdrModel(const SdrObject& rSdrObject, SdrModel& rSdrModel)
+{
+ rSdrModel.maAllIncarnatedObjects.insert(&rSdrObject);
+}
+void impRemoveIncarnatedSdrObjectToSdrModel(const SdrObject& rSdrObject, SdrModel& rSdrModel)
+{
+ if(!rSdrModel.maAllIncarnatedObjects.erase(&rSdrObject))
+ {
+ SAL_WARN("svx","SdrObject::~SdrObject: Destructed incarnation of SdrObject not member of this SdrModel (!)");
+ }
+}
+#endif
+
+SdrObject::SdrObject(SdrModel& rSdrModel)
+: mpFillGeometryDefiningShape(nullptr)
+ ,mrSdrModelFromSdrObject(rSdrModel)
+ ,m_pUserCall(nullptr)
+ ,mpImpl(new Impl)
+ ,mpParentOfSdrObject(nullptr)
+ ,m_nOrdNum(0)
+ ,mnNavigationPosition(SAL_MAX_UINT32)
+ ,mnLayerID(0)
+ ,mpSvxShape( nullptr )
+ ,mbDoNotInsertIntoPageAutomatically(false)
+{
+ m_bVirtObj =false;
+ m_bSnapRectDirty =true;
+ m_bMovProt =false;
+ m_bSizProt =false;
+ m_bNoPrint =false;
+ m_bEmptyPresObj =false;
+ m_bNotVisibleAsMaster=false;
+ m_bClosedObj =false;
+ mbVisible = true;
+
+ // #i25616#
+ mbLineIsOutsideGeometry = false;
+
+ // #i25616#
+ mbSupportTextIndentingOnLineWidthChange = false;
+
+ m_bIsEdge=false;
+ m_bIs3DObj=false;
+ m_bMarkProt=false;
+ m_bIsUnoObj=false;
+#ifdef DBG_UTIL
+ // SdrObjectLifetimeWatchDog:
+ impAddIncarnatedSdrObjectToSdrModel(*this, getSdrModelFromSdrObject());
+#endif
+}
+
+SdrObject::SdrObject(SdrModel& rSdrModel, SdrObject const & rSource)
+: mpFillGeometryDefiningShape(nullptr)
+ ,mrSdrModelFromSdrObject(rSdrModel)
+ ,m_pUserCall(nullptr)
+ ,mpImpl(new Impl)
+ ,mpParentOfSdrObject(nullptr)
+ ,m_nOrdNum(0)
+ ,mnNavigationPosition(SAL_MAX_UINT32)
+ ,mnLayerID(0)
+ ,mpSvxShape( nullptr )
+ ,mbDoNotInsertIntoPageAutomatically(false)
+{
+ m_bVirtObj =false;
+ m_bSnapRectDirty =true;
+ m_bMovProt =false;
+ m_bSizProt =false;
+ m_bNoPrint =false;
+ m_bEmptyPresObj =false;
+ m_bNotVisibleAsMaster=false;
+ m_bClosedObj =false;
+ mbVisible = true;
+
+ // #i25616#
+ mbLineIsOutsideGeometry = false;
+
+ // #i25616#
+ mbSupportTextIndentingOnLineWidthChange = false;
+
+ m_bIsEdge=false;
+ m_bIs3DObj=false;
+ m_bMarkProt=false;
+ m_bIsUnoObj=false;
+#ifdef DBG_UTIL
+ // SdrObjectLifetimeWatchDog:
+ impAddIncarnatedSdrObjectToSdrModel(*this, getSdrModelFromSdrObject());
+#endif
+
+ mpProperties.reset();
+ mpViewContact.reset();
+
+ // The CloneSdrObject() method uses the local copy constructor from the individual
+ // sdr::properties::BaseProperties class. Since the target class maybe for another
+ // draw object, an SdrObject needs to be provided, as in the normal constructor.
+ mpProperties = rSource.GetProperties().Clone(*this);
+
+ m_aOutRect=rSource.m_aOutRect;
+ mnLayerID = rSource.mnLayerID;
+ m_aAnchor =rSource.m_aAnchor;
+ m_bVirtObj=rSource.m_bVirtObj;
+ m_bSizProt=rSource.m_bSizProt;
+ m_bMovProt=rSource.m_bMovProt;
+ m_bNoPrint=rSource.m_bNoPrint;
+ mbVisible=rSource.mbVisible;
+ m_bMarkProt=rSource.m_bMarkProt;
+ m_bEmptyPresObj =rSource.m_bEmptyPresObj;
+ m_bNotVisibleAsMaster=rSource.m_bNotVisibleAsMaster;
+ m_bSnapRectDirty=true;
+ m_pPlusData.reset();
+ if (rSource.m_pPlusData!=nullptr) {
+ m_pPlusData.reset(rSource.m_pPlusData->Clone(this));
+ }
+ if (m_pPlusData!=nullptr && m_pPlusData->pBroadcast!=nullptr) {
+ m_pPlusData->pBroadcast.reset(); // broadcaster isn't copied
+ }
+
+ m_pGrabBagItem.reset();
+ if (rSource.m_pGrabBagItem!=nullptr)
+ m_pGrabBagItem.reset(rSource.m_pGrabBagItem->Clone());
+}
+
+SdrObject::~SdrObject()
+{
+ // Tell all the registered ObjectUsers that the page is in destruction.
+ // And clear the vector. This means that user do not need to call RemoveObjectUser()
+ // when they get called from ObjectInDestruction().
+ sdr::ObjectUserVector aList;
+ aList.swap(mpImpl->maObjectUsers);
+ for(sdr::ObjectUser* pObjectUser : aList)
+ {
+ DBG_ASSERT(pObjectUser, "SdrObject::~SdrObject: corrupt ObjectUser list (!)");
+ pObjectUser->ObjectInDestruction(*this);
+ }
+
+ // UserCall
+ SendUserCall(SdrUserCallType::Delete, GetLastBoundRect());
+ o3tl::reset_preserve_ptr_during(m_pPlusData);
+
+ m_pGrabBagItem.reset();
+ mpProperties.reset();
+ mpViewContact.reset();
+
+#ifdef DBG_UTIL
+ // SdrObjectLifetimeWatchDog:
+ impRemoveIncarnatedSdrObjectToSdrModel(*this, getSdrModelFromSdrObject());
+#endif
+}
+
+void SdrObject::Free( SdrObject*& _rpObject )
+{
+ SdrObject* pObject = _rpObject; _rpObject = nullptr;
+
+ if(nullptr == pObject)
+ {
+ // nothing to do
+ return;
+ }
+
+ SvxShape* pShape(pObject->getSvxShape());
+
+ if(pShape)
+ {
+ if(pShape->HasSdrObjectOwnership())
+ {
+ // only the SvxShape is allowed to delete me, and will reset
+ // the ownership before doing so
+ return;
+ }
+ else
+ {
+ // not only delete pObject, but also need to dispose uno shape
+ try
+ {
+ pShape->InvalidateSdrObject();
+ uno::Reference< lang::XComponent > xShapeComp( pObject->getWeakUnoShape().get(), uno::UNO_QUERY_THROW );
+ xShapeComp->dispose();
+ }
+ catch( const uno::Exception& )
+ {
+ DBG_UNHANDLED_EXCEPTION("svx");
+ }
+ }
+ }
+
+ delete pObject;
+}
+
+void SdrObject::SetBoundAndSnapRectsDirty(bool bNotMyself, bool bRecursive)
+{
+ if (!bNotMyself)
+ {
+ SetBoundRectDirty();
+ m_bSnapRectDirty=true;
+ }
+
+ if (bRecursive && nullptr != getParentSdrObjListFromSdrObject())
+ {
+ getParentSdrObjListFromSdrObject()->SetSdrObjListRectsDirty();
+ }
+}
+
+void SdrObject::handlePageChange(SdrPage* pOldPage, SdrPage* pNewPage)
+{
+ // The creation of the UNO shape in SdrObject::getUnoShape is influenced
+ // by pPage, so when the page changes we need to discard the cached UNO
+ // shape so that a new one will be created.
+ // If the page is changing to another page with the same model, we
+ // assume they create compatible UNO shape objects so we shouldn't have
+ // to invalidate.
+ // TTTT: This causes quite some problems in SvxDrawPage::add when used
+ // e.g. from Writer - the SdrObject may be cloned to target model, and
+ // the xShape was added to it by purpose (see there). Thus it will be
+ // good to think about if this is really needed - it *seems* to be intended
+ // for a xShape being a on-demand-creatable resource - with the argument that
+ // the SdrPage/UnoPage used influences the SvxShape creation. This uses
+ // resources and would be nice to get rid of anyways.
+ if(nullptr == pOldPage || nullptr == pNewPage)
+ {
+ SvxShape* const pShape(getSvxShape());
+
+ if (pShape && !pShape->HasSdrObjectOwnership())
+ {
+ setUnoShape(nullptr);
+ }
+ }
+}
+
+
+// global static ItemPool for not-yet-inserted items
+static rtl::Reference<SdrItemPool> mpGlobalItemPool;
+
+/** If we let the libc runtime clean us up, we trigger a crash */
+namespace
+{
+class TerminateListener : public ::cppu::WeakImplHelper< css::frame::XTerminateListener >
+{
+ void SAL_CALL queryTermination( const lang::EventObject& ) override
+ {}
+ void SAL_CALL notifyTermination( const lang::EventObject& ) override
+ {
+ mpGlobalItemPool.clear();
+ }
+ virtual void SAL_CALL disposing( const ::css::lang::EventObject& ) override
+ {}
+};
+};
+
+// init global static itempool
+SdrItemPool& SdrObject::GetGlobalDrawObjectItemPool()
+{
+ if(!mpGlobalItemPool)
+ {
+ mpGlobalItemPool = new SdrItemPool();
+ rtl::Reference<SfxItemPool> pGlobalOutlPool = EditEngine::CreatePool();
+ mpGlobalItemPool->SetSecondaryPool(pGlobalOutlPool.get());
+ mpGlobalItemPool->SetDefaultMetric(SdrEngineDefaults::GetMapUnit());
+ mpGlobalItemPool->FreezeIdRanges();
+ if (utl::ConfigManager::IsFuzzing())
+ mpGlobalItemPool->acquire();
+ else
+ {
+ uno::Reference< frame::XDesktop2 > xDesktop = frame::Desktop::create(comphelper::getProcessComponentContext());
+ uno::Reference< frame::XTerminateListener > xListener( new TerminateListener );
+ xDesktop->addTerminateListener( xListener );
+ }
+ }
+
+ return *mpGlobalItemPool;
+}
+
+void SdrObject::SetRelativeWidth( double nValue )
+{
+ mpImpl->mnRelativeWidth = nValue;
+}
+
+void SdrObject::SetRelativeWidthRelation( sal_Int16 eValue )
+{
+ mpImpl->meRelativeWidthRelation = eValue;
+}
+
+void SdrObject::SetRelativeHeight( double nValue )
+{
+ mpImpl->mnRelativeHeight = nValue;
+}
+
+void SdrObject::SetRelativeHeightRelation( sal_Int16 eValue )
+{
+ mpImpl->meRelativeHeightRelation = eValue;
+}
+
+const double* SdrObject::GetRelativeWidth( ) const
+{
+ if (!mpImpl->mnRelativeWidth)
+ return nullptr;
+
+ return &*mpImpl->mnRelativeWidth;
+}
+
+sal_Int16 SdrObject::GetRelativeWidthRelation() const
+{
+ return mpImpl->meRelativeWidthRelation;
+}
+
+const double* SdrObject::GetRelativeHeight( ) const
+{
+ if (!mpImpl->mnRelativeHeight)
+ return nullptr;
+
+ return &*mpImpl->mnRelativeHeight;
+}
+
+sal_Int16 SdrObject::GetRelativeHeightRelation() const
+{
+ return mpImpl->meRelativeHeightRelation;
+}
+
+SfxItemPool& SdrObject::GetObjectItemPool() const
+{
+ return getSdrModelFromSdrObject().GetItemPool();
+}
+
+SdrInventor SdrObject::GetObjInventor() const
+{
+ return SdrInventor::Default;
+}
+
+SdrObjKind SdrObject::GetObjIdentifier() const
+{
+ return SdrObjKind::NONE;
+}
+
+void SdrObject::TakeObjInfo(SdrObjTransformInfoRec& rInfo) const
+{
+ rInfo.bRotateFreeAllowed=false;
+ rInfo.bMirrorFreeAllowed=false;
+ rInfo.bTransparenceAllowed = false;
+ rInfo.bShearAllowed =false;
+ rInfo.bEdgeRadiusAllowed=false;
+ rInfo.bCanConvToPath =false;
+ rInfo.bCanConvToPoly =false;
+ rInfo.bCanConvToContour = false;
+ rInfo.bCanConvToPathLineToArea=false;
+ rInfo.bCanConvToPolyLineToArea=false;
+}
+
+SdrLayerID SdrObject::GetLayer() const
+{
+ return mnLayerID;
+}
+
+void SdrObject::getMergedHierarchySdrLayerIDSet(SdrLayerIDSet& rSet) const
+{
+ rSet.Set(GetLayer());
+ SdrObjList* pOL=GetSubList();
+ if (pOL!=nullptr) {
+ const size_t nObjCount = pOL->GetObjCount();
+ for (size_t nObjNum = 0; nObjNum<nObjCount; ++nObjNum) {
+ pOL->GetObj(nObjNum)->getMergedHierarchySdrLayerIDSet(rSet);
+ }
+ }
+}
+
+void SdrObject::NbcSetLayer(SdrLayerID nLayer)
+{
+ mnLayerID = nLayer;
+}
+
+void SdrObject::SetLayer(SdrLayerID nLayer)
+{
+ NbcSetLayer(nLayer);
+ SetChanged();
+ BroadcastObjectChange();
+}
+
+void SdrObject::AddListener(SfxListener& rListener)
+{
+ ImpForcePlusData();
+ if (m_pPlusData->pBroadcast==nullptr) m_pPlusData->pBroadcast.reset(new SfxBroadcaster);
+
+ // SdrEdgeObj may be connected to same SdrObject on both ends so allow it
+ // to listen twice
+ SdrEdgeObj const*const pEdge(dynamic_cast<SdrEdgeObj const*>(&rListener));
+ rListener.StartListening(*m_pPlusData->pBroadcast, pEdge ? DuplicateHandling::Allow : DuplicateHandling::Unexpected);
+}
+
+void SdrObject::RemoveListener(SfxListener& rListener)
+{
+ if (m_pPlusData!=nullptr && m_pPlusData->pBroadcast!=nullptr) {
+ rListener.EndListening(*m_pPlusData->pBroadcast);
+ if (!m_pPlusData->pBroadcast->HasListeners()) {
+ m_pPlusData->pBroadcast.reset();
+ }
+ }
+}
+
+const SfxBroadcaster* SdrObject::GetBroadcaster() const
+{
+ return m_pPlusData!=nullptr ? m_pPlusData->pBroadcast.get() : nullptr;
+}
+
+void SdrObject::AddReference(SdrVirtObj& rVrtObj)
+{
+ AddListener(rVrtObj);
+}
+
+void SdrObject::DelReference(SdrVirtObj& rVrtObj)
+{
+ RemoveListener(rVrtObj);
+}
+
+bool SdrObject::IsGroupObject() const
+{
+ return GetSubList()!=nullptr;
+}
+
+SdrObjList* SdrObject::GetSubList() const
+{
+ return nullptr;
+}
+
+SdrObject* SdrObject::getParentSdrObjectFromSdrObject() const
+{
+ SdrObjList* pParent(getParentSdrObjListFromSdrObject());
+
+ if(nullptr == pParent)
+ {
+ return nullptr;
+ }
+
+ return pParent->getSdrObjectFromSdrObjList();
+}
+
+void SdrObject::SetName(const OUString& rStr, const bool bSetChanged)
+{
+ if (!rStr.isEmpty() && !m_pPlusData)
+ {
+ ImpForcePlusData();
+ }
+
+ if(!(m_pPlusData && m_pPlusData->aObjName != rStr))
+ return;
+
+ // Undo/Redo for setting object's name (#i73249#)
+ bool bUndo( false );
+ if ( getSdrModelFromSdrObject().IsUndoEnabled() )
+ {
+ bUndo = true;
+ std::unique_ptr<SdrUndoAction> pUndoAction =
+ SdrUndoFactory::CreateUndoObjectStrAttr(
+ *this,
+ SdrUndoObjStrAttr::ObjStrAttrType::Name,
+ GetName(),
+ rStr );
+ getSdrModelFromSdrObject().BegUndo( pUndoAction->GetComment() );
+ getSdrModelFromSdrObject().AddUndo( std::move(pUndoAction) );
+ }
+ m_pPlusData->aObjName = rStr;
+ // Undo/Redo for setting object's name (#i73249#)
+ if ( bUndo )
+ {
+ getSdrModelFromSdrObject().EndUndo();
+ }
+ if (bSetChanged)
+ {
+ SetChanged();
+ BroadcastObjectChange();
+ }
+}
+
+const OUString & SdrObject::GetName() const
+{
+ static const OUString EMPTY = u"";
+
+ if(m_pPlusData)
+ {
+ return m_pPlusData->aObjName;
+ }
+
+ return EMPTY;
+}
+
+void SdrObject::SetTitle(const OUString& rStr)
+{
+ if (!rStr.isEmpty() && !m_pPlusData)
+ {
+ ImpForcePlusData();
+ }
+
+ if(!(m_pPlusData && m_pPlusData->aObjTitle != rStr))
+ return;
+
+ // Undo/Redo for setting object's title (#i73249#)
+ bool bUndo( false );
+ if ( getSdrModelFromSdrObject().IsUndoEnabled() )
+ {
+ bUndo = true;
+ std::unique_ptr<SdrUndoAction> pUndoAction =
+ SdrUndoFactory::CreateUndoObjectStrAttr(
+ *this,
+ SdrUndoObjStrAttr::ObjStrAttrType::Title,
+ GetTitle(),
+ rStr );
+ getSdrModelFromSdrObject().BegUndo( pUndoAction->GetComment() );
+ getSdrModelFromSdrObject().AddUndo( std::move(pUndoAction) );
+ }
+ m_pPlusData->aObjTitle = rStr;
+ // Undo/Redo for setting object's title (#i73249#)
+ if ( bUndo )
+ {
+ getSdrModelFromSdrObject().EndUndo();
+ }
+ SetChanged();
+ BroadcastObjectChange();
+}
+
+OUString SdrObject::GetTitle() const
+{
+ if(m_pPlusData)
+ {
+ return m_pPlusData->aObjTitle;
+ }
+
+ return OUString();
+}
+
+void SdrObject::SetDescription(const OUString& rStr)
+{
+ if (!rStr.isEmpty() && !m_pPlusData)
+ {
+ ImpForcePlusData();
+ }
+
+ if(!(m_pPlusData && m_pPlusData->aObjDescription != rStr))
+ return;
+
+ // Undo/Redo for setting object's description (#i73249#)
+ bool bUndo( false );
+ if ( getSdrModelFromSdrObject().IsUndoEnabled() )
+ {
+ bUndo = true;
+ std::unique_ptr<SdrUndoAction> pUndoAction =
+ SdrUndoFactory::CreateUndoObjectStrAttr(
+ *this,
+ SdrUndoObjStrAttr::ObjStrAttrType::Description,
+ GetDescription(),
+ rStr );
+ getSdrModelFromSdrObject().BegUndo( pUndoAction->GetComment() );
+ getSdrModelFromSdrObject().AddUndo( std::move(pUndoAction) );
+ }
+ m_pPlusData->aObjDescription = rStr;
+ // Undo/Redo for setting object's description (#i73249#)
+ if ( bUndo )
+ {
+ getSdrModelFromSdrObject().EndUndo();
+ }
+ SetChanged();
+ BroadcastObjectChange();
+}
+
+OUString SdrObject::GetDescription() const
+{
+ if(m_pPlusData)
+ {
+ return m_pPlusData->aObjDescription;
+ }
+
+ return OUString();
+}
+
+sal_uInt32 SdrObject::GetOrdNum() const
+{
+ if (SdrObjList* pParentList = getParentSdrObjListFromSdrObject())
+ {
+ if (pParentList->IsObjOrdNumsDirty())
+ {
+ pParentList->RecalcObjOrdNums();
+ }
+ } else const_cast<SdrObject*>(this)->m_nOrdNum=0;
+ return m_nOrdNum;
+}
+
+void SdrObject::SetOrdNum(sal_uInt32 nNum)
+{
+ m_nOrdNum = nNum;
+}
+
+void SdrObject::GetGrabBagItem(css::uno::Any& rVal) const
+{
+ if (m_pGrabBagItem != nullptr)
+ m_pGrabBagItem->QueryValue(rVal);
+ else
+ rVal <<= uno::Sequence<beans::PropertyValue>();
+}
+
+void SdrObject::SetGrabBagItem(const css::uno::Any& rVal)
+{
+ if (m_pGrabBagItem == nullptr)
+ m_pGrabBagItem.reset(new SfxGrabBagItem);
+
+ m_pGrabBagItem->PutValue(rVal, 0);
+
+ SetChanged();
+ BroadcastObjectChange();
+}
+
+sal_uInt32 SdrObject::GetNavigationPosition() const
+{
+ if (nullptr != getParentSdrObjListFromSdrObject() && getParentSdrObjListFromSdrObject()->RecalcNavigationPositions())
+ {
+ return mnNavigationPosition;
+ }
+ else
+ return GetOrdNum();
+}
+
+
+void SdrObject::SetNavigationPosition (const sal_uInt32 nNewPosition)
+{
+ mnNavigationPosition = nNewPosition;
+}
+
+
+// To make clearer that this method may trigger RecalcBoundRect and thus may be
+// expensive and sometimes problematic (inside a bigger object change you will get
+// non-useful BoundRects sometimes) I rename that method from GetBoundRect() to
+// GetCurrentBoundRect().
+const tools::Rectangle& SdrObject::GetCurrentBoundRect() const
+{
+ if(m_aOutRect.IsEmpty())
+ {
+ const_cast< SdrObject* >(this)->RecalcBoundRect();
+ }
+
+ return m_aOutRect;
+}
+
+// To have a possibility to get the last calculated BoundRect e.g for producing
+// the first rectangle for repaints (old and new need to be used) without forcing
+// a RecalcBoundRect (which may be problematical and expensive sometimes) I add here
+// a new method for accessing the last BoundRect.
+const tools::Rectangle& SdrObject::GetLastBoundRect() const
+{
+ return m_aOutRect;
+}
+
+void SdrObject::RecalcBoundRect()
+{
+ // #i101680# suppress BoundRect calculations on import(s)
+ if ((getSdrModelFromSdrObject().isLocked()) || utl::ConfigManager::IsFuzzing())
+ return;
+
+ // central new method which will calculate the BoundRect using primitive geometry
+ if(!m_aOutRect.IsEmpty())
+ return;
+
+ // Use view-independent data - we do not want any connections
+ // to e.g. GridOffset in SdrObject-level
+ drawinglayer::primitive2d::Primitive2DContainer xPrimitives;
+ GetViewContact().getViewIndependentPrimitive2DContainer(xPrimitives);
+
+ if(xPrimitives.empty())
+ return;
+
+ // use neutral ViewInformation and get the range of the primitives
+ const drawinglayer::geometry::ViewInformation2D aViewInformation2D;
+ const basegfx::B2DRange aRange(xPrimitives.getB2DRange(aViewInformation2D));
+
+ if(!aRange.isEmpty())
+ {
+ m_aOutRect = tools::Rectangle(
+ static_cast<tools::Long>(floor(aRange.getMinX())),
+ static_cast<tools::Long>(floor(aRange.getMinY())),
+ static_cast<tools::Long>(ceil(aRange.getMaxX())),
+ static_cast<tools::Long>(ceil(aRange.getMaxY())));
+ return;
+ }
+}
+
+void SdrObject::BroadcastObjectChange() const
+{
+ if ((getSdrModelFromSdrObject().isLocked()) || utl::ConfigManager::IsFuzzing())
+ return;
+
+ bool bPlusDataBroadcast(m_pPlusData && m_pPlusData->pBroadcast);
+ bool bObjectChange(IsInserted());
+
+ if(!(bPlusDataBroadcast || bObjectChange))
+ return;
+
+ SdrHint aHint(SdrHintKind::ObjectChange, *this);
+
+ if(bPlusDataBroadcast)
+ {
+ m_pPlusData->pBroadcast->Broadcast(aHint);
+ }
+
+ if(bObjectChange)
+ {
+ getSdrModelFromSdrObject().Broadcast(aHint);
+ }
+}
+
+void SdrObject::SetChanged()
+{
+ // For testing purposes, use the new ViewContact for change
+ // notification now.
+ ActionChanged();
+
+ // TTTT Need to check meaning/usage of IsInserted in one
+ // of the next changes. It should not mean to have a SdrModel
+ // set (this is guaranteed now), but should be connected to
+ // being added to a SdrPage (?)
+ // TTTT tdf#120066 Indeed - This triggers e.g. by CustomShape
+ // geometry-presenting SdrObjects that are in a SdrObjGroup,
+ // but the SdrObjGroup is *by purpose* not inserted.
+ // Need to check deeper and maybe identify all ::IsInserted()
+ // calls by rename and let the compiler work...
+ if(nullptr != getSdrPageFromSdrObject())
+ {
+ getSdrModelFromSdrObject().SetChanged();
+ }
+}
+
+// tooling for painting a single object to an OutputDevice.
+void SdrObject::SingleObjectPainter(OutputDevice& rOut) const
+{
+ sdr::contact::SdrObjectVector aObjectVector;
+ aObjectVector.push_back(const_cast< SdrObject* >(this));
+
+ sdr::contact::ObjectContactOfObjListPainter aPainter(rOut, std::move(aObjectVector), getSdrPageFromSdrObject());
+ sdr::contact::DisplayInfo aDisplayInfo;
+
+ aPainter.ProcessDisplay(aDisplayInfo);
+}
+
+bool SdrObject::LineGeometryUsageIsNecessary() const
+{
+ drawing::LineStyle eXLS = GetMergedItem(XATTR_LINESTYLE).GetValue();
+ return (eXLS != drawing::LineStyle_NONE);
+}
+
+bool SdrObject::HasLimitedRotation() const
+{
+ // RotGrfFlyFrame: Default is false, support full rotation
+ return false;
+}
+
+SdrObject* SdrObject::CloneSdrObject(SdrModel& rTargetModel) const
+{
+ return new SdrObject(rTargetModel, *this);
+}
+
+OUString SdrObject::TakeObjNameSingul() const
+{
+ OUString sName(SvxResId(STR_ObjNameSingulNONE));
+
+ OUString aName(GetName());
+ if (!aName.isEmpty())
+ sName += " '" + aName + "'";
+ return sName;
+}
+
+OUString SdrObject::TakeObjNamePlural() const
+{
+ return SvxResId(STR_ObjNamePluralNONE);
+}
+
+OUString SdrObject::ImpGetDescriptionStr(TranslateId pStrCacheID) const
+{
+ OUString aStr = SvxResId(pStrCacheID);
+ sal_Int32 nPos = aStr.indexOf("%1");
+ if (nPos >= 0)
+ {
+ // Replace '%1' with the object name.
+ OUString aObjName(TakeObjNameSingul());
+ aStr = aStr.replaceAt(nPos, 2, aObjName);
+ }
+
+ nPos = aStr.indexOf("%2");
+ if (nPos >= 0)
+ // Replace '%2' with the passed value.
+ aStr = aStr.replaceAt(nPos, 2, u"0");
+ return aStr;
+}
+
+void SdrObject::ImpForcePlusData()
+{
+ if (!m_pPlusData)
+ m_pPlusData.reset( new SdrObjPlusData );
+}
+
+OUString SdrObject::GetMetrStr(tools::Long nVal) const
+{
+ return getSdrModelFromSdrObject().GetMetricString(nVal);
+}
+
+basegfx::B2DPolyPolygon SdrObject::TakeXorPoly() const
+{
+ basegfx::B2DPolyPolygon aRetval;
+ const tools::Rectangle aR(GetCurrentBoundRect());
+ aRetval.append(basegfx::utils::createPolygonFromRect(vcl::unotools::b2DRectangleFromRectangle(aR)));
+
+ return aRetval;
+}
+
+basegfx::B2DPolyPolygon SdrObject::TakeContour() const
+{
+ basegfx::B2DPolyPolygon aRetval;
+
+ // create cloned object without text, but with drawing::LineStyle_SOLID,
+ // COL_BLACK as line color and drawing::FillStyle_NONE
+ SdrObject* pClone(CloneSdrObject(getSdrModelFromSdrObject()));
+
+ if(pClone)
+ {
+ const SdrTextObj* pTextObj = dynamic_cast< const SdrTextObj* >(this);
+
+ if(pTextObj)
+ {
+ // no text and no text animation
+ pClone->SetMergedItem(SdrTextAniKindItem(SdrTextAniKind::NONE));
+ pClone->SetOutlinerParaObject(std::nullopt);
+ }
+
+ const SdrEdgeObj* pEdgeObj = dynamic_cast< const SdrEdgeObj* >(this);
+
+ if(pEdgeObj)
+ {
+ // create connections if connector, will be cleaned up when
+ // deleting the connector again
+ SdrObject* pLeft = pEdgeObj->GetConnectedNode(true);
+ SdrObject* pRight = pEdgeObj->GetConnectedNode(false);
+
+ if(pLeft)
+ {
+ pClone->ConnectToNode(true, pLeft);
+ }
+
+ if(pRight)
+ {
+ pClone->ConnectToNode(false, pRight);
+ }
+ }
+
+ SfxItemSet aNewSet(GetObjectItemPool());
+
+ // #i101980# ignore LineWidth; that's what the old implementation
+ // did. With line width, the result may be huge due to fat/thick
+ // line decompositions
+ aNewSet.Put(XLineWidthItem(0));
+
+ // solid black lines and no fill
+ aNewSet.Put(XLineStyleItem(drawing::LineStyle_SOLID));
+ aNewSet.Put(XLineColorItem(OUString(), COL_BLACK));
+ aNewSet.Put(XFillStyleItem(drawing::FillStyle_NONE));
+ pClone->SetMergedItemSet(aNewSet);
+
+ // get sequence from clone
+ const sdr::contact::ViewContact& rVC(pClone->GetViewContact());
+ drawinglayer::primitive2d::Primitive2DContainer xSequence;
+ rVC.getViewIndependentPrimitive2DContainer(xSequence);
+
+ if(!xSequence.empty())
+ {
+ // use neutral ViewInformation
+ const drawinglayer::geometry::ViewInformation2D aViewInformation2D;
+
+ // create extractor, process and get result (with hairlines as opened polygons)
+ drawinglayer::processor2d::ContourExtractor2D aExtractor(aViewInformation2D, false);
+ aExtractor.process(xSequence);
+ const basegfx::B2DPolyPolygonVector& rResult(aExtractor.getExtractedContour());
+ const sal_uInt32 nSize(rResult.size());
+
+ // when count is one, it is implied that the object has only its normal
+ // contour anyways and TakeContour() is to return an empty PolyPolygon
+ // (see old implementation for historical reasons)
+ if(nSize > 1)
+ {
+ // the topology for contour is correctly a vector of PolyPolygons; for
+ // historical reasons cut it back to a single tools::PolyPolygon here
+ for(sal_uInt32 a(0); a < nSize; a++)
+ {
+ aRetval.append(rResult[a]);
+ }
+ }
+ }
+
+ // Always use SdrObject::Free to delete SdrObjects (!)
+ SdrObject::Free(pClone);
+ }
+
+ return aRetval;
+}
+
+sal_uInt32 SdrObject::GetHdlCount() const
+{
+ return 8;
+}
+
+void SdrObject::AddToHdlList(SdrHdlList& rHdlList) const
+{
+ const tools::Rectangle& rR=GetSnapRect();
+ for (sal_uInt32 nHdlNum=0; nHdlNum<8; ++nHdlNum)
+ {
+ std::unique_ptr<SdrHdl> pH;
+ switch (nHdlNum) {
+ case 0: pH.reset(new SdrHdl(rR.TopLeft(), SdrHdlKind::UpperLeft)); break;
+ case 1: pH.reset(new SdrHdl(rR.TopCenter(), SdrHdlKind::Upper)); break;
+ case 2: pH.reset(new SdrHdl(rR.TopRight(), SdrHdlKind::UpperRight)); break;
+ case 3: pH.reset(new SdrHdl(rR.LeftCenter(), SdrHdlKind::Left )); break;
+ case 4: pH.reset(new SdrHdl(rR.RightCenter(), SdrHdlKind::Right)); break;
+ case 5: pH.reset(new SdrHdl(rR.BottomLeft(), SdrHdlKind::LowerLeft)); break;
+ case 6: pH.reset(new SdrHdl(rR.BottomCenter(),SdrHdlKind::Lower)); break;
+ case 7: pH.reset(new SdrHdl(rR.BottomRight(), SdrHdlKind::LowerRight)); break;
+ }
+ rHdlList.AddHdl(std::move(pH));
+ }
+}
+
+void SdrObject::AddToPlusHdlList(SdrHdlList&, SdrHdl&) const
+{
+}
+
+void SdrObject::addCropHandles(SdrHdlList& /*rTarget*/) const
+{
+ // Default implementation, does nothing. Overloaded in
+ // SdrGrafObj and SwVirtFlyDrawObj
+}
+
+tools::Rectangle SdrObject::ImpDragCalcRect(const SdrDragStat& rDrag) const
+{
+ tools::Rectangle aTmpRect(GetSnapRect());
+ tools::Rectangle aRect(aTmpRect);
+ const SdrHdl* pHdl=rDrag.GetHdl();
+ SdrHdlKind eHdl=pHdl==nullptr ? SdrHdlKind::Move : pHdl->GetKind();
+ bool bEcke=(eHdl==SdrHdlKind::UpperLeft || eHdl==SdrHdlKind::UpperRight || eHdl==SdrHdlKind::LowerLeft || eHdl==SdrHdlKind::LowerRight);
+ bool bOrtho=rDrag.GetView()!=nullptr && rDrag.GetView()->IsOrtho();
+ bool bBigOrtho=bEcke && bOrtho && rDrag.GetView()->IsBigOrtho();
+ Point aPos(rDrag.GetNow());
+ bool bLft=(eHdl==SdrHdlKind::UpperLeft || eHdl==SdrHdlKind::Left || eHdl==SdrHdlKind::LowerLeft);
+ bool bRgt=(eHdl==SdrHdlKind::UpperRight || eHdl==SdrHdlKind::Right || eHdl==SdrHdlKind::LowerRight);
+ bool bTop=(eHdl==SdrHdlKind::UpperRight || eHdl==SdrHdlKind::Upper || eHdl==SdrHdlKind::UpperLeft);
+ bool bBtm=(eHdl==SdrHdlKind::LowerRight || eHdl==SdrHdlKind::Lower || eHdl==SdrHdlKind::LowerLeft);
+ if (bLft) aTmpRect.SetLeft(aPos.X() );
+ if (bRgt) aTmpRect.SetRight(aPos.X() );
+ if (bTop) aTmpRect.SetTop(aPos.Y() );
+ if (bBtm) aTmpRect.SetBottom(aPos.Y() );
+ if (bOrtho) { // Ortho
+ tools::Long nWdt0=aRect.Right() -aRect.Left();
+ tools::Long nHgt0=aRect.Bottom()-aRect.Top();
+ tools::Long nXMul=aTmpRect.Right() -aTmpRect.Left();
+ tools::Long nYMul=aTmpRect.Bottom()-aTmpRect.Top();
+ tools::Long nXDiv=nWdt0;
+ tools::Long nYDiv=nHgt0;
+ bool bXNeg=(nXMul<0)!=(nXDiv<0);
+ bool bYNeg=(nYMul<0)!=(nYDiv<0);
+ nXMul=std::abs(nXMul);
+ nYMul=std::abs(nYMul);
+ nXDiv=std::abs(nXDiv);
+ nYDiv=std::abs(nYDiv);
+ Fraction aXFact(nXMul,nXDiv); // fractions for canceling
+ Fraction aYFact(nYMul,nYDiv); // and for comparing
+ nXMul=aXFact.GetNumerator();
+ nYMul=aYFact.GetNumerator();
+ nXDiv=aXFact.GetDenominator();
+ nYDiv=aYFact.GetDenominator();
+ if (bEcke) { // corner point handles
+ bool bUseX=(aXFact<aYFact) != bBigOrtho;
+ if (bUseX) {
+ tools::Long nNeed=tools::Long(BigInt(nHgt0)*BigInt(nXMul)/BigInt(nXDiv));
+ if (bYNeg) nNeed=-nNeed;
+ if (bTop) aTmpRect.SetTop(aTmpRect.Bottom()-nNeed );
+ if (bBtm) aTmpRect.SetBottom(aTmpRect.Top()+nNeed );
+ } else {
+ tools::Long nNeed=tools::Long(BigInt(nWdt0)*BigInt(nYMul)/BigInt(nYDiv));
+ if (bXNeg) nNeed=-nNeed;
+ if (bLft) aTmpRect.SetLeft(aTmpRect.Right()-nNeed );
+ if (bRgt) aTmpRect.SetRight(aTmpRect.Left()+nNeed );
+ }
+ } else { // apex handles
+ if ((bLft || bRgt) && nXDiv!=0) {
+ tools::Long nHgt0b=aRect.Bottom()-aRect.Top();
+ tools::Long nNeed=tools::Long(BigInt(nHgt0b)*BigInt(nXMul)/BigInt(nXDiv));
+ aTmpRect.AdjustTop( -((nNeed-nHgt0b)/2) );
+ aTmpRect.SetBottom(aTmpRect.Top()+nNeed );
+ }
+ if ((bTop || bBtm) && nYDiv!=0) {
+ tools::Long nWdt0b=aRect.Right()-aRect.Left();
+ tools::Long nNeed=tools::Long(BigInt(nWdt0b)*BigInt(nYMul)/BigInt(nYDiv));
+ aTmpRect.AdjustLeft( -((nNeed-nWdt0b)/2) );
+ aTmpRect.SetRight(aTmpRect.Left()+nNeed );
+ }
+ }
+ }
+ aTmpRect.Justify();
+ return aTmpRect;
+}
+
+
+bool SdrObject::hasSpecialDrag() const
+{
+ return false;
+}
+
+bool SdrObject::supportsFullDrag() const
+{
+ return true;
+}
+
+SdrObjectUniquePtr SdrObject::getFullDragClone() const
+{
+ // default uses simple clone
+ return SdrObjectUniquePtr(CloneSdrObject(getSdrModelFromSdrObject()));
+}
+
+bool SdrObject::beginSpecialDrag(SdrDragStat& rDrag) const
+{
+ const SdrHdl* pHdl = rDrag.GetHdl();
+
+ SdrHdlKind eHdl = (pHdl == nullptr) ? SdrHdlKind::Move : pHdl->GetKind();
+
+ return eHdl==SdrHdlKind::UpperLeft || eHdl==SdrHdlKind::Upper || eHdl==SdrHdlKind::UpperRight ||
+ eHdl==SdrHdlKind::Left || eHdl==SdrHdlKind::Right || eHdl==SdrHdlKind::LowerLeft ||
+ eHdl==SdrHdlKind::Lower || eHdl==SdrHdlKind::LowerRight;
+}
+
+bool SdrObject::applySpecialDrag(SdrDragStat& rDrag)
+{
+ tools::Rectangle aNewRect(ImpDragCalcRect(rDrag));
+
+ if(aNewRect != GetSnapRect())
+ {
+ NbcSetSnapRect(aNewRect);
+ }
+
+ return true;
+}
+
+OUString SdrObject::getSpecialDragComment(const SdrDragStat& /*rDrag*/) const
+{
+ return OUString();
+}
+
+basegfx::B2DPolyPolygon SdrObject::getSpecialDragPoly(const SdrDragStat& /*rDrag*/) const
+{
+ // default has nothing to add
+ return basegfx::B2DPolyPolygon();
+}
+
+
+// Create
+bool SdrObject::BegCreate(SdrDragStat& rStat)
+{
+ rStat.SetOrtho4Possible();
+ tools::Rectangle aRect1(rStat.GetStart(), rStat.GetNow());
+ aRect1.Justify();
+ rStat.SetActionRect(aRect1);
+ m_aOutRect = aRect1;
+ return true;
+}
+
+bool SdrObject::MovCreate(SdrDragStat& rStat)
+{
+ rStat.TakeCreateRect(m_aOutRect);
+ rStat.SetActionRect(m_aOutRect);
+ m_aOutRect.Justify();
+
+ return true;
+}
+
+bool SdrObject::EndCreate(SdrDragStat& rStat, SdrCreateCmd eCmd)
+{
+ rStat.TakeCreateRect(m_aOutRect);
+ m_aOutRect.Justify();
+
+ return (eCmd==SdrCreateCmd::ForceEnd || rStat.GetPointCount()>=2);
+}
+
+void SdrObject::BrkCreate(SdrDragStat& /*rStat*/)
+{
+}
+
+bool SdrObject::BckCreate(SdrDragStat& /*rStat*/)
+{
+ return false;
+}
+
+basegfx::B2DPolyPolygon SdrObject::TakeCreatePoly(const SdrDragStat& rDrag) const
+{
+ tools::Rectangle aRect1;
+ rDrag.TakeCreateRect(aRect1);
+ aRect1.Justify();
+
+ basegfx::B2DPolyPolygon aRetval;
+ aRetval.append(basegfx::utils::createPolygonFromRect(vcl::unotools::b2DRectangleFromRectangle(aRect1)));
+ return aRetval;
+}
+
+PointerStyle SdrObject::GetCreatePointer() const
+{
+ return PointerStyle::Cross;
+}
+
+// transformations
+void SdrObject::NbcMove(const Size& rSiz)
+{
+ m_aOutRect.Move(rSiz);
+ SetBoundAndSnapRectsDirty();
+}
+
+void SdrObject::NbcResize(const Point& rRef, const Fraction& xFact, const Fraction& yFact)
+{
+ bool bXMirr=(xFact.GetNumerator()<0) != (xFact.GetDenominator()<0);
+ bool bYMirr=(yFact.GetNumerator()<0) != (yFact.GetDenominator()<0);
+ if (bXMirr || bYMirr) {
+ Point aRef1(GetSnapRect().Center());
+ if (bXMirr) {
+ Point aRef2(aRef1);
+ aRef2.AdjustY( 1 );
+ NbcMirrorGluePoints(aRef1,aRef2);
+ }
+ if (bYMirr) {
+ Point aRef2(aRef1);
+ aRef2.AdjustX( 1 );
+ NbcMirrorGluePoints(aRef1,aRef2);
+ }
+ }
+ ResizeRect(m_aOutRect,rRef,xFact,yFact);
+ SetBoundAndSnapRectsDirty();
+}
+
+void SdrObject::NbcRotate(const Point& rRef, Degree100 nAngle)
+{
+ if (nAngle)
+ {
+ double a = toRadians(nAngle);
+ NbcRotate( rRef, nAngle, sin( a ), cos( a ) );
+ }
+}
+
+void SdrObject::NbcRotate(const Point& rRef, Degree100 nAngle, double sn, double cs)
+{
+ SetGlueReallyAbsolute(true);
+ m_aOutRect.Move(-rRef.X(),-rRef.Y());
+ tools::Rectangle R(m_aOutRect);
+ if (sn==1.0 && cs==0.0) { // 90deg
+ m_aOutRect.SetLeft(-R.Bottom() );
+ m_aOutRect.SetRight(-R.Top() );
+ m_aOutRect.SetTop(R.Left() );
+ m_aOutRect.SetBottom(R.Right() );
+ } else if (sn==0.0 && cs==-1.0) { // 180deg
+ m_aOutRect.SetLeft(-R.Right() );
+ m_aOutRect.SetRight(-R.Left() );
+ m_aOutRect.SetTop(-R.Bottom() );
+ m_aOutRect.SetBottom(-R.Top() );
+ } else if (sn==-1.0 && cs==0.0) { // 270deg
+ m_aOutRect.SetLeft(R.Top() );
+ m_aOutRect.SetRight(R.Bottom() );
+ m_aOutRect.SetTop(-R.Right() );
+ m_aOutRect.SetBottom(-R.Left() );
+ }
+ m_aOutRect.Move(rRef.X(),rRef.Y());
+ m_aOutRect.Justify(); // just in case
+ SetBoundAndSnapRectsDirty();
+ NbcRotateGluePoints(rRef,nAngle,sn,cs);
+ SetGlueReallyAbsolute(false);
+}
+
+void SdrObject::NbcMirror(const Point& rRef1, const Point& rRef2)
+{
+ SetGlueReallyAbsolute(true);
+ m_aOutRect.Move(-rRef1.X(),-rRef1.Y());
+ tools::Rectangle R(m_aOutRect);
+ tools::Long dx=rRef2.X()-rRef1.X();
+ tools::Long dy=rRef2.Y()-rRef1.Y();
+ if (dx==0) { // vertical axis
+ m_aOutRect.SetLeft(-R.Right() );
+ m_aOutRect.SetRight(-R.Left() );
+ } else if (dy==0) { // horizontal axis
+ m_aOutRect.SetTop(-R.Bottom() );
+ m_aOutRect.SetBottom(-R.Top() );
+ } else if (dx==dy) { // 45deg axis
+ m_aOutRect.SetLeft(R.Top() );
+ m_aOutRect.SetRight(R.Bottom() );
+ m_aOutRect.SetTop(R.Left() );
+ m_aOutRect.SetBottom(R.Right() );
+ } else if (dx==-dy) { // 45deg axis
+ m_aOutRect.SetLeft(-R.Bottom() );
+ m_aOutRect.SetRight(-R.Top() );
+ m_aOutRect.SetTop(-R.Right() );
+ m_aOutRect.SetBottom(-R.Left() );
+ }
+ m_aOutRect.Move(rRef1.X(),rRef1.Y());
+ m_aOutRect.Justify(); // just in case
+ SetBoundAndSnapRectsDirty();
+ NbcMirrorGluePoints(rRef1,rRef2);
+ SetGlueReallyAbsolute(false);
+}
+
+void SdrObject::NbcShear(const Point& rRef, Degree100 /*nAngle*/, double tn, bool bVShear)
+{
+ SetGlueReallyAbsolute(true);
+ NbcShearGluePoints(rRef,tn,bVShear);
+ SetGlueReallyAbsolute(false);
+}
+
+void SdrObject::Move(const Size& rSiz)
+{
+ if (rSiz.Width()!=0 || rSiz.Height()!=0) {
+ tools::Rectangle aBoundRect0; if (m_pUserCall!=nullptr) aBoundRect0=GetLastBoundRect();
+ NbcMove(rSiz);
+ SetChanged();
+ BroadcastObjectChange();
+ SendUserCall(SdrUserCallType::MoveOnly,aBoundRect0);
+ }
+}
+
+void SdrObject::NbcCrop(const basegfx::B2DPoint& /*aRef*/, double /*fxFact*/, double /*fyFact*/)
+{
+ // Default: does nothing. Real behaviour in SwVirtFlyDrawObj and SdrDragCrop::EndSdrDrag.
+ // Where SwVirtFlyDrawObj is the only real user of it to do something local
+}
+
+void SdrObject::Resize(const Point& rRef, const Fraction& xFact, const Fraction& yFact, bool bUnsetRelative)
+{
+ if (xFact.GetNumerator() == xFact.GetDenominator() && yFact.GetNumerator() == yFact.GetDenominator())
+ return;
+
+ if (bUnsetRelative)
+ {
+ mpImpl->mnRelativeWidth.reset();
+ mpImpl->meRelativeWidthRelation = text::RelOrientation::PAGE_FRAME;
+ mpImpl->meRelativeHeightRelation = text::RelOrientation::PAGE_FRAME;
+ mpImpl->mnRelativeHeight.reset();
+ }
+ tools::Rectangle aBoundRect0; if (m_pUserCall!=nullptr) aBoundRect0=GetLastBoundRect();
+ NbcResize(rRef,xFact,yFact);
+ SetChanged();
+ BroadcastObjectChange();
+ SendUserCall(SdrUserCallType::Resize,aBoundRect0);
+}
+
+void SdrObject::Crop(const basegfx::B2DPoint& rRef, double fxFact, double fyFact)
+{
+ tools::Rectangle aBoundRect0; if (m_pUserCall!=nullptr) aBoundRect0=GetLastBoundRect();
+ NbcCrop(rRef, fxFact, fyFact);
+ SetChanged();
+ BroadcastObjectChange();
+ SendUserCall(SdrUserCallType::Resize,aBoundRect0);
+}
+
+void SdrObject::Rotate(const Point& rRef, Degree100 nAngle, double sn, double cs)
+{
+ if (nAngle) {
+ tools::Rectangle aBoundRect0; if (m_pUserCall!=nullptr) aBoundRect0=GetLastBoundRect();
+ NbcRotate(rRef,nAngle,sn,cs);
+ SetChanged();
+ BroadcastObjectChange();
+ SendUserCall(SdrUserCallType::Resize,aBoundRect0);
+ }
+}
+
+void SdrObject::Mirror(const Point& rRef1, const Point& rRef2)
+{
+ tools::Rectangle aBoundRect0; if (m_pUserCall!=nullptr) aBoundRect0=GetLastBoundRect();
+ NbcMirror(rRef1,rRef2);
+ SetChanged();
+ BroadcastObjectChange();
+ SendUserCall(SdrUserCallType::Resize,aBoundRect0);
+}
+
+void SdrObject::Shear(const Point& rRef, Degree100 nAngle, double tn, bool bVShear)
+{
+ if (nAngle) {
+ tools::Rectangle aBoundRect0; if (m_pUserCall!=nullptr) aBoundRect0=GetLastBoundRect();
+ NbcShear(rRef,nAngle,tn,bVShear);
+ SetChanged();
+ BroadcastObjectChange();
+ SendUserCall(SdrUserCallType::Resize,aBoundRect0);
+ }
+}
+
+void SdrObject::NbcSetRelativePos(const Point& rPnt)
+{
+ Point aRelPos0(GetSnapRect().TopLeft()-m_aAnchor);
+ Size aSiz(rPnt.X()-aRelPos0.X(),rPnt.Y()-aRelPos0.Y());
+ NbcMove(aSiz); // This also calls SetRectsDirty()
+}
+
+void SdrObject::SetRelativePos(const Point& rPnt)
+{
+ if (rPnt!=GetRelativePos()) {
+ tools::Rectangle aBoundRect0; if (m_pUserCall!=nullptr) aBoundRect0=GetLastBoundRect();
+ NbcSetRelativePos(rPnt);
+ SetChanged();
+ BroadcastObjectChange();
+ SendUserCall(SdrUserCallType::MoveOnly,aBoundRect0);
+ }
+}
+
+Point SdrObject::GetRelativePos() const
+{
+ return GetSnapRect().TopLeft()-m_aAnchor;
+}
+
+void SdrObject::ImpSetAnchorPos(const Point& rPnt)
+{
+ m_aAnchor = rPnt;
+}
+
+void SdrObject::NbcSetAnchorPos(const Point& rPnt)
+{
+ Size aSiz(rPnt.X()-m_aAnchor.X(),rPnt.Y()-m_aAnchor.Y());
+ m_aAnchor=rPnt;
+ NbcMove(aSiz); // This also calls SetRectsDirty()
+}
+
+void SdrObject::SetAnchorPos(const Point& rPnt)
+{
+ if (rPnt!=m_aAnchor) {
+ tools::Rectangle aBoundRect0; if (m_pUserCall!=nullptr) aBoundRect0=GetLastBoundRect();
+ NbcSetAnchorPos(rPnt);
+ SetChanged();
+ BroadcastObjectChange();
+ SendUserCall(SdrUserCallType::MoveOnly,aBoundRect0);
+ }
+}
+
+const Point& SdrObject::GetAnchorPos() const
+{
+ return m_aAnchor;
+}
+
+void SdrObject::RecalcSnapRect()
+{
+}
+
+const tools::Rectangle& SdrObject::GetSnapRect() const
+{
+ return m_aOutRect;
+}
+
+void SdrObject::NbcSetSnapRect(const tools::Rectangle& rRect)
+{
+ m_aOutRect=rRect;
+}
+
+const tools::Rectangle& SdrObject::GetLogicRect() const
+{
+ return GetSnapRect();
+}
+
+void SdrObject::NbcSetLogicRect(const tools::Rectangle& rRect)
+{
+ NbcSetSnapRect(rRect);
+}
+
+void SdrObject::AdjustToMaxRect( const tools::Rectangle& rMaxRect, bool /* bShrinkOnly = false */ )
+{
+ SetLogicRect( rMaxRect );
+}
+
+void SdrObject::SetSnapRect(const tools::Rectangle& rRect)
+{
+ tools::Rectangle aBoundRect0; if (m_pUserCall!=nullptr) aBoundRect0=GetLastBoundRect();
+ NbcSetSnapRect(rRect);
+ SetChanged();
+ BroadcastObjectChange();
+ SendUserCall(SdrUserCallType::Resize,aBoundRect0);
+}
+
+void SdrObject::SetLogicRect(const tools::Rectangle& rRect)
+{
+ tools::Rectangle aBoundRect0; if (m_pUserCall!=nullptr) aBoundRect0=GetLastBoundRect();
+ NbcSetLogicRect(rRect);
+ SetChanged();
+ BroadcastObjectChange();
+ SendUserCall(SdrUserCallType::Resize,aBoundRect0);
+}
+
+Degree100 SdrObject::GetRotateAngle() const
+{
+ return 0_deg100;
+}
+
+Degree100 SdrObject::GetShearAngle(bool /*bVertical*/) const
+{
+ return 0_deg100;
+}
+
+sal_uInt32 SdrObject::GetSnapPointCount() const
+{
+ return GetPointCount();
+}
+
+Point SdrObject::GetSnapPoint(sal_uInt32 i) const
+{
+ return GetPoint(i);
+}
+
+bool SdrObject::IsPolyObj() const
+{
+ return false;
+}
+
+sal_uInt32 SdrObject::GetPointCount() const
+{
+ return 0;
+}
+
+Point SdrObject::GetPoint(sal_uInt32 /*i*/) const
+{
+ return Point();
+}
+
+void SdrObject::SetPoint(const Point& rPnt, sal_uInt32 i)
+{
+ tools::Rectangle aBoundRect0; if (m_pUserCall!=nullptr) aBoundRect0=GetLastBoundRect();
+ NbcSetPoint(rPnt, i);
+ SetChanged();
+ BroadcastObjectChange();
+ SendUserCall(SdrUserCallType::Resize,aBoundRect0);
+}
+
+void SdrObject::NbcSetPoint(const Point& /*rPnt*/, sal_uInt32 /*i*/)
+{
+}
+
+bool SdrObject::HasTextEdit() const
+{
+ return false;
+}
+
+bool SdrObject::Equals(const SdrObject& rOtherObj) const
+{
+ return (m_aAnchor.X() == rOtherObj.m_aAnchor.X() && m_aAnchor.Y() == rOtherObj.m_aAnchor.Y() &&
+ m_nOrdNum == rOtherObj.m_nOrdNum && mnNavigationPosition == rOtherObj.mnNavigationPosition &&
+ mbSupportTextIndentingOnLineWidthChange == rOtherObj.mbSupportTextIndentingOnLineWidthChange &&
+ mbLineIsOutsideGeometry == rOtherObj.mbLineIsOutsideGeometry && m_bMarkProt == rOtherObj.m_bMarkProt &&
+ m_bIs3DObj == rOtherObj.m_bIs3DObj && m_bIsEdge == rOtherObj.m_bIsEdge && m_bClosedObj == rOtherObj.m_bClosedObj &&
+ m_bNotVisibleAsMaster == rOtherObj.m_bNotVisibleAsMaster && m_bEmptyPresObj == rOtherObj.m_bEmptyPresObj &&
+ mbVisible == rOtherObj.mbVisible && m_bNoPrint == rOtherObj.m_bNoPrint && m_bSizProt == rOtherObj.m_bSizProt &&
+ m_bMovProt == rOtherObj.m_bMovProt && m_bVirtObj == rOtherObj.m_bVirtObj &&
+ mnLayerID == rOtherObj.mnLayerID && GetMergedItemSet().Equals(rOtherObj.GetMergedItemSet(), false) );
+}
+
+void SdrObject::dumpAsXml(xmlTextWriterPtr pWriter) const
+{
+ (void)xmlTextWriterStartElement(pWriter, BAD_CAST("SdrObject"));
+ (void)xmlTextWriterWriteFormatAttribute(pWriter, BAD_CAST("ptr"), "%p", this);
+ (void)xmlTextWriterWriteFormatAttribute(pWriter, BAD_CAST("symbol"), "%s", BAD_CAST(typeid(*this).name()));
+ (void)xmlTextWriterWriteFormatAttribute(pWriter, BAD_CAST("name"), "%s", BAD_CAST(GetName().toUtf8().getStr()));
+ (void)xmlTextWriterWriteFormatAttribute(pWriter, BAD_CAST("title"), "%s", BAD_CAST(GetTitle().toUtf8().getStr()));
+ (void)xmlTextWriterWriteFormatAttribute(pWriter, BAD_CAST("description"), "%s", BAD_CAST(GetDescription().toUtf8().getStr()));
+ (void)xmlTextWriterWriteFormatAttribute(pWriter, BAD_CAST("nOrdNum"), "%" SAL_PRIuUINT32, GetOrdNumDirect());
+ (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("aOutRect"), BAD_CAST(m_aOutRect.toString().getStr()));
+
+ if (m_pGrabBagItem)
+ {
+ m_pGrabBagItem->dumpAsXml(pWriter);
+ }
+
+ if (mpProperties)
+ {
+ mpProperties->dumpAsXml(pWriter);
+ }
+
+ if (const OutlinerParaObject* pOutliner = GetOutlinerParaObject())
+ pOutliner->dumpAsXml(pWriter);
+
+ (void)xmlTextWriterEndElement(pWriter);
+}
+
+void SdrObject::SetOutlinerParaObject(std::optional<OutlinerParaObject> pTextObject)
+{
+ tools::Rectangle aBoundRect0; if (m_pUserCall!=nullptr) aBoundRect0=GetLastBoundRect();
+ NbcSetOutlinerParaObject(std::move(pTextObject));
+ SetChanged();
+ BroadcastObjectChange();
+ if (GetCurrentBoundRect()!=aBoundRect0) {
+ SendUserCall(SdrUserCallType::Resize,aBoundRect0);
+ }
+
+ if (!getSdrModelFromSdrObject().IsUndoEnabled())
+ return;
+
+ // Don't do this during import.
+ SdrObject* pTopGroupObj = nullptr;
+ if (getParentSdrObjectFromSdrObject())
+ {
+ pTopGroupObj = getParentSdrObjectFromSdrObject();
+ while (pTopGroupObj->getParentSdrObjectFromSdrObject())
+ {
+ pTopGroupObj = pTopGroupObj->getParentSdrObjectFromSdrObject();
+ }
+ }
+ if (pTopGroupObj)
+ {
+ // A shape was modified, which is in a group shape. Empty the group shape's grab-bag,
+ // which potentially contains the old text of the shapes in case of diagrams.
+ pTopGroupObj->SetGrabBagItem(uno::Any(uno::Sequence<beans::PropertyValue>()));
+ }
+}
+
+void SdrObject::NbcSetOutlinerParaObject(std::optional<OutlinerParaObject> /*pTextObject*/)
+{
+}
+
+OutlinerParaObject* SdrObject::GetOutlinerParaObject() const
+{
+ return nullptr;
+}
+
+void SdrObject::NbcReformatText()
+{
+}
+
+void SdrObject::BurnInStyleSheetAttributes()
+{
+ GetProperties().ForceStyleToHardAttributes();
+}
+
+bool SdrObject::HasMacro() const
+{
+ return false;
+}
+
+SdrObject* SdrObject::CheckMacroHit(const SdrObjMacroHitRec& rRec) const
+{
+ if(rRec.pPageView)
+ {
+ return SdrObjectPrimitiveHit(*this, rRec.aPos, rRec.nTol, *rRec.pPageView, rRec.pVisiLayer, false);
+ }
+
+ return nullptr;
+}
+
+PointerStyle SdrObject::GetMacroPointer(const SdrObjMacroHitRec&) const
+{
+ return PointerStyle::RefHand;
+}
+
+void SdrObject::PaintMacro(OutputDevice& rOut, const tools::Rectangle& , const SdrObjMacroHitRec& ) const
+{
+ const RasterOp eRop(rOut.GetRasterOp());
+ const basegfx::B2DPolyPolygon aPolyPolygon(TakeXorPoly());
+
+ rOut.SetLineColor(COL_BLACK);
+ rOut.SetFillColor();
+ rOut.SetRasterOp(RasterOp::Invert);
+
+ for(auto const& rPolygon : aPolyPolygon)
+ {
+ rOut.DrawPolyLine(rPolygon);
+ }
+
+ rOut.SetRasterOp(eRop);
+}
+
+bool SdrObject::DoMacro(const SdrObjMacroHitRec&)
+{
+ return false;
+}
+
+bool SdrObject::IsMacroHit(const SdrObjMacroHitRec& rRec) const
+{
+ return CheckMacroHit(rRec) != nullptr;
+}
+
+
+std::unique_ptr<SdrObjGeoData> SdrObject::NewGeoData() const
+{
+ return std::make_unique<SdrObjGeoData>();
+}
+
+void SdrObject::SaveGeoData(SdrObjGeoData& rGeo) const
+{
+ rGeo.aBoundRect =GetCurrentBoundRect();
+ rGeo.aAnchor =m_aAnchor ;
+ rGeo.bMovProt =m_bMovProt ;
+ rGeo.bSizProt =m_bSizProt ;
+ rGeo.bNoPrint =m_bNoPrint ;
+ rGeo.mbVisible =mbVisible ;
+ rGeo.bClosedObj =m_bClosedObj ;
+ rGeo.mnLayerID = mnLayerID;
+
+ // user-defined gluepoints
+ if (m_pPlusData!=nullptr && m_pPlusData->pGluePoints!=nullptr) {
+ if (rGeo.pGPL!=nullptr) {
+ *rGeo.pGPL=*m_pPlusData->pGluePoints;
+ } else {
+ rGeo.pGPL.reset( new SdrGluePointList(*m_pPlusData->pGluePoints) );
+ }
+ } else {
+ rGeo.pGPL.reset();
+ }
+}
+
+void SdrObject::RestoreGeoData(const SdrObjGeoData& rGeo)
+{
+ SetBoundAndSnapRectsDirty();
+ m_aOutRect =rGeo.aBoundRect ;
+ m_aAnchor =rGeo.aAnchor ;
+ m_bMovProt =rGeo.bMovProt ;
+ m_bSizProt =rGeo.bSizProt ;
+ m_bNoPrint =rGeo.bNoPrint ;
+ mbVisible =rGeo.mbVisible ;
+ m_bClosedObj =rGeo.bClosedObj ;
+ mnLayerID = rGeo.mnLayerID;
+
+ // user-defined gluepoints
+ if (rGeo.pGPL!=nullptr) {
+ ImpForcePlusData();
+ if (m_pPlusData->pGluePoints!=nullptr) {
+ *m_pPlusData->pGluePoints=*rGeo.pGPL;
+ } else {
+ m_pPlusData->pGluePoints.reset(new SdrGluePointList(*rGeo.pGPL));
+ }
+ } else {
+ if (m_pPlusData!=nullptr && m_pPlusData->pGluePoints!=nullptr) {
+ m_pPlusData->pGluePoints.reset();
+ }
+ }
+}
+
+std::unique_ptr<SdrObjGeoData> SdrObject::GetGeoData() const
+{
+ std::unique_ptr<SdrObjGeoData> pGeo = NewGeoData();
+ SaveGeoData(*pGeo);
+ return pGeo;
+}
+
+void SdrObject::SetGeoData(const SdrObjGeoData& rGeo)
+{
+ tools::Rectangle aBoundRect0; if (m_pUserCall!=nullptr) aBoundRect0=GetLastBoundRect();
+ RestoreGeoData(rGeo);
+ SetChanged();
+ BroadcastObjectChange();
+ SendUserCall(SdrUserCallType::Resize,aBoundRect0);
+}
+
+
+// ItemSet access
+
+const SfxItemSet& SdrObject::GetObjectItemSet() const
+{
+ return GetProperties().GetObjectItemSet();
+}
+
+const SfxItemSet& SdrObject::GetMergedItemSet() const
+{
+ return GetProperties().GetMergedItemSet();
+}
+
+void SdrObject::SetObjectItem(const SfxPoolItem& rItem)
+{
+ GetProperties().SetObjectItem(rItem);
+}
+
+void SdrObject::SetMergedItem(const SfxPoolItem& rItem)
+{
+ GetProperties().SetMergedItem(rItem);
+}
+
+void SdrObject::ClearMergedItem(const sal_uInt16 nWhich)
+{
+ GetProperties().ClearMergedItem(nWhich);
+}
+
+void SdrObject::SetObjectItemSet(const SfxItemSet& rSet)
+{
+ GetProperties().SetObjectItemSet(rSet);
+}
+
+void SdrObject::SetMergedItemSet(const SfxItemSet& rSet, bool bClearAllItems)
+{
+ GetProperties().SetMergedItemSet(rSet, bClearAllItems);
+}
+
+const SfxPoolItem& SdrObject::GetObjectItem(const sal_uInt16 nWhich) const
+{
+ return GetObjectItemSet().Get(nWhich);
+}
+
+const SfxPoolItem& SdrObject::GetMergedItem(const sal_uInt16 nWhich) const
+{
+ return GetMergedItemSet().Get(nWhich);
+}
+
+void SdrObject::SetMergedItemSetAndBroadcast(const SfxItemSet& rSet, bool bClearAllItems)
+{
+ GetProperties().SetMergedItemSetAndBroadcast(rSet, bClearAllItems);
+}
+
+void SdrObject::ApplyNotPersistAttr(const SfxItemSet& rAttr)
+{
+ tools::Rectangle aBoundRect0; if (m_pUserCall!=nullptr) aBoundRect0=GetLastBoundRect();
+ NbcApplyNotPersistAttr(rAttr);
+ SetChanged();
+ BroadcastObjectChange();
+ SendUserCall(SdrUserCallType::Resize,aBoundRect0);
+}
+
+void SdrObject::NbcApplyNotPersistAttr(const SfxItemSet& rAttr)
+{
+ const tools::Rectangle& rSnap=GetSnapRect();
+ const tools::Rectangle& rLogic=GetLogicRect();
+ Point aRef1(rSnap.Center());
+
+ if (const SdrTransformRef1XItem *pPoolItem = rAttr.GetItemIfSet(SDRATTR_TRANSFORMREF1X))
+ {
+ aRef1.setX(pPoolItem->GetValue() );
+ }
+ if (const SdrTransformRef1YItem *pPoolItem = rAttr.GetItemIfSet(SDRATTR_TRANSFORMREF1Y))
+ {
+ aRef1.setY(pPoolItem->GetValue() );
+ }
+
+ tools::Rectangle aNewSnap(rSnap);
+ if (const SdrMoveXItem *pPoolItem = rAttr.GetItemIfSet(SDRATTR_MOVEX))
+ {
+ tools::Long n = pPoolItem->GetValue();
+ aNewSnap.Move(n,0);
+ }
+ if (const SdrMoveYItem *pPoolItem = rAttr.GetItemIfSet(SDRATTR_MOVEY))
+ {
+ tools::Long n = pPoolItem->GetValue();
+ aNewSnap.Move(0,n);
+ }
+ if (const SdrOnePositionXItem *pPoolItem = rAttr.GetItemIfSet(SDRATTR_ONEPOSITIONX))
+ {
+ tools::Long n = pPoolItem->GetValue();
+ aNewSnap.Move(n-aNewSnap.Left(),0);
+ }
+ if (const SdrOnePositionYItem *pPoolItem = rAttr.GetItemIfSet(SDRATTR_ONEPOSITIONY))
+ {
+ tools::Long n = pPoolItem->GetValue();
+ aNewSnap.Move(0,n-aNewSnap.Top());
+ }
+ if (const SdrOneSizeWidthItem *pPoolItem = rAttr.GetItemIfSet(SDRATTR_ONESIZEWIDTH))
+ {
+ tools::Long n = pPoolItem->GetValue();
+ aNewSnap.SetRight(aNewSnap.Left()+n );
+ }
+ if (const SdrOneSizeHeightItem *pPoolItem = rAttr.GetItemIfSet(SDRATTR_ONESIZEHEIGHT))
+ {
+ tools::Long n = pPoolItem->GetValue();
+ aNewSnap.SetBottom(aNewSnap.Top()+n );
+ }
+ if (aNewSnap!=rSnap) {
+ if (aNewSnap.GetSize()==rSnap.GetSize()) {
+ NbcMove(Size(aNewSnap.Left()-rSnap.Left(),aNewSnap.Top()-rSnap.Top()));
+ } else {
+ NbcSetSnapRect(aNewSnap);
+ }
+ }
+
+ if (const SdrShearAngleItem *pPoolItem = rAttr.GetItemIfSet(SDRATTR_SHEARANGLE))
+ {
+ Degree100 n = pPoolItem->GetValue();
+ n-=GetShearAngle();
+ if (n) {
+ double nTan = tan(toRadians(n));
+ NbcShear(aRef1,n,nTan,false);
+ }
+ }
+ if (const SdrAngleItem *pPoolItem = rAttr.GetItemIfSet(SDRATTR_ROTATEANGLE))
+ {
+ Degree100 n = pPoolItem->GetValue();
+ n-=GetRotateAngle();
+ if (n) {
+ NbcRotate(aRef1,n);
+ }
+ }
+ if (const SdrRotateOneItem *pPoolItem = rAttr.GetItemIfSet(SDRATTR_ROTATEONE))
+ {
+ Degree100 n = pPoolItem->GetValue();
+ NbcRotate(aRef1,n);
+ }
+ if (const SdrHorzShearOneItem *pPoolItem = rAttr.GetItemIfSet(SDRATTR_HORZSHEARONE))
+ {
+ Degree100 n = pPoolItem->GetValue();
+ double nTan = tan(toRadians(n));
+ NbcShear(aRef1,n,nTan,false);
+ }
+ if (const SdrVertShearOneItem *pPoolItem = rAttr.GetItemIfSet(SDRATTR_VERTSHEARONE))
+ {
+ Degree100 n = pPoolItem->GetValue();
+ double nTan = tan(toRadians(n));
+ NbcShear(aRef1,n,nTan,true);
+ }
+
+ if (const SdrYesNoItem *pPoolItem = rAttr.GetItemIfSet(SDRATTR_OBJMOVEPROTECT))
+ {
+ bool b = pPoolItem->GetValue();
+ SetMoveProtect(b);
+ }
+ if (const SdrYesNoItem *pPoolItem = rAttr.GetItemIfSet(SDRATTR_OBJSIZEPROTECT))
+ {
+ bool b = pPoolItem->GetValue();
+ SetResizeProtect(b);
+ }
+
+ /* move protect always sets size protect */
+ if( IsMoveProtect() )
+ SetResizeProtect( true );
+
+ if (const SdrObjPrintableItem *pPoolItem = rAttr.GetItemIfSet(SDRATTR_OBJPRINTABLE))
+ {
+ bool b = pPoolItem->GetValue();
+ SetPrintable(b);
+ }
+
+ if (const SdrObjVisibleItem *pPoolItem = rAttr.GetItemIfSet(SDRATTR_OBJVISIBLE))
+ {
+ bool b = pPoolItem->GetValue();
+ SetVisible(b);
+ }
+
+ SdrLayerID nLayer=SDRLAYER_NOTFOUND;
+ if (const SdrLayerIdItem *pPoolItem = rAttr.GetItemIfSet(SDRATTR_LAYERID))
+ {
+ nLayer = pPoolItem->GetValue();
+ }
+ if (const SdrLayerNameItem *pPoolItem = rAttr.GetItemIfSet(SDRATTR_LAYERNAME))
+ {
+ OUString aLayerName = pPoolItem->GetValue();
+ const SdrLayerAdmin& rLayAd(nullptr != getSdrPageFromSdrObject()
+ ? getSdrPageFromSdrObject()->GetLayerAdmin()
+ : getSdrModelFromSdrObject().GetLayerAdmin());
+ const SdrLayer* pLayer = rLayAd.GetLayer(aLayerName);
+
+ if(nullptr != pLayer)
+ {
+ nLayer=pLayer->GetID();
+ }
+ }
+ if (nLayer!=SDRLAYER_NOTFOUND) {
+ NbcSetLayer(nLayer);
+ }
+
+ if (const SfxStringItem *pPoolItem = rAttr.GetItemIfSet(SDRATTR_OBJECTNAME))
+ {
+ OUString aName = pPoolItem->GetValue();
+ SetName(aName);
+ }
+ tools::Rectangle aNewLogic(rLogic);
+ if (const SdrLogicSizeWidthItem *pPoolItem = rAttr.GetItemIfSet(SDRATTR_LOGICSIZEWIDTH))
+ {
+ tools::Long n = pPoolItem->GetValue();
+ aNewLogic.SetRight(aNewLogic.Left()+n );
+ }
+ if (const SdrLogicSizeHeightItem *pPoolItem = rAttr.GetItemIfSet(SDRATTR_LOGICSIZEHEIGHT))
+ {
+ tools::Long n = pPoolItem->GetValue();
+ aNewLogic.SetBottom(aNewLogic.Top()+n );
+ }
+ if (aNewLogic!=rLogic) {
+ NbcSetLogicRect(aNewLogic);
+ }
+ Fraction aResizeX(1,1);
+ Fraction aResizeY(1,1);
+ if (const SdrResizeXOneItem *pPoolItem = rAttr.GetItemIfSet(SDRATTR_RESIZEXONE))
+ {
+ aResizeX *= pPoolItem->GetValue();
+ }
+ if (const SdrResizeYOneItem *pPoolItem = rAttr.GetItemIfSet(SDRATTR_RESIZEYONE))
+ {
+ aResizeY *= pPoolItem->GetValue();
+ }
+ if (aResizeX!=Fraction(1,1) || aResizeY!=Fraction(1,1)) {
+ NbcResize(aRef1,aResizeX,aResizeY);
+ }
+}
+
+void SdrObject::TakeNotPersistAttr(SfxItemSet& rAttr) const
+{
+ const tools::Rectangle& rSnap=GetSnapRect();
+ const tools::Rectangle& rLogic=GetLogicRect();
+ rAttr.Put(SdrYesNoItem(SDRATTR_OBJMOVEPROTECT, IsMoveProtect()));
+ rAttr.Put(SdrYesNoItem(SDRATTR_OBJSIZEPROTECT, IsResizeProtect()));
+ rAttr.Put(SdrObjPrintableItem(IsPrintable()));
+ rAttr.Put(SdrObjVisibleItem(IsVisible()));
+ rAttr.Put(SdrAngleItem(SDRATTR_ROTATEANGLE, GetRotateAngle()));
+ rAttr.Put(SdrShearAngleItem(GetShearAngle()));
+ rAttr.Put(SdrOneSizeWidthItem(rSnap.GetWidth()-1));
+ rAttr.Put(SdrOneSizeHeightItem(rSnap.GetHeight()-1));
+ rAttr.Put(SdrOnePositionXItem(rSnap.Left()));
+ rAttr.Put(SdrOnePositionYItem(rSnap.Top()));
+ if (rLogic.GetWidth()!=rSnap.GetWidth()) {
+ rAttr.Put(SdrLogicSizeWidthItem(rLogic.GetWidth()-1));
+ }
+ if (rLogic.GetHeight()!=rSnap.GetHeight()) {
+ rAttr.Put(SdrLogicSizeHeightItem(rLogic.GetHeight()-1));
+ }
+ OUString aName(GetName());
+
+ if (!aName.isEmpty())
+ {
+ rAttr.Put(SfxStringItem(SDRATTR_OBJECTNAME, aName));
+ }
+
+ rAttr.Put(SdrLayerIdItem(GetLayer()));
+ const SdrLayerAdmin& rLayAd(nullptr != getSdrPageFromSdrObject()
+ ? getSdrPageFromSdrObject()->GetLayerAdmin()
+ : getSdrModelFromSdrObject().GetLayerAdmin());
+ const SdrLayer* pLayer = rLayAd.GetLayerPerID(GetLayer());
+ if(nullptr != pLayer)
+ {
+ rAttr.Put(SdrLayerNameItem(pLayer->GetName()));
+ }
+ Point aRef1(rSnap.Center());
+ Point aRef2(aRef1); aRef2.AdjustY( 1 );
+ rAttr.Put(SdrTransformRef1XItem(aRef1.X()));
+ rAttr.Put(SdrTransformRef1YItem(aRef1.Y()));
+ rAttr.Put(SdrTransformRef2XItem(aRef2.X()));
+ rAttr.Put(SdrTransformRef2YItem(aRef2.Y()));
+}
+
+SfxStyleSheet* SdrObject::GetStyleSheet() const
+{
+ return GetProperties().GetStyleSheet();
+}
+
+void SdrObject::SetStyleSheet(SfxStyleSheet* pNewStyleSheet, bool bDontRemoveHardAttr)
+{
+ tools::Rectangle aBoundRect0;
+
+ if(m_pUserCall)
+ aBoundRect0 = GetLastBoundRect();
+
+ InternalSetStyleSheet(pNewStyleSheet, bDontRemoveHardAttr, true);
+ SetChanged();
+ BroadcastObjectChange();
+ SendUserCall(SdrUserCallType::ChangeAttr, aBoundRect0);
+}
+
+void SdrObject::NbcSetStyleSheet(SfxStyleSheet* pNewStyleSheet, bool bDontRemoveHardAttr)
+{
+ InternalSetStyleSheet(pNewStyleSheet, bDontRemoveHardAttr, false);
+}
+
+void SdrObject::InternalSetStyleSheet(SfxStyleSheet* pNewStyleSheet, bool bDontRemoveHardAttr, bool bBroadcast)
+{
+ GetProperties().SetStyleSheet(pNewStyleSheet, bDontRemoveHardAttr, bBroadcast);
+}
+
+// Broadcasting while setting attributes is managed by the AttrObj.
+
+
+SdrGluePoint SdrObject::GetVertexGluePoint(sal_uInt16 nPosNum) const
+{
+ // #i41936# Use SnapRect for default GluePoints
+ const tools::Rectangle aR(GetSnapRect());
+ Point aPt;
+
+ switch(nPosNum)
+ {
+ case 0 : aPt = aR.TopCenter(); break;
+ case 1 : aPt = aR.RightCenter(); break;
+ case 2 : aPt = aR.BottomCenter(); break;
+ case 3 : aPt = aR.LeftCenter(); break;
+ }
+
+ aPt -= aR.Center();
+ SdrGluePoint aGP(aPt);
+ aGP.SetPercent(false);
+
+ return aGP;
+}
+
+SdrGluePoint SdrObject::GetCornerGluePoint(sal_uInt16 nPosNum) const
+{
+ tools::Rectangle aR(GetCurrentBoundRect());
+ Point aPt;
+ switch (nPosNum) {
+ case 0 : aPt=aR.TopLeft(); break;
+ case 1 : aPt=aR.TopRight(); break;
+ case 2 : aPt=aR.BottomRight(); break;
+ case 3 : aPt=aR.BottomLeft(); break;
+ }
+ aPt-=GetSnapRect().Center();
+ SdrGluePoint aGP(aPt);
+ aGP.SetPercent(false);
+ return aGP;
+}
+
+const SdrGluePointList* SdrObject::GetGluePointList() const
+{
+ if (m_pPlusData!=nullptr) return m_pPlusData->pGluePoints.get();
+ return nullptr;
+}
+
+
+SdrGluePointList* SdrObject::ForceGluePointList()
+{
+ ImpForcePlusData();
+ if (m_pPlusData->pGluePoints==nullptr) {
+ m_pPlusData->pGluePoints.reset(new SdrGluePointList);
+ }
+ return m_pPlusData->pGluePoints.get();
+}
+
+void SdrObject::SetGlueReallyAbsolute(bool bOn)
+{
+ // First a const call to see whether there are any gluepoints.
+ // Force const call!
+ if (GetGluePointList()!=nullptr) {
+ SdrGluePointList* pGPL=ForceGluePointList();
+ pGPL->SetReallyAbsolute(bOn,*this);
+ }
+}
+
+void SdrObject::NbcRotateGluePoints(const Point& rRef, Degree100 nAngle, double sn, double cs)
+{
+ // First a const call to see whether there are any gluepoints.
+ // Force const call!
+ if (GetGluePointList()!=nullptr) {
+ SdrGluePointList* pGPL=ForceGluePointList();
+ pGPL->Rotate(rRef,nAngle,sn,cs,this);
+ }
+}
+
+void SdrObject::NbcMirrorGluePoints(const Point& rRef1, const Point& rRef2)
+{
+ // First a const call to see whether there are any gluepoints.
+ // Force const call!
+ if (GetGluePointList()!=nullptr) {
+ SdrGluePointList* pGPL=ForceGluePointList();
+ pGPL->Mirror(rRef1,rRef2,this);
+ }
+}
+
+void SdrObject::NbcShearGluePoints(const Point& rRef, double tn, bool bVShear)
+{
+ // First a const call to see whether there are any gluepoints.
+ // Force const call!
+ if (GetGluePointList()!=nullptr) {
+ SdrGluePointList* pGPL=ForceGluePointList();
+ pGPL->Shear(rRef,tn,bVShear,this);
+ }
+}
+
+void SdrObject::ConnectToNode(bool /*bTail1*/, SdrObject* /*pObj*/)
+{
+}
+
+void SdrObject::DisconnectFromNode(bool /*bTail1*/)
+{
+}
+
+SdrObject* SdrObject::GetConnectedNode(bool /*bTail1*/) const
+{
+ return nullptr;
+}
+
+
+static void extractLineContourFromPrimitive2DSequence(
+ const drawinglayer::primitive2d::Primitive2DContainer& rxSequence,
+ basegfx::B2DPolygonVector& rExtractedHairlines,
+ basegfx::B2DPolyPolygonVector& rExtractedLineFills)
+{
+ rExtractedHairlines.clear();
+ rExtractedLineFills.clear();
+
+ if(rxSequence.empty())
+ return;
+
+ // use neutral ViewInformation
+ const drawinglayer::geometry::ViewInformation2D aViewInformation2D;
+
+ // create extractor, process and get result
+ drawinglayer::processor2d::LineGeometryExtractor2D aExtractor(aViewInformation2D);
+ aExtractor.process(rxSequence);
+
+ // copy line results
+ rExtractedHairlines = aExtractor.getExtractedHairlines();
+
+ // copy fill rsults
+ rExtractedLineFills = aExtractor.getExtractedLineFills();
+}
+
+
+SdrObject* SdrObject::ImpConvertToContourObj(bool bForceLineDash)
+{
+ SdrObject* pRetval(nullptr);
+
+ if(LineGeometryUsageIsNecessary())
+ {
+ basegfx::B2DPolyPolygon aMergedLineFillPolyPolygon;
+ basegfx::B2DPolyPolygon aMergedHairlinePolyPolygon;
+ drawinglayer::primitive2d::Primitive2DContainer xSequence;
+ GetViewContact().getViewIndependentPrimitive2DContainer(xSequence);
+
+ if(!xSequence.empty())
+ {
+ basegfx::B2DPolygonVector aExtractedHairlines;
+ basegfx::B2DPolyPolygonVector aExtractedLineFills;
+
+ extractLineContourFromPrimitive2DSequence(xSequence, aExtractedHairlines, aExtractedLineFills);
+
+ // for SdrObject creation, just copy all to a single Hairline-PolyPolygon
+ for(const basegfx::B2DPolygon & rExtractedHairline : aExtractedHairlines)
+ {
+ aMergedHairlinePolyPolygon.append(rExtractedHairline);
+ }
+
+ // check for fill rsults
+ if (!aExtractedLineFills.empty() && !utl::ConfigManager::IsFuzzing())
+ {
+ // merge to a single tools::PolyPolygon (OR)
+ aMergedLineFillPolyPolygon = basegfx::utils::mergeToSinglePolyPolygon(std::move(aExtractedLineFills));
+ }
+ }
+
+ if(aMergedLineFillPolyPolygon.count() || (bForceLineDash && aMergedHairlinePolyPolygon.count()))
+ {
+ SfxItemSet aSet(GetMergedItemSet());
+ drawing::FillStyle eOldFillStyle = aSet.Get(XATTR_FILLSTYLE).GetValue();
+ SdrPathObj* aLinePolygonPart = nullptr;
+ SdrPathObj* aLineHairlinePart = nullptr;
+ bool bBuildGroup(false);
+
+ if(aMergedLineFillPolyPolygon.count())
+ {
+ // create SdrObject for filled line geometry
+ aLinePolygonPart = new SdrPathObj(
+ getSdrModelFromSdrObject(),
+ SdrObjKind::PathFill,
+ aMergedLineFillPolyPolygon);
+
+ // correct item properties
+ aSet.Put(XLineWidthItem(0));
+ aSet.Put(XLineStyleItem(drawing::LineStyle_NONE));
+ Color aColorLine = aSet.Get(XATTR_LINECOLOR).GetColorValue();
+ sal_uInt16 nTransLine = aSet.Get(XATTR_LINETRANSPARENCE).GetValue();
+ aSet.Put(XFillColorItem(OUString(), aColorLine));
+ aSet.Put(XFillStyleItem(drawing::FillStyle_SOLID));
+ aSet.Put(XFillTransparenceItem(nTransLine));
+
+ aLinePolygonPart->SetMergedItemSet(aSet);
+ }
+
+ if(aMergedHairlinePolyPolygon.count())
+ {
+ // create SdrObject for hairline geometry
+ // OBJ_PATHLINE is necessary here, not OBJ_PATHFILL. This is intended
+ // to get a non-filled object. If the poly is closed, the PathObj takes care for
+ // the correct closed state.
+ aLineHairlinePart = new SdrPathObj(
+ getSdrModelFromSdrObject(),
+ SdrObjKind::PathLine,
+ aMergedHairlinePolyPolygon);
+
+ aSet.Put(XLineWidthItem(0));
+ aSet.Put(XFillStyleItem(drawing::FillStyle_NONE));
+ aSet.Put(XLineStyleItem(drawing::LineStyle_SOLID));
+
+ // it is also necessary to switch off line start and ends here
+ aSet.Put(XLineStartWidthItem(0));
+ aSet.Put(XLineEndWidthItem(0));
+
+ aLineHairlinePart->SetMergedItemSet(aSet);
+
+ if(aLinePolygonPart)
+ {
+ bBuildGroup = true;
+ }
+ }
+
+ // check if original geometry should be added (e.g. filled and closed)
+ bool bAddOriginalGeometry(false);
+ SdrPathObj* pPath = dynamic_cast<SdrPathObj*>(this);
+
+ if(pPath && pPath->IsClosed())
+ {
+ if(eOldFillStyle != drawing::FillStyle_NONE)
+ {
+ bAddOriginalGeometry = true;
+ }
+ }
+
+ // do we need a group?
+ if(bBuildGroup || bAddOriginalGeometry)
+ {
+ SdrObject* pGroup = new SdrObjGroup(getSdrModelFromSdrObject());
+
+ if(bAddOriginalGeometry)
+ {
+ // Add a clone of the original geometry.
+ aSet.ClearItem();
+ aSet.Put(GetMergedItemSet());
+ aSet.Put(XLineStyleItem(drawing::LineStyle_NONE));
+ aSet.Put(XLineWidthItem(0));
+
+ SdrObject* pClone(CloneSdrObject(getSdrModelFromSdrObject()));
+ pClone->SetMergedItemSet(aSet);
+
+ pGroup->GetSubList()->NbcInsertObject(pClone);
+ }
+
+ if(aLinePolygonPart)
+ {
+ pGroup->GetSubList()->NbcInsertObject(aLinePolygonPart);
+ }
+
+ if(aLineHairlinePart)
+ {
+ pGroup->GetSubList()->NbcInsertObject(aLineHairlinePart);
+ }
+
+ pRetval = pGroup;
+ }
+ else
+ {
+ if(aLinePolygonPart)
+ {
+ pRetval = aLinePolygonPart;
+ }
+ else if(aLineHairlinePart)
+ {
+ pRetval = aLineHairlinePart;
+ }
+ }
+ }
+ }
+
+ if(nullptr == pRetval)
+ {
+ // due to current method usage, create and return a clone when nothing has changed
+ SdrObject* pClone(CloneSdrObject(getSdrModelFromSdrObject()));
+ pRetval = pClone;
+ }
+
+ return pRetval;
+}
+
+
+void SdrObject::SetMarkProtect(bool bProt)
+{
+ m_bMarkProt = bProt;
+}
+
+
+void SdrObject::SetEmptyPresObj(bool bEpt)
+{
+ m_bEmptyPresObj = bEpt;
+}
+
+
+void SdrObject::SetNotVisibleAsMaster(bool bFlg)
+{
+ m_bNotVisibleAsMaster=bFlg;
+}
+
+
+// convert this path object to contour object, even when it is a group
+SdrObject* SdrObject::ConvertToContourObj(SdrObject* pRet, bool bForceLineDash) const
+{
+ if(dynamic_cast<const SdrObjGroup*>( pRet) != nullptr)
+ {
+ SdrObjList* pObjList2 = pRet->GetSubList();
+ SdrObject* pGroup = new SdrObjGroup(getSdrModelFromSdrObject());
+
+ for(size_t a=0; a<pObjList2->GetObjCount(); ++a)
+ {
+ SdrObject* pIterObj = pObjList2->GetObj(a);
+ pGroup->GetSubList()->NbcInsertObject(ConvertToContourObj(pIterObj, bForceLineDash));
+ }
+
+ pRet = pGroup;
+ }
+ else
+ {
+ if (SdrPathObj *pPathObj = dynamic_cast<SdrPathObj*>(pRet))
+ {
+ // bezier geometry got created, even for straight edges since the given
+ // object is a result of DoConvertToPolyObj. For conversion to contour
+ // this is not really needed and can be reduced again AFAP
+ pPathObj->SetPathPoly(basegfx::utils::simplifyCurveSegments(pPathObj->GetPathPoly()));
+ }
+
+ pRet = pRet->ImpConvertToContourObj(bForceLineDash);
+ }
+
+ // #i73441# preserve LayerID
+ if(pRet && pRet->GetLayer() != GetLayer())
+ {
+ pRet->SetLayer(GetLayer());
+ }
+
+ return pRet;
+}
+
+
+SdrObjectUniquePtr SdrObject::ConvertToPolyObj(bool bBezier, bool bLineToArea) const
+{
+ SdrObjectUniquePtr pRet = DoConvertToPolyObj(bBezier, true);
+
+ if(pRet && bLineToArea)
+ {
+ SdrObject* pNewRet = ConvertToContourObj(pRet.get());
+ pRet.reset(pNewRet);
+ }
+
+ // #i73441# preserve LayerID
+ if(pRet && pRet->GetLayer() != GetLayer())
+ {
+ pRet->SetLayer(GetLayer());
+ }
+
+ return pRet;
+}
+
+
+SdrObjectUniquePtr SdrObject::DoConvertToPolyObj(bool /*bBezier*/, bool /*bAddText*/) const
+{
+ return nullptr;
+}
+
+
+void SdrObject::InsertedStateChange()
+{
+ const bool bIsInserted(nullptr != getParentSdrObjListFromSdrObject());
+ const tools::Rectangle aBoundRect0(GetLastBoundRect());
+
+ if(bIsInserted)
+ {
+ SendUserCall(SdrUserCallType::Inserted, aBoundRect0);
+ }
+ else
+ {
+ SendUserCall(SdrUserCallType::Removed, aBoundRect0);
+ }
+
+ if(nullptr != m_pPlusData && nullptr != m_pPlusData->pBroadcast)
+ {
+ SdrHint aHint(bIsInserted ? SdrHintKind::ObjectInserted : SdrHintKind::ObjectRemoved, *this);
+ m_pPlusData->pBroadcast->Broadcast(aHint);
+ }
+}
+
+void SdrObject::SetMoveProtect(bool bProt)
+{
+ if(IsMoveProtect() != bProt)
+ {
+ // #i77187# secured and simplified
+ m_bMovProt = bProt;
+ SetChanged();
+ BroadcastObjectChange();
+ }
+}
+
+void SdrObject::SetResizeProtect(bool bProt)
+{
+ if(IsResizeProtect() != bProt)
+ {
+ // #i77187# secured and simplified
+ m_bSizProt = bProt;
+ SetChanged();
+ BroadcastObjectChange();
+ }
+}
+
+void SdrObject::SetPrintable(bool bPrn)
+{
+ if( bPrn == m_bNoPrint )
+ {
+ m_bNoPrint=!bPrn;
+ SetChanged();
+ if (IsInserted())
+ {
+ SdrHint aHint(SdrHintKind::ObjectChange, *this);
+ getSdrModelFromSdrObject().Broadcast(aHint);
+ }
+ }
+}
+
+void SdrObject::SetVisible(bool bVisible)
+{
+ if( bVisible != mbVisible )
+ {
+ mbVisible = bVisible;
+ SetChanged();
+ if (IsInserted())
+ {
+ SdrHint aHint(SdrHintKind::ObjectChange, *this);
+ getSdrModelFromSdrObject().Broadcast(aHint);
+ }
+ }
+}
+
+
+sal_uInt16 SdrObject::GetUserDataCount() const
+{
+ if (m_pPlusData==nullptr || m_pPlusData->pUserDataList==nullptr) return 0;
+ return m_pPlusData->pUserDataList->GetUserDataCount();
+}
+
+SdrObjUserData* SdrObject::GetUserData(sal_uInt16 nNum) const
+{
+ if (m_pPlusData==nullptr || m_pPlusData->pUserDataList==nullptr) return nullptr;
+ return &m_pPlusData->pUserDataList->GetUserData(nNum);
+}
+
+void SdrObject::AppendUserData(std::unique_ptr<SdrObjUserData> pData)
+{
+ if (!pData)
+ {
+ OSL_FAIL("SdrObject::AppendUserData(): pData is NULL pointer.");
+ return;
+ }
+
+ ImpForcePlusData();
+ if (!m_pPlusData->pUserDataList)
+ m_pPlusData->pUserDataList.reset( new SdrObjUserDataList );
+
+ m_pPlusData->pUserDataList->AppendUserData(std::move(pData));
+}
+
+void SdrObject::DeleteUserData(sal_uInt16 nNum)
+{
+ sal_uInt16 nCount=GetUserDataCount();
+ if (nNum<nCount) {
+ m_pPlusData->pUserDataList->DeleteUserData(nNum);
+ if (nCount==1) {
+ m_pPlusData->pUserDataList.reset();
+ }
+ } else {
+ OSL_FAIL("SdrObject::DeleteUserData(): Invalid Index.");
+ }
+}
+
+void SdrObject::SetUserCall(SdrObjUserCall* pUser)
+{
+ m_pUserCall = pUser;
+}
+
+
+void SdrObject::SendUserCall(SdrUserCallType eUserCall, const tools::Rectangle& rBoundRect) const
+{
+ SdrObject* pGroup(getParentSdrObjectFromSdrObject());
+
+ if ( m_pUserCall )
+ {
+ m_pUserCall->Changed( *this, eUserCall, rBoundRect );
+ }
+
+ if(nullptr != pGroup && pGroup->GetUserCall())
+ {
+ // broadcast to group
+ SdrUserCallType eChildUserType = SdrUserCallType::ChildChangeAttr;
+
+ switch( eUserCall )
+ {
+ case SdrUserCallType::MoveOnly:
+ eChildUserType = SdrUserCallType::ChildMoveOnly;
+ break;
+
+ case SdrUserCallType::Resize:
+ eChildUserType = SdrUserCallType::ChildResize;
+ break;
+
+ case SdrUserCallType::ChangeAttr:
+ eChildUserType = SdrUserCallType::ChildChangeAttr;
+ break;
+
+ case SdrUserCallType::Delete:
+ eChildUserType = SdrUserCallType::ChildDelete;
+ break;
+
+ case SdrUserCallType::Inserted:
+ eChildUserType = SdrUserCallType::ChildInserted;
+ break;
+
+ case SdrUserCallType::Removed:
+ eChildUserType = SdrUserCallType::ChildRemoved;
+ break;
+
+ default: break;
+ }
+
+ pGroup->GetUserCall()->Changed( *this, eChildUserType, rBoundRect );
+ }
+
+ // notify our UNO shape listeners
+ switch ( eUserCall )
+ {
+ case SdrUserCallType::Resize:
+ notifyShapePropertyChange( svx::ShapePropertyProviderId::Size );
+ [[fallthrough]]; // RESIZE might also imply a change of the position
+ case SdrUserCallType::MoveOnly:
+ notifyShapePropertyChange( svx::ShapePropertyProviderId::Position );
+ break;
+ default:
+ // not interested in
+ break;
+ }
+}
+
+void SdrObject::setUnoShape( const uno::Reference< drawing::XShape >& _rxUnoShape )
+{
+ const uno::Reference< uno::XInterface>& xOldUnoShape( maWeakUnoShape );
+ // the UNO shape would be gutted by the following code; return early
+ if ( _rxUnoShape == xOldUnoShape )
+ {
+ if ( !xOldUnoShape.is() )
+ {
+ // make sure there is no stale impl. pointer if the UNO
+ // shape was destroyed meanwhile (remember we only hold weak
+ // reference to it!)
+ mpSvxShape = nullptr;
+ }
+ return;
+ }
+
+ bool bTransferOwnership( false );
+ if ( xOldUnoShape.is() )
+ {
+ bTransferOwnership = mpSvxShape->HasSdrObjectOwnership();
+ // Remove yourself from the current UNO shape. Its destructor
+ // will reset our UNO shape otherwise.
+ mpSvxShape->InvalidateSdrObject();
+ }
+
+ maWeakUnoShape = _rxUnoShape;
+ mpSvxShape = comphelper::getFromUnoTunnel<SvxShape>( _rxUnoShape );
+
+ // I think this may never happen... But I am not sure enough .-)
+ if ( bTransferOwnership )
+ {
+ if (mpSvxShape)
+ mpSvxShape->TakeSdrObjectOwnership();
+ SAL_WARN( "svx.uno", "a UNO shape took over an SdrObject previously owned by another UNO shape!");
+ }
+}
+
+/** only for internal use! */
+SvxShape* SdrObject::getSvxShape()
+{
+ DBG_TESTSOLARMUTEX();
+ // retrieving the impl pointer and subsequently using it is not thread-safe, of course, so it needs to be
+ // guarded by the SolarMutex
+
+ uno::Reference< uno::XInterface > xShape( maWeakUnoShape );
+#if OSL_DEBUG_LEVEL > 0
+ OSL_ENSURE( !( !xShape.is() && mpSvxShape ),
+ "SdrObject::getSvxShape: still having IMPL-Pointer to dead object!" );
+#endif
+ //#113608#, make sure mpSvxShape is always synchronized with maWeakUnoShape
+ if ( mpSvxShape && !xShape.is() )
+ mpSvxShape = nullptr;
+
+ return mpSvxShape;
+}
+
+css::uno::Reference< css::drawing::XShape > SdrObject::getUnoShape()
+{
+ // try weak reference first
+ uno::Reference< css::drawing::XShape > xShape( getWeakUnoShape() );
+ if( xShape )
+ return xShape;
+
+ OSL_ENSURE( mpSvxShape == nullptr, "SdrObject::getUnoShape: XShape already dead, but still an IMPL pointer!" );
+
+ // try to access SdrPage from this SdrObject. This will only exist if the SdrObject is
+ // inserted in a SdrObjList (page/group/3dScene)
+ SdrPage* pPageCandidate(getSdrPageFromSdrObject());
+
+ // tdf#12152, tdf#120728
+ //
+ // With the paradigm change to only get a SdrPage for a SdrObject when the SdrObject
+ // is *inserted*, the functionality for creating 1:1 associated UNO API implementation
+ // SvxShapes was partially broken: The used ::CreateShape relies on the SvxPage being
+ // derived and the CreateShape method overloaded, implementing additional SdrInventor
+ // types as needed.
+ //
+ // The fallback to use SvxDrawPage::CreateShapeByTypeAndInventor is a trap: It's only
+ // a static fallback that handles the SdrInventor types SdrInventor::E3d and
+ // SdrInventor::Default. Due to that, e.g. the ReportDesigner broke in various conditions.
+ //
+ // That again has to do with the ReportDesigner being implemented using the UNO API
+ // aspects of SdrObjects early during their construction, not just after these are
+ // inserted to a SdrPage - but that is not illegal or wrong, the SdrObject exists already.
+ //
+ // As a current solution, use the (now always available) SdrModel and any of the
+ // existing SdrPages. The only important thing is to get a SdrPage where ::CreateShape is
+ // overloaded and implemented as needed.
+ //
+ // Note for the future:
+ // In a more ideal world there would be only one factory method for creating SdrObjects (not
+ // ::CreateShape and ::CreateShapeByTypeAndInventor). This also would not be placed at
+ // SdrPage/SvxPage at all, but at the Model where it belongs - where else would you expect
+ // objects for the current Model to be constructed? To have this at the Page only would make
+ // sense if different shapes would need to be constructed for different Pages in the same Model
+ // - this is never the case.
+ // At that Model extended functionality for that factory (or overloads and implementations)
+ // should be placed. But to be realistic, migrating the factories to Model now is too much
+ // work - maybe over time when melting SdrObject/SvxObject one day...
+ //
+ // More Note (added by noel grandin)
+ // Except that sd/ is being naughty and doing all kinds of magic during CreateShape that
+ // requires knowing which page the object is being created for. Fixing that would require
+ // moving a bunch of nasty logic from object creation time, to the point in time when
+ // it is actually added to a page.
+ if(nullptr == pPageCandidate)
+ {
+ // If not inserted, alternatively access a SdrPage using the SdrModel. There is
+ // no reason not to create and return a UNO API XShape when the SdrObject is not
+ // inserted - it may be in construction. Main paradigm is that it exists.
+ if(0 != getSdrModelFromSdrObject().GetPageCount())
+ {
+ // Take 1st SdrPage. That may be e.g. a special page (in SD), but the
+ // to-be-used method ::CreateShape will be correctly overloaded in
+ // all cases
+ pPageCandidate = getSdrModelFromSdrObject().GetPage(0);
+ }
+ }
+
+ if(nullptr != pPageCandidate)
+ {
+ uno::Reference< uno::XInterface > xPage(pPageCandidate->getUnoPage());
+ if( xPage.is() )
+ {
+ SvxDrawPage* pDrawPage = comphelper::getFromUnoTunnel<SvxDrawPage>(xPage);
+ if( pDrawPage )
+ {
+ // create one
+ xShape = pDrawPage->CreateShape( this );
+ setUnoShape( xShape );
+ }
+ }
+ }
+ else
+ {
+ // Fallback to static base functionality. CAUTION: This will only support
+ // the most basic stuff like SdrInventor::E3d and SdrInventor::Default. All
+ // the other SdrInventor enum entries are from overloads and are *not accessible*
+ // using this fallback (!) - what a bad trap
+ rtl::Reference<SvxShape> xNewShape = SvxDrawPage::CreateShapeByTypeAndInventor( GetObjIdentifier(), GetObjInventor(), this );
+ mpSvxShape = xNewShape.get();
+ maWeakUnoShape = xShape = mpSvxShape;
+ }
+
+ return xShape;
+}
+
+svx::PropertyChangeNotifier& SdrObject::getShapePropertyChangeNotifier()
+{
+ DBG_TESTSOLARMUTEX();
+
+ SvxShape* pSvxShape = getSvxShape();
+ ENSURE_OR_THROW( pSvxShape, "no SvxShape, yet!" );
+ return pSvxShape->getShapePropertyChangeNotifier();
+}
+
+void SdrObject::notifyShapePropertyChange( const svx::ShapePropertyProviderId _eProperty ) const
+{
+ DBG_TESTSOLARMUTEX();
+
+ SvxShape* pSvxShape = const_cast< SdrObject* >( this )->getSvxShape();
+ if ( pSvxShape )
+ return pSvxShape->getShapePropertyChangeNotifier().notifyPropertyChange( _eProperty );
+}
+
+
+// transformation interface for StarOfficeAPI. This implements support for
+// homogeneous 3x3 matrices containing the transformation of the SdrObject. At the
+// moment it contains a shearX, rotation and translation, but for setting all linear
+// transforms like Scale, ShearX, ShearY, Rotate and Translate are supported.
+
+
+// gets base transformation and rectangle of object. If it's an SdrPathObj it fills the PolyPolygon
+// with the base geometry and returns TRUE. Otherwise it returns FALSE.
+bool SdrObject::TRGetBaseGeometry(basegfx::B2DHomMatrix& rMatrix, basegfx::B2DPolyPolygon& /*rPolyPolygon*/) const
+{
+ // any kind of SdrObject, just use SnapRect
+ tools::Rectangle aRectangle(GetSnapRect());
+
+ // convert to transformation values
+ basegfx::B2DTuple aScale(aRectangle.GetWidth(), aRectangle.GetHeight());
+ basegfx::B2DTuple aTranslate(aRectangle.Left(), aRectangle.Top());
+
+ // position maybe relative to anchorpos, convert
+ if(getSdrModelFromSdrObject().IsWriter())
+ {
+ if(GetAnchorPos().X() || GetAnchorPos().Y())
+ {
+ aTranslate -= basegfx::B2DTuple(GetAnchorPos().X(), GetAnchorPos().Y());
+ }
+ }
+
+ // build matrix
+ rMatrix = basegfx::utils::createScaleTranslateB2DHomMatrix(aScale, aTranslate);
+
+ return false;
+}
+
+// sets the base geometry of the object using infos contained in the homogeneous 3x3 matrix.
+// If it's an SdrPathObj it will use the provided geometry information. The Polygon has
+// to use (0,0) as upper left and will be scaled to the given size in the matrix.
+void SdrObject::TRSetBaseGeometry(const basegfx::B2DHomMatrix& rMatrix, const basegfx::B2DPolyPolygon& /*rPolyPolygon*/)
+{
+ // break up matrix
+ basegfx::B2DTuple aScale;
+ basegfx::B2DTuple aTranslate;
+ double fRotate, fShearX;
+ rMatrix.decompose(aScale, aTranslate, fRotate, fShearX);
+
+ // #i75086# Old DrawingLayer (GeoStat and geometry) does not support holding negative scalings
+ // in X and Y which equal a 180 degree rotation. Recognize it and react accordingly
+ if(basegfx::fTools::less(aScale.getX(), 0.0) && basegfx::fTools::less(aScale.getY(), 0.0))
+ {
+ aScale.setX(fabs(aScale.getX()));
+ aScale.setY(fabs(aScale.getY()));
+ }
+
+ // if anchor is used, make position relative to it
+ if(getSdrModelFromSdrObject().IsWriter())
+ {
+ if(GetAnchorPos().X() || GetAnchorPos().Y())
+ {
+ aTranslate += basegfx::B2DTuple(GetAnchorPos().X(), GetAnchorPos().Y());
+ }
+ }
+
+ // build BaseRect
+ Point aPoint(FRound(aTranslate.getX()), FRound(aTranslate.getY()));
+ tools::Rectangle aBaseRect(aPoint, Size(FRound(aScale.getX()), FRound(aScale.getY())));
+
+ // set BaseRect
+ SetSnapRect(aBaseRect);
+}
+
+// Give info if object is in destruction
+bool SdrObject::IsInDestruction() const
+{
+ return getSdrModelFromSdrObject().IsInDestruction();
+}
+
+// return if fill is != drawing::FillStyle_NONE
+bool SdrObject::HasFillStyle() const
+{
+ return GetObjectItem(XATTR_FILLSTYLE).GetValue() != drawing::FillStyle_NONE;
+}
+
+bool SdrObject::HasLineStyle() const
+{
+ return GetObjectItem(XATTR_LINESTYLE).GetValue() != drawing::LineStyle_NONE;
+}
+
+
+// #i52224#
+// on import of OLE object from MS documents the BLIP size might be retrieved,
+// the following four methods are used to control it;
+// usually this data makes no sense after the import is finished, since the object
+// might be resized
+
+
+void SdrObject::SetBLIPSizeRectangle( const tools::Rectangle& aRect )
+{
+ maBLIPSizeRectangle = aRect;
+}
+
+void SdrObject::SetContextWritingMode( const sal_Int16 /*_nContextWritingMode*/ )
+{
+ // this base class does not support different writing modes, so ignore the call
+}
+
+void SdrObject::SetDoNotInsertIntoPageAutomatically(const bool bSet)
+{
+ mbDoNotInsertIntoPageAutomatically = bSet;
+}
+
+
+// #i121917#
+bool SdrObject::HasText() const
+{
+ return false;
+}
+
+bool SdrObject::IsTextBox() const
+{
+ return false;
+}
+
+void SdrObject::MakeNameUnique()
+{
+ if (GetName().isEmpty())
+ {
+ if (const E3dScene* pE3dObj = dynamic_cast<const E3dScene*>(this))
+ {
+ SdrObjList* pObjList = pE3dObj->GetSubList();
+ if (pObjList)
+ {
+ SdrObject* pObj0 = pObjList->GetObj(0);
+ if (pObj0)
+ SetName(pObj0->TakeObjNameSingul());
+ }
+ }
+ else
+ SetName(TakeObjNameSingul());
+ }
+
+ std::unordered_set<OUString> aNameSet;
+ MakeNameUnique(aNameSet);
+}
+
+void SdrObject::MakeNameUnique(std::unordered_set<OUString>& rNameSet)
+{
+ if (GetName().isEmpty())
+ return;
+
+ if (rNameSet.empty())
+ {
+ SdrPage* pPage;
+ SdrObject* pObj;
+ for (sal_uInt16 nPage(0); nPage < mrSdrModelFromSdrObject.GetPageCount(); ++nPage)
+ {
+ pPage = mrSdrModelFromSdrObject.GetPage(nPage);
+ SdrObjListIter aIter(pPage, SdrIterMode::DeepWithGroups);
+ while (aIter.IsMore())
+ {
+ pObj = aIter.Next();
+ if (pObj != this)
+ rNameSet.insert(pObj->GetName());
+ }
+ }
+ }
+
+ OUString sName(GetName().trim());
+ OUString sRootName(sName);
+
+ if (!sName.isEmpty() && rtl::isAsciiDigit(sName[sName.getLength() - 1]))
+ {
+ sal_Int32 nPos(sName.getLength() - 1);
+ while (nPos > 0 && rtl::isAsciiDigit(sName[--nPos]));
+ sRootName = o3tl::trim(sName.subView(0, nPos + 1));
+ }
+ else
+ sName += " 1";
+
+ for (sal_uInt32 n = 1; rNameSet.find(sName) != rNameSet.end(); n++)
+ sName = sRootName + " " + OUString::number(n);
+ rNameSet.insert(sName);
+
+ SetName(sName);
+}
+
+void SdrObject::ForceMetricToItemPoolMetric(basegfx::B2DPolyPolygon& rPolyPolygon) const noexcept
+{
+ MapUnit eMapUnit(getSdrModelFromSdrObject().GetItemPool().GetMetric(0));
+ if(eMapUnit == MapUnit::Map100thMM)
+ return;
+
+ if (const auto eTo = MapToO3tlLength(eMapUnit); eTo != o3tl::Length::invalid)
+ {
+ const double fConvert(o3tl::convert(1.0, o3tl::Length::mm100, eTo));
+ rPolyPolygon.transform(basegfx::utils::createScaleB2DHomMatrix(fConvert, fConvert));
+ }
+ else
+ {
+ OSL_FAIL("Missing unit translation to PoolMetric!");
+ }
+}
+
+SdrObject* SdrObjFactory::CreateObjectFromFactory(SdrModel& rSdrModel, SdrInventor nInventor, SdrObjKind nObjIdentifier)
+{
+ SdrObjCreatorParams aParams { nInventor, nObjIdentifier, rSdrModel };
+ for (const auto & i : ImpGetUserMakeObjHdl()) {
+ SdrObject* pObj = i.Call(aParams);
+ if (pObj) {
+ return pObj;
+ }
+ }
+ return nullptr;
+}
+
+SdrObject* SdrObjFactory::MakeNewObject(
+ SdrModel& rSdrModel,
+ SdrInventor nInventor,
+ SdrObjKind nIdentifier,
+ const tools::Rectangle* pSnapRect)
+{
+ SdrObject* pObj(nullptr);
+ bool bSetSnapRect(nullptr != pSnapRect);
+
+ if (nInventor == SdrInventor::Default)
+ {
+ switch (nIdentifier)
+ {
+ case SdrObjKind::Measure:
+ {
+ if(nullptr != pSnapRect)
+ {
+ pObj = new SdrMeasureObj(
+ rSdrModel,
+ pSnapRect->TopLeft(),
+ pSnapRect->BottomRight());
+ }
+ else
+ {
+ pObj = new SdrMeasureObj(rSdrModel);
+ }
+ }
+ break;
+ case SdrObjKind::Line:
+ {
+ if(nullptr != pSnapRect)
+ {
+ basegfx::B2DPolygon aPoly;
+
+ aPoly.append(
+ basegfx::B2DPoint(
+ pSnapRect->Left(),
+ pSnapRect->Top()));
+ aPoly.append(
+ basegfx::B2DPoint(
+ pSnapRect->Right(),
+ pSnapRect->Bottom()));
+ pObj = new SdrPathObj(
+ rSdrModel,
+ SdrObjKind::Line,
+ basegfx::B2DPolyPolygon(aPoly));
+ }
+ else
+ {
+ pObj = new SdrPathObj(
+ rSdrModel,
+ SdrObjKind::Line);
+ }
+ }
+ break;
+ case SdrObjKind::Text:
+ case SdrObjKind::TitleText:
+ case SdrObjKind::OutlineText:
+ {
+ if(nullptr != pSnapRect)
+ {
+ pObj = new SdrRectObj(
+ rSdrModel,
+ nIdentifier,
+ *pSnapRect);
+ bSetSnapRect = false;
+ }
+ else
+ {
+ pObj = new SdrRectObj(
+ rSdrModel,
+ nIdentifier);
+ }
+ }
+ break;
+ case SdrObjKind::CircleOrEllipse:
+ case SdrObjKind::CircleSection:
+ case SdrObjKind::CircleArc:
+ case SdrObjKind::CircleCut:
+ {
+ SdrCircKind eCircKind = ToSdrCircKind(nIdentifier);
+ if(nullptr != pSnapRect)
+ {
+ pObj = new SdrCircObj(rSdrModel, eCircKind, *pSnapRect);
+ bSetSnapRect = false;
+ }
+ else
+ {
+ pObj = new SdrCircObj(rSdrModel, eCircKind);
+ }
+ }
+ break;
+ case SdrObjKind::NONE : pObj=new SdrObject(rSdrModel); break;
+ case SdrObjKind::Group : pObj=new SdrObjGroup(rSdrModel); break;
+ case SdrObjKind::Polygon : pObj=new SdrPathObj(rSdrModel, SdrObjKind::Polygon ); break;
+ case SdrObjKind::PolyLine : pObj=new SdrPathObj(rSdrModel, SdrObjKind::PolyLine ); break;
+ case SdrObjKind::PathLine : pObj=new SdrPathObj(rSdrModel, SdrObjKind::PathLine ); break;
+ case SdrObjKind::PathFill : pObj=new SdrPathObj(rSdrModel, SdrObjKind::PathFill ); break;
+ case SdrObjKind::FreehandLine : pObj=new SdrPathObj(rSdrModel, SdrObjKind::FreehandLine ); break;
+ case SdrObjKind::FreehandFill : pObj=new SdrPathObj(rSdrModel, SdrObjKind::FreehandFill ); break;
+ case SdrObjKind::PathPoly : pObj=new SdrPathObj(rSdrModel, SdrObjKind::Polygon ); break;
+ case SdrObjKind::PathPolyLine : pObj=new SdrPathObj(rSdrModel, SdrObjKind::PolyLine ); break;
+ case SdrObjKind::Edge : pObj=new SdrEdgeObj(rSdrModel); break;
+ case SdrObjKind::Rectangle : pObj=new SdrRectObj(rSdrModel); break;
+ case SdrObjKind::Graphic : pObj=new SdrGrafObj(rSdrModel); break;
+ case SdrObjKind::OLE2 : pObj=new SdrOle2Obj(rSdrModel); break;
+ case SdrObjKind::OLEPluginFrame : pObj=new SdrOle2Obj(rSdrModel, true); break;
+ case SdrObjKind::Caption : pObj=new SdrCaptionObj(rSdrModel); break;
+ case SdrObjKind::Page : pObj=new SdrPageObj(rSdrModel); break;
+ case SdrObjKind::UNO : pObj=new SdrUnoObj(rSdrModel, OUString()); break;
+ case SdrObjKind::CustomShape: pObj=new SdrObjCustomShape(rSdrModel); break;
+#if HAVE_FEATURE_AVMEDIA
+ case SdrObjKind::Media : pObj=new SdrMediaObj(rSdrModel); break;
+#endif
+ case SdrObjKind::Table : pObj=new sdr::table::SdrTableObj(rSdrModel); break;
+ default: break;
+ }
+ }
+
+ if (!pObj)
+ {
+ pObj = CreateObjectFromFactory(rSdrModel, nInventor, nIdentifier);
+ }
+
+ if (!pObj)
+ {
+ // Well, if no one wants it...
+ return nullptr;
+ }
+
+ if(bSetSnapRect && nullptr != pSnapRect)
+ {
+ pObj->SetSnapRect(*pSnapRect);
+ }
+
+ return pObj;
+}
+
+void SdrObjFactory::InsertMakeObjectHdl(Link<SdrObjCreatorParams, SdrObject*> const & rLink)
+{
+ std::vector<Link<SdrObjCreatorParams, SdrObject*>>& rLL=ImpGetUserMakeObjHdl();
+ auto it = std::find(rLL.begin(), rLL.end(), rLink);
+ if (it != rLL.end()) {
+ OSL_FAIL("SdrObjFactory::InsertMakeObjectHdl(): Link already in place.");
+ } else {
+ rLL.push_back(rLink);
+ }
+}
+
+void SdrObjFactory::RemoveMakeObjectHdl(Link<SdrObjCreatorParams, SdrObject*> const & rLink)
+{
+ std::vector<Link<SdrObjCreatorParams, SdrObject*>>& rLL=ImpGetUserMakeObjHdl();
+ auto it = std::find(rLL.begin(), rLL.end(), rLink);
+ if (it != rLL.end())
+ rLL.erase(it);
+}
+
+namespace svx
+{
+ ISdrObjectFilter::~ISdrObjectFilter()
+ {
+ }
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/svdraw/svdobjplusdata.cxx b/svx/source/svdraw/svdobjplusdata.cxx
new file mode 100644
index 000000000..af27c5629
--- /dev/null
+++ b/svx/source/svdraw/svdobjplusdata.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/.
+*/
+
+#include <svdobjplusdata.hxx>
+#include <svdobjuserdatalist.hxx>
+#include <o3tl/deleter.hxx>
+#include <svx/svdglue.hxx>
+#include <svl/SfxBroadcaster.hxx>
+#include <osl/diagnose.h>
+
+SdrObjPlusData::SdrObjPlusData()
+{
+}
+
+SdrObjPlusData::~SdrObjPlusData()
+{
+ o3tl::reset_preserve_ptr_during(pBroadcast);
+ pUserDataList.reset();
+ pGluePoints.reset();
+}
+
+SdrObjPlusData* SdrObjPlusData::Clone(SdrObject* pObj1) const
+{
+ SdrObjPlusData* pNewPlusData=new SdrObjPlusData;
+ if (pUserDataList!=nullptr) {
+ sal_uInt16 nCount=pUserDataList->GetUserDataCount();
+ if (nCount!=0) {
+ pNewPlusData->pUserDataList.reset(new SdrObjUserDataList);
+ for (sal_uInt16 i=0; i<nCount; i++) {
+ std::unique_ptr<SdrObjUserData> pNewUserData=pUserDataList->GetUserData(i).Clone(pObj1);
+ if (pNewUserData!=nullptr) {
+ pNewPlusData->pUserDataList->AppendUserData(std::move(pNewUserData));
+ } else {
+ OSL_FAIL("SdrObjPlusData::Clone(): UserData.Clone() returns NULL.");
+ }
+ }
+ }
+ }
+ if (pGluePoints!=nullptr) pNewPlusData->pGluePoints.reset(new SdrGluePointList(*pGluePoints));
+ // MtfAnimator isn't copied either
+
+ // #i68101#
+ // copy object name, title and description
+ pNewPlusData->aObjName = aObjName;
+ pNewPlusData->aObjTitle = aObjTitle;
+ pNewPlusData->aObjDescription = aObjDescription;
+
+ return pNewPlusData;
+}
+
+void SdrObjPlusData::SetGluePoints(const SdrGluePointList& rPts)
+{
+ *pGluePoints = rPts;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/svdraw/svdobjuserdatalist.cxx b/svx/source/svdraw/svdobjuserdatalist.cxx
new file mode 100644
index 000000000..cae5e8db6
--- /dev/null
+++ b/svx/source/svdraw/svdobjuserdatalist.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/.
+*/
+
+#include <memory>
+#include <svdobjuserdatalist.hxx>
+
+SdrObjUserDataList::SdrObjUserDataList() {}
+SdrObjUserDataList::~SdrObjUserDataList() {}
+
+size_t SdrObjUserDataList::GetUserDataCount() const
+{
+ return maList.size();
+}
+
+SdrObjUserData& SdrObjUserDataList::GetUserData(size_t nNum)
+{
+ return *maList.at(nNum);
+}
+
+void SdrObjUserDataList::AppendUserData(std::unique_ptr<SdrObjUserData> pData)
+{
+ maList.push_back(std::move(pData));
+}
+
+void SdrObjUserDataList::DeleteUserData(size_t nNum)
+{
+ maList.erase(maList.begin()+nNum);
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/svdraw/svdocapt.cxx b/svx/source/svdraw/svdocapt.cxx
new file mode 100644
index 000000000..4734ea222
--- /dev/null
+++ b/svx/source/svdraw/svdocapt.cxx
@@ -0,0 +1,756 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <sal/config.h>
+
+#include <cassert>
+
+#include <basegfx/matrix/b2dhommatrix.hxx>
+#include <basegfx/polygon/b2dpolygon.hxx>
+#include <basegfx/polygon/b2dpolygontools.hxx>
+#include <basegfx/range/b2drange.hxx>
+#include <basegfx/tuple/b2dtuple.hxx>
+#include <tools/bigint.hxx>
+#include <tools/helpers.hxx>
+
+#include <svx/dialmgr.hxx>
+#include <svx/strings.hrc>
+
+#include <sdr/contact/viewcontactofsdrcaptionobj.hxx>
+#include <sdr/properties/captionproperties.hxx>
+#include <svx/sdrhittesthelper.hxx>
+#include <svx/sdooitm.hxx>
+#include <svx/svddrag.hxx>
+#include <svx/svdhdl.hxx>
+#include <svx/svdmodel.hxx>
+#include <svx/svdocapt.hxx>
+#include <svx/svdopath.hxx>
+#include <svx/svdogrp.hxx>
+#include <svx/svdpage.hxx>
+#include <svx/svdtrans.hxx>
+#include <svx/svdview.hxx>
+#include <svx/sxcecitm.hxx>
+#include <svx/sxcgitm.hxx>
+#include <svx/sxcllitm.hxx>
+#include <svx/sxctitm.hxx>
+#include <vcl/canvastools.hxx>
+#include <vcl/ptrstyle.hxx>
+
+namespace {
+
+enum EscDir {LKS,RTS,OBN,UNT};
+
+}
+
+class ImpCaptParams
+{
+public:
+ SdrCaptionType eType;
+ tools::Long nGap;
+ tools::Long nEscRel;
+ tools::Long nEscAbs;
+ tools::Long nLineLen;
+ SdrCaptionEscDir eEscDir;
+ bool bFitLineLen;
+ bool bEscRel;
+ bool bFixedAngle;
+
+public:
+ ImpCaptParams()
+ : eType(SdrCaptionType::Type3),
+ nGap(0), nEscRel(5000), nEscAbs(0),
+ nLineLen(0), eEscDir(SdrCaptionEscDir::Horizontal),
+ bFitLineLen(true), bEscRel(true), bFixedAngle(false)
+ {
+ }
+ void CalcEscPos(const Point& rTail, const tools::Rectangle& rRect, Point& rPt, EscDir& rDir) const;
+};
+
+void ImpCaptParams::CalcEscPos(const Point& rTailPt, const tools::Rectangle& rRect, Point& rPt, EscDir& rDir) const
+{
+ Point aTl(rTailPt); // copy locally for performance reasons
+ tools::Long nX,nY;
+ if (bEscRel) {
+ nX=rRect.Right()-rRect.Left();
+ nX=BigMulDiv(nX,nEscRel,10000);
+ nY=rRect.Bottom()-rRect.Top();
+ nY=BigMulDiv(nY,nEscRel,10000);
+ } else {
+ nX=nEscAbs;
+ nY=nEscAbs;
+ }
+ nX+=rRect.Left();
+ nY+=rRect.Top();
+ Point aBestPt;
+ EscDir eBestDir=LKS;
+ bool bTryH=eEscDir==SdrCaptionEscDir::BestFit;
+ if (!bTryH) {
+ if (eType!=SdrCaptionType::Type1) {
+ bTryH=eEscDir==SdrCaptionEscDir::Horizontal;
+ } else {
+ bTryH=eEscDir==SdrCaptionEscDir::Vertical;
+ }
+ }
+ bool bTryV=eEscDir==SdrCaptionEscDir::BestFit;
+ if (!bTryV) {
+ if (eType!=SdrCaptionType::Type1) {
+ bTryV=eEscDir==SdrCaptionEscDir::Vertical;
+ } else {
+ bTryV=eEscDir==SdrCaptionEscDir::Horizontal;
+ }
+ }
+
+ if (bTryH) {
+ Point aLft(rRect.Left()-nGap,nY);
+ Point aRgt(rRect.Right()+nGap,nY);
+ bool bLft=(aTl.X()-aLft.X()<aRgt.X()-aTl.X());
+ if (bLft) {
+ eBestDir=LKS;
+ aBestPt=aLft;
+ } else {
+ eBestDir=RTS;
+ aBestPt=aRgt;
+ }
+ }
+ if (bTryV) {
+ Point aTop(nX,rRect.Top()-nGap);
+ Point aBtm(nX,rRect.Bottom()+nGap);
+ bool bTop=(aTl.Y()-aTop.Y()<aBtm.Y()-aTl.Y());
+ Point aBest2;
+ EscDir eBest2;
+ if (bTop) {
+ eBest2=OBN;
+ aBest2=aTop;
+ } else {
+ eBest2=UNT;
+ aBest2=aBtm;
+ }
+ bool bTakeIt=eEscDir!=SdrCaptionEscDir::BestFit;
+ if (!bTakeIt) {
+ BigInt aHorX(aBestPt.X()-aTl.X()); aHorX*=aHorX;
+ BigInt aHorY(aBestPt.Y()-aTl.Y()); aHorY*=aHorY;
+ BigInt aVerX(aBest2.X()-aTl.X()); aVerX*=aVerX;
+ BigInt aVerY(aBest2.Y()-aTl.Y()); aVerY*=aVerY;
+ if (eType!=SdrCaptionType::Type1) {
+ bTakeIt=aVerX+aVerY<aHorX+aHorY;
+ } else {
+ bTakeIt=aVerX+aVerY>=aHorX+aHorY;
+ }
+ }
+ if (bTakeIt) {
+ aBestPt=aBest2;
+ eBestDir=eBest2;
+ }
+ }
+ rPt=aBestPt;
+ rDir=eBestDir;
+}
+
+
+// BaseProperties section
+
+std::unique_ptr<sdr::properties::BaseProperties> SdrCaptionObj::CreateObjectSpecificProperties()
+{
+ return std::make_unique<sdr::properties::CaptionProperties>(*this);
+}
+
+
+// DrawContact section
+
+std::unique_ptr<sdr::contact::ViewContact> SdrCaptionObj::CreateObjectSpecificViewContact()
+{
+ return std::make_unique<sdr::contact::ViewContactOfSdrCaptionObj>(*this);
+}
+
+
+SdrCaptionObj::SdrCaptionObj(SdrModel& rSdrModel)
+: SdrRectObj(rSdrModel, SdrObjKind::Text),
+ aTailPoly(3), // default size: 3 points = 2 lines
+ mbSpecialTextBoxShadow(false),
+ mbFixedTail(false),
+ mbSuppressGetBitmap(false)
+{
+}
+
+SdrCaptionObj::SdrCaptionObj(SdrModel& rSdrModel, SdrCaptionObj const & rSource)
+: SdrRectObj(rSdrModel, rSource),
+ mbSuppressGetBitmap(false)
+{
+ aTailPoly = rSource.aTailPoly;
+ mbSpecialTextBoxShadow = rSource.mbSpecialTextBoxShadow;
+ mbFixedTail = rSource.mbFixedTail;
+ maFixedTailPos = rSource.maFixedTailPos;
+}
+
+SdrCaptionObj::SdrCaptionObj(
+ SdrModel& rSdrModel,
+ const tools::Rectangle& rRect,
+ const Point& rTail)
+: SdrRectObj(rSdrModel, SdrObjKind::Text,rRect),
+ aTailPoly(3), // default size: 3 points = 2 lines
+ mbSpecialTextBoxShadow(false),
+ mbFixedTail(false),
+ mbSuppressGetBitmap(false)
+{
+ aTailPoly[0]=maFixedTailPos=rTail;
+}
+
+SdrCaptionObj::~SdrCaptionObj()
+{
+}
+
+void SdrCaptionObj::TakeObjInfo(SdrObjTransformInfoRec& rInfo) const
+{
+ rInfo.bRotateFreeAllowed=false;
+ rInfo.bRotate90Allowed =false;
+ rInfo.bMirrorFreeAllowed=false;
+ rInfo.bMirror45Allowed =false;
+ rInfo.bMirror90Allowed =false;
+ rInfo.bTransparenceAllowed = false;
+ rInfo.bShearAllowed =false;
+ rInfo.bEdgeRadiusAllowed=false;
+ rInfo.bCanConvToPath =true;
+ rInfo.bCanConvToPoly =true;
+ rInfo.bCanConvToPathLineToArea=false;
+ rInfo.bCanConvToPolyLineToArea=false;
+ rInfo.bCanConvToContour = (rInfo.bCanConvToPoly || LineGeometryUsageIsNecessary());
+}
+
+SdrObjKind SdrCaptionObj::GetObjIdentifier() const
+{
+ return SdrObjKind::Caption;
+}
+
+SdrCaptionObj* SdrCaptionObj::CloneSdrObject(SdrModel& rTargetModel) const
+{
+ return new SdrCaptionObj(rTargetModel, *this);
+}
+
+OUString SdrCaptionObj::TakeObjNameSingul() const
+{
+ OUString sName(SvxResId(STR_ObjNameSingulCAPTION));
+
+ OUString aName(GetName());
+ if (!aName.isEmpty())
+ sName += " '" + aName + "'";
+
+ return sName;
+}
+
+OUString SdrCaptionObj::TakeObjNamePlural() const
+{
+ return SvxResId(STR_ObjNamePluralCAPTION);
+}
+
+basegfx::B2DPolyPolygon SdrCaptionObj::TakeXorPoly() const
+{
+ basegfx::B2DPolyPolygon aPolyPoly(SdrRectObj::TakeXorPoly());
+ aPolyPoly.append(aTailPoly.getB2DPolygon());
+
+ return aPolyPoly;
+}
+
+sal_uInt32 SdrCaptionObj::GetHdlCount() const
+{
+ sal_uInt32 nCount1(SdrRectObj::GetHdlCount());
+ // Currently only dragging the tail's end is implemented.
+ return nCount1 + 1;
+}
+
+void SdrCaptionObj::AddToHdlList(SdrHdlList& rHdlList) const
+{
+ SdrRectObj::AddToHdlList(rHdlList);
+ // Currently only dragging the tail's end is implemented.
+ std::unique_ptr<SdrHdl> pHdl(new SdrHdl(aTailPoly.GetPoint(0), SdrHdlKind::Poly));
+ pHdl->SetPolyNum(1);
+ pHdl->SetPointNum(0);
+ rHdlList.AddHdl(std::move(pHdl));
+}
+
+bool SdrCaptionObj::hasSpecialDrag() const
+{
+ return true;
+}
+
+bool SdrCaptionObj::beginSpecialDrag(SdrDragStat& rDrag) const
+{
+ const SdrHdl* pHdl = rDrag.GetHdl();
+ rDrag.SetEndDragChangesAttributes(true);
+ rDrag.SetEndDragChangesGeoAndAttributes(true);
+
+ if(pHdl && 0 == pHdl->GetPolyNum())
+ {
+ return SdrRectObj::beginSpecialDrag(rDrag);
+ }
+ else
+ {
+ rDrag.SetOrtho8Possible();
+
+ if(!pHdl)
+ {
+ if (m_bMovProt)
+ return false;
+
+ rDrag.SetNoSnap();
+ rDrag.SetActionRect(maRect);
+
+ Point aHit(rDrag.GetStart());
+
+ if(rDrag.GetPageView() && SdrObjectPrimitiveHit(*this, aHit, 0, *rDrag.GetPageView(), nullptr, false))
+ {
+ return true;
+ }
+ }
+ else
+ {
+ if((1 == pHdl->GetPolyNum()) && (0 == pHdl->GetPointNum()))
+ return true;
+ }
+ }
+
+ return false;
+}
+
+bool SdrCaptionObj::applySpecialDrag(SdrDragStat& rDrag)
+{
+ const SdrHdl* pHdl = rDrag.GetHdl();
+
+ if(pHdl && 0 == pHdl->GetPolyNum())
+ {
+ const bool bRet(SdrRectObj::applySpecialDrag(rDrag));
+ ImpRecalcTail();
+ ActionChanged();
+
+ return bRet;
+ }
+ else
+ {
+ Point aDelt(rDrag.GetNow()-rDrag.GetStart());
+
+ if(!pHdl)
+ {
+ maRect.Move(aDelt.X(),aDelt.Y());
+ }
+ else
+ {
+ aTailPoly[0] += aDelt;
+ }
+
+ ImpRecalcTail();
+ ActionChanged();
+
+ return true;
+ }
+}
+
+OUString SdrCaptionObj::getSpecialDragComment(const SdrDragStat& rDrag) const
+{
+ const bool bCreateComment(rDrag.GetView() && this == rDrag.GetView()->GetCreateObj());
+
+ if(bCreateComment)
+ {
+ return OUString();
+ }
+ else
+ {
+ const SdrHdl* pHdl = rDrag.GetHdl();
+
+ if(pHdl && 0 == pHdl->GetPolyNum())
+ {
+ return SdrRectObj::getSpecialDragComment(rDrag);
+ }
+ else
+ {
+ if(!pHdl)
+ {
+ return ImpGetDescriptionStr(STR_DragCaptFram);
+ }
+ else
+ {
+ return ImpGetDescriptionStr(STR_DragCaptTail);
+ }
+ }
+ }
+}
+
+
+void SdrCaptionObj::ImpGetCaptParams(ImpCaptParams& rPara) const
+{
+ const SfxItemSet& rSet = GetObjectItemSet();
+ rPara.eType =rSet.Get(SDRATTR_CAPTIONTYPE ).GetValue();
+ rPara.bFixedAngle=rSet.Get(SDRATTR_CAPTIONFIXEDANGLE).GetValue();
+ rPara.nGap =static_cast<const SdrCaptionGapItem&> (rSet.Get(SDRATTR_CAPTIONGAP )).GetValue();
+ rPara.eEscDir =rSet.Get(SDRATTR_CAPTIONESCDIR ).GetValue();
+ rPara.bEscRel =rSet.Get(SDRATTR_CAPTIONESCISREL ).GetValue();
+ rPara.nEscRel =rSet.Get(SDRATTR_CAPTIONESCREL ).GetValue();
+ rPara.nEscAbs =rSet.Get(SDRATTR_CAPTIONESCABS ).GetValue();
+ rPara.nLineLen =rSet.Get(SDRATTR_CAPTIONLINELEN ).GetValue();
+ rPara.bFitLineLen=rSet.Get(SDRATTR_CAPTIONFITLINELEN).GetValue();
+}
+
+void SdrCaptionObj::ImpRecalcTail()
+{
+ ImpCaptParams aPara;
+ ImpGetCaptParams(aPara);
+ ImpCalcTail(aPara, aTailPoly, maRect);
+ SetBoundAndSnapRectsDirty();
+ SetXPolyDirty();
+}
+
+// #i35971#
+// SdrCaptionObj::ImpCalcTail1 does move the object(!). What a hack.
+// I really wonder why this had not triggered problems before. I am
+// sure there are some places where SetTailPos() is called at least
+// twice or SetSnapRect after it again just to work around this.
+// Changed this method to not do that.
+// Also found why this has been done: For interactive dragging of the
+// tail end pos for SdrCaptionType::Type1. This sure was the simplest method
+// to achieve this, at the cost of making a whole group of const methods
+// of this object implicitly change the object's position.
+void SdrCaptionObj::ImpCalcTail1(const ImpCaptParams& rPara, tools::Polygon& rPoly, tools::Rectangle const & rRect)
+{
+ tools::Polygon aPol(2);
+ Point aTl(rPoly[0]);
+
+ aPol[0] = aTl;
+ aPol[1] = aTl;
+
+ EscDir eEscDir;
+ Point aEscPos;
+
+ rPara.CalcEscPos(aTl, rRect, aEscPos, eEscDir);
+ aPol[1] = aEscPos;
+
+ if(eEscDir==LKS || eEscDir==RTS)
+ {
+ aPol[0].setX( aEscPos.X() );
+ }
+ else
+ {
+ aPol[0].setY( aEscPos.Y() );
+ }
+
+ rPoly = aPol;
+}
+
+void SdrCaptionObj::ImpCalcTail2(const ImpCaptParams& rPara, tools::Polygon& rPoly, tools::Rectangle const & rRect)
+{ // Gap/EscDir/EscPos/Angle
+ tools::Polygon aPol(2);
+ Point aTl(rPoly[0]);
+ aPol[0]=aTl;
+
+ EscDir eEscDir;
+ Point aEscPos;
+ rPara.CalcEscPos(aTl,rRect,aEscPos,eEscDir);
+ aPol[1]=aEscPos;
+
+ if (!rPara.bFixedAngle) {
+ // TODO: Implementation missing.
+ }
+ rPoly=aPol;
+}
+
+void SdrCaptionObj::ImpCalcTail3(const ImpCaptParams& rPara, tools::Polygon& rPoly, tools::Rectangle const & rRect)
+{ // Gap/EscDir/EscPos/Angle/LineLen
+ tools::Polygon aPol(3);
+ Point aTl(rPoly[0]);
+ aPol[0]=aTl;
+
+ EscDir eEscDir;
+ Point aEscPos;
+ rPara.CalcEscPos(aTl,rRect,aEscPos,eEscDir);
+ aPol[1]=aEscPos;
+ aPol[2]=aEscPos;
+
+ if (eEscDir==LKS || eEscDir==RTS) {
+ if (rPara.bFitLineLen) {
+ aPol[1].setX((aTl.X()+aEscPos.X())/2 );
+ } else {
+ if (eEscDir==LKS) aPol[1].AdjustX( -(rPara.nLineLen) );
+ else aPol[1].AdjustX(rPara.nLineLen );
+ }
+ } else {
+ if (rPara.bFitLineLen) {
+ aPol[1].setY((aTl.Y()+aEscPos.Y())/2 );
+ } else {
+ if (eEscDir==OBN) aPol[1].AdjustY( -(rPara.nLineLen) );
+ else aPol[1].AdjustY(rPara.nLineLen );
+ }
+ }
+ if (!rPara.bFixedAngle) {
+ // TODO: Implementation missing.
+ }
+ rPoly=aPol;
+}
+
+void SdrCaptionObj::ImpCalcTail(const ImpCaptParams& rPara, tools::Polygon& rPoly, tools::Rectangle const & rRect)
+{
+ switch (rPara.eType) {
+ case SdrCaptionType::Type1: ImpCalcTail1(rPara,rPoly,rRect); break;
+ case SdrCaptionType::Type2: ImpCalcTail2(rPara,rPoly,rRect); break;
+ case SdrCaptionType::Type3: ImpCalcTail3(rPara,rPoly,rRect); break;
+ case SdrCaptionType::Type4: ImpCalcTail3(rPara,rPoly,rRect); break;
+ }
+}
+
+bool SdrCaptionObj::BegCreate(SdrDragStat& rStat)
+{
+ if (maRect.IsEmpty()) return false; // Create currently only works with the given Rect
+
+ ImpCaptParams aPara;
+ ImpGetCaptParams(aPara);
+ maRect.SetPos(rStat.GetNow());
+ aTailPoly[0]=rStat.GetStart();
+ ImpCalcTail(aPara,aTailPoly,maRect);
+ rStat.SetActionRect(maRect);
+ return true;
+}
+
+bool SdrCaptionObj::MovCreate(SdrDragStat& rStat)
+{
+ ImpCaptParams aPara;
+ ImpGetCaptParams(aPara);
+ maRect.SetPos(rStat.GetNow());
+ ImpCalcTail(aPara,aTailPoly,maRect);
+ rStat.SetActionRect(maRect);
+ SetBoundRectDirty();
+ m_bSnapRectDirty=true;
+ return true;
+}
+
+bool SdrCaptionObj::EndCreate(SdrDragStat& rStat, SdrCreateCmd eCmd)
+{
+ ImpCaptParams aPara;
+ ImpGetCaptParams(aPara);
+ maRect.SetPos(rStat.GetNow());
+ ImpCalcTail(aPara,aTailPoly,maRect);
+ SetBoundAndSnapRectsDirty();
+ return (eCmd==SdrCreateCmd::ForceEnd || rStat.GetPointCount()>=2);
+}
+
+bool SdrCaptionObj::BckCreate(SdrDragStat& /*rStat*/)
+{
+ return false;
+}
+
+void SdrCaptionObj::BrkCreate(SdrDragStat& /*rStat*/)
+{
+}
+
+basegfx::B2DPolyPolygon SdrCaptionObj::TakeCreatePoly(const SdrDragStat& /*rDrag*/) const
+{
+ basegfx::B2DPolyPolygon aRetval;
+ const basegfx::B2DRange aRange =vcl::unotools::b2DRectangleFromRectangle(maRect);
+ aRetval.append(basegfx::utils::createPolygonFromRect(aRange));
+ aRetval.append(aTailPoly.getB2DPolygon());
+ return aRetval;
+}
+
+PointerStyle SdrCaptionObj::GetCreatePointer() const
+{
+ return PointerStyle::DrawCaption;
+}
+
+void SdrCaptionObj::NbcMove(const Size& rSiz)
+{
+ SdrRectObj::NbcMove(rSiz);
+ MovePoly(aTailPoly,rSiz);
+ if(mbFixedTail)
+ SetTailPos(GetFixedTailPos());
+}
+
+void SdrCaptionObj::NbcResize(const Point& rRef, const Fraction& xFact, const Fraction& yFact)
+{
+ SdrRectObj::NbcResize(rRef,xFact,yFact);
+ ResizePoly(aTailPoly,rRef,xFact,yFact);
+ ImpRecalcTail();
+ if(mbFixedTail)
+ SetTailPos(GetFixedTailPos());
+}
+
+void SdrCaptionObj::NbcSetRelativePos(const Point& rPnt)
+{
+ Point aRelPos0(aTailPoly.GetPoint(0)-m_aAnchor);
+ Size aSiz(rPnt.X()-aRelPos0.X(),rPnt.Y()-aRelPos0.Y());
+ NbcMove(aSiz); // This also calls SetRectsDirty()
+}
+
+Point SdrCaptionObj::GetRelativePos() const
+{
+ return aTailPoly.GetPoint(0)-m_aAnchor;
+}
+
+const tools::Rectangle& SdrCaptionObj::GetLogicRect() const
+{
+ return maRect;
+}
+
+void SdrCaptionObj::NbcSetLogicRect(const tools::Rectangle& rRect)
+{
+ SdrRectObj::NbcSetLogicRect(rRect);
+ ImpRecalcTail();
+}
+
+const Point& SdrCaptionObj::GetTailPos() const
+{
+ return aTailPoly[0];
+}
+
+void SdrCaptionObj::SetTailPos(const Point& rPos)
+{
+ if (aTailPoly.GetSize()==0 || aTailPoly[0]!=rPos) {
+ tools::Rectangle aBoundRect0; if (m_pUserCall!=nullptr) aBoundRect0=GetLastBoundRect();
+ NbcSetTailPos(rPos);
+ SetChanged();
+ BroadcastObjectChange();
+ SendUserCall(SdrUserCallType::Resize,aBoundRect0);
+ }
+}
+
+void SdrCaptionObj::NbcSetTailPos(const Point& rPos)
+{
+ aTailPoly[0]=rPos;
+ ImpRecalcTail();
+}
+
+sal_uInt32 SdrCaptionObj::GetSnapPointCount() const
+{
+ // TODO: Implementation missing.
+ return 0;
+}
+
+Point SdrCaptionObj::GetSnapPoint(sal_uInt32 /*i*/) const
+{
+ // TODO: Implementation missing.
+ return Point(0,0);
+}
+
+void SdrCaptionObj::Notify(SfxBroadcaster& rBC, const SfxHint& rHint)
+{
+ SdrRectObj::Notify(rBC,rHint);
+ ImpRecalcTail();
+}
+
+std::unique_ptr<SdrObjGeoData> SdrCaptionObj::NewGeoData() const
+{
+ return std::make_unique<SdrCaptObjGeoData>();
+}
+
+void SdrCaptionObj::SaveGeoData(SdrObjGeoData& rGeo) const
+{
+ SdrRectObj::SaveGeoData(rGeo);
+ SdrCaptObjGeoData& rCGeo=static_cast<SdrCaptObjGeoData&>(rGeo);
+ rCGeo.aTailPoly=aTailPoly;
+}
+
+void SdrCaptionObj::RestoreGeoData(const SdrObjGeoData& rGeo)
+{
+ SdrRectObj::RestoreGeoData(rGeo);
+ const SdrCaptObjGeoData& rCGeo=static_cast<const SdrCaptObjGeoData&>(rGeo);
+ aTailPoly=rCGeo.aTailPoly;
+}
+
+SdrObjectUniquePtr SdrCaptionObj::DoConvertToPolyObj(bool bBezier, bool bAddText) const
+{
+ SdrObjectUniquePtr pRect = SdrRectObj::DoConvertToPolyObj(bBezier, bAddText);
+ SdrObjectUniquePtr pTail = ImpConvertMakeObj(basegfx::B2DPolyPolygon(aTailPoly.getB2DPolygon()), false, bBezier);
+ SdrObjectUniquePtr pRet;
+ if (pTail && !pRect)
+ pRet = std::move(pTail);
+ else if (pRect && !pTail)
+ pRet = std::move(pRect);
+ else if (pTail && pRect)
+ {
+ if (pTail->GetSubList())
+ {
+ pTail->GetSubList()->NbcInsertObject(pRect.release());
+ pRet = std::move(pTail);
+ }
+ else if (pRect->GetSubList())
+ {
+ pRect->GetSubList()->NbcInsertObject(pTail.release(),0);
+ pRet = std::move(pRect);
+ }
+ else
+ {
+ SdrObjGroup* pGrp = new SdrObjGroup(getSdrModelFromSdrObject());
+ pGrp->GetSubList()->NbcInsertObject(pRect.release());
+ pGrp->GetSubList()->NbcInsertObject(pTail.release(),0);
+ pRet.reset(pGrp);
+ }
+ }
+ return pRet;
+}
+
+namespace {
+
+void handleNegativeScale(basegfx::B2DTuple & scale, double * rotate) {
+ assert(rotate != nullptr);
+
+ // #i75086# Old DrawingLayer (GeoStat and geometry) does not support holding negative scalings
+ // in X and Y which equal a 180 degree rotation. Recognize it and react accordingly
+ if(basegfx::fTools::less(scale.getX(), 0.0) && basegfx::fTools::less(scale.getY(), 0.0))
+ {
+ scale.setX(fabs(scale.getX()));
+ scale.setY(fabs(scale.getY()));
+ *rotate = fmod(*rotate + M_PI, 2 * M_PI);
+ }
+}
+
+}
+
+// #i32599#
+// Add own implementation for TRSetBaseGeometry to handle TailPos over changes.
+void SdrCaptionObj::TRSetBaseGeometry(const basegfx::B2DHomMatrix& rMatrix, const basegfx::B2DPolyPolygon& /*rPolyPolygon*/)
+{
+ // break up matrix
+ basegfx::B2DTuple aScale;
+ basegfx::B2DTuple aTranslate;
+ double fRotate, fShearX;
+ rMatrix.decompose(aScale, aTranslate, fRotate, fShearX);
+
+ handleNegativeScale(aScale, &fRotate);
+
+ // if anchor is used, make position relative to it
+ if(getSdrModelFromSdrObject().IsWriter())
+ {
+ if(GetAnchorPos().X() || GetAnchorPos().Y())
+ {
+ aTranslate += basegfx::B2DTuple(GetAnchorPos().X(), GetAnchorPos().Y());
+ }
+ }
+
+ // build BaseRect
+ Point aPoint(FRound(aTranslate.getX()), FRound(aTranslate.getY()));
+ tools::Rectangle aBaseRect(aPoint, Size(FRound(aScale.getX()), FRound(aScale.getY())));
+
+ // set BaseRect, but rescue TailPos over this call
+ const Point aTailPoint = GetTailPos();
+ SetSnapRect(aBaseRect);
+ SetTailPos(aTailPoint);
+ ImpRecalcTail();
+}
+
+// geometry access
+basegfx::B2DPolygon SdrCaptionObj::getTailPolygon() const
+{
+ return aTailPoly.getB2DPolygon();
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/svdraw/svdocirc.cxx b/svx/source/svdraw/svdocirc.cxx
new file mode 100644
index 000000000..0204b2c2e
--- /dev/null
+++ b/svx/source/svdraw/svdocirc.cxx
@@ -0,0 +1,1154 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <basegfx/matrix/b2dhommatrix.hxx>
+#include <basegfx/matrix/b2dhommatrixtools.hxx>
+#include <basegfx/point/b2dpoint.hxx>
+#include <basegfx/polygon/b2dpolygon.hxx>
+#include <basegfx/polygon/b2dpolygontools.hxx>
+#include <math.h>
+#include <rtl/ustrbuf.hxx>
+
+#include <svx/dialmgr.hxx>
+#include <svx/strings.hrc>
+
+#include <sdr/contact/viewcontactofsdrcircobj.hxx>
+#include <sdr/properties/circleproperties.hxx>
+#include <svx/svddrag.hxx>
+#include <svx/svdmodel.hxx>
+#include <svx/svdocirc.hxx>
+#include <svx/svdopath.hxx>
+#include <svx/svdtrans.hxx>
+#include <svx/svdview.hxx>
+#include <svx/sxciaitm.hxx>
+#include <sxcikitm.hxx>
+#include <svx/xfillit0.hxx>
+#include <svx/xlineit0.hxx>
+#include <svx/xlnedit.hxx>
+#include <svx/xlnedwit.hxx>
+#include <svx/xlnstit.hxx>
+#include <svx/xlnstwit.hxx>
+#include <svx/xlnwtit.hxx>
+#include <vcl/canvastools.hxx>
+#include <vcl/ptrstyle.hxx>
+
+using namespace com::sun::star;
+
+static Point GetAnglePnt(const tools::Rectangle& rR, Degree100 nAngle)
+{
+ Point aCenter(rR.Center());
+ tools::Long nWdt=rR.Right()-rR.Left();
+ tools::Long nHgt=rR.Bottom()-rR.Top();
+ tools::Long nMaxRad=(std::max(nWdt,nHgt)+1) /2;
+ double a = toRadians(nAngle);
+ Point aRetval(FRound(cos(a)*nMaxRad),-FRound(sin(a)*nMaxRad));
+ if (nWdt==0) aRetval.setX(0 );
+ if (nHgt==0) aRetval.setY(0 );
+ if (nWdt!=nHgt) {
+ if (nWdt>nHgt) {
+ if (nWdt!=0) {
+ // stop possible overruns for very large objects
+ if (std::abs(nHgt)>32767 || std::abs(aRetval.Y())>32767) {
+ aRetval.setY(BigMulDiv(aRetval.Y(),nHgt,nWdt) );
+ } else {
+ aRetval.setY(aRetval.Y()*nHgt/nWdt );
+ }
+ }
+ } else {
+ if (nHgt!=0) {
+ // stop possible overruns for very large objects
+ if (std::abs(nWdt)>32767 || std::abs(aRetval.X())>32767) {
+ aRetval.setX(BigMulDiv(aRetval.X(),nWdt,nHgt) );
+ } else {
+ aRetval.setX(aRetval.X()*nWdt/nHgt );
+ }
+ }
+ }
+ }
+ aRetval+=aCenter;
+ return aRetval;
+}
+
+
+// BaseProperties section
+
+std::unique_ptr<sdr::properties::BaseProperties> SdrCircObj::CreateObjectSpecificProperties()
+{
+ return std::make_unique<sdr::properties::CircleProperties>(*this);
+}
+
+
+// DrawContact section
+
+std::unique_ptr<sdr::contact::ViewContact> SdrCircObj::CreateObjectSpecificViewContact()
+{
+ return std::make_unique<sdr::contact::ViewContactOfSdrCircObj>(*this);
+}
+
+SdrCircKind ToSdrCircKind(SdrObjKind eKind)
+{
+ switch (eKind)
+ {
+ case SdrObjKind::CircleOrEllipse: return SdrCircKind::Full;
+ case SdrObjKind::CircleSection: return SdrCircKind::Section;
+ case SdrObjKind::CircleArc: return SdrCircKind::Arc;
+ case SdrObjKind::CircleCut: return SdrCircKind::Cut;
+ default: assert(false);
+ }
+ return SdrCircKind::Full;
+}
+
+SdrCircObj::SdrCircObj(
+ SdrModel& rSdrModel,
+ SdrCircKind eNewKind)
+: SdrRectObj(rSdrModel)
+{
+ nStartAngle=0_deg100;
+ nEndAngle=36000_deg100;
+ meCircleKind=eNewKind;
+ m_bClosedObj=eNewKind!=SdrCircKind::Arc;
+}
+
+SdrCircObj::SdrCircObj(SdrModel& rSdrModel, SdrCircObj const & rSource)
+: SdrRectObj(rSdrModel, rSource)
+{
+ meCircleKind = rSource.meCircleKind;
+ nStartAngle = rSource.nStartAngle;
+ nEndAngle = rSource.nEndAngle;
+ m_bClosedObj = rSource.m_bClosedObj;
+}
+
+SdrCircObj::SdrCircObj(
+ SdrModel& rSdrModel,
+ SdrCircKind eNewKind,
+ const tools::Rectangle& rRect)
+: SdrRectObj(rSdrModel, rRect)
+{
+ nStartAngle=0_deg100;
+ nEndAngle=36000_deg100;
+ meCircleKind=eNewKind;
+ m_bClosedObj=eNewKind!=SdrCircKind::Arc;
+}
+
+SdrCircObj::SdrCircObj(
+ SdrModel& rSdrModel,
+ SdrCircKind eNewKind,
+ const tools::Rectangle& rRect,
+ Degree100 nNewStartAngle,
+ Degree100 nNewEndAngle)
+: SdrRectObj(rSdrModel, rRect)
+{
+ Degree100 nAngleDif=nNewEndAngle-nNewStartAngle;
+ nStartAngle=NormAngle36000(nNewStartAngle);
+ nEndAngle=NormAngle36000(nNewEndAngle);
+ if (nAngleDif==36000_deg100) nEndAngle+=nAngleDif; // full circle
+ meCircleKind=eNewKind;
+ m_bClosedObj=eNewKind!=SdrCircKind::Arc;
+}
+
+SdrCircObj::~SdrCircObj()
+{
+}
+
+void SdrCircObj::TakeObjInfo(SdrObjTransformInfoRec& rInfo) const
+{
+ bool bCanConv=!HasText() || ImpCanConvTextToCurve();
+ rInfo.bEdgeRadiusAllowed = false;
+ rInfo.bCanConvToPath=bCanConv;
+ rInfo.bCanConvToPoly=bCanConv;
+ rInfo.bCanConvToContour = !IsFontwork() && (rInfo.bCanConvToPoly || LineGeometryUsageIsNecessary());
+}
+
+SdrObjKind SdrCircObj::GetObjIdentifier() const
+{
+ switch (meCircleKind)
+ {
+ case SdrCircKind::Full: return SdrObjKind::CircleOrEllipse;
+ case SdrCircKind::Section: return SdrObjKind::CircleSection;
+ case SdrCircKind::Cut: return SdrObjKind::CircleCut;
+ case SdrCircKind::Arc: return SdrObjKind::CircleArc;
+ default: assert(false);
+ }
+ return SdrObjKind::CircleOrEllipse;
+}
+
+bool SdrCircObj::PaintNeedsXPolyCirc() const
+{
+ // XPoly is necessary for all rotated ellipse objects, circle and
+ // ellipse segments.
+ // If not WIN, then (for now) also for circle/ellipse segments and circle/
+ // ellipse arcs (for precision)
+ bool bNeed = maGeo.nRotationAngle || maGeo.nShearAngle || meCircleKind == SdrCircKind::Cut;
+ // If not WIN, then for everything except full circle (for now!)
+ if (meCircleKind!=SdrCircKind::Full) bNeed = true;
+
+ const SfxItemSet& rSet = GetObjectItemSet();
+ if(!bNeed)
+ {
+ // XPoly is necessary for everything that isn't LineSolid or LineNone
+ drawing::LineStyle eLine = rSet.Get(XATTR_LINESTYLE).GetValue();
+ bNeed = eLine != drawing::LineStyle_NONE && eLine != drawing::LineStyle_SOLID;
+
+ // XPoly is necessary for thick lines
+ if(!bNeed && eLine != drawing::LineStyle_NONE)
+ bNeed = rSet.Get(XATTR_LINEWIDTH).GetValue() != 0;
+
+ // XPoly is necessary for circle arcs with line ends
+ if(!bNeed && meCircleKind == SdrCircKind::Arc)
+ {
+ // start of the line is here if StartPolygon, StartWidth!=0
+ bNeed=rSet.Get(XATTR_LINESTART).GetLineStartValue().count() != 0 &&
+ rSet.Get(XATTR_LINESTARTWIDTH).GetValue() != 0;
+
+ if(!bNeed)
+ {
+ // end of the line is here if EndPolygon, EndWidth!=0
+ bNeed = rSet.Get(XATTR_LINEEND).GetLineEndValue().count() != 0 &&
+ rSet.Get(XATTR_LINEENDWIDTH).GetValue() != 0;
+ }
+ }
+ }
+
+ // XPoly is necessary if Fill !=None and !=Solid
+ if(!bNeed && meCircleKind != SdrCircKind::Arc)
+ {
+ drawing::FillStyle eFill=rSet.Get(XATTR_FILLSTYLE).GetValue();
+ bNeed = eFill != drawing::FillStyle_NONE && eFill != drawing::FillStyle_SOLID;
+ }
+
+ if(!bNeed && meCircleKind != SdrCircKind::Full && nStartAngle == nEndAngle)
+ bNeed = true; // otherwise we're drawing a full circle
+
+ return bNeed;
+}
+
+basegfx::B2DPolygon SdrCircObj::ImpCalcXPolyCirc(const SdrCircKind eCircleKind, const tools::Rectangle& rRect1, Degree100 nStart, Degree100 nEnd) const
+{
+ const basegfx::B2DRange aRange = vcl::unotools::b2DRectangleFromRectangle(rRect1);
+ basegfx::B2DPolygon aCircPolygon;
+
+ if(SdrCircKind::Full == eCircleKind)
+ {
+ // create full circle. Do not use createPolygonFromEllipse; it's necessary
+ // to get the start point to the bottom of the circle to keep compatible to
+ // old geometry creation
+ aCircPolygon = basegfx::utils::createPolygonFromUnitCircle(1);
+
+ // needs own scaling and translation from unit circle to target size (same as
+ // would be in createPolygonFromEllipse)
+ const basegfx::B2DPoint aCenter(aRange.getCenter());
+ const basegfx::B2DHomMatrix aMatrix(basegfx::utils::createScaleTranslateB2DHomMatrix(
+ aRange.getWidth() / 2.0, aRange.getHeight() / 2.0,
+ aCenter.getX(), aCenter.getY()));
+ aCircPolygon.transform(aMatrix);
+ }
+ else
+ {
+ // mirror start, end for geometry creation since model coordinate system is mirrored in Y
+ const double fStart(toRadians((36000_deg100 - nEnd) % 36000_deg100));
+ const double fEnd(toRadians((36000_deg100 - nStart) % 36000_deg100));
+
+ // create circle segment. This is not closed by default
+ aCircPolygon = basegfx::utils::createPolygonFromEllipseSegment(
+ aRange.getCenter(), aRange.getWidth() / 2.0, aRange.getHeight() / 2.0,
+ fStart, fEnd);
+
+ // check closing states
+ const bool bCloseSegment(SdrCircKind::Arc != eCircleKind);
+ const bool bCloseUsingCenter(SdrCircKind::Section == eCircleKind);
+
+ if(bCloseSegment)
+ {
+ if(bCloseUsingCenter)
+ {
+ // add center point at start (for historical reasons)
+ basegfx::B2DPolygon aSector;
+ aSector.append(aRange.getCenter());
+ aSector.append(aCircPolygon);
+ aCircPolygon = aSector;
+ }
+
+ // close
+ aCircPolygon.setClosed(true);
+ }
+ }
+
+ // #i76950#
+ if (maGeo.nShearAngle || maGeo.nRotationAngle)
+ {
+ // translate top left to (0,0)
+ const basegfx::B2DPoint aTopLeft(aRange.getMinimum());
+ basegfx::B2DHomMatrix aMatrix(basegfx::utils::createTranslateB2DHomMatrix(
+ -aTopLeft.getX(), -aTopLeft.getY()));
+
+ // shear, rotate and back to top left (if needed)
+ aMatrix = basegfx::utils::createShearXRotateTranslateB2DHomMatrix(
+ -maGeo.mfTanShearAngle,
+ maGeo.nRotationAngle ? toRadians(36000_deg100 - maGeo.nRotationAngle) : 0.0,
+ aTopLeft) * aMatrix;
+
+ // apply transformation
+ aCircPolygon.transform(aMatrix);
+ }
+
+ return aCircPolygon;
+}
+
+void SdrCircObj::RecalcXPoly()
+{
+ basegfx::B2DPolygon aPolyCirc(ImpCalcXPolyCirc(meCircleKind, maRect, nStartAngle, nEndAngle));
+ mpXPoly = XPolygon(aPolyCirc);
+}
+
+OUString SdrCircObj::TakeObjNameSingul() const
+{
+ TranslateId pID=STR_ObjNameSingulCIRC;
+ if (maRect.GetWidth() == maRect.GetHeight() && maGeo.nShearAngle==0_deg100)
+ {
+ switch (meCircleKind) {
+ case SdrCircKind::Full: pID=STR_ObjNameSingulCIRC; break;
+ case SdrCircKind::Section: pID=STR_ObjNameSingulSECT; break;
+ case SdrCircKind::Arc: pID=STR_ObjNameSingulCARC; break;
+ case SdrCircKind::Cut: pID=STR_ObjNameSingulCCUT; break;
+ default: break;
+ }
+ } else {
+ switch (meCircleKind) {
+ case SdrCircKind::Full: pID=STR_ObjNameSingulCIRCE; break;
+ case SdrCircKind::Section: pID=STR_ObjNameSingulSECTE; break;
+ case SdrCircKind::Arc: pID=STR_ObjNameSingulCARCE; break;
+ case SdrCircKind::Cut: pID=STR_ObjNameSingulCCUTE; break;
+ default: break;
+ }
+ }
+ OUString sName(SvxResId(pID));
+
+ OUString aName(GetName());
+ if (!aName.isEmpty())
+ sName += " '" + aName + "'";
+ return sName;
+}
+
+OUString SdrCircObj::TakeObjNamePlural() const
+{
+ TranslateId pID=STR_ObjNamePluralCIRC;
+ if (maRect.GetWidth() == maRect.GetHeight() && maGeo.nShearAngle==0_deg100)
+ {
+ switch (meCircleKind) {
+ case SdrCircKind::Full: pID=STR_ObjNamePluralCIRC; break;
+ case SdrCircKind::Section: pID=STR_ObjNamePluralSECT; break;
+ case SdrCircKind::Arc: pID=STR_ObjNamePluralCARC; break;
+ case SdrCircKind::Cut: pID=STR_ObjNamePluralCCUT; break;
+ default: break;
+ }
+ } else {
+ switch (meCircleKind) {
+ case SdrCircKind::Full: pID=STR_ObjNamePluralCIRCE; break;
+ case SdrCircKind::Section: pID=STR_ObjNamePluralSECTE; break;
+ case SdrCircKind::Arc: pID=STR_ObjNamePluralCARCE; break;
+ case SdrCircKind::Cut: pID=STR_ObjNamePluralCCUTE; break;
+ default: break;
+ }
+ }
+ return SvxResId(pID);
+}
+
+SdrCircObj* SdrCircObj::CloneSdrObject(SdrModel& rTargetModel) const
+{
+ return new SdrCircObj(rTargetModel, *this);
+}
+
+basegfx::B2DPolyPolygon SdrCircObj::TakeXorPoly() const
+{
+ const basegfx::B2DPolygon aCircPolygon(ImpCalcXPolyCirc(meCircleKind, maRect, nStartAngle, nEndAngle));
+ return basegfx::B2DPolyPolygon(aCircPolygon);
+}
+
+namespace {
+
+struct ImpCircUser : public SdrDragStatUserData
+{
+ tools::Rectangle aR;
+ Point aCenter;
+ Point aP1;
+ tools::Long nHgt;
+ tools::Long nWdt;
+ Degree100 nStart;
+ Degree100 nEnd;
+
+public:
+ ImpCircUser()
+ : nHgt(0),
+ nWdt(0),
+ nStart(0),
+ nEnd(0)
+ {}
+ void SetCreateParams(SdrDragStat const & rStat);
+};
+
+}
+
+sal_uInt32 SdrCircObj::GetHdlCount() const
+{
+ if(SdrCircKind::Full != meCircleKind)
+ {
+ return 10;
+ }
+ else
+ {
+ return 8;
+ }
+}
+
+void SdrCircObj::AddToHdlList(SdrHdlList& rHdlList) const
+{
+ for (sal_uInt32 nHdlNum=(SdrCircKind::Full==meCircleKind)?2:0; nHdlNum<=9; ++nHdlNum)
+ {
+ Point aPnt;
+ SdrHdlKind eLocalKind(SdrHdlKind::Move);
+ sal_uInt32 nPNum(0);
+
+ switch (nHdlNum)
+ {
+ case 0:
+ aPnt = GetAnglePnt(maRect,nStartAngle);
+ eLocalKind = SdrHdlKind::Circle;
+ nPNum = 1;
+ break;
+ case 1:
+ aPnt = GetAnglePnt(maRect,nEndAngle);
+ eLocalKind = SdrHdlKind::Circle;
+ nPNum = 2;
+ break;
+ case 2:
+ aPnt = maRect.TopLeft();
+ eLocalKind = SdrHdlKind::UpperLeft;
+ break;
+ case 3:
+ aPnt = maRect.TopCenter();
+ eLocalKind = SdrHdlKind::Upper;
+ break;
+ case 4:
+ aPnt = maRect.TopRight();
+ eLocalKind = SdrHdlKind::UpperRight;
+ break;
+ case 5:
+ aPnt = maRect.LeftCenter();
+ eLocalKind = SdrHdlKind::Left;
+ break;
+ case 6:
+ aPnt = maRect.RightCenter();
+ eLocalKind = SdrHdlKind::Right;
+ break;
+ case 7:
+ aPnt = maRect.BottomLeft();
+ eLocalKind = SdrHdlKind::LowerLeft;
+ break;
+ case 8:
+ aPnt = maRect.BottomCenter();
+ eLocalKind = SdrHdlKind::Lower;
+ break;
+ case 9:
+ aPnt = maRect.BottomRight();
+ eLocalKind = SdrHdlKind::LowerRight;
+ break;
+ }
+
+ if (maGeo.nShearAngle)
+ {
+ ShearPoint(aPnt, maRect.TopLeft(), maGeo.mfTanShearAngle);
+ }
+
+ if (maGeo.nRotationAngle)
+ {
+ RotatePoint(aPnt, maRect.TopLeft(), maGeo.mfSinRotationAngle, maGeo.mfCosRotationAngle);
+ }
+
+ std::unique_ptr<SdrHdl> pH(new SdrHdl(aPnt,eLocalKind));
+ pH->SetPointNum(nPNum);
+ pH->SetObj(const_cast<SdrCircObj*>(this));
+ pH->SetRotationAngle(maGeo.nRotationAngle);
+ rHdlList.AddHdl(std::move(pH));
+ }
+}
+
+
+bool SdrCircObj::hasSpecialDrag() const
+{
+ return true;
+}
+
+bool SdrCircObj::beginSpecialDrag(SdrDragStat& rDrag) const
+{
+ const bool bAngle(rDrag.GetHdl() && SdrHdlKind::Circle == rDrag.GetHdl()->GetKind());
+
+ if(bAngle)
+ {
+ if(1 == rDrag.GetHdl()->GetPointNum() || 2 == rDrag.GetHdl()->GetPointNum())
+ {
+ rDrag.SetNoSnap();
+ }
+
+ return true;
+ }
+
+ return SdrTextObj::beginSpecialDrag(rDrag);
+}
+
+bool SdrCircObj::applySpecialDrag(SdrDragStat& rDrag)
+{
+ const bool bAngle(rDrag.GetHdl() && SdrHdlKind::Circle == rDrag.GetHdl()->GetKind());
+
+ if(bAngle)
+ {
+ Point aPt(rDrag.GetNow());
+
+ if (maGeo.nRotationAngle)
+ RotatePoint(aPt,maRect.TopLeft(), -maGeo.mfSinRotationAngle, maGeo.mfCosRotationAngle);
+
+ if (maGeo.nShearAngle)
+ ShearPoint(aPt,maRect.TopLeft(), -maGeo.mfTanShearAngle);
+
+ aPt -= maRect.Center();
+
+ tools::Long nWdt = maRect.Right() - maRect.Left();
+ tools::Long nHgt = maRect.Bottom() - maRect.Top();
+
+ if(nWdt>=nHgt)
+ {
+ aPt.setY(BigMulDiv(aPt.Y(),nWdt,nHgt) );
+ }
+ else
+ {
+ aPt.setX(BigMulDiv(aPt.X(),nHgt,nWdt) );
+ }
+
+ Degree100 nAngle=NormAngle36000(GetAngle(aPt));
+
+ if (rDrag.GetView() && rDrag.GetView()->IsAngleSnapEnabled())
+ {
+ Degree100 nSA=rDrag.GetView()->GetSnapAngle();
+
+ if (nSA)
+ {
+ nAngle+=nSA/2_deg100;
+ nAngle/=nSA;
+ nAngle*=nSA;
+ nAngle=NormAngle36000(nAngle);
+ }
+ }
+
+ if(1 == rDrag.GetHdl()->GetPointNum())
+ {
+ nStartAngle = nAngle;
+ }
+ else if(2 == rDrag.GetHdl()->GetPointNum())
+ {
+ nEndAngle = nAngle;
+ }
+
+ SetBoundAndSnapRectsDirty();
+ SetXPolyDirty();
+ ImpSetCircInfoToAttr();
+ SetChanged();
+
+ return true;
+ }
+ else
+ {
+ return SdrTextObj::applySpecialDrag(rDrag);
+ }
+}
+
+OUString SdrCircObj::getSpecialDragComment(const SdrDragStat& rDrag) const
+{
+ const bool bCreateComment(rDrag.GetView() && this == rDrag.GetView()->GetCreateObj());
+
+ if(bCreateComment)
+ {
+ OUStringBuffer aBuf(ImpGetDescriptionStr(STR_ViewCreateObj));
+ const sal_uInt32 nPointCount(rDrag.GetPointCount());
+
+ if(SdrCircKind::Full != meCircleKind && nPointCount > 2)
+ {
+ const ImpCircUser* pU = static_cast<const ImpCircUser*>(rDrag.GetUser());
+ Degree100 nAngle;
+
+ aBuf.append(" (");
+
+ if(3 == nPointCount)
+ {
+ nAngle = pU->nStart;
+ }
+ else
+ {
+ nAngle = pU->nEnd;
+ }
+
+ aBuf.append(SdrModel::GetAngleString(nAngle));
+ aBuf.append(')');
+ }
+
+ return aBuf.makeStringAndClear();
+ }
+ else
+ {
+ const bool bAngle(rDrag.GetHdl() && SdrHdlKind::Circle == rDrag.GetHdl()->GetKind());
+
+ if(bAngle)
+ {
+ const Degree100 nAngle(1 == rDrag.GetHdl()->GetPointNum() ? nStartAngle : nEndAngle);
+
+ return ImpGetDescriptionStr(STR_DragCircAngle) +
+ " (" +
+ SdrModel::GetAngleString(nAngle) +
+ ")";
+ }
+ else
+ {
+ return SdrTextObj::getSpecialDragComment(rDrag);
+ }
+ }
+}
+
+
+void ImpCircUser::SetCreateParams(SdrDragStat const & rStat)
+{
+ rStat.TakeCreateRect(aR);
+ aR.Justify();
+ aCenter=aR.Center();
+ nWdt=aR.Right()-aR.Left();
+ nHgt=aR.Bottom()-aR.Top();
+ nStart=0_deg100;
+ nEnd=36000_deg100;
+ if (rStat.GetPointCount()>2) {
+ Point aP(rStat.GetPoint(2)-aCenter);
+ if (nWdt==0) aP.setX(0 );
+ if (nHgt==0) aP.setY(0 );
+ if (nWdt>=nHgt) {
+ if (nHgt!=0) aP.setY(aP.Y()*nWdt/nHgt );
+ } else {
+ if (nWdt!=0) aP.setX(aP.X()*nHgt/nWdt );
+ }
+ nStart=NormAngle36000(GetAngle(aP));
+ if (rStat.GetView()!=nullptr && rStat.GetView()->IsAngleSnapEnabled()) {
+ Degree100 nSA=rStat.GetView()->GetSnapAngle();
+ if (nSA) { // angle snapping
+ nStart+=nSA/2_deg100;
+ nStart/=nSA;
+ nStart*=nSA;
+ nStart=NormAngle36000(nStart);
+ }
+ }
+ aP1 = GetAnglePnt(aR,nStart);
+ nEnd=nStart;
+ } else aP1=aCenter;
+ if (rStat.GetPointCount()<=3)
+ return;
+
+ Point aP(rStat.GetPoint(3)-aCenter);
+ if (nWdt>=nHgt) {
+ aP.setY(BigMulDiv(aP.Y(),nWdt,nHgt) );
+ } else {
+ aP.setX(BigMulDiv(aP.X(),nHgt,nWdt) );
+ }
+ nEnd=NormAngle36000(GetAngle(aP));
+ if (rStat.GetView()!=nullptr && rStat.GetView()->IsAngleSnapEnabled()) {
+ Degree100 nSA=rStat.GetView()->GetSnapAngle();
+ if (nSA) { // angle snapping
+ nEnd+=nSA/2_deg100;
+ nEnd/=nSA;
+ nEnd*=nSA;
+ nEnd=NormAngle36000(nEnd);
+ }
+ }
+}
+
+void SdrCircObj::ImpSetCreateParams(SdrDragStat& rStat)
+{
+ ImpCircUser* pU=static_cast<ImpCircUser*>(rStat.GetUser());
+ if (pU==nullptr) {
+ pU=new ImpCircUser;
+ rStat.SetUser(std::unique_ptr<ImpCircUser>(pU));
+ }
+ pU->SetCreateParams(rStat);
+}
+
+bool SdrCircObj::BegCreate(SdrDragStat& rStat)
+{
+ rStat.SetOrtho4Possible();
+ tools::Rectangle aRect1(rStat.GetStart(), rStat.GetNow());
+ aRect1.Justify();
+ rStat.SetActionRect(aRect1);
+ maRect = aRect1;
+ ImpSetCreateParams(rStat);
+ return true;
+}
+
+bool SdrCircObj::MovCreate(SdrDragStat& rStat)
+{
+ ImpSetCreateParams(rStat);
+ ImpCircUser* pU=static_cast<ImpCircUser*>(rStat.GetUser());
+ rStat.SetActionRect(pU->aR);
+ maRect = pU->aR; // for ObjName
+ ImpJustifyRect(maRect);
+ nStartAngle=pU->nStart;
+ nEndAngle=pU->nEnd;
+ SetBoundRectDirty();
+ m_bSnapRectDirty=true;
+ SetXPolyDirty();
+
+ // #i103058# push current angle settings to ItemSet to
+ // allow FullDrag visualisation
+ if(rStat.GetPointCount() >= 4)
+ {
+ ImpSetCircInfoToAttr();
+ }
+
+ return true;
+}
+
+bool SdrCircObj::EndCreate(SdrDragStat& rStat, SdrCreateCmd eCmd)
+{
+ ImpSetCreateParams(rStat);
+ ImpCircUser* pU=static_cast<ImpCircUser*>(rStat.GetUser());
+ bool bRet = false;
+ if (eCmd==SdrCreateCmd::ForceEnd && rStat.GetPointCount()<4) meCircleKind=SdrCircKind::Full;
+ if (meCircleKind==SdrCircKind::Full) {
+ bRet=rStat.GetPointCount()>=2;
+ if (bRet) {
+ maRect = pU->aR;
+ ImpJustifyRect(maRect);
+ }
+ } else {
+ rStat.SetNoSnap(rStat.GetPointCount()>=2);
+ rStat.SetOrtho4Possible(rStat.GetPointCount()<2);
+ bRet=rStat.GetPointCount()>=4;
+ if (bRet) {
+ maRect = pU->aR;
+ ImpJustifyRect(maRect);
+ nStartAngle=pU->nStart;
+ nEndAngle=pU->nEnd;
+ }
+ }
+ m_bClosedObj=meCircleKind!=SdrCircKind::Arc;
+ SetBoundAndSnapRectsDirty();
+ SetXPolyDirty();
+ ImpSetCircInfoToAttr();
+ if (bRet)
+ rStat.SetUser(nullptr);
+ return bRet;
+}
+
+void SdrCircObj::BrkCreate(SdrDragStat& rStat)
+{
+ rStat.SetUser(nullptr);
+}
+
+bool SdrCircObj::BckCreate(SdrDragStat& rStat)
+{
+ rStat.SetNoSnap(rStat.GetPointCount()>=3);
+ rStat.SetOrtho4Possible(rStat.GetPointCount()<3);
+ return meCircleKind!=SdrCircKind::Full;
+}
+
+basegfx::B2DPolyPolygon SdrCircObj::TakeCreatePoly(const SdrDragStat& rDrag) const
+{
+ const ImpCircUser* pU = static_cast<const ImpCircUser*>(rDrag.GetUser());
+
+ if(rDrag.GetPointCount() < 4)
+ {
+ // force to OBJ_CIRC to get full visualisation
+ basegfx::B2DPolyPolygon aRetval(ImpCalcXPolyCirc(SdrCircKind::Full, pU->aR, pU->nStart, pU->nEnd));
+
+ if(3 == rDrag.GetPointCount())
+ {
+ // add edge to first point on ellipse
+ basegfx::B2DPolygon aNew;
+
+ aNew.append(basegfx::B2DPoint(pU->aCenter.X(), pU->aCenter.Y()));
+ aNew.append(basegfx::B2DPoint(pU->aP1.X(), pU->aP1.Y()));
+ aRetval.append(aNew);
+ }
+
+ return aRetval;
+ }
+ else
+ {
+ return basegfx::B2DPolyPolygon(ImpCalcXPolyCirc(meCircleKind, pU->aR, pU->nStart, pU->nEnd));
+ }
+}
+
+PointerStyle SdrCircObj::GetCreatePointer() const
+{
+ switch (meCircleKind) {
+ case SdrCircKind::Full: return PointerStyle::DrawEllipse;
+ case SdrCircKind::Section: return PointerStyle::DrawPie;
+ case SdrCircKind::Arc: return PointerStyle::DrawArc;
+ case SdrCircKind::Cut: return PointerStyle::DrawCircleCut;
+ default: break;
+ } // switch
+ return PointerStyle::Cross;
+}
+
+void SdrCircObj::NbcMove(const Size& aSiz)
+{
+ maRect.Move(aSiz);
+ m_aOutRect.Move(aSiz);
+ maSnapRect.Move(aSiz);
+ SetXPolyDirty();
+ SetBoundAndSnapRectsDirty(true);
+}
+
+void SdrCircObj::NbcResize(const Point& rRef, const Fraction& xFact, const Fraction& yFact)
+{
+ Degree100 nAngle0 = maGeo.nRotationAngle;
+ bool bNoShearRota = (maGeo.nRotationAngle == 0_deg100 && maGeo.nShearAngle == 0_deg100);
+ SdrTextObj::NbcResize(rRef,xFact,yFact);
+ bNoShearRota |= (maGeo.nRotationAngle == 0_deg100 && maGeo.nShearAngle == 0_deg100);
+ if (meCircleKind!=SdrCircKind::Full) {
+ bool bXMirr=(xFact.GetNumerator()<0) != (xFact.GetDenominator()<0);
+ bool bYMirr=(yFact.GetNumerator()<0) != (yFact.GetDenominator()<0);
+ if (bXMirr || bYMirr) {
+ // At bXMirr!=bYMirr we should actually swap both line ends.
+ // That, however, is pretty bad (because of forced "hard" formatting).
+ // Alternatively, we could implement a bMirrored flag (maybe even
+ // a more general one, e. g. for mirrored text, ...).
+ Degree100 nS0=nStartAngle;
+ Degree100 nE0=nEndAngle;
+ if (bNoShearRota) {
+ // the RectObj already mirrors at VMirror because of a 180deg rotation
+ if (! (bXMirr && bYMirr)) {
+ Degree100 nTmp=nS0;
+ nS0=18000_deg100-nE0;
+ nE0=18000_deg100-nTmp;
+ }
+ } else { // mirror contorted ellipses
+ if (bXMirr!=bYMirr) {
+ nS0+=nAngle0;
+ nE0+=nAngle0;
+ if (bXMirr) {
+ Degree100 nTmp=nS0;
+ nS0=18000_deg100-nE0;
+ nE0=18000_deg100-nTmp;
+ }
+ if (bYMirr) {
+ Degree100 nTmp=nS0;
+ nS0=-nE0;
+ nE0=-nTmp;
+ }
+ nS0 -= maGeo.nRotationAngle;
+ nE0 -= maGeo.nRotationAngle;
+ }
+ }
+ Degree100 nAngleDif=nE0-nS0;
+ nStartAngle=NormAngle36000(nS0);
+ nEndAngle =NormAngle36000(nE0);
+ if (nAngleDif==36000_deg100) nEndAngle+=nAngleDif; // full circle
+ }
+ }
+ SetXPolyDirty();
+ ImpSetCircInfoToAttr();
+}
+
+void SdrCircObj::NbcShear(const Point& rRef, Degree100 nAngle, double tn, bool bVShear)
+{
+ SdrTextObj::NbcShear(rRef,nAngle,tn,bVShear);
+ SetXPolyDirty();
+ ImpSetCircInfoToAttr();
+}
+
+void SdrCircObj::NbcMirror(const Point& rRef1, const Point& rRef2)
+{
+ bool bFreeMirr=meCircleKind!=SdrCircKind::Full;
+ Point aTmpPt1;
+ Point aTmpPt2;
+ if (bFreeMirr) { // some preparations for using an arbitrary axis of reflection
+ Point aCenter(maRect.Center());
+ tools::Long nWdt=maRect.GetWidth()-1;
+ tools::Long nHgt=maRect.GetHeight()-1;
+ tools::Long nMaxRad=(std::max(nWdt,nHgt)+1) /2;
+ // starting point
+ double a = toRadians(nStartAngle);
+ aTmpPt1=Point(FRound(cos(a)*nMaxRad),-FRound(sin(a)*nMaxRad));
+ if (nWdt==0) aTmpPt1.setX(0 );
+ if (nHgt==0) aTmpPt1.setY(0 );
+ aTmpPt1+=aCenter;
+ // finishing point
+ a = toRadians(nEndAngle);
+ aTmpPt2=Point(FRound(cos(a)*nMaxRad),-FRound(sin(a)*nMaxRad));
+ if (nWdt==0) aTmpPt2.setX(0 );
+ if (nHgt==0) aTmpPt2.setY(0 );
+ aTmpPt2+=aCenter;
+ if (maGeo.nRotationAngle)
+ {
+ RotatePoint(aTmpPt1, maRect.TopLeft(), maGeo.mfSinRotationAngle, maGeo.mfCosRotationAngle);
+ RotatePoint(aTmpPt2, maRect.TopLeft(), maGeo.mfSinRotationAngle, maGeo.mfCosRotationAngle);
+ }
+ if (maGeo.nShearAngle)
+ {
+ ShearPoint(aTmpPt1, maRect.TopLeft(), maGeo.mfTanShearAngle);
+ ShearPoint(aTmpPt2, maRect.TopLeft(), maGeo.mfTanShearAngle);
+ }
+ }
+ SdrTextObj::NbcMirror(rRef1,rRef2);
+ if (meCircleKind!=SdrCircKind::Full) { // adapt starting and finishing angle
+ MirrorPoint(aTmpPt1,rRef1,rRef2);
+ MirrorPoint(aTmpPt2,rRef1,rRef2);
+ // unrotate:
+ if (maGeo.nRotationAngle)
+ {
+ RotatePoint(aTmpPt1, maRect.TopLeft(), -maGeo.mfSinRotationAngle, maGeo.mfCosRotationAngle); // -sin for reversion
+ RotatePoint(aTmpPt2, maRect.TopLeft(), -maGeo.mfSinRotationAngle, maGeo.mfCosRotationAngle); // -sin for reversion
+ }
+ // unshear:
+ if (maGeo.nShearAngle)
+ {
+ ShearPoint(aTmpPt1, maRect.TopLeft(), -maGeo.mfTanShearAngle); // -tan for reversion
+ ShearPoint(aTmpPt2, maRect.TopLeft(), -maGeo.mfTanShearAngle); // -tan for reversion
+ }
+ Point aCenter(maRect.Center());
+ aTmpPt1-=aCenter;
+ aTmpPt2-=aCenter;
+ // because it's mirrored, the angles are swapped, too
+ nStartAngle=GetAngle(aTmpPt2);
+ nEndAngle =GetAngle(aTmpPt1);
+ Degree100 nAngleDif=nEndAngle-nStartAngle;
+ nStartAngle=NormAngle36000(nStartAngle);
+ nEndAngle =NormAngle36000(nEndAngle);
+ if (nAngleDif==36000_deg100) nEndAngle+=nAngleDif; // full circle
+ }
+ SetXPolyDirty();
+ ImpSetCircInfoToAttr();
+}
+
+std::unique_ptr<SdrObjGeoData> SdrCircObj::NewGeoData() const
+{
+ return std::make_unique<SdrCircObjGeoData>();
+}
+
+void SdrCircObj::SaveGeoData(SdrObjGeoData& rGeo) const
+{
+ SdrRectObj::SaveGeoData(rGeo);
+ SdrCircObjGeoData& rCGeo=static_cast<SdrCircObjGeoData&>(rGeo);
+ rCGeo.nStartAngle=nStartAngle;
+ rCGeo.nEndAngle =nEndAngle;
+}
+
+void SdrCircObj::RestoreGeoData(const SdrObjGeoData& rGeo)
+{
+ SdrRectObj::RestoreGeoData(rGeo);
+ const SdrCircObjGeoData& rCGeo=static_cast<const SdrCircObjGeoData&>(rGeo);
+ nStartAngle=rCGeo.nStartAngle;
+ nEndAngle =rCGeo.nEndAngle;
+ SetXPolyDirty();
+ ImpSetCircInfoToAttr();
+}
+
+static void Union(tools::Rectangle& rR, const Point& rP)
+{
+ if (rP.X()<rR.Left ()) rR.SetLeft(rP.X() );
+ if (rP.X()>rR.Right ()) rR.SetRight(rP.X() );
+ if (rP.Y()<rR.Top ()) rR.SetTop(rP.Y() );
+ if (rP.Y()>rR.Bottom()) rR.SetBottom(rP.Y() );
+}
+
+void SdrCircObj::TakeUnrotatedSnapRect(tools::Rectangle& rRect) const
+{
+ rRect = maRect;
+ if (meCircleKind!=SdrCircKind::Full) {
+ const Point aPntStart(GetAnglePnt(maRect,nStartAngle));
+ const Point aPntEnd(GetAnglePnt(maRect,nEndAngle));
+ Degree100 a=nStartAngle;
+ Degree100 e=nEndAngle;
+ rRect.SetLeft(maRect.Right() );
+ rRect.SetRight(maRect.Left() );
+ rRect.SetTop(maRect.Bottom() );
+ rRect.SetBottom(maRect.Top() );
+ Union(rRect,aPntStart);
+ Union(rRect,aPntEnd);
+ if ((a<=18000_deg100 && e>=18000_deg100) || (a>e && (a<=18000_deg100 || e>=18000_deg100))) {
+ Union(rRect,maRect.LeftCenter());
+ }
+ if ((a<=27000_deg100 && e>=27000_deg100) || (a>e && (a<=27000_deg100 || e>=27000_deg100))) {
+ Union(rRect,maRect.BottomCenter());
+ }
+ if (a>e) {
+ Union(rRect,maRect.RightCenter());
+ }
+ if ((a<=9000_deg100 && e>=9000_deg100) || (a>e && (a<=9000_deg100 || e>=9000_deg100))) {
+ Union(rRect,maRect.TopCenter());
+ }
+ if (meCircleKind==SdrCircKind::Section) {
+ Union(rRect,maRect.Center());
+ }
+ if (maGeo.nRotationAngle)
+ {
+ Point aDst(rRect.TopLeft());
+ aDst-=maRect.TopLeft();
+ Point aDst0(aDst);
+ RotatePoint(aDst,Point(), maGeo.mfSinRotationAngle, maGeo.mfCosRotationAngle);
+ aDst-=aDst0;
+ rRect.Move(aDst.X(),aDst.Y());
+ }
+ }
+ if (maGeo.nShearAngle==0_deg100)
+ return;
+
+ tools::Long nDst = FRound((rRect.Bottom() - rRect.Top()) * maGeo.mfTanShearAngle);
+ if (maGeo.nShearAngle > 0_deg100)
+ {
+ Point aRef(rRect.TopLeft());
+ rRect.AdjustLeft( -nDst );
+ Point aTmpPt(rRect.TopLeft());
+ RotatePoint(aTmpPt, aRef, maGeo.mfSinRotationAngle, maGeo.mfCosRotationAngle);
+ aTmpPt-=rRect.TopLeft();
+ rRect.Move(aTmpPt.X(),aTmpPt.Y());
+ } else {
+ rRect.AdjustRight( -nDst );
+ }
+}
+
+void SdrCircObj::RecalcSnapRect()
+{
+ if (PaintNeedsXPolyCirc()) {
+ maSnapRect=GetXPoly().GetBoundRect();
+ } else {
+ TakeUnrotatedSnapRect(maSnapRect);
+ }
+}
+
+void SdrCircObj::NbcSetSnapRect(const tools::Rectangle& rRect)
+{
+ if (maGeo.nRotationAngle || maGeo.nShearAngle || meCircleKind != SdrCircKind::Full)
+ {
+ tools::Rectangle aSR0(GetSnapRect());
+ tools::Long nWdt0=aSR0.Right()-aSR0.Left();
+ tools::Long nHgt0=aSR0.Bottom()-aSR0.Top();
+ tools::Long nWdt1=rRect.Right()-rRect.Left();
+ tools::Long nHgt1=rRect.Bottom()-rRect.Top();
+ NbcResize(maSnapRect.TopLeft(),Fraction(nWdt1,nWdt0),Fraction(nHgt1,nHgt0));
+ NbcMove(Size(rRect.Left()-aSR0.Left(),rRect.Top()-aSR0.Top()));
+ } else {
+ maRect=rRect;
+ ImpJustifyRect(maRect);
+ }
+ SetBoundAndSnapRectsDirty();
+ SetXPolyDirty();
+ ImpSetCircInfoToAttr();
+}
+
+sal_uInt32 SdrCircObj::GetSnapPointCount() const
+{
+ if (meCircleKind==SdrCircKind::Full) {
+ return 1;
+ } else {
+ return 3;
+ }
+}
+
+Point SdrCircObj::GetSnapPoint(sal_uInt32 i) const
+{
+ switch (i) {
+ case 1 : return GetAnglePnt(maRect,nStartAngle);
+ case 2 : return GetAnglePnt(maRect,nEndAngle);
+ default: return maRect.Center();
+ }
+}
+
+void SdrCircObj::Notify(SfxBroadcaster& rBC, const SfxHint& rHint)
+{
+ SetXPolyDirty();
+ SdrRectObj::Notify(rBC,rHint);
+ ImpSetAttrToCircInfo();
+}
+
+
+void SdrCircObj::ImpSetAttrToCircInfo()
+{
+ const SfxItemSet& rSet = GetObjectItemSet();
+ SdrCircKind eNewKind = rSet.Get(SDRATTR_CIRCKIND).GetValue();
+
+ Degree100 nNewStart = rSet.Get(SDRATTR_CIRCSTARTANGLE).GetValue();
+ Degree100 nNewEnd = rSet.Get(SDRATTR_CIRCENDANGLE).GetValue();
+
+ bool bKindChg = meCircleKind != eNewKind;
+ bool bAngleChg = nNewStart != nStartAngle || nNewEnd != nEndAngle;
+
+ if(bKindChg || bAngleChg)
+ {
+ meCircleKind = eNewKind;
+ nStartAngle = nNewStart;
+ nEndAngle = nNewEnd;
+
+ if(bKindChg || (meCircleKind != SdrCircKind::Full && bAngleChg))
+ {
+ SetXPolyDirty();
+ SetBoundAndSnapRectsDirty();
+ }
+ }
+}
+
+void SdrCircObj::ImpSetCircInfoToAttr()
+{
+ const SfxItemSet& rSet = GetObjectItemSet();
+
+ SdrCircKind eOldKindA = rSet.Get(SDRATTR_CIRCKIND).GetValue();
+ Degree100 nOldStartAngle = rSet.Get(SDRATTR_CIRCSTARTANGLE).GetValue();
+ Degree100 nOldEndAngle = rSet.Get(SDRATTR_CIRCENDANGLE).GetValue();
+
+ if(meCircleKind == eOldKindA && nStartAngle == nOldStartAngle && nEndAngle == nOldEndAngle)
+ return;
+
+ // since SetItem() implicitly calls ImpSetAttrToCircInfo()
+ // setting the item directly is necessary here.
+ if(meCircleKind != eOldKindA)
+ {
+ GetProperties().SetObjectItemDirect(SdrCircKindItem(meCircleKind));
+ }
+
+ if(nStartAngle != nOldStartAngle)
+ {
+ GetProperties().SetObjectItemDirect(makeSdrCircStartAngleItem(nStartAngle));
+ }
+
+ if(nEndAngle != nOldEndAngle)
+ {
+ GetProperties().SetObjectItemDirect(makeSdrCircEndAngleItem(nEndAngle));
+ }
+
+ SetXPolyDirty();
+ ImpSetAttrToCircInfo();
+}
+
+SdrObjectUniquePtr SdrCircObj::DoConvertToPolyObj(bool bBezier, bool bAddText) const
+{
+ const bool bFill(meCircleKind != SdrCircKind::Arc);
+ const basegfx::B2DPolygon aCircPolygon(ImpCalcXPolyCirc(meCircleKind, maRect, nStartAngle, nEndAngle));
+ SdrObjectUniquePtr pRet = ImpConvertMakeObj(basegfx::B2DPolyPolygon(aCircPolygon), bFill, bBezier);
+
+ if(bAddText)
+ {
+ pRet = ImpConvertAddText(std::move(pRet), bBezier);
+ }
+
+ return pRet;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/svdraw/svdoedge.cxx b/svx/source/svdraw/svdoedge.cxx
new file mode 100644
index 000000000..769a720c3
--- /dev/null
+++ b/svx/source/svdraw/svdoedge.cxx
@@ -0,0 +1,2647 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * 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/dialmgr.hxx>
+#include <svx/strings.hrc>
+#include <osl/diagnose.h>
+
+#include <basegfx/matrix/b2dhommatrix.hxx>
+#include <basegfx/polygon/b2dpolygon.hxx>
+#include <svl/hint.hxx>
+
+#include <sdr/contact/viewcontactofsdredgeobj.hxx>
+#include <sdr/properties/connectorproperties.hxx>
+#include <svx/sdrhittesthelper.hxx>
+#include <svx/svddrag.hxx>
+#include <svx/svddrgmt.hxx>
+#include <svx/svdhdl.hxx>
+#include <svx/svdmodel.hxx>
+#include <svx/svdoedge.hxx>
+#include <svx/svdopath.hxx>
+#include <svx/svdpage.hxx>
+#include <svx/svdpagv.hxx>
+#include <svx/svdtrans.hxx>
+#include <svx/svdview.hxx>
+#include <svx/sxekitm.hxx>
+#include <svx/sxelditm.hxx>
+#include <svx/sxenditm.hxx>
+#include <svx/xpoly.hxx>
+#include <vcl/ptrstyle.hxx>
+#include <comphelper/lok.hxx>
+
+void SdrObjConnection::ResetVars()
+{
+ pObj=nullptr;
+ nConId=0;
+ bBestConn=true;
+ bBestVertex=true;
+ bAutoVertex=false;
+ bAutoCorner=false;
+}
+
+bool SdrObjConnection::TakeGluePoint(SdrGluePoint& rGP) const
+{
+ bool bRet = false;
+ if (pObj!=nullptr) { // one object has to be docked already!
+ if (bAutoVertex) {
+ rGP=pObj->GetVertexGluePoint(nConId);
+ bRet = true;
+ } else if (bAutoCorner) {
+ rGP=pObj->GetCornerGluePoint(nConId);
+ bRet = true;
+ } else {
+ const SdrGluePointList* pGPL=pObj->GetGluePointList();
+ if (pGPL!=nullptr) {
+ sal_uInt16 nNum=pGPL->FindGluePoint(nConId);
+ if (nNum!=SDRGLUEPOINT_NOTFOUND) {
+ rGP=(*pGPL)[nNum];
+ bRet = true;
+ }
+ }
+ }
+ }
+ if (bRet) {
+ Point aPt(rGP.GetAbsolutePos(*pObj));
+ aPt+=aObjOfs;
+ rGP.SetPos(aPt);
+ }
+ return bRet;
+}
+
+Point& SdrEdgeInfoRec::ImpGetLineOffsetPoint(SdrEdgeLineCode eLineCode)
+{
+ switch (eLineCode) {
+ case SdrEdgeLineCode::Obj1Line2 : return aObj1Line2;
+ case SdrEdgeLineCode::Obj1Line3 : return aObj1Line3;
+ case SdrEdgeLineCode::Obj2Line2 : return aObj2Line2;
+ case SdrEdgeLineCode::Obj2Line3 : return aObj2Line3;
+ case SdrEdgeLineCode::MiddleLine: return aMiddleLine;
+ } // switch
+ return aMiddleLine;
+}
+
+sal_uInt16 SdrEdgeInfoRec::ImpGetPolyIdx(SdrEdgeLineCode eLineCode, const XPolygon& rXP) const
+{
+ switch (eLineCode) {
+ case SdrEdgeLineCode::Obj1Line2 : return 1;
+ case SdrEdgeLineCode::Obj1Line3 : return 2;
+ case SdrEdgeLineCode::Obj2Line2 : return rXP.GetPointCount()-3;
+ case SdrEdgeLineCode::Obj2Line3 : return rXP.GetPointCount()-4;
+ case SdrEdgeLineCode::MiddleLine: return nMiddleLine;
+ } // switch
+ return 0;
+}
+
+bool SdrEdgeInfoRec::ImpIsHorzLine(SdrEdgeLineCode eLineCode, const XPolygon& rXP) const
+{
+ sal_uInt16 nIdx=ImpGetPolyIdx(eLineCode,rXP);
+ bool bHorz=nAngle1==0 || nAngle1==18000;
+ if (eLineCode==SdrEdgeLineCode::Obj2Line2 || eLineCode==SdrEdgeLineCode::Obj2Line3) {
+ nIdx=rXP.GetPointCount()-nIdx;
+ bHorz=nAngle2==0 || nAngle2==18000;
+ }
+ if ((nIdx & 1)==1) bHorz=!bHorz;
+ return bHorz;
+}
+
+void SdrEdgeInfoRec::ImpSetLineOffset(SdrEdgeLineCode eLineCode, const XPolygon& rXP, tools::Long nVal)
+{
+ Point& rPt=ImpGetLineOffsetPoint(eLineCode);
+ if (ImpIsHorzLine(eLineCode,rXP)) rPt.setY(nVal );
+ else rPt.setX(nVal );
+}
+
+tools::Long SdrEdgeInfoRec::ImpGetLineOffset(SdrEdgeLineCode eLineCode, const XPolygon& rXP) const
+{
+ const Point& rPt = const_cast<SdrEdgeInfoRec*>(this)->ImpGetLineOffsetPoint(eLineCode);
+ if (ImpIsHorzLine(eLineCode,rXP))
+ return rPt.Y();
+ else
+ return rPt.X();
+}
+
+
+// BaseProperties section
+
+std::unique_ptr<sdr::properties::BaseProperties> SdrEdgeObj::CreateObjectSpecificProperties()
+{
+ return std::make_unique<sdr::properties::ConnectorProperties>(*this);
+}
+
+
+// DrawContact section
+
+std::unique_ptr<sdr::contact::ViewContact> SdrEdgeObj::CreateObjectSpecificViewContact()
+{
+ return std::make_unique<sdr::contact::ViewContactOfSdrEdgeObj>(*this);
+}
+
+
+SdrEdgeObj::SdrEdgeObj(SdrModel& rSdrModel)
+: SdrTextObj(rSdrModel),
+ nNotifyingCount(0),
+ bEdgeTrackDirty(false),
+ bEdgeTrackUserDefined(false),
+ // Default is to allow default connects
+ mbSuppressDefaultConnect(false),
+ mbBoundRectCalculationRunning(false),
+ mbSuppressed(false)
+{
+ m_bClosedObj=false;
+ m_bIsEdge=true;
+ pEdgeTrack = XPolygon();
+}
+
+SdrEdgeObj::SdrEdgeObj(SdrModel& rSdrModel, SdrEdgeObj const & rSource)
+: SdrTextObj(rSdrModel, rSource),
+ nNotifyingCount(0),
+ bEdgeTrackDirty(false),
+ bEdgeTrackUserDefined(false),
+ // Default is to allow default connects
+ mbSuppressDefaultConnect(false),
+ mbBoundRectCalculationRunning(false),
+ mbSuppressed(false)
+{
+ m_bClosedObj = false;
+ m_bIsEdge = true;
+ pEdgeTrack = rSource.pEdgeTrack;
+ bEdgeTrackDirty=rSource.bEdgeTrackDirty;
+ aCon1 =rSource.aCon1;
+ aCon2 =rSource.aCon2;
+ aCon1.pObj=nullptr;
+ aCon2.pObj=nullptr;
+ aEdgeInfo=rSource.aEdgeInfo;
+}
+
+SdrEdgeObj::~SdrEdgeObj()
+{
+ SdrEdgeObj::DisconnectFromNode(true);
+ SdrEdgeObj::DisconnectFromNode(false);
+}
+
+void SdrEdgeObj::handlePageChange(SdrPage* pOldPage, SdrPage* pNewPage)
+{
+ // call parent
+ SdrTextObj::handlePageChange(pOldPage, pNewPage);
+
+ if(nullptr != GetConnection(true).GetObject() || nullptr != GetConnection(false).GetObject())
+ {
+ // check broadcasters; when we are not inserted we do not need broadcasters
+ // TTTT not yet added, but keep hint to do this here
+ // mpCon1->ownerPageChange();
+ // mpCon2->ownerPageChange();
+ }
+}
+
+void SdrEdgeObj::ImpSetAttrToEdgeInfo()
+{
+ const SfxItemSet& rSet = GetObjectItemSet();
+ SdrEdgeKind eKind = rSet.Get(SDRATTR_EDGEKIND).GetValue();
+ sal_Int32 nVal1 = rSet.Get(SDRATTR_EDGELINE1DELTA).GetValue();
+ sal_Int32 nVal2 = rSet.Get(SDRATTR_EDGELINE2DELTA).GetValue();
+ sal_Int32 nVal3 = rSet.Get(SDRATTR_EDGELINE3DELTA).GetValue();
+
+ if(eKind == SdrEdgeKind::OrthoLines || eKind == SdrEdgeKind::Bezier)
+ {
+ sal_Int32 nVals[3] = { nVal1, nVal2, nVal3 };
+ sal_uInt16 n = 0;
+
+ if(aEdgeInfo.nObj1Lines >= 2 && n < 3)
+ {
+ aEdgeInfo.ImpSetLineOffset(SdrEdgeLineCode::Obj1Line2, *pEdgeTrack, nVals[n]);
+ n++;
+ }
+
+ if(aEdgeInfo.nObj1Lines >= 3 && n < 3)
+ {
+ aEdgeInfo.ImpSetLineOffset(SdrEdgeLineCode::Obj1Line3, *pEdgeTrack, nVals[n]);
+ n++;
+ }
+
+ if(aEdgeInfo.nMiddleLine != 0xFFFF && n < 3)
+ {
+ aEdgeInfo.ImpSetLineOffset(SdrEdgeLineCode::MiddleLine, *pEdgeTrack, nVals[n]);
+ n++;
+ }
+
+ if(aEdgeInfo.nObj2Lines >= 3 && n < 3)
+ {
+ aEdgeInfo.ImpSetLineOffset(SdrEdgeLineCode::Obj2Line3, *pEdgeTrack, nVals[n]);
+ n++;
+ }
+
+ if(aEdgeInfo.nObj2Lines >= 2 && n < 3)
+ {
+ aEdgeInfo.ImpSetLineOffset(SdrEdgeLineCode::Obj2Line2, *pEdgeTrack, nVals[n]);
+ n++;
+ }
+ }
+ else if(eKind == SdrEdgeKind::ThreeLines)
+ {
+ bool bHor1 = aEdgeInfo.nAngle1 == 0 || aEdgeInfo.nAngle1 == 18000;
+ bool bHor2 = aEdgeInfo.nAngle2 == 0 || aEdgeInfo.nAngle2 == 18000;
+
+ if(bHor1)
+ {
+ aEdgeInfo.aObj1Line2.setX( nVal1 );
+ }
+ else
+ {
+ aEdgeInfo.aObj1Line2.setY( nVal1 );
+ }
+
+ if(bHor2)
+ {
+ aEdgeInfo.aObj2Line2.setX( nVal2 );
+ }
+ else
+ {
+ aEdgeInfo.aObj2Line2.setY( nVal2 );
+ }
+ }
+
+ ImpDirtyEdgeTrack();
+}
+
+void SdrEdgeObj::ImpSetEdgeInfoToAttr()
+{
+ const SfxItemSet& rSet = GetObjectItemSet();
+ SdrEdgeKind eKind = rSet.Get(SDRATTR_EDGEKIND).GetValue();
+ sal_Int32 nValCnt = rSet.Get(SDRATTR_EDGELINEDELTACOUNT).GetValue();
+ sal_Int32 nVal1 = rSet.Get(SDRATTR_EDGELINE1DELTA).GetValue();
+ sal_Int32 nVal2 = rSet.Get(SDRATTR_EDGELINE2DELTA).GetValue();
+ sal_Int32 nVal3 = rSet.Get(SDRATTR_EDGELINE3DELTA).GetValue();
+ sal_Int32 nVals[3] = { nVal1, nVal2, nVal3 };
+ sal_uInt16 n = 0;
+
+ if(eKind == SdrEdgeKind::OrthoLines || eKind == SdrEdgeKind::Bezier)
+ {
+ if(aEdgeInfo.nObj1Lines >= 2 && n < 3)
+ {
+ nVals[n] = aEdgeInfo.ImpGetLineOffset(SdrEdgeLineCode::Obj1Line2, *pEdgeTrack);
+ n++;
+ }
+
+ if(aEdgeInfo.nObj1Lines >= 3 && n < 3)
+ {
+ nVals[n] = aEdgeInfo.ImpGetLineOffset(SdrEdgeLineCode::Obj1Line3, *pEdgeTrack);
+ n++;
+ }
+
+ if(aEdgeInfo.nMiddleLine != 0xFFFF && n < 3)
+ {
+ nVals[n] = aEdgeInfo.ImpGetLineOffset(SdrEdgeLineCode::MiddleLine, *pEdgeTrack);
+ n++;
+ }
+
+ if(aEdgeInfo.nObj2Lines >= 3 && n < 3)
+ {
+ nVals[n] = aEdgeInfo.ImpGetLineOffset(SdrEdgeLineCode::Obj2Line3, *pEdgeTrack);
+ n++;
+ }
+
+ if(aEdgeInfo.nObj2Lines >= 2 && n < 3)
+ {
+ nVals[n] = aEdgeInfo.ImpGetLineOffset(SdrEdgeLineCode::Obj2Line2, *pEdgeTrack);
+ n++;
+ }
+ }
+ else if(eKind == SdrEdgeKind::ThreeLines)
+ {
+ bool bHor1 = aEdgeInfo.nAngle1 == 0 || aEdgeInfo.nAngle1 == 18000;
+ bool bHor2 = aEdgeInfo.nAngle2 == 0 || aEdgeInfo.nAngle2 == 18000;
+
+ n = 2;
+ nVals[0] = bHor1 ? aEdgeInfo.aObj1Line2.X() : aEdgeInfo.aObj1Line2.Y();
+ nVals[1] = bHor2 ? aEdgeInfo.aObj2Line2.X() : aEdgeInfo.aObj2Line2.Y();
+ }
+
+ if(!(n != nValCnt || nVals[0] != nVal1 || nVals[1] != nVal2 || nVals[2] != nVal3))
+ return;
+
+ // Here no more notifying is necessary, just local changes are OK.
+ if(n != nValCnt)
+ {
+ GetProperties().SetObjectItemDirect(SdrEdgeLineDeltaCountItem(n));
+ }
+
+ if(nVals[0] != nVal1)
+ {
+ GetProperties().SetObjectItemDirect(makeSdrEdgeLine1DeltaItem(nVals[0]));
+ }
+
+ if(nVals[1] != nVal2)
+ {
+ GetProperties().SetObjectItemDirect(makeSdrEdgeLine2DeltaItem(nVals[1]));
+ }
+
+ if(nVals[2] != nVal3)
+ {
+ GetProperties().SetObjectItemDirect(makeSdrEdgeLine3DeltaItem(nVals[2]));
+ }
+
+ if(n < 3)
+ {
+ GetProperties().ClearObjectItemDirect(SDRATTR_EDGELINE3DELTA);
+ }
+
+ if(n < 2)
+ {
+ GetProperties().ClearObjectItemDirect(SDRATTR_EDGELINE2DELTA);
+ }
+
+ if(n < 1)
+ {
+ GetProperties().ClearObjectItemDirect(SDRATTR_EDGELINE1DELTA);
+ }
+}
+
+void SdrEdgeObj::TakeObjInfo(SdrObjTransformInfoRec& rInfo) const
+{
+ // #i54102# allow rotation, mirror and shear
+ rInfo.bRotateFreeAllowed = true;
+ rInfo.bRotate90Allowed = true;
+ rInfo.bMirrorFreeAllowed = true;
+ rInfo.bMirror45Allowed = true;
+ rInfo.bMirror90Allowed = true;
+ rInfo.bTransparenceAllowed = false;
+ rInfo.bShearAllowed = true;
+ rInfo.bEdgeRadiusAllowed = false;
+ bool bCanConv=!HasText() || ImpCanConvTextToCurve();
+ rInfo.bCanConvToPath=bCanConv;
+ rInfo.bCanConvToPoly=bCanConv;
+ rInfo.bCanConvToContour = (rInfo.bCanConvToPoly || LineGeometryUsageIsNecessary());
+}
+
+SdrObjKind SdrEdgeObj::GetObjIdentifier() const
+{
+ return SdrObjKind::Edge;
+}
+
+const tools::Rectangle& SdrEdgeObj::GetCurrentBoundRect() const
+{
+ if(bEdgeTrackDirty)
+ {
+ const_cast<SdrEdgeObj*>(this)->ImpRecalcEdgeTrack();
+ }
+
+ return SdrTextObj::GetCurrentBoundRect();
+}
+
+const tools::Rectangle& SdrEdgeObj::GetSnapRect() const
+{
+ if(bEdgeTrackDirty)
+ {
+ const_cast<SdrEdgeObj*>(this)->ImpRecalcEdgeTrack();
+ }
+
+ return SdrTextObj::GetSnapRect();
+}
+
+void SdrEdgeObj::RecalcSnapRect()
+{
+ maSnapRect=pEdgeTrack->GetBoundRect();
+}
+
+void SdrEdgeObj::TakeUnrotatedSnapRect(tools::Rectangle& rRect) const
+{
+ rRect=GetSnapRect();
+}
+
+SdrGluePoint SdrEdgeObj::GetVertexGluePoint(sal_uInt16 nNum) const
+{
+ Point aPt;
+ sal_uInt16 nPointCount=pEdgeTrack->GetPointCount();
+ if (nPointCount>0)
+ {
+ Point aOfs = GetSnapRect().Center();
+ if (nNum==2 && GetConnectedNode(true)==nullptr) aPt=(*pEdgeTrack)[0];
+ else if (nNum==3 && GetConnectedNode(false)==nullptr) aPt=(*pEdgeTrack)[nPointCount-1];
+ else {
+ if ((nPointCount & 1) ==1) {
+ aPt=(*pEdgeTrack)[nPointCount/2];
+ } else {
+ Point aPt1((*pEdgeTrack)[nPointCount/2-1]);
+ Point aPt2((*pEdgeTrack)[nPointCount/2]);
+ aPt1+=aPt2;
+ aPt1.setX( aPt1.X() / 2 );
+ aPt1.setY( aPt1.Y() / 2 );
+ aPt=aPt1;
+ }
+ }
+ aPt-=aOfs;
+ }
+ SdrGluePoint aGP(aPt);
+ aGP.SetPercent(false);
+ return aGP;
+}
+
+SdrGluePoint SdrEdgeObj::GetCornerGluePoint(sal_uInt16 nNum) const
+{
+ return GetVertexGluePoint(nNum);
+}
+
+const SdrGluePointList* SdrEdgeObj::GetGluePointList() const
+{
+ return nullptr; // no user defined gluepoints for connectors
+}
+
+SdrGluePointList* SdrEdgeObj::ForceGluePointList()
+{
+ return nullptr; // no user defined gluepoints for connectors
+}
+
+void SdrEdgeObj::ConnectToNode(bool bTail1, SdrObject* pObj)
+{
+ SdrObjConnection& rCon=GetConnection(bTail1);
+ DisconnectFromNode(bTail1);
+ if (pObj!=nullptr) {
+ pObj->AddListener(*this);
+ rCon.pObj=pObj;
+
+ // #i120437# If connection is set, reset bEdgeTrackUserDefined
+ bEdgeTrackUserDefined = false;
+
+ ImpDirtyEdgeTrack();
+ }
+}
+
+void SdrEdgeObj::DisconnectFromNode(bool bTail1)
+{
+ SdrObjConnection& rCon=GetConnection(bTail1);
+ if (rCon.pObj!=nullptr) {
+ rCon.pObj->RemoveListener(*this);
+ rCon.pObj=nullptr;
+ }
+}
+
+SdrObject* SdrEdgeObj::GetConnectedNode(bool bTail1) const
+{
+ SdrObject* pObj(GetConnection(bTail1).pObj);
+
+ if(nullptr != pObj
+ && (pObj->getSdrPageFromSdrObject() != getSdrPageFromSdrObject() || !pObj->IsInserted()))
+ {
+ pObj = nullptr;
+ }
+
+ return pObj;
+}
+
+bool SdrEdgeObj::CheckNodeConnection(bool bTail1) const
+{
+ bool bRet = false;
+ const SdrObjConnection& rCon=GetConnection(bTail1);
+ sal_uInt16 nPointCount=pEdgeTrack->GetPointCount();
+
+ if(nullptr != rCon.pObj && rCon.pObj->getSdrPageFromSdrObject() == getSdrPageFromSdrObject() && 0 != nPointCount)
+ {
+ const SdrGluePointList* pGPL=rCon.pObj->GetGluePointList();
+ sal_uInt16 nGluePointCnt=pGPL==nullptr ? 0 : pGPL->GetCount();
+ sal_uInt16 nGesAnz=nGluePointCnt+8;
+ Point aTail(bTail1 ? (*pEdgeTrack)[0] : (*pEdgeTrack)[sal_uInt16(nPointCount-1)]);
+ for (sal_uInt16 i=0; i<nGesAnz && !bRet; i++) {
+ if (i<nGluePointCnt) { // UserDefined
+ bRet=aTail==(*pGPL)[i].GetAbsolutePos(*rCon.pObj);
+ } else if (i<nGluePointCnt+4) { // Vertex
+ SdrGluePoint aPt(rCon.pObj->GetVertexGluePoint(i-nGluePointCnt));
+ bRet=aTail==aPt.GetAbsolutePos(*rCon.pObj);
+ } else { // Corner
+ SdrGluePoint aPt(rCon.pObj->GetCornerGluePoint(i-nGluePointCnt-4));
+ bRet=aTail==aPt.GetAbsolutePos(*rCon.pObj);
+ }
+ }
+ }
+ return bRet;
+}
+
+void SdrEdgeObj::ImpSetTailPoint(bool bTail1, const Point& rPt)
+{
+ sal_uInt16 nPointCount=pEdgeTrack->GetPointCount();
+ if (nPointCount==0) {
+ (*pEdgeTrack)[0]=rPt;
+ (*pEdgeTrack)[1]=rPt;
+ } else if (nPointCount==1) {
+ if (!bTail1) (*pEdgeTrack)[1]=rPt;
+ else { (*pEdgeTrack)[1]=(*pEdgeTrack)[0]; (*pEdgeTrack)[0]=rPt; }
+ } else {
+ if (!bTail1) (*pEdgeTrack)[sal_uInt16(nPointCount-1)]=rPt;
+ else (*pEdgeTrack)[0]=rPt;
+ }
+ ImpRecalcEdgeTrack();
+ SetBoundAndSnapRectsDirty();
+}
+
+void SdrEdgeObj::ImpDirtyEdgeTrack()
+{
+ if ( !bEdgeTrackUserDefined || !getSdrModelFromSdrObject().isLocked() )
+ bEdgeTrackDirty = true;
+}
+
+void SdrEdgeObj::ImpUndirtyEdgeTrack()
+{
+ if (bEdgeTrackDirty && getSdrModelFromSdrObject().isLocked())
+ {
+ ImpRecalcEdgeTrack();
+ }
+}
+
+void SdrEdgeObj::ImpRecalcEdgeTrack()
+{
+ // #i120437# if bEdgeTrackUserDefined, do not recalculate
+ if(bEdgeTrackUserDefined)
+ {
+ return;
+ }
+
+ // #i120437# also not when model locked during import, but remember
+ if(getSdrModelFromSdrObject().isLocked())
+ {
+ mbSuppressed = true;
+ return;
+ }
+
+ // #i110649#
+ if(mbBoundRectCalculationRunning)
+ {
+ // This object is involved into another ImpRecalcEdgeTrack() call
+ // from another SdrEdgeObj. Do not calculate again to avoid loop.
+ // Also, do not change bEdgeTrackDirty so that it gets recalculated
+ // later at the first non-looping call.
+ }
+ else
+ {
+ // To not run in a depth loop, use a coloring algorithm on
+ // SdrEdgeObj BoundRect calculations
+ mbBoundRectCalculationRunning = true;
+
+ if(mbSuppressed)
+ {
+ // #i123048# If layouting was ever suppressed, it needs to be done once
+ // and the attr need to be set at EdgeInfo, else these attr *will be lost*
+ // in the following call to ImpSetEdgeInfoToAttr() since they were never
+ // set before (!)
+ *pEdgeTrack=ImpCalcEdgeTrack(*pEdgeTrack,aCon1,aCon2,&aEdgeInfo);
+ ImpSetAttrToEdgeInfo();
+ mbSuppressed = false;
+ }
+
+ tools::Rectangle aBoundRect0; if (m_pUserCall!=nullptr) aBoundRect0=GetCurrentBoundRect();
+ SetBoundAndSnapRectsDirty();
+ *pEdgeTrack=ImpCalcEdgeTrack(*pEdgeTrack,aCon1,aCon2,&aEdgeInfo);
+ ImpSetEdgeInfoToAttr(); // copy values from aEdgeInfo into the pool
+ bEdgeTrackDirty=false;
+
+ // Only redraw here, no object change
+ ActionChanged();
+
+ SendUserCall(SdrUserCallType::Resize,aBoundRect0);
+
+ mbBoundRectCalculationRunning = false;
+ }
+}
+
+SdrEscapeDirection SdrEdgeObj::ImpCalcEscAngle(SdrObject const * pObj, const Point& rPt)
+{
+ if (pObj==nullptr) return SdrEscapeDirection::ALL;
+ tools::Rectangle aR(pObj->GetSnapRect());
+ tools::Long dxl=rPt.X()-aR.Left();
+ tools::Long dyo=rPt.Y()-aR.Top();
+ tools::Long dxr=aR.Right()-rPt.X();
+ tools::Long dyu=aR.Bottom()-rPt.Y();
+ bool bxMitt=std::abs(dxl-dxr)<2;
+ bool byMitt=std::abs(dyo-dyu)<2;
+ tools::Long dx=std::min(dxl,dxr);
+ tools::Long dy=std::min(dyo,dyu);
+ bool bDiag=std::abs(dx-dy)<2;
+ if (bxMitt && byMitt) return SdrEscapeDirection::ALL; // in the center
+ if (bDiag) { // diagonally
+ SdrEscapeDirection nRet=SdrEscapeDirection::SMART;
+ if (byMitt) nRet|=SdrEscapeDirection::VERT;
+ if (bxMitt) nRet|=SdrEscapeDirection::HORZ;
+ if (dxl<dxr) { // left
+ if (dyo<dyu) nRet|=SdrEscapeDirection::LEFT | SdrEscapeDirection::TOP;
+ else nRet|=SdrEscapeDirection::LEFT | SdrEscapeDirection::BOTTOM;
+ } else { // right
+ if (dyo<dyu) nRet|=SdrEscapeDirection::RIGHT | SdrEscapeDirection::TOP;
+ else nRet|=SdrEscapeDirection::RIGHT | SdrEscapeDirection::BOTTOM;
+ }
+ return nRet;
+ }
+ if (dx<dy) { // horizontal
+ if (bxMitt) return SdrEscapeDirection::HORZ;
+ if (dxl<dxr) return SdrEscapeDirection::LEFT;
+ else return SdrEscapeDirection::RIGHT;
+ } else { // vertical
+ if (byMitt) return SdrEscapeDirection::VERT;
+ if (dyo<dyu) return SdrEscapeDirection::TOP;
+ else return SdrEscapeDirection::BOTTOM;
+ }
+}
+
+XPolygon SdrEdgeObj::ImpCalcObjToCenter(const Point& rStPt, tools::Long nEscAngle, const tools::Rectangle& rRect, const Point& rMeeting)
+{
+ XPolygon aXP;
+ aXP.Insert(XPOLY_APPEND,rStPt,PolyFlags::Normal);
+ bool bRts=nEscAngle==0;
+ bool bObn=nEscAngle==9000;
+ bool bLks=nEscAngle==18000;
+ bool bUnt=nEscAngle==27000;
+
+ Point aP1(rStPt); // mandatory difference first,...
+ if (bLks) aP1.setX(rRect.Left() );
+ if (bRts) aP1.setX(rRect.Right() );
+ if (bObn) aP1.setY(rRect.Top() );
+ if (bUnt) aP1.setY(rRect.Bottom() );
+
+ Point aP2(aP1); // ...now increase to Meeting height, if necessary
+ if (bLks && rMeeting.X()<=aP2.X()) aP2.setX(rMeeting.X() );
+ if (bRts && rMeeting.X()>=aP2.X()) aP2.setX(rMeeting.X() );
+ if (bObn && rMeeting.Y()<=aP2.Y()) aP2.setY(rMeeting.Y() );
+ if (bUnt && rMeeting.Y()>=aP2.Y()) aP2.setY(rMeeting.Y() );
+ aXP.Insert(XPOLY_APPEND,aP2,PolyFlags::Normal);
+
+ Point aP3(aP2);
+ if ((bLks && rMeeting.X()>aP2.X()) || (bRts && rMeeting.X()<aP2.X())) { // around
+ if (rMeeting.Y()<aP2.Y()) {
+ aP3.setY(rRect.Top() );
+ if (rMeeting.Y()<aP3.Y()) aP3.setY(rMeeting.Y() );
+ } else {
+ aP3.setY(rRect.Bottom() );
+ if (rMeeting.Y()>aP3.Y()) aP3.setY(rMeeting.Y() );
+ }
+ aXP.Insert(XPOLY_APPEND,aP3,PolyFlags::Normal);
+ if (aP3.Y()!=rMeeting.Y()) {
+ aP3.setX(rMeeting.X() );
+ aXP.Insert(XPOLY_APPEND,aP3,PolyFlags::Normal);
+ }
+ }
+ if ((bObn && rMeeting.Y()>aP2.Y()) || (bUnt && rMeeting.Y()<aP2.Y())) { // around
+ if (rMeeting.X()<aP2.X()) {
+ aP3.setX(rRect.Left() );
+ if (rMeeting.X()<aP3.X()) aP3.setX(rMeeting.X() );
+ } else {
+ aP3.setX(rRect.Right() );
+ if (rMeeting.X()>aP3.X()) aP3.setX(rMeeting.X() );
+ }
+ aXP.Insert(XPOLY_APPEND,aP3,PolyFlags::Normal);
+ if (aP3.X()!=rMeeting.X()) {
+ aP3.setY(rMeeting.Y() );
+ aXP.Insert(XPOLY_APPEND,aP3,PolyFlags::Normal);
+ }
+ }
+#ifdef DBG_UTIL
+ if (aXP.GetPointCount()>4) {
+ OSL_FAIL("SdrEdgeObj::ImpCalcObjToCenter(): Polygon has more than 4 points!");
+ }
+#endif
+ return aXP;
+}
+
+XPolygon SdrEdgeObj::ImpCalcEdgeTrack(const XPolygon& rTrack0, SdrObjConnection& rCon1, SdrObjConnection& rCon2, SdrEdgeInfoRec* pInfo) const
+{
+ Point aPt1,aPt2;
+ SdrGluePoint aGP1,aGP2;
+ SdrEscapeDirection nEsc1=SdrEscapeDirection::ALL,nEsc2=SdrEscapeDirection::ALL;
+ tools::Rectangle aBoundRect1;
+ tools::Rectangle aBoundRect2;
+ tools::Rectangle aBewareRect1;
+ tools::Rectangle aBewareRect2;
+ // first, get the old corner points
+ if (rTrack0.GetPointCount()!=0) {
+ aPt1=rTrack0[0];
+ sal_uInt16 nSiz=rTrack0.GetPointCount();
+ nSiz--;
+ aPt2=rTrack0[nSiz];
+ } else {
+ if (!m_aOutRect.IsEmpty()) {
+ aPt1=m_aOutRect.TopLeft();
+ aPt2=m_aOutRect.BottomRight();
+ }
+ }
+
+ // #i54102# To allow interactive preview, do also if not inserted
+ const bool bCon1(nullptr != rCon1.pObj && rCon1.pObj->getSdrPageFromSdrObject() == getSdrPageFromSdrObject());
+ const bool bCon2(nullptr != rCon2.pObj && rCon2.pObj->getSdrPageFromSdrObject() == getSdrPageFromSdrObject());
+ const SfxItemSet& rSet = GetObjectItemSet();
+
+ if (bCon1)
+ {
+ if (rCon1.pObj==static_cast<SdrObject const *>(this))
+ {
+ // check, just in case
+ aBoundRect1=m_aOutRect;
+ }
+ else
+ {
+ aBoundRect1 = rCon1.pObj->GetCurrentBoundRect();
+ }
+
+ aBoundRect1.Move(rCon1.aObjOfs.X(),rCon1.aObjOfs.Y());
+ aBewareRect1=aBoundRect1;
+ sal_Int32 nH = rSet.Get(SDRATTR_EDGENODE1HORZDIST).GetValue();
+ sal_Int32 nV = rSet.Get(SDRATTR_EDGENODE1VERTDIST).GetValue();
+ aBewareRect1.AdjustLeft( -nH );
+ aBewareRect1.AdjustRight(nH );
+ aBewareRect1.AdjustTop( -nV );
+ aBewareRect1.AdjustBottom(nV );
+ }
+ else
+ {
+ aBoundRect1=tools::Rectangle(aPt1,aPt1);
+ aBoundRect1.Move(rCon1.aObjOfs.X(),rCon1.aObjOfs.Y());
+ aBewareRect1=aBoundRect1;
+ }
+
+ if (bCon2)
+ {
+ if (rCon2.pObj==static_cast<SdrObject const *>(this))
+ { // check, just in case
+ aBoundRect2=m_aOutRect;
+ }
+ else
+ {
+ aBoundRect2 = rCon2.pObj->GetCurrentBoundRect();
+ }
+
+ aBoundRect2.Move(rCon2.aObjOfs.X(),rCon2.aObjOfs.Y());
+ aBewareRect2=aBoundRect2;
+ sal_Int32 nH = rSet.Get(SDRATTR_EDGENODE2HORZDIST).GetValue();
+ sal_Int32 nV = rSet.Get(SDRATTR_EDGENODE2VERTDIST).GetValue();
+ aBewareRect2.AdjustLeft( -nH );
+ aBewareRect2.AdjustRight(nH );
+ aBewareRect2.AdjustTop( -nV );
+ aBewareRect2.AdjustBottom(nV );
+ }
+ else
+ {
+ aBoundRect2=tools::Rectangle(aPt2,aPt2);
+ aBoundRect2.Move(rCon2.aObjOfs.X(),rCon2.aObjOfs.Y());
+ aBewareRect2=aBoundRect2;
+ }
+
+ XPolygon aBestXP;
+ sal_uIntPtr nBestQual=0xFFFFFFFF;
+ SdrEdgeInfoRec aBestInfo;
+ bool bAuto1=bCon1 && rCon1.bBestVertex;
+ bool bAuto2=bCon2 && rCon2.bBestVertex;
+ if (bAuto1) rCon1.bAutoVertex=true;
+ if (bAuto2) rCon2.bAutoVertex=true;
+ sal_uInt16 nBestAuto1=0;
+ sal_uInt16 nBestAuto2=0;
+ sal_uInt16 nCount1=bAuto1 ? 4 : 1;
+ sal_uInt16 nCount2=bAuto2 ? 4 : 1;
+
+ for (sal_uInt16 nNum1=0; nNum1<nCount1; nNum1++)
+ {
+ if (bAuto1) rCon1.nConId=nNum1;
+ if (bCon1 && rCon1.TakeGluePoint(aGP1))
+ {
+ aPt1=aGP1.GetPos();
+ nEsc1=aGP1.GetEscDir();
+ if (nEsc1==SdrEscapeDirection::SMART) nEsc1=ImpCalcEscAngle(rCon1.pObj,aPt1-rCon1.aObjOfs);
+ }
+ for (sal_uInt16 nNum2=0; nNum2<nCount2; nNum2++)
+ {
+ if (bAuto2) rCon2.nConId=nNum2;
+ if (bCon2 && rCon2.TakeGluePoint(aGP2))
+ {
+ aPt2=aGP2.GetPos();
+ nEsc2=aGP2.GetEscDir();
+ if (nEsc2==SdrEscapeDirection::SMART) nEsc2=ImpCalcEscAngle(rCon2.pObj,aPt2-rCon2.aObjOfs);
+ }
+ for (tools::Long nA1=0; nA1<36000; nA1+=9000)
+ {
+ SdrEscapeDirection nE1 = nA1==0 ? SdrEscapeDirection::RIGHT : nA1==9000 ? SdrEscapeDirection::TOP : nA1==18000 ? SdrEscapeDirection::LEFT : nA1==27000 ? SdrEscapeDirection::BOTTOM : SdrEscapeDirection::SMART;
+ for (tools::Long nA2=0; nA2<36000; nA2+=9000)
+ {
+ SdrEscapeDirection nE2 = nA2==0 ? SdrEscapeDirection::RIGHT : nA2==9000 ? SdrEscapeDirection::TOP : nA2==18000 ? SdrEscapeDirection::LEFT : nA2==27000 ? SdrEscapeDirection::BOTTOM : SdrEscapeDirection::SMART;
+ if ((nEsc1&nE1) && (nEsc2&nE2))
+ {
+ sal_uIntPtr nQual=0;
+ SdrEdgeInfoRec aInfo;
+ if (pInfo!=nullptr) aInfo=*pInfo;
+ XPolygon aXP(ImpCalcEdgeTrack(aPt1,nA1,aBoundRect1,aBewareRect1,aPt2,nA2,aBoundRect2,aBewareRect2,&nQual,&aInfo));
+ if (nQual<nBestQual)
+ {
+ aBestXP=std::move(aXP);
+ nBestQual=nQual;
+ aBestInfo=aInfo;
+ nBestAuto1=nNum1;
+ nBestAuto2=nNum2;
+ }
+ }
+ }
+ }
+ }
+ }
+ if (bAuto1) rCon1.nConId=nBestAuto1;
+ if (bAuto2) rCon2.nConId=nBestAuto2;
+ if (pInfo!=nullptr) *pInfo=aBestInfo;
+ return aBestXP;
+}
+
+XPolygon SdrEdgeObj::ImpCalcEdgeTrack(const Point& rPt1, tools::Long nAngle1, const tools::Rectangle& rBoundRect1, const tools::Rectangle& rBewareRect1,
+ const Point& rPt2, tools::Long nAngle2, const tools::Rectangle& rBoundRect2, const tools::Rectangle& rBewareRect2,
+ sal_uIntPtr* pnQuality, SdrEdgeInfoRec* pInfo) const
+{
+ SdrEdgeKind eKind=GetObjectItem(SDRATTR_EDGEKIND).GetValue();
+ bool bRts1=nAngle1==0;
+ bool bObn1=nAngle1==9000;
+ bool bLks1=nAngle1==18000;
+ bool bUnt1=nAngle1==27000;
+ bool bHor1=bLks1 || bRts1;
+ bool bVer1=bObn1 || bUnt1;
+ bool bRts2=nAngle2==0;
+ bool bObn2=nAngle2==9000;
+ bool bLks2=nAngle2==18000;
+ bool bUnt2=nAngle2==27000;
+ bool bHor2=bLks2 || bRts2;
+ bool bVer2=bObn2 || bUnt2;
+ bool bInfo=pInfo!=nullptr;
+ if (bInfo) {
+ pInfo->nAngle1=nAngle1;
+ pInfo->nAngle2=nAngle2;
+ pInfo->nObj1Lines=1;
+ pInfo->nObj2Lines=1;
+ pInfo->nMiddleLine=0xFFFF;
+ }
+ Point aPt1(rPt1);
+ Point aPt2(rPt2);
+ tools::Rectangle aBoundRect1 (rBoundRect1 );
+ tools::Rectangle aBoundRect2 (rBoundRect2 );
+ tools::Rectangle aBewareRect1(rBewareRect1);
+ tools::Rectangle aBewareRect2(rBewareRect2);
+ Point aMeeting((aPt1.X()+aPt2.X()+1)/2,(aPt1.Y()+aPt2.Y()+1)/2);
+ if (eKind==SdrEdgeKind::OneLine) {
+ XPolygon aXP(2);
+ aXP[0]=rPt1;
+ aXP[1]=rPt2;
+ if (pnQuality!=nullptr) {
+ *pnQuality=std::abs(rPt1.X()-rPt2.X())+std::abs(rPt1.Y()-rPt2.Y());
+ }
+ return aXP;
+ } else if (eKind==SdrEdgeKind::ThreeLines) {
+ XPolygon aXP(4);
+ aXP[0]=rPt1;
+ aXP[1]=rPt1;
+ aXP[2]=rPt2;
+ aXP[3]=rPt2;
+ if (bRts1) aXP[1].setX(aBewareRect1.Right() ); //+=500;
+ if (bObn1) aXP[1].setY(aBewareRect1.Top() ); //-=500;
+ if (bLks1) aXP[1].setX(aBewareRect1.Left() ); //-=500;
+ if (bUnt1) aXP[1].setY(aBewareRect1.Bottom() ); //+=500;
+ if (bRts2) aXP[2].setX(aBewareRect2.Right() ); //+=500;
+ if (bObn2) aXP[2].setY(aBewareRect2.Top() ); //-=500;
+ if (bLks2) aXP[2].setX(aBewareRect2.Left() ); //-=500;
+ if (bUnt2) aXP[2].setY(aBewareRect2.Bottom() ); //+=500;
+ if (pnQuality!=nullptr) {
+ tools::Long nQ=std::abs(aXP[1].X()-aXP[0].X())+std::abs(aXP[1].Y()-aXP[0].Y());
+ nQ+=std::abs(aXP[2].X()-aXP[1].X())+std::abs(aXP[2].Y()-aXP[1].Y());
+ nQ+=std::abs(aXP[3].X()-aXP[2].X())+std::abs(aXP[3].Y()-aXP[2].Y());
+ *pnQuality=nQ;
+ }
+ if (bInfo) {
+ pInfo->nObj1Lines=2;
+ pInfo->nObj2Lines=2;
+ if (bHor1) {
+ aXP[1].AdjustX(pInfo->aObj1Line2.X() );
+ } else {
+ aXP[1].AdjustY(pInfo->aObj1Line2.Y() );
+ }
+ if (bHor2) {
+ aXP[2].AdjustX(pInfo->aObj2Line2.X() );
+ } else {
+ aXP[2].AdjustY(pInfo->aObj2Line2.Y() );
+ }
+ }
+ return aXP;
+ }
+ sal_uInt16 nIntersections=0;
+ {
+ Point aC1(aBewareRect1.Center());
+ Point aC2(aBewareRect2.Center());
+ if (aBewareRect1.Left()<=aBewareRect2.Right() && aBewareRect1.Right()>=aBewareRect2.Left()) {
+ // overlapping on the x axis
+ tools::Long n1=std::max(aBewareRect1.Left(),aBewareRect2.Left());
+ tools::Long n2=std::min(aBewareRect1.Right(),aBewareRect2.Right());
+ aMeeting.setX((n1+n2+1)/2 );
+ } else {
+ // otherwise the center point of the empty space
+ if (aC1.X()<aC2.X()) {
+ aMeeting.setX((aBewareRect1.Right()+aBewareRect2.Left()+1)/2 );
+ } else {
+ aMeeting.setX((aBewareRect1.Left()+aBewareRect2.Right()+1)/2 );
+ }
+ }
+ if (aBewareRect1.Top()<=aBewareRect2.Bottom() && aBewareRect1.Bottom()>=aBewareRect2.Top()) {
+ // overlapping on the x axis
+ tools::Long n1=std::max(aBewareRect1.Top(),aBewareRect2.Top());
+ tools::Long n2=std::min(aBewareRect1.Bottom(),aBewareRect2.Bottom());
+ aMeeting.setY((n1+n2+1)/2 );
+ } else {
+ // otherwise the center point of the empty space
+ if (aC1.Y()<aC2.Y()) {
+ aMeeting.setY((aBewareRect1.Bottom()+aBewareRect2.Top()+1)/2 );
+ } else {
+ aMeeting.setY((aBewareRect1.Top()+aBewareRect2.Bottom()+1)/2 );
+ }
+ }
+ // Here, there are three cases:
+ // 1. both go into the same direction
+ // 2. both go into opposite directions
+ // 3. one is vertical, the other is horizontal
+ tools::Long nXMin=std::min(aBewareRect1.Left(),aBewareRect2.Left());
+ tools::Long nXMax=std::max(aBewareRect1.Right(),aBewareRect2.Right());
+ tools::Long nYMin=std::min(aBewareRect1.Top(),aBewareRect2.Top());
+ tools::Long nYMax=std::max(aBewareRect1.Bottom(),aBewareRect2.Bottom());
+ bool bBewareOverlap=aBewareRect1.Right()>aBewareRect2.Left() && aBewareRect1.Left()<aBewareRect2.Right() &&
+ aBewareRect1.Bottom()>aBewareRect2.Top() && aBewareRect1.Top()<aBewareRect2.Bottom();
+ unsigned nMainCase=3;
+ if (nAngle1==nAngle2) nMainCase=1;
+ else if ((bHor1 && bHor2) || (bVer1 && bVer2)) nMainCase=2;
+ if (nMainCase==1) { // case 1 (both go in one direction) is possible
+ if (bVer1) aMeeting.setX((aPt1.X()+aPt2.X()+1)/2 ); // Here, this is better than
+ if (bHor1) aMeeting.setY((aPt1.Y()+aPt2.Y()+1)/2 ); // using center point of empty space
+ // bX1Ok means that the vertical exiting Obj1 doesn't conflict with Obj2, ...
+ bool bX1Ok=aPt1.X()<=aBewareRect2.Left() || aPt1.X()>=aBewareRect2.Right();
+ bool bX2Ok=aPt2.X()<=aBewareRect1.Left() || aPt2.X()>=aBewareRect1.Right();
+ bool bY1Ok=aPt1.Y()<=aBewareRect2.Top() || aPt1.Y()>=aBewareRect2.Bottom();
+ bool bY2Ok=aPt2.Y()<=aBewareRect1.Top() || aPt2.Y()>=aBewareRect1.Bottom();
+ if (bLks1 && (bY1Ok || aBewareRect1.Left()<aBewareRect2.Right()) && (bY2Ok || aBewareRect2.Left()<aBewareRect1.Right())) {
+ aMeeting.setX(nXMin );
+ }
+ if (bRts1 && (bY1Ok || aBewareRect1.Right()>aBewareRect2.Left()) && (bY2Ok || aBewareRect2.Right()>aBewareRect1.Left())) {
+ aMeeting.setX(nXMax );
+ }
+ if (bObn1 && (bX1Ok || aBewareRect1.Top()<aBewareRect2.Bottom()) && (bX2Ok || aBewareRect2.Top()<aBewareRect1.Bottom())) {
+ aMeeting.setY(nYMin );
+ }
+ if (bUnt1 && (bX1Ok || aBewareRect1.Bottom()>aBewareRect2.Top()) && (bX2Ok || aBewareRect2.Bottom()>aBewareRect1.Top())) {
+ aMeeting.setY(nYMax );
+ }
+ } else if (nMainCase==2) {
+ // case 2:
+ if (bHor1) { // both horizontal
+ /* 9 sub-cases:
+ (legend: line exits to the left (-|), right (|-))
+
+ 2.1: Facing; overlap only on y axis
+ * * *
+ |--| *
+ * * *
+
+ 2.2, 2.3: Facing, offset vertically; no overlap on either
+ axis
+ |- * * * * *
+ * -| * * -| *
+ * * * , * * *
+
+ 2.4, 2.5: One below the other; overlap only on y axis
+ * |- * * * *
+ * -| * * -| *
+ * * * , * |- *
+
+ 2.6, 2.7: Not facing, offset vertically; no overlap on either
+ axis
+ * * |- * * *
+ * -| * * -| *
+ * * * , * * |-
+
+ 2.8: Not facing; overlap only on y axis
+ * * *
+ * -| |-
+ * * *
+
+ 2.9: The objects's BewareRects overlap on x and y axis
+
+ These cases, with some modifications are also valid for
+ horizontal line exits.
+ Cases 2.1 through 2.7 are covered well enough with the
+ default meetings. Only for cases 2.8 and 2.9 do we determine
+ special meeting points here.
+ */
+
+ // normalization; be aR1 the one exiting to the right,
+ // be aR2 the one exiting to the left
+ tools::Rectangle aBewR1(bRts1 ? aBewareRect1 : aBewareRect2);
+ tools::Rectangle aBewR2(bRts1 ? aBewareRect2 : aBewareRect1);
+ tools::Rectangle aBndR1(bRts1 ? aBoundRect1 : aBoundRect2);
+ tools::Rectangle aBndR2(bRts1 ? aBoundRect2 : aBoundRect1);
+ if (aBewR1.Bottom()>aBewR2.Top() && aBewR1.Top()<aBewR2.Bottom()) {
+ // overlap on y axis; cases 2.1, 2.8, 2.9
+ if (aBewR1.Right()>aBewR2.Left()) {
+ /* Cases 2.8, 2.9:
+ Case 2.8: always going around on the outside
+ (bDirect=false).
+
+ Case 2.9 could also be a direct connection (in the
+ case that the BewareRects overlap only slightly and
+ the BoundRects don't overlap at all and if the
+ line exits would otherwise violate the respective
+ other object's BewareRect).
+ */
+ bool bCase29Direct = false;
+ bool bCase29=aBewR1.Right()>aBewR2.Left();
+ if (aBndR1.Right()<=aBndR2.Left()) { // case 2.9 without BoundRect overlap
+ if ((aPt1.Y()>aBewareRect2.Top() && aPt1.Y()<aBewareRect2.Bottom()) ||
+ (aPt2.Y()>aBewareRect1.Top() && aPt2.Y()<aBewareRect1.Bottom())) {
+ bCase29Direct = true;
+ }
+ }
+ if (!bCase29Direct) {
+ bool bObenLang=std::abs(nYMin-aMeeting.Y())<=std::abs(nYMax-aMeeting.Y());
+ if (bObenLang) {
+ aMeeting.setY(nYMin );
+ } else {
+ aMeeting.setY(nYMax );
+ }
+ if (bCase29) {
+ // now make sure that the surrounded object
+ // isn't traversed
+ if ((aBewR1.Center().Y()<aBewR2.Center().Y()) != bObenLang) {
+ aMeeting.setX(aBewR2.Right() );
+ } else {
+ aMeeting.setX(aBewR1.Left() );
+ }
+ }
+ } else {
+ // We need a direct connection (3-line Z connection),
+ // because we have to violate the BewareRects.
+ // Use rule of three to scale down the BewareRects.
+ tools::Long nWant1=aBewR1.Right()-aBndR1.Right(); // distance at Obj1
+ tools::Long nWant2=aBndR2.Left()-aBewR2.Left(); // distance at Obj2
+ tools::Long nSpace=aBndR2.Left()-aBndR1.Right(); // available space
+ tools::Long nGet1=BigMulDiv(nWant1,nSpace,nWant1+nWant2);
+ tools::Long nGet2=nSpace-nGet1;
+ if (bRts1) { // revert normalization
+ aBewareRect1.AdjustRight(nGet1-nWant1 );
+ aBewareRect2.AdjustLeft( -(nGet2-nWant2) );
+ } else {
+ aBewareRect2.AdjustRight(nGet1-nWant1 );
+ aBewareRect1.AdjustLeft( -(nGet2-nWant2) );
+ }
+ nIntersections++; // lower quality
+ }
+ }
+ }
+ } else if (bVer1) { // both horizontal
+ tools::Rectangle aBewR1(bUnt1 ? aBewareRect1 : aBewareRect2);
+ tools::Rectangle aBewR2(bUnt1 ? aBewareRect2 : aBewareRect1);
+ tools::Rectangle aBndR1(bUnt1 ? aBoundRect1 : aBoundRect2);
+ tools::Rectangle aBndR2(bUnt1 ? aBoundRect2 : aBoundRect1);
+ if (aBewR1.Right()>aBewR2.Left() && aBewR1.Left()<aBewR2.Right()) {
+ // overlap on y axis; cases 2.1, 2.8, 2.9
+ if (aBewR1.Bottom()>aBewR2.Top()) {
+ /* Cases 2.8, 2.9
+ Case 2.8 always going around on the outside (bDirect=false).
+
+ Case 2.9 could also be a direct connection (in the
+ case that the BewareRects overlap only slightly and
+ the BoundRects don't overlap at all and if the
+ line exits would otherwise violate the respective
+ other object's BewareRect).
+ */
+ bool bCase29Direct = false;
+ bool bCase29=aBewR1.Bottom()>aBewR2.Top();
+ if (aBndR1.Bottom()<=aBndR2.Top()) { // case 2.9 without BoundRect overlap
+ if ((aPt1.X()>aBewareRect2.Left() && aPt1.X()<aBewareRect2.Right()) ||
+ (aPt2.X()>aBewareRect1.Left() && aPt2.X()<aBewareRect1.Right())) {
+ bCase29Direct = true;
+ }
+ }
+ if (!bCase29Direct) {
+ bool bLinksLang=std::abs(nXMin-aMeeting.X())<=std::abs(nXMax-aMeeting.X());
+ if (bLinksLang) {
+ aMeeting.setX(nXMin );
+ } else {
+ aMeeting.setX(nXMax );
+ }
+ if (bCase29) {
+ // now make sure that the surrounded object
+ // isn't traversed
+ if ((aBewR1.Center().X()<aBewR2.Center().X()) != bLinksLang) {
+ aMeeting.setY(aBewR2.Bottom() );
+ } else {
+ aMeeting.setY(aBewR1.Top() );
+ }
+ }
+ } else {
+ // We need a direct connection (3-line Z connection),
+ // because we have to violate the BewareRects.
+ // Use rule of three to scale down the BewareRects.
+ tools::Long nWant1=aBewR1.Bottom()-aBndR1.Bottom(); // difference at Obj1
+ tools::Long nWant2=aBndR2.Top()-aBewR2.Top(); // difference at Obj2
+ tools::Long nSpace=aBndR2.Top()-aBndR1.Bottom(); // available space
+ tools::Long nGet1=BigMulDiv(nWant1,nSpace,nWant1+nWant2);
+ tools::Long nGet2=nSpace-nGet1;
+ if (bUnt1) { // revert normalization
+ aBewareRect1.AdjustBottom(nGet1-nWant1 );
+ aBewareRect2.AdjustTop( -(nGet2-nWant2) );
+ } else {
+ aBewareRect2.AdjustBottom(nGet1-nWant1 );
+ aBewareRect1.AdjustTop( -(nGet2-nWant2) );
+ }
+ nIntersections++; // lower quality
+ }
+ }
+ }
+ }
+ } else if (nMainCase==3) { // case 3: one horizontal, the other vertical
+ /* legend:
+ The line exits to the:
+ -| left
+
+ |- right
+
+ _|_ top
+
+ T bottom
+
+ * . * . * -- no overlap, at most might touch
+ . . . . . -- overlap
+ * . |- . * -- same height
+ . . . . . -- overlap
+ * . * . * -- no overlap, at most might touch
+
+ Overall, there are 96 possible constellations, some of these can't even
+ be unambiguously assigned to a certain case/method of handling.
+
+
+ 3.1: All those constellations that are covered reasonably well
+ by the default MeetingPoint (20+12).
+
+ T T T . _|_ _|_ . T T T these 12 * . * T * * . * . * * T * . * * . * . *
+ . . . . _|_ _|_ . . . . constellations . . . . . . . . . T . . . . . T . . . .
+ * . |- . * * . -| . * are covered * . |- . _|_ * . |- . T _|_ . -| . * T . -| . *
+ . . . . T T . . . . only in . . . . _|_ . . . . . _|_ . . . . . . . . .
+ _|__|__|_ . T T . _|__|__|_ part: * . * _|_ * * . * . * * _|_ * . * * . * . *
+
+ The last 16 of these cases can be excluded, if the objects face each other openly.
+
+
+ 3.2: The objects face each other openly, thus a connection using only two lines is possible (4+20);
+ This case is priority #1.
+ * . * . T T . * . * these 20 * . * T * * T * . * * . * . * * . * . *
+ . . . . . . . . . . constellations . . . T T T T . . . . . . . . . . . . .
+ * . |- . * * . -| . * are covered * . |-_|__|_ _|__|_-| . * * . |- T T T T -| . *
+ . . . . . . . . . . only in . . . _|__|_ _|__|_ . . . . . . . . . . . . .
+ * . * . _|_ _|_ . * . * part: * . * _|_ * * _|_ * . * * . * . * * . * . *
+
+ 3.3: The line exits point away from the other object or miss its back (52+4).
+ _|__|__|__|_ * * _|__|__|__|_ * . . . * * . * . * these 4 * . * . * * . * . *
+ _|__|__|__|_ . . _|__|__|__|_ T T T . . . . T T T constellations . . . T . . T . . .
+ _|__|_ |- . * * . -| _|__|_ T T |- . * * . -| T T are covered * . |- . * * . -| . *
+ _|__|__|_ . . . . _|__|__|_ T T T T . . T T T T only in . . . _|_ . . _|_ . . .
+ * . * . * * . * . * T T T T * * T T T T part: * . * . * * . * . *
+ */
+
+ // case 3.2
+ tools::Rectangle aTmpR1(aBewareRect1);
+ tools::Rectangle aTmpR2(aBewareRect2);
+ if (bBewareOverlap) {
+ // overlapping BewareRects: use BoundRects for checking for case 3.2
+ aTmpR1=aBoundRect1;
+ aTmpR2=aBoundRect2;
+ }
+ if ((((bRts1 && aTmpR1.Right ()<=aPt2.X()) || (bLks1 && aTmpR1.Left()>=aPt2.X())) &&
+ ((bUnt2 && aTmpR2.Bottom()<=aPt1.Y()) || (bObn2 && aTmpR2.Top ()>=aPt1.Y()))) ||
+ (((bRts2 && aTmpR2.Right ()<=aPt1.X()) || (bLks2 && aTmpR2.Left()>=aPt1.X())) &&
+ ((bUnt1 && aTmpR1.Bottom()<=aPt2.Y()) || (bObn1 && aTmpR1.Top ()>=aPt2.Y())))) {
+ // case 3.2 applies: connector with only 2 lines
+ if (bHor1) {
+ aMeeting.setX(aPt2.X() );
+ aMeeting.setY(aPt1.Y() );
+ } else {
+ aMeeting.setX(aPt1.X() );
+ aMeeting.setY(aPt2.Y() );
+ }
+ // in the case of overlapping BewareRects:
+ aBewareRect1=aTmpR1;
+ aBewareRect2=aTmpR2;
+ } else if ((((bRts1 && aBewareRect1.Right ()>aBewareRect2.Left ()) ||
+ (bLks1 && aBewareRect1.Left ()<aBewareRect2.Right ())) &&
+ ((bUnt2 && aBewareRect2.Bottom()>aBewareRect1.Top ()) ||
+ (bObn2 && aBewareRect2.Top ()<aBewareRect1.Bottom()))) ||
+ (((bRts2 && aBewareRect2.Right ()>aBewareRect1.Left ()) ||
+ (bLks2 && aBewareRect2.Left ()<aBewareRect1.Right ())) &&
+ ((bUnt1 && aBewareRect1.Bottom()>aBewareRect2.Top ()) ||
+ (bObn1 && aBewareRect1.Top ()<aBewareRect2.Bottom())))) {
+ // case 3.3
+ if (bRts1 || bRts2) { aMeeting.setX(nXMax ); }
+ if (bLks1 || bLks2) { aMeeting.setX(nXMin ); }
+ if (bUnt1 || bUnt2) { aMeeting.setY(nYMax ); }
+ if (bObn1 || bObn2) { aMeeting.setY(nYMin ); }
+ }
+ }
+ }
+
+ XPolygon aXP1(ImpCalcObjToCenter(aPt1,nAngle1,aBewareRect1,aMeeting));
+ XPolygon aXP2(ImpCalcObjToCenter(aPt2,nAngle2,aBewareRect2,aMeeting));
+ sal_uInt16 nXP1Cnt=aXP1.GetPointCount();
+ sal_uInt16 nXP2Cnt=aXP2.GetPointCount();
+ if (bInfo) {
+ pInfo->nObj1Lines=nXP1Cnt; if (nXP1Cnt>1) pInfo->nObj1Lines--;
+ pInfo->nObj2Lines=nXP2Cnt; if (nXP2Cnt>1) pInfo->nObj2Lines--;
+ }
+ Point aEP1(aXP1[nXP1Cnt-1]);
+ Point aEP2(aXP2[nXP2Cnt-1]);
+ bool bInsMeetingPoint=aEP1.X()!=aEP2.X() && aEP1.Y()!=aEP2.Y();
+ bool bHorzE1=aEP1.Y()==aXP1[nXP1Cnt-2].Y(); // is last line of XP1 horizontal?
+ bool bHorzE2=aEP2.Y()==aXP2[nXP2Cnt-2].Y(); // is last line of XP2 horizontal?
+ if (aEP1==aEP2 && ((bHorzE1 && bHorzE2 && aEP1.Y()==aEP2.Y()) || (!bHorzE1 && !bHorzE2 && aEP1.X()==aEP2.X()))) {
+ // special casing 'I' connectors
+ nXP1Cnt--; aXP1.Remove(nXP1Cnt,1);
+ nXP2Cnt--; aXP2.Remove(nXP2Cnt,1);
+ }
+ if (bInsMeetingPoint) {
+ aXP1.Insert(XPOLY_APPEND,aMeeting,PolyFlags::Normal);
+ if (bInfo) {
+ // Inserting a MeetingPoint adds 2 new lines,
+ // either might become the center line.
+ if (pInfo->nObj1Lines==pInfo->nObj2Lines) {
+ pInfo->nObj1Lines++;
+ pInfo->nObj2Lines++;
+ } else {
+ if (pInfo->nObj1Lines>pInfo->nObj2Lines) {
+ pInfo->nObj2Lines++;
+ pInfo->nMiddleLine=nXP1Cnt-1;
+ } else {
+ pInfo->nObj1Lines++;
+ pInfo->nMiddleLine=nXP1Cnt;
+ }
+ }
+ }
+ } else if (bInfo && aEP1!=aEP2 && nXP1Cnt+nXP2Cnt>=4) {
+ // By connecting both ends, another line is added, this becomes the center line.
+ pInfo->nMiddleLine=nXP1Cnt-1;
+ }
+ sal_uInt16 nNum=aXP2.GetPointCount();
+ if (aXP1[nXP1Cnt-1]==aXP2[nXP2Cnt-1] && nXP1Cnt>1 && nXP2Cnt>1) nNum--;
+ while (nNum>0) {
+ nNum--;
+ aXP1.Insert(XPOLY_APPEND,aXP2[nNum],PolyFlags::Normal);
+ }
+ sal_uInt16 nPointCount=aXP1.GetPointCount();
+ char cForm;
+ if (bInfo || pnQuality!=nullptr) {
+ if (nPointCount==2) cForm='I';
+ else if (nPointCount==3) cForm='L';
+ else if (nPointCount==4) { // Z or U
+ if (nAngle1==nAngle2) cForm='U';
+ else cForm='Z';
+ } else if (nPointCount==6) { // S or C or ...
+ if (nAngle1!=nAngle2) {
+ // For type S, line 2 has the same direction as line 4.
+ // For type C, the opposite is true.
+ Point aP1(aXP1[1]);
+ Point aP2(aXP1[2]);
+ Point aP3(aXP1[3]);
+ Point aP4(aXP1[4]);
+ if (aP1.Y()==aP2.Y()) { // else both lines are horizontal
+ if ((aP1.X()<aP2.X())==(aP3.X()<aP4.X())) cForm='S';
+ else cForm='C';
+ } else { // else both lines are vertical
+ if ((aP1.Y()<aP2.Y())==(aP3.Y()<aP4.Y())) cForm='S';
+ else cForm='C';
+ }
+ } else cForm='4'; // else is case 3 with 5 lines
+ } else cForm='?';
+ // more shapes:
+ if (bInfo) {
+ if (cForm=='I' || cForm=='L' || cForm=='Z' || cForm=='U') {
+ pInfo->nObj1Lines=1;
+ pInfo->nObj2Lines=1;
+ if (cForm=='Z' || cForm=='U') {
+ pInfo->nMiddleLine=1;
+ } else {
+ pInfo->nMiddleLine=0xFFFF;
+ }
+ } else if (cForm=='S' || cForm=='C') {
+ pInfo->nObj1Lines=2;
+ pInfo->nObj2Lines=2;
+ pInfo->nMiddleLine=2;
+ }
+ }
+ }
+ else
+ {
+ cForm = 0;
+ }
+ if (pnQuality!=nullptr) {
+ sal_uIntPtr nQual=0;
+ sal_uIntPtr nQual0=nQual; // prevent overruns
+ bool bOverflow = false;
+ Point aPt0(aXP1[0]);
+ for (sal_uInt16 nPntNum=1; nPntNum<nPointCount; nPntNum++) {
+ Point aPt1b(aXP1[nPntNum]);
+ nQual+=std::abs(aPt1b.X()-aPt0.X())+std::abs(aPt1b.Y()-aPt0.Y());
+ if (nQual<nQual0) bOverflow = true;
+ nQual0=nQual;
+ aPt0=aPt1b;
+ }
+
+ sal_uInt16 nTmp=nPointCount;
+ if (cForm=='Z') {
+ nTmp=2; // Z shape with good quality (nTmp=2 instead of 4)
+ sal_uIntPtr n1=std::abs(aXP1[1].X()-aXP1[0].X())+std::abs(aXP1[1].Y()-aXP1[0].Y());
+ sal_uIntPtr n2=std::abs(aXP1[2].X()-aXP1[1].X())+std::abs(aXP1[2].Y()-aXP1[1].Y());
+ sal_uIntPtr n3=std::abs(aXP1[3].X()-aXP1[2].X())+std::abs(aXP1[3].Y()-aXP1[2].Y());
+ // try to make lines lengths similar
+ sal_uIntPtr nBesser=0;
+ n1+=n3;
+ n3=n2/4;
+ if (n1>=n2) nBesser=6;
+ else if (n1>=3*n3) nBesser=4;
+ else if (n1>=2*n3) nBesser=2;
+ if (aXP1[0].Y()!=aXP1[1].Y()) nBesser++; // vertical starting line gets a plus (for H/V-Prio)
+ if (nQual>nBesser) nQual-=nBesser; else nQual=0;
+ }
+ if (nTmp>=3) {
+ nQual0=nQual;
+ nQual+=static_cast<sal_uIntPtr>(nTmp)*0x01000000;
+ if (nQual<nQual0 || nTmp>15) bOverflow = true;
+ }
+ if (nPointCount>=2) { // check exit angle again
+ Point aP1(aXP1[1]); aP1-=aXP1[0];
+ Point aP2(aXP1[nPointCount-2]); aP2-=aXP1[nPointCount-1];
+ tools::Long nAng1=0; if (aP1.X()<0) nAng1=18000; if (aP1.Y()>0) nAng1=27000;
+ if (aP1.Y()<0) nAng1=9000;
+ if (aP1.X()!=0 && aP1.Y()!=0) nAng1=1; // slant?!
+ tools::Long nAng2=0; if (aP2.X()<0) nAng2=18000; if (aP2.Y()>0) nAng2=27000;
+ if (aP2.Y()<0) nAng2=9000;
+ if (aP2.X()!=0 && aP2.Y()!=0) nAng2=1; // slant?!
+ if (nAng1!=nAngle1) nIntersections++;
+ if (nAng2!=nAngle2) nIntersections++;
+ }
+
+ // For the quality check, use the original Rects and at the same time
+ // check whether one them was scaled down for the calculation of the
+ // Edges (e. g. case 2.9)
+ aBewareRect1=rBewareRect1;
+ aBewareRect2=rBewareRect2;
+
+ for (sal_uInt16 i=0; i<nPointCount; i++) {
+ Point aPt1b(aXP1[i]);
+ bool b1=aPt1b.X()>aBewareRect1.Left() && aPt1b.X()<aBewareRect1.Right() &&
+ aPt1b.Y()>aBewareRect1.Top() && aPt1b.Y()<aBewareRect1.Bottom();
+ bool b2=aPt1b.X()>aBewareRect2.Left() && aPt1b.X()<aBewareRect2.Right() &&
+ aPt1b.Y()>aBewareRect2.Top() && aPt1b.Y()<aBewareRect2.Bottom();
+ sal_uInt16 nInt0=nIntersections;
+ if (i==0 || i==nPointCount-1) {
+ if (b1 && b2) nIntersections++;
+ } else {
+ if (b1) nIntersections++;
+ if (b2) nIntersections++;
+ }
+ // check for overlaps
+ if (i>0 && nInt0==nIntersections) {
+ if (aPt0.Y()==aPt1b.Y()) { // horizontal line
+ if (aPt0.Y()>aBewareRect1.Top() && aPt0.Y()<aBewareRect1.Bottom() &&
+ ((aPt0.X()<=aBewareRect1.Left() && aPt1b.X()>=aBewareRect1.Right()) ||
+ (aPt1b.X()<=aBewareRect1.Left() && aPt0.X()>=aBewareRect1.Right()))) nIntersections++;
+ if (aPt0.Y()>aBewareRect2.Top() && aPt0.Y()<aBewareRect2.Bottom() &&
+ ((aPt0.X()<=aBewareRect2.Left() && aPt1b.X()>=aBewareRect2.Right()) ||
+ (aPt1b.X()<=aBewareRect2.Left() && aPt0.X()>=aBewareRect2.Right()))) nIntersections++;
+ } else { // vertical line
+ if (aPt0.X()>aBewareRect1.Left() && aPt0.X()<aBewareRect1.Right() &&
+ ((aPt0.Y()<=aBewareRect1.Top() && aPt1b.Y()>=aBewareRect1.Bottom()) ||
+ (aPt1b.Y()<=aBewareRect1.Top() && aPt0.Y()>=aBewareRect1.Bottom()))) nIntersections++;
+ if (aPt0.X()>aBewareRect2.Left() && aPt0.X()<aBewareRect2.Right() &&
+ ((aPt0.Y()<=aBewareRect2.Top() && aPt1b.Y()>=aBewareRect2.Bottom()) ||
+ (aPt1b.Y()<=aBewareRect2.Top() && aPt0.Y()>=aBewareRect2.Bottom()))) nIntersections++;
+ }
+ }
+ aPt0=aPt1b;
+ }
+ if (nPointCount<=1) nIntersections++;
+ nQual0=nQual;
+ nQual+=static_cast<sal_uIntPtr>(nIntersections)*0x10000000;
+ if (nQual<nQual0 || nIntersections>15) bOverflow = true;
+
+ if (bOverflow || nQual==0xFFFFFFFF) nQual=0xFFFFFFFE;
+ *pnQuality=nQual;
+ }
+ if (bInfo) { // now apply line offsets to aXP1
+ if (pInfo->nMiddleLine!=0xFFFF) {
+ sal_uInt16 nIdx=pInfo->ImpGetPolyIdx(SdrEdgeLineCode::MiddleLine,aXP1);
+ if (pInfo->ImpIsHorzLine(SdrEdgeLineCode::MiddleLine,aXP1)) {
+ aXP1[nIdx].AdjustY(pInfo->aMiddleLine.Y() );
+ aXP1[nIdx+1].AdjustY(pInfo->aMiddleLine.Y() );
+ } else {
+ aXP1[nIdx].AdjustX(pInfo->aMiddleLine.X() );
+ aXP1[nIdx+1].AdjustX(pInfo->aMiddleLine.X() );
+ }
+ }
+ if (pInfo->nObj1Lines>=2) {
+ sal_uInt16 nIdx=pInfo->ImpGetPolyIdx(SdrEdgeLineCode::Obj1Line2,aXP1);
+ if (pInfo->ImpIsHorzLine(SdrEdgeLineCode::Obj1Line2,aXP1)) {
+ aXP1[nIdx].AdjustY(pInfo->aObj1Line2.Y() );
+ aXP1[nIdx+1].AdjustY(pInfo->aObj1Line2.Y() );
+ } else {
+ aXP1[nIdx].AdjustX(pInfo->aObj1Line2.X() );
+ aXP1[nIdx+1].AdjustX(pInfo->aObj1Line2.X() );
+ }
+ }
+ if (pInfo->nObj1Lines>=3) {
+ sal_uInt16 nIdx=pInfo->ImpGetPolyIdx(SdrEdgeLineCode::Obj1Line3,aXP1);
+ if (pInfo->ImpIsHorzLine(SdrEdgeLineCode::Obj1Line3,aXP1)) {
+ aXP1[nIdx].AdjustY(pInfo->aObj1Line3.Y() );
+ aXP1[nIdx+1].AdjustY(pInfo->aObj1Line3.Y() );
+ } else {
+ aXP1[nIdx].AdjustX(pInfo->aObj1Line3.X() );
+ aXP1[nIdx+1].AdjustX(pInfo->aObj1Line3.X() );
+ }
+ }
+ if (pInfo->nObj2Lines>=2) {
+ sal_uInt16 nIdx=pInfo->ImpGetPolyIdx(SdrEdgeLineCode::Obj2Line2,aXP1);
+ if (pInfo->ImpIsHorzLine(SdrEdgeLineCode::Obj2Line2,aXP1)) {
+ aXP1[nIdx].AdjustY(pInfo->aObj2Line2.Y() );
+ aXP1[nIdx+1].AdjustY(pInfo->aObj2Line2.Y() );
+ } else {
+ aXP1[nIdx].AdjustX(pInfo->aObj2Line2.X() );
+ aXP1[nIdx+1].AdjustX(pInfo->aObj2Line2.X() );
+ }
+ }
+ if (pInfo->nObj2Lines>=3) {
+ sal_uInt16 nIdx=pInfo->ImpGetPolyIdx(SdrEdgeLineCode::Obj2Line3,aXP1);
+ if (pInfo->ImpIsHorzLine(SdrEdgeLineCode::Obj2Line3,aXP1)) {
+ aXP1[nIdx].AdjustY(pInfo->aObj2Line3.Y() );
+ aXP1[nIdx+1].AdjustY(pInfo->aObj2Line3.Y() );
+ } else {
+ aXP1[nIdx].AdjustX(pInfo->aObj2Line3.X() );
+ aXP1[nIdx+1].AdjustX(pInfo->aObj2Line3.X() );
+ }
+ }
+ }
+ // make the connector a bezier curve, if appropriate
+ if (eKind==SdrEdgeKind::Bezier && nPointCount>2) {
+ Point* pPt1=&aXP1[0];
+ Point* pPt2=&aXP1[1];
+ Point* pPt3=&aXP1[nPointCount-2];
+ Point* pPt4=&aXP1[nPointCount-1];
+ tools::Long dx1=pPt2->X()-pPt1->X();
+ tools::Long dy1=pPt2->Y()-pPt1->Y();
+ tools::Long dx2=pPt3->X()-pPt4->X();
+ tools::Long dy2=pPt3->Y()-pPt4->Y();
+ if (cForm=='L') { // nPointCount==3
+ aXP1.SetFlags(1,PolyFlags::Control);
+ Point aPt3(*pPt2);
+ aXP1.Insert(2,aPt3,PolyFlags::Control);
+ nPointCount=aXP1.GetPointCount();
+ pPt2=&aXP1[1];
+ pPt3=&aXP1[nPointCount-2];
+ pPt2->AdjustX( -(dx1/3) );
+ pPt2->AdjustY( -(dy1/3) );
+ pPt3->AdjustX( -(dx2/3) );
+ pPt3->AdjustY( -(dy2/3) );
+ } else if (nPointCount>=4 && nPointCount<=6) { // Z or U or ...
+ // To all others, the end points of the original lines become control
+ // points for now. Thus, we need to do some more work for nPointCount>4!
+ aXP1.SetFlags(1,PolyFlags::Control);
+ aXP1.SetFlags(nPointCount-2,PolyFlags::Control);
+ // distance x1.5
+ pPt2->AdjustX(dx1/2 );
+ pPt2->AdjustY(dy1/2 );
+ pPt3->AdjustX(dx2/2 );
+ pPt3->AdjustY(dy2/2 );
+ if (nPointCount==5) {
+ // add a control point before and after center
+ Point aCenter(aXP1[2]);
+ tools::Long dx1b=aCenter.X()-aXP1[1].X();
+ tools::Long dy1b=aCenter.Y()-aXP1[1].Y();
+ tools::Long dx2b=aCenter.X()-aXP1[3].X();
+ tools::Long dy2b=aCenter.Y()-aXP1[3].Y();
+ aXP1.Insert(2,aCenter,PolyFlags::Control);
+ aXP1.SetFlags(3,PolyFlags::Symmetric);
+ aXP1.Insert(4,aCenter,PolyFlags::Control);
+ aXP1[2].AdjustX( -(dx1b/2) );
+ aXP1[2].AdjustY( -(dy1b/2) );
+ aXP1[3].AdjustX( -((dx1b+dx2b)/4) );
+ aXP1[3].AdjustY( -((dy1b+dy2b)/4) );
+ aXP1[4].AdjustX( -(dx2b/2) );
+ aXP1[4].AdjustY( -(dy2b/2) );
+ }
+ if (nPointCount==6) {
+ Point aPt1b(aXP1[2]);
+ Point aPt2b(aXP1[3]);
+ aXP1.Insert(2,aPt1b,PolyFlags::Control);
+ aXP1.Insert(5,aPt2b,PolyFlags::Control);
+ tools::Long dx=aPt1b.X()-aPt2b.X();
+ tools::Long dy=aPt1b.Y()-aPt2b.Y();
+ aXP1[3].AdjustX( -(dx/2) );
+ aXP1[3].AdjustY( -(dy/2) );
+ aXP1.SetFlags(3,PolyFlags::Symmetric);
+ aXP1.Remove(4,1); // because it's identical with aXP1[3]
+ }
+ }
+ }
+ return aXP1;
+}
+
+/*
+There could be a maximum of 64 different developments with 5 lines, a
+maximum of 32 developments with 4 lines, a maximum of 16 developments with
+3 lines, a maximum of 8 developments with 2 lines.
+This gives us a total of 124 possibilities.
+Normalized for the 1st exit angle to the right, there remain 31 possibilities.
+Now, normalizing away the vertical mirroring, we get to a total of 16
+characteristic developments with 1 through 5 lines:
+
+1 line (type "I") --
+
+2 lines (type "L") __|
+
+3 lines (type "U") __ (type "Z") _
+ __| _|
+ _ _
+4 lines #1 _| #2 | | #3 |_ #4 | |
+ _| _| _| _|
+ Of these, #1 is implausible, #2 is a rotated version of #3. This leaves
+ #2 (from now on referred to as 4.1) and #4 (from now on referred to as 4.2).
+ _ _
+5 lines #1 _| #2 _| #3 ___ #4 _
+ _| _| _| _| _| |_
+ _ _ _
+ #5 |_ #6 |_ #7 _| | #8 ____
+ _| _| _| |_ _|
+ Of these, 5.1, 5.2, 5.4 and 5.5 are implausible, 5.7 is a reversed version
+ of 5.3. This leaves 5.3 (type "4"), 5.6 (type "S") and 5.8 (type "C").
+
+We now have discerned the 9 basic types to cover all 400 possible constellations
+of object positions and exit angles. 4 of the 9 types have got a center
+line (CL). The number of object margins per object varies between 0 and 3:
+
+ CL O1 O2 Note
+"I": n 0 0
+"L": n 0 0
+"U": n 0-1 0-1
+"Z": y 0 0
+4.2: y 0 1 = U+1, respectively 1+U
+4.4: n 0-2 0-2 = Z+1
+"4": y 0 2 = Z+2
+"S": y 1 1 = 1+Z+1
+"C": n 0-3 0-3 = 1+U+1
+*/
+
+void SdrEdgeObj::Notify(SfxBroadcaster& rBC, const SfxHint& rHint)
+{
+ const SfxHintId nId = rHint.GetId();
+ bool bDataChg=nId==SfxHintId::DataChanged;
+ bool bDying=nId==SfxHintId::Dying;
+ bool bObj1=aCon1.pObj!=nullptr && aCon1.pObj->GetBroadcaster()==&rBC;
+ bool bObj2=aCon2.pObj!=nullptr && aCon2.pObj->GetBroadcaster()==&rBC;
+ if (bDying && (bObj1 || bObj2)) {
+ // catch Dying, so AttrObj doesn't start broadcasting
+ // about an alleged change of template
+ if (bObj1) aCon1.pObj=nullptr;
+ if (bObj2) aCon2.pObj=nullptr;
+ return;
+ }
+ if ( bObj1 || bObj2 )
+ {
+ bEdgeTrackUserDefined = false;
+ }
+ SdrTextObj::Notify(rBC,rHint);
+ if (nNotifyingCount!=0)return;
+
+// a locking flag
+ nNotifyingCount++;
+ const SdrHint* pSdrHint = ( rHint.GetId() == SfxHintId::ThisIsAnSdrHint ? static_cast<const SdrHint*>(&rHint) : nullptr );
+
+ if (bDataChg) { // StyleSheet changed
+ ImpSetAttrToEdgeInfo(); // when changing templates, copy values from Pool to aEdgeInfo
+ }
+ if (bDataChg ||
+ (bObj1 && aCon1.pObj->getSdrPageFromSdrObject() == getSdrPageFromSdrObject()) ||
+ (bObj2 && aCon2.pObj->getSdrPageFromSdrObject() == getSdrPageFromSdrObject()) ||
+ (pSdrHint && pSdrHint->GetKind()==SdrHintKind::ObjectRemoved))
+ {
+ // broadcasting only, if on the same page
+ tools::Rectangle aBoundRect0; if (m_pUserCall!=nullptr) aBoundRect0=GetCurrentBoundRect();
+ ImpDirtyEdgeTrack();
+
+ // only redraw here, object hasn't actually changed
+ ActionChanged();
+
+ SendUserCall(SdrUserCallType::Resize,aBoundRect0);
+ }
+ nNotifyingCount--;
+}
+
+/** updates edges that are connected to the edges of this object
+ as if the connected objects sent a repaint broadcast
+*/
+void SdrEdgeObj::Reformat()
+{
+ if( nullptr != aCon1.pObj )
+ {
+ SfxHint aHint( SfxHintId::DataChanged );
+ Notify( *const_cast<SfxBroadcaster*>(aCon1.pObj->GetBroadcaster()), aHint );
+ }
+
+ if( nullptr != aCon2.pObj )
+ {
+ SfxHint aHint( SfxHintId::DataChanged );
+ Notify( *const_cast<SfxBroadcaster*>(aCon2.pObj->GetBroadcaster()), aHint );
+ }
+}
+
+SdrEdgeObj* SdrEdgeObj::CloneSdrObject(SdrModel& rTargetModel) const
+{
+ return new SdrEdgeObj(rTargetModel, *this);
+}
+
+OUString SdrEdgeObj::TakeObjNameSingul() const
+{
+ OUString sName(SvxResId(STR_ObjNameSingulEDGE));
+
+ OUString aName(GetName());
+ if (!aName.isEmpty())
+ sName += " '" + aName + "'";
+ return sName;
+}
+
+OUString SdrEdgeObj::TakeObjNamePlural() const
+{
+ return SvxResId(STR_ObjNamePluralEDGE);
+}
+
+basegfx::B2DPolyPolygon SdrEdgeObj::TakeXorPoly() const
+{
+ basegfx::B2DPolyPolygon aPolyPolygon;
+
+ if (bEdgeTrackDirty)
+ {
+ const_cast<SdrEdgeObj*>(this)->ImpRecalcEdgeTrack();
+ }
+
+ if(pEdgeTrack)
+ {
+ aPolyPolygon.append(pEdgeTrack->getB2DPolygon());
+ }
+
+ return aPolyPolygon;
+}
+
+void SdrEdgeObj::SetEdgeTrackPath( const basegfx::B2DPolyPolygon& rPoly )
+{
+ if ( !rPoly.count() )
+ {
+ bEdgeTrackDirty = true;
+ bEdgeTrackUserDefined = false;
+ }
+ else
+ {
+ *pEdgeTrack = XPolygon( rPoly.getB2DPolygon( 0 ) );
+ bEdgeTrackDirty = false;
+ bEdgeTrackUserDefined = true;
+
+ // #i110629# also set aRect and maSnapeRect depending on pEdgeTrack
+ const tools::Rectangle aPolygonBounds(pEdgeTrack->GetBoundRect());
+ maRect = aPolygonBounds;
+ maSnapRect = aPolygonBounds;
+ }
+}
+
+basegfx::B2DPolyPolygon SdrEdgeObj::GetEdgeTrackPath() const
+{
+ basegfx::B2DPolyPolygon aPolyPolygon;
+
+ if (bEdgeTrackDirty)
+ const_cast<SdrEdgeObj*>(this)->ImpRecalcEdgeTrack();
+
+ aPolyPolygon.append( pEdgeTrack->getB2DPolygon() );
+
+ return aPolyPolygon;
+}
+
+sal_uInt32 SdrEdgeObj::GetHdlCount() const
+{
+ SdrEdgeKind eKind=GetObjectItem(SDRATTR_EDGEKIND).GetValue();
+ sal_uInt32 nHdlCnt(0);
+ sal_uInt32 nPointCount(pEdgeTrack->GetPointCount());
+
+ if(nPointCount)
+ {
+ nHdlCnt = 2;
+ if ((eKind==SdrEdgeKind::OrthoLines || eKind==SdrEdgeKind::Bezier) && nPointCount >= 4)
+ {
+ sal_uInt32 nO1(aEdgeInfo.nObj1Lines > 0 ? aEdgeInfo.nObj1Lines - 1 : 0);
+ sal_uInt32 nO2(aEdgeInfo.nObj2Lines > 0 ? aEdgeInfo.nObj2Lines - 1 : 0);
+ sal_uInt32 nM(aEdgeInfo.nMiddleLine != 0xFFFF ? 1 : 0);
+ nHdlCnt += nO1 + nO2 + nM;
+ }
+ else if (eKind==SdrEdgeKind::ThreeLines && nPointCount == 4)
+ {
+ if(GetConnectedNode(true))
+ nHdlCnt++;
+
+ if(GetConnectedNode(false))
+ nHdlCnt++;
+ }
+ }
+
+ return nHdlCnt;
+}
+
+void SdrEdgeObj::AddToHdlList(SdrHdlList& rHdlList) const
+{
+ sal_uInt32 nPointCount(pEdgeTrack->GetPointCount());
+ if (nPointCount==0)
+ return;
+
+ {
+ std::unique_ptr<SdrHdl> pHdl(new ImpEdgeHdl((*pEdgeTrack)[0],SdrHdlKind::Poly));
+ if (aCon1.pObj!=nullptr && aCon1.bBestVertex) pHdl->Set1PixMore();
+ pHdl->SetPointNum(0);
+ rHdlList.AddHdl(std::move(pHdl));
+ }
+ {
+ std::unique_ptr<SdrHdl> pHdl(new ImpEdgeHdl((*pEdgeTrack)[sal_uInt16(nPointCount-1)],SdrHdlKind::Poly));
+ if (aCon2.pObj!=nullptr && aCon2.bBestVertex) pHdl->Set1PixMore();
+ pHdl->SetPointNum(1);
+ rHdlList.AddHdl(std::move(pHdl));
+ }
+ {
+ SdrEdgeKind eKind=GetObjectItem(SDRATTR_EDGEKIND).GetValue();
+ if ((eKind==SdrEdgeKind::OrthoLines || eKind==SdrEdgeKind::Bezier) && nPointCount >= 4)
+ {
+ sal_uInt32 nO1(aEdgeInfo.nObj1Lines > 0 ? aEdgeInfo.nObj1Lines - 1 : 0);
+ sal_uInt32 nO2(aEdgeInfo.nObj2Lines > 0 ? aEdgeInfo.nObj2Lines - 1 : 0);
+ sal_uInt32 nM(aEdgeInfo.nMiddleLine != 0xFFFF ? 1 : 0);
+ for(sal_uInt32 i = 0; i < (nO1 + nO2 + nM); ++i)
+ {
+ sal_Int32 nPt(0);
+ sal_uInt32 nNum = i;
+ std::unique_ptr<ImpEdgeHdl> pHdl(new ImpEdgeHdl(Point(),SdrHdlKind::Poly));
+ if (nNum<nO1) {
+ nPt=nNum+1;
+ if (nNum==0) pHdl->SetLineCode(SdrEdgeLineCode::Obj1Line2);
+ if (nNum==1) pHdl->SetLineCode(SdrEdgeLineCode::Obj1Line3);
+ } else {
+ nNum=nNum-nO1;
+ if (nNum<nO2) {
+ nPt=nPointCount-3-nNum;
+ if (nNum==0) pHdl->SetLineCode(SdrEdgeLineCode::Obj2Line2);
+ if (nNum==1) pHdl->SetLineCode(SdrEdgeLineCode::Obj2Line3);
+ } else {
+ nNum=nNum-nO2;
+ if (nNum<nM) {
+ nPt=aEdgeInfo.nMiddleLine;
+ pHdl->SetLineCode(SdrEdgeLineCode::MiddleLine);
+ }
+ }
+ }
+ if (nPt>0) {
+ Point aPos((*pEdgeTrack)[static_cast<sal_uInt16>(nPt)]);
+ aPos+=(*pEdgeTrack)[static_cast<sal_uInt16>(nPt)+1];
+ aPos.setX( aPos.X() / 2 );
+ aPos.setY( aPos.Y() / 2 );
+ pHdl->SetPos(aPos);
+ pHdl->SetPointNum(i + 2);
+ rHdlList.AddHdl(std::move(pHdl));
+ }
+ }
+ }
+ else if (eKind==SdrEdgeKind::ThreeLines && nPointCount == 4)
+ {
+ if(GetConnectedNode(true))
+ {
+ Point aPos((*pEdgeTrack)[1]);
+ std::unique_ptr<ImpEdgeHdl> pHdl(new ImpEdgeHdl(aPos,SdrHdlKind::Poly));
+ pHdl->SetLineCode(SdrEdgeLineCode::Obj1Line2);
+ pHdl->SetPointNum(2);
+ rHdlList.AddHdl(std::move(pHdl));
+ }
+ if(GetConnectedNode(false))
+ {
+ Point aPos((*pEdgeTrack)[2]);
+ std::unique_ptr<ImpEdgeHdl> pHdl(new ImpEdgeHdl(aPos,SdrHdlKind::Poly));
+ pHdl->SetLineCode(SdrEdgeLineCode::Obj2Line2);
+ pHdl->SetPointNum(3);
+ rHdlList.AddHdl(std::move(pHdl));
+ }
+ }
+ }
+}
+
+bool SdrEdgeObj::hasSpecialDrag() const
+{
+ return true;
+}
+
+SdrObjectUniquePtr SdrEdgeObj::getFullDragClone() const
+{
+ // use Clone operator
+ SdrEdgeObj* pRetval(CloneSdrObject(getSdrModelFromSdrObject()));
+
+ // copy connections for clone, SdrEdgeObj::operator= does not do this
+ pRetval->ConnectToNode(true, GetConnectedNode(true));
+ pRetval->ConnectToNode(false, GetConnectedNode(false));
+
+ return SdrObjectUniquePtr(pRetval);
+}
+
+bool SdrEdgeObj::beginSpecialDrag(SdrDragStat& rDrag) const
+{
+ if(!rDrag.GetHdl())
+ return false;
+
+ rDrag.SetEndDragChangesAttributes(true);
+
+ if(rDrag.GetHdl()->GetPointNum() < 2)
+ {
+ rDrag.SetNoSnap();
+ }
+
+ return true;
+}
+
+bool SdrEdgeObj::applySpecialDrag(SdrDragStat& rDragStat)
+{
+ SdrEdgeObj* pOriginalEdge = dynamic_cast< SdrEdgeObj* >(rDragStat.GetHdl()->GetObj());
+ const bool bOriginalEdgeModified(pOriginalEdge == this);
+
+ if(!bOriginalEdgeModified && pOriginalEdge)
+ {
+ // copy connections when clone is modified. This is needed because
+ // as preparation to this modification the data from the original object
+ // was copied to the clone using the operator=. As can be seen there,
+ // that operator does not copy the connections (for good reason)
+ ConnectToNode(true, pOriginalEdge->GetConnection(true).GetObject());
+ ConnectToNode(false, pOriginalEdge->GetConnection(false).GetObject());
+ }
+
+ if(rDragStat.GetHdl()->GetPointNum() < 2)
+ {
+ // start or end point connector drag
+ const bool bDragA(0 == rDragStat.GetHdl()->GetPointNum());
+ const Point aPointNow(rDragStat.GetNow());
+
+ rDragStat.SetEndDragChangesGeoAndAttributes(true);
+
+ if(rDragStat.GetPageView())
+ {
+ SdrObjConnection* pDraggedOne(bDragA ? &aCon1 : &aCon2);
+
+ // clear connection
+ DisconnectFromNode(bDragA);
+
+ // look for new connection
+ ImpFindConnector(aPointNow, *rDragStat.GetPageView(), *pDraggedOne, pOriginalEdge, nullptr, &rDragStat);
+
+ if(pDraggedOne->pObj)
+ {
+ // if found, officially connect to it; ImpFindConnector only
+ // sets pObj hard
+ SdrObject* pNewConnection = pDraggedOne->pObj;
+ pDraggedOne->pObj = nullptr;
+ ConnectToNode(bDragA, pNewConnection);
+ }
+
+ if(rDragStat.GetView() && !bOriginalEdgeModified)
+ {
+ // show IA helper, but only do this during IA, so not when the original
+ // Edge gets modified in the last call
+ rDragStat.GetView()->SetConnectMarker(*pDraggedOne);
+ }
+ }
+
+ if(pEdgeTrack)
+ {
+ // change pEdgeTrack to modified position
+ if(bDragA)
+ {
+ (*pEdgeTrack)[0] = aPointNow;
+ }
+ else
+ {
+ (*pEdgeTrack)[sal_uInt16(pEdgeTrack->GetPointCount()-1)] = aPointNow;
+ }
+ }
+
+ // reset edge info's offsets, this is an end point drag
+ aEdgeInfo.aObj1Line2 = Point();
+ aEdgeInfo.aObj1Line3 = Point();
+ aEdgeInfo.aObj2Line2 = Point();
+ aEdgeInfo.aObj2Line3 = Point();
+ aEdgeInfo.aMiddleLine = Point();
+ }
+ else
+ {
+ // control point connector drag
+ const ImpEdgeHdl* pEdgeHdl = static_cast<const ImpEdgeHdl*>(rDragStat.GetHdl());
+ const SdrEdgeLineCode eLineCode = pEdgeHdl->GetLineCode();
+ const Point aDist(rDragStat.GetNow() - rDragStat.GetStart());
+ sal_Int32 nDist(pEdgeHdl->IsHorzDrag() ? aDist.X() : aDist.Y());
+
+ nDist += aEdgeInfo.ImpGetLineOffset(eLineCode, *pEdgeTrack);
+ aEdgeInfo.ImpSetLineOffset(eLineCode, *pEdgeTrack, nDist);
+ }
+
+ // force recalculation of EdgeTrack
+ *pEdgeTrack = ImpCalcEdgeTrack(*pEdgeTrack, aCon1, aCon2, &aEdgeInfo);
+ bEdgeTrackDirty=false;
+
+ // save EdgeInfos and mark object as user modified
+ ImpSetEdgeInfoToAttr();
+ bEdgeTrackUserDefined = false;
+
+ SetBoundAndSnapRectsDirty();
+
+ if(bOriginalEdgeModified && rDragStat.GetView())
+ {
+ // hide connect marker helper again when original gets changed.
+ // This happens at the end of the interaction
+ rDragStat.GetView()->HideConnectMarker();
+ }
+
+ return true;
+}
+
+OUString SdrEdgeObj::getSpecialDragComment(const SdrDragStat& rDrag) const
+{
+ const bool bCreateComment(rDrag.GetView() && this == rDrag.GetView()->GetCreateObj());
+
+ if(bCreateComment)
+ {
+ return OUString();
+ }
+ else
+ {
+ return ImpGetDescriptionStr(STR_DragEdgeTail);
+ }
+}
+
+
+basegfx::B2DPolygon SdrEdgeObj::ImplAddConnectorOverlay(const SdrDragMethod& rDragMethod, bool bTail1, bool bTail2, bool bDetail) const
+{
+ basegfx::B2DPolygon aResult;
+
+ if(bDetail)
+ {
+ SdrObjConnection aMyCon1(aCon1);
+ SdrObjConnection aMyCon2(aCon2);
+
+ if (bTail1)
+ {
+ const basegfx::B2DPoint aTemp(rDragMethod.getCurrentTransformation() * basegfx::B2DPoint(aMyCon1.aObjOfs.X(), aMyCon1.aObjOfs.Y()));
+ aMyCon1.aObjOfs.setX( basegfx::fround(aTemp.getX()) );
+ aMyCon1.aObjOfs.setY( basegfx::fround(aTemp.getY()) );
+ }
+
+ if (bTail2)
+ {
+ const basegfx::B2DPoint aTemp(rDragMethod.getCurrentTransformation() * basegfx::B2DPoint(aMyCon2.aObjOfs.X(), aMyCon2.aObjOfs.Y()));
+ aMyCon2.aObjOfs.setX( basegfx::fround(aTemp.getX()) );
+ aMyCon2.aObjOfs.setY( basegfx::fround(aTemp.getY()) );
+ }
+
+ SdrEdgeInfoRec aInfo(aEdgeInfo);
+ XPolygon aXP(ImpCalcEdgeTrack(*pEdgeTrack, aMyCon1, aMyCon2, &aInfo));
+
+ if(aXP.GetPointCount())
+ {
+ aResult = aXP.getB2DPolygon();
+ }
+ }
+ else
+ {
+ Point aPt1((*pEdgeTrack)[0]);
+ Point aPt2((*pEdgeTrack)[sal_uInt16(pEdgeTrack->GetPointCount() - 1)]);
+
+ if (aCon1.pObj && (aCon1.bBestConn || aCon1.bBestVertex))
+ aPt1 = aCon1.pObj->GetSnapRect().Center();
+
+ if (aCon2.pObj && (aCon2.bBestConn || aCon2.bBestVertex))
+ aPt2 = aCon2.pObj->GetSnapRect().Center();
+
+ if (bTail1)
+ {
+ const basegfx::B2DPoint aTemp(rDragMethod.getCurrentTransformation() * basegfx::B2DPoint(aPt1.X(), aPt1.Y()));
+ aPt1.setX( basegfx::fround(aTemp.getX()) );
+ aPt1.setY( basegfx::fround(aTemp.getY()) );
+ }
+
+ if (bTail2)
+ {
+ const basegfx::B2DPoint aTemp(rDragMethod.getCurrentTransformation() * basegfx::B2DPoint(aPt2.X(), aPt2.Y()));
+ aPt2.setX( basegfx::fround(aTemp.getX()) );
+ aPt2.setY( basegfx::fround(aTemp.getY()) );
+ }
+
+ aResult.append(basegfx::B2DPoint(aPt1.X(), aPt1.Y()));
+ aResult.append(basegfx::B2DPoint(aPt2.X(), aPt2.Y()));
+ }
+
+ return aResult;
+}
+
+bool SdrEdgeObj::BegCreate(SdrDragStat& rDragStat)
+{
+ rDragStat.SetNoSnap();
+ pEdgeTrack->SetPointCount(2);
+ (*pEdgeTrack)[0]=rDragStat.GetStart();
+ (*pEdgeTrack)[1]=rDragStat.GetNow();
+ if (rDragStat.GetPageView()!=nullptr) {
+ ImpFindConnector(rDragStat.GetStart(),*rDragStat.GetPageView(),aCon1,this);
+ ConnectToNode(true,aCon1.pObj);
+ }
+ *pEdgeTrack=ImpCalcEdgeTrack(*pEdgeTrack,aCon1,aCon2,&aEdgeInfo);
+ return true;
+}
+
+bool SdrEdgeObj::MovCreate(SdrDragStat& rDragStat)
+{
+ sal_uInt16 nMax=pEdgeTrack->GetPointCount();
+ (*pEdgeTrack)[nMax-1]=rDragStat.GetNow();
+ if (rDragStat.GetPageView()!=nullptr) {
+ ImpFindConnector(rDragStat.GetNow(),*rDragStat.GetPageView(),aCon2,this);
+ rDragStat.GetView()->SetConnectMarker(aCon2);
+ }
+ SetBoundRectDirty();
+ m_bSnapRectDirty=true;
+ ConnectToNode(false,aCon2.pObj);
+ *pEdgeTrack=ImpCalcEdgeTrack(*pEdgeTrack,aCon1,aCon2,&aEdgeInfo);
+ bEdgeTrackDirty=false;
+ return true;
+}
+
+bool SdrEdgeObj::EndCreate(SdrDragStat& rDragStat, SdrCreateCmd eCmd)
+{
+ bool bOk=(eCmd==SdrCreateCmd::ForceEnd || rDragStat.GetPointCount()>=2);
+ if (bOk) {
+ ConnectToNode(true,aCon1.pObj);
+ ConnectToNode(false,aCon2.pObj);
+ if (rDragStat.GetView()!=nullptr) {
+ rDragStat.GetView()->HideConnectMarker();
+ }
+ ImpSetEdgeInfoToAttr(); // copy values from aEdgeInfo into the pool
+ }
+ SetBoundAndSnapRectsDirty();
+ return bOk;
+}
+
+bool SdrEdgeObj::BckCreate(SdrDragStat& rDragStat)
+{
+ if (rDragStat.GetView()!=nullptr) {
+ rDragStat.GetView()->HideConnectMarker();
+ }
+ return false;
+}
+
+void SdrEdgeObj::BrkCreate(SdrDragStat& rDragStat)
+{
+ if (rDragStat.GetView()!=nullptr) {
+ rDragStat.GetView()->HideConnectMarker();
+ }
+}
+
+basegfx::B2DPolyPolygon SdrEdgeObj::TakeCreatePoly(const SdrDragStat& /*rStatDrag*/) const
+{
+ basegfx::B2DPolyPolygon aRetval;
+ aRetval.append(pEdgeTrack->getB2DPolygon());
+ return aRetval;
+}
+
+PointerStyle SdrEdgeObj::GetCreatePointer() const
+{
+ return PointerStyle::DrawConnect;
+}
+
+bool SdrEdgeObj::ImpFindConnector(const Point& rPt, const SdrPageView& rPV, SdrObjConnection& rCon, const SdrEdgeObj* pThis, OutputDevice* pOut, SdrDragStat* pDragStat)
+{
+ rCon.ResetVars();
+ if (pOut==nullptr) pOut=rPV.GetView().GetFirstOutputDevice();
+ if (pOut==nullptr) return false;
+ SdrObjList* pOL=rPV.GetObjList();
+ const SdrLayerIDSet& rVisLayer=rPV.GetVisibleLayers();
+ // sensitive area of connectors is twice as large as the one of the handles
+ sal_uInt16 nMarkHdSiz=rPV.GetView().GetMarkHdlSizePixel();
+ Size aHalfConSiz(nMarkHdSiz,nMarkHdSiz);
+ if (comphelper::LibreOfficeKit::isActive() && pOut->GetMapMode().GetMapUnit() == MapUnit::Map100thMM)
+ aHalfConSiz=pOut->PixelToLogic(aHalfConSiz, MapMode(MapUnit::Map100thMM));
+ else
+ aHalfConSiz=pOut->PixelToLogic(aHalfConSiz);
+ tools::Rectangle aMouseRect(rPt,rPt);
+ aMouseRect.AdjustLeft( -(aHalfConSiz.Width()) );
+ aMouseRect.AdjustTop( -(aHalfConSiz.Height()) );
+ aMouseRect.AdjustRight(aHalfConSiz.Width() );
+ aMouseRect.AdjustBottom(aHalfConSiz.Height() );
+ sal_uInt16 nBoundHitTol=static_cast<sal_uInt16>(aHalfConSiz.Width())/2; if (nBoundHitTol==0) nBoundHitTol=1;
+ size_t no=pOL->GetObjCount();
+ bool bFnd = false;
+ SdrObjConnection aTestCon;
+ bool bTiledRendering = comphelper::LibreOfficeKit::isActive();
+ bool bHasRequestedOrdNum = false;
+ sal_Int32 requestedOrdNum = -1;
+
+ if (bTiledRendering && pDragStat)
+ {
+ auto& glueOptions = pDragStat->GetGlueOptions();
+ if (glueOptions.objectOrdNum != -1)
+ {
+ requestedOrdNum = glueOptions.objectOrdNum;
+ bHasRequestedOrdNum = true;
+ }
+ }
+
+ while (no>0 && !bFnd) {
+ // issue: group objects on different layers return LayerID=0!
+ no--;
+ SdrObject* pObj=pOL->GetObj(no);
+ if (bHasRequestedOrdNum)
+ {
+ if (pObj->GetOrdNumDirect() != static_cast<sal_uInt32>(requestedOrdNum))
+ continue;
+ }
+ if (rVisLayer.IsSet(pObj->GetLayer()) && pObj->IsVisible() && // only visible objects
+ (pThis==nullptr || pObj!=static_cast<SdrObject const *>(pThis))) // don't connect it to itself
+ {
+ tools::Rectangle aObjBound(pObj->GetCurrentBoundRect());
+ if (aObjBound.Overlaps(aMouseRect)) {
+ aTestCon.ResetVars();
+ bool bEdge=dynamic_cast<const SdrEdgeObj *>(pObj) != nullptr; // no BestCon for Edge
+ // User-defined connectors have absolute priority.
+ // After those come Vertex, Corner and center (Best), all prioritized equally.
+ // Finally, a HitTest for the object.
+ const SdrGluePointList* pGPL=pObj->GetGluePointList();
+ sal_uInt16 nGluePointCnt=pGPL==nullptr ? 0 : pGPL->GetCount();
+ sal_uInt16 nGesAnz=nGluePointCnt+9;
+ bool bUserFnd = false;
+ sal_uIntPtr nBestDist=0xFFFFFFFF;
+ for (sal_uInt16 i=0; i<nGesAnz; i++)
+ {
+ bool bUser=i<nGluePointCnt;
+ bool bVertex=i>=nGluePointCnt+0 && i<nGluePointCnt+4;
+ bool bCorner=i>=nGluePointCnt+4 && i<nGluePointCnt+8;
+ bool bCenter=i==nGluePointCnt+8;
+ bool bOk = false;
+ Point aConPos;
+ sal_uInt16 nConNum=i;
+ if (bUser) {
+ const SdrGluePoint& rGP=(*pGPL)[nConNum];
+ aConPos=rGP.GetAbsolutePos(*pObj);
+ nConNum=rGP.GetId();
+ bOk = true;
+ } else if (bVertex && !bUserFnd) {
+ nConNum=nConNum-nGluePointCnt;
+ SdrGluePoint aPt(pObj->GetVertexGluePoint(nConNum));
+ aConPos=aPt.GetAbsolutePos(*pObj);
+ bOk = true;
+ } else if (bCorner && !bUserFnd) {
+ nConNum-=nGluePointCnt+4;
+ i+=3;
+ }
+ else if (bCenter && !bUserFnd && !bEdge)
+ {
+ // Suppress default connect at object center
+ if(!pThis || !pThis->GetSuppressDefaultConnect())
+ {
+ // not the edges!
+ nConNum=0;
+ aConPos=aObjBound.Center();
+ bOk = true;
+ }
+ }
+ if (bOk && aMouseRect.Contains(aConPos)) {
+ if (bUser) bUserFnd = true;
+ bFnd = true;
+ sal_uIntPtr nDist=static_cast<sal_uIntPtr>(std::abs(aConPos.X()-rPt.X()))+static_cast<sal_uIntPtr>(std::abs(aConPos.Y()-rPt.Y()));
+ if (nDist<nBestDist) {
+ nBestDist=nDist;
+ aTestCon.pObj=pObj;
+ aTestCon.nConId=nConNum;
+ aTestCon.bAutoCorner=bCorner;
+ aTestCon.bAutoVertex=bVertex;
+ aTestCon.bBestConn=false; // bCenter;
+ aTestCon.bBestVertex=bCenter;
+ }
+ }
+ }
+ // if no connector is hit, try HitTest again, for BestConnector (=bCenter)
+ if(!bFnd &&
+ !bEdge &&
+ SdrObjectPrimitiveHit(*pObj, rPt, nBoundHitTol, rPV, &rVisLayer, false))
+ {
+ // Suppress default connect at object inside bound
+ if(!pThis || !pThis->GetSuppressDefaultConnect())
+ {
+ bFnd = true;
+ aTestCon.pObj=pObj;
+ aTestCon.bBestConn=true;
+ }
+ }
+ if (bFnd) {
+ aMouseRect.AdjustLeft( -nBoundHitTol );
+ aMouseRect.AdjustTop( -nBoundHitTol );
+ aMouseRect.AdjustRight(nBoundHitTol );
+ aMouseRect.AdjustBottom(nBoundHitTol );
+ }
+
+ }
+ }
+ }
+ rCon=aTestCon;
+ return bFnd;
+}
+
+void SdrEdgeObj::NbcSetSnapRect(const tools::Rectangle& rRect)
+{
+ const tools::Rectangle aOld(GetSnapRect());
+
+ if(aOld == rRect)
+ return;
+
+ if (maRect.IsEmpty() && 0 == pEdgeTrack->GetPointCount())
+ {
+ // #i110629# When initializing, do not scale on empty Rectangle; this
+ // will mirror the underlying text object (!)
+ maRect = rRect;
+ maSnapRect = rRect;
+ }
+ else
+ {
+ tools::Long nMulX = rRect.Right() - rRect.Left();
+ tools::Long nDivX = aOld.Right() - aOld.Left();
+ tools::Long nMulY = rRect.Bottom() - rRect.Top();
+ tools::Long nDivY = aOld.Bottom() - aOld.Top();
+ if ( nDivX == 0 ) { nMulX = 1; nDivX = 1; }
+ if ( nDivY == 0 ) { nMulY = 1; nDivY = 1; }
+ Fraction aX(nMulX, nDivX);
+ Fraction aY(nMulY, nDivY);
+ NbcResize(aOld.TopLeft(), aX, aY);
+ NbcMove(Size(rRect.Left() - aOld.Left(), rRect.Top() - aOld.Top()));
+ }
+}
+
+void SdrEdgeObj::NbcMove(const Size& rSiz)
+{
+ SdrTextObj::NbcMove(rSiz);
+ MoveXPoly(*pEdgeTrack,rSiz);
+}
+
+void SdrEdgeObj::NbcResize(const Point& rRefPnt, const Fraction& aXFact, const Fraction& aYFact)
+{
+ SdrTextObj::NbcResize(rRefPnt,aXFact,aXFact);
+ ResizeXPoly(*pEdgeTrack,rRefPnt,aXFact,aYFact);
+
+ // if resize is not from paste, forget user distances
+ if (!getSdrModelFromSdrObject().IsPasteResize())
+ {
+ aEdgeInfo.aObj1Line2 = Point();
+ aEdgeInfo.aObj1Line3 = Point();
+ aEdgeInfo.aObj2Line2 = Point();
+ aEdgeInfo.aObj2Line3 = Point();
+ aEdgeInfo.aMiddleLine = Point();
+ }
+}
+
+// #i54102# added rotation support
+void SdrEdgeObj::NbcRotate(const Point& rRef, Degree100 nAngle, double sn, double cs)
+{
+ if(bEdgeTrackUserDefined)
+ {
+ // #i120437# special handling when track is imported, apply
+ // transformation directly to imported track.
+ SdrTextObj::NbcRotate(rRef, nAngle, sn, cs);
+ RotateXPoly(*pEdgeTrack, rRef, sn, cs);
+ }
+ else
+ {
+ // handle start and end point if not connected
+ const bool bCon1(nullptr != aCon1.pObj && aCon1.pObj->getSdrPageFromSdrObject() == getSdrPageFromSdrObject());
+ const bool bCon2(nullptr != aCon2.pObj && aCon2.pObj->getSdrPageFromSdrObject() == getSdrPageFromSdrObject());
+
+ if(!bCon1 && pEdgeTrack)
+ {
+ RotatePoint((*pEdgeTrack)[0],rRef,sn,cs);
+ ImpDirtyEdgeTrack();
+ }
+
+ if(!bCon2 && pEdgeTrack)
+ {
+ sal_uInt16 nPointCount = pEdgeTrack->GetPointCount();
+ RotatePoint((*pEdgeTrack)[sal_uInt16(nPointCount-1)],rRef,sn,cs);
+ ImpDirtyEdgeTrack();
+ }
+ }
+}
+
+// #i54102# added mirror support
+void SdrEdgeObj::NbcMirror(const Point& rRef1, const Point& rRef2)
+{
+ if(bEdgeTrackUserDefined)
+ {
+ // #i120437# special handling when track is imported, apply
+ // transformation directly to imported track.
+ SdrTextObj::NbcMirror(rRef1, rRef2);
+ MirrorXPoly(*pEdgeTrack, rRef1, rRef2);
+ }
+ else
+ {
+ // handle start and end point if not connected
+ const bool bCon1(nullptr != aCon1.pObj && aCon1.pObj->getSdrPageFromSdrObject() == getSdrPageFromSdrObject());
+ const bool bCon2(nullptr != aCon2.pObj && aCon2.pObj->getSdrPageFromSdrObject() == getSdrPageFromSdrObject());
+
+ if(!bCon1 && pEdgeTrack)
+ {
+ MirrorPoint((*pEdgeTrack)[0],rRef1,rRef2);
+ ImpDirtyEdgeTrack();
+ }
+
+ if(!bCon2 && pEdgeTrack)
+ {
+ sal_uInt16 nPointCount = pEdgeTrack->GetPointCount();
+ MirrorPoint((*pEdgeTrack)[sal_uInt16(nPointCount-1)],rRef1,rRef2);
+ ImpDirtyEdgeTrack();
+ }
+ }
+}
+
+// #i54102# added shear support
+void SdrEdgeObj::NbcShear(const Point& rRef, Degree100 nAngle, double tn, bool bVShear)
+{
+ if(bEdgeTrackUserDefined)
+ {
+ // #i120437# special handling when track is imported, apply
+ // transformation directly to imported track.
+ SdrTextObj::NbcShear(rRef, nAngle, tn, bVShear);
+ ShearXPoly(*pEdgeTrack, rRef, tn, bVShear);
+ }
+ else
+ {
+ // handle start and end point if not connected
+ const bool bCon1(nullptr != aCon1.pObj && aCon1.pObj->getSdrPageFromSdrObject() == getSdrPageFromSdrObject());
+ const bool bCon2(nullptr != aCon2.pObj && aCon2.pObj->getSdrPageFromSdrObject() == getSdrPageFromSdrObject());
+
+ if(!bCon1 && pEdgeTrack)
+ {
+ ShearPoint((*pEdgeTrack)[0],rRef,tn,bVShear);
+ ImpDirtyEdgeTrack();
+ }
+
+ if(!bCon2 && pEdgeTrack)
+ {
+ sal_uInt16 nPointCount = pEdgeTrack->GetPointCount();
+ ShearPoint((*pEdgeTrack)[sal_uInt16(nPointCount-1)],rRef,tn,bVShear);
+ ImpDirtyEdgeTrack();
+ }
+ }
+}
+
+SdrObjectUniquePtr SdrEdgeObj::DoConvertToPolyObj(bool bBezier, bool bAddText) const
+{
+ basegfx::B2DPolyPolygon aPolyPolygon;
+ aPolyPolygon.append(pEdgeTrack->getB2DPolygon());
+ SdrObjectUniquePtr pRet = ImpConvertMakeObj(aPolyPolygon, false, bBezier);
+
+ if(bAddText)
+ {
+ pRet = ImpConvertAddText(std::move(pRet), bBezier);
+ }
+
+ return pRet;
+}
+
+sal_uInt32 SdrEdgeObj::GetSnapPointCount() const
+{
+ return 2;
+}
+
+Point SdrEdgeObj::GetSnapPoint(sal_uInt32 i) const
+{
+ const_cast<SdrEdgeObj*>(this)->ImpUndirtyEdgeTrack();
+ sal_uInt16 nCount=pEdgeTrack->GetPointCount();
+ if (i==0) return (*pEdgeTrack)[0];
+ else return (*pEdgeTrack)[nCount-1];
+}
+
+bool SdrEdgeObj::IsPolyObj() const
+{
+ return false;
+}
+
+sal_uInt32 SdrEdgeObj::GetPointCount() const
+{
+ return 0;
+}
+
+Point SdrEdgeObj::GetPoint(sal_uInt32 i) const
+{
+ const_cast<SdrEdgeObj*>(this)->ImpUndirtyEdgeTrack();
+ sal_uInt16 nCount=pEdgeTrack->GetPointCount();
+ if (0 == i)
+ return (*pEdgeTrack)[0];
+ else
+ return (*pEdgeTrack)[nCount-1];
+}
+
+void SdrEdgeObj::NbcSetPoint(const Point& rPnt, sal_uInt32 i)
+{
+ // TODO: Need an implementation to connect differently.
+ ImpUndirtyEdgeTrack();
+ sal_uInt16 nCount=pEdgeTrack->GetPointCount();
+ if (0 == i)
+ (*pEdgeTrack)[0]=rPnt;
+ if (1 == i)
+ (*pEdgeTrack)[nCount-1]=rPnt;
+ SetEdgeTrackDirty();
+ SetBoundAndSnapRectsDirty();
+}
+
+SdrEdgeObjGeoData::SdrEdgeObjGeoData()
+ : pEdgeTrack(std::in_place)
+ , bEdgeTrackDirty(false)
+ , bEdgeTrackUserDefined(false)
+{
+}
+
+SdrEdgeObjGeoData::~SdrEdgeObjGeoData()
+{
+}
+
+std::unique_ptr<SdrObjGeoData> SdrEdgeObj::NewGeoData() const
+{
+ return std::make_unique<SdrEdgeObjGeoData>();
+}
+
+void SdrEdgeObj::SaveGeoData(SdrObjGeoData& rGeo) const
+{
+ SdrTextObj::SaveGeoData(rGeo);
+ SdrEdgeObjGeoData& rEGeo=static_cast<SdrEdgeObjGeoData&>(rGeo);
+ rEGeo.aCon1 =aCon1;
+ rEGeo.aCon2 =aCon2;
+ *rEGeo.pEdgeTrack =*pEdgeTrack;
+ rEGeo.bEdgeTrackDirty=bEdgeTrackDirty;
+ rEGeo.bEdgeTrackUserDefined=bEdgeTrackUserDefined;
+ rEGeo.aEdgeInfo =aEdgeInfo;
+}
+
+void SdrEdgeObj::RestoreGeoData(const SdrObjGeoData& rGeo)
+{
+ SdrTextObj::RestoreGeoData(rGeo);
+ const SdrEdgeObjGeoData& rEGeo=static_cast<const SdrEdgeObjGeoData&>(rGeo);
+ if (aCon1.pObj!=rEGeo.aCon1.pObj) {
+ if (aCon1.pObj!=nullptr) aCon1.pObj->RemoveListener(*this);
+ aCon1=rEGeo.aCon1;
+ if (aCon1.pObj!=nullptr) aCon1.pObj->AddListener(*this);
+ }
+ else
+ aCon1=rEGeo.aCon1;
+
+ if (aCon2.pObj!=rEGeo.aCon2.pObj) {
+ if (aCon2.pObj!=nullptr) aCon2.pObj->RemoveListener(*this);
+ aCon2=rEGeo.aCon2;
+ if (aCon2.pObj!=nullptr) aCon2.pObj->AddListener(*this);
+ }
+ else
+ aCon2=rEGeo.aCon2;
+
+ *pEdgeTrack =*rEGeo.pEdgeTrack;
+ bEdgeTrackDirty=rEGeo.bEdgeTrackDirty;
+ bEdgeTrackUserDefined=rEGeo.bEdgeTrackUserDefined;
+ aEdgeInfo =rEGeo.aEdgeInfo;
+}
+
+Point SdrEdgeObj::GetTailPoint( bool bTail ) const
+{
+ if( pEdgeTrack && pEdgeTrack->GetPointCount()!=0)
+ {
+ const XPolygon& rTrack0 = *pEdgeTrack;
+ if(bTail)
+ {
+ return rTrack0[0];
+ }
+ else
+ {
+ const sal_uInt16 nSiz = rTrack0.GetPointCount() - 1;
+ return rTrack0[nSiz];
+ }
+ }
+ else
+ {
+ if(bTail)
+ return m_aOutRect.TopLeft();
+ else
+ return m_aOutRect.BottomRight();
+ }
+
+}
+
+void SdrEdgeObj::SetTailPoint( bool bTail, const Point& rPt )
+{
+ ImpSetTailPoint( bTail, rPt );
+ SetChanged();
+}
+
+/** this method is used by the api to set a gluepoint for a connection
+ nId == -1 : The best default point is automatically chosen
+ 0 <= nId <= 3 : One of the default points is chosen
+ nId >= 4 : A user defined gluepoint is chosen
+*/
+void SdrEdgeObj::setGluePointIndex( bool bTail, sal_Int32 nIndex /* = -1 */ )
+{
+ SdrObjConnection& rConn1 = GetConnection( bTail );
+
+ rConn1.SetAutoVertex( nIndex >= 0 && nIndex <= 3 );
+ rConn1.SetBestConnection( nIndex < 0 );
+ rConn1.SetBestVertex( nIndex < 0 );
+
+ if( nIndex > 3 )
+ {
+ nIndex -= 3; // the start api index is 0, whereas the implementation in svx starts from 1
+
+ // for user defined gluepoints we have
+ // to get the id for this index first
+ const SdrGluePointList* pList = rConn1.GetObject() ? rConn1.GetObject()->GetGluePointList() : nullptr;
+ if( pList == nullptr || SDRGLUEPOINT_NOTFOUND == pList->FindGluePoint(static_cast<sal_uInt16>(nIndex)) )
+ return;
+ }
+ else if( nIndex < 0 )
+ {
+ nIndex = 0;
+ }
+
+ rConn1.SetConnectorId( static_cast<sal_uInt16>(nIndex) );
+
+ SetChanged();
+ SetBoundAndSnapRectsDirty();
+ ImpRecalcEdgeTrack();
+}
+
+/** this method is used by the api to return a gluepoint id for a connection.
+ See setGluePointId for possible return values */
+sal_Int32 SdrEdgeObj::getGluePointIndex( bool bTail )
+{
+ SdrObjConnection& rConn1 = GetConnection( bTail );
+ sal_Int32 nId = -1;
+ if( !rConn1.IsBestConnection() )
+ {
+ nId = rConn1.GetConnectorId();
+ if( !rConn1.IsAutoVertex() )
+ nId += 3; // the start api index is 0, whereas the implementation in svx starts from 1
+ }
+ return nId;
+}
+
+// Implementation was missing; edge track needs to be invalidated additionally.
+void SdrEdgeObj::NbcSetAnchorPos(const Point& rPnt)
+{
+ // call parent functionality
+ SdrTextObj::NbcSetAnchorPos(rPnt);
+
+ // Additionally, invalidate edge track
+ ImpDirtyEdgeTrack();
+}
+
+bool SdrEdgeObj::TRGetBaseGeometry(basegfx::B2DHomMatrix& rMatrix, basegfx::B2DPolyPolygon& rPolyPolygon) const
+{
+ // use base method from SdrObject, it's not rotatable and
+ // a call to GetSnapRect() is used. That's what we need for Connector.
+ return SdrObject::TRGetBaseGeometry(rMatrix, rPolyPolygon);
+}
+
+void SdrEdgeObj::TRSetBaseGeometry(const basegfx::B2DHomMatrix& rMatrix, const basegfx::B2DPolyPolygon& rPolyPolygon)
+{
+ // where appropriate take care for existing connections. For now, just use the
+ // implementation from SdrObject.
+ SdrObject::TRSetBaseGeometry(rMatrix, rPolyPolygon);
+}
+
+// for geometry access
+::basegfx::B2DPolygon SdrEdgeObj::getEdgeTrack() const
+{
+ if(bEdgeTrackDirty)
+ {
+ const_cast< SdrEdgeObj* >(this)->ImpRecalcEdgeTrack();
+ }
+
+ if(pEdgeTrack)
+ {
+ return pEdgeTrack->getB2DPolygon();
+ }
+ else
+ {
+ return ::basegfx::B2DPolygon();
+ }
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/svdraw/svdograf.cxx b/svx/source/svdraw/svdograf.cxx
new file mode 100644
index 000000000..c369c5fcc
--- /dev/null
+++ b/svx/source/svdraw/svdograf.cxx
@@ -0,0 +1,1274 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * 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/streamwrap.hxx>
+
+#include <sfx2/lnkbase.hxx>
+#include <rtl/ustrbuf.hxx>
+#include <tools/helpers.hxx>
+#include <tools/stream.hxx>
+#include <sot/exchange.hxx>
+#include <sot/formats.hxx>
+#include <vcl/GraphicObject.hxx>
+#include <vcl/svapp.hxx>
+
+#include <sfx2/linkmgr.hxx>
+#include <svx/dialmgr.hxx>
+#include <svx/strings.hrc>
+#include <svx/svdhdl.hxx>
+#include <svx/svdmodel.hxx>
+#include <svx/svdpage.hxx>
+#include <svx/svdograf.hxx>
+#include <svx/svdogrp.hxx>
+#include <svx/xbtmpit.hxx>
+#include <svx/xfillit0.hxx>
+#include <svx/xflbmtit.hxx>
+#include "svdfmtf.hxx"
+#include <sdgcoitm.hxx>
+#include <svx/sdgcpitm.hxx>
+#include <svx/sdggaitm.hxx>
+#include <sdginitm.hxx>
+#include <svx/sdgluitm.hxx>
+#include <svx/sdgmoitm.hxx>
+#include <sdgtritm.hxx>
+#include <sdr/properties/graphicproperties.hxx>
+#include <sdr/contact/viewcontactofgraphic.hxx>
+#include <basegfx/matrix/b2dhommatrixtools.hxx>
+#include <drawinglayer/processor2d/objectinfoextractor2d.hxx>
+#include <drawinglayer/primitive2d/objectinfoprimitive2d.hxx>
+#include <memory>
+
+using namespace ::com::sun::star;
+using namespace ::com::sun::star::uno;
+using namespace ::com::sun::star::io;
+
+class SdrGraphicLink : public sfx2::SvBaseLink
+{
+ SdrGrafObj& rGrafObj;
+
+public:
+ explicit SdrGraphicLink(SdrGrafObj& rObj);
+
+ virtual void Closed() override;
+
+ virtual ::sfx2::SvBaseLink::UpdateResult DataChanged(
+ const OUString& rMimeType, const css::uno::Any & rValue ) override;
+
+ void Connect() { GetRealObject(); }
+};
+
+SdrGraphicLink::SdrGraphicLink(SdrGrafObj& rObj)
+: ::sfx2::SvBaseLink( ::SfxLinkUpdateMode::ONCALL, SotClipboardFormatId::SVXB )
+, rGrafObj( rObj )
+{
+ SetSynchron( false );
+}
+
+::sfx2::SvBaseLink::UpdateResult SdrGraphicLink::DataChanged(
+ const OUString& rMimeType, const css::uno::Any & rValue )
+{
+ SdrModel& rModel(rGrafObj.getSdrModelFromSdrObject());
+ sfx2::LinkManager* pLinkManager(rModel.GetLinkManager());
+
+ if( pLinkManager && rValue.hasValue() )
+ {
+ sfx2::LinkManager::GetDisplayNames( this, nullptr, &rGrafObj.aFileName, nullptr, &rGrafObj.aFilterName );
+
+ Graphic aGraphic;
+ if (pLinkManager->GetGraphicFromAny(rMimeType, rValue, aGraphic, nullptr))
+ {
+ rGrafObj.ImpSetLinkedGraphic(aGraphic);
+ }
+ else if( SotExchange::GetFormatIdFromMimeType( rMimeType ) != sfx2::LinkManager::RegisterStatusInfoId() )
+ {
+ // broadcasting, to update slide sorter
+ rGrafObj.BroadcastObjectChange();
+ }
+ }
+ return SUCCESS;
+}
+
+void SdrGraphicLink::Closed()
+{
+ // close connection; set pLink of the object to NULL, as link instance is just about getting destructed.
+ rGrafObj.ForceSwapIn();
+ rGrafObj.pGraphicLink=nullptr;
+ rGrafObj.ReleaseGraphicLink();
+ SvBaseLink::Closed();
+}
+
+std::unique_ptr<sdr::properties::BaseProperties> SdrGrafObj::CreateObjectSpecificProperties()
+{
+ return std::make_unique<sdr::properties::GraphicProperties>(*this);
+}
+
+
+// DrawContact section
+
+std::unique_ptr<sdr::contact::ViewContact> SdrGrafObj::CreateObjectSpecificViewContact()
+{
+ return std::make_unique<sdr::contact::ViewContactOfGraphic>(*this);
+}
+
+
+// check if SVG and if try to get ObjectInfoPrimitive2D and extract info
+
+void SdrGrafObj::onGraphicChanged()
+{
+ if (!mpGraphicObject || !mpGraphicObject->GetGraphic().isAvailable())
+ return;
+
+ auto const & rVectorGraphicDataPtr = mpGraphicObject->GetGraphic().getVectorGraphicData();
+
+ if (!rVectorGraphicDataPtr)
+ return;
+
+ // Skip for PDF as it is only a bitmap primitive in a sequence and
+ // doesn't contain metadata. However getting the primitive sequence
+ // will also trigger a premature rendering of the PDF.
+ if (rVectorGraphicDataPtr->getType() == VectorGraphicDataType::Pdf)
+ return;
+
+ const std::deque<css::uno::Reference<css::graphic::XPrimitive2D>>& rContainer(rVectorGraphicDataPtr->getPrimitive2DSequence());
+
+ if (rContainer.empty())
+ return;
+
+ drawinglayer::geometry::ViewInformation2D aViewInformation2D;
+ drawinglayer::processor2d::ObjectInfoPrimitiveExtractor2D aProcessor(aViewInformation2D);
+
+ aProcessor.process(rContainer);
+
+ const drawinglayer::primitive2d::ObjectInfoPrimitive2D* pResult = aProcessor.getResult();
+
+ if (!pResult)
+ return;
+
+ OUString aName = pResult->getName();
+ OUString aTitle = pResult->getTitle();
+ OUString aDesc = pResult->getDesc();
+
+ if(!aName.isEmpty())
+ {
+ SetName(aName);
+ }
+
+ if(!aTitle.isEmpty())
+ {
+ SetTitle(aTitle);
+ }
+
+ if(!aDesc.isEmpty())
+ {
+ SetDescription(aDesc);
+ }
+}
+
+SdrGrafObj::SdrGrafObj(SdrModel& rSdrModel)
+: SdrRectObj(rSdrModel)
+ ,mpGraphicObject(new GraphicObject)
+ ,pGraphicLink(nullptr)
+ ,bMirrored(false)
+ ,mbIsSignatureLine(false)
+ ,mbIsSignatureLineShowSignDate(true)
+ ,mbIsSignatureLineCanAddComment(false)
+ ,mbSignatureLineIsSigned(false)
+{
+ onGraphicChanged();
+
+ // #i118485# Shear allowed and possible now
+ mbNoShear = false;
+
+ mbGrafAnimationAllowed = true;
+
+ // #i25616#
+ mbLineIsOutsideGeometry = true;
+
+ // #i25616#
+ mbSupportTextIndentingOnLineWidthChange = false;
+}
+
+SdrGrafObj::SdrGrafObj(SdrModel& rSdrModel, SdrGrafObj const & rSource)
+: SdrRectObj(rSdrModel, rSource)
+ ,mpGraphicObject(new GraphicObject)
+ ,pGraphicLink(nullptr)
+{
+ onGraphicChanged();
+
+ // #i118485# Shear allowed and possible now
+ mbNoShear = false;
+
+ mbGrafAnimationAllowed = true;
+
+ // #i25616#
+ mbLineIsOutsideGeometry = true;
+
+ // #i25616#
+ mbSupportTextIndentingOnLineWidthChange = false;
+
+ aFileName = rSource.aFileName;
+ bMirrored = rSource.bMirrored;
+
+ mbIsSignatureLine = rSource.mbIsSignatureLine;
+ maSignatureLineId = rSource.maSignatureLineId;
+ maSignatureLineSuggestedSignerName = rSource.maSignatureLineSuggestedSignerName;
+ maSignatureLineSuggestedSignerTitle = rSource.maSignatureLineSuggestedSignerTitle;
+ maSignatureLineSuggestedSignerEmail = rSource.maSignatureLineSuggestedSignerEmail;
+ maSignatureLineSigningInstructions = rSource.maSignatureLineSigningInstructions;
+ mbIsSignatureLineShowSignDate = rSource.mbIsSignatureLineShowSignDate;
+ mbIsSignatureLineCanAddComment = rSource.mbIsSignatureLineCanAddComment;
+ mbSignatureLineIsSigned = false;
+ mpSignatureLineUnsignedGraphic = rSource.mpSignatureLineUnsignedGraphic;
+
+ if(rSource.mpBarCode)
+ {
+ mpBarCode = std::make_unique<css::drawing::BarCode>(*rSource.mpBarCode);
+ }
+ else
+ {
+ mpBarCode.reset();
+ }
+
+ if (mbIsSignatureLine && rSource.mpSignatureLineUnsignedGraphic)
+ mpGraphicObject->SetGraphic(rSource.mpSignatureLineUnsignedGraphic);
+ else
+ mpGraphicObject->SetGraphic( rSource.GetGraphic() );
+
+ if( rSource.IsLinkedGraphic() )
+ {
+ SetGraphicLink( aFileName );
+ }
+
+ ImpSetAttrToGrafInfo();
+}
+
+SdrGrafObj::SdrGrafObj(
+ SdrModel& rSdrModel,
+ const Graphic& rGraphic,
+ const tools::Rectangle& rRect)
+: SdrRectObj(rSdrModel, rRect)
+ ,mpGraphicObject(new GraphicObject(rGraphic))
+ ,pGraphicLink(nullptr)
+ ,bMirrored(false)
+ ,mbIsSignatureLine(false)
+ ,mbIsSignatureLineShowSignDate(true)
+ ,mbIsSignatureLineCanAddComment(false)
+ ,mbSignatureLineIsSigned(false)
+{
+ onGraphicChanged();
+
+ // #i118485# Shear allowed and possible now
+ mbNoShear = false;
+
+ mbGrafAnimationAllowed = true;
+
+ // #i25616#
+ mbLineIsOutsideGeometry = true;
+
+ // #i25616#
+ mbSupportTextIndentingOnLineWidthChange = false;
+}
+
+SdrGrafObj::SdrGrafObj(
+ SdrModel& rSdrModel,
+ const Graphic& rGraphic)
+: SdrRectObj(rSdrModel)
+ ,mpGraphicObject(new GraphicObject(rGraphic))
+ ,pGraphicLink(nullptr)
+ ,bMirrored(false)
+ ,mbIsSignatureLine(false)
+ ,mbIsSignatureLineShowSignDate(true)
+ ,mbIsSignatureLineCanAddComment(false)
+ ,mbSignatureLineIsSigned(false)
+{
+ onGraphicChanged();
+
+ // #i118485# Shear allowed and possible now
+ mbNoShear = false;
+
+ mbGrafAnimationAllowed = true;
+
+ // #i25616#
+ mbLineIsOutsideGeometry = true;
+
+ // #i25616#
+ mbSupportTextIndentingOnLineWidthChange = false;
+}
+
+SdrGrafObj::~SdrGrafObj()
+{
+ ImpDeregisterLink();
+}
+
+void SdrGrafObj::SetGraphicObject(const GraphicObject& rGraphicObject)
+{
+ mpGraphicObject.reset(new GraphicObject(rGraphicObject));
+ mpReplacementGraphicObject.reset();
+ mpGraphicObject->SetUserData();
+ SetChanged();
+ BroadcastObjectChange();
+ onGraphicChanged();
+}
+
+const GraphicObject& SdrGrafObj::GetGraphicObject(bool bForceSwapIn) const
+{
+ if (bForceSwapIn)
+ ForceSwapIn();
+ return *mpGraphicObject;
+}
+
+const GraphicObject* SdrGrafObj::GetReplacementGraphicObject() const
+{
+ if (!mpReplacementGraphicObject && mpGraphicObject)
+ {
+ auto const & rVectorGraphicDataPtr = mpGraphicObject->GetGraphic().getVectorGraphicData();
+
+ if (rVectorGraphicDataPtr)
+ {
+ const_cast< SdrGrafObj* >(this)->mpReplacementGraphicObject.reset(new GraphicObject(rVectorGraphicDataPtr->getReplacement()));
+ }
+ else if (mpGraphicObject->GetGraphic().GetType() == GraphicType::GdiMetafile)
+ {
+ // Replacement graphic for PDF and metafiles is just the bitmap.
+ const_cast<SdrGrafObj*>(this)->mpReplacementGraphicObject.reset(new GraphicObject(mpGraphicObject->GetGraphic().GetBitmapEx()));
+ }
+ }
+
+ return mpReplacementGraphicObject.get();
+}
+
+void SdrGrafObj::NbcSetGraphic(const Graphic& rGraphic)
+{
+ mpGraphicObject->SetGraphic(rGraphic);
+ mpReplacementGraphicObject.reset();
+ mpGraphicObject->SetUserData();
+ onGraphicChanged();
+}
+
+void SdrGrafObj::SetGraphic( const Graphic& rGraphic )
+{
+ if (!rGraphic.getOriginURL().isEmpty())
+ {
+ ImpDeregisterLink();
+ aFileName = rGraphic.getOriginURL();
+ aFilterName = "";
+ }
+ NbcSetGraphic(rGraphic);
+ if (!rGraphic.getOriginURL().isEmpty())
+ {
+ ImpRegisterLink();
+ mpGraphicObject->SetUserData();
+ }
+ SetChanged();
+ BroadcastObjectChange();
+ ForceSwapIn();
+}
+
+const Graphic& SdrGrafObj::GetGraphic() const
+{
+ return mpGraphicObject->GetGraphic();
+}
+
+Graphic SdrGrafObj::GetTransformedGraphic( SdrGrafObjTransformsAttrs nTransformFlags ) const
+{
+ // Refactored most of the code to GraphicObject, where
+ // everybody can use e.g. the cropping functionality
+ MapMode aDestMap(
+ getSdrModelFromSdrObject().GetScaleUnit(),
+ Point(),
+ getSdrModelFromSdrObject().GetScaleFraction(),
+ getSdrModelFromSdrObject().GetScaleFraction());
+ const Size aDestSize( GetLogicRect().GetSize() );
+ GraphicAttr aActAttr = GetGraphicAttr(nTransformFlags);
+
+ // Delegate to moved code in GraphicObject
+ return GetGraphicObject().GetTransformedGraphic( aDestSize, aDestMap, aActAttr );
+}
+
+GraphicType SdrGrafObj::GetGraphicType() const
+{
+ return mpGraphicObject->GetType();
+}
+
+GraphicAttr SdrGrafObj::GetGraphicAttr( SdrGrafObjTransformsAttrs nTransformFlags ) const
+{
+ GraphicAttr aActAttr;
+
+ GraphicType eType = GetGraphicType();
+ if( SdrGrafObjTransformsAttrs::NONE != nTransformFlags &&
+ GraphicType::NONE != eType )
+ {
+ const bool bMirror = bool( nTransformFlags & SdrGrafObjTransformsAttrs::MIRROR );
+ const bool bRotate = bool( nTransformFlags & SdrGrafObjTransformsAttrs::ROTATE ) &&
+ (maGeo.nRotationAngle && maGeo.nRotationAngle != 18000_deg100);
+
+ // Need cropping info earlier
+ const_cast<SdrGrafObj*>(this)->ImpSetAttrToGrafInfo();
+
+ // Actually transform the graphic only in this case.
+ // Cropping always happens, though.
+ aActAttr = aGrafInfo;
+
+ if( bMirror )
+ {
+ sal_uInt16 nMirrorCase = ( maGeo.nRotationAngle == 18000_deg100 ) ? ( bMirrored ? 3 : 4 ) : ( bMirrored ? 2 : 1 );
+ bool bHMirr = nMirrorCase == 2 || nMirrorCase == 4;
+ bool bVMirr = nMirrorCase == 3 || nMirrorCase == 4;
+
+ aActAttr.SetMirrorFlags( ( bHMirr ? BmpMirrorFlags::Horizontal : BmpMirrorFlags::NONE ) | ( bVMirr ? BmpMirrorFlags::Vertical : BmpMirrorFlags::NONE ) );
+ }
+
+ if( bRotate )
+ aActAttr.SetRotation( to<Degree10>(maGeo.nRotationAngle ) );
+ }
+
+ return aActAttr;
+}
+
+bool SdrGrafObj::IsAnimated() const
+{
+ return mpGraphicObject->IsAnimated();
+}
+
+bool SdrGrafObj::IsEPS() const
+{
+ return mpGraphicObject->IsEPS();
+}
+
+MapMode SdrGrafObj::GetGrafPrefMapMode() const
+{
+ return mpGraphicObject->GetPrefMapMode();
+}
+
+Size SdrGrafObj::GetGrafPrefSize() const
+{
+ return mpGraphicObject->GetPrefSize();
+}
+
+void SdrGrafObj::SetGrafStreamURL( const OUString& rGraphicStreamURL )
+{
+ if( rGraphicStreamURL.isEmpty() )
+ {
+ mpGraphicObject->SetUserData();
+ }
+ else if(getSdrModelFromSdrObject().IsSwapGraphics() )
+ {
+ mpGraphicObject->SetUserData( rGraphicStreamURL );
+ }
+}
+
+OUString const & SdrGrafObj::GetGrafStreamURL() const
+{
+ return mpGraphicObject->GetUserData();
+}
+
+Size SdrGrafObj::getOriginalSize() const
+{
+ Size aSize = GetGrafPrefSize();
+
+ if (aGrafInfo.IsCropped())
+ {
+ const tools::Long aCroppedTop(OutputDevice::LogicToLogic(aGrafInfo.GetTopCrop(), getSdrModelFromSdrObject().GetScaleUnit(), GetGrafPrefMapMode().GetMapUnit()));
+ const tools::Long aCroppedBottom(OutputDevice::LogicToLogic(aGrafInfo.GetBottomCrop(), getSdrModelFromSdrObject().GetScaleUnit(), GetGrafPrefMapMode().GetMapUnit()));
+ const tools::Long aCroppedLeft(OutputDevice::LogicToLogic(aGrafInfo.GetLeftCrop(), getSdrModelFromSdrObject().GetScaleUnit(), GetGrafPrefMapMode().GetMapUnit()));
+ const tools::Long aCroppedRight(OutputDevice::LogicToLogic(aGrafInfo.GetRightCrop(), getSdrModelFromSdrObject().GetScaleUnit(), GetGrafPrefMapMode().GetMapUnit()));
+ const tools::Long aCroppedWidth(aSize.getWidth() - aCroppedLeft + aCroppedRight);
+ const tools::Long aCroppedHeight(aSize.getHeight() - aCroppedTop + aCroppedBottom);
+
+ aSize = Size ( aCroppedWidth, aCroppedHeight);
+ }
+
+ if ( GetGrafPrefMapMode().GetMapUnit() == MapUnit::MapPixel )
+ aSize = Application::GetDefaultDevice()->PixelToLogic(aSize, MapMode(getSdrModelFromSdrObject().GetScaleUnit()));
+ else
+ aSize = OutputDevice::LogicToLogic(aSize, GetGrafPrefMapMode(), MapMode(getSdrModelFromSdrObject().GetScaleUnit()));
+
+ return aSize;
+}
+
+// TODO Remove
+void SdrGrafObj::ForceSwapIn() const
+{
+ if (pGraphicLink && (mpGraphicObject->GetType() == GraphicType::NONE ||
+ mpGraphicObject->GetType() == GraphicType::Default) )
+ {
+ pGraphicLink->Update();
+ }
+}
+
+void SdrGrafObj::ImpRegisterLink()
+{
+ sfx2::LinkManager* pLinkManager(getSdrModelFromSdrObject().GetLinkManager());
+
+ if( pLinkManager != nullptr && pGraphicLink == nullptr )
+ {
+ if (!aFileName.isEmpty())
+ {
+ pGraphicLink = new SdrGraphicLink( *this );
+ pLinkManager->InsertFileLink(
+ *pGraphicLink, sfx2::SvBaseLinkObjectType::ClientGraphic, aFileName, (aFilterName.isEmpty() ? nullptr : &aFilterName));
+ pGraphicLink->Connect();
+ }
+ }
+}
+
+void SdrGrafObj::ImpDeregisterLink()
+{
+ sfx2::LinkManager* pLinkManager(getSdrModelFromSdrObject().GetLinkManager());
+
+ if( pLinkManager != nullptr && pGraphicLink!=nullptr)
+ {
+ // When using Remove, the *pGraphicLink is implicitly deleted
+ pLinkManager->Remove( pGraphicLink );
+ pGraphicLink=nullptr;
+ }
+}
+
+void SdrGrafObj::SetGraphicLink(const OUString& rFileName)
+{
+ Graphic aGraphic;
+ aGraphic.setOriginURL(rFileName);
+ SetGraphic(aGraphic);
+}
+
+void SdrGrafObj::ReleaseGraphicLink()
+{
+ ImpDeregisterLink();
+ aFileName.clear();
+ aFilterName.clear();
+
+ auto aGraphic = mpGraphicObject->GetGraphic();
+ aGraphic.setOriginURL("");
+ SetGraphic(aGraphic);
+}
+
+bool SdrGrafObj::IsLinkedGraphic() const
+{
+ return !mpGraphicObject->GetGraphic().getOriginURL().isEmpty();
+}
+
+void SdrGrafObj::TakeObjInfo(SdrObjTransformInfoRec& rInfo) const
+{
+ bool bNoPresGrf = ( mpGraphicObject->GetType() != GraphicType::NONE ) && !m_bEmptyPresObj;
+
+ rInfo.bResizeFreeAllowed = maGeo.nRotationAngle.get() % 9000 == 0 ||
+ maGeo.nRotationAngle.get() % 18000 == 0 ||
+ maGeo.nRotationAngle.get() % 27000 == 0;
+
+ rInfo.bResizePropAllowed = true;
+ rInfo.bRotateFreeAllowed = bNoPresGrf;
+ rInfo.bRotate90Allowed = bNoPresGrf;
+ rInfo.bMirrorFreeAllowed = bNoPresGrf;
+ rInfo.bMirror45Allowed = bNoPresGrf;
+ rInfo.bMirror90Allowed = !m_bEmptyPresObj;
+ rInfo.bTransparenceAllowed = false;
+
+ // #i118485# Shear allowed and possible now
+ rInfo.bShearAllowed = true;
+
+ rInfo.bEdgeRadiusAllowed=false;
+ rInfo.bCanConvToPath = !IsEPS();
+ rInfo.bCanConvToPathLineToArea = false;
+ rInfo.bCanConvToPolyLineToArea = false;
+ rInfo.bCanConvToPoly = !IsEPS();
+ rInfo.bCanConvToContour = (rInfo.bCanConvToPoly || LineGeometryUsageIsNecessary());
+}
+
+SdrObjKind SdrGrafObj::GetObjIdentifier() const
+{
+ return SdrObjKind::Graphic;
+}
+
+void SdrGrafObj::ImpSetLinkedGraphic( const Graphic& rGraphic )
+{
+ const bool bIsChanged(getSdrModelFromSdrObject().IsChanged());
+ NbcSetGraphic( rGraphic );
+ ActionChanged();
+ BroadcastObjectChange();
+ getSdrModelFromSdrObject().SetChanged(bIsChanged);
+}
+
+OUString SdrGrafObj::TakeObjNameSingul() const
+{
+ if (!mpGraphicObject)
+ return OUString();
+
+ auto const & rVectorGraphicDataPtr = mpGraphicObject->GetGraphic().getVectorGraphicData();
+
+ OUStringBuffer sName;
+
+ if (rVectorGraphicDataPtr)
+ {
+ switch (rVectorGraphicDataPtr->getType())
+ {
+ case VectorGraphicDataType::Svg:
+ {
+ sName.append(SvxResId(STR_ObjNameSingulGRAFSVG));
+ break;
+ }
+ case VectorGraphicDataType::Wmf:
+ {
+ sName.append(SvxResId(STR_ObjNameSingulGRAFWMF));
+ break;
+ }
+ case VectorGraphicDataType::Emf:
+ {
+ sName.append(SvxResId(STR_ObjNameSingulGRAFEMF));
+ break;
+ }
+ case VectorGraphicDataType::Pdf:
+ {
+ sName.append(SvxResId(STR_ObjNameSingulGRAFPDF));
+ break;
+ }
+ } //no default, see tdf#137813
+ }
+ else
+ {
+ switch( mpGraphicObject->GetType() )
+ {
+ case GraphicType::Bitmap:
+ {
+ TranslateId pId = ( ( mpGraphicObject->IsTransparent() || GetObjectItem( SDRATTR_GRAFTRANSPARENCE ).GetValue() ) ?
+ ( IsLinkedGraphic() ? STR_ObjNameSingulGRAFBMPTRANSLNK : STR_ObjNameSingulGRAFBMPTRANS ) :
+ ( IsLinkedGraphic() ? STR_ObjNameSingulGRAFBMPLNK : STR_ObjNameSingulGRAFBMP ) );
+
+ sName.append(SvxResId(pId));
+ }
+ break;
+
+ case GraphicType::GdiMetafile:
+ sName.append(SvxResId(IsLinkedGraphic() ? STR_ObjNameSingulGRAFMTFLNK : STR_ObjNameSingulGRAFMTF));
+ break;
+
+ case GraphicType::NONE:
+ sName.append(SvxResId(IsLinkedGraphic() ? STR_ObjNameSingulGRAFNONELNK : STR_ObjNameSingulGRAFNONE));
+ break;
+
+ default:
+ sName.append(SvxResId(IsLinkedGraphic() ? STR_ObjNameSingulGRAFLNK : STR_ObjNameSingulGRAF));
+ break;
+ }
+ }
+
+ const OUString aName(GetName());
+
+ if (!aName.isEmpty())
+ {
+ sName.append(" '");
+ sName.append(aName);
+ sName.append('\'' );
+ }
+
+ return sName.makeStringAndClear();
+}
+
+OUString SdrGrafObj::TakeObjNamePlural() const
+{
+ if (!mpGraphicObject)
+ return OUString();
+
+ auto const & rVectorGraphicDataPtr = mpGraphicObject->GetGraphic().getVectorGraphicData();
+
+ OUStringBuffer sName;
+
+ if (rVectorGraphicDataPtr)
+ {
+ switch (rVectorGraphicDataPtr->getType())
+ {
+ case VectorGraphicDataType::Svg:
+ {
+ sName.append(SvxResId(STR_ObjNamePluralGRAFSVG));
+ break;
+ }
+ case VectorGraphicDataType::Wmf:
+ {
+ sName.append(SvxResId(STR_ObjNamePluralGRAFWMF));
+ break;
+ }
+ case VectorGraphicDataType::Emf:
+ {
+ sName.append(SvxResId(STR_ObjNamePluralGRAFEMF));
+ break;
+ }
+ case VectorGraphicDataType::Pdf:
+ {
+ sName.append(SvxResId(STR_ObjNamePluralGRAFPDF));
+ break;
+ }
+ } //no default, see tdf#137813
+ }
+ else
+ {
+ switch(mpGraphicObject->GetType())
+ {
+ case GraphicType::Bitmap:
+ {
+ TranslateId pId = ( ( mpGraphicObject->IsTransparent() || GetObjectItem( SDRATTR_GRAFTRANSPARENCE ).GetValue() ) ?
+ ( IsLinkedGraphic() ? STR_ObjNamePluralGRAFBMPTRANSLNK : STR_ObjNamePluralGRAFBMPTRANS ) :
+ ( IsLinkedGraphic() ? STR_ObjNamePluralGRAFBMPLNK : STR_ObjNamePluralGRAFBMP ) );
+
+ sName.append(SvxResId(pId));
+ }
+ break;
+
+ case GraphicType::GdiMetafile:
+ sName.append(SvxResId(IsLinkedGraphic() ? STR_ObjNamePluralGRAFMTFLNK : STR_ObjNamePluralGRAFMTF));
+ break;
+
+ case GraphicType::NONE:
+ sName.append(SvxResId(IsLinkedGraphic() ? STR_ObjNamePluralGRAFNONELNK : STR_ObjNamePluralGRAFNONE));
+ break;
+
+ default:
+ sName.append(SvxResId(IsLinkedGraphic() ? STR_ObjNamePluralGRAFLNK : STR_ObjNamePluralGRAF));
+ break;
+ }
+ }
+
+ const OUString aName(GetName());
+
+ if (!aName.isEmpty())
+ {
+ sName.append(" '");
+ sName.append(aName);
+ sName.append('\'');
+ }
+
+ return sName.makeStringAndClear();
+}
+
+SdrObjectUniquePtr SdrGrafObj::getFullDragClone() const
+{
+ // call parent
+ SdrObjectUniquePtr pRetval = SdrRectObj::getFullDragClone();
+
+ // #i103116# the full drag clone leads to problems
+ // with linked graphics, so reset the link in this
+ // temporary interaction object and load graphic
+ if(pRetval && IsLinkedGraphic())
+ {
+ static_cast< SdrGrafObj* >(pRetval.get())->ReleaseGraphicLink();
+ }
+
+ return pRetval;
+}
+
+SdrGrafObj* SdrGrafObj::CloneSdrObject(SdrModel& rTargetModel) const
+{
+ return new SdrGrafObj(rTargetModel, *this);
+}
+
+sal_uInt32 SdrGrafObj::GetHdlCount() const
+{
+ return 8;
+}
+
+void SdrGrafObj::AddToHdlList(SdrHdlList& rHdlList) const
+{
+ SdrHdlList tempList(nullptr);
+ SdrRectObj::AddToHdlList( tempList );
+ tempList.RemoveHdl(0);
+ tempList.MoveTo(rHdlList);
+}
+
+void SdrGrafObj::NbcResize(const Point& rRef, const Fraction& xFact, const Fraction& yFact)
+{
+ SdrRectObj::NbcResize( rRef, xFact, yFact );
+
+ bool bMirrX = xFact.GetNumerator() < 0;
+ bool bMirrY = yFact.GetNumerator() < 0;
+
+ if( bMirrX != bMirrY )
+ bMirrored = !bMirrored;
+}
+
+void SdrGrafObj::NbcMirror(const Point& rRef1, const Point& rRef2)
+{
+ SdrRectObj::NbcMirror(rRef1,rRef2);
+ bMirrored = !bMirrored;
+}
+
+std::unique_ptr<SdrObjGeoData> SdrGrafObj::NewGeoData() const
+{
+ return std::make_unique<SdrGrafObjGeoData>();
+}
+
+void SdrGrafObj::SaveGeoData(SdrObjGeoData& rGeo) const
+{
+ SdrRectObj::SaveGeoData(rGeo);
+ SdrGrafObjGeoData& rGGeo=static_cast<SdrGrafObjGeoData&>(rGeo);
+ rGGeo.bMirrored=bMirrored;
+}
+
+void SdrGrafObj::RestoreGeoData(const SdrObjGeoData& rGeo)
+{
+ SdrRectObj::RestoreGeoData(rGeo);
+ const SdrGrafObjGeoData& rGGeo=static_cast<const SdrGrafObjGeoData&>(rGeo);
+ bMirrored=rGGeo.bMirrored;
+}
+
+void SdrGrafObj::handlePageChange(SdrPage* pOldPage, SdrPage* pNewPage)
+{
+ const bool bRemove(pNewPage == nullptr && pOldPage != nullptr);
+ const bool bInsert(pNewPage != nullptr && pOldPage == nullptr);
+
+ if( bRemove )
+ {
+ // No SwapIn necessary here, because if something's not loaded, it can't be animated either.
+ if( mpGraphicObject->IsAnimated())
+ mpGraphicObject->StopAnimation();
+
+ if( pGraphicLink != nullptr )
+ ImpDeregisterLink();
+ }
+
+ // call parent
+ SdrRectObj::handlePageChange(pOldPage, pNewPage);
+
+ if (!aFileName.isEmpty() && bInsert)
+ {
+ ImpRegisterLink();
+ }
+}
+
+void SdrGrafObj::StartAnimation()
+{
+ SetGrafAnimationAllowed(true);
+}
+
+bool SdrGrafObj::HasGDIMetaFile() const
+{
+ return( mpGraphicObject->GetType() == GraphicType::GdiMetafile );
+}
+
+bool SdrGrafObj::isEmbeddedVectorGraphicData() const
+{
+ return GraphicType::Bitmap == GetGraphicType() && GetGraphic().getVectorGraphicData();
+}
+
+GDIMetaFile SdrGrafObj::getMetafileFromEmbeddedVectorGraphicData() const
+{
+ GDIMetaFile aRetval;
+
+ if(isEmbeddedVectorGraphicData())
+ {
+ ScopedVclPtrInstance< VirtualDevice > pOut;
+ const tools::Rectangle aBoundRect(GetCurrentBoundRect());
+ const MapMode aMap(
+ getSdrModelFromSdrObject().GetScaleUnit(),
+ Point(),
+ getSdrModelFromSdrObject().GetScaleFraction(),
+ getSdrModelFromSdrObject().GetScaleFraction());
+
+ pOut->EnableOutput(false);
+ pOut->SetMapMode(aMap);
+ aRetval.Record(pOut);
+ SingleObjectPainter(*pOut);
+ aRetval.Stop();
+ aRetval.WindStart();
+ aRetval.Move(-aBoundRect.Left(), -aBoundRect.Top());
+ aRetval.SetPrefMapMode(aMap);
+ aRetval.SetPrefSize(aBoundRect.GetSize());
+ }
+
+ return aRetval;
+}
+
+GDIMetaFile SdrGrafObj::GetMetaFile(GraphicType &rGraphicType) const
+{
+ if (isEmbeddedVectorGraphicData())
+ {
+ // Embedded Vector Graphic Data
+ // There is currently no helper to create SdrObjects from primitives (even if I'm thinking
+ // about writing one for some time). To get the roundtrip to SdrObjects it is necessary to
+ // use the old converter path over the MetaFile mechanism. Create Metafile from Svg
+ // primitives here pretty directly
+ rGraphicType = GraphicType::GdiMetafile;
+ return getMetafileFromEmbeddedVectorGraphicData();
+ }
+ else if (GraphicType::GdiMetafile == rGraphicType)
+ {
+ return GetTransformedGraphic(SdrGrafObjTransformsAttrs::MIRROR).GetGDIMetaFile();
+ }
+ return GDIMetaFile();
+}
+
+SdrObjectUniquePtr SdrGrafObj::DoConvertToPolyObj(bool bBezier, bool bAddText ) const
+{
+ SdrObject* pRetval = nullptr;
+ GraphicType aGraphicType(GetGraphicType());
+ GDIMetaFile aMtf(GetMetaFile(aGraphicType));
+ switch(aGraphicType)
+ {
+ case GraphicType::GdiMetafile:
+ {
+ // Sort into group and return ONLY those objects that can be created from the MetaFile.
+ ImpSdrGDIMetaFileImport aFilter(
+ getSdrModelFromSdrObject(),
+ GetLayer(),
+ maRect);
+ SdrObjGroup* pGrp = new SdrObjGroup(getSdrModelFromSdrObject());
+
+ if(aFilter.DoImport(aMtf, *pGrp->GetSubList(), 0))
+ {
+ {
+ // copy transformation
+ GeoStat aGeoStat(GetGeoStat());
+
+ if(aGeoStat.nShearAngle)
+ {
+ aGeoStat.RecalcTan();
+ pGrp->NbcShear(maRect.TopLeft(), aGeoStat.nShearAngle, aGeoStat.mfTanShearAngle, false);
+ }
+
+ if(aGeoStat.nRotationAngle)
+ {
+ aGeoStat.RecalcSinCos();
+ pGrp->NbcRotate(maRect.TopLeft(), aGeoStat.nRotationAngle, aGeoStat.mfSinRotationAngle, aGeoStat.mfCosRotationAngle);
+ }
+ }
+
+ pRetval = pGrp;
+ pGrp->NbcSetLayer(GetLayer());
+
+ if(bAddText)
+ {
+ pRetval = ImpConvertAddText(SdrObjectUniquePtr(pRetval), bBezier).release();
+ }
+
+ // convert all children
+ if( pRetval )
+ {
+ SdrObject* pHalfDone = pRetval;
+ pRetval = pRetval->DoConvertToPolyObj(bBezier, bAddText).release();
+ SdrObject::Free( pHalfDone ); // resulting object is newly created
+
+ if( pRetval )
+ {
+ // flatten subgroups. As we call
+ // DoConvertToPolyObj() on the resulting group
+ // objects, subgroups can exist (e.g. text is
+ // a group object for every line).
+ SdrObjList* pList = pRetval->GetSubList();
+ if( pList )
+ pList->FlattenGroups();
+ }
+ }
+ }
+ else
+ {
+ // always use SdrObject::Free(...) for SdrObjects (!)
+ SdrObject* pTemp(pGrp);
+ SdrObject::Free(pTemp);
+ }
+
+ // #i118485# convert line and fill
+ SdrObjectUniquePtr pLineFill = SdrRectObj::DoConvertToPolyObj(bBezier, false);
+
+ if(pLineFill)
+ {
+ if(pRetval)
+ {
+ pGrp = dynamic_cast< SdrObjGroup* >(pRetval);
+
+ if(!pGrp)
+ {
+ pGrp = new SdrObjGroup(getSdrModelFromSdrObject());
+ pGrp->NbcSetLayer(GetLayer());
+ pGrp->GetSubList()->NbcInsertObject(pRetval);
+ }
+
+ pGrp->GetSubList()->NbcInsertObject(pLineFill.release(), 0);
+ }
+ else
+ {
+ pRetval = pLineFill.release();
+ }
+ }
+
+ break;
+ }
+ case GraphicType::Bitmap:
+ {
+ // create basic object and add fill
+ pRetval = SdrRectObj::DoConvertToPolyObj(bBezier, bAddText).release();
+
+ // save bitmap as an attribute
+ if(pRetval)
+ {
+ // retrieve bitmap for the fill
+ SfxItemSet aSet(GetObjectItemSet());
+
+ aSet.Put(XFillStyleItem(drawing::FillStyle_BITMAP));
+ const BitmapEx aBitmapEx(GetTransformedGraphic().GetBitmapEx());
+ aSet.Put(XFillBitmapItem(OUString(), Graphic(aBitmapEx)));
+ aSet.Put(XFillBmpTileItem(false));
+
+ pRetval->SetMergedItemSet(aSet);
+ }
+ break;
+ }
+ case GraphicType::NONE:
+ case GraphicType::Default:
+ {
+ pRetval = SdrRectObj::DoConvertToPolyObj(bBezier, bAddText).release();
+ break;
+ }
+ }
+
+ return SdrObjectUniquePtr(pRetval);
+}
+
+void SdrGrafObj::Notify( SfxBroadcaster& rBC, const SfxHint& rHint )
+{
+ SetXPolyDirty();
+ SdrRectObj::Notify( rBC, rHint );
+ ImpSetAttrToGrafInfo();
+}
+
+
+void SdrGrafObj::SetMirrored( bool _bMirrored )
+{
+ bMirrored = _bMirrored;
+}
+
+void SdrGrafObj::ImpSetAttrToGrafInfo()
+{
+ const SfxItemSet& rSet = GetObjectItemSet();
+ const sal_uInt16 nTrans = rSet.Get( SDRATTR_GRAFTRANSPARENCE ).GetValue();
+ const SdrGrafCropItem& rCrop = rSet.Get( SDRATTR_GRAFCROP );
+
+ aGrafInfo.SetLuminance( rSet.Get( SDRATTR_GRAFLUMINANCE ).GetValue() );
+ aGrafInfo.SetContrast( rSet.Get( SDRATTR_GRAFCONTRAST ).GetValue() );
+ aGrafInfo.SetChannelR( rSet.Get( SDRATTR_GRAFRED ).GetValue() );
+ aGrafInfo.SetChannelG( rSet.Get( SDRATTR_GRAFGREEN ).GetValue() );
+ aGrafInfo.SetChannelB( rSet.Get( SDRATTR_GRAFBLUE ).GetValue() );
+ aGrafInfo.SetGamma( rSet.Get( SDRATTR_GRAFGAMMA ).GetValue() * 0.01 );
+ aGrafInfo.SetAlpha( 255 - static_cast<sal_uInt8>(FRound( std::min( nTrans, sal_uInt16(100) ) * 2.55 )) );
+ aGrafInfo.SetInvert( rSet.Get( SDRATTR_GRAFINVERT ).GetValue() );
+ aGrafInfo.SetDrawMode( rSet.Get( SDRATTR_GRAFMODE ).GetValue() );
+ aGrafInfo.SetCrop( rCrop.GetLeft(), rCrop.GetTop(), rCrop.GetRight(), rCrop.GetBottom() );
+
+ SetXPolyDirty();
+ SetBoundAndSnapRectsDirty();
+}
+
+void SdrGrafObj::AdjustToMaxRect( const tools::Rectangle& rMaxRect, bool bShrinkOnly )
+{
+ Size aSize;
+ Size aMaxSize( rMaxRect.GetSize() );
+ if (mpGraphicObject->GetPrefMapMode().GetMapUnit() == MapUnit::MapPixel)
+ aSize = Application::GetDefaultDevice()->PixelToLogic(mpGraphicObject->GetPrefSize(), MapMode(MapUnit::Map100thMM));
+ else
+ aSize = OutputDevice::LogicToLogic( mpGraphicObject->GetPrefSize(),
+ mpGraphicObject->GetPrefMapMode(),
+ MapMode( MapUnit::Map100thMM ) );
+
+ if( aSize.IsEmpty() )
+ return;
+
+ Point aPos( rMaxRect.TopLeft() );
+
+ // if the graphic is too large, fit it to page
+ if ( (!bShrinkOnly ||
+ ( aSize.Height() > aMaxSize.Height() ) ||
+ ( aSize.Width() > aMaxSize.Width() ) )&&
+ aSize.Height() && aMaxSize.Height() )
+ {
+ float fGrfWH = static_cast<float>(aSize.Width()) /
+ static_cast<float>(aSize.Height());
+ float fWinWH = static_cast<float>(aMaxSize.Width()) /
+ static_cast<float>(aMaxSize.Height());
+
+ // Scale graphic to page size
+ if ( fGrfWH < fWinWH )
+ {
+ aSize.setWidth( static_cast<tools::Long>(aMaxSize.Height() * fGrfWH) );
+ aSize.setHeight( aMaxSize.Height() );
+ }
+ else if ( fGrfWH > 0.F )
+ {
+ aSize.setWidth( aMaxSize.Width() );
+ aSize.setHeight( static_cast<tools::Long>(aMaxSize.Width() / fGrfWH) );
+ }
+
+ aPos = rMaxRect.Center();
+ }
+
+ if( bShrinkOnly )
+ aPos = maRect.TopLeft();
+
+ aPos.AdjustX( -(aSize.Width() / 2) );
+ aPos.AdjustY( -(aSize.Height() / 2) );
+ SetLogicRect( tools::Rectangle( aPos, aSize ) );
+}
+
+void SdrGrafObj::SetGrafAnimationAllowed(bool bNew)
+{
+ if(mbGrafAnimationAllowed != bNew)
+ {
+ mbGrafAnimationAllowed = bNew;
+ ActionChanged();
+ }
+}
+
+Reference< XInputStream > SdrGrafObj::getInputStream() const
+{
+ Reference< XInputStream > xStream;
+
+ if (mpGraphicObject && GetGraphic().IsGfxLink())
+ {
+ Graphic aGraphic( GetGraphic() );
+ GfxLink aLink( aGraphic.GetGfxLink() );
+ sal_uInt32 nSize = aLink.GetDataSize();
+ const void* pSourceData = static_cast<const void*>(aLink.GetData());
+ if( nSize && pSourceData )
+ {
+ sal_uInt8 * pBuffer = new sal_uInt8[ nSize ];
+ memcpy( pBuffer, pSourceData, nSize );
+
+ SvMemoryStream* pStream = new SvMemoryStream( static_cast<void*>(pBuffer), static_cast<std::size_t>(nSize), StreamMode::READ );
+ pStream->ObjectOwnsMemory( true );
+ xStream.set( new utl::OInputStreamWrapper( pStream, true ) );
+ }
+ }
+
+ if (!xStream.is() && !aFileName.isEmpty())
+ {
+ SvFileStream* pStream = new SvFileStream( aFileName, StreamMode::READ );
+ xStream.set( new utl::OInputStreamWrapper( pStream ) );
+ }
+
+ return xStream;
+}
+
+// moved crop handle creation here; this is the object type using them
+void SdrGrafObj::addCropHandles(SdrHdlList& rTarget) const
+{
+ basegfx::B2DHomMatrix aMatrix;
+ basegfx::B2DPolyPolygon aPolyPolygon;
+
+ // get object transformation
+ TRGetBaseGeometry(aMatrix, aPolyPolygon);
+
+ // part of object transformation correction, but used later, so defined outside next scope
+ double fShearX(0.0), fRotate(0.0);
+
+ { // TTTT correct shear, it comes currently mirrored from TRGetBaseGeometry, can be removed with aw080
+ basegfx::B2DTuple aScale;
+ basegfx::B2DTuple aTranslate;
+
+ aMatrix.decompose(aScale, aTranslate, fRotate, fShearX);
+
+ if(!basegfx::fTools::equalZero(fShearX))
+ {
+ // shearX is used, correct it
+ fShearX = -fShearX;
+ }
+
+ aMatrix = basegfx::utils::createScaleShearXRotateTranslateB2DHomMatrix(
+ aScale,
+ fShearX,
+ fRotate,
+ aTranslate);
+ }
+
+ // get crop values
+ const SdrGrafCropItem& rCrop = GetMergedItem(SDRATTR_GRAFCROP);
+
+ if(rCrop.GetLeft() || rCrop.GetTop() || rCrop.GetRight() ||rCrop.GetBottom())
+ {
+ // decompose object transformation to have current translate and scale
+ basegfx::B2DVector aScale, aTranslate;
+ double fLclRotate, fLclShearX;
+
+ aMatrix.decompose(aScale, aTranslate, fLclRotate, fLclShearX);
+
+ if(!aScale.equalZero())
+ {
+ // get crop scale
+ const basegfx::B2DVector aCropScaleFactor(
+ GetGraphicObject().calculateCropScaling(
+ aScale.getX(),
+ aScale.getY(),
+ rCrop.GetLeft(),
+ rCrop.GetTop(),
+ rCrop.GetRight(),
+ rCrop.GetBottom()));
+
+ // apply crop scale
+ const double fCropLeft(rCrop.GetLeft() * aCropScaleFactor.getX());
+ const double fCropTop(rCrop.GetTop() * aCropScaleFactor.getY());
+ const double fCropRight(rCrop.GetRight() * aCropScaleFactor.getX());
+ const double fCropBottom(rCrop.GetBottom() * aCropScaleFactor.getY());
+ basegfx::B2DHomMatrix aMatrixForCropViewHdl(aMatrix);
+
+ if(IsMirrored())
+ {
+ // create corrected new matrix, TTTT can be removed with aw080
+ // the old mirror only can mirror horizontally; the vertical mirror
+ // is faked by using the horizontal and 180 degree rotation. Since
+ // the object can be rotated differently from 180 degree, this is
+ // not safe to detect. Just correct horizontal mirror (which is
+ // in IsMirrored()) and keep the rotation angle
+ // caution: Do not modify aMatrix, it is used below to calculate
+ // the exact handle positions
+ basegfx::B2DHomMatrix aPreMultiply;
+
+ // mirrored X, apply
+ aPreMultiply.translate(-0.5, 0.0);
+ aPreMultiply.scale(-1.0, 1.0);
+ aPreMultiply.translate(0.5, 0.0);
+
+ aMatrixForCropViewHdl = aMatrixForCropViewHdl * aPreMultiply;
+ }
+
+ rTarget.AddHdl(
+ std::make_unique<SdrCropViewHdl>(
+ aMatrixForCropViewHdl,
+ GetGraphicObject().GetGraphic(),
+ fCropLeft,
+ fCropTop,
+ fCropRight,
+ fCropBottom));
+ }
+ }
+
+ basegfx::B2DPoint aPos;
+
+ aPos = aMatrix * 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 = aMatrix * 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 = aMatrix * 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 = aMatrix * 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 = aMatrix * 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 = aMatrix * 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 = aMatrix * 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 = aMatrix * 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));
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/svdraw/svdogrp.cxx b/svx/source/svdraw/svdogrp.cxx
new file mode 100644
index 000000000..7fc8e0b12
--- /dev/null
+++ b/svx/source/svdraw/svdogrp.cxx
@@ -0,0 +1,835 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * 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/svdogrp.hxx>
+
+#include <svx/svdmodel.hxx>
+#include <svx/svdpage.hxx>
+#include <svx/svdobj.hxx>
+#include <svx/svdtrans.hxx>
+#include <svx/dialmgr.hxx>
+#include <svx/strings.hrc>
+
+#include <sdr/properties/groupproperties.hxx>
+#include <sdr/contact/viewcontactofgroup.hxx>
+#include <basegfx/range/b2drange.hxx>
+#include <basegfx/polygon/b2dpolygontools.hxx>
+#include <libxml/xmlwriter.h>
+#include <vcl/canvastools.hxx>
+#include <svx/diagram/IDiagramHelper.hxx>
+
+const std::shared_ptr< svx::diagram::IDiagramHelper >& SdrObjGroup::getDiagramHelper() const
+{
+ return mp_DiagramHelper;
+}
+
+// BaseProperties section
+std::unique_ptr<sdr::properties::BaseProperties> SdrObjGroup::CreateObjectSpecificProperties()
+{
+ return std::make_unique<sdr::properties::GroupProperties>(*this);
+}
+
+// DrawContact section
+std::unique_ptr<sdr::contact::ViewContact> SdrObjGroup::CreateObjectSpecificViewContact()
+{
+ return std::make_unique<sdr::contact::ViewContactOfGroup>(*this);
+}
+
+SdrObjGroup::SdrObjGroup(SdrModel& rSdrModel)
+: SdrObject(rSdrModel)
+, SdrObjList()
+, maRefPoint(0, 0)
+, mp_DiagramHelper()
+{
+ m_bClosedObj=false;
+}
+
+SdrObjGroup::SdrObjGroup(SdrModel& rSdrModel, SdrObjGroup const & rSource)
+: SdrObject(rSdrModel, rSource)
+, SdrObjList()
+, maRefPoint(0, 0)
+, mp_DiagramHelper()
+{
+ m_bClosedObj=false;
+
+ // copy child SdrObjects
+ if(nullptr != rSource.GetSubList())
+ {
+ // #i36404# Copy SubList, init model and page first
+ const SdrObjList& rSourceSubList(*rSource.GetSubList());
+
+ CopyObjects(rSourceSubList);
+
+ // tdf#116979: needed here, we need bSnapRectDirty to be true
+ // which it is after using SdrObject::operator= (see above),
+ // but set to false again using CopyObjects
+ SetBoundAndSnapRectsDirty();
+ }
+
+ // copy local parameters
+ maRefPoint = rSource.maRefPoint;
+}
+
+void SdrObjGroup::AddToHdlList(SdrHdlList& rHdlList) const
+{
+ // only for diagram, so do nothing for just groups
+ if(!isDiagram())
+ return;
+
+ svx::diagram::IDiagramHelper::AddAdditionalVisualization(*this, rHdlList);
+}
+
+SdrObjGroup::~SdrObjGroup()
+{
+}
+
+SdrPage* SdrObjGroup::getSdrPageFromSdrObjList() const
+{
+ return getSdrPageFromSdrObject();
+}
+
+SdrObject* SdrObjGroup::getSdrObjectFromSdrObjList() const
+{
+ return const_cast< SdrObjGroup* >(this);
+}
+
+SdrObjList* SdrObjGroup::getChildrenOfSdrObject() const
+{
+ return const_cast< SdrObjGroup* >(this);
+}
+
+void SdrObjGroup::TakeObjInfo(SdrObjTransformInfoRec& rInfo) const
+{
+ rInfo.bNoContortion=false;
+ const size_t nObjCount(GetObjCount());
+ for (size_t i=0; i<nObjCount; ++i) {
+ SdrObject* pObj(GetObj(i));
+ SdrObjTransformInfoRec aInfo;
+ pObj->TakeObjInfo(aInfo);
+ if (!aInfo.bMoveAllowed ) rInfo.bMoveAllowed =false;
+ if (!aInfo.bResizeFreeAllowed ) rInfo.bResizeFreeAllowed =false;
+ if (!aInfo.bResizePropAllowed ) rInfo.bResizePropAllowed =false;
+ if (!aInfo.bRotateFreeAllowed ) rInfo.bRotateFreeAllowed =false;
+ if (!aInfo.bRotate90Allowed ) rInfo.bRotate90Allowed =false;
+ if (!aInfo.bMirrorFreeAllowed ) rInfo.bMirrorFreeAllowed =false;
+ if (!aInfo.bMirror45Allowed ) rInfo.bMirror45Allowed =false;
+ if (!aInfo.bMirror90Allowed ) rInfo.bMirror90Allowed =false;
+ if (!aInfo.bShearAllowed ) rInfo.bShearAllowed =false;
+ if (!aInfo.bEdgeRadiusAllowed ) rInfo.bEdgeRadiusAllowed =false;
+ if (!aInfo.bNoOrthoDesired ) rInfo.bNoOrthoDesired =false;
+ if (aInfo.bNoContortion ) rInfo.bNoContortion =true;
+ if (!aInfo.bCanConvToPath ) rInfo.bCanConvToPath =false;
+
+ if(!aInfo.bCanConvToContour)
+ rInfo.bCanConvToContour = false;
+
+ if (!aInfo.bCanConvToPoly ) rInfo.bCanConvToPoly =false;
+ if (!aInfo.bCanConvToPathLineToArea) rInfo.bCanConvToPathLineToArea=false;
+ if (!aInfo.bCanConvToPolyLineToArea) rInfo.bCanConvToPolyLineToArea=false;
+ }
+ if (nObjCount==0) {
+ rInfo.bRotateFreeAllowed=false;
+ rInfo.bRotate90Allowed =false;
+ rInfo.bMirrorFreeAllowed=false;
+ rInfo.bMirror45Allowed =false;
+ rInfo.bMirror90Allowed =false;
+ rInfo.bTransparenceAllowed = false;
+ rInfo.bShearAllowed =false;
+ rInfo.bEdgeRadiusAllowed=false;
+ rInfo.bNoContortion =true;
+ }
+ if(nObjCount != 1)
+ {
+ // only allowed if single object selected
+ rInfo.bTransparenceAllowed = false;
+ }
+}
+
+void SdrObjGroup::SetBoundRectDirty()
+{
+ // avoid resetting aOutRect which in case of this object is model data,
+ // not re-creatable view data
+}
+
+SdrObjKind SdrObjGroup::GetObjIdentifier() const
+{
+ return SdrObjKind::Group;
+}
+
+SdrLayerID SdrObjGroup::GetLayer() const
+{
+ bool b1st = true;
+ SdrLayerID nLay = SdrObject::GetLayer();
+ const size_t nObjCount(GetObjCount());
+ for (size_t i=0; i<nObjCount; ++i) {
+ SdrLayerID nLay1(GetObj(i)->GetLayer());
+ if (b1st) { nLay=nLay1; b1st = false; }
+ else if (nLay1!=nLay) return SdrLayerID(0);
+ }
+ return nLay;
+}
+
+void SdrObjGroup::NbcSetLayer(SdrLayerID nLayer)
+{
+ SdrObject::NbcSetLayer(nLayer);
+ const size_t nObjCount(GetObjCount());
+ for (size_t i=0; i<nObjCount; ++i) {
+ GetObj(i)->NbcSetLayer(nLayer);
+ }
+}
+
+void SdrObjGroup::handlePageChange(SdrPage* pOldPage, SdrPage* pNewPage)
+{
+ // call parent
+ SdrObject::handlePageChange(pOldPage, pNewPage);
+
+ for(size_t i(0); i < GetObjCount(); i++)
+ {
+ GetObj(i)->handlePageChange(pOldPage, pNewPage);
+ }
+}
+
+SdrObjList* SdrObjGroup::GetSubList() const
+{
+ return const_cast< SdrObjGroup* >(this);
+}
+
+static bool containsOOXData(const css::uno::Any& rVal)
+{
+ const css::uno::Sequence<css::beans::PropertyValue>& propList(rVal.get< css::uno::Sequence<css::beans::PropertyValue> >());
+ for (const auto& rProp : std::as_const(propList))
+ {
+ if(rProp.Name.startsWith("OOX"))
+ {
+ return true;
+ }
+ }
+
+ return false;
+}
+
+void SdrObjGroup::SetGrabBagItem(const css::uno::Any& rVal)
+{
+ // detect if the intention is to disable Diagram functionality
+ if(isDiagram() && !containsOOXData(rVal))
+ {
+ css::uno::Any aOld;
+ GetGrabBagItem(aOld);
+
+ if(containsOOXData(aOld))
+ {
+ mp_DiagramHelper.reset();
+ }
+ }
+
+ // call parent
+ SdrObject::SetGrabBagItem(rVal);
+}
+
+const tools::Rectangle& SdrObjGroup::GetCurrentBoundRect() const
+{
+ // <aOutRect> has to contain the bounding rectangle
+ if(0 != GetObjCount())
+ {
+ m_aOutRect = GetAllObjBoundRect();
+ }
+
+ return m_aOutRect;
+}
+
+const tools::Rectangle& SdrObjGroup::GetSnapRect() const
+{
+ // <aOutRect> has to contain the bounding rectangle
+ if(0 != GetObjCount())
+ {
+ return GetAllObjSnapRect();
+ }
+ else
+ {
+ return m_aOutRect;
+ }
+}
+
+SdrObjGroup* SdrObjGroup::CloneSdrObject(SdrModel& rTargetModel) const
+{
+ return new SdrObjGroup(rTargetModel, *this);
+}
+
+OUString SdrObjGroup::TakeObjNameSingul() const
+{
+ OUString sName;
+
+ if(0 == GetObjCount())
+ {
+ sName = SvxResId(STR_ObjNameSingulGRUPEMPTY);
+ }
+ else
+ {
+ if(isDiagram())
+ sName = SvxResId(STR_ObjNameSingulDIAGRAM);
+ else
+ sName = SvxResId(STR_ObjNameSingulGRUP);
+ }
+
+ const OUString aName(GetName());
+
+ if (!aName.isEmpty())
+ sName += " '" + aName + "'";
+
+ return sName;
+}
+
+
+OUString SdrObjGroup::TakeObjNamePlural() const
+{
+ if(0 == GetObjCount())
+ return SvxResId(STR_ObjNamePluralGRUPEMPTY);
+ if(isDiagram())
+ return SvxResId(RID_GALLERYSTR_THEME_DIAGRAMS);
+ return SvxResId(STR_ObjNamePluralGRUP);
+}
+
+
+void SdrObjGroup::RecalcSnapRect()
+{
+ // TODO: unnecessary, because we use the Rects from the SubList
+}
+
+basegfx::B2DPolyPolygon SdrObjGroup::TakeXorPoly() const
+{
+ basegfx::B2DPolyPolygon aRetval;
+ const size_t nObjCount(GetObjCount());
+
+ for(size_t a = 0; a < nObjCount; ++a)
+ {
+ SdrObject* pObj(GetObj(a));
+ aRetval.append(pObj->TakeXorPoly());
+ }
+
+ if(!aRetval.count())
+ {
+ const basegfx::B2DRange aRange = vcl::unotools::b2DRectangleFromRectangle(m_aOutRect);
+ aRetval.append(basegfx::utils::createPolygonFromRect(aRange));
+ }
+
+ return aRetval;
+}
+
+bool SdrObjGroup::beginSpecialDrag(SdrDragStat& /*rDrag*/) const
+{
+ return false;
+}
+
+
+bool SdrObjGroup::BegCreate(SdrDragStat& /*rStat*/)
+{
+ return false;
+}
+
+
+Degree100 SdrObjGroup::GetRotateAngle() const
+{
+ Degree100 nRetval(0);
+
+ if(0 != GetObjCount())
+ {
+ SdrObject* pObj(GetObj(0));
+
+ nRetval = pObj->GetRotateAngle();
+ }
+
+ return nRetval;
+}
+
+
+Degree100 SdrObjGroup::GetShearAngle(bool /*bVertical*/) const
+{
+ Degree100 nRetval(0);
+
+ if(0 != GetObjCount())
+ {
+ SdrObject* pObj(GetObj(0));
+
+ nRetval = pObj->GetShearAngle();
+ }
+
+ return nRetval;
+}
+
+
+void SdrObjGroup::NbcSetSnapRect(const tools::Rectangle& rRect)
+{
+ tools::Rectangle aOld(GetSnapRect());
+ tools::Long nMulX=rRect.Right()-rRect.Left();
+ tools::Long nDivX=aOld.Right()-aOld.Left();
+ tools::Long nMulY=rRect.Bottom()-rRect.Top();
+ tools::Long nDivY=aOld.Bottom()-aOld.Top();
+ if (nDivX==0) { nMulX=1; nDivX=1; }
+ if (nDivY==0) { nMulY=1; nDivY=1; }
+ if (nMulX!=nDivX || nMulY!=nDivY) {
+ Fraction aX(nMulX,nDivX);
+ Fraction aY(nMulY,nDivY);
+ NbcResize(aOld.TopLeft(),aX,aY);
+ }
+ if (rRect.Left()!=aOld.Left() || rRect.Top()!=aOld.Top()) {
+ NbcMove(Size(rRect.Left()-aOld.Left(),rRect.Top()-aOld.Top()));
+ }
+}
+
+
+void SdrObjGroup::NbcSetLogicRect(const tools::Rectangle& rRect)
+{
+ NbcSetSnapRect(rRect);
+}
+
+
+void SdrObjGroup::NbcMove(const Size& rSiz)
+{
+ maRefPoint.Move(rSiz);
+ const size_t nObjCount(GetObjCount());
+
+ if(0 != nObjCount)
+ {
+ for (size_t i=0; i<nObjCount; ++i)
+ {
+ SdrObject* pObj(GetObj(i));
+ pObj->NbcMove(rSiz);
+ }
+ }
+ else
+ {
+ m_aOutRect.Move(rSiz);
+ SetBoundAndSnapRectsDirty();
+ }
+}
+
+
+void SdrObjGroup::NbcResize(const Point& rRef, const Fraction& xFact, const Fraction& yFact)
+{
+ bool bXMirr=(xFact.GetNumerator()<0) != (xFact.GetDenominator()<0);
+ bool bYMirr=(yFact.GetNumerator()<0) != (yFact.GetDenominator()<0);
+ if (bXMirr || bYMirr) {
+ Point aRef1(GetSnapRect().Center());
+ if (bXMirr) {
+ Point aRef2(aRef1);
+ aRef2.AdjustY( 1 );
+ NbcMirrorGluePoints(aRef1,aRef2);
+ }
+ if (bYMirr) {
+ Point aRef2(aRef1);
+ aRef2.AdjustX( 1 );
+ NbcMirrorGluePoints(aRef1,aRef2);
+ }
+ }
+
+ ResizePoint(maRefPoint, rRef, xFact, yFact);
+
+ const size_t nObjCount(GetObjCount());
+ if(0 != nObjCount)
+ {
+ for (size_t i=0; i<nObjCount; ++i)
+ {
+ SdrObject* pObj(GetObj(i));
+ pObj->NbcResize(rRef,xFact,yFact);
+ }
+ }
+ else
+ {
+ ResizeRect(m_aOutRect,rRef,xFact,yFact);
+ SetBoundAndSnapRectsDirty();
+ }
+}
+
+
+void SdrObjGroup::NbcRotate(const Point& rRef, Degree100 nAngle, double sn, double cs)
+{
+ SetGlueReallyAbsolute(true);
+ RotatePoint(maRefPoint, rRef, sn, cs);
+ const size_t nObjCount(GetObjCount());
+
+ for (size_t i=0; i<nObjCount; ++i)
+ {
+ SdrObject* pObj(GetObj(i));
+ pObj->NbcRotate(rRef,nAngle,sn,cs);
+ }
+
+ NbcRotateGluePoints(rRef,nAngle,sn,cs);
+ SetGlueReallyAbsolute(false);
+}
+
+
+void SdrObjGroup::NbcMirror(const Point& rRef1, const Point& rRef2)
+{
+ SetGlueReallyAbsolute(true);
+ MirrorPoint(maRefPoint, rRef1, rRef2); // implementation missing in SvdEtc!
+ const size_t nObjCount(GetObjCount());
+
+ for (size_t i=0; i<nObjCount; ++i)
+ {
+ SdrObject* pObj(GetObj(i));
+ pObj->NbcMirror(rRef1,rRef2);
+ }
+
+ NbcMirrorGluePoints(rRef1,rRef2);
+ SetGlueReallyAbsolute(false);
+}
+
+
+void SdrObjGroup::NbcShear(const Point& rRef, Degree100 nAngle, double tn, bool bVShear)
+{
+ SetGlueReallyAbsolute(true);
+ ShearPoint(maRefPoint, rRef, tn);
+ const size_t nObjCount(GetObjCount());
+
+ for (size_t i=0; i<nObjCount; ++i)
+ {
+ SdrObject* pObj(GetObj(i));
+ pObj->NbcShear(rRef,nAngle,tn,bVShear);
+ }
+
+ NbcShearGluePoints(rRef,tn,bVShear);
+ SetGlueReallyAbsolute(false);
+}
+
+
+void SdrObjGroup::NbcSetAnchorPos(const Point& rPnt)
+{
+ m_aAnchor=rPnt;
+ Size aSiz(rPnt.X()-m_aAnchor.X(),rPnt.Y()-m_aAnchor.Y());
+ maRefPoint.Move(aSiz);
+ const size_t nObjCount(GetObjCount());
+
+ for (size_t i=0; i<nObjCount; ++i)
+ {
+ SdrObject* pObj(GetObj(i));
+ pObj->NbcSetAnchorPos(rPnt);
+ }
+}
+
+
+void SdrObjGroup::SetSnapRect(const tools::Rectangle& rRect)
+{
+ tools::Rectangle aBoundRect0; if (m_pUserCall!=nullptr) aBoundRect0=GetLastBoundRect();
+ tools::Rectangle aOld(GetSnapRect());
+ if (aOld.IsEmpty())
+ {
+ Fraction aX(1,1);
+ Fraction aY(1,1);
+ Resize(aOld.TopLeft(),aX,aY);
+ }
+ else
+ {
+ tools::Long nMulX=rRect.Right()-rRect.Left();
+ tools::Long nDivX=aOld.Right()-aOld.Left();
+ tools::Long nMulY=rRect.Bottom()-rRect.Top();
+ tools::Long nDivY=aOld.Bottom()-aOld.Top();
+ if (nDivX==0) { nMulX=1; nDivX=1; }
+ if (nDivY==0) { nMulY=1; nDivY=1; }
+ if (nMulX!=nDivX || nMulY!=nDivY) {
+ Fraction aX(nMulX,nDivX);
+ Fraction aY(nMulY,nDivY);
+ Resize(aOld.TopLeft(),aX,aY);
+ }
+ }
+ if (rRect.Left()!=aOld.Left() || rRect.Top()!=aOld.Top()) {
+ Move(Size(rRect.Left()-aOld.Left(),rRect.Top()-aOld.Top()));
+ }
+
+ SetChanged();
+ BroadcastObjectChange();
+ SendUserCall(SdrUserCallType::Resize,aBoundRect0);
+}
+
+
+void SdrObjGroup::SetLogicRect(const tools::Rectangle& rRect)
+{
+ SetSnapRect(rRect);
+}
+
+
+void SdrObjGroup::Move(const Size& rSiz)
+{
+ if (rSiz.Width()==0 && rSiz.Height()==0)
+ return;
+
+ tools::Rectangle aBoundRect0; if (m_pUserCall!=nullptr) aBoundRect0=GetLastBoundRect();
+ maRefPoint.Move(rSiz);
+ const size_t nObjCount(GetObjCount());
+
+ if(0 != nObjCount)
+ {
+ // first move the connectors, then everything else
+ for (size_t i=0; i<nObjCount; ++i)
+ {
+ SdrObject* pObj(GetObj(i));
+ if (pObj->IsEdgeObj())
+ pObj->Move(rSiz);
+ }
+
+ for (size_t i=0; i<nObjCount; ++i)
+ {
+ SdrObject* pObj(GetObj(i));
+ if (!pObj->IsEdgeObj())
+ pObj->Move(rSiz);
+ }
+ }
+ else
+ {
+ m_aOutRect.Move(rSiz);
+ SetBoundAndSnapRectsDirty();
+ }
+
+ SetChanged();
+ BroadcastObjectChange();
+ SendUserCall(SdrUserCallType::MoveOnly,aBoundRect0);
+}
+
+
+void SdrObjGroup::Resize(const Point& rRef, const Fraction& xFact, const Fraction& yFact, bool bUnsetRelative)
+{
+ if (xFact.GetNumerator()==xFact.GetDenominator() && yFact.GetNumerator()==yFact.GetDenominator())
+ return;
+
+ bool bXMirr=(xFact.GetNumerator()<0) != (xFact.GetDenominator()<0);
+ bool bYMirr=(yFact.GetNumerator()<0) != (yFact.GetDenominator()<0);
+ if (bXMirr || bYMirr) {
+ Point aRef1(GetSnapRect().Center());
+ if (bXMirr) {
+ Point aRef2(aRef1);
+ aRef2.AdjustY( 1 );
+ NbcMirrorGluePoints(aRef1,aRef2);
+ }
+ if (bYMirr) {
+ Point aRef2(aRef1);
+ aRef2.AdjustX( 1 );
+ NbcMirrorGluePoints(aRef1,aRef2);
+ }
+ }
+ tools::Rectangle aBoundRect0; if (m_pUserCall!=nullptr) aBoundRect0=GetLastBoundRect();
+ ResizePoint(maRefPoint, rRef, xFact, yFact);
+ const size_t nObjCount(GetObjCount());
+
+ if(0 != nObjCount)
+ {
+ // move the connectors first, everything else afterwards
+ for (size_t i=0; i<nObjCount; ++i)
+ {
+ SdrObject* pObj(GetObj(i));
+ if (pObj->IsEdgeObj())
+ pObj->Resize(rRef,xFact,yFact,bUnsetRelative);
+ }
+
+ for (size_t i=0; i<nObjCount; ++i)
+ {
+ SdrObject* pObj(GetObj(i));
+ if (!pObj->IsEdgeObj())
+ pObj->Resize(rRef,xFact,yFact,bUnsetRelative);
+ }
+ }
+ else
+ {
+ ResizeRect(m_aOutRect,rRef,xFact,yFact);
+ SetBoundAndSnapRectsDirty();
+ }
+
+ SetChanged();
+ BroadcastObjectChange();
+ SendUserCall(SdrUserCallType::Resize,aBoundRect0);
+}
+
+
+void SdrObjGroup::Rotate(const Point& rRef, Degree100 nAngle, double sn, double cs)
+{
+ if (nAngle==0_deg100)
+ return;
+
+ SetGlueReallyAbsolute(true);
+ tools::Rectangle aBoundRect0; if (m_pUserCall!=nullptr) aBoundRect0=GetLastBoundRect();
+ RotatePoint(maRefPoint, rRef, sn, cs);
+ // move the connectors first, everything else afterwards
+ const size_t nObjCount(GetObjCount());
+
+ for (size_t i=0; i<nObjCount; ++i)
+ {
+ SdrObject* pObj(GetObj(i));
+ if (pObj->IsEdgeObj())
+ pObj->Rotate(rRef,nAngle,sn,cs);
+ }
+
+ for (size_t i=0; i<nObjCount; ++i)
+ {
+ SdrObject* pObj(GetObj(i));
+ if (!pObj->IsEdgeObj())
+ pObj->Rotate(rRef,nAngle,sn,cs);
+ }
+
+ NbcRotateGluePoints(rRef,nAngle,sn,cs);
+ SetGlueReallyAbsolute(false);
+ SetChanged();
+ BroadcastObjectChange();
+ SendUserCall(SdrUserCallType::Resize,aBoundRect0);
+}
+
+
+void SdrObjGroup::Mirror(const Point& rRef1, const Point& rRef2)
+{
+ SetGlueReallyAbsolute(true);
+ tools::Rectangle aBoundRect0; if (m_pUserCall!=nullptr) aBoundRect0=GetLastBoundRect();
+ MirrorPoint(maRefPoint, rRef1, rRef2); // implementation missing in SvdEtc!
+ // move the connectors first, everything else afterwards
+ const size_t nObjCount(GetObjCount());
+
+ for (size_t i=0; i<nObjCount; ++i)
+ {
+ SdrObject* pObj(GetObj(i));
+ if (pObj->IsEdgeObj())
+ pObj->Mirror(rRef1,rRef2);
+ }
+
+ for (size_t i=0; i<nObjCount; ++i)
+ {
+ SdrObject* pObj(GetObj(i));
+ if (!pObj->IsEdgeObj())
+ pObj->Mirror(rRef1,rRef2);
+ }
+
+ NbcMirrorGluePoints(rRef1,rRef2);
+ SetGlueReallyAbsolute(false);
+ SetChanged();
+ BroadcastObjectChange();
+ SendUserCall(SdrUserCallType::Resize,aBoundRect0);
+}
+
+
+void SdrObjGroup::Shear(const Point& rRef, Degree100 nAngle, double tn, bool bVShear)
+{
+ if (nAngle==0_deg100)
+ return;
+
+ SetGlueReallyAbsolute(true);
+ tools::Rectangle aBoundRect0; if (m_pUserCall!=nullptr) aBoundRect0=GetLastBoundRect();
+ ShearPoint(maRefPoint, rRef, tn);
+ // move the connectors first, everything else afterwards
+ const size_t nObjCount(GetObjCount());
+
+ for (size_t i=0; i<nObjCount; ++i)
+ {
+ SdrObject* pObj(GetObj(i));
+ if (pObj->IsEdgeObj())
+ pObj->Shear(rRef,nAngle,tn,bVShear);
+ }
+
+ for (size_t i=0; i<nObjCount; ++i)
+ {
+ SdrObject* pObj(GetObj(i));
+ if (!pObj->IsEdgeObj())
+ pObj->Shear(rRef,nAngle,tn,bVShear);
+ }
+
+ NbcShearGluePoints(rRef,tn,bVShear);
+ SetGlueReallyAbsolute(false);
+ SetChanged();
+ BroadcastObjectChange();
+ SendUserCall(SdrUserCallType::Resize,aBoundRect0);
+
+}
+
+
+void SdrObjGroup::SetAnchorPos(const Point& rPnt)
+{
+ tools::Rectangle aBoundRect0; if (m_pUserCall!=nullptr) aBoundRect0=GetLastBoundRect();
+ bool bChg=m_aAnchor!=rPnt;
+ m_aAnchor=rPnt;
+ Size aSiz(rPnt.X()-m_aAnchor.X(),rPnt.Y()-m_aAnchor.Y());
+ maRefPoint.Move(aSiz);
+ // move the connectors first, everything else afterwards
+ const size_t nObjCount(GetObjCount());
+
+ for (size_t i=0; i<nObjCount; ++i)
+ {
+ SdrObject* pObj(GetObj(i));
+ if (pObj->IsEdgeObj())
+ pObj->SetAnchorPos(rPnt);
+ }
+
+ for (size_t i=0; i<nObjCount; ++i)
+ {
+ SdrObject* pObj(GetObj(i));
+ if (!pObj->IsEdgeObj())
+ pObj->SetAnchorPos(rPnt);
+ }
+
+ if (bChg)
+ {
+ SetChanged();
+ BroadcastObjectChange();
+ SendUserCall(SdrUserCallType::MoveOnly,aBoundRect0);
+ }
+}
+
+
+void SdrObjGroup::NbcSetRelativePos(const Point& rPnt)
+{
+ Point aRelPos0(GetSnapRect().TopLeft()-m_aAnchor);
+ Size aSiz(rPnt.X()-aRelPos0.X(),rPnt.Y()-aRelPos0.Y());
+ NbcMove(aSiz); // this also calls SetRectsDirty()
+}
+
+void SdrObjGroup::SetRelativePos(const Point& rPnt)
+{
+ Point aRelPos0(GetSnapRect().TopLeft()-m_aAnchor);
+ Size aSiz(rPnt.X()-aRelPos0.X(),rPnt.Y()-aRelPos0.Y());
+ if (aSiz.Width()!=0 || aSiz.Height()!=0) Move(aSiz); // this also calls SetRectsDirty() and Broadcast, ...
+}
+
+void SdrObjGroup::NbcReformatText()
+{
+ NbcReformatAllTextObjects();
+}
+
+SdrObjectUniquePtr SdrObjGroup::DoConvertToPolyObj(bool bBezier, bool bAddText) const
+{
+ SdrObjectUniquePtr pGroup( new SdrObjGroup(getSdrModelFromSdrObject()) );
+ const size_t nObjCount(GetObjCount());
+
+ for(size_t a=0; a < nObjCount; ++a)
+ {
+ SdrObject* pIterObj(GetObj(a));
+ SdrObjectUniquePtr pResult(pIterObj->DoConvertToPolyObj(bBezier, bAddText));
+
+ // pResult can be NULL e.g. for empty objects
+ if( pResult )
+ pGroup->GetSubList()->NbcInsertObject(pResult.release());
+ }
+
+ return pGroup;
+}
+
+void SdrObjGroup::dumpAsXml(xmlTextWriterPtr pWriter) const
+{
+ (void)xmlTextWriterStartElement(pWriter, BAD_CAST("SdrObjGroup"));
+ (void)xmlTextWriterWriteFormatAttribute(pWriter, BAD_CAST("ptr"), "%p", this);
+
+ SdrObject::dumpAsXml(pWriter);
+ SdrObjList::dumpAsXml(pWriter);
+
+ (void)xmlTextWriterEndElement(pWriter);
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/svdraw/svdomeas.cxx b/svx/source/svdraw/svdomeas.cxx
new file mode 100644
index 000000000..da9959c84
--- /dev/null
+++ b/svx/source/svdraw/svdomeas.cxx
@@ -0,0 +1,1429 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * 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/dialmgr.hxx>
+#include <svx/strings.hrc>
+
+#include <basegfx/matrix/b2dhommatrix.hxx>
+#include <basegfx/matrix/b2dhommatrixtools.hxx>
+#include <basegfx/point/b2dpoint.hxx>
+#include <basegfx/polygon/b2dpolygon.hxx>
+#include <basegfx/polygon/b2dpolypolygon.hxx>
+#include <editeng/editdata.hxx>
+#include <editeng/editobj.hxx>
+#include <editeng/eeitem.hxx>
+#include <editeng/flditem.hxx>
+#include <editeng/measfld.hxx>
+#include <editeng/outlobj.hxx>
+#include <math.h>
+#include <svl/style.hxx>
+
+#include <sdr/contact/viewcontactofsdrmeasureobj.hxx>
+#include <sdr/properties/measureproperties.hxx>
+#include <svx/svddrag.hxx>
+#include <svx/svdhdl.hxx>
+#include <svx/svdmodel.hxx>
+#include <svx/svdogrp.hxx>
+#include <svx/svdomeas.hxx>
+#include <svx/svdopath.hxx>
+#include <svx/svdoutl.hxx>
+#include <svx/svdpage.hxx>
+#include <svx/svdtrans.hxx>
+#include <svx/svdview.hxx>
+#include <svx/sxmbritm.hxx>
+#include <svx/sxmlhitm.hxx>
+#include <sxmsitm.hxx>
+#include <sxmtaitm.hxx>
+#include <svx/sxmtfitm.hxx>
+#include <svx/sxmtpitm.hxx>
+#include <svx/sxmtritm.hxx>
+#include <svx/sxmuitm.hxx>
+#include <svx/xlnedcit.hxx>
+#include <svx/xlnedit.hxx>
+#include <svx/xlnedwit.hxx>
+#include <svx/xlnstcit.hxx>
+#include <svx/xlnstit.hxx>
+#include <svx/xlnstwit.hxx>
+#include <svx/xlnwtit.hxx>
+#include <svx/xpoly.hxx>
+#include <unotools/syslocale.hxx>
+#include <unotools/localedatawrapper.hxx>
+#include <vcl/ptrstyle.hxx>
+
+
+SdrMeasureObjGeoData::SdrMeasureObjGeoData() {}
+SdrMeasureObjGeoData::~SdrMeasureObjGeoData() {}
+
+OUString SdrMeasureObj::TakeRepresentation(SdrMeasureFieldKind eMeasureFieldKind) const
+{
+ OUString aStr;
+ Fraction aMeasureScale(1, 1);
+ bool bTextRota90(false);
+ bool bShowUnit(false);
+ FieldUnit eMeasureUnit(FieldUnit::NONE);
+ FieldUnit eModUIUnit(FieldUnit::NONE);
+
+ const SfxItemSet& rSet = GetMergedItemSet();
+ bTextRota90 = rSet.Get(SDRATTR_MEASURETEXTROTA90).GetValue();
+ eMeasureUnit = rSet.Get(SDRATTR_MEASUREUNIT).GetValue();
+ aMeasureScale = rSet.Get(SDRATTR_MEASURESCALE).GetValue();
+ bShowUnit = rSet.Get(SDRATTR_MEASURESHOWUNIT).GetValue();
+ sal_Int16 nNumDigits = rSet.Get(SDRATTR_MEASUREDECIMALPLACES).GetValue();
+
+ switch(eMeasureFieldKind)
+ {
+ case SdrMeasureFieldKind::Value:
+ {
+ eModUIUnit = getSdrModelFromSdrObject().GetUIUnit();
+
+ if(eMeasureUnit == FieldUnit::NONE)
+ eMeasureUnit = eModUIUnit;
+
+ sal_Int32 nLen(GetLen(aPt2 - aPt1));
+ Fraction aFact(1,1);
+
+ if(eMeasureUnit != eModUIUnit)
+ {
+ // for the unit conversion
+ aFact *= GetMapFactor(eModUIUnit, eMeasureUnit).X();
+ }
+
+ if(aMeasureScale.GetNumerator() != aMeasureScale.GetDenominator())
+ {
+ aFact *= aMeasureScale;
+ }
+
+ if(aFact.GetNumerator() != aFact.GetDenominator())
+ {
+ // scale via BigInt, to avoid overruns
+ nLen = BigMulDiv(nLen, aFact.GetNumerator(), aFact.GetDenominator());
+ }
+
+ if(!aFact.IsValid())
+ {
+ aStr = "?";
+ }
+ else
+ {
+ aStr = getSdrModelFromSdrObject().GetMetricString(nLen, true, nNumDigits);
+ }
+
+ SvtSysLocale aSysLocale;
+ const LocaleDataWrapper& rLocaleDataWrapper = aSysLocale.GetLocaleData();
+ sal_Unicode cDec(rLocaleDataWrapper.getNumDecimalSep()[0]);
+ sal_Unicode cDecAlt(rLocaleDataWrapper.getNumDecimalSepAlt().toChar());
+
+ if(aStr.indexOf(cDec) != -1 || (cDecAlt && aStr.indexOf(cDecAlt) != -1))
+ {
+ sal_Int32 nLen2(aStr.getLength() - 1);
+
+ while(aStr[nLen2] == '0')
+ {
+ aStr = aStr.copy(0, nLen2);
+ nLen2--;
+ }
+
+ if(aStr[nLen2] == cDec || (cDecAlt && aStr[nLen2] == cDecAlt))
+ {
+ aStr = aStr.copy(0, nLen2);
+ nLen2--;
+ }
+
+ if(aStr.isEmpty())
+ aStr += "0";
+ }
+
+ break;
+ }
+ case SdrMeasureFieldKind::Unit:
+ {
+ if(bShowUnit)
+ {
+ eModUIUnit = getSdrModelFromSdrObject().GetUIUnit();
+
+ if(eMeasureUnit == FieldUnit::NONE)
+ eMeasureUnit = eModUIUnit;
+
+ aStr = SdrModel::GetUnitString(eMeasureUnit);
+ }
+
+ break;
+ }
+ case SdrMeasureFieldKind::Rotate90Blanks:
+ {
+ if(bTextRota90)
+ {
+ aStr = " ";
+ }
+
+ break;
+ }
+ }
+ return aStr;
+}
+
+
+// BaseProperties section
+
+std::unique_ptr<sdr::properties::BaseProperties> SdrMeasureObj::CreateObjectSpecificProperties()
+{
+ return std::make_unique<sdr::properties::MeasureProperties>(*this);
+}
+
+
+// DrawContact section
+
+std::unique_ptr<sdr::contact::ViewContact> SdrMeasureObj::CreateObjectSpecificViewContact()
+{
+ return std::make_unique<sdr::contact::ViewContactOfSdrMeasureObj>(*this);
+}
+
+
+SdrMeasureObj::SdrMeasureObj(SdrModel& rSdrModel)
+: SdrTextObj(rSdrModel),
+ bTextDirty(false)
+{
+ // #i25616#
+ mbSupportTextIndentingOnLineWidthChange = false;
+}
+
+SdrMeasureObj::SdrMeasureObj(SdrModel& rSdrModel, SdrMeasureObj const & rSource)
+: SdrTextObj(rSdrModel, rSource),
+ bTextDirty(false)
+{
+ // #i25616#
+ mbSupportTextIndentingOnLineWidthChange = false;
+
+ aPt1 = rSource.aPt1;
+ aPt2 = rSource.aPt2;
+ bTextDirty = rSource.bTextDirty;
+}
+
+SdrMeasureObj::SdrMeasureObj(
+ SdrModel& rSdrModel,
+ const Point& rPt1,
+ const Point& rPt2)
+: SdrTextObj(rSdrModel),
+ aPt1(rPt1),
+ aPt2(rPt2),
+ bTextDirty(false)
+{
+ // #i25616#
+ mbSupportTextIndentingOnLineWidthChange = false;
+}
+
+SdrMeasureObj::~SdrMeasureObj()
+{
+}
+
+void SdrMeasureObj::TakeObjInfo(SdrObjTransformInfoRec& rInfo) const
+{
+ rInfo.bMoveAllowed =true;
+ rInfo.bResizeFreeAllowed=true;
+ rInfo.bResizePropAllowed=true;
+ rInfo.bRotateFreeAllowed=true;
+ rInfo.bRotate90Allowed =true;
+ rInfo.bMirrorFreeAllowed=true;
+ rInfo.bMirror45Allowed =true;
+ rInfo.bMirror90Allowed =true;
+ rInfo.bTransparenceAllowed = false;
+ rInfo.bShearAllowed =true;
+ rInfo.bEdgeRadiusAllowed=false;
+ rInfo.bNoOrthoDesired =true;
+ rInfo.bNoContortion =false;
+ rInfo.bCanConvToPath =false;
+ rInfo.bCanConvToPoly =true;
+ rInfo.bCanConvToPathLineToArea=false;
+ rInfo.bCanConvToPolyLineToArea=false;
+ rInfo.bCanConvToContour = LineGeometryUsageIsNecessary();
+}
+
+SdrObjKind SdrMeasureObj::GetObjIdentifier() const
+{
+ return SdrObjKind::Measure;
+}
+
+struct ImpMeasureRec : public SdrDragStatUserData
+{
+ Point aPt1;
+ Point aPt2;
+ css::drawing::MeasureTextHorzPos eWantTextHPos;
+ css::drawing::MeasureTextVertPos eWantTextVPos;
+ tools::Long nLineDist;
+ tools::Long nHelplineOverhang;
+ tools::Long nHelplineDist;
+ tools::Long nHelpline1Len;
+ tools::Long nHelpline2Len;
+ bool bBelowRefEdge;
+ bool bTextRota90;
+ bool bTextUpsideDown;
+ bool bTextAutoAngle;
+ Degree100 nTextAutoAngleView;
+};
+
+namespace {
+
+struct ImpLineRec
+{
+ Point aP1;
+ Point aP2;
+};
+
+}
+
+struct ImpMeasurePoly
+{
+ ImpLineRec aMainline1; // those with the 1st arrowhead
+ ImpLineRec aMainline2; // those with the 2nd arrowhead
+ ImpLineRec aMainline3; // those in between
+ ImpLineRec aHelpline1;
+ ImpLineRec aHelpline2;
+ Size aTextSize;
+ tools::Long nLineLen;
+ Degree100 nLineAngle;
+ Degree100 nTextAngle;
+ Degree100 nHlpAngle;
+ double nLineSin;
+ double nLineCos;
+ sal_uInt16 nMainlineCnt;
+ css::drawing::MeasureTextHorzPos eUsedTextHPos;
+ css::drawing::MeasureTextVertPos eUsedTextVPos;
+ tools::Long nLineWdt2; // half the line width
+ tools::Long nArrow1Len; // length of 1st arrowhead; for Center, use only half
+ tools::Long nArrow2Len; // length of 2nd arrowhead; for Center, use only half
+ tools::Long nArrow1Wdt; // width of 1st arrow
+ tools::Long nArrow2Wdt; // width of 2nd arrow
+ tools::Long nShortLineLen; // line length, if PfeileAussen (arrowheads on the outside)
+ bool bAutoUpsideDown; // UpsideDown via automation
+ bool bBreakedLine;
+};
+
+void SdrMeasureObj::ImpTakeAttr(ImpMeasureRec& rRec) const
+{
+ rRec.aPt1 = aPt1;
+ rRec.aPt2 = aPt2;
+
+ const SfxItemSet& rSet = GetObjectItemSet();
+ rRec.eWantTextHPos =rSet.Get(SDRATTR_MEASURETEXTHPOS ).GetValue();
+ rRec.eWantTextVPos =rSet.Get(SDRATTR_MEASURETEXTVPOS ).GetValue();
+ rRec.nLineDist =rSet.Get(SDRATTR_MEASURELINEDIST ).GetValue();
+ rRec.nHelplineOverhang =rSet.Get(SDRATTR_MEASUREHELPLINEOVERHANG).GetValue();
+ rRec.nHelplineDist =rSet.Get(SDRATTR_MEASUREHELPLINEDIST ).GetValue();
+ rRec.nHelpline1Len =rSet.Get(SDRATTR_MEASUREHELPLINE1LEN ).GetValue();
+ rRec.nHelpline2Len =rSet.Get(SDRATTR_MEASUREHELPLINE2LEN ).GetValue();
+ rRec.bBelowRefEdge =rSet.Get(SDRATTR_MEASUREBELOWREFEDGE ).GetValue();
+ rRec.bTextRota90 =rSet.Get(SDRATTR_MEASURETEXTROTA90 ).GetValue();
+ rRec.bTextUpsideDown =static_cast<const SdrMeasureTextUpsideDownItem& >(rSet.Get(SDRATTR_MEASURETEXTUPSIDEDOWN )).GetValue();
+ rRec.bTextAutoAngle =rSet.Get(SDRATTR_MEASURETEXTAUTOANGLE ).GetValue();
+ rRec.nTextAutoAngleView=static_cast<const SdrMeasureTextAutoAngleViewItem&>(rSet.Get(SDRATTR_MEASURETEXTAUTOANGLEVIEW)).GetValue();
+}
+
+static tools::Long impGetLineStartEndDistance(const basegfx::B2DPolyPolygon& rPolyPolygon, tools::Long nNewWidth, bool bCenter)
+{
+ const basegfx::B2DRange aPolygonRange(rPolyPolygon.getB2DRange());
+ const double fOldWidth(std::max(aPolygonRange.getWidth(), 1.0));
+ const double fScale(static_cast<double>(nNewWidth) / fOldWidth);
+ tools::Long nHeight(basegfx::fround(aPolygonRange.getHeight() * fScale));
+
+ if(bCenter)
+ {
+ nHeight /= 2;
+ }
+
+ return nHeight;
+}
+
+void SdrMeasureObj::ImpCalcGeometrics(const ImpMeasureRec& rRec, ImpMeasurePoly& rPol) const
+{
+ Point aP1(rRec.aPt1);
+ Point aP2(rRec.aPt2);
+ Point aDelt(aP2); aDelt-=aP1;
+
+ rPol.aTextSize=GetTextSize();
+ rPol.nLineLen=GetLen(aDelt);
+
+ rPol.nLineWdt2=0;
+ tools::Long nArrow1Len=0; bool bArrow1Center=false;
+ tools::Long nArrow2Len=0; bool bArrow2Center=false;
+ tools::Long nArrow1Wdt=0;
+ tools::Long nArrow2Wdt=0;
+ rPol.nArrow1Wdt=0;
+ rPol.nArrow2Wdt=0;
+ tools::Long nArrowNeed=0;
+ tools::Long nShortLen=0;
+ bool bPfeileAussen = false;
+
+ const SfxItemSet& rSet = GetObjectItemSet();
+ sal_Int32 nLineWdt = rSet.Get(XATTR_LINEWIDTH).GetValue(); // line width
+ rPol.nLineWdt2 = (nLineWdt + 1) / 2;
+
+ nArrow1Wdt = rSet.Get(XATTR_LINESTARTWIDTH).GetValue();
+ if(nArrow1Wdt < 0)
+ nArrow1Wdt = -nLineWdt * nArrow1Wdt / 100; // <0 = relative
+
+ nArrow2Wdt = rSet.Get(XATTR_LINEENDWIDTH).GetValue();
+ if(nArrow2Wdt < 0)
+ nArrow2Wdt = -nLineWdt * nArrow2Wdt / 100; // <0 = relative
+
+ basegfx::B2DPolyPolygon aPol1(rSet.Get(XATTR_LINESTART).GetLineStartValue());
+ basegfx::B2DPolyPolygon aPol2(rSet.Get(XATTR_LINEEND).GetLineEndValue());
+ bArrow1Center = rSet.Get(XATTR_LINESTARTCENTER).GetValue();
+ bArrow2Center = rSet.Get(XATTR_LINEENDCENTER).GetValue();
+ nArrow1Len = impGetLineStartEndDistance(aPol1, nArrow1Wdt, bArrow1Center) - 1;
+ nArrow2Len = impGetLineStartEndDistance(aPol2, nArrow2Wdt, bArrow2Center) - 1;
+
+ // nArrowLen is already halved at bCenter.
+ // In the case of 2 arrowheads each 4mm long, we can't go below 10mm.
+ nArrowNeed=nArrow1Len+nArrow2Len+(nArrow1Wdt+nArrow2Wdt)/2;
+ if (rPol.nLineLen<nArrowNeed) bPfeileAussen = true;
+ nShortLen=(nArrow1Len+nArrow1Wdt + nArrow2Len+nArrow2Wdt) /2;
+
+ rPol.eUsedTextHPos=rRec.eWantTextHPos;
+ rPol.eUsedTextVPos=rRec.eWantTextVPos;
+ if (rPol.eUsedTextVPos == css::drawing::MeasureTextVertPos_AUTO)
+ rPol.eUsedTextVPos = css::drawing::MeasureTextVertPos_EAST;
+ bool bBrkLine=false;
+ if (rPol.eUsedTextVPos == css::drawing::MeasureTextVertPos_CENTERED)
+ {
+ OutlinerParaObject* pOutlinerParaObject = SdrTextObj::GetOutlinerParaObject();
+ if (pOutlinerParaObject!=nullptr && pOutlinerParaObject->GetTextObject().GetParagraphCount()==1)
+ {
+ bBrkLine=true; // dashed line if there's only on paragraph.
+ }
+ }
+ rPol.bBreakedLine=bBrkLine;
+ if (rPol.eUsedTextHPos==css::drawing::MeasureTextHorzPos_AUTO) { // if text is too wide, push it outside
+ bool bOutside = false;
+ tools::Long nNeedSiz=!rRec.bTextRota90 ? rPol.aTextSize.Width() : rPol.aTextSize.Height();
+ if (nNeedSiz>rPol.nLineLen) bOutside = true; // text doesn't fit in between
+ if (bBrkLine) {
+ if (nNeedSiz+nArrowNeed>rPol.nLineLen) bPfeileAussen = true; // text fits in between, if arrowheads are on the outside
+ } else {
+ tools::Long nSmallNeed=nArrow1Len+nArrow2Len+(nArrow1Wdt+nArrow2Wdt)/2/4;
+ if (nNeedSiz+nSmallNeed>rPol.nLineLen) bPfeileAussen = true; // text fits in between, if arrowheads are on the outside
+ }
+ rPol.eUsedTextHPos=bOutside ? css::drawing::MeasureTextHorzPos_LEFTOUTSIDE : css::drawing::MeasureTextHorzPos_INSIDE;
+ }
+ if (rPol.eUsedTextHPos != css::drawing::MeasureTextHorzPos_INSIDE) bPfeileAussen = true;
+ rPol.nArrow1Wdt=nArrow1Wdt;
+ rPol.nArrow2Wdt=nArrow2Wdt;
+ rPol.nShortLineLen=nShortLen;
+ rPol.nArrow1Len=nArrow1Len;
+ rPol.nArrow2Len=nArrow2Len;
+
+ rPol.nLineAngle=GetAngle(aDelt);
+ double a = toRadians(rPol.nLineAngle);
+ double nLineSin=sin(a);
+ double nLineCos=cos(a);
+ rPol.nLineSin=nLineSin;
+ rPol.nLineCos=nLineCos;
+
+ rPol.nTextAngle=rPol.nLineAngle;
+ if (rRec.bTextRota90) rPol.nTextAngle+=9000_deg100;
+
+ rPol.bAutoUpsideDown=false;
+ if (rRec.bTextAutoAngle) {
+ Degree100 nTmpAngle=NormAngle36000(rPol.nTextAngle-rRec.nTextAutoAngleView);
+ if (nTmpAngle>=18000_deg100) {
+ rPol.nTextAngle+=18000_deg100;
+ rPol.bAutoUpsideDown=true;
+ }
+ }
+
+ if (rRec.bTextUpsideDown) rPol.nTextAngle+=18000_deg100;
+ rPol.nTextAngle=NormAngle36000(rPol.nTextAngle);
+ rPol.nHlpAngle=rPol.nLineAngle+9000_deg100;
+ if (rRec.bBelowRefEdge) rPol.nHlpAngle+=18000_deg100;
+ rPol.nHlpAngle=NormAngle36000(rPol.nHlpAngle);
+ double nHlpSin=nLineCos;
+ double nHlpCos=-nLineSin;
+ if (rRec.bBelowRefEdge) {
+ nHlpSin=-nHlpSin;
+ nHlpCos=-nHlpCos;
+ }
+
+ tools::Long nLineDist=rRec.nLineDist;
+ tools::Long nOverhang=rRec.nHelplineOverhang;
+ tools::Long nHelplineDist=rRec.nHelplineDist;
+
+ tools::Long dx= FRound(nLineDist*nHlpCos);
+ tools::Long dy=-FRound(nLineDist*nHlpSin);
+ tools::Long dxh1a= FRound((nHelplineDist-rRec.nHelpline1Len)*nHlpCos);
+ tools::Long dyh1a=-FRound((nHelplineDist-rRec.nHelpline1Len)*nHlpSin);
+ tools::Long dxh1b= FRound((nHelplineDist-rRec.nHelpline2Len)*nHlpCos);
+ tools::Long dyh1b=-FRound((nHelplineDist-rRec.nHelpline2Len)*nHlpSin);
+ tools::Long dxh2= FRound((nLineDist+nOverhang)*nHlpCos);
+ tools::Long dyh2=-FRound((nLineDist+nOverhang)*nHlpSin);
+
+ // extension line 1
+ rPol.aHelpline1.aP1=Point(aP1.X()+dxh1a,aP1.Y()+dyh1a);
+ rPol.aHelpline1.aP2=Point(aP1.X()+dxh2,aP1.Y()+dyh2);
+
+ // extension line 2
+ rPol.aHelpline2.aP1=Point(aP2.X()+dxh1b,aP2.Y()+dyh1b);
+ rPol.aHelpline2.aP2=Point(aP2.X()+dxh2,aP2.Y()+dyh2);
+
+ // dimension line
+ Point aMainlinePt1(aP1.X()+dx,aP1.Y()+dy);
+ Point aMainlinePt2(aP2.X()+dx,aP2.Y()+dy);
+ if (!bPfeileAussen) {
+ rPol.aMainline1.aP1=aMainlinePt1;
+ rPol.aMainline1.aP2=aMainlinePt2;
+ rPol.aMainline2=rPol.aMainline1;
+ rPol.aMainline3=rPol.aMainline1;
+ rPol.nMainlineCnt=1;
+ if (bBrkLine) {
+ tools::Long nNeedSiz=!rRec.bTextRota90 ? rPol.aTextSize.Width() : rPol.aTextSize.Height();
+ tools::Long nHalfLen=(rPol.nLineLen-nNeedSiz-nArrow1Wdt/4-nArrow2Wdt/4) /2;
+ rPol.nMainlineCnt=2;
+ rPol.aMainline1.aP2=aMainlinePt1;
+ rPol.aMainline1.aP2.AdjustX(nHalfLen );
+ RotatePoint(rPol.aMainline1.aP2,rPol.aMainline1.aP1,nLineSin,nLineCos);
+ rPol.aMainline2.aP1=aMainlinePt2;
+ rPol.aMainline2.aP1.AdjustX( -nHalfLen );
+ RotatePoint(rPol.aMainline2.aP1,rPol.aMainline2.aP2,nLineSin,nLineCos);
+ }
+ } else {
+ tools::Long nLen1=nShortLen; // arrowhead's width as line length outside of the arrowhead
+ tools::Long nLen2=nShortLen;
+ tools::Long nTextWdt=rRec.bTextRota90 ? rPol.aTextSize.Height() : rPol.aTextSize.Width();
+ if (!bBrkLine) {
+ if (rPol.eUsedTextHPos==css::drawing::MeasureTextHorzPos_LEFTOUTSIDE) nLen1=nArrow1Len+nTextWdt;
+ if (rPol.eUsedTextHPos==css::drawing::MeasureTextHorzPos_RIGHTOUTSIDE) nLen2=nArrow2Len+nTextWdt;
+ }
+ rPol.aMainline1.aP1=aMainlinePt1;
+ rPol.aMainline1.aP2=aMainlinePt1; rPol.aMainline1.aP2.AdjustX( -nLen1 ); RotatePoint(rPol.aMainline1.aP2,aMainlinePt1,nLineSin,nLineCos);
+ rPol.aMainline2.aP1=aMainlinePt2; rPol.aMainline2.aP1.AdjustX(nLen2 ); RotatePoint(rPol.aMainline2.aP1,aMainlinePt2,nLineSin,nLineCos);
+ rPol.aMainline2.aP2=aMainlinePt2;
+ rPol.aMainline3.aP1=aMainlinePt1;
+ rPol.aMainline3.aP2=aMainlinePt2;
+ rPol.nMainlineCnt=3;
+ if (bBrkLine && rPol.eUsedTextHPos==css::drawing::MeasureTextHorzPos_INSIDE) rPol.nMainlineCnt=2;
+ }
+}
+
+basegfx::B2DPolyPolygon SdrMeasureObj::ImpCalcXPoly(const ImpMeasurePoly& rPol)
+{
+ basegfx::B2DPolyPolygon aRetval;
+ basegfx::B2DPolygon aPartPolyA;
+ aPartPolyA.append(basegfx::B2DPoint(rPol.aMainline1.aP1.X(), rPol.aMainline1.aP1.Y()));
+ aPartPolyA.append(basegfx::B2DPoint(rPol.aMainline1.aP2.X(), rPol.aMainline1.aP2.Y()));
+ aRetval.append(aPartPolyA);
+
+ if(rPol.nMainlineCnt > 1)
+ {
+ aPartPolyA.clear();
+ aPartPolyA.append(basegfx::B2DPoint(rPol.aMainline2.aP1.X(), rPol.aMainline2.aP1.Y()));
+ aPartPolyA.append(basegfx::B2DPoint(rPol.aMainline2.aP2.X(), rPol.aMainline2.aP2.Y()));
+ aRetval.append(aPartPolyA);
+ }
+
+ if(rPol.nMainlineCnt > 2)
+ {
+ aPartPolyA.clear();
+ aPartPolyA.append(basegfx::B2DPoint(rPol.aMainline3.aP1.X(), rPol.aMainline3.aP1.Y()));
+ aPartPolyA.append(basegfx::B2DPoint(rPol.aMainline3.aP2.X(), rPol.aMainline3.aP2.Y()));
+ aRetval.append(aPartPolyA);
+ }
+
+ aPartPolyA.clear();
+ aPartPolyA.append(basegfx::B2DPoint(rPol.aHelpline1.aP1.X(), rPol.aHelpline1.aP1.Y()));
+ aPartPolyA.append(basegfx::B2DPoint(rPol.aHelpline1.aP2.X(), rPol.aHelpline1.aP2.Y()));
+ aRetval.append(aPartPolyA);
+
+ aPartPolyA.clear();
+ aPartPolyA.append(basegfx::B2DPoint(rPol.aHelpline2.aP1.X(), rPol.aHelpline2.aP1.Y()));
+ aPartPolyA.append(basegfx::B2DPoint(rPol.aHelpline2.aP2.X(), rPol.aHelpline2.aP2.Y()));
+ aRetval.append(aPartPolyA);
+
+ return aRetval;
+}
+
+bool SdrMeasureObj::CalcFieldValue(const SvxFieldItem& rField, sal_Int32 nPara, sal_uInt16 nPos,
+ bool bEdit,
+ std::optional<Color>& rpTxtColor, std::optional<Color>& rpFldColor, OUString& rRet) const
+{
+ const SvxFieldData* pField=rField.GetField();
+ const SdrMeasureField* pMeasureField=dynamic_cast<const SdrMeasureField*>( pField );
+ if (pMeasureField!=nullptr) {
+ rRet = TakeRepresentation(pMeasureField->GetMeasureFieldKind());
+ if (rpFldColor && !bEdit)
+ {
+ rpFldColor.reset();
+ }
+ return true;
+ } else {
+ return SdrTextObj::CalcFieldValue(rField,nPara,nPos,bEdit,rpTxtColor,rpFldColor,rRet);
+ }
+}
+
+void SdrMeasureObj::UndirtyText() const
+{
+ if (!bTextDirty)
+ return;
+
+ SdrOutliner& rOutliner=ImpGetDrawOutliner();
+ OutlinerParaObject* pOutlinerParaObject = SdrTextObj::GetOutlinerParaObject();
+ if(pOutlinerParaObject==nullptr)
+ {
+ rOutliner.QuickInsertField(SvxFieldItem(SdrMeasureField(SdrMeasureFieldKind::Rotate90Blanks), EE_FEATURE_FIELD), ESelection(0,0));
+ rOutliner.QuickInsertField(SvxFieldItem(SdrMeasureField(SdrMeasureFieldKind::Value), EE_FEATURE_FIELD),ESelection(0,1));
+ rOutliner.QuickInsertText(" ", ESelection(0,2));
+ rOutliner.QuickInsertField(SvxFieldItem(SdrMeasureField(SdrMeasureFieldKind::Unit), EE_FEATURE_FIELD),ESelection(0,3));
+ rOutliner.QuickInsertField(SvxFieldItem(SdrMeasureField(SdrMeasureFieldKind::Rotate90Blanks), EE_FEATURE_FIELD),ESelection(0,4));
+
+ if(GetStyleSheet())
+ rOutliner.SetStyleSheet(0, GetStyleSheet());
+
+ rOutliner.SetParaAttribs(0, GetObjectItemSet());
+
+ // cast to nonconst
+ const_cast<SdrMeasureObj*>(this)->NbcSetOutlinerParaObject( rOutliner.CreateParaObject() );
+ }
+ else
+ {
+ rOutliner.SetText(*pOutlinerParaObject);
+ }
+
+ rOutliner.SetUpdateLayout(true);
+ rOutliner.UpdateFields();
+ Size aSiz(rOutliner.CalcTextSize());
+ rOutliner.Clear();
+ // cast to nonconst three times
+ const_cast<SdrMeasureObj*>(this)->maTextSize = aSiz;
+ const_cast<SdrMeasureObj*>(this)->mbTextSizeDirty = false;
+ const_cast<SdrMeasureObj*>(this)->bTextDirty = false;
+}
+
+void SdrMeasureObj::TakeUnrotatedSnapRect(tools::Rectangle& rRect) const
+{
+ if (bTextDirty) UndirtyText();
+ ImpMeasureRec aRec;
+ ImpMeasurePoly aMPol;
+ ImpTakeAttr(aRec);
+ ImpCalcGeometrics(aRec,aMPol);
+
+ // determine TextSize including text frame margins
+ Size aTextSize2(aMPol.aTextSize);
+ if (aTextSize2.Width()<1) aTextSize2.setWidth(1 );
+ if (aTextSize2.Height()<1) aTextSize2.setHeight(1 );
+ aTextSize2.AdjustWidth(GetTextLeftDistance()+GetTextRightDistance() );
+ aTextSize2.AdjustHeight(GetTextUpperDistance()+GetTextLowerDistance() );
+
+ Point aPt1b(aMPol.aMainline1.aP1);
+ tools::Long nLen=aMPol.nLineLen;
+ tools::Long nLWdt=aMPol.nLineWdt2;
+ tools::Long nArr1Len=aMPol.nArrow1Len;
+ tools::Long nArr2Len=aMPol.nArrow2Len;
+ if (aMPol.bBreakedLine) {
+ // In the case of a dashed line and Outside, the text should be
+ // placed next to the line at the arrowhead instead of directly
+ // at the arrowhead.
+ nArr1Len=aMPol.nShortLineLen+aMPol.nArrow1Wdt/4;
+ nArr2Len=aMPol.nShortLineLen+aMPol.nArrow2Wdt/4;
+ }
+
+ Point aTextPos;
+ bool bRota90=aRec.bTextRota90;
+ bool bUpsideDown=aRec.bTextUpsideDown!=aMPol.bAutoUpsideDown;
+ bool bBelowRefEdge=aRec.bBelowRefEdge;
+ css::drawing::MeasureTextHorzPos eMH=aMPol.eUsedTextHPos;
+ css::drawing::MeasureTextVertPos eMV=aMPol.eUsedTextVPos;
+ if (!bRota90) {
+ switch (eMH) {
+ case css::drawing::MeasureTextHorzPos_LEFTOUTSIDE: aTextPos.setX(aPt1b.X()-aTextSize2.Width()-nArr1Len-nLWdt ); break;
+ case css::drawing::MeasureTextHorzPos_RIGHTOUTSIDE: aTextPos.setX(aPt1b.X()+nLen+nArr2Len+nLWdt ); break;
+ default: aTextPos.setX(aPt1b.X() ); aTextSize2.setWidth(nLen );
+ }
+ switch (eMV) {
+ case css::drawing::MeasureTextVertPos_CENTERED:
+ aTextPos.setY(aPt1b.Y()-aTextSize2.Height()/2 ); break;
+ case css::drawing::MeasureTextVertPos_WEST: {
+ if (!bUpsideDown) aTextPos.setY(aPt1b.Y()+nLWdt );
+ else aTextPos.setY(aPt1b.Y()-aTextSize2.Height()-nLWdt );
+ } break;
+ default: {
+ if (!bUpsideDown) aTextPos.setY(aPt1b.Y()-aTextSize2.Height()-nLWdt );
+ else aTextPos.setY(aPt1b.Y()+nLWdt );
+ }
+ }
+ if (bUpsideDown) {
+ aTextPos.AdjustX(aTextSize2.Width() );
+ aTextPos.AdjustY(aTextSize2.Height() );
+ }
+ } else { // also if bTextRota90==TRUE
+ switch (eMH) {
+ case css::drawing::MeasureTextHorzPos_LEFTOUTSIDE: aTextPos.setX(aPt1b.X()-aTextSize2.Height()-nArr1Len ); break;
+ case css::drawing::MeasureTextHorzPos_RIGHTOUTSIDE: aTextPos.setX(aPt1b.X()+nLen+nArr2Len ); break;
+ default: aTextPos.setX(aPt1b.X() ); aTextSize2.setHeight(nLen );
+ }
+ switch (eMV) {
+ case css::drawing::MeasureTextVertPos_CENTERED:
+ aTextPos.setY(aPt1b.Y()+aTextSize2.Width()/2 ); break;
+ case css::drawing::MeasureTextVertPos_WEST: {
+ if (!bBelowRefEdge) aTextPos.setY(aPt1b.Y()+aTextSize2.Width()+nLWdt );
+ else aTextPos.setY(aPt1b.Y()-nLWdt );
+ } break;
+ default: {
+ if (!bBelowRefEdge) aTextPos.setY(aPt1b.Y()-nLWdt );
+ else aTextPos.setY(aPt1b.Y()+aTextSize2.Width()+nLWdt );
+ }
+ }
+ if (bUpsideDown) {
+ aTextPos.AdjustX(aTextSize2.Height() );
+ aTextPos.AdjustY( -(aTextSize2.Width()) );
+ }
+ }
+ if (aMPol.nTextAngle != maGeo.nRotationAngle) {
+ const_cast<SdrMeasureObj*>(this)->maGeo.nRotationAngle=aMPol.nTextAngle;
+ const_cast<SdrMeasureObj*>(this)->maGeo.RecalcSinCos();
+ }
+ RotatePoint(aTextPos,aPt1b,aMPol.nLineSin,aMPol.nLineCos);
+ aTextSize2.AdjustWidth( 1 ); aTextSize2.AdjustHeight( 1 ); // because of the Rect-Ctor's odd behavior
+ rRect=tools::Rectangle(aTextPos,aTextSize2);
+ rRect.Justify();
+ const_cast<SdrMeasureObj*>(this)->maRect=rRect;
+
+ if (aMPol.nTextAngle != maGeo.nRotationAngle) {
+ const_cast<SdrMeasureObj*>(this)->maGeo.nRotationAngle=aMPol.nTextAngle;
+ const_cast<SdrMeasureObj*>(this)->maGeo.RecalcSinCos();
+ }
+}
+
+SdrMeasureObj* SdrMeasureObj::CloneSdrObject(SdrModel& rTargetModel) const
+{
+ return new SdrMeasureObj(rTargetModel, *this);
+}
+
+OUString SdrMeasureObj::TakeObjNameSingul() const
+{
+ OUString sName(SvxResId(STR_ObjNameSingulMEASURE));
+
+ OUString aName( GetName() );
+ if (!aName.isEmpty())
+ sName += " '" + aName + "'";
+
+ return sName;
+}
+
+OUString SdrMeasureObj::TakeObjNamePlural() const
+{
+ return SvxResId(STR_ObjNamePluralMEASURE);
+}
+
+basegfx::B2DPolyPolygon SdrMeasureObj::TakeXorPoly() const
+{
+ ImpMeasureRec aRec;
+ ImpMeasurePoly aMPol;
+ ImpTakeAttr(aRec);
+ ImpCalcGeometrics(aRec,aMPol);
+ return ImpCalcXPoly(aMPol);
+}
+
+sal_uInt32 SdrMeasureObj::GetHdlCount() const
+{
+ return 6;
+}
+
+void SdrMeasureObj::AddToHdlList(SdrHdlList& rHdlList) const
+{
+ ImpMeasureRec aRec;
+ ImpMeasurePoly aMPol;
+ ImpTakeAttr(aRec);
+ aRec.nHelplineDist=0;
+ ImpCalcGeometrics(aRec,aMPol);
+
+ for (sal_uInt32 nHdlNum=0; nHdlNum<6; ++nHdlNum)
+ {
+ Point aPt;
+ switch (nHdlNum) {
+ case 0: aPt=aMPol.aHelpline1.aP1; break;
+ case 1: aPt=aMPol.aHelpline2.aP1; break;
+ case 2: aPt=aPt1; break;
+ case 3: aPt=aPt2; break;
+ case 4: aPt=aMPol.aHelpline1.aP2; break;
+ case 5: aPt=aMPol.aHelpline2.aP2; break;
+ } // switch
+ std::unique_ptr<SdrHdl> pHdl(new ImpMeasureHdl(aPt,SdrHdlKind::User));
+ pHdl->SetObjHdlNum(nHdlNum);
+ pHdl->SetRotationAngle(aMPol.nLineAngle);
+ rHdlList.AddHdl(std::move(pHdl));
+ }
+}
+
+
+bool SdrMeasureObj::hasSpecialDrag() const
+{
+ return true;
+}
+
+bool SdrMeasureObj::beginSpecialDrag(SdrDragStat& rDrag) const
+{
+ const SdrHdl* pHdl = rDrag.GetHdl();
+
+ if(pHdl)
+ {
+ const sal_uInt32 nHdlNum(pHdl->GetObjHdlNum());
+
+ if(nHdlNum != 2 && nHdlNum != 3)
+ {
+ rDrag.SetEndDragChangesAttributes(true);
+ }
+
+ return true;
+ }
+
+ return false;
+}
+
+bool SdrMeasureObj::applySpecialDrag(SdrDragStat& rDrag)
+{
+ ImpMeasureRec aMeasureRec;
+ const SdrHdl* pHdl = rDrag.GetHdl();
+ const sal_uInt32 nHdlNum(pHdl->GetObjHdlNum());
+
+ ImpTakeAttr(aMeasureRec);
+ ImpEvalDrag(aMeasureRec, rDrag);
+
+ switch (nHdlNum)
+ {
+ case 2:
+ {
+ aPt1 = aMeasureRec.aPt1;
+ SetTextDirty();
+ break;
+ }
+ case 3:
+ {
+ aPt2 = aMeasureRec.aPt2;
+ SetTextDirty();
+ break;
+ }
+ default:
+ {
+ switch(nHdlNum)
+ {
+ case 0:
+ case 1:
+ {
+ ImpMeasureRec aOrigMeasureRec;
+ ImpTakeAttr(aOrigMeasureRec);
+
+ if(aMeasureRec.nHelpline1Len != aOrigMeasureRec.nHelpline1Len)
+ {
+ SetObjectItem(makeSdrMeasureHelpline1LenItem(aMeasureRec.nHelpline1Len));
+ }
+
+ if(aMeasureRec.nHelpline2Len != aOrigMeasureRec.nHelpline2Len)
+ {
+ SetObjectItem(makeSdrMeasureHelpline2LenItem(aMeasureRec.nHelpline2Len));
+ }
+
+ break;
+ }
+
+ case 4:
+ case 5:
+ {
+ ImpMeasureRec aOrigMeasureRec;
+ ImpTakeAttr(aOrigMeasureRec);
+
+ if(aMeasureRec.nLineDist != aOrigMeasureRec.nLineDist)
+ {
+ SetObjectItem(makeSdrMeasureLineDistItem(aMeasureRec.nLineDist));
+ }
+
+ if(aMeasureRec.bBelowRefEdge != aOrigMeasureRec.bBelowRefEdge)
+ {
+ SetObjectItem(SdrMeasureBelowRefEdgeItem(aMeasureRec.bBelowRefEdge));
+ }
+ }
+ }
+ }
+ } // switch
+
+ SetBoundAndSnapRectsDirty();
+ SetChanged();
+
+ return true;
+}
+
+OUString SdrMeasureObj::getSpecialDragComment(const SdrDragStat& /*rDrag*/) const
+{
+ return OUString();
+}
+
+void SdrMeasureObj::ImpEvalDrag(ImpMeasureRec& rRec, const SdrDragStat& rDrag) const
+{
+ Degree100 nLineAngle=GetAngle(rRec.aPt2-rRec.aPt1);
+ double a = toRadians(nLineAngle);
+ double nSin=sin(a);
+ double nCos=cos(a);
+
+ const SdrHdl* pHdl=rDrag.GetHdl();
+ sal_uInt32 nHdlNum(pHdl->GetObjHdlNum());
+ bool bOrtho=rDrag.GetView()!=nullptr && rDrag.GetView()->IsOrtho();
+ bool bBigOrtho=bOrtho && rDrag.GetView()->IsBigOrtho();
+ bool bBelow=rRec.bBelowRefEdge;
+ Point aPt(rDrag.GetNow());
+
+ switch (nHdlNum) {
+ case 0: {
+ RotatePoint(aPt,aPt1,nSin,-nCos);
+ rRec.nHelpline1Len=aPt1.Y()-aPt.Y();
+ if (bBelow) rRec.nHelpline1Len=-rRec.nHelpline1Len;
+ if (bOrtho) rRec.nHelpline2Len=rRec.nHelpline1Len;
+ } break;
+ case 1: {
+ RotatePoint(aPt,aPt2,nSin,-nCos);
+ rRec.nHelpline2Len=aPt2.Y()-aPt.Y();
+ if (bBelow) rRec.nHelpline2Len=-rRec.nHelpline2Len;
+ if (bOrtho) rRec.nHelpline1Len=rRec.nHelpline2Len;
+ } break;
+ case 2: case 3: {
+ bool bAnf=nHdlNum==2;
+ Point& rMov=bAnf ? rRec.aPt1 : rRec.aPt2;
+ Point aMov(rMov);
+ Point aFix(bAnf ? rRec.aPt2 : rRec.aPt1);
+ if (bOrtho) {
+ tools::Long ndx0=aMov.X()-aFix.X();
+ tools::Long ndy0=aMov.Y()-aFix.Y();
+ bool bHLin=ndy0==0;
+ bool bVLin=ndx0==0;
+ if (!bHLin || !bVLin) { // else aPt1==aPt2
+ tools::Long ndx=aPt.X()-aFix.X();
+ tools::Long ndy=aPt.Y()-aFix.Y();
+ double nXFact=0; if (!bVLin) nXFact=static_cast<double>(ndx)/static_cast<double>(ndx0);
+ double nYFact=0; if (!bHLin) nYFact=static_cast<double>(ndy)/static_cast<double>(ndy0);
+ bool bHor=bHLin || (!bVLin && (nXFact>nYFact) ==bBigOrtho);
+ bool bVer=bVLin || (!bHLin && (nXFact<=nYFact)==bBigOrtho);
+ if (bHor) ndy=tools::Long(ndy0*nXFact);
+ if (bVer) ndx=tools::Long(ndx0*nYFact);
+ aPt=aFix;
+ aPt.AdjustX(ndx );
+ aPt.AdjustY(ndy );
+ } // else Ortho8
+ }
+ rMov=aPt;
+ } break;
+ case 4: case 5: {
+ tools::Long nVal0=rRec.nLineDist;
+ RotatePoint(aPt,(nHdlNum==4 ? aPt1 : aPt2),nSin,-nCos);
+ rRec.nLineDist=aPt.Y()- (nHdlNum==4 ? aPt1.Y() : aPt2.Y());
+ if (bBelow) rRec.nLineDist=-rRec.nLineDist;
+ if (rRec.nLineDist<0) {
+ rRec.nLineDist=-rRec.nLineDist;
+ rRec.bBelowRefEdge=!bBelow;
+ }
+ rRec.nLineDist-=rRec.nHelplineOverhang;
+ if (bOrtho) rRec.nLineDist=nVal0;
+ } break;
+ } // switch
+}
+
+
+bool SdrMeasureObj::BegCreate(SdrDragStat& rStat)
+{
+ rStat.SetOrtho8Possible();
+ aPt1=rStat.GetStart();
+ aPt2=rStat.GetNow();
+ SetTextDirty();
+ return true;
+}
+
+bool SdrMeasureObj::MovCreate(SdrDragStat& rStat)
+{
+ SdrView* pView=rStat.GetView();
+ aPt1=rStat.GetStart();
+ aPt2=rStat.GetNow();
+ if (pView!=nullptr && pView->IsCreate1stPointAsCenter()) {
+ aPt1+=aPt1;
+ aPt1-=rStat.GetNow();
+ }
+ SetTextDirty();
+ SetBoundRectDirty();
+ m_bSnapRectDirty=true;
+ return true;
+}
+
+bool SdrMeasureObj::EndCreate(SdrDragStat& rStat, SdrCreateCmd eCmd)
+{
+ SetTextDirty();
+ SetBoundAndSnapRectsDirty();
+ return (eCmd==SdrCreateCmd::ForceEnd || rStat.GetPointCount()>=2);
+}
+
+bool SdrMeasureObj::BckCreate(SdrDragStat& /*rStat*/)
+{
+ return false;
+}
+
+void SdrMeasureObj::BrkCreate(SdrDragStat& /*rStat*/)
+{
+}
+
+basegfx::B2DPolyPolygon SdrMeasureObj::TakeCreatePoly(const SdrDragStat& /*rDrag*/) const
+{
+ ImpMeasureRec aRec;
+ ImpMeasurePoly aMPol;
+
+ ImpTakeAttr(aRec);
+ ImpCalcGeometrics(aRec, aMPol);
+
+ return ImpCalcXPoly(aMPol);
+}
+
+PointerStyle SdrMeasureObj::GetCreatePointer() const
+{
+ return PointerStyle::Cross;
+}
+
+void SdrMeasureObj::NbcMove(const Size& rSiz)
+{
+ SdrTextObj::NbcMove(rSiz);
+ aPt1.Move(rSiz);
+ aPt2.Move(rSiz);
+}
+
+void SdrMeasureObj::NbcResize(const Point& rRef, const Fraction& xFact, const Fraction& yFact)
+{
+ SdrTextObj::NbcResize(rRef,xFact,yFact);
+ ResizePoint(aPt1,rRef,xFact,yFact);
+ ResizePoint(aPt2,rRef,xFact,yFact);
+ SetTextDirty();
+}
+
+void SdrMeasureObj::NbcRotate(const Point& rRef, Degree100 nAngle, double sn, double cs)
+{
+ SdrTextObj::NbcRotate(rRef,nAngle,sn,cs);
+ tools::Long nLen0=GetLen(aPt2-aPt1);
+ RotatePoint(aPt1,rRef,sn,cs);
+ RotatePoint(aPt2,rRef,sn,cs);
+ tools::Long nLen1=GetLen(aPt2-aPt1);
+ if (nLen1!=nLen0) { // rounding error!
+ tools::Long dx=aPt2.X()-aPt1.X();
+ tools::Long dy=aPt2.Y()-aPt1.Y();
+ dx=BigMulDiv(dx,nLen0,nLen1);
+ dy=BigMulDiv(dy,nLen0,nLen1);
+ if (rRef==aPt2) {
+ aPt1.setX(aPt2.X()-dx );
+ aPt1.setY(aPt2.Y()-dy );
+ } else {
+ aPt2.setX(aPt1.X()+dx );
+ aPt2.setY(aPt1.Y()+dy );
+ }
+ }
+ SetBoundAndSnapRectsDirty();
+}
+
+void SdrMeasureObj::NbcMirror(const Point& rRef1, const Point& rRef2)
+{
+ SdrTextObj::NbcMirror(rRef1,rRef2);
+ MirrorPoint(aPt1,rRef1,rRef2);
+ MirrorPoint(aPt2,rRef1,rRef2);
+ SetBoundAndSnapRectsDirty();
+}
+
+void SdrMeasureObj::NbcShear(const Point& rRef, Degree100 nAngle, double tn, bool bVShear)
+{
+ SdrTextObj::NbcShear(rRef,nAngle,tn,bVShear);
+ ShearPoint(aPt1,rRef,tn,bVShear);
+ ShearPoint(aPt2,rRef,tn,bVShear);
+ SetBoundAndSnapRectsDirty();
+ SetTextDirty();
+}
+
+Degree100 SdrMeasureObj::GetRotateAngle() const
+{
+ return GetAngle(aPt2-aPt1);
+}
+
+void SdrMeasureObj::RecalcSnapRect()
+{
+ ImpMeasureRec aRec;
+ ImpMeasurePoly aMPol;
+ XPolyPolygon aXPP;
+
+ ImpTakeAttr(aRec);
+ ImpCalcGeometrics(aRec, aMPol);
+ aXPP = XPolyPolygon(ImpCalcXPoly(aMPol));
+ maSnapRect = aXPP.GetBoundRect();
+}
+
+sal_uInt32 SdrMeasureObj::GetSnapPointCount() const
+{
+ return 2;
+}
+
+Point SdrMeasureObj::GetSnapPoint(sal_uInt32 i) const
+{
+ if (i==0) return aPt1;
+ else return aPt2;
+}
+
+bool SdrMeasureObj::IsPolyObj() const
+{
+ return true;
+}
+
+sal_uInt32 SdrMeasureObj::GetPointCount() const
+{
+ return 2;
+}
+
+Point SdrMeasureObj::GetPoint(sal_uInt32 i) const
+{
+ return (0 == i) ? aPt1 : aPt2;
+}
+
+void SdrMeasureObj::NbcSetPoint(const Point& rPnt, sal_uInt32 i)
+{
+ if (0 == i)
+ aPt1=rPnt;
+ if (1 == i)
+ aPt2=rPnt;
+ SetBoundAndSnapRectsDirty();
+ SetTextDirty();
+}
+
+std::unique_ptr<SdrObjGeoData> SdrMeasureObj::NewGeoData() const
+{
+ return std::make_unique<SdrMeasureObjGeoData>();
+}
+
+void SdrMeasureObj::SaveGeoData(SdrObjGeoData& rGeo) const
+{
+ SdrTextObj::SaveGeoData(rGeo);
+ SdrMeasureObjGeoData& rMGeo=static_cast<SdrMeasureObjGeoData&>(rGeo);
+ rMGeo.aPt1=aPt1;
+ rMGeo.aPt2=aPt2;
+}
+
+void SdrMeasureObj::RestoreGeoData(const SdrObjGeoData& rGeo)
+{
+ SdrTextObj::RestoreGeoData(rGeo);
+ const SdrMeasureObjGeoData& rMGeo=static_cast<const SdrMeasureObjGeoData&>(rGeo);
+ aPt1=rMGeo.aPt1;
+ aPt2=rMGeo.aPt2;
+ SetTextDirty();
+}
+
+SdrObjectUniquePtr SdrMeasureObj::DoConvertToPolyObj(bool bBezier, bool bAddText) const
+{
+ // get XOR Poly as base
+ XPolyPolygon aTmpPolyPolygon(TakeXorPoly());
+
+ // get local ItemSet and StyleSheet
+ SfxItemSet aSet(GetObjectItemSet());
+ SfxStyleSheet* pStyleSheet = GetStyleSheet();
+
+ // prepare group
+ std::unique_ptr<SdrObjGroup,SdrObjectFreeOp> pGroup(new SdrObjGroup(getSdrModelFromSdrObject()));
+
+ // prepare parameters
+ basegfx::B2DPolyPolygon aPolyPoly;
+ SdrPathObj* pPath;
+ sal_uInt16 nCount(aTmpPolyPolygon.Count());
+ sal_uInt16 nLoopStart(0);
+
+ if(nCount == 3)
+ {
+ // three lines, first one is the middle one
+ aPolyPoly.clear();
+ aPolyPoly.append(aTmpPolyPolygon[0].getB2DPolygon());
+
+ pPath = new SdrPathObj(
+ getSdrModelFromSdrObject(),
+ SdrObjKind::PathLine,
+ aPolyPoly);
+
+ pPath->SetMergedItemSet(aSet);
+ pPath->SetStyleSheet(pStyleSheet, true);
+ pGroup->GetSubList()->NbcInsertObject(pPath);
+ aSet.Put(XLineStartWidthItem(0));
+ aSet.Put(XLineEndWidthItem(0));
+ nLoopStart = 1;
+ }
+ else if(nCount == 4)
+ {
+ // four lines, middle line with gap, so there are two lines used
+ // which have one arrow each
+ sal_Int32 nEndWidth = aSet.Get(XATTR_LINEENDWIDTH).GetValue();
+ aSet.Put(XLineEndWidthItem(0));
+
+ aPolyPoly.clear();
+ aPolyPoly.append(aTmpPolyPolygon[0].getB2DPolygon());
+ pPath = new SdrPathObj(
+ getSdrModelFromSdrObject(),
+ SdrObjKind::PathLine,
+ aPolyPoly);
+
+ pPath->SetMergedItemSet(aSet);
+ pPath->SetStyleSheet(pStyleSheet, true);
+
+ pGroup->GetSubList()->NbcInsertObject(pPath);
+
+ aSet.Put(XLineEndWidthItem(nEndWidth));
+ aSet.Put(XLineStartWidthItem(0));
+
+ aPolyPoly.clear();
+ aPolyPoly.append(aTmpPolyPolygon[1].getB2DPolygon());
+ pPath = new SdrPathObj(
+ getSdrModelFromSdrObject(),
+ SdrObjKind::PathLine,
+ aPolyPoly);
+
+ pPath->SetMergedItemSet(aSet);
+ pPath->SetStyleSheet(pStyleSheet, true);
+
+ pGroup->GetSubList()->NbcInsertObject(pPath);
+
+ aSet.Put(XLineEndWidthItem(0));
+ nLoopStart = 2;
+ }
+ else if(nCount == 5)
+ {
+ // five lines, first two are the outer ones
+ sal_Int32 nEndWidth = aSet.Get(XATTR_LINEENDWIDTH).GetValue();
+
+ aSet.Put(XLineEndWidthItem(0));
+
+ aPolyPoly.clear();
+ aPolyPoly.append(aTmpPolyPolygon[0].getB2DPolygon());
+ pPath = new SdrPathObj(
+ getSdrModelFromSdrObject(),
+ SdrObjKind::PathLine,
+ aPolyPoly);
+
+ pPath->SetMergedItemSet(aSet);
+ pPath->SetStyleSheet(pStyleSheet, true);
+
+ pGroup->GetSubList()->NbcInsertObject(pPath);
+
+ aSet.Put(XLineEndWidthItem(nEndWidth));
+ aSet.Put(XLineStartWidthItem(0));
+
+ aPolyPoly.clear();
+ aPolyPoly.append(aTmpPolyPolygon[1].getB2DPolygon());
+ pPath = new SdrPathObj(
+ getSdrModelFromSdrObject(),
+ SdrObjKind::PathLine,
+ aPolyPoly);
+
+ pPath->SetMergedItemSet(aSet);
+ pPath->SetStyleSheet(pStyleSheet, true);
+
+ pGroup->GetSubList()->NbcInsertObject(pPath);
+
+ aSet.Put(XLineEndWidthItem(0));
+ nLoopStart = 2;
+ }
+
+ for(;nLoopStart<nCount;nLoopStart++)
+ {
+ aPolyPoly.clear();
+ aPolyPoly.append(aTmpPolyPolygon[nLoopStart].getB2DPolygon());
+ pPath = new SdrPathObj(
+ getSdrModelFromSdrObject(),
+ SdrObjKind::PathLine,
+ aPolyPoly);
+
+ pPath->SetMergedItemSet(aSet);
+ pPath->SetStyleSheet(pStyleSheet, true);
+
+ pGroup->GetSubList()->NbcInsertObject(pPath);
+ }
+
+ if(bAddText)
+ {
+ return ImpConvertAddText(std::move(pGroup), bBezier);
+ }
+ else
+ {
+ return pGroup;
+ }
+}
+
+bool SdrMeasureObj::BegTextEdit(SdrOutliner& rOutl)
+{
+ UndirtyText();
+ return SdrTextObj::BegTextEdit(rOutl);
+}
+
+const Size& SdrMeasureObj::GetTextSize() const
+{
+ if (bTextDirty) UndirtyText();
+ return SdrTextObj::GetTextSize();
+}
+
+OutlinerParaObject* SdrMeasureObj::GetOutlinerParaObject() const
+{
+ if(bTextDirty)
+ UndirtyText();
+ return SdrTextObj::GetOutlinerParaObject();
+}
+
+void SdrMeasureObj::NbcSetOutlinerParaObject(std::optional<OutlinerParaObject> pTextObject)
+{
+ SdrTextObj::NbcSetOutlinerParaObject(std::move(pTextObject));
+ if(SdrTextObj::GetOutlinerParaObject())
+ SetTextDirty(); // recalculate text
+}
+
+void SdrMeasureObj::TakeTextRect( SdrOutliner& rOutliner, tools::Rectangle& rTextRect, bool bNoEditText,
+ tools::Rectangle* pAnchorRect, bool bLineWidth ) const
+{
+ if (bTextDirty) UndirtyText();
+ SdrTextObj::TakeTextRect( rOutliner, rTextRect, bNoEditText, pAnchorRect, bLineWidth );
+}
+
+void SdrMeasureObj::TakeTextAnchorRect(tools::Rectangle& rAnchorRect) const
+{
+ if (bTextDirty) UndirtyText();
+ SdrTextObj::TakeTextAnchorRect(rAnchorRect);
+}
+
+void SdrMeasureObj::TakeTextEditArea(Size* pPaperMin, Size* pPaperMax, tools::Rectangle* pViewInit, tools::Rectangle* pViewMin) const
+{
+ if (bTextDirty) UndirtyText();
+ SdrTextObj::TakeTextEditArea(pPaperMin,pPaperMax,pViewInit,pViewMin);
+}
+
+EEAnchorMode SdrMeasureObj::GetOutlinerViewAnchorMode() const
+{
+ if (bTextDirty) UndirtyText();
+ ImpMeasureRec aRec;
+ ImpMeasurePoly aMPol;
+ ImpTakeAttr(aRec);
+ ImpCalcGeometrics(aRec,aMPol);
+
+ SdrTextHorzAdjust eTH=GetTextHorizontalAdjust();
+ SdrTextVertAdjust eTV=GetTextVerticalAdjust();
+ css::drawing::MeasureTextHorzPos eMH = aMPol.eUsedTextHPos;
+ css::drawing::MeasureTextVertPos eMV = aMPol.eUsedTextVPos;
+ bool bTextRota90=aRec.bTextRota90;
+ bool bBelowRefEdge=aRec.bBelowRefEdge;
+
+ // TODO: bTextUpsideDown should be interpreted here!
+ if (!bTextRota90) {
+ if (eMH==css::drawing::MeasureTextHorzPos_LEFTOUTSIDE) eTH=SDRTEXTHORZADJUST_RIGHT;
+ if (eMH==css::drawing::MeasureTextHorzPos_RIGHTOUTSIDE) eTH=SDRTEXTHORZADJUST_LEFT;
+ // at eMH==css::drawing::MeasureTextHorzPos_INSIDE we can anchor horizontally
+ if (eMV==css::drawing::MeasureTextVertPos_EAST) eTV=SDRTEXTVERTADJUST_BOTTOM;
+ if (eMV==css::drawing::MeasureTextVertPos_WEST) eTV=SDRTEXTVERTADJUST_TOP;
+ if (eMV==css::drawing::MeasureTextVertPos_CENTERED) eTV=SDRTEXTVERTADJUST_CENTER;
+ } else {
+ if (eMH==css::drawing::MeasureTextHorzPos_LEFTOUTSIDE) eTV=SDRTEXTVERTADJUST_BOTTOM;
+ if (eMH==css::drawing::MeasureTextHorzPos_RIGHTOUTSIDE) eTV=SDRTEXTVERTADJUST_TOP;
+ // at eMH==css::drawing::MeasureTextHorzPos_INSIDE we can anchor vertically
+ if (!bBelowRefEdge) {
+ if (eMV==css::drawing::MeasureTextVertPos_EAST) eTH=SDRTEXTHORZADJUST_LEFT;
+ if (eMV==css::drawing::MeasureTextVertPos_WEST) eTH=SDRTEXTHORZADJUST_RIGHT;
+ } else {
+ if (eMV==css::drawing::MeasureTextVertPos_EAST) eTH=SDRTEXTHORZADJUST_RIGHT;
+ if (eMV==css::drawing::MeasureTextVertPos_WEST) eTH=SDRTEXTHORZADJUST_LEFT;
+ }
+ if (eMV==css::drawing::MeasureTextVertPos_CENTERED) eTH=SDRTEXTHORZADJUST_CENTER;
+ }
+
+ EEAnchorMode eRet=EEAnchorMode::BottomHCenter;
+ if (eTH==SDRTEXTHORZADJUST_LEFT) {
+ if (eTV==SDRTEXTVERTADJUST_TOP) eRet=EEAnchorMode::TopLeft;
+ else if (eTV==SDRTEXTVERTADJUST_BOTTOM) eRet=EEAnchorMode::BottomLeft;
+ else eRet=EEAnchorMode::VCenterLeft;
+ } else if (eTH==SDRTEXTHORZADJUST_RIGHT) {
+ if (eTV==SDRTEXTVERTADJUST_TOP) eRet=EEAnchorMode::TopRight;
+ else if (eTV==SDRTEXTVERTADJUST_BOTTOM) eRet=EEAnchorMode::BottomRight;
+ else eRet=EEAnchorMode::VCenterRight;
+ } else {
+ if (eTV==SDRTEXTVERTADJUST_TOP) eRet=EEAnchorMode::TopHCenter;
+ else if (eTV==SDRTEXTVERTADJUST_BOTTOM) eRet=EEAnchorMode::BottomHCenter;
+ else eRet=EEAnchorMode::VCenterHCenter;
+ }
+ return eRet;
+}
+
+
+// #i97878#
+// TRGetBaseGeometry/TRSetBaseGeometry needs to be based on two positions,
+// same as line geometry in SdrPathObj. Thus needs to be overridden and
+// implemented since currently it is derived from SdrTextObj which uses
+// a functionality based on SnapRect which is not useful here
+
+bool SdrMeasureObj::TRGetBaseGeometry(basegfx::B2DHomMatrix& rMatrix, basegfx::B2DPolyPolygon& /*rPolyPolygon*/) const
+{
+ // handle the same as a simple line since the definition is based on two points
+ const basegfx::B2DRange aRange(aPt1.X(), aPt1.Y(), aPt2.X(), aPt2.Y());
+ basegfx::B2DTuple aScale(aRange.getRange());
+ basegfx::B2DTuple aTranslate(aRange.getMinimum());
+
+ // position maybe relative to anchor position, convert
+ if( getSdrModelFromSdrObject().IsWriter() )
+ {
+ if(GetAnchorPos().X() || GetAnchorPos().Y())
+ {
+ aTranslate -= basegfx::B2DTuple(GetAnchorPos().X(), GetAnchorPos().Y());
+ }
+ }
+
+ // build return value matrix
+ rMatrix = basegfx::utils::createScaleTranslateB2DHomMatrix(aScale, aTranslate);
+
+ return true;
+}
+
+void SdrMeasureObj::TRSetBaseGeometry(const basegfx::B2DHomMatrix& rMatrix, const basegfx::B2DPolyPolygon& /*rPolyPolygon*/)
+{
+ // use given transformation to derive the two defining points from unit line
+ basegfx::B2DPoint aPosA(rMatrix * basegfx::B2DPoint(0.0, 0.0));
+ basegfx::B2DPoint aPosB(rMatrix * basegfx::B2DPoint(1.0, 0.0));
+
+ if( getSdrModelFromSdrObject().IsWriter() )
+ {
+ // if anchor is used, make position relative to it
+ if(GetAnchorPos().X() || GetAnchorPos().Y())
+ {
+ const basegfx::B2DVector aAnchorOffset(GetAnchorPos().X(), GetAnchorPos().Y());
+
+ aPosA += aAnchorOffset;
+ aPosB += aAnchorOffset;
+ }
+ }
+
+ // derive new model data
+ const Point aNewPt1(basegfx::fround(aPosA.getX()), basegfx::fround(aPosA.getY()));
+ const Point aNewPt2(basegfx::fround(aPosB.getX()), basegfx::fround(aPosB.getY()));
+
+ if(aNewPt1 == aPt1 && aNewPt2 == aPt2)
+ return;
+
+ // set model values and broadcast
+ tools::Rectangle aBoundRect0; if (m_pUserCall!=nullptr) aBoundRect0=GetLastBoundRect();
+
+ aPt1 = aNewPt1;
+ aPt2 = aNewPt2;
+
+ SetTextDirty();
+ ActionChanged();
+ SetChanged();
+ BroadcastObjectChange();
+ SendUserCall(SdrUserCallType::MoveOnly,aBoundRect0);
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/svdraw/svdomedia.cxx b/svx/source/svdraw/svdomedia.cxx
new file mode 100644
index 000000000..46a35c2d6
--- /dev/null
+++ b/svx/source/svdraw/svdomedia.cxx
@@ -0,0 +1,457 @@
+/* -*- 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 <svx/svdomedia.hxx>
+
+#include <com/sun/star/text/GraphicCrop.hpp>
+
+#include <rtl/ustring.hxx>
+#include <sal/log.hxx>
+
+#include <ucbhelper/content.hxx>
+#include <comphelper/processfactory.hxx>
+#include <comphelper/storagehelper.hxx>
+
+#include <vcl/svapp.hxx>
+
+#include <svx/svdmodel.hxx>
+#include <svx/dialmgr.hxx>
+#include <svx/strings.hrc>
+#include <svx/sdr/contact/viewcontactofsdrmediaobj.hxx>
+#include <avmedia/mediawindow.hxx>
+#include <tools/diagnose_ex.h>
+
+using namespace ::com::sun::star;
+
+
+struct SdrMediaObj::Impl
+{
+ ::avmedia::MediaItem m_MediaProperties;
+#if HAVE_FEATURE_AVMEDIA
+ // Note: the temp file is read only, until it is deleted!
+ // It may be shared between multiple documents in case of copy/paste,
+ // hence the shared_ptr.
+ std::shared_ptr< ::avmedia::MediaTempFile > m_pTempFile;
+#endif
+ uno::Reference< graphic::XGraphic > m_xCachedSnapshot;
+ rtl::Reference<avmedia::PlayerListener> m_xPlayerListener;
+ OUString m_LastFailedPkgURL;
+};
+
+SdrMediaObj::SdrMediaObj(SdrModel& rSdrModel)
+: SdrRectObj(rSdrModel)
+ ,m_xImpl( new Impl )
+{
+}
+
+SdrMediaObj::SdrMediaObj(SdrModel& rSdrModel, SdrMediaObj const & rSource)
+: SdrRectObj(rSdrModel, rSource)
+ ,m_xImpl( new Impl )
+{
+#if HAVE_FEATURE_AVMEDIA
+ m_xImpl->m_pTempFile = rSource.m_xImpl->m_pTempFile; // before props
+#endif
+ setMediaProperties( rSource.getMediaProperties() );
+ m_xImpl->m_xCachedSnapshot = rSource.m_xImpl->m_xCachedSnapshot;
+}
+
+SdrMediaObj::SdrMediaObj(
+ SdrModel& rSdrModel,
+ const tools::Rectangle& rRect)
+: SdrRectObj(rSdrModel, rRect)
+ ,m_xImpl( new Impl )
+{
+ const bool bUndo(rSdrModel.IsUndoEnabled());
+ rSdrModel.EnableUndo(false);
+ MakeNameUnique();
+ rSdrModel.EnableUndo(bUndo);
+}
+
+SdrMediaObj::~SdrMediaObj()
+{
+}
+
+bool SdrMediaObj::HasTextEdit() const
+{
+ return false;
+}
+
+std::unique_ptr<sdr::contact::ViewContact> SdrMediaObj::CreateObjectSpecificViewContact()
+{
+ return std::make_unique<sdr::contact::ViewContactOfSdrMediaObj>( *this );
+}
+
+void SdrMediaObj::TakeObjInfo( SdrObjTransformInfoRec& rInfo ) const
+{
+ rInfo.bMoveAllowed = true;
+ rInfo.bResizeFreeAllowed = true;
+ rInfo.bResizePropAllowed = true;
+ rInfo.bRotateFreeAllowed = false;
+ rInfo.bRotate90Allowed = false;
+ rInfo.bMirrorFreeAllowed = false;
+ rInfo.bMirror45Allowed = false;
+ rInfo.bMirror90Allowed = false;
+ rInfo.bTransparenceAllowed = false;
+ rInfo.bShearAllowed = false;
+ rInfo.bEdgeRadiusAllowed = false;
+ rInfo.bNoOrthoDesired = false;
+ rInfo.bNoContortion = false;
+ rInfo.bCanConvToPath = false;
+ rInfo.bCanConvToPoly = false;
+ rInfo.bCanConvToContour = false;
+ rInfo.bCanConvToPathLineToArea = false;
+ rInfo.bCanConvToPolyLineToArea = false;
+}
+
+SdrObjKind SdrMediaObj::GetObjIdentifier() const
+{
+ return SdrObjKind::Media;
+}
+
+OUString SdrMediaObj::TakeObjNameSingul() const
+{
+ OUString sName(SvxResId(STR_ObjNameSingulMEDIA));
+
+ OUString aName(GetName());
+
+ if (!aName.isEmpty())
+ sName += " '" + aName + "'";
+
+ return sName;
+}
+
+OUString SdrMediaObj::TakeObjNamePlural() const
+{
+ return SvxResId(STR_ObjNamePluralMEDIA);
+}
+
+SdrMediaObj* SdrMediaObj::CloneSdrObject(SdrModel& rTargetModel) const
+{
+ return new SdrMediaObj(rTargetModel, *this);
+}
+
+uno::Reference< graphic::XGraphic > const & SdrMediaObj::getSnapshot() const
+{
+#if HAVE_FEATURE_AVMEDIA
+ if( !m_xImpl->m_xCachedSnapshot.is() )
+ {
+ Graphic aGraphic = m_xImpl->m_MediaProperties.getGraphic();
+ if (!aGraphic.IsNone())
+ {
+ Size aPref = aGraphic.GetPrefSize();
+ Size aPixel = aGraphic.GetSizePixel();
+ const text::GraphicCrop& rCrop = m_xImpl->m_MediaProperties.getCrop();
+ if (rCrop.Bottom > 0 || rCrop.Left > 0 || rCrop.Right > 0 || rCrop.Top > 0)
+ {
+ tools::Long nLeft = aPixel.getWidth() * rCrop.Left / aPref.getWidth();
+ tools::Long nTop = aPixel.getHeight() * rCrop.Top / aPref.getHeight();
+ tools::Long nRight = aPixel.getWidth() * rCrop.Right / aPref.getWidth();
+ tools::Long nBottom = aPixel.getHeight() * rCrop.Bottom / aPref.getHeight();
+ BitmapEx aBitmapEx = aGraphic.GetBitmapEx();
+ aBitmapEx.Crop({nLeft, nTop, aPixel.getWidth() - nRight, aPixel.getHeight() - nBottom});
+ aGraphic = aBitmapEx;
+ }
+
+ // We have an explicit graphic for this media object, then go with that instead of
+ // generating our own one.
+ m_xImpl->m_xCachedSnapshot = aGraphic.GetXGraphic();
+ return m_xImpl->m_xCachedSnapshot;
+ }
+
+ OUString aRealURL = m_xImpl->m_MediaProperties.getTempURL();
+ if( aRealURL.isEmpty() )
+ aRealURL = m_xImpl->m_MediaProperties.getURL();
+ OUString sReferer = m_xImpl->m_MediaProperties.getReferer();
+ OUString sMimeType = m_xImpl->m_MediaProperties.getMimeType();
+ uno::Reference<graphic::XGraphic> xCachedSnapshot = m_xImpl->m_xCachedSnapshot;
+
+ m_xImpl->m_xPlayerListener.set(new avmedia::PlayerListener(
+ [this, xCachedSnapshot, aRealURL, sReferer, sMimeType](const css::uno::Reference<css::media::XPlayer>& rPlayer){
+ SolarMutexGuard g;
+ uno::Reference<graphic::XGraphic> xGraphic
+ = m_xImpl->m_MediaProperties.getGraphic().GetXGraphic();
+ m_xImpl->m_xCachedSnapshot = avmedia::MediaWindow::grabFrame(rPlayer, xGraphic);
+ ActionChanged();
+ }));
+
+ avmedia::MediaWindow::grabFrame(aRealURL, sReferer, sMimeType, m_xImpl->m_xPlayerListener);
+ }
+#endif
+ return m_xImpl->m_xCachedSnapshot;
+}
+
+void SdrMediaObj::AdjustToMaxRect( const tools::Rectangle& rMaxRect, bool bShrinkOnly /* = false */ )
+{
+ Size aSize( Application::GetDefaultDevice()->PixelToLogic(
+ static_cast< sdr::contact::ViewContactOfSdrMediaObj& >( GetViewContact() ).getPreferredSize(),
+ MapMode(MapUnit::Map100thMM)) );
+ Size aMaxSize( rMaxRect.GetSize() );
+
+ if( aSize.IsEmpty() )
+ return;
+
+ Point aPos( rMaxRect.TopLeft() );
+
+ // if graphic is too large, fit it to the page
+ if ( (!bShrinkOnly ||
+ ( aSize.Height() > aMaxSize.Height() ) ||
+ ( aSize.Width() > aMaxSize.Width() ) )&&
+ aSize.Height() && aMaxSize.Height() )
+ {
+ float fGrfWH = static_cast<float>(aSize.Width()) /
+ static_cast<float>(aSize.Height());
+ float fWinWH = static_cast<float>(aMaxSize.Width()) /
+ static_cast<float>(aMaxSize.Height());
+
+ // scale graphic to page size
+ if ( fGrfWH < fWinWH )
+ {
+ aSize.setWidth( static_cast<tools::Long>(aMaxSize.Height() * fGrfWH) );
+ aSize.setHeight( aMaxSize.Height() );
+ }
+ else if ( fGrfWH > 0.F )
+ {
+ aSize.setWidth( aMaxSize.Width() );
+ aSize.setHeight( static_cast<tools::Long>(aMaxSize.Width() / fGrfWH) );
+ }
+
+ aPos = rMaxRect.Center();
+ }
+
+ if( bShrinkOnly )
+ aPos = maRect.TopLeft();
+
+ aPos.AdjustX( -(aSize.Width() / 2) );
+ aPos.AdjustY( -(aSize.Height() / 2) );
+ SetLogicRect( tools::Rectangle( aPos, aSize ) );
+}
+
+void SdrMediaObj::setURL( const OUString& rURL, const OUString& rReferer, const OUString& rMimeType )
+{
+ ::avmedia::MediaItem aURLItem;
+#if HAVE_FEATURE_AVMEDIA
+ if( !rMimeType.isEmpty() )
+ m_xImpl->m_MediaProperties.setMimeType(rMimeType);
+ aURLItem.setURL( rURL, "", rReferer );
+#else
+ (void) rMimeType;
+ (void) rURL;
+ (void) rReferer;
+#endif
+ setMediaProperties( aURLItem );
+}
+
+const OUString& SdrMediaObj::getURL() const
+{
+#if HAVE_FEATURE_AVMEDIA
+ return m_xImpl->m_MediaProperties.getURL();
+#else
+ static OUString ret;
+ return ret;
+#endif
+}
+
+void SdrMediaObj::setMediaProperties( const ::avmedia::MediaItem& rState )
+{
+ mediaPropertiesChanged( rState );
+ static_cast< sdr::contact::ViewContactOfSdrMediaObj& >( GetViewContact() ).executeMediaItem( getMediaProperties() );
+}
+
+const ::avmedia::MediaItem& SdrMediaObj::getMediaProperties() const
+{
+ return m_xImpl->m_MediaProperties;
+}
+
+uno::Reference<io::XInputStream> SdrMediaObj::GetInputStream() const
+{
+#if HAVE_FEATURE_AVMEDIA
+ if (!m_xImpl->m_pTempFile)
+ {
+ SAL_WARN("svx", "this is only intended for embedded media");
+ return nullptr;
+ }
+ ucbhelper::Content tempFile(m_xImpl->m_pTempFile->m_TempFileURL,
+ uno::Reference<ucb::XCommandEnvironment>(),
+ comphelper::getProcessComponentContext());
+ return tempFile.openStream();
+#else
+ return nullptr;
+#endif
+}
+
+void SdrMediaObj::SetInputStream(uno::Reference<io::XInputStream> const& xStream)
+{
+#if !HAVE_FEATURE_AVMEDIA
+ (void) xStream;
+#else
+ if (m_xImpl->m_pTempFile || m_xImpl->m_LastFailedPkgURL.isEmpty())
+ {
+ SAL_WARN("svx", "this is only intended for embedded media");
+ return;
+ }
+
+ OUString tempFileURL;
+ const bool bSuccess(
+ ::avmedia::CreateMediaTempFile(
+ xStream,
+ tempFileURL,
+ u""));
+
+ if (bSuccess)
+ {
+ m_xImpl->m_pTempFile = std::make_shared<::avmedia::MediaTempFile>(tempFileURL);
+ m_xImpl->m_MediaProperties.setURL(
+ m_xImpl->m_LastFailedPkgURL, tempFileURL, "");
+ }
+ m_xImpl->m_LastFailedPkgURL.clear(); // once only
+#endif
+}
+
+/// copy a stream from XStorage to temp file
+#if HAVE_FEATURE_AVMEDIA
+static bool lcl_HandlePackageURL(
+ OUString const & rURL,
+ const SdrModel& rModel,
+ OUString & o_rTempFileURL)
+{
+ ::comphelper::LifecycleProxy sourceProxy;
+ uno::Reference<io::XInputStream> xInStream;
+ try {
+ xInStream = rModel.GetDocumentStream(rURL, sourceProxy);
+ }
+ catch (container::NoSuchElementException const&)
+ {
+ SAL_INFO("svx", "not found: '" << rURL << "'");
+ return false;
+ }
+ catch (uno::Exception const&)
+ {
+ TOOLS_WARN_EXCEPTION("svx", "");
+ return false;
+ }
+ if (!xInStream.is())
+ {
+ SAL_WARN("svx", "no stream?");
+ return false;
+ }
+ // Make sure the temporary copy has the same file name extension as the original media file
+ // (like .mp4). That seems to be important for some AVFoundation APIs. For random extension-less
+ // file names, they don't seem to even bother looking inside the file.
+ sal_Int32 nLastDot = rURL.lastIndexOf('.');
+ sal_Int32 nLastSlash = rURL.lastIndexOf('/');
+ OUString sDesiredExtension;
+ if (nLastDot > nLastSlash && nLastDot+1 < rURL.getLength())
+ sDesiredExtension = rURL.copy(nLastDot);
+ return ::avmedia::CreateMediaTempFile(xInStream, o_rTempFileURL, sDesiredExtension);
+}
+#endif
+
+void SdrMediaObj::mediaPropertiesChanged( const ::avmedia::MediaItem& rNewProperties )
+{
+ bool bBroadcastChanged = false;
+#if HAVE_FEATURE_AVMEDIA
+ const AVMediaSetMask nMaskSet = rNewProperties.getMaskSet();
+
+ // use only a subset of MediaItem properties for own properties
+ if( AVMediaSetMask::MIME_TYPE & nMaskSet )
+ m_xImpl->m_MediaProperties.setMimeType( rNewProperties.getMimeType() );
+
+ if (nMaskSet & AVMediaSetMask::GRAPHIC)
+ {
+ m_xImpl->m_MediaProperties.setGraphic(rNewProperties.getGraphic());
+ }
+
+ if (nMaskSet & AVMediaSetMask::CROP)
+ {
+ m_xImpl->m_MediaProperties.setCrop(rNewProperties.getCrop());
+ }
+
+ if( ( AVMediaSetMask::URL & nMaskSet ) &&
+ ( rNewProperties.getURL() != getURL() ))
+ {
+ m_xImpl->m_xCachedSnapshot.clear();
+ m_xImpl->m_xPlayerListener.clear();
+ OUString const& url(rNewProperties.getURL());
+ if (url.startsWithIgnoreAsciiCase("vnd.sun.star.Package:"))
+ {
+ if ( !m_xImpl->m_pTempFile
+ || (m_xImpl->m_pTempFile->m_TempFileURL !=
+ rNewProperties.getTempURL()))
+ {
+ OUString tempFileURL;
+ const bool bSuccess(
+ lcl_HandlePackageURL(
+ url,
+ getSdrModelFromSdrObject(),
+ tempFileURL));
+
+ if (bSuccess)
+ {
+ m_xImpl->m_pTempFile =
+ std::make_shared<::avmedia::MediaTempFile>(tempFileURL);
+ m_xImpl->m_MediaProperties.setURL(url, tempFileURL, "");
+ }
+ else // this case is for Clone via operator=
+ {
+ m_xImpl->m_pTempFile.reset();
+ m_xImpl->m_MediaProperties.setURL("", "", "");
+ // UGLY: oox import also gets here, because unlike ODF
+ // getDocumentStorage() is not the imported file...
+ m_xImpl->m_LastFailedPkgURL = url;
+ }
+ }
+ else
+ {
+ m_xImpl->m_MediaProperties.setURL(url,
+ rNewProperties.getTempURL(), "");
+ }
+ }
+ else
+ {
+ m_xImpl->m_pTempFile.reset();
+ m_xImpl->m_MediaProperties.setURL(url, "", rNewProperties.getReferer());
+ }
+ bBroadcastChanged = true;
+ }
+
+ if( AVMediaSetMask::LOOP & nMaskSet )
+ m_xImpl->m_MediaProperties.setLoop( rNewProperties.isLoop() );
+
+ if( AVMediaSetMask::MUTE & nMaskSet )
+ m_xImpl->m_MediaProperties.setMute( rNewProperties.isMute() );
+
+ if( AVMediaSetMask::VOLUMEDB & nMaskSet )
+ m_xImpl->m_MediaProperties.setVolumeDB( rNewProperties.getVolumeDB() );
+
+ if( AVMediaSetMask::ZOOM & nMaskSet )
+ m_xImpl->m_MediaProperties.setZoom( rNewProperties.getZoom() );
+#else
+ (void) rNewProperties;
+#endif
+
+ if( bBroadcastChanged )
+ {
+ SetChanged();
+ BroadcastObjectChange();
+ }
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/svdraw/svdoole2.cxx b/svx/source/svdraw/svdoole2.cxx
new file mode 100644
index 000000000..110240782
--- /dev/null
+++ b/svx/source/svdraw/svdoole2.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 <svx/svdoole2.hxx>
+
+#include <com/sun/star/beans/XPropertySet.hpp>
+#include <com/sun/star/util/XModifyBroadcaster.hpp>
+#include <com/sun/star/util/XModifiable.hpp>
+#include <com/sun/star/embed/EmbedStates.hpp>
+#include <com/sun/star/embed/EmbedMisc.hpp>
+#include <com/sun/star/embed/Aspects.hpp>
+#include <com/sun/star/embed/ObjectSaveVetoException.hpp>
+#include <com/sun/star/embed/XEmbeddedObject.hpp>
+#include <com/sun/star/embed/XEmbedPersist2.hpp>
+#include <com/sun/star/embed/XInplaceClient.hpp>
+#include <com/sun/star/embed/XInplaceObject.hpp>
+#include <com/sun/star/embed/XLinkageSupport.hpp>
+#include <com/sun/star/embed/NoVisualAreaSizeException.hpp>
+#include <com/sun/star/embed/XWindowSupplier.hpp>
+#include <com/sun/star/document/XEventListener.hpp>
+#include <com/sun/star/container/XChild.hpp>
+#include <com/sun/star/document/XStorageBasedDocument.hpp>
+#include <com/sun/star/lang/WrappedTargetRuntimeException.hpp>
+
+#include <cppuhelper/exc_hlp.hxx>
+
+#include <toolkit/helper/vclunohelper.hxx>
+#include <toolkit/helper/convert.hxx>
+
+#include <svtools/colorcfg.hxx>
+#include <svtools/embedhlp.hxx>
+
+#include <sfx2/objsh.hxx>
+#include <sfx2/ipclient.hxx>
+#include <sfx2/lnkbase.hxx>
+#include <sfx2/linkmgr.hxx>
+#include <tools/debug.hxx>
+#include <tools/globname.hxx>
+#include <tools/diagnose_ex.h>
+#include <comphelper/classids.hxx>
+#include <comphelper/propertyvalue.hxx>
+
+#include <sot/formats.hxx>
+#include <cppuhelper/implbase.hxx>
+
+#include <vcl/svapp.hxx>
+
+#include <svx/svdmodel.hxx>
+#include <svx/dialmgr.hxx>
+#include <svx/strings.hrc>
+#include <svx/svdetc.hxx>
+#include <unomlstr.hxx>
+#include <sdr/contact/viewcontactofsdrole2obj.hxx>
+#include <svx/svdograf.hxx>
+#include <sdr/properties/oleproperties.hxx>
+#include <svx/unoshape.hxx>
+#include <svx/xlineit0.hxx>
+#include <svx/xlnclit.hxx>
+#include <svx/xbtmpit.hxx>
+#include <svx/xfillit0.hxx>
+#include <svx/xflbmtit.hxx>
+#include <svx/xflbstit.hxx>
+#include <basegfx/matrix/b2dhommatrix.hxx>
+#include <basegfx/polygon/b2dpolypolygon.hxx>
+#include <editeng/outlobj.hxx>
+#include <svx/svdpage.hxx>
+#include <rtl/ustrbuf.hxx>
+#include <rtl/ref.hxx>
+#include <bitmaps.hlst>
+
+using namespace ::com::sun::star;
+
+static uno::Reference < beans::XPropertySet > lcl_getFrame_throw(const SdrOle2Obj* _pObject)
+{
+ uno::Reference < beans::XPropertySet > xFrame;
+ if ( _pObject )
+ {
+ uno::Reference< frame::XController> xController = _pObject->GetParentXModel()->getCurrentController();
+ if ( xController.is() )
+ {
+ xFrame.set( xController->getFrame(),uno::UNO_QUERY_THROW);
+ }
+ } // if ( _pObject )
+ return xFrame;
+}
+
+namespace {
+
+class SdrLightEmbeddedClient_Impl : public ::cppu::WeakImplHelper
+ < embed::XStateChangeListener
+ , document::XEventListener
+ , embed::XInplaceClient
+ , embed::XEmbeddedClient
+ , embed::XWindowSupplier
+ >
+{
+ uno::Reference< awt::XWindow > m_xWindow;
+ SdrOle2Obj* mpObj;
+
+ Fraction m_aScaleWidth;
+ Fraction m_aScaleHeight;
+
+
+public:
+ explicit SdrLightEmbeddedClient_Impl( SdrOle2Obj* pObj );
+ virtual ~SdrLightEmbeddedClient_Impl() override;
+
+ void SetSizeScale( const Fraction& aScaleWidth, const Fraction& aScaleHeight )
+ {
+ m_aScaleWidth = aScaleWidth;
+ m_aScaleHeight = aScaleHeight;
+ }
+
+ const Fraction& GetScaleWidth() const { return m_aScaleWidth; }
+ const Fraction& GetScaleHeight() const { return m_aScaleHeight; }
+
+ void setWindow(const uno::Reference< awt::XWindow >& _xWindow);
+
+ void disconnect();
+private:
+
+ tools::Rectangle impl_getScaledRect_nothrow() const;
+ // XStateChangeListener
+ virtual void SAL_CALL changingState( const css::lang::EventObject& aEvent, ::sal_Int32 nOldState, ::sal_Int32 nNewState ) override;
+ virtual void SAL_CALL stateChanged( const css::lang::EventObject& aEvent, ::sal_Int32 nOldState, ::sal_Int32 nNewState ) override;
+ virtual void SAL_CALL disposing( const css::lang::EventObject& aEvent ) override;
+
+ // document::XEventListener
+ virtual void SAL_CALL notifyEvent( const document::EventObject& aEvent ) override;
+
+ // XEmbeddedClient
+ virtual void SAL_CALL saveObject() override;
+ virtual void SAL_CALL visibilityChanged( sal_Bool bVisible ) override;
+
+ // XComponentSupplier
+ virtual uno::Reference< util::XCloseable > SAL_CALL getComponent() override;
+
+ // XInplaceClient
+ virtual sal_Bool SAL_CALL canInplaceActivate() override;
+ virtual void SAL_CALL activatingInplace() override;
+ virtual void SAL_CALL activatingUI() override;
+ virtual void SAL_CALL deactivatedInplace() override;
+ virtual void SAL_CALL deactivatedUI() override;
+ virtual uno::Reference< css::frame::XLayoutManager > SAL_CALL getLayoutManager() override;
+ virtual uno::Reference< frame::XDispatchProvider > SAL_CALL getInplaceDispatchProvider() override;
+ virtual awt::Rectangle SAL_CALL getPlacement() override;
+ virtual awt::Rectangle SAL_CALL getClipRectangle() override;
+ virtual void SAL_CALL translateAccelerators( const uno::Sequence< awt::KeyEvent >& aKeys ) override;
+ virtual void SAL_CALL scrollObject( const awt::Size& aOffset ) override;
+ virtual void SAL_CALL changedPlacement( const awt::Rectangle& aPosRect ) override;
+
+ // XWindowSupplier
+ virtual uno::Reference< awt::XWindow > SAL_CALL getWindow() override;
+};
+
+}
+
+SdrLightEmbeddedClient_Impl::SdrLightEmbeddedClient_Impl( SdrOle2Obj* pObj )
+: mpObj( pObj )
+{
+}
+SdrLightEmbeddedClient_Impl::~SdrLightEmbeddedClient_Impl()
+{
+ assert(!mpObj);
+}
+tools::Rectangle SdrLightEmbeddedClient_Impl::impl_getScaledRect_nothrow() const
+{
+ tools::Rectangle aLogicRect( mpObj->GetLogicRect() );
+ // apply scaling to object area and convert to pixels
+ aLogicRect.SetSize( Size( tools::Long( aLogicRect.GetWidth() * m_aScaleWidth),
+ tools::Long( aLogicRect.GetHeight() * m_aScaleHeight) ) );
+ return aLogicRect;
+}
+
+void SAL_CALL SdrLightEmbeddedClient_Impl::changingState( const css::lang::EventObject& /*aEvent*/, ::sal_Int32 /*nOldState*/, ::sal_Int32 /*nNewState*/ )
+{
+}
+
+void SAL_CALL SdrLightEmbeddedClient_Impl::stateChanged( const css::lang::EventObject& /*aEvent*/, ::sal_Int32 nOldState, ::sal_Int32 nNewState )
+{
+ SolarMutexGuard aGuard;
+
+ if ( mpObj && nOldState == embed::EmbedStates::LOADED && nNewState == embed::EmbedStates::RUNNING )
+ {
+ mpObj->ObjectLoaded();
+ GetSdrGlobalData().GetOLEObjCache().InsertObj(mpObj);
+ }
+ else if ( mpObj && nNewState == embed::EmbedStates::LOADED && nOldState == embed::EmbedStates::RUNNING )
+ {
+ GetSdrGlobalData().GetOLEObjCache().RemoveObj(mpObj);
+ }
+}
+
+void SdrLightEmbeddedClient_Impl::disconnect()
+{
+ SolarMutexGuard aGuard;
+ if (!mpObj)
+ return;
+ GetSdrGlobalData().GetOLEObjCache().RemoveObj(mpObj);
+ mpObj = nullptr;
+}
+
+void SAL_CALL SdrLightEmbeddedClient_Impl::disposing( const css::lang::EventObject& /*aEvent*/ )
+{
+ disconnect();
+}
+
+void SAL_CALL SdrLightEmbeddedClient_Impl::notifyEvent( const document::EventObject& aEvent )
+{
+ // TODO/LATER: when writer uses this implementation the code could be shared with SfxInPlaceClient_Impl
+
+ SolarMutexGuard aGuard;
+
+ // the code currently makes sense only in case there is no other client
+ if ( !(mpObj && mpObj->GetAspect() != embed::Aspects::MSOLE_ICON && aEvent.EventName == "OnVisAreaChanged"
+ && mpObj->GetObjRef().is() && mpObj->GetObjRef()->getClientSite() == uno::Reference< embed::XEmbeddedClient >( this )) )
+ return;
+
+ try
+ {
+ MapUnit aContainerMapUnit( MapUnit::Map100thMM );
+ uno::Reference< embed::XVisualObject > xParentVis( mpObj->GetParentXModel(), uno::UNO_QUERY );
+ if ( xParentVis.is() )
+ aContainerMapUnit = VCLUnoHelper::UnoEmbed2VCLMapUnit( xParentVis->getMapUnit( mpObj->GetAspect() ) );
+
+ MapUnit aObjMapUnit = VCLUnoHelper::UnoEmbed2VCLMapUnit( mpObj->GetObjRef()->getMapUnit( mpObj->GetAspect() ) );
+
+ tools::Rectangle aVisArea;
+ awt::Size aSz;
+ try
+ {
+ aSz = mpObj->GetObjRef()->getVisualAreaSize( mpObj->GetAspect() );
+ }
+ catch( embed::NoVisualAreaSizeException& )
+ {
+ TOOLS_WARN_EXCEPTION("svx.svdraw", "No visual area size!");
+ aSz.Width = 5000;
+ aSz.Height = 5000;
+ }
+ catch( uno::Exception& )
+ {
+ TOOLS_WARN_EXCEPTION("svx.svdraw", "");
+ aSz.Width = 5000;
+ aSz.Height = 5000;
+ }
+
+ aVisArea.SetSize( Size( aSz.Width, aSz.Height ) );
+ aVisArea = OutputDevice::LogicToLogic(aVisArea, MapMode(aObjMapUnit), MapMode(aContainerMapUnit));
+ Size aScaledSize( static_cast< tools::Long >( m_aScaleWidth * Fraction( aVisArea.GetWidth() ) ),
+ static_cast< tools::Long >( m_aScaleHeight * Fraction( aVisArea.GetHeight() ) ) );
+ tools::Rectangle aLogicRect( mpObj->GetLogicRect() );
+
+ // react to the change if the difference is bigger than one pixel
+ Size aPixelDiff =
+ Application::GetDefaultDevice()->LogicToPixel(
+ Size( aLogicRect.GetWidth() - aScaledSize.Width(),
+ aLogicRect.GetHeight() - aScaledSize.Height() ),
+ MapMode(aContainerMapUnit));
+ if( aPixelDiff.Width() || aPixelDiff.Height() )
+ {
+ mpObj->SetLogicRect( tools::Rectangle( aLogicRect.TopLeft(), aScaledSize ) );
+ mpObj->BroadcastObjectChange();
+ }
+ else
+ mpObj->ActionChanged();
+ }
+ catch( uno::Exception& )
+ {
+ TOOLS_WARN_EXCEPTION("svx.svdraw", "");
+ }
+}
+
+void SAL_CALL SdrLightEmbeddedClient_Impl::saveObject()
+{
+ // TODO/LATER: when writer uses this implementation the code could be shared with SfxInPlaceClient_Impl
+ uno::Reference< embed::XCommonEmbedPersist > xPersist;
+ uno::Reference< util::XModifiable > xModifiable;
+
+ {
+ SolarMutexGuard aGuard;
+
+ if ( !mpObj )
+ throw embed::ObjectSaveVetoException();
+
+ // the common persistence is supported by objects and links
+ xPersist.set( mpObj->GetObjRef(), uno::UNO_QUERY_THROW );
+ xModifiable.set( mpObj->GetParentXModel(), uno::UNO_QUERY );
+ }
+
+ xPersist->storeOwn();
+
+ if ( xModifiable.is() )
+ xModifiable->setModified( true );
+}
+
+void SAL_CALL SdrLightEmbeddedClient_Impl::visibilityChanged( sal_Bool /*bVisible*/ )
+{
+ // nothing to do currently
+ // TODO/LATER: when writer uses this implementation the code could be shared with SfxInPlaceClient_Impl
+ if ( mpObj )
+ {
+ tools::Rectangle aLogicRect( mpObj->GetLogicRect() );
+ Size aLogicSize( aLogicRect.GetWidth(), aLogicRect.GetHeight() );
+
+ if( mpObj->IsChart() )
+ {
+ //charts never should be stretched see #i84323# for example
+ mpObj->SetLogicRect( tools::Rectangle( aLogicRect.TopLeft(), aLogicSize ) );
+ mpObj->BroadcastObjectChange();
+ } // if( mpObj->IsChart() )
+ }
+}
+
+uno::Reference< util::XCloseable > SAL_CALL SdrLightEmbeddedClient_Impl::getComponent()
+{
+ uno::Reference< util::XCloseable > xResult;
+
+ SolarMutexGuard aGuard;
+ if ( mpObj )
+ xResult.set( mpObj->GetParentXModel(), uno::UNO_QUERY );
+
+ return xResult;
+}
+// XInplaceClient
+
+sal_Bool SAL_CALL SdrLightEmbeddedClient_Impl::canInplaceActivate()
+{
+ bool bRet = false;
+ SolarMutexGuard aGuard;
+ if ( mpObj )
+ {
+ uno::Reference< embed::XEmbeddedObject > xObject = mpObj->GetObjRef();
+ if ( !xObject.is() )
+ throw uno::RuntimeException();
+ // we don't want to switch directly from outplace to inplace mode
+ bRet = ( xObject->getCurrentState() != embed::EmbedStates::ACTIVE && mpObj->GetAspect() != embed::Aspects::MSOLE_ICON );
+ } // if ( mpObj )
+ return bRet;
+}
+
+void SAL_CALL SdrLightEmbeddedClient_Impl::activatingInplace()
+{
+}
+
+void SAL_CALL SdrLightEmbeddedClient_Impl::activatingUI()
+{
+ SolarMutexGuard aGuard;
+
+ uno::Reference < beans::XPropertySet > xFrame( lcl_getFrame_throw(mpObj));
+ uno::Reference < frame::XFrame > xOwnFrame( xFrame,uno::UNO_QUERY);
+ uno::Reference < frame::XFramesSupplier > xParentFrame = xOwnFrame->getCreator();
+ if ( xParentFrame.is() )
+ xParentFrame->setActiveFrame( xOwnFrame );
+
+ OLEObjCache& rObjCache = GetSdrGlobalData().GetOLEObjCache();
+ const size_t nCount = rObjCache.size();
+ for(sal_Int32 i = nCount-1 ; i >= 0;--i)
+ {
+ SdrOle2Obj* pObj = rObjCache[i];
+ if ( pObj != mpObj )
+ {
+ // only deactivate ole objects which belongs to the same frame
+ if ( xFrame == lcl_getFrame_throw(pObj) )
+ {
+ const uno::Reference< embed::XEmbeddedObject >& xObject = pObj->GetObjRef();
+ try
+ {
+ if ( xObject->getStatus( pObj->GetAspect() ) & embed::EmbedMisc::MS_EMBED_ACTIVATEWHENVISIBLE )
+ xObject->changeState( embed::EmbedStates::INPLACE_ACTIVE );
+ else
+ {
+ // the links should not stay in running state for long time because of locking
+ uno::Reference< embed::XLinkageSupport > xLink( xObject, uno::UNO_QUERY );
+ if ( xLink.is() && xLink->isLink() )
+ xObject->changeState( embed::EmbedStates::LOADED );
+ else
+ xObject->changeState( embed::EmbedStates::RUNNING );
+ }
+ }
+ catch (css::uno::Exception& )
+ {}
+ }
+ }
+ } // for(sal_Int32 i = nCount-1 ; i >= 0;--i)
+}
+
+void SAL_CALL SdrLightEmbeddedClient_Impl::deactivatedInplace()
+{
+}
+
+void SAL_CALL SdrLightEmbeddedClient_Impl::deactivatedUI()
+{
+ SolarMutexGuard aGuard;
+ css::uno::Reference< css::frame::XLayoutManager > xLayoutManager(getLayoutManager());
+ if ( xLayoutManager.is() )
+ {
+ static constexpr OUStringLiteral aMenuBarURL = u"private:resource/menubar/menubar";
+ if ( !xLayoutManager->isElementVisible( aMenuBarURL ) )
+ xLayoutManager->createElement( aMenuBarURL );
+ }
+}
+
+uno::Reference< css::frame::XLayoutManager > SAL_CALL SdrLightEmbeddedClient_Impl::getLayoutManager()
+{
+ uno::Reference< css::frame::XLayoutManager > xMan;
+ SolarMutexGuard aGuard;
+ uno::Reference < beans::XPropertySet > xFrame( lcl_getFrame_throw(mpObj));
+ try
+ {
+ xMan.set(xFrame->getPropertyValue("LayoutManager"),uno::UNO_QUERY);
+ }
+ catch ( uno::Exception& ex )
+ {
+ css::uno::Any anyEx = cppu::getCaughtException();
+ throw css::lang::WrappedTargetRuntimeException( ex.Message,
+ nullptr, anyEx );
+ }
+
+ return xMan;
+}
+
+uno::Reference< frame::XDispatchProvider > SAL_CALL SdrLightEmbeddedClient_Impl::getInplaceDispatchProvider()
+{
+ SolarMutexGuard aGuard;
+ return uno::Reference < frame::XDispatchProvider >( lcl_getFrame_throw(mpObj), uno::UNO_QUERY_THROW );
+}
+
+awt::Rectangle SAL_CALL SdrLightEmbeddedClient_Impl::getPlacement()
+{
+ SolarMutexGuard aGuard;
+ if ( !mpObj )
+ throw uno::RuntimeException();
+
+ tools::Rectangle aLogicRect = impl_getScaledRect_nothrow();
+ MapUnit aContainerMapUnit( MapUnit::Map100thMM );
+ uno::Reference< embed::XVisualObject > xParentVis( mpObj->GetParentXModel(), uno::UNO_QUERY );
+ if ( xParentVis.is() )
+ aContainerMapUnit = VCLUnoHelper::UnoEmbed2VCLMapUnit( xParentVis->getMapUnit( mpObj->GetAspect() ) );
+
+ aLogicRect = Application::GetDefaultDevice()->LogicToPixel(aLogicRect, MapMode(aContainerMapUnit));
+ return AWTRectangle( aLogicRect );
+}
+
+awt::Rectangle SAL_CALL SdrLightEmbeddedClient_Impl::getClipRectangle()
+{
+ return getPlacement();
+}
+
+void SAL_CALL SdrLightEmbeddedClient_Impl::translateAccelerators( const uno::Sequence< awt::KeyEvent >& /*aKeys*/ )
+{
+}
+
+void SAL_CALL SdrLightEmbeddedClient_Impl::scrollObject( const awt::Size& /*aOffset*/ )
+{
+}
+
+void SAL_CALL SdrLightEmbeddedClient_Impl::changedPlacement( const awt::Rectangle& aPosRect )
+{
+ SolarMutexGuard aGuard;
+ if ( !mpObj )
+ throw uno::RuntimeException();
+
+ uno::Reference< embed::XInplaceObject > xInplace( mpObj->GetObjRef(), uno::UNO_QUERY_THROW );
+
+ // check if the change is at least one pixel in size
+ awt::Rectangle aOldRect = getPlacement();
+ tools::Rectangle aNewPixelRect = VCLRectangle( aPosRect );
+ tools::Rectangle aOldPixelRect = VCLRectangle( aOldRect );
+ if ( aOldPixelRect == aNewPixelRect )
+ // nothing has changed
+ return;
+
+ // new scaled object area
+ MapUnit aContainerMapUnit( MapUnit::Map100thMM );
+ uno::Reference< embed::XVisualObject > xParentVis( mpObj->GetParentXModel(), uno::UNO_QUERY );
+ if ( xParentVis.is() )
+ aContainerMapUnit = VCLUnoHelper::UnoEmbed2VCLMapUnit( xParentVis->getMapUnit( mpObj->GetAspect() ) );
+
+ tools::Rectangle aNewLogicRect = Application::GetDefaultDevice()->PixelToLogic(aNewPixelRect, MapMode(aContainerMapUnit));
+ tools::Rectangle aLogicRect = impl_getScaledRect_nothrow();
+
+ if ( aNewLogicRect == aLogicRect )
+ return;
+
+ // the calculation of the object area has not changed the object size
+ // it should be done here then
+ //SfxBooleanFlagGuard aGuard( m_bResizeNoScale, true );
+
+ // new size of the object area without scaling
+ Size aNewObjSize( tools::Long( aNewLogicRect.GetWidth() / m_aScaleWidth ),
+ tools::Long( aNewLogicRect.GetHeight() / m_aScaleHeight ) );
+
+ // now remove scaling from new placement and keep this at the new object area
+ aNewLogicRect.SetSize( aNewObjSize );
+ // react to the change if the difference is bigger than one pixel
+ Size aPixelDiff =
+ Application::GetDefaultDevice()->LogicToPixel(
+ Size( aLogicRect.GetWidth() - aNewObjSize.Width(),
+ aLogicRect.GetHeight() - aNewObjSize.Height() ),
+ MapMode(aContainerMapUnit));
+ if( aPixelDiff.Width() || aPixelDiff.Height() )
+ {
+ mpObj->SetLogicRect( tools::Rectangle( aLogicRect.TopLeft(), aNewObjSize ) );
+ mpObj->BroadcastObjectChange();
+ }
+ else
+ mpObj->ActionChanged();
+}
+// XWindowSupplier
+
+uno::Reference< awt::XWindow > SAL_CALL SdrLightEmbeddedClient_Impl::getWindow()
+{
+ SolarMutexGuard aGuard;
+ uno::Reference< awt::XWindow > xCurrent = m_xWindow;
+ if ( !xCurrent.is() )
+ {
+ if ( !mpObj )
+ throw uno::RuntimeException();
+ uno::Reference< frame::XFrame> xFrame(lcl_getFrame_throw(mpObj),uno::UNO_QUERY_THROW);
+ xCurrent = xFrame->getComponentWindow();
+ } // if ( !xCurrent.is() )
+ return xCurrent;
+}
+void SdrLightEmbeddedClient_Impl::setWindow(const uno::Reference< awt::XWindow >& _xWindow)
+{
+ m_xWindow = _xWindow;
+}
+
+SdrEmbedObjectLink::SdrEmbedObjectLink(SdrOle2Obj* pObject):
+ ::sfx2::SvBaseLink( ::SfxLinkUpdateMode::ONCALL, SotClipboardFormatId::SVXB ),
+ pObj(pObject)
+{
+ SetSynchron( false );
+}
+
+SdrEmbedObjectLink::~SdrEmbedObjectLink()
+{
+}
+
+::sfx2::SvBaseLink::UpdateResult SdrEmbedObjectLink::DataChanged(
+ const OUString& /*rMimeType*/, const css::uno::Any & /*rValue*/ )
+{
+ if ( !pObj->UpdateLinkURL_Impl() )
+ {
+ // the link URL was not changed
+ uno::Reference< embed::XEmbeddedObject > xObject = pObj->GetObjRef();
+ 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 ( uno::Exception& )
+ {
+ }
+ }
+ }
+
+ pObj->GetNewReplacement();
+ pObj->SetChanged();
+
+ return SUCCESS;
+}
+
+void SdrEmbedObjectLink::Closed()
+{
+ pObj->BreakFileLink_Impl();
+ SvBaseLink::Closed();
+}
+
+SdrIFrameLink::SdrIFrameLink(SdrOle2Obj* pObject)
+ : ::sfx2::SvBaseLink(::SfxLinkUpdateMode::ONCALL, SotClipboardFormatId::SVXB)
+ , m_pObject(pObject)
+{
+ SetSynchron( false );
+}
+
+::sfx2::SvBaseLink::UpdateResult SdrIFrameLink::DataChanged(
+ const OUString&, const uno::Any& )
+{
+ uno::Reference<embed::XEmbeddedObject> xObject = m_pObject->GetObjRef();
+ uno::Reference<embed::XCommonEmbedPersist> xPersObj(xObject, uno::UNO_QUERY);
+ if (xPersObj.is())
+ {
+ // let the IFrameObject reload the link
+ try
+ {
+ xPersObj->reload(uno::Sequence<beans::PropertyValue>(), uno::Sequence<beans::PropertyValue>());
+ }
+ catch (const uno::Exception&)
+ {
+ }
+
+ m_pObject->SetChanged();
+ }
+
+ return SUCCESS;
+}
+
+class SdrOle2ObjImpl
+{
+public:
+ svt::EmbeddedObjectRef mxObjRef;
+
+ std::unique_ptr<Graphic> mxGraphic;
+ OUString maProgName;
+ OUString aPersistName; // name of object in persist
+ rtl::Reference<SdrLightEmbeddedClient_Impl> mxLightClient; // must be registered as client only using AddOwnLightClient() call
+
+ bool mbFrame:1; // Due to compatibility at SdrTextObj for now
+ bool mbSuppressSetVisAreaSize:1; // #i118524#
+ mutable bool mbTypeAsked:1;
+ mutable bool mbIsChart:1;
+ bool mbLoadingOLEObjectFailed:1; // New local var to avoid repeated loading if load of OLE2 fails
+ bool mbConnected:1;
+
+ sfx2::SvBaseLink* mpObjectLink;
+ OUString maLinkURL;
+
+ rtl::Reference<SvxUnoShapeModifyListener> mxModifyListener;
+
+ explicit SdrOle2ObjImpl( bool bFrame ) :
+ mbFrame(bFrame),
+ mbSuppressSetVisAreaSize(false),
+ mbTypeAsked(false),
+ mbIsChart(false),
+ mbLoadingOLEObjectFailed(false),
+ mbConnected(false),
+ mpObjectLink(nullptr)
+ {
+ mxObjRef.Lock();
+ }
+
+ SdrOle2ObjImpl( bool bFrame, const svt::EmbeddedObjectRef& rObjRef ) :
+ mxObjRef(rObjRef),
+ mbFrame(bFrame),
+ mbSuppressSetVisAreaSize(false),
+ mbTypeAsked(false),
+ mbIsChart(false),
+ mbLoadingOLEObjectFailed(false),
+ mbConnected(false),
+ mpObjectLink(nullptr)
+ {
+ mxObjRef.Lock();
+ }
+
+ ~SdrOle2ObjImpl()
+ {
+ mxGraphic.reset();
+
+ if (mxModifyListener.is())
+ {
+ mxModifyListener->invalidate();
+ }
+ }
+};
+
+// Predicate determining whether the given OLE is an internal math
+// object
+static bool ImplIsMathObj( const uno::Reference < embed::XEmbeddedObject >& rObjRef )
+{
+ if ( !rObjRef.is() )
+ return false;
+
+ SvGlobalName aClassName( rObjRef->getClassID() );
+ return aClassName == SvGlobalName(SO3_SM_CLASSID_30) ||
+ aClassName == SvGlobalName(SO3_SM_CLASSID_40) ||
+ aClassName == SvGlobalName(SO3_SM_CLASSID_50) ||
+ aClassName == SvGlobalName(SO3_SM_CLASSID_60) ||
+ aClassName == SvGlobalName(SO3_SM_CLASSID);
+}
+
+// BaseProperties section
+
+std::unique_ptr<sdr::properties::BaseProperties> SdrOle2Obj::CreateObjectSpecificProperties()
+{
+ return std::make_unique<sdr::properties::OleProperties>(*this);
+}
+
+// DrawContact section
+
+std::unique_ptr<sdr::contact::ViewContact> SdrOle2Obj::CreateObjectSpecificViewContact()
+{
+ return std::make_unique<sdr::contact::ViewContactOfSdrOle2Obj>(*this);
+}
+
+void SdrOle2Obj::Init()
+{
+ // Stuff that was done from old SetModel:
+ // #i43086# #i85304 redo the change for charts for the above bugfix, as #i43086# does not occur anymore
+ // so maybe the ImpSetVisAreaSize call can be removed here completely
+ // Nevertheless I leave it in for other objects as I am not sure about the side effects when removing now
+ if(!getSdrModelFromSdrObject().isLocked() && !IsChart())
+ {
+ ImpSetVisAreaSize();
+ }
+
+ ::comphelper::IEmbeddedHelper* pDestPers(getSdrModelFromSdrObject().GetPersist());
+ if(pDestPers && !IsEmptyPresObj())
+ {
+ // object wasn't connected, now it should be
+ Connect_Impl();
+ }
+
+ AddListeners_Impl();
+}
+
+SdrOle2Obj::SdrOle2Obj(
+ SdrModel& rSdrModel,
+ bool bFrame_)
+: SdrRectObj(rSdrModel),
+ mpImpl(new SdrOle2ObjImpl(bFrame_))
+{
+ Init();
+}
+
+SdrOle2Obj::SdrOle2Obj(SdrModel& rSdrModel, SdrOle2Obj const & rSource)
+: SdrRectObj(rSdrModel, rSource),
+ mpImpl(new SdrOle2ObjImpl(/*bFrame*/false))
+{
+ Init();
+
+ // Manually copying bClosedObj attribute
+ SetClosedObj( rSource.IsClosedObj() );
+
+ mpImpl->aPersistName = rSource.mpImpl->aPersistName;
+ mpImpl->maProgName = rSource.mpImpl->maProgName;
+ mpImpl->mbFrame = rSource.mpImpl->mbFrame;
+
+ if (rSource.mpImpl->mxGraphic)
+ {
+ mpImpl->mxGraphic.reset(new Graphic(*rSource.mpImpl->mxGraphic));
+ }
+
+ if( IsEmptyPresObj() )
+ return;
+
+ ::comphelper::IEmbeddedHelper* pDestPers(getSdrModelFromSdrObject().GetPersist());
+ ::comphelper::IEmbeddedHelper* pSrcPers(rSource.getSdrModelFromSdrObject().GetPersist());
+ if( !(pDestPers && pSrcPers) )
+ return;
+
+ DBG_ASSERT( !mpImpl->mxObjRef.is(), "Object already existing!" );
+ comphelper::EmbeddedObjectContainer& rContainer = pSrcPers->getEmbeddedObjectContainer();
+ uno::Reference < embed::XEmbeddedObject > xObj = rContainer.GetEmbeddedObject( mpImpl->aPersistName );
+ if ( xObj.is() )
+ {
+ OUString aTmp;
+ mpImpl->mxObjRef.Assign( pDestPers->getEmbeddedObjectContainer().CopyAndGetEmbeddedObject(
+ rContainer, xObj, aTmp, pSrcPers->getDocumentBaseURL(), pDestPers->getDocumentBaseURL()), rSource.GetAspect());
+ mpImpl->mbTypeAsked = false;
+ mpImpl->aPersistName = aTmp;
+ CheckFileLink_Impl();
+ }
+
+ Connect();
+}
+
+SdrOle2Obj::SdrOle2Obj(
+ SdrModel& rSdrModel,
+ const svt::EmbeddedObjectRef& rNewObjRef,
+ const OUString& rNewObjName,
+ const tools::Rectangle& rNewRect)
+: SdrRectObj(rSdrModel, rNewRect),
+ mpImpl(new SdrOle2ObjImpl(false/*bFrame_*/, rNewObjRef))
+{
+ mpImpl->aPersistName = rNewObjName;
+
+ if (mpImpl->mxObjRef.is() && (mpImpl->mxObjRef->getStatus( GetAspect() ) & embed::EmbedMisc::EMBED_NEVERRESIZE ) )
+ m_bSizProt = true;
+
+ // For math objects, set closed state to transparent
+ SetClosedObj(!ImplIsMathObj( mpImpl->mxObjRef.GetObject() ));
+
+ Init();
+}
+
+OUString SdrOle2Obj::GetStyleString()
+{
+ OUString strStyle;
+ if (mpImpl->mxObjRef.is() && mpImpl->mxObjRef.IsChart())
+ {
+ strStyle = mpImpl->mxObjRef.GetChartType();
+ }
+ return strStyle;
+}
+
+SdrOle2Obj::~SdrOle2Obj()
+{
+ if ( mpImpl->mbConnected )
+ Disconnect();
+
+ DisconnectFileLink_Impl();
+
+ if (mpImpl->mxLightClient)
+ {
+ mpImpl->mxLightClient->disconnect();
+ mpImpl->mxLightClient.clear();
+ }
+}
+
+void SdrOle2Obj::SetAspect( sal_Int64 nAspect )
+{
+ mpImpl->mxObjRef.SetViewAspect( nAspect );
+}
+
+const svt::EmbeddedObjectRef& SdrOle2Obj::getEmbeddedObjectRef() const
+{
+ return mpImpl->mxObjRef;
+}
+
+sal_Int64 SdrOle2Obj::GetAspect() const
+{
+ return mpImpl->mxObjRef.GetViewAspect();
+}
+
+bool SdrOle2Obj::isInplaceActive() const
+{
+ return mpImpl->mxObjRef.is() && embed::EmbedStates::INPLACE_ACTIVE == mpImpl->mxObjRef->getCurrentState();
+}
+
+bool SdrOle2Obj::isUiActive() const
+{
+ return mpImpl->mxObjRef.is() && embed::EmbedStates::UI_ACTIVE == mpImpl->mxObjRef->getCurrentState();
+}
+
+void SdrOle2Obj::SetGraphic(const Graphic& rGrf)
+{
+ // only for setting a preview graphic
+ mpImpl->mxGraphic.reset(new Graphic(rGrf));
+
+ SetChanged();
+ BroadcastObjectChange();
+}
+
+void SdrOle2Obj::ClearGraphic()
+{
+ mpImpl->mxGraphic.reset();
+
+ SetChanged();
+ BroadcastObjectChange();
+}
+
+void SdrOle2Obj::SetProgName( const OUString& rName )
+{
+ mpImpl->maProgName = rName;
+}
+
+const OUString& SdrOle2Obj::GetProgName() const
+{
+ return mpImpl->maProgName;
+}
+
+bool SdrOle2Obj::IsEmpty() const
+{
+ return !mpImpl->mxObjRef.is();
+}
+
+void SdrOle2Obj::Connect(SvxOle2Shape* pCreator)
+{
+ if( IsEmptyPresObj() )
+ return;
+
+ if( mpImpl->mbConnected )
+ {
+ // currently there are situations where it seems to be unavoidable to have multiple connects
+ // changing this would need a larger code rewrite, so for now I remove the assertion
+ // OSL_FAIL("Connect() called on connected object!");
+ return;
+ }
+
+ Connect_Impl(pCreator);
+ AddListeners_Impl();
+}
+
+bool SdrOle2Obj::UpdateLinkURL_Impl()
+{
+ bool bResult = false;
+
+ if ( mpImpl->mpObjectLink )
+ {
+ sfx2::LinkManager* pLinkManager(getSdrModelFromSdrObject().GetLinkManager());
+
+ if ( pLinkManager )
+ {
+ OUString aNewLinkURL;
+ sfx2::LinkManager::GetDisplayNames( mpImpl->mpObjectLink, nullptr, &aNewLinkURL );
+ if ( !aNewLinkURL.equalsIgnoreAsciiCase( mpImpl->maLinkURL ) )
+ {
+ GetObjRef_Impl();
+ uno::Reference<embed::XCommonEmbedPersist> xPersObj( mpImpl->mxObjRef.GetObject(), uno::UNO_QUERY );
+ OSL_ENSURE( xPersObj.is(), "The object must exist!" );
+ if ( xPersObj.is() )
+ {
+ try
+ {
+ sal_Int32 nCurState = mpImpl->mxObjRef->getCurrentState();
+ if ( nCurState != embed::EmbedStates::LOADED )
+ mpImpl->mxObjRef->changeState(embed::EmbedStates::LOADED);
+
+ // TODO/LATER: there should be possible to get current mediadescriptor settings from the object
+ uno::Sequence< beans::PropertyValue > aArgs{ comphelper::makePropertyValue(
+ "URL", aNewLinkURL) };
+ xPersObj->reload( aArgs, uno::Sequence< beans::PropertyValue >() );
+
+ mpImpl->maLinkURL = aNewLinkURL;
+ bResult = true;
+
+ if ( nCurState != embed::EmbedStates::LOADED )
+ mpImpl->mxObjRef->changeState(nCurState);
+ }
+ catch( css::uno::Exception const & )
+ {
+ TOOLS_WARN_EXCEPTION( "svx", "SdrOle2Obj::UpdateLinkURL_Impl()" );
+ }
+ }
+
+ if ( !bResult )
+ {
+ // TODO/LATER: return the old name to the link manager, is it possible?
+ }
+ }
+ }
+ }
+
+ return bResult;
+}
+
+void SdrOle2Obj::BreakFileLink_Impl()
+{
+ uno::Reference<document::XStorageBasedDocument> xDoc(getSdrModelFromSdrObject().getUnoModel(), uno::UNO_QUERY);
+
+ if ( !xDoc.is() )
+ return;
+
+ uno::Reference< embed::XStorage > xStorage = xDoc->getDocumentStorage();
+ if ( !xStorage.is() )
+ return;
+
+ try
+ {
+ uno::Reference< embed::XLinkageSupport > xLinkSupport( mpImpl->mxObjRef.GetObject(), uno::UNO_QUERY_THROW );
+ xLinkSupport->breakLink( xStorage, mpImpl->aPersistName );
+ DisconnectFileLink_Impl();
+ mpImpl->maLinkURL.clear();
+ }
+ catch( css::uno::Exception& )
+ {
+ TOOLS_WARN_EXCEPTION( "svx", "SdrOle2Obj::BreakFileLink_Impl()" );
+ }
+}
+
+void SdrOle2Obj::DisconnectFileLink_Impl()
+{
+ sfx2::LinkManager* pLinkManager(getSdrModelFromSdrObject().GetLinkManager());
+
+ if ( pLinkManager && mpImpl->mpObjectLink )
+ {
+ pLinkManager->Remove( mpImpl->mpObjectLink );
+ mpImpl->mpObjectLink = nullptr;
+ }
+}
+
+void SdrOle2Obj::CheckFileLink_Impl()
+{
+ if (!mpImpl->mxObjRef.GetObject().is() || mpImpl->mpObjectLink)
+ return;
+
+ try
+ {
+ uno::Reference<embed::XEmbeddedObject> xObject = mpImpl->mxObjRef.GetObject();
+ if (!xObject)
+ return;
+
+ bool bIFrame = false;
+
+ OUString aLinkURL;
+ uno::Reference<embed::XLinkageSupport> xLinkSupport(xObject, uno::UNO_QUERY);
+ if (xLinkSupport)
+ {
+ if (xLinkSupport->isLink())
+ aLinkURL = xLinkSupport->getLinkURL();
+ }
+ else
+ {
+ // get IFrame (Floating Frames) listed and updatable from the
+ // manage links dialog
+ SvGlobalName aClassId(xObject->getClassID());
+ if (aClassId == SvGlobalName(SO3_IFRAME_CLASSID))
+ {
+ uno::Reference<beans::XPropertySet> xSet(xObject->getComponent(), uno::UNO_QUERY);
+ if (xSet.is())
+ xSet->getPropertyValue("FrameURL") >>= aLinkURL;
+ bIFrame = true;
+ }
+ }
+
+ if (!aLinkURL.isEmpty()) // this is a file link so the model link manager should handle it
+ {
+ sfx2::LinkManager* pLinkManager(getSdrModelFromSdrObject().GetLinkManager());
+
+ if ( pLinkManager )
+ {
+ SdrEmbedObjectLink* pEmbedObjectLink = nullptr;
+ if (!bIFrame)
+ {
+ pEmbedObjectLink = new SdrEmbedObjectLink(this);
+ mpImpl->mpObjectLink = pEmbedObjectLink;
+ }
+ else
+ mpImpl->mpObjectLink = new SdrIFrameLink(this);
+ mpImpl->maLinkURL = aLinkURL;
+ pLinkManager->InsertFileLink( *mpImpl->mpObjectLink, sfx2::SvBaseLinkObjectType::ClientOle, aLinkURL );
+ if (pEmbedObjectLink)
+ pEmbedObjectLink->Connect();
+ }
+ }
+ }
+ catch (const css::uno::Exception&)
+ {
+ TOOLS_WARN_EXCEPTION("svx", "SdrOle2Obj::CheckFileLink_Impl()");
+ }
+}
+
+void SdrOle2Obj::Connect_Impl(SvxOle2Shape* pCreator)
+{
+ if(mpImpl->aPersistName.isEmpty() )
+ return;
+
+ try
+ {
+ ::comphelper::IEmbeddedHelper* pPers(getSdrModelFromSdrObject().GetPersist());
+
+ if ( pPers )
+ {
+ comphelper::EmbeddedObjectContainer& rContainer = pPers->getEmbeddedObjectContainer();
+
+ if ( !rContainer.HasEmbeddedObject( mpImpl->aPersistName )
+ || ( mpImpl->mxObjRef.is() && !rContainer.HasEmbeddedObject( mpImpl->mxObjRef.GetObject() ) ) )
+ {
+ // object not known to container document
+ // No object -> disaster!
+ DBG_ASSERT( mpImpl->mxObjRef.is(), "No object in connect!");
+ if ( mpImpl->mxObjRef.is() )
+ {
+ // object came from the outside, now add it to the container
+ OUString aTmp;
+ rContainer.InsertEmbeddedObject( mpImpl->mxObjRef.GetObject(), aTmp );
+ mpImpl->aPersistName = aTmp;
+ }
+ }
+ else if ( !mpImpl->mxObjRef.is() )
+ {
+ mpImpl->mxObjRef.Assign( rContainer.GetEmbeddedObject( mpImpl->aPersistName ), mpImpl->mxObjRef.GetViewAspect() );
+ mpImpl->mbTypeAsked = false;
+ }
+
+ if ( mpImpl->mxObjRef.GetObject().is() )
+ {
+ mpImpl->mxObjRef.AssignToContainer( &rContainer, mpImpl->aPersistName );
+ mpImpl->mbConnected = true;
+ mpImpl->mxObjRef.Lock();
+ }
+ }
+
+ if (pCreator)
+ {
+ OUString sFrameURL(pCreator->GetAndClearInitialFrameURL());
+ if (!sFrameURL.isEmpty() && svt::EmbeddedObjectRef::TryRunningState(mpImpl->mxObjRef.GetObject()))
+ {
+ uno::Reference<beans::XPropertySet> xSet(mpImpl->mxObjRef->getComponent(), uno::UNO_QUERY);
+ if (xSet.is())
+ xSet->setPropertyValue("FrameURL", uno::Any(sFrameURL));
+ }
+ }
+
+ if ( mpImpl->mxObjRef.is() )
+ {
+ if ( !mpImpl->mxLightClient.is() )
+ mpImpl->mxLightClient = new SdrLightEmbeddedClient_Impl( this );
+
+ mpImpl->mxObjRef->addStateChangeListener( mpImpl->mxLightClient );
+ mpImpl->mxObjRef->addEventListener( mpImpl->mxLightClient );
+
+ if ( mpImpl->mxObjRef->getCurrentState() != embed::EmbedStates::LOADED )
+ GetSdrGlobalData().GetOLEObjCache().InsertObj(this);
+
+ CheckFileLink_Impl();
+
+ uno::Reference< container::XChild > xChild( mpImpl->mxObjRef.GetObject(), uno::UNO_QUERY );
+ if( xChild.is() )
+ {
+ uno::Reference< uno::XInterface > xParent( getSdrModelFromSdrObject().getUnoModel());
+ if( xParent.is())
+ xChild->setParent( getSdrModelFromSdrObject().getUnoModel() );
+ }
+
+ }
+ }
+ catch( css::uno::Exception& )
+ {
+ TOOLS_WARN_EXCEPTION( "svx", "SdrOle2Obj::Connect_Impl()" );
+ }
+}
+
+void SdrOle2Obj::ObjectLoaded()
+{
+ AddListeners_Impl();
+}
+
+void SdrOle2Obj::AddListeners_Impl()
+{
+ if( !(mpImpl->mxObjRef.is() && mpImpl->mxObjRef->getCurrentState() != embed::EmbedStates::LOADED) )
+ return;
+
+ // register modify listener
+ if (!mpImpl->mxModifyListener.is())
+ {
+ mpImpl->mxModifyListener = new SvxUnoShapeModifyListener(this);
+ }
+
+ uno::Reference< util::XModifyBroadcaster > xBC( getXModel(), uno::UNO_QUERY );
+ if (xBC.is())
+ {
+ xBC->addModifyListener( mpImpl->mxModifyListener );
+ }
+}
+
+void SdrOle2Obj::Disconnect()
+{
+ if( IsEmptyPresObj() )
+ return;
+
+ if( !mpImpl->mbConnected )
+ {
+ OSL_FAIL("Disconnect() called on disconnected object!");
+ return;
+ }
+
+ RemoveListeners_Impl();
+ Disconnect_Impl();
+}
+
+void SdrOle2Obj::RemoveListeners_Impl()
+{
+ if ( !mpImpl->mxObjRef.is() || mpImpl->aPersistName.isEmpty() )
+ return;
+
+ try
+ {
+ sal_Int32 nState = mpImpl->mxObjRef->getCurrentState();
+ if ( nState != embed::EmbedStates::LOADED )
+ {
+ uno::Reference< util::XModifyBroadcaster > xBC( getXModel(), uno::UNO_QUERY );
+ if (xBC.is() && mpImpl->mxModifyListener.is())
+ {
+ xBC->removeModifyListener( mpImpl->mxModifyListener );
+ }
+ }
+ }
+ catch( css::uno::Exception& )
+ {
+ TOOLS_WARN_EXCEPTION( "svx", "SdrOle2Obj::RemoveListeners_Impl()" );
+ }
+}
+
+void SdrOle2Obj::Disconnect_Impl()
+{
+ try
+ {
+ if ( !mpImpl->aPersistName.isEmpty() )
+ {
+ if( getSdrModelFromSdrObject().IsInDestruction() )
+ {
+ // TODO/LATER: here we must assume that the destruction of the model is enough to make clear that we will not
+ // remove the object from the container, even if the DrawingObject itself is not destroyed (unfortunately this
+ // There is no real need to do the following removing of the object from the container
+ // in case the model has correct persistence, but in case of problems such a removing
+ // would make the behavior of the office more stable
+
+ comphelper::EmbeddedObjectContainer* pContainer = mpImpl->mxObjRef.GetContainer();
+ if ( pContainer )
+ {
+ pContainer->CloseEmbeddedObject( mpImpl->mxObjRef.GetObject() );
+ mpImpl->mxObjRef.AssignToContainer( nullptr, mpImpl->aPersistName );
+ }
+
+ // happens later than the destruction of the model, so we can't assert that).
+ //DBG_ASSERT( bInDestruction, "Model is destroyed, but not me?!" );
+ //TODO/LATER: should be make sure that the ObjectShell also forgets the object, because we will close it soon?
+ /*
+ uno::Reference < util::XCloseable > xClose( xObjRef, uno::UNO_QUERY );
+ if ( xClose.is() )
+ {
+ try
+ {
+ xClose->close( true );
+ }
+ catch ( util::CloseVetoException& )
+ {
+ // there's still someone who needs the object!
+ }
+ }
+
+ xObjRef = NULL;*/
+ }
+ else if ( mpImpl->mxObjRef.is() )
+ {
+ if ( getSdrModelFromSdrObject().getUnoModel().is() )
+ {
+ // remove object, but don't close it (that's up to someone else)
+ comphelper::EmbeddedObjectContainer* pContainer = mpImpl->mxObjRef.GetContainer();
+ if ( pContainer )
+ {
+ pContainer->RemoveEmbeddedObject( mpImpl->mxObjRef.GetObject() );
+
+ // TODO/LATER: mpImpl->aPersistName contains outdated information, to keep it updated
+ // it should be returned from RemoveEmbeddedObject call. Currently it is no problem,
+ // since no container is adjusted, actually the empty string could be provided as a name here
+ mpImpl->mxObjRef.AssignToContainer( nullptr, mpImpl->aPersistName );
+ }
+
+ DisconnectFileLink_Impl();
+ }
+ }
+ }
+
+ if ( mpImpl->mxObjRef.is() && mpImpl->mxLightClient.is() )
+ {
+ mpImpl->mxObjRef->removeStateChangeListener ( mpImpl->mxLightClient );
+ mpImpl->mxObjRef->removeEventListener( mpImpl->mxLightClient );
+ mpImpl->mxObjRef->setClientSite( nullptr );
+
+ GetSdrGlobalData().GetOLEObjCache().RemoveObj(this);
+ }
+ }
+ catch( css::uno::Exception& )
+ {
+ TOOLS_WARN_EXCEPTION( "svx", "SdrOle2Obj::Disconnect_Impl()" );
+ }
+
+ mpImpl->mbConnected = false;
+}
+
+SdrObjectUniquePtr SdrOle2Obj::createSdrGrafObjReplacement(bool bAddText) const
+{
+ const Graphic* pOLEGraphic = GetGraphic();
+
+ if(pOLEGraphic)
+ {
+ // #i118485# allow creating a SdrGrafObj representation
+ SdrGrafObj* pClone = new SdrGrafObj(
+ getSdrModelFromSdrObject(),
+ *pOLEGraphic);
+
+ // copy transformation
+ basegfx::B2DHomMatrix aMatrix;
+ basegfx::B2DPolyPolygon aPolyPolygon;
+
+ TRGetBaseGeometry(aMatrix, aPolyPolygon);
+ pClone->TRSetBaseGeometry(aMatrix, aPolyPolygon);
+
+ // copy all attributes to support graphic styles for OLEs
+ pClone->SetStyleSheet(GetStyleSheet(), false);
+ pClone->SetMergedItemSet(GetMergedItemSet());
+
+ if(bAddText)
+ {
+ // #i118485# copy text (Caution! Model needed, as guaranteed in aw080)
+ OutlinerParaObject* pOPO = GetOutlinerParaObject();
+
+ if(pOPO)
+ {
+ pClone->NbcSetOutlinerParaObject(*pOPO);
+ }
+ }
+
+ return SdrObjectUniquePtr(pClone);
+ }
+ else
+ {
+ // #i100710# pOLEGraphic may be zero (no visualisation available),
+ // so we need to use the OLE replacement graphic
+ SdrRectObj* pClone = new SdrRectObj(
+ getSdrModelFromSdrObject(),
+ GetSnapRect());
+
+ // gray outline
+ pClone->SetMergedItem(XLineStyleItem(css::drawing::LineStyle_SOLID));
+ const svtools::ColorConfig aColorConfig;
+ const svtools::ColorConfigValue aColor(aColorConfig.GetColorValue(svtools::OBJECTBOUNDARIES));
+ pClone->SetMergedItem(XLineColorItem(OUString(), aColor.nColor));
+
+ // bitmap fill
+ pClone->SetMergedItem(XFillStyleItem(drawing::FillStyle_BITMAP));
+ pClone->SetMergedItem(XFillBitmapItem(OUString(), GetEmptyOLEReplacementGraphic()));
+ pClone->SetMergedItem(XFillBmpTileItem(false));
+ pClone->SetMergedItem(XFillBmpStretchItem(false));
+
+ return SdrObjectUniquePtr(pClone);
+ }
+}
+
+SdrObjectUniquePtr SdrOle2Obj::DoConvertToPolyObj(bool bBezier, bool bAddText) const
+{
+ // #i118485# missing converter added
+ SdrObjectUniquePtr pRetval = createSdrGrafObjReplacement(true);
+
+ if(pRetval)
+ {
+ return pRetval->DoConvertToPolyObj(bBezier, bAddText);
+ }
+
+ return nullptr;
+}
+
+void SdrOle2Obj::handlePageChange(SdrPage* pOldPage, SdrPage* pNewPage)
+{
+ const bool bRemove(pNewPage == nullptr && pOldPage != nullptr);
+ const bool bInsert(pNewPage != nullptr && pOldPage == nullptr);
+
+ if (bRemove && mpImpl->mbConnected )
+ {
+ Disconnect();
+ }
+
+ // call parent
+ SdrRectObj::handlePageChange(pOldPage, pNewPage);
+
+ if (bInsert && !mpImpl->mbConnected )
+ {
+ Connect();
+ }
+}
+
+void SdrOle2Obj::SetObjRef( const css::uno::Reference < css::embed::XEmbeddedObject >& rNewObjRef )
+{
+ DBG_ASSERT( !rNewObjRef.is() || !mpImpl->mxObjRef.GetObject().is(), "SetObjRef called on already initialized object!");
+ if( rNewObjRef == mpImpl->mxObjRef.GetObject() )
+ return;
+
+ // the caller of the method is responsible to control the old object, it will not be closed here
+ // Otherwise WW8 import crashes because it transfers control to OLENode by this method
+ if ( mpImpl->mxObjRef.GetObject().is() )
+ mpImpl->mxObjRef.Lock( false );
+
+ // avoid removal of object in Disconnect! It is definitely a HACK to call SetObjRef(0)!
+ // This call will try to close the objects; so if anybody else wants to keep it, it must be locked by a CloseListener
+ mpImpl->mxObjRef.Clear();
+
+ if ( mpImpl->mbConnected )
+ Disconnect();
+
+ mpImpl->mxObjRef.Assign( rNewObjRef, GetAspect() );
+ mpImpl->mbTypeAsked = false;
+
+ if ( mpImpl->mxObjRef.is() )
+ {
+ mpImpl->mxGraphic.reset();
+
+ if ( mpImpl->mxObjRef->getStatus( GetAspect() ) & embed::EmbedMisc::EMBED_NEVERRESIZE )
+ SetResizeProtect(true);
+
+ // For math objects, set closed state to transparent
+ SetClosedObj(!ImplIsMathObj( rNewObjRef ));
+
+ Connect();
+ }
+
+ SetChanged();
+ BroadcastObjectChange();
+}
+
+void SdrOle2Obj::SetClosedObj( bool bIsClosed )
+{
+ // TODO/LATER: do we still need this hack?
+ // Allow changes to the closed state of OLE objects
+ m_bClosedObj = bIsClosed;
+}
+
+SdrObjectUniquePtr SdrOle2Obj::getFullDragClone() const
+{
+ // #i118485# use central replacement generator
+ return createSdrGrafObjReplacement(false);
+}
+
+void SdrOle2Obj::SetPersistName( const OUString& rPersistName, SvxOle2Shape* pCreator )
+{
+ DBG_ASSERT( mpImpl->aPersistName.isEmpty(), "Persist name changed!");
+
+ mpImpl->aPersistName = rPersistName;
+ mpImpl->mbLoadingOLEObjectFailed = false;
+
+ Connect(pCreator);
+ SetChanged();
+}
+
+void SdrOle2Obj::AbandonObject()
+{
+ mpImpl->aPersistName.clear();
+ mpImpl->mbLoadingOLEObjectFailed = false;
+ SetObjRef(nullptr);
+}
+
+const OUString& SdrOle2Obj::GetPersistName() const
+{
+ return mpImpl->aPersistName;
+}
+
+void SdrOle2Obj::TakeObjInfo(SdrObjTransformInfoRec& rInfo) const
+{
+ // #i118485# Allowing much more attributes for OLEs
+ rInfo.bRotateFreeAllowed = true;
+ rInfo.bRotate90Allowed = true;
+ rInfo.bMirrorFreeAllowed = true;
+ rInfo.bMirror45Allowed = true;
+ rInfo.bMirror90Allowed = true;
+ rInfo.bTransparenceAllowed = true;
+ rInfo.bShearAllowed = true;
+ rInfo.bEdgeRadiusAllowed = false;
+ rInfo.bNoOrthoDesired = false;
+ rInfo.bCanConvToPath = true;
+ rInfo.bCanConvToPoly = true;
+ rInfo.bCanConvToPathLineToArea = false;
+ rInfo.bCanConvToPolyLineToArea = false;
+ rInfo.bCanConvToContour = true;
+}
+
+SdrObjKind SdrOle2Obj::GetObjIdentifier() const
+{
+ return mpImpl->mbFrame ? SdrObjKind::OLEPluginFrame : SdrObjKind::OLE2;
+}
+
+OUString SdrOle2Obj::TakeObjNameSingul() const
+{
+ OUStringBuffer sName(SvxResId(mpImpl->mbFrame ? STR_ObjNameSingulFrame : STR_ObjNameSingulOLE2));
+
+ const OUString aName(GetName());
+
+ if (!aName.isEmpty())
+ {
+ sName.append(" '");
+ sName.append(aName);
+ sName.append('\'');
+ }
+
+ return sName.makeStringAndClear();
+}
+
+OUString SdrOle2Obj::TakeObjNamePlural() const
+{
+ return SvxResId(mpImpl->mbFrame ? STR_ObjNamePluralFrame : STR_ObjNamePluralOLE2);
+}
+
+SdrOle2Obj* SdrOle2Obj::CloneSdrObject(SdrModel& rTargetModel) const
+{
+ return new SdrOle2Obj(rTargetModel, *this);
+}
+
+void SdrOle2Obj::ImpSetVisAreaSize()
+{
+ // #i118524# do not again set VisAreaSize when the call comes from OLE client (e.g. ObjectAreaChanged)
+ if (mpImpl->mbSuppressSetVisAreaSize)
+ return;
+
+ // currently there is no need to recalculate scaling for iconified objects
+ // TODO/LATER: it might be needed in future when it is possible to change the icon
+ if ( GetAspect() == embed::Aspects::MSOLE_ICON )
+ return;
+
+ // the object area of an embedded object was changed, e.g. by user interaction an a selected object
+ GetObjRef();
+ if (!mpImpl->mxObjRef.is())
+ return;
+
+ sal_Int64 nMiscStatus = mpImpl->mxObjRef->getStatus( GetAspect() );
+
+ // the client is required to get access to scaling
+ SfxInPlaceClient* pClient(
+ SfxInPlaceClient::GetClient(
+ dynamic_cast<SfxObjectShell*>(
+ getSdrModelFromSdrObject().GetPersist()),
+ mpImpl->mxObjRef.GetObject()));
+ const bool bHasOwnClient(
+ mpImpl->mxLightClient.is() &&
+ mpImpl->mxObjRef->getClientSite() == uno::Reference< embed::XEmbeddedClient >( mpImpl->mxLightClient ) );
+
+ if ( pClient || bHasOwnClient )
+ {
+ // TODO: IMHO we need to do similar things when object is UIActive or OutplaceActive?!
+ if ( ((nMiscStatus & embed::EmbedMisc::MS_EMBED_RECOMPOSEONRESIZE) &&
+ svt::EmbeddedObjectRef::TryRunningState( mpImpl->mxObjRef.GetObject() ))
+ || mpImpl->mxObjRef->getCurrentState() == embed::EmbedStates::INPLACE_ACTIVE
+ )
+ {
+ Fraction aScaleWidth;
+ Fraction aScaleHeight;
+ if ( pClient )
+ {
+ aScaleWidth = pClient->GetScaleWidth();
+ aScaleHeight = pClient->GetScaleHeight();
+ }
+ else
+ {
+ aScaleWidth = mpImpl->mxLightClient->GetScaleWidth();
+ aScaleHeight = mpImpl->mxLightClient->GetScaleHeight();
+ }
+
+ // The object wants to resize itself (f.e. Chart wants to recalculate the layout)
+ // or object is inplace active and so has a window that must be resized also
+ // In these cases the change in the object area size will be reflected in a change of the
+ // objects' visual area. The scaling will not change, but it might exist already and must
+ // be used in calculations
+ MapUnit aMapUnit = VCLUnoHelper::UnoEmbed2VCLMapUnit( mpImpl->mxObjRef->getMapUnit( GetAspect() ) );
+ Size aVisSize;
+ if (sal_Int32(aScaleWidth) != 0 && sal_Int32(aScaleHeight) != 0) // avoid div by zero
+ aVisSize = Size( static_cast<tools::Long>( Fraction( maRect.GetWidth() ) / aScaleWidth ),
+ static_cast<tools::Long>( Fraction( maRect.GetHeight() ) / aScaleHeight ) );
+
+ aVisSize = OutputDevice::LogicToLogic(
+ aVisSize,
+ MapMode(getSdrModelFromSdrObject().GetScaleUnit()),
+ MapMode(aMapUnit));
+ awt::Size aSz;
+ aSz.Width = aVisSize.Width();
+ aSz.Height = aVisSize.Height();
+ mpImpl->mxObjRef->setVisualAreaSize( GetAspect(), aSz );
+
+ try
+ {
+ aSz = mpImpl->mxObjRef->getVisualAreaSize( GetAspect() );
+ }
+ catch( embed::NoVisualAreaSizeException& )
+ {}
+
+ tools::Rectangle aAcceptedVisArea;
+ aAcceptedVisArea.SetSize( Size( static_cast<tools::Long>( Fraction( tools::Long( aSz.Width ) ) * aScaleWidth ),
+ static_cast<tools::Long>( Fraction( tools::Long( aSz.Height ) ) * aScaleHeight ) ) );
+ if (aVisSize != aAcceptedVisArea.GetSize())
+ {
+ // server changed VisArea to its liking and the VisArea is different than the suggested one
+ // store the new value as given by the object
+ MapUnit aNewMapUnit = VCLUnoHelper::UnoEmbed2VCLMapUnit( mpImpl->mxObjRef->getMapUnit( GetAspect() ) );
+ maRect.SetSize(
+ OutputDevice::LogicToLogic(
+ aAcceptedVisArea.GetSize(),
+ MapMode(aNewMapUnit),
+ MapMode(getSdrModelFromSdrObject().GetScaleUnit())));
+ }
+
+ // make the new object area known to the client
+ // compared to the "else" branch aRect might have been changed by the object and no additional scaling was applied
+ // WHY this -> OSL_ASSERT( pClient );
+ if( pClient )
+ pClient->SetObjArea(maRect);
+
+ // we need a new replacement image as the object has resized itself
+
+ //#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 there
+ if( !IsChart() )
+ mpImpl->mxObjRef.UpdateReplacement();
+ }
+ else
+ {
+ // The object isn't active and does not want to resize itself so the changed object area size
+ // will be reflected in a changed object scaling
+ Fraction aScaleWidth;
+ Fraction aScaleHeight;
+ Size aObjAreaSize;
+ if ( CalculateNewScaling( aScaleWidth, aScaleHeight, aObjAreaSize ) )
+ {
+ if ( pClient )
+ {
+ tools::Rectangle aScaleRect(maRect.TopLeft(), aObjAreaSize);
+ pClient->SetObjAreaAndScale( aScaleRect, aScaleWidth, aScaleHeight);
+ }
+ else
+ {
+ mpImpl->mxLightClient->SetSizeScale( aScaleWidth, aScaleHeight );
+ }
+ }
+ }
+ }
+ else if( (nMiscStatus & embed::EmbedMisc::MS_EMBED_RECOMPOSEONRESIZE) &&
+ svt::EmbeddedObjectRef::TryRunningState( mpImpl->mxObjRef.GetObject() ) )
+ {
+ //also handle not sfx based ole objects e.g. charts
+ //#i83860# resizing charts in impress distorts fonts
+ uno::Reference< embed::XVisualObject > xVisualObject( getXModel(), uno::UNO_QUERY );
+ if( xVisualObject.is() )
+ {
+ const MapUnit aMapUnit(
+ VCLUnoHelper::UnoEmbed2VCLMapUnit(
+ mpImpl->mxObjRef->getMapUnit(GetAspect())));
+ const Point aTL( maRect.TopLeft() );
+ const Point aBR( maRect.BottomRight() );
+ const Point aTL2(
+ OutputDevice::LogicToLogic(
+ aTL,
+ MapMode(getSdrModelFromSdrObject().GetScaleUnit()),
+ MapMode(aMapUnit)));
+ const Point aBR2(
+ OutputDevice::LogicToLogic(
+ aBR,
+ MapMode(getSdrModelFromSdrObject().GetScaleUnit()),
+ MapMode(aMapUnit)));
+ const tools::Rectangle aNewRect(
+ aTL2,
+ aBR2);
+
+ xVisualObject->setVisualAreaSize(
+ GetAspect(),
+ awt::Size(
+ aNewRect.GetWidth(),
+ aNewRect.GetHeight()));
+ }
+ }
+}
+
+void SdrOle2Obj::NbcResize(const Point& rRef, const Fraction& xFact, const Fraction& yFact)
+{
+ if(!getSdrModelFromSdrObject().isLocked())
+ {
+ GetObjRef();
+
+ if ( mpImpl->mxObjRef.is() && ( mpImpl->mxObjRef->getStatus( GetAspect() ) & embed::EmbedMisc::MS_EMBED_RECOMPOSEONRESIZE ) )
+ {
+ // if the object needs recompose on resize
+ // the client site should be created before the resize will take place
+ // check whether there is no client site and create it if necessary
+ AddOwnLightClient();
+ }
+ }
+
+ SdrRectObj::NbcResize(rRef,xFact,yFact);
+
+ if( !getSdrModelFromSdrObject().isLocked() )
+ ImpSetVisAreaSize();
+}
+
+void SdrOle2Obj::SetGeoData(const SdrObjGeoData& rGeo)
+{
+ SdrRectObj::SetGeoData(rGeo);
+
+ if( !getSdrModelFromSdrObject().isLocked() )
+ ImpSetVisAreaSize();
+}
+
+void SdrOle2Obj::NbcSetSnapRect(const tools::Rectangle& rRect)
+{
+ SdrRectObj::NbcSetSnapRect(rRect);
+
+ if( !getSdrModelFromSdrObject().isLocked() )
+ ImpSetVisAreaSize();
+
+ if ( mpImpl->mxObjRef.is() && IsChart() )
+ {
+ //#i103460# charts do not necessarily have an own size within ODF files,
+ //for this case they need to use the size settings from the surrounding frame,
+ //which is made available with this method as there is no other way
+ mpImpl->mxObjRef.SetDefaultSizeForChart( Size( rRect.GetWidth(), rRect.GetHeight() ) );
+ }
+}
+
+void SdrOle2Obj::NbcSetLogicRect(const tools::Rectangle& rRect)
+{
+ SdrRectObj::NbcSetLogicRect(rRect);
+
+ if( !getSdrModelFromSdrObject().isLocked() )
+ ImpSetVisAreaSize();
+}
+
+const Graphic* SdrOle2Obj::GetGraphic() const
+{
+ if ( mpImpl->mxObjRef.is() )
+ return mpImpl->mxObjRef.GetGraphic();
+ return mpImpl->mxGraphic.get();
+}
+
+void SdrOle2Obj::GetNewReplacement()
+{
+ if ( mpImpl->mxObjRef.is() )
+ mpImpl->mxObjRef.UpdateReplacement();
+}
+
+Size SdrOle2Obj::GetOrigObjSize( MapMode const * pTargetMapMode ) const
+{
+ return mpImpl->mxObjRef.GetSize( pTargetMapMode );
+}
+
+void SdrOle2Obj::setSuppressSetVisAreaSize( bool bNew )
+{
+ mpImpl->mbSuppressSetVisAreaSize = bNew;
+}
+
+void SdrOle2Obj::NbcMove(const Size& rSize)
+{
+ SdrRectObj::NbcMove(rSize);
+
+ if( !getSdrModelFromSdrObject().isLocked() )
+ ImpSetVisAreaSize();
+}
+
+bool SdrOle2Obj::CanUnloadRunningObj( const uno::Reference< embed::XEmbeddedObject >& xObj, sal_Int64 nAspect )
+{
+ uno::Reference<embed::XEmbedPersist2> xPersist(xObj, uno::UNO_QUERY);
+ if (xPersist.is())
+ {
+ if (!xPersist->isStored())
+ // It doesn't have persistent storage. We can't unload this.
+ return false;
+ }
+
+ bool bResult = false;
+
+ sal_Int32 nState = xObj->getCurrentState();
+ if ( nState == embed::EmbedStates::LOADED )
+ {
+ // the object is already unloaded
+ bResult = true;
+ }
+ else
+ {
+ uno::Reference < util::XModifiable > xModifiable( xObj->getComponent(), uno::UNO_QUERY );
+ if ( !xModifiable.is() )
+ bResult = true;
+ else
+ {
+ sal_Int64 nMiscStatus = xObj->getStatus( nAspect );
+
+ if ( embed::EmbedMisc::MS_EMBED_ALWAYSRUN != ( nMiscStatus & embed::EmbedMisc::MS_EMBED_ALWAYSRUN ) &&
+ embed::EmbedMisc::EMBED_ACTIVATEIMMEDIATELY != ( nMiscStatus & embed::EmbedMisc::EMBED_ACTIVATEIMMEDIATELY ) &&
+ !( xModifiable.is() && xModifiable->isModified() ) &&
+ !( nState == embed::EmbedStates::INPLACE_ACTIVE || nState == embed::EmbedStates::UI_ACTIVE || nState == embed::EmbedStates::ACTIVE ) )
+ {
+ bResult = true;
+ }
+ }
+ }
+
+ return bResult;
+}
+
+bool SdrOle2Obj::Unload( const uno::Reference< embed::XEmbeddedObject >& xObj, sal_Int64 nAspect )
+{
+ bool bResult = false;
+
+ if ( CanUnloadRunningObj( xObj, nAspect ) )
+ {
+ try
+ {
+ xObj->changeState( embed::EmbedStates::LOADED );
+ bResult = true;
+ }
+ catch( css::uno::Exception& )
+ {
+ TOOLS_WARN_EXCEPTION( "svx", "SdrOle2Obj::Unload()" );
+ }
+ }
+
+ return bResult;
+}
+
+bool SdrOle2Obj::Unload()
+{
+ if (!mpImpl->mxObjRef.is())
+ // Already unloaded.
+ return true;
+
+ return Unload(mpImpl->mxObjRef.GetObject(), GetAspect());
+}
+
+void SdrOle2Obj::GetObjRef_Impl()
+{
+ if ( !mpImpl->mxObjRef.is() && !mpImpl->aPersistName.isEmpty() && getSdrModelFromSdrObject().GetPersist() )
+ {
+ // Only try loading if it did not went wrong up to now
+ if(!mpImpl->mbLoadingOLEObjectFailed)
+ {
+ mpImpl->mxObjRef.Assign(
+ getSdrModelFromSdrObject().GetPersist()->getEmbeddedObjectContainer().GetEmbeddedObject(mpImpl->aPersistName),
+ GetAspect());
+ mpImpl->mbTypeAsked = false;
+ CheckFileLink_Impl();
+
+ // If loading of OLE object failed, remember that to not invoke an endless
+ // loop trying to load it again and again.
+ if( mpImpl->mxObjRef.is() )
+ {
+ mpImpl->mbLoadingOLEObjectFailed = true;
+ }
+
+ // For math objects, set closed state to transparent
+ SetClosedObj(!ImplIsMathObj( mpImpl->mxObjRef.GetObject() ));
+ }
+
+ if ( mpImpl->mxObjRef.is() )
+ {
+ if( !IsEmptyPresObj() )
+ {
+ // remember modified status of model
+ const bool bWasChanged(getSdrModelFromSdrObject().IsChanged());
+
+ // perhaps preview not valid anymore
+ // This line changes the modified state of the model
+ ClearGraphic();
+
+ // if status was not set before, force it back
+ // to not set, so that SetGraphic(0) above does not
+ // set the modified state of the model.
+ if(!bWasChanged && getSdrModelFromSdrObject().IsChanged())
+ {
+ getSdrModelFromSdrObject().SetChanged( false );
+ }
+ }
+ }
+
+ if ( mpImpl->mxObjRef.is() )
+ Connect();
+ }
+
+ if ( mpImpl->mbConnected )
+ {
+ // move object to first position in cache
+ GetSdrGlobalData().GetOLEObjCache().InsertObj(this);
+ }
+}
+
+uno::Reference < embed::XEmbeddedObject > const & SdrOle2Obj::GetObjRef() const
+{
+ const_cast<SdrOle2Obj*>(this)->GetObjRef_Impl();
+ return mpImpl->mxObjRef.GetObject();
+}
+
+uno::Reference < embed::XEmbeddedObject > const & SdrOle2Obj::GetObjRef_NoInit() const
+{
+ return mpImpl->mxObjRef.GetObject();
+}
+
+uno::Reference< frame::XModel > SdrOle2Obj::getXModel() const
+{
+ if (svt::EmbeddedObjectRef::TryRunningState(GetObjRef()))
+ return uno::Reference< frame::XModel >( mpImpl->mxObjRef->getComponent(), uno::UNO_QUERY );
+ else
+ return uno::Reference< frame::XModel >();
+}
+
+bool SdrOle2Obj::IsChart() const
+{
+ if (!mpImpl->mbTypeAsked)
+ {
+ mpImpl->mbIsChart = mpImpl->mxObjRef.IsChart();
+ mpImpl->mbTypeAsked = true;
+ }
+ return mpImpl->mbIsChart;
+}
+
+void SdrOle2Obj::SetGraphicToObj( const Graphic& aGraphic )
+{
+ mpImpl->mxObjRef.SetGraphic( aGraphic, OUString() );
+ // if the object isn't valid, e.g. link to something that doesn't exist, set the fallback
+ // graphic as mxGraphic so SdrOle2Obj::GetGraphic will show the fallback
+ if (const Graphic* pObjGraphic = mpImpl->mxObjRef.is() ? nullptr : mpImpl->mxObjRef.GetGraphic())
+ mpImpl->mxGraphic.reset(new Graphic(*pObjGraphic));
+}
+
+void SdrOle2Obj::SetGraphicToObj( const uno::Reference< io::XInputStream >& xGrStream, const OUString& aMediaType )
+{
+ mpImpl->mxObjRef.SetGraphicStream( xGrStream, aMediaType );
+ // if the object isn't valid, e.g. link to something that doesn't exist, set the fallback
+ // graphic as mxGraphic so SdrOle2Obj::GetGraphic will show the fallback
+ if (const Graphic* pObjGraphic = mpImpl->mxObjRef.is() ? nullptr : mpImpl->mxObjRef.GetGraphic())
+ mpImpl->mxGraphic.reset(new Graphic(*pObjGraphic));
+}
+
+bool SdrOle2Obj::IsCalc() const
+{
+ if ( !mpImpl->mxObjRef.is() )
+ return false;
+
+ SvGlobalName aObjClsId( mpImpl->mxObjRef->getClassID() );
+ return SvGlobalName(SO3_SC_CLASSID_30) == aObjClsId
+ || SvGlobalName(SO3_SC_CLASSID_40) == aObjClsId
+ || SvGlobalName(SO3_SC_CLASSID_50) == aObjClsId
+ || SvGlobalName(SO3_SC_CLASSID_60) == aObjClsId
+ || SvGlobalName(SO3_SC_OLE_EMBED_CLASSID_60) == aObjClsId
+ || SvGlobalName(SO3_SC_OLE_EMBED_CLASSID_8) == aObjClsId
+ || SvGlobalName(SO3_SC_CLASSID) == aObjClsId;
+}
+
+uno::Reference< frame::XModel > SdrOle2Obj::GetParentXModel() const
+{
+ uno::Reference< frame::XModel > xDoc(getSdrModelFromSdrObject().getUnoModel(), uno::UNO_QUERY);
+ return xDoc;
+}
+
+bool SdrOle2Obj::CalculateNewScaling( Fraction& aScaleWidth, Fraction& aScaleHeight, Size& aObjAreaSize )
+{
+ // TODO/LEAN: to avoid rounding errors scaling always uses the VisArea.
+ // If we don't cache it for own objects also we must load the object here
+ if (!mpImpl->mxObjRef.is())
+ return false;
+
+ MapMode aMapMode(getSdrModelFromSdrObject().GetScaleUnit());
+ aObjAreaSize = mpImpl->mxObjRef.GetSize( &aMapMode );
+
+ Size aSize = maRect.GetSize();
+ aScaleWidth = Fraction(aSize.Width(), aObjAreaSize.Width() );
+ aScaleHeight = Fraction(aSize.Height(), aObjAreaSize.Height() );
+
+ // reduce to 10 binary digits
+ aScaleHeight.ReduceInaccurate(10);
+ aScaleWidth.ReduceInaccurate(10);
+
+ return true;
+}
+
+bool SdrOle2Obj::AddOwnLightClient()
+{
+ // The Own Light Client must be registered in object only using this method!
+ if ( !SfxInPlaceClient::GetClient( dynamic_cast<SfxObjectShell*>(getSdrModelFromSdrObject().GetPersist()), mpImpl->mxObjRef.GetObject() )
+ && !( mpImpl->mxLightClient.is() && mpImpl->mxObjRef->getClientSite() == uno::Reference< embed::XEmbeddedClient >( mpImpl->mxLightClient ) ) )
+ {
+ Connect();
+
+ if ( mpImpl->mxObjRef.is() && mpImpl->mxLightClient.is() )
+ {
+ Fraction aScaleWidth;
+ Fraction aScaleHeight;
+ Size aObjAreaSize;
+ if ( CalculateNewScaling( aScaleWidth, aScaleHeight, aObjAreaSize ) )
+ {
+ mpImpl->mxLightClient->SetSizeScale( aScaleWidth, aScaleHeight );
+ try {
+ mpImpl->mxObjRef->setClientSite( mpImpl->mxLightClient );
+ return true;
+ } catch( uno::Exception& )
+ {}
+ }
+
+ }
+
+ return false;
+ }
+
+ return true;
+}
+
+Graphic SdrOle2Obj::GetEmptyOLEReplacementGraphic()
+{
+ return Graphic(BitmapEx(BMP_SVXOLEOBJ));
+}
+
+void SdrOle2Obj::SetWindow(const css::uno::Reference < css::awt::XWindow >& _xWindow)
+{
+ if ( mpImpl->mxObjRef.is() && mpImpl->mxLightClient.is() )
+ {
+ mpImpl->mxLightClient->setWindow(_xWindow);
+ }
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/svdraw/svdopage.cxx b/svx/source/svdraw/svdopage.cxx
new file mode 100644
index 000000000..67c7a104e
--- /dev/null
+++ b/svx/source/svdraw/svdopage.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 <svx/svdopage.hxx>
+#include <svx/dialmgr.hxx>
+#include <svx/strings.hrc>
+#include <svx/svdmodel.hxx>
+#include <svx/svdpage.hxx>
+#include <sdr/properties/pageproperties.hxx>
+#include <sdr/contact/viewcontactofpageobj.hxx>
+
+
+// BaseProperties section
+
+std::unique_ptr<sdr::properties::BaseProperties> SdrPageObj::CreateObjectSpecificProperties()
+{
+ return std::make_unique<sdr::properties::PageProperties>(*this);
+}
+
+
+// DrawContact section
+
+std::unique_ptr<sdr::contact::ViewContact> SdrPageObj::CreateObjectSpecificViewContact()
+{
+ return std::make_unique<sdr::contact::ViewContactOfPageObj>(*this);
+}
+
+
+// this method is called from the destructor of the referenced page.
+// do all necessary action to forget the page. It is not necessary to call
+// RemovePageUser(), that is done from the destructor.
+void SdrPageObj::PageInDestruction(const SdrPage& rPage)
+{
+ if(mpShownPage && mpShownPage == &rPage)
+ {
+ // #i58769# Do not call ActionChanged() here, because that would
+ // lead to the construction of a view contact object for a page that
+ // is being destroyed.
+
+ mpShownPage = nullptr;
+ }
+}
+
+SdrPageObj::SdrPageObj(
+ SdrModel& rSdrModel,
+ SdrPage* pNewPage)
+: SdrObject(rSdrModel),
+ mpShownPage(pNewPage)
+{
+ if(mpShownPage)
+ {
+ mpShownPage->AddPageUser(*this);
+ }
+}
+
+SdrPageObj::SdrPageObj(SdrModel& rSdrModel, SdrPageObj const & rSource)
+: SdrObject(rSdrModel, rSource),
+ mpShownPage(nullptr)
+{
+ SetReferencedPage( rSource.GetReferencedPage());
+}
+
+SdrPageObj::SdrPageObj(
+ SdrModel& rSdrModel,
+ const tools::Rectangle& rRect,
+ SdrPage* pNewPage)
+: SdrObject(rSdrModel),
+ mpShownPage(pNewPage)
+{
+ if(mpShownPage)
+ {
+ mpShownPage->AddPageUser(*this);
+ }
+
+ m_aOutRect = rRect;
+}
+
+SdrPageObj::~SdrPageObj()
+{
+ if(mpShownPage)
+ {
+ mpShownPage->RemovePageUser(*this);
+ }
+}
+
+
+void SdrPageObj::SetReferencedPage(SdrPage* pNewPage)
+{
+ if(mpShownPage == pNewPage)
+ return;
+
+ if(mpShownPage)
+ {
+ mpShownPage->RemovePageUser(*this);
+ }
+
+ mpShownPage = pNewPage;
+
+ if(mpShownPage)
+ {
+ mpShownPage->AddPageUser(*this);
+ }
+
+ SetChanged();
+ BroadcastObjectChange();
+}
+
+// #i96598#
+void SdrPageObj::SetBoundRectDirty()
+{
+ // avoid resetting aOutRect which in case of this object is model data,
+ // not re-creatable view data
+}
+
+SdrObjKind SdrPageObj::GetObjIdentifier() const
+{
+ return SdrObjKind::Page;
+}
+
+void SdrPageObj::TakeObjInfo(SdrObjTransformInfoRec& rInfo) const
+{
+ rInfo.bRotateFreeAllowed=false;
+ rInfo.bRotate90Allowed =false;
+ rInfo.bMirrorFreeAllowed=false;
+ rInfo.bMirror45Allowed =false;
+ rInfo.bMirror90Allowed =false;
+ rInfo.bTransparenceAllowed = false;
+ rInfo.bShearAllowed =false;
+ rInfo.bEdgeRadiusAllowed=false;
+ rInfo.bNoOrthoDesired =false;
+ rInfo.bCanConvToPath =false;
+ rInfo.bCanConvToPoly =false;
+ rInfo.bCanConvToPathLineToArea=false;
+ rInfo.bCanConvToPolyLineToArea=false;
+}
+
+SdrPageObj* SdrPageObj::CloneSdrObject(SdrModel& rTargetModel) const
+{
+ return new SdrPageObj(rTargetModel, *this);
+}
+
+OUString SdrPageObj::TakeObjNameSingul() const
+{
+ OUString sName(SvxResId(STR_ObjNameSingulPAGE));
+
+ OUString aName(GetName());
+ if (!aName.isEmpty())
+ sName += " '" + aName + "'";
+
+ return sName;
+}
+
+OUString SdrPageObj::TakeObjNamePlural() const
+{
+ return SvxResId(STR_ObjNamePluralPAGE);
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/svdraw/svdopath.cxx b/svx/source/svdraw/svdopath.cxx
new file mode 100644
index 000000000..9f338301e
--- /dev/null
+++ b/svx/source/svdraw/svdopath.cxx
@@ -0,0 +1,2994 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * 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/unit_conversion.hxx>
+#include <tools/bigint.hxx>
+#include <tools/helpers.hxx>
+#include <svx/svdopath.hxx>
+#include <math.h>
+#include <svx/xpoly.hxx>
+#include <svx/svdtrans.hxx>
+#include <svx/svddrag.hxx>
+#include <svx/svdmodel.hxx>
+#include <svx/svdhdl.hxx>
+#include <svx/svdview.hxx>
+#include <svx/dialmgr.hxx>
+#include <svx/strings.hrc>
+
+#include <svx/polypolygoneditor.hxx>
+#include <sdr/contact/viewcontactofsdrpathobj.hxx>
+#include <basegfx/matrix/b2dhommatrix.hxx>
+#include <basegfx/point/b2dpoint.hxx>
+#include <basegfx/polygon/b2dpolypolygontools.hxx>
+#include <basegfx/range/b2drange.hxx>
+#include <basegfx/curve/b2dcubicbezier.hxx>
+#include <basegfx/polygon/b2dpolygontools.hxx>
+#include <sdr/attribute/sdrtextattribute.hxx>
+#include <sdr/primitive2d/sdrattributecreator.hxx>
+#include <basegfx/matrix/b2dhommatrixtools.hxx>
+#include <sdr/attribute/sdrformtextattribute.hxx>
+#include <vcl/ptrstyle.hxx>
+#include <memory>
+#include <sal/log.hxx>
+#include <osl/diagnose.h>
+
+using namespace sdr;
+
+static sal_uInt16 GetPrevPnt(sal_uInt16 nPnt, sal_uInt16 nPntMax, bool bClosed)
+{
+ if (nPnt>0) {
+ nPnt--;
+ } else {
+ nPnt=nPntMax;
+ if (bClosed) nPnt--;
+ }
+ return nPnt;
+}
+
+static sal_uInt16 GetNextPnt(sal_uInt16 nPnt, sal_uInt16 nPntMax, bool bClosed)
+{
+ nPnt++;
+ if (nPnt>nPntMax || (bClosed && nPnt>=nPntMax)) nPnt=0;
+ return nPnt;
+}
+
+namespace {
+
+struct ImpSdrPathDragData : public SdrDragStatUserData
+{
+ XPolygon aXP; // section of the original polygon
+ bool bValid; // FALSE = too few points
+ bool bClosed; // closed object?
+ sal_uInt16 nPoly; // number of the polygon in the PolyPolygon
+ sal_uInt16 nPnt; // number of point in the above polygon
+ sal_uInt16 nPointCount; // number of points of the polygon
+ bool bBegPnt; // dragged point is first point of a Polyline
+ bool bEndPnt; // dragged point is finishing point of a Polyline
+ sal_uInt16 nPrevPnt; // index of previous point
+ sal_uInt16 nNextPnt; // index of next point
+ bool bPrevIsBegPnt; // previous point is first point of a Polyline
+ bool bNextIsEndPnt; // next point is first point of a Polyline
+ sal_uInt16 nPrevPrevPnt; // index of point before previous point
+ sal_uInt16 nNextNextPnt; // index of point after next point
+ bool bControl; // point is a control point
+ bool bIsNextControl; // point is a control point after a support point
+ bool bPrevIsControl; // if nPnt is a support point: a control point comes before
+ bool bNextIsControl; // if nPnt is a support point: a control point comes after
+ sal_uInt16 nPrevPrevPnt0;
+ sal_uInt16 nPrevPnt0;
+ sal_uInt16 nPnt0;
+ sal_uInt16 nNextPnt0;
+ sal_uInt16 nNextNextPnt0;
+ bool bEliminate; // delete point? (is set by MovDrag)
+
+ bool mbMultiPointDrag;
+ const XPolyPolygon maOrig;
+ XPolyPolygon maMove;
+ std::vector<SdrHdl*> maHandles;
+
+public:
+ ImpSdrPathDragData(const SdrPathObj& rPO, const SdrHdl& rHdl, bool bMuPoDr, const SdrDragStat& rDrag);
+ void ResetPoly(const SdrPathObj& rPO);
+ bool IsMultiPointDrag() const { return mbMultiPointDrag; }
+};
+
+}
+
+ImpSdrPathDragData::ImpSdrPathDragData(const SdrPathObj& rPO, const SdrHdl& rHdl, bool bMuPoDr, const SdrDragStat& rDrag)
+ : aXP(5)
+ , bValid(false)
+ , bClosed(false)
+ , nPoly(0)
+ , nPnt(0)
+ , nPointCount(0)
+ , bBegPnt(false)
+ , bEndPnt(false)
+ , nPrevPnt(0)
+ , nNextPnt(0)
+ , bPrevIsBegPnt(false)
+ , bNextIsEndPnt(false)
+ , nPrevPrevPnt(0)
+ , nNextNextPnt(0)
+ , bControl(false)
+ , bIsNextControl(false)
+ , bPrevIsControl(false)
+ , bNextIsControl(false)
+ , nPrevPrevPnt0(0)
+ , nPrevPnt0(0)
+ , nPnt0(0)
+ , nNextPnt0(0)
+ , nNextNextPnt0(0)
+ , bEliminate(false)
+ , mbMultiPointDrag(bMuPoDr)
+ , maOrig(rPO.GetPathPoly())
+ , maHandles(0)
+{
+ if(mbMultiPointDrag)
+ {
+ const SdrMarkView& rMarkView = *rDrag.GetView();
+ const SdrHdlList& rHdlList = rMarkView.GetHdlList();
+ const size_t nHdlCount = rHdlList.GetHdlCount();
+ const SdrObject* pInteractionObject(nHdlCount && rHdlList.GetHdl(0) ? rHdlList.GetHdl(0)->GetObj() : nullptr);
+
+ for(size_t a = 0; a < nHdlCount; ++a)
+ {
+ SdrHdl* pTestHdl = rHdlList.GetHdl(a);
+
+ if(pTestHdl && pTestHdl->IsSelected() && pTestHdl->GetObj() == pInteractionObject)
+ {
+ maHandles.push_back(pTestHdl);
+ }
+ }
+
+ maMove = maOrig;
+ bValid = true;
+ }
+ else
+ {
+ sal_uInt16 nPntMax = 0; // maximum index
+ bValid=false;
+ bClosed=rPO.IsClosed(); // closed object?
+ nPoly=static_cast<sal_uInt16>(rHdl.GetPolyNum()); // number of the polygon in the PolyPolygon
+ nPnt=static_cast<sal_uInt16>(rHdl.GetPointNum()); // number of points in the above polygon
+ const XPolygon aTmpXP(rPO.GetPathPoly().getB2DPolygon(nPoly));
+ nPointCount=aTmpXP.GetPointCount(); // number of point of the polygon
+ if (nPointCount==0 || (bClosed && nPointCount==1)) return; // minimum of 1 points for Lines, minimum of 2 points for Polygon
+ nPntMax=nPointCount-1; // maximum index
+ bBegPnt=!bClosed && nPnt==0; // dragged point is first point of a Polyline
+ bEndPnt=!bClosed && nPnt==nPntMax; // dragged point is finishing point of a Polyline
+ if (bClosed && nPointCount<=3) { // if polygon is only a line
+ bBegPnt=(nPointCount<3) || nPnt==0;
+ bEndPnt=(nPointCount<3) || nPnt==nPntMax-1;
+ }
+ nPrevPnt=nPnt; // index of previous point
+ nNextPnt=nPnt; // index of next point
+ if (!bBegPnt) nPrevPnt=GetPrevPnt(nPnt,nPntMax,bClosed);
+ if (!bEndPnt) nNextPnt=GetNextPnt(nPnt,nPntMax,bClosed);
+ bPrevIsBegPnt=bBegPnt || (!bClosed && nPrevPnt==0);
+ bNextIsEndPnt=bEndPnt || (!bClosed && nNextPnt==nPntMax);
+ nPrevPrevPnt=nPnt; // index of point before previous point
+ nNextNextPnt=nPnt; // index of point after next point
+ if (!bPrevIsBegPnt) nPrevPrevPnt=GetPrevPnt(nPrevPnt,nPntMax,bClosed);
+ if (!bNextIsEndPnt) nNextNextPnt=GetNextPnt(nNextPnt,nPntMax,bClosed);
+ bControl=rHdl.IsPlusHdl(); // point is a control point
+ bIsNextControl=false; // point is a control point after a support point
+ bPrevIsControl=false; // if nPnt is a support point: a control point comes before
+ bNextIsControl=false; // if nPnt is a support point: a control point comes after
+ if (bControl) {
+ bIsNextControl=!aTmpXP.IsControl(nPrevPnt);
+ } else {
+ bPrevIsControl=!bBegPnt && !bPrevIsBegPnt && aTmpXP.GetFlags(nPrevPnt)==PolyFlags::Control;
+ bNextIsControl=!bEndPnt && !bNextIsEndPnt && aTmpXP.GetFlags(nNextPnt)==PolyFlags::Control;
+ }
+ nPrevPrevPnt0=nPrevPrevPnt;
+ nPrevPnt0 =nPrevPnt;
+ nPnt0 =nPnt;
+ nNextPnt0 =nNextPnt;
+ nNextNextPnt0=nNextNextPnt;
+ nPrevPrevPnt=0;
+ nPrevPnt=1;
+ nPnt=2;
+ nNextPnt=3;
+ nNextNextPnt=4;
+ bEliminate=false;
+ ResetPoly(rPO);
+ bValid=true;
+ }
+}
+
+void ImpSdrPathDragData::ResetPoly(const SdrPathObj& rPO)
+{
+ const XPolygon aTmpXP(rPO.GetPathPoly().getB2DPolygon(nPoly));
+ aXP[0]=aTmpXP[nPrevPrevPnt0]; aXP.SetFlags(0,aTmpXP.GetFlags(nPrevPrevPnt0));
+ aXP[1]=aTmpXP[nPrevPnt0]; aXP.SetFlags(1,aTmpXP.GetFlags(nPrevPnt0));
+ aXP[2]=aTmpXP[nPnt0]; aXP.SetFlags(2,aTmpXP.GetFlags(nPnt0));
+ aXP[3]=aTmpXP[nNextPnt0]; aXP.SetFlags(3,aTmpXP.GetFlags(nNextPnt0));
+ aXP[4]=aTmpXP[nNextNextPnt0]; aXP.SetFlags(4,aTmpXP.GetFlags(nNextNextPnt0));
+}
+
+namespace {
+
+struct ImpPathCreateUser : public SdrDragStatUserData
+{
+ Point aBezControl0;
+ Point aBezStart;
+ Point aBezCtrl1;
+ Point aBezCtrl2;
+ Point aBezEnd;
+ Point aCircStart;
+ Point aCircEnd;
+ Point aCircCenter;
+ Point aLineStart;
+ Point aLineEnd;
+ Point aRectP1;
+ Point aRectP2;
+ Point aRectP3;
+ tools::Long nCircRadius;
+ Degree100 nCircStAngle;
+ Degree100 nCircRelAngle;
+ bool bBezier;
+ bool bBezHasCtrl0;
+ bool bCircle;
+ bool bAngleSnap;
+ bool bLine;
+ bool bLine90;
+ bool bRect;
+ bool bMixedCreate;
+ sal_uInt16 nBezierStartPoint;
+ SdrObjKind eStartKind;
+ SdrObjKind eCurrentKind;
+
+public:
+ ImpPathCreateUser(): nCircRadius(0),nCircStAngle(0),nCircRelAngle(0),
+ bBezier(false),bBezHasCtrl0(false),bCircle(false),bAngleSnap(false),bLine(false),bLine90(false),bRect(false),
+ bMixedCreate(false),nBezierStartPoint(0),eStartKind(SdrObjKind::NONE),eCurrentKind(SdrObjKind::NONE) { }
+
+ void ResetFormFlags() { bBezier=false; bCircle=false; bLine=false; bRect=false; }
+ bool IsFormFlag() const { return bBezier || bCircle || bLine || bRect; }
+ XPolygon GetFormPoly() const;
+ void CalcBezier(const Point& rP1, const Point& rP2, const Point& rDir, bool bMouseDown);
+ XPolygon GetBezierPoly() const;
+ void CalcCircle(const Point& rP1, const Point& rP2, const Point& rDir, SdrView const * pView);
+ XPolygon GetCirclePoly() const;
+ void CalcLine(const Point& rP1, const Point& rP2, const Point& rDir, SdrView const * pView);
+ static Point CalcLine(const Point& rCsr, tools::Long nDirX, tools::Long nDirY, SdrView const * pView);
+ XPolygon GetLinePoly() const;
+ void CalcRect(const Point& rP1, const Point& rP2, const Point& rDir, SdrView const * pView);
+ XPolygon GetRectPoly() const;
+};
+
+}
+
+XPolygon ImpPathCreateUser::GetFormPoly() const
+{
+ if (bBezier) return GetBezierPoly();
+ if (bCircle) return GetCirclePoly();
+ if (bLine) return GetLinePoly();
+ if (bRect) return GetRectPoly();
+ return XPolygon();
+}
+
+void ImpPathCreateUser::CalcBezier(const Point& rP1, const Point& rP2, const Point& rDir, bool bMouseDown)
+{
+ aBezStart=rP1;
+ aBezCtrl1=rP1+rDir;
+ aBezCtrl2=rP2;
+
+ // #i21479#
+ // Also copy the end point when no end point is set yet
+ if (!bMouseDown || (0 == aBezEnd.X() && 0 == aBezEnd.Y())) aBezEnd=rP2;
+
+ bBezier=true;
+}
+
+XPolygon ImpPathCreateUser::GetBezierPoly() const
+{
+ XPolygon aXP(4);
+ aXP[0]=aBezStart; aXP.SetFlags(0,PolyFlags::Smooth);
+ aXP[1]=aBezCtrl1; aXP.SetFlags(1,PolyFlags::Control);
+ aXP[2]=aBezCtrl2; aXP.SetFlags(2,PolyFlags::Control);
+ aXP[3]=aBezEnd;
+ return aXP;
+}
+
+void ImpPathCreateUser::CalcCircle(const Point& rP1, const Point& rP2, const Point& rDir, SdrView const * pView)
+{
+ Degree100 nTangAngle=GetAngle(rDir);
+ aCircStart=rP1;
+ aCircEnd=rP2;
+ aCircCenter=rP1;
+ tools::Long dx=rP2.X()-rP1.X();
+ tools::Long dy=rP2.Y()-rP1.Y();
+ Degree100 dAngle=GetAngle(Point(dx,dy))-nTangAngle;
+ dAngle=NormAngle36000(dAngle);
+ Degree100 nTmpAngle=NormAngle36000(9000_deg100-dAngle);
+ bool bRet=nTmpAngle!=9000_deg100 && nTmpAngle!=27000_deg100;
+ tools::Long nRad=0;
+ if (bRet) {
+ double cs = cos(toRadians(nTmpAngle));
+ double nR=static_cast<double>(GetLen(Point(dx,dy)))/cs/2;
+ nRad=std::abs(FRound(nR));
+ }
+ if (dAngle<18000_deg100) {
+ nCircStAngle=NormAngle36000(nTangAngle-9000_deg100);
+ nCircRelAngle=NormAngle36000(2_deg100*dAngle);
+ aCircCenter.AdjustX(FRound(nRad * cos(toRadians(nTangAngle + 9000_deg100))));
+ aCircCenter.AdjustY(-(FRound(nRad * sin(toRadians(nTangAngle + 9000_deg100)))));
+ } else {
+ nCircStAngle=NormAngle36000(nTangAngle+9000_deg100);
+ nCircRelAngle=-NormAngle36000(36000_deg100-2_deg100*dAngle);
+ aCircCenter.AdjustX(FRound(nRad * cos(toRadians(nTangAngle - 9000_deg100))));
+ aCircCenter.AdjustY(-(FRound(nRad * sin(toRadians(nTangAngle - 9000_deg100)))));
+ }
+ bAngleSnap=pView!=nullptr && pView->IsAngleSnapEnabled();
+ if (bAngleSnap) {
+ Degree100 nSA=pView->GetSnapAngle();
+ if (nSA) { // angle snapping
+ bool bNeg=nCircRelAngle<0_deg100;
+ if (bNeg) nCircRelAngle=-nCircRelAngle;
+ nCircRelAngle+=nSA/2_deg100;
+ nCircRelAngle/=nSA;
+ nCircRelAngle*=nSA;
+ nCircRelAngle=NormAngle36000(nCircRelAngle);
+ if (bNeg) nCircRelAngle=-nCircRelAngle;
+ }
+ }
+ nCircRadius=nRad;
+ if (nRad==0 || abs(nCircRelAngle).get()<5) bRet=false;
+ bCircle=bRet;
+}
+
+XPolygon ImpPathCreateUser::GetCirclePoly() const
+{
+ if (nCircRelAngle>=0_deg100) {
+ XPolygon aXP(aCircCenter,nCircRadius,nCircRadius,
+ nCircStAngle, nCircStAngle+nCircRelAngle,false);
+ aXP[0]=aCircStart; aXP.SetFlags(0,PolyFlags::Smooth);
+ if (!bAngleSnap) aXP[aXP.GetPointCount()-1]=aCircEnd;
+ return aXP;
+ } else {
+ XPolygon aXP(aCircCenter,nCircRadius,nCircRadius,
+ NormAngle36000(nCircStAngle+nCircRelAngle), nCircStAngle,false);
+ sal_uInt16 nCount=aXP.GetPointCount();
+ for (sal_uInt16 nNum=nCount/2; nNum>0;) {
+ nNum--; // reverse XPoly's order of points
+ sal_uInt16 n2=nCount-nNum-1;
+ Point aPt(aXP[nNum]);
+ aXP[nNum]=aXP[n2];
+ aXP[n2]=aPt;
+ }
+ aXP[0]=aCircStart; aXP.SetFlags(0,PolyFlags::Smooth);
+ if (!bAngleSnap) aXP[aXP.GetPointCount()-1]=aCircEnd;
+ return aXP;
+ }
+}
+
+Point ImpPathCreateUser::CalcLine(const Point& aCsr, tools::Long nDirX, tools::Long nDirY, SdrView const * pView)
+{
+ tools::Long x=aCsr.X();
+ tools::Long y=aCsr.Y();
+ bool bHLin=nDirY==0;
+ bool bVLin=nDirX==0;
+ if (bHLin) y=0;
+ else if (bVLin) x=0;
+ else {
+ tools::Long x1=BigMulDiv(y,nDirX,nDirY);
+ tools::Long y1=y;
+ tools::Long x2=x;
+ tools::Long y2=BigMulDiv(x,nDirY,nDirX);
+ tools::Long l1=std::abs(x1)+std::abs(y1);
+ tools::Long l2=std::abs(x2)+std::abs(y2);
+ if ((l1<=l2) != (pView!=nullptr && pView->IsBigOrtho())) {
+ x=x1; y=y1;
+ } else {
+ x=x2; y=y2;
+ }
+ }
+ return Point(x,y);
+}
+
+void ImpPathCreateUser::CalcLine(const Point& rP1, const Point& rP2, const Point& rDir, SdrView const * pView)
+{
+ aLineStart=rP1;
+ aLineEnd=rP2;
+ bLine90=false;
+ if (rP1==rP2 || (rDir.X()==0 && rDir.Y()==0)) { bLine=false; return; }
+ Point aTmpPt(rP2-rP1);
+ tools::Long nDirX=rDir.X();
+ tools::Long nDirY=rDir.Y();
+ Point aP1(CalcLine(aTmpPt, nDirX, nDirY,pView)); aP1-=aTmpPt; tools::Long nQ1=std::abs(aP1.X())+std::abs(aP1.Y());
+ Point aP2(CalcLine(aTmpPt, nDirY,-nDirX,pView)); aP2-=aTmpPt; tools::Long nQ2=std::abs(aP2.X())+std::abs(aP2.Y());
+ if (pView!=nullptr && pView->IsOrtho()) nQ1=0; // Ortho turns off at right angle
+ bLine90=nQ1>2*nQ2;
+ if (!bLine90) { // smooth transition
+ aLineEnd+=aP1;
+ } else { // rectangular transition
+ aLineEnd+=aP2;
+ }
+ bLine=true;
+}
+
+XPolygon ImpPathCreateUser::GetLinePoly() const
+{
+ XPolygon aXP(2);
+ aXP[0]=aLineStart; if (!bLine90) aXP.SetFlags(0,PolyFlags::Smooth);
+ aXP[1]=aLineEnd;
+ return aXP;
+}
+
+void ImpPathCreateUser::CalcRect(const Point& rP1, const Point& rP2, const Point& rDir, SdrView const * pView)
+{
+ aRectP1=rP1;
+ aRectP2=rP1;
+ aRectP3=rP2;
+ if (rP1==rP2 || (rDir.X()==0 && rDir.Y()==0)) { bRect=false; return; }
+ Point aTmpPt(rP2-rP1);
+ tools::Long nDirX=rDir.X();
+ tools::Long nDirY=rDir.Y();
+ tools::Long x=aTmpPt.X();
+ tools::Long y=aTmpPt.Y();
+ bool bHLin=nDirY==0;
+ bool bVLin=nDirX==0;
+ if (bHLin) y=0;
+ else if (bVLin) x=0;
+ else {
+ y=BigMulDiv(x,nDirY,nDirX);
+ tools::Long nHypLen=aTmpPt.Y()-y;
+ Degree100 nTangAngle=-GetAngle(rDir);
+ // sin=g/h, g=h*sin
+ double a = toRadians(nTangAngle);
+ double sn=sin(a);
+ double cs=cos(a);
+ double nGKathLen=nHypLen*sn;
+ y+=FRound(nGKathLen*sn);
+ x+=FRound(nGKathLen*cs);
+ }
+ aRectP2.AdjustX(x );
+ aRectP2.AdjustY(y );
+ if (pView!=nullptr && pView->IsOrtho()) {
+ tools::Long dx1=aRectP2.X()-aRectP1.X(); tools::Long dx1a=std::abs(dx1);
+ tools::Long dy1=aRectP2.Y()-aRectP1.Y(); tools::Long dy1a=std::abs(dy1);
+ tools::Long dx2=aRectP3.X()-aRectP2.X(); tools::Long dx2a=std::abs(dx2);
+ tools::Long dy2=aRectP3.Y()-aRectP2.Y(); tools::Long dy2a=std::abs(dy2);
+ bool b1MoreThan2=dx1a+dy1a>dx2a+dy2a;
+ if (b1MoreThan2 != pView->IsBigOrtho()) {
+ tools::Long xtemp=dy2a-dx1a; if (dx1<0) xtemp=-xtemp;
+ tools::Long ytemp=dx2a-dy1a; if (dy1<0) ytemp=-ytemp;
+ aRectP2.AdjustX(xtemp );
+ aRectP2.AdjustY(ytemp );
+ aRectP3.AdjustX(xtemp );
+ aRectP3.AdjustY(ytemp );
+ } else {
+ tools::Long xtemp=dy1a-dx2a; if (dx2<0) xtemp=-xtemp;
+ tools::Long ytemp=dx1a-dy2a; if (dy2<0) ytemp=-ytemp;
+ aRectP3.AdjustX(xtemp );
+ aRectP3.AdjustY(ytemp );
+ }
+ }
+ bRect=true;
+}
+
+XPolygon ImpPathCreateUser::GetRectPoly() const
+{
+ XPolygon aXP(3);
+ aXP[0]=aRectP1; aXP.SetFlags(0,PolyFlags::Smooth);
+ aXP[1]=aRectP2;
+ if (aRectP3!=aRectP2) aXP[2]=aRectP3;
+ return aXP;
+}
+
+class ImpPathForDragAndCreate
+{
+ SdrPathObj& mrSdrPathObject;
+ XPolyPolygon aPathPolygon;
+ SdrObjKind meObjectKind;
+ std::unique_ptr<ImpSdrPathDragData>
+ mpSdrPathDragData;
+ bool mbCreating;
+
+public:
+ explicit ImpPathForDragAndCreate(SdrPathObj& rSdrPathObject);
+
+ // drag stuff
+ bool beginPathDrag( SdrDragStat const & rDrag ) const;
+ bool movePathDrag( SdrDragStat& rDrag ) const;
+ bool endPathDrag( SdrDragStat const & rDrag );
+ OUString getSpecialDragComment(const SdrDragStat& rDrag) const;
+ basegfx::B2DPolyPolygon getSpecialDragPoly(const SdrDragStat& rDrag) const;
+
+ // create stuff
+ void BegCreate(SdrDragStat& rStat);
+ bool MovCreate(SdrDragStat& rStat);
+ bool EndCreate(SdrDragStat& rStat, SdrCreateCmd eCmd);
+ bool BckCreate(SdrDragStat const & rStat);
+ void BrkCreate(SdrDragStat& rStat);
+ PointerStyle GetCreatePointer() const;
+
+ // helping stuff
+ static bool IsClosed(SdrObjKind eKind) { return eKind==SdrObjKind::Polygon || eKind==SdrObjKind::PathPoly || eKind==SdrObjKind::PathFill || eKind==SdrObjKind::FreehandFill || eKind==SdrObjKind::SplineFill; }
+ static bool IsFreeHand(SdrObjKind eKind) { return eKind==SdrObjKind::FreehandLine || eKind==SdrObjKind::FreehandFill; }
+ static bool IsBezier(SdrObjKind eKind) { return eKind==SdrObjKind::PathLine || eKind==SdrObjKind::PathFill; }
+ bool IsCreating() const { return mbCreating; }
+
+ // get the polygon
+ basegfx::B2DPolyPolygon TakeObjectPolyPolygon(const SdrDragStat& rDrag) const;
+ static basegfx::B2DPolyPolygon TakeDragPolyPolygon(const SdrDragStat& rDrag);
+ basegfx::B2DPolyPolygon getModifiedPolyPolygon() const { return aPathPolygon.getB2DPolyPolygon(); }
+};
+
+ImpPathForDragAndCreate::ImpPathForDragAndCreate(SdrPathObj& rSdrPathObject)
+: mrSdrPathObject(rSdrPathObject),
+ aPathPolygon(rSdrPathObject.GetPathPoly()),
+ meObjectKind(mrSdrPathObject.meKind),
+ mbCreating(false)
+{
+}
+
+bool ImpPathForDragAndCreate::beginPathDrag( SdrDragStat const & rDrag ) const
+{
+ const SdrHdl* pHdl=rDrag.GetHdl();
+ if(!pHdl)
+ return false;
+
+ bool bMultiPointDrag(true);
+
+ if(aPathPolygon[static_cast<sal_uInt16>(pHdl->GetPolyNum())].IsControl(static_cast<sal_uInt16>(pHdl->GetPointNum())))
+ bMultiPointDrag = false;
+
+ if(bMultiPointDrag)
+ {
+ const SdrMarkView& rMarkView = *rDrag.GetView();
+ const SdrHdlList& rHdlList = rMarkView.GetHdlList();
+ const size_t nHdlCount = rHdlList.GetHdlCount();
+ const SdrObject* pInteractionObject(nHdlCount && rHdlList.GetHdl(0) ? rHdlList.GetHdl(0)->GetObj() : nullptr);
+ sal_uInt32 nSelectedPoints(0);
+
+ for(size_t a = 0; a < nHdlCount; ++a)
+ {
+ SdrHdl* pTestHdl = rHdlList.GetHdl(a);
+
+ if(pTestHdl && pTestHdl->IsSelected() && pTestHdl->GetObj() == pInteractionObject)
+ {
+ nSelectedPoints++;
+ }
+ }
+
+ if(nSelectedPoints <= 1)
+ bMultiPointDrag = false;
+ }
+
+ const_cast<ImpPathForDragAndCreate*>(this)->mpSdrPathDragData.reset( new ImpSdrPathDragData(mrSdrPathObject,*pHdl,bMultiPointDrag,rDrag) );
+
+ if(!mpSdrPathDragData || !mpSdrPathDragData->bValid)
+ {
+ OSL_FAIL("ImpPathForDragAndCreate::BegDrag(): ImpSdrPathDragData is invalid.");
+ const_cast<ImpPathForDragAndCreate*>(this)->mpSdrPathDragData.reset();
+ return false;
+ }
+
+ return true;
+}
+
+bool ImpPathForDragAndCreate::movePathDrag( SdrDragStat& rDrag ) const
+{
+ if(!mpSdrPathDragData || !mpSdrPathDragData->bValid)
+ {
+ OSL_FAIL("ImpPathForDragAndCreate::MovDrag(): ImpSdrPathDragData is invalid.");
+ return false;
+ }
+
+ if(mpSdrPathDragData->IsMultiPointDrag())
+ {
+ Point aDelta(rDrag.GetNow() - rDrag.GetStart());
+
+ if(aDelta.X() || aDelta.Y())
+ {
+ for(SdrHdl* pHandle : mpSdrPathDragData->maHandles)
+ {
+ const sal_uInt16 nPolyIndex(static_cast<sal_uInt16>(pHandle->GetPolyNum()));
+ const sal_uInt16 nPointIndex(static_cast<sal_uInt16>(pHandle->GetPointNum()));
+ const XPolygon& rOrig = mpSdrPathDragData->maOrig[nPolyIndex];
+ XPolygon& rMove = mpSdrPathDragData->maMove[nPolyIndex];
+ const sal_uInt16 nPointCount(rOrig.GetPointCount());
+ bool bClosed(rOrig[0] == rOrig[nPointCount-1]);
+
+ // move point itself
+ rMove[nPointIndex] = rOrig[nPointIndex] + aDelta;
+
+ // when point is first and poly closed, move close point, too.
+ if(nPointCount > 0 && !nPointIndex && bClosed)
+ {
+ rMove[nPointCount - 1] = rOrig[nPointCount - 1] + aDelta;
+
+ // when moving the last point it may be necessary to move the
+ // control point in front of this one, too.
+ if(nPointCount > 1 && rOrig.IsControl(nPointCount - 2))
+ rMove[nPointCount - 2] = rOrig[nPointCount - 2] + aDelta;
+ }
+
+ // is a control point before this?
+ if(nPointIndex > 0 && rOrig.IsControl(nPointIndex - 1))
+ {
+ // Yes, move it, too
+ rMove[nPointIndex - 1] = rOrig[nPointIndex - 1] + aDelta;
+ }
+
+ // is a control point after this?
+ if(nPointIndex + 1 < nPointCount && rOrig.IsControl(nPointIndex + 1))
+ {
+ // Yes, move it, too
+ rMove[nPointIndex + 1] = rOrig[nPointIndex + 1] + aDelta;
+ }
+ }
+ }
+ }
+ else
+ {
+ mpSdrPathDragData->ResetPoly(mrSdrPathObject);
+
+ // copy certain data locally to use less code and have faster access times
+ bool bClosed =mpSdrPathDragData->bClosed ; // closed object?
+ sal_uInt16 nPnt =mpSdrPathDragData->nPnt ; // number of point in the above polygon
+ bool bBegPnt =mpSdrPathDragData->bBegPnt ; // dragged point is first point of a Polyline
+ bool bEndPnt =mpSdrPathDragData->bEndPnt ; // dragged point is last point of a Polyline
+ sal_uInt16 nPrevPnt =mpSdrPathDragData->nPrevPnt ; // index of previous point
+ sal_uInt16 nNextPnt =mpSdrPathDragData->nNextPnt ; // index of next point
+ bool bPrevIsBegPnt =mpSdrPathDragData->bPrevIsBegPnt ; // previous point is first point of a Polyline
+ bool bNextIsEndPnt =mpSdrPathDragData->bNextIsEndPnt ; // next point is last point of a Polyline
+ sal_uInt16 nPrevPrevPnt =mpSdrPathDragData->nPrevPrevPnt ; // index of the point before the previous point
+ sal_uInt16 nNextNextPnt =mpSdrPathDragData->nNextNextPnt ; // index if the point after the next point
+ bool bControl =mpSdrPathDragData->bControl ; // point is a control point
+ bool bIsNextControl =mpSdrPathDragData->bIsNextControl; // point is a control point after a support point
+ bool bPrevIsControl =mpSdrPathDragData->bPrevIsControl; // if nPnt is a support point: there's a control point before
+ bool bNextIsControl =mpSdrPathDragData->bNextIsControl; // if nPnt is a support point: there's a control point after
+
+ // Ortho for lines/polygons: keep angle
+ if (!bControl && rDrag.GetView()!=nullptr && rDrag.GetView()->IsOrtho()) {
+ bool bBigOrtho=rDrag.GetView()->IsBigOrtho();
+ Point aPos(rDrag.GetNow()); // current position
+ Point aPnt(mpSdrPathDragData->aXP[nPnt]); // the dragged point
+ sal_uInt16 nPnt1=0xFFFF,nPnt2=0xFFFF; // its neighboring points
+ Point aNewPos1,aNewPos2; // new alternative for aPos
+ bool bPnt1 = false, bPnt2 = false; // are these valid alternatives?
+ if (!bClosed && mpSdrPathDragData->nPointCount>=2) { // minimum of 2 points for lines
+ if (!bBegPnt) nPnt1=nPrevPnt;
+ if (!bEndPnt) nPnt2=nNextPnt;
+ }
+ if (bClosed && mpSdrPathDragData->nPointCount>=3) { // minimum of 3 points for polygon
+ nPnt1=nPrevPnt;
+ nPnt2=nNextPnt;
+ }
+ if (nPnt1!=0xFFFF && !bPrevIsControl) {
+ Point aPnt1=mpSdrPathDragData->aXP[nPnt1];
+ tools::Long ndx0=aPnt.X()-aPnt1.X();
+ tools::Long ndy0=aPnt.Y()-aPnt1.Y();
+ bool bHLin=ndy0==0;
+ bool bVLin=ndx0==0;
+ if (!bHLin || !bVLin) {
+ tools::Long ndx=aPos.X()-aPnt1.X();
+ tools::Long ndy=aPos.Y()-aPnt1.Y();
+ bPnt1=true;
+ double nXFact=0; if (!bVLin) nXFact=static_cast<double>(ndx)/static_cast<double>(ndx0);
+ double nYFact=0; if (!bHLin) nYFact=static_cast<double>(ndy)/static_cast<double>(ndy0);
+ bool bHor=bHLin || (!bVLin && (nXFact>nYFact) ==bBigOrtho);
+ bool bVer=bVLin || (!bHLin && (nXFact<=nYFact)==bBigOrtho);
+ if (bHor) ndy=tools::Long(ndy0*nXFact);
+ if (bVer) ndx=tools::Long(ndx0*nYFact);
+ aNewPos1=aPnt1;
+ aNewPos1.AdjustX(ndx );
+ aNewPos1.AdjustY(ndy );
+ }
+ }
+ if (nPnt2!=0xFFFF && !bNextIsControl) {
+ Point aPnt2=mpSdrPathDragData->aXP[nPnt2];
+ tools::Long ndx0=aPnt.X()-aPnt2.X();
+ tools::Long ndy0=aPnt.Y()-aPnt2.Y();
+ bool bHLin=ndy0==0;
+ bool bVLin=ndx0==0;
+ if (!bHLin || !bVLin) {
+ tools::Long ndx=aPos.X()-aPnt2.X();
+ tools::Long ndy=aPos.Y()-aPnt2.Y();
+ bPnt2=true;
+ double nXFact=0; if (!bVLin) nXFact=static_cast<double>(ndx)/static_cast<double>(ndx0);
+ double nYFact=0; if (!bHLin) nYFact=static_cast<double>(ndy)/static_cast<double>(ndy0);
+ bool bHor=bHLin || (!bVLin && (nXFact>nYFact) ==bBigOrtho);
+ bool bVer=bVLin || (!bHLin && (nXFact<=nYFact)==bBigOrtho);
+ if (bHor) ndy=tools::Long(ndy0*nXFact);
+ if (bVer) ndx=tools::Long(ndx0*nYFact);
+ aNewPos2=aPnt2;
+ aNewPos2.AdjustX(ndx );
+ aNewPos2.AdjustY(ndy );
+ }
+ }
+ if (bPnt1 && bPnt2) { // both alternatives exist (and compete)
+ BigInt nX1(aNewPos1.X()-aPos.X()); nX1*=nX1;
+ BigInt nY1(aNewPos1.Y()-aPos.Y()); nY1*=nY1;
+ BigInt nX2(aNewPos2.X()-aPos.X()); nX2*=nX2;
+ BigInt nY2(aNewPos2.Y()-aPos.Y()); nY2*=nY2;
+ nX1+=nY1; // correction distance to square
+ nX2+=nY2; // correction distance to square
+ // let the alternative that allows fewer correction win
+ if (nX1<nX2) bPnt2=false; else bPnt1=false;
+ }
+ if (bPnt1) rDrag.SetNow(aNewPos1);
+ if (bPnt2) rDrag.SetNow(aNewPos2);
+ }
+ rDrag.SetActionRect(tools::Rectangle(rDrag.GetNow(),rDrag.GetNow()));
+
+ // specially for IBM: Eliminate points if both adjoining lines form near 180 degrees angle anyway
+ if (!bControl && rDrag.GetView()!=nullptr && rDrag.GetView()->IsEliminatePolyPoints() &&
+ !bBegPnt && !bEndPnt && !bPrevIsControl && !bNextIsControl)
+ {
+ Point aPt(mpSdrPathDragData->aXP[nNextPnt]);
+ aPt-=rDrag.GetNow();
+ Degree100 nAngle1=GetAngle(aPt);
+ aPt=rDrag.GetNow();
+ aPt-=mpSdrPathDragData->aXP[nPrevPnt];
+ Degree100 nAngle2=GetAngle(aPt);
+ Degree100 nDiff=nAngle1-nAngle2;
+ nDiff=abs(nDiff);
+ mpSdrPathDragData->bEliminate=nDiff<=rDrag.GetView()->GetEliminatePolyPointLimitAngle();
+ if (mpSdrPathDragData->bEliminate) { // adapt position, Smooth is true for the ends
+ aPt=mpSdrPathDragData->aXP[nNextPnt];
+ aPt+=mpSdrPathDragData->aXP[nPrevPnt];
+ aPt/=2;
+ rDrag.SetNow(aPt);
+ }
+ }
+
+ // we dragged by this distance
+ Point aDiff(rDrag.GetNow()); aDiff-=mpSdrPathDragData->aXP[nPnt];
+
+ /* There are 8 possible cases:
+ X 1. A control point neither on the left nor on the right.
+ o--X--o 2. There are control points on the left and the right, we are dragging a support point.
+ o--X 3. There is a control point on the left, we are dragging a support point.
+ X--o 4. There is a control point on the right, we are dragging a support point.
+ x--O--o 5. There are control points on the left and the right, we are dragging the left one.
+ x--O 6. There is a control point on the left, we are dragging it.
+ o--O--x 7. There are control points on the left and the right, we are dragging the right one.
+ O--x 8. There is a control point on the right, we are dragging it.
+ Note: modifying a line (not a curve!) might create a curve on the other end of the line
+ if Smooth is set there (with control points aligned to line).
+ */
+
+ mpSdrPathDragData->aXP[nPnt]+=aDiff;
+
+ // now check symmetric plus handles
+ if (bControl) { // cases 5,6,7,8
+ sal_uInt16 nSt; // the associated support point
+ sal_uInt16 nFix; // the opposing control point
+ if (bIsNextControl) { // if the next one is a control point, the on before has to be a support point
+ nSt=nPrevPnt;
+ nFix=nPrevPrevPnt;
+ } else {
+ nSt=nNextPnt;
+ nFix=nNextNextPnt;
+ }
+ if (mpSdrPathDragData->aXP.IsSmooth(nSt)) {
+ mpSdrPathDragData->aXP.CalcSmoothJoin(nSt,nPnt,nFix);
+ }
+ }
+
+ if (!bControl) { // Cases 1,2,3,4. In case 1, nothing happens; in cases 3 and 4, there is more following below.
+ // move both control points
+ if (bPrevIsControl) mpSdrPathDragData->aXP[nPrevPnt]+=aDiff;
+ if (bNextIsControl) mpSdrPathDragData->aXP[nNextPnt]+=aDiff;
+ // align control point to line, if appropriate
+ if (mpSdrPathDragData->aXP.IsSmooth(nPnt)) {
+ if (bPrevIsControl && !bNextIsControl && !bEndPnt) { // case 3
+ mpSdrPathDragData->aXP.CalcSmoothJoin(nPnt,nNextPnt,nPrevPnt);
+ }
+ if (bNextIsControl && !bPrevIsControl && !bBegPnt) { // case 4
+ mpSdrPathDragData->aXP.CalcSmoothJoin(nPnt,nPrevPnt,nNextPnt);
+ }
+ }
+ // Now check the other ends of the line (nPnt+-1). If there is a
+ // curve (IsControl(nPnt+-2)) with SmoothJoin (nPnt+-1), the
+ // associated control point (nPnt+-2) has to be adapted.
+ if (!bBegPnt && !bPrevIsControl && !bPrevIsBegPnt && mpSdrPathDragData->aXP.IsSmooth(nPrevPnt)) {
+ if (mpSdrPathDragData->aXP.IsControl(nPrevPrevPnt)) {
+ mpSdrPathDragData->aXP.CalcSmoothJoin(nPrevPnt,nPnt,nPrevPrevPnt);
+ }
+ }
+ if (!bEndPnt && !bNextIsControl && !bNextIsEndPnt && mpSdrPathDragData->aXP.IsSmooth(nNextPnt)) {
+ if (mpSdrPathDragData->aXP.IsControl(nNextNextPnt)) {
+ mpSdrPathDragData->aXP.CalcSmoothJoin(nNextPnt,nPnt,nNextNextPnt);
+ }
+ }
+ }
+ }
+
+ return true;
+}
+
+bool ImpPathForDragAndCreate::endPathDrag(SdrDragStat const & rDrag)
+{
+ Point aLinePt1;
+ Point aLinePt2;
+ bool bLineGlueMirror(SdrObjKind::Line == meObjectKind);
+ if (bLineGlueMirror) {
+ XPolygon& rXP=aPathPolygon[0];
+ aLinePt1=rXP[0];
+ aLinePt2=rXP[1];
+ }
+
+ if(!mpSdrPathDragData || !mpSdrPathDragData->bValid)
+ {
+ OSL_FAIL("ImpPathForDragAndCreate::MovDrag(): ImpSdrPathDragData is invalid.");
+ return false;
+ }
+
+ if(mpSdrPathDragData->IsMultiPointDrag())
+ {
+ aPathPolygon = mpSdrPathDragData->maMove;
+ }
+ else
+ {
+ const SdrHdl* pHdl=rDrag.GetHdl();
+
+ // reference the polygon
+ XPolygon& rXP=aPathPolygon[static_cast<sal_uInt16>(pHdl->GetPolyNum())];
+
+ // the 5 points that might have changed
+ if (!mpSdrPathDragData->bPrevIsBegPnt) rXP[mpSdrPathDragData->nPrevPrevPnt0]=mpSdrPathDragData->aXP[mpSdrPathDragData->nPrevPrevPnt];
+ if (!mpSdrPathDragData->bNextIsEndPnt) rXP[mpSdrPathDragData->nNextNextPnt0]=mpSdrPathDragData->aXP[mpSdrPathDragData->nNextNextPnt];
+ if (!mpSdrPathDragData->bBegPnt) rXP[mpSdrPathDragData->nPrevPnt0] =mpSdrPathDragData->aXP[mpSdrPathDragData->nPrevPnt];
+ if (!mpSdrPathDragData->bEndPnt) rXP[mpSdrPathDragData->nNextPnt0] =mpSdrPathDragData->aXP[mpSdrPathDragData->nNextPnt];
+ rXP[mpSdrPathDragData->nPnt0] =mpSdrPathDragData->aXP[mpSdrPathDragData->nPnt];
+
+ // for closed objects: last point has to be equal to first point
+ if (mpSdrPathDragData->bClosed) rXP[rXP.GetPointCount()-1]=rXP[0];
+
+ if (mpSdrPathDragData->bEliminate)
+ {
+ basegfx::B2DPolyPolygon aTempPolyPolygon(aPathPolygon.getB2DPolyPolygon());
+ sal_uInt32 nPoly,nPnt;
+
+ if(PolyPolygonEditor::GetRelativePolyPoint(aTempPolyPolygon, rDrag.GetHdl()->GetSourceHdlNum(), nPoly, nPnt))
+ {
+ basegfx::B2DPolygon aCandidate(aTempPolyPolygon.getB2DPolygon(nPoly));
+ aCandidate.remove(nPnt);
+
+ if(aCandidate.count() < 2)
+ {
+ aTempPolyPolygon.remove(nPoly);
+ }
+ else
+ {
+ aTempPolyPolygon.setB2DPolygon(nPoly, aCandidate);
+ }
+ }
+
+ aPathPolygon = XPolyPolygon(aTempPolyPolygon);
+ }
+
+ // adapt angle for text beneath a simple line
+ if (bLineGlueMirror)
+ {
+ Point aLinePt1_(aPathPolygon[0][0]);
+ Point aLinePt2_(aPathPolygon[0][1]);
+ bool bXMirr=(aLinePt1_.X()>aLinePt2_.X())!=(aLinePt1.X()>aLinePt2.X());
+ bool bYMirr=(aLinePt1_.Y()>aLinePt2_.Y())!=(aLinePt1.Y()>aLinePt2.Y());
+ if (bXMirr || bYMirr) {
+ Point aRef1(mrSdrPathObject.GetSnapRect().Center());
+ if (bXMirr) {
+ Point aRef2(aRef1);
+ aRef2.AdjustY( 1 );
+ mrSdrPathObject.NbcMirrorGluePoints(aRef1,aRef2);
+ }
+ if (bYMirr) {
+ Point aRef2(aRef1);
+ aRef2.AdjustX( 1 );
+ mrSdrPathObject.NbcMirrorGluePoints(aRef1,aRef2);
+ }
+ }
+ }
+ }
+
+ mpSdrPathDragData.reset();
+
+ return true;
+}
+
+OUString ImpPathForDragAndCreate::getSpecialDragComment(const SdrDragStat& rDrag) const
+{
+ OUString aStr;
+ const SdrHdl* pHdl = rDrag.GetHdl();
+ const bool bCreateComment(rDrag.GetView() && &mrSdrPathObject == rDrag.GetView()->GetCreateObj());
+
+ if(bCreateComment && rDrag.GetUser())
+ {
+ // #i103058# re-add old creation comment mode
+ const ImpPathCreateUser* pU = static_cast<const ImpPathCreateUser*>(rDrag.GetUser());
+ const SdrObjKind eOriginalKind(meObjectKind);
+ mrSdrPathObject.meKind = pU->eCurrentKind;
+ aStr = mrSdrPathObject.ImpGetDescriptionStr(STR_ViewCreateObj);
+ mrSdrPathObject.meKind = eOriginalKind;
+
+ Point aPrev(rDrag.GetPrev());
+ Point aNow(rDrag.GetNow());
+
+ if(pU->bLine)
+ aNow = pU->aLineEnd;
+
+ aNow -= aPrev;
+ aStr += " (";
+
+ if(pU->bCircle)
+ {
+ aStr += SdrModel::GetAngleString(abs(pU->nCircRelAngle))
+ + " r="
+ + mrSdrPathObject.getSdrModelFromSdrObject().GetMetricString(pU->nCircRadius, true);
+ }
+
+ aStr += "dx="
+ + mrSdrPathObject.getSdrModelFromSdrObject().GetMetricString(aNow.X(), true)
+ + " dy="
+ + mrSdrPathObject.getSdrModelFromSdrObject().GetMetricString(aNow.Y(), true);
+
+ if(!IsFreeHand(meObjectKind))
+ {
+ sal_Int32 nLen(GetLen(aNow));
+ Degree100 nAngle(GetAngle(aNow));
+ aStr += " l="
+ + mrSdrPathObject.getSdrModelFromSdrObject().GetMetricString(nLen, true)
+ + " "
+ + SdrModel::GetAngleString(nAngle);
+ }
+
+ aStr += ")";
+ }
+ else if(!pHdl)
+ {
+ // #i103058# fallback when no model and/or Handle, both needed
+ // for else-path
+ aStr = mrSdrPathObject.ImpGetDescriptionStr(STR_DragPathObj);
+ }
+ else
+ {
+ // #i103058# standard for modification; model and handle needed
+ ImpSdrPathDragData* pDragData = mpSdrPathDragData.get();
+
+ if(!pDragData)
+ {
+ // getSpecialDragComment is also used from create, so fallback to GetUser()
+ // when mpSdrPathDragData is not set
+ pDragData = static_cast<ImpSdrPathDragData*>(rDrag.GetUser());
+ }
+
+ if(!pDragData)
+ {
+ OSL_FAIL("ImpPathForDragAndCreate::MovDrag(): ImpSdrPathDragData is invalid.");
+ return OUString();
+ }
+
+ if(!pDragData->IsMultiPointDrag() && pDragData->bEliminate)
+ {
+ // point of ...
+ aStr = mrSdrPathObject.ImpGetDescriptionStr(STR_ViewMarkedPoint);
+
+ // delete %O
+ OUString aStr2(SvxResId(STR_EditDelete));
+
+ // UNICODE: delete point of ...
+ aStr2 = aStr2.replaceFirst("%1", aStr);
+
+ return aStr2;
+ }
+
+ // dx=0.00 dy=0.00 -- both sides bezier
+ // dx=0.00 dy=0.00 l=0.00 0.00\302\260 -- one bezier/lever on one side, a start, or an ending
+ // dx=0.00 dy=0.00 l=0.00 0.00\302\260 / l=0.00 0.00\302\260 -- in between
+ Point aBeg(rDrag.GetStart());
+ Point aNow(rDrag.GetNow());
+
+ aStr.clear();
+ aStr += "dx="
+ + mrSdrPathObject.getSdrModelFromSdrObject().GetMetricString(aNow.X() - aBeg.X(), true)
+ + " dy="
+ + mrSdrPathObject.getSdrModelFromSdrObject().GetMetricString(aNow.Y() - aBeg.Y(), true);
+
+ if(!pDragData->IsMultiPointDrag())
+ {
+ sal_uInt16 nPntNum(static_cast<sal_uInt16>(pHdl->GetPointNum()));
+ const XPolygon& rXPoly = aPathPolygon[static_cast<sal_uInt16>(rDrag.GetHdl()->GetPolyNum())];
+ sal_uInt16 nPointCount(rXPoly.GetPointCount());
+ bool bClose(IsClosed(meObjectKind));
+
+ if(bClose)
+ nPointCount--;
+
+ if(pHdl->IsPlusHdl())
+ {
+ // lever
+ sal_uInt16 nRef(nPntNum);
+
+ if(rXPoly.IsControl(nPntNum + 1))
+ nRef--;
+ else
+ nRef++;
+
+ aNow -= rXPoly[nRef];
+
+ sal_Int32 nLen(GetLen(aNow));
+ Degree100 nAngle(GetAngle(aNow));
+ aStr += " l="
+ + mrSdrPathObject.getSdrModelFromSdrObject().GetMetricString(nLen, true)
+ + " "
+ + SdrModel::GetAngleString(nAngle);
+ }
+ else if(nPointCount > 1)
+ {
+ sal_uInt16 nPntMax(nPointCount - 1);
+ bool bIsClosed(IsClosed(meObjectKind));
+ bool bPt1(nPntNum > 0);
+ bool bPt2(nPntNum < nPntMax);
+
+ if(bIsClosed && nPointCount > 2)
+ {
+ bPt1 = true;
+ bPt2 = true;
+ }
+
+ sal_uInt16 nPt1,nPt2;
+
+ if(nPntNum > 0)
+ nPt1 = nPntNum - 1;
+ else
+ nPt1 = nPntMax;
+
+ if(nPntNum < nPntMax)
+ nPt2 = nPntNum + 1;
+ else
+ nPt2 = 0;
+
+ if(bPt1 && rXPoly.IsControl(nPt1))
+ bPt1 = false; // don't display
+
+ if(bPt2 && rXPoly.IsControl(nPt2))
+ bPt2 = false; // of bezier data
+
+ if(bPt1)
+ {
+ Point aPt(aNow);
+ aPt -= rXPoly[nPt1];
+
+ sal_Int32 nLen(GetLen(aPt));
+ Degree100 nAngle(GetAngle(aPt));
+ aStr += " l="
+ + mrSdrPathObject.getSdrModelFromSdrObject().GetMetricString(nLen, true)
+ + " "
+ + SdrModel::GetAngleString(nAngle);
+ }
+
+ if(bPt2)
+ {
+ if(bPt1)
+ aStr += " / ";
+ else
+ aStr += " ";
+
+ Point aPt(aNow);
+ aPt -= rXPoly[nPt2];
+
+ sal_Int32 nLen(GetLen(aPt));
+ Degree100 nAngle(GetAngle(aPt));
+ aStr += "l="
+ + mrSdrPathObject.getSdrModelFromSdrObject().GetMetricString(nLen, true)
+ + " "
+ + SdrModel::GetAngleString(nAngle);
+ }
+ }
+ }
+ }
+
+ return aStr;
+}
+
+basegfx::B2DPolyPolygon ImpPathForDragAndCreate::getSpecialDragPoly(const SdrDragStat& rDrag) const
+{
+ if(!mpSdrPathDragData || !mpSdrPathDragData->bValid)
+ {
+ OSL_FAIL("ImpPathForDragAndCreate::MovDrag(): ImpSdrPathDragData is invalid.");
+ return basegfx::B2DPolyPolygon();
+ }
+
+ XPolyPolygon aRetval;
+
+ if(mpSdrPathDragData->IsMultiPointDrag())
+ {
+ aRetval.Insert(mpSdrPathDragData->maMove);
+ }
+ else
+ {
+ const XPolygon& rXP=aPathPolygon[static_cast<sal_uInt16>(rDrag.GetHdl()->GetPolyNum())];
+ if (rXP.GetPointCount()<=2) {
+ XPolygon aXPoly(rXP);
+ aXPoly[static_cast<sal_uInt16>(rDrag.GetHdl()->GetPointNum())]=rDrag.GetNow();
+ aRetval.Insert(std::move(aXPoly));
+ return aRetval.getB2DPolyPolygon();
+ }
+ // copy certain data locally to use less code and have faster access times
+ bool bClosed =mpSdrPathDragData->bClosed ; // closed object?
+ sal_uInt16 nPointCount = mpSdrPathDragData->nPointCount; // number of points
+ sal_uInt16 nPnt =mpSdrPathDragData->nPnt ; // number of points in the polygon
+ bool bBegPnt =mpSdrPathDragData->bBegPnt ; // dragged point is the first point of a Polyline
+ bool bEndPnt =mpSdrPathDragData->bEndPnt ; // dragged point is the last point of a Polyline
+ sal_uInt16 nPrevPnt =mpSdrPathDragData->nPrevPnt ; // index of the previous point
+ sal_uInt16 nNextPnt =mpSdrPathDragData->nNextPnt ; // index of the next point
+ bool bPrevIsBegPnt =mpSdrPathDragData->bPrevIsBegPnt ; // previous point is first point of a Polyline
+ bool bNextIsEndPnt =mpSdrPathDragData->bNextIsEndPnt ; // next point is last point of a Polyline
+ sal_uInt16 nPrevPrevPnt =mpSdrPathDragData->nPrevPrevPnt ; // index of the point before the previous point
+ sal_uInt16 nNextNextPnt =mpSdrPathDragData->nNextNextPnt ; // index of the point after the last point
+ bool bControl =mpSdrPathDragData->bControl ; // point is a control point
+ bool bIsNextControl =mpSdrPathDragData->bIsNextControl; //point is a control point after a support point
+ bool bPrevIsControl =mpSdrPathDragData->bPrevIsControl; // if nPnt is a support point: there's a control point before
+ bool bNextIsControl =mpSdrPathDragData->bNextIsControl; // if nPnt is a support point: there's a control point after
+ XPolygon aXPoly(mpSdrPathDragData->aXP);
+ XPolygon aLine1(2);
+ XPolygon aLine2(2);
+ XPolygon aLine3(2);
+ XPolygon aLine4(2);
+ if (bControl) {
+ aLine1[1]=mpSdrPathDragData->aXP[nPnt];
+ if (bIsNextControl) { // is this a control point after the support point?
+ aLine1[0]=mpSdrPathDragData->aXP[nPrevPnt];
+ aLine2[0]=mpSdrPathDragData->aXP[nNextNextPnt];
+ aLine2[1]=mpSdrPathDragData->aXP[nNextPnt];
+ if (mpSdrPathDragData->aXP.IsSmooth(nPrevPnt) && !bPrevIsBegPnt && mpSdrPathDragData->aXP.IsControl(nPrevPrevPnt)) {
+ aXPoly.Insert(0,rXP[mpSdrPathDragData->nPrevPrevPnt0-1],PolyFlags::Control);
+ aXPoly.Insert(0,rXP[mpSdrPathDragData->nPrevPrevPnt0-2],PolyFlags::Normal);
+ // leverage lines for the opposing curve segment
+ aLine3[0]=mpSdrPathDragData->aXP[nPrevPnt];
+ aLine3[1]=mpSdrPathDragData->aXP[nPrevPrevPnt];
+ aLine4[0]=rXP[mpSdrPathDragData->nPrevPrevPnt0-2];
+ aLine4[1]=rXP[mpSdrPathDragData->nPrevPrevPnt0-1];
+ } else {
+ aXPoly.Remove(0,1);
+ }
+ } else { // else this is a control point before a support point
+ aLine1[0]=mpSdrPathDragData->aXP[nNextPnt];
+ aLine2[0]=mpSdrPathDragData->aXP[nPrevPrevPnt];
+ aLine2[1]=mpSdrPathDragData->aXP[nPrevPnt];
+ if (mpSdrPathDragData->aXP.IsSmooth(nNextPnt) && !bNextIsEndPnt && mpSdrPathDragData->aXP.IsControl(nNextNextPnt)) {
+ aXPoly.Insert(XPOLY_APPEND,rXP[mpSdrPathDragData->nNextNextPnt0+1],PolyFlags::Control);
+ aXPoly.Insert(XPOLY_APPEND,rXP[mpSdrPathDragData->nNextNextPnt0+2],PolyFlags::Normal);
+ // leverage lines for the opposing curve segment
+ aLine3[0]=mpSdrPathDragData->aXP[nNextPnt];
+ aLine3[1]=mpSdrPathDragData->aXP[nNextNextPnt];
+ aLine4[0]=rXP[mpSdrPathDragData->nNextNextPnt0+2];
+ aLine4[1]=rXP[mpSdrPathDragData->nNextNextPnt0+1];
+ } else {
+ aXPoly.Remove(aXPoly.GetPointCount()-1,1);
+ }
+ }
+ } else { // else is not a control point
+ if (mpSdrPathDragData->bEliminate) {
+ aXPoly.Remove(2,1);
+ }
+ if (bPrevIsControl) aXPoly.Insert(0,rXP[mpSdrPathDragData->nPrevPrevPnt0-1],PolyFlags::Normal);
+ else if (!bBegPnt && !bPrevIsBegPnt && mpSdrPathDragData->aXP.IsControl(nPrevPrevPnt)) {
+ aXPoly.Insert(0,rXP[mpSdrPathDragData->nPrevPrevPnt0-1],PolyFlags::Control);
+ aXPoly.Insert(0,rXP[mpSdrPathDragData->nPrevPrevPnt0-2],PolyFlags::Normal);
+ } else {
+ aXPoly.Remove(0,1);
+ if (bBegPnt) aXPoly.Remove(0,1);
+ }
+ if (bNextIsControl) aXPoly.Insert(XPOLY_APPEND,rXP[mpSdrPathDragData->nNextNextPnt0+1],PolyFlags::Normal);
+ else if (!bEndPnt && !bNextIsEndPnt && mpSdrPathDragData->aXP.IsControl(nNextNextPnt)) {
+ aXPoly.Insert(XPOLY_APPEND,rXP[mpSdrPathDragData->nNextNextPnt0+1],PolyFlags::Control);
+ aXPoly.Insert(XPOLY_APPEND,rXP[mpSdrPathDragData->nNextNextPnt0+2],PolyFlags::Normal);
+ } else {
+ aXPoly.Remove(aXPoly.GetPointCount()-1,1);
+ if (bEndPnt) aXPoly.Remove(aXPoly.GetPointCount()-1,1);
+ }
+ if (bClosed) { // "pear problem": 2 lines, 1 curve, everything smoothed, a point between both lines is dragged
+ if (aXPoly.GetPointCount()>nPointCount && aXPoly.IsControl(1)) {
+ sal_uInt16 a=aXPoly.GetPointCount();
+ aXPoly[a-2]=aXPoly[2]; aXPoly.SetFlags(a-2,aXPoly.GetFlags(2));
+ aXPoly[a-1]=aXPoly[3]; aXPoly.SetFlags(a-1,aXPoly.GetFlags(3));
+ aXPoly.Remove(0,3);
+ }
+ }
+ }
+ aRetval.Insert(std::move(aXPoly));
+ if (aLine1.GetPointCount()>1) aRetval.Insert(std::move(aLine1));
+ if (aLine2.GetPointCount()>1) aRetval.Insert(std::move(aLine2));
+ if (aLine3.GetPointCount()>1) aRetval.Insert(std::move(aLine3));
+ if (aLine4.GetPointCount()>1) aRetval.Insert(std::move(aLine4));
+ }
+
+ return aRetval.getB2DPolyPolygon();
+}
+
+void ImpPathForDragAndCreate::BegCreate(SdrDragStat& rStat)
+{
+ bool bFreeHand(IsFreeHand(meObjectKind));
+ rStat.SetNoSnap(bFreeHand);
+ rStat.SetOrtho8Possible();
+ aPathPolygon.Clear();
+ mbCreating=true;
+ bool bMakeStartPoint = true;
+ SdrView* pView=rStat.GetView();
+ if (pView!=nullptr && pView->IsUseIncompatiblePathCreateInterface() &&
+ (meObjectKind==SdrObjKind::Polygon || meObjectKind==SdrObjKind::PolyLine || meObjectKind==SdrObjKind::PathLine || meObjectKind==SdrObjKind::PathFill)) {
+ bMakeStartPoint = false;
+ }
+ aPathPolygon.Insert(XPolygon());
+ aPathPolygon[0][0]=rStat.GetStart();
+ if (bMakeStartPoint) {
+ aPathPolygon[0][1]=rStat.GetNow();
+ }
+ std::unique_ptr<ImpPathCreateUser> pU(new ImpPathCreateUser);
+ pU->eStartKind=meObjectKind;
+ pU->eCurrentKind=meObjectKind;
+ rStat.SetUser(std::move(pU));
+}
+
+bool ImpPathForDragAndCreate::MovCreate(SdrDragStat& rStat)
+{
+ ImpPathCreateUser* pU=static_cast<ImpPathCreateUser*>(rStat.GetUser());
+ SdrView* pView=rStat.GetView();
+ XPolygon& rXPoly=aPathPolygon[aPathPolygon.Count()-1];
+ if (pView!=nullptr && pView->IsCreateMode()) {
+ // switch to different CreateTool, if appropriate
+ SdrObjKind nIdent;
+ SdrInventor nInvent;
+ pView->TakeCurrentObj(nIdent,nInvent);
+ if (nInvent==SdrInventor::Default && pU->eCurrentKind != nIdent) {
+ SdrObjKind eNewKind = nIdent;
+ switch (eNewKind) {
+ case SdrObjKind::CircleArc:
+ case SdrObjKind::CircleOrEllipse:
+ case SdrObjKind::CircleCut:
+ case SdrObjKind::CircleSection:
+ eNewKind=SdrObjKind::CircleArc;
+ [[fallthrough]];
+ case SdrObjKind::Rectangle:
+ case SdrObjKind::Line:
+ case SdrObjKind::PolyLine:
+ case SdrObjKind::Polygon:
+ case SdrObjKind::PathLine:
+ case SdrObjKind::PathFill:
+ case SdrObjKind::FreehandLine:
+ case SdrObjKind::FreehandFill:
+ case SdrObjKind::SplineLine:
+ case SdrObjKind::SplineFill: {
+ pU->eCurrentKind=eNewKind;
+ pU->bMixedCreate=true;
+ pU->nBezierStartPoint=rXPoly.GetPointCount();
+ if (pU->nBezierStartPoint>0) pU->nBezierStartPoint--;
+ } break;
+ default: break;
+ } // switch
+ }
+ }
+ sal_uInt16 nCurrentPoint=rXPoly.GetPointCount();
+ if (aPathPolygon.Count()>1 && rStat.IsMouseDown() && nCurrentPoint<2) {
+ rXPoly[0]=rStat.GetPos0();
+ rXPoly[1]=rStat.GetNow();
+ nCurrentPoint=2;
+ }
+ if (nCurrentPoint==0) {
+ rXPoly[0]=rStat.GetPos0();
+ } else nCurrentPoint--;
+ bool bFreeHand=IsFreeHand(pU->eCurrentKind);
+ rStat.SetNoSnap(bFreeHand);
+ rStat.SetOrtho8Possible(pU->eCurrentKind!=SdrObjKind::CircleArc && pU->eCurrentKind!=SdrObjKind::Rectangle && (!pU->bMixedCreate || pU->eCurrentKind!=SdrObjKind::Line));
+ rXPoly[nCurrentPoint]=rStat.GetNow();
+ if (!pU->bMixedCreate && pU->eStartKind==SdrObjKind::Line && rXPoly.GetPointCount()>=1) {
+ Point aPt(rStat.GetStart());
+ if (pView!=nullptr && pView->IsCreate1stPointAsCenter()) {
+ aPt+=aPt;
+ aPt-=rStat.GetNow();
+ }
+ rXPoly[0]=aPt;
+ }
+ OutputDevice* pOut=pView==nullptr ? nullptr : pView->GetFirstOutputDevice();
+ if (bFreeHand) {
+ if (pU->nBezierStartPoint>nCurrentPoint) pU->nBezierStartPoint=nCurrentPoint;
+ if (rStat.IsMouseDown() && nCurrentPoint>0) {
+ // don't allow two consecutive points to occupy too similar positions
+ tools::Long nMinDist=1;
+ if (pView!=nullptr) nMinDist=pView->GetFreeHandMinDistPix();
+ if (pOut!=nullptr) nMinDist=pOut->PixelToLogic(Size(nMinDist,0)).Width();
+ if (nMinDist<1) nMinDist=1;
+
+ Point aPt0(rXPoly[nCurrentPoint-1]);
+ Point aPt1(rStat.GetNow());
+ tools::Long dx=aPt0.X()-aPt1.X(); if (dx<0) dx=-dx;
+ tools::Long dy=aPt0.Y()-aPt1.Y(); if (dy<0) dy=-dy;
+ if (dx<nMinDist && dy<nMinDist) return false;
+
+ // TODO: the following is copied from EndCreate (with a few smaller modifications)
+ // and should be combined into a method with the code there.
+
+ if (nCurrentPoint-pU->nBezierStartPoint>=3 && ((nCurrentPoint-pU->nBezierStartPoint)%3)==0) {
+ rXPoly.PointsToBezier(nCurrentPoint-3);
+ rXPoly.SetFlags(nCurrentPoint-1,PolyFlags::Control);
+ rXPoly.SetFlags(nCurrentPoint-2,PolyFlags::Control);
+
+ if (nCurrentPoint>=6 && rXPoly.IsControl(nCurrentPoint-4)) {
+ rXPoly.CalcTangent(nCurrentPoint-3,nCurrentPoint-4,nCurrentPoint-2);
+ rXPoly.SetFlags(nCurrentPoint-3,PolyFlags::Smooth);
+ }
+ }
+ rXPoly[nCurrentPoint+1]=rStat.GetNow();
+ rStat.NextPoint();
+ } else {
+ pU->nBezierStartPoint=nCurrentPoint;
+ }
+ }
+
+ pU->ResetFormFlags();
+ if (IsBezier(pU->eCurrentKind)) {
+ if (nCurrentPoint>=2) {
+ pU->CalcBezier(rXPoly[nCurrentPoint-1],rXPoly[nCurrentPoint],rXPoly[nCurrentPoint-1]-rXPoly[nCurrentPoint-2],rStat.IsMouseDown());
+ } else if (pU->bBezHasCtrl0) {
+ pU->CalcBezier(rXPoly[nCurrentPoint-1],rXPoly[nCurrentPoint],pU->aBezControl0-rXPoly[nCurrentPoint-1],rStat.IsMouseDown());
+ }
+ }
+ if (pU->eCurrentKind==SdrObjKind::CircleArc && nCurrentPoint>=2) {
+ pU->CalcCircle(rXPoly[nCurrentPoint-1],rXPoly[nCurrentPoint],rXPoly[nCurrentPoint-1]-rXPoly[nCurrentPoint-2],pView);
+ }
+ if (pU->eCurrentKind==SdrObjKind::Line && nCurrentPoint>=2) {
+ pU->CalcLine(rXPoly[nCurrentPoint-1],rXPoly[nCurrentPoint],rXPoly[nCurrentPoint-1]-rXPoly[nCurrentPoint-2],pView);
+ }
+ if (pU->eCurrentKind==SdrObjKind::Rectangle && nCurrentPoint>=2) {
+ pU->CalcRect(rXPoly[nCurrentPoint-1],rXPoly[nCurrentPoint],rXPoly[nCurrentPoint-1]-rXPoly[nCurrentPoint-2],pView);
+ }
+
+ return true;
+}
+
+bool ImpPathForDragAndCreate::EndCreate(SdrDragStat& rStat, SdrCreateCmd eCmd)
+{
+ ImpPathCreateUser* pU=static_cast<ImpPathCreateUser*>(rStat.GetUser());
+ bool bRet = false;
+ SdrView* pView=rStat.GetView();
+ bool bIncomp=pView!=nullptr && pView->IsUseIncompatiblePathCreateInterface();
+ XPolygon& rXPoly=aPathPolygon[aPathPolygon.Count()-1];
+ sal_uInt16 nCurrentPoint=rXPoly.GetPointCount()-1;
+ rXPoly[nCurrentPoint]=rStat.GetNow();
+ if (!pU->bMixedCreate && pU->eStartKind==SdrObjKind::Line) {
+ if (rStat.GetPointCount()>=2) eCmd=SdrCreateCmd::ForceEnd;
+ bRet = eCmd==SdrCreateCmd::ForceEnd;
+ if (bRet) {
+ mbCreating = false;
+ rStat.SetUser(nullptr);
+ }
+ return bRet;
+ }
+
+ if (!pU->bMixedCreate && IsFreeHand(pU->eStartKind)) {
+ if (rStat.GetPointCount()>=2) eCmd=SdrCreateCmd::ForceEnd;
+ bRet=eCmd==SdrCreateCmd::ForceEnd;
+ if (bRet) {
+ mbCreating=false;
+ rStat.SetUser(nullptr);
+ }
+ return bRet;
+ }
+ if (eCmd==SdrCreateCmd::NextPoint || eCmd==SdrCreateCmd::NextObject) {
+ // don't allow two consecutive points to occupy the same position
+ if (nCurrentPoint==0 || rStat.GetNow()!=rXPoly[nCurrentPoint-1]) {
+ if (bIncomp) {
+ if (pU->nBezierStartPoint>nCurrentPoint) pU->nBezierStartPoint=nCurrentPoint;
+ if (IsBezier(pU->eCurrentKind) && nCurrentPoint-pU->nBezierStartPoint>=3 && ((nCurrentPoint-pU->nBezierStartPoint)%3)==0) {
+ rXPoly.PointsToBezier(nCurrentPoint-3);
+ rXPoly.SetFlags(nCurrentPoint-1,PolyFlags::Control);
+ rXPoly.SetFlags(nCurrentPoint-2,PolyFlags::Control);
+
+ if (nCurrentPoint>=6 && rXPoly.IsControl(nCurrentPoint-4)) {
+ rXPoly.CalcTangent(nCurrentPoint-3,nCurrentPoint-4,nCurrentPoint-2);
+ rXPoly.SetFlags(nCurrentPoint-3,PolyFlags::Smooth);
+ }
+ }
+ } else {
+ if (nCurrentPoint==1 && IsBezier(pU->eCurrentKind) && !pU->bBezHasCtrl0) {
+ pU->aBezControl0=rStat.GetNow();
+ pU->bBezHasCtrl0=true;
+ nCurrentPoint--;
+ }
+ if (pU->IsFormFlag()) {
+ sal_uInt16 nPointCount0=rXPoly.GetPointCount();
+ rXPoly.Remove(nCurrentPoint-1,2); // remove last two points and replace by form
+ rXPoly.Insert(XPOLY_APPEND,pU->GetFormPoly());
+ sal_uInt16 nPointCount1=rXPoly.GetPointCount();
+ for (sal_uInt16 i=nPointCount0+1; i<nPointCount1-1; i++) { // to make BckAction work
+ if (!rXPoly.IsControl(i)) rStat.NextPoint();
+ }
+ nCurrentPoint=rXPoly.GetPointCount()-1;
+ }
+ }
+ nCurrentPoint++;
+ rXPoly[nCurrentPoint]=rStat.GetNow();
+ }
+ if (eCmd==SdrCreateCmd::NextObject) {
+ if (rXPoly.GetPointCount()>=2) {
+ pU->bBezHasCtrl0=false;
+ // only a singular polygon may be opened, so close this
+ rXPoly[nCurrentPoint]=rXPoly[0];
+ XPolygon aXP;
+ aXP[0]=rStat.GetNow();
+ aPathPolygon.Insert(std::move(aXP));
+ }
+ }
+ }
+
+ sal_uInt16 nPolyCount=aPathPolygon.Count();
+ if (nPolyCount!=0) {
+ // delete last point, if necessary
+ if (eCmd==SdrCreateCmd::ForceEnd) {
+ XPolygon& rXP=aPathPolygon[nPolyCount-1];
+ sal_uInt16 nPointCount=rXP.GetPointCount();
+ if (nPointCount>=2) {
+ if (!rXP.IsControl(nPointCount-2)) {
+ if (rXP[nPointCount-1]==rXP[nPointCount-2]) {
+ rXP.Remove(nPointCount-1,1);
+ }
+ } else {
+ if (rXP[nPointCount-3]==rXP[nPointCount-2]) {
+ rXP.Remove(nPointCount-3,3);
+ }
+ }
+ }
+ }
+ for (sal_uInt16 nPolyNum=nPolyCount; nPolyNum>0;) {
+ nPolyNum--;
+ XPolygon& rXP=aPathPolygon[nPolyNum];
+ sal_uInt16 nPointCount=rXP.GetPointCount();
+ // delete polygons with too few points
+ if (nPolyNum<nPolyCount-1 || eCmd==SdrCreateCmd::ForceEnd) {
+ if (nPointCount<2) aPathPolygon.Remove(nPolyNum);
+ }
+ }
+ }
+ pU->ResetFormFlags();
+ bRet=eCmd==SdrCreateCmd::ForceEnd;
+ if (bRet) {
+ mbCreating=false;
+ rStat.SetUser(nullptr);
+ }
+ return bRet;
+}
+
+bool ImpPathForDragAndCreate::BckCreate(SdrDragStat const & rStat)
+{
+ ImpPathCreateUser* pU=static_cast<ImpPathCreateUser*>(rStat.GetUser());
+ if (aPathPolygon.Count()>0) {
+ XPolygon& rXPoly=aPathPolygon[aPathPolygon.Count()-1];
+ sal_uInt16 nCurrentPoint=rXPoly.GetPointCount();
+ if (nCurrentPoint>0) {
+ nCurrentPoint--;
+ // make the last part of a bezier curve a line
+ rXPoly.Remove(nCurrentPoint,1);
+ if (nCurrentPoint>=3 && rXPoly.IsControl(nCurrentPoint-1)) {
+ // there should never be a bezier segment at the end, so this is just in case...
+ rXPoly.Remove(nCurrentPoint-1,1);
+ if (rXPoly.IsControl(nCurrentPoint-2)) rXPoly.Remove(nCurrentPoint-2,1);
+ }
+ }
+ nCurrentPoint=rXPoly.GetPointCount();
+ if (nCurrentPoint>=4) { // no bezier segment at the end
+ nCurrentPoint--;
+ if (rXPoly.IsControl(nCurrentPoint-1)) {
+ rXPoly.Remove(nCurrentPoint-1,1);
+ if (rXPoly.IsControl(nCurrentPoint-2)) rXPoly.Remove(nCurrentPoint-2,1);
+ }
+ }
+ if (rXPoly.GetPointCount()<2) {
+ aPathPolygon.Remove(aPathPolygon.Count()-1);
+ }
+ if (aPathPolygon.Count()>0) {
+ XPolygon& rLocalXPoly=aPathPolygon[aPathPolygon.Count()-1];
+ sal_uInt16 nLocalCurrentPoint=rLocalXPoly.GetPointCount();
+ if (nLocalCurrentPoint>0) {
+ nLocalCurrentPoint--;
+ rLocalXPoly[nLocalCurrentPoint]=rStat.GetNow();
+ }
+ }
+ }
+ pU->ResetFormFlags();
+ return aPathPolygon.Count()!=0;
+}
+
+void ImpPathForDragAndCreate::BrkCreate(SdrDragStat& rStat)
+{
+ aPathPolygon.Clear();
+ mbCreating=false;
+ rStat.SetUser(nullptr);
+}
+
+basegfx::B2DPolyPolygon ImpPathForDragAndCreate::TakeObjectPolyPolygon(const SdrDragStat& rDrag) const
+{
+ basegfx::B2DPolyPolygon aRetval(aPathPolygon.getB2DPolyPolygon());
+ SdrView* pView = rDrag.GetView();
+
+ if(pView && pView->IsUseIncompatiblePathCreateInterface())
+ return aRetval;
+
+ ImpPathCreateUser* pU = static_cast<ImpPathCreateUser*>(rDrag.GetUser());
+ basegfx::B2DPolygon aNewPolygon(aRetval.count() ? aRetval.getB2DPolygon(aRetval.count() - 1) : basegfx::B2DPolygon());
+
+ if(pU->IsFormFlag() && aNewPolygon.count() > 1)
+ {
+ // remove last segment and replace with current
+ // do not forget to rescue the previous control point which will be lost when
+ // the point it's associated with is removed
+ const sal_uInt32 nChangeIndex(aNewPolygon.count() - 2);
+ const basegfx::B2DPoint aSavedPrevCtrlPoint(aNewPolygon.getPrevControlPoint(nChangeIndex));
+
+ aNewPolygon.remove(nChangeIndex, 2);
+ aNewPolygon.append(pU->GetFormPoly().getB2DPolygon());
+
+ if(nChangeIndex < aNewPolygon.count())
+ {
+ // if really something was added, set the saved previous control point to the
+ // point where it belongs
+ aNewPolygon.setPrevControlPoint(nChangeIndex, aSavedPrevCtrlPoint);
+ }
+ }
+
+ if(aRetval.count())
+ {
+ aRetval.setB2DPolygon(aRetval.count() - 1, aNewPolygon);
+ }
+ else
+ {
+ aRetval.append(aNewPolygon);
+ }
+
+ return aRetval;
+}
+
+basegfx::B2DPolyPolygon ImpPathForDragAndCreate::TakeDragPolyPolygon(const SdrDragStat& rDrag)
+{
+ basegfx::B2DPolyPolygon aRetval;
+ SdrView* pView = rDrag.GetView();
+
+ if(pView && pView->IsUseIncompatiblePathCreateInterface())
+ return aRetval;
+
+ const ImpPathCreateUser* pU = static_cast<const ImpPathCreateUser*>(rDrag.GetUser());
+
+ if(pU && pU->bBezier && rDrag.IsMouseDown())
+ {
+ // no more XOR, no need for complicated helplines
+ basegfx::B2DPolygon aHelpline;
+ aHelpline.append(basegfx::B2DPoint(pU->aBezCtrl2.X(), pU->aBezCtrl2.Y()));
+ aHelpline.append(basegfx::B2DPoint(pU->aBezEnd.X(), pU->aBezEnd.Y()));
+ aRetval.append(aHelpline);
+ }
+
+ return aRetval;
+}
+
+PointerStyle ImpPathForDragAndCreate::GetCreatePointer() const
+{
+ switch (meObjectKind) {
+ case SdrObjKind::Line : return PointerStyle::DrawLine;
+ case SdrObjKind::Polygon : return PointerStyle::DrawPolygon;
+ case SdrObjKind::PolyLine : return PointerStyle::DrawPolygon;
+ case SdrObjKind::PathLine: return PointerStyle::DrawBezier;
+ case SdrObjKind::PathFill: return PointerStyle::DrawBezier;
+ case SdrObjKind::FreehandLine: return PointerStyle::DrawFreehand;
+ case SdrObjKind::FreehandFill: return PointerStyle::DrawFreehand;
+ case SdrObjKind::SplineLine: return PointerStyle::DrawFreehand;
+ case SdrObjKind::SplineFill: return PointerStyle::DrawFreehand;
+ case SdrObjKind::PathPoly: return PointerStyle::DrawPolygon;
+ case SdrObjKind::PathPolyLine: return PointerStyle::DrawPolygon;
+ default: break;
+ } // switch
+ return PointerStyle::Cross;
+}
+
+SdrPathObjGeoData::SdrPathObjGeoData()
+ : meKind(SdrObjKind::NONE)
+{
+}
+
+SdrPathObjGeoData::~SdrPathObjGeoData()
+{
+}
+
+// DrawContact section
+
+std::unique_ptr<sdr::contact::ViewContact> SdrPathObj::CreateObjectSpecificViewContact()
+{
+ return std::make_unique<sdr::contact::ViewContactOfSdrPathObj>(*this);
+}
+
+
+SdrPathObj::SdrPathObj(
+ SdrModel& rSdrModel,
+ SdrObjKind eNewKind)
+: SdrTextObj(rSdrModel),
+ meKind(eNewKind)
+{
+ m_bClosedObj = IsClosed();
+}
+
+SdrPathObj::SdrPathObj(SdrModel& rSdrModel, SdrPathObj const & rSource)
+: SdrTextObj(rSdrModel, rSource),
+ meKind(rSource.meKind)
+{
+ m_bClosedObj = IsClosed();
+ maPathPolygon = rSource.GetPathPoly();
+}
+
+SdrPathObj::SdrPathObj(
+ SdrModel& rSdrModel,
+ SdrObjKind eNewKind,
+ const basegfx::B2DPolyPolygon& rPathPoly)
+: SdrTextObj(rSdrModel),
+ maPathPolygon(rPathPoly),
+ meKind(eNewKind)
+{
+ m_bClosedObj = IsClosed();
+ ImpForceKind();
+}
+
+SdrPathObj::~SdrPathObj() = default;
+
+static bool lcl_ImpIsLine(const basegfx::B2DPolyPolygon& rPolyPolygon)
+{
+ return (1 == rPolyPolygon.count() && 2 == rPolyPolygon.getB2DPolygon(0).count());
+}
+
+static tools::Rectangle lcl_ImpGetBoundRect(const basegfx::B2DPolyPolygon& rPolyPolygon)
+{
+ basegfx::B2DRange aRange(basegfx::utils::getRange(rPolyPolygon));
+
+ if (aRange.isEmpty())
+ return tools::Rectangle();
+
+ return tools::Rectangle(
+ FRound(aRange.getMinX()), FRound(aRange.getMinY()),
+ FRound(aRange.getMaxX()), FRound(aRange.getMaxY()));
+}
+
+void SdrPathObj::ImpForceLineAngle()
+{
+ if(SdrObjKind::Line != meKind || !lcl_ImpIsLine(GetPathPoly()))
+ return;
+
+ const basegfx::B2DPolygon aPoly(GetPathPoly().getB2DPolygon(0));
+ const basegfx::B2DPoint aB2DPoint0(aPoly.getB2DPoint(0));
+ const basegfx::B2DPoint aB2DPoint1(aPoly.getB2DPoint(1));
+ const Point aPoint0(FRound(aB2DPoint0.getX()), FRound(aB2DPoint0.getY()));
+ const Point aPoint1(FRound(aB2DPoint1.getX()), FRound(aB2DPoint1.getY()));
+ const basegfx::B2DPoint aB2DDelt(aB2DPoint1 - aB2DPoint0);
+ const Point aDelt(FRound(aB2DDelt.getX()), FRound(aB2DDelt.getY()));
+
+ maGeo.nRotationAngle=GetAngle(aDelt);
+ maGeo.nShearAngle=0_deg100;
+ maGeo.RecalcSinCos();
+ maGeo.RecalcTan();
+
+ // for SdrTextObj, keep aRect up to date
+ maRect = tools::Rectangle::Justify(aPoint0, aPoint1);
+}
+
+void SdrPathObj::ImpForceKind()
+{
+ if (meKind==SdrObjKind::PathPolyLine) meKind=SdrObjKind::PolyLine;
+ if (meKind==SdrObjKind::PathPoly) meKind=SdrObjKind::Polygon;
+
+ if(GetPathPoly().areControlPointsUsed())
+ {
+ switch (meKind)
+ {
+ case SdrObjKind::Line: meKind=SdrObjKind::PathLine; break;
+ case SdrObjKind::PolyLine: meKind=SdrObjKind::PathLine; break;
+ case SdrObjKind::Polygon: meKind=SdrObjKind::PathFill; break;
+ default: break;
+ }
+ }
+ else
+ {
+ switch (meKind)
+ {
+ case SdrObjKind::PathLine: meKind=SdrObjKind::PolyLine; break;
+ case SdrObjKind::FreehandLine: meKind=SdrObjKind::PolyLine; break;
+ case SdrObjKind::PathFill: meKind=SdrObjKind::Polygon; break;
+ case SdrObjKind::FreehandFill: meKind=SdrObjKind::Polygon; break;
+ default: break;
+ }
+ }
+
+ if (meKind==SdrObjKind::Line && !lcl_ImpIsLine(GetPathPoly())) meKind=SdrObjKind::PolyLine;
+ if (meKind==SdrObjKind::PolyLine && lcl_ImpIsLine(GetPathPoly())) meKind=SdrObjKind::Line;
+
+ m_bClosedObj=IsClosed();
+
+ if (meKind==SdrObjKind::Line)
+ {
+ ImpForceLineAngle();
+ }
+ else
+ {
+ // #i10659#, for polys with more than 2 points.
+
+ // Here i again need to fix something, because when Path-Polys are Copy-Pasted
+ // between Apps with different measurements (e.g. 100TH_MM and TWIPS) there is
+ // a scaling loop started from SdrExchangeView::Paste. In itself, this is not
+ // wrong, but aRect is wrong here and not even updated by RecalcSnapRect(). If
+ // this is the case, some size needs to be set here in aRect to avoid that the cycle
+ // through Rect2Poly - Poly2Rect does something badly wrong since that cycle is
+ // BASED on aRect. That cycle is triggered in SdrTextObj::NbcResize() which is called
+ // from the local Resize() implementation.
+
+ // Basic problem is that the member aRect in SdrTextObj basically is a unrotated
+ // text rectangle for the text object itself and methods at SdrTextObj do handle it
+ // in that way. Many draw objects derived from SdrTextObj 'abuse' aRect as SnapRect
+ // which is basically wrong. To make the SdrText methods which deal with aRect directly
+ // work it is necessary to always keep aRect updated. This e.g. not done after a Clone()
+ // command for SdrPathObj. Since adding this update mechanism with #101412# to
+ // ImpForceLineAngle() for lines was very successful, i add it to where ImpForceLineAngle()
+ // was called, once here below and once on a 2nd place below.
+
+ // #i10659# for SdrTextObj, keep aRect up to date
+ if(GetPathPoly().count())
+ {
+ maRect = lcl_ImpGetBoundRect(GetPathPoly());
+ }
+ }
+
+ // #i75974# adapt polygon state to object type. This may include a reinterpretation
+ // of a closed geometry as open one, but with identical first and last point
+ for(auto& rPolygon : maPathPolygon)
+ {
+ if(IsClosed() != rPolygon.isClosed())
+ {
+ // #i80213# really change polygon geometry; else e.g. the last point which
+ // needs to be identical with the first one will be missing when opening
+ // due to OBJ_PATH type
+ if(rPolygon.isClosed())
+ {
+ basegfx::utils::openWithGeometryChange(rPolygon);
+ }
+ else
+ {
+ basegfx::utils::closeWithGeometryChange(rPolygon);
+ }
+ }
+ }
+}
+
+void SdrPathObj::ImpSetClosed(bool bClose)
+{
+ if(bClose)
+ {
+ switch (meKind)
+ {
+ case SdrObjKind::Line : meKind=SdrObjKind::Polygon; break;
+ case SdrObjKind::PolyLine : meKind=SdrObjKind::Polygon; break;
+ case SdrObjKind::PathLine: meKind=SdrObjKind::PathFill; break;
+ case SdrObjKind::FreehandLine: meKind=SdrObjKind::FreehandFill; break;
+ case SdrObjKind::SplineLine: meKind=SdrObjKind::SplineFill; break;
+ default: break;
+ }
+
+ m_bClosedObj = true;
+ }
+ else
+ {
+ switch (meKind)
+ {
+ case SdrObjKind::Polygon : meKind=SdrObjKind::PolyLine; break;
+ case SdrObjKind::PathFill: meKind=SdrObjKind::PathLine; break;
+ case SdrObjKind::FreehandFill: meKind=SdrObjKind::FreehandLine; break;
+ case SdrObjKind::SplineFill: meKind=SdrObjKind::SplineLine; break;
+ default: break;
+ }
+
+ m_bClosedObj = false;
+ }
+
+ ImpForceKind();
+}
+
+void SdrPathObj::TakeObjInfo(SdrObjTransformInfoRec& rInfo) const
+{
+ rInfo.bNoContortion=false;
+
+ bool bCanConv = !HasText() || ImpCanConvTextToCurve();
+ bool bIsPath = IsBezier() || IsSpline();
+
+ rInfo.bEdgeRadiusAllowed = false;
+ rInfo.bCanConvToPath = bCanConv && !bIsPath;
+ rInfo.bCanConvToPoly = bCanConv && bIsPath;
+ rInfo.bCanConvToContour = !IsFontwork() && (rInfo.bCanConvToPoly || LineGeometryUsageIsNecessary());
+}
+
+SdrObjKind SdrPathObj::GetObjIdentifier() const
+{
+ return meKind;
+}
+
+SdrPathObj* SdrPathObj::CloneSdrObject(SdrModel& rTargetModel) const
+{
+ return new SdrPathObj(rTargetModel, *this);
+}
+
+OUString SdrPathObj::TakeObjNameSingul() const
+{
+ OUString sName;
+
+ if(SdrObjKind::Line == meKind)
+ {
+ TranslateId pId(STR_ObjNameSingulLINE);
+
+ if(lcl_ImpIsLine(GetPathPoly()))
+ {
+ const basegfx::B2DPolygon aPoly(GetPathPoly().getB2DPolygon(0));
+ const basegfx::B2DPoint aB2DPoint0(aPoly.getB2DPoint(0));
+ const basegfx::B2DPoint aB2DPoint1(aPoly.getB2DPoint(1));
+
+ if(aB2DPoint0 != aB2DPoint1)
+ {
+ if(aB2DPoint0.getY() == aB2DPoint1.getY())
+ {
+ pId = STR_ObjNameSingulLINE_Hori;
+ }
+ else if(aB2DPoint0.getX() == aB2DPoint1.getX())
+ {
+ pId = STR_ObjNameSingulLINE_Vert;
+ }
+ else
+ {
+ const double fDx(fabs(aB2DPoint0.getX() - aB2DPoint1.getX()));
+ const double fDy(fabs(aB2DPoint0.getY() - aB2DPoint1.getY()));
+
+ if(fDx == fDy)
+ {
+ pId = STR_ObjNameSingulLINE_Diag;
+ }
+ }
+ }
+ }
+
+ sName = SvxResId(pId);
+ }
+ else if(SdrObjKind::PolyLine == meKind || SdrObjKind::Polygon == meKind)
+ {
+ const bool bClosed(SdrObjKind::Polygon == meKind);
+ TranslateId pId;
+
+ if(mpDAC && mpDAC->IsCreating())
+ {
+ if(bClosed)
+ {
+ pId = STR_ObjNameSingulPOLY;
+ }
+ else
+ {
+ pId = STR_ObjNameSingulPLIN;
+ }
+
+ sName = SvxResId(pId);
+ }
+ else
+ {
+ // get point count
+ sal_uInt32 nPointCount(0);
+
+ for(auto const& rPolygon : GetPathPoly())
+ {
+ nPointCount += rPolygon.count();
+ }
+
+ if(bClosed)
+ {
+ pId = STR_ObjNameSingulPOLY_PointCount;
+ }
+ else
+ {
+ pId = STR_ObjNameSingulPLIN_PointCount;
+ }
+
+ // #i96537#
+ sName = SvxResId(pId).replaceFirst("%2", OUString::number(nPointCount));
+ }
+ }
+ else
+ {
+ switch (meKind)
+ {
+ case SdrObjKind::PathLine: sName = SvxResId(STR_ObjNameSingulPATHLINE); break;
+ case SdrObjKind::FreehandLine: sName = SvxResId(STR_ObjNameSingulFREELINE); break;
+ case SdrObjKind::SplineLine: sName = SvxResId(STR_ObjNameSingulNATSPLN); break;
+ case SdrObjKind::PathFill: sName = SvxResId(STR_ObjNameSingulPATHFILL); break;
+ case SdrObjKind::FreehandFill: sName = SvxResId(STR_ObjNameSingulFREEFILL); break;
+ case SdrObjKind::SplineFill: sName = SvxResId(STR_ObjNameSingulPERSPLN); break;
+ default: break;
+ }
+ }
+
+ OUString aName(GetName());
+ if (!aName.isEmpty())
+ sName += " '" + aName + "'";
+
+ return sName;
+}
+
+OUString SdrPathObj::TakeObjNamePlural() const
+{
+ OUString sName;
+ switch(meKind)
+ {
+ case SdrObjKind::Line : sName=SvxResId(STR_ObjNamePluralLINE ); break;
+ case SdrObjKind::PolyLine : sName=SvxResId(STR_ObjNamePluralPLIN ); break;
+ case SdrObjKind::Polygon : sName=SvxResId(STR_ObjNamePluralPOLY ); break;
+ case SdrObjKind::PathLine: sName=SvxResId(STR_ObjNamePluralPATHLINE); break;
+ case SdrObjKind::FreehandLine: sName=SvxResId(STR_ObjNamePluralFREELINE); break;
+ case SdrObjKind::SplineLine: sName=SvxResId(STR_ObjNamePluralNATSPLN); break;
+ case SdrObjKind::PathFill: sName=SvxResId(STR_ObjNamePluralPATHFILL); break;
+ case SdrObjKind::FreehandFill: sName=SvxResId(STR_ObjNamePluralFREEFILL); break;
+ case SdrObjKind::SplineFill: sName=SvxResId(STR_ObjNamePluralPERSPLN); break;
+ default: break;
+ }
+ return sName;
+}
+
+basegfx::B2DPolyPolygon SdrPathObj::TakeXorPoly() const
+{
+ return GetPathPoly();
+}
+
+sal_uInt32 SdrPathObj::GetHdlCount() const
+{
+ sal_uInt32 nRetval(0);
+
+ for(auto const& rPolygon : GetPathPoly())
+ {
+ nRetval += rPolygon.count();
+ }
+
+ return nRetval;
+}
+
+void SdrPathObj::AddToHdlList(SdrHdlList& rHdlList) const
+{
+ // keep old stuff to be able to keep old SdrHdl stuff, too
+ const XPolyPolygon aOldPathPolygon(GetPathPoly());
+ sal_uInt16 nPolyCnt=aOldPathPolygon.Count();
+ bool bClosed=IsClosed();
+ sal_uInt16 nIdx=0;
+
+ for (sal_uInt16 i=0; i<nPolyCnt; i++) {
+ const XPolygon& rXPoly=aOldPathPolygon.GetObject(i);
+ sal_uInt16 nPntCnt=rXPoly.GetPointCount();
+ if (bClosed && nPntCnt>1) nPntCnt--;
+
+ for (sal_uInt16 j=0; j<nPntCnt; j++) {
+ if (rXPoly.GetFlags(j)!=PolyFlags::Control) {
+ const Point& rPnt=rXPoly[j];
+ std::unique_ptr<SdrHdl> pHdl(new SdrHdl(rPnt,SdrHdlKind::Poly));
+ pHdl->SetPolyNum(i);
+ pHdl->SetPointNum(j);
+ pHdl->Set1PixMore(j==0);
+ pHdl->SetSourceHdlNum(nIdx);
+ nIdx++;
+ rHdlList.AddHdl(std::move(pHdl));
+ }
+ }
+ }
+}
+
+void SdrPathObj::AddToPlusHdlList(SdrHdlList& rHdlList, SdrHdl& rHdl) const
+{
+ // keep old stuff to be able to keep old SdrHdl stuff, too
+ const XPolyPolygon aOldPathPolygon(GetPathPoly());
+ sal_uInt16 nPnt = static_cast<sal_uInt16>(rHdl.GetPointNum());
+ sal_uInt16 nPolyNum = static_cast<sal_uInt16>(rHdl.GetPolyNum());
+
+ if (nPolyNum>=aOldPathPolygon.Count())
+ return;
+
+ const XPolygon& rXPoly = aOldPathPolygon[nPolyNum];
+ sal_uInt16 nPntMax = rXPoly.GetPointCount();
+
+ if (nPntMax<=0)
+ return;
+ nPntMax--;
+ if (nPnt>nPntMax)
+ return;
+
+ // calculate the number of plus points
+ sal_uInt16 nCnt = 0;
+ if (rXPoly.GetFlags(nPnt)!=PolyFlags::Control)
+ {
+ if (nPnt==0 && IsClosed())
+ nPnt=nPntMax;
+ if (nPnt>0 && rXPoly.GetFlags(nPnt-1)==PolyFlags::Control)
+ nCnt++;
+ if (nPnt==nPntMax && IsClosed())
+ nPnt=0;
+ if (nPnt<nPntMax && rXPoly.GetFlags(nPnt+1)==PolyFlags::Control)
+ nCnt++;
+ }
+
+ // construct the plus points
+ for (sal_uInt32 nPlusNum = 0; nPlusNum < nCnt; ++nPlusNum)
+ {
+ nPnt = static_cast<sal_uInt16>(rHdl.GetPointNum());
+ std::unique_ptr<SdrHdl> pHdl(new SdrHdlBezWgt(&rHdl));
+ pHdl->SetPolyNum(rHdl.GetPolyNum());
+
+ if (nPnt==0 && IsClosed())
+ nPnt=nPntMax;
+ if (nPnt>0 && rXPoly.GetFlags(nPnt-1)==PolyFlags::Control && nPlusNum==0)
+ {
+ pHdl->SetPos(rXPoly[nPnt-1]);
+ pHdl->SetPointNum(nPnt-1);
+ }
+ else
+ {
+ if (nPnt==nPntMax && IsClosed())
+ nPnt=0;
+ if (nPnt<rXPoly.GetPointCount()-1 && rXPoly.GetFlags(nPnt+1)==PolyFlags::Control)
+ {
+ pHdl->SetPos(rXPoly[nPnt+1]);
+ pHdl->SetPointNum(nPnt+1);
+ }
+ }
+
+ pHdl->SetSourceHdlNum(rHdl.GetSourceHdlNum());
+ pHdl->SetPlusHdl(true);
+ rHdlList.AddHdl(std::move(pHdl));
+ }
+}
+
+// tdf#123321: Make sure that SdrPathObj (e.g. line) has big enough extent for
+// visibility. This is realised by ensuring GetLogicRect() is the same as
+// GetSnapRect() for the SdrPathObj. Other SdrTextObj objects like
+// SdrObjCustomShape will still use a different version of this method that
+// does not consider the rotation. Otherwise, the rotated SdrObjCustomShape
+// would become mistakenly larger after save and reload (tdf#91687).
+// The invocation of the GetLogicRect() method that caused tdf#123321 was in
+// PlcDrawObj::WritePlc().
+const tools::Rectangle &SdrPathObj::GetLogicRect() const
+{
+ return GetSnapRect();
+}
+
+// dragging
+
+bool SdrPathObj::hasSpecialDrag() const
+{
+ return true;
+}
+
+bool SdrPathObj::beginSpecialDrag(SdrDragStat& rDrag) const
+{
+ ImpPathForDragAndCreate aDragAndCreate(*const_cast<SdrPathObj*>(this));
+
+ return aDragAndCreate.beginPathDrag(rDrag);
+}
+
+bool SdrPathObj::applySpecialDrag(SdrDragStat& rDrag)
+{
+ ImpPathForDragAndCreate aDragAndCreate(*this);
+ bool bRetval(aDragAndCreate.beginPathDrag(rDrag));
+
+ if(bRetval)
+ {
+ bRetval = aDragAndCreate.movePathDrag(rDrag);
+ }
+
+ if(bRetval)
+ {
+ bRetval = aDragAndCreate.endPathDrag(rDrag);
+ }
+
+ if(bRetval)
+ {
+ NbcSetPathPoly(aDragAndCreate.getModifiedPolyPolygon());
+ }
+
+ return bRetval;
+}
+
+OUString SdrPathObj::getSpecialDragComment(const SdrDragStat& rDrag) const
+{
+ OUString aRetval;
+
+ if(mpDAC)
+ {
+ // #i103058# also get a comment when in creation
+ const bool bCreateComment(rDrag.GetView() && this == rDrag.GetView()->GetCreateObj());
+
+ if(bCreateComment)
+ {
+ aRetval = mpDAC->getSpecialDragComment(rDrag);
+ }
+ }
+ else
+ {
+ ImpPathForDragAndCreate aDragAndCreate(*const_cast<SdrPathObj*>(this));
+ bool bDidWork(aDragAndCreate.beginPathDrag(rDrag));
+
+ if(bDidWork)
+ {
+ aRetval = aDragAndCreate.getSpecialDragComment(rDrag);
+ }
+ }
+
+ return aRetval;
+}
+
+basegfx::B2DPolyPolygon SdrPathObj::getSpecialDragPoly(const SdrDragStat& rDrag) const
+{
+ basegfx::B2DPolyPolygon aRetval;
+ ImpPathForDragAndCreate aDragAndCreate(*const_cast<SdrPathObj*>(this));
+ bool bDidWork(aDragAndCreate.beginPathDrag(rDrag));
+
+ if(bDidWork)
+ {
+ aRetval = aDragAndCreate.getSpecialDragPoly(rDrag);
+ }
+
+ return aRetval;
+}
+
+// creation
+
+bool SdrPathObj::BegCreate(SdrDragStat& rStat)
+{
+ mpDAC.reset();
+ impGetDAC().BegCreate(rStat);
+ return true;
+}
+
+bool SdrPathObj::MovCreate(SdrDragStat& rStat)
+{
+ return impGetDAC().MovCreate(rStat);
+}
+
+bool SdrPathObj::EndCreate(SdrDragStat& rStat, SdrCreateCmd eCmd)
+{
+ bool bRetval(impGetDAC().EndCreate(rStat, eCmd));
+
+ if(bRetval && mpDAC)
+ {
+ SetPathPoly(mpDAC->getModifiedPolyPolygon());
+
+ // #i75974# Check for AutoClose feature. Moved here from ImpPathForDragAndCreate::EndCreate
+ // to be able to use the type-changing ImpSetClosed method
+ if(!IsClosedObj())
+ {
+ SdrView* pView = rStat.GetView();
+
+ if(pView && !pView->IsUseIncompatiblePathCreateInterface())
+ {
+ OutputDevice* pOut = pView->GetFirstOutputDevice();
+
+ if(pOut)
+ {
+ if(GetPathPoly().count())
+ {
+ const basegfx::B2DPolygon aCandidate(GetPathPoly().getB2DPolygon(0));
+
+ if(aCandidate.count() > 2)
+ {
+ // check distance of first and last point
+ const sal_Int32 nCloseDist(pOut->PixelToLogic(Size(pView->GetAutoCloseDistPix(), 0)).Width());
+ const basegfx::B2DVector aDistVector(aCandidate.getB2DPoint(aCandidate.count() - 1) - aCandidate.getB2DPoint(0));
+
+ if(aDistVector.getLength() <= static_cast<double>(nCloseDist))
+ {
+ // close it
+ ImpSetClosed(true);
+ }
+ }
+ }
+ }
+ }
+ }
+
+ mpDAC.reset();
+ }
+
+ return bRetval;
+}
+
+bool SdrPathObj::BckCreate(SdrDragStat& rStat)
+{
+ return impGetDAC().BckCreate(rStat);
+}
+
+void SdrPathObj::BrkCreate(SdrDragStat& rStat)
+{
+ impGetDAC().BrkCreate(rStat);
+ mpDAC.reset();
+}
+
+// polygons
+
+basegfx::B2DPolyPolygon SdrPathObj::TakeCreatePoly(const SdrDragStat& rDrag) const
+{
+ basegfx::B2DPolyPolygon aRetval;
+
+ if(mpDAC)
+ {
+ aRetval = mpDAC->TakeObjectPolyPolygon(rDrag);
+ aRetval.append(ImpPathForDragAndCreate::TakeDragPolyPolygon(rDrag));
+ }
+
+ return aRetval;
+}
+
+// during drag or create, allow accessing the so-far created/modified polyPolygon
+basegfx::B2DPolyPolygon SdrPathObj::getObjectPolyPolygon(const SdrDragStat& rDrag) const
+{
+ basegfx::B2DPolyPolygon aRetval;
+
+ if(mpDAC)
+ {
+ aRetval = mpDAC->TakeObjectPolyPolygon(rDrag);
+ }
+
+ return aRetval;
+}
+
+basegfx::B2DPolyPolygon SdrPathObj::getDragPolyPolygon(const SdrDragStat& rDrag) const
+{
+ basegfx::B2DPolyPolygon aRetval;
+
+ if(mpDAC)
+ {
+ aRetval = ImpPathForDragAndCreate::TakeDragPolyPolygon(rDrag);
+ }
+
+ return aRetval;
+}
+
+PointerStyle SdrPathObj::GetCreatePointer() const
+{
+ return impGetDAC().GetCreatePointer();
+}
+
+void SdrPathObj::NbcMove(const Size& rSiz)
+{
+ maPathPolygon.transform(basegfx::utils::createTranslateB2DHomMatrix(rSiz.Width(), rSiz.Height()));
+
+ // #i19871# first modify locally, then call parent (to get correct SnapRect with GluePoints)
+ SdrTextObj::NbcMove(rSiz);
+}
+
+void SdrPathObj::NbcResize(const Point& rRef, const Fraction& xFact, const Fraction& yFact)
+{
+ const double fResizeX(xFact);
+ const double fResizeY(yFact);
+
+ if(basegfx::fTools::equal(fResizeX, 1.0) && basegfx::fTools::equal(fResizeY, 1.0))
+ {
+ // tdf#106792 avoid numerical unprecisions: If both scale factors are 1.0, do not
+ // manipulate at all - that may change maGeo rapidly (and wrongly) in
+ // SdrTextObj::NbcResize. Combined with the UNO API trying to not 'apply'
+ // a rotation but to manipulate the existing one, this is fatal. So just
+ // avoid this error as long as we have to deal with imprecise geometry
+ // manipulations
+ return;
+ }
+
+ basegfx::B2DHomMatrix aTrans(basegfx::utils::createTranslateB2DHomMatrix(-rRef.X(), -rRef.Y()));
+ aTrans = basegfx::utils::createScaleTranslateB2DHomMatrix(
+ double(xFact), double(yFact), rRef.X(), rRef.Y()) * aTrans;
+ maPathPolygon.transform(aTrans);
+
+ // #i19871# first modify locally, then call parent (to get correct SnapRect with GluePoints)
+ SdrTextObj::NbcResize(rRef,xFact,yFact);
+}
+
+void SdrPathObj::NbcRotate(const Point& rRef, Degree100 nAngle, double sn, double cs)
+{
+ // Thank JOE, the angles are defined mirrored to the mathematical meanings
+ const basegfx::B2DHomMatrix aTrans(
+ basegfx::utils::createRotateAroundPoint(rRef.X(), rRef.Y(), -toRadians(nAngle)));
+ maPathPolygon.transform(aTrans);
+
+ // #i19871# first modify locally, then call parent (to get correct SnapRect with GluePoints)
+ SdrTextObj::NbcRotate(rRef,nAngle,sn,cs);
+}
+
+void SdrPathObj::NbcShear(const Point& rRefPnt, Degree100 nAngle, double fTan, bool bVShear)
+{
+ basegfx::B2DHomMatrix aTrans(basegfx::utils::createTranslateB2DHomMatrix(-rRefPnt.X(), -rRefPnt.Y()));
+
+ if(bVShear)
+ {
+ // Thank JOE, the angles are defined mirrored to the mathematical meanings
+ aTrans.shearY(-fTan);
+ }
+ else
+ {
+ aTrans.shearX(-fTan);
+ }
+
+ aTrans.translate(rRefPnt.X(), rRefPnt.Y());
+ maPathPolygon.transform(aTrans);
+
+ // #i19871# first modify locally, then call parent (to get correct SnapRect with GluePoints)
+ SdrTextObj::NbcShear(rRefPnt,nAngle,fTan,bVShear);
+}
+
+void SdrPathObj::NbcMirror(const Point& rRefPnt1, const Point& rRefPnt2)
+{
+ const double fDiffX(rRefPnt2.X() - rRefPnt1.X());
+ const double fDiffY(rRefPnt2.Y() - rRefPnt1.Y());
+ const double fRot(atan2(fDiffY, fDiffX));
+ basegfx::B2DHomMatrix aTrans(basegfx::utils::createTranslateB2DHomMatrix(-rRefPnt1.X(), -rRefPnt1.Y()));
+ aTrans.rotate(-fRot);
+ aTrans.scale(1.0, -1.0);
+ aTrans.rotate(fRot);
+ aTrans.translate(rRefPnt1.X(), rRefPnt1.Y());
+ maPathPolygon.transform(aTrans);
+
+ // Do Joe's special handling for lines when mirroring, too
+ ImpForceKind();
+
+ // #i19871# first modify locally, then call parent (to get correct SnapRect with GluePoints)
+ SdrTextObj::NbcMirror(rRefPnt1,rRefPnt2);
+}
+
+void SdrPathObj::TakeUnrotatedSnapRect(tools::Rectangle& rRect) const
+{
+ if(!maGeo.nRotationAngle)
+ {
+ rRect = GetSnapRect();
+ }
+ else
+ {
+ XPolyPolygon aXPP(GetPathPoly());
+ RotateXPoly(aXPP,Point(),-maGeo.mfSinRotationAngle,maGeo.mfCosRotationAngle);
+ rRect=aXPP.GetBoundRect();
+ Point aTmp(rRect.TopLeft());
+ RotatePoint(aTmp,Point(),maGeo.mfSinRotationAngle,maGeo.mfCosRotationAngle);
+ aTmp-=rRect.TopLeft();
+ rRect.Move(aTmp.X(),aTmp.Y());
+ }
+}
+
+void SdrPathObj::RecalcSnapRect()
+{
+ if(GetPathPoly().count())
+ {
+ maSnapRect = lcl_ImpGetBoundRect(GetPathPoly());
+ }
+}
+
+void SdrPathObj::NbcSetSnapRect(const tools::Rectangle& rRect)
+{
+ tools::Rectangle aOld(GetSnapRect());
+ if (aOld.IsEmpty())
+ {
+ Fraction aX(1,1);
+ Fraction aY(1,1);
+ NbcResize(aOld.TopLeft(), aX, aY);
+ NbcMove(Size(rRect.Left() - aOld.Left(), rRect.Top() - aOld.Top()));
+ return;
+ }
+
+ // Take empty into account when calculating scale factors
+ tools::Long nMulX = rRect.IsWidthEmpty() ? 0 : rRect.Right() - rRect.Left();
+
+ tools::Long nDivX = aOld.Right() - aOld.Left();
+
+ // Take empty into account when calculating scale factors
+ tools::Long nMulY = rRect.IsHeightEmpty() ? 0 : rRect.Bottom() - rRect.Top();
+
+ tools::Long nDivY = aOld.Bottom() - aOld.Top();
+ if ( nDivX == 0 ) { nMulX = 1; nDivX = 1; }
+ if ( nDivY == 0 ) { nMulY = 1; nDivY = 1; }
+ if ( nDivX == nMulX ) { nMulX = 1; nDivX = 1; }
+ if ( nDivY == nMulY ) { nMulY = 1; nDivY = 1; }
+ Fraction aX(nMulX,nDivX);
+ Fraction aY(nMulY,nDivY);
+ NbcResize(aOld.TopLeft(), aX, aY);
+ NbcMove(Size(rRect.Left() - aOld.Left(), rRect.Top() - aOld.Top()));
+}
+
+sal_uInt32 SdrPathObj::GetSnapPointCount() const
+{
+ return GetHdlCount();
+}
+
+Point SdrPathObj::GetSnapPoint(sal_uInt32 nSnapPnt) const
+{
+ sal_uInt32 nPoly,nPnt;
+ if(!PolyPolygonEditor::GetRelativePolyPoint(GetPathPoly(), nSnapPnt, nPoly, nPnt))
+ {
+ SAL_WARN("svx", "SdrPathObj::GetSnapPoint: Point nSnapPnt does not exist.");
+ }
+
+ const basegfx::B2DPoint aB2DPoint(GetPathPoly().getB2DPolygon(nPoly).getB2DPoint(nPnt));
+ return Point(FRound(aB2DPoint.getX()), FRound(aB2DPoint.getY()));
+}
+
+bool SdrPathObj::IsPolyObj() const
+{
+ return true;
+}
+
+sal_uInt32 SdrPathObj::GetPointCount() const
+{
+ sal_uInt32 nRetval(0);
+
+ for(auto const& rPolygon : GetPathPoly())
+ {
+ nRetval += rPolygon.count();
+ }
+
+ return nRetval;
+}
+
+Point SdrPathObj::GetPoint(sal_uInt32 nHdlNum) const
+{
+ Point aRetval;
+ sal_uInt32 nPoly,nPnt;
+
+ if(PolyPolygonEditor::GetRelativePolyPoint(GetPathPoly(), nHdlNum, nPoly, nPnt))
+ {
+ const basegfx::B2DPolygon aPoly(GetPathPoly().getB2DPolygon(nPoly));
+ const basegfx::B2DPoint aPoint(aPoly.getB2DPoint(nPnt));
+ aRetval = Point(FRound(aPoint.getX()), FRound(aPoint.getY()));
+ }
+
+ return aRetval;
+}
+
+void SdrPathObj::NbcSetPoint(const Point& rPnt, sal_uInt32 nHdlNum)
+{
+ sal_uInt32 nPoly,nPnt;
+
+ if(!PolyPolygonEditor::GetRelativePolyPoint(GetPathPoly(), nHdlNum, nPoly, nPnt))
+ return;
+
+ basegfx::B2DPolygon aNewPolygon(GetPathPoly().getB2DPolygon(nPoly));
+ aNewPolygon.setB2DPoint(nPnt, basegfx::B2DPoint(rPnt.X(), rPnt.Y()));
+ maPathPolygon.setB2DPolygon(nPoly, aNewPolygon);
+
+ if(meKind==SdrObjKind::Line)
+ {
+ ImpForceLineAngle();
+ }
+ else
+ {
+ if(GetPathPoly().count())
+ {
+ // #i10659# for SdrTextObj, keep aRect up to date
+ maRect = lcl_ImpGetBoundRect(GetPathPoly());
+ }
+ }
+
+ SetBoundAndSnapRectsDirty();
+}
+
+sal_uInt32 SdrPathObj::NbcInsPointOld(const Point& rPos, bool bNewObj)
+{
+ sal_uInt32 nNewHdl;
+
+ if(bNewObj)
+ {
+ nNewHdl = NbcInsPoint(rPos, true);
+ }
+ else
+ {
+ // look for smallest distance data
+ const basegfx::B2DPoint aTestPoint(rPos.X(), rPos.Y());
+ sal_uInt32 nSmallestPolyIndex(0);
+ sal_uInt32 nSmallestEdgeIndex(0);
+ double fSmallestCut;
+ basegfx::utils::getSmallestDistancePointToPolyPolygon(GetPathPoly(), aTestPoint, nSmallestPolyIndex, nSmallestEdgeIndex, fSmallestCut);
+
+ nNewHdl = NbcInsPoint(rPos, false);
+ }
+
+ ImpForceKind();
+ return nNewHdl;
+}
+
+sal_uInt32 SdrPathObj::NbcInsPoint(const Point& rPos, bool bNewObj)
+{
+ sal_uInt32 nNewHdl;
+
+ if(bNewObj)
+ {
+ basegfx::B2DPolygon aNewPoly;
+ const basegfx::B2DPoint aPoint(rPos.X(), rPos.Y());
+ aNewPoly.append(aPoint);
+ aNewPoly.setClosed(IsClosed());
+ maPathPolygon.append(aNewPoly);
+ SetBoundAndSnapRectsDirty();
+ nNewHdl = GetHdlCount();
+ }
+ else
+ {
+ // look for smallest distance data
+ const basegfx::B2DPoint aTestPoint(rPos.X(), rPos.Y());
+ sal_uInt32 nSmallestPolyIndex(0);
+ sal_uInt32 nSmallestEdgeIndex(0);
+ double fSmallestCut;
+ basegfx::utils::getSmallestDistancePointToPolyPolygon(GetPathPoly(), aTestPoint, nSmallestPolyIndex, nSmallestEdgeIndex, fSmallestCut);
+ basegfx::B2DPolygon aCandidate(GetPathPoly().getB2DPolygon(nSmallestPolyIndex));
+ const bool bBefore(!aCandidate.isClosed() && 0 == nSmallestEdgeIndex && 0.0 == fSmallestCut);
+ const bool bAfter(!aCandidate.isClosed() && aCandidate.count() == nSmallestEdgeIndex + 2 && 1.0 == fSmallestCut);
+
+ if(bBefore)
+ {
+ // before first point
+ aCandidate.insert(0, aTestPoint);
+
+ if(aCandidate.areControlPointsUsed())
+ {
+ if(aCandidate.isNextControlPointUsed(1))
+ {
+ aCandidate.setNextControlPoint(0, interpolate(aTestPoint, aCandidate.getB2DPoint(1), (1.0 / 3.0)));
+ aCandidate.setPrevControlPoint(1, interpolate(aTestPoint, aCandidate.getB2DPoint(1), (2.0 / 3.0)));
+ }
+ }
+
+ nNewHdl = 0;
+ }
+ else if(bAfter)
+ {
+ // after last point
+ aCandidate.append(aTestPoint);
+
+ if(aCandidate.areControlPointsUsed())
+ {
+ if(aCandidate.isPrevControlPointUsed(aCandidate.count() - 2))
+ {
+ aCandidate.setNextControlPoint(aCandidate.count() - 2, interpolate(aCandidate.getB2DPoint(aCandidate.count() - 2), aTestPoint, (1.0 / 3.0)));
+ aCandidate.setPrevControlPoint(aCandidate.count() - 1, interpolate(aCandidate.getB2DPoint(aCandidate.count() - 2), aTestPoint, (2.0 / 3.0)));
+ }
+ }
+
+ nNewHdl = aCandidate.count() - 1;
+ }
+ else
+ {
+ // in between
+ bool bSegmentSplit(false);
+ const sal_uInt32 nNextIndex((nSmallestEdgeIndex + 1) % aCandidate.count());
+
+ if(aCandidate.areControlPointsUsed())
+ {
+ if(aCandidate.isNextControlPointUsed(nSmallestEdgeIndex) || aCandidate.isPrevControlPointUsed(nNextIndex))
+ {
+ bSegmentSplit = true;
+ }
+ }
+
+ if(bSegmentSplit)
+ {
+ // rebuild original segment to get the split data
+ basegfx::B2DCubicBezier aBezierA, aBezierB;
+ const basegfx::B2DCubicBezier aBezier(
+ aCandidate.getB2DPoint(nSmallestEdgeIndex),
+ aCandidate.getNextControlPoint(nSmallestEdgeIndex),
+ aCandidate.getPrevControlPoint(nNextIndex),
+ aCandidate.getB2DPoint(nNextIndex));
+
+ // split and insert hit point
+ aBezier.split(fSmallestCut, &aBezierA, &aBezierB);
+ aCandidate.insert(nSmallestEdgeIndex + 1, aTestPoint);
+
+ // since we inserted hit point and not split point, we need to add an offset
+ // to the control points to get the C1 continuity we want to achieve
+ const basegfx::B2DVector aOffset(aTestPoint - aBezierA.getEndPoint());
+ aCandidate.setNextControlPoint(nSmallestEdgeIndex, aBezierA.getControlPointA() + aOffset);
+ aCandidate.setPrevControlPoint(nSmallestEdgeIndex + 1, aBezierA.getControlPointB() + aOffset);
+ aCandidate.setNextControlPoint(nSmallestEdgeIndex + 1, aBezierB.getControlPointA() + aOffset);
+ aCandidate.setPrevControlPoint((nSmallestEdgeIndex + 2) % aCandidate.count(), aBezierB.getControlPointB() + aOffset);
+ }
+ else
+ {
+ aCandidate.insert(nSmallestEdgeIndex + 1, aTestPoint);
+ }
+
+ nNewHdl = nSmallestEdgeIndex + 1;
+ }
+
+ maPathPolygon.setB2DPolygon(nSmallestPolyIndex, aCandidate);
+
+ // create old polygon index from it
+ for(sal_uInt32 a(0); a < nSmallestPolyIndex; a++)
+ {
+ nNewHdl += GetPathPoly().getB2DPolygon(a).count();
+ }
+ }
+
+ ImpForceKind();
+ return nNewHdl;
+}
+
+SdrObject* SdrPathObj::RipPoint(sal_uInt32 nHdlNum, sal_uInt32& rNewPt0Index)
+{
+ SdrPathObj* pNewObj = nullptr;
+ const basegfx::B2DPolyPolygon aLocalPolyPolygon(GetPathPoly());
+ sal_uInt32 nPoly, nPnt;
+
+ if(PolyPolygonEditor::GetRelativePolyPoint(aLocalPolyPolygon, nHdlNum, nPoly, nPnt))
+ {
+ if(0 == nPoly)
+ {
+ const basegfx::B2DPolygon& aCandidate(aLocalPolyPolygon.getB2DPolygon(nPoly));
+ const sal_uInt32 nPointCount(aCandidate.count());
+
+ if(nPointCount)
+ {
+ if(IsClosed())
+ {
+ // when closed, RipPoint means to open the polygon at the selected point. To
+ // be able to do that, it is necessary to make the selected point the first one
+ basegfx::B2DPolygon aNewPolygon(basegfx::utils::makeStartPoint(aCandidate, nPnt));
+ SetPathPoly(basegfx::B2DPolyPolygon(aNewPolygon));
+ ToggleClosed();
+
+ // give back new position of old start point (historical reasons)
+ rNewPt0Index = (nPointCount - nPnt) % nPointCount;
+ }
+ else
+ {
+ if(nPointCount >= 3 && nPnt != 0 && nPnt + 1 < nPointCount)
+ {
+ // split in two objects at point nPnt
+ basegfx::B2DPolygon aSplitPolyA(aCandidate, 0, nPnt + 1);
+ SetPathPoly(basegfx::B2DPolyPolygon(aSplitPolyA));
+
+ pNewObj = CloneSdrObject(getSdrModelFromSdrObject());
+ basegfx::B2DPolygon aSplitPolyB(aCandidate, nPnt, nPointCount - nPnt);
+ pNewObj->SetPathPoly(basegfx::B2DPolyPolygon(aSplitPolyB));
+ }
+ }
+ }
+ }
+ }
+
+ return pNewObj;
+}
+
+SdrObjectUniquePtr SdrPathObj::DoConvertToPolyObj(bool bBezier, bool bAddText) const
+{
+ // #i89784# check for FontWork with activated HideContour
+ const drawinglayer::attribute::SdrTextAttribute aText(
+ drawinglayer::primitive2d::createNewSdrTextAttribute(GetObjectItemSet(), *getText(0)));
+ const bool bHideContour(
+ !aText.isDefault() && !aText.getSdrFormTextAttribute().isDefault() && aText.isHideContour());
+
+ SdrObjectUniquePtr pRet;
+
+ if(!bHideContour)
+ {
+ SdrPathObjUniquePtr pPath = ImpConvertMakeObj(GetPathPoly(), IsClosed(), bBezier);
+
+ if(pPath->GetPathPoly().areControlPointsUsed())
+ {
+ if(!bBezier)
+ {
+ // reduce all bezier curves
+ pPath->SetPathPoly(basegfx::utils::adaptiveSubdivideByAngle(pPath->GetPathPoly()));
+ }
+ }
+ else
+ {
+ if(bBezier)
+ {
+ // create bezier curves
+ pPath->SetPathPoly(basegfx::utils::expandToCurve(pPath->GetPathPoly()));
+ }
+ }
+ pRet = std::move(pPath);
+ }
+
+ if(bAddText)
+ {
+ pRet = ImpConvertAddText(std::move(pRet), bBezier);
+ }
+
+ return pRet;
+}
+
+std::unique_ptr<SdrObjGeoData> SdrPathObj::NewGeoData() const
+{
+ return std::make_unique<SdrPathObjGeoData>();
+}
+
+void SdrPathObj::SaveGeoData(SdrObjGeoData& rGeo) const
+{
+ SdrTextObj::SaveGeoData(rGeo);
+ SdrPathObjGeoData& rPGeo = static_cast<SdrPathObjGeoData&>( rGeo );
+ rPGeo.maPathPolygon=GetPathPoly();
+ rPGeo.meKind=meKind;
+}
+
+void SdrPathObj::RestoreGeoData(const SdrObjGeoData& rGeo)
+{
+ SdrTextObj::RestoreGeoData(rGeo);
+ const SdrPathObjGeoData& rPGeo=static_cast<const SdrPathObjGeoData&>(rGeo);
+ maPathPolygon=rPGeo.maPathPolygon;
+ meKind=rPGeo.meKind;
+ ImpForceKind(); // to set bClosed (among other things)
+}
+
+void SdrPathObj::NbcSetPathPoly(const basegfx::B2DPolyPolygon& rPathPoly)
+{
+ if(GetPathPoly() != rPathPoly)
+ {
+ maPathPolygon=rPathPoly;
+ ImpForceKind();
+ SetBoundAndSnapRectsDirty();
+ }
+}
+
+void SdrPathObj::SetPathPoly(const basegfx::B2DPolyPolygon& rPathPoly)
+{
+ if(GetPathPoly() != rPathPoly)
+ {
+ tools::Rectangle aBoundRect0; if (m_pUserCall!=nullptr) aBoundRect0=GetLastBoundRect();
+ NbcSetPathPoly(rPathPoly);
+ SetChanged();
+ BroadcastObjectChange();
+ SendUserCall(SdrUserCallType::Resize,aBoundRect0);
+ }
+}
+
+void SdrPathObj::ToggleClosed()
+{
+ tools::Rectangle aBoundRect0;
+ if(m_pUserCall != nullptr)
+ aBoundRect0 = GetLastBoundRect();
+ ImpSetClosed(!IsClosed()); // set new ObjKind
+ ImpForceKind(); // because we want Line -> Poly -> PolyLine instead of Line -> Poly -> Line
+ SetBoundAndSnapRectsDirty();
+ SetChanged();
+ BroadcastObjectChange();
+ SendUserCall(SdrUserCallType::Resize, aBoundRect0);
+}
+
+ImpPathForDragAndCreate& SdrPathObj::impGetDAC() const
+{
+ if(!mpDAC)
+ {
+ const_cast<SdrPathObj*>(this)->mpDAC.reset(new ImpPathForDragAndCreate(*const_cast<SdrPathObj*>(this)));
+ }
+
+ return *mpDAC;
+}
+
+
+// transformation interface for StarOfficeAPI. This implements support for
+// homogeneous 3x3 matrices containing the transformation of the SdrObject. At the
+// moment it contains a shearX, rotation and translation, but for setting all linear
+// transforms like Scale, ShearX, ShearY, Rotate and Translate are supported.
+
+
+// gets base transformation and rectangle of object. If it's an SdrPathObj it fills the PolyPolygon
+// with the base geometry and returns TRUE. Otherwise it returns FALSE.
+bool SdrPathObj::TRGetBaseGeometry(basegfx::B2DHomMatrix& rMatrix, basegfx::B2DPolyPolygon& rPolyPolygon) const
+{
+ double fRotate(0.0);
+ double fShearX(0.0);
+ basegfx::B2DTuple aScale(1.0, 1.0);
+ basegfx::B2DTuple aTranslate(0.0, 0.0);
+
+ if(GetPathPoly().count())
+ {
+ // copy geometry
+ basegfx::B2DHomMatrix aMoveToZeroMatrix;
+ rPolyPolygon = GetPathPoly();
+
+ if(SdrObjKind::Line == meKind)
+ {
+ // ignore shear and rotate, just use scale and translate
+ OSL_ENSURE(GetPathPoly().count() > 0 && GetPathPoly().getB2DPolygon(0).count() > 1, "OBJ_LINE with too few polygons (!)");
+ // #i72287# use polygon without control points for range calculation. Do not change rPolyPolygon
+ // itself, else this method will no longer return the full polygon information (curve will
+ // be lost)
+ const basegfx::B2DRange aPolyRangeNoCurve(basegfx::utils::getRange(rPolyPolygon));
+ aScale = aPolyRangeNoCurve.getRange();
+ aTranslate = aPolyRangeNoCurve.getMinimum();
+
+ // define matrix for move polygon to zero point
+ aMoveToZeroMatrix.translate(-aTranslate.getX(), -aTranslate.getY());
+ }
+ else
+ {
+ if(maGeo.nShearAngle || maGeo.nRotationAngle)
+ {
+ // get rotate and shear in drawingLayer notation
+ fRotate = toRadians(maGeo.nRotationAngle);
+ fShearX = toRadians(maGeo.nShearAngle);
+
+ // build mathematically correct (negative shear and rotate) object transform
+ // containing shear and rotate to extract unsheared, unrotated polygon
+ basegfx::B2DHomMatrix aObjectMatrix;
+ aObjectMatrix.shearX(-maGeo.mfTanShearAngle);
+ aObjectMatrix.rotate(toRadians(36000_deg100 - maGeo.nRotationAngle));
+
+ // create inverse from it and back-transform polygon
+ basegfx::B2DHomMatrix aInvObjectMatrix(aObjectMatrix);
+ aInvObjectMatrix.invert();
+ rPolyPolygon.transform(aInvObjectMatrix);
+
+ // get range from unsheared, unrotated polygon and extract scale and translate.
+ // transform topLeft from it back to transformed state to get original
+ // topLeft (rotation center)
+ // #i72287# use polygon without control points for range calculation. Do not change rPolyPolygon
+ // itself, else this method will no longer return the full polygon information (curve will
+ // be lost)
+ const basegfx::B2DRange aCorrectedRangeNoCurve(basegfx::utils::getRange(rPolyPolygon));
+ aTranslate = aObjectMatrix * aCorrectedRangeNoCurve.getMinimum();
+ aScale = aCorrectedRangeNoCurve.getRange();
+
+ // define matrix for move polygon to zero point
+ // #i112280# Added missing minus for Y-Translation
+ aMoveToZeroMatrix.translate(-aCorrectedRangeNoCurve.getMinX(), -aCorrectedRangeNoCurve.getMinY());
+ }
+ else
+ {
+ // get scale and translate from unsheared, unrotated polygon
+ // #i72287# use polygon without control points for range calculation. Do not change rPolyPolygon
+ // itself, else this method will no longer return the full polygon information (curve will
+ // be lost)
+ const basegfx::B2DRange aPolyRangeNoCurve(basegfx::utils::getRange(rPolyPolygon));
+ aScale = aPolyRangeNoCurve.getRange();
+ aTranslate = aPolyRangeNoCurve.getMinimum();
+
+ // define matrix for move polygon to zero point
+ aMoveToZeroMatrix.translate(-aTranslate.getX(), -aTranslate.getY());
+ }
+ }
+
+ // move polygon to zero point with pre-defined matrix
+ rPolyPolygon.transform(aMoveToZeroMatrix);
+ }
+
+ // position maybe relative to anchorpos, convert
+ if( getSdrModelFromSdrObject().IsWriter() )
+ {
+ if(GetAnchorPos().X() || GetAnchorPos().Y())
+ {
+ aTranslate -= basegfx::B2DTuple(GetAnchorPos().X(), GetAnchorPos().Y());
+ }
+ }
+
+ // build return value matrix
+ rMatrix = basegfx::utils::createScaleShearXRotateTranslateB2DHomMatrix(
+ aScale,
+ basegfx::fTools::equalZero(fShearX) ? 0.0 : tan(fShearX),
+ basegfx::fTools::equalZero(fRotate) ? 0.0 : -fRotate,
+ aTranslate);
+
+ return true;
+}
+
+void SdrPathObj::SetHandleScale(bool bHandleScale)
+{
+ mbHandleScale = bHandleScale;
+}
+
+// Sets the base geometry of the object using infos contained in the homogeneous 3x3 matrix.
+// If it's an SdrPathObj it will use the provided geometry information. The Polygon has
+// to use (0,0) as upper left and will be scaled to the given size in the matrix.
+void SdrPathObj::TRSetBaseGeometry(const basegfx::B2DHomMatrix& rMatrix, const basegfx::B2DPolyPolygon& rPolyPolygon)
+{
+ // break up matrix
+ basegfx::B2DTuple aScale;
+ basegfx::B2DTuple aTranslate;
+ double fRotate, fShearX;
+ rMatrix.decompose(aScale, aTranslate, fRotate, fShearX);
+
+ // #i75086# Old DrawingLayer (GeoStat and geometry) does not support holding negative scalings
+ // in X and Y which equal a 180 degree rotation. Recognize it and react accordingly
+ if(basegfx::fTools::less(aScale.getX(), 0.0) && basegfx::fTools::less(aScale.getY(), 0.0))
+ {
+ aScale.setX(fabs(aScale.getX()));
+ aScale.setY(fabs(aScale.getY()));
+ fRotate = fmod(fRotate + M_PI, 2 * M_PI);
+ }
+
+ // copy poly
+ basegfx::B2DPolyPolygon aNewPolyPolygon(rPolyPolygon);
+
+ // reset object shear and rotations
+ maGeo.nRotationAngle = 0_deg100;
+ maGeo.RecalcSinCos();
+ maGeo.nShearAngle = 0_deg100;
+ maGeo.RecalcTan();
+
+ if( getSdrModelFromSdrObject().IsWriter() )
+ {
+ // if anchor is used, make position relative to it
+ if(GetAnchorPos().X() || GetAnchorPos().Y())
+ {
+ aTranslate += basegfx::B2DTuple(GetAnchorPos().X(), GetAnchorPos().Y());
+ }
+ }
+
+ // create transformation for polygon, set values at maGeo direct
+ basegfx::B2DHomMatrix aTransform;
+
+ // #i75086#
+ // Given polygon is already scaled (for historical reasons), but not mirrored yet.
+ // Thus, when scale is negative in X or Y, apply the needed mirroring accordingly.
+ double fScaleX(basegfx::fTools::less(aScale.getX(), 0.0) ? -1.0 : 1.0);
+ double fScaleY(basegfx::fTools::less(aScale.getY(), 0.0) ? -1.0 : 1.0);
+
+ // tdf#98565, tdf#98584. While loading a shape, svg:width and svg:height is used to scale
+ // the polygon. But draw:transform might introduce additional scaling factors, which need to
+ // be applied to the polygon too, so aScale cannot be ignored while loading.
+ // I use "maSnapRect.IsEmpty() && GetPathPoly().count()" to detect this case. Any better
+ // idea? The behavior in other cases is the same as it was before this fix.
+ if (maSnapRect.IsEmpty() && GetPathPoly().count() && mbHandleScale)
+ {
+ // In case of a Writer document, the scaling factors were converted to twips. That is not
+ // correct here, because width and height are already in the points coordinates and aScale
+ // is no length but only a factor here. Convert back.
+ if (getSdrModelFromSdrObject().IsWriter())
+ {
+ aScale.setX(o3tl::convert(aScale.getX(), o3tl::Length::twip, o3tl::Length::mm100));
+ aScale.setY(o3tl::convert(aScale.getY(), o3tl::Length::twip, o3tl::Length::mm100));
+ }
+ fScaleX *= fabs(aScale.getX());
+ fScaleY *= fabs(aScale.getY());
+ }
+
+ if (fScaleX != 1.0 || fScaleY != 1.0)
+ aTransform.scale(fScaleX, fScaleY);
+
+ if(!basegfx::fTools::equalZero(fShearX))
+ {
+ aTransform.shearX(tan(-atan(fShearX)));
+ maGeo.nShearAngle = Degree100(FRound(basegfx::rad2deg<100>(atan(fShearX))));
+ maGeo.RecalcTan();
+ }
+
+ if(!basegfx::fTools::equalZero(fRotate))
+ {
+ // #i78696#
+ // fRotate is mathematically correct for linear transformations, so it's
+ // the one to use for the geometry change
+ aTransform.rotate(fRotate);
+
+ // #i78696#
+ // fRotate is mathematically correct, but aGeoStat.nRotationAngle is
+ // mirrored -> mirror value here
+ maGeo.nRotationAngle = NormAngle36000(Degree100(FRound(-basegfx::rad2deg<100>(fRotate))));
+ maGeo.RecalcSinCos();
+ }
+
+ if(!aTranslate.equalZero())
+ {
+ // #i39529# absolute positioning, so get current position (without control points (!))
+ const basegfx::B2DRange aCurrentRange(basegfx::utils::getRange(aNewPolyPolygon));
+ aTransform.translate(aTranslate.getX() - aCurrentRange.getMinX(), aTranslate.getY() - aCurrentRange.getMinY());
+ }
+
+ // transform polygon and trigger change
+ aNewPolyPolygon.transform(aTransform);
+ SetPathPoly(aNewPolyPolygon);
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/svdraw/svdorect.cxx b/svx/source/svdraw/svdorect.cxx
new file mode 100644
index 000000000..2674464d2
--- /dev/null
+++ b/svx/source/svdraw/svdorect.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/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * 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/svdorect.hxx>
+#include <svx/xpoly.hxx>
+#include <svx/svdtrans.hxx>
+#include <svx/svddrag.hxx>
+#include <svx/svdmodel.hxx>
+#include <svx/svdview.hxx>
+#include <svx/svdopath.hxx>
+#include <svx/dialmgr.hxx>
+#include <svx/strings.hrc>
+#include <sdr/properties/rectangleproperties.hxx>
+#include <sdr/contact/viewcontactofsdrrectobj.hxx>
+#include <tools/debug.hxx>
+#include <vcl/ptrstyle.hxx>
+#include <osl/diagnose.h>
+
+using namespace com::sun::star;
+
+// BaseProperties section
+
+std::unique_ptr<sdr::properties::BaseProperties> SdrRectObj::CreateObjectSpecificProperties()
+{
+ return std::make_unique<sdr::properties::RectangleProperties>(*this);
+}
+
+
+// DrawContact section
+
+std::unique_ptr<sdr::contact::ViewContact> SdrRectObj::CreateObjectSpecificViewContact()
+{
+ return std::make_unique<sdr::contact::ViewContactOfSdrRectObj>(*this);
+}
+
+
+SdrRectObj::SdrRectObj(SdrModel& rSdrModel)
+: SdrTextObj(rSdrModel)
+{
+ m_bClosedObj=true;
+}
+
+SdrRectObj::SdrRectObj(SdrModel& rSdrModel, SdrRectObj const & rSource)
+: SdrTextObj(rSdrModel, rSource)
+{
+ m_bClosedObj=true;
+ mpXPoly = rSource.mpXPoly;
+}
+
+SdrRectObj::SdrRectObj(
+ SdrModel& rSdrModel,
+ const tools::Rectangle& rRect)
+: SdrTextObj(rSdrModel, rRect)
+{
+ m_bClosedObj=true;
+}
+
+SdrRectObj::SdrRectObj(
+ SdrModel& rSdrModel,
+ SdrObjKind eNewTextKind)
+: SdrTextObj(rSdrModel, eNewTextKind)
+{
+ DBG_ASSERT(meTextKind == SdrObjKind::Text ||
+ meTextKind == SdrObjKind::OutlineText || meTextKind == SdrObjKind::TitleText,
+ "SdrRectObj::SdrRectObj(SdrObjKind) can only be applied to text frames.");
+ m_bClosedObj=true;
+}
+
+SdrRectObj::SdrRectObj(
+ SdrModel& rSdrModel,
+ SdrObjKind eNewTextKind,
+ const tools::Rectangle& rRect)
+: SdrTextObj(rSdrModel, eNewTextKind, rRect)
+{
+ DBG_ASSERT(meTextKind == SdrObjKind::Text ||
+ meTextKind == SdrObjKind::OutlineText || meTextKind == SdrObjKind::TitleText,
+ "SdrRectObj::SdrRectObj(SdrObjKind,...) can only be applied to text frames.");
+ m_bClosedObj=true;
+}
+
+SdrRectObj::~SdrRectObj()
+{
+}
+
+void SdrRectObj::SetXPolyDirty()
+{
+ mpXPoly.reset();
+}
+
+XPolygon SdrRectObj::ImpCalcXPoly(const tools::Rectangle& rRect1, tools::Long nRad1) const
+{
+ XPolygon aXPoly(rRect1,nRad1,nRad1);
+ const sal_uInt16 nPointCnt(aXPoly.GetPointCount());
+ XPolygon aNewPoly(nPointCnt+1);
+ sal_uInt16 nShift=nPointCnt-2;
+ if (nRad1!=0) nShift=nPointCnt-5;
+ sal_uInt16 j=nShift;
+ for (sal_uInt16 i=1; i<nPointCnt; i++) {
+ aNewPoly[i]=aXPoly[j];
+ aNewPoly.SetFlags(i,aXPoly.GetFlags(j));
+ j++;
+ if (j>=nPointCnt) j=1;
+ }
+ aNewPoly[0]=rRect1.BottomCenter();
+ aNewPoly[nPointCnt]=aNewPoly[0];
+ aXPoly=aNewPoly;
+
+ // these angles always relate to the top left corner of aRect
+ if (maGeo.nShearAngle) ShearXPoly(aXPoly,maRect.TopLeft(),maGeo.mfTanShearAngle);
+ if (maGeo.nRotationAngle) RotateXPoly(aXPoly,maRect.TopLeft(),maGeo.mfSinRotationAngle,maGeo.mfCosRotationAngle);
+ return aXPoly;
+}
+
+void SdrRectObj::RecalcXPoly()
+{
+ mpXPoly = ImpCalcXPoly(maRect,GetEckenradius());
+}
+
+const XPolygon& SdrRectObj::GetXPoly() const
+{
+ if(!mpXPoly)
+ {
+ const_cast<SdrRectObj*>(this)->RecalcXPoly();
+ }
+
+ return *mpXPoly;
+}
+
+void SdrRectObj::TakeObjInfo(SdrObjTransformInfoRec& rInfo) const
+{
+ bool bNoTextFrame=!IsTextFrame();
+ rInfo.bResizeFreeAllowed=bNoTextFrame || ((maGeo.nRotationAngle.get() % 9000) == 0);
+ rInfo.bResizePropAllowed=true;
+ rInfo.bRotateFreeAllowed=true;
+ rInfo.bRotate90Allowed =true;
+ rInfo.bMirrorFreeAllowed=bNoTextFrame;
+ rInfo.bMirror45Allowed =bNoTextFrame;
+ rInfo.bMirror90Allowed =bNoTextFrame;
+
+ // allow transparency
+ rInfo.bTransparenceAllowed = true;
+
+ rInfo.bShearAllowed =bNoTextFrame;
+ rInfo.bEdgeRadiusAllowed=true;
+
+ bool bCanConv=!HasText() || ImpCanConvTextToCurve();
+ if (bCanConv && !bNoTextFrame && !HasText()) {
+ bCanConv=HasFill() || HasLine();
+ }
+ rInfo.bCanConvToPath =bCanConv;
+ rInfo.bCanConvToPoly =bCanConv;
+ rInfo.bCanConvToContour = (rInfo.bCanConvToPoly || LineGeometryUsageIsNecessary());
+}
+
+SdrObjKind SdrRectObj::GetObjIdentifier() const
+{
+ if (IsTextFrame())
+ return meTextKind;
+ else return SdrObjKind::Rectangle;
+}
+
+void SdrRectObj::TakeUnrotatedSnapRect(tools::Rectangle& rRect) const
+{
+ rRect = maRect;
+ if (maGeo.nShearAngle==0_deg100)
+ return;
+
+ tools::Long nDst=FRound((maRect.Bottom()-maRect.Top()) * maGeo.mfTanShearAngle);
+ if (maGeo.nShearAngle>0_deg100)
+ {
+ Point aRef(rRect.TopLeft());
+ rRect.AdjustLeft( -nDst );
+ Point aTmpPt(rRect.TopLeft());
+ RotatePoint(aTmpPt,aRef,maGeo.mfSinRotationAngle,maGeo.mfCosRotationAngle);
+ aTmpPt-=rRect.TopLeft();
+ rRect.Move(aTmpPt.X(),aTmpPt.Y());
+ }
+ else
+ {
+ rRect.AdjustRight( -nDst );
+ }
+}
+
+OUString SdrRectObj::TakeObjNameSingul() const
+{
+ if (IsTextFrame())
+ {
+ return SdrTextObj::TakeObjNameSingul();
+ }
+
+ bool bRounded = GetEckenradius() != 0; // rounded down
+ TranslateId pResId = bRounded ? STR_ObjNameSingulRECTRND : STR_ObjNameSingulRECT;
+ if (maGeo.nShearAngle)
+ {
+ pResId = bRounded ? STR_ObjNameSingulPARALRND : STR_ObjNameSingulPARAL; // parallelogram or, maybe, rhombus
+ }
+ else if (maRect.GetWidth() == maRect.GetHeight())
+ {
+ pResId = bRounded ? STR_ObjNameSingulQUADRND : STR_ObjNameSingulQUAD; // square
+ }
+ OUString sName(SvxResId(pResId));
+
+ OUString aName(GetName());
+ if (!aName.isEmpty())
+ sName += " '" + aName + "'";
+
+ return sName;
+}
+
+OUString SdrRectObj::TakeObjNamePlural() const
+{
+ if (IsTextFrame())
+ {
+ return SdrTextObj::TakeObjNamePlural();
+ }
+
+ bool bRounded = GetEckenradius() != 0; // rounded down
+ TranslateId pResId = bRounded ? STR_ObjNamePluralRECTRND : STR_ObjNamePluralRECT;
+ if (maGeo.nShearAngle)
+ {
+ pResId = bRounded ? STR_ObjNamePluralPARALRND : STR_ObjNamePluralPARAL; // parallelogram or rhombus
+ }
+ else if (maRect.GetWidth() == maRect.GetHeight())
+ {
+ pResId = bRounded ? STR_ObjNamePluralQUADRND : STR_ObjNamePluralQUAD; // square
+ }
+
+ return SvxResId(pResId);
+}
+
+SdrRectObj* SdrRectObj::CloneSdrObject(SdrModel& rTargetModel) const
+{
+ return new SdrRectObj(rTargetModel, *this);
+}
+
+basegfx::B2DPolyPolygon SdrRectObj::TakeXorPoly() const
+{
+ XPolyPolygon aXPP;
+ aXPP.Insert(ImpCalcXPoly(maRect,GetEckenradius()));
+ return aXPP.getB2DPolyPolygon();
+}
+
+void SdrRectObj::RecalcSnapRect()
+{
+ tools::Long nEckRad=GetEckenradius();
+ if ((maGeo.nRotationAngle || maGeo.nShearAngle) && nEckRad!=0) {
+ maSnapRect=GetXPoly().GetBoundRect();
+ } else {
+ SdrTextObj::RecalcSnapRect();
+ }
+}
+
+void SdrRectObj::NbcSetSnapRect(const tools::Rectangle& rRect)
+{
+ SdrTextObj::NbcSetSnapRect(rRect);
+ SetXPolyDirty();
+}
+
+void SdrRectObj::NbcSetLogicRect(const tools::Rectangle& rRect)
+{
+ SdrTextObj::NbcSetLogicRect(rRect);
+ SetXPolyDirty();
+}
+
+sal_uInt32 SdrRectObj::GetHdlCount() const
+{
+ return IsTextFrame() ? 10 : 9;
+}
+
+void SdrRectObj::AddToHdlList(SdrHdlList& rHdlList) const
+{
+ // A text box has an additional (pseudo-)handle for the blinking frame.
+ if(IsTextFrame())
+ {
+ OSL_ENSURE(!IsTextEditActive(), "Do not use an ImpTextframeHdl for highlighting text in active text edit, this will collide with EditEngine paints (!)");
+ std::unique_ptr<SdrHdl> pH(new ImpTextframeHdl(maRect));
+ pH->SetObj(const_cast<SdrRectObj*>(this));
+ pH->SetRotationAngle(maGeo.nRotationAngle);
+ rHdlList.AddHdl(std::move(pH));
+ }
+
+ for(sal_Int32 nHdlNum = 1; nHdlNum <= 9; ++nHdlNum)
+ {
+ Point aPnt;
+ SdrHdlKind eKind = SdrHdlKind::Move;
+
+ switch(nHdlNum)
+ {
+ case 1: // Handle for changing the corner radius
+ {
+ tools::Long a = GetEckenradius();
+ tools::Long b = std::max(maRect.GetWidth(),maRect.GetHeight())/2; // rounded up, because GetWidth() adds 1
+ if (a>b) a=b;
+ if (a<0) a=0;
+ aPnt=maRect.TopLeft();
+ aPnt.AdjustX(a );
+ eKind = SdrHdlKind::Circle;
+ break;
+ }
+ case 2: aPnt=maRect.TopLeft(); eKind = SdrHdlKind::UpperLeft; break;
+ case 3: aPnt=maRect.TopCenter(); eKind = SdrHdlKind::Upper; break;
+ case 4: aPnt=maRect.TopRight(); eKind = SdrHdlKind::UpperRight; break;
+ case 5: aPnt=maRect.LeftCenter(); eKind = SdrHdlKind::Left ; break;
+ case 6: aPnt=maRect.RightCenter(); eKind = SdrHdlKind::Right; break;
+ case 7: aPnt=maRect.BottomLeft(); eKind = SdrHdlKind::LowerLeft; break;
+ case 8: aPnt=maRect.BottomCenter(); eKind = SdrHdlKind::Lower; break;
+ case 9: aPnt=maRect.BottomRight(); eKind = SdrHdlKind::LowerRight; break;
+ }
+
+ if (maGeo.nShearAngle)
+ {
+ ShearPoint(aPnt,maRect.TopLeft(),maGeo.mfTanShearAngle);
+ }
+ if (maGeo.nRotationAngle)
+ {
+ RotatePoint(aPnt,maRect.TopLeft(),maGeo.mfSinRotationAngle,maGeo.mfCosRotationAngle);
+ }
+
+ std::unique_ptr<SdrHdl> pH(new SdrHdl(aPnt,eKind));
+ pH->SetObj(const_cast<SdrRectObj*>(this));
+ pH->SetRotationAngle(maGeo.nRotationAngle);
+ rHdlList.AddHdl(std::move(pH));
+ }
+}
+
+bool SdrRectObj::hasSpecialDrag() const
+{
+ return true;
+}
+
+bool SdrRectObj::beginSpecialDrag(SdrDragStat& rDrag) const
+{
+ const bool bRad(rDrag.GetHdl() && SdrHdlKind::Circle == rDrag.GetHdl()->GetKind());
+
+ if(bRad)
+ {
+ rDrag.SetEndDragChangesAttributes(true);
+
+ return true;
+ }
+
+ return SdrTextObj::beginSpecialDrag(rDrag);
+}
+
+bool SdrRectObj::applySpecialDrag(SdrDragStat& rDrag)
+{
+ const bool bRad(rDrag.GetHdl() && SdrHdlKind::Circle == rDrag.GetHdl()->GetKind());
+
+ if (bRad)
+ {
+ Point aPt(rDrag.GetNow());
+
+ if (maGeo.nRotationAngle)
+ RotatePoint(aPt, maRect.TopLeft(), -maGeo.mfSinRotationAngle, maGeo.mfCosRotationAngle);
+
+ sal_Int32 nRad(aPt.X() - maRect.Left());
+
+ if (nRad < 0)
+ nRad = 0;
+
+ if(nRad != GetEckenradius())
+ {
+ NbcSetEckenradius(nRad);
+ }
+
+ return true;
+ }
+ else
+ {
+ return SdrTextObj::applySpecialDrag(rDrag);
+ }
+}
+
+OUString SdrRectObj::getSpecialDragComment(const SdrDragStat& rDrag) const
+{
+ const bool bCreateComment(rDrag.GetView() && this == rDrag.GetView()->GetCreateObj());
+
+ if(bCreateComment)
+ {
+ return OUString();
+ }
+ else
+ {
+ const bool bRad(rDrag.GetHdl() && SdrHdlKind::Circle == rDrag.GetHdl()->GetKind());
+
+ if(bRad)
+ {
+ Point aPt(rDrag.GetNow());
+
+ // -sin for reversal
+ if (maGeo.nRotationAngle)
+ RotatePoint(aPt, maRect.TopLeft(), -maGeo.mfSinRotationAngle, maGeo.mfCosRotationAngle);
+
+ sal_Int32 nRad(aPt.X() - maRect.Left());
+
+ if(nRad < 0)
+ nRad = 0;
+
+ return ImpGetDescriptionStr(STR_DragRectEckRad) +
+ " (" +
+ GetMetrStr(nRad) +
+ ")";
+ }
+ else
+ {
+ return SdrTextObj::getSpecialDragComment(rDrag);
+ }
+ }
+}
+
+
+basegfx::B2DPolyPolygon SdrRectObj::TakeCreatePoly(const SdrDragStat& rDrag) const
+{
+ tools::Rectangle aRect1;
+ rDrag.TakeCreateRect(aRect1);
+ aRect1.Justify();
+
+ basegfx::B2DPolyPolygon aRetval;
+ aRetval.append(ImpCalcXPoly(aRect1,GetEckenradius()).getB2DPolygon());
+ return aRetval;
+}
+
+PointerStyle SdrRectObj::GetCreatePointer() const
+{
+ if (IsTextFrame()) return PointerStyle::DrawText;
+ return PointerStyle::DrawRect;
+}
+
+void SdrRectObj::NbcMove(const Size& rSiz)
+{
+ SdrTextObj::NbcMove(rSiz);
+ SetXPolyDirty();
+}
+
+void SdrRectObj::NbcResize(const Point& rRef, const Fraction& xFact, const Fraction& yFact)
+{
+ SdrTextObj::NbcResize(rRef,xFact,yFact);
+ SetXPolyDirty();
+}
+
+void SdrRectObj::NbcRotate(const Point& rRef, Degree100 nAngle, double sn, double cs)
+{
+ SdrTextObj::NbcRotate(rRef,nAngle,sn,cs);
+ SetXPolyDirty();
+}
+
+void SdrRectObj::NbcShear(const Point& rRef, Degree100 nAngle, double tn, bool bVShear)
+{
+ SdrTextObj::NbcShear(rRef,nAngle,tn,bVShear);
+ SetXPolyDirty();
+}
+
+void SdrRectObj::NbcMirror(const Point& rRef1, const Point& rRef2)
+{
+ SdrTextObj::NbcMirror(rRef1,rRef2);
+ SetXPolyDirty();
+}
+
+SdrGluePoint SdrRectObj::GetVertexGluePoint(sal_uInt16 nPosNum) const
+{
+ sal_Int32 nWdt = ImpGetLineWdt(); // #i25616#
+
+ // #i25616#
+ if(!LineIsOutsideGeometry())
+ {
+ nWdt++;
+ nWdt /= 2;
+ }
+
+ Point aPt;
+ switch (nPosNum) {
+ case 0: aPt=maRect.TopCenter(); aPt.AdjustY( -nWdt ); break;
+ case 1: aPt=maRect.RightCenter(); aPt.AdjustX(nWdt ); break;
+ case 2: aPt=maRect.BottomCenter(); aPt.AdjustY(nWdt ); break;
+ case 3: aPt=maRect.LeftCenter(); aPt.AdjustX( -nWdt ); break;
+ }
+ if (maGeo.nShearAngle) ShearPoint(aPt, maRect.TopLeft(), maGeo.mfTanShearAngle);
+ if (maGeo.nRotationAngle) RotatePoint(aPt, maRect.TopLeft(), maGeo.mfSinRotationAngle, maGeo.mfCosRotationAngle);
+ aPt-=GetSnapRect().Center();
+ SdrGluePoint aGP(aPt);
+ aGP.SetPercent(false);
+ return aGP;
+}
+
+SdrGluePoint SdrRectObj::GetCornerGluePoint(sal_uInt16 nPosNum) const
+{
+ sal_Int32 nWdt = ImpGetLineWdt(); // #i25616#
+
+ // #i25616#
+ if(!LineIsOutsideGeometry())
+ {
+ nWdt++;
+ nWdt /= 2;
+ }
+
+ Point aPt;
+ switch (nPosNum) {
+ case 0: aPt=maRect.TopLeft(); aPt.AdjustX( -nWdt ); aPt.AdjustY( -nWdt ); break;
+ case 1: aPt=maRect.TopRight(); aPt.AdjustX(nWdt ); aPt.AdjustY( -nWdt ); break;
+ case 2: aPt=maRect.BottomRight(); aPt.AdjustX(nWdt ); aPt.AdjustY(nWdt ); break;
+ case 3: aPt=maRect.BottomLeft(); aPt.AdjustX( -nWdt ); aPt.AdjustY(nWdt ); break;
+ }
+ if (maGeo.nShearAngle) ShearPoint(aPt,maRect.TopLeft(),maGeo.mfTanShearAngle);
+ if (maGeo.nRotationAngle) RotatePoint(aPt,maRect.TopLeft(),maGeo.mfSinRotationAngle,maGeo.mfCosRotationAngle);
+ aPt-=GetSnapRect().Center();
+ SdrGluePoint aGP(aPt);
+ aGP.SetPercent(false);
+ return aGP;
+}
+
+SdrObjectUniquePtr SdrRectObj::DoConvertToPolyObj(bool bBezier, bool bAddText) const
+{
+ XPolygon aXP(ImpCalcXPoly(maRect,GetEckenradius()));
+ { // TODO: this is only for the moment, until we have the new TakeContour()
+ aXP.Remove(0,1);
+ aXP[aXP.GetPointCount()-1]=aXP[0];
+ }
+
+ basegfx::B2DPolyPolygon aPolyPolygon(aXP.getB2DPolygon());
+ aPolyPolygon.removeDoublePoints();
+ SdrObjectUniquePtr pRet;
+
+ // small correction: Do not create something when no fill and no line. To
+ // be sure to not damage something with non-text frames, do this only
+ // when used with bAddText==false from other converters
+ if((bAddText && !IsTextFrame()) || HasFill() || HasLine())
+ {
+ pRet = ImpConvertMakeObj(aPolyPolygon, true, bBezier);
+ }
+
+ if(bAddText)
+ {
+ pRet = ImpConvertAddText(std::move(pRet), bBezier);
+ }
+
+ return pRet;
+}
+
+void SdrRectObj::Notify(SfxBroadcaster& rBC, const SfxHint& rHint)
+{
+ SdrTextObj::Notify(rBC,rHint);
+ SetXPolyDirty(); // because of the corner radius
+}
+
+void SdrRectObj::RestoreGeoData(const SdrObjGeoData& rGeo)
+{
+ SdrTextObj::RestoreGeoData(rGeo);
+ SetXPolyDirty();
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/svdraw/svdotext.cxx b/svx/source/svdraw/svdotext.cxx
new file mode 100644
index 000000000..fe5e13446
--- /dev/null
+++ b/svx/source/svdraw/svdotext.cxx
@@ -0,0 +1,2174 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * 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/stritem.hxx>
+#include <svx/svdotext.hxx>
+#include <svx/svdpage.hxx>
+#include <svx/svdoutl.hxx>
+#include <svx/svdmodel.hxx>
+#include <svx/dialmgr.hxx>
+#include <svx/strings.hrc>
+#include <editeng/writingmodeitem.hxx>
+#include <svx/sdtfchim.hxx>
+#include <editeng/editdata.hxx>
+#include <editeng/editstat.hxx>
+#include <editeng/outlobj.hxx>
+#include <editeng/editobj.hxx>
+#include <editeng/outliner.hxx>
+#include <textchain.hxx>
+#include <textchainflow.hxx>
+#include <tools/helpers.hxx>
+#include <svx/sderitm.hxx>
+#include <svx/sdooitm.hxx>
+#include <svx/sdshitm.hxx>
+#include <svx/sdtagitm.hxx>
+#include <svx/sdtfsitm.hxx>
+#include <svx/sdtmfitm.hxx>
+#include <svx/xtextit0.hxx>
+#include <sdr/properties/textproperties.hxx>
+#include <sdr/contact/viewcontactoftextobj.hxx>
+#include <basegfx/tuple/b2dtuple.hxx>
+#include <basegfx/matrix/b2dhommatrix.hxx>
+#include <vcl/gdimtf.hxx>
+#include <vcl/virdev.hxx>
+#include <basegfx/matrix/b2dhommatrixtools.hxx>
+#include <sal/log.hxx>
+#include <o3tl/temporary.hxx>
+#include <unotools/configmgr.hxx>
+
+using namespace com::sun::star;
+
+// BaseProperties section
+std::unique_ptr<sdr::properties::BaseProperties> SdrTextObj::CreateObjectSpecificProperties()
+{
+ return std::make_unique<sdr::properties::TextProperties>(*this);
+}
+
+// DrawContact section
+std::unique_ptr<sdr::contact::ViewContact> SdrTextObj::CreateObjectSpecificViewContact()
+{
+ return std::make_unique<sdr::contact::ViewContactOfTextObj>(*this);
+}
+
+SdrTextObj::SdrTextObj(SdrModel& rSdrModel)
+: SdrAttrObj(rSdrModel),
+ mpEditingOutliner(nullptr),
+ meTextKind(SdrObjKind::Text)
+{
+ mbTextSizeDirty = false;
+ mbTextFrame = false;
+ mbNoShear = false;
+ mbDisableAutoWidthOnDragging = false;
+
+ mbInEditMode = false;
+ mbTextAnimationAllowed = true;
+ maTextEditOffset = Point(0, 0);
+
+ // #i25616#
+ mbSupportTextIndentingOnLineWidthChange = true;
+ mbInDownScale = false;
+}
+
+SdrTextObj::SdrTextObj(SdrModel& rSdrModel, SdrTextObj const & rSource)
+: SdrAttrObj(rSdrModel, rSource),
+ mpEditingOutliner(nullptr)
+{
+ mbInEditMode = false;
+ mbTextAnimationAllowed = true;
+ maTextEditOffset = Point(0, 0);
+
+ // #i25616#
+ mbSupportTextIndentingOnLineWidthChange = true;
+ mbInDownScale = false;
+
+ maRect = rSource.maRect;
+ maGeo = rSource.maGeo;
+ meTextKind = rSource.meTextKind;
+ mbTextFrame = rSource.mbTextFrame;
+ maTextSize = rSource.maTextSize;
+ mbTextSizeDirty = rSource.mbTextSizeDirty;
+
+ // Not all of the necessary parameters were copied yet.
+ mbNoShear = rSource.mbNoShear;
+ mbDisableAutoWidthOnDragging = rSource.mbDisableAutoWidthOnDragging;
+ SdrText* pText = getActiveText();
+
+ if( pText && rSource.HasText() )
+ {
+ // before pNewOutlinerParaObject was created the same, but
+ // set at mpText (outside this scope), but mpText might be
+ // empty (this operator== seems not prepared for MultiText
+ // objects). In the current form it makes only sense to
+ // create locally and use locally on a known existing SdrText
+ const Outliner* pEO = rSource.mpEditingOutliner;
+ std::optional<OutlinerParaObject> pNewOutlinerParaObject;
+
+ if (pEO!=nullptr)
+ {
+ pNewOutlinerParaObject = pEO->CreateParaObject();
+ }
+ else if (nullptr != rSource.getActiveText()->GetOutlinerParaObject())
+ {
+ pNewOutlinerParaObject = *rSource.getActiveText()->GetOutlinerParaObject();
+ }
+
+ pText->SetOutlinerParaObject( std::move(pNewOutlinerParaObject) );
+ }
+
+ ImpSetTextStyleSheetListeners();
+}
+
+SdrTextObj::SdrTextObj(
+ SdrModel& rSdrModel,
+ const tools::Rectangle& rNewRect)
+: SdrAttrObj(rSdrModel),
+ maRect(rNewRect),
+ mpEditingOutliner(nullptr),
+ meTextKind(SdrObjKind::Text)
+{
+ mbTextSizeDirty = false;
+ mbTextFrame = false;
+ mbNoShear = false;
+ mbDisableAutoWidthOnDragging = false;
+ ImpJustifyRect(maRect);
+
+ mbInEditMode = false;
+ mbTextAnimationAllowed = true;
+ mbInDownScale = false;
+ maTextEditOffset = Point(0, 0);
+
+ // #i25616#
+ mbSupportTextIndentingOnLineWidthChange = true;
+}
+
+SdrTextObj::SdrTextObj(
+ SdrModel& rSdrModel,
+ SdrObjKind eNewTextKind)
+: SdrAttrObj(rSdrModel),
+ mpEditingOutliner(nullptr),
+ meTextKind(eNewTextKind)
+{
+ mbTextSizeDirty = false;
+ mbTextFrame = true;
+ mbNoShear = true;
+ mbDisableAutoWidthOnDragging = false;
+
+ mbInEditMode = false;
+ mbTextAnimationAllowed = true;
+ mbInDownScale = false;
+ maTextEditOffset = Point(0, 0);
+
+ // #i25616#
+ mbSupportTextIndentingOnLineWidthChange = true;
+}
+
+SdrTextObj::SdrTextObj(
+ SdrModel& rSdrModel,
+ SdrObjKind eNewTextKind,
+ const tools::Rectangle& rNewRect)
+: SdrAttrObj(rSdrModel),
+ maRect(rNewRect),
+ mpEditingOutliner(nullptr),
+ meTextKind(eNewTextKind)
+{
+ mbTextSizeDirty = false;
+ mbTextFrame = true;
+ mbNoShear = true;
+ mbDisableAutoWidthOnDragging = false;
+ ImpJustifyRect(maRect);
+
+ mbInEditMode = false;
+ mbTextAnimationAllowed = true;
+ mbInDownScale = false;
+ maTextEditOffset = Point(0, 0);
+
+ // #i25616#
+ mbSupportTextIndentingOnLineWidthChange = true;
+}
+
+SdrTextObj::~SdrTextObj()
+{
+ SdrOutliner& rOutl(getSdrModelFromSdrObject().GetHitTestOutliner());
+ if( rOutl.GetTextObj() == this )
+ rOutl.SetTextObj( nullptr );
+ mpText.reset();
+ ImpDeregisterLink();
+}
+
+void SdrTextObj::FitFrameToTextSize()
+{
+ ImpJustifyRect(maRect);
+
+ SdrText* pText = getActiveText();
+ if(pText==nullptr || !pText->GetOutlinerParaObject())
+ return;
+
+ SdrOutliner& rOutliner=ImpGetDrawOutliner();
+ rOutliner.SetPaperSize(Size(maRect.Right()-maRect.Left(),maRect.Bottom()-maRect.Top()));
+ rOutliner.SetUpdateLayout(true);
+ rOutliner.SetText(*pText->GetOutlinerParaObject());
+ Size aNewSize(rOutliner.CalcTextSize());
+ rOutliner.Clear();
+ aNewSize.AdjustWidth( 1 ); // because of possible rounding errors
+ aNewSize.AdjustWidth(GetTextLeftDistance()+GetTextRightDistance() );
+ aNewSize.AdjustHeight(GetTextUpperDistance()+GetTextLowerDistance() );
+ tools::Rectangle aNewRect(maRect);
+ aNewRect.SetSize(aNewSize);
+ ImpJustifyRect(aNewRect);
+ if (aNewRect!=maRect) {
+ SetLogicRect(aNewRect);
+ }
+}
+
+void SdrTextObj::NbcSetText(const OUString& rStr)
+{
+ SdrOutliner& rOutliner=ImpGetDrawOutliner();
+ rOutliner.SetStyleSheet( 0, GetStyleSheet());
+ rOutliner.SetText(rStr,rOutliner.GetParagraph( 0 ));
+ std::optional<OutlinerParaObject> pNewText=rOutliner.CreateParaObject();
+ NbcSetOutlinerParaObject(std::move(pNewText));
+ mbTextSizeDirty=true;
+}
+
+void SdrTextObj::SetText(const OUString& rStr)
+{
+ tools::Rectangle aBoundRect0; if (m_pUserCall!=nullptr) aBoundRect0=GetLastBoundRect();
+ NbcSetText(rStr);
+ SetChanged();
+ BroadcastObjectChange();
+ SendUserCall(SdrUserCallType::Resize,aBoundRect0);
+}
+
+void SdrTextObj::NbcSetText(SvStream& rInput, const OUString& rBaseURL, EETextFormat eFormat)
+{
+ SdrOutliner& rOutliner=ImpGetDrawOutliner();
+ rOutliner.SetStyleSheet( 0, GetStyleSheet());
+ rOutliner.Read(rInput,rBaseURL,eFormat);
+ std::optional<OutlinerParaObject> pNewText=rOutliner.CreateParaObject();
+ rOutliner.SetUpdateLayout(true);
+ Size aSize(rOutliner.CalcTextSize());
+ rOutliner.Clear();
+ NbcSetOutlinerParaObject(std::move(pNewText));
+ maTextSize=aSize;
+ mbTextSizeDirty=false;
+}
+
+void SdrTextObj::SetText(SvStream& rInput, const OUString& rBaseURL, EETextFormat eFormat)
+{
+ tools::Rectangle aBoundRect0; if (m_pUserCall!=nullptr) aBoundRect0=GetLastBoundRect();
+ NbcSetText(rInput,rBaseURL,eFormat);
+ SetChanged();
+ BroadcastObjectChange();
+ SendUserCall(SdrUserCallType::Resize,aBoundRect0);
+}
+
+const Size& SdrTextObj::GetTextSize() const
+{
+ if (mbTextSizeDirty)
+ {
+ Size aSiz;
+ SdrText* pText = getActiveText();
+ if( pText && pText->GetOutlinerParaObject ())
+ {
+ SdrOutliner& rOutliner=ImpGetDrawOutliner();
+ rOutliner.SetText(*pText->GetOutlinerParaObject());
+ rOutliner.SetUpdateLayout(true);
+ aSiz=rOutliner.CalcTextSize();
+ rOutliner.Clear();
+ }
+ // casting to nonconst twice
+ const_cast<SdrTextObj*>(this)->maTextSize = aSiz;
+ const_cast<SdrTextObj*>(this)->mbTextSizeDirty = false;
+ }
+ return maTextSize;
+}
+
+bool SdrTextObj::IsAutoGrowHeight() const
+{
+ if(!mbTextFrame)
+ return false; // AutoGrow only together with TextFrames
+
+ const SfxItemSet& rSet = GetObjectItemSet();
+ bool bRet = rSet.Get(SDRATTR_TEXT_AUTOGROWHEIGHT).GetValue();
+
+ if(bRet)
+ {
+ SdrTextAniKind eAniKind = rSet.Get(SDRATTR_TEXT_ANIKIND).GetValue();
+
+ if(eAniKind == SdrTextAniKind::Scroll || eAniKind == SdrTextAniKind::Alternate || eAniKind == SdrTextAniKind::Slide)
+ {
+ SdrTextAniDirection eDirection = rSet.Get(SDRATTR_TEXT_ANIDIRECTION).GetValue();
+
+ if(eDirection == SdrTextAniDirection::Up || eDirection == SdrTextAniDirection::Down)
+ {
+ bRet = false;
+ }
+ }
+ }
+ return bRet;
+}
+
+bool SdrTextObj::IsAutoGrowWidth() const
+{
+ if (!mbTextFrame)
+ return false; // AutoGrow only together with TextFrames
+
+ const SfxItemSet& rSet = GetObjectItemSet();
+ bool bRet = rSet.Get(SDRATTR_TEXT_AUTOGROWWIDTH).GetValue();
+
+ bool bInEditMOde = IsInEditMode();
+
+ if(!bInEditMOde && bRet)
+ {
+ SdrTextAniKind eAniKind = rSet.Get(SDRATTR_TEXT_ANIKIND).GetValue();
+
+ if(eAniKind == SdrTextAniKind::Scroll || eAniKind == SdrTextAniKind::Alternate || eAniKind == SdrTextAniKind::Slide)
+ {
+ SdrTextAniDirection eDirection = rSet.Get(SDRATTR_TEXT_ANIDIRECTION).GetValue();
+
+ if(eDirection == SdrTextAniDirection::Left || eDirection == SdrTextAniDirection::Right)
+ {
+ bRet = false;
+ }
+ }
+ }
+ return bRet;
+}
+
+SdrTextHorzAdjust SdrTextObj::GetTextHorizontalAdjust() const
+{
+ return GetTextHorizontalAdjust(GetObjectItemSet());
+}
+
+SdrTextHorzAdjust SdrTextObj::GetTextHorizontalAdjust(const SfxItemSet& rSet) const
+{
+ if(IsContourTextFrame())
+ return SDRTEXTHORZADJUST_BLOCK;
+
+ SdrTextHorzAdjust eRet = rSet.Get(SDRATTR_TEXT_HORZADJUST).GetValue();
+
+ bool bInEditMode = IsInEditMode();
+
+ if(!bInEditMode && eRet == SDRTEXTHORZADJUST_BLOCK)
+ {
+ SdrTextAniKind eAniKind = rSet.Get(SDRATTR_TEXT_ANIKIND).GetValue();
+
+ if(eAniKind == SdrTextAniKind::Scroll || eAniKind == SdrTextAniKind::Alternate || eAniKind == SdrTextAniKind::Slide)
+ {
+ SdrTextAniDirection eDirection = rSet.Get(SDRATTR_TEXT_ANIDIRECTION).GetValue();
+
+ if(eDirection == SdrTextAniDirection::Left || eDirection == SdrTextAniDirection::Right)
+ {
+ eRet = SDRTEXTHORZADJUST_LEFT;
+ }
+ }
+ }
+
+ return eRet;
+} // defaults: BLOCK (justify) for text frame, CENTER for captions of drawing objects
+
+SdrTextVertAdjust SdrTextObj::GetTextVerticalAdjust() const
+{
+ return GetTextVerticalAdjust(GetObjectItemSet());
+}
+
+SdrTextVertAdjust SdrTextObj::GetTextVerticalAdjust(const SfxItemSet& rSet) const
+{
+ if(IsContourTextFrame())
+ return SDRTEXTVERTADJUST_TOP;
+
+ // Take care for vertical text animation here
+ SdrTextVertAdjust eRet = rSet.Get(SDRATTR_TEXT_VERTADJUST).GetValue();
+ bool bInEditMode = IsInEditMode();
+
+ // Take care for vertical text animation here
+ if(!bInEditMode && eRet == SDRTEXTVERTADJUST_BLOCK)
+ {
+ SdrTextAniKind eAniKind = rSet.Get(SDRATTR_TEXT_ANIKIND).GetValue();
+
+ if(eAniKind == SdrTextAniKind::Scroll || eAniKind == SdrTextAniKind::Alternate || eAniKind == SdrTextAniKind::Slide)
+ {
+ SdrTextAniDirection eDirection = rSet.Get(SDRATTR_TEXT_ANIDIRECTION).GetValue();
+
+ if(eDirection == SdrTextAniDirection::Left || eDirection == SdrTextAniDirection::Right)
+ {
+ eRet = SDRTEXTVERTADJUST_TOP;
+ }
+ }
+ }
+
+ return eRet;
+} // defaults: TOP for text frame, CENTER for captions of drawing objects
+
+void SdrTextObj::ImpJustifyRect(tools::Rectangle& rRect)
+{
+ if (!rRect.IsEmpty()) {
+ rRect.Justify();
+ if (rRect.Left()==rRect.Right()) rRect.AdjustRight( 1 );
+ if (rRect.Top()==rRect.Bottom()) rRect.AdjustBottom( 1 );
+ }
+}
+
+void SdrTextObj::ImpCheckShear()
+{
+ if (mbNoShear && maGeo.nShearAngle)
+ {
+ maGeo.nShearAngle = 0_deg100;
+ maGeo.mfTanShearAngle = 0;
+ }
+}
+
+void SdrTextObj::TakeObjInfo(SdrObjTransformInfoRec& rInfo) const
+{
+ bool bNoTextFrame=!IsTextFrame();
+ rInfo.bResizeFreeAllowed=bNoTextFrame || ((maGeo.nRotationAngle.get() % 9000) == 0);
+ rInfo.bResizePropAllowed=true;
+ rInfo.bRotateFreeAllowed=true;
+ rInfo.bRotate90Allowed =true;
+ rInfo.bMirrorFreeAllowed=bNoTextFrame;
+ rInfo.bMirror45Allowed =bNoTextFrame;
+ rInfo.bMirror90Allowed =bNoTextFrame;
+
+ // allow transparency
+ rInfo.bTransparenceAllowed = true;
+
+ rInfo.bShearAllowed =bNoTextFrame;
+ rInfo.bEdgeRadiusAllowed=true;
+ bool bCanConv=ImpCanConvTextToCurve();
+ rInfo.bCanConvToPath =bCanConv;
+ rInfo.bCanConvToPoly =bCanConv;
+ rInfo.bCanConvToPathLineToArea=bCanConv;
+ rInfo.bCanConvToPolyLineToArea=bCanConv;
+ rInfo.bCanConvToContour = (rInfo.bCanConvToPoly || LineGeometryUsageIsNecessary());
+}
+
+SdrObjKind SdrTextObj::GetObjIdentifier() const
+{
+ return meTextKind;
+}
+
+bool SdrTextObj::HasTextImpl( SdrOutliner const * pOutliner )
+{
+ bool bRet=false;
+ if(pOutliner)
+ {
+ Paragraph* p1stPara=pOutliner->GetParagraph( 0 );
+ sal_Int32 nParaCount=pOutliner->GetParagraphCount();
+ if(p1stPara==nullptr)
+ nParaCount=0;
+
+ if(nParaCount==1)
+ {
+ // if it is only one paragraph, check if that paragraph is empty
+ if( pOutliner->GetText(p1stPara).isEmpty() )
+ nParaCount = 0;
+ }
+
+ bRet= nParaCount!=0;
+ }
+ return bRet;
+}
+
+void SdrTextObj::handlePageChange(SdrPage* pOldPage, SdrPage* pNewPage)
+{
+ const bool bRemove(pNewPage == nullptr && pOldPage != nullptr);
+ const bool bInsert(pNewPage != nullptr && pOldPage == nullptr);
+ const bool bLinked(IsLinkedText());
+
+ if (bLinked && bRemove)
+ {
+ ImpDeregisterLink();
+ }
+
+ // call parent
+ SdrAttrObj::handlePageChange(pOldPage, pNewPage);
+
+ if (bLinked && bInsert)
+ {
+ ImpRegisterLink();
+ }
+}
+
+void SdrTextObj::NbcSetEckenradius(tools::Long nRad)
+{
+ SetObjectItem(makeSdrEckenradiusItem(nRad));
+}
+
+// #115391# This implementation is based on the object size (aRect) and the
+// states of IsAutoGrowWidth/Height to correctly set TextMinFrameWidth/Height
+void SdrTextObj::AdaptTextMinSize()
+{
+ if (!mbTextFrame)
+ // Only do this for text frame.
+ return;
+
+ if (getSdrModelFromSdrObject().IsPasteResize())
+ // Don't do this during paste resize.
+ return;
+
+ const bool bW = IsAutoGrowWidth();
+ const bool bH = IsAutoGrowHeight();
+
+ if (!bW && !bH)
+ // No auto grow requested. Bail out.
+ return;
+
+ SfxItemSetFixed<SDRATTR_TEXT_MINFRAMEHEIGHT, SDRATTR_TEXT_AUTOGROWHEIGHT,
+ SDRATTR_TEXT_MINFRAMEWIDTH, SDRATTR_TEXT_AUTOGROWWIDTH> // contains SDRATTR_TEXT_MAXFRAMEWIDTH
+ aSet(*GetObjectItemSet().GetPool());
+
+ if(bW)
+ {
+ // Set minimum width.
+ const tools::Long nDist = GetTextLeftDistance() + GetTextRightDistance();
+ const tools::Long nW = std::max<tools::Long>(0, maRect.GetWidth() - 1 - nDist); // text width without margins
+
+ aSet.Put(makeSdrTextMinFrameWidthItem(nW));
+
+ if(!IsVerticalWriting() && mbDisableAutoWidthOnDragging)
+ {
+ mbDisableAutoWidthOnDragging = true;
+ aSet.Put(makeSdrTextAutoGrowWidthItem(false));
+ }
+ }
+
+ if(bH)
+ {
+ // Set Minimum height.
+ const tools::Long nDist = GetTextUpperDistance() + GetTextLowerDistance();
+ const tools::Long nH = std::max<tools::Long>(0, maRect.GetHeight() - 1 - nDist); // text height without margins
+
+ aSet.Put(makeSdrTextMinFrameHeightItem(nH));
+
+ if(IsVerticalWriting() && mbDisableAutoWidthOnDragging)
+ {
+ mbDisableAutoWidthOnDragging = false;
+ aSet.Put(makeSdrTextAutoGrowHeightItem(false));
+ }
+ }
+
+ SetObjectItemSet(aSet);
+}
+
+void SdrTextObj::ImpSetContourPolygon( SdrOutliner& rOutliner, tools::Rectangle const & rAnchorRect, bool bLineWidth ) const
+{
+ basegfx::B2DPolyPolygon aXorPolyPolygon(TakeXorPoly());
+ std::optional<basegfx::B2DPolyPolygon> pContourPolyPolygon;
+ basegfx::B2DHomMatrix aMatrix(basegfx::utils::createTranslateB2DHomMatrix(
+ -rAnchorRect.Left(), -rAnchorRect.Top()));
+
+ if(maGeo.nRotationAngle)
+ {
+ // Unrotate!
+ aMatrix.rotate(-toRadians(maGeo.nRotationAngle));
+ }
+
+ aXorPolyPolygon.transform(aMatrix);
+
+ if( bLineWidth )
+ {
+ // Take line width into account.
+ // When doing the hit test, avoid this. (Performance!)
+ pContourPolyPolygon.emplace();
+
+ // test if shadow needs to be avoided for TakeContour()
+ const SfxItemSet& rSet = GetObjectItemSet();
+ bool bShadowOn = rSet.Get(SDRATTR_SHADOW).GetValue();
+
+ // #i33696#
+ // Remember TextObject currently set at the DrawOutliner, it WILL be
+ // replaced during calculating the outline since it uses an own paint
+ // and that one uses the DrawOutliner, too.
+ const SdrTextObj* pLastTextObject = rOutliner.GetTextObj();
+
+ if(bShadowOn)
+ {
+ // force shadow off
+ SdrObject* pCopy(CloneSdrObject(getSdrModelFromSdrObject()));
+ pCopy->SetMergedItem(makeSdrShadowItem(false));
+ *pContourPolyPolygon = pCopy->TakeContour();
+ SdrObject::Free( pCopy );
+ }
+ else
+ {
+ *pContourPolyPolygon = TakeContour();
+ }
+
+ // #i33696#
+ // restore remembered text object
+ if(pLastTextObject != rOutliner.GetTextObj())
+ {
+ rOutliner.SetTextObj(pLastTextObject);
+ }
+
+ pContourPolyPolygon->transform(aMatrix);
+ }
+
+ rOutliner.SetPolygon(aXorPolyPolygon, pContourPolyPolygon ? &*pContourPolyPolygon : nullptr);
+}
+
+void SdrTextObj::TakeUnrotatedSnapRect(tools::Rectangle& rRect) const
+{
+ rRect=maRect;
+}
+
+// See also: <unnamed>::getTextAnchorRange in svx/source/sdr/primitive2d/sdrdecompositiontools.cxx
+void SdrTextObj::AdjustRectToTextDistance(tools::Rectangle& rAnchorRect) const
+{
+ const tools::Long nLeftDist = GetTextLeftDistance();
+ const tools::Long nRightDist = GetTextRightDistance();
+ const tools::Long nUpperDist = GetTextUpperDistance();
+ const tools::Long nLowerDist = GetTextLowerDistance();
+ if (!IsVerticalWriting())
+ {
+ rAnchorRect.AdjustLeft(nLeftDist);
+ rAnchorRect.AdjustTop(nUpperDist);
+ rAnchorRect.AdjustRight(-nRightDist);
+ rAnchorRect.AdjustBottom(-nLowerDist);
+ }
+ else if (IsTopToBottom())
+ {
+ rAnchorRect.AdjustLeft(nLowerDist);
+ rAnchorRect.AdjustTop(nLeftDist);
+ rAnchorRect.AdjustRight(-nUpperDist);
+ rAnchorRect.AdjustBottom(-nRightDist);
+ }
+ else
+ {
+ rAnchorRect.AdjustLeft(nUpperDist);
+ rAnchorRect.AdjustTop(nRightDist);
+ rAnchorRect.AdjustRight(-nLowerDist);
+ rAnchorRect.AdjustBottom(-nLeftDist);
+ }
+
+ // Since sizes may be bigger than the object bounds it is necessary to
+ // justify the rect now.
+ ImpJustifyRect(rAnchorRect);
+}
+
+void SdrTextObj::TakeTextAnchorRect(tools::Rectangle& rAnchorRect) const
+{
+ tools::Rectangle aAnkRect(maRect); // the rectangle in which we anchor
+ bool bFrame=IsTextFrame();
+ if (!bFrame) {
+ TakeUnrotatedSnapRect(aAnkRect);
+ }
+ Point aRotateRef(aAnkRect.TopLeft());
+ AdjustRectToTextDistance(aAnkRect);
+
+ if (bFrame) {
+ // TODO: Optimize this.
+ if (aAnkRect.GetWidth()<2) aAnkRect.SetRight(aAnkRect.Left()+1 ); // minimum size h and v: 2 px
+ if (aAnkRect.GetHeight()<2) aAnkRect.SetBottom(aAnkRect.Top()+1 );
+ }
+ if (maGeo.nRotationAngle) {
+ Point aTmpPt(aAnkRect.TopLeft());
+ RotatePoint(aTmpPt,aRotateRef,maGeo.mfSinRotationAngle,maGeo.mfCosRotationAngle);
+ aTmpPt-=aAnkRect.TopLeft();
+ aAnkRect.Move(aTmpPt.X(),aTmpPt.Y());
+ }
+ rAnchorRect=aAnkRect;
+}
+
+void SdrTextObj::TakeTextRect( SdrOutliner& rOutliner, tools::Rectangle& rTextRect, bool bNoEditText,
+ tools::Rectangle* pAnchorRect, bool bLineWidth ) const
+{
+ tools::Rectangle aAnkRect; // the rectangle in which we anchor
+ TakeTextAnchorRect(aAnkRect);
+ SdrTextVertAdjust eVAdj=GetTextVerticalAdjust();
+ SdrTextHorzAdjust eHAdj=GetTextHorizontalAdjust();
+ SdrTextAniKind eAniKind=GetTextAniKind();
+ SdrTextAniDirection eAniDirection=GetTextAniDirection();
+
+ bool bFitToSize(IsFitToSize());
+ bool bContourFrame=IsContourTextFrame();
+
+ bool bFrame=IsTextFrame();
+ EEControlBits nStat0=rOutliner.GetControlWord();
+ Size aNullSize;
+ if (!bContourFrame)
+ {
+ rOutliner.SetControlWord(nStat0|EEControlBits::AUTOPAGESIZE);
+ rOutliner.SetMinAutoPaperSize(aNullSize);
+ rOutliner.SetMaxAutoPaperSize(Size(1000000,1000000));
+ }
+
+ if (!bFitToSize && !bContourFrame)
+ {
+ tools::Long nAnkWdt=aAnkRect.GetWidth();
+ tools::Long nAnkHgt=aAnkRect.GetHeight();
+ if (bFrame)
+ {
+ tools::Long nWdt=nAnkWdt;
+ tools::Long nHgt=nAnkHgt;
+
+ bool bInEditMode = IsInEditMode();
+
+ if (!bInEditMode && (eAniKind==SdrTextAniKind::Scroll || eAniKind==SdrTextAniKind::Alternate || eAniKind==SdrTextAniKind::Slide))
+ {
+ // unlimited paper size for ticker text
+ if (eAniDirection==SdrTextAniDirection::Left || eAniDirection==SdrTextAniDirection::Right) nWdt=1000000;
+ if (eAniDirection==SdrTextAniDirection::Up || eAniDirection==SdrTextAniDirection::Down) nHgt=1000000;
+ }
+
+ bool bChainedFrame = IsChainable();
+ // Might be required for overflow check working: do limit height to frame if box is chainable.
+ if (!bChainedFrame) {
+ // #i119885# Do not limit/force height to geometrical frame (vice versa for vertical writing)
+
+ if(IsVerticalWriting())
+ {
+ nWdt = 1000000;
+ }
+ else
+ {
+ nHgt = 1000000;
+ }
+ }
+
+ rOutliner.SetMaxAutoPaperSize(Size(nWdt,nHgt));
+ }
+
+ // New try with _BLOCK for hor and ver after completely
+ // supporting full width for vertical text.
+ if(SDRTEXTHORZADJUST_BLOCK == eHAdj && !IsVerticalWriting())
+ {
+ rOutliner.SetMinAutoPaperSize(Size(nAnkWdt, 0));
+ rOutliner.SetMinColumnWrapHeight(nAnkHgt);
+ }
+
+ if(SDRTEXTVERTADJUST_BLOCK == eVAdj && IsVerticalWriting())
+ {
+ rOutliner.SetMinAutoPaperSize(Size(0, nAnkHgt));
+ rOutliner.SetMinColumnWrapHeight(nAnkWdt);
+ }
+ }
+
+ rOutliner.SetPaperSize(aNullSize);
+ if (bContourFrame)
+ ImpSetContourPolygon( rOutliner, aAnkRect, bLineWidth );
+
+ // put text into the outliner, if available from the edit outliner
+ SdrText* pText = getActiveText();
+ OutlinerParaObject* pOutlinerParaObject = pText ? pText->GetOutlinerParaObject() : nullptr;
+ std::optional<OutlinerParaObject> pPara;
+ if (mpEditingOutliner && !bNoEditText)
+ pPara = mpEditingOutliner->CreateParaObject();
+ else if (pOutlinerParaObject)
+ pPara = *pOutlinerParaObject;
+
+ if (pPara)
+ {
+ const bool bHitTest(&getSdrModelFromSdrObject().GetHitTestOutliner() == &rOutliner);
+ const SdrTextObj* pTestObj = rOutliner.GetTextObj();
+
+ if( !pTestObj || !bHitTest || pTestObj != this ||
+ pTestObj->GetOutlinerParaObject() != pOutlinerParaObject )
+ {
+ if( bHitTest ) // #i33696# take back fix #i27510#
+ {
+ rOutliner.SetTextObj( this );
+ rOutliner.SetFixedCellHeight(GetMergedItem(SDRATTR_TEXT_USEFIXEDCELLHEIGHT).GetValue());
+ }
+
+ rOutliner.SetUpdateLayout(true);
+ rOutliner.SetText(*pPara);
+ }
+ }
+ else
+ {
+ rOutliner.SetTextObj( nullptr );
+ }
+
+ rOutliner.SetUpdateLayout(true);
+ rOutliner.SetControlWord(nStat0);
+
+ if( pText )
+ pText->CheckPortionInfo(rOutliner);
+
+ Point aTextPos(aAnkRect.TopLeft());
+ Size aTextSiz(rOutliner.GetPaperSize()); // GetPaperSize() adds a little tolerance, right?
+
+ // For draw objects containing text correct hor/ver alignment if text is bigger
+ // than the object itself. Without that correction, the text would always be
+ // formatted to the left edge (or top edge when vertical) of the draw object.
+ if(!IsTextFrame())
+ {
+ if(aAnkRect.GetWidth() < aTextSiz.Width() && !IsVerticalWriting())
+ {
+ // Horizontal case here. Correct only if eHAdj == SDRTEXTHORZADJUST_BLOCK,
+ // else the alignment is wanted.
+ if(SDRTEXTHORZADJUST_BLOCK == eHAdj)
+ {
+ eHAdj = SDRTEXTHORZADJUST_CENTER;
+ }
+ }
+
+ if(aAnkRect.GetHeight() < aTextSiz.Height() && IsVerticalWriting())
+ {
+ // Vertical case here. Correct only if eHAdj == SDRTEXTVERTADJUST_BLOCK,
+ // else the alignment is wanted.
+ if(SDRTEXTVERTADJUST_BLOCK == eVAdj)
+ {
+ eVAdj = SDRTEXTVERTADJUST_CENTER;
+ }
+ }
+ }
+
+ if (eHAdj==SDRTEXTHORZADJUST_CENTER || eHAdj==SDRTEXTHORZADJUST_RIGHT)
+ {
+ tools::Long nFreeWdt=aAnkRect.GetWidth()-aTextSiz.Width();
+ if (eHAdj==SDRTEXTHORZADJUST_CENTER)
+ aTextPos.AdjustX(nFreeWdt/2 );
+ if (eHAdj==SDRTEXTHORZADJUST_RIGHT)
+ aTextPos.AdjustX(nFreeWdt );
+ }
+ if (eVAdj==SDRTEXTVERTADJUST_CENTER || eVAdj==SDRTEXTVERTADJUST_BOTTOM)
+ {
+ tools::Long nFreeHgt=aAnkRect.GetHeight()-aTextSiz.Height();
+ if (eVAdj==SDRTEXTVERTADJUST_CENTER)
+ aTextPos.AdjustY(nFreeHgt/2 );
+ if (eVAdj==SDRTEXTVERTADJUST_BOTTOM)
+ aTextPos.AdjustY(nFreeHgt );
+ }
+ if (maGeo.nRotationAngle)
+ RotatePoint(aTextPos,aAnkRect.TopLeft(),maGeo.mfSinRotationAngle,maGeo.mfCosRotationAngle);
+
+ if (pAnchorRect)
+ *pAnchorRect=aAnkRect;
+
+ // rTextRect might not be correct in some cases at ContourFrame
+ rTextRect=tools::Rectangle(aTextPos,aTextSiz);
+ if (bContourFrame)
+ rTextRect=aAnkRect;
+}
+
+bool SdrTextObj::CanCreateEditOutlinerParaObject() const
+{
+ if( HasTextImpl( mpEditingOutliner ) )
+ {
+ return mpEditingOutliner->GetParagraphCount() > 0;
+ }
+ return false;
+}
+
+std::optional<OutlinerParaObject> SdrTextObj::CreateEditOutlinerParaObject() const
+{
+ std::optional<OutlinerParaObject> pPara;
+ if( HasTextImpl( mpEditingOutliner ) )
+ {
+ sal_Int32 nParaCount = mpEditingOutliner->GetParagraphCount();
+ pPara = mpEditingOutliner->CreateParaObject(0, nParaCount);
+ }
+ return pPara;
+}
+
+void SdrTextObj::ImpSetCharStretching(SdrOutliner& rOutliner, const Size& rTextSize, const Size& rShapeSize, Fraction& rFitXCorrection)
+{
+ OutputDevice* pOut = rOutliner.GetRefDevice();
+ bool bNoStretching(false);
+
+ if(pOut && pOut->GetOutDevType() == OUTDEV_PRINTER)
+ {
+ // check whether CharStretching is possible at all
+ GDIMetaFile* pMtf = pOut->GetConnectMetaFile();
+ OUString aTestString(u'J');
+
+ if(pMtf && (!pMtf->IsRecord() || pMtf->IsPause()))
+ pMtf = nullptr;
+
+ if(pMtf)
+ pMtf->Pause(true);
+
+ vcl::Font aOriginalFont(pOut->GetFont());
+ vcl::Font aTmpFont( OutputDevice::GetDefaultFont( DefaultFontType::SERIF, LANGUAGE_SYSTEM, GetDefaultFontFlags::OnlyOne ) );
+
+ aTmpFont.SetFontSize(Size(0,100));
+ pOut->SetFont(aTmpFont);
+ Size aSize1(pOut->GetTextWidth(aTestString), pOut->GetTextHeight());
+ aTmpFont.SetFontSize(Size(800,100));
+ pOut->SetFont(aTmpFont);
+ Size aSize2(pOut->GetTextWidth(aTestString), pOut->GetTextHeight());
+ pOut->SetFont(aOriginalFont);
+
+ if(pMtf)
+ pMtf->Pause(false);
+
+ bNoStretching = (aSize1 == aSize2);
+
+#ifdef _WIN32
+ // Windows zooms the font proportionally when using Size(100,500),
+ // we don't like that.
+ if(aSize2.Height() >= aSize1.Height() * 2)
+ {
+ bNoStretching = true;
+ }
+#endif
+ }
+ unsigned nLoopCount=0;
+ bool bNoMoreLoop = false;
+ tools::Long nXDiff0=0x7FFFFFFF;
+ tools::Long nWantWdt=rShapeSize.Width();
+ tools::Long nIsWdt=rTextSize.Width();
+ if (nIsWdt==0) nIsWdt=1;
+
+ tools::Long nWantHgt=rShapeSize.Height();
+ tools::Long nIsHgt=rTextSize.Height();
+ if (nIsHgt==0) nIsHgt=1;
+
+ tools::Long nXTolPl=nWantWdt/100; // tolerance: +1%
+ tools::Long nXTolMi=nWantWdt/25; // tolerance: -4%
+ tools::Long nXCorr =nWantWdt/20; // correction scale: 5%
+
+ tools::Long nX=(nWantWdt*100) /nIsWdt; // calculate X stretching
+ tools::Long nY=(nWantHgt*100) /nIsHgt; // calculate Y stretching
+ bool bChkX = true;
+ if (bNoStretching) { // might only be possible proportionally
+ if (nX>nY) { nX=nY; bChkX=false; }
+ else { nY=nX; }
+ }
+
+ while (nLoopCount<5 && !bNoMoreLoop) {
+ if (nX<0) nX=-nX;
+ if (nX<1) { nX=1; bNoMoreLoop = true; }
+ if (nX>65535) { nX=65535; bNoMoreLoop = true; }
+
+ if (nY<0) nY=-nY;
+ if (nY<1) { nY=1; bNoMoreLoop = true; }
+ if (nY>65535) { nY=65535; bNoMoreLoop = true; }
+
+ // exception, there is no text yet (horizontal case)
+ if(nIsWdt <= 1)
+ {
+ nX = nY;
+ bNoMoreLoop = true;
+ }
+
+ // exception, there is no text yet (vertical case)
+ if(nIsHgt <= 1)
+ {
+ nY = nX;
+ bNoMoreLoop = true;
+ }
+
+ rOutliner.SetGlobalCharStretching(static_cast<sal_uInt16>(nX),static_cast<sal_uInt16>(nY));
+ nLoopCount++;
+ Size aSiz(rOutliner.CalcTextSize());
+ tools::Long nXDiff=aSiz.Width()-nWantWdt;
+ rFitXCorrection=Fraction(nWantWdt,aSiz.Width());
+ if (((nXDiff>=nXTolMi || !bChkX) && nXDiff<=nXTolPl) || nXDiff==nXDiff0) {
+ bNoMoreLoop = true;
+ } else {
+ // correct stretching factors
+ tools::Long nMul=nWantWdt;
+ tools::Long nDiv=aSiz.Width();
+ if (std::abs(nXDiff)<=2*nXCorr) {
+ if (nMul>nDiv) nDiv+=(nMul-nDiv)/2; // but only add half of what we calculated,
+ else nMul+=(nDiv-nMul)/2; // because the EditEngine calculates wrongly later on
+ }
+ nX=nX*nMul/nDiv;
+ if (bNoStretching) nY=nX;
+ }
+ nXDiff0=nXDiff;
+ }
+}
+
+OUString SdrTextObj::TakeObjNameSingul() const
+{
+ OUString aStr;
+
+ switch(meTextKind)
+ {
+ case SdrObjKind::OutlineText:
+ {
+ aStr = SvxResId(STR_ObjNameSingulOUTLINETEXT);
+ break;
+ }
+
+ case SdrObjKind::TitleText :
+ {
+ aStr = SvxResId(STR_ObjNameSingulTITLETEXT);
+ break;
+ }
+
+ default:
+ {
+ if(IsLinkedText())
+ aStr = SvxResId(STR_ObjNameSingulTEXTLNK);
+ else
+ aStr = SvxResId(STR_ObjNameSingulTEXT);
+ break;
+ }
+ }
+
+ OutlinerParaObject* pOutlinerParaObject = GetOutlinerParaObject();
+ if(pOutlinerParaObject && meTextKind != SdrObjKind::OutlineText)
+ {
+ // shouldn't currently cause any problems at OUTLINETEXT
+ OUString aStr2(comphelper::string::stripStart(pOutlinerParaObject->GetTextObject().GetText(0), ' '));
+
+ // avoid non expanded text portions in object name
+ // (second condition is new)
+ if(!aStr2.isEmpty() && aStr2.indexOf(u'\x00FF') == -1)
+ {
+ // space between ResStr and content text
+ aStr += " \'";
+
+ if(aStr2.getLength() > 10)
+ {
+ aStr2 = OUString::Concat(aStr2.subView(0, 8)) + "...";
+ }
+
+ aStr += aStr2 + "\'";
+ }
+ }
+
+ OUString sName(aStr);
+
+ OUString aName(GetName());
+ if (!aName.isEmpty())
+ sName += " '" + aName + "'";
+
+ return sName;
+}
+
+OUString SdrTextObj::TakeObjNamePlural() const
+{
+ OUString sName;
+ switch (meTextKind)
+ {
+ case SdrObjKind::OutlineText: sName=SvxResId(STR_ObjNamePluralOUTLINETEXT); break;
+ case SdrObjKind::TitleText : sName=SvxResId(STR_ObjNamePluralTITLETEXT); break;
+ default: {
+ if (IsLinkedText()) {
+ sName=SvxResId(STR_ObjNamePluralTEXTLNK);
+ } else {
+ sName=SvxResId(STR_ObjNamePluralTEXT);
+ }
+ } break;
+ } // switch
+ return sName;
+}
+
+SdrTextObj* SdrTextObj::CloneSdrObject(SdrModel& rTargetModel) const
+{
+ return new SdrTextObj(rTargetModel, *this);
+}
+
+basegfx::B2DPolyPolygon SdrTextObj::TakeXorPoly() const
+{
+ tools::Polygon aPol(maRect);
+ if (maGeo.nShearAngle) ShearPoly(aPol,maRect.TopLeft(),maGeo.mfTanShearAngle);
+ if (maGeo.nRotationAngle) RotatePoly(aPol,maRect.TopLeft(),maGeo.mfSinRotationAngle,maGeo.mfCosRotationAngle);
+
+ basegfx::B2DPolyPolygon aRetval;
+ aRetval.append(aPol.getB2DPolygon());
+ return aRetval;
+}
+
+basegfx::B2DPolyPolygon SdrTextObj::TakeContour() const
+{
+ basegfx::B2DPolyPolygon aRetval(SdrAttrObj::TakeContour());
+
+ // and now add the BoundRect of the text, if necessary
+ if ( GetOutlinerParaObject() && !IsFontwork() && !IsContourTextFrame() )
+ {
+ // using Clone()-Paint() strategy inside TakeContour() leaves a destroyed
+ // SdrObject as pointer in DrawOutliner. Set *this again in fetching the outliner
+ // in every case
+ SdrOutliner& rOutliner=ImpGetDrawOutliner();
+
+ tools::Rectangle aAnchor2;
+ tools::Rectangle aR;
+ TakeTextRect(rOutliner,aR,false,&aAnchor2);
+ rOutliner.Clear();
+ bool bFitToSize(IsFitToSize());
+ if (bFitToSize) aR=aAnchor2;
+ tools::Polygon aPol(aR);
+ if (maGeo.nRotationAngle) RotatePoly(aPol,aR.TopLeft(), maGeo.mfSinRotationAngle, maGeo.mfCosRotationAngle);
+
+ aRetval.append(aPol.getB2DPolygon());
+ }
+
+ return aRetval;
+}
+
+void SdrTextObj::RecalcSnapRect()
+{
+ if (maGeo.nRotationAngle || maGeo.nShearAngle)
+ {
+ maSnapRect = Rect2Poly(maRect, maGeo).GetBoundRect();
+ } else {
+ maSnapRect = maRect;
+ }
+}
+
+sal_uInt32 SdrTextObj::GetSnapPointCount() const
+{
+ return 4;
+}
+
+Point SdrTextObj::GetSnapPoint(sal_uInt32 i) const
+{
+ Point aP;
+ switch (i) {
+ case 0: aP=maRect.TopLeft(); break;
+ case 1: aP=maRect.TopRight(); break;
+ case 2: aP=maRect.BottomLeft(); break;
+ case 3: aP=maRect.BottomRight(); break;
+ default: aP=maRect.Center(); break;
+ }
+ if (maGeo.nShearAngle) ShearPoint(aP,maRect.TopLeft(),maGeo.mfTanShearAngle);
+ if (maGeo.nRotationAngle) RotatePoint(aP,maRect.TopLeft(),maGeo.mfSinRotationAngle,maGeo.mfCosRotationAngle);
+ return aP;
+}
+
+// Extracted from ImpGetDrawOutliner()
+void SdrTextObj::ImpInitDrawOutliner( SdrOutliner& rOutl ) const
+{
+ rOutl.SetUpdateLayout(false);
+ OutlinerMode nOutlinerMode = OutlinerMode::OutlineObject;
+ if ( !IsOutlText() )
+ nOutlinerMode = OutlinerMode::TextObject;
+ rOutl.Init( nOutlinerMode );
+
+ rOutl.SetGlobalCharStretching();
+ EEControlBits nStat=rOutl.GetControlWord();
+ nStat &= ~EEControlBits(EEControlBits::STRETCHING|EEControlBits::AUTOPAGESIZE);
+ rOutl.SetControlWord(nStat);
+ Size aMaxSize(100000,100000);
+ rOutl.SetMinAutoPaperSize(Size());
+ rOutl.SetMaxAutoPaperSize(aMaxSize);
+ rOutl.SetPaperSize(aMaxSize);
+ rOutl.ClearPolygon();
+}
+
+SdrOutliner& SdrTextObj::ImpGetDrawOutliner() const
+{
+ SdrOutliner& rOutl(getSdrModelFromSdrObject().GetDrawOutliner(this));
+
+ // Code extracted to ImpInitDrawOutliner()
+ ImpInitDrawOutliner( rOutl );
+
+ return rOutl;
+}
+
+// Extracted from Paint()
+void SdrTextObj::ImpSetupDrawOutlinerForPaint( bool bContourFrame,
+ SdrOutliner& rOutliner,
+ tools::Rectangle& rTextRect,
+ tools::Rectangle& rAnchorRect,
+ tools::Rectangle& rPaintRect,
+ Fraction& rFitXCorrection ) const
+{
+ if (!bContourFrame)
+ {
+ // FitToSize can't be used together with ContourFrame for now
+ if (IsFitToSize() || IsAutoFit())
+ {
+ EEControlBits nStat=rOutliner.GetControlWord();
+ nStat|=EEControlBits::STRETCHING|EEControlBits::AUTOPAGESIZE;
+ rOutliner.SetControlWord(nStat);
+ }
+ }
+ rOutliner.SetFixedCellHeight(GetMergedItem(SDRATTR_TEXT_USEFIXEDCELLHEIGHT).GetValue());
+ TakeTextRect(rOutliner, rTextRect, false, &rAnchorRect);
+ rPaintRect = rTextRect;
+
+ if (bContourFrame)
+ return;
+
+ // FitToSize can't be used together with ContourFrame for now
+ if (IsFitToSize())
+ {
+ ImpSetCharStretching(rOutliner,rTextRect.GetSize(),rAnchorRect.GetSize(),rFitXCorrection);
+ rPaintRect=rAnchorRect;
+ }
+ else if (IsAutoFit())
+ {
+ ImpAutoFitText(rOutliner);
+ }
+}
+
+sal_uInt16 SdrTextObj::GetFontScaleY() const
+{
+ SdrOutliner& rOutliner = ImpGetDrawOutliner();
+ // This eventually calls ImpAutoFitText
+ UpdateOutlinerFormatting(rOutliner, o3tl::temporary(tools::Rectangle()));
+
+ sal_uInt16 nStretchY;
+ rOutliner.GetGlobalCharStretching(o3tl::temporary(sal_uInt16()), nStretchY);
+ return nStretchY;
+}
+
+void SdrTextObj::ImpAutoFitText( SdrOutliner& rOutliner ) const
+{
+ const Size aShapeSize=GetSnapRect().GetSize();
+ ImpAutoFitText( rOutliner,
+ Size(aShapeSize.Width()-GetTextLeftDistance()-GetTextRightDistance(),
+ aShapeSize.Height()-GetTextUpperDistance()-GetTextLowerDistance()),
+ IsVerticalWriting() );
+}
+
+void SdrTextObj::ImpAutoFitText(SdrOutliner& rOutliner, const Size& rTextSize,
+ bool bIsVerticalWriting) const
+{
+ // EditEngine formatting is unstable enough for
+ // line-breaking text that we need some more samples
+
+ // loop early-exits if we detect an already attained value
+ sal_uInt16 nMinStretchX=0, nMinStretchY=0;
+ sal_uInt16 aOldStretchXVals[]={0,0,0,0,0,0,0,0,0,0};
+ const size_t aStretchArySize=SAL_N_ELEMENTS(aOldStretchXVals);
+ for(unsigned int i=0; i<aStretchArySize; ++i)
+ {
+ const Size aCurrTextSize = rOutliner.CalcTextSizeNTP();
+ double fFactor(1.0);
+ if( bIsVerticalWriting )
+ {
+ if (aCurrTextSize.Width() != 0)
+ {
+ fFactor = double(rTextSize.Width())/aCurrTextSize.Width();
+ }
+ }
+ else if (aCurrTextSize.Height() != 0)
+ {
+ fFactor = double(rTextSize.Height())/aCurrTextSize.Height();
+ }
+ // fFactor scales in both x and y directions
+ // - this is fine for bulleted words
+ // - but it scales too much for a long paragraph
+ // - taking sqrt scales long paragraphs the best
+ // - bulleted words will have to go through more iterations
+ fFactor = std::sqrt(fFactor);
+
+ sal_uInt16 nCurrStretchX, nCurrStretchY;
+ rOutliner.GetGlobalCharStretching(nCurrStretchX, nCurrStretchY);
+
+ if (fFactor >= 1.0 )
+ {
+ // resulting text area fits into available shape rect -
+ // err on the larger stretching, to optimally fill area
+ nMinStretchX = std::max(nMinStretchX,nCurrStretchX);
+ nMinStretchY = std::max(nMinStretchY,nCurrStretchY);
+ }
+
+ aOldStretchXVals[i] = nCurrStretchX;
+ if( std::find(aOldStretchXVals, aOldStretchXVals+i, nCurrStretchX) != aOldStretchXVals+i )
+ break; // same value already attained once; algo is looping, exit
+
+ if (fFactor < 1.0 || nCurrStretchX != 100)
+ {
+ nCurrStretchX = sal::static_int_cast<sal_uInt16>(nCurrStretchX*fFactor);
+ nCurrStretchY = sal::static_int_cast<sal_uInt16>(nCurrStretchY*fFactor);
+ rOutliner.SetGlobalCharStretching(std::min(sal_uInt16(100),nCurrStretchX),
+ std::min(sal_uInt16(100),nCurrStretchY));
+ SAL_INFO("svx", "zoom is " << nCurrStretchX);
+ }
+ }
+
+ const SdrTextFitToSizeTypeItem& rItem = GetObjectItem(SDRATTR_TEXT_FITTOSIZE);
+ if (rItem.GetMaxScale() > 0)
+ {
+ nMinStretchX = std::min<sal_uInt16>(rItem.GetMaxScale(), nMinStretchX);
+ nMinStretchY = std::min<sal_uInt16>(rItem.GetMaxScale(), nMinStretchY);
+ }
+
+ SAL_INFO("svx", "final zoom is " << nMinStretchX);
+ rOutliner.SetGlobalCharStretching(std::min(sal_uInt16(100),nMinStretchX),
+ std::min(sal_uInt16(100),nMinStretchY));
+}
+
+void SdrTextObj::SetupOutlinerFormatting( SdrOutliner& rOutl, tools::Rectangle& rPaintRect ) const
+{
+ ImpInitDrawOutliner( rOutl );
+ UpdateOutlinerFormatting( rOutl, rPaintRect );
+}
+
+void SdrTextObj::UpdateOutlinerFormatting( SdrOutliner& rOutl, tools::Rectangle& rPaintRect ) const
+{
+ tools::Rectangle aTextRect;
+ tools::Rectangle aAnchorRect;
+ Fraction aFitXCorrection(1,1);
+
+ const bool bContourFrame(IsContourTextFrame());
+ const MapMode aMapMode(
+ getSdrModelFromSdrObject().GetScaleUnit(),
+ Point(0,0),
+ getSdrModelFromSdrObject().GetScaleFraction(),
+ getSdrModelFromSdrObject().GetScaleFraction());
+
+ rOutl.SetRefMapMode(aMapMode);
+ ImpSetupDrawOutlinerForPaint(
+ bContourFrame,
+ rOutl,
+ aTextRect,
+ aAnchorRect,
+ rPaintRect,
+ aFitXCorrection);
+}
+
+
+OutlinerParaObject* SdrTextObj::GetOutlinerParaObject() const
+{
+ SdrText* pText = getActiveText();
+ if( pText )
+ return pText->GetOutlinerParaObject();
+ else
+ return nullptr;
+}
+
+void SdrTextObj::NbcSetOutlinerParaObject(std::optional<OutlinerParaObject> pTextObject)
+{
+ NbcSetOutlinerParaObjectForText( std::move(pTextObject), getActiveText() );
+}
+
+namespace
+{
+ bool IsAutoGrow(const SdrTextObj& rObj)
+ {
+ bool bAutoGrow = rObj.IsAutoGrowHeight() || rObj.IsAutoGrowWidth();
+ return bAutoGrow && !utl::ConfigManager::IsFuzzing();
+ }
+}
+
+void SdrTextObj::NbcSetOutlinerParaObjectForText( std::optional<OutlinerParaObject> pTextObject, SdrText* pText )
+{
+ if( pText )
+ pText->SetOutlinerParaObject( std::move(pTextObject) );
+
+ if (pText && pText->GetOutlinerParaObject())
+ {
+ SvxWritingModeItem aWritingMode(pText->GetOutlinerParaObject()->IsEffectivelyVertical() && pText->GetOutlinerParaObject()->IsTopToBottom()
+ ? css::text::WritingMode_TB_RL
+ : css::text::WritingMode_LR_TB,
+ SDRATTR_TEXTDIRECTION);
+ GetProperties().SetObjectItemDirect(aWritingMode);
+ }
+
+ SetTextSizeDirty();
+ if (IsTextFrame() && IsAutoGrow(*this))
+ { // adapt text frame!
+ NbcAdjustTextFrameWidthAndHeight();
+ }
+ if (!IsTextFrame())
+ {
+ // the SnapRect keeps its size
+ SetBoundAndSnapRectsDirty(true);
+ }
+
+ // always invalidate BoundRect on change
+ SetBoundRectDirty();
+ ActionChanged();
+
+ ImpSetTextStyleSheetListeners();
+}
+
+void SdrTextObj::NbcReformatText()
+{
+ SdrText* pText = getActiveText();
+ if( !(pText && pText->GetOutlinerParaObject()) )
+ return;
+
+ pText->ReformatText();
+ if (mbTextFrame)
+ {
+ NbcAdjustTextFrameWidthAndHeight();
+ }
+ else
+ {
+ // the SnapRect keeps its size
+ SetBoundRectDirty();
+ SetBoundAndSnapRectsDirty(/*bNotMyself*/true);
+ }
+ SetTextSizeDirty();
+ ActionChanged();
+ // i22396
+ // Necessary here since we have no compare operator at the outliner
+ // para object which may detect changes regarding the combination
+ // of outliner para data and configuration (e.g., change of
+ // formatting of text numerals)
+ GetViewContact().flushViewObjectContacts(false);
+}
+
+std::unique_ptr<SdrObjGeoData> SdrTextObj::NewGeoData() const
+{
+ return std::make_unique<SdrTextObjGeoData>();
+}
+
+void SdrTextObj::SaveGeoData(SdrObjGeoData& rGeo) const
+{
+ SdrAttrObj::SaveGeoData(rGeo);
+ SdrTextObjGeoData& rTGeo=static_cast<SdrTextObjGeoData&>(rGeo);
+ rTGeo.maRect = maRect;
+ rTGeo.maGeo = maGeo;
+}
+
+void SdrTextObj::RestoreGeoData(const SdrObjGeoData& rGeo)
+{ // RectsDirty is called by SdrObject
+ SdrAttrObj::RestoreGeoData(rGeo);
+ const SdrTextObjGeoData& rTGeo=static_cast<const SdrTextObjGeoData&>(rGeo);
+ NbcSetLogicRect(rTGeo.maRect);
+ maGeo = rTGeo.maGeo;
+ SetTextSizeDirty();
+}
+
+drawing::TextFitToSizeType SdrTextObj::GetFitToSize() const
+{
+ drawing::TextFitToSizeType eType = drawing::TextFitToSizeType_NONE;
+
+ if(!IsAutoGrowWidth())
+ eType = GetObjectItem(SDRATTR_TEXT_FITTOSIZE).GetValue();
+
+ return eType;
+}
+
+const tools::Rectangle& SdrTextObj::GetGeoRect() const
+{
+ return maRect;
+}
+
+void SdrTextObj::ForceOutlinerParaObject()
+{
+ SdrText* pText = getActiveText();
+ if( pText && (pText->GetOutlinerParaObject() == nullptr) )
+ {
+ OutlinerMode nOutlMode = OutlinerMode::TextObject;
+ if( IsTextFrame() && meTextKind == SdrObjKind::OutlineText )
+ nOutlMode = OutlinerMode::OutlineObject;
+
+ pText->ForceOutlinerParaObject( nOutlMode );
+ }
+}
+
+TextChain *SdrTextObj::GetTextChain() const
+{
+ //if (!IsChainable())
+ // return NULL;
+
+ return getSdrModelFromSdrObject().GetTextChain();
+}
+
+bool SdrTextObj::IsVerticalWriting() const
+{
+ if(mpEditingOutliner)
+ {
+ return mpEditingOutliner->IsVertical();
+ }
+
+ OutlinerParaObject* pOutlinerParaObject = GetOutlinerParaObject();
+ if(pOutlinerParaObject)
+ {
+ return pOutlinerParaObject->IsEffectivelyVertical();
+ }
+
+ return false;
+}
+
+void SdrTextObj::SetVerticalWriting(bool bVertical)
+{
+ OutlinerParaObject* pOutlinerParaObject = GetOutlinerParaObject();
+
+ if( !pOutlinerParaObject && bVertical )
+ {
+ // we only need to force an outliner para object if the default of
+ // horizontal text is changed
+ ForceOutlinerParaObject();
+ pOutlinerParaObject = GetOutlinerParaObject();
+ }
+
+ if (!pOutlinerParaObject ||
+ (pOutlinerParaObject->IsEffectivelyVertical() == bVertical))
+ return;
+
+ // get item settings
+ const SfxItemSet& rSet = GetObjectItemSet();
+ bool bAutoGrowWidth = rSet.Get(SDRATTR_TEXT_AUTOGROWWIDTH).GetValue();
+ bool bAutoGrowHeight = rSet.Get(SDRATTR_TEXT_AUTOGROWHEIGHT).GetValue();
+
+ // Also exchange hor/ver adjust items
+ SdrTextHorzAdjust eHorz = rSet.Get(SDRATTR_TEXT_HORZADJUST).GetValue();
+ SdrTextVertAdjust eVert = rSet.Get(SDRATTR_TEXT_VERTADJUST).GetValue();
+
+ // rescue object size
+ tools::Rectangle aObjectRect = GetSnapRect();
+
+ // prepare ItemSet to set exchanged width and height items
+ SfxItemSetFixed<SDRATTR_TEXT_AUTOGROWHEIGHT, SDRATTR_TEXT_AUTOGROWHEIGHT,
+ // Expanded item ranges to also support hor and ver adjust.
+ SDRATTR_TEXT_VERTADJUST, SDRATTR_TEXT_VERTADJUST,
+ SDRATTR_TEXT_AUTOGROWWIDTH, SDRATTR_TEXT_HORZADJUST> aNewSet(*rSet.GetPool());
+
+ aNewSet.Put(rSet);
+ aNewSet.Put(makeSdrTextAutoGrowWidthItem(bAutoGrowHeight));
+ aNewSet.Put(makeSdrTextAutoGrowHeightItem(bAutoGrowWidth));
+
+ // Exchange horz and vert adjusts
+ switch (eVert)
+ {
+ case SDRTEXTVERTADJUST_TOP: aNewSet.Put(SdrTextHorzAdjustItem(SDRTEXTHORZADJUST_RIGHT)); break;
+ case SDRTEXTVERTADJUST_CENTER: aNewSet.Put(SdrTextHorzAdjustItem(SDRTEXTHORZADJUST_CENTER)); break;
+ case SDRTEXTVERTADJUST_BOTTOM: aNewSet.Put(SdrTextHorzAdjustItem(SDRTEXTHORZADJUST_LEFT)); break;
+ case SDRTEXTVERTADJUST_BLOCK: aNewSet.Put(SdrTextHorzAdjustItem(SDRTEXTHORZADJUST_BLOCK)); break;
+ }
+ switch (eHorz)
+ {
+ case SDRTEXTHORZADJUST_LEFT: aNewSet.Put(SdrTextVertAdjustItem(SDRTEXTVERTADJUST_BOTTOM)); break;
+ case SDRTEXTHORZADJUST_CENTER: aNewSet.Put(SdrTextVertAdjustItem(SDRTEXTVERTADJUST_CENTER)); break;
+ case SDRTEXTHORZADJUST_RIGHT: aNewSet.Put(SdrTextVertAdjustItem(SDRTEXTVERTADJUST_TOP)); break;
+ case SDRTEXTHORZADJUST_BLOCK: aNewSet.Put(SdrTextVertAdjustItem(SDRTEXTVERTADJUST_BLOCK)); break;
+ }
+
+ SetObjectItemSet(aNewSet);
+
+ pOutlinerParaObject = GetOutlinerParaObject();
+ if (pOutlinerParaObject)
+ {
+ // set ParaObject orientation accordingly
+ pOutlinerParaObject->SetVertical(bVertical);
+ }
+
+ // restore object size
+ SetSnapRect(aObjectRect);
+}
+
+bool SdrTextObj::IsTopToBottom() const
+{
+ if (mpEditingOutliner)
+ return mpEditingOutliner->IsTopToBottom();
+
+ if (OutlinerParaObject* pOutlinerParaObject = GetOutlinerParaObject())
+ return pOutlinerParaObject->IsTopToBottom();
+
+ return false;
+}
+
+// transformation interface for StarOfficeAPI. This implements support for
+// homogeneous 3x3 matrices containing the transformation of the SdrObject. At the
+// moment it contains a shearX, rotation and translation, but for setting all linear
+// transforms like Scale, ShearX, ShearY, Rotate and Translate are supported.
+
+
+// gets base transformation and rectangle of object. If it's an SdrPathObj it fills the PolyPolygon
+// with the base geometry and returns TRUE. Otherwise it returns FALSE.
+bool SdrTextObj::TRGetBaseGeometry(basegfx::B2DHomMatrix& rMatrix, basegfx::B2DPolyPolygon& /*rPolyPolygon*/) const
+{
+ // get turn and shear
+ double fRotate = toRadians(maGeo.nRotationAngle);
+ double fShearX = toRadians(maGeo.nShearAngle);
+
+ // get aRect, this is the unrotated snaprect
+ tools::Rectangle aRectangle(maRect);
+
+ // fill other values
+ basegfx::B2DTuple aScale(aRectangle.GetWidth(), aRectangle.GetHeight());
+ basegfx::B2DTuple aTranslate(aRectangle.Left(), aRectangle.Top());
+
+ // position maybe relative to anchorpos, convert
+ if( getSdrModelFromSdrObject().IsWriter() )
+ {
+ if(GetAnchorPos().X() || GetAnchorPos().Y())
+ {
+ aTranslate -= basegfx::B2DTuple(GetAnchorPos().X(), GetAnchorPos().Y());
+ }
+ }
+
+ // build matrix
+ rMatrix = basegfx::utils::createScaleShearXRotateTranslateB2DHomMatrix(
+ aScale,
+ basegfx::fTools::equalZero(fShearX) ? 0.0 : tan(fShearX),
+ basegfx::fTools::equalZero(fRotate) ? 0.0 : -fRotate,
+ aTranslate);
+
+ return false;
+}
+
+// sets the base geometry of the object using infos contained in the homogeneous 3x3 matrix.
+// If it's an SdrPathObj it will use the provided geometry information. The Polygon has
+// to use (0,0) as upper left and will be scaled to the given size in the matrix.
+void SdrTextObj::TRSetBaseGeometry(const basegfx::B2DHomMatrix& rMatrix, const basegfx::B2DPolyPolygon& /*rPolyPolygon*/)
+{
+ // break up matrix
+ basegfx::B2DTuple aScale;
+ basegfx::B2DTuple aTranslate;
+ double fRotate(0.0);
+ double fShearX(0.0);
+ rMatrix.decompose(aScale, aTranslate, fRotate, fShearX);
+
+ // flip?
+ bool bFlipX = aScale.getX() < 0.0,
+ bFlipY = aScale.getY() < 0.0;
+ if (bFlipX)
+ {
+ aScale.setX(fabs(aScale.getX()));
+ }
+ if (bFlipY)
+ {
+ aScale.setY(fabs(aScale.getY()));
+ }
+
+ // reset object shear and rotations
+ maGeo.nRotationAngle = 0_deg100;
+ maGeo.RecalcSinCos();
+ maGeo.nShearAngle = 0_deg100;
+ maGeo.RecalcTan();
+
+ // if anchor is used, make position relative to it
+ if( getSdrModelFromSdrObject().IsWriter() )
+ {
+ if(GetAnchorPos().X() || GetAnchorPos().Y())
+ {
+ aTranslate += basegfx::B2DTuple(GetAnchorPos().X(), GetAnchorPos().Y());
+ }
+ }
+
+ // build and set BaseRect (use scale)
+ Size aSize(FRound(aScale.getX()), FRound(aScale.getY()));
+ tools::Rectangle aBaseRect(Point(), aSize);
+ SetSnapRect(aBaseRect);
+
+ // flip?
+ if (bFlipX)
+ {
+ Mirror(Point(), Point(0, 1));
+ }
+ if (bFlipY)
+ {
+ Mirror(Point(), Point(1, 0));
+ }
+
+ // shear?
+ if(!basegfx::fTools::equalZero(fShearX))
+ {
+ GeoStat aGeoStat;
+ aGeoStat.nShearAngle = Degree100(FRound(basegfx::rad2deg<100>(atan(fShearX))));
+ aGeoStat.RecalcTan();
+ Shear(Point(), aGeoStat.nShearAngle, aGeoStat.mfTanShearAngle, false);
+ }
+
+ // rotation?
+ if(!basegfx::fTools::equalZero(fRotate))
+ {
+ GeoStat aGeoStat;
+
+ // #i78696#
+ // fRotate is matematically correct, but aGeoStat.nRotationAngle is
+ // mirrored -> mirror value here
+ aGeoStat.nRotationAngle = NormAngle36000(Degree100(FRound(-basegfx::rad2deg<100>(fRotate))));
+ aGeoStat.RecalcSinCos();
+ Rotate(Point(), aGeoStat.nRotationAngle, aGeoStat.mfSinRotationAngle, aGeoStat.mfCosRotationAngle);
+ }
+
+ // translate?
+ if(!aTranslate.equalZero())
+ {
+ Move(Size(FRound(aTranslate.getX()), FRound(aTranslate.getY())));
+ }
+}
+
+bool SdrTextObj::IsReallyEdited() const
+{
+ return mpEditingOutliner && mpEditingOutliner->IsModified();
+}
+
+// moved inlines here form hxx
+
+tools::Long SdrTextObj::GetEckenradius() const
+{
+ return GetObjectItemSet().Get(SDRATTR_CORNER_RADIUS).GetValue();
+}
+
+tools::Long SdrTextObj::GetMinTextFrameHeight() const
+{
+ return GetObjectItemSet().Get(SDRATTR_TEXT_MINFRAMEHEIGHT).GetValue();
+}
+
+tools::Long SdrTextObj::GetMaxTextFrameHeight() const
+{
+ return GetObjectItemSet().Get(SDRATTR_TEXT_MAXFRAMEHEIGHT).GetValue();
+}
+
+tools::Long SdrTextObj::GetMinTextFrameWidth() const
+{
+ return GetObjectItemSet().Get(SDRATTR_TEXT_MINFRAMEWIDTH).GetValue();
+}
+
+tools::Long SdrTextObj::GetMaxTextFrameWidth() const
+{
+ return GetObjectItemSet().Get(SDRATTR_TEXT_MAXFRAMEWIDTH).GetValue();
+}
+
+bool SdrTextObj::IsFontwork() const
+{
+ return !mbTextFrame // Default is FALSE
+ && GetObjectItemSet().Get(XATTR_FORMTXTSTYLE).GetValue() != XFormTextStyle::NONE;
+}
+
+bool SdrTextObj::IsHideContour() const
+{
+ return !mbTextFrame // Default is: no, don't HideContour; HideContour not together with TextFrames
+ && GetObjectItemSet().Get(XATTR_FORMTXTHIDEFORM).GetValue();
+}
+
+bool SdrTextObj::IsContourTextFrame() const
+{
+ return !mbTextFrame // ContourFrame not together with normal TextFrames
+ && GetObjectItemSet().Get(SDRATTR_TEXT_CONTOURFRAME).GetValue();
+}
+
+tools::Long SdrTextObj::GetTextLeftDistance() const
+{
+ return GetObjectItemSet().Get(SDRATTR_TEXT_LEFTDIST).GetValue();
+}
+
+tools::Long SdrTextObj::GetTextRightDistance() const
+{
+ return GetObjectItemSet().Get(SDRATTR_TEXT_RIGHTDIST).GetValue();
+}
+
+tools::Long SdrTextObj::GetTextUpperDistance() const
+{
+ return GetObjectItemSet().Get(SDRATTR_TEXT_UPPERDIST).GetValue();
+}
+
+tools::Long SdrTextObj::GetTextLowerDistance() const
+{
+ return GetObjectItemSet().Get(SDRATTR_TEXT_LOWERDIST).GetValue();
+}
+
+SdrTextAniKind SdrTextObj::GetTextAniKind() const
+{
+ return GetObjectItemSet().Get(SDRATTR_TEXT_ANIKIND).GetValue();
+}
+
+SdrTextAniDirection SdrTextObj::GetTextAniDirection() const
+{
+ return GetObjectItemSet().Get(SDRATTR_TEXT_ANIDIRECTION).GetValue();
+}
+
+bool SdrTextObj::HasTextColumnsNumber() const
+{
+ return GetObjectItemSet().HasItem(SDRATTR_TEXTCOLUMNS_NUMBER);
+}
+
+sal_Int16 SdrTextObj::GetTextColumnsNumber() const
+{
+ return GetObjectItemSet().Get(SDRATTR_TEXTCOLUMNS_NUMBER).GetValue();
+}
+
+void SdrTextObj::SetTextColumnsNumber(sal_Int16 nColumns)
+{
+ SetObjectItem(SfxInt16Item(SDRATTR_TEXTCOLUMNS_NUMBER, nColumns));
+}
+
+bool SdrTextObj::HasTextColumnsSpacing() const
+{
+ return GetObjectItemSet().HasItem(SDRATTR_TEXTCOLUMNS_SPACING);
+}
+
+sal_Int32 SdrTextObj::GetTextColumnsSpacing() const
+{
+ return GetObjectItemSet().Get(SDRATTR_TEXTCOLUMNS_SPACING).GetValue();
+}
+
+void SdrTextObj::SetTextColumnsSpacing(sal_Int32 nSpacing)
+{
+ SetObjectItem(SdrMetricItem(SDRATTR_TEXTCOLUMNS_SPACING, nSpacing));
+}
+
+// Get necessary data for text scroll animation. ATM base it on a Text-Metafile and a
+// painting rectangle. Rotation is excluded from the returned values.
+GDIMetaFile* SdrTextObj::GetTextScrollMetaFileAndRectangle(
+ tools::Rectangle& rScrollRectangle, tools::Rectangle& rPaintRectangle)
+{
+ GDIMetaFile* pRetval = nullptr;
+ SdrOutliner& rOutliner = ImpGetDrawOutliner();
+ tools::Rectangle aTextRect;
+ tools::Rectangle aAnchorRect;
+ tools::Rectangle aPaintRect;
+ Fraction aFitXCorrection(1,1);
+ bool bContourFrame(IsContourTextFrame());
+
+ // get outliner set up. To avoid getting a somehow rotated MetaFile,
+ // temporarily disable object rotation.
+ Degree100 nAngle(maGeo.nRotationAngle);
+ maGeo.nRotationAngle = 0_deg100;
+ ImpSetupDrawOutlinerForPaint( bContourFrame, rOutliner, aTextRect, aAnchorRect, aPaintRect, aFitXCorrection );
+ maGeo.nRotationAngle = nAngle;
+
+ tools::Rectangle aScrollFrameRect(aPaintRect);
+ const SfxItemSet& rSet = GetObjectItemSet();
+ SdrTextAniDirection eDirection = rSet.Get(SDRATTR_TEXT_ANIDIRECTION).GetValue();
+
+ if(SdrTextAniDirection::Left == eDirection || SdrTextAniDirection::Right == eDirection)
+ {
+ aScrollFrameRect.SetLeft( aAnchorRect.Left() );
+ aScrollFrameRect.SetRight( aAnchorRect.Right() );
+ }
+
+ if(SdrTextAniDirection::Up == eDirection || SdrTextAniDirection::Down == eDirection)
+ {
+ aScrollFrameRect.SetTop( aAnchorRect.Top() );
+ aScrollFrameRect.SetBottom( aAnchorRect.Bottom() );
+ }
+
+ // create the MetaFile
+ pRetval = new GDIMetaFile;
+ ScopedVclPtrInstance< VirtualDevice > pBlackHole;
+ pBlackHole->EnableOutput(false);
+ pRetval->Record(pBlackHole);
+ Point aPaintPos = aPaintRect.TopLeft();
+
+ rOutliner.Draw(*pBlackHole, aPaintPos);
+
+ pRetval->Stop();
+ pRetval->WindStart();
+
+ // return PaintRectanglePixel and pRetval;
+ rScrollRectangle = aScrollFrameRect;
+ rPaintRectangle = aPaintRect;
+
+ return pRetval;
+}
+
+// Access to TextAnimationAllowed flag
+bool SdrTextObj::IsAutoFit() const
+{
+ return GetFitToSize() == drawing::TextFitToSizeType_AUTOFIT;
+}
+
+bool SdrTextObj::IsFitToSize() const
+{
+ const drawing::TextFitToSizeType eFit = GetFitToSize();
+ return (eFit == drawing::TextFitToSizeType_PROPORTIONAL
+ || eFit == drawing::TextFitToSizeType_ALLLINES);
+}
+
+void SdrTextObj::SetTextAnimationAllowed(bool bNew)
+{
+ if(mbTextAnimationAllowed != bNew)
+ {
+ mbTextAnimationAllowed = bNew;
+ ActionChanged();
+ }
+}
+
+/** called from the SdrObjEditView during text edit when the status of the edit outliner changes */
+void SdrTextObj::onEditOutlinerStatusEvent( EditStatus* pEditStatus )
+{
+ const EditStatusFlags nStat = pEditStatus->GetStatusWord();
+ const bool bGrowX = bool(nStat & EditStatusFlags::TEXTWIDTHCHANGED);
+ const bool bGrowY = bool(nStat & EditStatusFlags::TextHeightChanged);
+ if(!(mbTextFrame && (bGrowX || bGrowY)))
+ return;
+
+ if ((bGrowX && IsAutoGrowWidth()) || (bGrowY && IsAutoGrowHeight()))
+ {
+ AdjustTextFrameWidthAndHeight();
+ }
+ else if ( (IsAutoFit() || IsFitToSize()) && !mbInDownScale)
+ {
+ assert(mpEditingOutliner);
+ mbInDownScale = true;
+
+ // sucks that we cannot disable paints via
+ // mpEditingOutliner->SetUpdateMode(FALSE) - but EditEngine skips
+ // formatting as well, then.
+ ImpAutoFitText(*mpEditingOutliner);
+ mbInDownScale = false;
+ }
+}
+
+/* Begin chaining code */
+
+// XXX: Make it a method somewhere?
+static SdrObject *ImpGetObjByName(SdrObjList const *pObjList, std::u16string_view aObjName)
+{
+ // scan the whole list
+ size_t nObjCount = pObjList->GetObjCount();
+ for (size_t i = 0; i < nObjCount; i++) {
+ SdrObject *pCurObj = pObjList->GetObj(i);
+
+ if (pCurObj->GetName() == aObjName) {
+ return pCurObj;
+ }
+ }
+ // not found
+ return nullptr;
+}
+
+// XXX: Make it a (private) method of SdrTextObj
+static void ImpUpdateChainLinks(SdrTextObj *pTextObj, std::u16string_view aNextLinkName)
+{
+ // XXX: Current implementation constraints text boxes to be on the same page
+
+ // No next link
+ if (aNextLinkName.empty()) {
+ pTextObj->SetNextLinkInChain(nullptr);
+ return;
+ }
+
+ SdrPage *pPage(pTextObj->getSdrPageFromSdrObject());
+ assert(pPage);
+ SdrTextObj *pNextTextObj = dynamic_cast< SdrTextObj * >
+ (ImpGetObjByName(pPage, aNextLinkName));
+ if (!pNextTextObj) {
+ SAL_INFO("svx.chaining", "[CHAINING] Can't find object as next link.");
+ return;
+ }
+
+ pTextObj->SetNextLinkInChain(pNextTextObj);
+}
+
+bool SdrTextObj::IsChainable() const
+{
+ // Read it as item
+ const SfxItemSet& rSet = GetObjectItemSet();
+ OUString aNextLinkName = rSet.Get(SDRATTR_TEXT_CHAINNEXTNAME).GetValue();
+
+ // Update links if any inconsistency is found
+ bool bNextLinkUnsetYet = !aNextLinkName.isEmpty() && !mpNextInChain;
+ bool bInconsistentNextLink = mpNextInChain && mpNextInChain->GetName() != aNextLinkName;
+ // if the link is not set despite there should be one OR if it has changed
+ if (bNextLinkUnsetYet || bInconsistentNextLink) {
+ ImpUpdateChainLinks(const_cast<SdrTextObj *>(this), aNextLinkName);
+ }
+
+ return !aNextLinkName.isEmpty(); // XXX: Should we also check for GetNilChainingEvent? (see old code below)
+
+/*
+ // Check that no overflow is going on
+ if (!GetTextChain() || GetTextChain()->GetNilChainingEvent(this))
+ return false;
+*/
+}
+
+void SdrTextObj::onChainingEvent()
+{
+ if (!mpEditingOutliner)
+ return;
+
+ // Outliner for text transfer
+ SdrOutliner &aDrawOutliner = ImpGetDrawOutliner();
+
+ EditingTextChainFlow aTxtChainFlow(this);
+ aTxtChainFlow.CheckForFlowEvents(mpEditingOutliner);
+
+ if (aTxtChainFlow.IsOverflow()) {
+ SAL_INFO("svx.chaining", "[CHAINING] Overflow going on");
+ // One outliner is for non-overflowing text, the other for overflowing text
+ // We remove text directly from the editing outliner
+ aTxtChainFlow.ExecuteOverflow(mpEditingOutliner, &aDrawOutliner);
+ } else if (aTxtChainFlow.IsUnderflow()) {
+ SAL_INFO("svx.chaining", "[CHAINING] Underflow going on");
+ // underflow-induced overflow
+ aTxtChainFlow.ExecuteUnderflow(&aDrawOutliner);
+ bool bIsOverflowFromUnderflow = aTxtChainFlow.IsOverflow();
+ // handle overflow
+ if (bIsOverflowFromUnderflow) {
+ SAL_INFO("svx.chaining", "[CHAINING] Overflow going on (underflow induced)");
+ // prevents infinite loops when setting text for editing outliner
+ aTxtChainFlow.ExecuteOverflow(&aDrawOutliner, &aDrawOutliner);
+ }
+ }
+}
+
+SdrTextObj* SdrTextObj::GetNextLinkInChain() const
+{
+ /*
+ if (GetTextChain())
+ return GetTextChain()->GetNextLink(this);
+
+ return NULL;
+ */
+
+ return mpNextInChain;
+}
+
+void SdrTextObj::SetNextLinkInChain(SdrTextObj *pNextObj)
+{
+ // Basically a doubly linked list implementation
+
+ SdrTextObj *pOldNextObj = mpNextInChain;
+
+ // Replace next link
+ mpNextInChain = pNextObj;
+ // Deal with old next link's prev link
+ if (pOldNextObj) {
+ pOldNextObj->mpPrevInChain = nullptr;
+ }
+
+ // Deal with new next link's prev link
+ if (mpNextInChain) {
+ // If there is a prev already at all and this is not already the current object
+ if (mpNextInChain->mpPrevInChain &&
+ mpNextInChain->mpPrevInChain != this)
+ mpNextInChain->mpPrevInChain->mpNextInChain = nullptr;
+ mpNextInChain->mpPrevInChain = this;
+ }
+
+ // TODO: Introduce check for circular chains
+
+}
+
+SdrTextObj* SdrTextObj::GetPrevLinkInChain() const
+{
+ /*
+ if (GetTextChain())
+ return GetTextChain()->GetPrevLink(this);
+
+ return NULL;
+ */
+
+ return mpPrevInChain;
+}
+
+bool SdrTextObj::GetPreventChainable() const
+{
+ // Prevent chaining it 1) during dragging && 2) when we are editing next link
+ return mbIsUnchainableClone || (GetNextLinkInChain() && GetNextLinkInChain()->IsInEditMode());
+}
+
+SdrObjectUniquePtr SdrTextObj::getFullDragClone() const
+{
+ SdrObjectUniquePtr pClone = SdrAttrObj::getFullDragClone();
+ SdrTextObj *pTextObjClone = dynamic_cast<SdrTextObj *>(pClone.get());
+ if (pTextObjClone != nullptr) {
+ // Avoid transferring of text for chainable object during dragging
+ pTextObjClone->mbIsUnchainableClone = true;
+ }
+
+ return pClone;
+ }
+
+/* End chaining code */
+
+/** returns the currently active text. */
+SdrText* SdrTextObj::getActiveText() const
+{
+ if( !mpText )
+ return getText( 0 );
+ else
+ return mpText.get();
+}
+
+/** returns the nth available text. */
+SdrText* SdrTextObj::getText( sal_Int32 nIndex ) const
+{
+ if( nIndex == 0 )
+ {
+ if( !mpText )
+ const_cast< SdrTextObj* >(this)->mpText.reset( new SdrText( *const_cast< SdrTextObj* >(this) ) );
+ return mpText.get();
+ }
+ else
+ {
+ return nullptr;
+ }
+}
+
+/** returns the number of texts available for this object. */
+sal_Int32 SdrTextObj::getTextCount() const
+{
+ return 1;
+}
+
+/** changes the current active text */
+void SdrTextObj::setActiveText( sal_Int32 /*nIndex*/ )
+{
+}
+
+/** returns the index of the text that contains the given point or -1 */
+sal_Int32 SdrTextObj::CheckTextHit(const Point& /*rPnt*/) const
+{
+ return 0;
+}
+
+void SdrTextObj::SetObjectItemNoBroadcast(const SfxPoolItem& rItem)
+{
+ static_cast< sdr::properties::TextProperties& >(GetProperties()).SetObjectItemNoBroadcast(rItem);
+}
+
+
+// The concept of the text object:
+// ~~~~~~~~~~~~~~~~~~~~~~~~
+// Attributes/Variations:
+// - bool text frame / graphics object with caption
+// - bool FontWork (if it is not a text frame and not a ContourTextFrame)
+// - bool ContourTextFrame (if it is not a text frame and not Fontwork)
+// - long rotation angle (if it is not FontWork)
+// - long text frame margins (if it is not FontWork)
+// - bool FitToSize (if it is not FontWork)
+// - bool AutoGrowingWidth/Height (if it is not FitToSize and not FontWork)
+// - long Min/MaxFrameWidth/Height (if AutoGrowingWidth/Height)
+// - enum horizontal text anchoring left,center,right,justify/block,Stretch(ni)
+// - enum vertical text anchoring top, middle, bottom, block, stretch(ni)
+// - enum ticker text (if it is not FontWork)
+
+// Every derived object is either a text frame (mbTextFrame=true)
+// or a drawing object with a caption (mbTextFrame=false).
+
+// Default anchoring for text frames:
+// SDRTEXTHORZADJUST_BLOCK, SDRTEXTVERTADJUST_TOP
+// = static Pool defaults
+// Default anchoring for drawing objects with a caption:
+// SDRTEXTHORZADJUST_CENTER, SDRTEXTVERTADJUST_CENTER
+// via "hard" attribution of SdrAttrObj
+
+// Every object derived from SdrTextObj must return an "UnrotatedSnapRect"
+// (->TakeUnrotatedSnapRect()) (the reference point for the rotation is the top
+// left of the rectangle (maGeo.nRotationAngle)) which is the basis for anchoring
+// text. We then subtract the text frame margins from this rectangle, as a re-
+// sult we get the anchoring area (->TakeTextAnchorRect()). Within this area, we
+// calculate the anchoring point and the painting area, depending on the hori-
+// zontal and vertical adjustment of the text (SdrTextVertAdjust,
+// SdrTextHorzAdjust).
+// In the case of drawing objects with a caption the painting area might well
+// be larger than the anchoring area, for text frames on the other hand, it is
+// always of the same or a smaller size (except when there are negative text
+// frame margins).
+
+// FitToSize takes priority over text anchoring and AutoGrowHeight/Width. When
+// FitToSize is turned on, the painting area is always equal to the anchoring
+// area. Additionally, FitToSize doesn't allow automatic line breaks.
+
+// ContourTextFrame:
+// - long rotation angle
+// - long text frame margins (maybe later)
+// - bool FitToSize (maybe later)
+// - bool AutoGrowingWidth/Height (maybe much later)
+// - long Min/MaxFrameWidth/Height (maybe much later)
+// - enum horizontal text anchoring (maybe later, for now: left, centered)
+// - enum vertical text anchoring (maybe later, for now: top)
+// - enum ticker text (maybe later, maybe even with correct clipping)
+
+// When making changes, check these:
+// - Paint
+// - HitTest
+// - ConvertToPoly
+// - Edit
+// - Printing, Saving, Painting in neighboring View while editing
+// - ModelChanged (e. g. through a neighboring View or rulers) while editing
+// - FillColorChanged while editing
+// - and many more...
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/svdraw/svdotextdecomposition.cxx b/svx/source/svdraw/svdotextdecomposition.cxx
new file mode 100644
index 000000000..522375b1f
--- /dev/null
+++ b/svx/source/svdraw/svdotextdecomposition.cxx
@@ -0,0 +1,1698 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * 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/svdetc.hxx>
+#include <svx/svdoutl.hxx>
+#include <svx/svdpage.hxx>
+#include <svx/svdotext.hxx>
+#include <svx/svdmodel.hxx>
+#include <svx/sdasitm.hxx>
+#include <textchain.hxx>
+#include <textchainflow.hxx>
+#include <svx/sdtacitm.hxx>
+#include <svx/sdtayitm.hxx>
+#include <svx/sdtaiitm.hxx>
+#include <svx/sdtaaitm.hxx>
+#include <svx/xfillit0.hxx>
+#include <svx/xbtmpit.hxx>
+#include <basegfx/vector/b2dvector.hxx>
+#include <sdr/primitive2d/sdrtextprimitive2d.hxx>
+#include <drawinglayer/primitive2d/textprimitive2d.hxx>
+#include <drawinglayer/primitive2d/textdecoratedprimitive2d.hxx>
+#include <basegfx/range/b2drange.hxx>
+#include <editeng/eeitem.hxx>
+#include <editeng/editstat.hxx>
+#include <tools/helpers.hxx>
+#include <svl/itemset.hxx>
+#include <drawinglayer/animation/animationtiming.hxx>
+#include <basegfx/color/bcolor.hxx>
+#include <vcl/svapp.hxx>
+#include <editeng/escapementitem.hxx>
+#include <editeng/svxenum.hxx>
+#include <editeng/flditem.hxx>
+#include <editeng/adjustitem.hxx>
+#include <drawinglayer/primitive2d/texthierarchyprimitive2d.hxx>
+#include <drawinglayer/primitive2d/wrongspellprimitive2d.hxx>
+#include <drawinglayer/primitive2d/graphicprimitive2d.hxx>
+#include <drawinglayer/primitive2d/textlayoutdevice.hxx>
+#include <drawinglayer/primitive2d/unifiedtransparenceprimitive2d.hxx>
+#include <svx/unoapi.hxx>
+#include <drawinglayer/geometry/viewinformation2d.hxx>
+#include <editeng/outlobj.hxx>
+#include <basegfx/matrix/b2dhommatrixtools.hxx>
+#include <sal/log.hxx>
+#include <osl/diagnose.h>
+
+using namespace com::sun::star;
+
+// helpers
+
+namespace
+{
+ class impTextBreakupHandler
+ {
+ private:
+ drawinglayer::primitive2d::Primitive2DContainer maTextPortionPrimitives;
+ drawinglayer::primitive2d::Primitive2DContainer maLinePrimitives;
+ drawinglayer::primitive2d::Primitive2DContainer maParagraphPrimitives;
+
+ SdrOutliner& mrOutliner;
+ basegfx::B2DHomMatrix maNewTransformA;
+ basegfx::B2DHomMatrix maNewTransformB;
+
+ // the visible area for contour text decomposition
+ basegfx::B2DVector maScale;
+
+ // ClipRange for BlockText decomposition; only text portions completely
+ // inside are to be accepted, so this is different from geometric clipping
+ // (which would allow e.g. upper parts of portions to remain). Only used for
+ // BlockText (see there)
+ basegfx::B2DRange maClipRange;
+
+ DECL_LINK(decomposeContourTextPrimitive, DrawPortionInfo*, void);
+ DECL_LINK(decomposeBlockTextPrimitive, DrawPortionInfo*, void);
+ DECL_LINK(decomposeStretchTextPrimitive, DrawPortionInfo*, void);
+
+ DECL_LINK(decomposeContourBulletPrimitive, DrawBulletInfo*, void);
+ DECL_LINK(decomposeBlockBulletPrimitive, DrawBulletInfo*, void);
+ DECL_LINK(decomposeStretchBulletPrimitive, DrawBulletInfo*, void);
+
+ void impCreateTextPortionPrimitive(const DrawPortionInfo& rInfo);
+ static rtl::Reference<drawinglayer::primitive2d::BasePrimitive2D> impCheckFieldPrimitive(drawinglayer::primitive2d::BasePrimitive2D* pPrimitive, const DrawPortionInfo& rInfo);
+ void impFlushTextPortionPrimitivesToLinePrimitives();
+ void impFlushLinePrimitivesToParagraphPrimitives(sal_Int32 nPara);
+ void impHandleDrawPortionInfo(const DrawPortionInfo& rInfo);
+ void impHandleDrawBulletInfo(const DrawBulletInfo& rInfo);
+
+ public:
+ explicit impTextBreakupHandler(SdrOutliner& rOutliner)
+ : mrOutliner(rOutliner)
+ {
+ }
+
+ void decomposeContourTextPrimitive(const basegfx::B2DHomMatrix& rNewTransformA, const basegfx::B2DHomMatrix& rNewTransformB, const basegfx::B2DVector& rScale)
+ {
+ maScale = rScale;
+ maNewTransformA = rNewTransformA;
+ maNewTransformB = rNewTransformB;
+ mrOutliner.SetDrawPortionHdl(LINK(this, impTextBreakupHandler, decomposeContourTextPrimitive));
+ mrOutliner.SetDrawBulletHdl(LINK(this, impTextBreakupHandler, decomposeContourBulletPrimitive));
+ mrOutliner.StripPortions();
+ mrOutliner.SetDrawPortionHdl(Link<DrawPortionInfo*,void>());
+ mrOutliner.SetDrawBulletHdl(Link<DrawBulletInfo*,void>());
+ }
+
+ void decomposeBlockTextPrimitive(
+ const basegfx::B2DHomMatrix& rNewTransformA,
+ const basegfx::B2DHomMatrix& rNewTransformB,
+ const basegfx::B2DRange& rClipRange)
+ {
+ maNewTransformA = rNewTransformA;
+ maNewTransformB = rNewTransformB;
+ maClipRange = rClipRange;
+ mrOutliner.SetDrawPortionHdl(LINK(this, impTextBreakupHandler, decomposeBlockTextPrimitive));
+ mrOutliner.SetDrawBulletHdl(LINK(this, impTextBreakupHandler, decomposeBlockBulletPrimitive));
+ mrOutliner.StripPortions();
+ mrOutliner.SetDrawPortionHdl(Link<DrawPortionInfo*,void>());
+ mrOutliner.SetDrawBulletHdl(Link<DrawBulletInfo*,void>());
+ }
+
+ void decomposeStretchTextPrimitive(const basegfx::B2DHomMatrix& rNewTransformA, const basegfx::B2DHomMatrix& rNewTransformB)
+ {
+ maNewTransformA = rNewTransformA;
+ maNewTransformB = rNewTransformB;
+ mrOutliner.SetDrawPortionHdl(LINK(this, impTextBreakupHandler, decomposeStretchTextPrimitive));
+ mrOutliner.SetDrawBulletHdl(LINK(this, impTextBreakupHandler, decomposeStretchBulletPrimitive));
+ mrOutliner.StripPortions();
+ mrOutliner.SetDrawPortionHdl(Link<DrawPortionInfo*,void>());
+ mrOutliner.SetDrawBulletHdl(Link<DrawBulletInfo*,void>());
+ }
+
+ drawinglayer::primitive2d::Primitive2DContainer extractPrimitive2DSequence();
+ };
+
+ void impTextBreakupHandler::impCreateTextPortionPrimitive(const DrawPortionInfo& rInfo)
+ {
+ if(rInfo.maText.isEmpty() || !rInfo.mnTextLen)
+ return;
+
+ OUString caseMappedText = rInfo.mrFont.CalcCaseMap( rInfo.maText );
+ basegfx::B2DVector aFontScaling;
+ drawinglayer::attribute::FontAttribute aFontAttribute(
+ drawinglayer::primitive2d::getFontAttributeFromVclFont(
+ aFontScaling,
+ rInfo.mrFont,
+ rInfo.IsRTL(),
+ false));
+ basegfx::B2DHomMatrix aNewTransform;
+
+ // add font scale to new transform
+ aNewTransform.scale(aFontScaling.getX(), aFontScaling.getY());
+
+ // look for proportional font scaling, if necessary, scale accordingly
+ sal_Int8 nPropr(rInfo.mrFont.GetPropr());
+ if(100 != nPropr)
+ {
+ const double fFactor(rInfo.mrFont.GetPropr() / 100.0);
+ aNewTransform.scale(fFactor, fFactor);
+ }
+
+ // apply font rotate
+ if(rInfo.mrFont.GetOrientation())
+ {
+ aNewTransform.rotate(-toRadians(rInfo.mrFont.GetOrientation()));
+ }
+
+ // look for escapement, if necessary, translate accordingly
+ if(rInfo.mrFont.GetEscapement())
+ {
+ sal_Int16 nEsc(rInfo.mrFont.GetEscapement());
+
+ if(DFLT_ESC_AUTO_SUPER == nEsc)
+ {
+ nEsc = .8 * (100 - nPropr);
+ assert (nEsc == DFLT_ESC_SUPER && "I'm sure this formula needs to be changed, but how to confirm that???");
+ nEsc = DFLT_ESC_SUPER;
+ }
+ else if(DFLT_ESC_AUTO_SUB == nEsc)
+ {
+ nEsc = .2 * -(100 - nPropr);
+ assert (nEsc == -20 && "I'm sure this formula needs to be changed, but how to confirm that???");
+ nEsc = -20;
+ }
+
+ if(nEsc > MAX_ESC_POS)
+ {
+ nEsc = MAX_ESC_POS;
+ }
+ else if(nEsc < -MAX_ESC_POS)
+ {
+ nEsc = -MAX_ESC_POS;
+ }
+
+ const double fEscapement(nEsc / -100.0);
+ aNewTransform.translate(0.0, fEscapement * aFontScaling.getY());
+ }
+
+ // apply transformA
+ aNewTransform *= maNewTransformA;
+
+ // apply local offset
+ aNewTransform.translate(rInfo.mrStartPos.X(), rInfo.mrStartPos.Y());
+
+ // also apply embedding object's transform
+ aNewTransform *= maNewTransformB;
+
+ // prepare DXArray content. To make it independent from font size (and such from
+ // the text transformation), scale it to unit coordinates
+ ::std::vector< double > aDXArray;
+
+ if(!rInfo.mpDXArray.empty() && rInfo.mnTextLen)
+ {
+ aDXArray.reserve(rInfo.mnTextLen);
+
+ for(sal_Int32 a=0; a < rInfo.mnTextLen; a++)
+ {
+ aDXArray.push_back(static_cast<double>(rInfo.mpDXArray[a]));
+ }
+ }
+
+ // create complex text primitive and append
+ const Color aFontColor(rInfo.mrFont.GetColor());
+ const basegfx::BColor aBFontColor(aFontColor.getBColor());
+
+ const Color aTextFillColor(rInfo.mrFont.GetFillColor());
+
+ // prepare wordLineMode (for underline and strikeout)
+ // NOT for bullet texts. It is set (this may be an error by itself), but needs to be suppressed to hinder e.g. '1)'
+ // to be split which would not look like the original
+ const bool bWordLineMode(rInfo.mrFont.IsWordLineMode() && !rInfo.mbEndOfBullet);
+
+ // prepare new primitive
+ rtl::Reference<drawinglayer::primitive2d::BasePrimitive2D> pNewPrimitive;
+ const bool bDecoratedIsNeeded(
+ LINESTYLE_NONE != rInfo.mrFont.GetOverline()
+ || LINESTYLE_NONE != rInfo.mrFont.GetUnderline()
+ || STRIKEOUT_NONE != rInfo.mrFont.GetStrikeout()
+ || FontEmphasisMark::NONE != (rInfo.mrFont.GetEmphasisMark() & FontEmphasisMark::Style)
+ || FontRelief::NONE != rInfo.mrFont.GetRelief()
+ || rInfo.mrFont.IsShadow()
+ || bWordLineMode);
+
+ if(bDecoratedIsNeeded)
+ {
+ // TextDecoratedPortionPrimitive2D needed, prepare some more data
+ // get overline and underline color. If it's on automatic (0xffffffff) use FontColor instead
+ const Color aUnderlineColor(rInfo.maTextLineColor);
+ const basegfx::BColor aBUnderlineColor((aUnderlineColor == COL_AUTO) ? aBFontColor : aUnderlineColor.getBColor());
+ const Color aOverlineColor(rInfo.maOverlineColor);
+ const basegfx::BColor aBOverlineColor((aOverlineColor == COL_AUTO) ? aBFontColor : aOverlineColor.getBColor());
+
+ // prepare overline and underline data
+ const drawinglayer::primitive2d::TextLine eFontOverline(
+ drawinglayer::primitive2d::mapFontLineStyleToTextLine(rInfo.mrFont.GetOverline()));
+ const drawinglayer::primitive2d::TextLine eFontLineStyle(
+ drawinglayer::primitive2d::mapFontLineStyleToTextLine(rInfo.mrFont.GetUnderline()));
+
+ // check UnderlineAbove
+ const bool bUnderlineAbove(
+ drawinglayer::primitive2d::TEXT_LINE_NONE != eFontLineStyle && rInfo.mrFont.IsUnderlineAbove());
+
+ // prepare strikeout data
+ const drawinglayer::primitive2d::TextStrikeout eTextStrikeout(
+ drawinglayer::primitive2d::mapFontStrikeoutToTextStrikeout(rInfo.mrFont.GetStrikeout()));
+
+ // prepare emphasis mark data
+ drawinglayer::primitive2d::TextEmphasisMark eTextEmphasisMark(drawinglayer::primitive2d::TEXT_FONT_EMPHASIS_MARK_NONE);
+
+ switch(rInfo.mrFont.GetEmphasisMark() & FontEmphasisMark::Style)
+ {
+ case FontEmphasisMark::Dot : eTextEmphasisMark = drawinglayer::primitive2d::TEXT_FONT_EMPHASIS_MARK_DOT; break;
+ case FontEmphasisMark::Circle : eTextEmphasisMark = drawinglayer::primitive2d::TEXT_FONT_EMPHASIS_MARK_CIRCLE; break;
+ case FontEmphasisMark::Disc : eTextEmphasisMark = drawinglayer::primitive2d::TEXT_FONT_EMPHASIS_MARK_DISC; break;
+ case FontEmphasisMark::Accent : eTextEmphasisMark = drawinglayer::primitive2d::TEXT_FONT_EMPHASIS_MARK_ACCENT; break;
+ default: break;
+ }
+
+ const bool bEmphasisMarkAbove(rInfo.mrFont.GetEmphasisMark() & FontEmphasisMark::PosAbove);
+ const bool bEmphasisMarkBelow(rInfo.mrFont.GetEmphasisMark() & FontEmphasisMark::PosBelow);
+
+ // prepare font relief data
+ drawinglayer::primitive2d::TextRelief eTextRelief(drawinglayer::primitive2d::TEXT_RELIEF_NONE);
+
+ switch(rInfo.mrFont.GetRelief())
+ {
+ case FontRelief::Embossed : eTextRelief = drawinglayer::primitive2d::TEXT_RELIEF_EMBOSSED; break;
+ case FontRelief::Engraved : eTextRelief = drawinglayer::primitive2d::TEXT_RELIEF_ENGRAVED; break;
+ default : break; // RELIEF_NONE, FontRelief_FORCE_EQUAL_SIZE
+ }
+
+ // prepare shadow/outline data
+ const bool bShadow(rInfo.mrFont.IsShadow());
+
+ // TextDecoratedPortionPrimitive2D is needed, create one
+ pNewPrimitive = new drawinglayer::primitive2d::TextDecoratedPortionPrimitive2D(
+
+ // attributes for TextSimplePortionPrimitive2D
+ aNewTransform,
+ caseMappedText,
+ rInfo.mnTextStart,
+ rInfo.mnTextLen,
+ std::vector(aDXArray),
+ aFontAttribute,
+ rInfo.mpLocale ? *rInfo.mpLocale : css::lang::Locale(),
+ aBFontColor,
+ aTextFillColor,
+
+ // attributes for TextDecoratedPortionPrimitive2D
+ aBOverlineColor,
+ aBUnderlineColor,
+ eFontOverline,
+ eFontLineStyle,
+ bUnderlineAbove,
+ eTextStrikeout,
+ bWordLineMode,
+ eTextEmphasisMark,
+ bEmphasisMarkAbove,
+ bEmphasisMarkBelow,
+ eTextRelief,
+ bShadow);
+ }
+ else
+ {
+ // TextSimplePortionPrimitive2D is enough
+ pNewPrimitive = new drawinglayer::primitive2d::TextSimplePortionPrimitive2D(
+ aNewTransform,
+ caseMappedText,
+ rInfo.mnTextStart,
+ rInfo.mnTextLen,
+ std::vector(aDXArray),
+ aFontAttribute,
+ rInfo.mpLocale ? *rInfo.mpLocale : css::lang::Locale(),
+ aBFontColor,
+ rInfo.mbFilled,
+ rInfo.mnWidthToFill,
+ aTextFillColor);
+ }
+
+ if (aFontColor.IsTransparent())
+ {
+ // Handle semi-transparent text for both the decorated and simple case here.
+ pNewPrimitive = new drawinglayer::primitive2d::UnifiedTransparencePrimitive2D(
+ drawinglayer::primitive2d::Primitive2DContainer{ pNewPrimitive },
+ (255 - aFontColor.GetAlpha()) / 255.0);
+ }
+
+ if(rInfo.mbEndOfBullet)
+ {
+ // embed in TextHierarchyBulletPrimitive2D
+ drawinglayer::primitive2d::Primitive2DReference aNewReference(pNewPrimitive);
+ drawinglayer::primitive2d::Primitive2DContainer aNewSequence { aNewReference } ;
+ pNewPrimitive = new drawinglayer::primitive2d::TextHierarchyBulletPrimitive2D(std::move(aNewSequence));
+ }
+
+ if(rInfo.mpFieldData)
+ {
+ pNewPrimitive = impCheckFieldPrimitive(pNewPrimitive.get(), rInfo);
+ }
+
+ maTextPortionPrimitives.push_back(pNewPrimitive);
+
+ // support for WrongSpellVector. Create WrongSpellPrimitives as needed
+ if(!rInfo.mpWrongSpellVector || aDXArray.empty())
+ return;
+
+ const sal_Int32 nSize(rInfo.mpWrongSpellVector->size());
+ const sal_Int32 nDXCount(aDXArray.size());
+ const basegfx::BColor aSpellColor(1.0, 0.0, 0.0); // red, hard coded
+
+ for(sal_Int32 a(0); a < nSize; a++)
+ {
+ const EEngineData::WrongSpellClass& rCandidate = (*rInfo.mpWrongSpellVector)[a];
+
+ if(rCandidate.nStart >= rInfo.mnTextStart && rCandidate.nEnd >= rInfo.mnTextStart && rCandidate.nEnd > rCandidate.nStart)
+ {
+ const sal_Int32 nStart(rCandidate.nStart - rInfo.mnTextStart);
+ const sal_Int32 nEnd(rCandidate.nEnd - rInfo.mnTextStart);
+ double fStart(0.0);
+ double fEnd(0.0);
+
+ if(nStart > 0 && nStart - 1 < nDXCount)
+ {
+ fStart = aDXArray[nStart - 1];
+ }
+
+ if(nEnd > 0 && nEnd - 1 < nDXCount)
+ {
+ fEnd = aDXArray[nEnd - 1];
+ }
+
+ if(!basegfx::fTools::equal(fStart, fEnd))
+ {
+ if(rInfo.IsRTL())
+ {
+ // #i98523#
+ // When the portion is RTL, mirror the redlining using the
+ // full portion width
+ const double fTextWidth(aDXArray[aDXArray.size() - 1]);
+
+ fStart = fTextWidth - fStart;
+ fEnd = fTextWidth - fEnd;
+ }
+
+ // need to take FontScaling out of values; it's already part of
+ // aNewTransform and would be double applied
+ const double fFontScaleX(aFontScaling.getX());
+
+ if(!basegfx::fTools::equal(fFontScaleX, 1.0)
+ && !basegfx::fTools::equalZero(fFontScaleX))
+ {
+ fStart /= fFontScaleX;
+ fEnd /= fFontScaleX;
+ }
+
+ maTextPortionPrimitives.push_back(new drawinglayer::primitive2d::WrongSpellPrimitive2D(
+ aNewTransform,
+ fStart,
+ fEnd,
+ aSpellColor));
+ }
+ }
+ }
+ }
+
+ rtl::Reference<drawinglayer::primitive2d::BasePrimitive2D> impTextBreakupHandler::impCheckFieldPrimitive(drawinglayer::primitive2d::BasePrimitive2D* pPrimitive, const DrawPortionInfo& rInfo)
+ {
+ rtl::Reference<drawinglayer::primitive2d::BasePrimitive2D> xRet = pPrimitive;
+ if(rInfo.mpFieldData)
+ {
+ // Support for FIELD_SEQ_BEGIN, FIELD_SEQ_END. If used, create a TextHierarchyFieldPrimitive2D
+ // which holds the field type and, if applicable, the URL
+ const SvxURLField* pURLField = dynamic_cast< const SvxURLField* >(rInfo.mpFieldData);
+ const SvxPageField* pPageField = dynamic_cast< const SvxPageField* >(rInfo.mpFieldData);
+
+ // embed current primitive to a sequence
+ drawinglayer::primitive2d::Primitive2DContainer aSequence;
+
+ if(pPrimitive)
+ {
+ aSequence.resize(1);
+ aSequence[0] = drawinglayer::primitive2d::Primitive2DReference(pPrimitive);
+ }
+
+ if(pURLField)
+ {
+ // extended this to hold more of the contents of the original
+ // SvxURLField since that stuff is still used in HitTest and e.g. Calc
+ std::vector< std::pair< OUString, OUString>> meValues;
+ meValues.emplace_back("URL", pURLField->GetURL());
+ meValues.emplace_back("Representation", pURLField->GetRepresentation());
+ meValues.emplace_back("TargetFrame", pURLField->GetTargetFrame());
+ meValues.emplace_back("SvxURLFormat", OUString::number(static_cast<sal_uInt16>(pURLField->GetFormat())));
+ xRet = new drawinglayer::primitive2d::TextHierarchyFieldPrimitive2D(std::move(aSequence), drawinglayer::primitive2d::FIELD_TYPE_URL, &meValues);
+ }
+ else if(pPageField)
+ {
+ xRet = new drawinglayer::primitive2d::TextHierarchyFieldPrimitive2D(std::move(aSequence), drawinglayer::primitive2d::FIELD_TYPE_PAGE);
+ }
+ else
+ {
+ xRet = new drawinglayer::primitive2d::TextHierarchyFieldPrimitive2D(std::move(aSequence), drawinglayer::primitive2d::FIELD_TYPE_COMMON);
+ }
+ }
+
+ return xRet;
+ }
+
+ void impTextBreakupHandler::impFlushTextPortionPrimitivesToLinePrimitives()
+ {
+ // only create a line primitive when we had content; there is no need for
+ // empty line primitives (contrary to paragraphs, see below).
+ if(!maTextPortionPrimitives.empty())
+ {
+ maLinePrimitives.push_back(new drawinglayer::primitive2d::TextHierarchyLinePrimitive2D(std::move(maTextPortionPrimitives)));
+ }
+ }
+
+ void impTextBreakupHandler::impFlushLinePrimitivesToParagraphPrimitives(sal_Int32 nPara)
+ {
+ sal_Int16 nDepth = mrOutliner.GetDepth(nPara);
+ EBulletInfo eInfo = mrOutliner.GetBulletInfo(nPara);
+ // Pass -1 to signal VclMetafileProcessor2D that there is no active
+ // bullets/numbering in this paragraph (i.e. this is normal text)
+ const sal_Int16 nOutlineLevel( eInfo.bVisible ? nDepth : -1);
+
+ // ALWAYS create a paragraph primitive, even when no content was added. This is done to
+ // have the correct paragraph count even with empty paragraphs. Those paragraphs will
+ // have an empty sub-PrimitiveSequence.
+ maParagraphPrimitives.push_back(
+ new drawinglayer::primitive2d::TextHierarchyParagraphPrimitive2D(
+ std::move(maLinePrimitives),
+ nOutlineLevel));
+ }
+
+ void impTextBreakupHandler::impHandleDrawPortionInfo(const DrawPortionInfo& rInfo)
+ {
+ impCreateTextPortionPrimitive(rInfo);
+
+ if(rInfo.mbEndOfLine || rInfo.mbEndOfParagraph)
+ {
+ impFlushTextPortionPrimitivesToLinePrimitives();
+ }
+
+ if(rInfo.mbEndOfParagraph)
+ {
+ impFlushLinePrimitivesToParagraphPrimitives(rInfo.mnPara);
+ }
+ }
+
+ void impTextBreakupHandler::impHandleDrawBulletInfo(const DrawBulletInfo& rInfo)
+ {
+ basegfx::B2DHomMatrix aNewTransform;
+
+ // add size to new transform
+ aNewTransform.scale(rInfo.maBulletSize.getWidth(), rInfo.maBulletSize.getHeight());
+
+ // apply transformA
+ aNewTransform *= maNewTransformA;
+
+ // apply local offset
+ aNewTransform.translate(rInfo.maBulletPosition.X(), rInfo.maBulletPosition.Y());
+
+ // also apply embedding object's transform
+ aNewTransform *= maNewTransformB;
+
+ // prepare empty GraphicAttr
+ const GraphicAttr aGraphicAttr;
+
+ // create GraphicPrimitive2D
+ const drawinglayer::primitive2d::Primitive2DReference aNewReference(new drawinglayer::primitive2d::GraphicPrimitive2D(
+ aNewTransform,
+ rInfo.maBulletGraphicObject,
+ aGraphicAttr));
+
+ // embed in TextHierarchyBulletPrimitive2D
+ drawinglayer::primitive2d::Primitive2DContainer aNewSequence { aNewReference };
+ rtl::Reference<drawinglayer::primitive2d::BasePrimitive2D> pNewPrimitive = new drawinglayer::primitive2d::TextHierarchyBulletPrimitive2D(std::move(aNewSequence));
+
+ // add to output
+ maTextPortionPrimitives.push_back(pNewPrimitive);
+ }
+
+ IMPL_LINK(impTextBreakupHandler, decomposeContourTextPrimitive, DrawPortionInfo*, pInfo, void)
+ {
+ // for contour text, ignore (clip away) all portions which are below
+ // the visible area given by maScale
+ if(pInfo && static_cast<double>(pInfo->mrStartPos.Y()) < maScale.getY())
+ {
+ impHandleDrawPortionInfo(*pInfo);
+ }
+ }
+
+ IMPL_LINK(impTextBreakupHandler, decomposeBlockTextPrimitive, DrawPortionInfo*, pInfo, void)
+ {
+ if(!pInfo)
+ return;
+
+ // Is clipping wanted? This is text clipping; only accept a portion
+ // if it's completely in the range
+ if(!maClipRange.isEmpty())
+ {
+ // Test start position first; this allows to not get the text range at
+ // all if text is far outside
+ const basegfx::B2DPoint aStartPosition(pInfo->mrStartPos.X(), pInfo->mrStartPos.Y());
+
+ if(!maClipRange.isInside(aStartPosition))
+ {
+ return;
+ }
+
+ // Start position is inside. Get TextBoundRect and TopLeft next
+ drawinglayer::primitive2d::TextLayouterDevice aTextLayouterDevice;
+ aTextLayouterDevice.setFont(pInfo->mrFont);
+
+ const basegfx::B2DRange aTextBoundRect(
+ aTextLayouterDevice.getTextBoundRect(
+ pInfo->maText, pInfo->mnTextStart, pInfo->mnTextLen));
+ const basegfx::B2DPoint aTopLeft(aTextBoundRect.getMinimum() + aStartPosition);
+
+ if(!maClipRange.isInside(aTopLeft))
+ {
+ return;
+ }
+
+ // TopLeft is inside. Get BottomRight and check
+ const basegfx::B2DPoint aBottomRight(aTextBoundRect.getMaximum() + aStartPosition);
+
+ if(!maClipRange.isInside(aBottomRight))
+ {
+ return;
+ }
+
+ // all inside, clip was successful
+ }
+ impHandleDrawPortionInfo(*pInfo);
+ }
+
+ IMPL_LINK(impTextBreakupHandler, decomposeStretchTextPrimitive, DrawPortionInfo*, pInfo, void)
+ {
+ if(pInfo)
+ {
+ impHandleDrawPortionInfo(*pInfo);
+ }
+ }
+
+ IMPL_LINK(impTextBreakupHandler, decomposeContourBulletPrimitive, DrawBulletInfo*, pInfo, void)
+ {
+ if(pInfo)
+ {
+ impHandleDrawBulletInfo(*pInfo);
+ }
+ }
+
+ IMPL_LINK(impTextBreakupHandler, decomposeBlockBulletPrimitive, DrawBulletInfo*, pInfo, void)
+ {
+ if(pInfo)
+ {
+ impHandleDrawBulletInfo(*pInfo);
+ }
+ }
+
+ IMPL_LINK(impTextBreakupHandler, decomposeStretchBulletPrimitive, DrawBulletInfo*, pInfo, void)
+ {
+ if(pInfo)
+ {
+ impHandleDrawBulletInfo(*pInfo);
+ }
+ }
+
+ drawinglayer::primitive2d::Primitive2DContainer impTextBreakupHandler::extractPrimitive2DSequence()
+ {
+ if(!maTextPortionPrimitives.empty())
+ {
+ // collect non-closed lines
+ impFlushTextPortionPrimitivesToLinePrimitives();
+ }
+
+ if(!maLinePrimitives.empty())
+ {
+ // collect non-closed paragraphs
+ impFlushLinePrimitivesToParagraphPrimitives(mrOutliner.GetParagraphCount() - 1);
+ }
+
+ return std::move(maParagraphPrimitives);
+ }
+} // end of anonymous namespace
+
+
+// primitive decompositions
+
+void SdrTextObj::impDecomposeContourTextPrimitive(
+ drawinglayer::primitive2d::Primitive2DContainer& rTarget,
+ const drawinglayer::primitive2d::SdrContourTextPrimitive2D& rSdrContourTextPrimitive,
+ const drawinglayer::geometry::ViewInformation2D& aViewInformation) const
+{
+ // decompose matrix to have position and size of text
+ basegfx::B2DVector aScale, aTranslate;
+ double fRotate, fShearX;
+ rSdrContourTextPrimitive.getObjectTransform().decompose(aScale, aTranslate, fRotate, fShearX);
+
+ // prepare contour polygon, force to non-mirrored for laying out
+ basegfx::B2DPolyPolygon aPolyPolygon(rSdrContourTextPrimitive.getUnitPolyPolygon());
+ aPolyPolygon.transform(basegfx::utils::createScaleB2DHomMatrix(fabs(aScale.getX()), fabs(aScale.getY())));
+
+ // prepare outliner
+ SolarMutexGuard aSolarGuard;
+ SdrOutliner& rOutliner = ImpGetDrawOutliner();
+ const Size aNullSize;
+ rOutliner.SetPaperSize(aNullSize);
+ rOutliner.SetPolygon(aPolyPolygon);
+ rOutliner.SetUpdateLayout(true);
+ rOutliner.SetText(rSdrContourTextPrimitive.getOutlinerParaObject());
+
+ // set visualizing page at Outliner; needed e.g. for PageNumberField decomposition
+ rOutliner.setVisualizedPage(GetSdrPageFromXDrawPage(aViewInformation.getVisualizedPage()));
+
+ // prepare matrices to apply to newly created primitives
+ basegfx::B2DHomMatrix aNewTransformA;
+
+ // mirroring. We are now in the polygon sizes. When mirroring in X and Y,
+ // move the null point which was top left to bottom right.
+ const bool bMirrorX(basegfx::fTools::less(aScale.getX(), 0.0));
+ const bool bMirrorY(basegfx::fTools::less(aScale.getY(), 0.0));
+
+ // in-between the translations of the single primitives will take place. Afterwards,
+ // the object's transformations need to be applied
+ const basegfx::B2DHomMatrix aNewTransformB(basegfx::utils::createScaleShearXRotateTranslateB2DHomMatrix(
+ bMirrorX ? -1.0 : 1.0, bMirrorY ? -1.0 : 1.0,
+ fShearX, fRotate, aTranslate.getX(), aTranslate.getY()));
+
+ // now break up text primitives.
+ impTextBreakupHandler aConverter(rOutliner);
+ aConverter.decomposeContourTextPrimitive(aNewTransformA, aNewTransformB, aScale);
+
+ // cleanup outliner
+ rOutliner.Clear();
+ rOutliner.setVisualizedPage(nullptr);
+
+ rTarget = aConverter.extractPrimitive2DSequence();
+}
+
+void SdrTextObj::impDecomposeAutoFitTextPrimitive(
+ drawinglayer::primitive2d::Primitive2DContainer& rTarget,
+ const drawinglayer::primitive2d::SdrAutoFitTextPrimitive2D& rSdrAutofitTextPrimitive,
+ const drawinglayer::geometry::ViewInformation2D& aViewInformation) const
+{
+ // decompose matrix to have position and size of text
+ basegfx::B2DVector aScale, aTranslate;
+ double fRotate, fShearX;
+ rSdrAutofitTextPrimitive.getTextRangeTransform().decompose(aScale, aTranslate, fRotate, fShearX);
+
+ // use B2DRange aAnchorTextRange for calculations
+ basegfx::B2DRange aAnchorTextRange(aTranslate);
+ aAnchorTextRange.expand(aTranslate + aScale);
+
+ // prepare outliner
+ const SfxItemSet& rTextItemSet = rSdrAutofitTextPrimitive.getSdrText()->GetItemSet();
+ SolarMutexGuard aSolarGuard;
+ SdrOutliner& rOutliner = ImpGetDrawOutliner();
+ SdrTextVertAdjust eVAdj = GetTextVerticalAdjust(rTextItemSet);
+ SdrTextHorzAdjust eHAdj = GetTextHorizontalAdjust(rTextItemSet);
+ const EEControlBits nOriginalControlWord(rOutliner.GetControlWord());
+ const Size aNullSize;
+
+ // set visualizing page at Outliner; needed e.g. for PageNumberField decomposition
+ rOutliner.setVisualizedPage(GetSdrPageFromXDrawPage(aViewInformation.getVisualizedPage()));
+
+ rOutliner.SetControlWord(nOriginalControlWord|EEControlBits::AUTOPAGESIZE|EEControlBits::STRETCHING);
+ rOutliner.SetMinAutoPaperSize(aNullSize);
+ rOutliner.SetMaxAutoPaperSize(Size(1000000,1000000));
+
+ // That color needs to be restored on leaving this method
+ Color aOriginalBackColor(rOutliner.GetBackgroundColor());
+ setSuitableOutlinerBg(rOutliner);
+
+ // add one to rage sizes to get back to the old Rectangle and outliner measurements
+ const sal_uInt32 nAnchorTextWidth(FRound(aAnchorTextRange.getWidth() + 1));
+ const sal_uInt32 nAnchorTextHeight(FRound(aAnchorTextRange.getHeight() + 1));
+ const OutlinerParaObject* pOutlinerParaObject = rSdrAutofitTextPrimitive.getSdrText()->GetOutlinerParaObject();
+ OSL_ENSURE(pOutlinerParaObject, "impDecomposeBlockTextPrimitive used with no OutlinerParaObject (!)");
+ const bool bVerticalWriting(pOutlinerParaObject->IsEffectivelyVertical());
+ const bool bTopToBottom(pOutlinerParaObject->IsTopToBottom());
+ const Size aAnchorTextSize(Size(nAnchorTextWidth, nAnchorTextHeight));
+
+ if(rSdrAutofitTextPrimitive.getWordWrap() || IsTextFrame())
+ {
+ rOutliner.SetMaxAutoPaperSize(aAnchorTextSize);
+ }
+
+ if(SDRTEXTHORZADJUST_BLOCK == eHAdj && !bVerticalWriting)
+ {
+ rOutliner.SetMinAutoPaperSize(Size(nAnchorTextWidth, 0));
+ rOutliner.SetMinColumnWrapHeight(nAnchorTextHeight);
+ }
+
+ if(SDRTEXTVERTADJUST_BLOCK == eVAdj && bVerticalWriting)
+ {
+ rOutliner.SetMinAutoPaperSize(Size(0, nAnchorTextHeight));
+ rOutliner.SetMinColumnWrapHeight(nAnchorTextWidth);
+ }
+
+ rOutliner.SetPaperSize(aNullSize);
+ rOutliner.SetUpdateLayout(true);
+ rOutliner.SetText(*pOutlinerParaObject);
+ ImpAutoFitText(rOutliner,aAnchorTextSize,bVerticalWriting);
+
+ // set visualizing page at Outliner; needed e.g. for PageNumberField decomposition
+ rOutliner.setVisualizedPage(GetSdrPageFromXDrawPage(aViewInformation.getVisualizedPage()));
+
+ // now get back the layouted text size from outliner
+ const Size aOutlinerTextSize(rOutliner.GetPaperSize());
+ const basegfx::B2DVector aOutlinerScale(aOutlinerTextSize.Width(), aOutlinerTextSize.Height());
+ basegfx::B2DVector aAdjustTranslate(0.0, 0.0);
+
+ // correct horizontal translation using the now known text size
+ if(SDRTEXTHORZADJUST_CENTER == eHAdj || SDRTEXTHORZADJUST_RIGHT == eHAdj)
+ {
+ const double fFree(aAnchorTextRange.getWidth() - aOutlinerScale.getX());
+
+ if(SDRTEXTHORZADJUST_CENTER == eHAdj)
+ {
+ aAdjustTranslate.setX(fFree / 2.0);
+ }
+
+ if(SDRTEXTHORZADJUST_RIGHT == eHAdj)
+ {
+ aAdjustTranslate.setX(fFree);
+ }
+ }
+
+ // correct vertical translation using the now known text size
+ if(SDRTEXTVERTADJUST_CENTER == eVAdj || SDRTEXTVERTADJUST_BOTTOM == eVAdj)
+ {
+ const double fFree(aAnchorTextRange.getHeight() - aOutlinerScale.getY());
+
+ if(SDRTEXTVERTADJUST_CENTER == eVAdj)
+ {
+ aAdjustTranslate.setY(fFree / 2.0);
+ }
+
+ if(SDRTEXTVERTADJUST_BOTTOM == eVAdj)
+ {
+ aAdjustTranslate.setY(fFree);
+ }
+ }
+
+ // prepare matrices to apply to newly created primitives. aNewTransformA
+ // will get coordinates in aOutlinerScale size and positive in X, Y.
+ basegfx::B2DHomMatrix aNewTransformA;
+ basegfx::B2DHomMatrix aNewTransformB;
+
+ // translate relative to given primitive to get same rotation and shear
+ // as the master shape we are working on. For vertical, use the top-right
+ // corner
+ const double fStartInX(bVerticalWriting && bTopToBottom ? aAdjustTranslate.getX() + aOutlinerScale.getX() : aAdjustTranslate.getX());
+ const double fStartInY(bVerticalWriting && !bTopToBottom ? aAdjustTranslate.getY() + aOutlinerScale.getY() : aAdjustTranslate.getY());
+ aNewTransformA.translate(fStartInX, fStartInY);
+
+ // mirroring. We are now in aAnchorTextRange sizes. When mirroring in X and Y,
+ // move the null point which was top left to bottom right.
+ const bool bMirrorX(basegfx::fTools::less(aScale.getX(), 0.0));
+ const bool bMirrorY(basegfx::fTools::less(aScale.getY(), 0.0));
+ aNewTransformB.scale(bMirrorX ? -1.0 : 1.0, bMirrorY ? -1.0 : 1.0);
+
+ // in-between the translations of the single primitives will take place. Afterwards,
+ // the object's transformations need to be applied
+ aNewTransformB.shearX(fShearX);
+ aNewTransformB.rotate(fRotate);
+ aNewTransformB.translate(aTranslate.getX(), aTranslate.getY());
+
+ basegfx::B2DRange aClipRange;
+
+ // now break up text primitives.
+ impTextBreakupHandler aConverter(rOutliner);
+ aConverter.decomposeBlockTextPrimitive(aNewTransformA, aNewTransformB, aClipRange);
+
+ // cleanup outliner
+ rOutliner.SetBackgroundColor(aOriginalBackColor);
+ rOutliner.Clear();
+ rOutliner.setVisualizedPage(nullptr);
+ rOutliner.SetControlWord(nOriginalControlWord);
+
+ rTarget = aConverter.extractPrimitive2DSequence();
+}
+
+// Resolves: fdo#35779 set background color of this shape as the editeng background if there
+// is one. Check the shape itself, then the host page, then that page's master page.
+bool SdrObject::setSuitableOutlinerBg(::Outliner& rOutliner) const
+{
+ const SfxItemSet* pBackgroundFillSet = getBackgroundFillSet();
+ if (drawing::FillStyle_NONE != pBackgroundFillSet->Get(XATTR_FILLSTYLE).GetValue())
+ {
+ Color aColor(rOutliner.GetBackgroundColor());
+ GetDraftFillColor(*pBackgroundFillSet, aColor);
+ rOutliner.SetBackgroundColor(aColor);
+ return true;
+ }
+ return false;
+}
+
+const SfxItemSet* SdrObject::getBackgroundFillSet() const
+{
+ const SfxItemSet* pBackgroundFillSet = &GetObjectItemSet();
+
+ if (drawing::FillStyle_NONE == pBackgroundFillSet->Get(XATTR_FILLSTYLE).GetValue())
+ {
+ SdrPage* pOwnerPage(getSdrPageFromSdrObject());
+ if (pOwnerPage)
+ {
+ pBackgroundFillSet = &pOwnerPage->getSdrPageProperties().GetItemSet();
+
+ if (drawing::FillStyle_NONE == pBackgroundFillSet->Get(XATTR_FILLSTYLE).GetValue())
+ {
+ if (!pOwnerPage->IsMasterPage() && pOwnerPage->TRG_HasMasterPage())
+ {
+ pBackgroundFillSet = &pOwnerPage->TRG_GetMasterPage().getSdrPageProperties().GetItemSet();
+ }
+ }
+ }
+ }
+ return pBackgroundFillSet;
+}
+
+const Graphic* SdrObject::getFillGraphic() const
+{
+ if(IsGroupObject()) // Doesn't make sense, and GetObjectItemSet() asserts.
+ return nullptr;
+ const SfxItemSet* pBackgroundFillSet = getBackgroundFillSet();
+ if (drawing::FillStyle_BITMAP != pBackgroundFillSet->Get(XATTR_FILLSTYLE).GetValue())
+ return nullptr;
+ return &pBackgroundFillSet->Get(XATTR_FILLBITMAP).GetGraphicObject().GetGraphic();
+}
+
+void SdrTextObj::impDecomposeBlockTextPrimitive(
+ drawinglayer::primitive2d::Primitive2DContainer& rTarget,
+ const drawinglayer::primitive2d::SdrBlockTextPrimitive2D& rSdrBlockTextPrimitive,
+ const drawinglayer::geometry::ViewInformation2D& aViewInformation) const
+{
+ // decompose matrix to have position and size of text
+ basegfx::B2DVector aScale, aTranslate;
+ double fRotate, fShearX;
+ rSdrBlockTextPrimitive.getTextRangeTransform().decompose(aScale, aTranslate, fRotate, fShearX);
+
+ // use B2DRange aAnchorTextRange for calculations
+ basegfx::B2DRange aAnchorTextRange(aTranslate);
+ aAnchorTextRange.expand(aTranslate + aScale);
+
+ // prepare outliner
+ const bool bIsCell(rSdrBlockTextPrimitive.getCellText());
+ SolarMutexGuard aSolarGuard;
+ SdrOutliner& rOutliner = ImpGetDrawOutliner();
+ SdrTextHorzAdjust eHAdj = rSdrBlockTextPrimitive.getSdrTextHorzAdjust();
+ SdrTextVertAdjust eVAdj = rSdrBlockTextPrimitive.getSdrTextVertAdjust();
+ const EEControlBits nOriginalControlWord(rOutliner.GetControlWord());
+ const Size aNullSize;
+
+ // set visualizing page at Outliner; needed e.g. for PageNumberField decomposition
+ rOutliner.setVisualizedPage(GetSdrPageFromXDrawPage(aViewInformation.getVisualizedPage()));
+ rOutliner.SetFixedCellHeight(rSdrBlockTextPrimitive.isFixedCellHeight());
+ rOutliner.SetControlWord(nOriginalControlWord|EEControlBits::AUTOPAGESIZE);
+ rOutliner.SetMinAutoPaperSize(aNullSize);
+ rOutliner.SetMaxAutoPaperSize(Size(1000000,1000000));
+
+ // That color needs to be restored on leaving this method
+ Color aOriginalBackColor(rOutliner.GetBackgroundColor());
+ setSuitableOutlinerBg(rOutliner);
+
+ // add one to rage sizes to get back to the old Rectangle and outliner measurements
+ const sal_uInt32 nAnchorTextWidth(FRound(aAnchorTextRange.getWidth() + 1));
+ const sal_uInt32 nAnchorTextHeight(FRound(aAnchorTextRange.getHeight() + 1));
+ const bool bVerticalWriting(rSdrBlockTextPrimitive.getOutlinerParaObject().IsEffectivelyVertical());
+ const bool bTopToBottom(rSdrBlockTextPrimitive.getOutlinerParaObject().IsTopToBottom());
+ const Size aAnchorTextSize(Size(nAnchorTextWidth, nAnchorTextHeight));
+
+ if(bIsCell)
+ {
+ // cell text is formatted neither like a text object nor like an object
+ // text, so use a special setup here
+ rOutliner.SetMaxAutoPaperSize(aAnchorTextSize);
+
+ // #i106214# To work with an unchangeable PaperSize (CellSize in
+ // this case) Set(Min|Max)AutoPaperSize and SetPaperSize have to be used.
+ // #i106214# This was not completely correct; to still measure the real
+ // text height to allow vertical adjust (and vice versa for VerticalWritintg)
+ // only one aspect has to be set, but the other one to zero
+ if(bVerticalWriting)
+ {
+ // measure the horizontal text size
+ rOutliner.SetMinAutoPaperSize(Size(0, aAnchorTextSize.Height()));
+ }
+ else
+ {
+ // measure the vertical text size
+ rOutliner.SetMinAutoPaperSize(Size(aAnchorTextSize.Width(), 0));
+ }
+
+ rOutliner.SetPaperSize(aAnchorTextSize);
+ rOutliner.SetUpdateLayout(true);
+ rOutliner.SetText(rSdrBlockTextPrimitive.getOutlinerParaObject());
+ }
+ else
+ {
+ // check if block text is used (only one of them can be true)
+ const bool bHorizontalIsBlock(SDRTEXTHORZADJUST_BLOCK == eHAdj && !bVerticalWriting);
+ const bool bVerticalIsBlock(SDRTEXTVERTADJUST_BLOCK == eVAdj && bVerticalWriting);
+
+ // set minimal paper size horizontally/vertically if needed
+ if(bHorizontalIsBlock)
+ {
+ rOutliner.SetMinAutoPaperSize(Size(nAnchorTextWidth, 0));
+ rOutliner.SetMinColumnWrapHeight(nAnchorTextHeight);
+ }
+ else if(bVerticalIsBlock)
+ {
+ rOutliner.SetMinAutoPaperSize(Size(0, nAnchorTextHeight));
+ rOutliner.SetMinColumnWrapHeight(nAnchorTextWidth);
+ }
+
+ if((rSdrBlockTextPrimitive.getWordWrap() || IsTextFrame()) && !rSdrBlockTextPrimitive.getUnlimitedPage())
+ {
+ // #i103454# maximal paper size hor/ver needs to be limited to text
+ // frame size. If it's block text, still allow the 'other' direction
+ // to grow to get a correct real text size when using GetPaperSize().
+ // When just using aAnchorTextSize as maximum, GetPaperSize()
+ // would just return aAnchorTextSize again: this means, the wanted
+ // 'measurement' of the real size of block text would not work
+ Size aMaxAutoPaperSize(aAnchorTextSize);
+
+ // Usual processing - always grow in one of directions
+ bool bAllowGrowVertical = !bVerticalWriting;
+ bool bAllowGrowHorizontal = bVerticalWriting;
+
+ // Compatibility mode for tdf#99729
+ if (getSdrModelFromSdrObject().IsAnchoredTextOverflowLegacy())
+ {
+ bAllowGrowVertical = bHorizontalIsBlock;
+ bAllowGrowHorizontal = bVerticalIsBlock;
+ }
+
+ if (bAllowGrowVertical)
+ {
+ // allow to grow vertical for horizontal texts
+ aMaxAutoPaperSize.setHeight(1000000);
+ }
+ else if (bAllowGrowHorizontal)
+ {
+ // allow to grow horizontal for vertical texts
+ aMaxAutoPaperSize.setWidth(1000000);
+ }
+
+ rOutliner.SetMaxAutoPaperSize(aMaxAutoPaperSize);
+ }
+
+ rOutliner.SetPaperSize(aNullSize);
+ rOutliner.SetUpdateLayout(true);
+ rOutliner.SetText(rSdrBlockTextPrimitive.getOutlinerParaObject());
+ }
+
+ rOutliner.SetControlWord(nOriginalControlWord);
+
+ // now get back the layouted text size from outliner
+ const Size aOutlinerTextSize(rOutliner.GetPaperSize());
+ const basegfx::B2DVector aOutlinerScale(aOutlinerTextSize.Width(), aOutlinerTextSize.Height());
+ basegfx::B2DVector aAdjustTranslate(0.0, 0.0);
+
+ // For draw objects containing text correct hor/ver alignment if text is bigger
+ // than the object itself. Without that correction, the text would always be
+ // formatted to the left edge (or top edge when vertical) of the draw object.
+ if(!IsTextFrame() && !bIsCell)
+ {
+ if(aAnchorTextRange.getWidth() < aOutlinerScale.getX() && !bVerticalWriting)
+ {
+ // Horizontal case here. Correct only if eHAdj == SDRTEXTHORZADJUST_BLOCK,
+ // else the alignment is wanted.
+ if(SDRTEXTHORZADJUST_BLOCK == eHAdj)
+ {
+ SvxAdjust eAdjust = GetObjectItemSet().Get(EE_PARA_JUST).GetAdjust();
+ switch(eAdjust)
+ {
+ case SvxAdjust::Left: eHAdj = SDRTEXTHORZADJUST_LEFT; break;
+ case SvxAdjust::Right: eHAdj = SDRTEXTHORZADJUST_RIGHT; break;
+ case SvxAdjust::Center: eHAdj = SDRTEXTHORZADJUST_CENTER; break;
+ default: break;
+ }
+ }
+ }
+
+ if(aAnchorTextRange.getHeight() < aOutlinerScale.getY() && bVerticalWriting)
+ {
+ // Vertical case here. Correct only if eHAdj == SDRTEXTVERTADJUST_BLOCK,
+ // else the alignment is wanted.
+ if(SDRTEXTVERTADJUST_BLOCK == eVAdj)
+ {
+ eVAdj = SDRTEXTVERTADJUST_CENTER;
+ }
+ }
+ }
+
+ // correct horizontal translation using the now known text size
+ if(SDRTEXTHORZADJUST_CENTER == eHAdj || SDRTEXTHORZADJUST_RIGHT == eHAdj)
+ {
+ const double fFree(aAnchorTextRange.getWidth() - aOutlinerScale.getX());
+
+ if(SDRTEXTHORZADJUST_CENTER == eHAdj)
+ {
+ aAdjustTranslate.setX(fFree / 2.0);
+ }
+
+ if(SDRTEXTHORZADJUST_RIGHT == eHAdj)
+ {
+ aAdjustTranslate.setX(fFree);
+ }
+ }
+
+ // correct vertical translation using the now known text size
+ if(SDRTEXTVERTADJUST_CENTER == eVAdj || SDRTEXTVERTADJUST_BOTTOM == eVAdj)
+ {
+ const double fFree(aAnchorTextRange.getHeight() - aOutlinerScale.getY());
+
+ if(SDRTEXTVERTADJUST_CENTER == eVAdj)
+ {
+ aAdjustTranslate.setY(fFree / 2.0);
+ }
+
+ if(SDRTEXTVERTADJUST_BOTTOM == eVAdj)
+ {
+ aAdjustTranslate.setY(fFree);
+ }
+ }
+
+ // prepare matrices to apply to newly created primitives. aNewTransformA
+ // will get coordinates in aOutlinerScale size and positive in X, Y.
+ // Translate relative to given primitive to get same rotation and shear
+ // as the master shape we are working on. For vertical, use the top-right
+ // corner
+ const double fStartInX(bVerticalWriting && bTopToBottom ? aAdjustTranslate.getX() + aOutlinerScale.getX() : aAdjustTranslate.getX());
+ const double fStartInY(bVerticalWriting && !bTopToBottom ? aAdjustTranslate.getY() + aOutlinerScale.getY() : aAdjustTranslate.getY());
+ basegfx::B2DHomMatrix aNewTransformA(basegfx::utils::createTranslateB2DHomMatrix(fStartInX, fStartInY));
+
+ // Apply the camera rotation. It have to be applied after adjustment of
+ // the text (top, bottom, center, left, right).
+ if(GetCameraZRotation() != 0)
+ {
+ // First find the text rect.
+ basegfx::B2DRange aTextRectangle(/*x1=*/aTranslate.getX() + aAdjustTranslate.getX(),
+ /*y1=*/aTranslate.getY() + aAdjustTranslate.getY(),
+ /*x2=*/aTranslate.getX() + aOutlinerScale.getX() - aAdjustTranslate.getX(),
+ /*y2=*/aTranslate.getY() + aOutlinerScale.getY() - aAdjustTranslate.getY());
+
+ // Rotate the text from the center point.
+ basegfx::B2DVector aTranslateToCenter(aTextRectangle.getWidth() / 2, aTextRectangle.getHeight() / 2);
+ aNewTransformA.translate(-aTranslateToCenter.getX(), -aTranslateToCenter.getY());
+ aNewTransformA.rotate(basegfx::deg2rad(360.0 - GetCameraZRotation() ));
+ aNewTransformA.translate(aTranslateToCenter.getX(), aTranslateToCenter.getY());
+ }
+
+ // mirroring. We are now in aAnchorTextRange sizes. When mirroring in X and Y,
+ // move the null point which was top left to bottom right.
+ const bool bMirrorX(basegfx::fTools::less(aScale.getX(), 0.0));
+ const bool bMirrorY(basegfx::fTools::less(aScale.getY(), 0.0));
+
+ // in-between the translations of the single primitives will take place. Afterwards,
+ // the object's transformations need to be applied
+ const basegfx::B2DHomMatrix aNewTransformB(basegfx::utils::createScaleShearXRotateTranslateB2DHomMatrix(
+ bMirrorX ? -1.0 : 1.0, bMirrorY ? -1.0 : 1.0,
+ fShearX, fRotate, aTranslate.getX(), aTranslate.getY()));
+
+
+ // create ClipRange (if needed)
+ basegfx::B2DRange aClipRange;
+
+ // now break up text primitives.
+ impTextBreakupHandler aConverter(rOutliner);
+ aConverter.decomposeBlockTextPrimitive(aNewTransformA, aNewTransformB, aClipRange);
+
+ // cleanup outliner
+ rOutliner.SetBackgroundColor(aOriginalBackColor);
+ rOutliner.Clear();
+ rOutliner.setVisualizedPage(nullptr);
+
+ rTarget = aConverter.extractPrimitive2DSequence();
+}
+
+void SdrTextObj::impDecomposeStretchTextPrimitive(
+ drawinglayer::primitive2d::Primitive2DContainer& rTarget,
+ const drawinglayer::primitive2d::SdrStretchTextPrimitive2D& rSdrStretchTextPrimitive,
+ const drawinglayer::geometry::ViewInformation2D& aViewInformation) const
+{
+ // decompose matrix to have position and size of text
+ basegfx::B2DVector aScale, aTranslate;
+ double fRotate, fShearX;
+ rSdrStretchTextPrimitive.getTextRangeTransform().decompose(aScale, aTranslate, fRotate, fShearX);
+
+ // prepare outliner
+ SolarMutexGuard aSolarGuard;
+ SdrOutliner& rOutliner = ImpGetDrawOutliner();
+ const EEControlBits nOriginalControlWord(rOutliner.GetControlWord());
+ const Size aNullSize;
+
+ rOutliner.SetControlWord(nOriginalControlWord|EEControlBits::STRETCHING|EEControlBits::AUTOPAGESIZE);
+ rOutliner.SetFixedCellHeight(rSdrStretchTextPrimitive.isFixedCellHeight());
+ rOutliner.SetMinAutoPaperSize(aNullSize);
+ rOutliner.SetMaxAutoPaperSize(Size(1000000,1000000));
+ rOutliner.SetPaperSize(aNullSize);
+ rOutliner.SetUpdateLayout(true);
+ rOutliner.SetText(rSdrStretchTextPrimitive.getOutlinerParaObject());
+
+ // set visualizing page at Outliner; needed e.g. for PageNumberField decomposition
+ rOutliner.setVisualizedPage(GetSdrPageFromXDrawPage(aViewInformation.getVisualizedPage()));
+
+ // now get back the laid out text size from outliner
+ const Size aOutlinerTextSize(rOutliner.CalcTextSize());
+ const basegfx::B2DVector aOutlinerScale(
+ aOutlinerTextSize.Width() == tools::Long(0) ? 1.0 : aOutlinerTextSize.Width(),
+ aOutlinerTextSize.Height() == tools::Long(0) ? 1.0 : aOutlinerTextSize.Height());
+
+ // prepare matrices to apply to newly created primitives
+ basegfx::B2DHomMatrix aNewTransformA;
+
+ // #i101957# Check for vertical text. If used, aNewTransformA
+ // needs to translate the text initially around object width to orient
+ // it relative to the topper right instead of the topper left
+ const bool bVertical(rSdrStretchTextPrimitive.getOutlinerParaObject().IsEffectivelyVertical());
+ const bool bTopToBottom(rSdrStretchTextPrimitive.getOutlinerParaObject().IsTopToBottom());
+
+ if(bVertical)
+ {
+ if(bTopToBottom)
+ aNewTransformA.translate(aScale.getX(), 0.0);
+ else
+ aNewTransformA.translate(0.0, aScale.getY());
+ }
+
+ // calculate global char stretching scale parameters. Use non-mirrored sizes
+ // to layout without mirroring
+ const double fScaleX(fabs(aScale.getX()) / aOutlinerScale.getX());
+ const double fScaleY(fabs(aScale.getY()) / aOutlinerScale.getY());
+ rOutliner.SetGlobalCharStretching(static_cast<sal_Int16>(FRound(fScaleX * 100.0)), static_cast<sal_Int16>(FRound(fScaleY * 100.0)));
+
+ // When mirroring in X and Y,
+ // move the null point which was top left to bottom right.
+ const bool bMirrorX(basegfx::fTools::less(aScale.getX(), 0.0));
+ const bool bMirrorY(basegfx::fTools::less(aScale.getY(), 0.0));
+
+ // in-between the translations of the single primitives will take place. Afterwards,
+ // the object's transformations need to be applied
+ const basegfx::B2DHomMatrix aNewTransformB(basegfx::utils::createScaleShearXRotateTranslateB2DHomMatrix(
+ bMirrorX ? -1.0 : 1.0, bMirrorY ? -1.0 : 1.0,
+ fShearX, fRotate, aTranslate.getX(), aTranslate.getY()));
+
+ // now break up text primitives.
+ impTextBreakupHandler aConverter(rOutliner);
+ aConverter.decomposeStretchTextPrimitive(aNewTransformA, aNewTransformB);
+
+ // cleanup outliner
+ rOutliner.SetControlWord(nOriginalControlWord);
+ rOutliner.Clear();
+ rOutliner.setVisualizedPage(nullptr);
+
+ rTarget = aConverter.extractPrimitive2DSequence();
+}
+
+
+// timing generators
+#define ENDLESS_LOOP (0xffffffff)
+#define ENDLESS_TIME (double(0xffffffff))
+#define PIXEL_DPI (96.0)
+
+void SdrTextObj::impGetBlinkTextTiming(drawinglayer::animation::AnimationEntryList& rAnimList) const
+{
+ if(SdrTextAniKind::Blink != GetTextAniKind())
+ return;
+
+ // get values
+ const SfxItemSet& rSet = GetObjectItemSet();
+ const sal_uInt32 nRepeat(static_cast<sal_uInt32>(rSet.Get(SDRATTR_TEXT_ANICOUNT).GetValue()));
+ double fDelay(static_cast<double>(rSet.Get(SDRATTR_TEXT_ANIDELAY).GetValue()));
+
+ if(0.0 == fDelay)
+ {
+ // use default
+ fDelay = 250.0;
+ }
+
+ // prepare loop and add
+ drawinglayer::animation::AnimationEntryLoop aLoop(nRepeat ? nRepeat : ENDLESS_LOOP);
+ drawinglayer::animation::AnimationEntryFixed aStart(fDelay, 0.0);
+ aLoop.append(aStart);
+ drawinglayer::animation::AnimationEntryFixed aEnd(fDelay, 1.0);
+ aLoop.append(aEnd);
+ rAnimList.append(aLoop);
+
+ // add stopped state if loop is not endless
+ if(0 != nRepeat)
+ {
+ bool bVisibleWhenStopped(rSet.Get(SDRATTR_TEXT_ANISTOPINSIDE).GetValue());
+ drawinglayer::animation::AnimationEntryFixed aStop(ENDLESS_TIME, bVisibleWhenStopped ? 0.0 : 1.0);
+ rAnimList.append(aStop);
+ }
+}
+
+static void impCreateScrollTiming(const SfxItemSet& rSet, drawinglayer::animation::AnimationEntryList& rAnimList, bool bForward, double fTimeFullPath, double fFrequency)
+{
+ bool bVisibleWhenStopped(rSet.Get(SDRATTR_TEXT_ANISTOPINSIDE).GetValue());
+ bool bVisibleWhenStarted(rSet.Get(SDRATTR_TEXT_ANISTARTINSIDE).GetValue());
+ const sal_uInt32 nRepeat(rSet.Get(SDRATTR_TEXT_ANICOUNT).GetValue());
+
+ if(bVisibleWhenStarted)
+ {
+ // move from center to outside
+ drawinglayer::animation::AnimationEntryLinear aInOut(fTimeFullPath * 0.5, fFrequency, 0.5, bForward ? 1.0 : 0.0);
+ rAnimList.append(aInOut);
+ }
+
+ // loop. In loop, move through
+ drawinglayer::animation::AnimationEntryLoop aLoop(nRepeat ? nRepeat : ENDLESS_LOOP);
+ drawinglayer::animation::AnimationEntryLinear aThrough(fTimeFullPath, fFrequency, bForward ? 0.0 : 1.0, bForward ? 1.0 : 0.0);
+ aLoop.append(aThrough);
+ rAnimList.append(aLoop);
+
+ if(0 != nRepeat && bVisibleWhenStopped)
+ {
+ // move from outside to center
+ drawinglayer::animation::AnimationEntryLinear aOutIn(fTimeFullPath * 0.5, fFrequency, bForward ? 0.0 : 1.0, 0.5);
+ rAnimList.append(aOutIn);
+
+ // add timing for staying at the end
+ drawinglayer::animation::AnimationEntryFixed aEnd(ENDLESS_TIME, 0.5);
+ rAnimList.append(aEnd);
+ }
+}
+
+static void impCreateAlternateTiming(const SfxItemSet& rSet, drawinglayer::animation::AnimationEntryList& rAnimList, double fRelativeTextLength, bool bForward, double fTimeFullPath, double fFrequency)
+{
+ if(basegfx::fTools::more(fRelativeTextLength, 0.5))
+ {
+ // this is the case when fTextLength > fFrameLength, text is bigger than animation frame.
+ // In that case, correct direction
+ bForward = !bForward;
+ }
+
+ const double fStartPosition(bForward ? fRelativeTextLength : 1.0 - fRelativeTextLength);
+ const double fEndPosition(bForward ? 1.0 - fRelativeTextLength : fRelativeTextLength);
+ bool bVisibleWhenStarted(rSet.Get(SDRATTR_TEXT_ANISTARTINSIDE).GetValue());
+ const sal_uInt32 nRepeat(rSet.Get(SDRATTR_TEXT_ANICOUNT).GetValue());
+
+ if(!bVisibleWhenStarted)
+ {
+ // move from outside to center
+ drawinglayer::animation::AnimationEntryLinear aOutIn(fTimeFullPath * 0.5, fFrequency, bForward ? 0.0 : 1.0, 0.5);
+ rAnimList.append(aOutIn);
+ }
+
+ // loop. In loop, move out and in again. fInnerMovePath may be negative when text is bigger then frame,
+ // so use absolute value
+ const double fInnerMovePath(fabs(1.0 - (fRelativeTextLength * 2.0)));
+ const double fTimeForInnerPath(fTimeFullPath * fInnerMovePath);
+ const double fHalfInnerPath(fTimeForInnerPath * 0.5);
+ const sal_uInt32 nDoubleRepeat(nRepeat / 2L);
+
+ if(nDoubleRepeat || 0 == nRepeat)
+ {
+ // double forth and back loop
+ drawinglayer::animation::AnimationEntryLoop aLoop(nDoubleRepeat ? nDoubleRepeat : ENDLESS_LOOP);
+ drawinglayer::animation::AnimationEntryLinear aTime0(fHalfInnerPath, fFrequency, 0.5, fEndPosition);
+ aLoop.append(aTime0);
+ drawinglayer::animation::AnimationEntryLinear aTime1(fTimeForInnerPath, fFrequency, fEndPosition, fStartPosition);
+ aLoop.append(aTime1);
+ drawinglayer::animation::AnimationEntryLinear aTime2(fHalfInnerPath, fFrequency, fStartPosition, 0.5);
+ aLoop.append(aTime2);
+ rAnimList.append(aLoop);
+ }
+
+ if(nRepeat % 2L)
+ {
+ // repeat is uneven, so we need one more forth and back to center
+ drawinglayer::animation::AnimationEntryLinear aTime0(fHalfInnerPath, fFrequency, 0.5, fEndPosition);
+ rAnimList.append(aTime0);
+ drawinglayer::animation::AnimationEntryLinear aTime1(fHalfInnerPath, fFrequency, fEndPosition, 0.5);
+ rAnimList.append(aTime1);
+ }
+
+ if(0 == nRepeat)
+ return;
+
+ bool bVisibleWhenStopped(rSet.Get(SDRATTR_TEXT_ANISTOPINSIDE).GetValue());
+ if(bVisibleWhenStopped)
+ {
+ // add timing for staying at the end
+ drawinglayer::animation::AnimationEntryFixed aEnd(ENDLESS_TIME, 0.5);
+ rAnimList.append(aEnd);
+ }
+ else
+ {
+ // move from center to outside
+ drawinglayer::animation::AnimationEntryLinear aInOut(fTimeFullPath * 0.5, fFrequency, 0.5, bForward ? 1.0 : 0.0);
+ rAnimList.append(aInOut);
+ }
+}
+
+static void impCreateSlideTiming(const SfxItemSet& rSet, drawinglayer::animation::AnimationEntryList& rAnimList, bool bForward, double fTimeFullPath, double fFrequency)
+{
+ // move in from outside, start outside
+ const double fStartPosition(bForward ? 0.0 : 1.0);
+ const sal_uInt32 nRepeat(rSet.Get(SDRATTR_TEXT_ANICOUNT).GetValue());
+
+ // move from outside to center
+ drawinglayer::animation::AnimationEntryLinear aOutIn(fTimeFullPath * 0.5, fFrequency, fStartPosition, 0.5);
+ rAnimList.append(aOutIn);
+
+ // loop. In loop, move out and in again
+ if(nRepeat > 1 || 0 == nRepeat)
+ {
+ drawinglayer::animation::AnimationEntryLoop aLoop(nRepeat ? nRepeat - 1 : ENDLESS_LOOP);
+ drawinglayer::animation::AnimationEntryLinear aTime0(fTimeFullPath * 0.5, fFrequency, 0.5, fStartPosition);
+ aLoop.append(aTime0);
+ drawinglayer::animation::AnimationEntryLinear aTime1(fTimeFullPath * 0.5, fFrequency, fStartPosition, 0.5);
+ aLoop.append(aTime1);
+ rAnimList.append(aLoop);
+ }
+
+ // always visible when stopped, so add timing for staying at the end when not endless
+ if(0 != nRepeat)
+ {
+ drawinglayer::animation::AnimationEntryFixed aEnd(ENDLESS_TIME, 0.5);
+ rAnimList.append(aEnd);
+ }
+}
+
+void SdrTextObj::impGetScrollTextTiming(drawinglayer::animation::AnimationEntryList& rAnimList, double fFrameLength, double fTextLength) const
+{
+ const SdrTextAniKind eAniKind(GetTextAniKind());
+
+ if(SdrTextAniKind::Scroll != eAniKind && SdrTextAniKind::Alternate != eAniKind && SdrTextAniKind::Slide != eAniKind)
+ return;
+
+ // get data. Goal is to calculate fTimeFullPath which is the time needed to
+ // move animation from (0.0) to (1.0) state
+ const SfxItemSet& rSet = GetObjectItemSet();
+ double fAnimationDelay(static_cast<double>(rSet.Get(SDRATTR_TEXT_ANIDELAY).GetValue()));
+ double fSingleStepWidth(static_cast<double>(rSet.Get(SDRATTR_TEXT_ANIAMOUNT).GetValue()));
+ const SdrTextAniDirection eDirection(GetTextAniDirection());
+ const bool bForward(SdrTextAniDirection::Right == eDirection || SdrTextAniDirection::Down == eDirection);
+
+ if(basegfx::fTools::equalZero(fAnimationDelay))
+ {
+ // default to 1/20 second
+ fAnimationDelay = 50.0;
+ }
+
+ if(basegfx::fTools::less(fSingleStepWidth, 0.0))
+ {
+ // data is in pixels, convert to logic. Imply PIXEL_DPI dpi.
+ // It makes no sense to keep the view-transformation centered
+ // definitions, so get rid of them here.
+ fSingleStepWidth = (-fSingleStepWidth * (2540.0 / PIXEL_DPI));
+ }
+
+ if(basegfx::fTools::equalZero(fSingleStepWidth))
+ {
+ // default to 1 millimeter
+ fSingleStepWidth = 100.0;
+ }
+
+ // use the length of the full animation path and the number of steps
+ // to get the full path time
+ const double fFullPathLength(fFrameLength + fTextLength);
+ const double fNumberOfSteps(fFullPathLength / fSingleStepWidth);
+ double fTimeFullPath(fNumberOfSteps * fAnimationDelay);
+
+ if(fTimeFullPath < fAnimationDelay)
+ {
+ fTimeFullPath = fAnimationDelay;
+ }
+
+ switch(eAniKind)
+ {
+ case SdrTextAniKind::Scroll :
+ {
+ impCreateScrollTiming(rSet, rAnimList, bForward, fTimeFullPath, fAnimationDelay);
+ break;
+ }
+ case SdrTextAniKind::Alternate :
+ {
+ double fRelativeTextLength(fTextLength / (fFrameLength + fTextLength));
+ impCreateAlternateTiming(rSet, rAnimList, fRelativeTextLength, bForward, fTimeFullPath, fAnimationDelay);
+ break;
+ }
+ case SdrTextAniKind::Slide :
+ {
+ impCreateSlideTiming(rSet, rAnimList, bForward, fTimeFullPath, fAnimationDelay);
+ break;
+ }
+ default : break; // SdrTextAniKind::NONE, SdrTextAniKind::Blink
+ }
+}
+
+void SdrTextObj::impHandleChainingEventsDuringDecomposition(SdrOutliner &rOutliner) const
+{
+ if (GetTextChain()->GetNilChainingEvent(this))
+ return;
+
+ GetTextChain()->SetNilChainingEvent(this, true);
+
+ TextChainFlow aTxtChainFlow(const_cast<SdrTextObj*>(this));
+ bool bIsOverflow;
+
+#ifdef DBG_UTIL
+ // Some debug output
+ size_t nObjCount(getSdrPageFromSdrObject()->GetObjCount());
+ for (size_t i = 0; i < nObjCount; i++)
+ {
+ SdrTextObj* pCurObj(dynamic_cast< SdrTextObj* >(getSdrPageFromSdrObject()->GetObj(i)));
+ if(pCurObj == this)
+ {
+ SAL_INFO("svx.chaining", "Working on TextBox " << i);
+ break;
+ }
+ }
+#endif
+
+ aTxtChainFlow.CheckForFlowEvents(&rOutliner);
+
+ if (aTxtChainFlow.IsUnderflow() && !IsInEditMode())
+ {
+ // underflow-induced overflow
+ aTxtChainFlow.ExecuteUnderflow(&rOutliner);
+ bIsOverflow = aTxtChainFlow.IsOverflow();
+ } else {
+ // standard overflow (no underflow before)
+ bIsOverflow = aTxtChainFlow.IsOverflow();
+ }
+
+ if (bIsOverflow && !IsInEditMode()) {
+ // Initialize Chaining Outliner
+ SdrOutliner &rChainingOutl(getSdrModelFromSdrObject().GetChainingOutliner(this));
+ ImpInitDrawOutliner( rChainingOutl );
+ rChainingOutl.SetUpdateLayout(true);
+ // We must pass the chaining outliner otherwise we would mess up decomposition
+ aTxtChainFlow.ExecuteOverflow(&rOutliner, &rChainingOutl);
+ }
+
+ GetTextChain()->SetNilChainingEvent(this, false);
+}
+
+void SdrTextObj::impDecomposeChainedTextPrimitive(
+ drawinglayer::primitive2d::Primitive2DContainer& rTarget,
+ const drawinglayer::primitive2d::SdrChainedTextPrimitive2D& rSdrChainedTextPrimitive,
+ const drawinglayer::geometry::ViewInformation2D& aViewInformation) const
+{
+ // decompose matrix to have position and size of text
+ basegfx::B2DVector aScale, aTranslate;
+ double fRotate, fShearX;
+ rSdrChainedTextPrimitive.getTextRangeTransform().decompose(aScale, aTranslate, fRotate, fShearX);
+
+ // use B2DRange aAnchorTextRange for calculations
+ basegfx::B2DRange aAnchorTextRange(aTranslate);
+ aAnchorTextRange.expand(aTranslate + aScale);
+
+ // prepare outliner
+ const SfxItemSet& rTextItemSet = rSdrChainedTextPrimitive.getSdrText()->GetItemSet();
+ SolarMutexGuard aSolarGuard;
+ SdrOutliner& rOutliner = ImpGetDrawOutliner();
+
+ SdrTextVertAdjust eVAdj = GetTextVerticalAdjust(rTextItemSet);
+ SdrTextHorzAdjust eHAdj = GetTextHorizontalAdjust(rTextItemSet);
+ const EEControlBits nOriginalControlWord(rOutliner.GetControlWord());
+ const Size aNullSize;
+
+ // set visualizing page at Outliner; needed e.g. for PageNumberField decomposition
+ rOutliner.setVisualizedPage(GetSdrPageFromXDrawPage(aViewInformation.getVisualizedPage()));
+
+ rOutliner.SetControlWord(nOriginalControlWord|EEControlBits::AUTOPAGESIZE|EEControlBits::STRETCHING);
+ rOutliner.SetMinAutoPaperSize(aNullSize);
+ rOutliner.SetMaxAutoPaperSize(Size(1000000,1000000));
+
+ // add one to rage sizes to get back to the old Rectangle and outliner measurements
+ const sal_uInt32 nAnchorTextWidth(FRound(aAnchorTextRange.getWidth() + 1));
+ const sal_uInt32 nAnchorTextHeight(FRound(aAnchorTextRange.getHeight() + 1));
+
+ // Text
+ const OutlinerParaObject* pOutlinerParaObject = rSdrChainedTextPrimitive.getSdrText()->GetOutlinerParaObject();
+ OSL_ENSURE(pOutlinerParaObject, "impDecomposeBlockTextPrimitive used with no OutlinerParaObject (!)");
+
+ const bool bVerticalWriting(pOutlinerParaObject->IsEffectivelyVertical());
+ const bool bTopToBottom(pOutlinerParaObject->IsTopToBottom());
+ const Size aAnchorTextSize(Size(nAnchorTextWidth, nAnchorTextHeight));
+
+ if(IsTextFrame())
+ {
+ rOutliner.SetMaxAutoPaperSize(aAnchorTextSize);
+ }
+
+ if(SDRTEXTHORZADJUST_BLOCK == eHAdj && !bVerticalWriting)
+ {
+ rOutliner.SetMinAutoPaperSize(Size(nAnchorTextWidth, 0));
+ }
+
+ if(SDRTEXTVERTADJUST_BLOCK == eVAdj && bVerticalWriting)
+ {
+ rOutliner.SetMinAutoPaperSize(Size(0, nAnchorTextHeight));
+ }
+
+ rOutliner.SetPaperSize(aNullSize);
+ rOutliner.SetUpdateLayout(true);
+ // Sets original text
+ rOutliner.SetText(*pOutlinerParaObject);
+
+ /* Begin overflow/underflow handling */
+
+ impHandleChainingEventsDuringDecomposition(rOutliner);
+
+ /* End overflow/underflow handling */
+
+ // set visualizing page at Outliner; needed e.g. for PageNumberField decomposition
+ rOutliner.setVisualizedPage(GetSdrPageFromXDrawPage(aViewInformation.getVisualizedPage()));
+
+ // now get back the layouted text size from outliner
+ const Size aOutlinerTextSize(rOutliner.GetPaperSize());
+ const basegfx::B2DVector aOutlinerScale(aOutlinerTextSize.Width(), aOutlinerTextSize.Height());
+ basegfx::B2DVector aAdjustTranslate(0.0, 0.0);
+
+ // correct horizontal translation using the now known text size
+ if(SDRTEXTHORZADJUST_CENTER == eHAdj || SDRTEXTHORZADJUST_RIGHT == eHAdj)
+ {
+ const double fFree(aAnchorTextRange.getWidth() - aOutlinerScale.getX());
+
+ if(SDRTEXTHORZADJUST_CENTER == eHAdj)
+ {
+ aAdjustTranslate.setX(fFree / 2.0);
+ }
+
+ if(SDRTEXTHORZADJUST_RIGHT == eHAdj)
+ {
+ aAdjustTranslate.setX(fFree);
+ }
+ }
+
+ // correct vertical translation using the now known text size
+ if(SDRTEXTVERTADJUST_CENTER == eVAdj || SDRTEXTVERTADJUST_BOTTOM == eVAdj)
+ {
+ const double fFree(aAnchorTextRange.getHeight() - aOutlinerScale.getY());
+
+ if(SDRTEXTVERTADJUST_CENTER == eVAdj)
+ {
+ aAdjustTranslate.setY(fFree / 2.0);
+ }
+
+ if(SDRTEXTVERTADJUST_BOTTOM == eVAdj)
+ {
+ aAdjustTranslate.setY(fFree);
+ }
+ }
+
+ // prepare matrices to apply to newly created primitives. aNewTransformA
+ // will get coordinates in aOutlinerScale size and positive in X, Y.
+ basegfx::B2DHomMatrix aNewTransformA;
+ basegfx::B2DHomMatrix aNewTransformB;
+
+ // translate relative to given primitive to get same rotation and shear
+ // as the master shape we are working on. For vertical, use the top-right
+ // corner
+ const double fStartInX(bVerticalWriting && bTopToBottom ? aAdjustTranslate.getX() + aOutlinerScale.getX() : aAdjustTranslate.getX());
+ const double fStartInY(bVerticalWriting && !bTopToBottom ? aAdjustTranslate.getY() + aOutlinerScale.getY() : aAdjustTranslate.getY());
+ aNewTransformA.translate(fStartInX, fStartInY);
+
+ // mirroring. We are now in aAnchorTextRange sizes. When mirroring in X and Y,
+ // move the null point which was top left to bottom right.
+ const bool bMirrorX(basegfx::fTools::less(aScale.getX(), 0.0));
+ const bool bMirrorY(basegfx::fTools::less(aScale.getY(), 0.0));
+ aNewTransformB.scale(bMirrorX ? -1.0 : 1.0, bMirrorY ? -1.0 : 1.0);
+
+ // in-between the translations of the single primitives will take place. Afterwards,
+ // the object's transformations need to be applied
+ aNewTransformB.shearX(fShearX);
+ aNewTransformB.rotate(fRotate);
+ aNewTransformB.translate(aTranslate.getX(), aTranslate.getY());
+
+ basegfx::B2DRange aClipRange;
+
+ // now break up text primitives.
+ impTextBreakupHandler aConverter(rOutliner);
+ aConverter.decomposeBlockTextPrimitive(aNewTransformA, aNewTransformB, aClipRange);
+
+ // cleanup outliner
+ rOutliner.Clear();
+ rOutliner.setVisualizedPage(nullptr);
+ rOutliner.SetControlWord(nOriginalControlWord);
+
+ rTarget = aConverter.extractPrimitive2DSequence();
+}
+
+// Direct decomposer for text visualization when you already have a prepared
+// Outliner containing all the needed information
+void SdrTextObj::impDecomposeBlockTextPrimitiveDirect(
+ drawinglayer::primitive2d::Primitive2DContainer& rTarget,
+ SdrOutliner& rOutliner,
+ const basegfx::B2DHomMatrix& rNewTransformA,
+ const basegfx::B2DHomMatrix& rNewTransformB,
+ const basegfx::B2DRange& rClipRange)
+{
+ impTextBreakupHandler aConverter(rOutliner);
+ aConverter.decomposeBlockTextPrimitive(rNewTransformA, rNewTransformB, rClipRange);
+ rTarget.append(aConverter.extractPrimitive2DSequence());
+}
+
+double SdrTextObj::GetCameraZRotation() const
+{
+ const css::uno::Any* pAny;
+ double fTextCameraZRotateAngle = 0.0;
+ const SfxItemSet& rSet = GetObjectItemSet();
+ const SdrCustomShapeGeometryItem& rGeometryItem(rSet.Get(SDRATTR_CUSTOMSHAPE_GEOMETRY));
+
+ pAny = rGeometryItem.GetPropertyValueByName("TextCameraZRotateAngle");
+
+ if ( pAny )
+ *pAny >>= fTextCameraZRotateAngle;
+
+ return fTextCameraZRotateAngle;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/svdraw/svdotextpathdecomposition.cxx b/svx/source/svdraw/svdotextpathdecomposition.cxx
new file mode 100644
index 000000000..14bbd73ca
--- /dev/null
+++ b/svx/source/svdraw/svdotextpathdecomposition.cxx
@@ -0,0 +1,748 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * 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/svdotext.hxx>
+#include <svx/svdoutl.hxx>
+#include <basegfx/vector/b2dvector.hxx>
+#include <sdr/primitive2d/sdrtextprimitive2d.hxx>
+#include <basegfx/polygon/b2dpolygontools.hxx>
+#include <basegfx/polygon/b2dpolygon.hxx>
+#include <algorithm>
+#include <com/sun/star/i18n/BreakIterator.hpp>
+#include <comphelper/processfactory.hxx>
+#include <com/sun/star/i18n/CharacterIteratorMode.hpp>
+#include <drawinglayer/primitive2d/textlayoutdevice.hxx>
+#include <drawinglayer/primitive2d/textprimitive2d.hxx>
+#include <basegfx/color/bcolor.hxx>
+
+
+// primitive decomposition helpers
+
+#include <drawinglayer/attribute/strokeattribute.hxx>
+#include <drawinglayer/primitive2d/PolygonStrokePrimitive2D.hxx>
+#include <drawinglayer/primitive2d/unifiedtransparenceprimitive2d.hxx>
+#include <svx/unoapi.hxx>
+#include <drawinglayer/geometry/viewinformation2d.hxx>
+#include <sdr/attribute/sdrformtextoutlineattribute.hxx>
+
+
+using namespace ::com::sun::star::uno;
+using namespace ::com::sun::star::lang;
+using namespace ::com::sun::star::i18n;
+
+
+// PathTextPortion helper
+
+namespace
+{
+ class impPathTextPortion
+ {
+ basegfx::B2DVector maOffset;
+ OUString maText;
+ sal_Int32 mnTextStart;
+ sal_Int32 mnTextLength;
+ sal_Int32 mnParagraph;
+ SvxFont maFont;
+ ::std::vector< double > maDblDXArray; // double DXArray, font size independent -> unit coordinate system
+ css::lang::Locale maLocale;
+
+ bool mbRTL : 1;
+
+ public:
+ explicit impPathTextPortion(const DrawPortionInfo& rInfo)
+ : maOffset(rInfo.mrStartPos.X(), rInfo.mrStartPos.Y()),
+ maText(rInfo.maText),
+ mnTextStart(rInfo.mnTextStart),
+ mnTextLength(rInfo.mnTextLen),
+ mnParagraph(rInfo.mnPara),
+ maFont(rInfo.mrFont),
+ maLocale(rInfo.mpLocale ? *rInfo.mpLocale : css::lang::Locale()),
+ mbRTL(!rInfo.mrFont.IsVertical() && rInfo.IsRTL())
+ {
+ if(mnTextLength && !rInfo.mpDXArray.empty())
+ {
+ maDblDXArray.reserve(mnTextLength);
+
+ for(sal_Int32 a=0; a < mnTextLength; a++)
+ {
+ maDblDXArray.push_back(static_cast<double>(rInfo.mpDXArray[a]));
+ }
+ }
+ }
+
+ // for ::std::sort
+ bool operator<(const impPathTextPortion& rComp) const
+ {
+ if(mnParagraph < rComp.mnParagraph)
+ {
+ return true;
+ }
+
+ if(maOffset.getX() < rComp.maOffset.getX())
+ {
+ return true;
+ }
+
+ return (maOffset.getY() < rComp.maOffset.getY());
+ }
+
+ const OUString& getText() const { return maText; }
+ sal_Int32 getTextStart() const { return mnTextStart; }
+ sal_Int32 getTextLength() const { return mnTextLength; }
+ sal_Int32 getParagraph() const { return mnParagraph; }
+ const SvxFont& getFont() const { return maFont; }
+ bool isRTL() const { return mbRTL; }
+ const ::std::vector< double >& getDoubleDXArray() const { return maDblDXArray; }
+ const css::lang::Locale& getLocale() const { return maLocale; }
+
+ sal_Int32 getPortionIndex(sal_Int32 nIndex, sal_Int32 nLength) const
+ {
+ if(mbRTL)
+ {
+ return (mnTextStart + (mnTextLength - (nIndex + nLength)));
+ }
+ else
+ {
+ return (mnTextStart + nIndex);
+ }
+ }
+
+ double getDisplayLength(sal_Int32 nIndex, sal_Int32 nLength) const
+ {
+ drawinglayer::primitive2d::TextLayouterDevice aTextLayouter;
+ double fRetval(0.0);
+
+ if(maFont.IsVertical())
+ {
+ fRetval = aTextLayouter.getTextHeight() * static_cast<double>(nLength);
+ }
+ else
+ {
+ fRetval = aTextLayouter.getTextWidth(maText, getPortionIndex(nIndex, nLength), nLength);
+ }
+
+ return fRetval;
+ }
+ };
+} // end of anonymous namespace
+
+
+// TextBreakup helper
+
+namespace
+{
+ class impTextBreakupHandler
+ {
+ SdrOutliner& mrOutliner;
+ ::std::vector< impPathTextPortion > maPathTextPortions;
+
+ DECL_LINK(decompositionPathTextPrimitive, DrawPortionInfo*, void );
+
+ public:
+ explicit impTextBreakupHandler(SdrOutliner& rOutliner)
+ : mrOutliner(rOutliner)
+ {
+ }
+
+ const ::std::vector< impPathTextPortion >& decompositionPathTextPrimitive()
+ {
+ // strip portions to maPathTextPortions
+ mrOutliner.SetDrawPortionHdl(LINK(this, impTextBreakupHandler, decompositionPathTextPrimitive));
+ mrOutliner.StripPortions();
+
+ if(!maPathTextPortions.empty())
+ {
+ // sort portions by paragraph, x and y
+ ::std::sort(maPathTextPortions.begin(), maPathTextPortions.end());
+ }
+
+ return maPathTextPortions;
+ }
+ };
+
+ IMPL_LINK(impTextBreakupHandler, decompositionPathTextPrimitive, DrawPortionInfo*, pInfo, void)
+ {
+ maPathTextPortions.emplace_back(*pInfo);
+ }
+} // end of anonymous namespace
+
+
+// TextBreakup one poly and one paragraph helper
+
+namespace
+{
+ class impPolygonParagraphHandler
+ {
+ const drawinglayer::attribute::SdrFormTextAttribute maSdrFormTextAttribute; // FormText parameters
+ drawinglayer::primitive2d::Primitive2DContainer& mrDecomposition; // destination primitive list
+ drawinglayer::primitive2d::Primitive2DContainer& mrShadowDecomposition; // destination primitive list for shadow
+ Reference < css::i18n::XBreakIterator > mxBreak; // break iterator
+
+ static double getParagraphTextLength(const ::std::vector< const impPathTextPortion* >& rTextPortions)
+ {
+ drawinglayer::primitive2d::TextLayouterDevice aTextLayouter;
+ double fRetval(0.0);
+
+ for(const impPathTextPortion* pCandidate : rTextPortions)
+ {
+ if(pCandidate && pCandidate->getTextLength())
+ {
+ aTextLayouter.setFont(pCandidate->getFont());
+ fRetval += pCandidate->getDisplayLength(0, pCandidate->getTextLength());
+ }
+ }
+
+ return fRetval;
+ }
+
+ sal_Int32 getNextGlyphLen(const impPathTextPortion* pCandidate, sal_Int32 nPosition, const css::lang::Locale& rFontLocale)
+ {
+ sal_Int32 nNextGlyphLen(1);
+
+ if(mxBreak.is())
+ {
+ sal_Int32 nDone(0);
+ nNextGlyphLen = mxBreak->nextCharacters(pCandidate->getText(), nPosition,
+ rFontLocale, CharacterIteratorMode::SKIPCELL, 1, nDone) - nPosition;
+ }
+
+ return nNextGlyphLen;
+ }
+
+ public:
+ impPolygonParagraphHandler(
+ const drawinglayer::attribute::SdrFormTextAttribute& rSdrFormTextAttribute,
+ drawinglayer::primitive2d::Primitive2DContainer& rDecomposition,
+ drawinglayer::primitive2d::Primitive2DContainer& rShadowDecomposition)
+ : maSdrFormTextAttribute(rSdrFormTextAttribute),
+ mrDecomposition(rDecomposition),
+ mrShadowDecomposition(rShadowDecomposition)
+ {
+ // prepare BreakIterator
+ Reference < XComponentContext > xContext = ::comphelper::getProcessComponentContext();
+ mxBreak = css::i18n::BreakIterator::create(xContext);
+ }
+
+ void HandlePair(const basegfx::B2DPolygon& rPolygonCandidate, const ::std::vector< const impPathTextPortion* >& rTextPortions)
+ {
+ // prepare polygon geometry, take into account as many parameters as possible
+ basegfx::B2DPolygon aPolygonCandidate(rPolygonCandidate);
+ const double fPolyLength(basegfx::utils::getLength(aPolygonCandidate));
+ double fPolyEnd(fPolyLength);
+ double fPolyStart(0.0);
+ double fAutosizeScaleFactor(1.0);
+ bool bAutosizeScale(false);
+
+ if(maSdrFormTextAttribute.getFormTextMirror())
+ {
+ aPolygonCandidate.flip();
+ }
+
+ if(maSdrFormTextAttribute.getFormTextStart()
+ && (XFormTextAdjust::Left == maSdrFormTextAttribute.getFormTextAdjust()
+ || XFormTextAdjust::Right == maSdrFormTextAttribute.getFormTextAdjust()))
+ {
+ if(XFormTextAdjust::Left == maSdrFormTextAttribute.getFormTextAdjust())
+ {
+ fPolyStart += maSdrFormTextAttribute.getFormTextStart();
+
+ if(fPolyStart > fPolyEnd)
+ {
+ fPolyStart = fPolyEnd;
+ }
+ }
+ else
+ {
+ fPolyEnd -= maSdrFormTextAttribute.getFormTextStart();
+
+ if(fPolyEnd < fPolyStart)
+ {
+ fPolyEnd = fPolyStart;
+ }
+ }
+ }
+
+ if(XFormTextAdjust::Left != maSdrFormTextAttribute.getFormTextAdjust())
+ {
+ // calculate total text length of this paragraph, some layout needs to be done
+ const double fParagraphTextLength(getParagraphTextLength(rTextPortions));
+
+ // check if text is too long for paragraph. If yes, handle as if left aligned (default),
+ // but still take care of XFormTextAdjust::AutoSize in that case
+ const bool bTextTooLong(fParagraphTextLength > (fPolyEnd - fPolyStart));
+
+ if(XFormTextAdjust::Right == maSdrFormTextAttribute.getFormTextAdjust())
+ {
+ if(!bTextTooLong)
+ {
+ // if right aligned, add difference to polygon start
+ fPolyStart += ((fPolyEnd - fPolyStart) - fParagraphTextLength);
+ }
+ }
+ else if(XFormTextAdjust::Center == maSdrFormTextAttribute.getFormTextAdjust())
+ {
+ if(!bTextTooLong)
+ {
+ // if centered, add half of difference to polygon start
+ fPolyStart += ((fPolyEnd - fPolyStart) - fParagraphTextLength) / 2.0;
+ }
+ }
+ else if(XFormTextAdjust::AutoSize == maSdrFormTextAttribute.getFormTextAdjust())
+ {
+ // if scale, prepare scale factor between curve length and text length
+ if(0.0 != fParagraphTextLength)
+ {
+ fAutosizeScaleFactor = (fPolyEnd - fPolyStart) / fParagraphTextLength;
+ bAutosizeScale = true;
+ }
+ }
+ }
+
+ // handle text portions for this paragraph
+ for(auto a = rTextPortions.begin(); a != rTextPortions.end() && fPolyStart < fPolyEnd; ++a)
+ {
+ const impPathTextPortion* pCandidate = *a;
+ basegfx::B2DVector aFontScaling;
+
+ if(pCandidate && pCandidate->getTextLength())
+ {
+ const drawinglayer::attribute::FontAttribute aCandidateFontAttribute(
+ drawinglayer::primitive2d::getFontAttributeFromVclFont(
+ aFontScaling,
+ pCandidate->getFont(),
+ pCandidate->isRTL(),
+ false));
+
+ drawinglayer::primitive2d::TextLayouterDevice aTextLayouter;
+ aTextLayouter.setFont(pCandidate->getFont());
+ sal_Int32 nUsedTextLength(0);
+
+ while(nUsedTextLength < pCandidate->getTextLength() && fPolyStart < fPolyEnd)
+ {
+ sal_Int32 nNextGlyphLen(getNextGlyphLen(pCandidate, pCandidate->getTextStart() + nUsedTextLength, pCandidate->getLocale()));
+
+ // prepare portion length. Takes RTL sections into account.
+ double fPortionLength(pCandidate->getDisplayLength(nUsedTextLength, nNextGlyphLen));
+
+ if(bAutosizeScale)
+ {
+ // when autosize scaling, expand portion length
+ fPortionLength *= fAutosizeScaleFactor;
+ }
+
+ // create transformation
+ basegfx::B2DHomMatrix aNewTransformA, aNewTransformB, aNewShadowTransform;
+ basegfx::B2DPoint aStartPos(basegfx::utils::getPositionAbsolute(aPolygonCandidate, fPolyStart, fPolyLength));
+ basegfx::B2DPoint aEndPos(aStartPos);
+
+ // add font scaling
+ aNewTransformA.scale(aFontScaling.getX(), aFontScaling.getY());
+
+ // prepare scaling of text primitive
+ if(bAutosizeScale)
+ {
+ // when autosize scaling, expand text primitive scaling to it
+ aNewTransformA.scale(fAutosizeScaleFactor, fAutosizeScaleFactor);
+ }
+
+ // eventually create shadow primitives from aDecomposition and add to rDecomposition
+ const bool bShadow(XFormTextShadow::NONE != maSdrFormTextAttribute.getFormTextShadow());
+
+ if(bShadow)
+ {
+ if(XFormTextShadow::Normal == maSdrFormTextAttribute.getFormTextShadow())
+ {
+ aNewShadowTransform.translate(
+ maSdrFormTextAttribute.getFormTextShdwXVal(),
+ -maSdrFormTextAttribute.getFormTextShdwYVal());
+ }
+ else // XFormTextShadow::Slant
+ {
+ double fScaleValue(maSdrFormTextAttribute.getFormTextShdwYVal() / 100.0);
+ double fShearValue(-basegfx::deg2rad<10>(maSdrFormTextAttribute.getFormTextShdwXVal()));
+
+ aNewShadowTransform.scale(1.0, fScaleValue);
+ aNewShadowTransform.shearX(sin(fShearValue));
+ aNewShadowTransform.scale(1.0, cos(fShearValue));
+ }
+ }
+
+ switch(maSdrFormTextAttribute.getFormTextStyle())
+ {
+ case XFormTextStyle::Rotate :
+ {
+ aEndPos = basegfx::utils::getPositionAbsolute(aPolygonCandidate, fPolyStart + fPortionLength, fPolyLength);
+ const basegfx::B2DVector aDirection(aEndPos - aStartPos);
+ aNewTransformB.rotate(atan2(aDirection.getY(), aDirection.getX()));
+ aNewTransformB.translate(aStartPos.getX(), aStartPos.getY());
+
+ break;
+ }
+ case XFormTextStyle::Upright :
+ {
+ aNewTransformB.translate(aStartPos.getX() - (fPortionLength / 2.0), aStartPos.getY());
+
+ break;
+ }
+ case XFormTextStyle::SlantX :
+ {
+ aEndPos = basegfx::utils::getPositionAbsolute(aPolygonCandidate, fPolyStart + fPortionLength, fPolyLength);
+ const basegfx::B2DVector aDirection(aEndPos - aStartPos);
+ const double fShearValue(atan2(aDirection.getY(), aDirection.getX()));
+ const double fSin(sin(fShearValue));
+ const double fCos(cos(fShearValue));
+
+ aNewTransformB.shearX(-fSin);
+
+ // Scale may lead to objects without height since fCos == 0.0 is possible.
+ // Renderers need to handle that, it's not a forbidden value and does not
+ // need to be avoided
+ aNewTransformB.scale(1.0, fCos);
+ aNewTransformB.translate(aStartPos.getX() - (fPortionLength / 2.0), aStartPos.getY());
+
+ break;
+ }
+ case XFormTextStyle::SlantY :
+ {
+ aEndPos = basegfx::utils::getPositionAbsolute(aPolygonCandidate, fPolyStart + fPortionLength, fPolyLength);
+ const basegfx::B2DVector aDirection(aEndPos - aStartPos);
+ const double fShearValue(atan2(aDirection.getY(), aDirection.getX()));
+ const double fCos(cos(fShearValue));
+ const double fTan(tan(fShearValue));
+
+ // shear to 'stand' on the curve
+ aNewTransformB.shearY(fTan);
+
+ // scale in X to make as tight as needed. As with XFT_SLANT_X, this may
+ // lead to primitives without width which the renderers will handle
+ aNewTransformA.scale(fCos, 1.0);
+
+ aNewTransformB.translate(aStartPos.getX(), aStartPos.getY());
+
+ break;
+ }
+ default : break; // XFormTextStyle::NONE
+ }
+
+ // distance from path?
+ if(maSdrFormTextAttribute.getFormTextDistance())
+ {
+ if(aEndPos.equal(aStartPos))
+ {
+ aEndPos = basegfx::utils::getPositionAbsolute(aPolygonCandidate, fPolyStart + fPortionLength, fPolyLength);
+ }
+
+ // use back vector (aStartPos - aEndPos) here to get mirrored perpendicular as in old stuff
+ const basegfx::B2DVector aPerpendicular(
+ basegfx::getNormalizedPerpendicular(aStartPos - aEndPos) *
+ maSdrFormTextAttribute.getFormTextDistance());
+ aNewTransformB.translate(aPerpendicular.getX(), aPerpendicular.getY());
+ }
+
+ if(!pCandidate->getText().isEmpty() && nNextGlyphLen)
+ {
+ const sal_Int32 nPortionIndex(pCandidate->getPortionIndex(nUsedTextLength, nNextGlyphLen));
+ ::std::vector< double > aNewDXArray;
+
+ if(nNextGlyphLen > 1 && !pCandidate->getDoubleDXArray().empty())
+ {
+ // copy DXArray for portion
+ aNewDXArray.insert(
+ aNewDXArray.begin(),
+ pCandidate->getDoubleDXArray().begin() + nPortionIndex,
+ pCandidate->getDoubleDXArray().begin() + (nPortionIndex + nNextGlyphLen));
+
+ if(nPortionIndex > 0)
+ {
+ // adapt to portion start
+ double fDXOffset= *(pCandidate->getDoubleDXArray().begin() + (nPortionIndex - 1));
+ ::std::transform(
+ aNewDXArray.begin(), aNewDXArray.end(),
+ aNewDXArray.begin(), [fDXOffset](double x) { return x - fDXOffset; });
+ }
+
+ if(bAutosizeScale)
+ {
+ // when autosize scaling, adapt to DXArray, too
+ ::std::transform(
+ aNewDXArray.begin(), aNewDXArray.end(),
+ aNewDXArray.begin(), [fAutosizeScaleFactor](double x) { return x * fAutosizeScaleFactor; });
+ }
+ }
+
+ if(bShadow)
+ {
+ // shadow primitive creation
+ const Color aShadowColor(maSdrFormTextAttribute.getFormTextShdwColor());
+ const basegfx::BColor aRGBShadowColor(aShadowColor.getBColor());
+
+ mrShadowDecomposition.push_back(
+ new drawinglayer::primitive2d::TextSimplePortionPrimitive2D(
+ aNewTransformB * aNewShadowTransform * aNewTransformA,
+ pCandidate->getText(),
+ nPortionIndex,
+ nNextGlyphLen,
+ std::vector(aNewDXArray),
+ aCandidateFontAttribute,
+ pCandidate->getLocale(),
+ aRGBShadowColor) );
+ }
+
+ {
+ // primitive creation
+ const Color aColor(pCandidate->getFont().GetColor());
+ const basegfx::BColor aRGBColor(aColor.getBColor());
+
+ mrDecomposition.push_back(
+ new drawinglayer::primitive2d::TextSimplePortionPrimitive2D(
+ aNewTransformB * aNewTransformA,
+ pCandidate->getText(),
+ nPortionIndex,
+ nNextGlyphLen,
+ std::move(aNewDXArray),
+ aCandidateFontAttribute,
+ pCandidate->getLocale(),
+ aRGBColor) );
+ }
+ }
+
+ // consume from portion
+ nUsedTextLength += nNextGlyphLen;
+
+ // consume from polygon
+ fPolyStart += fPortionLength;
+ }
+ }
+ }
+ }
+ };
+} // end of anonymous namespace
+
+
+// primitive decomposition helpers
+
+namespace
+{
+ void impAddPolygonStrokePrimitives(
+ const basegfx::B2DPolyPolygonVector& rB2DPolyPolyVector,
+ const basegfx::B2DHomMatrix& rTransform,
+ const drawinglayer::attribute::LineAttribute& rLineAttribute,
+ const drawinglayer::attribute::StrokeAttribute& rStrokeAttribute,
+ drawinglayer::primitive2d::Primitive2DContainer& rTarget)
+ {
+ for(const auto& rB2DPolyPolygon : rB2DPolyPolyVector)
+ {
+ // prepare PolyPolygons
+ basegfx::B2DPolyPolygon aB2DPolyPolygon = rB2DPolyPolygon;
+ aB2DPolyPolygon.transform(rTransform);
+
+ for(auto const& rPolygon : std::as_const(aB2DPolyPolygon))
+ {
+ // create one primitive per polygon
+ rTarget.push_back(
+ new drawinglayer::primitive2d::PolygonStrokePrimitive2D(
+ rPolygon, rLineAttribute, rStrokeAttribute) );
+ }
+ }
+ }
+
+ drawinglayer::primitive2d::Primitive2DContainer impAddPathTextOutlines(
+ const drawinglayer::primitive2d::Primitive2DContainer& rSource,
+ const drawinglayer::attribute::SdrFormTextOutlineAttribute& rOutlineAttribute)
+ {
+ drawinglayer::primitive2d::Primitive2DContainer aNewPrimitives;
+
+ for(const drawinglayer::primitive2d::Primitive2DReference& a : rSource)
+ {
+ const drawinglayer::primitive2d::TextSimplePortionPrimitive2D* pTextCandidate = dynamic_cast< const drawinglayer::primitive2d::TextSimplePortionPrimitive2D* >(a.get());
+
+ if(pTextCandidate)
+ {
+ basegfx::B2DPolyPolygonVector aB2DPolyPolyVector;
+ basegfx::B2DHomMatrix aPolygonTransform;
+
+ // get text outlines and their object transformation
+ pTextCandidate->getTextOutlinesAndTransformation(aB2DPolyPolyVector, aPolygonTransform);
+
+ if(!aB2DPolyPolyVector.empty())
+ {
+ // create stroke primitives
+ drawinglayer::primitive2d::Primitive2DContainer aStrokePrimitives;
+ impAddPolygonStrokePrimitives(
+ aB2DPolyPolyVector,
+ aPolygonTransform,
+ rOutlineAttribute.getLineAttribute(),
+ rOutlineAttribute.getStrokeAttribute(),
+ aStrokePrimitives);
+ const sal_uInt32 nStrokeCount(aStrokePrimitives.size());
+
+ if(nStrokeCount)
+ {
+ if(rOutlineAttribute.getTransparence())
+ {
+ // create UnifiedTransparencePrimitive2D
+ aNewPrimitives.push_back(
+ new drawinglayer::primitive2d::UnifiedTransparencePrimitive2D(
+ std::move(aStrokePrimitives),
+ static_cast<double>(rOutlineAttribute.getTransparence()) / 100.0) );
+ }
+ else
+ {
+ // add polygons to rDecomposition as polygonStrokePrimitives
+ aNewPrimitives.append( std::move(aStrokePrimitives) );
+ }
+ }
+ }
+ }
+ }
+
+ return aNewPrimitives;
+ }
+} // end of anonymous namespace
+
+
+// primitive decomposition
+
+void SdrTextObj::impDecomposePathTextPrimitive(
+ drawinglayer::primitive2d::Primitive2DContainer& rTarget,
+ const drawinglayer::primitive2d::SdrPathTextPrimitive2D& rSdrPathTextPrimitive,
+ const drawinglayer::geometry::ViewInformation2D& aViewInformation) const
+{
+ drawinglayer::primitive2d::Primitive2DContainer aRetvalA;
+ drawinglayer::primitive2d::Primitive2DContainer aRetvalB;
+
+ // prepare outliner
+ SdrOutliner& rOutliner = ImpGetDrawOutliner();
+ rOutliner.SetUpdateLayout(true);
+ rOutliner.Clear();
+ rOutliner.SetPaperSize(Size(LONG_MAX,LONG_MAX));
+ rOutliner.SetText(rSdrPathTextPrimitive.getOutlinerParaObject());
+
+ // set visualizing page at Outliner; needed e.g. for PageNumberField decomposition
+ rOutliner.setVisualizedPage(GetSdrPageFromXDrawPage(aViewInformation.getVisualizedPage()));
+
+ // now break up to text portions
+ impTextBreakupHandler aConverter(rOutliner);
+ const ::std::vector< impPathTextPortion > rPathTextPortions = aConverter.decompositionPathTextPrimitive();
+
+ if(!rPathTextPortions.empty())
+ {
+ // get FormText and polygon values
+ const drawinglayer::attribute::SdrFormTextAttribute& rFormTextAttribute = rSdrPathTextPrimitive.getSdrFormTextAttribute();
+ const basegfx::B2DPolyPolygon& rPathPolyPolygon(rSdrPathTextPrimitive.getPathPolyPolygon());
+
+ // get loop count
+ sal_uInt32 nLoopCount(rPathPolyPolygon.count());
+
+ if(o3tl::make_unsigned(rOutliner.GetParagraphCount()) < nLoopCount)
+ {
+ nLoopCount = rOutliner.GetParagraphCount();
+ }
+
+ if(nLoopCount)
+ {
+ // prepare common decomposition stuff
+ drawinglayer::primitive2d::Primitive2DContainer aRegularDecomposition;
+ drawinglayer::primitive2d::Primitive2DContainer aShadowDecomposition;
+ impPolygonParagraphHandler aPolygonParagraphHandler(
+ rFormTextAttribute,
+ aRegularDecomposition,
+ aShadowDecomposition);
+ sal_uInt32 a;
+
+ for(a = 0; a < nLoopCount; a++)
+ {
+ // filter text portions for this paragraph
+ ::std::vector< const impPathTextPortion* > aParagraphTextPortions;
+
+ for(const auto & rCandidate : rPathTextPortions)
+ {
+ if(static_cast<sal_uInt32>(rCandidate.getParagraph()) == a)
+ {
+ aParagraphTextPortions.push_back(&rCandidate);
+ }
+ }
+
+ // handle data pair polygon/ParagraphTextPortions
+ if(!aParagraphTextPortions.empty())
+ {
+ aPolygonParagraphHandler.HandlePair(rPathPolyPolygon.getB2DPolygon(a), aParagraphTextPortions);
+ }
+ }
+
+ const sal_uInt32 nShadowCount(aShadowDecomposition.size());
+ const sal_uInt32 nRegularCount(aRegularDecomposition.size());
+
+ if(nShadowCount)
+ {
+ // add shadow primitives to decomposition
+
+ // if necessary, add shadow outlines
+ if(rFormTextAttribute.getFormTextOutline()
+ && !rFormTextAttribute.getShadowOutline().isDefault())
+ {
+ aRetvalA = aShadowDecomposition;
+ const drawinglayer::primitive2d::Primitive2DContainer aOutlines(
+ impAddPathTextOutlines(
+ aShadowDecomposition,
+ rFormTextAttribute.getShadowOutline()));
+
+ aRetvalA.append(aOutlines);
+ }
+ else
+ aRetvalA = std::move(aShadowDecomposition);
+ }
+
+ if(nRegularCount)
+ {
+ // add normal primitives to decomposition
+
+ // if necessary, add outlines
+ if(rFormTextAttribute.getFormTextOutline()
+ && !rFormTextAttribute.getOutline().isDefault())
+ {
+ aRetvalB = aRegularDecomposition;
+ const drawinglayer::primitive2d::Primitive2DContainer aOutlines(
+ impAddPathTextOutlines(
+ aRegularDecomposition,
+ rFormTextAttribute.getOutline()));
+
+ aRetvalB.append(aOutlines);
+ }
+ else
+ aRetvalB = std::move(aRegularDecomposition);
+ }
+ }
+ }
+
+ // clean up outliner
+ rOutliner.SetDrawPortionHdl(Link<DrawPortionInfo*,void>());
+ rOutliner.Clear();
+ rOutliner.setVisualizedPage(nullptr);
+
+ // concatenate all results
+ rTarget.append(std::move(aRetvalA));
+ rTarget.append(std::move(aRetvalB));
+}
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/svdraw/svdotxat.cxx b/svx/source/svdraw/svdotxat.cxx
new file mode 100644
index 000000000..97bdb5e59
--- /dev/null
+++ b/svx/source/svdraw/svdotxat.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 <comphelper/string.hxx>
+#include <o3tl/sorted_vector.hxx>
+#include <o3tl/string_view.hxx>
+#include <svl/style.hxx>
+#include <svx/svdotext.hxx>
+#include <svx/svdmodel.hxx>
+#include <svx/svdoutl.hxx>
+#include <svx/svdorect.hxx>
+#include <svx/svdocapt.hxx>
+#include <editeng/editdata.hxx>
+#include <svx/sdtfchim.hxx>
+
+
+#include <editeng/outlobj.hxx>
+#include <editeng/outliner.hxx>
+#include <editeng/editobj.hxx>
+
+namespace {
+// The style family which is appended to the style names is padded to this many characters.
+const short PADDING_LENGTH_FOR_STYLE_FAMILY = 5;
+// this character will be used to pad the style families when they are appended to the style names
+const char PADDING_CHARACTER_FOR_STYLE_FAMILY = ' ';
+}
+
+bool SdrTextObj::AdjustTextFrameWidthAndHeight( tools::Rectangle& rR, bool bHgt, bool bWdt ) const
+{
+ if (!mbTextFrame)
+ // Not a text frame. Bail out.
+ return false;
+
+ if (rR.IsEmpty())
+ // Empty rectangle.
+ return false;
+
+ bool bFitToSize = IsFitToSize();
+ if (bFitToSize)
+ return false;
+
+ bool bWdtGrow = bWdt && IsAutoGrowWidth();
+ bool bHgtGrow = bHgt && IsAutoGrowHeight();
+ if (!bWdtGrow && !bHgtGrow)
+ // Not supposed to auto-adjust width or height.
+ return false;
+
+ SdrTextAniKind eAniKind = GetTextAniKind();
+ SdrTextAniDirection eAniDir = GetTextAniDirection();
+
+ bool bScroll = eAniKind == SdrTextAniKind::Scroll || eAniKind == SdrTextAniKind::Alternate || eAniKind == SdrTextAniKind::Slide;
+ bool bHScroll = bScroll && (eAniDir == SdrTextAniDirection::Left || eAniDir == SdrTextAniDirection::Right);
+ bool bVScroll = bScroll && (eAniDir == SdrTextAniDirection::Up || eAniDir == SdrTextAniDirection::Down);
+
+ tools::Rectangle aOldRect = rR;
+ tools::Long nHgt = 0, nMinHgt = 0, nMaxHgt = 0;
+ tools::Long nWdt = 0, nMinWdt = 0, nMaxWdt = 0;
+
+ Size aNewSize = rR.GetSize();
+ aNewSize.AdjustWidth( -1 ); aNewSize.AdjustHeight( -1 );
+
+ Size aMaxSiz(100000, 100000);
+ Size aTmpSiz(getSdrModelFromSdrObject().GetMaxObjSize());
+
+ if (aTmpSiz.Width())
+ aMaxSiz.setWidth( aTmpSiz.Width() );
+ if (aTmpSiz.Height())
+ aMaxSiz.setHeight( aTmpSiz.Height() );
+
+ if (bWdtGrow)
+ {
+ nMinWdt = GetMinTextFrameWidth();
+ nMaxWdt = GetMaxTextFrameWidth();
+ if (nMaxWdt == 0 || nMaxWdt > aMaxSiz.Width())
+ nMaxWdt = aMaxSiz.Width();
+ if (nMinWdt <= 0)
+ nMinWdt = 1;
+
+ aNewSize.setWidth( nMaxWdt );
+ }
+
+ if (bHgtGrow)
+ {
+ nMinHgt = GetMinTextFrameHeight();
+ nMaxHgt = GetMaxTextFrameHeight();
+ if (nMaxHgt == 0 || nMaxHgt > aMaxSiz.Height())
+ nMaxHgt = aMaxSiz.Height();
+ if (nMinHgt <= 0)
+ nMinHgt = 1;
+
+ aNewSize.setHeight( nMaxHgt );
+ }
+
+ tools::Long nHDist = GetTextLeftDistance() + GetTextRightDistance();
+ tools::Long nVDist = GetTextUpperDistance() + GetTextLowerDistance();
+ aNewSize.AdjustWidth( -nHDist );
+ aNewSize.AdjustHeight( -nVDist );
+
+ if (aNewSize.Width() < 2)
+ aNewSize.setWidth( 2 );
+ if (aNewSize.Height() < 2)
+ aNewSize.setHeight( 2 );
+
+ if (!IsInEditMode())
+ {
+ if (bHScroll)
+ aNewSize.setWidth( 0x0FFFFFFF ); // don't break ticker text
+ if (bVScroll)
+ aNewSize.setHeight( 0x0FFFFFFF );
+ }
+
+ if (mpEditingOutliner)
+ {
+ mpEditingOutliner->SetMaxAutoPaperSize(aNewSize);
+ if (bWdtGrow)
+ {
+ Size aSiz2(mpEditingOutliner->CalcTextSize());
+ nWdt = aSiz2.Width() + 1; // a little tolerance
+ if (bHgtGrow)
+ nHgt = aSiz2.Height() + 1; // a little tolerance
+ }
+ else
+ {
+ nHgt = mpEditingOutliner->GetTextHeight() + 1; // a little tolerance
+ }
+ }
+ else
+ {
+ Outliner& rOutliner = ImpGetDrawOutliner();
+ rOutliner.SetPaperSize(aNewSize);
+ rOutliner.SetUpdateLayout(true);
+ // TODO: add the optimization with bPortionInfoChecked etc. here
+ OutlinerParaObject* pOutlinerParaObject = GetOutlinerParaObject();
+ if (pOutlinerParaObject)
+ {
+ rOutliner.SetText(*pOutlinerParaObject);
+ rOutliner.SetFixedCellHeight(GetMergedItem(SDRATTR_TEXT_USEFIXEDCELLHEIGHT).GetValue());
+ }
+
+ if (bWdtGrow)
+ {
+ Size aSiz2(rOutliner.CalcTextSize());
+ nWdt = aSiz2.Width() + 1; // a little tolerance
+ if (bHgtGrow)
+ nHgt = aSiz2.Height() + 1; // a little tolerance
+ }
+ else
+ {
+ nHgt = rOutliner.GetTextHeight() + 1; // a little tolerance
+ }
+ rOutliner.Clear();
+ }
+
+ if (nWdt < nMinWdt)
+ nWdt = nMinWdt;
+ if (nWdt > nMaxWdt)
+ nWdt = nMaxWdt;
+ nWdt += nHDist;
+ if (nWdt < 1)
+ nWdt = 1; // nHDist may be negative
+ if (nHgt < nMinHgt)
+ nHgt = nMinHgt;
+ if (nHgt > nMaxHgt)
+ nHgt = nMaxHgt;
+ nHgt += nVDist;
+ if (nHgt < 1)
+ nHgt = 1; // nVDist may be negative
+ tools::Long nWdtGrow = nWdt - (rR.Right() - rR.Left());
+ tools::Long nHgtGrow = nHgt - (rR.Bottom() - rR.Top());
+
+ if (nWdtGrow == 0)
+ bWdtGrow = false;
+ if (nHgtGrow == 0)
+ bHgtGrow = false;
+
+ if (!bWdtGrow && !bHgtGrow)
+ return false;
+
+ if (bWdtGrow)
+ {
+ SdrTextHorzAdjust eHAdj = GetTextHorizontalAdjust();
+
+ if (eHAdj == SDRTEXTHORZADJUST_LEFT)
+ rR.AdjustRight(nWdtGrow );
+ else if (eHAdj == SDRTEXTHORZADJUST_RIGHT)
+ rR.AdjustLeft( -nWdtGrow );
+ else
+ {
+ tools::Long nWdtGrow2 = nWdtGrow / 2;
+ rR.AdjustLeft( -nWdtGrow2 );
+ rR.SetRight( rR.Left() + nWdt );
+ }
+ }
+
+ if (bHgtGrow)
+ {
+ SdrTextVertAdjust eVAdj = GetTextVerticalAdjust();
+
+ if (eVAdj == SDRTEXTVERTADJUST_TOP)
+ rR.AdjustBottom(nHgtGrow );
+ else if (eVAdj == SDRTEXTVERTADJUST_BOTTOM)
+ rR.AdjustTop( -nHgtGrow );
+ else
+ {
+ tools::Long nHgtGrow2 = nHgtGrow / 2;
+ rR.AdjustTop( -nHgtGrow2 );
+ rR.SetBottom( rR.Top() + nHgt );
+ }
+ }
+
+ if (maGeo.nRotationAngle)
+ {
+ // Object is rotated.
+ Point aD1(rR.TopLeft());
+ aD1 -= aOldRect.TopLeft();
+ Point aD2(aD1);
+ RotatePoint(aD2, Point(), maGeo.mfSinRotationAngle, maGeo.mfCosRotationAngle);
+ aD2 -= aD1;
+ rR.Move(aD2.X(), aD2.Y());
+ }
+
+ return true;
+}
+
+bool SdrTextObj::NbcAdjustTextFrameWidthAndHeight(bool bHgt, bool bWdt)
+{
+ bool bRet = AdjustTextFrameWidthAndHeight(maRect,bHgt,bWdt);
+ if (bRet)
+ {
+ SetBoundAndSnapRectsDirty();
+ if (auto pRectObj = dynamic_cast<SdrRectObj *>(this)) { // this is a hack
+ pRectObj->SetXPolyDirty();
+ }
+ if (auto pCaptionObj = dynamic_cast<SdrCaptionObj *>(this)) { // this is a hack
+ pCaptionObj->ImpRecalcTail();
+ }
+ }
+ return bRet;
+}
+
+bool SdrTextObj::AdjustTextFrameWidthAndHeight()
+{
+ tools::Rectangle aNewRect(maRect);
+ bool bRet=AdjustTextFrameWidthAndHeight(aNewRect);
+ if (bRet) {
+ tools::Rectangle aBoundRect0; if (m_pUserCall!=nullptr) aBoundRect0=GetLastBoundRect();
+ maRect = aNewRect;
+ SetBoundAndSnapRectsDirty();
+ if (auto pRectObj = dynamic_cast<SdrRectObj *>(this)) { // this is a hack
+ pRectObj->SetXPolyDirty();
+ }
+ bool bScPostIt = false;
+ if (auto pCaptionObj = dynamic_cast<SdrCaptionObj *>(this)) { // this is a hack
+ pCaptionObj->ImpRecalcTail();
+ // tdf#114956, tdf#138549 use GetSpecialTextBoxShadow to recognize
+ // that this SdrCaption is for a ScPostit
+ bScPostIt = pCaptionObj->GetSpecialTextBoxShadow();
+ }
+
+ // to not slow down EditView visualization on Overlay (see
+ // TextEditOverlayObject) it is necessary to suppress the
+ // Invalidates for the deep repaint when the size of the
+ // TextFrame changed (AdjustTextFrameWidthAndHeight returned
+ // true). The ObjectChanges are valid, invalidate will be
+ // done on EndTextEdit anyways
+ const bool bSuppressChangeWhenEditOnOverlay(
+ IsInEditMode() &&
+ GetTextEditOutliner() &&
+ GetTextEditOutliner()->hasEditViewCallbacks());
+
+ if (!bSuppressChangeWhenEditOnOverlay || bScPostIt)
+ {
+ SetChanged();
+ BroadcastObjectChange();
+ }
+
+ SendUserCall(SdrUserCallType::Resize,aBoundRect0);
+ }
+ return bRet;
+}
+
+void SdrTextObj::ImpSetTextStyleSheetListeners()
+{
+ SfxStyleSheetBasePool* pStylePool(getSdrModelFromSdrObject().GetStyleSheetPool());
+ if (pStylePool==nullptr)
+ return;
+
+ std::vector<OUString> aStyleNames;
+ OutlinerParaObject* pOutlinerParaObject = GetOutlinerParaObject();
+ if (pOutlinerParaObject!=nullptr)
+ {
+ // First, we collect all stylesheets contained in the ParaObject in
+ // the container aStyles. The Family is always appended to the name
+ // of the stylesheet.
+ const EditTextObject& rTextObj=pOutlinerParaObject->GetTextObject();
+ OUString aStyleName;
+ SfxStyleFamily eStyleFam;
+ sal_Int32 nParaCnt=rTextObj.GetParagraphCount();
+
+
+ for(sal_Int32 nParaNum(0); nParaNum < nParaCnt; nParaNum++)
+ {
+ rTextObj.GetStyleSheet(nParaNum, aStyleName, eStyleFam);
+
+ if (!aStyleName.isEmpty())
+ {
+ AppendFamilyToStyleName(aStyleName, eStyleFam);
+
+ bool bFnd(false);
+ sal_uInt32 nNum(aStyleNames.size());
+
+ while(!bFnd && nNum > 0)
+ {
+ // we don't want duplicate stylesheets
+ nNum--;
+ bFnd = aStyleName == aStyleNames[nNum];
+ }
+
+ if(!bFnd)
+ {
+ aStyleNames.push_back(aStyleName);
+ }
+ }
+ }
+ }
+
+ // now convert the strings in the vector from names to StyleSheet*
+ o3tl::sorted_vector<SfxStyleSheet*> aStyleSheets;
+ while (!aStyleNames.empty()) {
+ OUString aName = aStyleNames.back();
+ aStyleNames.pop_back();
+
+ SfxStyleFamily eFam = ReadFamilyFromStyleName(aName);
+ SfxStyleSheetBase* pStyleBase = pStylePool->Find(aName,eFam);
+ SfxStyleSheet* pStyle = dynamic_cast<SfxStyleSheet*>( pStyleBase );
+ if (pStyle!=nullptr && pStyle!=GetStyleSheet()) {
+ aStyleSheets.insert(pStyle);
+ }
+ }
+ // now remove all superfluous stylesheets
+ sal_uInt16 nNum=GetBroadcasterCount();
+ while (nNum>0) {
+ nNum--;
+ SfxBroadcaster* pBroadcast=GetBroadcasterJOE(nNum);
+ SfxStyleSheet* pStyle=dynamic_cast<SfxStyleSheet*>( pBroadcast );
+ if (pStyle!=nullptr && pStyle!=GetStyleSheet()) { // special case for stylesheet of the object
+ if (aStyleSheets.find(pStyle)==aStyleSheets.end()) {
+ EndListening(*pStyle);
+ }
+ }
+ }
+ // and finally, merge all stylesheets that are contained in aStyles with previous broadcasters
+ for(SfxStyleSheet* pStyle : aStyleSheets) {
+ // let StartListening see for itself if there's already a listener registered
+ StartListening(*pStyle, DuplicateHandling::Prevent);
+ }
+}
+
+/** iterates over the paragraphs of a given SdrObject and removes all
+ hard set character attributes with the which ids contained in the
+ given vector
+*/
+void SdrTextObj::RemoveOutlinerCharacterAttribs( const std::vector<sal_uInt16>& rCharWhichIds )
+{
+ sal_Int32 nText = getTextCount();
+
+ while( --nText >= 0 )
+ {
+ SdrText* pText = getText( nText );
+ OutlinerParaObject* pOutlinerParaObject = pText ? pText->GetOutlinerParaObject() : nullptr;
+
+ if(pOutlinerParaObject)
+ {
+ Outliner* pOutliner = nullptr;
+
+ if( mpEditingOutliner || (pText == getActiveText()) )
+ pOutliner = mpEditingOutliner;
+
+ if(!pOutliner)
+ {
+ pOutliner = &ImpGetDrawOutliner();
+ pOutliner->SetText(*pOutlinerParaObject);
+ }
+
+ ESelection aSelAll( 0, 0, EE_PARA_ALL, EE_TEXTPOS_ALL );
+ for( const auto& rWhichId : rCharWhichIds )
+ {
+ pOutliner->RemoveAttribs( aSelAll, false, rWhichId );
+ }
+
+ if(!mpEditingOutliner || (pText != getActiveText()) )
+ {
+ const sal_Int32 nParaCount = pOutliner->GetParagraphCount();
+ std::optional<OutlinerParaObject> pTemp = pOutliner->CreateParaObject(0, nParaCount);
+ pOutliner->Clear();
+ NbcSetOutlinerParaObjectForText(std::move(pTemp), pText);
+ }
+ }
+ }
+}
+
+bool SdrTextObj::HasText() const
+{
+ if (mpEditingOutliner)
+ return HasTextImpl(mpEditingOutliner);
+
+ OutlinerParaObject* pOPO = GetOutlinerParaObject();
+
+ bool bHasText = false;
+ if( pOPO )
+ {
+ const EditTextObject& rETO = pOPO->GetTextObject();
+ sal_Int32 nParaCount = rETO.GetParagraphCount();
+
+ if( nParaCount > 0 )
+ bHasText = (nParaCount > 1) || (!rETO.GetText( 0 ).isEmpty());
+ }
+
+ return bHasText;
+}
+
+void SdrTextObj::AppendFamilyToStyleName(OUString& styleName, SfxStyleFamily family)
+{
+ OUStringBuffer aFam;
+ aFam.append(static_cast<sal_Int32>(family));
+ comphelper::string::padToLength(aFam, PADDING_LENGTH_FOR_STYLE_FAMILY , PADDING_CHARACTER_FOR_STYLE_FAMILY);
+
+ styleName += "|" + aFam;
+}
+
+SfxStyleFamily SdrTextObj::ReadFamilyFromStyleName(const OUString& styleName)
+{
+ std::u16string_view familyString = styleName.subView(styleName.getLength() - PADDING_LENGTH_FOR_STYLE_FAMILY);
+ familyString = comphelper::string::stripEnd(familyString, PADDING_CHARACTER_FOR_STYLE_FAMILY);
+ sal_uInt16 nFam = static_cast<sal_uInt16>(o3tl::toInt32(familyString));
+ assert(nFam != 0);
+ return static_cast<SfxStyleFamily>(nFam);
+}
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/svdraw/svdotxdr.cxx b/svx/source/svdraw/svdotxdr.cxx
new file mode 100644
index 000000000..ad2f79933
--- /dev/null
+++ b/svx/source/svdraw/svdotxdr.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 <svx/svdotext.hxx>
+#include <svx/svdhdl.hxx>
+#include <svx/svddrag.hxx>
+#include <svx/svdview.hxx>
+#include <svx/svdorect.hxx>
+#include <svx/strings.hrc>
+#include <svx/svdoashp.hxx>
+#include <tools/bigint.hxx>
+#include <basegfx/polygon/b2dpolygon.hxx>
+#include <basegfx/range/b2drange.hxx>
+#include <basegfx/polygon/b2dpolygontools.hxx>
+#include <vcl/canvastools.hxx>
+#include <vcl/ptrstyle.hxx>
+
+
+sal_uInt32 SdrTextObj::GetHdlCount() const
+{
+ return 8;
+}
+
+void SdrTextObj::AddToHdlList(SdrHdlList& rHdlList) const
+{
+ for(sal_uInt32 nHdlNum=0; nHdlNum<8; ++nHdlNum)
+ {
+ Point aPnt;
+ SdrHdlKind eKind = SdrHdlKind::UpperLeft;
+ switch (nHdlNum) {
+ case 0: aPnt=maRect.TopLeft(); eKind=SdrHdlKind::UpperLeft; break;
+ case 1: aPnt=maRect.TopCenter(); eKind=SdrHdlKind::Upper; break;
+ case 2: aPnt=maRect.TopRight(); eKind=SdrHdlKind::UpperRight; break;
+ case 3: aPnt=maRect.LeftCenter(); eKind=SdrHdlKind::Left ; break;
+ case 4: aPnt=maRect.RightCenter(); eKind=SdrHdlKind::Right; break;
+ case 5: aPnt=maRect.BottomLeft(); eKind=SdrHdlKind::LowerLeft; break;
+ case 6: aPnt=maRect.BottomCenter(); eKind=SdrHdlKind::Lower; break;
+ case 7: aPnt=maRect.BottomRight(); eKind=SdrHdlKind::LowerRight; break;
+ }
+ if (maGeo.nShearAngle) ShearPoint(aPnt,maRect.TopLeft(),maGeo.mfTanShearAngle);
+ if (maGeo.nRotationAngle) RotatePoint(aPnt,maRect.TopLeft(),maGeo.mfSinRotationAngle,maGeo.mfCosRotationAngle);
+ std::unique_ptr<SdrHdl> pH(new SdrHdl(aPnt,eKind));
+ pH->SetObj(const_cast<SdrTextObj*>(this));
+ pH->SetRotationAngle(maGeo.nRotationAngle);
+ rHdlList.AddHdl(std::move(pH));
+ }
+}
+
+
+bool SdrTextObj::hasSpecialDrag() const
+{
+ return true;
+}
+
+tools::Rectangle SdrTextObj::ImpDragCalcRect(const SdrDragStat& rDrag) const
+{
+ tools::Rectangle aTmpRect(maRect);
+ const SdrHdl* pHdl=rDrag.GetHdl();
+ SdrHdlKind eHdl=pHdl==nullptr ? SdrHdlKind::Move : pHdl->GetKind();
+ bool bEcke=(eHdl==SdrHdlKind::UpperLeft || eHdl==SdrHdlKind::UpperRight || eHdl==SdrHdlKind::LowerLeft || eHdl==SdrHdlKind::LowerRight);
+ bool bOrtho=rDrag.GetView()!=nullptr && rDrag.GetView()->IsOrtho();
+ bool bBigOrtho=bEcke && bOrtho && rDrag.GetView()->IsBigOrtho();
+ Point aPos(rDrag.GetNow());
+ // Unrotate:
+ if (maGeo.nRotationAngle) RotatePoint(aPos,aTmpRect.TopLeft(),-maGeo.mfSinRotationAngle,maGeo.mfCosRotationAngle);
+ // Unshear:
+ if (maGeo.nShearAngle) ShearPoint(aPos,aTmpRect.TopLeft(),-maGeo.mfTanShearAngle);
+
+ bool bLft=(eHdl==SdrHdlKind::UpperLeft || eHdl==SdrHdlKind::Left || eHdl==SdrHdlKind::LowerLeft);
+ bool bRgt=(eHdl==SdrHdlKind::UpperRight || eHdl==SdrHdlKind::Right || eHdl==SdrHdlKind::LowerRight);
+ bool bTop=(eHdl==SdrHdlKind::UpperRight || eHdl==SdrHdlKind::Upper || eHdl==SdrHdlKind::UpperLeft);
+ bool bBtm=(eHdl==SdrHdlKind::LowerRight || eHdl==SdrHdlKind::Lower || eHdl==SdrHdlKind::LowerLeft);
+ if (bLft) aTmpRect.SetLeft(aPos.X() );
+ if (bRgt) aTmpRect.SetRight(aPos.X() );
+ if (bTop) aTmpRect.SetTop(aPos.Y() );
+ if (bBtm) aTmpRect.SetBottom(aPos.Y() );
+ if (bOrtho) { // Ortho
+ tools::Long nWdt0=maRect.Right() -maRect.Left();
+ tools::Long nHgt0=maRect.Bottom()-maRect.Top();
+ tools::Long nXMul=aTmpRect.Right() -aTmpRect.Left();
+ tools::Long nYMul=aTmpRect.Bottom()-aTmpRect.Top();
+ tools::Long nXDiv=nWdt0;
+ tools::Long nYDiv=nHgt0;
+ bool bXNeg=(nXMul<0)!=(nXDiv<0);
+ bool bYNeg=(nYMul<0)!=(nYDiv<0);
+ nXMul=std::abs(nXMul);
+ nYMul=std::abs(nYMul);
+ nXDiv=std::abs(nXDiv);
+ nYDiv=std::abs(nYDiv);
+ Fraction aXFact(nXMul,nXDiv); // fractions for canceling
+ Fraction aYFact(nYMul,nYDiv); // and for comparing
+ nXMul=aXFact.GetNumerator();
+ nYMul=aYFact.GetNumerator();
+ nXDiv=aXFact.GetDenominator();
+ nYDiv=aYFact.GetDenominator();
+ if (bEcke) { // corner point handles
+ bool bUseX=(aXFact<aYFact) != bBigOrtho;
+ if (bUseX) {
+ tools::Long nNeed=tools::Long(BigInt(nHgt0)*BigInt(nXMul)/BigInt(nXDiv));
+ if (bYNeg) nNeed=-nNeed;
+ if (bTop) aTmpRect.SetTop(aTmpRect.Bottom()-nNeed );
+ if (bBtm) aTmpRect.SetBottom(aTmpRect.Top()+nNeed );
+ } else {
+ tools::Long nNeed=tools::Long(BigInt(nWdt0)*BigInt(nYMul)/BigInt(nYDiv));
+ if (bXNeg) nNeed=-nNeed;
+ if (bLft) aTmpRect.SetLeft(aTmpRect.Right()-nNeed );
+ if (bRgt) aTmpRect.SetRight(aTmpRect.Left()+nNeed );
+ }
+ } else { // apex handles
+ if ((bLft || bRgt) && nXDiv!=0) {
+ tools::Long nHgt0b=maRect.Bottom()-maRect.Top();
+ tools::Long nNeed=tools::Long(BigInt(nHgt0b)*BigInt(nXMul)/BigInt(nXDiv));
+ aTmpRect.AdjustTop( -((nNeed-nHgt0b)/2) );
+ aTmpRect.SetBottom(aTmpRect.Top()+nNeed );
+ }
+ if ((bTop || bBtm) && nYDiv!=0) {
+ tools::Long nWdt0b=maRect.Right()-maRect.Left();
+ tools::Long nNeed=tools::Long(BigInt(nWdt0b)*BigInt(nYMul)/BigInt(nYDiv));
+ aTmpRect.AdjustLeft( -((nNeed-nWdt0b)/2) );
+ aTmpRect.SetRight(aTmpRect.Left()+nNeed );
+ }
+ }
+ }
+ if (dynamic_cast<const SdrObjCustomShape*>(this) == nullptr) // not justifying when in CustomShapes, to be able to detect if a shape has to be mirrored
+ ImpJustifyRect(aTmpRect);
+ return aTmpRect;
+}
+
+
+// drag
+
+bool SdrTextObj::applySpecialDrag(SdrDragStat& rDrag)
+{
+ tools::Rectangle aNewRect(ImpDragCalcRect(rDrag));
+
+ if(aNewRect.TopLeft() != maRect.TopLeft() && (maGeo.nRotationAngle || maGeo.nShearAngle))
+ {
+ Point aNewPos(aNewRect.TopLeft());
+
+ if (maGeo.nShearAngle)
+ ShearPoint(aNewPos,maRect.TopLeft(),maGeo.mfTanShearAngle);
+
+ if (maGeo.nRotationAngle)
+ RotatePoint(aNewPos,maRect.TopLeft(),maGeo.mfSinRotationAngle,maGeo.mfCosRotationAngle);
+
+ aNewRect.SetPos(aNewPos);
+ }
+
+ if (aNewRect != maRect)
+ {
+ NbcSetLogicRect(aNewRect);
+ }
+
+ return true;
+}
+
+OUString SdrTextObj::getSpecialDragComment(const SdrDragStat& /*rDrag*/) const
+{
+ return ImpGetDescriptionStr(STR_DragRectResize);
+}
+
+
+// Create
+
+bool SdrTextObj::BegCreate(SdrDragStat& rStat)
+{
+ rStat.SetOrtho4Possible();
+ tools::Rectangle aRect1(rStat.GetStart(), rStat.GetNow());
+ aRect1.Justify();
+ rStat.SetActionRect(aRect1);
+ maRect = aRect1;
+ return true;
+}
+
+bool SdrTextObj::MovCreate(SdrDragStat& rStat)
+{
+ tools::Rectangle aRect1;
+ rStat.TakeCreateRect(aRect1);
+ ImpJustifyRect(aRect1);
+ rStat.SetActionRect(aRect1);
+ maRect = aRect1; // for ObjName
+ SetBoundRectDirty();
+ m_bSnapRectDirty=true;
+ if (auto pRectObj = dynamic_cast<SdrRectObj *>(this)) {
+ pRectObj->SetXPolyDirty();
+ }
+ return true;
+}
+
+bool SdrTextObj::EndCreate(SdrDragStat& rStat, SdrCreateCmd eCmd)
+{
+ rStat.TakeCreateRect(maRect);
+ ImpJustifyRect(maRect);
+
+ AdaptTextMinSize();
+
+ SetBoundAndSnapRectsDirty();
+ if (auto pRectObj = dynamic_cast<SdrRectObj *>(this)) {
+ pRectObj->SetXPolyDirty();
+ }
+ return (eCmd==SdrCreateCmd::ForceEnd || rStat.GetPointCount()>=2);
+}
+
+void SdrTextObj::BrkCreate(SdrDragStat& /*rStat*/)
+{
+}
+
+bool SdrTextObj::BckCreate(SdrDragStat& /*rStat*/)
+{
+ return true;
+}
+
+basegfx::B2DPolyPolygon SdrTextObj::TakeCreatePoly(const SdrDragStat& rDrag) const
+{
+ tools::Rectangle aRect1;
+ rDrag.TakeCreateRect(aRect1);
+ aRect1.Justify();
+
+ basegfx::B2DPolyPolygon aRetval;
+ const basegfx::B2DRange aRange = vcl::unotools::b2DRectangleFromRectangle(aRect1);
+ aRetval.append(basegfx::utils::createPolygonFromRect(aRange));
+ return aRetval;
+}
+
+PointerStyle SdrTextObj::GetCreatePointer() const
+{
+ if (IsTextFrame()) return PointerStyle::DrawText;
+ return PointerStyle::Cross;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/svdraw/svdotxed.cxx b/svx/source/svdraw/svdotxed.cxx
new file mode 100644
index 000000000..96523d679
--- /dev/null
+++ b/svx/source/svdraw/svdotxed.cxx
@@ -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 .
+ */
+
+
+#include <svx/svdotext.hxx>
+#include <svx/svdmodel.hxx>
+#include <svx/svdoutl.hxx>
+#include <editeng/editdata.hxx>
+#include <editeng/outliner.hxx>
+#include <editeng/outlobj.hxx>
+#include <editeng/editstat.hxx>
+#include <svl/itemset.hxx>
+#include <editeng/eeitem.hxx>
+#include <svx/sdtfchim.hxx>
+#include <textchain.hxx>
+
+
+bool SdrTextObj::HasTextEdit() const
+{
+ // linked text objects may be changed (no automatic reload)
+ return true;
+}
+
+bool SdrTextObj::BegTextEdit(SdrOutliner& rOutl)
+{
+ if (mpEditingOutliner!=nullptr) return false; // Textedit might already run in another View!
+ mpEditingOutliner=&rOutl;
+
+ mbInEditMode = true;
+
+ OutlinerMode nOutlinerMode = OutlinerMode::OutlineObject;
+ if ( !IsOutlText() )
+ nOutlinerMode = OutlinerMode::TextObject;
+ rOutl.Init( nOutlinerMode );
+ rOutl.SetRefDevice(getSdrModelFromSdrObject().GetRefDevice());
+
+ bool bFitToSize(IsFitToSize());
+ bool bContourFrame=IsContourTextFrame();
+ ImpSetTextEditParams();
+
+ if (!bContourFrame) {
+ EEControlBits nStat=rOutl.GetControlWord();
+ nStat|=EEControlBits::AUTOPAGESIZE;
+ if (bFitToSize || IsAutoFit())
+ nStat|=EEControlBits::STRETCHING;
+ else
+ nStat&=~EEControlBits::STRETCHING;
+ rOutl.SetControlWord(nStat);
+ }
+
+ // disable AUTOPAGESIZE if IsChainable (might be required for overflow check)
+ if ( IsChainable() ) {
+ EEControlBits nStat1=rOutl.GetControlWord();
+ nStat1 &=~EEControlBits::AUTOPAGESIZE;
+ rOutl.SetControlWord(nStat1);
+ }
+
+
+ OutlinerParaObject* pOutlinerParaObject = GetOutlinerParaObject();
+ if(pOutlinerParaObject!=nullptr)
+ {
+ rOutl.SetText(*GetOutlinerParaObject());
+ rOutl.SetFixedCellHeight(GetMergedItem(SDRATTR_TEXT_USEFIXEDCELLHEIGHT).GetValue());
+ }
+
+ // if necessary, set frame attributes for the first (new) paragraph of the
+ // outliner
+ if( !HasTextImpl( &rOutl ) )
+ {
+ // Outliner has no text so we must set some
+ // empty text so the outliner initialise itself
+ rOutl.SetText( "", rOutl.GetParagraph( 0 ) );
+
+ if(GetStyleSheet())
+ rOutl.SetStyleSheet( 0, GetStyleSheet());
+
+ // When setting the "hard" attributes for first paragraph, the Parent
+ // pOutlAttr (i. e. the template) has to be removed temporarily. Else,
+ // at SetParaAttribs(), all attributes contained in the parent become
+ // attributed hard to the paragraph.
+ const SfxItemSet& rSet = GetObjectItemSet();
+ SfxItemSetFixed<EE_ITEMS_START, EE_ITEMS_END> aFilteredSet(*rSet.GetPool());
+ aFilteredSet.Put(rSet);
+ rOutl.SetParaAttribs(0, aFilteredSet);
+ }
+ if (bFitToSize)
+ {
+ tools::Rectangle aAnchorRect;
+ tools::Rectangle aTextRect;
+ TakeTextRect(rOutl, aTextRect, false,
+ &aAnchorRect);
+ Fraction aFitXCorrection(1,1);
+ ImpSetCharStretching(rOutl,aTextRect.GetSize(),aAnchorRect.GetSize(),aFitXCorrection);
+ }
+ else if (IsAutoFit())
+ {
+ ImpAutoFitText(rOutl);
+ }
+
+ if(pOutlinerParaObject)
+ {
+ if (maGeo.nRotationAngle || IsFontwork())
+ {
+ // only repaint here, no real objectchange
+ BroadcastObjectChange();
+ }
+ }
+
+ rOutl.UpdateFields();
+ rOutl.ClearModifyFlag();
+
+ return true;
+}
+
+void SdrTextObj::TakeTextEditArea(Size* pPaperMin, Size* pPaperMax, tools::Rectangle* pViewInit, tools::Rectangle* pViewMin) const
+{
+ bool bFitToSize(IsFitToSize());
+ Size aPaperMin,aPaperMax;
+ tools::Rectangle aViewInit;
+ TakeTextAnchorRect(aViewInit);
+ if (maGeo.nRotationAngle) {
+ Point aCenter(aViewInit.Center());
+ aCenter-=aViewInit.TopLeft();
+ Point aCenter0(aCenter);
+ RotatePoint(aCenter,Point(),maGeo.mfSinRotationAngle,maGeo.mfCosRotationAngle);
+ aCenter-=aCenter0;
+ aViewInit.Move(aCenter.X(),aCenter.Y());
+ }
+ Size aAnkSiz(aViewInit.GetSize());
+ aAnkSiz.AdjustWidth( -1 ); aAnkSiz.AdjustHeight( -1 ); // because GetSize() adds 1
+ Size aMaxSiz(1000000,1000000);
+ Size aTmpSiz(getSdrModelFromSdrObject().GetMaxObjSize());
+ if (aTmpSiz.Width()!=0) aMaxSiz.setWidth(aTmpSiz.Width() );
+ if (aTmpSiz.Height()!=0) aMaxSiz.setHeight(aTmpSiz.Height() );
+
+ // Done earlier since used in else tree below
+ SdrTextHorzAdjust eHAdj(GetTextHorizontalAdjust());
+ SdrTextVertAdjust eVAdj(GetTextVerticalAdjust());
+
+ if(IsTextFrame())
+ {
+ tools::Long nMinWdt=GetMinTextFrameWidth();
+ tools::Long nMinHgt=GetMinTextFrameHeight();
+ tools::Long nMaxWdt=GetMaxTextFrameWidth();
+ tools::Long nMaxHgt=GetMaxTextFrameHeight();
+ if (nMinWdt<1) nMinWdt=1;
+ if (nMinHgt<1) nMinHgt=1;
+ if (!bFitToSize) {
+ if (nMaxWdt==0 || nMaxWdt>aMaxSiz.Width()) nMaxWdt=aMaxSiz.Width();
+ if (nMaxHgt==0 || nMaxHgt>aMaxSiz.Height()) nMaxHgt=aMaxSiz.Height();
+
+ if (!IsAutoGrowWidth() )
+ {
+ nMinWdt = aAnkSiz.Width();
+ nMaxWdt = nMinWdt;
+ }
+
+ if (!IsAutoGrowHeight())
+ {
+ nMinHgt = aAnkSiz.Height();
+ nMaxHgt = nMinHgt;
+ }
+
+ SdrTextAniKind eAniKind=GetTextAniKind();
+ SdrTextAniDirection eAniDirection=GetTextAniDirection();
+
+ bool bInEditMode = IsInEditMode();
+
+ if (!bInEditMode && (eAniKind==SdrTextAniKind::Scroll || eAniKind==SdrTextAniKind::Alternate || eAniKind==SdrTextAniKind::Slide))
+ {
+ // ticker text uses an unlimited paper size
+ if (eAniDirection==SdrTextAniDirection::Left || eAniDirection==SdrTextAniDirection::Right) nMaxWdt=1000000;
+ if (eAniDirection==SdrTextAniDirection::Up || eAniDirection==SdrTextAniDirection::Down) nMaxHgt=1000000;
+ }
+
+ bool bChainedFrame = IsChainable();
+ // Might be required for overflow check working: do limit height to frame if box is chainable.
+ if (!bChainedFrame) {
+ // #i119885# Do not limit/force height to geometrical frame (vice versa for vertical writing)
+ if(IsVerticalWriting())
+ {
+ nMaxWdt = 1000000;
+ }
+ else
+ {
+ nMaxHgt = 1000000;
+ }
+ }
+
+ aPaperMax.setWidth(nMaxWdt );
+ aPaperMax.setHeight(nMaxHgt );
+ }
+ else
+ {
+ aPaperMax=aMaxSiz;
+ }
+ aPaperMin.setWidth(nMinWdt );
+ aPaperMin.setHeight(nMinHgt );
+ }
+ else
+ {
+ // aPaperMin needs to be set to object's size if full width is activated
+ // for hor or ver writing respectively
+ if((SDRTEXTHORZADJUST_BLOCK == eHAdj && !IsVerticalWriting())
+ || (SDRTEXTVERTADJUST_BLOCK == eVAdj && IsVerticalWriting()))
+ {
+ aPaperMin = aAnkSiz;
+ }
+
+ aPaperMax=aMaxSiz;
+ }
+
+ if (pViewMin!=nullptr) {
+ *pViewMin=aViewInit;
+
+ tools::Long nXFree=aAnkSiz.Width()-aPaperMin.Width();
+ if (eHAdj==SDRTEXTHORZADJUST_LEFT) pViewMin->AdjustRight( -nXFree );
+ else if (eHAdj==SDRTEXTHORZADJUST_RIGHT) pViewMin->AdjustLeft(nXFree );
+ else { pViewMin->AdjustLeft(nXFree/2 ); pViewMin->SetRight(pViewMin->Left()+aPaperMin.Width() ); }
+
+ tools::Long nYFree=aAnkSiz.Height()-aPaperMin.Height();
+ if (eVAdj==SDRTEXTVERTADJUST_TOP) pViewMin->AdjustBottom( -nYFree );
+ else if (eVAdj==SDRTEXTVERTADJUST_BOTTOM) pViewMin->AdjustTop(nYFree );
+ else { pViewMin->AdjustTop(nYFree/2 ); pViewMin->SetBottom(pViewMin->Top()+aPaperMin.Height() ); }
+ }
+
+ // PaperSize should grow automatically in most cases
+ if(IsVerticalWriting())
+ aPaperMin.setWidth( 0 );
+ else
+ aPaperMin.setHeight( 0 );
+
+ if(eHAdj!=SDRTEXTHORZADJUST_BLOCK || bFitToSize) {
+ aPaperMin.setWidth(0 );
+ }
+
+ // For complete vertical adjustment support, set paper min height to 0, here.
+ if(SDRTEXTVERTADJUST_BLOCK != eVAdj || bFitToSize)
+ {
+ aPaperMin.setHeight( 0 );
+ }
+
+ if (pPaperMin!=nullptr) *pPaperMin=aPaperMin;
+ if (pPaperMax!=nullptr) *pPaperMax=aPaperMax;
+ if (pViewInit!=nullptr) *pViewInit=aViewInit;
+}
+
+void SdrTextObj::EndTextEdit(SdrOutliner& rOutl)
+{
+ if(rOutl.IsModified())
+ {
+
+ // to make the gray field background vanish again
+ rOutl.UpdateFields();
+
+ std::optional<OutlinerParaObject> pNewText = rOutl.CreateParaObject( 0, rOutl.GetParagraphCount() );
+
+ // need to end edit mode early since SetOutlinerParaObject already
+ // uses GetCurrentBoundRect() which needs to take the text into account
+ // to work correct
+ mbInEditMode = false;
+
+ // We don't want broadcasting if we are merely trying to move to next box (this prevents infinite loops)
+ if (IsChainable() && GetTextChain()->GetSwitchingToNextBox(this)) {
+ GetTextChain()->SetSwitchingToNextBox(this, false);
+ if( getActiveText() )
+ {
+ getActiveText()->SetOutlinerParaObject( std::move(pNewText) );
+ }
+ } else { // If we are not doing in-chaining switching just set the ParaObject
+ SetOutlinerParaObject(std::move(pNewText));
+ }
+ }
+
+ /* Chaining-related code */
+ rOutl.ClearOverflowingParaNum();
+
+ mpEditingOutliner = nullptr;
+ rOutl.Clear();
+ EEControlBits nStat = rOutl.GetControlWord();
+ nStat &= ~EEControlBits::AUTOPAGESIZE;
+ rOutl.SetControlWord(nStat);
+
+ mbInEditMode = false;
+}
+
+EEAnchorMode SdrTextObj::GetOutlinerViewAnchorMode() const
+{
+ SdrTextHorzAdjust eH=GetTextHorizontalAdjust();
+ SdrTextVertAdjust eV=GetTextVerticalAdjust();
+ EEAnchorMode eRet=EEAnchorMode::TopLeft;
+ if (IsContourTextFrame()) return eRet;
+ if (eH==SDRTEXTHORZADJUST_LEFT) {
+ if (eV==SDRTEXTVERTADJUST_TOP) {
+ eRet=EEAnchorMode::TopLeft;
+ } else if (eV==SDRTEXTVERTADJUST_BOTTOM) {
+ eRet=EEAnchorMode::BottomLeft;
+ } else {
+ eRet=EEAnchorMode::VCenterLeft;
+ }
+ } else if (eH==SDRTEXTHORZADJUST_RIGHT) {
+ if (eV==SDRTEXTVERTADJUST_TOP) {
+ eRet=EEAnchorMode::TopRight;
+ } else if (eV==SDRTEXTVERTADJUST_BOTTOM) {
+ eRet=EEAnchorMode::BottomRight;
+ } else {
+ eRet=EEAnchorMode::VCenterRight;
+ }
+ } else {
+ if (eV==SDRTEXTVERTADJUST_TOP) {
+ eRet=EEAnchorMode::TopHCenter;
+ } else if (eV==SDRTEXTVERTADJUST_BOTTOM) {
+ eRet=EEAnchorMode::BottomHCenter;
+ } else {
+ eRet=EEAnchorMode::VCenterHCenter;
+ }
+ }
+ return eRet;
+}
+
+void SdrTextObj::ImpSetTextEditParams() const
+{
+ if (mpEditingOutliner==nullptr)
+ return;
+
+ bool bUpdBuf=mpEditingOutliner->SetUpdateLayout(false);
+ Size aPaperMin;
+ Size aPaperMax;
+ tools::Rectangle aEditArea;
+ TakeTextEditArea(&aPaperMin,&aPaperMax,&aEditArea,nullptr);
+ bool bContourFrame=IsContourTextFrame();
+ mpEditingOutliner->SetMinAutoPaperSize(aPaperMin);
+ mpEditingOutliner->SetMaxAutoPaperSize(aPaperMax);
+ mpEditingOutliner->SetPaperSize(Size());
+ mpEditingOutliner->SetTextColumns(GetTextColumnsNumber(), GetTextColumnsSpacing());
+ if (bContourFrame) {
+ tools::Rectangle aAnchorRect;
+ TakeTextAnchorRect(aAnchorRect);
+ ImpSetContourPolygon(*mpEditingOutliner,aAnchorRect, true);
+ }
+ if (bUpdBuf) mpEditingOutliner->SetUpdateLayout(true);
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/svdraw/svdotxfl.cxx b/svx/source/svdraw/svdotxfl.cxx
new file mode 100644
index 000000000..e2c7c7515
--- /dev/null
+++ b/svx/source/svdraw/svdotxfl.cxx
@@ -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 .
+ */
+
+#include <svx/svdotext.hxx>
+
+bool SdrTextObj::CalcFieldValue(const SvxFieldItem& /*rField*/, sal_Int32 /*nPara*/, sal_uInt16 /*nPos*/,
+ bool /*bEdit*/, std::optional<Color>& /*rpTxtColor*/, std::optional<Color>& /*rpFldColor*/, OUString& /*rRet*/) const
+{
+ return false;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/svdraw/svdotxln.cxx b/svx/source/svdraw/svdotxln.cxx
new file mode 100644
index 000000000..19953dcc6
--- /dev/null
+++ b/svx/source/svdraw/svdotxln.cxx
@@ -0,0 +1,277 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <sal/config.h>
+
+#include <comphelper/processfactory.hxx>
+#include <osl/file.hxx>
+#include <osl/thread.h>
+#include <unotools/ucbstreamhelper.hxx>
+#include <ucbhelper/content.hxx>
+#include <unotools/datetime.hxx>
+#include <svx/svdotext.hxx>
+#include <svx/svdmodel.hxx>
+#include <editeng/editdata.hxx>
+#include <sfx2/lnkbase.hxx>
+#include <sfx2/linkmgr.hxx>
+#include <tools/urlobj.hxx>
+#include <tools/debug.hxx>
+#include <tools/tenccvt.hxx>
+#include <memory>
+
+class ImpSdrObjTextLink: public ::sfx2::SvBaseLink
+{
+ SdrTextObj* pSdrObj;
+
+public:
+ explicit ImpSdrObjTextLink( SdrTextObj* pObj1 )
+ : ::sfx2::SvBaseLink( ::SfxLinkUpdateMode::ONCALL, SotClipboardFormatId::SIMPLE_FILE ),
+ pSdrObj( pObj1 )
+ {}
+
+ virtual void Closed() override;
+ virtual ::sfx2::SvBaseLink::UpdateResult DataChanged(
+ const OUString& rMimeType, const css::uno::Any & rValue ) override;
+};
+
+void ImpSdrObjTextLink::Closed()
+{
+ if (pSdrObj )
+ {
+ // set pLink of the object to NULL, because we are destroying the link instance now
+ ImpSdrObjTextLinkUserData* pData=pSdrObj->GetLinkUserData();
+ if (pData!=nullptr) pData->mpLink = nullptr;
+ pSdrObj->ReleaseTextLink();
+ }
+ SvBaseLink::Closed();
+}
+
+
+::sfx2::SvBaseLink::UpdateResult ImpSdrObjTextLink::DataChanged(
+ const OUString& /*rMimeType*/, const css::uno::Any & /*rValue */)
+{
+ bool bForceReload = false;
+ SdrModel* pModel(pSdrObj ? &pSdrObj->getSdrModelFromSdrObject() : nullptr);
+ sfx2::LinkManager* pLinkManager(pModel ? pModel->GetLinkManager() : nullptr);
+
+ if( pLinkManager )
+ {
+ ImpSdrObjTextLinkUserData* pData=pSdrObj->GetLinkUserData();
+ if( pData )
+ {
+ OUString aFile;
+ OUString aFilter;
+ sfx2::LinkManager::GetDisplayNames( this, nullptr,&aFile, nullptr, &aFilter );
+
+ if( pData->maFileName != aFile ||
+ pData->maFilterName != aFilter )
+ {
+ pData->maFileName = aFile;
+ pData->maFilterName = aFilter;
+ pSdrObj->SetChanged();
+ bForceReload = true;
+ }
+ }
+ }
+ if (pSdrObj )
+ pSdrObj->ReloadLinkedText( bForceReload );
+
+ return SUCCESS;
+}
+
+
+ImpSdrObjTextLinkUserData::ImpSdrObjTextLinkUserData():
+ SdrObjUserData(SdrInventor::Default,SDRUSERDATA_OBJTEXTLINK),
+ maFileDate0( DateTime::EMPTY ),
+ meCharSet(RTL_TEXTENCODING_DONTKNOW)
+{
+}
+
+ImpSdrObjTextLinkUserData::~ImpSdrObjTextLinkUserData()
+{
+}
+
+std::unique_ptr<SdrObjUserData> ImpSdrObjTextLinkUserData::Clone(SdrObject* ) const
+{
+ ImpSdrObjTextLinkUserData* pData = new ImpSdrObjTextLinkUserData;
+ pData->maFileName = maFileName;
+ pData->maFilterName = maFilterName;
+ pData->maFileDate0 = maFileDate0;
+ pData->meCharSet = meCharSet;
+ pData->mpLink = nullptr;
+ return std::unique_ptr<SdrObjUserData>(pData);
+}
+
+
+void SdrTextObj::SetTextLink(const OUString& rFileName, const OUString& rFilterName)
+{
+ rtl_TextEncoding eCharSet = osl_getThreadTextEncoding();
+
+ ImpSdrObjTextLinkUserData* pData=GetLinkUserData();
+ if (pData!=nullptr) {
+ ReleaseTextLink();
+ }
+ pData=new ImpSdrObjTextLinkUserData;
+ pData->maFileName = rFileName;
+ pData->maFilterName = rFilterName;
+ pData->meCharSet = eCharSet;
+ AppendUserData(std::unique_ptr<SdrObjUserData>(pData));
+ ImpRegisterLink();
+}
+
+void SdrTextObj::ReleaseTextLink()
+{
+ ImpDeregisterLink();
+ sal_uInt16 nCount=GetUserDataCount();
+ for (sal_uInt16 nNum=nCount; nNum>0;) {
+ nNum--;
+ SdrObjUserData* pData=GetUserData(nNum);
+ if (pData->GetInventor()==SdrInventor::Default && pData->GetId()==SDRUSERDATA_OBJTEXTLINK) {
+ DeleteUserData(nNum);
+ }
+ }
+}
+
+bool SdrTextObj::ReloadLinkedText( bool bForceLoad)
+{
+ ImpSdrObjTextLinkUserData* pData = GetLinkUserData();
+ bool bRet = true;
+
+ if( pData )
+ {
+ DateTime aFileDT( DateTime::EMPTY );
+ bool bExists = true;
+
+ try
+ {
+ INetURLObject aURL( pData->maFileName );
+ DBG_ASSERT( aURL.GetProtocol() != INetProtocol::NotValid, "invalid URL" );
+
+ ::ucbhelper::Content aCnt( aURL.GetMainURL( INetURLObject::DecodeMechanism::NONE ), css::uno::Reference< css::ucb::XCommandEnvironment >(), comphelper::getProcessComponentContext() );
+ css::uno::Any aAny( aCnt.getPropertyValue("DateModified") );
+ css::util::DateTime aDateTime;
+
+ aAny >>= aDateTime;
+ ::utl::typeConvert( aDateTime, aFileDT );
+ }
+ catch( ... )
+ {
+ bExists = false;
+ }
+
+ if( bExists )
+ {
+ bool bLoad = false;
+ if( bForceLoad )
+ bLoad = true;
+ else
+ bLoad = ( aFileDT > pData->maFileDate0 );
+
+ if( bLoad )
+ {
+ bRet = LoadText( pData->maFileName, pData->meCharSet );
+ }
+
+ pData->maFileDate0 = aFileDT;
+ }
+ }
+
+ return bRet;
+}
+
+bool SdrTextObj::LoadText(const OUString& rFileName, rtl_TextEncoding eCharSet)
+{
+ INetURLObject aFileURL( rFileName );
+ bool bRet = false;
+
+ if( aFileURL.GetProtocol() == INetProtocol::NotValid )
+ {
+ OUString aFileURLStr;
+
+ if( osl::FileBase::getFileURLFromSystemPath( rFileName, aFileURLStr ) == osl::FileBase::E_None )
+ aFileURL = INetURLObject( aFileURLStr );
+ else
+ aFileURL.SetSmartURL( rFileName );
+ }
+
+ DBG_ASSERT( aFileURL.GetProtocol() != INetProtocol::NotValid, "invalid URL" );
+
+ std::unique_ptr<SvStream> pIStm(::utl::UcbStreamHelper::CreateStream( aFileURL.GetMainURL( INetURLObject::DecodeMechanism::NONE ), StreamMode::READ ));
+
+ if( pIStm )
+ {
+ pIStm->SetStreamCharSet(GetSOLoadTextEncoding(eCharSet));
+
+ char cRTF[5];
+ cRTF[4] = 0;
+ pIStm->ReadBytes(cRTF, 5);
+
+ bool bRTF = cRTF[0] == '{' && cRTF[1] == '\\' && cRTF[2] == 'r' && cRTF[3] == 't' && cRTF[4] == 'f';
+
+ pIStm->Seek(0);
+
+ if( !pIStm->GetError() )
+ {
+ SetText( *pIStm, aFileURL.GetMainURL( INetURLObject::DecodeMechanism::NONE ), bRTF ? EETextFormat::Rtf : EETextFormat::Text );
+ bRet = true;
+ }
+ }
+
+ return bRet;
+}
+
+ImpSdrObjTextLinkUserData* SdrTextObj::GetLinkUserData() const
+{
+ sal_uInt16 nCount=GetUserDataCount();
+ for (sal_uInt16 nNum=nCount; nNum>0;) {
+ nNum--;
+ SdrObjUserData * pData=GetUserData(nNum);
+ if (pData->GetInventor() == SdrInventor::Default
+ && pData->GetId() == SDRUSERDATA_OBJTEXTLINK)
+ {
+ return static_cast<ImpSdrObjTextLinkUserData *>(pData);
+ }
+ }
+ return nullptr;
+}
+
+void SdrTextObj::ImpRegisterLink()
+{
+ ImpSdrObjTextLinkUserData* pData=GetLinkUserData();
+ sfx2::LinkManager* pLinkManager(getSdrModelFromSdrObject().GetLinkManager());
+ if (pLinkManager!=nullptr && pData!=nullptr && pData->mpLink==nullptr) { // don't register twice
+ pData->mpLink = new ImpSdrObjTextLink(this);
+ pLinkManager->InsertFileLink(*pData->mpLink,sfx2::SvBaseLinkObjectType::ClientFile,pData->maFileName,
+ !pData->maFilterName.isEmpty() ?
+ &pData->maFilterName : nullptr);
+ }
+}
+
+void SdrTextObj::ImpDeregisterLink()
+{
+ ImpSdrObjTextLinkUserData* pData=GetLinkUserData();
+ sfx2::LinkManager* pLinkManager(getSdrModelFromSdrObject().GetLinkManager());
+ if (pLinkManager!=nullptr && pData!=nullptr && pData->mpLink!=nullptr) { // don't register twice
+ // when doing Remove, *pLink is deleted implicitly
+ pLinkManager->Remove( pData->mpLink.get() );
+ pData->mpLink=nullptr;
+ }
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/svdraw/svdotxtr.cxx b/svx/source/svdraw/svdotxtr.cxx
new file mode 100644
index 000000000..7da067468
--- /dev/null
+++ b/svx/source/svdraw/svdotxtr.cxx
@@ -0,0 +1,496 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * 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/svdtrans.hxx>
+#include <svx/svdogrp.hxx>
+#include <svx/svdopath.hxx>
+#include <svx/svdpage.hxx>
+#include <svx/svdmodel.hxx>
+#include <sdr/properties/itemsettools.hxx>
+#include <svx/sdr/properties/properties.hxx>
+#include <basegfx/polygon/b2dpolypolygontools.hxx>
+#include <svl/itemset.hxx>
+#include <drawinglayer/processor2d/textaspolygonextractor2d.hxx>
+#include <svx/sdr/contact/viewcontact.hxx>
+#include <svx/xfillit0.hxx>
+#include <svx/xflclit.hxx>
+#include <svx/xlineit0.hxx>
+#include <svx/xlnclit.hxx>
+#include <svx/xlnwtit.hxx>
+#include <svx/sdshitm.hxx>
+
+using namespace com::sun::star;
+
+void SdrTextObj::NbcSetSnapRect(const tools::Rectangle& rRect)
+{
+ if (maGeo.nRotationAngle || maGeo.nShearAngle)
+ {
+ // Either the rotation or shear angle exists.
+ tools::Rectangle aSR0(GetSnapRect());
+ tools::Long nWdt0=aSR0.Right()-aSR0.Left();
+ tools::Long nHgt0=aSR0.Bottom()-aSR0.Top();
+ tools::Long nWdt1=rRect.Right()-rRect.Left();
+ tools::Long nHgt1=rRect.Bottom()-rRect.Top();
+ SdrTextObj::NbcResize(maSnapRect.TopLeft(),Fraction(nWdt1,nWdt0),Fraction(nHgt1,nHgt0));
+ SdrTextObj::NbcMove(Size(rRect.Left()-aSR0.Left(),rRect.Top()-aSR0.Top()));
+ }
+ else
+ {
+ // No rotation or shear.
+
+ maRect = rRect;
+ ImpJustifyRect(maRect);
+
+ AdaptTextMinSize();
+
+ ImpCheckShear();
+ SetBoundAndSnapRectsDirty();
+ }
+}
+
+const tools::Rectangle& SdrTextObj::GetLogicRect() const
+{
+ return maRect;
+}
+
+void SdrTextObj::NbcSetLogicRect(const tools::Rectangle& rRect)
+{
+ maRect = rRect;
+ ImpJustifyRect(maRect);
+
+ AdaptTextMinSize();
+
+ SetBoundAndSnapRectsDirty();
+}
+
+Degree100 SdrTextObj::GetRotateAngle() const
+{
+ return maGeo.nRotationAngle;
+}
+
+Degree100 SdrTextObj::GetShearAngle(bool /*bVertical*/) const
+{
+ return maGeo.nShearAngle;
+}
+
+void SdrTextObj::NbcMove(const Size& rSiz)
+{
+ maRect.Move(rSiz);
+ m_aOutRect.Move(rSiz);
+ maSnapRect.Move(rSiz);
+ SetBoundAndSnapRectsDirty(true);
+}
+
+void SdrTextObj::NbcResize(const Point& rRef, const Fraction& xFact, const Fraction& yFact)
+{
+ bool bNotSheared=maGeo.nShearAngle==0_deg100;
+ bool bRotate90=bNotSheared && maGeo.nRotationAngle.get() % 9000 ==0;
+ bool bXMirr=(xFact.GetNumerator()<0) != (xFact.GetDenominator()<0);
+ bool bYMirr=(yFact.GetNumerator()<0) != (yFact.GetDenominator()<0);
+ if (bXMirr || bYMirr) {
+ Point aRef1(GetSnapRect().Center());
+ if (bXMirr) {
+ Point aRef2(aRef1);
+ aRef2.AdjustY( 1 );
+ NbcMirrorGluePoints(aRef1,aRef2);
+ }
+ if (bYMirr) {
+ Point aRef2(aRef1);
+ aRef2.AdjustX( 1 );
+ NbcMirrorGluePoints(aRef1,aRef2);
+ }
+ }
+
+ if (maGeo.nRotationAngle==0_deg100 && maGeo.nShearAngle==0_deg100) {
+ ResizeRect(maRect,rRef,xFact,yFact);
+ if (bYMirr) {
+ maRect.Justify();
+ maRect.Move(maRect.Right()-maRect.Left(),maRect.Bottom()-maRect.Top());
+ maGeo.nRotationAngle=18000_deg100;
+ maGeo.RecalcSinCos();
+ }
+ }
+ else
+ {
+ tools::Polygon aPol(Rect2Poly(maRect,maGeo));
+
+ for(sal_uInt16 a(0); a < aPol.GetSize(); a++)
+ {
+ ResizePoint(aPol[a], rRef, xFact, yFact);
+ }
+
+ if(bXMirr != bYMirr)
+ {
+ // turn polygon and move it a little
+ tools::Polygon aPol0(aPol);
+
+ aPol[0] = aPol0[1];
+ aPol[1] = aPol0[0];
+ aPol[2] = aPol0[3];
+ aPol[3] = aPol0[2];
+ aPol[4] = aPol0[1];
+ }
+
+ Poly2Rect(aPol, maRect, maGeo);
+ }
+
+ if (bRotate90) {
+ bool bRota90=maGeo.nRotationAngle.get() % 9000 ==0;
+ if (!bRota90) { // there's seems to be a rounding error occurring: correct it
+ Degree100 a=NormAngle36000(maGeo.nRotationAngle);
+ if (a<4500_deg100) a=0_deg100;
+ else if (a<13500_deg100) a=9000_deg100;
+ else if (a<22500_deg100) a=18000_deg100;
+ else if (a<31500_deg100) a=27000_deg100;
+ else a=0_deg100;
+ maGeo.nRotationAngle=a;
+ maGeo.RecalcSinCos();
+ }
+ if (bNotSheared!=(maGeo.nShearAngle==0_deg100)) { // correct a rounding error occurring with Shear
+ maGeo.nShearAngle=0_deg100;
+ maGeo.RecalcTan();
+ }
+ }
+
+ ImpJustifyRect(maRect);
+
+ AdaptTextMinSize();
+
+ if(mbTextFrame && !getSdrModelFromSdrObject().IsPasteResize())
+ {
+ NbcAdjustTextFrameWidthAndHeight();
+ }
+
+ ImpCheckShear();
+ SetBoundAndSnapRectsDirty();
+}
+
+void SdrTextObj::NbcRotate(const Point& rRef, Degree100 nAngle, double sn, double cs)
+{
+ SetGlueReallyAbsolute(true);
+ tools::Long dx=maRect.Right()-maRect.Left();
+ tools::Long dy=maRect.Bottom()-maRect.Top();
+ Point aP(maRect.TopLeft());
+ RotatePoint(aP,rRef,sn,cs);
+ maRect.SetLeft(aP.X() );
+ maRect.SetTop(aP.Y() );
+ maRect.SetRight(maRect.Left()+dx );
+ maRect.SetBottom(maRect.Top()+dy );
+ if (maGeo.nRotationAngle==0_deg100) {
+ maGeo.nRotationAngle=NormAngle36000(nAngle);
+ maGeo.mfSinRotationAngle=sn;
+ maGeo.mfCosRotationAngle=cs;
+ } else {
+ maGeo.nRotationAngle=NormAngle36000(maGeo.nRotationAngle+nAngle);
+ maGeo.RecalcSinCos();
+ }
+ SetBoundAndSnapRectsDirty();
+ NbcRotateGluePoints(rRef,nAngle,sn,cs);
+ SetGlueReallyAbsolute(false);
+}
+
+void SdrTextObj::NbcShear(const Point& rRef, Degree100 /*nAngle*/, double tn, bool bVShear)
+{
+ SetGlueReallyAbsolute(true);
+
+ // when this is a SdrPathObj, aRect may be uninitialized
+ tools::Polygon aPol(Rect2Poly(maRect.IsEmpty() ? GetSnapRect() : maRect, maGeo));
+
+ sal_uInt16 nPointCount=aPol.GetSize();
+ for (sal_uInt16 i=0; i<nPointCount; i++) {
+ ShearPoint(aPol[i],rRef,tn,bVShear);
+ }
+ Poly2Rect(aPol,maRect,maGeo);
+ ImpJustifyRect(maRect);
+ if (mbTextFrame) {
+ NbcAdjustTextFrameWidthAndHeight();
+ }
+ ImpCheckShear();
+ SetBoundAndSnapRectsDirty();
+ NbcShearGluePoints(rRef,tn,bVShear);
+ SetGlueReallyAbsolute(false);
+}
+
+void SdrTextObj::NbcMirror(const Point& rRef1, const Point& rRef2)
+{
+ SetGlueReallyAbsolute(true);
+ bool bNotSheared=maGeo.nShearAngle==0_deg100;
+ bool bRotate90 = false;
+ if (bNotSheared &&
+ (rRef1.X()==rRef2.X() || rRef1.Y()==rRef2.Y() ||
+ std::abs(rRef1.X()-rRef2.X())==std::abs(rRef1.Y()-rRef2.Y()))) {
+ bRotate90=maGeo.nRotationAngle.get() % 9000 ==0;
+ }
+ tools::Polygon aPol(Rect2Poly(maRect,maGeo));
+ sal_uInt16 i;
+ sal_uInt16 nPointCount=aPol.GetSize();
+ for (i=0; i<nPointCount; i++) {
+ MirrorPoint(aPol[i],rRef1,rRef2);
+ }
+ // turn polygon and move it a little
+ tools::Polygon aPol0(aPol);
+ aPol[0]=aPol0[1];
+ aPol[1]=aPol0[0];
+ aPol[2]=aPol0[3];
+ aPol[3]=aPol0[2];
+ aPol[4]=aPol0[1];
+ Poly2Rect(aPol,maRect,maGeo);
+
+ if (bRotate90) {
+ bool bRota90=maGeo.nRotationAngle.get() % 9000 ==0;
+ if (bRotate90 && !bRota90) { // there's seems to be a rounding error occurring: correct it
+ Degree100 a=NormAngle36000(maGeo.nRotationAngle);
+ if (a<4500_deg100) a=0_deg100;
+ else if (a<13500_deg100) a=9000_deg100;
+ else if (a<22500_deg100) a=18000_deg100;
+ else if (a<31500_deg100) a=27000_deg100;
+ else a=0_deg100;
+ maGeo.nRotationAngle=a;
+ maGeo.RecalcSinCos();
+ }
+ }
+ if (bNotSheared!=(maGeo.nShearAngle==0_deg100)) { // correct a rounding error occurring with Shear
+ maGeo.nShearAngle=0_deg100;
+ maGeo.RecalcTan();
+ }
+
+ ImpJustifyRect(maRect);
+ if (mbTextFrame) {
+ NbcAdjustTextFrameWidthAndHeight();
+ }
+ ImpCheckShear();
+ SetBoundAndSnapRectsDirty();
+ NbcMirrorGluePoints(rRef1,rRef2);
+ SetGlueReallyAbsolute(false);
+}
+
+
+SdrObjectUniquePtr SdrTextObj::ImpConvertContainedTextToSdrPathObjs(bool bToPoly) const
+{
+ SdrObjectUniquePtr pRetval;
+
+ if(!ImpCanConvTextToCurve())
+ {
+ // suppress HelpTexts from PresObj's
+ return nullptr;
+ }
+
+ // create an extractor with neutral ViewInformation
+ const drawinglayer::geometry::ViewInformation2D aViewInformation2D;
+ drawinglayer::processor2d::TextAsPolygonExtractor2D aExtractor(aViewInformation2D);
+
+ // extract text as polygons
+ GetViewContact().getViewIndependentPrimitive2DContainer(aExtractor);
+
+ // get results
+ const drawinglayer::processor2d::TextAsPolygonDataNodeVector& rResult = aExtractor.getTarget();
+ const sal_uInt32 nResultCount(rResult.size());
+
+ if(nResultCount)
+ {
+ // prepare own target
+ SdrObjGroup* pGroup = new SdrObjGroup(getSdrModelFromSdrObject());
+ SdrObjList* pObjectList = pGroup->GetSubList();
+
+ // process results
+ for(sal_uInt32 a(0); a < nResultCount; a++)
+ {
+ const drawinglayer::processor2d::TextAsPolygonDataNode& rCandidate = rResult[a];
+ basegfx::B2DPolyPolygon aPolyPolygon(rCandidate.getB2DPolyPolygon());
+
+ if(aPolyPolygon.count())
+ {
+ // take care of wanted polygon type
+ if(bToPoly)
+ {
+ if(aPolyPolygon.areControlPointsUsed())
+ {
+ aPolyPolygon = basegfx::utils::adaptiveSubdivideByAngle(aPolyPolygon);
+ }
+ }
+ else
+ {
+ if(!aPolyPolygon.areControlPointsUsed())
+ {
+ aPolyPolygon = basegfx::utils::expandToCurve(aPolyPolygon);
+ }
+ }
+
+ // create ItemSet with object attributes
+ SfxItemSet aAttributeSet(GetObjectItemSet());
+ SdrPathObj* pPathObj = nullptr;
+
+ // always clear objectshadow; this is included in the extraction
+ aAttributeSet.Put(makeSdrShadowItem(false));
+
+ if(rCandidate.getIsFilled())
+ {
+ // set needed items
+ aAttributeSet.Put(XFillColorItem(OUString(), Color(rCandidate.getBColor())));
+ aAttributeSet.Put(XLineStyleItem(drawing::LineStyle_NONE));
+ aAttributeSet.Put(XFillStyleItem(drawing::FillStyle_SOLID));
+
+ // create filled SdrPathObj
+ pPathObj = new SdrPathObj(
+ getSdrModelFromSdrObject(),
+ SdrObjKind::PathFill,
+ aPolyPolygon);
+ }
+ else
+ {
+ // set needed items
+ aAttributeSet.Put(XLineColorItem(OUString(), Color(rCandidate.getBColor())));
+ aAttributeSet.Put(XLineStyleItem(drawing::LineStyle_SOLID));
+ aAttributeSet.Put(XLineWidthItem(0));
+ aAttributeSet.Put(XFillStyleItem(drawing::FillStyle_NONE));
+
+ // create line SdrPathObj
+ pPathObj = new SdrPathObj(
+ getSdrModelFromSdrObject(),
+ SdrObjKind::PathLine,
+ aPolyPolygon);
+ }
+
+ // copy basic information from original
+ pPathObj->ImpSetAnchorPos(GetAnchorPos());
+ pPathObj->NbcSetLayer(GetLayer());
+ pPathObj->NbcSetStyleSheet(GetStyleSheet(), true);
+
+ // apply prepared ItemSet and add to target
+ pPathObj->SetMergedItemSet(aAttributeSet);
+ pObjectList->InsertObject(pPathObj);
+ }
+ }
+
+ // postprocess; if no result and/or only one object, simplify
+ if(!pObjectList->GetObjCount())
+ {
+ // always use SdrObject::Free(...) for SdrObjects (!)
+ SdrObject* pTemp(pGroup);
+ SdrObject::Free(pTemp);
+ }
+ else if(1 == pObjectList->GetObjCount())
+ {
+ pRetval.reset(pObjectList->RemoveObject(0));
+
+ // always use SdrObject::Free(...) for SdrObjects (!)
+ SdrObject* pTemp(pGroup);
+ SdrObject::Free(pTemp);
+ }
+ else
+ {
+ pRetval.reset(pGroup);
+ }
+ }
+
+ return pRetval;
+}
+
+
+SdrObjectUniquePtr SdrTextObj::DoConvertToPolyObj(bool bBezier, bool bAddText) const
+{
+ if(bAddText)
+ {
+ return ImpConvertContainedTextToSdrPathObjs(!bBezier);
+ }
+
+ return nullptr;
+}
+
+bool SdrTextObj::ImpCanConvTextToCurve() const
+{
+ return !IsOutlText();
+}
+
+SdrPathObjUniquePtr SdrTextObj::ImpConvertMakeObj(const basegfx::B2DPolyPolygon& rPolyPolygon, bool bClosed, bool bBezier) const
+{
+ SdrObjKind ePathKind = bClosed ? SdrObjKind::PathFill : SdrObjKind::PathLine;
+ basegfx::B2DPolyPolygon aB2DPolyPolygon(rPolyPolygon);
+
+ // #i37011#
+ if(!bBezier)
+ {
+ aB2DPolyPolygon = basegfx::utils::adaptiveSubdivideByAngle(aB2DPolyPolygon);
+ ePathKind = bClosed ? SdrObjKind::Polygon : SdrObjKind::PolyLine;
+ }
+
+ SdrPathObjUniquePtr pPathObj(new SdrPathObj(
+ getSdrModelFromSdrObject(),
+ ePathKind,
+ aB2DPolyPolygon));
+
+ if(bBezier)
+ {
+ // create bezier curves
+ pPathObj->SetPathPoly(basegfx::utils::expandToCurve(pPathObj->GetPathPoly()));
+ }
+
+ pPathObj->ImpSetAnchorPos(m_aAnchor);
+ pPathObj->NbcSetLayer(GetLayer());
+ sdr::properties::ItemChangeBroadcaster aC(*pPathObj);
+ pPathObj->ClearMergedItem();
+ pPathObj->SetMergedItemSet(GetObjectItemSet());
+ pPathObj->GetProperties().BroadcastItemChange(aC);
+ pPathObj->NbcSetStyleSheet(GetStyleSheet(), true);
+
+ return pPathObj;
+}
+
+SdrObjectUniquePtr SdrTextObj::ImpConvertAddText(SdrObjectUniquePtr pObj, bool bBezier) const
+{
+ if(!ImpCanConvTextToCurve())
+ {
+ return pObj;
+ }
+
+ SdrObjectUniquePtr pText = ImpConvertContainedTextToSdrPathObjs(!bBezier);
+
+ if(!pText)
+ {
+ return pObj;
+ }
+
+ if(!pObj)
+ {
+ return pText;
+ }
+
+ if(pText->IsGroupObject())
+ {
+ // is already group object, add partial shape in front
+ SdrObjList* pOL=pText->GetSubList();
+ pOL->InsertObject(pObj.release(),0);
+
+ return pText;
+ }
+ else
+ {
+ // not yet a group, create one and add partial and new shapes
+ std::unique_ptr<SdrObjGroup, SdrObjectFreeOp> pGrp(new SdrObjGroup(getSdrModelFromSdrObject()));
+ SdrObjList* pOL=pGrp->GetSubList();
+ pOL->InsertObject(pObj.release());
+ pOL->InsertObject(pText.release());
+
+ return pGrp;
+ }
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/svdraw/svdouno.cxx b/svx/source/svdraw/svdouno.cxx
new file mode 100644
index 000000000..4f7066726
--- /dev/null
+++ b/svx/source/svdraw/svdouno.cxx
@@ -0,0 +1,502 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <sdr/contact/viewcontactofunocontrol.hxx>
+#include <sdr/contact/viewobjectcontactofunocontrol.hxx>
+#include <com/sun/star/container/XChild.hpp>
+#include <com/sun/star/beans/XPropertySet.hpp>
+#include <com/sun/star/util/XCloneable.hpp>
+#include <com/sun/star/uno/XComponentContext.hpp>
+#include <comphelper/processfactory.hxx>
+#include <svx/svdouno.hxx>
+#include <svx/svdpagv.hxx>
+#include <svx/svdmodel.hxx>
+#include <svx/dialmgr.hxx>
+#include <svx/strings.hrc>
+#include <svx/svdview.hxx>
+#include <svx/svdorect.hxx>
+#include <svx/svdviter.hxx>
+#include <rtl/ref.hxx>
+#include <svx/sdrpagewindow.hxx>
+#include <tools/diagnose_ex.h>
+#include <tools/debug.hxx>
+#include <o3tl/sorted_vector.hxx>
+
+using namespace ::com::sun::star;
+using namespace sdr::contact;
+
+
+// Defines
+
+
+// Helper class SdrControlEventListenerImpl
+
+#include <com/sun/star/lang/XEventListener.hpp>
+
+#include <cppuhelper/implbase.hxx>
+
+
+class SdrControlEventListenerImpl : public ::cppu::WeakImplHelper< css::lang::XEventListener >
+{
+protected:
+ SdrUnoObj* pObj;
+
+public:
+ explicit SdrControlEventListenerImpl(SdrUnoObj* _pObj)
+ : pObj(_pObj)
+ {}
+
+ // XEventListener
+ virtual void SAL_CALL disposing( const css::lang::EventObject& Source ) override;
+
+ void StopListening(const uno::Reference< lang::XComponent >& xComp);
+ void StartListening(const uno::Reference< lang::XComponent >& xComp);
+};
+
+// XEventListener
+void SAL_CALL SdrControlEventListenerImpl::disposing( const css::lang::EventObject& /*Source*/)
+{
+ if (pObj)
+ {
+ pObj->xUnoControlModel = nullptr;
+ }
+}
+
+void SdrControlEventListenerImpl::StopListening(const uno::Reference< lang::XComponent >& xComp)
+{
+ if (xComp.is())
+ xComp->removeEventListener(this);
+}
+
+void SdrControlEventListenerImpl::StartListening(const uno::Reference< lang::XComponent >& xComp)
+{
+ if (xComp.is())
+ xComp->addEventListener(this);
+}
+
+
+struct SdrUnoObjDataHolder
+{
+ mutable ::rtl::Reference< SdrControlEventListenerImpl >
+ pEventListener;
+};
+
+
+namespace
+{
+ void lcl_ensureControlVisibility( SdrView const * _pView, const SdrUnoObj* _pObject, bool _bVisible )
+ {
+ OSL_PRECOND( _pObject, "lcl_ensureControlVisibility: no object -> no survival!" );
+
+ SdrPageView* pPageView = _pView ? _pView->GetSdrPageView() : nullptr;
+ DBG_ASSERT( pPageView, "lcl_ensureControlVisibility: no view found!" );
+ if ( !pPageView )
+ return;
+
+ ViewContact& rUnoControlContact( _pObject->GetViewContact() );
+
+ for ( sal_uInt32 i = 0; i < pPageView->PageWindowCount(); ++i )
+ {
+ SdrPageWindow* pPageWindow = pPageView->GetPageWindow( i );
+ DBG_ASSERT( pPageWindow, "lcl_ensureControlVisibility: invalid PageViewWindow!" );
+ if ( !pPageWindow )
+ continue;
+
+ if ( !pPageWindow->HasObjectContact() )
+ continue;
+
+ ObjectContact& rPageViewContact( pPageWindow->GetObjectContact() );
+ const ViewObjectContact& rViewObjectContact( rUnoControlContact.GetViewObjectContact( rPageViewContact ) );
+ const ViewObjectContactOfUnoControl* pUnoControlContact = dynamic_cast< const ViewObjectContactOfUnoControl* >( &rViewObjectContact );
+ DBG_ASSERT( pUnoControlContact, "lcl_ensureControlVisibility: wrong ViewObjectContact type!" );
+ if ( !pUnoControlContact )
+ continue;
+
+ pUnoControlContact->ensureControlVisibility( _bVisible );
+ }
+ }
+}
+
+SdrUnoObj::SdrUnoObj(
+ SdrModel& rSdrModel,
+ const OUString& rModelName)
+: SdrRectObj(rSdrModel),
+ m_pImpl( new SdrUnoObjDataHolder )
+{
+ m_bIsUnoObj = true;
+
+ m_pImpl->pEventListener = new SdrControlEventListenerImpl(this);
+
+ // only an owner may create independently
+ if (!rModelName.isEmpty())
+ CreateUnoControlModel(rModelName);
+}
+
+SdrUnoObj::SdrUnoObj( SdrModel& rSdrModel, SdrUnoObj const & rSource)
+: SdrRectObj(rSdrModel, rSource),
+ m_pImpl( new SdrUnoObjDataHolder )
+{
+ m_bIsUnoObj = true;
+
+ m_pImpl->pEventListener = new SdrControlEventListenerImpl(this);
+
+ aUnoControlModelTypeName = rSource.aUnoControlModelTypeName;
+ aUnoControlTypeName = rSource.aUnoControlTypeName;
+
+ // copy the uno control model
+ const uno::Reference< awt::XControlModel > xSourceControlModel = rSource.GetUnoControlModel();
+ if ( xSourceControlModel.is() )
+ {
+ try
+ {
+ uno::Reference< util::XCloneable > xClone( xSourceControlModel, uno::UNO_QUERY_THROW );
+ xUnoControlModel.set( xClone->createClone(), uno::UNO_QUERY_THROW );
+ }
+ catch( const uno::Exception& )
+ {
+ DBG_UNHANDLED_EXCEPTION("svx");
+ }
+ }
+
+ // get service name of the control from the control model
+ uno::Reference< beans::XPropertySet > xSet(xUnoControlModel, uno::UNO_QUERY);
+ if (xSet.is())
+ {
+ uno::Any aValue( xSet->getPropertyValue("DefaultControl") );
+ OUString aStr;
+
+ if( aValue >>= aStr )
+ aUnoControlTypeName = aStr;
+ }
+
+ uno::Reference< lang::XComponent > xComp(xUnoControlModel, uno::UNO_QUERY);
+ if (xComp.is())
+ m_pImpl->pEventListener->StartListening(xComp);
+}
+
+SdrUnoObj::SdrUnoObj(
+ SdrModel& rSdrModel,
+ const OUString& rModelName,
+ const uno::Reference< lang::XMultiServiceFactory >& rxSFac)
+: SdrRectObj(rSdrModel),
+ m_pImpl( new SdrUnoObjDataHolder )
+{
+ m_bIsUnoObj = true;
+
+ m_pImpl->pEventListener = new SdrControlEventListenerImpl(this);
+
+ // only an owner may create independently
+ if (!rModelName.isEmpty())
+ CreateUnoControlModel(rModelName,rxSFac);
+}
+
+SdrUnoObj::~SdrUnoObj()
+{
+ try
+ {
+ // clean up the control model
+ uno::Reference< lang::XComponent > xComp(xUnoControlModel, uno::UNO_QUERY);
+ if (xComp.is())
+ {
+ // is the control model owned by its environment?
+ uno::Reference< container::XChild > xContent(xUnoControlModel, uno::UNO_QUERY);
+ if (xContent.is() && !xContent->getParent().is())
+ xComp->dispose();
+ else
+ m_pImpl->pEventListener->StopListening(xComp);
+ }
+ }
+ catch( const uno::Exception& )
+ {
+ TOOLS_WARN_EXCEPTION( "svx", "SdrUnoObj::~SdrUnoObj" );
+ }
+}
+
+void SdrUnoObj::TakeObjInfo(SdrObjTransformInfoRec& rInfo) const
+{
+ rInfo.bRotateFreeAllowed = false;
+ rInfo.bRotate90Allowed = false;
+ rInfo.bMirrorFreeAllowed = false;
+ rInfo.bMirror45Allowed = false;
+ rInfo.bMirror90Allowed = false;
+ rInfo.bTransparenceAllowed = false;
+ rInfo.bShearAllowed = false;
+ rInfo.bEdgeRadiusAllowed = false;
+ rInfo.bNoOrthoDesired = false;
+ rInfo.bCanConvToPath = false;
+ rInfo.bCanConvToPoly = false;
+ rInfo.bCanConvToPathLineToArea = false;
+ rInfo.bCanConvToPolyLineToArea = false;
+ rInfo.bCanConvToContour = false;
+}
+
+SdrObjKind SdrUnoObj::GetObjIdentifier() const
+{
+ return SdrObjKind::UNO;
+}
+
+void SdrUnoObj::SetContextWritingMode( const sal_Int16 _nContextWritingMode )
+{
+ try
+ {
+ uno::Reference< beans::XPropertySet > xModelProperties( GetUnoControlModel(), uno::UNO_QUERY_THROW );
+ xModelProperties->setPropertyValue( "ContextWritingMode", uno::Any( _nContextWritingMode ) );
+ }
+ catch( const uno::Exception& )
+ {
+ DBG_UNHANDLED_EXCEPTION("svx");
+ }
+}
+
+OUString SdrUnoObj::TakeObjNameSingul() const
+{
+ OUString sName(SvxResId(STR_ObjNameSingulUno));
+
+ OUString aName(GetName());
+ if (!aName.isEmpty())
+ sName += " '" + aName + "'";
+
+ return sName;
+}
+
+OUString SdrUnoObj::TakeObjNamePlural() const
+{
+ return SvxResId(STR_ObjNamePluralUno);
+}
+
+SdrUnoObj* SdrUnoObj::CloneSdrObject(SdrModel& rTargetModel) const
+{
+ return new SdrUnoObj(rTargetModel, *this);
+}
+
+void SdrUnoObj::NbcResize(const Point& rRef, const Fraction& xFact, const Fraction& yFact)
+{
+ SdrRectObj::NbcResize(rRef,xFact,yFact);
+
+ if (maGeo.nShearAngle==0_deg100 && maGeo.nRotationAngle==0_deg100)
+ return;
+
+ // small correctors
+ if (maGeo.nRotationAngle>=9000_deg100 && maGeo.nRotationAngle<27000_deg100)
+ {
+ maRect.Move(maRect.Left()-maRect.Right(),maRect.Top()-maRect.Bottom());
+ }
+
+ maGeo.nRotationAngle = 0_deg100;
+ maGeo.nShearAngle = 0_deg100;
+ maGeo.mfSinRotationAngle = 0.0;
+ maGeo.mfCosRotationAngle = 1.0;
+ maGeo.mfTanShearAngle = 0.0;
+ SetBoundAndSnapRectsDirty();
+}
+
+
+bool SdrUnoObj::hasSpecialDrag() const
+{
+ // no special drag; we have no rounding rect and
+ // do want frame handles
+ return false;
+}
+
+void SdrUnoObj::NbcSetLayer( SdrLayerID _nLayer )
+{
+ if ( GetLayer() == _nLayer )
+ { // redundant call -> not interested in doing anything here
+ SdrRectObj::NbcSetLayer( _nLayer );
+ return;
+ }
+
+ // we need some special handling here in case we're moved from an invisible layer
+ // to a visible one, or vice versa
+ // (relative to a layer. Remember that the visibility of a layer is a view attribute
+ // - the same layer can be visible in one view, and invisible in another view, at the
+ // same time)
+
+ // collect all views in which our old layer is visible
+ o3tl::sorted_vector< SdrView* > aPreviouslyVisible;
+
+ {
+ SdrViewIter aIter( this );
+ for ( SdrView* pView = aIter.FirstView(); pView; pView = aIter.NextView() )
+ aPreviouslyVisible.insert( pView );
+ }
+
+ SdrRectObj::NbcSetLayer( _nLayer );
+
+ // collect all views in which our new layer is visible
+ o3tl::sorted_vector< SdrView* > aNewlyVisible;
+
+ {
+ SdrViewIter aIter( this );
+ for ( SdrView* pView = aIter.FirstView(); pView; pView = aIter.NextView() )
+ {
+ if ( aPreviouslyVisible.erase(pView) == 0 )
+ {
+ // in pView, we were visible _before_ the layer change, and are
+ // _not_ visible after the layer change
+ // => remember this view, as our visibility there changed
+ aNewlyVisible.insert( pView );
+ }
+ }
+ }
+
+ // now aPreviouslyVisible contains all views where we became invisible
+ for (const auto& rpView : aPreviouslyVisible)
+ {
+ lcl_ensureControlVisibility( rpView, this, false );
+ }
+
+ // and aNewlyVisible all views where we became visible
+ for (const auto& rpView : aNewlyVisible)
+ {
+ lcl_ensureControlVisibility( rpView, this, true );
+ }
+}
+
+void SdrUnoObj::CreateUnoControlModel(const OUString& rModelName)
+{
+ DBG_ASSERT(!xUnoControlModel.is(), "model already exists");
+
+ aUnoControlModelTypeName = rModelName;
+
+ uno::Reference< awt::XControlModel > xModel;
+ uno::Reference< uno::XComponentContext > xContext( ::comphelper::getProcessComponentContext() );
+ if (!aUnoControlModelTypeName.isEmpty() )
+ {
+ xModel.set(xContext->getServiceManager()->createInstanceWithContext(
+ aUnoControlModelTypeName, xContext), uno::UNO_QUERY);
+
+ if (xModel.is())
+ SetChanged();
+ }
+
+ SetUnoControlModel(xModel);
+}
+
+void SdrUnoObj::CreateUnoControlModel(const OUString& rModelName,
+ const uno::Reference< lang::XMultiServiceFactory >& rxSFac)
+{
+ DBG_ASSERT(!xUnoControlModel.is(), "model already exists");
+
+ aUnoControlModelTypeName = rModelName;
+
+ uno::Reference< awt::XControlModel > xModel;
+ if (!aUnoControlModelTypeName.isEmpty() && rxSFac.is() )
+ {
+ xModel.set(rxSFac->createInstance(aUnoControlModelTypeName), uno::UNO_QUERY);
+
+ if (xModel.is())
+ SetChanged();
+ }
+
+ SetUnoControlModel(xModel);
+}
+
+void SdrUnoObj::SetUnoControlModel( const uno::Reference< awt::XControlModel >& xModel)
+{
+ if (xUnoControlModel.is())
+ {
+ uno::Reference< lang::XComponent > xComp(xUnoControlModel, uno::UNO_QUERY);
+ if (xComp.is())
+ m_pImpl->pEventListener->StopListening(xComp);
+ }
+
+ xUnoControlModel = xModel;
+
+ // control model has to contain service name of the control
+ if (xUnoControlModel.is())
+ {
+ uno::Reference< beans::XPropertySet > xSet(xUnoControlModel, uno::UNO_QUERY);
+ if (xSet.is())
+ {
+ uno::Any aValue( xSet->getPropertyValue("DefaultControl") );
+ OUString aStr;
+ if( aValue >>= aStr )
+ aUnoControlTypeName = aStr;
+ }
+
+ uno::Reference< lang::XComponent > xComp(xUnoControlModel, uno::UNO_QUERY);
+ if (xComp.is())
+ m_pImpl->pEventListener->StartListening(xComp);
+ }
+
+ // invalidate all ViewObject contacts
+ ViewContactOfUnoControl* pVC = nullptr;
+ if ( impl_getViewContact( pVC ) )
+ {
+ // flushViewObjectContacts() removes all existing VOCs for the local DrawHierarchy. This
+ // is always allowed since they will be re-created on demand (and with the changed model)
+ GetViewContact().flushViewObjectContacts();
+ }
+}
+
+
+uno::Reference< awt::XControl > SdrUnoObj::GetUnoControl(const SdrView& _rView, const OutputDevice& _rOut) const
+{
+ uno::Reference< awt::XControl > xControl;
+
+ SdrPageView* pPageView = _rView.GetSdrPageView();
+ OSL_ENSURE( pPageView && getSdrPageFromSdrObject() == pPageView->GetPage(), "SdrUnoObj::GetUnoControl: This object is not displayed in that particular view!" );
+ if ( !pPageView || getSdrPageFromSdrObject() != pPageView->GetPage() )
+ return nullptr;
+
+ SdrPageWindow* pPageWindow = pPageView->FindPageWindow( _rOut );
+ OSL_ENSURE( pPageWindow, "SdrUnoObj::GetUnoControl: did not find my SdrPageWindow!" );
+ if ( !pPageWindow )
+ return nullptr;
+
+ ViewObjectContact& rViewObjectContact( GetViewContact().GetViewObjectContact( pPageWindow->GetObjectContact() ) );
+ ViewObjectContactOfUnoControl* pUnoContact = dynamic_cast< ViewObjectContactOfUnoControl* >( &rViewObjectContact );
+ OSL_ENSURE( pUnoContact, "SdrUnoObj::GetUnoControl: wrong contact type!" );
+ if ( pUnoContact )
+ xControl = pUnoContact->getControl();
+
+ return xControl;
+}
+
+
+uno::Reference< awt::XControl > SdrUnoObj::GetTemporaryControlForWindow(
+ const vcl::Window& _rWindow, uno::Reference< awt::XControlContainer >& _inout_ControlContainer ) const
+{
+ uno::Reference< awt::XControl > xControl;
+
+ ViewContactOfUnoControl* pVC = nullptr;
+ if ( impl_getViewContact( pVC ) )
+ xControl = pVC->getTemporaryControlForWindow( _rWindow, _inout_ControlContainer );
+
+ return xControl;
+}
+
+
+bool SdrUnoObj::impl_getViewContact( ViewContactOfUnoControl*& _out_rpContact ) const
+{
+ ViewContact& rViewContact( GetViewContact() );
+ _out_rpContact = dynamic_cast< ViewContactOfUnoControl* >( &rViewContact );
+ DBG_ASSERT( _out_rpContact, "SdrUnoObj::impl_getViewContact: could not find my ViewContact!" );
+ return ( _out_rpContact != nullptr );
+}
+
+
+std::unique_ptr<sdr::contact::ViewContact> SdrUnoObj::CreateObjectSpecificViewContact()
+{
+ return std::make_unique<sdr::contact::ViewContactOfUnoControl>( *this );
+}
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/svdraw/svdoutl.cxx b/svx/source/svdraw/svdoutl.cxx
new file mode 100644
index 000000000..3737b1fdf
--- /dev/null
+++ b/svx/source/svdraw/svdoutl.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 <svx/svdoutl.hxx>
+#include <editeng/outliner.hxx>
+#include <svx/svdotext.hxx>
+#include <editeng/editstat.hxx>
+#include <svl/itempool.hxx>
+#include <editeng/editview.hxx>
+
+
+SdrOutliner::SdrOutliner( SfxItemPool* pItemPool, OutlinerMode nMode )
+: Outliner( pItemPool, nMode ),
+ mpVisualizedPage(nullptr)
+{
+}
+
+
+SdrOutliner::~SdrOutliner()
+{
+}
+
+
+void SdrOutliner::SetTextObj( const SdrTextObj* pObj )
+{
+ if( pObj && pObj != mxWeakTextObj.get() )
+ {
+ SetUpdateLayout(false);
+ OutlinerMode nOutlinerMode2 = OutlinerMode::OutlineObject;
+ if ( !pObj->IsOutlText() )
+ nOutlinerMode2 = OutlinerMode::TextObject;
+ Init( nOutlinerMode2 );
+
+ SetGlobalCharStretching();
+
+ EEControlBits nStat = GetControlWord();
+ nStat &= ~EEControlBits( EEControlBits::STRETCHING | EEControlBits::AUTOPAGESIZE );
+ SetControlWord(nStat);
+
+ Size aMaxSize( 100000,100000 );
+ SetMinAutoPaperSize( Size() );
+ SetMaxAutoPaperSize( aMaxSize );
+ SetPaperSize( aMaxSize );
+ SetTextColumns(pObj->GetTextColumnsNumber(), pObj->GetTextColumnsSpacing());
+ ClearPolygon();
+ }
+
+ mxWeakTextObj.reset( const_cast< SdrTextObj* >(pObj) );
+}
+
+void SdrOutliner::SetTextObjNoInit( const SdrTextObj* pObj )
+{
+ mxWeakTextObj.reset( const_cast< SdrTextObj* >(pObj) );
+}
+
+OUString SdrOutliner::CalcFieldValue(const SvxFieldItem& rField, sal_Int32 nPara, sal_Int32 nPos,
+ std::optional<Color>& rpTxtColor, std::optional<Color>& rpFldColor)
+{
+ bool bOk = false;
+ OUString aRet;
+
+ if(mxWeakTextObj.is())
+ bOk = mxWeakTextObj->CalcFieldValue(rField, nPara, nPos, false, rpTxtColor, rpFldColor, aRet);
+
+ if (!bOk)
+ aRet = Outliner::CalcFieldValue(rField, nPara, nPos, rpTxtColor, rpFldColor);
+
+ return aRet;
+}
+
+const SdrTextObj* SdrOutliner::GetTextObj() const
+{
+ return mxWeakTextObj.get();
+}
+
+bool SdrOutliner::hasEditViewCallbacks() const
+{
+ for (size_t a(0); a < GetViewCount(); a++)
+ {
+ OutlinerView* pOutlinerView = GetView(a);
+
+ if (pOutlinerView && pOutlinerView->GetEditView().getEditViewCallbacks())
+ {
+ return true;
+ }
+ }
+
+ return false;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/svdraw/svdoutlinercache.cxx b/svx/source/svdraw/svdoutlinercache.cxx
new file mode 100644
index 000000000..0fc6fc973
--- /dev/null
+++ b/svx/source/svdraw/svdoutlinercache.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 <svdoutlinercache.hxx>
+#include <svx/svdoutl.hxx>
+#include <svx/svdmodel.hxx>
+#include <svx/svdetc.hxx>
+
+SdrOutlinerCache::SdrOutlinerCache( SdrModel* pModel )
+: mpModel( pModel )
+{
+}
+
+std::unique_ptr<SdrOutliner> SdrOutlinerCache::createOutliner( OutlinerMode nOutlinerMode )
+{
+ std::unique_ptr<SdrOutliner> pOutliner;
+
+ if( (OutlinerMode::OutlineObject == nOutlinerMode) && !maModeOutline.empty() )
+ {
+ pOutliner = std::move(maModeOutline.back());
+ maModeOutline.pop_back();
+ }
+ else if( (OutlinerMode::TextObject == nOutlinerMode) && !maModeText.empty() )
+ {
+ pOutliner = std::move(maModeText.back());
+ maModeText.pop_back();
+ }
+ else
+ {
+ pOutliner = SdrMakeOutliner(nOutlinerMode, *mpModel);
+ Outliner& aDrawOutliner = mpModel->GetDrawOutliner();
+ pOutliner->SetCalcFieldValueHdl( aDrawOutliner.GetCalcFieldValueHdl() );
+ maActiveOutliners.insert(pOutliner.get());
+ }
+
+ return pOutliner;
+}
+
+SdrOutlinerCache::~SdrOutlinerCache()
+{
+}
+
+void SdrOutlinerCache::disposeOutliner( std::unique_ptr<SdrOutliner> pOutliner )
+{
+ if( !pOutliner )
+ return;
+
+ OutlinerMode nOutlMode = pOutliner->GetOutlinerMode();
+
+ if( OutlinerMode::OutlineObject == nOutlMode )
+ {
+ pOutliner->Clear();
+ pOutliner->SetVertical( false );
+
+ // Deregister on outliner, might be reused from outliner cache
+ pOutliner->SetNotifyHdl( Link<EENotify&,void>() );
+ maModeOutline.emplace_back(std::move(pOutliner));
+ }
+ else if( OutlinerMode::TextObject == nOutlMode )
+ {
+ pOutliner->Clear();
+ pOutliner->SetVertical( false );
+
+ // Deregister on outliner, might be reused from outliner cache
+ pOutliner->SetNotifyHdl( Link<EENotify&,void>() );
+ maModeText.emplace_back(std::move(pOutliner));
+ }
+ else
+ {
+ maActiveOutliners.erase(pOutliner.get());
+ }
+}
+
+std::vector< SdrOutliner* > SdrOutlinerCache::GetActiveOutliners() const
+{
+ return std::vector< SdrOutliner* >(maActiveOutliners.begin(), maActiveOutliners.end());
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/svdraw/svdovirt.cxx b/svx/source/svdraw/svdovirt.cxx
new file mode 100644
index 000000000..372756cce
--- /dev/null
+++ b/svx/source/svdraw/svdovirt.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/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * 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/svdovirt.hxx>
+#include <svx/svdhdl.hxx>
+#include <svx/sdr/contact/viewcontactofvirtobj.hxx>
+#include <svx/svdograf.hxx>
+#include <svx/svddrgv.hxx>
+#include <basegfx/matrix/b2dhommatrixtools.hxx>
+
+sdr::properties::BaseProperties& SdrVirtObj::GetProperties() const
+{
+ return rRefObj.GetProperties();
+}
+
+
+// #i27224#
+std::unique_ptr<sdr::contact::ViewContact> SdrVirtObj::CreateObjectSpecificViewContact()
+{
+ return std::make_unique<sdr::contact::ViewContactOfVirtObj>(*this);
+}
+
+SdrVirtObj::SdrVirtObj(
+ SdrModel& rSdrModel,
+ SdrObject& rNewObj)
+: SdrObject(rSdrModel),
+ rRefObj(rNewObj)
+{
+ m_bVirtObj=true; // this is only a virtual object
+ rRefObj.AddReference(*this);
+ m_bClosedObj=rRefObj.IsClosedObj();
+}
+
+SdrVirtObj::SdrVirtObj(
+ SdrModel& rSdrModel, SdrVirtObj const & rSource)
+: SdrObject(rSdrModel, rSource),
+ rRefObj(rSource.rRefObj)
+{
+ m_bVirtObj=true; // this is only a virtual object
+ m_bClosedObj=rRefObj.IsClosedObj();
+
+ rRefObj.AddReference(*this);
+
+ aSnapRect = rSource.aSnapRect;
+ m_aAnchor = rSource.m_aAnchor;
+}
+
+SdrVirtObj::~SdrVirtObj()
+{
+ rRefObj.DelReference(*this);
+}
+
+const SdrObject& SdrVirtObj::GetReferencedObj() const
+{
+ return rRefObj;
+}
+
+SdrObject& SdrVirtObj::ReferencedObj()
+{
+ return rRefObj;
+}
+
+void SdrVirtObj::Notify(SfxBroadcaster& /*rBC*/, const SfxHint& /*rHint*/)
+{
+ m_bClosedObj=rRefObj.IsClosedObj();
+ SetBoundAndSnapRectsDirty(); // TODO: Optimize this.
+
+ // Only a repaint here, rRefObj may have changed and broadcasts
+ ActionChanged();
+}
+
+void SdrVirtObj::NbcSetAnchorPos(const Point& rAnchorPos)
+{
+ m_aAnchor=rAnchorPos;
+}
+
+void SdrVirtObj::TakeObjInfo(SdrObjTransformInfoRec& rInfo) const
+{
+ rRefObj.TakeObjInfo(rInfo);
+}
+
+SdrInventor SdrVirtObj::GetObjInventor() const
+{
+ return rRefObj.GetObjInventor();
+}
+
+SdrObjKind SdrVirtObj::GetObjIdentifier() const
+{
+ return rRefObj.GetObjIdentifier();
+}
+
+SdrObjList* SdrVirtObj::GetSubList() const
+{
+ return rRefObj.GetSubList();
+}
+
+void SdrVirtObj::SetName(const OUString& rStr, const bool bSetChanged)
+{
+ return rRefObj.SetName(rStr, bSetChanged);
+}
+
+const OUString & SdrVirtObj::GetName() const
+{
+ return rRefObj.GetName();
+}
+
+void SdrVirtObj::SetTitle(const OUString& rStr)
+{
+ return rRefObj.SetTitle(rStr);
+}
+
+OUString SdrVirtObj::GetTitle() const
+{
+ return rRefObj.GetTitle();
+}
+
+void SdrVirtObj::SetDescription(const OUString& rStr)
+{
+ return rRefObj.SetDescription(rStr);
+}
+
+OUString SdrVirtObj::GetDescription() const
+{
+ return rRefObj.GetDescription();
+}
+
+const tools::Rectangle& SdrVirtObj::GetCurrentBoundRect() const
+{
+ m_aOutRect = rRefObj.GetCurrentBoundRect(); // TODO: Optimize this.
+ m_aOutRect += m_aAnchor;
+ return m_aOutRect;
+}
+
+const tools::Rectangle& SdrVirtObj::GetLastBoundRect() const
+{
+ m_aOutRect = rRefObj.GetLastBoundRect(); // TODO: Optimize this.
+ m_aOutRect += m_aAnchor;
+ return m_aOutRect;
+}
+
+void SdrVirtObj::RecalcBoundRect()
+{
+ m_aOutRect=rRefObj.GetCurrentBoundRect();
+ m_aOutRect+=m_aAnchor;
+}
+
+SdrVirtObj* SdrVirtObj::CloneSdrObject(SdrModel& rTargetModel) const
+{
+ return new SdrVirtObj(rTargetModel, *this);
+ // TTTT not sure if the above works - how could SdrObjFactory::MakeNewObject
+ // create an object with correct rRefObj (?) OTOH VirtObj probably needs not
+ // to be cloned ever - only used in Writer for multiple instances e.g. Header/Footer
+ // return new SdrVirtObj(
+ // getSdrModelFromSdrObject(),
+ // rRefObj); // only a further reference
+}
+
+OUString SdrVirtObj::TakeObjNameSingul() const
+{
+ OUString sName = "[" + rRefObj.TakeObjNameSingul() + "]";
+
+ OUString aName(GetName());
+ if (!aName.isEmpty())
+ sName += " '" + aName + "'";
+
+ return sName;
+}
+
+OUString SdrVirtObj::TakeObjNamePlural() const
+{
+ return "[" + rRefObj.TakeObjNamePlural() + "]";
+}
+
+bool SdrVirtObj::HasLimitedRotation() const
+{
+ // RotGrfFlyFrame: If true, this SdrObject supports only limited rotation
+ return rRefObj.HasLimitedRotation();
+}
+
+basegfx::B2DPolyPolygon SdrVirtObj::TakeXorPoly() const
+{
+ basegfx::B2DPolyPolygon aPolyPolygon(rRefObj.TakeXorPoly());
+
+ if(m_aAnchor.X() || m_aAnchor.Y())
+ {
+ aPolyPolygon.transform(basegfx::utils::createTranslateB2DHomMatrix(m_aAnchor.X(), m_aAnchor.Y()));
+ }
+
+ return aPolyPolygon;
+}
+
+
+sal_uInt32 SdrVirtObj::GetHdlCount() const
+{
+ return rRefObj.GetHdlCount();
+}
+
+void SdrVirtObj::AddToHdlList(SdrHdlList& rHdlList) const
+{
+ SdrHdlList tempList(nullptr);
+ rRefObj.AddToHdlList(tempList);
+ for (size_t i=0; i<tempList.GetHdlCount(); ++i)
+ {
+ SdrHdl* pHdl = tempList.GetHdl(i);
+ Point aP(pHdl->GetPos()+m_aAnchor);
+ pHdl->SetPos(aP);
+ }
+ tempList.MoveTo(rHdlList);
+}
+
+void SdrVirtObj::AddToPlusHdlList(SdrHdlList& rHdlList, SdrHdl& rHdl) const
+{
+ SdrHdlList tempList(nullptr);
+ rRefObj.AddToPlusHdlList(tempList, rHdl);
+ for (size_t i=0; i<tempList.GetHdlCount(); ++i)
+ {
+ SdrHdl* pHdl = tempList.GetHdl(i);
+ Point aP(pHdl->GetPos()+m_aAnchor);
+ pHdl->SetPos(aP);
+ }
+ tempList.MoveTo(rHdlList);
+}
+
+bool SdrVirtObj::hasSpecialDrag() const
+{
+ return rRefObj.hasSpecialDrag();
+}
+
+bool SdrVirtObj::supportsFullDrag() const
+{
+ return false;
+}
+
+SdrObjectUniquePtr SdrVirtObj::getFullDragClone() const
+{
+ SdrObject& rReferencedObject = const_cast<SdrVirtObj*>(this)->ReferencedObj();
+ return SdrObjectUniquePtr(new SdrGrafObj(
+ getSdrModelFromSdrObject(),
+ SdrDragView::GetObjGraphic(rReferencedObject),
+ GetLogicRect()));
+}
+
+bool SdrVirtObj::beginSpecialDrag(SdrDragStat& rDrag) const
+{
+ return rRefObj.beginSpecialDrag(rDrag);
+}
+
+bool SdrVirtObj::applySpecialDrag(SdrDragStat& rDrag)
+{
+ return rRefObj.applySpecialDrag(rDrag);
+}
+
+basegfx::B2DPolyPolygon SdrVirtObj::getSpecialDragPoly(const SdrDragStat& rDrag) const
+{
+ return rRefObj.getSpecialDragPoly(rDrag);
+ // TODO: we don't handle offsets yet!
+}
+
+OUString SdrVirtObj::getSpecialDragComment(const SdrDragStat& rDrag) const
+{
+ return rRefObj.getSpecialDragComment(rDrag);
+}
+
+
+bool SdrVirtObj::BegCreate(SdrDragStat& rStat)
+{
+ return rRefObj.BegCreate(rStat);
+}
+
+bool SdrVirtObj::MovCreate(SdrDragStat& rStat)
+{
+ return rRefObj.MovCreate(rStat);
+}
+
+bool SdrVirtObj::EndCreate(SdrDragStat& rStat, SdrCreateCmd eCmd)
+{
+ return rRefObj.EndCreate(rStat,eCmd);
+}
+
+bool SdrVirtObj::BckCreate(SdrDragStat& rStat)
+{
+ return rRefObj.BckCreate(rStat);
+}
+
+void SdrVirtObj::BrkCreate(SdrDragStat& rStat)
+{
+ rRefObj.BrkCreate(rStat);
+}
+
+basegfx::B2DPolyPolygon SdrVirtObj::TakeCreatePoly(const SdrDragStat& rDrag) const
+{
+ return rRefObj.TakeCreatePoly(rDrag);
+ // TODO: we don't handle offsets yet!
+}
+
+
+void SdrVirtObj::NbcMove(const Size& rSiz)
+{
+ m_aAnchor.Move(rSiz);
+ SetBoundAndSnapRectsDirty();
+}
+
+void SdrVirtObj::NbcResize(const Point& rRef, const Fraction& xFact, const Fraction& yFact)
+{
+ rRefObj.NbcResize(rRef-m_aAnchor,xFact,yFact);
+ SetBoundAndSnapRectsDirty();
+}
+
+void SdrVirtObj::NbcRotate(const Point& rRef, Degree100 nAngle, double sn, double cs)
+{
+ rRefObj.NbcRotate(rRef-m_aAnchor,nAngle,sn,cs);
+ SetBoundAndSnapRectsDirty();
+}
+
+void SdrVirtObj::NbcMirror(const Point& rRef1, const Point& rRef2)
+{
+ rRefObj.NbcMirror(rRef1-m_aAnchor,rRef2-m_aAnchor);
+ SetBoundAndSnapRectsDirty();
+}
+
+void SdrVirtObj::NbcShear(const Point& rRef, Degree100 nAngle, double tn, bool bVShear)
+{
+ rRefObj.NbcShear(rRef-m_aAnchor,nAngle,tn,bVShear);
+ SetBoundAndSnapRectsDirty();
+}
+
+
+void SdrVirtObj::Move(const Size& rSiz)
+{
+ if (!rSiz.IsEmpty()) {
+ tools::Rectangle aBoundRect0; if (m_pUserCall!=nullptr) aBoundRect0=GetLastBoundRect();
+ NbcMove(rSiz);
+ SetChanged();
+ BroadcastObjectChange();
+ SendUserCall(SdrUserCallType::MoveOnly,aBoundRect0);
+ }
+}
+
+void SdrVirtObj::Resize(const Point& rRef, const Fraction& xFact, const Fraction& yFact, bool bUnsetRelative)
+{
+ if (xFact.GetNumerator()!=xFact.GetDenominator() || yFact.GetNumerator()!=yFact.GetDenominator()) {
+ tools::Rectangle aBoundRect0; if (m_pUserCall!=nullptr) aBoundRect0=GetLastBoundRect();
+ rRefObj.Resize(rRef-m_aAnchor,xFact,yFact, bUnsetRelative);
+ SetBoundAndSnapRectsDirty();
+ SendUserCall(SdrUserCallType::Resize,aBoundRect0);
+ }
+}
+
+void SdrVirtObj::Rotate(const Point& rRef, Degree100 nAngle, double sn, double cs)
+{
+ if (nAngle) {
+ tools::Rectangle aBoundRect0; if (m_pUserCall!=nullptr) aBoundRect0=GetLastBoundRect();
+ rRefObj.Rotate(rRef-m_aAnchor,nAngle,sn,cs);
+ SetBoundAndSnapRectsDirty();
+ SendUserCall(SdrUserCallType::Resize,aBoundRect0);
+ }
+}
+
+void SdrVirtObj::Mirror(const Point& rRef1, const Point& rRef2)
+{
+ tools::Rectangle aBoundRect0; if (m_pUserCall!=nullptr) aBoundRect0=GetLastBoundRect();
+ rRefObj.Mirror(rRef1-m_aAnchor,rRef2-m_aAnchor);
+ SetBoundAndSnapRectsDirty();
+ SendUserCall(SdrUserCallType::Resize,aBoundRect0);
+}
+
+void SdrVirtObj::Shear(const Point& rRef, Degree100 nAngle, double tn, bool bVShear)
+{
+ if (nAngle) {
+ tools::Rectangle aBoundRect0; if (m_pUserCall!=nullptr) aBoundRect0=GetLastBoundRect();
+ rRefObj.Shear(rRef-m_aAnchor,nAngle,tn,bVShear);
+ SetBoundAndSnapRectsDirty();
+ SendUserCall(SdrUserCallType::Resize,aBoundRect0);
+ }
+}
+
+
+void SdrVirtObj::RecalcSnapRect()
+{
+ aSnapRect=rRefObj.GetSnapRect();
+ aSnapRect+=m_aAnchor;
+}
+
+const tools::Rectangle& SdrVirtObj::GetSnapRect() const
+{
+ const_cast<SdrVirtObj*>(this)->aSnapRect=rRefObj.GetSnapRect();
+ const_cast<SdrVirtObj*>(this)->aSnapRect+=m_aAnchor;
+ return aSnapRect;
+}
+
+void SdrVirtObj::SetSnapRect(const tools::Rectangle& rRect)
+{
+ tools::Rectangle aBoundRect0; if (m_pUserCall!=nullptr) aBoundRect0=GetLastBoundRect();
+ tools::Rectangle aR(rRect);
+ aR-=m_aAnchor;
+ rRefObj.SetSnapRect(aR);
+ SetBoundAndSnapRectsDirty();
+ SendUserCall(SdrUserCallType::Resize,aBoundRect0);
+}
+
+void SdrVirtObj::NbcSetSnapRect(const tools::Rectangle& rRect)
+{
+ tools::Rectangle aR(rRect);
+ aR-=m_aAnchor;
+ SetBoundAndSnapRectsDirty();
+ rRefObj.NbcSetSnapRect(aR);
+}
+
+
+const tools::Rectangle& SdrVirtObj::GetLogicRect() const
+{
+ const_cast<SdrVirtObj*>(this)->aSnapRect=rRefObj.GetLogicRect(); // An abuse of aSnapRect!
+ const_cast<SdrVirtObj*>(this)->aSnapRect+=m_aAnchor; // If there's trouble, we need another Rectangle Member (or a Heap).
+ return aSnapRect;
+}
+
+void SdrVirtObj::SetLogicRect(const tools::Rectangle& rRect)
+{
+ tools::Rectangle aBoundRect0; if (m_pUserCall!=nullptr) aBoundRect0=GetLastBoundRect();
+ tools::Rectangle aR(rRect);
+ aR-=m_aAnchor;
+ rRefObj.SetLogicRect(aR);
+ SetBoundAndSnapRectsDirty();
+ SendUserCall(SdrUserCallType::Resize,aBoundRect0);
+}
+
+void SdrVirtObj::NbcSetLogicRect(const tools::Rectangle& rRect)
+{
+ tools::Rectangle aR(rRect);
+ aR-=m_aAnchor;
+ SetBoundAndSnapRectsDirty();
+ rRefObj.NbcSetLogicRect(aR);
+}
+
+
+Degree100 SdrVirtObj::GetRotateAngle() const
+{
+ return rRefObj.GetRotateAngle();
+}
+
+Degree100 SdrVirtObj::GetShearAngle(bool bVertical) const
+{
+ return rRefObj.GetShearAngle(bVertical);
+}
+
+
+sal_uInt32 SdrVirtObj::GetSnapPointCount() const
+{
+ return rRefObj.GetSnapPointCount();
+}
+
+Point SdrVirtObj::GetSnapPoint(sal_uInt32 i) const
+{
+ Point aP(rRefObj.GetSnapPoint(i));
+ aP+=m_aAnchor;
+ return aP;
+}
+
+bool SdrVirtObj::IsPolyObj() const
+{
+ return rRefObj.IsPolyObj();
+}
+
+sal_uInt32 SdrVirtObj::GetPointCount() const
+{
+ return rRefObj.GetPointCount();
+}
+
+Point SdrVirtObj::GetPoint(sal_uInt32 i) const
+{
+ return rRefObj.GetPoint(i) + m_aAnchor;
+}
+
+void SdrVirtObj::NbcSetPoint(const Point& rPnt, sal_uInt32 i)
+{
+ Point aP(rPnt);
+ aP-=m_aAnchor;
+ rRefObj.SetPoint(aP,i);
+ SetBoundAndSnapRectsDirty();
+}
+
+
+std::unique_ptr<SdrObjGeoData> SdrVirtObj::NewGeoData() const
+{
+ return rRefObj.NewGeoData();
+}
+
+void SdrVirtObj::SaveGeoData(SdrObjGeoData& rGeo) const
+{
+ rRefObj.SaveGeoData(rGeo);
+}
+
+void SdrVirtObj::RestoreGeoData(const SdrObjGeoData& rGeo)
+{
+ rRefObj.RestoreGeoData(rGeo);
+ SetBoundAndSnapRectsDirty();
+}
+
+
+std::unique_ptr<SdrObjGeoData> SdrVirtObj::GetGeoData() const
+{
+ return rRefObj.GetGeoData();
+}
+
+void SdrVirtObj::SetGeoData(const SdrObjGeoData& rGeo)
+{
+ tools::Rectangle aBoundRect0; if (m_pUserCall!=nullptr) aBoundRect0=GetLastBoundRect();
+ rRefObj.SetGeoData(rGeo);
+ SetBoundAndSnapRectsDirty();
+ SendUserCall(SdrUserCallType::Resize,aBoundRect0);
+}
+
+
+void SdrVirtObj::NbcReformatText()
+{
+ rRefObj.NbcReformatText();
+}
+
+bool SdrVirtObj::HasMacro() const
+{
+ return rRefObj.HasMacro();
+}
+
+SdrObject* SdrVirtObj::CheckMacroHit(const SdrObjMacroHitRec& rRec) const
+{
+ return rRefObj.CheckMacroHit(rRec); // TODO: positioning offset
+}
+
+PointerStyle SdrVirtObj::GetMacroPointer(const SdrObjMacroHitRec& rRec) const
+{
+ return rRefObj.GetMacroPointer(rRec); // TODO: positioning offset
+}
+
+void SdrVirtObj::PaintMacro(OutputDevice& rOut, const tools::Rectangle& rDirtyRect, const SdrObjMacroHitRec& rRec) const
+{
+ rRefObj.PaintMacro(rOut,rDirtyRect,rRec); // TODO: positioning offset
+}
+
+bool SdrVirtObj::DoMacro(const SdrObjMacroHitRec& rRec)
+{
+ return rRefObj.DoMacro(rRec); // TODO: positioning offset
+}
+
+Point SdrVirtObj::GetOffset() const
+{
+ // #i73248# default offset of SdrVirtObj is aAnchor
+ return m_aAnchor;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/svdraw/svdpage.cxx b/svx/source/svdraw/svdpage.cxx
new file mode 100644
index 000000000..b7aa8777e
--- /dev/null
+++ b/svx/source/svdraw/svdpage.cxx
@@ -0,0 +1,1895 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * 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 <cassert>
+#include <set>
+#include <unordered_set>
+
+#include <svx/svdpage.hxx>
+
+#include <o3tl/safeint.hxx>
+#include <string.h>
+
+#include <tools/debug.hxx>
+#include <tools/diagnose_ex.h>
+
+#include <svtools/colorcfg.hxx>
+#include <svx/svdetc.hxx>
+#include <svx/svdobj.hxx>
+#include <svx/svdogrp.hxx>
+#include <svx/svdoedge.hxx>
+#include <svx/svdoole2.hxx>
+#include <svx/svditer.hxx>
+#include <svx/svdmodel.hxx>
+#include <svx/svdlayer.hxx>
+#include <svx/svdpagv.hxx>
+#include <svx/svdundo.hxx>
+#include <svx/xfillit0.hxx>
+#include <svx/fmdpage.hxx>
+
+#include <sdr/contact/viewcontactofsdrpage.hxx>
+#include <svx/sdr/contact/viewobjectcontact.hxx>
+#include <svx/sdr/contact/displayinfo.hxx>
+#include <algorithm>
+#include <svl/hint.hxx>
+#include <rtl/strbuf.hxx>
+#include <libxml/xmlwriter.h>
+
+#include <com/sun/star/lang/IllegalArgumentException.hpp>
+
+using namespace ::com::sun::star;
+
+//////////////////////////////////////////////////////////////////////////////
+
+SdrObjList::SdrObjList()
+: mbObjOrdNumsDirty(false),
+ mbRectsDirty(false),
+ mbIsNavigationOrderDirty(false)
+{
+}
+
+void SdrObjList::impClearSdrObjList(bool bBroadcast)
+{
+ SdrModel* pSdrModelFromRemovedSdrObject(nullptr);
+
+ while(!maList.empty())
+ {
+ // remove last object from list
+ SdrObject* pObj(maList.back());
+ RemoveObjectFromContainer(maList.size()-1);
+
+ // flushViewObjectContacts() is done since SdrObject::Free is not guaranteed
+ // to delete the object and thus refresh visualisations
+ pObj->GetViewContact().flushViewObjectContacts();
+
+ if(bBroadcast)
+ {
+ if(nullptr == pSdrModelFromRemovedSdrObject)
+ {
+ pSdrModelFromRemovedSdrObject = &pObj->getSdrModelFromSdrObject();
+ }
+
+ // sent remove hint (after removal, see RemoveObject())
+ // TTTT SdrPage not needed, can be accessed using SdrObject
+ SdrHint aHint(SdrHintKind::ObjectRemoved, *pObj, getSdrPageFromSdrObjList());
+ pObj->getSdrModelFromSdrObject().Broadcast(aHint);
+ }
+
+ // delete the object itself
+ SdrObject::Free( pObj );
+ }
+
+ if(bBroadcast && nullptr != pSdrModelFromRemovedSdrObject)
+ {
+ pSdrModelFromRemovedSdrObject->SetChanged();
+ }
+}
+
+void SdrObjList::ClearSdrObjList()
+{
+ // clear SdrObjects with broadcasting
+ impClearSdrObjList(true);
+}
+
+SdrObjList::~SdrObjList()
+{
+ // clear SdrObjects without broadcasting
+ impClearSdrObjList(false);
+}
+
+SdrPage* SdrObjList::getSdrPageFromSdrObjList() const
+{
+ // default is no page and returns zero
+ return nullptr;
+}
+
+SdrObject* SdrObjList::getSdrObjectFromSdrObjList() const
+{
+ // default is no SdrObject (SdrObjGroup)
+ return nullptr;
+}
+
+void SdrObjList::CopyObjects(const SdrObjList& rSrcList)
+{
+ // clear SdrObjects with broadcasting
+ ClearSdrObjList();
+
+ mbObjOrdNumsDirty = false;
+ mbRectsDirty = false;
+ size_t nCloneErrCnt(0);
+ const size_t nCount(rSrcList.GetObjCount());
+
+ if(nullptr == getSdrObjectFromSdrObjList() && nullptr == getSdrPageFromSdrObjList())
+ {
+ OSL_ENSURE(false, "SdrObjList which is not part of SdrPage or SdrObject (!)");
+ return;
+ }
+
+ SdrModel& rTargetSdrModel(nullptr == getSdrObjectFromSdrObjList()
+ ? getSdrPageFromSdrObjList()->getSdrModelFromSdrPage()
+ : getSdrObjectFromSdrObjList()->getSdrModelFromSdrObject());
+
+ for (size_t no(0); no < nCount; ++no)
+ {
+ SdrObject* pSO(rSrcList.GetObj(no));
+ SdrObject* pDO(pSO->CloneSdrObject(rTargetSdrModel));
+
+ if(nullptr != pDO)
+ {
+ NbcInsertObject(pDO, SAL_MAX_SIZE);
+ }
+ else
+ {
+ nCloneErrCnt++;
+ }
+ }
+
+ // and now for the Connectors
+ // The new objects would be shown in the rSrcList
+ // and then the object connections are made.
+ // Similar implementation are setup as the following:
+ // void SdrObjList::CopyObjects(const SdrObjList& rSrcList)
+ // SdrModel* SdrExchangeView::CreateMarkedObjModel() const
+ // BOOL SdrExchangeView::Paste(const SdrModel& rMod,...)
+ // void SdrEditView::CopyMarked()
+ if (nCloneErrCnt==0) {
+ for (size_t no=0; no<nCount; ++no) {
+ const SdrObject* pSrcOb=rSrcList.GetObj(no);
+ const SdrEdgeObj* pSrcEdge=dynamic_cast<const SdrEdgeObj*>( pSrcOb );
+ if (pSrcEdge!=nullptr) {
+ SdrObject* pSrcNode1=pSrcEdge->GetConnectedNode(true);
+ SdrObject* pSrcNode2=pSrcEdge->GetConnectedNode(false);
+ if (pSrcNode1!=nullptr && pSrcNode1->getParentSdrObjListFromSdrObject()!=pSrcEdge->getParentSdrObjListFromSdrObject()) pSrcNode1=nullptr; // can't do this
+ if (pSrcNode2!=nullptr && pSrcNode2->getParentSdrObjListFromSdrObject()!=pSrcEdge->getParentSdrObjListFromSdrObject()) pSrcNode2=nullptr; // across all lists (yet)
+ if (pSrcNode1!=nullptr || pSrcNode2!=nullptr) {
+ SdrObject* pEdgeObjTmp=GetObj(no);
+ SdrEdgeObj* pDstEdge=dynamic_cast<SdrEdgeObj*>( pEdgeObjTmp );
+ if (pDstEdge!=nullptr) {
+ if (pSrcNode1!=nullptr) {
+ sal_uInt32 nDstNode1=pSrcNode1->GetOrdNum();
+ SdrObject* pDstNode1=GetObj(nDstNode1);
+ if (pDstNode1!=nullptr) { // else we get an error!
+ pDstEdge->ConnectToNode(true,pDstNode1);
+ } else {
+ OSL_FAIL("SdrObjList::operator=(): pDstNode1==NULL!");
+ }
+ }
+ if (pSrcNode2!=nullptr) {
+ sal_uInt32 nDstNode2=pSrcNode2->GetOrdNum();
+ SdrObject* pDstNode2=GetObj(nDstNode2);
+ if (pDstNode2!=nullptr) { // else the node was probably not selected
+ pDstEdge->ConnectToNode(false,pDstNode2);
+ } else {
+ OSL_FAIL("SdrObjList::operator=(): pDstNode2==NULL!");
+ }
+ }
+ } else {
+ OSL_FAIL("SdrObjList::operator=(): pDstEdge==NULL!");
+ }
+ }
+ }
+ }
+ } else {
+#ifdef DBG_UTIL
+ OStringBuffer aStr("SdrObjList::operator=(): Error when cloning ");
+
+ if(nCloneErrCnt == 1)
+ {
+ aStr.append("a drawing object.");
+ }
+ else
+ {
+ aStr.append(static_cast<sal_Int32>(nCloneErrCnt));
+ aStr.append(" drawing objects.");
+ }
+
+ aStr.append(" Not copying connectors.");
+
+ OSL_FAIL(aStr.getStr());
+#endif
+ }
+}
+
+void SdrObjList::RecalcObjOrdNums()
+{
+ const size_t nCount = GetObjCount();
+ for (size_t no=0; no<nCount; ++no) {
+ SdrObject* pObj=GetObj(no);
+ pObj->SetOrdNum(no);
+ }
+ mbObjOrdNumsDirty=false;
+}
+
+void SdrObjList::RecalcRects()
+{
+ maSdrObjListOutRect=tools::Rectangle();
+ maSdrObjListSnapRect=maSdrObjListOutRect;
+ const size_t nCount = GetObjCount();
+ for (size_t i=0; i<nCount; ++i) {
+ SdrObject* pObj=GetObj(i);
+ if (i==0) {
+ maSdrObjListOutRect=pObj->GetCurrentBoundRect();
+ maSdrObjListSnapRect=pObj->GetSnapRect();
+ } else {
+ maSdrObjListOutRect.Union(pObj->GetCurrentBoundRect());
+ maSdrObjListSnapRect.Union(pObj->GetSnapRect());
+ }
+ }
+}
+
+void SdrObjList::SetSdrObjListRectsDirty()
+{
+ mbRectsDirty=true;
+ SdrObject* pParentSdrObject(getSdrObjectFromSdrObjList());
+
+ if(nullptr != pParentSdrObject)
+ {
+ pParentSdrObject->SetBoundAndSnapRectsDirty();
+ }
+}
+
+void SdrObjList::impChildInserted(SdrObject const & rChild)
+{
+ sdr::contact::ViewContact* pParent = rChild.GetViewContact().GetParentContact();
+
+ if(pParent)
+ {
+ pParent->ActionChildInserted(rChild.GetViewContact());
+ }
+}
+
+void SdrObjList::NbcInsertObject(SdrObject* pObj, size_t nPos)
+{
+ DBG_ASSERT(pObj!=nullptr,"SdrObjList::NbcInsertObject(NULL)");
+ if (pObj==nullptr)
+ return;
+
+ DBG_ASSERT(!pObj->IsInserted(),"The object already has the status Inserted.");
+ const size_t nCount = GetObjCount();
+ if (nPos>nCount) nPos=nCount;
+ InsertObjectIntoContainer(*pObj,nPos);
+
+ if (nPos<nCount) mbObjOrdNumsDirty=true;
+ pObj->SetOrdNum(nPos);
+ pObj->setParentOfSdrObject(this);
+
+ // Inform the parent about change to allow invalidations at
+ // evtl. existing parent visualisations
+ impChildInserted(*pObj);
+
+ if (!mbRectsDirty) {
+ mbRectsDirty = true;
+ }
+ pObj->InsertedStateChange(); // calls the UserCall (among others)
+}
+
+void SdrObjList::InsertObjectThenMakeNameUnique(SdrObject* pObj)
+{
+ std::unordered_set<rtl::OUString> aNameSet;
+ InsertObjectThenMakeNameUnique(pObj, aNameSet);
+}
+
+void SdrObjList::InsertObjectThenMakeNameUnique(SdrObject* pObj, std::unordered_set<OUString>& rNameSet, size_t nPos)
+{
+ InsertObject(pObj, nPos);
+ if (pObj->GetName().isEmpty())
+ return;
+
+ pObj->MakeNameUnique(rNameSet);
+ SdrObjList* pSdrObjList = pObj->GetSubList(); // group
+ if (pSdrObjList)
+ {
+ SdrObject* pListObj;
+ SdrObjListIter aIter(pSdrObjList, SdrIterMode::DeepWithGroups);
+ while (aIter.IsMore())
+ {
+ pListObj = aIter.Next();
+ pListObj->MakeNameUnique(rNameSet);
+ }
+ }
+}
+
+void SdrObjList::InsertObject(SdrObject* pObj, size_t nPos)
+{
+ DBG_ASSERT(pObj!=nullptr,"SdrObjList::InsertObject(NULL)");
+
+ if(!pObj)
+ return;
+
+ // if anchor is used, reset it before grouping
+ if(getSdrObjectFromSdrObjList())
+ {
+ const Point& rAnchorPos = pObj->GetAnchorPos();
+ if(rAnchorPos.X() || rAnchorPos.Y())
+ pObj->NbcSetAnchorPos(Point());
+ }
+
+ // do insert to new group
+ NbcInsertObject(pObj, nPos);
+
+ // In case the object is inserted into a group and doesn't overlap with
+ // the group's other members, it needs an own repaint.
+ SdrObject* pParentSdrObject(getSdrObjectFromSdrObjList());
+
+ if(pParentSdrObject)
+ {
+ // only repaint here
+ pParentSdrObject->ActionChanged();
+ }
+
+ // TODO: We need a different broadcast here!
+ // Repaint from object number ... (heads-up: GroupObj)
+ if(pObj->getSdrPageFromSdrObject() && !pObj->getSdrModelFromSdrObject().isLocked())
+ {
+ SdrHint aHint(SdrHintKind::ObjectInserted, *pObj);
+ pObj->getSdrModelFromSdrObject().Broadcast(aHint);
+ }
+
+ pObj->getSdrModelFromSdrObject().SetChanged();
+}
+
+SdrObject* SdrObjList::NbcRemoveObject(size_t nObjNum)
+{
+ if (nObjNum >= maList.size())
+ {
+ OSL_ASSERT(nObjNum<maList.size());
+ return nullptr;
+ }
+
+ const size_t nCount = GetObjCount();
+ SdrObject* pObj=maList[nObjNum];
+ RemoveObjectFromContainer(nObjNum);
+
+ DBG_ASSERT(pObj!=nullptr,"Could not find object to remove.");
+ if (pObj!=nullptr)
+ {
+ // flushViewObjectContacts() clears the VOC's and those invalidate
+ pObj->GetViewContact().flushViewObjectContacts();
+
+ DBG_ASSERT(pObj->IsInserted(),"The object does not have the status Inserted.");
+
+ // tdf#121022 Do first remove from SdrObjList - InsertedStateChange
+ // relies now on IsInserted which uses getParentSdrObjListFromSdrObject
+ pObj->setParentOfSdrObject(nullptr);
+
+ // calls UserCall, among other
+ pObj->InsertedStateChange();
+
+ if (!mbObjOrdNumsDirty)
+ {
+ // optimizing for the case that the last object has to be removed
+ if (nObjNum+1!=nCount) {
+ mbObjOrdNumsDirty=true;
+ }
+ }
+ SetSdrObjListRectsDirty();
+ }
+ return pObj;
+}
+
+SdrObject* SdrObjList::RemoveObject(size_t nObjNum)
+{
+ if (nObjNum >= maList.size())
+ {
+ OSL_ASSERT(nObjNum<maList.size());
+ return nullptr;
+ }
+
+ const size_t nCount = GetObjCount();
+ SdrObject* pObj=maList[nObjNum];
+ RemoveObjectFromContainer(nObjNum);
+
+ DBG_ASSERT(pObj!=nullptr,"Object to remove not found.");
+ if(pObj)
+ {
+ // flushViewObjectContacts() clears the VOC's and those invalidate
+ pObj->GetViewContact().flushViewObjectContacts();
+ DBG_ASSERT(pObj->IsInserted(),"The object does not have the status Inserted.");
+
+ // TODO: We need a different broadcast here.
+ if (pObj->getSdrPageFromSdrObject()!=nullptr)
+ {
+ SdrHint aHint(SdrHintKind::ObjectRemoved, *pObj);
+ pObj->getSdrModelFromSdrObject().Broadcast(aHint);
+ }
+
+ pObj->getSdrModelFromSdrObject().SetChanged();
+
+ // tdf#121022 Do first remove from SdrObjList - InsertedStateChange
+ // relies now on IsInserted which uses getParentSdrObjListFromSdrObject
+ pObj->setParentOfSdrObject(nullptr);
+
+ // calls, among other things, the UserCall
+ pObj->InsertedStateChange();
+
+ if (!mbObjOrdNumsDirty)
+ {
+ // optimization for the case that the last object is removed
+ if (nObjNum+1!=nCount) {
+ mbObjOrdNumsDirty=true;
+ }
+ }
+
+ SetSdrObjListRectsDirty();
+ SdrObject* pParentSdrObject(getSdrObjectFromSdrObjList());
+
+ if(pParentSdrObject && !GetObjCount())
+ {
+ // empty group created; it needs to be repainted since it's
+ // visualization changes
+ pParentSdrObject->ActionChanged();
+ }
+ }
+ return pObj;
+}
+
+SdrObject* SdrObjList::ReplaceObject(SdrObject* pNewObj, size_t nObjNum)
+{
+ if (nObjNum >= maList.size())
+ {
+ OSL_ASSERT(nObjNum<maList.size());
+ return nullptr;
+ }
+ if (pNewObj == nullptr)
+ {
+ OSL_ASSERT(pNewObj!=nullptr);
+ return nullptr;
+ }
+
+ SdrObject* pObj=maList[nObjNum];
+ DBG_ASSERT(pObj!=nullptr,"SdrObjList::ReplaceObject: Could not find object to remove.");
+ if (pObj!=nullptr) {
+ DBG_ASSERT(pObj->IsInserted(),"SdrObjList::ReplaceObject: the object does not have status Inserted.");
+
+ // TODO: We need a different broadcast here.
+ if (pObj->getSdrPageFromSdrObject()!=nullptr)
+ {
+ SdrHint aHint(SdrHintKind::ObjectRemoved, *pObj);
+ pObj->getSdrModelFromSdrObject().Broadcast(aHint);
+ }
+
+ // Change parent and replace in SdrObjList
+ pObj->setParentOfSdrObject(nullptr);
+ ReplaceObjectInContainer(*pNewObj,nObjNum);
+
+ // tdf#121022 InsertedStateChange uses the parent
+ // to detect if pObj is inserted or not, so have to call
+ // it *after* changing these settings, else an obviously wrong
+ // 'SdrUserCallType::Inserted' would be sent
+ pObj->InsertedStateChange();
+
+ // flushViewObjectContacts() clears the VOC's and those
+ // trigger the evtl. needed invalidate(s)
+ pObj->GetViewContact().flushViewObjectContacts();
+
+ // Setup data at new SdrObject - it already *is* inserted to
+ // the SdrObjList due to 'ReplaceObjectInContainer' above
+ pNewObj->SetOrdNum(nObjNum);
+ pNewObj->setParentOfSdrObject(this);
+
+ // Inform the parent about change to allow invalidations at
+ // evtl. existing parent visualisations, but also react on
+ // newly inserted SdrObjects (as e.g. GraphCtrlUserCall does)
+ impChildInserted(*pNewObj);
+
+ pNewObj->InsertedStateChange();
+
+ // TODO: We need a different broadcast here.
+ if (pNewObj->getSdrPageFromSdrObject()!=nullptr) {
+ SdrHint aHint(SdrHintKind::ObjectInserted, *pNewObj);
+ pNewObj->getSdrModelFromSdrObject().Broadcast(aHint);
+ }
+
+ pNewObj->getSdrModelFromSdrObject().SetChanged();
+
+ SetSdrObjListRectsDirty();
+ }
+ return pObj;
+}
+
+SdrObject* SdrObjList::SetObjectOrdNum(size_t nOldObjNum, size_t nNewObjNum)
+{
+ if (nOldObjNum >= maList.size() || nNewObjNum >= maList.size())
+ {
+ OSL_ASSERT(nOldObjNum<maList.size());
+ OSL_ASSERT(nNewObjNum<maList.size());
+ return nullptr;
+ }
+
+ SdrObject* pObj=maList[nOldObjNum];
+ if (nOldObjNum==nNewObjNum) return pObj;
+ DBG_ASSERT(pObj!=nullptr,"SdrObjList::SetObjectOrdNum: Object not found.");
+ if (pObj!=nullptr) {
+ DBG_ASSERT(pObj->IsInserted(),"SdrObjList::SetObjectOrdNum: the object does not have status Inserted.");
+ RemoveObjectFromContainer(nOldObjNum);
+ InsertObjectIntoContainer(*pObj,nNewObjNum);
+
+ // No need to delete visualisation data since same object
+ // gets inserted again. Also a single ActionChanged is enough
+ pObj->ActionChanged();
+
+ pObj->SetOrdNum(nNewObjNum);
+ mbObjOrdNumsDirty=true;
+
+ // TODO: We need a different broadcast here.
+ if (pObj->getSdrPageFromSdrObject()!=nullptr)
+ pObj->getSdrModelFromSdrObject().Broadcast(SdrHint(SdrHintKind::ObjectChange, *pObj));
+ pObj->getSdrModelFromSdrObject().SetChanged();
+ }
+ return pObj;
+}
+
+void SdrObjList::SetExistingObjectOrdNum(SdrObject* pObj, size_t nNewObjNum)
+{
+ assert(std::find(maList.begin(), maList.end(), pObj) != maList.end() && "This method requires that the child object already be inserted");
+ assert(pObj->IsInserted() && "SdrObjList::SetObjectOrdNum: the object does not have status Inserted.");
+
+ // I am deliberately bypassing getOrdNum() because I don't want to unnecessarily
+ // trigger RecalcObjOrdNums()
+ const sal_uInt32 nOldOrdNum = pObj->m_nOrdNum;
+ if (!mbObjOrdNumsDirty && nOldOrdNum == nNewObjNum)
+ return;
+
+ // Update the navigation positions.
+ if (HasObjectNavigationOrder())
+ {
+ tools::WeakReference<SdrObject> aReference (pObj);
+ auto iObject = ::std::find(
+ mxNavigationOrder->begin(),
+ mxNavigationOrder->end(),
+ aReference);
+ mxNavigationOrder->erase(iObject);
+ mbIsNavigationOrderDirty = true;
+ // The new object does not have a user defined position so append it
+ // to the list.
+ pObj->SetNavigationPosition(mxNavigationOrder->size());
+ mxNavigationOrder->push_back(pObj);
+ }
+ if (nOldOrdNum < maList.size() && maList[nOldOrdNum] == pObj)
+ maList.erase(maList.begin()+nOldOrdNum);
+ else
+ {
+ auto it = std::find(maList.begin(), maList.end(), pObj);
+ maList.erase(it);
+ }
+ // Insert object into object list. Because the insert() method requires
+ // a valid iterator as insertion position, we have to use push_back() to
+ // insert at the end of the list.
+ if (nNewObjNum >= maList.size())
+ maList.push_back(pObj);
+ else
+ maList.insert(maList.begin()+nNewObjNum, pObj);
+
+ mbObjOrdNumsDirty=true;
+
+ // No need to delete visualisation data since same object
+ // gets inserted again. Also a single ActionChanged is enough
+ pObj->ActionChanged();
+
+ pObj->SetOrdNum(nNewObjNum);
+ mbObjOrdNumsDirty=true;
+
+ // TODO: We need a different broadcast here.
+ if (pObj->getSdrPageFromSdrObject()!=nullptr)
+ pObj->getSdrModelFromSdrObject().Broadcast(SdrHint(SdrHintKind::ObjectChange, *pObj));
+ pObj->getSdrModelFromSdrObject().SetChanged();
+}
+
+void SdrObjList::sort( std::vector<sal_Int32>& sortOrder)
+{
+ // no negative indexes and indexes larger than maList size are allowed
+ auto it = std::find_if( sortOrder.begin(), sortOrder.end(), [this](const sal_Int32& rIt)
+ { return ( rIt < 0 || o3tl::make_unsigned(rIt) >= maList.size() ); } );
+ if ( it != sortOrder.end())
+ throw css::lang::IllegalArgumentException("negative index of shape", nullptr, 1);
+
+ // no duplicates
+ std::vector<bool> aNoDuplicates(sortOrder.size(), false);
+ for (size_t i = 0; i < sortOrder.size(); ++i )
+ {
+ size_t idx = static_cast<size_t>( sortOrder[i] );
+
+ if ( aNoDuplicates[idx] )
+ throw css::lang::IllegalArgumentException("duplicate index of shape", nullptr, 2);
+
+ aNoDuplicates[idx] = true;
+ }
+
+ // example sortOrder [2 0 1]
+ // example maList [T T S T T] ( T T = shape with textbox, S = just a shape )
+ // (shapes at positions 0 and 2 have a textbox)
+
+ std::deque<SdrObject*> aNewList(maList.size());
+ std::set<sal_Int32> aShapesWithTextbox;
+ std::vector<sal_Int32> aIncrements;
+ std::vector<sal_Int32> aDuplicates;
+
+ if ( maList.size() > 1)
+ {
+ for (size_t i = 1; i< maList.size(); ++i)
+ {
+ // if this shape is a textbox, then look at its left neighbour
+ // (shape this textbox is in)
+ // and insert the number of textboxes to the left of it
+ if (maList[i]->IsTextBox())
+ aShapesWithTextbox.insert( i - 1 - aShapesWithTextbox.size() );
+ }
+ // example aShapesWithTextbox [0 2]
+ }
+
+ if (aShapesWithTextbox.size() != maList.size() - sortOrder.size())
+ {
+ throw lang::IllegalArgumentException("mismatch of no. of shapes", nullptr, 0);
+ }
+
+ for (size_t i = 0; i< sortOrder.size(); ++i)
+ {
+
+ if (aShapesWithTextbox.count(sortOrder[i]) > 0)
+ aDuplicates.push_back(sortOrder[i]);
+
+ aDuplicates.push_back(sortOrder[i]);
+
+ // example aDuplicates [2 2 0 0 1]
+ }
+ assert(aDuplicates.size() == maList.size());
+
+ aIncrements.push_back(0);
+ for (size_t i = 1; i< sortOrder.size(); ++i)
+ {
+ if (aShapesWithTextbox.count(i - 1))
+ aIncrements.push_back(aIncrements[i-1] + 1 );
+ else
+ aIncrements.push_back(aIncrements[i-1]);
+
+ // example aIncrements [0 1 1]
+ }
+ assert(aIncrements.size() == sortOrder.size());
+
+ std::vector<sal_Int32> aNewSortOrder(maList.size());
+ sal_Int32 nPrev = -1;
+ for (size_t i = 0; i< aDuplicates.size(); ++i)
+ {
+ if (nPrev != aDuplicates[i])
+ aNewSortOrder[i] = aDuplicates[i] + aIncrements[aDuplicates[i]];
+ else
+ aNewSortOrder[i] = aNewSortOrder[i-1] + 1;
+
+ nPrev = aDuplicates[i];
+
+ // example aNewSortOrder [3 4 0 1 2]
+ }
+ assert(aNewSortOrder.size() == maList.size());
+
+#ifndef NDEBUG
+ {
+ std::vector<sal_Int32> tmp(aNewSortOrder);
+ std::sort(tmp.begin(), tmp.end());
+ for (size_t i = 0; i < tmp.size(); ++i)
+ {
+ assert(size_t(tmp[i]) == i);
+ }
+ }
+#endif
+
+ SdrModel & rModel(getSdrPageFromSdrObjList()->getSdrModelFromSdrPage());
+ bool const isUndo(rModel.IsUndoEnabled());
+ if (isUndo)
+ {
+ rModel.AddUndo(SdrUndoFactory::CreateUndoSort(*getSdrPageFromSdrObjList(), sortOrder));
+ }
+
+ for (size_t i = 0; i < aNewSortOrder.size(); ++i)
+ {
+ aNewList[i] = maList[ aNewSortOrder[i] ];
+ aNewList[i]->SetOrdNum(i);
+ }
+
+ std::swap(aNewList, maList);
+}
+
+const tools::Rectangle& SdrObjList::GetAllObjSnapRect() const
+{
+ if (mbRectsDirty) {
+ const_cast<SdrObjList*>(this)->RecalcRects();
+ const_cast<SdrObjList*>(this)->mbRectsDirty=false;
+ }
+ return maSdrObjListSnapRect;
+}
+
+const tools::Rectangle& SdrObjList::GetAllObjBoundRect() const
+{
+ // #i106183# for deep group hierarchies like in chart2, the invalidates
+ // through the hierarchy are not correct; use a 2nd hint for the needed
+ // recalculation. Future versions will have no bool flag at all, but
+ // just maSdrObjListOutRect in empty state to represent an invalid state, thus
+ // it's a step in the right direction.
+ if (mbRectsDirty || maSdrObjListOutRect.IsEmpty())
+ {
+ const_cast<SdrObjList*>(this)->RecalcRects();
+ const_cast<SdrObjList*>(this)->mbRectsDirty=false;
+ }
+ return maSdrObjListOutRect;
+}
+
+void SdrObjList::NbcReformatAllTextObjects()
+{
+ size_t nCount=GetObjCount();
+ size_t nNum=0;
+
+ while (nNum<nCount)
+ {
+ SdrObject* pObj = GetObj(nNum);
+
+ pObj->NbcReformatText();
+ nCount=GetObjCount(); // ReformatText may delete an object
+ nNum++;
+ }
+
+}
+
+void SdrObjList::ReformatAllTextObjects()
+{
+ NbcReformatAllTextObjects();
+}
+
+/** steps over all available objects and reformats all
+ edge objects that are connected to other objects so that
+ they may reposition themselves.
+*/
+void SdrObjList::ReformatAllEdgeObjects()
+{
+ // #i120437# go over whole hierarchy, not only over object level null (seen from grouping)
+ SdrObjListIter aIter(this, SdrIterMode::DeepNoGroups);
+
+ while(aIter.IsMore())
+ {
+ SdrObject* pObj = aIter.Next();
+ if (pObj->GetObjIdentifier() != SdrObjKind::Edge)
+ continue;
+
+ SdrEdgeObj* pSdrEdgeObj = static_cast< SdrEdgeObj* >(pObj);
+ pSdrEdgeObj->Reformat();
+ }
+}
+
+void SdrObjList::BurnInStyleSheetAttributes()
+{
+ for(size_t a = 0; a < GetObjCount(); ++a)
+ {
+ GetObj(a)->BurnInStyleSheetAttributes();
+ }
+}
+
+size_t SdrObjList::GetObjCount() const
+{
+ return maList.size();
+}
+
+
+SdrObject* SdrObjList::GetObj(size_t nNum) const
+{
+ if (nNum < maList.size())
+ return maList[nNum];
+
+ return nullptr;
+}
+
+
+bool SdrObjList::IsReadOnly() const
+{
+ bool bRet(false);
+ SdrObject* pParentSdrObject(getSdrObjectFromSdrObjList());
+
+ if(nullptr != pParentSdrObject)
+ {
+ SdrPage* pSdrPage(pParentSdrObject->getSdrPageFromSdrObject());
+
+ if(nullptr != pSdrPage)
+ {
+ bRet = pSdrPage->IsReadOnly();
+ }
+ }
+
+ return bRet;
+}
+
+void SdrObjList::FlattenGroups()
+{
+ const size_t nObj = GetObjCount();
+ for( size_t i = nObj; i>0; )
+ UnGroupObj(--i);
+}
+
+void SdrObjList::UnGroupObj( size_t nObjNum )
+{
+ // if the given object is no group, this method is a noop
+ SdrObject* pUngroupObj = GetObj( nObjNum );
+ if( pUngroupObj )
+ {
+ SdrObjList* pSrcLst = pUngroupObj->GetSubList();
+ if(pSrcLst)
+ if(auto pUngroupGroup = dynamic_cast<SdrObjGroup*>( pUngroupObj))
+ {
+ // ungroup recursively (has to be head recursion,
+ // otherwise our indices will get trashed when doing it in
+ // the loop)
+ pSrcLst->FlattenGroups();
+
+ // the position at which we insert the members of rUngroupGroup
+ size_t nInsertPos( pUngroupGroup->GetOrdNum() );
+
+ const size_t nCount = pSrcLst->GetObjCount();
+ for( size_t i=0; i<nCount; ++i )
+ {
+ SdrObject* pObj = pSrcLst->RemoveObject(0);
+ InsertObject(pObj, nInsertPos);
+ ++nInsertPos;
+ }
+
+ RemoveObject(nInsertPos);
+ }
+ }
+#ifdef DBG_UTIL
+ else
+ OSL_FAIL("SdrObjList::UnGroupObj: object index invalid");
+#endif
+}
+
+bool SdrObjList::HasObjectNavigationOrder() const { return bool(mxNavigationOrder); }
+
+void SdrObjList::SetObjectNavigationPosition (
+ SdrObject& rObject,
+ const sal_uInt32 nNewPosition)
+{
+ // When the navigation order container has not yet been created then
+ // create one now. It is initialized with the z-order taken from
+ // maList.
+ if (!mxNavigationOrder)
+ {
+ mxNavigationOrder.emplace(maList.begin(), maList.end());
+ }
+ OSL_ASSERT(bool(mxNavigationOrder));
+ OSL_ASSERT( mxNavigationOrder->size() == maList.size());
+
+ tools::WeakReference<SdrObject> aReference (&rObject);
+
+ // Look up the object whose navigation position is to be changed.
+ auto iObject = ::std::find(
+ mxNavigationOrder->begin(),
+ mxNavigationOrder->end(),
+ aReference);
+ if (iObject == mxNavigationOrder->end())
+ {
+ // The given object is not a member of the navigation order.
+ return;
+ }
+
+ // Move the object to its new position.
+ const sal_uInt32 nOldPosition = ::std::distance(mxNavigationOrder->begin(), iObject);
+ if (nOldPosition == nNewPosition)
+ return;
+
+ mxNavigationOrder->erase(iObject);
+ sal_uInt32 nInsertPosition (nNewPosition);
+ // Adapt insertion position for the just erased object.
+ if (nNewPosition >= nOldPosition)
+ nInsertPosition -= 1;
+ if (nInsertPosition >= mxNavigationOrder->size())
+ mxNavigationOrder->push_back(aReference);
+ else
+ mxNavigationOrder->insert(mxNavigationOrder->begin()+nInsertPosition, aReference);
+
+ mbIsNavigationOrderDirty = true;
+
+ // The navigation order is written out to file so mark the model as modified.
+ rObject.getSdrModelFromSdrObject().SetChanged();
+}
+
+
+SdrObject* SdrObjList::GetObjectForNavigationPosition (const sal_uInt32 nNavigationPosition) const
+{
+ if (HasObjectNavigationOrder())
+ {
+ // There is a user defined navigation order. Make sure the object
+ // index is correct and look up the object in mxNavigationOrder.
+ if (nNavigationPosition >= mxNavigationOrder->size())
+ {
+ OSL_ASSERT(nNavigationPosition < mxNavigationOrder->size());
+ }
+ else
+ return (*mxNavigationOrder)[nNavigationPosition].get();
+ }
+ else
+ {
+ // There is no user defined navigation order. Use the z-order
+ // instead.
+ if (nNavigationPosition >= maList.size())
+ {
+ OSL_ASSERT(nNavigationPosition < maList.size());
+ }
+ else
+ return maList[nNavigationPosition];
+ }
+ return nullptr;
+}
+
+
+void SdrObjList::ClearObjectNavigationOrder()
+{
+ mxNavigationOrder.reset();
+ mbIsNavigationOrderDirty = true;
+}
+
+
+bool SdrObjList::RecalcNavigationPositions()
+{
+ if (mbIsNavigationOrderDirty)
+ {
+ if (mxNavigationOrder)
+ {
+ mbIsNavigationOrderDirty = false;
+
+ sal_uInt32 nIndex (0);
+ for (auto& rpObject : *mxNavigationOrder)
+ {
+ rpObject->SetNavigationPosition(nIndex);
+ ++nIndex;
+ }
+ }
+ }
+
+ return bool(mxNavigationOrder);
+}
+
+
+void SdrObjList::SetNavigationOrder (const uno::Reference<container::XIndexAccess>& rxOrder)
+{
+ if (rxOrder.is())
+ {
+ const sal_Int32 nCount = rxOrder->getCount();
+ if (static_cast<sal_uInt32>(nCount) != maList.size())
+ return;
+
+ if (!mxNavigationOrder)
+ mxNavigationOrder = std::vector<tools::WeakReference<SdrObject>>(nCount);
+
+ for (sal_Int32 nIndex=0; nIndex<nCount; ++nIndex)
+ {
+ uno::Reference<uno::XInterface> xShape (rxOrder->getByIndex(nIndex), uno::UNO_QUERY);
+ SdrObject* pObject = SdrObject::getSdrObjectFromXShape(xShape);
+ if (pObject == nullptr)
+ break;
+ (*mxNavigationOrder)[nIndex] = pObject;
+ }
+
+ mbIsNavigationOrderDirty = true;
+ }
+ else
+ {
+ ClearObjectNavigationOrder();
+ }
+}
+
+
+void SdrObjList::InsertObjectIntoContainer (
+ SdrObject& rObject,
+ const sal_uInt32 nInsertPosition)
+{
+ OSL_ASSERT(nInsertPosition<=maList.size());
+
+ // Update the navigation positions.
+ if (HasObjectNavigationOrder())
+ {
+ // The new object does not have a user defined position so append it
+ // to the list.
+ rObject.SetNavigationPosition(mxNavigationOrder->size());
+ mxNavigationOrder->push_back(&rObject);
+ }
+
+ // Insert object into object list. Because the insert() method requires
+ // a valid iterator as insertion position, we have to use push_back() to
+ // insert at the end of the list.
+ if (nInsertPosition >= maList.size())
+ maList.push_back(&rObject);
+ else
+ maList.insert(maList.begin()+nInsertPosition, &rObject);
+ mbObjOrdNumsDirty=true;
+}
+
+
+void SdrObjList::ReplaceObjectInContainer (
+ SdrObject& rNewObject,
+ const sal_uInt32 nObjectPosition)
+{
+ if (nObjectPosition >= maList.size())
+ {
+ OSL_ASSERT(nObjectPosition<maList.size());
+ return;
+ }
+
+ // Update the navigation positions.
+ if (HasObjectNavigationOrder())
+ {
+ // A user defined position of the object that is to be replaced is
+ // not transferred to the new object so erase the former and append
+ // the later object from/to the navigation order.
+ OSL_ASSERT(nObjectPosition < maList.size());
+ tools::WeakReference<SdrObject> aReference (maList[nObjectPosition]);
+ auto iObject = ::std::find(
+ mxNavigationOrder->begin(),
+ mxNavigationOrder->end(),
+ aReference);
+ if (iObject != mxNavigationOrder->end())
+ mxNavigationOrder->erase(iObject);
+
+ mxNavigationOrder->push_back(&rNewObject);
+
+ mbIsNavigationOrderDirty = true;
+ }
+
+ maList[nObjectPosition] = &rNewObject;
+ mbObjOrdNumsDirty=true;
+}
+
+
+void SdrObjList::RemoveObjectFromContainer (
+ const sal_uInt32 nObjectPosition)
+{
+ if (nObjectPosition >= maList.size())
+ {
+ OSL_ASSERT(nObjectPosition<maList.size());
+ return;
+ }
+
+ // Update the navigation positions.
+ if (HasObjectNavigationOrder())
+ {
+ tools::WeakReference<SdrObject> aReference (maList[nObjectPosition]);
+ auto iObject = ::std::find(
+ mxNavigationOrder->begin(),
+ mxNavigationOrder->end(),
+ aReference);
+ if (iObject != mxNavigationOrder->end())
+ mxNavigationOrder->erase(iObject);
+ mbIsNavigationOrderDirty = true;
+ }
+
+ maList.erase(maList.begin()+nObjectPosition);
+ mbObjOrdNumsDirty=true;
+}
+
+void SdrObjList::dumpAsXml(xmlTextWriterPtr pWriter) const
+{
+ (void)xmlTextWriterStartElement(pWriter, BAD_CAST("SdrObjList"));
+ (void)xmlTextWriterWriteFormatAttribute(pWriter, BAD_CAST("ptr"), "%p", this);
+ (void)xmlTextWriterWriteFormatAttribute(pWriter, BAD_CAST("symbol"), "%s", BAD_CAST(typeid(*this).name()));
+
+ size_t nObjCount = GetObjCount();
+ for (size_t i = 0; i < nObjCount; ++i)
+ {
+ if (const SdrObject* pObject = GetObj(i))
+ pObject->dumpAsXml(pWriter);
+ }
+
+ (void)xmlTextWriterEndElement(pWriter);
+}
+
+
+void SdrPageGridFrameList::Clear()
+{
+ sal_uInt16 nCount=GetCount();
+ for (sal_uInt16 i=0; i<nCount; i++) {
+ delete GetObject(i);
+ }
+ aList.clear();
+}
+
+
+// PageUser section
+
+void SdrPage::AddPageUser(sdr::PageUser& rNewUser)
+{
+ maPageUsers.push_back(&rNewUser);
+}
+
+void SdrPage::RemovePageUser(sdr::PageUser& rOldUser)
+{
+ const sdr::PageUserVector::iterator aFindResult = ::std::find(maPageUsers.begin(), maPageUsers.end(), &rOldUser);
+ if(aFindResult != maPageUsers.end())
+ {
+ maPageUsers.erase(aFindResult);
+ }
+}
+
+
+// DrawContact section
+
+std::unique_ptr<sdr::contact::ViewContact> SdrPage::CreateObjectSpecificViewContact()
+{
+ return std::make_unique<sdr::contact::ViewContactOfSdrPage>(*this);
+}
+
+const sdr::contact::ViewContact& SdrPage::GetViewContact() const
+{
+ if (!mpViewContact)
+ const_cast<SdrPage*>(this)->mpViewContact =
+ const_cast<SdrPage*>(this)->CreateObjectSpecificViewContact();
+
+ return *mpViewContact;
+}
+
+sdr::contact::ViewContact& SdrPage::GetViewContact()
+{
+ if (!mpViewContact)
+ mpViewContact = CreateObjectSpecificViewContact();
+
+ return *mpViewContact;
+}
+
+void SdrPageProperties::ImpRemoveStyleSheet()
+{
+ if(mpStyleSheet)
+ {
+ EndListening(*mpStyleSheet);
+ maProperties.SetParent(nullptr);
+ mpStyleSheet = nullptr;
+ }
+}
+
+void SdrPageProperties::ImpAddStyleSheet(SfxStyleSheet& rNewStyleSheet)
+{
+ if(mpStyleSheet != &rNewStyleSheet)
+ {
+ ImpRemoveStyleSheet();
+ mpStyleSheet = &rNewStyleSheet;
+ StartListening(rNewStyleSheet);
+ maProperties.SetParent(&rNewStyleSheet.GetItemSet());
+ }
+}
+
+static void ImpPageChange(SdrPage& rSdrPage)
+{
+ rSdrPage.ActionChanged();
+ rSdrPage.getSdrModelFromSdrPage().SetChanged();
+ SdrHint aHint(SdrHintKind::PageOrderChange, &rSdrPage);
+ rSdrPage.getSdrModelFromSdrPage().Broadcast(aHint);
+}
+
+SdrPageProperties::SdrPageProperties(SdrPage& rSdrPage)
+: mpSdrPage(&rSdrPage),
+ mpStyleSheet(nullptr),
+ maProperties(
+ mpSdrPage->getSdrModelFromSdrPage().GetItemPool(),
+ svl::Items<XATTR_FILL_FIRST, XATTR_FILL_LAST>)
+{
+ if(!rSdrPage.IsMasterPage())
+ {
+ maProperties.Put(XFillStyleItem(drawing::FillStyle_NONE));
+ }
+}
+
+SdrPageProperties::~SdrPageProperties()
+{
+ ImpRemoveStyleSheet();
+}
+
+void SdrPageProperties::Notify(SfxBroadcaster& /*rBC*/, const SfxHint& rHint)
+{
+ switch(rHint.GetId())
+ {
+ case SfxHintId::DataChanged :
+ {
+ // notify change, broadcast
+ ImpPageChange(*mpSdrPage);
+ break;
+ }
+ case SfxHintId::Dying :
+ {
+ // Style needs to be forgotten
+ ImpRemoveStyleSheet();
+ break;
+ }
+ default: break;
+ }
+}
+
+bool SdrPageProperties::isUsedByModel() const
+{
+ assert(mpSdrPage);
+ return mpSdrPage->IsInserted();
+}
+
+
+void SdrPageProperties::PutItemSet(const SfxItemSet& rSet)
+{
+ OSL_ENSURE(!mpSdrPage->IsMasterPage(), "Item set at MasterPage Attributes (!)");
+ maProperties.Put(rSet);
+ ImpPageChange(*mpSdrPage);
+}
+
+void SdrPageProperties::PutItem(const SfxPoolItem& rItem)
+{
+ OSL_ENSURE(!mpSdrPage->IsMasterPage(), "Item set at MasterPage Attributes (!)");
+ maProperties.Put(rItem);
+ ImpPageChange(*mpSdrPage);
+}
+
+void SdrPageProperties::ClearItem(const sal_uInt16 nWhich)
+{
+ maProperties.ClearItem(nWhich);
+ ImpPageChange(*mpSdrPage);
+}
+
+void SdrPageProperties::SetStyleSheet(SfxStyleSheet* pStyleSheet)
+{
+ if(pStyleSheet)
+ {
+ ImpAddStyleSheet(*pStyleSheet);
+ }
+ else
+ {
+ ImpRemoveStyleSheet();
+ }
+
+ ImpPageChange(*mpSdrPage);
+}
+
+void SdrPageProperties::SetTheme(std::unique_ptr<svx::Theme> pTheme)
+{
+ mpTheme = std::move(pTheme);
+
+ if (mpTheme && mpSdrPage->IsMasterPage())
+ {
+ SdrModel& rModel = mpSdrPage->getSdrModelFromSdrPage();
+ sal_uInt16 nPageCount = rModel.GetPageCount();
+ for (sal_uInt16 nPage = 0; nPage < nPageCount; ++nPage)
+ {
+ SdrPage* pPage = rModel.GetPage(nPage);
+ if (!pPage->TRG_HasMasterPage() || &pPage->TRG_GetMasterPage() != mpSdrPage)
+ {
+ continue;
+ }
+
+ mpTheme->UpdateSdrPage(pPage);
+ }
+ }
+}
+
+svx::Theme* SdrPageProperties::GetTheme() { return mpTheme.get(); }
+
+void SdrPageProperties::dumpAsXml(xmlTextWriterPtr pWriter) const
+{
+ (void)xmlTextWriterStartElement(pWriter, BAD_CAST("SdrPageProperties"));
+ (void)xmlTextWriterWriteFormatAttribute(pWriter, BAD_CAST("ptr"), "%p", this);
+
+ if (mpTheme)
+ {
+ mpTheme->dumpAsXml(pWriter);
+ }
+
+ (void)xmlTextWriterEndElement(pWriter);
+}
+
+SdrPage::SdrPage(SdrModel& rModel, bool bMasterPage)
+: mrSdrModelFromSdrPage(rModel),
+ mnWidth(10),
+ mnHeight(10),
+ mnBorderLeft(0),
+ mnBorderUpper(0),
+ mnBorderRight(0),
+ mnBorderLower(0),
+ mpLayerAdmin(new SdrLayerAdmin(&rModel.GetLayerAdmin())),
+ nPageNum(0),
+ mbMaster(bMasterPage),
+ mbInserted(false),
+ mbObjectsNotPersistent(false),
+ mbPageBorderOnlyLeftRight(false)
+{
+ mpSdrPageProperties.reset(new SdrPageProperties(*this));
+}
+
+SdrPage::~SdrPage()
+{
+ if( mxUnoPage.is() ) try
+ {
+ uno::Reference< lang::XComponent > xPageComponent( mxUnoPage, uno::UNO_QUERY_THROW );
+ mxUnoPage.clear();
+ xPageComponent->dispose();
+ }
+ catch( const uno::Exception& )
+ {
+ DBG_UNHANDLED_EXCEPTION("svx");
+ }
+
+ // tell all the registered PageUsers that the page is in destruction
+ // This causes some (all?) PageUsers to remove themselves from the list
+ // of page users. Therefore we have to use a copy of the list for the
+ // iteration.
+ sdr::PageUserVector aListCopy (maPageUsers.begin(), maPageUsers.end());
+ for(sdr::PageUser* pPageUser : aListCopy)
+ {
+ DBG_ASSERT(pPageUser, "SdrPage::~SdrPage: corrupt PageUser list (!)");
+ pPageUser->PageInDestruction(*this);
+ }
+
+ // Clear the vector. This means that user do not need to call RemovePageUser()
+ // when they get called from PageInDestruction().
+ maPageUsers.clear();
+
+ mpLayerAdmin.reset();
+
+ TRG_ClearMasterPage();
+
+ mpViewContact.reset();
+ mpSdrPageProperties.reset();
+}
+
+void SdrPage::lateInit(const SdrPage& rSrcPage)
+{
+ assert(!mpViewContact);
+ assert(!mxUnoPage.is());
+
+ // copy all the local parameters to make this instance
+ // a valid copy of source page before copying and inserting
+ // the contained objects
+ mbMaster = rSrcPage.mbMaster;
+ mbPageBorderOnlyLeftRight = rSrcPage.mbPageBorderOnlyLeftRight;
+ mnWidth = rSrcPage.mnWidth;
+ mnHeight = rSrcPage.mnHeight;
+ mnBorderLeft = rSrcPage.mnBorderLeft;
+ mnBorderUpper = rSrcPage.mnBorderUpper;
+ mnBorderRight = rSrcPage.mnBorderRight;
+ mnBorderLower = rSrcPage.mnBorderLower;
+ mbBackgroundFullSize = rSrcPage.mbBackgroundFullSize;
+ nPageNum = rSrcPage.nPageNum;
+
+ if(rSrcPage.TRG_HasMasterPage())
+ {
+ TRG_SetMasterPage(rSrcPage.TRG_GetMasterPage());
+ TRG_SetMasterPageVisibleLayers(rSrcPage.TRG_GetMasterPageVisibleLayers());
+ }
+ else
+ {
+ TRG_ClearMasterPage();
+ }
+
+ mbObjectsNotPersistent = rSrcPage.mbObjectsNotPersistent;
+
+ {
+ mpSdrPageProperties.reset(new SdrPageProperties(*this));
+
+ if(!IsMasterPage())
+ {
+ mpSdrPageProperties->PutItemSet(rSrcPage.getSdrPageProperties().GetItemSet());
+ }
+
+ mpSdrPageProperties->SetStyleSheet(rSrcPage.getSdrPageProperties().GetStyleSheet());
+ }
+
+ // Now copy the contained objects
+ if(0 != rSrcPage.GetObjCount())
+ {
+ CopyObjects(rSrcPage);
+ }
+}
+
+rtl::Reference<SdrPage> SdrPage::CloneSdrPage(SdrModel& rTargetModel) const
+{
+ rtl::Reference<SdrPage> pClonedPage(new SdrPage(rTargetModel));
+ pClonedPage->lateInit(*this);
+ return pClonedPage;
+}
+
+void SdrPage::SetSize(const Size& aSiz)
+{
+ bool bChanged(false);
+
+ if(aSiz.Width() != mnWidth)
+ {
+ mnWidth = aSiz.Width();
+ bChanged = true;
+ }
+
+ if(aSiz.Height() != mnHeight)
+ {
+ mnHeight = aSiz.Height();
+ bChanged = true;
+ }
+
+ if(bChanged)
+ {
+ SetChanged();
+ }
+}
+
+Size SdrPage::GetSize() const
+{
+ return Size(mnWidth,mnHeight);
+}
+
+tools::Long SdrPage::GetWidth() const
+{
+ return mnWidth;
+}
+
+void SdrPage::SetOrientation(Orientation eOri)
+{
+ // square: handle like portrait format
+ Size aSiz(GetSize());
+ if (aSiz.Width()!=aSiz.Height()) {
+ if ((eOri==Orientation::Portrait) == (aSiz.Width()>aSiz.Height())) {
+ // coverity[swapped_arguments : FALSE] - this is in the correct order
+ SetSize(Size(aSiz.Height(),aSiz.Width()));
+ }
+ }
+}
+
+Orientation SdrPage::GetOrientation() const
+{
+ // square: handle like portrait format
+ Orientation eRet=Orientation::Portrait;
+ Size aSiz(GetSize());
+ if (aSiz.Width()>aSiz.Height()) eRet=Orientation::Landscape;
+ return eRet;
+}
+
+tools::Long SdrPage::GetHeight() const
+{
+ return mnHeight;
+}
+
+void SdrPage::SetBorder(sal_Int32 nLft, sal_Int32 nUpp, sal_Int32 nRgt, sal_Int32 nLwr)
+{
+ bool bChanged(false);
+
+ if(mnBorderLeft != nLft)
+ {
+ mnBorderLeft = nLft;
+ bChanged = true;
+ }
+
+ if(mnBorderUpper != nUpp)
+ {
+ mnBorderUpper = nUpp;
+ bChanged = true;
+ }
+
+ if(mnBorderRight != nRgt)
+ {
+ mnBorderRight = nRgt;
+ bChanged = true;
+ }
+
+ if(mnBorderLower != nLwr)
+ {
+ mnBorderLower = nLwr;
+ bChanged = true;
+ }
+
+ if(bChanged)
+ {
+ SetChanged();
+ }
+}
+
+void SdrPage::SetLeftBorder(sal_Int32 nBorder)
+{
+ if(mnBorderLeft != nBorder)
+ {
+ mnBorderLeft = nBorder;
+ SetChanged();
+ }
+}
+
+void SdrPage::SetUpperBorder(sal_Int32 nBorder)
+{
+ if(mnBorderUpper != nBorder)
+ {
+ mnBorderUpper = nBorder;
+ SetChanged();
+ }
+}
+
+void SdrPage::SetRightBorder(sal_Int32 nBorder)
+{
+ if(mnBorderRight != nBorder)
+ {
+ mnBorderRight=nBorder;
+ SetChanged();
+ }
+}
+
+void SdrPage::SetLowerBorder(sal_Int32 nBorder)
+{
+ if(mnBorderLower != nBorder)
+ {
+ mnBorderLower=nBorder;
+ SetChanged();
+ }
+}
+
+sal_Int32 SdrPage::GetLeftBorder() const
+{
+ return mnBorderLeft;
+}
+
+sal_Int32 SdrPage::GetUpperBorder() const
+{
+ return mnBorderUpper;
+}
+
+sal_Int32 SdrPage::GetRightBorder() const
+{
+ return mnBorderRight;
+}
+
+sal_Int32 SdrPage::GetLowerBorder() const
+{
+ return mnBorderLower;
+}
+
+void SdrPage::SetBackgroundFullSize(bool const bIn)
+{
+ if (bIn != mbBackgroundFullSize)
+ {
+ mbBackgroundFullSize = bIn;
+ SetChanged();
+ }
+}
+
+bool SdrPage::IsBackgroundFullSize() const
+{
+ return mbBackgroundFullSize;
+}
+
+// #i68775# React on PageNum changes (from Model in most cases)
+void SdrPage::SetPageNum(sal_uInt16 nNew)
+{
+ if(nNew != nPageNum)
+ {
+ // change
+ nPageNum = nNew;
+
+ // notify visualisations, also notifies e.g. buffered MasterPages
+ ActionChanged();
+ }
+}
+
+sal_uInt16 SdrPage::GetPageNum() const
+{
+ if (!mbInserted)
+ return 0;
+
+ if (mbMaster) {
+ if (getSdrModelFromSdrPage().IsMPgNumsDirty())
+ getSdrModelFromSdrPage().RecalcPageNums(true);
+ } else {
+ if (getSdrModelFromSdrPage().IsPagNumsDirty())
+ getSdrModelFromSdrPage().RecalcPageNums(false);
+ }
+ return nPageNum;
+}
+
+void SdrPage::SetChanged()
+{
+ // For test purposes, use the new ViewContact for change
+ // notification now.
+ ActionChanged();
+ getSdrModelFromSdrPage().SetChanged();
+}
+
+SdrPage* SdrPage::getSdrPageFromSdrObjList() const
+{
+ return const_cast< SdrPage* >(this);
+}
+
+// MasterPage interface
+
+void SdrPage::TRG_SetMasterPage(SdrPage& rNew)
+{
+ if(mpMasterPageDescriptor && &(mpMasterPageDescriptor->GetUsedPage()) == &rNew)
+ return;
+
+ if(mpMasterPageDescriptor)
+ TRG_ClearMasterPage();
+
+ mpMasterPageDescriptor.reset(new sdr::MasterPageDescriptor(*this, rNew));
+ GetViewContact().ActionChanged();
+}
+
+void SdrPage::TRG_ClearMasterPage()
+{
+ if(mpMasterPageDescriptor)
+ {
+ SetChanged();
+
+ // the flushViewObjectContacts() will do needed invalidates by deleting the involved VOCs
+ mpMasterPageDescriptor->GetUsedPage().GetViewContact().flushViewObjectContacts();
+
+ mpMasterPageDescriptor.reset();
+ }
+}
+
+SdrPage& SdrPage::TRG_GetMasterPage() const
+{
+ DBG_ASSERT(mpMasterPageDescriptor != nullptr, "TRG_GetMasterPage(): No MasterPage available. Use TRG_HasMasterPage() before access (!)");
+ return mpMasterPageDescriptor->GetUsedPage();
+}
+
+const SdrLayerIDSet& SdrPage::TRG_GetMasterPageVisibleLayers() const
+{
+ DBG_ASSERT(mpMasterPageDescriptor != nullptr, "TRG_GetMasterPageVisibleLayers(): No MasterPage available. Use TRG_HasMasterPage() before access (!)");
+ return mpMasterPageDescriptor->GetVisibleLayers();
+}
+
+void SdrPage::TRG_SetMasterPageVisibleLayers(const SdrLayerIDSet& rNew)
+{
+ DBG_ASSERT(mpMasterPageDescriptor != nullptr, "TRG_SetMasterPageVisibleLayers(): No MasterPage available. Use TRG_HasMasterPage() before access (!)");
+ mpMasterPageDescriptor->SetVisibleLayers(rNew);
+}
+
+sdr::contact::ViewContact& SdrPage::TRG_GetMasterPageDescriptorViewContact() const
+{
+ DBG_ASSERT(mpMasterPageDescriptor != nullptr, "TRG_GetMasterPageDescriptorViewContact(): No MasterPage available. Use TRG_HasMasterPage() before access (!)");
+ return mpMasterPageDescriptor->GetViewContact();
+}
+
+// used from SdrModel::RemoveMasterPage
+void SdrPage::TRG_ImpMasterPageRemoved(const SdrPage& rRemovedPage)
+{
+ if(TRG_HasMasterPage())
+ {
+ if(&TRG_GetMasterPage() == &rRemovedPage)
+ {
+ TRG_ClearMasterPage();
+ }
+ }
+}
+
+void SdrPage::MakePageObjectsNamesUnique()
+{
+ std::unordered_set<OUString> aNameSet;
+ for (size_t no(0); no < GetObjCount(); ++no)
+ {
+ SdrObject* pObj(GetObj(no));
+ if(nullptr != pObj)
+ {
+ if (!pObj->GetName().isEmpty())
+ {
+ pObj->MakeNameUnique(aNameSet);
+ SdrObjList* pSdrObjList = pObj->GetSubList(); // group
+ if (pSdrObjList)
+ {
+ SdrObject* pListObj;
+ SdrObjListIter aIter(pSdrObjList, SdrIterMode::DeepWithGroups);
+ while (aIter.IsMore())
+ {
+ pListObj = aIter.Next();
+ pListObj->MakeNameUnique(aNameSet);
+ }
+ }
+ }
+ }
+ }
+}
+
+const SdrPageGridFrameList* SdrPage::GetGridFrameList(const SdrPageView* /*pPV*/, const tools::Rectangle* /*pRect*/) const
+{
+ return nullptr;
+}
+
+const SdrLayerAdmin& SdrPage::GetLayerAdmin() const
+{
+ return *mpLayerAdmin;
+}
+
+SdrLayerAdmin& SdrPage::GetLayerAdmin()
+{
+ return *mpLayerAdmin;
+}
+
+OUString SdrPage::GetLayoutName() const
+{
+ return OUString();
+}
+
+void SdrPage::SetInserted( bool bIns )
+{
+ if( mbInserted == bIns )
+ return;
+
+ mbInserted = bIns;
+
+ // #i120437# go over whole hierarchy, not only over object level null (seen from grouping)
+ SdrObjListIter aIter(this, SdrIterMode::DeepNoGroups);
+
+ while ( aIter.IsMore() )
+ {
+ SdrObject* pObj = aIter.Next();
+ if ( auto pOleObj = dynamic_cast<SdrOle2Obj* >(pObj) )
+ {
+ if( mbInserted )
+ pOleObj->Connect();
+ else
+ pOleObj->Disconnect();
+ }
+ }
+}
+
+void SdrPage::SetUnoPage(uno::Reference<drawing::XDrawPage> const& xNewPage)
+{
+ mxUnoPage = xNewPage;
+}
+
+uno::Reference< uno::XInterface > const & SdrPage::getUnoPage()
+{
+ if( !mxUnoPage.is() )
+ {
+ // create one
+ mxUnoPage = createUnoPage();
+ }
+
+ return mxUnoPage;
+}
+
+uno::Reference< uno::XInterface > SdrPage::createUnoPage()
+{
+ css::uno::Reference< css::uno::XInterface > xInt =
+ static_cast<cppu::OWeakObject*>( new SvxFmDrawPage( this ) );
+ return xInt;
+}
+
+SfxStyleSheet* SdrPage::GetTextStyleSheetForObject( SdrObject* pObj ) const
+{
+ return pObj->GetStyleSheet();
+}
+
+/** returns an averaged background color of this page */
+// #i75566# GetBackgroundColor -> GetPageBackgroundColor and bScreenDisplay hint value
+Color SdrPage::GetPageBackgroundColor( SdrPageView const * pView, bool bScreenDisplay ) const
+{
+ Color aColor;
+
+ if(bScreenDisplay && (!pView || pView->GetApplicationDocumentColor() == COL_AUTO))
+ {
+ svtools::ColorConfig aColorConfig;
+ aColor = aColorConfig.GetColorValue( svtools::DOCCOLOR ).nColor;
+ }
+ else
+ {
+ aColor = pView->GetApplicationDocumentColor();
+ }
+
+ const SfxItemSet* pBackgroundFill = &getSdrPageProperties().GetItemSet();
+
+ if(!IsMasterPage() && TRG_HasMasterPage())
+ {
+ if(drawing::FillStyle_NONE == pBackgroundFill->Get(XATTR_FILLSTYLE).GetValue())
+ {
+ pBackgroundFill = &TRG_GetMasterPage().getSdrPageProperties().GetItemSet();
+ }
+ }
+
+ GetDraftFillColor(*pBackgroundFill, aColor);
+
+ return aColor;
+}
+
+/** *deprecated, use GetBackgroundColor with SdrPageView */
+Color SdrPage::GetPageBackgroundColor() const
+// #i75566# GetBackgroundColor -> GetPageBackgroundColor
+{
+ return GetPageBackgroundColor( nullptr );
+}
+
+/** this method returns true if the object from the ViewObjectContact should
+ be visible on this page while rendering.
+ bEdit selects if visibility test is for an editing view or a final render,
+ like printing.
+*/
+bool SdrPage::checkVisibility(
+ const sdr::contact::ViewObjectContact& /*rOriginal*/,
+ const sdr::contact::DisplayInfo& /*rDisplayInfo*/,
+ bool /*bEdit*/)
+{
+ // this will be handled in the application if needed
+ return true;
+}
+
+void SdrPage::dumpAsXml(xmlTextWriterPtr pWriter) const
+{
+ (void)xmlTextWriterStartElement(pWriter, BAD_CAST("SdrPage"));
+ SdrObjList::dumpAsXml(pWriter);
+
+ (void)xmlTextWriterStartElement(pWriter, BAD_CAST("width"));
+ (void)xmlTextWriterWriteFormatAttribute(pWriter, BAD_CAST("value"), "%s",
+ BAD_CAST(OString::number(mnWidth).getStr()));
+ (void)xmlTextWriterEndElement(pWriter);
+ (void)xmlTextWriterStartElement(pWriter, BAD_CAST("height"));
+ (void)xmlTextWriterWriteFormatAttribute(pWriter, BAD_CAST("value"), "%s",
+ BAD_CAST(OString::number(mnHeight).getStr()));
+ (void)xmlTextWriterEndElement(pWriter);
+
+ if (mpSdrPageProperties)
+ {
+ mpSdrPageProperties->dumpAsXml(pWriter);
+ }
+
+ (void)xmlTextWriterEndElement(pWriter);
+}
+
+// DrawContact support: Methods for handling Page changes
+void SdrPage::ActionChanged()
+{
+ // Do necessary ViewContact actions
+ GetViewContact().ActionChanged();
+
+ // #i48535# also handle MasterPage change
+ if(TRG_HasMasterPage())
+ {
+ TRG_GetMasterPageDescriptorViewContact().ActionChanged();
+ }
+}
+
+SdrPageProperties& SdrPage::getSdrPageProperties()
+{
+ return *mpSdrPageProperties;
+}
+
+const SdrPageProperties& SdrPage::getSdrPageProperties() const
+{
+ return *mpSdrPageProperties;
+}
+
+const SdrPageProperties* SdrPage::getCorrectSdrPageProperties() const
+{
+ if(mpMasterPageDescriptor)
+ {
+ return mpMasterPageDescriptor->getCorrectSdrPageProperties();
+ }
+ else
+ {
+ return &getSdrPageProperties();
+ }
+}
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/svdraw/svdpagv.cxx b/svx/source/svdraw/svdpagv.cxx
new file mode 100644
index 000000000..60cd8cfe8
--- /dev/null
+++ b/svx/source/svdraw/svdpagv.cxx
@@ -0,0 +1,894 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * 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/svdpagv.hxx>
+#include <svx/svdpage.hxx>
+#include <svx/svdview.hxx>
+
+#include <svx/svdobj.hxx>
+#include <svx/svdogrp.hxx>
+#include <svx/svdtypes.hxx>
+
+#include <svx/sdr/contact/viewobjectcontactredirector.hxx>
+
+#include <algorithm>
+
+#include <svx/sdrpagewindow.hxx>
+#include <svx/sdrpaintwindow.hxx>
+#include <comphelper/lok.hxx>
+#include <comphelper/scopeguard.hxx>
+#include <basegfx/range/b2irectangle.hxx>
+#include <osl/diagnose.h>
+
+using namespace ::com::sun::star;
+
+// interface to SdrPageWindow
+
+SdrPageWindow* SdrPageView::FindPageWindow(const SdrPaintWindow& rPaintWindow) const
+{
+ for(auto & a : maPageWindows)
+ {
+ if(&(a->GetPaintWindow()) == &rPaintWindow)
+ {
+ return a.get();
+ }
+ }
+
+ return nullptr;
+}
+
+const SdrPageWindow* SdrPageView::FindPatchedPageWindow( const OutputDevice& _rOutDev ) const
+{
+ for ( auto const & pPageWindow : maPageWindows )
+ {
+ const SdrPaintWindow& rPaintWindow( pPageWindow->GetOriginalPaintWindow() ? *pPageWindow->GetOriginalPaintWindow() : pPageWindow->GetPaintWindow() );
+ if ( &rPaintWindow.GetOutputDevice() == &_rOutDev )
+ {
+ return pPageWindow.get();
+ }
+ }
+
+ return nullptr;
+}
+
+SdrPageWindow* SdrPageView::FindPageWindow(const OutputDevice& rOutDev) const
+{
+ for ( auto const & pPageWindow : maPageWindows )
+ {
+ if(&(pPageWindow->GetPaintWindow().GetOutputDevice()) == &rOutDev)
+ {
+ return pPageWindow.get();
+ }
+ }
+
+ return nullptr;
+}
+
+SdrPageWindow* SdrPageView::GetPageWindow(sal_uInt32 nIndex) const
+{
+ return maPageWindows[nIndex].get();
+}
+
+SdrPageView::SdrPageView(SdrPage* pPage1, SdrView& rNewView)
+: mrView(rNewView),
+ // col_auto color lets the view takes the default SvxColorConfig entry
+ maDocumentColor( COL_AUTO ),
+ maBackgroundColor( COL_AUTO ), // #i48367# also react on autocolor
+ mpPreparedPageWindow(nullptr) // #i72752#
+{
+ mpPage = pPage1;
+
+ if(mpPage)
+ {
+ aPgOrg.setX(mpPage->GetLeftBorder() );
+ aPgOrg.setY(mpPage->GetUpperBorder() );
+ }
+ // For example, in the case of charts, there is a LayerAdmin, but it has no valid values. Therefore
+ // a solution like pLayerAdmin->getVisibleLayersODF(aLayerVisi) is not possible. So use the
+ // generic SetAll() for now.
+ aLayerVisi.SetAll();
+ aLayerPrn.SetAll();
+
+ mbHasMarked = false;
+ mbVisible = false;
+ pCurrentList = nullptr;
+ pCurrentGroup = nullptr;
+ SetCurrentGroupAndList(nullptr, mpPage);
+
+ for(sal_uInt32 a(0); a < rNewView.PaintWindowCount(); a++)
+ {
+ AddPaintWindowToPageView(*rNewView.GetPaintWindow(a));
+ }
+}
+
+SdrPageView::~SdrPageView()
+{
+}
+
+void SdrPageView::AddPaintWindowToPageView(SdrPaintWindow& rPaintWindow)
+{
+ if(!FindPageWindow(rPaintWindow))
+ {
+ maPageWindows.emplace_back(new SdrPageWindow(*this, rPaintWindow));
+ }
+}
+
+void SdrPageView::RemovePaintWindowFromPageView(SdrPaintWindow& rPaintWindow)
+{
+ auto it = std::find_if(maPageWindows.begin(), maPageWindows.end(),
+ [&rPaintWindow](const std::unique_ptr<SdrPageWindow>& rpWindow) {
+ return &(rpWindow->GetPaintWindow()) == &rPaintWindow;
+ });
+ if (it != maPageWindows.end())
+ maPageWindows.erase(it);
+}
+
+css::uno::Reference< css::awt::XControlContainer > SdrPageView::GetControlContainer( const OutputDevice& _rDevice ) const
+{
+ css::uno::Reference< css::awt::XControlContainer > xReturn;
+ const SdrPageWindow* pCandidate = FindPatchedPageWindow( _rDevice );
+
+ if ( pCandidate )
+ xReturn = pCandidate->GetControlContainer();
+
+ return xReturn;
+}
+
+void SdrPageView::ModelHasChanged()
+{
+ if (GetCurrentGroup()!=nullptr) CheckCurrentGroup();
+}
+
+bool SdrPageView::IsReadOnly() const
+{
+ return (nullptr == GetPage() || GetView().GetModel()->IsReadOnly() || GetPage()->IsReadOnly() || GetObjList()->IsReadOnly());
+}
+
+void SdrPageView::Show()
+{
+ if(!IsVisible())
+ {
+ mbVisible = true;
+
+ for(sal_uInt32 a(0); a < GetView().PaintWindowCount(); a++)
+ {
+ AddPaintWindowToPageView(*GetView().GetPaintWindow(a));
+ }
+ }
+}
+
+void SdrPageView::Hide()
+{
+ if(IsVisible())
+ {
+ if (!comphelper::LibreOfficeKit::isActive())
+ {
+ InvalidateAllWin();
+ }
+ mbVisible = false;
+ maPageWindows.clear();
+ }
+}
+
+tools::Rectangle SdrPageView::GetPageRect() const
+{
+ if (GetPage()==nullptr) return tools::Rectangle();
+ return tools::Rectangle(Point(),Size(GetPage()->GetWidth()+1,GetPage()->GetHeight()+1));
+}
+
+void SdrPageView::InvalidateAllWin()
+{
+ if(IsVisible() && GetPage())
+ {
+ tools::Rectangle aRect(Point(0,0),Size(GetPage()->GetWidth()+1,GetPage()->GetHeight()+1));
+ aRect.Union(GetPage()->GetAllObjBoundRect());
+ GetView().InvalidateAllWin(aRect);
+ }
+}
+
+
+void SdrPageView::PrePaint()
+{
+ const sal_uInt32 nCount(PageWindowCount());
+
+ for(sal_uInt32 a(0); a < nCount; a++)
+ {
+ SdrPageWindow* pCandidate = GetPageWindow(a);
+
+ if(pCandidate)
+ {
+ pCandidate->PrePaint();
+ }
+ }
+}
+
+void SdrPageView::CompleteRedraw(
+ SdrPaintWindow& rPaintWindow, const vcl::Region& rReg, sdr::contact::ViewObjectContactRedirector* pRedirector )
+{
+ if(!GetPage())
+ return;
+
+ SdrPageWindow* pPageWindow = FindPageWindow(rPaintWindow);
+ std::unique_ptr<SdrPageWindow> pTempPageWindow;
+
+ if(!pPageWindow)
+ {
+ // create temp PageWindow
+ pTempPageWindow.reset(new SdrPageWindow(*this, rPaintWindow));
+ pPageWindow = pTempPageWindow.get();
+ }
+
+ // do the redraw
+ pPageWindow->PrepareRedraw(rReg);
+ pPageWindow->RedrawAll(pRedirector);
+}
+
+
+// #i74769# use SdrPaintWindow directly
+
+void SdrPageView::setPreparedPageWindow(SdrPageWindow* pKnownTarget)
+{
+ // #i72752# remember prepared SdrPageWindow
+ mpPreparedPageWindow = pKnownTarget;
+}
+
+void SdrPageView::DrawLayer(SdrLayerID nID, OutputDevice* pGivenTarget,
+ sdr::contact::ViewObjectContactRedirector* pRedirector,
+ const tools::Rectangle& rRect, basegfx::B2IRectangle const*const pPageFrame)
+{
+ if(!GetPage())
+ return;
+
+ if(pGivenTarget)
+ {
+ SdrPageWindow* pKnownTarget = FindPageWindow(*pGivenTarget);
+
+ if(pKnownTarget)
+ {
+ // paint known target
+ pKnownTarget->RedrawLayer(&nID, pRedirector, pPageFrame);
+ }
+ else
+ {
+ // #i72752# DrawLayer() uses an OutputDevice different from BeginDrawLayer. This happens
+ // e.g. when SW paints a single text line in text edit mode. Try to use it
+ SdrPageWindow* pPreparedTarget = mpPreparedPageWindow;
+
+ if(pPreparedTarget)
+ {
+ // if we have a prepared target, do not use a new SdrPageWindow since this
+ // works but is expensive. Just use a temporary PaintWindow
+ SdrPaintWindow aTemporaryPaintWindow(mrView, *pGivenTarget);
+
+ // Copy existing paint region to use the same as prepared in BeginDrawLayer
+ SdrPaintWindow& rExistingPaintWindow = pPreparedTarget->GetPaintWindow();
+ const vcl::Region& rExistingRegion = rExistingPaintWindow.GetRedrawRegion();
+ bool bUseRect(false);
+ if (!rRect.IsEmpty())
+ {
+ vcl::Region r(rExistingRegion);
+ r.Intersect(rRect);
+ // fdo#74435: FIXME: visibility check broken if empty
+ if (!r.IsEmpty())
+ bUseRect = true;
+ }
+ if (!bUseRect)
+ aTemporaryPaintWindow.SetRedrawRegion(rExistingRegion);
+ else
+ aTemporaryPaintWindow.SetRedrawRegion(vcl::Region(rRect));
+
+ // patch the ExistingPageWindow
+ auto pPreviousWindow = pPreparedTarget->patchPaintWindow(aTemporaryPaintWindow);
+ // unpatch window when leaving the scope
+ const ::comphelper::ScopeGuard aGuard(
+ [&pPreviousWindow, &pPreparedTarget]() { pPreparedTarget->unpatchPaintWindow(pPreviousWindow); } );
+ // redraw the layer
+ pPreparedTarget->RedrawLayer(&nID, pRedirector, pPageFrame);
+ }
+ else
+ {
+ OSL_FAIL("SdrPageView::DrawLayer: Creating temporary SdrPageWindow (ObjectContact), this should never be needed (!)");
+
+ // None of the known OutputDevices is the target of this paint, use
+ // a temporary SdrPageWindow for this Redraw.
+ SdrPaintWindow aTemporaryPaintWindow(mrView, *pGivenTarget);
+ SdrPageWindow aTemporaryPageWindow(*this, aTemporaryPaintWindow);
+
+ // #i72752#
+ // Copy existing paint region if other PageWindows exist, this was created by
+ // PrepareRedraw() from BeginDrawLayer(). Needs to be used e.g. when suddenly SW
+ // paints into an unknown device other than the view was created for (e.g. VirtualDevice)
+ if(PageWindowCount())
+ {
+ SdrPageWindow* pExistingPageWindow = GetPageWindow(0);
+ SdrPaintWindow& rExistingPaintWindow = pExistingPageWindow->GetPaintWindow();
+ const vcl::Region& rExistingRegion = rExistingPaintWindow.GetRedrawRegion();
+ aTemporaryPaintWindow.SetRedrawRegion(rExistingRegion);
+ }
+
+ aTemporaryPageWindow.RedrawLayer(&nID, pRedirector, nullptr);
+ }
+ }
+ }
+ else
+ {
+ // paint in all known windows
+ for(sal_uInt32 a(0); a < PageWindowCount(); a++)
+ {
+ SdrPageWindow* pTarget = GetPageWindow(a);
+ pTarget->RedrawLayer(&nID, pRedirector, nullptr);
+ }
+ }
+}
+
+void SdrPageView::SetDesignMode( bool _bDesignMode ) const
+{
+ for ( sal_uInt32 i = 0; i < PageWindowCount(); ++i )
+ {
+ const SdrPageWindow& rPageViewWindow = *GetPageWindow(i);
+ rPageViewWindow.SetDesignMode( _bDesignMode );
+ }
+}
+
+
+void SdrPageView::DrawPageViewGrid(OutputDevice& rOut, const tools::Rectangle& rRect, Color aColor)
+{
+ if (GetPage()==nullptr)
+ return;
+
+ tools::Long nx1=GetView().maGridBig.Width();
+ tools::Long nx2=GetView().maGridFin.Width();
+ tools::Long ny1=GetView().maGridBig.Height();
+ tools::Long ny2=GetView().maGridFin.Height();
+
+ if (nx1==0) nx1=nx2;
+ if (nx2==0) nx2=nx1;
+ if (ny1==0) ny1=ny2;
+ if (ny2==0) ny2=ny1;
+ if (nx1==0) { nx1=ny1; nx2=ny2; }
+ if (ny1==0) { ny1=nx1; ny2=nx2; }
+ if (nx1<0) nx1=-nx1;
+ if (nx2<0) nx2=-nx2;
+ if (ny1<0) ny1=-ny1;
+ if (ny2<0) ny2=-ny2;
+
+ if (nx1==0)
+ return;
+
+ // no more global output size, use window size instead to decide grid sizes
+ tools::Long nScreenWdt = rOut.GetOutputSizePixel().Width();
+
+ tools::Long nMinDotPix=2;
+ tools::Long nMinLinPix=4;
+
+ if (nScreenWdt>=1600)
+ {
+ nMinDotPix=4;
+ nMinLinPix=8;
+ }
+ else if (nScreenWdt>=1024)
+ {
+ nMinDotPix=3;
+ nMinLinPix=6;
+ }
+ else
+ { // e. g. 640x480
+ nMinDotPix=2;
+ nMinLinPix=4;
+ }
+ Size aMinDotDist(rOut.PixelToLogic(Size(nMinDotPix,nMinDotPix)));
+ Size aMinLinDist(rOut.PixelToLogic(Size(nMinLinPix,nMinLinPix)));
+ bool bHoriSolid=nx2<aMinDotDist.Width();
+ bool bVertSolid=ny2<aMinDotDist.Height();
+ // enlarge line offset (minimum 4 pixels)
+ // enlarge by: *2 *5 *10 *20 *50 *100 ...
+ int nTgl=0;
+ tools::Long nVal0=nx1;
+ while (nx1<aMinLinDist.Width())
+ {
+ tools::Long a=nx1;
+
+ if (nTgl==0) nx1*=2;
+ if (nTgl==1) nx1=nVal0*5; // => nx1*=2.5
+ if (nTgl==2) nx1*=2;
+
+ nVal0=a;
+ nTgl++; if (nTgl>=3) nTgl=0;
+ }
+ nTgl=0;
+ nVal0=ny1;
+ while (ny1<aMinLinDist.Height())
+ {
+ tools::Long a=ny1;
+
+ if (nTgl==0) ny1*=2;
+ if (nTgl==1) ny1=nVal0*5; // => ny1*=2.5
+ if (nTgl==2) ny1*=2;
+
+ nVal0=a;
+ nTgl++;
+
+ if (nTgl>=3) nTgl=0;
+ }
+
+ bool bHoriFine=nx2<nx1;
+ bool bVertFine=ny2<ny1;
+ bool bHoriLines=bHoriSolid || bHoriFine || !bVertFine;
+ bool bVertLines=bVertSolid || bVertFine;
+
+ Color aOriginalLineColor( rOut.GetLineColor() );
+ rOut.SetLineColor( aColor );
+
+ bool bMap0=rOut.IsMapModeEnabled();
+
+ tools::Long nWrX=0;
+ tools::Long nWrY=0;
+ Point aOrg(aPgOrg);
+ tools::Long x1 = 0;
+ tools::Long x2 = 0;
+ if (GetPage()->GetWidth() < 0) // ScDrawPage of RTL sheet
+ {
+ x1 = GetPage()->GetWidth() + GetPage()->GetLeftBorder() + 1;
+ x2 = - GetPage()->GetRightBorder() - 1;
+ }
+ else
+ {
+ x1 = GetPage()->GetLeftBorder() + 1;
+ x2 = GetPage()->GetWidth() - GetPage()->GetRightBorder() - 1;
+ }
+ tools::Long y1 = GetPage()->GetUpperBorder() + 1;
+ tools::Long y2 = GetPage()->GetHeight() - GetPage()->GetLowerBorder() - 1;
+ const SdrPageGridFrameList* pFrames=GetPage()->GetGridFrameList(this,nullptr);
+
+ sal_uInt16 nGridPaintCnt=1;
+ if (pFrames!=nullptr) nGridPaintCnt=pFrames->GetCount();
+ for (sal_uInt16 nGridPaintNum=0; nGridPaintNum<nGridPaintCnt; nGridPaintNum++) {
+ if (pFrames!=nullptr) {
+ const SdrPageGridFrame& rGF=(*pFrames)[nGridPaintNum];
+ nWrX=rGF.GetPaperRect().Left();
+ nWrY=rGF.GetPaperRect().Top();
+ x1=rGF.GetUserArea().Left();
+ x2=rGF.GetUserArea().Right();
+ y1=rGF.GetUserArea().Top();
+ y2=rGF.GetUserArea().Bottom();
+ aOrg=rGF.GetUserArea().TopLeft();
+ aOrg-=rGF.GetPaperRect().TopLeft();
+ }
+ if (!rRect.IsEmpty()) {
+ Size a1PixSiz(rOut.PixelToLogic(Size(1,1)));
+ tools::Long nX1Pix=a1PixSiz.Width(); // add 1 pixel of tolerance
+ tools::Long nY1Pix=a1PixSiz.Height();
+ if (x1<rRect.Left() -nX1Pix) x1=rRect.Left() -nX1Pix;
+ if (x2>rRect.Right() +nX1Pix) x2=rRect.Right() +nX1Pix;
+ if (y1<rRect.Top() -nY1Pix) y1=rRect.Top() -nY1Pix;
+ if (y2>rRect.Bottom()+nY1Pix) y2=rRect.Bottom()+nY1Pix;
+ }
+
+ tools::Long xBigOrg=aOrg.X()+nWrX;
+ while (xBigOrg>=x1) xBigOrg-=nx1;
+ while (xBigOrg<x1) xBigOrg+=nx1;
+ tools::Long xFinOrg=xBigOrg;
+ while (xFinOrg>=x1) xFinOrg-=nx2;
+ while (xFinOrg<x1) xFinOrg+=nx2;
+
+ tools::Long yBigOrg=aOrg.Y()+nWrY;
+ while (yBigOrg>=y1) yBigOrg-=ny1;
+ while (yBigOrg<y1) yBigOrg+=ny1;
+ tools::Long yFinOrg=yBigOrg;
+ while (yFinOrg>=y1) yFinOrg-=ny2;
+ while (yFinOrg<y1) yFinOrg+=ny2;
+
+ if( x1 <= x2 && y1 <= y2 )
+ {
+ if( bHoriLines )
+ {
+ DrawGridFlags nGridFlags = ( bHoriSolid ? DrawGridFlags::HorzLines : DrawGridFlags::Dots );
+ sal_uInt16 nSteps = sal_uInt16(nx1 / nx2);
+ sal_uInt32 nRestPerStepMul1000 = nSteps ? ( ((nx1 * 1000)/ nSteps) - (nx2 * 1000) ) : 0;
+ sal_uInt32 nStepOffset = 0;
+ sal_uInt16 nPointOffset = 0;
+
+ for(sal_uInt16 a=0;a<nSteps;a++)
+ {
+ // draw
+ rOut.DrawGrid(
+ tools::Rectangle( xFinOrg + (a * nx2) + nPointOffset, yBigOrg, x2, y2 ),
+ Size( nx1, ny1 ), nGridFlags );
+
+ // do a step
+ nStepOffset += nRestPerStepMul1000;
+ while(nStepOffset >= 1000)
+ {
+ nStepOffset -= 1000;
+ nPointOffset++;
+ }
+ }
+ }
+
+ if( bVertLines )
+ {
+ DrawGridFlags nGridFlags = ( bVertSolid ? DrawGridFlags::VertLines : DrawGridFlags::Dots );
+ sal_uInt16 nSteps = sal_uInt16(ny1 / ny2);
+ sal_uInt32 nRestPerStepMul1000 = nSteps ? ( ((ny1 * 1000L)/ nSteps) - (ny2 * 1000L) ) : 0;
+ sal_uInt32 nStepOffset = 0;
+ sal_uInt16 nPointOffset = 0;
+
+ for(sal_uInt16 a=0;a<nSteps;a++)
+ {
+ // draw
+ rOut.DrawGrid(
+ tools::Rectangle( xBigOrg, yFinOrg + (a * ny2) + nPointOffset, x2, y2 ),
+ Size( nx1, ny1 ), nGridFlags );
+
+ // do a step
+ nStepOffset += nRestPerStepMul1000;
+ while(nStepOffset >= 1000)
+ {
+ nStepOffset -= 1000;
+ nPointOffset++;
+ }
+ }
+ }
+ }
+ }
+
+ rOut.EnableMapMode(bMap0);
+ rOut.SetLineColor(aOriginalLineColor);
+}
+
+void SdrPageView::AdjHdl()
+{
+ GetView().AdjustMarkHdl();
+}
+
+void SdrPageView::SetLayer(const OUString& rName, SdrLayerIDSet& rBS, bool bJa)
+{
+ if(!GetPage())
+ return;
+
+ SdrLayerID nID = GetPage()->GetLayerAdmin().GetLayerID(rName);
+
+ if(SDRLAYER_NOTFOUND != nID)
+ rBS.Set(nID, bJa);
+}
+
+bool SdrPageView::IsLayer(const OUString& rName, const SdrLayerIDSet& rBS) const
+{
+ if(!GetPage())
+ return false;
+
+ bool bRet(false);
+
+ if (!rName.isEmpty())
+ {
+ SdrLayerID nId = GetPage()->GetLayerAdmin().GetLayerID(rName);
+
+ if(SDRLAYER_NOTFOUND != nId)
+ {
+ bRet = rBS.IsSet(nId);
+ }
+ }
+
+ return bRet;
+}
+
+bool SdrPageView::IsObjMarkable(SdrObject const * pObj) const
+{
+ if (!pObj)
+ return false;
+ if (pObj->IsMarkProtect())
+ return false; // excluded from selection?
+ if (!pObj->IsVisible())
+ return false; // only visible are selectable
+ if (!pObj->IsInserted())
+ return false; // Obj deleted?
+ if (auto pObjGroup = dynamic_cast<const SdrObjGroup*>(pObj))
+ {
+ // If object is a Group object, visibility may depend on
+ // multiple layers. If one object is markable, Group is markable.
+ SdrObjList* pObjList = pObjGroup->GetSubList();
+
+ if (pObjList && pObjList->GetObjCount())
+ {
+ for (size_t a = 0; a < pObjList->GetObjCount(); ++a)
+ {
+ SdrObject* pCandidate = pObjList->GetObj(a);
+ // call recursively
+ if (IsObjMarkable(pCandidate))
+ return true;
+ }
+ return false;
+ }
+ else
+ {
+ // #i43302#
+ // Allow empty groups to be selected to be able to delete them
+ return true;
+ }
+ }
+ if (!pObj->Is3DObj() && pObj->getSdrPageFromSdrObject() != GetPage())
+ {
+ // Obj suddenly in different Page
+ return false;
+ }
+
+ // the layer has to be visible and must not be locked
+ SdrLayerID nL = pObj->GetLayer();
+ if (!aLayerVisi.IsSet(nL))
+ return false;
+ if (aLayerLock.IsSet(nL))
+ return false;
+ return true;
+}
+
+void SdrPageView::SetPageOrigin(const Point& rOrg)
+{
+ if (rOrg!=aPgOrg) {
+ aPgOrg=rOrg;
+ if (GetView().IsGridVisible()) {
+ InvalidateAllWin();
+ }
+ }
+}
+
+void SdrPageView::ImpInvalidateHelpLineArea(sal_uInt16 nNum) const
+{
+ if (!(GetView().IsHlplVisible() && nNum<aHelpLines.GetCount())) return;
+
+ const SdrHelpLine& rHL=aHelpLines[nNum];
+
+ for(sal_uInt32 a(0); a < GetView().PaintWindowCount(); a++)
+ {
+ SdrPaintWindow* pCandidate = GetView().GetPaintWindow(a);
+
+ if(pCandidate->OutputToWindow())
+ {
+ OutputDevice& rOutDev = pCandidate->GetOutputDevice();
+ tools::Rectangle aR(rHL.GetBoundRect(rOutDev));
+ Size aSiz(rOutDev.PixelToLogic(Size(1,1)));
+ aR.AdjustLeft( -(aSiz.Width()) );
+ aR.AdjustRight(aSiz.Width() );
+ aR.AdjustTop( -(aSiz.Height()) );
+ aR.AdjustBottom(aSiz.Height() );
+ const_cast<SdrView&>(GetView()).InvalidateOneWin(rOutDev, aR);
+ }
+ }
+}
+
+void SdrPageView::SetHelpLines(const SdrHelpLineList& rHLL)
+{
+ aHelpLines=rHLL;
+ InvalidateAllWin();
+}
+
+void SdrPageView::SetHelpLine(sal_uInt16 nNum, const SdrHelpLine& rNewHelpLine)
+{
+ if (nNum >= aHelpLines.GetCount() || aHelpLines[nNum] == rNewHelpLine)
+ return;
+
+ bool bNeedRedraw = true;
+ if (aHelpLines[nNum].GetKind()==rNewHelpLine.GetKind()) {
+ switch (rNewHelpLine.GetKind()) {
+ case SdrHelpLineKind::Vertical : if (aHelpLines[nNum].GetPos().X()==rNewHelpLine.GetPos().X()) bNeedRedraw = false; break;
+ case SdrHelpLineKind::Horizontal: if (aHelpLines[nNum].GetPos().Y()==rNewHelpLine.GetPos().Y()) bNeedRedraw = false; break;
+ default: break;
+ } // switch
+ }
+ if (bNeedRedraw) ImpInvalidateHelpLineArea(nNum);
+ aHelpLines[nNum]=rNewHelpLine;
+ if (bNeedRedraw) ImpInvalidateHelpLineArea(nNum);
+}
+
+void SdrPageView::DeleteHelpLine(sal_uInt16 nNum)
+{
+ if (nNum<aHelpLines.GetCount()) {
+ ImpInvalidateHelpLineArea(nNum);
+ aHelpLines.Delete(nNum);
+ }
+}
+
+void SdrPageView::InsertHelpLine(const SdrHelpLine& rHL)
+{
+ sal_uInt16 nNum = aHelpLines.GetCount();
+ aHelpLines.Insert(rHL,nNum);
+ if (GetView().IsHlplVisible())
+ ImpInvalidateHelpLineArea(nNum);
+}
+
+// set current group and list
+void SdrPageView::SetCurrentGroupAndList(SdrObject* pNewGroup, SdrObjList* pNewList)
+{
+ if(pCurrentGroup != pNewGroup)
+ {
+ pCurrentGroup = pNewGroup;
+ }
+ if(pCurrentList != pNewList)
+ {
+ pCurrentList = pNewList;
+ }
+}
+
+bool SdrPageView::EnterGroup(SdrObject* pObj)
+{
+ if(!pObj || !pObj->IsGroupObject())
+ return false;
+
+ // Don't allow enter Diagrams
+ if(nullptr != pObj && pObj->isDiagram())
+ return false;
+
+ const bool bGlueInvalidate(GetView().ImpIsGlueVisible());
+
+ if (bGlueInvalidate)
+ {
+ GetView().GlueInvalidate();
+ }
+
+ // deselect all
+ GetView().UnmarkAll();
+
+ // set current group and list
+ SdrObjList* pNewObjList = pObj->GetSubList();
+ SetCurrentGroupAndList(pObj, pNewObjList);
+
+ // select contained object if only one object is contained,
+ // else select nothing and let the user decide what to do next
+ if(pNewObjList && pNewObjList->GetObjCount() == 1)
+ {
+ SdrObject* pFirstObject = pNewObjList->GetObj(0);
+
+ if(GetView().GetSdrPageView())
+ {
+ GetView().MarkObj(pFirstObject, GetView().GetSdrPageView());
+ }
+ }
+
+ // build new handles
+ GetView().AdjustMarkHdl();
+
+ // invalidate only when view wants to visualize group entering
+ InvalidateAllWin();
+
+ if (bGlueInvalidate)
+ {
+ GetView().GlueInvalidate();
+ }
+
+ return true;
+}
+
+void SdrPageView::LeaveOneGroup()
+{
+ SdrObject* pLastGroup = GetCurrentGroup();
+ if (!pLastGroup)
+ return;
+
+ bool bGlueInvalidate = GetView().ImpIsGlueVisible();
+
+ if(bGlueInvalidate)
+ GetView().GlueInvalidate();
+
+ SdrObject* pParentGroup = pLastGroup->getParentSdrObjectFromSdrObject();
+ SdrObjList* pParentList = GetPage();
+
+ if(pParentGroup)
+ pParentList = pParentGroup->GetSubList();
+
+ // deselect everything
+ GetView().UnmarkAll();
+
+ // allocations, pCurrentGroup and pCurrentList need to be set
+ SetCurrentGroupAndList(pParentGroup, pParentList);
+
+ // select the group we just left
+ if (GetView().GetSdrPageView())
+ GetView().MarkObj(pLastGroup, GetView().GetSdrPageView());
+
+ GetView().AdjustMarkHdl();
+
+ // invalidate only if view wants to visualize group entering
+ InvalidateAllWin();
+
+ if(bGlueInvalidate)
+ GetView().GlueInvalidate();
+}
+
+void SdrPageView::LeaveAllGroup()
+{
+ SdrObject* pLastGroup = GetCurrentGroup();
+ if (!pLastGroup)
+ return;
+
+ bool bGlueInvalidate = GetView().ImpIsGlueVisible();
+
+ if(bGlueInvalidate)
+ GetView().GlueInvalidate();
+
+ // deselect everything
+ GetView().UnmarkAll();
+
+ // allocations, pCurrentGroup and pCurrentList always need to be set
+ SetCurrentGroupAndList(nullptr, GetPage());
+
+ // find and select uppermost group
+ while (pLastGroup->getParentSdrObjectFromSdrObject())
+ pLastGroup = pLastGroup->getParentSdrObjectFromSdrObject();
+
+ if (GetView().GetSdrPageView())
+ GetView().MarkObj(pLastGroup, GetView().GetSdrPageView());
+
+ GetView().AdjustMarkHdl();
+
+ // invalidate only when view wants to visualize group entering
+ InvalidateAllWin();
+
+ if(bGlueInvalidate)
+ GetView().GlueInvalidate();
+}
+
+sal_uInt16 SdrPageView::GetEnteredLevel() const
+{
+ sal_uInt16 nCount=0;
+ SdrObject* pGrp=GetCurrentGroup();
+ while (pGrp!=nullptr) {
+ nCount++;
+ pGrp=pGrp->getParentSdrObjectFromSdrObject();
+ }
+ return nCount;
+}
+
+void SdrPageView::CheckCurrentGroup()
+{
+ SdrObject* pGrp(GetCurrentGroup());
+
+ while(nullptr != pGrp &&
+ (!pGrp->IsInserted() || nullptr == pGrp->getParentSdrObjListFromSdrObject() || nullptr == pGrp->getSdrPageFromSdrObject()))
+ {
+ // anything outside of the borders?
+ pGrp = pGrp->getParentSdrObjectFromSdrObject();
+ }
+
+ if(pGrp != GetCurrentGroup())
+ {
+ if(nullptr != pGrp)
+ {
+ EnterGroup(pGrp);
+ }
+ else
+ {
+ LeaveAllGroup();
+ }
+ }
+}
+
+// Set background color for svx at SdrPageViews
+void SdrPageView::SetApplicationBackgroundColor(Color aBackgroundColor)
+{
+ maBackgroundColor = aBackgroundColor;
+}
+
+
+// Set document color for svx at SdrPageViews
+void SdrPageView::SetApplicationDocumentColor(Color aDocumentColor)
+{
+ maDocumentColor = aDocumentColor;
+}
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/svdraw/svdpdf.cxx b/svx/source/svdraw/svdpdf.cxx
new file mode 100644
index 000000000..7beba145a
--- /dev/null
+++ b/svx/source/svdraw/svdpdf.cxx
@@ -0,0 +1,1052 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <svdpdf.hxx>
+
+#include <tools/UnitConversion.hxx>
+#include <vcl/graph.hxx>
+#include <vcl/vectorgraphicdata.hxx>
+
+#include <math.h>
+#include <editeng/eeitem.hxx>
+#include <editeng/fhgtitem.hxx>
+#include <editeng/wghtitem.hxx>
+#include <editeng/postitem.hxx>
+#include <editeng/udlnitem.hxx>
+#include <editeng/crossedoutitem.hxx>
+#include <editeng/shdditem.hxx>
+#include <svx/xlnclit.hxx>
+#include <svx/xlncapit.hxx>
+#include <svx/xlnwtit.hxx>
+#include <svx/xflclit.hxx>
+#include <editeng/fontitem.hxx>
+#include <editeng/wrlmitem.hxx>
+#include <editeng/contouritem.hxx>
+#include <editeng/colritem.hxx>
+#include <vcl/metric.hxx>
+#include <editeng/charscaleitem.hxx>
+#include <svx/sdtditm.hxx>
+#include <svx/sdtagitm.hxx>
+#include <svx/sdtfsitm.hxx>
+#include <svx/svdmodel.hxx>
+#include <svx/svdpage.hxx>
+#include <svx/svdobj.hxx>
+#include <svx/svdotext.hxx>
+#include <svx/svdorect.hxx>
+#include <svx/svdograf.hxx>
+#include <svx/svdopath.hxx>
+#include <svx/svdetc.hxx>
+#include <svl/itemset.hxx>
+#include <basegfx/polygon/b2dpolygon.hxx>
+#include <tools/helpers.hxx>
+#include <basegfx/matrix/b2dhommatrix.hxx>
+#include <basegfx/matrix/b2dhommatrixtools.hxx>
+#include <svx/xlinjoit.hxx>
+#include <svx/xlndsit.hxx>
+#include <basegfx/polygon/b2dpolygonclipper.hxx>
+#include <svx/xbtmpit.hxx>
+#include <svx/xfillit0.hxx>
+#include <svx/xflbmtit.hxx>
+#include <svx/xflbstit.hxx>
+#include <svx/xlineit0.hxx>
+#include <basegfx/polygon/b2dpolypolygontools.hxx>
+#include <svx/svditer.hxx>
+#include <svx/svdogrp.hxx>
+#include <vcl/dibtools.hxx>
+#include <sal/log.hxx>
+#include <osl/diagnose.h>
+
+using namespace com::sun::star;
+
+ImpSdrPdfImport::ImpSdrPdfImport(SdrModel& rModel, SdrLayerID nLay, const tools::Rectangle& rRect,
+ Graphic const& rGraphic)
+ : mpVD(VclPtr<VirtualDevice>::Create())
+ , maScaleRect(rRect)
+ , mnMapScalingOfs(0)
+ , mpModel(&rModel)
+ , mnLayer(nLay)
+ , mnLineWidth(0)
+ , maDash(css::drawing::DashStyle_RECT, 0, 0, 0, 0, 0)
+ , mbMov(false)
+ , mbSize(false)
+ , maOfs(0, 0)
+ , mfScaleX(1.0)
+ , mfScaleY(1.0)
+ , maScaleX(1.0)
+ , maScaleY(1.0)
+ , mbFntDirty(true)
+ , mbLastObjWasPolyWithoutLine(false)
+ , mbNoLine(false)
+ , mbNoFill(false)
+ , mnPageCount(0)
+ , mdPageHeightPts(0)
+ , mpPDFium(vcl::pdf::PDFiumLibrary::get())
+{
+ mpVD->EnableOutput(false);
+ mpVD->SetLineColor();
+ mpVD->SetFillColor();
+ maOldLineColor.SetRed(mpVD->GetLineColor().GetRed() + 1);
+ mpLineAttr = std::make_unique<SfxItemSetFixed<XATTR_LINE_FIRST, XATTR_LINE_LAST>>(
+ rModel.GetItemPool());
+ mpFillAttr = std::make_unique<SfxItemSetFixed<XATTR_FILL_FIRST, XATTR_FILL_LAST>>(
+ rModel.GetItemPool());
+ mpTextAttr
+ = std::make_unique<SfxItemSetFixed<EE_ITEMS_START, EE_ITEMS_END>>(rModel.GetItemPool());
+
+ checkClip();
+
+ // Load the buffer using pdfium.
+ auto const& rVectorGraphicData = rGraphic.getVectorGraphicData();
+ auto* pData = rVectorGraphicData->getBinaryDataContainer().getData();
+ sal_Int32 nSize = rVectorGraphicData->getBinaryDataContainer().getSize();
+ mpPdfDocument = mpPDFium ? mpPDFium->openDocument(pData, nSize, OString()) : nullptr;
+ if (!mpPdfDocument)
+ return;
+
+ mnPageCount = mpPdfDocument->getPageCount();
+}
+
+ImpSdrPdfImport::~ImpSdrPdfImport() = default;
+
+void ImpSdrPdfImport::DoObjects(SvdProgressInfo* pProgrInfo, sal_uInt32* pActionsToReport,
+ int nPageIndex)
+{
+ const int nPageCount = mpPdfDocument->getPageCount();
+ if (!(nPageCount > 0 && nPageIndex >= 0 && nPageIndex < nPageCount))
+ return;
+
+ // Render next page.
+ auto pPdfPage = mpPdfDocument->openPage(nPageIndex);
+ if (!pPdfPage)
+ return;
+
+ basegfx::B2DSize dPageSize = mpPdfDocument->getPageSize(nPageIndex);
+
+ const double dPageWidth = dPageSize.getX();
+ const double dPageHeight = dPageSize.getY();
+
+ SetupPageScale(dPageWidth, dPageHeight);
+
+ // Load the page text to extract it when we get text elements.
+ auto pTextPage = pPdfPage->getTextPage();
+
+ const int nPageObjectCount = pPdfPage->getObjectCount();
+ if (pProgrInfo)
+ pProgrInfo->SetActionCount(nPageObjectCount);
+
+ for (int nPageObjectIndex = 0; nPageObjectIndex < nPageObjectCount; ++nPageObjectIndex)
+ {
+ auto pPageObject = pPdfPage->getObject(nPageObjectIndex);
+ ImportPdfObject(pPageObject, pTextPage, nPageObjectIndex);
+ if (pProgrInfo && pActionsToReport)
+ {
+ (*pActionsToReport)++;
+
+ if (*pActionsToReport >= 16)
+ {
+ if (!pProgrInfo->ReportActions(*pActionsToReport))
+ break;
+
+ *pActionsToReport = 0;
+ }
+ }
+ }
+}
+
+void ImpSdrPdfImport::SetupPageScale(const double dPageWidth, const double dPageHeight)
+{
+ mfScaleX = mfScaleY = 1.0;
+
+ // Store the page dimensions in Points.
+ mdPageHeightPts = dPageHeight;
+
+ Size aPageSize(convertPointToMm100(dPageWidth), convertPointToMm100(dPageHeight));
+
+ if (aPageSize.Width() && aPageSize.Height() && (!maScaleRect.IsEmpty()))
+ {
+ maOfs = maScaleRect.TopLeft();
+
+ if (aPageSize.Width() != (maScaleRect.GetWidth() - 1))
+ {
+ mfScaleX = static_cast<double>(maScaleRect.GetWidth() - 1)
+ / static_cast<double>(aPageSize.Width());
+ }
+
+ if (aPageSize.Height() != (maScaleRect.GetHeight() - 1))
+ {
+ mfScaleY = static_cast<double>(maScaleRect.GetHeight() - 1)
+ / static_cast<double>(aPageSize.Height());
+ }
+ }
+
+ mbMov = maOfs.X() != 0 || maOfs.Y() != 0;
+ mbSize = false;
+ maScaleX = Fraction(1, 1);
+ maScaleY = Fraction(1, 1);
+
+ if (aPageSize.Width() != (maScaleRect.GetWidth() - 1))
+ {
+ maScaleX = Fraction(maScaleRect.GetWidth() - 1, aPageSize.Width());
+ mbSize = true;
+ }
+
+ if (aPageSize.Height() != (maScaleRect.GetHeight() - 1))
+ {
+ maScaleY = Fraction(maScaleRect.GetHeight() - 1, aPageSize.Height());
+ mbSize = true;
+ }
+}
+
+size_t ImpSdrPdfImport::DoImport(SdrObjList& rOL, size_t nInsPos, int nPageNumber,
+ SvdProgressInfo* pProgrInfo)
+{
+ sal_uInt32 nActionsToReport(0);
+
+ // execute
+ DoObjects(pProgrInfo, &nActionsToReport, nPageNumber);
+
+ if (pProgrInfo)
+ {
+ pProgrInfo->ReportActions(nActionsToReport);
+ nActionsToReport = 0;
+ }
+
+ // MapMode scaling
+ MapScaling();
+
+ // To calculate the progress meter, we use GetActionSize()*3.
+ // However, maTmpList has a lower entry count limit than GetActionSize(),
+ // so the actions that were assumed were too much have to be re-added.
+ // nActionsToReport = (rMtf.GetActionSize() - maTmpList.size()) * 2;
+
+ // announce all currently unannounced rescales
+ if (pProgrInfo)
+ {
+ pProgrInfo->ReportRescales(nActionsToReport);
+ pProgrInfo->SetInsertCount(maTmpList.size());
+ }
+
+ nActionsToReport = 0;
+
+ // insert all objects cached in aTmpList now into rOL from nInsPos
+ nInsPos = std::min(nInsPos, rOL.GetObjCount());
+
+ for (SdrObject* pObj : maTmpList)
+ {
+ rOL.NbcInsertObject(pObj, nInsPos);
+ nInsPos++;
+
+ if (pProgrInfo)
+ {
+ nActionsToReport++;
+
+ if (nActionsToReport >= 32) // update all 32 actions
+ {
+ pProgrInfo->ReportInserts(nActionsToReport);
+ nActionsToReport = 0;
+ }
+ }
+ }
+
+ // report all remaining inserts for the last time
+ if (pProgrInfo)
+ {
+ pProgrInfo->ReportInserts(nActionsToReport);
+ }
+
+ return maTmpList.size();
+}
+
+void ImpSdrPdfImport::SetAttributes(SdrObject* pObj, bool bForceTextAttr)
+{
+ mbNoLine = false;
+ mbNoFill = false;
+ bool bLine(!bForceTextAttr);
+ bool bFill(!pObj || (pObj->IsClosedObj() && !bForceTextAttr));
+ bool bText(bForceTextAttr || (pObj && pObj->GetOutlinerParaObject()));
+
+ if (bLine)
+ {
+ if (mnLineWidth)
+ {
+ mpLineAttr->Put(XLineWidthItem(mnLineWidth));
+ }
+ else
+ {
+ mpLineAttr->Put(XLineWidthItem(0));
+ }
+
+ maOldLineColor = mpVD->GetLineColor();
+
+ if (mpVD->IsLineColor())
+ {
+ mpLineAttr->Put(XLineStyleItem(drawing::LineStyle_SOLID)); //TODO support dashed lines.
+ mpLineAttr->Put(XLineColorItem(OUString(), mpVD->GetLineColor()));
+ }
+ else
+ {
+ mpLineAttr->Put(XLineStyleItem(drawing::LineStyle_NONE));
+ }
+
+ mpLineAttr->Put(XLineJointItem(css::drawing::LineJoint_NONE));
+
+ // Add LineCap support
+ mpLineAttr->Put(XLineCapItem(gaLineCap));
+
+ if (((maDash.GetDots() && maDash.GetDotLen())
+ || (maDash.GetDashes() && maDash.GetDashLen()))
+ && maDash.GetDistance())
+ {
+ mpLineAttr->Put(XLineDashItem(OUString(), maDash));
+ }
+ else
+ {
+ mpLineAttr->Put(XLineDashItem(OUString(), XDash(css::drawing::DashStyle_RECT)));
+ }
+ }
+ else
+ {
+ mbNoLine = true;
+ }
+
+ if (bFill)
+ {
+ if (mpVD->IsFillColor())
+ {
+ mpFillAttr->Put(XFillStyleItem(drawing::FillStyle_SOLID));
+ mpFillAttr->Put(XFillColorItem(OUString(), mpVD->GetFillColor()));
+ }
+ else
+ {
+ mpFillAttr->Put(XFillStyleItem(drawing::FillStyle_NONE));
+ }
+ }
+ else
+ {
+ mbNoFill = true;
+ }
+
+ if (bText && mbFntDirty)
+ {
+ vcl::Font aFnt(mpVD->GetFont());
+ const sal_uInt32 nHeight(FRound(aFnt.GetFontSize().Height() * mfScaleY));
+
+ mpTextAttr->Put(SvxFontItem(aFnt.GetFamilyType(), aFnt.GetFamilyName(), aFnt.GetStyleName(),
+ aFnt.GetPitch(), aFnt.GetCharSet(), EE_CHAR_FONTINFO));
+ mpTextAttr->Put(SvxFontItem(aFnt.GetFamilyType(), aFnt.GetFamilyName(), aFnt.GetStyleName(),
+ aFnt.GetPitch(), aFnt.GetCharSet(), EE_CHAR_FONTINFO_CJK));
+ mpTextAttr->Put(SvxFontItem(aFnt.GetFamilyType(), aFnt.GetFamilyName(), aFnt.GetStyleName(),
+ aFnt.GetPitch(), aFnt.GetCharSet(), EE_CHAR_FONTINFO_CTL));
+ mpTextAttr->Put(SvxPostureItem(aFnt.GetItalic(), EE_CHAR_ITALIC));
+ mpTextAttr->Put(SvxWeightItem(aFnt.GetWeight(), EE_CHAR_WEIGHT));
+ mpTextAttr->Put(SvxFontHeightItem(nHeight, 100, EE_CHAR_FONTHEIGHT));
+ mpTextAttr->Put(SvxFontHeightItem(nHeight, 100, EE_CHAR_FONTHEIGHT_CJK));
+ mpTextAttr->Put(SvxFontHeightItem(nHeight, 100, EE_CHAR_FONTHEIGHT_CTL));
+ mpTextAttr->Put(SvxCharScaleWidthItem(100, EE_CHAR_FONTWIDTH));
+ mpTextAttr->Put(SvxUnderlineItem(aFnt.GetUnderline(), EE_CHAR_UNDERLINE));
+ mpTextAttr->Put(SvxOverlineItem(aFnt.GetOverline(), EE_CHAR_OVERLINE));
+ mpTextAttr->Put(SvxCrossedOutItem(aFnt.GetStrikeout(), EE_CHAR_STRIKEOUT));
+ mpTextAttr->Put(SvxShadowedItem(aFnt.IsShadow(), EE_CHAR_SHADOW));
+
+ // #i118485# Setting this item leads to problems (written #i118498# for this)
+ // mpTextAttr->Put(SvxAutoKernItem(aFnt.IsKerning(), EE_CHAR_KERNING));
+
+ mpTextAttr->Put(SvxWordLineModeItem(aFnt.IsWordLineMode(), EE_CHAR_WLM));
+ mpTextAttr->Put(SvxContourItem(aFnt.IsOutline(), EE_CHAR_OUTLINE));
+ mpTextAttr->Put(SvxColorItem(mpVD->GetTextColor(), EE_CHAR_COLOR));
+ //... svxfont textitem svditext
+ mbFntDirty = false;
+ }
+
+ if (!pObj)
+ return;
+
+ pObj->SetLayer(mnLayer);
+
+ if (bLine)
+ {
+ pObj->SetMergedItemSet(*mpLineAttr);
+ }
+
+ if (bFill)
+ {
+ pObj->SetMergedItemSet(*mpFillAttr);
+ }
+
+ if (bText)
+ {
+ pObj->SetMergedItemSet(*mpTextAttr);
+ pObj->SetMergedItem(SdrTextHorzAdjustItem(SDRTEXTHORZADJUST_LEFT));
+ }
+}
+
+void ImpSdrPdfImport::InsertObj(SdrObject* pObj, bool bScale)
+{
+ if (bScale && !maScaleRect.IsEmpty())
+ {
+ if (mbSize)
+ {
+ pObj->NbcResize(Point(), maScaleX, maScaleY);
+ }
+
+ if (mbMov)
+ {
+ pObj->NbcMove(Size(maOfs.X(), maOfs.Y()));
+ }
+ }
+
+ if (isClip())
+ {
+ const basegfx::B2DPolyPolygon aPoly(pObj->TakeXorPoly());
+ const basegfx::B2DRange aOldRange(aPoly.getB2DRange());
+ const SdrLayerID aOldLayer(pObj->GetLayer());
+ const SfxItemSet aOldItemSet(pObj->GetMergedItemSet());
+ const SdrGrafObj* pSdrGrafObj = dynamic_cast<SdrGrafObj*>(pObj);
+ const SdrTextObj* pSdrTextObj = dynamic_cast<SdrTextObj*>(pObj);
+
+ if (pSdrTextObj && pSdrTextObj->HasText())
+ {
+ // all text objects are created from ImportText and have no line or fill attributes, so
+ // it is okay to concentrate on the text itself
+ while (true)
+ {
+ const basegfx::B2DPolyPolygon aTextContour(pSdrTextObj->TakeContour());
+ const basegfx::B2DRange aTextRange(aTextContour.getB2DRange());
+ const basegfx::B2DRange aClipRange(maClip.getB2DRange());
+
+ // no overlap -> completely outside
+ if (!aClipRange.overlaps(aTextRange))
+ {
+ SdrObject::Free(pObj);
+ break;
+ }
+
+ // when the clip is a rectangle fast check for inside is possible
+ if (basegfx::utils::isRectangle(maClip) && aClipRange.isInside(aTextRange))
+ {
+ // completely inside ClipRect
+ break;
+ }
+
+ // here text needs to be clipped; to do so, convert to SdrObjects with polygons
+ // and add these recursively. Delete original object, do not add in this run
+ SdrObjectUniquePtr pConverted = pSdrTextObj->ConvertToPolyObj(true, true);
+ SdrObject::Free(pObj);
+
+ if (pConverted)
+ {
+ // recursively add created conversion; per definition this shall not
+ // contain further SdrTextObjs. Visit only non-group objects
+ SdrObjListIter aIter(*pConverted, SdrIterMode::DeepNoGroups);
+
+ // work with clones; the created conversion may contain group objects
+ // and when working with the original objects the loop itself could
+ // break and the cleanup later would be pretty complicated (only delete group
+ // objects, are these empty, ...?)
+ while (aIter.IsMore())
+ {
+ SdrObject* pCandidate = aIter.Next();
+ OSL_ENSURE(pCandidate && dynamic_cast<SdrObjGroup*>(pCandidate) == nullptr,
+ "SdrObjListIter with SdrIterMode::DeepNoGroups error (!)");
+ SdrObject* pNewClone(
+ pCandidate->CloneSdrObject(pCandidate->getSdrModelFromSdrObject()));
+
+ if (pNewClone)
+ {
+ InsertObj(pNewClone, false);
+ }
+ else
+ {
+ OSL_ENSURE(false, "SdrObject::Clone() failed (!)");
+ }
+ }
+ }
+
+ break;
+ }
+ }
+ else
+ {
+ BitmapEx aBitmapEx;
+
+ if (pSdrGrafObj)
+ {
+ aBitmapEx = pSdrGrafObj->GetGraphic().GetBitmapEx();
+ }
+
+ SdrObject::Free(pObj);
+
+ if (!aOldRange.isEmpty())
+ {
+ // clip against ClipRegion
+ const basegfx::B2DPolyPolygon aNewPoly(basegfx::utils::clipPolyPolygonOnPolyPolygon(
+ aPoly, maClip, true, !aPoly.isClosed()));
+ const basegfx::B2DRange aNewRange(aNewPoly.getB2DRange());
+
+ if (!aNewRange.isEmpty())
+ {
+ pObj = new SdrPathObj(
+ *mpModel, aNewPoly.isClosed() ? SdrObjKind::Polygon : SdrObjKind::PolyLine,
+ aNewPoly);
+
+ pObj->SetLayer(aOldLayer);
+ pObj->SetMergedItemSet(aOldItemSet);
+
+ if (!aBitmapEx.IsEmpty())
+ {
+ // aNewRange is inside of aOldRange and defines which part of aBitmapEx is used
+ const double fScaleX(aBitmapEx.GetSizePixel().Width()
+ / (aOldRange.getWidth() ? aOldRange.getWidth() : 1.0));
+ const double fScaleY(
+ aBitmapEx.GetSizePixel().Height()
+ / (aOldRange.getHeight() ? aOldRange.getHeight() : 1.0));
+ basegfx::B2DRange aPixel(aNewRange);
+ basegfx::B2DHomMatrix aTrans;
+
+ aTrans.translate(-aOldRange.getMinX(), -aOldRange.getMinY());
+ aTrans.scale(fScaleX, fScaleY);
+ aPixel.transform(aTrans);
+
+ const Size aOrigSizePixel(aBitmapEx.GetSizePixel());
+ const Point aClipTopLeft(
+ basegfx::fround(floor(std::max(0.0, aPixel.getMinX()))),
+ basegfx::fround(floor(std::max(0.0, aPixel.getMinY()))));
+ const Size aClipSize(
+ basegfx::fround(ceil(std::min(
+ static_cast<double>(aOrigSizePixel.Width()), aPixel.getWidth()))),
+ basegfx::fround(
+ ceil(std::min(static_cast<double>(aOrigSizePixel.Height()),
+ aPixel.getHeight()))));
+ const BitmapEx aClippedBitmap(aBitmapEx, aClipTopLeft, aClipSize);
+
+ pObj->SetMergedItem(XFillStyleItem(drawing::FillStyle_BITMAP));
+ pObj->SetMergedItem(XFillBitmapItem(OUString(), Graphic(aClippedBitmap)));
+ pObj->SetMergedItem(XFillBmpTileItem(false));
+ pObj->SetMergedItem(XFillBmpStretchItem(true));
+ }
+ }
+ }
+ }
+ }
+
+ if (!pObj)
+ return;
+
+ // #i111954# check object for visibility
+ // used are SdrPathObj, SdrRectObj, SdrCircObj, SdrGrafObj
+ bool bVisible(false);
+
+ if (pObj->HasLineStyle())
+ {
+ bVisible = true;
+ }
+
+ if (!bVisible && pObj->HasFillStyle())
+ {
+ bVisible = true;
+ }
+
+ if (!bVisible)
+ {
+ SdrTextObj* pTextObj = dynamic_cast<SdrTextObj*>(pObj);
+
+ if (pTextObj && pTextObj->HasText())
+ {
+ bVisible = true;
+ }
+ }
+
+ if (!bVisible)
+ {
+ SdrGrafObj* pGrafObj = dynamic_cast<SdrGrafObj*>(pObj);
+
+ if (pGrafObj)
+ {
+ // this may be refined to check if the graphic really is visible. It
+ // is here to ensure that graphic objects without fill, line and text
+ // get created
+ bVisible = true;
+ }
+ }
+
+ if (!bVisible)
+ {
+ SdrObject::Free(pObj);
+ }
+ else
+ {
+ maTmpList.push_back(pObj);
+
+ if (dynamic_cast<SdrPathObj*>(pObj))
+ {
+ const bool bClosed(pObj->IsClosedObj());
+
+ mbLastObjWasPolyWithoutLine = mbNoLine && bClosed;
+ }
+ else
+ {
+ mbLastObjWasPolyWithoutLine = false;
+ }
+ }
+}
+
+bool ImpSdrPdfImport::CheckLastPolyLineAndFillMerge(const basegfx::B2DPolyPolygon& rPolyPolygon)
+{
+ // #i73407# reformulation to use new B2DPolygon classes
+ if (mbLastObjWasPolyWithoutLine)
+ {
+ SdrObject* pTmpObj = !maTmpList.empty() ? maTmpList[maTmpList.size() - 1] : nullptr;
+ SdrPathObj* pLastPoly = dynamic_cast<SdrPathObj*>(pTmpObj);
+
+ if (pLastPoly)
+ {
+ if (pLastPoly->GetPathPoly() == rPolyPolygon)
+ {
+ SetAttributes(nullptr);
+
+ if (!mbNoLine && mbNoFill)
+ {
+ pLastPoly->SetMergedItemSet(*mpLineAttr);
+
+ return true;
+ }
+ }
+ }
+ }
+
+ return false;
+}
+
+void ImpSdrPdfImport::checkClip()
+{
+ if (mpVD->IsClipRegion())
+ {
+ maClip = mpVD->GetClipRegion().GetAsB2DPolyPolygon();
+
+ if (isClip())
+ {
+ const basegfx::B2DHomMatrix aTransform(basegfx::utils::createScaleTranslateB2DHomMatrix(
+ mfScaleX, mfScaleY, maOfs.X(), maOfs.Y()));
+
+ maClip.transform(aTransform);
+ }
+ }
+}
+
+bool ImpSdrPdfImport::isClip() const { return !maClip.getB2DRange().isEmpty(); }
+void ImpSdrPdfImport::ImportPdfObject(
+ std::unique_ptr<vcl::pdf::PDFiumPageObject> const& pPageObject,
+ std::unique_ptr<vcl::pdf::PDFiumTextPage> const& pTextPage, int nPageObjectIndex)
+{
+ if (!pPageObject)
+ return;
+
+ const vcl::pdf::PDFPageObjectType ePageObjectType = pPageObject->getType();
+ switch (ePageObjectType)
+ {
+ case vcl::pdf::PDFPageObjectType::Text:
+ ImportText(pPageObject, pTextPage, nPageObjectIndex);
+ break;
+ case vcl::pdf::PDFPageObjectType::Path:
+ ImportPath(pPageObject, nPageObjectIndex);
+ break;
+ case vcl::pdf::PDFPageObjectType::Image:
+ ImportImage(pPageObject, nPageObjectIndex);
+ break;
+ case vcl::pdf::PDFPageObjectType::Shading:
+ SAL_WARN("sd.filter", "Got page object SHADING: " << nPageObjectIndex);
+ break;
+ case vcl::pdf::PDFPageObjectType::Form:
+ ImportForm(pPageObject, pTextPage, nPageObjectIndex);
+ break;
+ default:
+ SAL_WARN("sd.filter", "Unknown PDF page object #" << nPageObjectIndex << " of type: "
+ << static_cast<int>(ePageObjectType));
+ break;
+ }
+}
+
+void ImpSdrPdfImport::ImportForm(std::unique_ptr<vcl::pdf::PDFiumPageObject> const& pPageObject,
+ std::unique_ptr<vcl::pdf::PDFiumTextPage> const& pTextPage,
+ int /*nPageObjectIndex*/)
+{
+ // Get the form matrix to perform correct translation/scaling of the form sub-objects.
+ const basegfx::B2DHomMatrix aOldMatrix = maCurrentMatrix;
+
+ maCurrentMatrix = pPageObject->getMatrix();
+
+ const int nCount = pPageObject->getFormObjectCount();
+ for (int nIndex = 0; nIndex < nCount; ++nIndex)
+ {
+ auto pFormObject = pPageObject->getFormObject(nIndex);
+
+ ImportPdfObject(pFormObject, pTextPage, -1);
+ }
+
+ // Restore the old one.
+ maCurrentMatrix = aOldMatrix;
+}
+
+void ImpSdrPdfImport::ImportText(std::unique_ptr<vcl::pdf::PDFiumPageObject> const& pPageObject,
+ std::unique_ptr<vcl::pdf::PDFiumTextPage> const& pTextPage,
+ int /*nPageObjectIndex*/)
+{
+ basegfx::B2DRectangle aTextRect = pPageObject->getBounds();
+ basegfx::B2DHomMatrix aMatrix = pPageObject->getMatrix();
+
+ basegfx::B2DHomMatrix aTextMatrix(maCurrentMatrix);
+
+ aTextRect *= aTextMatrix;
+ const tools::Rectangle aRect = PointsToLogic(aTextRect.getMinX(), aTextRect.getMaxX(),
+ aTextRect.getMinY(), aTextRect.getMaxY());
+
+ OUString sText = pPageObject->getText(pTextPage);
+
+ const double dFontSize = pPageObject->getFontSize();
+ double dFontSizeH = fabs(std::hypot(aMatrix.a(), aMatrix.c()) * dFontSize);
+ double dFontSizeV = fabs(std::hypot(aMatrix.b(), aMatrix.d()) * dFontSize);
+
+ dFontSizeH = convertPointToMm100(dFontSizeH);
+ dFontSizeV = convertPointToMm100(dFontSizeV);
+
+ const Size aFontSize(dFontSizeH, dFontSizeV);
+ vcl::Font aFnt = mpVD->GetFont();
+ if (aFontSize != aFnt.GetFontSize())
+ {
+ aFnt.SetFontSize(aFontSize);
+ mpVD->SetFont(aFnt);
+ mbFntDirty = true;
+ }
+
+ OUString sFontName = pPageObject->getFontName();
+ if (!sFontName.isEmpty() && sFontName != aFnt.GetFamilyName())
+ {
+ aFnt.SetFamilyName(sFontName);
+ mpVD->SetFont(aFnt);
+ mbFntDirty = true;
+ }
+
+ Color aTextColor(COL_TRANSPARENT);
+ bool bFill = false;
+ bool bUse = true;
+ switch (pPageObject->getTextRenderMode())
+ {
+ case vcl::pdf::PDFTextRenderMode::Fill:
+ case vcl::pdf::PDFTextRenderMode::FillClip:
+ case vcl::pdf::PDFTextRenderMode::FillStroke:
+ case vcl::pdf::PDFTextRenderMode::FillStrokeClip:
+ bFill = true;
+ break;
+ case vcl::pdf::PDFTextRenderMode::Stroke:
+ case vcl::pdf::PDFTextRenderMode::StrokeClip:
+ case vcl::pdf::PDFTextRenderMode::Unknown:
+ break;
+ case vcl::pdf::PDFTextRenderMode::Invisible:
+ case vcl::pdf::PDFTextRenderMode::Clip:
+ bUse = false;
+ break;
+ }
+ if (bUse)
+ {
+ Color aColor = bFill ? pPageObject->getFillColor() : pPageObject->getStrokeColor();
+ if (aColor != COL_TRANSPARENT)
+ aTextColor = aColor.GetRGBColor();
+ }
+
+ if (aTextColor != mpVD->GetTextColor())
+ {
+ mpVD->SetTextColor(aTextColor);
+ mbFntDirty = true;
+ }
+
+ InsertTextObject(aRect.TopLeft(), aRect.GetSize(), sText);
+}
+
+void ImpSdrPdfImport::InsertTextObject(const Point& rPos, const Size& rSize, const OUString& rStr)
+{
+ // calc text box size, add 5% to make it fit safely
+
+ FontMetric aFontMetric(mpVD->GetFontMetric());
+ vcl::Font aFont(mpVD->GetFont());
+ TextAlign eAlignment(aFont.GetAlignment());
+
+ // sal_Int32 nTextWidth = static_cast<sal_Int32>(mpVD->GetTextWidth(rStr) * mfScaleX);
+ sal_Int32 nTextHeight = static_cast<sal_Int32>(mpVD->GetTextHeight() * mfScaleY);
+
+ Point aPosition(FRound(rPos.X() * mfScaleX + maOfs.X()),
+ FRound(rPos.Y() * mfScaleY + maOfs.Y()));
+ Size aSize(FRound(rSize.Width() * mfScaleX), FRound(rSize.Height() * mfScaleY));
+
+ if (eAlignment == ALIGN_BASELINE)
+ aPosition.AdjustY(-FRound(aFontMetric.GetAscent() * mfScaleY));
+ else if (eAlignment == ALIGN_BOTTOM)
+ aPosition.AdjustY(-nTextHeight);
+
+ tools::Rectangle aTextRect(aPosition, aSize);
+ SdrRectObj* pText = new SdrRectObj(*mpModel, SdrObjKind::Text, aTextRect);
+
+ pText->SetMergedItem(makeSdrTextUpperDistItem(0));
+ pText->SetMergedItem(makeSdrTextLowerDistItem(0));
+ pText->SetMergedItem(makeSdrTextRightDistItem(0));
+ pText->SetMergedItem(makeSdrTextLeftDistItem(0));
+
+ if (aFont.GetAverageFontWidth())
+ {
+ pText->ClearMergedItem(SDRATTR_TEXT_AUTOGROWWIDTH);
+ pText->SetMergedItem(makeSdrTextAutoGrowHeightItem(false));
+ // don't let the margins eat the space needed for the text
+ pText->SetMergedItem(SdrTextFitToSizeTypeItem(drawing::TextFitToSizeType_ALLLINES));
+ }
+ else
+ {
+ pText->SetMergedItem(makeSdrTextAutoGrowWidthItem(true));
+ }
+
+ pText->SetLayer(mnLayer);
+ pText->NbcSetText(rStr);
+ SetAttributes(pText, true);
+ pText->SetSnapRect(aTextRect);
+
+ if (!aFont.IsTransparent())
+ {
+ SfxItemSetFixed<XATTR_FILL_FIRST, XATTR_FILL_LAST> aAttr(*mpFillAttr->GetPool());
+ aAttr.Put(XFillStyleItem(drawing::FillStyle_SOLID));
+ aAttr.Put(XFillColorItem(OUString(), aFont.GetFillColor()));
+ pText->SetMergedItemSet(aAttr);
+ }
+ Degree100 nAngle = to<Degree100>(aFont.GetOrientation());
+ if (nAngle)
+ pText->SdrAttrObj::NbcRotate(aPosition, nAngle);
+ InsertObj(pText, false);
+}
+
+void ImpSdrPdfImport::MapScaling()
+{
+ const size_t nCount(maTmpList.size());
+ const MapMode& rMap = mpVD->GetMapMode();
+ Point aMapOrg(rMap.GetOrigin());
+ bool bMov2(aMapOrg.X() != 0 || aMapOrg.Y() != 0);
+
+ if (bMov2)
+ {
+ for (size_t i = mnMapScalingOfs; i < nCount; i++)
+ {
+ SdrObject* pObj = maTmpList[i];
+
+ pObj->NbcMove(Size(aMapOrg.X(), aMapOrg.Y()));
+ }
+ }
+
+ mnMapScalingOfs = nCount;
+}
+
+void ImpSdrPdfImport::ImportImage(std::unique_ptr<vcl::pdf::PDFiumPageObject> const& pPageObject,
+ int /*nPageObjectIndex*/)
+{
+ std::unique_ptr<vcl::pdf::PDFiumBitmap> bitmap = pPageObject->getImageBitmap();
+ if (!bitmap)
+ {
+ SAL_WARN("sd.filter", "Failed to get IMAGE");
+ return;
+ }
+
+ const vcl::pdf::PDFBitmapType format = bitmap->getFormat();
+ if (format == vcl::pdf::PDFBitmapType::Unknown)
+ {
+ SAL_WARN("sd.filter", "Failed to get IMAGE format");
+ return;
+ }
+
+ const unsigned char* pBuf = bitmap->getBuffer();
+ const int nWidth = bitmap->getWidth();
+ const int nHeight = bitmap->getHeight();
+ const int nStride = bitmap->getStride();
+ BitmapEx aBitmap(Size(nWidth, nHeight), vcl::PixelFormat::N24_BPP);
+
+ switch (format)
+ {
+ case vcl::pdf::PDFBitmapType::BGR:
+ ReadRawDIB(aBitmap, pBuf, ScanlineFormat::N24BitTcBgr, nHeight, nStride);
+ break;
+ case vcl::pdf::PDFBitmapType::BGRx:
+ ReadRawDIB(aBitmap, pBuf, ScanlineFormat::N32BitTcRgba, nHeight, nStride);
+ break;
+ case vcl::pdf::PDFBitmapType::BGRA:
+ ReadRawDIB(aBitmap, pBuf, ScanlineFormat::N32BitTcBgra, nHeight, nStride);
+ break;
+ default:
+ SAL_WARN("sd.filter", "Got IMAGE width: " << nWidth << ", height: " << nHeight
+ << ", stride: " << nStride
+ << ", format: " << static_cast<int>(format));
+ break;
+ }
+
+ basegfx::B2DRectangle aBounds = pPageObject->getBounds();
+ float left = aBounds.getMinX();
+ // Upside down.
+ float bottom = aBounds.getMinY();
+ float right = aBounds.getMaxX();
+ // Upside down.
+ float top = aBounds.getMaxY();
+ tools::Rectangle aRect = PointsToLogic(left, right, top, bottom);
+ aRect.AdjustRight(1);
+ aRect.AdjustBottom(1);
+
+ SdrGrafObj* pGraf = new SdrGrafObj(*mpModel, Graphic(aBitmap), aRect);
+
+ // This action is not creating line and fill, set directly, do not use SetAttributes(..)
+ pGraf->SetMergedItem(XLineStyleItem(drawing::LineStyle_NONE));
+ pGraf->SetMergedItem(XFillStyleItem(drawing::FillStyle_NONE));
+ InsertObj(pGraf);
+}
+
+void ImpSdrPdfImport::ImportPath(std::unique_ptr<vcl::pdf::PDFiumPageObject> const& pPageObject,
+ int /*nPageObjectIndex*/)
+{
+ auto aPathMatrix = pPageObject->getMatrix();
+
+ aPathMatrix *= maCurrentMatrix;
+
+ basegfx::B2DPolyPolygon aPolyPoly;
+ basegfx::B2DPolygon aPoly;
+ std::vector<basegfx::B2DPoint> aBezier;
+
+ const int nSegments = pPageObject->getPathSegmentCount();
+ for (int nSegmentIndex = 0; nSegmentIndex < nSegments; ++nSegmentIndex)
+ {
+ auto pPathSegment = pPageObject->getPathSegment(nSegmentIndex);
+ if (pPathSegment != nullptr)
+ {
+ basegfx::B2DPoint aB2DPoint = pPathSegment->getPoint();
+ aB2DPoint *= aPathMatrix;
+
+ const bool bClose = pPathSegment->isClosed();
+ if (bClose)
+ aPoly.setClosed(bClose); // TODO: Review
+
+ Point aPoint = PointsToLogic(aB2DPoint.getX(), aB2DPoint.getY());
+ aB2DPoint.setX(aPoint.X());
+ aB2DPoint.setY(aPoint.Y());
+
+ const vcl::pdf::PDFSegmentType eSegmentType = pPathSegment->getType();
+ switch (eSegmentType)
+ {
+ case vcl::pdf::PDFSegmentType::Lineto:
+ aPoly.append(aB2DPoint);
+ break;
+
+ case vcl::pdf::PDFSegmentType::Bezierto:
+ aBezier.emplace_back(aB2DPoint.getX(), aB2DPoint.getY());
+ if (aBezier.size() == 3)
+ {
+ aPoly.appendBezierSegment(aBezier[0], aBezier[1], aBezier[2]);
+ aBezier.clear();
+ }
+ break;
+
+ case vcl::pdf::PDFSegmentType::Moveto:
+ // New Poly.
+ if (aPoly.count() > 0)
+ {
+ aPolyPoly.append(aPoly, 1);
+ aPoly.clear();
+ }
+
+ aPoly.append(aB2DPoint);
+ break;
+
+ case vcl::pdf::PDFSegmentType::Unknown:
+ default:
+ SAL_WARN("sd.filter", "Unknown path segment type in PDF: "
+ << static_cast<int>(eSegmentType));
+ break;
+ }
+ }
+ }
+
+ if (aBezier.size() == 3)
+ {
+ aPoly.appendBezierSegment(aBezier[0], aBezier[1], aBezier[2]);
+ aBezier.clear();
+ }
+
+ if (aPoly.count() > 0)
+ {
+ aPolyPoly.append(aPoly, 1);
+ aPoly.clear();
+ }
+
+ const basegfx::B2DHomMatrix aTransform(
+ basegfx::utils::createScaleTranslateB2DHomMatrix(mfScaleX, mfScaleY, maOfs.X(), maOfs.Y()));
+ aPolyPoly.transform(aTransform);
+
+ float fWidth = pPageObject->getStrokeWidth();
+ const double dWidth = 0.5 * fabs(std::hypot(aPathMatrix.a(), aPathMatrix.c()) * fWidth);
+ mnLineWidth = convertPointToMm100(dWidth);
+
+ vcl::pdf::PDFFillMode nFillMode = vcl::pdf::PDFFillMode::Alternate;
+ bool bStroke = true; // Assume we have to draw, unless told otherwise.
+ if (pPageObject->getDrawMode(nFillMode, bStroke))
+ {
+ if (nFillMode == vcl::pdf::PDFFillMode::Alternate)
+ mpVD->SetDrawMode(DrawModeFlags::Default);
+ else if (nFillMode == vcl::pdf::PDFFillMode::Winding)
+ mpVD->SetDrawMode(DrawModeFlags::Default);
+ else
+ mpVD->SetDrawMode(DrawModeFlags::NoFill);
+ }
+
+ mpVD->SetFillColor(pPageObject->getFillColor());
+
+ if (bStroke)
+ {
+ mpVD->SetLineColor(pPageObject->getStrokeColor());
+ }
+ else
+ mpVD->SetLineColor(COL_TRANSPARENT);
+
+ if (!mbLastObjWasPolyWithoutLine || !CheckLastPolyLineAndFillMerge(aPolyPoly))
+ {
+ SdrPathObj* pPath = new SdrPathObj(*mpModel, SdrObjKind::Polygon, aPolyPoly);
+ SetAttributes(pPath);
+ InsertObj(pPath, false);
+ }
+}
+
+Point ImpSdrPdfImport::PointsToLogic(double x, double y) const
+{
+ y = correctVertOrigin(y);
+
+ Point aPos(convertPointToMm100(x), convertPointToMm100(y));
+ return aPos;
+}
+
+tools::Rectangle ImpSdrPdfImport::PointsToLogic(double left, double right, double top,
+ double bottom) const
+{
+ top = correctVertOrigin(top);
+ bottom = correctVertOrigin(bottom);
+
+ Point aPos(convertPointToMm100(left), convertPointToMm100(top));
+ Size aSize(convertPointToMm100(right - left), convertPointToMm100(bottom - top));
+
+ return tools::Rectangle(aPos, aSize);
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/svdraw/svdpntv.cxx b/svx/source/svdraw/svdpntv.cxx
new file mode 100644
index 000000000..f403ddd57
--- /dev/null
+++ b/svx/source/svdraw/svdpntv.cxx
@@ -0,0 +1,1206 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * 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/svdpntv.hxx>
+#include <vcl/weld.hxx>
+#include <vcl/window.hxx>
+#include <svx/sdrpaintwindow.hxx>
+#include <svx/svdmodel.hxx>
+
+#include <svx/svdpage.hxx>
+#include <svx/svdpagv.hxx>
+#include <svl/hint.hxx>
+
+#include <svx/svdview.hxx>
+#include <svx/svdglue.hxx>
+#include <svx/svdobj.hxx>
+#include <sxlayitm.hxx>
+#include <svl/itemiter.hxx>
+#include <editeng/eeitem.hxx>
+#include <svl/whiter.hxx>
+#include <svl/style.hxx>
+#include <svx/sdrpagewindow.hxx>
+#include <vcl/svapp.hxx>
+#include <svx/sdr/contact/objectcontact.hxx>
+#include <svx/sdr/animation/objectanimator.hxx>
+#include <drawinglayer/primitive2d/metafileprimitive2d.hxx>
+#include <drawinglayer/converters.hxx>
+#include <basegfx/matrix/b2dhommatrixtools.hxx>
+#include <comphelper/lok.hxx>
+#include <svx/svdviter.hxx>
+#include <svtools/optionsdrawinglayer.hxx>
+#include <osl/diagnose.h>
+
+using namespace ::com::sun::star;
+
+// interface to SdrPaintWindow
+
+SdrPaintWindow* SdrPaintView::FindPaintWindow(const OutputDevice& rOut) const
+{
+ // back to loop - there is more to test than a std::find_if and a lambda can do
+ for(auto& candidate : maPaintWindows)
+ {
+ if(&(candidate->GetOutputDevice()) == &rOut)
+ {
+ return candidate.get();
+ }
+
+ // check for patched to allow finding in that state, too
+ if(nullptr != candidate->getPatched() && &(candidate->getPatched()->GetOutputDevice()) == &rOut)
+ {
+ return candidate->getPatched();
+ }
+ }
+
+ return nullptr;
+}
+
+SdrPaintWindow* SdrPaintView::GetPaintWindow(sal_uInt32 nIndex) const
+{
+ return maPaintWindows[nIndex].get();
+}
+
+void SdrPaintView::DeletePaintWindow(const SdrPaintWindow& rOld)
+{
+ auto aFindResult = ::std::find_if(maPaintWindows.begin(), maPaintWindows.end(),
+ [&](const std::unique_ptr<SdrPaintWindow>& p) { return p.get() == &rOld; });
+
+ if(aFindResult != maPaintWindows.end())
+ {
+ maPaintWindows.erase(aFindResult);
+ }
+}
+
+OutputDevice* SdrPaintView::GetFirstOutputDevice() const
+{
+ if(PaintWindowCount())
+ {
+ return &(GetPaintWindow(0)->GetOutputDevice());
+ }
+
+ return nullptr;
+}
+
+
+SvxViewChangedHint::SvxViewChangedHint()
+{
+}
+
+
+BitmapEx convertMetafileToBitmapEx(
+ const GDIMetaFile& rMtf,
+ const basegfx::B2DRange& rTargetRange,
+ const sal_uInt32 nMaximumQuadraticPixels)
+{
+ BitmapEx aBitmapEx;
+
+ if(rMtf.GetActionSize())
+ {
+ const drawinglayer::primitive2d::Primitive2DReference aMtf(
+ new drawinglayer::primitive2d::MetafilePrimitive2D(
+ basegfx::utils::createScaleTranslateB2DHomMatrix(
+ rTargetRange.getRange(),
+ rTargetRange.getMinimum()),
+ rMtf));
+ aBitmapEx = drawinglayer::convertPrimitive2DContainerToBitmapEx(
+ drawinglayer::primitive2d::Primitive2DContainer { aMtf },
+ rTargetRange,
+ nMaximumQuadraticPixels);
+ }
+
+ return aBitmapEx;
+}
+
+SdrPaintView::SdrPaintView(SdrModel& rSdrModel, OutputDevice* pOut)
+ : mrSdrModelFromSdrView(rSdrModel)
+ , mpModel(&rSdrModel)
+ , mpActualOutDev(nullptr)
+ , mpDragWin(nullptr)
+ , mpDefaultStyleSheet(nullptr)
+ , maDefaultAttr(rSdrModel.GetItemPool())
+ , maComeBackIdle( "svx::SdrPaintView aComeBackIdle" )
+ , meAnimationMode(SdrAnimationMode::Animate)
+ , mnHitTolPix(2)
+ , mnMinMovPix(3)
+ , mnHitTolLog(0)
+ , mnMinMovLog(0)
+ , mbPageVisible(true)
+ , mbPageShadowVisible(true)
+ , mbPageBorderVisible(true)
+ , mbBordVisible(true)
+ , mbGridVisible(true)
+ , mbGridFront(false)
+ , mbHlplVisible(true)
+ , mbHlplFront(true)
+ , mbGlueVisible(false)
+ , mbGlueVisible2(false)
+ , mbGlueVisible3(false)
+ , mbGlueVisible4(false)
+ , mbSomeObjChgdFlag(false)
+ , mbSwapAsynchron(false)
+ , mbPrintPreview(false)
+ , mbAnimationPause(false)
+ , mbBufferedOutputAllowed(false)
+ , mbBufferedOverlayAllowed(false)
+ , mbPagePaintingAllowed(true)
+ , mbPreviewRenderer(false)
+ , mbHideOle(false)
+ , mbHideChart(false)
+ , mbHideDraw(false)
+ , mbHideFormControl(false)
+ , maGridColor(COL_BLACK)
+{
+ maComeBackIdle.SetPriority(TaskPriority::REPAINT);
+ maComeBackIdle.SetInvokeHandler(LINK(this,SdrPaintView,ImpComeBackHdl));
+
+ if (mpModel)
+ SetDefaultStyleSheet(mpModel->GetDefaultStyleSheet(), true);
+
+ if (pOut)
+ AddWindowToPaintView(pOut, nullptr);
+
+ maColorConfig.AddListener(this);
+ onChangeColorConfig();
+}
+
+SdrPaintView::~SdrPaintView()
+{
+ if (mpDefaultStyleSheet)
+ EndListening(*mpDefaultStyleSheet);
+
+ maColorConfig.RemoveListener(this);
+ ClearPageView();
+
+ // delete existing SdrPaintWindows
+ maPaintWindows.clear();
+}
+
+
+void SdrPaintView::Notify(SfxBroadcaster& rBC, const SfxHint& rHint)
+{
+ //If the stylesheet has been destroyed
+ if (&rBC == mpDefaultStyleSheet)
+ {
+ if (rHint.GetId() == SfxHintId::Dying)
+ mpDefaultStyleSheet = nullptr;
+ return;
+ }
+
+ if (rHint.GetId() != SfxHintId::ThisIsAnSdrHint)
+ return;
+ const SdrHint* pSdrHint = static_cast<const SdrHint*>(&rHint);
+ SdrHintKind eKind = pSdrHint->GetKind();
+ if (eKind==SdrHintKind::ObjectChange || eKind==SdrHintKind::ObjectInserted || eKind==SdrHintKind::ObjectRemoved)
+ {
+ bool bObjChg = !mbSomeObjChgdFlag; // if true, evaluate for ComeBack timer
+ if (bObjChg)
+ {
+ mbSomeObjChgdFlag=true;
+ maComeBackIdle.Start();
+ }
+ }
+
+ if (eKind==SdrHintKind::PageOrderChange)
+ {
+ const SdrPage* pPg=pSdrHint->GetPage();
+ if (pPg && !pPg->IsInserted())
+ {
+ if(mpPageView && mpPageView->GetPage() == pPg)
+ {
+ HideSdrPage();
+ }
+ }
+ }
+}
+
+void SdrPaintView::ConfigurationChanged( ::utl::ConfigurationBroadcaster* , ConfigurationHints )
+{
+ onChangeColorConfig();
+ InvalidateAllWin();
+}
+
+IMPL_LINK_NOARG(SdrPaintView, ImpComeBackHdl, Timer *, void)
+{
+ if (mbSomeObjChgdFlag) {
+ mbSomeObjChgdFlag=false;
+ ModelHasChanged();
+ }
+}
+
+void SdrPaintView::FlushComeBackTimer() const
+{
+ if (mbSomeObjChgdFlag) {
+ // casting to nonconst
+ const_cast<SdrPaintView*>(this)->ImpComeBackHdl(&const_cast<SdrPaintView*>(this)->maComeBackIdle);
+ const_cast<SdrPaintView*>(this)->maComeBackIdle.Stop();
+ }
+}
+
+void SdrPaintView::ModelHasChanged()
+{
+ // broadcast to all PageViews
+ if(mpPageView && !mpPageView->GetPage()->IsInserted())
+ {
+ HideSdrPage();
+ }
+
+ // test mpPageView here again, HideSdrPage() may have invalidated it.
+ if(mpPageView)
+ {
+ mpPageView->ModelHasChanged();
+ }
+}
+
+
+bool SdrPaintView::IsAction() const
+{
+ return false;
+}
+
+void SdrPaintView::MovAction(const Point&)
+{
+}
+
+void SdrPaintView::EndAction()
+{
+}
+
+void SdrPaintView::BckAction()
+{
+}
+
+void SdrPaintView::BrkAction()
+{
+}
+
+void SdrPaintView::TakeActionRect(tools::Rectangle&) const
+{
+}
+
+
+// info about TextEdit. Default is false.
+bool SdrPaintView::IsTextEdit() const
+{
+ return false;
+}
+
+sal_uInt16 SdrPaintView::ImpGetMinMovLogic(short nMinMov, const OutputDevice* pOut) const
+{
+ if (nMinMov>=0) return sal_uInt16(nMinMov);
+ if (pOut==nullptr)
+ {
+ pOut = GetFirstOutputDevice();
+ }
+ if (pOut!=nullptr) {
+ return short(-pOut->PixelToLogic(Size(nMinMov,0)).Width());
+ } else {
+ return 0;
+ }
+}
+
+sal_uInt16 SdrPaintView::ImpGetHitTolLogic(short nHitTol, const OutputDevice* pOut) const
+{
+ if (nHitTol>=0) return sal_uInt16(nHitTol);
+ if (pOut==nullptr)
+ {
+ pOut = GetFirstOutputDevice();
+ }
+ if (pOut!=nullptr) {
+ return short(-pOut->PixelToLogic(Size(nHitTol,0)).Width());
+ } else {
+ return 0;
+ }
+}
+
+void SdrPaintView::TheresNewMapMode()
+{
+ if (mpActualOutDev) {
+ mnHitTolLog=static_cast<sal_uInt16>(mpActualOutDev->PixelToLogic(Size(mnHitTolPix,0)).Width());
+ mnMinMovLog=static_cast<sal_uInt16>(mpActualOutDev->PixelToLogic(Size(mnMinMovPix,0)).Width());
+ }
+}
+
+void SdrPaintView::SetActualWin(const OutputDevice* pWin)
+{
+ mpActualOutDev = const_cast<OutputDevice *>(pWin);
+ TheresNewMapMode();
+}
+
+
+void SdrPaintView::ClearPageView()
+{
+ BrkAction();
+
+ if(mpPageView)
+ {
+ InvalidateAllWin();
+ mpPageView.reset();
+ }
+}
+
+SdrPageView* SdrPaintView::ShowSdrPage(SdrPage* pPage)
+{
+ if(pPage && (!mpPageView || mpPageView->GetPage() != pPage))
+ {
+ if(mpPageView)
+ {
+ InvalidateAllWin();
+ mpPageView.reset();
+ }
+
+ if (SdrView *pView = dynamic_cast<SdrView*>(this))
+ {
+ mpPageView.reset(new SdrPageView(pPage, *pView));
+ mpPageView->Show();
+ }
+ }
+
+ return mpPageView.get();
+}
+
+void SdrPaintView::HideSdrPage()
+{
+ if(mpPageView)
+ {
+ mpPageView->Hide();
+ mpPageView.reset();
+ }
+}
+
+void SdrPaintView::AddWindowToPaintView(OutputDevice* pNewWin, vcl::Window *pWindow)
+{
+ DBG_ASSERT(pNewWin, "SdrPaintView::AddWindowToPaintView: No OutputDevice(!)");
+ SdrPaintWindow* pNewPaintWindow = new SdrPaintWindow(*this, *pNewWin, pWindow);
+ maPaintWindows.emplace_back(pNewPaintWindow);
+
+ if(mpPageView)
+ {
+ mpPageView->AddPaintWindowToPageView(*pNewPaintWindow);
+ }
+}
+
+void SdrPaintView::DeleteWindowFromPaintView(OutputDevice* pOldWin)
+{
+ assert(pOldWin && "SdrPaintView::DeleteWindowFromPaintView: No OutputDevice(!)");
+ SdrPaintWindow* pCandidate = FindPaintWindow(*pOldWin);
+
+ if(pCandidate)
+ {
+ if(mpPageView)
+ {
+ mpPageView->RemovePaintWindowFromPageView(*pCandidate);
+ }
+
+ DeletePaintWindow(*pCandidate);
+ }
+}
+
+void SdrPaintView::SetLayerVisible(const OUString& rName, bool bShow)
+{
+ if(mpPageView)
+ {
+ mpPageView->SetLayerVisible(rName, bShow);
+ }
+
+ InvalidateAllWin();
+}
+
+bool SdrPaintView::IsLayerVisible(const OUString& rName) const
+{
+ if(mpPageView)
+ {
+ return mpPageView->IsLayerVisible(rName);
+ }
+
+ return false;
+}
+
+void SdrPaintView::SetLayerLocked(const OUString& rName, bool bLock)
+{
+ if(mpPageView)
+ {
+ mpPageView->SetLayerLocked(rName,bLock);
+ }
+}
+
+bool SdrPaintView::IsLayerLocked(const OUString& rName) const
+{
+ if(mpPageView)
+ {
+ return mpPageView->IsLayerLocked(rName);
+ }
+
+ return false;
+}
+
+void SdrPaintView::SetLayerPrintable(const OUString& rName, bool bPrn)
+{
+ if(mpPageView)
+ {
+ mpPageView->SetLayerPrintable(rName,bPrn);
+ }
+}
+
+bool SdrPaintView::IsLayerPrintable(const OUString& rName) const
+{
+ if(mpPageView)
+ {
+ return mpPageView->IsLayerPrintable(rName);
+ }
+
+ return false;
+}
+
+void SdrPaintView::PrePaint()
+{
+ if(mpPageView)
+ {
+ mpPageView->PrePaint();
+ }
+}
+
+
+// #define SVX_REPAINT_TIMER_TEST
+
+void SdrPaintView::CompleteRedraw(OutputDevice* pOut, const vcl::Region& rReg, sdr::contact::ViewObjectContactRedirector* pRedirector)
+{
+#ifdef SVX_REPAINT_TIMER_TEST
+#define REMEMBERED_TIMES_COUNT (10)
+ static bool bDoTimerTest(false);
+ static bool bTimesInited(false);
+ static sal_uInt32 nRepeatCount(10);
+ static double fLastTimes[REMEMBERED_TIMES_COUNT];
+ const sal_uInt64 nStartTime(tools::Time::GetSystemTicks());
+ sal_uInt32 count(1);
+ sal_uInt32 a;
+
+ if(bDoTimerTest)
+ {
+ count = nRepeatCount;
+ }
+
+ for(a = 0; a < count; a++)
+ {
+#endif // SVX_REPAINT_TIMER_TEST
+
+ // #i74769# check if pOut is a win and has a ClipRegion. If Yes, the Region
+ // rReg may be made more granular (fine) with using it. Normally, rReg
+ // does come from Window::Paint() anyways and thus is based on a single
+ // rectangle which was derived from exactly that repaint region
+ vcl::Region aOptimizedRepaintRegion(rReg);
+
+ if(pOut && OUTDEV_WINDOW == pOut->GetOutDevType())
+ {
+ vcl::Window* pWindow = pOut->GetOwnerWindow();
+
+ if(pWindow->IsInPaint())
+ {
+ if(!pWindow->GetPaintRegion().IsEmpty())
+ {
+ aOptimizedRepaintRegion.Intersect(pWindow->GetPaintRegion());
+ }
+ }
+ }
+
+ SdrPaintWindow* pPaintWindow = BeginCompleteRedraw(pOut);
+ OSL_ENSURE(pPaintWindow, "SdrPaintView::CompleteRedraw: No OutDev (!)");
+
+ DoCompleteRedraw(*pPaintWindow, aOptimizedRepaintRegion, pRedirector);
+ EndCompleteRedraw(*pPaintWindow, true);
+
+#ifdef SVX_REPAINT_TIMER_TEST
+ }
+
+ if(bDoTimerTest)
+ {
+ const sal_uInt64 nStopTime(tools::Time::GetSystemTicks());
+ const sal_uInt64 nNeededTime(nStopTime - nStartTime);
+ const double fTimePerPaint((double)nNeededTime / (double)nRepeatCount);
+
+ if(!bTimesInited)
+ {
+ for(a = 0; a < REMEMBERED_TIMES_COUNT; a++)
+ {
+ fLastTimes[a] = fTimePerPaint;
+ }
+
+ bTimesInited = true;
+ }
+ else
+ {
+ for(a = 1; a < REMEMBERED_TIMES_COUNT; a++)
+ {
+ fLastTimes[a - 1] = fLastTimes[a];
+ }
+
+ fLastTimes[REMEMBERED_TIMES_COUNT - 1] = fTimePerPaint;
+ }
+
+ double fAddedTimes(0.0);
+
+ for(a = 0; a < REMEMBERED_TIMES_COUNT; a++)
+ {
+ fAddedTimes += fLastTimes[a];
+ }
+
+ const double fAverageTimePerPaint(fAddedTimes / (double)REMEMBERED_TIMES_COUNT);
+
+ fprintf(stderr, "-----------(start result)----------\n");
+ fprintf(stderr, "StartTime : %" SAL_PRIuUINT64 ", StopTime: %" SAL_PRIuUINT64 ", NeededTime: %" SAL_PRIuUINT64 ", TimePerPaint: %f\n", nStartTime, nStopTime, nNeededTime, fTimePerPaint);
+ fprintf(stderr, "Remembered times: ");
+
+ for(a = 0; a < REMEMBERED_TIMES_COUNT; a++)
+ {
+ fprintf(stderr, "%d: %f ", a, fLastTimes[a]);
+ }
+
+ fprintf(stderr, "\n");
+ fprintf(stderr, "AverageTimePerPaint: %f\n", fAverageTimePerPaint);
+ fprintf(stderr, "-----------(stop result)----------\n");
+ }
+#endif // SVX_REPAINT_TIMER_TEST
+}
+
+
+// #i72889#
+
+SdrPaintWindow* SdrPaintView::BeginCompleteRedraw(OutputDevice* pOut)
+{
+ OSL_ENSURE(pOut, "SdrPaintView::BeginCompleteRedraw: No OutDev (!)");
+ SdrPaintWindow* pPaintWindow = FindPaintWindow(*pOut);
+
+ if(pPaintWindow)
+ {
+ // draw preprocessing, only for known devices
+ // prepare PreRendering
+ pPaintWindow->PreparePreRenderDevice();
+ }
+ else
+ {
+ // None of the known OutputDevices is the target of this paint, use
+ // a temporary SdrPaintWindow for this Redraw.
+ pPaintWindow = new SdrPaintWindow(*this, *pOut);
+ pPaintWindow->setTemporaryTarget(true);
+ }
+
+ return pPaintWindow;
+}
+
+void SdrPaintView::DoCompleteRedraw(SdrPaintWindow& rPaintWindow, const vcl::Region& rReg, sdr::contact::ViewObjectContactRedirector* pRedirector)
+{
+ // redraw all PageViews with the target. This may expand the RedrawRegion
+ // at the PaintWindow, plus taking care of FormLayer expansion
+ if(mpPageView)
+ {
+ mpPageView->CompleteRedraw(rPaintWindow, rReg, pRedirector);
+ }
+}
+
+void SdrPaintView::EndCompleteRedraw(SdrPaintWindow& rPaintWindow, bool bPaintFormLayer)
+{
+ std::unique_ptr<SdrPaintWindow> pPaintWindow;
+ if (comphelper::LibreOfficeKit::isActive() && rPaintWindow.getTemporaryTarget())
+ {
+ // Tiled rendering, we must paint the TextEdit to the output device.
+ pPaintWindow.reset(&rPaintWindow);
+ pPaintWindow->setTemporaryTarget(false);
+ }
+
+ if(rPaintWindow.getTemporaryTarget())
+ {
+ // get rid of temp target again
+ delete &rPaintWindow;
+ }
+ else
+ {
+ // draw postprocessing, only for known devices
+ // it is necessary to always paint FormLayer
+ if(bPaintFormLayer)
+ {
+ ImpFormLayerDrawing(rPaintWindow);
+ }
+
+ // look for active TextEdit. As long as this cannot be painted to a VDev,
+ // it cannot get part of buffering. In that case, output evtl. prerender
+ // early and paint text edit to window.
+ if(IsTextEdit() && GetSdrPageView())
+ {
+ static_cast< SdrView* >(this)->TextEditDrawing(rPaintWindow);
+ }
+
+ if (comphelper::LibreOfficeKit::isActive())
+ {
+ // Look for active text edits in other views showing the same page,
+ // and show them as well.
+ if (SdrPageView* pPageView = GetSdrPageView())
+ {
+ SdrViewIter aIter(pPageView->GetPage());
+ for (SdrView* pView = aIter.FirstView(); pView; pView = aIter.NextView())
+ {
+ if (pView == this)
+ continue;
+
+ if (pView->IsTextEdit() && pView->GetSdrPageView())
+ {
+ pView->TextEditDrawing(rPaintWindow);
+ }
+ }
+ }
+ }
+
+ // draw Overlay, also to PreRender device if exists
+ rPaintWindow.DrawOverlay(rPaintWindow.GetRedrawRegion());
+
+ // output PreRendering
+ rPaintWindow.OutputPreRenderDevice(rPaintWindow.GetRedrawRegion());
+ }
+}
+
+
+SdrPaintWindow* SdrPaintView::BeginDrawLayers(OutputDevice* pOut, const vcl::Region& rReg, bool bDisableIntersect)
+{
+ // #i74769# use BeginCompleteRedraw() as common base
+ SdrPaintWindow* pPaintWindow = BeginCompleteRedraw(pOut);
+ OSL_ENSURE(pPaintWindow, "SdrPaintView::BeginDrawLayers: No SdrPaintWindow (!)");
+
+ if(mpPageView)
+ {
+ SdrPageWindow* pKnownTarget = mpPageView->FindPageWindow(*pPaintWindow);
+
+ if(pKnownTarget)
+ {
+ vcl::Region aOptimizedRepaintRegion = OptimizeDrawLayersRegion( pOut, rReg, bDisableIntersect );
+
+ // prepare redraw
+ pKnownTarget->PrepareRedraw(aOptimizedRepaintRegion);
+
+ // remember prepared SdrPageWindow
+ mpPageView->setPreparedPageWindow(pKnownTarget);
+ }
+ }
+
+ return pPaintWindow;
+}
+
+void SdrPaintView::EndDrawLayers(SdrPaintWindow& rPaintWindow, bool bPaintFormLayer)
+{
+ // #i74769# use EndCompleteRedraw() as common base
+ EndCompleteRedraw(rPaintWindow, bPaintFormLayer);
+
+ if(mpPageView)
+ {
+ // forget prepared SdrPageWindow
+ mpPageView->setPreparedPageWindow(nullptr);
+ }
+}
+
+void SdrPaintView::UpdateDrawLayersRegion(const OutputDevice* pOut, const vcl::Region& rReg)
+{
+ SdrPaintWindow* pPaintWindow = FindPaintWindow(*pOut);
+ OSL_ENSURE(pPaintWindow, "SdrPaintView::UpdateDrawLayersRegion: No SdrPaintWindow (!)");
+
+ if(mpPageView)
+ {
+ SdrPageWindow* pKnownTarget = mpPageView->FindPageWindow(*pPaintWindow);
+
+ if(pKnownTarget)
+ {
+ vcl::Region aOptimizedRepaintRegion = OptimizeDrawLayersRegion( pOut, rReg, false/*bDisableIntersect*/ );
+ pKnownTarget->GetPaintWindow().SetRedrawRegion(aOptimizedRepaintRegion);
+ mpPageView->setPreparedPageWindow(pKnownTarget); // already set actually
+ }
+ }
+}
+
+vcl::Region SdrPaintView::OptimizeDrawLayersRegion(const OutputDevice* pOut, const vcl::Region& rReg, bool bDisableIntersect)
+{
+ // #i74769# check if pOut is a win and has a ClipRegion. If Yes, the Region
+ // rReg may be made more granular (fine) with using it. Normally, rReg
+ // does come from Window::Paint() anyways and thus is based on a single
+ // rectangle which was derived from exactly that repaint region
+ vcl::Region aOptimizedRepaintRegion(rReg);
+
+ // #i76114# Intersecting the region with the Window's paint region is disabled
+ // for print preview in Calc, because the intersection can be empty (if the paint
+ // region is outside of the table area of the page), and then no clip region
+ // would be set.
+ if(pOut && OUTDEV_WINDOW == pOut->GetOutDevType() && !bDisableIntersect)
+ {
+ vcl::Window* pWindow = pOut->GetOwnerWindow();
+
+ if(pWindow->IsInPaint())
+ {
+ if(!pWindow->GetPaintRegion().IsEmpty())
+ {
+ aOptimizedRepaintRegion.Intersect(pWindow->GetPaintRegion());
+ }
+ }
+ }
+ return aOptimizedRepaintRegion;
+}
+
+
+void SdrPaintView::ImpFormLayerDrawing( SdrPaintWindow& rPaintWindow )
+{
+ if(!mpPageView)
+ return;
+
+ SdrPageWindow* pKnownTarget = mpPageView->FindPageWindow(rPaintWindow);
+
+ if(pKnownTarget)
+ {
+ const SdrModel& rModel = *(GetModel());
+ const SdrLayerAdmin& rLayerAdmin = rModel.GetLayerAdmin();
+ const SdrLayerID nControlLayerId = rLayerAdmin.GetLayerID(rLayerAdmin.GetControlLayerName());
+
+ // BUFFERED use GetTargetOutputDevice() now, it may be targeted to VDevs, too
+ // need to set PreparedPageWindow to make DrawLayer use the correct ObjectContact
+ mpPageView->setPreparedPageWindow(pKnownTarget);
+ mpPageView->DrawLayer(nControlLayerId, &rPaintWindow.GetTargetOutputDevice());
+ mpPageView->setPreparedPageWindow(nullptr);
+ }
+}
+
+
+bool SdrPaintView::KeyInput(const KeyEvent& /*rKEvt*/, vcl::Window* /*pWin*/)
+{
+ return false;
+}
+
+void SdrPaintView::GlueInvalidate() const
+{
+ // Do not invalidate GluePoints in Online
+ // They are handled on front-end
+ if (comphelper::LibreOfficeKit::isActive())
+ return;
+
+ const sal_uInt32 nWindowCount(PaintWindowCount());
+
+ for(sal_uInt32 nWinNum(0); nWinNum < nWindowCount; nWinNum++)
+ {
+ SdrPaintWindow* pPaintWindow = GetPaintWindow(nWinNum);
+
+ if(pPaintWindow->OutputToWindow())
+ {
+ OutputDevice& rOutDev = pPaintWindow->GetOutputDevice();
+
+ if(mpPageView)
+ {
+ const SdrObjList* pOL=mpPageView->GetObjList();
+ const size_t nObjCount = pOL->GetObjCount();
+ for (size_t nObjNum=0; nObjNum<nObjCount; ++nObjNum) {
+ const SdrObject* pObj=pOL->GetObj(nObjNum);
+ const SdrGluePointList* pGPL=pObj->GetGluePointList();
+ if (pGPL!=nullptr && pGPL->GetCount()!=0) {
+ pGPL->Invalidate(*rOutDev.GetOwnerWindow(), pObj);
+ }
+ }
+ }
+ }
+ }
+}
+
+void SdrPaintView::InvalidateAllWin()
+{
+ const sal_uInt32 nWindowCount(PaintWindowCount());
+
+ for(sal_uInt32 a(0); a < nWindowCount; a++)
+ {
+ SdrPaintWindow* pPaintWindow = GetPaintWindow(a);
+
+ if(pPaintWindow->OutputToWindow())
+ {
+ InvalidateOneWin(pPaintWindow->GetOutputDevice());
+ }
+ }
+}
+
+void SdrPaintView::InvalidateAllWin(const tools::Rectangle& rRect)
+{
+ const sal_uInt32 nWindowCount(PaintWindowCount());
+
+ for(sal_uInt32 a(0); a < nWindowCount; a++)
+ {
+ SdrPaintWindow* pPaintWindow = GetPaintWindow(a);
+
+ if(pPaintWindow->OutputToWindow())
+ {
+ OutputDevice& rOutDev = pPaintWindow->GetOutputDevice();
+ tools::Rectangle aRect(rRect);
+
+ Point aOrg(rOutDev.GetMapMode().GetOrigin());
+ aOrg.setX(-aOrg.X() ); aOrg.setY(-aOrg.Y() );
+ tools::Rectangle aOutRect(aOrg, rOutDev.GetOutputSize());
+
+ // In case of tiled rendering we want to get all invalidations, so visual area is not interesting.
+ if (aRect.Overlaps(aOutRect) || comphelper::LibreOfficeKit::isActive())
+ {
+ InvalidateOneWin(rOutDev, aRect);
+ }
+ }
+ }
+}
+
+void SdrPaintView::InvalidateOneWin(OutputDevice& rDevice)
+{
+ // do not erase background, that causes flicker (!)
+ rDevice.GetOwnerWindow()->Invalidate(InvalidateFlags::NoErase);
+}
+
+void SdrPaintView::InvalidateOneWin(OutputDevice& rDevice, const tools::Rectangle& rRect)
+{
+ // do not erase background, that causes flicker (!)
+ rDevice.GetOwnerWindow()->Invalidate(rRect, InvalidateFlags::NoErase);
+}
+
+void SdrPaintView::LeaveOneGroup()
+{
+ if(mpPageView)
+ {
+ mpPageView->LeaveOneGroup();
+ }
+}
+
+void SdrPaintView::LeaveAllGroup()
+{
+ if(mpPageView)
+ {
+ mpPageView->LeaveAllGroup();
+ }
+}
+
+bool SdrPaintView::IsGroupEntered() const
+{
+ if(mpPageView)
+ {
+ return (mpPageView->GetEnteredLevel() != 0);
+ }
+
+ return false;
+}
+
+void SdrPaintView::SetNotPersistDefaultAttr(const SfxItemSet& rAttr)
+{
+ // bReplaceAll has no effect here at all.
+ bool bMeasure= dynamic_cast<const SdrView*>(this) != nullptr && static_cast<SdrView*>(this)->IsMeasureTool();
+
+ if (const SdrLayerIdItem *pPoolItem = rAttr.GetItemIfSet(SDRATTR_LAYERID))
+ {
+ SdrLayerID nLayerId = pPoolItem->GetValue();
+ const SdrLayer* pLayer=mpModel->GetLayerAdmin().GetLayerPerID(nLayerId);
+ if (pLayer!=nullptr) {
+ if (bMeasure) maMeasureLayer=pLayer->GetName();
+ else maActualLayer=pLayer->GetName();
+ }
+ }
+ if (const SdrLayerNameItem *pPoolItem = rAttr.GetItemIfSet(SDRATTR_LAYERNAME))
+ {
+ if (bMeasure) maMeasureLayer = pPoolItem->GetValue();
+ else maActualLayer = pPoolItem->GetValue();
+ }
+}
+
+void SdrPaintView::MergeNotPersistDefaultAttr(SfxItemSet& rAttr) const
+{
+ // bOnlyHardAttr has no effect here at all.
+ bool bMeasure= dynamic_cast<const SdrView*>(this) != nullptr && static_cast<const SdrView*>(this)->IsMeasureTool();
+ const OUString& aNam = bMeasure ? maMeasureLayer : maActualLayer;
+ rAttr.Put(SdrLayerNameItem(aNam));
+ SdrLayerID nLayer=mpModel->GetLayerAdmin().GetLayerID(aNam);
+ if (nLayer!=SDRLAYER_NOTFOUND) {
+ rAttr.Put(SdrLayerIdItem(nLayer));
+ }
+}
+
+void SdrPaintView::SetDefaultAttr(const SfxItemSet& rAttr, bool bReplaceAll)
+{
+#ifdef DBG_UTIL
+ {
+ bool bHasEEFeatureItems=false;
+ SfxItemIter aIter(rAttr);
+ for (const SfxPoolItem* pItem = aIter.GetCurItem(); !bHasEEFeatureItems && pItem;
+ pItem = aIter.NextItem())
+ {
+ if (!IsInvalidItem(pItem)) {
+ sal_uInt16 nW=pItem->Which();
+ if (nW>=EE_FEATURE_START && nW<=EE_FEATURE_END) bHasEEFeatureItems=true;
+ }
+ }
+
+ if(bHasEEFeatureItems)
+ {
+ std::unique_ptr<weld::MessageDialog> xInfoBox(Application::CreateMessageDialog(nullptr,
+ VclMessageType::Info, VclButtonsType::Ok,
+ "SdrPaintView::SetDefaultAttr(): Setting EE_FEATURE items at the SdrView does not make sense! It only leads to overhead and unreadable documents."));
+ xInfoBox->run();
+ }
+ }
+#endif
+ if (bReplaceAll) maDefaultAttr.Set(rAttr);
+ else maDefaultAttr.Put(rAttr,false); // if FALSE, regard InvalidItems as "holes," not as Default
+ SetNotPersistDefaultAttr(rAttr);
+}
+
+void SdrPaintView::SetDefaultStyleSheet(SfxStyleSheet* pStyleSheet, bool bDontRemoveHardAttr)
+{
+ if (mpDefaultStyleSheet)
+ EndListening(*mpDefaultStyleSheet);
+ mpDefaultStyleSheet=pStyleSheet;
+ if (mpDefaultStyleSheet)
+ StartListening(*mpDefaultStyleSheet);
+
+ if (pStyleSheet!=nullptr && !bDontRemoveHardAttr) {
+ SfxWhichIter aIter(pStyleSheet->GetItemSet());
+ sal_uInt16 nWhich=aIter.FirstWhich();
+ while (nWhich!=0) {
+ if (aIter.GetItemState()==SfxItemState::SET) {
+ maDefaultAttr.ClearItem(nWhich);
+ }
+ nWhich=aIter.NextWhich();
+ }
+ }
+}
+
+void SdrPaintView::GetAttributes(SfxItemSet& rTargetSet, bool bOnlyHardAttr) const
+{
+ if(bOnlyHardAttr || !mpDefaultStyleSheet)
+ {
+ rTargetSet.Put(maDefaultAttr, false);
+ }
+ else
+ {
+ // else merge with DefStyleSheet
+ rTargetSet.Put(mpDefaultStyleSheet->GetItemSet(), false);
+ rTargetSet.Put(maDefaultAttr, false);
+ }
+ MergeNotPersistDefaultAttr(rTargetSet);
+}
+
+void SdrPaintView::SetAttributes(const SfxItemSet& rSet, bool bReplaceAll)
+{
+ SetDefaultAttr(rSet,bReplaceAll);
+}
+
+SfxStyleSheet* SdrPaintView::GetStyleSheet() const
+{
+ return mpDefaultStyleSheet;
+}
+
+void SdrPaintView::SetStyleSheet(SfxStyleSheet* pStyleSheet, bool bDontRemoveHardAttr)
+{
+ SetDefaultStyleSheet(pStyleSheet,bDontRemoveHardAttr);
+}
+
+void SdrPaintView::MakeVisible(const tools::Rectangle& rRect, vcl::Window& rWin)
+{
+ // TODO: handle when the text cursor goes out of the chart area
+ // However this hack avoids that the cursor gets misplaced wrt the text.
+ if (comphelper::LibreOfficeKit::isActive() && rWin.IsChart())
+ {
+ return;
+ }
+
+ MapMode aMap(rWin.GetMapMode());
+ Size aActualSize(rWin.GetOutDev()->GetOutputSize());
+
+ if( aActualSize.IsEmpty() )
+ return;
+
+ Size aNewSize(rRect.GetSize());
+ bool bNewScale=false;
+ bool bNeedMoreX=aNewSize.Width()>aActualSize.Width();
+ bool bNeedMoreY=aNewSize.Height()>aActualSize.Height();
+ if (bNeedMoreX || bNeedMoreY)
+ {
+ bNewScale=true;
+ // set new MapMode (Size+Org) and invalidate everything
+ Fraction aXFact(aNewSize.Width(),aActualSize.Width());
+ Fraction aYFact(aNewSize.Height(),aActualSize.Height());
+ if (aYFact>aXFact) aXFact=aYFact;
+ aXFact*=aMap.GetScaleX();
+ aXFact.ReduceInaccurate(10); // to avoid runovers and BigInt mapping
+ aMap.SetScaleX(aXFact);
+ aMap.SetScaleY(aYFact);
+ rWin.SetMapMode(aMap);
+ aActualSize=rWin.GetOutDev()->GetOutputSize();
+ }
+ Point aOrg(aMap.GetOrigin());
+ tools::Long dx=0,dy=0;
+ tools::Long l=-aOrg.X();
+ tools::Long r=-aOrg.X()+aActualSize.Width()-1;
+ tools::Long o=-aOrg.Y();
+ tools::Long u=-aOrg.Y()+aActualSize.Height()-1;
+ if (l>rRect.Left()) dx=rRect.Left()-l;
+ else if (r<rRect.Right()) dx=rRect.Right()-r;
+ if (o>rRect.Top()) dy=rRect.Top()-o;
+ else if (u<rRect.Bottom()) dy=rRect.Bottom()-u;
+ aMap.SetOrigin(Point(aOrg.X()-dx,aOrg.Y()-dy));
+ if (!bNewScale) {
+ if (dx!=0 || dy!=0) {
+ rWin.Scroll(-dx,-dy);
+ rWin.SetMapMode(aMap);
+ rWin.PaintImmediately();
+ }
+ } else {
+ rWin.SetMapMode(aMap);
+ InvalidateOneWin(*rWin.GetOutDev());
+ }
+}
+
+void SdrPaintView::DoConnect(SdrOle2Obj* /*pOleObj*/)
+{
+}
+
+void SdrPaintView::SetAnimationEnabled( bool bEnable )
+{
+ SetAnimationMode( bEnable ? SdrAnimationMode::Animate : SdrAnimationMode::Disable );
+}
+
+void SdrPaintView::SetAnimationPause( bool bSet )
+{
+ if(mbAnimationPause == bSet)
+ return;
+
+ mbAnimationPause = bSet;
+
+ if(!mpPageView)
+ return;
+
+ for(sal_uInt32 b(0); b < mpPageView->PageWindowCount(); b++)
+ {
+ SdrPageWindow& rPageWindow = *(mpPageView->GetPageWindow(b));
+ sdr::contact::ObjectContact& rObjectContact = rPageWindow.GetObjectContact();
+ sdr::animation::primitiveAnimator& rAnimator = rObjectContact.getPrimitiveAnimator();
+
+ if(rAnimator.IsPaused() != bSet)
+ {
+ rAnimator.SetPaused(bSet);
+ }
+ }
+}
+
+void SdrPaintView::SetAnimationMode( const SdrAnimationMode eMode )
+{
+ meAnimationMode = eMode;
+}
+
+void SdrPaintView::VisAreaChanged(const OutputDevice* pOut)
+{
+ if(!mpPageView)
+ return;
+
+ if (pOut)
+ {
+ SdrPageWindow* pWindow = mpPageView->FindPageWindow(*const_cast<OutputDevice*>(pOut));
+
+ if(pWindow)
+ {
+ VisAreaChanged();
+ }
+ }
+ else
+ {
+ VisAreaChanged();
+ }
+}
+
+void SdrPaintView::VisAreaChanged()
+{
+ // notify SfxListener
+ Broadcast(SvxViewChangedHint());
+}
+
+
+void SdrPaintView::onChangeColorConfig()
+{
+ maGridColor = maColorConfig.GetColorValue( svtools::DRAWGRID ).nColor;
+}
+
+
+// Set background color for svx at SdrPageViews
+void SdrPaintView::SetApplicationBackgroundColor(Color aBackgroundColor)
+{
+ if(mpPageView)
+ {
+ mpPageView->SetApplicationBackgroundColor(aBackgroundColor);
+ }
+}
+
+// Set document color for svx at SdrPageViews
+void SdrPaintView::SetApplicationDocumentColor(Color aDocumentColor)
+{
+ if(mpPageView)
+ {
+ mpPageView->SetApplicationDocumentColor(aDocumentColor);
+ }
+}
+
+bool SdrPaintView::IsBufferedOutputAllowed() const
+{
+ return (mbBufferedOutputAllowed && SvtOptionsDrawinglayer::IsPaintBuffer());
+}
+
+void SdrPaintView::SetBufferedOutputAllowed(bool bNew)
+{
+ if(bNew != mbBufferedOutputAllowed)
+ {
+ mbBufferedOutputAllowed = bNew;
+ }
+}
+
+bool SdrPaintView::IsBufferedOverlayAllowed() const
+{
+ return (mbBufferedOverlayAllowed && SvtOptionsDrawinglayer::IsOverlayBuffer());
+}
+
+void SdrPaintView::SetBufferedOverlayAllowed(bool bNew)
+{
+ if(bNew != mbBufferedOverlayAllowed)
+ {
+ mbBufferedOverlayAllowed = bNew;
+ }
+}
+
+
+void SdrPaintView::SetPagePaintingAllowed(bool bNew)
+{
+ if(bNew != mbPagePaintingAllowed)
+ {
+ mbPagePaintingAllowed = bNew;
+ }
+}
+
+// #i38135# Sets the timer for Object animations and restarts.
+void SdrPaintView::SetAnimationTimer(sal_uInt32 nTime)
+{
+ if(mpPageView)
+ {
+ // first, reset all timers at all windows to 0L
+ for(sal_uInt32 a(0); a < mpPageView->PageWindowCount(); a++)
+ {
+ SdrPageWindow& rPageWindow = *mpPageView->GetPageWindow(a);
+ sdr::contact::ObjectContact& rObjectContact = rPageWindow.GetObjectContact();
+ sdr::animation::primitiveAnimator& rAnimator = rObjectContact.getPrimitiveAnimator();
+ rAnimator.SetTime(nTime);
+ }
+ }
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/svdraw/svdpoev.cxx b/svx/source/svdraw/svdpoev.cxx
new file mode 100644
index 000000000..bcf9168a4
--- /dev/null
+++ b/svx/source/svdraw/svdpoev.cxx
@@ -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 .
+ */
+
+
+#include <svx/svdpoev.hxx>
+#include <math.h>
+#include <svx/svdpagv.hxx>
+#include <svx/svdpage.hxx>
+#include <svx/svdopath.hxx>
+#include <svx/svdundo.hxx>
+#include <svx/strings.hrc>
+#include <svx/dialmgr.hxx>
+#include <svx/svdtrans.hxx>
+#include <basegfx/polygon/b2dpolygon.hxx>
+#include <basegfx/polygon/b2dpolygontools.hxx>
+#include <tools/debug.hxx>
+#include <tools/helpers.hxx>
+
+#include <svx/polypolygoneditor.hxx>
+
+using namespace sdr;
+
+
+void SdrPolyEditView::ImpResetPolyPossibilityFlags()
+{
+ eMarkedPointsSmooth=SdrPathSmoothKind::DontCare;
+ eMarkedSegmentsKind=SdrPathSegmentKind::DontCare;
+ bSetMarkedPointsSmoothPossible=false;
+ bSetMarkedSegmentsKindPossible=false;
+}
+
+SdrPolyEditView::SdrPolyEditView(
+ SdrModel& rSdrModel,
+ OutputDevice* pOut)
+: SdrEditView(rSdrModel, pOut)
+{
+ ImpResetPolyPossibilityFlags();
+}
+
+SdrPolyEditView::~SdrPolyEditView()
+{
+}
+
+void SdrPolyEditView::ImpCheckPolyPossibilities()
+{
+ ImpResetPolyPossibilityFlags();
+ const size_t nMarkCount(GetMarkedObjectCount());
+
+ if(!nMarkCount || ImpIsFrameHandles())
+ return;
+
+ bool b1stSmooth(true);
+ bool b1stSegm(true);
+ bool bCurve(false);
+ bool bSmoothFuz(false);
+ bool bSegmFuz(false);
+ basegfx::B2VectorContinuity eSmooth = basegfx::B2VectorContinuity::NONE;
+
+ for(size_t nMarkNum = 0; nMarkNum < nMarkCount; ++nMarkNum)
+ {
+ SdrMark* pM = GetSdrMarkByIndex(nMarkNum);
+ CheckPolyPossibilitiesHelper( pM, b1stSmooth, b1stSegm, bCurve, bSmoothFuz, bSegmFuz, eSmooth );
+ }
+}
+
+void SdrPolyEditView::CheckPolyPossibilitiesHelper( SdrMark* pM, bool& b1stSmooth, bool& b1stSegm, bool& bCurve, bool& bSmoothFuz, bool& bSegmFuz, basegfx::B2VectorContinuity& eSmooth )
+{
+ SdrObject* pObj = pM->GetMarkedSdrObj();
+ SdrPathObj* pPath = dynamic_cast<SdrPathObj*>( pObj );
+
+ if (!pPath)
+ return;
+
+ SdrUShortCont& rPts = pM->GetMarkedPoints();
+ if (rPts.empty())
+ return;
+
+ const bool bClosed(pPath->IsClosed());
+ bSetMarkedPointsSmoothPossible = true;
+
+ if (bClosed)
+ {
+ bSetMarkedSegmentsKindPossible = true;
+ }
+
+ for (const auto& rPt : rPts)
+ {
+ sal_uInt32 nNum(rPt);
+ sal_uInt32 nPolyNum, nPntNum;
+
+ if(PolyPolygonEditor::GetRelativePolyPoint(pPath->GetPathPoly(), nNum, nPolyNum, nPntNum))
+ {
+ const basegfx::B2DPolygon aLocalPolygon(pPath->GetPathPoly().getB2DPolygon(nPolyNum));
+ bool bCanSegment(bClosed || nPntNum < aLocalPolygon.count() - 1);
+
+ if(!bSetMarkedSegmentsKindPossible && bCanSegment)
+ {
+ bSetMarkedSegmentsKindPossible = true;
+ }
+
+ if(!bSmoothFuz)
+ {
+ if (b1stSmooth)
+ {
+ b1stSmooth = false;
+ eSmooth = basegfx::utils::getContinuityInPoint(aLocalPolygon, nPntNum);
+ }
+ else
+ {
+ bSmoothFuz = (eSmooth != basegfx::utils::getContinuityInPoint(aLocalPolygon, nPntNum));
+ }
+ }
+
+ if(!bSegmFuz && bCanSegment)
+ {
+ bool bCrv(aLocalPolygon.isNextControlPointUsed(nPntNum));
+
+ if(b1stSegm)
+ {
+ b1stSegm = false;
+ bCurve = bCrv;
+ }
+ else
+ {
+ bSegmFuz = (bCrv != bCurve);
+ }
+ }
+ }
+ }
+
+ if(!b1stSmooth && !bSmoothFuz)
+ {
+ if(basegfx::B2VectorContinuity::NONE == eSmooth)
+ {
+ eMarkedPointsSmooth = SdrPathSmoothKind::Angular;
+ }
+
+ if(basegfx::B2VectorContinuity::C1 == eSmooth)
+ {
+ eMarkedPointsSmooth = SdrPathSmoothKind::Asymmetric;
+ }
+
+ if(basegfx::B2VectorContinuity::C2 == eSmooth)
+ {
+ eMarkedPointsSmooth = SdrPathSmoothKind::Symmetric;
+ }
+ }
+
+ if(!b1stSegm && !bSegmFuz)
+ {
+ eMarkedSegmentsKind = bCurve ? SdrPathSegmentKind::Curve : SdrPathSegmentKind::Line;
+ }
+}
+
+void SdrPolyEditView::SetMarkedPointsSmooth(SdrPathSmoothKind eKind)
+{
+ basegfx::B2VectorContinuity eFlags;
+
+ if(SdrPathSmoothKind::Angular == eKind)
+ {
+ eFlags = basegfx::B2VectorContinuity::NONE;
+ }
+ else if(SdrPathSmoothKind::Asymmetric == eKind)
+ {
+ eFlags = basegfx::B2VectorContinuity::C1;
+ }
+ else if(SdrPathSmoothKind::Symmetric == eKind)
+ {
+ eFlags = basegfx::B2VectorContinuity::C2;
+ }
+ else
+ {
+ return;
+ }
+
+ if(!HasMarkedPoints())
+ return;
+
+ SortMarkedObjects();
+
+ const bool bUndo = IsUndoEnabled();
+ if( bUndo )
+ BegUndo(SvxResId(STR_EditSetPointsSmooth), GetDescriptionOfMarkedPoints());
+ const size_t nMarkCount(GetMarkedObjectCount());
+
+ for(size_t nMarkNum(nMarkCount); nMarkNum > 0;)
+ {
+ --nMarkNum;
+ SdrMark* pM = GetSdrMarkByIndex(nMarkNum);
+ SdrPathObj* pPath = dynamic_cast< SdrPathObj* >( pM->GetMarkedSdrObj() );
+ if (!pPath)
+ continue;
+
+ SdrUShortCont& rPts = pM->GetMarkedPoints();
+ PolyPolygonEditor aEditor(pPath->GetPathPoly());
+ if (aEditor.SetPointsSmooth(eFlags, rPts))
+ {
+ if( bUndo )
+ AddUndo(GetModel()->GetSdrUndoFactory().CreateUndoGeoObject(*pPath));
+ pPath->SetPathPoly(aEditor.GetPolyPolygon());
+ }
+ }
+
+ if( bUndo )
+ EndUndo();
+}
+
+void SdrPolyEditView::SetMarkedSegmentsKind(SdrPathSegmentKind eKind)
+{
+ if(!HasMarkedPoints())
+ return;
+
+ SortMarkedObjects();
+
+ const bool bUndo = IsUndoEnabled();
+ if( bUndo )
+ BegUndo(SvxResId(STR_EditSetSegmentsKind), GetDescriptionOfMarkedPoints());
+ const size_t nMarkCount(GetMarkedObjectCount());
+
+ for(size_t nMarkNum=nMarkCount; nMarkNum > 0;)
+ {
+ --nMarkNum;
+ SdrMark* pM = GetSdrMarkByIndex(nMarkNum);
+ SdrPathObj* pPath = dynamic_cast< SdrPathObj* >( pM->GetMarkedSdrObj() );
+ if (!pPath)
+ continue;
+ SdrUShortCont& rPts = pM->GetMarkedPoints();
+ PolyPolygonEditor aEditor( pPath->GetPathPoly());
+ if (aEditor.SetSegmentsKind(eKind, rPts))
+ {
+ if( bUndo )
+ AddUndo(GetModel()->GetSdrUndoFactory().CreateUndoGeoObject(*pPath));
+ pPath->SetPathPoly(aEditor.GetPolyPolygon());
+ }
+ }
+
+ if( bUndo )
+ EndUndo();
+}
+
+bool SdrPolyEditView::IsSetMarkedPointsSmoothPossible() const
+{
+ ForcePossibilities();
+ return bSetMarkedPointsSmoothPossible;
+}
+
+SdrPathSmoothKind SdrPolyEditView::GetMarkedPointsSmooth() const
+{
+ ForcePossibilities();
+ return eMarkedPointsSmooth;
+}
+
+bool SdrPolyEditView::IsSetMarkedSegmentsKindPossible() const
+{
+ ForcePossibilities();
+ return bSetMarkedSegmentsKindPossible;
+}
+
+SdrPathSegmentKind SdrPolyEditView::GetMarkedSegmentsKind() const
+{
+ ForcePossibilities();
+ return eMarkedSegmentsKind;
+}
+
+bool SdrPolyEditView::IsDeleteMarkedPointsPossible() const
+{
+ return HasMarkedPoints();
+}
+
+void SdrPolyEditView::DeleteMarkedPoints()
+{
+ if (!HasMarkedPoints())
+ return;
+
+ BrkAction();
+ SortMarkedObjects();
+ const size_t nMarkCount=GetMarkedObjectCount();
+
+ const bool bUndo = IsUndoEnabled();
+ if( bUndo )
+ {
+ // Description
+ BegUndo(SvxResId(STR_EditDelete),GetDescriptionOfMarkedPoints(),SdrRepeatFunc::Delete);
+ }
+
+ for (size_t nMarkNum=nMarkCount; nMarkNum>0;)
+ {
+ --nMarkNum;
+ SdrMark* pM=GetSdrMarkByIndex(nMarkNum);
+ SdrPathObj* pPath = dynamic_cast< SdrPathObj* >( pM->GetMarkedSdrObj() );
+ if (!pPath)
+ continue;
+
+ SdrUShortCont& rPts = pM->GetMarkedPoints();
+ PolyPolygonEditor aEditor( pPath->GetPathPoly());
+ if (aEditor.DeletePoints(rPts))
+ {
+ if( aEditor.GetPolyPolygon().count() )
+ {
+ if( bUndo )
+ AddUndo(GetModel()->GetSdrUndoFactory().CreateUndoGeoObject(*pPath ));
+ pPath->SetPathPoly( aEditor.GetPolyPolygon() );
+ }
+ else
+ {
+ if( bUndo )
+ AddUndo( GetModel()->GetSdrUndoFactory().CreateUndoDeleteObject(*pPath ) );
+ pM->GetPageView()->GetObjList()->RemoveObject(pPath->GetOrdNum());
+ if( !bUndo )
+ {
+ SdrObject* pObj = pPath;
+ SdrObject::Free(pObj);
+ }
+ }
+ }
+ }
+
+ if( bUndo )
+ EndUndo();
+ UnmarkAllPoints();
+ MarkListHasChanged();
+}
+
+void SdrPolyEditView::RipUpAtMarkedPoints()
+{
+ if(!HasMarkedPoints())
+ return;
+
+ SortMarkedObjects();
+ const size_t nMarkCount(GetMarkedObjectCount());
+
+ const bool bUndo = IsUndoEnabled();
+ if( bUndo )
+ BegUndo(SvxResId(STR_EditRipUp), GetDescriptionOfMarkedPoints());
+
+ for(size_t nMarkNum = nMarkCount; nMarkNum > 0;)
+ {
+ --nMarkNum;
+ SdrMark* pM = GetSdrMarkByIndex(nMarkNum);
+ SdrPathObj* pObj = dynamic_cast<SdrPathObj*>( pM->GetMarkedSdrObj() );
+ if (!pObj)
+ continue;
+
+ SdrUShortCont& rPts = pM->GetMarkedPoints();
+
+ if( bUndo )
+ AddUndo(GetModel()->GetSdrUndoFactory().CreateUndoGeoObject(*pObj));
+ bool bCorrectionFlag(false);
+ sal_uInt32 nMax(pObj->GetHdlCount());
+
+ for(SdrUShortCont::const_reverse_iterator it = rPts.rbegin(); it != rPts.rend(); ++it)
+ {
+ sal_uInt32 nNewPt0Idx(0);
+ SdrObject* pNewObj = pObj->RipPoint(*it, nNewPt0Idx);
+
+ if(pNewObj)
+ {
+ pM->GetPageView()->GetObjList()->InsertObject(pNewObj, pObj->GetOrdNum() + 1);
+ if( bUndo )
+ AddUndo(GetModel()->GetSdrUndoFactory().CreateUndoNewObject(*pNewObj));
+ MarkObj(pNewObj, pM->GetPageView(), false, true);
+ }
+
+ if(nNewPt0Idx)
+ {
+ // correction necessary?
+ DBG_ASSERT(!bCorrectionFlag,"Multiple index corrections at SdrPolyEditView::RipUp().");
+ if(!bCorrectionFlag)
+ {
+ bCorrectionFlag = true;
+
+ SdrUShortCont aReplaceSet;
+ for(const auto& rPt : rPts)
+ {
+ sal_uInt32 nPntNum(rPt);
+ nPntNum += nNewPt0Idx;
+
+ if(nPntNum >= nMax)
+ {
+ nPntNum -= nMax;
+ }
+
+ aReplaceSet.insert( static_cast<sal_uInt16>(nPntNum) );
+ }
+ rPts.swap(aReplaceSet);
+
+ it = rPts.rbegin();
+ }
+ }
+ }
+ }
+
+ UnmarkAllPoints();
+ if( bUndo )
+ EndUndo();
+ MarkListHasChanged();
+}
+
+bool SdrPolyEditView::IsRipUpAtMarkedPointsPossible() const
+{
+ bool bRetval(false);
+ const size_t nMarkCount(GetMarkedObjectCount());
+
+ for(size_t a = 0; a < nMarkCount; ++a)
+ {
+ const SdrMark* pMark = GetSdrMarkByIndex(a);
+ const SdrPathObj* pMarkedPathObject = dynamic_cast< const SdrPathObj* >(pMark->GetMarkedSdrObj());
+
+ if (!pMarkedPathObject)
+ continue;
+
+ const SdrUShortCont& rSelectedPoints = pMark->GetMarkedPoints();
+ if (rSelectedPoints.empty())
+ continue;
+
+ const basegfx::B2DPolyPolygon& rPathPolyPolygon = pMarkedPathObject->GetPathPoly();
+
+ if(1 == rPathPolyPolygon.count())
+ {
+ // #i76617# Do not yet use basegfx::B2DPolygon since curve definitions
+ // are different and methods need to be changed thoroughly with interaction rework
+ const tools::Polygon aPathPolygon(rPathPolyPolygon.getB2DPolygon(0));
+ const sal_uInt16 nPointCount(aPathPolygon.GetSize());
+
+ if(nPointCount >= 3)
+ {
+ bRetval = pMarkedPathObject->IsClosedObj() // #i76617#
+ || std::any_of(rSelectedPoints.begin(), rSelectedPoints.end(),
+ [nPointCount](const sal_uInt16 nMarkedPointNum) {
+ return nMarkedPointNum > 0 && nMarkedPointNum < nPointCount - 1;
+ });
+ }
+ }
+ }
+
+ return bRetval;
+}
+
+bool SdrPolyEditView::IsOpenCloseMarkedObjectsPossible() const
+{
+ bool bRetval(false);
+ const size_t nMarkCount(GetMarkedObjectCount());
+
+ for(size_t a = 0; a < nMarkCount; ++a)
+ {
+ const SdrMark* pMark = GetSdrMarkByIndex(a);
+ const SdrPathObj* pMarkedPathObject = dynamic_cast< const SdrPathObj* >(pMark->GetMarkedSdrObj());
+
+ if(pMarkedPathObject)
+ {
+ // #i76617# Do not yet use basegfx::B2DPolygon since curve definitions
+ // are different and methods need to be changed thoroughly with interaction rework
+ const tools::PolyPolygon aPathPolyPolygon(pMarkedPathObject->GetPathPoly());
+ const sal_uInt16 nPolygonCount(aPathPolyPolygon.Count());
+
+ for(sal_uInt16 b(0); !bRetval && b < nPolygonCount; b++)
+ {
+ const tools::Polygon& rPathPolygon = aPathPolyPolygon[b];
+ const sal_uInt16 nPointCount(rPathPolygon.GetSize());
+
+ bRetval = (nPointCount >= 3);
+ }
+ }
+ }
+
+ return bRetval;
+}
+
+SdrObjClosedKind SdrPolyEditView::GetMarkedObjectsClosedState() const
+{
+ bool bOpen(false);
+ bool bClosed(false);
+ const size_t nMarkCount(GetMarkedObjectCount());
+
+ for(size_t a = 0; !(bOpen && bClosed) && a < nMarkCount; ++a)
+ {
+ const SdrMark* pMark = GetSdrMarkByIndex(a);
+ const SdrPathObj* pMarkedPathObject = dynamic_cast< const SdrPathObj* >(pMark->GetMarkedSdrObj());
+
+ if(pMarkedPathObject)
+ {
+ if(pMarkedPathObject->IsClosedObj())
+ {
+ bClosed = true;
+ }
+ else
+ {
+ bOpen = true;
+ }
+ }
+ }
+
+ if(bOpen && bClosed)
+ {
+ return SdrObjClosedKind::DontCare;
+ }
+ else if(bOpen)
+ {
+ return SdrObjClosedKind::Open;
+ }
+ else
+ {
+ return SdrObjClosedKind::Closed;
+ }
+}
+
+void SdrPolyEditView::ImpTransformMarkedPoints(PPolyTrFunc pTrFunc, const void* p1, const void* p2, const void* p3, const void* p4)
+{
+ const bool bUndo = IsUndoEnabled();
+
+ const size_t nMarkCount=GetMarkedObjectCount();
+ for (size_t nm=0; nm<nMarkCount; ++nm)
+ {
+ SdrMark* pM=GetSdrMarkByIndex(nm);
+ SdrObject* pObj=pM->GetMarkedSdrObj();
+ SdrPathObj* pPath=dynamic_cast<SdrPathObj*>( pObj );
+ if (!pPath)
+ continue;
+
+ const SdrUShortCont& rPts = pM->GetMarkedPoints();
+ if (rPts.empty())
+ continue;
+
+ if( bUndo )
+ AddUndo(GetModel()->GetSdrUndoFactory().CreateUndoGeoObject(*pObj));
+
+ basegfx::B2DPolyPolygon aXPP(pPath->GetPathPoly());
+
+ for (const auto& rPt : rPts)
+ {
+ sal_uInt32 nPt = rPt;
+ sal_uInt32 nPolyNum, nPointNum;
+
+ if(PolyPolygonEditor::GetRelativePolyPoint(aXPP, nPt, nPolyNum, nPointNum))
+ {
+ //#i83671# used nLocalPointNum (which was the polygon point count)
+ // instead of the point index (nPointNum). This of course led
+ // to a wrong point access to the B2DPolygon.
+ basegfx::B2DPolygon aNewXP(aXPP.getB2DPolygon(nPolyNum));
+ Point aPos, aC1, aC2;
+ bool bC1(false);
+ bool bC2(false);
+
+ const basegfx::B2DPoint aB2DPos(aNewXP.getB2DPoint(nPointNum));
+ aPos = Point(FRound(aB2DPos.getX()), FRound(aB2DPos.getY()));
+
+ if(aNewXP.isPrevControlPointUsed(nPointNum))
+ {
+ const basegfx::B2DPoint aB2DC1(aNewXP.getPrevControlPoint(nPointNum));
+ aC1 = Point(FRound(aB2DC1.getX()), FRound(aB2DC1.getY()));
+ bC1 = true;
+ }
+
+ if(aNewXP.isNextControlPointUsed(nPointNum))
+ {
+ const basegfx::B2DPoint aB2DC2(aNewXP.getNextControlPoint(nPointNum));
+ aC2 = Point(FRound(aB2DC2.getX()), FRound(aB2DC2.getY()));
+ bC2 = true;
+ }
+
+ (*pTrFunc)(aPos,&aC1,&aC2,p1,p2,p3,p4);
+ aNewXP.setB2DPoint(nPointNum, basegfx::B2DPoint(aPos.X(), aPos.Y()));
+
+ if (bC1)
+ {
+ aNewXP.setPrevControlPoint(nPointNum, basegfx::B2DPoint(aC1.X(), aC1.Y()));
+ }
+
+ if (bC2)
+ {
+ aNewXP.setNextControlPoint(nPointNum, basegfx::B2DPoint(aC2.X(), aC2.Y()));
+ }
+
+ aXPP.setB2DPolygon(nPolyNum, aNewXP);
+ }
+ }
+
+ pPath->SetPathPoly(aXPP);
+ }
+}
+
+
+static void ImpMove(Point& rPt, Point* pC1, Point* pC2, const void* p1, const void* /*p2*/, const void* /*p3*/, const void* /*p4*/)
+{
+ rPt.Move(*static_cast<const Size*>(p1));
+ if (pC1!=nullptr) pC1->Move(*static_cast<const Size*>(p1));
+ if (pC2!=nullptr) pC2->Move(*static_cast<const Size*>(p1));
+}
+
+void SdrPolyEditView::MoveMarkedPoints(const Size& rSiz)
+{
+ ForceUndirtyMrkPnt();
+ OUString aStr(SvxResId(STR_EditMove));
+ BegUndo(aStr,GetDescriptionOfMarkedPoints(),SdrRepeatFunc::Move);
+ ImpTransformMarkedPoints(ImpMove,&rSiz);
+ EndUndo();
+ AdjustMarkHdl();
+}
+
+static void ImpResize(Point& rPt, Point* pC1, Point* pC2, const void* p1, const void* p2, const void* p3, const void* /*p4*/)
+{
+ ResizePoint(rPt,*static_cast<const Point*>(p1),*static_cast<const Fraction*>(p2),*static_cast<const Fraction*>(p3));
+ if (pC1!=nullptr) ResizePoint(*pC1,*static_cast<const Point*>(p1),*static_cast<const Fraction*>(p2),*static_cast<const Fraction*>(p3));
+ if (pC2!=nullptr) ResizePoint(*pC2,*static_cast<const Point*>(p1),*static_cast<const Fraction*>(p2),*static_cast<const Fraction*>(p3));
+}
+
+void SdrPolyEditView::ResizeMarkedPoints(const Point& rRef, const Fraction& xFact, const Fraction& yFact)
+{
+ ForceUndirtyMrkPnt();
+ OUString aStr(SvxResId(STR_EditResize));
+ BegUndo(aStr,GetDescriptionOfMarkedPoints(),SdrRepeatFunc::Resize);
+ ImpTransformMarkedPoints(ImpResize,&rRef,&xFact,&yFact);
+ EndUndo();
+ AdjustMarkHdl();
+}
+
+static void ImpRotate(Point& rPt, Point* pC1, Point* pC2, const void* p1, const void* /*p2*/, const void* p3, const void* p4)
+{
+ RotatePoint(rPt,*static_cast<const Point*>(p1),*static_cast<const double*>(p3),*static_cast<const double*>(p4));
+ if (pC1!=nullptr) RotatePoint(*pC1,*static_cast<const Point*>(p1),*static_cast<const double*>(p3),*static_cast<const double*>(p4));
+ if (pC2!=nullptr) RotatePoint(*pC2,*static_cast<const Point*>(p1),*static_cast<const double*>(p3),*static_cast<const double*>(p4));
+}
+
+void SdrPolyEditView::RotateMarkedPoints(const Point& rRef, Degree100 nAngle)
+{
+ ForceUndirtyMrkPnt();
+ OUString aStr(SvxResId(STR_EditResize));
+ BegUndo(aStr,GetDescriptionOfMarkedPoints(),SdrRepeatFunc::Rotate);
+ double nSin = sin(toRadians(nAngle));
+ double nCos = cos(toRadians(nAngle));
+ ImpTransformMarkedPoints(ImpRotate,&rRef,&nAngle,&nSin,&nCos);
+ EndUndo();
+ AdjustMarkHdl();
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/svdraw/svdsnpv.cxx b/svx/source/svdraw/svdsnpv.cxx
new file mode 100644
index 000000000..bcef8b3c3
--- /dev/null
+++ b/svx/source/svdraw/svdsnpv.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 <svx/svdsnpv.hxx>
+#include <math.h>
+
+#include <svx/svdobj.hxx>
+#include <svx/svdpagv.hxx>
+#include <svx/svdpage.hxx>
+#include <svx/svditer.hxx>
+#include <svx/sdr/overlay/overlayobjectlist.hxx>
+#include <sdr/overlay/overlaycrosshair.hxx>
+#include <sdr/overlay/overlayhelpline.hxx>
+#include <svx/sdr/overlay/overlaymanager.hxx>
+#include <svx/sdrpaintwindow.hxx>
+#include <tools/debug.hxx>
+#include <vcl/ptrstyle.hxx>
+
+
+class ImplPageOriginOverlay
+{
+ // The OverlayObjects
+ sdr::overlay::OverlayObjectList maObjects;
+
+ // The current position in logical coordinates
+ basegfx::B2DPoint maPosition;
+
+public:
+ ImplPageOriginOverlay(const SdrPaintView& rView, const basegfx::B2DPoint& rStartPos);
+
+ // The OverlayObjects are cleared using the destructor of OverlayObjectList.
+ // That destructor calls clear() at the list which removes all objects from the
+ // OverlayManager and deletes them.
+
+ void SetPosition(const basegfx::B2DPoint& rNewPosition);
+};
+
+ImplPageOriginOverlay::ImplPageOriginOverlay(const SdrPaintView& rView, const basegfx::B2DPoint& rStartPos)
+: maPosition(rStartPos)
+{
+ for(sal_uInt32 a(0); a < rView.PaintWindowCount(); a++)
+ {
+ SdrPaintWindow* pCandidate = rView.GetPaintWindow(a);
+ const rtl::Reference< sdr::overlay::OverlayManager >& xTargetOverlay = pCandidate->GetOverlayManager();
+
+ if (xTargetOverlay.is())
+ {
+ std::unique_ptr<sdr::overlay::OverlayCrosshairStriped> aNew(new sdr::overlay::OverlayCrosshairStriped(
+ maPosition));
+ xTargetOverlay->add(*aNew);
+ maObjects.append(std::move(aNew));
+ }
+ }
+}
+
+void ImplPageOriginOverlay::SetPosition(const basegfx::B2DPoint& rNewPosition)
+{
+ if(rNewPosition == maPosition)
+ return;
+
+ // apply to OverlayObjects
+ for(sal_uInt32 a(0); a < maObjects.count(); a++)
+ {
+ sdr::overlay::OverlayCrosshairStriped* pCandidate =
+ static_cast< sdr::overlay::OverlayCrosshairStriped* >(&maObjects.getOverlayObject(a));
+
+ if(pCandidate)
+ {
+ pCandidate->setBasePosition(rNewPosition);
+ }
+ }
+
+ // remember new position
+ maPosition = rNewPosition;
+}
+
+
+class ImplHelpLineOverlay
+{
+ // The OverlayObjects
+ sdr::overlay::OverlayObjectList maObjects;
+
+ // The current position in logical coordinates
+ basegfx::B2DPoint maPosition;
+
+ // HelpLine specific stuff
+ SdrPageView* mpPageView;
+ sal_uInt16 mnHelpLineNumber;
+ SdrHelpLineKind meHelpLineKind;
+
+public:
+ ImplHelpLineOverlay(const SdrPaintView& rView, const basegfx::B2DPoint& rStartPos,
+ SdrPageView* pPageView, sal_uInt16 nHelpLineNumber, SdrHelpLineKind eKind);
+
+ // The OverlayObjects are cleared using the destructor of OverlayObjectList.
+ // That destructor calls clear() at the list which removes all objects from the
+ // OverlayManager and deletes them.
+
+ void SetPosition(const basegfx::B2DPoint& rNewPosition);
+
+ // access to HelpLine specific stuff
+ SdrPageView* GetPageView() const { return mpPageView; }
+ sal_uInt16 GetHelpLineNumber() const { return mnHelpLineNumber; }
+ SdrHelpLineKind GetHelpLineKind() const { return meHelpLineKind; }
+};
+
+ImplHelpLineOverlay::ImplHelpLineOverlay(
+ const SdrPaintView& rView, const basegfx::B2DPoint& rStartPos,
+ SdrPageView* pPageView, sal_uInt16 nHelpLineNumber, SdrHelpLineKind eKind)
+: maPosition(rStartPos),
+ mpPageView(pPageView),
+ mnHelpLineNumber(nHelpLineNumber),
+ meHelpLineKind(eKind)
+{
+ for(sal_uInt32 a(0); a < rView.PaintWindowCount(); a++)
+ {
+ SdrPaintWindow* pCandidate = rView.GetPaintWindow(a);
+ const rtl::Reference< sdr::overlay::OverlayManager >& xTargetOverlay = pCandidate->GetOverlayManager();
+
+ if (xTargetOverlay.is())
+ {
+ std::unique_ptr<sdr::overlay::OverlayHelplineStriped> aNew(new sdr::overlay::OverlayHelplineStriped(
+ maPosition, meHelpLineKind));
+ xTargetOverlay->add(*aNew);
+ maObjects.append(std::move(aNew));
+ }
+ }
+}
+
+void ImplHelpLineOverlay::SetPosition(const basegfx::B2DPoint& rNewPosition)
+{
+ if(rNewPosition == maPosition)
+ return;
+
+ // apply to OverlayObjects
+ for(sal_uInt32 a(0); a < maObjects.count(); a++)
+ {
+ sdr::overlay::OverlayHelplineStriped* pCandidate =
+ static_cast< sdr::overlay::OverlayHelplineStriped* >(&maObjects.getOverlayObject(a));
+
+ if(pCandidate)
+ {
+ pCandidate->setBasePosition(rNewPosition);
+ }
+ }
+
+ // remember new position
+ maPosition = rNewPosition;
+}
+
+SdrSnapView::SdrSnapView(
+ SdrModel& rSdrModel,
+ OutputDevice* pOut)
+: SdrPaintView(rSdrModel, pOut)
+ ,mpPageOriginOverlay(nullptr)
+ ,mpHelpLineOverlay(nullptr)
+ ,nMagnSizPix(4)
+ ,nSnapAngle(1500)
+ ,nEliminatePolyPointLimitAngle(0)
+ ,eCrookMode(SdrCrookMode::Rotate)
+ ,bSnapEnab(true)
+ ,bGridSnap(true)
+ ,bBordSnap(true)
+ ,bHlplSnap(true)
+ ,bOFrmSnap(true)
+ ,bOPntSnap(false)
+ ,bOConSnap(true)
+ ,bMoveSnapOnlyTopLeft(false)
+ ,bOrtho(false)
+ ,bBigOrtho(true)
+ ,bAngleSnapEnab(false)
+ ,bMoveOnlyDragging(false)
+ ,bSlantButShear(false)
+ ,bCrookNoContortion(false)
+ ,bEliminatePolyPoints(false)
+{
+}
+
+SdrSnapView::~SdrSnapView()
+{
+ BrkSetPageOrg();
+ BrkDragHelpLine();
+}
+
+
+bool SdrSnapView::IsAction() const
+{
+ return IsSetPageOrg() || IsDragHelpLine() || SdrPaintView::IsAction();
+}
+
+void SdrSnapView::MovAction(const Point& rPnt)
+{
+ SdrPaintView::MovAction(rPnt);
+ if (IsSetPageOrg()) {
+ MovSetPageOrg(rPnt);
+ }
+ if (IsDragHelpLine()) {
+ MovDragHelpLine(rPnt);
+ }
+}
+
+void SdrSnapView::EndAction()
+{
+ if (IsSetPageOrg()) {
+ EndSetPageOrg();
+ }
+ if (IsDragHelpLine()) {
+ EndDragHelpLine();
+ }
+ SdrPaintView::EndAction();
+}
+
+void SdrSnapView::BckAction()
+{
+ BrkSetPageOrg();
+ BrkDragHelpLine();
+ SdrPaintView::BckAction();
+}
+
+void SdrSnapView::BrkAction()
+{
+ BrkSetPageOrg();
+ BrkDragHelpLine();
+ SdrPaintView::BrkAction();
+}
+
+void SdrSnapView::TakeActionRect(tools::Rectangle& rRect) const
+{
+ if (IsSetPageOrg() || IsDragHelpLine()) {
+ rRect=tools::Rectangle(maDragStat.GetNow(),maDragStat.GetNow());
+ } else {
+ SdrPaintView::TakeActionRect(rRect);
+ }
+}
+
+
+Point SdrSnapView::GetSnapPos(const Point& rPnt, const SdrPageView* pPV) const
+{
+ Point aPt(rPnt);
+ SnapPos(aPt,pPV);
+ return aPt;
+}
+
+#define NOT_SNAPPED 0x7FFFFFFF
+SdrSnap SdrSnapView::SnapPos(Point& rPnt, const SdrPageView* pPV) const
+{
+ if (!bSnapEnab) return SdrSnap::NOTSNAPPED;
+ tools::Long x=rPnt.X();
+ tools::Long y=rPnt.Y();
+ if (pPV==nullptr) {
+ pPV=GetSdrPageView();
+ if (pPV==nullptr) return SdrSnap::NOTSNAPPED;
+ }
+
+ tools::Long dx=NOT_SNAPPED;
+ tools::Long dy=NOT_SNAPPED;
+ tools::Long dx1,dy1;
+ tools::Long mx=aMagnSiz.Width();
+ tools::Long my=aMagnSiz.Height();
+ if (mbHlplVisible && bHlplSnap && !IsDragHelpLine())
+ {
+ const SdrHelpLineList& rHLL=pPV->GetHelpLines();
+ sal_uInt16 nCount=rHLL.GetCount();
+ for (sal_uInt16 i=nCount; i>0;) {
+ i--;
+ const SdrHelpLine& rHL=rHLL[i];
+ const Point& rPos=rHL.GetPos();
+ switch (rHL.GetKind()) {
+ case SdrHelpLineKind::Vertical: {
+ tools::Long a=x-rPos.X();
+ if (std::abs(a)<=mx) { dx1=-a; if (std::abs(dx1)<std::abs(dx)) dx=dx1; }
+ } break;
+ case SdrHelpLineKind::Horizontal: {
+ tools::Long b=y-rPos.Y();
+ if (std::abs(b)<=my) { dy1=-b; if (std::abs(dy1)<std::abs(dy)) dy=dy1; }
+ } break;
+ case SdrHelpLineKind::Point: {
+ tools::Long a=x-rPos.X();
+ tools::Long b=y-rPos.Y();
+ if (std::abs(a)<=mx && std::abs(b)<=my) {
+ dx1=-a; dy1=-b;
+ if (std::abs(dx1)<std::abs(dx) && std::abs(dy1)<std::abs(dy)) { dx=dx1; dy=dy1; }
+ }
+ } break;
+ } // switch
+ }
+ }
+ if (mbBordVisible && bBordSnap) {
+ SdrPage* pPage=pPV->GetPage();
+ tools::Long xs=pPage->GetWidth();
+ tools::Long ys=pPage->GetHeight();
+ tools::Long lft=pPage->GetLeftBorder();
+ tools::Long rgt=pPage->GetRightBorder();
+ tools::Long upp=pPage->GetUpperBorder();
+ tools::Long lwr=pPage->GetLowerBorder();
+ tools::Long a;
+ a=x- lft ; if (std::abs(a)<=mx) { dx1=-a; if (std::abs(dx1)<std::abs(dx)) dx=dx1; } // left margin
+ a=x-(xs-rgt); if (std::abs(a)<=mx) { dx1=-a; if (std::abs(dx1)<std::abs(dx)) dx=dx1; } // right margin
+ a=x ; if (std::abs(a)<=mx) { dx1=-a; if (std::abs(dx1)<std::abs(dx)) dx=dx1; } // left edge of paper
+ a=x- xs ; if (std::abs(a)<=mx) { dx1=-a; if (std::abs(dx1)<std::abs(dx)) dx=dx1; } // right edge of paper
+ a=y- upp ; if (std::abs(a)<=my) { dy1=-a; if (std::abs(dy1)<std::abs(dy)) dy=dy1; } // left margin
+ a=y-(ys-lwr); if (std::abs(a)<=my) { dy1=-a; if (std::abs(dy1)<std::abs(dy)) dy=dy1; } // right margin
+ a=y ; if (std::abs(a)<=my) { dy1=-a; if (std::abs(dy1)<std::abs(dy)) dy=dy1; } // left edge of paper
+ a=y- ys ; if (std::abs(a)<=my) { dy1=-a; if (std::abs(dy1)<std::abs(dy)) dy=dy1; } // right edge of paper
+ }
+ if (bOFrmSnap || bOPntSnap) {
+ sal_uInt32 nMaxPointSnapCount=200;
+ sal_uInt32 nMaxFrameSnapCount=200;
+
+ // go back to SdrIterMode::DeepNoGroups runthrough for snap to object comparisons
+ SdrObjListIter aIter(pPV->GetPage(),SdrIterMode::DeepNoGroups,true);
+
+ while (aIter.IsMore() && (nMaxPointSnapCount>0 || nMaxFrameSnapCount>0)) {
+ SdrObject* pO=aIter.Next();
+ tools::Rectangle aRect(pO->GetCurrentBoundRect());
+ aRect.AdjustLeft( -mx );
+ aRect.AdjustRight(mx );
+ aRect.AdjustTop( -my );
+ aRect.AdjustBottom(my );
+ if (aRect.Contains(rPnt)) {
+ if (bOPntSnap && nMaxPointSnapCount>0)
+ {
+ sal_uInt32 nCount(pO->GetSnapPointCount());
+ for (sal_uInt32 i(0); i < nCount && nMaxPointSnapCount > 0; i++)
+ {
+ Point aP(pO->GetSnapPoint(i));
+ dx1=x-aP.X();
+ dy1=y-aP.Y();
+ if (std::abs(dx1)<=mx && std::abs(dy1)<=my && std::abs(dx1)<std::abs(dx) && std::abs(dy1)<std::abs(dy)) {
+ dx=-dx1;
+ dy=-dy1;
+ }
+ nMaxPointSnapCount--;
+ }
+ }
+ if (bOFrmSnap && nMaxFrameSnapCount>0) {
+ tools::Rectangle aLog(pO->GetSnapRect());
+ tools::Rectangle aR1(aLog);
+ aR1.AdjustLeft( -mx );
+ aR1.AdjustRight(mx );
+ aR1.AdjustTop( -my );
+ aR1.AdjustBottom(my );
+ if (aR1.Contains(rPnt)) {
+ if (std::abs(x-aLog.Left ())<=mx) { dx1=-(x-aLog.Left ()); if (std::abs(dx1)<std::abs(dx)) dx=dx1; }
+ if (std::abs(x-aLog.Right ())<=mx) { dx1=-(x-aLog.Right ()); if (std::abs(dx1)<std::abs(dx)) dx=dx1; }
+ if (std::abs(y-aLog.Top ())<=my) { dy1=-(y-aLog.Top ()); if (std::abs(dy1)<std::abs(dy)) dy=dy1; }
+ if (std::abs(y-aLog.Bottom())<=my) { dy1=-(y-aLog.Bottom()); if (std::abs(dy1)<std::abs(dy)) dy=dy1; }
+ }
+ nMaxFrameSnapCount--;
+ }
+ }
+ }
+ }
+ if(bGridSnap)
+ {
+ double fSnapWidth(aSnapWdtX);
+ if(dx == NOT_SNAPPED && fSnapWidth != 0.0)
+ {
+ double fx = static_cast<double>(x);
+
+ // round instead of trunc
+ if(fx - static_cast<double>(pPV->GetPageOrigin().X()) >= 0.0)
+ fx += fSnapWidth / 2.0;
+ else
+ fx -= fSnapWidth / 2.0;
+
+ x = static_cast<tools::Long>((fx - static_cast<double>(pPV->GetPageOrigin().X())) / fSnapWidth);
+ x = static_cast<tools::Long>(static_cast<double>(x) * fSnapWidth + static_cast<double>(pPV->GetPageOrigin().X()));
+ dx = 0;
+ }
+ fSnapWidth = double(aSnapWdtY);
+ if(dy == NOT_SNAPPED && fSnapWidth)
+ {
+ double fy = static_cast<double>(y);
+
+ // round instead of trunc
+ if(fy - static_cast<double>(pPV->GetPageOrigin().Y()) >= 0.0)
+ fy += fSnapWidth / 2.0;
+ else
+ fy -= fSnapWidth / 2.0;
+
+ y = static_cast<tools::Long>((fy - static_cast<double>(pPV->GetPageOrigin().Y())) / fSnapWidth);
+ y = static_cast<tools::Long>(static_cast<double>(y) * fSnapWidth + static_cast<double>(pPV->GetPageOrigin().Y()));
+ dy = 0;
+ }
+ }
+ SdrSnap bRet=SdrSnap::NOTSNAPPED;
+ if (dx==NOT_SNAPPED) dx=0; else bRet|=SdrSnap::XSNAPPED;
+ if (dy==NOT_SNAPPED) dy=0; else bRet|=SdrSnap::YSNAPPED;
+ rPnt.setX(x+dx );
+ rPnt.setY(y+dy );
+ return bRet;
+}
+
+void SdrSnapView::CheckSnap(const Point& rPt, tools::Long& nBestXSnap, tools::Long& nBestYSnap, bool& bXSnapped, bool& bYSnapped) const
+{
+ Point aPt(rPt);
+ SdrSnap nRet=SnapPos(aPt,nullptr);
+ aPt-=rPt;
+ if (nRet & SdrSnap::XSNAPPED) {
+ if (bXSnapped) {
+ if (std::abs(aPt.X())<std::abs(nBestXSnap)) {
+ nBestXSnap=aPt.X();
+ }
+ } else {
+ nBestXSnap=aPt.X();
+ bXSnapped=true;
+ }
+ }
+ if (nRet & SdrSnap::YSNAPPED) {
+ if (bYSnapped) {
+ if (std::abs(aPt.Y())<std::abs(nBestYSnap)) {
+ nBestYSnap=aPt.Y();
+ }
+ } else {
+ nBestYSnap=aPt.Y();
+ bYSnapped=true;
+ }
+ }
+}
+
+
+void SdrSnapView::BegSetPageOrg(const Point& rPnt)
+{
+ BrkAction();
+
+ DBG_ASSERT(nullptr == mpPageOriginOverlay, "SdrSnapView::BegSetPageOrg: There exists an ImplPageOriginOverlay (!)");
+ basegfx::B2DPoint aStartPos(rPnt.X(), rPnt.Y());
+ mpPageOriginOverlay = new ImplPageOriginOverlay(*this, aStartPos);
+ maDragStat.Reset(GetSnapPos(rPnt,nullptr));
+}
+
+void SdrSnapView::MovSetPageOrg(const Point& rPnt)
+{
+ if(IsSetPageOrg())
+ {
+ maDragStat.NextMove(GetSnapPos(rPnt,nullptr));
+ DBG_ASSERT(mpPageOriginOverlay, "SdrSnapView::MovSetPageOrg: no ImplPageOriginOverlay (!)");
+ basegfx::B2DPoint aNewPos(maDragStat.GetNow().X(), maDragStat.GetNow().Y());
+ mpPageOriginOverlay->SetPosition(aNewPos);
+ }
+}
+
+void SdrSnapView::EndSetPageOrg()
+{
+ if(!IsSetPageOrg())
+ return;
+
+ SdrPageView* pPV = GetSdrPageView();
+
+ if(pPV)
+ {
+ Point aPnt(maDragStat.GetNow());
+ pPV->SetPageOrigin(aPnt);
+ }
+
+ // cleanup
+ BrkSetPageOrg();
+}
+
+void SdrSnapView::BrkSetPageOrg()
+{
+ if(IsSetPageOrg())
+ {
+ DBG_ASSERT(mpPageOriginOverlay, "SdrSnapView::MovSetPageOrg: no ImplPageOriginOverlay (!)");
+ delete mpPageOriginOverlay;
+ mpPageOriginOverlay = nullptr;
+ }
+}
+
+
+bool SdrSnapView::PickHelpLine(const Point& rPnt, short nTol, const OutputDevice& rOut, sal_uInt16& rnHelpLineNum, SdrPageView*& rpPV) const
+{
+ rpPV=nullptr;
+ nTol=ImpGetHitTolLogic(nTol,&rOut);
+ SdrPageView* pPV = GetSdrPageView();
+
+ if(pPV)
+ {
+ Point aPnt(rPnt);
+ sal_uInt16 nIndex=pPV->GetHelpLines().HitTest(aPnt,sal_uInt16(nTol),rOut);
+ if (nIndex!=SDRHELPLINE_NOTFOUND) {
+ rpPV=pPV;
+ rnHelpLineNum=nIndex;
+ return true;
+ }
+ }
+ return false;
+}
+
+// start HelpLine drag for new HelpLine
+bool SdrSnapView::BegDragHelpLine(sal_uInt16 nHelpLineNum, SdrPageView* pPV)
+{
+ bool bRet(false);
+
+ BrkAction();
+
+ if(pPV && nHelpLineNum < pPV->GetHelpLines().GetCount())
+ {
+ const SdrHelpLineList& rHelpLines = pPV->GetHelpLines();
+ const SdrHelpLine& rHelpLine = rHelpLines[nHelpLineNum];
+ Point aHelpLinePos = rHelpLine.GetPos();
+ basegfx::B2DPoint aStartPos(aHelpLinePos.X(), aHelpLinePos.Y());
+
+ DBG_ASSERT(nullptr == mpHelpLineOverlay, "SdrSnapView::BegDragHelpLine: There exists an ImplHelpLineOverlay (!)");
+ mpHelpLineOverlay = new ImplHelpLineOverlay(*this, aStartPos, pPV, nHelpLineNum, rHelpLine.GetKind());
+
+ maDragStat.Reset(GetSnapPos(aHelpLinePos, pPV));
+ maDragStat.SetMinMove(ImpGetMinMovLogic(-3, nullptr));
+
+ bRet = true;
+ }
+
+ return bRet;
+}
+
+// start HelpLine drag with existing HelpLine
+void SdrSnapView::BegDragHelpLine(const Point& rPnt, SdrHelpLineKind eNewKind)
+{
+ BrkAction();
+
+ if(GetSdrPageView())
+ {
+ DBG_ASSERT(nullptr == mpHelpLineOverlay, "SdrSnapView::BegDragHelpLine: There exists an ImplHelpLineOverlay (!)");
+ basegfx::B2DPoint aStartPos(rPnt.X(), rPnt.Y());
+ mpHelpLineOverlay = new ImplHelpLineOverlay(*this, aStartPos, nullptr, 0, eNewKind);
+ maDragStat.Reset(GetSnapPos(rPnt, nullptr));
+ }
+}
+
+PointerStyle SdrSnapView::GetDraggedHelpLinePointer() const
+{
+ if(IsDragHelpLine())
+ {
+ switch(mpHelpLineOverlay->GetHelpLineKind())
+ {
+ case SdrHelpLineKind::Vertical : return PointerStyle::ESize;
+ case SdrHelpLineKind::Horizontal: return PointerStyle::SSize;
+ default : return PointerStyle::Move;
+ }
+ }
+
+ return PointerStyle::Move;
+}
+
+void SdrSnapView::MovDragHelpLine(const Point& rPnt)
+{
+ if(IsDragHelpLine() && maDragStat.CheckMinMoved(rPnt))
+ {
+ Point aPnt(GetSnapPos(rPnt, nullptr));
+
+ if(aPnt != maDragStat.GetNow())
+ {
+ maDragStat.NextMove(aPnt);
+ DBG_ASSERT(mpHelpLineOverlay, "SdrSnapView::MovDragHelpLine: no ImplHelpLineOverlay (!)");
+ basegfx::B2DPoint aNewPos(maDragStat.GetNow().X(), maDragStat.GetNow().Y());
+ mpHelpLineOverlay->SetPosition(aNewPos);
+ }
+ }
+}
+
+bool SdrSnapView::EndDragHelpLine()
+{
+ bool bRet(false);
+
+ if(IsDragHelpLine())
+ {
+ if(maDragStat.IsMinMoved())
+ {
+ SdrPageView* pPageView = mpHelpLineOverlay->GetPageView();
+
+ if(pPageView)
+ {
+ // moved existing one
+ Point aPnt(maDragStat.GetNow());
+ const SdrHelpLineList& rHelpLines = pPageView->GetHelpLines();
+ SdrHelpLine aChangedHelpLine = rHelpLines[mpHelpLineOverlay->GetHelpLineNumber()];
+ aChangedHelpLine.SetPos(aPnt);
+ pPageView->SetHelpLine(mpHelpLineOverlay->GetHelpLineNumber(), aChangedHelpLine);
+
+ bRet = true;
+ }
+ else
+ {
+ // create new one
+ pPageView = GetSdrPageView();
+
+ if(pPageView)
+ {
+ Point aPnt(maDragStat.GetNow());
+ SdrHelpLine aNewHelpLine(mpHelpLineOverlay->GetHelpLineKind(), aPnt);
+ pPageView->InsertHelpLine(aNewHelpLine);
+
+ bRet = true;
+ }
+ }
+ }
+
+ // cleanup
+ BrkDragHelpLine();
+ }
+
+ return bRet;
+}
+
+void SdrSnapView::BrkDragHelpLine()
+{
+ if(IsDragHelpLine())
+ {
+ DBG_ASSERT(mpHelpLineOverlay, "SdrSnapView::EndDragHelpLine: no ImplHelpLineOverlay (!)");
+ delete mpHelpLineOverlay;
+ mpHelpLineOverlay = nullptr;
+ }
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/svdraw/svdtext.cxx b/svx/source/svdraw/svdtext.cxx
new file mode 100644
index 000000000..72591ef67
--- /dev/null
+++ b/svx/source/svdraw/svdtext.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 <svx/svdotext.hxx>
+#include <svx/svdetc.hxx>
+#include <editeng/outlobj.hxx>
+#include <svx/svdoutl.hxx>
+#include <svx/svdmodel.hxx>
+#include <svl/itemset.hxx>
+#include <osl/diagnose.h>
+#include <libxml/xmlwriter.h>
+#include <memory>
+
+SdrText::SdrText( SdrTextObj& rObject )
+: mrObject( rObject )
+, mbPortionInfoChecked( false )
+{
+ OSL_ENSURE(&mrObject, "SdrText created without SdrTextObj (!)");
+}
+
+SdrText::~SdrText()
+{
+ clearWeak();
+}
+
+void SdrText::CheckPortionInfo( const SdrOutliner& rOutliner )
+{
+ if(mbPortionInfoChecked)
+ return;
+
+ // #i102062# no action when the Outliner is the HitTestOutliner,
+ // this will remove WrongList info at the OPO
+ if(&rOutliner == &mrObject.getSdrModelFromSdrObject().GetHitTestOutliner())
+ return;
+
+ // TODO: optimization: we could create a BigTextObject
+ mbPortionInfoChecked=true;
+
+ if(mpOutlinerParaObject && rOutliner.ShouldCreateBigTextObject())
+ {
+ // #i102062# MemoryLeak closed
+ mpOutlinerParaObject = rOutliner.CreateParaObject();
+ }
+}
+
+void SdrText::ReformatText()
+{
+ mbPortionInfoChecked=false;
+ mpOutlinerParaObject->ClearPortionInfo();
+}
+
+const SfxItemSet& SdrText::GetItemSet() const
+{
+ return const_cast< SdrText* >(this)->GetObjectItemSet();
+}
+
+void SdrText::SetOutlinerParaObject( std::optional<OutlinerParaObject> pTextObject )
+{
+ // Update HitTestOutliner
+ const SdrTextObj* pTestObj(mrObject.getSdrModelFromSdrObject().GetHitTestOutliner().GetTextObj());
+
+ if(pTestObj)
+ if ( (!pTestObj->GetOutlinerParaObject() && !mpOutlinerParaObject)
+ || (pTestObj->GetOutlinerParaObject() && mpOutlinerParaObject && *pTestObj->GetOutlinerParaObject() == *mpOutlinerParaObject) )
+ {
+ mrObject.getSdrModelFromSdrObject().GetHitTestOutliner().SetTextObj(nullptr);
+ }
+
+ mpOutlinerParaObject = std::move(pTextObject);
+ mbPortionInfoChecked = false;
+}
+
+OutlinerParaObject* SdrText::GetOutlinerParaObject()
+{
+ return mpOutlinerParaObject ? &*mpOutlinerParaObject : nullptr;
+}
+
+const OutlinerParaObject* SdrText::GetOutlinerParaObject() const
+{
+ return mpOutlinerParaObject ? &*mpOutlinerParaObject : nullptr;
+}
+
+/** returns the current OutlinerParaObject and removes it from this instance */
+std::optional<OutlinerParaObject> SdrText::RemoveOutlinerParaObject()
+{
+ // Update HitTestOutliner
+ const SdrTextObj* pTestObj(mrObject.getSdrModelFromSdrObject().GetHitTestOutliner().GetTextObj());
+
+ if(pTestObj)
+ if ( (!pTestObj->GetOutlinerParaObject() && !mpOutlinerParaObject)
+ || (pTestObj->GetOutlinerParaObject() && mpOutlinerParaObject && *pTestObj->GetOutlinerParaObject() == *mpOutlinerParaObject) )
+ {
+ mrObject.getSdrModelFromSdrObject().GetHitTestOutliner().SetTextObj(nullptr);
+ }
+
+ std::optional<OutlinerParaObject> pOPO = std::move(mpOutlinerParaObject);
+ mbPortionInfoChecked = false;
+
+ return pOPO;
+}
+
+void SdrText::ForceOutlinerParaObject( OutlinerMode nOutlMode )
+{
+ if(mpOutlinerParaObject)
+ return;
+
+ std::unique_ptr<Outliner> pOutliner(
+ SdrMakeOutliner(
+ nOutlMode,
+ mrObject.getSdrModelFromSdrObject()));
+
+ if(pOutliner)
+ {
+ Outliner& aDrawOutliner(mrObject.getSdrModelFromSdrObject().GetDrawOutliner());
+ pOutliner->SetCalcFieldValueHdl( aDrawOutliner.GetCalcFieldValueHdl() );
+ pOutliner->SetStyleSheet( 0, GetStyleSheet());
+ SetOutlinerParaObject( pOutliner->CreateParaObject() );
+ }
+}
+
+const SfxItemSet& SdrText::GetObjectItemSet()
+{
+ return mrObject.GetObjectItemSet();
+}
+
+SfxStyleSheet* SdrText::GetStyleSheet() const
+{
+ return mrObject.GetStyleSheet();
+}
+
+void SdrText::dumpAsXml(xmlTextWriterPtr pWriter) const
+{
+ (void)xmlTextWriterStartElement(pWriter, BAD_CAST("SdrText"));
+ mpOutlinerParaObject->dumpAsXml(pWriter);
+ (void)xmlTextWriterEndElement(pWriter);
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/svdraw/svdtrans.cxx b/svx/source/svdraw/svdtrans.cxx
new file mode 100644
index 000000000..201e9f86d
--- /dev/null
+++ b/svx/source/svdraw/svdtrans.cxx
@@ -0,0 +1,867 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * 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/svdtrans.hxx>
+#include <math.h>
+#include <svx/xpoly.hxx>
+#include <rtl/ustrbuf.hxx>
+
+#include <vcl/virdev.hxx>
+#include <tools/bigint.hxx>
+#include <tools/UnitConversion.hxx>
+#include <unotools/syslocale.hxx>
+#include <unotools/localedatawrapper.hxx>
+#include <sal/log.hxx>
+
+void MoveXPoly(XPolygon& rPoly, const Size& S)
+{
+ rPoly.Move(S.Width(),S.Height());
+}
+
+void ResizeRect(tools::Rectangle& rRect, const Point& rRef, const Fraction& rxFact, const Fraction& ryFact)
+{
+ Fraction aXFact(rxFact);
+ Fraction aYFact(ryFact);
+
+ if (!aXFact.IsValid()) {
+ SAL_WARN( "svx.svdraw", "invalid fraction xFract, using Fraction(1,1)" );
+ aXFact = Fraction(1,1);
+ tools::Long nWdt = rRect.Right() - rRect.Left();
+ if (nWdt == 0) rRect.AdjustRight( 1 );
+ }
+ rRect.SetLeft( rRef.X() + FRound( (rRect.Left() - rRef.X()) * double(aXFact) ) );
+ rRect.SetRight( rRef.X() + FRound( (rRect.Right() - rRef.X()) * double(aXFact) ) );
+
+ if (!aYFact.IsValid()) {
+ SAL_WARN( "svx.svdraw", "invalid fraction yFract, using Fraction(1,1)" );
+ aYFact = Fraction(1,1);
+ tools::Long nHgt = rRect.Bottom() - rRect.Top();
+ if (nHgt == 0) rRect.AdjustBottom( 1 );
+ }
+ rRect.SetTop( rRef.Y() + FRound( (rRect.Top() - rRef.Y()) * double(aYFact) ) );
+ rRect.SetBottom( rRef.Y() + FRound( (rRect.Bottom() - rRef.Y()) * double(aYFact) ) );
+
+ rRect.Justify();
+}
+
+
+void ResizePoly(tools::Polygon& rPoly, const Point& rRef, const Fraction& xFact, const Fraction& yFact)
+{
+ sal_uInt16 nCount=rPoly.GetSize();
+ for (sal_uInt16 i=0; i<nCount; i++) {
+ ResizePoint(rPoly[i],rRef,xFact,yFact);
+ }
+}
+
+void ResizeXPoly(XPolygon& rPoly, const Point& rRef, const Fraction& xFact, const Fraction& yFact)
+{
+ sal_uInt16 nCount=rPoly.GetPointCount();
+ for (sal_uInt16 i=0; i<nCount; i++) {
+ ResizePoint(rPoly[i],rRef,xFact,yFact);
+ }
+}
+
+void RotatePoly(tools::Polygon& rPoly, const Point& rRef, double sn, double cs)
+{
+ sal_uInt16 nCount=rPoly.GetSize();
+ for (sal_uInt16 i=0; i<nCount; i++) {
+ RotatePoint(rPoly[i],rRef,sn,cs);
+ }
+}
+
+void RotateXPoly(XPolygon& rPoly, const Point& rRef, double sn, double cs)
+{
+ sal_uInt16 nCount=rPoly.GetPointCount();
+ for (sal_uInt16 i=0; i<nCount; i++) {
+ RotatePoint(rPoly[i],rRef,sn,cs);
+ }
+}
+
+void RotateXPoly(XPolyPolygon& rPoly, const Point& rRef, double sn, double cs)
+{
+ sal_uInt16 nCount=rPoly.Count();
+ for (sal_uInt16 i=0; i<nCount; i++) {
+ RotateXPoly(rPoly[i],rRef,sn,cs);
+ }
+}
+
+void MirrorPoint(Point& rPnt, const Point& rRef1, const Point& rRef2)
+{
+ tools::Long mx=rRef2.X()-rRef1.X();
+ tools::Long my=rRef2.Y()-rRef1.Y();
+ if (mx==0) { // vertical axis
+ tools::Long dx=rRef1.X()-rPnt.X();
+ rPnt.AdjustX(2*dx );
+ } else if (my==0) { // horizontal axis
+ tools::Long dy=rRef1.Y()-rPnt.Y();
+ rPnt.AdjustY(2*dy );
+ } else if (mx==my) { // diagonal axis '\'
+ tools::Long dx1=rPnt.X()-rRef1.X();
+ tools::Long dy1=rPnt.Y()-rRef1.Y();
+ rPnt.setX(rRef1.X()+dy1 );
+ rPnt.setY(rRef1.Y()+dx1 );
+ } else if (mx==-my) { // diagonal axis '/'
+ tools::Long dx1=rPnt.X()-rRef1.X();
+ tools::Long dy1=rPnt.Y()-rRef1.Y();
+ rPnt.setX(rRef1.X()-dy1 );
+ rPnt.setY(rRef1.Y()-dx1 );
+ } else { // arbitrary axis
+ // TODO: Optimize this! Raise perpendicular on the mirroring axis..?
+ Degree100 nRefAngle=GetAngle(rRef2-rRef1);
+ rPnt-=rRef1;
+ Degree100 nPntAngle=GetAngle(rPnt);
+ Degree100 nAngle=2_deg100*(nRefAngle-nPntAngle);
+ double a = toRadians(nAngle);
+ double nSin=sin(a);
+ double nCos=cos(a);
+ RotatePoint(rPnt,Point(),nSin,nCos);
+ rPnt+=rRef1;
+ }
+}
+
+void MirrorXPoly(XPolygon& rPoly, const Point& rRef1, const Point& rRef2)
+{
+ sal_uInt16 nCount=rPoly.GetPointCount();
+ for (sal_uInt16 i=0; i<nCount; i++) {
+ MirrorPoint(rPoly[i],rRef1,rRef2);
+ }
+}
+
+void ShearPoly(tools::Polygon& rPoly, const Point& rRef, double tn)
+{
+ sal_uInt16 nCount=rPoly.GetSize();
+ for (sal_uInt16 i=0; i<nCount; i++) {
+ ShearPoint(rPoly[i],rRef,tn);
+ }
+}
+
+void ShearXPoly(XPolygon& rPoly, const Point& rRef, double tn, bool bVShear)
+{
+ sal_uInt16 nCount=rPoly.GetPointCount();
+ for (sal_uInt16 i=0; i<nCount; i++) {
+ ShearPoint(rPoly[i],rRef,tn,bVShear);
+ }
+}
+
+double CrookRotateXPoint(Point& rPnt, Point* pC1, Point* pC2, const Point& rCenter,
+ const Point& rRad, double& rSin, double& rCos, bool bVert)
+{
+ bool bC1=pC1!=nullptr;
+ bool bC2=pC2!=nullptr;
+ tools::Long x0=rPnt.X();
+ tools::Long y0=rPnt.Y();
+ tools::Long cx=rCenter.X();
+ tools::Long cy=rCenter.Y();
+ double nAngle=GetCrookAngle(rPnt,rCenter,rRad,bVert);
+ double sn=sin(nAngle);
+ double cs=cos(nAngle);
+ RotatePoint(rPnt,rCenter,sn,cs);
+ if (bC1) {
+ if (bVert) {
+ // move into the direction of the center, as a basic position for the rotation
+ pC1->AdjustY( -y0 );
+ // resize, account for the distance from the center
+ pC1->setY(FRound(static_cast<double>(pC1->Y()) /rRad.X()*(cx-pC1->X())) );
+ pC1->AdjustY(cy );
+ } else {
+ // move into the direction of the center, as a basic position for the rotation
+ pC1->AdjustX( -x0 );
+ // resize, account for the distance from the center
+ tools::Long nPntRad=cy-pC1->Y();
+ double nFact=static_cast<double>(nPntRad)/static_cast<double>(rRad.Y());
+ pC1->setX(FRound(static_cast<double>(pC1->X())*nFact) );
+ pC1->AdjustX(cx );
+ }
+ RotatePoint(*pC1,rCenter,sn,cs);
+ }
+ if (bC2) {
+ if (bVert) {
+ // move into the direction of the center, as a basic position for the rotation
+ pC2->AdjustY( -y0 );
+ // resize, account for the distance from the center
+ pC2->setY(FRound(static_cast<double>(pC2->Y()) /rRad.X()*(rCenter.X()-pC2->X())) );
+ pC2->AdjustY(cy );
+ } else {
+ // move into the direction of the center, as a basic position for the rotation
+ pC2->AdjustX( -x0 );
+ // resize, account for the distance from the center
+ tools::Long nPntRad=rCenter.Y()-pC2->Y();
+ double nFact=static_cast<double>(nPntRad)/static_cast<double>(rRad.Y());
+ pC2->setX(FRound(static_cast<double>(pC2->X())*nFact) );
+ pC2->AdjustX(cx );
+ }
+ RotatePoint(*pC2,rCenter,sn,cs);
+ }
+ rSin=sn;
+ rCos=cs;
+ return nAngle;
+}
+
+double CrookSlantXPoint(Point& rPnt, Point* pC1, Point* pC2, const Point& rCenter,
+ const Point& rRad, double& rSin, double& rCos, bool bVert)
+{
+ bool bC1=pC1!=nullptr;
+ bool bC2=pC2!=nullptr;
+ tools::Long x0=rPnt.X();
+ tools::Long y0=rPnt.Y();
+ tools::Long dx1=0,dy1=0;
+ tools::Long dxC1=0,dyC1=0;
+ tools::Long dxC2=0,dyC2=0;
+ if (bVert) {
+ tools::Long nStart=rCenter.X()-rRad.X();
+ dx1=rPnt.X()-nStart;
+ rPnt.setX(nStart );
+ if (bC1) {
+ dxC1=pC1->X()-nStart;
+ pC1->setX(nStart );
+ }
+ if (bC2) {
+ dxC2=pC2->X()-nStart;
+ pC2->setX(nStart );
+ }
+ } else {
+ tools::Long nStart=rCenter.Y()-rRad.Y();
+ dy1=rPnt.Y()-nStart;
+ rPnt.setY(nStart );
+ if (bC1) {
+ dyC1=pC1->Y()-nStart;
+ pC1->setY(nStart );
+ }
+ if (bC2) {
+ dyC2=pC2->Y()-nStart;
+ pC2->setY(nStart );
+ }
+ }
+ double nAngle=GetCrookAngle(rPnt,rCenter,rRad,bVert);
+ double sn=sin(nAngle);
+ double cs=cos(nAngle);
+ RotatePoint(rPnt,rCenter,sn,cs);
+ if (bC1) { if (bVert) pC1->AdjustY( -(y0-rCenter.Y()) ); else pC1->AdjustX( -(x0-rCenter.X()) ); RotatePoint(*pC1,rCenter,sn,cs); }
+ if (bC2) { if (bVert) pC2->AdjustY( -(y0-rCenter.Y()) ); else pC2->AdjustX( -(x0-rCenter.X()) ); RotatePoint(*pC2,rCenter,sn,cs); }
+ if (bVert) {
+ rPnt.AdjustX(dx1 );
+ if (bC1) pC1->AdjustX(dxC1 );
+ if (bC2) pC2->AdjustX(dxC2 );
+ } else {
+ rPnt.AdjustY(dy1 );
+ if (bC1) pC1->AdjustY(dyC1 );
+ if (bC2) pC2->AdjustY(dyC2 );
+ }
+ rSin=sn;
+ rCos=cs;
+ return nAngle;
+}
+
+double CrookStretchXPoint(Point& rPnt, Point* pC1, Point* pC2, const Point& rCenter,
+ const Point& rRad, double& rSin, double& rCos, bool bVert,
+ const tools::Rectangle& rRefRect)
+{
+ tools::Long y0=rPnt.Y();
+ CrookSlantXPoint(rPnt,pC1,pC2,rCenter,rRad,rSin,rCos,bVert);
+ if (bVert) {
+ } else {
+ tools::Long nTop=rRefRect.Top();
+ tools::Long nBtm=rRefRect.Bottom();
+ tools::Long nHgt=nBtm-nTop;
+ tools::Long dy=rPnt.Y()-y0;
+ double a=static_cast<double>(y0-nTop)/nHgt;
+ a*=dy;
+ rPnt.setY(y0+FRound(a) );
+ }
+ return 0.0;
+}
+
+
+void CrookRotatePoly(XPolygon& rPoly, const Point& rCenter, const Point& rRad, bool bVert)
+{
+ double nSin,nCos;
+ sal_uInt16 nPointCnt=rPoly.GetPointCount();
+ sal_uInt16 i=0;
+ while (i<nPointCnt) {
+ Point* pPnt=&rPoly[i];
+ Point* pC1=nullptr;
+ Point* pC2=nullptr;
+ if (i+1<nPointCnt && rPoly.IsControl(i)) { // control point to the left
+ pC1=pPnt;
+ i++;
+ pPnt=&rPoly[i];
+ }
+ i++;
+ if (i<nPointCnt && rPoly.IsControl(i)) { // control point to the right
+ pC2=&rPoly[i];
+ i++;
+ }
+ CrookRotateXPoint(*pPnt,pC1,pC2,rCenter,rRad,nSin,nCos,bVert);
+ }
+}
+
+void CrookSlantPoly(XPolygon& rPoly, const Point& rCenter, const Point& rRad, bool bVert)
+{
+ double nSin,nCos;
+ sal_uInt16 nPointCnt=rPoly.GetPointCount();
+ sal_uInt16 i=0;
+ while (i<nPointCnt) {
+ Point* pPnt=&rPoly[i];
+ Point* pC1=nullptr;
+ Point* pC2=nullptr;
+ if (i+1<nPointCnt && rPoly.IsControl(i)) { // control point to the left
+ pC1=pPnt;
+ i++;
+ pPnt=&rPoly[i];
+ }
+ i++;
+ if (i<nPointCnt && rPoly.IsControl(i)) { // control point to the right
+ pC2=&rPoly[i];
+ i++;
+ }
+ CrookSlantXPoint(*pPnt,pC1,pC2,rCenter,rRad,nSin,nCos,bVert);
+ }
+}
+
+void CrookStretchPoly(XPolygon& rPoly, const Point& rCenter, const Point& rRad, bool bVert, const tools::Rectangle& rRefRect)
+{
+ double nSin,nCos;
+ sal_uInt16 nPointCnt=rPoly.GetPointCount();
+ sal_uInt16 i=0;
+ while (i<nPointCnt) {
+ Point* pPnt=&rPoly[i];
+ Point* pC1=nullptr;
+ Point* pC2=nullptr;
+ if (i+1<nPointCnt && rPoly.IsControl(i)) { // control point to the left
+ pC1=pPnt;
+ i++;
+ pPnt=&rPoly[i];
+ }
+ i++;
+ if (i<nPointCnt && rPoly.IsControl(i)) { // control point to the right
+ pC2=&rPoly[i];
+ i++;
+ }
+ CrookStretchXPoint(*pPnt,pC1,pC2,rCenter,rRad,nSin,nCos,bVert,rRefRect);
+ }
+}
+
+
+void CrookRotatePoly(XPolyPolygon& rPoly, const Point& rCenter, const Point& rRad, bool bVert)
+{
+ sal_uInt16 nPolyCount=rPoly.Count();
+ for (sal_uInt16 nPolyNum=0; nPolyNum<nPolyCount; nPolyNum++) {
+ CrookRotatePoly(rPoly[nPolyNum],rCenter,rRad,bVert);
+ }
+}
+
+void CrookSlantPoly(XPolyPolygon& rPoly, const Point& rCenter, const Point& rRad, bool bVert)
+{
+ sal_uInt16 nPolyCount=rPoly.Count();
+ for (sal_uInt16 nPolyNum=0; nPolyNum<nPolyCount; nPolyNum++) {
+ CrookSlantPoly(rPoly[nPolyNum],rCenter,rRad,bVert);
+ }
+}
+
+void CrookStretchPoly(XPolyPolygon& rPoly, const Point& rCenter, const Point& rRad, bool bVert, const tools::Rectangle& rRefRect)
+{
+ sal_uInt16 nPolyCount=rPoly.Count();
+ for (sal_uInt16 nPolyNum=0; nPolyNum<nPolyCount; nPolyNum++) {
+ CrookStretchPoly(rPoly[nPolyNum],rCenter,rRad,bVert,rRefRect);
+ }
+}
+
+
+Degree100 GetAngle(const Point& rPnt)
+{
+ Degree100 a;
+ if (rPnt.Y()==0) {
+ if (rPnt.X()<0) a=-18000_deg100;
+ } else if (rPnt.X()==0) {
+ if (rPnt.Y()>0) a=-9000_deg100;
+ else a=9000_deg100;
+ } else {
+ a = Degree100(FRound(basegfx::rad2deg<100>(atan2(static_cast<double>(-rPnt.Y()), static_cast<double>(rPnt.X())))));
+ }
+ return a;
+}
+
+Degree100 NormAngle18000(Degree100 a)
+{
+ while (a<-18000_deg100) a+=36000_deg100;
+ while (a>=18000_deg100) a-=36000_deg100;
+ return a;
+}
+
+Degree100 NormAngle36000(Degree100 a)
+{
+ a %= 36000_deg100;
+ if (a < 0_deg100)
+ a += 36000_deg100;
+ return a;
+}
+
+sal_uInt16 GetAngleSector(Degree100 nAngle) { return (NormAngle36000(nAngle) / 9000_deg100).get(); }
+
+tools::Long GetLen(const Point& rPnt)
+{
+ tools::Long x=std::abs(rPnt.X());
+ tools::Long y=std::abs(rPnt.Y());
+ if (x+y<0x8000) { // because 7FFF * 7FFF * 2 = 7FFE0002
+ x*=x;
+ y*=y;
+ x+=y;
+ x=FRound(sqrt(static_cast<double>(x)));
+ return x;
+ } else {
+ double nx=x;
+ double ny=y;
+ nx*=nx;
+ ny*=ny;
+ nx+=ny;
+ nx=sqrt(nx);
+ if (nx>0x7FFFFFFF) {
+ return 0x7FFFFFFF; // we can't go any further, for fear of an overrun!
+ } else {
+ return FRound(nx);
+ }
+ }
+}
+
+
+void GeoStat::RecalcSinCos()
+{
+ if (nRotationAngle==0_deg100) {
+ mfSinRotationAngle=0.0;
+ mfCosRotationAngle=1.0;
+ } else {
+ double a = toRadians(nRotationAngle);
+ mfSinRotationAngle=sin(a);
+ mfCosRotationAngle=cos(a);
+ }
+}
+
+void GeoStat::RecalcTan()
+{
+ if (nShearAngle==0_deg100) {
+ mfTanShearAngle=0.0;
+ } else {
+ double a = toRadians(nShearAngle);
+ mfTanShearAngle=tan(a);
+ }
+}
+
+
+tools::Polygon Rect2Poly(const tools::Rectangle& rRect, const GeoStat& rGeo)
+{
+ tools::Polygon aPol(5);
+ aPol[0]=rRect.TopLeft();
+ aPol[1]=rRect.TopRight();
+ aPol[2]=rRect.BottomRight();
+ aPol[3]=rRect.BottomLeft();
+ aPol[4]=rRect.TopLeft();
+ if (rGeo.nShearAngle) ShearPoly(aPol,rRect.TopLeft(),rGeo.mfTanShearAngle);
+ if (rGeo.nRotationAngle) RotatePoly(aPol,rRect.TopLeft(),rGeo.mfSinRotationAngle,rGeo.mfCosRotationAngle);
+ return aPol;
+}
+
+void Poly2Rect(const tools::Polygon& rPol, tools::Rectangle& rRect, GeoStat& rGeo)
+{
+ rGeo.nRotationAngle=GetAngle(rPol[1]-rPol[0]);
+ rGeo.nRotationAngle=NormAngle36000(rGeo.nRotationAngle);
+ // rotation successful
+ rGeo.RecalcSinCos();
+
+ Point aPt1(rPol[1]-rPol[0]);
+ if (rGeo.nRotationAngle) RotatePoint(aPt1,Point(0,0),-rGeo.mfSinRotationAngle,rGeo.mfCosRotationAngle); // -Sin to reverse rotation
+ tools::Long nWdt=aPt1.X();
+
+ Point aPt0(rPol[0]);
+ Point aPt3(rPol[3]-rPol[0]);
+ if (rGeo.nRotationAngle) RotatePoint(aPt3,Point(0,0),-rGeo.mfSinRotationAngle,rGeo.mfCosRotationAngle); // -Sin to reverse rotation
+ tools::Long nHgt=aPt3.Y();
+
+
+ Degree100 nShW=GetAngle(aPt3);
+ nShW-=27000_deg100; // ShearWink is measured against a vertical line
+ nShW=-nShW; // negating, because '+' is shearing clock-wise
+
+ bool bMirr=aPt3.Y()<0;
+ if (bMirr) { // "exchange of points" when mirroring
+ nHgt=-nHgt;
+ nShW+=18000_deg100;
+ aPt0=rPol[3];
+ }
+ nShW=NormAngle18000(nShW);
+ if (nShW<-9000_deg100 || nShW>9000_deg100) {
+ nShW=NormAngle18000(nShW+18000_deg100);
+ }
+ if (nShW<-SDRMAXSHEAR) nShW=-SDRMAXSHEAR; // limit ShearWinkel (shear angle) to +/- 89.00 deg
+ if (nShW>SDRMAXSHEAR) nShW=SDRMAXSHEAR;
+ rGeo.nShearAngle=nShW;
+ rGeo.RecalcTan();
+ Point aRU(aPt0);
+ aRU.AdjustX(nWdt );
+ aRU.AdjustY(nHgt );
+ rRect=tools::Rectangle(aPt0,aRU);
+}
+
+
+void OrthoDistance8(const Point& rPt0, Point& rPt, bool bBigOrtho)
+{
+ tools::Long dx=rPt.X()-rPt0.X();
+ tools::Long dy=rPt.Y()-rPt0.Y();
+ tools::Long dxa=std::abs(dx);
+ tools::Long dya=std::abs(dy);
+ if (dx==0 || dy==0 || dxa==dya) return;
+ if (dxa>=dya*2) { rPt.setY(rPt0.Y() ); return; }
+ if (dya>=dxa*2) { rPt.setX(rPt0.X() ); return; }
+ if ((dxa<dya) != bBigOrtho) {
+ rPt.setY(rPt0.Y()+(dxa* (dy>=0 ? 1 : -1) ) );
+ } else {
+ rPt.setX(rPt0.X()+(dya* (dx>=0 ? 1 : -1) ) );
+ }
+}
+
+void OrthoDistance4(const Point& rPt0, Point& rPt, bool bBigOrtho)
+{
+ tools::Long dx=rPt.X()-rPt0.X();
+ tools::Long dy=rPt.Y()-rPt0.Y();
+ tools::Long dxa=std::abs(dx);
+ tools::Long dya=std::abs(dy);
+ if ((dxa<dya) != bBigOrtho) {
+ rPt.setY(rPt0.Y()+(dxa* (dy>=0 ? 1 : -1) ) );
+ } else {
+ rPt.setX(rPt0.X()+(dya* (dx>=0 ? 1 : -1) ) );
+ }
+}
+
+
+tools::Long BigMulDiv(tools::Long nVal, tools::Long nMul, tools::Long nDiv)
+{
+ if (!nDiv)
+ return 0x7fffffff;
+ return BigInt::Scale(nVal, nMul, nDiv);
+}
+
+static FrPair toPair(o3tl::Length eFrom, o3tl::Length eTo)
+{
+ const auto& [nNum, nDen] = o3tl::getConversionMulDiv(eFrom, eTo);
+ return FrPair(nNum, nDen);
+}
+
+// How many eU units fit into a mm, respectively an inch?
+// Or: How many mm, respectively inches, are there in an eU (and then give me the inverse)
+
+static FrPair GetInchOrMM(MapUnit eU)
+{
+ switch (eU) {
+ case MapUnit::Map1000thInch: return toPair(o3tl::Length::in, o3tl::Length::in1000);
+ case MapUnit::Map100thInch : return toPair(o3tl::Length::in, o3tl::Length::in100);
+ case MapUnit::Map10thInch : return toPair(o3tl::Length::in, o3tl::Length::in10);
+ case MapUnit::MapInch : return toPair(o3tl::Length::in, o3tl::Length::in);
+ case MapUnit::MapPoint : return toPair(o3tl::Length::in, o3tl::Length::pt);
+ case MapUnit::MapTwip : return toPair(o3tl::Length::in, o3tl::Length::twip);
+ case MapUnit::Map100thMM : return toPair(o3tl::Length::mm, o3tl::Length::mm100);
+ case MapUnit::Map10thMM : return toPair(o3tl::Length::mm, o3tl::Length::mm10);
+ case MapUnit::MapMM : return toPair(o3tl::Length::mm, o3tl::Length::mm);
+ case MapUnit::MapCM : return toPair(o3tl::Length::mm, o3tl::Length::cm);
+ case MapUnit::MapPixel : {
+ ScopedVclPtrInstance< VirtualDevice > pVD;
+ pVD->SetMapMode(MapMode(MapUnit::Map100thMM));
+ Point aP(pVD->PixelToLogic(Point(64,64))); // 64 pixels for more accuracy
+ return FrPair(6400,aP.X(),6400,aP.Y());
+ }
+ case MapUnit::MapAppFont: case MapUnit::MapSysFont: {
+ ScopedVclPtrInstance< VirtualDevice > pVD;
+ pVD->SetMapMode(MapMode(eU));
+ Point aP(pVD->LogicToPixel(Point(32,32))); // 32 units for more accuracy
+ pVD->SetMapMode(MapMode(MapUnit::Map100thMM));
+ aP=pVD->PixelToLogic(aP);
+ return FrPair(3200,aP.X(),3200,aP.Y());
+ }
+ default: break;
+ }
+ return Fraction(1,1);
+}
+
+// Calculate the factor that we need to convert units from eS to eD.
+// e. g. GetMapFactor(UNIT_MM,UNIT_100TH_MM) => 100.
+
+FrPair GetMapFactor(MapUnit eS, MapUnit eD)
+{
+ if (eS==eD) return FrPair(1,1,1,1);
+ const auto eFrom = MapToO3tlLength(eS, o3tl::Length::invalid);
+ const auto eTo = MapToO3tlLength(eD, o3tl::Length::invalid);
+ if (eFrom != o3tl::Length::invalid && eTo != o3tl::Length::invalid)
+ return toPair(eFrom, eTo);
+ FrPair aS(GetInchOrMM(eS));
+ FrPair aD(GetInchOrMM(eD));
+ bool bSInch=IsInch(eS);
+ bool bDInch=IsInch(eD);
+ FrPair aRet(aD.X()/aS.X(),aD.Y()/aS.Y());
+ if (bSInch && !bDInch) { aRet.X()*=Fraction(127,5); aRet.Y()*=Fraction(127,5); }
+ if (!bSInch && bDInch) { aRet.X()*=Fraction(5,127); aRet.Y()*=Fraction(5,127); }
+ return aRet;
+};
+
+FrPair GetMapFactor(FieldUnit eS, FieldUnit eD)
+{
+ if (eS==eD) return FrPair(1,1,1,1);
+ auto eFrom = FieldToO3tlLength(eS), eTo = FieldToO3tlLength(eD);
+ if (eFrom == o3tl::Length::invalid)
+ {
+ if (eTo == o3tl::Length::invalid)
+ return FrPair(1,1,1,1);
+ eFrom = IsInch(eD) ? o3tl::Length::in : o3tl::Length::mm;
+ }
+ else if (eTo == o3tl::Length::invalid)
+ eTo = IsInch(eS) ? o3tl::Length::in : o3tl::Length::mm;
+ return toPair(eFrom, eTo);
+};
+
+void SdrFormatter::Undirty()
+{
+ const o3tl::Length eFrom = MapToO3tlLength(eSrcMU, o3tl::Length::invalid);
+ const o3tl::Length eTo = MapToO3tlLength(eDstMU, o3tl::Length::invalid);
+ if (eFrom != o3tl::Length::invalid && eTo != o3tl::Length::invalid)
+ {
+ const auto& [mul, div] = o3tl::getConversionMulDiv(eFrom, eTo);
+ sal_Int64 nMul = mul;
+ sal_Int64 nDiv = div;
+ short nComma = 0;
+
+ // shorten trailing zeros for dividend
+ while (0 == (nMul % 10))
+ {
+ nComma--;
+ nMul /= 10;
+ }
+
+ // shorten trailing zeros for divisor
+ while (0 == (nDiv % 10))
+ {
+ nComma++;
+ nDiv /= 10;
+ }
+ nMul_ = nMul;
+ nDiv_ = nDiv;
+ nComma_ = nComma;
+ }
+ else
+ {
+ nMul_ = nDiv_ = 1;
+ nComma_ = 0;
+ }
+ bDirty=false;
+}
+
+
+OUString SdrFormatter::GetStr(tools::Long nVal) const
+{
+ const OUString aNullCode("0");
+
+ if(!nVal)
+ {
+ return aNullCode;
+ }
+
+ // we may lose some decimal places here, because of MulDiv instead of Real
+ bool bNeg(nVal < 0);
+ SvtSysLocale aSysLoc;
+ const LocaleDataWrapper& rLoc = aSysLoc.GetLocaleData();
+
+ if (bDirty)
+ const_cast<SdrFormatter*>(this)->Undirty();
+
+ sal_Int16 nC(nComma_);
+
+ if(bNeg)
+ nVal = -nVal;
+
+ while(nC <= -3)
+ {
+ nVal *= 1000;
+ nC += 3;
+ }
+
+ while(nC <= -1)
+ {
+ nVal *= 10;
+ nC++;
+ }
+
+ if(nMul_ != nDiv_)
+ nVal = BigMulDiv(nVal, nMul_, nDiv_);
+
+ OUStringBuffer aStr = OUString::number(nVal);
+
+ if(nC > 0 && aStr.getLength() <= nC )
+ {
+ // decimal separator necessary
+ sal_Int32 nCount(nC - aStr.getLength());
+
+ if(nCount >= 0 && LocaleDataWrapper::isNumLeadingZero())
+ nCount++;
+
+ for(sal_Int32 i=0; i<nCount; i++)
+ aStr.insert(0, aNullCode);
+
+ // remove superfluous decimal points
+ sal_Int32 nNumDigits(LocaleDataWrapper::getNumDigits());
+ sal_Int32 nWeg(nC - nNumDigits);
+
+ if(nWeg > 0)
+ {
+ // TODO: we should round here
+ aStr.remove(aStr.getLength() - nWeg, nWeg);
+ nC = nNumDigits;
+ }
+ }
+
+ // remember everything before the decimal separator for later
+ sal_Int32 nForComma(aStr.getLength() - nC);
+
+ if(nC > 0)
+ {
+ // insert comma char (decimal separator)
+ // remove trailing zeros
+ while(nC > 0 && aStr[aStr.getLength() - 1] == aNullCode[0])
+ {
+ aStr.remove(aStr.getLength() - 1, 1);
+ nC--;
+ }
+
+ if(nC > 0)
+ {
+ // do we still have decimal places?
+ sal_Unicode cDec(rLoc.getNumDecimalSep()[0]);
+ aStr.insert(nForComma, cDec);
+ }
+ }
+
+ // add in thousands separator (if necessary)
+ if( nForComma > 3 )
+ {
+ const OUString& aThoSep( rLoc.getNumThousandSep() );
+ if ( aThoSep.getLength() > 0 )
+ {
+ sal_Unicode cTho( aThoSep[0] );
+ sal_Int32 i(nForComma - 3);
+
+ while(i > 0)
+ {
+ aStr.insert(i, cTho);
+ i -= 3;
+ }
+ }
+ }
+
+ if(aStr.isEmpty())
+ aStr.append(aNullCode);
+
+ if(bNeg && (aStr.getLength() > 1 || aStr[0] != aNullCode[0]))
+ {
+ aStr.insert(0, "-");
+ }
+
+ return aStr.makeStringAndClear();
+}
+
+OUString SdrFormatter::GetUnitStr(MapUnit eUnit)
+{
+ switch(eUnit)
+ {
+ // metrically
+ case MapUnit::Map100thMM :
+ return "/100mm";
+ case MapUnit::Map10thMM :
+ return "/10mm";
+ case MapUnit::MapMM :
+ return "mm";
+ case MapUnit::MapCM :
+ return "cm";
+
+ // Inch
+ case MapUnit::Map1000thInch:
+ return "/1000\"";
+ case MapUnit::Map100thInch :
+ return "/100\"";
+ case MapUnit::Map10thInch :
+ return "/10\"";
+ case MapUnit::MapInch :
+ return "\"";
+ case MapUnit::MapPoint :
+ return "pt";
+ case MapUnit::MapTwip :
+ return "twip";
+
+ // others
+ case MapUnit::MapPixel :
+ return "pixel";
+ case MapUnit::MapSysFont :
+ return "sysfont";
+ case MapUnit::MapAppFont :
+ return "appfont";
+ case MapUnit::MapRelative :
+ return "%";
+ default:
+ return OUString();
+ }
+}
+
+OUString SdrFormatter::GetUnitStr(FieldUnit eUnit)
+{
+ switch(eUnit)
+ {
+ default :
+ case FieldUnit::NONE :
+ case FieldUnit::CUSTOM :
+ return OUString();
+
+ // metrically
+ case FieldUnit::MM_100TH:
+ return "/100mm";
+ case FieldUnit::MM :
+ return "mm";
+ case FieldUnit::CM :
+ return "cm";
+ case FieldUnit::M :
+ return "m";
+ case FieldUnit::KM :
+ return "km";
+
+ // Inch
+ case FieldUnit::TWIP :
+ return "twip";
+ case FieldUnit::POINT :
+ return "pt";
+ case FieldUnit::PICA :
+ return "pica";
+ case FieldUnit::INCH :
+ return "\"";
+ case FieldUnit::FOOT :
+ return "ft";
+ case FieldUnit::MILE :
+ return "mile(s)";
+
+ // others
+ case FieldUnit::PERCENT:
+ return "%";
+ }
+}
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/svdraw/svdundo.cxx b/svx/source/svdraw/svdundo.cxx
new file mode 100644
index 000000000..15c690427
--- /dev/null
+++ b/svx/source/svdraw/svdundo.cxx
@@ -0,0 +1,1834 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * 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/FillStyle.hpp>
+
+#include <svx/svdundo.hxx>
+#include <svx/svdotext.hxx>
+#include <svx/svdobj.hxx>
+#include <svx/svdpage.hxx>
+#include <svx/svdlayer.hxx>
+#include <svx/svdmodel.hxx>
+#include <svx/svdview.hxx>
+#include <svx/xbtmpit.hxx>
+#include <svx/xfillit0.hxx>
+#include <svx/strings.hrc>
+#include <svx/dialmgr.hxx>
+#include <svx/scene3d.hxx>
+#include <editeng/outlobj.hxx>
+#include <svx/svdogrp.hxx>
+#include <sdr/properties/itemsettools.hxx>
+#include <svx/sdr/properties/properties.hxx>
+#include <svx/svdocapt.hxx>
+#include <svl/whiter.hxx>
+#include <svx/e3dsceneupdater.hxx>
+#include <svx/svdviter.hxx>
+#include <svx/svdotable.hxx> // #i124389#
+#include <vcl/svapp.hxx>
+#include <sfx2/viewsh.hxx>
+#include <svx/svdoashp.hxx>
+#include <sal/log.hxx>
+#include <osl/diagnose.h>
+#include <svx/diagram/datamodel.hxx>
+#include <svx/diagram/IDiagramHelper.hxx>
+
+
+// iterates over all views and unmarks this SdrObject if it is marked
+static void ImplUnmarkObject( SdrObject* pObj )
+{
+ SdrViewIter aIter( pObj );
+ for ( SdrView* pView = aIter.FirstView(); pView; pView = aIter.NextView() )
+ {
+ pView->MarkObj( pObj, pView->GetSdrPageView(), true );
+ }
+}
+
+SdrUndoAction::SdrUndoAction(SdrModel& rNewMod)
+ : rMod(rNewMod), m_nViewShellId(-1)
+{
+ if (SfxViewShell* pViewShell = SfxViewShell::Current())
+ m_nViewShellId = pViewShell->GetViewShellId();
+}
+
+SdrUndoAction::~SdrUndoAction() {}
+
+bool SdrUndoAction::CanRepeat(SfxRepeatTarget& rView) const
+{
+ SdrView* pV=dynamic_cast<SdrView*>( &rView );
+ if (pV!=nullptr) return CanSdrRepeat(*pV);
+ return false;
+}
+
+void SdrUndoAction::Repeat(SfxRepeatTarget& rView)
+{
+ SdrView* pV=dynamic_cast<SdrView*>( &rView );
+ if (pV!=nullptr) SdrRepeat(*pV);
+ DBG_ASSERT(pV!=nullptr,"Repeat: SfxRepeatTarget that was handed over is not a SdrView");
+}
+
+OUString SdrUndoAction::GetRepeatComment(SfxRepeatTarget& rView) const
+{
+ SdrView* pV=dynamic_cast<SdrView*>( &rView );
+ if (pV!=nullptr) return GetSdrRepeatComment();
+ return OUString();
+}
+
+bool SdrUndoAction::CanSdrRepeat(SdrView& /*rView*/) const
+{
+ return false;
+}
+
+void SdrUndoAction::SdrRepeat(SdrView& /*rView*/)
+{
+}
+
+OUString SdrUndoAction::GetSdrRepeatComment() const
+{
+ return OUString();
+}
+
+ViewShellId SdrUndoAction::GetViewShellId() const
+{
+ return m_nViewShellId;
+}
+
+SdrUndoGroup::SdrUndoGroup(SdrModel& rNewMod)
+: SdrUndoAction(rNewMod),
+ eFunction(SdrRepeatFunc::NONE)
+{}
+
+SdrUndoGroup::~SdrUndoGroup()
+{
+}
+
+void SdrUndoGroup::AddAction(std::unique_ptr<SdrUndoAction> pAct)
+{
+ maActions.push_back(std::move(pAct));
+}
+
+void SdrUndoGroup::Undo()
+{
+ for (auto it = maActions.rbegin(); it != maActions.rend(); ++it)
+ (*it)->Undo();
+}
+
+void SdrUndoGroup::Redo()
+{
+ for (std::unique_ptr<SdrUndoAction> & pAction : maActions)
+ pAction->Redo();
+}
+
+OUString SdrUndoGroup::GetComment() const
+{
+ return aComment.replaceAll("%1", aObjDescription);
+}
+
+bool SdrUndoGroup::CanSdrRepeat(SdrView& rView) const
+{
+ switch (eFunction)
+ {
+ case SdrRepeatFunc::NONE : return false;
+ case SdrRepeatFunc::Delete : return rView.AreObjectsMarked();
+ case SdrRepeatFunc::CombinePolyPoly: return rView.IsCombinePossible();
+ case SdrRepeatFunc::CombineOnePoly : return rView.IsCombinePossible(true);
+ case SdrRepeatFunc::DismantlePolys : return rView.IsDismantlePossible();
+ case SdrRepeatFunc::DismantleLines : return rView.IsDismantlePossible(true);
+ case SdrRepeatFunc::ConvertToPoly : return rView.IsConvertToPolyObjPossible();
+ case SdrRepeatFunc::ConvertToPath : return rView.IsConvertToPathObjPossible();
+ case SdrRepeatFunc::Group : return rView.IsGroupPossible();
+ case SdrRepeatFunc::Ungroup : return rView.IsUnGroupPossible();
+ case SdrRepeatFunc::PutToTop : return rView.IsToTopPossible();
+ case SdrRepeatFunc::PutToBottom : return rView.IsToBtmPossible();
+ case SdrRepeatFunc::MoveToTop : return rView.IsToTopPossible();
+ case SdrRepeatFunc::MoveToBottom : return rView.IsToBtmPossible();
+ case SdrRepeatFunc::ReverseOrder : return rView.IsReverseOrderPossible();
+ case SdrRepeatFunc::ImportMtf : return rView.IsImportMtfPossible();
+ default: break;
+ } // switch
+ return false;
+}
+
+void SdrUndoGroup::SdrRepeat(SdrView& rView)
+{
+ switch (eFunction)
+ {
+ case SdrRepeatFunc::NONE : break;
+ case SdrRepeatFunc::Delete : rView.DeleteMarked(); break;
+ case SdrRepeatFunc::CombinePolyPoly : rView.CombineMarkedObjects(false); break;
+ case SdrRepeatFunc::CombineOnePoly : rView.CombineMarkedObjects(); break;
+ case SdrRepeatFunc::DismantlePolys : rView.DismantleMarkedObjects(); break;
+ case SdrRepeatFunc::DismantleLines : rView.DismantleMarkedObjects(true); break;
+ case SdrRepeatFunc::ConvertToPoly : rView.ConvertMarkedToPolyObj(); break;
+ case SdrRepeatFunc::ConvertToPath : rView.ConvertMarkedToPathObj(false); break;
+ case SdrRepeatFunc::Group : rView.GroupMarked(); break;
+ case SdrRepeatFunc::Ungroup : rView.UnGroupMarked(); break;
+ case SdrRepeatFunc::PutToTop : rView.PutMarkedToTop(); break;
+ case SdrRepeatFunc::PutToBottom : rView.PutMarkedToBtm(); break;
+ case SdrRepeatFunc::MoveToTop : rView.MovMarkedToTop(); break;
+ case SdrRepeatFunc::MoveToBottom : rView.MovMarkedToBtm(); break;
+ case SdrRepeatFunc::ReverseOrder : rView.ReverseOrderOfMarked(); break;
+ case SdrRepeatFunc::ImportMtf : rView.DoImportMarkedMtf(); break;
+ default: break;
+ } // switch
+}
+
+OUString SdrUndoGroup::GetSdrRepeatComment() const
+{
+ return aComment.replaceAll("%1", SvxResId(STR_ObjNameSingulPlural));
+}
+
+SdrUndoObj::SdrUndoObj(SdrObject& rNewObj)
+: SdrUndoAction(rNewObj.getSdrModelFromSdrObject())
+ ,pObj(&rNewObj)
+{
+}
+
+OUString SdrUndoObj::GetDescriptionStringForObject( const SdrObject& _rForObject, TranslateId pStrCacheID, bool bRepeat )
+{
+ const OUString rStr {SvxResId(pStrCacheID)};
+
+ const sal_Int32 nPos = rStr.indexOf("%1");
+ if (nPos < 0)
+ return rStr;
+
+ if (bRepeat)
+ return rStr.replaceAt(nPos, 2, SvxResId(STR_ObjNameSingulPlural));
+
+ return rStr.replaceAt(nPos, 2, _rForObject.TakeObjNameSingul());
+}
+
+OUString SdrUndoObj::ImpGetDescriptionStr(TranslateId pStrCacheID, bool bRepeat) const
+{
+ if ( pObj )
+ return GetDescriptionStringForObject( *pObj, pStrCacheID, bRepeat );
+ return OUString();
+}
+
+// common call method for possible change of the page when UNDO/REDO is triggered
+void SdrUndoObj::ImpShowPageOfThisObject()
+{
+ if(pObj && pObj->IsInserted() && pObj->getSdrPageFromSdrObject())
+ {
+ SdrHint aHint(SdrHintKind::SwitchToPage, *pObj, pObj->getSdrPageFromSdrObject());
+ pObj->getSdrModelFromSdrObject().Broadcast(aHint);
+ }
+}
+
+void SdrUndoAttrObj::ensureStyleSheetInStyleSheetPool(SfxStyleSheetBasePool& rStyleSheetPool, SfxStyleSheet& rSheet)
+{
+ SfxStyleSheetBase* pThere = rStyleSheetPool.Find(rSheet.GetName(), rSheet.GetFamily());
+
+ if(!pThere)
+ {
+ // re-insert remembered style which was removed in the meantime. To do this
+ // without assertion, do it without parent and set parent after insertion
+ const OUString aParent(rSheet.GetParent());
+
+ rSheet.SetParent(OUString());
+ rStyleSheetPool.Insert(&rSheet);
+ rSheet.SetParent(aParent);
+ }
+}
+
+SdrUndoAttrObj::SdrUndoAttrObj(SdrObject& rNewObj, bool bStyleSheet1, bool bSaveText)
+ : SdrUndoObj(rNewObj)
+ , bHaveToTakeRedoSet(true)
+{
+ bStyleSheet = bStyleSheet1;
+
+ SdrObjList* pOL = rNewObj.GetSubList();
+ bool bIsGroup(pOL!=nullptr && pOL->GetObjCount());
+ bool bIs3DScene(bIsGroup && dynamic_cast< E3dScene* >(pObj) != nullptr);
+
+ if(bIsGroup)
+ {
+ // it's a group object!
+ pUndoGroup.reset(new SdrUndoGroup(pObj->getSdrModelFromSdrObject()));
+ const size_t nObjCount(pOL->GetObjCount());
+
+ for(size_t nObjNum = 0; nObjNum < nObjCount; ++nObjNum)
+ {
+ pUndoGroup->AddAction(
+ std::make_unique<SdrUndoAttrObj>(*pOL->GetObj(nObjNum), bStyleSheet1));
+ }
+ }
+
+ if(bIsGroup && !bIs3DScene)
+ return;
+
+ moUndoSet.emplace( pObj->GetMergedItemSet() );
+
+ if(bStyleSheet)
+ mxUndoStyleSheet = pObj->GetStyleSheet();
+
+ if(bSaveText)
+ {
+ auto p = pObj->GetOutlinerParaObject();
+ if(p)
+ pTextUndo = *p;
+ }
+}
+
+SdrUndoAttrObj::~SdrUndoAttrObj()
+{
+ moUndoSet.reset();
+ moRedoSet.reset();
+ pUndoGroup.reset();
+ pTextUndo.reset();
+ pTextRedo.reset();
+}
+
+void SdrUndoAttrObj::Undo()
+{
+ E3DModifySceneSnapRectUpdater aUpdater(pObj);
+ bool bIs3DScene(dynamic_cast< E3dScene* >(pObj) != nullptr);
+
+ // Trigger PageChangeCall
+ ImpShowPageOfThisObject();
+
+ if(!pUndoGroup || bIs3DScene)
+ {
+ if(bHaveToTakeRedoSet)
+ {
+ bHaveToTakeRedoSet = false;
+
+ moRedoSet.emplace( pObj->GetMergedItemSet() );
+
+ if(bStyleSheet)
+ mxRedoStyleSheet = pObj->GetStyleSheet();
+
+ if(pTextUndo)
+ {
+ // #i8508#
+ auto p = pObj->GetOutlinerParaObject();
+ if(p)
+ pTextRedo = *p;
+ }
+ }
+
+ if(bStyleSheet)
+ {
+ mxRedoStyleSheet = pObj->GetStyleSheet();
+ SfxStyleSheet* pSheet = dynamic_cast< SfxStyleSheet* >(mxUndoStyleSheet.get());
+
+ if(pSheet && pObj->getSdrModelFromSdrObject().GetStyleSheetPool())
+ {
+ ensureStyleSheetInStyleSheetPool(*pObj->getSdrModelFromSdrObject().GetStyleSheetPool(), *pSheet);
+ pObj->SetStyleSheet(pSheet, true);
+ }
+ else
+ {
+ OSL_ENSURE(false, "OOps, something went wrong in SdrUndoAttrObj (!)");
+ }
+ }
+
+ sdr::properties::ItemChangeBroadcaster aItemChange(*pObj);
+
+ // Since ClearItem sets back everything to normal
+ // it also sets fit-to-size text to non-fit-to-size text and
+ // switches on autogrowheight (the default). That may lead to
+ // losing the geometry size info for the object when it is
+ // laid out again from AdjustTextFrameWidthAndHeight(). This makes
+ // rescuing the size of the object necessary.
+ const tools::Rectangle aSnapRect = pObj->GetSnapRect();
+ // SdrObjCustomShape::NbcSetSnapRect needs logic instead of snap rect
+ const tools::Rectangle aLogicRect = pObj->GetLogicRect();
+
+ if(moUndoSet)
+ {
+ if(dynamic_cast<const SdrCaptionObj*>( pObj) != nullptr)
+ {
+ // do a more smooth item deletion here, else the text
+ // rect will be reformatted, especially when information regarding
+ // vertical text is changed. When clearing only set items it's
+ // slower, but safer regarding such information (it's not changed
+ // usually)
+ SfxWhichIter aIter(*moUndoSet);
+ sal_uInt16 nWhich(aIter.FirstWhich());
+
+ while(nWhich)
+ {
+ if(SfxItemState::SET != aIter.GetItemState(false))
+ {
+ pObj->ClearMergedItem(nWhich);
+ }
+
+ nWhich = aIter.NextWhich();
+ }
+ }
+ else
+ {
+ pObj->ClearMergedItem();
+ }
+
+ pObj->SetMergedItemSet(*moUndoSet);
+ }
+
+ // Restore previous size here when it was changed.
+ if(aSnapRect != pObj->GetSnapRect())
+ {
+ if(dynamic_cast<const SdrObjCustomShape*>(pObj))
+ pObj->NbcSetSnapRect(aLogicRect);
+ else
+ pObj->NbcSetSnapRect(aSnapRect);
+ }
+
+ pObj->GetProperties().BroadcastItemChange(aItemChange);
+
+ if(pTextUndo)
+ {
+ pObj->SetOutlinerParaObject(*pTextUndo);
+ }
+ }
+
+ if(pUndoGroup)
+ {
+ pUndoGroup->Undo();
+ }
+}
+
+void SdrUndoAttrObj::Redo()
+{
+ E3DModifySceneSnapRectUpdater aUpdater(pObj);
+ bool bIs3DScene(dynamic_cast< E3dScene* >(pObj) != nullptr);
+
+ if(!pUndoGroup || bIs3DScene)
+ {
+ if(bStyleSheet)
+ {
+ mxUndoStyleSheet = pObj->GetStyleSheet();
+ SfxStyleSheet* pSheet = dynamic_cast< SfxStyleSheet* >(mxRedoStyleSheet.get());
+
+ if(pSheet && pObj->getSdrModelFromSdrObject().GetStyleSheetPool())
+ {
+ ensureStyleSheetInStyleSheetPool(*pObj->getSdrModelFromSdrObject().GetStyleSheetPool(), *pSheet);
+ pObj->SetStyleSheet(pSheet, true);
+ }
+ else
+ {
+ OSL_ENSURE(false, "OOps, something went wrong in SdrUndoAttrObj (!)");
+ }
+ }
+
+ sdr::properties::ItemChangeBroadcaster aItemChange(*pObj);
+
+ const tools::Rectangle aSnapRect = pObj->GetSnapRect();
+ const tools::Rectangle aLogicRect = pObj->GetLogicRect();
+
+ if(moRedoSet)
+ {
+ if(dynamic_cast<const SdrCaptionObj*>( pObj) != nullptr)
+ {
+ // do a more smooth item deletion here, else the text
+ // rect will be reformatted, especially when information regarding
+ // vertical text is changed. When clearing only set items it's
+ // slower, but safer regarding such information (it's not changed
+ // usually)
+ SfxWhichIter aIter(*moRedoSet);
+ sal_uInt16 nWhich(aIter.FirstWhich());
+
+ while(nWhich)
+ {
+ if(SfxItemState::SET != aIter.GetItemState(false))
+ {
+ pObj->ClearMergedItem(nWhich);
+ }
+
+ nWhich = aIter.NextWhich();
+ }
+ }
+ else
+ {
+ pObj->ClearMergedItem();
+ }
+
+ pObj->SetMergedItemSet(*moRedoSet);
+ }
+
+ // Restore previous size here when it was changed.
+ if(aSnapRect != pObj->GetSnapRect())
+ {
+ if(dynamic_cast<const SdrObjCustomShape*>(pObj))
+ pObj->NbcSetSnapRect(aLogicRect);
+ else
+ pObj->NbcSetSnapRect(aSnapRect);
+ }
+
+ pObj->GetProperties().BroadcastItemChange(aItemChange);
+
+ // #i8508#
+ if(pTextRedo)
+ {
+ pObj->SetOutlinerParaObject(*pTextRedo);
+ }
+ }
+
+ if(pUndoGroup)
+ {
+ pUndoGroup->Redo();
+ }
+
+ // Trigger PageChangeCall
+ ImpShowPageOfThisObject();
+}
+
+OUString SdrUndoAttrObj::GetComment() const
+{
+ if(bStyleSheet)
+ {
+ return ImpGetDescriptionStr(STR_EditSetStylesheet);
+ }
+ else
+ {
+ return ImpGetDescriptionStr(STR_EditSetAttributes);
+ }
+}
+
+OUString SdrUndoAttrObj::GetSdrRepeatComment() const
+{
+ if(bStyleSheet)
+ {
+ return ImpGetDescriptionStr(STR_EditSetStylesheet, true);
+ }
+ else
+ {
+ return ImpGetDescriptionStr(STR_EditSetAttributes, true);
+ }
+}
+
+
+SdrUndoMoveObj::~SdrUndoMoveObj() {}
+
+void SdrUndoMoveObj::Undo()
+{
+ // Trigger PageChangeCall
+ ImpShowPageOfThisObject();
+
+ pObj->Move(Size(-aDistance.Width(),-aDistance.Height()));
+}
+
+void SdrUndoMoveObj::Redo()
+{
+ pObj->Move(Size(aDistance.Width(),aDistance.Height()));
+
+ // Trigger PageChangeCall
+ ImpShowPageOfThisObject();
+}
+
+OUString SdrUndoMoveObj::GetComment() const
+{
+ return ImpGetDescriptionStr(STR_EditMove);
+}
+
+void SdrUndoMoveObj::SdrRepeat(SdrView& rView)
+{
+ rView.MoveMarkedObj(aDistance);
+}
+
+bool SdrUndoMoveObj::CanSdrRepeat(SdrView& rView) const
+{
+ return rView.AreObjectsMarked();
+}
+
+OUString SdrUndoMoveObj::GetSdrRepeatComment() const
+{
+ return ImpGetDescriptionStr(STR_EditMove,true);
+}
+
+
+SdrUndoGeoObj::SdrUndoGeoObj(SdrObject& rNewObj)
+ : SdrUndoObj(rNewObj)
+ , mbSkipChangeLayout(false)
+{
+ SdrObjList* pOL=rNewObj.GetSubList();
+ if (pOL!=nullptr && pOL->GetObjCount() && dynamic_cast<const E3dScene* >( &rNewObj) == nullptr)
+ {
+ // this is a group object!
+ // If this were 3D scene, we'd only add an Undo for the scene itself
+ // (which we do elsewhere).
+ pUndoGroup.reset(new SdrUndoGroup(pObj->getSdrModelFromSdrObject()));
+ const size_t nObjCount = pOL->GetObjCount();
+ for (size_t nObjNum = 0; nObjNum<nObjCount; ++nObjNum) {
+ pUndoGroup->AddAction(std::make_unique<SdrUndoGeoObj>(*pOL->GetObj(nObjNum)));
+ }
+ }
+ else
+ {
+ pUndoGeo = pObj->GetGeoData();
+ }
+}
+
+SdrUndoGeoObj::~SdrUndoGeoObj()
+{
+ pUndoGeo.reset();
+ pRedoGeo.reset();
+ pUndoGroup.reset();
+}
+
+void SdrUndoGeoObj::Undo()
+{
+ // Trigger PageChangeCall
+ ImpShowPageOfThisObject();
+
+ if(pUndoGroup)
+ {
+ pUndoGroup->Undo();
+
+ // only repaint, no objectchange
+ pObj->ActionChanged();
+ }
+ else
+ {
+ pRedoGeo = pObj->GetGeoData();
+
+ auto pTableObj = dynamic_cast<sdr::table::SdrTableObj*>(pObj);
+ if (pTableObj && mbSkipChangeLayout)
+ pTableObj->SetSkipChangeLayout(true);
+ pObj->SetGeoData(*pUndoGeo);
+ if (pTableObj && mbSkipChangeLayout)
+ pTableObj->SetSkipChangeLayout(false);
+ }
+}
+
+void SdrUndoGeoObj::Redo()
+{
+ if(pUndoGroup)
+ {
+ pUndoGroup->Redo();
+
+ // only repaint, no objectchange
+ pObj->ActionChanged();
+ }
+ else
+ {
+ pUndoGeo = pObj->GetGeoData();
+ pObj->SetGeoData(*pRedoGeo);
+ }
+
+ // Trigger PageChangeCall
+ ImpShowPageOfThisObject();
+}
+
+OUString SdrUndoGeoObj::GetComment() const
+{
+ return ImpGetDescriptionStr(STR_DragMethObjOwn);
+}
+
+SdrUndoDiagramModelData::SdrUndoDiagramModelData(SdrObject& rNewObj, svx::diagram::DiagramDataStatePtr& rStartState)
+: SdrUndoObj(rNewObj)
+, m_aStartState(rStartState)
+, m_aEndState()
+{
+ if(rNewObj.isDiagram())
+ m_aEndState = rNewObj.getDiagramHelper()->extractDiagramDataState();
+}
+
+SdrUndoDiagramModelData::~SdrUndoDiagramModelData()
+{
+}
+
+void SdrUndoDiagramModelData::implUndoRedo(bool bUndo)
+{
+ if(nullptr == pObj)
+ return;
+
+ if(!pObj->isDiagram())
+ return;
+
+ pObj->getDiagramHelper()->applyDiagramDataState(
+ bUndo ? m_aStartState : m_aEndState);
+ pObj->getDiagramHelper()->reLayout(*static_cast<SdrObjGroup*>(pObj));
+}
+
+void SdrUndoDiagramModelData::Undo()
+{
+ implUndoRedo(true);
+}
+
+void SdrUndoDiagramModelData::Redo()
+{
+ implUndoRedo(false);
+}
+
+OUString SdrUndoDiagramModelData::GetComment() const
+{
+ return ImpGetDescriptionStr(STR_DiagramModelDataChange);
+}
+
+SdrUndoObjList::SdrUndoObjList(SdrObject& rNewObj, bool bOrdNumDirect)
+ : SdrUndoObj(rNewObj)
+ , bOwner(false)
+{
+ pObjList=pObj->getParentSdrObjListFromSdrObject();
+ if (bOrdNumDirect)
+ {
+ nOrdNum=pObj->GetOrdNumDirect();
+ }
+ else
+ {
+ nOrdNum=pObj->GetOrdNum();
+ }
+}
+
+SdrUndoObjList::~SdrUndoObjList()
+{
+ SolarMutexGuard aGuard;
+
+ if (pObj!=nullptr && IsOwner())
+ {
+ // Attribute have to go back to the regular Pool
+ SetOwner(false);
+
+ // now delete
+ SdrObject::Free( pObj );
+ }
+}
+
+void SdrUndoObjList::SetOwner(bool bNew)
+{
+ bOwner = bNew;
+}
+
+
+void SdrUndoRemoveObj::Undo()
+{
+ // Trigger PageChangeCall
+ ImpShowPageOfThisObject();
+
+ DBG_ASSERT(!pObj->IsInserted(),"UndoRemoveObj: pObj has already been inserted.");
+ if (pObj->IsInserted())
+ return;
+
+ // #i11426#
+ // For UNDOs in Calc/Writer it is necessary to adapt the anchor
+ // position of the target object.
+ Point aOwnerAnchorPos(0, 0);
+
+ if (dynamic_cast< const SdrObjGroup* >(pObjList->getSdrObjectFromSdrObjList()) != nullptr)
+ {
+ aOwnerAnchorPos = pObjList->getSdrObjectFromSdrObjList()->GetAnchorPos();
+ }
+
+ E3DModifySceneSnapRectUpdater aUpdater(pObjList->getSdrObjectFromSdrObjList());
+ pObjList->InsertObject(pObj,nOrdNum);
+
+ // #i11426#
+ if(aOwnerAnchorPos.X() || aOwnerAnchorPos.Y())
+ {
+ pObj->NbcSetAnchorPos(aOwnerAnchorPos);
+ }
+}
+
+void SdrUndoRemoveObj::Redo()
+{
+ DBG_ASSERT(pObj->IsInserted(),"RedoRemoveObj: pObj is not inserted.");
+ if (pObj->IsInserted())
+ {
+ ImplUnmarkObject( pObj );
+ E3DModifySceneSnapRectUpdater aUpdater(pObj);
+ pObjList->RemoveObject(pObj->GetOrdNum());
+ }
+
+ // Trigger PageChangeCall
+ ImpShowPageOfThisObject();
+}
+
+SdrUndoRemoveObj::~SdrUndoRemoveObj()
+{
+}
+
+
+void SdrUndoInsertObj::Undo()
+{
+ // Trigger PageChangeCall
+ ImpShowPageOfThisObject();
+
+ DBG_ASSERT(pObj->IsInserted(),"UndoInsertObj: pObj is not inserted.");
+ if (pObj->IsInserted())
+ {
+ ImplUnmarkObject( pObj );
+
+ SdrObject* pChkObj= pObjList->RemoveObject(pObj->GetOrdNum());
+ DBG_ASSERT(pChkObj==pObj,"UndoInsertObj: RemoveObjNum!=pObj");
+ }
+}
+
+void SdrUndoInsertObj::Redo()
+{
+ DBG_ASSERT(!pObj->IsInserted(),"RedoInsertObj: pObj is already inserted");
+ if (!pObj->IsInserted())
+ {
+ // Restore anchor position of an object,
+ // which becomes a member of a group, because its cleared in method
+ // <InsertObject(..)>. Needed for correct Redo in Writer. (#i45952#)
+ Point aAnchorPos( 0, 0 );
+
+ if (dynamic_cast<const SdrObjGroup*>(pObjList->getSdrObjectFromSdrObjList()) != nullptr)
+ {
+ aAnchorPos = pObj->GetAnchorPos();
+ }
+
+ pObjList->InsertObject(pObj,nOrdNum);
+
+ // Arcs lose position when grouped (#i45952#)
+ if ( aAnchorPos.X() || aAnchorPos.Y() )
+ {
+ pObj->NbcSetAnchorPos( aAnchorPos );
+ }
+ }
+
+ // Trigger PageChangeCall
+ ImpShowPageOfThisObject();
+}
+
+SdrUndoDelObj::SdrUndoDelObj(SdrObject& rNewObj, bool bOrdNumDirect)
+: SdrUndoRemoveObj(rNewObj,bOrdNumDirect)
+{
+ SetOwner(true);
+}
+
+void SdrUndoDelObj::Undo()
+{
+ SdrUndoRemoveObj::Undo();
+ DBG_ASSERT(IsOwner(),"UndoDeleteObj: pObj does not belong to UndoAction");
+ SetOwner(false);
+}
+
+void SdrUndoDelObj::Redo()
+{
+ SdrUndoRemoveObj::Redo();
+ DBG_ASSERT(!IsOwner(),"RedoDeleteObj: pObj already belongs to UndoAction");
+ SetOwner(true);
+}
+
+OUString SdrUndoDelObj::GetComment() const
+{
+ return ImpGetDescriptionStr(STR_EditDelete);
+}
+
+void SdrUndoDelObj::SdrRepeat(SdrView& rView)
+{
+ rView.DeleteMarked();
+}
+
+bool SdrUndoDelObj::CanSdrRepeat(SdrView& rView) const
+{
+ return rView.AreObjectsMarked();
+}
+
+OUString SdrUndoDelObj::GetSdrRepeatComment() const
+{
+ return ImpGetDescriptionStr(STR_EditDelete,true);
+}
+
+
+void SdrUndoNewObj::Undo()
+{
+ SdrUndoInsertObj::Undo();
+ DBG_ASSERT(!IsOwner(),"RedoNewObj: pObj already belongs to UndoAction");
+ SetOwner(true);
+}
+
+void SdrUndoNewObj::Redo()
+{
+ SdrUndoInsertObj::Redo();
+ DBG_ASSERT(IsOwner(),"RedoNewObj: pObj does not belong to UndoAction");
+ SetOwner(false);
+}
+
+OUString SdrUndoNewObj::GetComment( const SdrObject& _rForObject )
+{
+ return GetDescriptionStringForObject( _rForObject, STR_UndoInsertObj );
+}
+
+OUString SdrUndoNewObj::GetComment() const
+{
+ return ImpGetDescriptionStr(STR_UndoInsertObj);
+}
+
+SdrUndoReplaceObj::SdrUndoReplaceObj(SdrObject& rOldObj1, SdrObject& rNewObj1)
+ : SdrUndoObj(rOldObj1)
+ , bOldOwner(false)
+ , bNewOwner(false)
+ , pNewObj(&rNewObj1)
+{
+ SetOldOwner(true);
+ pObjList=pObj->getParentSdrObjListFromSdrObject();
+}
+
+SdrUndoReplaceObj::~SdrUndoReplaceObj()
+{
+ if (pObj!=nullptr && IsOldOwner())
+ {
+ // Attribute have to go back into the Pool
+ SetOldOwner(false);
+
+ // now delete
+ SdrObject::Free( pObj );
+ }
+ if (pNewObj!=nullptr && IsNewOwner())
+ {
+ // Attribute have to go back into the Pool
+ SetNewOwner(false);
+
+ // now delete
+ SdrObject::Free( pNewObj );
+ }
+}
+
+void SdrUndoReplaceObj::Undo()
+{
+ // Trigger PageChangeCall
+ ImpShowPageOfThisObject();
+
+ if (IsOldOwner() && !IsNewOwner())
+ {
+ DBG_ASSERT(!pObj->IsInserted(),"SdrUndoReplaceObj::Undo(): Old object is already inserted!");
+ DBG_ASSERT(pNewObj->IsInserted(),"SdrUndoReplaceObj::Undo(): New object is not inserted!");
+ SetOldOwner(false);
+ SetNewOwner(true);
+
+ ImplUnmarkObject( pNewObj );
+ pObjList->ReplaceObject(pObj,pNewObj->GetOrdNum());
+ }
+ else
+ {
+ OSL_FAIL("SdrUndoReplaceObj::Undo(): Wrong IsMine flags. Did you call Undo twice?");
+ }
+}
+
+void SdrUndoReplaceObj::Redo()
+{
+ if (!IsOldOwner() && IsNewOwner())
+ {
+ DBG_ASSERT(!pNewObj->IsInserted(),"SdrUndoReplaceObj::Redo(): New object is already inserted!!");
+ DBG_ASSERT(pObj->IsInserted(),"SdrUndoReplaceObj::Redo(): Old object is not inserted!!");
+ SetOldOwner(true);
+ SetNewOwner(false);
+
+ ImplUnmarkObject( pObj );
+ pObjList->ReplaceObject(pNewObj,pObj->GetOrdNum());
+
+ }
+ else
+ {
+ OSL_FAIL("SdrUndoReplaceObj::Redo(): Wrong IsMine flags. Did you call Redo twice?");
+ }
+
+ // Trigger PageChangeCall
+ ImpShowPageOfThisObject();
+}
+
+void SdrUndoReplaceObj::SetNewOwner(bool bNew)
+{
+ bNewOwner = bNew;
+}
+
+void SdrUndoReplaceObj::SetOldOwner(bool bNew)
+{
+ bOldOwner = bNew;
+}
+
+
+OUString SdrUndoCopyObj::GetComment() const
+{
+ return ImpGetDescriptionStr(STR_UndoCopyObj);
+}
+
+
+// #i11702#
+
+SdrUndoObjectLayerChange::SdrUndoObjectLayerChange(SdrObject& rObj, SdrLayerID aOldLayer, SdrLayerID aNewLayer)
+ : SdrUndoObj(rObj)
+ , maOldLayer(aOldLayer)
+ , maNewLayer(aNewLayer)
+{
+}
+
+void SdrUndoObjectLayerChange::Undo()
+{
+ ImpShowPageOfThisObject();
+ pObj->SetLayer(maOldLayer);
+}
+
+void SdrUndoObjectLayerChange::Redo()
+{
+ pObj->SetLayer(maNewLayer);
+ ImpShowPageOfThisObject();
+}
+
+
+SdrUndoObjOrdNum::SdrUndoObjOrdNum(SdrObject& rNewObj, sal_uInt32 nOldOrdNum1, sal_uInt32 nNewOrdNum1)
+ : SdrUndoObj(rNewObj)
+ , nOldOrdNum(nOldOrdNum1)
+ , nNewOrdNum(nNewOrdNum1)
+{
+}
+
+void SdrUndoObjOrdNum::Undo()
+{
+ // Trigger PageChangeCall
+ ImpShowPageOfThisObject();
+
+ SdrObjList* pOL=pObj->getParentSdrObjListFromSdrObject();
+ if (pOL==nullptr)
+ {
+ OSL_FAIL("UndoObjOrdNum: pObj does not have an ObjList.");
+ return;
+ }
+ pOL->SetObjectOrdNum(nNewOrdNum,nOldOrdNum);
+}
+
+void SdrUndoObjOrdNum::Redo()
+{
+ SdrObjList* pOL=pObj->getParentSdrObjListFromSdrObject();
+ if (pOL==nullptr)
+ {
+ OSL_FAIL("RedoObjOrdNum: pObj does not have an ObjList.");
+ return;
+ }
+ pOL->SetObjectOrdNum(nOldOrdNum,nNewOrdNum);
+
+ // Trigger PageChangeCall
+ ImpShowPageOfThisObject();
+}
+
+OUString SdrUndoObjOrdNum::GetComment() const
+{
+ return ImpGetDescriptionStr(STR_UndoObjOrdNum);
+}
+
+SdrUndoSort::SdrUndoSort(const SdrPage & rPage,
+ ::std::vector<sal_Int32> const& rSortOrder)
+ : SdrUndoAction(rPage.getSdrModelFromSdrPage())
+ , m_OldSortOrder(rSortOrder.size())
+ , m_NewSortOrder(rSortOrder)
+ , m_nPage(rPage.GetPageNum())
+{
+ // invert order
+ for (size_t i = 0; i < rSortOrder.size(); ++i)
+ {
+ m_OldSortOrder[rSortOrder[i]] = i;
+ }
+}
+
+void SdrUndoSort::Do(::std::vector<sal_Int32> & rSortOrder)
+{
+ SdrPage & rPage(*rMod.GetPage(m_nPage));
+ if (rPage.GetObjCount() != rSortOrder.size())
+ {
+ // can probably happen with sw's cursed SdrVirtObj mess - no good solution for that
+ SAL_WARN("svx", "SdrUndoSort size mismatch");
+ return;
+ }
+
+ // hopefully this can't throw
+ rPage.sort(rSortOrder);
+}
+
+void SdrUndoSort::Undo()
+{
+ Do(m_OldSortOrder);
+}
+
+void SdrUndoSort::Redo()
+{
+ Do(m_NewSortOrder);
+}
+
+OUString SdrUndoSort::GetComment() const
+{
+ return SvxResId(STR_SortShapes);
+}
+
+SdrUndoObjSetText::SdrUndoObjSetText(SdrObject& rNewObj, sal_Int32 nText)
+ : SdrUndoObj(rNewObj)
+ , bNewTextAvailable(false)
+ , bEmptyPresObj(false)
+ , mnText(nText)
+{
+ SdrText* pText = static_cast< SdrTextObj*>( &rNewObj )->getText(mnText);
+ if( pText && pText->GetOutlinerParaObject() )
+ pOldText = *pText->GetOutlinerParaObject();
+
+ bEmptyPresObj = rNewObj.IsEmptyPresObj();
+}
+
+SdrUndoObjSetText::~SdrUndoObjSetText()
+{
+ pOldText.reset();
+ pNewText.reset();
+}
+
+bool SdrUndoObjSetText::IsDifferent() const
+{
+ if (!pOldText || !pNewText)
+ return pOldText || pNewText;
+ return *pOldText != *pNewText;
+}
+
+void SdrUndoObjSetText::AfterSetText()
+{
+ if (!bNewTextAvailable)
+ {
+ SdrText* pText = static_cast< SdrTextObj*>( pObj )->getText(mnText);
+ if( pText && pText->GetOutlinerParaObject() )
+ pNewText = *pText->GetOutlinerParaObject();
+ bNewTextAvailable=true;
+ }
+}
+
+void SdrUndoObjSetText::Undo()
+{
+ // only works with SdrTextObj
+ SdrTextObj* pTarget = dynamic_cast< SdrTextObj* >(pObj);
+
+ if(!pTarget)
+ {
+ OSL_ENSURE(false, "SdrUndoObjSetText::Undo with SdrObject not based on SdrTextObj (!)");
+ return;
+ }
+
+ // Trigger PageChangeCall
+ ImpShowPageOfThisObject();
+
+ // save old text for Redo
+ if(!bNewTextAvailable)
+ {
+ AfterSetText();
+ }
+
+ SdrText* pText = pTarget->getText(mnText);
+ if (pText)
+ {
+ // copy text for Undo, because the original now belongs to SetOutlinerParaObject()
+ pTarget->NbcSetOutlinerParaObjectForText(pOldText, pText);
+ }
+
+ pTarget->SetEmptyPresObj(bEmptyPresObj);
+ pTarget->ActionChanged();
+
+ // #i124389# if it's a table, also need to relayout TextFrame
+ if(dynamic_cast< sdr::table::SdrTableObj* >(pTarget) != nullptr)
+ {
+ pTarget->NbcAdjustTextFrameWidthAndHeight();
+ }
+
+ // #i122410# SetOutlinerParaObject at SdrText does not trigger a
+ // BroadcastObjectChange, but it is needed to make evtl. SlideSorters
+ // update their preview.
+ pTarget->BroadcastObjectChange();
+}
+
+void SdrUndoObjSetText::Redo()
+{
+ // only works with SdrTextObj
+ SdrTextObj* pTarget = dynamic_cast< SdrTextObj* >(pObj);
+
+ if(!pTarget)
+ {
+ OSL_ENSURE(false, "SdrUndoObjSetText::Redo with SdrObject not based on SdrTextObj (!)");
+ return;
+ }
+
+ SdrText* pText = pTarget->getText(mnText);
+ if (pText)
+ {
+ // copy text for Undo, because the original now belongs to SetOutlinerParaObject()
+ pTarget->NbcSetOutlinerParaObjectForText( pNewText, pText );
+ }
+
+ pTarget->ActionChanged();
+
+ // #i124389# if it's a table, also need to relayout TextFrame
+ if(dynamic_cast< sdr::table::SdrTableObj* >(pTarget) != nullptr)
+ {
+ pTarget->NbcAdjustTextFrameWidthAndHeight();
+ }
+
+ // #i122410# NbcSetOutlinerParaObjectForText at SdrTextObj does not trigger a
+ // BroadcastObjectChange, but it is needed to make evtl. SlideSorters
+ // update their preview.
+ pTarget->BroadcastObjectChange();
+
+ // Trigger PageChangeCall
+ ImpShowPageOfThisObject();
+}
+
+OUString SdrUndoObjSetText::GetComment() const
+{
+ return ImpGetDescriptionStr(STR_UndoObjSetText);
+}
+
+OUString SdrUndoObjSetText::GetSdrRepeatComment() const
+{
+ return ImpGetDescriptionStr(STR_UndoObjSetText);
+}
+
+void SdrUndoObjSetText::SdrRepeat(SdrView& rView)
+{
+ if (!(bNewTextAvailable && rView.AreObjectsMarked()))
+ return;
+
+ const SdrMarkList& rML=rView.GetMarkedObjectList();
+
+ const bool bUndo = rView.IsUndoEnabled();
+ if( bUndo )
+ {
+ OUString aStr = ImpGetDescriptionStr(STR_UndoObjSetText);
+ rView.BegUndo(aStr);
+ }
+
+ const size_t nCount=rML.GetMarkCount();
+ for (size_t nm=0; nm<nCount; ++nm)
+ {
+ SdrObject* pObj2=rML.GetMark(nm)->GetMarkedSdrObj();
+ SdrTextObj* pTextObj=dynamic_cast<SdrTextObj*>( pObj2 );
+ if (pTextObj!=nullptr)
+ {
+ if( bUndo )
+ rView.AddUndo(std::make_unique<SdrUndoObjSetText>(*pTextObj,0));
+
+ pTextObj->SetOutlinerParaObject(pNewText);
+ }
+ }
+
+ if( bUndo )
+ rView.EndUndo();
+}
+
+bool SdrUndoObjSetText::CanSdrRepeat(SdrView& rView) const
+{
+ bool bOk = false;
+ if (bNewTextAvailable && rView.AreObjectsMarked()) {
+ bOk=true;
+ }
+ return bOk;
+}
+
+// Undo/Redo for setting object's name (#i73249#)
+SdrUndoObjStrAttr::SdrUndoObjStrAttr( SdrObject& rNewObj,
+ const ObjStrAttrType eObjStrAttr,
+ const OUString& sOldStr,
+ const OUString& sNewStr)
+ : SdrUndoObj( rNewObj )
+ , meObjStrAttr( eObjStrAttr )
+ , msOldStr( sOldStr )
+ , msNewStr( sNewStr )
+{
+}
+
+void SdrUndoObjStrAttr::Undo()
+{
+ ImpShowPageOfThisObject();
+
+ switch ( meObjStrAttr )
+ {
+ case ObjStrAttrType::Name:
+ pObj->SetName( msOldStr );
+ break;
+ case ObjStrAttrType::Title:
+ pObj->SetTitle( msOldStr );
+ break;
+ case ObjStrAttrType::Description:
+ pObj->SetDescription( msOldStr );
+ break;
+ }
+}
+
+void SdrUndoObjStrAttr::Redo()
+{
+ switch ( meObjStrAttr )
+ {
+ case ObjStrAttrType::Name:
+ pObj->SetName( msNewStr );
+ break;
+ case ObjStrAttrType::Title:
+ pObj->SetTitle( msNewStr );
+ break;
+ case ObjStrAttrType::Description:
+ pObj->SetDescription( msNewStr );
+ break;
+ }
+
+ ImpShowPageOfThisObject();
+}
+
+OUString SdrUndoObjStrAttr::GetComment() const
+{
+ OUString aStr;
+ switch ( meObjStrAttr )
+ {
+ case ObjStrAttrType::Name:
+ aStr = ImpGetDescriptionStr( STR_UndoObjName) +
+ " '" + msNewStr + "'";
+ break;
+ case ObjStrAttrType::Title:
+ aStr = ImpGetDescriptionStr( STR_UndoObjTitle );
+ break;
+ case ObjStrAttrType::Description:
+ aStr = ImpGetDescriptionStr( STR_UndoObjDescription );
+ break;
+ }
+
+ return aStr;
+}
+
+
+SdrUndoLayer::SdrUndoLayer(sal_uInt16 nLayerNum, SdrLayerAdmin& rNewLayerAdmin, SdrModel& rNewModel)
+ : SdrUndoAction(rNewModel)
+ , pLayer(rNewLayerAdmin.GetLayer(nLayerNum))
+ , pLayerAdmin(&rNewLayerAdmin)
+ , nNum(nLayerNum)
+ , bItsMine(false)
+{
+}
+
+SdrUndoLayer::~SdrUndoLayer()
+{
+ if (bItsMine)
+ {
+ delete pLayer;
+ }
+}
+
+
+void SdrUndoNewLayer::Undo()
+{
+ DBG_ASSERT(!bItsMine,"SdrUndoNewLayer::Undo(): Layer already belongs to UndoAction.");
+ bItsMine=true;
+ SdrLayer* pCmpLayer= pLayerAdmin->RemoveLayer(nNum).release();
+ DBG_ASSERT(pCmpLayer==pLayer,"SdrUndoNewLayer::Undo(): Removed layer is != pLayer.");
+}
+
+void SdrUndoNewLayer::Redo()
+{
+ DBG_ASSERT(bItsMine,"SdrUndoNewLayer::Undo(): Layer does not belong to UndoAction.");
+ bItsMine=false;
+ pLayerAdmin->InsertLayer(std::unique_ptr<SdrLayer>(pLayer),nNum);
+}
+
+OUString SdrUndoNewLayer::GetComment() const
+{
+ return SvxResId(STR_UndoNewLayer);
+}
+
+
+void SdrUndoDelLayer::Undo()
+{
+ DBG_ASSERT(bItsMine,"SdrUndoDelLayer::Undo(): Layer does not belong to UndoAction.");
+ bItsMine=false;
+ pLayerAdmin->InsertLayer(std::unique_ptr<SdrLayer>(pLayer),nNum);
+}
+
+void SdrUndoDelLayer::Redo()
+{
+ DBG_ASSERT(!bItsMine,"SdrUndoDelLayer::Undo(): Layer already belongs to UndoAction.");
+ bItsMine=true;
+ SdrLayer* pCmpLayer= pLayerAdmin->RemoveLayer(nNum).release();
+ DBG_ASSERT(pCmpLayer==pLayer,"SdrUndoDelLayer::Redo(): Removed layer is != pLayer.");
+}
+
+OUString SdrUndoDelLayer::GetComment() const
+{
+ return SvxResId(STR_UndoDelLayer);
+}
+
+
+SdrUndoPage::SdrUndoPage(SdrPage& rNewPg)
+: SdrUndoAction(rNewPg.getSdrModelFromSdrPage())
+ ,mxPage(&rNewPg)
+{
+}
+
+SdrUndoPage::~SdrUndoPage() {}
+
+void SdrUndoPage::ImpInsertPage(sal_uInt16 nNum)
+{
+ DBG_ASSERT(!mxPage->IsInserted(),"SdrUndoPage::ImpInsertPage(): mxPage is already inserted.");
+ if (!mxPage->IsInserted())
+ {
+ if (mxPage->IsMasterPage())
+ {
+ rMod.InsertMasterPage(mxPage.get(), nNum);
+ }
+ else
+ {
+ rMod.InsertPage(mxPage.get(), nNum);
+ }
+ }
+}
+
+void SdrUndoPage::ImpRemovePage(sal_uInt16 nNum)
+{
+ DBG_ASSERT(mxPage->IsInserted(),"SdrUndoPage::ImpRemovePage(): mxPage is not inserted.");
+ if (!mxPage->IsInserted())
+ return;
+
+ rtl::Reference<SdrPage> pChkPg;
+ if (mxPage->IsMasterPage())
+ {
+ pChkPg = rMod.RemoveMasterPage(nNum);
+ }
+ else
+ {
+ pChkPg = rMod.RemovePage(nNum);
+ }
+ DBG_ASSERT(pChkPg==mxPage,"SdrUndoPage::ImpRemovePage(): RemovePage!=mxPage");
+}
+
+void SdrUndoPage::ImpMovePage(sal_uInt16 nOldNum, sal_uInt16 nNewNum)
+{
+ DBG_ASSERT(mxPage->IsInserted(),"SdrUndoPage::ImpMovePage(): mxPage is not inserted.");
+ if (mxPage->IsInserted())
+ {
+ if (mxPage->IsMasterPage())
+ {
+ rMod.MoveMasterPage(nOldNum,nNewNum);
+ }
+ else
+ {
+ rMod.MovePage(nOldNum,nNewNum);
+ }
+ }
+}
+
+OUString SdrUndoPage::ImpGetDescriptionStr(TranslateId pStrCacheID)
+{
+ return SvxResId(pStrCacheID);
+}
+
+
+SdrUndoPageList::SdrUndoPageList(SdrPage& rNewPg)
+ : SdrUndoPage(rNewPg)
+{
+ nPageNum=rNewPg.GetPageNum();
+}
+
+SdrUndoPageList::~SdrUndoPageList()
+{
+}
+
+
+SdrUndoDelPage::SdrUndoDelPage(SdrPage& rNewPg)
+ : SdrUndoPageList(rNewPg)
+ , mbHasFillBitmap(false)
+{
+ // keep fill bitmap separately to remove it from pool if not used elsewhere
+ if (mxPage->IsMasterPage())
+ {
+ SfxStyleSheet* const pStyleSheet = mxPage->getSdrPageProperties().GetStyleSheet();
+ if (pStyleSheet)
+ queryFillBitmap(pStyleSheet->GetItemSet());
+ }
+ else
+ {
+ queryFillBitmap(mxPage->getSdrPageProperties().GetItemSet());
+ }
+ if (bool(mpFillBitmapItem))
+ clearFillBitmap();
+
+ // now remember the master page relationships
+ if(!mxPage->IsMasterPage())
+ return;
+
+ sal_uInt16 nPageCnt(rMod.GetPageCount());
+
+ for(sal_uInt16 nPageNum2(0); nPageNum2 < nPageCnt; nPageNum2++)
+ {
+ SdrPage* pDrawPage = rMod.GetPage(nPageNum2);
+
+ if(pDrawPage->TRG_HasMasterPage())
+ {
+ SdrPage& rMasterPage = pDrawPage->TRG_GetMasterPage();
+
+ if(mxPage.get() == &rMasterPage)
+ {
+ if(!pUndoGroup)
+ {
+ pUndoGroup.reset( new SdrUndoGroup(rMod) );
+ }
+
+ pUndoGroup->AddAction(rMod.GetSdrUndoFactory().CreateUndoPageRemoveMasterPage(*pDrawPage));
+ }
+ }
+ }
+}
+
+SdrUndoDelPage::~SdrUndoDelPage()
+{
+}
+
+void SdrUndoDelPage::Undo()
+{
+ if (bool(mpFillBitmapItem))
+ restoreFillBitmap();
+ ImpInsertPage(nPageNum);
+ if (pUndoGroup!=nullptr)
+ {
+ // recover master page relationships
+ pUndoGroup->Undo();
+ }
+}
+
+void SdrUndoDelPage::Redo()
+{
+ ImpRemovePage(nPageNum);
+ if (bool(mpFillBitmapItem))
+ clearFillBitmap();
+}
+
+OUString SdrUndoDelPage::GetComment() const
+{
+ return ImpGetDescriptionStr(STR_UndoDelPage);
+}
+
+OUString SdrUndoDelPage::GetSdrRepeatComment() const
+{
+ return ImpGetDescriptionStr(STR_UndoDelPage);
+}
+
+void SdrUndoDelPage::SdrRepeat(SdrView& /*rView*/)
+{
+}
+
+bool SdrUndoDelPage::CanSdrRepeat(SdrView& /*rView*/) const
+{
+ return false;
+}
+
+void SdrUndoDelPage::queryFillBitmap(const SfxItemSet& rItemSet)
+{
+ if (const XFillBitmapItem *pItem = rItemSet.GetItemIfSet(XATTR_FILLBITMAP, false))
+ mpFillBitmapItem.reset(pItem->Clone());
+ if (const XFillStyleItem *pItem = rItemSet.GetItemIfSet(XATTR_FILLSTYLE, false))
+ mbHasFillBitmap = pItem->GetValue() == css::drawing::FillStyle_BITMAP;
+}
+
+void SdrUndoDelPage::clearFillBitmap()
+{
+ if (mxPage->IsMasterPage())
+ {
+ SfxStyleSheet* const pStyleSheet = mxPage->getSdrPageProperties().GetStyleSheet();
+ assert(bool(pStyleSheet)); // who took away my stylesheet?
+ if (pStyleSheet->GetListenerCount() == 1)
+ {
+ SfxItemSet& rItemSet = pStyleSheet->GetItemSet();
+ rItemSet.ClearItem(XATTR_FILLBITMAP);
+ if (mbHasFillBitmap)
+ rItemSet.ClearItem(XATTR_FILLSTYLE);
+ }
+ }
+ else
+ {
+ SdrPageProperties &rPageProps = mxPage->getSdrPageProperties();
+ rPageProps.ClearItem(XATTR_FILLBITMAP);
+ if (mbHasFillBitmap)
+ rPageProps.ClearItem(XATTR_FILLSTYLE);
+ }
+}
+
+void SdrUndoDelPage::restoreFillBitmap()
+{
+ if (mxPage->IsMasterPage())
+ {
+ SfxStyleSheet* const pStyleSheet = mxPage->getSdrPageProperties().GetStyleSheet();
+ assert(bool(pStyleSheet)); // who took away my stylesheet?
+ if (pStyleSheet->GetListenerCount() == 1)
+ {
+ SfxItemSet& rItemSet = pStyleSheet->GetItemSet();
+ rItemSet.Put(*mpFillBitmapItem);
+ if (mbHasFillBitmap)
+ rItemSet.Put(XFillStyleItem(css::drawing::FillStyle_BITMAP));
+ }
+ }
+ else
+ {
+ SdrPageProperties &rPageProps = mxPage->getSdrPageProperties();
+ rPageProps.PutItem(*mpFillBitmapItem);
+ if (mbHasFillBitmap)
+ rPageProps.PutItem(XFillStyleItem(css::drawing::FillStyle_BITMAP));
+ }
+}
+
+
+void SdrUndoNewPage::Undo()
+{
+ ImpRemovePage(nPageNum);
+}
+
+void SdrUndoNewPage::Redo()
+{
+ ImpInsertPage(nPageNum);
+}
+
+OUString SdrUndoNewPage::GetComment() const
+{
+ return ImpGetDescriptionStr(STR_UndoNewPage);
+}
+
+
+OUString SdrUndoCopyPage::GetComment() const
+{
+ return ImpGetDescriptionStr(STR_UndoCopPage);
+}
+
+OUString SdrUndoCopyPage::GetSdrRepeatComment() const
+{
+ return ImpGetDescriptionStr(STR_UndoCopPage);
+}
+
+void SdrUndoCopyPage::SdrRepeat(SdrView& /*rView*/)
+{
+
+}
+
+bool SdrUndoCopyPage::CanSdrRepeat(SdrView& /*rView*/) const
+{
+ return false;
+}
+
+
+void SdrUndoSetPageNum::Undo()
+{
+ ImpMovePage(nNewPageNum,nOldPageNum);
+}
+
+void SdrUndoSetPageNum::Redo()
+{
+ ImpMovePage(nOldPageNum,nNewPageNum);
+}
+
+OUString SdrUndoSetPageNum::GetComment() const
+{
+ return ImpGetDescriptionStr(STR_UndoMovPage);
+}
+
+SdrUndoPageMasterPage::SdrUndoPageMasterPage(SdrPage& rChangedPage)
+ : SdrUndoPage(rChangedPage)
+ , mbOldHadMasterPage(mxPage->TRG_HasMasterPage())
+ , maOldMasterPageNumber(0)
+{
+ // get current state from page
+ if(mbOldHadMasterPage)
+ {
+ maOldSet = mxPage->TRG_GetMasterPageVisibleLayers();
+ maOldMasterPageNumber = mxPage->TRG_GetMasterPage().GetPageNum();
+ }
+}
+
+SdrUndoPageMasterPage::~SdrUndoPageMasterPage()
+{
+}
+
+SdrUndoPageRemoveMasterPage::SdrUndoPageRemoveMasterPage(SdrPage& rChangedPage)
+: SdrUndoPageMasterPage(rChangedPage)
+{
+}
+
+void SdrUndoPageRemoveMasterPage::Undo()
+{
+ if(mbOldHadMasterPage)
+ {
+ mxPage->TRG_SetMasterPage(*mxPage->getSdrModelFromSdrPage().GetMasterPage(maOldMasterPageNumber));
+ mxPage->TRG_SetMasterPageVisibleLayers(maOldSet);
+ }
+}
+
+void SdrUndoPageRemoveMasterPage::Redo()
+{
+ mxPage->TRG_ClearMasterPage();
+}
+
+OUString SdrUndoPageRemoveMasterPage::GetComment() const
+{
+ return ImpGetDescriptionStr(STR_UndoDelPageMasterDscr);
+}
+
+SdrUndoPageChangeMasterPage::SdrUndoPageChangeMasterPage(SdrPage& rChangedPage)
+ : SdrUndoPageMasterPage(rChangedPage)
+ , mbNewHadMasterPage(false)
+ , maNewMasterPageNumber(0)
+{
+}
+
+void SdrUndoPageChangeMasterPage::Undo()
+{
+ // remember values from new page
+ if(mxPage->TRG_HasMasterPage())
+ {
+ mbNewHadMasterPage = true;
+ maNewSet = mxPage->TRG_GetMasterPageVisibleLayers();
+ maNewMasterPageNumber = mxPage->TRG_GetMasterPage().GetPageNum();
+ }
+
+ // restore old values
+ if(mbOldHadMasterPage)
+ {
+ mxPage->TRG_ClearMasterPage();
+ mxPage->TRG_SetMasterPage(*mxPage->getSdrModelFromSdrPage().GetMasterPage(maOldMasterPageNumber));
+ mxPage->TRG_SetMasterPageVisibleLayers(maOldSet);
+ }
+}
+
+void SdrUndoPageChangeMasterPage::Redo()
+{
+ // restore new values
+ if(mbNewHadMasterPage)
+ {
+ mxPage->TRG_ClearMasterPage();
+ mxPage->TRG_SetMasterPage(*mxPage->getSdrModelFromSdrPage().GetMasterPage(maNewMasterPageNumber));
+ mxPage->TRG_SetMasterPageVisibleLayers(maNewSet);
+ }
+}
+
+OUString SdrUndoPageChangeMasterPage::GetComment() const
+{
+ return ImpGetDescriptionStr(STR_UndoChgPageMasterDscr);
+}
+
+
+SdrUndoFactory::~SdrUndoFactory(){}
+
+// shapes
+
+std::unique_ptr<SdrUndoAction> SdrUndoFactory::CreateUndoMoveObject( SdrObject& rObject, const Size& rDist )
+{
+ return std::make_unique<SdrUndoMoveObj>( rObject, rDist );
+}
+
+std::unique_ptr<SdrUndoAction> SdrUndoFactory::CreateUndoGeoObject( SdrObject& rObject )
+{
+ return std::make_unique<SdrUndoGeoObj>( rObject );
+}
+
+// Diagram ModelData changes
+std::unique_ptr<SdrUndoAction> SdrUndoFactory::CreateUndoDiagramModelData( SdrObject& rObject, std::shared_ptr< svx::diagram::DiagramDataState >& rStartState )
+{
+ return std::make_unique<SdrUndoDiagramModelData>( rObject, rStartState );
+}
+
+std::unique_ptr<SdrUndoAction> SdrUndoFactory::CreateUndoAttrObject( SdrObject& rObject, bool bStyleSheet1, bool bSaveText )
+{
+ return std::make_unique<SdrUndoAttrObj>( rObject, bStyleSheet1, bSaveText );
+}
+
+std::unique_ptr<SdrUndoAction> SdrUndoFactory::CreateUndoRemoveObject(SdrObject& rObject)
+{
+ return std::make_unique<SdrUndoRemoveObj>(rObject);
+}
+
+std::unique_ptr<SdrUndoAction> SdrUndoFactory::CreateUndoInsertObject( SdrObject& rObject, bool bOrdNumDirect )
+{
+ return std::make_unique<SdrUndoInsertObj>( rObject, bOrdNumDirect );
+}
+
+std::unique_ptr<SdrUndoAction> SdrUndoFactory::CreateUndoDeleteObject( SdrObject& rObject, bool bOrdNumDirect )
+{
+ return std::make_unique<SdrUndoDelObj>( rObject, bOrdNumDirect );
+}
+
+std::unique_ptr<SdrUndoAction> SdrUndoFactory::CreateUndoNewObject( SdrObject& rObject, bool bOrdNumDirect )
+{
+ return std::make_unique<SdrUndoNewObj>( rObject, bOrdNumDirect );
+}
+
+std::unique_ptr<SdrUndoAction> SdrUndoFactory::CreateUndoCopyObject( SdrObject& rObject, bool bOrdNumDirect )
+{
+ return std::make_unique<SdrUndoCopyObj>( rObject, bOrdNumDirect );
+}
+
+std::unique_ptr<SdrUndoAction> SdrUndoFactory::CreateUndoObjectOrdNum( SdrObject& rObject, sal_uInt32 nOldOrdNum1, sal_uInt32 nNewOrdNum1)
+{
+ return std::make_unique<SdrUndoObjOrdNum>( rObject, nOldOrdNum1, nNewOrdNum1 );
+}
+
+std::unique_ptr<SdrUndoAction> SdrUndoFactory::CreateUndoSort(SdrPage & rPage, ::std::vector<sal_Int32> const& rSortOrder)
+{
+ return std::make_unique<SdrUndoSort>(rPage, rSortOrder);
+}
+
+std::unique_ptr<SdrUndoAction> SdrUndoFactory::CreateUndoReplaceObject( SdrObject& rOldObject, SdrObject& rNewObject )
+{
+ return std::make_unique<SdrUndoReplaceObj>( rOldObject, rNewObject );
+}
+
+std::unique_ptr<SdrUndoAction> SdrUndoFactory::CreateUndoObjectLayerChange( SdrObject& rObject, SdrLayerID aOldLayer, SdrLayerID aNewLayer )
+{
+ return std::make_unique<SdrUndoObjectLayerChange>( rObject, aOldLayer, aNewLayer );
+}
+
+std::unique_ptr<SdrUndoAction> SdrUndoFactory::CreateUndoObjectSetText( SdrObject& rNewObj, sal_Int32 nText )
+{
+ return std::make_unique<SdrUndoObjSetText>( rNewObj, nText );
+}
+
+std::unique_ptr<SdrUndoAction> SdrUndoFactory::CreateUndoObjectStrAttr( SdrObject& rObject,
+ SdrUndoObjStrAttr::ObjStrAttrType eObjStrAttrType,
+ const OUString& sOldStr,
+ const OUString& sNewStr )
+{
+ return std::make_unique<SdrUndoObjStrAttr>( rObject, eObjStrAttrType, sOldStr, sNewStr );
+}
+
+
+// layer
+std::unique_ptr<SdrUndoAction> SdrUndoFactory::CreateUndoNewLayer(sal_uInt16 nLayerNum, SdrLayerAdmin& rNewLayerAdmin, SdrModel& rNewModel)
+{
+ return std::make_unique<SdrUndoNewLayer>( nLayerNum, rNewLayerAdmin, rNewModel );
+}
+
+std::unique_ptr<SdrUndoAction> SdrUndoFactory::CreateUndoDeleteLayer(sal_uInt16 nLayerNum, SdrLayerAdmin& rNewLayerAdmin, SdrModel& rNewModel)
+{
+ return std::make_unique<SdrUndoDelLayer>( nLayerNum, rNewLayerAdmin, rNewModel );
+}
+
+// page
+std::unique_ptr<SdrUndoAction> SdrUndoFactory::CreateUndoDeletePage(SdrPage& rPage)
+{
+ return std::make_unique<SdrUndoDelPage>(rPage);
+}
+
+std::unique_ptr<SdrUndoAction> SdrUndoFactory::CreateUndoNewPage(SdrPage& rPage)
+{
+ return std::make_unique<SdrUndoNewPage>( rPage );
+}
+
+std::unique_ptr<SdrUndoAction> SdrUndoFactory::CreateUndoCopyPage(SdrPage& rPage)
+{
+ return std::make_unique<SdrUndoCopyPage>( rPage );
+}
+
+std::unique_ptr<SdrUndoAction> SdrUndoFactory::CreateUndoSetPageNum(SdrPage& rNewPg, sal_uInt16 nOldPageNum1, sal_uInt16 nNewPageNum1)
+{
+ return std::make_unique<SdrUndoSetPageNum>( rNewPg, nOldPageNum1, nNewPageNum1 );
+}
+ // master page
+std::unique_ptr<SdrUndoAction> SdrUndoFactory::CreateUndoPageRemoveMasterPage(SdrPage& rChangedPage)
+{
+ return std::make_unique<SdrUndoPageRemoveMasterPage>( rChangedPage );
+}
+
+std::unique_ptr<SdrUndoAction> SdrUndoFactory::CreateUndoPageChangeMasterPage(SdrPage& rChangedPage)
+{
+ return std::make_unique<SdrUndoPageChangeMasterPage>(rChangedPage);
+}
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/svdraw/svdview.cxx b/svx/source/svdraw/svdview.cxx
new file mode 100644
index 000000000..d7d6d5d3a
--- /dev/null
+++ b/svx/source/svdraw/svdview.cxx
@@ -0,0 +1,1544 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * 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/outlobj.hxx>
+
+#include <svx/strings.hrc>
+#include <svx/dialmgr.hxx>
+#include <svx/svdpagv.hxx>
+#include <svx/svdmrkv.hxx>
+#include <svx/svdedxv.hxx>
+#include <svx/svdobj.hxx>
+#include <svx/svdopath.hxx>
+#include <svx/svdograf.hxx>
+#include <svx/svdomedia.hxx>
+#include <svx/svdetc.hxx>
+
+#include <svx/sdr/table/tablecontroller.hxx>
+#include <svx/svdoutl.hxx>
+#include <svx/svdview.hxx>
+#include <editeng/flditem.hxx>
+#include <svx/obj3d.hxx>
+#include <svx/svddrgmt.hxx>
+#include <svx/svdotable.hxx>
+#include <tools/debug.hxx>
+#include <svx/sdr/overlay/overlaypolypolygon.hxx>
+#include <svx/sdr/overlay/overlaymanager.hxx>
+#include <svx/sdrpaintwindow.hxx>
+#include <svx/sdrpagewindow.hxx>
+#include <svx/sdrhittesthelper.hxx>
+#include <drawinglayer/primitive2d/drawinglayer_primitivetypes2d.hxx>
+#include <drawinglayer/primitive2d/texthierarchyprimitive2d.hxx>
+#include <svx/sdr/contact/objectcontactofpageview.hxx>
+#include <sal/log.hxx>
+#include <vcl/ptrstyle.hxx>
+#include <vcl/window.hxx>
+#include <comphelper/lok.hxx>
+
+
+SdrViewEvent::SdrViewEvent()
+ : mpHdl(nullptr),
+ mpObj(nullptr),
+ mpRootObj(nullptr),
+ mpPV(nullptr),
+ mpURLField(nullptr),
+ meHit(SdrHitKind::NONE),
+ meEvent(SdrEventKind::NONE),
+ mnMouseClicks(0),
+ mnMouseMode(MouseEventModifiers::NONE),
+ mnMouseCode(0),
+ mnHlplIdx(0),
+ mnGlueId(0),
+ mbMouseDown(false),
+ mbMouseUp(false),
+ mbIsAction(false),
+ mbIsTextEdit(false),
+ mbAddMark(false),
+ mbUnmark(false),
+ mbPrevNextMark(false),
+ mbMarkPrev(false)
+{
+}
+
+// helper class for all D&D overlays
+
+void SdrDropMarkerOverlay::ImplCreateOverlays(
+ const SdrView& rView,
+ const basegfx::B2DPolyPolygon& rLinePolyPolygon)
+{
+ for(sal_uInt32 a(0); a < rView.PaintWindowCount(); a++)
+ {
+ SdrPaintWindow* pCandidate = rView.GetPaintWindow(a);
+ const rtl::Reference< sdr::overlay::OverlayManager >& xTargetOverlay = pCandidate->GetOverlayManager();
+
+ if (xTargetOverlay.is())
+ {
+ std::unique_ptr<sdr::overlay::OverlayPolyPolygonStripedAndFilled> pNew(new sdr::overlay::OverlayPolyPolygonStripedAndFilled(
+ rLinePolyPolygon));
+
+ xTargetOverlay->add(*pNew);
+ maObjects.append(std::move(pNew));
+ }
+ }
+}
+
+SdrDropMarkerOverlay::SdrDropMarkerOverlay(const SdrView& rView, const SdrObject& rObject)
+{
+ ImplCreateOverlays(
+ rView,
+ rObject.TakeXorPoly());
+}
+
+SdrDropMarkerOverlay::SdrDropMarkerOverlay(const SdrView& rView, const tools::Rectangle& rRectangle)
+{
+ basegfx::B2DPolygon aB2DPolygon;
+
+ aB2DPolygon.append(basegfx::B2DPoint(rRectangle.Left(), rRectangle.Top()));
+ aB2DPolygon.append(basegfx::B2DPoint(rRectangle.Right(), rRectangle.Top()));
+ aB2DPolygon.append(basegfx::B2DPoint(rRectangle.Right(), rRectangle.Bottom()));
+ aB2DPolygon.append(basegfx::B2DPoint(rRectangle.Left(), rRectangle.Bottom()));
+ aB2DPolygon.setClosed(true);
+
+ ImplCreateOverlays(
+ rView,
+ basegfx::B2DPolyPolygon(aB2DPolygon));
+}
+
+SdrDropMarkerOverlay::SdrDropMarkerOverlay(const SdrView& rView, const Point& rStart, const Point& rEnd)
+{
+ basegfx::B2DPolygon aB2DPolygon;
+
+ aB2DPolygon.append(basegfx::B2DPoint(rStart.X(), rStart.Y()));
+ aB2DPolygon.append(basegfx::B2DPoint(rEnd.X(), rEnd.Y()));
+ aB2DPolygon.setClosed(true);
+
+ ImplCreateOverlays(
+ rView,
+ basegfx::B2DPolyPolygon(aB2DPolygon));
+}
+
+SdrDropMarkerOverlay::~SdrDropMarkerOverlay()
+{
+ // The OverlayObjects are cleared using the destructor of OverlayObjectList.
+ // That destructor calls clear() at the list which removes all objects from the
+ // OverlayManager and deletes them.
+}
+
+SdrView::SdrView(
+ SdrModel& rSdrModel,
+ OutputDevice* pOut)
+: SdrCreateView(rSdrModel, pOut),
+ mbNoExtendedMouseDispatcher(false),
+ mbNoExtendedKeyDispatcher(false),
+ mbMasterPagePaintCaching(false)
+{
+ maAccessibilityOptions.AddListener(this);
+ onAccessibilityOptionsChanged();
+}
+
+SdrView::~SdrView()
+{
+ maAccessibilityOptions.RemoveListener(this);
+}
+
+bool SdrView::KeyInput(const KeyEvent& rKEvt, vcl::Window* pWin)
+{
+ SetActualWin(pWin ? pWin->GetOutDev() : nullptr);
+ bool bRet = SdrCreateView::KeyInput(rKEvt,pWin);
+ if (!bRet && !IsExtendedKeyInputDispatcherEnabled()) {
+ bRet = true;
+ switch (rKEvt.GetKeyCode().GetFullFunction()) {
+ case KeyFuncType::DELETE: DeleteMarked(); break;
+ case KeyFuncType::UNDO: mpModel->Undo(); break;
+ case KeyFuncType::REDO: mpModel->Redo(); break;
+ default: {
+ switch (rKEvt.GetKeyCode().GetFullCode()) {
+ case KEY_ESCAPE: {
+ if (IsTextEdit()) SdrEndTextEdit();
+ if (IsAction()) BrkAction();
+ if (pWin!=nullptr) pWin->ReleaseMouse();
+ } break;
+ case KEY_DELETE: DeleteMarked(); break;
+ case KEY_UNDO: case KEY_BACKSPACE+KEY_MOD2: mpModel->Undo(); break;
+ case KEY_BACKSPACE+KEY_MOD2+KEY_SHIFT: mpModel->Redo(); break;
+ case KEY_REPEAT: case KEY_BACKSPACE+KEY_MOD2+KEY_MOD1: mpModel->Repeat(*this); break;
+ case KEY_MOD1+KEY_A: MarkAll(); break;
+ default: bRet=false;
+ } // switch
+ }
+ } // switch
+ if (bRet && pWin!=nullptr) {
+ pWin->SetPointer(GetPreferredPointer(
+ pWin->PixelToLogic(pWin->ScreenToOutputPixel( pWin->GetPointerPosPixel() ) ),
+ pWin->GetOutDev(),
+ rKEvt.GetKeyCode().GetModifier()));
+ }
+ }
+ return bRet;
+}
+
+bool SdrView::MouseButtonDown(const MouseEvent& rMEvt, OutputDevice* pWin)
+{
+ SetActualWin(pWin);
+ if (rMEvt.IsLeft()) maDragStat.SetMouseDown(true);
+ bool bRet = SdrCreateView::MouseButtonDown(rMEvt,pWin);
+ if (!bRet && !IsExtendedMouseEventDispatcherEnabled()) {
+ SdrViewEvent aVEvt;
+ PickAnything(rMEvt,SdrMouseEventKind::BUTTONDOWN,aVEvt);
+ bRet = DoMouseEvent(aVEvt);
+ }
+ return bRet;
+}
+
+bool SdrView::MouseButtonUp(const MouseEvent& rMEvt, OutputDevice* pWin)
+{
+ SetActualWin(pWin);
+ if (rMEvt.IsLeft()) maDragStat.SetMouseDown(false);
+ bool bAction = IsAction();
+ bool bRet = !bAction && SdrCreateView::MouseButtonUp(rMEvt,pWin);
+ if (!bRet && !IsExtendedMouseEventDispatcherEnabled()) {
+ SdrViewEvent aVEvt;
+ PickAnything(rMEvt,SdrMouseEventKind::BUTTONUP,aVEvt);
+ bRet = DoMouseEvent(aVEvt);
+ }
+ return bRet;
+}
+
+bool SdrView::MouseMove(const MouseEvent& rMEvt, OutputDevice* pWin)
+{
+ SetActualWin(pWin);
+ maDragStat.SetMouseDown(rMEvt.IsLeft());
+ bool bRet = SdrCreateView::MouseMove(rMEvt,pWin);
+ if (!IsExtendedMouseEventDispatcherEnabled() && !IsTextEditInSelectionMode()) {
+ SdrViewEvent aVEvt;
+ PickAnything(rMEvt,SdrMouseEventKind::MOVE,aVEvt);
+ if (DoMouseEvent(aVEvt)) bRet=true;
+ }
+
+ return bRet;
+}
+
+bool SdrView::Command(const CommandEvent& rCEvt, vcl::Window* pWin)
+{
+ SetActualWin(pWin->GetOutDev());
+ bool bRet = SdrCreateView::Command(rCEvt,pWin);
+ return bRet;
+}
+
+void SdrView::GetAttributes(SfxItemSet& rTargetSet, bool bOnlyHardAttr) const
+{
+ SdrCreateView::GetAttributes(rTargetSet, bOnlyHardAttr);
+}
+
+SdrHitKind SdrView::PickAnything(const MouseEvent& rMEvt, SdrMouseEventKind nEventKind, SdrViewEvent& rVEvt) const
+{
+ rVEvt.mbMouseDown = nEventKind==SdrMouseEventKind::BUTTONDOWN;
+ rVEvt.mbMouseUp = nEventKind==SdrMouseEventKind::BUTTONUP;
+ rVEvt.mnMouseClicks = rMEvt.GetClicks();
+ rVEvt.mnMouseMode = rMEvt.GetMode();
+ rVEvt.mnMouseCode = rMEvt.GetButtons() | rMEvt.GetModifier();
+ const OutputDevice* pOut=mpActualOutDev;
+ if (pOut==nullptr)
+ {
+ pOut = GetFirstOutputDevice();
+ }
+ Point aPnt(rMEvt.GetPosPixel());
+ if (pOut!=nullptr) aPnt=pOut->PixelToLogic(aPnt);
+
+ if (mbNegativeX)
+ {
+ // Shape's x coordinates are all negated,
+ // Hence negate mouse event's x coord to match.
+ aPnt.setX(-aPnt.X());
+ }
+
+ rVEvt.maLogicPos = aPnt;
+ return PickAnything(aPnt,rVEvt);
+}
+
+// Dragging with the Mouse (Move)
+// Example when creating a rectangle: MouseDown has to happen without a ModKey,
+// else we usually force a selection (see below).
+// When pressing Shift, Ctrl and Alt at the same time while doing a MouseMove,
+// a centered, not snapped square is created.
+// The dual allocation of Ortho and Shift won't usually create a problem, as the
+// two functions are in most cases mutually exclusive. Only shearing (the kind
+// that happens when contorting, not when rotating) can use both functions at
+// the same time. To get around this, the user can use e. g. help lines.
+#define MODKEY_NoSnap bCtrl /* temporarily disable snapping */
+#define MODKEY_Ortho bShift /* ortho */
+#define MODKEY_Center bAlt /* create/resize centeredly */
+#define MODKEY_AngleSnap bShift
+#define MODKEY_CopyDrag bCtrl /* drag and copy */
+
+// click somewhere (MouseDown)
+#define MODKEY_PolyPoly bAlt /* new Poly at InsPt and at Create */
+#define MODKEY_MultiMark bShift /* MarkObj without doing UnmarkAll first */
+#define MODKEY_Unmark bAlt /* deselect with a dragged frame */
+#define MODKEY_ForceMark bCtrl /* force dragging a frame, even if there's an object at cursor position */
+#define MODKEY_DeepMark bAlt /* MarkNextObj */
+#define MODKEY_DeepBackw bShift /* MarkNextObj but backwards */
+
+SdrHitKind SdrView::PickAnything(const Point& rLogicPos, SdrViewEvent& rVEvt) const
+{
+ const OutputDevice* pOut=mpActualOutDev;
+ if (pOut==nullptr)
+ {
+ pOut = GetFirstOutputDevice();
+ }
+
+ // #i73628# Use a non-changeable copy of he logic position
+ const Point aLocalLogicPosition(rLogicPos);
+
+ bool bEditMode=IsEditMode();
+ bool bPointMode=bEditMode && HasMarkablePoints();
+ bool bGluePointMode=IsGluePointEditMode();
+ bool bInsPolyPt=bPointMode && IsInsObjPointMode() && IsInsObjPointPossible();
+ bool bInsGluePt=bGluePointMode && IsInsGluePointMode() && IsInsGluePointPossible();
+ bool bIsTextEdit=IsTextEdit();
+ bool bTextEditHit=IsTextEditHit(aLocalLogicPosition);
+ bool bTextEditSel=IsTextEditInSelectionMode();
+ bool bShift = (rVEvt.mnMouseCode & KEY_SHIFT) != 0;
+ bool bCtrl = (rVEvt.mnMouseCode & KEY_MOD1) != 0;
+ bool bAlt = (rVEvt.mnMouseCode & KEY_MOD2) != 0;
+ SdrHitKind eHit=SdrHitKind::NONE;
+ SdrHdl* pHdl=pOut!=nullptr && !bTextEditSel ? PickHandle(aLocalLogicPosition) : nullptr;
+ SdrPageView* pPV=nullptr;
+ SdrObject* pObj=nullptr;
+ SdrObject* pHitObj=nullptr;
+ bool bHitPassDirect=true;
+ sal_uInt16 nHlplIdx=0;
+ sal_uInt16 nGlueId=0;
+ if (bTextEditHit || bTextEditSel)
+ {
+ eHit=SdrHitKind::TextEdit;
+ }
+ else if (pHdl!=nullptr)
+ {
+ eHit=SdrHitKind::Handle; // handle is hit: highest priority
+ }
+ else if (bEditMode && IsHlplVisible() && IsHlplFront() && pOut!=nullptr && PickHelpLine(aLocalLogicPosition,mnHitTolLog,*pOut,nHlplIdx,pPV))
+ {
+ eHit=SdrHitKind::HelpLine; // help line in the foreground hit: can be moved now
+ }
+ else if (bGluePointMode && PickGluePoint(aLocalLogicPosition,pObj,nGlueId,pPV))
+ {
+ eHit=SdrHitKind::Gluepoint; // deselected gluepoint hit
+ }
+ else if ((pHitObj = PickObj(aLocalLogicPosition,mnHitTolLog,pPV,SdrSearchOptions::DEEP|SdrSearchOptions::MARKED,&pObj,&bHitPassDirect)))
+ {
+ eHit=SdrHitKind::MarkedObject;
+ sdr::table::SdrTableObj* pTableObj = dynamic_cast< sdr::table::SdrTableObj* >( pObj );
+ if( pTableObj )
+ {
+ sal_Int32 nX = 0, nY = 0;
+ switch( pTableObj->CheckTableHit( aLocalLogicPosition, nX, nY ) )
+ {
+ case sdr::table::TableHitKind::Cell:
+ eHit = SdrHitKind::Cell;
+ break;
+ case sdr::table::TableHitKind::CellTextArea:
+ eHit = SdrHitKind::TextEditObj;
+ break;
+ default:
+ break;
+ }
+ }
+ }
+ else if ((pHitObj = PickObj(aLocalLogicPosition,mnHitTolLog,pPV,SdrSearchOptions::DEEP|SdrSearchOptions::ALSOONMASTER|SdrSearchOptions::WHOLEPAGE,&pObj,&bHitPassDirect)))
+ {
+ // MasterPages and WholePage for Macro and URL
+ eHit=SdrHitKind::UnmarkedObject;
+ sdr::table::SdrTableObj* pTableObj = dynamic_cast< sdr::table::SdrTableObj* >( pObj );
+ if( pTableObj )
+ {
+ sal_Int32 nX = 0, nY = 0;
+ switch( pTableObj->CheckTableHit( aLocalLogicPosition, nX, nY, mnHitTolLog ) )
+ {
+ case sdr::table::TableHitKind::Cell:
+ eHit = SdrHitKind::Cell;
+ break;
+ case sdr::table::TableHitKind::CellTextArea:
+ // Keep state on UnmarkedObject to allow the below
+ // 'check for URL field' to be executed, else popups
+ // for e.g. URL links when hoovering and clicking
+ // them will not work. Tried several other changes,
+ // but this one safely keeps existing behaviour as-is.
+ // Except for the LOK. LOK doesn't have hoovering popup
+ // feature.
+ eHit = comphelper::LibreOfficeKit::isActive() ? SdrHitKind::TextEditObj : SdrHitKind::UnmarkedObject;
+ break;
+ default:
+ break;
+ }
+ }
+ }
+ else if (bEditMode && IsHlplVisible() && !IsHlplFront() && pOut!=nullptr && PickHelpLine(aLocalLogicPosition,mnHitTolLog,*pOut,nHlplIdx,pPV))
+ {
+ eHit=SdrHitKind::HelpLine; // help line in foreground hit: can be moved now
+ }
+ if (eHit==SdrHitKind::UnmarkedObject)
+ {
+ bool bRoot=pObj->HasMacro();
+ bool bDeep=pObj!=pHitObj && pHitObj->HasMacro();
+ bool bMid=false; // Have we hit upon a grouped group with a macro?
+ SdrObject* pMidObj=nullptr;
+ if (pObj!=pHitObj)
+ {
+ SdrObject* pObjTmp=pHitObj->getParentSdrObjectFromSdrObject();
+ if (pObjTmp==pObj) pObjTmp=nullptr;
+ while (pObjTmp!=nullptr)
+ {
+ if (pObjTmp->HasMacro())
+ {
+ bMid=true;
+ pMidObj=pObjTmp;
+ }
+ pObjTmp=pObjTmp->getParentSdrObjectFromSdrObject();
+ if (pObjTmp==pObj) pObjTmp=nullptr;
+ }
+ }
+
+ if (bDeep || bMid || bRoot)
+ {
+ SdrObjMacroHitRec aHitRec;
+ aHitRec.aPos=aLocalLogicPosition;
+ aHitRec.nTol=mnHitTolLog;
+ aHitRec.pVisiLayer=&pPV->GetVisibleLayers();
+ aHitRec.pPageView=pPV;
+ if (bDeep) bDeep=pHitObj->IsMacroHit(aHitRec);
+ if (bMid ) bMid =pMidObj->IsMacroHit(aHitRec);
+ if (bRoot) bRoot=pObj->IsMacroHit(aHitRec);
+ if (bRoot || bMid || bDeep)
+ {
+ // Priorities: 1. Root, 2. Mid, 3. Deep
+ rVEvt.mpRootObj = pObj;
+ if (!bRoot) pObj=pMidObj;
+ if (!bRoot && !bMid) pObj=pHitObj;
+ eHit=SdrHitKind::Macro;
+ }
+ }
+ }
+ // check for URL field
+ if (eHit==SdrHitKind::UnmarkedObject)
+ {
+ SdrTextObj* pTextObj=dynamic_cast<SdrTextObj*>( pHitObj );
+ if (pTextObj!=nullptr && pTextObj->HasText())
+ {
+ // use the primitive-based HitTest which is more accurate anyways. It
+ // will correctly handle rotated/mirrored/sheared/scaled text and can
+ // now return a HitContainer containing the primitive hierarchy of the
+ // primitive that triggered the hit. The first entry is that primitive,
+ // the others are the full stack of primitives leading to that one which
+ // includes grouping primitives (like TextHierarchyPrimitives we deed here)
+ // but also all decomposed ones which lead to the creation of that primitive
+ drawinglayer::primitive2d::Primitive2DContainer aHitContainer;
+ const bool bTEHit(pPV && SdrObjectPrimitiveHit(*pTextObj, aLocalLogicPosition, 0, *pPV, &pPV->GetVisibleLayers(), true, &aHitContainer));
+
+ if (bTEHit && !aHitContainer.empty())
+ {
+ // search for TextHierarchyFieldPrimitive2D which contains the needed information
+ // about a possible URLField
+ const drawinglayer::primitive2d::TextHierarchyFieldPrimitive2D* pTextHierarchyFieldPrimitive2D = nullptr;
+
+ for (const drawinglayer::primitive2d::Primitive2DReference& xReference : aHitContainer)
+ {
+ auto pBasePrimitive = xReference.get();
+ if (pBasePrimitive && pBasePrimitive->getPrimitive2DID() == PRIMITIVE2D_ID_TEXTHIERARCHYFIELDPRIMITIVE2D)
+ {
+ pTextHierarchyFieldPrimitive2D = static_cast<const drawinglayer::primitive2d::TextHierarchyFieldPrimitive2D*>(pBasePrimitive);
+ break;
+ }
+ }
+
+ if (nullptr != pTextHierarchyFieldPrimitive2D)
+ {
+ if (drawinglayer::primitive2d::FieldType::FIELD_TYPE_URL == pTextHierarchyFieldPrimitive2D->getType())
+ {
+ // problem with the old code is that a *pointer* to an instance of
+ // SvxURLField is set in the Event which is per se not good since that
+ // data comes from a temporary EditEngine's data and could vanish any
+ // moment. Have to replace for now with a static instance that gets
+ // filled/initialized from the original data held in the TextHierarchyField-
+ // Primitive2D (see impTextBreakupHandler::impCheckFieldPrimitive).
+ // Unfortunately things like 'TargetFrame' are still used in Calc, so this
+ // can currently not get replaced. For the future the Name/Value vector or
+ // the TextHierarchyFieldPrimitive2D itself should/will be used for handling
+ // that data
+ static SvxURLField aSvxURLField;
+
+ aSvxURLField.SetURL(pTextHierarchyFieldPrimitive2D->getValue("URL"));
+ aSvxURLField.SetRepresentation(pTextHierarchyFieldPrimitive2D->getValue("Representation"));
+ aSvxURLField.SetTargetFrame(pTextHierarchyFieldPrimitive2D->getValue("TargetFrame"));
+ const OUString aFormat(pTextHierarchyFieldPrimitive2D->getValue("SvxURLFormat"));
+
+ if (!aFormat.isEmpty())
+ {
+ aSvxURLField.SetFormat(static_cast<SvxURLFormat>(aFormat.toInt32()));
+ }
+
+ // set HitKind and pointer to local static instance in the Event
+ // to comply to old stuff
+ eHit = SdrHitKind::UrlField;
+ rVEvt.mpURLField = &aSvxURLField;
+ }
+ }
+ }
+ }
+ if (eHit==SdrHitKind::UnmarkedObject && !pHitObj->getHyperlink().isEmpty())
+ {
+ static SvxURLField aSvxURLField;
+ aSvxURLField.SetURL(pHitObj->getHyperlink());
+ rVEvt.mpURLField = &aSvxURLField;
+ eHit = SdrHitKind::UrlField;
+ }
+ }
+
+ if (bHitPassDirect &&
+ (eHit==SdrHitKind::MarkedObject || eHit==SdrHitKind::UnmarkedObject) &&
+ (IsTextTool() || (IsEditMode() && IsQuickTextEditMode())) && pHitObj->HasTextEdit())
+ {
+ // Around the TextEditArea there's a border to select without going into text edit mode.
+ tools::Rectangle aBoundRect(pHitObj->GetCurrentBoundRect());
+
+ // Force to SnapRect when Fontwork
+ if( auto pTextObj = dynamic_cast<const SdrTextObj*>(pHitObj) )
+ if( pTextObj->IsFontwork() )
+ aBoundRect = pHitObj->GetSnapRect();
+
+ sal_Int32 nTolerance(mnHitTolLog);
+ bool bBoundRectHit(false);
+
+ if(pOut)
+ {
+ nTolerance = pOut->PixelToLogic(Size(2, 0)).Width();
+ }
+
+ if( (aLocalLogicPosition.X() >= aBoundRect.Left() - nTolerance && aLocalLogicPosition.X() <= aBoundRect.Left() + nTolerance)
+ || (aLocalLogicPosition.X() >= aBoundRect.Right() - nTolerance && aLocalLogicPosition.X() <= aBoundRect.Right() + nTolerance)
+ || (aLocalLogicPosition.Y() >= aBoundRect.Top() - nTolerance && aLocalLogicPosition.Y() <= aBoundRect.Top() + nTolerance)
+ || (aLocalLogicPosition.Y() >= aBoundRect.Bottom() - nTolerance && aLocalLogicPosition.Y() <= aBoundRect.Bottom() + nTolerance))
+ {
+ bBoundRectHit = true;
+ }
+
+ if(!bBoundRectHit)
+ {
+ bool bTEHit(pPV &&
+ SdrObjectPrimitiveHit(*pHitObj, aLocalLogicPosition, 0, *pPV, &pPV->GetVisibleLayers(), true));
+
+ // TextEdit attached to an object in a locked layer
+ if (bTEHit && pPV->GetLockedLayers().IsSet(pHitObj->GetLayer()))
+ {
+ bTEHit=false;
+ }
+
+ if (bTEHit)
+ {
+ rVEvt.mpRootObj=pObj;
+ pObj=pHitObj;
+ eHit=SdrHitKind::TextEditObj;
+ }
+ }
+ }
+ if (!bHitPassDirect && eHit==SdrHitKind::UnmarkedObject) {
+ eHit=SdrHitKind::NONE;
+ pObj=nullptr;
+ pPV=nullptr;
+ }
+ bool bMouseLeft = (rVEvt.mnMouseCode & MOUSE_LEFT) != 0;
+ bool bMouseRight = (rVEvt.mnMouseCode & MOUSE_RIGHT) != 0;
+ bool bMouseDown = rVEvt.mbMouseDown;
+ bool bMouseUp = rVEvt.mbMouseUp;
+ SdrEventKind eEvent=SdrEventKind::NONE;
+ bool bIsAction=IsAction();
+
+ if (bIsAction)
+ {
+ if (bMouseDown)
+ {
+ if (bMouseRight) eEvent=SdrEventKind::BackAction;
+ }
+ else if (bMouseUp)
+ {
+ if (bMouseLeft)
+ {
+ eEvent=SdrEventKind::EndAction;
+ if (IsDragObj())
+ {
+ eEvent=SdrEventKind::EndDrag;
+ }
+ else if (IsCreateObj() || IsInsObjPoint())
+ {
+ eEvent=IsCreateObj() ? SdrEventKind::EndCreate : SdrEventKind::EndInsertObjPoint;
+ }
+ else if (IsMarking())
+ {
+ eEvent=SdrEventKind::EndMark;
+ if (!maDragStat.IsMinMoved())
+ {
+ eEvent=SdrEventKind::BrkMark;
+ rVEvt.mbAddMark = MODKEY_MultiMark;
+ }
+ }
+ }
+ }
+ else
+ {
+ eEvent=SdrEventKind::MoveAction;
+ }
+ }
+ else if (eHit==SdrHitKind::TextEdit)
+ {
+ eEvent=SdrEventKind::TextEdit;
+ }
+ else if (bMouseDown && bMouseLeft)
+ {
+ if (rVEvt.mnMouseClicks == 2 && rVEvt.mnMouseCode == MOUSE_LEFT && pObj!=nullptr && pHitObj!=nullptr && pHitObj->HasTextEdit() && eHit==SdrHitKind::MarkedObject)
+ {
+ rVEvt.mpRootObj = pObj;
+ pObj=pHitObj;
+ eEvent=SdrEventKind::BeginTextEdit;
+ }
+ else if (MODKEY_ForceMark && eHit!=SdrHitKind::UrlField)
+ {
+ eEvent=SdrEventKind::BeginMark; // AddMark,Unmark */
+ }
+ else if (eHit==SdrHitKind::HelpLine)
+ {
+ eEvent=SdrEventKind::BeginDragHelpline; // nothing, actually
+ }
+ else if (eHit==SdrHitKind::Gluepoint)
+ {
+ eEvent=SdrEventKind::MarkGluePoint; // AddMark+Drag
+ rVEvt.mbAddMark = MODKEY_MultiMark || MODKEY_DeepMark; // if not hit with Deep
+ }
+ else if (eHit==SdrHitKind::Handle)
+ {
+ eEvent=SdrEventKind::BeginDragObj; // Mark+Drag,AddMark+Drag,DeepMark+Drag,Unmark
+ bool bGlue=pHdl->GetKind()==SdrHdlKind::Glue;
+ bool bPoly=!bGlue && IsPointMarkable(*pHdl);
+ bool bMarked=bGlue || (bPoly && pHdl->IsSelected());
+ if (bGlue || bPoly)
+ {
+ eEvent=bGlue ? SdrEventKind::MarkGluePoint : SdrEventKind::MarkPoint;
+ if (MODKEY_DeepMark)
+ {
+ rVEvt.mbAddMark = true;
+ rVEvt.mbPrevNextMark = true;
+ rVEvt.mbMarkPrev = MODKEY_DeepBackw;
+ }
+ else if (MODKEY_MultiMark)
+ {
+ rVEvt.mbAddMark = true;
+ rVEvt.mbUnmark = bMarked; // Toggle
+ if (bGlue)
+ {
+ pObj=pHdl->GetObj();
+ nGlueId=static_cast<sal_uInt16>(pHdl->GetObjHdlNum());
+ }
+ }
+ else if (bMarked)
+ {
+ eEvent=SdrEventKind::BeginDragObj; // don't change MarkState, only change Drag
+ }
+ }
+ }
+ else if (bInsPolyPt && (MODKEY_PolyPoly || (!MODKEY_MultiMark && !MODKEY_DeepMark)))
+ {
+ eEvent=SdrEventKind::BeginInsertObjPoint;
+ }
+ else if (bInsGluePt && !MODKEY_MultiMark && !MODKEY_DeepMark)
+ {
+ eEvent=SdrEventKind::BeginInsertGluePoint;
+ }
+ else if (eHit==SdrHitKind::TextEditObj)
+ {
+ eEvent=SdrEventKind::BeginTextEdit; // AddMark+Drag,DeepMark+Drag,Unmark
+ if (MODKEY_MultiMark || MODKEY_DeepMark)
+ { // if not hit with Deep
+ eEvent=SdrEventKind::MarkObj;
+ }
+ }
+ else if (eHit==SdrHitKind::Macro)
+ {
+ eEvent=SdrEventKind::BeginMacroObj; // AddMark+Drag
+ if (MODKEY_MultiMark || MODKEY_DeepMark)
+ { // if not hit with Deep
+ eEvent=SdrEventKind::MarkObj;
+ }
+ }
+ else if (eHit==SdrHitKind::UrlField)
+ {
+ eEvent=SdrEventKind::ExecuteUrl; // AddMark+Drag
+ if (MODKEY_MultiMark || MODKEY_DeepMark)
+ { // if not hit with Deep
+ eEvent=SdrEventKind::MarkObj;
+ }
+ }
+ else if (eHit==SdrHitKind::MarkedObject)
+ {
+ eEvent=SdrEventKind::BeginDragObj; // DeepMark+Drag,Unmark
+
+ if (MODKEY_MultiMark || MODKEY_DeepMark)
+ { // if not hit with Deep
+ eEvent=SdrEventKind::MarkObj;
+ }
+ }
+ else if (IsCreateMode())
+ {
+ eEvent=SdrEventKind::BeginCreateObj; // nothing, actually
+ }
+ else if (eHit==SdrHitKind::UnmarkedObject)
+ {
+ eEvent=SdrEventKind::MarkObj; // AddMark+Drag
+ }
+ else
+ {
+ eEvent=SdrEventKind::BeginMark;
+ }
+
+ if (eEvent==SdrEventKind::MarkObj)
+ {
+ rVEvt.mbAddMark = MODKEY_MultiMark || MODKEY_DeepMark; // if not hit with Deep
+ rVEvt.mbPrevNextMark = MODKEY_DeepMark;
+ rVEvt.mbMarkPrev = MODKEY_DeepMark && MODKEY_DeepBackw;
+ }
+ if (eEvent==SdrEventKind::BeginMark)
+ {
+ rVEvt.mbAddMark = MODKEY_MultiMark;
+ rVEvt.mbUnmark = MODKEY_Unmark;
+ }
+ }
+ rVEvt.mbIsAction = bIsAction;
+ rVEvt.mbIsTextEdit = bIsTextEdit;
+ rVEvt.maLogicPos = aLocalLogicPosition;
+ rVEvt.mpHdl = pHdl;
+ rVEvt.mpObj = pObj;
+ if (rVEvt.mpRootObj == nullptr)
+ rVEvt.mpRootObj = pObj;
+ rVEvt.mpPV = pPV;
+ rVEvt.mnHlplIdx = nHlplIdx;
+ rVEvt.mnGlueId = nGlueId;
+ rVEvt.meHit = eHit;
+ rVEvt.meEvent = eEvent;
+#ifdef DGB_UTIL
+ if (rVEvt.mpRootObj != nullptr)
+ {
+ if (rVEvt.mpRootObj->getParentSdrObjListFromSdrObject() != rVEvt.mpPV->GetObjList())
+ {
+ OSL_FAIL("SdrView::PickAnything(): pRootObj->getParentSdrObjListFromSdrObject()!=pPV->GetObjList() !");
+ }
+ }
+#endif
+ return eHit;
+}
+
+bool SdrView::DoMouseEvent(const SdrViewEvent& rVEvt)
+{
+ bool bRet=false;
+ SdrHitKind eHit = rVEvt.meHit;
+ Point aLogicPos(rVEvt.maLogicPos);
+
+ bool bShift = (rVEvt.mnMouseCode & KEY_SHIFT) != 0;
+ bool bCtrl = (rVEvt.mnMouseCode & KEY_MOD1) != 0;
+ bool bAlt = (rVEvt.mnMouseCode & KEY_MOD2) != 0;
+ bool bMouseLeft = (rVEvt.mnMouseCode & MOUSE_LEFT) != 0;
+ bool bMouseDown = rVEvt.mbMouseDown;
+ bool bMouseUp = rVEvt.mbMouseUp;
+ if (bMouseDown) {
+ if (bMouseLeft) maDragStat.SetMouseDown(true);
+ } else if (bMouseUp) {
+ if (bMouseLeft) maDragStat.SetMouseDown(false);
+ } else { // else, MouseMove
+ maDragStat.SetMouseDown(bMouseLeft);
+ }
+
+#ifdef MODKEY_NoSnap
+ SetSnapEnabled(!MODKEY_NoSnap);
+#endif
+#ifdef MODKEY_Ortho
+ SetOrtho(MODKEY_Ortho!=IsOrthoDesired());
+#endif
+#ifdef MODKEY_AngleSnap
+ SetAngleSnapEnabled(MODKEY_AngleSnap);
+#endif
+#ifdef MODKEY_CopyDrag
+ SetDragWithCopy(MODKEY_CopyDrag);
+#endif
+#ifdef MODKEY_Center
+ SetCreate1stPointAsCenter(MODKEY_Center);
+ SetResizeAtCenter(MODKEY_Center);
+ SetCrookAtCenter(MODKEY_Center);
+#endif
+ if (bMouseLeft && bMouseDown && rVEvt.mbIsTextEdit && (eHit==SdrHitKind::UnmarkedObject || eHit==SdrHitKind::NONE)) {
+ SdrEndTextEdit(); // User has clicked beneath object, exit edit mode.
+ // pHdl is invalid, then, that shouldn't matter, though, as we expect
+ // pHdl==NULL (because of eHit).
+ }
+ switch (rVEvt.meEvent)
+ {
+ case SdrEventKind::NONE: bRet=false; break;
+ case SdrEventKind::TextEdit: bRet=false; break; // Events handled by the OutlinerView are not taken into account here.
+ case SdrEventKind::MoveAction: MovAction(aLogicPos); bRet=true; break;
+ case SdrEventKind::EndAction: EndAction(); bRet=true; break;
+ case SdrEventKind::BackAction: BckAction(); bRet=true; break;
+ case SdrEventKind::EndMark : EndAction(); bRet=true; break;
+ case SdrEventKind::BrkMark : {
+ BrkAction();
+ if (!MarkObj(aLogicPos, mnHitTolLog, rVEvt.mbAddMark))
+ {
+ // No object hit. Do the following:
+ // 1. deselect any selected gluepoints
+ // 2. deselect any selected polygon points
+ // 3. deselect any selected objects
+ if (!rVEvt.mbAddMark) UnmarkAll();
+ }
+ bRet=true;
+ } break;
+ case SdrEventKind::EndCreate: { // if necessary, MarkObj
+ SdrCreateCmd eCmd=SdrCreateCmd::NextPoint;
+ if (MODKEY_PolyPoly) eCmd=SdrCreateCmd::NextObject;
+ if (rVEvt.mnMouseClicks > 1) eCmd=SdrCreateCmd::ForceEnd;
+ if (!EndCreateObj(eCmd)) { // Don't evaluate event for Create? -> Select
+ if (eHit==SdrHitKind::UnmarkedObject || eHit==SdrHitKind::TextEdit) {
+ MarkObj(rVEvt.mpRootObj, rVEvt.mpPV);
+ if (eHit==SdrHitKind::TextEdit)
+ {
+ bool bRet2(mpActualOutDev && OUTDEV_WINDOW == mpActualOutDev->GetOutDevType() &&
+ SdrBeginTextEdit(rVEvt.mpObj, rVEvt.mpPV, mpActualOutDev->GetOwnerWindow()));
+
+ if(bRet2)
+ {
+ MouseEvent aMEvt(mpActualOutDev->LogicToPixel(aLogicPos), 1,
+ rVEvt.mnMouseMode,rVEvt.mnMouseCode,rVEvt.mnMouseCode);
+
+ OutlinerView* pOLV=GetTextEditOutlinerView();
+ if (pOLV!=nullptr) {
+ pOLV->MouseButtonDown(aMEvt); // event for the Outliner, but without double-click
+ pOLV->MouseButtonUp(aMEvt); // event for the Outliner, but without double-click
+ }
+ }
+ }
+ bRet=true; // object is selected and (if necessary) TextEdit is started
+ } else bRet=false; // canceled Create, nothing else
+ } else bRet=true; // return true for EndCreate
+ } break;
+ case SdrEventKind::EndDrag: {
+ bRet=EndDragObj(IsDragWithCopy());
+ ForceMarkedObjToAnotherPage(); // TODO: Undo+bracing missing!
+ } break;
+ case SdrEventKind::MarkObj: { // + (if applicable) BegDrag
+ if (!rVEvt.mbAddMark) UnmarkAllObj();
+ bool bUnmark = rVEvt.mbUnmark;
+ if (rVEvt.mbPrevNextMark) {
+ bRet=MarkNextObj(aLogicPos, mnHitTolLog, rVEvt.mbMarkPrev);
+ } else {
+ SortMarkedObjects();
+ const size_t nCount0=GetMarkedObjectCount();
+ bRet=MarkObj(aLogicPos, mnHitTolLog, rVEvt.mbAddMark);
+ SortMarkedObjects();
+ const size_t nCount1=GetMarkedObjectCount();
+ bUnmark=nCount1<nCount0;
+ }
+ if (!bUnmark) {
+ BegDragObj(aLogicPos,nullptr,nullptr,mnMinMovLog);
+ bRet=true;
+ }
+ } break;
+ case SdrEventKind::MarkPoint: { // + (if applicable) BegDrag
+ if (!rVEvt.mbAddMark) UnmarkAllPoints();
+ if (rVEvt.mbPrevNextMark) {
+ MarkNextPoint();
+ bRet=false;
+ } else {
+ bRet = MarkPoint(*rVEvt.mpHdl, rVEvt.mbUnmark);
+ }
+ if (!rVEvt.mbUnmark && !rVEvt.mbPrevNextMark) {
+ BegDragObj(aLogicPos, nullptr, rVEvt.mpHdl, mnMinMovLog);
+ bRet=true;
+ }
+ } break;
+ case SdrEventKind::MarkGluePoint: { // + (if applicable) BegDrag
+ if (!rVEvt.mbAddMark) UnmarkAllGluePoints();
+ if (rVEvt.mbPrevNextMark) {
+ MarkNextGluePoint();
+ bRet=false;
+ } else {
+ bRet=MarkGluePoint(rVEvt.mpObj,rVEvt.mnGlueId,rVEvt.mbUnmark);
+ }
+ if (!rVEvt.mbUnmark && !rVEvt.mbPrevNextMark) {
+ SdrHdl* pHdl = GetGluePointHdl(rVEvt.mpObj, rVEvt.mnGlueId);
+ BegDragObj(aLogicPos,nullptr,pHdl,mnMinMovLog);
+ bRet=true;
+ }
+ } break;
+ case SdrEventKind::BeginMark: bRet = BegMark(aLogicPos,rVEvt.mbAddMark,rVEvt.mbUnmark); break;
+ case SdrEventKind::BeginInsertObjPoint: bRet = BegInsObjPoint(aLogicPos, MODKEY_PolyPoly); break;
+ case SdrEventKind::EndInsertObjPoint: {
+ SdrCreateCmd eCmd=SdrCreateCmd::NextPoint;
+ if (MODKEY_PolyPoly) eCmd=SdrCreateCmd::NextObject;
+ if (rVEvt.mnMouseClicks > 1) eCmd = SdrCreateCmd::ForceEnd;
+ EndInsObjPoint(eCmd);
+ bRet=true;
+ } break;
+ case SdrEventKind::BeginInsertGluePoint: bRet=BegInsGluePoint(aLogicPos); break;
+ case SdrEventKind::BeginDragHelpline: bRet = BegDragHelpLine(rVEvt.mnHlplIdx,rVEvt.mpPV); break;
+ case SdrEventKind::BeginDragObj: bRet=BegDragObj(aLogicPos, nullptr, rVEvt.mpHdl, mnMinMovLog); break;
+ case SdrEventKind::BeginCreateObj: {
+ if (mnCurrentInvent==SdrInventor::Default && mnCurrentIdent==SdrObjKind::Caption) {
+ tools::Long nHgt=SdrEngineDefaults::GetFontHeight();
+ bRet=BegCreateCaptionObj(aLogicPos,Size(5*nHgt,2*nHgt));
+ } else bRet=BegCreateObj(aLogicPos);
+ } break;
+ case SdrEventKind::BeginMacroObj: {
+ BegMacroObj(aLogicPos, mnHitTolLog, rVEvt.mpObj, rVEvt.mpPV, mpActualOutDev->GetOwnerWindow());
+ bRet=false;
+ } break;
+ case SdrEventKind::BeginTextEdit: {
+ if (!IsObjMarked(rVEvt.mpObj)) {
+ UnmarkAllObj();
+ MarkObj(rVEvt.mpRootObj,rVEvt.mpPV);
+ }
+
+ bRet = mpActualOutDev && OUTDEV_WINDOW == mpActualOutDev->GetOutDevType()&&
+ SdrBeginTextEdit(rVEvt.mpObj, rVEvt.mpPV, mpActualOutDev->GetOwnerWindow());
+
+ if(bRet)
+ {
+ MouseEvent aMEvt(mpActualOutDev->LogicToPixel(aLogicPos),
+ 1, rVEvt.mnMouseMode, rVEvt.mnMouseCode, rVEvt.mnMouseCode);
+ OutlinerView* pOLV=GetTextEditOutlinerView();
+ if (pOLV!=nullptr) pOLV->MouseButtonDown(aMEvt); // event for the Outliner, but without double-click
+ }
+ } break;
+ default: break;
+ } // switch
+ if (bRet && mpActualOutDev && mpActualOutDev->GetOutDevType()==OUTDEV_WINDOW) {
+ vcl::Window* pWin=mpActualOutDev->GetOwnerWindow();
+ // left mouse button pressed?
+ bool bLeftDown = (rVEvt.mnMouseCode & MOUSE_LEFT) != 0 && rVEvt.mbMouseDown;
+ // left mouse button released?
+ bool bLeftUp = (rVEvt.mnMouseCode & MOUSE_LEFT) != 0 && rVEvt.mbMouseUp;
+ // left mouse button pressed or held?
+ bool bLeftDown1=(rVEvt.mnMouseCode & MOUSE_LEFT) != 0 && !rVEvt.mbMouseUp;
+ pWin->SetPointer(GetPreferredPointer(rVEvt.maLogicPos, pWin->GetOutDev(),
+ rVEvt.mnMouseCode & (KEY_SHIFT|KEY_MOD1|KEY_MOD2),bLeftDown1));
+ bool bAction=IsAction();
+ if (bLeftDown && bAction)
+ pWin->CaptureMouse();
+ else if (bLeftUp || (rVEvt.mbIsAction && !bAction))
+ pWin->ReleaseMouse();
+ }
+ return bRet;
+}
+
+PointerStyle SdrView::GetPreferredPointer(const Point& rMousePos, const OutputDevice* pOut, sal_uInt16 nModifier, bool bLeftDown) const
+{
+ // Actions
+ if (IsCreateObj())
+ {
+ return mpCurrentCreate->GetCreatePointer();
+ }
+ if (mpCurrentSdrDragMethod)
+ {
+ return mpCurrentSdrDragMethod->GetSdrDragPointer();
+ }
+ if (IsMarkObj() || IsMarkPoints() || IsMarkGluePoints() || IsSetPageOrg()) return PointerStyle::Arrow;
+ if (IsDragHelpLine()) return GetDraggedHelpLinePointer();
+ if (IsMacroObj()) {
+ SdrObjMacroHitRec aHitRec;
+ aHitRec.aPos=pOut->LogicToPixel(rMousePos);
+ aHitRec.nTol=nMacroTol;
+ aHitRec.pVisiLayer=&pMacroPV->GetVisibleLayers();
+ aHitRec.pPageView=pMacroPV;
+ return pMacroObj->GetMacroPointer(aHitRec);
+ }
+
+ // TextEdit, ObjEdit, Macro
+ if (IsTextEdit() && (IsTextEditInSelectionMode() || IsTextEditHit(rMousePos)))
+ {
+ if(!pOut || IsTextEditInSelectionMode())
+ {
+ if (mpTextEditOutliner->IsVertical())
+ return PointerStyle::TextVertical;
+ else
+ return PointerStyle::Text;
+ }
+ // Outliner should return something here...
+ Point aPos(pOut->LogicToPixel(rMousePos));
+ PointerStyle aPointer(mpTextEditOutlinerView->GetPointer(aPos));
+ if (aPointer==PointerStyle::Arrow)
+ {
+ if (mpTextEditOutliner->IsVertical())
+ aPointer = PointerStyle::TextVertical;
+ else
+ aPointer = PointerStyle::Text;
+ }
+ return aPointer;
+ }
+
+ SdrViewEvent aVEvt;
+ aVEvt.mnMouseCode = (nModifier&(KEY_SHIFT|KEY_MOD1|KEY_MOD2))|MOUSE_LEFT; // to see what would happen on MouseLeftDown
+ aVEvt.mbMouseDown = !bLeftDown; // What if ..?
+ aVEvt.mbMouseUp = bLeftDown; // What if ..?
+ if (pOut!=nullptr)
+ const_cast<SdrView*>(this)->SetActualWin(pOut);
+ SdrHitKind eHit=PickAnything(rMousePos,aVEvt);
+ SdrEventKind eEvent = aVEvt.meEvent;
+ switch (eEvent)
+ {
+ case SdrEventKind::BeginCreateObj:
+ return maCurrentCreatePointer;
+ case SdrEventKind::MarkObj:
+ return PointerStyle::Move;
+ case SdrEventKind::BeginMark:
+ return PointerStyle::Arrow;
+ case SdrEventKind::MarkPoint:
+ case SdrEventKind::MarkGluePoint:
+ return PointerStyle::MovePoint;
+ case SdrEventKind::BeginInsertObjPoint:
+ case SdrEventKind::BeginInsertGluePoint:
+ return PointerStyle::Cross;
+ case SdrEventKind::ExecuteUrl:
+ return PointerStyle::RefHand;
+ case SdrEventKind::BeginMacroObj:
+ {
+ SdrObjMacroHitRec aHitRec;
+ aHitRec.aPos = aVEvt.maLogicPos;
+ aHitRec.nTol=mnHitTolLog;
+ aHitRec.pVisiLayer = &aVEvt.mpPV->GetVisibleLayers();
+ aHitRec.pPageView = aVEvt.mpPV;
+ return aVEvt.mpObj->GetMacroPointer(aHitRec);
+ }
+ default: break;
+ } // switch
+
+ switch(eHit)
+ {
+ case SdrHitKind::Cell:
+ return PointerStyle::Arrow;
+ case SdrHitKind::HelpLine :
+ return aVEvt.mpPV->GetHelpLines()[aVEvt.mnHlplIdx].GetPointer();
+ case SdrHitKind::Gluepoint:
+ return PointerStyle::MovePoint;
+ case SdrHitKind::TextEdit :
+ case SdrHitKind::TextEditObj:
+ {
+ SdrTextObj* pText = dynamic_cast< SdrTextObj* >(aVEvt.mpObj);
+ if(pText && pText->HasText())
+ {
+ OutlinerParaObject* pParaObj = pText->GetOutlinerParaObject();
+ if(pParaObj && pParaObj->IsEffectivelyVertical())
+ return PointerStyle::TextVertical;
+ }
+ return PointerStyle::Text;
+ }
+ default: break;
+ }
+
+ bool bMarkHit=eHit==SdrHitKind::MarkedObject;
+ SdrHdl* pHdl = aVEvt.mpHdl;
+ // now check the pointers for dragging
+ if (pHdl!=nullptr || bMarkHit) {
+ SdrHdlKind eHdl= pHdl!=nullptr ? pHdl->GetKind() : SdrHdlKind::Move;
+ bool bCorner=pHdl!=nullptr && pHdl->IsCornerHdl();
+ bool bVertex=pHdl!=nullptr && pHdl->IsVertexHdl();
+ bool bMov=eHdl==SdrHdlKind::Move;
+ if (bMov && (meDragMode==SdrDragMode::Move || meDragMode==SdrDragMode::Resize || mbMarkedHitMovesAlways)) {
+ if (!IsMoveAllowed()) return PointerStyle::Arrow; // because double click or drag & drop is possible
+ return PointerStyle::Move;
+ }
+ switch (meDragMode) {
+ case SdrDragMode::Rotate: {
+ if ((bCorner || bMov) && !IsRotateAllowed(true))
+ return PointerStyle::NotAllowed;
+
+ // are 3D objects selected?
+ bool b3DObjSelected = false;
+ for (size_t a=0; !b3DObjSelected && a<GetMarkedObjectCount(); ++a) {
+ SdrObject* pObj = GetMarkedObjectByIndex(a);
+ if(dynamic_cast<const E3dObject* >(pObj) != nullptr)
+ b3DObjSelected = true;
+ }
+ // If we have a 3D object, go on despite !IsShearAllowed,
+ // because then we have a rotation instead of a shear.
+ if (bVertex && !IsShearAllowed() && !b3DObjSelected)
+ return PointerStyle::NotAllowed;
+ if (bMov)
+ return PointerStyle::Rotate;
+ } break;
+ case SdrDragMode::Shear: {
+ if (bCorner) {
+ if (!IsDistortAllowed(true) && !IsDistortAllowed()) return PointerStyle::NotAllowed;
+ else return PointerStyle::RefHand;
+ }
+ if (bVertex && !IsShearAllowed()) return PointerStyle::NotAllowed;
+ if (bMov) {
+ if (!IsMoveAllowed()) return PointerStyle::Arrow; // because double click or drag & drop is possible
+ return PointerStyle::Move;
+ }
+ } break;
+ case SdrDragMode::Mirror: {
+ if (bCorner || bVertex || bMov) {
+ SdrHdl* pH1=maHdlList.GetHdl(SdrHdlKind::Ref1);
+ SdrHdl* pH2=maHdlList.GetHdl(SdrHdlKind::Ref2);
+ bool b90=false;
+ bool b45=false;
+ if (pH1!=nullptr && pH2!=nullptr) {
+ Point aDif = pH2->GetPos()-pH1->GetPos();
+ b90=(aDif.X()==0) || aDif.Y()==0;
+ b45=b90 || (std::abs(aDif.X())==std::abs(aDif.Y()));
+ }
+ bool bNo=false;
+ if (!IsMirrorAllowed(true,true)) bNo=true; // any mirroring is forbidden
+ if (!IsMirrorAllowed() && !b45) bNo=true; // mirroring freely is forbidden
+ if (!IsMirrorAllowed(true) && !b90) bNo=true; // mirroring horizontally/vertically is allowed
+ if (bNo) return PointerStyle::NotAllowed;
+ if (b90) {
+ return PointerStyle::Mirror;
+ }
+ return PointerStyle::Mirror;
+ }
+ } break;
+
+ case SdrDragMode::Transparence:
+ {
+ if(!IsTransparenceAllowed())
+ return PointerStyle::NotAllowed;
+
+ return PointerStyle::RefHand;
+ }
+
+ case SdrDragMode::Gradient:
+ {
+ if(!IsGradientAllowed())
+ return PointerStyle::NotAllowed;
+
+ return PointerStyle::RefHand;
+ }
+
+ case SdrDragMode::Crook: {
+ if (bCorner || bVertex || bMov) {
+ if (!IsCrookAllowed(true) && !IsCrookAllowed()) return PointerStyle::NotAllowed;
+ return PointerStyle::Crook;
+ }
+ break;
+ }
+
+ case SdrDragMode::Crop:
+ {
+ return PointerStyle::Crop;
+ }
+
+ default: {
+ if ((bCorner || bVertex) && !IsResizeAllowed(true)) return PointerStyle::NotAllowed;
+ }
+ }
+ if (pHdl!=nullptr) return pHdl->GetPointer();
+ if (bMov) {
+ if (!IsMoveAllowed()) return PointerStyle::Arrow; // because double click or drag & drop is possible
+ return PointerStyle::Move;
+ }
+ }
+ if (meEditMode==SdrViewEditMode::Create) return maCurrentCreatePointer;
+ return PointerStyle::Arrow;
+}
+
+constexpr OUStringLiteral STR_NOTHING = u"nothing";
+OUString SdrView::GetStatusText()
+{
+ OUString aName;
+ OUString aStr = STR_NOTHING;
+
+ if (mpCurrentCreate!=nullptr)
+ {
+ aStr=mpCurrentCreate->getSpecialDragComment(maDragStat);
+
+ if(aStr.isEmpty())
+ {
+ aName = mpCurrentCreate->TakeObjNameSingul();
+ aStr = SvxResId(STR_ViewCreateObj);
+ }
+ }
+ else if (mpCurrentSdrDragMethod)
+ {
+ if (mbInsPolyPoint || IsInsertGluePoint())
+ {
+ aStr=maInsPointUndoStr;
+ }
+ else
+ {
+ if (maDragStat.IsMinMoved())
+ {
+ SAL_INFO(
+ "svx.svdraw",
+ "(" << this << ") " << mpCurrentSdrDragMethod.get());
+ aStr = mpCurrentSdrDragMethod->GetSdrDragComment();
+ }
+ }
+ }
+ else if(IsMarkObj())
+ {
+ if(AreObjectsMarked())
+ {
+ aStr = SvxResId(STR_ViewMarkMoreObjs);
+ }
+ else
+ {
+ aStr = SvxResId(STR_ViewMarkObjs);
+ }
+ }
+ else if(IsMarkPoints())
+ {
+ if(HasMarkedPoints())
+ {
+ aStr = SvxResId(STR_ViewMarkMorePoints);
+ }
+ else
+ {
+ aStr = SvxResId(STR_ViewMarkPoints);
+ }
+ } else if (IsMarkGluePoints())
+ {
+ if(HasMarkedGluePoints())
+ {
+ aStr = SvxResId(STR_ViewMarkMoreGluePoints);
+ }
+ else
+ {
+ aStr = SvxResId(STR_ViewMarkGluePoints);
+ }
+ }
+ else if (IsTextEdit() && mpTextEditOutlinerView != nullptr) {
+ aStr=SvxResId(STR_ViewTextEdit); // "TextEdit - Row y, Column x";
+ ESelection aSel(mpTextEditOutlinerView->GetSelection());
+ tools::Long nPar = aSel.nEndPara,nLin=0,nCol=aSel.nEndPos;
+ if (aSel.nEndPara>0) {
+ for (sal_Int32 nParaNum=0; nParaNum<aSel.nEndPara; nParaNum++) {
+ nLin += mpTextEditOutliner->GetLineCount(nParaNum);
+ }
+ }
+ // A little imperfection:
+ // At the end of a line of any multi-line paragraph, we display the
+ // position of the next line of the same paragraph, if there is one.
+ sal_uInt16 nParaLine = 0;
+ sal_uLong nParaLineCount = mpTextEditOutliner->GetLineCount(aSel.nEndPara);
+ bool bBrk = false;
+ while (!bBrk)
+ {
+ sal_uInt16 nLen = mpTextEditOutliner->GetLineLen(aSel.nEndPara, nParaLine);
+ bool bLastLine = (nParaLine == nParaLineCount - 1);
+ if (nCol>nLen || (!bLastLine && nCol == nLen))
+ {
+ nCol -= nLen;
+ nLin++;
+ nParaLine++;
+ }
+ else
+ bBrk = true;
+
+ if (nLen == 0)
+ bBrk = true; // to be sure
+ }
+
+ aStr = aStr.replaceFirst("%1", OUString::number(nPar + 1));
+ aStr = aStr.replaceFirst("%2", OUString::number(nLin + 1));
+ aStr = aStr.replaceFirst("%3", OUString::number(nCol + 1));
+
+#ifdef DBG_UTIL
+ aStr += ", Level " + OUString::number(mpTextEditOutliner->GetDepth( aSel.nEndPara ));
+#endif
+ }
+
+ if(aStr == STR_NOTHING)
+ {
+ if (AreObjectsMarked()) {
+ aStr = ImpGetDescriptionString(STR_ViewMarked);
+ if (IsGluePointEditMode()) {
+ if (HasMarkedGluePoints()) {
+ aStr = ImpGetDescriptionString(STR_ViewMarked, ImpGetDescriptionOptions::GLUEPOINTS);
+ }
+ } else {
+ if (HasMarkedPoints()) {
+ aStr = ImpGetDescriptionString(STR_ViewMarked, ImpGetDescriptionOptions::POINTS);
+ }
+ }
+ } else {
+ aStr.clear();
+ }
+ }
+ else if(!aName.isEmpty())
+ {
+ aStr = aStr.replaceFirst("%1", aName);
+ }
+
+ if(!aStr.isEmpty())
+ {
+ // capitalize first letter
+ aStr = aStr.replaceAt(0, 1, OUString(aStr[0]).toAsciiUpperCase());
+ }
+ return aStr;
+}
+
+SdrViewContext SdrView::GetContext() const
+{
+ if( IsGluePointEditMode() )
+ return SdrViewContext::GluePointEdit;
+
+ const size_t nMarkCount = GetMarkedObjectCount();
+
+ if( HasMarkablePoints() && !IsFrameHandles() )
+ {
+ bool bPath=true;
+ for( size_t nMarkNum = 0; nMarkNum < nMarkCount && bPath; ++nMarkNum )
+ if (dynamic_cast<const SdrPathObj*>(GetMarkedObjectByIndex(nMarkNum)) == nullptr)
+ bPath=false;
+
+ if( bPath )
+ return SdrViewContext::PointEdit;
+ }
+
+ if( GetMarkedObjectCount() )
+ {
+ bool bGraf = true, bMedia = true, bTable = true;
+
+ for( size_t nMarkNum = 0; nMarkNum < nMarkCount && ( bGraf || bMedia ); ++nMarkNum )
+ {
+ const SdrObject* pMarkObj = GetMarkedObjectByIndex( nMarkNum );
+ DBG_ASSERT( pMarkObj, "SdrView::GetContext(), null pointer in mark list!" );
+
+ if( !pMarkObj )
+ continue;
+
+ if( dynamic_cast<const SdrGrafObj*>( pMarkObj) == nullptr )
+ bGraf = false;
+
+ if( dynamic_cast<const SdrMediaObj*>( pMarkObj) == nullptr )
+ bMedia = false;
+
+ if( dynamic_cast<const sdr::table::SdrTableObj* >( pMarkObj ) == nullptr )
+ bTable = false;
+ }
+
+ if( bGraf )
+ return SdrViewContext::Graphic;
+ else if( bMedia )
+ return SdrViewContext::Media;
+ else if( bTable )
+ return SdrViewContext::Table;
+ }
+
+ return SdrViewContext::Standard;
+}
+
+void SdrView::MarkAll()
+{
+ if (IsTextEdit()) {
+ GetTextEditOutlinerView()->SetSelection(ESelection(0,0,EE_PARA_ALL,EE_TEXTPOS_ALL));
+ } else if (IsGluePointEditMode()) MarkAllGluePoints();
+ else if (HasMarkablePoints()) MarkAllPoints();
+ else {
+ // check for table
+ bool bMarkAll = true;
+ const SdrMarkList& rMarkList = GetMarkedObjectList();
+ if (rMarkList.GetMarkCount() == 1)
+ {
+ const SdrObject* pObj(rMarkList.GetMark(0)->GetMarkedSdrObj());
+ SdrView* pView = this;
+ if (pObj && pView && (pObj->GetObjInventor() == SdrInventor::Default)
+ && (pObj->GetObjIdentifier() == SdrObjKind::Table))
+ {
+ mxSelectionController.clear();
+ mxSelectionController = sdr::table::CreateTableController(
+ *pView, static_cast<const sdr::table::SdrTableObj&>(*pObj),
+ mxLastSelectionController);
+
+ if (mxSelectionController.is())
+ {
+ mxLastSelectionController.clear();
+ mxSelectionController->onSelectAll();
+ bMarkAll = false;
+ }
+ }
+ }
+ if ( bMarkAll )
+ MarkAllObj();
+ }
+}
+
+void SdrView::UnmarkAll()
+{
+ if (IsTextEdit()) {
+ ESelection eSel=GetTextEditOutlinerView()->GetSelection();
+ eSel.nStartPara=eSel.nEndPara;
+ eSel.nStartPos=eSel.nEndPos;
+ GetTextEditOutlinerView()->SetSelection(eSel);
+ } else if (HasMarkedGluePoints()) UnmarkAllGluePoints();
+ else if (HasMarkedPoints()) UnmarkAllPoints(); // Marked, not Markable!
+ else UnmarkAllObj();
+}
+
+const tools::Rectangle& SdrView::GetMarkedRect() const
+{
+ if (IsGluePointEditMode() && HasMarkedGluePoints()) {
+ return GetMarkedGluePointsRect();
+ }
+ if (HasMarkedPoints()) {
+ return GetMarkedPointsRect();
+ }
+ return GetMarkedObjRect();
+}
+
+void SdrView::DeleteMarked()
+{
+ if (IsTextEdit())
+ {
+ SdrObjEditView::KeyInput(KeyEvent(0, vcl::KeyCode(KeyFuncType::DELETE)), mpTextEditWin);
+ }
+ else
+ {
+ if( mxSelectionController.is() && mxSelectionController->DeleteMarked() )
+ {
+ // action already performed by current selection controller, do nothing
+ }
+ else if (IsGluePointEditMode() && HasMarkedGluePoints())
+ {
+ DeleteMarkedGluePoints();
+ }
+ else if (GetContext()==SdrViewContext::PointEdit && HasMarkedPoints())
+ {
+ DeleteMarkedPoints();
+ }
+ else
+ {
+ DeleteMarkedObj();
+ }
+ }
+}
+
+bool SdrView::BegMark(const Point& rPnt, bool bAddMark, bool bUnmark)
+{
+ if (bUnmark) bAddMark=true;
+ if (IsGluePointEditMode()) {
+ if (!bAddMark) UnmarkAllGluePoints();
+ return BegMarkGluePoints(rPnt,bUnmark);
+ } else if (HasMarkablePoints()) {
+ if (!bAddMark) UnmarkAllPoints();
+ return BegMarkPoints(rPnt,bUnmark);
+ } else {
+ if (!bAddMark) UnmarkAllObj();
+ BegMarkObj(rPnt,bUnmark);
+ return true;
+ }
+}
+
+bool SdrView::MoveShapeHandle(const sal_uInt32 handleNum, const Point& aEndPoint, const sal_Int32 aObjectOrdNum)
+{
+ if (GetHdlList().IsMoveOutside())
+ return false;
+
+ if (!GetMarkedObjectList().GetMarkCount())
+ return false;
+
+ SdrHdl * pHdl = GetHdlList().GetHdl(handleNum);
+ if (pHdl == nullptr)
+ return false;
+
+ SdrDragStat& rDragStat = const_cast<SdrDragStat&>(GetDragStat());
+ // start dragging
+ BegDragObj(pHdl->GetPos(), nullptr, pHdl, 0);
+ if (!IsDragObj())
+ return false;
+
+ bool bWasNoSnap = rDragStat.IsNoSnap();
+ bool bWasSnapEnabled = IsSnapEnabled();
+
+ // switch snapping off
+ if(!bWasNoSnap)
+ rDragStat.SetNoSnap();
+ if(bWasSnapEnabled)
+ SetSnapEnabled(false);
+
+ if (aObjectOrdNum != -1)
+ {
+ rDragStat.GetGlueOptions().objectOrdNum = aObjectOrdNum;
+ }
+ MovDragObj(aEndPoint);
+ EndDragObj();
+
+ // Clear Glue Options
+ rDragStat.GetGlueOptions().objectOrdNum = -1;
+
+ if (!bWasNoSnap)
+ rDragStat.SetNoSnap(bWasNoSnap);
+ if (bWasSnapEnabled)
+ SetSnapEnabled(bWasSnapEnabled);
+
+ return true;
+}
+
+void SdrView::ConfigurationChanged( ::utl::ConfigurationBroadcaster*p, ConfigurationHints nHint)
+{
+ onAccessibilityOptionsChanged();
+ SdrCreateView::ConfigurationChanged(p, nHint);
+}
+
+
+/** method is called whenever the global SvtAccessibilityOptions is changed */
+void SdrView::onAccessibilityOptionsChanged()
+{
+}
+
+void SdrView::SetMasterPagePaintCaching(bool bOn)
+{
+ if(mbMasterPagePaintCaching == bOn)
+ return;
+
+ mbMasterPagePaintCaching = bOn;
+
+ // reset at all SdrPageWindows
+ SdrPageView* pPageView = GetSdrPageView();
+
+ if(!pPageView)
+ return;
+
+ for(sal_uInt32 b(0); b < pPageView->PageWindowCount(); b++)
+ {
+ SdrPageWindow* pPageWindow = pPageView->GetPageWindow(b);
+ assert(pPageWindow && "SdrView::SetMasterPagePaintCaching: Corrupt SdrPageWindow list (!)");
+
+ // force deletion of ObjectContact, so at re-display all VOCs
+ // will be re-created with updated flag setting
+ pPageWindow->ResetObjectContact();
+ }
+
+ // force redraw of this view
+ pPageView->InvalidateAllWin();
+}
+
+// Default ObjectContact is ObjectContactOfPageView
+sdr::contact::ObjectContact* SdrView::createViewSpecificObjectContact(
+ SdrPageWindow& rPageWindow,
+ const char* pDebugName) const
+{
+ return new sdr::contact::ObjectContactOfPageView(rPageWindow, pDebugName);
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/svdraw/svdviter.cxx b/svx/source/svdraw/svdviter.cxx
new file mode 100644
index 000000000..ff2d6da5f
--- /dev/null
+++ b/svx/source/svdraw/svdviter.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/svdviter.hxx>
+#include <svx/svdobj.hxx>
+#include <svx/svdpage.hxx>
+#include <svx/svdmodel.hxx>
+#include <svx/svdview.hxx>
+#include <svx/svdpagv.hxx>
+#include <svx/svdsob.hxx>
+
+void SdrViewIter::ImpInitVars()
+{
+ mnListenerNum = 0;
+ mpCurrentView = nullptr;
+}
+
+SdrViewIter::SdrViewIter(const SdrPage* pPage)
+{
+ mpPage = pPage;
+ mpModel = pPage ? &pPage->getSdrModelFromSdrPage() : nullptr;
+ mpObject = nullptr;
+ ImpInitVars();
+}
+
+SdrViewIter::SdrViewIter(const SdrObject* pObject)
+{
+ mpObject = pObject;
+ mpModel = pObject ? &pObject->getSdrModelFromSdrObject() : nullptr;
+ mpPage = pObject ? pObject->getSdrPageFromSdrObject() : nullptr;
+
+ if (!mpModel || !mpPage)
+ {
+ mpModel = nullptr;
+ mpPage = nullptr;
+ }
+
+ ImpInitVars();
+}
+
+bool SdrViewIter::ImpCheckPageView(SdrPageView const* pPV) const
+{
+ if (!mpPage)
+ return true;
+
+ bool bMaster(mpPage->IsMasterPage());
+ SdrPage* pPg = pPV->GetPage();
+
+ if (pPg == mpPage)
+ {
+ if (mpObject)
+ {
+ // Looking for an object? First, determine if it visible in
+ // this PageView.
+ SdrLayerIDSet aObjLay;
+ mpObject->getMergedHierarchySdrLayerIDSet(aObjLay);
+ aObjLay &= pPV->GetVisibleLayers();
+ return !aObjLay.IsEmpty();
+ }
+ else
+ {
+ return true;
+ }
+ }
+ else if (bMaster && (!mpObject || !mpObject->IsNotVisibleAsMaster()))
+ {
+ if (pPg->TRG_HasMasterPage())
+ {
+ SdrPage& rMasterPage = pPg->TRG_GetMasterPage();
+
+ if (&rMasterPage == mpPage)
+ {
+ // the page we're looking for is a master page in this PageView
+ if (mpObject)
+ {
+ // Looking for an object? First, determine if it visible in
+ // this PageView.
+ SdrLayerIDSet aObjLay;
+ mpObject->getMergedHierarchySdrLayerIDSet(aObjLay);
+ aObjLay &= pPV->GetVisibleLayers();
+ aObjLay &= pPg->TRG_GetMasterPageVisibleLayers();
+
+ if (!aObjLay.IsEmpty())
+ {
+ return true;
+ } // else, look at the next master page of this page...
+ }
+ else
+ {
+ return true;
+ }
+ }
+ }
+ }
+
+ // master page forbidden or no fitting master page found
+ return false;
+}
+
+SdrView* SdrViewIter::ImpFindView()
+{
+ if (mpModel)
+ {
+ const size_t nLsCnt(mpModel->GetSizeOfVector());
+
+ while (mnListenerNum < nLsCnt)
+ {
+ SfxListener* pLs = mpModel->GetListener(mnListenerNum);
+ mpCurrentView = dynamic_cast<SdrView*>(pLs);
+
+ if (mpCurrentView)
+ {
+ if (mpPage)
+ {
+ SdrPageView* pPV = mpCurrentView->GetSdrPageView();
+
+ if (pPV && ImpCheckPageView(pPV))
+ {
+ return mpCurrentView;
+ }
+ }
+ else
+ {
+ return mpCurrentView;
+ }
+ }
+
+ mnListenerNum++;
+ }
+ }
+
+ mpCurrentView = nullptr;
+ return mpCurrentView;
+}
+
+SdrView* SdrViewIter::FirstView()
+{
+ ImpInitVars();
+ return ImpFindView();
+}
+
+SdrView* SdrViewIter::NextView()
+{
+ mnListenerNum++;
+ return ImpFindView();
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/svdraw/svdxcgv.cxx b/svx/source/svdraw/svdxcgv.cxx
new file mode 100644
index 000000000..a9b720f30
--- /dev/null
+++ b/svx/source/svdraw/svdxcgv.cxx
@@ -0,0 +1,784 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <vector>
+#include <unordered_set>
+#include <editeng/editdata.hxx>
+#include <rtl/strbuf.hxx>
+#include <svx/xfillit0.hxx>
+#include <svx/xlineit0.hxx>
+#include <svx/svdxcgv.hxx>
+#include <svx/svdoutl.hxx>
+#include <svx/svdundo.hxx>
+#include <svx/svdograf.hxx>
+#include <svx/svdoole2.hxx>
+#include <svx/svdorect.hxx>
+#include <svx/svdopage.hxx>
+#include <svx/svdpage.hxx>
+#include <svx/svdpagv.hxx>
+#include <svx/svdtrans.hxx>
+#include <svx/strings.hrc>
+#include <svx/dialmgr.hxx>
+#include <tools/bigint.hxx>
+#include <clonelist.hxx>
+#include <vcl/virdev.hxx>
+#include <svl/style.hxx>
+#include <fmobj.hxx>
+#include <vcl/vectorgraphicdata.hxx>
+#include <drawinglayer/primitive2d/groupprimitive2d.hxx>
+#include <drawinglayer/geometry/viewinformation2d.hxx>
+#include <drawinglayer/converters.hxx>
+#include <svx/sdr/contact/viewcontact.hxx>
+#include <sdr/contact/objectcontactofobjlistpainter.hxx>
+#include <svx/sdr/contact/displayinfo.hxx>
+#include <svx/svdotable.hxx>
+#include <sal/log.hxx>
+#include <osl/diagnose.h>
+#include <comphelper/lok.hxx>
+
+using namespace com::sun::star;
+
+SdrExchangeView::SdrExchangeView(
+ SdrModel& rSdrModel,
+ OutputDevice* pOut)
+: SdrObjEditView(rSdrModel, pOut)
+{
+}
+
+bool SdrExchangeView::ImpLimitToWorkArea(Point& rPt) const
+{
+ bool bRet(false);
+
+ if(!maMaxWorkArea.IsEmpty())
+ {
+ if(rPt.X()<maMaxWorkArea.Left())
+ {
+ rPt.setX( maMaxWorkArea.Left() );
+ bRet = true;
+ }
+
+ if(rPt.X()>maMaxWorkArea.Right())
+ {
+ rPt.setX( maMaxWorkArea.Right() );
+ bRet = true;
+ }
+
+ if(rPt.Y()<maMaxWorkArea.Top())
+ {
+ rPt.setY( maMaxWorkArea.Top() );
+ bRet = true;
+ }
+
+ if(rPt.Y()>maMaxWorkArea.Bottom())
+ {
+ rPt.setY( maMaxWorkArea.Bottom() );
+ bRet = true;
+ }
+ }
+ return bRet;
+}
+
+void SdrExchangeView::ImpGetPasteObjList(Point& /*rPos*/, SdrObjList*& rpLst)
+{
+ if (rpLst==nullptr)
+ {
+ SdrPageView* pPV = GetSdrPageView();
+
+ if (pPV!=nullptr) {
+ rpLst=pPV->GetObjList();
+ }
+ }
+}
+
+bool SdrExchangeView::ImpGetPasteLayer(const SdrObjList* pObjList, SdrLayerID& rLayer) const
+{
+ bool bRet=false;
+ rLayer=SdrLayerID(0);
+ if (pObjList!=nullptr) {
+ const SdrPage* pPg=pObjList->getSdrPageFromSdrObjList();
+ if (pPg!=nullptr) {
+ rLayer=pPg->GetLayerAdmin().GetLayerID(maActualLayer);
+ if (rLayer==SDRLAYER_NOTFOUND) rLayer=SdrLayerID(0);
+ SdrPageView* pPV = GetSdrPageView();
+ if (pPV!=nullptr) {
+ bRet=!pPV->GetLockedLayers().IsSet(rLayer) && pPV->GetVisibleLayers().IsSet(rLayer);
+ }
+ }
+ }
+ return bRet;
+}
+
+bool SdrExchangeView::Paste(const OUString& rStr, const Point& rPos, SdrObjList* pLst, SdrInsertFlags nOptions)
+{
+ if (rStr.isEmpty())
+ return false;
+
+ Point aPos(rPos);
+ ImpGetPasteObjList(aPos,pLst);
+ ImpLimitToWorkArea( aPos );
+ if (pLst==nullptr) return false;
+ SdrLayerID nLayer;
+ if (!ImpGetPasteLayer(pLst,nLayer)) return false;
+ bool bUnmark = (nOptions & (SdrInsertFlags::DONTMARK|SdrInsertFlags::ADDMARK))==SdrInsertFlags::NONE && !IsTextEdit();
+ if (bUnmark) UnmarkAllObj();
+ tools::Rectangle aTextRect(0,0,500,500);
+ SdrPage* pPage=pLst->getSdrPageFromSdrObjList();
+ if (pPage!=nullptr) {
+ aTextRect.SetSize(pPage->GetSize());
+ }
+ SdrRectObj* pObj = new SdrRectObj(
+ getSdrModelFromSdrView(),
+ SdrObjKind::Text,
+ aTextRect);
+
+ pObj->SetLayer(nLayer);
+ pObj->NbcSetText(rStr); // SetText before SetAttr, else SetAttr doesn't work!
+ if (mpDefaultStyleSheet!=nullptr) pObj->NbcSetStyleSheet(mpDefaultStyleSheet, false);
+
+ pObj->SetMergedItemSet(maDefaultAttr);
+
+ SfxItemSet aTempAttr(mpModel->GetItemPool()); // no fill, no line
+ aTempAttr.Put(XLineStyleItem(drawing::LineStyle_NONE));
+ aTempAttr.Put(XFillStyleItem(drawing::FillStyle_NONE));
+
+ pObj->SetMergedItemSet(aTempAttr);
+
+ pObj->FitFrameToTextSize();
+ Size aSiz(pObj->GetLogicRect().GetSize());
+ MapUnit eMap=mpModel->GetScaleUnit();
+ Fraction aMap=mpModel->GetScaleFraction();
+ ImpPasteObject(pObj,*pLst,aPos,aSiz,MapMode(eMap,Point(0,0),aMap,aMap),nOptions);
+ return true;
+}
+
+bool SdrExchangeView::Paste(SvStream& rInput, EETextFormat eFormat, const Point& rPos, SdrObjList* pLst, SdrInsertFlags nOptions)
+{
+ Point aPos(rPos);
+ ImpGetPasteObjList(aPos,pLst);
+ ImpLimitToWorkArea( aPos );
+ if (pLst==nullptr) return false;
+ SdrLayerID nLayer;
+ if (!ImpGetPasteLayer(pLst,nLayer)) return false;
+ bool bUnmark=(nOptions&(SdrInsertFlags::DONTMARK|SdrInsertFlags::ADDMARK))==SdrInsertFlags::NONE && !IsTextEdit();
+ if (bUnmark) UnmarkAllObj();
+ tools::Rectangle aTextRect(0,0,500,500);
+ SdrPage* pPage=pLst->getSdrPageFromSdrObjList();
+ if (pPage!=nullptr) {
+ aTextRect.SetSize(pPage->GetSize());
+ }
+ SdrRectObj* pObj = new SdrRectObj(
+ getSdrModelFromSdrView(),
+ SdrObjKind::Text,
+ aTextRect);
+
+ pObj->SetLayer(nLayer);
+ if (mpDefaultStyleSheet!=nullptr) pObj->NbcSetStyleSheet(mpDefaultStyleSheet, false);
+
+ pObj->SetMergedItemSet(maDefaultAttr);
+
+ SfxItemSet aTempAttr(mpModel->GetItemPool()); // no fill, no line
+ aTempAttr.Put(XLineStyleItem(drawing::LineStyle_NONE));
+ aTempAttr.Put(XFillStyleItem(drawing::FillStyle_NONE));
+
+ pObj->SetMergedItemSet(aTempAttr);
+
+ pObj->NbcSetText(rInput,OUString(),eFormat);
+ pObj->FitFrameToTextSize();
+ Size aSiz(pObj->GetLogicRect().GetSize());
+ MapUnit eMap=mpModel->GetScaleUnit();
+ Fraction aMap=mpModel->GetScaleFraction();
+ ImpPasteObject(pObj,*pLst,aPos,aSiz,MapMode(eMap,Point(0,0),aMap,aMap),nOptions);
+
+ // b4967543
+ if(pObj->GetOutlinerParaObject())
+ {
+ SdrOutliner& rOutliner = pObj->getSdrModelFromSdrObject().GetHitTestOutliner();
+ rOutliner.SetText(*pObj->GetOutlinerParaObject());
+
+ if(1 == rOutliner.GetParagraphCount())
+ {
+ SfxStyleSheet* pCandidate = rOutliner.GetStyleSheet(0);
+
+ if(pCandidate)
+ {
+ if(pObj->getSdrModelFromSdrObject().GetStyleSheetPool() == pCandidate->GetPool())
+ {
+ pObj->NbcSetStyleSheet(pCandidate, true);
+ }
+ }
+ }
+ }
+
+ return true;
+}
+
+bool SdrExchangeView::Paste(
+ const SdrModel& rMod, const Point& rPos, SdrObjList* pLst, SdrInsertFlags nOptions)
+{
+ const SdrModel* pSrcMod=&rMod;
+ if (pSrcMod==mpModel)
+ return false; // this can't work, right?
+
+ const bool bUndo = IsUndoEnabled();
+
+ if( bUndo )
+ BegUndo(SvxResId(STR_ExchangePaste));
+
+ if( mxSelectionController.is() && mxSelectionController->PasteObjModel( rMod ) )
+ {
+ if( bUndo )
+ EndUndo();
+ return true;
+ }
+
+ Point aPos(rPos);
+ ImpGetPasteObjList(aPos,pLst);
+ SdrPageView* pMarkPV=nullptr;
+ SdrPageView* pPV = GetSdrPageView();
+
+ if(pPV && pPV->GetObjList() == pLst )
+ pMarkPV=pPV;
+
+ ImpLimitToWorkArea( aPos );
+ if (pLst==nullptr)
+ return false;
+
+ bool bUnmark=(nOptions&(SdrInsertFlags::DONTMARK|SdrInsertFlags::ADDMARK))==SdrInsertFlags::NONE && !IsTextEdit();
+ if (bUnmark)
+ UnmarkAllObj();
+
+ // Rescale, if the Model uses a different MapUnit.
+ // Calculate the necessary factors first.
+ MapUnit eSrcUnit=pSrcMod->GetScaleUnit();
+ MapUnit eDstUnit=mpModel->GetScaleUnit();
+ bool bResize=eSrcUnit!=eDstUnit;
+ Fraction aXResize,aYResize;
+ Point aPt0;
+ if (bResize)
+ {
+ FrPair aResize(GetMapFactor(eSrcUnit,eDstUnit));
+ aXResize=aResize.X();
+ aYResize=aResize.Y();
+ }
+ SdrObjList* pDstLst=pLst;
+ sal_uInt16 nPg,nPgCount=pSrcMod->GetPageCount();
+ for (nPg=0; nPg<nPgCount; nPg++)
+ {
+ const SdrPage* pSrcPg=pSrcMod->GetPage(nPg);
+
+ // Use SnapRect, not BoundRect here
+ tools::Rectangle aR=pSrcPg->GetAllObjSnapRect();
+
+ if (bResize)
+ ResizeRect(aR,aPt0,aXResize,aYResize);
+ Point aDist(aPos-aR.Center());
+ Size aSiz(aDist.X(),aDist.Y());
+ size_t nCloneErrCnt = 0;
+ const size_t nObjCount = pSrcPg->GetObjCount();
+ bool bMark = pMarkPV!=nullptr && !IsTextEdit() && (nOptions&SdrInsertFlags::DONTMARK)==SdrInsertFlags::NONE;
+
+ // #i13033#
+ // New mechanism to re-create the connections of cloned connectors
+ CloneList aCloneList;
+ std::unordered_set<rtl::OUString> aNameSet;
+ for (size_t nOb=0; nOb<nObjCount; ++nOb)
+ {
+ const SdrObject* pSrcOb=pSrcPg->GetObj(nOb);
+
+ SdrObject* pNewObj(pSrcOb->CloneSdrObject(*mpModel));
+
+ if (pNewObj!=nullptr)
+ {
+ if(bResize)
+ {
+ pNewObj->getSdrModelFromSdrObject().SetPasteResize(true);
+ pNewObj->NbcResize(aPt0,aXResize,aYResize);
+ pNewObj->getSdrModelFromSdrObject().SetPasteResize(false);
+ }
+
+ // #i39861#
+ pNewObj->NbcMove(aSiz);
+
+ const SdrPage* pPg = pDstLst->getSdrPageFromSdrObjList();
+
+ if(pPg)
+ {
+ // #i72535#
+ const SdrLayerAdmin& rAd = pPg->GetLayerAdmin();
+ SdrLayerID nLayer(0);
+
+ if(dynamic_cast<const FmFormObj*>( pNewObj) != nullptr)
+ {
+ // for FormControls, force to form layer
+ nLayer = rAd.GetLayerID(rAd.GetControlLayerName());
+ }
+ else
+ {
+ nLayer = rAd.GetLayerID(maActualLayer);
+ }
+
+ if(SDRLAYER_NOTFOUND == nLayer)
+ {
+ nLayer = SdrLayerID(0);
+ }
+
+ pNewObj->SetLayer(nLayer);
+ }
+
+ pDstLst->InsertObjectThenMakeNameUnique(pNewObj, aNameSet);
+
+ if( bUndo )
+ AddUndo(getSdrModelFromSdrView().GetSdrUndoFactory().CreateUndoNewObject(*pNewObj));
+
+ if (bMark) {
+ // Don't already set Markhandles!
+ // That is instead being done by ModelHasChanged in MarkView.
+ MarkObj(pNewObj,pMarkPV,false,true);
+ }
+
+ // #i13033#
+ aCloneList.AddPair(pSrcOb, pNewObj);
+ }
+ else
+ {
+ nCloneErrCnt++;
+ }
+ }
+
+ // #i13033#
+ // New mechanism to re-create the connections of cloned connectors
+ aCloneList.CopyConnections();
+
+ if(0 != nCloneErrCnt)
+ {
+#ifdef DBG_UTIL
+ OStringBuffer aStr("SdrExchangeView::Paste(): Error when cloning ");
+
+ if(nCloneErrCnt == 1)
+ {
+ aStr.append("a drawing object.");
+ }
+ else
+ {
+ aStr.append(static_cast<sal_Int32>(nCloneErrCnt));
+ aStr.append(" drawing objects.");
+ }
+
+ aStr.append(" Not copying object connectors.");
+
+ OSL_FAIL(aStr.getStr());
+#endif
+ }
+ }
+
+ if( bUndo )
+ EndUndo();
+
+ return true;
+}
+
+void SdrExchangeView::ImpPasteObject(SdrObject* pObj, SdrObjList& rLst, const Point& rCenter, const Size& rSiz, const MapMode& rMap, SdrInsertFlags nOptions)
+{
+ BigInt nSizX(rSiz.Width());
+ BigInt nSizY(rSiz.Height());
+ MapUnit eSrcMU=rMap.GetMapUnit();
+ MapUnit eDstMU=mpModel->GetScaleUnit();
+ FrPair aMapFact(GetMapFactor(eSrcMU,eDstMU));
+ Fraction aDstFr(mpModel->GetScaleFraction());
+ nSizX *= double(aMapFact.X() * rMap.GetScaleX() * aDstFr);
+ nSizX *= aDstFr.GetDenominator();
+ nSizY *= double(aMapFact.Y() * rMap.GetScaleY());
+ nSizY /= aDstFr.GetNumerator();
+ tools::Long xs=nSizX;
+ tools::Long ys=nSizY;
+ // set the pos to 0, 0 for online case
+ bool isLOK = comphelper::LibreOfficeKit::isActive();
+ Point aPos(isLOK ? 0 : rCenter.X()-xs/2, isLOK ? 0 : rCenter.Y()-ys/2);
+ tools::Rectangle aR(aPos.X(),aPos.Y(),aPos.X()+xs,aPos.Y()+ys);
+ pObj->SetLogicRect(aR);
+ rLst.InsertObject(pObj, SAL_MAX_SIZE);
+
+ if( IsUndoEnabled() )
+ AddUndo(getSdrModelFromSdrView().GetSdrUndoFactory().CreateUndoNewObject(*pObj));
+
+ SdrPageView* pMarkPV=nullptr;
+ SdrPageView* pPV = GetSdrPageView();
+
+ if(pPV && pPV->GetObjList()==&rLst)
+ pMarkPV=pPV;
+
+ bool bMark = pMarkPV!=nullptr && !IsTextEdit() && (nOptions&SdrInsertFlags::DONTMARK)==SdrInsertFlags::NONE;
+ if (bMark)
+ { // select object the first PageView we found
+ MarkObj(pObj,pMarkPV);
+ }
+}
+
+BitmapEx SdrExchangeView::GetMarkedObjBitmapEx(bool bNoVDevIfOneBmpMarked, const sal_uInt32 nMaximumQuadraticPixels, const std::optional<Size>& rTargetDPI) const
+{
+ BitmapEx aBmp;
+
+ if( AreObjectsMarked() )
+ {
+ if(1 == GetMarkedObjectCount())
+ {
+ if(bNoVDevIfOneBmpMarked)
+ {
+ SdrObject* pGrafObjTmp = GetMarkedObjectByIndex( 0 );
+ SdrGrafObj* pGrafObj = dynamic_cast<SdrGrafObj*>( pGrafObjTmp );
+
+ if( pGrafObj && ( pGrafObj->GetGraphicType() == GraphicType::Bitmap ) )
+ {
+ aBmp = pGrafObj->GetTransformedGraphic().GetBitmapEx();
+ }
+ }
+ else
+ {
+ const SdrGrafObj* pSdrGrafObj = dynamic_cast< const SdrGrafObj* >(GetMarkedObjectByIndex(0));
+
+ if(pSdrGrafObj && pSdrGrafObj->isEmbeddedVectorGraphicData())
+ {
+ aBmp = pSdrGrafObj->GetGraphic().getVectorGraphicData()->getReplacement();
+ }
+ }
+ }
+
+ if( aBmp.IsEmpty() )
+ {
+ // choose conversion directly using primitives to bitmap to avoid
+ // rendering errors with tiled bitmap fills (these will be tiled in a
+ // in-between metafile, but tend to show 'gaps' since the target is *no*
+ // bitmap rendering)
+ ::std::vector< SdrObject* > aSdrObjects(GetMarkedObjects());
+ const sal_uInt32 nCount(aSdrObjects.size());
+
+ if(nCount)
+ {
+ // collect sub-primitives as group objects, thus no expensive append
+ // to existing sequence is needed
+ drawinglayer::primitive2d::Primitive2DContainer xPrimitives(nCount);
+
+ for(sal_uInt32 a(0); a < nCount; a++)
+ {
+ SdrObject* pCandidate = aSdrObjects[a];
+ SdrGrafObj* pSdrGrafObj = dynamic_cast< SdrGrafObj* >(pCandidate);
+
+ if(pSdrGrafObj)
+ {
+ // #122753# To ensure existence of graphic content, force swap in
+ pSdrGrafObj->ForceSwapIn();
+ }
+
+ drawinglayer::primitive2d::Primitive2DContainer xRetval;
+ pCandidate->GetViewContact().getViewIndependentPrimitive2DContainer(xRetval);
+ xPrimitives[a] = new drawinglayer::primitive2d::GroupPrimitive2D(
+ std::move(xRetval));
+ }
+
+ // get logic range
+ const drawinglayer::geometry::ViewInformation2D aViewInformation2D;
+ const basegfx::B2DRange aRange(xPrimitives.getB2DRange(aViewInformation2D));
+
+ if(!aRange.isEmpty())
+ {
+ o3tl::Length eRangeUnit = o3tl::Length::mm100;
+
+ if (GetModel()->IsWriter())
+ {
+ eRangeUnit = o3tl::Length::twip;
+ }
+
+ // if we have geometry and it has a range, convert to BitmapEx using
+ // common tooling
+ aBmp = drawinglayer::convertPrimitive2DContainerToBitmapEx(
+ std::move(xPrimitives),
+ aRange,
+ nMaximumQuadraticPixels,
+ eRangeUnit,
+ rTargetDPI);
+ }
+ }
+ }
+ }
+
+ return aBmp;
+}
+
+
+GDIMetaFile SdrExchangeView::GetMarkedObjMetaFile(bool bNoVDevIfOneMtfMarked) const
+{
+ GDIMetaFile aMtf;
+
+ if( AreObjectsMarked() )
+ {
+ tools::Rectangle aBound( GetMarkedObjBoundRect() );
+ Size aBoundSize( aBound.GetWidth(), aBound.GetHeight() );
+ MapMode aMap( mpModel->GetScaleUnit(), Point(), mpModel->GetScaleFraction(), mpModel->GetScaleFraction() );
+
+ if( bNoVDevIfOneMtfMarked )
+ {
+ SdrObject* pGrafObjTmp = GetMarkedObjectByIndex( 0 );
+ SdrGrafObj* pGrafObj = ( GetMarkedObjectCount() ==1 ) ? dynamic_cast<SdrGrafObj*>( pGrafObjTmp ) : nullptr;
+
+ if( pGrafObj )
+ {
+ Graphic aGraphic( pGrafObj->GetTransformedGraphic() );
+
+ // #119735# just use GetGDIMetaFile, it will create a buffered version of contained bitmap now automatically
+ aMtf = aGraphic.GetGDIMetaFile();
+ }
+ }
+
+ if( !aMtf.GetActionSize() )
+ {
+ ScopedVclPtrInstance< VirtualDevice > pOut;
+ const Size aDummySize(2, 2);
+
+ pOut->SetOutputSizePixel(aDummySize);
+ pOut->EnableOutput(false);
+ pOut->SetMapMode(aMap);
+ aMtf.Clear();
+ aMtf.Record(pOut);
+
+ DrawMarkedObj(*pOut);
+
+ aMtf.Stop();
+ aMtf.WindStart();
+
+ // moving the result is more reliable then setting a relative MapMode at the VDev (used
+ // before), also see #i99268# in GetObjGraphic() below. Some draw actions at
+ // the OutDev are simply not handled correctly when a MapMode is set at the
+ // target device, e.g. MetaFloatTransparentAction. Even the Move for this action
+ // was missing the manipulation of the embedded Metafile
+ aMtf.Move(-aBound.Left(), -aBound.Top());
+
+ aMtf.SetPrefMapMode( aMap );
+
+ // removed PrefSize extension. It is principally wrong to set a reduced size at
+ // the created MetaFile. The mentioned errors occur at output time since the integer
+ // MapModes from VCL lead to errors. It is now corrected in the VCLRenderer for
+ // primitives (and may later be done in breaking up a MetaFile to primitives)
+ aMtf.SetPrefSize(aBoundSize);
+ }
+ }
+
+ return aMtf;
+}
+
+
+Graphic SdrExchangeView::GetAllMarkedGraphic() const
+{
+ Graphic aRet;
+
+ if( AreObjectsMarked() )
+ {
+ if( ( 1 == GetMarkedObjectCount() ) && GetSdrMarkByIndex( 0 ) )
+ aRet = SdrExchangeView::GetObjGraphic(*GetMarkedObjectByIndex(0));
+ else
+ aRet = GetMarkedObjMetaFile();
+ }
+
+ return aRet;
+}
+
+
+Graphic SdrExchangeView::GetObjGraphic(const SdrObject& rSdrObject)
+{
+ Graphic aRet;
+
+ // try to get a graphic from the object first
+ const SdrGrafObj* pSdrGrafObj(dynamic_cast< const SdrGrafObj* >(&rSdrObject));
+ const SdrOle2Obj* pSdrOle2Obj(dynamic_cast< const SdrOle2Obj* >(&rSdrObject));
+
+ if(pSdrGrafObj)
+ {
+ if(pSdrGrafObj->isEmbeddedVectorGraphicData())
+ {
+ // get Metafile for Svg content
+ aRet = pSdrGrafObj->getMetafileFromEmbeddedVectorGraphicData();
+ }
+ else
+ {
+ // Make behaviour coherent with metafile
+ // recording below (which of course also takes
+ // view-transformed objects)
+ aRet = pSdrGrafObj->GetTransformedGraphic();
+ }
+ }
+ else if(pSdrOle2Obj)
+ {
+ if(pSdrOle2Obj->GetGraphic())
+ {
+ aRet = *pSdrOle2Obj->GetGraphic();
+ }
+ }
+
+ // if graphic could not be retrieved => go the hard way and create a MetaFile
+ if((GraphicType::NONE == aRet.GetType()) || (GraphicType::Default == aRet.GetType()))
+ {
+ ScopedVclPtrInstance< VirtualDevice > pOut;
+ GDIMetaFile aMtf;
+ const tools::Rectangle aBoundRect(rSdrObject.GetCurrentBoundRect());
+ const MapMode aMap(rSdrObject.getSdrModelFromSdrObject().GetScaleUnit(),
+ Point(),
+ rSdrObject.getSdrModelFromSdrObject().GetScaleFraction(),
+ rSdrObject.getSdrModelFromSdrObject().GetScaleFraction());
+
+ pOut->EnableOutput(false);
+ pOut->SetMapMode(aMap);
+ aMtf.Record(pOut);
+ rSdrObject.SingleObjectPainter(*pOut);
+ aMtf.Stop();
+ aMtf.WindStart();
+
+ // #i99268# replace the original offset from using XOutDev's SetOffset
+ // NOT (as tried with #i92760#) with another MapMode which gets recorded
+ // by the Metafile itself (what always leads to problems), but by
+ // moving the result directly
+ aMtf.Move(-aBoundRect.Left(), -aBoundRect.Top());
+ aMtf.SetPrefMapMode(aMap);
+ aMtf.SetPrefSize(aBoundRect.GetSize());
+
+ if(aMtf.GetActionSize())
+ {
+ aRet = aMtf;
+ }
+ }
+
+ return aRet;
+}
+
+
+::std::vector< SdrObject* > SdrExchangeView::GetMarkedObjects() const
+{
+ SortMarkedObjects();
+ ::std::vector< SdrObject* > aRetval;
+
+ ::std::vector< ::std::vector< SdrMark* > > aObjVectors( 2 );
+ ::std::vector< SdrMark* >& rObjVector1 = aObjVectors[ 0 ];
+ ::std::vector< SdrMark* >& rObjVector2 = aObjVectors[ 1 ];
+ const SdrLayerAdmin& rLayerAdmin = mpModel->GetLayerAdmin();
+ const SdrLayerID nControlLayerId = rLayerAdmin.GetLayerID( rLayerAdmin.GetControlLayerName() );
+
+ for( size_t n = 0, nCount = GetMarkedObjectCount(); n < nCount; ++n )
+ {
+ SdrMark* pMark = GetSdrMarkByIndex( n );
+
+ // paint objects on control layer on top of all other objects
+ if( nControlLayerId == pMark->GetMarkedSdrObj()->GetLayer() )
+ rObjVector2.push_back( pMark );
+ else
+ rObjVector1.push_back( pMark );
+ }
+
+ for(const std::vector<SdrMark*> & rObjVector : aObjVectors)
+ {
+ for(SdrMark* pMark : rObjVector)
+ {
+ aRetval.push_back(pMark->GetMarkedSdrObj());
+ }
+ }
+
+ return aRetval;
+}
+
+
+void SdrExchangeView::DrawMarkedObj(OutputDevice& rOut) const
+{
+ ::std::vector< SdrObject* > aSdrObjects(GetMarkedObjects());
+
+ if(!aSdrObjects.empty())
+ {
+ sdr::contact::ObjectContactOfObjListPainter aPainter(rOut, std::move(aSdrObjects), aSdrObjects[0]->getSdrPageFromSdrObject());
+ sdr::contact::DisplayInfo aDisplayInfo;
+
+ // do processing
+ aPainter.ProcessDisplay(aDisplayInfo);
+ }
+}
+
+std::unique_ptr<SdrModel> SdrExchangeView::CreateMarkedObjModel() const
+{
+ // Sorting the MarkList here might be problematic in the future, so
+ // use a copy.
+ SortMarkedObjects();
+ std::unique_ptr<SdrModel> pNewModel(mpModel->AllocModel());
+ rtl::Reference<SdrPage> pNewPage = pNewModel->AllocPage(false);
+ pNewModel->InsertPage(pNewPage.get());
+ ::std::vector< SdrObject* > aSdrObjects(GetMarkedObjects());
+
+ // #i13033#
+ // New mechanism to re-create the connections of cloned connectors
+ CloneList aCloneList;
+
+ for(SdrObject* pObj : aSdrObjects)
+ {
+ SdrObject* pNewObj(nullptr);
+
+ if(nullptr != dynamic_cast< const SdrPageObj* >(pObj))
+ {
+ // convert SdrPageObj's to a graphic representation, because
+ // virtual connection to referenced page gets lost in new model
+ pNewObj = new SdrGrafObj(
+ *pNewModel,
+ GetObjGraphic(*pObj),
+ pObj->GetLogicRect());
+ }
+ else if(nullptr != dynamic_cast< const sdr::table::SdrTableObj* >(pObj))
+ {
+ // check if we have a valid selection *different* from whole table
+ // being selected
+ if(mxSelectionController.is())
+ {
+ pNewObj = mxSelectionController->GetMarkedSdrObjClone(*pNewModel);
+ }
+ }
+
+ if(nullptr == pNewObj)
+ {
+ // not cloned yet
+ if(pObj->GetObjIdentifier() == SdrObjKind::OLE2 && nullptr == mpModel->GetPersist())
+ {
+ // tdf#125520 - former fix was wrong, the SdrModel
+ // has to have a GetPersist() already, see task.
+ // We can still warn here when this is not the case
+ SAL_WARN( "svx", "OLE gets cloned Persist, EmbeddedObjectContainer will not be copied" );
+ }
+
+ // use default way
+ pNewObj = pObj->CloneSdrObject(*pNewModel);
+ }
+
+ if(pNewObj)
+ {
+ pNewPage->InsertObject(pNewObj, SAL_MAX_SIZE);
+
+ // #i13033#
+ aCloneList.AddPair(pObj, pNewObj);
+ }
+ }
+
+ // #i13033#
+ // New mechanism to re-create the connections of cloned connectors
+ aCloneList.CopyConnections();
+
+ return pNewModel;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/svdraw/textchain.cxx b/svx/source/svdraw/textchain.cxx
new file mode 100644
index 000000000..32c1f9b4e
--- /dev/null
+++ b/svx/source/svdraw/textchain.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 <textchain.hxx>
+#include <svx/svdotext.hxx>
+
+/*
+ * Definition of Properties Interface
+*/
+
+CursorChainingEvent const & TextChain::GetCursorEvent(const SdrTextObj *pTarget)
+{
+ ImpChainLinkProperties *pLinkProperties = GetLinkProperties(pTarget);
+ return pLinkProperties->aCursorEvent;
+}
+void TextChain::SetCursorEvent(const SdrTextObj *pTarget, CursorChainingEvent const & rPropParam)
+{
+ ImpChainLinkProperties *pLinkProperties = GetLinkProperties(pTarget);
+ pLinkProperties->aCursorEvent = rPropParam;
+}
+
+bool TextChain::GetNilChainingEvent(const SdrTextObj *pTarget)
+{
+ ImpChainLinkProperties *pLinkProperties = GetLinkProperties(pTarget);
+ return pLinkProperties->aNilChainingEvent;
+}
+void TextChain::SetNilChainingEvent(const SdrTextObj *pTarget, bool b)
+{
+ ImpChainLinkProperties *pLinkProperties = GetLinkProperties(pTarget);
+ pLinkProperties->aNilChainingEvent = b;
+}
+
+ESelection const & TextChain::GetPreChainingSel(const SdrTextObj *pTarget)
+{
+ ImpChainLinkProperties *pLinkProperties = GetLinkProperties(pTarget);
+ return pLinkProperties->aPreChainingSel;
+}
+void TextChain::SetPreChainingSel(const SdrTextObj *pTarget, ESelection const & rPropParam)
+{
+ ImpChainLinkProperties *pLinkProperties = GetLinkProperties(pTarget);
+ pLinkProperties->aPreChainingSel = rPropParam;
+}
+
+ESelection const & TextChain::GetPostChainingSel(const SdrTextObj *pTarget)
+{
+ ImpChainLinkProperties *pLinkProperties = GetLinkProperties(pTarget);
+ return pLinkProperties->aPostChainingSel;
+}
+void TextChain::SetPostChainingSel(const SdrTextObj *pTarget, ESelection const & rPropParam)
+{
+ ImpChainLinkProperties *pLinkProperties = GetLinkProperties(pTarget);
+ pLinkProperties->aPostChainingSel = rPropParam;
+}
+
+bool TextChain::GetIsPartOfLastParaInNextLink(const SdrTextObj *pTarget)
+{
+ ImpChainLinkProperties *pLinkProperties = GetLinkProperties(pTarget);
+ return pLinkProperties->aIsPartOfLastParaInNextLink;
+}
+void TextChain::SetIsPartOfLastParaInNextLink(const SdrTextObj *pTarget, bool b)
+{
+ ImpChainLinkProperties *pLinkProperties = GetLinkProperties(pTarget);
+ pLinkProperties->aIsPartOfLastParaInNextLink = b;
+}
+
+bool TextChain::GetSwitchingToNextBox(const SdrTextObj *pTarget)
+{
+ ImpChainLinkProperties *pLinkProperties = GetLinkProperties(pTarget);
+ return pLinkProperties->aSwitchingToNextBox;
+}
+void TextChain::SetSwitchingToNextBox(const SdrTextObj *pTarget, bool b)
+{
+ ImpChainLinkProperties *pLinkProperties = GetLinkProperties(pTarget);
+ pLinkProperties->aSwitchingToNextBox = b;
+}
+
+/* End Definition of Properties Interface */
+
+/* TextChain */
+
+// NOTE: All getters in the class assume that the guy is in the chain
+
+TextChain::TextChain()
+{
+}
+
+TextChain::~TextChain()
+{
+ // XXX: Should free all LinkProperties
+}
+
+namespace {
+
+ChainLinkId GetId(const SdrTextObj *pLink)
+{
+ return pLink->GetName();
+}
+
+}
+
+ImpChainLinkProperties *TextChain::GetLinkProperties(const SdrTextObj *pLink)
+{
+ // if the guy does not already have properties in the map make them
+ ChainLinkId aLinkId = GetId(pLink);
+ if (maLinkPropertiesMap.find(aLinkId) == maLinkPropertiesMap.end()) {
+ maLinkPropertiesMap[aLinkId] = new ImpChainLinkProperties;
+ }
+
+ return maLinkPropertiesMap[aLinkId];
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/svdraw/textchaincursor.cxx b/svx/source/svdraw/textchaincursor.cxx
new file mode 100644
index 000000000..51c4d1d8e
--- /dev/null
+++ b/svx/source/svdraw/textchaincursor.cxx
@@ -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 .
+ */
+
+#include <textchain.hxx>
+#include <textchaincursor.hxx>
+#include <svx/svdedxv.hxx>
+#include <svx/svdoutl.hxx>
+#include <vcl/event.hxx>
+
+// XXX: Possible duplication of code in behavior with stuff in ImpEditView (or ImpEditEngine) and OutlinerView
+
+// XXX: We violate Demeter's Law several times here, I'm afraid
+
+TextChainCursorManager::TextChainCursorManager(SdrObjEditView *pEditView, const SdrTextObj *pTextObj) :
+ mpEditView(pEditView),
+ mpTextObj(pTextObj),
+ mbHandlingDel(false)
+{
+ assert(mpEditView);
+ assert(mpTextObj);
+
+}
+
+bool TextChainCursorManager::HandleKeyEvent( const KeyEvent& rKEvt )
+{
+ ESelection aNewSel;
+ CursorChainingEvent aCursorEvent;
+
+ // check what the cursor/event situation looks like
+ bool bCompletelyHandled = false;
+ impDetectEvent(rKEvt, aCursorEvent, aNewSel, bCompletelyHandled);
+
+ if (aCursorEvent == CursorChainingEvent::NULL_EVENT)
+ return false;
+ else {
+ HandleCursorEvent(aCursorEvent, aNewSel);
+ // return value depends on the situation we are in
+ return bCompletelyHandled;
+ }
+}
+
+void TextChainCursorManager::impDetectEvent(const KeyEvent& rKEvt,
+ CursorChainingEvent& rOutCursorEvt,
+ ESelection& rOutSel,
+ bool& rOutHandled)
+{
+ SdrOutliner *pOutl = mpEditView->GetTextEditOutliner();
+ OutlinerView *pOLV = mpEditView->GetTextEditOutlinerView();
+
+ SdrTextObj *pNextLink = mpTextObj->GetNextLinkInChain();
+ SdrTextObj *pPrevLink = mpTextObj->GetPrevLinkInChain();
+
+ KeyFuncType eFunc = rKEvt.GetKeyCode().GetFunction();
+
+ // We need to have this KeyFuncType
+ if (eFunc != KeyFuncType::DONTKNOW && eFunc != KeyFuncType::DELETE)
+ {
+ rOutCursorEvt = CursorChainingEvent::NULL_EVENT;
+ return;
+ }
+
+ sal_uInt16 nCode = rKEvt.GetKeyCode().GetCode();
+ ESelection aCurSel = pOLV->GetSelection();
+
+ ESelection aEndSelPrevBox(100000, 100000);
+
+ sal_Int32 nLastPara = pOutl->GetParagraphCount()-1;
+ OUString aLastParaText = pOutl->GetText(pOutl->GetParagraph(nLastPara));
+ sal_Int32 nLastParaLen = aLastParaText.getLength();
+
+ ESelection aEndSel(nLastPara, nLastParaLen);
+ bool bAtEndOfTextContent = aCurSel == aEndSel;
+
+ // Possibility: Are we "pushing" at the end of the object?
+ if (nCode == KEY_RIGHT && bAtEndOfTextContent && pNextLink)
+ {
+ rOutCursorEvt = CursorChainingEvent::TO_NEXT_LINK;
+ // Selection unchanged: we are at the beginning of the box
+ rOutHandled = true; // Nothing more to do than move cursor
+ return;
+ }
+
+ // Possibility: Are we "pushing" at the end of the object?
+ if (eFunc == KeyFuncType::DELETE && bAtEndOfTextContent && pNextLink)
+ {
+ rOutCursorEvt = CursorChainingEvent::TO_NEXT_LINK;
+ // Selection unchanged: we are at the beginning of the box
+ rOutHandled = false; // We still need to delete the characters
+ mbHandlingDel = true;
+ return;
+ }
+
+ ESelection aStartSel(0, 0);
+ bool bAtStartOfTextContent = aCurSel == aStartSel;
+
+ // Possibility: Are we "pushing" at the start of the object?
+ if (nCode == KEY_LEFT && bAtStartOfTextContent && pPrevLink)
+ {
+ rOutCursorEvt = CursorChainingEvent::TO_PREV_LINK;
+ rOutSel = aEndSelPrevBox; // Set at end of selection
+ rOutHandled = true; // Nothing more to do than move cursor
+ return;
+ }
+
+ // Possibility: Are we "pushing" at the start of the object and deleting left?
+ if (nCode == KEY_BACKSPACE && bAtStartOfTextContent && pPrevLink)
+ {
+ rOutCursorEvt = CursorChainingEvent::TO_PREV_LINK;
+ rOutSel = aEndSelPrevBox; // Set at end of selection
+ rOutHandled = false; // We need to delete characters after moving cursor
+ return;
+ }
+
+ // If arrived here there is no event detected
+ rOutCursorEvt = CursorChainingEvent::NULL_EVENT;
+
+}
+
+void TextChainCursorManager::HandleCursorEventAfterChaining(
+ const CursorChainingEvent aCurEvt,
+ const ESelection& aNewSel)
+
+{
+ // Special case for DELETE handling: we need to get back at the end of the prev box
+ if (mbHandlingDel) {
+ // reset flag
+ mbHandlingDel = false;
+
+ // Move to end of prev box
+ SdrTextObj *pPrevLink = mpTextObj->GetPrevLinkInChain();
+ ESelection aEndSel(100000, 100000);
+ impChangeEditingTextObj(pPrevLink, aEndSel);
+ return;
+ }
+
+ // Standard handling
+ HandleCursorEvent(aCurEvt, aNewSel);
+}
+
+
+void TextChainCursorManager::HandleCursorEvent(
+ const CursorChainingEvent aCurEvt,
+ const ESelection& aNewSel)
+
+{
+
+ OutlinerView* pOLV = mpEditView->GetTextEditOutlinerView();
+ SdrTextObj *pNextLink = mpTextObj->GetNextLinkInChain();
+ SdrTextObj *pPrevLink = mpTextObj->GetPrevLinkInChain();
+
+
+ switch ( aCurEvt ) {
+ case CursorChainingEvent::UNCHANGED:
+ // Set same selection as before the chaining (which is saved as PostChainingSel)
+ // We need an explicit set because the Outliner is messed up
+ // after text transfer and otherwise it brings us at arbitrary positions.
+ pOLV->SetSelection(aNewSel);
+ break;
+ case CursorChainingEvent::TO_NEXT_LINK:
+ mpTextObj->GetTextChain()->SetSwitchingToNextBox(mpTextObj, true);
+ impChangeEditingTextObj(pNextLink, aNewSel);
+ break;
+ case CursorChainingEvent::TO_PREV_LINK:
+ impChangeEditingTextObj(pPrevLink, aNewSel);
+ break;
+ case CursorChainingEvent::NULL_EVENT:
+ // Do nothing here
+ break;
+ }
+
+}
+
+void TextChainCursorManager::impChangeEditingTextObj(SdrTextObj *pTargetTextObj, ESelection aNewSel)
+{
+ assert(pTargetTextObj);
+
+ mpEditView->SdrEndTextEdit();
+ mpEditView->SdrBeginTextEdit(pTargetTextObj);
+ // OutlinerView has changed, so we update the pointer
+ OutlinerView *pOLV = mpEditView->GetTextEditOutlinerView();
+ pOLV->SetSelection(aNewSel);
+
+ // Update reference text obj
+ mpTextObj = pTargetTextObj;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/svdraw/textchainflow.cxx b/svx/source/svdraw/textchainflow.cxx
new file mode 100644
index 000000000..9763ea501
--- /dev/null
+++ b/svx/source/svdraw/textchainflow.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 <svx/svdotext.hxx>
+#include <svx/svdoutl.hxx>
+#include <editeng/outlobj.hxx>
+#include <editeng/editobj.hxx>
+#include <editeng/overflowingtxt.hxx>
+#include <textchainflow.hxx>
+#include <sal/log.hxx>
+
+TextChainFlow::TextChainFlow(SdrTextObj *pChainTarget)
+ : mpTargetLink(pChainTarget)
+{
+ SAL_INFO("svx.chaining", "[TEXTCHAINFLOW] Creating a new TextChainFlow");
+
+ mpTextChain = mpTargetLink->GetTextChain();
+ mpNextLink = mpTargetLink->GetNextLinkInChain();
+
+ bUnderflow = bOverflow = false;
+
+ mbOFisUFinduced = false;
+
+ mpOverflChText = nullptr;
+ mpUnderflChText = nullptr;
+
+ mbPossiblyCursorOut = false;
+}
+
+
+TextChainFlow::~TextChainFlow()
+{
+ mpOverflChText.reset();
+ mpUnderflChText.reset();
+}
+
+void TextChainFlow::impSetFlowOutlinerParams(SdrOutliner *, SdrOutliner *)
+{
+ // Nothing to do if not in editing mode
+}
+
+/*
+ * Check for overflow in the state of pFlowOutl.
+ * If pParamOutl is not NULL sets some parameters from there.
+ * This is useful in case the outliner is not set for overflow
+ * (e.g. in editing mode we check for overflow in drawing outl but
+ * parameters come from editing outliner)
+*/
+void TextChainFlow::impCheckForFlowEvents(SdrOutliner *pFlowOutl, SdrOutliner *pParamOutl)
+{
+ bool bOldUpdateMode = pFlowOutl->IsUpdateLayout();
+
+ // XXX: This could be reorganized moving most of this stuff inside EditingTextChainFlow
+ if (pParamOutl != nullptr)
+ {
+ // We need this since it's required to check overflow
+ pFlowOutl->SetUpdateLayout(true);
+
+ // XXX: does this work if you do it before setting the text? Seems so.
+ impSetFlowOutlinerParams(pFlowOutl, pParamOutl);
+ }
+
+ bool bIsPageOverflow = pFlowOutl->IsPageOverflow();
+
+ // NOTE: overflow and underflow cannot be both true
+ bOverflow = bIsPageOverflow && mpNextLink;
+ bUnderflow = !bIsPageOverflow && mpNextLink && mpNextLink->HasText();
+
+ // Get old state on whether to merge para-s or not
+ // NOTE: We handle UF/OF using the _old_ state. The new one is simply saved
+ bool bMustMergeParaAmongLinks = GetTextChain()->GetIsPartOfLastParaInNextLink(mpTargetLink);
+
+ // Set (Non)OverflowingTxt here (if any)
+
+ // If we had an underflow before we have to deep merge paras anyway
+ bool bMustMergeParaOF = bMustMergeParaAmongLinks || mbOFisUFinduced;
+
+ mpOverflChText.reset( bOverflow ?
+ new OFlowChainedText(pFlowOutl, bMustMergeParaOF) :
+ nullptr );
+
+ // Set current underflowing text (if any)
+ mpUnderflChText.reset( bUnderflow ?
+ new UFlowChainedText(pFlowOutl, bMustMergeParaAmongLinks) :
+ nullptr );
+
+ // Reset update mode // Reset it here because we use WriteRTF (needing updatemode = true) in the two constructors above
+ if (!bOldUpdateMode) // Reset only if the old value was false
+ pFlowOutl->SetUpdateLayout(bOldUpdateMode);
+
+ // NOTE: Must be called after mp*ChText abd b*flow have been set but before mbOFisUFinduced is reset
+ impUpdateCursorInfo();
+
+ // To check whether an overflow is underflow induced or not (useful in cursor checking)
+ mbOFisUFinduced = bUnderflow;
+}
+
+void TextChainFlow::impUpdateCursorInfo()
+{
+ // XXX: Maybe we can get rid of mbOFisUFinduced by not allowing handling of more than one event by the same TextChainFlow
+ // if this is not an OF triggered during an UF
+
+ mbPossiblyCursorOut = bOverflow;
+
+ if(mbPossiblyCursorOut ) {
+ maOverflowPosSel = mpOverflChText->GetOverflowPointSel();
+ ESelection aSelAtUFTime = GetTextChain()->GetPreChainingSel(GetLinkTarget());
+ // Might be an invalid selection if the cursor at UF time was before
+ // the (possibly UF-induced) Overflowing point but we don't use it in that case
+ maPostChainingSel = ESelection(aSelAtUFTime.nStartPara-maOverflowPosSel.nStartPara,
+ aSelAtUFTime.nStartPos-maOverflowPosSel.nStartPos );
+ }
+
+ // XXX: It may not be necessary anymore to keep this method separated from EditingTextChainFlow::impBroadcastCursorInfo
+}
+
+void TextChainFlow::CheckForFlowEvents(SdrOutliner *pFlowOutl)
+{
+ impCheckForFlowEvents(pFlowOutl, nullptr);
+}
+
+
+bool TextChainFlow::IsOverflow() const
+{
+ return bOverflow;
+}
+
+bool TextChainFlow::IsUnderflow() const
+{
+ return bUnderflow;
+}
+
+
+// XXX: In editing mode you need to get "underflowing" text from editing outliner, so it's kinda separate from the drawing one!
+
+// XXX:Would it be possible to unify underflow and its possibly following overflow?
+void TextChainFlow::ExecuteUnderflow(SdrOutliner *pOutl)
+{
+ //GetTextChain()->SetNilChainingEvent(mpTargetLink, true);
+ // making whole text
+ // merges underflowing text with the one in the next box
+ std::optional<OutlinerParaObject> pNewText = mpUnderflChText->CreateMergedUnderflowParaObject(pOutl, mpNextLink->GetOutlinerParaObject());
+
+ // Set the other box empty; it will be replaced by the rest of the text if overflow occurs
+ if (!mpTargetLink->GetPreventChainable())
+ mpNextLink->NbcSetOutlinerParaObject(pOutl->GetEmptyParaObject());
+
+ // We store the size since NbcSetOutlinerParaObject can change it
+ //Size aOldSize = pOutl->GetMaxAutoPaperSize();
+
+ // This should not be done in editing mode!! //XXX
+ if (!mpTargetLink->IsInEditMode())
+ {
+ mpTargetLink->NbcSetOutlinerParaObject(pNewText);
+ }
+
+ // Restore size and set new text
+ //pOutl->SetMaxAutoPaperSize(aOldSize); // XXX (it seems to be working anyway without this)
+ pOutl->SetText(*pNewText);
+
+ //GetTextChain()->SetNilChainingEvent(mpTargetLink, false);
+
+ // Check for new overflow
+ CheckForFlowEvents(pOutl);
+}
+
+void TextChainFlow::ExecuteOverflow(SdrOutliner *pNonOverflOutl, SdrOutliner *pOverflOutl)
+{
+ //GetTextChain()->SetNilChainingEvent(mpTargetLink, true);
+ // Leave only non overflowing text
+ impLeaveOnlyNonOverflowingText(pNonOverflOutl);
+
+ // Transfer of text to next link
+ if (!mpTargetLink->GetPreventChainable() ) // we don't transfer text while dragging because of resizing
+ {
+ impMoveChainedTextToNextLink(pOverflOutl);
+ }
+
+ //GetTextChain()->SetNilChainingEvent(mpTargetLink, false);
+}
+
+void TextChainFlow::impLeaveOnlyNonOverflowingText(SdrOutliner *pNonOverflOutl)
+{
+ std::optional<OutlinerParaObject> pNewText = mpOverflChText->RemoveOverflowingText(pNonOverflOutl);
+
+ SAL_INFO("svx.chaining", "[TEXTCHAINFLOW - OF] SOURCE box set to "
+ << pNewText->GetTextObject().GetParagraphCount() << " paras");
+
+ // adds it to current outliner anyway (useful in static decomposition)
+ pNonOverflOutl->SetText(*pNewText);
+
+ mpTargetLink->NbcSetOutlinerParaObject(std::move(pNewText));
+ // For some reason the paper size is lost after last instruction, so we set it.
+ pNonOverflOutl->SetPaperSize(Size(pNonOverflOutl->GetPaperSize().Width(),
+ pNonOverflOutl->GetTextHeight()));
+
+}
+
+void TextChainFlow::impMoveChainedTextToNextLink(SdrOutliner *pOverflOutl)
+{
+ // prevent copying text in same box
+ if ( mpNextLink == mpTargetLink ) {
+ SAL_INFO("svx.chaining", "[CHAINING] Trying to copy text for next link in same object");
+ return;
+ }
+
+ std::optional<OutlinerParaObject> pNewText =
+ mpOverflChText->InsertOverflowingText(pOverflOutl,
+ mpNextLink->GetOutlinerParaObject());
+ SAL_INFO("svx.chaining", "[TEXTCHAINFLOW - OF] DEST box set to "
+ << pNewText->GetTextObject().GetParagraphCount() << " paras");
+
+ if (pNewText)
+ mpNextLink->NbcSetOutlinerParaObject(std::move(pNewText));
+
+ // Set Deep Merge status
+ SAL_INFO("svx.chaining", "[DEEPMERGE] Setting deepMerge to "
+ << mpOverflChText->IsLastParaInterrupted());
+ GetTextChain()->SetIsPartOfLastParaInNextLink(
+ mpTargetLink,
+ mpOverflChText->IsLastParaInterrupted());
+}
+
+SdrTextObj *TextChainFlow::GetLinkTarget() const
+{
+ return mpTargetLink;
+}
+
+TextChain *TextChainFlow::GetTextChain() const
+{
+ return mpTextChain;
+}
+
+// EditingTextChainFlow
+
+EditingTextChainFlow::EditingTextChainFlow(SdrTextObj *pLinkTarget) :
+ TextChainFlow(pLinkTarget)
+{
+ SAL_INFO("svx.chaining", "[TEXTCHAINFLOW] Creating a new EditingTextChainFlow");
+}
+
+void EditingTextChainFlow::CheckForFlowEvents(SdrOutliner *pFlowOutl)
+{
+ // if this is editing outliner no need to set parameters
+ if (pFlowOutl == GetLinkTarget()->mpEditingOutliner)
+ impCheckForFlowEvents(pFlowOutl, nullptr);
+ else
+ impCheckForFlowEvents(pFlowOutl, GetLinkTarget()->mpEditingOutliner);
+
+ // Broadcast events for cursor handling
+ impBroadcastCursorInfo();
+}
+
+void EditingTextChainFlow::impLeaveOnlyNonOverflowingText(SdrOutliner *pNonOverflOutl)
+{
+ mpOverflChText->RemoveOverflowingText(pNonOverflOutl);
+ //impSetTextForEditingOutliner(pNewText); //XXX: Don't call it since we do everything with NonOverflowingText::ToParaObject // XXX: You may need this for Underflow
+
+ // XXX: I'm not sure whether we need this (after all operations such as Paste don't change this - as far as I understand)
+ //GetLinkTarget()->NbcSetOutlinerParaObject(pNewText);
+}
+
+void EditingTextChainFlow::impSetFlowOutlinerParams(SdrOutliner *pFlowOutl, SdrOutliner *pParamOutl)
+{
+ // Set right size for overflow
+ pFlowOutl->SetMaxAutoPaperSize(pParamOutl->GetMaxAutoPaperSize());
+ pFlowOutl->SetMinAutoPaperSize(pParamOutl->GetMinAutoPaperSize());
+ pFlowOutl->SetPaperSize(pParamOutl->GetPaperSize());
+}
+
+void EditingTextChainFlow::impBroadcastCursorInfo() const
+{
+ ESelection aPreChainingSel = GetTextChain()->GetPreChainingSel(GetLinkTarget()) ;
+
+ // Test whether the cursor is out of the box.
+ bool bCursorOut = mbPossiblyCursorOut && maOverflowPosSel < aPreChainingSel;
+
+ // NOTE: I handled already the stuff for the comments below. They will be kept temporarily till stuff settles down.
+ // Possibility: 1) why don't we stop passing the actual event to the TextChain and instead we pass
+ // the overflow pos and mbPossiblyCursorOut
+ // 2) We pass the current selection before anything happens and we make impBroadcastCursorInfo compute it.
+
+
+ if (bCursorOut) {
+ //maCursorEvent = CursorChainingEvent::TO_NEXT_LINK;
+ GetTextChain()->SetPostChainingSel(GetLinkTarget(), maPostChainingSel);
+ GetTextChain()->SetCursorEvent(GetLinkTarget(), CursorChainingEvent::TO_NEXT_LINK);
+ } else {
+ //maCursorEvent = CursorChainingEvent::UNCHANGED;
+ GetTextChain()->SetPostChainingSel(GetLinkTarget(), aPreChainingSel);
+ GetTextChain()->SetCursorEvent(GetLinkTarget(), CursorChainingEvent::UNCHANGED);
+ }
+
+
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/table/accessiblecell.cxx b/svx/source/table/accessiblecell.cxx
new file mode 100644
index 000000000..1052ab5eb
--- /dev/null
+++ b/svx/source/table/accessiblecell.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 <sal/config.h>
+#include <sal/log.hxx>
+
+#include <memory>
+
+#include "accessiblecell.hxx"
+#include <cell.hxx>
+
+#include <com/sun/star/accessibility/AccessibleRole.hpp>
+#include <com/sun/star/accessibility/AccessibleStateType.hpp>
+#include <com/sun/star/accessibility/AccessibleEventId.hpp>
+
+#include <editeng/unoedsrc.hxx>
+#include <vcl/svapp.hxx>
+#include <vcl/window.hxx>
+
+#include <unotools/accessiblestatesethelper.hxx>
+#include <comphelper/string.hxx>
+#include <comphelper/sequence.hxx>
+#include <svx/IAccessibleViewForwarder.hxx>
+#include <svx/unoshtxt.hxx>
+#include <svx/svdotext.hxx>
+#include <tools/debug.hxx>
+
+using namespace sdr::table;
+using namespace ::com::sun::star;
+using namespace ::com::sun::star::uno;
+using namespace ::com::sun::star::accessibility;
+using namespace ::com::sun::star::lang;
+using namespace ::com::sun::star::container;
+
+namespace accessibility {
+
+AccessibleCell::AccessibleCell( const css::uno::Reference< css::accessibility::XAccessible>& rxParent, const sdr::table::CellRef& rCell, sal_Int32 nIndex, const AccessibleShapeTreeInfo& rShapeTreeInfo )
+: AccessibleCellBase( rxParent, AccessibleRole::TABLE_CELL )
+, maShapeTreeInfo( rShapeTreeInfo )
+, mnIndexInParent( nIndex )
+, mxCell( rCell )
+{
+ //Init the pAccTable var
+ pAccTable = dynamic_cast <AccessibleTableShape *> (rxParent.get());
+}
+
+
+AccessibleCell::~AccessibleCell()
+{
+ DBG_ASSERT( mpText == nullptr, "svx::AccessibleCell::~AccessibleCell(), not disposed!?" );
+}
+
+
+void AccessibleCell::Init()
+{
+ SdrView* pView = maShapeTreeInfo.GetSdrView();
+ const vcl::Window* pWindow = maShapeTreeInfo.GetWindow ();
+ if( !((pView != nullptr) && (pWindow != nullptr) && mxCell.is()))
+ return;
+
+ // create AccessibleTextHelper to handle this shape's text
+ if( mxCell->CanCreateEditOutlinerParaObject() || mxCell->GetOutlinerParaObject() != nullptr )
+ {
+ // non-empty text -> use full-fledged edit source right away
+
+ mpText.reset( new AccessibleTextHelper( std::make_unique<SvxTextEditSource>(mxCell->GetObject(), mxCell.get(), *pView, *pWindow->GetOutDev()) ) );
+ if( mxCell.is() && mxCell->IsActiveCell() )
+ mpText->SetFocus();
+ mpText->SetEventSource(this);
+ }
+}
+
+
+bool AccessibleCell::SetState (sal_Int16 aState)
+{
+ bool bStateHasChanged = false;
+
+ if (aState == AccessibleStateType::FOCUSED && mpText != nullptr)
+ {
+ // Offer FOCUSED state to edit engine and detect whether the state
+ // changes.
+ bool bIsFocused = mpText->HaveFocus ();
+ mpText->SetFocus();
+ bStateHasChanged = (bIsFocused != mpText->HaveFocus ());
+ }
+ else
+ bStateHasChanged = AccessibleContextBase::SetState (aState);
+
+ return bStateHasChanged;
+}
+
+
+bool AccessibleCell::ResetState (sal_Int16 aState)
+{
+ bool bStateHasChanged = false;
+
+ if (aState == AccessibleStateType::FOCUSED && mpText != nullptr)
+ {
+ // Try to remove FOCUSED state from the edit engine and detect
+ // whether the state changes.
+ bool bIsFocused = mpText->HaveFocus ();
+ mpText->SetFocus (false);
+ bStateHasChanged = (bIsFocused != mpText->HaveFocus ());
+ }
+ else
+ bStateHasChanged = AccessibleContextBase::ResetState (aState);
+
+ return bStateHasChanged;
+}
+
+
+// XInterface
+
+
+Any SAL_CALL AccessibleCell::queryInterface( const Type& aType )
+{
+ return AccessibleCellBase::queryInterface( aType );
+}
+
+
+void SAL_CALL AccessibleCell::acquire( ) noexcept
+{
+ AccessibleCellBase::acquire();
+}
+
+
+void SAL_CALL AccessibleCell::release( ) noexcept
+{
+ AccessibleCellBase::release();
+}
+
+
+// XAccessibleContext
+
+
+/** The children of this cell come from the paragraphs of text.
+*/
+sal_Int32 SAL_CALL AccessibleCell::getAccessibleChildCount()
+{
+ SolarMutexGuard aSolarGuard;
+ ThrowIfDisposed ();
+ return mpText != nullptr ? mpText->GetChildCount () : 0;
+}
+
+
+/** Forward the request to the shape. Return the requested shape or throw
+ an exception for a wrong index.
+*/
+Reference<XAccessible> SAL_CALL AccessibleCell::getAccessibleChild (sal_Int32 nIndex)
+{
+ SolarMutexGuard aSolarGuard;
+ ThrowIfDisposed ();
+
+ // todo: does GetChild throw IndexOutOfBoundsException?
+ return mpText->GetChild (nIndex);
+}
+
+
+/** Return a copy of the state set.
+ Possible states are:
+ ENABLED
+ SHOWING
+ VISIBLE
+*/
+Reference<XAccessibleStateSet> SAL_CALL AccessibleCell::getAccessibleStateSet()
+{
+ SolarMutexGuard aSolarGuard;
+ ::osl::MutexGuard aGuard (m_aMutex);
+ Reference<XAccessibleStateSet> xStateSet;
+
+ if (rBHelper.bDisposed || mpText == nullptr)
+ {
+ // Return a minimal state set that only contains the DEFUNC state.
+ xStateSet = AccessibleContextBase::getAccessibleStateSet ();
+ }
+ else
+ {
+ if(mxStateSet)
+ {
+ // Merge current FOCUSED state from edit engine.
+ if (mpText != nullptr)
+ {
+ if (mpText->HaveFocus())
+ mxStateSet->AddState (AccessibleStateType::FOCUSED);
+ else
+ mxStateSet->RemoveState (AccessibleStateType::FOCUSED);
+ }
+ // Set the invisible state for merged cell
+ if (mxCell.is() && mxCell->isMerged())
+ mxStateSet->RemoveState(AccessibleStateType::VISIBLE);
+ else
+ mxStateSet->AddState(AccessibleStateType::VISIBLE);
+
+
+ //Just when the parent table is not read-only,set states EDITABLE,RESIZABLE,MOVEABLE
+ css::uno::Reference<XAccessible> xTempAcc = getAccessibleParent();
+ if( xTempAcc.is() )
+ {
+ css::uno::Reference<XAccessibleContext>
+ xTempAccContext = xTempAcc->getAccessibleContext();
+ if( xTempAccContext.is() )
+ {
+ css::uno::Reference<XAccessibleStateSet> rState =
+ xTempAccContext->getAccessibleStateSet();
+ if( rState.is() )
+ {
+ const css::uno::Sequence<short> aStates = rState->getStates();
+ if (std::find(aStates.begin(), aStates.end(), AccessibleStateType::EDITABLE) != aStates.end())
+ {
+ mxStateSet->AddState (AccessibleStateType::EDITABLE);
+ mxStateSet->AddState (AccessibleStateType::RESIZABLE);
+ mxStateSet->AddState (AccessibleStateType::MOVEABLE);
+ }
+ }
+ }
+ }
+ // Create a copy of the state set that may be modified by the
+ // caller without affecting the current state set.
+ xStateSet.set(new ::utl::AccessibleStateSetHelper (*mxStateSet));
+ }
+ }
+
+ return xStateSet;
+}
+
+
+// XAccessibleComponent
+
+
+sal_Bool SAL_CALL AccessibleCell::containsPoint( const css::awt::Point& aPoint)
+{
+ return AccessibleComponentBase::containsPoint( aPoint );
+}
+
+/** The implementation below is at the moment straightforward. It iterates
+ over all children (and thereby instances all children which have not
+ been already instantiated) until a child covering the specified point is
+ found.
+ This leaves room for improvement. For instance, first iterate only over
+ the already instantiated children and only if no match is found
+ instantiate the remaining ones.
+*/
+Reference<XAccessible > SAL_CALL AccessibleCell::getAccessibleAtPoint ( const css::awt::Point& aPoint)
+{
+ SolarMutexGuard aSolarGuard;
+ ::osl::MutexGuard aGuard (m_aMutex);
+
+ sal_Int32 nChildCount = getAccessibleChildCount ();
+ for (sal_Int32 i=0; i<nChildCount; ++i)
+ {
+ Reference<XAccessible> xChild (getAccessibleChild (i));
+ if (xChild.is())
+ {
+ Reference<XAccessibleComponent> xChildComponent (xChild->getAccessibleContext(), uno::UNO_QUERY);
+ if (xChildComponent.is())
+ {
+ awt::Rectangle aBBox (xChildComponent->getBounds());
+ if ( (aPoint.X >= aBBox.X)
+ && (aPoint.Y >= aBBox.Y)
+ && (aPoint.X < aBBox.X+aBBox.Width)
+ && (aPoint.Y < aBBox.Y+aBBox.Height) )
+ return xChild;
+ }
+ }
+ }
+
+ // Have not found a child under the given point. Returning empty
+ // reference to indicate this.
+ return uno::Reference<XAccessible>();
+}
+
+
+css::awt::Rectangle SAL_CALL AccessibleCell::getBounds()
+{
+ SolarMutexGuard aSolarGuard;
+ ::osl::MutexGuard aGuard (m_aMutex);
+
+ ThrowIfDisposed ();
+ css::awt::Rectangle aBoundingBox;
+ if( mxCell.is() )
+ {
+ // Get the cell's bounding box in internal coordinates (in 100th of mm)
+ const ::tools::Rectangle aCellRect( mxCell->getCellRect() );
+
+ // Transform coordinates from internal to pixel.
+ if (maShapeTreeInfo.GetViewForwarder() == nullptr)
+ throw uno::RuntimeException ("AccessibleCell has no valid view forwarder",static_cast<uno::XWeak*>(this));
+
+ ::Size aPixelSize( maShapeTreeInfo.GetViewForwarder()->LogicToPixel(::Size(aCellRect.GetWidth(), aCellRect.GetHeight())) );
+ ::Point aPixelPosition( maShapeTreeInfo.GetViewForwarder()->LogicToPixel( aCellRect.TopLeft() ));
+
+ // Clip the shape's bounding box with the bounding box of its parent.
+ Reference<XAccessibleComponent> xParentComponent ( getAccessibleParent(), uno::UNO_QUERY);
+ if (xParentComponent.is())
+ {
+ // Make the coordinates relative to the parent.
+ awt::Point aParentLocation (xParentComponent->getLocationOnScreen());
+ int x = aPixelPosition.getX() - aParentLocation.X;
+ int y = aPixelPosition.getY() - aParentLocation.Y;
+
+ // Clip with parent (with coordinates relative to itself).
+ ::tools::Rectangle aBBox ( x, y, x + aPixelSize.getWidth(), y + aPixelSize.getHeight());
+ awt::Size aParentSize (xParentComponent->getSize());
+ ::tools::Rectangle aParentBBox (0,0, aParentSize.Width, aParentSize.Height);
+ aBBox = aBBox.GetIntersection (aParentBBox);
+ aBoundingBox = awt::Rectangle ( aBBox.Left(), aBBox.Top(), aBBox.getWidth(), aBBox.getHeight());
+ }
+ else
+ {
+ SAL_INFO("svx", "parent does not support component");
+ aBoundingBox = awt::Rectangle (aPixelPosition.getX(), aPixelPosition.getY(),aPixelSize.getWidth(), aPixelSize.getHeight());
+ }
+ }
+
+ return aBoundingBox;
+}
+
+
+css::awt::Point SAL_CALL AccessibleCell::getLocation()
+{
+ ThrowIfDisposed ();
+ css::awt::Rectangle aBoundingBox(getBounds());
+ return css::awt::Point(aBoundingBox.X, aBoundingBox.Y);
+}
+
+
+css::awt::Point SAL_CALL AccessibleCell::getLocationOnScreen()
+{
+ ThrowIfDisposed ();
+
+ // Get relative position...
+ css::awt::Point aLocation(getLocation ());
+
+ // ... and add absolute position of the parent.
+ Reference<XAccessibleComponent> xParentComponent( getAccessibleParent(), uno::UNO_QUERY);
+ if(xParentComponent.is())
+ {
+ css::awt::Point aParentLocation(xParentComponent->getLocationOnScreen());
+ aLocation.X += aParentLocation.X;
+ aLocation.Y += aParentLocation.Y;
+ }
+ else
+ {
+ SAL_WARN("svx", "parent does not support XAccessibleComponent");
+ }
+
+ return aLocation;
+}
+
+
+awt::Size SAL_CALL AccessibleCell::getSize()
+{
+ ThrowIfDisposed ();
+ awt::Rectangle aBoundingBox (getBounds());
+ return awt::Size (aBoundingBox.Width, aBoundingBox.Height);
+}
+
+
+void SAL_CALL AccessibleCell::grabFocus()
+{
+ AccessibleComponentBase::grabFocus();
+}
+
+
+sal_Int32 SAL_CALL AccessibleCell::getForeground()
+{
+ ThrowIfDisposed ();
+
+ // todo
+ return sal_Int32(0x0ffffffL);
+}
+
+
+sal_Int32 SAL_CALL AccessibleCell::getBackground()
+{
+ ThrowIfDisposed ();
+
+ // todo
+ return 0;
+}
+
+
+// XAccessibleExtendedComponent
+
+
+css::uno::Reference< css::awt::XFont > SAL_CALL AccessibleCell::getFont()
+{
+//todo
+ return AccessibleComponentBase::getFont();
+}
+
+
+OUString SAL_CALL AccessibleCell::getTitledBorderText()
+{
+ return AccessibleComponentBase::getTitledBorderText();
+}
+
+
+OUString SAL_CALL AccessibleCell::getToolTipText()
+{
+ return AccessibleComponentBase::getToolTipText();
+}
+
+
+// XAccessibleEventBroadcaster
+
+
+void SAL_CALL AccessibleCell::addAccessibleEventListener( const Reference<XAccessibleEventListener >& rxListener)
+{
+ SolarMutexGuard aSolarGuard;
+ ::osl::MutexGuard aGuard (m_aMutex);
+ if (rBHelper.bDisposed || rBHelper.bInDispose)
+ {
+ Reference<XInterface> xSource( static_cast<XComponent *>(this) );
+ lang::EventObject aEventObj(xSource);
+ rxListener->disposing(aEventObj);
+ }
+ else
+ {
+ AccessibleContextBase::addAccessibleEventListener (rxListener);
+ if (mpText != nullptr)
+ mpText->AddEventListener (rxListener);
+ }
+}
+
+
+void SAL_CALL AccessibleCell::removeAccessibleEventListener( const Reference<XAccessibleEventListener >& rxListener)
+{
+ SolarMutexGuard aSolarGuard;
+ AccessibleContextBase::removeAccessibleEventListener(rxListener);
+ if (mpText != nullptr)
+ mpText->RemoveEventListener (rxListener);
+}
+
+
+// XServiceInfo
+
+
+OUString SAL_CALL AccessibleCell::getImplementationName()
+{
+ return "AccessibleCell";
+}
+
+
+Sequence<OUString> SAL_CALL AccessibleCell::getSupportedServiceNames()
+{
+ ThrowIfDisposed ();
+ const css::uno::Sequence<OUString> vals { "com.sun.star.drawing.AccessibleCell" };
+ return comphelper::concatSequences(AccessibleContextBase::getSupportedServiceNames(), vals);
+}
+
+
+// IAccessibleViewForwarderListener
+
+
+void AccessibleCell::ViewForwarderChanged()
+{
+ // Inform all listeners that the graphical representation (i.e. size
+ // and/or position) of the shape has changed.
+ CommitChange(AccessibleEventId::VISIBLE_DATA_CHANGED, Any(), Any());
+
+ // update our children that our screen position might have changed
+ if( mpText )
+ mpText->UpdateChildren();
+}
+
+
+// protected
+
+
+void AccessibleCell::disposing()
+{
+ SolarMutexGuard aSolarGuard;
+ ::osl::MutexGuard aGuard (m_aMutex);
+
+ // Make sure to send an event that this object loses the focus in the
+ // case that it has the focus.
+ if (mxStateSet != nullptr)
+ mxStateSet->RemoveState(AccessibleStateType::FOCUSED);
+
+ if (mpText != nullptr)
+ {
+ mpText->Dispose();
+ mpText.reset();
+ }
+
+ // Cleanup. Remove references to objects to allow them to be
+ // destroyed.
+ mxCell.clear();
+ maShapeTreeInfo.dispose();
+
+ // Call base classes.
+ AccessibleContextBase::dispose ();
+}
+
+sal_Int32 SAL_CALL AccessibleCell::getAccessibleIndexInParent()
+{
+ ThrowIfDisposed ();
+ return mnIndexInParent;
+}
+
+
+OUString AccessibleCell::getCellName( sal_Int32 nCol, sal_Int32 nRow )
+{
+ OUStringBuffer aBuf;
+
+ if (nCol < 26*26)
+ {
+ if (nCol < 26)
+ aBuf.append( static_cast<sal_Unicode>( 'A' +
+ static_cast<sal_uInt16>(nCol)));
+ else
+ {
+ aBuf.append( static_cast<sal_Unicode>( 'A' +
+ (static_cast<sal_uInt16>(nCol) / 26) - 1));
+ aBuf.append( static_cast<sal_Unicode>( 'A' +
+ (static_cast<sal_uInt16>(nCol) % 26)));
+ }
+ }
+ else
+ {
+ OUStringBuffer aStr;
+ while (nCol >= 26)
+ {
+ sal_Int32 nC = nCol % 26;
+ aStr.append(static_cast<sal_Unicode>( 'A' +
+ static_cast<sal_uInt16>(nC)));
+ nCol = nCol - nC;
+ nCol = nCol / 26 - 1;
+ }
+ aStr.append(static_cast<sal_Unicode>( 'A' +
+ static_cast<sal_uInt16>(nCol)));
+ aBuf.append(comphelper::string::reverseString(aStr));
+ }
+ aBuf.append(nRow+1);
+ return aBuf.makeStringAndClear();
+}
+
+OUString SAL_CALL AccessibleCell::getAccessibleName()
+{
+ ThrowIfDisposed ();
+ SolarMutexGuard aSolarGuard;
+
+ if( pAccTable )
+ {
+ try
+ {
+ sal_Int32 nRow = 0, nCol = 0;
+ pAccTable->getColumnAndRow(mnIndexInParent, nCol, nRow);
+ return getCellName( nCol, nRow );
+ }
+ catch(const Exception&)
+ {
+ }
+ }
+
+ return AccessibleCellBase::getAccessibleName();
+}
+
+void AccessibleCell::UpdateChildren()
+{
+ if (mpText)
+ mpText->UpdateChildren();
+}
+
+/* MT: Above getAccessibleName was introduced with IA2 CWS, while below was introduce in 3.3 meanwhile. Check which one is correct
++If this is correct, we also don't need sdr::table::CellRef getCellRef(), UpdateChildren(), getCellName( sal_Int32 nCol, sal_Int32 nRow ) above
++
+
+OUString SAL_CALL AccessibleCell::getAccessibleName() throw (css::uno::RuntimeException)
+{
+ ThrowIfDisposed ();
+ SolarMutexGuard aSolarGuard;
+
+ if( mxCell.is() )
+ return mxCell->getName();
+
+ return AccessibleCellBase::getAccessibleName();
+}
+*/
+
+} // end of namespace accessibility
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/table/accessiblecell.hxx b/svx/source/table/accessiblecell.hxx
new file mode 100644
index 000000000..65b9665ab
--- /dev/null
+++ b/svx/source/table/accessiblecell.hxx
@@ -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 .
+ */
+
+#ifndef INCLUDED_SVX_SOURCE_TABLE_ACCESSIBLECELL_HXX
+#define INCLUDED_SVX_SOURCE_TABLE_ACCESSIBLECELL_HXX
+
+#include <com/sun/star/accessibility/XAccessible.hpp>
+#include <com/sun/star/accessibility/XAccessibleExtendedComponent.hpp>
+
+#include <editeng/AccessibleContextBase.hxx>
+#include <editeng/AccessibleComponentBase.hxx>
+#include <svx/IAccessibleViewForwarderListener.hxx>
+#include <svx/AccessibleTextHelper.hxx>
+#include <svx/AccessibleShapeTreeInfo.hxx>
+#include <AccessibleTableShape.hxx>
+
+#include <cppuhelper/implbase.hxx>
+
+#include <celltypes.hxx>
+
+
+namespace accessibility
+{
+
+class AccessibleShapeTreeInfo;
+
+typedef ::cppu::ImplInheritanceHelper< AccessibleContextBase, css::accessibility::XAccessibleExtendedComponent > AccessibleCellBase;
+
+class AccessibleCell : public AccessibleCellBase
+ , public AccessibleComponentBase
+ , public IAccessibleViewForwarderListener
+{
+public:
+ AccessibleCell( const css::uno::Reference< css::accessibility::XAccessible>& rxParent, const sdr::table::CellRef& rCell, sal_Int32 nIndex, const AccessibleShapeTreeInfo& rShapeTreeInfo);
+ virtual ~AccessibleCell() override;
+ AccessibleCell(const AccessibleCell&) = delete;
+ AccessibleCell& operator=(const AccessibleCell&) = delete;
+
+ void Init();
+
+ virtual bool SetState (sal_Int16 aState) override;
+ virtual bool ResetState (sal_Int16 aState) override;
+
+ // XInterface
+ virtual css::uno::Any SAL_CALL queryInterface( const css::uno::Type& aType ) override;
+ virtual void SAL_CALL acquire( ) noexcept override;
+ virtual void SAL_CALL release( ) noexcept override;
+
+ // XAccessibleContext
+ virtual sal_Int32 SAL_CALL getAccessibleChildCount() override;
+ virtual css::uno::Reference< css::accessibility::XAccessible> SAL_CALL getAccessibleChild(sal_Int32 nIndex) override;
+ virtual css::uno::Reference< css::accessibility::XAccessibleStateSet> SAL_CALL getAccessibleStateSet() override;
+ virtual sal_Int32 SAL_CALL getAccessibleIndexInParent() override;
+ virtual OUString SAL_CALL getAccessibleName() override;
+ const sdr::table::CellRef& getCellRef() const { return mxCell;}
+ void UpdateChildren();
+ static OUString getCellName( sal_Int32 nCol, sal_Int32 nRow );
+
+ // 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;
+
+ // XAccessibleExtendedComponent
+ virtual css::uno::Reference< css::awt::XFont > SAL_CALL getFont() override;
+ virtual OUString SAL_CALL getTitledBorderText() override;
+ virtual OUString SAL_CALL getToolTipText() override;
+
+ // XAccessibleEventBroadcaster
+ virtual void SAL_CALL addAccessibleEventListener( const css::uno::Reference< css::accessibility::XAccessibleEventListener >& rxListener) override;
+ virtual void SAL_CALL removeAccessibleEventListener( const css::uno::Reference< css::accessibility::XAccessibleEventListener >& rxListener) override;
+
+ // XServiceInfo
+ virtual OUString SAL_CALL getImplementationName() override;
+ virtual css::uno::Sequence< OUString> SAL_CALL getSupportedServiceNames() override;
+
+ // IAccessibleViewForwarderListener
+ virtual void ViewForwarderChanged() override;
+
+ // Misc
+
+ /** set the index _nIndex at the accessible cell param _nIndex The new index in parent.
+ */
+ void setIndexInParent(sal_Int32 _nIndex) { mnIndexInParent = _nIndex; }
+
+ //Get the parent table
+ AccessibleTableShape* GetParentTable() { return pAccTable; }
+protected:
+ /// Bundle of information passed to all shapes in a document tree.
+ AccessibleShapeTreeInfo maShapeTreeInfo;
+
+ /// the index in parent.
+ sal_Int32 mnIndexInParent;
+
+ /// The accessible text engine. May be NULL if it can not be created.
+ std::unique_ptr<AccessibleTextHelper> mpText;
+
+ sdr::table::CellRef mxCell;
+
+ /// This method is called from the component helper base class while disposing.
+ virtual void SAL_CALL disposing() override;
+
+ AccessibleTableShape *pAccTable;
+};
+
+} // end of namespace accessibility
+
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/table/accessibletableshape.cxx b/svx/source/table/accessibletableshape.cxx
new file mode 100644
index 000000000..540ae3116
--- /dev/null
+++ b/svx/source/table/accessibletableshape.cxx
@@ -0,0 +1,1324 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * 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/XMergeableCell.hpp>
+#include <com/sun/star/accessibility/AccessibleEventId.hpp>
+#include <com/sun/star/accessibility/AccessibleStateType.hpp>
+#include <com/sun/star/accessibility/AccessibleRole.hpp>
+#include <com/sun/star/drawing/XShape.hpp>
+#include <com/sun/star/lang/IndexOutOfBoundsException.hpp>
+
+#include <tools/diagnose_ex.h>
+#include <vcl/svapp.hxx>
+
+#include <AccessibleTableShape.hxx>
+#include <svx/sdr/table/tablecontroller.hxx>
+#include "accessiblecell.hxx"
+#include <cell.hxx>
+
+#include <algorithm>
+#include <unordered_map>
+
+#include <cppuhelper/implbase.hxx>
+#include <svx/svdotable.hxx>
+#include <com/sun/star/view/XSelectionSupplier.hpp>
+
+
+using namespace ::accessibility;
+using namespace sdr::table;
+using namespace ::com::sun::star::accessibility;
+using namespace ::com::sun::star::uno;
+using namespace ::com::sun::star::beans;
+using namespace ::com::sun::star::util;
+using namespace ::com::sun::star::lang;
+using namespace ::com::sun::star::drawing;
+using namespace ::com::sun::star::table;
+using namespace ::com::sun::star::container;
+
+namespace accessibility
+{
+
+typedef std::unordered_map< Reference< XCell >, rtl::Reference< AccessibleCell > > AccessibleCellMap;
+
+class AccessibleTableShapeImpl : public cppu::WeakImplHelper< XModifyListener >
+{
+public:
+ explicit AccessibleTableShapeImpl( AccessibleShapeTreeInfo& rShapeTreeInfo );
+
+ void init( const Reference< XAccessible>& xAccessible, const Reference< XTable >& xTable );
+ void dispose();
+
+ /// @throws IndexOutOfBoundsException
+ /// @throws RuntimeException
+ Reference< XAccessible > getAccessibleChild(sal_Int32 i);
+ /// @throws IndexOutOfBoundsException
+ void getColumnAndRow( sal_Int32 nChildIndex, sal_Int32& rnColumn, sal_Int32& rnRow );
+
+ // XModifyListener
+ virtual void SAL_CALL modified( const EventObject& aEvent ) override;
+
+ // XEventListener
+ virtual void SAL_CALL disposing( const EventObject& Source ) override;
+
+ AccessibleShapeTreeInfo& mrShapeTreeInfo;
+ Reference< XTable > mxTable;
+ AccessibleCellMap maChildMap;
+ Reference< XAccessible> mxAccessible;
+ sal_Int32 mRowCount, mColCount;
+ //get the cached AccessibleCell from XCell
+ rtl::Reference< AccessibleCell > getAccessibleCell (const Reference< XCell >& xCell);
+ /// @throws IndexOutOfBoundsException
+ /// @throws RuntimeException
+ rtl::Reference< AccessibleCell > getAccessibleCell (sal_Int32 nRow, sal_Int32 nColumn);
+};
+
+
+AccessibleTableShapeImpl::AccessibleTableShapeImpl( AccessibleShapeTreeInfo& rShapeTreeInfo )
+: mrShapeTreeInfo( rShapeTreeInfo )
+, mRowCount(0)
+, mColCount(0)
+{
+}
+
+
+void AccessibleTableShapeImpl::init( const Reference< XAccessible>& xAccessible, const Reference< XTable >& xTable )
+{
+ mxAccessible = xAccessible;
+ mxTable = xTable;
+
+ if( mxTable.is() )
+ {
+ Reference< XModifyListener > xListener( this );
+ mxTable->addModifyListener( xListener );
+ //register the listener with table model
+ Reference< css::view::XSelectionSupplier > xSelSupplier(xTable, UNO_QUERY);
+ Reference< css::view::XSelectionChangeListener > xSelListener( xAccessible, UNO_QUERY );
+ if (xSelSupplier.is())
+ xSelSupplier->addSelectionChangeListener(xSelListener);
+ mRowCount = mxTable->getRowCount();
+ mColCount = mxTable->getColumnCount();
+ }
+}
+
+
+void AccessibleTableShapeImpl::dispose()
+{
+ if( mxTable.is() )
+ {
+ //remove all the cell's acc object in table's dispose.
+ for( auto& rEntry : maChildMap )
+ {
+ rEntry.second->dispose();
+ }
+ maChildMap.clear();
+ Reference< XModifyListener > xListener( this );
+ mxTable->removeModifyListener( xListener );
+ mxTable.clear();
+ }
+ mxAccessible.clear();
+}
+
+
+//get the cached AccessibleCell from XCell
+rtl::Reference< AccessibleCell > AccessibleTableShapeImpl::getAccessibleCell (const Reference< XCell >& xCell)
+{
+ AccessibleCellMap::iterator iter( maChildMap.find( xCell ) );
+
+ if( iter != maChildMap.end() )
+ {
+ rtl::Reference< AccessibleCell > xChild( (*iter).second );
+ return xChild;
+ }
+ return rtl::Reference< AccessibleCell >();
+}
+
+rtl::Reference< AccessibleCell > AccessibleTableShapeImpl::getAccessibleCell (sal_Int32 nRow, sal_Int32 nColumn)
+{
+ Reference< XCell > xCell( mxTable->getCellByPosition( nColumn, nRow ) );
+ rtl::Reference< AccessibleCell > xChild = getAccessibleCell( xCell );
+
+ if( !xChild.is() && mxTable.is() )
+ {
+ sal_Int32 nChildIndex = mxTable->getColumnCount() * nRow + nColumn;
+ CellRef xCellRef( dynamic_cast< Cell* >( xCell.get() ) );
+
+ rtl::Reference< AccessibleCell > xAccessibleCell( new AccessibleCell( mxAccessible, xCellRef, nChildIndex, mrShapeTreeInfo ) );
+
+ xAccessibleCell->Init();
+ maChildMap[xCell] = xAccessibleCell;
+
+ xChild = xAccessibleCell;
+ }
+ return xChild;
+}
+
+
+Reference< XAccessible > AccessibleTableShapeImpl::getAccessibleChild(sal_Int32 nChildIndex)
+{
+ sal_Int32 nColumn = 0, nRow = 0;
+ getColumnAndRow( nChildIndex, nColumn, nRow );
+
+ Reference< XCell > xCell( mxTable->getCellByPosition( nColumn, nRow ) );
+ AccessibleCellMap::iterator iter( maChildMap.find( xCell ) );
+
+ if( iter != maChildMap.end() )
+ {
+ Reference< XAccessible > xChild( (*iter).second );
+ return xChild;
+ }
+ else
+ {
+ CellRef xCellRef( dynamic_cast< Cell* >( xCell.get() ) );
+
+ rtl::Reference< AccessibleCell > xAccessibleCell( new AccessibleCell( mxAccessible, xCellRef, nChildIndex, mrShapeTreeInfo ) );
+
+ xAccessibleCell->Init();
+ maChildMap[xCell] = xAccessibleCell;
+
+ return xAccessibleCell;
+ }
+}
+
+
+void AccessibleTableShapeImpl::getColumnAndRow( sal_Int32 nChildIndex, sal_Int32& rnColumn, sal_Int32& rnRow )
+{
+ rnRow = 0;
+ rnColumn = nChildIndex;
+
+ if( mxTable.is() )
+ {
+ const sal_Int32 nColumnCount = mxTable->getColumnCount();
+ while( rnColumn >= nColumnCount )
+ {
+ rnRow++;
+ rnColumn -= nColumnCount;
+ }
+
+ if( rnRow < mxTable->getRowCount() )
+ return;
+ }
+
+ throw IndexOutOfBoundsException();
+}
+
+// XModifyListener
+void SAL_CALL AccessibleTableShapeImpl::modified( const EventObject& /*aEvent*/ )
+{
+ if( !mxTable.is() )
+ return;
+
+ try
+ {
+ // structural changes may have happened to the table, validate all accessible cell instances
+ AccessibleCellMap aTempChildMap;
+ aTempChildMap.swap( maChildMap );
+
+ // first move all still existing cells to maChildMap again and update their index
+
+ const sal_Int32 nRowCount = mxTable->getRowCount();
+ const sal_Int32 nColCount = mxTable->getColumnCount();
+
+ bool bRowOrColumnChanged = false;
+ if (mRowCount != nRowCount || mColCount != nColCount )
+ {
+ bRowOrColumnChanged = true;
+ mRowCount = nRowCount;
+ mColCount = nColCount;
+ }
+ sal_Int32 nChildIndex = 0;
+
+ for( sal_Int32 nRow = 0; nRow < nRowCount; ++nRow )
+ {
+ for( sal_Int32 nCol = 0; nCol < nColCount; ++nCol )
+ {
+ Reference< XCell > xCell( mxTable->getCellByPosition( nCol, nRow ) );
+ AccessibleCellMap::iterator iter( aTempChildMap.find( xCell ) );
+
+ if( iter != aTempChildMap.end() )
+ {
+ rtl::Reference< AccessibleCell > xAccessibleCell( (*iter).second );
+ xAccessibleCell->setIndexInParent( nChildIndex );
+ xAccessibleCell->UpdateChildren();
+ // If row or column count is changed, there is split or merge, so all cell's acc name should be updated
+ if (bRowOrColumnChanged)
+ {
+ xAccessibleCell->SetAccessibleName(xAccessibleCell->getAccessibleName(), AccessibleContextBase::ManuallySet);
+ }
+ // For merged cell, add invisible & disabled state.
+ Reference< XMergeableCell > xMergedCell( mxTable->getCellByPosition( nCol, nRow ), UNO_QUERY );
+ if (xMergedCell.is() && xMergedCell->isMerged())
+ {
+ xAccessibleCell->ResetState(AccessibleStateType::VISIBLE);
+ xAccessibleCell->ResetState(AccessibleStateType::ENABLED);
+ // IA2 CWS. MT: OFFSCREEN == !SHOWING, should stay consistent
+ // xAccessibleCell->SetState(AccessibleStateType::OFFSCREEN);
+ xAccessibleCell->ResetState(AccessibleStateType::SHOWING);
+ }
+ else
+ {
+ xAccessibleCell->SetState(AccessibleStateType::VISIBLE);
+ xAccessibleCell->SetState(AccessibleStateType::ENABLED);
+ // IA2 CWS. MT: OFFSCREEN == !SHOWING, should stay consistent
+ // xAccessibleCell->ResetState(AccessibleStateType::OFFSCREEN);
+ xAccessibleCell->SetState(AccessibleStateType::SHOWING);
+ }
+
+ // move still existing cell from temporary child map to our child map
+ maChildMap[xCell] = xAccessibleCell;
+ aTempChildMap.erase( iter );
+ }
+ else
+ {
+ CellRef xCellRef( dynamic_cast< Cell* >( xCell.get() ) );
+
+ rtl::Reference< AccessibleCell > xAccessibleCell( new AccessibleCell( mxAccessible, xCellRef, nChildIndex, mrShapeTreeInfo ) );
+
+ xAccessibleCell->Init();
+ maChildMap[xCell] = xAccessibleCell;
+ }
+
+ ++nChildIndex;
+ }
+ }
+
+ // all accessible cell instances still left in aTempChildMap must be disposed
+ // as they are no longer part of the table
+
+ for( auto& rEntry : aTempChildMap )
+ {
+ rEntry.second->dispose();
+ }
+ //notify bridge to update the acc cache.
+ AccessibleTableShape *pAccTable = dynamic_cast <AccessibleTableShape *> (mxAccessible.get());
+ if (pAccTable)
+ pAccTable->CommitChange(AccessibleEventId::INVALIDATE_ALL_CHILDREN, Any(), Any());
+ }
+ catch( const Exception& )
+ {
+ TOOLS_WARN_EXCEPTION("svx.table", "");
+ }
+}
+
+// XEventListener
+void SAL_CALL AccessibleTableShapeImpl::disposing( const EventObject& /*Source*/ )
+{
+}
+
+AccessibleTableShape::AccessibleTableShape( const AccessibleShapeInfo& rShapeInfo, const AccessibleShapeTreeInfo& rShapeTreeInfo)
+: AccessibleTableShape_Base(rShapeInfo, rShapeTreeInfo)
+, mnPreviousSelectionCount(0)
+, mxImpl( new AccessibleTableShapeImpl( maShapeTreeInfo ) )
+{
+}
+
+
+AccessibleTableShape::~AccessibleTableShape()
+{
+}
+
+
+void AccessibleTableShape::Init()
+{
+ try
+ {
+ Reference< XPropertySet > xSet( mxShape, UNO_QUERY_THROW );
+ Reference< XTable > xTable( xSet->getPropertyValue("Model"), UNO_QUERY_THROW );
+
+ mxImpl->init( this, xTable );
+ }
+ catch( Exception& )
+ {
+ TOOLS_WARN_EXCEPTION("svx.table", "");
+ }
+
+ AccessibleTableShape_Base::Init();
+}
+
+
+SvxTableController* AccessibleTableShape::getTableController()
+{
+ SdrView* pView = maShapeTreeInfo.GetSdrView ();
+ if( pView )
+ return dynamic_cast< SvxTableController* >( pView->getSelectionController().get() );
+ else
+ return nullptr;
+}
+
+
+// XInterface
+
+
+Any SAL_CALL AccessibleTableShape::queryInterface( const Type& aType )
+{
+ if ( aType == cppu::UnoType<XAccessibleTableSelection>::get())
+ {
+ Reference<XAccessibleTableSelection> xThis( this );
+ Any aRet;
+ aRet <<= xThis;
+ return aRet;
+ }
+ else
+ return AccessibleTableShape_Base::queryInterface( aType );
+}
+
+
+void SAL_CALL AccessibleTableShape::acquire( ) noexcept
+{
+ AccessibleTableShape_Base::acquire();
+}
+
+
+void SAL_CALL AccessibleTableShape::release( ) noexcept
+{
+ AccessibleTableShape_Base::release();
+}
+
+
+// XAccessible
+
+
+OUString SAL_CALL AccessibleTableShape::getImplementationName()
+{
+ return "com.sun.star.comp.accessibility.AccessibleTableShape";
+}
+
+
+OUString AccessibleTableShape::CreateAccessibleBaseName()
+{
+ return "TableShape";
+}
+
+
+sal_Int32 SAL_CALL AccessibleTableShape::getAccessibleChildCount( )
+{
+ SolarMutexGuard aSolarGuard;
+ return mxImpl->mxTable.is() ? mxImpl->mxTable->getRowCount() * mxImpl->mxTable->getColumnCount() : 0;
+}
+
+
+Reference< XAccessible > SAL_CALL AccessibleTableShape::getAccessibleChild( sal_Int32 i )
+{
+ SolarMutexGuard aSolarGuard;
+ ThrowIfDisposed();
+
+ return mxImpl->getAccessibleChild( i );
+}
+
+
+sal_Int16 SAL_CALL AccessibleTableShape::getAccessibleRole()
+{
+ return AccessibleRole::TABLE;
+}
+
+
+void SAL_CALL AccessibleTableShape::disposing()
+{
+ mxImpl->dispose();
+
+ // let the base do its stuff
+ AccessibleShape::disposing();
+}
+
+
+// XAccessibleTable
+
+
+sal_Int32 SAL_CALL AccessibleTableShape::getAccessibleRowCount()
+{
+ SolarMutexGuard aSolarGuard;
+ return mxImpl->mxTable.is() ? mxImpl->mxTable->getRowCount() : 0;
+}
+
+
+sal_Int32 SAL_CALL AccessibleTableShape::getAccessibleColumnCount( )
+{
+ SolarMutexGuard aSolarGuard;
+ return mxImpl->mxTable.is() ? mxImpl->mxTable->getColumnCount() : 0;
+}
+
+
+OUString SAL_CALL AccessibleTableShape::getAccessibleRowDescription( sal_Int32 nRow )
+{
+ checkCellPosition( 0, nRow );
+ return OUString();
+}
+
+
+OUString SAL_CALL AccessibleTableShape::getAccessibleColumnDescription( sal_Int32 nColumn )
+{
+ SolarMutexGuard aSolarGuard;
+ checkCellPosition( nColumn, 0 );
+ return OUString();
+}
+
+
+sal_Int32 SAL_CALL AccessibleTableShape::getAccessibleRowExtentAt( sal_Int32 nRow, sal_Int32 nColumn )
+{
+ SolarMutexGuard aSolarGuard;
+ checkCellPosition( nColumn, nRow );
+ if( mxImpl->mxTable.is() )
+ {
+ Reference< XMergeableCell > xCell( mxImpl->mxTable->getCellByPosition( nColumn, nRow ), UNO_QUERY );
+ if( xCell.is() )
+ return xCell->getRowSpan();
+ }
+ return 1;
+}
+
+
+sal_Int32 SAL_CALL AccessibleTableShape::getAccessibleColumnExtentAt( sal_Int32 nRow, sal_Int32 nColumn )
+{
+ SolarMutexGuard aSolarGuard;
+ checkCellPosition( nColumn, nRow );
+ if( mxImpl->mxTable.is() )
+ {
+ Reference< XMergeableCell > xCell( mxImpl->mxTable->getCellByPosition( nColumn, nRow ), UNO_QUERY );
+ if( xCell.is() )
+ return xCell->getColumnSpan();
+ }
+ return 1;
+}
+
+
+Reference< XAccessibleTable > SAL_CALL AccessibleTableShape::getAccessibleRowHeaders( )
+{
+ Reference< XAccessibleTable > xRet;
+ SvxTableController* pController = getTableController();
+ if( pController )
+ {
+ if( pController->isRowHeader() )
+ {
+ xRet = new AccessibleTableHeaderShape( this, true );
+ }
+ }
+ return xRet;
+}
+
+
+Reference< XAccessibleTable > SAL_CALL AccessibleTableShape::getAccessibleColumnHeaders( )
+{
+ Reference< XAccessibleTable > xRet;
+ SvxTableController* pController = getTableController();
+ if( pController )
+ {
+ if( pController->isColumnHeader() )
+ {
+ xRet = new AccessibleTableHeaderShape( this, false );
+ }
+ }
+ return xRet;
+}
+
+
+Sequence< sal_Int32 > SAL_CALL AccessibleTableShape::getSelectedAccessibleRows( )
+{
+ sal_Int32 nRow = getAccessibleRowCount();
+ ::std::vector<bool> aSelected( nRow, true );
+ sal_Int32 nCount = nRow;
+ for( sal_Int32 i = 0; i < nRow; i++ )
+ {
+ try
+ {
+ aSelected[i] = isAccessibleRowSelected( i );
+ }
+ catch( ... )
+ {
+ return Sequence< sal_Int32 >();
+ }
+
+ if( !aSelected[i] )
+ nCount--;
+ }
+ Sequence < sal_Int32 > aRet( nCount );
+ sal_Int32 *pRet = aRet.getArray();
+ sal_Int32 nPos = 0;
+ size_t nSize = aSelected.size();
+ for( size_t i=0; i < nSize && nPos < nCount; i++ )
+ {
+ if( aSelected[i] )
+ {
+ *pRet++ = i;
+ nPos++;
+ }
+ }
+
+ return aRet;
+}
+
+
+Sequence< sal_Int32 > SAL_CALL AccessibleTableShape::getSelectedAccessibleColumns( )
+{
+ sal_Int32 nColumn = getAccessibleColumnCount();
+ ::std::vector<bool> aSelected( nColumn, true );
+ sal_Int32 nCount = nColumn;
+ for( sal_Int32 i = 0; i < nColumn; i++ )
+ {
+ try
+ {
+ aSelected[i] = isAccessibleColumnSelected( i );
+ }
+ catch( ... )
+ {
+ return Sequence< sal_Int32 >();
+ }
+
+ if( !aSelected[i] )
+ nCount--;
+ }
+ Sequence < sal_Int32 > aRet( nCount );
+ sal_Int32 *pRet = aRet.getArray();
+ sal_Int32 nPos = 0;
+ size_t nSize = aSelected.size();
+ for( size_t i=0; i < nSize && nPos < nCount; i++ )
+ {
+ if( aSelected[i] )
+ {
+ *pRet++ = i;
+ nPos++;
+ }
+ }
+
+ return aRet;
+}
+
+
+sal_Bool SAL_CALL AccessibleTableShape::isAccessibleRowSelected( sal_Int32 nRow )
+{
+ SolarMutexGuard aSolarGuard;
+ checkCellPosition( 0, nRow );
+ SvxTableController* pController = getTableController();
+ if( pController )
+ {
+ return pController->isRowSelected( nRow );
+ }
+ return false;
+}
+
+
+sal_Bool SAL_CALL AccessibleTableShape::isAccessibleColumnSelected( sal_Int32 nColumn )
+{
+ SolarMutexGuard aSolarGuard;
+ checkCellPosition( nColumn, 0 );
+ SvxTableController* pController = getTableController();
+ if( pController )
+ {
+ return pController->isColumnSelected( nColumn );
+ }
+ return false;
+}
+
+
+Reference< XAccessible > SAL_CALL AccessibleTableShape::getAccessibleCellAt( sal_Int32 nRow, sal_Int32 nColumn )
+{
+ SolarMutexGuard aSolarGuard;
+ checkCellPosition( nColumn, nRow );
+
+ sal_Int32 nChildIndex = 0;
+ if( mxImpl->mxTable.is() )
+ nChildIndex = mxImpl->mxTable->getColumnCount() * nRow + nColumn;
+
+ return getAccessibleChild( nChildIndex );
+}
+
+
+Reference< XAccessible > SAL_CALL AccessibleTableShape::getAccessibleCaption( )
+{
+ Reference< XAccessible > xRet;
+ return xRet;
+}
+
+
+Reference< XAccessible > SAL_CALL AccessibleTableShape::getAccessibleSummary( )
+{
+ Reference< XAccessible > xRet;
+ return xRet;
+}
+
+
+sal_Bool SAL_CALL AccessibleTableShape::isAccessibleSelected( sal_Int32 nRow, sal_Int32 nColumn )
+{
+ SolarMutexGuard aSolarGuard;
+ checkCellPosition( nColumn, nRow );
+
+ SvxTableController* pController = getTableController();
+ if( pController && pController->hasSelectedCells() )
+ {
+ CellPos aFirstPos, aLastPos;
+ pController->getSelectedCells( aFirstPos, aLastPos );
+ if( (aFirstPos.mnRow <= nRow) && (aFirstPos.mnCol <= nColumn) && (nRow <= aLastPos.mnRow) && (nColumn <= aLastPos.mnCol) )
+ return true;
+ }
+
+ return false;
+}
+
+
+sal_Int32 SAL_CALL AccessibleTableShape::getAccessibleIndex( sal_Int32 nRow, sal_Int32 nColumn )
+{
+ SolarMutexGuard aSolarGuard;
+ checkCellPosition( nColumn, nRow );
+ return mxImpl->mxTable.is() ? (nRow * mxImpl->mxTable->getColumnCount() + nColumn) : 0;
+}
+
+
+sal_Int32 SAL_CALL AccessibleTableShape::getAccessibleRow( sal_Int32 nChildIndex )
+{
+ SolarMutexGuard aSolarGuard;
+ sal_Int32 nColumn = 0, nRow = 0;
+ mxImpl->getColumnAndRow( nChildIndex, nColumn, nRow );
+ return nRow;
+}
+
+
+sal_Int32 SAL_CALL AccessibleTableShape::getAccessibleColumn( sal_Int32 nChildIndex )
+{
+ SolarMutexGuard aSolarGuard;
+ sal_Int32 nColumn = 0, nRow = 0;
+ mxImpl->getColumnAndRow( nChildIndex, nColumn, nRow );
+ return nColumn;
+}
+
+
+// XAccessibleSelection
+
+
+void SAL_CALL AccessibleTableShape::selectAccessibleChild( sal_Int32 nChildIndex )
+{
+ SolarMutexGuard aSolarGuard;
+ CellPos aPos;
+ mxImpl->getColumnAndRow( nChildIndex, aPos.mnCol, aPos.mnRow );
+
+ // todo, select table shape?!?
+ SvxTableController* pController = getTableController();
+ if( !pController )
+ return;
+
+ CellPos aFirstPos( aPos ), aLastPos( aPos );
+ if( pController->hasSelectedCells() )
+ {
+ pController->getSelectedCells( aFirstPos, aLastPos );
+
+ aFirstPos.mnRow = std::min( aFirstPos.mnRow, aPos.mnRow );
+ aFirstPos.mnCol = std::min( aFirstPos.mnCol, aPos.mnCol );
+ aLastPos.mnRow = std::max( aLastPos.mnRow, aPos.mnRow );
+ aLastPos.mnCol = std::max( aLastPos.mnCol, aPos.mnCol );
+ }
+ pController->setSelectedCells( aFirstPos, aLastPos );
+}
+
+
+sal_Bool SAL_CALL AccessibleTableShape::isAccessibleChildSelected( sal_Int32 nChildIndex )
+{
+ SolarMutexGuard aSolarGuard;
+ CellPos aPos;
+ mxImpl->getColumnAndRow( nChildIndex, aPos.mnCol, aPos.mnRow );
+
+ return isAccessibleSelected(aPos.mnRow, aPos.mnCol);
+}
+
+
+void SAL_CALL AccessibleTableShape::clearAccessibleSelection()
+{
+ SolarMutexGuard aSolarGuard;
+
+ SvxTableController* pController = getTableController();
+ if( pController )
+ pController->clearSelection();
+}
+
+
+void SAL_CALL AccessibleTableShape::selectAllAccessibleChildren()
+{
+ SolarMutexGuard aSolarGuard;
+
+ // todo: force selection of shape?
+ SvxTableController* pController = getTableController();
+ if( pController )
+ pController->selectAll();
+}
+
+
+sal_Int32 SAL_CALL AccessibleTableShape::getSelectedAccessibleChildCount()
+{
+ SolarMutexGuard aSolarGuard;
+
+ SvxTableController* pController = getTableController();
+ if( pController && pController->hasSelectedCells() )
+ {
+ CellPos aFirstPos, aLastPos;
+ pController->getSelectedCells( aFirstPos, aLastPos );
+
+ const sal_Int32 nSelectedColumns = std::max( sal_Int32(0), aLastPos.mnCol - aFirstPos.mnCol ) + 1;
+ const sal_Int32 nSelectedRows = std::max( sal_Int32(0), aLastPos.mnRow - aFirstPos.mnRow ) + 1;
+ return nSelectedRows * nSelectedColumns;
+ }
+
+ return 0;
+}
+
+
+Reference< XAccessible > SAL_CALL AccessibleTableShape::getSelectedAccessibleChild( sal_Int32 nSelectedChildIndex )
+{
+ SolarMutexGuard aSolarGuard;
+
+ if( nSelectedChildIndex < 0 )
+ throw IndexOutOfBoundsException();
+
+ sal_Int32 nChildIndex = GetIndexOfSelectedChild( nSelectedChildIndex );
+
+ if( nChildIndex < 0 )
+ throw IndexOutOfBoundsException();
+
+ if ( nChildIndex >= getAccessibleChildCount() )
+ {
+ throw IndexOutOfBoundsException();
+ }
+
+ return getAccessibleChild( nChildIndex );
+}
+
+
+void SAL_CALL AccessibleTableShape::deselectAccessibleChild( sal_Int32 nChildIndex )
+{
+ SolarMutexGuard aSolarGuard;
+ CellPos aPos;
+ mxImpl->getColumnAndRow( nChildIndex, aPos.mnCol, aPos.mnRow );
+
+ // todo, select table shape?!?
+ SvxTableController* pController = getTableController();
+ if( !(pController && pController->hasSelectedCells()) )
+ return;
+
+ CellPos aFirstPos, aLastPos;
+ pController->getSelectedCells( aFirstPos, aLastPos );
+
+ // create a selection where aPos is not part of anymore
+ aFirstPos.mnRow = std::min( aFirstPos.mnRow, aPos.mnRow+1 );
+ aFirstPos.mnCol = std::min( aFirstPos.mnCol, aPos.mnCol+1 );
+ aLastPos.mnRow = std::max( aLastPos.mnRow, aPos.mnRow-1 );
+ aLastPos.mnCol = std::max( aLastPos.mnCol, aPos.mnCol-1 );
+
+ // new selection may be invalid (child to deselect is not at a border of the selection but in between)
+ if( (aFirstPos.mnRow > aLastPos.mnRow) || (aFirstPos.mnCol > aLastPos.mnCol) )
+ pController->clearSelection(); // if selection is invalid, clear all
+ else
+ pController->setSelectedCells( aFirstPos, aLastPos );
+}
+
+// XAccessibleTableSelection
+sal_Bool SAL_CALL AccessibleTableShape::selectRow( sal_Int32 row )
+{
+ SolarMutexGuard aSolarGuard;
+ SvxTableController* pController = getTableController();
+ if( !pController )
+ return false;
+ return pController->selectRow( row );
+}
+
+sal_Bool SAL_CALL AccessibleTableShape::selectColumn( sal_Int32 column )
+{
+ SolarMutexGuard aSolarGuard;
+ SvxTableController* pController = getTableController();
+ if( !pController )
+ return false;
+ return pController->selectColumn( column );
+}
+
+sal_Bool SAL_CALL AccessibleTableShape::unselectRow( sal_Int32 row )
+{
+ SolarMutexGuard aSolarGuard;
+ SvxTableController* pController = getTableController();
+ if( !pController )
+ return false;
+ return pController->deselectRow( row );
+}
+
+sal_Bool SAL_CALL AccessibleTableShape::unselectColumn( sal_Int32 column )
+{
+ SolarMutexGuard aSolarGuard;
+ SvxTableController* pController = getTableController();
+ if( !pController )
+ return false;
+ return pController->deselectColumn( column );
+}
+
+sal_Int32 AccessibleTableShape::GetIndexOfSelectedChild(
+ sal_Int32 nSelectedChildIndex ) const
+{
+ sal_Int32 nChildren = const_cast<AccessibleTableShape*>(this)->getAccessibleChildCount();
+
+ if( nSelectedChildIndex >= nChildren )
+ return -1;
+
+ sal_Int32 n = 0;
+ while( n < nChildren )
+ {
+ if( const_cast<AccessibleTableShape*>(this)->isAccessibleChildSelected( n ) )
+ {
+ if( 0 == nSelectedChildIndex )
+ break;
+ else
+ --nSelectedChildIndex;
+ }
+ ++n;
+ }
+
+ return n < nChildren ? n : -1;
+}
+void AccessibleTableShape::getColumnAndRow( sal_Int32 nChildIndex, sal_Int32& rnColumn, sal_Int32& rnRow )
+{
+ mxImpl->getColumnAndRow(nChildIndex, rnColumn, rnRow);
+}
+
+// XSelectionChangeListener
+void SAL_CALL
+ AccessibleTableShape::disposing (const EventObject& aEvent)
+{
+ AccessibleShape::disposing(aEvent);
+}
+void SAL_CALL AccessibleTableShape::selectionChanged (const EventObject& rEvent)
+{
+ //sdr::table::CellRef xCellRef = static_cast< sdr::table::CellRef > (rEvent.Source);
+ Reference< XCell > xCell(rEvent.Source, UNO_QUERY);
+ if (!xCell.is())
+ return;
+
+ rtl::Reference< AccessibleCell > xAccCell = mxImpl->getAccessibleCell( xCell );
+ if (!xAccCell.is())
+ return;
+
+ sal_Int32 nIndex = xAccCell->getAccessibleIndexInParent(),
+ nCount = getSelectedAccessibleChildCount();
+ bool bSelected = isAccessibleChildSelected(nIndex);
+ if (mnPreviousSelectionCount == 0 && nCount > 0 && bSelected)
+ {
+ xAccCell->SetState(AccessibleStateType::SELECTED);
+ xAccCell->CommitChange(AccessibleEventId::SELECTION_CHANGED, Any(), Any());
+ }
+ else if (bSelected)
+ {
+ xAccCell->SetState(AccessibleStateType::SELECTED);
+ xAccCell->CommitChange(AccessibleEventId::SELECTION_CHANGED_ADD, Any(), Any());
+ }
+ else
+ {
+ xAccCell->ResetState(AccessibleStateType::SELECTED);
+ xAccCell->CommitChange(AccessibleEventId::SELECTION_CHANGED_REMOVE, Any(), Any());
+ }
+ mnPreviousSelectionCount = nCount;
+}
+// Get the currently active cell which is text editing
+AccessibleCell* AccessibleTableShape::GetActiveAccessibleCell()
+{
+ rtl::Reference< AccessibleCell > xAccCell;
+ AccessibleCell* pAccCell = nullptr;
+ SvxTableController* pController = getTableController();
+ if (pController)
+ {
+ sdr::table::SdrTableObj* pTableObj = pController->GetTableObj();
+ if ( pTableObj )
+ {
+ const sdr::table::CellRef& xCellRef (pTableObj->getActiveCell());
+ if ( xCellRef.is() )
+ {
+ try
+ {
+ CellPos rPos;
+ pTableObj->getActiveCellPos( rPos );
+ xAccCell = mxImpl->getAccessibleCell( rPos.mnRow, rPos.mnCol );
+ if ( xAccCell.is() )
+ pAccCell = xAccCell.get();
+ }
+ catch ( IndexOutOfBoundsException& ) {}
+ }
+ }
+ }
+ return pAccCell;
+}
+
+//If current active cell is in editing, the focus state should be set to internal text
+bool AccessibleTableShape::SetState (sal_Int16 aState)
+{
+ if( aState == AccessibleStateType::FOCUSED )
+ {
+ AccessibleCell* pActiveAccessibleCell = GetActiveAccessibleCell();
+ if( pActiveAccessibleCell != nullptr )
+ return pActiveAccessibleCell->SetState(aState);
+ }
+
+ return AccessibleShape::SetState (aState);
+}
+
+//If current active cell is in editing, the focus state should be reset to internal text
+bool AccessibleTableShape::ResetState (sal_Int16 aState)
+{
+ if( aState == AccessibleStateType::FOCUSED )
+ {
+ AccessibleCell* pActiveAccessibleCell = GetActiveAccessibleCell();
+ if( pActiveAccessibleCell != nullptr )
+ return pActiveAccessibleCell->ResetState(aState);
+ }
+
+ return AccessibleShape::ResetState (aState);
+}
+
+bool AccessibleTableShape::SetStateDirectly (sal_Int16 aState)
+{
+ return AccessibleContextBase::SetState (aState);
+}
+
+bool AccessibleTableShape::ResetStateDirectly (sal_Int16 aState)
+{
+ return AccessibleContextBase::ResetState (aState);
+}
+
+void AccessibleTableShape::checkCellPosition( sal_Int32 nCol, sal_Int32 nRow )
+{
+ if( (nCol >= 0) && (nRow >= 0) && mxImpl->mxTable.is() && (nCol < mxImpl->mxTable->getColumnCount()) && (nRow < mxImpl->mxTable->getRowCount()) )
+ return;
+
+ throw IndexOutOfBoundsException();
+}
+
+AccessibleTableHeaderShape::AccessibleTableHeaderShape( AccessibleTableShape* pTable, bool bRow )
+{
+ mpTable = pTable;
+ mbRow = bRow;
+}
+
+AccessibleTableHeaderShape::~AccessibleTableHeaderShape()
+{
+ mpTable = nullptr;
+}
+
+// XAccessible
+Reference< XAccessibleContext > SAL_CALL AccessibleTableHeaderShape::getAccessibleContext()
+{
+ return this;
+}
+
+// XAccessibleContext
+sal_Int32 SAL_CALL AccessibleTableHeaderShape::getAccessibleChildCount( )
+{
+ return getAccessibleRowCount() * getAccessibleColumnCount();
+}
+
+Reference< XAccessible > SAL_CALL AccessibleTableHeaderShape::getAccessibleChild( sal_Int32 i )
+{
+ return mpTable->getAccessibleChild( i );
+}
+
+Reference< XAccessible > SAL_CALL AccessibleTableHeaderShape::getAccessibleParent()
+{
+ Reference< XAccessible > XParent;
+ return XParent;
+}
+
+sal_Int32 SAL_CALL AccessibleTableHeaderShape::getAccessibleIndexInParent()
+{
+ return -1;
+}
+
+sal_Int16 SAL_CALL AccessibleTableHeaderShape::getAccessibleRole()
+{
+ return mpTable->getAccessibleRole();
+}
+
+OUString SAL_CALL AccessibleTableHeaderShape::getAccessibleDescription()
+{
+ return mpTable->getAccessibleDescription();
+}
+
+OUString SAL_CALL AccessibleTableHeaderShape::getAccessibleName()
+{
+ return mpTable->getAccessibleName();
+}
+
+Reference< XAccessibleStateSet > SAL_CALL AccessibleTableHeaderShape::getAccessibleStateSet()
+{
+ return mpTable->getAccessibleStateSet();
+}
+
+Reference< XAccessibleRelationSet > SAL_CALL AccessibleTableHeaderShape::getAccessibleRelationSet()
+{
+ return mpTable->getAccessibleRelationSet();
+}
+
+Locale SAL_CALL AccessibleTableHeaderShape::getLocale()
+{
+ return mpTable->getLocale();
+}
+
+//XAccessibleComponent
+sal_Bool SAL_CALL AccessibleTableHeaderShape::containsPoint ( const css::awt::Point& aPoint )
+{
+ return mpTable->containsPoint( aPoint );
+}
+
+Reference< XAccessible > SAL_CALL AccessibleTableHeaderShape::getAccessibleAtPoint ( const css::awt::Point& aPoint)
+{
+ return mpTable->getAccessibleAtPoint( aPoint );
+}
+
+css::awt::Rectangle SAL_CALL AccessibleTableHeaderShape::getBounds()
+{
+ return mpTable->getBounds();
+}
+
+css::awt::Point SAL_CALL AccessibleTableHeaderShape::getLocation()
+{
+ return mpTable->getLocation();
+}
+
+css::awt::Point SAL_CALL AccessibleTableHeaderShape::getLocationOnScreen()
+{
+ return mpTable->getLocationOnScreen();
+}
+
+css::awt::Size SAL_CALL AccessibleTableHeaderShape::getSize()
+{
+ return mpTable->getSize();
+}
+
+sal_Int32 SAL_CALL AccessibleTableHeaderShape::getForeground()
+{
+ return mpTable->getForeground();
+}
+
+sal_Int32 SAL_CALL AccessibleTableHeaderShape::getBackground()
+{
+ return mpTable->getBackground();
+}
+
+void SAL_CALL AccessibleTableHeaderShape::grabFocus()
+{
+ mpTable->grabFocus();
+}
+// XAccessibleTable
+sal_Int32 SAL_CALL AccessibleTableHeaderShape::getAccessibleRowCount()
+{
+ return mbRow ? 1 : mpTable->getAccessibleRowCount();
+}
+
+sal_Int32 SAL_CALL AccessibleTableHeaderShape::getAccessibleColumnCount()
+{
+ return !mbRow ? 1 : mpTable->getAccessibleColumnCount();
+}
+
+OUString SAL_CALL AccessibleTableHeaderShape::getAccessibleRowDescription( sal_Int32 nRow )
+{
+ return mpTable->getAccessibleRowDescription( nRow );
+}
+
+OUString SAL_CALL AccessibleTableHeaderShape::getAccessibleColumnDescription( sal_Int32 nColumn )
+{
+ return mpTable->getAccessibleColumnDescription( nColumn );
+}
+
+sal_Int32 SAL_CALL AccessibleTableHeaderShape::getAccessibleRowExtentAt( sal_Int32 nRow, sal_Int32 nColumn )
+{
+ return mpTable->getAccessibleRowExtentAt( nRow, nColumn );
+}
+
+sal_Int32 SAL_CALL AccessibleTableHeaderShape::getAccessibleColumnExtentAt( sal_Int32 nRow, sal_Int32 nColumn )
+{
+ return mpTable->getAccessibleColumnExtentAt( nRow, nColumn );
+}
+
+Reference< XAccessibleTable > SAL_CALL AccessibleTableHeaderShape::getAccessibleRowHeaders( )
+{
+ Reference< XAccessibleTable > xRet;
+ return xRet;
+}
+
+Reference< XAccessibleTable > SAL_CALL AccessibleTableHeaderShape::getAccessibleColumnHeaders( )
+{
+ Reference< XAccessibleTable > xRet;
+ return xRet;
+}
+
+Sequence< sal_Int32 > SAL_CALL AccessibleTableHeaderShape::getSelectedAccessibleRows( )
+{
+ sal_Int32 nRow = getAccessibleRowCount();
+ ::std::vector<bool> aSelected( nRow, true );
+ sal_Int32 nCount = nRow;
+ for( sal_Int32 i = 0; i < nRow; i++ )
+ {
+ try
+ {
+ aSelected[i] = isAccessibleRowSelected( i );
+ }
+ catch( ... )
+ {
+ return Sequence< sal_Int32 >();
+ }
+
+ if( !aSelected[i] )
+ nCount--;
+ }
+ Sequence < sal_Int32 > aRet( nCount );
+ sal_Int32 *pRet = aRet.getArray();
+ sal_Int32 nPos = 0;
+ size_t nSize = aSelected.size();
+ for( size_t i=0; i < nSize && nPos < nCount; i++ )
+ {
+ if( aSelected[i] )
+ {
+ *pRet++ = i;
+ nPos++;
+ }
+ }
+
+ return aRet;
+}
+
+Sequence< sal_Int32 > SAL_CALL AccessibleTableHeaderShape::getSelectedAccessibleColumns( )
+{
+ sal_Int32 nColumn = getAccessibleColumnCount();
+ ::std::vector<bool> aSelected( nColumn, true );
+ sal_Int32 nCount = nColumn;
+ for( sal_Int32 i = 0; i < nColumn; i++ )
+ {
+ try
+ {
+ aSelected[i] = isAccessibleColumnSelected( i );
+ }
+ catch( ... )
+ {
+ return Sequence< sal_Int32 >();
+ }
+
+ if( !aSelected[i] )
+ nCount--;
+ }
+ Sequence < sal_Int32 > aRet( nCount );
+ sal_Int32 *pRet = aRet.getArray();
+ sal_Int32 nPos = 0;
+ size_t nSize = aSelected.size();
+ for( size_t i=0; i < nSize && nPos < nCount; i++ )
+ {
+ if( aSelected[i] )
+ {
+ *pRet++ = i;
+ nPos++;
+ }
+ }
+
+ return aRet;
+}
+
+sal_Bool SAL_CALL AccessibleTableHeaderShape::isAccessibleRowSelected( sal_Int32 nRow )
+{
+ return mpTable->isAccessibleRowSelected( nRow );
+}
+
+sal_Bool SAL_CALL AccessibleTableHeaderShape::isAccessibleColumnSelected( sal_Int32 nColumn )
+{
+ return mpTable->isAccessibleColumnSelected( nColumn );
+}
+
+Reference< XAccessible > SAL_CALL AccessibleTableHeaderShape::getAccessibleCellAt( sal_Int32 nRow, sal_Int32 nColumn )
+{
+ return mpTable->getAccessibleCellAt( nRow, nColumn );
+}
+
+Reference< XAccessible > SAL_CALL AccessibleTableHeaderShape::getAccessibleCaption( )
+{
+ return mpTable->getAccessibleCaption();
+}
+
+Reference< XAccessible > SAL_CALL AccessibleTableHeaderShape::getAccessibleSummary( )
+{
+ return mpTable->getAccessibleSummary();
+}
+
+sal_Bool SAL_CALL AccessibleTableHeaderShape::isAccessibleSelected( sal_Int32 nRow, sal_Int32 nColumn )
+{
+ return mpTable->isAccessibleSelected( nRow, nColumn );
+}
+
+sal_Int32 SAL_CALL AccessibleTableHeaderShape::getAccessibleIndex( sal_Int32 nRow, sal_Int32 nColumn )
+{
+ return mpTable->getAccessibleIndex( nRow, nColumn );
+}
+
+sal_Int32 SAL_CALL AccessibleTableHeaderShape::getAccessibleRow( sal_Int32 nChildIndex )
+{
+ return mpTable->getAccessibleRow( nChildIndex );
+}
+
+sal_Int32 SAL_CALL AccessibleTableHeaderShape::getAccessibleColumn( sal_Int32 nChildIndex )
+{
+ return mpTable->getAccessibleColumn( nChildIndex );
+}
+
+// XAccessibleTableSelection
+sal_Bool SAL_CALL AccessibleTableHeaderShape::selectRow( sal_Int32 row )
+{
+ if( mbRow )
+ return mpTable->selectRow( row );
+ else
+ {
+ mpTable->clearAccessibleSelection();
+ sal_Int32 nIndex = mpTable->getAccessibleIndex( row, 0 );
+ mpTable->selectAccessibleChild( nIndex );
+ return true;
+ }
+}
+
+sal_Bool SAL_CALL AccessibleTableHeaderShape::selectColumn( sal_Int32 column )
+{
+ if( !mbRow )
+ return mpTable->selectColumn( column );
+ else
+ {
+ mpTable->clearAccessibleSelection();
+ sal_Int32 nIndex = mpTable->getAccessibleIndex( 0, column );
+ mpTable->selectAccessibleChild( nIndex );
+ return true;
+ }
+}
+
+sal_Bool SAL_CALL AccessibleTableHeaderShape::unselectRow( sal_Int32 row )
+{
+ if( mbRow )
+ return mpTable->unselectRow( row );
+ else
+ {
+ sal_Int32 nIndex = mpTable->getAccessibleIndex( row, 0 );
+ mpTable->deselectAccessibleChild( nIndex );
+ return true;
+ }
+}
+
+sal_Bool SAL_CALL AccessibleTableHeaderShape::unselectColumn( sal_Int32 column )
+{
+ if( !mbRow )
+ return mpTable->unselectColumn( column );
+ else
+ {
+ sal_Int32 nIndex = mpTable->getAccessibleIndex( 0, column );
+ mpTable->deselectAccessibleChild( nIndex );
+ return true;
+ }
+}
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/table/cell.cxx b/svx/source/table/cell.cxx
new file mode 100644
index 000000000..ba335ca27
--- /dev/null
+++ b/svx/source/table/cell.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/drawing/BitmapMode.hpp>
+#include <com/sun/star/style/XStyle.hpp>
+#include <com/sun/star/text/WritingMode.hpp>
+#include <com/sun/star/table/TableBorder.hpp>
+#include <com/sun/star/table/BorderLine2.hpp>
+#include <com/sun/star/lang/Locale.hpp>
+
+#include <comphelper/sequence.hxx>
+#include <o3tl/any.hxx>
+#include <svl/style.hxx>
+#include <svl/itemset.hxx>
+
+#include <vcl/svapp.hxx>
+#include <libxml/xmlwriter.h>
+
+#include <sdr/properties/textproperties.hxx>
+#include <editeng/outlobj.hxx>
+#include <editeng/writingmodeitem.hxx>
+#include <svx/svdotable.hxx>
+#include <svx/svdoutl.hxx>
+#include <svx/unoshtxt.hxx>
+#include <svx/svdmodel.hxx>
+#include <svx/sdooitm.hxx>
+#include <svx/sdtagitm.hxx>
+#include <svx/sdmetitm.hxx>
+#include <svx/xit.hxx>
+#include <getallcharpropids.hxx>
+#include "tableundo.hxx"
+#include <cell.hxx>
+#include <svx/unoshprp.hxx>
+#include <svx/unoshape.hxx>
+#include <editeng/editobj.hxx>
+#include <editeng/borderline.hxx>
+#include <editeng/boxitem.hxx>
+#include <editeng/charrotateitem.hxx>
+#include <svx/xflbstit.hxx>
+#include <svx/xflbmtit.hxx>
+#include <svx/svdpool.hxx>
+#include <svx/xflclit.hxx>
+#include <tools/diagnose_ex.h>
+
+
+using ::editeng::SvxBorderLine;
+using namespace ::com::sun::star::uno;
+using namespace ::com::sun::star::beans;
+using namespace ::com::sun::star::lang;
+using namespace ::com::sun::star::text;
+using namespace ::com::sun::star::table;
+using namespace ::com::sun::star::drawing;
+using namespace ::com::sun::star::style;
+using namespace ::com::sun::star::container;
+
+
+static const SvxItemPropertySet* ImplGetSvxCellPropertySet()
+{
+ // property map for an outliner text
+ static const SfxItemPropertyMapEntry aSvxCellPropertyMap[] =
+ {
+ FILL_PROPERTIES
+// { "HasLevels", OWN_ATTR_HASLEVELS, cppu::UnoType<bool>::get(), css::beans::PropertyAttribute::READONLY, 0},
+ { u"Style", OWN_ATTR_STYLE, cppu::UnoType< css::style::XStyle >::get(), css::beans::PropertyAttribute::MAYBEVOID, 0},
+ { UNO_NAME_TEXT_WRITINGMODE, SDRATTR_TEXTDIRECTION, cppu::UnoType<css::text::WritingMode>::get(), 0, 0},
+ { UNO_NAME_TEXT_HORZADJUST, SDRATTR_TEXT_HORZADJUST, cppu::UnoType<css::drawing::TextHorizontalAdjust>::get(), 0, 0},
+ { UNO_NAME_TEXT_LEFTDIST, SDRATTR_TEXT_LEFTDIST, cppu::UnoType<sal_Int32>::get(), 0, 0, PropertyMoreFlags::METRIC_ITEM},
+ { UNO_NAME_TEXT_LOWERDIST, SDRATTR_TEXT_LOWERDIST, cppu::UnoType<sal_Int32>::get(), 0, 0, PropertyMoreFlags::METRIC_ITEM},
+ { UNO_NAME_TEXT_RIGHTDIST, SDRATTR_TEXT_RIGHTDIST, cppu::UnoType<sal_Int32>::get(), 0, 0, PropertyMoreFlags::METRIC_ITEM},
+ { UNO_NAME_TEXT_UPPERDIST, SDRATTR_TEXT_UPPERDIST, cppu::UnoType<sal_Int32>::get(), 0, 0, PropertyMoreFlags::METRIC_ITEM},
+ { UNO_NAME_TEXT_VERTADJUST, SDRATTR_TEXT_VERTADJUST, cppu::UnoType<css::drawing::TextVerticalAdjust>::get(), 0, 0},
+ { UNO_NAME_TEXT_WORDWRAP, SDRATTR_TEXT_WORDWRAP, cppu::UnoType<bool>::get(), 0, 0},
+
+ { u"TableBorder", OWN_ATTR_TABLEBORDER, cppu::UnoType<TableBorder>::get(), 0, 0 },
+ { u"TopBorder", SDRATTR_TABLE_BORDER, cppu::UnoType<BorderLine>::get(), 0, TOP_BORDER },
+ { u"BottomBorder", SDRATTR_TABLE_BORDER, cppu::UnoType<BorderLine>::get(), 0, BOTTOM_BORDER },
+ { u"LeftBorder", SDRATTR_TABLE_BORDER, cppu::UnoType<BorderLine>::get(), 0, LEFT_BORDER },
+ { u"RightBorder", SDRATTR_TABLE_BORDER, cppu::UnoType<BorderLine>::get(), 0, RIGHT_BORDER },
+ { u"RotateAngle", SDRATTR_TABLE_TEXT_ROTATION, cppu::UnoType<sal_Int32>::get(), 0, 0 },
+
+ SVX_UNOEDIT_OUTLINER_PROPERTIES,
+ SVX_UNOEDIT_CHAR_PROPERTIES,
+ SVX_UNOEDIT_PARA_PROPERTIES,
+ { u"", 0, css::uno::Type(), 0, 0 }
+ };
+
+ static SvxItemPropertySet aSvxCellPropertySet( aSvxCellPropertyMap, SdrObject::GetGlobalDrawObjectItemPool() );
+ return &aSvxCellPropertySet;
+}
+
+namespace
+{
+
+class CellTextProvider : public svx::ITextProvider
+{
+public:
+ explicit CellTextProvider(const sdr::table::CellRef& rCell);
+ virtual ~CellTextProvider();
+
+private:
+ virtual sal_Int32 getTextCount() const override;
+ virtual SdrText* getText(sal_Int32 nIndex) const override;
+
+private:
+ const sdr::table::CellRef m_xCell;
+};
+
+CellTextProvider::CellTextProvider(const sdr::table::CellRef& rCell)
+ : m_xCell(rCell)
+{
+}
+
+CellTextProvider::~CellTextProvider()
+{
+}
+
+sal_Int32 CellTextProvider::getTextCount() const
+{
+ return 1;
+}
+
+SdrText* CellTextProvider::getText(sal_Int32 nIndex) const
+{
+ (void) nIndex;
+ assert(nIndex == 0);
+ return m_xCell.get();
+}
+
+}
+
+namespace sdr::properties
+{
+ class CellProperties : public TextProperties
+ {
+ protected:
+ // create a new itemset
+ SfxItemSet CreateObjectSpecificItemSet(SfxItemPool& rPool) override;
+
+ const svx::ITextProvider& getTextProvider() const override;
+
+ public:
+ // basic constructor
+ CellProperties(SdrObject& rObj, sdr::table::Cell* pCell );
+
+ // constructor for copying, but using new object
+ CellProperties(const CellProperties& rProps, SdrObject& rObj, sdr::table::Cell* pCell);
+
+ // Clone() operator, normally just calls the local copy constructor
+ std::unique_ptr<BaseProperties> Clone(SdrObject& rObj) const override;
+
+ void ForceDefaultAttributes() override;
+
+ void ItemSetChanged(o3tl::span< const SfxPoolItem* const > aChangedItems, sal_uInt16 nDeletedWhich) override;
+
+ void ItemChange(const sal_uInt16 nWhich, const SfxPoolItem* pNewItem = nullptr) override;
+
+ sdr::table::CellRef mxCell;
+
+ private:
+ const CellTextProvider maTextProvider;
+ };
+
+ // create a new itemset
+ SfxItemSet CellProperties::CreateObjectSpecificItemSet(SfxItemPool& rPool)
+ {
+ return SfxItemSet(rPool,
+
+ // range from SdrAttrObj
+ svl::Items<SDRATTR_START, SDRATTR_SHADOW_LAST,
+ SDRATTR_MISC_FIRST, SDRATTR_MISC_LAST,
+ SDRATTR_TEXTDIRECTION, SDRATTR_TEXTDIRECTION,
+
+ // range for SdrTableObj
+ SDRATTR_TABLE_FIRST, SDRATTR_TABLE_LAST,
+
+ // range from SdrTextObj
+ EE_ITEMS_START, EE_ITEMS_END>);
+ }
+
+ const svx::ITextProvider& CellProperties::getTextProvider() const
+ {
+ return maTextProvider;
+ }
+
+ CellProperties::CellProperties(SdrObject& rObj, sdr::table::Cell* pCell)
+ : TextProperties(rObj)
+ , mxCell(pCell)
+ , maTextProvider(mxCell)
+ {
+ }
+
+ CellProperties::CellProperties(const CellProperties& rProps, SdrObject& rObj, sdr::table::Cell* pCell)
+ : TextProperties(rProps, rObj)
+ , mxCell( pCell )
+ , maTextProvider(mxCell)
+ {
+ }
+
+ std::unique_ptr<BaseProperties> CellProperties::Clone(SdrObject& rObj) const
+ {
+ OSL_FAIL("CellProperties::Clone(), does not work yet!");
+ return std::unique_ptr<BaseProperties>(new CellProperties(*this, rObj,nullptr));
+ }
+
+ void CellProperties::ForceDefaultAttributes()
+ {
+ }
+
+ void CellProperties::ItemSetChanged(o3tl::span< const SfxPoolItem* const > aChangedItems, sal_uInt16 nDeletedWhich)
+ {
+ SdrTextObj& rObj = static_cast<SdrTextObj&>(GetSdrObject());
+
+ if( mxCell.is() )
+ {
+ std::optional<OutlinerParaObject> pParaObj = mxCell->CreateEditOutlinerParaObject();
+
+ if( !pParaObj && mxCell->GetOutlinerParaObject())
+ pParaObj = *mxCell->GetOutlinerParaObject();
+
+ if(pParaObj)
+ {
+ // handle outliner attributes
+ Outliner* pOutliner = nullptr;
+
+ if(mxCell->IsTextEditActive())
+ {
+ pOutliner = rObj.GetTextEditOutliner();
+ }
+ else
+ {
+ pOutliner = &rObj.ImpGetDrawOutliner();
+ pOutliner->SetText(*pParaObj);
+ }
+
+ sal_Int32 nParaCount(pOutliner->GetParagraphCount());
+
+ // if the user sets character attributes to the complete
+ // cell we want to remove all hard set character attributes
+ // with same which ids from the text
+ std::vector<sal_uInt16> aCharWhichIds(GetAllCharPropIds(aChangedItems));
+
+ for(sal_Int32 nPara = 0; nPara < nParaCount; nPara++)
+ {
+ SfxItemSet aSet(pOutliner->GetParaAttribs(nPara));
+ for (const SfxPoolItem* pItem : aChangedItems)
+ aSet.Put(*pItem);
+ if (nDeletedWhich)
+ aSet.ClearItem(nDeletedWhich);
+
+ for (const auto& rWhichId : aCharWhichIds)
+ {
+ pOutliner->RemoveCharAttribs(nPara, rWhichId);
+ }
+
+ pOutliner->SetParaAttribs(nPara, aSet);
+ }
+
+ if(!mxCell->IsTextEditActive())
+ {
+ if(nParaCount)
+ {
+ // force ItemSet
+ GetObjectItemSet();
+
+ SfxItemSet aNewSet(pOutliner->GetParaAttribs(0));
+ mxItemSet->Put(aNewSet);
+ }
+
+ std::optional<OutlinerParaObject> pTemp = pOutliner->CreateParaObject(0, nParaCount);
+ pOutliner->Clear();
+ mxCell->SetOutlinerParaObject(std::move(pTemp));
+ }
+
+ }
+ }
+
+ // call parent
+ AttributeProperties::ItemSetChanged(aChangedItems, nDeletedWhich);
+
+ if( mxCell.is() )
+ mxCell->notifyModified();
+ }
+
+ void CellProperties::ItemChange(const sal_uInt16 nWhich, const SfxPoolItem* pNewItem)
+ {
+ if(pNewItem && (SDRATTR_TEXTDIRECTION == nWhich))
+ {
+ bool bVertical(css::text::WritingMode_TB_RL == static_cast<const SvxWritingModeItem*>(pNewItem)->GetValue());
+
+ sdr::table::SdrTableObj& rObj = static_cast<sdr::table::SdrTableObj&>(GetSdrObject());
+ rObj.SetVerticalWriting(bVertical);
+
+ // Set a cell vertical property
+ std::optional<OutlinerParaObject> pEditParaObj = mxCell->CreateEditOutlinerParaObject();
+
+ if( !pEditParaObj && mxCell->GetOutlinerParaObject() )
+ {
+ OutlinerParaObject* pParaObj = mxCell->GetOutlinerParaObject();
+ if(pParaObj)
+ pParaObj->SetVertical(bVertical);
+ }
+ }
+
+ if (pNewItem && (SDRATTR_TABLE_TEXT_ROTATION == nWhich))
+ {
+ const SvxTextRotateItem* pRotateItem = static_cast<const SvxTextRotateItem*>(pNewItem);
+
+ // Set a cell vertical property
+ std::optional<OutlinerParaObject> pEditParaObj = mxCell->CreateEditOutlinerParaObject();
+
+ if (!pEditParaObj && mxCell->GetOutlinerParaObject())
+ {
+ OutlinerParaObject* pParaObj = mxCell->GetOutlinerParaObject();
+ if (pParaObj)
+ {
+ if(pRotateItem->IsVertical() && pRotateItem->IsTopToBottom())
+ pParaObj->SetRotation(TextRotation::TOPTOBOTTOM);
+ else if (pRotateItem->IsVertical())
+ pParaObj->SetRotation(TextRotation::BOTTOMTOTOP);
+ else
+ pParaObj->SetRotation(TextRotation::NONE);
+ }
+ }
+
+ // Change autogrow direction
+ SdrTextObj& rObj = static_cast<SdrTextObj&>(GetSdrObject());
+
+ // rescue object size
+ tools::Rectangle aObjectRect = rObj.GetSnapRect();
+
+ const SfxItemSet& rSet = rObj.GetObjectItemSet();
+ bool bAutoGrowWidth = rSet.Get(SDRATTR_TEXT_AUTOGROWWIDTH).GetValue();
+ bool bAutoGrowHeight = rSet.Get(SDRATTR_TEXT_AUTOGROWHEIGHT).GetValue();
+
+ // prepare ItemSet to set exchanged width and height items
+ SfxItemSetFixed<SDRATTR_TEXT_AUTOGROWHEIGHT, SDRATTR_TEXT_AUTOGROWHEIGHT> aNewSet(*rSet.GetPool());
+
+ aNewSet.Put(rSet);
+ aNewSet.Put(makeSdrTextAutoGrowWidthItem(bAutoGrowHeight));
+ aNewSet.Put(makeSdrTextAutoGrowHeightItem(bAutoGrowWidth));
+ rObj.SetObjectItemSet(aNewSet);
+
+ // restore object size
+ rObj.SetSnapRect(aObjectRect);
+ }
+
+ // call parent
+ AttributeProperties::ItemChange( nWhich, pNewItem );
+ }
+
+} // end of namespace sdr::properties
+
+namespace sdr::table {
+
+
+// Cell
+
+
+rtl::Reference< Cell > Cell::create( SdrTableObj& rTableObj )
+{
+ rtl::Reference< Cell > xCell( new Cell( rTableObj ) );
+ if( xCell->mxTable.is() )
+ {
+ xCell->mxTable->addEventListener( xCell );
+ }
+ return xCell;
+}
+
+
+Cell::Cell(
+ SdrTableObj& rTableObj)
+: SdrText(rTableObj)
+ ,SvxUnoTextBase( ImplGetSvxUnoOutlinerTextCursorSvxPropertySet() )
+ ,mpPropSet( ImplGetSvxCellPropertySet() )
+ ,mpProperties( new sdr::properties::CellProperties( rTableObj, this ) )
+ ,mnCellContentType( CellContentType_EMPTY )
+ ,mfValue( 0.0 )
+ ,mnError( 0 )
+ ,mbMerged( false )
+ ,mnRowSpan( 1 )
+ ,mnColSpan( 1 )
+ ,mxTable( rTableObj.getTable() )
+{
+ // Caution: Old SetModel() indirectly did a very necessary thing here,
+ // it created a valid SvxTextEditSource which is needed to bind contained
+ // Text to the UNO API and thus to save/load and more. Added version without
+ // model change.
+ // Also done was (not needed, for reference):
+ // SetStyleSheet( nullptr, true );
+ // ForceOutlinerParaObject( OutlinerMode::TextObject );
+ if(nullptr == GetEditSource())
+ {
+ SetEditSource(new SvxTextEditSource(&GetObject(), this));
+ }
+}
+
+Cell::~Cell() COVERITY_NOEXCEPT_FALSE
+{
+ dispose();
+}
+
+void Cell::dispose()
+{
+ if( mxTable.is() )
+ {
+ try
+ {
+ Reference< XEventListener > xThis( this );
+ mxTable->removeEventListener( xThis );
+ }
+ catch( Exception& )
+ {
+ TOOLS_WARN_EXCEPTION("svx.table", "");
+ }
+ mxTable.clear();
+ }
+
+ // tdf#118199 avoid double dispose, detect by using mpProperties
+ // as indicator. Only use SetOutlinerParaObject once
+ if( mpProperties )
+ {
+ mpProperties.reset();
+ SetOutlinerParaObject( std::nullopt );
+ }
+}
+
+void Cell::merge( sal_Int32 nColumnSpan, sal_Int32 nRowSpan )
+{
+ if ((mnColSpan != nColumnSpan) || (mnRowSpan != nRowSpan) || mbMerged)
+ {
+ mnColSpan = nColumnSpan;
+ mnRowSpan = nRowSpan;
+ mbMerged = false;
+ notifyModified();
+ }
+}
+
+
+void Cell::mergeContent( const CellRef& xSourceCell )
+{
+ SdrTableObj& rTableObj = dynamic_cast< SdrTableObj& >( GetObject() );
+
+ if( !xSourceCell->hasText() )
+ return;
+
+ SdrOutliner& rOutliner=rTableObj.ImpGetDrawOutliner();
+ rOutliner.SetUpdateLayout(true);
+
+ if( hasText() )
+ {
+ rOutliner.SetText(*GetOutlinerParaObject());
+ rOutliner.AddText(*xSourceCell->GetOutlinerParaObject());
+ }
+ else
+ {
+ rOutliner.SetText(*xSourceCell->GetOutlinerParaObject());
+ }
+
+ SetOutlinerParaObject( rOutliner.CreateParaObject() );
+ rOutliner.Clear();
+ xSourceCell->SetOutlinerParaObject(rOutliner.CreateParaObject());
+ rOutliner.Clear();
+ SetStyleSheet( GetStyleSheet(), true );
+}
+
+
+void Cell::cloneFrom( const CellRef& xCell )
+{
+ if( xCell.is() )
+ {
+ replaceContentAndFormatting( xCell );
+
+ mnCellContentType = xCell->mnCellContentType;
+
+ msFormula = xCell->msFormula;
+ mfValue = xCell->mfValue;
+ mnError = xCell->mnError;
+
+ mbMerged = xCell->mbMerged;
+ mnRowSpan = xCell->mnRowSpan;
+ mnColSpan = xCell->mnColSpan;
+
+ }
+ notifyModified();
+}
+
+void Cell::replaceContentAndFormatting( const CellRef& xSourceCell )
+{
+ if( !(xSourceCell.is() && mpProperties) )
+ return;
+
+ mpProperties->SetMergedItemSet( xSourceCell->GetObjectItemSet() );
+
+ // tdf#118354 OutlinerParaObject may be nullptr, do not dereference when
+ // not set (!)
+ if(xSourceCell->GetOutlinerParaObject())
+ {
+ SetOutlinerParaObject( *xSourceCell->GetOutlinerParaObject() );
+ }
+
+ SdrTableObj& rTableObj = dynamic_cast< SdrTableObj& >( GetObject() );
+ SdrTableObj& rSourceTableObj = dynamic_cast< SdrTableObj& >( xSourceCell->GetObject() );
+
+ if(&rSourceTableObj.getSdrModelFromSdrObject() != &rTableObj.getSdrModelFromSdrObject())
+ {
+ // TTTT should not happen - if, then a clone may be needed
+ // Maybe add an assertion here later
+ SetStyleSheet( nullptr, true );
+ }
+}
+
+
+void Cell::setMerged()
+{
+ if( !mbMerged )
+ {
+ mbMerged = true;
+ notifyModified();
+ }
+}
+
+
+void Cell::copyFormatFrom( const CellRef& xSourceCell )
+{
+ if( !(xSourceCell.is() && mpProperties) )
+ return;
+
+ mpProperties->SetMergedItemSet( xSourceCell->GetObjectItemSet() );
+ SdrTableObj& rTableObj = dynamic_cast< SdrTableObj& >( GetObject() );
+ SdrTableObj& rSourceTableObj = dynamic_cast< SdrTableObj& >( xSourceCell->GetObject() );
+
+ if(&rSourceTableObj.getSdrModelFromSdrObject() != &rTableObj.getSdrModelFromSdrObject())
+ {
+ // TTTT should not happen - if, then a clone may be needed
+ // Maybe add an assertion here later
+ SetStyleSheet( nullptr, true );
+ }
+
+ notifyModified();
+}
+
+
+void Cell::notifyModified()
+{
+ if( mxTable.is() )
+ mxTable->setModified( true );
+}
+
+
+// SdrTextShape proxy
+
+
+bool Cell::IsActiveCell() const
+{
+ bool isActive = false;
+ SdrTableObj& rTableObj = dynamic_cast< SdrTableObj& >( GetObject() );
+ if( rTableObj.getActiveCell().get() == this )
+ isActive = true;
+
+ return isActive;
+}
+
+bool Cell::IsTextEditActive() const
+{
+ bool isActive = false;
+ SdrTableObj& rTableObj = dynamic_cast< SdrTableObj& >( GetObject() );
+ if(rTableObj.getActiveCell().get() == this )
+ {
+ if( rTableObj.CanCreateEditOutlinerParaObject() )
+ {
+ isActive = true;
+ }
+ }
+ return isActive;
+}
+
+
+bool Cell::hasText() const
+{
+ const OutlinerParaObject* pParaObj = GetOutlinerParaObject();
+ if( pParaObj )
+ {
+ const EditTextObject& rTextObj = pParaObj->GetTextObject();
+ if( rTextObj.GetParagraphCount() >= 1 )
+ {
+ if( rTextObj.GetParagraphCount() == 1 )
+ {
+ if( rTextObj.GetText(0).isEmpty() )
+ return false;
+ }
+ return true;
+ }
+ }
+
+ return false;
+}
+
+bool Cell::CanCreateEditOutlinerParaObject() const
+{
+ SdrTableObj& rTableObj = dynamic_cast< SdrTableObj& >( GetObject() );
+ if( rTableObj.getActiveCell().get() == this )
+ return rTableObj.CanCreateEditOutlinerParaObject();
+ return false;
+}
+
+std::optional<OutlinerParaObject> Cell::CreateEditOutlinerParaObject() const
+{
+ SdrTableObj& rTableObj = dynamic_cast< SdrTableObj& >( GetObject() );
+ if( rTableObj.getActiveCell().get() == this )
+ return rTableObj.CreateEditOutlinerParaObject();
+ return std::nullopt;
+}
+
+
+void Cell::SetStyleSheet( SfxStyleSheet* pStyleSheet, bool bDontRemoveHardAttr )
+{
+ // only allow cell styles for cells
+ if( pStyleSheet && pStyleSheet->GetFamily() != SfxStyleFamily::Frame )
+ return;
+
+ if( mpProperties && (mpProperties->GetStyleSheet() != pStyleSheet) )
+ {
+ mpProperties->SetStyleSheet( pStyleSheet, bDontRemoveHardAttr, true );
+ }
+}
+
+
+const SfxItemSet& Cell::GetObjectItemSet()
+{
+ if( mpProperties )
+ {
+ return mpProperties->GetObjectItemSet();
+ }
+ else
+ {
+ OSL_FAIL("Cell::GetObjectItemSet(), called without properties!");
+ return GetObject().GetObjectItemSet();
+ }
+}
+
+void Cell::SetObjectItem(const SfxPoolItem& rItem)
+{
+ if( mpProperties )
+ {
+ mpProperties->SetObjectItem( rItem );
+ notifyModified();
+ }
+}
+
+void Cell::SetMergedItem(const SfxPoolItem& rItem)
+{
+ SetObjectItem(rItem);
+}
+
+SfxStyleSheet* Cell::GetStyleSheet() const
+{
+ if( mpProperties )
+ return mpProperties->GetStyleSheet();
+ else
+ return nullptr;
+}
+
+void Cell::TakeTextAnchorRect(tools::Rectangle& rAnchorRect) const
+{
+ rAnchorRect.SetLeft( maCellRect.Left() + GetTextLeftDistance() );
+ rAnchorRect.SetRight( maCellRect.Right() - GetTextRightDistance() );
+ rAnchorRect.SetTop( maCellRect.Top() + GetTextUpperDistance() );
+ rAnchorRect.SetBottom( maCellRect.Bottom() - GetTextLowerDistance() );
+}
+
+
+void Cell::SetMergedItemSetAndBroadcast(const SfxItemSet& rSet, bool bClearAllItems)
+{
+ if( mpProperties )
+ {
+ mpProperties->SetMergedItemSetAndBroadcast(rSet, bClearAllItems);
+ notifyModified();
+ }
+}
+
+
+sal_Int32 Cell::calcPreferredWidth( const Size aSize )
+{
+ if ( !hasText() )
+ return getMinimumWidth();
+
+ Outliner& rOutliner=static_cast< SdrTableObj& >( GetObject() ).ImpGetDrawOutliner();
+ rOutliner.SetPaperSize(aSize);
+ rOutliner.SetUpdateLayout(true);
+ ForceOutlinerParaObject( OutlinerMode::TextObject );
+
+ if( GetOutlinerParaObject() )
+ rOutliner.SetText(*GetOutlinerParaObject());
+
+ sal_Int32 nPreferredWidth = const_cast<EditEngine&>(rOutliner.GetEditEngine()).CalcTextWidth();
+ rOutliner.Clear();
+
+ return GetTextLeftDistance() + GetTextRightDistance() + nPreferredWidth;
+}
+
+sal_Int32 Cell::getMinimumWidth() const
+{
+ return GetTextLeftDistance() + GetTextRightDistance() + 100;
+}
+
+
+sal_Int32 Cell::getMinimumHeight()
+{
+ if( !mpProperties )
+ return 0;
+
+ SdrTableObj& rTableObj = dynamic_cast< SdrTableObj& >( GetObject() );
+ sal_Int32 nMinimumHeight = 0;
+
+ tools::Rectangle aTextRect;
+ TakeTextAnchorRect( aTextRect );
+ Size aSize( aTextRect.GetSize() );
+ aSize.setHeight(0x0FFFFFFF );
+
+ SdrOutliner* pEditOutliner = rTableObj.GetCellTextEditOutliner( *this );
+ if(pEditOutliner)
+ {
+ pEditOutliner->SetMaxAutoPaperSize(aSize);
+ nMinimumHeight = pEditOutliner->GetTextHeight()+1;
+ }
+ else
+ {
+ Outliner& rOutliner=rTableObj.ImpGetDrawOutliner();
+ rOutliner.SetPaperSize(aSize);
+ rOutliner.SetUpdateLayout(true);
+ ForceOutlinerParaObject( OutlinerMode::TextObject );
+
+ if( GetOutlinerParaObject() )
+ {
+ rOutliner.SetText(*GetOutlinerParaObject());
+ }
+ nMinimumHeight=rOutliner.GetTextHeight()+1;
+ rOutliner.Clear();
+ }
+
+ nMinimumHeight += GetTextUpperDistance() + GetTextLowerDistance();
+ return nMinimumHeight;
+}
+
+
+tools::Long Cell::GetTextLeftDistance() const
+{
+ return GetItemSet().Get(SDRATTR_TEXT_LEFTDIST).GetValue();
+}
+
+
+tools::Long Cell::GetTextRightDistance() const
+{
+ return GetItemSet().Get(SDRATTR_TEXT_RIGHTDIST).GetValue();
+}
+
+
+tools::Long Cell::GetTextUpperDistance() const
+{
+ return GetItemSet().Get(SDRATTR_TEXT_UPPERDIST).GetValue();
+}
+
+
+tools::Long Cell::GetTextLowerDistance() const
+{
+ return GetItemSet().Get(SDRATTR_TEXT_LOWERDIST).GetValue();
+}
+
+
+SdrTextVertAdjust Cell::GetTextVerticalAdjust() const
+{
+ return GetItemSet().Get(SDRATTR_TEXT_VERTADJUST).GetValue();
+}
+
+
+SdrTextHorzAdjust Cell::GetTextHorizontalAdjust() const
+{
+ return GetItemSet().Get(SDRATTR_TEXT_HORZADJUST).GetValue();
+}
+
+
+void Cell::SetOutlinerParaObject( std::optional<OutlinerParaObject> pTextObject )
+{
+ bool bNullTextObject = !pTextObject;
+ SdrText::SetOutlinerParaObject( std::move(pTextObject) );
+ maSelection.nStartPara = EE_PARA_MAX_COUNT;
+
+ if( bNullTextObject )
+ ForceOutlinerParaObject( OutlinerMode::TextObject );
+}
+
+
+void Cell::AddUndo()
+{
+ SdrObject& rObj = GetObject();
+
+ if( rObj.IsInserted() && rObj.getSdrModelFromSdrObject().IsUndoEnabled() )
+ {
+ CellRef xCell( this );
+ rObj.getSdrModelFromSdrObject().AddUndo( std::make_unique<CellUndo>( &rObj, xCell ) );
+
+ // Undo action for the after-text-edit-ended stack.
+ SdrTableObj* pTableObj = dynamic_cast<sdr::table::SdrTableObj*>(&rObj);
+ if (pTableObj && pTableObj->IsTextEditActive())
+ pTableObj->AddUndo(new CellUndo(pTableObj, xCell));
+ }
+}
+
+
+sdr::properties::TextProperties* Cell::CloneProperties( sdr::properties::TextProperties const * pProperties, SdrObject& rNewObj, Cell& rNewCell )
+{
+ if( pProperties )
+ return new sdr::properties::CellProperties( *static_cast<sdr::properties::CellProperties const *>(pProperties), rNewObj, &rNewCell );
+ else
+ return nullptr;
+}
+
+
+sdr::properties::TextProperties* Cell::CloneProperties( SdrObject& rNewObj, Cell& rNewCell )
+{
+ return CloneProperties(mpProperties.get(),rNewObj,rNewCell);
+}
+
+
+// XInterface
+
+
+Any SAL_CALL Cell::queryInterface( const Type & rType )
+{
+ if( rType == cppu::UnoType<XMergeableCell>::get() )
+ return Any( Reference< XMergeableCell >( this ) );
+
+ if( rType == cppu::UnoType<XCell>::get() )
+ return Any( Reference< XCell >( this ) );
+
+ if( rType == cppu::UnoType<XLayoutConstrains>::get() )
+ return Any( Reference< XLayoutConstrains >( this ) );
+
+ if( rType == cppu::UnoType<XEventListener>::get() )
+ return Any( Reference< XEventListener >( this ) );
+
+ Any aRet( SvxUnoTextBase::queryAggregation( rType ) );
+ if( aRet.hasValue() )
+ return aRet;
+
+ return ::cppu::OWeakObject::queryInterface( rType );
+}
+
+
+void SAL_CALL Cell::acquire() noexcept
+{
+ ::cppu::OWeakObject::acquire();
+}
+
+
+void SAL_CALL Cell::release() noexcept
+{
+ ::cppu::OWeakObject::release();
+}
+
+
+// XTypeProvider
+
+
+Sequence< Type > SAL_CALL Cell::getTypes( )
+{
+ return comphelper::concatSequences( SvxUnoTextBase::getTypes(),
+ Sequence {
+ cppu::UnoType<XMergeableCell>::get(),
+ cppu::UnoType<XLayoutConstrains>::get() });
+}
+
+
+Sequence< sal_Int8 > SAL_CALL Cell::getImplementationId( )
+{
+ return css::uno::Sequence<sal_Int8>();
+}
+
+// XLayoutConstrains
+css::awt::Size SAL_CALL Cell::getMinimumSize()
+{
+ return css::awt::Size( getMinimumWidth(), getMinimumHeight() );
+}
+
+
+css::awt::Size SAL_CALL Cell::getPreferredSize()
+{
+ return getMinimumSize();
+}
+
+
+css::awt::Size SAL_CALL Cell::calcAdjustedSize( const css::awt::Size& aNewSize )
+{
+ return aNewSize;
+}
+
+
+// XMergeableCell
+
+
+sal_Int32 SAL_CALL Cell::getRowSpan()
+{
+ return mnRowSpan;
+}
+
+
+sal_Int32 SAL_CALL Cell::getColumnSpan()
+{
+ return mnColSpan;
+}
+
+
+sal_Bool SAL_CALL Cell::isMerged()
+{
+ return mbMerged;
+}
+
+
+// XCell
+
+
+OUString SAL_CALL Cell::getFormula( )
+{
+ return msFormula;
+}
+
+
+void SAL_CALL Cell::setFormula( const OUString& aFormula )
+{
+ if( msFormula != aFormula )
+ {
+ msFormula = aFormula;
+ }
+}
+
+
+double SAL_CALL Cell::getValue( )
+{
+ return mfValue;
+}
+
+
+void SAL_CALL Cell::setValue( double nValue )
+{
+ if( mfValue != nValue )
+ {
+ mfValue = nValue;
+ mnCellContentType = CellContentType_VALUE;
+ }
+}
+
+
+CellContentType SAL_CALL Cell::getType()
+{
+ return mnCellContentType;
+}
+
+
+sal_Int32 SAL_CALL Cell::getError( )
+{
+ return mnError;
+}
+
+
+// XPropertySet
+
+
+Any Cell::GetAnyForItem( SfxItemSet const & aSet, const SfxItemPropertyMapEntry* pMap )
+{
+ Any aAny( SvxItemPropertySet_getPropertyValue( pMap, aSet ) );
+
+ if( pMap->aType != aAny.getValueType() )
+ {
+ // since the sfx uint16 item now exports a sal_Int32, we may have to fix this here
+ if( ( pMap->aType == ::cppu::UnoType<sal_Int16>::get()) && aAny.getValueType() == ::cppu::UnoType<sal_Int32>::get() )
+ {
+ sal_Int32 nValue = 0;
+ aAny >>= nValue;
+ aAny <<= static_cast<sal_Int16>(nValue);
+ }
+ else
+ {
+ OSL_FAIL("GetAnyForItem() Returnvalue has wrong Type!" );
+ }
+ }
+
+ return aAny;
+}
+
+Reference< XPropertySetInfo > SAL_CALL Cell::getPropertySetInfo()
+{
+ return mpPropSet->getPropertySetInfo();
+}
+
+
+void SAL_CALL Cell::setPropertyValue( const OUString& rPropertyName, const Any& rValue )
+{
+ ::SolarMutexGuard aGuard;
+
+ if(mpProperties == nullptr)
+ throw DisposedException();
+
+ const SfxItemPropertyMapEntry* pMap = mpPropSet->getPropertyMapEntry(rPropertyName);
+ if( pMap )
+ {
+ if( (pMap->nFlags & PropertyAttribute::READONLY ) != 0 )
+ throw PropertyVetoException();
+
+ switch( pMap->nWID )
+ {
+ case OWN_ATTR_STYLE:
+ {
+ Reference< XStyle > xStyle;
+ if( !( rValue >>= xStyle ) )
+ throw IllegalArgumentException();
+
+ SfxUnoStyleSheet* pStyle = SfxUnoStyleSheet::getUnoStyleSheet(xStyle);
+ SetStyleSheet( pStyle, true );
+ return;
+ }
+ case OWN_ATTR_TABLEBORDER:
+ {
+ auto pBorder = o3tl::tryAccess<TableBorder>(rValue);
+ if(!pBorder)
+ break;
+
+ SvxBoxItem aBox( SDRATTR_TABLE_BORDER );
+ SvxBoxInfoItem aBoxInfo( SDRATTR_TABLE_BORDER_INNER );
+ SvxBorderLine aLine;
+
+ bool bSet = SvxBoxItem::LineToSvxLine(pBorder->TopLine, aLine, false);
+ aBox.SetLine(bSet ? &aLine : nullptr, SvxBoxItemLine::TOP);
+ aBoxInfo.SetValid(SvxBoxInfoItemValidFlags::TOP, pBorder->IsTopLineValid);
+
+ bSet = SvxBoxItem::LineToSvxLine(pBorder->BottomLine, aLine, false);
+ aBox.SetLine(bSet ? &aLine : nullptr, SvxBoxItemLine::BOTTOM);
+ aBoxInfo.SetValid(SvxBoxInfoItemValidFlags::BOTTOM, pBorder->IsBottomLineValid);
+
+ bSet = SvxBoxItem::LineToSvxLine(pBorder->LeftLine, aLine, false);
+ aBox.SetLine(bSet ? &aLine : nullptr, SvxBoxItemLine::LEFT);
+ aBoxInfo.SetValid(SvxBoxInfoItemValidFlags::LEFT, pBorder->IsLeftLineValid);
+
+ bSet = SvxBoxItem::LineToSvxLine(pBorder->RightLine, aLine, false);
+ aBox.SetLine(bSet ? &aLine : nullptr, SvxBoxItemLine::RIGHT);
+ aBoxInfo.SetValid(SvxBoxInfoItemValidFlags::RIGHT, pBorder->IsRightLineValid);
+
+ bSet = SvxBoxItem::LineToSvxLine(pBorder->HorizontalLine, aLine, false);
+ aBoxInfo.SetLine(bSet ? &aLine : nullptr, SvxBoxInfoItemLine::HORI);
+ aBoxInfo.SetValid(SvxBoxInfoItemValidFlags::HORI, pBorder->IsHorizontalLineValid);
+
+ bSet = SvxBoxItem::LineToSvxLine(pBorder->VerticalLine, aLine, false);
+ aBoxInfo.SetLine(bSet ? &aLine : nullptr, SvxBoxInfoItemLine::VERT);
+ aBoxInfo.SetValid(SvxBoxInfoItemValidFlags::VERT, pBorder->IsVerticalLineValid);
+
+ aBox.SetAllDistances(pBorder->Distance); //TODO
+ aBoxInfo.SetValid(SvxBoxInfoItemValidFlags::DISTANCE, pBorder->IsDistanceValid);
+
+ mpProperties->SetObjectItem(aBox);
+ mpProperties->SetObjectItem(aBoxInfo);
+ return;
+ }
+ case OWN_ATTR_FILLBMP_MODE:
+ {
+ BitmapMode eMode;
+ if(!(rValue >>= eMode) )
+ {
+ sal_Int32 nMode = 0;
+ if(!(rValue >>= nMode))
+ throw IllegalArgumentException();
+
+ eMode = static_cast<BitmapMode>(nMode);
+ }
+
+ mpProperties->SetObjectItem( XFillBmpStretchItem( eMode == BitmapMode_STRETCH ) );
+ mpProperties->SetObjectItem( XFillBmpTileItem( eMode == BitmapMode_REPEAT ) );
+ return;
+ }
+ case SDRATTR_TABLE_TEXT_ROTATION:
+ {
+ sal_Int32 nRotVal = 0;
+ if (!(rValue >>= nRotVal))
+ throw IllegalArgumentException();
+
+ if (nRotVal != 27000 && nRotVal != 9000 && nRotVal != 0)
+ throw IllegalArgumentException();
+
+ mpProperties->SetObjectItem(SvxTextRotateItem(Degree10(nRotVal/10), SDRATTR_TABLE_TEXT_ROTATION));
+ return;
+ }
+ default:
+ {
+ SfxItemSet aSet(GetObject().getSdrModelFromSdrObject().GetItemPool(), pMap->nWID, pMap->nWID);
+ aSet.Put(mpProperties->GetItem(pMap->nWID));
+
+ bool bSpecial = false;
+
+ switch( pMap->nWID )
+ {
+ case XATTR_FILLBITMAP:
+ case XATTR_FILLGRADIENT:
+ case XATTR_FILLHATCH:
+ case XATTR_FILLFLOATTRANSPARENCE:
+ case XATTR_LINEEND:
+ case XATTR_LINESTART:
+ case XATTR_LINEDASH:
+ {
+ if( pMap->nMemberId == MID_NAME )
+ {
+ OUString aApiName;
+ if( rValue >>= aApiName )
+ {
+ if(SvxShape::SetFillAttribute(pMap->nWID, aApiName, aSet, &GetObject().getSdrModelFromSdrObject()))
+ bSpecial = true;
+ }
+ }
+ }
+ break;
+ }
+
+ if( !bSpecial )
+ {
+
+ if( !SvxUnoTextRangeBase::SetPropertyValueHelper( pMap, rValue, aSet ))
+ {
+ if( aSet.GetItemState( pMap->nWID ) != SfxItemState::SET )
+ {
+ // fetch the default from ItemPool
+ if(SfxItemPool::IsWhich(pMap->nWID))
+ aSet.Put(GetObject().getSdrModelFromSdrObject().GetItemPool().GetDefaultItem(pMap->nWID));
+ }
+
+ if( aSet.GetItemState( pMap->nWID ) == SfxItemState::SET )
+ {
+ SvxItemPropertySet_setPropertyValue( pMap, rValue, aSet );
+ }
+ }
+ }
+
+ GetObject().getSdrModelFromSdrObject().SetChanged();
+ mpProperties->SetMergedItemSetAndBroadcast( aSet );
+ return;
+ }
+ }
+ }
+ throw UnknownPropertyException( rPropertyName, static_cast<cppu::OWeakObject*>(this));
+}
+
+
+Any SAL_CALL Cell::getPropertyValue( const OUString& PropertyName )
+{
+ ::SolarMutexGuard aGuard;
+
+ if(mpProperties == nullptr)
+ throw DisposedException();
+
+ const SfxItemPropertyMapEntry* pMap = mpPropSet->getPropertyMapEntry(PropertyName);
+ if( pMap )
+ {
+ switch( pMap->nWID )
+ {
+ case OWN_ATTR_STYLE:
+ {
+ return Any( Reference< XStyle >( dynamic_cast< SfxUnoStyleSheet* >( GetStyleSheet() ) ) );
+ }
+ case OWN_ATTR_TABLEBORDER:
+ {
+ const SvxBoxInfoItem& rBoxInfoItem = mpProperties->GetItem(SDRATTR_TABLE_BORDER_INNER);
+ const SvxBoxItem& rBox = mpProperties->GetItem(SDRATTR_TABLE_BORDER);
+
+ TableBorder aTableBorder;
+ aTableBorder.TopLine = SvxBoxItem::SvxLineToLine(rBox.GetTop(), false);
+ aTableBorder.IsTopLineValid = rBoxInfoItem.IsValid(SvxBoxInfoItemValidFlags::TOP);
+ aTableBorder.BottomLine = SvxBoxItem::SvxLineToLine(rBox.GetBottom(), false);
+ aTableBorder.IsBottomLineValid = rBoxInfoItem.IsValid(SvxBoxInfoItemValidFlags::BOTTOM);
+ aTableBorder.LeftLine = SvxBoxItem::SvxLineToLine(rBox.GetLeft(), false);
+ aTableBorder.IsLeftLineValid = rBoxInfoItem.IsValid(SvxBoxInfoItemValidFlags::LEFT);
+ aTableBorder.RightLine = SvxBoxItem::SvxLineToLine(rBox.GetRight(), false);
+ aTableBorder.IsRightLineValid = rBoxInfoItem.IsValid(SvxBoxInfoItemValidFlags::RIGHT );
+ aTableBorder.HorizontalLine = SvxBoxItem::SvxLineToLine(rBoxInfoItem.GetHori(), false);
+ aTableBorder.IsHorizontalLineValid = rBoxInfoItem.IsValid(SvxBoxInfoItemValidFlags::HORI);
+ aTableBorder.VerticalLine = SvxBoxItem::SvxLineToLine(rBoxInfoItem.GetVert(), false);
+ aTableBorder.IsVerticalLineValid = rBoxInfoItem.IsValid(SvxBoxInfoItemValidFlags::VERT);
+ aTableBorder.Distance = rBox.GetSmallestDistance();
+ aTableBorder.IsDistanceValid = rBoxInfoItem.IsValid(SvxBoxInfoItemValidFlags::DISTANCE);
+
+ return Any( aTableBorder );
+ }
+ case OWN_ATTR_FILLBMP_MODE:
+ {
+ const XFillBmpStretchItem& rStretchItem = mpProperties->GetItem(XATTR_FILLBMP_STRETCH);
+ const XFillBmpTileItem& rTileItem = mpProperties->GetItem(XATTR_FILLBMP_TILE);
+ if( rTileItem.GetValue() )
+ {
+ return Any( BitmapMode_REPEAT );
+ }
+ else if( rStretchItem.GetValue() )
+ {
+ return Any( BitmapMode_STRETCH );
+ }
+ else
+ {
+ return Any( BitmapMode_NO_REPEAT );
+ }
+ }
+ case SDRATTR_TABLE_TEXT_ROTATION:
+ {
+ const SvxTextRotateItem& rTextRotate = mpProperties->GetItem(SDRATTR_TABLE_TEXT_ROTATION);
+ return Any(sal_Int32(to<Degree100>(rTextRotate.GetValue())));
+ }
+ default:
+ {
+ SfxItemSet aSet(GetObject().getSdrModelFromSdrObject().GetItemPool(), pMap->nWID, pMap->nWID);
+ aSet.Put(mpProperties->GetItem(pMap->nWID));
+
+ Any aAny;
+ if(!SvxUnoTextRangeBase::GetPropertyValueHelper( aSet, pMap, aAny ))
+ {
+ if(!aSet.Count())
+ {
+ // fetch the default from ItemPool
+ if(SfxItemPool::IsWhich(pMap->nWID))
+ aSet.Put(GetObject().getSdrModelFromSdrObject().GetItemPool().GetDefaultItem(pMap->nWID));
+ }
+
+ if( aSet.Count() )
+ aAny = GetAnyForItem( aSet, pMap );
+ }
+
+ return aAny;
+ }
+ }
+ }
+ throw UnknownPropertyException( PropertyName, static_cast<cppu::OWeakObject*>(this));
+}
+
+
+void SAL_CALL Cell::addPropertyChangeListener( const OUString& /*aPropertyName*/, const Reference< XPropertyChangeListener >& /*xListener*/ )
+{
+}
+
+
+void SAL_CALL Cell::removePropertyChangeListener( const OUString& /*aPropertyName*/, const Reference< XPropertyChangeListener >& /*aListener*/ )
+{
+}
+
+
+void SAL_CALL Cell::addVetoableChangeListener( const OUString& /*PropertyName*/, const Reference< XVetoableChangeListener >& /*aListener*/ )
+{
+}
+
+
+void SAL_CALL Cell::removeVetoableChangeListener( const OUString& /*PropertyName*/, const Reference< XVetoableChangeListener >& /*aListener*/ )
+{
+}
+
+
+// XMultiPropertySet
+
+
+void SAL_CALL Cell::setPropertyValues( const Sequence< OUString >& aPropertyNames, const Sequence< Any >& aValues )
+{
+ ::SolarMutexGuard aSolarGuard;
+
+ if(mpProperties == nullptr)
+ throw DisposedException();
+
+ const sal_Int32 nCount = aPropertyNames.getLength();
+
+ const OUString* pNames = aPropertyNames.getConstArray();
+ const Any* pValues = aValues.getConstArray();
+
+ for( sal_Int32 nIdx = 0; nIdx < nCount; nIdx++, pNames++, pValues++ )
+ {
+ try
+ {
+ setPropertyValue( *pNames, *pValues );
+ }
+ catch( UnknownPropertyException& )
+ {
+ TOOLS_WARN_EXCEPTION("svx.table", "unknown property!");
+ }
+ catch( Exception& )
+ {
+ TOOLS_WARN_EXCEPTION("svx.table", "");
+ }
+ }
+}
+
+
+Sequence< Any > SAL_CALL Cell::getPropertyValues( const Sequence< OUString >& aPropertyNames )
+{
+ ::SolarMutexGuard aSolarGuard;
+
+ if(mpProperties == nullptr)
+ throw DisposedException();
+
+ const sal_Int32 nCount = aPropertyNames.getLength();
+ Sequence< Any > aRet( nCount );
+ Any* pValue = aRet.getArray();
+
+ for( const OUString& rName : aPropertyNames )
+ {
+ try
+ {
+ *pValue = getPropertyValue( rName );
+ }
+ catch( UnknownPropertyException& )
+ {
+ TOOLS_WARN_EXCEPTION("svx.table", "unknown property!");
+ }
+ catch( Exception& )
+ {
+ TOOLS_WARN_EXCEPTION("svx.table", "");
+ }
+ pValue++;
+ }
+
+ return aRet;
+}
+
+
+void SAL_CALL Cell::addPropertiesChangeListener( const Sequence< OUString >& /*aPropertyNames*/, const Reference< XPropertiesChangeListener >& /*xListener*/ )
+{
+}
+
+
+void SAL_CALL Cell::removePropertiesChangeListener( const Reference< XPropertiesChangeListener >& /*xListener*/ )
+{
+}
+
+
+void SAL_CALL Cell::firePropertiesChangeEvent( const Sequence< OUString >& /*aPropertyNames*/, const Reference< XPropertiesChangeListener >& /*xListener*/ )
+{
+}
+
+
+// XPropertyState
+
+
+PropertyState SAL_CALL Cell::getPropertyState( const OUString& PropertyName )
+{
+ ::SolarMutexGuard aGuard;
+
+ if(mpProperties == nullptr)
+ throw DisposedException();
+
+ const SfxItemPropertyMapEntry* pMap = mpPropSet->getPropertyMapEntry(PropertyName);
+
+ if( pMap )
+ {
+ PropertyState eState;
+ switch( pMap->nWID )
+ {
+ case OWN_ATTR_FILLBMP_MODE:
+ {
+ const SfxItemSet& rSet = mpProperties->GetMergedItemSet();
+
+ const bool bStretch = rSet.GetItemState( XATTR_FILLBMP_STRETCH, false ) == SfxItemState::SET;
+ const bool bTile = rSet.GetItemState( XATTR_FILLBMP_TILE, false ) == SfxItemState::SET;
+ if( bStretch || bTile )
+ {
+ eState = PropertyState_DIRECT_VALUE;
+ }
+ else
+ {
+ eState = PropertyState_DEFAULT_VALUE;
+ }
+ break;
+ }
+ case OWN_ATTR_STYLE:
+ {
+ return PropertyState_DIRECT_VALUE;
+ }
+ case OWN_ATTR_TABLEBORDER:
+ {
+ const SfxItemSet& rSet = mpProperties->GetMergedItemSet();
+ if( (rSet.GetItemState( SDRATTR_TABLE_BORDER_INNER, false ) == SfxItemState::DEFAULT) && (rSet.GetItemState( SDRATTR_TABLE_BORDER, false ) == SfxItemState::DEFAULT) )
+ return PropertyState_DEFAULT_VALUE;
+
+ return PropertyState_DIRECT_VALUE;
+ }
+ default:
+ {
+ const SfxItemSet& rSet = mpProperties->GetMergedItemSet();
+
+ switch( rSet.GetItemState( pMap->nWID, false ) )
+ {
+ case SfxItemState::SET:
+ eState = PropertyState_DIRECT_VALUE;
+ break;
+ case SfxItemState::DEFAULT:
+ eState = PropertyState_DEFAULT_VALUE;
+ break;
+ default:
+ eState = PropertyState_AMBIGUOUS_VALUE;
+ break;
+ }
+
+ // if an item is set, this doesn't mean we want it :)
+ if( PropertyState_DIRECT_VALUE == eState )
+ {
+ switch( pMap->nWID )
+ {
+ // the following items are disabled by changing the
+ // fill style or the line style. so there is no need
+ // to export items without names which should be empty
+ case XATTR_FILLBITMAP:
+ case XATTR_FILLGRADIENT:
+ case XATTR_FILLHATCH:
+ case XATTR_LINEDASH:
+ {
+ const NameOrIndex* pItem = rSet.GetItem<NameOrIndex>(pMap->nWID);
+ if( ( pItem == nullptr ) || pItem->GetName().isEmpty() )
+ eState = PropertyState_DEFAULT_VALUE;
+ }
+ break;
+
+ // #i36115#
+ // If e.g. the LineStart is on NONE and thus the string has length 0, it still
+ // may be a hard attribute covering the set LineStart of the parent (Style).
+ // #i37644#
+ // same is for fill float transparency
+ case XATTR_LINEEND:
+ case XATTR_LINESTART:
+ case XATTR_FILLFLOATTRANSPARENCE:
+ {
+ const NameOrIndex* pItem = rSet.GetItem<NameOrIndex>(pMap->nWID);
+ if( pItem == nullptr )
+ eState = PropertyState_DEFAULT_VALUE;
+ }
+ break;
+ case XATTR_FILLCOLOR:
+ if (pMap->nMemberId == MID_COLOR_THEME_INDEX)
+ {
+ const XFillColorItem* pColor = rSet.GetItem<XFillColorItem>(pMap->nWID);
+ if (pColor->GetThemeColor().GetThemeIndex() == -1)
+ {
+ eState = PropertyState_DEFAULT_VALUE;
+ }
+ }
+ else if (pMap->nMemberId == MID_COLOR_LUM_MOD)
+ {
+ const XFillColorItem* pColor = rSet.GetItem<XFillColorItem>(pMap->nWID);
+ if (pColor->GetThemeColor().GetLumMod() == 10000)
+ {
+ eState = PropertyState_DEFAULT_VALUE;
+ }
+ }
+ else if (pMap->nMemberId == MID_COLOR_LUM_OFF)
+ {
+ const XFillColorItem* pColor = rSet.GetItem<XFillColorItem>(pMap->nWID);
+ if (pColor->GetThemeColor().GetLumOff() == 0)
+ {
+ eState = PropertyState_DEFAULT_VALUE;
+ }
+ }
+ }
+ }
+ }
+ }
+ return eState;
+ }
+ throw UnknownPropertyException(PropertyName);
+}
+
+
+Sequence< PropertyState > SAL_CALL Cell::getPropertyStates( const Sequence< OUString >& aPropertyName )
+{
+ ::SolarMutexGuard aGuard;
+
+ if(mpProperties == nullptr)
+ throw DisposedException();
+
+ const sal_Int32 nCount = aPropertyName.getLength();
+ Sequence< PropertyState > aRet( nCount );
+
+ std::transform(aPropertyName.begin(), aPropertyName.end(), aRet.getArray(),
+ [this](const OUString& rName) -> PropertyState {
+ try
+ {
+ return getPropertyState( rName );
+ }
+ catch( Exception& )
+ {
+ return PropertyState_AMBIGUOUS_VALUE;
+ }
+ });
+
+ return aRet;
+}
+
+
+void SAL_CALL Cell::setPropertyToDefault( const OUString& PropertyName )
+{
+ ::SolarMutexGuard aGuard;
+
+ if(mpProperties == nullptr)
+ throw DisposedException();
+
+ const SfxItemPropertyMapEntry* pMap = mpPropSet->getPropertyMapEntry(PropertyName);
+ if( pMap )
+ {
+ switch( pMap->nWID )
+ {
+ case OWN_ATTR_FILLBMP_MODE:
+ {
+ mpProperties->ClearObjectItem( XATTR_FILLBMP_STRETCH );
+ mpProperties->ClearObjectItem( XATTR_FILLBMP_TILE );
+ break;
+ }
+ case OWN_ATTR_STYLE:
+ break;
+
+ case OWN_ATTR_TABLEBORDER:
+ {
+ mpProperties->ClearObjectItem( SDRATTR_TABLE_BORDER_INNER );
+ mpProperties->ClearObjectItem( SDRATTR_TABLE_BORDER );
+ break;
+ }
+
+ default:
+ {
+ mpProperties->ClearObjectItem( pMap->nWID );
+ }
+ }
+
+ GetObject().getSdrModelFromSdrObject().SetChanged();
+ return;
+ }
+ throw UnknownPropertyException( PropertyName, static_cast<cppu::OWeakObject*>(this));
+}
+
+
+Any SAL_CALL Cell::getPropertyDefault( const OUString& aPropertyName )
+{
+ ::SolarMutexGuard aGuard;
+
+ if(mpProperties == nullptr)
+ throw DisposedException();
+
+ const SfxItemPropertyMapEntry* pMap = mpPropSet->getPropertyMapEntry(aPropertyName);
+ if( pMap )
+ {
+ switch( pMap->nWID )
+ {
+ case OWN_ATTR_FILLBMP_MODE:
+ return Any( BitmapMode_NO_REPEAT );
+
+ case OWN_ATTR_STYLE:
+ {
+ Reference< XStyle > xStyle;
+ return Any( xStyle );
+ }
+
+ case OWN_ATTR_TABLEBORDER:
+ {
+ TableBorder aBorder;
+ return Any( aBorder );
+ }
+
+ default:
+ {
+ if( SfxItemPool::IsWhich(pMap->nWID) )
+ {
+ SfxItemSet aSet(GetObject().getSdrModelFromSdrObject().GetItemPool(), pMap->nWID, pMap->nWID);
+ aSet.Put(GetObject().getSdrModelFromSdrObject().GetItemPool().GetDefaultItem(pMap->nWID));
+ return GetAnyForItem( aSet, pMap );
+ }
+ }
+ }
+ }
+ throw UnknownPropertyException( aPropertyName, static_cast<cppu::OWeakObject*>(this));
+}
+
+
+// XMultiPropertyStates
+
+
+void SAL_CALL Cell::setAllPropertiesToDefault()
+{
+ mpProperties.reset(new sdr::properties::CellProperties( static_cast< SdrTableObj& >( GetObject() ), this ));
+
+ SdrOutliner& rOutliner = GetObject().ImpGetDrawOutliner();
+
+ OutlinerParaObject* pParaObj = GetOutlinerParaObject();
+ if( !pParaObj )
+ return;
+
+ rOutliner.SetText(*pParaObj);
+ sal_Int32 nParaCount(rOutliner.GetParagraphCount());
+
+ if(nParaCount)
+ {
+ ESelection aSelection( 0, 0, EE_PARA_ALL, EE_TEXTPOS_ALL);
+ rOutliner.RemoveAttribs(aSelection, true, 0);
+
+ std::optional<OutlinerParaObject> pTemp = rOutliner.CreateParaObject(0, nParaCount);
+ rOutliner.Clear();
+
+ SetOutlinerParaObject(std::move(pTemp));
+ }
+}
+
+
+void SAL_CALL Cell::setPropertiesToDefault( const Sequence< OUString >& aPropertyNames )
+{
+ for(const OUString& rName : aPropertyNames)
+ setPropertyToDefault( rName );
+}
+
+
+Sequence< Any > SAL_CALL Cell::getPropertyDefaults( const Sequence< OUString >& aPropertyNames )
+{
+ sal_Int32 nCount = aPropertyNames.getLength();
+ Sequence< Any > aDefaults( nCount );
+
+ std::transform(aPropertyNames.begin(), aPropertyNames.end(), aDefaults.getArray(),
+ [this](const OUString& rName) -> Any { return getPropertyDefault(rName); });
+
+ return aDefaults;
+}
+
+
+// XText
+
+
+void SAL_CALL Cell::insertTextContent( const Reference< XTextRange >& xRange, const Reference< XTextContent >& xContent, sal_Bool bAbsorb )
+{
+ SvxUnoTextBase::insertTextContent( xRange, xContent, bAbsorb );
+ notifyModified();
+}
+
+
+void SAL_CALL Cell::removeTextContent( const Reference< XTextContent >& xContent )
+{
+ SvxUnoTextBase::removeTextContent( xContent );
+ notifyModified();
+}
+
+
+// XSimpleText
+
+
+void SAL_CALL Cell::insertString( const Reference< XTextRange >& xRange, const OUString& aString, sal_Bool bAbsorb )
+{
+ SvxUnoTextBase::insertString( xRange, aString, bAbsorb );
+ notifyModified();
+}
+
+
+void SAL_CALL Cell::insertControlCharacter( const Reference< XTextRange >& xRange, sal_Int16 nControlCharacter, sal_Bool bAbsorb )
+{
+ SvxUnoTextBase::insertControlCharacter( xRange, nControlCharacter, bAbsorb );
+ notifyModified();
+}
+
+
+// XTextRange
+
+
+OUString SAL_CALL Cell::getString( )
+{
+ maSelection.nStartPara = EE_PARA_MAX_COUNT;
+ return SvxUnoTextBase::getString();
+}
+
+
+void SAL_CALL Cell::setString( const OUString& aString )
+{
+ SvxUnoTextBase::setString( aString );
+ notifyModified();
+}
+
+// XEventListener
+void SAL_CALL Cell::disposing( const EventObject& /*Source*/ )
+{
+ mxTable.clear();
+ dispose();
+}
+
+void Cell::dumpAsXml(xmlTextWriterPtr pWriter, sal_Int32 nRow, sal_Int32 nCol) const
+{
+ (void)xmlTextWriterStartElement(pWriter, BAD_CAST("Cell"));
+ (void)xmlTextWriterWriteFormatAttribute(pWriter, BAD_CAST("row"), "%" SAL_PRIdINT32, nRow);
+ (void)xmlTextWriterWriteFormatAttribute(pWriter, BAD_CAST("col"), "%" SAL_PRIdINT32, nCol);
+ SdrText::dumpAsXml(pWriter);
+ //SvxUnoTextBase::dumpAsXml(pWriter);
+ //mpPropSet->dumpAsXml(pWriter);
+ mpProperties->dumpAsXml(pWriter);
+ (void)xmlTextWriterEndElement(pWriter);
+}
+
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/table/cellcursor.cxx b/svx/source/table/cellcursor.cxx
new file mode 100644
index 000000000..313ec63df
--- /dev/null
+++ b/svx/source/table/cellcursor.cxx
@@ -0,0 +1,546 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * 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/lang/NoSupportException.hpp>
+#include <svx/svdotable.hxx>
+#include "cellcursor.hxx"
+#include "tablelayouter.hxx"
+#include <cell.hxx>
+#include <svx/svdmodel.hxx>
+#include <svx/strings.hrc>
+#include <svx/dialmgr.hxx>
+#include <tools/debug.hxx>
+#include <tools/diagnose_ex.h>
+
+
+using namespace ::com::sun::star::uno;
+using namespace ::com::sun::star::lang;
+using namespace ::com::sun::star::container;
+using namespace ::com::sun::star::beans;
+using namespace ::com::sun::star::table;
+
+
+namespace sdr::table {
+
+CellCursor::CellCursor( const TableModelRef & xTable, sal_Int32 nLeft, sal_Int32 nTop, sal_Int32 nRight, sal_Int32 nBottom )
+: CellCursorBase( xTable, nLeft, nTop, nRight, nBottom )
+{
+}
+
+
+CellCursor::~CellCursor()
+{
+}
+
+
+// XCellCursor
+
+
+Reference< XCell > SAL_CALL CellCursor::getCellByPosition( sal_Int32 nColumn, sal_Int32 nRow )
+{
+ return CellRange::getCellByPosition( nColumn, nRow );
+}
+
+
+Reference< XCellRange > SAL_CALL CellCursor::getCellRangeByPosition( sal_Int32 nLeft, sal_Int32 nTop, sal_Int32 nRight, sal_Int32 nBottom )
+{
+ return CellRange::getCellRangeByPosition( nLeft, nTop, nRight, nBottom );
+}
+
+
+Reference< XCellRange > SAL_CALL CellCursor::getCellRangeByName( const OUString& aRange )
+{
+ return CellRange::getCellRangeByName( aRange );
+}
+
+
+// XCellCursor
+
+
+void SAL_CALL CellCursor::gotoStart( )
+{
+ mnRight = mnLeft;
+ mnBottom = mnTop;
+}
+
+
+void SAL_CALL CellCursor::gotoEnd( )
+{
+ mnLeft = mnRight;
+ mnTop = mnBottom;
+}
+
+
+void SAL_CALL CellCursor::gotoNext( )
+{
+ if( mxTable.is() )
+ {
+ mnRight++;
+ if( mnRight >= mxTable->getColumnCount() )
+ {
+ // if we past the last column, try skip to the row line
+ mnTop++;
+ if( mnTop >= mxTable->getRowCount() )
+ {
+ // if we past the last row, do not move cursor at all
+ mnTop--;
+ mnRight--;
+ }
+ else
+ {
+ // restart at the first column on the next row
+ mnRight = 0;
+ }
+ }
+ }
+
+ mnLeft = mnRight;
+ mnTop = mnBottom;
+}
+
+
+void SAL_CALL CellCursor::gotoPrevious( )
+{
+ if( mxTable.is() )
+ {
+ if( mnLeft > 0 )
+ {
+ --mnLeft;
+ }
+ else if( mnTop > 0 )
+ {
+ --mnTop;
+ mnLeft = mxTable->getColumnCount() - 1;
+ }
+ }
+
+ mnRight = mnLeft;
+ mnBottom = mnTop;
+}
+
+
+void SAL_CALL CellCursor::gotoOffset( ::sal_Int32 nColumnOffset, ::sal_Int32 nRowOffset )
+{
+ if( mxTable.is() )
+ {
+ const sal_Int32 nLeft = mnLeft + nColumnOffset;
+ if( (nLeft >= 0) && (nLeft < mxTable->getColumnCount() ) )
+ mnRight = mnLeft = nLeft;
+
+ const sal_Int32 nTop = mnTop + nRowOffset;
+ if( (nTop >= 0) && (nTop < mxTable->getRowCount()) )
+ mnTop = mnBottom = nTop;
+ }
+}
+
+
+// XMergeableCellCursor
+
+
+/** returns true and the merged cell positions if a merge is valid or false if a merge is
+ not valid for that range */
+bool CellCursor::GetMergedSelection( CellPos& rStart, CellPos& rEnd )
+{
+ rStart.mnCol = mnLeft; rStart.mnRow = mnTop;
+ rEnd.mnCol = mnRight; rEnd.mnRow = mnBottom;
+
+ // single cell merge is never valid
+ if( mxTable.is() && ((mnLeft != mnRight) || (mnTop != mnBottom)) ) try
+ {
+ CellRef xCell( dynamic_cast< Cell* >( mxTable->getCellByPosition( mnLeft, mnTop ).get() ) );
+
+ // check if first cell is merged
+ if( xCell.is() && xCell->isMerged() )
+ findMergeOrigin( mxTable, mnLeft, mnTop, rStart.mnCol, rStart.mnRow );
+
+ // check if last cell is merged
+ xCell.set( dynamic_cast< Cell* >( mxTable->getCellByPosition( mnRight, mnBottom ).get() ) );
+ if( xCell.is() )
+ {
+ if( xCell->isMerged() )
+ {
+ findMergeOrigin( mxTable, mnRight, mnBottom, rEnd.mnCol, rEnd.mnRow );
+ // merge not possible if selection is only one cell and all its merges
+ if( rEnd == rStart )
+ return false;
+ xCell.set( dynamic_cast< Cell* >( mxTable->getCellByPosition( rEnd.mnCol, rEnd.mnRow ).get() ) );
+ }
+ }
+ if( xCell.is() )
+ {
+ rEnd.mnCol += xCell->getColumnSpan()-1;
+ rEnd.mnRow += xCell->getRowSpan()-1;
+ }
+
+ // now check if everything is inside the given bounds
+ sal_Int32 nRow, nCol;
+ for( nRow = rStart.mnRow; nRow <= rEnd.mnRow; nRow++ )
+ {
+ for( nCol = rStart.mnCol; nCol <= rEnd.mnCol; nCol++ )
+ {
+ xCell.set( dynamic_cast< Cell* >( mxTable->getCellByPosition( nCol, nRow ).get() ) );
+ if( !xCell.is() )
+ continue;
+
+ if( xCell->isMerged() )
+ {
+ sal_Int32 nOriginCol, nOriginRow;
+ if( findMergeOrigin( mxTable, nCol, nRow, nOriginCol, nOriginRow ) )
+ {
+ if( (nOriginCol < rStart.mnCol) || (nOriginRow < rStart.mnRow) )
+ return false;
+
+ xCell.set( dynamic_cast< Cell* >( mxTable->getCellByPosition( nOriginCol, nOriginRow ).get() ) );
+ if( xCell.is() )
+ {
+ nOriginCol += xCell->getColumnSpan()-1;
+ nOriginRow += xCell->getRowSpan()-1;
+
+ if( (nOriginCol > rEnd.mnCol) || (nOriginRow > rEnd.mnRow) )
+ return false;
+ }
+ }
+ }
+ else if( ((nCol + xCell->getColumnSpan() - 1) > rEnd.mnCol) || ((nRow + xCell->getRowSpan() - 1 ) > rEnd.mnRow) )
+ {
+ return false;
+ }
+ }
+ }
+ return true;
+ }
+ catch( Exception& )
+ {
+ TOOLS_WARN_EXCEPTION("svx.table", "");
+ }
+ return false;
+}
+
+
+void SAL_CALL CellCursor::merge( )
+{
+ CellPos aStart, aEnd;
+ if( !GetMergedSelection( aStart, aEnd ) )
+ throw NoSupportException();
+
+ if( !mxTable.is() || (mxTable->getSdrTableObj() == nullptr) )
+ throw DisposedException();
+
+ SdrModel& rModel(mxTable->getSdrTableObj()->getSdrModelFromSdrObject());
+ const bool bUndo(mxTable->getSdrTableObj()->IsInserted() && rModel.IsUndoEnabled());
+
+ if( bUndo )
+ rModel.BegUndo( SvxResId(STR_TABLE_MERGE) );
+
+ try
+ {
+ mxTable->merge( aStart.mnCol, aStart.mnRow, aEnd.mnCol - aStart.mnCol + 1, aEnd.mnRow - aStart.mnRow + 1 );
+ mxTable->optimize();
+ mxTable->setModified(true);
+ }
+ catch( Exception& )
+ {
+ TOOLS_WARN_EXCEPTION("svx.table", "");
+ }
+
+ if( bUndo )
+ rModel.EndUndo();
+
+ rModel.SetChanged();
+}
+
+
+void CellCursor::split_column( sal_Int32 nCol, sal_Int32 nColumns, std::vector< sal_Int32 >& rLeftOvers )
+{
+ const sal_Int32 nRowCount = mxTable->getRowCount();
+
+ sal_Int32 nNewCols = 0, nRow;
+
+ // first check how many columns we need to add
+ for( nRow = mnTop; nRow <= mnBottom; ++nRow )
+ {
+ CellRef xCell( dynamic_cast< Cell* >( mxTable->getCellByPosition( nCol, nRow ).get() ) );
+ if( xCell.is() && !xCell->isMerged() )
+ nNewCols = std::max( nNewCols, nColumns - xCell->getColumnSpan() + 1 - rLeftOvers[nRow] );
+ }
+
+ if( nNewCols > 0 )
+ {
+ static const OUStringLiteral sWidth(u"Width");
+ Reference< XTableColumns > xCols( mxTable->getColumns(), UNO_SET_THROW );
+ Reference< XPropertySet > xRefColumn( xCols->getByIndex( nCol ), UNO_QUERY_THROW );
+ sal_Int32 nWidth = 0;
+ xRefColumn->getPropertyValue( sWidth ) >>= nWidth;
+ const sal_Int32 nNewWidth = nWidth / (nNewCols + 1);
+
+ // reference column gets new width + rounding errors
+ xRefColumn->setPropertyValue( sWidth, Any( nWidth - (nNewWidth * nNewCols) ) );
+
+ xCols->insertByIndex( nCol + 1, nNewCols );
+ mnRight += nNewCols;
+
+ // distribute new width
+ for( sal_Int32 nNewCol = nCol + nNewCols; nNewCol > nCol; --nNewCol )
+ {
+ Reference< XPropertySet > xNewCol( xCols->getByIndex( nNewCol ), UNO_QUERY_THROW );
+ xNewCol->setPropertyValue( sWidth, Any( nNewWidth ) );
+ }
+ }
+
+ for( nRow = 0; nRow < nRowCount; ++nRow )
+ {
+ CellRef xCell( dynamic_cast< Cell* >( mxTable->getCellByPosition( nCol, nRow ).get() ) );
+ if( !xCell.is() || xCell->isMerged() )
+ {
+ if( nNewCols > 0 )
+ {
+ // merged cells are ignored, but newly added columns will be added to leftovers
+ xCell.set( dynamic_cast< Cell* >(mxTable->getCellByPosition( nCol+1, nRow ).get() ) );
+ if( !xCell.is() || !xCell->isMerged() )
+ rLeftOvers[nRow] += nNewCols;
+ }
+ }
+ else
+ {
+ sal_Int32 nRowSpan = xCell->getRowSpan() - 1;
+ sal_Int32 nColSpan = xCell->getColumnSpan() - 1;
+
+ if( (nRow >= mnTop) && (nRow <= mnBottom) )
+ {
+ sal_Int32 nCellsAvailable = 1 + nColSpan + rLeftOvers[nRow];
+ if( nColSpan == 0 )
+ nCellsAvailable += nNewCols;
+
+ DBG_ASSERT( nCellsAvailable > nColumns, "sdr::table::CellCursor::split_column(), somethings wrong" );
+
+ sal_Int32 nSplitSpan = (nCellsAvailable / (nColumns + 1)) - 1;
+
+ sal_Int32 nSplitCol = nCol;
+ sal_Int32 nSplits = nColumns + 1;
+ while( nSplits-- )
+ {
+ // last split eats rounding cells
+ if( nSplits == 0 )
+ nSplitSpan = nCellsAvailable - ((nSplitSpan+1) * nColumns) - 1;
+
+ mxTable->merge( nSplitCol, nRow, nSplitSpan + 1, nRowSpan + 1);
+ if( nSplits > 0 )
+ nSplitCol += nSplitSpan + 1;
+ }
+
+ do
+ {
+ rLeftOvers[nRow++] = 0;
+ }
+ while( nRowSpan-- );
+ --nRow;
+ }
+ else
+ {
+ // cope with outside cells, merge if needed
+ if( nColSpan < (rLeftOvers[nRow] + nNewCols) )
+ mxTable->merge( nCol, nRow, (rLeftOvers[nRow] + nNewCols) + 1, nRowSpan + 1 );
+
+ do
+ {
+ rLeftOvers[nRow++] = 0; // consumed
+ }
+ while( nRowSpan-- );
+ --nRow;
+ }
+ }
+ }
+}
+
+
+void CellCursor::split_horizontal( sal_Int32 nColumns )
+{
+ const sal_Int32 nRowCount = mxTable->getRowCount();
+
+ std::vector< sal_Int32 > aLeftOvers( nRowCount );
+
+ for( sal_Int32 nCol = mnRight; nCol >= mnLeft; --nCol )
+ split_column( nCol, nColumns, aLeftOvers );
+}
+
+
+void CellCursor::split_row( sal_Int32 nRow, sal_Int32 nRows, std::vector< sal_Int32 >& rLeftOvers )
+{
+ const sal_Int32 nColCount = mxTable->getColumnCount();
+
+ sal_Int32 nNewRows = 0, nCol;
+
+ // first check how many columns we need to add
+ for( nCol = mnLeft; nCol <= mnRight; ++nCol )
+ {
+ CellRef xCell( dynamic_cast< Cell* >( mxTable->getCellByPosition( nCol, nRow ).get() ) );
+ if( xCell.is() && !xCell->isMerged() )
+ nNewRows = std::max( nNewRows, nRows - xCell->getRowSpan() + 1 - rLeftOvers[nCol] );
+ }
+
+ if( nNewRows > 0 )
+ {
+ static const OUStringLiteral sHeight(u"Height");
+ Reference< XTableRows > xRows( mxTable->getRows(), UNO_SET_THROW );
+ Reference< XPropertySet > xRefRow( xRows->getByIndex( nRow ), UNO_QUERY_THROW );
+ sal_Int32 nHeight = 0;
+ xRefRow->getPropertyValue( sHeight ) >>= nHeight;
+ const sal_Int32 nNewHeight = nHeight / (nNewRows + 1);
+
+ // reference row gets new height + rounding errors
+ xRefRow->setPropertyValue( sHeight, Any( nHeight - (nNewHeight * nNewRows) ) );
+
+ xRows->insertByIndex( nRow + 1, nNewRows );
+ mnBottom += nNewRows;
+
+ // distribute new width
+ for( sal_Int32 nNewRow = nRow + nNewRows; nNewRow > nRow; --nNewRow )
+ {
+ Reference< XPropertySet > xNewRow( xRows->getByIndex( nNewRow ), UNO_QUERY_THROW );
+ xNewRow->setPropertyValue( sHeight, Any( nNewHeight ) );
+ }
+ }
+
+ for( nCol = 0; nCol < nColCount; ++nCol )
+ {
+ CellRef xCell( dynamic_cast< Cell* >( mxTable->getCellByPosition( nCol, nRow ).get() ) );
+ if( !xCell.is() || xCell->isMerged() )
+ {
+ if( nNewRows )
+ {
+ // merged cells are ignored, but newly added columns will be added to leftovers
+ xCell.set( dynamic_cast< Cell* >(mxTable->getCellByPosition( nCol, nRow+1 ).get() ) );
+ if( !xCell.is() || !xCell->isMerged() )
+ rLeftOvers[nCol] += nNewRows;
+ }
+ }
+ else
+ {
+ sal_Int32 nRowSpan = xCell->getRowSpan() - 1;
+ sal_Int32 nColSpan = xCell->getColumnSpan() - 1;
+
+ if( (nCol >= mnLeft) && (nCol <= mnRight) )
+ {
+ sal_Int32 nCellsAvailable = 1 + nRowSpan + rLeftOvers[nCol];
+ if( nRowSpan == 0 )
+ nCellsAvailable += nNewRows;
+
+ DBG_ASSERT( nCellsAvailable > nRows, "sdr::table::CellCursor::split_row(), somethings wrong" );
+
+ sal_Int32 nSplitSpan = (nCellsAvailable / (nRows + 1)) - 1;
+
+ sal_Int32 nSplitRow = nRow;
+ sal_Int32 nSplits = nRows + 1;
+ while( nSplits-- )
+ {
+ // last split eats rounding cells
+ if( nSplits == 0 )
+ nSplitSpan = nCellsAvailable - ((nSplitSpan+1) * nRows) - 1;
+
+ mxTable->merge( nCol, nSplitRow, nColSpan + 1, nSplitSpan + 1 );
+ if( nSplits > 0 )
+ nSplitRow += nSplitSpan + 1;
+ }
+
+ do
+ {
+ rLeftOvers[nCol++] = 0;
+ }
+ while( nColSpan-- );
+ --nCol;
+ }
+ else
+ {
+ // cope with outside cells, merge if needed
+ if( nRowSpan < (rLeftOvers[nCol] + nNewRows) )
+ mxTable->merge( nCol, nRow, nColSpan + 1, (rLeftOvers[nCol] + nNewRows) + 1 );
+
+ do
+ {
+ rLeftOvers[nCol++] = 0; // consumed
+ }
+ while( nColSpan-- );
+ --nCol;
+ }
+ }
+ }
+}
+
+
+void CellCursor::split_vertical( sal_Int32 nRows )
+{
+ const sal_Int32 nColCount = mxTable->getColumnCount();
+
+ std::vector< sal_Int32 > aLeftOvers( nColCount );
+
+ for( sal_Int32 nRow = mnBottom; nRow >= mnTop; --nRow )
+ split_row( nRow, nRows, aLeftOvers );
+}
+
+
+void SAL_CALL CellCursor::split( sal_Int32 nColumns, sal_Int32 nRows )
+{
+ if( (nColumns < 0) || (nRows < 0) )
+ throw IllegalArgumentException();
+
+ if( !mxTable.is() || (mxTable->getSdrTableObj() == nullptr) )
+ throw DisposedException();
+
+ SdrModel& rModel(mxTable->getSdrTableObj()->getSdrModelFromSdrObject());
+ const bool bUndo(mxTable->getSdrTableObj()->IsInserted() && rModel.IsUndoEnabled());
+
+ if( bUndo )
+ rModel.BegUndo( SvxResId(STR_TABLE_SPLIT) );
+
+ try
+ {
+ if( nColumns > 0 )
+ split_horizontal( nColumns );
+
+ if( nRows > 0 )
+ split_vertical( nRows );
+
+ if( nColumns > 0 ||nRows > 0 )
+ mxTable->setModified(true);
+ }
+ catch( Exception& )
+ {
+ TOOLS_WARN_EXCEPTION("svx.table", "");
+ throw NoSupportException();
+ }
+
+ if( bUndo )
+ rModel.EndUndo();
+
+ rModel.SetChanged();
+}
+
+
+sal_Bool SAL_CALL CellCursor::isMergeable( )
+{
+ CellPos aStart, aEnd;
+ return GetMergedSelection( aStart, aEnd );
+}
+
+
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/table/cellcursor.hxx b/svx/source/table/cellcursor.hxx
new file mode 100644
index 000000000..e6756c926
--- /dev/null
+++ b/svx/source/table/cellcursor.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_SVX_SOURCE_TABLE_CELLCURSOR_HXX
+#define INCLUDED_SVX_SOURCE_TABLE_CELLCURSOR_HXX
+
+#include <com/sun/star/table/XMergeableCellRange.hpp>
+#include <com/sun/star/table/XCellCursor.hpp>
+#include <cppuhelper/implbase.hxx>
+#include "cellrange.hxx"
+
+
+namespace sdr::table {
+
+struct CellPos;
+
+typedef ::cppu::ImplInheritanceHelper< CellRange, css::table::XCellCursor, css::table::XMergeableCellRange > CellCursorBase;
+
+class CellCursor final : public CellCursorBase
+{
+public:
+ CellCursor( const TableModelRef& xTableModel, sal_Int32 nLeft, sal_Int32 nTop, sal_Int32 nRight, sal_Int32 nBottom );
+ virtual ~CellCursor() override;
+
+ // XCellRange
+ virtual css::uno::Reference< css::table::XCell > SAL_CALL getCellByPosition( sal_Int32 nColumn, sal_Int32 nRow ) override;
+ virtual css::uno::Reference< css::table::XCellRange > SAL_CALL getCellRangeByPosition( sal_Int32 nLeft, sal_Int32 nTop, sal_Int32 nRight, sal_Int32 nBottom ) override;
+ virtual css::uno::Reference< css::table::XCellRange > SAL_CALL getCellRangeByName( const OUString& aRange ) override;
+
+ // XCellCursor
+ virtual void SAL_CALL gotoStart( ) override;
+ virtual void SAL_CALL gotoEnd( ) override;
+ virtual void SAL_CALL gotoNext( ) override;
+ virtual void SAL_CALL gotoPrevious( ) override;
+ virtual void SAL_CALL gotoOffset( ::sal_Int32 nColumnOffset, ::sal_Int32 nRowOffset ) override;
+
+ // XMergeableCellRange
+ virtual void SAL_CALL merge( ) override;
+ virtual void SAL_CALL split( ::sal_Int32 Columns, ::sal_Int32 Rows ) override;
+ virtual sal_Bool SAL_CALL isMergeable( ) override;
+
+private:
+ bool GetMergedSelection( CellPos& rStart, CellPos& rEnd );
+
+ void split_column( sal_Int32 nCol, sal_Int32 nColumns, std::vector< sal_Int32 >& rLeftOvers );
+ void split_horizontal( sal_Int32 nColumns );
+ void split_row( sal_Int32 nRow, sal_Int32 nRows, std::vector< sal_Int32 >& rLeftOvers );
+ void split_vertical( sal_Int32 nRows );
+};
+
+}
+
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/table/cellrange.cxx b/svx/source/table/cellrange.cxx
new file mode 100644
index 000000000..f9941fd24
--- /dev/null
+++ b/svx/source/table/cellrange.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 <sal/config.h>
+
+#include <com/sun/star/lang/IndexOutOfBoundsException.hpp>
+
+#include "cellrange.hxx"
+
+
+using namespace ::com::sun::star::uno;
+using namespace ::com::sun::star::lang;
+using namespace ::com::sun::star::container;
+using namespace ::com::sun::star::table;
+
+
+namespace sdr::table {
+
+CellRange::CellRange( const TableModelRef & xTable, sal_Int32 nLeft, sal_Int32 nTop, sal_Int32 nRight, sal_Int32 nBottom )
+: mxTable( xTable )
+, mnLeft(nLeft)
+, mnTop(nTop)
+, mnRight(nRight)
+, mnBottom(nBottom)
+{
+}
+
+
+CellRange::~CellRange()
+{
+}
+
+
+// ICellRange
+
+
+sal_Int32 CellRange::getLeft()
+{
+ return mnLeft;
+}
+
+sal_Int32 CellRange::getTop()
+{
+ return mnTop;
+}
+
+sal_Int32 CellRange::getRight()
+{
+ return mnRight;
+}
+
+sal_Int32 CellRange::getBottom()
+{
+ return mnBottom;
+}
+
+Reference< XTable > CellRange::getTable()
+{
+ return mxTable;
+}
+
+
+// XCellRange
+
+
+Reference< XCell > SAL_CALL CellRange::getCellByPosition( sal_Int32 nColumn, sal_Int32 nRow )
+{
+ return mxTable->getCellByPosition( mnLeft + nColumn, mnTop + nRow );
+}
+
+
+Reference< XCellRange > SAL_CALL CellRange::getCellRangeByPosition( sal_Int32 nLeft, sal_Int32 nTop, sal_Int32 nRight, sal_Int32 nBottom )
+{
+ if( (nLeft >= 0 ) && (nTop >= 0) && (nRight >= nLeft) && (nBottom >= nTop) )
+ {
+ nLeft += mnLeft;
+ nTop += mnTop;
+ nRight += mnLeft;
+ nBottom += mnTop;
+
+ const sal_Int32 nMaxColumns = (mnRight == -1) ? mxTable->getColumnCount() : mnLeft;
+ const sal_Int32 nMaxRows = (mnBottom == -1) ? mxTable->getRowCount() : mnBottom;
+ if( (nLeft < nMaxColumns) && (nRight < nMaxColumns) && (nTop < nMaxRows) && (nBottom < nMaxRows) )
+ {
+ return mxTable->getCellRangeByPosition( nLeft, nTop, nRight, nBottom );
+ }
+ }
+ throw IndexOutOfBoundsException();
+}
+
+
+Reference< XCellRange > SAL_CALL CellRange::getCellRangeByName( const OUString& /*aRange*/ )
+{
+ return Reference< XCellRange >();
+}
+
+
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/table/cellrange.hxx b/svx/source/table/cellrange.hxx
new file mode 100644
index 000000000..b4802eff0
--- /dev/null
+++ b/svx/source/table/cellrange.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_SVX_SOURCE_TABLE_CELLRANGE_HXX
+#define INCLUDED_SVX_SOURCE_TABLE_CELLRANGE_HXX
+
+#include <com/sun/star/table/XCellRange.hpp>
+#include <cppuhelper/implbase1.hxx>
+
+#include <tablemodel.hxx>
+
+
+namespace sdr::table {
+
+class CellRange : public ::cppu::WeakAggImplHelper1< css::table::XCellRange >, public ICellRange
+{
+public:
+ CellRange( const TableModelRef & xTable, sal_Int32 nLeft, sal_Int32 nTop, sal_Int32 nRight, sal_Int32 nBottom );
+ virtual ~CellRange() override;
+
+ // ICellRange
+ virtual sal_Int32 getLeft() override;
+ virtual sal_Int32 getTop() override;
+ virtual sal_Int32 getRight() override;
+ virtual sal_Int32 getBottom() override;
+ virtual css::uno::Reference< css::table::XTable > getTable() override;
+
+ // XCellRange
+ virtual css::uno::Reference< css::table::XCell > SAL_CALL getCellByPosition( sal_Int32 nColumn, sal_Int32 nRow ) override;
+ virtual css::uno::Reference< css::table::XCellRange > SAL_CALL getCellRangeByPosition( sal_Int32 nLeft, sal_Int32 nTop, sal_Int32 nRight, sal_Int32 nBottom ) override;
+ virtual css::uno::Reference< css::table::XCellRange > SAL_CALL getCellRangeByName( const OUString& aRange ) override;
+
+protected:
+ TableModelRef mxTable;
+ sal_Int32 mnLeft;
+ sal_Int32 mnTop;
+ sal_Int32 mnRight;
+ sal_Int32 mnBottom;
+};
+
+}
+
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/table/propertyset.cxx b/svx/source/table/propertyset.cxx
new file mode 100644
index 000000000..0dc90358c
--- /dev/null
+++ b/svx/source/table/propertyset.cxx
@@ -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 .
+ */
+
+
+#include "propertyset.hxx"
+
+using namespace ::com::sun::star::uno;
+using namespace ::com::sun::star::beans;
+using namespace ::com::sun::star::lang;
+
+namespace sdr::table {
+
+FastPropertySetInfo::FastPropertySetInfo( const PropertyVector& rProps )
+{
+ addProperties( rProps );
+}
+
+
+FastPropertySetInfo::~FastPropertySetInfo()
+{
+}
+
+
+void FastPropertySetInfo::addProperties( const PropertyVector& rProps )
+{
+ sal_uInt32 nIndex = maProperties.size();
+ sal_uInt32 nCount = rProps.size();
+ maProperties.resize( nIndex + nCount );
+ for( const Property& rProperty : rProps )
+ {
+ maProperties[nIndex] = rProperty;
+ maMap[ rProperty.Name ] = nIndex++;
+ }
+}
+
+
+const Property& FastPropertySetInfo::getProperty( const OUString& aName )
+{
+ PropertyMap::iterator aIter( maMap.find( aName ) );
+ if( aIter == maMap.end() )
+ throw UnknownPropertyException( aName, static_cast<cppu::OWeakObject*>(this));
+ return maProperties[(*aIter).second];
+}
+
+
+const Property* FastPropertySetInfo::hasProperty( const OUString& aName )
+{
+ PropertyMap::iterator aIter( maMap.find( aName ) );
+ if( aIter == maMap.end() )
+ return nullptr;
+ else
+ return &maProperties[(*aIter).second];
+}
+
+
+// XPropertySetInfo
+
+
+Sequence< Property > SAL_CALL FastPropertySetInfo::getProperties()
+{
+ return Sequence< Property >( maProperties.data(), maProperties.size() );
+}
+
+
+Property SAL_CALL FastPropertySetInfo::getPropertyByName( const OUString& aName )
+{
+ return getProperty( aName );
+}
+
+
+sal_Bool SAL_CALL FastPropertySetInfo::hasPropertyByName( const OUString& aName )
+{
+ return hasProperty( aName ) != nullptr;
+}
+
+FastPropertySet::FastPropertySet( const rtl::Reference< FastPropertySetInfo >& xInfo )
+: mxInfo( xInfo )
+{
+}
+
+
+FastPropertySet::~FastPropertySet()
+{
+}
+
+
+// XPropertySet
+
+
+Reference< XPropertySetInfo > SAL_CALL FastPropertySet::getPropertySetInfo( )
+{
+ return mxInfo;
+}
+
+
+void SAL_CALL FastPropertySet::setPropertyValue( const OUString& aPropertyName, const Any& aValue )
+{
+ setFastPropertyValue( mxInfo->getProperty( aPropertyName ).Handle, aValue );
+}
+
+
+Any SAL_CALL FastPropertySet::getPropertyValue( const OUString& aPropertyName )
+{
+ return getFastPropertyValue( mxInfo->getProperty( aPropertyName ).Handle );
+}
+
+
+void SAL_CALL FastPropertySet::addPropertyChangeListener( const OUString&, const Reference< XPropertyChangeListener >& )
+{
+}
+
+
+void SAL_CALL FastPropertySet::removePropertyChangeListener( const OUString&, const Reference< XPropertyChangeListener >& )
+{
+}
+
+
+void SAL_CALL FastPropertySet::addVetoableChangeListener( const OUString&, const Reference< XVetoableChangeListener >& )
+{
+}
+
+
+void SAL_CALL FastPropertySet::removeVetoableChangeListener( const OUString&, const Reference< XVetoableChangeListener >& )
+{
+}
+
+
+// XMultiPropertySet
+
+
+void SAL_CALL FastPropertySet::setPropertyValues( const Sequence< OUString >& aPropertyNames, const Sequence< Any >& aValues )
+{
+ if( aPropertyNames.getLength() != aValues.getLength() )
+ throw IllegalArgumentException();
+
+ const Any* pValues = aValues.getConstArray();
+ for( const OUString& rPropertyName : aPropertyNames )
+ {
+ const Property* pProperty = mxInfo->hasProperty( rPropertyName );
+ if( pProperty ) try
+ {
+ setFastPropertyValue( pProperty->Handle, *pValues );
+ }
+ catch( UnknownPropertyException& )
+ {
+ }
+ pValues++;
+ }
+}
+
+
+Sequence< Any > SAL_CALL FastPropertySet::getPropertyValues( const Sequence< OUString >& aPropertyNames )
+{
+ sal_Int32 nCount = aPropertyNames.getLength();
+ Sequence< Any > aValues( nCount );
+
+ Any* pValues = aValues.getArray();
+ for( const OUString& rPropertyName : aPropertyNames )
+ {
+ const Property* pProperty = mxInfo->hasProperty( rPropertyName );
+ if( pProperty ) try
+ {
+ *pValues = getFastPropertyValue( pProperty->Handle );
+ }
+ catch( UnknownPropertyException& )
+ {
+ }
+ pValues++;
+ }
+ return aValues;
+}
+
+
+void SAL_CALL FastPropertySet::addPropertiesChangeListener( const Sequence< OUString >&, const Reference< XPropertiesChangeListener >& )
+{
+}
+
+
+void SAL_CALL FastPropertySet::removePropertiesChangeListener( const Reference< XPropertiesChangeListener >& )
+{
+}
+
+
+void SAL_CALL FastPropertySet::firePropertiesChangeEvent( const Sequence< OUString >&, const Reference< XPropertiesChangeListener >& )
+{
+}
+
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/table/propertyset.hxx b/svx/source/table/propertyset.hxx
new file mode 100644
index 000000000..69500afe0
--- /dev/null
+++ b/svx/source/table/propertyset.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_SVX_SOURCE_TABLE_PROPERTYSET_HXX
+#define INCLUDED_SVX_SOURCE_TABLE_PROPERTYSET_HXX
+
+#include <com/sun/star/beans/XPropertySet.hpp>
+#include <com/sun/star/beans/XMultiPropertySet.hpp>
+#include <com/sun/star/beans/XFastPropertySet.hpp>
+#include <rtl/ref.hxx>
+#include <unordered_map>
+#include <vector>
+
+#include <cppuhelper/implbase1.hxx>
+#include <cppuhelper/implbase3.hxx>
+
+namespace sdr::table {
+
+typedef std::vector< css::beans::Property > PropertyVector;
+typedef std::unordered_map< OUString, ::sal_uInt32 > PropertyMap;
+
+class FastPropertySetInfo : public ::cppu::WeakAggImplHelper1< css::beans::XPropertySetInfo >
+{
+public:
+ explicit FastPropertySetInfo( const PropertyVector& rProps );
+ virtual ~FastPropertySetInfo() override;
+
+ void addProperties( const PropertyVector& rProps );
+
+ /// @throws css::beans::UnknownPropertyException
+ const css::beans::Property& getProperty( const OUString& aName );
+ const css::beans::Property* hasProperty( const OUString& aName );
+
+ // XPropertySetInfo
+ virtual css::uno::Sequence< css::beans::Property > SAL_CALL getProperties( ) override;
+ virtual css::beans::Property SAL_CALL getPropertyByName( const OUString& aName ) override;
+ virtual sal_Bool SAL_CALL hasPropertyByName( const OUString& Name ) override;
+
+private:
+ PropertyVector maProperties;
+ PropertyMap maMap;
+};
+
+
+class FastPropertySet : public ::cppu::WeakAggImplHelper3< css::beans::XPropertySet, css::beans::XMultiPropertySet, css::beans::XFastPropertySet >
+{
+public:
+ explicit FastPropertySet( const rtl::Reference< FastPropertySetInfo >& xInfo );
+ virtual ~FastPropertySet() 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;
+
+ // XMultiPropertySet
+// virtual css::uno::Reference< css::beans::XPropertySetInfo > SAL_CALL getPropertySetInfo( ) throw (css::uno::RuntimeException);
+ 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;
+
+ // XFastPropertySet
+ virtual void SAL_CALL setFastPropertyValue( ::sal_Int32 nHandle, const css::uno::Any& aValue ) override = 0;
+ virtual css::uno::Any SAL_CALL getFastPropertyValue( ::sal_Int32 nHandle ) override = 0;
+
+private:
+ rtl::Reference< FastPropertySetInfo > mxInfo;
+};
+
+}
+
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/table/svdotable.cxx b/svx/source/table/svdotable.cxx
new file mode 100644
index 000000000..8b46feb29
--- /dev/null
+++ b/svx/source/table/svdotable.cxx
@@ -0,0 +1,2534 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * 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/container/XNamed.hpp>
+#include <com/sun/star/container/XNameAccess.hpp>
+#include <com/sun/star/container/XIndexAccess.hpp>
+#include <unotools/configmgr.hxx>
+#include <vcl/canvastools.hxx>
+#include <vcl/ptrstyle.hxx>
+#include <com/sun/star/style/XStyle.hpp>
+#include <com/sun/star/beans/XPropertySet.hpp>
+#include <basegfx/polygon/b2dpolygontools.hxx>
+#include <basegfx/polygon/b2dpolypolygon.hxx>
+#include <basegfx/polygon/b2dpolygon.hxx>
+#include <svl/style.hxx>
+#include <editeng/editstat.hxx>
+#include <editeng/outlobj.hxx>
+#include <sdr/properties/textproperties.hxx>
+#include <svx/svdmodel.hxx>
+#include <svx/svdotable.hxx>
+#include <svx/svdhdl.hxx>
+#include "viewcontactoftableobj.hxx"
+#include <svx/svdoutl.hxx>
+#include <svx/svddrag.hxx>
+#include <tablemodel.hxx>
+#include <cell.hxx>
+#include "tablelayouter.hxx"
+#include "tablehandles.hxx"
+#include <svx/sdr/table/tabledesign.hxx>
+#include <svx/svdundo.hxx>
+#include <svx/strings.hrc>
+#include <svx/dialmgr.hxx>
+#include <editeng/writingmodeitem.hxx>
+#include <editeng/frmdiritem.hxx>
+#include <cppuhelper/implbase.hxx>
+#include <libxml/xmlwriter.h>
+#include <tools/diagnose_ex.h>
+
+#include <boost/property_tree/ptree.hpp>
+
+using ::com::sun::star::uno::Any;
+using ::com::sun::star::uno::Reference;
+using ::com::sun::star::uno::UNO_QUERY;
+using ::com::sun::star::uno::UNO_QUERY_THROW;
+using ::com::sun::star::uno::Exception;
+using ::com::sun::star::container::XIndexAccess;
+using ::com::sun::star::style::XStyle;
+using ::com::sun::star::table::XTableRows;
+using ::com::sun::star::table::XTableColumns;
+using ::com::sun::star::table::XTable;
+using ::com::sun::star::beans::XPropertySet;
+using ::com::sun::star::util::XModifyBroadcaster;
+using sdr::properties::TextProperties;
+using sdr::properties::BaseProperties;
+using namespace ::com::sun::star;
+using namespace ::com::sun::star::text;
+using namespace ::com::sun::star::container;
+using namespace ::com::sun::star::style;
+
+namespace sdr::table {
+
+namespace {
+
+class TableProperties : public TextProperties
+{
+protected:
+ // create a new itemset
+ SfxItemSet CreateObjectSpecificItemSet(SfxItemPool& rPool) override;
+
+public:
+ // basic constructor
+ explicit TableProperties(SdrObject& rObj );
+
+ // constructor for copying, but using new object
+ TableProperties(const TableProperties& rProps, SdrObject& rObj );
+
+ // Clone() operator, normally just calls the local copy constructor
+ std::unique_ptr<BaseProperties> Clone(SdrObject& rObj) const override;
+
+ virtual void ItemChange(const sal_uInt16 nWhich, const SfxPoolItem* pNewItem = nullptr) override;
+};
+
+}
+
+TableProperties::TableProperties(SdrObject& rObj)
+: TextProperties(rObj)
+{
+}
+
+TableProperties::TableProperties(const TableProperties& rProps, SdrObject& rObj)
+: TextProperties(rProps, rObj)
+{
+}
+
+std::unique_ptr<BaseProperties> TableProperties::Clone(SdrObject& rObj) const
+{
+ return std::unique_ptr<BaseProperties>(new TableProperties(*this, rObj));
+}
+
+void TableProperties::ItemChange(const sal_uInt16 nWhich, const SfxPoolItem* pNewItem)
+{
+ if( nWhich == SDRATTR_TEXTDIRECTION )
+ AttributeProperties::ItemChange( nWhich, pNewItem );
+ else
+ TextProperties::ItemChange( nWhich, pNewItem );
+}
+
+// create a new itemset
+SfxItemSet TableProperties::CreateObjectSpecificItemSet(SfxItemPool& rPool)
+{
+ return SfxItemSet(rPool,
+
+ // range from SdrAttrObj
+ svl::Items<SDRATTR_START, SDRATTR_SHADOW_LAST,
+ SDRATTR_MISC_FIRST, SDRATTR_MISC_LAST,
+ SDRATTR_TEXTDIRECTION, SDRATTR_TEXTDIRECTION,
+
+ // range for SdrTableObj
+ SDRATTR_TABLE_FIRST, SDRATTR_TABLE_LAST,
+
+ // range from SdrTextObj
+ EE_ITEMS_START, EE_ITEMS_END>);
+}
+
+namespace {
+
+class TableObjectGeoData : public SdrTextObjGeoData
+{
+public:
+ tools::Rectangle maLogicRect;
+};
+
+}
+
+TableStyleSettings::TableStyleSettings()
+: mbUseFirstRow(true)
+, mbUseLastRow(false)
+, mbUseFirstColumn(false)
+, mbUseLastColumn(false)
+, mbUseRowBanding(true)
+, mbUseColumnBanding(false)
+{
+}
+
+TableStyleSettings::TableStyleSettings( const TableStyleSettings& rStyle )
+{
+ (*this) = rStyle;
+}
+
+TableStyleSettings& TableStyleSettings::operator=(const TableStyleSettings& rStyle)
+{
+ mbUseFirstRow = rStyle.mbUseFirstRow;
+ mbUseLastRow = rStyle.mbUseLastRow;
+ mbUseFirstColumn = rStyle.mbUseFirstColumn;
+ mbUseLastColumn = rStyle.mbUseLastColumn;
+ mbUseRowBanding = rStyle.mbUseRowBanding;
+ mbUseColumnBanding = rStyle.mbUseColumnBanding;
+ return *this;
+}
+
+bool TableStyleSettings::operator==( const TableStyleSettings& rStyle ) const
+{
+ return
+ (mbUseFirstRow == rStyle.mbUseFirstRow) &&
+ (mbUseLastRow == rStyle.mbUseLastRow) &&
+ (mbUseFirstColumn == rStyle.mbUseFirstColumn) &&
+ (mbUseLastColumn == rStyle.mbUseLastColumn) &&
+ (mbUseRowBanding == rStyle.mbUseRowBanding) &&
+ (mbUseColumnBanding == rStyle.mbUseColumnBanding);
+}
+
+
+class SdrTableObjImpl : public TableDesignUser, public ::cppu::WeakImplHelper< css::util::XModifyListener >
+{
+public:
+ CellRef mxActiveCell;
+ TableModelRef mxTable;
+ SdrTableObj* mpTableObj;
+ std::unique_ptr<TableLayouter> mpLayouter;
+ CellPos maEditPos;
+ TableStyleSettings maTableStyle;
+ Reference< XIndexAccess > mxTableStyle;
+ std::vector<std::unique_ptr<SdrUndoAction>> maUndos;
+ bool mbSkipChangeLayout;
+
+ void CropTableModelToSelection(const CellPos& rStart, const CellPos& rEnd);
+
+ CellRef getCell( const CellPos& rPos ) const;
+ void LayoutTable( tools::Rectangle& rArea, bool bFitWidth, bool bFitHeight );
+
+ void ApplyCellStyles();
+ void UpdateCells( tools::Rectangle const & rArea );
+
+ SdrTableObjImpl();
+ virtual ~SdrTableObjImpl() override;
+
+ void init( SdrTableObj* pTable, sal_Int32 nColumns, sal_Int32 nRows );
+ void dispose();
+
+ sal_Int32 getColumnCount() const;
+ /// Get widths of the columns in the table.
+ std::vector<sal_Int32> getColumnWidths() const;
+ sal_Int32 getRowCount() const;
+
+ void DragEdge( bool mbHorizontal, int nEdge, sal_Int32 nOffset );
+
+ SdrTableObjImpl& operator=( const SdrTableObjImpl& rSource );
+
+ // XModifyListener
+ virtual void SAL_CALL modified( const css::lang::EventObject& aEvent ) override;
+
+ // XEventListener
+ virtual void SAL_CALL disposing( const css::lang::EventObject& Source ) override;
+
+ void update();
+
+ void connectTableStyle();
+ void disconnectTableStyle();
+ virtual bool isInUse() override;
+ void dumpAsXml(xmlTextWriterPtr pWriter) const;
+private:
+ static SdrTableObjImpl* lastLayoutTable;
+ static tools::Rectangle lastLayoutInputRectangle;
+ static tools::Rectangle lastLayoutResultRectangle;
+ static bool lastLayoutFitWidth;
+ static bool lastLayoutFitHeight;
+ static WritingMode lastLayoutMode;
+ static sal_Int32 lastRowCount;
+ static sal_Int32 lastColCount;
+ static std::vector<sal_Int32> lastColWidths;
+ static bool rowSizeChanged;
+};
+
+SdrTableObjImpl* SdrTableObjImpl::lastLayoutTable = nullptr;
+tools::Rectangle SdrTableObjImpl::lastLayoutInputRectangle;
+tools::Rectangle SdrTableObjImpl::lastLayoutResultRectangle;
+bool SdrTableObjImpl::lastLayoutFitWidth;
+bool SdrTableObjImpl::lastLayoutFitHeight;
+WritingMode SdrTableObjImpl::lastLayoutMode;
+sal_Int32 SdrTableObjImpl::lastRowCount;
+sal_Int32 SdrTableObjImpl::lastColCount;
+bool SdrTableObjImpl::rowSizeChanged = false;
+std::vector<sal_Int32> SdrTableObjImpl::lastColWidths;
+
+SdrTableObjImpl::SdrTableObjImpl()
+: mpTableObj( nullptr )
+, mbSkipChangeLayout(false)
+{
+}
+
+
+SdrTableObjImpl::~SdrTableObjImpl()
+{
+ if( lastLayoutTable == this )
+ lastLayoutTable = nullptr;
+}
+
+
+void SdrTableObjImpl::CropTableModelToSelection(const CellPos& rStart, const CellPos& rEnd)
+{
+ if(!mxTable.is())
+ {
+ return;
+ }
+
+ const sal_Int32 nColumns(rEnd.mnCol - rStart.mnCol + 1);
+ const sal_Int32 nRows(rEnd.mnRow - rStart.mnRow + 1);
+
+ if(nColumns < 1 || nRows < 1 || nColumns > getColumnCount() || nRows > getRowCount())
+ {
+ return;
+ }
+
+ // tdf#116977 First thought was to create the new TableModel, copy data to it and then exchange
+ // mxTable and dispose old one. This does *not* work, even when all stuff looks nicely referenced
+ // and safe *because* Cell::create gets handed over the current SdrTableObj, hands it to
+ // ::Cell and there the local mxTable is initialized using rTableObj.getTable() (!). Due to This,
+ // the new created Cells in a new created TableModel based on given mpTableObj *will be disposed*
+ // when the old mxTable gets disposed - ARGH!
+ // To avoid, change strategy: Remember old TableModel, reset mxTable immediately - this is the
+ // SdrTableObjImpl of the current SdrTableObj anyways. Luckily, this works as intended...
+
+ // remember old TableModel
+ TableModelRef xOldTable(mxTable);
+
+ // immediately create new one and initialize. This creates ::Cell's which then will use
+ // the correct TableModel (accessed through SdrTableObj, but using local mxTable)
+ mxTable = new TableModel(mpTableObj);
+ mxTable->init(nColumns, nRows);
+
+ // copy cells
+ for( sal_Int32 nRow = 0; nRow < nRows; ++nRow )
+ {
+ for( sal_Int32 nCol = 0; nCol < nColumns; ++nCol ) try
+ {
+ CellRef xTargetCell( dynamic_cast< Cell* >( mxTable->getCellByPosition( nCol, nRow ).get() ) );
+ if( xTargetCell.is() )
+ xTargetCell->cloneFrom( dynamic_cast< Cell* >( xOldTable->getCellByPosition( rStart.mnCol + nCol, rStart.mnRow + nRow ).get() ) );
+ }
+ catch( Exception& )
+ {
+ TOOLS_WARN_EXCEPTION("svx.table", "");
+ }
+ }
+
+ // copy row heights
+ Reference< XTableRows > xNewRows(mxTable->getRows(), css::uno::UNO_SET_THROW );
+ static const OUStringLiteral sHeight( u"Height" );
+ for( sal_Int32 nRow = 0; nRow < nRows; ++nRow )
+ {
+ Reference< XPropertySet > xNewSet( xNewRows->getByIndex( nRow ), UNO_QUERY_THROW );
+ xNewSet->setPropertyValue( sHeight, Any( mpLayouter->getRowHeight( rStart.mnRow + nRow ) ) );
+ }
+
+ // copy column widths
+ Reference< XTableColumns > xNewColumns( mxTable->getColumns(), css::uno::UNO_SET_THROW );
+ static const OUStringLiteral sWidth( u"Width" );
+ for( sal_Int32 nCol = 0; nCol < nColumns; ++nCol )
+ {
+ Reference< XPropertySet > xNewSet( xNewColumns->getByIndex( nCol ), UNO_QUERY_THROW );
+ xNewSet->setPropertyValue( sWidth, Any( mpLayouter->getColumnWidth( rStart.mnCol + nCol ) ) );
+ }
+
+ // reset layouter which still holds a copy to old TableModel
+ mpLayouter.reset();
+
+ // cleanup old TableModel
+ {
+ Reference< XModifyListener > xListener( static_cast< css::util::XModifyListener* >(this) );
+ xOldTable->removeModifyListener( xListener );
+ xOldTable->dispose();
+ xOldTable.clear();
+ }
+
+ // create and hand over to new TableLayouter
+ mpLayouter.reset(new TableLayouter( mxTable ));
+
+ // add needed listener to react on changes
+ Reference< XModifyListener > xListener( static_cast< css::util::XModifyListener* >(this) );
+ mxTable->addModifyListener( xListener );
+
+ // Apply Style to Cells
+ ApplyCellStyles();
+
+ // layout cropped table
+ LayoutTable( mpTableObj->maRect, false, false );
+}
+
+void SdrTableObjImpl::init( SdrTableObj* pTable, sal_Int32 nColumns, sal_Int32 nRows )
+{
+ mpTableObj = pTable;
+ mxTable = new TableModel( pTable );
+ mxTable->init( nColumns, nRows );
+ Reference< XModifyListener > xListener( static_cast< css::util::XModifyListener* >(this) );
+ mxTable->addModifyListener( xListener );
+ mpLayouter.reset(new TableLayouter( mxTable ));
+ LayoutTable( mpTableObj->maRect, true, true );
+ mpTableObj->maLogicRect = mpTableObj->maRect;
+}
+
+
+SdrTableObjImpl& SdrTableObjImpl::operator=( const SdrTableObjImpl& rSource )
+{
+ if(this == &rSource)
+ {
+ return *this;
+ }
+
+ if(nullptr == mpTableObj || nullptr == rSource.mpTableObj)
+ {
+ // error: need both SdrObjects to successfully copy data
+ return *this;
+ }
+
+ // remove evtl. listeners from local
+ disconnectTableStyle();
+
+ // reset layouter which holds a copy
+ mpLayouter.reset();
+
+ // cleanup local mxTable if used
+ if( mxTable.is() )
+ {
+ Reference< XModifyListener > xListener( static_cast< css::util::XModifyListener* >(this) );
+ mxTable->removeModifyListener( xListener );
+ mxTable->dispose();
+ mxTable.clear();
+ }
+
+ // tdf#127481: reset active cell reference
+ mxActiveCell.clear();
+
+ // copy TableStyle (short internal data)
+ maTableStyle = rSource.maTableStyle;
+
+ // create/copy new mxTable. This will copy all needed cells, too
+ mxTable = new TableModel( mpTableObj, rSource.mxTable );
+
+ // create and hand over to new TableLayouter
+ mpLayouter.reset(new TableLayouter( mxTable ));
+
+ // add needed listener to react on changes
+ Reference< XModifyListener > xListener( static_cast< css::util::XModifyListener* >(this) );
+ mxTable->addModifyListener( xListener );
+
+ // handle TableStyle
+ Reference< XIndexAccess > xNewTableStyle;
+ SdrModel& rSourceSdrModel(rSource.mpTableObj->getSdrModelFromSdrObject());
+ SdrModel& rTargetSdrModel(mpTableObj->getSdrModelFromSdrObject());
+
+ if(rSource.mxTableStyle.is() && &rSourceSdrModel == &rTargetSdrModel)
+ {
+ // source and target model the same -> keep current TableStyle
+ xNewTableStyle = rSource.mxTableStyle;
+ }
+
+ if(!xNewTableStyle.is() && rSource.mxTableStyle.is()) try
+ {
+ // search in target SdrModel for that TableStyle
+ const OUString sStyleName( Reference< XNamed >( rSource.mxTableStyle, UNO_QUERY_THROW )->getName() );
+ Reference< XStyleFamiliesSupplier > xSFS(rTargetSdrModel.getUnoModel(), UNO_QUERY_THROW );
+ Reference< XNameAccess > xFamilyNameAccess( xSFS->getStyleFamilies(), css::uno::UNO_SET_THROW );
+ Reference< XNameAccess > xTableFamilyAccess( xFamilyNameAccess->getByName( "table" ), UNO_QUERY_THROW );
+
+ if( xTableFamilyAccess->hasByName( sStyleName ) )
+ {
+ // found table style with the same name
+ xTableFamilyAccess->getByName( sStyleName ) >>= xNewTableStyle;
+ }
+ else
+ {
+ // copy or? Not found, use 1st existing TableStyle (or none)
+ Reference< XIndexAccess > xIndexAccess( xTableFamilyAccess, UNO_QUERY_THROW );
+ xIndexAccess->getByIndex( 0 ) >>= xNewTableStyle;
+ }
+ }
+ catch( Exception& )
+ {
+ TOOLS_WARN_EXCEPTION("svx.table", "");
+ }
+
+ // set that TableStyle
+ mxTableStyle = xNewTableStyle;
+
+ // Apply Style to Cells
+ ApplyCellStyles();
+
+ // copy geometry
+ mpTableObj->maRect = mpTableObj->maLogicRect;
+
+ // layout cloned table
+ LayoutTable( mpTableObj->maRect, false, false );
+
+ // re-connect to styles (evtl. in new SdrModel)
+ connectTableStyle();
+
+ return *this;
+}
+
+void SdrTableObjImpl::ApplyCellStyles()
+{
+ if( !mxTable.is() || !mxTableStyle.is() )
+ return;
+
+ const sal_Int32 nColCount = getColumnCount();
+ const sal_Int32 nRowCount = getRowCount();
+
+ const TableStyleSettings& rStyle = maTableStyle;
+
+ CellPos aPos;
+ for( aPos.mnRow = 0; aPos.mnRow < nRowCount; ++aPos.mnRow )
+ {
+ const bool bFirstRow = (aPos.mnRow == 0) && rStyle.mbUseFirstRow;
+ const bool bLastRow = (aPos.mnRow == nRowCount-1) && rStyle.mbUseLastRow;
+
+ for( aPos.mnCol = 0; aPos.mnCol < nColCount; ++aPos.mnCol )
+ {
+ Reference< XStyle > xStyle;
+
+ // first and last row win first, if used and available
+ if( bFirstRow )
+ {
+ mxTableStyle->getByIndex(first_row_style) >>= xStyle;
+ }
+ else if( bLastRow )
+ {
+ mxTableStyle->getByIndex(last_row_style) >>= xStyle;
+ }
+
+ if( !xStyle.is() )
+ {
+ // next come first and last column, if used and available
+ if( rStyle.mbUseFirstColumn && (aPos.mnCol == 0) )
+ {
+ mxTableStyle->getByIndex(first_column_style) >>= xStyle;
+ }
+ else if( rStyle.mbUseLastColumn && (aPos.mnCol == nColCount-1) )
+ {
+ mxTableStyle->getByIndex(last_column_style) >>= xStyle;
+ }
+ }
+
+ if( !xStyle.is() && rStyle.mbUseRowBanding )
+ {
+ if( (aPos.mnRow & 1) == 0 )
+ {
+ mxTableStyle->getByIndex(even_rows_style) >>= xStyle;
+ }
+ else
+ {
+ mxTableStyle->getByIndex(odd_rows_style) >>= xStyle;
+ }
+ }
+
+ if( !xStyle.is() && rStyle.mbUseColumnBanding )
+ {
+ if( (aPos.mnCol & 1) == 0 )
+ {
+ mxTableStyle->getByIndex(even_columns_style) >>= xStyle;
+ }
+ else
+ {
+ mxTableStyle->getByIndex(odd_columns_style) >>= xStyle;
+ }
+ }
+
+ if( !xStyle.is() )
+ {
+ // use default cell style if non found yet
+ mxTableStyle->getByIndex(body_style) >>= xStyle;
+ }
+
+
+ if( xStyle.is() )
+ {
+ SfxUnoStyleSheet* pStyle = SfxUnoStyleSheet::getUnoStyleSheet(xStyle);
+
+ if( pStyle )
+ {
+ CellRef xCell( getCell( aPos ) );
+ if( xCell.is() && ( xCell->GetStyleSheet() != pStyle ) )
+ {
+ xCell->SetStyleSheet( pStyle, true );
+ }
+ }
+ }
+ }
+ }
+}
+
+
+void SdrTableObjImpl::dispose()
+{
+ disconnectTableStyle();
+ mxTableStyle.clear();
+
+ mpLayouter.reset();
+
+ if( mxTable.is() )
+ {
+ Reference< XModifyListener > xListener( static_cast< css::util::XModifyListener* >(this) );
+ mxTable->removeModifyListener( xListener );
+ mxTable->dispose();
+ mxTable.clear();
+ }
+}
+
+
+void SdrTableObjImpl::DragEdge( bool mbHorizontal, int nEdge, sal_Int32 nOffset )
+{
+ if( !((nEdge >= 0) && mxTable.is()))
+ return;
+
+ try
+ {
+ static const OUStringLiteral sSize( u"Size" );
+ if( mbHorizontal )
+ {
+ if (nEdge <= getRowCount())
+ {
+ sal_Int32 nHeight = mpLayouter->getRowHeight( (!nEdge)?nEdge:(nEdge-1) );
+ if(nEdge==0)
+ nHeight -= nOffset;
+ else
+ nHeight += nOffset;
+ Reference< XIndexAccess > xRows( mxTable->getRows(), UNO_QUERY_THROW );
+ Reference< XPropertySet > xRowSet( xRows->getByIndex( (!nEdge)?nEdge:(nEdge-1) ), UNO_QUERY_THROW );
+ xRowSet->setPropertyValue( sSize, Any( nHeight ) );
+ rowSizeChanged = true;
+ }
+ }
+ else
+ {
+ /*
+ fixes fdo#59889 and resizing of table in edge dragging
+ Total vertical edges in a NxN table is N+1, indexed from 0 to N and total Columns is N, indexed from 0 to N-1
+ In LTR table vertical edge responsible for dragging of column x(x=0 to N-1) is, Edge x+1
+ But in RTL table vertical edge responsible for dragging of column x(x=0 to N-1, but from right to left)is, Edge x
+ In LTR table dragging of edge 0(for RTL table edge N) does nothing.
+ */
+ //Todo: Implement Dragging functionality for leftmost edge of table.
+ if (nEdge <= getColumnCount())
+ {
+ const bool bRTL = mpTableObj != nullptr && (mpTableObj->GetWritingMode() == WritingMode_RL_TB);
+ sal_Int32 nWidth;
+ if(bRTL)
+ {
+ nWidth = mpLayouter->getColumnWidth( nEdge );
+ }
+ else
+ {
+ nWidth = mpLayouter->getColumnWidth( (!nEdge)?nEdge:(nEdge-1) );
+ }
+ Reference< XIndexAccess > xCols( mxTable->getColumns(), UNO_QUERY_THROW );
+ nWidth += nOffset;
+ if(bRTL && nEdge<getColumnCount())
+ {
+ Reference< XPropertySet > xColSet( xCols->getByIndex( nEdge ), UNO_QUERY_THROW );
+ xColSet->setPropertyValue( sSize, Any( nWidth ) );
+ }
+ else if(!bRTL && nEdge>0)
+ {
+ Reference< XPropertySet > xColSet( xCols->getByIndex( nEdge-1 ), UNO_QUERY_THROW );
+ xColSet->setPropertyValue( sSize, Any( nWidth ) );
+ }
+ /* To prevent the table resizing on edge dragging */
+ if( nEdge > 0 && nEdge < mxTable->getColumnCount() )
+ {
+ if( bRTL )
+ nEdge--;
+
+ nWidth = mpLayouter->getColumnWidth(nEdge);
+ nWidth = std::max(static_cast<sal_Int32>(nWidth - nOffset), sal_Int32(0));
+
+ Reference<XPropertySet> xColSet(xCols->getByIndex(nEdge), UNO_QUERY_THROW);
+ xColSet->setPropertyValue(sSize, Any(nWidth));
+ }
+ }
+ }
+ }
+ catch( Exception& )
+ {
+ TOOLS_WARN_EXCEPTION("svx.table", "");
+ }
+}
+
+
+// XModifyListener
+
+
+void SAL_CALL SdrTableObjImpl::modified( const css::lang::EventObject& /*aEvent*/ )
+{
+ update();
+}
+
+void SdrTableObjImpl::update()
+{
+ // source can be the table model itself or the assigned table template
+ TableModelNotifyGuard aGuard( mxTable.get() );
+ if( !mpTableObj )
+ return;
+
+ if( (maEditPos.mnRow >= getRowCount()) || (maEditPos.mnCol >= getColumnCount()) || (getCell( maEditPos ) != mxActiveCell) )
+ {
+ if(maEditPos.mnRow >= getRowCount())
+ maEditPos.mnRow = getRowCount()-1;
+
+ if(maEditPos.mnCol >= getColumnCount())
+ maEditPos.mnCol = getColumnCount()-1;
+
+ mpTableObj->setActiveCell( maEditPos );
+ }
+
+ ApplyCellStyles();
+
+ mpTableObj->maRect = mpTableObj->maLogicRect;
+ LayoutTable( mpTableObj->maRect, false, false );
+
+ mpTableObj->SetBoundAndSnapRectsDirty();
+ mpTableObj->ActionChanged();
+ mpTableObj->BroadcastObjectChange();
+}
+
+
+void SdrTableObjImpl::connectTableStyle()
+{
+ if( mxTableStyle.is() )
+ {
+ Reference< XModifyBroadcaster > xBroadcaster( mxTableStyle, UNO_QUERY );
+ if( xBroadcaster.is() )
+ {
+ Reference< XModifyListener > xListener( static_cast< css::util::XModifyListener* >(this) );
+ xBroadcaster->addModifyListener( xListener );
+ }
+ }
+}
+
+
+void SdrTableObjImpl::disconnectTableStyle()
+{
+ if( mxTableStyle.is() )
+ {
+ Reference< XModifyBroadcaster > xBroadcaster( mxTableStyle, UNO_QUERY );
+ if( xBroadcaster.is() )
+ {
+ Reference< XModifyListener > xListener( static_cast< css::util::XModifyListener* >(this) );
+ xBroadcaster->removeModifyListener( xListener );
+ }
+ }
+}
+
+
+bool SdrTableObjImpl::isInUse()
+{
+ return mpTableObj && mpTableObj->IsInserted();
+}
+
+void SdrTableObjImpl::dumpAsXml(xmlTextWriterPtr pWriter) const
+{
+ (void)xmlTextWriterStartElement(pWriter, BAD_CAST("SdrTableObjImpl"));
+ if (mpLayouter)
+ mpLayouter->dumpAsXml(pWriter);
+ mxTable->dumpAsXml(pWriter);
+ (void)xmlTextWriterEndElement(pWriter);
+}
+
+
+// XEventListener
+
+
+void SAL_CALL SdrTableObjImpl::disposing( const css::lang::EventObject& /*Source*/ )
+{
+ mxActiveCell.clear();
+ mxTable.clear();
+ mpLayouter.reset();
+ mpTableObj = nullptr;
+}
+
+
+CellRef SdrTableObjImpl::getCell( const CellPos& rPos ) const
+{
+ CellRef xCell;
+ if( mxTable.is() ) try
+ {
+ xCell.set( dynamic_cast< Cell* >( mxTable->getCellByPosition( rPos.mnCol, rPos.mnRow ).get() ) );
+ }
+ catch( Exception& )
+ {
+ TOOLS_WARN_EXCEPTION("svx.table", "");
+ }
+ return xCell;
+}
+
+
+sal_Int32 SdrTableObjImpl::getColumnCount() const
+{
+ return mxTable.is() ? mxTable->getColumnCount() : 0;
+}
+
+std::vector<sal_Int32> SdrTableObjImpl::getColumnWidths() const
+{
+ std::vector<sal_Int32> aRet;
+
+ if (mxTable.is())
+ aRet = mxTable->getColumnWidths();
+
+ return aRet;
+}
+
+sal_Int32 SdrTableObjImpl::getRowCount() const
+{
+ return mxTable.is() ? mxTable->getRowCount() : 0;
+}
+
+void SdrTableObjImpl::LayoutTable( tools::Rectangle& rArea, bool bFitWidth, bool bFitHeight )
+{
+ if (utl::ConfigManager::IsFuzzing())
+ return;
+ if(!mpLayouter)
+ return;
+
+ // Optimization: SdrTableObj::SetChanged() can call this very often, repeatedly
+ // with the same settings, noticeably increasing load time. Skip if already done.
+ bool bInteractiveMightGrowBecauseTextChanged =
+ mpTableObj->IsReallyEdited() && (mpTableObj->IsAutoGrowHeight() || mpTableObj->IsAutoGrowWidth());
+ WritingMode writingMode = mpTableObj->GetWritingMode();
+ if( bInteractiveMightGrowBecauseTextChanged
+ || lastLayoutTable != this || lastLayoutInputRectangle != rArea
+ || lastLayoutFitWidth != bFitWidth || lastLayoutFitHeight != bFitHeight
+ || lastLayoutMode != writingMode
+ || lastRowCount != getRowCount()
+ || lastColCount != getColumnCount()
+ || lastColWidths != getColumnWidths()
+ || rowSizeChanged )
+ {
+ lastLayoutTable = this;
+ lastLayoutInputRectangle = rArea;
+ lastLayoutFitWidth = bFitWidth;
+ lastLayoutFitHeight = bFitHeight;
+ lastLayoutMode = writingMode;
+ lastRowCount = getRowCount();
+ lastColCount = getColumnCount();
+ // Column resize, when the total width and column count of the
+ // table is unchanged, but re-layout is still needed.
+ lastColWidths = getColumnWidths();
+ TableModelNotifyGuard aGuard( mxTable.get() );
+ mpLayouter->LayoutTable( rArea, bFitWidth, bFitHeight );
+ lastLayoutResultRectangle = rArea;
+ rowSizeChanged = false;
+ }
+ else
+ {
+ rArea = lastLayoutResultRectangle;
+ mpLayouter->UpdateBorderLayout();
+ }
+}
+
+void SdrTableObjImpl::UpdateCells( tools::Rectangle const & rArea )
+{
+ if( mpLayouter && mxTable.is() )
+ {
+ TableModelNotifyGuard aGuard( mxTable.get() );
+ mpLayouter->updateCells( rArea );
+ mxTable->setModified(true);
+ }
+}
+
+
+// BaseProperties section
+
+
+std::unique_ptr<sdr::properties::BaseProperties> SdrTableObj::CreateObjectSpecificProperties()
+{
+ return std::make_unique<TableProperties>(*this);
+}
+
+
+// DrawContact section
+
+
+std::unique_ptr<sdr::contact::ViewContact> SdrTableObj::CreateObjectSpecificViewContact()
+{
+ return std::make_unique<sdr::contact::ViewContactOfTableObj>(*this);
+}
+
+SdrTableObj::SdrTableObj(SdrModel& rSdrModel)
+: SdrTextObj(rSdrModel)
+{
+ init( 1, 1 );
+}
+
+SdrTableObj::SdrTableObj(SdrModel& rSdrModel, SdrTableObj const & rSource)
+: SdrTextObj(rSdrModel, rSource)
+{
+ init( 1, 1 );
+
+ TableModelNotifyGuard aGuard( mpImpl.is() ? mpImpl->mxTable.get() : nullptr );
+
+ maLogicRect = rSource.maLogicRect;
+ maRect = rSource.maRect;
+ maGeo = rSource.maGeo;
+ meTextKind = rSource.meTextKind;
+ mbTextFrame = rSource.mbTextFrame;
+ maTextSize = rSource.maTextSize;
+ mbTextSizeDirty = rSource.mbTextSizeDirty;
+ mbNoShear = rSource.mbNoShear;
+ mbDisableAutoWidthOnDragging = rSource.mbDisableAutoWidthOnDragging;
+
+ // use SdrTableObjImpl::operator= now to
+ // copy model data and other stuff (see there)
+ *mpImpl = *rSource.mpImpl;
+}
+
+SdrTableObj::SdrTableObj(
+ SdrModel& rSdrModel,
+ const ::tools::Rectangle& rNewRect,
+ sal_Int32 nColumns,
+ sal_Int32 nRows)
+: SdrTextObj(rSdrModel, rNewRect)
+ ,maLogicRect(rNewRect)
+{
+ if( nColumns <= 0 )
+ nColumns = 1;
+
+ if( nRows <= 0 )
+ nRows = 1;
+
+ init( nColumns, nRows );
+}
+
+
+void SdrTableObj::init( sal_Int32 nColumns, sal_Int32 nRows )
+{
+ m_bClosedObj = true;
+
+ mpImpl = new SdrTableObjImpl;
+ mpImpl->init( this, nColumns, nRows );
+
+ // Stuff done from old SetModel:
+ if( !maLogicRect.IsEmpty() )
+ {
+ maRect = maLogicRect;
+ mpImpl->LayoutTable( maRect, false, false );
+ }
+}
+
+
+SdrTableObj::~SdrTableObj()
+{
+ mpImpl->dispose();
+}
+
+
+// table stuff
+
+
+Reference< XTable > SdrTableObj::getTable() const
+{
+ return mpImpl->mxTable;
+}
+
+
+bool SdrTableObj::isValid( const CellPos& rPos ) const
+{
+ return (rPos.mnCol >= 0) && (rPos.mnCol < mpImpl->getColumnCount()) && (rPos.mnRow >= 0) && (rPos.mnRow < mpImpl->getRowCount());
+}
+
+
+CellPos SdrTableObj::getFirstCell()
+{
+ return CellPos( 0,0 );
+}
+
+
+CellPos SdrTableObj::getLastCell() const
+{
+ CellPos aPos;
+ if( mpImpl->mxTable.is() )
+ {
+ aPos.mnCol = mpImpl->getColumnCount()-1;
+ aPos.mnRow = mpImpl->getRowCount()-1;
+ }
+ return aPos;
+}
+
+
+CellPos SdrTableObj::getLeftCell( const CellPos& rPos, bool bEdgeTravel ) const
+{
+ switch( GetWritingMode() )
+ {
+ default:
+ case WritingMode_LR_TB:
+ return getPreviousCell( rPos, bEdgeTravel );
+ case WritingMode_RL_TB:
+ return getNextCell( rPos, bEdgeTravel );
+ case WritingMode_TB_RL:
+ return getPreviousRow( rPos, bEdgeTravel );
+ }
+}
+
+
+CellPos SdrTableObj::getRightCell( const CellPos& rPos, bool bEdgeTravel ) const
+{
+ switch( GetWritingMode() )
+ {
+ default:
+ case WritingMode_LR_TB:
+ return getNextCell( rPos, bEdgeTravel );
+ case WritingMode_RL_TB:
+ return getPreviousCell( rPos, bEdgeTravel );
+ case WritingMode_TB_RL:
+ return getNextRow( rPos, bEdgeTravel );
+ }
+}
+
+
+CellPos SdrTableObj::getUpCell( const CellPos& rPos, bool bEdgeTravel ) const
+{
+ switch( GetWritingMode() )
+ {
+ default:
+ case WritingMode_LR_TB:
+ case WritingMode_RL_TB:
+ return getPreviousRow( rPos, bEdgeTravel );
+ case WritingMode_TB_RL:
+ return getPreviousCell( rPos, bEdgeTravel );
+ }
+}
+
+
+CellPos SdrTableObj::getDownCell( const CellPos& rPos, bool bEdgeTravel ) const
+{
+ switch( GetWritingMode() )
+ {
+ default:
+ case WritingMode_LR_TB:
+ case WritingMode_RL_TB:
+ return getNextRow( rPos, bEdgeTravel );
+ case WritingMode_TB_RL:
+ return getNextCell( rPos, bEdgeTravel );
+ }
+}
+
+
+CellPos SdrTableObj::getPreviousCell( const CellPos& rPos, bool bEdgeTravel ) const
+{
+ CellPos aPos( rPos );
+ if( mpImpl.is() )
+ {
+ CellRef xCell( mpImpl->getCell( aPos ) );
+ if( xCell.is() && xCell->isMerged() )
+ {
+ sal_Int32 nTemp = 0;
+ findMergeOrigin( mpImpl->mxTable, aPos.mnCol, aPos.mnRow, aPos.mnCol, nTemp );
+ }
+
+ if( aPos.mnCol > 0 )
+ {
+ --aPos.mnCol;
+ }
+
+ else if( bEdgeTravel && (aPos.mnRow > 0) )
+ {
+ aPos.mnCol = mpImpl->mxTable->getColumnCount()-1;
+ --aPos.mnRow;
+ }
+ }
+ return aPos;
+}
+
+
+CellPos SdrTableObj::getNextCell( const CellPos& rPos, bool bEdgeTravel ) const
+{
+ CellPos aPos( rPos );
+ if( mpImpl.is() )
+ {
+ CellRef xCell( mpImpl->getCell( aPos ) );
+ if( xCell.is() )
+ {
+ if( xCell->isMerged() )
+ {
+ findMergeOrigin( mpImpl->mxTable, aPos.mnCol, aPos.mnRow, aPos.mnCol, aPos.mnRow );
+
+ xCell = mpImpl->getCell(aPos);
+
+ if( xCell.is() )
+ {
+ aPos.mnCol += xCell->getColumnSpan();
+ aPos.mnRow = rPos.mnRow;
+ }
+ }
+ else
+ {
+ aPos.mnCol += xCell->getColumnSpan();
+ }
+
+ if( aPos.mnCol < mpImpl->mxTable->getColumnCount() )
+ return aPos;
+
+ if( bEdgeTravel && ((aPos.mnRow + 1) < mpImpl->getRowCount()) )
+ {
+ aPos.mnCol = 0;
+ aPos.mnRow += 1;
+ return aPos;
+ }
+ }
+ }
+
+ // last cell reached, no traveling possible
+ return rPos;
+}
+
+
+CellPos SdrTableObj::getPreviousRow( const CellPos& rPos, bool bEdgeTravel ) const
+{
+ CellPos aPos( rPos );
+ if( mpImpl.is() )
+ {
+ CellRef xCell( mpImpl->getCell( aPos ) );
+ if( xCell.is() && xCell->isMerged() )
+ {
+ sal_Int32 nTemp = 0;
+ findMergeOrigin( mpImpl->mxTable, aPos.mnCol, aPos.mnRow, nTemp, aPos.mnRow );
+ }
+
+ if( aPos.mnRow > 0 )
+ {
+ --aPos.mnRow;
+ }
+ else if( bEdgeTravel && (aPos.mnCol > 0) )
+ {
+ aPos.mnRow = mpImpl->mxTable->getRowCount()-1;
+ --aPos.mnCol;
+ }
+ }
+ return aPos;
+}
+
+
+CellPos SdrTableObj::getNextRow( const CellPos& rPos, bool bEdgeTravel ) const
+{
+ CellPos aPos( rPos );
+
+ if( mpImpl.is() )
+ {
+ CellRef xCell( mpImpl->getCell( rPos ) );
+ if( xCell.is() )
+ {
+ if( xCell->isMerged() )
+ {
+ findMergeOrigin( mpImpl->mxTable, aPos.mnCol, aPos.mnRow, aPos.mnCol, aPos.mnRow );
+ xCell = mpImpl->getCell(aPos);
+ aPos.mnCol = rPos.mnCol;
+ }
+
+ if( xCell.is() )
+ aPos.mnRow += xCell->getRowSpan();
+
+ if( aPos.mnRow < mpImpl->mxTable->getRowCount() )
+ return aPos;
+
+ if( bEdgeTravel && (aPos.mnCol + 1) < mpImpl->mxTable->getColumnCount() )
+ {
+ aPos.mnRow = 0;
+ aPos.mnCol += 1;
+
+ while( aPos.mnCol < mpImpl->mxTable->getColumnCount() )
+ {
+ xCell = mpImpl->getCell( aPos );
+ if( xCell.is() && !xCell->isMerged() )
+ return aPos;
+ aPos.mnCol += 1;
+ }
+ }
+ }
+ }
+
+ // last position reached, no more traveling possible
+ return rPos;
+}
+
+
+const TableStyleSettings& SdrTableObj::getTableStyleSettings() const
+{
+ if( mpImpl.is())
+ {
+ return mpImpl->maTableStyle;
+ }
+ else
+ {
+ static TableStyleSettings aTmp;
+ return aTmp;
+ }
+}
+
+
+void SdrTableObj::setTableStyleSettings( const TableStyleSettings& rStyle )
+{
+ if( mpImpl.is() )
+ {
+ mpImpl->maTableStyle = rStyle;
+ mpImpl->update();
+ }
+}
+
+
+TableHitKind SdrTableObj::CheckTableHit( const Point& rPos, sal_Int32& rnX, sal_Int32& rnY, const sal_uInt16 aTol ) const
+{
+ if( !mpImpl.is() || !mpImpl->mxTable.is() )
+ return TableHitKind::NONE;
+
+ rnX = 0;
+ rnY = 0;
+
+ const sal_Int32 nColCount = mpImpl->getColumnCount();
+ const sal_Int32 nRowCount = mpImpl->getRowCount();
+
+ sal_Int32 nX = rPos.X() - maRect.Left();
+ sal_Int32 nY = rPos.Y() - maRect.Top();
+
+ if( (nX < 0) || (nX > maRect.GetWidth()) || (nY < 0) || (nY > maRect.GetHeight() ) )
+ return TableHitKind::NONE;
+
+ // get vertical edge number and check for a hit
+ const bool bRTL = (GetWritingMode() == WritingMode_RL_TB);
+ bool bVrtHit = false;
+ if( !bRTL )
+ {
+ while( rnX <= nColCount )
+ {
+ if( nX - aTol <= 0 )
+ {
+ bVrtHit = true;
+ break;
+ }
+
+ if( rnX == nColCount )
+ break;
+
+ nX -= mpImpl->mpLayouter->getColumnWidth( rnX );
+ if( nX < 0 )
+ break;
+ rnX++;
+ }
+ }
+ else
+ {
+ rnX = nColCount;
+ while( rnX >= 0 )
+ {
+ if( nX - aTol <= 0 )
+ {
+ bVrtHit = true;
+ break;
+ }
+
+ if( rnX == 0 )
+ break;
+
+ rnX--;
+ nX -= mpImpl->mpLayouter->getColumnWidth( rnX );
+ if( nX < 0 )
+ break;
+ }
+ }
+
+ // rnX is now the edge number left to the pointer, if it was hit bHrzHit is also true
+
+ // get vertical edge number and check for a hit
+ bool bHrzHit = false;
+ while( rnY <= nRowCount )
+ {
+ if( nY - aTol <= 0 )
+ {
+ bHrzHit = true;
+ break;
+ }
+
+ if( rnY == nRowCount )
+ break;
+
+ nY -= mpImpl->mpLayouter->getRowHeight(rnY);
+ if( nY < 0 )
+ break;
+ rnY++;
+ }
+
+ // rnY is now the edge number above the pointer, if it was hit bVrtHit is also true
+
+ if( bVrtHit && mpImpl->mpLayouter->isEdgeVisible( rnX, rnY, false ) )
+ return TableHitKind::VerticallBorder;
+
+ if( bHrzHit && mpImpl->mpLayouter->isEdgeVisible( rnX, rnY, true ) )
+ return TableHitKind::HorizontalBorder;
+
+ CellRef xCell( mpImpl->getCell( CellPos( rnX, rnY ) ) );
+ if( xCell.is() && xCell->isMerged() )
+ findMergeOrigin( mpImpl->mxTable, rnX, rnY, rnX, rnY );
+
+ if( xCell.is() )
+ {
+ nX += mpImpl->mpLayouter->getColumnWidth( rnX );
+ //Fix for fdo#62673 : non-editable cell in table on cell merge
+ sal_Int32 i=0;
+ while(xCell.is() && xCell->isMerged())
+ {
+ nX += mpImpl->mpLayouter->getColumnWidth( rnX+i );
+ i++;
+ if(rnX+i < nColCount)
+ xCell=mpImpl->getCell( CellPos( rnX+i, rnY) );
+ else
+ break;
+ }
+
+ if( nX < xCell->GetTextLeftDistance() )
+ return TableHitKind::Cell;
+ }
+
+ return TableHitKind::CellTextArea;
+}
+
+const SfxItemSet& SdrTableObj::GetActiveCellItemSet() const
+{
+ return getActiveCell()->GetItemSet();
+}
+
+void SdrTableObj::setTableStyle( const Reference< XIndexAccess >& xTableStyle )
+{
+ if( mpImpl.is() && (mpImpl->mxTableStyle != xTableStyle) )
+ {
+ mpImpl->disconnectTableStyle();
+ mpImpl->mxTableStyle = xTableStyle;
+ mpImpl->connectTableStyle();
+ mpImpl->update();
+ }
+}
+
+
+const Reference< XIndexAccess >& SdrTableObj::getTableStyle() const
+{
+ if( mpImpl.is() )
+ {
+ return mpImpl->mxTableStyle;
+ }
+ else
+ {
+ static Reference< XIndexAccess > aTmp;
+ return aTmp;
+ }
+}
+
+
+// text stuff
+
+
+/** returns the currently active text. */
+SdrText* SdrTableObj::getActiveText() const
+{
+ return getActiveCell().get();
+}
+
+
+/** returns the nth available text. */
+SdrText* SdrTableObj::getText( sal_Int32 nIndex ) const
+{
+ if( mpImpl->mxTable.is() )
+ {
+ const sal_Int32 nColCount = mpImpl->getColumnCount();
+ if( nColCount )
+ {
+ CellPos aPos( nIndex % nColCount, nIndex / nColCount );
+
+ CellRef xCell( mpImpl->getCell( aPos ) );
+ return xCell.get();
+ }
+ }
+ return nullptr;
+}
+
+
+/** returns the number of texts available for this object. */
+sal_Int32 SdrTableObj::getTextCount() const
+{
+ if( mpImpl->mxTable.is() )
+ {
+ const sal_Int32 nColCount = mpImpl->getColumnCount();
+ const sal_Int32 nRowCount = mpImpl->getRowCount();
+
+ return nColCount * nRowCount;
+ }
+ else
+ {
+ return 0;
+ }
+}
+
+
+/** changes the current active text */
+void SdrTableObj::setActiveText( sal_Int32 nIndex )
+{
+ if( mpImpl.is() && mpImpl->mxTable.is() )
+ {
+ const sal_Int32 nColCount = mpImpl->mxTable->getColumnCount();
+ if( nColCount )
+ {
+ CellPos aPos( nIndex % nColCount, nIndex / nColCount );
+ if( isValid( aPos ) )
+ setActiveCell( aPos );
+ }
+ }
+}
+
+
+/** returns the index of the text that contains the given point or -1 */
+sal_Int32 SdrTableObj::CheckTextHit(const Point& rPnt) const
+{
+ if( mpImpl.is() && mpImpl->mxTable.is() )
+ {
+ CellPos aPos;
+ if( CheckTableHit( rPnt, aPos.mnCol, aPos.mnRow ) == TableHitKind::CellTextArea )
+ return aPos.mnRow * mpImpl->mxTable->getColumnCount() + aPos.mnCol;
+ }
+
+ return 0;
+}
+
+SdrOutliner* SdrTableObj::GetCellTextEditOutliner( const Cell& rCell ) const
+{
+ if( mpImpl.is() && (mpImpl->getCell( mpImpl->maEditPos ).get() == &rCell) )
+ return mpEditingOutliner;
+ else
+ return nullptr;
+}
+
+const TableLayouter& SdrTableObj::getTableLayouter() const
+{
+ assert(mpImpl.is() && mpImpl->mpLayouter && "getTableLayouter() error: no mpImpl or mpLayouter (!)");
+ return *(mpImpl->mpLayouter);
+}
+
+bool SdrTableObj::IsAutoGrowHeight() const
+{
+ return true;
+}
+
+bool SdrTableObj::IsAutoGrowWidth() const
+{
+ return true;
+}
+
+bool SdrTableObj::HasText() const
+{
+ return true;
+}
+
+bool SdrTableObj::IsTextEditActive( const CellPos& rPos )
+{
+ return mpEditingOutliner && mpImpl.is() && (rPos == mpImpl->maEditPos);
+}
+
+
+void SdrTableObj::onEditOutlinerStatusEvent( EditStatus* pEditStatus )
+{
+ if( (pEditStatus->GetStatusWord() & EditStatusFlags::TextHeightChanged) && mpImpl.is() && mpImpl->mpLayouter )
+ {
+ tools::Rectangle aRect0( maRect );
+ maRect = maLogicRect;
+ mpImpl->LayoutTable( maRect, false, false );
+ SetBoundAndSnapRectsDirty();
+ ActionChanged();
+ BroadcastObjectChange();
+ if (aRect0 != maRect)
+ SendUserCall(SdrUserCallType::Resize,aRect0);
+ }
+}
+
+
+void SdrTableObj::TakeObjInfo(SdrObjTransformInfoRec& rInfo) const
+{
+ rInfo.bResizeFreeAllowed=true;
+ rInfo.bResizePropAllowed=true;
+ rInfo.bRotateFreeAllowed=false;
+ rInfo.bRotate90Allowed =false;
+ rInfo.bMirrorFreeAllowed=false;
+ rInfo.bMirror45Allowed =false;
+ rInfo.bMirror90Allowed =false;
+
+ // allow transparence
+ rInfo.bTransparenceAllowed = true;
+
+ rInfo.bShearAllowed =false;
+ rInfo.bEdgeRadiusAllowed=false;
+ rInfo.bCanConvToPath =false;
+ rInfo.bCanConvToPoly =false;
+ rInfo.bCanConvToPathLineToArea=false;
+ rInfo.bCanConvToPolyLineToArea=false;
+ rInfo.bCanConvToContour = false;
+}
+
+SdrObjKind SdrTableObj::GetObjIdentifier() const
+{
+ return SdrObjKind::Table;
+}
+
+void SdrTableObj::TakeTextRect( SdrOutliner& rOutliner, tools::Rectangle& rTextRect, bool bNoEditText, tools::Rectangle* pAnchorRect, bool /*bLineWidth*/ ) const
+{
+ if( mpImpl.is() )
+ TakeTextRect( mpImpl->maEditPos, rOutliner, rTextRect, bNoEditText, pAnchorRect );
+}
+
+
+void SdrTableObj::TakeTextRect( const CellPos& rPos, SdrOutliner& rOutliner, tools::Rectangle& rTextRect, bool bNoEditText, tools::Rectangle* pAnchorRect ) const
+{
+ if( !mpImpl.is())
+ return;
+
+ CellRef xCell( mpImpl->getCell( rPos ) );
+ if( !xCell.is() )
+ return;
+
+ tools::Rectangle aAnkRect;
+ TakeTextAnchorRect( rPos, aAnkRect );
+
+ SdrTextVertAdjust eVAdj=xCell->GetTextVerticalAdjust();
+
+ EEControlBits nStat0=rOutliner.GetControlWord();
+ nStat0 |= EEControlBits::AUTOPAGESIZE;
+ rOutliner.SetControlWord(nStat0);
+ rOutliner.SetMinAutoPaperSize(Size());
+ rOutliner.SetMaxAutoPaperSize(aAnkRect.GetSize());
+ rOutliner.SetPaperSize(aAnkRect.GetSize());
+
+ // #103516# New try with _BLOCK for hor and ver after completely
+ // supporting full width for vertical text.
+// if( SDRTEXTHORZADJUST_BLOCK == eHAdj && !IsVerticalWriting())
+// {
+ rOutliner.SetMinAutoPaperSize(Size(aAnkRect.GetWidth(), 0));
+// }
+// else if(SDRTEXTVERTADJUST_BLOCK == eVAdj && IsVerticalWriting())
+// {
+// rOutliner.SetMinAutoPaperSize(Size(0, aAnkRect.GetHeight()));
+// }
+
+
+ // set text at outliner, maybe from edit outliner
+ std::optional<OutlinerParaObject> pPara;
+ if (xCell->GetOutlinerParaObject())
+ pPara = *xCell->GetOutlinerParaObject();
+ if (mpEditingOutliner && !bNoEditText && mpImpl->mxActiveCell == xCell )
+ pPara = mpEditingOutliner->CreateParaObject();
+
+ if (pPara)
+ {
+ const bool bHitTest(&getSdrModelFromSdrObject().GetHitTestOutliner() == &rOutliner);
+ const SdrTextObj* pTestObj(rOutliner.GetTextObj());
+
+ if( !pTestObj || !bHitTest || (pTestObj != this) || (pTestObj->GetOutlinerParaObject() != xCell->GetOutlinerParaObject()) )
+ {
+ if( bHitTest ) // #i33696# take back fix #i27510#
+ rOutliner.SetTextObj( this );
+
+ rOutliner.SetUpdateLayout(true);
+ rOutliner.SetText(*pPara);
+ }
+ }
+ else
+ {
+ rOutliner.SetTextObj( nullptr );
+ }
+
+ rOutliner.SetUpdateLayout(true);
+ rOutliner.SetControlWord(nStat0);
+
+ Point aTextPos(aAnkRect.TopLeft());
+ Size aTextSiz(rOutliner.GetPaperSize());
+ if (eVAdj==SDRTEXTVERTADJUST_CENTER || eVAdj==SDRTEXTVERTADJUST_BOTTOM)
+ {
+ tools::Long nFreeHgt=aAnkRect.GetHeight()-aTextSiz.Height();
+ if (eVAdj==SDRTEXTVERTADJUST_CENTER)
+ aTextPos.AdjustY(nFreeHgt/2 );
+ if (eVAdj==SDRTEXTVERTADJUST_BOTTOM)
+ aTextPos.AdjustY(nFreeHgt );
+ }
+
+ if (pAnchorRect)
+ *pAnchorRect=aAnkRect;
+
+ rTextRect=tools::Rectangle(aTextPos,aTextSiz);
+}
+
+
+const CellRef& SdrTableObj::getActiveCell() const
+{
+ if( mpImpl.is() )
+ {
+ if( !mpImpl->mxActiveCell.is() )
+ {
+ CellPos aPos;
+ const_cast< SdrTableObj* >(this)->setActiveCell( aPos );
+ }
+ return mpImpl->mxActiveCell;
+ }
+ else
+ {
+ static CellRef xCell;
+ return xCell;
+ }
+}
+
+
+sal_Int32 SdrTableObj::getColumnCount() const
+{
+ return mpImpl.is() ? mpImpl->getColumnCount() : 0;
+}
+
+sal_Int32 SdrTableObj::getRowCount() const
+{
+ return mpImpl.is() ? mpImpl->getRowCount() : 0;
+}
+
+void SdrTableObj::changeEdge(bool bHorizontal, int nEdge, sal_Int32 nOffset)
+{
+ if (mpImpl.is())
+ mpImpl->DragEdge(bHorizontal, nEdge, nOffset);
+}
+
+void SdrTableObj::setActiveCell( const CellPos& rPos )
+{
+ if( !(mpImpl.is() && mpImpl->mxTable.is()) )
+ return;
+
+ try
+ {
+ mpImpl->mxActiveCell.set( dynamic_cast< Cell* >( mpImpl->mxTable->getCellByPosition( rPos.mnCol, rPos.mnRow ).get() ) );
+ if( mpImpl->mxActiveCell.is() && mpImpl->mxActiveCell->isMerged() )
+ {
+ CellPos aOrigin;
+ findMergeOrigin( mpImpl->mxTable, rPos.mnCol, rPos.mnRow, aOrigin.mnCol, aOrigin.mnRow );
+ mpImpl->mxActiveCell.set( dynamic_cast< Cell* >( mpImpl->mxTable->getCellByPosition( aOrigin.mnCol, aOrigin.mnRow ).get() ) );
+ mpImpl->maEditPos = aOrigin;
+ }
+ else
+ {
+ mpImpl->maEditPos = rPos;
+ }
+ }
+ catch( Exception& )
+ {
+ TOOLS_WARN_EXCEPTION("svx.table", "");
+ }
+}
+
+
+void SdrTableObj::getActiveCellPos( CellPos& rPos ) const
+{
+ rPos = mpImpl->maEditPos;
+}
+
+
+void SdrTableObj::getCellBounds( const CellPos& rPos, ::tools::Rectangle& rCellRect )
+{
+ if( mpImpl.is() )
+ {
+ CellRef xCell( mpImpl->getCell( rPos ) );
+ if( xCell.is() )
+ rCellRect = xCell->getCellRect();
+ }
+}
+
+
+void SdrTableObj::TakeTextAnchorRect(tools::Rectangle& rAnchorRect) const
+{
+ if( mpImpl.is() )
+ TakeTextAnchorRect( mpImpl->maEditPos, rAnchorRect );
+}
+
+
+void SdrTableObj::TakeTextAnchorRect( const CellPos& rPos, tools::Rectangle& rAnchorRect ) const
+{
+ tools::Rectangle aAnkRect(maRect);
+
+ if( mpImpl.is() )
+ {
+ CellRef xCell( mpImpl->getCell( rPos ) );
+ if( xCell.is() )
+ xCell->TakeTextAnchorRect( aAnkRect );
+ }
+
+ ImpJustifyRect(aAnkRect);
+ rAnchorRect=aAnkRect;
+}
+
+
+void SdrTableObj::TakeTextEditArea(Size* pPaperMin, Size* pPaperMax, tools::Rectangle* pViewInit, tools::Rectangle* pViewMin) const
+{
+ if( mpImpl.is() )
+ TakeTextEditArea( mpImpl->maEditPos, pPaperMin, pPaperMax, pViewInit, pViewMin );
+}
+
+
+void SdrTableObj::TakeTextEditArea( const CellPos& rPos, Size* pPaperMin, Size* pPaperMax, tools::Rectangle* pViewInit, tools::Rectangle* pViewMin ) const
+{
+ Size aPaperMin,aPaperMax;
+ tools::Rectangle aViewInit;
+ TakeTextAnchorRect( rPos, aViewInit );
+
+ Size aAnkSiz(aViewInit.GetSize());
+ aAnkSiz.AdjustWidth( -1 ); aAnkSiz.AdjustHeight( -1 ); // because GetSize() increments by one
+
+ Size aMaxSiz(aAnkSiz.Width(),1000000);
+ Size aTmpSiz(getSdrModelFromSdrObject().GetMaxObjSize());
+ if (aTmpSiz.Height()!=0)
+ aMaxSiz.setHeight(aTmpSiz.Height() );
+
+ CellRef xCell( mpImpl->getCell( rPos ) );
+ SdrTextVertAdjust eVAdj = xCell.is() ? xCell->GetTextVerticalAdjust() : SDRTEXTVERTADJUST_TOP;
+
+ aPaperMax=aMaxSiz;
+
+ aPaperMin.setWidth( aAnkSiz.Width() );
+
+ if (pViewMin!=nullptr)
+ {
+ *pViewMin=aViewInit;
+ tools::Long nYFree=aAnkSiz.Height()-aPaperMin.Height();
+
+ if (eVAdj==SDRTEXTVERTADJUST_TOP)
+ {
+ pViewMin->AdjustBottom( -nYFree );
+ }
+ else if (eVAdj==SDRTEXTVERTADJUST_BOTTOM)
+ {
+ pViewMin->AdjustTop(nYFree );
+ }
+ else
+ {
+ pViewMin->AdjustTop(nYFree/2 );
+ pViewMin->SetBottom(pViewMin->Top()+aPaperMin.Height() );
+ }
+ }
+
+
+ if(IsVerticalWriting())
+ aPaperMin.setWidth( 0 );
+ else
+ aPaperMin.setHeight( 0 );
+
+ if (pPaperMin!=nullptr) *pPaperMin=aPaperMin;
+ if (pPaperMax!=nullptr) *pPaperMax=aPaperMax;
+ if (pViewInit!=nullptr) *pViewInit=aViewInit;
+}
+
+
+EEAnchorMode SdrTableObj::GetOutlinerViewAnchorMode() const
+{
+ EEAnchorMode eRet=EEAnchorMode::TopLeft;
+ CellRef xCell( getActiveCell() );
+ if( xCell.is() )
+ {
+ SdrTextVertAdjust eV=xCell->GetTextVerticalAdjust();
+
+ {
+ if (eV==SDRTEXTVERTADJUST_TOP)
+ {
+ eRet=EEAnchorMode::TopLeft;
+ }
+ else if (eV==SDRTEXTVERTADJUST_BOTTOM)
+ {
+ eRet=EEAnchorMode::BottomLeft;
+ }
+ else
+ {
+ eRet=EEAnchorMode::VCenterLeft;
+ }
+ }
+ }
+ return eRet;
+}
+
+
+OUString SdrTableObj::TakeObjNameSingul() const
+{
+ OUString sName(SvxResId(STR_ObjNameSingulTable));
+
+ OUString aName(GetName());
+ if (!aName.isEmpty())
+ sName += " '" + aName + "'";
+
+ return sName;
+}
+
+
+OUString SdrTableObj::TakeObjNamePlural() const
+{
+ return SvxResId(STR_ObjNamePluralTable);
+}
+
+
+SdrTableObj* SdrTableObj::CloneSdrObject(SdrModel& rTargetModel) const
+{
+ return new SdrTableObj(rTargetModel, *this);
+}
+
+
+const tools::Rectangle& SdrTableObj::GetSnapRect() const
+{
+ return maRect;
+}
+
+
+void SdrTableObj::NbcSetSnapRect(const tools::Rectangle& rRect)
+{
+ NbcSetLogicRect( rRect );
+}
+
+
+const tools::Rectangle& SdrTableObj::GetLogicRect() const
+{
+ return maLogicRect;
+}
+
+
+void SdrTableObj::RecalcSnapRect()
+{
+}
+
+
+bool SdrTableObj::BegTextEdit(SdrOutliner& rOutl)
+{
+ if( mpEditingOutliner != nullptr )
+ return false;
+
+ mpEditingOutliner=&rOutl;
+
+ mbInEditMode = true;
+
+ rOutl.Init( OutlinerMode::TextObject );
+ rOutl.SetRefDevice(getSdrModelFromSdrObject().GetRefDevice());
+
+ bool bUpdateMode = rOutl.SetUpdateLayout(false);
+ Size aPaperMin;
+ Size aPaperMax;
+ tools::Rectangle aEditArea;
+ TakeTextEditArea(&aPaperMin,&aPaperMax,&aEditArea,nullptr);
+
+ rOutl.SetMinAutoPaperSize(aPaperMin);
+ rOutl.SetMaxAutoPaperSize(aPaperMax);
+ rOutl.SetPaperSize(aPaperMax);
+
+ if (bUpdateMode) rOutl.SetUpdateLayout(true);
+
+ EEControlBits nStat=rOutl.GetControlWord();
+ nStat |= EEControlBits::AUTOPAGESIZE;
+ nStat &=~EEControlBits::STRETCHING;
+ rOutl.SetControlWord(nStat);
+
+ OutlinerParaObject* pPara = GetOutlinerParaObject();
+ if(pPara)
+ rOutl.SetText(*pPara);
+
+ rOutl.UpdateFields();
+ rOutl.ClearModifyFlag();
+
+ return true;
+}
+
+
+void SdrTableObj::EndTextEdit(SdrOutliner& rOutl)
+{
+
+ if (getSdrModelFromSdrObject().IsUndoEnabled() && !mpImpl->maUndos.empty())
+ {
+ // These actions should be on the undo stack after text edit.
+ for (std::unique_ptr<SdrUndoAction>& pAction : mpImpl->maUndos)
+ getSdrModelFromSdrObject().AddUndo( std::move(pAction));
+ mpImpl->maUndos.clear();
+
+ getSdrModelFromSdrObject().AddUndo(getSdrModelFromSdrObject().GetSdrUndoFactory().CreateUndoGeoObject(*this));
+ }
+
+ if(rOutl.IsModified())
+ {
+ std::optional<OutlinerParaObject> pNewText;
+ Paragraph* p1stPara = rOutl.GetParagraph( 0 );
+ sal_Int32 nParaCnt = rOutl.GetParagraphCount();
+
+ if(p1stPara)
+ {
+ // to remove the grey field background
+ rOutl.UpdateFields();
+
+ // create new text object
+ pNewText = rOutl.CreateParaObject( 0, nParaCnt );
+ }
+ SetOutlinerParaObject(std::move(pNewText));
+ }
+
+ mpEditingOutliner = nullptr;
+ rOutl.Clear();
+ EEControlBits nStat = rOutl.GetControlWord();
+ nStat &= ~EEControlBits::AUTOPAGESIZE;
+ rOutl.SetControlWord(nStat);
+
+ mbInEditMode = false;
+}
+
+
+OutlinerParaObject* SdrTableObj::GetOutlinerParaObject() const
+{
+ CellRef xCell( getActiveCell() );
+ if( xCell.is() )
+ return xCell->GetOutlinerParaObject();
+ else
+ return nullptr;
+}
+
+
+void SdrTableObj::NbcSetOutlinerParaObject( std::optional<OutlinerParaObject> pTextObject)
+{
+ CellRef xCell( getActiveCell() );
+ if( !xCell.is() )
+ return;
+
+ // Update HitTestOutliner
+ const SdrTextObj* pTestObj(getSdrModelFromSdrObject().GetHitTestOutliner().GetTextObj());
+
+ if(pTestObj && pTestObj->GetOutlinerParaObject() == xCell->GetOutlinerParaObject())
+ {
+ getSdrModelFromSdrObject().GetHitTestOutliner().SetTextObj(nullptr);
+ }
+
+ xCell->SetOutlinerParaObject( std::move(pTextObject) );
+ SetTextSizeDirty();
+ NbcAdjustTextFrameWidthAndHeight();
+}
+
+
+void SdrTableObj::NbcSetLogicRect(const tools::Rectangle& rRect)
+{
+ maLogicRect=rRect;
+ ImpJustifyRect(maLogicRect);
+ const bool bWidth = maLogicRect.getWidth() != maRect.getWidth();
+ const bool bHeight = maLogicRect.getHeight() != maRect.getHeight();
+ maRect = maLogicRect;
+ if (mpImpl->mbSkipChangeLayout)
+ // Avoid distributing newly available space between existing cells.
+ NbcAdjustTextFrameWidthAndHeight();
+ else
+ NbcAdjustTextFrameWidthAndHeight(!bHeight, !bWidth);
+ SetBoundAndSnapRectsDirty();
+}
+
+
+void SdrTableObj::AdjustToMaxRect( const tools::Rectangle& rMaxRect, bool /* bShrinkOnly = false */ )
+{
+ tools::Rectangle aAdjustRect( rMaxRect );
+ aAdjustRect.setHeight( GetLogicRect().getHeight() );
+ SetLogicRect( aAdjustRect );
+}
+
+
+void SdrTableObj::NbcMove(const Size& rSiz)
+{
+ maLogicRect.Move(rSiz);
+ SdrTextObj::NbcMove( rSiz );
+ if( mpImpl.is() )
+ mpImpl->UpdateCells( maRect );
+}
+
+
+void SdrTableObj::NbcResize(const Point& rRef, const Fraction& xFact, const Fraction& yFact)
+{
+ tools::Rectangle aOldRect( maLogicRect );
+ ResizeRect(maLogicRect,rRef,xFact,yFact);
+
+ maRect = maLogicRect;
+ NbcAdjustTextFrameWidthAndHeight( maLogicRect.GetHeight() == aOldRect.GetHeight(), maLogicRect.GetWidth() == aOldRect.GetWidth() );
+ SetBoundAndSnapRectsDirty();
+}
+
+
+bool SdrTableObj::AdjustTextFrameWidthAndHeight()
+{
+ tools::Rectangle aNewRect(maLogicRect);
+ bool bRet=AdjustTextFrameWidthAndHeight(aNewRect);
+ if (bRet)
+ {
+ tools::Rectangle aBoundRect0;
+ if (m_pUserCall!=nullptr)
+ aBoundRect0=GetLastBoundRect();
+ maRect = aNewRect;
+ SetBoundAndSnapRectsDirty();
+ SetChanged();
+ BroadcastObjectChange();
+ SendUserCall(SdrUserCallType::Resize,aBoundRect0);
+ }
+ return bRet;
+}
+
+
+bool SdrTableObj::AdjustTextFrameWidthAndHeight(tools::Rectangle& rR, bool bHeight, bool bWidth) const
+{
+ if(rR.IsEmpty() || !mpImpl.is() || !mpImpl->mxTable.is())
+ return false;
+
+ tools::Rectangle aRectangle( rR );
+ mpImpl->LayoutTable( aRectangle, !bWidth, !bHeight );
+
+ if( aRectangle != rR )
+ {
+ rR = aRectangle;
+ return true;
+ }
+ else
+ {
+ return false;
+ }
+}
+
+
+void SdrTableObj::NbcReformatText()
+{
+ NbcAdjustTextFrameWidthAndHeight();
+}
+
+
+bool SdrTableObj::IsVerticalWriting() const
+{
+ const SvxWritingModeItem& rModeItem = GetObjectItem( SDRATTR_TEXTDIRECTION );
+ return rModeItem.GetValue() == css::text::WritingMode_TB_RL;
+}
+
+
+void SdrTableObj::SetVerticalWriting(bool bVertical)
+{
+ if(bVertical != IsVerticalWriting() )
+ {
+ SvxWritingModeItem aModeItem( css::text::WritingMode_LR_TB, SDRATTR_TEXTDIRECTION );
+ SetObjectItem( aModeItem );
+ }
+}
+
+
+WritingMode SdrTableObj::GetWritingMode() const
+{
+ SfxStyleSheet* pStyle = GetStyleSheet();
+ if ( !pStyle )
+ return WritingMode_LR_TB;
+
+ WritingMode eWritingMode = WritingMode_LR_TB;
+ const SfxItemSet &rSet = pStyle->GetItemSet();
+
+ if ( const SvxWritingModeItem *pItem = rSet.GetItemIfSet( SDRATTR_TEXTDIRECTION ))
+ eWritingMode = pItem->GetValue();
+
+ if ( const SvxFrameDirectionItem *pItem;
+ ( eWritingMode != WritingMode_TB_RL ) &&
+ ( pItem = rSet.GetItemIfSet( EE_PARA_WRITINGDIR, false ) ) )
+ {
+ if ( pItem->GetValue() == SvxFrameDirection::Horizontal_LR_TB )
+ eWritingMode = WritingMode_LR_TB;
+ else
+ eWritingMode = WritingMode_RL_TB;
+ }
+
+ return eWritingMode;
+}
+
+void SdrTableObj::AddUndo(SdrUndoAction* pUndo)
+{
+ mpImpl->maUndos.push_back(std::unique_ptr<SdrUndoAction>(pUndo));
+}
+
+void SdrTableObj::SetSkipChangeLayout(bool bSkipChangeLayout)
+{
+ mpImpl->mbSkipChangeLayout = bSkipChangeLayout;
+}
+
+bool SdrTableObj::IsReallyEdited() const
+{
+ return mpEditingOutliner && mpEditingOutliner->IsModified();
+}
+
+bool SdrTableObj::IsFontwork() const
+{
+ return false;
+}
+
+sal_uInt32 SdrTableObj::GetHdlCount() const
+{
+ sal_uInt32 nCount = SdrTextObj::GetHdlCount();
+ const sal_Int32 nRowCount = mpImpl->getRowCount();
+ const sal_Int32 nColCount = mpImpl->getColumnCount();
+
+ if( nRowCount && nColCount )
+ nCount += nRowCount + nColCount + 2 + 1;
+
+ return nCount;
+}
+
+void SdrTableObj::AddToHdlList(SdrHdlList& rHdlList) const
+{
+ const sal_Int32 nRowCount = mpImpl->getRowCount();
+ const sal_Int32 nColCount = mpImpl->getColumnCount();
+
+ // first add row handles
+ std::vector<TableEdgeHdl*> aRowEdges(nRowCount + 1);
+ for (auto const & rEdge : mpImpl->mpLayouter->getHorizontalEdges())
+ {
+ Point aPoint(maRect.TopLeft());
+ aPoint.AdjustY(rEdge.nPosition);
+
+ std::unique_ptr<TableEdgeHdl> pHdl(new TableEdgeHdl(aPoint, true, rEdge.nMin, rEdge.nMax, nColCount + 1));
+ pHdl->SetPointNum(rEdge.nIndex);
+ aRowEdges[rEdge.nIndex] = pHdl.get();
+ rHdlList.AddHdl(std::move(pHdl));
+ }
+
+ // second add column handles
+ std::vector<TableEdgeHdl*> aColEdges(nColCount + 1);
+ for (auto const & rEdge : mpImpl->mpLayouter->getVerticalEdges())
+ {
+ Point aPoint(maRect.TopLeft());
+ aPoint.AdjustX(rEdge.nPosition);
+
+ std::unique_ptr<TableEdgeHdl> pHdl(new TableEdgeHdl(aPoint, false, rEdge.nMin, rEdge.nMax, nRowCount + 1));
+ pHdl->SetPointNum(rEdge.nIndex);
+ aColEdges[rEdge.nIndex] = pHdl.get();
+ rHdlList.AddHdl(std::move(pHdl));
+ }
+
+ // now add visible edges to row and column handles
+ if( mpImpl->mpLayouter )
+ {
+ TableLayouter& rLayouter = *mpImpl->mpLayouter;
+
+ sal_Int32 nY = 0;
+
+ for( sal_Int32 nRow = 0; nRow <= nRowCount; ++nRow )
+ {
+ const sal_Int32 nRowHeight = (nRow == nRowCount) ? 0 : rLayouter.getRowHeight(nRow);
+ sal_Int32 nX = 0;
+
+ for( sal_Int32 nCol = 0; nCol <= nColCount; ++nCol )
+ {
+ const sal_Int32 nColWidth = (nCol == nColCount) ? 0 : rLayouter.getColumnWidth(nCol);
+
+ if( nRowHeight > 0 )
+ {
+ if( rLayouter.isEdgeVisible( nCol, nRow, false ) )
+ aColEdges[nCol]->SetEdge( nRow, nY, nY + nRowHeight, (rLayouter.getBorderLine( nCol, nRow, false ) == nullptr) ? Visible : Invisible);
+ }
+
+ if( nColWidth > 0 )
+ {
+ if( rLayouter.isEdgeVisible( nCol, nRow, true ) )
+ aRowEdges[nRow]->SetEdge( nCol, nX, nX + nColWidth, (rLayouter.getBorderLine( nCol, nRow, true ) == nullptr) ? Visible : Invisible);
+ }
+
+ nX += nColWidth;
+ }
+
+ nY += nRowHeight;
+ }
+ }
+
+ // add remaining handles
+ SdrHdlList tempList(nullptr);
+ tempList.AddHdl( std::make_unique<TableBorderHdl>( maRect, !IsTextEditActive() ) );
+ tempList.AddHdl( std::make_unique<SdrHdl>(maRect.TopLeft(),SdrHdlKind::UpperLeft) );
+ tempList.AddHdl( std::make_unique<SdrHdl>(maRect.TopCenter(),SdrHdlKind::Upper) );
+ tempList.AddHdl( std::make_unique<SdrHdl>(maRect.TopRight(),SdrHdlKind::UpperRight) );
+ tempList.AddHdl( std::make_unique<SdrHdl>(maRect.LeftCenter(),SdrHdlKind::Left) );
+ tempList.AddHdl( std::make_unique<SdrHdl>(maRect.RightCenter(),SdrHdlKind::Right) );
+ tempList.AddHdl( std::make_unique<SdrHdl>(maRect.BottomLeft(),SdrHdlKind::LowerLeft) );
+ tempList.AddHdl( std::make_unique<SdrHdl>(maRect.BottomCenter(),SdrHdlKind::Lower) );
+ tempList.AddHdl( std::make_unique<SdrHdl>(maRect.BottomRight(),SdrHdlKind::LowerRight) );
+ for( size_t nHdl = 0; nHdl < tempList.GetHdlCount(); ++nHdl )
+ tempList.GetHdl(nHdl)->SetMoveOutside(true);
+ tempList.MoveTo(rHdlList);
+
+ const size_t nHdlCount = rHdlList.GetHdlCount();
+ for( size_t nHdl = 0; nHdl < nHdlCount; ++nHdl )
+ rHdlList.GetHdl(nHdl)->SetObj(const_cast<SdrTableObj*>(this));
+}
+
+// Dragging
+
+bool SdrTableObj::hasSpecialDrag() const
+{
+ return true;
+}
+
+bool SdrTableObj::beginSpecialDrag(SdrDragStat& rDrag) const
+{
+ const SdrHdl* pHdl = rDrag.GetHdl();
+ const SdrHdlKind eHdl((pHdl == nullptr) ? SdrHdlKind::Move : pHdl->GetKind());
+
+ switch( eHdl )
+ {
+ case SdrHdlKind::UpperLeft:
+ case SdrHdlKind::Upper:
+ case SdrHdlKind::UpperRight:
+ case SdrHdlKind::Left:
+ case SdrHdlKind::Right:
+ case SdrHdlKind::LowerLeft:
+ case SdrHdlKind::Lower:
+ case SdrHdlKind::LowerRight:
+ case SdrHdlKind::Move:
+ {
+ break;
+ }
+
+ case SdrHdlKind::User:
+ {
+ rDrag.SetEndDragChangesAttributes(false);
+ rDrag.SetNoSnap();
+ break;
+ }
+
+ default:
+ {
+ return false;
+ }
+ }
+
+ return true;
+}
+
+bool SdrTableObj::applySpecialDrag(SdrDragStat& rDrag)
+{
+ bool bRet(true);
+ const SdrHdl* pHdl = rDrag.GetHdl();
+ const SdrHdlKind eHdl((pHdl == nullptr) ? SdrHdlKind::Move : pHdl->GetKind());
+
+ switch( eHdl )
+ {
+ case SdrHdlKind::UpperLeft:
+ case SdrHdlKind::Upper:
+ case SdrHdlKind::UpperRight:
+ case SdrHdlKind::Left:
+ case SdrHdlKind::Right:
+ case SdrHdlKind::LowerLeft:
+ case SdrHdlKind::Lower:
+ case SdrHdlKind::LowerRight:
+ {
+ const tools::Rectangle aNewRectangle(ImpDragCalcRect(rDrag));
+
+ if (aNewRectangle != maRect)
+ {
+ NbcSetLogicRect(aNewRectangle);
+ }
+
+ break;
+ }
+
+ case SdrHdlKind::Move:
+ {
+ NbcMove( Size( rDrag.GetDX(), rDrag.GetDY() ) );
+ break;
+ }
+
+ case SdrHdlKind::User:
+ {
+ rDrag.SetEndDragChangesAttributes(false);
+ rDrag.SetNoSnap();
+ const TableEdgeHdl* pEdgeHdl = dynamic_cast< const TableEdgeHdl* >( pHdl );
+
+ if( pEdgeHdl )
+ {
+ if( IsInserted() )
+ {
+ rDrag.SetEndDragChangesAttributes(true);
+ rDrag.SetEndDragChangesLayout(true);
+ }
+
+ mpImpl->DragEdge( pEdgeHdl->IsHorizontalEdge(), pEdgeHdl->GetPointNum(), pEdgeHdl->GetValidDragOffset( rDrag ) );
+ }
+ break;
+ }
+
+ default:
+ {
+ bRet = false;
+ }
+ }
+
+ return bRet;
+}
+
+basegfx::B2DPolyPolygon SdrTableObj::getSpecialDragPoly(const SdrDragStat& rDrag) const
+{
+ basegfx::B2DPolyPolygon aRetval;
+ const SdrHdl* pHdl = rDrag.GetHdl();
+
+ if( pHdl && (SdrHdlKind::User == pHdl->GetKind()) )
+ {
+ const TableEdgeHdl* pEdgeHdl = dynamic_cast< const TableEdgeHdl* >( pHdl );
+
+ if( pEdgeHdl )
+ {
+ aRetval = pEdgeHdl->getSpecialDragPoly( rDrag );
+ }
+ }
+
+ return aRetval;
+}
+
+
+// Create
+
+
+bool SdrTableObj::BegCreate(SdrDragStat& rStat)
+{
+ rStat.SetOrtho4Possible();
+ tools::Rectangle aRect1(rStat.GetStart(), rStat.GetNow());
+ aRect1.Justify();
+ rStat.SetActionRect(aRect1);
+ maRect = aRect1;
+ return true;
+}
+
+
+bool SdrTableObj::MovCreate(SdrDragStat& rStat)
+{
+ tools::Rectangle aRect1;
+ rStat.TakeCreateRect(aRect1);
+ ImpJustifyRect(aRect1);
+ rStat.SetActionRect(aRect1);
+ maRect = aRect1; // for ObjName
+ SetBoundRectDirty();
+ m_bSnapRectDirty=true;
+ return true;
+}
+
+
+bool SdrTableObj::EndCreate(SdrDragStat& rStat, SdrCreateCmd eCmd)
+{
+ rStat.TakeCreateRect(maRect);
+ ImpJustifyRect(maRect);
+ return (eCmd==SdrCreateCmd::ForceEnd || rStat.GetPointCount()>=2);
+}
+
+void SdrTableObj::BrkCreate(SdrDragStat& /*rStat*/)
+{
+}
+
+
+bool SdrTableObj::BckCreate(SdrDragStat& /*rStat*/)
+{
+ return true;
+}
+
+
+basegfx::B2DPolyPolygon SdrTableObj::TakeCreatePoly(const SdrDragStat& rDrag) const
+{
+ tools::Rectangle aRect1;
+ rDrag.TakeCreateRect(aRect1);
+ aRect1.Justify();
+
+ basegfx::B2DPolyPolygon aRetval;
+ const basegfx::B2DRange aRange = vcl::unotools::b2DRectangleFromRectangle(aRect1);
+ aRetval.append(basegfx::utils::createPolygonFromRect(aRange));
+ return aRetval;
+}
+
+
+PointerStyle SdrTableObj::GetCreatePointer() const
+{
+ return PointerStyle::Cross;
+}
+
+
+void SdrTableObj::createCell( CellRef& xNewCell )
+{
+ xNewCell = Cell::create( *this );
+}
+
+
+std::unique_ptr<SdrObjGeoData> SdrTableObj::NewGeoData() const
+{
+ return std::make_unique<TableObjectGeoData>();
+}
+
+
+void SdrTableObj::SaveGeoData(SdrObjGeoData& rGeo) const
+{
+ DBG_ASSERT( dynamic_cast< TableObjectGeoData* >( &rGeo ), "svx::SdrTableObj::SaveGeoData(), illegal geo data!" );
+ SdrTextObj::SaveGeoData (rGeo);
+
+ static_cast<TableObjectGeoData &>(rGeo).maLogicRect = maLogicRect;
+}
+
+
+void SdrTableObj::RestoreGeoData(const SdrObjGeoData& rGeo)
+{
+ DBG_ASSERT( dynamic_cast< const TableObjectGeoData* >( &rGeo ), "svx::SdrTableObj::SaveGeoData(), illegal geo data!" );
+
+ maLogicRect = static_cast<const TableObjectGeoData &>(rGeo).maLogicRect;
+
+ SdrTextObj::RestoreGeoData (rGeo);
+
+ if( mpImpl.is() )
+ mpImpl->LayoutTable(maRect, false, false);
+ ActionChanged();
+}
+
+void SdrTableObj::CropTableModelToSelection(const CellPos& rStart, const CellPos& rEnd)
+{
+ if(!mpImpl.is())
+ {
+ return;
+ }
+
+ mpImpl->CropTableModelToSelection(rStart, rEnd);
+}
+
+sal_Int32 SdrTableObj::getHeightWithoutFitting()
+{
+ tools::Rectangle aRect{};
+ if( mpImpl.is() && mpImpl->mpLayouter)
+ {
+ mpImpl->mpLayouter->LayoutTableHeight(aRect, /*bFit=*/false);
+ return aRect.GetHeight();
+ }
+ else
+ return 0;
+}
+
+void SdrTableObj::DistributeColumns( sal_Int32 nFirstColumn, sal_Int32 nLastColumn, const bool bOptimize, const bool bMinimize )
+{
+ if( mpImpl.is() && mpImpl->mpLayouter )
+ {
+ TableModelNotifyGuard aGuard( mpImpl->mxTable.get() );
+ mpImpl->mpLayouter->DistributeColumns( maRect, nFirstColumn, nLastColumn, bOptimize, bMinimize );
+ }
+}
+
+
+void SdrTableObj::DistributeRows( sal_Int32 nFirstRow, sal_Int32 nLastRow, const bool bOptimize, const bool bMinimize )
+{
+ if( mpImpl.is() && mpImpl->mpLayouter )
+ {
+ TableModelNotifyGuard aGuard( mpImpl->mxTable.get() );
+ mpImpl->mpLayouter->DistributeRows( maRect, nFirstRow, nLastRow, bOptimize, bMinimize );
+ }
+}
+
+
+void SdrTableObj::SetChanged()
+{
+ if( mpImpl.is() )
+ {
+ mpImpl->LayoutTable( maRect, false, false );
+ }
+
+ ::SdrTextObj::SetChanged();
+}
+
+
+void SdrTableObj::uno_lock()
+{
+ if( mpImpl.is() && mpImpl->mxTable.is() )
+ mpImpl->mxTable->lockBroadcasts();
+}
+
+
+void SdrTableObj::uno_unlock()
+{
+ if( mpImpl.is() && mpImpl->mxTable.is() )
+ mpImpl->mxTable->unlockBroadcasts();
+}
+
+void SdrTableObj::dumpAsXml(xmlTextWriterPtr pWriter) const
+{
+ (void)xmlTextWriterStartElement(pWriter, BAD_CAST("SdrTableObj"));
+ (void)xmlTextWriterWriteFormatAttribute(pWriter, BAD_CAST("ptr"), "%p", this);
+
+ SdrObject::dumpAsXml(pWriter);
+
+ mpImpl->dumpAsXml(pWriter);
+
+ (void)xmlTextWriterEndElement(pWriter);
+}
+
+bool SdrTableObj::createTableEdgesJson(boost::property_tree::ptree & rJsonRoot)
+{
+ if (!mpImpl.is() || !mpImpl->mxTable.is())
+ return false;
+
+ tools::Rectangle aRect = GetCurrentBoundRect();
+ boost::property_tree::ptree aTableColumns;
+ {
+ aTableColumns.put("tableOffset", o3tl::toTwips(aRect.Left(), o3tl::Length::mm100));
+
+ boost::property_tree::ptree aEntries;
+ auto const & aEdges = mpImpl->mpLayouter->getVerticalEdges();
+ for (auto & rEdge : aEdges)
+ {
+ if (rEdge.nIndex == 0)
+ {
+ aTableColumns.put("left", o3tl::toTwips(rEdge.nPosition, o3tl::Length::mm100));
+ }
+ else if (rEdge.nIndex == sal_Int32(aEdges.size() - 1))
+ {
+ aTableColumns.put("right", o3tl::toTwips(rEdge.nPosition, o3tl::Length::mm100));
+ }
+ else
+ {
+ boost::property_tree::ptree aEntry;
+ aEntry.put("position", o3tl::toTwips(rEdge.nPosition, o3tl::Length::mm100));
+ aEntry.put("min", o3tl::toTwips(rEdge.nPosition + rEdge.nMin, o3tl::Length::mm100));
+ aEntry.put("max", o3tl::toTwips(rEdge.nPosition + rEdge.nMax, o3tl::Length::mm100));
+ aEntry.put("hidden", false);
+ aEntries.push_back(std::make_pair("", aEntry));
+ }
+ }
+ aTableColumns.push_back(std::make_pair("entries", aEntries));
+ }
+ rJsonRoot.add_child("columns", aTableColumns);
+
+ boost::property_tree::ptree aTableRows;
+ {
+ aTableRows.put("tableOffset", o3tl::toTwips(aRect.Top(), o3tl::Length::mm100));
+
+ boost::property_tree::ptree aEntries;
+ auto const & aEdges = mpImpl->mpLayouter->getHorizontalEdges();
+ for (auto & rEdge : aEdges)
+ {
+ if (rEdge.nIndex == 0)
+ {
+ aTableRows.put("left", o3tl::toTwips(rEdge.nPosition, o3tl::Length::mm100));
+ }
+ else if (rEdge.nIndex == sal_Int32(aEdges.size() - 1))
+ {
+ aTableRows.put("right", o3tl::toTwips(rEdge.nPosition, o3tl::Length::mm100));
+ }
+ else
+ {
+ boost::property_tree::ptree aEntry;
+ aEntry.put("position", o3tl::toTwips(rEdge.nPosition, o3tl::Length::mm100));
+ aEntry.put("min", o3tl::toTwips(rEdge.nPosition + rEdge.nMin, o3tl::Length::mm100));
+ aEntry.put("max", o3tl::toTwips(rEdge.nPosition + rEdge.nMax, o3tl::Length::mm100));
+ aEntry.put("hidden", false);
+ aEntries.push_back(std::make_pair("", aEntry));
+ }
+ }
+ aTableRows.push_back(std::make_pair("entries", aEntries));
+ }
+ rJsonRoot.add_child("rows", aTableRows);
+ return true;
+}
+
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/table/tablecolumn.cxx b/svx/source/table/tablecolumn.cxx
new file mode 100644
index 000000000..d9e33db47
--- /dev/null
+++ b/svx/source/table/tablecolumn.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 <com/sun/star/lang/DisposedException.hpp>
+#include <com/sun/star/lang/IndexOutOfBoundsException.hpp>
+
+#include <tablemodel.hxx>
+#include "tablecolumn.hxx"
+#include "tableundo.hxx"
+#include <svx/svdmodel.hxx>
+#include <svx/svdotable.hxx>
+
+
+using namespace ::com::sun::star::uno;
+using namespace ::com::sun::star::lang;
+using namespace ::com::sun::star::container;
+using namespace ::com::sun::star::table;
+using namespace ::com::sun::star::beans;
+
+
+namespace sdr::table {
+
+const sal_Int32 Property_Width = 0;
+const sal_Int32 Property_OptimalWidth = 1;
+const sal_Int32 Property_IsVisible = 2;
+const sal_Int32 Property_IsStartOfNewPage = 3;
+
+
+// TableRow
+
+
+TableColumn::TableColumn( const TableModelRef& xTableModel, sal_Int32 nColumn )
+: TableColumnBase( getStaticPropertySetInfo() )
+, mxTableModel( xTableModel )
+, mnColumn( nColumn )
+, mnWidth( 0 )
+, mbOptimalWidth( true )
+, mbIsVisible( true )
+, mbIsStartOfNewPage( false )
+{
+}
+
+
+TableColumn::~TableColumn()
+{
+}
+
+
+void TableColumn::dispose()
+{
+ mxTableModel.clear();
+}
+
+
+void TableColumn::throwIfDisposed() const
+{
+ if( !mxTableModel.is() )
+ throw DisposedException();
+}
+
+
+TableColumn& TableColumn::operator=( const TableColumn& r )
+{
+ mnWidth = r.mnWidth;
+ mbOptimalWidth = r.mbOptimalWidth;
+ mbIsVisible = r.mbIsVisible;
+ mbIsStartOfNewPage = r.mbIsStartOfNewPage;
+ maName = r.maName;
+ mnColumn = r.mnColumn;
+
+ return *this;
+}
+
+
+// XCellRange
+
+
+Reference< XCell > SAL_CALL TableColumn::getCellByPosition( sal_Int32 nColumn, sal_Int32 nRow )
+{
+ throwIfDisposed();
+ if( nColumn != 0 )
+ throw IndexOutOfBoundsException();
+
+ return mxTableModel->getCellByPosition( mnColumn, nRow );
+}
+
+
+Reference< XCellRange > SAL_CALL TableColumn::getCellRangeByPosition( sal_Int32 nLeft, sal_Int32 nTop, sal_Int32 nRight, sal_Int32 nBottom )
+{
+ throwIfDisposed();
+ if( (nTop >= 0 ) && (nLeft == 0) && (nBottom >= nTop) && (nRight == 0) )
+ {
+ return mxTableModel->getCellRangeByPosition( mnColumn, nTop, mnColumn, nBottom );
+ }
+ throw IndexOutOfBoundsException();
+}
+
+
+Reference< XCellRange > SAL_CALL TableColumn::getCellRangeByName( const OUString& /*aRange*/ )
+{
+ return Reference< XCellRange >();
+}
+
+
+// XNamed
+
+
+OUString SAL_CALL TableColumn::getName()
+{
+ return maName;
+}
+
+
+void SAL_CALL TableColumn::setName( const OUString& aName )
+{
+ maName = aName;
+}
+
+
+// XFastPropertySet
+
+
+void SAL_CALL TableColumn::setFastPropertyValue( sal_Int32 nHandle, const Any& aValue )
+{
+ bool bOk = false;
+ bool bChange = false;
+
+ SdrModel& rModel(mxTableModel->getSdrTableObj()->getSdrModelFromSdrObject());
+ std::unique_ptr<TableColumnUndo> pUndo;
+
+ if( mxTableModel.is() && mxTableModel->getSdrTableObj() && mxTableModel->getSdrTableObj()->IsInserted() && rModel.IsUndoEnabled() )
+ {
+ TableColumnRef xThis( this );
+ pUndo.reset( new TableColumnUndo( xThis ) );
+ }
+
+ switch( nHandle )
+ {
+ case Property_Width:
+ {
+ sal_Int32 nWidth = mnWidth;
+ bOk = aValue >>= nWidth;
+ if( bOk && (nWidth != mnWidth) )
+ {
+ mnWidth = nWidth;
+ mbOptimalWidth = mnWidth == 0;
+ bChange = true;
+ }
+ break;
+ }
+ case Property_OptimalWidth:
+ {
+ bool bOptimalWidth = mbOptimalWidth;
+ bOk = aValue >>= bOptimalWidth;
+ if( bOk && (mbOptimalWidth != bOptimalWidth) )
+ {
+ mbOptimalWidth = bOptimalWidth;
+ if( bOptimalWidth )
+ mnWidth = 0;
+ bChange = true;
+ }
+ break;
+ }
+ case Property_IsVisible:
+ {
+ bool bIsVisible = mbIsVisible;
+ bOk = aValue >>= bIsVisible;
+ if( bOk && (mbIsVisible != bIsVisible) )
+ {
+ mbIsVisible = bIsVisible;
+ bChange = true;
+ }
+ break;
+ }
+
+ case Property_IsStartOfNewPage:
+ {
+ bool bIsStartOfNewPage = mbIsStartOfNewPage;
+ bOk = aValue >>= bIsStartOfNewPage;
+ if( bOk && (mbIsStartOfNewPage != bIsStartOfNewPage) )
+ {
+ mbIsStartOfNewPage = bIsStartOfNewPage;
+ bChange = true;
+ }
+ break;
+ }
+ default:
+ throw UnknownPropertyException( OUString::number(nHandle), static_cast<cppu::OWeakObject*>(this));
+ }
+ if( !bOk )
+ {
+ throw IllegalArgumentException();
+ }
+
+ if( bChange )
+ {
+ if( pUndo )
+ {
+ rModel.AddUndo( std::move(pUndo) );
+ }
+ mxTableModel->setModified(true);
+ }
+}
+
+
+Any SAL_CALL TableColumn::getFastPropertyValue( sal_Int32 nHandle )
+{
+ switch( nHandle )
+ {
+ case Property_Width: return Any( mnWidth );
+ case Property_OptimalWidth: return Any( mbOptimalWidth );
+ case Property_IsVisible: return Any( mbIsVisible );
+ case Property_IsStartOfNewPage: return Any( mbIsStartOfNewPage );
+ default: throw UnknownPropertyException( OUString::number(nHandle), static_cast<cppu::OWeakObject*>(this));
+ }
+}
+
+
+rtl::Reference< FastPropertySetInfo > TableColumn::getStaticPropertySetInfo()
+{
+ static rtl::Reference<FastPropertySetInfo> xInfo = []() {
+ PropertyVector aProperties(6);
+
+ aProperties[0].Name = "Width";
+ aProperties[0].Handle = Property_Width;
+ aProperties[0].Type = ::cppu::UnoType<sal_Int32>::get();
+ aProperties[0].Attributes = 0;
+
+ aProperties[1].Name = "OptimalWidth";
+ aProperties[1].Handle = Property_OptimalWidth;
+ aProperties[1].Type = cppu::UnoType<bool>::get();
+ aProperties[1].Attributes = 0;
+
+ aProperties[2].Name = "IsVisible";
+ aProperties[2].Handle = Property_IsVisible;
+ aProperties[2].Type = cppu::UnoType<bool>::get();
+ aProperties[2].Attributes = 0;
+
+ aProperties[3].Name = "IsStartOfNewPage";
+ aProperties[3].Handle = Property_IsStartOfNewPage;
+ aProperties[3].Type = cppu::UnoType<bool>::get();
+ aProperties[3].Attributes = 0;
+
+ aProperties[4].Name = "Size";
+ aProperties[4].Handle = Property_Width;
+ aProperties[4].Type = ::cppu::UnoType<sal_Int32>::get();
+ aProperties[4].Attributes = 0;
+
+ aProperties[5].Name = "OptimalSize";
+ aProperties[5].Handle = Property_OptimalWidth;
+ aProperties[5].Type = cppu::UnoType<bool>::get();
+ aProperties[5].Attributes = 0;
+
+ return rtl::Reference<FastPropertySetInfo>(new FastPropertySetInfo(aProperties));
+ }();
+
+ return xInfo;
+}
+
+TableModelRef const & TableColumn::getModel() const
+{
+ return mxTableModel;
+}
+
+sal_Int32 TableColumn::getWidth() const
+{
+ return mnWidth;
+}
+
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/table/tablecolumn.hxx b/svx/source/table/tablecolumn.hxx
new file mode 100644
index 000000000..0d1dbb802
--- /dev/null
+++ b/svx/source/table/tablecolumn.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_SVX_SOURCE_TABLE_TABLECOLUMN_HXX
+#define INCLUDED_SVX_SOURCE_TABLE_TABLECOLUMN_HXX
+
+#include <com/sun/star/table/XCellRange.hpp>
+#include <com/sun/star/container/XNamed.hpp>
+#include <cppuhelper/implbase.hxx>
+
+#include "propertyset.hxx"
+#include <celltypes.hxx>
+
+
+namespace sdr::table {
+
+typedef ::cppu::ImplInheritanceHelper< FastPropertySet, css::table::XCellRange, css::container::XNamed > TableColumnBase;
+
+class TableColumn : public TableColumnBase
+{
+ friend class TableColumnUndo;
+ friend class TableModel;
+public:
+ TableColumn( const TableModelRef& xTableModel, sal_Int32 nColumn );
+ virtual ~TableColumn() override;
+
+ void dispose();
+ /// @throws css::uno::RuntimeException
+ void throwIfDisposed() const;
+
+ TableColumn& operator=( const TableColumn& );
+
+ // XCellRange
+ virtual css::uno::Reference< css::table::XCell > SAL_CALL getCellByPosition( sal_Int32 nColumn, sal_Int32 nRow ) override;
+ virtual css::uno::Reference< css::table::XCellRange > SAL_CALL getCellRangeByPosition( sal_Int32 nLeft, sal_Int32 nTop, sal_Int32 nRight, sal_Int32 nBottom ) override;
+ virtual css::uno::Reference< css::table::XCellRange > SAL_CALL getCellRangeByName( const OUString& aRange ) override;
+
+ // XNamed
+ virtual OUString SAL_CALL getName() override;
+ virtual void SAL_CALL setName( const OUString& aName ) override;
+
+ // XFastPropertySet
+ virtual void SAL_CALL setFastPropertyValue( ::sal_Int32 nHandle, const css::uno::Any& aValue ) override;
+ virtual css::uno::Any SAL_CALL getFastPropertyValue( ::sal_Int32 nHandle ) override;
+
+ /// Get the table that owns this column.
+ TableModelRef const & getModel() const;
+ /// Get the width of this column.
+ sal_Int32 getWidth() const;
+
+private:
+ static rtl::Reference< FastPropertySetInfo > getStaticPropertySetInfo();
+
+ TableModelRef mxTableModel;
+ sal_Int32 mnColumn;
+ sal_Int32 mnWidth;
+ bool mbOptimalWidth;
+ bool mbIsVisible;
+ bool mbIsStartOfNewPage;
+ OUString maName;
+};
+
+}
+
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/table/tablecolumns.cxx b/svx/source/table/tablecolumns.cxx
new file mode 100644
index 000000000..c202f3a52
--- /dev/null
+++ b/svx/source/table/tablecolumns.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 <com/sun/star/lang/DisposedException.hpp>
+#include <com/sun/star/lang/IndexOutOfBoundsException.hpp>
+
+#include <tablemodel.hxx>
+#include "tablecolumns.hxx"
+#include "tablecolumn.hxx"
+
+
+using namespace ::com::sun::star::uno;
+using namespace ::com::sun::star::lang;
+using namespace ::com::sun::star::container;
+using namespace ::com::sun::star::table;
+
+
+namespace sdr::table {
+
+TableColumns::TableColumns( const TableModelRef& xTableModel )
+: mxTableModel( xTableModel )
+{
+}
+
+
+TableColumns::~TableColumns()
+{
+ dispose();
+}
+
+
+void TableColumns::dispose()
+{
+ mxTableModel.clear();
+}
+
+
+void TableColumns::throwIfDisposed() const
+{
+ if( !mxTableModel.is() )
+ throw DisposedException();
+}
+
+
+// XTableRows
+
+
+void SAL_CALL TableColumns::insertByIndex( sal_Int32 nIndex, sal_Int32 nCount )
+{
+ throwIfDisposed();
+ mxTableModel->insertColumns( nIndex, nCount );
+}
+
+
+void SAL_CALL TableColumns::removeByIndex( sal_Int32 nIndex, sal_Int32 nCount )
+{
+ throwIfDisposed();
+ mxTableModel->removeColumns( nIndex, nCount );
+}
+
+
+// XIndexAccess
+
+
+sal_Int32 SAL_CALL TableColumns::getCount()
+{
+ throwIfDisposed();
+ return mxTableModel->getColumnCount();
+}
+
+
+Any SAL_CALL TableColumns::getByIndex( sal_Int32 Index )
+{
+ throwIfDisposed();
+
+ if( ( Index < 0 ) || ( Index >= mxTableModel->getColumnCount() ) )
+ throw IndexOutOfBoundsException();
+
+ return Any( Reference< XCellRange >( mxTableModel->getColumn( Index ) ) );
+}
+
+
+// XElementAccess
+
+
+Type SAL_CALL TableColumns::getElementType()
+{
+ throwIfDisposed();
+
+ return cppu::UnoType<XCellRange>::get();
+}
+
+
+sal_Bool SAL_CALL TableColumns::hasElements()
+{
+ throwIfDisposed();
+
+ return mxTableModel->getColumnCount() != 0;
+}
+
+
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/table/tablecolumns.hxx b/svx/source/table/tablecolumns.hxx
new file mode 100644
index 000000000..696463aac
--- /dev/null
+++ b/svx/source/table/tablecolumns.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_SVX_SOURCE_TABLE_TABLECOLUMNS_HXX
+#define INCLUDED_SVX_SOURCE_TABLE_TABLECOLUMNS_HXX
+
+#include <com/sun/star/table/XTableColumns.hpp>
+#include <cppuhelper/implbase1.hxx>
+
+#include <celltypes.hxx>
+
+
+namespace sdr::table {
+
+class TableColumns : public ::cppu::WeakAggImplHelper1< css::table::XTableColumns >
+{
+public:
+ explicit TableColumns( const TableModelRef& xTableModel );
+ virtual ~TableColumns() override;
+
+ void dispose();
+ /// @throws css::uno::RuntimeException
+ void throwIfDisposed() const;
+
+ // XTableColumns
+ virtual void SAL_CALL insertByIndex( sal_Int32 nIndex, sal_Int32 nCount ) override;
+ virtual void SAL_CALL removeByIndex( sal_Int32 nIndex, sal_Int32 nCount ) override;
+
+ // XIndexAccess
+ virtual sal_Int32 SAL_CALL getCount() override;
+ virtual css::uno::Any SAL_CALL getByIndex( sal_Int32 Index ) override;
+
+ // Methods
+ virtual css::uno::Type SAL_CALL getElementType() override;
+ virtual sal_Bool SAL_CALL hasElements() override;
+
+private:
+ TableModelRef mxTableModel;
+};
+
+}
+
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/table/tablecontroller.cxx b/svx/source/table/tablecontroller.cxx
new file mode 100644
index 000000000..1ceaa5141
--- /dev/null
+++ b/svx/source/table/tablecontroller.cxx
@@ -0,0 +1,3363 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * 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 <svx/sdr/table/tablecontroller.hxx>
+#include <tablemodel.hxx>
+
+#include <com/sun/star/style/XStyleFamiliesSupplier.hpp>
+#include <com/sun/star/container/XIndexAccess.hpp>
+
+#include <com/sun/star/beans/XPropertySet.hpp>
+#include <com/sun/star/table/XMergeableCellRange.hpp>
+#include <com/sun/star/table/XMergeableCell.hpp>
+
+#include <sal/config.h>
+#include <sal/log.hxx>
+
+#include <vcl/svapp.hxx>
+#include <vcl/settings.hxx>
+#include <vcl/window.hxx>
+
+#include <svl/whiter.hxx>
+#include <svl/stritem.hxx>
+
+#include <sfx2/request.hxx>
+
+#include <svx/svdotable.hxx>
+#include <sdr/overlay/overlayobjectcell.hxx>
+#include <svx/sdr/overlay/overlaymanager.hxx>
+#include <svx/svxids.hrc>
+#include <svx/svdoutl.hxx>
+#include <svx/svdpagv.hxx>
+#include <svx/svdetc.hxx>
+#include <svx/selectioncontroller.hxx>
+#include <svx/svdmodel.hxx>
+#include <svx/sdrpaintwindow.hxx>
+#include <svx/svxdlg.hxx>
+#include <editeng/boxitem.hxx>
+#include <cell.hxx>
+#include <editeng/borderline.hxx>
+#include <editeng/colritem.hxx>
+#include <editeng/lineitem.hxx>
+#include <svx/strings.hrc>
+#include <svx/dialmgr.hxx>
+#include <svx/svdpage.hxx>
+#include <svx/sdmetitm.hxx>
+#include <svx/sdtditm.hxx>
+#include "tableundo.hxx"
+#include "tablelayouter.hxx"
+#include <LibreOfficeKit/LibreOfficeKitEnums.h>
+#include <memory>
+#include <o3tl/enumarray.hxx>
+#include <o3tl/enumrange.hxx>
+#include <cppuhelper/implbase.hxx>
+#include <comphelper/lok.hxx>
+#include <sfx2/viewsh.hxx>
+#include <editeng/editview.hxx>
+#include <tools/UnitConversion.hxx>
+#include <tools/diagnose_ex.h>
+
+using ::editeng::SvxBorderLine;
+using namespace sdr::table;
+using namespace ::com::sun::star;
+using namespace ::com::sun::star::uno;
+using namespace ::com::sun::star::table;
+using namespace ::com::sun::star::beans;
+using namespace ::com::sun::star::container;
+using namespace ::com::sun::star::text;
+using namespace ::com::sun::star::style;
+
+namespace {
+
+enum class CellPosFlag // signals the relative position of a cell to a selection
+{
+ NONE = 0x0000, // not set or inside
+ // row
+ Before = 0x0001,
+ Left = 0x0002,
+ Right = 0x0004,
+ After = 0x0008,
+ // column
+ Upper = 0x0010,
+ Top = 0x0020,
+ Bottom = 0x0040,
+ Lower = 0x0080
+};
+
+}
+
+namespace o3tl
+{ template<> struct typed_flags<CellPosFlag> : is_typed_flags<CellPosFlag, 0xff> {}; }
+
+namespace sdr::table {
+
+namespace {
+
+class SvxTableControllerModifyListener : public ::cppu::WeakImplHelper< css::util::XModifyListener >
+{
+public:
+ explicit SvxTableControllerModifyListener( SvxTableController* pController )
+ : mpController( pController ) {}
+
+ // XModifyListener
+ virtual void SAL_CALL modified( const css::lang::EventObject& aEvent ) override;
+
+ // XEventListener
+ virtual void SAL_CALL disposing( const css::lang::EventObject& Source ) override;
+
+ SvxTableController* mpController;
+};
+
+}
+
+// XModifyListener
+
+
+void SAL_CALL SvxTableControllerModifyListener::modified( const css::lang::EventObject& )
+{
+ if( mpController )
+ mpController->onTableModified();
+}
+
+
+// XEventListener
+
+
+void SAL_CALL SvxTableControllerModifyListener::disposing( const css::lang::EventObject& )
+{
+ mpController = nullptr;
+}
+
+
+
+
+rtl::Reference< sdr::SelectionController > CreateTableController(
+ SdrView& rView,
+ const SdrTableObj& rObj,
+ const rtl::Reference< sdr::SelectionController >& xRefController )
+{
+ return SvxTableController::create(rView, rObj, xRefController);
+}
+
+
+rtl::Reference< sdr::SelectionController > SvxTableController::create(
+ SdrView& rView,
+ const SdrTableObj& rObj,
+ const rtl::Reference< sdr::SelectionController >& xRefController )
+{
+ if( xRefController.is() )
+ {
+ SvxTableController* pController = dynamic_cast< SvxTableController* >( xRefController.get() );
+
+ if(pController && (pController->mxTableObj.get() == &rObj) && (&pController->mrView == &rView))
+ {
+ return xRefController;
+ }
+ }
+
+ return new SvxTableController(rView, rObj);
+}
+
+
+SvxTableController::SvxTableController(
+ SdrView& rView,
+ const SdrTableObj& rObj)
+: mbCellSelectionMode(false)
+ ,mbHasJustMerged(false)
+ ,mbLeftButtonDown(false)
+ ,mrView(rView)
+ ,mxTableObj(const_cast< SdrTableObj* >(&rObj))
+ ,mnUpdateEvent( nullptr )
+{
+ mxTableObj->getActiveCellPos( maCursorFirstPos );
+ maCursorLastPos = maCursorFirstPos;
+
+ Reference< XTable > xTable( mxTableObj->getTable() );
+ if( xTable.is() )
+ {
+ mxModifyListener = new SvxTableControllerModifyListener( this );
+ xTable->addModifyListener( mxModifyListener );
+
+ mxTable.set( dynamic_cast< TableModel* >( xTable.get() ) );
+ }
+}
+
+SvxTableController::~SvxTableController()
+{
+ if( mnUpdateEvent )
+ {
+ Application::RemoveUserEvent( mnUpdateEvent );
+ }
+
+ if( mxModifyListener.is() && mxTableObj )
+ {
+ Reference< XTable > xTable( mxTableObj->getTable() );
+ if( xTable.is() )
+ {
+ xTable->removeModifyListener( mxModifyListener );
+ mxModifyListener.clear();
+ }
+ }
+}
+
+bool SvxTableController::onKeyInput(const KeyEvent& rKEvt, vcl::Window* pWindow )
+{
+ if(!checkTableObject())
+ return false;
+
+ SdrTableObj& rTableObj(*mxTableObj);
+ SdrModel& rModel(rTableObj.getSdrModelFromSdrObject());
+
+ // check if we are read only
+ if( rModel.IsReadOnly())
+ {
+ switch( rKEvt.GetKeyCode().GetCode() )
+ {
+ case awt::Key::DOWN:
+ case awt::Key::UP:
+ case awt::Key::LEFT:
+ case awt::Key::RIGHT:
+ case awt::Key::TAB:
+ case awt::Key::HOME:
+ case awt::Key::END:
+ case awt::Key::NUM2:
+ case awt::Key::NUM4:
+ case awt::Key::NUM6:
+ case awt::Key::NUM8:
+ case awt::Key::ESCAPE:
+ case awt::Key::F2:
+ break;
+ default:
+ // tell the view we eat the event, no further processing needed
+ return true;
+ }
+ }
+
+ TblAction nAction = getKeyboardAction(rKEvt);
+
+ return executeAction( nAction, rKEvt.GetKeyCode().IsShift(), pWindow );
+}
+
+namespace {
+
+Point pixelToLogic(const Point& rPoint, vcl::Window const * pWindow)
+{
+ if (!pWindow)
+ return rPoint;
+
+ return pWindow->PixelToLogic(rPoint);
+}
+
+}
+
+bool SvxTableController::onMouseButtonDown(const MouseEvent& rMEvt, vcl::Window* pWindow )
+{
+ if (comphelper::LibreOfficeKit::isActive() && !pWindow)
+ {
+ // Tiled rendering: get the window that has the disabled map mode.
+ if (OutputDevice* pOutputDevice = mrView.GetFirstOutputDevice())
+ {
+ if (pOutputDevice->GetOutDevType() == OUTDEV_WINDOW)
+ pWindow = pOutputDevice->GetOwnerWindow();
+ }
+ }
+
+ if( !pWindow || !checkTableObject() )
+ return false;
+
+ SdrViewEvent aVEvt;
+ if( !rMEvt.IsRight() && mrView.PickAnything(rMEvt,SdrMouseEventKind::BUTTONDOWN, aVEvt) == SdrHitKind::Handle )
+ return false;
+
+ TableHitKind eHit = mxTableObj->CheckTableHit(pixelToLogic(rMEvt.GetPosPixel(), pWindow), maMouseDownPos.mnCol, maMouseDownPos.mnRow);
+
+ mbLeftButtonDown = (rMEvt.GetClicks() == 1) && rMEvt.IsLeft();
+
+ if( eHit == TableHitKind::Cell )
+ {
+ StartSelection( maMouseDownPos );
+ return true;
+ }
+
+ if( rMEvt.IsRight() && eHit != TableHitKind::NONE )
+ return true; // right click will become context menu
+
+ // for cell selection with the mouse remember our first hit
+ if( mbLeftButtonDown )
+ {
+ RemoveSelection();
+
+ SdrHdl* pHdl = mrView.PickHandle(pixelToLogic(rMEvt.GetPosPixel(), pWindow));
+
+ if( pHdl )
+ {
+ mbLeftButtonDown = false;
+ }
+ else
+ {
+ sdr::table::SdrTableObj* pTableObj = mxTableObj.get();
+
+ if (!pTableObj || eHit == TableHitKind::NONE)
+ {
+ mbLeftButtonDown = false;
+ }
+ }
+ }
+
+ if (comphelper::LibreOfficeKit::isActive() && rMEvt.GetClicks() == 2 && rMEvt.IsLeft() && eHit == TableHitKind::CellTextArea)
+ {
+ bool bEmptyOutliner = false;
+ if (Outliner* pOutliner = mrView.GetTextEditOutliner())
+ {
+ if (pOutliner->GetParagraphCount() == 1)
+ {
+ if (Paragraph* pParagraph = pOutliner->GetParagraph(0))
+ bEmptyOutliner = pOutliner->GetText(pParagraph).isEmpty();
+ }
+ }
+ if (bEmptyOutliner)
+ {
+ // Tiled rendering: a left double-click in an empty cell: select it.
+ StartSelection(maMouseDownPos);
+ setSelectedCells(maMouseDownPos, maMouseDownPos);
+ // Update graphic selection, should be hidden now.
+ mrView.AdjustMarkHdl();
+ return true;
+ }
+ }
+
+ return false;
+}
+
+
+bool SvxTableController::onMouseButtonUp(const MouseEvent& rMEvt, vcl::Window* /*pWin*/)
+{
+ if( !checkTableObject() )
+ return false;
+
+ mbLeftButtonDown = false;
+
+ return rMEvt.GetClicks() == 2;
+}
+
+
+bool SvxTableController::onMouseMove(const MouseEvent& rMEvt, vcl::Window* pWindow )
+{
+ if( !checkTableObject() )
+ return false;
+
+ SdrTableObj* pTableObj = mxTableObj.get();
+ CellPos aPos;
+ if (mbLeftButtonDown && pTableObj && pTableObj->CheckTableHit(pixelToLogic(rMEvt.GetPosPixel(), pWindow), aPos.mnCol, aPos.mnRow ) != TableHitKind::NONE)
+ {
+ if(aPos != maMouseDownPos)
+ {
+ if( mbCellSelectionMode )
+ {
+ setSelectedCells( maMouseDownPos, aPos );
+ return true;
+ }
+ else
+ {
+ StartSelection( maMouseDownPos );
+ }
+ }
+ else if( mbCellSelectionMode )
+ {
+ UpdateSelection( aPos );
+ return true;
+ }
+ }
+ return false;
+}
+
+
+void SvxTableController::onSelectionHasChanged()
+{
+ bool bSelected = false;
+
+ SdrTableObj* pTableObj = mxTableObj.get();
+ if( pTableObj && pTableObj->IsTextEditActive() )
+ {
+ pTableObj->getActiveCellPos( maCursorFirstPos );
+ maCursorLastPos = maCursorFirstPos;
+ mbCellSelectionMode = false;
+ }
+ else
+ {
+ const SdrMarkList& rMarkList= mrView.GetMarkedObjectList();
+ if( rMarkList.GetMarkCount() == 1 )
+ bSelected = mxTableObj.get() == rMarkList.GetMark(0)->GetMarkedSdrObj();
+ }
+
+ if( bSelected )
+ {
+ updateSelectionOverlay();
+ }
+ else
+ {
+ destroySelectionOverlay();
+ }
+}
+void SvxTableController::onSelectAll()
+{
+ sdr::table::SdrTableObj* pTableObj = mxTableObj.get();
+ if ( pTableObj && !pTableObj->IsTextEditActive())
+ {
+ selectAll();
+ }
+}
+
+
+void SvxTableController::GetState( SfxItemSet& rSet )
+{
+ if(!mxTable.is() || !mxTableObj.is())
+ return;
+
+ SdrTableObj& rTableObj(*mxTableObj);
+ SdrModel& rModel(rTableObj.getSdrModelFromSdrObject());
+ std::optional<SfxItemSet> oSet;
+ bool bVertDone(false);
+
+ // Iterate over all requested items in the set.
+ SfxWhichIter aIter( rSet );
+ sal_uInt16 nWhich = aIter.FirstWhich();
+ while (nWhich)
+ {
+ switch (nWhich)
+ {
+ case SID_TABLE_VERT_BOTTOM:
+ case SID_TABLE_VERT_CENTER:
+ case SID_TABLE_VERT_NONE:
+ {
+ if(!bVertDone)
+ {
+ if (!oSet)
+ {
+ oSet.emplace(rModel.GetItemPool());
+ MergeAttrFromSelectedCells(*oSet, false);
+ }
+
+ SdrTextVertAdjust eAdj = SDRTEXTVERTADJUST_BLOCK;
+
+ if (oSet->GetItemState( SDRATTR_TEXT_VERTADJUST ) != SfxItemState::DONTCARE)
+ eAdj = oSet->Get(SDRATTR_TEXT_VERTADJUST).GetValue();
+
+ rSet.Put(SfxBoolItem(SID_TABLE_VERT_BOTTOM, eAdj == SDRTEXTVERTADJUST_BOTTOM));
+ rSet.Put(SfxBoolItem(SID_TABLE_VERT_CENTER, eAdj == SDRTEXTVERTADJUST_CENTER));
+ rSet.Put(SfxBoolItem(SID_TABLE_VERT_NONE, eAdj == SDRTEXTVERTADJUST_TOP));
+ bVertDone = true;
+ }
+ break;
+ }
+ case SID_TABLE_DELETE_ROW:
+ if( !mxTable.is() || !hasSelectedCells() || (!comphelper::LibreOfficeKit::isActive() && mxTable->getRowCount() <= 1) )
+ rSet.DisableItem(SID_TABLE_DELETE_ROW);
+ break;
+ case SID_TABLE_DELETE_COL:
+ if( !mxTable.is() || !hasSelectedCells() || (!comphelper::LibreOfficeKit::isActive() && mxTable->getColumnCount() <= 1) )
+ rSet.DisableItem(SID_TABLE_DELETE_COL);
+ break;
+ case SID_TABLE_DELETE_TABLE:
+ if( !mxTable.is() )
+ rSet.DisableItem(SID_TABLE_DELETE_TABLE);
+ break;
+ case SID_TABLE_MERGE_CELLS:
+ if( !mxTable.is() || !hasSelectedCells() )
+ rSet.DisableItem(SID_TABLE_MERGE_CELLS);
+ break;
+ case SID_TABLE_SPLIT_CELLS:
+ if( !hasSelectedCells() || !mxTable.is() )
+ rSet.DisableItem(SID_TABLE_SPLIT_CELLS);
+ break;
+
+ case SID_TABLE_OPTIMAL_ROW_HEIGHT:
+ case SID_TABLE_DISTRIBUTE_COLUMNS:
+ case SID_TABLE_DISTRIBUTE_ROWS:
+ {
+ bool bDistributeColumns = false;
+ bool bDistributeRows = false;
+ if( mxTable.is() )
+ {
+ CellPos aStart, aEnd;
+ getSelectedCells( aStart, aEnd );
+
+ bDistributeColumns = aStart.mnCol != aEnd.mnCol;
+ bDistributeRows = aStart.mnRow != aEnd.mnRow;
+ }
+ if( !bDistributeColumns )
+ rSet.DisableItem(SID_TABLE_DISTRIBUTE_COLUMNS);
+ if( !bDistributeRows )
+ {
+ rSet.DisableItem(SID_TABLE_OPTIMAL_ROW_HEIGHT);
+ rSet.DisableItem(SID_TABLE_DISTRIBUTE_ROWS);
+ }
+ break;
+ }
+
+ default:
+ break;
+ }
+ nWhich = aIter.NextWhich();
+ }
+}
+
+
+void SvxTableController::onInsert( sal_uInt16 nSId, const SfxItemSet* pArgs )
+{
+ if(!checkTableObject())
+ return;
+
+ SdrTableObj& rTableObj(*mxTableObj);
+ SdrModel& rModel(rTableObj.getSdrModelFromSdrObject());
+ bool bInsertAfter = true;
+ sal_uInt16 nCount = 0;
+
+ if( pArgs )
+ {
+ const SfxPoolItem* pItem = nullptr;
+ pArgs->GetItemState(nSId, false, &pItem);
+ if (pItem)
+ {
+ nCount = static_cast<const SfxInt16Item*>(pItem)->GetValue();
+ if(const SfxBoolItem* pItem2 = pArgs->GetItemIfSet(SID_TABLE_PARAM_INSERT_AFTER))
+ bInsertAfter = pItem2->GetValue();
+ }
+ }
+
+ CellPos aStart, aEnd;
+ if( hasSelectedCells() )
+ {
+ getSelectedCells( aStart, aEnd );
+ }
+ else
+ {
+ if( bInsertAfter )
+ {
+ aStart.mnCol = mxTable->getColumnCount() - 1;
+ aStart.mnRow = mxTable->getRowCount() - 1;
+ aEnd = aStart;
+ }
+ }
+
+ if( rTableObj.IsTextEditActive() )
+ mrView.SdrEndTextEdit(true);
+
+ RemoveSelection();
+
+ static const OUStringLiteral sSize( u"Size" );
+ const bool bUndo(rModel.IsUndoEnabled());
+
+ switch( nSId )
+ {
+ case SID_TABLE_INSERT_COL:
+ {
+ TableModelNotifyGuard aGuard( mxTable.get() );
+
+ if( bUndo )
+ {
+ rModel.BegUndo( SvxResId(STR_TABLE_INSCOL) );
+ rModel.AddUndo(rModel.GetSdrUndoFactory().CreateUndoGeoObject(rTableObj));
+ }
+
+ Reference< XTableColumns > xCols( mxTable->getColumns() );
+ const sal_Int32 nNewColumns = (nCount == 0) ? (aEnd.mnCol - aStart.mnCol + 1) : nCount;
+ const sal_Int32 nNewStartColumn = aEnd.mnCol + (bInsertAfter ? 1 : 0);
+ xCols->insertByIndex( nNewStartColumn, nNewColumns );
+
+ for( sal_Int32 nOffset = 0; nOffset < nNewColumns; nOffset++ )
+ {
+ // Resolves fdo#61540
+ // On Insert before, the reference column whose size is going to be
+ // used for newly created column(s) is wrong. As the new columns are
+ // inserted before the reference column, the reference column moved
+ // to the new position by no., of new columns i.e (earlier+newcolumns).
+ Reference< XPropertySet >(xCols->getByIndex(nNewStartColumn+nOffset), UNO_QUERY_THROW )->
+ setPropertyValue( sSize,
+ Reference< XPropertySet >(xCols->getByIndex( bInsertAfter?nNewStartColumn-1:nNewStartColumn+nNewColumns ), UNO_QUERY_THROW )->
+ getPropertyValue( sSize ) );
+ }
+
+ // Copy cell properties
+ sal_Int32 nPropSrcCol = (bInsertAfter ? aEnd.mnCol : aStart.mnCol + nNewColumns);
+ sal_Int32 nRowSpan = 0;
+ bool bNewSpan = false;
+
+ for( sal_Int32 nRow = 0; nRow < mxTable->getRowCount(); ++nRow )
+ {
+ CellRef xSourceCell( dynamic_cast< Cell* >( mxTable->getCellByPosition( nPropSrcCol, nRow ).get() ) );
+
+ // When we insert new COLUMNs, we want to copy ROW spans.
+ if (xSourceCell.is() && nRowSpan == 0)
+ {
+ // we are not in a span yet. Let's find out if the current cell is in a span.
+ sal_Int32 nColSpan = sal_Int32();
+ sal_Int32 nSpanInfoCol = sal_Int32();
+
+ if( xSourceCell->getRowSpan() > 1 )
+ {
+ // The current cell is the top-left cell in a span.
+ // Get the span info and propagate it to the target.
+ nRowSpan = xSourceCell->getRowSpan();
+ nColSpan = xSourceCell->getColumnSpan();
+ nSpanInfoCol = nPropSrcCol;
+ }
+ else if( xSourceCell->isMerged() )
+ {
+ // The current cell is a middle cell in a 2D span.
+ // Look for the top-left cell in the span.
+ for( nSpanInfoCol = nPropSrcCol - 1; nSpanInfoCol >= 0; --nSpanInfoCol )
+ {
+ CellRef xMergeInfoCell( dynamic_cast< Cell* >( mxTable->getCellByPosition( nSpanInfoCol, nRow ).get() ) );
+ if (xMergeInfoCell.is() && !xMergeInfoCell->isMerged())
+ {
+ nRowSpan = xMergeInfoCell->getRowSpan();
+ nColSpan = xMergeInfoCell->getColumnSpan();
+ break;
+ }
+ }
+ if( nRowSpan == 1 )
+ nRowSpan = 0;
+ }
+
+ // The target columns are outside the span; Start a new span.
+ if( nRowSpan > 0 && ( nNewStartColumn < nSpanInfoCol || nSpanInfoCol + nColSpan <= nNewStartColumn ) )
+ bNewSpan = true;
+ }
+
+ // Now copy the properties from the source to the targets
+ for( sal_Int32 nOffset = 0; nOffset < nNewColumns; nOffset++ )
+ {
+ CellRef xTargetCell( dynamic_cast< Cell* >( mxTable->getCellByPosition( nNewStartColumn + nOffset, nRow ).get() ) );
+ if( xTargetCell.is() )
+ {
+ if( nRowSpan > 0 )
+ {
+ if( bNewSpan )
+ xTargetCell->merge( 1, nRowSpan );
+ else
+ xTargetCell->setMerged();
+ }
+ xTargetCell->copyFormatFrom( xSourceCell );
+ }
+ }
+
+ if( nRowSpan > 0 )
+ {
+ --nRowSpan;
+ bNewSpan = false;
+ }
+ }
+
+ if( bUndo )
+ rModel.EndUndo();
+
+ aStart.mnCol = nNewStartColumn;
+ aStart.mnRow = 0;
+ aEnd.mnCol = aStart.mnCol + nNewColumns - 1;
+ aEnd.mnRow = mxTable->getRowCount() - 1;
+ break;
+ }
+
+ case SID_TABLE_INSERT_ROW:
+ {
+ TableModelNotifyGuard aGuard( mxTable.get() );
+
+ if( bUndo )
+ {
+ rModel.BegUndo( SvxResId(STR_TABLE_INSROW ) );
+ rModel.AddUndo(rModel.GetSdrUndoFactory().CreateUndoGeoObject(rTableObj));
+ }
+
+ Reference< XTableRows > xRows( mxTable->getRows() );
+ const sal_Int32 nNewRows = (nCount == 0) ? (aEnd.mnRow - aStart.mnRow + 1) : nCount;
+ const sal_Int32 nNewRowStart = aEnd.mnRow + (bInsertAfter ? 1 : 0);
+ xRows->insertByIndex( nNewRowStart, nNewRows );
+
+ for( sal_Int32 nOffset = 0; nOffset < nNewRows; nOffset++ )
+ {
+ Reference< XPropertySet >( xRows->getByIndex( aEnd.mnRow + nOffset + 1 ), UNO_QUERY_THROW )->
+ setPropertyValue( sSize,
+ Reference< XPropertySet >( xRows->getByIndex( aStart.mnRow + nOffset ), UNO_QUERY_THROW )->
+ getPropertyValue( sSize ) );
+ }
+
+ // Copy the cell properties
+ sal_Int32 nPropSrcRow = (bInsertAfter ? aEnd.mnRow : aStart.mnRow + nNewRows);
+ sal_Int32 nColSpan = 0;
+ bool bNewSpan = false;
+
+ for( sal_Int32 nCol = 0; nCol < mxTable->getColumnCount(); ++nCol )
+ {
+ CellRef xSourceCell( dynamic_cast< Cell* >( mxTable->getCellByPosition( nCol, nPropSrcRow ).get() ) );
+
+ if (!xSourceCell.is())
+ continue;
+
+ // When we insert new ROWs, we want to copy COLUMN spans.
+ if( nColSpan == 0 )
+ {
+ // we are not in a span yet. Let's find out if the current cell is in a span.
+ sal_Int32 nRowSpan = sal_Int32();
+ sal_Int32 nSpanInfoRow = sal_Int32();
+
+ if( xSourceCell->getColumnSpan() > 1 )
+ {
+ // The current cell is the top-left cell in a span.
+ // Get the span info and propagate it to the target.
+ nColSpan = xSourceCell->getColumnSpan();
+ nRowSpan = xSourceCell->getRowSpan();
+ nSpanInfoRow = nPropSrcRow;
+ }
+ else if( xSourceCell->isMerged() )
+ {
+ // The current cell is a middle cell in a 2D span.
+ // Look for the top-left cell in the span.
+ for( nSpanInfoRow = nPropSrcRow - 1; nSpanInfoRow >= 0; --nSpanInfoRow )
+ {
+ CellRef xMergeInfoCell( dynamic_cast< Cell* >( mxTable->getCellByPosition( nCol, nSpanInfoRow ).get() ) );
+ if (xMergeInfoCell.is() && !xMergeInfoCell->isMerged())
+ {
+ nColSpan = xMergeInfoCell->getColumnSpan();
+ nRowSpan = xMergeInfoCell->getRowSpan();
+ break;
+ }
+ }
+ if( nColSpan == 1 )
+ nColSpan = 0;
+ }
+
+ // Inserted rows are outside the span; Start a new span.
+ if( nColSpan > 0 && ( nNewRowStart < nSpanInfoRow || nSpanInfoRow + nRowSpan <= nNewRowStart ) )
+ bNewSpan = true;
+ }
+
+ // Now copy the properties from the source to the targets
+ for( sal_Int32 nOffset = 0; nOffset < nNewRows; ++nOffset )
+ {
+ CellRef xTargetCell( dynamic_cast< Cell* >( mxTable->getCellByPosition( nCol, nNewRowStart + nOffset ).get() ) );
+ if( xTargetCell.is() )
+ {
+ if( nColSpan > 0 )
+ {
+ if( bNewSpan )
+ xTargetCell->merge( nColSpan, 1 );
+ else
+ xTargetCell->setMerged();
+ }
+ xTargetCell->copyFormatFrom( xSourceCell );
+ }
+ }
+
+ if( nColSpan > 0 )
+ {
+ --nColSpan;
+ bNewSpan = false;
+ }
+ }
+
+ if( bUndo )
+ rModel.EndUndo();
+
+ aStart.mnCol = 0;
+ aStart.mnRow = nNewRowStart;
+ aEnd.mnCol = mxTable->getColumnCount() - 1;
+ aEnd.mnRow = aStart.mnRow + nNewRows - 1;
+ break;
+ }
+ }
+
+ StartSelection( aStart );
+ UpdateSelection( aEnd );
+}
+
+
+void SvxTableController::onDelete( sal_uInt16 nSId )
+{
+ sdr::table::SdrTableObj* pTableObj = mxTableObj.get();
+ if( !pTableObj || !mxTable.is() )
+ return;
+
+ if( nSId == SID_TABLE_DELETE_TABLE )
+ {
+ if( pTableObj->IsTextEditActive() )
+ mrView.SdrEndTextEdit(true);
+
+ mrView.DeleteMarkedObj();
+ }
+ else if( hasSelectedCells() )
+ {
+ CellPos aStart, aEnd;
+ getSelectedCells( aStart, aEnd );
+
+ if( pTableObj->IsTextEditActive() )
+ mrView.SdrEndTextEdit(true);
+
+ RemoveSelection();
+
+ bool bDeleteTable = false;
+ switch( nSId )
+ {
+ case SID_TABLE_DELETE_COL:
+ {
+ const sal_Int32 nRemovedColumns = aEnd.mnCol - aStart.mnCol + 1;
+ if( nRemovedColumns == mxTable->getColumnCount() )
+ {
+ bDeleteTable = true;
+ }
+ else
+ {
+ Reference< XTableColumns > xCols( mxTable->getColumns() );
+ xCols->removeByIndex( aStart.mnCol, nRemovedColumns );
+ EditCell(aStart, nullptr, TblAction::NONE);
+ }
+ break;
+ }
+
+ case SID_TABLE_DELETE_ROW:
+ {
+ const sal_Int32 nRemovedRows = aEnd.mnRow - aStart.mnRow + 1;
+ if( nRemovedRows == mxTable->getRowCount() )
+ {
+ bDeleteTable = true;
+ }
+ else
+ {
+ Reference< XTableRows > xRows( mxTable->getRows() );
+ xRows->removeByIndex( aStart.mnRow, nRemovedRows );
+ EditCell(aStart, nullptr, TblAction::NONE);
+ }
+ break;
+ }
+ }
+
+ if( bDeleteTable )
+ mrView.DeleteMarkedObj();
+ else
+ UpdateTableShape();
+ }
+}
+
+
+void SvxTableController::onSelect( sal_uInt16 nSId )
+{
+ if( !mxTable.is() )
+ return;
+
+ const sal_Int32 nRowCount = mxTable->getRowCount();
+ const sal_Int32 nColCount = mxTable->getColumnCount();
+ if( !(nRowCount && nColCount) )
+ return;
+
+ CellPos aStart, aEnd;
+ getSelectedCells( aStart, aEnd );
+
+ switch( nSId )
+ {
+ case SID_TABLE_SELECT_ALL:
+ aEnd.mnCol = 0; aEnd.mnRow = 0;
+ aStart.mnCol = nColCount - 1; aStart.mnRow = nRowCount - 1;
+ break;
+ case SID_TABLE_SELECT_COL:
+ aEnd.mnRow = nRowCount - 1;
+ aStart.mnRow = 0;
+ break;
+ case SID_TABLE_SELECT_ROW:
+ aEnd.mnCol = nColCount - 1;
+ aStart.mnCol = 0;
+ break;
+ }
+
+ StartSelection( aEnd );
+ gotoCell( aStart, true, nullptr );
+}
+
+namespace
+{
+ SvxBoxItem mergeDrawinglayerTextDistancesAndSvxBoxItem(const SfxItemSet& rAttrSet)
+ {
+ // merge drawing layer text distance items into SvxBoxItem used by the dialog
+ SvxBoxItem aBoxItem( rAttrSet.Get( SDRATTR_TABLE_BORDER ) );
+ aBoxItem.SetDistance( sal::static_int_cast< sal_uInt16 >( rAttrSet.Get(SDRATTR_TEXT_LEFTDIST).GetValue()), SvxBoxItemLine::LEFT );
+ aBoxItem.SetDistance( sal::static_int_cast< sal_uInt16 >( rAttrSet.Get(SDRATTR_TEXT_RIGHTDIST).GetValue()), SvxBoxItemLine::RIGHT );
+ aBoxItem.SetDistance( sal::static_int_cast< sal_uInt16 >( rAttrSet.Get(SDRATTR_TEXT_UPPERDIST).GetValue()), SvxBoxItemLine::TOP );
+ aBoxItem.SetDistance( sal::static_int_cast< sal_uInt16 >( rAttrSet.Get(SDRATTR_TEXT_LOWERDIST).GetValue()), SvxBoxItemLine::BOTTOM );
+ return aBoxItem;
+ }
+}
+
+void SvxTableController::onFormatTable(const SfxRequest& rReq)
+{
+ if(!mxTableObj.is())
+ return;
+
+ SdrTableObj& rTableObj(*mxTableObj);
+ SdrModel& rModel(rTableObj.getSdrModelFromSdrObject());
+ const SfxItemSet* pArgs = rReq.GetArgs();
+
+ if(pArgs)
+ return;
+
+ SfxItemSet aNewAttr(rModel.GetItemPool());
+
+ // merge drawing layer text distance items into SvxBoxItem used by the dialog
+ SvxBoxItem aBoxItem(mergeDrawinglayerTextDistancesAndSvxBoxItem(aNewAttr));
+
+ SvxBoxInfoItem aBoxInfoItem( aNewAttr.Get( SDRATTR_TABLE_BORDER_INNER ) );
+
+ MergeAttrFromSelectedCells(aNewAttr, false);
+ FillCommonBorderAttrFromSelectedCells( aBoxItem, aBoxInfoItem );
+ aNewAttr.Put( aBoxItem );
+ aNewAttr.Put( aBoxInfoItem );
+
+ // Fill in shadow properties.
+ const SfxItemSet& rTableItemSet = rTableObj.GetMergedItemSet();
+ for (sal_uInt16 nWhich = SDRATTR_SHADOW_FIRST; nWhich <= SDRATTR_SHADOW_LAST; ++nWhich)
+ {
+ if (rTableItemSet.GetItemState(nWhich, false) != SfxItemState::SET)
+ {
+ continue;
+ }
+
+ aNewAttr.Put(rTableItemSet.Get(nWhich));
+ }
+
+ SvxAbstractDialogFactory* pFact = SvxAbstractDialogFactory::Create();
+ VclPtr<SfxAbstractTabDialog> xDlg( pFact->CreateSvxFormatCellsDialog(
+ rReq.GetFrameWeld(),
+ &aNewAttr,
+ rModel) );
+
+ // Even Cancel Button is returning positive(101) value,
+ xDlg->StartExecuteAsync([xDlg, this, aBoxItem, aBoxInfoItem](int nResult){
+ if (nResult == RET_OK)
+ {
+ SfxItemSet aNewSet(*(xDlg->GetOutputItemSet()));
+
+ //Only properties that were unchanged by the dialog appear in this
+ //itemset. We had constructed these two properties from other
+ //ones, so if they were not changed, then forcible set them back to
+ //their originals in the new result set so we can decompose that
+ //unchanged state back to their input properties
+ if (aNewSet.GetItemState(SDRATTR_TABLE_BORDER, false) != SfxItemState::SET)
+ {
+ aNewSet.Put(aBoxItem);
+ }
+ if (aNewSet.GetItemState(SDRATTR_TABLE_BORDER_INNER, false) != SfxItemState::SET)
+ {
+ aNewSet.Put(aBoxInfoItem);
+ }
+
+ SvxBoxItem aNewBoxItem( aNewSet.Get( SDRATTR_TABLE_BORDER ) );
+
+ if( aNewBoxItem.GetDistance( SvxBoxItemLine::LEFT ) != aBoxItem.GetDistance( SvxBoxItemLine::LEFT ) )
+ aNewSet.Put(makeSdrTextLeftDistItem( aNewBoxItem.GetDistance( SvxBoxItemLine::LEFT ) ) );
+
+ if( aNewBoxItem.GetDistance( SvxBoxItemLine::RIGHT ) != aBoxItem.GetDistance( SvxBoxItemLine::RIGHT ) )
+ aNewSet.Put(makeSdrTextRightDistItem( aNewBoxItem.GetDistance( SvxBoxItemLine::RIGHT ) ) );
+
+ if( aNewBoxItem.GetDistance( SvxBoxItemLine::TOP ) != aBoxItem.GetDistance( SvxBoxItemLine::TOP ) )
+ aNewSet.Put(makeSdrTextUpperDistItem( aNewBoxItem.GetDistance( SvxBoxItemLine::TOP ) ) );
+
+ if( aNewBoxItem.GetDistance( SvxBoxItemLine::BOTTOM ) != aBoxItem.GetDistance( SvxBoxItemLine::BOTTOM ) )
+ aNewSet.Put(makeSdrTextLowerDistItem( aNewBoxItem.GetDistance( SvxBoxItemLine::BOTTOM ) ) );
+
+ if (checkTableObject() && mxTable.is())
+ {
+ // Create a single undo action when applying the result of the dialog.
+ SdrTableObj& rTableObject(*mxTableObj);
+ SdrModel& rSdrModel(rTableObject.getSdrModelFromSdrObject());
+ bool bUndo = rSdrModel.IsUndoEnabled() && !mrView.IsTextEdit();
+ if (bUndo)
+ {
+ rSdrModel.BegUndo(SvxResId(STR_TABLE_NUMFORMAT));
+ }
+
+ this->SetAttrToSelectedCells(aNewSet, false);
+
+ this->SetAttrToSelectedShape(aNewSet);
+
+ if (bUndo)
+ {
+ rSdrModel.EndUndo();
+ }
+ }
+ }
+
+ xDlg->disposeOnce();
+ });
+}
+
+void SvxTableController::Execute( SfxRequest& rReq )
+{
+ const sal_uInt16 nSId = rReq.GetSlot();
+ switch( nSId )
+ {
+ case SID_TABLE_INSERT_ROW:
+ case SID_TABLE_INSERT_COL:
+ onInsert( nSId, rReq.GetArgs() );
+ break;
+ case SID_TABLE_DELETE_ROW:
+ case SID_TABLE_DELETE_COL:
+ case SID_TABLE_DELETE_TABLE:
+ onDelete( nSId );
+ break;
+ case SID_TABLE_SELECT_ALL:
+ case SID_TABLE_SELECT_COL:
+ case SID_TABLE_SELECT_ROW:
+ onSelect( nSId );
+ break;
+ case SID_FORMAT_TABLE_DLG:
+ onFormatTable( rReq );
+ break;
+
+ case SID_FRAME_LINESTYLE:
+ case SID_FRAME_LINECOLOR:
+ case SID_ATTR_BORDER:
+ {
+ const SfxItemSet* pArgs = rReq.GetArgs();
+ if( pArgs )
+ ApplyBorderAttr( *pArgs );
+ }
+ break;
+
+ case SID_ATTR_FILL_STYLE:
+ {
+ const SfxItemSet* pArgs = rReq.GetArgs();
+ if( pArgs )
+ SetAttributes( *pArgs, false );
+ }
+ break;
+
+ case SID_TABLE_MERGE_CELLS:
+ MergeMarkedCells();
+ break;
+
+ case SID_TABLE_SPLIT_CELLS:
+ SplitMarkedCells(rReq);
+ break;
+
+ case SID_TABLE_MINIMAL_COLUMN_WIDTH:
+ DistributeColumns(/*bOptimize=*/true, /*bMinimize=*/true);
+ break;
+
+ case SID_TABLE_OPTIMAL_COLUMN_WIDTH:
+ DistributeColumns(/*bOptimize=*/true, /*bMinimize=*/false);
+ break;
+
+ case SID_TABLE_DISTRIBUTE_COLUMNS:
+ DistributeColumns(/*bOptimize=*/false, /*bMinimize=*/false);
+ break;
+
+ case SID_TABLE_MINIMAL_ROW_HEIGHT:
+ DistributeRows(/*bOptimize=*/true, /*bMinimize=*/true);
+ break;
+
+ case SID_TABLE_OPTIMAL_ROW_HEIGHT:
+ DistributeRows(/*bOptimize=*/true, /*bMinimize=*/false);
+ break;
+
+ case SID_TABLE_DISTRIBUTE_ROWS:
+ DistributeRows(/*bOptimize=*/false, /*bMinimize=*/false);
+ break;
+
+ case SID_TABLE_VERT_BOTTOM:
+ case SID_TABLE_VERT_CENTER:
+ case SID_TABLE_VERT_NONE:
+ SetVertical( nSId );
+ break;
+
+ case SID_AUTOFORMAT:
+ case SID_TABLE_SORT_DIALOG:
+ case SID_TABLE_AUTOSUM:
+ default:
+ break;
+
+ case SID_TABLE_STYLE:
+ SetTableStyle( rReq.GetArgs() );
+ break;
+
+ case SID_TABLE_STYLE_SETTINGS:
+ SetTableStyleSettings( rReq.GetArgs() );
+ break;
+ case SID_TABLE_CHANGE_CURRENT_BORDER_POSITION:
+ changeTableEdge(rReq);
+ break;
+ }
+}
+
+void SvxTableController::SetTableStyle( const SfxItemSet* pArgs )
+{
+ if(!checkTableObject())
+ return;
+
+ SdrTableObj& rTableObj(*mxTableObj);
+ SdrModel& rModel(rTableObj.getSdrModelFromSdrObject());
+
+ if(!pArgs || (SfxItemState::SET != pArgs->GetItemState(SID_TABLE_STYLE, false)))
+ return;
+
+ const SfxStringItem* pArg = dynamic_cast< const SfxStringItem* >( &pArgs->Get( SID_TABLE_STYLE ) );
+ if( !(pArg && mxTable.is()) )
+ return;
+
+ try
+ {
+ Reference< XStyleFamiliesSupplier > xSFS( rModel.getUnoModel(), UNO_QUERY_THROW );
+ Reference< XNameAccess > xFamilyNameAccess( xSFS->getStyleFamilies(), UNO_SET_THROW );
+ Reference< XNameAccess > xTableFamilyAccess( xFamilyNameAccess->getByName( "table" ), UNO_QUERY_THROW );
+
+ if( xTableFamilyAccess->hasByName( pArg->GetValue() ) )
+ {
+ // found table style with the same name
+ Reference< XIndexAccess > xNewTableStyle( xTableFamilyAccess->getByName( pArg->GetValue() ), UNO_QUERY_THROW );
+
+ const bool bUndo = rModel.IsUndoEnabled();
+
+ if( bUndo )
+ {
+ rModel.BegUndo(SvxResId(STR_TABLE_STYLE));
+ rModel.AddUndo(std::make_unique<TableStyleUndo>(rTableObj));
+ }
+
+ rTableObj.setTableStyle( xNewTableStyle );
+
+ const sal_Int32 nRowCount = mxTable->getRowCount();
+ const sal_Int32 nColCount = mxTable->getColumnCount();
+ for( sal_Int32 nRow = 0; nRow < nRowCount; nRow++ )
+ {
+ for( sal_Int32 nCol = 0; nCol < nColCount; nCol++ ) try
+ {
+ CellRef xCell( dynamic_cast< Cell* >( mxTable->getCellByPosition( nCol, nRow ).get() ) );
+ if( xCell.is() )
+ {
+ SfxItemSet aSet( xCell->GetItemSet() );
+ bool bChanges = false;
+ SfxStyleSheet *pStyleSheet = xCell->GetStyleSheet();
+ SAL_WARN_IF(!pStyleSheet, "svx", "no stylesheet for table cell?");
+ if (pStyleSheet)
+ {
+ const SfxItemSet& rStyleAttribs = pStyleSheet->GetItemSet();
+
+ for ( sal_uInt16 nWhich = SDRATTR_START; nWhich <= SDRATTR_TABLE_LAST; nWhich++ )
+ {
+ if( (rStyleAttribs.GetItemState( nWhich ) == SfxItemState::SET) && (aSet.GetItemState( nWhich ) == SfxItemState::SET) )
+ {
+ aSet.ClearItem( nWhich );
+ bChanges = true;
+ }
+ }
+ }
+
+ if( bChanges )
+ {
+ if( bUndo )
+ xCell->AddUndo();
+
+ xCell->SetMergedItemSetAndBroadcast( aSet, true );
+ }
+ }
+ }
+ catch( Exception& )
+ {
+ TOOLS_WARN_EXCEPTION("svx.table", "");
+ }
+ }
+
+ if( bUndo )
+ rModel.EndUndo();
+ }
+ }
+ catch( Exception& )
+ {
+ TOOLS_WARN_EXCEPTION("svx.table", "");
+ }
+}
+
+void SvxTableController::SetTableStyleSettings( const SfxItemSet* pArgs )
+{
+ if(!checkTableObject())
+ return;
+
+ SdrTableObj& rTableObj(*mxTableObj);
+ SdrModel& rModel(rTableObj.getSdrModelFromSdrObject());
+
+ TableStyleSettings aSettings(rTableObj.getTableStyleSettings() );
+ const SfxBoolItem *pPoolItem=nullptr;
+
+ if( (pPoolItem = pArgs->GetItemIfSet(ID_VAL_USEFIRSTROWSTYLE, false)) )
+ aSettings.mbUseFirstRow = pPoolItem->GetValue();
+
+ if( (pPoolItem = pArgs->GetItemIfSet(ID_VAL_USELASTROWSTYLE, false)) )
+ aSettings.mbUseLastRow = pPoolItem->GetValue();
+
+ if( (pPoolItem = pArgs->GetItemIfSet(ID_VAL_USEBANDINGROWSTYLE, false)) )
+ aSettings.mbUseRowBanding = pPoolItem->GetValue();
+
+ if( (pPoolItem = pArgs->GetItemIfSet(ID_VAL_USEFIRSTCOLUMNSTYLE, false)) )
+ aSettings.mbUseFirstColumn = pPoolItem->GetValue();
+
+ if( (pPoolItem = pArgs->GetItemIfSet(ID_VAL_USELASTCOLUMNSTYLE, false)) )
+ aSettings.mbUseLastColumn = pPoolItem->GetValue();
+
+ if( (pPoolItem = pArgs->GetItemIfSet(ID_VAL_USEBANDINGCOLUMNSTYLE, false)) )
+ aSettings.mbUseColumnBanding = pPoolItem->GetValue();
+
+ if( aSettings == rTableObj.getTableStyleSettings() )
+ return;
+
+ const bool bUndo(rModel.IsUndoEnabled());
+
+ if( bUndo )
+ {
+ rModel.BegUndo( SvxResId(STR_TABLE_STYLE_SETTINGS) );
+ rModel.AddUndo(std::make_unique<TableStyleUndo>(rTableObj));
+ }
+
+ rTableObj.setTableStyleSettings( aSettings );
+
+ if( bUndo )
+ rModel.EndUndo();
+}
+
+void SvxTableController::SetVertical( sal_uInt16 nSId )
+{
+ if(!checkTableObject())
+ return;
+
+ SdrTableObj& rTableObj(*mxTableObj);
+ SdrModel& rModel(rTableObj.getSdrModelFromSdrObject());
+
+ TableModelNotifyGuard aGuard( mxTable.get() );
+ const bool bUndo(rModel.IsUndoEnabled());
+
+ if (bUndo)
+ {
+ rModel.BegUndo(SvxResId(STR_TABLE_NUMFORMAT));
+ rModel.AddUndo(rModel.GetSdrUndoFactory().CreateUndoAttrObject(rTableObj));
+ }
+
+ CellPos aStart, aEnd;
+ getSelectedCells( aStart, aEnd );
+
+ SdrTextVertAdjust eAdj = SDRTEXTVERTADJUST_TOP;
+
+ switch( nSId )
+ {
+ case SID_TABLE_VERT_BOTTOM:
+ eAdj = SDRTEXTVERTADJUST_BOTTOM;
+ break;
+ case SID_TABLE_VERT_CENTER:
+ eAdj = SDRTEXTVERTADJUST_CENTER;
+ break;
+ //case SID_TABLE_VERT_NONE:
+ default:
+ break;
+ }
+
+ SdrTextVertAdjustItem aItem( eAdj );
+
+ for( sal_Int32 nRow = aStart.mnRow; nRow <= aEnd.mnRow; nRow++ )
+ {
+ for( sal_Int32 nCol = aStart.mnCol; nCol <= aEnd.mnCol; nCol++ )
+ {
+ CellRef xCell( dynamic_cast< Cell* >( mxTable->getCellByPosition( nCol, nRow ).get() ) );
+ if( xCell.is() )
+ {
+ if (bUndo)
+ xCell->AddUndo();
+ SfxItemSet aSet(xCell->GetItemSet());
+ aSet.Put(aItem);
+ xCell->SetMergedItemSetAndBroadcast(aSet, /*bClearAllItems=*/false);
+ }
+ }
+ }
+
+ UpdateTableShape();
+
+ if (bUndo)
+ rModel.EndUndo();
+}
+
+void SvxTableController::MergeMarkedCells()
+{
+ CellPos aStart, aEnd;
+ getSelectedCells( aStart, aEnd );
+ SdrTableObj* pTableObj = mxTableObj.get();
+ if( pTableObj )
+ {
+ if( pTableObj->IsTextEditActive() )
+ mrView.SdrEndTextEdit(true);
+
+ TableModelNotifyGuard aGuard( mxTable.get() );
+ MergeRange( aStart.mnCol, aStart.mnRow, aEnd.mnCol, aEnd.mnRow );
+ }
+}
+
+void SvxTableController::SplitMarkedCells(const SfxRequest& rReq)
+{
+ if(!checkTableObject() || !mxTable.is())
+ return;
+
+ SvxAbstractDialogFactory* pFact = SvxAbstractDialogFactory::Create();
+ VclPtr<SvxAbstractSplitTableDialog> xDlg(pFact->CreateSvxSplitTableDialog(rReq.GetFrameWeld(), false, 99));
+
+ xDlg->StartExecuteAsync([xDlg, this](int) {
+ const sal_Int32 nCount = xDlg->GetCount() - 1;
+
+ if( nCount < 1 )
+ return;
+
+ CellPos aStart, aEnd;
+ getSelectedCells( aStart, aEnd );
+ Reference< XMergeableCellRange > xRange( mxTable->createCursorByRange( mxTable->getCellRangeByPosition( aStart.mnCol, aStart.mnRow, aEnd.mnCol, aEnd.mnRow ) ), UNO_QUERY_THROW );
+ const sal_Int32 nRowCount = mxTable->getRowCount();
+ const sal_Int32 nColCount = mxTable->getColumnCount();
+ SdrTableObj& rTableObj(*mxTableObj);
+
+ if( rTableObj.IsTextEditActive() )
+ mrView.SdrEndTextEdit(true);
+
+ TableModelNotifyGuard aGuard( mxTable.get() );
+ SdrModel& rModel(rTableObj.getSdrModelFromSdrObject());
+ const bool bUndo(rModel.IsUndoEnabled());
+
+ if( bUndo )
+ {
+ rModel.BegUndo( SvxResId(STR_TABLE_SPLIT) );
+ rModel.AddUndo(rModel.GetSdrUndoFactory().CreateUndoGeoObject(rTableObj));
+ }
+
+ if( xDlg->IsHorizontal() )
+ {
+ xRange->split( 0, nCount );
+ }
+ else
+ {
+ xRange->split( nCount, 0 );
+ }
+
+ if( bUndo )
+ rModel.EndUndo();
+
+ aEnd.mnRow += mxTable->getRowCount() - nRowCount;
+ aEnd.mnCol += mxTable->getColumnCount() - nColCount;
+
+ setSelectedCells( aStart, aEnd );
+
+ xDlg->disposeOnce();
+ });
+}
+
+void SvxTableController::DistributeColumns(const bool bOptimize, const bool bMinimize)
+{
+ if(!checkTableObject())
+ return;
+
+ SdrTableObj& rTableObj(*mxTableObj);
+ SdrModel& rModel(rTableObj.getSdrModelFromSdrObject());
+ const bool bUndo(rModel.IsUndoEnabled());
+
+ if( bUndo )
+ {
+ rModel.BegUndo( SvxResId(STR_TABLE_DISTRIBUTE_COLUMNS) );
+ rModel.AddUndo(rModel.GetSdrUndoFactory().CreateUndoGeoObject(rTableObj));
+ }
+
+ CellPos aStart, aEnd;
+ getSelectedCells( aStart, aEnd );
+ rTableObj.DistributeColumns( aStart.mnCol, aEnd.mnCol, bOptimize, bMinimize );
+
+ if( bUndo )
+ rModel.EndUndo();
+}
+
+void SvxTableController::DistributeRows(const bool bOptimize, const bool bMinimize)
+{
+ if(!checkTableObject())
+ return;
+
+ SdrTableObj& rTableObj(*mxTableObj);
+ SdrModel& rModel(rTableObj.getSdrModelFromSdrObject());
+ const bool bUndo(rModel.IsUndoEnabled());
+
+ if( bUndo )
+ {
+ rModel.BegUndo( SvxResId(STR_TABLE_DISTRIBUTE_ROWS) );
+ rModel.AddUndo(rModel.GetSdrUndoFactory().CreateUndoGeoObject(rTableObj));
+ }
+
+ CellPos aStart, aEnd;
+ getSelectedCells( aStart, aEnd );
+ rTableObj.DistributeRows( aStart.mnRow, aEnd.mnRow, bOptimize, bMinimize );
+
+ if( bUndo )
+ rModel.EndUndo();
+}
+
+bool SvxTableController::HasMarked() const
+{
+ return mbCellSelectionMode && mxTable.is();
+}
+
+bool SvxTableController::DeleteMarked()
+{
+ if(!checkTableObject() || !HasMarked())
+ return false;
+
+ SdrTableObj& rTableObj(*mxTableObj);
+ SdrModel& rModel(rTableObj.getSdrModelFromSdrObject());
+ const bool bUndo(rModel.IsUndoEnabled());
+ bool bDeleteTable = false;
+
+ if (bUndo)
+ rModel.BegUndo(SvxResId(STR_TABLE_DELETE_CELL_CONTENTS));
+
+ CellPos aStart, aEnd;
+ getSelectedCells( aStart, aEnd );
+ const sal_Int32 nRemovedColumns = aEnd.mnCol - aStart.mnCol + 1;
+ const sal_Int32 nRemovedRows = aEnd.mnRow - aStart.mnRow + 1;
+ if( nRemovedColumns == mxTable->getColumnCount() && nRemovedRows == mxTable->getRowCount())
+ {
+ bDeleteTable = true;
+ }
+ else
+ {
+ for( sal_Int32 nRow = aStart.mnRow; nRow <= aEnd.mnRow; nRow++ )
+ {
+ for( sal_Int32 nCol = aStart.mnCol; nCol <= aEnd.mnCol; nCol++ )
+ {
+ CellRef xCell( dynamic_cast< Cell* >( mxTable->getCellByPosition( nCol, nRow ).get() ) );
+ if (xCell.is() && xCell->hasText())
+ {
+ if (bUndo)
+ xCell->AddUndo();
+ xCell->SetOutlinerParaObject(std::nullopt);
+ }
+ }
+ }
+ }
+
+ if (bDeleteTable)
+ mrView.DeleteMarkedObj();
+
+ if (bUndo)
+ rModel.EndUndo();
+
+ if (!bDeleteTable)
+ UpdateTableShape();
+ return true;
+}
+
+bool SvxTableController::GetStyleSheet( SfxStyleSheet*& rpStyleSheet ) const
+{
+ if( hasSelectedCells() )
+ {
+ rpStyleSheet = nullptr;
+
+ if( mxTable.is() )
+ {
+ SfxStyleSheet* pRet=nullptr;
+ bool b1st=true;
+
+ CellPos aStart, aEnd;
+ const_cast<SvxTableController&>(*this).getSelectedCells( aStart, aEnd );
+
+ for( sal_Int32 nRow = aStart.mnRow; nRow <= aEnd.mnRow; nRow++ )
+ {
+ for( sal_Int32 nCol = aStart.mnCol; nCol <= aEnd.mnCol; nCol++ )
+ {
+ CellRef xCell( dynamic_cast< Cell* >( mxTable->getCellByPosition( nCol, nRow ).get() ) );
+ if( xCell.is() )
+ {
+ SfxStyleSheet* pSS=xCell->GetStyleSheet();
+ if(b1st)
+ {
+ pRet=pSS;
+ }
+ else if(pRet != pSS)
+ {
+ return true;
+ }
+ b1st=false;
+ }
+ }
+ }
+ rpStyleSheet = pRet;
+ return true;
+ }
+ }
+ return false;
+}
+
+bool SvxTableController::SetStyleSheet( SfxStyleSheet* pStyleSheet, bool bDontRemoveHardAttr )
+{
+ if( hasSelectedCells() && (!pStyleSheet || pStyleSheet->GetFamily() == SfxStyleFamily::Frame) )
+ {
+ if( mxTable.is() )
+ {
+ CellPos aStart, aEnd;
+ getSelectedCells( aStart, aEnd );
+
+ for( sal_Int32 nRow = aStart.mnRow; nRow <= aEnd.mnRow; nRow++ )
+ {
+ for( sal_Int32 nCol = aStart.mnCol; nCol <= aEnd.mnCol; nCol++ )
+ {
+ CellRef xCell( dynamic_cast< Cell* >( mxTable->getCellByPosition( nCol, nRow ).get() ) );
+ if( xCell.is() )
+ xCell->SetStyleSheet(pStyleSheet,bDontRemoveHardAttr);
+ }
+ }
+
+ UpdateTableShape();
+ return true;
+ }
+ }
+ return false;
+}
+
+void SvxTableController::changeTableEdge(const SfxRequest& rReq)
+{
+ if (!checkTableObject())
+ return;
+
+ const auto* pType = rReq.GetArg<SfxStringItem>(SID_TABLE_BORDER_TYPE);
+ const auto* pIndex = rReq.GetArg<SfxUInt16Item>(SID_TABLE_BORDER_INDEX);
+ const auto* pOffset = rReq.GetArg<SfxInt32Item>(SID_TABLE_BORDER_OFFSET);
+
+ if (!(pType && pIndex && pOffset))
+ return;
+
+ const OUString sType = pType->GetValue();
+ const sal_uInt16 nIndex = pIndex->GetValue();
+ const sal_Int32 nOffset = convertTwipToMm100(pOffset->GetValue());
+
+ SdrTableObj& rTableObj(*mxTableObj);
+
+ sal_Int32 nEdgeIndex = -1;
+ bool bHorizontal = sType.startsWith("row");
+
+ if (sType == "column-left" || sType == "row-left")
+ {
+ nEdgeIndex = 0;
+ }
+ else if (sType == "column-right")
+ {
+ // Number of edges = number of columns + 1
+ nEdgeIndex = rTableObj.getColumnCount();
+ }
+ else if (sType == "row-right")
+ {
+ // Number of edges = number of rows + 1
+ nEdgeIndex = rTableObj.getRowCount();
+ }
+ else if (sType == "column-middle" || sType == "row-middle")
+ {
+ nEdgeIndex = nIndex + 1;
+ }
+
+ if (nEdgeIndex < 0)
+ return;
+
+ TableModelNotifyGuard aGuard(mxTable.get());
+ SdrModel& rModel(rTableObj.getSdrModelFromSdrObject());
+ const bool bUndo(rModel.IsUndoEnabled());
+ if (bUndo)
+ {
+ auto pUndoObject = rModel.GetSdrUndoFactory().CreateUndoGeoObject(rTableObj);
+ rModel.BegUndo(pUndoObject->GetComment());
+
+ auto* pGeoUndo = static_cast<SdrUndoGeoObj*>(pUndoObject.get());
+ if (pGeoUndo)
+ pGeoUndo->SetSkipChangeLayout(true);
+
+ rModel.AddUndo(std::move(pUndoObject));
+ }
+ tools::Rectangle aBoundRect;
+ if (rTableObj.GetUserCall())
+ aBoundRect = rTableObj.GetLastBoundRect();
+ rTableObj.changeEdge(bHorizontal, nEdgeIndex, nOffset);
+ rTableObj.SetChanged();
+ rTableObj.SendUserCall(SdrUserCallType::Resize, aBoundRect);
+ if (bUndo)
+ rModel.EndUndo();
+}
+
+// internals
+
+
+bool SvxTableController::checkTableObject()
+{
+ return mxTableObj.is();
+}
+
+
+SvxTableController::TblAction SvxTableController::getKeyboardAction(const KeyEvent& rKEvt)
+{
+ const bool bMod1 = rKEvt.GetKeyCode().IsMod1(); // ctrl
+ const bool bMod2 = rKEvt.GetKeyCode().IsMod2(); // Alt
+ const bool bTextEdit = mrView.IsTextEdit();
+
+ TblAction nAction = TblAction::HandledByView;
+
+ sdr::table::SdrTableObj* pTableObj = mxTableObj.get();
+ if( !pTableObj )
+ return nAction;
+
+ // handle special keys
+ const sal_Int16 nCode = rKEvt.GetKeyCode().GetCode();
+ switch( nCode )
+ {
+ case awt::Key::ESCAPE: // handle escape
+ {
+ if( bTextEdit )
+ {
+ // escape during text edit ends text edit
+ nAction = TblAction::StopTextEdit;
+ }
+ if( mbCellSelectionMode )
+ {
+ // escape with selected cells removes selection
+ nAction = TblAction::RemoveSelection;
+ }
+ break;
+ }
+ case awt::Key::RETURN: // handle return
+ {
+ if( !bMod1 && !bMod2 && !bTextEdit )
+ {
+ // when not already editing, return starts text edit
+ setSelectionStart( SdrTableObj::getFirstCell() );
+ nAction = TblAction::EditCell;
+ }
+ break;
+ }
+ case awt::Key::F2: // f2 toggles text edit
+ {
+ if( bMod1 || bMod2 ) // f2 with modifiers is handled by the view
+ {
+ }
+ else if( bTextEdit )
+ {
+ // f2 during text edit stops text edit
+ nAction = TblAction::StopTextEdit;
+ }
+ else if( mbCellSelectionMode )
+ {
+ // f2 with selected cells removes selection
+ nAction = TblAction::RemoveSelection;
+ }
+ else
+ {
+ // f2 with no selection and no text edit starts text edit
+ setSelectionStart( SdrTableObj::getFirstCell() );
+ nAction = TblAction::EditCell;
+ }
+ break;
+ }
+ case awt::Key::HOME:
+ case awt::Key::NUM7:
+ {
+ if( (bMod1 || bMod2) && (bTextEdit || mbCellSelectionMode) )
+ {
+ if( bMod1 && !bMod2 )
+ {
+ // ctrl + home jumps to first cell
+ nAction = TblAction::GotoFirstCell;
+ }
+ else if( !bMod1 && bMod2 )
+ {
+ // alt + home jumps to first column
+ nAction = TblAction::GotoFirstColumn;
+ }
+ }
+ break;
+ }
+ case awt::Key::END:
+ case awt::Key::NUM1:
+ {
+ if( (bMod1 || bMod2) && (bTextEdit || mbCellSelectionMode) )
+ {
+ if( bMod1 && !bMod2 )
+ {
+ // ctrl + end jumps to last cell
+ nAction = TblAction::GotoLastCell;
+ }
+ else if( !bMod1 && bMod2 )
+ {
+ // alt + home jumps to last column
+ nAction = TblAction::GotoLastColumn;
+ }
+ }
+ break;
+ }
+
+ case awt::Key::TAB:
+ {
+ if( bTextEdit || mbCellSelectionMode )
+ nAction = TblAction::Tab;
+ break;
+ }
+
+ case awt::Key::UP:
+ case awt::Key::NUM8:
+ case awt::Key::DOWN:
+ case awt::Key::NUM2:
+ case awt::Key::LEFT:
+ case awt::Key::NUM4:
+ case awt::Key::RIGHT:
+ case awt::Key::NUM6:
+ {
+
+ if( !bMod1 && bMod2 )
+ {
+ if(bTextEdit || mbCellSelectionMode)
+ {
+ if( (nCode == awt::Key::UP) || (nCode == awt::Key::NUM8) )
+ {
+ nAction = TblAction::GotoLeftCell;
+ break;
+ }
+ else if( (nCode == awt::Key::DOWN) || (nCode == awt::Key::NUM2) )
+ {
+ nAction = TblAction::GotoRightCell;
+ break;
+ }
+ }
+ }
+
+ bool bTextMove = false;
+ OutlinerView* pOLV = mrView.GetTextEditOutlinerView();
+ if( pOLV )
+ {
+ RemoveSelection();
+ // during text edit, check if we navigate out of the cell
+ ESelection aOldSelection = pOLV->GetSelection();
+ pOLV->PostKeyEvent(rKEvt);
+ bTextMove = aOldSelection == pOLV->GetSelection();
+ if( !bTextMove )
+ {
+ nAction = TblAction::NONE;
+ }
+ }
+
+ if( mbCellSelectionMode || bTextMove )
+ {
+ // no text edit, navigate in cells if selection active
+ switch( nCode )
+ {
+ case awt::Key::LEFT:
+ case awt::Key::NUM4:
+ nAction = TblAction::GotoLeftCell;
+ break;
+ case awt::Key::RIGHT:
+ case awt::Key::NUM6:
+ nAction = TblAction::GotoRightCell;
+ break;
+ case awt::Key::DOWN:
+ case awt::Key::NUM2:
+ nAction = TblAction::GotoDownCell;
+ break;
+ case awt::Key::UP:
+ case awt::Key::NUM8:
+ nAction = TblAction::GotoUpCell;
+ break;
+ }
+ }
+ break;
+ }
+ case awt::Key::PAGEUP:
+ if( bMod2 )
+ nAction = TblAction::GotoFirstRow;
+ break;
+
+ case awt::Key::PAGEDOWN:
+ if( bMod2 )
+ nAction = TblAction::GotoLastRow;
+ break;
+ }
+ return nAction;
+}
+
+bool SvxTableController::executeAction(TblAction nAction, bool bSelect, vcl::Window* pWindow)
+{
+ sdr::table::SdrTableObj* pTableObj = mxTableObj.get();
+ if( !pTableObj )
+ return false;
+
+ switch( nAction )
+ {
+ case TblAction::GotoFirstCell:
+ {
+ gotoCell( SdrTableObj::getFirstCell(), bSelect, pWindow, nAction );
+ break;
+ }
+
+ case TblAction::GotoLeftCell:
+ {
+ gotoCell( pTableObj->getLeftCell( getSelectionEnd(), !bSelect ), bSelect, pWindow, nAction );
+ break;
+ }
+
+ case TblAction::GotoRightCell:
+ {
+ gotoCell( pTableObj->getRightCell( getSelectionEnd(), !bSelect ), bSelect, pWindow, nAction);
+ break;
+ }
+
+ case TblAction::GotoLastCell:
+ {
+ gotoCell( pTableObj->getLastCell(), bSelect, pWindow, nAction );
+ break;
+ }
+
+ case TblAction::GotoFirstColumn:
+ {
+ CellPos aPos( SdrTableObj::getFirstCell().mnCol, getSelectionEnd().mnRow );
+ gotoCell( aPos, bSelect, pWindow, nAction );
+ break;
+ }
+
+ case TblAction::GotoLastColumn:
+ {
+ CellPos aPos( pTableObj->getLastCell().mnCol, getSelectionEnd().mnRow );
+ gotoCell( aPos, bSelect, pWindow, nAction );
+ break;
+ }
+
+ case TblAction::GotoFirstRow:
+ {
+ CellPos aPos( getSelectionEnd().mnCol, SdrTableObj::getFirstCell().mnRow );
+ gotoCell( aPos, bSelect, pWindow, nAction );
+ break;
+ }
+
+ case TblAction::GotoUpCell:
+ {
+ gotoCell( pTableObj->getUpCell(getSelectionEnd(), !bSelect), bSelect, pWindow, nAction );
+ break;
+ }
+
+ case TblAction::GotoDownCell:
+ {
+ gotoCell( pTableObj->getDownCell(getSelectionEnd(), !bSelect), bSelect, pWindow, nAction );
+ break;
+ }
+
+ case TblAction::GotoLastRow:
+ {
+ CellPos aPos( getSelectionEnd().mnCol, pTableObj->getLastCell().mnRow );
+ gotoCell( aPos, bSelect, pWindow, nAction );
+ break;
+ }
+
+ case TblAction::EditCell:
+ EditCell( getSelectionStart(), pWindow, nAction );
+ break;
+
+ case TblAction::StopTextEdit:
+ StopTextEdit();
+ break;
+
+ case TblAction::RemoveSelection:
+ RemoveSelection();
+ break;
+
+ case TblAction::Tab:
+ {
+ if( bSelect )
+ gotoCell( pTableObj->getPreviousCell( getSelectionEnd(), true ), false, pWindow, nAction );
+ else
+ {
+ CellPos aSelectionEnd( getSelectionEnd() );
+ CellPos aNextCell( pTableObj->getNextCell( aSelectionEnd, true ) );
+ if( aSelectionEnd == aNextCell )
+ {
+ onInsert( SID_TABLE_INSERT_ROW );
+ aNextCell = pTableObj->getNextCell( aSelectionEnd, true );
+ }
+ gotoCell( aNextCell, false, pWindow, nAction );
+ }
+ break;
+ }
+ default:
+ break;
+ }
+
+ return nAction != TblAction::HandledByView;
+}
+
+
+void SvxTableController::gotoCell(const CellPos& rPos, bool bSelect, vcl::Window* pWindow, TblAction nAction /*= TblAction::NONE */)
+{
+ if( mxTableObj.is() && mxTableObj->IsTextEditActive() )
+ mrView.SdrEndTextEdit(true);
+
+ if( bSelect )
+ {
+ maCursorLastPos = rPos;
+ if( mxTableObj.is() )
+ mxTableObj->setActiveCell( rPos );
+
+ if( !mbCellSelectionMode )
+ {
+ setSelectedCells( maCursorFirstPos, rPos );
+ }
+ else
+ {
+ UpdateSelection( rPos );
+ }
+ }
+ else
+ {
+ RemoveSelection();
+ EditCell( rPos, pWindow, nAction );
+ }
+}
+
+
+const CellPos& SvxTableController::getSelectionStart()
+{
+ checkCell( maCursorFirstPos );
+ return maCursorFirstPos;
+}
+
+
+void SvxTableController::setSelectionStart( const CellPos& rPos )
+{
+ maCursorFirstPos = rPos;
+}
+
+
+const CellPos& SvxTableController::getSelectionEnd()
+{
+ checkCell( maCursorLastPos );
+ return maCursorLastPos;
+}
+
+
+void SvxTableController::MergeRange( sal_Int32 nFirstCol, sal_Int32 nFirstRow, sal_Int32 nLastCol, sal_Int32 nLastRow )
+{
+ if(!checkTableObject() || !mxTable.is())
+ return;
+
+ try
+ {
+ Reference< XMergeableCellRange > xRange( mxTable->createCursorByRange( mxTable->getCellRangeByPosition( nFirstCol, nFirstRow,nLastCol, nLastRow ) ), UNO_QUERY_THROW );
+
+ if( xRange->isMergeable() )
+ {
+ SdrTableObj& rTableObj(*mxTableObj);
+ SdrModel& rModel(rTableObj.getSdrModelFromSdrObject());
+ const bool bUndo(rModel.IsUndoEnabled());
+
+ if( bUndo )
+ {
+ rModel.BegUndo( SvxResId(STR_TABLE_MERGE) );
+ rModel.AddUndo(rModel.GetSdrUndoFactory().CreateUndoGeoObject(rTableObj));
+ }
+
+ xRange->merge();
+ mbHasJustMerged = true;
+ setSelectedCells( maCursorFirstPos, maCursorFirstPos );
+
+ if( bUndo )
+ rModel.EndUndo();
+ }
+ }
+ catch( Exception& )
+ {
+ TOOLS_WARN_EXCEPTION( "svx.table", "" );
+ }
+}
+
+
+void SvxTableController::checkCell( CellPos& rPos ) const
+{
+ if( !mxTable.is() )
+ return;
+
+ try
+ {
+ if( rPos.mnCol >= mxTable->getColumnCount() )
+ rPos.mnCol = mxTable->getColumnCount()-1;
+
+ if( rPos.mnRow >= mxTable->getRowCount() )
+ rPos.mnRow = mxTable->getRowCount()-1;
+ }
+ catch( Exception& )
+ {
+ TOOLS_WARN_EXCEPTION("svx.table", "");
+ }
+}
+
+
+void SvxTableController::findMergeOrigin( CellPos& rPos )
+{
+ if( !mxTable.is() )
+ return;
+
+ try
+ {
+ Reference< XMergeableCell > xCell( mxTable->getCellByPosition( rPos.mnCol, rPos.mnRow ), UNO_QUERY_THROW );
+ if( xCell->isMerged() )
+ {
+ ::findMergeOrigin( mxTable, rPos.mnCol, rPos.mnRow, rPos.mnCol, rPos.mnRow );
+ }
+ }
+ catch( Exception& )
+ {
+ TOOLS_WARN_EXCEPTION("svx.table", "");
+ }
+}
+
+
+void SvxTableController::EditCell(const CellPos& rPos, vcl::Window* pWindow, TblAction nAction /*= TblAction::NONE */)
+{
+ SdrPageView* pPV(mrView.GetSdrPageView());
+
+ if(nullptr == pPV || !checkTableObject())
+ return;
+
+ SdrTableObj& rTableObj(*mxTableObj);
+
+ if(rTableObj.getSdrPageFromSdrObject() != pPV->GetPage())
+ return;
+
+ bool bEmptyOutliner = false;
+
+ if(!rTableObj.GetOutlinerParaObject() && mrView.GetTextEditOutliner())
+ {
+ ::Outliner* pOutl = mrView.GetTextEditOutliner();
+ sal_Int32 nParaCnt = pOutl->GetParagraphCount();
+ Paragraph* p1stPara = pOutl->GetParagraph( 0 );
+
+ if(nParaCnt==1 && p1stPara)
+ {
+ // with only one paragraph
+ if (pOutl->GetText(p1stPara).isEmpty())
+ {
+ bEmptyOutliner = true;
+ }
+ }
+ }
+
+ CellPos aPos( rPos );
+ findMergeOrigin( aPos );
+
+ if( &rTableObj == mrView.GetTextEditObject() && !bEmptyOutliner && rTableObj.IsTextEditActive( aPos ) )
+ return;
+
+ if( rTableObj.IsTextEditActive() )
+ mrView.SdrEndTextEdit(true);
+
+ rTableObj.setActiveCell( aPos );
+
+ // create new outliner, owner will be the SdrObjEditView
+ SdrModel& rModel(rTableObj.getSdrModelFromSdrObject());
+ std::unique_ptr<SdrOutliner> pOutl(SdrMakeOutliner(OutlinerMode::OutlineObject, rModel));
+
+ if (pOutl && rTableObj.IsVerticalWriting())
+ pOutl->SetVertical( true );
+
+ if (!mrView.SdrBeginTextEdit(&rTableObj, pPV, pWindow, true, pOutl.release()))
+ return;
+
+ maCursorLastPos = maCursorFirstPos = rPos;
+
+ OutlinerView* pOLV = mrView.GetTextEditOutlinerView();
+
+ // Move cursor to end of text
+ ESelection aNewSelection;
+
+ const WritingMode eMode = rTableObj.GetWritingMode();
+ if (((nAction == TblAction::GotoLeftCell) || (nAction == TblAction::GotoRightCell)) && (eMode != WritingMode_TB_RL))
+ {
+ const bool bLast = ((nAction == TblAction::GotoLeftCell) && (eMode == WritingMode_LR_TB)) ||
+ ((nAction == TblAction::GotoRightCell) && (eMode == WritingMode_RL_TB));
+
+ if( bLast )
+ aNewSelection = ESelection(EE_PARA_NOT_FOUND, EE_INDEX_NOT_FOUND, EE_PARA_NOT_FOUND, EE_INDEX_NOT_FOUND);
+ }
+ pOLV->SetSelection(aNewSelection);
+}
+
+
+void SvxTableController::StopTextEdit()
+{
+ if(mrView.IsTextEdit())
+ {
+ mrView.SdrEndTextEdit();
+ mrView.SetCurrentObj(SdrObjKind::Table);
+ mrView.SetEditMode(SdrViewEditMode::Edit);
+ }
+}
+
+
+void SvxTableController::getSelectedCells( CellPos& rFirst, CellPos& rLast )
+{
+ if( mbCellSelectionMode )
+ {
+ checkCell( maCursorFirstPos );
+ checkCell( maCursorLastPos );
+
+ rFirst.mnCol = std::min( maCursorFirstPos.mnCol, maCursorLastPos.mnCol );
+ rFirst.mnRow = std::min( maCursorFirstPos.mnRow, maCursorLastPos.mnRow );
+ rLast.mnCol = std::max( maCursorFirstPos.mnCol, maCursorLastPos.mnCol );
+ rLast.mnRow = std::max( maCursorFirstPos.mnRow, maCursorLastPos.mnRow );
+
+ if( !mxTable.is() )
+ return;
+
+ bool bExt = false;
+ do
+ {
+ bExt = false;
+ for( sal_Int32 nRow = rFirst.mnRow; nRow <= rLast.mnRow && !bExt; nRow++ )
+ {
+ for( sal_Int32 nCol = rFirst.mnCol; nCol <= rLast.mnCol && !bExt; nCol++ )
+ {
+ Reference< XMergeableCell > xCell( mxTable->getCellByPosition( nCol, nRow ), UNO_QUERY );
+ if( !xCell.is() )
+ continue;
+
+ if( xCell->isMerged() )
+ {
+ CellPos aPos( nCol, nRow );
+ findMergeOrigin( aPos );
+ if( (aPos.mnCol < rFirst.mnCol) || (aPos.mnRow < rFirst.mnRow) )
+ {
+ rFirst.mnCol = std::min( rFirst.mnCol, aPos.mnCol );
+ rFirst.mnRow = std::min( rFirst.mnRow, aPos.mnRow );
+ bExt = true;
+ }
+ }
+ else
+ {
+ if( ((nCol + xCell->getColumnSpan() - 1) > rLast.mnCol) || (nRow + xCell->getRowSpan() - 1 ) > rLast.mnRow )
+ {
+ rLast.mnCol = std::max( rLast.mnCol, nCol + xCell->getColumnSpan() - 1 );
+ rLast.mnRow = std::max( rLast.mnRow, nRow + xCell->getRowSpan() - 1 );
+ bExt = true;
+ }
+ }
+ }
+ }
+ }
+ while(bExt);
+ }
+ else if(mrView.IsTextEdit())
+ {
+ rFirst = getSelectionStart();
+ findMergeOrigin( rFirst );
+ rLast = rFirst;
+
+ if( mxTable.is() )
+ {
+ Reference< XMergeableCell > xCell( mxTable->getCellByPosition( rLast.mnCol, rLast.mnRow ), UNO_QUERY );
+ if( xCell.is() )
+ {
+ rLast.mnCol += xCell->getColumnSpan() - 1;
+ rLast.mnRow += xCell->getRowSpan() - 1;
+ }
+ }
+ }
+ else
+ {
+ rFirst.mnCol = 0;
+ rFirst.mnRow = 0;
+ if( mxTable.is() )
+ {
+ rLast.mnRow = mxTable->getRowCount()-1;
+ rLast.mnCol = mxTable->getColumnCount()-1;
+ }
+ else
+ {
+ rLast.mnRow = 0;
+ rLast.mnCol = 0;
+ }
+ }
+}
+
+
+void SvxTableController::StartSelection( const CellPos& rPos )
+{
+ StopTextEdit();
+ mbCellSelectionMode = true;
+ maCursorLastPos = maCursorFirstPos = rPos;
+ mrView.MarkListHasChanged();
+}
+
+
+void SvxTableController::setSelectedCells( const CellPos& rStart, const CellPos& rEnd )
+{
+ StopTextEdit();
+ mbCellSelectionMode = true;
+ maCursorFirstPos = rStart;
+ UpdateSelection( rEnd );
+}
+
+
+bool SvxTableController::ChangeFontSize(bool bGrow, const FontList* pFontList)
+{
+ if(!checkTableObject() || !mxTable.is())
+ return false;
+
+ SdrTableObj& rTableObj(*mxTableObj);
+ SdrModel& rModel(rTableObj.getSdrModelFromSdrObject());
+
+ if (mrView.IsTextEdit())
+ return true;
+
+ CellPos aStart, aEnd;
+
+ if(hasSelectedCells())
+ {
+ getSelectedCells(aStart, aEnd);
+ }
+ else
+ {
+ aStart.mnRow = 0;
+ aStart.mnCol = 0;
+ aEnd.mnRow = mxTable->getRowCount() - 1;
+ aEnd.mnCol = mxTable->getColumnCount() - 1;
+ }
+
+ for (sal_Int32 nRow = aStart.mnRow; nRow <= aEnd.mnRow; nRow++)
+ {
+ for (sal_Int32 nCol = aStart.mnCol; nCol <= aEnd.mnCol; nCol++)
+ {
+ CellRef xCell(dynamic_cast< Cell* >(mxTable->getCellByPosition(nCol, nRow).get()));
+ if (xCell.is())
+ {
+ if (rModel.IsUndoEnabled())
+ xCell->AddUndo();
+
+ SfxItemSet aCellSet(xCell->GetItemSet());
+ if (EditView::ChangeFontSize(bGrow, aCellSet, pFontList))
+ {
+ xCell->SetMergedItemSetAndBroadcast(aCellSet, false);
+ }
+ }
+ }
+ }
+
+ UpdateTableShape();
+
+ return true;
+}
+
+
+void SvxTableController::UpdateSelection( const CellPos& rPos )
+{
+ maCursorLastPos = rPos;
+ mrView.MarkListHasChanged();
+}
+
+
+void SvxTableController::clearSelection()
+{
+ RemoveSelection();
+}
+
+
+void SvxTableController::selectAll()
+{
+ if( mxTable.is() )
+ {
+ CellPos aPos2( mxTable->getColumnCount()-1, mxTable->getRowCount()-1 );
+ if( (aPos2.mnCol >= 0) && (aPos2.mnRow >= 0) )
+ {
+ CellPos aPos1;
+ setSelectedCells( aPos1, aPos2 );
+ }
+ }
+}
+
+
+void SvxTableController::RemoveSelection()
+{
+ if( mbCellSelectionMode )
+ {
+ mbCellSelectionMode = false;
+ mrView.MarkListHasChanged();
+ }
+}
+
+
+void SvxTableController::onTableModified()
+{
+ if( mnUpdateEvent == nullptr )
+ mnUpdateEvent = Application::PostUserEvent( LINK( this, SvxTableController, UpdateHdl ) );
+}
+
+
+void SvxTableController::updateSelectionOverlay()
+{
+ // There is no need to update selection overlay after merging cells
+ // since the selection overlay should remain the same
+ if ( mbHasJustMerged )
+ return;
+
+ destroySelectionOverlay();
+ if( !mbCellSelectionMode )
+ return;
+
+ sdr::table::SdrTableObj* pTableObj = mxTableObj.get();
+ if( !pTableObj )
+ return;
+
+ sdr::overlay::OverlayObjectCell::RangeVector aRanges;
+
+ tools::Rectangle aStartRect, aEndRect;
+ CellPos aStart,aEnd;
+ getSelectedCells( aStart, aEnd );
+ pTableObj->getCellBounds( aStart, aStartRect );
+
+ basegfx::B2DRange a2DRange( basegfx::B2DPoint(aStartRect.Left(), aStartRect.Top()) );
+ a2DRange.expand( basegfx::B2DPoint(aStartRect.Right(), aStartRect.Bottom()) );
+
+ findMergeOrigin( aEnd );
+ pTableObj->getCellBounds( aEnd, aEndRect );
+ a2DRange.expand( basegfx::B2DPoint(aEndRect.Left(), aEndRect.Top()) );
+ a2DRange.expand( basegfx::B2DPoint(aEndRect.Right(), aEndRect.Bottom()) );
+ aRanges.push_back( a2DRange );
+
+ ::Color aHighlight( COL_BLUE );
+ OutputDevice* pOutDev = mrView.GetFirstOutputDevice();
+ if( pOutDev )
+ aHighlight = pOutDev->GetSettings().GetStyleSettings().GetHighlightColor();
+
+ const sal_uInt32 nCount = mrView.PaintWindowCount();
+ for( sal_uInt32 nIndex = 0; nIndex < nCount; nIndex++ )
+ {
+ SdrPaintWindow* pPaintWindow = mrView.GetPaintWindow(nIndex);
+ if( pPaintWindow )
+ {
+ const rtl::Reference < sdr::overlay::OverlayManager >& xOverlayManager = pPaintWindow->GetOverlayManager();
+ if( xOverlayManager.is() )
+ {
+ std::unique_ptr<sdr::overlay::OverlayObjectCell> pOverlay(new sdr::overlay::OverlayObjectCell( aHighlight, std::vector(aRanges) ));
+
+ xOverlayManager->add(*pOverlay);
+ mpSelectionOverlay.emplace();
+ mpSelectionOverlay->append(std::move(pOverlay));
+ }
+ }
+ }
+
+ // If tiled rendering, emit callbacks for sdr table selection.
+ if (!(pOutDev && comphelper::LibreOfficeKit::isActive()))
+ return;
+
+ tools::Rectangle aSelection(a2DRange.getMinX(), a2DRange.getMinY(), a2DRange.getMaxX(), a2DRange.getMaxY());
+
+ if (pOutDev->GetMapMode().GetMapUnit() == MapUnit::Map100thMM)
+ aSelection = o3tl::toTwips(aSelection, o3tl::Length::mm100);
+
+ if(SfxViewShell* pViewShell = SfxViewShell::Current())
+ {
+ pViewShell->libreOfficeKitViewCallback(LOK_CALLBACK_CELL_SELECTION_AREA, aSelection.toString().getStr());
+ pViewShell->libreOfficeKitViewCallback(LOK_CALLBACK_TEXT_SELECTION, aSelection.toString().getStr());
+ }
+}
+
+
+void SvxTableController::destroySelectionOverlay()
+{
+ if( !mpSelectionOverlay )
+ return;
+
+ mpSelectionOverlay.reset();
+
+ if (comphelper::LibreOfficeKit::isActive())
+ {
+ // Clear the LOK text selection so far provided by this table.
+ if(SfxViewShell* pViewShell = SfxViewShell::Current())
+ {
+ pViewShell->libreOfficeKitViewCallback(LOK_CALLBACK_CELL_SELECTION_AREA, "EMPTY");
+ pViewShell->libreOfficeKitViewCallback(LOK_CALLBACK_TEXT_SELECTION_START, "EMPTY");
+ pViewShell->libreOfficeKitViewCallback(LOK_CALLBACK_TEXT_SELECTION_END, "EMPTY");
+ pViewShell->libreOfficeKitViewCallback(LOK_CALLBACK_TEXT_SELECTION, "EMPTY");
+ }
+ }
+}
+
+
+void SvxTableController::MergeAttrFromSelectedCells(SfxItemSet& rAttr, bool bOnlyHardAttr) const
+{
+ if( !mxTable.is() )
+ return;
+
+ CellPos aStart, aEnd;
+ const_cast<SvxTableController&>(*this).getSelectedCells( aStart, aEnd );
+
+ for( sal_Int32 nRow = aStart.mnRow; nRow <= aEnd.mnRow; nRow++ )
+ {
+ for( sal_Int32 nCol = aStart.mnCol; nCol <= aEnd.mnCol; nCol++ )
+ {
+ CellRef xCell( dynamic_cast< Cell* >( mxTable->getCellByPosition( nCol, nRow ).get() ) );
+ if( xCell.is() && !xCell->isMerged() )
+ {
+ const SfxItemSet& rSet = xCell->GetItemSet();
+ SfxWhichIter aIter(rSet);
+ sal_uInt16 nWhich(aIter.FirstWhich());
+ while(nWhich)
+ {
+ SfxItemState nState = aIter.GetItemState(false);
+ if(!bOnlyHardAttr)
+ {
+ if(SfxItemState::DONTCARE == nState)
+ rAttr.InvalidateItem(nWhich);
+ else
+ rAttr.MergeValue(rSet.Get(nWhich), true);
+ }
+ else if(SfxItemState::SET == nState)
+ {
+ const SfxPoolItem& rItem = rSet.Get(nWhich);
+ rAttr.MergeValue(rItem, true);
+ }
+
+ nWhich = aIter.NextWhich();
+ }
+ }
+ }
+ }
+}
+
+
+static void ImplSetLinePreserveColor( SvxBoxItem& rNewFrame, const SvxBorderLine* pNew, SvxBoxItemLine nLine )
+{
+ if( pNew )
+ {
+ const SvxBorderLine* pOld = rNewFrame.GetLine(nLine);
+ if( pOld )
+ {
+ SvxBorderLine aNewLine( *pNew );
+ aNewLine.SetColor( pOld->GetColor() );
+ rNewFrame.SetLine( &aNewLine, nLine );
+ return;
+ }
+ }
+ rNewFrame.SetLine( pNew, nLine );
+}
+
+
+static void ImplApplyBoxItem( CellPosFlag nCellPosFlags, const SvxBoxItem* pBoxItem, const SvxBoxInfoItem* pBoxInfoItem, SvxBoxItem& rNewFrame )
+{
+ if (nCellPosFlags & (CellPosFlag::Before|CellPosFlag::After|CellPosFlag::Upper|CellPosFlag::Lower))
+ {
+ // current cell is outside the selection
+
+ if (!(nCellPosFlags & (CellPosFlag::Before|CellPosFlag::After))) // check if it's not any corner
+ {
+ if (nCellPosFlags & CellPosFlag::Upper)
+ {
+ if( pBoxInfoItem->IsValid(SvxBoxInfoItemValidFlags::TOP) )
+ rNewFrame.SetLine(nullptr, SvxBoxItemLine::BOTTOM );
+ }
+ else if (nCellPosFlags & CellPosFlag::Lower)
+ {
+ if( pBoxInfoItem->IsValid(SvxBoxInfoItemValidFlags::BOTTOM) )
+ rNewFrame.SetLine( nullptr, SvxBoxItemLine::TOP );
+ }
+ }
+ else if (!(nCellPosFlags & (CellPosFlag::Upper|CellPosFlag::Lower))) // check if it's not any corner
+ {
+ if (nCellPosFlags & CellPosFlag::Before)
+ {
+ if( pBoxInfoItem->IsValid(SvxBoxInfoItemValidFlags::LEFT) )
+ rNewFrame.SetLine( nullptr, SvxBoxItemLine::RIGHT );
+ }
+ else if (nCellPosFlags & CellPosFlag::After)
+ {
+ if( pBoxInfoItem->IsValid(SvxBoxInfoItemValidFlags::RIGHT) )
+ rNewFrame.SetLine( nullptr, SvxBoxItemLine::LEFT );
+ }
+ }
+ }
+ else
+ {
+ // current cell is inside the selection
+
+ if ((nCellPosFlags & CellPosFlag::Left) ? pBoxInfoItem->IsValid(SvxBoxInfoItemValidFlags::LEFT)
+ : pBoxInfoItem->IsValid(SvxBoxInfoItemValidFlags::VERT))
+ rNewFrame.SetLine( (nCellPosFlags & CellPosFlag::Left) ? pBoxItem->GetLeft() : pBoxInfoItem->GetVert(), SvxBoxItemLine::LEFT );
+
+ if( (nCellPosFlags & CellPosFlag::Right) ? pBoxInfoItem->IsValid(SvxBoxInfoItemValidFlags::RIGHT) : pBoxInfoItem->IsValid(SvxBoxInfoItemValidFlags::VERT) )
+ rNewFrame.SetLine( (nCellPosFlags & CellPosFlag::Right) ? pBoxItem->GetRight() : pBoxInfoItem->GetVert(), SvxBoxItemLine::RIGHT );
+
+ if( (nCellPosFlags & CellPosFlag::Top) ? pBoxInfoItem->IsValid(SvxBoxInfoItemValidFlags::TOP) : pBoxInfoItem->IsValid(SvxBoxInfoItemValidFlags::HORI) )
+ rNewFrame.SetLine( (nCellPosFlags & CellPosFlag::Top) ? pBoxItem->GetTop() : pBoxInfoItem->GetHori(), SvxBoxItemLine::TOP );
+
+ if( (nCellPosFlags & CellPosFlag::Bottom) ? pBoxInfoItem->IsValid(SvxBoxInfoItemValidFlags::BOTTOM) : pBoxInfoItem->IsValid(SvxBoxInfoItemValidFlags::HORI) )
+ rNewFrame.SetLine( (nCellPosFlags & CellPosFlag::Bottom) ? pBoxItem->GetBottom() : pBoxInfoItem->GetHori(), SvxBoxItemLine::BOTTOM );
+
+ // apply distance to borders
+ if( pBoxInfoItem->IsValid( SvxBoxInfoItemValidFlags::DISTANCE ) )
+ for( SvxBoxItemLine nLine : o3tl::enumrange<SvxBoxItemLine>() )
+ rNewFrame.SetDistance( pBoxItem->GetDistance( nLine ), nLine );
+ }
+}
+
+
+static void ImplSetLineColor( SvxBoxItem& rNewFrame, SvxBoxItemLine nLine, const Color& rColor )
+{
+ const SvxBorderLine* pSourceLine = rNewFrame.GetLine( nLine );
+ if( pSourceLine )
+ {
+ SvxBorderLine aLine( *pSourceLine );
+ aLine.SetColor( rColor );
+ rNewFrame.SetLine( &aLine, nLine );
+ }
+}
+
+
+static void ImplApplyLineColorItem( CellPosFlag nCellPosFlags, const SvxColorItem* pLineColorItem, SvxBoxItem& rNewFrame )
+{
+ const Color aColor( pLineColorItem->GetValue() );
+
+ if (!(nCellPosFlags & (CellPosFlag::Lower|CellPosFlag::Before|CellPosFlag::After)))
+ ImplSetLineColor( rNewFrame, SvxBoxItemLine::BOTTOM, aColor );
+
+ if (!(nCellPosFlags & (CellPosFlag::Upper|CellPosFlag::Before|CellPosFlag::After)))
+ ImplSetLineColor( rNewFrame, SvxBoxItemLine::TOP, aColor );
+
+ if (!(nCellPosFlags & (CellPosFlag::Upper|CellPosFlag::Lower|CellPosFlag::After)))
+ ImplSetLineColor( rNewFrame, SvxBoxItemLine::RIGHT, aColor );
+
+ if (!(nCellPosFlags & (CellPosFlag::Upper|CellPosFlag::Lower|CellPosFlag::Before)))
+ ImplSetLineColor( rNewFrame, SvxBoxItemLine::LEFT, aColor );
+}
+
+
+static void ImplApplyBorderLineItem( CellPosFlag nCellPosFlags, const SvxBorderLine* pBorderLineItem, SvxBoxItem& rNewFrame )
+{
+ if (nCellPosFlags & (CellPosFlag::Before|CellPosFlag::After|CellPosFlag::Upper|CellPosFlag::Lower))
+ {
+ if (!(nCellPosFlags & (CellPosFlag::Before|CellPosFlag::After))) // check if it's not any corner
+ {
+ if (nCellPosFlags & CellPosFlag::Upper)
+ {
+ if( rNewFrame.GetBottom() )
+ ImplSetLinePreserveColor( rNewFrame, pBorderLineItem, SvxBoxItemLine::BOTTOM );
+ }
+ else if (nCellPosFlags & CellPosFlag::Lower)
+ {
+ if( rNewFrame.GetTop() )
+ ImplSetLinePreserveColor( rNewFrame, pBorderLineItem, SvxBoxItemLine::TOP );
+ }
+ }
+ else if (!(nCellPosFlags & (CellPosFlag::Upper|CellPosFlag::Lower))) // check if it's not any corner
+ {
+ if (nCellPosFlags & CellPosFlag::Before)
+ {
+ if( rNewFrame.GetRight() )
+ ImplSetLinePreserveColor( rNewFrame, pBorderLineItem, SvxBoxItemLine::RIGHT );
+ }
+ else if (nCellPosFlags & CellPosFlag::After)
+ {
+ if( rNewFrame.GetLeft() )
+ ImplSetLinePreserveColor( rNewFrame, pBorderLineItem, SvxBoxItemLine::LEFT );
+ }
+ }
+ }
+ else
+ {
+ if( rNewFrame.GetBottom() )
+ ImplSetLinePreserveColor( rNewFrame, pBorderLineItem, SvxBoxItemLine::BOTTOM );
+ if( rNewFrame.GetTop() )
+ ImplSetLinePreserveColor( rNewFrame, pBorderLineItem, SvxBoxItemLine::TOP );
+ if( rNewFrame.GetRight() )
+ ImplSetLinePreserveColor( rNewFrame, pBorderLineItem, SvxBoxItemLine::RIGHT );
+ if( rNewFrame.GetLeft() )
+ ImplSetLinePreserveColor( rNewFrame, pBorderLineItem, SvxBoxItemLine::LEFT );
+ }
+}
+
+
+void SvxTableController::ApplyBorderAttr( const SfxItemSet& rAttr )
+{
+ if( !mxTable.is() )
+ return;
+
+ const sal_Int32 nRowCount = mxTable->getRowCount();
+ const sal_Int32 nColCount = mxTable->getColumnCount();
+ if( !(nRowCount && nColCount) )
+ return;
+
+ const SvxBoxItem* pBoxItem = nullptr;
+ if(SfxItemState::SET == rAttr.GetItemState(SDRATTR_TABLE_BORDER, false) )
+ pBoxItem = &rAttr.Get( SDRATTR_TABLE_BORDER );
+
+ const SvxBoxInfoItem* pBoxInfoItem = nullptr;
+ if(SfxItemState::SET == rAttr.GetItemState(SDRATTR_TABLE_BORDER_INNER, false) )
+ pBoxInfoItem = &rAttr.Get( SDRATTR_TABLE_BORDER_INNER );
+
+ const SvxColorItem* pLineColorItem = nullptr;
+ if(SfxItemState::SET == rAttr.GetItemState(SID_FRAME_LINECOLOR, false) )
+ pLineColorItem = &rAttr.Get( SID_FRAME_LINECOLOR );
+
+ const SvxBorderLine* pBorderLineItem = nullptr;
+ if(SfxItemState::SET == rAttr.GetItemState(SID_FRAME_LINESTYLE, false) )
+ pBorderLineItem = rAttr.Get( SID_FRAME_LINESTYLE ).GetLine();
+
+ if( pBoxInfoItem && !pBoxItem )
+ {
+ const static SvxBoxItem gaEmptyBoxItem( SDRATTR_TABLE_BORDER );
+ pBoxItem = &gaEmptyBoxItem;
+ }
+ else if( pBoxItem && !pBoxInfoItem )
+ {
+ const static SvxBoxInfoItem gaEmptyBoxInfoItem( SDRATTR_TABLE_BORDER_INNER );
+ pBoxInfoItem = &gaEmptyBoxInfoItem;
+ }
+
+ CellPos aStart, aEnd;
+ getSelectedCells( aStart, aEnd );
+
+ const sal_Int32 nLastRow = std::min( aEnd.mnRow + 2, nRowCount );
+ const sal_Int32 nLastCol = std::min( aEnd.mnCol + 2, nColCount );
+
+ for( sal_Int32 nRow = std::max( aStart.mnRow - 1, sal_Int32(0) ); nRow < nLastRow; nRow++ )
+ {
+ CellPosFlag nRowFlags = CellPosFlag::NONE;
+ nRowFlags |= (nRow == aStart.mnRow) ? CellPosFlag::Top : CellPosFlag::NONE;
+ nRowFlags |= (nRow == aEnd.mnRow) ? CellPosFlag::Bottom : CellPosFlag::NONE;
+ nRowFlags |= (nRow < aStart.mnRow) ? CellPosFlag::Upper : CellPosFlag::NONE;
+ nRowFlags |= (nRow > aEnd.mnRow) ? CellPosFlag::Lower : CellPosFlag::NONE;
+
+ for( sal_Int32 nCol = std::max( aStart.mnCol - 1, sal_Int32(0) ); nCol < nLastCol; nCol++ )
+ {
+ CellRef xCell( dynamic_cast< Cell* >( mxTable->getCellByPosition( nCol, nRow ).get() ) );
+ if( !xCell.is() )
+ continue;
+
+ const SfxItemSet& rSet = xCell->GetItemSet();
+ const SvxBoxItem* pOldOuter = &rSet.Get( SDRATTR_TABLE_BORDER );
+
+ SvxBoxItem aNewFrame( *pOldOuter );
+
+ CellPosFlag nCellPosFlags = nRowFlags;
+ nCellPosFlags |= (nCol == aStart.mnCol) ? CellPosFlag::Left : CellPosFlag::NONE;
+ nCellPosFlags |= (nCol == aEnd.mnCol) ? CellPosFlag::Right : CellPosFlag::NONE;
+ nCellPosFlags |= (nCol < aStart.mnCol) ? CellPosFlag::Before : CellPosFlag::NONE;
+ nCellPosFlags |= (nCol > aEnd.mnCol) ? CellPosFlag::After : CellPosFlag::NONE;
+
+ if( pBoxItem && pBoxInfoItem )
+ ImplApplyBoxItem( nCellPosFlags, pBoxItem, pBoxInfoItem, aNewFrame );
+
+ if( pLineColorItem )
+ ImplApplyLineColorItem( nCellPosFlags, pLineColorItem, aNewFrame );
+
+ if( pBorderLineItem )
+ ImplApplyBorderLineItem( nCellPosFlags, pBorderLineItem, aNewFrame );
+
+ if (aNewFrame != *pOldOuter)
+ {
+ SfxItemSet aAttr(*rSet.GetPool(), rSet.GetRanges());
+ aAttr.Put(aNewFrame);
+ xCell->SetMergedItemSetAndBroadcast( aAttr, false );
+ }
+ }
+ }
+}
+
+
+void SvxTableController::UpdateTableShape()
+{
+ SdrObject* pTableObj = mxTableObj.get();
+ if( pTableObj )
+ {
+ pTableObj->ActionChanged();
+ pTableObj->BroadcastObjectChange();
+ }
+ updateSelectionOverlay();
+}
+
+
+void SvxTableController::SetAttrToSelectedCells(const SfxItemSet& rAttr, bool bReplaceAll)
+{
+ if(!checkTableObject() || !mxTable.is())
+ return;
+
+ SdrTableObj& rTableObj(*mxTableObj);
+ SdrModel& rModel(rTableObj.getSdrModelFromSdrObject());
+ const bool bUndo(rModel.IsUndoEnabled());
+
+ if( bUndo )
+ rModel.BegUndo( SvxResId(STR_TABLE_NUMFORMAT) );
+
+ CellPos aStart, aEnd;
+ getSelectedCells( aStart, aEnd );
+
+ SfxItemSet aAttr(*rAttr.GetPool(), rAttr.GetRanges());
+ aAttr.Put(rAttr);
+
+ const bool bFrame = (rAttr.GetItemState( SDRATTR_TABLE_BORDER ) == SfxItemState::SET) || (rAttr.GetItemState( SDRATTR_TABLE_BORDER_INNER ) == SfxItemState::SET);
+
+ if( bFrame )
+ {
+ aAttr.ClearItem( SDRATTR_TABLE_BORDER );
+ aAttr.ClearItem( SDRATTR_TABLE_BORDER_INNER );
+ }
+
+ for( sal_Int32 nRow = aStart.mnRow; nRow <= aEnd.mnRow; nRow++ )
+ {
+ for( sal_Int32 nCol = aStart.mnCol; nCol <= aEnd.mnCol; nCol++ )
+ {
+ CellRef xCell( dynamic_cast< Cell* >( mxTable->getCellByPosition( nCol, nRow ).get() ) );
+ if( xCell.is() )
+ {
+ if( bUndo )
+ xCell->AddUndo();
+ xCell->SetMergedItemSetAndBroadcast(aAttr, bReplaceAll);
+ }
+ }
+ }
+
+ if( bFrame )
+ {
+ ApplyBorderAttr( rAttr );
+ }
+
+ UpdateTableShape();
+
+ if( bUndo )
+ rModel.EndUndo();
+}
+
+void SvxTableController::SetAttrToSelectedShape(const SfxItemSet& rAttr)
+{
+ if (!checkTableObject() || !mxTable.is())
+ return;
+
+ // Filter out non-shadow items from rAttr.
+ SfxItemSetFixed<SDRATTR_SHADOW_FIRST, SDRATTR_SHADOW_LAST> aSet(*rAttr.GetPool());
+ aSet.Put(rAttr);
+
+ if (!aSet.Count())
+ {
+ // If there are no items to be applied on the shape, then don't set anything, it would
+ // terminate text edit without a good reason, which affects undo/redo.
+ return;
+ }
+
+ // Set shadow items on the marked shape.
+ mrView.SetAttrToMarked(aSet, /*bReplaceAll=*/false);
+}
+
+bool SvxTableController::GetAttributes(SfxItemSet& rTargetSet, bool bOnlyHardAttr) const
+{
+ if( mxTableObj.is() && hasSelectedCells() )
+ {
+ MergeAttrFromSelectedCells( rTargetSet, bOnlyHardAttr );
+
+ if( mrView.IsTextEdit() )
+ {
+ OutlinerView* pTextEditOutlinerView = mrView.GetTextEditOutlinerView();
+ if(pTextEditOutlinerView)
+ {
+ // FALSE= consider InvalidItems not as the default, but as "holes"
+ rTargetSet.Put(pTextEditOutlinerView->GetAttribs(), false);
+ }
+ }
+
+ return true;
+ }
+ else
+ {
+ return false;
+ }
+}
+
+
+bool SvxTableController::SetAttributes(const SfxItemSet& rSet, bool bReplaceAll)
+{
+ if( mbCellSelectionMode || mrView.IsTextEdit() )
+ {
+ SetAttrToSelectedCells( rSet, bReplaceAll );
+ return true;
+ }
+ return false;
+}
+
+SdrObject* SvxTableController::GetMarkedSdrObjClone(SdrModel& rTargetModel)
+{
+ SdrTableObj* pRetval(nullptr);
+ sdr::table::SdrTableObj* pCurrentSdrTableObj(GetTableObj());
+
+ if(nullptr == pCurrentSdrTableObj)
+ {
+ return pRetval;
+ }
+
+ if(!mxTableObj.is())
+ {
+ return pRetval;
+ }
+
+ // get selection and create full selection
+ CellPos aStart, aEnd;
+ const CellPos aFullStart, aFullEnd(mxTable->getColumnCount()-1, mxTable->getRowCount()-1);
+
+ getSelectedCells(aStart, aEnd);
+
+ // compare to see if we have a partial selection
+ if(aStart != aFullStart || aEnd != aFullEnd)
+ {
+ // create full clone
+ pRetval = pCurrentSdrTableObj->CloneSdrObject(rTargetModel);
+
+ // limit SdrObject's TableModel to partial selection
+ pRetval->CropTableModelToSelection(aStart, aEnd);
+ }
+
+ return pRetval;
+}
+
+bool SvxTableController::PasteObjModel( const SdrModel& rModel )
+{
+ if( mxTableObj.is() && (rModel.GetPageCount() >= 1) )
+ {
+ const SdrPage* pPastePage = rModel.GetPage(0);
+ if( pPastePage && pPastePage->GetObjCount() == 1 )
+ {
+ SdrTableObj* pPasteTableObj = dynamic_cast< SdrTableObj* >( pPastePage->GetObj(0) );
+ if( pPasteTableObj )
+ {
+ return PasteObject( pPasteTableObj );
+ }
+ }
+ }
+
+ return false;
+}
+
+
+bool SvxTableController::PasteObject( SdrTableObj const * pPasteTableObj )
+{
+ if( !pPasteTableObj )
+ return false;
+
+ Reference< XTable > xPasteTable( pPasteTableObj->getTable() );
+ if( !xPasteTable.is() )
+ return false;
+
+ if( !mxTable.is() )
+ return false;
+
+ sal_Int32 nPasteColumns = xPasteTable->getColumnCount();
+ sal_Int32 nPasteRows = xPasteTable->getRowCount();
+
+ CellPos aStart, aEnd;
+ getSelectedCells( aStart, aEnd );
+
+ if( mrView.IsTextEdit() )
+ mrView.SdrEndTextEdit(true);
+
+ sal_Int32 nColumns = mxTable->getColumnCount();
+ sal_Int32 nRows = mxTable->getRowCount();
+
+ const sal_Int32 nMissing = nPasteRows - ( nRows - aStart.mnRow );
+ if( nMissing > 0 )
+ {
+ Reference< XTableRows > xRows( mxTable->getRows() );
+ xRows->insertByIndex( nRows, nMissing );
+ nRows = mxTable->getRowCount();
+ }
+
+ nPasteRows = std::min( nPasteRows, nRows - aStart.mnRow );
+ nPasteColumns = std::min( nPasteColumns, nColumns - aStart.mnCol );
+
+ // copy cell contents
+ for( sal_Int32 nRow = 0; nRow < nPasteRows; ++nRow )
+ {
+ for( sal_Int32 nCol = 0, nTargetCol = aStart.mnCol; nCol < nPasteColumns; ++nCol )
+ {
+ CellRef xTargetCell( dynamic_cast< Cell* >( mxTable->getCellByPosition( nTargetCol, aStart.mnRow + nRow ).get() ) );
+ if( xTargetCell.is() && !xTargetCell->isMerged() )
+ {
+ CellRef xSourceCell(dynamic_cast<Cell*>(xPasteTable->getCellByPosition(nCol, nRow).get()));
+ if (xSourceCell.is())
+ {
+ xTargetCell->AddUndo();
+ // Prevent cell span exceeding the pasting range.
+ if (nColumns < nTargetCol + xSourceCell->getColumnSpan())
+ xTargetCell->replaceContentAndFormatting(xSourceCell);
+ else
+ xTargetCell->cloneFrom(xSourceCell);
+
+ nCol += xSourceCell->getColumnSpan() - 1;
+ nTargetCol += xTargetCell->getColumnSpan();
+ }
+ }
+ }
+ }
+
+ UpdateTableShape();
+
+ return true;
+}
+
+bool SvxTableController::ApplyFormatPaintBrush( SfxItemSet& rFormatSet, bool bNoCharacterFormats, bool bNoParagraphFormats )
+{
+ if(!mbCellSelectionMode)
+ {
+ return false;
+ }
+
+ if(!checkTableObject())
+ return false;
+
+ SdrTableObj& rTableObj(*mxTableObj);
+ SdrModel& rModel(rTableObj.getSdrModelFromSdrObject());
+ const bool bUndo(rModel.IsUndoEnabled());
+
+ if( bUndo )
+ rModel.BegUndo(SvxResId(STR_TABLE_NUMFORMAT));
+
+ CellPos aStart, aEnd;
+ getSelectedCells( aStart, aEnd );
+ const bool bFrame = (rFormatSet.GetItemState( SDRATTR_TABLE_BORDER ) == SfxItemState::SET) || (rFormatSet.GetItemState( SDRATTR_TABLE_BORDER_INNER ) == SfxItemState::SET);
+
+ for( sal_Int32 nRow = aStart.mnRow; nRow <= aEnd.mnRow; nRow++ )
+ {
+ for( sal_Int32 nCol = aStart.mnCol; nCol <= aEnd.mnCol; nCol++ )
+ {
+ CellRef xCell( dynamic_cast< Cell* >( mxTable->getCellByPosition( nCol, nRow ).get() ) );
+ if( xCell.is() )
+ {
+ if (bUndo)
+ xCell->AddUndo();
+ SdrText* pText = xCell.get();
+ SdrObjEditView::ApplyFormatPaintBrushToText( rFormatSet, rTableObj, pText, bNoCharacterFormats, bNoParagraphFormats );
+ }
+ }
+ }
+
+ if( bFrame )
+ {
+ ApplyBorderAttr( rFormatSet );
+ }
+
+ UpdateTableShape();
+
+ if( bUndo )
+ rModel.EndUndo();
+
+ return true;
+}
+
+
+IMPL_LINK_NOARG(SvxTableController, UpdateHdl, void*, void)
+{
+ mnUpdateEvent = nullptr;
+
+ if( mbCellSelectionMode )
+ {
+ CellPos aStart( maCursorFirstPos );
+ CellPos aEnd( maCursorLastPos );
+ checkCell(aStart);
+ checkCell(aEnd);
+ if( aStart != maCursorFirstPos || aEnd != maCursorLastPos )
+ {
+ setSelectedCells( aStart, aEnd );
+ }
+ }
+
+ updateSelectionOverlay();
+ mbHasJustMerged = false;
+}
+
+namespace
+{
+
+struct LinesState
+{
+ LinesState(SvxBoxItem& rBoxItem_, SvxBoxInfoItem& rBoxInfoItem_)
+ : rBoxItem(rBoxItem_)
+ , rBoxInfoItem(rBoxInfoItem_)
+ , bDistanceIndeterminate(false)
+ {
+ aBorderSet.fill(false);
+ aInnerLineSet.fill(false);
+ aBorderIndeterminate.fill(false);
+ aInnerLineIndeterminate.fill(false);
+ aDistanceSet.fill(false);
+ aDistance.fill(0);
+ }
+
+ SvxBoxItem& rBoxItem;
+ SvxBoxInfoItem& rBoxInfoItem;
+ o3tl::enumarray<SvxBoxItemLine, bool> aBorderSet;
+ o3tl::enumarray<SvxBoxInfoItemLine, bool> aInnerLineSet;
+ o3tl::enumarray<SvxBoxItemLine, bool> aBorderIndeterminate;
+ o3tl::enumarray<SvxBoxInfoItemLine, bool> aInnerLineIndeterminate;
+ o3tl::enumarray<SvxBoxItemLine, bool> aDistanceSet;
+ o3tl::enumarray<SvxBoxItemLine, sal_uInt16> aDistance;
+ bool bDistanceIndeterminate;
+};
+
+class BoxItemWrapper
+{
+public:
+ BoxItemWrapper(SvxBoxItem& rBoxItem, SvxBoxInfoItem& rBoxInfoItem, SvxBoxItemLine nBorderLine, SvxBoxInfoItemLine nInnerLine, bool bBorder);
+
+ const SvxBorderLine* getLine() const;
+ void setLine(const SvxBorderLine* pLine);
+
+private:
+ SvxBoxItem& m_rBoxItem;
+ SvxBoxInfoItem& m_rBoxInfoItem;
+ const SvxBoxItemLine m_nBorderLine;
+ const SvxBoxInfoItemLine m_nInnerLine;
+ const bool m_bBorder;
+};
+
+BoxItemWrapper::BoxItemWrapper(
+ SvxBoxItem& rBoxItem, SvxBoxInfoItem& rBoxInfoItem,
+ const SvxBoxItemLine nBorderLine, const SvxBoxInfoItemLine nInnerLine, const bool bBorder)
+ : m_rBoxItem(rBoxItem)
+ , m_rBoxInfoItem(rBoxInfoItem)
+ , m_nBorderLine(nBorderLine)
+ , m_nInnerLine(nInnerLine)
+ , m_bBorder(bBorder)
+{
+}
+
+const SvxBorderLine* BoxItemWrapper::getLine() const
+{
+ if (m_bBorder)
+ return m_rBoxItem.GetLine(m_nBorderLine);
+ else
+ return (m_nInnerLine == SvxBoxInfoItemLine::HORI) ? m_rBoxInfoItem.GetHori() : m_rBoxInfoItem.GetVert();
+}
+
+void BoxItemWrapper::setLine(const SvxBorderLine* pLine)
+{
+ if (m_bBorder)
+ m_rBoxItem.SetLine(pLine, m_nBorderLine);
+ else
+ m_rBoxInfoItem.SetLine(pLine, m_nInnerLine);
+}
+
+void lcl_MergeBorderLine(
+ LinesState& rLinesState, const SvxBorderLine* const pLine, const SvxBoxItemLine nLine,
+ SvxBoxInfoItemValidFlags nValidFlag, const bool bBorder = true)
+{
+ const SvxBoxInfoItemLine nInnerLine(bBorder ? SvxBoxInfoItemLine::HORI : ((nValidFlag & SvxBoxInfoItemValidFlags::HORI) ? SvxBoxInfoItemLine::HORI : SvxBoxInfoItemLine::VERT));
+ BoxItemWrapper aBoxItem(rLinesState.rBoxItem, rLinesState.rBoxInfoItem, nLine, nInnerLine, bBorder);
+ bool& rbSet(bBorder ? rLinesState.aBorderSet[nLine] : rLinesState.aInnerLineSet[nInnerLine]);
+
+ if (rbSet)
+ {
+ bool& rbIndeterminate(bBorder ? rLinesState.aBorderIndeterminate[nLine] : rLinesState.aInnerLineIndeterminate[nInnerLine]);
+ if (!rbIndeterminate)
+ {
+ const SvxBorderLine* const pMergedLine(aBoxItem.getLine());
+ if ((pLine && !pMergedLine) || (!pLine && pMergedLine) || (pLine && (*pLine != *pMergedLine)))
+ {
+ aBoxItem.setLine(nullptr);
+ rbIndeterminate = true;
+ }
+ }
+ }
+ else
+ {
+ aBoxItem.setLine(pLine);
+ rbSet = true;
+ }
+}
+
+void lcl_MergeBorderOrInnerLine(
+ LinesState& rLinesState, const SvxBorderLine* const pLine, const SvxBoxItemLine nLine,
+ SvxBoxInfoItemValidFlags nValidFlag, const bool bBorder)
+{
+ if (bBorder)
+ lcl_MergeBorderLine(rLinesState, pLine, nLine, nValidFlag);
+ else
+ {
+ const bool bVertical = (nLine == SvxBoxItemLine::LEFT) || (nLine == SvxBoxItemLine::RIGHT);
+ lcl_MergeBorderLine(rLinesState, pLine, nLine, bVertical ? SvxBoxInfoItemValidFlags::VERT : SvxBoxInfoItemValidFlags::HORI, false);
+ }
+}
+
+void lcl_MergeDistance(
+ LinesState& rLinesState, const SvxBoxItemLine nIndex, const sal_uInt16 nDistance)
+{
+ if (rLinesState.aDistanceSet[nIndex])
+ {
+ if (!rLinesState.bDistanceIndeterminate)
+ rLinesState.bDistanceIndeterminate = nDistance != rLinesState.aDistance[nIndex];
+ }
+ else
+ {
+ rLinesState.aDistance[nIndex] = nDistance;
+ rLinesState.aDistanceSet[nIndex] = true;
+ }
+}
+
+void lcl_MergeCommonBorderAttr(LinesState& rLinesState, const SvxBoxItem& rCellBoxItem, const CellPosFlag nCellPosFlags)
+{
+ if (nCellPosFlags & (CellPosFlag::Before|CellPosFlag::After|CellPosFlag::Upper|CellPosFlag::Lower))
+ {
+ // current cell is outside the selection
+
+ if (!(nCellPosFlags & (CellPosFlag::Before|CellPosFlag::After))) // check if it's not any corner
+ {
+ if (nCellPosFlags & CellPosFlag::Upper)
+ lcl_MergeBorderLine(rLinesState, rCellBoxItem.GetBottom(), SvxBoxItemLine::TOP, SvxBoxInfoItemValidFlags::TOP);
+ else if (nCellPosFlags & CellPosFlag::Lower)
+ lcl_MergeBorderLine(rLinesState, rCellBoxItem.GetTop(), SvxBoxItemLine::BOTTOM, SvxBoxInfoItemValidFlags::BOTTOM);
+ }
+ else if (!(nCellPosFlags & (CellPosFlag::Upper|CellPosFlag::Lower))) // check if it's not any corner
+ {
+ if (nCellPosFlags & CellPosFlag::Before)
+ lcl_MergeBorderLine(rLinesState, rCellBoxItem.GetRight(), SvxBoxItemLine::LEFT, SvxBoxInfoItemValidFlags::LEFT);
+ else if (nCellPosFlags & CellPosFlag::After)
+ lcl_MergeBorderLine(rLinesState, rCellBoxItem.GetLeft(), SvxBoxItemLine::RIGHT, SvxBoxInfoItemValidFlags::RIGHT);
+ }
+
+ // NOTE: inner distances for cells outside the selected range
+ // are not relevant -> we ignore them.
+ }
+ else
+ {
+ // current cell is inside the selection
+
+ lcl_MergeBorderOrInnerLine(rLinesState, rCellBoxItem.GetTop(), SvxBoxItemLine::TOP, SvxBoxInfoItemValidFlags::TOP, static_cast<bool>(nCellPosFlags & CellPosFlag::Top));
+ lcl_MergeBorderOrInnerLine(rLinesState, rCellBoxItem.GetBottom(), SvxBoxItemLine::BOTTOM, SvxBoxInfoItemValidFlags::BOTTOM, static_cast<bool>(nCellPosFlags & CellPosFlag::Bottom));
+ lcl_MergeBorderOrInnerLine(rLinesState, rCellBoxItem.GetLeft(), SvxBoxItemLine::LEFT, SvxBoxInfoItemValidFlags::LEFT, static_cast<bool>(nCellPosFlags & CellPosFlag::Left));
+ lcl_MergeBorderOrInnerLine(rLinesState, rCellBoxItem.GetRight(), SvxBoxItemLine::RIGHT, SvxBoxInfoItemValidFlags::RIGHT, static_cast<bool>(nCellPosFlags & CellPosFlag::Right));
+
+ lcl_MergeDistance(rLinesState, SvxBoxItemLine::TOP, rCellBoxItem.GetDistance(SvxBoxItemLine::TOP));
+ lcl_MergeDistance(rLinesState, SvxBoxItemLine::BOTTOM, rCellBoxItem.GetDistance(SvxBoxItemLine::BOTTOM));
+ lcl_MergeDistance(rLinesState, SvxBoxItemLine::LEFT, rCellBoxItem.GetDistance(SvxBoxItemLine::LEFT));
+ lcl_MergeDistance(rLinesState, SvxBoxItemLine::RIGHT, rCellBoxItem.GetDistance(SvxBoxItemLine::RIGHT));
+ }
+}
+
+}
+
+void SvxTableController::FillCommonBorderAttrFromSelectedCells( SvxBoxItem& rBoxItem, SvxBoxInfoItem& rBoxInfoItem ) const
+{
+ if( !mxTable.is() )
+ return;
+
+ const sal_Int32 nRowCount = mxTable->getRowCount();
+ const sal_Int32 nColCount = mxTable->getColumnCount();
+ if( !(nRowCount && nColCount) )
+ return;
+
+ CellPos aStart, aEnd;
+ const_cast< SvxTableController* >( this )->getSelectedCells( aStart, aEnd );
+
+ // We are adding one more row/column around the block of selected cells.
+ // We will be checking the adjoining border of these too.
+ const sal_Int32 nLastRow = std::min( aEnd.mnRow + 2, nRowCount );
+ const sal_Int32 nLastCol = std::min( aEnd.mnCol + 2, nColCount );
+
+ rBoxInfoItem.SetValid( SvxBoxInfoItemValidFlags::ALL, false );
+ LinesState aLinesState( rBoxItem, rBoxInfoItem );
+
+ /* Here we go through all the selected cells (enhanced by
+ * the adjoining row/column on each side) and determine the
+ * lines for presentation. The algorithm is simple:
+ * 1. if a border or inner line is set (or unset) in all
+ * cells to the same value, it will be used.
+ * 2. if a border or inner line is set only in some cells,
+ * it will be set to indeterminate state (SetValid() on
+ * rBoxInfoItem).
+ */
+ for( sal_Int32 nRow = std::max( aStart.mnRow - 1, sal_Int32(0) ); nRow < nLastRow; nRow++ )
+ {
+ CellPosFlag nRowFlags = CellPosFlag::NONE;
+ nRowFlags |= (nRow == aStart.mnRow) ? CellPosFlag::Top : CellPosFlag::NONE;
+ nRowFlags |= (nRow == aEnd.mnRow) ? CellPosFlag::Bottom : CellPosFlag::NONE;
+ nRowFlags |= (nRow < aStart.mnRow) ? CellPosFlag::Upper : CellPosFlag::NONE;
+ nRowFlags |= (nRow > aEnd.mnRow) ? CellPosFlag::Lower : CellPosFlag::NONE;
+
+ for( sal_Int32 nCol = std::max( aStart.mnCol - 1, sal_Int32(0) ); nCol < nLastCol; nCol++ )
+ {
+ CellRef xCell( dynamic_cast< Cell* >( mxTable->getCellByPosition( nCol, nRow ).get() ) );
+ if( !xCell.is() )
+ continue;
+
+ CellPosFlag nCellPosFlags = nRowFlags;
+ nCellPosFlags |= (nCol == aStart.mnCol) ? CellPosFlag::Left : CellPosFlag::NONE;
+ nCellPosFlags |= (nCol == aEnd.mnCol) ? CellPosFlag::Right : CellPosFlag::NONE;
+ nCellPosFlags |= (nCol < aStart.mnCol) ? CellPosFlag::Before : CellPosFlag::NONE;
+ nCellPosFlags |= (nCol > aEnd.mnCol) ? CellPosFlag::After : CellPosFlag::NONE;
+
+ const SfxItemSet& rSet = xCell->GetItemSet();
+ SvxBoxItem aCellBoxItem(mergeDrawinglayerTextDistancesAndSvxBoxItem(rSet));
+ lcl_MergeCommonBorderAttr( aLinesState, aCellBoxItem, nCellPosFlags );
+ }
+ }
+
+ if (!aLinesState.aBorderIndeterminate[SvxBoxItemLine::TOP])
+ aLinesState.rBoxInfoItem.SetValid(SvxBoxInfoItemValidFlags::TOP);
+ if (!aLinesState.aBorderIndeterminate[SvxBoxItemLine::BOTTOM])
+ aLinesState.rBoxInfoItem.SetValid(SvxBoxInfoItemValidFlags::BOTTOM);
+ if (!aLinesState.aBorderIndeterminate[SvxBoxItemLine::LEFT])
+ aLinesState.rBoxInfoItem.SetValid(SvxBoxInfoItemValidFlags::LEFT);
+ if (!aLinesState.aBorderIndeterminate[SvxBoxItemLine::RIGHT])
+ aLinesState.rBoxInfoItem.SetValid(SvxBoxInfoItemValidFlags::RIGHT);
+ if (!aLinesState.aInnerLineIndeterminate[SvxBoxInfoItemLine::HORI])
+ aLinesState.rBoxInfoItem.SetValid(SvxBoxInfoItemValidFlags::HORI);
+ if (!aLinesState.aInnerLineIndeterminate[SvxBoxInfoItemLine::VERT])
+ aLinesState.rBoxInfoItem.SetValid(SvxBoxInfoItemValidFlags::VERT);
+
+ if (!aLinesState.bDistanceIndeterminate)
+ {
+ if (aLinesState.aDistanceSet[SvxBoxItemLine::TOP])
+ aLinesState.rBoxItem.SetDistance(aLinesState.aDistance[SvxBoxItemLine::TOP], SvxBoxItemLine::TOP);
+ if (aLinesState.aDistanceSet[SvxBoxItemLine::BOTTOM])
+ aLinesState.rBoxItem.SetDistance(aLinesState.aDistance[SvxBoxItemLine::BOTTOM], SvxBoxItemLine::BOTTOM);
+ if (aLinesState.aDistanceSet[SvxBoxItemLine::LEFT])
+ aLinesState.rBoxItem.SetDistance(aLinesState.aDistance[SvxBoxItemLine::LEFT], SvxBoxItemLine::LEFT);
+ if (aLinesState.aDistanceSet[SvxBoxItemLine::RIGHT])
+ aLinesState.rBoxItem.SetDistance(aLinesState.aDistance[SvxBoxItemLine::RIGHT], SvxBoxItemLine::RIGHT);
+ aLinesState.rBoxInfoItem.SetValid(SvxBoxInfoItemValidFlags::DISTANCE);
+ }
+}
+
+bool SvxTableController::selectRow( sal_Int32 row )
+{
+ if( !mxTable.is() )
+ return false;
+ CellPos aStart( 0, row ), aEnd( mxTable->getColumnCount() - 1, row );
+ StartSelection( aEnd );
+ gotoCell( aStart, true, nullptr );
+ return true;
+}
+
+bool SvxTableController::selectColumn( sal_Int32 column )
+{
+ if( !mxTable.is() )
+ return false;
+ CellPos aStart( column, 0 ), aEnd( column, mxTable->getRowCount() - 1 );
+ StartSelection( aEnd );
+ gotoCell( aStart, true, nullptr );
+ return true;
+}
+
+bool SvxTableController::deselectRow( sal_Int32 row )
+{
+ if( !mxTable.is() )
+ return false;
+ CellPos aStart( 0, row ), aEnd( mxTable->getColumnCount() - 1, row );
+ StartSelection( aEnd );
+ gotoCell( aStart, false, nullptr );
+ return true;
+}
+
+bool SvxTableController::deselectColumn( sal_Int32 column )
+{
+ if( !mxTable.is() )
+ return false;
+ CellPos aStart( column, 0 ), aEnd( column, mxTable->getRowCount() - 1 );
+ StartSelection( aEnd );
+ gotoCell( aStart, false, nullptr );
+ return true;
+}
+
+bool SvxTableController::isRowSelected( sal_Int32 nRow )
+{
+ if( hasSelectedCells() )
+ {
+ CellPos aFirstPos, aLastPos;
+ getSelectedCells( aFirstPos, aLastPos );
+ if( (aFirstPos.mnCol == 0) && (nRow >= aFirstPos.mnRow && nRow <= aLastPos.mnRow) && (mxTable->getColumnCount() - 1 == aLastPos.mnCol) )
+ return true;
+ }
+ return false;
+}
+
+bool SvxTableController::isColumnSelected( sal_Int32 nColumn )
+{
+ if( hasSelectedCells() )
+ {
+ CellPos aFirstPos, aLastPos;
+ getSelectedCells( aFirstPos, aLastPos );
+ if( (aFirstPos.mnRow == 0) && (nColumn >= aFirstPos.mnCol && nColumn <= aLastPos.mnCol) && (mxTable->getRowCount() - 1 == aLastPos.mnRow) )
+ return true;
+ }
+ return false;
+}
+
+bool SvxTableController::isRowHeader()
+{
+ if(!checkTableObject())
+ return false;
+
+ SdrTableObj& rTableObj(*mxTableObj);
+ TableStyleSettings aSettings(rTableObj.getTableStyleSettings());
+
+ return aSettings.mbUseFirstRow;
+}
+
+bool SvxTableController::isColumnHeader()
+{
+ if(!checkTableObject())
+ return false;
+
+ SdrTableObj& rTableObj(*mxTableObj);
+ TableStyleSettings aSettings(rTableObj.getTableStyleSettings());
+
+ return aSettings.mbUseFirstColumn;
+}
+
+bool SvxTableController::setCursorLogicPosition(const Point& rPosition, bool bPoint)
+{
+ if (mxTableObj->GetObjIdentifier() != SdrObjKind::Table)
+ return false;
+
+ SdrTableObj* pTableObj = mxTableObj.get();
+ CellPos aCellPos;
+ if (pTableObj->CheckTableHit(rPosition, aCellPos.mnCol, aCellPos.mnRow) != TableHitKind::NONE)
+ {
+ // Position is a table cell.
+ if (mbCellSelectionMode)
+ {
+ // We have a table selection already: adjust the point or the mark.
+ if (bPoint)
+ setSelectedCells(maCursorFirstPos, aCellPos);
+ else
+ setSelectedCells(aCellPos, maCursorLastPos);
+ return true;
+ }
+ else if (aCellPos != maMouseDownPos)
+ {
+ // No selection, but rPosition is at another cell: start table selection.
+ StartSelection(maMouseDownPos);
+ // Update graphic selection, should be hidden now.
+ mrView.AdjustMarkHdl();
+ }
+ }
+
+ return false;
+}
+
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/table/tabledesign.cxx b/svx/source/table/tabledesign.cxx
new file mode 100644
index 000000000..03b16250f
--- /dev/null
+++ b/svx/source/table/tabledesign.cxx
@@ -0,0 +1,705 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * 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/style/XStyle.hpp>
+#include <com/sun/star/lang/IndexOutOfBoundsException.hpp>
+#include <com/sun/star/lang/XServiceInfo.hpp>
+#include <com/sun/star/lang/XComponent.hpp>
+#include <com/sun/star/lang/XSingleServiceFactory.hpp>
+#include <com/sun/star/container/XIndexAccess.hpp>
+#include <com/sun/star/container/XNameContainer.hpp>
+#include <com/sun/star/beans/XPropertySet.hpp>
+#include <com/sun/star/util/XModifyBroadcaster.hpp>
+#include <com/sun/star/util/XModifyListener.hpp>
+
+#include <vcl/svapp.hxx>
+
+#include <comphelper/compbase.hxx>
+#include <comphelper/interfacecontainer4.hxx>
+#include <cppuhelper/implbase.hxx>
+#include <cppuhelper/supportsservice.hxx>
+#include <comphelper/sequence.hxx>
+
+#include <svx/sdr/table/tabledesign.hxx>
+#include <svx/dialmgr.hxx>
+#include <svx/strings.hrc>
+
+#include <celltypes.hxx>
+
+#include <vector>
+#include <map>
+
+
+using namespace ::com::sun::star::uno;
+using namespace ::com::sun::star::style;
+using namespace ::com::sun::star::lang;
+using namespace ::com::sun::star::beans;
+using namespace ::com::sun::star::util;
+using namespace ::com::sun::star::container;
+
+namespace sdr::table {
+
+typedef std::map< OUString, sal_Int32 > CellStyleNameMap;
+
+typedef ::comphelper::WeakComponentImplHelper< XStyle, XNameReplace, XServiceInfo, XIndexAccess, XModifyBroadcaster, XModifyListener > TableDesignStyleBase;
+
+namespace {
+
+class TableDesignStyle : public TableDesignStyleBase
+{
+public:
+ TableDesignStyle();
+
+ // XServiceInfo
+ virtual OUString SAL_CALL getImplementationName() override;
+ virtual sal_Bool SAL_CALL supportsService( const OUString& ServiceName ) override;
+ virtual Sequence< OUString > SAL_CALL getSupportedServiceNames() 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;
+
+ // XNamed
+ virtual OUString SAL_CALL getName() override;
+ virtual void SAL_CALL setName( const OUString& aName ) override;
+
+ // XNameAccess
+ virtual Any SAL_CALL getByName( const OUString& aName ) override;
+ virtual 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;
+
+ // XIndexAccess
+ virtual sal_Int32 SAL_CALL getCount() override ;
+ virtual Any SAL_CALL getByIndex( sal_Int32 Index ) override;
+
+ // XNameReplace
+ virtual void SAL_CALL replaceByName( const OUString& aName, const Any& aElement ) override;
+
+ // XModifyBroadcaster
+ virtual void SAL_CALL addModifyListener( const Reference< XModifyListener >& aListener ) override;
+ virtual void SAL_CALL removeModifyListener( const Reference< XModifyListener >& aListener ) override;
+
+ // XModifyListener
+ virtual void SAL_CALL modified( const css::lang::EventObject& aEvent ) override;
+ virtual void SAL_CALL disposing( const css::lang::EventObject& Source ) override;
+
+ void notifyModifyListener();
+
+ // this function is called upon disposing the component
+ virtual void disposing(std::unique_lock<std::mutex>&) override;
+
+ static const CellStyleNameMap& getCellStyleNameMap();
+
+ OUString msName;
+ Reference< XStyle > maCellStyles[style_count];
+ comphelper::OInterfaceContainerHelper4<XModifyListener> maModifyListeners;
+};
+
+}
+
+typedef std::vector< Reference< XStyle > > TableDesignStyleVector;
+
+namespace {
+
+class TableDesignFamily : public ::cppu::WeakImplHelper< XNameContainer, XNamed, XIndexAccess, XSingleServiceFactory, XServiceInfo, XComponent, XPropertySet >
+{
+public:
+ // XServiceInfo
+ virtual OUString SAL_CALL getImplementationName() override;
+ virtual sal_Bool SAL_CALL supportsService( const OUString& ServiceName ) override;
+ virtual Sequence< OUString > SAL_CALL getSupportedServiceNames() override;
+
+ // XNamed
+ virtual OUString SAL_CALL getName( ) override;
+ virtual void SAL_CALL setName( const OUString& aName ) override;
+
+ // XNameAccess
+ virtual Any SAL_CALL getByName( const OUString& aName ) override;
+ virtual Sequence< OUString > SAL_CALL getElementNames() override;
+ virtual sal_Bool SAL_CALL hasByName( const OUString& aName ) override;
+
+ // XElementAccess
+ virtual Type SAL_CALL getElementType() override;
+ virtual sal_Bool SAL_CALL hasElements() override;
+
+ // XIndexAccess
+ virtual sal_Int32 SAL_CALL getCount() override ;
+ virtual Any SAL_CALL getByIndex( sal_Int32 Index ) override;
+
+ // XNameContainer
+ virtual void SAL_CALL insertByName( const OUString& aName, const Any& aElement ) override;
+ virtual void SAL_CALL removeByName( const OUString& Name ) override;
+
+ // XNameReplace
+ virtual void SAL_CALL replaceByName( const OUString& aName, const Any& aElement ) override;
+
+ // XSingleServiceFactory
+ virtual Reference< XInterface > SAL_CALL createInstance( ) override;
+ virtual Reference< XInterface > SAL_CALL createInstanceWithArguments( const Sequence< Any >& aArguments ) override;
+
+ // XComponent
+ virtual void SAL_CALL dispose( ) override;
+ virtual void SAL_CALL addEventListener( const Reference< XEventListener >& xListener ) override;
+ virtual void SAL_CALL removeEventListener( const Reference< XEventListener >& aListener ) override;
+
+ // XPropertySet
+ virtual Reference<XPropertySetInfo> SAL_CALL getPropertySetInfo() override;
+ virtual void SAL_CALL setPropertyValue( const OUString& aPropertyName, const Any& aValue ) override;
+ virtual Any SAL_CALL getPropertyValue( const OUString& PropertyName ) override;
+ virtual void SAL_CALL addPropertyChangeListener( const OUString& aPropertyName, const Reference<XPropertyChangeListener>& xListener ) override;
+ virtual void SAL_CALL removePropertyChangeListener( const OUString& aPropertyName, const Reference<XPropertyChangeListener>& aListener ) override;
+ virtual void SAL_CALL addVetoableChangeListener(const OUString& PropertyName, const Reference<XVetoableChangeListener>& aListener ) override;
+ virtual void SAL_CALL removeVetoableChangeListener(const OUString& PropertyName,const Reference<XVetoableChangeListener>&aListener ) override;
+
+ TableDesignStyleVector maDesigns;
+};
+
+}
+
+TableDesignStyle::TableDesignStyle()
+{
+}
+
+const CellStyleNameMap& TableDesignStyle::getCellStyleNameMap()
+{
+ static CellStyleNameMap const aMap
+ {
+ { OUString( "first-row" ) , first_row_style },
+ { OUString( "last-row" ) , last_row_style },
+ { OUString( "first-column" ) , first_column_style },
+ { OUString( "last-column" ) , last_column_style },
+ { OUString( "body" ) , body_style },
+ { OUString( "even-rows" ) , even_rows_style },
+ { OUString( "odd-rows" ) , odd_rows_style },
+ { OUString( "even-columns" ) , even_columns_style },
+ { OUString( "odd-columns" ) , odd_columns_style },
+ { OUString( "background" ) , background_style },
+ };
+
+ return aMap;
+}
+
+// XServiceInfo
+OUString SAL_CALL TableDesignStyle::getImplementationName()
+{
+ return "TableDesignStyle";
+}
+
+sal_Bool SAL_CALL TableDesignStyle::supportsService( const OUString& ServiceName )
+{
+ return cppu::supportsService( this, ServiceName );
+}
+
+Sequence< OUString > SAL_CALL TableDesignStyle::getSupportedServiceNames()
+{
+ return { "com.sun.star.style.Style" };
+}
+
+// XStyle
+sal_Bool SAL_CALL TableDesignStyle::isUserDefined()
+{
+ return false;
+}
+
+sal_Bool SAL_CALL TableDesignStyle::isInUse()
+{
+ std::unique_lock aGuard( m_aMutex );
+ if (maModifyListeners.getLength(aGuard))
+ {
+ comphelper::OInterfaceIteratorHelper4 it(aGuard, maModifyListeners);
+ while ( it.hasMoreElements() )
+ {
+ TableDesignUser* pUser = dynamic_cast< TableDesignUser* >( it.next().get() );
+ if( pUser && pUser->isInUse() )
+ return true;
+ }
+ }
+ return false;
+}
+
+
+OUString SAL_CALL TableDesignStyle::getParentStyle()
+{
+ return OUString();
+}
+
+
+void SAL_CALL TableDesignStyle::setParentStyle( const OUString& )
+{
+}
+
+
+// XNamed
+
+
+OUString SAL_CALL TableDesignStyle::getName()
+{
+ return msName;
+}
+
+
+void SAL_CALL TableDesignStyle::setName( const OUString& rName )
+{
+ msName = rName;
+}
+
+
+// XNameAccess
+
+
+Any SAL_CALL TableDesignStyle::getByName( const OUString& rName )
+{
+ const CellStyleNameMap& rMap = getCellStyleNameMap();
+
+ CellStyleNameMap::const_iterator iter = rMap.find( rName );
+ if( iter == rMap.end() )
+ throw NoSuchElementException();
+
+ return Any( maCellStyles[(*iter).second] );
+}
+
+
+Sequence< OUString > SAL_CALL TableDesignStyle::getElementNames()
+{
+ return comphelper::mapKeysToSequence( getCellStyleNameMap() );
+}
+
+
+sal_Bool SAL_CALL TableDesignStyle::hasByName( const OUString& rName )
+{
+ const CellStyleNameMap& rMap = getCellStyleNameMap();
+
+ CellStyleNameMap::const_iterator iter = rMap.find( rName );
+ return iter != rMap.end();
+}
+
+
+// XElementAccess
+
+
+Type SAL_CALL TableDesignStyle::getElementType()
+{
+ return cppu::UnoType<XStyle>::get();
+}
+
+
+sal_Bool SAL_CALL TableDesignStyle::hasElements()
+{
+ return true;
+}
+
+
+// XIndexAccess
+
+
+sal_Int32 SAL_CALL TableDesignStyle::getCount()
+{
+ return style_count;
+}
+
+
+Any SAL_CALL TableDesignStyle::getByIndex( sal_Int32 Index )
+{
+ if( (Index < 0) || (Index >= style_count) )
+ throw IndexOutOfBoundsException();
+
+ std::unique_lock aGuard( m_aMutex );
+ return Any( maCellStyles[Index] );
+}
+
+
+// XNameReplace
+
+
+void SAL_CALL TableDesignStyle::replaceByName( const OUString& rName, const Any& aElement )
+{
+ const CellStyleNameMap& rMap = getCellStyleNameMap();
+ CellStyleNameMap::const_iterator iter = rMap.find( rName );
+ if( iter == rMap.end() )
+ throw NoSuchElementException();
+
+
+ Reference< XStyle > xNewStyle;
+ if( !(aElement >>= xNewStyle) )
+ throw IllegalArgumentException();
+
+ const sal_Int32 nIndex = (*iter).second;
+
+ std::unique_lock aGuard( m_aMutex );
+
+ Reference< XStyle > xOldStyle( maCellStyles[nIndex] );
+
+ if( xNewStyle == xOldStyle )
+ return;
+
+ Reference< XModifyListener > xListener( this );
+
+ // end listening to old style, if possible
+ Reference< XModifyBroadcaster > xOldBroadcaster( xOldStyle, UNO_QUERY );
+ if( xOldBroadcaster.is() )
+ xOldBroadcaster->removeModifyListener( xListener );
+
+ // start listening to new style, if possible
+ Reference< XModifyBroadcaster > xNewBroadcaster( xNewStyle, UNO_QUERY );
+ if( xNewBroadcaster.is() )
+ xNewBroadcaster->addModifyListener( xListener );
+
+ maCellStyles[nIndex] = xNewStyle;
+}
+
+
+// XComponent
+
+
+void TableDesignStyle::disposing(std::unique_lock<std::mutex>&)
+{
+ for(Reference<XStyle> & rCellStyle : maCellStyles)
+ rCellStyle.clear();
+}
+
+
+// XModifyBroadcaster
+
+
+void SAL_CALL TableDesignStyle::addModifyListener( const Reference< XModifyListener >& xListener )
+{
+ std::unique_lock aGuard( m_aMutex );
+ if (m_bDisposed)
+ {
+ aGuard.unlock();
+ EventObject aEvt( static_cast< OWeakObject * >( this ) );
+ xListener->disposing( aEvt );
+ }
+ else
+ {
+ maModifyListeners.addInterface( aGuard, xListener );
+ }
+}
+
+
+void SAL_CALL TableDesignStyle::removeModifyListener( const Reference< XModifyListener >& xListener )
+{
+ std::unique_lock aGuard( m_aMutex );
+ maModifyListeners.removeInterface( aGuard, xListener );
+}
+
+
+void TableDesignStyle::notifyModifyListener()
+{
+ std::unique_lock aGuard( m_aMutex );
+
+ if( maModifyListeners.getLength(aGuard) )
+ {
+ EventObject aEvt( static_cast< OWeakObject * >( this ) );
+ maModifyListeners.forEach(aGuard,
+ [&] (Reference<XModifyListener> const& xListener)
+ { return xListener->modified(aEvt); });
+ }
+}
+
+
+// XModifyListener
+
+
+// if we get a modify hint from a style, notify all registered XModifyListener
+void SAL_CALL TableDesignStyle::modified( const css::lang::EventObject& )
+{
+ notifyModifyListener();
+}
+
+
+void SAL_CALL TableDesignStyle::disposing( const css::lang::EventObject& )
+{
+}
+
+
+// TableStyle
+
+
+// XServiceInfo
+OUString SAL_CALL TableDesignFamily::getImplementationName()
+{
+ return "TableDesignFamily";
+}
+
+sal_Bool SAL_CALL TableDesignFamily::supportsService( const OUString& ServiceName )
+{
+ return cppu::supportsService( this, ServiceName );
+}
+
+Sequence< OUString > SAL_CALL TableDesignFamily::getSupportedServiceNames()
+{
+ return { "com.sun.star.style.StyleFamily" };
+}
+
+// XNamed
+OUString SAL_CALL TableDesignFamily::getName()
+{
+ return "table";
+}
+
+void SAL_CALL TableDesignFamily::setName( const OUString& )
+{
+}
+
+// XNameAccess
+Any SAL_CALL TableDesignFamily::getByName( const OUString& rName )
+{
+ SolarMutexGuard aGuard;
+
+ auto iter = std::find_if(maDesigns.begin(), maDesigns.end(),
+ [&rName](const Reference<XStyle>& rpStyle) { return rpStyle->getName() == rName; });
+ if (iter != maDesigns.end())
+ return Any( (*iter) );
+
+ throw NoSuchElementException();
+}
+
+
+Sequence< OUString > SAL_CALL TableDesignFamily::getElementNames()
+{
+ SolarMutexGuard aGuard;
+
+ Sequence< OUString > aRet( maDesigns.size() );
+ OUString* pNames = aRet.getArray();
+
+ for( const auto& rpStyle : maDesigns )
+ *pNames++ = rpStyle->getName();
+
+ return aRet;
+}
+
+
+sal_Bool SAL_CALL TableDesignFamily::hasByName( const OUString& aName )
+{
+ SolarMutexGuard aGuard;
+
+ return std::any_of(maDesigns.begin(), maDesigns.end(),
+ [&aName](const Reference<XStyle>& rpStyle) { return rpStyle->getName() == aName; });
+}
+
+
+// XElementAccess
+
+
+Type SAL_CALL TableDesignFamily::getElementType()
+{
+ return cppu::UnoType<XStyle>::get();
+}
+
+
+sal_Bool SAL_CALL TableDesignFamily::hasElements()
+{
+ SolarMutexGuard aGuard;
+
+ return !maDesigns.empty();
+}
+
+
+// XIndexAccess
+
+
+sal_Int32 SAL_CALL TableDesignFamily::getCount()
+{
+ SolarMutexGuard aGuard;
+
+ return sal::static_int_cast< sal_Int32 >( maDesigns.size() );
+}
+
+
+Any SAL_CALL TableDesignFamily::getByIndex( sal_Int32 Index )
+{
+ SolarMutexGuard aGuard;
+
+ if( (Index >= 0) && (Index < sal::static_int_cast< sal_Int32 >( maDesigns.size() ) ) )
+ return Any( maDesigns[Index] );
+
+ throw IndexOutOfBoundsException();
+}
+
+
+// XNameContainer
+
+
+void SAL_CALL TableDesignFamily::insertByName( const OUString& rName, const Any& rElement )
+{
+ SolarMutexGuard aGuard;
+
+ Reference< XStyle > xStyle( rElement, UNO_QUERY );
+ if( !xStyle.is() )
+ throw IllegalArgumentException();
+
+ xStyle->setName( rName );
+ if (std::any_of(maDesigns.begin(), maDesigns.end(),
+ [&rName](const Reference<XStyle>& rpStyle) { return rpStyle->getName() == rName; }))
+ throw ElementExistException();
+
+ maDesigns.push_back( xStyle );
+}
+
+
+void SAL_CALL TableDesignFamily::removeByName( const OUString& rName )
+{
+ SolarMutexGuard aGuard;
+
+ auto iter = std::find_if(maDesigns.begin(), maDesigns.end(),
+ [&rName](const Reference<XStyle>& rpStyle) { return rpStyle->getName() == rName; });
+ if (iter != maDesigns.end())
+ {
+ maDesigns.erase( iter );
+ return;
+ }
+
+ throw NoSuchElementException();
+}
+
+
+// XNameReplace
+
+
+void SAL_CALL TableDesignFamily::replaceByName( const OUString& rName, const Any& aElement )
+{
+ SolarMutexGuard aGuard;
+
+ Reference< XStyle > xStyle( aElement, UNO_QUERY );
+ if( !xStyle.is() )
+ throw IllegalArgumentException();
+
+ auto iter = std::find_if(maDesigns.begin(), maDesigns.end(),
+ [&rName](const Reference<XStyle>& rpStyle) { return rpStyle->getName() == rName; });
+ if (iter != maDesigns.end())
+ {
+ (*iter) = xStyle;
+ xStyle->setName( rName );
+ return;
+ }
+
+ throw NoSuchElementException();
+}
+
+
+// XSingleServiceFactory
+
+
+Reference< XInterface > SAL_CALL TableDesignFamily::createInstance()
+{
+ return Reference< XInterface >( static_cast< XStyle* >( new TableDesignStyle ) );
+}
+
+
+Reference< XInterface > SAL_CALL TableDesignFamily::createInstanceWithArguments( const Sequence< Any >& )
+{
+ return createInstance();
+}
+
+
+// XComponent
+
+
+void SAL_CALL TableDesignFamily::dispose( )
+{
+ TableDesignStyleVector aDesigns;
+ aDesigns.swap( maDesigns );
+
+ for( const auto& rStyle : aDesigns )
+ {
+ Reference< XComponent > xComp( rStyle, UNO_QUERY );
+ if( xComp.is() )
+ xComp->dispose();
+ }
+}
+
+
+void SAL_CALL TableDesignFamily::addEventListener( const Reference< XEventListener >& )
+{
+}
+
+
+void SAL_CALL TableDesignFamily::removeEventListener( const Reference< XEventListener >& )
+{
+}
+
+
+// XPropertySet
+
+
+Reference<XPropertySetInfo> TableDesignFamily::getPropertySetInfo()
+{
+ OSL_FAIL( "###unexpected!" );
+ return Reference<XPropertySetInfo>();
+}
+
+
+void TableDesignFamily::setPropertyValue( const OUString& , const Any& )
+{
+ OSL_FAIL( "###unexpected!" );
+}
+
+
+Any TableDesignFamily::getPropertyValue( const OUString& PropertyName )
+{
+ if ( PropertyName != "DisplayName" )
+ {
+ throw UnknownPropertyException( "unknown property: " + PropertyName, static_cast<OWeakObject *>(this) );
+ }
+
+ OUString sDisplayName( SvxResId( RID_SVXSTR_STYLEFAMILY_TABLEDESIGN ) );
+ return Any( sDisplayName );
+}
+
+
+void TableDesignFamily::addPropertyChangeListener( const OUString& , const Reference<XPropertyChangeListener>& )
+{
+ OSL_FAIL( "###unexpected!" );
+}
+
+
+void TableDesignFamily::removePropertyChangeListener( const OUString& , const Reference<XPropertyChangeListener>& )
+{
+ OSL_FAIL( "###unexpected!" );
+}
+
+
+void TableDesignFamily::addVetoableChangeListener( const OUString& , const Reference<XVetoableChangeListener>& )
+{
+ OSL_FAIL( "###unexpected!" );
+}
+
+
+void TableDesignFamily::removeVetoableChangeListener( const OUString& , const Reference<XVetoableChangeListener>& )
+{
+ OSL_FAIL( "###unexpected!" );
+}
+
+
+Reference< XNameAccess > CreateTableDesignFamily()
+{
+ return new TableDesignFamily;
+}
+
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/table/tablehandles.cxx b/svx/source/table/tablehandles.cxx
new file mode 100644
index 000000000..a776c579e
--- /dev/null
+++ b/svx/source/table/tablehandles.cxx
@@ -0,0 +1,313 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+
+#include "tablehandles.hxx"
+
+#include <vcl/outdev.hxx>
+#include <vcl/canvastools.hxx>
+#include <vcl/ptrstyle.hxx>
+#include <basegfx/polygon/b2dpolygon.hxx>
+#include <svx/sdr/overlay/overlayobject.hxx>
+#include <svx/sdr/overlay/overlaymanager.hxx>
+#include <svx/sdrpagewindow.hxx>
+#include <svx/sdrpaintwindow.hxx>
+#include <svx/svdmrkv.hxx>
+#include <svx/svdpagv.hxx>
+#include <drawinglayer/primitive2d/PolyPolygonHairlinePrimitive2D.hxx>
+#include <sdr/overlay/overlayrectangle.hxx>
+#include <drawinglayer/primitive2d/hiddengeometryprimitive2d.hxx>
+#include <svtools/optionsdrawinglayer.hxx>
+#include <osl/diagnose.h>
+
+namespace sdr::table {
+
+namespace {
+
+class OverlayTableEdge : public sdr::overlay::OverlayObject
+{
+protected:
+ basegfx::B2DPolyPolygon maPolyPolygon;
+ bool mbVisible;
+
+ // geometry creation for OverlayObject
+ virtual drawinglayer::primitive2d::Primitive2DContainer createOverlayObjectPrimitive2DSequence() override;
+
+public:
+ OverlayTableEdge( const basegfx::B2DPolyPolygon& rPolyPolygon, bool bVisible );
+};
+
+}
+
+TableEdgeHdl::TableEdgeHdl( const Point& rPnt, bool bHorizontal, sal_Int32 nMin, sal_Int32 nMax, sal_Int32 nEdges )
+: SdrHdl( rPnt, SdrHdlKind::User )
+, mbHorizontal( bHorizontal )
+, mnMin( nMin )
+, mnMax( nMax )
+, maEdges(nEdges)
+{
+}
+
+void TableEdgeHdl::SetEdge( sal_Int32 nEdge, sal_Int32 nStart, sal_Int32 nEnd, TableEdgeState eState )
+{
+ if( (nEdge >= 0) && (nEdge <= sal::static_int_cast<sal_Int32>(maEdges.size())) )
+ {
+ maEdges[nEdge].mnStart = nStart;
+ maEdges[nEdge].mnEnd = nEnd;
+ maEdges[nEdge].meState = eState;
+ }
+ else
+ {
+ OSL_FAIL( "sdr::table::TableEdgeHdl::SetEdge(), invalid edge!" );
+ }
+}
+
+PointerStyle TableEdgeHdl::GetPointer() const
+{
+ if( mbHorizontal )
+ return PointerStyle::VSplit;
+ else
+ return PointerStyle::HSplit;
+}
+
+sal_Int32 TableEdgeHdl::GetValidDragOffset( const SdrDragStat& rDrag ) const
+{
+ return std::clamp( static_cast<sal_Int32>(mbHorizontal ? rDrag.GetDY() : rDrag.GetDX()), mnMin, mnMax );
+}
+
+basegfx::B2DPolyPolygon TableEdgeHdl::getSpecialDragPoly(const SdrDragStat& rDrag) const
+{
+ basegfx::B2DPolyPolygon aVisible;
+ basegfx::B2DPolyPolygon aInvisible;
+
+ // create and return visible and non-visible parts for drag
+ getPolyPolygon(aVisible, aInvisible, &rDrag);
+ aVisible.append(aInvisible);
+
+ return aVisible;
+}
+
+void TableEdgeHdl::getPolyPolygon(basegfx::B2DPolyPolygon& rVisible, basegfx::B2DPolyPolygon& rInvisible, const SdrDragStat* pDrag) const
+{
+ // changed method to create visible and invisible partial polygons in one run in
+ // separate PolyPolygons; both kinds are used
+ basegfx::B2DPoint aOffset(aPos.X(), aPos.Y());
+ rVisible.clear();
+ rInvisible.clear();
+
+ if( pDrag )
+ {
+ basegfx::Axis2D eDragAxis = mbHorizontal ? basegfx::Axis2D::Y : basegfx::Axis2D::X;
+ aOffset.set(eDragAxis, aOffset.get(eDragAxis) + GetValidDragOffset( *pDrag ));
+ }
+
+ basegfx::B2DPoint aStart(aOffset), aEnd(aOffset);
+ basegfx::Axis2D eAxis = mbHorizontal ? basegfx::Axis2D::X : basegfx::Axis2D::Y;
+
+ for( const TableEdge& aEdge : maEdges )
+ {
+ aStart.set(eAxis, aOffset.get(eAxis) + aEdge.mnStart);
+ aEnd.set(eAxis, aOffset.get(eAxis) + aEdge.mnEnd);
+
+ basegfx::B2DPolygon aPolygon;
+ aPolygon.append( aStart );
+ aPolygon.append( aEnd );
+
+ if(aEdge.meState == Visible)
+ {
+ rVisible.append(aPolygon);
+ }
+ else
+ {
+ rInvisible.append(aPolygon);
+ }
+ }
+}
+
+void TableEdgeHdl::CreateB2dIAObject()
+{
+ GetRidOfIAObject();
+
+ if(!pHdlList || !pHdlList->GetView() || pHdlList->GetView()->areMarkHandlesHidden())
+ return;
+
+ SdrMarkView* pView = pHdlList->GetView();
+ SdrPageView* pPageView = pView->GetSdrPageView();
+
+ if(!pPageView)
+ return;
+
+ basegfx::B2DPolyPolygon aVisible;
+ basegfx::B2DPolyPolygon aInvisible;
+
+ // get visible and invisible parts
+ getPolyPolygon(aVisible, aInvisible, nullptr);
+
+ if(!(aVisible.count() || aInvisible.count()))
+ return;
+
+ for(sal_uInt32 nWindow = 0; nWindow < pPageView->PageWindowCount(); nWindow++)
+ {
+ const SdrPageWindow& rPageWindow = *pPageView->GetPageWindow(nWindow);
+
+ if(rPageWindow.GetPaintWindow().OutputToWindow())
+ {
+ const rtl::Reference< sdr::overlay::OverlayManager >& xManager = rPageWindow.GetOverlayManager();
+ if (xManager.is())
+ {
+ if(aVisible.count())
+ {
+ // create overlay object for visible parts
+ std::unique_ptr<sdr::overlay::OverlayObject> pOverlayObject(new OverlayTableEdge(aVisible, true));
+
+ // OVERLAYMANAGER
+ insertNewlyCreatedOverlayObjectForSdrHdl(
+ std::move(pOverlayObject),
+ rPageWindow.GetObjectContact(),
+ *xManager);
+ }
+
+ if(aInvisible.count())
+ {
+ // also create overlay object for invisible parts to allow
+ // a standard HitTest using the primitives from that overlay object
+ // (see OverlayTableEdge implementation)
+ std::unique_ptr<sdr::overlay::OverlayObject> pOverlayObject(new OverlayTableEdge(aInvisible, false));
+
+ // OVERLAYMANAGER
+ insertNewlyCreatedOverlayObjectForSdrHdl(
+ std::move(pOverlayObject),
+ rPageWindow.GetObjectContact(),
+ *xManager);
+ }
+ }
+ }
+ }
+}
+
+
+OverlayTableEdge::OverlayTableEdge( const basegfx::B2DPolyPolygon& rPolyPolygon, bool bVisible )
+: OverlayObject(COL_GRAY)
+, maPolyPolygon( rPolyPolygon )
+, mbVisible(bVisible)
+{
+}
+
+drawinglayer::primitive2d::Primitive2DContainer OverlayTableEdge::createOverlayObjectPrimitive2DSequence()
+{
+ drawinglayer::primitive2d::Primitive2DContainer aRetval;
+
+ if(maPolyPolygon.count())
+ {
+ // Discussed with CL. Currently i will leave the transparence out since this
+ // a little bit expensive. We may check the look with drag polygons later
+ const drawinglayer::primitive2d::Primitive2DReference aReference(
+ new drawinglayer::primitive2d::PolyPolygonHairlinePrimitive2D(
+ maPolyPolygon,
+ getBaseColor().getBColor()));
+
+ if(mbVisible)
+ {
+ // visible, just return as sequence
+ aRetval = drawinglayer::primitive2d::Primitive2DContainer { aReference };
+ }
+ else
+ {
+ // embed in HiddenGeometryPrimitive2D to support HitTest of this invisible
+ // overlay object
+ drawinglayer::primitive2d::Primitive2DContainer aSequence { aReference };
+ const drawinglayer::primitive2d::Primitive2DReference aNewReference(
+ new drawinglayer::primitive2d::HiddenGeometryPrimitive2D(std::move(aSequence)));
+ aRetval = drawinglayer::primitive2d::Primitive2DContainer { aNewReference };
+ }
+ }
+
+ return aRetval;
+}
+
+
+TableBorderHdl::TableBorderHdl(
+ const tools::Rectangle& rRect,
+ bool bAnimate)
+: SdrHdl(rRect.TopLeft(), SdrHdlKind::Move),
+ maRectangle(rRect),
+ mbAnimate(bAnimate)
+{
+}
+
+PointerStyle TableBorderHdl::GetPointer() const
+{
+ return PointerStyle::Move;
+}
+
+// create marker for this kind
+void TableBorderHdl::CreateB2dIAObject()
+{
+ GetRidOfIAObject();
+
+ if (!pHdlList || !pHdlList->GetView() || pHdlList->GetView()->areMarkHandlesHidden())
+ return;
+
+ SdrMarkView* pView = pHdlList->GetView();
+ SdrPageView* pPageView = pView->GetSdrPageView();
+
+ if (!pPageView)
+ return;
+
+ for(sal_uInt32 nWindow = 0; nWindow < pPageView->PageWindowCount(); nWindow++)
+ {
+ const SdrPageWindow& rPageWindow = *pPageView->GetPageWindow(nWindow);
+
+ if (rPageWindow.GetPaintWindow().OutputToWindow())
+ {
+ const rtl::Reference<sdr::overlay::OverlayManager>& xManager = rPageWindow.GetOverlayManager();
+
+ if (xManager.is())
+ {
+ const basegfx::B2DRange aRange = vcl::unotools::b2DRectangleFromRectangle(maRectangle);
+ const Color aHilightColor(SvtOptionsDrawinglayer::getHilightColor());
+ const double fTransparence(SvtOptionsDrawinglayer::GetTransparentSelectionPercent() * 0.01);
+ // make animation dependent from text edit active, because for tables
+ // this handle is also used when text edit *is* active for it. This
+ // interferes too much concerning repaint stuff (at least as long as
+ // text edit is not yet on the overlay)
+
+ OutputDevice& rOutDev = rPageWindow.GetPaintWindow().GetOutputDevice();
+ float fScaleFactor = rOutDev.GetDPIScaleFactor();
+ double fWidth = fScaleFactor * 6.0;
+
+ std::unique_ptr<sdr::overlay::OverlayObject> pOverlayObject(
+ new sdr::overlay::OverlayRectangle(aRange.getMinimum(), aRange.getMaximum(),
+ aHilightColor, fTransparence,
+ fWidth, 0.0, 0.0, mbAnimate));
+
+ // OVERLAYMANAGER
+ insertNewlyCreatedOverlayObjectForSdrHdl(
+ std::move(pOverlayObject),
+ rPageWindow.GetObjectContact(),
+ *xManager);
+ }
+ }
+ }
+}
+
+
+} // end of namespace sdr::table
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/table/tablehandles.hxx b/svx/source/table/tablehandles.hxx
new file mode 100644
index 000000000..095edb0b5
--- /dev/null
+++ b/svx/source/table/tablehandles.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_SVX_SOURCE_TABLE_TABLEHANDLES_HXX
+#define INCLUDED_SVX_SOURCE_TABLE_TABLEHANDLES_HXX
+
+#include <svx/svdhdl.hxx>
+
+
+namespace sdr::table {
+
+enum TableEdgeState { Empty, Invisible, Visible };
+
+struct TableEdge
+{
+ sal_Int32 mnStart;
+ sal_Int32 mnEnd;
+ TableEdgeState meState;
+
+ TableEdge() : mnStart(0), mnEnd(0), meState(Empty) {}
+};
+
+class TableEdgeHdl : public SdrHdl
+{
+public:
+ TableEdgeHdl( const Point& rPnt, bool bHorizontal, sal_Int32 nMin, sal_Int32 nMax, sal_Int32 nEdges );
+
+ sal_Int32 GetValidDragOffset( const SdrDragStat& rDrag ) const;
+
+ virtual PointerStyle GetPointer() const override;
+
+ void SetEdge( sal_Int32 nEdge, sal_Int32 nStart, sal_Int32 nEnd, TableEdgeState nState );
+
+ bool IsHorizontalEdge() const { return mbHorizontal; }
+
+ basegfx::B2DPolyPolygon getSpecialDragPoly(const SdrDragStat& rDrag) const;
+ void getPolyPolygon(basegfx::B2DPolyPolygon& rVisible, basegfx::B2DPolyPolygon& rInvisible, const SdrDragStat* pDrag) const;
+
+protected:
+ // create marker for this kind
+ virtual void CreateB2dIAObject() override;
+
+private:
+ bool mbHorizontal;
+ sal_Int32 mnMin, mnMax;
+ std::vector< TableEdge > maEdges;
+};
+
+class TableBorderHdl : public SdrHdl
+{
+public:
+ TableBorderHdl(
+ const tools::Rectangle& rRect,
+ bool bAnimate);
+
+ virtual PointerStyle GetPointer() const override;
+
+protected:
+ // create marker for this kind
+ virtual void CreateB2dIAObject() override;
+
+private:
+ tools::Rectangle maRectangle;
+
+ bool mbAnimate : 1;
+};
+
+} // end of namespace sdr::table
+
+
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/table/tablelayouter.cxx b/svx/source/table/tablelayouter.cxx
new file mode 100644
index 000000000..d846a719c
--- /dev/null
+++ b/svx/source/table/tablelayouter.cxx
@@ -0,0 +1,1311 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * 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/XMergeableCell.hpp>
+
+#include <tools/debug.hxx>
+#include <tools/diagnose_ex.h>
+#include <tools/gen.hxx>
+#include <libxml/xmlwriter.h>
+
+#include <cell.hxx>
+#include <o3tl/safeint.hxx>
+#include <tablemodel.hxx>
+#include "tablelayouter.hxx"
+#include <svx/svdotable.hxx>
+#include <editeng/borderline.hxx>
+#include <editeng/boxitem.hxx>
+
+using ::editeng::SvxBorderLine;
+using namespace ::com::sun::star::uno;
+using namespace ::com::sun::star::lang;
+using namespace ::com::sun::star::container;
+using namespace ::com::sun::star::beans;
+using namespace ::com::sun::star::table;
+using namespace ::com::sun::star::text;
+
+
+namespace sdr::table {
+
+
+static SvxBorderLine gEmptyBorder;
+
+constexpr OUStringLiteral gsSize( u"Size" );
+
+TableLayouter::TableLayouter( const TableModelRef& xTableModel )
+: mxTable( xTableModel )
+{
+}
+
+
+TableLayouter::~TableLayouter()
+{
+ ClearBorderLayout();
+}
+
+
+basegfx::B2ITuple TableLayouter::getCellSize( const CellRef& xCell, const CellPos& rPos ) const
+{
+ sal_Int32 width = 0;
+ sal_Int32 height = 0;
+
+ try
+ {
+ if( xCell.is() && !xCell->isMerged() )
+ {
+ CellPos aPos( rPos );
+
+ sal_Int32 nRowCount = getRowCount();
+ sal_Int32 nRowSpan = std::max( xCell->getRowSpan(), sal_Int32(1) );
+ while( nRowSpan && (aPos.mnRow < nRowCount) )
+ {
+ if( static_cast<sal_Int32>(maRows.size()) <= aPos.mnRow )
+ break;
+
+ height = o3tl::saturating_add(height, maRows[aPos.mnRow++].mnSize);
+ nRowSpan--;
+ }
+
+ sal_Int32 nColCount = getColumnCount();
+ sal_Int32 nColSpan = std::max( xCell->getColumnSpan(), sal_Int32(1) );
+ while( nColSpan && (aPos.mnCol < nColCount ) )
+ {
+ if( static_cast<sal_Int32>(maColumns.size()) <= aPos.mnCol )
+ break;
+
+ width = o3tl::saturating_add(width, maColumns[aPos.mnCol++].mnSize);
+ nColSpan--;
+ }
+ }
+ }
+ catch( Exception& )
+ {
+ TOOLS_WARN_EXCEPTION("svx", "");
+ }
+
+ return basegfx::B2ITuple( width, height );
+}
+
+
+bool TableLayouter::getCellArea( const CellRef& xCell, const CellPos& rPos, basegfx::B2IRectangle& rArea ) const
+{
+ try
+ {
+ if( xCell.is() && !xCell->isMerged() && isValid(rPos) )
+ {
+ const basegfx::B2ITuple aCellSize( getCellSize( xCell, rPos ) );
+ const bool bRTL = (mxTable->getSdrTableObj()->GetWritingMode() == WritingMode_RL_TB);
+
+ if( (rPos.mnCol < static_cast<sal_Int32>(maColumns.size())) && (rPos.mnRow < static_cast<sal_Int32>(maRows.size()) ) )
+ {
+ const sal_Int32 y = maRows[rPos.mnRow].mnPos;
+
+ sal_Int32 endy;
+ if (o3tl::checked_add(y, aCellSize.getY(), endy))
+ return false;
+
+ if(bRTL)
+ {
+ ///For RTL Table Calculate the Right End of cell instead of Left
+ const sal_Int32 x = maColumns[rPos.mnCol].mnPos + maColumns[rPos.mnCol].mnSize;
+ sal_Int32 startx;
+ if (o3tl::checked_sub(x, aCellSize.getX(), startx))
+ return false;
+ rArea = basegfx::B2IRectangle(startx, y, x, endy);
+ }
+ else
+ {
+ const sal_Int32 x = maColumns[rPos.mnCol].mnPos;
+ sal_Int32 endx;
+ if (o3tl::checked_add(x, aCellSize.getX(), endx))
+ return false;
+ rArea = basegfx::B2IRectangle(x, y, endx, endy);
+ }
+ return true;
+ }
+ }
+ }
+ catch( Exception& )
+ {
+ TOOLS_WARN_EXCEPTION("svx", "");
+ }
+ return false;
+}
+
+
+sal_Int32 TableLayouter::getRowHeight( sal_Int32 nRow ) const
+{
+ if( isValidRow(nRow) )
+ return maRows[nRow].mnSize;
+ else
+ return 0;
+}
+
+
+sal_Int32 TableLayouter::getColumnWidth( sal_Int32 nColumn ) const
+{
+ if( isValidColumn(nColumn) )
+ return maColumns[nColumn].mnSize;
+ else
+ return 0;
+}
+
+sal_Int32 TableLayouter::calcPreferredColumnWidth( sal_Int32 nColumn, Size aSize ) const
+{
+ sal_Int32 nRet = 0;
+ for ( sal_uInt32 nRow = 0; nRow < static_cast<sal_uInt32>(maRows.size()); ++nRow )
+ {
+ // Account for the space desired by spanned columns.
+ // Only the last spanned cell will try to ensure sufficient space,
+ // by looping through the previous columns, subtracting their portion.
+ sal_Int32 nWish = 0;
+ sal_Int32 nSpannedColumn = nColumn;
+ bool bFindSpan = true;
+ while ( bFindSpan && isValidColumn(nSpannedColumn) )
+ {
+ // recursive function call gets earlier portion of spanned column.
+ if ( nSpannedColumn < nColumn )
+ nWish -= calcPreferredColumnWidth( nSpannedColumn, aSize );
+
+ CellRef xCell( getCell( CellPos( nSpannedColumn, nRow ) ) );
+ if ( xCell.is() && !xCell->isMerged()
+ && (xCell->getColumnSpan() == 1 || nSpannedColumn < nColumn) )
+ {
+ nWish += xCell->calcPreferredWidth(aSize);
+ bFindSpan = false;
+ }
+ else if ( xCell.is() && xCell->isMerged()
+ && nColumn == nSpannedColumn
+ && isValidColumn(nColumn + 1) )
+ {
+ xCell = getCell( CellPos( nColumn + 1, nRow ) );
+ bFindSpan = xCell.is() && !xCell->isMerged();
+ }
+ nSpannedColumn--;
+ }
+ nRet = std::max( nRet, nWish );
+ }
+ return nRet;
+}
+
+
+bool TableLayouter::isEdgeVisible( sal_Int32 nEdgeX, sal_Int32 nEdgeY, bool bHorizontal ) const
+{
+ const BorderLineMap& rMap = bHorizontal ? maHorizontalBorders : maVerticalBorders;
+
+ if( (nEdgeX >= 0) && (nEdgeX < sal::static_int_cast<sal_Int32>(rMap.size())) &&
+ (nEdgeY >= 0) && (nEdgeY < sal::static_int_cast<sal_Int32>(rMap[nEdgeX].size())) )
+ {
+ return rMap[nEdgeX][nEdgeY] != nullptr;
+ }
+ else
+ {
+ OSL_FAIL( "sdr::table::TableLayouter::getBorderLine(), invalid edge!" );
+ }
+
+ return false;
+}
+
+
+/** returns the requested borderline in rpBorderLine or a null pointer if there is no border at this edge */
+SvxBorderLine* TableLayouter::getBorderLine( sal_Int32 nEdgeX, sal_Int32 nEdgeY, bool bHorizontal )const
+{
+ SvxBorderLine* pLine = nullptr;
+
+ const BorderLineMap& rMap = bHorizontal ? maHorizontalBorders : maVerticalBorders;
+
+ if( (nEdgeX >= 0) && (nEdgeX < sal::static_int_cast<sal_Int32>(rMap.size())) &&
+ (nEdgeY >= 0) && (nEdgeY < sal::static_int_cast<sal_Int32>(rMap[nEdgeX].size())) )
+ {
+ pLine = rMap[nEdgeX][nEdgeY];
+ if( pLine == &gEmptyBorder )
+ pLine = nullptr;
+ }
+ else
+ {
+ OSL_FAIL( "sdr::table::TableLayouter::getBorderLine(), invalid edge!" );
+ }
+
+ return pLine;
+}
+
+std::vector<EdgeInfo> TableLayouter::getHorizontalEdges()
+{
+ std::vector<EdgeInfo> aReturn;
+ sal_Int32 nRowSize = sal_Int32(maRows.size());
+ for (sal_Int32 i = 0; i <= nRowSize; i++)
+ {
+ sal_Int32 nEdgeMin = 0;
+ sal_Int32 nEdgeMax = 0;
+ sal_Int32 nEdge = getHorizontalEdge(i, &nEdgeMin, &nEdgeMax);
+ nEdgeMin -= nEdge;
+ nEdgeMax -= nEdge;
+ aReturn.emplace_back(i, nEdge, nEdgeMin, nEdgeMax);
+ }
+ return aReturn;
+}
+
+std::vector<EdgeInfo> TableLayouter::getVerticalEdges()
+{
+ std::vector<EdgeInfo> aReturn;
+ sal_Int32 nColumnSize = sal_Int32(maColumns.size());
+ for (sal_Int32 i = 0; i <= nColumnSize; i++)
+ {
+ sal_Int32 nEdgeMin = 0;
+ sal_Int32 nEdgeMax = 0;
+ sal_Int32 nEdge = getVerticalEdge(i, &nEdgeMin, &nEdgeMax);
+ nEdgeMin -= nEdge;
+ nEdgeMax -= nEdge;
+ aReturn.emplace_back(i, nEdge, nEdgeMin, nEdgeMax);
+ }
+ return aReturn;
+}
+
+sal_Int32 TableLayouter::getHorizontalEdge( int nEdgeY, sal_Int32* pnMin /*= 0*/, sal_Int32* pnMax /*= 0*/ )
+{
+ sal_Int32 nRet = 0;
+ const sal_Int32 nRowCount = getRowCount();
+ if( (nEdgeY >= 0) && (nEdgeY <= nRowCount ) )
+ nRet = maRows[std::min(static_cast<sal_Int32>(nEdgeY),nRowCount-1)].mnPos;
+
+ if( nEdgeY == nRowCount )
+ nRet += maRows[nEdgeY - 1].mnSize;
+
+ if( pnMin )
+ {
+ if( (nEdgeY > 0) && (nEdgeY <= nRowCount ) )
+ {
+ *pnMin = maRows[nEdgeY-1].mnPos + 600; // todo
+ }
+ else
+ {
+ *pnMin = nRet;
+ }
+ }
+
+ if( pnMax )
+ {
+ *pnMax = 0x0fffffff;
+ }
+ return nRet;
+}
+
+
+sal_Int32 TableLayouter::getVerticalEdge( int nEdgeX, sal_Int32* pnMin /*= 0*/, sal_Int32* pnMax /*= 0*/ )
+{
+ sal_Int32 nRet = 0;
+
+ const sal_Int32 nColCount = getColumnCount();
+ if( (nEdgeX >= 0) && (nEdgeX <= nColCount ) )
+ nRet = maColumns[std::min(static_cast<sal_Int32>(nEdgeX),nColCount-1)].mnPos;
+
+ const bool bRTL = (mxTable->getSdrTableObj()->GetWritingMode() == WritingMode_RL_TB);
+ if( bRTL )
+ {
+ if( (nEdgeX >= 0) && (nEdgeX < nColCount) )
+ nRet += maColumns[nEdgeX].mnSize;
+ }
+ else
+ {
+ if( nEdgeX == nColCount )
+ nRet += maColumns[nEdgeX - 1].mnSize;
+ }
+
+ if( pnMin )
+ {
+ *pnMin = nRet;
+ if( bRTL )
+ {
+ if( nEdgeX < nColCount )
+ *pnMin = nRet - maColumns[nEdgeX].mnSize + getMinimumColumnWidth(nEdgeX);
+ }
+ else
+ {
+ if( (nEdgeX > 0) && (nEdgeX <= nColCount ) )
+ *pnMin = maColumns[nEdgeX-1].mnPos + getMinimumColumnWidth( nEdgeX-1 );
+ }
+ }
+
+ if( pnMax )
+ {
+ *pnMax = 0x0fffffff; // todo
+ if( bRTL )
+ {
+ if( nEdgeX > 0 )
+ *pnMax = nRet + maColumns[nEdgeX-1].mnSize - getMinimumColumnWidth( nEdgeX-1 );
+ }
+ else
+ {
+ if( (nEdgeX >= 0) && (nEdgeX < nColCount ) )
+ *pnMax = maColumns[nEdgeX].mnPos + maColumns[nEdgeX].mnSize - getMinimumColumnWidth( nEdgeX );
+ }
+ }
+
+ return nRet;
+}
+
+
+static bool checkMergeOrigin( const TableModelRef& xTable, sal_Int32 nMergedX, sal_Int32 nMergedY, sal_Int32 nCellX, sal_Int32 nCellY, bool& bRunning )
+{
+ Reference< XMergeableCell > xCell( xTable->getCellByPosition( nCellX, nCellY ), UNO_QUERY );
+ if( xCell.is() && !xCell->isMerged() )
+ {
+ const sal_Int32 nRight = xCell->getColumnSpan() + nCellX;
+ const sal_Int32 nBottom = xCell->getRowSpan() + nCellY;
+ if( (nMergedX < nRight) && (nMergedY < nBottom) )
+ return true;
+
+ bRunning = false;
+ }
+ return false;
+}
+
+/** returns true if the cell(nMergedX,nMergedY) is merged with other cells.
+ the returned cell( rOriginX, rOriginY ) is the origin( top left cell ) of the merge.
+*/
+bool findMergeOrigin( const TableModelRef& xTable, sal_Int32 nMergedX, sal_Int32 nMergedY, sal_Int32& rOriginX, sal_Int32& rOriginY )
+{
+ rOriginX = nMergedX;
+ rOriginY = nMergedY;
+
+ if( xTable.is() ) try
+ {
+ // check if this cell already the origin or not merged at all
+ Reference< XMergeableCell > xCell( xTable->getCellByPosition( nMergedX, nMergedY ), UNO_QUERY_THROW );
+ if( !xCell->isMerged() )
+ return true;
+
+ bool bCheckVert = true;
+ bool bCheckHorz = true;
+
+ sal_Int32 nMinCol = 0;
+ sal_Int32 nMinRow = 0;
+
+ sal_Int32 nStep = 1, i;
+
+ sal_Int32 nRow, nCol;
+ do
+ {
+ if( bCheckVert )
+ {
+ nRow = nMergedY - nStep;
+ if( nRow >= nMinRow )
+ {
+ nCol = nMergedX;
+ for( i = 0; (i <= nStep) && (nCol >= nMinCol); i++, nCol-- )
+ {
+ if( checkMergeOrigin( xTable, nMergedX, nMergedY, nCol, nRow, bCheckVert ) )
+ {
+ rOriginX = nCol; rOriginY = nRow;
+ return true;
+ }
+
+ if( !bCheckVert )
+ {
+ if( nCol == nMergedX )
+ {
+ nMinRow = nRow+1;
+ }
+ else
+ {
+ bCheckVert = true;
+ }
+ break;
+ }
+ }
+ }
+ else
+ {
+ bCheckVert = false;
+ }
+ }
+
+ if( bCheckHorz )
+ {
+ nCol = nMergedX - nStep;
+ if( nCol >= nMinCol )
+ {
+ nRow = nMergedY;
+ for( i = 0; (i < nStep) && (nRow >= nMinRow); i++, nRow-- )
+ {
+ if( checkMergeOrigin( xTable, nMergedX, nMergedY, nCol, nRow, bCheckHorz ) )
+ {
+ rOriginX = nCol; rOriginY = nRow;
+ return true;
+ }
+
+ if( !bCheckHorz )
+ {
+ if( nRow == nMergedY )
+ {
+ nMinCol = nCol+1;
+ }
+ else
+ {
+ bCheckHorz = true;
+ }
+ break;
+ }
+ }
+ }
+ else
+ {
+ bCheckHorz = false;
+ }
+ }
+ nStep++;
+ }
+ while( bCheckVert || bCheckHorz );
+ }
+ catch( Exception& )
+ {
+ TOOLS_WARN_EXCEPTION("svx", "");
+ }
+ return false;
+}
+
+
+sal_Int32 TableLayouter::getMinimumColumnWidth( sal_Int32 nColumn )
+{
+ if( isValidColumn( nColumn ) )
+ {
+ return maColumns[nColumn].mnMinSize;
+ }
+ else
+ {
+ OSL_FAIL( "TableLayouter::getMinimumColumnWidth(), column out of range!" );
+ return 0;
+ }
+}
+
+
+sal_Int32 TableLayouter::distribute( LayoutVector& rLayouts, sal_Int32 nDistribute )
+{
+ // break loops after 100 runs to avoid freezing office due to developer error
+ sal_Int32 nSafe = 100;
+
+ const std::size_t nCount = rLayouts.size();
+ std::size_t nIndex;
+
+ bool bConstrainsBroken = false;
+
+ do
+ {
+ bConstrainsBroken = false;
+
+ // first enforce minimum size constrains on all entities
+ for( nIndex = 0; nIndex < nCount; ++nIndex )
+ {
+ Layout& rLayout = rLayouts[nIndex];
+ if( rLayout.mnSize < rLayout.mnMinSize )
+ {
+ sal_Int32 nDiff(0);
+ bConstrainsBroken |= o3tl::checked_sub(rLayout.mnMinSize, rLayout.mnSize, nDiff);
+ bConstrainsBroken |= o3tl::checked_sub(nDistribute, nDiff, nDistribute);
+ rLayout.mnSize = rLayout.mnMinSize;
+ }
+ }
+
+ // calculate current width
+ // if nDistribute is < 0 (shrinking), entities that are already
+ // at minimum width are not counted
+ sal_Int32 nCurrentWidth = 0;
+ for( nIndex = 0; nIndex < nCount; ++nIndex )
+ {
+ Layout& rLayout = rLayouts[nIndex];
+ if( (nDistribute > 0) || (rLayout.mnSize > rLayout.mnMinSize) )
+ nCurrentWidth = o3tl::saturating_add(nCurrentWidth, rLayout.mnSize);
+ }
+
+ // now distribute over entities
+ if( (nCurrentWidth != 0) && (nDistribute != 0) )
+ {
+ sal_Int32 nDistributed = nDistribute;
+ for( nIndex = 0; nIndex < nCount; ++nIndex )
+ {
+ Layout& rLayout = rLayouts[nIndex];
+ if( (nDistribute > 0) || (rLayout.mnSize > rLayout.mnMinSize) )
+ {
+ sal_Int32 n(nDistributed); // for last entity use up rest
+ if (nIndex != (nCount-1))
+ {
+ bConstrainsBroken |= o3tl::checked_multiply(nDistribute, rLayout.mnSize, n);
+ n /= nCurrentWidth;
+ }
+
+ bConstrainsBroken |= o3tl::checked_add(rLayout.mnSize, n, rLayout.mnSize);
+ nDistributed -= n;
+
+ if( rLayout.mnSize < rLayout.mnMinSize )
+ bConstrainsBroken = true;
+ }
+ }
+ }
+ } while( bConstrainsBroken && --nSafe );
+
+ sal_Int32 nSize = 0;
+ for( nIndex = 0; nIndex < nCount; ++nIndex )
+ nSize = o3tl::saturating_add(nSize, rLayouts[nIndex].mnSize);
+
+ return nSize;
+}
+
+
+typedef std::vector< CellRef > MergeableCellVector;
+typedef std::vector< MergeableCellVector > MergeVector;
+
+
+void TableLayouter::LayoutTableWidth( tools::Rectangle& rArea, bool bFit )
+{
+ const sal_Int32 nColCount = getColumnCount();
+ const sal_Int32 nRowCount = getRowCount();
+ if( nColCount == 0 )
+ return;
+
+ MergeVector aMergedCells( nColCount );
+ std::vector<sal_Int32> aOptimalColumns;
+
+ static const OUStringLiteral sOptimalSize(u"OptimalSize");
+
+ if( sal::static_int_cast< sal_Int32 >( maColumns.size() ) != nColCount )
+ maColumns.resize( nColCount );
+
+ Reference< XTableColumns > xCols( mxTable->getColumns(), UNO_SET_THROW );
+
+ // first calculate current width and initial minimum width per column,
+ // merged cells will be counted later
+ sal_Int32 nCurrentWidth = 0;
+ sal_Int32 nCol = 0, nRow = 0;
+ for( nCol = 0; nCol < nColCount; nCol++ )
+ {
+ sal_Int32 nMinWidth = 0;
+
+ bool bIsEmpty = true; // check if all cells in this column are merged
+
+ for( nRow = 0; nRow < nRowCount; ++nRow )
+ {
+ CellRef xCell( getCell( CellPos( nCol, nRow ) ) );
+ if( xCell.is() && !xCell->isMerged() )
+ {
+ bIsEmpty = false;
+
+ sal_Int32 nColSpan = xCell->getColumnSpan();
+ if( nColSpan > 1 )
+ {
+ // merged cells will be evaluated later
+ aMergedCells[nCol+nColSpan-1].push_back( xCell );
+ }
+ else
+ {
+ nMinWidth = std::max( nMinWidth, xCell->getMinimumWidth() );
+ }
+ }
+ }
+
+ maColumns[nCol].mnMinSize = nMinWidth;
+
+ if( bIsEmpty )
+ {
+ maColumns[nCol].mnSize = 0;
+ }
+ else
+ {
+ sal_Int32 nColWidth = 0;
+ Reference< XPropertySet > xColSet( xCols->getByIndex( nCol ), UNO_QUERY_THROW );
+ bool bOptimal = false;
+ xColSet->getPropertyValue( sOptimalSize ) >>= bOptimal;
+ if( bOptimal )
+ {
+ aOptimalColumns.push_back(nCol);
+ }
+ else
+ {
+ xColSet->getPropertyValue( gsSize ) >>= nColWidth;
+ }
+
+ maColumns[nCol].mnSize = std::max( nColWidth, nMinWidth);
+
+ nCurrentWidth = o3tl::saturating_add(nCurrentWidth, maColumns[nCol].mnSize);
+ }
+ }
+
+ // if we have optimal sized rows, distribute what is given (left)
+ if( !bFit && !aOptimalColumns.empty() && (nCurrentWidth < rArea.getWidth()) )
+ {
+ sal_Int32 nLeft = rArea.getWidth() - nCurrentWidth;
+ sal_Int32 nDistribute = nLeft / aOptimalColumns.size();
+
+ auto iter( aOptimalColumns.begin() );
+ while( iter != aOptimalColumns.end() )
+ {
+ sal_Int32 nOptCol = *iter++;
+ if( iter == aOptimalColumns.end() )
+ nDistribute = nLeft;
+
+ maColumns[nOptCol].mnSize += nDistribute;
+ nLeft -= nDistribute;
+ }
+
+ DBG_ASSERT( nLeft == 0, "svx::TableLayouter::LayoutTableWidtht(), layouting failed!" );
+ }
+
+ // now check if merged cells fit
+ for( nCol = 1; nCol < nColCount; ++nCol )
+ {
+ bool bChanges = false;
+
+ const sal_Int32 nOldSize = maColumns[nCol].mnSize;
+
+ for( const CellRef& xCell : aMergedCells[nCol] )
+ {
+ sal_Int32 nMinWidth = xCell->getMinimumWidth();
+
+ for( sal_Int32 nMCol = nCol - xCell->getColumnSpan() + 1; (nMCol > 0) && (nMCol < nCol); ++nMCol )
+ nMinWidth -= maColumns[nMCol].mnSize;
+
+ if( nMinWidth > maColumns[nCol].mnMinSize )
+ maColumns[nCol].mnMinSize = nMinWidth;
+
+ if( nMinWidth > maColumns[nCol].mnSize )
+ {
+ maColumns[nCol].mnSize = nMinWidth;
+ bChanges = true;
+ }
+ }
+
+ if( bChanges )
+ {
+ nCurrentWidth = o3tl::saturating_add(nCurrentWidth, maColumns[nCol].mnSize - nOldSize);
+ }
+ }
+
+ // now scale if wanted and needed
+ if( bFit && (nCurrentWidth != rArea.getWidth()) )
+ distribute( maColumns, rArea.getWidth() - nCurrentWidth );
+
+ // last step, update left edges
+ sal_Int32 nNewWidth = 0;
+
+ const bool bRTL = (mxTable->getSdrTableObj()->GetWritingMode() == WritingMode_RL_TB);
+ RangeIterator<sal_Int32> coliter( 0, nColCount, !bRTL );
+ while( coliter.next(nCol ) )
+ {
+ maColumns[nCol].mnPos = nNewWidth;
+ nNewWidth = o3tl::saturating_add(nNewWidth, maColumns[nCol].mnSize);
+ if( bFit )
+ {
+ Reference< XPropertySet > xColSet( xCols->getByIndex(nCol), UNO_QUERY_THROW );
+ xColSet->setPropertyValue( gsSize, Any( maColumns[nCol].mnSize ) );
+ }
+ }
+
+ rArea.SetSize( Size( nNewWidth, rArea.GetHeight() ) );
+ updateCells( rArea );
+}
+
+
+void TableLayouter::LayoutTableHeight( tools::Rectangle& rArea, bool bFit )
+{
+ const sal_Int32 nColCount = getColumnCount();
+ const sal_Int32 nRowCount = getRowCount();
+
+ if( nRowCount == 0 )
+ return;
+
+ Reference< XTableRows > xRows( mxTable->getRows() );
+
+ MergeVector aMergedCells( nRowCount );
+ std::vector<sal_Int32> aOptimalRows;
+
+ static const OUStringLiteral sOptimalSize(u"OptimalSize");
+
+ // first calculate current height and initial minimum size per column,
+ // merged cells will be counted later
+ sal_Int32 nCurrentHeight = 0;
+ sal_Int32 nCol, nRow;
+ for( nRow = 0; nRow < nRowCount; ++nRow )
+ {
+ sal_Int32 nMinHeight = 0;
+
+ bool bIsEmpty = true; // check if all cells in this row are merged
+
+ for( nCol = 0; nCol < nColCount; ++nCol )
+ {
+ CellRef xCell( getCell( CellPos( nCol, nRow ) ) );
+ if( xCell.is() && !xCell->isMerged() )
+ {
+ bIsEmpty = false;
+
+ sal_Int32 nRowSpan = xCell->getRowSpan();
+ if( nRowSpan > 1 )
+ {
+ // merged cells will be evaluated later
+ aMergedCells[nRow+nRowSpan-1].push_back( xCell );
+ }
+ else
+ {
+ nMinHeight = std::max( nMinHeight, xCell->getMinimumHeight() );
+ }
+ }
+ }
+
+ maRows[nRow].mnMinSize = nMinHeight;
+
+ if( bIsEmpty )
+ {
+ maRows[nRow].mnSize = 0;
+ }
+ else
+ {
+ sal_Int32 nRowHeight = 0;
+ Reference<XPropertySet> xRowSet(xRows->getByIndex(nRow), UNO_QUERY_THROW);
+
+ bool bOptimal = false;
+ xRowSet->getPropertyValue( sOptimalSize ) >>= bOptimal;
+ if( bOptimal )
+ {
+ aOptimalRows.push_back( nRow );
+ }
+ else
+ {
+ xRowSet->getPropertyValue( gsSize ) >>= nRowHeight;
+ }
+
+ maRows[nRow].mnSize = nRowHeight;
+
+ if( maRows[nRow].mnSize < nMinHeight )
+ maRows[nRow].mnSize = nMinHeight;
+
+ nCurrentHeight = o3tl::saturating_add(nCurrentHeight, maRows[nRow].mnSize);
+ }
+ }
+
+ // if we have optimal sized rows, distribute what is given (left)
+ if( !bFit && !aOptimalRows.empty() && (nCurrentHeight < rArea.getHeight()) )
+ {
+ sal_Int32 nLeft = rArea.getHeight() - nCurrentHeight;
+ sal_Int32 nDistribute = nLeft / aOptimalRows.size();
+
+ auto iter( aOptimalRows.begin() );
+ while( iter != aOptimalRows.end() )
+ {
+ sal_Int32 nOptRow = *iter++;
+ if( iter == aOptimalRows.end() )
+ nDistribute = nLeft;
+
+ maRows[nOptRow].mnSize += nDistribute;
+ nLeft -= nDistribute;
+
+ }
+
+ DBG_ASSERT( nLeft == 0, "svx::TableLayouter::LayoutTableHeight(), layouting failed!" );
+ }
+
+ // now check if merged cells fit
+ for( nRow = 1; nRow < nRowCount; ++nRow )
+ {
+ bool bChanges = false;
+ sal_Int32 nOldSize = maRows[nRow].mnSize;
+
+ for( const CellRef& xCell : aMergedCells[nRow] )
+ {
+ sal_Int32 nMinHeight = xCell->getMinimumHeight();
+
+ for( sal_Int32 nMRow = nRow - xCell->getRowSpan() + 1; (nMRow > 0) && (nMRow < nRow); ++nMRow )
+ nMinHeight -= maRows[nMRow].mnSize;
+
+ if( nMinHeight > maRows[nRow].mnMinSize )
+ maRows[nRow].mnMinSize = nMinHeight;
+
+ if( nMinHeight > maRows[nRow].mnSize )
+ {
+ maRows[nRow].mnSize = nMinHeight;
+ bChanges = true;
+ }
+ }
+ if( bChanges )
+ nCurrentHeight = o3tl::saturating_add(nCurrentHeight, maRows[nRow].mnSize - nOldSize);
+ }
+
+ // now scale if wanted and needed
+ if( bFit && nCurrentHeight != rArea.getHeight() )
+ distribute(maRows, o3tl::saturating_sub<sal_Int32>(rArea.getHeight(), nCurrentHeight));
+
+ // last step, update left edges
+ sal_Int32 nNewHeight = 0;
+ for( nRow = 0; nRow < nRowCount; ++nRow )
+ {
+ maRows[nRow].mnPos = nNewHeight;
+ nNewHeight = o3tl::saturating_add(nNewHeight, maRows[nRow].mnSize);
+
+ if( bFit )
+ {
+ Reference< XPropertySet > xRowSet( xRows->getByIndex(nRow), UNO_QUERY_THROW );
+ xRowSet->setPropertyValue( gsSize, Any( maRows[nRow].mnSize ) );
+ }
+ }
+
+ rArea.SetSize( Size( rArea.GetWidth(), nNewHeight ) );
+ updateCells( rArea );
+}
+
+
+/** try to fit the table into the given rectangle.
+ If the rectangle is too small, it will be grown to fit the table. */
+void TableLayouter::LayoutTable( tools::Rectangle& rRectangle, bool bFitWidth, bool bFitHeight )
+{
+ if( !mxTable.is() )
+ return;
+
+ const sal_Int32 nRowCount = mxTable->getRowCount();
+ const sal_Int32 nColCount = mxTable->getColumnCount();
+
+ if( (nRowCount != getRowCount()) || (nColCount != getColumnCount()) )
+ {
+ if( static_cast< sal_Int32 >( maRows.size() ) != nRowCount )
+ maRows.resize( nRowCount );
+
+ for( sal_Int32 nRow = 0; nRow < nRowCount; nRow++ )
+ maRows[nRow].clear();
+
+ if( static_cast< sal_Int32 >( maColumns.size() ) != nColCount )
+ maColumns.resize( nColCount );
+
+ for( sal_Int32 nCol = 0; nCol < nColCount; nCol++ )
+ maColumns[nCol].clear();
+ }
+
+ LayoutTableWidth( rRectangle, bFitWidth );
+ LayoutTableHeight( rRectangle, bFitHeight );
+ UpdateBorderLayout();
+}
+
+
+void TableLayouter::updateCells( tools::Rectangle const & rRectangle )
+{
+ const sal_Int32 nColCount = getColumnCount();
+ const sal_Int32 nRowCount = getRowCount();
+
+ CellPos aPos;
+ for( aPos.mnRow = 0; aPos.mnRow < nRowCount; aPos.mnRow++ )
+ {
+ for( aPos.mnCol = 0; aPos.mnCol < nColCount; aPos.mnCol++ )
+ {
+ CellRef xCell( getCell( aPos ) );
+ if( xCell.is() )
+ {
+ basegfx::B2IRectangle aCellArea;
+ if( getCellArea( xCell, aPos, aCellArea ) )
+ {
+ tools::Rectangle aCellRect;
+ aCellRect.SetLeft( aCellArea.getMinX() );
+ aCellRect.SetRight( aCellArea.getMaxX() );
+ aCellRect.SetTop( aCellArea.getMinY() );
+ aCellRect.SetBottom( aCellArea.getMaxY() );
+ aCellRect.Move( rRectangle.Left(), rRectangle.Top() );
+ xCell->setCellRect( aCellRect );
+ }
+ }
+ }
+ }
+}
+
+
+CellRef TableLayouter::getCell( const CellPos& rPos ) const
+{
+ CellRef xCell;
+ if( mxTable.is() ) try
+ {
+ xCell.set( dynamic_cast< Cell* >( mxTable->getCellByPosition( rPos.mnCol, rPos.mnRow ).get() ) );
+ }
+ catch( Exception& )
+ {
+ TOOLS_WARN_EXCEPTION("svx", "");
+ }
+ return xCell;
+}
+
+
+bool TableLayouter::HasPriority( const SvxBorderLine* pThis, const SvxBorderLine* pOther )
+{
+ if (!pThis || ((pThis == &gEmptyBorder) && (pOther != nullptr)))
+ return false;
+ if (!pOther || (pOther == &gEmptyBorder))
+ return true;
+
+ sal_uInt16 nThisSize = pThis->GetScaledWidth();
+ sal_uInt16 nOtherSize = pOther->GetScaledWidth();
+
+ if (nThisSize > nOtherSize)
+ return true;
+
+ else if (nThisSize < nOtherSize)
+ {
+ return false;
+ }
+ else
+ {
+ if ( pOther->GetInWidth() && !pThis->GetInWidth() )
+ {
+ return true;
+ }
+ else if ( pThis->GetInWidth() && !pOther->GetInWidth() )
+ {
+ return false;
+ }
+ else
+ {
+ return true; //! ???
+ }
+ }
+}
+
+void TableLayouter::SetBorder( sal_Int32 nCol, sal_Int32 nRow, bool bHorizontal, const SvxBorderLine* pLine )
+{
+ if (!pLine)
+ pLine = &gEmptyBorder;
+
+ BorderLineMap& rMap = bHorizontal ? maHorizontalBorders : maVerticalBorders;
+
+ if( (nCol >= 0) && (nCol < sal::static_int_cast<sal_Int32>(rMap.size())) &&
+ (nRow >= 0) && (nRow < sal::static_int_cast<sal_Int32>(rMap[nCol].size())) )
+ {
+ SvxBorderLine *pOld = rMap[nCol][nRow];
+
+ if (HasPriority(pLine, pOld))
+ {
+ if (pOld && pOld != &gEmptyBorder)
+ delete pOld;
+
+ SvxBorderLine* pNew = (pLine != &gEmptyBorder) ? new SvxBorderLine(*pLine) : &gEmptyBorder;
+
+ rMap[nCol][nRow] = pNew;
+ }
+ }
+ else
+ {
+ OSL_FAIL( "sdr::table::TableLayouter::SetBorder(), invalid border!" );
+ }
+}
+
+void TableLayouter::ClearBorderLayout()
+{
+ ClearBorderLayout(maHorizontalBorders);
+ ClearBorderLayout(maVerticalBorders);
+}
+
+void TableLayouter::ClearBorderLayout(BorderLineMap& rMap)
+{
+ const sal_Int32 nColCount = rMap.size();
+
+ for( sal_Int32 nCol = 0; nCol < nColCount; nCol++ )
+ {
+ const sal_Int32 nRowCount = rMap[nCol].size();
+ for( sal_Int32 nRow = 0; nRow < nRowCount; nRow++ )
+ {
+ SvxBorderLine* pLine = rMap[nCol][nRow];
+ if( pLine )
+ {
+ if( pLine != &gEmptyBorder )
+ delete pLine;
+
+ rMap[nCol][nRow] = nullptr;
+ }
+ }
+ }
+}
+
+void TableLayouter::ResizeBorderLayout()
+{
+ ClearBorderLayout();
+ ResizeBorderLayout(maHorizontalBorders);
+ ResizeBorderLayout(maVerticalBorders);
+}
+
+
+void TableLayouter::ResizeBorderLayout( BorderLineMap& rMap )
+{
+ const sal_Int32 nColCount = getColumnCount() + 1;
+ const sal_Int32 nRowCount = getRowCount() + 1;
+
+ if( sal::static_int_cast<sal_Int32>(rMap.size()) != nColCount )
+ rMap.resize( nColCount );
+
+ for( sal_Int32 nCol = 0; nCol < nColCount; nCol++ )
+ {
+ if( sal::static_int_cast<sal_Int32>(rMap[nCol].size()) != nRowCount )
+ rMap[nCol].resize( nRowCount );
+ }
+}
+
+
+void TableLayouter::UpdateBorderLayout()
+{
+ // make sure old border layout is cleared and border maps have correct size
+ ResizeBorderLayout();
+
+ const sal_Int32 nColCount = getColumnCount();
+ const sal_Int32 nRowCount = getRowCount();
+
+ CellPos aPos;
+ for( aPos.mnRow = 0; aPos.mnRow < nRowCount; aPos.mnRow++ )
+ {
+ for( aPos.mnCol = 0; aPos.mnCol < nColCount; aPos.mnCol++ )
+ {
+ CellRef xCell( getCell( aPos ) );
+ if( !xCell.is() )
+ continue;
+
+ const SvxBoxItem* pThisAttr = xCell->GetItemSet().GetItem<SvxBoxItem>( SDRATTR_TABLE_BORDER );
+ OSL_ENSURE(pThisAttr,"sdr::table::TableLayouter::UpdateBorderLayout(), no border attribute?");
+
+ if( !pThisAttr )
+ continue;
+
+ const sal_Int32 nLastRow = xCell->getRowSpan() + aPos.mnRow;
+ const sal_Int32 nLastCol = xCell->getColumnSpan() + aPos.mnCol;
+
+ for( sal_Int32 nRow = aPos.mnRow; nRow < nLastRow; nRow++ )
+ {
+ SetBorder( aPos.mnCol, nRow, false, pThisAttr->GetLeft() );
+ SetBorder( nLastCol, nRow, false, pThisAttr->GetRight() );
+ }
+
+ for( sal_Int32 nCol = aPos.mnCol; nCol < nLastCol; nCol++ )
+ {
+ SetBorder( nCol, aPos.mnRow, true, pThisAttr->GetTop() );
+ SetBorder( nCol, nLastRow, true, pThisAttr->GetBottom() );
+ }
+ }
+ }
+}
+
+
+void TableLayouter::DistributeColumns( ::tools::Rectangle& rArea,
+ sal_Int32 nFirstCol,
+ sal_Int32 nLastCol,
+ const bool bOptimize,
+ const bool bMinimize )
+{
+ if( !mxTable.is() )
+ return;
+
+ try
+ {
+ const sal_Int32 nColCount = getColumnCount();
+ Reference< XTableColumns > xCols( mxTable->getColumns(), UNO_SET_THROW );
+ const Size aSize(0xffffff, 0xffffff);
+
+ //special case - optimize a single column
+ if ( (bOptimize || bMinimize) && nFirstCol == nLastCol )
+ {
+ const sal_Int32 nWish = calcPreferredColumnWidth(nFirstCol, aSize);
+ if ( nWish < getColumnWidth(nFirstCol) )
+ {
+ Reference< XPropertySet > xColSet( xCols->getByIndex(nFirstCol), UNO_QUERY_THROW );
+ xColSet->setPropertyValue( gsSize, Any( nWish ) );
+
+ //FitWidth automatically distributes the new excess space
+ LayoutTable( rArea, /*bFitWidth=*/!bMinimize, /*bFitHeight=*/false );
+ }
+ }
+
+ if( (nFirstCol < 0) || (nFirstCol>= nLastCol) || (nLastCol >= nColCount) )
+ return;
+
+ sal_Int32 nAllWidth = 0;
+ float fAllWish = 0;
+ sal_Int32 nUnused = 0;
+ std::vector<sal_Int32> aWish(nColCount);
+
+ for( sal_Int32 nCol = nFirstCol; nCol <= nLastCol; ++nCol )
+ nAllWidth += getColumnWidth(nCol);
+
+ const sal_Int32 nEqualWidth = nAllWidth / (nLastCol-nFirstCol+1);
+
+ //pass 1 - collect unneeded space (from an equal width perspective)
+ if ( bMinimize || bOptimize )
+ {
+ for( sal_Int32 nCol = nFirstCol; nCol <= nLastCol; ++nCol )
+ {
+ const sal_Int32 nIndex = nCol - nFirstCol;
+ aWish[nIndex] = calcPreferredColumnWidth(nCol, aSize);
+ fAllWish += aWish[nIndex];
+ if ( aWish[nIndex] < nEqualWidth )
+ nUnused += nEqualWidth - aWish[nIndex];
+ }
+ }
+ const sal_Int32 nDistributeExcess = nAllWidth - fAllWish;
+
+ sal_Int32 nWidth = nEqualWidth;
+ for( sal_Int32 nCol = nFirstCol; nCol <= nLastCol; ++nCol )
+ {
+ if ( !bMinimize && nCol == nLastCol )
+ nWidth = nAllWidth; // last column gets rounding/logic errors
+ else if ( (bMinimize || bOptimize) && fAllWish )
+ {
+ //pass 2 - first come, first served when requesting from the
+ // unneeded pool, or proportionally allocate excess.
+ const sal_Int32 nIndex = nCol - nFirstCol;
+ if ( aWish[nIndex] > nEqualWidth + nUnused )
+ {
+ nWidth = nEqualWidth + nUnused;
+ nUnused = 0;
+ }
+ else
+ {
+ nWidth = aWish[nIndex];
+ if ( aWish[nIndex] > nEqualWidth )
+ nUnused -= aWish[nIndex] - nEqualWidth;
+
+ if ( !bMinimize && nDistributeExcess > 0 )
+ nWidth += nWidth / fAllWish * nDistributeExcess;
+ }
+ }
+
+ Reference< XPropertySet > xColSet( xCols->getByIndex( nCol ), UNO_QUERY_THROW );
+ xColSet->setPropertyValue( gsSize, Any( nWidth ) );
+
+ nAllWidth -= nWidth;
+ }
+
+ LayoutTable( rArea, !bMinimize, false );
+ }
+ catch( Exception& )
+ {
+ TOOLS_WARN_EXCEPTION("svx", "");
+ }
+}
+
+
+void TableLayouter::DistributeRows( ::tools::Rectangle& rArea,
+ sal_Int32 nFirstRow,
+ sal_Int32 nLastRow,
+ const bool bOptimize,
+ const bool bMinimize )
+{
+ if( !mxTable.is() )
+ return;
+
+ try
+ {
+ const sal_Int32 nRowCount = mxTable->getRowCount();
+ Reference< XTableRows > xRows( mxTable->getRows(), UNO_SET_THROW );
+ sal_Int32 nMinHeight = 0;
+
+ //special case - minimize a single row
+ if ( bMinimize && nFirstRow == nLastRow )
+ {
+ const sal_Int32 nWish = std::max( maRows[nFirstRow].mnMinSize, nMinHeight );
+ if ( nWish < getRowHeight(nFirstRow) )
+ {
+ Reference< XPropertySet > xRowSet( xRows->getByIndex( nFirstRow ), UNO_QUERY_THROW );
+ xRowSet->setPropertyValue( gsSize, Any( nWish ) );
+
+ LayoutTable( rArea, /*bFitWidth=*/false, /*bFitHeight=*/!bMinimize );
+ }
+ }
+
+ if( (nFirstRow < 0) || (nFirstRow>= nLastRow) || (nLastRow >= nRowCount) )
+ return;
+
+ sal_Int32 nAllHeight = 0;
+ sal_Int32 nMaxHeight = 0;
+
+ for( sal_Int32 nRow = nFirstRow; nRow <= nLastRow; ++nRow )
+ {
+ nMinHeight = std::max( maRows[nRow].mnMinSize, nMinHeight );
+ nMaxHeight = std::max( maRows[nRow].mnSize, nMaxHeight );
+ nAllHeight += maRows[nRow].mnSize;
+ }
+
+ const sal_Int32 nRows = nLastRow-nFirstRow+1;
+ sal_Int32 nHeight = nAllHeight / nRows;
+
+ if ( !bMinimize && nHeight < nMaxHeight )
+ {
+ if ( !bOptimize )
+ {
+ sal_Int32 nNeededHeight = nRows * nMaxHeight;
+ rArea.AdjustBottom(nNeededHeight - nAllHeight );
+ nHeight = nMaxHeight;
+ nAllHeight = nRows * nMaxHeight;
+ }
+ else if ( nHeight < nMinHeight )
+ {
+ sal_Int32 nNeededHeight = nRows * nMinHeight;
+ rArea.AdjustBottom(nNeededHeight - nAllHeight );
+ nHeight = nMinHeight;
+ nAllHeight = nRows * nMinHeight;
+ }
+ }
+
+ for( sal_Int32 nRow = nFirstRow; nRow <= nLastRow; ++nRow )
+ {
+ if ( bMinimize )
+ nHeight = maRows[nRow].mnMinSize;
+ else if ( nRow == nLastRow )
+ nHeight = nAllHeight; // last row get round errors
+
+ Reference< XPropertySet > xRowSet( xRows->getByIndex( nRow ), UNO_QUERY_THROW );
+ xRowSet->setPropertyValue( gsSize, Any( nHeight ) );
+
+ nAllHeight -= nHeight;
+ }
+
+ LayoutTable( rArea, false, !bMinimize );
+ }
+ catch( Exception& )
+ {
+ TOOLS_WARN_EXCEPTION("svx", "");
+ }
+}
+
+void TableLayouter::dumpAsXml(xmlTextWriterPtr pWriter) const
+{
+ (void)xmlTextWriterStartElement(pWriter, BAD_CAST("TableLayouter"));
+
+ (void)xmlTextWriterStartElement(pWriter, BAD_CAST("columns"));
+ for (const auto& rColumn : maColumns)
+ rColumn.dumpAsXml(pWriter);
+ (void)xmlTextWriterEndElement(pWriter);
+
+ (void)xmlTextWriterStartElement(pWriter, BAD_CAST("rows"));
+ for (const auto& rRow : maRows)
+ rRow.dumpAsXml(pWriter);
+ (void)xmlTextWriterEndElement(pWriter);
+
+ (void)xmlTextWriterEndElement(pWriter);
+}
+
+void TableLayouter::Layout::dumpAsXml(xmlTextWriterPtr pWriter) const
+{
+ (void)xmlTextWriterStartElement(pWriter, BAD_CAST("TableLayouter_Layout"));
+
+ (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("pos"), BAD_CAST(OString::number(mnPos).getStr()));
+ (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("size"), BAD_CAST(OString::number(mnSize).getStr()));
+ (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("minSize"), BAD_CAST(OString::number(mnMinSize).getStr()));
+
+ (void)xmlTextWriterEndElement(pWriter);
+}
+
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/table/tablelayouter.hxx b/svx/source/table/tablelayouter.hxx
new file mode 100644
index 000000000..2ff69598e
--- /dev/null
+++ b/svx/source/table/tablelayouter.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 .
+ */
+
+#ifndef INCLUDED_SVX_SOURCE_TABLE_TABLELAYOUTER_HXX
+#define INCLUDED_SVX_SOURCE_TABLE_TABLELAYOUTER_HXX
+
+#include <sal/types.h>
+#include <basegfx/range/b2irectangle.hxx>
+#include <basegfx/tuple/b2ituple.hxx>
+#include <o3tl/safeint.hxx>
+#include <vector>
+
+#include <svx/svdotable.hxx>
+#include <celltypes.hxx>
+
+namespace tools { class Rectangle; }
+
+
+namespace editeng {
+ class SvxBorderLine;
+}
+
+namespace sdr::table {
+
+/** returns true if the cell(nMergedCol,nMergedRow) is merged with other cells.
+ the returned cell( rOriginCol, rOriginRow ) is the origin( top left cell ) of the merge.
+*/
+bool findMergeOrigin( const TableModelRef& xTable, sal_Int32 nMergedCol, sal_Int32 nMergedRow, sal_Int32& rOriginCol, sal_Int32& rOriginRow );
+
+typedef std::vector< editeng::SvxBorderLine* > BorderLineVector;
+typedef std::vector< BorderLineVector > BorderLineMap;
+
+
+// TableModel
+
+struct EdgeInfo final
+{
+ sal_Int32 nIndex;
+ sal_Int32 nPosition;
+ sal_Int32 nMin;
+ sal_Int32 nMax;
+
+ EdgeInfo(sal_Int32 nInIndex, sal_Int32 nInPosition, sal_Int32 nInMin, sal_Int32 nInMax)
+ : nIndex(nInIndex)
+ , nPosition(nInPosition)
+ , nMin(nInMin)
+ , nMax(nInMax)
+ {}
+};
+
+class TableLayouter final
+{
+public:
+ explicit TableLayouter( const TableModelRef& xTableModel );
+ ~TableLayouter();
+
+ /** try to fit the table into the given rectangle.
+ If the rectangle is too small, it will be grown to fit the table.
+
+ if bFitWidth or bFitHeight is set, the layouter tries to scale
+ the rows and/or columns to the given area. The result my be bigger
+ to fulfill constrains.
+
+ if bFitWidth or bFitHeight is set, the model is changed.
+ */
+ void LayoutTable( ::tools::Rectangle& rRectangle, bool bFitWidth, bool bFitHeight );
+
+ void UpdateBorderLayout();
+
+ bool getCellArea( const CellRef& xCell, const CellPos& rPos, basegfx::B2IRectangle& rArea ) const;
+
+ ::sal_Int32 getRowCount() const { return static_cast< ::sal_Int32 >( maRows.size() ); }
+ ::sal_Int32 getColumnCount() const { return static_cast< ::sal_Int32 >( maColumns.size() ); }
+ sal_Int32 getRowHeight( sal_Int32 nRow ) const;
+
+ sal_Int32 getColumnWidth( sal_Int32 nColumn ) const;
+ sal_Int32 calcPreferredColumnWidth( sal_Int32 nColumn, Size aSize ) const;
+
+ sal_Int32 getMinimumColumnWidth( sal_Int32 nColumn );
+
+ /** checks if the given edge is visible.
+ Edges between merged cells are not visible.
+ */
+ bool isEdgeVisible( sal_Int32 nEdgeX, sal_Int32 nEdgeY, bool bHorizontal ) const;
+
+ /** returns the requested borderline in rpBorderLine or a null pointer if there is no border at this edge */
+ editeng::SvxBorderLine* getBorderLine( sal_Int32 nEdgeX, sal_Int32 nEdgeY, bool bHorizontal )const;
+
+ void updateCells( ::tools::Rectangle const & rRectangle );
+
+ std::vector<EdgeInfo> getHorizontalEdges();
+ sal_Int32 getHorizontalEdge( int nEdgeY, sal_Int32* pnMin, sal_Int32* pnMax );
+
+ std::vector<EdgeInfo> getVerticalEdges();
+ sal_Int32 getVerticalEdge( int nEdgeX , sal_Int32* pnMin, sal_Int32* pnMax);
+
+
+ void DistributeColumns( ::tools::Rectangle& rArea,
+ sal_Int32 nFirstCol,
+ sal_Int32 nLastCol,
+ const bool bOptimize,
+ const bool bMinimize );
+ void DistributeRows( ::tools::Rectangle& rArea,
+ sal_Int32 nFirstRow,
+ sal_Int32 nLastRow,
+ const bool bOptimize,
+ const bool bMinimize );
+ void dumpAsXml(xmlTextWriterPtr pWriter) const;
+
+ void LayoutTableWidth(::tools::Rectangle& rArea, bool bFit);
+ void LayoutTableHeight(::tools::Rectangle& rArea, bool bFit);
+
+private:
+ CellRef getCell( const CellPos& rPos ) const;
+ basegfx::B2ITuple getCellSize( const CellRef& xCell, const CellPos& rPos ) const;
+
+ bool isValidColumn( sal_Int32 nColumn ) const { return (nColumn >= 0) && (o3tl::make_unsigned(nColumn) < maColumns.size()); }
+ bool isValidRow( sal_Int32 nRow ) const { return (nRow >= 0) && (o3tl::make_unsigned(nRow) < maRows.size()); }
+ bool isValid( const CellPos& rPos ) const { return isValidColumn( rPos.mnCol ) && isValidRow( rPos.mnRow ); }
+
+ void ClearBorderLayout();
+ static void ClearBorderLayout(BorderLineMap& rMap);
+ void ResizeBorderLayout();
+ void ResizeBorderLayout( BorderLineMap& rMap );
+
+ void SetBorder( sal_Int32 nCol, sal_Int32 nRow, bool bHorizontal, const editeng::SvxBorderLine* pLine );
+
+ static bool HasPriority( const editeng::SvxBorderLine* pThis, const editeng::SvxBorderLine* pOther );
+
+ struct Layout
+ {
+ sal_Int32 mnPos;
+ sal_Int32 mnSize;
+ sal_Int32 mnMinSize;
+
+ Layout() : mnPos( 0 ), mnSize( 0 ), mnMinSize( 0 ) {}
+ void clear() { mnPos = 0; mnSize = 0; mnMinSize = 0; }
+ void dumpAsXml(xmlTextWriterPtr pWriter) const;
+ };
+ typedef std::vector< Layout > LayoutVector;
+
+ static sal_Int32 distribute( LayoutVector& rLayouts, sal_Int32 nDistribute );
+
+ TableModelRef mxTable;
+ LayoutVector maRows;
+ LayoutVector maColumns;
+
+ BorderLineMap maHorizontalBorders;
+ BorderLineMap maVerticalBorders;
+};
+
+}
+
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/table/tablemodel.cxx b/svx/source/table/tablemodel.cxx
new file mode 100644
index 000000000..9104a0a2d
--- /dev/null
+++ b/svx/source/table/tablemodel.cxx
@@ -0,0 +1,1127 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * 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/lang/IndexOutOfBoundsException.hpp>
+#include <com/sun/star/table/XMergeableCell.hpp>
+
+#include <algorithm>
+
+#include <vcl/svapp.hxx>
+#include <osl/mutex.hxx>
+#include <libxml/xmlwriter.h>
+#include <tools/debug.hxx>
+#include <tools/diagnose_ex.h>
+
+#include <cell.hxx>
+#include "cellcursor.hxx"
+#include <tablemodel.hxx>
+#include "tablerow.hxx"
+#include "tablerows.hxx"
+#include "tablecolumn.hxx"
+#include "tablecolumns.hxx"
+#include "tableundo.hxx"
+#include <o3tl/safeint.hxx>
+#include <svx/svdotable.hxx>
+#include <svx/svdmodel.hxx>
+#include <svx/strings.hrc>
+#include <svx/dialmgr.hxx>
+
+using namespace ::osl;
+using namespace ::com::sun::star::uno;
+using namespace ::com::sun::star::table;
+using namespace ::com::sun::star::lang;
+using namespace ::com::sun::star::container;
+using namespace ::com::sun::star::beans;
+using namespace ::com::sun::star::util;
+
+
+namespace sdr::table {
+
+
+// removes the given range from a vector
+template< class Vec, class Iter > static void remove_range( Vec& rVector, sal_Int32 nIndex, sal_Int32 nCount )
+{
+ const sal_Int32 nSize = static_cast<sal_Int32>(rVector.size());
+ if( nCount && (nIndex >= 0) && (nIndex < nSize) )
+ {
+ if( (nIndex + nCount) >= nSize )
+ {
+ // remove at end
+ rVector.resize( nIndex );
+ }
+ else
+ {
+ rVector.erase(rVector.begin() + nIndex, rVector.begin() + nIndex + nCount);
+ }
+ }
+}
+
+
+/** inserts a range into a vector */
+template< class Vec, class Iter, class Entry > static sal_Int32 insert_range( Vec& rVector, sal_Int32 nIndex, sal_Int32 nCount )
+{
+ if( nCount )
+ {
+ if( nIndex >= static_cast< sal_Int32 >( rVector.size() ) )
+ {
+ // append at end
+ nIndex = static_cast< sal_Int32 >( rVector.size() ); // cap to end
+ rVector.resize( nIndex + nCount );
+ }
+ else
+ {
+ // insert
+ Iter aIter( rVector.begin() );
+ std::advance( aIter, nIndex );
+
+ Entry aEmpty;
+ rVector.insert( aIter, nCount, aEmpty );
+ }
+ }
+ return nIndex;
+}
+
+
+TableModel::TableModel( SdrTableObj* pTableObj )
+: TableModelBase( m_aMutex )
+, mpTableObj( pTableObj )
+, mbModified( false )
+, mbNotifyPending( false )
+, mnNotifyLock( 0 )
+{
+}
+
+TableModel::TableModel( SdrTableObj* pTableObj, const TableModelRef& xSourceTable )
+: TableModelBase( m_aMutex )
+, mpTableObj( pTableObj )
+, mbModified( false )
+, mbNotifyPending( false )
+, mnNotifyLock( 0 )
+{
+ if( !xSourceTable.is() )
+ return;
+
+ const sal_Int32 nColCount = xSourceTable->getColumnCountImpl();
+ const sal_Int32 nRowCount = xSourceTable->getRowCountImpl();
+
+ init( nColCount, nRowCount );
+
+ sal_Int32 nRows = nRowCount;
+ while( nRows-- )
+ (*maRows[nRows]) = *xSourceTable->maRows[nRows];
+
+ sal_Int32 nColumns = nColCount;
+ while( nColumns-- )
+ (*maColumns[nColumns]) = *xSourceTable->maColumns[nColumns];
+
+ // copy cells
+ for( sal_Int32 nCol = 0; nCol < nColCount; ++nCol )
+ {
+ for( sal_Int32 nRow = 0; nRow < nRowCount; ++nRow )
+ {
+ CellRef xTargetCell( getCell( nCol, nRow ) );
+ if( xTargetCell.is() )
+ xTargetCell->cloneFrom( xSourceTable->getCell( nCol, nRow ) );
+ }
+ }
+}
+
+
+TableModel::~TableModel()
+{
+}
+
+
+void TableModel::init( sal_Int32 nColumns, sal_Int32 nRows )
+{
+ if( nRows < 20 )
+ maRows.reserve( 20 );
+
+ if( nColumns < 20 )
+ maColumns.reserve( 20 );
+
+ if( nRows && nColumns )
+ {
+ maColumns.resize( nColumns );
+ maRows.resize( nRows );
+
+ while( nRows-- )
+ maRows[nRows].set( new TableRow( this, nRows, nColumns ) );
+
+ while( nColumns-- )
+ maColumns[nColumns].set( new TableColumn( this, nColumns ) );
+ }
+}
+
+
+// ICellRange
+
+
+sal_Int32 TableModel::getLeft()
+{
+ return 0;
+}
+
+
+sal_Int32 TableModel::getTop()
+{
+ return 0;
+}
+
+
+sal_Int32 TableModel::getRight()
+{
+ return getColumnCount();
+}
+
+
+sal_Int32 TableModel::getBottom()
+{
+ return getRowCount();
+}
+
+
+Reference< XTable > TableModel::getTable()
+{
+ return this;
+}
+
+
+void TableModel::UndoInsertRows( sal_Int32 nIndex, sal_Int32 nCount )
+{
+ TableModelNotifyGuard aGuard( this );
+
+ // remove the rows
+ remove_range<RowVector,RowVector::iterator>( maRows, nIndex, nCount );
+ updateRows();
+ setModified(true);
+}
+
+
+void TableModel::UndoRemoveRows( sal_Int32 nIndex, RowVector& aRows )
+{
+ TableModelNotifyGuard aGuard( this );
+
+ const sal_Int32 nCount = sal::static_int_cast< sal_Int32 >( aRows.size() );
+
+ nIndex = insert_range<RowVector,RowVector::iterator,TableRowRef>( maRows, nIndex, nCount );
+
+ for( sal_Int32 nOffset = 0; nOffset < nCount; ++nOffset )
+ maRows[nIndex+nOffset] = aRows[nOffset];
+
+ updateRows();
+ setModified(true);
+}
+
+
+void TableModel::UndoInsertColumns( sal_Int32 nIndex, sal_Int32 nCount )
+{
+ TableModelNotifyGuard aGuard( this );
+
+ // now remove the columns
+ remove_range<ColumnVector,ColumnVector::iterator>( maColumns, nIndex, nCount );
+ sal_Int32 nRows = getRowCountImpl();
+ while( nRows-- )
+ maRows[nRows]->removeColumns( nIndex, nCount );
+
+ updateColumns();
+ setModified(true);
+}
+
+
+void TableModel::UndoRemoveColumns( sal_Int32 nIndex, ColumnVector& aCols, CellVector& aCells )
+{
+ TableModelNotifyGuard aGuard( this );
+
+ const sal_Int32 nCount = sal::static_int_cast< sal_Int32 >( aCols.size() );
+
+ // assert if there are not enough cells saved
+ DBG_ASSERT( (aCols.size() * maRows.size()) == aCells.size(), "sdr::table::TableModel::UndoRemoveColumns(), invalid undo data!" );
+
+ nIndex = insert_range<ColumnVector,ColumnVector::iterator,TableColumnRef>( maColumns, nIndex, nCount );
+ for( sal_Int32 nOffset = 0; nOffset < nCount; ++nOffset )
+ maColumns[nIndex+nOffset] = aCols[nOffset];
+
+ CellVector::iterator aIter( aCells.begin() );
+
+ sal_Int32 nRows = getRowCountImpl();
+ for( sal_Int32 nRow = 0; nRow < nRows; ++nRow )
+ {
+ CellVector::iterator aIter2 = aIter + nRow * nCount;
+ OSL_ENSURE(aIter2 < aCells.end(), "invalid iterator!");
+ maRows[nRow]->insertColumns( nIndex, nCount, &aIter2 );
+ }
+
+ updateColumns();
+ setModified(true);
+}
+
+
+// XTable
+
+
+Reference< XCellCursor > SAL_CALL TableModel::createCursor()
+{
+ ::SolarMutexGuard aGuard;
+ return createCursorByRange( Reference< XCellRange >( this ) );
+}
+
+
+Reference< XCellCursor > SAL_CALL TableModel::createCursorByRange( const Reference< XCellRange >& rRange )
+{
+ ::SolarMutexGuard aGuard;
+
+ ICellRange* pRange = dynamic_cast< ICellRange* >( rRange.get() );
+ if( (pRange == nullptr) || (pRange->getTable().get() != this) )
+ throw IllegalArgumentException();
+
+ TableModelRef xModel( this );
+ return new CellCursor( xModel, pRange->getLeft(), pRange->getTop(), pRange->getRight(), pRange->getBottom() );
+}
+
+
+sal_Int32 SAL_CALL TableModel::getRowCount()
+{
+ ::SolarMutexGuard aGuard;
+ return getRowCountImpl();
+}
+
+sal_Int32 SAL_CALL TableModel::getColumnCount()
+{
+ ::SolarMutexGuard aGuard;
+ return getColumnCountImpl();
+}
+
+std::vector<sal_Int32> TableModel::getColumnWidths()
+{
+ std::vector<sal_Int32> aRet;
+ for (const TableColumnRef& xColumn : maColumns)
+ aRet.push_back(xColumn->getWidth());
+ return aRet;
+}
+
+// XComponent
+
+
+void TableModel::dispose()
+{
+ ::SolarMutexGuard aGuard;
+ TableModelBase::dispose();
+}
+
+
+// XModifiable
+
+
+sal_Bool SAL_CALL TableModel::isModified( )
+{
+ ::SolarMutexGuard aGuard;
+ return mbModified;
+}
+
+
+void SAL_CALL TableModel::setModified( sal_Bool bModified )
+{
+ {
+ ::SolarMutexGuard aGuard;
+ mbModified = bModified;
+ }
+ if( bModified )
+ notifyModification();
+}
+
+
+// XModifyBroadcaster
+
+
+void SAL_CALL TableModel::addModifyListener( const Reference< XModifyListener >& xListener )
+{
+ rBHelper.addListener( cppu::UnoType<XModifyListener>::get() , xListener );
+}
+
+
+void SAL_CALL TableModel::removeModifyListener( const Reference< XModifyListener >& xListener )
+{
+ rBHelper.removeListener( cppu::UnoType<XModifyListener>::get() , xListener );
+}
+
+
+// XColumnRowRange
+
+
+Reference< XTableColumns > SAL_CALL TableModel::getColumns()
+{
+ ::SolarMutexGuard aGuard;
+
+ if( !mxTableColumns.is() )
+ mxTableColumns.set( new TableColumns( this ) );
+ return mxTableColumns;
+}
+
+
+Reference< XTableRows > SAL_CALL TableModel::getRows()
+{
+ ::SolarMutexGuard aGuard;
+
+ if( !mxTableRows.is() )
+ mxTableRows.set( new TableRows( this ) );
+ return mxTableRows;
+}
+
+
+// XCellRange
+
+
+Reference< XCell > SAL_CALL TableModel::getCellByPosition( sal_Int32 nColumn, sal_Int32 nRow )
+{
+ ::SolarMutexGuard aGuard;
+
+ CellRef xCell( getCell( nColumn, nRow ) );
+ if( xCell.is() )
+ return xCell;
+
+ throw IndexOutOfBoundsException();
+}
+
+
+Reference< XCellRange > SAL_CALL TableModel::getCellRangeByPosition( sal_Int32 nLeft, sal_Int32 nTop, sal_Int32 nRight, sal_Int32 nBottom )
+{
+ ::SolarMutexGuard aGuard;
+
+ if( (nLeft >= 0) && (nTop >= 0) && (nRight >= nLeft) && (nBottom >= nTop) && (nRight < getColumnCountImpl()) && (nBottom < getRowCountImpl() ) )
+ {
+ TableModelRef xModel( this );
+ return new CellRange( xModel, nLeft, nTop, nRight, nBottom );
+ }
+
+ throw IndexOutOfBoundsException();
+}
+
+
+Reference< XCellRange > SAL_CALL TableModel::getCellRangeByName( const OUString& /*aRange*/ )
+{
+ return Reference< XCellRange >();
+}
+
+
+// XPropertySet
+
+
+Reference< XPropertySetInfo > SAL_CALL TableModel::getPropertySetInfo( )
+{
+ Reference< XPropertySetInfo > xInfo;
+ return xInfo;
+}
+
+
+void SAL_CALL TableModel::setPropertyValue( const OUString& /*aPropertyName*/, const Any& /*aValue*/ )
+{
+}
+
+
+Any SAL_CALL TableModel::getPropertyValue( const OUString& /*PropertyName*/ )
+{
+ return Any();
+}
+
+
+void SAL_CALL TableModel::addPropertyChangeListener( const OUString& /*aPropertyName*/, const Reference< XPropertyChangeListener >& /*xListener*/ )
+{
+}
+
+
+void SAL_CALL TableModel::removePropertyChangeListener( const OUString& /*aPropertyName*/, const Reference< XPropertyChangeListener >& /*xListener*/ )
+{
+}
+
+
+void SAL_CALL TableModel::addVetoableChangeListener( const OUString& /*aPropertyName*/, const Reference< XVetoableChangeListener >& /*xListener*/ )
+{
+}
+
+
+void SAL_CALL TableModel::removeVetoableChangeListener( const OUString& /*aPropertyName*/, const Reference< XVetoableChangeListener >& /*xListener*/ )
+{
+}
+
+
+// XFastPropertySet
+
+
+void SAL_CALL TableModel::setFastPropertyValue( ::sal_Int32 /*nHandle*/, const Any& /*aValue*/ )
+{
+}
+
+
+Any SAL_CALL TableModel::getFastPropertyValue( ::sal_Int32 /*nHandle*/ )
+{
+ Any aAny;
+ return aAny;
+}
+
+
+// internals
+
+
+sal_Int32 TableModel::getRowCountImpl() const
+{
+ return static_cast< sal_Int32 >( maRows.size() );
+}
+
+
+sal_Int32 TableModel::getColumnCountImpl() const
+{
+ return static_cast< sal_Int32 >( maColumns.size() );
+}
+
+
+void TableModel::disposing()
+{
+ if( !maRows.empty() )
+ {
+ for( auto& rpRow : maRows )
+ rpRow->dispose();
+ RowVector().swap(maRows);
+ }
+
+ if( !maColumns.empty() )
+ {
+ for( auto& rpCol : maColumns )
+ rpCol->dispose();
+ ColumnVector().swap(maColumns);
+ }
+
+ if( mxTableColumns.is() )
+ {
+ mxTableColumns->dispose();
+ mxTableColumns.clear();
+ }
+
+ if( mxTableRows.is() )
+ {
+ mxTableRows->dispose();
+ mxTableRows.clear();
+ }
+
+ mpTableObj = nullptr;
+}
+
+
+// XBroadcaster
+
+
+void TableModel::lockBroadcasts()
+{
+ ::SolarMutexGuard aGuard;
+ ++mnNotifyLock;
+}
+
+
+void TableModel::unlockBroadcasts()
+{
+ ::SolarMutexGuard aGuard;
+ --mnNotifyLock;
+ if( mnNotifyLock <= 0 )
+ {
+ mnNotifyLock = 0;
+ if( mbNotifyPending )
+ notifyModification();
+ }
+}
+
+
+void TableModel::notifyModification()
+{
+ ::osl::MutexGuard guard( m_aMutex );
+ if( (mnNotifyLock == 0) && mpTableObj )
+ {
+ mbNotifyPending = false;
+
+ ::cppu::OInterfaceContainerHelper * pModifyListeners = rBHelper.getContainer( cppu::UnoType<XModifyListener>::get() );
+ if( pModifyListeners )
+ {
+ EventObject aSource;
+ aSource.Source = static_cast< ::cppu::OWeakObject* >(this);
+ pModifyListeners->notifyEach( &XModifyListener::modified, aSource);
+ }
+ }
+ else
+ {
+ mbNotifyPending = true;
+ }
+}
+
+
+CellRef TableModel::getCell( sal_Int32 nCol, sal_Int32 nRow ) const
+{
+ if( ((nRow >= 0) && (nRow < getRowCountImpl())) && (nCol >= 0) && (nCol < getColumnCountImpl()) )
+ {
+ return maRows[nRow]->maCells[nCol];
+ }
+ else
+ {
+ CellRef xRet;
+ return xRet;
+ }
+}
+
+
+CellRef TableModel::createCell()
+{
+ CellRef xCell;
+ if( mpTableObj )
+ mpTableObj->createCell( xCell );
+ return xCell;
+}
+
+
+void TableModel::insertColumns( sal_Int32 nIndex, sal_Int32 nCount )
+{
+ if( !(nCount && mpTableObj) )
+ return;
+
+ try
+ {
+ SdrModel& rModel(mpTableObj->getSdrModelFromSdrObject());
+ TableModelNotifyGuard aGuard( this );
+ nIndex = insert_range<ColumnVector,ColumnVector::iterator,TableColumnRef>( maColumns, nIndex, nCount );
+
+ sal_Int32 nRows = getRowCountImpl();
+ while( nRows-- )
+ maRows[nRows]->insertColumns( nIndex, nCount, nullptr );
+
+ ColumnVector aNewColumns(nCount);
+ for( sal_Int32 nOffset = 0; nOffset < nCount; ++nOffset )
+ {
+ TableColumnRef xNewCol( new TableColumn( this, nIndex+nOffset ) );
+ maColumns[nIndex+nOffset] = xNewCol;
+ aNewColumns[nOffset] = xNewCol;
+ }
+
+ const bool bUndo(mpTableObj->IsInserted() && rModel.IsUndoEnabled());
+
+ if( bUndo )
+ {
+ rModel.BegUndo( SvxResId(STR_TABLE_INSCOL) );
+ rModel.AddUndo( rModel.GetSdrUndoFactory().CreateUndoGeoObject(*mpTableObj) );
+
+ TableModelRef xThis( this );
+
+ nRows = getRowCountImpl();
+ CellVector aNewCells( nCount * nRows );
+ CellVector::iterator aCellIter( aNewCells.begin() );
+
+ nRows = getRowCountImpl();
+ for( sal_Int32 nRow = 0; nRow < nRows; ++nRow )
+ {
+ for( sal_Int32 nOffset = 0; nOffset < nCount; ++nOffset )
+ (*aCellIter++) = getCell( nIndex + nOffset, nRow );
+ }
+
+ rModel.AddUndo( std::make_unique<InsertColUndo>( xThis, nIndex, aNewColumns, aNewCells ) );
+ }
+
+ const sal_Int32 nRowCount = getRowCountImpl();
+ // check if cells merge over new columns
+ for( sal_Int32 nCol = 0; nCol < nIndex; ++nCol )
+ {
+ for( sal_Int32 nRow = 0; nRow < nRowCount; ++nRow )
+ {
+ CellRef xCell( getCell( nCol, nRow ) );
+ sal_Int32 nColSpan = (xCell.is() && !xCell->isMerged()) ? xCell->getColumnSpan() : 1;
+ if( (nColSpan != 1) && ((nColSpan + nCol ) > nIndex) )
+ {
+ // cell merges over newly created columns, so add the new columns to the merged cell
+ const sal_Int32 nRowSpan = xCell->getRowSpan();
+ nColSpan += nCount;
+ merge( nCol, nRow, nColSpan, nRowSpan );
+ }
+ }
+ }
+
+ if( bUndo )
+ rModel.EndUndo();
+
+ rModel.SetChanged();
+ }
+ catch( Exception& )
+ {
+ TOOLS_WARN_EXCEPTION("svx", "");
+ }
+ updateColumns();
+ setModified(true);
+}
+
+
+void TableModel::removeColumns( sal_Int32 nIndex, sal_Int32 nCount )
+{
+ sal_Int32 nColCount = getColumnCountImpl();
+
+ if( !(mpTableObj && nCount && (nIndex >= 0) && (nIndex < nColCount)) )
+ return;
+
+ try
+ {
+ TableModelNotifyGuard aGuard( this );
+
+ // clip removed columns to columns actually available
+ if( (nIndex + nCount) > nColCount )
+ nCount = nColCount - nIndex;
+
+ sal_Int32 nRows = getRowCountImpl();
+ SdrModel& rModel(mpTableObj->getSdrModelFromSdrObject());
+ const bool bUndo(mpTableObj->IsInserted() && rModel.IsUndoEnabled());
+
+ if( bUndo )
+ {
+ rModel.BegUndo( SvxResId(STR_UNDO_COL_DELETE) );
+ rModel.AddUndo( rModel.GetSdrUndoFactory().CreateUndoGeoObject(*mpTableObj) );
+ }
+
+ // only rows before and inside the removed rows are considered
+ nColCount = nIndex + nCount + 1;
+
+ const sal_Int32 nRowCount = getRowCountImpl();
+
+ // first check merged cells before and inside the removed rows
+ for( sal_Int32 nCol = 0; nCol < nColCount; ++nCol )
+ {
+ for( sal_Int32 nRow = 0; nRow < nRowCount; ++nRow )
+ {
+ CellRef xCell( getCell( nCol, nRow ) );
+ sal_Int32 nColSpan = (xCell.is() && !xCell->isMerged()) ? xCell->getColumnSpan() : 1;
+ if( nColSpan <= 1 )
+ continue;
+
+ if( nCol >= nIndex )
+ {
+ // current cell is inside the removed columns
+ if( (nCol + nColSpan) > ( nIndex + nCount ) )
+ {
+ // current cells merges with columns after the removed columns
+ const sal_Int32 nRemove = nCount - nCol + nIndex;
+
+ CellRef xTargetCell( getCell( nIndex + nCount, nRow ) );
+ if( xTargetCell.is() )
+ {
+ if( bUndo )
+ xTargetCell->AddUndo();
+ xTargetCell->merge( nColSpan - nRemove, xCell->getRowSpan() );
+ xTargetCell->replaceContentAndFormatting( xCell );
+ }
+ }
+ }
+ else if( nColSpan > (nIndex - nCol) )
+ {
+ // current cells spans inside the removed columns, so adjust
+ const sal_Int32 nRemove = ::std::min( nCount, nCol + nColSpan - nIndex );
+ if( bUndo )
+ xCell->AddUndo();
+ xCell->merge( nColSpan - nRemove, xCell->getRowSpan() );
+ }
+ }
+ }
+
+ // We must not add RemoveColUndo before we make cell spans correct, otherwise we
+ // get invalid cell span after undo.
+ if( bUndo )
+ {
+ TableModelRef xThis( this );
+ ColumnVector aRemovedCols( nCount );
+ sal_Int32 nOffset;
+ for( nOffset = 0; nOffset < nCount; ++nOffset )
+ {
+ aRemovedCols[nOffset] = maColumns[nIndex+nOffset];
+ }
+
+ CellVector aRemovedCells( nCount * nRows );
+ CellVector::iterator aCellIter( aRemovedCells.begin() );
+ for( sal_Int32 nRow = 0; nRow < nRows; ++nRow )
+ {
+ for( nOffset = 0; nOffset < nCount; ++nOffset )
+ (*aCellIter++) = getCell( nIndex + nOffset, nRow );
+ }
+
+ rModel.AddUndo( std::make_unique<RemoveColUndo>( xThis, nIndex, aRemovedCols, aRemovedCells ) );
+ }
+
+ // now remove the columns
+ remove_range<ColumnVector,ColumnVector::iterator>( maColumns, nIndex, nCount );
+ while( nRows-- )
+ maRows[nRows]->removeColumns( nIndex, nCount );
+
+ if( bUndo )
+ rModel.EndUndo();
+
+ rModel.SetChanged();
+ }
+ catch( Exception& )
+ {
+ TOOLS_WARN_EXCEPTION("svx", "");
+ }
+
+ updateColumns();
+ setModified(true);
+}
+
+
+void TableModel::insertRows( sal_Int32 nIndex, sal_Int32 nCount )
+{
+ if( !(nCount && mpTableObj) )
+ return;
+
+ SdrModel& rModel(mpTableObj->getSdrModelFromSdrObject());
+ const bool bUndo(mpTableObj->IsInserted() && rModel.IsUndoEnabled());
+
+ try
+ {
+ TableModelNotifyGuard aGuard( this );
+
+ nIndex = insert_range<RowVector,RowVector::iterator,TableRowRef>( maRows, nIndex, nCount );
+
+ RowVector aNewRows(nCount);
+ const sal_Int32 nColCount = getColumnCountImpl();
+ for( sal_Int32 nOffset = 0; nOffset < nCount; ++nOffset )
+ {
+ TableRowRef xNewRow( new TableRow( this, nIndex+nOffset, nColCount ) );
+ maRows[nIndex+nOffset] = xNewRow;
+ aNewRows[nOffset] = xNewRow;
+ }
+
+ if( bUndo )
+ {
+ rModel.BegUndo( SvxResId(STR_TABLE_INSROW) );
+ rModel.AddUndo( rModel.GetSdrUndoFactory().CreateUndoGeoObject(*mpTableObj) );
+ TableModelRef xThis( this );
+ rModel.AddUndo( std::make_unique<InsertRowUndo>( xThis, nIndex, aNewRows ) );
+ }
+
+ // check if cells merge over new columns
+ for( sal_Int32 nRow = 0; nRow < nIndex; ++nRow )
+ {
+ for( sal_Int32 nCol = 0; nCol < nColCount; ++nCol )
+ {
+ CellRef xCell( getCell( nCol, nRow ) );
+ sal_Int32 nRowSpan = (xCell.is() && !xCell->isMerged()) ? xCell->getRowSpan() : 1;
+ if( (nRowSpan > 1) && ((nRowSpan + nRow) > nIndex) )
+ {
+ // cell merges over newly created columns, so add the new columns to the merged cell
+ const sal_Int32 nColSpan = xCell->getColumnSpan();
+ nRowSpan += nCount;
+ merge( nCol, nRow, nColSpan, nRowSpan );
+ }
+ }
+ }
+ }
+ catch( Exception& )
+ {
+ TOOLS_WARN_EXCEPTION("svx", "");
+ }
+ if( bUndo )
+ rModel.EndUndo();
+
+ rModel.SetChanged();
+
+ updateRows();
+ setModified(true);
+}
+
+
+void TableModel::removeRows( sal_Int32 nIndex, sal_Int32 nCount )
+{
+ sal_Int32 nRowCount = getRowCountImpl();
+
+ if( !(mpTableObj && nCount && (nIndex >= 0) && (nIndex < nRowCount)) )
+ return;
+
+ SdrModel& rModel(mpTableObj->getSdrModelFromSdrObject());
+ const bool bUndo(mpTableObj->IsInserted() && rModel.IsUndoEnabled());
+
+ try
+ {
+ TableModelNotifyGuard aGuard( this );
+
+ // clip removed rows to rows actually available
+ if( (nIndex + nCount) > nRowCount )
+ nCount = nRowCount - nIndex;
+
+ if( bUndo )
+ {
+ rModel.BegUndo( SvxResId(STR_UNDO_ROW_DELETE) );
+ rModel.AddUndo( rModel.GetSdrUndoFactory().CreateUndoGeoObject(*mpTableObj) );
+ }
+
+ // only rows before and inside the removed rows are considered
+ nRowCount = nIndex + nCount + 1;
+
+ const sal_Int32 nColCount = getColumnCountImpl();
+
+ // first check merged cells before and inside the removed rows
+ for( sal_Int32 nRow = 0; nRow < nRowCount; ++nRow )
+ {
+ for( sal_Int32 nCol = 0; nCol < nColCount; ++nCol )
+ {
+ CellRef xCell( getCell( nCol, nRow ) );
+ sal_Int32 nRowSpan = (xCell.is() && !xCell->isMerged()) ? xCell->getRowSpan() : 1;
+ if( nRowSpan <= 1 )
+ continue;
+
+ if( nRow >= nIndex )
+ {
+ // current cell is inside the removed rows
+ if( (nRow + nRowSpan) > (nIndex + nCount) )
+ {
+ // current cells merges with rows after the removed rows
+ const sal_Int32 nRemove = nCount - nRow + nIndex;
+
+ CellRef xTargetCell( getCell( nCol, nIndex + nCount ) );
+ if( xTargetCell.is() )
+ {
+ if( bUndo )
+ xTargetCell->AddUndo();
+ xTargetCell->merge( xCell->getColumnSpan(), nRowSpan - nRemove );
+ xTargetCell->replaceContentAndFormatting( xCell );
+ }
+ }
+ }
+ else if( nRowSpan > (nIndex - nRow) )
+ {
+ // current cells spans inside the removed rows, so adjust
+ const sal_Int32 nRemove = ::std::min( nCount, nRow + nRowSpan - nIndex );
+ if( bUndo )
+ xCell->AddUndo();
+ xCell->merge( xCell->getColumnSpan(), nRowSpan - nRemove );
+ }
+ }
+ }
+
+ if( bUndo )
+ {
+ TableModelRef xThis( this );
+
+ RowVector aRemovedRows( nCount );
+ for( sal_Int32 nOffset = 0; nOffset < nCount; ++nOffset )
+ aRemovedRows[nOffset] = maRows[nIndex+nOffset];
+
+ // We must not RemoveRowUndo before we make cell spans correct, otherwise we
+ // get invalid cell span after undo.
+ rModel.AddUndo( std::make_unique<RemoveRowUndo>( xThis, nIndex, aRemovedRows ) );
+ }
+ // now remove the rows
+ remove_range<RowVector,RowVector::iterator>( maRows, nIndex, nCount );
+
+ if( bUndo )
+ rModel.EndUndo();
+
+ rModel.SetChanged();
+ }
+ catch( Exception& )
+ {
+ TOOLS_WARN_EXCEPTION("svx", "");
+ }
+
+ updateRows();
+ setModified(true);
+}
+
+
+TableRowRef const & TableModel::getRow( sal_Int32 nRow ) const
+{
+ if( (nRow >= 0) && (nRow < getRowCountImpl()) )
+ return maRows[nRow];
+
+ throw IndexOutOfBoundsException();
+}
+
+
+TableColumnRef const & TableModel::getColumn( sal_Int32 nColumn ) const
+{
+ if( (nColumn >= 0) && (nColumn < getColumnCountImpl()) )
+ return maColumns[nColumn];
+
+ throw IndexOutOfBoundsException();
+}
+
+
+/** deletes rows and columns that are completely merged. Must be called between BegUndo/EndUndo! */
+void TableModel::optimize()
+{
+ TableModelNotifyGuard aGuard( this );
+
+ bool bWasModified = false;
+
+ if( !maRows.empty() && !maColumns.empty() )
+ {
+ sal_Int32 nCol = getColumnCountImpl() - 1;
+ sal_Int32 nRows = getRowCountImpl();
+ while( nCol > 0 )
+ {
+ bool bEmpty = true;
+ for( sal_Int32 nRow = 0; (nRow < nRows) && bEmpty; nRow++ )
+ {
+ Reference< XMergeableCell > xCell( getCellByPosition( nCol, nRow ), UNO_QUERY );
+ if( xCell.is() && !xCell->isMerged() )
+ bEmpty = false;
+ }
+
+ if( bEmpty )
+ {
+ try
+ {
+ static const OUStringLiteral sWidth(u"Width");
+ sal_Int32 nWidth1 = 0, nWidth2 = 0;
+ Reference< XPropertySet > xSet1( static_cast< XCellRange* >( maColumns[nCol].get() ), UNO_QUERY_THROW );
+ Reference< XPropertySet > xSet2( static_cast< XCellRange* >( maColumns[nCol-1].get() ), UNO_QUERY_THROW );
+ xSet1->getPropertyValue( sWidth ) >>= nWidth1;
+ xSet2->getPropertyValue( sWidth ) >>= nWidth2;
+ nWidth1 = o3tl::saturating_add(nWidth1, nWidth2);
+ xSet2->setPropertyValue( sWidth, Any( nWidth1 ) );
+ }
+ catch( Exception& )
+ {
+ TOOLS_WARN_EXCEPTION("svx", "");
+ }
+
+ removeColumns( nCol, 1 );
+ bWasModified = true;
+ }
+
+ nCol--;
+ }
+
+ sal_Int32 nRow = getRowCountImpl() - 1;
+ sal_Int32 nCols = getColumnCountImpl();
+ while( nRow > 0 )
+ {
+ bool bEmpty = true;
+ for( nCol = 0; (nCol < nCols) && bEmpty; nCol++ )
+ {
+ Reference< XMergeableCell > xCell( getCellByPosition( nCol, nRow ), UNO_QUERY );
+ if( xCell.is() && !xCell->isMerged() )
+ bEmpty = false;
+ }
+
+ if( bEmpty )
+ {
+ try
+ {
+ static const OUStringLiteral sHeight(u"Height");
+ sal_Int32 nHeight1 = 0, nHeight2 = 0;
+ Reference< XPropertySet > xSet1( static_cast< XCellRange* >( maRows[nRow].get() ), UNO_QUERY_THROW );
+ Reference< XPropertySet > xSet2( static_cast< XCellRange* >( maRows[nRow-1].get() ), UNO_QUERY_THROW );
+ xSet1->getPropertyValue( sHeight ) >>= nHeight1;
+ xSet2->getPropertyValue( sHeight ) >>= nHeight2;
+ nHeight1 = o3tl::saturating_add(nHeight1, nHeight2);
+ xSet2->setPropertyValue( sHeight, Any( nHeight1 ) );
+ }
+ catch( Exception& )
+ {
+ TOOLS_WARN_EXCEPTION("svx", "");
+ }
+
+ removeRows( nRow, 1 );
+ bWasModified = true;
+ }
+
+ nRow--;
+ }
+ }
+ if( bWasModified )
+ setModified(true);
+}
+
+
+void TableModel::merge( sal_Int32 nCol, sal_Int32 nRow, sal_Int32 nColSpan, sal_Int32 nRowSpan )
+{
+ if(nullptr == mpTableObj)
+ return;
+
+ SdrModel& rModel(mpTableObj->getSdrModelFromSdrObject());
+ const bool bUndo(mpTableObj->IsInserted() && rModel.IsUndoEnabled());
+ const sal_Int32 nLastRow = nRow + nRowSpan;
+ const sal_Int32 nLastCol = nCol + nColSpan;
+
+ if( (nLastRow > getRowCount()) || (nLastCol > getColumnCount() ) )
+ {
+ OSL_FAIL("TableModel::merge(), merge beyond the table!");
+ }
+
+ // merge first cell
+ CellRef xOriginCell( dynamic_cast< Cell* >( getCellByPosition( nCol, nRow ).get() ) );
+ if(!xOriginCell.is())
+ return;
+
+ if( bUndo )
+ xOriginCell->AddUndo();
+ xOriginCell->merge( nColSpan, nRowSpan );
+
+ sal_Int32 nTempCol = nCol + 1;
+
+ // merge remaining cells
+ for( ; nRow < nLastRow; nRow++ )
+ {
+ for( ; nTempCol < nLastCol; nTempCol++ )
+ {
+ CellRef xCell( dynamic_cast< Cell* >( getCellByPosition( nTempCol, nRow ).get() ) );
+ if( xCell.is() && !xCell->isMerged() )
+ {
+ if( bUndo )
+ xCell->AddUndo();
+ xCell->setMerged();
+ xOriginCell->mergeContent( xCell );
+ }
+ }
+ nTempCol = nCol;
+ }
+}
+
+void TableModel::updateRows()
+{
+ sal_Int32 nRow = 0;
+ for( auto& rpRow : maRows )
+ {
+ rpRow->mnRow = nRow++;
+ }
+}
+
+void TableModel::updateColumns()
+{
+ sal_Int32 nColumn = 0;
+ for( auto& rpCol : maColumns )
+ {
+ rpCol->mnColumn = nColumn++;
+ }
+}
+
+void TableModel::dumpAsXml(xmlTextWriterPtr pWriter) const
+{
+ (void)xmlTextWriterStartElement(pWriter, BAD_CAST("TableModel"));
+ for (sal_Int32 nRow = 0; nRow < getRowCountImpl(); ++nRow)
+ for (sal_Int32 nCol = 0; nCol < getColumnCountImpl(); ++nCol)
+ {
+ maRows[nRow]->maCells[nCol]->dumpAsXml(pWriter, nRow, nCol);
+ }
+ (void)xmlTextWriterEndElement(pWriter);
+}
+
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/table/tablerow.cxx b/svx/source/table/tablerow.cxx
new file mode 100644
index 000000000..71b51a653
--- /dev/null
+++ b/svx/source/table/tablerow.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/lang/DisposedException.hpp>
+#include <com/sun/star/lang/IndexOutOfBoundsException.hpp>
+
+#include <cell.hxx>
+#include "tablerow.hxx"
+#include "tableundo.hxx"
+#include <svx/svdmodel.hxx>
+#include <svx/svdotable.hxx>
+
+
+using namespace ::com::sun::star::uno;
+using namespace ::com::sun::star::lang;
+using namespace ::com::sun::star::container;
+using namespace ::com::sun::star::table;
+using namespace ::com::sun::star::beans;
+
+
+namespace sdr::table {
+
+const sal_Int32 Property_Height = 0;
+const sal_Int32 Property_OptimalHeight = 1;
+const sal_Int32 Property_IsVisible = 2;
+const sal_Int32 Property_IsStartOfNewPage = 3;
+
+TableRow::TableRow( const TableModelRef& xTableModel, sal_Int32 nRow, sal_Int32 nColumns )
+: TableRowBase( getStaticPropertySetInfo() )
+, mxTableModel( xTableModel )
+, mnRow( nRow )
+, mnHeight( 0 )
+, mbOptimalHeight( true )
+, mbIsVisible( true )
+, mbIsStartOfNewPage( false )
+{
+ if( nColumns < 20 )
+ maCells.reserve( 20 );
+
+ if( nColumns )
+ {
+ maCells.resize( nColumns );
+ while( nColumns-- )
+ maCells[ nColumns ] = mxTableModel->createCell();
+ }
+}
+
+
+TableRow::~TableRow()
+{
+}
+
+
+void TableRow::dispose()
+{
+ mxTableModel.clear();
+ if( !maCells.empty() )
+ {
+ for( auto& rpCell : maCells )
+ rpCell->dispose();
+ CellVector().swap(maCells);
+ }
+}
+
+
+void TableRow::throwIfDisposed() const
+{
+ if( !mxTableModel.is() )
+ throw DisposedException();
+}
+
+
+TableRow& TableRow::operator=( const TableRow& r )
+{
+ mnHeight = r.mnHeight;
+ mbOptimalHeight = r.mbOptimalHeight;
+ mbIsVisible = r.mbIsVisible;
+ mbIsStartOfNewPage = r.mbIsStartOfNewPage;
+ maName = r.maName;
+ mnRow = r.mnRow;
+
+ return *this;
+}
+
+
+void TableRow::insertColumns( sal_Int32 nIndex, sal_Int32 nCount, CellVector::iterator const * pIter /* = 0 */ )
+{
+ throwIfDisposed();
+ if( !nCount )
+ return;
+
+ if( nIndex >= static_cast< sal_Int32 >( maCells.size() ) )
+ nIndex = static_cast< sal_Int32 >( maCells.size() );
+ if ( pIter )
+ maCells.insert( maCells.begin() + nIndex, *pIter, (*pIter) + nCount );
+ else
+ {
+ maCells.reserve( maCells.size() + nCount );
+ for ( sal_Int32 i = 0; i < nCount; i++ )
+ maCells.insert( maCells.begin() + nIndex + i, mxTableModel->createCell() );
+ }
+}
+
+
+void TableRow::removeColumns( sal_Int32 nIndex, sal_Int32 nCount )
+{
+ throwIfDisposed();
+ if( (nCount < 0) || ( nIndex < 0))
+ return;
+
+ if( (nIndex + nCount) < static_cast< sal_Int32 >( maCells.size() ) )
+ {
+ CellVector::iterator aBegin( maCells.begin() );
+ std::advance(aBegin, nIndex);
+
+ if( nCount > 1 )
+ {
+ CellVector::iterator aEnd( aBegin );
+ while( nCount-- && (aEnd != maCells.end()) )
+ ++aEnd;
+ maCells.erase( aBegin, aEnd );
+ }
+ else
+ {
+ maCells.erase( aBegin );
+ }
+ }
+ else
+ {
+ maCells.resize( nIndex );
+ }
+}
+
+const TableModelRef& TableRow::getModel() const
+{
+ return mxTableModel;
+}
+
+// XCellRange
+
+
+Reference< XCell > SAL_CALL TableRow::getCellByPosition( sal_Int32 nColumn, sal_Int32 nRow )
+{
+ throwIfDisposed();
+ if( nRow != 0 )
+ throw IndexOutOfBoundsException();
+
+ return mxTableModel->getCellByPosition( nColumn, mnRow );
+}
+
+
+Reference< XCellRange > SAL_CALL TableRow::getCellRangeByPosition( sal_Int32 nLeft, sal_Int32 nTop, sal_Int32 nRight, sal_Int32 nBottom )
+{
+ throwIfDisposed();
+ if( (nLeft >= 0 ) && (nTop == 0) && (nRight >= nLeft) && (nBottom == 0) )
+ {
+ return mxTableModel->getCellRangeByPosition( nLeft, mnRow, nRight, mnRow );
+ }
+ throw IndexOutOfBoundsException();
+}
+
+
+Reference< XCellRange > SAL_CALL TableRow::getCellRangeByName( const OUString& /*aRange*/ )
+{
+ throwIfDisposed();
+ return Reference< XCellRange >();
+}
+
+
+// XNamed
+
+
+OUString SAL_CALL TableRow::getName()
+{
+ return maName;
+}
+
+
+void SAL_CALL TableRow::setName( const OUString& aName )
+{
+ maName = aName;
+}
+
+
+// XFastPropertySet
+
+
+void SAL_CALL TableRow::setFastPropertyValue( sal_Int32 nHandle, const Any& aValue )
+{
+ if(!mxTableModel.is() || nullptr == mxTableModel->getSdrTableObj())
+ return;
+
+ SdrTableObj& rTableObj(*mxTableModel->getSdrTableObj());
+ SdrModel& rModel(rTableObj.getSdrModelFromSdrObject());
+ bool bOk(false);
+ bool bChange(false);
+ std::unique_ptr<TableRowUndo> pUndo;
+ const bool bUndo(rTableObj.IsInserted() && rModel.IsUndoEnabled());
+
+ if( bUndo )
+ {
+ TableRowRef xThis( this );
+ pUndo.reset(new TableRowUndo( xThis ));
+ }
+
+ switch( nHandle )
+ {
+ case Property_Height:
+ {
+ sal_Int32 nHeight = mnHeight;
+ bOk = aValue >>= nHeight;
+ if( bOk && (mnHeight != nHeight) )
+ {
+ mnHeight = nHeight;
+ mbOptimalHeight = mnHeight == 0;
+ bChange = true;
+ }
+ break;
+ }
+
+ case Property_OptimalHeight:
+ {
+ bool bOptimalHeight = mbOptimalHeight;
+ bOk = aValue >>= bOptimalHeight;
+ if( bOk && (mbOptimalHeight != bOptimalHeight) )
+ {
+ mbOptimalHeight = bOptimalHeight;
+ if( bOptimalHeight )
+ mnHeight = 0;
+ bChange = true;
+ }
+ break;
+ }
+ case Property_IsVisible:
+ {
+ bool bIsVisible = mbIsVisible;
+ bOk = aValue >>= bIsVisible;
+ if( bOk && (mbIsVisible != bIsVisible) )
+ {
+ mbIsVisible = bIsVisible;
+ bChange = true;
+ }
+ break;
+ }
+
+ case Property_IsStartOfNewPage:
+ {
+ bool bIsStartOfNewPage = mbIsStartOfNewPage;
+ bOk = aValue >>= bIsStartOfNewPage;
+ if( bOk && (mbIsStartOfNewPage != bIsStartOfNewPage) )
+ {
+ mbIsStartOfNewPage = bIsStartOfNewPage;
+ bChange = true;
+ }
+ break;
+ }
+ default:
+ throw UnknownPropertyException( OUString::number(nHandle), static_cast<cppu::OWeakObject*>(this));
+ }
+
+ if( !bOk )
+ {
+ throw IllegalArgumentException();
+ }
+
+ if( bChange )
+ {
+ if( pUndo )
+ {
+ rModel.AddUndo( std::move(pUndo) );
+ }
+ mxTableModel->setModified(true);
+ }
+}
+
+
+Any SAL_CALL TableRow::getFastPropertyValue( sal_Int32 nHandle )
+{
+ switch( nHandle )
+ {
+ case Property_Height: return Any( mnHeight );
+ case Property_OptimalHeight: return Any( mbOptimalHeight );
+ case Property_IsVisible: return Any( mbIsVisible );
+ case Property_IsStartOfNewPage: return Any( mbIsStartOfNewPage );
+ default: throw UnknownPropertyException( OUString::number(nHandle), static_cast<cppu::OWeakObject*>(this));
+ }
+}
+
+
+rtl::Reference< FastPropertySetInfo > TableRow::getStaticPropertySetInfo()
+{
+ static rtl::Reference<FastPropertySetInfo> xInfo = []() {
+ PropertyVector aProperties(6);
+
+ aProperties[0].Name = "Height";
+ aProperties[0].Handle = Property_Height;
+ aProperties[0].Type = ::cppu::UnoType<sal_Int32>::get();
+ aProperties[0].Attributes = 0;
+
+ aProperties[1].Name = "OptimalHeight";
+ aProperties[1].Handle = Property_OptimalHeight;
+ aProperties[1].Type = cppu::UnoType<bool>::get();
+ aProperties[1].Attributes = 0;
+
+ aProperties[2].Name = "IsVisible";
+ aProperties[2].Handle = Property_IsVisible;
+ aProperties[2].Type = cppu::UnoType<bool>::get();
+ aProperties[2].Attributes = 0;
+
+ aProperties[3].Name = "IsStartOfNewPage";
+ aProperties[3].Handle = Property_IsStartOfNewPage;
+ aProperties[3].Type = cppu::UnoType<bool>::get();
+ aProperties[3].Attributes = 0;
+
+ aProperties[4].Name = "Size";
+ aProperties[4].Handle = Property_Height;
+ aProperties[4].Type = ::cppu::UnoType<sal_Int32>::get();
+ aProperties[4].Attributes = 0;
+
+ aProperties[5].Name = "OptimalSize";
+ aProperties[5].Handle = Property_OptimalHeight;
+ aProperties[5].Type = cppu::UnoType<bool>::get();
+ aProperties[5].Attributes = 0;
+
+ return rtl::Reference<FastPropertySetInfo>(new FastPropertySetInfo(aProperties));
+ }();
+
+ return xInfo;
+}
+
+
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/table/tablerow.hxx b/svx/source/table/tablerow.hxx
new file mode 100644
index 000000000..9548e317f
--- /dev/null
+++ b/svx/source/table/tablerow.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_SVX_SOURCE_TABLE_TABLEROW_HXX
+#define INCLUDED_SVX_SOURCE_TABLE_TABLEROW_HXX
+
+#include <com/sun/star/table/XCellRange.hpp>
+#include <com/sun/star/container/XNamed.hpp>
+#include <cppuhelper/implbase.hxx>
+
+#include "propertyset.hxx"
+#include <celltypes.hxx>
+
+
+namespace sdr::table {
+
+typedef ::cppu::ImplInheritanceHelper< FastPropertySet, css::table::XCellRange, css::container::XNamed > TableRowBase;
+
+class TableRow : public TableRowBase
+{
+ friend class TableModel;
+ friend class TableRowUndo;
+public:
+ TableRow( const TableModelRef& xTableModel, sal_Int32 nRow, sal_Int32 nColumns );
+ virtual ~TableRow() override;
+
+ void dispose();
+ /// @throws css::uno::RuntimeException
+ void throwIfDisposed() const;
+
+ TableRow& operator=( const TableRow& );
+
+ void insertColumns( sal_Int32 nIndex, sal_Int32 nCount, CellVector::iterator const * pIter );
+ void removeColumns( sal_Int32 nIndex, sal_Int32 nCount );
+ /// Reference to the table model containing this row.
+ const TableModelRef& getModel() const;
+
+ // XCellRange
+ virtual css::uno::Reference< css::table::XCell > SAL_CALL getCellByPosition( sal_Int32 nColumn, sal_Int32 nRow ) override;
+ virtual css::uno::Reference< css::table::XCellRange > SAL_CALL getCellRangeByPosition( sal_Int32 nLeft, sal_Int32 nTop, sal_Int32 nRight, sal_Int32 nBottom ) override;
+ virtual css::uno::Reference< css::table::XCellRange > SAL_CALL getCellRangeByName( const OUString& aRange ) override;
+
+ // XNamed
+ virtual OUString SAL_CALL getName() override;
+ virtual void SAL_CALL setName( const OUString& aName ) override;
+
+ // XFastPropertySet
+ virtual void SAL_CALL setFastPropertyValue( ::sal_Int32 nHandle, const css::uno::Any& aValue ) override;
+ virtual css::uno::Any SAL_CALL getFastPropertyValue( ::sal_Int32 nHandle ) override;
+
+private:
+ static rtl::Reference< FastPropertySetInfo > getStaticPropertySetInfo();
+
+ TableModelRef mxTableModel;
+ CellVector maCells;
+ sal_Int32 mnRow;
+ sal_Int32 mnHeight;
+ bool mbOptimalHeight;
+ bool mbIsVisible;
+ bool mbIsStartOfNewPage;
+ OUString maName;
+};
+
+}
+
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/table/tablerows.cxx b/svx/source/table/tablerows.cxx
new file mode 100644
index 000000000..8698f72f4
--- /dev/null
+++ b/svx/source/table/tablerows.cxx
@@ -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 .
+ */
+
+
+#include <com/sun/star/lang/DisposedException.hpp>
+
+#include <tablemodel.hxx>
+#include "tablerow.hxx"
+#include "tablerows.hxx"
+
+
+using namespace ::com::sun::star::uno;
+using namespace ::com::sun::star::lang;
+using namespace ::com::sun::star::container;
+using namespace ::com::sun::star::table;
+
+
+namespace sdr::table {
+
+TableRows::TableRows( const TableModelRef& xTableModel )
+: mxTableModel( xTableModel )
+{
+}
+
+
+TableRows::~TableRows()
+{
+ dispose();
+}
+
+
+void TableRows::dispose()
+{
+ mxTableModel.clear();
+}
+
+
+void TableRows::throwIfDisposed() const
+{
+ if( !mxTableModel.is() )
+ throw DisposedException();
+}
+
+
+// XTableRows
+
+
+void SAL_CALL TableRows::insertByIndex( sal_Int32 nIndex, sal_Int32 nCount )
+{
+ throwIfDisposed();
+ mxTableModel->insertRows( nIndex, nCount );
+}
+
+
+void SAL_CALL TableRows::removeByIndex( sal_Int32 nIndex, sal_Int32 nCount )
+{
+ throwIfDisposed();
+ mxTableModel->removeRows( nIndex, nCount );
+}
+
+
+// XIndexAccess
+
+
+sal_Int32 SAL_CALL TableRows::getCount()
+{
+ throwIfDisposed();
+ return mxTableModel->getRowCount();
+}
+
+
+Any SAL_CALL TableRows::getByIndex( sal_Int32 Index )
+{
+ throwIfDisposed();
+ return Any( Reference< XCellRange >( static_cast< XCellRange* >( mxTableModel->getRow( Index ).get() ) ) );
+}
+
+
+// XElementAccess
+
+
+Type SAL_CALL TableRows::getElementType()
+{
+ throwIfDisposed();
+ return cppu::UnoType<XCellRange>::get();
+}
+
+
+sal_Bool SAL_CALL TableRows::hasElements()
+{
+ throwIfDisposed();
+ return mxTableModel->getRowCount() != 0;
+}
+
+
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/table/tablerows.hxx b/svx/source/table/tablerows.hxx
new file mode 100644
index 000000000..3b25d3d9e
--- /dev/null
+++ b/svx/source/table/tablerows.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_SVX_SOURCE_TABLE_TABLEROWS_HXX
+#define INCLUDED_SVX_SOURCE_TABLE_TABLEROWS_HXX
+
+#include <com/sun/star/table/XTableRows.hpp>
+#include <cppuhelper/implbase1.hxx>
+
+#include <celltypes.hxx>
+
+namespace sdr::table
+{
+class TableRows : public ::cppu::WeakAggImplHelper1<css::table::XTableRows>
+{
+public:
+ explicit TableRows(const TableModelRef& xTableModel);
+ virtual ~TableRows() override;
+
+ void dispose();
+ /// @throws css::uno::RuntimeException
+ void throwIfDisposed() const;
+
+ // XTableRows
+ virtual void SAL_CALL insertByIndex(sal_Int32 nIndex, sal_Int32 nCount) override;
+ virtual void SAL_CALL removeByIndex(sal_Int32 nIndex, sal_Int32 nCount) override;
+
+ // XIndexAccess
+ virtual sal_Int32 SAL_CALL getCount() override;
+ virtual css::uno::Any SAL_CALL getByIndex(sal_Int32 Index) override;
+
+ // Methods
+ virtual css::uno::Type SAL_CALL getElementType() override;
+ virtual sal_Bool SAL_CALL hasElements() override;
+
+private:
+ TableModelRef mxTableModel;
+};
+}
+
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/table/tablertfexporter.cxx b/svx/source/table/tablertfexporter.cxx
new file mode 100644
index 000000000..c298d5d1a
--- /dev/null
+++ b/svx/source/table/tablertfexporter.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 <vector>
+
+#include <com/sun/star/table/XTable.hpp>
+#include <com/sun/star/beans/XPropertySet.hpp>
+
+#include <tools/diagnose_ex.h>
+#include <tools/stream.hxx>
+#include <svtools/rtfkeywd.hxx>
+#include <svtools/rtfout.hxx>
+
+#include <editeng/eeitem.hxx>
+#include <svx/sdtaitm.hxx>
+#include <editeng/wghtitem.hxx>
+#include <editeng/postitem.hxx>
+#include <editeng/udlnitem.hxx>
+
+#include <cell.hxx>
+#include <svx/svdotable.hxx>
+#include <svx/svdoutl.hxx>
+#include <editeng/editeng.hxx>
+#include <editeng/outlobj.hxx>
+
+
+using namespace ::com::sun::star::uno;
+using namespace ::com::sun::star::table;
+using namespace ::com::sun::star::container;
+using namespace ::com::sun::star::beans;
+
+namespace sdr::table {
+
+class SdrTableRtfExporter
+{
+public:
+ SdrTableRtfExporter( SvStream& rStrmP, SdrTableObj& rObj );
+ void Write();
+ void WriteRow( const Reference< XPropertySet >& xRowSet, sal_Int32 nRow, const std::vector< sal_Int32 >& aColumnStart );
+ void WriteCell( sal_Int32 nCol, sal_Int32 nRow );
+
+private:
+ SvStream& mrStrm;
+ SdrTableObj& mrObj;
+ Reference< XTable > mxTable;
+};
+
+void ExportAsRTF( SvStream& rStrm, SdrTableObj& rObj )
+{
+ SdrTableRtfExporter aEx( rStrm, rObj );
+ aEx.Write();
+}
+
+constexpr OUStringLiteral gsSize( u"Size" );
+
+SdrTableRtfExporter::SdrTableRtfExporter( SvStream& rStrm, SdrTableObj& rObj )
+: mrStrm( rStrm )
+, mrObj( rObj )
+, mxTable( rObj.getTable() )
+{
+}
+
+void SdrTableRtfExporter::Write()
+{
+ mrStrm.WriteChar( '{' ).WriteCharPtr( OOO_STRING_SVTOOLS_RTF_RTF );
+ mrStrm.WriteCharPtr( OOO_STRING_SVTOOLS_RTF_ANSI ).WriteCharPtr( SAL_NEWLINE_STRING );
+
+ Reference< XTableColumns > xColumns( mxTable->getColumns() );
+ const sal_Int32 nColCount = xColumns->getCount();
+
+ std::vector< sal_Int32 > aColumnStart;
+ aColumnStart.reserve( nColCount );
+
+ // determine right offset of cells
+ sal_Int32 nPos = 0;
+ for( sal_Int32 nCol = 0; nCol < nColCount; nCol++ ) try
+ {
+ Reference< XPropertySet > xSet( xColumns->getByIndex(nCol), UNO_QUERY_THROW );
+ sal_Int32 nWidth = 0;
+ xSet->getPropertyValue( gsSize ) >>= nWidth;
+ nPos += o3tl::toTwips(nWidth, o3tl::Length::mm100);
+ aColumnStart.push_back( nPos );
+ }
+ catch( Exception& )
+ {
+ TOOLS_WARN_EXCEPTION("svx", "");
+ }
+
+ // export rows
+ Reference< XTableRows > xRows( mxTable->getRows() );
+ const sal_Int32 nRowCount = xRows->getCount();
+
+ for( sal_Int32 nRow = 0; nRow < nRowCount; nRow++ ) try
+ {
+ Reference< XPropertySet > xRowSet( xRows->getByIndex(nRow), UNO_QUERY_THROW );
+ WriteRow( xRowSet, nRow, aColumnStart );
+ }
+ catch( Exception& )
+ {
+ TOOLS_WARN_EXCEPTION("svx", "");
+ }
+
+ mrStrm.WriteChar( '}' ).WriteCharPtr( SAL_NEWLINE_STRING );
+}
+
+void SdrTableRtfExporter::WriteRow( const Reference< XPropertySet >& xRowSet, sal_Int32 nRow, const std::vector< sal_Int32 >& aColumnStart )
+{
+ sal_Int32 nRowHeight = 0;
+ xRowSet->getPropertyValue( gsSize ) >>= nRowHeight;
+
+ mrStrm.WriteCharPtr( OOO_STRING_SVTOOLS_RTF_TROWD ).WriteCharPtr( OOO_STRING_SVTOOLS_RTF_TRGAPH ).WriteCharPtr( "30" ).WriteCharPtr( OOO_STRING_SVTOOLS_RTF_TRLEFT ).WriteCharPtr( "-30" );
+ mrStrm.WriteCharPtr( OOO_STRING_SVTOOLS_RTF_TRRH ).WriteOString( OString::number(nRowHeight) );
+
+ const sal_Int32 nColCount = mxTable->getColumnCount();
+ for( sal_Int32 nCol = 0; nCol < nColCount; nCol++ )
+ {
+ CellRef xCell( dynamic_cast< Cell* >( mxTable->getCellByPosition( nCol, nRow ).get() ) );
+
+ if( !xCell.is() )
+ continue;
+
+ mrStrm.WriteCharPtr( OOO_STRING_SVTOOLS_RTF_CELLX ).WriteOString( OString::number(aColumnStart[nCol]) );
+ if ( (nCol & 0x0F) == 0x0F )
+ mrStrm.WriteCharPtr( SAL_NEWLINE_STRING ); // prevent long lines
+ }
+ mrStrm.WriteCharPtr( OOO_STRING_SVTOOLS_RTF_PARD ).WriteCharPtr( OOO_STRING_SVTOOLS_RTF_PLAIN ).WriteCharPtr( OOO_STRING_SVTOOLS_RTF_INTBL ).WriteCharPtr( SAL_NEWLINE_STRING );
+
+ sal_uInt64 nStrmPos = mrStrm.Tell();
+ for( sal_Int32 nCol = 0; nCol < nColCount; nCol++ )
+ {
+ WriteCell( nCol, nRow );
+ if ( mrStrm.Tell() - nStrmPos > 255 )
+ {
+ mrStrm.WriteCharPtr( SAL_NEWLINE_STRING );
+ nStrmPos = mrStrm.Tell();
+ }
+ }
+ mrStrm.WriteCharPtr( OOO_STRING_SVTOOLS_RTF_ROW ).WriteCharPtr( SAL_NEWLINE_STRING );
+}
+
+
+void SdrTableRtfExporter::WriteCell( sal_Int32 nCol, sal_Int32 nRow )
+{
+ CellRef xCell( dynamic_cast< Cell* >( mxTable->getCellByPosition( nCol, nRow ).get() ) );
+
+ if( !xCell.is() || xCell->isMerged() )
+ {
+ mrStrm.WriteCharPtr( OOO_STRING_SVTOOLS_RTF_CELL );
+ return ;
+ }
+
+ OUString aContent;
+
+ std::optional<OutlinerParaObject> pParaObj = xCell->CreateEditOutlinerParaObject();
+
+ if( !pParaObj && xCell->GetOutlinerParaObject() )
+ pParaObj = *xCell->GetOutlinerParaObject();
+
+ if(pParaObj)
+ {
+ // handle outliner attributes
+ SdrOutliner& rOutliner = mrObj.ImpGetDrawOutliner();
+ rOutliner.SetText(*pParaObj);
+
+ aContent = rOutliner.GetEditEngine().GetText();
+
+ rOutliner.Clear();
+ }
+
+ bool bResetAttr = false;
+
+ SdrTextHorzAdjust eHAdj = xCell->GetTextHorizontalAdjust();
+
+ const SfxItemSet& rCellSet = xCell->GetItemSet();
+
+ const SvxWeightItem& rWeightItem = rCellSet.Get( EE_CHAR_WEIGHT );
+ const SvxPostureItem& rPostureItem = rCellSet.Get( EE_CHAR_ITALIC );
+ const SvxUnderlineItem& rUnderlineItem = rCellSet.Get( EE_CHAR_UNDERLINE );
+
+ const char* pChar;
+
+ switch( eHAdj )
+ {
+ case SDRTEXTHORZADJUST_CENTER: pChar = OOO_STRING_SVTOOLS_RTF_QC; break;
+ case SDRTEXTHORZADJUST_BLOCK: pChar = OOO_STRING_SVTOOLS_RTF_QJ; break;
+ case SDRTEXTHORZADJUST_RIGHT: pChar = OOO_STRING_SVTOOLS_RTF_QR; break;
+ case SDRTEXTHORZADJUST_LEFT:
+ default: pChar = OOO_STRING_SVTOOLS_RTF_QL; break;
+ }
+ mrStrm.WriteCharPtr( pChar );
+
+ if ( rWeightItem.GetWeight() >= WEIGHT_BOLD )
+ { // bold
+ bResetAttr = true;
+ mrStrm.WriteCharPtr( OOO_STRING_SVTOOLS_RTF_B );
+ }
+ if ( rPostureItem.GetPosture() != ITALIC_NONE )
+ { // italic
+ bResetAttr = true;
+ mrStrm.WriteCharPtr( OOO_STRING_SVTOOLS_RTF_I );
+ }
+ if ( rUnderlineItem.GetLineStyle() != LINESTYLE_NONE )
+ { // underline
+ bResetAttr = true;
+ mrStrm.WriteCharPtr( OOO_STRING_SVTOOLS_RTF_UL );
+ }
+
+ mrStrm.WriteChar( ' ' );
+ RTFOutFuncs::Out_String( mrStrm, aContent );
+ mrStrm.WriteCharPtr( OOO_STRING_SVTOOLS_RTF_CELL );
+
+ if ( bResetAttr )
+ mrStrm.WriteCharPtr( OOO_STRING_SVTOOLS_RTF_PLAIN );
+}
+
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/table/tablertfimporter.cxx b/svx/source/table/tablertfimporter.cxx
new file mode 100644
index 000000000..8f6e1c649
--- /dev/null
+++ b/svx/source/table/tablertfimporter.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 <vector>
+
+#include <com/sun/star/table/XTable.hpp>
+#include <com/sun/star/table/XMergeableCellRange.hpp>
+
+#include <tools/stream.hxx>
+#include <tools/UnitConversion.hxx>
+#include <svtools/rtftoken.h>
+
+#include <svx/svdetc.hxx>
+#include <editeng/outlobj.hxx>
+
+#include <cell.hxx>
+#include <svx/svdotable.hxx>
+#include <svx/svdoutl.hxx>
+#include <editeng/editeng.hxx>
+#include <editeng/editdata.hxx>
+#include <svx/svdmodel.hxx>
+#include <editeng/editids.hrc>
+#include <editeng/svxrtf.hxx>
+#include <sal/log.hxx>
+#include <tools/debug.hxx>
+#include <tools/diagnose_ex.h>
+
+using namespace ::com::sun::star::uno;
+using namespace ::com::sun::star::table;
+using namespace ::com::sun::star::container;
+using namespace ::com::sun::star::beans;
+
+namespace sdr::table {
+
+namespace {
+
+struct RTFCellDefault
+{
+ SfxItemSet maItemSet;
+ sal_Int32 mnRowSpan;
+ sal_Int32 mnColSpan; // MergeCell if >1, merged cells if 0
+ sal_Int32 mnCellX;
+
+ explicit RTFCellDefault( SfxItemPool* pPool ) : maItemSet( *pPool ), mnRowSpan(1), mnColSpan(1), mnCellX(0) {}
+};
+
+}
+
+typedef std::vector< std::shared_ptr< RTFCellDefault > > RTFCellDefaultVector;
+
+namespace {
+
+struct RTFCellInfo
+{
+ SfxItemSet maItemSet;
+ sal_Int32 mnStartPara;
+ sal_Int32 mnParaCount;
+ sal_Int32 mnCellX;
+ sal_Int32 mnRowSpan;
+ std::shared_ptr< RTFCellInfo > mxVMergeCell;
+
+ explicit RTFCellInfo( SfxItemPool& rPool ) : maItemSet( rPool ), mnStartPara(0), mnParaCount(0), mnCellX(0), mnRowSpan(1) {}
+};
+
+}
+
+typedef std::shared_ptr< RTFCellInfo > RTFCellInfoPtr;
+typedef std::vector< RTFCellInfoPtr > RTFColumnVector;
+
+typedef std::shared_ptr< RTFColumnVector > RTFColumnVectorPtr;
+
+class SdrTableRTFParser
+{
+public:
+ explicit SdrTableRTFParser( SdrTableObj& rTableObj );
+
+ void Read( SvStream& rStream );
+
+ void ProcToken( RtfImportInfo* pInfo );
+
+ void NextRow();
+ void NextColumn();
+ void NewCellRow();
+
+ void InsertCell( RtfImportInfo const * pInfo );
+ void InsertColumnEdge( sal_Int32 nEdge );
+
+ void FillTable();
+
+ DECL_LINK( RTFImportHdl, RtfImportInfo&, void );
+
+private:
+ SdrTableObj& mrTableObj;
+ std::unique_ptr<SdrOutliner> mpOutliner;
+ SfxItemPool& mrItemPool;
+
+ RTFCellDefaultVector maDefaultList;
+ RTFCellDefaultVector::iterator maDefaultIterator;
+
+ int mnLastToken;
+ bool mbNewDef;
+
+ sal_Int32 mnStartPara;
+
+ sal_Int32 mnRowCnt;
+ sal_Int32 mnLastEdge;
+ sal_Int32 mnVMergeIdx;
+
+ std::vector< sal_Int32 > maColumnEdges;
+ std::vector< sal_Int32 >::iterator maLastEdge;
+ std::vector< RTFColumnVectorPtr > maRows;
+
+ std::unique_ptr<RTFCellDefault> mpInsDefault;
+ RTFCellDefault* mpActDefault;
+ RTFCellDefault* mpDefMerge;
+
+ Reference< XTable > mxTable;
+
+ RTFColumnVectorPtr mxLastRow;
+ // Copy assignment is forbidden and not implemented.
+ SdrTableRTFParser (const SdrTableRTFParser &) = delete;
+ SdrTableRTFParser & operator= (const SdrTableRTFParser &) = delete;
+};
+
+SdrTableRTFParser::SdrTableRTFParser( SdrTableObj& rTableObj )
+: mrTableObj( rTableObj )
+, mpOutliner( SdrMakeOutliner( OutlinerMode::TextObject, rTableObj.getSdrModelFromSdrObject() ) )
+, mrItemPool( rTableObj.getSdrModelFromSdrObject().GetItemPool() )
+, mnLastToken( 0 )
+, mbNewDef( false )
+, mnStartPara( 0 )
+, mnRowCnt( 0 )
+, mnLastEdge( 0 )
+, mnVMergeIdx ( 0 )
+, mpActDefault( nullptr )
+, mpDefMerge( nullptr )
+, mxTable( rTableObj.getTable() )
+{
+ mpOutliner->SetUpdateLayout(true);
+ mpOutliner->SetStyleSheet( 0, mrTableObj.GetStyleSheet() );
+ mpInsDefault.reset( new RTFCellDefault( &mrItemPool ) );
+}
+
+void SdrTableRTFParser::Read( SvStream& rStream )
+{
+ EditEngine& rEdit = const_cast< EditEngine& >( mpOutliner->GetEditEngine() );
+
+ Link<RtfImportInfo&,void> aOldLink( rEdit.GetRtfImportHdl() );
+ rEdit.SetRtfImportHdl( LINK( this, SdrTableRTFParser, RTFImportHdl ) );
+ mpOutliner->Read( rStream, OUString(), EETextFormat::Rtf );
+ rEdit.SetRtfImportHdl( aOldLink );
+
+ FillTable();
+}
+
+IMPL_LINK( SdrTableRTFParser, RTFImportHdl, RtfImportInfo&, rInfo, void )
+{
+ switch ( rInfo.eState )
+ {
+ case RtfImportState::NextToken:
+ ProcToken( &rInfo );
+ break;
+ case RtfImportState::UnknownAttr:
+ ProcToken( &rInfo );
+ break;
+ case RtfImportState::Start:
+ {
+ SvxRTFParser* pParser = static_cast<SvxRTFParser*>(rInfo.pParser);
+ pParser->SetAttrPool( &mrItemPool );
+ pParser->SetPardMap(SID_ATTR_BORDER_OUTER, SDRATTR_TABLE_BORDER);
+ }
+ break;
+ case RtfImportState::End:
+ if ( rInfo.aSelection.nEndPos )
+ {
+ mpActDefault = nullptr;
+ rInfo.nToken = RTF_PAR;
+ rInfo.aSelection.nEndPara++;
+ ProcToken( &rInfo );
+ }
+ break;
+ case RtfImportState::SetAttr:
+ case RtfImportState::InsertText:
+ case RtfImportState::InsertPara:
+ break;
+ default:
+ SAL_WARN( "svx.table","unknown ImportInfo.eState");
+ }
+}
+
+void SdrTableRTFParser::NextRow()
+{
+ mxLastRow = maRows.back();
+ mnVMergeIdx = 0;
+ ++mnRowCnt;
+}
+
+void SdrTableRTFParser::InsertCell( RtfImportInfo const * pInfo )
+{
+
+ RTFCellInfoPtr xCellInfo = std::make_shared<RTFCellInfo>(mrItemPool);
+
+ xCellInfo->mnStartPara = mnStartPara;
+ xCellInfo->mnParaCount = pInfo->aSelection.nEndPara - 1 - mnStartPara;
+ xCellInfo->mnCellX = mpActDefault->mnCellX;
+ xCellInfo->mnRowSpan = mpActDefault->mnRowSpan;
+
+
+ if ( mxLastRow != nullptr )
+ {
+ sal_Int32 nSize = mxLastRow->size();
+ while( mnVMergeIdx < nSize &&
+ (*mxLastRow)[mnVMergeIdx]->mnCellX < xCellInfo->mnCellX )
+ ++mnVMergeIdx;
+
+ if ( xCellInfo->mnRowSpan == 0 && mnVMergeIdx < nSize )
+ {
+ RTFCellInfoPtr xLastCell( (*mxLastRow)[mnVMergeIdx] );
+ if (xLastCell->mnRowSpan)
+ xCellInfo->mxVMergeCell = xLastCell;
+ else
+ xCellInfo->mxVMergeCell = xLastCell->mxVMergeCell;
+ }
+ }
+
+ if( !maRows.empty() )
+ {
+ RTFColumnVectorPtr xColumn( maRows.back() );
+ if ( xCellInfo->mxVMergeCell )
+ {
+ if ( xColumn->empty() ||
+ xColumn->back()->mxVMergeCell != xCellInfo->mxVMergeCell )
+ xCellInfo->mxVMergeCell->mnRowSpan++;
+ }
+
+ xColumn->push_back( xCellInfo );
+ }
+
+ mnStartPara = pInfo->aSelection.nEndPara - 1;
+}
+
+void SdrTableRTFParser::InsertColumnEdge( sal_Int32 nEdge )
+{
+ auto aNextEdge = std::lower_bound( maLastEdge, maColumnEdges.end(), nEdge );
+
+ if ( aNextEdge == maColumnEdges.end() || nEdge != *aNextEdge )
+ {
+ maLastEdge = maColumnEdges.insert( aNextEdge , nEdge );
+ mnLastEdge = nEdge;
+ }
+}
+
+void SdrTableRTFParser::FillTable()
+{
+ try
+ {
+ sal_Int32 nColCount = mxTable->getColumnCount();
+ Reference< XTableColumns > xCols( mxTable->getColumns(), UNO_SET_THROW );
+ sal_Int32 nColMax = maColumnEdges.size();
+ if( nColCount < nColMax )
+ {
+ xCols->insertByIndex( nColCount, nColMax - nColCount );
+ nColCount = mxTable->getColumnCount();
+ }
+
+ static const OUStringLiteral sWidth(u"Width");
+ sal_Int32 nCol, nLastEdge = 0;
+ for( nCol = 0; nCol < nColCount; nCol++ )
+ {
+ Reference< XPropertySet > xSet( xCols->getByIndex( nCol ), UNO_QUERY_THROW );
+ sal_Int32 nWidth = maColumnEdges[nCol] - nLastEdge;
+
+ xSet->setPropertyValue( sWidth, Any( nWidth ) );
+ nLastEdge += nWidth;
+ }
+
+ const sal_Int32 nRowCount = mxTable->getRowCount();
+ if( nRowCount < mnRowCnt )
+ {
+ Reference< XTableRows > xRows( mxTable->getRows(), UNO_SET_THROW );
+ xRows->insertByIndex( nRowCount, mnRowCnt - nRowCount );
+ }
+
+ for( sal_Int32 nRow = 0; nRow < static_cast<sal_Int32>(maRows.size()); nRow++ )
+ {
+ RTFColumnVectorPtr xColumn( maRows[nRow] );
+ nCol = 0;
+ auto aEdge = maColumnEdges.begin();
+ for( sal_Int32 nIdx = 0; nCol < nColMax && nIdx < static_cast<sal_Int32>(xColumn->size()); nIdx++ )
+ {
+ RTFCellInfoPtr xCellInfo( (*xColumn)[nIdx] );
+
+ CellRef xCell( dynamic_cast< Cell* >( mxTable->getCellByPosition( nCol, nRow ).get() ) );
+ if( xCell.is() && xCellInfo )
+ {
+ const SfxPoolItem *pPoolItem = nullptr;
+ if( xCellInfo->maItemSet.GetItemState(SDRATTR_TABLE_BORDER,false,&pPoolItem)==SfxItemState::SET)
+ xCell->SetMergedItem( *pPoolItem );
+
+ std::optional<OutlinerParaObject> pTextObject(mpOutliner->CreateParaObject( xCellInfo->mnStartPara, xCellInfo->mnParaCount ));
+ if( pTextObject )
+ {
+ SdrOutliner& rOutliner=mrTableObj.ImpGetDrawOutliner();
+ rOutliner.SetUpdateLayout(true);
+ rOutliner.SetText( *pTextObject );
+ mrTableObj.NbcSetOutlinerParaObjectForText( rOutliner.CreateParaObject(), xCell.get() );
+ }
+
+ sal_Int32 nLastRow = nRow;
+ if ( xCellInfo->mnRowSpan )
+ nLastRow += xCellInfo->mnRowSpan - 1;
+
+ aEdge = std::lower_bound( aEdge, maColumnEdges.end(), xCellInfo->mnCellX );
+ sal_Int32 nLastCol = nCol;
+ if ( aEdge != maColumnEdges.end() )
+ {
+ nLastCol = std::distance( maColumnEdges.begin(), aEdge);
+ ++aEdge;
+ }
+
+ if ( nLastCol > nCol || nLastRow > nRow )
+ {
+ Reference< XMergeableCellRange > xRange( mxTable->createCursorByRange( mxTable->getCellRangeByPosition( nCol, nRow, nLastCol, nLastRow ) ), UNO_QUERY_THROW );
+ if( xRange->isMergeable() )
+ xRange->merge();
+ }
+ nCol = nLastCol + 1;
+ }
+ }
+ }
+
+ tools::Rectangle aRect( mrTableObj.GetSnapRect() );
+ aRect.SetRight( aRect.Left() + nLastEdge );
+ mrTableObj.NbcSetSnapRect( aRect );
+
+ }
+ catch( Exception& )
+ {
+ TOOLS_WARN_EXCEPTION("svx", "");
+ }
+}
+
+void SdrTableRTFParser::NewCellRow()
+{
+ if( mbNewDef )
+ {
+ mbNewDef = false;
+
+ maRows.push_back( std::make_shared<std::vector<std::shared_ptr<RTFCellInfo>>>( ) );
+ }
+ mpDefMerge = nullptr;
+ maDefaultIterator = maDefaultList.begin();
+
+ NextColumn();
+
+ DBG_ASSERT( mpActDefault, "NewCellRow: pActDefault==0" );
+}
+
+void SdrTableRTFParser::NextColumn()
+{
+ if( maDefaultIterator != maDefaultList.end() )
+ mpActDefault = (*maDefaultIterator++).get();
+ else
+ mpActDefault = nullptr;
+}
+
+void SdrTableRTFParser::ProcToken( RtfImportInfo* pInfo )
+{
+ switch ( pInfo->nToken )
+ {
+ case RTF_TROWD: // denotes table row default, before RTF_CELLX
+ {
+ maDefaultList.clear();
+ mpDefMerge = nullptr;
+ mnLastToken = pInfo->nToken;
+ maLastEdge = maColumnEdges.begin();
+ mnLastEdge = 0;
+ }
+ break;
+ case RTF_CLMGF: // The first cell of cells to be merged
+ {
+ mpDefMerge = mpInsDefault.get();
+ mnLastToken = pInfo->nToken;
+ }
+ break;
+ case RTF_CLMRG: // A cell to be merged with the preceding cell
+ {
+ if ( !mpDefMerge )
+ mpDefMerge = maDefaultList.back().get();
+ DBG_ASSERT( mpDefMerge, "RTF_CLMRG: pDefMerge==0" );
+ if( mpDefMerge )
+ mpDefMerge->mnColSpan++;
+ mpInsDefault->mnColSpan = 0;
+ mnLastToken = pInfo->nToken;
+ }
+ break;
+ case RTF_CLVMGF:
+ {
+ mnLastToken = pInfo->nToken;
+ }
+ break;
+ case RTF_CLVMRG:
+ {
+ mpInsDefault->mnRowSpan = 0;
+ mnLastToken = pInfo->nToken;
+ }
+ break;
+ case RTF_CELLX: // closes cell default
+ {
+ mbNewDef = true;
+ std::shared_ptr<RTFCellDefault> pDefault( mpInsDefault.release() );
+ maDefaultList.push_back( pDefault );
+
+
+ const sal_Int32 nSize = convertTwipToMm100(pInfo->nTokenValue);
+ if ( nSize > mnLastEdge )
+ InsertColumnEdge( nSize );
+
+ pDefault->mnCellX = nSize;
+ // Record cellx in the first merged cell.
+ if ( mpDefMerge && pDefault->mnColSpan == 0 )
+ mpDefMerge->mnCellX = nSize;
+
+ mpInsDefault.reset( new RTFCellDefault( &mrItemPool ) );
+
+ mnLastToken = pInfo->nToken;
+ }
+ break;
+ case RTF_INTBL: // before the first RTF_CELL
+ {
+ if ( mnLastToken != RTF_INTBL && mnLastToken != RTF_CELL && mnLastToken != RTF_PAR )
+ {
+ NewCellRow();
+ mnLastToken = pInfo->nToken;
+ }
+ }
+ break;
+ case RTF_CELL: // denotes the end of a cell.
+ {
+ DBG_ASSERT( mpActDefault, "RTF_CELL: pActDefault==0" );
+ if ( mbNewDef || !mpActDefault )
+ NewCellRow();
+ if ( !mpActDefault )
+ mpActDefault = mpInsDefault.get();
+ if ( mpActDefault->mnColSpan > 0 )
+ {
+ InsertCell(pInfo);
+ }
+ NextColumn();
+ mnLastToken = pInfo->nToken;
+ }
+ break;
+ case RTF_ROW: // means the end of a row
+ {
+ NextRow();
+ mnLastToken = pInfo->nToken;
+ }
+ break;
+ case RTF_PAR: // Paragraph
+ mnLastToken = pInfo->nToken;
+ break;
+ default:
+ { // do not set nLastToken
+ switch ( pInfo->nToken & ~(0xff | RTF_TABLEDEF) )
+ {
+ case RTF_SHADINGDEF:
+// ((SvxRTFParser*)pInfo->pParser)->ReadBackgroundAttr(pInfo->nToken, mpInsDefault->maItemSet, sal_True );
+ break;
+ case RTF_BRDRDEF:
+ static_cast<SvxRTFParser*>(pInfo->pParser)->ReadBorderAttr(pInfo->nToken, mpInsDefault->maItemSet, true );
+ break;
+ }
+ }
+ }
+}
+
+void ImportAsRTF( SvStream& rStream, SdrTableObj& rObj )
+{
+ SdrTableRTFParser aParser( rObj );
+ aParser.Read( rStream );
+}
+
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/table/tableundo.cxx b/svx/source/table/tableundo.cxx
new file mode 100644
index 000000000..66641469d
--- /dev/null
+++ b/svx/source/table/tableundo.cxx
@@ -0,0 +1,516 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+
+#include <sdr/properties/textproperties.hxx>
+#include <editeng/outlobj.hxx>
+
+#include <cell.hxx>
+#include "tableundo.hxx"
+#include <svx/svdotable.hxx>
+#include "tablerow.hxx"
+#include "tablecolumn.hxx"
+
+
+using namespace ::com::sun::star::uno;
+using namespace ::com::sun::star::table;
+
+
+namespace sdr::table {
+
+CellUndo::CellUndo( SdrObject* pObjRef, const CellRef& xCell )
+: SdrUndoAction(xCell->GetObject().getSdrModelFromSdrObject())
+ ,mxObjRef( pObjRef )
+ ,mxCell( xCell )
+ ,mbUndo( true )
+{
+ if( mxCell.is() && mxObjRef.is() )
+ {
+ getDataFromCell( maUndoData );
+ mxObjRef->AddObjectUser( *this );
+ }
+}
+
+CellUndo::~CellUndo()
+{
+ if( mxObjRef.is() )
+ mxObjRef->RemoveObjectUser( *this );
+ dispose();
+}
+
+void CellUndo::dispose()
+{
+ mxCell.clear();
+ delete maUndoData.mpProperties;
+ maUndoData.mpProperties = nullptr;
+ delete maRedoData.mpProperties;
+ maRedoData.mpProperties = nullptr;
+ maUndoData.mpOutlinerParaObject.reset();
+ maRedoData.mpOutlinerParaObject.reset();
+}
+
+void CellUndo::ObjectInDestruction(const SdrObject& )
+{
+ dispose();
+}
+
+void CellUndo::Undo()
+{
+ if( mxCell.is() && mbUndo )
+ {
+ if( maRedoData.mpProperties == nullptr )
+ getDataFromCell( maRedoData );
+
+ setDataToCell( maUndoData );
+ mbUndo = false;
+ }
+}
+
+void CellUndo::Redo()
+{
+ if( mxCell.is() && !mbUndo )
+ {
+ setDataToCell( maRedoData );
+ mbUndo = true;
+ }
+}
+
+bool CellUndo::Merge( SfxUndoAction *pNextAction )
+{
+ CellUndo* pNext = dynamic_cast< CellUndo* >( pNextAction );
+ return pNext && pNext->mxCell.get() == mxCell.get();
+}
+
+void CellUndo::setDataToCell( const Data& rData )
+{
+ if( rData.mpProperties )
+ mxCell->mpProperties.reset(Cell::CloneProperties( rData.mpProperties, *mxObjRef, *mxCell ));
+ else
+ mxCell->mpProperties.reset();
+
+ if( rData.mpOutlinerParaObject )
+ mxCell->SetOutlinerParaObject( *rData.mpOutlinerParaObject );
+ else
+ mxCell->RemoveOutlinerParaObject();
+
+ mxCell->msFormula = rData.msFormula;
+ mxCell->mfValue = rData.mfValue;
+ mxCell->mnError = rData.mnError;
+ mxCell->mbMerged = rData.mbMerged;
+ mxCell->mnRowSpan = rData.mnRowSpan;
+ mxCell->mnColSpan = rData.mnColSpan;
+
+ if(mxObjRef.is())
+ {
+ // #i120201# ActionChanged is not enough, we need to trigger TableLayouter::UpdateBorderLayout()
+ // and this is done best using ReformatText() for table objects
+ mxObjRef->ActionChanged();
+ mxObjRef->NbcReformatText();
+ }
+}
+
+void CellUndo::getDataFromCell( Data& rData )
+{
+ if( !(mxObjRef.is() && mxCell.is()) )
+ return;
+
+ if( mxCell->mpProperties )
+ rData.mpProperties = mxCell->CloneProperties( *mxObjRef, *mxCell);
+
+ if( mxCell->GetOutlinerParaObject() )
+ rData.mpOutlinerParaObject = *mxCell->GetOutlinerParaObject();
+ else
+ rData.mpOutlinerParaObject.reset();
+
+ rData.msFormula = mxCell->msFormula;
+ rData.mfValue = mxCell->mfValue;
+ rData.mnError = mxCell->mnError;
+ rData.mbMerged = mxCell->mbMerged;
+ rData.mnRowSpan = mxCell->mnRowSpan;
+ rData.mnColSpan = mxCell->mnColSpan;
+}
+
+
+// class InsertRowUndo : public SdrUndoAction
+
+
+static void Dispose( RowVector& rRows )
+{
+ for( auto& rpRow : rRows )
+ rpRow->dispose();
+}
+
+
+InsertRowUndo::InsertRowUndo( const TableModelRef& xTable, sal_Int32 nIndex, RowVector& aNewRows )
+: SdrUndoAction(xTable->getSdrTableObj()->getSdrModelFromSdrObject())
+ ,mxTable( xTable )
+ ,mnIndex( nIndex )
+ ,mbUndo( true )
+{
+ maRows.swap( aNewRows );
+}
+
+
+InsertRowUndo::~InsertRowUndo()
+{
+ if( !mbUndo )
+ Dispose( maRows );
+}
+
+
+void InsertRowUndo::Undo()
+{
+ if( mxTable.is() )
+ {
+ mxTable->UndoInsertRows( mnIndex, sal::static_int_cast< sal_Int32 >( maRows.size() ) );
+ mbUndo = false;
+ }
+}
+
+
+void InsertRowUndo::Redo()
+{
+ if( mxTable.is() )
+ {
+ mxTable->UndoRemoveRows( mnIndex, maRows );
+ mbUndo = true;
+ }
+}
+
+
+// class RemoveRowUndo : public SdrUndoAction
+
+
+RemoveRowUndo::RemoveRowUndo( const TableModelRef& xTable, sal_Int32 nIndex, RowVector& aRemovedRows )
+: SdrUndoAction(xTable->getSdrTableObj()->getSdrModelFromSdrObject())
+ ,mxTable( xTable )
+ ,mnIndex( nIndex )
+ ,mbUndo( true )
+{
+ maRows.swap( aRemovedRows );
+}
+
+
+RemoveRowUndo::~RemoveRowUndo()
+{
+ if( mbUndo )
+ Dispose( maRows );
+}
+
+
+void RemoveRowUndo::Undo()
+{
+ if( mxTable.is() )
+ {
+ mxTable->UndoRemoveRows( mnIndex, maRows );
+ mbUndo = false;
+ }
+}
+
+
+void RemoveRowUndo::Redo()
+{
+ if( mxTable.is() )
+ {
+ mxTable->UndoInsertRows( mnIndex, sal::static_int_cast< sal_Int32 >( maRows.size() ) );
+ mbUndo = true;
+ }
+}
+
+
+// class InsertColUndo : public SdrUndoAction
+
+
+static void Dispose( ColumnVector& rCols )
+{
+ for( auto& rpCol : rCols )
+ rpCol->dispose();
+}
+
+
+static void Dispose( CellVector& rCells )
+{
+ for( auto& rpCell : rCells )
+ rpCell->dispose();
+}
+
+
+InsertColUndo::InsertColUndo( const TableModelRef& xTable, sal_Int32 nIndex, ColumnVector& aNewCols, CellVector& aCells )
+: SdrUndoAction(xTable->getSdrTableObj()->getSdrModelFromSdrObject())
+ ,mxTable( xTable )
+ ,mnIndex( nIndex )
+ ,mbUndo( true )
+{
+ maColumns.swap( aNewCols );
+ maCells.swap( aCells );
+}
+
+
+InsertColUndo::~InsertColUndo()
+{
+ if( !mbUndo )
+ {
+ Dispose( maColumns );
+ Dispose( maCells );
+ }
+}
+
+
+void InsertColUndo::Undo()
+{
+ if( mxTable.is() )
+ {
+ mxTable->UndoInsertColumns( mnIndex, sal::static_int_cast< sal_Int32 >( maColumns.size() ) );
+ mbUndo = false;
+ }
+}
+
+
+void InsertColUndo::Redo()
+{
+ if( mxTable.is() )
+ {
+ mxTable->UndoRemoveColumns( mnIndex, maColumns, maCells );
+ mbUndo = true;
+ }
+}
+
+
+// class RemoveColUndo : public SdrUndoAction
+
+
+RemoveColUndo::RemoveColUndo( const TableModelRef& xTable, sal_Int32 nIndex, ColumnVector& aNewCols, CellVector& aCells )
+: SdrUndoAction(xTable->getSdrTableObj()->getSdrModelFromSdrObject())
+ ,mxTable( xTable )
+ ,mnIndex( nIndex )
+ ,mbUndo( true )
+{
+ maColumns.swap( aNewCols );
+ maCells.swap( aCells );
+}
+
+
+RemoveColUndo::~RemoveColUndo()
+{
+ if( mbUndo )
+ {
+ Dispose( maColumns );
+ Dispose( maCells );
+ }
+}
+
+
+void RemoveColUndo::Undo()
+{
+ if( mxTable.is() )
+ {
+ mxTable->UndoRemoveColumns( mnIndex, maColumns, maCells );
+ mbUndo = false;
+ }
+}
+
+
+void RemoveColUndo::Redo()
+{
+ if( mxTable.is() )
+ {
+ mxTable->UndoInsertColumns( mnIndex, sal::static_int_cast< sal_Int32 >( maColumns.size() ) );
+ mbUndo = true;
+ }
+}
+
+
+// class TableColumnUndo : public SdrUndoAction
+
+
+TableColumnUndo::TableColumnUndo( const TableColumnRef& xCol )
+: SdrUndoAction(xCol->mxTableModel->getSdrTableObj()->getSdrModelFromSdrObject())
+ ,mxCol( xCol )
+ ,mbHasRedoData( false )
+{
+ getData( maUndoData );
+}
+
+
+TableColumnUndo::~TableColumnUndo()
+{
+}
+
+
+void TableColumnUndo::Undo()
+{
+ if( !mbHasRedoData )
+ {
+ getData( maRedoData );
+ mbHasRedoData = true;
+ }
+ setData( maUndoData );
+}
+
+
+void TableColumnUndo::Redo()
+{
+ setData( maRedoData );
+}
+
+
+bool TableColumnUndo::Merge( SfxUndoAction *pNextAction )
+{
+ TableColumnUndo* pNext = dynamic_cast< TableColumnUndo* >( pNextAction );
+ return pNext && pNext->mxCol == mxCol;
+}
+
+
+void TableColumnUndo::setData( const Data& rData )
+{
+ mxCol->mnColumn = rData.mnColumn;
+ mxCol->mnWidth = rData.mnWidth;
+ mxCol->mbOptimalWidth = rData.mbOptimalWidth;
+ mxCol->mbIsVisible = rData.mbIsVisible;
+ mxCol->mbIsStartOfNewPage = rData.mbIsStartOfNewPage;
+ mxCol->maName = rData.maName;
+
+ // Trigger re-layout of the table.
+ mxCol->getModel()->setModified(true);
+}
+
+
+void TableColumnUndo::getData( Data& rData )
+{
+ rData.mnColumn = mxCol->mnColumn;
+ rData.mnWidth = mxCol->mnWidth;
+ rData.mbOptimalWidth = mxCol->mbOptimalWidth;
+ rData.mbIsVisible = mxCol->mbIsVisible;
+ rData.mbIsStartOfNewPage = mxCol->mbIsStartOfNewPage;
+ rData.maName = mxCol->maName;
+}
+
+
+// class TableRowUndo : public SdrUndoAction
+
+
+TableRowUndo::TableRowUndo( const TableRowRef& xRow )
+: SdrUndoAction(xRow->mxTableModel->getSdrTableObj()->getSdrModelFromSdrObject())
+ , mxRow( xRow )
+ , mbHasRedoData( false )
+{
+ getData( maUndoData );
+}
+
+
+TableRowUndo::~TableRowUndo()
+{
+}
+
+
+void TableRowUndo::Undo()
+{
+ if( !mbHasRedoData )
+ {
+ getData( maRedoData );
+ mbHasRedoData = true;
+ }
+ setData( maUndoData );
+}
+
+
+void TableRowUndo::Redo()
+{
+ setData( maRedoData );
+}
+
+
+bool TableRowUndo::Merge( SfxUndoAction *pNextAction )
+{
+ TableRowUndo* pNext = dynamic_cast< TableRowUndo* >( pNextAction );
+ return pNext && pNext->mxRow == mxRow;
+}
+
+
+void TableRowUndo::setData( const Data& rData )
+{
+ mxRow->mnRow = rData.mnRow;
+ mxRow->mnHeight = rData.mnHeight;
+ mxRow->mbOptimalHeight = rData.mbOptimalHeight;
+ mxRow->mbIsVisible = rData.mbIsVisible;
+ mxRow->mbIsStartOfNewPage = rData.mbIsStartOfNewPage;
+ mxRow->maName = rData.maName;
+
+ // Trigger re-layout of the table.
+ mxRow->getModel()->setModified(true);
+ }
+
+
+void TableRowUndo::getData( Data& rData )
+{
+ rData.mnRow = mxRow->mnRow;
+ rData.mnHeight = mxRow->mnHeight;
+ rData.mbOptimalHeight = mxRow->mbOptimalHeight;
+ rData.mbIsVisible = mxRow->mbIsVisible;
+ rData.mbIsStartOfNewPage = mxRow->mbIsStartOfNewPage;
+ rData.maName = mxRow->maName;
+}
+
+
+TableStyleUndo::TableStyleUndo( const SdrTableObj& rTableObj )
+: SdrUndoAction(rTableObj.getSdrModelFromSdrObject())
+ ,mxObjRef( const_cast< sdr::table::SdrTableObj*>( &rTableObj ) )
+ ,mbHasRedoData(false)
+{
+ getData( maUndoData );
+}
+
+void TableStyleUndo::Undo()
+{
+ if( !mbHasRedoData )
+ {
+ getData( maRedoData );
+ mbHasRedoData = true;
+ }
+ setData( maUndoData );
+}
+
+void TableStyleUndo::Redo()
+{
+ setData( maRedoData );
+}
+
+void TableStyleUndo::setData( const Data& rData )
+{
+ SdrTableObj* pTableObj = mxObjRef.get();
+ if( pTableObj )
+ {
+ pTableObj->setTableStyle( rData.mxTableStyle );
+ pTableObj->setTableStyleSettings( rData.maSettings );
+ }
+}
+
+void TableStyleUndo::getData( Data& rData )
+{
+ SdrTableObj* pTableObj = mxObjRef.get();
+ if( pTableObj )
+ {
+ rData.maSettings = pTableObj->getTableStyleSettings();
+ rData.mxTableStyle = pTableObj->getTableStyle();
+ }
+}
+
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/table/tableundo.hxx b/svx/source/table/tableundo.hxx
new file mode 100644
index 000000000..822f5ec64
--- /dev/null
+++ b/svx/source/table/tableundo.hxx
@@ -0,0 +1,258 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#ifndef INCLUDED_SVX_SOURCE_TABLE_TABLEUNDO_HXX
+#define INCLUDED_SVX_SOURCE_TABLE_TABLEUNDO_HXX
+
+#include <com/sun/star/container/XIndexAccess.hpp>
+
+#include <svx/svdotable.hxx>
+#include <svx/svdobj.hxx>
+#include <svx/svdundo.hxx>
+#include <svx/sdrobjectuser.hxx>
+
+#include <celltypes.hxx>
+
+namespace sdr::properties {
+ class TextProperties;
+}
+
+class OutlinerParaObject;
+
+
+namespace sdr::table {
+
+class CellUndo : public SdrUndoAction, public sdr::ObjectUser
+{
+public:
+ CellUndo( SdrObject* pObj, const CellRef& xCell );
+ virtual ~CellUndo() override;
+
+ virtual void Undo() override;
+ virtual void Redo() override;
+ virtual bool Merge( SfxUndoAction *pNextAction ) override;
+
+ void dispose();
+ virtual void ObjectInDestruction(const SdrObject& rObject) override;
+
+private:
+ struct Data
+ {
+ sdr::properties::TextProperties* mpProperties;
+ std::optional<OutlinerParaObject> mpOutlinerParaObject;
+
+ OUString msFormula;
+ double mfValue;
+ ::sal_Int32 mnError;
+ bool mbMerged;
+ ::sal_Int32 mnRowSpan;
+ ::sal_Int32 mnColSpan;
+
+ Data()
+ : mpProperties(nullptr)
+ , mfValue(0)
+ , mnError(0)
+ , mbMerged(false)
+ , mnRowSpan(0)
+ , mnColSpan(0)
+ {
+ }
+ };
+
+ void setDataToCell( const Data& rData );
+ void getDataFromCell( Data& rData );
+
+ tools::WeakReference<SdrObject> mxObjRef;
+ CellRef mxCell;
+ Data maUndoData;
+ Data maRedoData;
+ bool mbUndo;
+};
+
+
+class InsertRowUndo : public SdrUndoAction
+{
+public:
+ InsertRowUndo( const TableModelRef& xTable, sal_Int32 nIndex, RowVector& aNewRows );
+ virtual ~InsertRowUndo() override;
+
+ virtual void Undo() override;
+ virtual void Redo() override;
+
+private:
+ TableModelRef mxTable;
+ sal_Int32 mnIndex;
+ RowVector maRows;
+ bool mbUndo;
+};
+
+
+class RemoveRowUndo : public SdrUndoAction
+{
+public:
+ RemoveRowUndo( const TableModelRef& xTable, sal_Int32 nIndex, RowVector& aRemovedRows );
+ virtual ~RemoveRowUndo() override;
+
+ virtual void Undo() override;
+ virtual void Redo() override;
+
+private:
+ TableModelRef mxTable;
+ sal_Int32 mnIndex;
+ RowVector maRows;
+ bool mbUndo;
+};
+
+
+class InsertColUndo : public SdrUndoAction
+{
+public:
+ InsertColUndo( const TableModelRef& xTable, sal_Int32 nIndex, ColumnVector& aNewCols, CellVector& aCells );
+ virtual ~InsertColUndo() override;
+
+ virtual void Undo() override;
+ virtual void Redo() override;
+
+private:
+ TableModelRef mxTable;
+ sal_Int32 mnIndex;
+ ColumnVector maColumns;
+ CellVector maCells;
+ bool mbUndo;
+};
+
+
+class RemoveColUndo : public SdrUndoAction
+{
+public:
+ RemoveColUndo( const TableModelRef& xTable, sal_Int32 nIndex, ColumnVector& aNewCols, CellVector& aCells );
+ virtual ~RemoveColUndo() override;
+
+ virtual void Undo() override;
+ virtual void Redo() override;
+
+private:
+ TableModelRef mxTable;
+ sal_Int32 mnIndex;
+ ColumnVector maColumns;
+ CellVector maCells;
+ bool mbUndo;
+};
+
+
+class TableColumnUndo : public SdrUndoAction
+{
+public:
+ explicit TableColumnUndo( const TableColumnRef& xCol );
+ virtual ~TableColumnUndo() override;
+
+ virtual void Undo() override;
+ virtual void Redo() override;
+ virtual bool Merge( SfxUndoAction *pNextAction ) override;
+
+private:
+ struct Data
+ {
+ sal_Int32 mnColumn;
+ sal_Int32 mnWidth;
+ bool mbOptimalWidth;
+ bool mbIsVisible;
+ bool mbIsStartOfNewPage;
+ OUString maName;
+
+ Data()
+ : mnColumn(0)
+ , mnWidth(0)
+ , mbOptimalWidth(false)
+ , mbIsVisible(false)
+ , mbIsStartOfNewPage(false)
+ {
+ }
+ };
+
+ void setData( const Data& rData );
+ void getData( Data& rData );
+
+ TableColumnRef mxCol;
+ Data maUndoData;
+ Data maRedoData;
+ bool mbHasRedoData;
+};
+
+
+class TableRowUndo : public SdrUndoAction
+{
+public:
+ explicit TableRowUndo( const TableRowRef& xRow );
+ virtual ~TableRowUndo() override;
+
+ virtual void Undo() override;
+ virtual void Redo() override;
+ virtual bool Merge( SfxUndoAction *pNextAction ) override;
+
+private:
+ struct Data
+ {
+ sal_Int32 mnRow;
+ sal_Int32 mnHeight;
+ bool mbOptimalHeight;
+ bool mbIsVisible;
+ bool mbIsStartOfNewPage;
+ OUString maName;
+ };
+
+ void setData( const Data& rData );
+ void getData( Data& rData );
+
+ TableRowRef mxRow;
+ Data maUndoData;
+ Data maRedoData;
+ bool mbHasRedoData;
+};
+
+class TableStyleUndo : public SdrUndoAction
+{
+public:
+ explicit TableStyleUndo( const SdrTableObj& rTableObj );
+
+ virtual void Undo() override;
+ virtual void Redo() override;
+
+private:
+ tools::WeakReference<SdrTableObj> mxObjRef;
+
+ struct Data
+ {
+ TableStyleSettings maSettings;
+ css::uno::Reference< css::container::XIndexAccess > mxTableStyle;
+ };
+
+ void setData( const Data& rData );
+ void getData( Data& rData );
+
+ Data maUndoData;
+ Data maRedoData;
+ bool mbHasRedoData;
+};
+
+}
+
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/table/viewcontactoftableobj.cxx b/svx/source/table/viewcontactoftableobj.cxx
new file mode 100644
index 000000000..c550c5ec2
--- /dev/null
+++ b/svx/source/table/viewcontactoftableobj.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 "viewcontactoftableobj.hxx"
+#include <svx/svdotable.hxx>
+#include <com/sun/star/table/XTable.hpp>
+#include <basegfx/polygon/b2dpolygontools.hxx>
+#include <sdr/primitive2d/sdrattributecreator.hxx>
+#include <sdr/primitive2d/sdrdecompositiontools.hxx>
+#include <basegfx/matrix/b2dhommatrix.hxx>
+#include <sdr/attribute/sdrtextattribute.hxx>
+#include <svx/sdr/primitive2d/svx_primitivetypes2d.hxx>
+#include <editeng/borderline.hxx>
+#include <sdr/attribute/sdrfilltextattribute.hxx>
+#include <drawinglayer/primitive2d/BufferedDecompositionPrimitive2D.hxx>
+#include <drawinglayer/attribute/sdrlineattribute.hxx>
+#include <drawinglayer/attribute/sdrshadowattribute.hxx>
+#include <drawinglayer/primitive2d/sdrdecompositiontools2d.hxx>
+#include <drawinglayer/primitive2d/structuretagprimitive2d.hxx>
+#include <drawinglayer/primitive2d/transformprimitive2d.hxx>
+#include <basegfx/matrix/b2dhommatrixtools.hxx>
+#include <svx/sdr/contact/viewobjectcontactofsdrobj.hxx>
+#include <svx/sdr/contact/objectcontact.hxx>
+#include <svx/svdpage.hxx>
+#include <svx/framelink.hxx>
+#include <svx/framelinkarray.hxx>
+#include <svx/sdooitm.hxx>
+#include <vcl/canvastools.hxx>
+#include <o3tl/unit_conversion.hxx>
+#include <svx/xfltrit.hxx>
+
+#include <cell.hxx>
+#include "tablelayouter.hxx"
+
+
+using editeng::SvxBorderLine;
+using namespace com::sun::star;
+
+
+namespace drawinglayer::primitive2d
+{
+ namespace {
+
+ class SdrCellPrimitive2D : public BufferedDecompositionPrimitive2D
+ {
+ private:
+ basegfx::B2DHomMatrix maTransform;
+ attribute::SdrFillTextAttribute maSdrFTAttribute;
+
+ protected:
+ // local decomposition.
+ virtual void create2DDecomposition(Primitive2DContainer& rContainer, const geometry::ViewInformation2D& aViewInformation) const override;
+
+ public:
+ SdrCellPrimitive2D(
+ const basegfx::B2DHomMatrix& rTransform,
+ const attribute::SdrFillTextAttribute& rSdrFTAttribute)
+ : maTransform(rTransform),
+ maSdrFTAttribute(rSdrFTAttribute)
+ {
+ }
+
+ // data access
+ const basegfx::B2DHomMatrix& getTransform() const { return maTransform; }
+ const attribute::SdrFillTextAttribute& getSdrFTAttribute() const { return maSdrFTAttribute; }
+
+ // compare operator
+ virtual bool operator==(const BasePrimitive2D& rPrimitive) const override;
+
+ // provide unique ID
+ virtual sal_uInt32 getPrimitive2DID() const override;
+ };
+
+ }
+
+ void SdrCellPrimitive2D::create2DDecomposition(Primitive2DContainer& rContainer, const geometry::ViewInformation2D& /*aViewInformation*/) const
+ {
+ // prepare unit polygon
+ const basegfx::B2DPolyPolygon aUnitPolyPolygon(basegfx::utils::createUnitPolygon());
+
+ // add fill
+ if(!getSdrFTAttribute().getFill().isDefault())
+ {
+ basegfx::B2DPolyPolygon aTransformed(aUnitPolyPolygon);
+
+ aTransformed.transform(getTransform());
+ rContainer.push_back(
+ createPolyPolygonFillPrimitive(
+ aTransformed,
+ getSdrFTAttribute().getFill(),
+ getSdrFTAttribute().getFillFloatTransGradient()));
+ }
+ else
+ {
+ // if no fill create one for HitTest and BoundRect fallback
+ rContainer.push_back(
+ createHiddenGeometryPrimitives2D(
+ true,
+ aUnitPolyPolygon,
+ getTransform()));
+ }
+
+ // add text
+ if(!getSdrFTAttribute().getText().isDefault())
+ {
+ rContainer.push_back(
+ createTextPrimitive(
+ aUnitPolyPolygon,
+ getTransform(),
+ getSdrFTAttribute().getText(),
+ attribute::SdrLineAttribute(),
+ true,
+ false));
+ }
+ }
+
+ bool SdrCellPrimitive2D::operator==(const BasePrimitive2D& rPrimitive) const
+ {
+ if(BufferedDecompositionPrimitive2D::operator==(rPrimitive))
+ {
+ const SdrCellPrimitive2D& rCompare = static_cast<const SdrCellPrimitive2D&>(rPrimitive);
+
+ return (getTransform() == rCompare.getTransform()
+ && getSdrFTAttribute() == rCompare.getSdrFTAttribute());
+ }
+
+ return false;
+ }
+
+ // provide unique ID
+ sal_uInt32 SdrCellPrimitive2D::getPrimitive2DID() const
+ {
+ return PRIMITIVE2D_ID_SDRCELLPRIMITIVE2D;
+ }
+
+} // end of namespace
+
+namespace sdr::contact
+{
+
+ namespace {
+ class ViewObjectContactOfTableObj : public ViewObjectContactOfSdrObj
+ {
+ public:
+ ViewObjectContactOfTableObj(ObjectContact& rObjectContact, ViewContact& rViewContact)
+ : ViewObjectContactOfSdrObj(rObjectContact, rViewContact)
+ {
+ }
+
+ protected:
+ virtual void createPrimitive2DSequence(DisplayInfo const& rDisplayInfo, drawinglayer::primitive2d::Primitive2DDecompositionVisitor& rVisitor) const override;
+ };
+ } // namespace
+
+ static svx::frame::Style impGetLineStyle(
+ const sdr::table::TableLayouter& rLayouter,
+ sal_Int32 nX,
+ sal_Int32 nY,
+ bool bHorizontal,
+ sal_Int32 nColCount,
+ sal_Int32 nRowCount,
+ bool bIsRTL)
+ {
+ if(nX >= 0 && nX <= nColCount && nY >= 0 && nY <= nRowCount)
+ {
+ const SvxBorderLine* pLine = rLayouter.getBorderLine(nX, nY, bHorizontal);
+
+ if(pLine)
+ {
+ // copy line content
+ SvxBorderLine aLine(*pLine);
+
+ // check for mirroring. This shall always be done when it is
+ // not a top- or rightmost line
+ bool bMirror(aLine.isDouble());
+
+ if(bMirror)
+ {
+ if(bHorizontal)
+ {
+ // mirror all bottom lines
+ bMirror = (0 != nY);
+ }
+ else
+ {
+ // mirror all left lines
+ bMirror = (bIsRTL ? 0 != nX : nX != nColCount);
+ }
+ }
+
+ if(bMirror)
+ {
+ aLine.SetMirrorWidths( );
+ }
+
+ constexpr double fTwipsToMM(
+ o3tl::convert(1.0, o3tl::Length::twip, o3tl::Length::mm100));
+ return svx::frame::Style(&aLine, fTwipsToMM);
+ }
+ }
+
+ // no success, copy empty line
+ return svx::frame::Style();
+ }
+
+ static void createPrimitive2DSequenceImpl(
+ sdr::table::SdrTableObj const& rTableObj,
+ bool const isTaggedPDF,
+ drawinglayer::primitive2d::Primitive2DDecompositionVisitor& rVisitor)
+ {
+ const uno::Reference< css::table::XTable > xTable = rTableObj.getTable();
+
+ if(xTable.is())
+ {
+ // create primitive representation for table. Cell info goes
+ // directly to aRetval, Border info to aBorderSequence and added
+ // later to get the correct overlapping
+ drawinglayer::primitive2d::Primitive2DContainer aRetval;
+ drawinglayer::primitive2d::Primitive2DContainer aRetvalForShadow;
+ const sal_Int32 nRowCount(xTable->getRowCount());
+ const sal_Int32 nColCount(xTable->getColumnCount());
+ const sal_Int32 nAllCount(nRowCount * nColCount);
+ SdrPage const*const pPage(rTableObj.getSdrPageFromSdrObject());
+
+ if(nAllCount)
+ {
+ const sdr::table::TableLayouter& rTableLayouter(rTableObj.getTableLayouter());
+ const bool bIsRTL(css::text::WritingMode_RL_TB == rTableObj.GetWritingMode());
+ sdr::table::CellPos aCellPos;
+ sdr::table::CellRef xCurrentCell;
+ basegfx::B2IRectangle aCellArea;
+
+ // create range using the model data directly. This is in SdrTextObj::aRect which i will access using
+ // GetGeoRect() to not trigger any calculations. It's the unrotated geometry.
+ const basegfx::B2DRange aObjectRange = vcl::unotools::b2DRectangleFromRectangle(rTableObj.GetGeoRect());
+
+ // To create the CellBorderPrimitives, use the tooling from svx::frame::Array
+ // which is capable of creating the needed visualization. Fill it during the
+ // anyways needed run over the table.
+ svx::frame::Array aArray;
+
+ // initialize CellBorderArray for primitive creation
+ aArray.Initialize(nColCount, nRowCount);
+
+ // create single primitives per cell
+ for(aCellPos.mnRow = 0; aCellPos.mnRow < nRowCount; aCellPos.mnRow++)
+ {
+ drawinglayer::primitive2d::Primitive2DContainer row;
+ // add RowHeight to CellBorderArray for primitive creation
+ aArray.SetRowHeight(aCellPos.mnRow, rTableLayouter.getRowHeight(aCellPos.mnRow));
+
+ for(aCellPos.mnCol = 0; aCellPos.mnCol < nColCount; aCellPos.mnCol++)
+ {
+ drawinglayer::primitive2d::Primitive2DContainer cell;
+ // add ColWidth to CellBorderArray for primitive creation, only
+ // needs to be done in the 1st run
+ if(0 == aCellPos.mnRow)
+ {
+ aArray.SetColWidth(aCellPos.mnCol, rTableLayouter.getColumnWidth(aCellPos.mnCol));
+ }
+
+ // access the cell
+ xCurrentCell.set(dynamic_cast< sdr::table::Cell* >(xTable->getCellByPosition(aCellPos.mnCol, aCellPos.mnRow).get()));
+
+ if(xCurrentCell.is())
+ {
+ // copy styles for current cell to CellBorderArray for primitive creation
+ aArray.SetCellStyleLeft(aCellPos.mnCol, aCellPos.mnRow, impGetLineStyle(rTableLayouter, aCellPos.mnCol, aCellPos.mnRow, false, nColCount, nRowCount, bIsRTL));
+ aArray.SetCellStyleRight(aCellPos.mnCol, aCellPos.mnRow, impGetLineStyle(rTableLayouter, aCellPos.mnCol + 1, aCellPos.mnRow, false, nColCount, nRowCount, bIsRTL));
+ aArray.SetCellStyleTop(aCellPos.mnCol, aCellPos.mnRow, impGetLineStyle(rTableLayouter, aCellPos.mnCol, aCellPos.mnRow, true, nColCount, nRowCount, bIsRTL));
+ aArray.SetCellStyleBottom(aCellPos.mnCol, aCellPos.mnRow, impGetLineStyle(rTableLayouter, aCellPos.mnCol, aCellPos.mnRow + 1, true, nColCount, nRowCount, bIsRTL));
+
+ // ignore merged cells (all except the top-left of a merged cell)
+ if(!xCurrentCell->isMerged())
+ {
+ // check if we are the top-left of a merged cell
+ const sal_Int32 nXSpan(xCurrentCell->getColumnSpan());
+ const sal_Int32 nYSpan(xCurrentCell->getRowSpan());
+
+ if(nXSpan > 1 || nYSpan > 1)
+ {
+ // if merged, set so at CellBorderArray for primitive creation
+ aArray.SetMergedRange(aCellPos.mnCol, aCellPos.mnRow, aCellPos.mnCol + nXSpan - 1, aCellPos.mnRow + nYSpan - 1);
+ }
+ }
+ }
+
+ if(xCurrentCell.is() && !xCurrentCell->isMerged())
+ {
+ if(rTableLayouter.getCellArea(xCurrentCell, aCellPos, aCellArea))
+ {
+ // create cell transformation matrix
+ basegfx::B2DHomMatrix aCellMatrix;
+ aCellMatrix.set(0, 0, static_cast<double>(aCellArea.getWidth()));
+ aCellMatrix.set(1, 1, static_cast<double>(aCellArea.getHeight()));
+ aCellMatrix.set(0, 2, static_cast<double>(aCellArea.getMinX()) + aObjectRange.getMinX());
+ aCellMatrix.set(1, 2, static_cast<double>(aCellArea.getMinY()) + aObjectRange.getMinY());
+
+ // handle cell fillings and text
+ const SfxItemSet& rCellItemSet = xCurrentCell->GetItemSet();
+ const sal_uInt32 nTextIndex(nColCount * aCellPos.mnRow + aCellPos.mnCol);
+ const SdrText* pSdrText = rTableObj.getText(nTextIndex);
+ drawinglayer::attribute::SdrFillTextAttribute aAttribute;
+
+ if(pSdrText)
+ {
+ // #i101508# take cell's local text frame distances into account
+ const sal_Int32 nLeft(xCurrentCell->GetTextLeftDistance());
+ const sal_Int32 nRight(xCurrentCell->GetTextRightDistance());
+ const sal_Int32 nUpper(xCurrentCell->GetTextUpperDistance());
+ const sal_Int32 nLower(xCurrentCell->GetTextLowerDistance());
+
+ aAttribute = drawinglayer::primitive2d::createNewSdrFillTextAttribute(
+ rCellItemSet,
+ pSdrText,
+ &nLeft,
+ &nUpper,
+ &nRight,
+ &nLower);
+ }
+ else
+ {
+ aAttribute = drawinglayer::primitive2d::createNewSdrFillTextAttribute(
+ rCellItemSet,
+ pSdrText);
+ }
+
+ // always create cell primitives for BoundRect and HitTest
+ {
+ const drawinglayer::primitive2d::Primitive2DReference xCellReference(
+ new drawinglayer::primitive2d::SdrCellPrimitive2D(
+ aCellMatrix, aAttribute));
+ cell.append(xCellReference);
+ }
+
+ // Create cell primitive without text.
+ aAttribute
+ = drawinglayer::primitive2d::createNewSdrFillTextAttribute(
+ rCellItemSet, nullptr);
+ rtl::Reference pCellReference
+ = new drawinglayer::primitive2d::SdrCellPrimitive2D(
+ aCellMatrix, aAttribute);
+
+ sal_uInt16 nTransparence(
+ rCellItemSet.Get(XATTR_FILLTRANSPARENCE).GetValue());
+ if (nTransparence != 0)
+ {
+ pCellReference->setTransparenceForShadow(nTransparence);
+ }
+
+ const drawinglayer::primitive2d::Primitive2DReference
+ xCellReference(pCellReference);
+ aRetvalForShadow.append(xCellReference);
+ }
+ }
+ if (isTaggedPDF && pPage)
+ {
+ // heuristic: if there's a special formatting on
+ // first row, assume that it's a header row
+ auto const eType(
+ aCellPos.mnRow == 0 && rTableObj.getTableStyleSettings().mbUseFirstRow
+ ? vcl::PDFWriter::TableHeader
+ : vcl::PDFWriter::TableData);
+ cell = drawinglayer::primitive2d::Primitive2DContainer {
+ new drawinglayer::primitive2d::StructureTagPrimitive2D(
+ eType,
+ pPage->IsMasterPage(),
+ false,
+ std::move(cell)) };
+ }
+ row.append(cell);
+ }
+
+ if (isTaggedPDF && pPage)
+ {
+ row = drawinglayer::primitive2d::Primitive2DContainer {
+ new drawinglayer::primitive2d::StructureTagPrimitive2D(
+ vcl::PDFWriter::TableRow,
+ pPage->IsMasterPage(),
+ false,
+ std::move(row)) };
+ }
+ aRetval.append(row);
+ }
+
+ // now create all CellBorderPrimitives
+ drawinglayer::primitive2d::Primitive2DContainer aCellBorderPrimitives(aArray.CreateB2DPrimitiveArray());
+
+ if(!aCellBorderPrimitives.empty())
+ {
+ // this is already scaled (due to Table in non-uniform coordinates), so
+ // first transform removing scale
+ basegfx::B2DHomMatrix aTransform(
+ basegfx::utils::createScaleB2DHomMatrix(
+ 1.0 / aObjectRange.getWidth(),
+ 1.0 / aObjectRange.getHeight()));
+
+ // If RTL, mirror the whole unified table in X and move right.
+ // This is much easier than taking this into account for the whole
+ // index calculations
+ if(bIsRTL)
+ {
+ aTransform.scale(-1.0, 1.0);
+ aTransform.translate(1.0, 0.0);
+ }
+
+ // create object matrix
+ const GeoStat& rGeoStat(rTableObj.GetGeoStat());
+ const double fShearX(-rGeoStat.mfTanShearAngle);
+ const double fRotate(rGeoStat.nRotationAngle ? toRadians(36000_deg100 - rGeoStat.nRotationAngle) : 0.0);
+ const basegfx::B2DHomMatrix aObjectMatrix(basegfx::utils::createScaleShearXRotateTranslateB2DHomMatrix(
+ aObjectRange.getWidth(), aObjectRange.getHeight(), fShearX, fRotate,
+ aObjectRange.getMinX(), aObjectRange.getMinY()));
+
+ // add object matrix to transform. By doing so theoretically
+ // CellBorders could be also rotated/sheared for the first time ever.
+ // To completely make that work, the primitives already created in
+ // aRetval would also have to be based on ObjectMatrix, not only on
+ // ObjectRange as it currently is.
+ aTransform *= aObjectMatrix;
+
+ // create a transform primitive with this and embed CellBorders
+ // and append to retval
+ aRetval.append(
+ new drawinglayer::primitive2d::TransformPrimitive2D(
+ aTransform,
+ drawinglayer::primitive2d::Primitive2DContainer(aCellBorderPrimitives)));
+
+ // Borders are always the same for shadow as well.
+ aRetvalForShadow.append(new drawinglayer::primitive2d::TransformPrimitive2D(
+ aTransform, std::move(aCellBorderPrimitives)));
+ }
+ }
+
+ if(!aRetval.empty())
+ {
+ // check and create evtl. shadow for created content
+ const SfxItemSet& rObjectItemSet = rTableObj.GetMergedItemSet();
+ const drawinglayer::attribute::SdrShadowAttribute aNewShadowAttribute(
+ drawinglayer::primitive2d::createNewSdrShadowAttribute(rObjectItemSet));
+
+ if(!aNewShadowAttribute.isDefault())
+ {
+ bool bDirectShadow
+ = rObjectItemSet.Get(SDRATTR_SHADOW, /*bSrchInParent=*/false)
+ .GetValue();
+ if (bDirectShadow)
+ {
+ // Shadow as direct formatting: no shadow for text, to be compatible
+ // with PowerPoint.
+ basegfx::B2DHomMatrix aMatrix;
+ aRetval = drawinglayer::primitive2d::createEmbeddedShadowPrimitive(
+ std::move(aRetval), aNewShadowAttribute, aMatrix, &aRetvalForShadow);
+ }
+ else
+ {
+ // Shadow as style: shadow for text, to be backwards-compatible.
+ aRetval = drawinglayer::primitive2d::createEmbeddedShadowPrimitive(
+ std::move(aRetval), aNewShadowAttribute);
+ }
+ }
+ }
+
+ rVisitor.visit(std::move(aRetval));
+ }
+ else
+ {
+ // take unrotated snap rect (direct model data) for position and size
+ const basegfx::B2DRange aObjectRange = vcl::unotools::b2DRectangleFromRectangle(rTableObj.GetGeoRect());
+
+ // create object matrix
+ const GeoStat& rGeoStat(rTableObj.GetGeoStat());
+ const double fShearX(-rGeoStat.mfTanShearAngle);
+ const double fRotate(rGeoStat.nRotationAngle ? toRadians(36000_deg100 - rGeoStat.nRotationAngle) : 0.0);
+ const basegfx::B2DHomMatrix aObjectMatrix(basegfx::utils::createScaleShearXRotateTranslateB2DHomMatrix(
+ aObjectRange.getWidth(), aObjectRange.getHeight(), fShearX, fRotate,
+ aObjectRange.getMinX(), aObjectRange.getMinY()));
+
+ // created an invisible outline for the cases where no visible content exists
+ const drawinglayer::primitive2d::Primitive2DReference xReference(
+ drawinglayer::primitive2d::createHiddenGeometryPrimitives2D(
+ aObjectMatrix));
+
+ rVisitor.visit(xReference);
+ }
+ }
+
+ void ViewObjectContactOfTableObj::createPrimitive2DSequence(
+ DisplayInfo const& rDisplayInfo,
+ drawinglayer::primitive2d::Primitive2DDecompositionVisitor& rVisitor) const
+ {
+ bool const isTaggedPDF(GetObjectContact().isExportTaggedPDF());
+ if (isTaggedPDF)
+ {
+ // this will be unbuffered and contain structure tags
+ const sdr::table::SdrTableObj& rTableObj =
+ static_cast<const sdr::table::SdrTableObj&>(*GetViewContact().TryToGetSdrObject());
+ return createPrimitive2DSequenceImpl(rTableObj, true, rVisitor);
+ }
+ else
+ {
+ // call it via the base class - this is supposed to be buffered
+ return sdr::contact::ViewObjectContactOfSdrObj::createPrimitive2DSequence(rDisplayInfo, rVisitor);
+ }
+ }
+
+ void ViewContactOfTableObj::createViewIndependentPrimitive2DSequence(
+ drawinglayer::primitive2d::Primitive2DDecompositionVisitor& rVisitor) const
+ {
+ const sdr::table::SdrTableObj& rTableObj =
+ static_cast<const sdr::table::SdrTableObj&>(GetSdrObject());
+ return createPrimitive2DSequenceImpl(rTableObj, false, rVisitor);
+ }
+
+ ViewObjectContact& ViewContactOfTableObj::CreateObjectSpecificViewObjectContact(ObjectContact& rObjectContact)
+ {
+ return *new ViewObjectContactOfTableObj(rObjectContact, *this);
+ }
+
+ ViewContactOfTableObj::ViewContactOfTableObj(sdr::table::SdrTableObj& rTableObj)
+ : ViewContactOfSdrObj(rTableObj)
+ {
+ }
+
+ ViewContactOfTableObj::~ViewContactOfTableObj()
+ {
+ }
+} // end of namespace
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/table/viewcontactoftableobj.hxx b/svx/source/table/viewcontactoftableobj.hxx
new file mode 100644
index 000000000..fbdd80538
--- /dev/null
+++ b/svx/source/table/viewcontactoftableobj.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_SVX_SOURCE_TABLE_VIEWCONTACTOFTABLEOBJ_HXX
+#define INCLUDED_SVX_SOURCE_TABLE_VIEWCONTACTOFTABLEOBJ_HXX
+
+#include <svx/sdr/contact/viewcontactofsdrobj.hxx>
+#include <svx/svdotable.hxx>
+
+namespace sdr::contact
+ {
+ class ViewContactOfTableObj : public ViewContactOfSdrObj
+ {
+ protected:
+ // This method is responsible for creating the graphical visualisation data derived ONLY from
+ // the model data
+ virtual void createViewIndependentPrimitive2DSequence(drawinglayer::primitive2d::Primitive2DDecompositionVisitor& rVisitor) const override;
+ virtual ViewObjectContact& CreateObjectSpecificViewObjectContact(ObjectContact& rObjectContact) override;
+
+ public:
+ // basic constructor, used from SdrObject.
+ explicit ViewContactOfTableObj(sdr::table::SdrTableObj& rTextObj);
+ virtual ~ViewContactOfTableObj() override;
+ };
+} // end of namespace sdr::contact
+
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/tbxctrls/Palette.cxx b/svx/source/tbxctrls/Palette.cxx
new file mode 100644
index 000000000..84858c2eb
--- /dev/null
+++ b/svx/source/tbxctrls/Palette.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 <svx/Palette.hxx>
+#include <tools/stream.hxx>
+
+#include <palettes.hxx>
+
+Palette::~Palette()
+{
+}
+
+PaletteASE::~PaletteASE()
+{
+}
+
+PaletteASE::PaletteASE( const OUString &rFPath, const OUString &rFName ) :
+ mbValidPalette( false ),
+ maFPath ( rFPath ),
+ maASEPaletteName ( rFName )
+{
+ LoadPalette();
+}
+
+void PaletteASE::LoadColorSet(SvxColorValueSet& rColorSet)
+{
+ rColorSet.Clear();
+ int nIx = 1;
+ for (const auto& rColor : maColors)
+ {
+ rColorSet.InsertItem(nIx, rColor.first, rColor.second);
+ ++nIx;
+ }
+}
+
+const OUString& PaletteASE::GetName()
+{
+ return maASEPaletteName;
+}
+
+const OUString& PaletteASE::GetPath()
+{
+ return maFPath;
+}
+
+bool PaletteASE::IsValid()
+{
+ return mbValidPalette;
+}
+
+// CMYK values from 0 to 1
+// TODO: Deduplicate me (taken from core/cui/source/dialogs/colorpicker.cxx)
+static void lcl_CMYKtoRGB( float fCyan, float fMagenta, float fYellow, float fKey, float& dR, float& dG, float& dB )
+{
+ fCyan = (fCyan * ( 1.0 - fKey )) + fKey;
+ fMagenta = (fMagenta * ( 1.0 - fKey )) + fKey;
+ fYellow = (fYellow * ( 1.0 - fKey )) + fKey;
+
+ dR = std::clamp( 1.0 - fCyan, 0.0, 1.0 );
+ dG = std::clamp( 1.0 - fMagenta, 0.0, 1.0 );
+ dB = std::clamp( 1.0 - fYellow, 0.0, 1.0 );
+}
+
+void PaletteASE::LoadPalette()
+{
+ SvFileStream aFile(maFPath, StreamMode::READ);
+ aFile.SetEndian(SvStreamEndian::BIG);
+
+ // Verify magic first 4 characters
+ char cMagic[5] = {0};
+ if ((aFile.ReadBytes(cMagic, 4) != 4) || (strncmp(cMagic, "ASEF", 4) != 0))
+ {
+ mbValidPalette = false;
+ return;
+ }
+
+ // Ignore the version number
+ aFile.SeekRel(4);
+
+ sal_uInt32 nBlocks = 0;
+ aFile.ReadUInt32(nBlocks);
+ for (sal_uInt32 nI = 0; nI < nBlocks; nI++) {
+ sal_uInt32 nChunkType = 0;
+ aFile.ReadUInt32(nChunkType);
+ // End chunk
+ if (nChunkType == 0)
+ break;
+
+ // Grab chunk size, name length
+ sal_uInt16 nChunkSize = 0;
+ sal_uInt16 nChars = 0;
+ aFile.ReadUInt16(nChunkSize);
+ aFile.ReadUInt16(nChars);
+
+ OUString aPaletteName("");
+ if (nChars > 1)
+ aPaletteName = read_uInt16s_ToOUString(aFile, nChars);
+ else
+ aFile.SeekRel(2);
+
+ if (nChunkType == 0xC0010000)
+ {
+ // Got a start chunk, so set palette name
+ maASEPaletteName = aPaletteName;
+ // Is there color data? (shouldn't happen in a start block, but check anyway)
+ if (nChunkSize > ((nChars * 2) + 2))
+ aPaletteName.clear();
+ else
+ continue;
+ }
+
+ char cColorModel[5] = {0};
+ aFile.ReadBytes(cColorModel, 4);
+ OString aColorModel(cColorModel);
+ // r, g, and b are floats ranging from 0 to 1
+ float r = 0, g = 0, b = 0;
+
+ if (aColorModel.equalsIgnoreAsciiCase("cmyk"))
+ {
+ float c = 0, m = 0, y = 0, k = 0;
+ aFile.ReadFloat(c);
+ aFile.ReadFloat(m);
+ aFile.ReadFloat(y);
+ aFile.ReadFloat(k);
+ lcl_CMYKtoRGB(c, m, y, k, r, g, b);
+ }
+ else if (aColorModel.equalsIgnoreAsciiCase("rgb "))
+ {
+ aFile.ReadFloat(r);
+ aFile.ReadFloat(g);
+ aFile.ReadFloat(b);
+ }
+ else if (aColorModel.equalsIgnoreAsciiCase("gray"))
+ {
+ float nVal = 0;
+ aFile.ReadFloat(nVal);
+ r = g = b = nVal;
+ }
+ else
+ {
+ float nL = 0, nA = 0, nB = 0;
+ aFile.ReadFloat(nL);
+ aFile.ReadFloat(nA);
+ aFile.ReadFloat(nB);
+ // TODO: How to convert LAB to RGB?
+ r = g = b = 0;
+ }
+
+ // Ignore color type
+ aFile.SeekRel(2);
+ maColors.emplace_back(Color(r * 255, g * 255, b * 255), aPaletteName);
+ }
+
+ mbValidPalette = true;
+}
+
+// PaletteGPL ------------------------------------------------------------------
+
+static OString lcl_getToken(OStringBuffer& rStr, sal_Int32& index);
+
+PaletteGPL::PaletteGPL( const OUString &rFPath, const OUString &rFName ) :
+ mbLoadedPalette( false ),
+ mbValidPalette( false ),
+ maFName( rFName ),
+ maFPath( rFPath )
+{
+ LoadPaletteHeader();
+}
+
+PaletteGPL::~PaletteGPL()
+{
+}
+
+const OUString& PaletteGPL::GetName()
+{
+ return maGPLPaletteName;
+}
+
+const OUString& PaletteGPL::GetPath()
+{
+ return maFPath;
+}
+
+void PaletteGPL::LoadColorSet(SvxColorValueSet& rColorSet)
+{
+ LoadPalette();
+
+ rColorSet.Clear();
+ int nIx = 1;
+ for (const auto& rColor : maColors)
+ {
+ rColorSet.InsertItem(nIx, rColor.first, rColor.second);
+ ++nIx;
+ }
+}
+
+bool PaletteGPL::IsValid()
+{
+ return mbValidPalette;
+}
+
+bool PaletteGPL::ReadPaletteHeader(SvFileStream& rFileStream)
+{
+ OString aLine;
+ OString aPaletteName;
+
+ rFileStream.ReadLine(aLine);
+ if( !aLine.startsWith("GIMP Palette") ) return false;
+ rFileStream.ReadLine(aLine);
+ if( aLine.startsWith("Name: ", &aPaletteName) )
+ {
+ maGPLPaletteName = OStringToOUString(aPaletteName, RTL_TEXTENCODING_ASCII_US);
+ rFileStream.ReadLine(aLine);
+ if( aLine.startsWith("Columns: "))
+ rFileStream.ReadLine(aLine); // we can ignore this
+ }
+ else
+ {
+ maGPLPaletteName = maFName;
+ }
+ return true;
+}
+
+void PaletteGPL::LoadPaletteHeader()
+{
+ SvFileStream aFile(maFPath, StreamMode::READ);
+ mbValidPalette = ReadPaletteHeader( aFile );
+}
+
+void PaletteGPL::LoadPalette()
+{
+ if( mbLoadedPalette ) return;
+ mbLoadedPalette = true;
+
+ // TODO add error handling!!!
+ SvFileStream aFile(maFPath, StreamMode::READ);
+ mbValidPalette = ReadPaletteHeader( aFile );
+
+ if( !mbValidPalette ) return;
+
+ OStringBuffer aLine;
+ do {
+ if (aLine[0] != '#' && aLine[0] != '\n')
+ {
+ // TODO check if r,g,b are 0<= x <=255, or just clamp?
+ sal_Int32 nIndex = 0;
+ OString token;
+
+ token = lcl_getToken(aLine, nIndex);
+ if(token.isEmpty() || nIndex == -1) continue;
+ sal_Int32 r = token.toInt32();
+
+ token = lcl_getToken(aLine, nIndex);
+ if(token.isEmpty() || nIndex == -1) continue;
+ sal_Int32 g = token.toInt32();
+
+ token = lcl_getToken(aLine, nIndex);
+ if(token.isEmpty()) continue;
+ sal_Int32 b = token.toInt32();
+
+ std::string_view name;
+ if(nIndex != -1)
+ name = std::string_view(aLine).substr(nIndex);
+
+ maColors.emplace_back(
+ Color(r, g, b),
+ OStringToOUString(name, RTL_TEXTENCODING_ASCII_US));
+ }
+ } while (aFile.ReadLine(aLine));
+}
+
+// finds first token in rStr from index, separated by whitespace
+// returns position of next token in index
+static OString lcl_getToken(OStringBuffer& rStr, sal_Int32& index)
+{
+ sal_Int32 substart, toklen = 0;
+ OUString aWhitespaceChars( " \n\t" );
+
+ while(index < rStr.getLength() &&
+ aWhitespaceChars.indexOf( rStr[index] ) != -1)
+ ++index;
+ if(index == rStr.getLength())
+ {
+ index = -1;
+ return OString();
+ }
+ substart = index;
+
+ //counts length of token
+ while(index < rStr.getLength() &&
+ aWhitespaceChars.indexOf( rStr[index] ) == -1 )
+ {
+ ++index;
+ ++toklen;
+ }
+
+ //counts to position of next token
+ while(index < rStr.getLength() &&
+ aWhitespaceChars.indexOf( rStr[index] ) != -1 )
+ ++index;
+ if(index == rStr.getLength())
+ index = -1;
+
+ return OString(std::string_view(rStr).substr(substart, toklen));
+}
+
+// PaletteSOC ------------------------------------------------------------------
+
+PaletteSOC::PaletteSOC( const OUString &rFPath, const OUString &rFName ) :
+ mbLoadedPalette( false ),
+ maFPath( rFPath ),
+ maSOCPaletteName( rFName )
+{
+}
+
+PaletteSOC::~PaletteSOC()
+{
+}
+
+const OUString& PaletteSOC::GetName()
+{
+ return maSOCPaletteName;
+}
+
+const OUString& PaletteSOC::GetPath()
+{
+ return maFPath;
+}
+
+void PaletteSOC::LoadColorSet(SvxColorValueSet& rColorSet)
+{
+ if( !mbLoadedPalette )
+ {
+ mbLoadedPalette = true;
+ mpColorList = XPropertyList::AsColorList(XPropertyList::CreatePropertyListFromURL(XPropertyListType::Color, maFPath));
+ (void)mpColorList->Load();
+ }
+ rColorSet.Clear();
+ if( mpColorList.is() )
+ rColorSet.addEntriesForXColorList( *mpColorList );
+}
+
+bool PaletteSOC::IsValid()
+{
+ return true;
+}
+
+namespace svx
+{
+NamedColor NamedThemedColor::ToNamedColor() const { return { m_aColor, m_aName }; }
+
+NamedThemedColor NamedThemedColor::FromNamedColor(const NamedColor& rNamedColor)
+{
+ NamedThemedColor aColor;
+ aColor.m_aColor = rNamedColor.first;
+ aColor.m_aName = rNamedColor.second;
+ return aColor;
+}
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/tbxctrls/PaletteManager.cxx b/svx/source/tbxctrls/PaletteManager.cxx
new file mode 100644
index 000000000..55b2fdbd8
--- /dev/null
+++ b/svx/source/tbxctrls/PaletteManager.cxx
@@ -0,0 +1,442 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * 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/PaletteManager.hxx>
+
+#include <comphelper/propertyvalue.hxx>
+#include <tools/urlobj.hxx>
+#include <osl/file.hxx>
+#include <unotools/pathoptions.hxx>
+#include <sfx2/objsh.hxx>
+#include <svx/drawitem.hxx>
+#include <svx/strings.hrc>
+#include <svx/svxids.hrc>
+#include <svx/dialmgr.hxx>
+#include <tbxcolorupdate.hxx>
+#include <vcl/svapp.hxx>
+#include <vcl/settings.hxx>
+#include <comphelper/sequence.hxx>
+#include <stack>
+#include <set>
+#include <officecfg/Office/Common.hxx>
+#include <com/sun/star/frame/XDispatchProvider.hpp>
+#include <com/sun/star/frame/XDispatch.hpp>
+#include <com/sun/star/frame/Desktop.hpp>
+#include <com/sun/star/util/XURLTransformer.hpp>
+#include <com/sun/star/util/URLTransformer.hpp>
+
+#include <palettes.hxx>
+
+namespace
+{
+// Luminance modulation for the 6 effect presets.
+// 10000 is the default.
+sal_Int16 g_aLumMods[] = { 10000, 2000, 4000, 6000, 7500, 5000 };
+
+// Luminance offset for the 6 effect presets.
+// 0 is the default.
+sal_Int16 g_aLumOffs[] = { 0, 8000, 6000, 4000, 0, 0 };
+}
+
+PaletteManager::PaletteManager() :
+ mnMaxRecentColors(Application::GetSettings().GetStyleSettings().GetColorValueSetColumnCount()),
+ mnNumOfPalettes(3),
+ mnCurrentPalette(0),
+ mnColorCount(0),
+ mpBtnUpdater(nullptr),
+ maColorSelectFunction(PaletteManager::DispatchColorCommand)
+{
+ SfxObjectShell* pDocSh = SfxObjectShell::Current();
+ if(pDocSh)
+ {
+ const SfxPoolItem* pItem = nullptr;
+ if( nullptr != ( pItem = pDocSh->GetItem(SID_COLOR_TABLE) ) )
+ pColorList = static_cast<const SvxColorListItem*>(pItem)->GetColorList();
+ }
+ if(!pColorList.is())
+ pColorList = XColorList::CreateStdColorList();
+ LoadPalettes();
+ mnNumOfPalettes += m_Palettes.size();
+
+}
+
+PaletteManager::~PaletteManager()
+{
+}
+
+void PaletteManager::LoadPalettes()
+{
+ m_Palettes.clear();
+ OUString aPalPaths = SvtPathOptions().GetPalettePath();
+
+ std::stack<OUString> aDirs;
+ sal_Int32 nIndex = 0;
+ do
+ {
+ aDirs.push(aPalPaths.getToken(0, ';', nIndex));
+ }
+ while (nIndex >= 0);
+
+ std::set<OUString> aNames;
+ //try all entries palette path list user first, then
+ //system, ignoring duplicate file names
+ while (!aDirs.empty())
+ {
+ OUString aPalPath = aDirs.top();
+ aDirs.pop();
+
+ osl::Directory aDir(aPalPath);
+ osl::DirectoryItem aDirItem;
+ osl::FileStatus aFileStat( osl_FileStatus_Mask_FileName |
+ osl_FileStatus_Mask_FileURL |
+ osl_FileStatus_Mask_Type );
+ if( aDir.open() == osl::FileBase::E_None )
+ {
+ while( aDir.getNextItem(aDirItem) == osl::FileBase::E_None )
+ {
+ aDirItem.getFileStatus(aFileStat);
+ if(aFileStat.isRegular() || aFileStat.isLink())
+ {
+ OUString aFName = aFileStat.getFileName();
+ INetURLObject aURLObj( aFileStat.getFileURL() );
+ OUString aFNameWithoutExt = aURLObj.GetBase();
+ if (aNames.find(aFName) == aNames.end())
+ {
+ std::unique_ptr<Palette> pPalette;
+ if( aFName.endsWithIgnoreAsciiCase(".gpl") )
+ pPalette.reset(new PaletteGPL(aFileStat.getFileURL(), aFNameWithoutExt));
+ else if( aFName.endsWithIgnoreAsciiCase(".soc") )
+ pPalette.reset(new PaletteSOC(aFileStat.getFileURL(), aFNameWithoutExt));
+ else if ( aFName.endsWithIgnoreAsciiCase(".ase") )
+ pPalette.reset(new PaletteASE(aFileStat.getFileURL(), aFNameWithoutExt));
+
+ if( pPalette && pPalette->IsValid() )
+ m_Palettes.push_back( std::move(pPalette) );
+ aNames.insert(aFNameWithoutExt);
+ }
+ }
+ }
+ }
+ }
+}
+
+bool PaletteManager::IsThemePaletteSelected() const
+{
+ return mnCurrentPalette == mnNumOfPalettes - 2;
+}
+
+void PaletteManager::GetThemeIndexLumModOff(sal_uInt16 nItemId, sal_Int16& rThemeIndex,
+ sal_Int16& rLumMod, sal_Int16& rLumOff)
+{
+ // Each column is the same color with different effects.
+ rThemeIndex = nItemId % 12;
+
+ // Each row is the same effect with different colors.
+ rLumMod = g_aLumMods[nItemId / 12];
+ rLumOff = g_aLumOffs[nItemId / 12];
+}
+
+void PaletteManager::ReloadColorSet(SvxColorValueSet &rColorSet)
+{
+ if( mnCurrentPalette == 0)
+ {
+ rColorSet.Clear();
+ css::uno::Sequence< sal_Int32 > CustomColorList( officecfg::Office::Common::UserColors::CustomColor::get() );
+ css::uno::Sequence< OUString > CustomColorNameList( officecfg::Office::Common::UserColors::CustomColorName::get() );
+ int nIx = 1;
+ for (int i = 0; i < CustomColorList.getLength(); ++i)
+ {
+ Color aColor(ColorTransparency, CustomColorList[i]);
+ rColorSet.InsertItem(nIx, aColor, CustomColorNameList[i]);
+ ++nIx;
+ }
+ }
+ else if (IsThemePaletteSelected())
+ {
+ SfxObjectShell* pObjectShell = SfxObjectShell::Current();
+ if (pObjectShell)
+ {
+ std::vector<Color> aColors = pObjectShell->GetThemeColors();
+ mnColorCount = aColors.size();
+ rColorSet.Clear();
+ if (aColors.size() >= 12)
+ {
+ std::vector<OUString> aEffectNames = {
+ SvxResId(RID_SVXSTR_THEME_EFFECT1), SvxResId(RID_SVXSTR_THEME_EFFECT2),
+ SvxResId(RID_SVXSTR_THEME_EFFECT3), SvxResId(RID_SVXSTR_THEME_EFFECT4),
+ SvxResId(RID_SVXSTR_THEME_EFFECT5),
+ };
+
+ std::vector<OUString> aColorNames = {
+ SvxResId(RID_SVXSTR_THEME_COLOR1), SvxResId(RID_SVXSTR_THEME_COLOR2),
+ SvxResId(RID_SVXSTR_THEME_COLOR3), SvxResId(RID_SVXSTR_THEME_COLOR4),
+ SvxResId(RID_SVXSTR_THEME_COLOR5), SvxResId(RID_SVXSTR_THEME_COLOR6),
+ SvxResId(RID_SVXSTR_THEME_COLOR7), SvxResId(RID_SVXSTR_THEME_COLOR8),
+ SvxResId(RID_SVXSTR_THEME_COLOR9), SvxResId(RID_SVXSTR_THEME_COLOR10),
+ SvxResId(RID_SVXSTR_THEME_COLOR11), SvxResId(RID_SVXSTR_THEME_COLOR12),
+ };
+
+ sal_uInt16 nItemId = 0;
+ // Each row is one effect type (no effect + each type).
+ for (size_t nEffect = 0; nEffect < aEffectNames.size() + 1; ++nEffect)
+ {
+ // Each column is one color type.
+ for (size_t nColor = 0; nColor < aColorNames.size(); ++nColor)
+ {
+ Color aColor = aColors[nColor];
+ aColor.ApplyLumModOff(g_aLumMods[nEffect], g_aLumOffs[nEffect]);
+ OUString aColorName;
+ if (nEffect == 0)
+ {
+ aColorName = aColorNames[nColor];
+ }
+ else
+ {
+ aColorName = aEffectNames[nEffect - 1].replaceAll("%1", aColorNames[nColor]);
+ }
+ rColorSet.InsertItem(nItemId++, aColor, aColorName);
+ }
+ }
+ }
+ }
+ }
+ else if( mnCurrentPalette == mnNumOfPalettes - 1 )
+ {
+ // Add doc colors to palette
+ SfxObjectShell* pDocSh = SfxObjectShell::Current();
+ if (pDocSh)
+ {
+ std::set<Color> aColors = pDocSh->GetDocColors();
+ mnColorCount = aColors.size();
+ rColorSet.Clear();
+ rColorSet.addEntriesForColorSet(aColors, SvxResId( RID_SVXSTR_DOC_COLOR_PREFIX ) + " " );
+ }
+ }
+ else
+ {
+ m_Palettes[mnCurrentPalette - 1]->LoadColorSet( rColorSet );
+ mnColorCount = rColorSet.GetItemCount();
+ }
+}
+
+void PaletteManager::ReloadRecentColorSet(SvxColorValueSet& rColorSet)
+{
+ maRecentColors.clear();
+ rColorSet.Clear();
+ css::uno::Sequence< sal_Int32 > Colorlist(officecfg::Office::Common::UserColors::RecentColor::get());
+ css::uno::Sequence< OUString > ColorNamelist(officecfg::Office::Common::UserColors::RecentColorName::get());
+ int nIx = 1;
+ const bool bHasColorNames = Colorlist.getLength() == ColorNamelist.getLength();
+ for (int i = 0; i < Colorlist.getLength(); ++i)
+ {
+ Color aColor(ColorTransparency, Colorlist[i]);
+ OUString sColorName = bHasColorNames ? ColorNamelist[i] : ("#" + aColor.AsRGBHexString().toAsciiUpperCase());
+ maRecentColors.emplace_back(aColor, sColorName);
+ rColorSet.InsertItem(nIx, aColor, sColorName);
+ ++nIx;
+ }
+}
+
+std::vector<OUString> PaletteManager::GetPaletteList()
+{
+ std::vector<OUString> aPaletteNames
+ {
+ SvxResId( RID_SVXSTR_CUSTOM_PAL )
+ };
+ for (auto const& it : m_Palettes)
+ {
+ aPaletteNames.push_back( (*it).GetName() );
+ }
+ aPaletteNames.push_back(SvxResId(RID_SVXSTR_THEME_COLORS));
+ aPaletteNames.push_back( SvxResId ( RID_SVXSTR_DOC_COLORS ) );
+
+ return aPaletteNames;
+}
+
+void PaletteManager::SetPalette( sal_Int32 nPos )
+{
+ mnCurrentPalette = nPos;
+ if( nPos != mnNumOfPalettes - 1 && nPos != 0)
+ {
+ pColorList = XPropertyList::AsColorList(
+ XPropertyList::CreatePropertyListFromURL(
+ XPropertyListType::Color, GetSelectedPalettePath()));
+ auto name = GetPaletteName(); // may change pColorList
+ pColorList->SetName(name);
+ if(pColorList->Load())
+ {
+ SfxObjectShell* pShell = SfxObjectShell::Current();
+ if (pShell != nullptr)
+ {
+ SvxColorListItem aColorItem(pColorList, SID_COLOR_TABLE);
+ pShell->PutItem( aColorItem );
+ }
+ }
+ }
+ OUString aPaletteName(officecfg::Office::Common::UserColors::PaletteName::get());
+ if (aPaletteName != GetPaletteName())
+ {
+ std::shared_ptr<comphelper::ConfigurationChanges> batch(comphelper::ConfigurationChanges::create());
+ officecfg::Office::Common::UserColors::PaletteName::set(GetPaletteName(), batch);
+ batch->commit();
+ }
+}
+
+sal_Int32 PaletteManager::GetPalette() const
+{
+ return mnCurrentPalette;
+}
+
+OUString PaletteManager::GetPaletteName()
+{
+ std::vector<OUString> aNames(GetPaletteList());
+ if(mnCurrentPalette != mnNumOfPalettes - 1 && mnCurrentPalette != 0)
+ {
+ SfxObjectShell* pDocSh = SfxObjectShell::Current();
+ if(pDocSh)
+ {
+ const SfxPoolItem* pItem = nullptr;
+ if( nullptr != ( pItem = pDocSh->GetItem(SID_COLOR_TABLE) ) )
+ pColorList = static_cast<const SvxColorListItem*>(pItem)->GetColorList();
+ }
+ }
+ return aNames[mnCurrentPalette];
+}
+
+OUString PaletteManager::GetSelectedPalettePath()
+{
+ if (mnCurrentPalette < m_Palettes.size() && mnCurrentPalette != 0)
+ return m_Palettes[mnCurrentPalette - 1]->GetPath();
+ else
+ return OUString();
+}
+
+tools::Long PaletteManager::GetColorCount() const
+{
+ return mnColorCount;
+}
+
+tools::Long PaletteManager::GetRecentColorCount() const
+{
+ return maRecentColors.size();
+}
+
+void PaletteManager::AddRecentColor(const Color& rRecentColor, const OUString& rName, bool bFront)
+{
+ auto itColor = std::find_if(maRecentColors.begin(),
+ maRecentColors.end(),
+ [rRecentColor] (const NamedColor &a) { return a.first == rRecentColor; });
+ // if recent color to be added is already in list, remove it
+ if( itColor != maRecentColors.end() )
+ maRecentColors.erase( itColor );
+
+ if (maRecentColors.size() == mnMaxRecentColors)
+ maRecentColors.pop_back();
+ if (bFront)
+ maRecentColors.push_front(std::make_pair(rRecentColor, rName));
+ else
+ maRecentColors.emplace_back(rRecentColor, rName);
+ css::uno::Sequence< sal_Int32 > aColorList(maRecentColors.size());
+ auto aColorListRange = asNonConstRange(aColorList);
+ css::uno::Sequence< OUString > aColorNameList(maRecentColors.size());
+ auto aColorNameListRange = asNonConstRange(aColorNameList);
+ for (size_t i = 0; i < maRecentColors.size(); ++i)
+ {
+ aColorListRange[i] = static_cast<sal_Int32>(maRecentColors[i].first);
+ aColorNameListRange[i] = maRecentColors[i].second;
+ }
+ std::shared_ptr<comphelper::ConfigurationChanges> batch(comphelper::ConfigurationChanges::create());
+ officecfg::Office::Common::UserColors::RecentColor::set(aColorList, batch);
+ officecfg::Office::Common::UserColors::RecentColorName::set(aColorNameList, batch);
+ batch->commit();
+}
+
+void PaletteManager::SetBtnUpdater(svx::ToolboxButtonColorUpdaterBase* pBtnUpdater)
+{
+ mpBtnUpdater = pBtnUpdater;
+}
+
+void PaletteManager::SetColorSelectFunction(const ColorSelectFunction& aColorSelectFunction)
+{
+ maColorSelectFunction = aColorSelectFunction;
+}
+
+void PaletteManager::PopupColorPicker(weld::Window* pParent, const OUString& aCommand, const Color& rInitialColor)
+{
+ // The calling object goes away during aColorDlg.Execute(), so we must copy this
+ OUString aCommandCopy = aCommand;
+ m_pColorDlg = std::make_unique<SvColorDialog>();
+ m_pColorDlg->SetColor(rInitialColor);
+ m_pColorDlg->SetMode(svtools::ColorPickerMode::Modify);
+ m_pColorDlg->ExecuteAsync(pParent, [this, aCommandCopy] (sal_Int32 nResult) {
+ if (nResult == RET_OK)
+ {
+ Color aLastColor = m_pColorDlg->GetColor();
+ OUString sColorName = "#" + aLastColor.AsRGBHexString().toAsciiUpperCase();
+ NamedColor aNamedColor = std::make_pair(aLastColor, sColorName);
+ if (mpBtnUpdater)
+ mpBtnUpdater->Update(aNamedColor);
+ AddRecentColor(aLastColor, sColorName);
+ maColorSelectFunction(aCommandCopy, svx::NamedThemedColor::FromNamedColor(aNamedColor));
+ }
+ });
+}
+
+void PaletteManager::DispatchColorCommand(const OUString& aCommand, const svx::NamedThemedColor& rColor)
+{
+ using namespace css::uno;
+ using namespace css::frame;
+ using namespace css::beans;
+ using namespace css::util;
+
+ Reference<XComponentContext> xContext(comphelper::getProcessComponentContext());
+ Reference<XDesktop2> xDesktop = Desktop::create(xContext);
+ Reference<XFrame> xFrame(xDesktop->getCurrentFrame());
+ Reference<XDispatchProvider> xDispatchProvider(xFrame, UNO_QUERY);
+ if (!xDispatchProvider.is())
+ return;
+
+ INetURLObject aObj( aCommand );
+
+ std::vector<PropertyValue> aArgs{
+ comphelper::makePropertyValue(aObj.GetURLPath(), sal_Int32(rColor.m_aColor)),
+ };
+ if (rColor.m_nThemeIndex != -1)
+ {
+ aArgs.push_back(comphelper::makePropertyValue("ColorThemeIndex", rColor.m_nThemeIndex));
+ aArgs.push_back(comphelper::makePropertyValue("ColorLumMod", rColor.m_nLumMod));
+ aArgs.push_back(comphelper::makePropertyValue("ColorLumOff", rColor.m_nLumOff));
+ }
+
+ URL aTargetURL;
+ aTargetURL.Complete = aCommand;
+ Reference<XURLTransformer> xURLTransformer(URLTransformer::create(comphelper::getProcessComponentContext()));
+ xURLTransformer->parseStrict(aTargetURL);
+
+ Reference<XDispatch> xDispatch = xDispatchProvider->queryDispatch(aTargetURL, OUString(), 0);
+ if (xDispatch.is())
+ {
+ xDispatch->dispatch(aTargetURL, comphelper::containerToSequence(aArgs));
+ if (xFrame->getContainerWindow().is())
+ xFrame->getContainerWindow()->setFocus();
+ }
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/tbxctrls/StylesPreviewToolBoxControl.cxx b/svx/source/tbxctrls/StylesPreviewToolBoxControl.cxx
new file mode 100644
index 000000000..64e6ed782
--- /dev/null
+++ b/svx/source/tbxctrls/StylesPreviewToolBoxControl.cxx
@@ -0,0 +1,189 @@
+/* -*- 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 <StylesPreviewToolBoxControl.hxx>
+#include <cppuhelper/supportsservice.hxx>
+#include <vcl/svapp.hxx>
+#include <toolkit/helper/vclunohelper.hxx>
+#include <com/sun/star/uno/Reference.hxx>
+#include <com/sun/star/style/XStyleFamiliesSupplier.hpp>
+#include <com/sun/star/beans/XPropertySet.hpp>
+
+StylesPreviewToolBoxControl::StylesPreviewToolBoxControl() {}
+
+StylesPreviewToolBoxControl::~StylesPreviewToolBoxControl() {}
+
+void SAL_CALL
+StylesPreviewToolBoxControl::initialize(const css::uno::Sequence<css::uno::Any>& rArguments)
+{
+ svt::ToolboxController::initialize(rArguments);
+
+ if (m_xFrame.is())
+ {
+ InitializeStyles(m_xFrame->getController()->getModel());
+
+ m_xDispatchProvider = css::uno::Reference<css::frame::XDispatchProvider>(
+ m_xFrame->getController(), css::uno::UNO_QUERY);
+ }
+}
+
+void SAL_CALL StylesPreviewToolBoxControl::dispose()
+{
+ svt::ToolboxController::dispose();
+
+ SolarMutexGuard aSolarMutexGuard;
+ m_xVclBox.disposeAndClear();
+ m_xWeldBox.reset();
+}
+
+void StylesPreviewToolBoxControl::InitializeStyles(
+ const css::uno::Reference<css::frame::XModel>& xModel)
+{
+ m_aDefaultStyles.clear();
+
+ //now convert the default style names to the localized names
+ try
+ {
+ css::uno::Reference<css::style::XStyleFamiliesSupplier> xStylesSupplier(
+ xModel, css::uno::UNO_QUERY_THROW);
+ css::uno::Reference<css::lang::XServiceInfo> xServices(xModel, css::uno::UNO_QUERY_THROW);
+ if (xServices->supportsService("com.sun.star.text.TextDocument"))
+ {
+ css::uno::Reference<css::container::XNameAccess> xParaStyles;
+ xStylesSupplier->getStyleFamilies()->getByName("ParagraphStyles") >>= xParaStyles;
+ static const std::vector<OUString> aWriterStyles
+ = { "Standard", "Text body", "Heading 1", "Heading 2", "Heading 3",
+ "Heading 4", "Title", "Subtitle", "Quotations", "Preformatted Text" };
+ for (const OUString& aStyle : aWriterStyles)
+ {
+ try
+ {
+ css::uno::Reference<css::beans::XPropertySet> xStyle;
+ xParaStyles->getByName(aStyle) >>= xStyle;
+ OUString sName;
+ xStyle->getPropertyValue("DisplayName") >>= sName;
+ if (!sName.isEmpty())
+ m_aDefaultStyles.push_back(std::pair<OUString, OUString>(aStyle, sName));
+ }
+ catch (const css::container::NoSuchElementException&)
+ {
+ }
+ catch (const css::uno::Exception&)
+ {
+ }
+ }
+ }
+ else if (xServices->supportsService("com.sun.star.sheet.SpreadsheetDocument"))
+ {
+ static const char* aCalcStyles[] = { "Default", "Accent 1", "Accent 2", "Accent 3",
+ "Heading 1", "Heading 2", "Result" };
+ css::uno::Reference<css::container::XNameAccess> xCellStyles;
+ xStylesSupplier->getStyleFamilies()->getByName("CellStyles") >>= xCellStyles;
+ for (const char* pCalcStyle : aCalcStyles)
+ {
+ try
+ {
+ const OUString sStyleName(OUString::createFromAscii(pCalcStyle));
+ if (xCellStyles->hasByName(sStyleName))
+ {
+ css::uno::Reference<css::beans::XPropertySet> xStyle(
+ xCellStyles->getByName(sStyleName), css::uno::UNO_QUERY_THROW);
+ OUString sName;
+ xStyle->getPropertyValue("DisplayName") >>= sName;
+ if (!sName.isEmpty())
+ {
+ m_aDefaultStyles.push_back(
+ std::pair<OUString, OUString>(sStyleName, sName));
+ }
+ }
+ }
+ catch (const css::uno::Exception&)
+ {
+ }
+ }
+ }
+ }
+ catch (const css::uno::Exception&)
+ {
+ OSL_FAIL("error while initializing style names");
+ }
+}
+
+void SAL_CALL StylesPreviewToolBoxControl::update() {}
+
+void StylesPreviewToolBoxControl::statusChanged(const css::frame::FeatureStateEvent& /*rEvent*/) {}
+
+css::uno::Reference<css::awt::XWindow>
+StylesPreviewToolBoxControl::createItemWindow(const css::uno::Reference<css::awt::XWindow>& rParent)
+{
+ css::uno::Reference<css::awt::XWindow> xItemWindow;
+
+ /* TODO
+ if (m_pBuilder)
+ {
+ SolarMutexGuard aSolarMutexGuard;
+
+ std::unique_ptr<weld::Container> xWidget(*m_pBuilder);
+
+ xItemWindow
+ = css::uno::Reference<css::awt::XWindow>(new weld::TransportAsXWindow(xWidget.get()));
+
+ m_xWeldBox.reset(new StylesPreviewWindow_Base(std::move(xWidget)));
+ m_pBox = m_xWeldBox.get();
+ }
+ else
+ */
+ {
+ VclPtr<vcl::Window> pParent = VCLUnoHelper::GetWindow(rParent);
+ if (pParent)
+ {
+ SolarMutexGuard aSolarMutexGuard;
+
+ m_xVclBox = VclPtr<StylesPreviewWindow_Impl>::Create(
+ pParent, std::vector(m_aDefaultStyles), m_xDispatchProvider);
+ xItemWindow = VCLUnoHelper::GetInterface(m_xVclBox);
+ }
+ }
+
+ return xItemWindow;
+}
+
+OUString StylesPreviewToolBoxControl::getImplementationName()
+{
+ return "com.sun.star.comp.svx.StylesPreviewToolBoxControl";
+}
+
+sal_Bool StylesPreviewToolBoxControl::supportsService(const OUString& rServiceName)
+{
+ return cppu::supportsService(this, rServiceName);
+}
+
+css::uno::Sequence<OUString> StylesPreviewToolBoxControl::getSupportedServiceNames()
+{
+ return { "com.sun.star.frame.ToolbarController" };
+}
+
+extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface*
+com_sun_star_comp_svx_StylesPreviewToolBoxControl_get_implementation(
+ css::uno::XComponentContext*, css::uno::Sequence<css::uno::Any> const&)
+{
+ return cppu::acquire(new StylesPreviewToolBoxControl());
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab cinoptions=b1,g0,N-s cinkeys+=0=break: */
diff --git a/svx/source/tbxctrls/StylesPreviewWindow.cxx b/svx/source/tbxctrls/StylesPreviewWindow.cxx
new file mode 100644
index 000000000..e786c7b60
--- /dev/null
+++ b/svx/source/tbxctrls/StylesPreviewWindow.cxx
@@ -0,0 +1,520 @@
+/* -*- 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 <StylesPreviewWindow.hxx>
+
+#include <comphelper/propertyvalue.hxx>
+#include <vcl/svapp.hxx>
+#include <sfx2/objsh.hxx>
+#include <svl/itemset.hxx>
+#include <sfx2/tbxctrl.hxx>
+#include <sfx2/sfxsids.hrc>
+#include <sfx2/tplpitem.hxx>
+#include <sfx2/viewsh.hxx>
+#include <vcl/glyphitemcache.hxx>
+#include <vcl/virdev.hxx>
+#include <vcl/settings.hxx>
+
+#include <editeng/editids.hrc>
+#include <editeng/fontitem.hxx>
+#include <editeng/fhgtitem.hxx>
+#include <editeng/svxfont.hxx>
+#include <editeng/wghtitem.hxx>
+#include <editeng/postitem.hxx>
+#include <editeng/contouritem.hxx>
+#include <editeng/shdditem.hxx>
+#include <editeng/charreliefitem.hxx>
+#include <editeng/udlnitem.hxx>
+#include <editeng/crossedoutitem.hxx>
+#include <editeng/colritem.hxx>
+#include <editeng/cmapitem.hxx>
+#include <editeng/emphasismarkitem.hxx>
+#include <editeng/brushitem.hxx>
+
+#include <i18nlangtag/mslangid.hxx>
+
+#include <svx/xfillit0.hxx>
+#include <svx/xdef.hxx>
+#include <svx/xflclit.hxx>
+
+#include <com/sun/star/drawing/FillStyle.hpp>
+#include <com/sun/star/i18n/ScriptType.hpp>
+#include <com/sun/star/uno/Sequence.hxx>
+
+#include <vcl/commandevent.hxx>
+
+StyleStatusListener::StyleStatusListener(
+ StylesPreviewWindow_Base* pPreviewControl,
+ const css::uno::Reference<css::frame::XDispatchProvider>& xDispatchProvider)
+ : SfxStatusListener(xDispatchProvider, SID_STYLE_FAMILY2, ".uno:ParaStyle")
+ , m_pPreviewControl(pPreviewControl)
+{
+ ReBind();
+}
+
+void StyleStatusListener::StateChangedAtStatusListener(SfxItemState /*eState*/,
+ const SfxPoolItem* pState)
+{
+ const SfxTemplateItem* pStateItem = dynamic_cast<const SfxTemplateItem*>(pState);
+ if (pStateItem)
+ {
+ if (pStateItem->GetStyleIdentifier().isEmpty())
+ m_pPreviewControl->Select(pStateItem->GetStyleName());
+ else
+ m_pPreviewControl->Select(pStateItem->GetStyleIdentifier());
+ }
+}
+
+StylePoolChangeListener::StylePoolChangeListener(StylesPreviewWindow_Base* pPreviewControl)
+ : m_pPreviewControl(pPreviewControl)
+{
+ SfxObjectShell* pDocShell = SfxObjectShell::Current();
+
+ m_pStyleSheetPool = pDocShell ? pDocShell->GetStyleSheetPool() : nullptr;
+
+ if (m_pStyleSheetPool)
+ {
+ StartListening(*m_pStyleSheetPool);
+ }
+}
+
+StylePoolChangeListener::~StylePoolChangeListener()
+{
+ if (m_pStyleSheetPool)
+ EndListening(*m_pStyleSheetPool);
+}
+
+void StylePoolChangeListener::Notify(SfxBroadcaster& /*rBC*/, const SfxHint& /*rHint*/)
+{
+ m_pPreviewControl->RequestStylesListUpdate();
+}
+
+StyleItemController::StyleItemController(const std::pair<OUString, OUString>& aStyleName)
+ : m_eStyleFamily(SfxStyleFamily::Para)
+ , m_aStyleName(aStyleName)
+{
+}
+
+void StyleItemController::Paint(vcl::RenderContext& rRenderContext)
+{
+ rRenderContext.Push(vcl::PushFlags::FILLCOLOR | vcl::PushFlags::FONT
+ | vcl::PushFlags::TEXTCOLOR);
+
+ DrawEntry(rRenderContext);
+
+ rRenderContext.Pop();
+}
+
+bool StylesPreviewWindow_Base::Command(const CommandEvent& rEvent)
+{
+ if (rEvent.GetCommand() != CommandEventId::ContextMenu)
+ return false;
+
+ std::unique_ptr<weld::Builder> xBuilder(
+ Application::CreateBuilder(m_xStylesView.get(), "svx/ui/stylemenu.ui"));
+ std::unique_ptr<weld::Menu> xMenu(xBuilder->weld_menu("menu"));
+ OString rIdent = xMenu->popup_at_rect(m_xStylesView.get(),
+ tools::Rectangle(rEvent.GetMousePosPixel(), Size(1, 1)));
+ if (rIdent == "update" || rIdent == "edit")
+ {
+ css::uno::Sequence<css::beans::PropertyValue> aArgs(0);
+
+ SfxToolBoxControl::Dispatch(m_xDispatchProvider,
+ rIdent == "update" ? OUString(".uno:StyleUpdateByExample")
+ : OUString(".uno:EditStyle"),
+ aArgs);
+
+ return true;
+ }
+
+ return false;
+}
+
+static Color GetTextColorFromItemSet(std::optional<SfxItemSet> const& pItemSet)
+{
+ const SfxPoolItem* pItem = pItemSet->GetItem(SID_ATTR_CHAR_COLOR);
+ if (pItem)
+ return static_cast<const SvxColorItem*>(pItem)->GetValue();
+
+ return COL_AUTO;
+}
+
+static Color GetHighlightColorFromItemSet(std::optional<SfxItemSet> const& pItemSet)
+{
+ const SfxPoolItem* pItem = pItemSet->GetItem(SID_ATTR_BRUSH_CHAR);
+ if (pItem)
+ return static_cast<const SvxBrushItem*>(pItem)->GetColor();
+
+ return COL_AUTO;
+}
+
+static Color GetBackgroundColorFromItemSet(std::optional<SfxItemSet> const& pItemSet)
+{
+ const SfxPoolItem* pItem = pItemSet->GetItem(XATTR_FILLCOLOR);
+ if (pItem)
+ return static_cast<const XFillColorItem*>(pItem)->GetColorValue();
+
+ return COL_AUTO;
+}
+
+static css::drawing::FillStyle GetFillStyleFromItemSet(std::optional<SfxItemSet> const& pItemSet)
+{
+ const SfxPoolItem* pItem = pItemSet->GetItem(XATTR_FILLSTYLE);
+ if (pItem)
+ return static_cast<const XFillStyleItem*>(pItem)->GetValue();
+
+ return css::drawing::FillStyle_NONE;
+}
+
+static SvxFont GetFontFromItems(const SvxFontItem* pFontItem, Size aPixelFontSize,
+ std::optional<SfxItemSet> const& pItemSet)
+{
+ SvxFont aFont;
+
+ aFont.SetFamilyName(pFontItem->GetFamilyName());
+ aFont.SetStyleName(pFontItem->GetStyleName());
+ aFont.SetFontSize(aPixelFontSize);
+
+ const SfxPoolItem* pItem = pItemSet->GetItem(SID_ATTR_CHAR_WEIGHT);
+ if (pItem)
+ aFont.SetWeight(static_cast<const SvxWeightItem*>(pItem)->GetWeight());
+
+ pItem = pItemSet->GetItem(SID_ATTR_CHAR_POSTURE);
+ if (pItem)
+ aFont.SetItalic(static_cast<const SvxPostureItem*>(pItem)->GetPosture());
+
+ pItem = pItemSet->GetItem(SID_ATTR_CHAR_CONTOUR);
+ if (pItem)
+ aFont.SetOutline(static_cast<const SvxContourItem*>(pItem)->GetValue());
+
+ pItem = pItemSet->GetItem(SID_ATTR_CHAR_SHADOWED);
+ if (pItem)
+ aFont.SetShadow(static_cast<const SvxShadowedItem*>(pItem)->GetValue());
+
+ pItem = pItemSet->GetItem(SID_ATTR_CHAR_RELIEF);
+ if (pItem)
+ aFont.SetRelief(static_cast<const SvxCharReliefItem*>(pItem)->GetValue());
+
+ pItem = pItemSet->GetItem(SID_ATTR_CHAR_UNDERLINE);
+ if (pItem)
+ aFont.SetUnderline(static_cast<const SvxUnderlineItem*>(pItem)->GetLineStyle());
+
+ pItem = pItemSet->GetItem(SID_ATTR_CHAR_OVERLINE);
+ if (pItem)
+ aFont.SetOverline(static_cast<const SvxOverlineItem*>(pItem)->GetValue());
+
+ pItem = pItemSet->GetItem(SID_ATTR_CHAR_STRIKEOUT);
+ if (pItem)
+ aFont.SetStrikeout(static_cast<const SvxCrossedOutItem*>(pItem)->GetStrikeout());
+
+ pItem = pItemSet->GetItem(SID_ATTR_CHAR_CASEMAP);
+ if (pItem)
+ aFont.SetCaseMap(static_cast<const SvxCaseMapItem*>(pItem)->GetCaseMap());
+
+ pItem = pItemSet->GetItem(SID_ATTR_CHAR_EMPHASISMARK);
+ if (pItem)
+ aFont.SetEmphasisMark(static_cast<const SvxEmphasisMarkItem*>(pItem)->GetEmphasisMark());
+
+ return aFont;
+}
+
+void StyleItemController::DrawEntry(vcl::RenderContext& rRenderContext)
+{
+ SfxObjectShell* pShell = SfxObjectShell::Current();
+ if (!pShell)
+ return;
+
+ SfxStyleSheetBasePool* pPool = pShell->GetStyleSheetPool();
+ SfxStyleSheetBase* pStyle = nullptr;
+
+ if (!pPool)
+ return;
+
+ pStyle = pPool->First(m_eStyleFamily);
+ while (pStyle && pStyle->GetName() != m_aStyleName.first
+ && pStyle->GetName() != m_aStyleName.second)
+ pStyle = pPool->Next();
+
+ if (!pStyle)
+ return;
+
+ Size aSize(rRenderContext.GetOutputSizePixel());
+ tools::Rectangle aFullRect(Point(0, 0), aSize);
+ tools::Rectangle aContentRect(aFullRect);
+
+ Color aOriginalColor = rRenderContext.GetFillColor();
+ Color aOriginalLineColor = rRenderContext.GetLineColor();
+
+ DrawContentBackground(rRenderContext, aContentRect, aOriginalColor);
+
+ std::optional<SfxItemSet> const pItemSet(pStyle->GetItemSetForPreview());
+ if (!pItemSet)
+ return;
+
+ Color aFontHighlight = COL_AUTO;
+
+ sal_Int16 nScriptType
+ = MsLangId::getScriptType(Application::GetSettings().GetUILanguageTag().getLanguageType());
+
+ sal_uInt16 nFontSlot = SID_ATTR_CHAR_FONT;
+ if (nScriptType == css::i18n::ScriptType::ASIAN)
+ nFontSlot = SID_ATTR_CHAR_CJK_FONT;
+ else if (nScriptType == css::i18n::ScriptType::COMPLEX)
+ nFontSlot = SID_ATTR_CHAR_CTL_FONT;
+
+ const SvxFontItem* const pFontItem = pItemSet->GetItem<SvxFontItem>(nFontSlot);
+ const SvxFontHeightItem* const pFontHeightItem
+ = pItemSet->GetItem<SvxFontHeightItem>(SID_ATTR_CHAR_FONTHEIGHT);
+
+ if (pFontItem && pFontHeightItem)
+ {
+ Size aFontSize(0, pFontHeightItem->GetHeight());
+ Size aPixelSize(rRenderContext.LogicToPixel(aFontSize, MapMode(pShell->GetMapUnit())));
+
+ SvxFont aFont = GetFontFromItems(pFontItem, aPixelSize, pItemSet);
+ rRenderContext.SetFont(aFont);
+
+ Color aFontCol = GetTextColorFromItemSet(pItemSet);
+ if (aFontCol != COL_AUTO)
+ rRenderContext.SetTextColor(aFontCol);
+
+ aFontHighlight = GetHighlightColorFromItemSet(pItemSet);
+
+ css::drawing::FillStyle style = GetFillStyleFromItemSet(pItemSet);
+
+ switch (style)
+ {
+ case css::drawing::FillStyle_SOLID:
+ {
+ Color aBackCol = GetBackgroundColorFromItemSet(pItemSet);
+ if (aBackCol != COL_AUTO)
+ DrawContentBackground(rRenderContext, aContentRect, aBackCol);
+ }
+ break;
+
+ default:
+ break;
+ //TODO Draw the other background styles: gradient, hatching and bitmap
+ }
+ }
+
+ if (aFontHighlight != COL_AUTO)
+ DrawHighlight(rRenderContext, aFontHighlight);
+
+ DrawText(rRenderContext);
+
+ rRenderContext.SetFillColor(aOriginalColor);
+ rRenderContext.SetLineColor(aOriginalLineColor);
+}
+
+void StyleItemController::DrawContentBackground(vcl::RenderContext& rRenderContext,
+ const tools::Rectangle& aContentRect,
+ const Color& aColor)
+{
+ rRenderContext.SetLineColor(aColor);
+ rRenderContext.SetFillColor(aColor);
+ rRenderContext.DrawRect(aContentRect);
+}
+
+void StyleItemController::DrawHighlight(vcl::RenderContext& rRenderContext, Color aFontBack)
+{
+ tools::Rectangle aTextRect;
+ rRenderContext.GetTextBoundRect(aTextRect, m_aStyleName.second);
+
+ Size aSize = aTextRect.GetSize();
+ aSize.AdjustHeight(aSize.getHeight());
+ aTextRect.SetSize(aSize);
+
+ Point aPos(0, 0);
+ aPos.AdjustX(LEFT_MARGIN);
+ aPos.AdjustY((rRenderContext.GetOutputHeightPixel() - aTextRect.Bottom()) / 2);
+ aTextRect.SetPos(aPos);
+
+ rRenderContext.SetLineColor(aFontBack);
+ rRenderContext.SetFillColor(aFontBack);
+
+ rRenderContext.DrawRect(aTextRect);
+}
+
+void StyleItemController::DrawText(vcl::RenderContext& rRenderContext)
+{
+ const SalLayoutGlyphs* layoutGlyphs
+ = SalLayoutGlyphsCache::self()->GetLayoutGlyphs(&rRenderContext, m_aStyleName.second);
+ tools::Rectangle aTextRect;
+ rRenderContext.GetTextBoundRect(aTextRect, m_aStyleName.second, 0, 0, -1, 0, {}, layoutGlyphs);
+
+ Point aPos(0, 0);
+ aPos.AdjustX(LEFT_MARGIN);
+ aPos.AdjustY((rRenderContext.GetOutputHeightPixel() - aTextRect.Bottom()) / 2);
+
+ rRenderContext.DrawText(aPos, m_aStyleName.second, 0, -1, nullptr, nullptr, layoutGlyphs);
+}
+
+StylesPreviewWindow_Base::StylesPreviewWindow_Base(
+ weld::Builder& xBuilder, std::vector<std::pair<OUString, OUString>>&& aDefaultStyles,
+ const css::uno::Reference<css::frame::XDispatchProvider>& xDispatchProvider)
+ : m_xDispatchProvider(xDispatchProvider)
+ , m_xStylesView(xBuilder.weld_icon_view("stylesview"))
+ , m_aUpdateTask(*this)
+ , m_aDefaultStyles(std::move(aDefaultStyles))
+{
+ m_xStylesView->connect_selection_changed(LINK(this, StylesPreviewWindow_Base, Selected));
+ m_xStylesView->connect_item_activated(LINK(this, StylesPreviewWindow_Base, DoubleClick));
+ m_xStylesView->connect_command(LINK(this, StylesPreviewWindow_Base, DoCommand));
+
+ m_xStatusListener = new StyleStatusListener(this, xDispatchProvider);
+
+ m_pStylePoolChangeListener.reset(new StylePoolChangeListener(this));
+
+ RequestStylesListUpdate();
+}
+
+IMPL_LINK(StylesPreviewWindow_Base, Selected, weld::IconView&, rIconView, void)
+{
+ OUString sStyleName = rIconView.get_selected_text();
+
+ css::uno::Sequence<css::beans::PropertyValue> aArgs{
+ comphelper::makePropertyValue("Template", sStyleName),
+ comphelper::makePropertyValue("Family", sal_Int16(SfxStyleFamily::Para))
+ };
+ SfxToolBoxControl::Dispatch(m_xDispatchProvider, ".uno:StyleApply", aArgs);
+}
+
+IMPL_LINK(StylesPreviewWindow_Base, DoubleClick, weld::IconView&, rIconView, bool)
+{
+ OUString sStyleName = rIconView.get_selected_text();
+
+ css::uno::Sequence<css::beans::PropertyValue> aArgs{
+ comphelper::makePropertyValue("Param", sStyleName),
+ comphelper::makePropertyValue("Family", sal_Int16(SfxStyleFamily::Para))
+ };
+ SfxToolBoxControl::Dispatch(m_xDispatchProvider, ".uno:EditStyle", aArgs);
+
+ return true;
+}
+
+IMPL_LINK(StylesPreviewWindow_Base, DoCommand, const CommandEvent&, rPos, bool)
+{
+ return Command(rPos);
+}
+
+StylesPreviewWindow_Base::~StylesPreviewWindow_Base()
+{
+ m_xStatusListener->UnBind();
+
+ m_aUpdateTask.Stop();
+
+ try
+ {
+ m_xStatusListener->dispose();
+ }
+ catch (css::uno::Exception&)
+ {
+ }
+
+ m_xStatusListener = nullptr;
+}
+
+void StylesPreviewWindow_Base::Select(const OUString& rStyleName)
+{
+ m_sSelectedStyle = rStyleName;
+
+ UpdateSelection();
+}
+
+void StylesPreviewWindow_Base::UpdateSelection()
+{
+ for (std::vector<std::pair<OUString, OUString>>::size_type i = 0; i < m_aAllStyles.size(); ++i)
+ {
+ if (m_aAllStyles[i].first == m_sSelectedStyle || m_aAllStyles[i].second == m_sSelectedStyle)
+ {
+ m_xStylesView->select(i);
+ break;
+ }
+ }
+}
+
+void StylesPreviewWindow_Base::RequestStylesListUpdate() { m_aUpdateTask.Start(); }
+
+void StylesListUpdateTask::Invoke()
+{
+ m_rStylesList.UpdateStylesList();
+ m_rStylesList.UpdateSelection();
+}
+
+void StylesPreviewWindow_Base::UpdateStylesList()
+{
+ m_aAllStyles = m_aDefaultStyles;
+
+ SfxObjectShell* pDocShell = SfxObjectShell::Current();
+ SfxStyleSheetBasePool* pStyleSheetPool = nullptr;
+
+ if (pDocShell)
+ pStyleSheetPool = pDocShell->GetStyleSheetPool();
+
+ if (pStyleSheetPool)
+ {
+ auto xIter = pStyleSheetPool->CreateIterator(SfxStyleFamily::Para,
+ SfxStyleSearchBits::UserDefined);
+
+ SfxStyleSheetBase* pStyle = xIter->First();
+
+ while (pStyle)
+ {
+ m_aAllStyles.push_back(std::pair<OUString, OUString>("", pStyle->GetName()));
+ pStyle = xIter->Next();
+ }
+ }
+
+ m_xStylesView->clear();
+ for (const auto& rStyle : m_aAllStyles)
+ {
+ ScopedVclPtr<VirtualDevice> pImg = VclPtr<VirtualDevice>::Create();
+ const Size aSize(100, 30);
+ pImg->SetOutputSizePixel(aSize);
+
+ StyleItemController aStyleController(rStyle);
+ aStyleController.Paint(*pImg);
+
+ m_xStylesView->append(rStyle.first, rStyle.second, pImg);
+ }
+}
+
+StylesPreviewWindow_Impl::StylesPreviewWindow_Impl(
+ vcl::Window* pParent, std::vector<std::pair<OUString, OUString>>&& aDefaultStyles,
+ const css::uno::Reference<css::frame::XDispatchProvider>& xDispatchProvider)
+ : InterimItemWindow(pParent, "svx/ui/stylespreview.ui", "ApplyStyleBox", true,
+ reinterpret_cast<sal_uInt64>(SfxViewShell::Current()))
+ , StylesPreviewWindow_Base(*m_xBuilder, std::move(aDefaultStyles), xDispatchProvider)
+{
+ SetOptimalSize();
+}
+
+StylesPreviewWindow_Impl::~StylesPreviewWindow_Impl() { disposeOnce(); }
+
+void StylesPreviewWindow_Impl::dispose()
+{
+ m_xStylesView.reset();
+
+ InterimItemWindow::dispose();
+}
+
+void StylesPreviewWindow_Impl::SetOptimalSize() { SetSizePixel(get_preferred_size()); }
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab cinoptions=b1,g0,N-s cinkeys+=0=break: */
diff --git a/svx/source/tbxctrls/SvxColorChildWindow.cxx b/svx/source/tbxctrls/SvxColorChildWindow.cxx
new file mode 100644
index 000000000..c7fb9a683
--- /dev/null
+++ b/svx/source/tbxctrls/SvxColorChildWindow.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 <svx/SvxColorChildWindow.hxx>
+#include <svx/svxids.hrc>
+#include <colrctrl.hxx>
+
+SFX_IMPL_DOCKINGWINDOW_WITHID( SvxColorChildWindow, SID_COLOR_CONTROL )
+
+// Derivation from SfxChildWindow as "container" for animator
+SvxColorChildWindow::SvxColorChildWindow( vcl::Window* _pParent,
+ sal_uInt16 nId,
+ SfxBindings* pBindings,
+ SfxChildWinInfo* pInfo ) :
+ SfxChildWindow( _pParent, nId )
+{
+ VclPtr<SvxColorDockingWindow> pWin = VclPtr<SvxColorDockingWindow>::Create( pBindings, this,
+ _pParent );
+
+ SetWindow(pWin);
+
+ SetAlignment(SfxChildAlignment::RIGHT);
+
+ pWin->Initialize( pInfo );
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/tbxctrls/SvxColorValueSet.cxx b/svx/source/tbxctrls/SvxColorValueSet.cxx
new file mode 100644
index 000000000..4b4547dfc
--- /dev/null
+++ b/svx/source/tbxctrls/SvxColorValueSet.cxx
@@ -0,0 +1,166 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * 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/SvxColorValueSet.hxx>
+#include <svx/xtable.hxx>
+#include <vcl/svapp.hxx>
+#include <vcl/settings.hxx>
+#include <osl/diagnose.h>
+
+#include <svx/uiobject.hxx>
+
+SvxColorValueSet::SvxColorValueSet(std::unique_ptr<weld::ScrolledWindow> pWindow)
+ : ValueSet(std::move(pWindow))
+{
+ SetEdgeBlending(true);
+}
+
+FactoryFunction SvxColorValueSet::GetUITestFactory() const
+{
+ return SvxColorValueSetUIObject::create;
+}
+
+sal_uInt32 SvxColorValueSet::getEntryEdgeLength()
+{
+ const StyleSettings& rStyleSettings = Application::GetSettings().GetStyleSettings();
+
+ return rStyleSettings.GetListBoxPreviewDefaultPixelSize().Height() + 1;
+}
+
+sal_uInt32 SvxColorValueSet::getColumnCount()
+{
+ const StyleSettings& rStyleSettings = Application::GetSettings().GetStyleSettings();
+
+ return rStyleSettings.GetColorValueSetColumnCount();
+}
+
+void SvxColorValueSet::addEntriesForXColorList(const XColorList& rXColorList, sal_uInt32 nStartIndex)
+{
+ const sal_uInt32 nColorCount(rXColorList.Count());
+
+ for(sal_uInt32 nIndex(0); nIndex < nColorCount; nIndex++, nStartIndex++)
+ {
+ const XColorEntry* pEntry = rXColorList.GetColor(nIndex);
+
+ if(pEntry)
+ {
+ InsertItem(nStartIndex, pEntry->GetColor(), pEntry->GetName());
+ }
+ else
+ {
+ OSL_ENSURE(false, "OOps, XColorList with empty entries (!)");
+ }
+ }
+}
+
+void SvxColorValueSet::addEntriesForColorSet(const std::set<Color>& rColorSet, const OUString& rNamePrefix)
+{
+ sal_uInt32 nStartIndex = 1;
+ if(rNamePrefix.getLength() != 0)
+ {
+ for(const auto& rColor : rColorSet)
+ {
+ InsertItem(nStartIndex, rColor, rNamePrefix + OUString::number(nStartIndex));
+ nStartIndex++;
+ }
+ }
+ else
+ {
+ for(const auto& rColor : rColorSet)
+ {
+ InsertItem(nStartIndex, rColor, "");
+ nStartIndex++;
+ }
+ }
+}
+
+Size SvxColorValueSet::layoutAllVisible(sal_uInt32 nEntryCount)
+{
+ if(!nEntryCount)
+ {
+ nEntryCount++;
+ }
+
+ const sal_uInt32 nRowCount(ceil(double(nEntryCount)/SvxColorValueSet::getColumnCount()));
+ const Size aItemSize(SvxColorValueSet::getEntryEdgeLength() - 2, SvxColorValueSet::getEntryEdgeLength() - 2);
+ const WinBits aWinBits(GetStyle() & ~WB_VSCROLL);
+
+ if (nRowCount > SvxColorValueSet::getMaxRowCount())
+ {
+ SetStyle(aWinBits|WB_VSCROLL);
+ }
+ else
+ {
+ SetStyle(aWinBits);
+ }
+
+ SetColCount(SvxColorValueSet::getColumnCount());
+ SetLineCount(std::min(nRowCount, SvxColorValueSet::getMaxRowCount()));
+ SetItemWidth(aItemSize.Width());
+ SetItemHeight(aItemSize.Height());
+
+ return CalcWindowSizePixel(aItemSize);
+}
+
+void SvxColorValueSet::Resize()
+{
+ layoutToGivenHeight(GetOutputSizePixel().Height(), GetItemCount());
+ ValueSet::Resize();
+}
+
+void SvxColorValueSet::layoutToGivenHeight(sal_uInt32 nHeight, sal_uInt32 nEntryCount)
+{
+ if(!nEntryCount)
+ {
+ nEntryCount++;
+ }
+
+ const Size aItemSize(SvxColorValueSet::getEntryEdgeLength() - 2, SvxColorValueSet::getEntryEdgeLength() - 2);
+ const WinBits aWinBits(GetStyle() & ~WB_VSCROLL);
+
+ // get size with all fields disabled
+ const WinBits aWinBitsNoScrollNoFields(GetStyle() & ~(WB_VSCROLL|WB_NAMEFIELD|WB_NONEFIELD));
+ SetStyle(aWinBitsNoScrollNoFields);
+ const Size aSizeNoScrollNoFields(CalcWindowSizePixel(aItemSize, SvxColorValueSet::getColumnCount()));
+
+ // get size with all needed fields
+ SetStyle(aWinBits);
+ Size aNewSize(CalcWindowSizePixel(aItemSize, SvxColorValueSet::getColumnCount()));
+
+ const Size aItemSizePixel(CalcItemSizePixel(aItemSize));
+ // calculate field height and available height for requested height
+ const sal_uInt32 nFieldHeight(aNewSize.Height() - aSizeNoScrollNoFields.Height());
+ const sal_uInt32 nAvailableHeight(nHeight >= nFieldHeight ? nHeight - nFieldHeight + aItemSizePixel.Height() - 1 : 0);
+
+ // calculate how many lines can be shown there
+ const sal_uInt32 nLineCount(nAvailableHeight / aItemSizePixel.Height());
+ const sal_uInt32 nLineMax(ceil(double(nEntryCount)/SvxColorValueSet::getColumnCount()));
+
+ if(nLineMax > nLineCount)
+ {
+ SetStyle(aWinBits|WB_VSCROLL);
+ }
+
+ SetItemWidth(aItemSize.Width());
+ SetItemHeight(aItemSize.Height());
+ SetColCount(SvxColorValueSet::getColumnCount());
+ SetLineCount(nLineCount);
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/tbxctrls/SvxPresetListBox.cxx b/svx/source/tbxctrls/SvxPresetListBox.cxx
new file mode 100644
index 000000000..5cda71a8c
--- /dev/null
+++ b/svx/source/tbxctrls/SvxPresetListBox.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 <svx/SvxPresetListBox.hxx>
+#include <svx/xtable.hxx>
+#include <vcl/commandevent.hxx>
+#include <vcl/image.hxx>
+#include <vcl/svapp.hxx>
+
+SvxPresetListBox::SvxPresetListBox(std::unique_ptr<weld::ScrolledWindow> pWindow)
+ : ValueSet(std::move(pWindow))
+ , aIconSize(60, 64)
+{
+ SetEdgeBlending(true);
+}
+
+void SvxPresetListBox::SetDrawingArea(weld::DrawingArea* pDrawingArea)
+{
+ ValueSet::SetDrawingArea(pDrawingArea);
+ SetStyle(GetStyle() | WB_ITEMBORDER);
+}
+
+void SvxPresetListBox::Resize()
+{
+ DrawLayout();
+ WinBits aWinBits(GetStyle());
+ aWinBits |= WB_VSCROLL;
+ SetStyle(aWinBits);
+ ValueSet::Resize();
+}
+
+bool SvxPresetListBox::Command(const CommandEvent& rEvent)
+{
+ if (rEvent.GetCommand() != CommandEventId::ContextMenu)
+ return CustomWidgetController::Command(rEvent);
+ const sal_uInt16 nIndex = GetSelectedItemId();
+ if (nIndex > 0)
+ {
+ std::unique_ptr<weld::Builder> xBuilder(
+ Application::CreateBuilder(GetDrawingArea(), "svx/ui/presetmenu.ui"));
+ std::unique_ptr<weld::Menu> xMenu(xBuilder->weld_menu("menu"));
+ OnMenuItemSelected(xMenu->popup_at_rect(
+ GetDrawingArea(), tools::Rectangle(rEvent.GetMousePosPixel(), Size(1, 1))));
+ return true;
+ }
+ return false;
+}
+
+void SvxPresetListBox::DrawLayout()
+{
+ SetColCount(nColCount);
+ SetLineCount(5);
+}
+
+template <typename ListType, typename EntryType>
+void SvxPresetListBox::FillPresetListBoxImpl(ListType& pList, sal_uInt32 nStartIndex)
+{
+ const Size aSize(GetIconSize());
+ BitmapEx aBitmap;
+ for (tools::Long nIndex = 0; nIndex < pList.Count(); nIndex++, nStartIndex++)
+ {
+ aBitmap = pList.GetBitmapForPreview(nIndex, aSize);
+ EntryType* pItem = static_cast<EntryType*>(pList.Get(nIndex));
+ InsertItem(nStartIndex, Image(aBitmap), pItem->GetName());
+ }
+}
+
+void SvxPresetListBox::FillPresetListBox(XGradientList& pList, sal_uInt32 nStartIndex)
+{
+ FillPresetListBoxImpl<XGradientList, XGradientEntry>(pList, nStartIndex);
+}
+
+void SvxPresetListBox::FillPresetListBox(XHatchList& pList, sal_uInt32 nStartIndex)
+{
+ FillPresetListBoxImpl<XHatchList, XHatchEntry>(pList, nStartIndex);
+}
+
+void SvxPresetListBox::FillPresetListBox(XBitmapList& pList, sal_uInt32 nStartIndex)
+{
+ FillPresetListBoxImpl<XBitmapList, XBitmapEntry>(pList, nStartIndex);
+}
+
+void SvxPresetListBox::FillPresetListBox(XPatternList& pList, sal_uInt32 nStartIndex)
+{
+ FillPresetListBoxImpl<XPatternList, XBitmapEntry>(pList, nStartIndex);
+}
+
+void SvxPresetListBox::OnMenuItemSelected(std::string_view rIdent)
+{
+ if (rIdent == "rename")
+ maRenameHdl.Call(this);
+ else if (rIdent == "delete")
+ maDeleteHdl.Call(this);
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/tbxctrls/bulletsnumbering.cxx b/svx/source/tbxctrls/bulletsnumbering.cxx
new file mode 100644
index 000000000..8bf832761
--- /dev/null
+++ b/svx/source/tbxctrls/bulletsnumbering.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/.
+ */
+
+#include <com/sun/star/text/DefaultNumberingProvider.hpp>
+#include <com/sun/star/text/XNumberingFormatter.hpp>
+
+#include <comphelper/propertysequence.hxx>
+#include <i18nlangtag/languagetag.hxx>
+#include <svtools/popupwindowcontroller.hxx>
+#include <svtools/toolbarmenu.hxx>
+#include <svx/strings.hrc>
+#include <svx/dialmgr.hxx>
+#include <svx/numvset.hxx>
+#include <vcl/commandinfoprovider.hxx>
+#include <vcl/toolbox.hxx>
+#include <vcl/settings.hxx>
+#include <vcl/svapp.hxx>
+
+namespace {
+
+class NumberingToolBoxControl;
+
+class NumberingPopup : public WeldToolbarPopup
+{
+ NumberingPageType mePageType;
+ NumberingToolBoxControl& mrController;
+ std::unique_ptr<SvxNumValueSet> mxValueSet;
+ std::unique_ptr<weld::CustomWeld> mxValueSetWin;
+ std::unique_ptr<weld::Button> mxMoreButton;
+ DECL_LINK(VSSelectValueSetHdl, ValueSet*, void);
+ DECL_LINK(VSButtonClickSetHdl, weld::Button&, void);
+
+ virtual void GrabFocus() override;
+
+public:
+ NumberingPopup(NumberingToolBoxControl& rController, weld::Widget* pParent, NumberingPageType ePageType);
+
+ virtual void statusChanged( const css::frame::FeatureStateEvent& rEvent ) override;
+};
+
+class NumberingToolBoxControl : public svt::PopupWindowController
+{
+ NumberingPageType mePageType;
+
+public:
+ explicit NumberingToolBoxControl( const css::uno::Reference< css::uno::XComponentContext >& rxContext );
+ virtual VclPtr<vcl::Window> createVclPopupWindow( vcl::Window* pParent ) override;
+ std::unique_ptr<WeldToolbarPopup> weldPopupWindow() override;
+
+ // XInitialization
+ virtual void SAL_CALL initialize( const css::uno::Sequence< css::uno::Any >& aArguments ) override;
+
+ // XServiceInfo
+ virtual OUString SAL_CALL getImplementationName() override;
+ virtual css::uno::Sequence< OUString > SAL_CALL getSupportedServiceNames() override;
+};
+
+}
+
+NumberingPopup::NumberingPopup(NumberingToolBoxControl& rController,
+ weld::Widget* pParent, NumberingPageType ePageType)
+ : WeldToolbarPopup(rController.getFrameInterface(), pParent, "svx/ui/numberingwindow.ui", "NumberingWindow")
+ , mePageType(ePageType)
+ , mrController(rController)
+ , mxValueSet(new SvxNumValueSet(m_xBuilder->weld_scrolled_window("valuesetwin", true)))
+ , mxValueSetWin(new weld::CustomWeld(*m_xBuilder, "valueset", *mxValueSet))
+ , mxMoreButton(m_xBuilder->weld_button("more"))
+{
+ mxValueSet->SetStyle(WB_MENUSTYLEVALUESET | WB_FLATVALUESET | WB_NO_DIRECTSELECT);
+ mxValueSet->init(mePageType);
+
+ if ( mePageType != NumberingPageType::BULLET )
+ {
+ css::uno::Reference< css::text::XDefaultNumberingProvider > xDefNum = css::text::DefaultNumberingProvider::create( mrController.getContext() );
+ if ( xDefNum.is() )
+ {
+ css::lang::Locale aLocale = Application::GetSettings().GetLanguageTag().getLocale();
+ css::uno::Reference< css::text::XNumberingFormatter > xFormat( xDefNum, css::uno::UNO_QUERY );
+
+ if ( mePageType == NumberingPageType::SINGLENUM )
+ {
+ css::uno::Sequence< css::uno::Sequence< css::beans::PropertyValue > > aNumberings(
+ xDefNum->getDefaultContinuousNumberingLevels( aLocale ) );
+ mxValueSet->SetNumberingSettings( aNumberings, xFormat, aLocale );
+ }
+ else if ( mePageType == NumberingPageType::OUTLINE )
+ {
+ css::uno::Sequence< css::uno::Reference< css::container::XIndexAccess > > aOutline(
+ xDefNum->getDefaultOutlineNumberings( aLocale ) );
+ mxValueSet->SetOutlineNumberingSettings( aOutline, xFormat, aLocale );
+ }
+ }
+ }
+
+ weld::DrawingArea* pDrawingArea = mxValueSet->GetDrawingArea();
+ OutputDevice& rRefDevice = pDrawingArea->get_ref_device();
+ Size aItemSize(rRefDevice.LogicToPixel(Size(30, 42), MapMode(MapUnit::MapAppFont)));
+ mxValueSet->SetExtraSpacing( 2 );
+ Size aSize(mxValueSet->CalcWindowSizePixel(aItemSize));
+ pDrawingArea->set_size_request(aSize.Width(), aSize.Height());
+ mxValueSet->SetOutputSizePixel(aSize);
+ mxValueSet->SetColor(Application::GetSettings().GetStyleSettings().GetFieldColor());
+
+ OUString aMoreItemText;
+ if ( mePageType == NumberingPageType::BULLET )
+ {
+ aMoreItemText = SvxResId( RID_SVXSTR_MOREBULLETS );
+ AddStatusListener( ".uno:CurrentBulletListType" );
+ }
+ else if ( mePageType == NumberingPageType::SINGLENUM )
+ {
+ aMoreItemText = SvxResId( RID_SVXSTR_MORENUMBERING );
+ AddStatusListener( ".uno:CurrentNumListType" );
+ }
+ else
+ {
+ aMoreItemText = SvxResId( RID_SVXSTR_MORE );
+ AddStatusListener( ".uno:CurrentOutlineType" );
+ }
+
+ auto xImage = vcl::CommandInfoProvider::GetXGraphicForCommand(".uno:OutlineBullet", mrController.getFrameInterface());
+ mxMoreButton->set_image(xImage);
+ mxMoreButton->set_label(aMoreItemText);
+ mxMoreButton->connect_clicked(LINK(this, NumberingPopup, VSButtonClickSetHdl));
+
+ mxValueSet->SetSelectHdl(LINK(this, NumberingPopup, VSSelectValueSetHdl));
+}
+
+void NumberingPopup::statusChanged( const css::frame::FeatureStateEvent& rEvent )
+{
+ mxValueSet->SetNoSelection();
+
+ sal_Int32 nSelItem;
+ if ( rEvent.State >>= nSelItem )
+ mxValueSet->SelectItem( nSelItem );
+}
+
+IMPL_LINK_NOARG(NumberingPopup, VSSelectValueSetHdl, ValueSet*, void)
+{
+ sal_uInt16 nSelItem = mxValueSet->GetSelectedItemId();
+ if ( mePageType == NumberingPageType::BULLET )
+ {
+ auto aArgs( comphelper::InitPropertySequence( { { "SetBullet", css::uno::Any( nSelItem ) } } ) );
+ mrController.dispatchCommand( ".uno:SetBullet", aArgs );
+ }
+ else if ( mePageType == NumberingPageType::SINGLENUM )
+ {
+ auto aArgs( comphelper::InitPropertySequence( { { "SetNumber", css::uno::Any( nSelItem ) } } ) );
+ mrController.dispatchCommand( ".uno:SetNumber", aArgs );
+ }
+ else
+ {
+ auto aArgs( comphelper::InitPropertySequence( { { "SetOutline", css::uno::Any( nSelItem ) } } ) );
+ mrController.dispatchCommand( ".uno:SetOutline", aArgs );
+ }
+ mrController.EndPopupMode();
+}
+
+void NumberingPopup::GrabFocus()
+{
+ mxValueSet->GrabFocus();
+}
+
+IMPL_LINK_NOARG(NumberingPopup, VSButtonClickSetHdl, weld::Button&, void)
+{
+ auto aArgs( comphelper::InitPropertySequence( { { "Page", css::uno::Any( OUString("customize") ) } } ) );
+ mrController.dispatchCommand( ".uno:OutlineBullet", aArgs );
+
+ mrController.EndPopupMode();
+}
+
+NumberingToolBoxControl::NumberingToolBoxControl( const css::uno::Reference< css::uno::XComponentContext >& rxContext ):
+ svt::PopupWindowController( rxContext, css::uno::Reference< css::frame::XFrame >(), OUString() ),
+ mePageType( NumberingPageType::SINGLENUM )
+{
+}
+
+std::unique_ptr<WeldToolbarPopup> NumberingToolBoxControl::weldPopupWindow()
+{
+ return std::make_unique<NumberingPopup>(*this, m_pToolbar, mePageType);
+}
+
+VclPtr<vcl::Window> NumberingToolBoxControl::createVclPopupWindow( vcl::Window* pParent )
+{
+ mxInterimPopover = VclPtr<InterimToolbarPopup>::Create(getFrameInterface(), pParent,
+ std::make_unique<NumberingPopup>(*this, pParent->GetFrameWeld(), mePageType));
+
+ mxInterimPopover->Show();
+
+ return mxInterimPopover;
+}
+
+void SAL_CALL NumberingToolBoxControl::initialize( const css::uno::Sequence< css::uno::Any >& aArguments )
+{
+ svt::PopupWindowController::initialize( aArguments );
+
+ if ( m_aCommandURL == ".uno:DefaultBullet" )
+ mePageType = NumberingPageType::BULLET;
+ else if ( m_aCommandURL == ".uno:SetOutline" )
+ mePageType = NumberingPageType::OUTLINE;
+
+ if (m_pToolbar)
+ {
+ mxPopoverContainer.reset(new ToolbarPopupContainer(m_pToolbar));
+ m_pToolbar->set_item_popover(m_aCommandURL.toUtf8(), mxPopoverContainer->getTopLevel());
+ return;
+ }
+
+ ToolBox* pToolBox = nullptr;
+ ToolBoxItemId nId;
+ if (getToolboxId(nId, &pToolBox))
+ {
+ ToolBoxItemBits nBits = ( mePageType == NumberingPageType::OUTLINE ) ? ToolBoxItemBits::DROPDOWNONLY : ToolBoxItemBits::DROPDOWN;
+ pToolBox->SetItemBits( nId, pToolBox->GetItemBits( nId ) | nBits );
+ }
+}
+
+OUString SAL_CALL NumberingToolBoxControl::getImplementationName()
+{
+ return "com.sun.star.comp.svx.NumberingToolBoxControl";
+}
+
+css::uno::Sequence< OUString > SAL_CALL NumberingToolBoxControl::getSupportedServiceNames()
+{
+ return { "com.sun.star.frame.ToolbarController" };
+}
+
+extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface *
+com_sun_star_comp_svx_NumberingToolBoxControl_get_implementation(
+ css::uno::XComponentContext *rxContext,
+ css::uno::Sequence<css::uno::Any> const & )
+{
+ return cppu::acquire( new NumberingToolBoxControl( rxContext ) );
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/tbxctrls/colrctrl.cxx b/svx/source/tbxctrls/colrctrl.cxx
new file mode 100644
index 000000000..d3bc9ae24
--- /dev/null
+++ b/svx/source/tbxctrls/colrctrl.cxx
@@ -0,0 +1,423 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * 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 <sot/exchange.hxx>
+#include <svx/strings.hrc>
+#include <svx/svxids.hrc>
+
+#include <sfx2/viewsh.hxx>
+#include <sfx2/objsh.hxx>
+#include <sfx2/dispatch.hxx>
+#include <sfx2/viewfrm.hxx>
+#include <vcl/image.hxx>
+#include <vcl/transfer.hxx>
+
+#include <colrctrl.hxx>
+
+#include <svx/svdview.hxx>
+#include <svx/drawitem.hxx>
+#include <svx/xfillit0.hxx>
+#include <svx/xflclit.hxx>
+#include <editeng/colritem.hxx>
+#include <svx/xlineit0.hxx>
+#include <svx/xlnclit.hxx>
+#include <svx/xtable.hxx>
+#include <svx/dialmgr.hxx>
+#include <helpids.h>
+#include <vcl/virdev.hxx>
+
+#include <com/sun/star/beans/NamedValue.hpp>
+
+using namespace com::sun::star;
+
+class SvxColorValueSetData final : public TransferDataContainer
+{
+private:
+ uno::Sequence<beans::NamedValue> m_Data;
+
+ virtual void AddSupportedFormats() override;
+ virtual bool GetData(const css::datatransfer::DataFlavor& rFlavor, const OUString& rDestDoc) override;
+
+public:
+ SvxColorValueSetData()
+ {
+ }
+
+ void SetData(const uno::Sequence<beans::NamedValue>& rData)
+ {
+ m_Data = rData;
+ ClearFormats(); // invalidate m_aAny so new data will take effect
+ }
+};
+
+void SvxColorValueSetData::AddSupportedFormats()
+{
+ AddFormat( SotClipboardFormatId::XFA );
+}
+
+bool SvxColorValueSetData::GetData( const css::datatransfer::DataFlavor& rFlavor, const OUString& /*rDestDoc*/ )
+{
+ bool bRet = false;
+
+ if( SotExchange::GetFormat( rFlavor ) == SotClipboardFormatId::XFA )
+ {
+ SetAny(uno::Any(m_Data));
+ bRet = true;
+ }
+
+ return bRet;
+}
+
+void SvxColorValueSet_docking::SetDrawingArea(weld::DrawingArea* pDrawingArea)
+{
+ SvxColorValueSet::SetDrawingArea(pDrawingArea);
+ SetAccessibleName(SvxResId(STR_COLORTABLE));
+ SetStyle(GetStyle() | WB_ITEMBORDER);
+
+ m_xHelper.set(new SvxColorValueSetData);
+ rtl::Reference<TransferDataContainer> xHelper(m_xHelper);
+ SetDragDataTransferrable(xHelper, DND_ACTION_COPY);
+}
+
+SvxColorValueSet_docking::SvxColorValueSet_docking(std::unique_ptr<weld::ScrolledWindow> xWindow)
+ : SvxColorValueSet(std::move(xWindow))
+ , mbLeftButton(true)
+{
+}
+
+bool SvxColorValueSet_docking::MouseButtonDown( const MouseEvent& rMEvt )
+{
+ bool bRet;
+
+ // For Mac still handle differently!
+ if( rMEvt.IsLeft() )
+ {
+ mbLeftButton = true;
+ bRet = SvxColorValueSet::MouseButtonDown( rMEvt );
+ }
+ else
+ {
+ mbLeftButton = false;
+ MouseEvent aMEvt( rMEvt.GetPosPixel(),
+ rMEvt.GetClicks(),
+ rMEvt.GetMode(),
+ MOUSE_LEFT,
+ rMEvt.GetModifier() );
+ bRet = SvxColorValueSet::MouseButtonDown( aMEvt );
+ }
+
+ return bRet;
+}
+
+bool SvxColorValueSet_docking::MouseButtonUp( const MouseEvent& rMEvt )
+{
+ bool bRet;
+
+ // For Mac still handle differently!
+ if( rMEvt.IsLeft() )
+ {
+ mbLeftButton = true;
+ bRet = SvxColorValueSet::MouseButtonUp( rMEvt );
+ }
+ else
+ {
+ mbLeftButton = false;
+ MouseEvent aMEvt( rMEvt.GetPosPixel(),
+ rMEvt.GetClicks(),
+ rMEvt.GetMode(),
+ MOUSE_LEFT,
+ rMEvt.GetModifier() );
+ bRet = SvxColorValueSet::MouseButtonUp( aMEvt );
+ }
+ SetNoSelection();
+
+ return bRet;
+}
+
+bool SvxColorValueSet_docking::StartDrag()
+{
+ sal_uInt16 nPos = GetSelectedItemId();
+ Color aItemColor( GetItemColor( nPos ) );
+ OUString sItemText( GetItemText( nPos ) );
+
+ drawing::FillStyle eStyle = ((1 == nPos)
+ ? drawing::FillStyle_NONE
+ : drawing::FillStyle_SOLID);
+
+ XFillColorItem const color(sItemText, aItemColor);
+ XFillStyleItem const style(eStyle);
+ uno::Any c, s;
+ color.QueryValue(c, 0);
+ style.QueryValue(s, 0);
+
+ uno::Sequence<beans::NamedValue> props{ { "FillColor", std::move(c) },
+ { "FillStyle", std::move(s) } };
+ m_xHelper->SetData(props);
+
+ return false;
+}
+
+constexpr sal_uInt16 gnLeftSlot = SID_ATTR_FILL_COLOR;
+constexpr sal_uInt16 gnRightSlot = SID_ATTR_LINE_COLOR;
+
+SvxColorDockingWindow::SvxColorDockingWindow(SfxBindings* _pBindings, SfxChildWindow* pCW, vcl::Window* _pParent)
+ : SfxDockingWindow(_pBindings, pCW, _pParent,
+ "DockingColorWindow", "svx/ui/dockingcolorwindow.ui")
+ , xColorSet(new SvxColorValueSet_docking(m_xBuilder->weld_scrolled_window("valuesetwin", true)))
+ , xColorSetWin(new weld::CustomWeld(*m_xBuilder, "valueset", *xColorSet))
+{
+ SetText(SvxResId(STR_COLORTABLE));
+ SetQuickHelpText(SvxResId(RID_SVXSTR_COLORBAR));
+ SetSizePixel(LogicToPixel(Size(150, 22), MapMode(MapUnit::MapAppFont)));
+ SetHelpId(HID_CTRL_COLOR);
+
+ xColorSet->SetSelectHdl( LINK( this, SvxColorDockingWindow, SelectHdl ) );
+ xColorSet->SetHelpId(HID_COLOR_CTL_COLORS);
+
+ // Get the model from the view shell. Using SfxObjectShell::Current()
+ // is unreliable when called at the wrong times.
+ SfxObjectShell* pDocSh = nullptr;
+ if (_pBindings != nullptr)
+ {
+ SfxDispatcher* pDispatcher = _pBindings->GetDispatcher();
+ if (pDispatcher != nullptr)
+ {
+ SfxViewFrame* pFrame = pDispatcher->GetFrame();
+ if (pFrame != nullptr)
+ {
+ SfxViewShell* pViewShell = pFrame->GetViewShell();
+ if (pViewShell != nullptr)
+ pDocSh = pViewShell->GetObjectShell();
+ }
+ }
+ }
+
+ if ( pDocSh )
+ {
+ const SfxPoolItem* pItem = pDocSh->GetItem( SID_COLOR_TABLE );
+ if( pItem )
+ {
+ pColorList = static_cast<const SvxColorListItem*>(pItem)->GetColorList();
+ FillValueSet();
+ }
+ }
+
+ Size aItemSize = xColorSet->CalcItemSizePixel(Size(SvxColorValueSet::getEntryEdgeLength(), SvxColorValueSet::getEntryEdgeLength()));
+ aItemSize.setWidth( aItemSize.Width() + SvxColorValueSet::getEntryEdgeLength() );
+ aItemSize.setWidth( aItemSize.Width() / 2 );
+ aItemSize.setHeight( aItemSize.Height() + SvxColorValueSet::getEntryEdgeLength() );
+ aItemSize.setHeight( aItemSize.Height() / 2 );
+
+ if (_pBindings != nullptr)
+ StartListening(*_pBindings, DuplicateHandling::Prevent);
+}
+
+SvxColorDockingWindow::~SvxColorDockingWindow()
+{
+ disposeOnce();
+}
+
+void SvxColorDockingWindow::dispose()
+{
+ EndListening( GetBindings() );
+ xColorSetWin.reset();
+ xColorSet.reset();
+ SfxDockingWindow::dispose();
+}
+
+void SvxColorDockingWindow::Notify( SfxBroadcaster& , const SfxHint& rHint )
+{
+ const SfxPoolItemHint* pPoolItemHint = dynamic_cast<const SfxPoolItemHint*>(&rHint);
+ if ( pPoolItemHint )
+ if (auto pColorListItem = dynamic_cast<const SvxColorListItem*>(pPoolItemHint->GetObject()))
+ {
+ // The list of colors has changed
+ pColorList = pColorListItem->GetColorList();
+ FillValueSet();
+ }
+}
+
+void SvxColorDockingWindow::FillValueSet()
+{
+ if( !pColorList.is() )
+ return;
+
+ xColorSet->Clear();
+
+ xColorSet->addEntriesForXColorList(*pColorList, 2);
+
+ // create the last entry for 'invisible/none'
+ const Size aColorSize(SvxColorValueSet::getEntryEdgeLength(), SvxColorValueSet::getEntryEdgeLength());
+ tools::Long nPtX = aColorSize.Width() - 1;
+ tools::Long nPtY = aColorSize.Height() - 1;
+ ScopedVclPtrInstance< VirtualDevice > pVD;
+
+ pVD->SetOutputSizePixel( aColorSize );
+ pVD->SetLineColor( COL_BLACK );
+ pVD->SetBackground( Wallpaper( COL_WHITE ) );
+ pVD->DrawLine( Point(), Point( nPtX, nPtY ) );
+ pVD->DrawLine( Point( 0, nPtY ), Point( nPtX, 0 ) );
+
+ BitmapEx aBmp( pVD->GetBitmapEx( Point(), aColorSize ) );
+
+ xColorSet->InsertItem( sal_uInt16(1), Image(aBmp), SvxResId( RID_SVXSTR_INVISIBLE ) );
+}
+
+bool SvxColorDockingWindow::Close()
+{
+ SfxBoolItem aItem( SID_COLOR_CONTROL, false );
+ GetBindings().GetDispatcher()->ExecuteList(SID_COLOR_CONTROL,
+ SfxCallMode::ASYNCHRON | SfxCallMode::RECORD, { &aItem });
+ SfxDockingWindow::Close();
+ return true;
+}
+
+IMPL_LINK_NOARG(SvxColorDockingWindow, SelectHdl, ValueSet*, void)
+{
+ SfxDispatcher* pDispatcher = GetBindings().GetDispatcher();
+ sal_uInt16 nPos = xColorSet->GetSelectedItemId();
+ Color aColor( xColorSet->GetItemColor( nPos ) );
+ OUString aStr( xColorSet->GetItemText( nPos ) );
+
+ if (xColorSet->IsLeftButton())
+ {
+ if ( gnLeftSlot == SID_ATTR_FILL_COLOR )
+ {
+ if ( nPos == 1 ) // invisible
+ {
+ XFillStyleItem aXFillStyleItem( drawing::FillStyle_NONE );
+ pDispatcher->ExecuteList(gnLeftSlot, SfxCallMode::RECORD,
+ { &aXFillStyleItem });
+ }
+ else
+ {
+ bool bDone = false;
+
+ // If we have a DrawView and we are in TextEdit mode, then
+ // not the area color but the text color is assigned
+ SfxViewShell* pViewSh = SfxViewShell::Current();
+ if ( pViewSh )
+ {
+ SdrView* pView = pViewSh->GetDrawView();
+ if ( pView && pView->IsTextEdit() )
+ {
+ SvxColorItem aTextColorItem( aColor, SID_ATTR_CHAR_COLOR );
+ pDispatcher->ExecuteList(SID_ATTR_CHAR_COLOR,
+ SfxCallMode::RECORD, { &aTextColorItem });
+ bDone = true;
+ }
+ }
+ if ( !bDone )
+ {
+ XFillStyleItem aXFillStyleItem( drawing::FillStyle_SOLID );
+ XFillColorItem aXFillColorItem( aStr, aColor );
+ pDispatcher->ExecuteList(gnLeftSlot, SfxCallMode::RECORD,
+ { &aXFillColorItem, &aXFillStyleItem });
+ }
+ }
+ }
+ else if ( nPos != 1 ) // invisible
+ {
+ SvxColorItem aLeftColorItem( aColor, gnLeftSlot );
+ pDispatcher->ExecuteList(gnLeftSlot, SfxCallMode::RECORD,
+ { &aLeftColorItem });
+ }
+ }
+ else
+ {
+ if ( gnRightSlot == SID_ATTR_LINE_COLOR )
+ {
+ if( nPos == 1 ) // invisible
+ {
+ XLineStyleItem aXLineStyleItem( drawing::LineStyle_NONE );
+ pDispatcher->ExecuteList(gnRightSlot, SfxCallMode::RECORD,
+ { &aXLineStyleItem });
+ }
+ else
+ {
+ // If the LineStyle is invisible, it is set to SOLID
+ SfxViewShell* pViewSh = SfxViewShell::Current();
+ if ( pViewSh )
+ {
+ SdrView* pView = pViewSh->GetDrawView();
+ if ( pView )
+ {
+ SfxItemSet aAttrSet( pView->GetModel()->GetItemPool() );
+ pView->GetAttributes( aAttrSet );
+ if ( aAttrSet.GetItemState( XATTR_LINESTYLE ) != SfxItemState::DONTCARE )
+ {
+ drawing::LineStyle eXLS =
+ aAttrSet.Get( XATTR_LINESTYLE ).GetValue();
+ if ( eXLS == drawing::LineStyle_NONE )
+ {
+ XLineStyleItem aXLineStyleItem( drawing::LineStyle_SOLID );
+ pDispatcher->ExecuteList(gnRightSlot,
+ SfxCallMode::RECORD, { &aXLineStyleItem });
+ }
+ }
+ }
+ }
+
+ XLineColorItem aXLineColorItem( aStr, aColor );
+ pDispatcher->ExecuteList(gnRightSlot, SfxCallMode::RECORD,
+ { &aXLineColorItem });
+ }
+ }
+ else if ( nPos != 1 ) // invisible
+ {
+ SvxColorItem aRightColorItem( aColor, gnRightSlot );
+ pDispatcher->ExecuteList(gnRightSlot, SfxCallMode::RECORD,
+ { &aRightColorItem });
+ }
+ }
+}
+
+void SvxColorDockingWindow::GetFocus()
+{
+ SfxDockingWindow::GetFocus();
+ if (xColorSet)
+ {
+ // Grab the focus to the color value set so that it can be controlled
+ // with the keyboard.
+ xColorSet->GrabFocus();
+ }
+}
+
+bool SvxColorDockingWindow::EventNotify( NotifyEvent& rNEvt )
+{
+ bool bRet = false;
+ if( rNEvt.GetType() == MouseNotifyEvent::KEYINPUT )
+ {
+ KeyEvent aKeyEvt = *rNEvt.GetKeyEvent();
+ sal_uInt16 nKeyCode = aKeyEvt.GetKeyCode().GetCode();
+ switch( nKeyCode )
+ {
+ case KEY_ESCAPE:
+ GrabFocusToDocument();
+ bRet = true;
+ break;
+ }
+ }
+
+ return bRet || SfxDockingWindow::EventNotify(rNEvt);
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/tbxctrls/extrusioncontrols.cxx b/svx/source/tbxctrls/extrusioncontrols.cxx
new file mode 100644
index 000000000..9affab6d7
--- /dev/null
+++ b/svx/source/tbxctrls/extrusioncontrols.cxx
@@ -0,0 +1,968 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * 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/propertyvalue.hxx>
+#include <svtools/toolbarmenu.hxx>
+#include <vcl/toolbox.hxx>
+
+#include <svx/strings.hrc>
+#include <svx/svdtrans.hxx>
+#include <svx/dialmgr.hxx>
+
+#include <helpids.h>
+#include "extrusioncontrols.hxx"
+#include <extrusiondepthdialog.hxx>
+
+#include <bitmaps.hlst>
+
+using namespace ::com::sun::star::uno;
+using namespace ::com::sun::star::lang;
+using namespace ::com::sun::star::beans;
+using namespace ::com::sun::star::util;
+using namespace ::com::sun::star::graphic;
+
+namespace svx
+{
+
+const sal_Int32 gSkewList[] = { 135, 90, 45, 180, 0, -360, -135, -90, -45 };
+constexpr OUStringLiteral g_sExtrusionDirection = u".uno:ExtrusionDirection";
+constexpr OUStringLiteral g_sExtrusionProjection = u".uno:ExtrusionProjection";
+constexpr OUStringLiteral EMPTY = u"";
+
+constexpr rtl::OUStringConstExpr aLightOffBmps[] =
+{
+ RID_SVXBMP_LIGHT_OFF_FROM_TOP_LEFT,
+ RID_SVXBMP_LIGHT_OFF_FROM_TOP,
+ RID_SVXBMP_LIGHT_OFF_FROM_TOP_RIGHT,
+ RID_SVXBMP_LIGHT_OFF_FROM_LEFT,
+ EMPTY,
+ RID_SVXBMP_LIGHT_OFF_FROM_RIGHT,
+ RID_SVXBMP_LIGHT_OFF_FROM_BOTTOM_LEFT,
+ RID_SVXBMP_LIGHT_OFF_FROM_BOTTOM,
+ RID_SVXBMP_LIGHT_OFF_FROM_BOTTOM_RIGHT
+};
+
+constexpr rtl::OUStringConstExpr aLightOnBmps[] =
+{
+ RID_SVXBMP_LIGHT_ON_FROM_TOP_LEFT,
+ RID_SVXBMP_LIGHT_ON_FROM_TOP,
+ RID_SVXBMP_LIGHT_ON_FROM_TOP_RIGHT,
+ RID_SVXBMP_LIGHT_ON_FROM_LEFT,
+ EMPTY,
+ RID_SVXBMP_LIGHT_ON_FROM_RIGHT,
+ RID_SVXBMP_LIGHT_ON_FROM_BOTTOM_LEFT,
+ RID_SVXBMP_LIGHT_ON_FROM_BOTTOM,
+ RID_SVXBMP_LIGHT_ON_FROM_BOTTOM_RIGHT
+};
+
+constexpr rtl::OUStringConstExpr aLightPreviewBmps[] =
+{
+ RID_SVXBMP_LIGHT_PREVIEW_FROM_TOP_LEFT,
+ RID_SVXBMP_LIGHT_PREVIEW_FROM_TOP,
+ RID_SVXBMP_LIGHT_PREVIEW_FROM_TOP_RIGHT,
+ RID_SVXBMP_LIGHT_PREVIEW_FROM_LEFT,
+ RID_SVXBMP_LIGHT_PREVIEW_FROM_FRONT,
+ RID_SVXBMP_LIGHT_PREVIEW_FROM_RIGHT,
+ RID_SVXBMP_LIGHT_PREVIEW_FROM_BOTTOM_LEFT,
+ RID_SVXBMP_LIGHT_PREVIEW_FROM_BOTTOM,
+ RID_SVXBMP_LIGHT_PREVIEW_FROM_BOTTOM_RIGHT
+};
+
+constexpr rtl::OUStringConstExpr aDirectionBmps[] =
+{
+ RID_SVXBMP_DIRECTION_DIRECTION_NW,
+ RID_SVXBMP_DIRECTION_DIRECTION_N,
+ RID_SVXBMP_DIRECTION_DIRECTION_NE,
+ RID_SVXBMP_DIRECTION_DIRECTION_W,
+ RID_SVXBMP_DIRECTION_DIRECTION_NONE,
+ RID_SVXBMP_DIRECTION_DIRECTION_E,
+ RID_SVXBMP_DIRECTION_DIRECTION_SW,
+ RID_SVXBMP_DIRECTION_DIRECTION_S,
+ RID_SVXBMP_DIRECTION_DIRECTION_SE
+};
+
+static TranslateId aDirectionStrs[] =
+{
+ RID_SVXSTR_DIRECTION_NW,
+ RID_SVXSTR_DIRECTION_N,
+ RID_SVXSTR_DIRECTION_NE,
+ RID_SVXSTR_DIRECTION_W,
+ RID_SVXSTR_DIRECTION_NONE,
+ RID_SVXSTR_DIRECTION_E,
+ RID_SVXSTR_DIRECTION_SW,
+ RID_SVXSTR_DIRECTION_S,
+ RID_SVXSTR_DIRECTION_SE
+};
+
+ExtrusionDirectionWindow::ExtrusionDirectionWindow(
+ svt::PopupWindowController* pControl,
+ weld::Widget* pParent)
+ : WeldToolbarPopup(pControl->getFrameInterface(), pParent, "svx/ui/directionwindow.ui", "DirectionWindow")
+ , mxControl(pControl)
+ , mxDirectionSet(new ValueSet(nullptr))
+ , mxDirectionSetWin(new weld::CustomWeld(*m_xBuilder, "valueset", *mxDirectionSet))
+ , mxPerspective(m_xBuilder->weld_radio_button("perspective"))
+ , mxParallel(m_xBuilder->weld_radio_button("parallel"))
+{
+ mxDirectionSet->SetStyle(WB_TABSTOP | WB_MENUSTYLEVALUESET | WB_FLATVALUESET | WB_NOBORDER | WB_NO_DIRECTSELECT);
+
+ for (sal_uInt16 i = DIRECTION_NW; i <= DIRECTION_SE; ++i)
+ {
+ maImgDirection[i] = Image(StockImage::Yes, OUString(aDirectionBmps[i]));
+ }
+
+ mxDirectionSet->SetSelectHdl( LINK( this, ExtrusionDirectionWindow, SelectValueSetHdl ) );
+ mxDirectionSet->SetColCount( 3 );
+ mxDirectionSet->EnableFullItemMode( false );
+
+ for (sal_uInt16 i = DIRECTION_NW; i <= DIRECTION_SE; ++i)
+ {
+ mxDirectionSet->InsertItem(i + 1, maImgDirection[i], SvxResId(aDirectionStrs[i]));
+ }
+
+ Size aSize(72, 72);
+ mxDirectionSet->GetDrawingArea()->set_size_request(aSize.Width(), aSize.Height());
+ mxDirectionSet->SetOutputSizePixel(aSize);
+
+ mxPerspective->connect_toggled(LINK(this, ExtrusionDirectionWindow, SelectToolbarMenuHdl));
+
+ AddStatusListener( g_sExtrusionDirection );
+ AddStatusListener( g_sExtrusionProjection );
+}
+
+void ExtrusionDirectionWindow::GrabFocus()
+{
+ mxDirectionSet->GrabFocus();
+}
+
+ExtrusionDirectionWindow::~ExtrusionDirectionWindow()
+{
+}
+
+void ExtrusionDirectionWindow::implSetDirection( sal_Int32 nSkew, bool bEnabled )
+{
+ sal_uInt16 nItemId;
+ for( nItemId = DIRECTION_NW; nItemId <= DIRECTION_SE; nItemId++ )
+ {
+ if( gSkewList[nItemId] == nSkew )
+ break;
+ }
+
+ if( nItemId <= DIRECTION_SE )
+ {
+ mxDirectionSet->SelectItem( nItemId+1 );
+ }
+ else
+ {
+ mxDirectionSet->SetNoSelection();
+ }
+
+ if (bEnabled)
+ mxDirectionSet->Enable();
+ else
+ mxDirectionSet->Disable();
+}
+
+void ExtrusionDirectionWindow::implSetProjection( sal_Int32 nProjection, bool bEnabled )
+{
+ mxPerspective->set_active(nProjection == 0 && bEnabled);
+ mxParallel->set_active(nProjection == 1 && bEnabled);
+ mxPerspective->set_sensitive(bEnabled);
+ mxParallel->set_sensitive(bEnabled);
+}
+
+void ExtrusionDirectionWindow::statusChanged(
+ const css::frame::FeatureStateEvent& Event
+)
+{
+ if( Event.FeatureURL.Main == g_sExtrusionDirection )
+ {
+ if( !Event.IsEnabled )
+ {
+ implSetDirection( -1, false );
+ }
+ else
+ {
+ sal_Int32 nValue = 0;
+ if( Event.State >>= nValue )
+ implSetDirection( nValue, true );
+ }
+ }
+ else if( Event.FeatureURL.Main == g_sExtrusionProjection )
+ {
+ if( !Event.IsEnabled )
+ {
+ implSetProjection( -1, false );
+ }
+ else
+ {
+ sal_Int32 nValue = 0;
+ if( Event.State >>= nValue )
+ implSetProjection( nValue, true );
+ }
+ }
+}
+
+IMPL_LINK_NOARG(ExtrusionDirectionWindow, SelectValueSetHdl, ValueSet*, void)
+{
+ Sequence< PropertyValue > aArgs{ comphelper::makePropertyValue(
+ OUString(g_sExtrusionDirection).copy(5),
+ gSkewList[mxDirectionSet->GetSelectedItemId()-1]) };
+
+ mxControl->dispatchCommand( g_sExtrusionDirection, aArgs );
+
+ mxControl->EndPopupMode();
+}
+
+IMPL_LINK_NOARG(ExtrusionDirectionWindow, SelectToolbarMenuHdl, weld::Toggleable&, void)
+{
+ int nProjection = mxPerspective->get_active() ? 0 : 1;
+
+ Sequence< PropertyValue > aArgs{ comphelper::makePropertyValue(
+ OUString(g_sExtrusionProjection).copy(5), static_cast<sal_Int32>(nProjection)) };
+
+ mxControl->dispatchCommand( g_sExtrusionProjection, aArgs );
+ implSetProjection( nProjection, true );
+
+ mxControl->EndPopupMode();
+}
+
+ExtrusionDirectionControl::ExtrusionDirectionControl(
+ const Reference< XComponentContext >& rxContext
+) : svt::PopupWindowController(
+ rxContext,
+ Reference< css::frame::XFrame >(),
+ ".uno:ExtrusionDirectionFloater"
+ )
+{
+}
+
+std::unique_ptr<WeldToolbarPopup> ExtrusionDirectionControl::weldPopupWindow()
+{
+ return std::make_unique<ExtrusionDirectionWindow>(this, m_pToolbar);
+}
+
+VclPtr<vcl::Window> ExtrusionDirectionControl::createVclPopupWindow( vcl::Window* pParent )
+{
+ mxInterimPopover = VclPtr<InterimToolbarPopup>::Create(getFrameInterface(), pParent,
+ std::make_unique<ExtrusionDirectionWindow>(this, pParent->GetFrameWeld()));
+
+ mxInterimPopover->Show();
+
+ return mxInterimPopover;
+}
+
+// XInitialization
+void SAL_CALL ExtrusionDirectionControl::initialize( const css::uno::Sequence< css::uno::Any >& aArguments )
+{
+ svt::PopupWindowController::initialize( aArguments );
+
+ ToolBox* pToolBox = nullptr;
+ ToolBoxItemId nId;
+ if ( getToolboxId( nId, &pToolBox ) )
+ pToolBox->SetItemBits( nId, pToolBox->GetItemBits( nId ) | ToolBoxItemBits::DROPDOWNONLY );
+}
+
+// XServiceInfo
+
+
+OUString ExtrusionDirectionControl::getImplementationName()
+{
+ return "com.sun.star.comp.svx.ExtrusionDirectionController";
+}
+
+
+Sequence< OUString > ExtrusionDirectionControl::getSupportedServiceNames()
+{
+ return { "com.sun.star.frame.ToolbarController" };
+}
+
+
+extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface*
+com_sun_star_comp_svx_ExtrusionDirectionControl_get_implementation(
+ css::uno::XComponentContext* xContext,
+ css::uno::Sequence<css::uno::Any> const &)
+{
+ return cppu::acquire(new ExtrusionDirectionControl(xContext));
+}
+
+
+ExtrusionDepthDialog::ExtrusionDepthDialog(weld::Window* pParent, double fDepth, FieldUnit eDefaultUnit)
+ : GenericDialogController(pParent, "svx/ui/extrustiondepthdialog.ui", "ExtrustionDepthDialog")
+ , m_xMtrDepth(m_xBuilder->weld_metric_spin_button("depth", eDefaultUnit))
+{
+ m_xMtrDepth->set_value(static_cast<int>(fDepth) * 100, FieldUnit::MM_100TH);
+}
+
+ExtrusionDepthDialog::~ExtrusionDepthDialog()
+{
+}
+
+double ExtrusionDepthDialog::getDepth() const
+{
+ return static_cast<double>(m_xMtrDepth->get_value(FieldUnit::MM_100TH)) / 100.0;
+}
+
+double const aDepthListInch[] = { 0, 1270,2540,5080,10160 };
+double const aDepthListMM[] = { 0, 1000, 2500, 5000, 10000 };
+
+constexpr OUStringLiteral gsExtrusionDepth( u".uno:ExtrusionDepth" );
+constexpr OUStringLiteral gsMetricUnit( u".uno:MetricUnit" );
+
+ExtrusionDepthWindow::ExtrusionDepthWindow(svt::PopupWindowController* pControl, weld::Widget* pParent)
+ : WeldToolbarPopup(pControl->getFrameInterface(), pParent, "svx/ui/depthwindow.ui", "DepthWindow")
+ , mxControl(pControl)
+ , mxDepth0(m_xBuilder->weld_radio_button("depth0"))
+ , mxDepth1(m_xBuilder->weld_radio_button("depth1"))
+ , mxDepth2(m_xBuilder->weld_radio_button("depth2"))
+ , mxDepth3(m_xBuilder->weld_radio_button("depth3"))
+ , mxDepth4(m_xBuilder->weld_radio_button("depth4"))
+ , mxInfinity(m_xBuilder->weld_radio_button("infinity"))
+ , mxCustom(m_xBuilder->weld_radio_button("custom"))
+ , meUnit(FieldUnit::NONE)
+ , mfDepth( -1.0 )
+ , mbSettingValue(false)
+ , mbCommandDispatched(false)
+{
+ mxDepth0->connect_toggled(LINK(this, ExtrusionDepthWindow, SelectHdl));
+ mxDepth1->connect_toggled(LINK(this, ExtrusionDepthWindow, SelectHdl));
+ mxDepth2->connect_toggled(LINK(this, ExtrusionDepthWindow, SelectHdl));
+ mxDepth3->connect_toggled(LINK(this, ExtrusionDepthWindow, SelectHdl));
+ mxDepth4->connect_toggled(LINK(this, ExtrusionDepthWindow, SelectHdl));
+ mxInfinity->connect_toggled(LINK(this, ExtrusionDepthWindow, SelectHdl));
+ mxCustom->connect_toggled(LINK(this, ExtrusionDepthWindow, SelectHdl));
+ mxCustom->connect_mouse_release(LINK(this, ExtrusionDepthWindow, MouseReleaseHdl));
+
+ AddStatusListener( gsExtrusionDepth );
+ AddStatusListener( gsMetricUnit );
+}
+
+void ExtrusionDepthWindow::GrabFocus()
+{
+ mxDepth0->grab_focus();
+}
+
+void ExtrusionDepthWindow::implSetDepth( double fDepth )
+{
+ mfDepth = fDepth;
+
+ bool bSettingValue = mbSettingValue;
+ mbSettingValue = true;
+
+ mxCustom->set_active(true);
+ bool bIsMetric = IsMetric(meUnit);
+ mxDepth0->set_active(fDepth == (bIsMetric ? aDepthListMM[0] : aDepthListInch[0]));
+ mxDepth1->set_active(fDepth == (bIsMetric ? aDepthListMM[1] : aDepthListInch[1]));
+ mxDepth2->set_active(fDepth == (bIsMetric ? aDepthListMM[2] : aDepthListInch[2]));
+ mxDepth3->set_active(fDepth == (bIsMetric ? aDepthListMM[3] : aDepthListInch[3]));
+ mxDepth4->set_active(fDepth == (bIsMetric ? aDepthListMM[4] : aDepthListInch[4]));
+ mxInfinity->set_active(fDepth >= 338666);
+
+ mbSettingValue = bSettingValue;
+}
+
+void ExtrusionDepthWindow::implFillStrings( FieldUnit eUnit )
+{
+ meUnit = eUnit;
+
+ const TranslateId aDepths[] =
+ {
+ RID_SVXSTR_DEPTH_0,
+ RID_SVXSTR_DEPTH_1,
+ RID_SVXSTR_DEPTH_2,
+ RID_SVXSTR_DEPTH_3,
+ RID_SVXSTR_DEPTH_4
+ };
+
+ const TranslateId aDepthsInch[] =
+ {
+ RID_SVXSTR_DEPTH_0_INCH,
+ RID_SVXSTR_DEPTH_1_INCH,
+ RID_SVXSTR_DEPTH_2_INCH,
+ RID_SVXSTR_DEPTH_3_INCH,
+ RID_SVXSTR_DEPTH_4_INCH
+ };
+
+ static_assert(SAL_N_ELEMENTS(aDepths) == SAL_N_ELEMENTS(aDepthsInch));
+
+ const TranslateId* pResource = IsMetric(eUnit) ? aDepths : aDepthsInch;
+
+ mxDepth0->set_label(SvxResId(pResource[0]));
+ mxDepth1->set_label(SvxResId(pResource[1]));
+ mxDepth2->set_label(SvxResId(pResource[2]));
+ mxDepth3->set_label(SvxResId(pResource[3]));
+ mxDepth4->set_label(SvxResId(pResource[4]));
+}
+
+void ExtrusionDepthWindow::statusChanged(
+ const css::frame::FeatureStateEvent& Event
+)
+{
+ if( Event.FeatureURL.Main == gsExtrusionDepth )
+ {
+ if( !Event.IsEnabled )
+ {
+ implSetDepth( 0 );
+ }
+ else
+ {
+ double fValue = 0.0;
+ if( Event.State >>= fValue )
+ implSetDepth( fValue );
+ }
+ }
+ else if( Event.FeatureURL.Main == gsMetricUnit )
+ {
+ if( Event.IsEnabled )
+ {
+ sal_Int32 nValue = 0;
+ if( Event.State >>= nValue )
+ {
+ implFillStrings( static_cast<FieldUnit>(nValue) );
+ if( mfDepth >= 0.0 )
+ implSetDepth( mfDepth );
+ }
+ }
+ }
+}
+
+void ExtrusionDepthWindow::DispatchDepthDialog()
+{
+ Sequence< PropertyValue > aArgs{
+ comphelper::makePropertyValue("Depth", mfDepth),
+ comphelper::makePropertyValue("Metric", static_cast<sal_Int32>( meUnit ))
+ };
+
+ rtl::Reference<svt::PopupWindowController> xControl(mxControl);
+ xControl->EndPopupMode();
+ xControl->dispatchCommand(".uno:ExtrusionDepthDialog", aArgs);
+ mbCommandDispatched = true;
+}
+
+IMPL_LINK(ExtrusionDepthWindow, SelectHdl, weld::Toggleable&, rButton, void)
+{
+ if (mbSettingValue || !rButton.get_active())
+ return;
+
+ // see MouseReleaseHdl for mbCommandDispatched check, there's no guarantee
+ // this toggle will happen before that mouse release though it does in
+ // practice for vcl and gtk
+ if (mbCommandDispatched)
+ return;
+
+ if (mxCustom->get_active())
+ DispatchDepthDialog();
+ else
+ {
+ double fDepth;
+
+ if (mxInfinity->get_active())
+ {
+ fDepth = 338666.6;
+ }
+ else
+ {
+ int nSelected;
+ if (mxDepth0->get_active())
+ nSelected = 0;
+ else if (mxDepth1->get_active())
+ nSelected = 1;
+ else if (mxDepth2->get_active())
+ nSelected = 2;
+ else if (mxDepth3->get_active())
+ nSelected = 3;
+ else
+ nSelected = 4;
+
+ fDepth = IsMetric( meUnit ) ? aDepthListMM[nSelected] : aDepthListInch[nSelected];
+ }
+
+ Sequence< PropertyValue > aArgs{ comphelper::makePropertyValue(
+ OUString(gsExtrusionDepth).copy(5), fDepth) };
+
+ mxControl->dispatchCommand( gsExtrusionDepth, aArgs );
+ mbCommandDispatched = true;
+ implSetDepth( fDepth );
+
+ mxControl->EndPopupMode();
+ }
+}
+
+IMPL_LINK_NOARG(ExtrusionDepthWindow, MouseReleaseHdl, const MouseEvent&, bool)
+{
+ /*
+ tdf#145296 if the "custom" radiobutton was presented preselected as
+ toggled on and the user clicked on it then there's no toggled signal sent
+ because the item was already toggled on and didn't change state.
+
+ So if that happens launch the custom spacing dialog explicitly here on
+ mouse release.
+ */
+ if (mxCustom->get_active() && !mbCommandDispatched)
+ {
+ DispatchDepthDialog();
+ return true;
+ }
+ return false;
+}
+
+// ExtrusionDirectionControl
+ExtrusionDepthController::ExtrusionDepthController(
+ const Reference< XComponentContext >& rxContext
+) : svt::PopupWindowController(
+ rxContext,
+ Reference< css::frame::XFrame >(),
+ ".uno:ExtrusionDepthFloater"
+ )
+{
+}
+
+std::unique_ptr<WeldToolbarPopup> ExtrusionDepthController::weldPopupWindow()
+{
+ return std::make_unique<ExtrusionDepthWindow>(this, m_pToolbar);
+}
+
+VclPtr<vcl::Window> ExtrusionDepthController::createVclPopupWindow( vcl::Window* pParent )
+{
+ mxInterimPopover = VclPtr<InterimToolbarPopup>::Create(getFrameInterface(), pParent,
+ std::make_unique<ExtrusionDepthWindow>(this, pParent->GetFrameWeld()));
+
+ mxInterimPopover->Show();
+
+ return mxInterimPopover;
+}
+
+// XInitialization
+void SAL_CALL ExtrusionDepthController::initialize( const css::uno::Sequence< css::uno::Any >& aArguments )
+{
+ svt::PopupWindowController::initialize( aArguments );
+
+ ToolBox* pToolBox = nullptr;
+ ToolBoxItemId nId;
+ if ( getToolboxId( nId, &pToolBox ) )
+ pToolBox->SetItemBits( nId, pToolBox->GetItemBits( nId ) | ToolBoxItemBits::DROPDOWNONLY );
+}
+
+// XServiceInfo
+
+
+OUString ExtrusionDepthController::getImplementationName()
+{
+ return "com.sun.star.comp.svx.ExtrusionDepthController";
+}
+
+
+Sequence< OUString > ExtrusionDepthController::getSupportedServiceNames()
+{
+ return { "com.sun.star.frame.ToolbarController" };
+}
+
+
+extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface*
+com_sun_star_comp_svx_ExtrusionDepthController_get_implementation(
+ css::uno::XComponentContext* xContext,
+ css::uno::Sequence<css::uno::Any> const &)
+{
+ return cppu::acquire(new ExtrusionDepthController(xContext));
+}
+
+
+constexpr OUStringLiteral g_sExtrusionLightingDirection = u".uno:ExtrusionLightingDirection";
+constexpr OUStringLiteral g_sExtrusionLightingIntensity = u".uno:ExtrusionLightingIntensity";
+
+ExtrusionLightingWindow::ExtrusionLightingWindow(svt::PopupWindowController* pControl,
+ weld::Widget* pParent)
+ : WeldToolbarPopup(pControl->getFrameInterface(), pParent, "svx/ui/lightingwindow.ui", "LightingWindow")
+ , mxControl(pControl)
+ , mxLightingSet(new ValueSet(nullptr))
+ , mxLightingSetWin(new weld::CustomWeld(*m_xBuilder, "valueset", *mxLightingSet))
+ , mxBright(m_xBuilder->weld_radio_button("bright"))
+ , mxNormal(m_xBuilder->weld_radio_button("normal"))
+ , mxDim(m_xBuilder->weld_radio_button("dim"))
+{
+ mxLightingSet->SetStyle(WB_TABSTOP | WB_MENUSTYLEVALUESET | WB_FLATVALUESET | WB_NOBORDER | WB_NO_DIRECTSELECT);
+
+ for (sal_uInt16 i = FROM_TOP_LEFT; i <= FROM_BOTTOM_RIGHT; ++i)
+ {
+ if( i != FROM_FRONT )
+ {
+ maImgLightingOff[i] = Image(StockImage::Yes, OUString(aLightOffBmps[i]));
+ maImgLightingOn[i] = Image(StockImage::Yes, OUString(aLightOnBmps[i]));
+ }
+ maImgLightingPreview[i] = Image(StockImage::Yes, OUString(aLightPreviewBmps[i]));
+ }
+
+ mxLightingSet->SetHelpId( HID_VALUESET_EXTRUSION_LIGHTING );
+
+ mxLightingSet->SetSelectHdl( LINK( this, ExtrusionLightingWindow, SelectValueSetHdl ) );
+ mxLightingSet->SetColCount( 3 );
+ mxLightingSet->EnableFullItemMode( false );
+
+ for (sal_uInt16 i = FROM_TOP_LEFT; i <= FROM_BOTTOM_RIGHT; ++i)
+ {
+ if( i != FROM_FRONT )
+ {
+ mxLightingSet->InsertItem( i+1, maImgLightingOff[i] );
+ }
+ else
+ {
+ mxLightingSet->InsertItem( 5, maImgLightingPreview[FROM_FRONT] );
+ }
+ }
+ Size aSize(72, 72);
+ mxLightingSet->GetDrawingArea()->set_size_request(aSize.Width(), aSize.Height());
+ mxLightingSet->SetOutputSizePixel(aSize);
+
+ mxBright->connect_toggled(LINK(this, ExtrusionLightingWindow, SelectToolbarMenuHdl));
+ mxNormal->connect_toggled(LINK(this, ExtrusionLightingWindow, SelectToolbarMenuHdl));
+ mxDim->connect_toggled(LINK(this, ExtrusionLightingWindow, SelectToolbarMenuHdl));
+
+ AddStatusListener( g_sExtrusionLightingDirection );
+ AddStatusListener( g_sExtrusionLightingIntensity );
+}
+
+void ExtrusionLightingWindow::GrabFocus()
+{
+ mxLightingSet->GrabFocus();
+}
+
+ExtrusionLightingWindow::~ExtrusionLightingWindow()
+{
+}
+
+void ExtrusionLightingWindow::implSetIntensity( int nLevel, bool bEnabled )
+{
+ mxBright->set_sensitive(bEnabled);
+ mxBright->set_active(nLevel == 0 && bEnabled);
+ mxNormal->set_sensitive(bEnabled);
+ mxNormal->set_active(nLevel == 1 && bEnabled);
+ mxDim->set_sensitive(bEnabled);
+ mxDim->set_active(nLevel == 2 && bEnabled);
+}
+
+void ExtrusionLightingWindow::implSetDirection( int nDirection, bool bEnabled )
+{
+ if( !bEnabled )
+ nDirection = FROM_FRONT;
+
+ sal_uInt16 nItemId;
+ for( nItemId = FROM_TOP_LEFT; nItemId <= FROM_BOTTOM_RIGHT; nItemId++ )
+ {
+ if( nItemId == FROM_FRONT )
+ {
+ mxLightingSet->SetItemImage( nItemId + 1, maImgLightingPreview[ nDirection ] );
+ }
+ else
+ {
+ mxLightingSet->SetItemImage(
+ nItemId + 1,
+ static_cast<sal_uInt16>(nDirection) == nItemId ? maImgLightingOn[nItemId] : maImgLightingOff[nItemId]
+ );
+ }
+ }
+
+ if (bEnabled)
+ mxLightingSet->Enable();
+ else
+ mxLightingSet->Disable();
+}
+
+void ExtrusionLightingWindow::statusChanged(
+ const css::frame::FeatureStateEvent& Event
+)
+{
+ if( Event.FeatureURL.Main == g_sExtrusionLightingIntensity )
+ {
+ if( !Event.IsEnabled )
+ {
+ implSetIntensity( 0, false );
+ }
+ else
+ {
+ sal_Int32 nValue = 0;
+ if( Event.State >>= nValue )
+ implSetIntensity( nValue, true );
+ }
+ }
+ else if( Event.FeatureURL.Main == g_sExtrusionLightingDirection )
+ {
+ if( !Event.IsEnabled )
+ {
+ implSetDirection( 0, false );
+ }
+ else
+ {
+ sal_Int32 nValue = 0;
+ if( Event.State >>= nValue )
+ implSetDirection( nValue, true );
+ }
+ }
+}
+
+IMPL_LINK_NOARG(ExtrusionLightingWindow, SelectValueSetHdl, ValueSet*, void)
+{
+ sal_Int32 nDirection = mxLightingSet->GetSelectedItemId();
+
+ if( (nDirection > 0) && (nDirection < 10) )
+ {
+ nDirection--;
+
+ Sequence< PropertyValue > aArgs{ comphelper::makePropertyValue(
+ OUString(g_sExtrusionLightingDirection).copy(5), nDirection) };
+
+ mxControl->dispatchCommand( g_sExtrusionLightingDirection, aArgs );
+
+ implSetDirection( nDirection, true );
+ }
+
+ mxControl->EndPopupMode();
+}
+
+IMPL_LINK(ExtrusionLightingWindow, SelectToolbarMenuHdl, weld::Toggleable&, rButton, void)
+{
+ if (!rButton.get_active())
+ return;
+
+ int nLevel;
+ if (mxBright->get_active())
+ nLevel = 0;
+ else if (mxNormal->get_active())
+ nLevel = 1;
+ else
+ nLevel = 2;
+
+ Sequence< PropertyValue > aArgs{ comphelper::makePropertyValue(
+ OUString(g_sExtrusionLightingIntensity).copy(5), static_cast<sal_Int32>(nLevel)) };
+
+ mxControl->dispatchCommand( g_sExtrusionLightingIntensity, aArgs );
+
+ implSetIntensity( nLevel, true );
+
+ mxControl->EndPopupMode();
+}
+
+ExtrusionLightingControl::ExtrusionLightingControl(
+ const Reference< XComponentContext >& rxContext
+) : svt::PopupWindowController( rxContext,
+ Reference< css::frame::XFrame >(),
+ ".uno:ExtrusionDirectionFloater"
+ )
+{
+}
+
+std::unique_ptr<WeldToolbarPopup> ExtrusionLightingControl::weldPopupWindow()
+{
+ return std::make_unique<ExtrusionLightingWindow>(this, m_pToolbar);
+}
+
+VclPtr<vcl::Window> ExtrusionLightingControl::createVclPopupWindow( vcl::Window* pParent )
+{
+ mxInterimPopover = VclPtr<InterimToolbarPopup>::Create(getFrameInterface(), pParent,
+ std::make_unique<ExtrusionLightingWindow>(this, pParent->GetFrameWeld()));
+
+ mxInterimPopover->Show();
+
+ return mxInterimPopover;
+}
+
+// XInitialization
+void SAL_CALL ExtrusionLightingControl::initialize( const css::uno::Sequence< css::uno::Any >& aArguments )
+{
+ svt::PopupWindowController::initialize( aArguments );
+
+ ToolBox* pToolBox = nullptr;
+ ToolBoxItemId nId;
+ if ( getToolboxId( nId, &pToolBox ) )
+ pToolBox->SetItemBits( nId, pToolBox->GetItemBits( nId ) | ToolBoxItemBits::DROPDOWNONLY );
+}
+
+// XServiceInfo
+
+
+OUString ExtrusionLightingControl::getImplementationName()
+{
+ return "com.sun.star.comp.svx.ExtrusionLightingController";
+}
+
+
+Sequence< OUString > ExtrusionLightingControl::getSupportedServiceNames()
+{
+ return { "com.sun.star.frame.ToolbarController" };
+}
+
+
+extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface*
+com_sun_star_comp_svx_ExtrusionLightingControl_get_implementation(
+ css::uno::XComponentContext* xContext,
+ css::uno::Sequence<css::uno::Any> const &)
+{
+ return cppu::acquire(new ExtrusionLightingControl(xContext));
+}
+
+
+constexpr OUStringLiteral g_sExtrusionSurface = u".uno:ExtrusionSurface";
+
+ExtrusionSurfaceWindow::ExtrusionSurfaceWindow(svt::PopupWindowController* pControl, weld::Widget* pParent)
+ : WeldToolbarPopup(pControl->getFrameInterface(), pParent, "svx/ui/surfacewindow.ui", "SurfaceWindow")
+ , mxControl(pControl)
+ , mxWireFrame(m_xBuilder->weld_radio_button("wireframe"))
+ , mxMatt(m_xBuilder->weld_radio_button("matt"))
+ , mxPlastic(m_xBuilder->weld_radio_button("plastic"))
+ , mxMetal(m_xBuilder->weld_radio_button("metal"))
+ , mxMetalMSO(m_xBuilder->weld_radio_button("metalMSO"))
+{
+ mxWireFrame->connect_toggled(LINK(this, ExtrusionSurfaceWindow, SelectHdl));
+ mxMatt->connect_toggled(LINK(this, ExtrusionSurfaceWindow, SelectHdl));
+ mxPlastic->connect_toggled(LINK(this, ExtrusionSurfaceWindow, SelectHdl));
+ mxMetal->connect_toggled(LINK(this, ExtrusionSurfaceWindow, SelectHdl));
+ mxMetalMSO->connect_toggled(LINK(this, ExtrusionSurfaceWindow, SelectHdl));
+
+ AddStatusListener( g_sExtrusionSurface );
+}
+
+void ExtrusionSurfaceWindow::GrabFocus()
+{
+ mxWireFrame->grab_focus();
+}
+
+void ExtrusionSurfaceWindow::implSetSurface( int nSurface, bool bEnabled )
+{
+ mxWireFrame->set_active(nSurface == 0 && bEnabled);
+ mxWireFrame->set_sensitive(bEnabled);
+ mxMatt->set_active(nSurface == 1 && bEnabled);
+ mxMatt->set_sensitive(bEnabled);
+ mxPlastic->set_active(nSurface == 2 && bEnabled);
+ mxPlastic->set_sensitive(bEnabled);
+ mxMetal->set_active(nSurface == 3 && bEnabled);
+ mxMetal->set_sensitive(bEnabled);
+ mxMetalMSO->set_active(nSurface == 4 && bEnabled);
+ mxMetalMSO->set_sensitive(bEnabled);
+}
+
+void ExtrusionSurfaceWindow::statusChanged(
+ const css::frame::FeatureStateEvent& Event
+)
+{
+ if( Event.FeatureURL.Main != g_sExtrusionSurface )
+ return;
+
+ if( !Event.IsEnabled )
+ {
+ implSetSurface( 0, false );
+ }
+ else
+ {
+ sal_Int32 nValue = 0;
+ if( Event.State >>= nValue )
+ implSetSurface( nValue, true );
+ }
+}
+
+IMPL_LINK(ExtrusionSurfaceWindow, SelectHdl, weld::Toggleable&, rButton, void)
+{
+ if (!rButton.get_active())
+ return;
+
+ sal_Int32 nSurface;
+ if (mxWireFrame->get_active())
+ nSurface = 0;
+ else if (mxMatt->get_active())
+ nSurface = 1;
+ else if (mxPlastic->get_active())
+ nSurface = 2;
+ else if (mxMetal->get_active())
+ nSurface = 3;
+ else
+ nSurface = 4;
+
+ Sequence< PropertyValue > aArgs{ comphelper::makePropertyValue(
+ OUString(g_sExtrusionSurface).copy(5), nSurface) };
+
+ mxControl->dispatchCommand( g_sExtrusionSurface, aArgs );
+
+ implSetSurface( nSurface, true );
+
+ mxControl->EndPopupMode();
+}
+
+ExtrusionSurfaceControl::ExtrusionSurfaceControl(
+ const Reference< XComponentContext >& rxContext
+)
+: svt::PopupWindowController(
+ rxContext,
+ Reference< css::frame::XFrame >(),
+ ".uno:ExtrusionSurfaceFloater"
+ )
+{
+}
+
+std::unique_ptr<WeldToolbarPopup> ExtrusionSurfaceControl::weldPopupWindow()
+{
+ return std::make_unique<ExtrusionSurfaceWindow>(this, m_pToolbar);
+}
+
+VclPtr<vcl::Window> ExtrusionSurfaceControl::createVclPopupWindow( vcl::Window* pParent )
+{
+ mxInterimPopover = VclPtr<InterimToolbarPopup>::Create(getFrameInterface(), pParent,
+ std::make_unique<ExtrusionSurfaceWindow>(this, pParent->GetFrameWeld()));
+
+ mxInterimPopover->Show();
+
+ return mxInterimPopover;
+}
+
+// XInitialization
+void SAL_CALL ExtrusionSurfaceControl::initialize( const css::uno::Sequence< css::uno::Any >& aArguments )
+{
+ svt::PopupWindowController::initialize( aArguments );
+
+ ToolBox* pToolBox = nullptr;
+ ToolBoxItemId nId;
+ if ( getToolboxId( nId, &pToolBox ) )
+ pToolBox->SetItemBits( nId, pToolBox->GetItemBits( nId ) | ToolBoxItemBits::DROPDOWNONLY );
+}
+
+// XServiceInfo
+
+
+OUString ExtrusionSurfaceControl::getImplementationName()
+{
+ return "com.sun.star.comp.svx.ExtrusionSurfaceController";
+}
+
+
+Sequence< OUString > ExtrusionSurfaceControl::getSupportedServiceNames()
+{
+ return { "com.sun.star.frame.ToolbarController" };
+}
+
+
+extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface*
+com_sun_star_comp_svx_ExtrusionSurfaceControl_get_implementation(
+ css::uno::XComponentContext* xContext,
+ css::uno::Sequence<css::uno::Any> const &)
+{
+ return cppu::acquire(new ExtrusionSurfaceControl(xContext));
+}
+
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/tbxctrls/extrusioncontrols.hxx b/svx/source/tbxctrls/extrusioncontrols.hxx
new file mode 100644
index 000000000..8b7c2e8af
--- /dev/null
+++ b/svx/source/tbxctrls/extrusioncontrols.hxx
@@ -0,0 +1,224 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+#ifndef INCLUDED_SVX_SOURCE_TBXCTRLS_EXTRUSIONCONTROLS_HXX
+#define INCLUDED_SVX_SOURCE_TBXCTRLS_EXTRUSIONCONTROLS_HXX
+
+#include <svtools/toolbarmenu.hxx>
+#include <svtools/popupwindowcontroller.hxx>
+#include <svtools/valueset.hxx>
+#include <vcl/customweld.hxx>
+
+// enum to index light images
+#define FROM_TOP_LEFT 0
+#define FROM_TOP 1
+#define FROM_TOP_RIGHT 2
+#define FROM_LEFT 3
+#define FROM_FRONT 4
+#define FROM_RIGHT 5
+#define FROM_BOTTOM_LEFT 6
+#define FROM_BOTTOM 7
+#define FROM_BOTTOM_RIGHT 8
+
+#define DIRECTION_NW 0
+#define DIRECTION_N 1
+#define DIRECTION_NE 2
+#define DIRECTION_W 3
+#define DIRECTION_NONE 4
+#define DIRECTION_E 5
+#define DIRECTION_SW 6
+#define DIRECTION_S 7
+#define DIRECTION_SE 8
+
+namespace svx
+{
+class ExtrusionDirectionWindow final : public WeldToolbarPopup
+{
+public:
+ ExtrusionDirectionWindow(svt::PopupWindowController* pControl, weld::Widget* pParentWindow);
+ virtual void GrabFocus() override;
+ virtual ~ExtrusionDirectionWindow() override;
+
+ virtual void statusChanged( const css::frame::FeatureStateEvent& Event ) override;
+
+private:
+ rtl::Reference<svt::PopupWindowController> mxControl;
+ std::unique_ptr<ValueSet> mxDirectionSet;
+ std::unique_ptr<weld::CustomWeld> mxDirectionSetWin;
+ std::unique_ptr<weld::RadioButton> mxPerspective;
+ std::unique_ptr<weld::RadioButton> mxParallel;
+
+ Image maImgDirection[9];
+
+ DECL_LINK( SelectToolbarMenuHdl, weld::Toggleable&, void );
+ DECL_LINK( SelectValueSetHdl, ValueSet*, void );
+
+ void implSetDirection( sal_Int32 nSkew, bool bEnabled );
+ void implSetProjection( sal_Int32 nProjection, bool bEnabled );
+
+};
+
+class ExtrusionDirectionControl : public svt::PopupWindowController
+{
+public:
+ explicit ExtrusionDirectionControl( const css::uno::Reference< css::uno::XComponentContext >& rxContext );
+
+ virtual std::unique_ptr<WeldToolbarPopup> weldPopupWindow() override;
+ virtual VclPtr<vcl::Window> createVclPopupWindow( vcl::Window* pParent ) override;
+
+ // XInitialization
+ virtual void SAL_CALL initialize( const css::uno::Sequence< css::uno::Any >& aArguments ) override;
+
+ // XServiceInfo
+ virtual OUString SAL_CALL getImplementationName() override;
+ virtual css::uno::Sequence< OUString > SAL_CALL getSupportedServiceNames() override;
+};
+
+class ExtrusionDepthWindow final : public WeldToolbarPopup
+{
+private:
+ rtl::Reference<svt::PopupWindowController> mxControl;
+ std::unique_ptr<weld::RadioButton> mxDepth0;
+ std::unique_ptr<weld::RadioButton> mxDepth1;
+ std::unique_ptr<weld::RadioButton> mxDepth2;
+ std::unique_ptr<weld::RadioButton> mxDepth3;
+ std::unique_ptr<weld::RadioButton> mxDepth4;
+ std::unique_ptr<weld::RadioButton> mxInfinity;
+ std::unique_ptr<weld::RadioButton> mxCustom;
+
+ FieldUnit meUnit;
+ double mfDepth;
+ bool mbSettingValue;
+ bool mbCommandDispatched;
+
+ DECL_LINK( SelectHdl, weld::Toggleable&, void );
+ DECL_LINK( MouseReleaseHdl, const MouseEvent&, bool );
+
+ void implFillStrings( FieldUnit eUnit );
+ void implSetDepth( double fDepth );
+
+ void DispatchDepthDialog();
+
+public:
+ ExtrusionDepthWindow(svt::PopupWindowController* pControl, weld::Widget* pParentWindow);
+ virtual void GrabFocus() override;
+
+ virtual void statusChanged( const css::frame::FeatureStateEvent& Event ) override;
+};
+
+class ExtrusionDepthController : public svt::PopupWindowController
+{
+public:
+ explicit ExtrusionDepthController( const css::uno::Reference< css::uno::XComponentContext >& rxContext );
+
+ virtual std::unique_ptr<WeldToolbarPopup> weldPopupWindow() override;
+ virtual VclPtr<vcl::Window> createVclPopupWindow( vcl::Window* pParent ) override;
+
+ // XInitialization
+ virtual void SAL_CALL initialize( const css::uno::Sequence< css::uno::Any >& aArguments ) override;
+
+ // XServiceInfo
+ virtual OUString SAL_CALL getImplementationName() override;
+ virtual css::uno::Sequence< OUString > SAL_CALL getSupportedServiceNames() override;
+};
+
+class ExtrusionLightingWindow final : public WeldToolbarPopup
+{
+private:
+ rtl::Reference<svt::PopupWindowController> mxControl;
+ std::unique_ptr<ValueSet> mxLightingSet;
+ std::unique_ptr<weld::CustomWeld> mxLightingSetWin;
+ std::unique_ptr<weld::RadioButton> mxBright;
+ std::unique_ptr<weld::RadioButton> mxNormal;
+ std::unique_ptr<weld::RadioButton> mxDim;
+
+ Image maImgLightingOff[9];
+ Image maImgLightingOn[9];
+ Image maImgLightingPreview[9];
+
+ void implSetIntensity( int nLevel, bool bEnabled );
+ void implSetDirection( int nDirection, bool bEnabled );
+
+ DECL_LINK( SelectToolbarMenuHdl, weld::Toggleable&, void );
+ DECL_LINK( SelectValueSetHdl, ValueSet*, void );
+public:
+ ExtrusionLightingWindow(svt::PopupWindowController* pControl, weld::Widget* pParentWindow);
+ virtual void GrabFocus() override;
+ virtual ~ExtrusionLightingWindow() override;
+
+ virtual void statusChanged( const css::frame::FeatureStateEvent& Event ) override;
+};
+
+class ExtrusionLightingControl : public svt::PopupWindowController
+{
+public:
+ explicit ExtrusionLightingControl( const css::uno::Reference< css::uno::XComponentContext >& rxContext );
+
+ virtual std::unique_ptr<WeldToolbarPopup> weldPopupWindow() override;
+ virtual VclPtr<vcl::Window> createVclPopupWindow( vcl::Window* pParent ) override;
+
+ // XInitialization
+ virtual void SAL_CALL initialize( const css::uno::Sequence< css::uno::Any >& aArguments ) override;
+
+ // XServiceInfo
+ virtual OUString SAL_CALL getImplementationName() override;
+ virtual css::uno::Sequence< OUString > SAL_CALL getSupportedServiceNames() override;
+};
+
+class ExtrusionSurfaceWindow final : public WeldToolbarPopup
+{
+private:
+ rtl::Reference<svt::PopupWindowController> mxControl;
+ std::unique_ptr<weld::RadioButton> mxWireFrame;
+ std::unique_ptr<weld::RadioButton> mxMatt;
+ std::unique_ptr<weld::RadioButton> mxPlastic;
+ std::unique_ptr<weld::RadioButton> mxMetal;
+ std::unique_ptr<weld::RadioButton> mxMetalMSO;
+
+ DECL_LINK( SelectHdl, weld::Toggleable&, void );
+
+ void implSetSurface( int nSurface, bool bEnabled );
+
+public:
+ ExtrusionSurfaceWindow(svt::PopupWindowController* pControl, weld::Widget* pParentWindow);
+ virtual void GrabFocus() override;
+
+ virtual void statusChanged( const css::frame::FeatureStateEvent& Event ) override;
+};
+
+
+class ExtrusionSurfaceControl : public svt::PopupWindowController
+{
+public:
+ explicit ExtrusionSurfaceControl( const css::uno::Reference< css::uno::XComponentContext >& rxContext );
+
+ virtual std::unique_ptr<WeldToolbarPopup> weldPopupWindow() override;
+ virtual VclPtr<vcl::Window> createVclPopupWindow( vcl::Window* pParent ) override;
+
+ // XInitialization
+ virtual void SAL_CALL initialize( const css::uno::Sequence< css::uno::Any >& aArguments ) override;
+
+ // XServiceInfo
+ virtual OUString SAL_CALL getImplementationName() override;
+ virtual css::uno::Sequence< OUString > SAL_CALL getSupportedServiceNames() override;
+};
+
+}
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/tbxctrls/fillctrl.cxx b/svx/source/tbxctrls/fillctrl.cxx
new file mode 100644
index 000000000..cfb7583be
--- /dev/null
+++ b/svx/source/tbxctrls/fillctrl.cxx
@@ -0,0 +1,1087 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * 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/objsh.hxx>
+#include <sfx2/viewsh.hxx>
+#include <rtl/ustring.hxx>
+#include <vcl/event.hxx>
+#include <vcl/settings.hxx>
+#include <vcl/toolbox.hxx>
+#include <vcl/virdev.hxx>
+#include <svx/svxids.hrc>
+#include <tools/json_writer.hxx>
+
+constexpr OUStringLiteral TMP_STR_BEGIN = u"[";
+constexpr OUStringLiteral TMP_STR_END = u"]";
+
+#include <svx/drawitem.hxx>
+#include <svx/xfillit0.hxx>
+#include <svx/xtable.hxx>
+#include <svx/fillctrl.hxx>
+#include <svx/itemwin.hxx>
+#include <svx/xflclit.hxx>
+#include <svx/xflgrit.hxx>
+#include <svx/xflhtit.hxx>
+#include <svx/xbtmpit.hxx>
+#include <memory>
+
+using namespace ::com::sun::star;
+using namespace ::com::sun::star::uno;
+using namespace ::com::sun::star::util;
+using namespace ::com::sun::star::beans;
+using namespace ::com::sun::star::lang;
+
+namespace {
+
+enum eFillStyle
+{
+ NONE,
+ SOLID,
+ GRADIENT,
+ HATCH,
+ BITMAP,
+ PATTERN
+};
+
+drawing::FillStyle toCssFillStyle( eFillStyle eXFS )
+{
+ if (eXFS == PATTERN)
+ {
+ return drawing::FillStyle_BITMAP;
+ }
+
+ return static_cast<drawing::FillStyle>(eXFS);
+}
+
+}
+
+SFX_IMPL_TOOLBOX_CONTROL( SvxFillToolBoxControl, XFillStyleItem );
+
+SvxFillToolBoxControl::SvxFillToolBoxControl(
+ sal_uInt16 nSlotId,
+ ToolBoxItemId nId,
+ ToolBox& rTbx )
+ : SfxToolBoxControl( nSlotId, nId, rTbx )
+ , mxFillControl(nullptr)
+ , mpLbFillType(nullptr)
+ , mpToolBoxColor(nullptr)
+ , mpLbFillAttr(nullptr)
+ , mnLastXFS(-1)
+ , mnLastPosGradient(0)
+ , mnLastPosHatch(0)
+ , mnLastPosBitmap(0)
+ , mnLastPosPattern(0)
+{
+ addStatusListener( ".uno:FillColor");
+ addStatusListener( ".uno:FillGradient");
+ addStatusListener( ".uno:FillHatch");
+ addStatusListener( ".uno:FillBitmap");
+ addStatusListener( ".uno:ColorTableState");
+ addStatusListener( ".uno:GradientListState");
+ addStatusListener( ".uno:HatchListState");
+ addStatusListener( ".uno:BitmapListState");
+}
+
+SvxFillToolBoxControl::~SvxFillToolBoxControl()
+{
+}
+
+void SvxFillToolBoxControl::StateChangedAtToolBoxControl(
+ sal_uInt16 nSID,
+ SfxItemState eState,
+ const SfxPoolItem* pState)
+{
+ const bool bDisabled(SfxItemState::DISABLED == eState);
+
+ switch(nSID)
+ {
+ case SID_ATTR_FILL_STYLE:
+ {
+ if(bDisabled)
+ {
+ mpLbFillType->set_sensitive(false);
+ mpLbFillType->set_active(-1);
+ mpLbFillAttr->show();
+ mpLbFillAttr->set_sensitive(false);
+ mpLbFillAttr->set_active(-1);
+ mpToolBoxColor->hide();
+ mnLastXFS = -1;
+ mpStyleItem.reset();
+ }
+
+ if(eState >= SfxItemState::DEFAULT)
+ {
+ const XFillStyleItem* pItem = dynamic_cast< const XFillStyleItem* >(pState);
+
+ if(pItem)
+ {
+ mpStyleItem.reset(pItem->Clone());
+ mpLbFillType->set_sensitive(true);
+ drawing::FillStyle eXFS = mpStyleItem->GetValue();
+ mnLastXFS = sal::static_int_cast< sal_Int32 >(eXFS);
+ mpLbFillType->set_active(mnLastXFS);
+
+ if(drawing::FillStyle_NONE == eXFS)
+ {
+ mpLbFillAttr->set_active(-1);
+ mpLbFillAttr->set_sensitive(false);
+ }
+
+ Update();
+ break;
+ }
+ }
+
+ mpLbFillType->set_active(-1);
+ mpLbFillAttr->show();
+ mpLbFillAttr->set_sensitive(false);
+ mpLbFillAttr->set_active(-1);
+ mpToolBoxColor->hide();
+ mnLastXFS = -1;
+ mpStyleItem.reset();
+ mxFillControl->Resize();
+ break;
+ }
+ case SID_ATTR_FILL_COLOR:
+ {
+ if(SfxItemState::DEFAULT == eState)
+ {
+ mpColorItem.reset(pState ? static_cast<XFillColorItem*>(pState->Clone()) : nullptr);
+ }
+
+ if(mpStyleItem && drawing::FillStyle_SOLID == mpStyleItem->GetValue())
+ {
+ mpLbFillAttr->hide();
+ mpToolBoxColor->show();
+ mxFillControl->Resize();
+
+ Update();
+ }
+ break;
+ }
+ case SID_ATTR_FILL_GRADIENT:
+ {
+ if(SfxItemState::DEFAULT == eState)
+ {
+ mpFillGradientItem.reset(pState ? static_cast<XFillGradientItem*>(pState->Clone()) : nullptr);
+ }
+
+ if(mpStyleItem && drawing::FillStyle_GRADIENT == mpStyleItem->GetValue())
+ {
+ mpLbFillAttr->show();
+ mpToolBoxColor->hide();
+ mxFillControl->Resize();
+
+ if(SfxItemState::DEFAULT == eState)
+ {
+ mpLbFillAttr->set_sensitive(true);
+ Update();
+ }
+ else if(SfxItemState::DISABLED == eState )
+ {
+ mpLbFillAttr->set_sensitive(false);
+ mpLbFillAttr->set_active(-1);
+ }
+ else
+ {
+ mpLbFillAttr->set_active(-1);
+ }
+ }
+ break;
+ }
+ case SID_ATTR_FILL_HATCH:
+ {
+ if(SfxItemState::DEFAULT == eState)
+ {
+ mpHatchItem.reset(pState ? static_cast<XFillHatchItem*>(pState->Clone()) : nullptr);
+ }
+
+ if(mpStyleItem && drawing::FillStyle_HATCH == mpStyleItem->GetValue())
+ {
+ mpLbFillAttr->show();
+ mpToolBoxColor->hide();
+ mxFillControl->Resize();
+
+ if(SfxItemState::DEFAULT == eState)
+ {
+ mpLbFillAttr->set_sensitive(true);
+ Update();
+ }
+ else if(SfxItemState::DISABLED == eState )
+ {
+ mpLbFillAttr->set_sensitive(false);
+ mpLbFillAttr->set_active(-1);
+ }
+ else
+ {
+ mpLbFillAttr->set_active(-1);
+ }
+ }
+ break;
+ }
+ case SID_ATTR_FILL_BITMAP:
+ {
+ if(SfxItemState::DEFAULT == eState)
+ {
+ mpBitmapItem.reset(pState ? static_cast<XFillBitmapItem*>(pState->Clone()) : nullptr);
+ }
+
+ if(mpStyleItem && drawing::FillStyle_BITMAP == mpStyleItem->GetValue())
+ {
+ mpLbFillAttr->show();
+ mpToolBoxColor->hide();
+ mxFillControl->Resize();
+
+ if(SfxItemState::DEFAULT == eState)
+ {
+ mpLbFillAttr->set_sensitive(true);
+ Update();
+ }
+ else if(SfxItemState::DISABLED == eState )
+ {
+ mpLbFillAttr->set_sensitive(false);
+ mpLbFillAttr->set_active(-1);
+ }
+ else
+ {
+ mpLbFillAttr->set_active(-1);
+ }
+ }
+ break;
+ }
+ case SID_GRADIENT_LIST:
+ {
+ if(SfxItemState::DEFAULT == eState)
+ {
+ if(mpStyleItem && drawing::FillStyle_GRADIENT == mpStyleItem->GetValue())
+ {
+ if(mpFillGradientItem)
+ {
+ const OUString aString( mpFillGradientItem->GetName() );
+ mpLbFillAttr->clear();
+ if (const SfxObjectShell* pSh = SfxObjectShell::Current())
+ {
+ mpLbFillAttr->set_sensitive(true);
+ SvxFillAttrBox::Fill(*mpLbFillAttr, pSh->GetItem(SID_GRADIENT_LIST)->GetGradientList());
+ }
+ mpLbFillAttr->set_active_text(aString);
+ }
+ else
+ {
+ mpLbFillAttr->set_active(-1);
+ }
+ }
+ }
+ break;
+ }
+ case SID_HATCH_LIST:
+ {
+ if(SfxItemState::DEFAULT == eState)
+ {
+ if(mpStyleItem && drawing::FillStyle_HATCH == mpStyleItem->GetValue())
+ {
+ if(mpHatchItem)
+ {
+ const OUString aString( mpHatchItem->GetName() );
+ mpLbFillAttr->clear();
+ if (const SfxObjectShell* pSh = SfxObjectShell::Current())
+ {
+ mpLbFillAttr->set_sensitive(true);
+ SvxFillAttrBox::Fill(*mpLbFillAttr, pSh->GetItem(SID_HATCH_LIST)->GetHatchList());
+ }
+ mpLbFillAttr->set_active_text(aString);
+ }
+ else
+ {
+ mpLbFillAttr->set_active(-1);
+ }
+ }
+ }
+ break;
+ }
+ case SID_BITMAP_LIST:
+ {
+ if(SfxItemState::DEFAULT == eState)
+ {
+ if(mpStyleItem && drawing::FillStyle_BITMAP == mpStyleItem->GetValue())
+ {
+ if(mpBitmapItem)
+ {
+ const OUString aString( mpBitmapItem->GetName() );
+ mpLbFillAttr->clear();
+ if (const SfxObjectShell* pSh = SfxObjectShell::Current())
+ {
+ mpLbFillAttr->set_sensitive(true);
+ SvxFillAttrBox::Fill(*mpLbFillAttr, pSh->GetItem(SID_BITMAP_LIST)->GetBitmapList());
+ }
+ mpLbFillAttr->set_active_text(aString);
+ }
+ else
+ {
+ mpLbFillAttr->set_active(-1);
+ }
+ }
+ }
+ break;
+ }
+ }
+}
+
+void SvxFillToolBoxControl::Update()
+{
+ if(!mpStyleItem)
+ return;
+
+ const drawing::FillStyle eXFS = mpStyleItem->GetValue();
+ SfxObjectShell* pSh = SfxObjectShell::Current();
+
+ switch( eXFS )
+ {
+ case drawing::FillStyle_NONE:
+ {
+ mpLbFillAttr->show();
+ mpToolBoxColor->hide();
+ mxFillControl->Resize();
+ break;
+ }
+ case drawing::FillStyle_SOLID:
+ {
+ if(mpColorItem)
+ {
+ mpLbFillAttr->hide();
+ mpToolBoxColor->show();
+ mxFillControl->Resize();
+ }
+ break;
+ }
+ case drawing::FillStyle_GRADIENT:
+ {
+ mpLbFillAttr->show();
+ mpToolBoxColor->hide();
+ mxFillControl->Resize();
+
+ if(pSh && pSh->GetItem(SID_GRADIENT_LIST))
+ {
+ mpLbFillAttr->set_sensitive(true);
+ mpLbFillAttr->clear();
+ SvxFillAttrBox::Fill(*mpLbFillAttr, pSh->GetItem(SID_GRADIENT_LIST)->GetGradientList());
+
+ if(mpFillGradientItem)
+ {
+ const OUString aString(mpFillGradientItem->GetName());
+
+ mpLbFillAttr->set_active_text(aString);
+
+ // Check if the entry is not in the list
+ if (mpLbFillAttr->get_active_text() != aString)
+ {
+ sal_Int32 nCount = mpLbFillAttr->get_count();
+ OUString aTmpStr;
+ if( nCount > 0 )
+ {
+ // Last entry gets tested against temporary entry
+ aTmpStr = mpLbFillAttr->get_text( nCount - 1 );
+ if( aTmpStr.startsWith(TMP_STR_BEGIN) &&
+ aTmpStr.endsWith(TMP_STR_END) )
+ {
+ mpLbFillAttr->remove(nCount - 1);
+ }
+ }
+ aTmpStr = TMP_STR_BEGIN + aString + TMP_STR_END;
+
+ rtl::Reference<XGradientList> xGradientList = new XGradientList( "", ""/*TODO?*/ );
+ xGradientList->Insert(std::make_unique<XGradientEntry>(mpFillGradientItem->GetGradientValue(), aTmpStr));
+ xGradientList->SetDirty( false );
+ const BitmapEx aBmp = xGradientList->GetUiBitmap( 0 );
+
+ if (!aBmp.IsEmpty())
+ {
+ ScopedVclPtrInstance< VirtualDevice > pVD;
+ const Size aBmpSize(aBmp.GetSizePixel());
+ pVD->SetOutputSizePixel(aBmpSize, false);
+ pVD->DrawBitmapEx(Point(), aBmp);
+ mpLbFillAttr->append("", xGradientList->Get(0)->GetName(), *pVD);
+ mpLbFillAttr->set_active(mpLbFillAttr->get_count() - 1);
+ }
+ }
+
+ }
+ else
+ {
+ mpLbFillAttr->set_active(-1);
+ }
+ }
+ else
+ {
+ mpLbFillAttr->set_active(-1);
+ }
+ break;
+ }
+ case drawing::FillStyle_HATCH:
+ {
+ mpLbFillAttr->show();
+ mpToolBoxColor->hide();
+ mxFillControl->Resize();
+
+ if(pSh && pSh->GetItem(SID_HATCH_LIST))
+ {
+ mpLbFillAttr->set_sensitive(true);
+ mpLbFillAttr->clear();
+ SvxFillAttrBox::Fill(*mpLbFillAttr, pSh->GetItem(SID_HATCH_LIST)->GetHatchList());
+
+ if(mpHatchItem)
+ {
+ const OUString aString(mpHatchItem->GetName());
+
+ mpLbFillAttr->set_active_text( aString );
+
+ // Check if the entry is not in the list
+ if( mpLbFillAttr->get_active_text() != aString )
+ {
+ const sal_Int32 nCount = mpLbFillAttr->get_count();
+ OUString aTmpStr;
+ if( nCount > 0 )
+ {
+ // Last entry gets tested against temporary entry
+ aTmpStr = mpLbFillAttr->get_text( nCount - 1 );
+ if( aTmpStr.startsWith(TMP_STR_BEGIN) &&
+ aTmpStr.endsWith(TMP_STR_END) )
+ {
+ mpLbFillAttr->remove( nCount - 1 );
+ }
+ }
+ aTmpStr = TMP_STR_BEGIN + aString + TMP_STR_END;
+
+ rtl::Reference<XHatchList> xHatchList = new XHatchList( "", ""/*TODO?*/ );
+ xHatchList->Insert(std::make_unique<XHatchEntry>(mpHatchItem->GetHatchValue(), aTmpStr));
+ xHatchList->SetDirty( false );
+ const BitmapEx & aBmp = xHatchList->GetUiBitmap( 0 );
+
+ if( !aBmp.IsEmpty() )
+ {
+ ScopedVclPtrInstance< VirtualDevice > pVD;
+ const Size aBmpSize(aBmp.GetSizePixel());
+ pVD->SetOutputSizePixel(aBmpSize, false);
+ pVD->DrawBitmapEx(Point(), aBmp);
+ mpLbFillAttr->append("", xHatchList->GetHatch(0)->GetName(), *pVD);
+ mpLbFillAttr->set_active(mpLbFillAttr->get_count() - 1);
+ }
+ }
+ }
+ else
+ {
+ mpLbFillAttr->set_active(-1);
+ }
+ }
+ else
+ {
+ mpLbFillAttr->set_active(-1);
+ }
+ break;
+ }
+ case drawing::FillStyle_BITMAP:
+ {
+ mpLbFillAttr->show();
+ mpToolBoxColor->hide();
+ mxFillControl->Resize();
+
+ if(pSh)
+ {
+ mpLbFillAttr->set_sensitive(true);
+ mpLbFillAttr->clear();
+
+ if(mpBitmapItem && !mpBitmapItem->isPattern() && pSh->GetItem(SID_BITMAP_LIST))
+ {
+ SvxFillAttrBox::Fill(*mpLbFillAttr, pSh->GetItem(SID_BITMAP_LIST)->GetBitmapList());
+
+ const OUString aString(mpBitmapItem->GetName());
+
+ mpLbFillAttr->set_active_text(aString);
+
+ // Check if the entry is not in the list
+ if (mpLbFillAttr->get_active_text() != aString)
+ {
+ sal_Int32 nCount = mpLbFillAttr->get_count();
+ OUString aTmpStr;
+ if( nCount > 0 )
+ {
+ // Last entry gets tested against temporary entry
+ aTmpStr = mpLbFillAttr->get_text(nCount - 1);
+ if( aTmpStr.startsWith(TMP_STR_BEGIN) &&
+ aTmpStr.endsWith(TMP_STR_END) )
+ {
+ mpLbFillAttr->remove(nCount - 1);
+ }
+ }
+ aTmpStr = TMP_STR_BEGIN + aString + TMP_STR_END;
+
+ XBitmapListRef xBitmapList =
+ XPropertyList::AsBitmapList(
+ XPropertyList::CreatePropertyList(
+ XPropertyListType::Bitmap, "TmpList", ""/*TODO?*/));
+ xBitmapList->Insert(std::make_unique<XBitmapEntry>(mpBitmapItem->GetGraphicObject(), aTmpStr));
+ xBitmapList->SetDirty( false );
+ SvxFillAttrBox::Fill(*mpLbFillAttr, xBitmapList);
+ mpLbFillAttr->set_active(mpLbFillAttr->get_count() - 1);
+ }
+
+ }
+ else if (mpBitmapItem && mpBitmapItem->isPattern() && pSh->GetItem(SID_PATTERN_LIST))
+ {
+ mnLastXFS = sal::static_int_cast<sal_Int32>(PATTERN);
+ mpLbFillType->set_active(mnLastXFS);
+
+ SvxFillAttrBox::Fill(*mpLbFillAttr, pSh->GetItem(SID_PATTERN_LIST)->GetPatternList());
+ const OUString aString(mpBitmapItem->GetName());
+
+ mpLbFillAttr->set_active_text(aString);
+ }
+ else
+ {
+ mpLbFillAttr->set_active(-1);
+ }
+ }
+ else
+ {
+ mpLbFillAttr->set_active(-1);
+ }
+ break;
+ }
+ default:
+ OSL_ENSURE(false, "Non supported FillType (!)");
+ break;
+ }
+
+}
+
+VclPtr<InterimItemWindow> SvxFillToolBoxControl::CreateItemWindow(vcl::Window *pParent)
+{
+ if(GetSlotId() == SID_ATTR_FILL_STYLE)
+ {
+ mxFillControl.reset(VclPtr<FillControl>::Create(pParent, m_xFrame));
+
+ mpLbFillType = mxFillControl->mxLbFillType.get();
+ mpLbFillAttr = mxFillControl->mxLbFillAttr.get();
+ mpToolBoxColor = mxFillControl->mxToolBoxColor.get();
+
+ mpLbFillType->connect_changed(LINK(this,SvxFillToolBoxControl,SelectFillTypeHdl));
+ mpLbFillAttr->connect_changed(LINK(this,SvxFillToolBoxControl,SelectFillAttrHdl));
+
+
+ return mxFillControl;
+ }
+ return VclPtr<InterimItemWindow>();
+}
+
+FillControl::FillControl(vcl::Window* pParent, const css::uno::Reference<css::frame::XFrame>& rFrame)
+ : InterimItemWindow(pParent, "svx/ui/fillctrlbox.ui", "FillCtrlBox")
+ , mxLbFillType(m_xBuilder->weld_combo_box("type"))
+ , mxToolBoxColor(m_xBuilder->weld_toolbar("color"))
+ , mxColorDispatch(new ToolbarUnoDispatcher(*mxToolBoxColor, *m_xBuilder, rFrame))
+ , mxLbFillAttr(m_xBuilder->weld_combo_box("attr"))
+ , mnTypeCurPos(0)
+ , mnAttrCurPos(0)
+{
+ InitControlBase(mxLbFillType.get());
+
+ mxLbFillAttr->connect_key_press(LINK(this, FillControl, AttrKeyInputHdl));
+ mxLbFillType->connect_key_press(LINK(this, FillControl, TypeKeyInputHdl));
+ mxToolBoxColor->connect_key_press(LINK(this, FillControl, ColorKeyInputHdl));
+
+ mxLbFillType->connect_get_property_tree(LINK(this, FillControl, DumpAsPropertyTreeHdl));
+
+ mxLbFillType->connect_focus_in(LINK(this, FillControl, TypeFocusHdl));
+ mxLbFillAttr->connect_focus_in(LINK(this, FillControl, AttrFocusHdl));
+
+ SvxFillTypeBox::Fill(*mxLbFillType);
+
+ SetOptimalSize();
+}
+
+IMPL_STATIC_LINK(FillControl, DumpAsPropertyTreeHdl, tools::JsonWriter&, rJsonWriter, void)
+{
+ rJsonWriter.put("command", ".uno:FillStyle");
+}
+
+void FillControl::ReleaseFocus_Impl()
+{
+ SfxViewShell* pCurSh = SfxViewShell::Current();
+ if (pCurSh)
+ {
+ vcl::Window* pShellWnd = pCurSh->GetWindow();
+ if (pShellWnd)
+ pShellWnd->GrabFocus();
+ }
+}
+
+IMPL_LINK(FillControl, TypeKeyInputHdl, const KeyEvent&, rKEvt, bool)
+{
+ const vcl::KeyCode& rKeyCode = rKEvt.GetKeyCode();
+ sal_uInt16 nCode = rKeyCode.GetCode();
+
+ if (nCode == KEY_ESCAPE)
+ {
+ mxLbFillType->set_active(mnTypeCurPos);
+ ReleaseFocus_Impl();
+ return true;
+ }
+
+ if (nCode != KEY_TAB)
+ return false;
+ if (rKeyCode.IsShift())
+ return ChildKeyInput(rKEvt);
+ if (mxLbFillAttr->get_visible() && !mxLbFillAttr->get_sensitive())
+ return ChildKeyInput(rKEvt);
+ return false;
+}
+
+IMPL_LINK_NOARG(FillControl, TypeFocusHdl, weld::Widget&, void)
+{
+ mnTypeCurPos = mxLbFillType->get_active();
+}
+
+IMPL_LINK_NOARG(FillControl, AttrFocusHdl, weld::Widget&, void)
+{
+ mnAttrCurPos = mxLbFillAttr->get_active();
+}
+
+IMPL_LINK(FillControl, AttrKeyInputHdl, const KeyEvent&, rKEvt, bool)
+{
+ const vcl::KeyCode& rKeyCode = rKEvt.GetKeyCode();
+ sal_uInt16 nCode = rKeyCode.GetCode();
+
+ if (nCode == KEY_ESCAPE)
+ {
+ mxLbFillAttr->set_active(mnAttrCurPos);
+ ReleaseFocus_Impl();
+ return true;
+ }
+
+ return ChildKeyInput(rKEvt);
+}
+
+IMPL_LINK(FillControl, ColorKeyInputHdl, const KeyEvent&, rKEvt, bool)
+{
+ return ChildKeyInput(rKEvt);
+}
+
+FillControl::~FillControl()
+{
+ disposeOnce();
+}
+
+void FillControl::dispose()
+{
+ mxLbFillAttr.reset();
+ mxColorDispatch.reset();
+ mxToolBoxColor.reset();
+ mxLbFillType.reset();
+ InterimItemWindow::dispose();
+}
+
+IMPL_LINK_NOARG(SvxFillToolBoxControl, SelectFillTypeHdl, weld::ComboBox&, void)
+{
+ sal_Int32 nXFS = mpLbFillType->get_active();
+
+ if(mnLastXFS == nXFS)
+ return;
+
+ eFillStyle eXFS = static_cast<eFillStyle>(nXFS);
+ mpLbFillAttr->clear();
+ SfxObjectShell* pSh = SfxObjectShell::Current();
+ const XFillStyleItem aXFillStyleItem(toCssFillStyle(eXFS));
+
+ // #i122676# Do no longer trigger two Execute calls, one for SID_ATTR_FILL_STYLE
+ // and one for setting the fill attribute itself, but add two SfxPoolItems to the
+ // call to get just one action at the SdrObject and to create only one Undo action, too.
+ // Checked that this works in all apps.
+ switch( eXFS )
+ {
+ default:
+ case NONE:
+ {
+ mpLbFillAttr->show();
+ mpToolBoxColor->hide();
+ mpLbFillAttr->set_sensitive(false);
+ if (pSh)
+ {
+ // #i122676# need to call a single SID_ATTR_FILL_STYLE change
+ pSh->GetDispatcher()->ExecuteList(
+ SID_ATTR_FILL_STYLE, SfxCallMode::RECORD,
+ { &aXFillStyleItem });
+ }
+ break;
+ }
+ case SOLID:
+ {
+ mpLbFillAttr->hide();
+ mpToolBoxColor->show();
+ if (pSh)
+ {
+ const ::Color aColor = mpColorItem ? mpColorItem->GetColorValue() : COL_AUTO;
+ const XFillColorItem aXFillColorItem( "", aColor );
+
+ // #i122676# change FillStyle and Color in one call
+ pSh->GetDispatcher()->ExecuteList(
+ SID_ATTR_FILL_COLOR, SfxCallMode::RECORD,
+ { &aXFillColorItem, &aXFillStyleItem });
+ }
+ break;
+ }
+ case GRADIENT:
+ {
+ mpLbFillAttr->show();
+ mpToolBoxColor->hide();
+
+ if(pSh && pSh->GetItem(SID_GRADIENT_LIST))
+ {
+ if(!mpLbFillAttr->get_count())
+ {
+ mpLbFillAttr->set_sensitive(true);
+ mpLbFillAttr->clear();
+ SvxFillAttrBox::Fill(*mpLbFillAttr, pSh->GetItem(SID_GRADIENT_LIST)->GetGradientList());
+ }
+
+ if (mnLastPosGradient != -1)
+ {
+ const SvxGradientListItem * pItem = pSh->GetItem(SID_GRADIENT_LIST);
+
+ if(mnLastPosGradient < pItem->GetGradientList()->Count())
+ {
+ const XGradient aGradient = pItem->GetGradientList()->GetGradient(mnLastPosGradient)->GetGradient();
+ const XFillGradientItem aXFillGradientItem(mpLbFillAttr->get_text(mnLastPosGradient), aGradient);
+
+ // #i122676# change FillStyle and Gradient in one call
+ pSh->GetDispatcher()->ExecuteList(
+ SID_ATTR_FILL_GRADIENT, SfxCallMode::RECORD,
+ { &aXFillGradientItem, &aXFillStyleItem });
+ mpLbFillAttr->set_active(mnLastPosGradient);
+ }
+ }
+ }
+ else
+ {
+ mpLbFillAttr->set_sensitive(false);
+ }
+ break;
+ }
+ case HATCH:
+ {
+ mpLbFillAttr->show();
+ mpToolBoxColor->hide();
+
+ if(pSh && pSh->GetItem(SID_HATCH_LIST))
+ {
+ if(!mpLbFillAttr->get_count())
+ {
+ mpLbFillAttr->set_sensitive(true);
+ mpLbFillAttr->clear();
+ SvxFillAttrBox::Fill(*mpLbFillAttr, pSh->GetItem(SID_HATCH_LIST)->GetHatchList());
+ }
+
+ if (mnLastPosHatch != -1)
+ {
+ const SvxHatchListItem * pItem = pSh->GetItem(SID_HATCH_LIST);
+
+ if(mnLastPosHatch < pItem->GetHatchList()->Count())
+ {
+ const XHatch aHatch = pItem->GetHatchList()->GetHatch(mnLastPosHatch)->GetHatch();
+ const XFillHatchItem aXFillHatchItem(mpLbFillAttr->get_active_text(), aHatch);
+
+ // #i122676# change FillStyle and Hatch in one call
+ pSh->GetDispatcher()->ExecuteList(
+ SID_ATTR_FILL_HATCH, SfxCallMode::RECORD,
+ { &aXFillHatchItem, &aXFillStyleItem });
+ mpLbFillAttr->set_active(mnLastPosHatch);
+ }
+ }
+ }
+ else
+ {
+ mpLbFillAttr->set_sensitive(false);
+ }
+ break;
+ }
+ case BITMAP:
+ {
+ mpLbFillAttr->show();
+ mpToolBoxColor->hide();
+
+ if(pSh && pSh->GetItem(SID_BITMAP_LIST))
+ {
+ if(!mpLbFillAttr->get_count())
+ {
+ mpLbFillAttr->set_sensitive(true);
+ mpLbFillAttr->clear();
+ SvxFillAttrBox::Fill(*mpLbFillAttr, pSh->GetItem(SID_BITMAP_LIST)->GetBitmapList());
+ }
+
+ if (mnLastPosBitmap != -1)
+ {
+ const SvxBitmapListItem * pItem = pSh->GetItem(SID_BITMAP_LIST);
+
+ if(mnLastPosBitmap < pItem->GetBitmapList()->Count())
+ {
+ const XBitmapEntry* pXBitmapEntry = pItem->GetBitmapList()->GetBitmap(mnLastPosBitmap);
+ const XFillBitmapItem aXFillBitmapItem(mpLbFillAttr->get_active_text(), pXBitmapEntry->GetGraphicObject());
+
+ // #i122676# change FillStyle and Bitmap in one call
+ pSh->GetDispatcher()->ExecuteList(
+ SID_ATTR_FILL_BITMAP, SfxCallMode::RECORD,
+ { &aXFillBitmapItem, &aXFillStyleItem });
+ mpLbFillAttr->set_active(mnLastPosBitmap);
+ }
+ }
+ }
+ else
+ {
+ mpLbFillAttr->set_sensitive(false);
+ }
+ break;
+ }
+ case PATTERN:
+ {
+ mpLbFillAttr->show();
+ mpToolBoxColor->hide();
+
+ if(pSh && pSh->GetItem(SID_PATTERN_LIST))
+ {
+ if(!mpLbFillAttr->get_count())
+ {
+ mpLbFillAttr->set_sensitive(true);
+ mpLbFillAttr->clear();
+ SvxFillAttrBox::Fill(*mpLbFillAttr, pSh->GetItem(SID_PATTERN_LIST)->GetPatternList());
+ }
+
+ if (mnLastPosPattern != -1)
+ {
+ const SvxPatternListItem * pItem = pSh->GetItem(SID_PATTERN_LIST);
+
+ if(mnLastPosPattern < pItem->GetPatternList()->Count())
+ {
+ const XBitmapEntry* pXBitmapEntry = pItem->GetPatternList()->GetBitmap(mnLastPosPattern);
+ const XFillBitmapItem aXFillBitmapItem(mpLbFillAttr->get_active_text(), pXBitmapEntry->GetGraphicObject());
+
+ // #i122676# change FillStyle and Bitmap in one call
+ pSh->GetDispatcher()->ExecuteList(
+ SID_ATTR_FILL_BITMAP, SfxCallMode::RECORD,
+ { &aXFillBitmapItem, &aXFillStyleItem });
+ mpLbFillAttr->set_active(mnLastPosPattern);
+ }
+ }
+ }
+ else
+ {
+ mpLbFillAttr->set_sensitive(false);
+ }
+ break;
+ }
+
+ }
+
+ mnLastXFS = nXFS;
+
+ mxFillControl->Resize();
+}
+
+IMPL_LINK_NOARG(SvxFillToolBoxControl, SelectFillAttrHdl, weld::ComboBox&, void)
+{
+ sal_Int32 nXFS = mpLbFillType->get_active();
+ eFillStyle eXFS = static_cast<eFillStyle>(nXFS);
+
+ const XFillStyleItem aXFillStyleItem(toCssFillStyle(eXFS));
+ SfxObjectShell* pSh = SfxObjectShell::Current();
+
+ // #i122676# dependent from bFillStyleChange, do execute a single or two
+ // changes in one Execute call
+ const bool bFillStyleChange(mnLastXFS != nXFS);
+
+ switch (eXFS)
+ {
+ case SOLID:
+ {
+ if (bFillStyleChange && pSh)
+ {
+ // #i122676# Single FillStyle change call needed here
+ pSh->GetDispatcher()->ExecuteList(
+ SID_ATTR_FILL_STYLE, SfxCallMode::RECORD,
+ { &aXFillStyleItem });
+ }
+ break;
+ }
+ case GRADIENT:
+ {
+ sal_Int32 nPos = mpLbFillAttr->get_active();
+
+ if (nPos == -1)
+ {
+ nPos = mnLastPosGradient;
+ }
+
+ if (nPos != -1 && pSh && pSh->GetItem(SID_GRADIENT_LIST))
+ {
+ const SvxGradientListItem * pItem = pSh->GetItem(SID_GRADIENT_LIST);
+
+ if(nPos < pItem->GetGradientList()->Count())
+ {
+ const XGradient aGradient = pItem->GetGradientList()->GetGradient(nPos)->GetGradient();
+ const XFillGradientItem aXFillGradientItem(mpLbFillAttr->get_active_text(), aGradient);
+
+ // #i122676# Change FillStyle and Gradient in one call
+ pSh->GetDispatcher()->ExecuteList(
+ SID_ATTR_FILL_GRADIENT, SfxCallMode::RECORD,
+ bFillStyleChange
+ ? std::initializer_list<SfxPoolItem const*>{ &aXFillGradientItem, &aXFillStyleItem }
+ : std::initializer_list<SfxPoolItem const*>{ &aXFillGradientItem });
+ }
+ }
+
+ if (nPos != -1)
+ {
+ mnLastPosGradient = nPos;
+ }
+ break;
+ }
+ case HATCH:
+ {
+ sal_Int32 nPos = mpLbFillAttr->get_active();
+
+ if (nPos == -1)
+ {
+ nPos = mnLastPosHatch;
+ }
+
+ if (nPos != -1 && pSh && pSh->GetItem(SID_HATCH_LIST))
+ {
+ const SvxHatchListItem * pItem = pSh->GetItem(SID_HATCH_LIST);
+
+ if(nPos < pItem->GetHatchList()->Count())
+ {
+ const XHatch aHatch = pItem->GetHatchList()->GetHatch(nPos)->GetHatch();
+ const XFillHatchItem aXFillHatchItem( mpLbFillAttr->get_active_text(), aHatch);
+
+ // #i122676# Change FillStyle and Hatch in one call
+ pSh->GetDispatcher()->ExecuteList(
+ SID_ATTR_FILL_HATCH, SfxCallMode::RECORD,
+ bFillStyleChange
+ ? std::initializer_list<SfxPoolItem const*>{ &aXFillHatchItem, &aXFillStyleItem }
+ : std::initializer_list<SfxPoolItem const*>{ &aXFillHatchItem });
+ }
+ }
+
+ if (nPos != -1)
+ {
+ mnLastPosHatch = nPos;
+ }
+ break;
+ }
+ case BITMAP:
+ {
+ sal_Int32 nPos = mpLbFillAttr->get_active();
+
+ if (nPos == -1)
+ {
+ nPos = mnLastPosBitmap;
+ }
+
+ if (nPos != -1 && pSh && pSh->GetItem(SID_BITMAP_LIST))
+ {
+ const SvxBitmapListItem * pItem = pSh->GetItem(SID_BITMAP_LIST);
+
+ if(nPos < pItem->GetBitmapList()->Count())
+ {
+ const XBitmapEntry* pXBitmapEntry = pItem->GetBitmapList()->GetBitmap(nPos);
+ const XFillBitmapItem aXFillBitmapItem(mpLbFillAttr->get_active_text(), pXBitmapEntry->GetGraphicObject());
+
+ // #i122676# Change FillStyle and Bitmap in one call
+ pSh->GetDispatcher()->ExecuteList(
+ SID_ATTR_FILL_BITMAP, SfxCallMode::RECORD,
+ bFillStyleChange
+ ? std::initializer_list<SfxPoolItem const*>{ &aXFillBitmapItem, &aXFillStyleItem }
+ : std::initializer_list<SfxPoolItem const*>{ &aXFillBitmapItem });
+ }
+ }
+
+ if (nPos != -1)
+ {
+ mnLastPosBitmap = nPos;
+ }
+ break;
+ }
+ case PATTERN:
+ {
+ sal_Int32 nPos = mpLbFillAttr->get_active();
+
+ if (nPos == -1)
+ {
+ nPos = mnLastPosPattern;
+ }
+
+ if (nPos != -1 && pSh && pSh->GetItem(SID_PATTERN_LIST))
+ {
+ const SvxPatternListItem * pItem = pSh->GetItem(SID_PATTERN_LIST);
+
+ if(nPos < pItem->GetPatternList()->Count())
+ {
+ const XBitmapEntry* pXBitmapEntry = pItem->GetPatternList()->GetBitmap(nPos);
+ const XFillBitmapItem aXFillBitmapItem(mpLbFillAttr->get_active_text(), pXBitmapEntry->GetGraphicObject());
+
+ // #i122676# Change FillStyle and Bitmap in one call
+ pSh->GetDispatcher()->ExecuteList(
+ SID_ATTR_FILL_BITMAP, SfxCallMode::RECORD,
+ bFillStyleChange
+ ? std::initializer_list<SfxPoolItem const*>{ &aXFillBitmapItem, &aXFillStyleItem }
+ : std::initializer_list<SfxPoolItem const*>{ &aXFillBitmapItem });
+ }
+ }
+
+ if (nPos != -1)
+ {
+ mnLastPosPattern = nPos;
+ }
+ break;
+ }
+
+ default: break;
+ }
+}
+
+void FillControl::SetOptimalSize()
+{
+ Size aSize(mxLbFillType->get_preferred_size());
+ Size aFirstSize(mxToolBoxColor->get_preferred_size());
+ auto nWidth = std::max(aFirstSize.Width(), LogicToPixel(Size(55, 0), MapMode(MapUnit::MapAppFont)).Width());
+ auto nHeight = std::max(aSize.Height(), aFirstSize.Height());
+ mxToolBoxColor->set_size_request(nWidth, -1);
+ mxLbFillAttr->set_size_request(42, -1); //something narrow so the toolbar sets the overall size of this column
+ SetSizePixel(Size(m_xContainer->get_preferred_size().Width(), nHeight));
+}
+
+void FillControl::DataChanged(const DataChangedEvent& rDCEvt)
+{
+ if((rDCEvt.GetType() == DataChangedEventType::SETTINGS) &&
+ (rDCEvt.GetFlags() & AllSettingsFlags::STYLE))
+ {
+ SetOptimalSize();
+ }
+ InterimItemWindow::DataChanged(rDCEvt);
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/tbxctrls/fontworkgallery.cxx b/svx/source/tbxctrls/fontworkgallery.cxx
new file mode 100644
index 000000000..802cc1f4f
--- /dev/null
+++ b/svx/source/tbxctrls/fontworkgallery.cxx
@@ -0,0 +1,799 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * 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/WritingMode.hpp>
+
+#include <comphelper/lok.hxx>
+#include <comphelper/propertyvalue.hxx>
+
+#include <vcl/toolbox.hxx>
+#include <vcl/virdev.hxx>
+
+#include <sfx2/viewsh.hxx>
+
+#include <svl/itempool.hxx>
+
+#include <svtools/toolbarmenu.hxx>
+#include <svtools/popupwindowcontroller.hxx>
+
+#include <svx/fmmodel.hxx>
+#include <svx/svdpage.hxx>
+#include <svx/svdobj.hxx>
+#include <svx/svdview.hxx>
+
+#include <svx/gallery.hxx>
+#include <svx/fontworkgallery.hxx>
+
+#include <tools/UnitConversion.hxx>
+
+#include <algorithm>
+#include <memory>
+
+using namespace ::com::sun::star::uno;
+using namespace ::com::sun::star::lang;
+using namespace ::com::sun::star::beans;
+
+namespace svx
+{
+
+FontWorkGalleryDialog::FontWorkGalleryDialog(weld::Window* pParent, SdrView& rSdrView)
+ : GenericDialogController(pParent, "svx/ui/fontworkgallerydialog.ui", "FontworkGalleryDialog")
+ , mnThemeId(0xffff)
+ , mrSdrView(rSdrView)
+ , mbInsertIntoPage(true)
+ , mpSdrObject(nullptr)
+ , mpDestModel(nullptr)
+ , maCtlFavorites(m_xBuilder->weld_icon_view("ctlFavoriteswin"))
+ , mxOKButton(m_xBuilder->weld_button("ok"))
+{
+ Size aSize(530, 400);
+ maCtlFavorites->set_size_request(aSize.Width(), aSize.Height());
+
+ maCtlFavorites->connect_item_activated( LINK( this, FontWorkGalleryDialog, DoubleClickFavoriteHdl ) );
+ mxOKButton->connect_clicked(LINK(this, FontWorkGalleryDialog, ClickOKHdl));
+
+ initFavorites( GALLERY_THEME_FONTWORK );
+ fillFavorites( GALLERY_THEME_FONTWORK );
+}
+
+FontWorkGalleryDialog::~FontWorkGalleryDialog()
+{
+}
+
+void FontWorkGalleryDialog::initFavorites(sal_uInt16 nThemeId)
+{
+ // the favorites are read via the gallery
+ sal_uInt32 nFavCount = GalleryExplorer::GetSdrObjCount( nThemeId );
+
+ // lock gallery theme
+ GalleryExplorer::BeginLocking(nThemeId);
+
+ sal_uInt32 nModelPos;
+ FmFormModel *pModel = nullptr;
+
+ for( nModelPos = 0; nModelPos < nFavCount; nModelPos++ )
+ {
+ BitmapEx aThumb;
+
+ if (GalleryExplorer::GetSdrObj(nThemeId, nModelPos, pModel, &aThumb) && !aThumb.IsEmpty())
+ {
+ VclPtr< VirtualDevice > pVDev = VclPtr<VirtualDevice>::Create();
+ const Point aNull(0, 0);
+
+ if (pVDev->GetDPIScaleFactor() > 1)
+ aThumb.Scale(pVDev->GetDPIScaleFactor(), pVDev->GetDPIScaleFactor());
+
+ const Size aSize(aThumb.GetSizePixel());
+
+ pVDev->SetOutputSizePixel(aSize);
+
+ static const sal_uInt32 nLen(8);
+ static const Color aW(COL_WHITE);
+ static const Color aG(0xef, 0xef, 0xef);
+
+ pVDev->DrawCheckered(aNull, aSize, nLen, aW, aG);
+
+ pVDev->DrawBitmapEx(aNull, aThumb);
+ maFavoritesHorizontal.emplace_back(pVDev);
+ }
+ }
+
+ // release gallery theme
+ GalleryExplorer::EndLocking(nThemeId);
+}
+
+void FontWorkGalleryDialog::fillFavorites(sal_uInt16 nThemeId)
+{
+ mnThemeId = nThemeId;
+
+ auto nFavCount = maFavoritesHorizontal.size();
+
+ maCtlFavorites->clear();
+
+ for( size_t nFavorite = 1; nFavorite <= nFavCount; nFavorite++ )
+ {
+ OUString sId = OUString::number(static_cast<sal_uInt16>(nFavorite));
+ maCtlFavorites->insert(-1, nullptr, &sId, maFavoritesHorizontal[nFavorite - 1], nullptr);
+ }
+
+ if (maCtlFavorites->n_children())
+ maCtlFavorites->select(0);
+}
+
+void FontWorkGalleryDialog::SetSdrObjectRef( SdrModel* pModel)
+{
+ mbInsertIntoPage = false;
+ mpDestModel = pModel;
+}
+
+void FontWorkGalleryDialog::insertSelectedFontwork()
+{
+ OUString sItemId = maCtlFavorites->get_selected_id();
+ if (sItemId.isEmpty())
+ return;
+
+ sal_Int32 nItemId = sItemId.toInt32();
+
+ if (nItemId == 0)
+ return;
+
+ FmFormModel aModel;
+ aModel.GetItemPool().FreezeIdRanges();
+
+ if( !GalleryExplorer::GetSdrObj( mnThemeId, nItemId-1, &aModel ) )
+ return;
+
+ SdrPage* pPage = aModel.GetPage(0);
+ if( !(pPage && pPage->GetObjCount()) )
+ return;
+
+ // tdf#116993 Calc uses a 'special' mode for this dialog in being the
+ // only caller of ::SetSdrObjectRef. Only in that case mpDestModel seems
+ // to be the correct target SdrModel.
+ // If this is not used, the correct SdrModel seems to be the one from
+ // the mrSdrView that is used to insert (InsertObjectAtView below) the
+ // cloned SdrObject.
+ const bool bUseSpecialCalcMode(!mbInsertIntoPage && nullptr != mpDestModel);
+
+ // center shape on current view
+ OutputDevice* pOutDev(mrSdrView.GetFirstOutputDevice());
+
+ if (!pOutDev)
+ return;
+
+ // Clone directly to target SdrModel (may be different due to user/caller (!))
+ SdrObject* pNewObject(
+ pPage->GetObj(0)->CloneSdrObject(
+ bUseSpecialCalcMode ? *mpDestModel : mrSdrView.getSdrModelFromSdrView()));
+
+ pNewObject->MakeNameUnique();
+
+ // tdf#117629
+ // Since the 'old' ::CloneSdrObject also copies the SdrPage* the
+ // SdrObject::getUnoShape() *will* create the wrong UNO API object
+ // early. This IS one of the reasons I do change these things - this
+ // error does not happen with my next change I am working on already
+ // ARGH! For now, reset the SdrPage* to nullptr.
+ // What sense does it have to copy the SdrPage* of the original SdrObject ?!?
+ // TTTT: This also *might* be the hidden reason for the strange code at the
+ // end of SdrObject::SetPage that tries to delete the SvxShape under some
+ // circumstances...
+ // pNewObject->SetPage(nullptr);
+
+ tools::Rectangle aObjRect( pNewObject->GetLogicRect() );
+ Point aPagePos;
+ Size aFontworkSize = aObjRect.GetSize();
+
+ if (comphelper::LibreOfficeKit::isActive())
+ {
+ SfxViewShell* pViewShell = SfxViewShell::Current();
+
+ aPagePos = pViewShell->getLOKVisibleArea().Center();
+
+ aPagePos.setX(convertTwipToMm100(aPagePos.X()));
+ aPagePos.setY(convertTwipToMm100(aPagePos.Y()));
+
+ sal_Int32 nLOKViewWidth = 0.8 * convertTwipToMm100(pViewShell->getLOKVisibleArea().getWidth());
+ if (aFontworkSize.getWidth() > nLOKViewWidth)
+ {
+ double fScale = static_cast<double>(aFontworkSize.getWidth()) / nLOKViewWidth;
+ aFontworkSize.setWidth(aFontworkSize.getWidth() / fScale);
+ aFontworkSize.setHeight(aFontworkSize.getHeight() / fScale);
+ }
+ }
+ else
+ {
+ Size aSize = pOutDev->GetOutputSizePixel();
+ tools::Rectangle aPixelVisRect(Point(0,0), aSize);
+ tools::Rectangle aVisArea = pOutDev->PixelToLogic(aPixelVisRect);
+
+ aPagePos = aVisArea.Center();
+ }
+
+ if (aPagePos.getX() > aFontworkSize.getWidth() / 2)
+ aPagePos.AdjustX( -(aFontworkSize.getWidth() / 2) );
+ if (aPagePos.getY() > aFontworkSize.getHeight() / 2)
+ aPagePos.AdjustY( -(aFontworkSize.getHeight() / 2) );
+
+ tools::Rectangle aNewObjectRectangle(aPagePos, aFontworkSize);
+ pNewObject->SetLogicRect(aNewObjectRectangle);
+
+ if (bUseSpecialCalcMode)
+ {
+ mpSdrObject = pNewObject;
+ }
+ else
+ {
+ SdrPageView* pPV(mrSdrView.GetSdrPageView());
+
+ if (nullptr != pPV)
+ {
+ mrSdrView.InsertObjectAtView( pNewObject, *pPV );
+ }
+ else
+ {
+ // tdf#116993 no target -> delete clone
+ SdrObject::Free(pNewObject);
+ }
+ }
+}
+
+IMPL_LINK_NOARG(FontWorkGalleryDialog, ClickOKHdl, weld::Button&, void)
+{
+ insertSelectedFontwork();
+ m_xDialog->response(RET_OK);
+}
+
+IMPL_LINK_NOARG(FontWorkGalleryDialog, DoubleClickFavoriteHdl, weld::IconView&, bool)
+{
+ insertSelectedFontwork();
+ m_xDialog->response(RET_OK);
+ return true;
+}
+
+namespace {
+
+class FontworkAlignmentWindow final : public WeldToolbarPopup
+{
+public:
+ FontworkAlignmentWindow(svt::PopupWindowController* pControl, weld::Widget* pParentWindow);
+ virtual void GrabFocus() override
+ {
+ mxLeft->grab_focus();
+ }
+ virtual void statusChanged( const css::frame::FeatureStateEvent& Event ) override;
+
+private:
+ rtl::Reference<svt::PopupWindowController> mxControl;
+ std::unique_ptr<weld::RadioButton> mxLeft;
+ std::unique_ptr<weld::RadioButton> mxCenter;
+ std::unique_ptr<weld::RadioButton> mxRight;
+ std::unique_ptr<weld::RadioButton> mxStretch;
+ bool mbSettingValue;
+
+ DECL_LINK( SelectHdl, weld::Toggleable&, void );
+
+ void implSetAlignment( int nAlignmentMode, bool bEnabled );
+};
+
+}
+
+constexpr OUStringLiteral gsFontworkAlignment(u".uno:FontworkAlignment");
+
+FontworkAlignmentWindow::FontworkAlignmentWindow(svt::PopupWindowController* pControl, weld::Widget* pParent)
+ : WeldToolbarPopup(pControl->getFrameInterface(), pParent, "svx/ui/fontworkalignmentcontrol.ui", "FontworkAlignmentControl")
+ , mxControl(pControl)
+ , mxLeft(m_xBuilder->weld_radio_button("left"))
+ , mxCenter(m_xBuilder->weld_radio_button("center"))
+ , mxRight(m_xBuilder->weld_radio_button("right"))
+ , mxStretch(m_xBuilder->weld_radio_button("stretch"))
+ , mbSettingValue(false)
+{
+ mxLeft->connect_toggled(LINK(this, FontworkAlignmentWindow, SelectHdl));
+ mxCenter->connect_toggled(LINK(this, FontworkAlignmentWindow, SelectHdl));
+ mxRight->connect_toggled(LINK(this, FontworkAlignmentWindow, SelectHdl));
+ mxStretch->connect_toggled(LINK(this, FontworkAlignmentWindow, SelectHdl));
+
+ AddStatusListener( gsFontworkAlignment );
+}
+
+void FontworkAlignmentWindow::implSetAlignment( int nSurface, bool bEnabled )
+{
+ bool bSettingValue = mbSettingValue;
+ mbSettingValue = true;
+ mxLeft->set_active(nSurface == 0 && bEnabled);
+ mxLeft->set_sensitive(bEnabled);
+ mxCenter->set_active(nSurface == 1 && bEnabled);
+ mxCenter->set_sensitive(bEnabled);
+ mxRight->set_active(nSurface == 2 && bEnabled);
+ mxRight->set_sensitive(bEnabled);
+ //Refer https://bugs.documentfoundation.org/show_bug.cgi?id=145092 for why following lines are commented
+ //mxWord->set_active(nSurface == 3 && bEnabled);
+ //mxWord->set_sensitive(bEnabled);
+ mxStretch->set_active(nSurface == 4 && bEnabled);
+ mxStretch->set_sensitive(bEnabled);
+ mbSettingValue = bSettingValue;
+}
+
+void FontworkAlignmentWindow::statusChanged( const css::frame::FeatureStateEvent& Event )
+{
+ if( Event.FeatureURL.Main != gsFontworkAlignment )
+ return;
+
+ if( !Event.IsEnabled )
+ {
+ implSetAlignment( 0, false );
+ }
+ else
+ {
+ sal_Int32 nValue = 0;
+ if( Event.State >>= nValue )
+ implSetAlignment( nValue, true );
+ }
+}
+
+IMPL_LINK(FontworkAlignmentWindow, SelectHdl, weld::Toggleable&, rButton, void)
+{
+ if (mbSettingValue || !rButton.get_active())
+ return;
+
+ sal_Int32 nAlignment;
+ if (mxLeft->get_active())
+ nAlignment = 0;
+ else if (mxCenter->get_active())
+ nAlignment = 1;
+ else if (mxRight->get_active())
+ nAlignment = 2;
+ //Refer https://bugs.documentfoundation.org/show_bug.cgi?id=145092 for why following lines are commented
+ //else if (mxWord->get_active())
+ // nAlignment = 3;
+ else
+ nAlignment = 4;
+
+ Sequence< PropertyValue > aArgs{ comphelper::makePropertyValue(
+ OUString(gsFontworkAlignment).copy(5), nAlignment) };
+
+ mxControl->dispatchCommand( gsFontworkAlignment, aArgs );
+
+ implSetAlignment( nAlignment, true );
+
+ mxControl->EndPopupMode();
+}
+
+namespace {
+
+class FontworkAlignmentControl : public svt::PopupWindowController
+{
+public:
+ explicit FontworkAlignmentControl( const css::uno::Reference< css::uno::XComponentContext >& rxContext );
+
+ virtual std::unique_ptr<WeldToolbarPopup> weldPopupWindow() override;
+ virtual VclPtr<vcl::Window> createVclPopupWindow( vcl::Window* pParent ) override;
+
+ // XInitialization
+ virtual void SAL_CALL initialize( const css::uno::Sequence< css::uno::Any >& aArguments ) override;
+
+ // XServiceInfo
+ virtual OUString SAL_CALL getImplementationName() override;
+ virtual css::uno::Sequence< OUString > SAL_CALL getSupportedServiceNames() override;
+};
+
+}
+
+FontworkAlignmentControl::FontworkAlignmentControl( const Reference< XComponentContext >& rxContext )
+: svt::PopupWindowController( rxContext, Reference< css::frame::XFrame >(), ".uno:FontworkAlignment" )
+{
+}
+
+std::unique_ptr<WeldToolbarPopup> FontworkAlignmentControl::weldPopupWindow()
+{
+ return std::make_unique<FontworkAlignmentWindow>(this, m_pToolbar);
+}
+
+VclPtr<vcl::Window> FontworkAlignmentControl::createVclPopupWindow( vcl::Window* pParent )
+{
+ mxInterimPopover = VclPtr<InterimToolbarPopup>::Create(getFrameInterface(), pParent,
+ std::make_unique<FontworkAlignmentWindow>(this, pParent->GetFrameWeld()));
+
+ mxInterimPopover->Show();
+
+ return mxInterimPopover;
+}
+
+// XInitialization
+void SAL_CALL FontworkAlignmentControl::initialize( const css::uno::Sequence< css::uno::Any >& aArguments )
+{
+ svt::PopupWindowController::initialize( aArguments );
+
+ if (m_pToolbar)
+ {
+ mxPopoverContainer.reset(new ToolbarPopupContainer(m_pToolbar));
+ m_pToolbar->set_item_popover(m_aCommandURL.toUtf8(), mxPopoverContainer->getTopLevel());
+ }
+
+ ToolBox* pToolBox = nullptr;
+ ToolBoxItemId nId;
+ if ( getToolboxId( nId, &pToolBox ) )
+ pToolBox->SetItemBits( nId, pToolBox->GetItemBits( nId ) | ToolBoxItemBits::DROPDOWNONLY );
+}
+
+// XServiceInfo
+
+
+OUString FontworkAlignmentControl::getImplementationName()
+{
+ return "com.sun.star.comp.svx.FontworkAlignmentController";
+}
+
+
+Sequence< OUString > FontworkAlignmentControl::getSupportedServiceNames()
+{
+ return { "com.sun.star.frame.ToolbarController" };
+}
+
+
+extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface*
+com_sun_star_comp_svx_FontworkAlignmentControl_get_implementation(
+ css::uno::XComponentContext* xContext,
+ css::uno::Sequence<css::uno::Any> const &)
+{
+ return cppu::acquire(new FontworkAlignmentControl(xContext));
+}
+
+namespace {
+
+class FontworkCharacterSpacingWindow final : public WeldToolbarPopup
+{
+public:
+ FontworkCharacterSpacingWindow(svt::PopupWindowController* pControl, weld::Widget* pParentWindow);
+ virtual void GrabFocus() override;
+
+ virtual void statusChanged( const css::frame::FeatureStateEvent& Event ) override;
+private:
+ rtl::Reference<svt::PopupWindowController> mxControl;
+ std::unique_ptr<weld::RadioButton> mxVeryTight;
+ std::unique_ptr<weld::RadioButton> mxTight;
+ std::unique_ptr<weld::RadioButton> mxNormal;
+ std::unique_ptr<weld::RadioButton> mxLoose;
+ std::unique_ptr<weld::RadioButton> mxVeryLoose;
+ std::unique_ptr<weld::RadioButton> mxCustom;
+ std::unique_ptr<weld::CheckButton> mxKernPairs;
+ sal_Int32 mnCharacterSpacing;
+ bool mbSettingValue;
+ bool mbCommandDispatched;
+
+ DECL_LINK( KernSelectHdl, weld::Toggleable&, void );
+ DECL_LINK( SelectHdl, weld::Toggleable&, void );
+ DECL_LINK( MouseReleaseHdl, const MouseEvent&, bool );
+
+ void implSetCharacterSpacing( sal_Int32 nCharacterSpacing, bool bEnabled );
+ void implSetKernCharacterPairs(bool bKernOnOff, bool bEnabled);
+ void DispatchSpacingDialog();
+};
+
+}
+
+constexpr OUStringLiteral gsFontworkCharacterSpacing(u".uno:FontworkCharacterSpacing");
+constexpr OUStringLiteral gsFontworkKernCharacterPairs(u".uno:FontworkKernCharacterPairs");
+
+FontworkCharacterSpacingWindow::FontworkCharacterSpacingWindow(svt::PopupWindowController* pControl, weld::Widget* pParent)
+ : WeldToolbarPopup(pControl->getFrameInterface(), pParent, "svx/ui/fontworkcharacterspacingcontrol.ui", "FontworkCharacterSpacingControl")
+ , mxControl(pControl)
+ , mxVeryTight(m_xBuilder->weld_radio_button("verytight"))
+ , mxTight(m_xBuilder->weld_radio_button("tight"))
+ , mxNormal(m_xBuilder->weld_radio_button("normal"))
+ , mxLoose(m_xBuilder->weld_radio_button("loose"))
+ , mxVeryLoose(m_xBuilder->weld_radio_button("veryloose"))
+ , mxCustom(m_xBuilder->weld_radio_button("custom"))
+ , mxKernPairs(m_xBuilder->weld_check_button("kernpairs"))
+ , mnCharacterSpacing(0)
+ , mbSettingValue(false)
+ , mbCommandDispatched(false)
+{
+ mxVeryTight->connect_toggled(LINK(this, FontworkCharacterSpacingWindow, SelectHdl));
+ mxTight->connect_toggled(LINK(this, FontworkCharacterSpacingWindow, SelectHdl));
+ mxNormal->connect_toggled(LINK(this, FontworkCharacterSpacingWindow, SelectHdl));
+ mxLoose->connect_toggled(LINK(this, FontworkCharacterSpacingWindow, SelectHdl));
+ mxVeryLoose->connect_toggled(LINK(this, FontworkCharacterSpacingWindow, SelectHdl));
+ mxCustom->connect_toggled(LINK(this, FontworkCharacterSpacingWindow, SelectHdl));
+ mxCustom->connect_mouse_release(LINK(this, FontworkCharacterSpacingWindow, MouseReleaseHdl));
+
+ mxKernPairs->connect_toggled(LINK(this, FontworkCharacterSpacingWindow, KernSelectHdl));
+
+ AddStatusListener( gsFontworkCharacterSpacing );
+ AddStatusListener( gsFontworkKernCharacterPairs );
+
+ // See TODO in svx/source/toolbars/fontworkbar.cxx for SID_FONTWORK_KERN_CHARACTER_PAIRS,
+ // the kernpairs setting is ignored, so hide the widget entirely
+ mxKernPairs->hide();
+}
+
+void FontworkCharacterSpacingWindow::GrabFocus()
+{
+ mxVeryTight->grab_focus();
+}
+
+void FontworkCharacterSpacingWindow::implSetCharacterSpacing( sal_Int32 nCharacterSpacing, bool bEnabled )
+{
+ bool bSettingValue = mbSettingValue;
+ mbSettingValue = true;
+
+ mxVeryTight->set_sensitive(bEnabled);
+ mxTight->set_sensitive(bEnabled);
+ mxNormal->set_sensitive(bEnabled);
+ mxLoose->set_sensitive(bEnabled);
+ mxVeryLoose->set_sensitive(bEnabled);
+ mxCustom->set_sensitive(bEnabled);
+
+ mxVeryTight->set_active(false);
+ mxTight->set_active(false);
+ mxNormal->set_active(false);
+ mxLoose->set_active(false);
+ mxVeryLoose->set_active(false);
+ mxCustom->set_active(true);
+
+ switch(nCharacterSpacing)
+ {
+ case 80:
+ mxVeryTight->set_active(true);
+ break;
+ case 90:
+ mxTight->set_active(true);
+ break;
+ case 100:
+ mxNormal->set_active(true);
+ break;
+ case 120:
+ mxLoose->set_active(true);
+ break;
+ case 150:
+ mxVeryLoose->set_active(true);
+ break;
+ }
+
+ mnCharacterSpacing = nCharacterSpacing;
+
+ mbSettingValue = bSettingValue;
+}
+
+void FontworkCharacterSpacingWindow::implSetKernCharacterPairs(bool bKernOnOff, bool bEnabled)
+{
+ mxKernPairs->set_sensitive(bEnabled);
+ mxKernPairs->set_active(bKernOnOff);
+}
+
+void FontworkCharacterSpacingWindow::statusChanged( const css::frame::FeatureStateEvent& Event )
+{
+ if( Event.FeatureURL.Main == gsFontworkCharacterSpacing )
+ {
+ if( !Event.IsEnabled )
+ {
+ implSetCharacterSpacing( 0, false );
+ }
+ else
+ {
+ sal_Int32 nValue = 0;
+ if( Event.State >>= nValue )
+ implSetCharacterSpacing( nValue, true );
+ }
+ }
+ else if( Event.FeatureURL.Main == gsFontworkKernCharacterPairs )
+ {
+ if( !Event.IsEnabled )
+ {
+ implSetKernCharacterPairs(false, false);
+ }
+ else
+ {
+ bool bValue = false;
+ if( Event.State >>= bValue )
+ implSetKernCharacterPairs(bValue, true);
+ }
+ }
+}
+
+IMPL_LINK_NOARG(FontworkCharacterSpacingWindow, KernSelectHdl, weld::Toggleable&, void)
+{
+ if (mbSettingValue)
+ return;
+
+ bool bKernOnOff = mxKernPairs->get_active();
+ Sequence< PropertyValue > aArgs{ comphelper::makePropertyValue(
+ OUString(gsFontworkKernCharacterPairs).copy(5), bKernOnOff) };
+
+ mxControl->dispatchCommand( gsFontworkKernCharacterPairs, aArgs );
+ mbCommandDispatched = true;
+
+ implSetKernCharacterPairs(bKernOnOff, true);
+
+ mxControl->EndPopupMode();
+}
+
+void FontworkCharacterSpacingWindow::DispatchSpacingDialog()
+{
+ Sequence< PropertyValue > aArgs{ comphelper::makePropertyValue(
+ OUString(gsFontworkCharacterSpacing).copy(5), mnCharacterSpacing) };
+
+ rtl::Reference<svt::PopupWindowController> xControl(mxControl);
+ xControl->EndPopupMode();
+ xControl->dispatchCommand(".uno:FontworkCharacterSpacingDialog", aArgs);
+ mbCommandDispatched = true;
+}
+
+IMPL_LINK(FontworkCharacterSpacingWindow, SelectHdl, weld::Toggleable&, rButton, void)
+{
+ if (!rButton.get_active())
+ return;
+
+ if (mbSettingValue)
+ return;
+
+ // see MouseReleaseHdl for mbCommandDispatched check, there's no guarantee
+ // this toggle will happen before that mouse release though it does in
+ // practice for vcl and gtk
+ if (mbCommandDispatched)
+ return;
+
+ if (mxCustom->get_active())
+ DispatchSpacingDialog();
+ else
+ {
+ sal_Int32 nCharacterSpacing;
+ if (mxVeryTight->get_active())
+ nCharacterSpacing = 80;
+ else if (mxTight->get_active())
+ nCharacterSpacing = 90;
+ else if (mxLoose->get_active())
+ nCharacterSpacing = 120;
+ else if (mxVeryLoose->get_active())
+ nCharacterSpacing = 150;
+ else
+ nCharacterSpacing = 100;
+
+ Sequence< PropertyValue > aArgs{ comphelper::makePropertyValue(
+ OUString(gsFontworkCharacterSpacing).copy(5), nCharacterSpacing) };
+
+ mxControl->dispatchCommand( gsFontworkCharacterSpacing, aArgs );
+ mbCommandDispatched = true;
+
+ implSetCharacterSpacing( nCharacterSpacing, true );
+ }
+
+ mxControl->EndPopupMode();
+}
+
+IMPL_LINK_NOARG(FontworkCharacterSpacingWindow, MouseReleaseHdl, const MouseEvent&, bool)
+{
+ /*
+ tdf#145296 if the "custom" radiobutton was presented preselected as
+ toggled on and the user clicked on it then there's no toggled signal sent
+ because the item was already toggled on and didn't change state.
+
+ So if that happens launch the custom spacing dialog explicitly here on
+ mouse release.
+ */
+ if (mxCustom->get_active() && !mbCommandDispatched)
+ {
+ DispatchSpacingDialog();
+ return true;
+ }
+ return false;
+}
+
+namespace {
+
+class FontworkCharacterSpacingControl : public svt::PopupWindowController
+{
+public:
+ explicit FontworkCharacterSpacingControl( const css::uno::Reference< css::uno::XComponentContext >& rxContext );
+
+ virtual std::unique_ptr<WeldToolbarPopup> weldPopupWindow() override;
+ virtual VclPtr<vcl::Window> createVclPopupWindow( vcl::Window* pParent ) override;
+
+ // XInitialization
+ virtual void SAL_CALL initialize( const css::uno::Sequence< css::uno::Any >& aArguments ) override;
+
+ // XServiceInfo
+ virtual OUString SAL_CALL getImplementationName() override;
+ virtual css::uno::Sequence< OUString > SAL_CALL getSupportedServiceNames() override;
+};
+
+}
+
+FontworkCharacterSpacingControl::FontworkCharacterSpacingControl( const Reference< XComponentContext >& rxContext )
+: svt::PopupWindowController( rxContext, Reference< css::frame::XFrame >(), ".uno:FontworkCharacterSpacingFloater" )
+{
+}
+
+std::unique_ptr<WeldToolbarPopup> FontworkCharacterSpacingControl::weldPopupWindow()
+{
+ return std::make_unique<FontworkCharacterSpacingWindow>(this, m_pToolbar);
+}
+
+VclPtr<vcl::Window> FontworkCharacterSpacingControl::createVclPopupWindow( vcl::Window* pParent )
+{
+ mxInterimPopover = VclPtr<InterimToolbarPopup>::Create(getFrameInterface(), pParent,
+ std::make_unique<FontworkCharacterSpacingWindow>(this, pParent->GetFrameWeld()));
+
+ mxInterimPopover->Show();
+
+ return mxInterimPopover;
+}
+
+// XInitialization
+void SAL_CALL FontworkCharacterSpacingControl::initialize( const css::uno::Sequence< css::uno::Any >& aArguments )
+{
+ svt::PopupWindowController::initialize( aArguments );
+
+ if (m_pToolbar)
+ {
+ mxPopoverContainer.reset(new ToolbarPopupContainer(m_pToolbar));
+ m_pToolbar->set_item_popover(m_aCommandURL.toUtf8(), mxPopoverContainer->getTopLevel());
+ }
+
+ ToolBox* pToolBox = nullptr;
+ ToolBoxItemId nId;
+ if ( getToolboxId( nId, &pToolBox ) )
+ pToolBox->SetItemBits( nId, pToolBox->GetItemBits( nId ) | ToolBoxItemBits::DROPDOWNONLY );
+}
+
+// XServiceInfo
+
+
+OUString FontworkCharacterSpacingControl::getImplementationName()
+{
+ return "com.sun.star.comp.svx.FontworkCharacterSpacingController";
+}
+
+
+Sequence< OUString > FontworkCharacterSpacingControl::getSupportedServiceNames()
+{
+ Sequence<OUString> aSNS { "com.sun.star.frame.ToolbarController" };
+ return aSNS;
+}
+
+
+extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface*
+com_sun_star_comp_svx_FontworkCharacterSpacingControl_get_implementation(
+ css::uno::XComponentContext* xContext,
+ css::uno::Sequence<css::uno::Any> const &)
+{
+ return cppu::acquire(new FontworkCharacterSpacingControl(xContext));
+}
+
+FontworkCharacterSpacingDialog::FontworkCharacterSpacingDialog(weld::Window* pParent, sal_Int32 nScale)
+ : GenericDialogController(pParent, "svx/ui/fontworkspacingdialog.ui", "FontworkSpacingDialog")
+ , m_xMtrScale(m_xBuilder->weld_metric_spin_button("entry", FieldUnit::PERCENT))
+{
+ m_xMtrScale->set_value(nScale, FieldUnit::PERCENT);
+}
+
+FontworkCharacterSpacingDialog::~FontworkCharacterSpacingDialog()
+{
+}
+
+sal_Int32 FontworkCharacterSpacingDialog::getScale() const
+{
+ return static_cast<sal_Int32>(m_xMtrScale->get_value(FieldUnit::PERCENT));
+}
+
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/tbxctrls/formatpaintbrushctrl.cxx b/svx/source/tbxctrls/formatpaintbrushctrl.cxx
new file mode 100644
index 000000000..f67efd12c
--- /dev/null
+++ b/svx/source/tbxctrls/formatpaintbrushctrl.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 <svx/formatpaintbrushctrl.hxx>
+
+#include <comphelper/propertyvalue.hxx>
+#include <svl/eitem.hxx>
+#include <vcl/toolbox.hxx>
+#include <vcl/settings.hxx>
+
+
+namespace svx
+{
+
+
+using namespace ::com::sun::star::uno;
+using namespace ::com::sun::star::beans;
+
+SFX_IMPL_TOOLBOX_CONTROL( FormatPaintBrushToolBoxControl, SfxBoolItem );
+
+FormatPaintBrushToolBoxControl::FormatPaintBrushToolBoxControl( sal_uInt16 nSlotId, ToolBoxItemId nId, ToolBox& rTbx )
+ : SfxToolBoxControl( nSlotId, nId, rTbx )
+ , m_bPersistentCopy(false)
+ , m_aDoubleClickTimer("FormatPaintBrushToolBoxControl m_aDoubleClickTimer")
+{
+ sal_uInt64 nDblClkTime = rTbx.GetSettings().GetMouseSettings().GetDoubleClickTime();
+
+ m_aDoubleClickTimer.SetInvokeHandler( LINK(this, FormatPaintBrushToolBoxControl, WaitDoubleClickHdl) );
+ m_aDoubleClickTimer.SetTimeout(nDblClkTime);
+}
+
+
+FormatPaintBrushToolBoxControl::~FormatPaintBrushToolBoxControl()
+{
+ m_aDoubleClickTimer.Stop();
+}
+
+
+void FormatPaintBrushToolBoxControl::impl_executePaintBrush()
+{
+ Sequence< PropertyValue > aArgs{ comphelper::makePropertyValue("PersistentCopy",
+ m_bPersistentCopy) };
+ Dispatch( ".uno:FormatPaintbrush"
+ , aArgs );
+}
+
+
+void FormatPaintBrushToolBoxControl::DoubleClick()
+{
+ m_aDoubleClickTimer.Stop();
+
+ m_bPersistentCopy = true;
+ impl_executePaintBrush();
+}
+
+
+void FormatPaintBrushToolBoxControl::Click()
+{
+ m_bPersistentCopy = false;
+ m_aDoubleClickTimer.Start();
+}
+
+
+IMPL_LINK_NOARG(FormatPaintBrushToolBoxControl, WaitDoubleClickHdl, Timer *, void)
+{
+ //there was no second click during waiting
+ impl_executePaintBrush();
+}
+
+
+void FormatPaintBrushToolBoxControl::Select(sal_uInt16 /*nSelectModifier*/)
+{
+}
+
+
+void FormatPaintBrushToolBoxControl::StateChangedAtToolBoxControl( sal_uInt16 nSID, SfxItemState eState,
+ const SfxPoolItem* pState )
+{
+ if( eState != SfxItemState::DEFAULT && eState != SfxItemState::SET )
+ m_bPersistentCopy = false;
+ SfxToolBoxControl::StateChangedAtToolBoxControl( nSID, eState, pState );
+}
+
+
+} //namespace svx
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/tbxctrls/grafctrl.cxx b/svx/source/tbxctrls/grafctrl.cxx
new file mode 100644
index 000000000..733cfb3af
--- /dev/null
+++ b/svx/source/tbxctrls/grafctrl.cxx
@@ -0,0 +1,961 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * 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/propertyvalue.hxx>
+#include <o3tl/string_view.hxx>
+#include <vcl/toolbox.hxx>
+#include <vcl/idle.hxx>
+#include <svl/intitem.hxx>
+#include <svl/itempool.hxx>
+#include <svl/eitem.hxx>
+#include <svl/whiter.hxx>
+#include <sfx2/viewsh.hxx>
+#include <sfx2/request.hxx>
+#include <sfx2/basedlgs.hxx>
+#include <vcl/InterimItemWindow.hxx>
+#include <sfx2/sfxdlg.hxx>
+#include <tools/urlobj.hxx>
+
+#include <svx/dialogs.hrc>
+#include <svx/svxids.hrc>
+#include <svx/strings.hrc>
+#include <editeng/brushitem.hxx>
+#include <editeng/sizeitem.hxx>
+#include <svx/sdgcpitm.hxx>
+
+#include <svx/dialmgr.hxx>
+#include <svx/svdview.hxx>
+#include <svx/svdmodel.hxx>
+#include <svx/svdograf.hxx>
+#include <svx/svdundo.hxx>
+#include <svx/svdtrans.hxx>
+#include <svx/grafctrl.hxx>
+#include <svx/tbxcolor.hxx>
+#include <sdgcoitm.hxx>
+#include <svx/sdggaitm.hxx>
+#include <svx/sdgluitm.hxx>
+#include <svx/sdgmoitm.hxx>
+#include <sdgtritm.hxx>
+#include <bitmaps.hlst>
+
+#include <com/sun/star/frame/XDispatchProvider.hpp>
+
+using namespace ::com::sun::star::uno;
+using namespace ::com::sun::star::frame;
+using namespace ::com::sun::star::util;
+using namespace ::com::sun::star::beans;
+using namespace ::com::sun::star::lang;
+
+constexpr OUStringLiteral TOOLBOX_NAME = u"colorbar";
+#define RID_SVXSTR_UNDO_GRAFCROP RID_SVXSTR_GRAFCROP
+
+namespace {
+
+class ImplGrafControl final : public InterimItemWindow
+{
+private:
+ OUString maCommand;
+ Reference<XFrame> mxFrame;
+ std::unique_ptr<weld::Image> mxImage;
+ std::unique_ptr<weld::MetricSpinButton> mxField;
+
+ DECL_LINK(ValueChangedHdl, weld::MetricSpinButton&, void);
+ DECL_LINK(KeyInputHdl, const KeyEvent&, bool);
+ void ImplModify();
+
+public:
+ ImplGrafControl( vcl::Window* pParent, const OUString& rCmd, const Reference< XFrame >& rFrame );
+ virtual ~ImplGrafControl() override;
+ virtual void dispose() override;
+
+ void Update( const SfxPoolItem* pItem );
+ void set_field_text(const OUString& rStr) { mxField->set_text(rStr); }
+ void set_sensitive(bool bSensitive)
+ {
+ Enable(bSensitive);
+ mxImage->set_sensitive(bSensitive);
+ mxField->set_sensitive(bSensitive);
+ }
+};
+
+}
+
+IMPL_LINK_NOARG(ImplGrafControl, ValueChangedHdl, weld::MetricSpinButton&, void)
+{
+ ImplModify();
+}
+
+void ImplGrafControl::ImplModify()
+{
+ const sal_Int64 nVal = mxField->get_value(FieldUnit::NONE);
+
+ // Convert value to an any to be usable with dispatch API
+ Any a;
+ if ( maCommand == ".uno:GrafRed" ||
+ maCommand == ".uno:GrafGreen" ||
+ maCommand == ".uno:GrafBlue" ||
+ maCommand == ".uno:GrafLuminance" ||
+ maCommand == ".uno:GrafContrast" )
+ a <<= sal_Int16( nVal );
+ else if ( maCommand == ".uno:GrafGamma" ||
+ maCommand == ".uno:GrafTransparence" )
+ a <<= sal_Int32( nVal );
+
+ if ( !a.hasValue() )
+ return;
+
+ INetURLObject aObj( maCommand );
+
+ Sequence< PropertyValue > aArgs{ comphelper::makePropertyValue(aObj.GetURLPath(), a) };
+
+ SfxToolBoxControl::Dispatch(
+ Reference< XDispatchProvider >( mxFrame->getController(), UNO_QUERY ),
+ maCommand,
+ aArgs );
+}
+
+void ImplGrafControl::Update( const SfxPoolItem* pItem )
+{
+ if( pItem )
+ {
+ tools::Long nValue;
+
+ if ( maCommand == ".uno:GrafTransparence" )
+ nValue = static_cast<const SfxUInt16Item*>( pItem )->GetValue();
+ else if ( maCommand == ".uno:GrafGamma" )
+ nValue = static_cast<const SfxUInt32Item*>( pItem )->GetValue();
+ else
+ nValue = static_cast<const SfxInt16Item*>( pItem )->GetValue();
+
+ mxField->set_value(nValue, FieldUnit::NONE);
+ }
+ else
+ mxField->set_text(OUString());
+}
+
+namespace {
+
+struct CommandToRID
+{
+ const char* pCommand;
+ rtl::OUStringConstExpr sResId;
+};
+
+}
+
+static OUString ImplGetRID( std::u16string_view aCommand )
+{
+ static constexpr OUStringLiteral EMPTY = u"";
+ static const CommandToRID aImplCommandToResMap[] =
+ {
+ { ".uno:GrafRed", RID_SVXBMP_GRAF_RED },
+ { ".uno:GrafGreen", RID_SVXBMP_GRAF_GREEN },
+ { ".uno:GrafBlue", RID_SVXBMP_GRAF_BLUE },
+ { ".uno:GrafLuminance", RID_SVXBMP_GRAF_LUMINANCE },
+ { ".uno:GrafContrast", RID_SVXBMP_GRAF_CONTRAST },
+ { ".uno:GrafGamma", RID_SVXBMP_GRAF_GAMMA },
+ { ".uno:GrafTransparence", RID_SVXBMP_GRAF_TRANSPARENCE },
+ { nullptr, EMPTY }
+ };
+
+ OUString sRID;
+
+ sal_Int32 i( 0 );
+ while ( aImplCommandToResMap[ i ].pCommand )
+ {
+ if ( o3tl::equalsAscii( aCommand, aImplCommandToResMap[ i ].pCommand ))
+ {
+ sRID = aImplCommandToResMap[i].sResId;
+ break;
+ }
+ ++i;
+ }
+
+ return sRID;
+}
+
+ImplGrafControl::ImplGrafControl(
+ vcl::Window* pParent,
+ const OUString& rCmd,
+ const Reference< XFrame >& rFrame)
+ : InterimItemWindow(pParent, "svx/ui/grafctrlbox.ui", "GrafCtrlBox")
+ , maCommand(rCmd)
+ , mxFrame(rFrame)
+ , mxImage(m_xBuilder->weld_image("image"))
+ , mxField(m_xBuilder->weld_metric_spin_button("spinfield", FieldUnit::NONE))
+{
+ InitControlBase(&mxField->get_widget());
+
+ OUString sResId(ImplGetRID(rCmd));
+ mxImage->set_from_icon_name(sResId);
+ mxImage->set_toolbar_background();
+
+ SetBackground( Wallpaper() ); // transparent background
+
+ mxField->set_help_id(OUStringToOString(rCmd, RTL_TEXTENCODING_UTF8));
+ mxField->get_widget().connect_key_press(LINK(this, ImplGrafControl, KeyInputHdl));
+ mxField->connect_value_changed(LINK(this, ImplGrafControl, ValueChangedHdl));
+
+ if (maCommand == ".uno:GrafGamma")
+ {
+ mxField->set_digits(2);
+
+ mxField->set_range(10, 1000, FieldUnit::NONE);
+ mxField->set_increments(10, 100, FieldUnit::NONE);
+ }
+ else
+ {
+ const tools::Long nMinVal = maCommand == ".uno:GrafTransparence" ? 0 : -100;
+
+ mxField->set_unit(FieldUnit::PERCENT);
+ mxField->set_digits(0);
+
+ mxField->set_range(nMinVal, 100, FieldUnit::PERCENT);
+ mxField->set_increments(1, 10, FieldUnit::PERCENT);
+ }
+
+ SetSizePixel(m_xContainer->get_preferred_size());
+}
+
+IMPL_LINK(ImplGrafControl, KeyInputHdl, const KeyEvent&, rKEvt, bool)
+{
+ return ChildKeyInput(rKEvt);
+}
+
+ImplGrafControl::~ImplGrafControl()
+{
+ disposeOnce();
+}
+
+void ImplGrafControl::dispose()
+{
+ mxImage.reset();
+ mxField.reset();
+ InterimItemWindow::dispose();
+}
+
+namespace {
+
+class ImplGrafModeControl final : public InterimItemWindow
+{
+private:
+ sal_uInt16 mnCurPos;
+ Reference< XFrame > mxFrame;
+ std::unique_ptr<weld::ComboBox> m_xWidget;
+
+ DECL_LINK(SelectHdl, weld::ComboBox&, void);
+ DECL_LINK(KeyInputHdl, const KeyEvent&, bool);
+ DECL_LINK(FocusInHdl, weld::Widget&, void);
+
+ static void ImplReleaseFocus();
+
+public:
+ ImplGrafModeControl( vcl::Window* pParent, const Reference< XFrame >& rFrame );
+ virtual void dispose() override;
+ virtual ~ImplGrafModeControl() override;
+
+ void set_sensitive(bool bSensitive)
+ {
+ Enable(bSensitive);
+ m_xWidget->set_sensitive(true);
+ }
+
+ void set_active(int nActive)
+ {
+ m_xWidget->set_active(nActive);
+ }
+
+ void Update( const SfxPoolItem* pItem );
+};
+
+}
+
+ImplGrafModeControl::ImplGrafModeControl(vcl::Window* pParent, const Reference<XFrame>& rFrame)
+ : InterimItemWindow(pParent, "svx/ui/grafmodebox.ui", "GrafModeBox")
+ , mnCurPos(0)
+ , mxFrame(rFrame)
+ , m_xWidget(m_xBuilder->weld_combo_box("grafmode"))
+{
+ InitControlBase(m_xWidget.get());
+
+ m_xWidget->append_text( SvxResId( RID_SVXSTR_GRAFMODE_STANDARD ) );
+ m_xWidget->append_text( SvxResId( RID_SVXSTR_GRAFMODE_GREYS ) );
+ m_xWidget->append_text( SvxResId( RID_SVXSTR_GRAFMODE_MONO ) );
+ m_xWidget->append_text( SvxResId( RID_SVXSTR_GRAFMODE_WATERMARK ) );
+
+ m_xWidget->connect_changed(LINK(this, ImplGrafModeControl, SelectHdl));
+ m_xWidget->connect_key_press(LINK(this, ImplGrafModeControl, KeyInputHdl));
+ m_xWidget->connect_focus_in(LINK(this, ImplGrafModeControl, FocusInHdl));
+
+ SetSizePixel(m_xWidget->get_preferred_size());
+}
+
+void ImplGrafModeControl::dispose()
+{
+ m_xWidget.reset();
+ InterimItemWindow::dispose();
+}
+
+ImplGrafModeControl::~ImplGrafModeControl()
+{
+ disposeOnce();
+}
+
+IMPL_LINK(ImplGrafModeControl, SelectHdl, weld::ComboBox&, rBox, void)
+{
+ Sequence< PropertyValue > aArgs{ comphelper::makePropertyValue("GrafMode",
+ sal_Int16(rBox.get_active())) };
+
+ /* #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. */
+ ImplReleaseFocus();
+
+ SfxToolBoxControl::Dispatch(
+ Reference< XDispatchProvider >( mxFrame->getController(), UNO_QUERY ),
+ ".uno:GrafMode",
+ aArgs );
+}
+
+IMPL_LINK(ImplGrafModeControl, KeyInputHdl, const KeyEvent&, rKEvt, bool)
+{
+ bool bHandled(false);
+
+ if (rKEvt.GetKeyCode().GetCode() == KEY_ESCAPE)
+ {
+ m_xWidget->set_active(mnCurPos);
+ ImplReleaseFocus();
+ bHandled = true;
+ }
+
+ return bHandled || ChildKeyInput(rKEvt);
+}
+
+IMPL_LINK_NOARG(ImplGrafModeControl, FocusInHdl, weld::Widget&, void)
+{
+ mnCurPos = m_xWidget->get_active();
+}
+
+void ImplGrafModeControl::ImplReleaseFocus()
+{
+ if( SfxViewShell::Current() )
+ {
+ vcl::Window* pShellWnd = SfxViewShell::Current()->GetWindow();
+
+ if( pShellWnd )
+ pShellWnd->GrabFocus();
+ }
+}
+
+void ImplGrafModeControl::Update( const SfxPoolItem* pItem )
+{
+ if( pItem )
+ m_xWidget->set_active(static_cast<const SfxUInt16Item*>(pItem)->GetValue());
+ else
+ m_xWidget->set_active(-1);
+}
+
+SvxGrafToolBoxControl::SvxGrafToolBoxControl( sal_uInt16 nSlotId, ToolBoxItemId nId, ToolBox& rTbx) :
+ SfxToolBoxControl( nSlotId, nId, rTbx )
+{
+ rTbx.SetItemBits( nId, ToolBoxItemBits::DROPDOWN | rTbx.GetItemBits( nId ) );
+ rTbx.Invalidate();
+}
+
+SvxGrafToolBoxControl::~SvxGrafToolBoxControl()
+{
+}
+
+void SvxGrafToolBoxControl::StateChangedAtToolBoxControl( sal_uInt16, SfxItemState eState, const SfxPoolItem* pState )
+{
+ ImplGrafControl* pCtrl = static_cast<ImplGrafControl*>( GetToolBox().GetItemWindow( GetId() ) );
+ DBG_ASSERT( pCtrl, "Control not found" );
+
+ if( eState == SfxItemState::DISABLED )
+ {
+ pCtrl->set_sensitive(false);
+ pCtrl->set_field_text( OUString() );
+ }
+ else
+ {
+ pCtrl->set_sensitive(true);
+
+ if( eState == SfxItemState::DEFAULT )
+ pCtrl->Update( pState );
+ else
+ pCtrl->Update( nullptr );
+ }
+}
+
+VclPtr<InterimItemWindow> SvxGrafToolBoxControl::CreateItemWindow( vcl::Window *pParent )
+{
+ return VclPtr<ImplGrafControl>::Create( pParent, m_aCommandURL, m_xFrame ).get();
+}
+
+SFX_IMPL_TOOLBOX_CONTROL( SvxGrafRedToolBoxControl, SfxInt16Item );
+
+SvxGrafRedToolBoxControl::SvxGrafRedToolBoxControl( sal_uInt16 nSlotId, ToolBoxItemId nId, ToolBox& rTbx ) :
+ SvxGrafToolBoxControl( nSlotId, nId, rTbx )
+{
+}
+
+SFX_IMPL_TOOLBOX_CONTROL( SvxGrafGreenToolBoxControl, SfxInt16Item );
+
+SvxGrafGreenToolBoxControl::SvxGrafGreenToolBoxControl( sal_uInt16 nSlotId, ToolBoxItemId nId, ToolBox& rTbx ) :
+ SvxGrafToolBoxControl( nSlotId, nId, rTbx )
+{
+}
+
+SFX_IMPL_TOOLBOX_CONTROL( SvxGrafBlueToolBoxControl, SfxInt16Item );
+
+SvxGrafBlueToolBoxControl::SvxGrafBlueToolBoxControl( sal_uInt16 nSlotId, ToolBoxItemId nId, ToolBox& rTbx ) :
+ SvxGrafToolBoxControl( nSlotId, nId, rTbx )
+{
+}
+
+SFX_IMPL_TOOLBOX_CONTROL( SvxGrafLuminanceToolBoxControl, SfxInt16Item );
+
+SvxGrafLuminanceToolBoxControl::SvxGrafLuminanceToolBoxControl( sal_uInt16 nSlotId, ToolBoxItemId nId, ToolBox& rTbx ) :
+ SvxGrafToolBoxControl( nSlotId, nId, rTbx )
+{
+}
+
+SFX_IMPL_TOOLBOX_CONTROL( SvxGrafContrastToolBoxControl, SfxInt16Item );
+
+SvxGrafContrastToolBoxControl::SvxGrafContrastToolBoxControl( sal_uInt16 nSlotId, ToolBoxItemId nId, ToolBox& rTbx ) :
+ SvxGrafToolBoxControl( nSlotId, nId, rTbx )
+{
+}
+
+SFX_IMPL_TOOLBOX_CONTROL( SvxGrafGammaToolBoxControl, SfxUInt32Item );
+
+SvxGrafGammaToolBoxControl::SvxGrafGammaToolBoxControl( sal_uInt16 nSlotId, ToolBoxItemId nId, ToolBox& rTbx ) :
+ SvxGrafToolBoxControl( nSlotId, nId, rTbx )
+{
+}
+
+SFX_IMPL_TOOLBOX_CONTROL( SvxGrafTransparenceToolBoxControl, SfxUInt16Item );
+
+SvxGrafTransparenceToolBoxControl::SvxGrafTransparenceToolBoxControl( sal_uInt16 nSlotId, ToolBoxItemId nId, ToolBox& rTbx ) :
+ SvxGrafToolBoxControl( nSlotId, nId, rTbx )
+{
+}
+
+SFX_IMPL_TOOLBOX_CONTROL( SvxGrafModeToolBoxControl, SfxUInt16Item );
+
+SvxGrafModeToolBoxControl::SvxGrafModeToolBoxControl( sal_uInt16 nSlotId, ToolBoxItemId nId, ToolBox& rTbx ) :
+ SfxToolBoxControl( nSlotId, nId, rTbx )
+{
+}
+
+SvxGrafModeToolBoxControl::~SvxGrafModeToolBoxControl()
+{
+}
+
+void SvxGrafModeToolBoxControl::StateChangedAtToolBoxControl( sal_uInt16, SfxItemState eState, const SfxPoolItem* pState )
+
+{
+ ImplGrafModeControl* pCtrl = static_cast<ImplGrafModeControl*>( GetToolBox().GetItemWindow( GetId() ) );
+ DBG_ASSERT( pCtrl, "Control not found" );
+
+ if( eState == SfxItemState::DISABLED )
+ {
+ pCtrl->set_sensitive(false);
+ pCtrl->set_active(-1);
+ }
+ else
+ {
+ pCtrl->set_sensitive(true);
+
+ if( eState == SfxItemState::DEFAULT )
+ pCtrl->Update( pState );
+ else
+ pCtrl->Update( nullptr );
+ }
+}
+
+VclPtr<InterimItemWindow> SvxGrafModeToolBoxControl::CreateItemWindow( vcl::Window *pParent )
+{
+ return VclPtr<ImplGrafModeControl>::Create( pParent, m_xFrame ).get();
+}
+
+void SvxGrafAttrHelper::ExecuteGrafAttr( SfxRequest& rReq, SdrView& rView )
+{
+ SfxItemPool& rPool = rView.GetModel()->GetItemPool();
+ SfxItemSetFixed<SDRATTR_GRAF_FIRST, SDRATTR_GRAF_LAST> aSet( rPool );
+ OUString aUndoStr;
+ const bool bUndo = rView.IsUndoEnabled();
+
+ if( bUndo )
+ {
+ aUndoStr = rView.GetDescriptionOfMarkedObjects() + " ";
+ }
+
+ 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_ATTR_GRAF_RED:
+ {
+ if( pItem )
+ {
+ aSet.Put( SdrGrafRedItem( static_cast<const SfxInt16Item*>(pItem)->GetValue() ));
+ if( bUndo )
+ aUndoStr += SvxResId( RID_SVXSTR_UNDO_GRAFRED );
+ }
+ }
+ break;
+
+ case SID_ATTR_GRAF_GREEN:
+ {
+ if( pItem )
+ {
+ aSet.Put( SdrGrafGreenItem( static_cast<const SfxInt16Item*>(pItem)->GetValue() ));
+ if( bUndo )
+ aUndoStr += SvxResId( RID_SVXSTR_UNDO_GRAFGREEN );
+ }
+ }
+ break;
+
+ case SID_ATTR_GRAF_BLUE:
+ {
+ if( pItem )
+ {
+ aSet.Put( SdrGrafBlueItem( static_cast<const SfxInt16Item*>(pItem)->GetValue() ));
+ if( bUndo )
+ aUndoStr += SvxResId( RID_SVXSTR_UNDO_GRAFBLUE );
+ }
+ }
+ break;
+
+ case SID_ATTR_GRAF_LUMINANCE:
+ {
+ if( pItem )
+ {
+ aSet.Put( SdrGrafLuminanceItem( static_cast<const SfxInt16Item*>(pItem)->GetValue() ));
+ if( bUndo )
+ aUndoStr += SvxResId( RID_SVXSTR_UNDO_GRAFLUMINANCE );
+ }
+ }
+ break;
+
+ case SID_ATTR_GRAF_CONTRAST:
+ {
+ if( pItem )
+ {
+ aSet.Put( SdrGrafContrastItem( static_cast<const SfxInt16Item*>(pItem)->GetValue() ));
+ if( bUndo )
+ aUndoStr += SvxResId( RID_SVXSTR_UNDO_GRAFCONTRAST );
+ }
+ }
+ break;
+
+ case SID_ATTR_GRAF_GAMMA:
+ {
+ if( pItem )
+ {
+ aSet.Put( SdrGrafGamma100Item( static_cast<const SfxUInt32Item*>(pItem)->GetValue() ));
+ if( bUndo )
+ aUndoStr += SvxResId( RID_SVXSTR_UNDO_GRAFGAMMA );
+ }
+ }
+ break;
+
+ case SID_ATTR_GRAF_TRANSPARENCE:
+ {
+ if( pItem )
+ {
+ aSet.Put( SdrGrafTransparenceItem( static_cast<const SfxUInt16Item*>(pItem)->GetValue() ));
+ if( bUndo )
+ aUndoStr += SvxResId( RID_SVXSTR_UNDO_GRAFTRANSPARENCY );
+ }
+ }
+ break;
+
+ case SID_ATTR_GRAF_MODE:
+ {
+ if( pItem )
+ {
+ aSet.Put( SdrGrafModeItem( static_cast<GraphicDrawMode>(static_cast<const SfxUInt16Item*>(pItem)->GetValue()) ));
+ if( bUndo )
+ aUndoStr += SvxResId( RID_SVXSTR_UNDO_GRAFMODE );
+ }
+ }
+ break;
+
+ case SID_ATTR_GRAF_CROP:
+ {
+ const SdrMarkList& rMarkList = rView.GetMarkedObjectList();
+
+ if( 0 < rMarkList.GetMarkCount() )
+ {
+ SdrGrafObj* pObj = static_cast<SdrGrafObj*>( rMarkList.GetMark( 0 )->GetMarkedSdrObj() );
+
+ if( ( pObj->GetGraphicType() != GraphicType::NONE ) &&
+ ( pObj->GetGraphicType() != GraphicType::Default ) )
+ {
+ SfxItemSetFixed<SDRATTR_GRAFCROP, SDRATTR_GRAFCROP> aGrfAttr( rPool );
+ const MapUnit eOldMetric = rPool.GetMetric( 0 );
+
+ aGrfAttr.Put(pObj->GetMergedItemSet());
+ rPool.SetDefaultMetric( MapUnit::MapTwip );
+
+ SfxItemSetFixed<
+ SDRATTR_GRAFCROP, SDRATTR_GRAFCROP,
+ SID_ATTR_PAGE_SIZE, SID_ATTR_PAGE_SIZE,
+ SID_ATTR_GRAF_CROP, SID_ATTR_GRAF_FRMSIZE,
+ SID_ATTR_GRAF_GRAPHIC, SID_ATTR_GRAF_GRAPHIC>
+ aCropDlgAttr(rPool);
+
+ aCropDlgAttr.Put( SvxBrushItem( pObj->GetGraphic(), GPOS_MM, SID_ATTR_GRAF_GRAPHIC ) );
+ aCropDlgAttr.Put( SvxSizeItem( SID_ATTR_PAGE_SIZE,
+ o3tl::convert(Size(200000, 200000), o3tl::Length::mm100, o3tl::Length::twip)));
+ aCropDlgAttr.Put( SvxSizeItem( SID_ATTR_GRAF_FRMSIZE,
+ o3tl::convert(pObj->GetLogicRect().GetSize(), o3tl::Length::mm100, o3tl::Length::twip)));
+
+ const SdrGrafCropItem& rCrop = aGrfAttr.Get( SDRATTR_GRAFCROP );
+ Size aLTSize = o3tl::convert(Size(rCrop.GetLeft(), rCrop.GetTop()), o3tl::Length::mm100, o3tl::Length::twip);
+ Size aRBSize = o3tl::convert(Size(rCrop.GetRight(), rCrop.GetBottom()), o3tl::Length::mm100, o3tl::Length::twip);
+
+ aCropDlgAttr.Put( SdrGrafCropItem( aLTSize.Width(), aLTSize.Height(),
+ aRBSize.Width(), aRBSize.Height() ) );
+
+ vcl::Window* pParent(SfxViewShell::Current() ? SfxViewShell::Current()->GetWindow() : nullptr);
+ SfxSingleTabDialogController aCropDialog(pParent ? pParent->GetFrameWeld() : nullptr,
+ &aCropDlgAttr);
+ const OUString aCropStr(SvxResId(RID_SVXSTR_GRAFCROP));
+
+ SfxAbstractDialogFactory* pFact = SfxAbstractDialogFactory::Create();
+ ::CreateTabPage fnCreatePage = pFact->GetTabPageCreatorFunc( RID_SVXPAGE_GRFCROP );
+ std::unique_ptr<SfxTabPage> xTabPage = (*fnCreatePage)(aCropDialog.get_content_area(), &aCropDialog, &aCropDlgAttr);
+ sal_Int32 nPreferredDPI = rView.getSdrModelFromSdrView().getImagePreferredDPI();
+ xTabPage->getAdditionalProperties().emplace("PreferredDPI", css::uno::Any(nPreferredDPI));
+ xTabPage->SetPageTitle(aCropStr);
+ aCropDialog.SetTabPage(std::move(xTabPage));
+
+ if (aCropDialog.run() == RET_OK)
+ {
+ const SfxItemSet* pOutAttr = aCropDialog.GetOutputItemSet();
+
+ if( pOutAttr )
+ {
+ aUndoStr += SvxResId( RID_SVXSTR_UNDO_GRAFCROP );
+
+ // set crop attributes
+ if( SfxItemState::SET <= pOutAttr->GetItemState( SDRATTR_GRAFCROP ) )
+ {
+ const SdrGrafCropItem& rNewCrop = pOutAttr->Get( SDRATTR_GRAFCROP );
+
+ aLTSize = o3tl::convert(Size(rNewCrop.GetLeft(), rNewCrop.GetTop()), o3tl::Length::twip, o3tl::Length::mm100);
+ aRBSize = o3tl::convert(Size(rNewCrop.GetRight(), rNewCrop.GetBottom()), o3tl::Length::twip, o3tl::Length::mm100);
+ aSet.Put( SdrGrafCropItem( aLTSize.Width(), aLTSize.Height(), aRBSize.Width(), aRBSize.Height() ) );
+ }
+
+ // set new logic rect
+ if( SfxItemState::SET <= pOutAttr->GetItemState( SID_ATTR_GRAF_FRMSIZE ) )
+ {
+ Point aNewOrigin( pObj->GetLogicRect().TopLeft() );
+ const Size& rGrfSize = pOutAttr->Get( SID_ATTR_GRAF_FRMSIZE ).GetSize();
+ Size aNewGrfSize = o3tl::convert(rGrfSize, o3tl::Length::twip, o3tl::Length::mm100);
+ Size aOldGrfSize( pObj->GetLogicRect().GetSize() );
+
+ tools::Rectangle aNewRect( aNewOrigin, aNewGrfSize );
+ Point aOffset( (aNewGrfSize.Width() - aOldGrfSize.Width()) >> 1,
+ (aNewGrfSize.Height() - aOldGrfSize.Height()) >> 1 );
+
+ // #106181# rotate snap rect before setting it
+ const GeoStat& aGeo = pObj->GetGeoStat();
+
+ if (aGeo.nRotationAngle || aGeo.nShearAngle)
+ {
+ tools::Polygon aPol(aNewRect);
+
+ // also transform origin offset
+ if (aGeo.nShearAngle)
+ {
+ ShearPoly(aPol,
+ aNewRect.TopLeft(),
+ aGeo.mfTanShearAngle);
+ ShearPoint(aOffset, Point(0,0), aGeo.mfTanShearAngle);
+ }
+ if (aGeo.nRotationAngle)
+ {
+ RotatePoly(aPol,
+ aNewRect.TopLeft(),
+ aGeo.mfSinRotationAngle,aGeo.mfCosRotationAngle);
+ RotatePoint(aOffset, Point(0,0), aGeo.mfSinRotationAngle,aGeo.mfCosRotationAngle);
+ }
+
+ // apply offset
+ aPol.Move( -aOffset.X(), -aOffset.Y() );
+ aNewRect=aPol.GetBoundRect();
+ }
+ else
+ {
+ aNewRect.Move( -aOffset.X(), -aOffset.Y() );
+ }
+
+ if( !aSet.Count() )
+ rView.SetMarkedObjRect( aNewRect );
+ else
+ {
+ if( bUndo )
+ {
+ rView.BegUndo( aUndoStr );
+ rView.AddUndo( rView.GetModel()->GetSdrUndoFactory().CreateUndoGeoObject( *pObj ) );
+ }
+ pObj->SetSnapRect( aNewRect );
+ rView.SetAttributes( aSet );
+
+ if( bUndo )
+ rView.EndUndo();
+ aSet.ClearItem();
+ }
+ }
+ }
+ }
+
+ rPool.SetDefaultMetric( eOldMetric );
+ }
+ }
+ }
+ break;
+
+ case SID_COLOR_SETTINGS:
+ {
+ svx::ToolboxAccess aToolboxAccess( TOOLBOX_NAME );
+ aToolboxAccess.toggleToolbox();
+ rReq.Done();
+ break;
+ }
+
+ default:
+ break;
+ }
+
+ if( aSet.Count() )
+ {
+ if( bUndo )
+ rView.BegUndo( aUndoStr );
+
+ rView.SetAttributes( aSet );
+
+ if( bUndo )
+ rView.EndUndo();
+ }
+}
+
+void SvxGrafAttrHelper::GetGrafAttrState( SfxItemSet& rSet, SdrView const & rView )
+{
+ SfxItemPool& rPool = rView.GetModel()->GetItemPool();
+ SfxItemSet aAttrSet( rPool );
+ SfxWhichIter aIter( rSet );
+ sal_uInt16 nWhich = aIter.FirstWhich();
+ const SdrMarkList& rMarkList = rView.GetMarkedObjectList();
+ bool bEnableColors = true;
+ bool bEnableTransparency = true;
+ bool bEnableCrop = ( 1 == rMarkList.GetMarkCount() );
+
+ for( size_t i = 0, nCount = rMarkList.GetMarkCount(); i < nCount; ++i )
+ {
+ SdrGrafObj* pGrafObj = dynamic_cast< SdrGrafObj* >( rMarkList.GetMark( i )->GetMarkedSdrObj() );
+
+ if( !pGrafObj ||
+ ( pGrafObj->GetGraphicType() == GraphicType::NONE ) ||
+ ( pGrafObj->GetGraphicType() == GraphicType::Default ))
+ {
+ bEnableColors = bEnableTransparency = bEnableCrop = false;
+ break;
+ }
+ else if( bEnableTransparency && ( pGrafObj->HasGDIMetaFile() || pGrafObj->IsAnimated() ) )
+ {
+ bEnableTransparency = false;
+ }
+ }
+
+ rView.GetAttributes( aAttrSet );
+
+ while( nWhich )
+ {
+ sal_uInt16 nSlotId = SfxItemPool::IsWhich( nWhich ) ? rPool.GetSlotId( nWhich ) : nWhich;
+
+ switch( nSlotId )
+ {
+ case SID_ATTR_GRAF_MODE:
+ {
+ if( SfxItemState::DEFAULT <= aAttrSet.GetItemState( SDRATTR_GRAFMODE ) )
+ {
+ if( bEnableColors )
+ {
+ rSet.Put( SfxUInt16Item( nSlotId,
+ sal::static_int_cast< sal_uInt16 >( aAttrSet.Get(SDRATTR_GRAFMODE).GetValue() ) ) );
+ }
+ else
+ {
+ rSet.DisableItem( SID_ATTR_GRAF_MODE );
+ }
+ }
+ }
+ break;
+
+ case SID_ATTR_GRAF_RED:
+ {
+ if( SfxItemState::DEFAULT <= aAttrSet.GetItemState( SDRATTR_GRAFRED ) )
+ {
+ if( bEnableColors )
+ {
+ rSet.Put( SfxInt16Item( nSlotId, aAttrSet.Get(SDRATTR_GRAFRED).GetValue() ) );
+ }
+ else
+ {
+ rSet.DisableItem( SID_ATTR_GRAF_RED );
+ }
+ }
+ }
+ break;
+
+ case SID_ATTR_GRAF_GREEN:
+ {
+ if( SfxItemState::DEFAULT <= aAttrSet.GetItemState( SDRATTR_GRAFGREEN ) )
+ {
+ if( bEnableColors )
+ {
+ rSet.Put( SfxInt16Item( nSlotId, aAttrSet.Get(SDRATTR_GRAFGREEN).GetValue()) );
+ }
+ else
+ {
+ rSet.DisableItem( SID_ATTR_GRAF_GREEN );
+ }
+ }
+ }
+ break;
+
+ case SID_ATTR_GRAF_BLUE:
+ {
+ if( SfxItemState::DEFAULT <= aAttrSet.GetItemState( SDRATTR_GRAFBLUE ) )
+ {
+ if( bEnableColors )
+ {
+ rSet.Put( SfxInt16Item( nSlotId, aAttrSet.Get(SDRATTR_GRAFBLUE).GetValue()) );
+ }
+ else
+ {
+ rSet.DisableItem( SID_ATTR_GRAF_BLUE );
+ }
+ }
+ }
+ break;
+
+ case SID_ATTR_GRAF_LUMINANCE:
+ {
+ if( SfxItemState::DEFAULT <= aAttrSet.GetItemState( SDRATTR_GRAFLUMINANCE ) )
+ {
+ if( bEnableColors )
+ {
+ rSet.Put( SfxInt16Item( nSlotId, aAttrSet.Get(SDRATTR_GRAFLUMINANCE).GetValue()) );
+ }
+ else
+ {
+ rSet.DisableItem( SID_ATTR_GRAF_LUMINANCE );
+ }
+ }
+ }
+ break;
+
+ case SID_ATTR_GRAF_CONTRAST:
+ {
+ if( SfxItemState::DEFAULT <= aAttrSet.GetItemState( SDRATTR_GRAFCONTRAST ) )
+ {
+ if( bEnableColors )
+ {
+ rSet.Put( SfxInt16Item( nSlotId,
+ aAttrSet.Get(SDRATTR_GRAFCONTRAST).GetValue()) );
+ }
+ else
+ {
+ rSet.DisableItem( SID_ATTR_GRAF_CONTRAST );
+ }
+ }
+ }
+ break;
+
+ case SID_ATTR_GRAF_GAMMA:
+ {
+ if( SfxItemState::DEFAULT <= aAttrSet.GetItemState( SDRATTR_GRAFGAMMA ) )
+ {
+ if( bEnableColors )
+ {
+ rSet.Put( SfxUInt32Item( nSlotId,
+ aAttrSet.Get(SDRATTR_GRAFGAMMA).GetValue() ) );
+ }
+ else
+ {
+ rSet.DisableItem( SID_ATTR_GRAF_GAMMA );
+ }
+ }
+ }
+ break;
+
+ case SID_ATTR_GRAF_TRANSPARENCE:
+ {
+ if( SfxItemState::DEFAULT <= aAttrSet.GetItemState( SDRATTR_GRAFTRANSPARENCE ) )
+ {
+ if( bEnableTransparency )
+ {
+ rSet.Put( SfxUInt16Item( nSlotId,
+ aAttrSet.Get(SDRATTR_GRAFTRANSPARENCE).GetValue() ) );
+ }
+ else
+ {
+ rSet.DisableItem( SID_ATTR_GRAF_TRANSPARENCE );
+ }
+ }
+ }
+ break;
+
+ case SID_ATTR_GRAF_CROP:
+ {
+ if( !bEnableCrop )
+ rSet.DisableItem( nSlotId );
+ }
+ break;
+
+ case SID_COLOR_SETTINGS :
+ {
+ svx::ToolboxAccess aToolboxAccess( TOOLBOX_NAME );
+ rSet.Put( SfxBoolItem( nWhich, aToolboxAccess.isToolboxVisible() ) );
+ break;
+ }
+
+ default:
+ break;
+ }
+
+ nWhich = aIter.NextWhich();
+ }
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/tbxctrls/itemwin.cxx b/svx/source/tbxctrls/itemwin.cxx
new file mode 100644
index 000000000..1a9b6d3bc
--- /dev/null
+++ b/svx/source/tbxctrls/itemwin.cxx
@@ -0,0 +1,347 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <com/sun/star/drawing/FillStyle.hpp>
+#include <com/sun/star/frame/XDispatchProvider.hpp>
+#include <com/sun/star/frame/XFrame.hpp>
+
+#include <comphelper/propertyvalue.hxx>
+#include <sfx2/tbxctrl.hxx>
+#include <sfx2/viewsh.hxx>
+#include <sfx2/module.hxx>
+
+#include <vcl/event.hxx>
+#include <vcl/svapp.hxx>
+#include <vcl/settings.hxx>
+#include <vcl/virdev.hxx>
+
+#include <svx/dialmgr.hxx>
+#include <svx/strings.hrc>
+
+#include <svx/xlnwtit.hxx>
+#include <svx/xtable.hxx>
+#include <svx/itemwin.hxx>
+#include <svtools/unitconv.hxx>
+#include "linemetricbox.hxx"
+
+using namespace ::com::sun::star;
+using namespace ::com::sun::star::uno;
+using namespace ::com::sun::star::frame;
+using namespace ::com::sun::star::lang;
+using namespace ::com::sun::star::beans;
+
+SvxMetricField::SvxMetricField(
+ vcl::Window* pParent, const Reference< XFrame >& rFrame )
+ : InterimItemWindow(pParent, "svx/ui/metricfieldbox.ui", "MetricFieldBox")
+ , m_xWidget(m_xBuilder->weld_metric_spin_button("metricfield", FieldUnit::MM))
+ , nCurValue(0)
+ , eDestPoolUnit(MapUnit::Map100thMM)
+ , eDlgUnit(SfxModule::GetModuleFieldUnit(rFrame))
+ , mxFrame(rFrame)
+{
+ InitControlBase(&m_xWidget->get_widget());
+
+ m_xWidget->set_range(0, 5000, FieldUnit::NONE);
+ m_xWidget->connect_value_changed(LINK(this, SvxMetricField, ModifyHdl));
+ m_xWidget->connect_focus_in(LINK(this, SvxMetricField, FocusInHdl));
+ m_xWidget->get_widget().connect_key_press(LINK(this, SvxMetricField, KeyInputHdl));
+
+ SetFieldUnit(*m_xWidget, eDlgUnit);
+
+ SetSizePixel(m_xWidget->get_preferred_size());
+}
+
+void SvxMetricField::dispose()
+{
+ m_xWidget.reset();
+ InterimItemWindow::dispose();
+}
+
+SvxMetricField::~SvxMetricField()
+{
+ disposeOnce();
+}
+
+void SvxMetricField::set_sensitive(bool bSensitive)
+{
+ Enable(bSensitive);
+ m_xWidget->set_sensitive(bSensitive);
+ if (!bSensitive)
+ m_xWidget->set_text("");
+}
+
+void SvxMetricField::Update( const XLineWidthItem* pItem )
+{
+ if ( pItem )
+ {
+ // tdf#132169 we always get the value in MapUnit::Map100thMM but have
+ // to set it in the core metric of the target application
+ if (pItem->GetValue() != GetCoreValue(*m_xWidget, MapUnit::Map100thMM))
+ SetMetricValue(*m_xWidget, pItem->GetValue(), MapUnit::Map100thMM);
+ }
+ else
+ m_xWidget->set_text("");
+}
+
+IMPL_LINK_NOARG(SvxMetricField, ModifyHdl, weld::MetricSpinButton&, void)
+{
+ auto nTmp = GetCoreValue(*m_xWidget, eDestPoolUnit);
+ XLineWidthItem aLineWidthItem( nTmp );
+
+ Any a;
+ aLineWidthItem.QueryValue( a );
+ Sequence< PropertyValue > aArgs{ comphelper::makePropertyValue("LineWidth", a) };
+ SfxToolBoxControl::Dispatch( Reference< XDispatchProvider >( mxFrame->getController(), UNO_QUERY ),
+ ".uno:LineWidth",
+ aArgs );
+}
+
+void SvxMetricField::ReleaseFocus_Impl()
+{
+ if( SfxViewShell::Current() )
+ {
+ vcl::Window* pShellWnd = SfxViewShell::Current()->GetWindow();
+ if ( pShellWnd )
+ pShellWnd->GrabFocus();
+ }
+}
+
+void SvxMetricField::SetDestCoreUnit( MapUnit eUnit )
+{
+ eDestPoolUnit = eUnit;
+}
+
+void SvxMetricField::RefreshDlgUnit()
+{
+ FieldUnit eTmpUnit = SfxModule::GetModuleFieldUnit( mxFrame );
+ if ( eDlgUnit != eTmpUnit )
+ {
+ eDlgUnit = eTmpUnit;
+ SetFieldUnit(*m_xWidget, eDlgUnit);
+ }
+}
+
+IMPL_LINK_NOARG(SvxMetricField, FocusInHdl, weld::Widget&, void)
+{
+ nCurValue = m_xWidget->get_value(FieldUnit::NONE);
+}
+
+IMPL_LINK(SvxMetricField, KeyInputHdl, const KeyEvent&, rKEvt, bool)
+{
+ bool bHandled = false;
+
+ sal_uInt16 nCode = rKEvt.GetKeyCode().GetCode();
+
+ if (nCode == KEY_ESCAPE)
+ {
+ m_xWidget->set_value(nCurValue, FieldUnit::NONE);
+ ModifyHdl(*m_xWidget);
+ ReleaseFocus_Impl();
+ bHandled = true;
+ }
+
+ return bHandled || ChildKeyInput(rKEvt);
+}
+
+void SvxMetricField::DataChanged( const DataChangedEvent& rDCEvt )
+{
+ if ( (rDCEvt.GetType() == DataChangedEventType::SETTINGS) &&
+ (rDCEvt.GetFlags() & AllSettingsFlags::STYLE) )
+ {
+ SetSizePixel(m_xWidget->get_preferred_size());
+ }
+
+ InterimItemWindow::DataChanged( rDCEvt );
+}
+
+void SvxFillTypeBox::Fill(weld::ComboBox& rListBox)
+{
+ rListBox.freeze();
+
+ rListBox.append_text(SvxResId(RID_SVXSTR_INVISIBLE));
+ rListBox.append_text(SvxResId(RID_SVXSTR_COLOR));
+ rListBox.append_text(SvxResId(RID_SVXSTR_GRADIENT));
+ rListBox.append_text(SvxResId(RID_SVXSTR_HATCH));
+ rListBox.append_text(SvxResId(RID_SVXSTR_BITMAP));
+ rListBox.append_text(SvxResId(RID_SVXSTR_PATTERN));
+ rListBox.append_text(SvxResId(RID_SVXSTR_USE_BACKGROUND));
+
+ rListBox.thaw();
+
+ rListBox.set_active(1); // solid color
+}
+
+namespace
+{
+ void formatBitmapExToSize(BitmapEx& rBitmapEx, const Size& rSize)
+ {
+ if(rBitmapEx.IsEmpty() || rSize.IsEmpty())
+ return;
+
+ ScopedVclPtrInstance< VirtualDevice > pVirtualDevice;
+ pVirtualDevice->SetOutputSizePixel(rSize);
+
+ if(rBitmapEx.IsAlpha())
+ {
+ const StyleSettings& rStyleSettings = Application::GetSettings().GetStyleSettings();
+
+ if(rStyleSettings.GetPreviewUsesCheckeredBackground())
+ {
+ const Point aNull(0, 0);
+ static const sal_uInt32 nLen(8);
+ static const Color aW(COL_WHITE);
+ static const Color aG(0xef, 0xef, 0xef);
+
+ pVirtualDevice->DrawCheckered(aNull, rSize, nLen, aW, aG);
+ }
+ else
+ {
+ pVirtualDevice->SetBackground(rStyleSettings.GetFieldColor());
+ pVirtualDevice->Erase();
+ }
+ }
+
+ if(rBitmapEx.GetSizePixel().Width() >= rSize.Width() && rBitmapEx.GetSizePixel().Height() >= rSize.Height())
+ {
+ rBitmapEx.Scale(rSize);
+ pVirtualDevice->DrawBitmapEx(Point(0, 0), rBitmapEx);
+ }
+ else
+ {
+ const Size aBitmapSize(rBitmapEx.GetSizePixel());
+
+ for(tools::Long y(0); y < rSize.Height(); y += aBitmapSize.Height())
+ {
+ for(tools::Long x(0); x < rSize.Width(); x += aBitmapSize.Width())
+ {
+ pVirtualDevice->DrawBitmapEx(
+ Point(x, y),
+ rBitmapEx);
+ }
+ }
+ }
+
+ rBitmapEx = pVirtualDevice->GetBitmapEx(Point(0, 0), rSize);
+ }
+} // end of anonymous namespace
+
+void SvxFillAttrBox::Fill(weld::ComboBox& rBox, const XHatchListRef &pList)
+{
+ if( !pList.is() )
+ return;
+
+ tools::Long nCount = pList->Count();
+ ScopedVclPtrInstance< VirtualDevice > pVD;
+ rBox.freeze();
+
+ for( tools::Long i = 0; i < nCount; i++ )
+ {
+ const XHatchEntry* pEntry = pList->GetHatch(i);
+ const BitmapEx aBitmapEx = pList->GetUiBitmap( i );
+ if( !aBitmapEx.IsEmpty() )
+ {
+ const Size aBmpSize(aBitmapEx.GetSizePixel());
+ pVD->SetOutputSizePixel(aBmpSize, false);
+ pVD->DrawBitmapEx(Point(), aBitmapEx);
+ rBox.append("", pEntry->GetName(), *pVD);
+ }
+ else
+ rBox.append_text(pEntry->GetName());
+ }
+
+ rBox.thaw();
+}
+
+void SvxFillAttrBox::Fill(weld::ComboBox& rBox, const XGradientListRef &pList)
+{
+ if( !pList.is() )
+ return;
+
+ tools::Long nCount = pList->Count();
+ ScopedVclPtrInstance< VirtualDevice > pVD;
+ rBox.freeze();
+
+ for( tools::Long i = 0; i < nCount; i++ )
+ {
+ const XGradientEntry* pEntry = pList->GetGradient(i);
+ const BitmapEx aBitmapEx = pList->GetUiBitmap( i );
+ if( !aBitmapEx.IsEmpty() )
+ {
+ const Size aBmpSize(aBitmapEx.GetSizePixel());
+ pVD->SetOutputSizePixel(aBmpSize, false);
+ pVD->DrawBitmapEx(Point(), aBitmapEx);
+ rBox.append("", pEntry->GetName(), *pVD);
+ }
+ else
+ rBox.append_text(pEntry->GetName());
+ }
+
+ rBox.thaw();
+}
+
+void SvxFillAttrBox::Fill(weld::ComboBox& rBox, const XBitmapListRef &pList)
+{
+ if( !pList.is() )
+ return;
+
+ tools::Long nCount = pList->Count();
+ const StyleSettings& rStyleSettings = Application::GetSettings().GetStyleSettings();
+ const Size aSize(rStyleSettings.GetListBoxPreviewDefaultPixelSize());
+ ScopedVclPtrInstance< VirtualDevice > pVD;
+ pVD->SetOutputSizePixel(aSize, false);
+ rBox.freeze();
+
+ for( tools::Long i = 0; i < nCount; i++ )
+ {
+ const XBitmapEntry* pEntry = pList->GetBitmap( i );
+ BitmapEx aBitmapEx = pEntry->GetGraphicObject().GetGraphic().GetBitmapEx();
+ formatBitmapExToSize(aBitmapEx, aSize);
+ pVD->DrawBitmapEx(Point(), aBitmapEx);
+ rBox.append("", pEntry->GetName(), *pVD);
+ }
+
+ rBox.thaw();
+}
+
+void SvxFillAttrBox::Fill(weld::ComboBox& rBox, const XPatternListRef &pList)
+{
+ if( !pList.is() )
+ return;
+
+ tools::Long nCount = pList->Count();
+ const StyleSettings& rStyleSettings = Application::GetSettings().GetStyleSettings();
+ const Size aSize(rStyleSettings.GetListBoxPreviewDefaultPixelSize());
+ ScopedVclPtrInstance< VirtualDevice > pVD;
+ pVD->SetOutputSizePixel(aSize, false);
+ rBox.freeze();
+
+ for( tools::Long i = 0; i < nCount; i++ )
+ {
+ const XBitmapEntry* pEntry = pList->GetBitmap( i );
+ BitmapEx aBitmapEx = pEntry->GetGraphicObject().GetGraphic().GetBitmapEx();
+ formatBitmapExToSize(aBitmapEx, aSize);
+ pVD->DrawBitmapEx(Point(), aBitmapEx);
+ rBox.append("", pEntry->GetName(), *pVD);
+ }
+
+ rBox.thaw();
+
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/tbxctrls/layctrl.cxx b/svx/source/tbxctrls/layctrl.cxx
new file mode 100644
index 000000000..79dd794fd
--- /dev/null
+++ b/svx/source/tbxctrls/layctrl.cxx
@@ -0,0 +1,798 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * 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/customweld.hxx>
+#include <vcl/event.hxx>
+#include <vcl/settings.hxx>
+#include <vcl/svapp.hxx>
+#include <vcl/toolbox.hxx>
+
+#include <svx/strings.hrc>
+#include <layctrl.hxx>
+#include <svx/dialmgr.hxx>
+#include <comphelper/processfactory.hxx>
+#include <comphelper/propertyvalue.hxx>
+#include <svtools/colorcfg.hxx>
+#include <svtools/toolbarmenu.hxx>
+#include <com/sun/star/util/URLTransformer.hpp>
+#include <com/sun/star/frame/XDispatchProvider.hpp>
+
+// namespaces
+using namespace ::com::sun::star::uno;
+using namespace ::com::sun::star::beans;
+using namespace ::com::sun::star::util;
+using namespace ::com::sun::star::frame;
+
+namespace {
+
+class TableWidget final : public weld::CustomWidgetController
+{
+private:
+ rtl::Reference<SvxTableToolBoxControl> mxControl;
+ OUString maCommand;
+
+ tools::Long nCol;
+ tools::Long nLine;
+
+ static const tools::Long TABLE_CELLS_HORIZ;
+ static const tools::Long TABLE_CELLS_VERT;
+
+ tools::Long mnTableCellWidth;
+ tools::Long mnTableCellHeight;
+
+ tools::Long mnTableWidth;
+ tools::Long mnTableHeight;
+
+ ::Color aFontColor;
+ ::Color aLineColor;
+ ::Color aFillColor;
+ ::Color aHighlightFillColor;
+ ::Color aBackgroundColor;
+
+ void Update(tools::Long nNewCol, tools::Long nNewLine);
+ void InsertTable();
+
+public:
+ TableWidget(SvxTableToolBoxControl* pControl, const OUString& rCommand);
+
+ virtual void SetDrawingArea(weld::DrawingArea* pDrawingArea) override;
+
+ virtual bool KeyInput(const KeyEvent&) override;
+ virtual bool MouseButtonDown(const MouseEvent&) override;
+ virtual bool MouseMove(const MouseEvent&) override;
+ virtual bool MouseButtonUp(const MouseEvent&) override;
+ virtual void Paint(vcl::RenderContext& rRenderContext, const tools::Rectangle& rRect) override;
+};
+
+class TableWindow final : public WeldToolbarPopup
+{
+private:
+ std::unique_ptr<weld::Button> mxTableButton;
+ std::unique_ptr<TableWidget> mxTableWidget;
+ std::unique_ptr<weld::CustomWeld> mxTableWidgetWin;
+ rtl::Reference<SvxTableToolBoxControl> mxControl;
+
+ DECL_LINK(SelectHdl, weld::Button&, void);
+
+public:
+ TableWindow( SvxTableToolBoxControl* pControl, weld::Widget* pParent,
+ const OUString& rCmd);
+ virtual void GrabFocus() override
+ {
+ mxTableWidget->GrabFocus();
+ }
+};
+
+}
+
+const tools::Long TableWidget::TABLE_CELLS_HORIZ = 10;
+const tools::Long TableWidget::TABLE_CELLS_VERT = 15;
+
+IMPL_LINK_NOARG(TableWindow, SelectHdl, weld::Button&, void)
+{
+ mxControl->CloseAndShowTableDialog();
+}
+
+TableWindow::TableWindow(SvxTableToolBoxControl* pControl, weld::Widget* pParent, const OUString& rCmd)
+ : WeldToolbarPopup(pControl->getFrameInterface(), pParent, "svx/ui/tablewindow.ui", "TableWindow")
+ , mxTableButton(m_xBuilder->weld_button("moreoptions"))
+ , mxTableWidget(new TableWidget(pControl, rCmd))
+ , mxTableWidgetWin(new weld::CustomWeld(*m_xBuilder, "table", *mxTableWidget))
+ , mxControl(pControl)
+{
+ mxTableButton->set_label( SvxResId( RID_SVXSTR_MORE ) );
+ mxTableButton->connect_clicked( LINK( this, TableWindow, SelectHdl ) );
+ mxTableButton->show();
+}
+
+TableWidget::TableWidget(SvxTableToolBoxControl* pControl, const OUString& rCommand)
+ : mxControl(pControl)
+ , maCommand(rCommand)
+ , nCol(0)
+ , nLine(0)
+ , mnTableCellWidth(0)
+ , mnTableCellHeight(0)
+ , mnTableWidth(0)
+ , mnTableHeight(0)
+{
+ const StyleSettings& rStyles = Application::GetSettings().GetStyleSettings();
+ aFontColor = rStyles.GetLabelTextColor();
+ aLineColor = rStyles.GetShadowColor();
+ aFillColor = rStyles.GetWindowColor();
+ aHighlightFillColor = rStyles.GetHighlightColor();
+ aBackgroundColor = rStyles.GetFaceColor();
+}
+
+void TableWidget::SetDrawingArea(weld::DrawingArea* pDrawingArea)
+{
+ float fScaleFactor = pDrawingArea->get_ref_device().GetDPIScaleFactor();
+
+ mnTableCellWidth = 15 * fScaleFactor;
+ mnTableCellHeight = 15 * fScaleFactor;
+
+ mnTableWidth = TABLE_CELLS_HORIZ*mnTableCellWidth;
+ mnTableHeight = TABLE_CELLS_VERT*mnTableCellHeight;
+
+ // + 1 to leave space to draw the right/bottom borders
+ Size aSize(mnTableWidth + 1, mnTableHeight + 1);
+ pDrawingArea->set_size_request(aSize.Width(), aSize.Height());
+ CustomWidgetController::SetDrawingArea(pDrawingArea);
+ SetOutputSizePixel(aSize);
+}
+
+bool TableWidget::MouseMove(const MouseEvent& rMEvt)
+{
+ Point aPos = rMEvt.GetPosPixel();
+ Point aMousePos( aPos );
+
+ tools::Long nNewCol = ( aMousePos.X() + mnTableCellWidth ) / mnTableCellWidth;
+ tools::Long nNewLine = ( aMousePos.Y() + mnTableCellHeight ) / mnTableCellHeight;
+
+ Update( nNewCol, nNewLine );
+
+ return true;
+}
+
+bool TableWidget::KeyInput(const KeyEvent& rKEvt)
+{
+ bool bHandled = false;
+ sal_uInt16 nModifier = rKEvt.GetKeyCode().GetModifier();
+ sal_uInt16 nKey = rKEvt.GetKeyCode().GetCode();
+ if ( !nModifier )
+ {
+ bHandled = true;
+ tools::Long nNewCol = nCol;
+ tools::Long nNewLine = nLine;
+ switch(nKey)
+ {
+ case KEY_UP:
+ if ( nNewLine > 1 )
+ nNewLine--;
+ else
+ mxControl->EndPopupMode();
+ break;
+ case KEY_DOWN:
+ if ( nNewLine < TABLE_CELLS_VERT )
+ {
+ nNewLine++;
+ if ( nNewCol == 0 )
+ nNewCol = 1;
+ }
+ else
+ mxControl->CloseAndShowTableDialog();
+ break;
+ case KEY_LEFT:
+ if ( nNewCol > 1 )
+ nNewCol--;
+ else
+ mxControl->EndPopupMode();
+ break;
+ case KEY_RIGHT:
+ if ( nNewCol < TABLE_CELLS_HORIZ )
+ {
+ nNewCol++;
+ if ( nNewLine == 0 )
+ nNewLine = 1;
+ }
+ else
+ mxControl->CloseAndShowTableDialog();
+ break;
+ case KEY_ESCAPE:
+ mxControl->EndPopupMode();
+ break;
+ case KEY_RETURN:
+ InsertTable();
+ mxControl->EndPopupMode();
+ return true;
+ default:
+ bHandled = false;
+ }
+ if ( bHandled )
+ {
+ Update( nNewCol, nNewLine );
+ }
+ }
+ else if (KEY_MOD1 == nModifier && KEY_RETURN == nKey)
+ {
+ InsertTable();
+ mxControl->EndPopupMode();
+ return true;
+ }
+
+ return bHandled;
+}
+
+bool TableWidget::MouseButtonUp(const MouseEvent&)
+{
+ InsertTable();
+ mxControl->EndPopupMode();
+ return true;
+}
+
+bool TableWidget::MouseButtonDown(const MouseEvent&)
+{
+ return true;
+}
+
+void TableWidget::Paint(vcl::RenderContext& rRenderContext, const tools::Rectangle&)
+{
+ rRenderContext.Push(vcl::PushFlags::FONT);
+
+ rRenderContext.SetBackground( aBackgroundColor );
+ vcl::Font aFont = rRenderContext.GetFont();
+ aFont.SetColor( aFontColor );
+ aFont.SetFillColor( aBackgroundColor );
+ aFont.SetTransparent( false );
+ rRenderContext.SetFont( aFont );
+
+ const tools::Long nSelectionWidth = nCol * mnTableCellWidth;
+ const tools::Long nSelectionHeight = nLine * mnTableCellHeight;
+
+ // the non-selected parts of the table
+ rRenderContext.SetLineColor(aLineColor);
+ rRenderContext.SetFillColor(aFillColor);
+ rRenderContext.DrawRect(tools::Rectangle(nSelectionWidth, 0, mnTableWidth, nSelectionHeight));
+ rRenderContext.DrawRect(tools::Rectangle(0, nSelectionHeight, nSelectionWidth, mnTableHeight));
+ rRenderContext.DrawRect(tools::Rectangle(nSelectionWidth, nSelectionHeight, mnTableWidth, mnTableHeight));
+
+ // the selection
+ if (nCol > 0 && nLine > 0)
+ {
+ rRenderContext.SetFillColor(aHighlightFillColor);
+ rRenderContext.DrawRect(tools::Rectangle(0, 0, nSelectionWidth, nSelectionHeight));
+ }
+
+ // lines inside of the table
+ rRenderContext.SetLineColor(aLineColor);
+ for (tools::Long i = 1; i < TABLE_CELLS_VERT; ++i)
+ {
+ rRenderContext.DrawLine(Point(0, i*mnTableCellHeight),
+ Point(mnTableWidth, i*mnTableCellHeight));
+ }
+
+ for (tools::Long i = 1; i < TABLE_CELLS_HORIZ; ++i)
+ {
+ rRenderContext.DrawLine(Point( i*mnTableCellWidth, 0),
+ Point( i*mnTableCellWidth, mnTableHeight));
+ }
+
+ // the text near the mouse cursor telling the table dimensions
+ if (!nCol || !nLine)
+ {
+ rRenderContext.Pop();
+ return;
+ }
+
+ OUString aText = OUString::number( nCol ) + " x " + OUString::number( nLine );
+ if (maCommand == ".uno:ShowMultiplePages")
+ {
+ aText += " " + SvxResId(RID_SVXSTR_PAGES);
+ }
+
+ Size aTextSize(rRenderContext.GetTextWidth(aText), rRenderContext.GetTextHeight());
+
+ tools::Long nTextX = nSelectionWidth + mnTableCellWidth;
+ tools::Long nTextY = nSelectionHeight + mnTableCellHeight;
+ const tools::Long nTipBorder = 2;
+
+ if (aTextSize.Width() + mnTableCellWidth + 2 * nTipBorder < nSelectionWidth)
+ nTextX = nSelectionWidth - mnTableCellWidth - aTextSize.Width();
+
+ if (aTextSize.Height() + mnTableCellHeight + 2 * nTipBorder < nSelectionHeight)
+ nTextY = nSelectionHeight - mnTableCellHeight - aTextSize.Height();
+
+ rRenderContext.SetLineColor(aLineColor);
+ rRenderContext.SetFillColor(aBackgroundColor);
+ rRenderContext.DrawRect(tools::Rectangle(nTextX - 2 * nTipBorder,
+ nTextY - 2 * nTipBorder,
+ nTextX + aTextSize.Width() + nTipBorder,
+ nTextY + aTextSize.Height() + nTipBorder));
+
+ // #i95350# force RTL output
+ if (IsRTLEnabled())
+ aText = u"\u202D" + aText;
+
+ rRenderContext.DrawText(Point(nTextX, nTextY), aText);
+
+ rRenderContext.Pop();
+}
+
+void TableWidget::InsertTable()
+{
+ if (nCol && nLine)
+ {
+ Sequence< PropertyValue > aArgs{ comphelper::makePropertyValue("Columns", sal_Int16( nCol )),
+ comphelper::makePropertyValue("Rows", sal_Int16( nLine )) };
+
+ mxControl->TableDialog( aArgs );
+ }
+}
+
+void TableWidget::Update( tools::Long nNewCol, tools::Long nNewLine )
+{
+ if ( nNewCol < 0 || nNewCol > TABLE_CELLS_HORIZ )
+ nNewCol = 0;
+
+ if ( nNewLine < 0 || nNewLine > TABLE_CELLS_VERT )
+ nNewLine = 0;
+
+ if ( nNewCol != nCol || nNewLine != nLine )
+ {
+ nCol = nNewCol;
+ nLine = nNewLine;
+ Invalidate(tools::Rectangle(0, 0, mnTableWidth, mnTableHeight));
+ }
+}
+
+void SvxTableToolBoxControl::TableDialog( const Sequence< PropertyValue >& rArgs )
+{
+ Reference< XDispatchProvider > xDispatchProvider( m_xFrame, UNO_QUERY );
+ if ( xDispatchProvider.is() )
+ {
+ css::util::URL aTargetURL;
+ Reference < XURLTransformer > xTrans( URLTransformer::create(::comphelper::getProcessComponentContext()) );
+ aTargetURL.Complete = m_aCommandURL;
+ xTrans->parseStrict( aTargetURL );
+
+ Reference< XDispatch > xDispatch = xDispatchProvider->queryDispatch( aTargetURL, OUString(), 0 );
+ if ( xDispatch.is() )
+ xDispatch->dispatch( aTargetURL, rArgs );
+ }
+}
+
+void SvxTableToolBoxControl::CloseAndShowTableDialog()
+{
+ // close the toolbar tool
+ EndPopupMode();
+
+ // and open the table dialog instead
+ TableDialog( Sequence< PropertyValue >() );
+}
+
+namespace {
+
+class ColumnsWidget final : public weld::CustomWidgetController
+{
+private:
+ static constexpr tools::Long WIDTH = 5;
+
+ rtl::Reference<SvxColumnsToolBoxControl> mxControl;
+ weld::SpinButton& mrSpinButton;
+
+ ::Color aLineColor;
+ ::Color aHighlightLineColor;
+ ::Color aFillColor;
+ ::Color aHighlightFillColor;
+ ::Color aFaceColor;
+ tools::Long nCol;
+ tools::Long nMX;
+ bool m_bMod1;
+
+ DECL_LINK(ValueChangedHdl, weld::SpinButton&, void);
+ DECL_LINK(ActivateHdl, weld::Entry&, bool);
+
+ void InsertColumns();
+ void UpdateSize_Impl( tools::Long nNewCol );
+public:
+ ColumnsWidget(SvxColumnsToolBoxControl* pControl, weld::SpinButton& rSpinButton);
+
+ virtual void SetDrawingArea(weld::DrawingArea* pDrawingArea) override;
+
+ virtual bool KeyInput(const KeyEvent&) override;
+ virtual bool MouseButtonDown(const MouseEvent&) override;
+ virtual bool MouseMove(const MouseEvent&) override;
+ virtual bool MouseButtonUp(const MouseEvent&) override;
+ virtual void Paint(vcl::RenderContext& rRenderContext, const tools::Rectangle& rRect) override;
+};
+
+
+class ColumnsWindow final : public WeldToolbarPopup
+{
+private:
+ std::unique_ptr<weld::SpinButton> mxSpinButton;
+ std::unique_ptr<ColumnsWidget> mxColumnsWidget;
+ std::unique_ptr<weld::CustomWeld> mxColumnsWidgetWin;
+ rtl::Reference<SvxColumnsToolBoxControl> mxControl;
+
+public:
+ ColumnsWindow(SvxColumnsToolBoxControl* pControl, weld::Widget* pParent);
+
+ virtual void GrabFocus() override
+ {
+ mxColumnsWidget->GrabFocus();
+ }
+};
+
+}
+
+ColumnsWindow::ColumnsWindow(SvxColumnsToolBoxControl* pControl, weld::Widget* pParent)
+ : WeldToolbarPopup(pControl->getFrameInterface(), pParent, "svx/ui/columnswindow.ui", "ColumnsWindow")
+ , mxSpinButton(m_xBuilder->weld_spin_button("spinbutton"))
+ , mxColumnsWidget(new ColumnsWidget(pControl, *mxSpinButton))
+ , mxColumnsWidgetWin(new weld::CustomWeld(*m_xBuilder, "columns", *mxColumnsWidget))
+ , mxControl(pControl)
+{
+}
+
+ColumnsWidget::ColumnsWidget(SvxColumnsToolBoxControl* pControl, weld::SpinButton& rSpinButton)
+ : mxControl(pControl)
+ , mrSpinButton(rSpinButton)
+ , nCol(1)
+ , nMX(0)
+ , m_bMod1(false)
+{
+ mrSpinButton.connect_value_changed(LINK(this, ColumnsWidget, ValueChangedHdl));
+ mrSpinButton.connect_activate(LINK(this, ColumnsWidget, ActivateHdl));
+
+ const StyleSettings& rStyles = Application::GetSettings().GetStyleSettings();
+ aLineColor = rStyles.GetLabelTextColor();
+ aHighlightLineColor = rStyles.GetHighlightTextColor();
+ aFillColor = rStyles.GetWindowColor();
+ aHighlightFillColor = rStyles.GetHighlightColor();
+ aFaceColor = rStyles.GetFaceColor();
+}
+
+IMPL_LINK_NOARG(ColumnsWidget, ValueChangedHdl, weld::SpinButton&, void)
+{
+ UpdateSize_Impl(mrSpinButton.get_value());
+}
+
+IMPL_LINK_NOARG(ColumnsWidget, ActivateHdl, weld::Entry&, bool)
+{
+ InsertColumns();
+ mxControl->EndPopupMode();
+ return true;
+}
+
+void ColumnsWidget::SetDrawingArea(weld::DrawingArea* pDrawingArea)
+{
+ OutputDevice& rDevice = pDrawingArea->get_ref_device();
+ Size aLogicSize = rDevice.LogicToPixel( Size( 95, 155 ), MapMode( MapUnit::Map10thMM ) );
+ nMX = aLogicSize.Width();
+ Size aSize(nMX*WIDTH-1, aLogicSize.Height());
+ pDrawingArea->set_size_request(aSize.Width(), aSize.Height());
+ CustomWidgetController::SetDrawingArea(pDrawingArea);
+ SetOutputSizePixel(aSize);
+}
+
+bool ColumnsWidget::MouseMove(const MouseEvent& rMEvt)
+{
+ Point aPos = rMEvt.GetPosPixel();
+
+ tools::Long nNewCol = 1;
+ if ( aPos.X() > 0 )
+ nNewCol = aPos.X() / nMX + 1;
+ if ( nNewCol > 20 )
+ nNewCol = 20;
+ UpdateSize_Impl( nNewCol );
+
+ return true;
+}
+
+void ColumnsWidget::UpdateSize_Impl( tools::Long nNewCol )
+{
+ if ( nNewCol == nCol )
+ return;
+
+ Size aWinSize = GetOutputSizePixel();
+
+ Invalidate( tools::Rectangle( 0, aWinSize.Height() - 2,
+ aWinSize.Width(), aWinSize.Height() ) );
+
+ tools::Long nMinCol = 0, nMaxCol = 0;
+
+ if ( nNewCol < nCol )
+ {
+ nMinCol = nNewCol;
+ nMaxCol = nCol;
+ }
+ else
+ {
+ nMinCol = nCol;
+ nMaxCol = nNewCol;
+ }
+
+ Invalidate( tools::Rectangle( nMinCol*nMX-1, 0,
+ nMaxCol*nMX+1, aWinSize.Height() - 2 ) );
+ nCol = nNewCol;
+ mrSpinButton.set_value(nCol);
+}
+
+bool ColumnsWidget::MouseButtonDown(const MouseEvent&)
+{
+ return true;
+}
+
+bool ColumnsWidget::KeyInput(const KeyEvent& rKEvt)
+{
+ bool bHandled = false;
+ sal_uInt16 nModifier = rKEvt.GetKeyCode().GetModifier();
+ sal_uInt16 nKey = rKEvt.GetKeyCode().GetCode();
+ if(!nModifier)
+ {
+ if( KEY_LEFT == nKey || KEY_RIGHT == nKey ||
+ KEY_RETURN == nKey ||KEY_ESCAPE == nKey ||
+ KEY_UP == nKey)
+ {
+ bHandled = true;
+ tools::Long nNewCol = nCol;
+ switch(nKey)
+ {
+ case KEY_LEFT :
+ if(nNewCol)
+ nNewCol--;
+ break;
+ case KEY_RIGHT :
+ nNewCol++;
+ break;
+ case KEY_RETURN :
+ InsertColumns();
+ mxControl->EndPopupMode();
+ break;
+ case KEY_ESCAPE :
+ case KEY_UP :
+ mxControl->EndPopupMode();
+ break;
+ }
+ UpdateSize_Impl( nNewCol );
+ }
+ }
+ else if(KEY_MOD1 == nModifier && KEY_RETURN == nKey)
+ {
+ m_bMod1 = true;
+ InsertColumns();
+ mxControl->EndPopupMode();
+ }
+ return bHandled;
+}
+
+bool ColumnsWidget::MouseButtonUp(const MouseEvent&)
+{
+ InsertColumns();
+ mxControl->EndPopupMode();
+ return true;
+}
+
+void ColumnsWidget::Paint(vcl::RenderContext& rRenderContext, const tools::Rectangle&)
+{
+ rRenderContext.Push(vcl::PushFlags::FONT);
+
+ rRenderContext.SetBackground();
+ vcl::Font aFont( rRenderContext.GetFont() );
+ aFont.SetColor( aLineColor );
+ aFont.SetFillColor( aFaceColor );
+ aFont.SetTransparent( false );
+ rRenderContext.SetFont( aFont );
+
+ tools::Long i;
+ tools::Long nLineWidth;
+ Size aSize(GetOutputSizePixel());
+
+ for (i = 0; i < WIDTH; i++)
+ {
+ if (i < nCol)
+ {
+ rRenderContext.SetLineColor(aHighlightLineColor);
+ rRenderContext.SetFillColor(aHighlightFillColor);
+ }
+ else
+ {
+ rRenderContext.SetLineColor(aLineColor);
+ rRenderContext.SetFillColor(aFillColor);
+ }
+
+ rRenderContext.DrawRect(tools::Rectangle(i * nMX - 1, -1, i * nMX + nMX, aSize.Height() - 1));
+
+ tools::Long j = 4;
+ while (j < aSize.Height() - 4)
+ {
+ if (!(j % 16))
+ nLineWidth = 10;
+ else
+ nLineWidth = 4;
+ rRenderContext.DrawLine(Point(i * nMX + 4, j), Point(i * nMX + nMX - nLineWidth - 4, j));
+ j += 4;
+ }
+ }
+
+ rRenderContext.SetLineColor();
+ rRenderContext.SetFillColor(aFaceColor);
+
+ rRenderContext.DrawRect(tools::Rectangle(0,
+ aSize.Height() - 2,
+ aSize.Width() / 2 - 1,
+ aSize.Height()));
+
+ rRenderContext.DrawRect(tools::Rectangle(aSize.Width() / 2,
+ aSize.Height() - 2,
+ aSize.Width(),
+ aSize.Height()));
+
+ rRenderContext.SetLineColor(aLineColor);
+ rRenderContext.SetFillColor();
+ rRenderContext.DrawRect(tools::Rectangle( 0, 0, aSize.Width() - 1, aSize.Height() - 1));
+
+ rRenderContext.Pop();
+}
+
+void SvxColumnsToolBoxControl::InsertColumns(const Sequence< PropertyValue >& rArgs)
+{
+ Reference< XDispatchProvider > xDispatchProvider( m_xFrame, UNO_QUERY );
+ if ( xDispatchProvider.is() )
+ {
+ css::util::URL aTargetURL;
+ Reference < XURLTransformer > xTrans( URLTransformer::create(::comphelper::getProcessComponentContext()) );
+ aTargetURL.Complete = m_aCommandURL;
+ xTrans->parseStrict( aTargetURL );
+
+ Reference< XDispatch > xDispatch = xDispatchProvider->queryDispatch( aTargetURL, OUString(), 0 );
+ if ( xDispatch.is() )
+ xDispatch->dispatch( aTargetURL, rArgs );
+ }
+}
+
+void ColumnsWidget::InsertColumns()
+{
+ if (nCol)
+ {
+ Sequence< PropertyValue > aArgs{
+ comphelper::makePropertyValue("Columns", sal_Int16( nCol )),
+ comphelper::makePropertyValue("Modifier", sal_Int16( m_bMod1 ? KEY_MOD1 : 0 ))
+ };
+ mxControl->InsertColumns(aArgs);
+ }
+}
+
+SvxTableToolBoxControl::SvxTableToolBoxControl(const css::uno::Reference<css::uno::XComponentContext>& rContext)
+ : PopupWindowController(rContext, nullptr, OUString())
+{
+}
+
+void SvxTableToolBoxControl::initialize( const css::uno::Sequence< css::uno::Any >& rArguments )
+{
+ PopupWindowController::initialize(rArguments);
+
+ ToolBox* pToolBox = nullptr;
+ ToolBoxItemId nId;
+ if (getToolboxId(nId, &pToolBox))
+ pToolBox->SetItemBits(nId, ToolBoxItemBits::DROPDOWNONLY | pToolBox->GetItemBits(nId));
+}
+
+SvxTableToolBoxControl::~SvxTableToolBoxControl()
+{
+}
+
+std::unique_ptr<WeldToolbarPopup> SvxTableToolBoxControl::weldPopupWindow()
+{
+ return std::make_unique<TableWindow>(this, m_pToolbar, m_aCommandURL);
+}
+
+VclPtr<vcl::Window> SvxTableToolBoxControl::createVclPopupWindow( vcl::Window* pParent )
+{
+ ToolBox* pToolBox = nullptr;
+ ToolBoxItemId nId;
+ bool bToolBox = getToolboxId(nId, &pToolBox);
+
+ mxInterimPopover = VclPtr<InterimToolbarPopup>::Create(getFrameInterface(), pParent,
+ std::make_unique<TableWindow>(this, pParent->GetFrameWeld(), m_aCommandURL));
+
+ mxInterimPopover->SetText(bToolBox ? pToolBox->GetItemText(nId) : OUString());
+
+ mxInterimPopover->Show();
+
+ return mxInterimPopover;
+}
+
+OUString SvxTableToolBoxControl::getImplementationName()
+{
+ return "com.sun.star.comp.svx.TableToolBoxControl";
+}
+
+css::uno::Sequence<OUString> SvxTableToolBoxControl::getSupportedServiceNames()
+{
+ return { "com.sun.star.frame.ToolbarController" };
+}
+
+extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface *
+com_sun_star_comp_svx_TableToolBoxControl_get_implementation(
+ css::uno::XComponentContext* rContext,
+ css::uno::Sequence<css::uno::Any> const & )
+{
+ return cppu::acquire(new SvxTableToolBoxControl(rContext));
+}
+
+SvxColumnsToolBoxControl::SvxColumnsToolBoxControl(const css::uno::Reference<css::uno::XComponentContext>& rContext)
+ : PopupWindowController(rContext, nullptr, OUString())
+{
+}
+
+void SvxColumnsToolBoxControl::initialize( const css::uno::Sequence< css::uno::Any >& rArguments )
+{
+ PopupWindowController::initialize(rArguments);
+
+ ToolBox* pToolBox = nullptr;
+ ToolBoxItemId nId;
+ if (getToolboxId(nId, &pToolBox))
+ pToolBox->SetItemBits(nId, ToolBoxItemBits::DROPDOWNONLY | pToolBox->GetItemBits(nId));
+}
+
+SvxColumnsToolBoxControl::~SvxColumnsToolBoxControl()
+{
+}
+
+std::unique_ptr<WeldToolbarPopup> SvxColumnsToolBoxControl::weldPopupWindow()
+{
+ return std::make_unique<ColumnsWindow>(this, m_pToolbar);
+}
+
+VclPtr<vcl::Window> SvxColumnsToolBoxControl::createVclPopupWindow(vcl::Window* pParent)
+{
+ ToolBox* pToolBox = nullptr;
+ ToolBoxItemId nId;
+ bool bToolBox = getToolboxId(nId, &pToolBox);
+
+ mxInterimPopover = VclPtr<InterimToolbarPopup>::Create(getFrameInterface(), pParent,
+ std::make_unique<ColumnsWindow>(this, pParent->GetFrameWeld()));
+
+ mxInterimPopover->SetText(bToolBox ? pToolBox->GetItemText(nId) : OUString());
+
+ mxInterimPopover->Show();
+
+ return mxInterimPopover;
+}
+
+OUString SvxColumnsToolBoxControl::getImplementationName()
+{
+ return "com.sun.star.comp.svx.ColumnsToolBoxControl";
+}
+
+css::uno::Sequence<OUString> SvxColumnsToolBoxControl::getSupportedServiceNames()
+{
+ return { "com.sun.star.frame.ToolbarController" };
+}
+
+extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface *
+com_sun_star_comp_svx_ColumnsToolBoxControl_get_implementation(
+ css::uno::XComponentContext* rContext,
+ css::uno::Sequence<css::uno::Any> const & )
+{
+ return cppu::acquire(new SvxColumnsToolBoxControl(rContext));
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/tbxctrls/lboxctrl.cxx b/svx/source/tbxctrls/lboxctrl.cxx
new file mode 100644
index 000000000..8f628876f
--- /dev/null
+++ b/svx/source/tbxctrls/lboxctrl.cxx
@@ -0,0 +1,347 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <sal/config.h>
+
+#include <sal/types.h>
+#include <vcl/event.hxx>
+#include <vcl/toolbox.hxx>
+#include <sfx2/bindings.hxx>
+#include <svtools/toolbarmenu.hxx>
+#include <svx/dialmgr.hxx>
+#include <lboxctrl.hxx>
+#include <tools/urlobj.hxx>
+
+#include <svx/strings.hrc>
+
+#include <comphelper/processfactory.hxx>
+#include <comphelper/propertyvalue.hxx>
+
+#include <com/sun/star/util/URLTransformer.hpp>
+#include <com/sun/star/frame/XDispatchProvider.hpp>
+
+
+using namespace ::com::sun::star::uno;
+using namespace ::com::sun::star::beans;
+using namespace ::com::sun::star::util;
+using namespace ::com::sun::star::frame;
+
+class SvxPopupWindowListBox final : public WeldToolbarPopup
+{
+ rtl::Reference<SvxUndoRedoControl> m_xControl;
+ std::unique_ptr<weld::TreeView> m_xListBox;
+ std::unique_ptr<weld::TreeIter> m_xScratchIter;
+ int m_nSelectedRows;
+ int m_nVisRows;
+
+ void UpdateRow(int nRow);
+
+ DECL_LINK(KeyInputHdl, const KeyEvent&, bool);
+ DECL_LINK(ActivateHdl, weld::TreeView&, bool);
+ DECL_LINK(MouseMoveHdl, const MouseEvent&, bool);
+ DECL_LINK(MousePressHdl, const MouseEvent&, bool);
+ DECL_LINK(MouseReleaseHdl, const MouseEvent&, bool);
+
+public:
+ SvxPopupWindowListBox(SvxUndoRedoControl* pControl, weld::Widget* pParent,
+ const std::vector<OUString>& rUndoRedoList);
+
+ virtual void GrabFocus() override
+ {
+ m_xListBox->grab_focus();
+ }
+};
+
+SvxPopupWindowListBox::SvxPopupWindowListBox(SvxUndoRedoControl* pControl, weld::Widget* pParent,
+ const std::vector<OUString>& rUndoRedoList)
+ : WeldToolbarPopup(pControl->getFrameInterface(), pParent, "svx/ui/floatingundoredo.ui", "FloatingUndoRedo")
+ , m_xControl(pControl)
+ , m_xListBox(m_xBuilder->weld_tree_view("treeview"))
+ , m_xScratchIter(m_xListBox->make_iterator())
+ , m_nVisRows(10)
+{
+ m_xListBox->set_selection_mode(SelectionMode::Multiple);
+
+ for (const OUString& s : rUndoRedoList)
+ m_xListBox->append_text(s);
+ if (!rUndoRedoList.empty())
+ {
+ m_xListBox->set_cursor(0);
+ m_xListBox->select(0);
+ m_nSelectedRows = 1;
+ }
+ else
+ m_nSelectedRows = 0;
+
+ m_xListBox->set_size_request(m_xListBox->get_approximate_digit_width() * 25,
+ m_xListBox->get_height_rows(m_nVisRows) + 2);
+
+ m_xListBox->connect_row_activated(LINK(this, SvxPopupWindowListBox, ActivateHdl));
+ m_xListBox->connect_mouse_move(LINK(this, SvxPopupWindowListBox, MouseMoveHdl));
+ m_xListBox->connect_mouse_press(LINK(this, SvxPopupWindowListBox, MousePressHdl));
+ m_xListBox->connect_mouse_release(LINK(this, SvxPopupWindowListBox, MouseReleaseHdl));
+ m_xListBox->connect_key_press(LINK(this, SvxPopupWindowListBox, KeyInputHdl));
+}
+
+void SvxUndoRedoControl::SetInfo( sal_Int32 nCount )
+{
+ TranslateId pId;
+ if (nCount == 1)
+ pId = getCommandURL() == ".uno:Undo" ? RID_SVXSTR_NUM_UNDO_ACTION : RID_SVXSTR_NUM_REDO_ACTION;
+ else
+ pId = getCommandURL() == ".uno:Undo" ? RID_SVXSTR_NUM_UNDO_ACTIONS : RID_SVXSTR_NUM_REDO_ACTIONS;
+ OUString aActionStr = SvxResId(pId);
+ OUString aText = aActionStr.replaceAll("$(ARG1)", OUString::number(nCount));
+ SetText(aText);
+}
+
+void SvxPopupWindowListBox::UpdateRow(int nRow)
+{
+ int nOldSelectedRows = m_nSelectedRows;
+ while (m_nSelectedRows < nRow + 1)
+ {
+ m_xListBox->select(m_nSelectedRows++);
+ }
+ while (m_nSelectedRows - 1 > nRow)
+ {
+ m_xListBox->unselect(--m_nSelectedRows);
+ }
+ if (nOldSelectedRows != m_nSelectedRows)
+ m_xControl->SetInfo(m_nSelectedRows);
+}
+
+IMPL_LINK(SvxPopupWindowListBox, MouseMoveHdl, const MouseEvent&, rMEvt, bool)
+{
+ if (m_xListBox->get_dest_row_at_pos(rMEvt.GetPosPixel(), m_xScratchIter.get(), false))
+ UpdateRow(m_xListBox->get_iter_index_in_parent(*m_xScratchIter));
+ return false;
+}
+
+IMPL_LINK(SvxPopupWindowListBox, MousePressHdl, const MouseEvent&, rMEvt, bool)
+{
+ if (m_xListBox->get_dest_row_at_pos(rMEvt.GetPosPixel(), m_xScratchIter.get(), false))
+ {
+ UpdateRow(m_xListBox->get_iter_index_in_parent(*m_xScratchIter));
+ ActivateHdl(*m_xListBox);
+ }
+ return true;
+}
+
+IMPL_LINK(SvxPopupWindowListBox, MouseReleaseHdl, const MouseEvent&, rMEvt, bool)
+{
+ if (m_xListBox->get_dest_row_at_pos(rMEvt.GetPosPixel(), m_xScratchIter.get(), false))
+ UpdateRow(m_xListBox->get_iter_index_in_parent(*m_xScratchIter));
+ return true;
+}
+
+IMPL_LINK(SvxPopupWindowListBox, KeyInputHdl, const KeyEvent&, rKEvt, bool)
+{
+ const vcl::KeyCode& rKCode = rKEvt.GetKeyCode();
+ if (rKCode.GetModifier()) // only with no modifiers held
+ return true;
+
+ sal_uInt16 nCode = rKCode.GetCode();
+
+ if (nCode == KEY_UP || nCode == KEY_PAGEUP ||
+ nCode == KEY_DOWN || nCode == KEY_PAGEDOWN)
+ {
+ sal_Int32 nIndex = m_nSelectedRows - 1;
+ sal_Int32 nOrigIndex = nIndex;
+ sal_Int32 nCount = m_xListBox->n_children();
+
+ if (nCode == KEY_UP)
+ --nIndex;
+ else if (nCode == KEY_DOWN)
+ ++nIndex;
+ else if (nCode == KEY_PAGEUP)
+ nIndex -= m_nVisRows;
+ else if (nCode == KEY_PAGEDOWN)
+ nIndex += m_nVisRows;
+
+ if (nIndex < 0)
+ nIndex = 0;
+ if (nIndex >= nCount)
+ nIndex = nCount - 1;
+
+ if (nIndex != nOrigIndex)
+ {
+ m_xListBox->scroll_to_row(nIndex);
+ if (nIndex > nOrigIndex)
+ {
+ for (int i = nOrigIndex + 1; i <= nIndex; ++i)
+ UpdateRow(i);
+ }
+ else
+ {
+ for (int i = nOrigIndex - 1; i >= nIndex; --i)
+ UpdateRow(i);
+ }
+ }
+ return true;
+ }
+
+ return false;
+}
+
+IMPL_LINK_NOARG(SvxPopupWindowListBox, ActivateHdl, weld::TreeView&, bool)
+{
+ m_xControl->Do(m_nSelectedRows);
+ m_xControl->EndPopupMode();
+ return true;
+}
+
+void SvxUndoRedoControl::Do(sal_Int16 nCount)
+{
+ Reference< XDispatchProvider > xDispatchProvider( m_xFrame, UNO_QUERY );
+ if ( !xDispatchProvider.is() )
+ return;
+
+ css::util::URL aTargetURL;
+ Reference < XURLTransformer > xTrans( URLTransformer::create(::comphelper::getProcessComponentContext()) );
+ aTargetURL.Complete = m_aCommandURL;
+ xTrans->parseStrict( aTargetURL );
+
+ Reference< XDispatch > xDispatch = xDispatchProvider->queryDispatch( aTargetURL, OUString(), 0 );
+ if ( xDispatch.is() )
+ {
+ INetURLObject aObj( m_aCommandURL );
+ Sequence< PropertyValue > aArgs{ comphelper::makePropertyValue(aObj.GetURLPath(), nCount) };
+ xDispatch->dispatch(aTargetURL, aArgs);
+ }
+}
+
+SvxUndoRedoControl::SvxUndoRedoControl(const css::uno::Reference<css::uno::XComponentContext>& rContext)
+ : PopupWindowController(rContext, nullptr, OUString())
+{
+}
+
+void SvxUndoRedoControl::initialize( const css::uno::Sequence< css::uno::Any >& rArguments )
+{
+ PopupWindowController::initialize(rArguments);
+
+ ToolBox* pToolBox = nullptr;
+ ToolBoxItemId nId;
+ if (!getToolboxId(nId, &pToolBox) && !m_pToolbar)
+ return;
+
+ if (getModuleName() != "com.sun.star.script.BasicIDE")
+ {
+ if (pToolBox)
+ pToolBox->SetItemBits(nId, ToolBoxItemBits::DROPDOWN | pToolBox->GetItemBits(nId));
+ if (m_pToolbar)
+ aDefaultTooltip = m_pToolbar->get_item_tooltip_text(m_aCommandURL.toUtf8());
+ else
+ aDefaultTooltip = pToolBox->GetQuickHelpText(nId);
+ }
+}
+
+SvxUndoRedoControl::~SvxUndoRedoControl()
+{
+}
+
+void SvxUndoRedoControl::SetText(const OUString& rText)
+{
+ mxInterimPopover->SetText(rText);
+}
+
+// XStatusListener
+void SAL_CALL SvxUndoRedoControl::statusChanged(const css::frame::FeatureStateEvent& rEvent)
+{
+ if (rEvent.FeatureURL.Main == ".uno:GetUndoStrings" || rEvent.FeatureURL.Main == ".uno:GetRedoStrings")
+ {
+ css::uno::Sequence<OUString> aStrings;
+ rEvent.State >>= aStrings;
+ aUndoRedoList = comphelper::sequenceToContainer<std::vector<OUString>>(aStrings);
+ return;
+ }
+
+ PopupWindowController::statusChanged(rEvent);
+
+ ToolBox* pToolBox = nullptr;
+ ToolBoxItemId nId;
+ if (!getToolboxId(nId, &pToolBox) && !m_pToolbar)
+ return;
+
+ if (!rEvent.IsEnabled)
+ {
+ if (m_pToolbar)
+ m_pToolbar->set_item_tooltip_text(m_aCommandURL.toUtf8(), aDefaultTooltip);
+ else
+ pToolBox->SetQuickHelpText(nId, aDefaultTooltip);
+ return;
+ }
+
+ OUString aQuickHelpText;
+ if (rEvent.State >>= aQuickHelpText)
+ {
+ if (m_pToolbar)
+ m_pToolbar->set_item_tooltip_text(m_aCommandURL.toUtf8(), aQuickHelpText);
+ else
+ pToolBox->SetQuickHelpText(nId, aQuickHelpText);
+ }
+}
+
+std::unique_ptr<WeldToolbarPopup> SvxUndoRedoControl::weldPopupWindow()
+{
+ if ( m_aCommandURL == ".uno:Undo" )
+ updateStatus( ".uno:GetUndoStrings");
+ else
+ updateStatus( ".uno:GetRedoStrings");
+
+ return std::make_unique<SvxPopupWindowListBox>(this, m_pToolbar, aUndoRedoList);
+}
+
+VclPtr<vcl::Window> SvxUndoRedoControl::createVclPopupWindow( vcl::Window* pParent )
+{
+ if ( m_aCommandURL == ".uno:Undo" )
+ updateStatus( ".uno:GetUndoStrings");
+ else
+ updateStatus( ".uno:GetRedoStrings");
+
+ auto xPopupWin = std::make_unique<SvxPopupWindowListBox>(this, pParent->GetFrameWeld(), aUndoRedoList);
+
+ mxInterimPopover = VclPtr<InterimToolbarPopup>::Create(getFrameInterface(), pParent,
+ std::move(xPopupWin));
+
+ SetInfo(1); // count of selected rows
+
+ mxInterimPopover->Show();
+
+ return mxInterimPopover;
+}
+
+OUString SvxUndoRedoControl::getImplementationName()
+{
+ return "com.sun.star.comp.svx.UndoRedoToolBoxControl";
+}
+
+css::uno::Sequence<OUString> SvxUndoRedoControl::getSupportedServiceNames()
+{
+ return { "com.sun.star.frame.ToolbarController" };
+}
+
+extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface *
+com_sun_star_comp_svx_UndoRedoToolBoxControl_get_implementation(
+ css::uno::XComponentContext* rContext,
+ css::uno::Sequence<css::uno::Any> const & )
+{
+ return cppu::acquire(new SvxUndoRedoControl(rContext));
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/tbxctrls/linectrl.cxx b/svx/source/tbxctrls/linectrl.cxx
new file mode 100644
index 000000000..9fcaeb22b
--- /dev/null
+++ b/svx/source/tbxctrls/linectrl.cxx
@@ -0,0 +1,650 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * 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/debug.hxx>
+#include <vcl/settings.hxx>
+#include <vcl/svapp.hxx>
+#include <vcl/toolbox.hxx>
+#include <vcl/virdev.hxx>
+#include <sfx2/objsh.hxx>
+
+#include <svtools/toolbarmenu.hxx>
+#include <svtools/popupwindowcontroller.hxx>
+#include <svtools/valueset.hxx>
+
+#include <svx/strings.hrc>
+#include <svx/svxids.hrc>
+#include <helpids.h>
+
+#include <svx/drawitem.hxx>
+#include <svx/xlineit0.hxx>
+#include <svx/xlncapit.hxx>
+#include <svx/xlndsit.hxx>
+#include <svx/xlnstit.hxx>
+#include <svx/xlnedit.hxx>
+#include <svx/xtable.hxx>
+#include <svx/linectrl.hxx>
+#include <svx/itemwin.hxx>
+#include <svx/dialmgr.hxx>
+#include <tbxcolorupdate.hxx>
+
+#include <memory>
+
+#include <comphelper/lok.hxx>
+#include <comphelper/propertyvalue.hxx>
+
+using namespace ::com::sun::star::uno;
+using namespace ::com::sun::star::beans;
+using namespace ::com::sun::star::util;
+using namespace ::com::sun::star::frame;
+using namespace ::com::sun::star::lang;
+using namespace ::com::sun::star;
+
+// For End Line Controller
+#define MAX_LINES 12
+
+SvxLineStyleToolBoxControl::SvxLineStyleToolBoxControl( const css::uno::Reference<css::uno::XComponentContext>& rContext )
+ : svt::PopupWindowController( rContext, nullptr, OUString() )
+{
+ addStatusListener(".uno:LineDash");
+}
+
+SvxLineStyleToolBoxControl::~SvxLineStyleToolBoxControl()
+{
+}
+
+void SAL_CALL SvxLineStyleToolBoxControl::statusChanged( const frame::FeatureStateEvent& rEvent )
+{
+ ToolBox* pToolBox = nullptr;
+ ToolBoxItemId nId;
+ if (!getToolboxId(nId, &pToolBox) && !m_pToolbar)
+ return;
+
+ OString sId(m_aCommandURL.toUtf8());
+
+ if ( rEvent.FeatureURL.Complete == m_aCommandURL )
+ {
+ if (m_pToolbar)
+ m_pToolbar->set_item_sensitive(sId, rEvent.IsEnabled);
+ else
+ pToolBox->EnableItem( nId, rEvent.IsEnabled );
+ }
+
+ m_xBtnUpdater->Update(rEvent);
+
+ SfxObjectShell* pSh = SfxObjectShell::Current();
+ if (!pSh)
+ return;
+
+ const SvxDashListItem* pItem = pSh->GetItem( SID_DASH_LIST );
+ if (!pItem)
+ return;
+
+ XDashListRef xList = pItem->GetDashList();
+ int nIndex = m_xBtnUpdater->GetStyleIndex();
+ bool bNoneLineStyle = false;
+ switch (nIndex)
+ {
+ case -1:
+ case 0:
+ {
+ BitmapEx aEmpty(xList->GetBitmapForUISolidLine());
+ aEmpty.Erase(Application::GetSettings().GetStyleSettings().GetFieldColor());
+ if (m_pToolbar)
+ {
+ Graphic aGraf(aEmpty);
+ m_pToolbar->set_item_image(sId, aGraf.GetXGraphic());
+ }
+ else
+ pToolBox->SetItemImage(nId, Image(aEmpty));
+ bNoneLineStyle = true;
+ break;
+ }
+ case 1:
+ if (m_pToolbar)
+ {
+ Graphic aGraf(xList->GetBitmapForUISolidLine());
+ m_pToolbar->set_item_image(sId, aGraf.GetXGraphic());
+ }
+ else
+ pToolBox->SetItemImage(nId, Image(xList->GetBitmapForUISolidLine()));
+ break;
+ default:
+ if (m_pToolbar)
+ {
+ Graphic aGraf(xList->GetUiBitmap(nIndex - 2));
+ m_pToolbar->set_item_image(sId, aGraf.GetXGraphic());
+ }
+ else
+ pToolBox->SetItemImage(nId, Image(xList->GetUiBitmap(nIndex - 2)));
+ break;
+ }
+ if (m_aLineStyleIsNoneFunction)
+ m_aLineStyleIsNoneFunction(bNoneLineStyle);
+}
+
+void SAL_CALL SvxLineStyleToolBoxControl::execute(sal_Int16 /*KeyModifier*/)
+{
+ if (m_pToolbar)
+ {
+ // Toggle the popup also when toolbutton is activated
+ const OString aId(m_aCommandURL.toUtf8());
+ m_pToolbar->set_menu_item_active(aId, !m_pToolbar->get_menu_item_active(aId));
+ }
+ else
+ {
+ // Open the popup also when Enter key is pressed.
+ createPopupWindow();
+ }
+}
+
+void SvxLineStyleToolBoxControl::initialize( const css::uno::Sequence<css::uno::Any>& rArguments )
+{
+ svt::PopupWindowController::initialize( rArguments );
+
+ if (m_pToolbar)
+ {
+ mxPopoverContainer.reset(new ToolbarPopupContainer(m_pToolbar));
+ m_pToolbar->set_item_popover(m_aCommandURL.toUtf8(), mxPopoverContainer->getTopLevel());
+ }
+
+ ToolBox* pToolBox = nullptr;
+ ToolBoxItemId nId;
+ if ( getToolboxId( nId, &pToolBox ) )
+ {
+ pToolBox->SetItemBits( nId, pToolBox->GetItemBits( nId ) | ToolBoxItemBits::DROPDOWNONLY );
+ }
+
+ m_xBtnUpdater.reset(new svx::ToolboxButtonLineStyleUpdater);
+}
+
+void SvxLineStyleToolBoxControl::setLineStyleSelectFunction(const LineStyleSelectFunction& rLineStyleSelectFunction)
+{
+ m_aLineStyleSelectFunction = rLineStyleSelectFunction;
+}
+
+void SvxLineStyleToolBoxControl::setLineStyleIsNoneFunction(const LineStyleIsNoneFunction& rLineStyleIsNoneFunction)
+{
+ m_aLineStyleIsNoneFunction = rLineStyleIsNoneFunction;
+}
+
+void SvxLineStyleToolBoxControl::dispatchLineStyleCommand(const OUString& rCommand, const Sequence<PropertyValue>& rArgs)
+{
+ if (m_aLineStyleSelectFunction && m_aLineStyleSelectFunction(rCommand, rArgs[0].Value))
+ return;
+
+ dispatchCommand(rCommand, rArgs);
+}
+
+std::unique_ptr<WeldToolbarPopup> SvxLineStyleToolBoxControl::weldPopupWindow()
+{
+ return std::make_unique<SvxLineBox>(this, m_pToolbar, m_xBtnUpdater->GetStyleIndex());
+}
+
+VclPtr<vcl::Window> SvxLineStyleToolBoxControl::createVclPopupWindow( vcl::Window* pParent )
+{
+ mxInterimPopover = VclPtr<InterimToolbarPopup>::Create(getFrameInterface(), pParent,
+ std::make_unique<SvxLineBox>(this, pParent->GetFrameWeld(), m_xBtnUpdater->GetStyleIndex()));
+
+ mxInterimPopover->Show();
+
+ return mxInterimPopover;
+}
+
+OUString SvxLineStyleToolBoxControl::getImplementationName()
+{
+ return "com.sun.star.comp.svx.LineStyleToolBoxControl";
+}
+
+css::uno::Sequence<OUString> SvxLineStyleToolBoxControl::getSupportedServiceNames()
+{
+ return { "com.sun.star.frame.ToolbarController" };
+}
+
+extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface *
+com_sun_star_comp_svx_LineStyleToolBoxControl_get_implementation(
+ css::uno::XComponentContext* rContext,
+ css::uno::Sequence<css::uno::Any> const & )
+{
+ return cppu::acquire( new SvxLineStyleToolBoxControl( rContext ) );
+}
+
+namespace {
+
+class SvxLineEndToolBoxControl final : public svt::PopupWindowController
+{
+public:
+ explicit SvxLineEndToolBoxControl( const css::uno::Reference<css::uno::XComponentContext>& rContext );
+
+ // XInitialization
+ virtual void SAL_CALL initialize( const css::uno::Sequence<css::uno::Any>& rArguments ) override;
+
+ // XServiceInfo
+ virtual OUString SAL_CALL getImplementationName() override;
+ virtual css::uno::Sequence<OUString> SAL_CALL getSupportedServiceNames() override;
+
+ virtual void SAL_CALL execute(sal_Int16 nKeyModifier) override;
+
+private:
+ virtual std::unique_ptr<WeldToolbarPopup> weldPopupWindow() override;
+ virtual VclPtr<vcl::Window> createVclPopupWindow( vcl::Window* pParent ) override;
+};
+
+class SvxLineEndWindow final : public WeldToolbarPopup
+{
+private:
+ XLineEndListRef mpLineEndList;
+ rtl::Reference<SvxLineEndToolBoxControl> mxControl;
+ std::unique_ptr<ValueSet> mxLineEndSet;
+ std::unique_ptr<weld::CustomWeld> mxLineEndSetWin;
+ sal_uInt16 mnLines;
+ Size maBmpSize;
+
+ DECL_LINK( SelectHdl, ValueSet*, void );
+ void FillValueSet();
+ void SetSize();
+
+ virtual void GrabFocus() override
+ {
+ mxLineEndSet->GrabFocus();
+ }
+
+public:
+ SvxLineEndWindow(SvxLineEndToolBoxControl* pControl, weld::Widget* pParent);
+ virtual void statusChanged( const css::frame::FeatureStateEvent& rEvent ) override;
+};
+
+}
+
+constexpr sal_uInt16 gnCols = 2;
+
+SvxLineEndWindow::SvxLineEndWindow(SvxLineEndToolBoxControl* pControl, weld::Widget* pParent)
+ : WeldToolbarPopup(pControl->getFrameInterface(), pParent, "svx/ui/floatinglineend.ui", "FloatingLineEnd")
+ , mxControl(pControl)
+ , mxLineEndSet(new ValueSet(m_xBuilder->weld_scrolled_window("valuesetwin", true)))
+ , mxLineEndSetWin(new weld::CustomWeld(*m_xBuilder, "valueset", *mxLineEndSet))
+ , mnLines(12)
+{
+ mxLineEndSet->SetStyle(mxLineEndSet->GetStyle() | WB_ITEMBORDER | WB_3DLOOK | WB_NO_DIRECTSELECT);
+ mxLineEndSet->SetHelpId(HID_POPUP_LINEEND_CTRL);
+ m_xTopLevel->set_help_id(HID_POPUP_LINEEND);
+
+ SfxObjectShell* pDocSh = SfxObjectShell::Current();
+ if ( pDocSh )
+ {
+ const SfxPoolItem* pItem = pDocSh->GetItem( SID_LINEEND_LIST );
+ if( pItem )
+ mpLineEndList = static_cast<const SvxLineEndListItem*>( pItem )->GetLineEndList();
+ }
+ DBG_ASSERT( mpLineEndList.is(), "LineEndList not found" );
+
+ mxLineEndSet->SetSelectHdl( LINK( this, SvxLineEndWindow, SelectHdl ) );
+ mxLineEndSet->SetColCount( gnCols );
+
+ // ValueSet fill with entries of LineEndList
+ FillValueSet();
+
+ AddStatusListener( ".uno:LineEndListState");
+}
+
+IMPL_LINK_NOARG(SvxLineEndWindow, SelectHdl, ValueSet*, void)
+{
+ std::unique_ptr<XLineEndItem> pLineEndItem;
+ std::unique_ptr<XLineStartItem> pLineStartItem;
+ sal_uInt16 nId = mxLineEndSet->GetSelectedItemId();
+
+ if( nId == 1 )
+ {
+ pLineStartItem.reset(new XLineStartItem());
+ }
+ else if( nId == 2 )
+ {
+ pLineEndItem.reset(new XLineEndItem());
+ }
+ else if( nId % 2 ) // beginning of line
+ {
+ const XLineEndEntry* pEntry = mpLineEndList->GetLineEnd( (nId - 1) / 2 - 1 );
+ pLineStartItem.reset(new XLineStartItem(pEntry->GetName(), pEntry->GetLineEnd()));
+ }
+ else // end of line
+ {
+ const XLineEndEntry* pEntry = mpLineEndList->GetLineEnd( nId / 2 - 2 );
+ pLineEndItem.reset(new XLineEndItem(pEntry->GetName(), pEntry->GetLineEnd()));
+ }
+
+ OUString name;
+ Any a;
+
+ if ( pLineStartItem )
+ {
+ name = "LineStart";
+ pLineStartItem->QueryValue( a );
+ }
+ else
+ {
+ name = "LineEnd";
+ pLineEndItem->QueryValue( a );
+ }
+ Sequence< PropertyValue > aArgs{ comphelper::makePropertyValue(name, a) };
+
+ /* #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. */
+ mxLineEndSet->SetNoSelection();
+
+ mxControl->dispatchCommand(mxControl->getCommandURL(), aArgs);
+
+ mxControl->EndPopupMode();
+}
+
+void SvxLineEndWindow::FillValueSet()
+{
+ if( !mpLineEndList.is() )
+ return;
+
+ ScopedVclPtrInstance< VirtualDevice > pVD;
+
+ tools::Long nCount = mpLineEndList->Count();
+
+ // First entry: no line end.
+ // An entry is temporarily added to get the UI bitmap
+ basegfx::B2DPolyPolygon aNothing;
+ mpLineEndList->Insert(std::make_unique<XLineEndEntry>(aNothing,
+ comphelper::LibreOfficeKit::isActive() ? SvxResId(RID_SVXSTR_INVISIBLE)
+ : SvxResId(RID_SVXSTR_NONE)));
+ const XLineEndEntry* pEntry = mpLineEndList->GetLineEnd(nCount);
+ BitmapEx aBmp = mpLineEndList->GetUiBitmap( nCount );
+ OSL_ENSURE( !aBmp.IsEmpty(), "UI bitmap was not created" );
+
+ maBmpSize = aBmp.GetSizePixel();
+ pVD->SetOutputSizePixel( maBmpSize, false );
+ maBmpSize.setWidth( maBmpSize.Width() / 2 );
+ Point aPt0( 0, 0 );
+ Point aPt1( maBmpSize.Width(), 0 );
+
+ pVD->DrawBitmapEx( Point(), aBmp );
+ mxLineEndSet->InsertItem(1, Image(pVD->GetBitmapEx(aPt0, maBmpSize)), pEntry->GetName());
+ mxLineEndSet->InsertItem(2, Image(pVD->GetBitmapEx(aPt1, maBmpSize)), pEntry->GetName());
+
+ mpLineEndList->Remove(nCount);
+
+ for( tools::Long i = 0; i < nCount; i++ )
+ {
+ pEntry = mpLineEndList->GetLineEnd( i );
+ DBG_ASSERT( pEntry, "Could not access LineEndEntry" );
+ aBmp = mpLineEndList->GetUiBitmap( i );
+ OSL_ENSURE( !aBmp.IsEmpty(), "UI bitmap was not created" );
+
+ pVD->DrawBitmapEx( aPt0, aBmp );
+ mxLineEndSet->InsertItem(static_cast<sal_uInt16>((i+1)*2L+1),
+ Image(pVD->GetBitmapEx(aPt0, maBmpSize)), pEntry->GetName());
+ mxLineEndSet->InsertItem(static_cast<sal_uInt16>((i+2)*2L),
+ Image(pVD->GetBitmapEx(aPt1, maBmpSize)), pEntry->GetName());
+ }
+ mnLines = std::min( static_cast<sal_uInt16>(nCount + 1), sal_uInt16(MAX_LINES) );
+ mxLineEndSet->SetLineCount( mnLines );
+
+ SetSize();
+}
+
+void SvxLineEndWindow::statusChanged( const css::frame::FeatureStateEvent& rEvent )
+{
+ if ( rEvent.FeatureURL.Complete != ".uno:LineEndListState" )
+ return;
+
+ // The list of line ends (LineEndList) has changed
+ css::uno::Reference< css::uno::XWeak > xWeak;
+ if ( rEvent.State >>= xWeak )
+ {
+ mpLineEndList.set( static_cast< XLineEndList* >( xWeak.get() ) );
+ DBG_ASSERT( mpLineEndList.is(), "LineEndList not found" );
+
+ mxLineEndSet->Clear();
+ FillValueSet();
+ }
+}
+
+void SvxLineEndWindow::SetSize()
+{
+ sal_uInt16 nItemCount = mxLineEndSet->GetItemCount();
+ sal_uInt16 nMaxLines = nItemCount / gnCols;
+
+ WinBits nBits = mxLineEndSet->GetStyle();
+ if ( mnLines == nMaxLines )
+ nBits &= ~WB_VSCROLL;
+ else
+ nBits |= WB_VSCROLL;
+ mxLineEndSet->SetStyle( nBits );
+
+ Size aSize( maBmpSize );
+ aSize.AdjustWidth(6 );
+ aSize.AdjustHeight(6 );
+ aSize = mxLineEndSet->CalcWindowSizePixel( aSize );
+ mxLineEndSet->GetDrawingArea()->set_size_request(aSize.Width(), aSize.Height());
+ mxLineEndSet->SetOutputSizePixel(aSize);
+}
+
+SvxLineEndToolBoxControl::SvxLineEndToolBoxControl( const css::uno::Reference<css::uno::XComponentContext>& rContext )
+ : svt::PopupWindowController( rContext, nullptr, OUString() )
+{
+}
+
+void SAL_CALL SvxLineEndToolBoxControl::execute(sal_Int16 /*KeyModifier*/)
+{
+ if (m_pToolbar)
+ {
+ // Toggle the popup also when toolbutton is activated
+ const OString aId(m_aCommandURL.toUtf8());
+ m_pToolbar->set_menu_item_active(aId, !m_pToolbar->get_menu_item_active(aId));
+ }
+ else
+ {
+ // Open the popup also when Enter key is pressed.
+ createPopupWindow();
+ }
+}
+
+void SvxLineEndToolBoxControl::initialize( const css::uno::Sequence<css::uno::Any>& rArguments )
+{
+ svt::PopupWindowController::initialize( rArguments );
+
+ if (m_pToolbar)
+ {
+ mxPopoverContainer.reset(new ToolbarPopupContainer(m_pToolbar));
+ m_pToolbar->set_item_popover(m_aCommandURL.toUtf8(), mxPopoverContainer->getTopLevel());
+ }
+
+ ToolBox* pToolBox = nullptr;
+ ToolBoxItemId nId;
+ if ( getToolboxId( nId, &pToolBox ) )
+ pToolBox->SetItemBits( nId, pToolBox->GetItemBits( nId ) | ToolBoxItemBits::DROPDOWNONLY );
+}
+
+std::unique_ptr<WeldToolbarPopup> SvxLineEndToolBoxControl::weldPopupWindow()
+{
+ return std::make_unique<SvxLineEndWindow>(this, m_pToolbar);
+}
+
+VclPtr<vcl::Window> SvxLineEndToolBoxControl::createVclPopupWindow( vcl::Window* pParent )
+{
+ mxInterimPopover = VclPtr<InterimToolbarPopup>::Create(getFrameInterface(), pParent,
+ std::make_unique<SvxLineEndWindow>(this, pParent->GetFrameWeld()));
+
+ mxInterimPopover->Show();
+
+ mxInterimPopover->SetText(SvxResId(RID_SVXSTR_LINEEND));
+
+ return mxInterimPopover;
+}
+
+OUString SvxLineEndToolBoxControl::getImplementationName()
+{
+ return "com.sun.star.comp.svx.LineEndToolBoxControl";
+}
+
+css::uno::Sequence<OUString> SvxLineEndToolBoxControl::getSupportedServiceNames()
+{
+ return { "com.sun.star.frame.ToolbarController" };
+}
+
+extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface *
+com_sun_star_comp_svx_LineEndToolBoxControl_get_implementation(
+ css::uno::XComponentContext* rContext,
+ css::uno::Sequence<css::uno::Any> const & )
+{
+ return cppu::acquire( new SvxLineEndToolBoxControl( rContext ) );
+}
+
+SvxLineBox::SvxLineBox(SvxLineStyleToolBoxControl* pControl, weld::Widget* pParent, int nInitialIndex)
+ : WeldToolbarPopup(pControl->getFrameInterface(), pParent, "svx/ui/floatinglinestyle.ui", "FloatingLineStyle")
+ , mxControl(pControl)
+ , mxLineStyleSet(new ValueSet(m_xBuilder->weld_scrolled_window("valuesetwin", true)))
+ , mxLineStyleSetWin(new weld::CustomWeld(*m_xBuilder, "valueset", *mxLineStyleSet))
+{
+ mxLineStyleSet->SetStyle(WB_FLATVALUESET | WB_ITEMBORDER | WB_3DLOOK | WB_NO_DIRECTSELECT);
+
+ FillControl();
+
+ mxLineStyleSet->SelectItem(nInitialIndex + 1);
+
+ mxLineStyleSet->SetSelectHdl( LINK( this, SvxLineBox, SelectHdl ) );
+}
+
+void SvxLineBox::GrabFocus()
+{
+ mxLineStyleSet->GrabFocus();
+}
+
+SvxLineBox::~SvxLineBox()
+{
+}
+
+// Fills the listbox (provisional) with strings
+
+void SvxLineBox::Fill( const XDashListRef &pList )
+{
+ mxLineStyleSet->Clear();
+
+ if( !pList.is() )
+ return;
+
+ // entry for 'none'
+ mxLineStyleSet->InsertItem(1, Image(), pList->GetStringForUiNoLine());
+
+ // entry for solid line
+ auto aBmp = pList->GetBitmapForUISolidLine();
+ Size aBmpSize = aBmp.GetSizePixel();
+ mxLineStyleSet->InsertItem(2, Image(aBmp), pList->GetStringForUiSolidLine());
+
+ // entries for dashed lines
+ tools::Long nCount = pList->Count();
+ for( tools::Long i = 0; i < nCount; i++ )
+ {
+ const XDashEntry* pEntry = pList->GetDash(i);
+ const BitmapEx aBitmap = pList->GetUiBitmap(i);
+
+ mxLineStyleSet->InsertItem(i + 3, Image(aBitmap), pEntry->GetName());
+ }
+
+ sal_uInt16 nLines = std::min( static_cast<sal_uInt16>(nCount + 2), sal_uInt16(MAX_LINES) );
+ mxLineStyleSet->SetLineCount(nLines);
+
+ WinBits nBits = mxLineStyleSet->GetStyle();
+ if ( nLines == mxLineStyleSet->GetItemCount() )
+ nBits &= ~WB_VSCROLL;
+ else
+ nBits |= WB_VSCROLL;
+ mxLineStyleSet->SetStyle( nBits );
+
+ Size aSize(aBmpSize);
+ aSize.AdjustWidth(6);
+ aSize.AdjustHeight(6);
+ aSize = mxLineStyleSet->CalcWindowSizePixel(aSize);
+ mxLineStyleSet->GetDrawingArea()->set_size_request(aSize.Width(), aSize.Height());
+ mxLineStyleSet->SetOutputSizePixel(aSize);
+}
+
+IMPL_LINK_NOARG(SvxLineBox, SelectHdl, ValueSet*, void)
+{
+ drawing::LineStyle eXLS;
+ sal_Int32 nPos = mxLineStyleSet->GetSelectedItemId();
+ --nPos; // ids start at 1, get the pos of the id
+
+ switch ( nPos )
+ {
+ case 0:
+ eXLS = drawing::LineStyle_NONE;
+ break;
+
+ case 1:
+ eXLS = drawing::LineStyle_SOLID;
+ break;
+
+ default:
+ {
+ eXLS = drawing::LineStyle_DASH;
+ const SfxObjectShell* pObjSh = SfxObjectShell::Current();
+ if (nPos != -1 && pObjSh && pObjSh->GetItem(SID_DASH_LIST))
+ {
+ // LineDashItem will only be sent if it also has a dash.
+ // Notify cares!
+ SvxDashListItem const * pItem = pObjSh->GetItem( SID_DASH_LIST );
+ const XDashEntry* pEntry = pItem->GetDashList()->GetDash(nPos - 2);
+ XLineDashItem aLineDashItem(pEntry->GetName(), pEntry->GetDash());
+
+ Any a;
+ aLineDashItem.QueryValue ( a );
+ Sequence< PropertyValue > aArgs{ comphelper::makePropertyValue("LineDash", a) };
+ mxControl->dispatchLineStyleCommand(".uno:LineDash", aArgs);
+
+ // set also cap style using the toolbar line style selection popup
+ css::drawing::DashStyle eStyle = pEntry->GetDash().GetDashStyle();
+ XLineCapItem aLineCapItem(
+ eStyle == drawing::DashStyle_RECT || eStyle == drawing::DashStyle_RECTRELATIVE
+ ? css::drawing::LineCap_BUTT
+ : css::drawing::LineCap_ROUND );
+ aLineCapItem.QueryValue ( a );
+ Sequence< PropertyValue > aArgs2{ comphelper::makePropertyValue("LineCap", a) };
+ mxControl->dispatchLineStyleCommand(".uno:LineCap", aArgs2);
+ }
+ }
+ break;
+ }
+
+ XLineStyleItem aLineStyleItem( eXLS );
+ Any a;
+ aLineStyleItem.QueryValue ( a );
+ Sequence< PropertyValue > aArgs{ comphelper::makePropertyValue("XLineStyle", a) };
+ mxControl->dispatchLineStyleCommand(".uno:XLineStyle", aArgs);
+
+ mxControl->EndPopupMode();
+}
+
+void SvxLineBox::FillControl()
+{
+ SfxObjectShell* pSh = SfxObjectShell::Current();
+ if (pSh)
+ {
+ const SvxDashListItem* pItem = pSh->GetItem( SID_DASH_LIST );
+ if (pItem)
+ Fill(pItem->GetDashList());
+ }
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/tbxctrls/linemetricbox.hxx b/svx/source/tbxctrls/linemetricbox.hxx
new file mode 100644
index 000000000..81f19a1c6
--- /dev/null
+++ b/svx/source/tbxctrls/linemetricbox.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 .
+ */
+#pragma once
+
+#include <vcl/InterimItemWindow.hxx>
+#include <com/sun/star/frame/XFrame.hpp>
+
+class XLineWidthItem;
+
+class SvxMetricField final : public InterimItemWindow
+{
+private:
+ std::unique_ptr<weld::MetricSpinButton> m_xWidget;
+ int nCurValue;
+ MapUnit eDestPoolUnit;
+ FieldUnit eDlgUnit;
+ css::uno::Reference< css::frame::XFrame > mxFrame;
+
+ DECL_LINK(ModifyHdl, weld::MetricSpinButton&, void);
+ DECL_LINK(KeyInputHdl, const KeyEvent&, bool);
+ DECL_LINK(FocusInHdl, weld::Widget&, void);
+
+ static void ReleaseFocus_Impl();
+
+ virtual void DataChanged( const DataChangedEvent& rDCEvt ) override;
+
+public:
+ SvxMetricField( vcl::Window* pParent,
+ const css::uno::Reference< css::frame::XFrame >& rFrame );
+ virtual void dispose() override;
+ virtual ~SvxMetricField() override;
+
+ void Update( const XLineWidthItem* pItem );
+ void SetDestCoreUnit( MapUnit eUnit );
+ void RefreshDlgUnit();
+
+ void set_sensitive(bool bSensitive);
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/tbxctrls/linewidthctrl.cxx b/svx/source/tbxctrls/linewidthctrl.cxx
new file mode 100644
index 000000000..166f4bb45
--- /dev/null
+++ b/svx/source/tbxctrls/linewidthctrl.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 <vcl/toolbox.hxx>
+#include <sfx2/app.hxx>
+#include <sfx2/objsh.hxx>
+#include <svl/itempool.hxx>
+#include <svx/svxids.hrc>
+#include <svx/xlnwtit.hxx>
+#include <svx/linectrl.hxx>
+#include "linemetricbox.hxx"
+
+using namespace ::com::sun::star::uno;
+using namespace ::com::sun::star::beans;
+using namespace ::com::sun::star::util;
+using namespace ::com::sun::star::frame;
+using namespace ::com::sun::star::lang;
+using namespace ::com::sun::star;
+
+SFX_IMPL_TOOLBOX_CONTROL( SvxLineWidthToolBoxControl, XLineWidthItem );
+
+SvxLineWidthToolBoxControl::SvxLineWidthToolBoxControl(
+ sal_uInt16 nSlotId, ToolBoxItemId nId, ToolBox& rTbx ) :
+ SfxToolBoxControl( nSlotId, nId, rTbx )
+{
+ addStatusListener( ".uno:MetricUnit");
+}
+
+
+SvxLineWidthToolBoxControl::~SvxLineWidthToolBoxControl()
+{
+}
+
+void SvxLineWidthToolBoxControl::StateChangedAtToolBoxControl(
+ sal_uInt16 nSID, SfxItemState eState, const SfxPoolItem* pState )
+{
+ SvxMetricField* pFld = static_cast<SvxMetricField*>(
+ GetToolBox().GetItemWindow( GetId() ));
+ DBG_ASSERT( pFld, "Window not found" );
+
+ if ( nSID == SID_ATTR_METRIC )
+ {
+ pFld->RefreshDlgUnit();
+ }
+ else
+ {
+ if ( eState == SfxItemState::DISABLED )
+ {
+ pFld->set_sensitive(false);
+ }
+ else
+ {
+ pFld->set_sensitive(true);
+
+ if ( eState == SfxItemState::DEFAULT )
+ {
+ DBG_ASSERT( dynamic_cast<const XLineWidthItem*>( pState) != nullptr, "wrong ItemType" );
+
+ pFld->SetDestCoreUnit(GetCoreMetric());
+
+ pFld->Update( static_cast<const XLineWidthItem*>(pState) );
+ }
+ else
+ pFld->Update( nullptr );
+ }
+ }
+}
+
+MapUnit SvxLineWidthToolBoxControl::GetCoreMetric()
+{
+ SfxObjectShell* pSh = SfxObjectShell::Current();
+ SfxItemPool& rPool = pSh ? pSh->GetPool() : SfxGetpApp()->GetPool();
+ sal_uInt16 nWhich = rPool.GetWhich(SID_ATTR_LINE_WIDTH);
+ return rPool.GetMetric(nWhich);
+}
+
+VclPtr<InterimItemWindow> SvxLineWidthToolBoxControl::CreateItemWindow(vcl::Window *pParent)
+{
+ VclPtr<SvxMetricField> pWindow = VclPtr<SvxMetricField>::Create(pParent, m_xFrame);
+ pWindow->Show();
+
+ return pWindow;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/tbxctrls/tbcontrl.cxx b/svx/source/tbxctrls/tbcontrl.cxx
new file mode 100644
index 000000000..ff1d519e5
--- /dev/null
+++ b/svx/source/tbxctrls/tbcontrl.cxx
@@ -0,0 +1,4186 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <utility>
+
+#include <comphelper/configurationlistener.hxx>
+#include <comphelper/propertysequence.hxx>
+#include <comphelper/propertyvalue.hxx>
+#include <tools/color.hxx>
+#include <svl/numformat.hxx>
+#include <svl/poolitem.hxx>
+#include <svl/itemset.hxx>
+#include <vcl/commandinfoprovider.hxx>
+#include <vcl/event.hxx>
+#include <vcl/toolbox.hxx>
+#include <vcl/customweld.hxx>
+#include <vcl/vclptr.hxx>
+#include <vcl/weldutils.hxx>
+#include <svtools/valueset.hxx>
+#include <svtools/ctrlbox.hxx>
+#include <svl/style.hxx>
+#include <svtools/ctrltool.hxx>
+#include <svtools/borderhelper.hxx>
+#include <vcl/InterimItemWindow.hxx>
+#include <sfx2/tbxctrl.hxx>
+#include <sfx2/tplpitem.hxx>
+#include <sfx2/sfxstatuslistener.hxx>
+#include <toolkit/helper/vclunohelper.hxx>
+#include <sfx2/viewfrm.hxx>
+#include <vcl/svapp.hxx>
+#include <vcl/settings.hxx>
+#include <vcl/virdev.hxx>
+#include <com/sun/star/awt/FontDescriptor.hpp>
+#include <com/sun/star/table/BorderLine2.hpp>
+#include <com/sun/star/style/XStyleFamiliesSupplier.hpp>
+#include <com/sun/star/lang/XServiceInfo.hpp>
+#include <com/sun/star/beans/XPropertySet.hpp>
+#include <com/sun/star/util/XNumberFormatsSupplier.hpp>
+#include <com/sun/star/frame/XDispatchProvider.hpp>
+#include <com/sun/star/frame/XFrame.hpp>
+#include <svx/strings.hrc>
+#include <svx/svxids.hrc>
+#include <helpids.h>
+#include <sfx2/sidebar/Sidebar.hxx>
+#include <svx/xtable.hxx>
+#include <editeng/editids.hrc>
+#include <editeng/fontitem.hxx>
+#include <editeng/fhgtitem.hxx>
+#include <editeng/boxitem.hxx>
+#include <editeng/charreliefitem.hxx>
+#include <editeng/contouritem.hxx>
+#include <editeng/colritem.hxx>
+#include <editeng/crossedoutitem.hxx>
+#include <editeng/emphasismarkitem.hxx>
+#include <editeng/flstitem.hxx>
+#include <editeng/lineitem.hxx>
+#include <editeng/postitem.hxx>
+#include <editeng/shdditem.hxx>
+#include <editeng/udlnitem.hxx>
+#include <editeng/wghtitem.hxx>
+#include <editeng/svxfont.hxx>
+#include <editeng/cmapitem.hxx>
+#include <svx/colorwindow.hxx>
+#include <svx/colorbox.hxx>
+#include <svx/tbcontrl.hxx>
+#include <svx/dialmgr.hxx>
+#include <svx/PaletteManager.hxx>
+#include <memory>
+
+#include <tbxcolorupdate.hxx>
+#include <editeng/eerdll.hxx>
+#include <editeng/editrids.hrc>
+#include <svx/xdef.hxx>
+#include <svx/xfillit0.hxx>
+#include <svx/xflclit.hxx>
+#include <svl/currencytable.hxx>
+#include <svtools/langtab.hxx>
+#include <cppu/unotype.hxx>
+#include <cppuhelper/supportsservice.hxx>
+#include <officecfg/Office/Common.hxx>
+#include <o3tl/safeint.hxx>
+#include <o3tl/typed_flags_set.hxx>
+#include <bitmaps.hlst>
+#include <sal/log.hxx>
+#include <unotools/collatorwrapper.hxx>
+
+#include <comphelper/lok.hxx>
+#include <tools/json_writer.hxx>
+
+#define MAX_MRU_FONTNAME_ENTRIES 5
+
+#define COMBO_WIDTH_IN_CHARS 18
+
+// namespaces
+using namespace ::editeng;
+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;
+
+namespace
+{
+class SvxStyleBox_Base
+{
+public:
+ SvxStyleBox_Base(std::unique_ptr<weld::ComboBox> xWidget, const OUString& rCommand, SfxStyleFamily eFamily,
+ const Reference<XDispatchProvider>& rDispatchProvider,
+ const Reference<XFrame>& _xFrame,const OUString& rClearFormatKey,
+ const OUString& rMoreKey, bool bInSpecialMode, SvxStyleToolBoxControl& rCtrl);
+
+ virtual ~SvxStyleBox_Base()
+ {
+ }
+
+ void SetFamily( SfxStyleFamily eNewFamily );
+
+ void SetDefaultStyle( const OUString& rDefault ) { sDefaultStyle = rDefault; }
+
+ int get_count() const { return m_xWidget->get_count(); }
+ OUString get_text(int nIndex) const { return m_xWidget->get_text(nIndex); }
+ OUString get_active_text() const { return m_xWidget->get_active_text(); }
+
+ void append_text(const OUString& rStr)
+ {
+ OUString sId(OUString::number(m_xWidget->get_count()));
+ m_xWidget->append(sId, rStr);
+ }
+
+ void insert_separator(int pos, const OUString& rId)
+ {
+ m_xWidget->insert_separator(pos, rId);
+ }
+
+ void set_active_or_entry_text(const OUString& rText)
+ {
+ const int nFound = m_xWidget->find_text(rText);
+ if (nFound != -1)
+ m_xWidget->set_active(nFound);
+ else
+ m_xWidget->set_entry_text(rText);
+ }
+
+ void set_active(int nActive)
+ {
+ m_xWidget->set_active(nActive);
+ }
+
+ void freeze()
+ {
+ m_xWidget->freeze();
+ }
+
+ void save_value()
+ {
+ m_xWidget->save_value();
+ }
+
+ void clear()
+ {
+ m_xWidget->clear();
+ m_nMaxUserDrawFontWidth = 0;
+ }
+
+ void thaw()
+ {
+ m_xWidget->thaw();
+ }
+
+ virtual bool DoKeyInput(const KeyEvent& rKEvt);
+
+private:
+ DECL_LINK(SelectHdl, weld::ComboBox&, void);
+ DECL_LINK(KeyInputHdl, const KeyEvent&, bool);
+ DECL_LINK(ActivateHdl, weld::ComboBox&, bool);
+ DECL_LINK(FocusOutHdl, weld::Widget&, void);
+ DECL_LINK(DumpAsPropertyTreeHdl, tools::JsonWriter&, void);
+ DECL_LINK(CustomRenderHdl, weld::ComboBox::render_args, void);
+ DECL_LINK(CustomGetSizeHdl, OutputDevice&, Size);
+
+ /// Calculate the optimal width of the dropdown. Very expensive operation, triggers lots of font measurement.
+ void CalcOptimalExtraUserWidth(vcl::RenderContext& rRenderContext);
+
+ void Select(bool bNonTravelSelect);
+
+protected:
+ SvxStyleToolBoxControl& m_rCtrl;
+
+ std::unique_ptr<weld::Builder> m_xMenuBuilder;
+ std::unique_ptr<weld::Menu> m_xMenu;
+ std::unique_ptr<weld::ComboBox> m_xWidget;
+
+ SfxStyleFamily eStyleFamily;
+ int m_nMaxUserDrawFontWidth;
+ int m_nLastItemWithMenu;
+ bool bRelease;
+ Reference< XDispatchProvider > m_xDispatchProvider;
+ Reference< XFrame > m_xFrame;
+ OUString m_aCommand;
+ OUString aClearFormatKey;
+ OUString aMoreKey;
+ OUString sDefaultStyle;
+ bool bInSpecialMode;
+
+ void ReleaseFocus();
+ static Color TestColorsVisible(const Color &FontCol, const Color &BackCol);
+ static void UserDrawEntry(vcl::RenderContext& rRenderContext, const tools::Rectangle& rRect, const OUString &rStyleName);
+ void SetupEntry(vcl::RenderContext& rRenderContext, sal_Int32 nItem, const tools::Rectangle& rRect, std::u16string_view rStyleName, bool bIsNotSelected);
+ static bool AdjustFontForItemHeight(OutputDevice& rDevice, tools::Rectangle const & rTextRect, tools::Long nHeight);
+ DECL_LINK(MenuSelectHdl, const OString&, void);
+ DECL_STATIC_LINK(SvxStyleBox_Base, ShowMoreHdl, void*, void);
+};
+
+class SvxStyleBox_Impl final : public InterimItemWindow
+ , public SvxStyleBox_Base
+{
+public:
+ SvxStyleBox_Impl(vcl::Window* pParent, const OUString& rCommand, SfxStyleFamily eFamily, const Reference< XDispatchProvider >& rDispatchProvider,
+ const Reference< XFrame >& _xFrame,const OUString& rClearFormatKey, const OUString& rMoreKey, bool bInSpecialMode, SvxStyleToolBoxControl& rCtrl);
+
+ virtual ~SvxStyleBox_Impl() override
+ {
+ disposeOnce();
+ }
+
+ virtual void dispose() override
+ {
+ m_xWidget.reset();
+ m_xMenu.reset();
+ m_xMenuBuilder.reset();
+ InterimItemWindow::dispose();
+ }
+
+ virtual bool DoKeyInput(const KeyEvent& rKEvt) override;
+
+private:
+
+ virtual void DataChanged(const DataChangedEvent& rDCEvt) override;
+ void SetOptimalSize();
+};
+
+class SvxFontNameBox_Impl;
+class SvxFontNameBox_Base;
+
+class SvxFontNameToolBoxControl final : public cppu::ImplInheritanceHelper<svt::ToolboxController,
+ css::lang::XServiceInfo>
+{
+public:
+ SvxFontNameToolBoxControl();
+
+ // XStatusListener
+ virtual void SAL_CALL statusChanged( const css::frame::FeatureStateEvent& rEvent ) override;
+
+ // XToolbarController
+ virtual css::uno::Reference<css::awt::XWindow> SAL_CALL createItemWindow(const css::uno::Reference<css::awt::XWindow>& rParent) override;
+
+ // XComponent
+ virtual void SAL_CALL dispose() 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;
+
+private:
+ VclPtr<SvxFontNameBox_Impl> m_xVclBox;
+ std::unique_ptr<SvxFontNameBox_Base> m_xWeldBox;
+ SvxFontNameBox_Base* m_pBox;
+};
+
+class FontOptionsListener final : public comphelper::ConfigurationListenerProperty<bool>
+{
+private:
+ SvxFontNameBox_Base& m_rBox;
+
+ virtual void setProperty(const css::uno::Any &rProperty) override;
+public:
+ FontOptionsListener(const rtl::Reference<comphelper::ConfigurationListener>& rListener, const OUString& rProp, SvxFontNameBox_Base& rBox)
+ : comphelper::ConfigurationListenerProperty<bool>(rListener, rProp)
+ , m_rBox(rBox)
+ {
+ }
+};
+
+class SvxFontNameBox_Base
+{
+private:
+ rtl::Reference<comphelper::ConfigurationListener> m_xListener;
+ FontOptionsListener m_aWYSIWYG;
+ FontOptionsListener m_aHistory;
+
+protected:
+ SvxFontNameToolBoxControl& m_rCtrl;
+
+ std::unique_ptr<FontNameBox> m_xWidget;
+ const FontList* pFontList;
+ ::std::unique_ptr<FontList> m_aOwnFontList;
+ vcl::Font aCurFont;
+ sal_uInt16 nFtCount;
+ bool bRelease;
+ Reference< XDispatchProvider > m_xDispatchProvider;
+ Reference< XFrame > m_xFrame;
+ bool mbCheckingUnknownFont;
+
+ void ReleaseFocus_Impl();
+
+ void Select(bool bNonTravelSelect);
+
+ void EndPreview()
+ {
+ Sequence< PropertyValue > aArgs;
+ SfxToolBoxControl::Dispatch( m_xDispatchProvider,
+ ".uno:CharEndPreviewFontName",
+ aArgs );
+ }
+ void CheckAndMarkUnknownFont();
+
+public:
+ SvxFontNameBox_Base(std::unique_ptr<weld::ComboBox> xWidget, const Reference<XDispatchProvider>& rDispatchProvider,
+ const Reference<XFrame>& rFrame, SvxFontNameToolBoxControl& rCtrl);
+ virtual ~SvxFontNameBox_Base()
+ {
+ m_xListener->dispose();
+ }
+
+ void FillList();
+ void Update( const css::awt::FontDescriptor* pFontDesc );
+ sal_uInt16 GetListCount() const { return nFtCount; }
+ void Clear() { m_xWidget->clear(); nFtCount = 0; }
+ void Fill( const FontList* pList )
+ {
+ m_xWidget->Fill(pList);
+ nFtCount = pList->GetFontNameCount();
+ }
+
+ void SetOwnFontList(::std::unique_ptr<FontList> && _aOwnFontList) { m_aOwnFontList = std::move(_aOwnFontList); }
+
+ virtual void set_sensitive(bool bSensitive)
+ {
+ m_xWidget->set_sensitive(bSensitive);
+ }
+
+ void set_active_or_entry_text(const OUString& rText);
+
+ void statusChanged_Impl(const css::frame::FeatureStateEvent& rEvent);
+
+ virtual bool DoKeyInput(const KeyEvent& rKEvt);
+
+ void EnableControls();
+
+ DECL_LINK(SelectHdl, weld::ComboBox&, void);
+ DECL_LINK(KeyInputHdl, const KeyEvent&, bool);
+ DECL_LINK(ActivateHdl, weld::ComboBox&, bool);
+ DECL_LINK(FocusInHdl, weld::Widget&, void);
+ DECL_LINK(FocusOutHdl, weld::Widget&, void);
+ DECL_LINK(DumpAsPropertyTreeHdl, tools::JsonWriter&, void);
+};
+
+void FontOptionsListener::setProperty(const css::uno::Any &rProperty)
+{
+ comphelper::ConfigurationListenerProperty<bool>::setProperty(rProperty);
+ m_rBox.EnableControls();
+}
+
+class SvxFontNameBox_Impl final : public InterimItemWindow
+ , public SvxFontNameBox_Base
+{
+private:
+ virtual void DataChanged( const DataChangedEvent& rDCEvt ) override;
+ virtual void GetFocus() override
+ {
+ if (m_xWidget)
+ m_xWidget->grab_focus();
+ InterimItemWindow::GetFocus();
+ }
+
+ void SetOptimalSize();
+
+ virtual bool DoKeyInput(const KeyEvent& rKEvt) override;
+
+public:
+ SvxFontNameBox_Impl(vcl::Window* pParent, const Reference<XDispatchProvider>& rDispatchProvider,
+ const Reference<XFrame>& rFrame, SvxFontNameToolBoxControl& rCtrl);
+
+ virtual void dispose() override
+ {
+ m_xWidget.reset();
+ InterimItemWindow::dispose();
+ }
+
+ virtual ~SvxFontNameBox_Impl() override
+ {
+ disposeOnce();
+ }
+
+ virtual Reference< css::accessibility::XAccessible > CreateAccessible() override;
+
+ virtual void set_sensitive(bool bSensitive) override
+ {
+ m_xWidget->set_sensitive(bSensitive);
+ if (bSensitive)
+ InterimItemWindow::Enable();
+ else
+ InterimItemWindow::Disable();
+ }
+};
+
+
+// SelectHdl needs the Modifiers, get them in MouseButtonUp
+class SvxFrmValueSet_Impl final : public ValueSet
+{
+private:
+ sal_uInt16 nModifier;
+
+ virtual bool MouseButtonUp(const MouseEvent& rMEvt) override
+ {
+ nModifier = rMEvt.GetModifier();
+ return ValueSet::MouseButtonUp(rMEvt);
+ }
+
+public:
+ SvxFrmValueSet_Impl()
+ : ValueSet(nullptr)
+ , nModifier(0)
+ {
+ }
+ sal_uInt16 GetModifier() const {return nModifier;}
+};
+
+}
+
+namespace {
+
+class SvxFrameToolBoxControl;
+
+class SvxFrameWindow_Impl final : public WeldToolbarPopup
+{
+private:
+ rtl::Reference<SvxFrameToolBoxControl> mxControl;
+ std::unique_ptr<SvxFrmValueSet_Impl> mxFrameSet;
+ std::unique_ptr<weld::CustomWeld> mxFrameSetWin;
+ std::vector<std::pair<BitmapEx, OUString>> aImgVec;
+ bool bParagraphMode;
+ bool m_bIsWriter;
+
+ void InitImageList();
+ void CalcSizeValueSet();
+ DECL_LINK( SelectHdl, ValueSet*, void );
+
+ void SetDiagonalDownBorder(const SvxLineItem& dDownLineItem);
+ void SetDiagonalUpBorder(const SvxLineItem& dUpLineItem);
+
+public:
+ SvxFrameWindow_Impl(SvxFrameToolBoxControl* pControl, weld::Widget* pParent);
+ virtual void GrabFocus() override
+ {
+ mxFrameSet->GrabFocus();
+ }
+
+ virtual void statusChanged( const css::frame::FeatureStateEvent& rEvent ) override;
+};
+
+class SvxFrameToolBoxControl : public svt::PopupWindowController
+{
+public:
+ explicit SvxFrameToolBoxControl( const css::uno::Reference< css::uno::XComponentContext >& rContext );
+
+ // XInitialization
+ virtual void SAL_CALL initialize( const css::uno::Sequence< css::uno::Any >& rArguments ) override;
+
+ // XServiceInfo
+ virtual OUString SAL_CALL getImplementationName() override;
+ virtual css::uno::Sequence< OUString > SAL_CALL getSupportedServiceNames() override;
+
+ virtual void SAL_CALL execute(sal_Int16 nKeyModifier) override;
+private:
+ virtual std::unique_ptr<WeldToolbarPopup> weldPopupWindow() override;
+ virtual VclPtr<vcl::Window> createVclPopupWindow( vcl::Window* pParent ) override;
+};
+
+ class LineListBox final : public ValueSet
+ {
+ public:
+ typedef Color (*ColorFunc)(Color);
+ typedef Color (*ColorDistFunc)(Color, Color);
+
+ LineListBox();
+
+ /** Set the width in Twips */
+ Size SetWidth( tools::Long nWidth )
+ {
+ tools::Long nOldWidth = m_nWidth;
+ m_nWidth = nWidth;
+ return UpdateEntries( nOldWidth );
+ }
+
+ void SetNone( const OUString& sNone )
+ {
+ m_sNone = sNone;
+ }
+
+ /** Insert a listbox entry with all widths in Twips. */
+ void InsertEntry(const BorderWidthImpl& rWidthImpl,
+ SvxBorderLineStyle nStyle, tools::Long nMinWidth = 0,
+ ColorFunc pColor1Fn = &sameColor,
+ ColorFunc pColor2Fn = &sameColor,
+ ColorDistFunc pColorDistFn = &sameDistColor);
+
+ SvxBorderLineStyle GetEntryStyle( sal_Int32 nPos ) const;
+
+ SvxBorderLineStyle GetSelectEntryStyle() const;
+
+ void SetSourceUnit( FieldUnit eNewUnit ) { eSourceUnit = eNewUnit; }
+
+ const Color& GetColor() const { return aColor; }
+
+ virtual void SetDrawingArea(weld::DrawingArea* pDrawingArea) override;
+ private:
+
+ void ImpGetLine(tools::Long nLine1, tools::Long nLine2, tools::Long nDistance,
+ Color nColor1, Color nColor2, Color nColorDist,
+ SvxBorderLineStyle nStyle, BitmapEx& rBmp);
+
+ void UpdatePaintLineColor(); // returns sal_True if maPaintCol has changed
+
+ Size UpdateEntries( tools::Long nOldWidth );
+ sal_Int32 GetStylePos( sal_Int32 nListPos, tools::Long nWidth );
+
+ const Color& GetPaintColor() const
+ {
+ return maPaintCol;
+ }
+
+ Color GetColorLine1( sal_Int32 nPos );
+ Color GetColorLine2( sal_Int32 nPos );
+ Color GetColorDist( sal_Int32 nPos );
+
+ LineListBox( const LineListBox& ) = delete;
+ LineListBox& operator =( const LineListBox& ) = delete;
+
+ std::vector<std::unique_ptr<ImpLineListData>> m_vLineList;
+ tools::Long m_nWidth;
+ OUString m_sNone;
+ ScopedVclPtr<VirtualDevice> aVirDev;
+ Size aTxtSize;
+ Color const aColor;
+ Color maPaintCol;
+ FieldUnit eSourceUnit;
+ };
+
+ SvxBorderLineStyle LineListBox::GetSelectEntryStyle() const
+ {
+ SvxBorderLineStyle nStyle = SvxBorderLineStyle::SOLID;
+ size_t nPos = GetSelectItemPos();
+ if (nPos != VALUESET_ITEM_NOTFOUND)
+ {
+ if (!m_sNone.isEmpty())
+ --nPos;
+ nStyle = GetEntryStyle( nPos );
+ }
+
+ return nStyle;
+ }
+
+ void LineListBox::ImpGetLine( tools::Long nLine1, tools::Long nLine2, tools::Long nDistance,
+ Color aColor1, Color aColor2, Color aColorDist,
+ SvxBorderLineStyle nStyle, BitmapEx& rBmp )
+ {
+ auto nMinWidth = GetDrawingArea()->get_ref_device().approximate_digit_width() * COMBO_WIDTH_IN_CHARS;
+ Size aSize(nMinWidth, aTxtSize.Height());
+ aSize.AdjustWidth( -(aTxtSize.Width()) );
+ aSize.AdjustWidth( -6 );
+
+ // SourceUnit to Twips
+ if ( eSourceUnit == FieldUnit::POINT )
+ {
+ nLine1 /= 5;
+ nLine2 /= 5;
+ nDistance /= 5;
+ }
+
+ // Paint the lines
+ aSize = aVirDev->PixelToLogic( aSize );
+ tools::Long nPix = aVirDev->PixelToLogic( Size( 0, 1 ) ).Height();
+ sal_uInt32 n1 = nLine1;
+ sal_uInt32 n2 = nLine2;
+ tools::Long nDist = nDistance;
+ n1 += nPix-1;
+ n1 -= n1%nPix;
+ if ( n2 )
+ {
+ nDist += nPix-1;
+ nDist -= nDist%nPix;
+ n2 += nPix-1;
+ n2 -= n2%nPix;
+ }
+ tools::Long nVirHeight = n1+nDist+n2;
+ if ( nVirHeight > aSize.Height() )
+ aSize.setHeight( nVirHeight );
+ // negative width should not be drawn
+ if ( aSize.Width() <= 0 )
+ return;
+
+ Size aVirSize = aVirDev->LogicToPixel( aSize );
+ if ( aVirDev->GetOutputSizePixel() != aVirSize )
+ aVirDev->SetOutputSizePixel( aVirSize );
+ aVirDev->SetFillColor( aColorDist );
+ aVirDev->DrawRect( tools::Rectangle( Point(), aSize ) );
+
+ aVirDev->SetFillColor( aColor1 );
+
+ double y1 = double( n1 ) / 2;
+ svtools::DrawLine( *aVirDev, basegfx::B2DPoint( 0, y1 ), basegfx::B2DPoint( aSize.Width( ), y1 ), n1, nStyle );
+
+ if ( n2 )
+ {
+ double y2 = n1 + nDist + double( n2 ) / 2;
+ aVirDev->SetFillColor( aColor2 );
+ svtools::DrawLine( *aVirDev, basegfx::B2DPoint( 0, y2 ), basegfx::B2DPoint( aSize.Width(), y2 ), n2, SvxBorderLineStyle::SOLID );
+ }
+ rBmp = aVirDev->GetBitmapEx( Point(), Size( aSize.Width(), n1+nDist+n2 ) );
+ }
+
+ LineListBox::LineListBox()
+ : ValueSet(nullptr)
+ , m_nWidth( 5 )
+ , aVirDev(VclPtr<VirtualDevice>::Create())
+ , aColor(Application::GetSettings().GetStyleSettings().GetWindowTextColor())
+ , maPaintCol(COL_BLACK)
+ , eSourceUnit(FieldUnit::POINT)
+ {
+ aVirDev->SetLineColor();
+ aVirDev->SetMapMode( MapMode( MapUnit::MapTwip ) );
+ }
+
+ void LineListBox::SetDrawingArea(weld::DrawingArea* pDrawingArea)
+ {
+ ValueSet::SetDrawingArea(pDrawingArea);
+
+ OutputDevice& rDevice = pDrawingArea->get_ref_device();
+
+ aTxtSize.setWidth( rDevice.approximate_digit_width() );
+ aTxtSize.setHeight( rDevice.GetTextHeight() );
+
+ UpdatePaintLineColor();
+ }
+
+ sal_Int32 LineListBox::GetStylePos( sal_Int32 nListPos, tools::Long nWidth )
+ {
+ sal_Int32 nPos = -1;
+ if (!m_sNone.isEmpty())
+ nListPos--;
+
+ sal_Int32 n = 0;
+ size_t i = 0;
+ size_t nCount = m_vLineList.size();
+ while ( nPos == -1 && i < nCount )
+ {
+ auto& pData = m_vLineList[ i ];
+ if ( pData->GetMinWidth() <= nWidth )
+ {
+ if ( nListPos == n )
+ nPos = static_cast<sal_Int32>(i);
+ n++;
+ }
+ i++;
+ }
+
+ return nPos;
+ }
+
+ void LineListBox::InsertEntry(
+ const BorderWidthImpl& rWidthImpl, SvxBorderLineStyle nStyle, tools::Long nMinWidth,
+ ColorFunc pColor1Fn, ColorFunc pColor2Fn, ColorDistFunc pColorDistFn )
+ {
+ m_vLineList.emplace_back(new ImpLineListData(
+ rWidthImpl, nStyle, nMinWidth, pColor1Fn, pColor2Fn, pColorDistFn));
+ }
+
+ SvxBorderLineStyle LineListBox::GetEntryStyle( sal_Int32 nPos ) const
+ {
+ ImpLineListData* pData = (0 <= nPos && o3tl::make_unsigned(nPos) < m_vLineList.size()) ? m_vLineList[ nPos ].get() : nullptr;
+ return pData ? pData->GetStyle() : SvxBorderLineStyle::NONE;
+ }
+
+ void LineListBox::UpdatePaintLineColor()
+ {
+ const StyleSettings& rSettings = Application::GetSettings().GetStyleSettings();
+ Color aNewCol( rSettings.GetWindowColor().IsDark()? rSettings.GetLabelTextColor() : aColor );
+
+ bool bRet = aNewCol != maPaintCol;
+
+ if( bRet )
+ maPaintCol = aNewCol;
+ }
+
+ Size LineListBox::UpdateEntries( tools::Long nOldWidth )
+ {
+ Size aSize;
+
+ UpdatePaintLineColor( );
+
+ sal_Int32 nSelEntry = GetSelectItemPos();
+ sal_Int32 nTypePos = GetStylePos( nSelEntry, nOldWidth );
+
+ // Remove the old entries
+ Clear();
+
+ sal_uInt16 nId(1);
+
+ // Add the new entries based on the defined width
+ if (!m_sNone.isEmpty())
+ InsertItem(nId++, Image(), m_sNone);
+
+ sal_uInt16 n = 0;
+ sal_uInt16 nCount = m_vLineList.size( );
+ while ( n < nCount )
+ {
+ auto& pData = m_vLineList[ n ];
+ if ( pData->GetMinWidth() <= m_nWidth )
+ {
+ BitmapEx aBmp;
+ ImpGetLine( pData->GetLine1ForWidth( m_nWidth ),
+ pData->GetLine2ForWidth( m_nWidth ),
+ pData->GetDistForWidth( m_nWidth ),
+ GetColorLine1( GetItemCount( ) ),
+ GetColorLine2( GetItemCount( ) ),
+ GetColorDist( GetItemCount( ) ),
+ pData->GetStyle(), aBmp );
+ InsertItem(nId, Image(aBmp), SvtLineListBox::GetLineStyleName(pData->GetStyle()));
+ Size aBmpSize = aBmp.GetSizePixel();
+ if (aBmpSize.Width() > aSize.Width())
+ aSize.setWidth(aBmpSize.getWidth());
+ if (aBmpSize.Height() > aSize.Height())
+ aSize.setHeight(aBmpSize.getHeight());
+ if ( n == nTypePos )
+ SelectItem(nId);
+ }
+ else if ( n == nTypePos )
+ SetNoSelection();
+ n++;
+ ++nId;
+ }
+
+ Invalidate();
+
+ return aSize;
+ }
+
+ Color LineListBox::GetColorLine1( sal_Int32 nPos )
+ {
+ sal_Int32 nStyle = GetStylePos( nPos, m_nWidth );
+ if (nStyle == -1)
+ return GetPaintColor( );
+ auto& pData = m_vLineList[ nStyle ];
+ return pData->GetColorLine1( GetColor( ) );
+ }
+
+ Color LineListBox::GetColorLine2( sal_Int32 nPos )
+ {
+ sal_Int32 nStyle = GetStylePos( nPos, m_nWidth );
+ if (nStyle == -1)
+ return GetPaintColor( );
+ auto& pData = m_vLineList[ nStyle ];
+ return pData->GetColorLine2( GetColor( ) );
+ }
+
+ Color LineListBox::GetColorDist( sal_Int32 nPos )
+ {
+ Color rResult = Application::GetSettings().GetStyleSettings().GetFieldColor();
+
+ sal_Int32 nStyle = GetStylePos( nPos, m_nWidth );
+ if (nStyle == -1)
+ return rResult;
+ auto& pData = m_vLineList[ nStyle ];
+ return pData->GetColorDist( GetColor( ), rResult );
+ }
+}
+
+namespace {
+
+class SvxLineWindow_Impl final : public WeldToolbarPopup
+{
+private:
+ rtl::Reference<SvxFrameToolBoxControl> m_xControl;
+ std::unique_ptr<LineListBox> m_xLineStyleLb;
+ std::unique_ptr<weld::CustomWeld> m_xLineStyleLbWin;
+ bool m_bIsWriter;
+
+ DECL_LINK( SelectHdl, ValueSet*, void );
+
+public:
+ SvxLineWindow_Impl(SvxFrameToolBoxControl* pControl, weld::Widget* pParent);
+ virtual void GrabFocus() override
+ {
+ m_xLineStyleLb->GrabFocus();
+ }
+};
+
+}
+
+class SvxStyleToolBoxControl;
+
+class SfxStyleControllerItem_Impl : public SfxStatusListener
+{
+ public:
+ SfxStyleControllerItem_Impl( const Reference< XDispatchProvider >& rDispatchProvider,
+ sal_uInt16 nSlotId,
+ const OUString& rCommand,
+ SvxStyleToolBoxControl& rTbxCtl );
+
+ protected:
+ virtual void StateChangedAtStatusListener( SfxItemState eState, const SfxPoolItem* pState ) override;
+
+ private:
+ SvxStyleToolBoxControl& rControl;
+};
+
+#define BUTTON_PADDING 10
+#define ITEM_HEIGHT 30
+
+SvxStyleBox_Base::SvxStyleBox_Base(std::unique_ptr<weld::ComboBox> xWidget,
+ const OUString& rCommand,
+ SfxStyleFamily eFamily,
+ const Reference< XDispatchProvider >& rDispatchProvider,
+ const Reference< XFrame >& _xFrame,
+ const OUString& rClearFormatKey,
+ const OUString& rMoreKey,
+ bool bInSpec, SvxStyleToolBoxControl& rCtrl)
+ : m_rCtrl(rCtrl)
+ , m_xMenuBuilder(Application::CreateBuilder(nullptr, "svx/ui/stylemenu.ui"))
+ , m_xMenu(m_xMenuBuilder->weld_menu("menu"))
+ , m_xWidget(std::move(xWidget))
+ , eStyleFamily( eFamily )
+ , m_nMaxUserDrawFontWidth(0)
+ , m_nLastItemWithMenu(-1)
+ , bRelease( true )
+ , m_xDispatchProvider( rDispatchProvider )
+ , m_xFrame(_xFrame)
+ , m_aCommand( rCommand )
+ , aClearFormatKey( rClearFormatKey )
+ , aMoreKey( rMoreKey )
+ , bInSpecialMode( bInSpec )
+{
+ m_xWidget->connect_changed(LINK(this, SvxStyleBox_Base, SelectHdl));
+ m_xWidget->connect_key_press(LINK(this, SvxStyleBox_Base, KeyInputHdl));
+ m_xWidget->connect_entry_activate(LINK(this, SvxStyleBox_Base, ActivateHdl));
+ m_xWidget->connect_focus_out(LINK(this, SvxStyleBox_Base, FocusOutHdl));
+ m_xWidget->connect_get_property_tree(LINK(this, SvxStyleBox_Base, DumpAsPropertyTreeHdl));
+ m_xWidget->set_help_id(HID_STYLE_LISTBOX);
+ m_xWidget->set_entry_completion(true);
+ m_xMenu->connect_activate(LINK(this, SvxStyleBox_Base, MenuSelectHdl));
+
+ m_xWidget->connect_custom_get_size(LINK(this, SvxStyleBox_Base, CustomGetSizeHdl));
+ m_xWidget->connect_custom_render(LINK(this, SvxStyleBox_Base, CustomRenderHdl));
+ m_xWidget->set_custom_renderer(true);
+
+ m_xWidget->set_entry_width_chars(COMBO_WIDTH_IN_CHARS + 3);
+}
+
+IMPL_LINK(SvxStyleBox_Base, CustomGetSizeHdl, OutputDevice&, rArg, Size)
+{
+ CalcOptimalExtraUserWidth(rArg);
+ return Size(m_nMaxUserDrawFontWidth, ITEM_HEIGHT);
+}
+
+SvxStyleBox_Impl::SvxStyleBox_Impl(vcl::Window* pParent,
+ const OUString& rCommand,
+ SfxStyleFamily eFamily,
+ const Reference< XDispatchProvider >& rDispatchProvider,
+ const Reference< XFrame >& _xFrame,
+ const OUString& rClearFormatKey,
+ const OUString& rMoreKey,
+ bool bInSpec, SvxStyleToolBoxControl& rCtrl)
+ : InterimItemWindow(pParent, "svx/ui/applystylebox.ui", "ApplyStyleBox")
+ , SvxStyleBox_Base(m_xBuilder->weld_combo_box("applystyle"), rCommand, eFamily,
+ rDispatchProvider, _xFrame, rClearFormatKey, rMoreKey, bInSpec, rCtrl)
+{
+ InitControlBase(m_xWidget.get());
+
+ set_id("applystyle");
+ SetOptimalSize();
+}
+
+void SvxStyleBox_Base::ReleaseFocus()
+{
+ if ( !bRelease )
+ {
+ bRelease = true;
+ return;
+ }
+ if ( m_xFrame.is() && m_xFrame->getContainerWindow().is() )
+ m_xFrame->getContainerWindow()->setFocus();
+}
+
+IMPL_LINK(SvxStyleBox_Base, MenuSelectHdl, const OString&, rMenuIdent, void)
+{
+ if (m_nLastItemWithMenu < 0 || m_nLastItemWithMenu >= m_xWidget->get_count())
+ return;
+
+ OUString sEntry = m_xWidget->get_text(m_nLastItemWithMenu);
+
+ ReleaseFocus(); // It must be after getting entry pos!
+ Sequence<PropertyValue> aArgs{ comphelper::makePropertyValue("Param", sEntry),
+ comphelper::makePropertyValue("Family",
+ sal_Int16( eStyleFamily )) };
+
+ if (rMenuIdent == "update")
+ {
+ SfxToolBoxControl::Dispatch( m_xDispatchProvider,
+ ".uno:StyleUpdateByExample", aArgs );
+ }
+ else if (rMenuIdent == "edit")
+ {
+ SfxToolBoxControl::Dispatch( m_xDispatchProvider,
+ ".uno:EditStyle", aArgs );
+ }
+}
+
+IMPL_STATIC_LINK_NOARG(SvxStyleBox_Base, ShowMoreHdl, void*, void)
+{
+ SfxViewFrame* pViewFrm = SfxViewFrame::Current();
+ DBG_ASSERT( pViewFrm, "SvxStyleBox_Base::Select(): no viewframe" );
+ if (!pViewFrm)
+ return;
+ pViewFrm->ShowChildWindow(SID_SIDEBAR);
+ ::sfx2::sidebar::Sidebar::ShowPanel(u"StyleListPanel", pViewFrm->GetFrame().GetFrameInterface(), true);
+}
+
+IMPL_LINK(SvxStyleBox_Base, SelectHdl, weld::ComboBox&, rCombo, void)
+{
+ Select(rCombo.changed_by_direct_pick()); // only when picked from the list
+}
+
+IMPL_LINK_NOARG(SvxStyleBox_Base, ActivateHdl, weld::ComboBox&, bool)
+{
+ Select(true);
+ return true;
+}
+
+void SvxStyleBox_Base::Select(bool bNonTravelSelect)
+{
+ if (!bNonTravelSelect)
+ return;
+
+ OUString aSearchEntry(m_xWidget->get_active_text());
+ bool bDoIt = true, bClear = false;
+ if( bInSpecialMode )
+ {
+ if( aSearchEntry == aClearFormatKey && m_xWidget->get_active() == 0 )
+ {
+ aSearchEntry = sDefaultStyle;
+ bClear = true;
+ //not only apply default style but also call 'ClearFormatting'
+ Sequence< PropertyValue > aEmptyVals;
+ SfxToolBoxControl::Dispatch( m_xDispatchProvider, ".uno:ResetAttributes",
+ aEmptyVals);
+ }
+ else if (aSearchEntry == aMoreKey && m_xWidget->get_active() == (m_xWidget->get_count() - 1))
+ {
+ Application::PostUserEvent(LINK(nullptr, SvxStyleBox_Base, ShowMoreHdl));
+ //tdf#113214 change text back to previous entry
+ set_active_or_entry_text(m_xWidget->get_saved_value());
+ bDoIt = false;
+ }
+ }
+
+ //Do we need to create a new style?
+ SfxObjectShell *pShell = SfxObjectShell::Current();
+ SfxStyleSheetBasePool* pPool = pShell->GetStyleSheetPool();
+ SfxStyleSheetBase* pStyle = nullptr;
+
+ bool bCreateNew = false;
+
+ if ( pPool )
+ {
+ pStyle = pPool->First(eStyleFamily);
+ while ( pStyle && pStyle->GetName() != aSearchEntry )
+ pStyle = pPool->Next();
+ }
+
+ if ( !pStyle )
+ {
+ // cannot find the style for whatever reason
+ // therefore create a new style
+ bCreateNew = true;
+ }
+
+ /* #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();
+
+ if( !bDoIt )
+ return;
+
+ if ( bClear )
+ set_active_or_entry_text(aSearchEntry);
+ m_xWidget->save_value();
+
+ Sequence< PropertyValue > aArgs( 2 );
+ auto pArgs = aArgs.getArray();
+ pArgs[0].Value <<= aSearchEntry;
+ pArgs[1].Name = "Family";
+ pArgs[1].Value <<= sal_Int16( eStyleFamily );
+ if( bCreateNew )
+ {
+ pArgs[0].Name = "Param";
+ SfxToolBoxControl::Dispatch( m_xDispatchProvider, ".uno:StyleNewByExample", aArgs);
+ }
+ else
+ {
+ pArgs[0].Name = "Template";
+ SfxToolBoxControl::Dispatch( m_xDispatchProvider, m_aCommand, aArgs );
+ }
+}
+
+void SvxStyleBox_Base::SetFamily( SfxStyleFamily eNewFamily )
+{
+ eStyleFamily = eNewFamily;
+}
+
+IMPL_LINK_NOARG(SvxStyleBox_Base, 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
+ set_active_or_entry_text(m_xWidget->get_saved_value());
+}
+
+IMPL_LINK(SvxStyleBox_Base, KeyInputHdl, const KeyEvent&, rKEvt, bool)
+{
+ return DoKeyInput(rKEvt);
+}
+
+bool SvxStyleBox_Base::DoKeyInput(const KeyEvent& rKEvt)
+{
+ bool bHandled = false;
+
+ sal_uInt16 nCode = rKEvt.GetKeyCode().GetCode();
+
+ switch (nCode)
+ {
+ case KEY_TAB:
+ bRelease = false;
+ Select(true);
+ break;
+ case KEY_ESCAPE:
+ set_active_or_entry_text(m_xWidget->get_saved_value());
+ if (!m_rCtrl.IsInSidebar())
+ {
+ ReleaseFocus();
+ bHandled = true;
+ }
+ break;
+ }
+
+ return bHandled;
+}
+
+bool SvxStyleBox_Impl::DoKeyInput(const KeyEvent& rKEvt)
+{
+ return SvxStyleBox_Base::DoKeyInput(rKEvt) || ChildKeyInput(rKEvt);
+}
+
+void SvxStyleBox_Impl::DataChanged( const DataChangedEvent& rDCEvt )
+{
+ if ( (rDCEvt.GetType() == DataChangedEventType::SETTINGS) &&
+ (rDCEvt.GetFlags() & AllSettingsFlags::STYLE) )
+ {
+ SetOptimalSize();
+ }
+
+ InterimItemWindow::DataChanged( rDCEvt );
+}
+
+bool SvxStyleBox_Base::AdjustFontForItemHeight(OutputDevice& rDevice, tools::Rectangle const & rTextRect, tools::Long nHeight)
+{
+ if (rTextRect.Bottom() > nHeight)
+ {
+ // the text does not fit, adjust the font size
+ double ratio = static_cast< double >( nHeight ) / rTextRect.Bottom();
+ vcl::Font aFont(rDevice.GetFont());
+ Size aPixelSize(aFont.GetFontSize());
+ aPixelSize.setWidth( aPixelSize.Width() * ratio );
+ aPixelSize.setHeight( aPixelSize.Height() * ratio );
+ aFont.SetFontSize(aPixelSize);
+ rDevice.SetFont(aFont);
+ return true;
+ }
+ return false;
+}
+
+void SvxStyleBox_Impl::SetOptimalSize()
+{
+ // set width in chars low so the size request will not be overridden
+ m_xWidget->set_entry_width_chars(1);
+ // tdf#132338 purely using this calculation to keep things their traditional width
+ Size aSize(LogicToPixel(Size((COMBO_WIDTH_IN_CHARS + 3) * 4, 0), MapMode(MapUnit::MapAppFont)));
+ m_xWidget->set_size_request(aSize.Width(), -1);
+
+ SetSizePixel(get_preferred_size());
+}
+
+void SvxStyleBox_Base::UserDrawEntry(vcl::RenderContext& rRenderContext, const tools::Rectangle& rRect, const OUString &rStyleName)
+{
+ // IMG_TXT_DISTANCE in ilstbox.hxx is 6, then 1 is added as
+ // nBorder, and we are adding 1 in order to look better when
+ // italics is present
+ const int nLeftDistance = 8;
+
+ tools::Rectangle aTextRect;
+ rRenderContext.GetTextBoundRect(aTextRect, rStyleName);
+
+ Point aPos(rRect.TopLeft());
+ aPos.AdjustX(nLeftDistance );
+
+ if (!AdjustFontForItemHeight(rRenderContext, aTextRect, rRect.GetHeight()))
+ aPos.AdjustY((rRect.GetHeight() - aTextRect.Bottom() ) / 2);
+
+ rRenderContext.DrawText(aPos, rStyleName);
+}
+
+void SvxStyleBox_Base::SetupEntry(vcl::RenderContext& rRenderContext, sal_Int32 nItem, const tools::Rectangle& rRect, std::u16string_view rStyleName, bool bIsNotSelected)
+{
+ const StyleSettings& rStyleSettings = Application::GetSettings().GetStyleSettings();
+ if (!bIsNotSelected)
+ rRenderContext.SetTextColor(rStyleSettings.GetHighlightTextColor());
+ else
+ rRenderContext.SetTextColor(rStyleSettings.GetDialogTextColor());
+
+ // handle the push-button
+ if (!bIsNotSelected)
+ {
+ if (nItem == 0 || nItem == m_xWidget->get_count() - 1)
+ m_xWidget->set_item_menu(OString::number(nItem), nullptr);
+ else
+ {
+ m_nLastItemWithMenu = nItem;
+ m_xWidget->set_item_menu(OString::number(nItem), m_xMenu.get());
+ }
+ }
+
+ if (nItem <= 0 || nItem >= m_xWidget->get_count() - 1)
+ return;
+
+ SfxObjectShell *pShell = SfxObjectShell::Current();
+ if (!pShell)
+ return;
+
+ SfxStyleSheetBasePool* pPool = pShell->GetStyleSheetPool();
+ if (!pPool)
+ return;
+
+ SfxStyleSheetBase* pStyle = pPool->First(eStyleFamily);
+ while (pStyle && pStyle->GetName() != rStyleName)
+ pStyle = pPool->Next();
+
+ if (!pStyle )
+ return;
+
+ std::optional<SfxItemSet> const pItemSet(pStyle->GetItemSetForPreview());
+ if (!pItemSet) return;
+
+ const SvxFontItem * const pFontItem =
+ pItemSet->GetItem<SvxFontItem>(SID_ATTR_CHAR_FONT);
+ const SvxFontHeightItem * const pFontHeightItem =
+ pItemSet->GetItem<SvxFontHeightItem>(SID_ATTR_CHAR_FONTHEIGHT);
+
+ if ( !(pFontItem && pFontHeightItem) )
+ return;
+
+ Size aFontSize( 0, pFontHeightItem->GetHeight() );
+ Size aPixelSize(rRenderContext.LogicToPixel(aFontSize, MapMode(pShell->GetMapUnit())));
+
+ // setup the font properties
+ SvxFont aFont;
+ aFont.SetFamilyName(pFontItem->GetFamilyName());
+ aFont.SetStyleName(pFontItem->GetStyleName());
+ aFont.SetFontSize(aPixelSize);
+
+ const SfxPoolItem *pItem = pItemSet->GetItem( SID_ATTR_CHAR_WEIGHT );
+ if ( pItem )
+ aFont.SetWeight( static_cast< const SvxWeightItem* >( pItem )->GetWeight() );
+
+ pItem = pItemSet->GetItem( SID_ATTR_CHAR_POSTURE );
+ if ( pItem )
+ aFont.SetItalic( static_cast< const SvxPostureItem* >( pItem )->GetPosture() );
+
+ pItem = pItemSet->GetItem( SID_ATTR_CHAR_CONTOUR );
+ if ( pItem )
+ aFont.SetOutline( static_cast< const SvxContourItem* >( pItem )->GetValue() );
+
+ pItem = pItemSet->GetItem( SID_ATTR_CHAR_SHADOWED );
+ if ( pItem )
+ aFont.SetShadow( static_cast< const SvxShadowedItem* >( pItem )->GetValue() );
+
+ pItem = pItemSet->GetItem( SID_ATTR_CHAR_RELIEF );
+ if ( pItem )
+ aFont.SetRelief( static_cast< const SvxCharReliefItem* >( pItem )->GetValue() );
+
+ pItem = pItemSet->GetItem( SID_ATTR_CHAR_UNDERLINE );
+ if ( pItem )
+ aFont.SetUnderline( static_cast< const SvxUnderlineItem* >( pItem )->GetLineStyle() );
+
+ pItem = pItemSet->GetItem( SID_ATTR_CHAR_OVERLINE );
+ if ( pItem )
+ aFont.SetOverline( static_cast< const SvxOverlineItem* >( pItem )->GetValue() );
+
+ pItem = pItemSet->GetItem( SID_ATTR_CHAR_STRIKEOUT );
+ if ( pItem )
+ aFont.SetStrikeout( static_cast< const SvxCrossedOutItem* >( pItem )->GetStrikeout() );
+
+ pItem = pItemSet->GetItem( SID_ATTR_CHAR_CASEMAP );
+ if ( pItem )
+ aFont.SetCaseMap(static_cast<const SvxCaseMapItem*>(pItem)->GetCaseMap());
+
+ pItem = pItemSet->GetItem( SID_ATTR_CHAR_EMPHASISMARK );
+ if ( pItem )
+ aFont.SetEmphasisMark( static_cast< const SvxEmphasisMarkItem* >( pItem )->GetEmphasisMark() );
+
+ // setup the device & draw
+ Color aFontCol = COL_AUTO, aBackCol = COL_AUTO;
+
+ rRenderContext.SetFont(aFont);
+
+ pItem = pItemSet->GetItem( SID_ATTR_CHAR_COLOR );
+ // text color, when nothing is selected
+ if ( (nullptr != pItem) && bIsNotSelected)
+ aFontCol = static_cast< const SvxColorItem* >( pItem )->GetValue();
+
+ drawing::FillStyle style = drawing::FillStyle_NONE;
+ // which kind of Fill style is selected
+ pItem = pItemSet->GetItem( XATTR_FILLSTYLE );
+ // only when ok and not selected
+ if ( (nullptr != pItem) && bIsNotSelected)
+ style = static_cast< const XFillStyleItem* >( pItem )->GetValue();
+
+ switch(style)
+ {
+ case drawing::FillStyle_SOLID:
+ {
+ // set background color
+ pItem = pItemSet->GetItem( XATTR_FILLCOLOR );
+ if ( nullptr != pItem )
+ aBackCol = static_cast< const XFillColorItem* >( pItem )->GetColorValue();
+
+ if ( aBackCol != COL_AUTO )
+ {
+ rRenderContext.SetFillColor(aBackCol);
+ rRenderContext.DrawRect(rRect);
+ }
+ }
+ break;
+
+ default: break;
+ //TODO Draw the other background styles: gradient, hatching and bitmap
+ }
+
+ // when the font and background color are too similar, adjust the Font-Color
+ if( (aFontCol != COL_AUTO) || (aBackCol != COL_AUTO) )
+ aFontCol = TestColorsVisible(aFontCol, (aBackCol != COL_AUTO) ? aBackCol : rRenderContext.GetBackground().GetColor());
+
+ // set text color
+ if ( aFontCol != COL_AUTO )
+ rRenderContext.SetTextColor(aFontCol);
+}
+
+IMPL_LINK(SvxStyleBox_Base, CustomRenderHdl, weld::ComboBox::render_args, aPayload, void)
+{
+ vcl::RenderContext& rRenderContext = std::get<0>(aPayload);
+ const ::tools::Rectangle& rRect = std::get<1>(aPayload);
+ bool bSelected = std::get<2>(aPayload);
+ const OUString& rId = std::get<3>(aPayload);
+
+ sal_uInt32 nIndex = rId.toUInt32();
+
+ OUString aStyleName(m_xWidget->get_text(nIndex));
+
+ rRenderContext.Push(vcl::PushFlags::FILLCOLOR | vcl::PushFlags::FONT | vcl::PushFlags::TEXTCOLOR);
+
+ SetupEntry(rRenderContext, nIndex, rRect, aStyleName, !bSelected);
+
+ UserDrawEntry(rRenderContext, rRect, aStyleName);
+
+ rRenderContext.Pop();
+}
+
+void SvxStyleBox_Base::CalcOptimalExtraUserWidth(vcl::RenderContext& rRenderContext)
+{
+ if (m_nMaxUserDrawFontWidth)
+ return;
+
+ tools::Long nMaxNormalFontWidth = 0;
+ sal_Int32 nEntryCount = m_xWidget->get_count();
+ for (sal_Int32 i = 0; i < nEntryCount; ++i)
+ {
+ OUString sStyleName(get_text(i));
+ tools::Rectangle aTextRectForDefaultFont;
+ rRenderContext.GetTextBoundRect(aTextRectForDefaultFont, sStyleName);
+
+ const tools::Long nWidth = aTextRectForDefaultFont.GetWidth();
+
+ nMaxNormalFontWidth = std::max(nWidth, nMaxNormalFontWidth);
+ }
+
+ m_nMaxUserDrawFontWidth = nMaxNormalFontWidth;
+ for (sal_Int32 i = 1; i < nEntryCount-1; ++i)
+ {
+ OUString sStyleName(get_text(i));
+
+ rRenderContext.Push(vcl::PushFlags::FILLCOLOR | vcl::PushFlags::FONT | vcl::PushFlags::TEXTCOLOR);
+ SetupEntry(rRenderContext, i, tools::Rectangle(0, 0, RECT_MAX, ITEM_HEIGHT), sStyleName, true);
+ tools::Rectangle aTextRectForActualFont;
+ rRenderContext.GetTextBoundRect(aTextRectForActualFont, sStyleName);
+ if (AdjustFontForItemHeight(rRenderContext, aTextRectForActualFont, ITEM_HEIGHT))
+ {
+ //Font didn't fit, so it was changed, refetch with final font size
+ rRenderContext.GetTextBoundRect(aTextRectForActualFont, sStyleName);
+ }
+ rRenderContext.Pop();
+
+ const int nWidth = aTextRectForActualFont.GetWidth() + m_xWidget->get_menu_button_width() + BUTTON_PADDING;
+
+ m_nMaxUserDrawFontWidth = std::max(nWidth, m_nMaxUserDrawFontWidth);
+ }
+}
+
+// test is the color between Font- and background-color to be identify
+// return is always the Font-Color
+// when both light or dark, change the Contrast
+// in other case do not change the origin color
+// when the color is R=G=B=128 the DecreaseContrast make 128 the need an exception
+Color SvxStyleBox_Base::TestColorsVisible(const Color &FontCol, const Color &BackCol)
+{
+ constexpr sal_uInt8 ChgVal = 60; // increase/decrease the Contrast
+
+ Color retCol = FontCol;
+ if ((FontCol.IsDark() == BackCol.IsDark()) && (FontCol.IsBright() == BackCol.IsBright()))
+ {
+ sal_uInt8 lumi = retCol.GetLuminance();
+
+ if((lumi > 120) && (lumi < 140))
+ retCol.DecreaseLuminance(ChgVal / 2);
+ else
+ retCol.DecreaseContrast(ChgVal);
+ }
+
+ return retCol;
+}
+
+IMPL_LINK(SvxStyleBox_Base, DumpAsPropertyTreeHdl, tools::JsonWriter&, rJsonWriter, void)
+{
+ {
+ auto entriesNode = rJsonWriter.startNode("entries");
+ for (int i = 0, nEntryCount = m_xWidget->get_count(); i < nEntryCount; ++i)
+ {
+ auto entryNode = rJsonWriter.startNode("");
+ rJsonWriter.put("", m_xWidget->get_text(i));
+ }
+ }
+
+ int nActive = m_xWidget->get_active();
+ rJsonWriter.put("selectedCount", static_cast<sal_Int32>(nActive == -1 ? 0 : 1));
+
+ {
+ auto selectedNode = rJsonWriter.startNode("selectedEntries");
+ if (nActive != -1)
+ {
+ auto node = rJsonWriter.startNode("");
+ rJsonWriter.put("", static_cast<sal_Int32>(nActive));
+ }
+ }
+
+ rJsonWriter.put("command", ".uno:StyleApply");
+}
+
+static bool lcl_GetDocFontList(const FontList** ppFontList, SvxFontNameBox_Base* pBox)
+{
+ bool bChanged = false;
+ const SfxObjectShell* pDocSh = SfxObjectShell::Current();
+ const SvxFontListItem* pFontListItem = nullptr;
+
+ if ( pDocSh )
+ pFontListItem =
+ static_cast<const SvxFontListItem*>(pDocSh->GetItem( SID_ATTR_CHAR_FONTLIST ));
+ else
+ {
+ ::std::unique_ptr<FontList> aFontList(new FontList(Application::GetDefaultDevice()));
+ *ppFontList = aFontList.get();
+ pBox->SetOwnFontList(std::move(aFontList));
+ bChanged = true;
+ }
+
+ if ( pFontListItem )
+ {
+ const FontList* pNewFontList = pFontListItem->GetFontList();
+ DBG_ASSERT( pNewFontList, "Doc-FontList not available!" );
+
+ // No old list, but a new list
+ if ( !*ppFontList && pNewFontList )
+ {
+ // => take over
+ *ppFontList = pNewFontList;
+ bChanged = true;
+ }
+ else
+ {
+ // Comparing the font lists is not perfect.
+ // When you change the font list in the Doc, you can track
+ // changes here only on the Listbox, because ppFontList
+ // has already been updated.
+ bChanged =
+ ( ( *ppFontList != pNewFontList ) ||
+ pBox->GetListCount() != pNewFontList->GetFontNameCount() );
+ // HACK: Comparing is incomplete
+
+ if ( bChanged )
+ *ppFontList = pNewFontList;
+ }
+
+ if ( pBox )
+ pBox->set_sensitive(true);
+ }
+ else if ( pBox && ( pDocSh || !ppFontList ))
+ {
+ // Disable box only when we have a SfxObjectShell and didn't get a font list OR
+ // we don't have a SfxObjectShell and no current font list.
+ // It's possible that we currently have no SfxObjectShell, but a current font list.
+ // See #i58471: When a user set the focus into the font name combo box and opens
+ // the help window with F1. After closing the help window, we disable the font name
+ // combo box. The SfxObjectShell::Current() method returns in that case zero. But the
+ // font list hasn't changed and therefore the combo box shouldn't be disabled!
+ pBox->set_sensitive(false);
+ }
+
+ // Fill the FontBox, also the new list if necessary
+ if ( pBox && bChanged )
+ {
+ if ( *ppFontList )
+ pBox->Fill( *ppFontList );
+ else
+ pBox->Clear();
+ }
+ return bChanged;
+}
+
+SvxFontNameBox_Base::SvxFontNameBox_Base(std::unique_ptr<weld::ComboBox> xWidget,
+ const Reference<XDispatchProvider>& rDispatchProvider,
+ const Reference<XFrame>& rFrame,
+ SvxFontNameToolBoxControl& rCtrl)
+ : m_xListener(new comphelper::ConfigurationListener("/org.openoffice.Office.Common/Font/View"))
+ , m_aWYSIWYG(m_xListener, "ShowFontBoxWYSIWYG", *this)
+ , m_aHistory(m_xListener, "History", *this)
+ , m_rCtrl(rCtrl)
+ , m_xWidget(new FontNameBox(std::move(xWidget)))
+ , pFontList(nullptr)
+ , nFtCount(0)
+ , bRelease(true)
+ , m_xDispatchProvider(rDispatchProvider)
+ , m_xFrame(rFrame)
+ , mbCheckingUnknownFont(false)
+{
+ EnableControls();
+
+ m_xWidget->connect_changed(LINK(this, SvxFontNameBox_Base, SelectHdl));
+ m_xWidget->connect_key_press(LINK(this, SvxFontNameBox_Base, KeyInputHdl));
+ m_xWidget->connect_entry_activate(LINK(this, SvxFontNameBox_Base, ActivateHdl));
+ m_xWidget->connect_focus_in(LINK(this, SvxFontNameBox_Base, FocusInHdl));
+ m_xWidget->connect_focus_out(LINK(this, SvxFontNameBox_Base, FocusOutHdl));
+ m_xWidget->connect_get_property_tree(LINK(this, SvxFontNameBox_Base, DumpAsPropertyTreeHdl));
+
+ m_xWidget->set_entry_width_chars(COMBO_WIDTH_IN_CHARS + 5);
+}
+
+SvxFontNameBox_Impl::SvxFontNameBox_Impl(vcl::Window* pParent, const Reference<XDispatchProvider>& rDispatchProvider,
+ const Reference<XFrame>& rFrame, SvxFontNameToolBoxControl& rCtrl)
+ : InterimItemWindow(pParent, "svx/ui/fontnamebox.ui", "FontNameBox")
+ , SvxFontNameBox_Base(m_xBuilder->weld_combo_box("fontnamecombobox"), rDispatchProvider, rFrame, rCtrl)
+{
+ set_id("fontnamecombobox");
+ SetOptimalSize();
+}
+
+void SvxFontNameBox_Base::FillList()
+{
+ if (!m_xWidget) // e.g. disposed
+ return;
+ // Save old Selection, set back in the end
+ int nStartPos, nEndPos;
+ m_xWidget->get_entry_selection_bounds(nStartPos, nEndPos);
+
+ // Did Doc-Fontlist change?
+ lcl_GetDocFontList(&pFontList, this);
+
+ m_xWidget->select_entry_region(nStartPos, nEndPos);
+}
+
+void SvxFontNameBox_Base::CheckAndMarkUnknownFont()
+{
+ if (mbCheckingUnknownFont) //tdf#117537 block rentry
+ return;
+ mbCheckingUnknownFont = true;
+ OUString fontname = m_xWidget->get_active_text();
+ lcl_GetDocFontList( &pFontList, this );
+ // If the font is unknown, show it in italic.
+ vcl::Font font = m_xWidget->get_entry_font();
+ if( pFontList != nullptr && pFontList->IsAvailable( fontname ))
+ {
+ if( font.GetItalic() != ITALIC_NONE )
+ {
+ font.SetItalic( ITALIC_NONE );
+ m_xWidget->set_entry_font(font);
+ m_xWidget->set_tooltip_text(SvxResId(RID_SVXSTR_CHARFONTNAME));
+ }
+ }
+ else
+ {
+ if( font.GetItalic() != ITALIC_NORMAL )
+ {
+ font.SetItalic( ITALIC_NORMAL );
+ m_xWidget->set_entry_font(font);
+ m_xWidget->set_tooltip_text(SvxResId(RID_SVXSTR_CHARFONTNAME_NOTAVAILABLE));
+ }
+ }
+ mbCheckingUnknownFont = false;
+}
+
+void SvxFontNameBox_Base::Update( const css::awt::FontDescriptor* pFontDesc )
+{
+ if ( pFontDesc )
+ {
+ aCurFont.SetFamilyName ( pFontDesc->Name );
+ aCurFont.SetFamily ( FontFamily( pFontDesc->Family ) );
+ aCurFont.SetStyleName ( pFontDesc->StyleName );
+ aCurFont.SetPitch ( FontPitch( pFontDesc->Pitch ) );
+ aCurFont.SetCharSet ( rtl_TextEncoding( pFontDesc->CharSet ) );
+ }
+ OUString aCurName = aCurFont.GetFamilyName();
+ OUString aText = m_xWidget->get_active_text();
+ if (aText != aCurName)
+ set_active_or_entry_text(aCurName);
+}
+
+void SvxFontNameBox_Base::set_active_or_entry_text(const OUString& rText)
+{
+ m_xWidget->set_active_or_entry_text(rText);
+ CheckAndMarkUnknownFont();
+}
+
+IMPL_LINK_NOARG(SvxFontNameBox_Base, FocusInHdl, weld::Widget&, void)
+{
+ FillList();
+}
+
+IMPL_LINK(SvxFontNameBox_Base, KeyInputHdl, const KeyEvent&, rKEvt, bool)
+{
+ return DoKeyInput(rKEvt);
+}
+
+bool SvxFontNameBox_Base::DoKeyInput(const KeyEvent& rKEvt)
+{
+ bool bHandled = false;
+
+ sal_uInt16 nCode = rKEvt.GetKeyCode().GetCode();
+
+ switch (nCode)
+ {
+ case KEY_TAB:
+ bRelease = false;
+ Select(true);
+ break;
+
+ case KEY_ESCAPE:
+ set_active_or_entry_text(m_xWidget->get_saved_value());
+ if (!m_rCtrl.IsInSidebar())
+ {
+ ReleaseFocus_Impl();
+ bHandled = true;
+ }
+ EndPreview();
+ break;
+ }
+
+ return bHandled;
+}
+
+bool SvxFontNameBox_Impl::DoKeyInput(const KeyEvent& rKEvt)
+{
+ return SvxFontNameBox_Base::DoKeyInput(rKEvt) || ChildKeyInput(rKEvt);
+}
+
+IMPL_LINK_NOARG(SvxFontNameBox_Base, 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
+ {
+ set_active_or_entry_text(m_xWidget->get_saved_value());
+ // send EndPreview
+ EndPreview();
+ }
+}
+
+void SvxFontNameBox_Impl::SetOptimalSize()
+{
+ // set width in chars low so the size request will not be overridden
+ m_xWidget->set_entry_width_chars(1);
+ // tdf#132338 purely using this calculation to keep things their traditional width
+ Size aSize(LogicToPixel(Size((COMBO_WIDTH_IN_CHARS +5) * 4, 0), MapMode(MapUnit::MapAppFont)));
+ m_xWidget->set_size_request(aSize.Width(), -1);
+
+ SetSizePixel(get_preferred_size());
+}
+
+void SvxFontNameBox_Impl::DataChanged( const DataChangedEvent& rDCEvt )
+{
+ if ( (rDCEvt.GetType() == DataChangedEventType::SETTINGS) &&
+ (rDCEvt.GetFlags() & AllSettingsFlags::STYLE) )
+ {
+ SetOptimalSize();
+ }
+ else if ( ( rDCEvt.GetType() == DataChangedEventType::FONTS ) ||
+ ( rDCEvt.GetType() == DataChangedEventType::DISPLAY ) )
+ {
+ // The old font list in shell has likely been destroyed at this point, so we need to get
+ // the new one before doing anything further.
+ lcl_GetDocFontList( &pFontList, this );
+ }
+}
+
+void SvxFontNameBox_Base::ReleaseFocus_Impl()
+{
+ if ( !bRelease )
+ {
+ bRelease = true;
+ return;
+ }
+ if ( m_xFrame.is() && m_xFrame->getContainerWindow().is() )
+ m_xFrame->getContainerWindow()->setFocus();
+}
+
+void SvxFontNameBox_Base::EnableControls()
+{
+ bool bEnableMRU = m_aHistory.get();
+ sal_uInt16 nEntries = bEnableMRU ? MAX_MRU_FONTNAME_ENTRIES : 0;
+
+ bool bNewWYSIWYG = m_aWYSIWYG.get();
+ bool bOldWYSIWYG = m_xWidget->IsWYSIWYGEnabled();
+
+ if (m_xWidget->get_max_mru_count() != nEntries || bNewWYSIWYG != bOldWYSIWYG)
+ {
+ // refill in the next GetFocus-Handler
+ pFontList = nullptr;
+ Clear();
+ m_xWidget->set_max_mru_count(nEntries);
+ }
+
+ if (bNewWYSIWYG != bOldWYSIWYG)
+ m_xWidget->EnableWYSIWYG(bNewWYSIWYG);
+}
+
+IMPL_LINK(SvxFontNameBox_Base, SelectHdl, weld::ComboBox&, rCombo, void)
+{
+ Select(rCombo.changed_by_direct_pick()); // only when picked from the list
+}
+
+IMPL_LINK_NOARG(SvxFontNameBox_Base, ActivateHdl, weld::ComboBox&, bool)
+{
+ Select(true);
+ return true;
+}
+
+void SvxFontNameBox_Base::Select(bool bNonTravelSelect)
+{
+ Sequence< PropertyValue > aArgs( 1 );
+ auto pArgs = aArgs.getArray();
+ std::unique_ptr<SvxFontItem> pFontItem;
+ if ( pFontList )
+ {
+ FontMetric aFontMetric( pFontList->Get(m_xWidget->get_active_text(),
+ aCurFont.GetWeight(),
+ aCurFont.GetItalic() ) );
+ aCurFont = aFontMetric;
+
+ pFontItem.reset( new SvxFontItem( aFontMetric.GetFamilyType(),
+ aFontMetric.GetFamilyName(),
+ aFontMetric.GetStyleName(),
+ aFontMetric.GetPitch(),
+ aFontMetric.GetCharSet(),
+ SID_ATTR_CHAR_FONT ) );
+
+ Any a;
+ pFontItem->QueryValue( a );
+ pArgs[0].Value = a;
+ }
+
+ if (bNonTravelSelect)
+ {
+ CheckAndMarkUnknownFont();
+ // #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();
+ EndPreview();
+ if (pFontItem)
+ {
+ pArgs[0].Name = "CharFontName";
+ SfxToolBoxControl::Dispatch( m_xDispatchProvider,
+ ".uno:CharFontName",
+ aArgs );
+ }
+ }
+ else
+ {
+ if (pFontItem)
+ {
+ pArgs[0].Name = "CharPreviewFontName";
+ SfxToolBoxControl::Dispatch( m_xDispatchProvider,
+ ".uno:CharPreviewFontName",
+ aArgs );
+ }
+ }
+}
+
+IMPL_LINK(SvxFontNameBox_Base, DumpAsPropertyTreeHdl, tools::JsonWriter&, rJsonWriter, void)
+{
+ {
+ auto entriesNode = rJsonWriter.startNode("entries");
+ for (int i = 0, nEntryCount = m_xWidget->get_count(); i < nEntryCount; ++i)
+ {
+ auto entryNode = rJsonWriter.startNode("");
+ rJsonWriter.put("", m_xWidget->get_text(i));
+ }
+ }
+
+ int nSelectedEntry = m_xWidget->get_active();
+ rJsonWriter.put("selectedCount", static_cast<sal_Int32>(nSelectedEntry == -1 ? 0 : 1));
+
+ {
+ auto selectedNode = rJsonWriter.startNode("selectedEntries");
+ if (nSelectedEntry != -1)
+ {
+ auto entryNode = rJsonWriter.startNode("");
+ rJsonWriter.put("", m_xWidget->get_text(nSelectedEntry));
+ }
+ }
+
+ rJsonWriter.put("command", ".uno:CharFontName");
+}
+
+ColorWindow::ColorWindow(const OUString& rCommand,
+ std::shared_ptr<PaletteManager> const & rPaletteManager,
+ ColorStatus& rColorStatus,
+ sal_uInt16 nSlotId,
+ const Reference< XFrame >& rFrame,
+ const MenuOrToolMenuButton& rMenuButton,
+ TopLevelParentFunction const& rTopLevelParentFunction,
+ ColorSelectFunction const & rColorSelectFunction)
+ : WeldToolbarPopup(rFrame, rMenuButton.get_widget(), "svx/ui/colorwindow.ui", "palette_popup_window")
+ , theSlotId(nSlotId)
+ , maCommand(rCommand)
+ , maMenuButton(rMenuButton)
+ , mxPaletteManager(rPaletteManager)
+ , mrColorStatus(rColorStatus)
+ , maTopLevelParentFunction(rTopLevelParentFunction)
+ , maColorSelectFunction(rColorSelectFunction)
+ , mxColorSet(new SvxColorValueSet(m_xBuilder->weld_scrolled_window("colorsetwin", true)))
+ , mxRecentColorSet(new SvxColorValueSet(nullptr))
+ , mxPaletteListBox(m_xBuilder->weld_combo_box("palette_listbox"))
+ , mxButtonAutoColor(m_xBuilder->weld_button("auto_color_button"))
+ , mxButtonNoneColor(m_xBuilder->weld_button("none_color_button"))
+ , mxButtonPicker(m_xBuilder->weld_button("color_picker_button"))
+ , mxAutomaticSeparator(m_xBuilder->weld_widget("separator4"))
+ , mxColorSetWin(new weld::CustomWeld(*m_xBuilder, "colorset", *mxColorSet))
+ , mxRecentColorSetWin(new weld::CustomWeld(*m_xBuilder, "recent_colorset", *mxRecentColorSet))
+ , mpDefaultButton(nullptr)
+{
+ mxColorSet->SetStyle( WinBits(WB_FLATVALUESET | WB_ITEMBORDER | WB_3DLOOK | WB_NO_DIRECTSELECT | WB_TABSTOP) );
+ mxRecentColorSet->SetStyle( WinBits(WB_FLATVALUESET | WB_ITEMBORDER | WB_3DLOOK | WB_NO_DIRECTSELECT | WB_TABSTOP) );
+
+ switch ( theSlotId )
+ {
+ case SID_ATTR_CHAR_COLOR_BACKGROUND:
+ case SID_BACKGROUND_COLOR:
+ case SID_ATTR_CHAR_BACK_COLOR:
+ case SID_TABLE_CELL_BACKGROUND_COLOR:
+ {
+ mxButtonAutoColor->set_label( SvxResId( RID_SVXSTR_NOFILL ) );
+ break;
+ }
+ case SID_AUTHOR_COLOR:
+ {
+ mxButtonAutoColor->set_label( SvxResId( RID_SVXSTR_BY_AUTHOR ) );
+ break;
+ }
+ case SID_BMPMASK_COLOR:
+ {
+ mxButtonAutoColor->set_label( SvxResId( RID_SVXSTR_TRANSPARENT ) );
+ break;
+ }
+ case SID_ATTR_CHAR_COLOR:
+ case SID_ATTR_CHAR_COLOR2:
+ case SID_EXTRUSION_3D_COLOR:
+ {
+ mxButtonAutoColor->set_label(EditResId(RID_SVXSTR_AUTOMATIC));
+ break;
+ }
+ case SID_FM_CTL_PROPERTIES:
+ {
+ mxButtonAutoColor->set_label( SvxResId( RID_SVXSTR_DEFAULT ) );
+ break;
+ }
+ default:
+ {
+ mxButtonAutoColor->hide();
+ mxAutomaticSeparator->hide();
+ break;
+ }
+ }
+
+ mxPaletteListBox->connect_changed(LINK(this, ColorWindow, SelectPaletteHdl));
+ std::vector<OUString> aPaletteList = mxPaletteManager->GetPaletteList();
+ mxPaletteListBox->freeze();
+ for (const auto& rPalette : aPaletteList)
+ mxPaletteListBox->append_text(rPalette);
+ mxPaletteListBox->thaw();
+ OUString aPaletteName( officecfg::Office::Common::UserColors::PaletteName::get() );
+ mxPaletteListBox->set_active_text(aPaletteName);
+ const int nSelectedEntry(mxPaletteListBox->get_active());
+ if (nSelectedEntry != -1)
+ mxPaletteManager->SetPalette(nSelectedEntry);
+
+ mxButtonAutoColor->connect_clicked(LINK(this, ColorWindow, AutoColorClickHdl));
+ mxButtonNoneColor->connect_clicked(LINK(this, ColorWindow, AutoColorClickHdl));
+ mxButtonPicker->connect_clicked(LINK(this, ColorWindow, OpenPickerClickHdl));
+
+ mxColorSet->SetSelectHdl(LINK( this, ColorWindow, SelectHdl));
+ mxRecentColorSet->SetSelectHdl(LINK( this, ColorWindow, SelectHdl));
+ m_xTopLevel->set_help_id(HID_POPUP_COLOR);
+ mxColorSet->SetHelpId(HID_POPUP_COLOR_CTRL);
+
+ mxPaletteManager->ReloadColorSet(*mxColorSet);
+ const sal_uInt32 nMaxItems(SvxColorValueSet::getMaxRowCount() * SvxColorValueSet::getColumnCount());
+ Size aSize = mxColorSet->layoutAllVisible(nMaxItems);
+ mxColorSet->set_size_request(aSize.Width(), aSize.Height());
+
+ mxPaletteManager->ReloadRecentColorSet(*mxRecentColorSet);
+ aSize = mxRecentColorSet->layoutAllVisible(mxPaletteManager->GetRecentColorCount());
+ mxRecentColorSet->set_size_request(aSize.Width(), aSize.Height());
+
+ AddStatusListener( ".uno:ColorTableState" );
+ AddStatusListener( maCommand );
+ if ( maCommand == ".uno:FrameLineColor" )
+ {
+ AddStatusListener( ".uno:BorderTLBR" );
+ AddStatusListener( ".uno:BorderBLTR" );
+ }
+}
+
+void ColorWindow::GrabFocus()
+{
+ if (mxColorSet->IsNoSelection() && mpDefaultButton)
+ mpDefaultButton->grab_focus();
+ else
+ mxColorSet->GrabFocus();
+}
+
+void ColorWindow::ShowNoneButton()
+{
+ mxButtonNoneColor->show();
+}
+
+ColorWindow::~ColorWindow()
+{
+}
+
+NamedColor ColorWindow::GetSelectEntryColor(ValueSet const * pColorSet)
+{
+ Color aColor = pColorSet->GetItemColor(pColorSet->GetSelectedItemId());
+ OUString sColorName = pColorSet->GetItemText(pColorSet->GetSelectedItemId());
+ return std::make_pair(aColor, sColorName);
+}
+
+namespace
+{
+ NamedColor GetAutoColor(sal_uInt16 nSlotId)
+ {
+ Color aColor;
+ OUString sColorName;
+ switch (nSlotId)
+ {
+ case SID_ATTR_CHAR_COLOR_BACKGROUND:
+ case SID_BACKGROUND_COLOR:
+ case SID_ATTR_CHAR_BACK_COLOR:
+ case SID_TABLE_CELL_BACKGROUND_COLOR:
+ aColor = COL_TRANSPARENT;
+ sColorName = SvxResId(RID_SVXSTR_NOFILL);
+ break;
+ case SID_AUTHOR_COLOR:
+ aColor = COL_TRANSPARENT;
+ sColorName = SvxResId(RID_SVXSTR_BY_AUTHOR);
+ break;
+ case SID_BMPMASK_COLOR:
+ aColor = COL_TRANSPARENT;
+ sColorName = SvxResId(RID_SVXSTR_TRANSPARENT);
+ break;
+ case SID_FM_CTL_PROPERTIES:
+ aColor = COL_TRANSPARENT;
+ sColorName = SvxResId(RID_SVXSTR_DEFAULT);
+ break;
+ case SID_ATTR_CHAR_COLOR:
+ case SID_ATTR_CHAR_COLOR2:
+ case SID_EXTRUSION_3D_COLOR:
+ default:
+ aColor = COL_AUTO;
+ sColorName = EditResId(RID_SVXSTR_AUTOMATIC);
+ break;
+ }
+
+ return std::make_pair(aColor, sColorName);
+ }
+
+ NamedColor GetNoneColor()
+ {
+ return std::make_pair(COL_NONE_COLOR, comphelper::LibreOfficeKit::isActive() ? SvxResId(RID_SVXSTR_INVISIBLE)
+ : SvxResId(RID_SVXSTR_NONE));
+ }
+}
+
+NamedColor ColorWindow::GetSelectEntryColor() const
+{
+ if (!mxColorSet->IsNoSelection())
+ return GetSelectEntryColor(mxColorSet.get());
+ if (!mxRecentColorSet->IsNoSelection())
+ return GetSelectEntryColor(mxRecentColorSet.get());
+ if (mxButtonNoneColor.get() == mpDefaultButton)
+ return GetNoneColor();
+ return GetAutoColor();
+}
+
+IMPL_LINK(ColorWindow, SelectHdl, ValueSet*, pColorSet, void)
+{
+ NamedColor aNamedColor = GetSelectEntryColor(pColorSet);
+
+ if (pColorSet != mxRecentColorSet.get())
+ {
+ mxPaletteManager->AddRecentColor(aNamedColor.first, aNamedColor.second);
+ if (!maMenuButton.get_active())
+ mxPaletteManager->ReloadRecentColorSet(*mxRecentColorSet);
+ }
+
+ maSelectedLink.Call(aNamedColor);
+
+ // deliberate take a copy here in case maMenuButton.set_inactive
+ // triggers a callback that destroys ourself
+ ColorSelectFunction aColorSelectFunction(maColorSelectFunction);
+ OUString sCommand(maCommand);
+ // Same for querying IsTheme early.
+ bool bThemePaletteSelected = mxPaletteManager->IsThemePaletteSelected();
+ sal_uInt16 nSelectedItemId = pColorSet->GetSelectedItemId();
+
+ maMenuButton.set_inactive();
+
+ auto aNamedThemedColor = svx::NamedThemedColor::FromNamedColor(aNamedColor);
+ if (bThemePaletteSelected)
+ {
+ PaletteManager::GetThemeIndexLumModOff(nSelectedItemId, aNamedThemedColor.m_nThemeIndex,
+ aNamedThemedColor.m_nLumMod,
+ aNamedThemedColor.m_nLumOff);
+ }
+ aColorSelectFunction(sCommand, aNamedThemedColor);
+}
+
+IMPL_LINK_NOARG(ColorWindow, SelectPaletteHdl, weld::ComboBox&, void)
+{
+ int nPos = mxPaletteListBox->get_active();
+ mxPaletteManager->SetPalette( nPos );
+ mxPaletteManager->ReloadColorSet(*mxColorSet);
+ mxColorSet->layoutToGivenHeight(mxColorSet->GetOutputSizePixel().Height(), mxPaletteManager->GetColorCount());
+}
+
+NamedColor ColorWindow::GetAutoColor() const
+{
+ return ::GetAutoColor(theSlotId);
+}
+
+IMPL_LINK(ColorWindow, AutoColorClickHdl, weld::Button&, rButton, void)
+{
+ NamedColor aNamedColor = &rButton == mxButtonAutoColor.get() ? GetAutoColor() : GetNoneColor();
+
+ mxColorSet->SetNoSelection();
+ mxRecentColorSet->SetNoSelection();
+ mpDefaultButton = &rButton;
+
+ maSelectedLink.Call(aNamedColor);
+
+ // deliberate take a copy here in case maMenuButton.set_inactive
+ // triggers a callback that destroys ourself
+ ColorSelectFunction aColorSelectFunction(maColorSelectFunction);
+ OUString sCommand(maCommand);
+
+ maMenuButton.set_inactive();
+
+ aColorSelectFunction(sCommand, svx::NamedThemedColor::FromNamedColor(aNamedColor));
+}
+
+IMPL_LINK_NOARG(ColorWindow, OpenPickerClickHdl, weld::Button&, void)
+{
+ // copy before set_inactive
+ auto nColor = GetSelectEntryColor().first;
+ auto pParentWindow = maTopLevelParentFunction();
+ OUString sCommand = maCommand;
+ std::shared_ptr<PaletteManager> xPaletteManager(mxPaletteManager);
+
+ maMenuButton.set_inactive();
+
+ xPaletteManager->PopupColorPicker(pParentWindow, sCommand, nColor);
+}
+
+void ColorWindow::SetNoSelection()
+{
+ mxColorSet->SetNoSelection();
+ mxRecentColorSet->SetNoSelection();
+ mpDefaultButton = nullptr;
+}
+
+bool ColorWindow::IsNoSelection() const
+{
+ if (!mxColorSet->IsNoSelection())
+ return false;
+ if (!mxRecentColorSet->IsNoSelection())
+ return false;
+ return !mxButtonAutoColor->get_visible() && !mxButtonNoneColor->get_visible();
+}
+
+void ColorWindow::statusChanged( const css::frame::FeatureStateEvent& rEvent )
+{
+ if (rEvent.FeatureURL.Complete == ".uno:ColorTableState")
+ {
+ if (rEvent.IsEnabled && mxPaletteManager->GetPalette() == 0)
+ {
+ mxPaletteManager->ReloadColorSet(*mxColorSet);
+ mxColorSet->layoutToGivenHeight(mxColorSet->GetOutputSizePixel().Height(), mxPaletteManager->GetColorCount());
+ }
+ }
+ else
+ {
+ mrColorStatus.statusChanged(rEvent);
+ SelectEntry(mrColorStatus.GetColor());
+ }
+}
+
+bool ColorWindow::SelectValueSetEntry(SvxColorValueSet* pColorSet, const Color& rColor)
+{
+ for (size_t i = 1; i <= pColorSet->GetItemCount(); ++i)
+ {
+ if (rColor == pColorSet->GetItemColor(i))
+ {
+ pColorSet->SelectItem(i);
+ return true;
+ }
+ }
+ return false;
+}
+
+void ColorWindow::SelectEntry(const NamedColor& rNamedColor)
+{
+ SetNoSelection();
+
+ const Color &rColor = rNamedColor.first;
+
+ if (mxButtonAutoColor->get_visible() && (rColor == COL_TRANSPARENT || rColor == COL_AUTO))
+ {
+ mpDefaultButton = mxButtonAutoColor.get();
+ return;
+ }
+
+ if (mxButtonNoneColor->get_visible() && rColor == COL_NONE_COLOR)
+ {
+ mpDefaultButton = mxButtonNoneColor.get();
+ return;
+ }
+
+ // try current palette
+ bool bFoundColor = SelectValueSetEntry(mxColorSet.get(), rColor);
+ // try recently used
+ if (!bFoundColor)
+ bFoundColor = SelectValueSetEntry(mxRecentColorSet.get(), rColor);
+ // if it's not there, add it there now to the end of the recently used
+ // so its available somewhere handy, but not without trashing the
+ // whole recently used
+ if (!bFoundColor)
+ {
+ const OUString& rColorName = rNamedColor.second;
+ mxPaletteManager->AddRecentColor(rColor, rColorName, false);
+ mxPaletteManager->ReloadRecentColorSet(*mxRecentColorSet);
+ SelectValueSetEntry(mxRecentColorSet.get(), rColor);
+ }
+}
+
+void ColorWindow::SelectEntry(const Color& rColor)
+{
+ OUString sColorName = "#" + rColor.AsRGBHexString().toAsciiUpperCase();
+ ColorWindow::SelectEntry(std::make_pair(rColor, sColorName));
+}
+
+ColorStatus::ColorStatus() :
+ maColor( COL_TRANSPARENT ),
+ maTLBRColor( COL_TRANSPARENT ),
+ maBLTRColor( COL_TRANSPARENT )
+{
+}
+
+void ColorStatus::statusChanged( const css::frame::FeatureStateEvent& rEvent )
+{
+ Color aColor( COL_TRANSPARENT );
+ css::table::BorderLine2 aTable;
+
+ if ( rEvent.State >>= aTable )
+ {
+ SvxBorderLine aLine;
+ SvxBoxItem::LineToSvxLine( aTable, aLine, false );
+ if ( !aLine.isEmpty() )
+ aColor = aLine.GetColor();
+ }
+ else
+ rEvent.State >>= aColor;
+
+ if ( rEvent.FeatureURL.Path == "BorderTLBR" )
+ maTLBRColor = aColor;
+ else if ( rEvent.FeatureURL.Path == "BorderBLTR" )
+ maBLTRColor = aColor;
+ else
+ maColor = aColor;
+}
+
+Color ColorStatus::GetColor()
+{
+ Color aColor( maColor );
+
+ if ( maTLBRColor != COL_TRANSPARENT )
+ {
+ if ( aColor != maTLBRColor && aColor != COL_TRANSPARENT )
+ return COL_TRANSPARENT;
+ aColor = maTLBRColor;
+ }
+
+ if ( maBLTRColor != COL_TRANSPARENT )
+ {
+ if ( aColor != maBLTRColor && aColor != COL_TRANSPARENT )
+ return COL_TRANSPARENT;
+ return maBLTRColor;
+ }
+
+ return aColor;
+}
+
+
+SvxFrameWindow_Impl::SvxFrameWindow_Impl(SvxFrameToolBoxControl* pControl, weld::Widget* pParent)
+ : WeldToolbarPopup(pControl->getFrameInterface(), pParent, "svx/ui/floatingframeborder.ui", "FloatingFrameBorder")
+ , mxControl(pControl)
+ , mxFrameSet(new SvxFrmValueSet_Impl)
+ , mxFrameSetWin(new weld::CustomWeld(*m_xBuilder, "valueset", *mxFrameSet))
+ , bParagraphMode(false)
+ , m_bIsWriter(false)
+{
+
+ // check whether the document is Writer or not
+ if (Reference<lang::XServiceInfo> xSI{ m_xFrame->getController()->getModel(), UNO_QUERY })
+ m_bIsWriter = xSI->supportsService("com.sun.star.text.TextDocument");
+
+ mxFrameSet->SetStyle(WB_ITEMBORDER | WB_DOUBLEBORDER | WB_3DLOOK | WB_NO_DIRECTSELECT);
+ AddStatusListener(".uno:BorderReducedMode");
+ InitImageList();
+
+ /*
+ * 1 2 3 4 5
+ * ------------------------------------------------------
+ * NONE LEFT RIGHT LEFTRIGHT DIAGONALDOWN
+ * TOP BOTTOM TOPBOTTOM OUTER DIAGONALUP
+ * ------------------------------------------------------
+ * HOR HORINNER VERINNER ALL CRISSCROSS <- can be switched of via bParagraphMode
+ */
+
+ sal_uInt16 i = 0;
+
+ // diagonal borders available only for Calc.
+ // Therefore, Calc uses 10 border types while
+ // Writer uses 8 of them - for a single cell.
+ for ( i=1; i < (m_bIsWriter ? 9 : 11); i++ )
+ mxFrameSet->InsertItem(i, Image(aImgVec[i-1].first), aImgVec[i-1].second);
+
+ //bParagraphMode should have been set in StateChanged
+ if ( !bParagraphMode )
+ // when multiple cell selected:
+ // Writer has 12 border types and Calc has 15 of them.
+ for ( i = (m_bIsWriter ? 9 : 11); i < (m_bIsWriter ? 13 : 16); i++ )
+ mxFrameSet->InsertItem(i, Image(aImgVec[i-1].first), aImgVec[i-1].second);
+
+ // adjust frame column for Writer
+ sal_uInt16 colCount = m_bIsWriter ? 4 : 5;
+ mxFrameSet->SetColCount( colCount );
+ mxFrameSet->SetSelectHdl( LINK( this, SvxFrameWindow_Impl, SelectHdl ) );
+ CalcSizeValueSet();
+
+ mxFrameSet->SetHelpId( HID_POPUP_FRAME );
+ mxFrameSet->SetAccessibleName( SvxResId(RID_SVXSTR_FRAME) );
+}
+
+namespace {
+
+enum class FrmValidFlags {
+ NONE = 0x00,
+ Left = 0x01,
+ Right = 0x02,
+ Top = 0x04,
+ Bottom = 0x08,
+ HInner = 0x10,
+ VInner = 0x20,
+ AllMask = 0x3f,
+};
+
+}
+
+namespace o3tl {
+ template<> struct typed_flags<FrmValidFlags> : is_typed_flags<FrmValidFlags, 0x3f> {};
+}
+
+// By default unset lines remain unchanged.
+// Via Shift unset lines are reset
+
+IMPL_LINK_NOARG(SvxFrameWindow_Impl, SelectHdl, ValueSet*, void)
+{
+ SvxBoxItem aBorderOuter( SID_ATTR_BORDER_OUTER );
+ SvxBoxInfoItem aBorderInner( SID_ATTR_BORDER_INNER );
+ SvxBorderLine theDefLine;
+
+ // diagonal down border
+ SvxBorderLine dDownBorderLine(nullptr, SvxBorderLineWidth::Hairline);
+ SvxLineItem dDownLineItem(SID_ATTR_BORDER_DIAG_TLBR);
+
+ // diagonal up border
+ SvxBorderLine dUpBorderLine(nullptr, SvxBorderLineWidth::Hairline);
+ SvxLineItem dUpLineItem(SID_ATTR_BORDER_DIAG_BLTR);
+
+ bool bIsDiagonalBorder = false;
+
+ SvxBorderLine *pLeft = nullptr,
+ *pRight = nullptr,
+ *pTop = nullptr,
+ *pBottom = nullptr;
+ sal_uInt16 nSel = mxFrameSet->GetSelectedItemId();
+ sal_uInt16 nModifier = mxFrameSet->GetModifier();
+ FrmValidFlags nValidFlags = FrmValidFlags::NONE;
+
+ // tdf#48622, tdf#145828 use correct default to create intended 0.75pt
+ // cell border using the border formatting tool in the standard toolbar
+ theDefLine.GuessLinesWidths(theDefLine.GetBorderLineStyle(), SvxBorderLineWidth::Thin);
+
+ // nSel has 15 cases which means 15 border
+ // types for Calc. But Writer uses only 12
+ // of them - when diagonal borders excluded.
+ if (m_bIsWriter)
+ {
+ // add appropriate increments
+ // to match the correct borders.
+ if (nSel > 8) { nSel += 2; }
+ else if (nSel > 4) { nSel++; }
+ }
+
+ switch ( nSel )
+ {
+ case 1: nValidFlags |= FrmValidFlags::AllMask;
+ // set nullptr to remove diagonal lines
+ dDownLineItem.SetLine(nullptr);
+ dUpLineItem.SetLine(nullptr);
+ SetDiagonalDownBorder(dDownLineItem);
+ SetDiagonalUpBorder(dUpLineItem);
+ break; // NONE
+ case 2: pLeft = &theDefLine;
+ nValidFlags |= FrmValidFlags::Left;
+ break; // LEFT
+ case 3: pRight = &theDefLine;
+ nValidFlags |= FrmValidFlags::Right;
+ break; // RIGHT
+ case 4: pLeft = pRight = &theDefLine;
+ nValidFlags |= FrmValidFlags::Right|FrmValidFlags::Left;
+ break; // LEFTRIGHT
+ case 5: dDownLineItem.SetLine(&dDownBorderLine);
+ SetDiagonalDownBorder(dDownLineItem);
+ bIsDiagonalBorder = true;
+ break; // DIAGONAL DOWN
+ case 6: pTop = &theDefLine;
+ nValidFlags |= FrmValidFlags::Top;
+ break; // TOP
+ case 7: pBottom = &theDefLine;
+ nValidFlags |= FrmValidFlags::Bottom;
+ break; // BOTTOM
+ case 8: pTop = pBottom = &theDefLine;
+ nValidFlags |= FrmValidFlags::Bottom|FrmValidFlags::Top;
+ break; // TOPBOTTOM
+ case 9: pLeft = pRight = pTop = pBottom = &theDefLine;
+ nValidFlags |= FrmValidFlags::Left | FrmValidFlags::Right | FrmValidFlags::Top | FrmValidFlags::Bottom;
+ break; // OUTER
+ case 10:
+ dUpLineItem.SetLine(&dUpBorderLine);
+ SetDiagonalUpBorder(dUpLineItem);
+ bIsDiagonalBorder = true;
+ break; // DIAGONAL UP
+
+ // Inner Table:
+ case 11: // HOR
+ pTop = pBottom = &theDefLine;
+ aBorderInner.SetLine( &theDefLine, SvxBoxInfoItemLine::HORI );
+ aBorderInner.SetLine( nullptr, SvxBoxInfoItemLine::VERT );
+ nValidFlags |= FrmValidFlags::HInner|FrmValidFlags::Top|FrmValidFlags::Bottom;
+ break;
+
+ case 12: // HORINNER
+ pLeft = pRight = pTop = pBottom = &theDefLine;
+ aBorderInner.SetLine( &theDefLine, SvxBoxInfoItemLine::HORI );
+ aBorderInner.SetLine( nullptr, SvxBoxInfoItemLine::VERT );
+ nValidFlags |= FrmValidFlags::Right|FrmValidFlags::Left|FrmValidFlags::HInner|FrmValidFlags::Top|FrmValidFlags::Bottom;
+ break;
+
+ case 13: // VERINNER
+ pLeft = pRight = pTop = pBottom = &theDefLine;
+ aBorderInner.SetLine( nullptr, SvxBoxInfoItemLine::HORI );
+ aBorderInner.SetLine( &theDefLine, SvxBoxInfoItemLine::VERT );
+ nValidFlags |= FrmValidFlags::Right|FrmValidFlags::Left|FrmValidFlags::VInner|FrmValidFlags::Top|FrmValidFlags::Bottom;
+ break;
+
+ case 14: // ALL
+ pLeft = pRight = pTop = pBottom = &theDefLine;
+ aBorderInner.SetLine( &theDefLine, SvxBoxInfoItemLine::HORI );
+ aBorderInner.SetLine( &theDefLine, SvxBoxInfoItemLine::VERT );
+ nValidFlags |= FrmValidFlags::AllMask;
+ break;
+
+ case 15:
+ // set both diagonal lines to draw criss-cross line
+ dDownLineItem.SetLine(&dDownBorderLine);
+ dUpLineItem.SetLine(&dUpBorderLine);
+
+ SetDiagonalDownBorder(dDownLineItem);
+ SetDiagonalUpBorder(dUpLineItem);
+ bIsDiagonalBorder = true;
+ break; // CRISS-CROSS
+
+ default:
+ break;
+ }
+
+ // if diagonal borders selected,
+ // no need to execute this block
+ if (!bIsDiagonalBorder)
+ {
+ aBorderOuter.SetLine( pLeft, SvxBoxItemLine::LEFT );
+ aBorderOuter.SetLine( pRight, SvxBoxItemLine::RIGHT );
+ aBorderOuter.SetLine( pTop, SvxBoxItemLine::TOP );
+ aBorderOuter.SetLine( pBottom, SvxBoxItemLine::BOTTOM );
+
+ if(nModifier == KEY_SHIFT)
+ nValidFlags |= FrmValidFlags::AllMask;
+ aBorderInner.SetValid( SvxBoxInfoItemValidFlags::TOP, bool(nValidFlags&FrmValidFlags::Top ));
+ aBorderInner.SetValid( SvxBoxInfoItemValidFlags::BOTTOM, bool(nValidFlags&FrmValidFlags::Bottom ));
+ aBorderInner.SetValid( SvxBoxInfoItemValidFlags::LEFT, bool(nValidFlags&FrmValidFlags::Left));
+ aBorderInner.SetValid( SvxBoxInfoItemValidFlags::RIGHT, bool(nValidFlags&FrmValidFlags::Right ));
+ aBorderInner.SetValid( SvxBoxInfoItemValidFlags::HORI, bool(nValidFlags&FrmValidFlags::HInner ));
+ aBorderInner.SetValid( SvxBoxInfoItemValidFlags::VERT, bool(nValidFlags&FrmValidFlags::VInner));
+ aBorderInner.SetValid( SvxBoxInfoItemValidFlags::DISTANCE );
+ aBorderInner.SetValid( SvxBoxInfoItemValidFlags::DISABLE, false );
+
+ Any a1, a2;
+ aBorderOuter.QueryValue( a1 );
+ aBorderInner.QueryValue( a2 );
+ Sequence< PropertyValue > aArgs{ comphelper::makePropertyValue("OuterBorder", a1),
+ comphelper::makePropertyValue("InnerBorder", a2) };
+
+ mxControl->dispatchCommand( ".uno:SetBorderStyle", aArgs );
+ }
+
+ if (mxFrameSet)
+ {
+ /* #i33380# 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. */
+ mxFrameSet->SetNoSelection();
+ }
+
+ mxControl->EndPopupMode();
+}
+
+void SvxFrameWindow_Impl::SetDiagonalDownBorder(const SvxLineItem& dDownLineItem)
+{
+ // apply diagonal down border
+ Any a;
+ dDownLineItem.QueryValue(a);
+ Sequence<PropertyValue> aArgs{ comphelper::makePropertyValue("BorderTLBR", a) };
+
+ mxControl->dispatchCommand(".uno:BorderTLBR", aArgs);
+}
+
+void SvxFrameWindow_Impl::SetDiagonalUpBorder(const SvxLineItem& dUpLineItem)
+{
+ // apply diagonal up border
+ Any a;
+ dUpLineItem.QueryValue(a);
+ Sequence<PropertyValue> aArgs{ comphelper::makePropertyValue("BorderBLTR", a) };
+
+ mxControl->dispatchCommand(".uno:BorderBLTR", aArgs);
+}
+
+void SvxFrameWindow_Impl::statusChanged( const css::frame::FeatureStateEvent& rEvent )
+{
+ if ( rEvent.FeatureURL.Complete != ".uno:BorderReducedMode" )
+ return;
+
+ bool bValue;
+ if ( !(rEvent.State >>= bValue) )
+ return;
+
+ bParagraphMode = bValue;
+ //initial calls mustn't insert or remove elements
+ if(!mxFrameSet->GetItemCount())
+ return;
+
+ // set 12 border types for Writer, otherwise 15 for Calc.
+ bool bTableMode = ( mxFrameSet->GetItemCount() == static_cast<size_t>(m_bIsWriter ? 12 : 15) );
+ bool bResize = false;
+
+ if ( bTableMode && bParagraphMode )
+ {
+ for ( sal_uInt16 i = (m_bIsWriter ? 9 : 11); i < (m_bIsWriter ? 13 : 16); i++ )
+ mxFrameSet->RemoveItem(i);
+ bResize = true;
+ }
+ else if ( !bTableMode && !bParagraphMode )
+ {
+ for ( sal_uInt16 i = (m_bIsWriter ? 9 : 11); i < (m_bIsWriter ? 13 : 16); i++ )
+ mxFrameSet->InsertItem(i, Image(aImgVec[i-1].first), aImgVec[i-1].second);
+ bResize = true;
+ }
+
+ if ( bResize )
+ {
+ CalcSizeValueSet();
+ }
+}
+
+void SvxFrameWindow_Impl::CalcSizeValueSet()
+{
+ weld::DrawingArea* pDrawingArea = mxFrameSet->GetDrawingArea();
+ const OutputDevice& rDevice = pDrawingArea->get_ref_device();
+ Size aItemSize( 20 * rDevice.GetDPIScaleFactor(), 20 * rDevice.GetDPIScaleFactor() );
+ Size aSize = mxFrameSet->CalcWindowSizePixel( aItemSize );
+ pDrawingArea->set_size_request(aSize.Width(), aSize.Height());
+ mxFrameSet->SetOutputSizePixel(aSize);
+}
+
+void SvxFrameWindow_Impl::InitImageList()
+{
+ if (m_bIsWriter)
+ {
+ // Writer-specific aImgVec.
+ // Since Writer doesn't have diagonal borders,
+ // we have to use 12 border types here.
+ aImgVec = {
+ {BitmapEx(RID_SVXBMP_FRAME1), SvxResId(RID_SVXSTR_TABLE_PRESET_NONE)},
+ {BitmapEx(RID_SVXBMP_FRAME2), SvxResId(RID_SVXSTR_PARA_PRESET_ONLYLEFT)},
+ {BitmapEx(RID_SVXBMP_FRAME3), SvxResId(RID_SVXSTR_PARA_PRESET_ONLYRIGHT)},
+ {BitmapEx(RID_SVXBMP_FRAME4), SvxResId(RID_SVXSTR_PARA_PRESET_LEFTRIGHT)},
+
+ {BitmapEx(RID_SVXBMP_FRAME5), SvxResId(RID_SVXSTR_PARA_PRESET_ONLYTOP)},
+ {BitmapEx(RID_SVXBMP_FRAME6), SvxResId(RID_SVXSTR_PARA_PRESET_ONLYTBOTTOM)},
+ {BitmapEx(RID_SVXBMP_FRAME7), SvxResId(RID_SVXSTR_PARA_PRESET_TOPBOTTOM)},
+ {BitmapEx(RID_SVXBMP_FRAME8), SvxResId(RID_SVXSTR_TABLE_PRESET_ONLYOUTER)},
+
+ {BitmapEx(RID_SVXBMP_FRAME9), SvxResId(RID_SVXSTR_PARA_PRESET_TOPBOTTOMHORI)},
+ {BitmapEx(RID_SVXBMP_FRAME10), SvxResId(RID_SVXSTR_TABLE_PRESET_OUTERHORI)},
+ {BitmapEx(RID_SVXBMP_FRAME11), SvxResId(RID_SVXSTR_TABLE_PRESET_OUTERVERI)},
+ {BitmapEx(RID_SVXBMP_FRAME12), SvxResId(RID_SVXSTR_TABLE_PRESET_OUTERALL)}
+ };
+ }
+ else
+ {
+ // Calc has diagonal borders feature.
+ // Therefore use additional 3 diagonal border types,
+ // which make border types 15 in total.
+ aImgVec = {
+ {BitmapEx(RID_SVXBMP_FRAME1), SvxResId(RID_SVXSTR_TABLE_PRESET_NONE)},
+ {BitmapEx(RID_SVXBMP_FRAME2), SvxResId(RID_SVXSTR_PARA_PRESET_ONLYLEFT)},
+ {BitmapEx(RID_SVXBMP_FRAME3), SvxResId(RID_SVXSTR_PARA_PRESET_ONLYRIGHT)},
+ {BitmapEx(RID_SVXBMP_FRAME4), SvxResId(RID_SVXSTR_PARA_PRESET_LEFTRIGHT)},
+ {BitmapEx(RID_SVXBMP_FRAME14), SvxResId(RID_SVXSTR_PARA_PRESET_DIAGONALDOWN)}, // diagonal down border
+
+ {BitmapEx(RID_SVXBMP_FRAME5), SvxResId(RID_SVXSTR_PARA_PRESET_ONLYTOP)},
+ {BitmapEx(RID_SVXBMP_FRAME6), SvxResId(RID_SVXSTR_PARA_PRESET_ONLYTBOTTOM)},
+ {BitmapEx(RID_SVXBMP_FRAME7), SvxResId(RID_SVXSTR_PARA_PRESET_TOPBOTTOM)},
+ {BitmapEx(RID_SVXBMP_FRAME8), SvxResId(RID_SVXSTR_TABLE_PRESET_ONLYOUTER)},
+ {BitmapEx(RID_SVXBMP_FRAME13), SvxResId(RID_SVXSTR_PARA_PRESET_DIAGONALUP)}, // diagonal up border
+
+ {BitmapEx(RID_SVXBMP_FRAME9), SvxResId(RID_SVXSTR_PARA_PRESET_TOPBOTTOMHORI)},
+ {BitmapEx(RID_SVXBMP_FRAME10), SvxResId(RID_SVXSTR_TABLE_PRESET_OUTERHORI)},
+ {BitmapEx(RID_SVXBMP_FRAME11), SvxResId(RID_SVXSTR_TABLE_PRESET_OUTERVERI)},
+ {BitmapEx(RID_SVXBMP_FRAME12), SvxResId(RID_SVXSTR_TABLE_PRESET_OUTERALL)},
+ {BitmapEx(RID_SVXBMP_FRAME15), SvxResId(RID_SVXSTR_PARA_PRESET_CRISSCROSS)} // criss-cross border
+ };
+ }
+}
+
+static Color lcl_mediumColor( Color aMain, Color /*aDefault*/ )
+{
+ return SvxBorderLine::threeDMediumColor( aMain );
+}
+
+SvxLineWindow_Impl::SvxLineWindow_Impl(SvxFrameToolBoxControl* pControl, weld::Widget* pParent)
+ : WeldToolbarPopup(pControl->getFrameInterface(), pParent, "svx/ui/floatingframeborder.ui", "FloatingFrameBorder")
+ , m_xControl(pControl)
+ , m_xLineStyleLb(new LineListBox)
+ , m_xLineStyleLbWin(new weld::CustomWeld(*m_xBuilder, "valueset", *m_xLineStyleLb))
+ , m_bIsWriter(false)
+{
+ try
+ {
+ Reference< lang::XServiceInfo > xServices(m_xFrame->getController()->getModel(), UNO_QUERY_THROW);
+ m_bIsWriter = xServices->supportsService("com.sun.star.text.TextDocument");
+ }
+ catch(const uno::Exception& )
+ {
+ }
+
+ m_xLineStyleLb->SetStyle( WinBits(WB_FLATVALUESET | WB_ITEMBORDER | WB_3DLOOK | WB_NO_DIRECTSELECT | WB_TABSTOP) );
+
+ m_xLineStyleLb->SetSourceUnit( FieldUnit::TWIP );
+ m_xLineStyleLb->SetNone( comphelper::LibreOfficeKit::isActive() ? SvxResId(RID_SVXSTR_INVISIBLE)
+ :SvxResId(RID_SVXSTR_NONE) );
+
+ m_xLineStyleLb->InsertEntry( SvxBorderLine::getWidthImpl( SvxBorderLineStyle::SOLID ), SvxBorderLineStyle::SOLID );
+ m_xLineStyleLb->InsertEntry( SvxBorderLine::getWidthImpl( SvxBorderLineStyle::DOTTED ), SvxBorderLineStyle::DOTTED );
+ m_xLineStyleLb->InsertEntry( SvxBorderLine::getWidthImpl( SvxBorderLineStyle::DASHED ), SvxBorderLineStyle::DASHED );
+
+ // Double lines
+ m_xLineStyleLb->InsertEntry( SvxBorderLine::getWidthImpl( SvxBorderLineStyle::DOUBLE ), SvxBorderLineStyle::DOUBLE );
+ m_xLineStyleLb->InsertEntry( SvxBorderLine::getWidthImpl( SvxBorderLineStyle::THINTHICK_SMALLGAP ), SvxBorderLineStyle::THINTHICK_SMALLGAP, 20 );
+ m_xLineStyleLb->InsertEntry( SvxBorderLine::getWidthImpl( SvxBorderLineStyle::THINTHICK_MEDIUMGAP ), SvxBorderLineStyle::THINTHICK_MEDIUMGAP );
+ m_xLineStyleLb->InsertEntry( SvxBorderLine::getWidthImpl( SvxBorderLineStyle::THINTHICK_LARGEGAP ), SvxBorderLineStyle::THINTHICK_LARGEGAP );
+ m_xLineStyleLb->InsertEntry( SvxBorderLine::getWidthImpl( SvxBorderLineStyle::THICKTHIN_SMALLGAP ), SvxBorderLineStyle::THICKTHIN_SMALLGAP, 20 );
+ m_xLineStyleLb->InsertEntry( SvxBorderLine::getWidthImpl( SvxBorderLineStyle::THICKTHIN_MEDIUMGAP ), SvxBorderLineStyle::THICKTHIN_MEDIUMGAP );
+ m_xLineStyleLb->InsertEntry( SvxBorderLine::getWidthImpl( SvxBorderLineStyle::THICKTHIN_LARGEGAP ), SvxBorderLineStyle::THICKTHIN_LARGEGAP );
+
+ // Engraved / Embossed
+ m_xLineStyleLb->InsertEntry( SvxBorderLine::getWidthImpl( SvxBorderLineStyle::EMBOSSED ), SvxBorderLineStyle::EMBOSSED, 15,
+ &SvxBorderLine::threeDLightColor, &SvxBorderLine::threeDDarkColor,
+ &lcl_mediumColor );
+ m_xLineStyleLb->InsertEntry( SvxBorderLine::getWidthImpl( SvxBorderLineStyle::ENGRAVED ), SvxBorderLineStyle::ENGRAVED, 15,
+ &SvxBorderLine::threeDDarkColor, &SvxBorderLine::threeDLightColor,
+ &lcl_mediumColor );
+
+ // Inset / Outset
+ m_xLineStyleLb->InsertEntry( SvxBorderLine::getWidthImpl( SvxBorderLineStyle::OUTSET ), SvxBorderLineStyle::OUTSET, 10,
+ &SvxBorderLine::lightColor, &SvxBorderLine::darkColor );
+ m_xLineStyleLb->InsertEntry( SvxBorderLine::getWidthImpl( SvxBorderLineStyle::INSET ), SvxBorderLineStyle::INSET, 10,
+ &SvxBorderLine::darkColor, &SvxBorderLine::lightColor );
+ Size aSize = m_xLineStyleLb->SetWidth( 20 ); // 1pt by default
+
+ m_xLineStyleLb->SetSelectHdl( LINK( this, SvxLineWindow_Impl, SelectHdl ) );
+
+ m_xContainer->set_help_id(HID_POPUP_LINE);
+
+ aSize.AdjustWidth(6);
+ aSize.AdjustHeight(6);
+ aSize = m_xLineStyleLb->CalcWindowSizePixel(aSize);
+ m_xLineStyleLb->GetDrawingArea()->set_size_request(aSize.Width(), aSize.Height());
+ m_xLineStyleLb->SetOutputSizePixel(aSize);
+}
+
+IMPL_LINK_NOARG(SvxLineWindow_Impl, SelectHdl, ValueSet*, void)
+{
+ SvxLineItem aLineItem( SID_FRAME_LINESTYLE );
+ SvxBorderLineStyle nStyle = m_xLineStyleLb->GetSelectEntryStyle();
+
+ if ( m_xLineStyleLb->GetSelectItemPos( ) > 0 )
+ {
+ SvxBorderLine aTmp;
+ aTmp.SetBorderLineStyle( nStyle );
+ aTmp.SetWidth( SvxBorderLineWidth::Thin ); // TODO Make it depend on a width field
+ aLineItem.SetLine( &aTmp );
+ }
+ else
+ aLineItem.SetLine( nullptr );
+
+ Any a;
+ aLineItem.QueryValue( a, m_bIsWriter ? CONVERT_TWIPS : 0 );
+ Sequence< PropertyValue > aArgs{ comphelper::makePropertyValue("LineStyle", a) };
+
+ m_xControl->dispatchCommand( ".uno:LineStyle", aArgs );
+
+ m_xControl->EndPopupMode();
+}
+
+SfxStyleControllerItem_Impl::SfxStyleControllerItem_Impl(
+ const Reference< XDispatchProvider >& rDispatchProvider,
+ sal_uInt16 nSlotId, // Family-ID
+ const OUString& rCommand, // .uno: command bound to this item
+ SvxStyleToolBoxControl& rTbxCtl ) // controller instance, which the item is assigned to.
+ : SfxStatusListener( rDispatchProvider, nSlotId, rCommand ),
+ rControl( rTbxCtl )
+{
+}
+
+void SfxStyleControllerItem_Impl::StateChangedAtStatusListener(
+ SfxItemState eState, const SfxPoolItem* pState )
+{
+ switch ( GetId() )
+ {
+ case SID_STYLE_FAMILY1:
+ case SID_STYLE_FAMILY2:
+ case SID_STYLE_FAMILY3:
+ case SID_STYLE_FAMILY4:
+ case SID_STYLE_FAMILY5:
+ {
+ const sal_uInt16 nIdx = GetId() - SID_STYLE_FAMILY_START;
+
+ if ( SfxItemState::DEFAULT == eState )
+ {
+ const SfxTemplateItem* pStateItem =
+ dynamic_cast<const SfxTemplateItem*>( pState );
+ DBG_ASSERT( pStateItem != nullptr, "SfxTemplateItem expected" );
+ rControl.SetFamilyState( nIdx, pStateItem );
+ }
+ else
+ rControl.SetFamilyState( nIdx, nullptr );
+ break;
+ }
+ }
+}
+
+struct SvxStyleToolBoxControl::Impl
+{
+ OUString aClearForm;
+ OUString aMore;
+ ::std::vector< std::pair< OUString, OUString > > aDefaultStyles;
+ bool bSpecModeWriter;
+ bool bSpecModeCalc;
+
+ VclPtr<SvxStyleBox_Impl> m_xVclBox;
+ std::unique_ptr<SvxStyleBox_Base> m_xWeldBox;
+ SvxStyleBox_Base* m_pBox;
+
+ Impl()
+ :aClearForm ( SvxResId( RID_SVXSTR_CLEARFORM ) )
+ ,aMore ( SvxResId( RID_SVXSTR_MORE_STYLES ) )
+ ,bSpecModeWriter ( false )
+ ,bSpecModeCalc ( false )
+ ,m_pBox ( nullptr )
+ {
+
+
+ }
+ void InitializeStyles(const Reference < frame::XModel >& xModel)
+ {
+ aDefaultStyles.clear();
+
+ //now convert the default style names to the localized names
+ try
+ {
+ Reference< style::XStyleFamiliesSupplier > xStylesSupplier( xModel, UNO_QUERY_THROW );
+ Reference< lang::XServiceInfo > xServices( xModel, UNO_QUERY_THROW );
+ bSpecModeWriter = xServices->supportsService("com.sun.star.text.TextDocument");
+ if(bSpecModeWriter)
+ {
+ Reference<container::XNameAccess> xParaStyles;
+ xStylesSupplier->getStyleFamilies()->getByName("ParagraphStyles") >>=
+ xParaStyles;
+ static const std::vector<OUString> aWriterStyles =
+ {
+ "Standard",
+ "Text body",
+ "Title",
+ "Subtitle",
+ "Heading 1",
+ "Heading 2",
+ "Heading 3",
+ "Heading 4",
+ "Quotations",
+ "Preformatted Text"
+ };
+ for( const OUString& aStyle: aWriterStyles )
+ {
+ try
+ {
+ Reference< beans::XPropertySet > xStyle;
+ xParaStyles->getByName( aStyle ) >>= xStyle;
+ OUString sName;
+ xStyle->getPropertyValue("DisplayName") >>= sName;
+ if( !sName.isEmpty() )
+ aDefaultStyles.push_back(
+ std::pair<OUString, OUString>(aStyle, sName) );
+ }
+ catch( const uno::Exception& )
+ {}
+ }
+
+ }
+ else if( (
+ bSpecModeCalc = xServices->supportsService(
+ "com.sun.star.sheet.SpreadsheetDocument")))
+ {
+ static const char* aCalcStyles[] =
+ {
+ "Default",
+ "Accent 1",
+ "Accent 2",
+ "Accent 3",
+ "Heading 1",
+ "Heading 2",
+ "Result"
+ };
+ Reference<container::XNameAccess> xCellStyles;
+ xStylesSupplier->getStyleFamilies()->getByName("CellStyles") >>= xCellStyles;
+ for(const char* pCalcStyle : aCalcStyles)
+ {
+ try
+ {
+ const OUString sStyleName( OUString::createFromAscii( pCalcStyle ) );
+ if( xCellStyles->hasByName( sStyleName ) )
+ {
+ Reference< beans::XPropertySet > xStyle( xCellStyles->getByName( sStyleName), UNO_QUERY_THROW );
+ OUString sName;
+ xStyle->getPropertyValue("DisplayName") >>= sName;
+ if( !sName.isEmpty() )
+ aDefaultStyles.push_back(
+ std::pair<OUString, OUString>(sStyleName, sName) );
+ }
+ }
+ catch( const uno::Exception& )
+ {}
+ }
+ }
+ }
+ catch(const uno::Exception& )
+ {
+ OSL_FAIL("error while initializing style names");
+ }
+ }
+};
+
+// mapping table from bound items. BE CAREFUL this table must be in the
+// same order as the uno commands bound to the slots SID_STYLE_FAMILY1..n
+// MAX_FAMILIES must also be correctly set!
+static const char* StyleSlotToStyleCommand[MAX_FAMILIES] =
+{
+ ".uno:CharStyle",
+ ".uno:ParaStyle",
+ ".uno:FrameStyle",
+ ".uno:PageStyle",
+ ".uno:TemplateFamily5"
+};
+
+SvxStyleToolBoxControl::SvxStyleToolBoxControl()
+ : pImpl(new Impl)
+ , pStyleSheetPool(nullptr)
+ , nActFamily(0xffff)
+{
+ for (sal_uInt16 i = 0; i < MAX_FAMILIES; ++i)
+ {
+ m_xBoundItems[i].clear();
+ pFamilyState[i] = nullptr;
+ }
+}
+
+SvxStyleToolBoxControl::~SvxStyleToolBoxControl()
+{
+}
+
+void SAL_CALL SvxStyleToolBoxControl::initialize(const Sequence<Any>& rArguments)
+{
+ svt::ToolboxController::initialize(rArguments);
+
+ // After initialize we should have a valid frame member where we can retrieve our
+ // dispatch provider.
+ if ( !m_xFrame.is() )
+ return;
+
+ pImpl->InitializeStyles(m_xFrame->getController()->getModel());
+ Reference< XDispatchProvider > xDispatchProvider( m_xFrame->getController(), UNO_QUERY );
+ for ( sal_uInt16 i=0; i<MAX_FAMILIES; i++ )
+ {
+ m_xBoundItems[i] = new SfxStyleControllerItem_Impl( xDispatchProvider,
+ SID_STYLE_FAMILY_START + i,
+ OUString::createFromAscii( StyleSlotToStyleCommand[i] ),
+ *this );
+ pFamilyState[i] = nullptr;
+ }
+}
+
+// XComponent
+void SAL_CALL SvxStyleToolBoxControl::dispose()
+{
+ svt::ToolboxController::dispose();
+
+ SolarMutexGuard aSolarMutexGuard;
+ pImpl->m_xVclBox.disposeAndClear();
+ pImpl->m_xWeldBox.reset();
+ pImpl->m_pBox = nullptr;
+
+ for (rtl::Reference<SfxStyleControllerItem_Impl>& pBoundItem : m_xBoundItems)
+ {
+ if (!pBoundItem)
+ continue;
+ pBoundItem->UnBind();
+ }
+ unbindListener();
+
+ for( sal_uInt16 i=0; i<MAX_FAMILIES; i++ )
+ {
+ if ( m_xBoundItems[i].is() )
+ {
+ try
+ {
+ m_xBoundItems[i]->dispose();
+ }
+ catch ( Exception& )
+ {
+ }
+
+ m_xBoundItems[i].clear();
+ }
+ pFamilyState[i].reset();
+ }
+ pStyleSheetPool = nullptr;
+ pImpl.reset();
+}
+
+OUString SvxStyleToolBoxControl::getImplementationName()
+{
+ return "com.sun.star.comp.svx.StyleToolBoxControl";
+}
+
+sal_Bool SvxStyleToolBoxControl::supportsService( const OUString& rServiceName )
+{
+ return cppu::supportsService( this, rServiceName );
+}
+
+css::uno::Sequence< OUString > SvxStyleToolBoxControl::getSupportedServiceNames()
+{
+ return { "com.sun.star.frame.ToolbarController" };
+}
+
+extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface *
+com_sun_star_comp_svx_StyleToolBoxControl_get_implementation(
+ css::uno::XComponentContext*,
+ css::uno::Sequence<css::uno::Any> const & )
+{
+ return cppu::acquire( new SvxStyleToolBoxControl() );
+}
+
+void SAL_CALL SvxStyleToolBoxControl::update()
+{
+ for (rtl::Reference<SfxStyleControllerItem_Impl>& pBoundItem : m_xBoundItems)
+ pBoundItem->ReBind();
+ bindListener();
+}
+
+SfxStyleFamily SvxStyleToolBoxControl::GetActFamily() const
+{
+ switch ( nActFamily-1 + SID_STYLE_FAMILY_START )
+ {
+ case SID_STYLE_FAMILY1: return SfxStyleFamily::Char;
+ case SID_STYLE_FAMILY2: return SfxStyleFamily::Para;
+ case SID_STYLE_FAMILY3: return SfxStyleFamily::Frame;
+ case SID_STYLE_FAMILY4: return SfxStyleFamily::Page;
+ case SID_STYLE_FAMILY5: return SfxStyleFamily::Pseudo;
+ default:
+ OSL_FAIL( "unknown style family" );
+ break;
+ }
+ return SfxStyleFamily::Para;
+}
+
+void SvxStyleToolBoxControl::FillStyleBox()
+{
+ SvxStyleBox_Base* pBox = pImpl->m_pBox;
+
+ DBG_ASSERT( pStyleSheetPool, "StyleSheetPool not found!" );
+ DBG_ASSERT( pBox, "Control not found!" );
+
+ if ( !(pStyleSheetPool && pBox && nActFamily!=0xffff) )
+ return;
+
+ const SfxStyleFamily eFamily = GetActFamily();
+ SfxStyleSheetBase* pStyle = nullptr;
+ bool bDoFill = false;
+
+ auto xIter = pStyleSheetPool->CreateIterator(eFamily, SfxStyleSearchBits::Used);
+ sal_uInt16 nCount = xIter->Count();
+
+ // Check whether fill is necessary
+ pStyle = xIter->First();
+ //!!! TODO: This condition isn't right any longer, because we always show some default entries
+ //!!! so the list doesn't show the count
+ if ( nCount != pBox->get_count() )
+ {
+ bDoFill = true;
+ }
+ else
+ {
+ sal_uInt16 i= 0;
+ while ( pStyle && !bDoFill )
+ {
+ bDoFill = ( pBox->get_text(i) != pStyle->GetName() );
+ pStyle = xIter->Next();
+ i++;
+ }
+ }
+
+ if ( !bDoFill )
+ return;
+
+ OUString aStrSel(pBox->get_active_text());
+ pBox->freeze();
+ pBox->clear();
+
+ std::vector<OUString> aStyles;
+
+ {
+ pStyle = xIter->Next();
+
+ if( pImpl->bSpecModeWriter || pImpl->bSpecModeCalc )
+ {
+ while ( pStyle )
+ {
+ // sort out default styles
+ bool bInsert = true;
+ OUString aName( pStyle->GetName() );
+ for( auto const & _i: pImpl->aDefaultStyles )
+ {
+ if( _i.first == aName || _i.second == aName )
+ {
+ bInsert = false;
+ break;
+ }
+ }
+
+ if( bInsert )
+ aStyles.push_back(aName);
+ pStyle = xIter->Next();
+ }
+ }
+ else
+ {
+ while ( pStyle )
+ {
+ aStyles.push_back(pStyle->GetName());
+ pStyle = xIter->Next();
+ }
+ }
+ }
+
+ if (pImpl->bSpecModeWriter || pImpl->bSpecModeCalc)
+ {
+ pBox->append_text(pImpl->aClearForm);
+ pBox->insert_separator(1, "separator");
+
+ // insert default styles
+ for (const auto &rStyle : pImpl->aDefaultStyles)
+ pBox->append_text(rStyle.second);
+ }
+
+ std::sort(aStyles.begin(), aStyles.end());
+
+ for (const auto& rStyle : aStyles)
+ pBox->append_text(rStyle);
+
+ if ((pImpl->bSpecModeWriter || pImpl->bSpecModeCalc) && !comphelper::LibreOfficeKit::isActive())
+ pBox->append_text(pImpl->aMore);
+
+ pBox->thaw();
+ pBox->set_active_or_entry_text(aStrSel);
+ pBox->SetFamily( eFamily );
+}
+
+void SvxStyleToolBoxControl::SelectStyle( const OUString& rStyleName )
+{
+ SvxStyleBox_Base* pBox = pImpl->m_pBox;
+ DBG_ASSERT( pBox, "Control not found!" );
+
+ if ( !pBox )
+ return;
+
+ OUString aStrSel(pBox->get_active_text());
+
+ if ( !rStyleName.isEmpty() )
+ {
+ OUString aNewStyle = rStyleName;
+
+ auto aFound = std::find_if(pImpl->aDefaultStyles.begin(), pImpl->aDefaultStyles.end(),
+ [rStyleName] (auto it) { return it.first == rStyleName || it.second == rStyleName; }
+ );
+
+ if (aFound != pImpl->aDefaultStyles.end())
+ aNewStyle = aFound->second;
+
+ if ( aNewStyle != aStrSel )
+ pBox->set_active_or_entry_text( aNewStyle );
+ }
+ else
+ pBox->set_active(-1);
+ pBox->save_value();
+}
+
+void SvxStyleToolBoxControl::Update()
+{
+ SfxStyleSheetBasePool* pPool = nullptr;
+ SfxObjectShell* pDocShell = SfxObjectShell::Current();
+
+ if ( pDocShell )
+ pPool = pDocShell->GetStyleSheetPool();
+
+ sal_uInt16 i;
+ for ( i=0; i<MAX_FAMILIES; i++ )
+ if( pFamilyState[i] )
+ break;
+
+ if ( i==MAX_FAMILIES || !pPool )
+ {
+ pStyleSheetPool = pPool;
+ return;
+ }
+
+
+ const SfxTemplateItem* pItem = nullptr;
+
+ if ( nActFamily == 0xffff || nullptr == (pItem = pFamilyState[nActFamily-1].get()) )
+ // Current range not within allowed ranges or default
+ {
+ pStyleSheetPool = pPool;
+ nActFamily = 2;
+
+ pItem = pFamilyState[nActFamily-1].get();
+ if ( !pItem )
+ {
+ nActFamily++;
+ pItem = pFamilyState[nActFamily-1].get();
+ }
+ }
+ else if ( pPool != pStyleSheetPool )
+ pStyleSheetPool = pPool;
+
+ FillStyleBox(); // Decides by itself whether Fill is needed
+
+ if ( pItem )
+ SelectStyle( pItem->GetStyleName() );
+}
+
+void SvxStyleToolBoxControl::SetFamilyState( sal_uInt16 nIdx,
+ const SfxTemplateItem* pItem )
+{
+ pFamilyState[nIdx].reset( pItem == nullptr ? nullptr : new SfxTemplateItem( *pItem ) );
+ Update();
+}
+
+void SvxStyleToolBoxControl::statusChanged( const css::frame::FeatureStateEvent& rEvent )
+{
+ SolarMutexGuard aGuard;
+
+ if (m_pToolbar)
+ m_pToolbar->set_item_sensitive(m_aCommandURL.toUtf8(), rEvent.IsEnabled);
+ else
+ {
+ ToolBox* pToolBox = nullptr;
+ ToolBoxItemId nId;
+ if (!getToolboxId( nId, &pToolBox ) )
+ return;
+ pToolBox->EnableItem( nId, rEvent.IsEnabled );
+ }
+
+ if (rEvent.IsEnabled)
+ Update();
+}
+
+css::uno::Reference<css::awt::XWindow> SvxStyleToolBoxControl::createItemWindow(const css::uno::Reference< css::awt::XWindow>& rParent)
+{
+ uno::Reference< awt::XWindow > xItemWindow;
+
+ if (m_pBuilder)
+ {
+ SolarMutexGuard aSolarMutexGuard;
+
+ std::unique_ptr<weld::ComboBox> xWidget(m_pBuilder->weld_combo_box("applystyle"));
+
+ xItemWindow = css::uno::Reference<css::awt::XWindow>(new weld::TransportAsXWindow(xWidget.get()));
+
+ pImpl->m_xWeldBox.reset(new SvxStyleBox_Base(std::move(xWidget),
+ ".uno:StyleApply",
+ SfxStyleFamily::Para,
+ Reference< XDispatchProvider >( m_xFrame->getController(), UNO_QUERY ),
+ m_xFrame,
+ pImpl->aClearForm,
+ pImpl->aMore,
+ pImpl->bSpecModeWriter || pImpl->bSpecModeCalc, *this));
+ pImpl->m_pBox = pImpl->m_xWeldBox.get();
+ }
+ else
+ {
+ VclPtr<vcl::Window> pParent = VCLUnoHelper::GetWindow(rParent);
+ if ( pParent )
+ {
+ SolarMutexGuard aSolarMutexGuard;
+
+ pImpl->m_xVclBox = VclPtr<SvxStyleBox_Impl>::Create(pParent,
+ ".uno:StyleApply",
+ SfxStyleFamily::Para,
+ Reference< XDispatchProvider >( m_xFrame->getController(), UNO_QUERY ),
+ m_xFrame,
+ pImpl->aClearForm,
+ pImpl->aMore,
+ pImpl->bSpecModeWriter || pImpl->bSpecModeCalc, *this);
+ pImpl->m_pBox = pImpl->m_xVclBox.get();
+ xItemWindow = VCLUnoHelper::GetInterface(pImpl->m_xVclBox);
+ }
+ }
+
+ if (pImpl->m_pBox && !pImpl->aDefaultStyles.empty())
+ pImpl->m_pBox->SetDefaultStyle(pImpl->aDefaultStyles[0].second);
+
+ return xItemWindow;
+}
+
+SvxFontNameToolBoxControl::SvxFontNameToolBoxControl()
+ : m_pBox(nullptr)
+{
+}
+
+void SvxFontNameBox_Base::statusChanged_Impl( const css::frame::FeatureStateEvent& rEvent )
+{
+ if ( !rEvent.IsEnabled )
+ {
+ set_sensitive(false);
+ Update( nullptr );
+ }
+ else
+ {
+ set_sensitive(true);
+
+ css::awt::FontDescriptor aFontDesc;
+ if ( rEvent.State >>= aFontDesc )
+ Update(&aFontDesc);
+ else {
+ // no active element; delete value in the display
+ m_xWidget->set_active(-1);
+ set_active_or_entry_text("");
+ }
+ m_xWidget->save_value();
+ }
+}
+
+void SvxFontNameToolBoxControl::statusChanged( const css::frame::FeatureStateEvent& rEvent )
+{
+ SolarMutexGuard aGuard;
+ m_pBox->statusChanged_Impl(rEvent);
+
+ if (m_pToolbar)
+ m_pToolbar->set_item_sensitive(m_aCommandURL.toUtf8(), rEvent.IsEnabled);
+ else
+ {
+ ToolBox* pToolBox = nullptr;
+ ToolBoxItemId nId;
+ if (!getToolboxId( nId, &pToolBox ) )
+ return;
+ pToolBox->EnableItem( nId, rEvent.IsEnabled );
+ }
+}
+
+css::uno::Reference<css::awt::XWindow> SvxFontNameToolBoxControl::createItemWindow(const css::uno::Reference<css::awt::XWindow>& rParent)
+{
+ uno::Reference< awt::XWindow > xItemWindow;
+
+ if (m_pBuilder)
+ {
+ SolarMutexGuard aSolarMutexGuard;
+
+ std::unique_ptr<weld::ComboBox> xWidget(m_pBuilder->weld_combo_box("fontnamecombobox"));
+
+ xItemWindow = css::uno::Reference<css::awt::XWindow>(new weld::TransportAsXWindow(xWidget.get()));
+
+ m_xWeldBox.reset(new SvxFontNameBox_Base(std::move(xWidget),
+ Reference<XDispatchProvider>(m_xFrame->getController(), UNO_QUERY),
+ m_xFrame, *this));
+ m_pBox = m_xWeldBox.get();
+ }
+ else
+ {
+ VclPtr<vcl::Window> pParent = VCLUnoHelper::GetWindow(rParent);
+ if ( pParent )
+ {
+ SolarMutexGuard aSolarMutexGuard;
+ m_xVclBox = VclPtr<SvxFontNameBox_Impl>::Create(pParent,
+ Reference<XDispatchProvider>(m_xFrame->getController(), UNO_QUERY),
+ m_xFrame, *this);
+ m_pBox = m_xVclBox.get();
+ xItemWindow = VCLUnoHelper::GetInterface(m_xVclBox);
+ }
+ }
+
+ return xItemWindow;
+}
+
+void SvxFontNameToolBoxControl::dispose()
+{
+ ToolboxController::dispose();
+
+ SolarMutexGuard aSolarMutexGuard;
+ m_xVclBox.disposeAndClear();
+ m_xWeldBox.reset();
+ m_pBox = nullptr;
+}
+
+OUString SvxFontNameToolBoxControl::getImplementationName()
+{
+ return "com.sun.star.comp.svx.FontNameToolBoxControl";
+}
+
+sal_Bool SvxFontNameToolBoxControl::supportsService( const OUString& rServiceName )
+{
+ return cppu::supportsService( this, rServiceName );
+}
+
+css::uno::Sequence< OUString > SvxFontNameToolBoxControl::getSupportedServiceNames()
+{
+ return { "com.sun.star.frame.ToolbarController" };
+}
+
+extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface *
+com_sun_star_comp_svx_FontNameToolBoxControl_get_implementation(
+ css::uno::XComponentContext*,
+ css::uno::Sequence<css::uno::Any> const & )
+{
+ return cppu::acquire( new SvxFontNameToolBoxControl() );
+}
+
+SvxColorToolBoxControl::SvxColorToolBoxControl( const css::uno::Reference<css::uno::XComponentContext>& rContext ) :
+ ImplInheritanceHelper( rContext, nullptr, OUString() ),
+ m_bSplitButton(true),
+ m_nSlotId(0),
+ m_aColorSelectFunction(PaletteManager::DispatchColorCommand)
+{
+}
+
+namespace {
+
+sal_uInt16 MapCommandToSlotId(const OUString& rCommand)
+{
+ if (rCommand == ".uno:Color")
+ return SID_ATTR_CHAR_COLOR;
+ else if (rCommand == ".uno:FontColor")
+ return SID_ATTR_CHAR_COLOR2;
+ else if (rCommand == ".uno:BackColor")
+ return SID_ATTR_CHAR_COLOR_BACKGROUND;
+ else if (rCommand == ".uno:CharBackColor")
+ return SID_ATTR_CHAR_BACK_COLOR;
+ else if (rCommand == ".uno:BackgroundColor")
+ return SID_BACKGROUND_COLOR;
+ else if (rCommand == ".uno:TableCellBackgroundColor")
+ return SID_TABLE_CELL_BACKGROUND_COLOR;
+ else if (rCommand == ".uno:Extrusion3DColor")
+ return SID_EXTRUSION_3D_COLOR;
+ else if (rCommand == ".uno:XLineColor")
+ return SID_ATTR_LINE_COLOR;
+ else if (rCommand == ".uno:FillColor")
+ return SID_ATTR_FILL_COLOR;
+ else if (rCommand == ".uno:FrameLineColor")
+ return SID_FRAME_LINECOLOR;
+
+ SAL_WARN("svx.tbxcrtls", "Unknown color command: " << rCommand);
+ return 0;
+}
+
+}
+
+void SvxColorToolBoxControl::initialize( const css::uno::Sequence<css::uno::Any>& rArguments )
+{
+ PopupWindowController::initialize( rArguments );
+
+ m_nSlotId = MapCommandToSlotId( m_aCommandURL );
+
+ if ( m_nSlotId == SID_ATTR_LINE_COLOR || m_nSlotId == SID_ATTR_FILL_COLOR ||
+ m_nSlotId == SID_FRAME_LINECOLOR || m_nSlotId == SID_BACKGROUND_COLOR )
+ {
+ // Sidebar uses wide buttons for those.
+ m_bSplitButton = !m_bSidebar;
+ }
+
+ auto aProperties = vcl::CommandInfoProvider::GetCommandProperties(getCommandURL(), getModuleName());
+ OUString aCommandLabel = vcl::CommandInfoProvider::GetLabelForCommand(aProperties);
+
+ OString aId(m_aCommandURL.toUtf8());
+
+ if (m_pToolbar)
+ {
+ mxPopoverContainer.reset(new ToolbarPopupContainer(m_pToolbar));
+ m_pToolbar->set_item_popover(aId, mxPopoverContainer->getTopLevel());
+ m_xBtnUpdater.reset(new svx::ToolboxButtonColorUpdater(m_nSlotId, aId, m_pToolbar, !m_bSplitButton, aCommandLabel, m_xFrame));
+ return;
+ }
+
+ ToolBox* pToolBox = nullptr;
+ ToolBoxItemId nId;
+ if (getToolboxId(nId, &pToolBox))
+ {
+ m_xBtnUpdater.reset( new svx::VclToolboxButtonColorUpdater( m_nSlotId, nId, pToolBox, !m_bSplitButton, aCommandLabel, m_aCommandURL, m_xFrame ) );
+ pToolBox->SetItemBits( nId, pToolBox->GetItemBits( nId ) | ( m_bSplitButton ? ToolBoxItemBits::DROPDOWN : ToolBoxItemBits::DROPDOWNONLY ) );
+ }
+}
+
+void SvxColorToolBoxControl::update()
+{
+ PopupWindowController::update();
+
+ switch( m_nSlotId )
+ {
+ case SID_ATTR_CHAR_COLOR2:
+ addStatusListener( ".uno:CharColorExt");
+ break;
+
+ case SID_ATTR_CHAR_COLOR_BACKGROUND:
+ addStatusListener( ".uno:CharBackgroundExt");
+ break;
+
+ case SID_FRAME_LINECOLOR:
+ addStatusListener( ".uno:BorderTLBR");
+ addStatusListener( ".uno:BorderBLTR");
+ break;
+ }
+}
+
+void SvxColorToolBoxControl::EnsurePaletteManager()
+{
+ if (!m_xPaletteManager)
+ {
+ m_xPaletteManager = std::make_shared<PaletteManager>();
+ m_xPaletteManager->SetBtnUpdater(m_xBtnUpdater.get());
+ }
+}
+
+SvxColorToolBoxControl::~SvxColorToolBoxControl()
+{
+ if (m_xPaletteManager)
+ m_xPaletteManager->SetBtnUpdater(nullptr);
+}
+
+void SvxColorToolBoxControl::setColorSelectFunction(const ColorSelectFunction& aColorSelectFunction)
+{
+ m_aColorSelectFunction = aColorSelectFunction;
+ if (m_xPaletteManager)
+ m_xPaletteManager->SetColorSelectFunction(aColorSelectFunction);
+}
+
+weld::Window* SvxColorToolBoxControl::GetParentFrame() const
+{
+ const css::uno::Reference<css::awt::XWindow> xParent = m_xFrame->getContainerWindow();
+ return Application::GetFrameWeld(xParent);
+}
+
+std::unique_ptr<WeldToolbarPopup> SvxColorToolBoxControl::weldPopupWindow()
+{
+ EnsurePaletteManager();
+
+ const OString aId(m_aCommandURL.toUtf8());
+
+ auto xPopover = std::make_unique<ColorWindow>(
+ m_aCommandURL,
+ m_xPaletteManager,
+ m_aColorStatus,
+ m_nSlotId,
+ m_xFrame,
+ MenuOrToolMenuButton(m_pToolbar, aId),
+ [this] { return GetParentFrame(); },
+ m_aColorSelectFunction);
+
+ if ( m_bSplitButton )
+ xPopover->SetSelectedHdl( LINK( this, SvxColorToolBoxControl, SelectedHdl ) );
+
+ return xPopover;
+}
+
+VclPtr<vcl::Window> SvxColorToolBoxControl::createVclPopupWindow( vcl::Window* pParent )
+{
+ ToolBox* pToolBox = nullptr;
+ ToolBoxItemId nId;
+ if (!getToolboxId(nId, &pToolBox))
+ return nullptr;
+
+ EnsurePaletteManager();
+
+ auto xPopover = std::make_unique<ColorWindow>(
+ m_aCommandURL,
+ m_xPaletteManager,
+ m_aColorStatus,
+ m_nSlotId,
+ m_xFrame,
+ MenuOrToolMenuButton(this, pToolBox, nId),
+ [this] { return GetParentFrame(); },
+ m_aColorSelectFunction);
+
+ if ( m_bSplitButton )
+ xPopover->SetSelectedHdl( LINK( this, SvxColorToolBoxControl, SelectedHdl ) );
+
+ mxInterimPopover = VclPtr<InterimToolbarPopup>::Create(getFrameInterface(), pParent,
+ std::move(xPopover), true);
+
+ auto aProperties = vcl::CommandInfoProvider::GetCommandProperties(m_aCommandURL, m_sModuleName);
+ OUString aWindowTitle = vcl::CommandInfoProvider::GetLabelForCommand(aProperties);
+ mxInterimPopover->SetText(aWindowTitle);
+
+ mxInterimPopover->Show();
+
+ return mxInterimPopover;
+}
+
+IMPL_LINK(SvxColorToolBoxControl, SelectedHdl, const NamedColor&, rColor, void)
+{
+ m_xBtnUpdater->Update(rColor);
+}
+
+void SvxColorToolBoxControl::statusChanged( const css::frame::FeatureStateEvent& rEvent )
+{
+ ToolBox* pToolBox = nullptr;
+ ToolBoxItemId nId;
+ if (!getToolboxId(nId, &pToolBox) && !m_pToolbar)
+ return;
+
+ if ( rEvent.FeatureURL.Complete == m_aCommandURL )
+ {
+ if (m_pToolbar)
+ m_pToolbar->set_item_sensitive(m_aCommandURL.toUtf8(), rEvent.IsEnabled);
+ else
+ pToolBox->EnableItem( nId, rEvent.IsEnabled );
+ }
+
+ bool bValue;
+ if ( !m_bSplitButton )
+ {
+ m_aColorStatus.statusChanged( rEvent );
+ m_xBtnUpdater->Update( m_aColorStatus.GetColor() );
+ }
+ else if ( rEvent.State >>= bValue )
+ {
+ if (m_pToolbar)
+ m_pToolbar->set_item_active(m_aCommandURL.toUtf8(), bValue);
+ else if (pToolBox)
+ pToolBox->CheckItem( nId, bValue );
+ }
+}
+
+void SvxColorToolBoxControl::execute(sal_Int16 /*nSelectModifier*/)
+{
+ if ( !m_bSplitButton )
+ {
+ if (m_pToolbar)
+ {
+ // Toggle the popup also when toolbutton is activated
+ const OString aId(m_aCommandURL.toUtf8());
+ m_pToolbar->set_menu_item_active(aId, !m_pToolbar->get_menu_item_active(aId));
+ }
+ else
+ {
+ // Open the popup also when Enter key is pressed.
+ createPopupWindow();
+ }
+ return;
+ }
+
+ OUString aCommand = m_aCommandURL;
+ Color aColor = m_xBtnUpdater->GetCurrentColor();
+
+ switch( m_nSlotId )
+ {
+ case SID_ATTR_CHAR_COLOR2 :
+ aCommand = ".uno:CharColorExt";
+ break;
+
+ case SID_ATTR_CHAR_COLOR_BACKGROUND :
+ aCommand = ".uno:CharBackgroundExt";
+ break;
+ }
+
+ auto aArgs( comphelper::InitPropertySequence( {
+ { m_aCommandURL.copy(5), css::uno::Any(aColor) }
+ } ) );
+ dispatchCommand( aCommand, aArgs );
+
+ EnsurePaletteManager();
+ OUString sColorName = m_xBtnUpdater->GetCurrentColorName();
+ m_xPaletteManager->AddRecentColor(aColor, sColorName);
+}
+
+sal_Bool SvxColorToolBoxControl::opensSubToolbar()
+{
+ // We mark this controller as a sub-toolbar controller, so we get notified
+ // (through updateImage method) on button image changes, and could redraw
+ // the last used color on top of it.
+ return true;
+}
+
+void SvxColorToolBoxControl::updateImage()
+{
+ m_xBtnUpdater->Update(m_xBtnUpdater->GetCurrentColor(), true);
+}
+
+OUString SvxColorToolBoxControl::getSubToolbarName()
+{
+ return OUString();
+}
+
+void SvxColorToolBoxControl::functionSelected( const OUString& /*rCommand*/ )
+{
+}
+
+OUString SvxColorToolBoxControl::getImplementationName()
+{
+ return "com.sun.star.comp.svx.ColorToolBoxControl";
+}
+
+css::uno::Sequence<OUString> SvxColorToolBoxControl::getSupportedServiceNames()
+{
+ return { "com.sun.star.frame.ToolbarController" };
+}
+
+extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface *
+com_sun_star_comp_svx_ColorToolBoxControl_get_implementation(
+ css::uno::XComponentContext* rContext,
+ css::uno::Sequence<css::uno::Any> const & )
+{
+ return cppu::acquire( new SvxColorToolBoxControl( rContext ) );
+}
+
+SvxFrameToolBoxControl::SvxFrameToolBoxControl( const css::uno::Reference< css::uno::XComponentContext >& rContext )
+ : svt::PopupWindowController( rContext, nullptr, OUString() )
+{
+}
+
+void SAL_CALL SvxFrameToolBoxControl::execute(sal_Int16 /*KeyModifier*/)
+{
+ if (m_pToolbar)
+ {
+ // Toggle the popup also when toolbutton is activated
+ const OString aId(m_aCommandURL.toUtf8());
+ m_pToolbar->set_menu_item_active(aId, !m_pToolbar->get_menu_item_active(aId));
+ }
+ else
+ {
+ // Open the popup also when Enter key is pressed.
+ createPopupWindow();
+ }
+}
+
+void SvxFrameToolBoxControl::initialize( const css::uno::Sequence< css::uno::Any >& rArguments )
+{
+ svt::PopupWindowController::initialize( rArguments );
+
+ if (m_pToolbar)
+ {
+ mxPopoverContainer.reset(new ToolbarPopupContainer(m_pToolbar));
+ m_pToolbar->set_item_popover(m_aCommandURL.toUtf8(), mxPopoverContainer->getTopLevel());
+ }
+
+ ToolBox* pToolBox = nullptr;
+ ToolBoxItemId nId;
+ if (getToolboxId(nId, &pToolBox))
+ pToolBox->SetItemBits( nId, pToolBox->GetItemBits( nId ) | ToolBoxItemBits::DROPDOWNONLY );
+}
+
+std::unique_ptr<WeldToolbarPopup> SvxFrameToolBoxControl::weldPopupWindow()
+{
+ if ( m_aCommandURL == ".uno:LineStyle" )
+ return std::make_unique<SvxLineWindow_Impl>(this, m_pToolbar);
+ return std::make_unique<SvxFrameWindow_Impl>(this, m_pToolbar);
+}
+
+VclPtr<vcl::Window> SvxFrameToolBoxControl::createVclPopupWindow( vcl::Window* pParent )
+{
+ if ( m_aCommandURL == ".uno:LineStyle" )
+ {
+ mxInterimPopover = VclPtr<InterimToolbarPopup>::Create(getFrameInterface(), pParent,
+ std::make_unique<SvxLineWindow_Impl>(this, pParent->GetFrameWeld()), true);
+
+ mxInterimPopover->Show();
+
+ mxInterimPopover->SetText(SvxResId(RID_SVXSTR_FRAME_STYLE));
+
+ return mxInterimPopover;
+ }
+
+ mxInterimPopover = VclPtr<InterimToolbarPopup>::Create(getFrameInterface(), pParent,
+ std::make_unique<SvxFrameWindow_Impl>(this, pParent->GetFrameWeld()), true);
+
+ mxInterimPopover->Show();
+
+ mxInterimPopover->SetText(SvxResId(RID_SVXSTR_FRAME));
+
+ return mxInterimPopover;
+}
+
+OUString SvxFrameToolBoxControl::getImplementationName()
+{
+ return "com.sun.star.comp.svx.FrameToolBoxControl";
+}
+
+css::uno::Sequence< OUString > SvxFrameToolBoxControl::getSupportedServiceNames()
+{
+ return { "com.sun.star.frame.ToolbarController" };
+}
+
+extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface *
+com_sun_star_comp_svx_FrameToolBoxControl_get_implementation(
+ css::uno::XComponentContext* rContext,
+ css::uno::Sequence<css::uno::Any> const & )
+{
+ return cppu::acquire( new SvxFrameToolBoxControl( rContext ) );
+}
+
+SvxCurrencyToolBoxControl::SvxCurrencyToolBoxControl( const css::uno::Reference<css::uno::XComponentContext>& rContext ) :
+ PopupWindowController( rContext, nullptr, OUString() ),
+ m_eLanguage( Application::GetSettings().GetLanguageTag().getLanguageType() ),
+ m_nFormatKey( NUMBERFORMAT_ENTRY_NOT_FOUND )
+{
+}
+
+SvxCurrencyToolBoxControl::~SvxCurrencyToolBoxControl() {}
+
+namespace
+{
+ class SvxCurrencyList_Impl : public WeldToolbarPopup
+ {
+ private:
+ rtl::Reference<SvxCurrencyToolBoxControl> m_xControl;
+ std::unique_ptr<weld::Label> m_xLabel;
+ std::unique_ptr<weld::TreeView> m_xCurrencyLb;
+ std::unique_ptr<weld::Button> m_xOkBtn;
+ OUString& m_rSelectedFormat;
+ LanguageType& m_eSelectedLanguage;
+
+ std::vector<OUString> m_aFormatEntries;
+ LanguageType m_eFormatLanguage;
+ DECL_LINK(RowActivatedHdl, weld::TreeView&, bool);
+ DECL_LINK(OKHdl, weld::Button&, void);
+
+ virtual void GrabFocus() override;
+
+ public:
+ SvxCurrencyList_Impl(SvxCurrencyToolBoxControl* pControl, weld::Widget* pParent, OUString& rSelectedFormat, LanguageType& eSelectedLanguage)
+ : WeldToolbarPopup(pControl->getFrameInterface(), pParent, "svx/ui/currencywindow.ui", "CurrencyWindow")
+ , m_xControl(pControl)
+ , m_xLabel(m_xBuilder->weld_label("label"))
+ , m_xCurrencyLb(m_xBuilder->weld_tree_view("currency"))
+ , m_xOkBtn(m_xBuilder->weld_button("ok"))
+ , m_rSelectedFormat(rSelectedFormat)
+ , m_eSelectedLanguage(eSelectedLanguage)
+ {
+ std::vector< OUString > aList;
+ std::vector< sal_uInt16 > aCurrencyList;
+ const NfCurrencyTable& rCurrencyTable = SvNumberFormatter::GetTheCurrencyTable();
+ sal_uInt16 nLen = rCurrencyTable.size();
+
+ SvNumberFormatter aFormatter( m_xControl->getContext(), LANGUAGE_SYSTEM );
+ m_eFormatLanguage = aFormatter.GetLanguage();
+
+ SvxCurrencyToolBoxControl::GetCurrencySymbols( aList, true, aCurrencyList );
+
+ sal_uInt16 nPos = 0, nCount = 0;
+ sal_Int32 nSelectedPos = -1;
+ bool bIsSymbol;
+ NfWSStringsDtor aStringsDtor;
+
+ OUString sLongestString;
+
+ m_xCurrencyLb->freeze();
+ for( const auto& rItem : aList )
+ {
+ sal_uInt16& rCurrencyIndex = aCurrencyList[ nCount ];
+ if ( rCurrencyIndex < nLen )
+ {
+ m_xCurrencyLb->append_text(rItem);
+
+ if (rItem.getLength() > sLongestString.getLength())
+ sLongestString = rItem;
+
+ bIsSymbol = nPos >= nLen;
+
+ sal_uInt16 nDefaultFormat;
+ const NfCurrencyEntry& rCurrencyEntry = rCurrencyTable[ rCurrencyIndex ];
+ if (rCurrencyIndex == 0)
+ {
+ // Stored with system locale, but we want the resolved
+ // full LCID format string. For example
+ // "[$$-409]#,##0.00" instead of "[$$]#,##0.00".
+ NfCurrencyEntry aCurrencyEntry( rCurrencyEntry);
+ aCurrencyEntry.SetLanguage( LanguageTag( aCurrencyEntry.GetLanguage()).getLanguageType());
+ nDefaultFormat = aFormatter.GetCurrencyFormatStrings( aStringsDtor, aCurrencyEntry, bIsSymbol);
+ }
+ else
+ {
+ nDefaultFormat = aFormatter.GetCurrencyFormatStrings( aStringsDtor, rCurrencyEntry, bIsSymbol);
+ }
+ const OUString& rFormatStr = aStringsDtor[ nDefaultFormat ];
+ m_aFormatEntries.push_back( rFormatStr );
+ if( rFormatStr == m_rSelectedFormat )
+ nSelectedPos = nPos;
+ ++nPos;
+ }
+ ++nCount;
+ }
+ m_xCurrencyLb->thaw();
+ // enable multiple selection enabled so we can start with nothing selected
+ m_xCurrencyLb->set_selection_mode(SelectionMode::Multiple);
+ m_xCurrencyLb->connect_row_activated( LINK( this, SvxCurrencyList_Impl, RowActivatedHdl ) );
+ m_xLabel->set_label(SvxResId(RID_SVXSTR_TBLAFMT_CURRENCY));
+ m_xCurrencyLb->select( nSelectedPos );
+ m_xOkBtn->connect_clicked(LINK(this, SvxCurrencyList_Impl, OKHdl));
+
+ // gtk will initially make a best guess depending on the first few entries, so copy the probable
+ // longest entry to the start temporarily and force in the width at this point
+ m_xCurrencyLb->insert_text(0, sLongestString);
+ m_xCurrencyLb->set_size_request(m_xCurrencyLb->get_preferred_size().Width(), m_xCurrencyLb->get_height_rows(12));
+ m_xCurrencyLb->remove(0);
+ }
+ };
+
+ void SvxCurrencyList_Impl::GrabFocus()
+ {
+ m_xCurrencyLb->grab_focus();
+ }
+
+ IMPL_LINK_NOARG(SvxCurrencyList_Impl, OKHdl, weld::Button&, void)
+ {
+ RowActivatedHdl(*m_xCurrencyLb);
+ }
+
+ IMPL_LINK_NOARG(SvxCurrencyList_Impl, RowActivatedHdl, weld::TreeView&, bool)
+ {
+ if (!m_xControl.is())
+ return true;
+
+ // multiple selection enabled so we can start with nothing selected,
+ // so force single selection after something is picked
+ int nSelected = m_xCurrencyLb->get_selected_index();
+ if (nSelected == -1)
+ return true;
+
+ m_xCurrencyLb->set_selection_mode(SelectionMode::Single);
+
+ m_rSelectedFormat = m_aFormatEntries[nSelected];
+ m_eSelectedLanguage = m_eFormatLanguage;
+
+ m_xControl->execute(nSelected + 1);
+
+ m_xControl->EndPopupMode();
+
+ return true;
+ }
+}
+
+void SvxCurrencyToolBoxControl::initialize( const css::uno::Sequence< css::uno::Any >& rArguments )
+{
+ PopupWindowController::initialize(rArguments);
+
+ if (m_pToolbar)
+ {
+ mxPopoverContainer.reset(new ToolbarPopupContainer(m_pToolbar));
+ m_pToolbar->set_item_popover(m_aCommandURL.toUtf8(), mxPopoverContainer->getTopLevel());
+ return;
+ }
+
+ ToolBox* pToolBox = nullptr;
+ ToolBoxItemId nId;
+ if (getToolboxId(nId, &pToolBox) && pToolBox->GetItemCommand(nId) == m_aCommandURL)
+ pToolBox->SetItemBits(nId, ToolBoxItemBits::DROPDOWN | pToolBox->GetItemBits(nId));
+}
+
+std::unique_ptr<WeldToolbarPopup> SvxCurrencyToolBoxControl::weldPopupWindow()
+{
+ return std::make_unique<SvxCurrencyList_Impl>(this, m_pToolbar, m_aFormatString, m_eLanguage);
+}
+
+VclPtr<vcl::Window> SvxCurrencyToolBoxControl::createVclPopupWindow( vcl::Window* pParent )
+{
+ mxInterimPopover = VclPtr<InterimToolbarPopup>::Create(getFrameInterface(), pParent,
+ std::make_unique<SvxCurrencyList_Impl>(this, pParent->GetFrameWeld(), m_aFormatString, m_eLanguage));
+
+ mxInterimPopover->Show();
+
+ return mxInterimPopover;
+}
+
+void SvxCurrencyToolBoxControl::execute( sal_Int16 nSelectModifier )
+{
+ sal_uInt32 nFormatKey;
+ if (m_aFormatString.isEmpty())
+ nFormatKey = NUMBERFORMAT_ENTRY_NOT_FOUND;
+ else
+ {
+ if ( nSelectModifier > 0 )
+ {
+ try
+ {
+ uno::Reference< util::XNumberFormatsSupplier > xRef( m_xFrame->getController()->getModel(), uno::UNO_QUERY );
+ uno::Reference< util::XNumberFormats > rxNumberFormats( xRef->getNumberFormats(), uno::UNO_SET_THROW );
+ css::lang::Locale aLocale = LanguageTag::convertToLocale( m_eLanguage );
+ nFormatKey = rxNumberFormats->queryKey( m_aFormatString, aLocale, false );
+ if ( nFormatKey == NUMBERFORMAT_ENTRY_NOT_FOUND )
+ nFormatKey = rxNumberFormats->addNew( m_aFormatString, aLocale );
+ }
+ catch( const uno::Exception& )
+ {
+ nFormatKey = m_nFormatKey;
+ }
+ }
+ else
+ nFormatKey = m_nFormatKey;
+ }
+
+ if( nFormatKey != NUMBERFORMAT_ENTRY_NOT_FOUND )
+ {
+ Sequence< PropertyValue > aArgs{ comphelper::makePropertyValue("NumberFormatCurrency",
+ nFormatKey) };
+ dispatchCommand( m_aCommandURL, aArgs );
+ m_nFormatKey = nFormatKey;
+ }
+ else
+ PopupWindowController::execute( nSelectModifier );
+}
+
+OUString SvxCurrencyToolBoxControl::getImplementationName()
+{
+ return "com.sun.star.comp.svx.CurrencyToolBoxControl";
+}
+
+css::uno::Sequence<OUString> SvxCurrencyToolBoxControl::getSupportedServiceNames()
+{
+ return { "com.sun.star.frame.ToolbarController" };
+}
+
+extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface *
+com_sun_star_comp_svx_CurrencyToolBoxControl_get_implementation(
+ css::uno::XComponentContext* rContext,
+ css::uno::Sequence<css::uno::Any> const & )
+{
+ return cppu::acquire( new SvxCurrencyToolBoxControl( rContext ) );
+}
+
+Reference< css::accessibility::XAccessible > SvxFontNameBox_Impl::CreateAccessible()
+{
+ FillList();
+ return InterimItemWindow::CreateAccessible();
+}
+
+//static
+void SvxCurrencyToolBoxControl::GetCurrencySymbols( std::vector<OUString>& rList, bool bFlag,
+ std::vector<sal_uInt16>& rCurrencyList )
+{
+ rCurrencyList.clear();
+
+ const NfCurrencyTable& rCurrencyTable = SvNumberFormatter::GetTheCurrencyTable();
+ sal_uInt16 nCount = rCurrencyTable.size();
+
+ sal_uInt16 nStart = 1;
+
+ OUString aString( ApplyLreOrRleEmbedding( rCurrencyTable[0].GetSymbol() ) + " " );
+ aString += ApplyLreOrRleEmbedding( SvtLanguageTable::GetLanguageString(
+ rCurrencyTable[0].GetLanguage() ) );
+
+ rList.push_back( aString );
+ rCurrencyList.push_back( sal_uInt16(-1) ); // nAuto
+
+ if( bFlag )
+ {
+ rList.push_back( aString );
+ rCurrencyList.push_back( 0 );
+ ++nStart;
+ }
+
+ CollatorWrapper aCollator( ::comphelper::getProcessComponentContext() );
+ aCollator.loadDefaultCollator( Application::GetSettings().GetLanguageTag().getLocale(), 0 );
+
+ static const OUStringLiteral aTwoSpace(u" ");
+
+ for( sal_uInt16 i = 1; i < nCount; ++i )
+ {
+ OUString aStr( ApplyLreOrRleEmbedding( rCurrencyTable[i].GetBankSymbol() ) );
+ aStr += aTwoSpace;
+ aStr += ApplyLreOrRleEmbedding( rCurrencyTable[i].GetSymbol() );
+ aStr += aTwoSpace;
+ aStr += ApplyLreOrRleEmbedding( SvtLanguageTable::GetLanguageString(
+ rCurrencyTable[i].GetLanguage() ) );
+
+ std::vector<OUString>::size_type j = nStart;
+ for( ; j < rList.size(); ++j )
+ if ( aCollator.compareString( aStr, rList[j] ) < 0 )
+ break; // insert before first greater than
+
+ rList.insert( rList.begin() + j, aStr );
+ rCurrencyList.insert( rCurrencyList.begin() + j, i );
+ }
+
+ // Append ISO codes to symbol list.
+ // XXX If this is to be changed, various other places would had to be
+ // adapted that assume this order!
+ std::vector<OUString>::size_type nCont = rList.size();
+
+ for ( sal_uInt16 i = 1; i < nCount; ++i )
+ {
+ bool bInsert = true;
+ OUString aStr( ApplyLreOrRleEmbedding( rCurrencyTable[i].GetBankSymbol() ) );
+
+ std::vector<OUString>::size_type j = nCont;
+ for ( ; j < rList.size() && bInsert; ++j )
+ {
+ if( rList[j] == aStr )
+ bInsert = false;
+ else if ( aCollator.compareString( aStr, rList[j] ) < 0 )
+ break; // insert before first greater than
+ }
+ if ( bInsert )
+ {
+ rList.insert( rList.begin() + j, aStr );
+ rCurrencyList.insert( rCurrencyList.begin() + j, i );
+ }
+ }
+}
+
+ListBoxColorWrapper::ListBoxColorWrapper(ColorListBox* pControl)
+ : mpControl(pControl)
+{
+}
+
+void ListBoxColorWrapper::operator()(
+ [[maybe_unused]] const OUString& /*rCommand*/, const svx::NamedThemedColor& rColor)
+{
+ mpControl->Selected(rColor);
+}
+
+void ColorListBox::EnsurePaletteManager()
+{
+ if (!m_xPaletteManager)
+ {
+ m_xPaletteManager = std::make_shared<PaletteManager>();
+ m_xPaletteManager->SetColorSelectFunction(std::ref(m_aColorWrapper));
+ }
+}
+
+void ColorListBox::SetSlotId(sal_uInt16 nSlotId, bool bShowNoneButton)
+{
+ m_nSlotId = nSlotId;
+ m_bShowNoneButton = bShowNoneButton;
+ m_xButton->set_popover(nullptr);
+ m_xColorWindow.reset();
+ m_aSelectedColor = svx::NamedThemedColor::FromNamedColor(bShowNoneButton ? GetNoneColor() : GetAutoColor(m_nSlotId));
+ ShowPreview(m_aSelectedColor.ToNamedColor());
+ createColorWindow();
+}
+
+ColorListBox::ColorListBox(std::unique_ptr<weld::MenuButton> pControl, TopLevelParentFunction const& rTopLevelParentFunction)
+ : m_xButton(std::move(pControl))
+ , m_aColorWrapper(this)
+ , m_aAutoDisplayColor(Application::GetSettings().GetStyleSettings().GetDialogColor())
+ , m_nSlotId(0)
+ , m_bShowNoneButton(false)
+ , m_aTopLevelParentFunction(rTopLevelParentFunction)
+{
+ m_xButton->connect_toggled(LINK(this, ColorListBox, ToggleHdl));
+ m_aSelectedColor = svx::NamedThemedColor::FromNamedColor(GetAutoColor(m_nSlotId));
+ LockWidthRequest();
+ ShowPreview(m_aSelectedColor.ToNamedColor());
+}
+
+IMPL_LINK(ColorListBox, ToggleHdl, weld::Toggleable&, rButton, void)
+{
+ if (rButton.get_active())
+ {
+ ColorWindow* pColorWindow = getColorWindow();
+ if (pColorWindow && !comphelper::LibreOfficeKit::isActive())
+ pColorWindow->GrabFocus();
+ }
+}
+
+ColorListBox::~ColorListBox()
+{
+}
+
+ColorWindow* ColorListBox::getColorWindow() const
+{
+ if (!m_xColorWindow)
+ const_cast<ColorListBox*>(this)->createColorWindow();
+ return m_xColorWindow.get();
+}
+
+void ColorListBox::createColorWindow()
+{
+ const SfxViewFrame* pViewFrame = SfxViewFrame::Current();
+ const SfxFrame* pFrame = pViewFrame ? &pViewFrame->GetFrame() : nullptr;
+ css::uno::Reference<css::frame::XFrame> xFrame(pFrame ? pFrame->GetFrameInterface() : uno::Reference<css::frame::XFrame>());
+
+ EnsurePaletteManager();
+
+ m_xColorWindow.reset(new ColorWindow(
+ OUString() /*m_aCommandURL*/,
+ m_xPaletteManager,
+ m_aColorStatus,
+ m_nSlotId,
+ xFrame,
+ m_xButton.get(),
+ m_aTopLevelParentFunction,
+ m_aColorWrapper));
+
+ SetNoSelection();
+ m_xButton->set_popover(m_xColorWindow->getTopLevel());
+ if (m_bShowNoneButton)
+ m_xColorWindow->ShowNoneButton();
+ m_xColorWindow->SelectEntry(m_aSelectedColor.ToNamedColor());
+}
+
+void ColorListBox::SelectEntry(const NamedColor& rColor)
+{
+ if (rColor.second.trim().isEmpty())
+ {
+ SelectEntry(rColor.first);
+ return;
+ }
+ ColorWindow* pColorWindow = getColorWindow();
+ pColorWindow->SelectEntry(rColor);
+ m_aSelectedColor = svx::NamedThemedColor::FromNamedColor(pColorWindow->GetSelectEntryColor());
+ ShowPreview(m_aSelectedColor.ToNamedColor());
+}
+
+void ColorListBox::SelectEntry(const Color& rColor)
+{
+ ColorWindow* pColorWindow = getColorWindow();
+ pColorWindow->SelectEntry(rColor);
+ m_aSelectedColor = svx::NamedThemedColor::FromNamedColor(pColorWindow->GetSelectEntryColor());
+ ShowPreview(m_aSelectedColor.ToNamedColor());
+}
+
+void ColorListBox::Selected(const svx::NamedThemedColor& rColor)
+{
+ ShowPreview(rColor.ToNamedColor());
+ m_aSelectedColor = rColor;
+ if (m_aSelectedLink.IsSet())
+ m_aSelectedLink.Call(*this);
+}
+
+//to avoid the box resizing every time the color is changed to
+//the optimal size of the individual color, get the longest
+//standard color and stick with that as the size for all
+void ColorListBox::LockWidthRequest()
+{
+ NamedColor aLongestColor;
+ tools::Long nMaxStandardColorTextWidth = 0;
+ XColorListRef const xColorTable = XColorList::CreateStdColorList();
+ for (tools::Long i = 0; i != xColorTable->Count(); ++i)
+ {
+ XColorEntry& rEntry = *xColorTable->GetColor(i);
+ auto nColorTextWidth = m_xButton->get_pixel_size(rEntry.GetName()).Width();
+ if (nColorTextWidth > nMaxStandardColorTextWidth)
+ {
+ nMaxStandardColorTextWidth = nColorTextWidth;
+ aLongestColor.second = rEntry.GetName();
+ }
+ }
+ ShowPreview(aLongestColor);
+ m_xButton->set_size_request(m_xButton->get_preferred_size().Width(), -1);
+}
+
+void ColorListBox::ShowPreview(const NamedColor &rColor)
+{
+ // ScGridWindow::UpdateAutoFilterFromMenu is similar
+ const StyleSettings& rStyleSettings = Application::GetSettings().GetStyleSettings();
+ Size aImageSize(rStyleSettings.GetListBoxPreviewDefaultPixelSize());
+
+ ScopedVclPtrInstance<VirtualDevice> xDevice;
+ xDevice->SetOutputSize(aImageSize);
+ const tools::Rectangle aRect(Point(0, 0), aImageSize);
+ if (m_bShowNoneButton && rColor.first == COL_NONE_COLOR)
+ {
+ const Color aW(COL_WHITE);
+ const Color aG(0xef, 0xef, 0xef);
+ int nMinDim = std::min(aImageSize.Width(), aImageSize.Height()) + 1;
+ int nCheckSize = nMinDim / 3;
+ xDevice->DrawCheckered(aRect.TopLeft(), aRect.GetSize(), std::min(nCheckSize, 8), aW, aG);
+ xDevice->SetFillColor();
+ }
+ else
+ {
+ if (rColor.first == COL_AUTO)
+ xDevice->SetFillColor(m_aAutoDisplayColor);
+ else
+ xDevice->SetFillColor(rColor.first);
+ }
+
+ xDevice->SetLineColor(rStyleSettings.GetDisableColor());
+ xDevice->DrawRect(aRect);
+
+ m_xButton->set_image(xDevice.get());
+ m_xButton->set_label(rColor.second);
+}
+
+MenuOrToolMenuButton::MenuOrToolMenuButton(weld::MenuButton* pMenuButton)
+ : m_pMenuButton(pMenuButton)
+ , m_pToolbar(nullptr)
+ , m_pControl(nullptr)
+ , m_nId(0)
+{
+}
+
+MenuOrToolMenuButton::MenuOrToolMenuButton(weld::Toolbar* pToolbar, const OString& rIdent)
+ : m_pMenuButton(nullptr)
+ , m_pToolbar(pToolbar)
+ , m_aIdent(rIdent)
+ , m_pControl(nullptr)
+ , m_nId(0)
+{
+}
+
+MenuOrToolMenuButton::MenuOrToolMenuButton(SvxColorToolBoxControl* pControl, ToolBox* pToolbar, ToolBoxItemId nId)
+ : m_pMenuButton(nullptr)
+ , m_pToolbar(nullptr)
+ , m_pControl(pControl)
+ , m_xToolBox(pToolbar)
+ , m_nId(nId)
+{
+}
+
+MenuOrToolMenuButton::~MenuOrToolMenuButton()
+{
+}
+
+bool MenuOrToolMenuButton::get_active() const
+{
+ if (m_pMenuButton)
+ return m_pMenuButton->get_active();
+ if (m_pToolbar)
+ return m_pToolbar->get_menu_item_active(m_aIdent);
+ return m_xToolBox->GetDownItemId() == m_nId;
+}
+
+void MenuOrToolMenuButton::set_inactive() const
+{
+ if (m_pMenuButton)
+ {
+ if (m_pMenuButton->get_active())
+ m_pMenuButton->set_active(false);
+ return;
+ }
+ if (m_pToolbar)
+ {
+ if (m_pToolbar->get_menu_item_active(m_aIdent))
+ m_pToolbar->set_menu_item_active(m_aIdent, false);
+ return;
+ }
+ m_pControl->EndPopupMode();
+}
+
+weld::Widget* MenuOrToolMenuButton::get_widget() const
+{
+ if (m_pMenuButton)
+ return m_pMenuButton;
+ if (m_pToolbar)
+ return m_pToolbar;
+ return m_xToolBox->GetFrameWeld();
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/tbxctrls/tbunocontroller.cxx b/svx/source/tbxctrls/tbunocontroller.cxx
new file mode 100644
index 000000000..a8073a1a7
--- /dev/null
+++ b/svx/source/tbxctrls/tbunocontroller.cxx
@@ -0,0 +1,538 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * 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/XFrame.hpp>
+#include <com/sun/star/frame/status/FontHeight.hpp>
+#include <com/sun/star/frame/XDispatchProvider.hpp>
+#include <com/sun/star/beans/PropertyValue.hpp>
+#include <com/sun/star/lang/XServiceInfo.hpp>
+#include <com/sun/star/util/XURLTransformer.hpp>
+
+#include <comphelper/propertyvalue.hxx>
+#include <rtl/math.hxx>
+#include <vcl/event.hxx>
+#include <vcl/svapp.hxx>
+#include <vcl/weldutils.hxx>
+#include <vcl/window.hxx>
+#include <vcl/settings.hxx>
+#include <toolkit/helper/vclunohelper.hxx>
+#include <svtools/ctrltool.hxx>
+#include <svtools/ctrlbox.hxx>
+#include <svtools/toolboxcontroller.hxx>
+#include <tools/json_writer.hxx>
+#include <vcl/toolbox.hxx>
+#include <cppuhelper/queryinterface.hxx>
+#include <cppuhelper/supportsservice.hxx>
+
+#include <memory>
+
+#include <vcl/InterimItemWindow.hxx>
+
+using namespace ::com::sun::star;
+
+namespace {
+
+class SvxFontSizeBox_Base;
+class SvxFontSizeBox_Impl;
+
+class FontHeightToolBoxControl : public svt::ToolboxController,
+ public lang::XServiceInfo
+{
+ public:
+ explicit FontHeightToolBoxControl(
+ 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() noexcept override;
+ virtual void SAL_CALL release() noexcept 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<SvxFontSizeBox_Impl> m_xVclBox;
+ std::unique_ptr<SvxFontSizeBox_Base> m_xWeldBox;
+ SvxFontSizeBox_Base* m_pBox;
+};
+
+class SvxFontSizeBox_Base
+{
+public:
+ SvxFontSizeBox_Base(std::unique_ptr<weld::ComboBox> xWidget,
+ const uno::Reference< frame::XFrame >& _xFrame,
+ FontHeightToolBoxControl& rCtrl);
+
+ virtual ~SvxFontSizeBox_Base()
+ {
+ }
+
+ virtual void set_sensitive(bool bSensitive)
+ {
+ m_xWidget->set_sensitive(bSensitive);
+ }
+
+ void statusChanged_Impl(tools::Long nHeight, bool bErase);
+ void UpdateFont();
+
+protected:
+ FontHeightToolBoxControl& m_rCtrl;
+ OUString m_aCurText;
+ bool m_bRelease;
+ uno::Reference<frame::XFrame> m_xFrame;
+ std::unique_ptr<FontSizeBox> m_xWidget;
+
+ void ReleaseFocus_Impl();
+ void Select();
+
+ virtual bool DoKeyInput(const KeyEvent& rKEvt);
+
+ DECL_LINK(SelectHdl, weld::ComboBox&, void);
+ DECL_LINK(KeyInputHdl, const KeyEvent&, bool);
+ DECL_LINK(ActivateHdl, weld::ComboBox&, bool);
+ DECL_LINK(FocusOutHdl, weld::Widget&, void);
+ DECL_LINK(DumpAsPropertyTreeHdl, tools::JsonWriter&, void);
+};
+
+class SvxFontSizeBox_Impl final : public InterimItemWindow
+ , public SvxFontSizeBox_Base
+{
+public:
+ SvxFontSizeBox_Impl(vcl::Window* pParent,
+ const uno::Reference< frame::XFrame >& _xFrame,
+ FontHeightToolBoxControl& rCtrl);
+
+ virtual void dispose() override
+ {
+ m_xWidget.reset();
+ InterimItemWindow::dispose();
+ }
+
+ virtual void GetFocus() override
+ {
+ if (m_xWidget)
+ m_xWidget->grab_focus();
+ InterimItemWindow::GetFocus();
+ }
+
+ virtual ~SvxFontSizeBox_Impl() override
+ {
+ disposeOnce();
+ }
+
+ void SetOptimalSize();
+
+ virtual void DataChanged(const DataChangedEvent& rDCEvt) override;
+
+ virtual void set_sensitive(bool bSensitive) override
+ {
+ m_xWidget->set_sensitive(bSensitive);
+ if (bSensitive)
+ InterimItemWindow::Enable();
+ else
+ InterimItemWindow::Disable();
+ }
+
+private:
+ virtual bool DoKeyInput(const KeyEvent& rKEvt) override;
+};
+
+SvxFontSizeBox_Base::SvxFontSizeBox_Base(std::unique_ptr<weld::ComboBox> xWidget,
+ const uno::Reference<frame::XFrame>& rFrame,
+ FontHeightToolBoxControl& rCtrl)
+ : m_rCtrl(rCtrl)
+ , m_bRelease(true)
+ , m_xFrame(rFrame)
+ , m_xWidget(new FontSizeBox(std::move(xWidget)))
+{
+ m_xWidget->set_value(0);
+ m_xWidget->set_active_or_entry_text("");
+ m_xWidget->disable_entry_completion();
+
+ m_xWidget->connect_changed(LINK(this, SvxFontSizeBox_Base, SelectHdl));
+ m_xWidget->connect_key_press(LINK(this, SvxFontSizeBox_Base, KeyInputHdl));
+ m_xWidget->connect_entry_activate(LINK(this, SvxFontSizeBox_Base, ActivateHdl));
+ m_xWidget->connect_focus_out(LINK(this, SvxFontSizeBox_Base, FocusOutHdl));
+ m_xWidget->connect_get_property_tree(LINK(this, SvxFontSizeBox_Base, DumpAsPropertyTreeHdl));
+}
+
+void SvxFontSizeBox_Base::ReleaseFocus_Impl()
+{
+ if ( !m_bRelease )
+ {
+ m_bRelease = true;
+ return;
+ }
+
+ if ( m_xFrame.is() && m_xFrame->getContainerWindow().is() )
+ m_xFrame->getContainerWindow()->setFocus();
+}
+
+IMPL_LINK(SvxFontSizeBox_Base, SelectHdl, weld::ComboBox&, rCombo, void)
+{
+ if (rCombo.changed_by_direct_pick()) // only when picked from the list
+ Select();
+}
+
+IMPL_LINK_NOARG(SvxFontSizeBox_Base, ActivateHdl, weld::ComboBox&, bool)
+{
+ Select();
+ return true;
+}
+
+void SvxFontSizeBox_Base::Select()
+{
+ sal_Int64 nSelVal = m_xWidget->get_value();
+ float fSelVal = float( nSelVal ) / 10;
+
+ uno::Sequence< beans::PropertyValue > aArgs{ comphelper::makePropertyValue("FontHeight.Height",
+ fSelVal) };
+
+ /* #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_rCtrl.dispatchCommand( aArgs );
+}
+
+void SvxFontSizeBox_Base::statusChanged_Impl( tools::Long nPoint, bool bErase )
+{
+ if ( !bErase )
+ {
+ // convert the metric
+ tools::Long nVal = nPoint;
+
+ // changed => set new value
+ if (m_xWidget->get_value() != nVal)
+ m_xWidget->set_value(nVal);
+ }
+ else
+ {
+ // delete value in the display
+ m_xWidget->set_value(-1L);
+ m_xWidget->set_active_or_entry_text("");
+ }
+ m_aCurText = m_xWidget->get_active_text();
+}
+
+void SvxFontSizeBox_Base::UpdateFont()
+{
+ // filling up the sizes list
+ auto nOldVal = m_xWidget->get_value(); // memorize old value
+ FontList aFontList(Application::GetDefaultDevice());
+
+ m_xWidget->Fill(&aFontList);
+
+ m_xWidget->set_value(nOldVal); // restore old value
+ m_aCurText = m_xWidget->get_active_text(); // memorize to reset at ESC
+}
+
+IMPL_LINK(SvxFontSizeBox_Base, KeyInputHdl, const KeyEvent&, rKEvt, bool)
+{
+ return DoKeyInput(rKEvt);
+}
+
+bool SvxFontSizeBox_Base::DoKeyInput(const KeyEvent& rKEvt)
+{
+ bool bHandled = false;
+
+ sal_uInt16 nCode = rKEvt.GetKeyCode().GetCode();
+
+ switch (nCode)
+ {
+ case KEY_TAB:
+ m_bRelease = false;
+ Select();
+ break;
+
+ case KEY_ESCAPE:
+ m_xWidget->set_active_or_entry_text(m_aCurText);
+ if (!m_rCtrl.IsInSidebar())
+ {
+ ReleaseFocus_Impl();
+ bHandled = true;
+ }
+ break;
+ }
+
+ return bHandled;
+}
+
+bool SvxFontSizeBox_Impl::DoKeyInput(const KeyEvent& rKEvt)
+{
+ return SvxFontSizeBox_Base::DoKeyInput(rKEvt) || ChildKeyInput(rKEvt);
+}
+
+IMPL_LINK_NOARG(SvxFontSizeBox_Base, 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_active_or_entry_text(m_aCurText);
+}
+
+void SvxFontSizeBox_Impl::SetOptimalSize()
+{
+ SetSizePixel(get_preferred_size());
+}
+
+SvxFontSizeBox_Impl::SvxFontSizeBox_Impl(vcl::Window* pParent,
+ const uno::Reference<frame::XFrame>& rFrame,
+ FontHeightToolBoxControl& rCtrl)
+ : InterimItemWindow(pParent, "svx/ui/fontsizebox.ui", "FontSizeBox")
+ , SvxFontSizeBox_Base(m_xBuilder->weld_combo_box("fontsizecombobox"), rFrame, rCtrl)
+{
+}
+
+void SvxFontSizeBox_Impl::DataChanged( const DataChangedEvent& rDCEvt )
+{
+ if ( (rDCEvt.GetType() == DataChangedEventType::SETTINGS) &&
+ (rDCEvt.GetFlags() & AllSettingsFlags::STYLE) )
+ {
+ SetOptimalSize();
+ }
+}
+
+IMPL_LINK(SvxFontSizeBox_Base, DumpAsPropertyTreeHdl, tools::JsonWriter&, rJsonWriter, void)
+{
+ {
+ auto entriesNode = rJsonWriter.startNode("entries");
+ for (int i = 0, nCount = m_xWidget->get_count(); i < nCount; ++i)
+ {
+ auto entryNode = rJsonWriter.startNode("");
+ rJsonWriter.put("", m_xWidget->get_text(i));
+ }
+ }
+
+ int nActive = m_xWidget->get_active();
+ rJsonWriter.put("selectedCount", static_cast<sal_Int32>(nActive == -1 ? 0 : 1));
+ {
+ auto selectedNode = rJsonWriter.startNode("selectedEntries");
+ if (nActive != -1)
+ {
+ auto node = rJsonWriter.startNode("");
+ rJsonWriter.put("", static_cast<sal_Int32>(nActive));
+ }
+ }
+
+ rJsonWriter.put("command", ".uno:FontHeight");
+}
+
+FontHeightToolBoxControl::FontHeightToolBoxControl( const uno::Reference< uno::XComponentContext >& rxContext )
+ : svt::ToolboxController( rxContext,
+ uno::Reference< frame::XFrame >(),
+ ".uno:FontHeight" ),
+ m_pBox( nullptr )
+{
+ addStatusListener( ".uno:CharFontName");
+}
+
+// XInterface
+css::uno::Any SAL_CALL FontHeightToolBoxControl::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 FontHeightToolBoxControl::acquire() noexcept
+{
+ ToolboxController::acquire();
+}
+
+void SAL_CALL FontHeightToolBoxControl::release() noexcept
+{
+ ToolboxController::release();
+}
+
+// XServiceInfo
+sal_Bool SAL_CALL FontHeightToolBoxControl::supportsService( const OUString& ServiceName )
+{
+ return cppu::supportsService(this, ServiceName);
+}
+
+OUString SAL_CALL FontHeightToolBoxControl::getImplementationName()
+{
+ return "com.sun.star.svx.FontHeightToolBoxController";
+}
+
+uno::Sequence< OUString > SAL_CALL FontHeightToolBoxControl::getSupportedServiceNames( )
+{
+ return { "com.sun.star.frame.ToolbarController" };
+}
+
+// XComponent
+void SAL_CALL FontHeightToolBoxControl::dispose()
+{
+ svt::ToolboxController::dispose();
+
+ SolarMutexGuard aSolarMutexGuard;
+ m_xVclBox.disposeAndClear();
+ m_xWeldBox.reset();
+ m_pBox = nullptr;
+}
+
+// XStatusListener
+void SAL_CALL FontHeightToolBoxControl::statusChanged(
+ const frame::FeatureStateEvent& rEvent )
+{
+ if ( !m_pBox )
+ return;
+
+ SolarMutexGuard aSolarMutexGuard;
+ if (rEvent.FeatureURL.Path == "FontHeight")
+ {
+ if ( rEvent.IsEnabled )
+ {
+ m_pBox->set_sensitive(true);
+ frame::status::FontHeight aFontHeight;
+ if ( rEvent.State >>= aFontHeight )
+ {
+ // tdf#83090 - correctly round the height of the font
+ aFontHeight.Height = rtl::math::round(10. * aFontHeight.Height);
+ m_pBox->statusChanged_Impl(tools::Long(aFontHeight.Height), false);
+ }
+ else
+ m_pBox->statusChanged_Impl( tools::Long( -1 ), true );
+ }
+ else
+ {
+ m_pBox->set_sensitive(false);
+ m_pBox->statusChanged_Impl( tools::Long( -1 ), true );
+ }
+
+ if (m_pToolbar)
+ m_pToolbar->set_item_sensitive(m_aCommandURL.toUtf8(), rEvent.IsEnabled);
+ else
+ {
+ ToolBox* pToolBox = nullptr;
+ ToolBoxItemId nId;
+ if (getToolboxId(nId, &pToolBox))
+ pToolBox->EnableItem(nId, rEvent.IsEnabled);
+ }
+ }
+ else if ( rEvent.FeatureURL.Path == "CharFontName" )
+ {
+ m_pBox->UpdateFont();
+ }
+}
+
+// XToolbarController
+void SAL_CALL FontHeightToolBoxControl::execute( sal_Int16 /*KeyModifier*/ )
+{
+}
+
+void SAL_CALL FontHeightToolBoxControl::click()
+{
+}
+
+void SAL_CALL FontHeightToolBoxControl::doubleClick()
+{
+}
+
+uno::Reference< awt::XWindow > SAL_CALL FontHeightToolBoxControl::createPopupWindow()
+{
+ return uno::Reference< awt::XWindow >();
+}
+
+uno::Reference< awt::XWindow > SAL_CALL FontHeightToolBoxControl::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("fontsizecombobox"));
+
+ xItemWindow = css::uno::Reference<css::awt::XWindow>(new weld::TransportAsXWindow(xWidget.get()));
+
+ m_xWeldBox.reset(new SvxFontSizeBox_Base(std::move(xWidget), m_xFrame, *this));
+ m_pBox = m_xWeldBox.get();
+ //Get the box to fill itself with all its sizes
+ m_pBox->UpdateFont();
+ }
+ else
+ {
+ VclPtr<vcl::Window> pParent = VCLUnoHelper::GetWindow( xParent );
+ if ( pParent )
+ {
+ SolarMutexGuard aSolarMutexGuard;
+ m_xVclBox = VclPtr<SvxFontSizeBox_Impl>::Create( pParent, m_xFrame, *this );
+ m_pBox = m_xVclBox.get();
+ //Get the box to fill itself with all its sizes
+ m_pBox->UpdateFont();
+ //Make it size itself to its optimal size re above sizes
+ m_xVclBox->SetOptimalSize();
+ xItemWindow = VCLUnoHelper::GetInterface(m_xVclBox);
+ }
+ }
+
+ return xItemWindow;
+}
+
+void FontHeightToolBoxControl::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:FontHeight";
+ xURLTransformer->parseStrict( aURL );
+ xDispatch = xDispatchProvider->queryDispatch( aURL, OUString(), 0 );
+ if ( xDispatch.is() )
+ xDispatch->dispatch( aURL, rArgs );
+ }
+}
+
+}
+
+extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface *
+com_sun_star_svx_FontHeightToolBoxController_get_implementation(
+ css::uno::XComponentContext *rxContext,
+ css::uno::Sequence<css::uno::Any> const &)
+{
+ return cppu::acquire(new FontHeightToolBoxControl(rxContext));
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/tbxctrls/tbunosearchcontrollers.cxx b/svx/source/tbxctrls/tbunosearchcontrollers.cxx
new file mode 100644
index 000000000..487168285
--- /dev/null
+++ b/svx/source/tbxctrls/tbunosearchcontrollers.cxx
@@ -0,0 +1,1662 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * 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 <map>
+#include <vector>
+
+#include <config_feature_desktop.h>
+
+#include <svx/strings.hrc>
+#include <svx/dialmgr.hxx>
+
+#include <comphelper/propertysequence.hxx>
+#include <cppuhelper/queryinterface.hxx>
+#include <cppuhelper/supportsservice.hxx>
+#include <cppuhelper/weak.hxx>
+#include <com/sun/star/beans/PropertyValue.hpp>
+#include <com/sun/star/beans/XPropertySet.hpp>
+#include <com/sun/star/frame/DispatchDescriptor.hpp>
+#include <com/sun/star/frame/XDispatch.hpp>
+#include <com/sun/star/frame/XDispatchProvider.hpp>
+#include <com/sun/star/frame/XLayoutManager.hpp>
+#include <com/sun/star/frame/XStatusListener.hpp>
+#include <com/sun/star/lang/XInitialization.hpp>
+#include <com/sun/star/lang/XServiceInfo.hpp>
+#include <com/sun/star/text/XTextRange.hpp>
+#include <com/sun/star/ui/XUIElement.hpp>
+#include <com/sun/star/util/URL.hpp>
+#include <com/sun/star/util/URLTransformer.hpp>
+#include <com/sun/star/util/SearchAlgorithms.hpp>
+#include <com/sun/star/util/SearchAlgorithms2.hpp>
+
+#include <vcl/InterimItemWindow.hxx>
+#include <svl/ctloptions.hxx>
+#include <svl/srchitem.hxx>
+#include <svtools/acceleratorexecute.hxx>
+#include <svtools/toolboxcontroller.hxx>
+#include <toolkit/helper/vclunohelper.hxx>
+#include <vcl/toolbox.hxx>
+#include <vcl/svapp.hxx>
+#include <svx/labelitemwindow.hxx>
+#include <svx/srchdlg.hxx>
+#include <vcl/event.hxx>
+
+#include <findtextfield.hxx>
+
+using namespace css;
+
+namespace {
+
+constexpr OUStringLiteral COMMAND_FINDTEXT = u".uno:FindText";
+constexpr OUStringLiteral COMMAND_DOWNSEARCH = u".uno:DownSearch";
+constexpr OUStringLiteral COMMAND_UPSEARCH = u".uno:UpSearch";
+constexpr OUStringLiteral COMMAND_FINDALL = u".uno:FindAll";
+constexpr OUStringLiteral COMMAND_MATCHCASE = u".uno:MatchCase";
+constexpr OUStringLiteral COMMAND_SEARCHFORMATTED = u".uno:SearchFormattedDisplayString";
+
+const sal_Int32 REMEMBER_SIZE = 10;
+
+class CheckButtonItemWindow final : public InterimItemWindow
+{
+public:
+ CheckButtonItemWindow(vcl::Window* pParent, const OUString& rLabel)
+ : InterimItemWindow(pParent, "svx/ui/checkbuttonbox.ui", "CheckButtonBox")
+ , m_xWidget(m_xBuilder->weld_check_button("checkbutton"))
+ {
+ InitControlBase(m_xWidget.get());
+
+ m_xWidget->connect_key_press(LINK(this, CheckButtonItemWindow, KeyInputHdl));
+ m_xWidget->set_label(rLabel);
+ SetSizePixel(m_xWidget->get_preferred_size());
+ }
+
+ bool get_active() const
+ {
+ return m_xWidget->get_active();
+ }
+
+ virtual void dispose() override
+ {
+ m_xWidget.reset();
+ InterimItemWindow::dispose();
+ }
+
+ virtual ~CheckButtonItemWindow() override
+ {
+ disposeOnce();
+ }
+
+private:
+ std::unique_ptr<weld::CheckButton> m_xWidget;
+
+ DECL_LINK(KeyInputHdl, const KeyEvent&, bool);
+};
+
+IMPL_LINK(CheckButtonItemWindow, KeyInputHdl, const KeyEvent&, rKeyEvent, bool)
+{
+ return ChildKeyInput(rKeyEvent);
+}
+
+void impl_executeSearch( const css::uno::Reference< css::uno::XComponentContext >& rxContext,
+ const css::uno::Reference< css::frame::XFrame >& xFrame,
+ const ToolBox* pToolBox,
+ const bool aSearchBackwards,
+ const bool aFindAll = false )
+{
+ css::uno::Reference< css::util::XURLTransformer > xURLTransformer( css::util::URLTransformer::create( rxContext ) );
+ css::util::URL aURL;
+ aURL.Complete = ".uno:ExecuteSearch";
+ xURLTransformer->parseStrict(aURL);
+
+ OUString sFindText;
+ bool aMatchCase = false;
+ bool bSearchFormatted = false;
+ if ( pToolBox )
+ {
+ ToolBox::ImplToolItems::size_type nItemCount = pToolBox->GetItemCount();
+ for ( ToolBox::ImplToolItems::size_type i=0; i<nItemCount; ++i )
+ {
+ ToolBoxItemId id = pToolBox->GetItemId(i);
+ OUString sItemCommand = pToolBox->GetItemCommand(id);
+ if ( sItemCommand == COMMAND_FINDTEXT )
+ {
+ FindTextFieldControl* pItemWin = static_cast<FindTextFieldControl*>(pToolBox->GetItemWindow(id));
+ if (pItemWin)
+ {
+ sFindText = pItemWin->get_active_text();
+ if (aFindAll && !pItemWin->ControlHasFocus())
+ pItemWin->GetFocus();
+ }
+ } else if ( sItemCommand == COMMAND_MATCHCASE )
+ {
+ CheckButtonItemWindow* pItemWin = static_cast<CheckButtonItemWindow*>(pToolBox->GetItemWindow(id));
+ if (pItemWin)
+ aMatchCase = pItemWin->get_active();
+ } else if ( sItemCommand == COMMAND_SEARCHFORMATTED )
+ {
+ CheckButtonItemWindow* pItemWin = static_cast<CheckButtonItemWindow*>(pToolBox->GetItemWindow(id));
+ if (pItemWin)
+ bSearchFormatted = pItemWin->get_active();
+ }
+ }
+ }
+
+ SvtCTLOptions aCTLOptions;
+ TransliterationFlags nFlags = TransliterationFlags::NONE;
+ if (!aMatchCase)
+ nFlags |= TransliterationFlags::IGNORE_CASE;
+ if (aCTLOptions.IsCTLFontEnabled())
+ nFlags |= TransliterationFlags::IGNORE_DIACRITICS_CTL
+ | TransliterationFlags::IGNORE_KASHIDA_CTL;
+
+ auto aArgs( comphelper::InitPropertySequence( {
+ { "SearchItem.SearchString", css::uno::Any( sFindText ) },
+ // Related tdf#102506: make Find Bar Ctrl+F searching by value by default
+ { "SearchItem.CellType", css::uno::Any( sal_Int16(SvxSearchCellType::VALUE) ) },
+ { "SearchItem.Backward", css::uno::Any( aSearchBackwards ) },
+ { "SearchItem.SearchFlags", css::uno::Any( sal_Int32(0) ) },
+ { "SearchItem.TransliterateFlags", css::uno::Any( static_cast<sal_Int32>(nFlags) ) },
+ { "SearchItem.Command", css::uno::Any( static_cast<sal_Int16>(aFindAll ?SvxSearchCmd::FIND_ALL : SvxSearchCmd::FIND ) ) },
+ { "SearchItem.AlgorithmType", css::uno::Any( sal_Int16(css::util::SearchAlgorithms_ABSOLUTE) ) },
+ { "SearchItem.AlgorithmType2", css::uno::Any( sal_Int16(css::util::SearchAlgorithms2::ABSOLUTE) ) },
+ { "SearchItem.SearchFormatted", css::uno::Any( bSearchFormatted ) }
+ } ) );
+
+ css::uno::Reference< css::frame::XDispatchProvider > xDispatchProvider(xFrame, css::uno::UNO_QUERY);
+ if ( xDispatchProvider.is() )
+ {
+ css::uno::Reference< css::frame::XDispatch > xDispatch = xDispatchProvider->queryDispatch( aURL, OUString(), 0 );
+ if ( xDispatch.is() && !aURL.Complete.isEmpty() )
+ xDispatch->dispatch( aURL, aArgs );
+ }
+}
+
+}
+
+FindTextFieldControl::FindTextFieldControl( vcl::Window* pParent,
+ css::uno::Reference< css::frame::XFrame > const & xFrame,
+ const css::uno::Reference< css::uno::XComponentContext >& xContext) :
+ InterimItemWindow(pParent, "svx/ui/findbox.ui", "FindBox"),
+ m_nAsyncGetFocusId(nullptr),
+ m_xWidget(m_xBuilder->weld_combo_box("find")),
+ m_xFrame(xFrame),
+ m_xContext(xContext),
+ m_pAcc(svt::AcceleratorExecute::createAcceleratorHelper())
+{
+ InitControlBase(m_xWidget.get());
+
+ m_xWidget->set_entry_placeholder_text(SvxResId(RID_SVXSTR_FINDBAR_FIND));
+ m_xWidget->set_entry_completion(true, true);
+ m_pAcc->init(m_xContext, m_xFrame);
+
+ m_xWidget->connect_focus_in(LINK(this, FindTextFieldControl, FocusInHdl));
+ m_xWidget->connect_key_press(LINK(this, FindTextFieldControl, KeyInputHdl));
+ m_xWidget->connect_entry_activate(LINK(this, FindTextFieldControl, ActivateHdl));
+
+ m_xWidget->set_size_request(250, -1);
+ SetSizePixel(m_xWidget->get_preferred_size());
+}
+
+void FindTextFieldControl::Remember_Impl(const OUString& rStr)
+{
+ const sal_Int32 nCount = m_xWidget->get_count();
+
+ for (sal_Int32 i=0; i<nCount; ++i)
+ {
+ if (rStr == m_xWidget->get_text(i))
+ return;
+ }
+
+ if (nCount == REMEMBER_SIZE)
+ m_xWidget->remove(REMEMBER_SIZE-1);
+
+ m_xWidget->insert_text(0, rStr);
+}
+
+void FindTextFieldControl::SetTextToSelected_Impl()
+{
+ OUString aString;
+
+ try
+ {
+ css::uno::Reference<css::frame::XController> xController(m_xFrame->getController(), css::uno::UNO_SET_THROW);
+ css::uno::Reference<css::frame::XModel> xModel(xController->getModel(), css::uno::UNO_SET_THROW);
+ css::uno::Reference<css::container::XIndexAccess> xIndexAccess(xModel->getCurrentSelection(), css::uno::UNO_QUERY_THROW);
+ if (xIndexAccess->getCount() > 0)
+ {
+ css::uno::Reference<css::text::XTextRange> xTextRange(xIndexAccess->getByIndex(0), css::uno::UNO_QUERY_THROW);
+ aString = xTextRange->getString();
+ }
+ }
+ catch ( ... )
+ {
+ }
+
+ if ( !aString.isEmpty() )
+ {
+ // If something is selected in the document, prepopulate with this
+ m_xWidget->set_entry_text(aString);
+ m_aChangeHdl.Call(*m_xWidget);
+ }
+ else if (get_count() > 0)
+ {
+ // Else, prepopulate with last search word (fdo#84256)
+ m_xWidget->set_entry_text(m_xWidget->get_text(0));
+ }
+}
+
+IMPL_LINK(FindTextFieldControl, KeyInputHdl, const KeyEvent&, rKeyEvent, bool)
+{
+ if (isDisposed())
+ return true;
+
+ bool bRet = false;
+
+ bool bShift = rKeyEvent.GetKeyCode().IsShift();
+ bool bMod1 = rKeyEvent.GetKeyCode().IsMod1();
+ sal_uInt16 nCode = rKeyEvent.GetKeyCode().GetCode();
+
+ // Close the search bar on Escape
+ if ( KEY_ESCAPE == nCode )
+ {
+ bRet = true;
+ GrabFocusToDocument();
+
+ // hide the findbar
+ css::uno::Reference< css::beans::XPropertySet > xPropSet(m_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())
+ {
+ static const OUStringLiteral sResourceURL( u"private:resource/toolbar/findbar" );
+ xLayoutManager->hideElement( sResourceURL );
+ xLayoutManager->destroyElement( sResourceURL );
+ }
+ }
+ }
+ else
+ {
+ auto awtKey = svt::AcceleratorExecute::st_VCLKey2AWTKey(rKeyEvent.GetKeyCode());
+ const OUString aCommand(m_pAcc->findCommand(awtKey));
+
+ // Select text in the search box when Ctrl-F pressed
+ if ( bMod1 && nCode == KEY_F )
+ m_xWidget->select_entry_region(0, -1);
+ // Execute the search when Ctrl-G, F3 and Shift-RETURN pressed (in addition to ActivateHdl condition which handles bare RETURN)
+ else if ( (bMod1 && KEY_G == nCode) || (bShift && KEY_RETURN == nCode) || (KEY_F3 == nCode) )
+ {
+ ActivateFind(bShift);
+ bRet = true;
+ }
+ else if (aCommand == ".uno:SearchDialog")
+ bRet = m_pAcc->execute(awtKey);
+
+ // find-shortcut called with focus already in find
+ if (aCommand == "vnd.sun.star.findbar:FocusToFindbar")
+ {
+ m_xWidget->call_attention_to();
+ bRet = true;
+ }
+ }
+
+ return bRet || ChildKeyInput(rKeyEvent);
+}
+
+void FindTextFieldControl::ActivateFind(bool bShift)
+{
+ Remember_Impl(m_xWidget->get_active_text());
+
+ vcl::Window* pWindow = GetParent();
+ ToolBox* pToolBox = static_cast<ToolBox*>(pWindow);
+
+ impl_executeSearch(m_xContext, m_xFrame, pToolBox, bShift);
+
+ m_xWidget->grab_focus();
+}
+
+// Execute the search when activated, typically due to "Return"
+IMPL_LINK_NOARG(FindTextFieldControl, ActivateHdl, weld::ComboBox&, bool)
+{
+ if (isDisposed())
+ return true;
+
+ ActivateFind(false);
+
+ return true;
+}
+
+IMPL_LINK_NOARG(FindTextFieldControl, OnAsyncGetFocus, void*, void)
+{
+ m_nAsyncGetFocusId = nullptr;
+ m_xWidget->select_entry_region(0, -1);
+}
+
+void FindTextFieldControl::FocusIn()
+{
+ if (m_nAsyncGetFocusId || !m_xWidget)
+ return;
+
+ // do it async to defeat entry in combobox having its own ideas about the focus
+ m_nAsyncGetFocusId = Application::PostUserEvent(LINK(this, FindTextFieldControl, OnAsyncGetFocus));
+
+ GrabFocus(); // tdf#137993 ensure the toplevel vcl::Window is activated so SfxViewFrame::Current is valid
+}
+
+IMPL_LINK_NOARG(FindTextFieldControl, FocusInHdl, weld::Widget&, void)
+{
+ FocusIn();
+}
+
+void FindTextFieldControl::dispose()
+{
+ if (m_nAsyncGetFocusId)
+ {
+ Application::RemoveUserEvent(m_nAsyncGetFocusId);
+ m_nAsyncGetFocusId = nullptr;
+ }
+ m_xWidget.reset();
+ InterimItemWindow::dispose();
+}
+
+FindTextFieldControl::~FindTextFieldControl()
+{
+ disposeOnce();
+}
+
+void FindTextFieldControl::connect_changed(const Link<weld::ComboBox&, void>& rLink)
+{
+ m_aChangeHdl = rLink;
+ m_xWidget->connect_changed(rLink);
+}
+
+int FindTextFieldControl::get_count() const
+{
+ return m_xWidget->get_count();
+}
+
+OUString FindTextFieldControl::get_text(int nIndex) const
+{
+ return m_xWidget->get_text(nIndex);
+}
+
+OUString FindTextFieldControl::get_active_text() const
+{
+ return m_xWidget->get_active_text();
+}
+
+void FindTextFieldControl::append_text(const OUString& rText)
+{
+ m_xWidget->append_text(rText);
+}
+
+void FindTextFieldControl::set_entry_message_type(weld::EntryMessageType eType)
+{
+ m_xWidget->set_entry_message_type(eType);
+}
+
+namespace {
+
+class SearchToolbarControllersManager
+{
+public:
+
+ SearchToolbarControllersManager();
+
+ static SearchToolbarControllersManager& createControllersManager();
+
+ void registryController( const css::uno::Reference< css::frame::XFrame >& xFrame, const css::uno::Reference< css::frame::XStatusListener >& xStatusListener, const OUString& sCommandURL );
+ void freeController ( const css::uno::Reference< css::frame::XFrame >& xFrame, const OUString& sCommandURL );
+ css::uno::Reference< css::frame::XStatusListener > findController( const css::uno::Reference< css::frame::XFrame >& xFrame, const OUString& sCommandURL );
+
+ void saveSearchHistory(const FindTextFieldControl* m_pFindTextFieldControl);
+ void loadSearchHistory(FindTextFieldControl* m_pFindTextFieldControl);
+
+private:
+
+ typedef ::std::vector< css::beans::PropertyValue > SearchToolbarControllersVec;
+ typedef ::std::map< css::uno::Reference< css::frame::XFrame >, SearchToolbarControllersVec > SearchToolbarControllersMap;
+ SearchToolbarControllersMap aSearchToolbarControllersMap;
+ std::vector<OUString> m_aSearchStrings;
+
+};
+
+SearchToolbarControllersManager::SearchToolbarControllersManager()
+{
+}
+
+SearchToolbarControllersManager& SearchToolbarControllersManager::createControllersManager()
+{
+ static SearchToolbarControllersManager theSearchToolbarControllersManager;
+ return theSearchToolbarControllersManager;
+}
+
+void SearchToolbarControllersManager::saveSearchHistory(const FindTextFieldControl* pFindTextFieldControl)
+{
+ const sal_Int32 nECount( pFindTextFieldControl->get_count() );
+ m_aSearchStrings.resize( nECount );
+ for( sal_Int32 i=0; i<nECount; ++i )
+ {
+ m_aSearchStrings[i] = pFindTextFieldControl->get_text(i);
+ }
+}
+
+void SearchToolbarControllersManager::loadSearchHistory(FindTextFieldControl* pFindTextFieldControl)
+{
+ for( size_t i=0; i<m_aSearchStrings.size(); ++i )
+ {
+ pFindTextFieldControl->append_text(m_aSearchStrings[i]);
+ }
+}
+
+void SearchToolbarControllersManager::registryController( const css::uno::Reference< css::frame::XFrame >& xFrame, const css::uno::Reference< css::frame::XStatusListener >& xStatusListener, const OUString& sCommandURL )
+{
+ SearchToolbarControllersMap::iterator pIt = aSearchToolbarControllersMap.find(xFrame);
+ if (pIt == aSearchToolbarControllersMap.end())
+ {
+ SearchToolbarControllersVec lControllers(1);
+ lControllers[0].Name = sCommandURL;
+ lControllers[0].Value <<= xStatusListener;
+ aSearchToolbarControllersMap.emplace(xFrame, lControllers);
+ }
+ else
+ {
+ sal_Int32 nSize = pIt->second.size();
+ for (sal_Int32 i=0; i<nSize; ++i)
+ {
+ if (pIt->second[i].Name == sCommandURL)
+ return;
+ }
+
+ pIt->second.resize(nSize+1);
+ pIt->second[nSize].Name = sCommandURL;
+ pIt->second[nSize].Value <<= xStatusListener;
+ }
+}
+
+void SearchToolbarControllersManager::freeController( const css::uno::Reference< css::frame::XFrame >& xFrame, const OUString& sCommandURL )
+{
+ SearchToolbarControllersMap::iterator pIt = aSearchToolbarControllersMap.find(xFrame);
+ if (pIt != aSearchToolbarControllersMap.end())
+ {
+ auto pItCtrl = std::find_if(pIt->second.begin(), pIt->second.end(),
+ [&sCommandURL](const css::beans::PropertyValue& rCtrl) { return rCtrl.Name == sCommandURL; });
+ if (pItCtrl != pIt->second.end())
+ pIt->second.erase(pItCtrl);
+
+ if (pIt->second.empty())
+ aSearchToolbarControllersMap.erase(pIt);
+ }
+}
+
+css::uno::Reference< css::frame::XStatusListener > SearchToolbarControllersManager::findController( const css::uno::Reference< css::frame::XFrame >& xFrame, const OUString& sCommandURL )
+{
+ css::uno::Reference< css::frame::XStatusListener > xStatusListener;
+
+ SearchToolbarControllersMap::iterator pIt = aSearchToolbarControllersMap.find(xFrame);
+ if (pIt != aSearchToolbarControllersMap.end())
+ {
+ auto pItCtrl = std::find_if(pIt->second.begin(), pIt->second.end(),
+ [&sCommandURL](const css::beans::PropertyValue& rCtrl) { return rCtrl.Name == sCommandURL; });
+ if (pItCtrl != pIt->second.end())
+ pItCtrl->Value >>= xStatusListener;
+ }
+
+ return xStatusListener;
+}
+
+class FindTextToolbarController : public svt::ToolboxController,
+ public css::lang::XServiceInfo
+{
+public:
+
+ FindTextToolbarController( const css::uno::Reference< css::uno::XComponentContext > & rxContext );
+
+ // XInterface
+ virtual css::uno::Any SAL_CALL queryInterface( const css::uno::Type& aType ) override;
+ virtual void SAL_CALL acquire() noexcept override;
+ virtual void SAL_CALL release() noexcept 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;
+
+ // XInitialization
+ virtual void SAL_CALL initialize( const css::uno::Sequence< css::uno::Any >& aArguments ) override;
+
+ // XToolbarController
+ virtual css::uno::Reference< css::awt::XWindow > SAL_CALL createItemWindow( const css::uno::Reference< css::awt::XWindow >& Parent ) override;
+
+ // XStatusListener
+ virtual void SAL_CALL statusChanged( const css::frame::FeatureStateEvent& Event ) override;
+
+ DECL_LINK(EditModifyHdl, weld::ComboBox&, void);
+
+private:
+
+ void textfieldChanged();
+
+ VclPtr<FindTextFieldControl> m_pFindTextFieldControl;
+
+ ToolBoxItemId m_nDownSearchId;
+ ToolBoxItemId m_nUpSearchId;
+ ToolBoxItemId m_nFindAllId;
+
+};
+
+FindTextToolbarController::FindTextToolbarController( const css::uno::Reference< css::uno::XComponentContext >& rxContext )
+ : svt::ToolboxController(rxContext, css::uno::Reference< css::frame::XFrame >(), COMMAND_FINDTEXT)
+ , m_pFindTextFieldControl(nullptr)
+ , m_nDownSearchId(0)
+ , m_nUpSearchId(0)
+ , m_nFindAllId(0)
+{
+}
+
+// XInterface
+css::uno::Any SAL_CALL FindTextToolbarController::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 FindTextToolbarController::acquire() noexcept
+{
+ ToolboxController::acquire();
+}
+
+void SAL_CALL FindTextToolbarController::release() noexcept
+{
+ ToolboxController::release();
+}
+
+// XServiceInfo
+OUString SAL_CALL FindTextToolbarController::getImplementationName()
+{
+ return "com.sun.star.svx.FindTextToolboxController";
+}
+
+sal_Bool SAL_CALL FindTextToolbarController::supportsService( const OUString& ServiceName )
+{
+ return cppu::supportsService(this, ServiceName);
+}
+
+css::uno::Sequence< OUString > SAL_CALL FindTextToolbarController::getSupportedServiceNames()
+{
+ return { "com.sun.star.frame.ToolbarController" };
+}
+
+// XComponent
+void SAL_CALL FindTextToolbarController::dispose()
+{
+ SolarMutexGuard aSolarMutexGuard;
+
+ SearchToolbarControllersManager::createControllersManager().freeController(m_xFrame, m_aCommandURL);
+
+ svt::ToolboxController::dispose();
+ if (m_pFindTextFieldControl != nullptr) {
+ SearchToolbarControllersManager::createControllersManager()
+ .saveSearchHistory(m_pFindTextFieldControl);
+ m_pFindTextFieldControl.disposeAndClear();
+ }
+}
+
+// XInitialization
+void SAL_CALL FindTextToolbarController::initialize( const css::uno::Sequence< css::uno::Any >& aArguments )
+{
+ svt::ToolboxController::initialize(aArguments);
+
+ VclPtr<vcl::Window> pWindow = VCLUnoHelper::GetWindow( getParent() );
+ ToolBox* pToolBox = static_cast<ToolBox*>(pWindow.get());
+ if ( pToolBox )
+ {
+ m_nDownSearchId = pToolBox->GetItemId(COMMAND_DOWNSEARCH);
+ m_nUpSearchId = pToolBox->GetItemId(COMMAND_UPSEARCH);
+ m_nFindAllId = pToolBox->GetItemId(COMMAND_FINDALL);
+ }
+
+ SearchToolbarControllersManager::createControllersManager().registryController(m_xFrame, css::uno::Reference< css::frame::XStatusListener >(this), m_aCommandURL);
+}
+
+css::uno::Reference< css::awt::XWindow > SAL_CALL FindTextToolbarController::createItemWindow( const css::uno::Reference< css::awt::XWindow >& xParent )
+{
+ css::uno::Reference< css::awt::XWindow > xItemWindow;
+
+ VclPtr<vcl::Window> pParent = VCLUnoHelper::GetWindow( xParent );
+ if ( pParent )
+ {
+ ToolBox* pToolbar = static_cast<ToolBox*>(pParent.get());
+ m_pFindTextFieldControl = VclPtr<FindTextFieldControl>::Create(pToolbar, m_xFrame, m_xContext);
+
+ m_pFindTextFieldControl->connect_changed(LINK(this, FindTextToolbarController, EditModifyHdl));
+ SearchToolbarControllersManager::createControllersManager().loadSearchHistory(m_pFindTextFieldControl);
+ }
+ xItemWindow = VCLUnoHelper::GetInterface( m_pFindTextFieldControl );
+
+ return xItemWindow;
+}
+
+// XStatusListener
+void SAL_CALL FindTextToolbarController::statusChanged( const css::frame::FeatureStateEvent& rEvent )
+{
+ SolarMutexGuard aSolarMutexGuard;
+ if ( m_bDisposed )
+ return;
+
+ OUString aFeatureURL = rEvent.FeatureURL.Complete;
+ if ( aFeatureURL == "AppendSearchHistory" )
+ {
+ m_pFindTextFieldControl->Remember_Impl(m_pFindTextFieldControl->get_active_text());
+ }
+ // enable up/down buttons in case there is already text (from the search history)
+ textfieldChanged();
+}
+
+IMPL_LINK_NOARG(FindTextToolbarController, EditModifyHdl, weld::ComboBox&, void)
+{
+ // Clear SearchLabel when search string altered
+ #if HAVE_FEATURE_DESKTOP
+ SvxSearchDialogWrapper::SetSearchLabel(SearchLabel::Empty);
+ #endif
+
+ textfieldChanged();
+}
+
+void FindTextToolbarController::textfieldChanged() {
+ // enable or disable item DownSearch/UpSearch/FindAll of findbar
+ VclPtr<vcl::Window> pWindow = VCLUnoHelper::GetWindow( getParent() );
+ ToolBox* pToolBox = static_cast<ToolBox*>(pWindow.get());
+ if ( pToolBox && m_pFindTextFieldControl )
+ {
+ bool enableButtons = !m_pFindTextFieldControl->get_active_text().isEmpty();
+ pToolBox->EnableItem(m_nDownSearchId, enableButtons);
+ pToolBox->EnableItem(m_nUpSearchId, enableButtons);
+ pToolBox->EnableItem(m_nFindAllId, enableButtons);
+ }
+}
+
+class UpDownSearchToolboxController : public svt::ToolboxController,
+ public css::lang::XServiceInfo
+{
+public:
+ enum Type { UP, DOWN };
+
+ UpDownSearchToolboxController( 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() noexcept override;
+ virtual void SAL_CALL release() noexcept 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;
+
+ // XInitialization
+ virtual void SAL_CALL initialize( const css::uno::Sequence< css::uno::Any >& aArguments ) override;
+
+ // XToolbarController
+ virtual void SAL_CALL execute( sal_Int16 KeyModifier ) override;
+
+ // XStatusListener
+ virtual void SAL_CALL statusChanged( const css::frame::FeatureStateEvent& rEvent ) override;
+
+private:
+ Type meType;
+};
+
+UpDownSearchToolboxController::UpDownSearchToolboxController( const css::uno::Reference< css::uno::XComponentContext > & rxContext, Type eType )
+ : svt::ToolboxController( rxContext,
+ css::uno::Reference< css::frame::XFrame >(),
+ (eType == UP) ? OUString( COMMAND_UPSEARCH ): OUString( COMMAND_DOWNSEARCH ) ),
+ meType( eType )
+{
+}
+
+// XInterface
+css::uno::Any SAL_CALL UpDownSearchToolboxController::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 UpDownSearchToolboxController::acquire() noexcept
+{
+ ToolboxController::acquire();
+}
+
+void SAL_CALL UpDownSearchToolboxController::release() noexcept
+{
+ ToolboxController::release();
+}
+
+// XServiceInfo
+OUString SAL_CALL UpDownSearchToolboxController::getImplementationName()
+{
+ return meType == UpDownSearchToolboxController::UP?
+ OUString( "com.sun.star.svx.UpSearchToolboxController" ) :
+ OUString( "com.sun.star.svx.DownSearchToolboxController" );
+}
+
+sal_Bool SAL_CALL UpDownSearchToolboxController::supportsService( const OUString& ServiceName )
+{
+ return cppu::supportsService(this, ServiceName);
+}
+
+css::uno::Sequence< OUString > SAL_CALL UpDownSearchToolboxController::getSupportedServiceNames()
+{
+ return { "com.sun.star.frame.ToolbarController" };
+}
+
+// XComponent
+void SAL_CALL UpDownSearchToolboxController::dispose()
+{
+ SolarMutexGuard aSolarMutexGuard;
+
+ SearchToolbarControllersManager::createControllersManager().freeController(m_xFrame, m_aCommandURL);
+
+ svt::ToolboxController::dispose();
+}
+
+// XInitialization
+void SAL_CALL UpDownSearchToolboxController::initialize( const css::uno::Sequence< css::uno::Any >& aArguments )
+{
+ svt::ToolboxController::initialize( aArguments );
+ SearchToolbarControllersManager::createControllersManager().registryController(m_xFrame, css::uno::Reference< css::frame::XStatusListener >(this), m_aCommandURL);
+}
+
+// XToolbarController
+void SAL_CALL UpDownSearchToolboxController::execute( sal_Int16 /*KeyModifier*/ )
+{
+ if ( m_bDisposed )
+ throw css::lang::DisposedException();
+
+ VclPtr<vcl::Window> pWindow = VCLUnoHelper::GetWindow( getParent() );
+ ToolBox* pToolBox = static_cast<ToolBox*>(pWindow.get());
+
+ impl_executeSearch(m_xContext, m_xFrame, pToolBox, meType == UP );
+
+ css::frame::FeatureStateEvent aEvent;
+ aEvent.FeatureURL.Complete = "AppendSearchHistory";
+ css::uno::Reference< css::frame::XStatusListener > xStatusListener = SearchToolbarControllersManager::createControllersManager().findController(m_xFrame, COMMAND_FINDTEXT);
+ if (xStatusListener.is())
+ xStatusListener->statusChanged( aEvent );
+}
+
+// XStatusListener
+void SAL_CALL UpDownSearchToolboxController::statusChanged( const css::frame::FeatureStateEvent& /*rEvent*/ )
+{
+}
+
+class MatchCaseToolboxController : public svt::ToolboxController,
+ public css::lang::XServiceInfo
+{
+public:
+ MatchCaseToolboxController( const css::uno::Reference< css::uno::XComponentContext >& rxContext );
+
+ // XInterface
+ virtual css::uno::Any SAL_CALL queryInterface( const css::uno::Type& aType ) override;
+ virtual void SAL_CALL acquire() noexcept override;
+ virtual void SAL_CALL release() noexcept 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;
+
+ // XInitialization
+ virtual void SAL_CALL initialize( const css::uno::Sequence< css::uno::Any >& aArguments ) override;
+
+ // XToolbarController
+ virtual css::uno::Reference< css::awt::XWindow > SAL_CALL createItemWindow( const css::uno::Reference< css::awt::XWindow >& Parent ) override;
+
+ // XStatusListener
+ virtual void SAL_CALL statusChanged( const css::frame::FeatureStateEvent& rEvent ) override;
+
+private:
+ VclPtr<CheckButtonItemWindow> m_xMatchCaseControl;
+};
+
+MatchCaseToolboxController::MatchCaseToolboxController( const css::uno::Reference< css::uno::XComponentContext >& rxContext )
+ : svt::ToolboxController( rxContext,
+ css::uno::Reference< css::frame::XFrame >(),
+ COMMAND_MATCHCASE )
+ , m_xMatchCaseControl(nullptr)
+{
+}
+
+// XInterface
+css::uno::Any SAL_CALL MatchCaseToolboxController::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 MatchCaseToolboxController::acquire() noexcept
+{
+ ToolboxController::acquire();
+}
+
+void SAL_CALL MatchCaseToolboxController::release() noexcept
+{
+ ToolboxController::release();
+}
+
+// XServiceInfo
+OUString SAL_CALL MatchCaseToolboxController::getImplementationName()
+{
+ return "com.sun.star.svx.MatchCaseToolboxController";
+}
+
+sal_Bool SAL_CALL MatchCaseToolboxController::supportsService( const OUString& ServiceName )
+{
+ return cppu::supportsService(this, ServiceName);
+}
+
+css::uno::Sequence< OUString > SAL_CALL MatchCaseToolboxController::getSupportedServiceNames()
+{
+ return { "com.sun.star.frame.ToolbarController" };
+}
+
+// XComponent
+void SAL_CALL MatchCaseToolboxController::dispose()
+{
+ SolarMutexGuard aSolarMutexGuard;
+
+ SearchToolbarControllersManager::createControllersManager().freeController(m_xFrame, m_aCommandURL);
+
+ svt::ToolboxController::dispose();
+
+ m_xMatchCaseControl.disposeAndClear();
+}
+
+// XInitialization
+void SAL_CALL MatchCaseToolboxController::initialize( const css::uno::Sequence< css::uno::Any >& aArguments )
+{
+ svt::ToolboxController::initialize(aArguments);
+
+ SearchToolbarControllersManager::createControllersManager().registryController(m_xFrame, css::uno::Reference< css::frame::XStatusListener >(this), m_aCommandURL);
+}
+
+css::uno::Reference< css::awt::XWindow > SAL_CALL MatchCaseToolboxController::createItemWindow( const css::uno::Reference< css::awt::XWindow >& xParent )
+{
+ css::uno::Reference< css::awt::XWindow > xItemWindow;
+
+ VclPtr<vcl::Window> pParent = VCLUnoHelper::GetWindow( xParent );
+ if ( pParent )
+ {
+ ToolBox* pToolbar = static_cast<ToolBox*>(pParent.get());
+ m_xMatchCaseControl = VclPtr<CheckButtonItemWindow>::Create(pToolbar, SvxResId(RID_SVXSTR_FINDBAR_MATCHCASE));
+ }
+ xItemWindow = VCLUnoHelper::GetInterface(m_xMatchCaseControl);
+
+ return xItemWindow;
+}
+
+// XStatusListener
+void SAL_CALL MatchCaseToolboxController::statusChanged( const css::frame::FeatureStateEvent& )
+{
+}
+
+class SearchFormattedToolboxController : public svt::ToolboxController,
+ public css::lang::XServiceInfo
+{
+public:
+ SearchFormattedToolboxController( const css::uno::Reference< css::uno::XComponentContext >& rxContext );
+
+ // XInterface
+ virtual css::uno::Any SAL_CALL queryInterface( const css::uno::Type& aType ) override;
+ virtual void SAL_CALL acquire() noexcept override;
+ virtual void SAL_CALL release() noexcept 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;
+
+ // XInitialization
+ virtual void SAL_CALL initialize( const css::uno::Sequence< css::uno::Any >& aArguments ) override;
+
+ // XToolbarController
+ virtual css::uno::Reference< css::awt::XWindow > SAL_CALL createItemWindow( const css::uno::Reference< css::awt::XWindow >& Parent ) override;
+
+ // XStatusListener
+ virtual void SAL_CALL statusChanged( const css::frame::FeatureStateEvent& rEvent ) override;
+
+private:
+ VclPtr<CheckButtonItemWindow> m_xSearchFormattedControl;
+};
+
+SearchFormattedToolboxController::SearchFormattedToolboxController( const css::uno::Reference< css::uno::XComponentContext >& rxContext )
+ : svt::ToolboxController( rxContext,
+ css::uno::Reference< css::frame::XFrame >(),
+ COMMAND_SEARCHFORMATTED )
+ , m_xSearchFormattedControl(nullptr)
+{
+}
+
+// XInterface
+css::uno::Any SAL_CALL SearchFormattedToolboxController::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 SearchFormattedToolboxController::acquire() noexcept
+{
+ ToolboxController::acquire();
+}
+
+void SAL_CALL SearchFormattedToolboxController::release() noexcept
+{
+ ToolboxController::release();
+}
+
+// XServiceInfo
+OUString SAL_CALL SearchFormattedToolboxController::getImplementationName()
+{
+ return "com.sun.star.svx.SearchFormattedToolboxController";
+}
+
+sal_Bool SAL_CALL SearchFormattedToolboxController::supportsService( const OUString& ServiceName )
+{
+ return cppu::supportsService(this, ServiceName);
+}
+
+css::uno::Sequence< OUString > SAL_CALL SearchFormattedToolboxController::getSupportedServiceNames()
+{
+ return { "com.sun.star.frame.ToolbarController" };
+}
+
+// XComponent
+void SAL_CALL SearchFormattedToolboxController::dispose()
+{
+ SolarMutexGuard aSolarMutexGuard;
+
+ SearchToolbarControllersManager::createControllersManager().freeController(m_xFrame, m_aCommandURL);
+
+ svt::ToolboxController::dispose();
+
+ m_xSearchFormattedControl.disposeAndClear();
+}
+
+// XInitialization
+void SAL_CALL SearchFormattedToolboxController::initialize( const css::uno::Sequence< css::uno::Any >& aArguments )
+{
+ svt::ToolboxController::initialize(aArguments);
+
+ SearchToolbarControllersManager::createControllersManager().registryController(m_xFrame, css::uno::Reference< css::frame::XStatusListener >(this), m_aCommandURL);
+}
+
+css::uno::Reference< css::awt::XWindow > SAL_CALL SearchFormattedToolboxController::createItemWindow( const css::uno::Reference< css::awt::XWindow >& xParent )
+{
+ css::uno::Reference< css::awt::XWindow > xItemWindow;
+
+ VclPtr<vcl::Window> pParent = VCLUnoHelper::GetWindow( xParent );
+ if ( pParent )
+ {
+ ToolBox* pToolbar = static_cast<ToolBox*>(pParent.get());
+ m_xSearchFormattedControl = VclPtr<CheckButtonItemWindow>::Create(pToolbar, SvxResId(RID_SVXSTR_FINDBAR_SEARCHFORMATTED));
+ }
+ xItemWindow = VCLUnoHelper::GetInterface(m_xSearchFormattedControl);
+
+ return xItemWindow;
+}
+
+// XStatusListener
+void SAL_CALL SearchFormattedToolboxController::statusChanged( const css::frame::FeatureStateEvent& )
+{
+}
+
+class FindAllToolboxController : public svt::ToolboxController,
+ public css::lang::XServiceInfo
+{
+public:
+ FindAllToolboxController( const css::uno::Reference< css::uno::XComponentContext >& rxContext );
+
+ // XInterface
+ virtual css::uno::Any SAL_CALL queryInterface( const css::uno::Type& aType ) override;
+ virtual void SAL_CALL acquire() noexcept override;
+ virtual void SAL_CALL release() noexcept 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;
+
+ // XInitialization
+ virtual void SAL_CALL initialize( const css::uno::Sequence< css::uno::Any >& aArguments ) override;
+
+ // XToolbarController
+ virtual void SAL_CALL execute( sal_Int16 KeyModifier ) override;
+
+ // XStatusListener
+ virtual void SAL_CALL statusChanged( const css::frame::FeatureStateEvent& rEvent ) override;
+};
+
+FindAllToolboxController::FindAllToolboxController( const css::uno::Reference< css::uno::XComponentContext > & rxContext )
+ : svt::ToolboxController( rxContext,
+ css::uno::Reference< css::frame::XFrame >(),
+ ".uno:FindAll" )
+{
+}
+
+// XInterface
+css::uno::Any SAL_CALL FindAllToolboxController::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 FindAllToolboxController::acquire() noexcept
+{
+ ToolboxController::acquire();
+}
+
+void SAL_CALL FindAllToolboxController::release() noexcept
+{
+ ToolboxController::release();
+}
+
+// XServiceInfo
+OUString SAL_CALL FindAllToolboxController::getImplementationName()
+{
+ return "com.sun.star.svx.FindAllToolboxController";
+}
+
+
+sal_Bool SAL_CALL FindAllToolboxController::supportsService( const OUString& ServiceName )
+{
+ return cppu::supportsService(this, ServiceName);
+}
+
+css::uno::Sequence< OUString > SAL_CALL FindAllToolboxController::getSupportedServiceNames()
+{
+ return { "com.sun.star.frame.ToolbarController" };
+}
+
+// XComponent
+void SAL_CALL FindAllToolboxController::dispose()
+{
+ SolarMutexGuard aSolarMutexGuard;
+
+ SearchToolbarControllersManager::createControllersManager().freeController(m_xFrame, m_aCommandURL);
+
+ svt::ToolboxController::dispose();
+}
+
+// XInitialization
+void SAL_CALL FindAllToolboxController::initialize( const css::uno::Sequence< css::uno::Any >& aArguments )
+{
+ svt::ToolboxController::initialize( aArguments );
+ SearchToolbarControllersManager::createControllersManager().registryController(m_xFrame, css::uno::Reference< css::frame::XStatusListener >(this), m_aCommandURL);
+}
+
+// XToolbarController
+void SAL_CALL FindAllToolboxController::execute( sal_Int16 /*KeyModifier*/ )
+{
+ if ( m_bDisposed )
+ throw css::lang::DisposedException();
+
+ VclPtr<vcl::Window> pWindow = VCLUnoHelper::GetWindow( getParent() );
+ ToolBox* pToolBox = static_cast<ToolBox*>(pWindow.get());
+
+ impl_executeSearch(m_xContext, m_xFrame, pToolBox, false, true);
+}
+
+// XStatusListener
+void SAL_CALL FindAllToolboxController::statusChanged( const css::frame::FeatureStateEvent& /*rEvent*/ )
+{
+}
+
+class ExitSearchToolboxController : public svt::ToolboxController,
+ public css::lang::XServiceInfo
+{
+public:
+ ExitSearchToolboxController( const css::uno::Reference< css::uno::XComponentContext >& rxContext );
+
+ // XInterface
+ virtual css::uno::Any SAL_CALL queryInterface( const css::uno::Type& aType ) override;
+ virtual void SAL_CALL acquire() noexcept override;
+ virtual void SAL_CALL release() noexcept 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;
+
+ // XInitialization
+ virtual void SAL_CALL initialize( const css::uno::Sequence< css::uno::Any >& aArguments ) override;
+
+ // XToolbarController
+ virtual void SAL_CALL execute( sal_Int16 KeyModifier ) override;
+
+ // XStatusListener
+ virtual void SAL_CALL statusChanged( const css::frame::FeatureStateEvent& rEvent ) override;
+};
+
+ExitSearchToolboxController::ExitSearchToolboxController( const css::uno::Reference< css::uno::XComponentContext > & rxContext )
+ : svt::ToolboxController( rxContext,
+ css::uno::Reference< css::frame::XFrame >(),
+ ".uno:ExitSearch" )
+{
+}
+
+// XInterface
+css::uno::Any SAL_CALL ExitSearchToolboxController::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 ExitSearchToolboxController::acquire() noexcept
+{
+ ToolboxController::acquire();
+}
+
+void SAL_CALL ExitSearchToolboxController::release() noexcept
+{
+ ToolboxController::release();
+}
+
+// XServiceInfo
+OUString SAL_CALL ExitSearchToolboxController::getImplementationName()
+{
+ return "com.sun.star.svx.ExitFindbarToolboxController";
+}
+
+
+sal_Bool SAL_CALL ExitSearchToolboxController::supportsService( const OUString& ServiceName )
+{
+ return cppu::supportsService(this, ServiceName);
+}
+
+css::uno::Sequence< OUString > SAL_CALL ExitSearchToolboxController::getSupportedServiceNames()
+{
+ return { "com.sun.star.frame.ToolbarController" };
+}
+
+// XComponent
+void SAL_CALL ExitSearchToolboxController::dispose()
+{
+ SolarMutexGuard aSolarMutexGuard;
+
+ SearchToolbarControllersManager::createControllersManager().freeController(m_xFrame, m_aCommandURL);
+
+ svt::ToolboxController::dispose();
+}
+
+// XInitialization
+void SAL_CALL ExitSearchToolboxController::initialize( const css::uno::Sequence< css::uno::Any >& aArguments )
+{
+ svt::ToolboxController::initialize( aArguments );
+ SearchToolbarControllersManager::createControllersManager().registryController(m_xFrame, css::uno::Reference< css::frame::XStatusListener >(this), m_aCommandURL);
+}
+
+// XToolbarController
+void SAL_CALL ExitSearchToolboxController::execute( sal_Int16 /*KeyModifier*/ )
+{
+ vcl::Window *pFocusWindow = Application::GetFocusWindow();
+ if ( pFocusWindow )
+ pFocusWindow->GrabFocusToDocument();
+
+ // hide the findbar
+ css::uno::Reference< css::beans::XPropertySet > xPropSet(m_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())
+ {
+ static const OUStringLiteral sResourceURL( u"private:resource/toolbar/findbar" );
+ xLayoutManager->hideElement( sResourceURL );
+ xLayoutManager->destroyElement( sResourceURL );
+ }
+ }
+}
+
+// XStatusListener
+void SAL_CALL ExitSearchToolboxController::statusChanged( const css::frame::FeatureStateEvent& /*rEvent*/ )
+{
+}
+
+class SearchLabelToolboxController : public svt::ToolboxController,
+ public css::lang::XServiceInfo
+{
+public:
+ SearchLabelToolboxController( const css::uno::Reference< css::uno::XComponentContext >& rxContext );
+
+ // XInterface
+ virtual css::uno::Any SAL_CALL queryInterface( const css::uno::Type& aType ) override;
+ virtual void SAL_CALL acquire() noexcept override;
+ virtual void SAL_CALL release() noexcept 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;
+
+ // XInitialization
+ virtual void SAL_CALL initialize( const css::uno::Sequence< css::uno::Any >& aArguments ) override;
+
+ // XToolbarController
+ virtual css::uno::Reference< css::awt::XWindow > SAL_CALL createItemWindow( const css::uno::Reference< css::awt::XWindow >& Parent ) override;
+
+ // XStatusListener
+ virtual void SAL_CALL statusChanged( const css::frame::FeatureStateEvent& rEvent ) override;
+
+private:
+ VclPtr<LabelItemWindow> m_xSL;
+};
+
+SearchLabelToolboxController::SearchLabelToolboxController( const css::uno::Reference< css::uno::XComponentContext > & rxContext )
+ : svt::ToolboxController( rxContext,
+ css::uno::Reference< css::frame::XFrame >(),
+ ".uno:SearchLabel" )
+{
+}
+
+// XInterface
+css::uno::Any SAL_CALL SearchLabelToolboxController::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 SearchLabelToolboxController::acquire() noexcept
+{
+ ToolboxController::acquire();
+}
+
+void SAL_CALL SearchLabelToolboxController::release() noexcept
+{
+ ToolboxController::release();
+}
+
+// XServiceInfo
+OUString SAL_CALL SearchLabelToolboxController::getImplementationName()
+{
+ return "com.sun.star.svx.SearchLabelToolboxController";
+}
+
+
+sal_Bool SAL_CALL SearchLabelToolboxController::supportsService( const OUString& ServiceName )
+{
+ return cppu::supportsService(this, ServiceName);
+}
+
+css::uno::Sequence< OUString > SAL_CALL SearchLabelToolboxController::getSupportedServiceNames()
+{
+ return { "com.sun.star.frame.ToolbarController" };
+}
+
+// XComponent
+void SAL_CALL SearchLabelToolboxController::dispose()
+{
+ SolarMutexGuard aSolarMutexGuard;
+
+ SearchToolbarControllersManager::createControllersManager().freeController(m_xFrame, m_aCommandURL);
+
+ svt::ToolboxController::dispose();
+ m_xSL.disposeAndClear();
+}
+
+// XInitialization
+void SAL_CALL SearchLabelToolboxController::initialize( const css::uno::Sequence< css::uno::Any >& aArguments )
+{
+ svt::ToolboxController::initialize( aArguments );
+ SearchToolbarControllersManager::createControllersManager().registryController(m_xFrame, css::uno::Reference< css::frame::XStatusListener >(this), m_aCommandURL);
+}
+
+// XStatusListener
+void SAL_CALL SearchLabelToolboxController::statusChanged( const css::frame::FeatureStateEvent& )
+{
+ if (m_xSL)
+ {
+ OUString aStr = SvxSearchDialogWrapper::GetSearchLabel();
+ m_xSL->set_label(aStr);
+ m_xSL->SetOptimalSize();
+ Size aSize(m_xSL->GetSizePixel());
+ tools::Long nWidth = !aStr.isEmpty() ? aSize.getWidth() : 16;
+ m_xSL->SetSizePixel(Size(nWidth, aSize.Height()));
+ }
+}
+
+css::uno::Reference< css::awt::XWindow > SAL_CALL SearchLabelToolboxController::createItemWindow( const css::uno::Reference< css::awt::XWindow >& Parent )
+{
+ ToolBox* pToolBox = nullptr;
+ ToolBoxItemId nId;
+ if (getToolboxId(nId, &pToolBox))
+ pToolBox->SetItemWindowNonInteractive(nId, true);
+
+ m_xSL = VclPtr<LabelItemWindow>::Create(VCLUnoHelper::GetWindow(Parent), "");
+ m_xSL->SetSizePixel(Size(16, m_xSL->GetSizePixel().Height()));
+ return VCLUnoHelper::GetInterface(m_xSL);
+}
+
+// protocol handler for "vnd.sun.star.findbar:*" URLs
+// The dispatch object will be used for shortcut commands for findbar
+class FindbarDispatcher : public css::lang::XServiceInfo,
+ public css::lang::XInitialization,
+ public css::frame::XDispatchProvider,
+ public css::frame::XDispatch,
+ public ::cppu::OWeakObject
+{
+public:
+
+ FindbarDispatcher();
+ virtual ~FindbarDispatcher() override;
+
+ // XInterface
+ virtual css::uno::Any SAL_CALL queryInterface( const css::uno::Type& aType ) override;
+ virtual void SAL_CALL acquire() noexcept override;
+ virtual void SAL_CALL release() noexcept 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;
+
+ // XInitialization
+ virtual void SAL_CALL initialize( const css::uno::Sequence< css::uno::Any >& aArguments ) override;
+
+ // XDispatchProvider
+ virtual css::uno::Reference< css::frame::XDispatch > SAL_CALL queryDispatch( const css::util::URL& aURL, const OUString& sTargetFrameName , sal_Int32 nSearchFlags ) override;
+ virtual css::uno::Sequence< css::uno::Reference< css::frame::XDispatch > > SAL_CALL queryDispatches( const css::uno::Sequence< css::frame::DispatchDescriptor >& lDescriptions ) override;
+
+ // XDispatch
+ virtual void SAL_CALL dispatch( const css::util::URL& aURL, const css::uno::Sequence< css::beans::PropertyValue >& lArguments ) override;
+ virtual void SAL_CALL addStatusListener( const css::uno::Reference< css::frame::XStatusListener >& xListener, const css::util::URL& aURL ) override;
+ virtual void SAL_CALL removeStatusListener( const css::uno::Reference< css::frame::XStatusListener >& xListener, const css::util::URL& aURL ) override;
+
+private:
+
+ css::uno::Reference< css::frame::XFrame > m_xFrame;
+
+};
+
+FindbarDispatcher::FindbarDispatcher()
+{
+}
+
+FindbarDispatcher::~FindbarDispatcher()
+{
+ m_xFrame = nullptr;
+}
+
+// XInterface
+css::uno::Any SAL_CALL FindbarDispatcher::queryInterface( const css::uno::Type& aType )
+{
+ css::uno::Any aReturn( ::cppu::queryInterface( aType,
+ static_cast< css::lang::XServiceInfo* >(this),
+ static_cast< css::lang::XInitialization* >(this),
+ static_cast< css::frame::XDispatchProvider* >(this),
+ static_cast< css::frame::XDispatch* >(this)) );
+
+ if ( aReturn.hasValue() )
+ return aReturn;
+
+ return OWeakObject::queryInterface( aType );
+}
+
+void SAL_CALL FindbarDispatcher::acquire() noexcept
+{
+ OWeakObject::acquire();
+}
+
+void SAL_CALL FindbarDispatcher::release() noexcept
+{
+ OWeakObject::release();
+}
+
+// XServiceInfo
+OUString SAL_CALL FindbarDispatcher::getImplementationName()
+{
+ return "com.sun.star.comp.svx.Impl.FindbarDispatcher";
+}
+
+sal_Bool SAL_CALL FindbarDispatcher::supportsService( const OUString& ServiceName )
+{
+ return cppu::supportsService(this, ServiceName);
+}
+
+css::uno::Sequence< OUString > SAL_CALL FindbarDispatcher::getSupportedServiceNames()
+{
+ return { "com.sun.star.comp.svx.FindbarDispatcher", "com.sun.star.frame.ProtocolHandler" };
+}
+
+// XInitialization
+void SAL_CALL FindbarDispatcher::initialize( const css::uno::Sequence< css::uno::Any >& aArguments )
+{
+ if ( aArguments.hasElements() )
+ aArguments[0] >>= m_xFrame;
+}
+
+// XDispatchProvider
+css::uno::Reference< css::frame::XDispatch > SAL_CALL FindbarDispatcher::queryDispatch( const css::util::URL& aURL, const OUString& /*sTargetFrameName*/, sal_Int32 /*nSearchFlags*/ )
+{
+ css::uno::Reference< css::frame::XDispatch > xDispatch;
+
+ if ( aURL.Protocol == "vnd.sun.star.findbar:" )
+ xDispatch = this;
+
+ return xDispatch;
+}
+
+css::uno::Sequence < css::uno::Reference< css::frame::XDispatch > > SAL_CALL FindbarDispatcher::queryDispatches( const css::uno::Sequence < css::frame::DispatchDescriptor >& seqDescripts )
+{
+ sal_Int32 nCount = seqDescripts.getLength();
+ css::uno::Sequence < css::uno::Reference < XDispatch > > lDispatcher( nCount );
+
+ std::transform(seqDescripts.begin(), seqDescripts.end(), lDispatcher.getArray(),
+ [this](const css::frame::DispatchDescriptor& rDescript) -> css::uno::Reference < XDispatch > {
+ return queryDispatch( rDescript.FeatureURL, rDescript.FrameName, rDescript.SearchFlags ); });
+
+ return lDispatcher;
+}
+
+// XDispatch
+void SAL_CALL FindbarDispatcher::dispatch( const css::util::URL& aURL, const css::uno::Sequence < css::beans::PropertyValue >& /*lArgs*/ )
+{
+ //vnd.sun.star.findbar:FocusToFindbar - set cursor to the FindTextFieldControl of the findbar
+ if ( aURL.Path != "FocusToFindbar" )
+ return;
+
+ css::uno::Reference< css::beans::XPropertySet > xPropSet(m_xFrame, css::uno::UNO_QUERY);
+ if(!xPropSet.is())
+ return;
+
+ css::uno::Reference< css::frame::XLayoutManager > xLayoutManager;
+ css::uno::Any aValue = xPropSet->getPropertyValue("LayoutManager");
+ aValue >>= xLayoutManager;
+ if (!xLayoutManager.is())
+ return;
+
+ static const OUStringLiteral sResourceURL( u"private:resource/toolbar/findbar" );
+ css::uno::Reference< css::ui::XUIElement > xUIElement = xLayoutManager->getElement(sResourceURL);
+ if (!xUIElement.is())
+ {
+ // show the findbar if necessary
+ xLayoutManager->createElement( sResourceURL );
+ xLayoutManager->showElement( sResourceURL );
+ xUIElement = xLayoutManager->getElement( sResourceURL );
+ if ( !xUIElement.is() )
+ return;
+ }
+
+ css::uno::Reference< css::awt::XWindow > xWindow(xUIElement->getRealInterface(), css::uno::UNO_QUERY);
+ VclPtr<vcl::Window> pWindow = VCLUnoHelper::GetWindow( xWindow );
+ ToolBox* pToolBox = static_cast<ToolBox*>(pWindow.get());
+ pToolBox->set_id("FindBar");
+ if ( !pToolBox )
+ return;
+
+ ToolBox::ImplToolItems::size_type nItemCount = pToolBox->GetItemCount();
+ for ( ToolBox::ImplToolItems::size_type i=0; i<nItemCount; ++i )
+ {
+ ToolBoxItemId id = pToolBox->GetItemId(i);
+ OUString sItemCommand = pToolBox->GetItemCommand(id);
+ if ( sItemCommand == COMMAND_FINDTEXT )
+ {
+ vcl::Window* pItemWin = pToolBox->GetItemWindow( id );
+ if ( pItemWin )
+ {
+ SolarMutexGuard aSolarMutexGuard;
+ FindTextFieldControl* pFindTextFieldControl = dynamic_cast<FindTextFieldControl*>(pItemWin);
+ if ( pFindTextFieldControl )
+ pFindTextFieldControl->SetTextToSelected_Impl();
+ pItemWin->GrabFocus();
+ return;
+ }
+ }
+ }
+}
+
+void SAL_CALL FindbarDispatcher::addStatusListener( const css::uno::Reference< css::frame::XStatusListener >& /*xControl*/, const css::util::URL& /*aURL*/ )
+{
+}
+
+void SAL_CALL FindbarDispatcher::removeStatusListener( const css::uno::Reference< css::frame::XStatusListener >& /*xControl*/, const css::util::URL& /*aURL*/ )
+{
+}
+
+}
+
+extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface *
+com_sun_star_svx_FindTextToolboxController_get_implementation(
+ css::uno::XComponentContext *context,
+ css::uno::Sequence<css::uno::Any> const &)
+{
+ return cppu::acquire(new FindTextToolbarController(context));
+}
+
+extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface *
+com_sun_star_svx_ExitFindbarToolboxController_get_implementation(
+ css::uno::XComponentContext *context,
+ css::uno::Sequence<css::uno::Any> const &)
+{
+ return cppu::acquire(new ExitSearchToolboxController(context));
+}
+
+extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface *
+com_sun_star_svx_UpSearchToolboxController_get_implementation(
+ css::uno::XComponentContext *context,
+ css::uno::Sequence<css::uno::Any> const &)
+{
+ return cppu::acquire(new UpDownSearchToolboxController(context, UpDownSearchToolboxController::UP));
+}
+
+extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface *
+com_sun_star_svx_DownSearchToolboxController_get_implementation(
+ css::uno::XComponentContext *context,
+ css::uno::Sequence<css::uno::Any> const &)
+{
+ return cppu::acquire(new UpDownSearchToolboxController(context, UpDownSearchToolboxController::DOWN));
+}
+
+extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface *
+com_sun_star_svx_MatchCaseToolboxController_get_implementation(
+ css::uno::XComponentContext *context,
+ css::uno::Sequence<css::uno::Any> const &)
+{
+ return cppu::acquire(new MatchCaseToolboxController(context));
+}
+
+extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface *
+com_sun_star_svx_SearchFormattedToolboxController_get_implementation(
+ css::uno::XComponentContext *context,
+ css::uno::Sequence<css::uno::Any> const &)
+{
+ return cppu::acquire(new SearchFormattedToolboxController(context));
+}
+
+extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface *
+com_sun_star_svx_FindAllToolboxController_get_implementation(
+ css::uno::XComponentContext *context,
+ css::uno::Sequence<css::uno::Any> const &)
+{
+ return cppu::acquire(new FindAllToolboxController(context));
+}
+
+extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface *
+com_sun_star_svx_SearchLabelToolboxController_get_implementation(
+ css::uno::XComponentContext *context,
+ css::uno::Sequence<css::uno::Any> const &)
+{
+ return cppu::acquire(new SearchLabelToolboxController(context));
+}
+
+extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface *
+com_sun_star_comp_svx_Impl_FindbarDispatcher_get_implementation(
+ SAL_UNUSED_PARAMETER css::uno::XComponentContext *,
+ css::uno::Sequence<css::uno::Any> const &)
+{
+ return cppu::acquire(new FindbarDispatcher);
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/tbxctrls/tbxcolor.cxx b/svx/source/tbxctrls/tbxcolor.cxx
new file mode 100644
index 000000000..f3dbbe096
--- /dev/null
+++ b/svx/source/tbxctrls/tbxcolor.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 <svx/tbxcolor.hxx>
+#include <sfx2/viewfrm.hxx>
+#include <com/sun/star/beans/XPropertySet.hpp>
+#include <osl/diagnose.h>
+#include <tools/diagnose_ex.h>
+
+
+namespace svx
+{
+
+
+ using namespace ::com::sun::star::uno;
+ using namespace ::com::sun::star::frame;
+ using namespace ::com::sun::star::beans;
+
+ ToolboxAccess::ToolboxAccess( std::u16string_view rToolboxName ) :
+ m_sToolboxResName ( "private:resource/toolbar/" )
+ {
+ m_sToolboxResName += rToolboxName;
+
+ // the layout manager
+ if ( !SfxViewFrame::Current() )
+ return;
+
+ try
+ {
+ Reference< XFrame > xFrame = SfxViewFrame::Current()->GetFrame().GetFrameInterface();
+ Reference< XPropertySet > xFrameProps( xFrame, UNO_QUERY );
+ if ( xFrameProps.is() )
+ xFrameProps->getPropertyValue( "LayoutManager" ) >>= m_xLayouter;
+ }
+ catch ( Exception const & )
+ {
+ TOOLS_WARN_EXCEPTION( "svx.tbxcrtls", "ToolboxAccess::Ctor()" );
+ }
+ }
+
+
+ void ToolboxAccess::toggleToolbox() const
+ {
+ try
+ {
+ Reference< XLayoutManager > xManager( m_xLayouter );
+ OSL_ENSURE( xManager. is(), "ToolboxAccess::toggleToolbox: couldn't obtain the layout manager!" );
+ if ( xManager. is() )
+ {
+ if ( xManager->isElementVisible( m_sToolboxResName ) )
+ {
+ xManager->hideElement( m_sToolboxResName );
+ xManager->destroyElement( m_sToolboxResName );
+ }
+ else
+ {
+ xManager->createElement( m_sToolboxResName );
+ xManager->showElement( m_sToolboxResName );
+ }
+ }
+ }
+ catch( const Exception& )
+ {
+ TOOLS_WARN_EXCEPTION( "svx", "ToolboxAccess::toggleToolbox" );
+ }
+ }
+
+
+ bool ToolboxAccess::isToolboxVisible() const
+ {
+ return ( m_xLayouter.is() && m_xLayouter->isElementVisible( m_sToolboxResName ) );
+ }
+
+
+}
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/tbxctrls/tbxcolorupdate.cxx b/svx/source/tbxctrls/tbxcolorupdate.cxx
new file mode 100644
index 000000000..4e6ec87f3
--- /dev/null
+++ b/svx/source/tbxctrls/tbxcolorupdate.cxx
@@ -0,0 +1,355 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * 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/objsh.hxx>
+#include <svx/drawitem.hxx>
+#include <tbxcolorupdate.hxx>
+#include <svx/svxids.hrc>
+#include <svx/xdef.hxx>
+#include <svx/xlineit0.hxx>
+#include <svx/xlndsit.hxx>
+
+#include <vcl/commandinfoprovider.hxx>
+#include <vcl/gdimtf.hxx>
+#include <vcl/svapp.hxx>
+#include <vcl/toolbox.hxx>
+#include <vcl/virdev.hxx>
+#include <vcl/weld.hxx>
+#include <vcl/settings.hxx>
+
+#include <svx/strings.hrc>
+#include <svx/dialmgr.hxx>
+
+namespace svx
+{
+ ToolboxButtonColorUpdaterBase::ToolboxButtonColorUpdaterBase(bool bWideButton, const OUString& rCommandLabel,
+ const OUString& rCommandURL,
+ const css::uno::Reference<css::frame::XFrame>& rFrame)
+ : mbWideButton(bWideButton)
+ , mbWasHiContrastMode(Application::GetSettings().GetStyleSettings().GetHighContrastMode())
+ , maCurColor(COL_TRANSPARENT)
+ , meImageType(vcl::ImageType::Size16)
+ , maCommandLabel(rCommandLabel)
+ , maCommandURL(rCommandURL)
+ , mxFrame(rFrame)
+ {
+ }
+
+ void ToolboxButtonColorUpdaterBase::Init(sal_uInt16 nSlotId)
+ {
+ switch (nSlotId)
+ {
+ case SID_ATTR_CHAR_COLOR:
+ case SID_ATTR_CHAR_COLOR2:
+ Update(NamedColor(COL_DEFAULT_FONT, SvxResId(RID_SVXSTR_COLOR_DEFAULT_FONT)));
+ break;
+ case SID_FRAME_LINECOLOR:
+ Update(NamedColor(COL_DEFAULT_FRAMELINE, SvxResId(RID_SVXSTR_COLOR_DEFAULT_FRAMELINE)));
+ break;
+ case SID_ATTR_CHAR_COLOR_BACKGROUND:
+ case SID_ATTR_CHAR_BACK_COLOR:
+ case SID_BACKGROUND_COLOR:
+ case SID_TABLE_CELL_BACKGROUND_COLOR:
+ Update(NamedColor(COL_DEFAULT_HIGHLIGHT, SvxResId(RID_SVXSTR_COLOR_DEFAULT_HIGHLIGHT)));
+ break;
+ case SID_ATTR_LINE_COLOR:
+ Update(NamedColor(COL_DEFAULT_SHAPE_STROKE, SvxResId(RID_SVXSTR_COLOR_DEFAULT_SHAPE_STROKE)));
+ break;
+ case SID_ATTR_FILL_COLOR:
+ Update(NamedColor(COL_DEFAULT_SHAPE_FILLING, SvxResId(RID_SVXSTR_COLOR_DEFAULT_SHAPE_FILLING)));
+ break;
+ default:
+ Update(COL_TRANSPARENT);
+ }
+ }
+
+ VclToolboxButtonColorUpdater::VclToolboxButtonColorUpdater(
+ sal_uInt16 nSlotId, ToolBoxItemId nTbxBtnId, ToolBox* pToolBox, bool bWideButton,
+ const OUString& rCommandLabel, const OUString& rCommandURL,
+ const css::uno::Reference<css::frame::XFrame>& rFrame)
+ : ToolboxButtonColorUpdaterBase(bWideButton, rCommandLabel, rCommandURL, rFrame)
+ , mnBtnId(nTbxBtnId)
+ , mpTbx(pToolBox)
+ {
+ Init(nSlotId);
+ }
+
+ void VclToolboxButtonColorUpdater::SetQuickHelpText(const OUString& rText)
+ {
+ mpTbx->SetQuickHelpText(mnBtnId, rText);
+ }
+
+ OUString VclToolboxButtonColorUpdater::GetQuickHelpText() const
+ {
+ return mpTbx->GetQuickHelpText(mnBtnId);
+ }
+
+ void VclToolboxButtonColorUpdater::SetImage(VirtualDevice* pVirDev)
+ {
+ GDIMetaFile* pMtf = pVirDev->GetConnectMetaFile();
+
+ assert(pMtf && "should have been set in ToolboxButtonColorUpdaterBase::Update");
+
+ pMtf->Stop();
+ pMtf->WindStart();
+
+ Graphic aGraphic(*pMtf);
+
+ mpTbx->SetItemImage(mnBtnId, Image(aGraphic.GetXGraphic()));
+ }
+
+ VclPtr<VirtualDevice> VclToolboxButtonColorUpdater::CreateVirtualDevice() const
+ {
+ return VclPtr<VirtualDevice>::Create(*mpTbx->GetOutDev());
+ }
+
+ vcl::ImageType VclToolboxButtonColorUpdater::GetImageSize() const
+ {
+ return mpTbx->GetImageSize();
+ }
+
+ Size VclToolboxButtonColorUpdater::GetItemSize(const Size& rImageSize) const
+ {
+ if (mbWideButton)
+ return mpTbx->GetItemContentSize(mnBtnId);
+ return rImageSize;
+ }
+
+ ToolboxButtonColorUpdaterBase::~ToolboxButtonColorUpdaterBase()
+ {}
+
+ void ToolboxButtonColorUpdaterBase::Update(const NamedColor &rNamedColor)
+ {
+ Update(rNamedColor.first);
+ if (!mbWideButton)
+ {
+ // Also show the current color as QuickHelpText
+ OUString colorSuffix = OUString(" (%1)").replaceFirst("%1", rNamedColor.second);
+ OUString colorHelpText = maCommandLabel + colorSuffix;
+
+ SetQuickHelpText(colorHelpText);
+ }
+ }
+
+ void ToolboxButtonColorUpdaterBase::Update(const Color& rColor, bool bForceUpdate)
+ {
+ vcl::ImageType eImageType = GetImageSize();
+
+#ifdef IOS // tdf#126966
+ eImageType = vcl::ImageType::Size32;
+#endif
+
+ const bool bSizeChanged = (meImageType != eImageType);
+ meImageType = eImageType;
+ const bool bDisplayModeChanged = (mbWasHiContrastMode != Application::GetSettings().GetStyleSettings().GetHighContrastMode());
+ Color aColor(rColor);
+
+ // !!! #109290# Workaround for SetFillColor with COL_AUTO
+ if (aColor == COL_AUTO)
+ aColor = COL_TRANSPARENT;
+
+ if ((maCurColor == aColor) && !bSizeChanged && !bDisplayModeChanged && !bForceUpdate)
+ return;
+
+ auto xImage = vcl::CommandInfoProvider::GetXGraphicForCommand(maCommandURL, mxFrame, meImageType);
+ Image aImage(xImage);
+
+ Size aItemSize = GetItemSize(aImage.GetSizePixel());
+ if (!aItemSize.Width() || !aItemSize.Height())
+ return;
+
+ ScopedVclPtr<VirtualDevice> pVirDev(CreateVirtualDevice());
+ pVirDev->SetOutputSizePixel(aItemSize);
+ maBmpSize = aItemSize;
+
+ std::unique_ptr<GDIMetaFile> xMetaFile;
+ if (RecordVirtualDevice())
+ {
+ xMetaFile.reset(new GDIMetaFile);
+ xMetaFile->SetPrefSize(pVirDev->GetOutputSize());
+ xMetaFile->SetPrefMapMode(pVirDev->GetMapMode());
+ xMetaFile->Record(pVirDev.get());
+ pVirDev->EnableOutput(false);
+ }
+
+ if (maBmpSize.Width() == maBmpSize.Height())
+ // tdf#84985 align color bar with icon bottom edge; integer arithmetic e.g. 26 - 26/4 <> 26 * 3/4
+ maUpdRect = tools::Rectangle(Point( 0, maBmpSize.Height() - maBmpSize.Height() / 4), Size(maBmpSize.Width(), maBmpSize.Height() / 4));
+ else
+ maUpdRect = tools::Rectangle(Point( maBmpSize.Height() + 2, 2), Point(maBmpSize.Width() - 3, maBmpSize.Height() - 3));
+
+ pVirDev->Push(vcl::PushFlags::CLIPREGION);
+
+ // tdf#135121 don't include the part of the image which we will
+ // overwrite with the target color so that for the transparent color
+ // case the original background of the device is shown
+ vcl::Region aRegion(tools::Rectangle(Point(0, 0), maBmpSize));
+ aRegion.Exclude(maUpdRect);
+ pVirDev->SetClipRegion(aRegion);
+
+ pVirDev->DrawImage(Point(0, 0), aImage);
+
+ pVirDev->Pop();
+
+ const StyleSettings& rStyleSettings = Application::GetSettings().GetStyleSettings();
+ mbWasHiContrastMode = rStyleSettings.GetHighContrastMode();
+
+ if ((COL_TRANSPARENT != aColor) && (maBmpSize.Width() == maBmpSize.Height()))
+ pVirDev->SetLineColor(aColor);
+ else
+ pVirDev->SetLineColor(rStyleSettings.GetDisableColor());
+
+ // use not only COL_TRANSPARENT for detection of transparence,
+ // but the method/way which is designed to do that
+ const bool bIsFullyTransparent(aColor.IsFullyTransparent());
+ maCurColor = aColor;
+
+ if (bIsFullyTransparent)
+ {
+ pVirDev->SetFillColor();
+ }
+ else
+ {
+ pVirDev->SetFillColor(maCurColor);
+ }
+
+ pVirDev->DrawRect(maUpdRect);
+
+ SetImage(pVirDev.get());
+ }
+
+ OUString ToolboxButtonColorUpdaterBase::GetCurrentColorName() const
+ {
+ OUString sColorName = GetQuickHelpText();
+ // The obtained string is of format: color context (color name)
+ // Generate a substring which contains only the color name
+ sal_Int32 nStart = sColorName.indexOf('(');
+ sColorName = sColorName.copy( nStart + 1 );
+ sal_Int32 nLength = sColorName.getLength();
+ if(nLength > 0)
+ sColorName = sColorName.copy( 0, nLength - 1);
+ return sColorName;
+ }
+
+ ToolboxButtonColorUpdater::ToolboxButtonColorUpdater(sal_uInt16 nSlotId, const OString& rTbxBtnId, weld::Toolbar* ptrTbx, bool bWideButton,
+ const OUString& rCommandLabel, const css::uno::Reference<css::frame::XFrame>& rFrame)
+ : ToolboxButtonColorUpdaterBase(bWideButton, rCommandLabel, OUString::fromUtf8(rTbxBtnId), rFrame)
+ , msBtnId(rTbxBtnId)
+ , mpTbx(ptrTbx)
+ {
+ Init(nSlotId);
+ }
+
+ void ToolboxButtonColorUpdater::SetQuickHelpText(const OUString& rText)
+ {
+ mpTbx->set_item_tooltip_text(msBtnId, rText);
+ }
+
+ OUString ToolboxButtonColorUpdater::GetQuickHelpText() const
+ {
+ return mpTbx->get_item_tooltip_text(msBtnId);
+ }
+
+ void ToolboxButtonColorUpdater::SetImage(VirtualDevice* pVirDev)
+ {
+ mpTbx->set_item_image(msBtnId, pVirDev);
+ }
+
+ VclPtr<VirtualDevice> ToolboxButtonColorUpdater::CreateVirtualDevice() const
+ {
+ return mpTbx->create_virtual_device();
+ }
+
+ vcl::ImageType ToolboxButtonColorUpdater::GetImageSize() const
+ {
+ return mpTbx->get_icon_size();
+ }
+
+ Size ToolboxButtonColorUpdater::GetItemSize(const Size& rImageSize) const
+ {
+ auto nWidth = rImageSize.Width();
+ if (mbWideButton)
+ nWidth = nWidth * 5;
+ return Size(nWidth, rImageSize.Height());
+ }
+
+ ToolboxButtonLineStyleUpdater::ToolboxButtonLineStyleUpdater()
+ : m_eXLS(css::drawing::LineStyle_NONE)
+ , m_nDashStyleIndex(-1)
+ {
+ }
+
+ void ToolboxButtonLineStyleUpdater::Update(const com::sun::star::frame::FeatureStateEvent& rEvent)
+ {
+ if (rEvent.FeatureURL.Complete == ".uno:LineDash")
+ {
+ m_nDashStyleIndex = -1;
+
+ SfxObjectShell* pSh = SfxObjectShell::Current();
+ if (!pSh)
+ return;
+ const SvxDashListItem* pItem = pSh->GetItem( SID_DASH_LIST );
+ if (!pItem)
+ return;
+
+ XLineDashItem aDashItem;
+ aDashItem.PutValue(rEvent.State, 0);
+ const XDash& rDash = aDashItem.GetDashValue();
+
+ XDashListRef xLineStyleList = pItem->GetDashList();
+ for (tools::Long i = 0; i < xLineStyleList->Count(); ++i)
+ {
+ const XDashEntry* pEntry = xLineStyleList->GetDash(i);
+ const XDash& rEntry = pEntry->GetDash();
+ if (rDash == rEntry)
+ {
+ m_nDashStyleIndex = i;
+ break;
+ }
+ }
+ }
+ else if (rEvent.FeatureURL.Complete == ".uno:XLineStyle")
+ {
+ XLineStyleItem aLineStyleItem;
+ aLineStyleItem.PutValue(rEvent.State, 0);
+
+ m_eXLS = aLineStyleItem.GetValue();
+ }
+ }
+
+ int ToolboxButtonLineStyleUpdater::GetStyleIndex() const
+ {
+ int nRet;
+ switch (m_eXLS)
+ {
+ case css::drawing::LineStyle_NONE:
+ nRet = 0;
+ break;
+ case css::drawing::LineStyle_SOLID:
+ nRet = 1;
+ break;
+ default:
+ nRet = m_nDashStyleIndex + 2;
+ break;
+ }
+ return nRet;
+ }
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/tbxctrls/tbxdrctl.cxx b/svx/source/tbxctrls/tbxdrctl.cxx
new file mode 100644
index 000000000..7eb116161
--- /dev/null
+++ b/svx/source/tbxctrls/tbxdrctl.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 <svl/eitem.hxx>
+#include <vcl/toolbox.hxx>
+
+#include <svx/tbxctl.hxx>
+
+#include <com/sun/star/frame/XLayoutManager.hpp>
+
+SFX_IMPL_TOOLBOX_CONTROL(SvxTbxCtlDraw, SfxBoolItem);
+
+using namespace ::com::sun::star::uno;
+using namespace ::com::sun::star::frame;
+
+
+SvxTbxCtlDraw::SvxTbxCtlDraw( sal_uInt16 nSlotId, ToolBoxItemId nId, ToolBox& rTbx ) :
+ SfxToolBoxControl( nSlotId, nId, rTbx )
+{
+ rTbx.SetItemBits( nId, ToolBoxItemBits::CHECKABLE | rTbx.GetItemBits( nId ) );
+ rTbx.Invalidate();
+}
+
+void SAL_CALL SvxTbxCtlDraw::initialize( const css::uno::Sequence< css::uno::Any >& aArguments )
+{
+ svt::ToolboxController::initialize(aArguments);
+ /*
+ * Toolbar name is defined as "private:resource/toolbar/drawbar" in writer and calc,
+ * "private:resource/toolbar/toolbar" in draw and impress. Control is added for this
+ * difference.
+ */
+ if( m_aCommandURL==".uno:TrackChangesBar")
+ m_sToolboxName="private:resource/toolbar/changes";
+ else if ( m_sModuleName == "com.sun.star.presentation.PresentationDocument" || m_sModuleName == "com.sun.star.drawing.DrawingDocument" )
+ m_sToolboxName="private:resource/toolbar/toolbar";
+ else
+ m_sToolboxName="private:resource/toolbar/drawbar";
+}
+
+
+void SvxTbxCtlDraw::StateChangedAtToolBoxControl( sal_uInt16 nSID, SfxItemState eState,
+ const SfxPoolItem* pState )
+{
+ GetToolBox().EnableItem( GetId(), ( eState != SfxItemState::DISABLED ) );
+ SfxToolBoxControl::StateChangedAtToolBoxControl( nSID, eState, pState );
+
+ Reference< XLayoutManager > xLayoutMgr = getLayoutManager();
+ if ( xLayoutMgr.is() )
+ GetToolBox().CheckItem(
+ GetId(), xLayoutMgr->isElementVisible( m_sToolboxName ) );
+}
+
+
+void SvxTbxCtlDraw::toggleToolbox()
+{
+ Reference< XLayoutManager > xLayoutMgr = getLayoutManager();
+ if ( !xLayoutMgr.is() )
+ return;
+
+ bool bCheck = false;
+ if ( xLayoutMgr->isElementVisible( m_sToolboxName ) )
+ {
+ xLayoutMgr->hideElement( m_sToolboxName );
+ xLayoutMgr->destroyElement( m_sToolboxName );
+ }
+ else
+ {
+ bCheck = true;
+ xLayoutMgr->createElement( m_sToolboxName );
+ xLayoutMgr->showElement( m_sToolboxName );
+ }
+
+ GetToolBox().CheckItem( GetId(), bCheck );
+}
+
+
+void SvxTbxCtlDraw::Select(sal_uInt16 /*nSelectModifier*/)
+{
+ toggleToolbox();
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/tbxctrls/verttexttbxctrl.cxx b/svx/source/tbxctrls/verttexttbxctrl.cxx
new file mode 100644
index 000000000..d15a276fe
--- /dev/null
+++ b/svx/source/tbxctrls/verttexttbxctrl.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 <cppuhelper/supportsservice.hxx>
+#include <verttexttbxctrl.hxx>
+#include <svl/cjkoptions.hxx>
+#include <svl/ctloptions.hxx>
+#include <vcl/toolbox.hxx>
+#include <vcl/weld.hxx>
+#include <rtl/ustring.hxx>
+
+SvxCTLTextTbxCtrl::SvxCTLTextTbxCtrl(
+ const css::uno::Reference<css::uno::XComponentContext>& rContext)
+ : SvxVertCTLTextTbxCtrl(rContext)
+{
+ addStatusListener(".uno:CTLFontState");
+}
+
+OUString SvxCTLTextTbxCtrl::getImplementationName()
+{
+ return "com.sun.star.comp.svx.CTLToolBoxControl";
+}
+
+extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface*
+com_sun_star_comp_svx_CTLToolBoxControl_get_implementation(css::uno::XComponentContext* rContext,
+ css::uno::Sequence<css::uno::Any> const&)
+{
+ return cppu::acquire(new SvxCTLTextTbxCtrl(rContext));
+}
+
+SvxVertTextTbxCtrl::SvxVertTextTbxCtrl(
+ const css::uno::Reference<css::uno::XComponentContext>& rContext)
+ : SvxVertCTLTextTbxCtrl(rContext)
+{
+ addStatusListener(".uno:VerticalTextState");
+}
+
+OUString SvxVertTextTbxCtrl::getImplementationName()
+{
+ return "com.sun.star.comp.svx.VertTextToolBoxControl";
+}
+
+extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface*
+com_sun_star_comp_svx_VertTextToolBoxControl_get_implementation(
+ css::uno::XComponentContext* rContext, css::uno::Sequence<css::uno::Any> const&)
+{
+ return cppu::acquire(new SvxVertTextTbxCtrl(rContext));
+}
+
+SvxVertCTLTextTbxCtrl::SvxVertCTLTextTbxCtrl(
+ const css::uno::Reference<css::uno::XComponentContext>& rContext)
+ : SvxVertCTLTextTbxCtrl_Base(rContext, nullptr, OUString())
+ , m_bVisible(false)
+{
+}
+
+SvxVertCTLTextTbxCtrl::~SvxVertCTLTextTbxCtrl() {}
+
+void SAL_CALL SvxVertCTLTextTbxCtrl::initialize(const css::uno::Sequence<css::uno::Any>& rArguments)
+{
+ SvxVertCTLTextTbxCtrl_Base::initialize(rArguments);
+ // fdo#83320 Hide vertical text commands early
+ setFastPropertyValue_NoBroadcast(1, css::uno::Any(true));
+
+ if (m_pToolbar)
+ {
+ m_bVisible = m_pToolbar->get_item_visible(m_aCommandURL.toUtf8());
+ return;
+ }
+
+ ToolBox* pToolBox = nullptr;
+ ToolBoxItemId nItemId;
+ if (getToolboxId(nItemId, &pToolBox))
+ m_bVisible = pToolBox->IsItemVisible(nItemId);
+}
+
+void SAL_CALL SvxVertCTLTextTbxCtrl::statusChanged(const css::frame::FeatureStateEvent& rEvent)
+{
+ ToolBox* pToolBox = nullptr;
+ ToolBoxItemId nItemId;
+ bool bVclToolBox = getToolboxId(nItemId, &pToolBox);
+
+ bool bEnabled = false;
+ if (rEvent.FeatureURL.Complete == ".uno:VerticalTextState")
+ {
+ bEnabled = m_bVisible && SvtCJKOptions::IsVerticalTextEnabled();
+ }
+ else if (rEvent.FeatureURL.Complete == ".uno:CTLFontState")
+ {
+ SvtCTLOptions aLangOptions;
+ bEnabled = m_bVisible && aLangOptions.IsCTLFontEnabled();
+ }
+ else
+ {
+ // normal command
+ bool bValue = false;
+ rEvent.State >>= bValue;
+
+ if (m_pToolbar)
+ {
+ OString sId = m_aCommandURL.toUtf8();
+ m_pToolbar->set_item_active(sId, bValue);
+ m_pToolbar->set_item_sensitive(sId, rEvent.IsEnabled);
+ }
+
+ if (bVclToolBox)
+ {
+ pToolBox->CheckItem(nItemId, bValue);
+ pToolBox->EnableItem(nItemId, rEvent.IsEnabled);
+ }
+
+ return;
+ }
+
+ if (m_pToolbar)
+ {
+ m_pToolbar->set_item_visible(m_aCommandURL.toUtf8(), bEnabled);
+ return;
+ }
+
+ if (bVclToolBox)
+ {
+ pToolBox->ShowItem(nItemId, bEnabled);
+
+ vcl::Window* pParent = pToolBox->GetParent();
+ if (WindowType::FLOATINGWINDOW == pParent->GetType())
+ {
+ Size aSize(pToolBox->CalcWindowSizePixel());
+ pToolBox->SetPosSizePixel(Point(), aSize);
+ pParent->SetOutputSizePixel(aSize);
+ }
+ }
+}
+
+// XServiceInfo
+sal_Bool SAL_CALL SvxVertCTLTextTbxCtrl::supportsService(const OUString& ServiceName)
+{
+ return cppu::supportsService(this, ServiceName);
+}
+
+css::uno::Sequence<OUString> SvxVertCTLTextTbxCtrl::getSupportedServiceNames()
+{
+ return { "com.sun.star.frame.ToolbarController" };
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/toolbars/extrusionbar.cxx b/svx/source/toolbars/extrusionbar.cxx
new file mode 100644
index 000000000..3e0273c28
--- /dev/null
+++ b/svx/source/toolbars/extrusionbar.cxx
@@ -0,0 +1,1338 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * 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/EnhancedCustomShapeMetalType.hpp>
+#include <com/sun/star/drawing/EnhancedCustomShapeParameterPair.hpp>
+#include <com/sun/star/drawing/EnhancedCustomShapeParameterType.hpp>
+#include <com/sun/star/drawing/ShadeMode.hpp>
+#include <com/sun/star/drawing/Position3D.hpp>
+#include <com/sun/star/drawing/Direction3D.hpp>
+#include <com/sun/star/drawing/ProjectionMode.hpp>
+#include <svx/svxids.hrc>
+#include <svx/svdundo.hxx>
+#include <sfx2/request.hxx>
+#include <sfx2/objface.hxx>
+#include <sfx2/viewsh.hxx>
+#include <sfx2/bindings.hxx>
+#include <svx/xsflclit.hxx>
+#include <svx/dialmgr.hxx>
+#include <svx/svdoashp.hxx>
+#include <svx/strings.hrc>
+#include <svx/svdview.hxx>
+#include <editeng/colritem.hxx>
+#include <svx/chrtitem.hxx>
+#include <svx/sdasitm.hxx>
+#include <svl/intitem.hxx>
+#include <rtl/math.hxx>
+#include <basegfx/numeric/ftools.hxx>
+
+#include <svx/extrusionbar.hxx>
+#include <extrusiondepthdialog.hxx>
+
+using namespace ::svx;
+using namespace ::cppu;
+using namespace ::com::sun::star::beans;
+using namespace ::com::sun::star::drawing;
+using namespace ::com::sun::star::uno;
+
+// Declare the default interface. (The slotmap must not be empty, so
+// we enter something which never occurs here (hopefully).)
+static SfxSlot aExtrusionBarSlots_Impl[] =
+{
+ { 0, SfxGroupId::NONE, SfxSlotMode::NONE, 0, 0, nullptr, nullptr, nullptr, nullptr, nullptr, 0, SfxDisableFlags::NONE, nullptr }
+};
+
+SFX_IMPL_INTERFACE(ExtrusionBar, SfxShell)
+
+void ExtrusionBar::InitInterface_Impl()
+{
+ GetStaticInterface()->RegisterObjectBar(SFX_OBJECTBAR_OBJECT, SfxVisibilityFlags::Invisible, ToolbarId::Svx_Extrusion_Bar);
+}
+
+
+ExtrusionBar::ExtrusionBar(SfxViewShell* pViewShell )
+: SfxShell(pViewShell)
+{
+ DBG_ASSERT( pViewShell, "svx::ExtrusionBar::ExtrusionBar(), I need a viewshell!" );
+ if( pViewShell )
+ SetPool(&pViewShell->GetPool());
+
+ SetName(SvxResId(RID_SVX_EXTRUSION_BAR));
+}
+
+ExtrusionBar::~ExtrusionBar()
+{
+ SetRepeatTarget(nullptr);
+}
+
+static void getLightingDirectionDefaults( const Direction3D **pLighting1Defaults, const Direction3D **pLighting2Defaults )
+{
+
+ static const Direction3D aLighting1Defaults[9] =
+ {
+ Direction3D( -50000, -50000, 10000 ),
+ Direction3D( 0, -50000, 10000 ),
+ Direction3D( 50000, -50000, 10000 ),
+ Direction3D( -50000, 0, 10000 ),
+ Direction3D( 0, 0, 10000 ),
+ Direction3D( 50000, 0, 10000 ),
+ Direction3D( -50000, 50000, 10000 ),
+ Direction3D( 0, 50000, 10000 ),
+ Direction3D( 50000, 50000, 10000 )
+ };
+
+ static const Direction3D aLighting2Defaults[9] =
+ {
+ Direction3D( 50000,0, 10000 ),
+ Direction3D( 0, 50000, 10000 ),
+ Direction3D( -50000, 0, 10000 ),
+ Direction3D( 50000, 0, 10000 ),
+ Direction3D( 0, 0, 10000 ),
+ Direction3D( -50000, 0, 10000 ),
+ Direction3D( 50000, 0, 10000 ),
+ Direction3D( 0, -50000, 10000 ),
+ Direction3D( -50000, 0, 10000 )
+ };
+
+ *pLighting1Defaults = aLighting1Defaults;
+ *pLighting2Defaults = aLighting2Defaults;
+};
+
+static void impl_execute( SfxRequest const & rReq, SdrCustomShapeGeometryItem& rGeometryItem, SdrObject* pObj )
+{
+ static constexpr OUStringLiteral sExtrusion = u"Extrusion";
+ static constexpr OUStringLiteral sRotateAngle = u"RotateAngle";
+
+ sal_uInt16 nSID = rReq.GetSlot();
+ switch( nSID )
+ {
+ case SID_EXTRUSION_TOGGLE:
+ {
+ bool bOn(false);
+ css::uno::Any* pAny = rGeometryItem.GetPropertyValueByName( sExtrusion, sExtrusion );
+ if ( pAny )
+ {
+ (*pAny) >>= bOn;
+ bOn = !bOn;
+ css::beans::PropertyValue aPropValue;
+ aPropValue.Name = sExtrusion;
+ aPropValue.Value <<= bOn;
+ rGeometryItem.SetPropertyValue(sExtrusion, aPropValue);
+ }
+ else
+ {
+ css::beans::PropertyValue aPropValue;
+ aPropValue.Name = sExtrusion;
+ aPropValue.Value <<= true;
+ rGeometryItem.SetPropertyValue(sExtrusion, aPropValue);
+ bOn = true;
+ }
+
+ // draw:extrusion-diffusion has default 0% and c3DDiffuseAmt has default 100%. We set property
+ // "Diffusion" with value 100% here if it does not exist already. This forces, that the
+ // property is written to file in case an extrusion is newly created, and users of old
+ // documents, which usually do not have this property, can force the value to 100% by toggling
+ // the extrusion off and on.
+ if (bOn)
+ {
+ pAny = rGeometryItem.GetPropertyValueByName(sExtrusion, u"Diffusion");
+ if (!pAny)
+ {
+ css::beans::PropertyValue aPropValue;
+ aPropValue.Name = u"Diffusion";
+ aPropValue.Value <<= 100.0;
+ rGeometryItem.SetPropertyValue( sExtrusion, aPropValue );
+ }
+ }
+ }
+ break;
+
+ case SID_EXTRUSION_TILT_DOWN:
+ case SID_EXTRUSION_TILT_UP:
+ case SID_EXTRUSION_TILT_LEFT:
+ case SID_EXTRUSION_TILT_RIGHT:
+ {
+ bool bHorizontal = ( nSID == SID_EXTRUSION_TILT_DOWN ) || ( nSID == SID_EXTRUSION_TILT_UP );
+ sal_Int32 nDiff = ( nSID == SID_EXTRUSION_TILT_LEFT ) || ( nSID == SID_EXTRUSION_TILT_UP ) ? 5 : -5;
+ EnhancedCustomShapeParameterPair aRotateAnglePropPair;
+ double fX = 0.0;
+ double fY = 0.0;
+ aRotateAnglePropPair.First.Value <<= fX;
+ aRotateAnglePropPair.First.Type = EnhancedCustomShapeParameterType::NORMAL;
+ aRotateAnglePropPair.Second.Value <<= fY;
+ aRotateAnglePropPair.Second.Type = EnhancedCustomShapeParameterType::NORMAL;
+ css::uno::Any* pAny = rGeometryItem.GetPropertyValueByName( sExtrusion, sRotateAngle );
+ if( pAny && ( *pAny >>= aRotateAnglePropPair ) )
+ {
+ aRotateAnglePropPair.First.Value >>= fX;
+ aRotateAnglePropPair.Second.Value >>= fY;
+ }
+ if ( bHorizontal )
+ fX += nDiff;
+ else
+ fY += nDiff;
+ aRotateAnglePropPair.First.Value <<= fX;
+ aRotateAnglePropPair.Second.Value <<= fY;
+ css::beans::PropertyValue aPropValue;
+ aPropValue.Name = sRotateAngle;
+ aPropValue.Value <<= aRotateAnglePropPair;
+ rGeometryItem.SetPropertyValue( sExtrusion, aPropValue );
+ }
+ break;
+
+ case SID_EXTRUSION_DIRECTION:
+ {
+ if( rReq.GetArgs() && rReq.GetArgs()->GetItemState( SID_EXTRUSION_DIRECTION ) == SfxItemState::SET )
+ {
+ sal_Int32 nSkew = rReq.GetArgs()->GetItem<SfxInt32Item>(SID_EXTRUSION_DIRECTION)->GetValue();
+
+ Position3D aViewPoint( 3472, -3472, 25000 );
+ double fOriginX = 0.50;
+ double fOriginY = -0.50;
+ double fSkewAngle = nSkew;
+ double fSkew = 50.0;
+
+ switch( nSkew )
+ {
+ case 135:
+ aViewPoint.PositionY = 3472;
+ fOriginY = 0.50;
+ break;
+ case 90:
+ aViewPoint.PositionX = 0;
+ aViewPoint.PositionY = 3472;
+ fOriginX = 0;
+ fOriginY = 0.50;
+ break;
+ case 45:
+ aViewPoint.PositionX = -3472;
+ aViewPoint.PositionY = 3472;
+ fOriginX = -0.50;
+ fOriginY = 0.50;
+ break;
+ case 180:
+ aViewPoint.PositionY = 0;
+ fOriginY = 0;
+ break;
+ case 0:
+ aViewPoint.PositionX = 0;
+ aViewPoint.PositionY = 0;
+ fOriginX = 0;
+ fOriginY = 0;
+ fSkew = 0.0;
+ break;
+ case -360:
+ aViewPoint.PositionX = -3472;
+ aViewPoint.PositionY = 0;
+ fOriginX = -0.50;
+ fOriginY = 0;
+ break;
+ case -90:
+ aViewPoint.PositionX = 0;
+ fOriginX = 0;
+ break;
+ case -45:
+ aViewPoint.PositionX = -3472;
+ fOriginX = -0.50;
+ break;
+ }
+
+ css::beans::PropertyValue aPropValue;
+
+ aPropValue.Name = "ViewPoint";
+ aPropValue.Value <<= aViewPoint;
+ rGeometryItem.SetPropertyValue( sExtrusion, aPropValue );
+
+
+ EnhancedCustomShapeParameterPair aOriginPropPair;
+ aOriginPropPair.First.Value <<= fOriginX;
+ aOriginPropPair.First.Type = EnhancedCustomShapeParameterType::NORMAL;
+ aOriginPropPair.Second.Value <<= fOriginY;
+ aOriginPropPair.Second.Type = EnhancedCustomShapeParameterType::NORMAL;
+ aPropValue.Name = "Origin";
+ aPropValue.Value <<= aOriginPropPair;
+ rGeometryItem.SetPropertyValue( sExtrusion, aPropValue );
+
+ EnhancedCustomShapeParameterPair aSkewPropPair;
+ aSkewPropPair.First.Value <<= fSkew;
+ aSkewPropPair.First.Type = EnhancedCustomShapeParameterType::NORMAL;
+ aSkewPropPair.Second.Value <<= fSkewAngle;
+ aSkewPropPair.Second.Type = EnhancedCustomShapeParameterType::NORMAL;
+ aPropValue.Name = "Skew";
+ aPropValue.Value <<= aSkewPropPair;
+ rGeometryItem.SetPropertyValue( sExtrusion, aPropValue );
+ }
+ }
+ break;
+ case SID_EXTRUSION_PROJECTION:
+ {
+ if( rReq.GetArgs() && rReq.GetArgs()->GetItemState( SID_EXTRUSION_PROJECTION ) == SfxItemState::SET )
+ {
+ sal_Int32 nProjection = rReq.GetArgs()->GetItem<SfxInt32Item>(SID_EXTRUSION_PROJECTION)->GetValue();
+ ProjectionMode eProjectionMode = nProjection == 1 ? ProjectionMode_PARALLEL : ProjectionMode_PERSPECTIVE;
+ css::beans::PropertyValue aPropValue;
+ aPropValue.Name = "ProjectionMode";
+ aPropValue.Value <<= eProjectionMode;
+ rGeometryItem.SetPropertyValue( sExtrusion, aPropValue );
+ }
+ }
+ break;
+ case SID_EXTRUSION_DEPTH:
+ {
+ if( rReq.GetArgs() && rReq.GetArgs()->GetItemState( SID_EXTRUSION_DEPTH ) == SfxItemState::SET)
+ {
+ double fDepth = rReq.GetArgs()->GetItem<SvxDoubleItem>(SID_EXTRUSION_DEPTH)->GetValue();
+ EnhancedCustomShapeParameterPair aDepthPropPair;
+ aDepthPropPair.First.Value <<= fDepth;
+ aDepthPropPair.First.Type = EnhancedCustomShapeParameterType::NORMAL;
+ aDepthPropPair.Second.Value <<= 0.0; // fraction
+ aDepthPropPair.Second.Type = EnhancedCustomShapeParameterType::NORMAL;
+
+ css::beans::PropertyValue aPropValue;
+ aPropValue.Name = "Depth";
+ aPropValue.Value <<= aDepthPropPair;
+ rGeometryItem.SetPropertyValue( sExtrusion, aPropValue );
+ }
+ }
+ break;
+ case SID_EXTRUSION_3D_COLOR:
+ {
+ if( rReq.GetArgs() && rReq.GetArgs()->GetItemState( SID_EXTRUSION_3D_COLOR ) == SfxItemState::SET)
+ {
+ Color aColor( static_cast<const SvxColorItem&>(rReq.GetArgs()->Get(SID_EXTRUSION_3D_COLOR)).GetValue() );
+
+ const bool bAuto = aColor == COL_AUTO;
+
+ css::beans::PropertyValue aPropValue;
+ aPropValue.Name = "Color";
+ aPropValue.Value <<= !bAuto;
+ rGeometryItem.SetPropertyValue( sExtrusion, aPropValue );
+
+ if( bAuto )
+ {
+ pObj->ClearMergedItem( XATTR_SECONDARYFILLCOLOR );
+ }
+ else
+ {
+ pObj->SetMergedItem( XSecondaryFillColorItem( "", aColor ) );
+ }
+ pObj->BroadcastObjectChange();
+ }
+ }
+ break;
+ case SID_EXTRUSION_SURFACE:
+ {
+ if( rReq.GetArgs() && rReq.GetArgs()->GetItemState( SID_EXTRUSION_SURFACE ) == SfxItemState::SET)
+ {
+ sal_Int32 nSurface = rReq.GetArgs()->GetItem<SfxInt32Item>(SID_EXTRUSION_SURFACE)->GetValue();
+
+ // Set ShadeMode only when changing from or to wireframe, otherwise keep existing value.
+ ShadeMode eOldShadeMode(ShadeMode_FLAT);
+ css::uno::Any* pAny = rGeometryItem.GetPropertyValueByName(sExtrusion, u"ShadeMode");
+ if (pAny)
+ *pAny >>= eOldShadeMode;
+ ShadeMode eShadeMode(eOldShadeMode);
+ switch (nSurface)
+ {
+ case 0: // wireframe
+ eShadeMode = ShadeMode_DRAFT;
+ break;
+ case 1: // matte
+ case 2: // plastic
+ case 3: // metal ODF
+ case 4: // metal MS Office
+ if (eOldShadeMode == ShadeMode_DRAFT)
+ eShadeMode = ShadeMode_FLAT; // ODF default
+ break;
+ }
+
+ // ODF has no dedicated property for 'surface'. MS Office binary format uses attribute
+ // c3DSpecularAmt to distinguish between 'matte' (=0) and 'plastic'.
+ // We do the same.
+ double fOldSpecularity = 0.0;
+ pAny = rGeometryItem.GetPropertyValueByName(sExtrusion, u"Specularity");
+ if (pAny)
+ *pAny >>= fOldSpecularity;
+ double fSpecularity = fOldSpecularity;
+ switch( nSurface )
+ {
+ case 0: // wireframe
+ break;
+ case 1: // matte
+ fSpecularity = 0.0;
+ break;
+ case 2: // plastic
+ case 3: // metal ODF
+ case 4: // metal MS Office
+ if (basegfx::fTools::equalZero(fOldSpecularity, 0.0001))
+ // MS Office uses 80000/65536. That is currently not allowed in ODF.
+ // But the ODF error will be caught in xmloff.
+ fSpecularity = 80000.0 / 655.36; // interpreted as %
+ break;
+ }
+
+ // MS Office binary format uses attribute c3DDiffuseAmt with value =43712 (Fixed 16.16) in
+ // addition to the 'metal' flag. For other surface kinds default = 65536 is used.
+ // We toggle between 100 and 43712.0 / 655.36 here, to get better ODF -> MSO binary.
+ // We keep other values, those might be set outside regular UI, e.g by macro.
+ double fOldDiffusion = 100.0;
+ pAny = rGeometryItem.GetPropertyValueByName(sExtrusion, u"Diffusion");
+ if (pAny)
+ *pAny >>= fOldDiffusion;
+ double fDiffusion = fOldDiffusion;
+ if (nSurface == 4)
+ {
+ if (fOldDiffusion == 100.0)
+ fDiffusion = 43712.0 / 655.36; // interpreted as %
+ }
+ else
+ {
+ if (basegfx::fTools::equalZero(fOldDiffusion - 43712.0 / 655.36, 0.0001))
+ fDiffusion = 100.0;
+ }
+
+ css::beans::PropertyValue aPropValue;
+ aPropValue.Name = "ShadeMode";
+ aPropValue.Value <<= eShadeMode;
+ rGeometryItem.SetPropertyValue( sExtrusion, aPropValue );
+
+ aPropValue.Name = "Metal";
+ aPropValue.Value <<= nSurface == 3 || nSurface == 4;
+ rGeometryItem.SetPropertyValue(sExtrusion, aPropValue);
+
+ if (nSurface == 3 || nSurface == 4)
+ {
+ aPropValue.Name = "MetalType";
+ aPropValue.Value <<= nSurface == 4
+ ? EnhancedCustomShapeMetalType::MetalMSCompatible
+ : EnhancedCustomShapeMetalType::MetalODF;
+ rGeometryItem.SetPropertyValue(sExtrusion, aPropValue);
+ }
+
+ if (!basegfx::fTools::equalZero(fOldSpecularity - fSpecularity, 0.0001))
+ {
+ aPropValue.Name = "Specularity";
+ aPropValue.Value <<= fSpecularity;
+ rGeometryItem.SetPropertyValue(sExtrusion, aPropValue);
+ }
+
+ if (!basegfx::fTools::equalZero(fOldDiffusion - fDiffusion, 0.0001))
+ {
+ aPropValue.Name = "Diffusion";
+ aPropValue.Value <<= fDiffusion;
+ rGeometryItem.SetPropertyValue(sExtrusion, aPropValue);
+ }
+ }
+ }
+ break;
+ case SID_EXTRUSION_LIGHTING_INTENSITY:
+ {
+ if( rReq.GetArgs() && rReq.GetArgs()->GetItemState( SID_EXTRUSION_LIGHTING_INTENSITY ) == SfxItemState::SET)
+ {
+ sal_Int32 nLevel = rReq.GetArgs()->GetItem<SfxInt32Item>(SID_EXTRUSION_LIGHTING_INTENSITY)->GetValue();
+
+ double fBrightness; // c3DAmbientIntensity in MS Office
+ double fLevel1; // c3DKeyIntensity in MS Office
+ double fLevel2; // c3DFillIntensity in MS Office
+
+ // ToDo: "bright" values are different from MS Office. Should they be kept?
+ switch( nLevel )
+ {
+ case 0: // bright
+ fBrightness = 33.0; // ODF default.
+ fLevel1 = 66.0; // ODF default
+ fLevel2 = 66.0; // ODF default
+ break;
+ case 1: // normal
+ fBrightness = 15.0;
+ fLevel1 = 67.0;
+ fLevel2 = 37.0;
+ break;
+ case 2: // dim
+ fBrightness = 6.0;
+ fLevel1 = 79.0;
+ fLevel2 = 21.0;
+ break;
+ }
+
+ css::beans::PropertyValue aPropValue;
+ aPropValue.Name = "Brightness";
+ aPropValue.Value <<= fBrightness;
+ rGeometryItem.SetPropertyValue( sExtrusion, aPropValue );
+
+ aPropValue.Name = "FirstLightLevel";
+ aPropValue.Value <<= fLevel1;
+ rGeometryItem.SetPropertyValue( sExtrusion, aPropValue );
+
+ aPropValue.Name = "SecondLightLevel";
+ aPropValue.Value <<= fLevel2;
+ rGeometryItem.SetPropertyValue( sExtrusion, aPropValue );
+
+ // If a user sets light preset 'Dim' in MS Office, MS Office sets second light to harsh.
+ // In other cases it is soft.
+ aPropValue.Name = "SecondLightHarsh";
+ aPropValue.Value <<= nLevel == 2;
+ rGeometryItem.SetPropertyValue(sExtrusion, aPropValue);
+ }
+ }
+ break;
+ case SID_EXTRUSION_LIGHTING_DIRECTION:
+ {
+ if( rReq.GetArgs() && rReq.GetArgs()->GetItemState( SID_EXTRUSION_LIGHTING_DIRECTION ) == SfxItemState::SET)
+ {
+ sal_Int32 nDirection = rReq.GetArgs()->GetItem<SfxInt32Item>(SID_EXTRUSION_LIGHTING_DIRECTION)->GetValue();
+
+ if((nDirection >= 0) && (nDirection < 9))
+ {
+ const Direction3D * pLighting1Defaults;
+ const Direction3D * pLighting2Defaults;
+
+ getLightingDirectionDefaults( &pLighting1Defaults, &pLighting2Defaults );
+
+ css::beans::PropertyValue aPropValue;
+ aPropValue.Name = "FirstLightDirection";
+ aPropValue.Value <<= pLighting1Defaults[nDirection];
+ rGeometryItem.SetPropertyValue( sExtrusion, aPropValue );
+
+ aPropValue.Name = "SecondLightDirection";
+ aPropValue.Value <<= pLighting2Defaults[nDirection];
+ rGeometryItem.SetPropertyValue( sExtrusion, aPropValue );
+ }
+ }
+ }
+ break;
+
+ }
+}
+
+void ExtrusionBar::execute( SdrView* pSdrView, SfxRequest const & rReq, SfxBindings& rBindings )
+{
+ sal_uInt16 nSID = rReq.GetSlot();
+ TranslateId pStrResId;
+
+ const bool bUndo = pSdrView && pSdrView->IsUndoEnabled();
+
+ switch( nSID )
+ {
+ case SID_EXTRUSION_TOGGLE:
+ {
+ if ( !pStrResId )
+ pStrResId = RID_SVXSTR_UNDO_APPLY_EXTRUSION_ON_OFF;
+ [[fallthrough]];
+ }
+ case SID_EXTRUSION_TILT_DOWN:
+ {
+ if ( !pStrResId )
+ pStrResId = RID_SVXSTR_UNDO_APPLY_EXTRUSION_ROTATE_DOWN;
+ [[fallthrough]];
+ }
+ case SID_EXTRUSION_TILT_UP:
+ {
+ if ( !pStrResId )
+ pStrResId = RID_SVXSTR_UNDO_APPLY_EXTRUSION_ROTATE_UP;
+ [[fallthrough]];
+ }
+ case SID_EXTRUSION_TILT_LEFT:
+ {
+ if ( !pStrResId )
+ pStrResId = RID_SVXSTR_UNDO_APPLY_EXTRUSION_ROTATE_LEFT;
+ [[fallthrough]];
+ }
+ case SID_EXTRUSION_TILT_RIGHT:
+ {
+ if ( !pStrResId )
+ pStrResId = RID_SVXSTR_UNDO_APPLY_EXTRUSION_ROTATE_RIGHT;
+ [[fallthrough]];
+ }
+ case SID_EXTRUSION_DIRECTION:
+ {
+ if ( !pStrResId )
+ pStrResId = RID_SVXSTR_UNDO_APPLY_EXTRUSION_ORIENTATION;
+ [[fallthrough]];
+ }
+ case SID_EXTRUSION_PROJECTION:
+ {
+ if ( !pStrResId )
+ pStrResId = RID_SVXSTR_UNDO_APPLY_EXTRUSION_PROJECTION;
+ [[fallthrough]];
+ }
+ case SID_EXTRUSION_DEPTH:
+ {
+ if ( !pStrResId )
+ pStrResId = RID_SVXSTR_UNDO_APPLY_EXTRUSION_DEPTH;
+ [[fallthrough]];
+ }
+ case SID_EXTRUSION_3D_COLOR:
+ {
+ if ( !pStrResId )
+ pStrResId = RID_SVXSTR_UNDO_APPLY_EXTRUSION_COLOR;
+ [[fallthrough]];
+ }
+ case SID_EXTRUSION_SURFACE:
+ {
+ if ( !pStrResId )
+ pStrResId = RID_SVXSTR_UNDO_APPLY_EXTRUSION_SURFACE;
+ [[fallthrough]];
+ }
+ case SID_EXTRUSION_LIGHTING_INTENSITY:
+ {
+ if ( !pStrResId )
+ pStrResId = RID_SVXSTR_UNDO_APPLY_EXTRUSION_BRIGHTNESS;
+ [[fallthrough]];
+ }
+ case SID_EXTRUSION_LIGHTING_DIRECTION:
+ {
+ if ( !pStrResId )
+ pStrResId = RID_SVXSTR_UNDO_APPLY_EXTRUSION_LIGHTING;
+
+ if (pSdrView)
+ {
+ const SdrMarkList& rMarkList = pSdrView->GetMarkedObjectList();
+ const size_t nCount = rMarkList.GetMarkCount();
+
+ for(size_t i=0; i<nCount; ++i)
+ {
+ SdrObject* pObj = rMarkList.GetMark(i)->GetMarkedSdrObj();
+ if( dynamic_cast<const SdrObjCustomShape*>( pObj) != nullptr )
+ {
+ if( bUndo )
+ {
+ OUString aStr( SvxResId( pStrResId ) );
+ pSdrView->BegUndo( aStr );
+ pSdrView->AddUndo( pSdrView->GetModel()->GetSdrUndoFactory().CreateUndoAttrObject( *pObj ) );
+ }
+ SdrCustomShapeGeometryItem aGeometryItem( pObj->GetMergedItem( SDRATTR_CUSTOMSHAPE_GEOMETRY ) );
+ impl_execute( rReq, aGeometryItem, pObj );
+ pObj->SetMergedItem( aGeometryItem );
+ pObj->BroadcastObjectChange();
+ if( bUndo )
+ pSdrView->EndUndo();
+
+ // simulate a context change:
+ // force SelectionHasChanged() being called
+ // so that extrusion bar will be visible/hidden
+ pSdrView->MarkListHasChanged();
+ }
+ }
+ }
+ }
+ break;
+
+ case SID_EXTRUSION_DEPTH_DIALOG:
+ if( rReq.GetArgs() &&
+ (rReq.GetArgs()->GetItemState( SID_EXTRUSION_DEPTH ) == SfxItemState::SET) &&
+ (rReq.GetArgs()->GetItemState( SID_ATTR_METRIC ) == SfxItemState::SET))
+ {
+ double fDepth = rReq.GetArgs()->GetItem<SvxDoubleItem>(SID_EXTRUSION_DEPTH)->GetValue();
+ FieldUnit eUnit = static_cast<FieldUnit>(rReq.GetArgs()->GetItem<SfxUInt16Item>(SID_ATTR_METRIC)->GetValue());
+
+ ExtrusionDepthDialog aDlg(rReq.GetFrameWeld(), fDepth, eUnit);
+ sal_uInt16 nRet = aDlg.run();
+ if (nRet == RET_OK)
+ {
+ fDepth = aDlg.getDepth();
+
+ SvxDoubleItem aItem( fDepth, SID_EXTRUSION_DEPTH );
+ SfxPoolItem* aItems[] = { &aItem, nullptr };
+ rBindings.Execute( SID_EXTRUSION_DEPTH, const_cast<const SfxPoolItem**>(aItems) );
+ }
+ }
+ break;
+ }
+
+ if( nSID != SID_EXTRUSION_TOGGLE )
+ return;
+
+ static const sal_uInt16 SidArray[] = {
+ SID_EXTRUSION_TILT_DOWN,
+ SID_EXTRUSION_TILT_UP,
+ SID_EXTRUSION_TILT_LEFT,
+ SID_EXTRUSION_TILT_RIGHT,
+ SID_EXTRUSION_DEPTH_FLOATER,
+ SID_EXTRUSION_DIRECTION_FLOATER,
+ SID_EXTRUSION_LIGHTING_FLOATER,
+ SID_EXTRUSION_SURFACE_FLOATER,
+ SID_EXTRUSION_3D_COLOR,
+ SID_EXTRUSION_DEPTH,
+ SID_EXTRUSION_DIRECTION,
+ SID_EXTRUSION_PROJECTION,
+ SID_EXTRUSION_LIGHTING_DIRECTION,
+ SID_EXTRUSION_LIGHTING_INTENSITY,
+ SID_EXTRUSION_SURFACE,
+ 0 };
+
+ rBindings.Invalidate( SidArray );
+}
+
+static void getExtrusionDirectionState( SdrView const * pSdrView, SfxItemSet& rSet )
+{
+ const SdrMarkList& rMarkList = pSdrView->GetMarkedObjectList();
+ const size_t nCount = rMarkList.GetMarkCount();
+
+ static constexpr OUStringLiteral sExtrusion = u"Extrusion";
+
+ const css::uno::Any* pAny;
+
+ double fFinalSkewAngle = -1;
+ bool bHasCustomShape = false;
+
+ for(size_t i=0; i<nCount; ++i)
+ {
+ SdrObject* pObj = rMarkList.GetMark(i)->GetMarkedSdrObj();
+ if( dynamic_cast<const SdrObjCustomShape*>( pObj) != nullptr )
+ {
+ const SdrCustomShapeGeometryItem & rGeometryItem( pObj->GetMergedItem( SDRATTR_CUSTOMSHAPE_GEOMETRY ) );
+
+ // see if this is an extruded customshape
+ if( !bHasCustomShape )
+ {
+ const Any* pAny_ = rGeometryItem.GetPropertyValueByName( sExtrusion, sExtrusion );
+ if( pAny_ )
+ *pAny_ >>= bHasCustomShape;
+
+ if( !bHasCustomShape )
+ continue;
+ }
+
+ bool bParallel = true;
+ Position3D aViewPoint( 3472, -3472, 25000 ); // MSO default
+ double fSkewAngle = -135; // MSO default
+
+ pAny = rGeometryItem.GetPropertyValueByName( sExtrusion, "ProjectionMode" );
+ sal_Int16 nProjectionMode = sal_Int16();
+ if( pAny && ( *pAny >>= nProjectionMode ) )
+ bParallel = static_cast<ProjectionMode>(nProjectionMode) == ProjectionMode_PARALLEL;
+
+ if( bParallel )
+ {
+ double fSkew = 50.0;
+ EnhancedCustomShapeParameterPair aSkewPropPair;
+ pAny = rGeometryItem.GetPropertyValueByName( sExtrusion, "Skew" );
+ if( pAny && ( *pAny >>= aSkewPropPair ) )
+ {
+ aSkewPropPair.First.Value >>= fSkew;
+ aSkewPropPair.Second.Value >>= fSkewAngle;
+ }
+ if ( fSkew == 0.0 )
+ fSkewAngle = 0.0;
+ else if ( fSkewAngle == 0.0 )
+ fSkewAngle = -360.0;
+ }
+ else
+ {
+ double fOriginX = 0.50;
+ double fOriginY = -0.50;
+ pAny = rGeometryItem.GetPropertyValueByName( sExtrusion, "ViewPoint" );
+ if( pAny )
+ *pAny >>= aViewPoint;
+
+ EnhancedCustomShapeParameterPair aOriginPropPair;
+ pAny = rGeometryItem.GetPropertyValueByName( sExtrusion, "Origin" );
+ if( pAny && ( *pAny >>= aOriginPropPair ) )
+ {
+ aOriginPropPair.First.Value >>= fOriginX;
+ aOriginPropPair.Second.Value >>= fOriginY;
+ }
+ fSkewAngle = -1;
+ const double e = 0.0001;
+ if( aViewPoint.PositionX > e )
+ {
+ if( aViewPoint.PositionY > e )
+ {
+ if( (fOriginX > e ) && ( fOriginY > e ) )
+ fSkewAngle = 135.0;
+ }
+ else if( aViewPoint.PositionY < -e )
+ {
+ if( ( fOriginX > e ) && ( fOriginY < -e ) )
+ fSkewAngle = -135.0;
+ }
+ else
+ {
+ if( ( fOriginX > e ) && ( fOriginY > -e ) && ( fOriginY < e ) )
+ fSkewAngle = 180.0;
+ }
+ }
+ else if( aViewPoint.PositionX < -e )
+ {
+ if( aViewPoint.PositionY < -e )
+ {
+ if( ( fOriginX < -e ) && ( fOriginY < -e ) )
+ fSkewAngle = -45.0;
+ }
+ else if( aViewPoint.PositionY > e )
+ {
+ if( ( fOriginX < -e ) && ( fOriginY > e ) )
+ fSkewAngle = 45.0;
+ }
+ else
+ {
+ if( ( fOriginX < e ) && ( fOriginY > -e ) && ( fOriginY < e ) )
+ fSkewAngle = -360.0;
+ }
+ }
+ else
+ {
+ if( aViewPoint.PositionY < -e )
+ {
+ if( ( fOriginX > -e ) && ( fOriginX < e ) && ( fOriginY < -e ) )
+ fSkewAngle = -90.0;
+ }
+ else if( aViewPoint.PositionY > e )
+ {
+ if( ( fOriginX > -e ) && ( fOriginX < e ) && ( fOriginY > e ) )
+ fSkewAngle = 90.0;
+ }
+ else
+ {
+ if( ( fOriginX > -e ) && ( fOriginX < e ) && ( fOriginY > -e ) && ( fOriginY < e ) )
+ fSkewAngle = 0.0;
+ }
+ }
+ }
+
+ if( rtl::math::approxEqual(fFinalSkewAngle, -1.0) )
+ {
+ fFinalSkewAngle = fSkewAngle;
+ }
+ else if( !rtl::math::approxEqual(fSkewAngle, fFinalSkewAngle) )
+ {
+ fFinalSkewAngle = -1.0;
+ }
+
+ if( rtl::math::approxEqual(fFinalSkewAngle, -1.0) )
+ break;
+ }
+ }
+
+ if( bHasCustomShape )
+ rSet.Put( SfxInt32Item( SID_EXTRUSION_DIRECTION, static_cast<sal_Int32>(fFinalSkewAngle) ) );
+ else
+ rSet.DisableItem( SID_EXTRUSION_DIRECTION );
+}
+
+static void getExtrusionProjectionState( SdrView const * pSdrView, SfxItemSet& rSet )
+{
+ const SdrMarkList& rMarkList = pSdrView->GetMarkedObjectList();
+ const size_t nCount = rMarkList.GetMarkCount();
+
+ static constexpr OUStringLiteral sExtrusion = u"Extrusion";
+
+ const css::uno::Any* pAny;
+
+ sal_Int32 nFinalProjection = -1;
+ bool bHasCustomShape = false;
+
+ for(size_t i=0; i<nCount; ++i)
+ {
+ SdrObject* pObj = rMarkList.GetMark(i)->GetMarkedSdrObj();
+ if( dynamic_cast<const SdrObjCustomShape*>( pObj) != nullptr )
+ {
+ // see if this is an extruded customshape
+ if( !bHasCustomShape )
+ {
+ const SdrCustomShapeGeometryItem & rGeometryItem( pObj->GetMergedItem( SDRATTR_CUSTOMSHAPE_GEOMETRY ) );
+ const Any* pAny_ = rGeometryItem.GetPropertyValueByName( sExtrusion, sExtrusion );
+ if( pAny_ )
+ *pAny_ >>= bHasCustomShape;
+
+ if( !bHasCustomShape )
+ continue;
+ }
+
+ const SdrCustomShapeGeometryItem & rGeometryItem( pObj->GetMergedItem( SDRATTR_CUSTOMSHAPE_GEOMETRY ) );
+
+ bool bParallel = true;
+ pAny = rGeometryItem.GetPropertyValueByName( sExtrusion, "ProjectionMode" );
+ ProjectionMode eProjectionMode;
+ if( pAny && ( *pAny >>= eProjectionMode ) )
+ bParallel = eProjectionMode == ProjectionMode_PARALLEL;
+
+ if( nFinalProjection == -1 )
+ {
+ nFinalProjection = bParallel ? 1 : 0;
+ }
+ else if( nFinalProjection != (bParallel ? 1 : 0) )
+ {
+ nFinalProjection = -1;
+ break;
+ }
+ }
+ }
+
+ if( bHasCustomShape )
+ rSet.Put( SfxInt32Item( SID_EXTRUSION_PROJECTION, nFinalProjection ) );
+ else
+ rSet.DisableItem( SID_EXTRUSION_PROJECTION );
+}
+
+static void getExtrusionSurfaceState( SdrView const * pSdrView, SfxItemSet& rSet )
+{
+ const SdrMarkList& rMarkList = pSdrView->GetMarkedObjectList();
+ const size_t nCount = rMarkList.GetMarkCount();
+
+ static constexpr OUStringLiteral sExtrusion = u"Extrusion";
+
+ const css::uno::Any* pAny;
+
+ sal_Int32 nFinalSurface = -1;
+ bool bHasCustomShape = false;
+
+ for(size_t i=0; i<nCount; ++i)
+ {
+ SdrObject* pObj = rMarkList.GetMark(i)->GetMarkedSdrObj();
+ if( dynamic_cast<const SdrObjCustomShape*>( pObj) != nullptr )
+ {
+ const SdrCustomShapeGeometryItem & rGeometryItem( pObj->GetMergedItem( SDRATTR_CUSTOMSHAPE_GEOMETRY ) );
+
+ // see if this is an extruded customshape
+ if( !bHasCustomShape )
+ {
+ const Any* pAny_ = rGeometryItem.GetPropertyValueByName( sExtrusion, sExtrusion );
+ if( pAny_ )
+ *pAny_ >>= bHasCustomShape;
+
+ if( !bHasCustomShape )
+ continue;
+ }
+
+ sal_Int32 nSurface = 0; // wire frame
+
+ ShadeMode eShadeMode( ShadeMode_FLAT );
+ pAny = rGeometryItem.GetPropertyValueByName( sExtrusion, u"ShadeMode" );
+ if( pAny )
+ *pAny >>= eShadeMode;
+
+ if (eShadeMode != ShadeMode_DRAFT)
+ {
+ bool bMetal = false;
+ pAny = rGeometryItem.GetPropertyValueByName( sExtrusion, u"Metal" );
+ if( pAny )
+ *pAny >>= bMetal;
+
+ if( bMetal )
+ {
+ nSurface = 3; // metal ODF
+ sal_Int16 eMetalType = EnhancedCustomShapeMetalType::MetalODF;
+ pAny = rGeometryItem.GetPropertyValueByName( sExtrusion, u"MetalType" );
+ if (pAny)
+ {
+ *pAny >>= eMetalType;
+ if (eMetalType == EnhancedCustomShapeMetalType::MetalMSCompatible)
+ nSurface = 4; // metal MS Office
+ }
+ }
+ else
+ {
+ double fSpecularity = 0;
+ pAny = rGeometryItem.GetPropertyValueByName( sExtrusion, u"Specularity" );
+ if( pAny )
+ *pAny >>= fSpecularity;
+
+ const double e = 0.0001;
+ if( (fSpecularity > -e) && (fSpecularity < e) )
+ {
+ nSurface = 1; // matte
+ }
+ else
+ {
+ nSurface = 2; // plastic
+ }
+ }
+ }
+
+ if( nFinalSurface == -1 )
+ {
+ nFinalSurface = nSurface;
+ }
+ else if( nFinalSurface != nSurface )
+ {
+ nFinalSurface = -1;
+ break;
+ }
+ }
+ }
+
+ if( bHasCustomShape )
+ rSet.Put( SfxInt32Item( SID_EXTRUSION_SURFACE, nFinalSurface ) );
+ else
+ rSet.DisableItem( SID_EXTRUSION_SURFACE );
+}
+
+static void getExtrusionDepthState( SdrView const * pSdrView, SfxItemSet& rSet )
+{
+ const SdrMarkList& rMarkList = pSdrView->GetMarkedObjectList();
+ const size_t nCount = rMarkList.GetMarkCount();
+
+ static constexpr OUStringLiteral sExtrusion = u"Extrusion";
+
+ const css::uno::Any* pAny;
+
+ double fFinalDepth = -1;
+ bool bHasCustomShape = false;
+
+ for(size_t i=0; i<nCount; ++i)
+ {
+ SdrObject* pObj = rMarkList.GetMark(i)->GetMarkedSdrObj();
+ if( dynamic_cast<const SdrObjCustomShape*>( pObj) != nullptr )
+ {
+ const SdrCustomShapeGeometryItem & rGeometryItem( pObj->GetMergedItem( SDRATTR_CUSTOMSHAPE_GEOMETRY ) );
+
+ // see if this is an extruded customshape
+ if( !bHasCustomShape )
+ {
+ const Any* pAny_ = rGeometryItem.GetPropertyValueByName( sExtrusion, sExtrusion );
+ if( pAny_ )
+ *pAny_ >>= bHasCustomShape;
+
+ if( !bHasCustomShape )
+ continue;
+ }
+
+ double fDepth = 1270.0; // =36pt ODF default
+ pAny = rGeometryItem.GetPropertyValueByName( sExtrusion, "Depth" );
+ if( pAny )
+ {
+ EnhancedCustomShapeParameterPair aDepthPropPair;
+ if ( *pAny >>= aDepthPropPair )
+ aDepthPropPair.First.Value >>= fDepth;
+ }
+
+ if( fFinalDepth == -1 )
+ {
+ fFinalDepth = fDepth;
+ }
+ else if( !rtl::math::approxEqual(fFinalDepth, fDepth) )
+ {
+ fFinalDepth = -1;
+ break;
+ }
+ }
+ }
+
+ if( pSdrView->GetModel() )
+ {
+ FieldUnit eUnit = pSdrView->GetModel()->GetUIUnit();
+ rSet.Put( SfxUInt16Item( SID_ATTR_METRIC, static_cast<sal_uInt16>(eUnit) ) );
+ }
+
+ if( bHasCustomShape )
+ rSet.Put( SvxDoubleItem( fFinalDepth, SID_EXTRUSION_DEPTH ) );
+ else
+ rSet.DisableItem( SID_EXTRUSION_DEPTH );
+}
+
+static bool compare_direction( const Direction3D& d1, const Direction3D& d2 )
+{
+ if( ((d1.DirectionX < 0) && (d2.DirectionX < 0)) || ((d1.DirectionX == 0) && (d2.DirectionX == 0)) || ((d1.DirectionX > 0) && (d2.DirectionX > 0)) )
+ {
+ if( ((d1.DirectionY < 0) && (d2.DirectionY < 0)) || ((d1.DirectionY == 0) && (d2.DirectionY == 0)) || ((d1.DirectionY > 0) && (d2.DirectionY > 0)) )
+ {
+ if( ((d1.DirectionZ < 0) && (d2.DirectionZ < 0)) || ((d1.DirectionZ == 0) && (d2.DirectionZ == 0)) || ((d1.DirectionZ > 0) && (d2.DirectionZ > 0)) )
+ {
+ return true;
+ }
+ }
+ }
+
+ return false;
+}
+
+static void getExtrusionLightingDirectionState( SdrView const * pSdrView, SfxItemSet& rSet )
+{
+ const SdrMarkList& rMarkList = pSdrView->GetMarkedObjectList();
+ const size_t nCount = rMarkList.GetMarkCount();
+
+ static constexpr OUStringLiteral sExtrusion = u"Extrusion";
+
+ const Direction3D * pLighting1Defaults;
+ const Direction3D * pLighting2Defaults;
+
+ getLightingDirectionDefaults( &pLighting1Defaults, &pLighting2Defaults );
+
+ const css::uno::Any* pAny;
+
+ int nFinalDirection = -1;
+ bool bHasCustomShape = false;
+
+ for(size_t i=0; i<nCount; ++i)
+ {
+ SdrObject* pObj = rMarkList.GetMark(i)->GetMarkedSdrObj();
+ if( dynamic_cast<const SdrObjCustomShape*>( pObj) != nullptr )
+ {
+ const SdrCustomShapeGeometryItem & rGeometryItem( pObj->GetMergedItem( SDRATTR_CUSTOMSHAPE_GEOMETRY ) );
+
+ // see if this is an extruded customshape
+ if( !bHasCustomShape )
+ {
+ const Any* pAny_ = rGeometryItem.GetPropertyValueByName( sExtrusion, sExtrusion );
+ if( pAny_ )
+ *pAny_ >>= bHasCustomShape;
+
+ if( !bHasCustomShape )
+ continue;
+ }
+
+ Direction3D aFirstLightDirection( 50000, 0, 10000 );
+ Direction3D aSecondLightDirection( -50000, 0, 10000 );
+
+ pAny = rGeometryItem.GetPropertyValueByName( sExtrusion, "FirstLightDirection" );
+ if( pAny )
+ *pAny >>= aFirstLightDirection;
+
+ pAny = rGeometryItem.GetPropertyValueByName( sExtrusion, "SecondLightDirection" );
+ if( pAny )
+ *pAny >>= aSecondLightDirection;
+
+ int nDirection = -1;
+
+ int j;
+ for( j = 0; j < 9; j++ )
+ {
+ if( compare_direction( aFirstLightDirection, pLighting1Defaults[j] ) &&
+ compare_direction( aSecondLightDirection, pLighting2Defaults[j] ))
+ {
+ nDirection = j;
+ break;
+ }
+ }
+
+ if( nFinalDirection == -1 )
+ {
+ nFinalDirection = nDirection;
+ }
+ else if( nDirection != nFinalDirection )
+ {
+ nFinalDirection = -1;
+ }
+
+ if( nFinalDirection == -1 )
+ break;
+ }
+ }
+
+ if( bHasCustomShape )
+ rSet.Put( SfxInt32Item( SID_EXTRUSION_LIGHTING_DIRECTION, static_cast<sal_Int32>(nFinalDirection) ) );
+ else
+ rSet.DisableItem( SID_EXTRUSION_LIGHTING_DIRECTION );
+}
+
+static void getExtrusionLightingIntensityState( SdrView const * pSdrView, SfxItemSet& rSet )
+{
+ const SdrMarkList& rMarkList = pSdrView->GetMarkedObjectList();
+ const size_t nCount = rMarkList.GetMarkCount();
+
+ static constexpr OUStringLiteral sExtrusion = u"Extrusion";
+
+ const css::uno::Any* pAny;
+
+ int nFinalLevel = -1;
+ bool bHasCustomShape = false;
+
+ for(size_t i=0; i<nCount; ++i)
+ {
+ SdrObject* pObj = rMarkList.GetMark(i)->GetMarkedSdrObj();
+ if( dynamic_cast<const SdrObjCustomShape*>( pObj) != nullptr )
+ {
+ const SdrCustomShapeGeometryItem & rGeometryItem( pObj->GetMergedItem( SDRATTR_CUSTOMSHAPE_GEOMETRY ) );
+
+ // see if this is an extruded customshape
+ if( !bHasCustomShape )
+ {
+ const Any* pAny_ = rGeometryItem.GetPropertyValueByName( sExtrusion, sExtrusion );
+ if( pAny_ )
+ *pAny_ >>= bHasCustomShape;
+
+ if( !bHasCustomShape )
+ continue;
+ }
+
+ double fBrightness = 22178.0 / 655.36;
+ pAny = rGeometryItem.GetPropertyValueByName( sExtrusion, "Brightness" );
+ if( pAny )
+ *pAny >>= fBrightness;
+
+ int nLevel;
+ if( fBrightness >= 30.0 )
+ {
+ nLevel = 0; // Bright
+ }
+ else if( fBrightness >= 10.0 )
+ {
+ nLevel = 1; // Normal;
+ }
+ else
+ {
+ nLevel = 2; // Dim
+ }
+
+ if( nFinalLevel == -1 )
+ {
+ nFinalLevel = nLevel;
+ }
+ else if( nFinalLevel != nLevel )
+ {
+ nFinalLevel = -1;
+ break;
+ }
+ }
+ }
+
+ if( bHasCustomShape )
+ rSet.Put( SfxInt32Item( SID_EXTRUSION_LIGHTING_INTENSITY, nFinalLevel ) );
+ else
+ rSet.DisableItem( SID_EXTRUSION_LIGHTING_INTENSITY );
+}
+
+static void getExtrusionColorState( SdrView const * pSdrView, SfxItemSet& rSet )
+{
+ const SdrMarkList& rMarkList = pSdrView->GetMarkedObjectList();
+ const size_t nCount = rMarkList.GetMarkCount();
+
+ static constexpr OUStringLiteral sExtrusion = u"Extrusion";
+
+ const css::uno::Any* pAny;
+
+ bool bInit = false;
+ bool bAmbigius = false;
+ Color aFinalColor;
+ bool bHasCustomShape = false;
+
+ for(size_t i=0; i<nCount; ++i)
+ {
+ SdrObject* pObj = rMarkList.GetMark(i)->GetMarkedSdrObj();
+ if( dynamic_cast<const SdrObjCustomShape*>( pObj) != nullptr )
+ {
+ const SdrCustomShapeGeometryItem & rGeometryItem( pObj->GetMergedItem( SDRATTR_CUSTOMSHAPE_GEOMETRY ) );
+
+ // see if this is an extruded customshape
+ if( !bHasCustomShape )
+ {
+ const Any* pAny_ = rGeometryItem.GetPropertyValueByName( sExtrusion, sExtrusion );
+ if( pAny_ )
+ *pAny_ >>= bHasCustomShape;
+
+ if( !bHasCustomShape )
+ continue;
+ }
+
+ Color aColor;
+
+ bool bUseColor = false;
+ pAny = rGeometryItem.GetPropertyValueByName( sExtrusion, "Color" );
+ if( pAny )
+ *pAny >>= bUseColor;
+
+ if( bUseColor )
+ {
+ const XSecondaryFillColorItem& rItem = pObj->GetMergedItem( XATTR_SECONDARYFILLCOLOR );
+ aColor = rItem.GetColorValue();
+ }
+ else
+ {
+ aColor = COL_AUTO;
+ }
+
+ if( !bInit )
+ {
+ aFinalColor = aColor;
+ bInit = true;
+ }
+ else if( aFinalColor != aColor )
+ {
+ bAmbigius = true;
+ break;
+ }
+ }
+ }
+
+ if( bAmbigius )
+ aFinalColor = COL_AUTO;
+
+ if( bHasCustomShape )
+ rSet.Put( SvxColorItem( aFinalColor, SID_EXTRUSION_3D_COLOR ) );
+ else
+ rSet.DisableItem( SID_EXTRUSION_3D_COLOR );
+}
+
+namespace svx {
+bool checkForSelectedCustomShapes( SdrView const * pSdrView, bool bOnlyExtruded )
+{
+ static constexpr OUStringLiteral sExtrusion = u"Extrusion";
+
+ const SdrMarkList& rMarkList = pSdrView->GetMarkedObjectList();
+ const size_t nCount = rMarkList.GetMarkCount();
+ bool bFound = false;
+
+ for(size_t i=0;(i<nCount) && !bFound ; ++i)
+ {
+ SdrObject* pObj = rMarkList.GetMark(i)->GetMarkedSdrObj();
+ if( dynamic_cast<const SdrObjCustomShape*>( pObj) != nullptr )
+ {
+ if( bOnlyExtruded )
+ {
+ const SdrCustomShapeGeometryItem & rGeometryItem( pObj->GetMergedItem( SDRATTR_CUSTOMSHAPE_GEOMETRY ) );
+ const Any* pAny = rGeometryItem.GetPropertyValueByName( sExtrusion, sExtrusion );
+ if( pAny )
+ *pAny >>= bFound;
+ }
+ else
+ {
+ bFound = true;
+ }
+ }
+ }
+
+ return bFound;
+}
+}
+
+void ExtrusionBar::getState( SdrView const * pSdrView, SfxItemSet& rSet )
+{
+ getExtrusionDirectionState( pSdrView, rSet );
+ getExtrusionProjectionState( pSdrView, rSet );
+
+ const bool bOnlyExtrudedCustomShapes(checkForSelectedCustomShapes( pSdrView, true ));
+
+ if (! bOnlyExtrudedCustomShapes)
+ {
+ rSet.DisableItem( SID_EXTRUSION_TILT_DOWN );
+ rSet.DisableItem( SID_EXTRUSION_TILT_DOWN );
+ rSet.DisableItem( SID_EXTRUSION_TILT_UP );
+ rSet.DisableItem( SID_EXTRUSION_TILT_LEFT );
+ rSet.DisableItem( SID_EXTRUSION_TILT_RIGHT );
+ rSet.DisableItem( SID_EXTRUSION_3D_COLOR );
+ rSet.DisableItem( SID_EXTRUSION_DEPTH_FLOATER );
+ rSet.DisableItem( SID_EXTRUSION_DIRECTION_FLOATER );
+ rSet.DisableItem( SID_EXTRUSION_LIGHTING_FLOATER );
+ rSet.DisableItem( SID_EXTRUSION_SURFACE_FLOATER );
+ }
+
+ if( !checkForSelectedCustomShapes( pSdrView, false ) )
+ rSet.DisableItem( SID_EXTRUSION_TOGGLE );
+
+ getExtrusionDepthState( pSdrView, rSet );
+ getExtrusionSurfaceState( pSdrView, rSet );
+ getExtrusionLightingIntensityState( pSdrView, rSet );
+ getExtrusionLightingDirectionState( pSdrView, rSet );
+ getExtrusionColorState( pSdrView, rSet );
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/toolbars/fontworkbar.cxx b/svx/source/toolbars/fontworkbar.cxx
new file mode 100644
index 000000000..7b2e8653b
--- /dev/null
+++ b/svx/source/toolbars/fontworkbar.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 <svx/svdundo.hxx>
+#include <sfx2/request.hxx>
+#include <sfx2/objface.hxx>
+#include <sfx2/viewsh.hxx>
+#include <svx/unoapi.hxx>
+#include <com/sun/star/drawing/XShape.hpp>
+#include <com/sun/star/drawing/XEnhancedCustomShapeDefaulter.hpp>
+#include <sal/log.hxx>
+#include <svx/dialmgr.hxx>
+#include <svx/svdoashp.hxx>
+#include <svx/strings.hrc>
+#include <svx/svdpage.hxx>
+#include <svx/svdview.hxx>
+#include <svx/sdasitm.hxx>
+#include <svx/gallery.hxx>
+#include <svx/fmmodel.hxx>
+#include <svx/sdtfsitm.hxx>
+#include <svl/itempool.hxx>
+#include <svl/stritem.hxx>
+#include <sfx2/bindings.hxx>
+#include <editeng/eeitem.hxx>
+#include <editeng/charscaleitem.hxx>
+#include <editeng/kernitem.hxx>
+
+#include <svx/svxids.hrc>
+#include <svx/fontworkbar.hxx>
+#include <svx/fontworkgallery.hxx>
+
+
+using namespace ::svx;
+using namespace ::cppu;
+using namespace ::com::sun::star;
+using namespace ::com::sun::star::beans;
+using namespace ::com::sun::star::uno;
+
+static void SetAlignmentState( SdrView const * pSdrView, SfxItemSet& rSet )
+{
+ const SdrMarkList& rMarkList = pSdrView->GetMarkedObjectList();
+ const size_t nCount = rMarkList.GetMarkCount();
+
+ sal_Int32 nAlignment = -1;
+ for( size_t i = 0; i < nCount; ++i )
+ {
+ SdrObject* pObj = rMarkList.GetMark( i )->GetMarkedSdrObj();
+ if( dynamic_cast<const SdrObjCustomShape*>( pObj) != nullptr )
+ {
+ sal_Int32 nOldAlignment = nAlignment;
+ const SdrTextHorzAdjustItem& rTextHorzAdjustItem = pObj->GetMergedItem( SDRATTR_TEXT_HORZADJUST );
+ const SdrTextFitToSizeTypeItem& rTextFitToSizeTypeItem = pObj->GetMergedItem( SDRATTR_TEXT_FITTOSIZE );
+ switch ( rTextHorzAdjustItem.GetValue() )
+ {
+ case SDRTEXTHORZADJUST_LEFT : nAlignment = 0; break;
+ case SDRTEXTHORZADJUST_CENTER : nAlignment = 1; break;
+ case SDRTEXTHORZADJUST_RIGHT : nAlignment = 2; break;
+ case SDRTEXTHORZADJUST_BLOCK :
+ {
+ auto const fit(rTextFitToSizeTypeItem.GetValue());
+ if (fit == drawing::TextFitToSizeType_NONE)
+ {
+ nAlignment = 3;
+ }
+ else if (fit == drawing::TextFitToSizeType_ALLLINES ||
+ fit == drawing::TextFitToSizeType_PROPORTIONAL)
+ {
+ nAlignment = 4;
+ }
+ }
+ }
+ if ( ( nOldAlignment != -1 ) && ( nOldAlignment != nAlignment ) )
+ {
+ nAlignment = -1;
+ break;
+ }
+ }
+ }
+ rSet.Put( SfxInt32Item( SID_FONTWORK_ALIGNMENT, nAlignment ) );
+}
+
+static void SetCharacterSpacingState( SdrView const * pSdrView, SfxItemSet& rSet )
+{
+ const SdrMarkList& rMarkList = pSdrView->GetMarkedObjectList();
+ const size_t nCount = rMarkList.GetMarkCount();
+
+ sal_Int32 nCharacterSpacing = -1;
+ for( size_t i = 0; i < nCount; ++i )
+ {
+ SdrObject* pObj = rMarkList.GetMark( i )->GetMarkedSdrObj();
+ if( dynamic_cast<const SdrObjCustomShape*>( pObj) != nullptr )
+ {
+ sal_Int32 nOldCharacterSpacing = nCharacterSpacing;
+ const SvxCharScaleWidthItem& rCharScaleWidthItem = pObj->GetMergedItem( EE_CHAR_FONTWIDTH );
+ nCharacterSpacing = rCharScaleWidthItem.GetValue();
+ if ( ( nOldCharacterSpacing != -1 ) && ( nOldCharacterSpacing != nCharacterSpacing ) )
+ {
+ nCharacterSpacing = -1;
+ break;
+ }
+ }
+ }
+ rSet.Put( SfxInt32Item( SID_FONTWORK_CHARACTER_SPACING, nCharacterSpacing ) );
+}
+
+
+static void SetKernCharacterPairsState( SdrView const * pSdrView, SfxItemSet& rSet )
+{
+ const SdrMarkList& rMarkList = pSdrView->GetMarkedObjectList();
+ const size_t nCount = rMarkList.GetMarkCount();
+
+ bool bChecked = false;
+ for( size_t i = 0; i < nCount; ++i )
+ {
+ SdrObject* pObj = rMarkList.GetMark( i )->GetMarkedSdrObj();
+ if( dynamic_cast<const SdrObjCustomShape*>( pObj) != nullptr )
+ {
+ const SvxKerningItem& rKerningItem = pObj->GetMergedItem( EE_CHAR_KERNING );
+ if ( rKerningItem.GetValue() )
+ bChecked = true;
+ }
+ }
+ rSet.Put( SfxBoolItem( SID_FONTWORK_KERN_CHARACTER_PAIRS, bChecked ) );
+}
+
+static void SetFontWorkShapeTypeState( SdrView const * pSdrView, SfxItemSet& rSet )
+{
+ const SdrMarkList& rMarkList = pSdrView->GetMarkedObjectList();
+ const size_t nCount = rMarkList.GetMarkCount();
+
+ OUString aFontWorkShapeType;
+
+ for( size_t i = 0; i < nCount; ++i )
+ {
+ SdrObject* pObj = rMarkList.GetMark( i )->GetMarkedSdrObj();
+ if( dynamic_cast<const SdrObjCustomShape*>( pObj) != nullptr )
+ {
+ const SdrCustomShapeGeometryItem & rGeometryItem( pObj->GetMergedItem( SDRATTR_CUSTOMSHAPE_GEOMETRY ) );
+ const Any* pAny = rGeometryItem.GetPropertyValueByName( "Type" );
+ if( pAny )
+ {
+ OUString aType;
+ if ( *pAny >>= aType )
+ {
+ if ( !aFontWorkShapeType.isEmpty() )
+ {
+ if ( aFontWorkShapeType != aType ) // different FontWorkShapeTypes selected ?
+ {
+ aFontWorkShapeType.clear();
+ break;
+ }
+ }
+ aFontWorkShapeType = aType;
+ }
+ }
+ }
+ }
+ rSet.Put( SfxStringItem( SID_FONTWORK_SHAPE_TYPE, aFontWorkShapeType ) );
+}
+
+// Declare the default interface. (The slotmap must not be empty, so
+// we enter something which never occurs here (hopefully).)
+static SfxSlot aFontworkBarSlots_Impl[] =
+{
+ { 0, SfxGroupId::NONE, SfxSlotMode::NONE, 0, 0, nullptr, nullptr, nullptr, nullptr, nullptr, 0, SfxDisableFlags::NONE, nullptr }
+};
+
+SFX_IMPL_INTERFACE(FontworkBar, SfxShell)
+
+void FontworkBar::InitInterface_Impl()
+{
+ GetStaticInterface()->RegisterObjectBar(SFX_OBJECTBAR_OBJECT, SfxVisibilityFlags::Invisible, ToolbarId::Svx_Fontwork_Bar);
+}
+
+
+FontworkBar::FontworkBar(SfxViewShell* pViewShell )
+: SfxShell(pViewShell)
+{
+ DBG_ASSERT( pViewShell, "svx::FontworkBar::FontworkBar(), I need a viewshell!" );
+ if( pViewShell )
+ SetPool(&pViewShell->GetPool());
+
+ SetName( SvxResId( RID_SVX_FONTWORK_BAR ));
+}
+
+FontworkBar::~FontworkBar()
+{
+ SetRepeatTarget(nullptr);
+}
+
+namespace svx {
+bool checkForFontWork( const SdrObject* pObj )
+{
+ static constexpr OUStringLiteral sTextPath = u"TextPath";
+ bool bFound = false;
+
+ if( dynamic_cast<const SdrObjCustomShape*>( pObj) != nullptr )
+ {
+ const SdrCustomShapeGeometryItem & rGeometryItem( pObj->GetMergedItem( SDRATTR_CUSTOMSHAPE_GEOMETRY ) );
+ const Any* pAny = rGeometryItem.GetPropertyValueByName( sTextPath, sTextPath );
+ if( pAny )
+ *pAny >>= bFound;
+ }
+
+ return bFound;
+}
+
+bool checkForSelectedFontWork( SdrView const * pSdrView )
+{
+ const SdrMarkList& rMarkList = pSdrView->GetMarkedObjectList();
+ const size_t nCount = rMarkList.GetMarkCount();
+ bool bFound = false;
+ for(size_t i=0; (i<nCount) && !bFound ; ++i)
+ {
+ SdrObject* pObj = rMarkList.GetMark(i)->GetMarkedSdrObj();
+ bFound = checkForFontWork(pObj);
+ }
+ return bFound;
+}
+
+static void impl_execute( SfxRequest const & rReq, SdrCustomShapeGeometryItem& rGeometryItem, SdrObject* pObj )
+{
+ sal_uInt16 nSID = rReq.GetSlot();
+ switch( nSID )
+ {
+ case SID_FONTWORK_SAME_LETTER_HEIGHTS:
+ {
+ css::uno::Any* pAny = rGeometryItem.GetPropertyValueByName( "TextPath", "SameLetterHeights" );
+ if( pAny )
+ {
+ bool bOn = false;
+ (*pAny) >>= bOn;
+ bOn = !bOn;
+ css::beans::PropertyValue aPropValue;
+ aPropValue.Name = "SameLetterHeights";
+ aPropValue.Value <<= bOn;
+ rGeometryItem.SetPropertyValue("TextPath", aPropValue);
+ }
+ }
+ break;
+
+ case SID_FONTWORK_ALIGNMENT:
+ {
+ if( rReq.GetArgs() && rReq.GetArgs()->GetItemState( SID_FONTWORK_ALIGNMENT ) == SfxItemState::SET )
+ {
+ sal_Int32 nValue = rReq.GetArgs()->GetItem<SfxInt32Item>(SID_FONTWORK_ALIGNMENT)->GetValue();
+ if ( ( nValue >= 0 ) && ( nValue < 5 ) )
+ {
+ drawing::TextFitToSizeType eFTS = drawing::TextFitToSizeType_NONE;
+ SdrTextHorzAdjust eHorzAdjust;
+ switch ( nValue )
+ {
+ case 4 : eFTS = drawing::TextFitToSizeType_ALLLINES; [[fallthrough]];
+ case 3 : eHorzAdjust = SDRTEXTHORZADJUST_BLOCK; break;
+ default: eHorzAdjust = SDRTEXTHORZADJUST_LEFT; break;
+ case 1 : eHorzAdjust = SDRTEXTHORZADJUST_CENTER; break;
+ case 2 : eHorzAdjust = SDRTEXTHORZADJUST_RIGHT; break;
+ }
+ pObj->SetMergedItem( SdrTextHorzAdjustItem( eHorzAdjust ) );
+ pObj->SetMergedItem( SdrTextFitToSizeTypeItem( eFTS ) );
+ pObj->BroadcastObjectChange();
+ }
+ }
+ }
+ break;
+
+ case SID_FONTWORK_CHARACTER_SPACING:
+ {
+ if( rReq.GetArgs() && ( rReq.GetArgs()->GetItemState( SID_FONTWORK_CHARACTER_SPACING ) == SfxItemState::SET ) )
+ {
+ sal_Int32 nCharSpacing = rReq.GetArgs()->GetItem<SfxInt32Item>(SID_FONTWORK_CHARACTER_SPACING)->GetValue();
+ pObj->SetMergedItem( SvxCharScaleWidthItem( static_cast<sal_uInt16>(nCharSpacing), EE_CHAR_FONTWIDTH ) );
+ pObj->BroadcastObjectChange();
+ }
+ }
+ break;
+
+ case SID_FONTWORK_KERN_CHARACTER_PAIRS:
+ {
+ if( rReq.GetArgs() && ( rReq.GetArgs()->GetItemState( SID_FONTWORK_KERN_CHARACTER_PAIRS ) == SfxItemState::SET ) )
+ {
+ // sal_Bool bKernCharacterPairs = ((const SfxBoolItem*)rReq.GetArgs()->GetItem(SID_FONTWORK_KERN_CHARACTER_PAIRS))->GetValue();
+//TODO: pObj->SetMergedItem( SvxCharScaleWidthItem( (sal_uInt16)nCharSpacing, EE_CHAR_FONTWIDTH ) );
+ pObj->BroadcastObjectChange();
+ }
+ }
+ break;
+ }
+}
+
+static void GetGeometryForCustomShape( SdrCustomShapeGeometryItem& rGeometryItem, const OUString& rCustomShape )
+{
+ static const OUStringLiteral sType( u"Type" );
+
+ css::beans::PropertyValue aPropVal;
+ aPropVal.Name = sType;
+ aPropVal.Value <<= rCustomShape;
+ rGeometryItem.SetPropertyValue( aPropVal );
+
+ static const OUStringLiteral sAdjustmentValues( u"AdjustmentValues" );
+ static const OUStringLiteral sCoordinateOrigin( u"CoordinateOrigin" );
+ static const OUStringLiteral sCoordinateSize( u"CoordinateSize" );
+ static const OUStringLiteral sEquations( u"Equations" );
+ static const OUStringLiteral sHandles( u"Handles" );
+ static const OUStringLiteral sPath( u"Path" );
+ rGeometryItem.ClearPropertyValue( sAdjustmentValues );
+ rGeometryItem.ClearPropertyValue( sCoordinateOrigin );
+ rGeometryItem.ClearPropertyValue( sCoordinateSize );
+ rGeometryItem.ClearPropertyValue( sEquations );
+ rGeometryItem.ClearPropertyValue( sHandles );
+ rGeometryItem.ClearPropertyValue( sPath );
+
+ /* SJ: CustomShapes that are available in the gallery are having the highest
+ priority, so we will take a look there before taking the internal default */
+
+ if ( !GalleryExplorer::GetSdrObjCount( GALLERY_THEME_POWERPOINT ) )
+ return;
+
+ std::vector< OUString > aObjList;
+ if ( !GalleryExplorer::FillObjListTitle( GALLERY_THEME_POWERPOINT, aObjList ) )
+ return;
+
+ for ( std::vector<OUString>::size_type i = 0; i < aObjList.size(); i++ )
+ {
+ if ( aObjList[ i ].equalsIgnoreAsciiCase( rCustomShape ) )
+ {
+ FmFormModel aFormModel;
+ SfxItemPool& rPool(aFormModel.GetItemPool());
+ rPool.FreezeIdRanges();
+
+ if ( GalleryExplorer::GetSdrObj( GALLERY_THEME_POWERPOINT, i, &aFormModel ) )
+ {
+ const SdrObject* pSourceObj = nullptr;
+ if (aFormModel.GetPageCount() > 0)
+ pSourceObj = aFormModel.GetPage( 0 )->GetObj( 0 );
+ SAL_WARN_IF(!pSourceObj, "svx.form", "No content in gallery custom shape '" << rCustomShape << "'" );
+ if( pSourceObj )
+ {
+ PropertyValue aPropVal_;
+ const SdrCustomShapeGeometryItem& rSourceGeometry = pSourceObj->GetMergedItem( SDRATTR_CUSTOMSHAPE_GEOMETRY );
+ const css::uno::Any* pAny = rSourceGeometry.GetPropertyValueByName( sType );
+ if ( pAny )
+ {
+ aPropVal_.Name = sType;
+ aPropVal_.Value = *pAny;
+ rGeometryItem.SetPropertyValue( aPropVal_ );
+ }
+ pAny = rSourceGeometry.GetPropertyValueByName( sAdjustmentValues );
+ if ( pAny )
+ {
+ aPropVal_.Name = sAdjustmentValues;
+ aPropVal_.Value = *pAny;
+ rGeometryItem.SetPropertyValue( aPropVal_ );
+ }
+ pAny = rSourceGeometry.GetPropertyValueByName( sCoordinateOrigin );
+ if ( pAny )
+ {
+ aPropVal_.Name = sCoordinateOrigin;
+ aPropVal_.Value = *pAny;
+ rGeometryItem.SetPropertyValue( aPropVal_ );
+ }
+ pAny = rSourceGeometry.GetPropertyValueByName( sCoordinateSize );
+ if ( pAny )
+ {
+ aPropVal_.Name = sCoordinateSize;
+ aPropVal_.Value = *pAny;
+ rGeometryItem.SetPropertyValue( aPropVal_ );
+ }
+ pAny = rSourceGeometry.GetPropertyValueByName( sEquations );
+ if ( pAny )
+ {
+ aPropVal_.Name = sEquations;
+ aPropVal_.Value = *pAny;
+ rGeometryItem.SetPropertyValue( aPropVal_ );
+ }
+ pAny = rSourceGeometry.GetPropertyValueByName( sHandles );
+ if ( pAny )
+ {
+ aPropVal_.Name = sHandles;
+ aPropVal_.Value = *pAny;
+ rGeometryItem.SetPropertyValue( aPropVal_ );
+ }
+ pAny = rSourceGeometry.GetPropertyValueByName( sPath );
+ if ( pAny )
+ {
+ aPropVal_.Name = sPath;
+ aPropVal_.Value = *pAny;
+ rGeometryItem.SetPropertyValue( aPropVal_ );
+ }
+ }
+ }
+ }
+ }
+}
+
+
+void FontworkBar::execute( SdrView& rSdrView, SfxRequest const & rReq, SfxBindings& rBindings )
+{
+ TranslateId pStrResId;
+
+ sal_uInt16 nSID = rReq.GetSlot();
+ switch( nSID )
+ {
+ case SID_FONTWORK_GALLERY_FLOATER:
+ {
+ std::shared_ptr<FontWorkGalleryDialog> pDlg = std::make_shared<FontWorkGalleryDialog>(rReq.GetFrameWeld(), rSdrView);
+ weld::DialogController::runAsync(pDlg, [](int){});
+ }
+ break;
+
+ case SID_FONTWORK_SHAPE_TYPE:
+ {
+ OUString aCustomShape;
+ const SfxItemSet* pArgs = rReq.GetArgs();
+ if ( pArgs )
+ {
+ const SfxStringItem& rItm = static_cast<const SfxStringItem&>(pArgs->Get( rReq.GetSlot() ));
+ aCustomShape = rItm.GetValue();
+ }
+ if ( !aCustomShape.isEmpty() )
+ {
+ const SdrMarkList& rMarkList = rSdrView.GetMarkedObjectList();
+ const size_t nCount = rMarkList.GetMarkCount();
+ for( size_t i = 0; i < nCount; ++i )
+ {
+ SdrObject* pObj = rMarkList.GetMark( i )->GetMarkedSdrObj();
+ if( auto pCustomShape = dynamic_cast<SdrObjCustomShape*>( pObj) )
+ {
+ const bool bUndo = rSdrView.IsUndoEnabled();
+
+ if( bUndo )
+ {
+ OUString aStr( SvxResId( RID_SVXSTR_UNDO_APPLY_FONTWORK_SHAPE ) );
+ rSdrView.BegUndo(aStr);
+ rSdrView.AddUndo(rSdrView.GetModel()->GetSdrUndoFactory().CreateUndoAttrObject(*pObj));
+ }
+ SdrCustomShapeGeometryItem aGeometryItem( pObj->GetMergedItem( SDRATTR_CUSTOMSHAPE_GEOMETRY ) );
+ GetGeometryForCustomShape( aGeometryItem, aCustomShape );
+ pObj->SetMergedItem( aGeometryItem );
+
+ Reference< drawing::XShape > aXShape = GetXShapeForSdrObject( pCustomShape );
+ if ( aXShape.is() )
+ {
+ Reference< drawing::XEnhancedCustomShapeDefaulter > xDefaulter( aXShape, UNO_QUERY );
+ if( xDefaulter.is() )
+ xDefaulter->createCustomShapeDefaults( aCustomShape );
+ }
+
+ pObj->BroadcastObjectChange();
+ if (bUndo)
+ rSdrView.EndUndo();
+ rSdrView.AdjustMarkHdl(); //HMH sal_True );
+ rBindings.Invalidate( SID_FONTWORK_SHAPE_TYPE );
+ }
+ }
+ }
+ }
+ break;
+
+ case SID_FONTWORK_CHARACTER_SPACING_DIALOG :
+ {
+ if( rReq.GetArgs() && ( rReq.GetArgs()->GetItemState( SID_FONTWORK_CHARACTER_SPACING ) == SfxItemState::SET ) )
+ {
+ sal_Int32 nCharSpacing = rReq.GetArgs()->GetItem<SfxInt32Item>(SID_FONTWORK_CHARACTER_SPACING)->GetValue();
+ FontworkCharacterSpacingDialog aDlg(rReq.GetFrameWeld(), nCharSpacing);
+ sal_uInt16 nRet = aDlg.run();
+ if (nRet != RET_CANCEL)
+ {
+ SfxInt32Item aItem(SID_FONTWORK_CHARACTER_SPACING, aDlg.getScale());
+ SfxPoolItem* aItems[] = { &aItem, nullptr };
+ rBindings.Execute( SID_FONTWORK_CHARACTER_SPACING, const_cast<const SfxPoolItem**>(aItems) );
+ }
+ }
+ }
+ break;
+
+ case SID_FONTWORK_SHAPE:
+ case SID_FONTWORK_ALIGNMENT:
+ {
+ if ( !pStrResId )
+ pStrResId = RID_SVXSTR_UNDO_APPLY_FONTWORK_ALIGNMENT;
+ [[fallthrough]];
+ }
+ case SID_FONTWORK_CHARACTER_SPACING:
+ {
+ if ( !pStrResId )
+ pStrResId = RID_SVXSTR_UNDO_APPLY_FONTWORK_CHARACTER_SPACING;
+ [[fallthrough]];
+ }
+ case SID_FONTWORK_KERN_CHARACTER_PAIRS:
+ {
+ if ( !pStrResId )
+ pStrResId = RID_SVXSTR_UNDO_APPLY_FONTWORK_CHARACTER_SPACING;
+ [[fallthrough]];
+ }
+ case SID_FONTWORK_SAME_LETTER_HEIGHTS:
+ {
+ if ( !pStrResId )
+ pStrResId = RID_SVXSTR_UNDO_APPLY_FONTWORK_SAME_LETTER_HEIGHT;
+
+ const SdrMarkList& rMarkList = rSdrView.GetMarkedObjectList();
+ const size_t nCount = rMarkList.GetMarkCount();
+ for( size_t i = 0; i < nCount; ++i )
+ {
+ SdrObject* pObj = rMarkList.GetMark(i)->GetMarkedSdrObj();
+ if( dynamic_cast<const SdrObjCustomShape*>( pObj) != nullptr )
+ {
+ const bool bUndo = rSdrView.IsUndoEnabled();
+ if( bUndo )
+ {
+ OUString aStr( SvxResId( pStrResId ) );
+ rSdrView.BegUndo(aStr);
+ rSdrView.AddUndo(rSdrView.GetModel()->GetSdrUndoFactory().CreateUndoAttrObject(*pObj));
+ }
+ SdrCustomShapeGeometryItem aGeometryItem( pObj->GetMergedItem( SDRATTR_CUSTOMSHAPE_GEOMETRY ) );
+ impl_execute( rReq, aGeometryItem, pObj );
+ pObj->SetMergedItem( aGeometryItem );
+ pObj->BroadcastObjectChange();
+ if (bUndo)
+ rSdrView.EndUndo();
+ }
+ }
+ }
+ break;
+ }
+}
+
+void FontworkBar::getState( SdrView const * pSdrView, SfxItemSet& rSet )
+{
+ if ( checkForSelectedFontWork( pSdrView ) )
+ {
+ SetAlignmentState( pSdrView, rSet );
+ SetCharacterSpacingState( pSdrView, rSet );
+ SetKernCharacterPairsState( pSdrView, rSet );
+ SetFontWorkShapeTypeState( pSdrView, rSet );
+ }
+ else
+ {
+ rSet.DisableItem( SID_FONTWORK_ALIGNMENT_FLOATER );
+ rSet.DisableItem( SID_FONTWORK_ALIGNMENT );
+ rSet.DisableItem( SID_FONTWORK_CHARACTER_SPACING_FLOATER );
+ rSet.DisableItem( SID_FONTWORK_CHARACTER_SPACING );
+ rSet.DisableItem( SID_FONTWORK_KERN_CHARACTER_PAIRS );
+ rSet.DisableItem( SID_FONTWORK_SAME_LETTER_HEIGHTS );
+ rSet.DisableItem( SID_FONTWORK_SHAPE_TYPE );
+ }
+}
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/uitest/sdrobject.cxx b/svx/source/uitest/sdrobject.cxx
new file mode 100644
index 000000000..bc72ee2a8
--- /dev/null
+++ b/svx/source/uitest/sdrobject.cxx
@@ -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/.
+ */
+
+#include <svx/uiobject.hxx>
+#include <svx/svdobj.hxx>
+#include <svx/SvxColorValueSet.hxx>
+#include <tools/fract.hxx>
+#include <vcl/window.hxx>
+#include <memory>
+
+SdrUIObject::~SdrUIObject() {}
+
+StringMap SdrUIObject::get_state()
+{
+ StringMap aMap;
+ SdrObject* pObject = get_object();
+
+ if (!pObject)
+ return aMap;
+
+ aMap["Name"] = pObject->GetName();
+ aMap["Description"] = pObject->GetDescription();
+ aMap["Title"] = pObject->GetTitle();
+ aMap["Z-Order"] = OUString::number(pObject->GetOrdNum());
+ aMap["Layer"] = OUString::number(pObject->GetLayer().get());
+ aMap["IsGroupObject"] = OUString::boolean(pObject->IsGroupObject());
+ aMap["IsPolyObject"] = OUString::boolean(pObject->IsPolyObj());
+ aMap["PointCount"] = OUString::number(pObject->GetPointCount());
+ aMap["HasTextEdit"] = OUString::boolean(pObject->HasTextEdit());
+ aMap["HasMacro"] = OUString::boolean(pObject->HasMacro());
+ aMap["IsClosed"] = OUString::boolean(pObject->IsClosedObj());
+ aMap["IsEdgeObject"] = OUString::boolean(pObject->IsEdgeObj());
+ aMap["Is3DObject"] = OUString::boolean(pObject->Is3DObj());
+ aMap["IsUNOObject"] = OUString::boolean(pObject->IsUnoObj());
+ aMap["MoveProtected"] = OUString::boolean(pObject->IsMoveProtect());
+ aMap["ResizeProtected"] = OUString::boolean(pObject->IsResizeProtect());
+ aMap["Printable"] = OUString::boolean(pObject->IsPrintable());
+ aMap["Visible"] = OUString::boolean(pObject->IsVisible());
+ aMap["HasText"] = OUString::boolean(pObject->HasText());
+
+ return aMap;
+}
+
+void SdrUIObject::execute(const OUString& rAction, const StringMap& rParameters)
+{
+ SdrObject* pObj = get_object();
+ if (!pObj)
+ return;
+
+ if (rAction == "MOVE")
+ {
+ auto itrNX = rParameters.find("X");
+ if (itrNX == rParameters.end())
+ throw css::uno::RuntimeException("missing parameter X");
+
+ auto itrNY = rParameters.find("Y");
+ if (itrNY == rParameters.end())
+ throw css::uno::RuntimeException("missing parameter Y");
+
+ tools::Long nX = itrNX->second.toInt32();
+ tools::Long nY = itrNY->second.toInt32();
+ Size aMoveRange(nX, nY);
+ pObj->Move(aMoveRange);
+ }
+ else if (rAction == "RESIZE")
+ {
+ auto itrNX = rParameters.find("X");
+ if (itrNX == rParameters.end())
+ throw css::uno::RuntimeException("missing parameter X");
+
+ auto itrNY = rParameters.find("Y");
+ if (itrNY == rParameters.end())
+ throw css::uno::RuntimeException("missing parameter Y");
+
+ tools::Long nX = itrNX->second.toInt32();
+ tools::Long nY = itrNY->second.toInt32();
+ Point aPos(nX, nY);
+
+ auto itrFracX = rParameters.find("FRAC_X");
+ if (itrFracX == rParameters.end())
+ throw css::uno::RuntimeException("missing parameter FRAC_X");
+ double nFracX = itrFracX->second.toDouble();
+ Fraction aFracX(nFracX);
+
+ auto itrFracY = rParameters.find("FRAC_Y");
+ if (itrFracY == rParameters.end())
+ throw css::uno::RuntimeException("missing parameter FRAC_Y");
+ double nFracY = itrFracY->second.toDouble();
+ Fraction aFracY(nFracY);
+ pObj->Resize(aPos, aFracX, aFracY, true /*bRelative*/);
+ }
+ else if (rAction == "CROP")
+ {
+ // RotateFlyFrame3: Note: Crop does nothing at SdrObject
+ // anymore, see comment at SdrObject::NbcCrop
+ auto itrNX = rParameters.find("X");
+ if (itrNX == rParameters.end())
+ throw css::uno::RuntimeException("missing parameter X");
+
+ auto itrNY = rParameters.find("Y");
+ if (itrNY == rParameters.end())
+ throw css::uno::RuntimeException("missing parameter Y");
+
+ const double fX(itrNX->second.toDouble());
+ const double fY(itrNY->second.toDouble());
+ const basegfx::B2DPoint aPos(fX, fY);
+
+ auto itrFracX = rParameters.find("FRAC_X");
+ if (itrFracX == rParameters.end())
+ throw css::uno::RuntimeException("missing parameter FRAC_X");
+ const double fFracX(itrFracX->second.toDouble());
+
+ auto itrFracY = rParameters.find("FRAC_Y");
+ if (itrFracY == rParameters.end())
+ throw css::uno::RuntimeException("missing parameter FRAC_Y");
+ const double fFracY(itrFracY->second.toDouble());
+
+ pObj->Crop(aPos, fFracX, fFracY);
+ }
+ else if (rAction == "ROTATE")
+ {
+ auto itrNX = rParameters.find("X");
+ if (itrNX == rParameters.end())
+ throw css::uno::RuntimeException("missing parameter X");
+
+ auto itrNY = rParameters.find("Y");
+ if (itrNY == rParameters.end())
+ throw css::uno::RuntimeException("missing parameter Y");
+
+ tools::Long nX = itrNX->second.toInt32();
+ tools::Long nY = itrNY->second.toInt32();
+ Point aPos(nX, nY);
+
+ auto itrAngle = rParameters.find("ANGLE");
+ if (itrAngle == rParameters.end())
+ throw css::uno::RuntimeException("missing parameter ANGLE");
+
+ double nAngle = itrAngle->second.toDouble();
+ pObj->Rotate(aPos, Degree100(sal_Int32(nAngle)), 0, 0);
+ }
+ else if (rAction == "Mirror")
+ {
+ pObj->Mirror(Point(), Point());
+ }
+ else if (rAction == "SHEAR")
+ {
+ pObj->Shear(Point(), 0_deg100 /*nAngle*/, 0, false);
+ }
+}
+
+OUString SdrUIObject::get_type() const { return "SdrUIObject"; }
+
+SvxColorValueSetUIObject::SvxColorValueSetUIObject(vcl::Window* pColorSetWin)
+ : DrawingAreaUIObject(pColorSetWin)
+ , mpColorSet(static_cast<SvxColorValueSet*>(mpController))
+{
+}
+
+void SvxColorValueSetUIObject::execute(const OUString& rAction, const StringMap& rParameters)
+{
+ if (rAction == "CHOOSE")
+ {
+ if (rParameters.find("POS") != rParameters.end())
+ {
+ OUString aIndexStr = rParameters.find("POS")->second;
+ sal_Int32 nIndex = aIndexStr.toInt32();
+ mpColorSet->SelectItem(nIndex);
+ mpColorSet->Select();
+ }
+ }
+ else
+ DrawingAreaUIObject::execute(rAction, rParameters);
+}
+
+std::unique_ptr<UIObject> SvxColorValueSetUIObject::create(vcl::Window* pWindow)
+{
+ return std::unique_ptr<UIObject>(new SvxColorValueSetUIObject(pWindow));
+}
+
+OUString SvxColorValueSetUIObject::get_name() const { return "SvxColorValueSetUIObject"; }
+
+StringMap SvxColorValueSetUIObject::get_state()
+{
+ StringMap aMap = DrawingAreaUIObject::get_state();
+ aMap["CurrColorId"] = OUString::number(mpColorSet->GetSelectedItemId());
+ aMap["CurrColorPos"] = OUString::number(mpColorSet->GetSelectItemPos());
+ aMap["ColorsCount"] = OUString::number(mpColorSet->GetItemCount());
+ aMap["ColCount"] = OUString::number(mpColorSet->GetColCount());
+ aMap["ColorText"] = mpColorSet->GetItemText(mpColorSet->GetSelectedItemId());
+ Color currColor = mpColorSet->GetItemColor(mpColorSet->GetSelectedItemId());
+ aMap["R"] = OUString::number(currColor.GetRed());
+ aMap["G"] = OUString::number(currColor.GetGreen());
+ aMap["B"] = OUString::number(currColor.GetBlue());
+ aMap["RGB"] = "(" + OUString::number(currColor.GetRed()) + ","
+ + OUString::number(currColor.GetGreen()) + ","
+ + OUString::number(currColor.GetBlue()) + ")";
+ return aMap;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/uitest/uiobject.cxx b/svx/source/uitest/uiobject.cxx
new file mode 100644
index 000000000..1c56b9b61
--- /dev/null
+++ b/svx/source/uitest/uiobject.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/.
+ */
+
+#include <memory>
+#include <uiobject.hxx>
+#include <svx/charmap.hxx>
+#include <svx/numvset.hxx>
+#include <vcl/window.hxx>
+
+SvxShowCharSetUIObject::SvxShowCharSetUIObject(const VclPtr<vcl::Window>& rCharSetWin)
+ : DrawingAreaUIObject(rCharSetWin)
+ , mpCharSet(static_cast<SvxShowCharSet*>(mpController))
+{
+}
+
+void SvxShowCharSetUIObject::execute(const OUString& rAction,
+ const StringMap& rParameters)
+{
+ if (rAction == "SELECT")
+ {
+ if (rParameters.find("INDEX") != rParameters.end())
+ {
+ OUString aIndexStr = rParameters.find("INDEX")->second;
+
+ sal_Int32 nIndex = aIndexStr.toInt32();
+ mpCharSet->OutputIndex(nIndex);
+ }
+ else if (rParameters.find("COLUMN") != rParameters.end() &&
+ rParameters.find("ROW") != rParameters.end())
+ {
+ OUString aColStr = rParameters.find("COLUMN")->second;
+ OUString aRowStr = rParameters.find("ROW")->second;
+
+ sal_Int32 nColumn = aColStr.toInt32();
+ sal_Int32 nRow = aRowStr.toInt32();
+
+ sal_Int32 nIndex = nColumn * COLUMN_COUNT + nRow;
+ mpCharSet->OutputIndex(nIndex);
+ }
+ }
+ else
+ WindowUIObject::execute(rAction, rParameters);
+}
+
+std::unique_ptr<UIObject> SvxShowCharSetUIObject::create(vcl::Window* pWindow)
+{
+ return std::unique_ptr<UIObject>(new SvxShowCharSetUIObject(pWindow));
+}
+
+OUString SvxShowCharSetUIObject::get_name() const
+{
+ return "SvxShowCharSetUIObject";
+}
+
+
+SvxNumValueSetUIObject::SvxNumValueSetUIObject(vcl::Window* pNumValueSetWin)
+ : DrawingAreaUIObject(pNumValueSetWin)
+ , mpNumValueSet(static_cast<SvxNumValueSet*>(mpController))
+{
+}
+
+void SvxNumValueSetUIObject::execute(const OUString& rAction,
+ const StringMap& rParameters)
+{
+ if (rAction == "CHOOSE")
+ {
+ if (rParameters.find("POS") != rParameters.end())
+ {
+ OUString aIndexStr = rParameters.find("POS")->second;
+ sal_Int32 nIndex = aIndexStr.toInt32();
+ mpNumValueSet->SelectItem(nIndex);
+ mpNumValueSet->Select();
+ }
+ }
+ else
+ DrawingAreaUIObject::execute(rAction, rParameters);
+}
+
+std::unique_ptr<UIObject> SvxNumValueSetUIObject::create(vcl::Window* pWindow)
+{
+ return std::unique_ptr<UIObject>(new SvxNumValueSetUIObject(pWindow));
+}
+
+OUString SvxNumValueSetUIObject::get_name() const
+{
+ return "SvxNumValueSetUIObject";
+}
+
+StringMap SvxNumValueSetUIObject::get_state()
+{
+ StringMap aMap = WindowUIObject::get_state();
+ aMap["SelectedItemId"] = OUString::number( mpNumValueSet->GetSelectedItemId() );
+ aMap["SelectedItemPos"] = OUString::number( mpNumValueSet->GetSelectItemPos() );
+ aMap["ItemsCount"] = OUString::number(mpNumValueSet->GetItemCount());
+ aMap["ItemText"] = mpNumValueSet->GetItemText(mpNumValueSet->GetSelectedItemId());
+ return aMap;
+}
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/unodialogs/textconversiondlgs/chinese_dictionarydialog.cxx b/svx/source/unodialogs/textconversiondlgs/chinese_dictionarydialog.cxx
new file mode 100644
index 000000000..ed81ba92f
--- /dev/null
+++ b/svx/source/unodialogs/textconversiondlgs/chinese_dictionarydialog.cxx
@@ -0,0 +1,676 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+
+#include "chinese_dictionarydialog.hxx"
+#include <cppuhelper/bootstrap.hxx>
+#include <com/sun/star/i18n/TextConversionOption.hpp>
+#include <com/sun/star/linguistic2/ConversionDictionaryType.hpp>
+#include <com/sun/star/linguistic2/ConversionPropertyType.hpp>
+#include <com/sun/star/linguistic2/ConversionDictionaryList.hpp>
+#include <com/sun/star/linguistic2/XConversionPropertyType.hpp>
+#include <com/sun/star/util/XFlushable.hpp>
+#include <com/sun/star/lang/Locale.hpp>
+#include <o3tl/safeint.hxx>
+#include <unotools/lingucfg.hxx>
+#include <unotools/linguprops.hxx>
+#include <osl/diagnose.h>
+
+namespace textconversiondlgs
+{
+
+using namespace ::com::sun::star;
+using namespace ::com::sun::star::uno;
+
+DictionaryList::DictionaryList(std::unique_ptr<weld::TreeView> xControl)
+ : m_xControl(std::move(xControl))
+ , m_xIter(m_xControl->make_iterator())
+ , m_pED_Term(nullptr)
+ , m_pED_Mapping(nullptr)
+ , m_pLB_Property(nullptr)
+{
+ m_xControl->make_sorted();
+}
+
+OUString DictionaryList::getPropertyTypeName( sal_Int16 nConversionPropertyType ) const
+{
+ if (!m_pLB_Property || !m_pLB_Property->get_count())
+ return OUString();
+
+ sal_uInt16 nPos = static_cast<sal_uInt16>( nConversionPropertyType )-1;
+ if (nPos < m_pLB_Property->get_count())
+ return m_pLB_Property->get_text(nPos);
+ return m_pLB_Property->get_text(0);
+}
+
+void DictionaryList::save()
+{
+ if (!m_xDictionary.is())
+ return;
+
+ Reference< linguistic2::XConversionPropertyType > xPropertyType( m_xDictionary, uno::UNO_QUERY );
+
+ sal_Int32 nN;
+ DictionaryEntry* pE;
+
+ for( nN = m_aToBeDeleted.size(); nN--; )
+ {
+ pE = m_aToBeDeleted[nN];
+ m_xDictionary->removeEntry( pE->m_aTerm, pE->m_aMapping );
+ }
+ int nRowCount = m_xControl->n_children();
+ for( nN = nRowCount; nN--; )
+ {
+ pE = getEntryOnPos( nN );
+ if(pE->m_bNewEntry)
+ {
+ try
+ {
+ m_xDictionary->addEntry( pE->m_aTerm, pE->m_aMapping );
+ xPropertyType->setPropertyType( pE->m_aTerm, pE->m_aMapping, pE->m_nConversionPropertyType );
+ }
+ catch( uno::Exception& )
+ {
+
+ }
+ }
+ }
+ Reference< util::XFlushable > xFlush( m_xDictionary, uno::UNO_QUERY );
+ if( xFlush.is() )
+ xFlush->flush();
+}
+
+void DictionaryList::deleteAll()
+{
+ sal_Int32 nN;
+ int nRowCount = m_xControl->n_children();
+ for( nN = nRowCount; nN--; )
+ deleteEntryOnPos( nN );
+ for( nN = m_aToBeDeleted.size(); nN--; )
+ {
+ DictionaryEntry* pE = m_aToBeDeleted[nN];
+ delete pE;
+ }
+ m_aToBeDeleted.clear();
+}
+
+void DictionaryList::refillFromDictionary( sal_Int32 nTextConversionOptions )
+{
+ deleteAll();
+
+ if(!m_xDictionary.is())
+ return;
+
+ const Sequence< OUString > aLeftList( m_xDictionary->getConversionEntries( linguistic2::ConversionDirection_FROM_LEFT ) );
+
+ Reference< linguistic2::XConversionPropertyType > xPropertyType( m_xDictionary, uno::UNO_QUERY );
+
+ OUString aRight;
+ sal_Int16 nConversionPropertyType;
+
+ for(const OUString& aLeft : aLeftList)
+ {
+ Sequence< OUString > aRightList( m_xDictionary->getConversions(
+ aLeft, 0, aLeft.getLength()
+ , linguistic2::ConversionDirection_FROM_LEFT, nTextConversionOptions ) );
+
+ if(aRightList.getLength()!=1)
+ {
+ OSL_FAIL("The Chinese Translation Dictionary should have exactly one Mapping for each term.");
+ continue;
+ }
+
+ aRight = aRightList[0];
+ nConversionPropertyType = linguistic2::ConversionPropertyType::OTHER;
+ if(xPropertyType.is())
+ nConversionPropertyType = xPropertyType->getPropertyType(aLeft, aRight);
+
+ DictionaryEntry* pEntry = new DictionaryEntry( aLeft, aRight, nConversionPropertyType );
+
+ m_xControl->append(m_xIter.get());
+ m_xControl->set_text(*m_xIter, pEntry->m_aTerm, 0);
+ m_xControl->set_text(*m_xIter, pEntry->m_aMapping, 1);
+ m_xControl->set_text(*m_xIter, getPropertyTypeName(pEntry->m_nConversionPropertyType), 2);
+ m_xControl->set_id(*m_xIter, weld::toId(pEntry));
+ }
+}
+
+DictionaryEntry* DictionaryList::getFirstSelectedEntry() const
+{
+ DictionaryEntry* pRet=nullptr;
+ int nN = m_xControl->get_selected_index();
+ if (nN != -1)
+ pRet = getEntryOnPos( nN );
+ return pRet;
+}
+
+DictionaryEntry* DictionaryList::getEntryOnPos(sal_Int32 nPos) const
+{
+ OUString sLBEntry = m_xControl->get_id(nPos);
+ return weld::fromId<DictionaryEntry*>(sLBEntry);
+}
+
+DictionaryEntry* DictionaryList::getTermEntry( std::u16string_view rTerm ) const
+{
+ int nRowCount = m_xControl->n_children();
+ for( sal_Int32 nN = nRowCount; nN--; )
+ {
+ DictionaryEntry* pE = getEntryOnPos( nN );
+ if( pE && rTerm == pE->m_aTerm )
+ return pE;
+ }
+ return nullptr;
+}
+
+bool DictionaryList::hasTerm( std::u16string_view rTerm ) const
+{
+ return getTermEntry(rTerm) !=nullptr ;
+}
+
+void DictionaryList::addEntry(const OUString& rTerm, const OUString& rMapping,
+ sal_Int16 nConversionPropertyType, int nPos)
+{
+ if( hasTerm( rTerm ) )
+ return;
+
+ DictionaryEntry* pEntry = new DictionaryEntry( rTerm, rMapping, nConversionPropertyType, true );
+ m_xControl->insert(nPos, m_xIter.get());
+ m_xControl->set_text(*m_xIter, pEntry->m_aTerm, 0);
+ m_xControl->set_text(*m_xIter, pEntry->m_aMapping, 1);
+ m_xControl->set_text(*m_xIter, getPropertyTypeName(pEntry->m_nConversionPropertyType), 2);
+ m_xControl->set_id(*m_xIter, weld::toId(pEntry));
+ m_xControl->select(*m_xIter);
+}
+
+void DictionaryList::deleteEntryOnPos( sal_Int32 nPos )
+{
+ DictionaryEntry* pEntry = getEntryOnPos( nPos );
+ m_xControl->remove(nPos);
+ if (pEntry)
+ {
+ if( pEntry->m_bNewEntry )
+ delete pEntry;
+ else
+ m_aToBeDeleted.push_back( pEntry );
+ }
+}
+
+int DictionaryList::deleteEntries( std::u16string_view rTerm )
+{
+ int nPos = -1;
+ int nRowCount = m_xControl->n_children();
+ for (sal_Int32 nN = nRowCount; nN--;)
+ {
+ DictionaryEntry* pCurEntry = getEntryOnPos( nN );
+ if( rTerm == pCurEntry->m_aTerm )
+ {
+ nPos = nN;
+ m_xControl->remove(nN);
+ if( pCurEntry->m_bNewEntry )
+ delete pCurEntry;
+ else
+ m_aToBeDeleted.push_back( pCurEntry );
+ }
+ }
+ return nPos;
+}
+
+DictionaryEntry::DictionaryEntry( const OUString& rTerm, const OUString& rMapping
+ , sal_Int16 nConversionPropertyType
+ , bool bNewEntry )
+ : m_aTerm( rTerm )
+ , m_aMapping( rMapping )
+ , m_nConversionPropertyType( nConversionPropertyType )
+ , m_bNewEntry( bNewEntry )
+{
+ if( m_nConversionPropertyType == 0 )
+ m_nConversionPropertyType = 1;
+}
+
+DictionaryEntry::~DictionaryEntry()
+{
+}
+
+IMPL_LINK_NOARG(ChineseDictionaryDialog, SizeAllocHdl, const Size&, void)
+{
+ DictionaryList* pControl = m_xCT_DictionaryToTraditional->get_visible() ?
+ m_xCT_DictionaryToTraditional.get() :
+ m_xCT_DictionaryToSimplified.get();
+ std::vector<int> aWidths;
+ int x1, x2, y, width, height;
+ if (!m_xED_Mapping->get_extents_relative_to(pControl->get_widget(), x1, y, width, height))
+ return;
+ aWidths.push_back(x1);
+ if (!m_xLB_Property->get_extents_relative_to(pControl->get_widget(), x2, y, width, height))
+ return;
+ aWidths.push_back(x2 - x1);
+ m_xCT_DictionaryToTraditional->get_widget().set_column_fixed_widths(aWidths);
+ m_xCT_DictionaryToSimplified->get_widget().set_column_fixed_widths(aWidths);
+}
+
+void DictionaryList::init(const Reference< linguistic2::XConversionDictionary>& xDictionary,
+ weld::Entry *pED_Term, weld::Entry *pED_Mapping, weld::ComboBox *pLB_Property)
+{
+ if (m_xDictionary.is())
+ return;
+
+ m_xDictionary = xDictionary;
+
+ m_pED_Term = pED_Term;
+ m_pED_Mapping = pED_Mapping;
+ m_pLB_Property = pLB_Property;
+
+ m_xControl->set_sort_column(0);
+ m_xControl->set_sort_indicator(TRISTATE_TRUE, 0);
+
+ std::vector<int> aWidths
+ {
+ o3tl::narrowing<int>(m_pED_Term->get_preferred_size().Width()),
+ o3tl::narrowing<int>(m_pED_Mapping->get_preferred_size().Width())
+ };
+ m_xControl->set_column_fixed_widths(aWidths);
+}
+
+void ChineseDictionaryDialog::initDictionaryControl(DictionaryList *pList,
+ const Reference< linguistic2::XConversionDictionary>& xDictionary)
+{
+ //set widgets to track the width of for columns
+ pList->init(xDictionary,
+ m_xED_Term.get(), m_xED_Mapping.get(), m_xLB_Property.get());
+}
+
+ChineseDictionaryDialog::ChineseDictionaryDialog(weld::Window* pParent)
+ : GenericDialogController(pParent, "svx/ui/chinesedictionary.ui", "ChineseDictionaryDialog")
+ , m_nTextConversionOptions(i18n::TextConversionOption::NONE)
+ , m_xRB_To_Simplified(m_xBuilder->weld_radio_button("tradtosimple"))
+ , m_xRB_To_Traditional(m_xBuilder->weld_radio_button("simpletotrad"))
+ , m_xCB_Reverse(m_xBuilder->weld_check_button("reverse"))
+ , m_xFT_Term(m_xBuilder->weld_label("termft"))
+ , m_xED_Term(m_xBuilder->weld_entry("term"))
+ , m_xFT_Mapping(m_xBuilder->weld_label("mappingft"))
+ , m_xED_Mapping(m_xBuilder->weld_entry("mapping"))
+ , m_xFT_Property(m_xBuilder->weld_label("propertyft"))
+ , m_xLB_Property(m_xBuilder->weld_combo_box("property"))
+ , m_xCT_DictionaryToSimplified(new DictionaryList(m_xBuilder->weld_tree_view("tradtosimpleview")))
+ , m_xCT_DictionaryToTraditional(new DictionaryList(m_xBuilder->weld_tree_view("simpletotradview")))
+ , m_xPB_Add(m_xBuilder->weld_button("add"))
+ , m_xPB_Modify(m_xBuilder->weld_button("modify"))
+ , m_xPB_Delete(m_xBuilder->weld_button("delete"))
+{
+ m_xCT_DictionaryToSimplified->set_size_request(-1, m_xCT_DictionaryToSimplified->get_height_rows(8));
+ m_xCT_DictionaryToTraditional->set_size_request(-1, m_xCT_DictionaryToTraditional->get_height_rows(8));
+
+ SvtLinguConfig aLngCfg;
+ bool bValue;
+ Any aAny( aLngCfg.GetProperty( UPN_IS_REVERSE_MAPPING ) );
+ if( aAny >>= bValue )
+ m_xCB_Reverse->set_active( bValue );
+
+ m_xLB_Property->set_active(0);
+
+ Reference< linguistic2::XConversionDictionary > xDictionary_To_Simplified;
+ Reference< linguistic2::XConversionDictionary > xDictionary_To_Traditional;
+ //get dictionaries
+ {
+ if(!m_xContext.is())
+ m_xContext.set( ::cppu::defaultBootstrap_InitialComponentContext() );
+ if(m_xContext.is())
+ {
+ Reference< linguistic2::XConversionDictionaryList > xDictionaryList = linguistic2::ConversionDictionaryList::create(m_xContext);
+ Reference< container::XNameContainer > xContainer( xDictionaryList->getDictionaryContainer() );
+ if(xContainer.is())
+ {
+ try
+ {
+ OUString aNameTo_Simplified("ChineseT2S");
+ OUString aNameTo_Traditional("ChineseS2T");
+ lang::Locale aLocale;
+ aLocale.Language = "zh";
+
+ if( xContainer->hasByName( aNameTo_Simplified ) )
+ xDictionary_To_Simplified.set(
+ xContainer->getByName( aNameTo_Simplified ), UNO_QUERY );
+ else
+ {
+ aLocale.Country = "TW";
+ xDictionary_To_Simplified =
+ xDictionaryList->addNewDictionary( aNameTo_Simplified
+ , aLocale, linguistic2::ConversionDictionaryType::SCHINESE_TCHINESE
+ );
+ }
+ if (xDictionary_To_Simplified.is())
+ xDictionary_To_Simplified->setActive( true );
+
+
+ if( xContainer->hasByName( aNameTo_Traditional ) )
+ xDictionary_To_Traditional.set(
+ xContainer->getByName( aNameTo_Traditional ), UNO_QUERY );
+ else
+ {
+ aLocale.Country = "CN";
+ xDictionary_To_Traditional =
+ xDictionaryList->addNewDictionary( aNameTo_Traditional
+ ,aLocale, linguistic2::ConversionDictionaryType::SCHINESE_TCHINESE);
+ }
+ if (xDictionary_To_Traditional.is())
+ xDictionary_To_Traditional->setActive( true );
+
+ }
+ catch(const uno::Exception& )
+ {
+ }
+ }
+ }
+ }
+
+ //init dictionary controls
+ initDictionaryControl(m_xCT_DictionaryToSimplified.get(), xDictionary_To_Simplified);
+ initDictionaryControl(m_xCT_DictionaryToTraditional.get(), xDictionary_To_Traditional);
+
+ //set hdl
+ m_xCT_DictionaryToSimplified->connect_column_clicked(LINK(this, ChineseDictionaryDialog, ToSimplifiedHeaderBarClick));
+ m_xCT_DictionaryToTraditional->connect_column_clicked(LINK(this, ChineseDictionaryDialog, ToTraditionalHeaderBarClick));
+
+ updateAfterDirectionChange();
+
+ m_xED_Term->connect_changed( LINK( this, ChineseDictionaryDialog, EditFieldsHdl ) );
+ m_xED_Mapping->connect_changed( LINK( this, ChineseDictionaryDialog, EditFieldsHdl ) );
+ m_xLB_Property->connect_changed( LINK( this, ChineseDictionaryDialog, EditFieldsListBoxHdl ) );
+
+ m_xRB_To_Simplified->connect_toggled( LINK( this, ChineseDictionaryDialog, DirectionHdl ) );
+
+ m_xCT_DictionaryToSimplified->connect_changed( LINK( this, ChineseDictionaryDialog, MappingSelectHdl ));
+ m_xCT_DictionaryToTraditional->connect_changed( LINK( this, ChineseDictionaryDialog, MappingSelectHdl ));
+
+ m_xPB_Add->connect_clicked( LINK( this, ChineseDictionaryDialog, AddHdl ) );
+ m_xPB_Modify->connect_clicked( LINK( this, ChineseDictionaryDialog, ModifyHdl ) );
+ m_xPB_Delete->connect_clicked( LINK( this, ChineseDictionaryDialog, DeleteHdl ) );
+
+ m_xED_Mapping->connect_size_allocate(LINK(this, ChineseDictionaryDialog, SizeAllocHdl));
+ m_xLB_Property->connect_size_allocate(LINK(this, ChineseDictionaryDialog, SizeAllocHdl));
+}
+
+ChineseDictionaryDialog::~ChineseDictionaryDialog()
+{
+}
+
+void ChineseDictionaryDialog::setDirectionAndTextConversionOptions( bool bDirectionToSimplified, sal_Int32 nTextConversionOptions /*i18n::TextConversionOption*/ )
+{
+ if( bDirectionToSimplified == m_xRB_To_Simplified->get_active()
+ && nTextConversionOptions == m_nTextConversionOptions )
+ return;
+
+ m_nTextConversionOptions = nTextConversionOptions;
+
+ if (bDirectionToSimplified)
+ m_xRB_To_Simplified->set_active(true);
+ else
+ m_xRB_To_Traditional->set_active(true);
+ updateAfterDirectionChange();
+}
+
+IMPL_LINK_NOARG(ChineseDictionaryDialog, DirectionHdl, weld::Toggleable&, void)
+{
+ updateAfterDirectionChange();
+}
+
+void ChineseDictionaryDialog::updateAfterDirectionChange()
+{
+ Reference< linguistic2::XConversionDictionary > xDictionary;
+
+ if (m_xRB_To_Simplified->get_active())
+ {
+ m_xCT_DictionaryToTraditional->hide();
+ m_xCT_DictionaryToSimplified->show();
+ xDictionary = m_xCT_DictionaryToSimplified->m_xDictionary;
+ }
+ else
+ {
+ m_xCT_DictionaryToSimplified->hide();
+ m_xCT_DictionaryToTraditional->show();
+ xDictionary = m_xCT_DictionaryToTraditional->m_xDictionary;
+ }
+
+ updateButtons();
+}
+
+IMPL_LINK_NOARG(ChineseDictionaryDialog, EditFieldsListBoxHdl, weld::ComboBox&, void)
+{
+ updateButtons();
+}
+
+IMPL_LINK_NOARG(ChineseDictionaryDialog, EditFieldsHdl, weld::Entry&, void)
+{
+ updateButtons();
+}
+
+IMPL_LINK_NOARG(ChineseDictionaryDialog, MappingSelectHdl, weld::TreeView&, void)
+{
+ DictionaryEntry* pE = getActiveDictionary().getFirstSelectedEntry();
+ if (pE)
+ {
+ m_xED_Term->set_text( pE->m_aTerm );
+ m_xED_Mapping->set_text( pE->m_aMapping );
+ sal_Int16 nPos = pE->m_nConversionPropertyType-1;
+ if (nPos<0 || nPos>=m_xLB_Property->get_count())
+ nPos=0;
+ if (m_xLB_Property->get_count())
+ m_xLB_Property->set_active(nPos);
+ }
+
+ updateButtons();
+}
+
+bool ChineseDictionaryDialog::isEditFieldsHaveContent() const
+{
+ return !m_xED_Term->get_text().isEmpty() && !m_xED_Mapping->get_text().isEmpty();
+}
+
+bool ChineseDictionaryDialog::isEditFieldsContentEqualsSelectedListContent() const
+{
+ DictionaryEntry* pE = getActiveDictionary().getFirstSelectedEntry();
+ if( pE )
+ {
+ if (pE->m_aTerm != m_xED_Term->get_text())
+ return false;
+ if (pE->m_aMapping != m_xED_Mapping->get_text())
+ return false;
+ if (pE->m_nConversionPropertyType != m_xLB_Property->get_active() + 1)
+ return false;
+ return true;
+ }
+ return false;
+}
+
+const DictionaryList& ChineseDictionaryDialog::getActiveDictionary() const
+{
+ if( m_xRB_To_Traditional->get_active() )
+ return *m_xCT_DictionaryToTraditional;
+ return *m_xCT_DictionaryToSimplified;
+}
+
+DictionaryList& ChineseDictionaryDialog::getActiveDictionary()
+{
+ if( m_xRB_To_Traditional->get_active() )
+ return *m_xCT_DictionaryToTraditional;
+ return *m_xCT_DictionaryToSimplified;
+}
+
+const DictionaryList& ChineseDictionaryDialog::getReverseDictionary() const
+{
+ if( m_xRB_To_Traditional->get_active() )
+ return *m_xCT_DictionaryToSimplified;
+ return *m_xCT_DictionaryToTraditional;
+}
+
+DictionaryList& ChineseDictionaryDialog::getReverseDictionary()
+{
+ if( m_xRB_To_Traditional->get_active() )
+ return *m_xCT_DictionaryToSimplified;
+ return *m_xCT_DictionaryToTraditional;
+}
+
+void ChineseDictionaryDialog::updateButtons()
+{
+ bool bAdd = isEditFieldsHaveContent() && !getActiveDictionary().hasTerm(m_xED_Term->get_text());
+ m_xPB_Add->set_sensitive( bAdd );
+
+ m_xPB_Delete->set_sensitive(!bAdd && getActiveDictionary().get_selected_index() != -1);
+
+ bool bModify = false;
+ {
+ DictionaryEntry* pFirstSelectedEntry = getActiveDictionary().getFirstSelectedEntry();
+ bModify = !bAdd && pFirstSelectedEntry && pFirstSelectedEntry->m_aTerm == m_xED_Term->get_text();
+ if( bModify && isEditFieldsContentEqualsSelectedListContent() )
+ bModify = false;
+ }
+ m_xPB_Modify->set_sensitive( bModify );
+}
+
+IMPL_LINK_NOARG(ChineseDictionaryDialog, AddHdl, weld::Button&, void)
+{
+ if( !isEditFieldsHaveContent() )
+ return;
+
+ sal_Int16 nConversionPropertyType = m_xLB_Property->get_active() + 1;
+
+ getActiveDictionary().addEntry( m_xED_Term->get_text(), m_xED_Mapping->get_text(), nConversionPropertyType );
+
+ if( m_xCB_Reverse->get_active() )
+ {
+ getReverseDictionary().deleteEntries( m_xED_Mapping->get_text() );
+ getReverseDictionary().addEntry( m_xED_Mapping->get_text(), m_xED_Term->get_text(), nConversionPropertyType );
+ }
+
+ updateButtons();
+}
+
+IMPL_LINK_NOARG(ChineseDictionaryDialog, ModifyHdl, weld::Button&, void)
+{
+ OUString aTerm( m_xED_Term->get_text() );
+ OUString aMapping( m_xED_Mapping->get_text() );
+ sal_Int16 nConversionPropertyType = m_xLB_Property->get_active() + 1;
+
+ DictionaryList& rActive = getActiveDictionary();
+ DictionaryList& rReverse = getReverseDictionary();
+
+ DictionaryEntry* pE = rActive.getFirstSelectedEntry();
+ if( pE && pE->m_aTerm != aTerm )
+ return;
+
+ if( pE )
+ {
+ if( pE->m_aMapping != aMapping || pE->m_nConversionPropertyType != nConversionPropertyType )
+ {
+ if( m_xCB_Reverse->get_active() )
+ {
+ rReverse.deleteEntries( pE->m_aMapping );
+ int nPos = rReverse.deleteEntries( aMapping );
+ rReverse.addEntry( aMapping, aTerm, nConversionPropertyType, nPos );
+ }
+
+ int nPos = rActive.deleteEntries( aTerm );
+ rActive.addEntry( aTerm, aMapping, nConversionPropertyType, nPos );
+ }
+ }
+
+ updateButtons();
+}
+
+IMPL_LINK_NOARG(ChineseDictionaryDialog, DeleteHdl, weld::Button&, void)
+{
+ DictionaryList& rActive = getActiveDictionary();
+ DictionaryList& rReverse = getReverseDictionary();
+
+ int nEntry = rActive.get_selected_index();
+ if (nEntry != -1)
+ {
+ DictionaryEntry* pEntry = rActive.getEntryOnPos(nEntry);
+ if (pEntry)
+ {
+ OUString aMapping = pEntry->m_aMapping;
+ rActive.deleteEntryOnPos(nEntry);
+ if (m_xCB_Reverse->get_active())
+ rReverse.deleteEntries(aMapping);
+ }
+ }
+
+ updateButtons();
+}
+
+short ChineseDictionaryDialog::run()
+{
+ sal_Int32 nTextConversionOptions = m_nTextConversionOptions;
+ if(m_nTextConversionOptions & i18n::TextConversionOption::USE_CHARACTER_VARIANTS )
+ nTextConversionOptions = nTextConversionOptions^i18n::TextConversionOption::USE_CHARACTER_VARIANTS ;
+
+ m_xCT_DictionaryToSimplified->refillFromDictionary( nTextConversionOptions );
+ m_xCT_DictionaryToTraditional->refillFromDictionary( m_nTextConversionOptions );
+
+ short nRet = GenericDialogController::run();
+
+ if( nRet == RET_OK )
+ {
+ //save settings to configuration
+ SvtLinguConfig aLngCfg;
+ aLngCfg.SetProperty( UPN_IS_REVERSE_MAPPING, uno::Any(m_xCB_Reverse->get_active()) );
+
+ m_xCT_DictionaryToSimplified->save();
+ m_xCT_DictionaryToTraditional->save();
+ }
+
+ m_xCT_DictionaryToSimplified->deleteAll();
+ m_xCT_DictionaryToTraditional->deleteAll();
+
+ return nRet;
+}
+
+void ChineseDictionaryDialog::HeaderBarClick(DictionaryList& rList, int nColumn)
+{
+ bool bSortAtoZ = rList.get_sort_order();
+
+ //set new arrow positions in headerbar
+ if (nColumn == rList.get_sort_column())
+ {
+ bSortAtoZ = !bSortAtoZ;
+ rList.set_sort_order(bSortAtoZ);
+ }
+ else
+ {
+ rList.set_sort_indicator(TRISTATE_INDET, rList.get_sort_column());
+ rList.set_sort_column(nColumn);
+ }
+
+ //sort lists
+ rList.set_sort_indicator(bSortAtoZ ? TRISTATE_TRUE : TRISTATE_FALSE, nColumn);
+}
+
+IMPL_LINK(ChineseDictionaryDialog, ToSimplifiedHeaderBarClick, int, nColumn, void)
+{
+ HeaderBarClick(*m_xCT_DictionaryToSimplified, nColumn);
+}
+
+IMPL_LINK(ChineseDictionaryDialog, ToTraditionalHeaderBarClick, int, nColumn, void)
+{
+ HeaderBarClick(*m_xCT_DictionaryToTraditional, nColumn);
+}
+
+} //end namespace
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/unodialogs/textconversiondlgs/chinese_dictionarydialog.hxx b/svx/source/unodialogs/textconversiondlgs/chinese_dictionarydialog.hxx
new file mode 100644
index 000000000..0213c53da
--- /dev/null
+++ b/svx/source/unodialogs/textconversiondlgs/chinese_dictionarydialog.hxx
@@ -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 .
+ */
+
+#pragma once
+
+#include <vcl/weld.hxx>
+#include <com/sun/star/uno/XComponentContext.hpp>
+#include <com/sun/star/linguistic2/XConversionDictionary.hpp>
+
+#include <vector>
+
+namespace textconversiondlgs
+{
+
+struct DictionaryEntry final
+{
+ DictionaryEntry( const OUString& rTerm, const OUString& rMapping
+ , sal_Int16 nConversionPropertyType //linguistic2::ConversionPropertyType
+ , bool bNewEntry = false );
+
+ ~DictionaryEntry();
+
+ OUString m_aTerm;
+ OUString m_aMapping;
+ sal_Int16 m_nConversionPropertyType; //linguistic2::ConversionPropertyType
+
+ bool m_bNewEntry;
+};
+
+class DictionaryList
+{
+public:
+ DictionaryList(std::unique_ptr<weld::TreeView> xTreeView);
+
+ void init(const css::uno::Reference< css::linguistic2::XConversionDictionary>& xDictionary,
+ weld::Entry *pED_Term, weld::Entry *pED_Mapping, weld::ComboBox *pLB_Property);
+
+ void deleteAll();
+ void refillFromDictionary( sal_Int32 nTextConversionOptions /*i18n::TextConversionOption*/ );
+ void save();
+
+ DictionaryEntry* getTermEntry( std::u16string_view rTerm ) const;
+ bool hasTerm( std::u16string_view rTerm ) const;
+
+ void addEntry( const OUString& rTerm, const OUString& rMapping
+ , sal_Int16 nConversionPropertyType /*linguistic2::ConversionPropertyType*/, int nPos = -1);
+ int deleteEntries( std::u16string_view rTerm ); //return lowest position of deleted entries or -1 if no entry was deleted
+ void deleteEntryOnPos( sal_Int32 nPos );
+ DictionaryEntry* getEntryOnPos( sal_Int32 nPos ) const;
+ DictionaryEntry* getFirstSelectedEntry() const;
+
+ void set_size_request(int nWidth, int nHeight) { m_xControl->set_size_request(nWidth, nHeight); }
+ void hide() { m_xControl->hide(); }
+ void show() { m_xControl->show(); }
+ void connect_changed(const Link<weld::TreeView&, void>& rLink) { m_xControl->connect_changed(rLink); }
+ void connect_column_clicked(const Link<int, void>& rLink) { m_xControl->connect_column_clicked(rLink); }
+ bool get_sort_order() const { return m_xControl->get_sort_order(); }
+ void set_sort_order(bool bAscending) { return m_xControl->set_sort_order(bAscending); }
+ void set_sort_column(int nColumn) { return m_xControl->set_sort_column(nColumn); }
+ int get_sort_column() const { return m_xControl->get_sort_column(); }
+ int get_selected_index() const { return m_xControl->get_selected_index(); }
+ int get_height_rows(int nRows) const { return m_xControl->get_height_rows(nRows); }
+ bool get_visible() const { return m_xControl->get_visible(); }
+ void set_sort_indicator(TriState eState, int nColumn) { m_xControl->set_sort_indicator(eState, nColumn); }
+ weld::TreeView& get_widget() const { return *m_xControl; }
+
+private:
+ OUString getPropertyTypeName( sal_Int16 nConversionPropertyType /*linguistic2::ConversionPropertyType*/ ) const;
+
+public:
+ css::uno::Reference<css::linguistic2::XConversionDictionary> m_xDictionary;
+
+private:
+ std::unique_ptr<weld::TreeView> m_xControl;
+ std::unique_ptr<weld::TreeIter> m_xIter;
+ weld::Entry* m_pED_Term;
+ weld::Entry* m_pED_Mapping;
+ weld::ComboBox* m_pLB_Property;
+
+ std::vector< DictionaryEntry* > m_aToBeDeleted;
+};
+
+class ChineseDictionaryDialog : public weld::GenericDialogController
+{
+public:
+ explicit ChineseDictionaryDialog(weld::Window* pParent);
+ virtual ~ChineseDictionaryDialog() override;
+
+ //this method should be called once before calling execute
+ void setDirectionAndTextConversionOptions( bool bDirectionToSimplified, sal_Int32 nTextConversionOptions /*i18n::TextConversionOption*/ );
+
+ virtual short run() override;
+
+private:
+ DECL_LINK( DirectionHdl, weld::Toggleable&, void );
+ DECL_LINK( EditFieldsHdl, weld::Entry&, void );
+ DECL_LINK( EditFieldsListBoxHdl, weld::ComboBox&, void );
+ DECL_LINK( MappingSelectHdl, weld::TreeView&, void );
+ DECL_LINK( AddHdl, weld::Button&, void );
+ DECL_LINK( ModifyHdl, weld::Button&, void );
+ DECL_LINK( DeleteHdl, weld::Button&, void );
+ static void HeaderBarClick(DictionaryList& rList, int nColumn);
+ DECL_LINK(ToSimplifiedHeaderBarClick, int, void);
+ DECL_LINK(ToTraditionalHeaderBarClick, int, void);
+ DECL_LINK(SizeAllocHdl, const Size&, void);
+
+ void initDictionaryControl(DictionaryList *pList,
+ const css::uno::Reference< css::linguistic2::XConversionDictionary>& xDictionary);
+
+ void updateAfterDirectionChange();
+ void updateButtons();
+
+ bool isEditFieldsHaveContent() const;
+ bool isEditFieldsContentEqualsSelectedListContent() const;
+
+ DictionaryList& getActiveDictionary();
+ DictionaryList& getReverseDictionary();
+
+ const DictionaryList& getActiveDictionary() const;
+ const DictionaryList& getReverseDictionary() const;
+
+private:
+ sal_Int32 m_nTextConversionOptions; //i18n::TextConversionOption
+
+ css::uno::Reference<css::uno::XComponentContext> m_xContext;
+
+ std::unique_ptr<weld::RadioButton> m_xRB_To_Simplified;
+ std::unique_ptr<weld::RadioButton> m_xRB_To_Traditional;
+
+ std::unique_ptr<weld::CheckButton> m_xCB_Reverse;
+
+ std::unique_ptr<weld::Label> m_xFT_Term;
+ std::unique_ptr<weld::Entry> m_xED_Term;
+
+ std::unique_ptr<weld::Label> m_xFT_Mapping;
+ std::unique_ptr<weld::Entry> m_xED_Mapping;
+
+ std::unique_ptr<weld::Label> m_xFT_Property;
+ std::unique_ptr<weld::ComboBox> m_xLB_Property;
+
+ std::unique_ptr<DictionaryList> m_xCT_DictionaryToSimplified;
+ std::unique_ptr<DictionaryList> m_xCT_DictionaryToTraditional;
+
+ std::unique_ptr<weld::Button> m_xPB_Add;
+ std::unique_ptr<weld::Button> m_xPB_Modify;
+ std::unique_ptr<weld::Button> m_xPB_Delete;
+};
+
+
+} //end namespace
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/unodialogs/textconversiondlgs/chinese_translation_unodialog.cxx b/svx/source/unodialogs/textconversiondlgs/chinese_translation_unodialog.cxx
new file mode 100644
index 000000000..466a4efca
--- /dev/null
+++ b/svx/source/unodialogs/textconversiondlgs/chinese_translation_unodialog.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 "chinese_translation_unodialog.hxx"
+#include "chinese_translationdialog.hxx"
+#include <vcl/svapp.hxx>
+#include <cppuhelper/supportsservice.hxx>
+#include <com/sun/star/beans/PropertyValue.hpp>
+#include <com/sun/star/ui/dialogs/ExecutableDialogResults.hpp>
+
+namespace textconversiondlgs
+{
+using namespace ::com::sun::star;
+
+ChineseTranslation_UnoDialog::ChineseTranslation_UnoDialog()
+ : m_bDisposed(false)
+ , m_bInDispose(false)
+{
+}
+
+ChineseTranslation_UnoDialog::~ChineseTranslation_UnoDialog()
+{
+ SolarMutexGuard aSolarGuard;
+ impl_DeleteDialog();
+}
+
+void ChineseTranslation_UnoDialog::impl_DeleteDialog()
+{
+ if (m_xDialog)
+ {
+ m_xDialog->response(RET_CANCEL);
+ m_xDialog.reset();
+ }
+}
+
+// lang::XServiceInfo
+OUString SAL_CALL ChineseTranslation_UnoDialog::getImplementationName()
+{
+ return "com.sun.star.comp.linguistic2.ChineseTranslationDialog";
+}
+
+sal_Bool SAL_CALL ChineseTranslation_UnoDialog::supportsService( const OUString& ServiceName )
+{
+ return cppu::supportsService(this, ServiceName);
+}
+
+uno::Sequence< OUString > SAL_CALL ChineseTranslation_UnoDialog::getSupportedServiceNames()
+{
+ return { "com.sun.star.linguistic2.ChineseTranslationDialog" };
+}
+
+// ui::dialogs::XExecutableDialog
+void SAL_CALL ChineseTranslation_UnoDialog::setTitle( const OUString& )
+{
+ //not implemented - fell free to do so, if you do need this
+}
+
+
+void SAL_CALL ChineseTranslation_UnoDialog::initialize( const uno::Sequence< uno::Any >& aArguments )
+{
+ SolarMutexGuard aSolarGuard;
+ if( m_bDisposed || m_bInDispose )
+ return;
+
+ for(const uno::Any& rArgument : aArguments)
+ {
+ beans::PropertyValue aProperty;
+ if(rArgument >>= aProperty)
+ {
+ if( aProperty.Name == "ParentWindow" )
+ {
+ aProperty.Value >>= m_xParentWindow;
+ }
+ }
+ }
+}
+
+sal_Int16 SAL_CALL ChineseTranslation_UnoDialog::execute()
+{
+ sal_Int16 nRet = ui::dialogs::ExecutableDialogResults::CANCEL;
+ {
+ SolarMutexGuard aSolarGuard;
+ if (m_bDisposed || m_bInDispose)
+ return nRet;
+ if (!m_xDialog)
+ m_xDialog.reset(new ChineseTranslationDialog(Application::GetFrameWeld(m_xParentWindow)));
+ nRet = m_xDialog->run();
+ if (nRet == RET_OK)
+ nRet=ui::dialogs::ExecutableDialogResults::OK;
+ }
+ return nRet;
+}
+
+// lang::XComponent
+void SAL_CALL ChineseTranslation_UnoDialog::dispose()
+{
+ lang::EventObject aEvt;
+ {
+ SolarMutexGuard aSolarGuard;
+ if( m_bDisposed || m_bInDispose )
+ return;
+ m_bInDispose = true;
+
+ impl_DeleteDialog();
+ m_xParentWindow = nullptr;
+ m_bDisposed = true;
+
+ aEvt.Source = static_cast< XComponent * >( this );
+ }
+ std::unique_lock aGuard(m_aContainerMutex);
+ m_aDisposeEventListeners.disposeAndClear( aGuard, aEvt );
+}
+
+void SAL_CALL ChineseTranslation_UnoDialog::addEventListener( const uno::Reference< lang::XEventListener > & xListener )
+{
+ SolarMutexGuard aSolarGuard;
+ if( m_bDisposed || m_bInDispose )
+ return;
+ std::unique_lock aGuard(m_aContainerMutex);
+ m_aDisposeEventListeners.addInterface( aGuard, xListener );
+}
+
+void SAL_CALL ChineseTranslation_UnoDialog::removeEventListener( const uno::Reference< lang::XEventListener > & xListener )
+{
+ SolarMutexGuard aSolarGuard;
+ if( m_bDisposed || m_bInDispose )
+ return;
+ std::unique_lock aGuard(m_aContainerMutex);
+ m_aDisposeEventListeners.removeInterface( aGuard, xListener );
+}
+
+
+// XPropertySet
+
+uno::Reference< beans::XPropertySetInfo > SAL_CALL ChineseTranslation_UnoDialog::getPropertySetInfo( )
+{
+ return nullptr;
+}
+
+void SAL_CALL ChineseTranslation_UnoDialog::setPropertyValue( const OUString&, const uno::Any& )
+{
+ //only read only properties
+ throw beans::PropertyVetoException();
+}
+
+uno::Any SAL_CALL ChineseTranslation_UnoDialog::getPropertyValue( const OUString& rPropertyName )
+{
+ uno::Any aRet;
+
+ bool bDirectionToSimplified = true;
+ bool bTranslateCommonTerms = false;
+
+ {
+ SolarMutexGuard aSolarGuard;
+ if (m_bDisposed || m_bInDispose || !m_xDialog)
+ return aRet;
+ m_xDialog->getSettings(bDirectionToSimplified, bTranslateCommonTerms);
+ }
+
+ if( rPropertyName == "IsDirectionToSimplified" )
+ {
+ aRet <<= bDirectionToSimplified;
+ }
+ else if( rPropertyName == "IsUseCharacterVariants" )
+ {
+ aRet <<= false;
+ }
+ else if( rPropertyName == "IsTranslateCommonTerms" )
+ {
+ aRet <<= bTranslateCommonTerms;
+ }
+ else
+ {
+ throw beans::UnknownPropertyException( rPropertyName, static_cast<cppu::OWeakObject*>(this));
+ }
+ return aRet;
+
+}
+
+void SAL_CALL ChineseTranslation_UnoDialog::addPropertyChangeListener( const OUString& , const uno::Reference< beans::XPropertyChangeListener >& )
+{
+ //only not bound properties -> ignore listener
+}
+
+void SAL_CALL ChineseTranslation_UnoDialog::removePropertyChangeListener( const OUString& , const uno::Reference< beans::XPropertyChangeListener >& )
+{
+ //only not bound properties -> ignore listener
+}
+
+void SAL_CALL ChineseTranslation_UnoDialog::addVetoableChangeListener( const OUString& , const uno::Reference< beans::XVetoableChangeListener >& )
+{
+ //only not bound properties -> ignore listener
+}
+
+void SAL_CALL ChineseTranslation_UnoDialog::removeVetoableChangeListener( const OUString& , const uno::Reference< beans::XVetoableChangeListener >& )
+{
+ //only not bound properties -> ignore listener
+}
+
+} //end namespace
+
+
+extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface*
+svx_ChineseTranslation_UnoDialog_get_implementation(
+ css::uno::XComponentContext* , css::uno::Sequence<css::uno::Any> const&)
+{
+ return cppu::acquire(new textconversiondlgs::ChineseTranslation_UnoDialog());
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/unodialogs/textconversiondlgs/chinese_translation_unodialog.hxx b/svx/source/unodialogs/textconversiondlgs/chinese_translation_unodialog.hxx
new file mode 100644
index 000000000..0d40e1227
--- /dev/null
+++ b/svx/source/unodialogs/textconversiondlgs/chinese_translation_unodialog.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 .
+ */
+
+#pragma once
+
+#include <sal/config.h>
+
+#include <memory>
+
+#include <com/sun/star/awt/XWindow.hpp>
+#include <com/sun/star/beans/XPropertySet.hpp>
+#include <com/sun/star/lang/XInitialization.hpp>
+#include <com/sun/star/lang/XServiceInfo.hpp>
+#include <com/sun/star/ui/dialogs/XExecutableDialog.hpp>
+#include <cppuhelper/implbase.hxx>
+#include <comphelper/interfacecontainer4.hxx>
+#include <mutex>
+
+
+namespace textconversiondlgs
+{
+
+
+/** This class provides the chinese translation dialog as a uno component.
+
+It can be created via lang::XMultiComponentFactory::createInstanceWithContext
+with servicename "com.sun.star.linguistic2.ChineseTranslationDialog"
+or implementation name "com.sun.star.comp.linguistic2.ChineseTranslationDialog"
+
+It can be initialized via the XInitialization interface with the following single parameter:
+PropertyValue-Parameter: Name="ParentWindow" Type="awt::XWindow".
+
+It can be executed via the ui::dialogs::XExecutableDialog interface.
+
+Made settings can be retrieved via beans::XPropertySet interface.
+Following properties are available (read only and not bound):
+1) Name="IsDirectionToSimplified" Type="sal_Bool"
+2) Name="IsUseCharacterVariants" Type="sal_Bool"
+3) Name="IsTranslateCommonTerms" Type="sal_Bool"
+
+The dialog gets this information from the registry on execute and writes it back to the registry if ended with OK.
+*/
+
+class ChineseTranslationDialog;
+
+class ChineseTranslation_UnoDialog : public ::cppu::WeakImplHelper <
+ css::ui::dialogs::XExecutableDialog
+ , css::lang::XInitialization
+ , css::beans::XPropertySet
+ , css::lang::XComponent
+ , css::lang::XServiceInfo
+ >
+{
+public:
+ ChineseTranslation_UnoDialog();
+ virtual ~ChineseTranslation_UnoDialog() override;
+
+ // lang::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;
+
+ // lang::XInitialization
+ virtual void SAL_CALL initialize( const css::uno::Sequence< css::uno::Any >& aArguments ) override;
+
+ // ui::dialogs::XExecutableDialog
+ virtual void SAL_CALL setTitle( const OUString& aTitle ) override;
+ virtual sal_Int16 SAL_CALL execute( ) override;
+
+ // beans::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;
+
+ // lang::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;
+
+private:
+
+ void impl_DeleteDialog();
+
+private:
+ css::uno::Reference<
+ css::awt::XWindow > m_xParentWindow;
+
+ std::unique_ptr<ChineseTranslationDialog> m_xDialog;
+
+ bool m_bDisposed; ///Dispose call ready.
+ bool m_bInDispose;///In dispose call
+ std::mutex m_aContainerMutex;
+ comphelper::OInterfaceContainerHelper4<css::lang::XEventListener> m_aDisposeEventListeners;
+};
+
+
+} //end namespace
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/unodialogs/textconversiondlgs/chinese_translationdialog.cxx b/svx/source/unodialogs/textconversiondlgs/chinese_translationdialog.cxx
new file mode 100644
index 000000000..9bef507b7
--- /dev/null
+++ b/svx/source/unodialogs/textconversiondlgs/chinese_translationdialog.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 "chinese_translationdialog.hxx"
+#include "chinese_dictionarydialog.hxx"
+#include <com/sun/star/i18n/TextConversionOption.hpp>
+#include <unotools/lingucfg.hxx>
+#include <unotools/linguprops.hxx>
+
+namespace textconversiondlgs
+{
+
+using namespace ::com::sun::star;
+using namespace ::com::sun::star::uno;
+
+ChineseTranslationDialog::ChineseTranslationDialog(weld::Window* pParent)
+ : GenericDialogController(pParent, "svx/ui/chineseconversiondialog.ui", "ChineseConversionDialog")
+ , m_xBP_OK(m_xBuilder->weld_button("ok"))
+ , m_xPB_Editterms(m_xBuilder->weld_button("editterms"))
+ , m_xRB_To_Simplified(m_xBuilder->weld_radio_button("tosimplified"))
+ , m_xRB_To_Traditional(m_xBuilder->weld_radio_button("totraditional"))
+ , m_xCB_Translate_Commonterms(m_xBuilder->weld_check_button("commonterms"))
+{
+ SvtLinguConfig aLngCfg;
+ bool bValue = false;
+ Any aAny( aLngCfg.GetProperty( UPN_IS_DIRECTION_TO_SIMPLIFIED ) );
+ aAny >>= bValue;
+ if( bValue )
+ m_xRB_To_Simplified->set_active(true);
+ else
+ m_xRB_To_Traditional->set_active(true);
+
+ aAny = aLngCfg.GetProperty( UPN_IS_TRANSLATE_COMMON_TERMS );
+ if( aAny >>= bValue )
+ m_xCB_Translate_Commonterms->set_active( bValue );
+
+ m_xPB_Editterms->connect_clicked( LINK( this, ChineseTranslationDialog, DictionaryHdl ) );
+ m_xBP_OK->connect_clicked( LINK( this, ChineseTranslationDialog, OkHdl ) );
+}
+
+ChineseTranslationDialog::~ChineseTranslationDialog()
+{
+}
+
+void ChineseTranslationDialog::getSettings( bool& rbDirectionToSimplified
+ , bool& rbTranslateCommonTerms ) const
+{
+ rbDirectionToSimplified = m_xRB_To_Simplified->get_active();
+ rbTranslateCommonTerms = m_xCB_Translate_Commonterms->get_active();
+}
+
+IMPL_LINK_NOARG(ChineseTranslationDialog, OkHdl, weld::Button&, void)
+{
+ //save settings to configuration
+ SvtLinguConfig aLngCfg;
+ Any aAny;
+ aAny <<= m_xRB_To_Simplified->get_active();
+ aLngCfg.SetProperty( UPN_IS_DIRECTION_TO_SIMPLIFIED, aAny );
+ aAny <<= m_xCB_Translate_Commonterms->get_active();
+ aLngCfg.SetProperty( UPN_IS_TRANSLATE_COMMON_TERMS, aAny );
+
+ m_xDialog->response(RET_OK);
+}
+
+IMPL_LINK_NOARG(ChineseTranslationDialog, DictionaryHdl, weld::Button&, void)
+{
+ if( !m_xDictionaryDialog )
+ m_xDictionaryDialog.reset(new ChineseDictionaryDialog(m_xDialog.get()));
+ sal_Int32 nTextConversionOptions = i18n::TextConversionOption::NONE;
+ if (!m_xCB_Translate_Commonterms->get_active())
+ nTextConversionOptions = nTextConversionOptions | i18n::TextConversionOption::CHARACTER_BY_CHARACTER;
+ m_xDictionaryDialog->setDirectionAndTextConversionOptions(m_xRB_To_Simplified->get_active(), nTextConversionOptions);
+ m_xDictionaryDialog->run();
+}
+
+
+} //end namespace
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/unodialogs/textconversiondlgs/chinese_translationdialog.hxx b/svx/source/unodialogs/textconversiondlgs/chinese_translationdialog.hxx
new file mode 100644
index 000000000..d6a9c312d
--- /dev/null
+++ b/svx/source/unodialogs/textconversiondlgs/chinese_translationdialog.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 .
+ */
+
+#pragma once
+
+#include <vcl/weld.hxx>
+
+namespace textconversiondlgs
+{
+class ChineseDictionaryDialog;
+
+class ChineseTranslationDialog : public weld::GenericDialogController
+{
+public:
+ explicit ChineseTranslationDialog(weld::Window* pParent);
+ virtual ~ChineseTranslationDialog() override;
+
+ void getSettings(bool& rbDirectionToSimplified, bool& rbTranslateCommonTerms) const;
+
+private:
+ DECL_LINK(DictionaryHdl, weld::Button&, void);
+ DECL_LINK(OkHdl, weld::Button&, void);
+
+private:
+ std::unique_ptr<weld::Button> m_xBP_OK;
+ std::unique_ptr<weld::Button> m_xPB_Editterms;
+ std::unique_ptr<weld::RadioButton> m_xRB_To_Simplified;
+ std::unique_ptr<weld::RadioButton> m_xRB_To_Traditional;
+ std::unique_ptr<weld::CheckButton> m_xCB_Translate_Commonterms;
+ std::unique_ptr<ChineseDictionaryDialog> m_xDictionaryDialog;
+};
+
+} //end namespace
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/unodraw/SvxXTextColumns.cxx b/svx/source/unodraw/SvxXTextColumns.cxx
new file mode 100644
index 000000000..a0e399bd4
--- /dev/null
+++ b/svx/source/unodraw/SvxXTextColumns.cxx
@@ -0,0 +1,338 @@
+/* -*- 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 <sal/config.h>
+
+#include <com/sun/star/beans/PropertyAttribute.hpp>
+#include <com/sun/star/beans/PropertyVetoException.hpp>
+#include <com/sun/star/beans/UnknownPropertyException.hpp>
+#include <com/sun/star/beans/XPropertySet.hpp>
+#include <com/sun/star/lang/IllegalArgumentException.hpp>
+#include <com/sun/star/lang/XServiceInfo.hpp>
+#include <com/sun/star/style/VerticalAlignment.hpp>
+#include <com/sun/star/text/ColumnSeparatorStyle.hpp>
+#include <com/sun/star/text/XTextColumns.hpp>
+#include <com/sun/star/uno/Any.h>
+#include <com/sun/star/util/Color.hpp>
+
+#include <cppuhelper/supportsservice.hxx>
+#include <o3tl/safeint.hxx>
+#include <svl/itemprop.hxx>
+#include <svx/SvxXTextColumns.hxx>
+#include <tools/UnitConversion.hxx>
+#include <vcl/svapp.hxx>
+
+#include <numeric>
+
+namespace
+{
+enum : sal_uInt16
+{
+ WID_TXTCOL_IS_AUTOMATIC,
+ WID_TXTCOL_AUTO_DISTANCE,
+ WID_TXTCOL_LINE_WIDTH,
+ WID_TXTCOL_LINE_COLOR,
+ WID_TXTCOL_LINE_REL_HGT,
+ WID_TXTCOL_LINE_ALIGN,
+ WID_TXTCOL_LINE_IS_ON,
+ WID_TXTCOL_LINE_STYLE,
+};
+
+SfxItemPropertyMapEntry const saTextColumns_Impl[] = {
+ { u"IsAutomatic", WID_TXTCOL_IS_AUTOMATIC, cppu::UnoType<bool>::get(),
+ css::beans::PropertyAttribute::READONLY, 0 },
+ { u"AutomaticDistance", WID_TXTCOL_AUTO_DISTANCE, cppu::UnoType<sal_Int32>::get(), 0, 0 },
+ { u"SeparatorLineWidth", WID_TXTCOL_LINE_WIDTH, cppu::UnoType<sal_Int32>::get(), 0, 0 },
+ { u"SeparatorLineColor", WID_TXTCOL_LINE_COLOR,
+ cppu::UnoType<com::sun::star::util::Color>::get(), 0, 0 },
+ { u"SeparatorLineRelativeHeight", WID_TXTCOL_LINE_REL_HGT, cppu::UnoType<sal_Int32>::get(), 0,
+ 0 },
+ { u"SeparatorLineVerticalAlignment", WID_TXTCOL_LINE_ALIGN,
+ cppu::UnoType<css::style::VerticalAlignment>::get(), 0, 0 },
+ { u"SeparatorLineIsOn", WID_TXTCOL_LINE_IS_ON, cppu::UnoType<bool>::get(), 0, 0 },
+ { u"SeparatorLineStyle", WID_TXTCOL_LINE_STYLE, cppu::UnoType<sal_Int16>::get(), 0, 0 },
+ { u"", 0, css::uno::Type(), 0, 0 },
+};
+
+class SvxXTextColumns final
+ : public cppu::WeakImplHelper<css::beans::XPropertySet, css::text::XTextColumns,
+ css::lang::XServiceInfo>
+{
+public:
+ SvxXTextColumns() = default;
+
+ // XTextColumns
+ virtual sal_Int32 SAL_CALL getReferenceValue() override;
+ virtual sal_Int16 SAL_CALL getColumnCount() override;
+ virtual void SAL_CALL setColumnCount(sal_Int16 nColumns) override;
+ virtual css::uno::Sequence<css::text::TextColumn> SAL_CALL getColumns() override;
+ virtual void SAL_CALL
+ setColumns(const css::uno::Sequence<css::text::TextColumn>& Columns) 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;
+
+private:
+ sal_Int32 m_nReference = USHRT_MAX;
+ css::uno::Sequence<css::text::TextColumn> m_aTextColumns;
+ bool m_bIsAutomaticWidth = true;
+ sal_Int32 m_nAutoDistance = 0;
+
+ const SfxItemPropertySet m_aPropSet = { saTextColumns_Impl };
+
+ //separator line
+ sal_Int32 m_nSepLineWidth = 0;
+ com::sun::star::util::Color m_nSepLineColor = 0; // black
+ sal_Int32 m_nSepLineHeightRelative = 100; // full height
+ css::style::VerticalAlignment m_nSepLineVertAlign = css::style::VerticalAlignment_MIDDLE;
+ bool m_bSepLineIsOn = false;
+ sal_Int16 m_nSepLineStyle = css::text::ColumnSeparatorStyle::NONE;
+};
+
+OUString SvxXTextColumns::getImplementationName() { return "com.sun.star.comp.svx.TextColumns"; }
+
+sal_Bool SvxXTextColumns::supportsService(const OUString& rServiceName)
+{
+ return cppu::supportsService(this, rServiceName);
+}
+
+css::uno::Sequence<OUString> SvxXTextColumns::getSupportedServiceNames()
+{
+ return { "com.sun.star.text.TextColumns" };
+}
+
+sal_Int32 SvxXTextColumns::getReferenceValue()
+{
+ SolarMutexGuard aGuard;
+ return m_nReference;
+}
+
+sal_Int16 SvxXTextColumns::getColumnCount()
+{
+ SolarMutexGuard aGuard;
+ return o3tl::narrowing<sal_Int16>(m_aTextColumns.getLength());
+}
+
+void SvxXTextColumns::setColumnCount(sal_Int16 nColumns)
+{
+ SolarMutexGuard aGuard;
+ if (nColumns <= 0)
+ throw css::uno::RuntimeException();
+ m_bIsAutomaticWidth = true;
+ m_aTextColumns.realloc(nColumns);
+ css::text::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;
+}
+
+css::uno::Sequence<css::text::TextColumn> SvxXTextColumns::getColumns()
+{
+ SolarMutexGuard aGuard;
+ return m_aTextColumns;
+}
+
+void SvxXTextColumns::setColumns(const css::uno::Sequence<css::text::TextColumn>& rColumns)
+{
+ SolarMutexGuard aGuard;
+ sal_Int32 nReferenceTemp = std::accumulate(
+ rColumns.begin(), rColumns.end(), sal_Int32(0),
+ [](const sal_Int32 nSum, const css::text::TextColumn& rCol) { return nSum + rCol.Width; });
+ m_bIsAutomaticWidth = false;
+ m_nReference = !nReferenceTemp ? USHRT_MAX : nReferenceTemp;
+ m_aTextColumns = rColumns;
+}
+
+css::uno::Reference<css::beans::XPropertySetInfo> SvxXTextColumns::getPropertySetInfo()
+{
+ return m_aPropSet.getPropertySetInfo();
+}
+
+void SvxXTextColumns::setPropertyValue(const OUString& rPropertyName, const css::uno::Any& aValue)
+{
+ const SfxItemPropertyMapEntry* pEntry = m_aPropSet.getPropertyMap().getByName(rPropertyName);
+ if (!pEntry)
+ throw css::beans::UnknownPropertyException("Unknown property: " + rPropertyName,
+ static_cast<cppu::OWeakObject*>(this));
+ if (pEntry->nFlags & css::beans::PropertyAttribute::READONLY)
+ throw css::beans::PropertyVetoException("Property is read-only: " + rPropertyName,
+ static_cast<cppu::OWeakObject*>(this));
+
+ switch (pEntry->nWID)
+ {
+ case WID_TXTCOL_LINE_WIDTH:
+ {
+ sal_Int32 nTmp;
+ if (!(aValue >>= nTmp) || nTmp < 0)
+ throw css::lang::IllegalArgumentException();
+ m_nSepLineWidth = nTmp;
+ break;
+ }
+ case WID_TXTCOL_LINE_COLOR:
+ if (!(aValue >>= m_nSepLineColor))
+ throw css::lang::IllegalArgumentException();
+ break;
+ case WID_TXTCOL_LINE_STYLE:
+ if (!(aValue >>= m_nSepLineStyle))
+ throw css::lang::IllegalArgumentException();
+ break;
+ case WID_TXTCOL_LINE_REL_HGT:
+ {
+ sal_Int32 nTmp;
+ if (!(aValue >>= nTmp) || nTmp < 0)
+ throw css::lang::IllegalArgumentException();
+ m_nSepLineHeightRelative = nTmp;
+ break;
+ }
+ case WID_TXTCOL_LINE_ALIGN:
+ if (css::style::VerticalAlignment eAlign; aValue >>= eAlign)
+ m_nSepLineVertAlign = eAlign;
+ else if (sal_Int8 nTmp; aValue >>= nTmp)
+ m_nSepLineVertAlign = static_cast<css::style::VerticalAlignment>(nTmp);
+ else
+ throw css::lang::IllegalArgumentException();
+ break;
+ case WID_TXTCOL_LINE_IS_ON:
+ if (!(aValue >>= m_bSepLineIsOn))
+ throw css::lang::IllegalArgumentException();
+ break;
+ case WID_TXTCOL_AUTO_DISTANCE:
+ {
+ sal_Int32 nTmp;
+ if (!(aValue >>= nTmp) || nTmp < 0 || nTmp >= m_nReference)
+ throw css::lang::IllegalArgumentException();
+ m_nAutoDistance = nTmp;
+ sal_Int32 nColumns = m_aTextColumns.getLength();
+ css::text::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;
+ }
+ }
+}
+
+css::uno::Any SvxXTextColumns::getPropertyValue(const OUString& rPropertyName)
+{
+ const SfxItemPropertyMapEntry* pEntry = m_aPropSet.getPropertyMap().getByName(rPropertyName);
+ if (!pEntry)
+ throw css::beans::UnknownPropertyException("Unknown property: " + rPropertyName,
+ static_cast<cppu::OWeakObject*>(this));
+
+ css::uno::Any aRet;
+ switch (pEntry->nWID)
+ {
+ case WID_TXTCOL_LINE_WIDTH:
+ aRet <<= 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 SvxXTextColumns::addPropertyChangeListener(
+ const OUString& /*rPropertyName*/,
+ const css::uno::Reference<css::beans::XPropertyChangeListener>& /*xListener*/)
+{
+}
+
+void SvxXTextColumns::removePropertyChangeListener(
+ const OUString& /*rPropertyName*/,
+ const css::uno::Reference<css::beans::XPropertyChangeListener>& /*xListener*/)
+{
+}
+
+void SvxXTextColumns::addVetoableChangeListener(
+ const OUString& /*rPropertyName*/,
+ const css::uno::Reference<css::beans::XVetoableChangeListener>& /*xListener*/)
+{
+}
+
+void SvxXTextColumns::removeVetoableChangeListener(
+ const OUString& /*rPropertyName*/,
+ const css::uno::Reference<css::beans::XVetoableChangeListener>& /*xListener*/)
+{
+}
+}
+
+css::uno::Reference<css::uno::XInterface> SvxXTextColumns_createInstance() noexcept
+{
+ return static_cast<cppu::OWeakObject*>(new SvxXTextColumns);
+}
+
+extern "C" SVXCORE_DLLPUBLIC css::uno::XInterface*
+com_sun_star_comp_svx_TextColumns_get_implementation(css::uno::XComponentContext*,
+ css::uno::Sequence<css::uno::Any> const&)
+{
+ return cppu::acquire(new SvxXTextColumns);
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab cinoptions=b1,g0,N-s cinkeys+=0=break: */
diff --git a/svx/source/unodraw/UnoGraphicExporter.cxx b/svx/source/unodraw/UnoGraphicExporter.cxx
new file mode 100644
index 000000000..0cc43ce70
--- /dev/null
+++ b/svx/source/unodraw/UnoGraphicExporter.cxx
@@ -0,0 +1,1318 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * 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 <com/sun/star/io/XOutputStream.hpp>
+#include <com/sun/star/beans/XPropertySet.hpp>
+#include <com/sun/star/container/XChild.hpp>
+#include <com/sun/star/lang/XServiceInfo.hpp>
+#include <com/sun/star/lang/XComponent.hpp>
+#include <com/sun/star/drawing/XShape.hpp>
+#include <com/sun/star/drawing/XDrawPage.hpp>
+#include <com/sun/star/drawing/XGraphicExportFilter.hpp>
+#include <com/sun/star/graphic/XGraphic.hpp>
+#include <com/sun/star/graphic/XGraphicRenderer.hpp>
+#include <com/sun/star/task/XStatusIndicator.hpp>
+#include <com/sun/star/task/XInteractionHandler.hpp>
+#include <com/sun/star/task/XInteractionContinuation.hpp>
+#include <com/sun/star/uno/XComponentContext.hpp>
+
+#include <tools/debug.hxx>
+#include <tools/diagnose_ex.h>
+#include <tools/urlobj.hxx>
+#include <comphelper/interaction.hxx>
+#include <framework/interaction.hxx>
+#include <com/sun/star/drawing/GraphicFilterRequest.hpp>
+#include <com/sun/star/util/URL.hpp>
+#include <cppuhelper/implbase.hxx>
+#include <cppuhelper/supportsservice.hxx>
+#include <vcl/metaact.hxx>
+#include <vcl/svapp.hxx>
+#include <vcl/virdev.hxx>
+#include <svl/outstrm.hxx>
+#include <sdr/contact/objectcontactofobjlistpainter.hxx>
+#include <svx/sdr/contact/viewobjectcontact.hxx>
+#include <svx/sdr/contact/viewcontact.hxx>
+#include <svx/sdr/contact/displayinfo.hxx>
+#include <editeng/numitem.hxx>
+#include <svx/svdograf.hxx>
+#include <svx/xoutbmp.hxx>
+#include <vcl/graphicfilter.hxx>
+#include <svx/svdpage.hxx>
+#include <svx/svdmodel.hxx>
+#include <svx/fmview.hxx>
+#include <svx/fmmodel.hxx>
+#include <svx/unopage.hxx>
+#include <svx/svdoutl.hxx>
+#include <svx/xlineit0.hxx>
+#include <editeng/flditem.hxx>
+#include <svtools/optionsdrawinglayer.hxx>
+#include <comphelper/sequenceashashmap.hxx>
+#include <comphelper/propertysequence.hxx>
+#include <comphelper/sequence.hxx>
+#include "UnoGraphicExporter.hxx"
+#include <memory>
+
+#define MAX_EXT_PIX 2048
+
+using namespace ::comphelper;
+using namespace ::cppu;
+using namespace ::com::sun::star;
+using namespace ::com::sun::star::uno;
+using namespace ::com::sun::star::util;
+using namespace ::com::sun::star::container;
+using namespace ::com::sun::star::drawing;
+using namespace ::com::sun::star::lang;
+using namespace ::com::sun::star::document;
+using namespace ::com::sun::star::beans;
+using namespace ::com::sun::star::task;
+
+// #i102251#
+#include <editeng/editstat.hxx>
+
+namespace {
+
+ struct ExportSettings
+ {
+ OUString maFilterName;
+ OUString maMediaType;
+ URL maURL;
+ css::uno::Reference< css::io::XOutputStream > mxOutputStream;
+ css::uno::Reference< css::graphic::XGraphicRenderer > mxGraphicRenderer;
+ css::uno::Reference< css::task::XStatusIndicator > mxStatusIndicator;
+ css::uno::Reference< css::task::XInteractionHandler > mxInteractionHandler;
+
+ sal_Int32 mnWidth;
+ sal_Int32 mnHeight;
+ bool mbExportOnlyBackground;
+ bool mbScrollText;
+ bool mbUseHighContrast;
+ bool mbTranslucent;
+
+ Sequence< PropertyValue > maFilterData;
+
+ Fraction maScaleX;
+ Fraction maScaleY;
+
+ TriState meAntiAliasing = TRISTATE_INDET;
+
+ explicit ExportSettings(const SdrModel* pSdrModel);
+ };
+
+ ExportSettings::ExportSettings(const SdrModel* pSdrModel)
+ : mnWidth( 0 )
+ ,mnHeight( 0 )
+ ,mbExportOnlyBackground( false )
+ ,mbScrollText( false )
+ ,mbUseHighContrast( false )
+ ,mbTranslucent( false )
+ ,maScaleX( 1, 1 )
+ ,maScaleY( 1, 1 )
+ {
+ if (pSdrModel)
+ {
+ maScaleX = pSdrModel->GetScaleFraction();
+ maScaleY = pSdrModel->GetScaleFraction();
+ }
+ }
+
+ /** implements a component to export shapes or pages to external graphic formats.
+
+ @implements com.sun.star.drawing.GraphicExportFilter
+ */
+ class GraphicExporter : public WeakImplHelper< XGraphicExportFilter, XServiceInfo >
+ {
+ public:
+ GraphicExporter();
+
+ // XFilter
+ virtual sal_Bool SAL_CALL filter( const Sequence< PropertyValue >& aDescriptor ) override;
+ virtual void SAL_CALL cancel( ) override;
+
+ // XExporter
+ virtual void SAL_CALL setSourceDocument( const Reference< XComponent >& xDoc ) 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;
+
+ // XMimeTypeInfo
+ virtual sal_Bool SAL_CALL supportsMimeType( const OUString& MimeTypeName ) override;
+ virtual Sequence< OUString > SAL_CALL getSupportedMimeTypeNames( ) override;
+
+ VclPtr<VirtualDevice> CreatePageVDev( SdrPage* pPage, tools::Long nWidthPixel, tools::Long nHeightPixel ) const;
+
+ DECL_LINK( CalcFieldValueHdl, EditFieldInfo*, void );
+
+ void ParseSettings( const Sequence< PropertyValue >& aDescriptor, ExportSettings& rSettings );
+ bool GetGraphic( ExportSettings const & rSettings, Graphic& aGraphic, bool bVectorType );
+
+ private:
+ Reference< XShape > mxShape;
+ Reference< XDrawPage > mxPage;
+ Reference< XShapes > mxShapes;
+ Graphic maGraphic;
+
+ SvxDrawPage* mpUnoPage;
+
+ Link<EditFieldInfo*,void> maOldCalcFieldValueHdl;
+ sal_Int32 mnPageNumber;
+ SdrPage* mpCurrentPage;
+ SdrModel* mpDoc;
+ };
+
+ /** creates a bitmap that is optionally transparent from a metafile
+ */
+ BitmapEx GetBitmapFromMetaFile( const GDIMetaFile& rMtf, const Size* pSize )
+ {
+ // use new primitive conversion tooling
+ basegfx::B2DRange aRange(basegfx::B2DPoint(0.0, 0.0));
+ sal_uInt32 nMaximumQuadraticPixels(500000);
+
+ if(pSize)
+ {
+ // use 100th mm for primitive bitmap converter tool, input is pixel
+ // use a real OutDev to get the correct DPI, the static LogicToLogic assumes 72dpi which is wrong (!)
+ const Size aSize100th(Application::GetDefaultDevice()->PixelToLogic(*pSize, MapMode(MapUnit::Map100thMM)));
+
+ aRange.expand(basegfx::B2DPoint(aSize100th.Width(), aSize100th.Height()));
+
+ // when explicitly pixels are requested from the GraphicExporter, use a *very* high limit
+ // of 16gb (4096x4096 pixels), else use the default for the converters
+ nMaximumQuadraticPixels = std::min(sal_uInt32(4096 * 4096), sal_uInt32(pSize->Width() * pSize->Height()));
+ }
+ else
+ {
+ // use 100th mm for primitive bitmap converter tool
+ const Size aSize100th(OutputDevice::LogicToLogic(rMtf.GetPrefSize(), rMtf.GetPrefMapMode(), MapMode(MapUnit::Map100thMM)));
+
+ aRange.expand(basegfx::B2DPoint(aSize100th.Width(), aSize100th.Height()));
+ }
+
+ return convertMetafileToBitmapEx(rMtf, aRange, nMaximumQuadraticPixels);
+ }
+
+ Size* CalcSize( sal_Int32 nWidth, sal_Int32 nHeight, const Size& aBoundSize, Size& aOutSize )
+ {
+ if( (nWidth == 0) && (nHeight == 0) )
+ return nullptr;
+
+ if( (nWidth == 0) && (nHeight != 0) && (aBoundSize.Height() != 0) )
+ {
+ nWidth = ( nHeight * aBoundSize.Width() ) / aBoundSize.Height();
+ }
+ else if( (nWidth != 0) && (nHeight == 0) && (aBoundSize.Width() != 0) )
+ {
+ nHeight = ( nWidth * aBoundSize.Height() ) / aBoundSize.Width();
+ }
+
+ aOutSize.setWidth( nWidth );
+ aOutSize.setHeight( nHeight );
+
+ return &aOutSize;
+ }
+
+class ImplExportCheckVisisbilityRedirector : public sdr::contact::ViewObjectContactRedirector
+{
+public:
+ explicit ImplExportCheckVisisbilityRedirector( SdrPage* pCurrentPage );
+
+ virtual void createRedirectedPrimitive2DSequence(
+ const sdr::contact::ViewObjectContact& rOriginal,
+ const sdr::contact::DisplayInfo& rDisplayInfo,
+ drawinglayer::primitive2d::Primitive2DDecompositionVisitor& rVisitor) override;
+
+private:
+ SdrPage* mpCurrentPage;
+};
+
+ImplExportCheckVisisbilityRedirector::ImplExportCheckVisisbilityRedirector( SdrPage* pCurrentPage )
+: mpCurrentPage( pCurrentPage )
+{
+}
+
+void ImplExportCheckVisisbilityRedirector::createRedirectedPrimitive2DSequence(
+ const sdr::contact::ViewObjectContact& rOriginal,
+ const sdr::contact::DisplayInfo& rDisplayInfo,
+ drawinglayer::primitive2d::Primitive2DDecompositionVisitor& rVisitor)
+{
+ SdrObject* pObject = rOriginal.GetViewContact().TryToGetSdrObject();
+
+ if(pObject)
+ {
+ SdrPage* pPage = mpCurrentPage;
+
+ if(nullptr == pPage)
+ {
+ pPage = pObject->getSdrPageFromSdrObject();
+ }
+
+ if( (pPage == nullptr) || pPage->checkVisibility(rOriginal, rDisplayInfo, false) )
+ {
+ return sdr::contact::ViewObjectContactRedirector::createRedirectedPrimitive2DSequence(rOriginal, rDisplayInfo, rVisitor);
+ }
+
+ return;
+ }
+ else
+ {
+ // not an object, maybe a page
+ sdr::contact::ViewObjectContactRedirector::createRedirectedPrimitive2DSequence(rOriginal, rDisplayInfo, rVisitor);
+ }
+}
+
+GraphicExporter::GraphicExporter()
+: mpUnoPage( nullptr ), mnPageNumber(-1), mpCurrentPage(nullptr), mpDoc( nullptr )
+{
+}
+
+IMPL_LINK(GraphicExporter, CalcFieldValueHdl, EditFieldInfo*, pInfo, void)
+{
+ if( pInfo )
+ {
+ if( mpCurrentPage )
+ {
+ pInfo->SetSdrPage( mpCurrentPage );
+ }
+ else if( mnPageNumber != -1 )
+ {
+ const SvxFieldData* pField = pInfo->GetField().GetField();
+ if( dynamic_cast<const SvxPageField*>( pField) )
+ {
+ OUString aPageNumValue;
+ bool bUpper = false;
+
+ switch(mpDoc->GetPageNumType())
+ {
+ case css::style::NumberingType::CHARS_UPPER_LETTER:
+ aPageNumValue += OUStringChar( sal_Unicode((mnPageNumber - 1) % 26 + 'A') );
+ break;
+ case css::style::NumberingType::CHARS_LOWER_LETTER:
+ aPageNumValue += OUStringChar( sal_Unicode((mnPageNumber - 1) % 26 + 'a') );
+ break;
+ case css::style::NumberingType::ROMAN_UPPER:
+ bUpper = true;
+ [[fallthrough]];
+ case css::style::NumberingType::ROMAN_LOWER:
+ aPageNumValue += SvxNumberFormat::CreateRomanString(mnPageNumber, bUpper);
+ break;
+ case css::style::NumberingType::NUMBER_NONE:
+ aPageNumValue = " ";
+ break;
+ default:
+ aPageNumValue += OUString::number( mnPageNumber );
+ }
+
+ pInfo->SetRepresentation( aPageNumValue );
+
+ return;
+ }
+ }
+ }
+
+ maOldCalcFieldValueHdl.Call( pInfo );
+
+ if( pInfo && mpCurrentPage )
+ pInfo->SetSdrPage( nullptr );
+}
+
+/** creates a virtual device for the given page
+
+ @return the returned VirtualDevice is owned by the caller
+*/
+VclPtr<VirtualDevice> GraphicExporter::CreatePageVDev( SdrPage* pPage, tools::Long nWidthPixel, tools::Long nHeightPixel ) const
+{
+ VclPtr<VirtualDevice> pVDev = VclPtr<VirtualDevice>::Create();
+ MapMode aMM( MapUnit::Map100thMM );
+
+ Point aPoint( 0, 0 );
+ Size aPageSize(pPage->GetSize());
+
+ // use scaling?
+ if( nWidthPixel != 0 )
+ {
+ const Fraction aFrac( nWidthPixel, pVDev->LogicToPixel( aPageSize, aMM ).Width() );
+
+ aMM.SetScaleX( aFrac );
+
+ if( nHeightPixel == 0 )
+ aMM.SetScaleY( aFrac );
+ }
+
+ if( nHeightPixel != 0 )
+ {
+ const Fraction aFrac( nHeightPixel, pVDev->LogicToPixel( aPageSize, aMM ).Height() );
+
+ if( nWidthPixel == 0 )
+ aMM.SetScaleX( aFrac );
+
+ aMM.SetScaleY( aFrac );
+ }
+
+ pVDev->SetMapMode( aMM );
+ bool bSuccess(false);
+
+ // #i122820# If available, use pixel size directly
+ if(nWidthPixel && nHeightPixel)
+ {
+ bSuccess = pVDev->SetOutputSizePixel(Size(nWidthPixel, nHeightPixel));
+ }
+ else
+ {
+ bSuccess = pVDev->SetOutputSize(aPageSize);
+ }
+
+ if(bSuccess)
+ {
+ SdrView aView(*mpDoc, pVDev);
+
+ aView.SetPageVisible( false );
+ aView.SetBordVisible( false );
+ aView.SetGridVisible( false );
+ aView.SetHlplVisible( false );
+ aView.SetGlueVisible( false );
+ aView.ShowSdrPage(pPage);
+
+ vcl::Region aRegion (tools::Rectangle( aPoint, aPageSize ) );
+
+ ImplExportCheckVisisbilityRedirector aRedirector( mpCurrentPage );
+
+ aView.CompleteRedraw(pVDev, aRegion, &aRedirector);
+ }
+ else
+ {
+ OSL_ENSURE(false, "Could not get a VirtualDevice of requested size (!)");
+ }
+
+ return pVDev;
+}
+
+void GraphicExporter::ParseSettings(const Sequence<PropertyValue>& rDescriptor,
+ ExportSettings& rSettings)
+{
+ Sequence<PropertyValue> aDescriptor = rDescriptor;
+ if (aDescriptor.hasElements())
+ {
+ comphelper::SequenceAsHashMap aMap(aDescriptor);
+ Sequence<PropertyValue> aFilterData;
+ OUString aFilterOptions;
+ auto it = aMap.find("FilterData");
+ if (it != aMap.end())
+ {
+ it->second >>= aFilterData;
+ }
+ it = aMap.find("FilterOptions");
+ if (it != aMap.end())
+ {
+ it->second >>= aFilterOptions;
+ }
+ if (!aFilterData.hasElements() && !aFilterOptions.isEmpty())
+ {
+ // Allow setting filter data keys from the cmdline.
+ std::vector<PropertyValue> aData
+ = comphelper::JsonToPropertyValues(aFilterOptions.toUtf8());
+ aFilterData = comphelper::containerToSequence(aData);
+ if (aFilterData.hasElements())
+ {
+ aMap["FilterData"] <<= aFilterData;
+ aDescriptor = aMap.getAsConstPropertyValueList();
+ }
+ }
+ }
+
+ for( const PropertyValue& rValue : aDescriptor )
+ {
+ if ( rValue.Name == "FilterName" )
+ {
+ rValue.Value >>= rSettings.maFilterName;
+ }
+ else if ( rValue.Name == "MediaType" )
+ {
+ rValue.Value >>= rSettings.maMediaType;
+ }
+ else if ( rValue.Name == "URL" )
+ {
+ if( !( rValue.Value >>= rSettings.maURL ) )
+ {
+ rValue.Value >>= rSettings.maURL.Complete;
+ }
+ }
+ else if ( rValue.Name == "OutputStream" )
+ {
+ rValue.Value >>= rSettings.mxOutputStream;
+ }
+ else if ( rValue.Name == "GraphicRenderer" )
+ {
+ rValue.Value >>= rSettings.mxGraphicRenderer;
+ }
+ else if ( rValue.Name == "StatusIndicator" )
+ {
+ rValue.Value >>= rSettings.mxStatusIndicator;
+ }
+ else if ( rValue.Name == "InteractionHandler" )
+ {
+ rValue.Value >>= rSettings.mxInteractionHandler;
+ }
+ else if( rValue.Name == "Width" ) // for compatibility reasons, deprecated
+ {
+ rValue.Value >>= rSettings.mnWidth;
+ }
+ else if( rValue.Name == "Height" ) // for compatibility reasons, deprecated
+ {
+ rValue.Value >>= rSettings.mnHeight;
+ }
+ else if( rValue.Name == "ExportOnlyBackground" ) // for compatibility reasons, deprecated
+ {
+ rValue.Value >>= rSettings.mbExportOnlyBackground;
+ }
+ else if ( rValue.Name == "FilterData" )
+ {
+ rValue.Value >>= rSettings.maFilterData;
+
+ for( PropertyValue& rDataValue : asNonConstRange(rSettings.maFilterData) )
+ {
+ if ( rDataValue.Name == "Translucent" )
+ {
+ if ( !( rDataValue.Value >>= rSettings.mbTranslucent ) ) // SJ: TODO: The GIF Transparency is stored as int32 in
+ { // configuration files, this has to be changed to boolean
+ sal_Int32 nTranslucent = 0;
+ if ( rDataValue.Value >>= nTranslucent )
+ rSettings.mbTranslucent = nTranslucent != 0;
+ }
+ }
+ else if ( rDataValue.Name == "PixelWidth" )
+ {
+ rDataValue.Value >>= rSettings.mnWidth;
+ }
+ else if ( rDataValue.Name == "PixelHeight" )
+ {
+ rDataValue.Value >>= rSettings.mnHeight;
+ }
+ else if( rDataValue.Name == "Width" ) // for compatibility reasons, deprecated
+ {
+ rDataValue.Value >>= rSettings.mnWidth;
+ rDataValue.Name = "PixelWidth";
+ }
+ else if( rDataValue.Name == "Height" ) // for compatibility reasons, deprecated
+ {
+ rDataValue.Value >>= rSettings.mnHeight;
+ rDataValue.Name = "PixelHeight";
+ }
+ else if ( rDataValue.Name == "ExportOnlyBackground" )
+ {
+ rDataValue.Value >>= rSettings.mbExportOnlyBackground;
+ }
+ else if ( rDataValue.Name == "HighContrast" )
+ {
+ rDataValue.Value >>= rSettings.mbUseHighContrast;
+ }
+ else if ( rDataValue.Name == "PageNumber" )
+ {
+ rDataValue.Value >>= mnPageNumber;
+ }
+ else if ( rDataValue.Name == "ScrollText" )
+ {
+ // #110496# Read flag solitary scroll text metafile
+ rDataValue.Value >>= rSettings.mbScrollText;
+ }
+ else if ( rDataValue.Name == "CurrentPage" )
+ {
+ Reference< XDrawPage > xPage;
+ rDataValue.Value >>= xPage;
+ if( xPage.is() )
+ {
+ SvxDrawPage* pUnoPage = comphelper::getFromUnoTunnel<SvxDrawPage>( xPage );
+ if( pUnoPage && pUnoPage->GetSdrPage() )
+ mpCurrentPage = pUnoPage->GetSdrPage();
+ }
+ }
+ else if ( rDataValue.Name == "ScaleXNumerator" )
+ {
+ sal_Int32 nVal = 1;
+ if( rDataValue.Value >>= nVal )
+ rSettings.maScaleX = Fraction( nVal, rSettings.maScaleX.GetDenominator() );
+ }
+ else if ( rDataValue.Name == "ScaleXDenominator" )
+ {
+ sal_Int32 nVal = 1;
+ if( rDataValue.Value >>= nVal )
+ rSettings.maScaleX = Fraction( rSettings.maScaleX.GetNumerator(), nVal );
+ }
+ else if ( rDataValue.Name == "ScaleYNumerator" )
+ {
+ sal_Int32 nVal = 1;
+ if( rDataValue.Value >>= nVal )
+ rSettings.maScaleY = Fraction( nVal, rSettings.maScaleY.GetDenominator() );
+ }
+ else if ( rDataValue.Name == "ScaleYDenominator" )
+ {
+ sal_Int32 nVal = 1;
+ if( rDataValue.Value >>= nVal )
+ rSettings.maScaleY = Fraction( rSettings.maScaleY.GetNumerator(), nVal );
+ }
+ else if (rDataValue.Name == "AntiAliasing")
+ {
+ bool bAntiAliasing;
+ if (rDataValue.Value >>= bAntiAliasing)
+ rSettings.meAntiAliasing = bAntiAliasing ? TRISTATE_TRUE : TRISTATE_FALSE;
+ }
+ }
+ }
+ }
+
+ // putting the StatusIndicator that we got from the MediaDescriptor into our local FilterData copy
+ if ( rSettings.mxStatusIndicator.is() )
+ {
+ int i = rSettings.maFilterData.getLength();
+ rSettings.maFilterData.realloc( i + 1 );
+ auto pFilterData = rSettings.maFilterData.getArray();
+ pFilterData[ i ].Name = "StatusIndicator";
+ pFilterData[ i ].Value <<= rSettings.mxStatusIndicator;
+ }
+}
+
+bool GraphicExporter::GetGraphic( ExportSettings const & rSettings, Graphic& aGraphic, bool bVectorType )
+{
+ if( !mpDoc || !mpUnoPage )
+ return false;
+
+ SdrPage* pPage = mpUnoPage->GetSdrPage();
+ if( !pPage )
+ return false;
+
+ ScopedVclPtrInstance< VirtualDevice > aVDev;
+ const MapMode aMap( mpDoc->GetScaleUnit(), Point(), rSettings.maScaleX, rSettings.maScaleY );
+
+ SdrOutliner& rOutl=mpDoc->GetDrawOutliner();
+ maOldCalcFieldValueHdl = rOutl.GetCalcFieldValueHdl();
+ rOutl.SetCalcFieldValueHdl( LINK(this, GraphicExporter, CalcFieldValueHdl) );
+ rOutl.SetBackgroundColor( pPage->GetPageBackgroundColor() );
+
+ // #i102251#
+ const EEControlBits nOldCntrl(rOutl.GetControlWord());
+ EEControlBits nCntrl = nOldCntrl & ~EEControlBits::ONLINESPELLING;
+ rOutl.SetControlWord(nCntrl);
+
+ SdrObject* pTempBackgroundShape = nullptr;
+ std::vector< SdrObject* > aShapes;
+ bool bRet = true;
+
+ // export complete page?
+ if ( !mxShape.is() )
+ {
+ if( rSettings.mbExportOnlyBackground )
+ {
+ const SdrPageProperties* pCorrectProperties = pPage->getCorrectSdrPageProperties();
+
+ if(pCorrectProperties)
+ {
+ pTempBackgroundShape = new SdrRectObj(
+ *mpDoc,
+ tools::Rectangle(Point(0,0), pPage->GetSize()));
+ pTempBackgroundShape->SetMergedItemSet(pCorrectProperties->GetItemSet());
+ pTempBackgroundShape->SetMergedItem(XLineStyleItem(drawing::LineStyle_NONE));
+ pTempBackgroundShape->NbcSetStyleSheet(pCorrectProperties->GetStyleSheet(), true);
+ aShapes.push_back(pTempBackgroundShape);
+ }
+ }
+ else
+ {
+ const Size aSize( pPage->GetSize() );
+
+ // generate a bitmap to convert it to a pixel format.
+ // For gif pictures there can also be a vector format used (bTranslucent)
+ if ( !bVectorType && !rSettings.mbTranslucent )
+ {
+ tools::Long nWidthPix = 0;
+ tools::Long nHeightPix = 0;
+ if ( rSettings.mnWidth > 0 && rSettings.mnHeight > 0 )
+ {
+ nWidthPix = rSettings.mnWidth;
+ nHeightPix = rSettings.mnHeight;
+ }
+ else
+ {
+ const Size aSizePix( Application::GetDefaultDevice()->LogicToPixel( aSize, aMap ) );
+ if (aSizePix.Width() > MAX_EXT_PIX || aSizePix.Height() > MAX_EXT_PIX)
+ {
+ if (aSizePix.Width() > MAX_EXT_PIX)
+ nWidthPix = MAX_EXT_PIX;
+ else
+ nWidthPix = aSizePix.Width();
+ if (aSizePix.Height() > MAX_EXT_PIX)
+ nHeightPix = MAX_EXT_PIX;
+ else
+ nHeightPix = aSizePix.Height();
+
+ double fWidthDif = static_cast<double>(aSizePix.Width()) / nWidthPix;
+ double fHeightDif = static_cast<double>(aSizePix.Height()) / nHeightPix;
+
+ if (fWidthDif > fHeightDif)
+ nHeightPix = static_cast<tools::Long>(aSizePix.Height() / fWidthDif);
+ else
+ nWidthPix = static_cast<tools::Long>(aSizePix.Width() / fHeightDif);
+ }
+ else
+ {
+ nWidthPix = aSizePix.Width();
+ nHeightPix = aSizePix.Height();
+ }
+ }
+
+ std::unique_ptr<SdrView> xLocalView;
+
+ if (FmFormModel* pFormModel = dynamic_cast<FmFormModel*>(mpDoc))
+ {
+ xLocalView.reset(new FmFormView(*pFormModel, aVDev));
+ }
+ else
+ {
+ xLocalView.reset(new SdrView(*mpDoc, aVDev));
+ }
+
+ ScopedVclPtr<VirtualDevice> pVDev(CreatePageVDev( pPage, nWidthPix, nHeightPix ));
+
+ if( pVDev )
+ {
+ aGraphic = pVDev->GetBitmapEx( Point(), pVDev->GetOutputSize() );
+ aGraphic.SetPrefMapMode( aMap );
+ aGraphic.SetPrefSize( aSize );
+ }
+ }
+ // create a metafile to export a vector format
+ else
+ {
+ GDIMetaFile aMtf;
+
+ aVDev->SetMapMode( aMap );
+ if( rSettings.mbUseHighContrast )
+ aVDev->SetDrawMode( aVDev->GetDrawMode() | DrawModeFlags::SettingsLine | DrawModeFlags::SettingsFill | DrawModeFlags::SettingsText | DrawModeFlags::SettingsGradient );
+ aVDev->EnableOutput( false );
+ aMtf.Record( aVDev );
+ Size aNewSize;
+
+ // create a view
+ std::unique_ptr< SdrView > pView;
+
+ if (FmFormModel *pFormModel = dynamic_cast<FmFormModel*>(mpDoc))
+ {
+ pView.reset(new FmFormView(*pFormModel, aVDev));
+ }
+ else
+ {
+ pView.reset(new SdrView(*mpDoc, aVDev));
+ }
+
+ pView->SetBordVisible( false );
+ pView->SetPageVisible( false );
+ pView->ShowSdrPage( pPage );
+
+ // tdf#96922 completely deactivate EditView PageVisualization, including
+ // PageBackground (formerly 'wiese').
+ pView->SetPagePaintingAllowed(false);
+
+ const Point aNewOrg( pPage->GetLeftBorder(), pPage->GetUpperBorder() );
+ aNewSize = Size( aSize.Width() - pPage->GetLeftBorder() - pPage->GetRightBorder(),
+ aSize.Height() - pPage->GetUpperBorder() - pPage->GetLowerBorder() );
+ const tools::Rectangle aClipRect( aNewOrg, aNewSize );
+ MapMode aVMap( aMap );
+
+ aVDev->Push();
+ aVMap.SetOrigin( Point( -aNewOrg.X(), -aNewOrg.Y() ) );
+ aVDev->SetRelativeMapMode( aVMap );
+ aVDev->IntersectClipRegion( aClipRect );
+
+ // Use new StandardCheckVisisbilityRedirector
+ ImplExportCheckVisisbilityRedirector aRedirector( mpCurrentPage );
+
+ pView->CompleteRedraw(aVDev, vcl::Region(tools::Rectangle(aNewOrg, aNewSize)), &aRedirector);
+
+ aVDev->Pop();
+
+ aMtf.Stop();
+ aMtf.WindStart();
+ aMtf.SetPrefMapMode( aMap );
+ aMtf.SetPrefSize( aNewSize );
+
+ // AW: Here the current version was filtering out the MetaActionType::CLIPREGIONs
+ // from the metafile. I asked some other developers why this was done, but no
+ // one knew a direct reason. Since it's in for long time, it may be an old
+ // piece of code. MetaFiles save and load ClipRegions with polygons with preserving
+ // the polygons, so a resolution-independent roundtrip is supported. Removed this
+ // code since it destroys some MetaFiles where ClipRegions are used. Anyways,
+ // just filtering them out is a hack, at least the encapsulated content would need
+ // to be clipped geometrically.
+ aGraphic = Graphic(aMtf);
+
+ pView->HideSdrPage();
+
+ if( rSettings.mbTranslucent )
+ {
+ Size aOutSize;
+ aGraphic = GetBitmapFromMetaFile( aGraphic.GetGDIMetaFile(), CalcSize( rSettings.mnWidth, rSettings.mnHeight, aNewSize, aOutSize ) );
+ }
+ }
+ }
+ }
+
+ // export only single shape or shape collection
+ else
+ {
+ // build list of SdrObject
+ if( mxShapes.is() )
+ {
+ Reference< XShape > xShape;
+ const sal_Int32 nCount = mxShapes->getCount();
+
+ for( sal_Int32 nIndex = 0; nIndex < nCount; nIndex++ )
+ {
+ mxShapes->getByIndex( nIndex ) >>= xShape;
+ SdrObject* pObj = SdrObject::getSdrObjectFromXShape(xShape);
+ if( pObj )
+ aShapes.push_back( pObj );
+ }
+ }
+ else
+ {
+ // only one shape
+ SdrObject* pObj = SdrObject::getSdrObjectFromXShape(mxShape);
+ if( pObj )
+ aShapes.push_back( pObj );
+ }
+
+ if( aShapes.empty() )
+ bRet = false;
+ }
+
+ if( bRet && !aShapes.empty() )
+ {
+ // special treatment for only one SdrGrafObj that has text
+ bool bSingleGraphic = false;
+
+ if( 1 == aShapes.size() )
+ {
+ if( !bVectorType )
+ {
+ if( auto pGrafObj = dynamic_cast<const SdrGrafObj*>(aShapes.front()) )
+ if (pGrafObj->HasText() )
+ {
+ aGraphic = pGrafObj->GetTransformedGraphic();
+ if ( aGraphic.GetType() == GraphicType::Bitmap )
+ {
+ Size aSizePixel( aGraphic.GetSizePixel() );
+ if( rSettings.mnWidth && rSettings.mnHeight &&
+ ( ( rSettings.mnWidth != aSizePixel.Width() ) ||
+ ( rSettings.mnHeight != aSizePixel.Height() ) ) )
+ {
+ BitmapEx aBmpEx( aGraphic.GetBitmapEx() );
+ // export: use highest quality
+ aBmpEx.Scale( Size( rSettings.mnWidth, rSettings.mnHeight ), BmpScaleFlag::Lanczos );
+ aGraphic = aBmpEx;
+ }
+
+ // #118804# only accept for bitmap graphics, else the
+ // conversion to bitmap will happen anywhere without size control
+ // as evtl. defined in rSettings.mnWidth/mnHeight
+ bSingleGraphic = true;
+ }
+ }
+ }
+ else if( rSettings.mbScrollText )
+ {
+ SdrObject* pObj = aShapes.front();
+ auto pTextObj = dynamic_cast<SdrTextObj*>( pObj);
+ if( pTextObj && pTextObj->HasText() )
+ {
+ tools::Rectangle aScrollRectangle;
+ tools::Rectangle aPaintRectangle;
+
+ const std::unique_ptr< GDIMetaFile > pMtf(
+ pTextObj->GetTextScrollMetaFileAndRectangle(
+ aScrollRectangle, aPaintRectangle ) );
+
+ // take the larger one of the two rectangles (that
+ // should be the bound rect of the retrieved
+ // metafile)
+ tools::Rectangle aTextRect;
+
+ if( aScrollRectangle.Contains( aPaintRectangle ) )
+ aTextRect = aScrollRectangle;
+ else
+ aTextRect = aPaintRectangle;
+
+ // setup pref size and mapmode
+ pMtf->SetPrefSize( aTextRect.GetSize() );
+
+ // set actual origin (mtf is at actual shape
+ // output position)
+ MapMode aLocalMapMode( aMap );
+ aLocalMapMode.SetOrigin(
+ Point( -aPaintRectangle.Left(),
+ -aPaintRectangle.Top() ) );
+ pMtf->SetPrefMapMode( aLocalMapMode );
+
+ pMtf->AddAction( new MetaCommentAction(
+ "XTEXT_SCROLLRECT", 0,
+ reinterpret_cast<sal_uInt8 const*>(&aScrollRectangle),
+ sizeof( tools::Rectangle ) ) );
+ pMtf->AddAction( new MetaCommentAction(
+ "XTEXT_PAINTRECT", 0,
+ reinterpret_cast<sal_uInt8 const*>(&aPaintRectangle),
+ sizeof( tools::Rectangle ) ) );
+
+ aGraphic = Graphic( *pMtf );
+
+ bSingleGraphic = true;
+ }
+ }
+ }
+
+ if( !bSingleGraphic )
+ {
+ // create a metafile for all shapes
+ ScopedVclPtrInstance< VirtualDevice > aOut;
+
+ // calculate bound rect for all shapes
+ // tdf#126319 I did not convert all rendering to primities,
+ // that would be to much for this fix. But I did so for the
+ // range calculation to get a valid high quality range.
+ // Based on that the conversion is reliable. With the BoundRect
+ // fetched from the Metafile it was just not possible to get the
+ // examples from the task handled in a way to fit all cases -
+ // due to bad-quality range data from it.
+ basegfx::B2DRange aBound;
+ const drawinglayer::geometry::ViewInformation2D aViewInformation2D;
+
+ {
+ for( SdrObject* pObj : aShapes )
+ {
+ drawinglayer::primitive2d::Primitive2DContainer aSequence;
+ pObj->GetViewContact().getViewIndependentPrimitive2DContainer(aSequence);
+ aBound.expand(aSequence.getB2DRange(aViewInformation2D));
+ }
+ }
+
+ aOut->EnableOutput( false );
+ aOut->SetMapMode( aMap );
+ if( rSettings.mbUseHighContrast )
+ aOut->SetDrawMode( aOut->GetDrawMode() | DrawModeFlags::SettingsLine | DrawModeFlags::SettingsFill | DrawModeFlags::SettingsText | DrawModeFlags::SettingsGradient );
+
+ GDIMetaFile aMtf;
+ aMtf.Clear();
+ aMtf.Record( aOut );
+
+ MapMode aOutMap( aMap );
+ const Size aOnePixelInMtf(
+ Application::GetDefaultDevice()->PixelToLogic(
+ Size(1, 1),
+ aMap));
+ const Size aHalfPixelInMtf(
+ (aOnePixelInMtf.getWidth() + 1) / 2,
+ (aOnePixelInMtf.getHeight() + 1) / 2);
+
+ // tdf#126319 Immediately add needed offset to create metafile,
+ // that avoids to do it later by Metafile::Move what would be expensive
+ aOutMap.SetOrigin(
+ Point(
+ basegfx::fround(-aBound.getMinX() - aHalfPixelInMtf.getWidth()),
+ basegfx::fround(-aBound.getMinY() - aHalfPixelInMtf.getHeight()) ) );
+ aOut->SetRelativeMapMode( aOutMap );
+
+ sdr::contact::DisplayInfo aDisplayInfo;
+
+ if(mpCurrentPage)
+ {
+ if(mpCurrentPage->TRG_HasMasterPage() && pPage->IsMasterPage())
+ {
+ // MasterPage is processed as another page's SubContent
+ aDisplayInfo.SetProcessLayers(mpCurrentPage->TRG_GetMasterPageVisibleLayers());
+ aDisplayInfo.SetSubContentActive(true);
+ }
+ }
+
+ if(!aShapes.empty())
+ {
+ // more effective way to paint a vector of SdrObjects. Hand over the processed page
+ // to have it in the
+ ImplExportCheckVisisbilityRedirector aCheckVisibilityRedirector(mpCurrentPage);
+ sdr::contact::ObjectContactOfObjListPainter aMultiObjectPainter(*aOut, std::move(aShapes), mpCurrentPage);
+ aMultiObjectPainter.SetViewObjectContactRedirector(&aCheckVisibilityRedirector);
+
+ aMultiObjectPainter.ProcessDisplay(aDisplayInfo);
+ }
+
+ aMtf.Stop();
+ aMtf.WindStart();
+
+ // tdf#126319 Immediately add needed size to target's PrefSize
+ const Size aBoundSize(
+ basegfx::fround(aBound.getWidth() + aHalfPixelInMtf.getWidth()),
+ basegfx::fround(aBound.getHeight() + aHalfPixelInMtf.getHeight()));
+
+ aMtf.SetPrefMapMode( aMap );
+ aMtf.SetPrefSize( aBoundSize );
+
+ if( !bVectorType )
+ {
+ Size aOutSize;
+ aGraphic = GetBitmapFromMetaFile( aMtf, CalcSize( rSettings.mnWidth, rSettings.mnHeight, aBoundSize, aOutSize ) );
+ }
+ else
+ {
+ aGraphic = aMtf;
+ }
+ }
+ }
+
+ if(pTempBackgroundShape)
+ {
+ SdrObject::Free(pTempBackgroundShape);
+ }
+
+ rOutl.SetCalcFieldValueHdl( maOldCalcFieldValueHdl );
+
+ // #i102251#
+ rOutl.SetControlWord(nOldCntrl);
+
+ return bRet;
+
+}
+
+// XFilter
+sal_Bool SAL_CALL GraphicExporter::filter( const Sequence< PropertyValue >& aDescriptor )
+{
+ ::SolarMutexGuard aGuard;
+
+ if( maGraphic.IsNone() && nullptr == mpUnoPage )
+ return false;
+
+ if( maGraphic.IsNone() && ( nullptr == mpUnoPage->GetSdrPage() || nullptr == mpDoc ) )
+ return false;
+
+ GraphicFilter &rFilter = GraphicFilter::GetGraphicFilter();
+
+ // get the arguments from the descriptor
+ ExportSettings aSettings(mpDoc);
+ ParseSettings(aDescriptor, aSettings);
+
+ const sal_uInt16 nFilter = !aSettings.maMediaType.isEmpty()
+ ? rFilter.GetExportFormatNumberForMediaType( aSettings.maMediaType )
+ : rFilter.GetExportFormatNumberForShortName( aSettings.maFilterName );
+ bool bVectorType = !rFilter.IsExportPixelFormat( nFilter );
+
+ // create the output stuff
+ Graphic aGraphic = maGraphic;
+
+ ErrCode nStatus = ERRCODE_NONE;
+ if (maGraphic.IsNone())
+ {
+ bool bAntiAliasing = SvtOptionsDrawinglayer::IsAntiAliasing();
+ AllSettings aAllSettings = Application::GetSettings();
+ StyleSettings aStyleSettings = aAllSettings.GetStyleSettings();
+ bool bUseFontAAFromSystem = aStyleSettings.GetUseFontAAFromSystem();
+ if (aSettings.meAntiAliasing != TRISTATE_INDET)
+ {
+ // This is safe to do globally as we own the solar mutex.
+ SvtOptionsDrawinglayer::SetAntiAliasing(aSettings.meAntiAliasing == TRISTATE_TRUE, /*bTemporary*/true);
+ // Opt in to have AA affect font rendering as well.
+ aStyleSettings.SetUseFontAAFromSystem(false);
+ aAllSettings.SetStyleSettings(aStyleSettings);
+ Application::SetSettings(aAllSettings);
+ }
+ nStatus = GetGraphic( aSettings, aGraphic, bVectorType ) ? ERRCODE_NONE : ERRCODE_GRFILTER_FILTERERROR;
+ if (aSettings.meAntiAliasing != TRISTATE_INDET)
+ {
+ SvtOptionsDrawinglayer::SetAntiAliasing(bAntiAliasing, /*bTemporary*/true);
+ aStyleSettings.SetUseFontAAFromSystem(bUseFontAAFromSystem);
+ aAllSettings.SetStyleSettings(aStyleSettings);
+ Application::SetSettings(aAllSettings);
+ }
+ }
+
+ if( nStatus == ERRCODE_NONE )
+ {
+ // export graphic only if it has a size
+ const Size aGraphSize( aGraphic.GetPrefSize() );
+ if ( aGraphSize.IsEmpty() )
+ {
+ nStatus = ERRCODE_GRFILTER_FILTERERROR;
+ }
+ else
+ {
+ // now we have a graphic, so export it
+ if( aSettings.mxGraphicRenderer.is() )
+ {
+ // render graphic directly into given renderer
+ aSettings.mxGraphicRenderer->render( aGraphic.GetXGraphic() );
+ }
+ else if( aSettings.mxOutputStream.is() )
+ {
+ // TODO: Either utilize optional XSeekable functionality for the
+ // SvOutputStream, or adapt the graphic filter to not seek anymore.
+ SvMemoryStream aStream( 1024, 1024 );
+
+ nStatus = rFilter.ExportGraphic( aGraphic, u"", aStream, nFilter, &aSettings.maFilterData );
+
+ // copy temp stream to XOutputStream
+ SvOutputStream aOutputStream( aSettings.mxOutputStream );
+ aStream.Seek(0);
+ aOutputStream.WriteStream( aStream );
+ }
+ else
+ {
+ INetURLObject aURLObject( aSettings.maURL.Complete );
+ DBG_ASSERT( aURLObject.GetProtocol() != INetProtocol::NotValid, "invalid URL" );
+
+ nStatus = XOutBitmap::ExportGraphic( aGraphic, aURLObject, rFilter, nFilter, &aSettings.maFilterData );
+ }
+ }
+ }
+
+ if ( aSettings.mxInteractionHandler.is() && ( nStatus != ERRCODE_NONE ) )
+ {
+ Any aInteraction;
+ Sequence< css::uno::Reference< css::task::XInteractionContinuation > > lContinuations{
+ new ::comphelper::OInteractionApprove()
+ };
+
+ GraphicFilterRequest aErrorCode;
+ aErrorCode.ErrCode = sal_uInt32(nStatus);
+ aInteraction <<= aErrorCode;
+ aSettings.mxInteractionHandler->handle( framework::InteractionRequest::CreateRequest( aInteraction, lContinuations ) );
+ }
+ return nStatus == ERRCODE_NONE;
+}
+
+void SAL_CALL GraphicExporter::cancel()
+{
+}
+
+// XExporter
+
+/** the source 'document' could be a XDrawPage, a XShape or a generic XShapes */
+void SAL_CALL GraphicExporter::setSourceDocument( const Reference< lang::XComponent >& xComponent )
+{
+ ::SolarMutexGuard aGuard;
+
+ mxShapes = nullptr;
+ mpUnoPage = nullptr;
+
+ try
+ {
+ // any break inside this one loop while will throw an IllegalArgumentException
+ do
+ {
+ mxPage.set( xComponent, UNO_QUERY );
+ mxShapes.set( xComponent, UNO_QUERY );
+ mxShape.set( xComponent, UNO_QUERY );
+
+ // Step 1: try a generic XShapes
+ if( !mxPage.is() && !mxShape.is() && mxShapes.is() )
+ {
+ // we do not support empty shape collections
+ if( 0 == mxShapes->getCount() )
+ break;
+
+ // get first shape to detect corresponding page and model
+ mxShapes->getByIndex(0) >>= mxShape;
+ }
+ else
+ {
+ mxShapes = nullptr;
+ }
+
+ // Step 2: try a shape
+ if( mxShape.is() )
+ {
+ if (nullptr == SdrObject::getSdrObjectFromXShape(mxShape))
+ {
+ // This is not a Draw shape, let's see if it's a Writer one.
+ uno::Reference<beans::XPropertySet> xPropertySet(mxShape, uno::UNO_QUERY);
+ if (!xPropertySet.is())
+ break;
+ uno::Reference<graphic::XGraphic> xGraphic(
+ xPropertySet->getPropertyValue("Graphic"), uno::UNO_QUERY);
+ if (!xGraphic.is())
+ break;
+
+ maGraphic = Graphic(xGraphic);
+ if (!maGraphic.IsNone())
+ return;
+ else
+ break;
+ }
+
+ // get page for this shape
+ Reference< XChild > xChild( mxShape, UNO_QUERY );
+ if( !xChild.is() )
+ break;
+
+ Reference< XInterface > xInt;
+ do
+ {
+ xInt = xChild->getParent();
+ mxPage.set( xInt, UNO_QUERY );
+ if( !mxPage.is() )
+ xChild.set( xInt, UNO_QUERY );
+ }
+ while( !mxPage.is() && xChild.is() );
+
+ if( !mxPage.is() )
+ break;
+ }
+
+ // Step 3: check the page
+ if( !mxPage.is() )
+ break;
+
+ mpUnoPage = comphelper::getFromUnoTunnel<SvxDrawPage>( mxPage );
+
+ if( nullptr == mpUnoPage || nullptr == mpUnoPage->GetSdrPage() )
+ break;
+
+ mpDoc = &mpUnoPage->GetSdrPage()->getSdrModelFromSdrPage();
+
+ // Step 4: If we got a generic XShapes test all contained shapes
+ // if they belong to the same XDrawPage
+
+ if( mxShapes.is() )
+ {
+ SdrPage* pPage = mpUnoPage->GetSdrPage();
+ SdrObject* pObj;
+ Reference< XShape > xShape;
+
+ bool bOk = true;
+
+ const sal_Int32 nCount = mxShapes->getCount();
+
+ // test all but the first shape if they have the same page than
+ // the first shape
+ for( sal_Int32 nIndex = 1; bOk && ( nIndex < nCount ); nIndex++ )
+ {
+ mxShapes->getByIndex( nIndex ) >>= xShape;
+ pObj = SdrObject::getSdrObjectFromXShape(xShape);
+ bOk = pObj && pObj->getSdrPageFromSdrObject() == pPage;
+ }
+
+ if( !bOk )
+ break;
+ }
+
+ // no errors so far
+ return;
+ }
+ while( false );
+ }
+ catch( Exception& )
+ {
+ }
+
+ throw IllegalArgumentException();
+}
+
+// XServiceInfo
+OUString SAL_CALL GraphicExporter::getImplementationName( )
+{
+ return "com.sun.star.comp.Draw.GraphicExporter";
+}
+
+sal_Bool SAL_CALL GraphicExporter::supportsService( const OUString& ServiceName )
+{
+ return cppu::supportsService(this, ServiceName);
+}
+
+Sequence< OUString > SAL_CALL GraphicExporter::getSupportedServiceNames( )
+{
+ Sequence< OUString > aSupportedServiceNames { "com.sun.star.drawing.GraphicExportFilter" };
+ return aSupportedServiceNames;
+}
+
+// XMimeTypeInfo
+sal_Bool SAL_CALL GraphicExporter::supportsMimeType( const OUString& rMimeTypeName )
+{
+ GraphicFilter &rFilter = GraphicFilter::GetGraphicFilter();
+ sal_uInt16 nCount = rFilter.GetExportFormatCount();
+ sal_uInt16 nFilter;
+ for( nFilter = 0; nFilter < nCount; nFilter++ )
+ {
+ if( rMimeTypeName == rFilter.GetExportFormatMediaType( nFilter ) )
+ {
+ return true;
+ }
+ }
+
+ return false;
+}
+
+Sequence< OUString > SAL_CALL GraphicExporter::getSupportedMimeTypeNames( )
+{
+ GraphicFilter &rFilter = GraphicFilter::GetGraphicFilter();
+ sal_uInt16 nCount = rFilter.GetExportFormatCount();
+ sal_uInt16 nFilter;
+ sal_uInt16 nFound = 0;
+
+ Sequence< OUString > aSeq( nCount );
+ OUString* pStr = aSeq.getArray();
+
+ for( nFilter = 0; nFilter < nCount; nFilter++ )
+ {
+ OUString aMimeType( rFilter.GetExportFormatMediaType( nFilter ) );
+ if( !aMimeType.isEmpty() )
+ {
+ *pStr++ = aMimeType;
+ nFound++;
+ }
+ }
+
+ if( nFound < nCount )
+ aSeq.realloc( nFound );
+
+ return aSeq;
+}
+
+}
+
+Graphic SvxGetGraphicForShape( SdrObject& rShape )
+{
+ Graphic aGraphic;
+ try
+ {
+ rtl::Reference< GraphicExporter > xExporter( new GraphicExporter() );
+ Reference< XComponent > xComp( rShape.getUnoShape(), UNO_QUERY_THROW );
+ xExporter->setSourceDocument( xComp );
+ ExportSettings aSettings(&rShape.getSdrModelFromSdrObject());
+ xExporter->GetGraphic( aSettings, aGraphic, true/*bVector*/ );
+ }
+ catch( Exception& )
+ {
+ TOOLS_WARN_EXCEPTION("svx", "");
+ }
+ return aGraphic;
+}
+
+extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface *
+com_sun_star_comp_Draw_GraphicExporter_get_implementation(
+ css::uno::XComponentContext *,
+ css::uno::Sequence<css::uno::Any> const &)
+{
+ return cppu::acquire(new GraphicExporter);
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/unodraw/UnoGraphicExporter.hxx b/svx/source/unodraw/UnoGraphicExporter.hxx
new file mode 100644
index 000000000..ace0db252
--- /dev/null
+++ b/svx/source/unodraw/UnoGraphicExporter.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_SVX_SOURCE_UNODRAW_UNOGRAPHICEXPORTER_HXX
+#define INCLUDED_SVX_SOURCE_UNODRAW_UNOGRAPHICEXPORTER_HXX
+
+#include <sal/config.h>
+
+#include <vcl/graph.hxx>
+
+class SdrObject;
+
+Graphic SvxGetGraphicForShape(SdrObject& rShape);
+
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/unodraw/UnoNameItemTable.cxx b/svx/source/unodraw/UnoNameItemTable.cxx
new file mode 100644
index 000000000..67b1dcc7d
--- /dev/null
+++ b/svx/source/unodraw/UnoNameItemTable.cxx
@@ -0,0 +1,282 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+
+#include <set>
+
+#include <svl/itempool.hxx>
+#include <svl/itemset.hxx>
+#include <comphelper/profilezone.hxx>
+#include <comphelper/sequence.hxx>
+#include <cppuhelper/supportsservice.hxx>
+
+#include <svx/svdmodel.hxx>
+#include "UnoNameItemTable.hxx"
+#include <vcl/svapp.hxx>
+
+#include <svx/unoapi.hxx>
+#include <memory>
+
+using namespace ::com::sun::star;
+using namespace ::cppu;
+
+namespace
+{
+ // We need to override operator== here and specifically bypass the assert
+ // in SfxPoolItem::operator== in order to make the FindItemSurrogate call
+ // in SvxUnoNameItemTable::hasByName safe.
+ class SampleItem : public NameOrIndex
+ {
+ public:
+ SampleItem(sal_uInt16 nWhich, const OUString& rName) : NameOrIndex(TypedWhichId<NameOrIndex>(nWhich), rName) {}
+
+ bool operator==(const SfxPoolItem& rCmp) const
+ {
+ assert(dynamic_cast<const NameOrIndex*>(&rCmp) && "comparing different pool item subclasses");
+ auto const & rOther = static_cast<const NameOrIndex&>(rCmp);
+ return GetName() == rOther.GetName() && GetPalIndex() == rOther.GetPalIndex();
+ }
+ };
+
+}
+
+
+SvxUnoNameItemTable::SvxUnoNameItemTable( SdrModel* pModel, sal_uInt16 nWhich, sal_uInt8 nMemberId ) noexcept
+: mpModel( pModel ),
+ mpModelPool( pModel ? &pModel->GetItemPool() : nullptr ),
+ mnWhich( nWhich ), mnMemberId( nMemberId )
+{
+ if( pModel )
+ StartListening( *pModel );
+}
+
+SvxUnoNameItemTable::~SvxUnoNameItemTable() noexcept
+{
+ SolarMutexGuard aGuard;
+
+ if( mpModel )
+ EndListening( *mpModel );
+ dispose();
+}
+
+bool SvxUnoNameItemTable::isValid( const NameOrIndex* pItem ) const
+{
+ return pItem && !pItem->GetName().isEmpty();
+}
+
+void SvxUnoNameItemTable::dispose()
+{
+ maItemSetVector.clear();
+}
+
+void SvxUnoNameItemTable::Notify( SfxBroadcaster&, const SfxHint& rHint ) noexcept
+{
+ if (rHint.GetId() != SfxHintId::ThisIsAnSdrHint)
+ return;
+ const SdrHint* pSdrHint = static_cast<const SdrHint*>(&rHint);
+ if( SdrHintKind::ModelCleared == pSdrHint->GetKind() )
+ dispose();
+}
+
+sal_Bool SAL_CALL SvxUnoNameItemTable::supportsService( const OUString& ServiceName )
+{
+ return cppu::supportsService(this, ServiceName);
+}
+
+void SvxUnoNameItemTable::ImplInsertByName( const OUString& aName, const uno::Any& aElement )
+{
+ maItemSetVector.push_back( std::make_unique< SfxItemSet >( *mpModelPool, mnWhich, mnWhich ) );
+
+ std::unique_ptr<NameOrIndex> xNewItem(createItem());
+ xNewItem->SetName(aName);
+ xNewItem->PutValue(aElement, mnMemberId);
+ xNewItem->SetWhich(mnWhich);
+ maItemSetVector.back()->Put(std::move(xNewItem));
+}
+
+// XNameContainer
+void SAL_CALL SvxUnoNameItemTable::insertByName( const OUString& aApiName, const uno::Any& aElement )
+{
+ SolarMutexGuard aGuard;
+ comphelper::ProfileZone aZone("SvxUnoNameItemTable::insertByName");
+
+ if( hasByName( aApiName ) )
+ throw container::ElementExistException();
+
+ OUString aName = SvxUnogetInternalNameForItem(mnWhich, aApiName);
+
+ ImplInsertByName( aName, aElement );
+}
+
+void SAL_CALL SvxUnoNameItemTable::cancel()
+{
+ SolarMutexGuard aGuard;
+ // drop all items that are owned by this service and not the document
+ // (i.e. they are unused)
+ dispose();
+}
+
+void SAL_CALL SvxUnoNameItemTable::removeByName( const OUString& aApiName )
+{
+ SolarMutexGuard aGuard;
+ comphelper::ProfileZone aZone("SvxUnoNameItemTable::removeByName");
+
+ OUString sName = SvxUnogetInternalNameForItem(mnWhich, aApiName);
+
+ auto aIter = std::find_if(maItemSetVector.begin(), maItemSetVector.end(),
+ [&](const std::unique_ptr<SfxItemSet>& rpItem) {
+ const NameOrIndex *pItem = static_cast<const NameOrIndex *>(&(rpItem->Get( mnWhich ) ));
+ return sName == pItem->GetName();
+ });
+ if (aIter != maItemSetVector.end())
+ {
+ maItemSetVector.erase( aIter );
+ return;
+ }
+
+ if (!hasByName(sName))
+ throw container::NoSuchElementException();
+}
+
+// XNameReplace
+void SAL_CALL SvxUnoNameItemTable::replaceByName( const OUString& aApiName, const uno::Any& aElement )
+{
+ SolarMutexGuard aGuard;
+
+ OUString aName = SvxUnogetInternalNameForItem(mnWhich, aApiName);
+
+ auto aIter = std::find_if(maItemSetVector.begin(), maItemSetVector.end(),
+ [&](const std::unique_ptr<SfxItemSet>& rpItem) {
+ const NameOrIndex *pItem = static_cast<const NameOrIndex *>(&(rpItem->Get( mnWhich ) ));
+ return aName == pItem->GetName();
+ });
+ if (aIter != maItemSetVector.end())
+ {
+ std::unique_ptr<NameOrIndex> xNewItem(createItem());
+ xNewItem->SetName(aName);
+ if (!xNewItem->PutValue(aElement, mnMemberId) || !isValid(xNewItem.get()))
+ throw lang::IllegalArgumentException();
+ (*aIter)->Put(std::move(xNewItem));
+ return;
+ }
+
+ // if it is not in our own sets, modify the pool!
+ bool bFound = false;
+
+ if (mpModelPool)
+ {
+ SampleItem aSample(mnWhich, aName);
+ for (const SfxPoolItem* pNameOrIndex : mpModelPool->FindItemSurrogate(mnWhich, aSample))
+ if (isValid(static_cast<const NameOrIndex*>(pNameOrIndex)))
+ {
+ const_cast<SfxPoolItem*>(pNameOrIndex)->PutValue( aElement, mnMemberId );
+ bFound = true;
+ }
+ }
+
+ if( !bFound )
+ throw container::NoSuchElementException();
+
+ ImplInsertByName( aName, aElement );
+
+ if( !hasByName( aName ) )
+ throw container::NoSuchElementException();
+}
+
+// XNameAccess
+uno::Any SAL_CALL SvxUnoNameItemTable::getByName( const OUString& aApiName )
+{
+ SolarMutexGuard aGuard;
+ comphelper::ProfileZone aZone("SvxUnoNameItemTable::getByName");
+
+ OUString aName = SvxUnogetInternalNameForItem(mnWhich, aApiName);
+
+ if (mpModelPool && !aName.isEmpty())
+ {
+ SampleItem aSample(mnWhich, aName);
+ for (const SfxPoolItem* pFindItem : mpModelPool->FindItemSurrogate(mnWhich, aSample))
+ if (isValid(static_cast<const NameOrIndex*>(pFindItem)))
+ {
+ uno::Any aAny;
+ pFindItem->QueryValue( aAny, mnMemberId );
+ return aAny;
+ }
+ }
+
+ throw container::NoSuchElementException();
+}
+
+uno::Sequence< OUString > SAL_CALL SvxUnoNameItemTable::getElementNames( )
+{
+ SolarMutexGuard aGuard;
+
+ std::set< OUString > aNameSet;
+
+
+ if (mpModelPool)
+ for (const SfxPoolItem* pItem : mpModelPool->GetItemSurrogates(mnWhich))
+ {
+ const NameOrIndex *pNameOrIndex = static_cast<const NameOrIndex*>(pItem);
+
+ if( !isValid( pNameOrIndex ) )
+ continue;
+
+ OUString aApiName = SvxUnogetApiNameForItem(mnWhich, pNameOrIndex->GetName());
+ aNameSet.insert(aApiName);
+ }
+
+ return comphelper::containerToSequence(aNameSet);
+}
+
+sal_Bool SAL_CALL SvxUnoNameItemTable::hasByName( const OUString& aApiName )
+{
+ SolarMutexGuard aGuard;
+
+ OUString aName = SvxUnogetInternalNameForItem(mnWhich, aApiName);
+
+ if (aName.isEmpty())
+ return false;
+
+ if (!mpModelPool)
+ return false;
+
+ SampleItem aSample(mnWhich, aName);
+ for (const SfxPoolItem* pFindItem : mpModelPool->FindItemSurrogate(mnWhich, aSample))
+ if (isValid(static_cast<const NameOrIndex*>(pFindItem)))
+ return true;
+ return false;
+}
+
+sal_Bool SAL_CALL SvxUnoNameItemTable::hasElements( )
+{
+ SolarMutexGuard aGuard;
+
+ if (mpModelPool)
+ for (const SfxPoolItem* pItem : mpModelPool->GetItemSurrogates(mnWhich))
+ {
+ const NameOrIndex *pNameOrIndex = static_cast<const NameOrIndex*>(pItem);
+
+ if( isValid( pNameOrIndex ) )
+ return true;
+ }
+
+ return false;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/unodraw/UnoNameItemTable.hxx b/svx/source/unodraw/UnoNameItemTable.hxx
new file mode 100644
index 000000000..c1c798869
--- /dev/null
+++ b/svx/source/unodraw/UnoNameItemTable.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_SVX_SOURCE_UNODRAW_UNONAMEITEMTABLE_HXX
+#define INCLUDED_SVX_SOURCE_UNODRAW_UNONAMEITEMTABLE_HXX
+
+#include <com/sun/star/lang/XServiceInfo.hpp>
+#include <com/sun/star/container/XNameContainer.hpp>
+#include <com/sun/star/util/XCancellable.hpp>
+
+#include <cppuhelper/implbase.hxx>
+
+#include <memory>
+#include <vector>
+#include <svl/lstner.hxx>
+#include <svx/xit.hxx>
+
+class SdrModel;
+class SfxItemPool;
+class SfxItemSet;
+
+typedef std::vector< std::unique_ptr< SfxItemSet > > ItemPoolVector;
+class SvxUnoNameItemTable
+ : public cppu::WeakImplHelper<
+ css::util::XCancellable,
+ css::container::XNameContainer,
+ css::lang::XServiceInfo >
+ , public SfxListener
+{
+private:
+ SdrModel* mpModel;
+ SfxItemPool* mpModelPool;
+ sal_uInt16 mnWhich;
+ sal_uInt8 mnMemberId;
+
+ /// vector contains all items that were created by this service and will
+ /// keep them alive even if nothing in the document references them
+ ItemPoolVector maItemSetVector;
+
+ void ImplInsertByName( const OUString& aName, const css::uno::Any& aElement );
+
+public:
+ SvxUnoNameItemTable( SdrModel* pModel, sal_uInt16 nWhich, sal_uInt8 nMemberId ) noexcept;
+ virtual ~SvxUnoNameItemTable() noexcept override;
+
+ virtual NameOrIndex* createItem() const = 0;
+ virtual bool isValid( const NameOrIndex* pItem ) const;
+
+ void dispose();
+
+ // SfxListener
+ virtual void Notify( SfxBroadcaster& rBC, const SfxHint& rHint ) noexcept override;
+
+ // XServiceInfo
+ virtual sal_Bool SAL_CALL supportsService( const OUString& ServiceName ) override;
+
+ // XCancellable
+ virtual void SAL_CALL cancel() override;
+
+ // 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 sal_Bool SAL_CALL hasElements( ) override;
+};
+
+#endif // INCLUDED_SVX_SOURCE_UNODRAW_UNONAMEITEMTABLE_HXX
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/unodraw/UnoNamespaceMap.cxx b/svx/source/unodraw/UnoNamespaceMap.cxx
new file mode 100644
index 000000000..cd49cc0a6
--- /dev/null
+++ b/svx/source/unodraw/UnoNamespaceMap.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 <climits>
+#include <set>
+
+#include <svx/UnoNamespaceMap.hxx>
+#include <com/sun/star/container/XNameAccess.hpp>
+#include <com/sun/star/lang/XServiceInfo.hpp>
+
+#include <comphelper/sequence.hxx>
+#include <cppuhelper/implbase.hxx>
+#include <cppuhelper/supportsservice.hxx>
+#include <svl/itempool.hxx>
+#include <editeng/xmlcnitm.hxx>
+
+using namespace ::cppu;
+using namespace ::com::sun::star;
+using namespace ::com::sun::star::uno;
+using namespace ::com::sun::star::container;
+using namespace ::com::sun::star::lang;
+
+namespace svx
+{
+ namespace {
+
+ /** implements a component to export namespaces of all SvXMLAttrContainerItem inside
+ one or two pools with a variable count of which ids.
+ */
+ class NamespaceMap : public WeakImplHelper< XNameAccess, XServiceInfo >
+ {
+ private:
+ sal_uInt16* mpWhichIds;
+ SfxItemPool* mpPool;
+
+ public:
+ NamespaceMap( sal_uInt16* pWhichIds, SfxItemPool* pPool );
+
+ // XNameAccess
+ virtual Any SAL_CALL getByName( const OUString& aName ) override;
+ virtual Sequence< OUString > SAL_CALL getElementNames( ) override;
+ virtual sal_Bool SAL_CALL hasByName( const OUString& aName ) override;
+
+ // XElementAccess
+ virtual 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 Sequence< OUString > SAL_CALL getSupportedServiceNames( ) override;
+ };
+
+ }
+
+ Reference< XInterface > NamespaceMap_createInstance( sal_uInt16* pWhichIds, SfxItemPool* pPool )
+ {
+ return static_cast<XWeak*>(new NamespaceMap( pWhichIds, pPool ));
+ }
+
+ static Sequence< OUString > NamespaceMap_getSupportedServiceNames()
+ noexcept
+ {
+ Sequence<OUString> aSupportedServiceNames { "com.sun.star.xml.NamespaceMap" };
+ return aSupportedServiceNames;
+ }
+
+ static OUString NamespaceMap_getImplementationName()
+ noexcept
+ {
+ return "com.sun.star.comp.Svx.NamespaceMap";
+ }
+
+ namespace {
+
+ class NamespaceIteratorImpl
+ {
+ private:
+ SfxItemPool* mpPool;
+
+ sal_uInt16* mpWhichId;
+
+ std::vector<const SvXMLAttrContainerItem*> mvItems;
+ sal_Int32 mnItem;
+
+ const SvXMLAttrContainerItem* mpCurrentAttr;
+ sal_uInt16 mnCurrentAttr;
+
+ public:
+
+ NamespaceIteratorImpl( sal_uInt16* pWhichIds, SfxItemPool* pPool );
+
+ bool next( OUString& rPrefix, OUString& rURL );
+ };
+
+ }
+}
+
+using namespace ::svx;
+
+
+NamespaceIteratorImpl::NamespaceIteratorImpl( sal_uInt16* pWhichIds, SfxItemPool* pPool )
+{
+ mpPool = pPool;
+ mpCurrentAttr = nullptr;
+ mnCurrentAttr = 0;
+
+ mpWhichId = pWhichIds;
+
+ mnItem = -1;
+ if (mpWhichId && (0 != *mpWhichId) && mpPool)
+ {
+ mvItems.reserve(mpPool->GetItemCount2( *mpWhichId ));
+ for (const SfxPoolItem* pItem : mpPool->GetItemSurrogates( *mpWhichId ))
+ mvItems.push_back(static_cast<const SvXMLAttrContainerItem*>(pItem));
+ }
+}
+
+bool NamespaceIteratorImpl::next( OUString& rPrefix, OUString& rURL )
+{
+ // we still need to process the current attribute
+ if( mpCurrentAttr && (mnCurrentAttr != USHRT_MAX) )
+ {
+ rPrefix = mpCurrentAttr->GetPrefix( mnCurrentAttr );
+ rURL = mpCurrentAttr->GetNamespace( mnCurrentAttr );
+
+ mnCurrentAttr = mpCurrentAttr->GetNextNamespaceIndex( mnCurrentAttr );
+ return true;
+ }
+
+ // we need the next namespace item
+ mpCurrentAttr = nullptr;
+ mnItem++;
+
+ // are we finished with the current whichid?
+ if( mnItem == static_cast<sal_Int32>(mvItems.size()) )
+ {
+ mpWhichId++;
+
+ // are we finished with the current pool?
+ if( 0 == *mpWhichId )
+ return false;
+
+ mnItem = -1;
+ mvItems.clear();
+ if (mpPool)
+ {
+ mvItems.reserve(mpPool->GetItemCount2( *mpWhichId ));
+ for (const SfxPoolItem* pItem2 : mpPool->GetItemSurrogates( *mpWhichId ))
+ mvItems.push_back(static_cast<const SvXMLAttrContainerItem*>(pItem2));
+ }
+ return next( rPrefix, rURL );
+ }
+
+ auto pItem = mvItems[mnItem];
+ // get that item and see if there namespaces inside
+ if( pItem->GetAttrCount() > 0 )
+ {
+ mpCurrentAttr = pItem;
+ mnCurrentAttr = pItem->GetFirstNamespaceIndex();
+ }
+ return next( rPrefix, rURL );
+}
+
+
+NamespaceMap::NamespaceMap( sal_uInt16* pWhichIds, SfxItemPool* pPool )
+: mpWhichIds( pWhichIds ), mpPool( pPool )
+{
+}
+
+// XNameAccess
+Any SAL_CALL NamespaceMap::getByName( const OUString& aName )
+{
+ NamespaceIteratorImpl aIter( mpWhichIds, mpPool );
+
+ OUString aPrefix;
+ OUString aURL;
+
+ bool bFound;
+
+ do
+ {
+ bFound = aIter.next( aPrefix, aURL );
+ }
+ while( bFound && (aPrefix != aName ) );
+
+ if( !bFound )
+ throw NoSuchElementException();
+
+ return Any( aURL );
+}
+
+Sequence< OUString > SAL_CALL NamespaceMap::getElementNames()
+{
+ NamespaceIteratorImpl aIter( mpWhichIds, mpPool );
+
+ OUString aPrefix;
+ OUString aURL;
+
+ std::set< OUString > aPrefixSet;
+
+ while( aIter.next( aPrefix, aURL ) )
+ aPrefixSet.insert( aPrefix );
+
+ return comphelper::containerToSequence(aPrefixSet);
+}
+
+sal_Bool SAL_CALL NamespaceMap::hasByName( const OUString& aName )
+{
+ NamespaceIteratorImpl aIter( mpWhichIds, mpPool );
+
+ OUString aPrefix;
+ OUString aURL;
+
+ bool bFound;
+
+ do
+ {
+ bFound = aIter.next( aPrefix, aURL );
+ }
+ while( bFound && (aPrefix != aName ) );
+
+ return bFound;
+}
+
+// XElementAccess
+Type SAL_CALL NamespaceMap::getElementType()
+{
+ return ::cppu::UnoType<OUString>::get();
+}
+
+sal_Bool SAL_CALL NamespaceMap::hasElements()
+{
+ NamespaceIteratorImpl aIter( mpWhichIds, mpPool );
+
+ OUString aPrefix;
+ OUString aURL;
+
+ return aIter.next( aPrefix, aURL );
+}
+
+// XServiceInfo
+OUString SAL_CALL NamespaceMap::getImplementationName( )
+{
+ return NamespaceMap_getImplementationName();
+}
+
+sal_Bool SAL_CALL NamespaceMap::supportsService( const OUString& serviceName )
+{
+ return cppu::supportsService( this, serviceName );
+}
+
+Sequence< OUString > SAL_CALL NamespaceMap::getSupportedServiceNames( )
+{
+ return NamespaceMap_getSupportedServiceNames();
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/unodraw/XPropertyTable.cxx b/svx/source/unodraw/XPropertyTable.cxx
new file mode 100644
index 000000000..56bb13523
--- /dev/null
+++ b/svx/source/unodraw/XPropertyTable.cxx
@@ -0,0 +1,663 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * 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 <XPropertyTable.hxx>
+#include <com/sun/star/drawing/PolyPolygonBezierCoords.hpp>
+#include <com/sun/star/drawing/LineDash.hpp>
+#include <com/sun/star/awt/Gradient.hpp>
+#include <com/sun/star/awt/XBitmap.hpp>
+#include <com/sun/star/graphic/XGraphic.hpp>
+#include <com/sun/star/drawing/Hatch.hpp>
+#include <com/sun/star/lang/XServiceInfo.hpp>
+#include <com/sun/star/container/XNameContainer.hpp>
+#include <o3tl/any.hxx>
+#include <vcl/svapp.hxx>
+
+#include <cppuhelper/implbase.hxx>
+#include <cppuhelper/supportsservice.hxx>
+#include <svx/xdef.hxx>
+
+#include <svx/unoapi.hxx>
+#include <basegfx/polygon/b2dpolypolygontools.hxx>
+
+using namespace com::sun::star;
+using namespace ::cppu;
+
+namespace {
+
+class SvxUnoXPropertyTable : public WeakImplHelper< container::XNameContainer, lang::XServiceInfo >
+{
+private:
+ XPropertyList& mrList;
+ sal_Int16 mnWhich;
+
+ tools::Long getCount() const { return mrList.Count(); }
+ const XPropertyEntry* get(tools::Long index) const;
+public:
+ SvxUnoXPropertyTable( sal_Int16 nWhich, XPropertyList& rList ) noexcept;
+
+ /// @throws uno::RuntimeException
+ virtual uno::Any getAny( const XPropertyEntry* pEntry ) const = 0;
+ /// @throws uno::RuntimeException
+ /// @throws lang::IllegalArgumentException
+ virtual std::unique_ptr<XPropertyEntry> createEntry(const OUString& rName, const uno::Any& rAny) const = 0;
+
+ // XServiceInfo
+ virtual sal_Bool SAL_CALL supportsService( const OUString& ServiceName ) override;
+
+ // XNameContainer
+ virtual void SAL_CALL insertByName( const OUString& aName, const uno::Any& aElement ) override;
+ virtual void SAL_CALL removeByName( const OUString& Name ) override;
+
+ // XNameReplace
+ virtual void SAL_CALL replaceByName( const OUString& aName, const uno::Any& aElement ) override;
+
+ // XNameAccess
+ virtual uno::Any SAL_CALL getByName( const OUString& aName ) override;
+ virtual uno::Sequence< OUString > SAL_CALL getElementNames( ) override;
+ virtual sal_Bool SAL_CALL hasByName( const OUString& aName ) override;
+
+ // XElementAccess
+ virtual sal_Bool SAL_CALL hasElements( ) override;
+};
+
+}
+
+SvxUnoXPropertyTable::SvxUnoXPropertyTable( sal_Int16 nWhich, XPropertyList& rList ) noexcept
+: mrList( rList ), mnWhich( nWhich )
+{
+}
+
+const XPropertyEntry* SvxUnoXPropertyTable::get(tools::Long index) const
+{
+ return mrList.Get(index);
+}
+
+// XServiceInfo
+sal_Bool SAL_CALL SvxUnoXPropertyTable::supportsService( const OUString& ServiceName )
+{
+ return cppu::supportsService(this, ServiceName);
+}
+
+// XNameContainer
+void SAL_CALL SvxUnoXPropertyTable::insertByName( const OUString& aName, const uno::Any& aElement )
+{
+ SolarMutexGuard aGuard;
+
+ if( hasByName( aName ) )
+ throw container::ElementExistException();
+
+ OUString aInternalName = SvxUnogetInternalNameForItem(mnWhich, aName);
+
+ std::unique_ptr<XPropertyEntry> pNewEntry(createEntry(aInternalName, aElement));
+ if (!pNewEntry)
+ throw lang::IllegalArgumentException();
+
+ mrList.Insert(std::move(pNewEntry));
+}
+
+void SAL_CALL SvxUnoXPropertyTable::removeByName( const OUString& Name )
+{
+ SolarMutexGuard aGuard;
+
+ OUString aInternalName = SvxUnogetInternalNameForItem(mnWhich, Name);
+
+ const tools::Long nCount = getCount();
+ tools::Long i;
+ for( i = 0; i < nCount; i++ )
+ {
+ const XPropertyEntry* pEntry = get(i);
+ if (pEntry && aInternalName == pEntry->GetName())
+ {
+ mrList.Remove(i);
+ return;
+ }
+ }
+
+ throw container::NoSuchElementException();
+}
+
+// XNameReplace
+void SAL_CALL SvxUnoXPropertyTable::replaceByName( const OUString& aName, const uno::Any& aElement )
+{
+ SolarMutexGuard aGuard;
+
+ OUString aInternalName = SvxUnogetInternalNameForItem(mnWhich, aName);
+
+ const tools::Long nCount = getCount();
+ tools::Long i;
+ for( i = 0; i < nCount; i++ )
+ {
+ const XPropertyEntry* pEntry = get(i);
+ if (pEntry && aInternalName == pEntry->GetName())
+ {
+ std::unique_ptr<XPropertyEntry> pNewEntry(createEntry(aInternalName, aElement));
+ if (!pNewEntry)
+ throw lang::IllegalArgumentException();
+
+ mrList.Replace(std::move(pNewEntry), i);
+ return;
+ }
+ }
+
+ throw container::NoSuchElementException();
+}
+
+// XNameAccess
+uno::Any SAL_CALL SvxUnoXPropertyTable::getByName( const OUString& aName )
+{
+ SolarMutexGuard aGuard;
+
+ OUString aInternalName = SvxUnogetInternalNameForItem(mnWhich, aName);
+
+ const tools::Long nCount = getCount();
+ tools::Long i;
+ for( i = 0; i < nCount; i++ )
+ {
+ const XPropertyEntry* pEntry = get(i);
+
+ if (pEntry && aInternalName == pEntry->GetName())
+ return getAny( pEntry );
+ }
+
+ throw container::NoSuchElementException();
+}
+
+uno::Sequence< OUString > SAL_CALL SvxUnoXPropertyTable::getElementNames()
+{
+ SolarMutexGuard aGuard;
+
+ const tools::Long nCount = getCount();
+ uno::Sequence< OUString > aNames( nCount );
+ OUString* pNames = aNames.getArray();
+ tools::Long i;
+ for( i = 0; i < nCount; i++ )
+ {
+ const XPropertyEntry* pEntry = get(i);
+
+ if (pEntry)
+ *pNames++ = SvxUnogetApiNameForItem(mnWhich, pEntry->GetName());
+ }
+
+ return aNames;
+}
+
+sal_Bool SAL_CALL SvxUnoXPropertyTable::hasByName( const OUString& aName )
+{
+ SolarMutexGuard aGuard;
+
+ OUString aInternalName = SvxUnogetInternalNameForItem(mnWhich, aName);
+
+ const tools::Long nCount = mrList.Count();
+ tools::Long i;
+ for( i = 0; i < nCount; i++ )
+ {
+ const XPropertyEntry* pEntry = get(i);
+ if (pEntry && aInternalName == pEntry->GetName())
+ return true;
+ }
+
+ return false;
+}
+
+// XElementAccess
+sal_Bool SAL_CALL SvxUnoXPropertyTable::hasElements( )
+{
+ SolarMutexGuard aGuard;
+
+ return getCount() != 0;
+}
+
+namespace {
+
+class SvxUnoXColorTable : public SvxUnoXPropertyTable
+{
+public:
+ explicit SvxUnoXColorTable( XPropertyList& rList ) noexcept : SvxUnoXPropertyTable( XATTR_LINECOLOR, rList ) {};
+
+ // SvxUnoXPropertyTable
+ virtual uno::Any getAny( const XPropertyEntry* pEntry ) const noexcept override;
+ virtual std::unique_ptr<XPropertyEntry> createEntry(const OUString& rName, const uno::Any& rAny) const override;
+
+ // XElementAccess
+ virtual uno::Type SAL_CALL getElementType() override;
+
+ // XServiceInfo
+ virtual OUString SAL_CALL getImplementationName( ) override;
+ virtual uno::Sequence< OUString > SAL_CALL getSupportedServiceNames( ) override;
+};
+
+}
+
+uno::Reference< container::XNameContainer > SvxUnoXColorTable_createInstance( XPropertyList& rList ) noexcept
+{
+ return new SvxUnoXColorTable( rList );
+}
+
+// SvxUnoXPropertyTable
+uno::Any SvxUnoXColorTable::getAny( const XPropertyEntry* pEntry ) const noexcept
+{
+ return uno::Any( static_cast<sal_Int32>(static_cast<const XColorEntry*>(pEntry)->GetColor()) );
+}
+
+std::unique_ptr<XPropertyEntry> SvxUnoXColorTable::createEntry(const OUString& rName, const uno::Any& rAny) const
+{
+ Color aColor;
+ if( !(rAny >>= aColor) )
+ return std::unique_ptr<XPropertyEntry>();
+
+ return std::make_unique<XColorEntry>(aColor, rName);
+}
+
+// XElementAccess
+uno::Type SAL_CALL SvxUnoXColorTable::getElementType()
+{
+ return ::cppu::UnoType<sal_Int32>::get();
+}
+
+// XServiceInfo
+OUString SAL_CALL SvxUnoXColorTable::getImplementationName( )
+{
+ return "SvxUnoXColorTable";
+}
+
+uno::Sequence< OUString > SAL_CALL SvxUnoXColorTable::getSupportedServiceNames( )
+{
+ return { "com.sun.star.drawing.ColorTable" };
+}
+
+namespace {
+
+class SvxUnoXLineEndTable : public SvxUnoXPropertyTable
+{
+public:
+ explicit SvxUnoXLineEndTable( XPropertyList& rTable ) noexcept : SvxUnoXPropertyTable( XATTR_LINEEND, rTable ) {};
+
+ // SvxUnoXPropertyTable
+ virtual uno::Any getAny( const XPropertyEntry* pEntry ) const noexcept override;
+ virtual std::unique_ptr<XPropertyEntry> createEntry(const OUString& rName, const uno::Any& rAny) const override;
+
+ // XElementAccess
+ virtual uno::Type SAL_CALL getElementType() override;
+
+ // XServiceInfo
+ virtual OUString SAL_CALL getImplementationName( ) override;
+ virtual uno::Sequence< OUString > SAL_CALL getSupportedServiceNames( ) override;
+};
+
+}
+
+uno::Reference< container::XNameContainer > SvxUnoXLineEndTable_createInstance( XPropertyList& rTable ) noexcept
+{
+ return new SvxUnoXLineEndTable( rTable );
+}
+
+// SvxUnoXPropertyTable
+uno::Any SvxUnoXLineEndTable::getAny( const XPropertyEntry* pEntry ) const noexcept
+{
+ drawing::PolyPolygonBezierCoords aBezier;
+ basegfx::utils::B2DPolyPolygonToUnoPolyPolygonBezierCoords( static_cast<const XLineEndEntry*>(pEntry)->GetLineEnd(),
+ aBezier );
+ return uno::Any(aBezier);
+}
+
+std::unique_ptr<XPropertyEntry> SvxUnoXLineEndTable::createEntry(const OUString& rName, const uno::Any& rAny) const
+{
+ auto pCoords = o3tl::tryAccess<drawing::PolyPolygonBezierCoords>(rAny);
+ if( !pCoords )
+ return std::unique_ptr<XLineEndEntry>();
+
+ basegfx::B2DPolyPolygon aPolyPolygon;
+ if( pCoords->Coordinates.getLength() > 0 )
+ aPolyPolygon = basegfx::utils::UnoPolyPolygonBezierCoordsToB2DPolyPolygon( *pCoords );
+
+ // #86265# make sure polygon is closed
+ aPolyPolygon.setClosed(true);
+
+ return std::make_unique<XLineEndEntry>(aPolyPolygon, rName);
+}
+
+// XElementAccess
+uno::Type SAL_CALL SvxUnoXLineEndTable::getElementType()
+{
+ return cppu::UnoType<drawing::PolyPolygonBezierCoords>::get();
+}
+
+// XServiceInfo
+OUString SAL_CALL SvxUnoXLineEndTable::getImplementationName( )
+{
+ return "SvxUnoXLineEndTable";
+}
+
+uno::Sequence< OUString > SAL_CALL SvxUnoXLineEndTable::getSupportedServiceNames( )
+{
+ return { "com.sun.star.drawing.LineEndTable" };
+}
+
+namespace {
+
+class SvxUnoXDashTable : public SvxUnoXPropertyTable
+{
+public:
+ explicit SvxUnoXDashTable( XPropertyList& rTable ) noexcept : SvxUnoXPropertyTable( XATTR_LINEDASH, rTable ) {};
+
+ // SvxUnoXPropertyTable
+ virtual uno::Any getAny( const XPropertyEntry* pEntry ) const noexcept override;
+ virtual std::unique_ptr<XPropertyEntry> createEntry(const OUString& rName, const uno::Any& rAny) const override;
+
+ // XElementAccess
+ virtual uno::Type SAL_CALL getElementType() override;
+
+ // XServiceInfo
+ virtual OUString SAL_CALL getImplementationName( ) override;
+ virtual uno::Sequence< OUString > SAL_CALL getSupportedServiceNames( ) override;
+};
+
+}
+
+uno::Reference< container::XNameContainer > SvxUnoXDashTable_createInstance( XPropertyList& rTable ) noexcept
+{
+ return new SvxUnoXDashTable( rTable );
+}
+
+// SvxUnoXPropertyTable
+uno::Any SvxUnoXDashTable::getAny( const XPropertyEntry* pEntry ) const noexcept
+{
+ const XDash& rXD = static_cast<const XDashEntry*>(pEntry)->GetDash();
+
+ drawing::LineDash aLineDash;
+
+ aLineDash.Style = static_cast<css::drawing::DashStyle>(static_cast<sal_uInt16>(rXD.GetDashStyle()));
+ aLineDash.Dots = rXD.GetDots();
+ aLineDash.DotLen = rXD.GetDotLen();
+ aLineDash.Dashes = rXD.GetDashes();
+ aLineDash.DashLen = rXD.GetDashLen();
+ aLineDash.Distance = rXD.GetDistance();
+
+ return uno::Any(aLineDash);
+}
+
+std::unique_ptr<XPropertyEntry> SvxUnoXDashTable::createEntry(const OUString& rName, const uno::Any& rAny) const
+{
+ drawing::LineDash aLineDash;
+ if(!(rAny >>= aLineDash))
+ return std::unique_ptr<XDashEntry>();
+
+ XDash aXDash;
+
+ aXDash.SetDashStyle(static_cast<css::drawing::DashStyle>(static_cast<sal_uInt16>(aLineDash.Style)));
+ aXDash.SetDots(aLineDash.Dots);
+ aXDash.SetDotLen(aLineDash.DotLen);
+ aXDash.SetDashes(aLineDash.Dashes);
+ aXDash.SetDashLen(aLineDash.DashLen);
+ aXDash.SetDistance(aLineDash.Distance);
+
+ return std::make_unique<XDashEntry>(aXDash, rName);
+}
+
+// XElementAccess
+uno::Type SAL_CALL SvxUnoXDashTable::getElementType()
+{
+ return cppu::UnoType<drawing::LineDash>::get();
+}
+
+// XServiceInfo
+OUString SAL_CALL SvxUnoXDashTable::getImplementationName( )
+{
+ return "SvxUnoXDashTable";
+}
+
+uno::Sequence< OUString > SAL_CALL SvxUnoXDashTable::getSupportedServiceNames( )
+{
+ return { "com.sun.star.drawing.DashTable" };
+}
+
+namespace {
+
+class SvxUnoXHatchTable : public SvxUnoXPropertyTable
+{
+public:
+ explicit SvxUnoXHatchTable( XPropertyList& rTable ) noexcept : SvxUnoXPropertyTable( XATTR_FILLHATCH, rTable ) {};
+
+ // SvxUnoXPropertyTable
+ virtual uno::Any getAny( const XPropertyEntry* pEntry ) const noexcept override;
+ virtual std::unique_ptr<XPropertyEntry> createEntry(const OUString& rName, const uno::Any& rAny) const override;
+
+ // XElementAccess
+ virtual uno::Type SAL_CALL getElementType() override;
+
+ // XServiceInfo
+ virtual OUString SAL_CALL getImplementationName( ) override;
+ virtual uno::Sequence< OUString > SAL_CALL getSupportedServiceNames( ) override;
+};
+
+}
+
+uno::Reference< container::XNameContainer > SvxUnoXHatchTable_createInstance( XPropertyList& rTable ) noexcept
+{
+ return new SvxUnoXHatchTable( rTable );
+}
+
+// SvxUnoXPropertyTable
+uno::Any SvxUnoXHatchTable::getAny( const XPropertyEntry* pEntry ) const noexcept
+{
+ const XHatch& aHatch = static_cast<const XHatchEntry*>(pEntry)->GetHatch();
+
+ drawing::Hatch aUnoHatch;
+
+ aUnoHatch.Style = aHatch.GetHatchStyle();
+ aUnoHatch.Color = sal_Int32(aHatch.GetColor());
+ aUnoHatch.Distance = aHatch.GetDistance();
+ aUnoHatch.Angle = aHatch.GetAngle().get();
+
+ return uno::Any(aUnoHatch);
+}
+
+std::unique_ptr<XPropertyEntry> SvxUnoXHatchTable::createEntry(const OUString& rName, const uno::Any& rAny) const
+{
+ drawing::Hatch aUnoHatch;
+ if(!(rAny >>= aUnoHatch))
+ return std::unique_ptr<XHatchEntry>();
+
+ XHatch aXHatch;
+ aXHatch.SetHatchStyle( aUnoHatch.Style );
+ aXHatch.SetColor( Color(ColorTransparency, aUnoHatch.Color) );
+ aXHatch.SetDistance( aUnoHatch.Distance );
+ aXHatch.SetAngle( Degree10(aUnoHatch.Angle) );
+
+ return std::make_unique<XHatchEntry>(aXHatch, rName);
+}
+
+// XElementAccess
+uno::Type SAL_CALL SvxUnoXHatchTable::getElementType()
+{
+ return cppu::UnoType<drawing::Hatch>::get();
+}
+
+// XServiceInfo
+OUString SAL_CALL SvxUnoXHatchTable::getImplementationName( )
+{
+ return "SvxUnoXHatchTable";
+}
+
+uno::Sequence< OUString > SAL_CALL SvxUnoXHatchTable::getSupportedServiceNames( )
+{
+ return { "com.sun.star.drawing.HatchTable" };
+}
+
+namespace {
+
+class SvxUnoXGradientTable : public SvxUnoXPropertyTable
+{
+public:
+ explicit SvxUnoXGradientTable( XPropertyList& rTable ) noexcept : SvxUnoXPropertyTable( XATTR_FILLGRADIENT, rTable ) {};
+
+ // SvxUnoXPropertyTable
+ virtual uno::Any getAny( const XPropertyEntry* pEntry ) const noexcept override;
+ virtual std::unique_ptr<XPropertyEntry> createEntry(const OUString& rName, const uno::Any& rAny) const override;
+
+ // XElementAccess
+ virtual uno::Type SAL_CALL getElementType() override;
+
+ // XServiceInfo
+ virtual OUString SAL_CALL getImplementationName( ) override;
+ virtual uno::Sequence< OUString > SAL_CALL getSupportedServiceNames( ) override;
+};
+
+}
+
+uno::Reference< container::XNameContainer > SvxUnoXGradientTable_createInstance( XPropertyList& rTable ) noexcept
+{
+ return new SvxUnoXGradientTable( rTable );
+}
+
+// SvxUnoXPropertyTable
+uno::Any SvxUnoXGradientTable::getAny( const XPropertyEntry* pEntry ) const noexcept
+{
+ const XGradient& aXGradient = static_cast<const XGradientEntry*>(pEntry)->GetGradient();
+ awt::Gradient aGradient;
+
+ aGradient.Style = aXGradient.GetGradientStyle();
+ aGradient.StartColor = static_cast<sal_Int32>(aXGradient.GetStartColor());
+ aGradient.EndColor = static_cast<sal_Int32>(aXGradient.GetEndColor());
+ aGradient.Angle = static_cast<short>(aXGradient.GetAngle());
+ aGradient.Border = aXGradient.GetBorder();
+ aGradient.XOffset = aXGradient.GetXOffset();
+ aGradient.YOffset = aXGradient.GetYOffset();
+ aGradient.StartIntensity = aXGradient.GetStartIntens();
+ aGradient.EndIntensity = aXGradient.GetEndIntens();
+ aGradient.StepCount = aXGradient.GetSteps();
+
+ return uno::Any(aGradient);
+}
+
+std::unique_ptr<XPropertyEntry> SvxUnoXGradientTable::createEntry(const OUString& rName, const uno::Any& rAny) const
+{
+ awt::Gradient aGradient;
+ if(!(rAny >>= aGradient))
+ return std::unique_ptr<XPropertyEntry>();
+
+ XGradient aXGradient;
+
+ aXGradient.SetGradientStyle( aGradient.Style );
+ aXGradient.SetStartColor( Color(ColorTransparency, aGradient.StartColor) );
+ aXGradient.SetEndColor( Color(ColorTransparency, aGradient.EndColor) );
+ aXGradient.SetAngle( Degree10(aGradient.Angle) );
+ aXGradient.SetBorder( aGradient.Border );
+ aXGradient.SetXOffset( aGradient.XOffset );
+ aXGradient.SetYOffset( aGradient.YOffset );
+ aXGradient.SetStartIntens( aGradient.StartIntensity );
+ aXGradient.SetEndIntens( aGradient.EndIntensity );
+ aXGradient.SetSteps( aGradient.StepCount );
+
+ return std::make_unique<XGradientEntry>(aXGradient, rName);
+}
+
+// XElementAccess
+uno::Type SAL_CALL SvxUnoXGradientTable::getElementType()
+{
+ return cppu::UnoType<awt::Gradient>::get();
+}
+
+// XServiceInfo
+OUString SAL_CALL SvxUnoXGradientTable::getImplementationName( )
+{
+ return "SvxUnoXGradientTable";
+}
+
+uno::Sequence< OUString > SAL_CALL SvxUnoXGradientTable::getSupportedServiceNames( )
+{
+ return { "com.sun.star.drawing.GradientTable" };
+}
+
+namespace {
+
+class SvxUnoXBitmapTable : public SvxUnoXPropertyTable
+{
+public:
+ explicit SvxUnoXBitmapTable( XPropertyList& rTable ) noexcept : SvxUnoXPropertyTable( XATTR_FILLBITMAP, rTable ) {};
+
+ // SvxUnoXPropertyTable
+ virtual uno::Any getAny( const XPropertyEntry* pEntry ) const override;
+ virtual std::unique_ptr<XPropertyEntry> createEntry(const OUString& rName, const uno::Any& rAny) const override;
+
+ // XElementAccess
+ virtual uno::Type SAL_CALL getElementType() override;
+
+ // XServiceInfo
+ virtual OUString SAL_CALL getImplementationName( ) override;
+ virtual uno::Sequence< OUString > SAL_CALL getSupportedServiceNames( ) override;
+};
+
+}
+
+uno::Reference< container::XNameContainer > SvxUnoXBitmapTable_createInstance( XPropertyList& rTable ) noexcept
+{
+ return new SvxUnoXBitmapTable( rTable );
+}
+
+// SvxUnoXPropertyTable
+uno::Any SvxUnoXBitmapTable::getAny( const XPropertyEntry* pEntry ) const
+{
+ auto xBitmapEntry = static_cast<const XBitmapEntry*>(pEntry);
+ css::uno::Reference<css::awt::XBitmap> xBitmap(xBitmapEntry->GetGraphicObject().GetGraphic().GetXGraphic(), uno::UNO_QUERY);
+ return uno::Any(xBitmap);
+}
+
+std::unique_ptr<XPropertyEntry> SvxUnoXBitmapTable::createEntry(const OUString& rName, const uno::Any& rAny) const
+{
+ if (!rAny.has<uno::Reference<awt::XBitmap>>())
+ return std::unique_ptr<XPropertyEntry>();
+
+ auto xBitmap = rAny.get<uno::Reference<awt::XBitmap>>();
+ if (!xBitmap.is())
+ return nullptr;
+
+ uno::Reference<graphic::XGraphic> xGraphic(xBitmap, uno::UNO_QUERY);
+ if (!xGraphic.is())
+ return nullptr;
+
+ Graphic aGraphic(xGraphic);
+ if (aGraphic.IsNone())
+ return nullptr;
+
+ GraphicObject aGraphicObject(aGraphic);
+ return std::make_unique<XBitmapEntry>(aGraphicObject, rName);
+}
+
+// XElementAccess
+uno::Type SAL_CALL SvxUnoXBitmapTable::getElementType()
+{
+ return ::cppu::UnoType<awt::XBitmap>::get();
+}
+
+// XServiceInfo
+OUString SAL_CALL SvxUnoXBitmapTable::getImplementationName( )
+{
+ return "SvxUnoXBitmapTable";
+}
+
+uno::Sequence< OUString > SAL_CALL SvxUnoXBitmapTable::getSupportedServiceNames( )
+{
+ return { "com.sun.star.drawing.BitmapTable" };
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/unodraw/gluepts.cxx b/svx/source/unodraw/gluepts.cxx
new file mode 100644
index 000000000..584effb73
--- /dev/null
+++ b/svx/source/unodraw/gluepts.cxx
@@ -0,0 +1,513 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * 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/NoSuchElementException.hpp>
+#include <com/sun/star/container/XIdentifierContainer.hpp>
+#include <com/sun/star/container/XIndexContainer.hpp>
+#include <com/sun/star/drawing/GluePoint2.hpp>
+#include <com/sun/star/lang/IllegalArgumentException.hpp>
+#include <com/sun/star/lang/IndexOutOfBoundsException.hpp>
+
+#include <cppuhelper/implbase.hxx>
+#include <tools/weakbase.hxx>
+
+#include <svx/svdobj.hxx>
+#include <svx/svdglue.hxx>
+
+#include "gluepts.hxx"
+
+using namespace ::com::sun::star;
+using namespace ::cppu;
+
+const sal_uInt16 NON_USER_DEFINED_GLUE_POINTS = 4;
+
+namespace {
+
+class SvxUnoGluePointAccess : public WeakImplHelper< container::XIndexContainer, container::XIdentifierContainer >
+{
+private:
+ tools::WeakReference<SdrObject> mpObject;
+
+public:
+ explicit SvxUnoGluePointAccess( SdrObject* pObject ) noexcept;
+
+ // XIdentifierContainer
+ virtual sal_Int32 SAL_CALL insert( const uno::Any& aElement ) override;
+ virtual void SAL_CALL removeByIdentifier( sal_Int32 Identifier ) override;
+
+ // XIdentifierReplace
+ virtual void SAL_CALL replaceByIdentifer( sal_Int32 Identifier, const uno::Any& aElement ) override;
+
+ // XIdentifierReplace
+ virtual uno::Any SAL_CALL getByIdentifier( sal_Int32 Identifier ) override;
+ virtual uno::Sequence< sal_Int32 > SAL_CALL getIdentifiers( ) override;
+
+ /* deprecated */
+ // XIndexContainer
+ virtual void SAL_CALL insertByIndex( sal_Int32 Index, const uno::Any& Element ) override;
+ virtual void SAL_CALL removeByIndex( sal_Int32 Index ) override;
+
+ /* deprecated */
+ // XIndexReplace
+ virtual void SAL_CALL replaceByIndex( sal_Int32 Index, const uno::Any& Element ) override;
+
+ /* deprecated */
+ // XIndexAccess
+ virtual sal_Int32 SAL_CALL getCount( ) override;
+ virtual uno::Any SAL_CALL getByIndex( sal_Int32 Index ) override;
+
+ // XElementAccess
+ virtual uno::Type SAL_CALL getElementType( ) override;
+ virtual sal_Bool SAL_CALL hasElements( ) override;
+};
+
+}
+
+static void convert( const SdrGluePoint& rSdrGlue, drawing::GluePoint2& rUnoGlue ) noexcept
+{
+ rUnoGlue.Position.X = rSdrGlue.GetPos().X();
+ rUnoGlue.Position.Y = rSdrGlue.GetPos().Y();
+ rUnoGlue.IsRelative = rSdrGlue.IsPercent();
+
+ SdrAlign eAlign = rSdrGlue.GetAlign();
+ if (eAlign == (SdrAlign::VERT_TOP|SdrAlign::HORZ_LEFT))
+ rUnoGlue.PositionAlignment = drawing::Alignment_TOP_LEFT;
+ else if (eAlign == (SdrAlign::HORZ_CENTER|SdrAlign::VERT_TOP))
+ rUnoGlue.PositionAlignment = drawing::Alignment_TOP;
+ else if (eAlign == (SdrAlign::VERT_TOP|SdrAlign::HORZ_RIGHT))
+ rUnoGlue.PositionAlignment = drawing::Alignment_TOP_RIGHT;
+ else if (eAlign == (SdrAlign::HORZ_CENTER|SdrAlign::VERT_CENTER))
+ rUnoGlue.PositionAlignment = drawing::Alignment_CENTER;
+ else if (eAlign == (SdrAlign::HORZ_RIGHT|SdrAlign::VERT_CENTER))
+ rUnoGlue.PositionAlignment = drawing::Alignment_RIGHT;
+ else if (eAlign == (SdrAlign::HORZ_LEFT|SdrAlign::VERT_BOTTOM))
+ rUnoGlue.PositionAlignment = drawing::Alignment_BOTTOM_LEFT;
+ else if (eAlign == (SdrAlign::HORZ_CENTER|SdrAlign::VERT_BOTTOM))
+ rUnoGlue.PositionAlignment = drawing::Alignment_BOTTOM;
+ else if (eAlign == (SdrAlign::HORZ_RIGHT|SdrAlign::VERT_BOTTOM))
+ rUnoGlue.PositionAlignment = drawing::Alignment_BOTTOM_RIGHT;
+ else {
+ rUnoGlue.PositionAlignment = drawing::Alignment_LEFT;
+ }
+
+ switch( rSdrGlue.GetEscDir() )
+ {
+ case SdrEscapeDirection::LEFT:
+ rUnoGlue.Escape = drawing::EscapeDirection_LEFT;
+ break;
+ case SdrEscapeDirection::RIGHT:
+ rUnoGlue.Escape = drawing::EscapeDirection_RIGHT;
+ break;
+ case SdrEscapeDirection::TOP:
+ rUnoGlue.Escape = drawing::EscapeDirection_UP;
+ break;
+ case SdrEscapeDirection::BOTTOM:
+ rUnoGlue.Escape = drawing::EscapeDirection_DOWN;
+ break;
+ case SdrEscapeDirection::HORZ:
+ rUnoGlue.Escape = drawing::EscapeDirection_HORIZONTAL;
+ break;
+ case SdrEscapeDirection::VERT:
+ rUnoGlue.Escape = drawing::EscapeDirection_VERTICAL;
+ break;
+// case SdrEscapeDirection::SMART:
+ default:
+ rUnoGlue.Escape = drawing::EscapeDirection_SMART;
+ break;
+ }
+}
+
+static void convert( const drawing::GluePoint2& rUnoGlue, SdrGluePoint& rSdrGlue ) noexcept
+{
+ rSdrGlue.SetPos( Point( rUnoGlue.Position.X, rUnoGlue.Position.Y ) );
+ rSdrGlue.SetPercent( rUnoGlue.IsRelative );
+
+ switch( rUnoGlue.PositionAlignment )
+ {
+ case drawing::Alignment_TOP_LEFT:
+ rSdrGlue.SetAlign( SdrAlign::VERT_TOP|SdrAlign::HORZ_LEFT );
+ break;
+ case drawing::Alignment_TOP:
+ rSdrGlue.SetAlign( SdrAlign::HORZ_CENTER|SdrAlign::VERT_TOP );
+ break;
+ case drawing::Alignment_TOP_RIGHT:
+ rSdrGlue.SetAlign( SdrAlign::VERT_TOP|SdrAlign::HORZ_RIGHT );
+ break;
+ case drawing::Alignment_CENTER:
+ rSdrGlue.SetAlign( SdrAlign::HORZ_CENTER|SdrAlign::VERT_CENTER );
+ break;
+ case drawing::Alignment_RIGHT:
+ rSdrGlue.SetAlign( SdrAlign::HORZ_RIGHT|SdrAlign::VERT_CENTER );
+ break;
+ case drawing::Alignment_BOTTOM_LEFT:
+ rSdrGlue.SetAlign( SdrAlign::HORZ_LEFT|SdrAlign::VERT_BOTTOM );
+ break;
+ case drawing::Alignment_BOTTOM:
+ rSdrGlue.SetAlign( SdrAlign::HORZ_CENTER|SdrAlign::VERT_BOTTOM );
+ break;
+ case drawing::Alignment_BOTTOM_RIGHT:
+ rSdrGlue.SetAlign( SdrAlign::HORZ_RIGHT|SdrAlign::VERT_BOTTOM );
+ break;
+// case SdrAlign::HORZ_LEFT:
+ default:
+ rSdrGlue.SetAlign( SdrAlign::HORZ_LEFT );
+ break;
+ }
+ switch( rUnoGlue.Escape )
+ {
+ case drawing::EscapeDirection_LEFT:
+ rSdrGlue.SetEscDir(SdrEscapeDirection::LEFT);
+ break;
+ case drawing::EscapeDirection_RIGHT:
+ rSdrGlue.SetEscDir(SdrEscapeDirection::RIGHT);
+ break;
+ case drawing::EscapeDirection_UP:
+ rSdrGlue.SetEscDir(SdrEscapeDirection::TOP);
+ break;
+ case drawing::EscapeDirection_DOWN:
+ rSdrGlue.SetEscDir(SdrEscapeDirection::BOTTOM);
+ break;
+ case drawing::EscapeDirection_HORIZONTAL:
+ rSdrGlue.SetEscDir(SdrEscapeDirection::HORZ);
+ break;
+ case drawing::EscapeDirection_VERTICAL:
+ rSdrGlue.SetEscDir(SdrEscapeDirection::VERT);
+ break;
+// case drawing::EscapeDirection_SMART:
+ default:
+ rSdrGlue.SetEscDir(SdrEscapeDirection::SMART);
+ break;
+ }
+}
+
+SvxUnoGluePointAccess::SvxUnoGluePointAccess( SdrObject* pObject ) noexcept
+: mpObject( pObject )
+{
+}
+
+// XIdentifierContainer
+sal_Int32 SAL_CALL SvxUnoGluePointAccess::insert( const uno::Any& aElement )
+{
+ if( mpObject.is() )
+ {
+ SdrGluePointList* pList = mpObject->ForceGluePointList();
+ if( pList )
+ {
+ // second, insert the new gluepoint
+ drawing::GluePoint2 aUnoGlue;
+
+ if( aElement >>= aUnoGlue )
+ {
+ SdrGluePoint aSdrGlue;
+ convert( aUnoGlue, aSdrGlue );
+ sal_uInt16 nId = pList->Insert( aSdrGlue );
+
+ // only repaint, no objectchange
+ mpObject->ActionChanged();
+ // mpObject->BroadcastObjectChange();
+
+ return static_cast<sal_Int32>((*pList)[nId].GetId() + NON_USER_DEFINED_GLUE_POINTS) - 1;
+ }
+
+ throw lang::IllegalArgumentException();
+ }
+ }
+
+ return -1;
+}
+
+void SAL_CALL SvxUnoGluePointAccess::removeByIdentifier( sal_Int32 Identifier )
+{
+ if( mpObject.is() && ( Identifier >= NON_USER_DEFINED_GLUE_POINTS ))
+ {
+ const sal_uInt16 nId = static_cast<sal_uInt16>(Identifier - NON_USER_DEFINED_GLUE_POINTS) + 1;
+
+ SdrGluePointList* pList = const_cast<SdrGluePointList*>(mpObject->GetGluePointList());
+ const sal_uInt16 nCount = pList ? pList->GetCount() : 0;
+ sal_uInt16 i;
+
+ for( i = 0; i < nCount; i++ )
+ {
+ if( (*pList)[i].GetId() == nId )
+ {
+ pList->Delete( i );
+
+ // only repaint, no objectchange
+ mpObject->ActionChanged();
+ // mpObject->BroadcastObjectChange();
+
+ return;
+ }
+ }
+ }
+
+ throw container::NoSuchElementException();
+}
+
+// XIdentifierReplace
+void SAL_CALL SvxUnoGluePointAccess::replaceByIdentifer( sal_Int32 Identifier, const uno::Any& aElement )
+{
+ if( !mpObject.is() )
+ return;
+
+ struct drawing::GluePoint2 aGluePoint;
+ if( (Identifier < NON_USER_DEFINED_GLUE_POINTS) || !(aElement >>= aGluePoint))
+ throw lang::IllegalArgumentException();
+
+ const sal_uInt16 nId = static_cast<sal_uInt16>( Identifier - NON_USER_DEFINED_GLUE_POINTS ) + 1;
+
+ SdrGluePointList* pList = const_cast< SdrGluePointList* >( mpObject->GetGluePointList() );
+ const sal_uInt16 nCount = pList ? pList->GetCount() : 0;
+ sal_uInt16 i;
+ for( i = 0; i < nCount; i++ )
+ {
+ if( (*pList)[i].GetId() == nId )
+ {
+ // change the gluepoint
+ SdrGluePoint& rTempPoint = (*pList)[i];
+ convert( aGluePoint, rTempPoint );
+
+ // only repaint, no objectchange
+ mpObject->ActionChanged();
+ // mpObject->BroadcastObjectChange();
+
+ return;
+ }
+ }
+
+ throw container::NoSuchElementException();
+}
+
+// XIdentifierAccess
+uno::Any SAL_CALL SvxUnoGluePointAccess::getByIdentifier( sal_Int32 Identifier )
+{
+ if( mpObject.is() )
+ {
+ struct drawing::GluePoint2 aGluePoint;
+
+ if( Identifier < NON_USER_DEFINED_GLUE_POINTS ) // default gluepoint?
+ {
+ SdrGluePoint aTempPoint = mpObject->GetVertexGluePoint( static_cast<sal_uInt16>(Identifier) );
+ aGluePoint.IsUserDefined = false;
+ convert( aTempPoint, aGluePoint );
+ return uno::Any( aGluePoint );
+ }
+ else
+ {
+ const sal_uInt16 nId = static_cast<sal_uInt16>( Identifier - NON_USER_DEFINED_GLUE_POINTS ) + 1;
+
+ const SdrGluePointList* pList = mpObject->GetGluePointList();
+ const sal_uInt16 nCount = pList ? pList->GetCount() : 0;
+ for( sal_uInt16 i = 0; i < nCount; i++ )
+ {
+ const SdrGluePoint& rTempPoint = (*pList)[i];
+ if( rTempPoint.GetId() == nId )
+ {
+ // #i38892#
+ if(rTempPoint.IsUserDefined())
+ {
+ aGluePoint.IsUserDefined = true;
+ }
+
+ convert( rTempPoint, aGluePoint );
+ return uno::Any( aGluePoint );
+ }
+ }
+ }
+ }
+
+ throw container::NoSuchElementException();
+}
+
+uno::Sequence< sal_Int32 > SAL_CALL SvxUnoGluePointAccess::getIdentifiers()
+{
+ if( mpObject.is() )
+ {
+ const SdrGluePointList* pList = mpObject->GetGluePointList();
+ const sal_uInt16 nCount = pList ? pList->GetCount() : 0;
+
+ sal_uInt16 i;
+
+ uno::Sequence< sal_Int32 > aIdSequence( nCount + NON_USER_DEFINED_GLUE_POINTS );
+ sal_Int32 *pIdentifier = aIdSequence.getArray();
+
+ for( i = 0; i < NON_USER_DEFINED_GLUE_POINTS; i++ )
+ *pIdentifier++ = static_cast<sal_Int32>(i);
+
+ for( i = 0; i < nCount; i++ )
+ *pIdentifier++ = static_cast<sal_Int32>( (*pList)[i].GetId() + NON_USER_DEFINED_GLUE_POINTS ) - 1;
+
+ return aIdSequence;
+ }
+ else
+ {
+ uno::Sequence< sal_Int32 > aEmpty;
+ return aEmpty;
+ }
+}
+
+/* deprecated */
+
+// XIndexContainer
+void SAL_CALL SvxUnoGluePointAccess::insertByIndex( sal_Int32, const uno::Any& Element )
+{
+ if( mpObject.is() )
+ {
+ SdrGluePointList* pList = mpObject->ForceGluePointList();
+ if( pList )
+ {
+ drawing::GluePoint2 aUnoGlue;
+
+ if( Element >>= aUnoGlue )
+ {
+ SdrGluePoint aSdrGlue;
+ convert( aUnoGlue, aSdrGlue );
+ pList->Insert( aSdrGlue );
+
+ // only repaint, no objectchange
+ mpObject->ActionChanged();
+ // mpObject->BroadcastObjectChange();
+
+ return;
+ }
+
+ throw lang::IllegalArgumentException();
+ }
+ }
+
+ throw lang::IndexOutOfBoundsException();
+}
+
+void SAL_CALL SvxUnoGluePointAccess::removeByIndex( sal_Int32 Index )
+{
+ if( mpObject.is() )
+ {
+ SdrGluePointList* pList = mpObject->ForceGluePointList();
+ if( pList )
+ {
+ Index -= 4;
+ if( Index >= 0 && Index < pList->GetCount() )
+ {
+ pList->Delete( static_cast<sal_uInt16>(Index) );
+
+ // only repaint, no objectchange
+ mpObject->ActionChanged();
+ // mpObject->BroadcastObjectChange();
+
+ return;
+ }
+ }
+ }
+
+ throw lang::IndexOutOfBoundsException();
+}
+
+// XIndexReplace
+void SAL_CALL SvxUnoGluePointAccess::replaceByIndex( sal_Int32 Index, const uno::Any& Element )
+{
+ drawing::GluePoint2 aUnoGlue;
+ if(!(Element >>= aUnoGlue))
+ throw lang::IllegalArgumentException();
+
+ Index -= 4;
+ if( mpObject.is() && Index >= 0 )
+ {
+ SdrGluePointList* pList = const_cast< SdrGluePointList* >( mpObject->GetGluePointList() );
+ if( pList && Index < pList->GetCount() )
+ {
+ SdrGluePoint& rGlue = (*pList)[static_cast<sal_uInt16>(Index)];
+ convert( aUnoGlue, rGlue );
+
+ // only repaint, no objectchange
+ mpObject->ActionChanged();
+ // mpObject->BroadcastObjectChange();
+ }
+ }
+
+ throw lang::IndexOutOfBoundsException();
+}
+
+// XIndexAccess
+sal_Int32 SAL_CALL SvxUnoGluePointAccess::getCount()
+{
+ sal_Int32 nCount = 0;
+ if( mpObject.is() )
+ {
+ // each node has a default of 4 gluepoints
+ // and any number of user defined gluepoints
+ nCount += 4;
+
+ const SdrGluePointList* pList = mpObject->GetGluePointList();
+ if( pList )
+ nCount += pList->GetCount();
+ }
+
+ return nCount;
+}
+
+uno::Any SAL_CALL SvxUnoGluePointAccess::getByIndex( sal_Int32 Index )
+{
+ if( Index >= 0 && mpObject.is() )
+ {
+ struct drawing::GluePoint2 aGluePoint;
+
+ if( Index < 4 ) // default gluepoint?
+ {
+ SdrGluePoint aTempPoint = mpObject->GetVertexGluePoint( static_cast<sal_uInt16>(Index) );
+ aGluePoint.IsUserDefined = false;
+ convert( aTempPoint, aGluePoint );
+ return uno::Any(aGluePoint);
+ }
+ else
+ {
+ Index -= 4;
+ const SdrGluePointList* pList = mpObject->GetGluePointList();
+ if( pList && Index < pList->GetCount() )
+ {
+ const SdrGluePoint& rTempPoint = (*pList)[static_cast<sal_uInt16>(Index)];
+ aGluePoint.IsUserDefined = true;
+ convert( rTempPoint, aGluePoint );
+ return uno::Any(aGluePoint);
+ }
+ }
+ }
+
+ throw lang::IndexOutOfBoundsException();
+}
+
+// XElementAccess
+uno::Type SAL_CALL SvxUnoGluePointAccess::getElementType()
+{
+ return cppu::UnoType<drawing::GluePoint2>::get();
+}
+
+sal_Bool SAL_CALL SvxUnoGluePointAccess::hasElements()
+{
+ return mpObject.is();
+}
+
+/**
+ * Create a SvxUnoGluePointAccess
+ */
+uno::Reference< uno::XInterface > SvxUnoGluePointAccess_createInstance( SdrObject* pObject )
+{
+ return *new SvxUnoGluePointAccess(pObject);
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/unodraw/gluepts.hxx b/svx/source/unodraw/gluepts.hxx
new file mode 100644
index 000000000..303f7213e
--- /dev/null
+++ b/svx/source/unodraw/gluepts.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_SVX_SOURCE_UNODRAW_GLUEPTS_HXX
+#define INCLUDED_SVX_SOURCE_UNODRAW_GLUEPTS_HXX
+
+#include <sal/config.h>
+
+#include <com/sun/star/uno/Reference.hxx>
+
+namespace com::sun::star::uno
+{
+class XInterface;
+}
+class SdrObject;
+
+css::uno::Reference<css::uno::XInterface> SvxUnoGluePointAccess_createInstance(SdrObject* pObject);
+
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/unodraw/recoveryui.cxx b/svx/source/unodraw/recoveryui.cxx
new file mode 100644
index 000000000..56550fd7f
--- /dev/null
+++ b/svx/source/unodraw/recoveryui.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_folders.h>
+
+#include <docrecovery.hxx>
+#include <com/sun/star/frame/Desktop.hpp>
+#include <com/sun/star/frame/XSynchronousDispatch.hpp>
+#include <com/sun/star/lang/XServiceInfo.hpp>
+#include <cppuhelper/implbase.hxx>
+#include <osl/file.hxx>
+#include <rtl/bootstrap.hxx>
+#include <rtl/ref.hxx>
+#include <cppuhelper/supportsservice.hxx>
+
+#include <vcl/svapp.hxx>
+
+namespace svxdr = svx::DocRecovery;
+using namespace ::osl;
+
+namespace {
+
+class RecoveryUI : public ::cppu::WeakImplHelper< css::lang::XServiceInfo ,
+ css::frame::XSynchronousDispatch > // => XDispatch!
+{
+
+ // const, types, etcpp.
+ private:
+
+ /** @short TODO */
+ enum EJob
+ {
+ E_JOB_UNKNOWN,
+ E_DO_EMERGENCY_SAVE,
+ E_DO_RECOVERY,
+ E_DO_BRINGTOFRONT,
+ };
+
+
+ // member
+ private:
+
+ /** @short TODO */
+ css::uno::Reference< css::uno::XComponentContext > m_xContext;
+
+ /** @short TODO */
+ weld::Window* m_pParentWindow;
+
+ /** @short TODO */
+ RecoveryUI::EJob m_eJob;
+
+ // Active dialog
+ weld::Dialog* m_pDialog;
+
+ // interface
+ public:
+
+
+ /** @short TODO */
+ explicit RecoveryUI(const css::uno::Reference< css::uno::XComponentContext >& xContext);
+
+ // css.lang.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;
+
+
+ virtual css::uno::Any SAL_CALL dispatchWithReturnValue(const css::util::URL& aURL,
+ const css::uno::Sequence< css::beans::PropertyValue >& lArguments ) override;
+
+ void SetActiveDialog(weld::Dialog* pDialog)
+ {
+ m_pDialog = pDialog;
+ }
+
+ // helper
+ private:
+
+ EJob impl_classifyJob(const css::util::URL& aURL);
+
+ bool impl_doEmergencySave();
+
+ bool impl_doRecovery();
+
+ void impl_showAllRecoveredDocs();
+
+ bool impl_doBringToFront();
+};
+
+RecoveryUI::RecoveryUI(const css::uno::Reference< css::uno::XComponentContext >& xContext)
+ : m_xContext(xContext)
+ , m_pParentWindow(nullptr)
+ , m_eJob(RecoveryUI::E_JOB_UNKNOWN)
+ , m_pDialog(nullptr)
+{
+}
+
+OUString SAL_CALL RecoveryUI::getImplementationName()
+{
+ return "com.sun.star.comp.svx.RecoveryUI";
+}
+
+sal_Bool SAL_CALL RecoveryUI::supportsService(const OUString& sServiceName)
+{
+ return cppu::supportsService(this, sServiceName);
+}
+
+css::uno::Sequence< OUString > SAL_CALL RecoveryUI::getSupportedServiceNames()
+{
+ return { "com.sun.star.dialog.RecoveryUI" };
+}
+
+css::uno::Any SAL_CALL RecoveryUI::dispatchWithReturnValue(const css::util::URL& aURL,
+ const css::uno::Sequence< css::beans::PropertyValue >& )
+{
+ // Internally we use VCL ... every call into vcl based code must
+ // be guarded by locking the global solar mutex.
+ ::SolarMutexGuard aSolarLock;
+
+ css::uno::Any aRet;
+ RecoveryUI::EJob eJob = impl_classifyJob(aURL);
+ // TODO think about outside arguments
+
+ switch(eJob)
+ {
+ case RecoveryUI::E_DO_EMERGENCY_SAVE:
+ {
+ bool bRet = impl_doEmergencySave();
+ aRet <<= bRet;
+ break;
+ }
+
+ case RecoveryUI::E_DO_RECOVERY:
+ {
+ bool bRet = impl_doRecovery();
+ aRet <<= bRet;
+ break;
+ }
+
+ case RecoveryUI::E_DO_BRINGTOFRONT:
+ {
+ bool bRet = impl_doBringToFront();
+ aRet <<= bRet;
+ break;
+ }
+
+ default:
+ {
+ aRet <<= false;
+ break;
+ }
+ }
+
+ return aRet;
+}
+
+
+OUString GetCrashConfigDir()
+{
+
+#if defined(_WIN32)
+ OUString ustrValue = "${$BRAND_BASE_DIR/" LIBO_ETC_FOLDER "/bootstrap.ini:UserInstallation}";
+#elif defined(MACOSX)
+ OUString ustrValue = "~";
+#else
+ OUString ustrValue = "$SYSUSERCONFIG";
+#endif
+ rtl::Bootstrap::expandMacros( ustrValue );
+
+#if defined(_WIN32)
+ ustrValue += "/user/crashdata";
+#endif
+ return ustrValue;
+}
+
+
+#if defined(_WIN32)
+#define LCKFILE "crashdat.lck"
+#else
+#define LCKFILE ".crash_report_unsent"
+#endif
+
+
+OUString GetUnsentURL()
+{
+ OUString aURL = GetCrashConfigDir() + "/" LCKFILE;
+ return aURL;
+}
+
+
+bool delete_pending_crash()
+{
+ OUString aUnsentURL = GetUnsentURL();
+ return ( FileBase::E_None == File::remove( aUnsentURL ) );
+}
+
+RecoveryUI::EJob RecoveryUI::impl_classifyJob(const css::util::URL& aURL)
+{
+ m_eJob = RecoveryUI::E_JOB_UNKNOWN;
+ if (aURL.Protocol == RECOVERY_CMDPART_PROTOCOL)
+ {
+ if (aURL.Path == RECOVERY_CMDPART_DO_EMERGENCY_SAVE)
+ m_eJob = RecoveryUI::E_DO_EMERGENCY_SAVE;
+ else if (aURL.Path == RECOVERY_CMDPART_DO_RECOVERY)
+ m_eJob = RecoveryUI::E_DO_RECOVERY;
+ else if (aURL.Path == RECOVERY_CMDPART_DO_BRINGTOFRONT)
+ m_eJob = RecoveryUI::E_DO_BRINGTOFRONT;
+ }
+
+ return m_eJob;
+}
+
+struct DialogReleaseGuard
+{
+ RecoveryUI& m_rRecoveryUI;
+
+ DialogReleaseGuard(RecoveryUI& rRecoveryUI, weld::Dialog* p)
+ : m_rRecoveryUI(rRecoveryUI)
+ {
+ m_rRecoveryUI.SetActiveDialog(p);
+ }
+ ~DialogReleaseGuard()
+ {
+ m_rRecoveryUI.SetActiveDialog(nullptr);
+ }
+};
+
+bool RecoveryUI::impl_doEmergencySave()
+{
+ // create core service, which implements the real "emergency save" algorithm.
+ rtl::Reference<svxdr::RecoveryCore> pCore = new svxdr::RecoveryCore(m_xContext, true);
+
+ // create dialog for this operation and bind it to the used core service
+ std::unique_ptr<svxdr::SaveDialog> xDialog(new svxdr::SaveDialog(m_pParentWindow, pCore.get()));
+ DialogReleaseGuard dialogReleaseGuard(*this, xDialog->getDialog());
+
+ // start the dialog
+ short nRet = xDialog->run();
+ return (nRet==DLG_RET_OK_AUTOLUNCH);
+}
+
+bool RecoveryUI::impl_doRecovery()
+{
+ // create core service, which implements the real "emergency save" algorithm.
+ rtl::Reference<svxdr::RecoveryCore> pCore = new svxdr::RecoveryCore(m_xContext, false);
+
+ // create all needed dialogs for this operation
+ // and bind it to the used core service
+ std::unique_ptr<svxdr::RecoveryDialog> xDialog(new svxdr::RecoveryDialog(m_pParentWindow, pCore.get()));
+ DialogReleaseGuard dialogReleaseGuard(*this, xDialog->getDialog());
+
+ // start the dialog
+ short nRet = xDialog->run();
+
+ impl_showAllRecoveredDocs();
+
+ delete_pending_crash();
+
+ return nRet != RET_CANCEL;
+}
+
+void RecoveryUI::impl_showAllRecoveredDocs()
+{
+ css::uno::Reference< css::frame::XDesktop2 > xDesktop = css::frame::Desktop::create( m_xContext );
+
+ css::uno::Reference< css::container::XIndexAccess > xTaskContainer(
+ xDesktop->getFrames(),
+ css::uno::UNO_QUERY_THROW);
+
+ sal_Int32 c = xTaskContainer->getCount();
+ sal_Int32 i = 0;
+ for (i=0; i<c; ++i)
+ {
+ try
+ {
+ css::uno::Reference< css::frame::XFrame > xTask;
+ xTaskContainer->getByIndex(i) >>= xTask;
+ if (!xTask.is())
+ continue;
+
+ css::uno::Reference< css::awt::XWindow > xWindow = xTask->getContainerWindow();
+ if (!xWindow.is())
+ continue;
+
+ xWindow->setVisible(true);
+ }
+ catch(const css::uno::RuntimeException&)
+ { throw; }
+ catch(const css::uno::Exception&)
+ { continue; }
+ }
+}
+
+bool RecoveryUI::impl_doBringToFront()
+{
+ if (!m_pDialog || !m_pDialog->get_visible())
+ return false;
+ m_pDialog->present();
+ return true;
+}
+
+}
+
+extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface *
+com_sun_star_comp_svx_RecoveryUI_get_implementation(
+ css::uno::XComponentContext *context,
+ css::uno::Sequence<css::uno::Any> const &)
+{
+ return cppu::acquire(new RecoveryUI(context));
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/unodraw/shapeimpl.hxx b/svx/source/unodraw/shapeimpl.hxx
new file mode 100644
index 000000000..68189814f
--- /dev/null
+++ b/svx/source/unodraw/shapeimpl.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_SVX_SOURCE_UNODRAW_SHAPEIMPL_HXX
+#define INCLUDED_SVX_SOURCE_UNODRAW_SHAPEIMPL_HXX
+
+#include <svx/unoprov.hxx>
+#include <svx/unoshape.hxx>
+
+class SvxShapeCaption : public SvxShapeText
+{
+public:
+ explicit SvxShapeCaption(SdrObject* pObj);
+ virtual ~SvxShapeCaption() noexcept override;
+};
+class SvxPluginShape : public SvxOle2Shape
+{
+protected:
+ // override these for special property handling in subcasses. Return true if property is handled
+ virtual bool setPropertyValueImpl( const OUString& rName, const SfxItemPropertyMapEntry* pProperty, const css::uno::Any& rValue ) override;
+ virtual bool getPropertyValueImpl( const OUString& rName, const SfxItemPropertyMapEntry* pProperty, css::uno::Any& rValue ) override;
+
+public:
+ explicit SvxPluginShape(SdrObject* pObj);
+ virtual ~SvxPluginShape() noexcept override;
+
+ virtual void SAL_CALL setPropertyValue( const OUString& aPropertyName, const css::uno::Any& aValue ) override;
+ using SvxUnoTextRangeBase::setPropertyValue;
+
+ virtual void SAL_CALL setPropertyValues( const css::uno::Sequence< OUString >& aPropertyNames, const css::uno::Sequence< css::uno::Any >& aValues ) override;
+
+ virtual void Create( SdrObject* pNewOpj, SvxDrawPage* pNewPage ) override;
+};
+class SvxAppletShape : public SvxOle2Shape
+{
+protected:
+ // override these for special property handling in subcasses. Return true if property is handled
+ virtual bool setPropertyValueImpl( const OUString& rName, const SfxItemPropertyMapEntry* pProperty, const css::uno::Any& rValue ) override;
+ virtual bool getPropertyValueImpl( const OUString& rName, const SfxItemPropertyMapEntry* pProperty, css::uno::Any& rValue ) override;
+
+public:
+ explicit SvxAppletShape(SdrObject* pObj);
+ virtual ~SvxAppletShape() noexcept override;
+
+ virtual void SAL_CALL setPropertyValue( const OUString& aPropertyName, const css::uno::Any& aValue ) override;
+ using SvxUnoTextRangeBase::setPropertyValue;
+
+ virtual void SAL_CALL setPropertyValues( const css::uno::Sequence< OUString >& aPropertyNames, const css::uno::Sequence< css::uno::Any >& aValues ) override;
+
+ virtual void Create( SdrObject* pNewOpj, SvxDrawPage* pNewPage ) override;
+};
+
+class SvxFrameShape : public SvxOle2Shape
+{
+private:
+ OUString m_sInitialFrameURL;
+protected:
+ // override these for special property handling in subcasses. Return true if property is handled
+ virtual bool setPropertyValueImpl( const OUString& rName, const SfxItemPropertyMapEntry* pProperty, const css::uno::Any& rValue ) override;
+ virtual bool getPropertyValueImpl(const OUString& rName, const SfxItemPropertyMapEntry* pProperty,
+ css::uno::Any& rValue) override;
+
+public:
+ explicit SvxFrameShape(SdrObject* pObj);
+ virtual ~SvxFrameShape() noexcept override;
+
+ virtual void SAL_CALL setPropertyValue( const OUString& aPropertyName, const css::uno::Any& aValue ) override;
+ using SvxUnoTextRangeBase::setPropertyValue;
+
+ virtual void SAL_CALL setPropertyValues( const css::uno::Sequence< OUString >& aPropertyNames, const css::uno::Sequence< css::uno::Any >& aValues ) override;
+
+ virtual void Create( SdrObject* pNewOpj, SvxDrawPage* pNewPage ) override;
+
+ virtual OUString GetAndClearInitialFrameURL() override;
+};
+
+SvxUnoPropertyMapProvider& getSvxMapProvider();
+
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/unodraw/shapepropertynotifier.cxx b/svx/source/unodraw/shapepropertynotifier.cxx
new file mode 100644
index 000000000..a19a5fd96
--- /dev/null
+++ b/svx/source/unodraw/shapepropertynotifier.cxx
@@ -0,0 +1,143 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+
+#include <svx/shapepropertynotifier.hxx>
+
+#include <com/sun/star/beans/XPropertySet.hpp>
+
+#include <cppuhelper/weak.hxx>
+#include <tools/diagnose_ex.h>
+
+namespace
+{
+
+}
+
+
+namespace svx
+{
+
+
+ using ::com::sun::star::uno::Reference;
+ using ::com::sun::star::uno::UNO_QUERY_THROW;
+ using ::com::sun::star::uno::Exception;
+ using ::com::sun::star::uno::Any;
+ using ::com::sun::star::beans::PropertyChangeEvent;
+ using ::com::sun::star::beans::XPropertyChangeListener;
+ using ::com::sun::star::lang::EventObject;
+ using ::com::sun::star::beans::XPropertySet;
+
+ PropertyValueProvider::~PropertyValueProvider()
+ {
+ }
+
+ //= PropertyValueProvider
+
+
+ const OUString & PropertyValueProvider::getPropertyName() const
+ {
+ return m_sPropertyName;
+ }
+
+
+ void PropertyValueProvider::getCurrentValue( Any& _out_rValue ) const
+ {
+ Reference< XPropertySet > xContextProps( const_cast< PropertyValueProvider* >( this )->m_rContext, UNO_QUERY_THROW );
+ _out_rValue = xContextProps->getPropertyValue( getPropertyName() );
+ }
+
+ PropertyChangeNotifier::PropertyChangeNotifier( ::cppu::OWeakObject& _rOwner, ::osl::Mutex& _rMutex )
+ :m_rContext( _rOwner )
+ ,m_aPropertyChangeListeners( _rMutex )
+ {
+ }
+
+ PropertyChangeNotifier::~PropertyChangeNotifier()
+ {
+ }
+
+ void PropertyChangeNotifier::registerProvider(const ShapePropertyProviderId _eProperty, std::unique_ptr<PropertyValueProvider> _rProvider)
+ {
+ assert( _rProvider && "NULL factory not allowed." );
+
+ assert( ! m_aProviders[_eProperty] &&
+ "PropertyChangeNotifier::registerProvider: factory for this ID already present!" );
+
+ m_aProviders[ _eProperty ] = std::move(_rProvider);
+ }
+
+ void PropertyChangeNotifier::notifyPropertyChange( const ShapePropertyProviderId _eProperty ) const
+ {
+ auto & provPos = m_aProviders[ _eProperty ];
+ OSL_ENSURE( provPos, "PropertyChangeNotifier::notifyPropertyChange: no factory!" );
+ if (!provPos)
+ return;
+
+ const OUString & sPropertyName( provPos->getPropertyName() );
+
+ ::comphelper::OInterfaceContainerHelper3<XPropertyChangeListener>* pPropListeners = m_aPropertyChangeListeners.getContainer( sPropertyName );
+ ::comphelper::OInterfaceContainerHelper3<XPropertyChangeListener>* pAllListeners = m_aPropertyChangeListeners.getContainer( OUString() );
+ if ( !pPropListeners && !pAllListeners )
+ return;
+
+ try
+ {
+ PropertyChangeEvent aEvent;
+ aEvent.Source = m_rContext;
+ // Handle/OldValue not supported
+ aEvent.PropertyName = provPos->getPropertyName();
+ provPos->getCurrentValue( aEvent.NewValue );
+
+ if ( pPropListeners )
+ pPropListeners->notifyEach( &XPropertyChangeListener::propertyChange, aEvent );
+ if ( pAllListeners )
+ pAllListeners->notifyEach( &XPropertyChangeListener::propertyChange, aEvent );
+ }
+ catch( const Exception& )
+ {
+ DBG_UNHANDLED_EXCEPTION("svx");
+ }
+ }
+
+
+ void PropertyChangeNotifier::addPropertyChangeListener( const OUString& _rPropertyName, const Reference< XPropertyChangeListener >& _rxListener )
+ {
+ m_aPropertyChangeListeners.addInterface( _rPropertyName, _rxListener );
+ }
+
+
+ void PropertyChangeNotifier::removePropertyChangeListener( const OUString& _rPropertyName, const Reference< XPropertyChangeListener >& _rxListener )
+ {
+ m_aPropertyChangeListeners.removeInterface( _rPropertyName, _rxListener );
+ }
+
+
+ void PropertyChangeNotifier::disposing()
+ {
+ EventObject aEvent;
+ aEvent.Source = m_rContext;
+ m_aPropertyChangeListeners.disposeAndClear( aEvent );
+ }
+
+
+}
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/unodraw/tableshape.cxx b/svx/source/unodraw/tableshape.cxx
new file mode 100644
index 000000000..b92205164
--- /dev/null
+++ b/svx/source/unodraw/tableshape.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 <sal/config.h>
+
+#include "UnoGraphicExporter.hxx"
+#include "shapeimpl.hxx"
+#include <svx/unodraw/SvxTableShape.hxx>
+#include <svx/unoshprp.hxx>
+#include <svx/svdotable.hxx>
+#include <svx/svdpool.hxx>
+
+
+using namespace ::osl;
+using namespace ::cppu;
+using namespace sdr::table;
+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::beans;
+
+SvxTableShape::SvxTableShape(SdrObject* pObj)
+: SvxShape( pObj, getSvxMapProvider().GetMap(SVXMAP_TABLE), getSvxMapProvider().GetPropertySet(SVXMAP_TABLE, SdrObject::GetGlobalDrawObjectItemPool()) )
+{
+ SetShapeType( "com.sun.star.drawing.TableShape" );
+}
+
+SvxTableShape::~SvxTableShape() noexcept
+{
+}
+
+bool SvxTableShape::setPropertyValueImpl(
+ const OUString& rName,
+ const SfxItemPropertyMapEntry* pProperty,
+ const css::uno::Any& rValue )
+{
+ switch( pProperty->nWID )
+ {
+ case OWN_ATTR_TABLETEMPLATE:
+ {
+ Reference< XIndexAccess > xTemplate;
+
+ if( !(rValue >>= xTemplate) )
+ throw IllegalArgumentException();
+
+ if( HasSdrObject() )
+ static_cast< sdr::table::SdrTableObj* >( GetSdrObject() )->setTableStyle(xTemplate);
+
+ return true;
+ }
+ case OWN_ATTR_TABLETEMPLATE_FIRSTROW:
+ case OWN_ATTR_TABLETEMPLATE_LASTROW:
+ case OWN_ATTR_TABLETEMPLATE_FIRSTCOLUMN:
+ case OWN_ATTR_TABLETEMPLATE_LASTCOLUMN:
+ case OWN_ATTR_TABLETEMPLATE_BANDINGROWS:
+ case OWN_ATTR_TABLETEMPLATE_BANDINGCOLUMNS:
+ {
+ if( HasSdrObject() )
+ {
+ TableStyleSettings aSettings( static_cast< sdr::table::SdrTableObj* >( GetSdrObject() )->getTableStyleSettings() );
+
+ switch( pProperty->nWID )
+ {
+ case OWN_ATTR_TABLETEMPLATE_FIRSTROW: rValue >>= aSettings.mbUseFirstRow; break;
+ case OWN_ATTR_TABLETEMPLATE_LASTROW: rValue >>= aSettings.mbUseLastRow; break;
+ case OWN_ATTR_TABLETEMPLATE_FIRSTCOLUMN: rValue >>= aSettings.mbUseFirstColumn; break;
+ case OWN_ATTR_TABLETEMPLATE_LASTCOLUMN: rValue >>= aSettings.mbUseLastColumn; break;
+ case OWN_ATTR_TABLETEMPLATE_BANDINGROWS: rValue >>= aSettings.mbUseRowBanding; break;
+ case OWN_ATTR_TABLETEMPLATE_BANDINGCOLUMNS: rValue >>= aSettings.mbUseColumnBanding; break;
+ }
+
+ static_cast< sdr::table::SdrTableObj* >( GetSdrObject() )->setTableStyleSettings(aSettings);
+ }
+
+ return true;
+ }
+ default:
+ {
+ return SvxShape::setPropertyValueImpl( rName, pProperty, rValue );
+ }
+ }
+}
+
+bool SvxTableShape::getPropertyValueImpl(
+ const OUString& rName,
+ const SfxItemPropertyMapEntry* pProperty,
+ css::uno::Any& rValue )
+{
+ switch( pProperty->nWID )
+ {
+ case OWN_ATTR_OLEMODEL:
+ {
+ if( HasSdrObject() )
+ {
+ rValue <<= static_cast< sdr::table::SdrTableObj* >( GetSdrObject() )->getTable();
+ }
+ return true;
+ }
+ case OWN_ATTR_TABLETEMPLATE:
+ {
+ if( HasSdrObject() )
+ {
+ rValue <<= static_cast< sdr::table::SdrTableObj* >( GetSdrObject() )->getTableStyle();
+ }
+ return true;
+ }
+ case OWN_ATTR_REPLACEMENT_GRAPHIC:
+ {
+ if( HasSdrObject() )
+ {
+ Graphic aGraphic( SvxGetGraphicForShape( *GetSdrObject() ) );
+ rValue <<= aGraphic.GetXGraphic();
+ }
+ return true;
+ }
+ case OWN_ATTR_TABLETEMPLATE_FIRSTROW:
+ case OWN_ATTR_TABLETEMPLATE_LASTROW:
+ case OWN_ATTR_TABLETEMPLATE_FIRSTCOLUMN:
+ case OWN_ATTR_TABLETEMPLATE_LASTCOLUMN:
+ case OWN_ATTR_TABLETEMPLATE_BANDINGROWS:
+ case OWN_ATTR_TABLETEMPLATE_BANDINGCOLUMNS:
+ {
+ if( HasSdrObject() )
+ {
+ TableStyleSettings aSettings( static_cast< sdr::table::SdrTableObj* >( GetSdrObject() )->getTableStyleSettings() );
+
+ switch( pProperty->nWID )
+ {
+ case OWN_ATTR_TABLETEMPLATE_FIRSTROW: rValue <<= aSettings.mbUseFirstRow; break;
+ case OWN_ATTR_TABLETEMPLATE_LASTROW: rValue <<= aSettings.mbUseLastRow; break;
+ case OWN_ATTR_TABLETEMPLATE_FIRSTCOLUMN: rValue <<= aSettings.mbUseFirstColumn; break;
+ case OWN_ATTR_TABLETEMPLATE_LASTCOLUMN: rValue <<= aSettings.mbUseLastColumn; break;
+ case OWN_ATTR_TABLETEMPLATE_BANDINGROWS: rValue <<= aSettings.mbUseRowBanding; break;
+ case OWN_ATTR_TABLETEMPLATE_BANDINGCOLUMNS: rValue <<= aSettings.mbUseColumnBanding; break;
+ }
+ }
+
+ return true;
+ }
+ default:
+ {
+ return SvxShape::getPropertyValueImpl( rName, pProperty, rValue );
+ }
+ }
+}
+
+void SvxTableShape::lock()
+{
+ SvxShape::lock();
+ if( HasSdrObject() )
+ static_cast< sdr::table::SdrTableObj* >( GetSdrObject() )->uno_lock();
+}
+
+void SvxTableShape::unlock()
+{
+ if( HasSdrObject() )
+ static_cast< sdr::table::SdrTableObj* >( GetSdrObject() )->uno_unlock();
+ SvxShape::unlock();
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/unodraw/unobrushitemhelper.cxx b/svx/source/unodraw/unobrushitemhelper.cxx
new file mode 100644
index 000000000..bc9d04747
--- /dev/null
+++ b/svx/source/unodraw/unobrushitemhelper.cxx
@@ -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/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * 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 <osl/diagnose.h>
+#include <svl/itemset.hxx>
+#include <svx/unobrushitemhelper.hxx>
+#include <svx/xfillit0.hxx>
+#include <svx/xbtmpit.hxx>
+#include <svx/xflbmtit.hxx>
+#include <svx/xflbmpit.hxx>
+#include <svx/xflftrit.hxx>
+#include <svx/xflbstit.hxx>
+#include <svx/xflbckit.hxx>
+#include <svx/xflhtit.hxx>
+#include <svx/xflclit.hxx>
+#include <svx/xfltrit.hxx>
+
+using namespace com::sun::star;
+
+void setSvxBrushItemAsFillAttributesToTargetSet(const SvxBrushItem& rBrush, SfxItemSet& rToSet)
+{
+ // Clear all items from the DrawingLayer FillStyle range (if we have any). All
+ // items that need to be set will be set as hard attributes
+ for(sal_uInt16 a(XATTR_FILL_FIRST); rToSet.Count() && a < XATTR_FILL_LAST; a++)
+ {
+ rToSet.ClearItem(a);
+ }
+
+ const sal_uInt8 nTransparency(255 - rBrush.GetColor().GetAlpha());
+
+ // tdf#89478 check for image first
+ if (GPOS_NONE != rBrush.GetGraphicPos())
+ {
+ // we have a graphic fill, set fill style
+ rToSet.Put(XFillStyleItem(drawing::FillStyle_BITMAP));
+
+ // set graphic (if available)
+ const Graphic* pGraphic = rBrush.GetGraphic();
+
+ if(pGraphic)
+ {
+ rToSet.Put(XFillBitmapItem(OUString(), *pGraphic));
+ }
+ else
+ {
+ OSL_ENSURE(false, "Could not get Graphic from SvxBrushItem (!)");
+ }
+
+ if(GPOS_AREA == rBrush.GetGraphicPos())
+ {
+ // stretch, also means no tile (both items are defaulted to true)
+ rToSet.Put(XFillBmpStretchItem(true));
+ rToSet.Put(XFillBmpTileItem(false));
+
+ // default for stretch is also top-left, but this will not be visible
+ rToSet.Put(XFillBmpPosItem(RectPoint::LT));
+ }
+ else if(GPOS_TILED == rBrush.GetGraphicPos())
+ {
+ // tiled, also means no stretch (both items are defaulted to true)
+ rToSet.Put(XFillBmpStretchItem(false));
+ rToSet.Put(XFillBmpTileItem(true));
+
+ // default for tiled is top-left
+ rToSet.Put(XFillBmpPosItem(RectPoint::LT));
+ }
+ else
+ {
+ // everything else means no tile and no stretch
+ rToSet.Put(XFillBmpStretchItem(false));
+ rToSet.Put(XFillBmpTileItem(false));
+
+ RectPoint aRectPoint(RectPoint::MM);
+
+ switch(rBrush.GetGraphicPos())
+ {
+ case GPOS_LT: aRectPoint = RectPoint::LT; break;
+ case GPOS_MT: aRectPoint = RectPoint::MT; break;
+ case GPOS_RT: aRectPoint = RectPoint::RT; break;
+ case GPOS_LM: aRectPoint = RectPoint::LM; break;
+ case GPOS_MM: aRectPoint = RectPoint::MM; break;
+ case GPOS_RM: aRectPoint = RectPoint::RM; break;
+ case GPOS_LB: aRectPoint = RectPoint::LB; break;
+ case GPOS_MB: aRectPoint = RectPoint::MB; break;
+ case GPOS_RB: aRectPoint = RectPoint::RB; break;
+ default: break; // GPOS_NONE, GPOS_AREA and GPOS_TILED already handled
+ }
+
+ rToSet.Put(XFillBmpPosItem(aRectPoint));
+ }
+
+ // check for graphic's transparency
+ const sal_Int8 nGraphicTransparency(rBrush.getGraphicTransparency());
+
+ if(0 != nGraphicTransparency)
+ {
+ // nGraphicTransparency is in range [0..100]
+ rToSet.Put(XFillTransparenceItem(nGraphicTransparency));
+ }
+ }
+ else if (0xff != nTransparency)
+ {
+ // we have a color fill
+ const Color aColor(rBrush.GetColor().GetRGBColor());
+
+ rToSet.Put(XFillStyleItem(drawing::FillStyle_SOLID));
+ rToSet.Put(XFillColorItem(OUString(), aColor));
+
+ // #125189# nTransparency is in range [0..254], convert to [0..100] which is used in
+ // XFillTransparenceItem (caution with the range which is in an *item-specific* range)
+ rToSet.Put(XFillTransparenceItem(((static_cast<sal_Int32>(nTransparency) * 100) + 127) / 254));
+ }
+ else
+ {
+ // GPOS_NONE == rBrush.GetGraphicPos() && 0xff == rBrush.GetColor().GetTransparency(),
+ // still need to rescue the color used. There are sequences used on the UNO API at
+ // import time (OLE. e.g. chart) which first set RGB color (MID_BACK_COLOR_R_G_B,
+ // color stays transparent) and then set transparency (MID_BACK_COLOR_TRANSPARENCY)
+ // to zero later. When not saving the color, it will be lost.
+ // Also need to set the FillStyle to NONE to express the 0xff transparency flag; this
+ // is needed when e.g. first transparency is set to 0xff and then a Graphic gets set.
+ // When not changing the FillStyle, the next getSvxBrushItemFromSourceSet *will* return
+ // to drawing::FillStyle_SOLID with the rescued color.
+ const Color aColor(rBrush.GetColor().GetRGBColor());
+
+ rToSet.Put(XFillStyleItem(drawing::FillStyle_NONE));
+ rToSet.Put(XFillColorItem(OUString(), aColor));
+ }
+}
+
+static sal_uInt16 getTransparenceForSvxBrushItem(const SfxItemSet& rSourceSet, bool bSearchInParents)
+{
+ sal_uInt16 nFillTransparence(rSourceSet.Get(XATTR_FILLTRANSPARENCE, bSearchInParents).GetValue());
+ const XFillFloatTransparenceItem* pGradientItem = nullptr;
+
+ if((pGradientItem = rSourceSet.GetItemIfSet(XATTR_FILLFLOATTRANSPARENCE, bSearchInParents))
+ && pGradientItem->IsEnabled())
+ {
+ const XGradient& rGradient = pGradientItem->GetGradientValue();
+ const sal_uInt16 nStartLuminance(rGradient.GetStartColor().GetLuminance());
+ const sal_uInt16 nEndLuminance(rGradient.GetEndColor().GetLuminance());
+
+ // luminance is [0..255], transparence needs to be in [0..100].Maximum is 51200, thus sal_uInt16 is okay to use
+ nFillTransparence = static_cast< sal_uInt16 >(((nStartLuminance + nEndLuminance) * 100) / 512);
+ }
+
+ return nFillTransparence;
+}
+
+static std::unique_ptr<SvxBrushItem> getSvxBrushItemForSolid(const SfxItemSet& rSourceSet, bool bSearchInParents, sal_uInt16 nBackgroundID)
+{
+ Color aFillColor(rSourceSet.Get(XATTR_FILLCOLOR, bSearchInParents).GetColorValue());
+
+ // get evtl. mixed transparence
+ const sal_uInt16 nFillTransparence(getTransparenceForSvxBrushItem(rSourceSet, bSearchInParents));
+
+ if(0 != nFillTransparence)
+ {
+ // #i125189# nFillTransparence is in range [0..100] and needs to be in [0..254] unsigned
+ // It is necessary to use the maximum of 0xfe for transparence for the SvxBrushItem
+ // since the oxff value is used for special purposes (like no fill and derive from parent)
+ const sal_uInt8 aTargetTrans(std::min(sal_uInt8(0xfe), static_cast< sal_uInt8 >((nFillTransparence * 254) / 100)));
+
+ aFillColor.SetAlpha(255 - aTargetTrans);
+ }
+
+ return std::make_unique<SvxBrushItem>(aFillColor, nBackgroundID);
+}
+
+std::unique_ptr<SvxBrushItem> getSvxBrushItemFromSourceSet(const SfxItemSet& rSourceSet, sal_uInt16 nBackgroundID, bool bSearchInParents, bool bXMLImportHack)
+{
+ const XFillStyleItem* pXFillStyleItem(rSourceSet.GetItem<XFillStyleItem>(XATTR_FILLSTYLE, bSearchInParents));
+
+ if(!pXFillStyleItem || drawing::FillStyle_NONE == pXFillStyleItem->GetValue())
+ {
+ // no fill, still need to rescue the evtl. set RGB color, but use as transparent color (we have drawing::FillStyle_NONE)
+ Color aFillColor(rSourceSet.Get(XATTR_FILLCOLOR, bSearchInParents).GetColorValue());
+
+ // for writerfilter: when fill style is none, then don't allow anything other than 0 or auto.
+ if (!bXMLImportHack && aFillColor != Color(0))
+ aFillColor = COL_AUTO;
+
+ aFillColor.SetAlpha(0);
+
+ return std::make_unique<SvxBrushItem>(aFillColor, nBackgroundID);
+ }
+
+ std::unique_ptr<SvxBrushItem> xRetval;
+
+ switch(pXFillStyleItem->GetValue())
+ {
+ case drawing::FillStyle_NONE:
+ {
+ // already handled above, can not happen again
+ break;
+ }
+ case drawing::FillStyle_SOLID:
+ {
+ // create SvxBrushItem with fill color
+ xRetval = getSvxBrushItemForSolid(rSourceSet, bSearchInParents, nBackgroundID);
+ break;
+ }
+ case drawing::FillStyle_GRADIENT:
+ {
+ // cannot be directly supported, but do the best possible
+ const XGradient aXGradient(rSourceSet.Get(XATTR_FILLGRADIENT).GetGradientValue());
+ const basegfx::BColor aStartColor(aXGradient.GetStartColor().getBColor() * (aXGradient.GetStartIntens() * 0.01));
+ const basegfx::BColor aEndColor(aXGradient.GetEndColor().getBColor() * (aXGradient.GetEndIntens() * 0.01));
+
+ // use half/half mixed color from gradient start and end
+ Color aMixedColor((aStartColor + aEndColor) * 0.5);
+
+ // get evtl. mixed transparence
+ const sal_uInt16 nFillTransparence(getTransparenceForSvxBrushItem(rSourceSet, bSearchInParents));
+
+ if(0 != nFillTransparence)
+ {
+ // #i125189# nFillTransparence is in range [0..100] and needs to be in [0..254] unsigned
+ // It is necessary to use the maximum of 0xfe for transparence for the SvxBrushItem
+ // since the oxff value is used for special purposes (like no fill and derive from parent)
+ const sal_uInt8 aTargetTrans(std::min(sal_uInt8(0xfe), static_cast< sal_uInt8 >((nFillTransparence * 254) / 100)));
+
+ aMixedColor.SetAlpha(255 - aTargetTrans);
+ }
+
+ xRetval = std::make_unique<SvxBrushItem>(aMixedColor, nBackgroundID);
+ break;
+ }
+ case drawing::FillStyle_HATCH:
+ {
+ // cannot be directly supported, but do the best possible
+ const XHatch& rHatch(rSourceSet.Get(XATTR_FILLHATCH).GetHatchValue());
+ const bool bFillBackground(rSourceSet.Get(XATTR_FILLBACKGROUND).GetValue());
+
+ if(bFillBackground)
+ {
+ // hatch is background-filled, use FillColor as if drawing::FillStyle_SOLID
+ xRetval = getSvxBrushItemForSolid(rSourceSet, bSearchInParents, nBackgroundID);
+ }
+ else
+ {
+ // hatch is not background-filled and using hatch color would be too dark; compensate
+ // somewhat by making it more transparent
+ Color aHatchColor(rHatch.GetColor());
+
+ // get evtl. mixed transparence
+ sal_uInt16 nFillTransparence(getTransparenceForSvxBrushItem(rSourceSet, bSearchInParents));
+
+ // take half orig transparence, add half transparent, clamp result
+ nFillTransparence = std::clamp(static_cast<sal_uInt16>((nFillTransparence / 2) + 50), sal_uInt16(0), sal_uInt16(255));
+
+ // #i125189# nFillTransparence is in range [0..100] and needs to be in [0..254] unsigned
+ // It is necessary to use the maximum of 0xfe for transparence for the SvxBrushItem
+ // since the oxff value is used for special purposes (like no fill and derive from parent)
+ const sal_uInt8 aTargetTrans(std::min(sal_uInt8(0xfe), static_cast< sal_uInt8 >((nFillTransparence * 254) / 100)));
+
+ aHatchColor.SetAlpha(255 - aTargetTrans);
+ xRetval = std::make_unique<SvxBrushItem>(aHatchColor, nBackgroundID);
+ }
+
+ break;
+ }
+ case drawing::FillStyle_BITMAP:
+ {
+ // create SvxBrushItem with bitmap info and flags
+ const XFillBitmapItem& rBmpItm = rSourceSet.Get(XATTR_FILLBITMAP, bSearchInParents);
+ const Graphic aGraphic(rBmpItm.GetGraphicObject().GetGraphic());
+
+ // continue independent of evtl. GraphicType::NONE as aGraphic.GetType(), we still need to rescue positions
+ SvxGraphicPosition aSvxGraphicPosition(GPOS_NONE);
+ const XFillBmpStretchItem& rStretchItem = rSourceSet.Get(XATTR_FILLBMP_STRETCH, bSearchInParents);
+ const XFillBmpTileItem& rTileItem = rSourceSet.Get(XATTR_FILLBMP_TILE, bSearchInParents);
+
+ if(rTileItem.GetValue())
+ {
+ aSvxGraphicPosition = GPOS_TILED;
+ }
+ else if(rStretchItem.GetValue())
+ {
+ aSvxGraphicPosition = GPOS_AREA;
+ }
+ else
+ {
+ const XFillBmpPosItem& rPosItem = rSourceSet.Get(XATTR_FILLBMP_POS, bSearchInParents);
+
+ switch(rPosItem.GetValue())
+ {
+ case RectPoint::LT: aSvxGraphicPosition = GPOS_LT; break;
+ case RectPoint::MT: aSvxGraphicPosition = GPOS_MT; break;
+ case RectPoint::RT: aSvxGraphicPosition = GPOS_RT; break;
+ case RectPoint::LM: aSvxGraphicPosition = GPOS_LM; break;
+ case RectPoint::MM: aSvxGraphicPosition = GPOS_MM; break;
+ case RectPoint::RM: aSvxGraphicPosition = GPOS_RM; break;
+ case RectPoint::LB: aSvxGraphicPosition = GPOS_LB; break;
+ case RectPoint::MB: aSvxGraphicPosition = GPOS_MB; break;
+ case RectPoint::RB: aSvxGraphicPosition = GPOS_RB; break;
+ }
+ }
+
+ // create with given graphic and position
+ xRetval = std::make_unique<SvxBrushItem>(aGraphic, aSvxGraphicPosition, nBackgroundID);
+
+ // get evtl. mixed transparence
+ const sal_uInt16 nFillTransparence(getTransparenceForSvxBrushItem(rSourceSet, bSearchInParents));
+
+ if(0 != nFillTransparence)
+ {
+ // #i125189# nFillTransparence is in range [0..100] and needs to be in [0..100] signed
+ xRetval->setGraphicTransparency(static_cast< sal_Int8 >(nFillTransparence));
+ }
+
+ break;
+ }
+ default:
+ xRetval = std::make_unique<SvxBrushItem>(nBackgroundID);
+ break;
+ }
+
+ return xRetval;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/unodraw/unobtabl.cxx b/svx/source/unodraw/unobtabl.cxx
new file mode 100644
index 000000000..bba7bdbff
--- /dev/null
+++ b/svx/source/unodraw/unobtabl.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 <svx/xit.hxx>
+#include "UnoNameItemTable.hxx"
+
+#include <svx/xbtmpit.hxx>
+#include <svx/svdmodel.hxx>
+#include <svx/unomid.hxx>
+#include <svx/unofill.hxx>
+#include <com/sun/star/awt/XBitmap.hpp>
+
+using namespace ::com::sun::star;
+using namespace ::cppu;
+
+namespace {
+
+class SvxUnoBitmapTable : public SvxUnoNameItemTable
+{
+public:
+ explicit SvxUnoBitmapTable( SdrModel* pModel ) noexcept;
+
+ virtual NameOrIndex* createItem() const override;
+ virtual bool isValid( const NameOrIndex* pItem ) const override;
+
+ // XServiceInfo
+ virtual OUString SAL_CALL getImplementationName( ) override;
+ virtual uno::Sequence< OUString > SAL_CALL getSupportedServiceNames( ) override;
+
+ // XElementAccess
+ virtual uno::Type SAL_CALL getElementType( ) override;
+};
+
+}
+
+SvxUnoBitmapTable::SvxUnoBitmapTable( SdrModel* pModel ) noexcept
+: SvxUnoNameItemTable( pModel, XATTR_FILLBITMAP, MID_BITMAP )
+{
+}
+
+bool SvxUnoBitmapTable::isValid( const NameOrIndex* pItem ) const
+{
+ if( SvxUnoNameItemTable::isValid( pItem ) )
+ {
+ const XFillBitmapItem* pBitmapItem = dynamic_cast< const XFillBitmapItem* >( pItem );
+ if( pBitmapItem )
+ {
+ const Graphic& rGraphic = pBitmapItem->GetGraphicObject().GetGraphic();
+
+ return rGraphic.GetSizeBytes() > 0;
+ }
+ }
+
+ return false;
+}
+
+OUString SAL_CALL SvxUnoBitmapTable::getImplementationName()
+{
+ return "SvxUnoBitmapTable";
+}
+
+uno::Sequence< OUString > SAL_CALL SvxUnoBitmapTable::getSupportedServiceNames( )
+{
+ return { "com.sun.star.drawing.BitmapTable" };
+}
+
+NameOrIndex* SvxUnoBitmapTable::createItem() const
+{
+ return new XFillBitmapItem();
+}
+
+// XElementAccess
+uno::Type SAL_CALL SvxUnoBitmapTable::getElementType( )
+{
+ return ::cppu::UnoType<awt::XBitmap>::get();
+}
+
+/**
+ * Create a bitmaptable
+ */
+uno::Reference< uno::XInterface > SvxUnoBitmapTable_createInstance( SdrModel* pModel )
+{
+ return *new SvxUnoBitmapTable(pModel);
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/unodraw/unoctabl.cxx b/svx/source/unodraw/unoctabl.cxx
new file mode 100644
index 000000000..1d50a7c32
--- /dev/null
+++ b/svx/source/unodraw/unoctabl.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 <unotools/pathoptions.hxx>
+#include <com/sun/star/lang/XServiceInfo.hpp>
+#include <com/sun/star/container/XNameContainer.hpp>
+#include <com/sun/star/uno/XComponentContext.hpp>
+#include <cppuhelper/implbase.hxx>
+#include <cppuhelper/supportsservice.hxx>
+#include <rtl/ref.hxx>
+#include <svx/xtable.hxx>
+
+using namespace ::com::sun::star;
+
+namespace {
+
+class SvxUnoColorTable : public cppu::WeakImplHelper< container::XNameContainer, lang::XServiceInfo >
+{
+private:
+ XColorListRef pList;
+
+public:
+ SvxUnoColorTable();
+
+ // 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;
+
+ // XNameContainer
+ virtual void SAL_CALL insertByName( const OUString& aName, const uno::Any& aElement ) override;
+ virtual void SAL_CALL removeByName( const OUString& Name ) override;
+
+ // XNameReplace
+ virtual void SAL_CALL replaceByName( const OUString& aName, const uno::Any& aElement ) override;
+
+ // XNameAccess
+ virtual uno::Any SAL_CALL getByName( const OUString& aName ) override;
+
+ virtual uno::Sequence< OUString > SAL_CALL getElementNames() override;
+
+ virtual sal_Bool SAL_CALL hasByName( const OUString& aName ) override;
+
+ // XElementAccess
+ virtual uno::Type SAL_CALL getElementType() override;
+ virtual sal_Bool SAL_CALL hasElements() override;
+};
+
+SvxUnoColorTable::SvxUnoColorTable()
+ : pList(XPropertyList::AsColorList(
+ XPropertyList::CreatePropertyList(
+ XPropertyListType::Color, SvtPathOptions().GetPalettePath(), "")))
+{
+}
+
+sal_Bool SAL_CALL SvxUnoColorTable::supportsService( const OUString& ServiceName )
+{
+ return cppu::supportsService( this, ServiceName );
+}
+
+OUString SAL_CALL SvxUnoColorTable::getImplementationName()
+{
+ return "com.sun.star.drawing.SvxUnoColorTable";
+}
+
+uno::Sequence< OUString > SAL_CALL SvxUnoColorTable::getSupportedServiceNames()
+{
+ uno::Sequence<OUString> aSNS { "com.sun.star.drawing.ColorTable" };
+ return aSNS;
+}
+
+// XNameContainer
+void SAL_CALL SvxUnoColorTable::insertByName( const OUString& aName, const uno::Any& aElement )
+{
+ if( hasByName( aName ) )
+ throw container::ElementExistException();
+
+ Color aColor;
+ if( !(aElement >>= aColor) )
+ throw lang::IllegalArgumentException();
+
+ if( pList.is() )
+ {
+ pList->Insert(std::make_unique<XColorEntry>(aColor, aName));
+ }
+}
+
+void SAL_CALL SvxUnoColorTable::removeByName( const OUString& Name )
+{
+ tools::Long nIndex = pList.is() ? pList->GetIndex( Name ) : -1;
+ if( nIndex == -1 )
+ throw container::NoSuchElementException();
+
+ pList->Remove( nIndex );
+}
+
+// XNameReplace
+void SAL_CALL SvxUnoColorTable::replaceByName( const OUString& aName, const uno::Any& aElement )
+{
+ Color nColor;
+ if( !(aElement >>= nColor) )
+ throw lang::IllegalArgumentException();
+
+ tools::Long nIndex = pList.is() ? pList->GetIndex( aName ) : -1;
+ if( nIndex == -1 )
+ throw container::NoSuchElementException();
+
+ pList->Replace(nIndex, std::make_unique<XColorEntry>(nColor, aName ));
+}
+
+// XNameAccess
+uno::Any SAL_CALL SvxUnoColorTable::getByName( const OUString& aName )
+{
+ tools::Long nIndex = pList.is() ? pList->GetIndex( aName ) : -1;
+ if( nIndex == -1 )
+ throw container::NoSuchElementException();
+
+ const XColorEntry* pEntry = pList->GetColor(nIndex);
+ return uno::Any( static_cast<sal_Int32>(pEntry->GetColor().GetRGBColor()) );
+}
+
+uno::Sequence< OUString > SAL_CALL SvxUnoColorTable::getElementNames()
+{
+ const tools::Long nCount = pList.is() ? pList->Count() : 0;
+
+ uno::Sequence< OUString > aSeq( nCount );
+ OUString* pStrings = aSeq.getArray();
+
+ for( tools::Long nIndex = 0; nIndex < nCount; nIndex++ )
+ {
+ const XColorEntry* pEntry = pList->GetColor(nIndex);
+ pStrings[nIndex] = pEntry->GetName();
+ }
+
+ return aSeq;
+}
+
+sal_Bool SAL_CALL SvxUnoColorTable::hasByName( const OUString& aName )
+{
+ tools::Long nIndex = pList.is() ? pList->GetIndex( aName ) : -1;
+ return nIndex != -1;
+}
+
+// XElementAccess
+uno::Type SAL_CALL SvxUnoColorTable::getElementType()
+{
+ return ::cppu::UnoType<sal_Int32>::get();
+}
+
+sal_Bool SAL_CALL SvxUnoColorTable::hasElements()
+{
+ return pList.is() && pList->Count() != 0;
+}
+
+}
+
+extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface *
+com_sun_star_drawing_SvxUnoColorTable_get_implementation(
+ css::uno::XComponentContext *,
+ css::uno::Sequence<css::uno::Any> const &)
+{
+ return cppu::acquire(new SvxUnoColorTable);
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/unodraw/unodtabl.cxx b/svx/source/unodraw/unodtabl.cxx
new file mode 100644
index 000000000..c460bbd7c
--- /dev/null
+++ b/svx/source/unodraw/unodtabl.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 <com/sun/star/drawing/LineDash.hpp>
+
+#include "UnoNameItemTable.hxx"
+#include <svx/xlndsit.hxx>
+#include <svx/unomid.hxx>
+
+#include <svx/svdmodel.hxx>
+#include <svx/unofill.hxx>
+
+using namespace ::com::sun::star;
+using namespace ::cppu;
+
+namespace
+{
+class SvxUnoDashTable : public SvxUnoNameItemTable
+{
+public:
+ explicit SvxUnoDashTable(SdrModel* pModel) noexcept;
+
+ virtual NameOrIndex* createItem() const override;
+
+ // XServiceInfo
+ virtual OUString SAL_CALL getImplementationName() override;
+ virtual uno::Sequence<OUString> SAL_CALL getSupportedServiceNames() override;
+
+ // XElementAccess
+ virtual uno::Type SAL_CALL getElementType() override;
+};
+}
+
+SvxUnoDashTable::SvxUnoDashTable(SdrModel* pModel) noexcept
+ : SvxUnoNameItemTable(pModel, XATTR_LINEDASH, MID_LINEDASH)
+{
+}
+
+OUString SAL_CALL SvxUnoDashTable::getImplementationName() { return "SvxUnoDashTable"; }
+
+uno::Sequence<OUString> SAL_CALL SvxUnoDashTable::getSupportedServiceNames()
+{
+ return { "com.sun.star.drawing.DashTable" };
+}
+
+NameOrIndex* SvxUnoDashTable::createItem() const
+{
+ XLineDashItem* pNewItem = new XLineDashItem();
+ pNewItem->SetWhich(XATTR_LINEDASH); // set which id for pooling
+ return pNewItem;
+}
+
+// XElementAccess
+uno::Type SAL_CALL SvxUnoDashTable::getElementType()
+{
+ return cppu::UnoType<drawing::LineDash>::get();
+}
+
+/**
+ * Create a gradienttable
+ */
+uno::Reference<uno::XInterface> SvxUnoDashTable_createInstance(SdrModel* pModel)
+{
+ return *new SvxUnoDashTable(pModel);
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/unodraw/unogtabl.cxx b/svx/source/unodraw/unogtabl.cxx
new file mode 100644
index 000000000..7939ccfd0
--- /dev/null
+++ b/svx/source/unodraw/unogtabl.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 <com/sun/star/awt/Gradient.hpp>
+#include "UnoNameItemTable.hxx"
+
+#include <svx/svdmodel.hxx>
+#include <svx/xdef.hxx>
+#include <svx/xflgrit.hxx>
+#include <svx/unofill.hxx>
+#include <svx/unomid.hxx>
+
+using namespace ::com::sun::star;
+using namespace ::cppu;
+
+namespace
+{
+class SvxUnoGradientTable : public SvxUnoNameItemTable
+{
+public:
+ explicit SvxUnoGradientTable(SdrModel* pModel) noexcept;
+
+ virtual NameOrIndex* createItem() const override;
+
+ // XServiceInfo
+ virtual OUString SAL_CALL getImplementationName() override;
+ virtual uno::Sequence<OUString> SAL_CALL getSupportedServiceNames() override;
+
+ // XElementAccess
+ virtual uno::Type SAL_CALL getElementType() override;
+};
+}
+
+SvxUnoGradientTable::SvxUnoGradientTable(SdrModel* pModel) noexcept
+ : SvxUnoNameItemTable(pModel, XATTR_FILLGRADIENT, MID_FILLGRADIENT)
+{
+}
+
+OUString SAL_CALL SvxUnoGradientTable::getImplementationName() { return "SvxUnoGradientTable"; }
+
+uno::Sequence<OUString> SAL_CALL SvxUnoGradientTable::getSupportedServiceNames()
+{
+ return { "com.sun.star.drawing.GradientTable" };
+}
+
+// XNameContainer
+NameOrIndex* SvxUnoGradientTable::createItem() const { return new XFillGradientItem(); }
+
+// XElementAccess
+uno::Type SAL_CALL SvxUnoGradientTable::getElementType()
+{
+ return cppu::UnoType<awt::Gradient>::get();
+}
+
+/**
+ * Create a gradienttable
+ */
+uno::Reference<uno::XInterface> SvxUnoGradientTable_createInstance(SdrModel* pModel)
+{
+ return *new SvxUnoGradientTable(pModel);
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/unodraw/unohtabl.cxx b/svx/source/unodraw/unohtabl.cxx
new file mode 100644
index 000000000..cb36192bd
--- /dev/null
+++ b/svx/source/unodraw/unohtabl.cxx
@@ -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 .
+ */
+
+#include <com/sun/star/drawing/Hatch.hpp>
+#include "UnoNameItemTable.hxx"
+
+#include <svx/svdmodel.hxx>
+#include <svx/xdef.hxx>
+#include <svx/xflhtit.hxx>
+#include <svx/unomid.hxx>
+#include <svx/unofill.hxx>
+
+using namespace ::com::sun::star;
+using namespace ::cppu;
+
+namespace {
+
+class SvxUnoHatchTable : public SvxUnoNameItemTable
+{
+public:
+ explicit SvxUnoHatchTable( SdrModel* pModel ) noexcept;
+
+ virtual NameOrIndex* createItem() const override;
+
+ // XServiceInfo
+ virtual OUString SAL_CALL getImplementationName( ) override;
+ virtual uno::Sequence< OUString > SAL_CALL getSupportedServiceNames( ) override;
+
+ // XElementAccess
+ virtual uno::Type SAL_CALL getElementType( ) override;
+};
+
+}
+
+SvxUnoHatchTable::SvxUnoHatchTable( SdrModel* pModel ) noexcept
+: SvxUnoNameItemTable( pModel, XATTR_FILLHATCH, MID_FILLHATCH )
+{
+}
+
+OUString SAL_CALL SvxUnoHatchTable::getImplementationName()
+{
+ return "SvxUnoHatchTable";
+}
+
+uno::Sequence< OUString > SAL_CALL SvxUnoHatchTable::getSupportedServiceNames( )
+{
+ return { "com.sun.star.drawing.HatchTable" };
+}
+
+NameOrIndex* SvxUnoHatchTable::createItem() const
+{
+ return new XFillHatchItem();
+}
+
+// XElementAccess
+uno::Type SAL_CALL SvxUnoHatchTable::getElementType( )
+{
+ return cppu::UnoType<drawing::Hatch>::get();
+}
+
+/**
+ * Create a hatchtable
+ */
+uno::Reference< uno::XInterface > SvxUnoHatchTable_createInstance( SdrModel* pModel )
+{
+ return *new SvxUnoHatchTable(pModel);
+}
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/unodraw/unomlstr.cxx b/svx/source/unodraw/unomlstr.cxx
new file mode 100644
index 000000000..781b3dc38
--- /dev/null
+++ b/svx/source/unodraw/unomlstr.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 <svx/svdobj.hxx>
+#include <vcl/svapp.hxx>
+
+#include <unomlstr.hxx>
+
+using namespace ::com::sun::star;
+
+SvxUnoShapeModifyListener::SvxUnoShapeModifyListener( SdrObject* pObj ) noexcept
+{
+ mpObj = pObj;
+}
+
+SvxUnoShapeModifyListener::~SvxUnoShapeModifyListener() noexcept
+{
+}
+
+// css::util::XModifyListener
+void SAL_CALL SvxUnoShapeModifyListener::modified(const lang::EventObject& )
+{
+ SolarMutexGuard aGuard;
+ if( mpObj )
+ {
+ mpObj->SetChanged();
+ mpObj->BroadcastObjectChange();
+ }
+}
+
+// css::lang::XEventListener
+void SvxUnoShapeModifyListener::disposing(const lang::EventObject& )
+{
+ invalidate();
+}
+
+// internal
+void SvxUnoShapeModifyListener::invalidate() noexcept
+{
+ mpObj = nullptr;
+}
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/unodraw/unomod.cxx b/svx/source/unodraw/unomod.cxx
new file mode 100644
index 000000000..b36eb09a5
--- /dev/null
+++ b/svx/source/unodraw/unomod.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 <sal/config.h>
+
+#include <com/sun/star/lang/IndexOutOfBoundsException.hpp>
+#include <com/sun/star/lang/ServiceNotRegisteredException.hpp>
+#include <com/sun/star/lang/NoSupportException.hpp>
+#include <com/sun/star/drawing/XShape.hpp>
+#include <vcl/svapp.hxx>
+#include <svl/itempool.hxx>
+#include <svtools/unoevent.hxx>
+#include <comphelper/sequence.hxx>
+#include <o3tl/string_view.hxx>
+#include <cppuhelper/supportsservice.hxx>
+
+#include <cppuhelper/implbase.hxx>
+#include <svx/unofill.hxx>
+#include <editeng/unonrule.hxx>
+#include <svtools/unoimap.hxx>
+#include <sfx2/event.hxx>
+#include <svx/fmdpage.hxx>
+#include <svx/fmmodel.hxx>
+#include <svx/fmpage.hxx>
+#include <svx/unoapi.hxx>
+
+#include <svx/svdmodel.hxx>
+#include <svx/unoprov.hxx>
+#include <svx/unopage.hxx>
+#include <editeng/unofield.hxx>
+#include <svx/unomod.hxx>
+#include <svx/unomodel.hxx>
+#include <svx/svdobj.hxx>
+#include <svx/svdpage.hxx>
+#include <svx/SvxXTextColumns.hxx>
+#include <svx/unoshape.hxx>
+#include <svx/xmlgrhlp.hxx>
+
+#include <com/sun/star/text/textfield/Type.hpp>
+
+//-
+
+using namespace ::com::sun::star;
+
+//-
+
+#define QUERYINT( xint ) \
+ if( rType == cppu::UnoType<xint>::get() ) \
+ aAny <<= uno::Reference< xint >(this)
+
+//-
+
+class SvxUnoDrawPagesAccess : public ::cppu::WeakImplHelper< css::drawing::XDrawPages, css::lang::XServiceInfo >
+{
+private:
+ SvxUnoDrawingModel& mrModel;
+
+public:
+ explicit SvxUnoDrawPagesAccess( SvxUnoDrawingModel& rMyModel ) noexcept;
+
+ // XDrawPages
+ virtual css::uno::Reference< css::drawing::XDrawPage > SAL_CALL insertNewByIndex( sal_Int32 nIndex ) override;
+ virtual void SAL_CALL remove( const css::uno::Reference< css::drawing::XDrawPage >& xPage ) override;
+
+ // XIndexAccess
+ virtual sal_Int32 SAL_CALL getCount() override ;
+ virtual css::uno::Any SAL_CALL getByIndex( sal_Int32 Index ) 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;
+};
+//-
+
+static const SvEventDescription* ImplGetSupportedMacroItems()
+{
+ static const SvEventDescription aMacroDescriptionsImpl[] =
+ {
+ { SvMacroItemId::OnMouseOver, "OnMouseOver" },
+ { SvMacroItemId::OnMouseOut, "OnMouseOut" },
+ { SvMacroItemId::NONE, nullptr }
+ };
+
+ return aMacroDescriptionsImpl;
+}
+
+//-
+
+/** fills the given EventObject from the given SdrHint.
+ @returns
+ true if the SdrHint could be translated to an EventObject<br>
+ false if not
+*/
+bool SvxUnoDrawMSFactory::createEvent( const SdrModel* pDoc, const SdrHint* pSdrHint, css::document::EventObject& aEvent )
+{
+ const SdrObject* pObj = nullptr;
+ const SdrPage* pPage = nullptr;
+
+ switch( pSdrHint->GetKind() )
+ {
+// case SdrHintKind::LayerChange: // layer definition changed
+// case SdrHintKind::LayerOrderChange: // layer order changed (Insert/Remove/ChangePos)
+// case HINT_LAYERSETCHG: // layer set changed
+// case HINT_LAYERSETORDERCHG: // layer set order changed (Insert/Remove/ChangePos)
+
+// case HINT_PAGECHG: // page changed
+// aEvent.EventName = "PageModified";
+// pPage = pSdrHint->GetPage();
+// break;
+ case SdrHintKind::PageOrderChange: // draw or master page order changed (Insert/Remove/ChangePos)
+ aEvent.EventName = "PageOrderModified";
+ pPage = pSdrHint->GetPage();
+ break;
+ case SdrHintKind::ObjectChange: // object changed
+ aEvent.EventName = "ShapeModified";
+ pObj = pSdrHint->GetObject();
+ break;
+ case SdrHintKind::ObjectInserted: // add new draw object
+ aEvent.EventName = "ShapeInserted";
+ pObj = pSdrHint->GetObject();
+ break;
+ case SdrHintKind::ObjectRemoved: // removed draw object from list
+ aEvent.EventName = "ShapeRemoved";
+ pObj = pSdrHint->GetObject();
+ break;
+// SdrHintKind::DefaultTabChange, // default tab width changed
+// SdrHintKind::SwitchToPage, // #94278# UNDO/REDO at an object evtl. on another page
+// HINT_OBJLISTCLEAR // Is called before an SdrObjList will be cleared
+ default:
+ return false;
+ }
+
+ if( pObj )
+ aEvent.Source = const_cast<SdrObject*>(pObj)->getUnoShape();
+ else if( pPage )
+ aEvent.Source = const_cast<SdrPage*>(pPage)->getUnoPage();
+ else
+ aEvent.Source = const_cast<SdrModel*>(pDoc)->getUnoModel();
+
+ return true;
+}
+
+namespace {
+
+css::uno::Reference<css::uno::XInterface> create(
+ OUString const & rServiceSpecifier, OUString const & referer)
+{
+ if( rServiceSpecifier.startsWith("com.sun.star.drawing.") )
+ {
+ std::optional<SdrObjKind> nType = UHashMap::getId( rServiceSpecifier );
+ if( nType )
+ {
+ SdrInventor nI = IsInventorE3D(*nType) ? SdrInventor::E3d : SdrInventor::Default;
+
+ return uno::Reference< uno::XInterface >( static_cast<drawing::XShape*>(SvxDrawPage::CreateShapeByTypeAndInventor( *nType, nI, nullptr, nullptr, referer ).get()) );
+ }
+ }
+ else if (rServiceSpecifier == "com.sun.star.document.ImportGraphicStorageHandler")
+ {
+ rtl::Reference<SvXMLGraphicHelper> pGraphicHelper = SvXMLGraphicHelper::Create( SvXMLGraphicHelperMode::Read );
+ uno::Reference< uno::XInterface> xRet( static_cast< ::cppu::OWeakObject* >( pGraphicHelper.get() ) );
+ return xRet;
+ }
+ else if (rServiceSpecifier == "com.sun.star.text.TextColumns")
+ {
+ return SvxXTextColumns_createInstance();
+ }
+
+ uno::Reference< uno::XInterface > xRet( SvxUnoDrawMSFactory::createTextField( rServiceSpecifier ) );
+ if( !xRet.is() )
+ throw lang::ServiceNotRegisteredException("unknown service: " + rServiceSpecifier);
+
+ return xRet;
+}
+
+}
+
+uno::Reference< uno::XInterface > SAL_CALL SvxUnoDrawMSFactory::createInstance( const OUString& rServiceSpecifier )
+{
+ return create(rServiceSpecifier, "");
+}
+
+uno::Reference< uno::XInterface > SvxUnoDrawMSFactory::createTextField( std::u16string_view ServiceSpecifier )
+{
+ return SvxUnoTextCreateTextField( ServiceSpecifier );
+}
+
+uno::Reference< uno::XInterface > SAL_CALL SvxUnoDrawMSFactory::createInstanceWithArguments( const OUString& ServiceSpecifier, const uno::Sequence< css::uno::Any >& Arguments )
+{
+ OUString arg;
+ if ((ServiceSpecifier == "com.sun.star.drawing.GraphicObjectShape"
+ || ServiceSpecifier == "com.sun.star.drawing.MediaShape")
+ && Arguments.getLength() == 1 && (Arguments[0] >>= arg))
+ {
+ return create(ServiceSpecifier, arg);
+ }
+ throw lang::NoSupportException();
+}
+
+uno::Sequence< OUString > SAL_CALL SvxUnoDrawMSFactory::getAvailableServiceNames()
+{
+ return UHashMap::getServiceNames();
+}
+
+SdrModel& SvxUnoDrawingModel::getSdrModelFromUnoModel() const
+{
+ OSL_ENSURE(mpDoc, "No SdrModel in UnoDrawingModel, should not happen");
+ return *mpDoc;
+}
+
+SvxUnoDrawingModel::SvxUnoDrawingModel(SdrModel* pDoc) noexcept
+: SfxBaseModel(nullptr),
+ mpDoc(pDoc)
+{
+}
+
+SvxUnoDrawingModel::~SvxUnoDrawingModel() noexcept
+{
+}
+
+uno::Any SAL_CALL SvxUnoDrawingModel::queryInterface( const uno::Type & rType )
+{
+ uno::Any aAny;
+
+ QUERYINT(lang::XServiceInfo);
+ else QUERYINT(lang::XMultiServiceFactory);
+ else QUERYINT(drawing::XDrawPagesSupplier);
+ else QUERYINT(css::ucb::XAnyCompareFactory);
+ else
+ return SfxBaseModel::queryInterface( rType );
+
+ return aAny;
+}
+
+// XTypeProvider
+uno::Sequence< uno::Type > SAL_CALL SvxUnoDrawingModel::getTypes( )
+{
+ if( !maTypeSequence.hasElements() )
+ {
+ maTypeSequence = comphelper::concatSequences( SfxBaseModel::getTypes(),
+ uno::Sequence {
+ cppu::UnoType<lang::XServiceInfo>::get(),
+ cppu::UnoType<lang::XMultiServiceFactory>::get(),
+ cppu::UnoType<drawing::XDrawPagesSupplier>::get(),
+ cppu::UnoType<css::ucb::XAnyCompareFactory>::get() });
+ }
+ return maTypeSequence;
+}
+
+uno::Sequence< sal_Int8 > SAL_CALL SvxUnoDrawingModel::getImplementationId( )
+{
+ return css::uno::Sequence<sal_Int8>();
+}
+
+void SAL_CALL SvxUnoDrawingModel::lockControllers( )
+{
+ if( mpDoc )
+ mpDoc->setLock(true);
+}
+
+void SAL_CALL SvxUnoDrawingModel::unlockControllers( )
+{
+ if( mpDoc && mpDoc->isLocked() )
+ {
+ mpDoc->setLock(false);
+ }
+}
+
+sal_Bool SAL_CALL SvxUnoDrawingModel::hasControllersLocked( )
+{
+ return mpDoc && mpDoc->isLocked();
+}
+
+// XDrawPagesSupplier
+uno::Reference< drawing::XDrawPages > SAL_CALL SvxUnoDrawingModel::getDrawPages()
+{
+ ::SolarMutexGuard aGuard;
+
+ uno::Reference< drawing::XDrawPages > xDrawPages( mxDrawPagesAccess );
+
+ if( !xDrawPages.is() )
+ mxDrawPagesAccess = xDrawPages = new SvxUnoDrawPagesAccess(*this);
+
+ return xDrawPages;
+}
+
+// XMultiServiceFactory ( SvxFmMSFactory )
+uno::Reference< uno::XInterface > SAL_CALL SvxUnoDrawingModel::createInstance( const OUString& aServiceSpecifier )
+{
+ ::SolarMutexGuard aGuard;
+
+ if( aServiceSpecifier == "com.sun.star.drawing.DashTable" )
+ {
+ if( !mxDashTable.is() )
+ mxDashTable = SvxUnoDashTable_createInstance( mpDoc );
+ return mxDashTable;
+ }
+ if( aServiceSpecifier == "com.sun.star.drawing.GradientTable" )
+ {
+ if( !mxGradientTable.is() )
+ mxGradientTable = SvxUnoGradientTable_createInstance( mpDoc );
+ return mxGradientTable;
+ }
+ if( aServiceSpecifier == "com.sun.star.drawing.HatchTable" )
+ {
+ if( !mxHatchTable.is() )
+ mxHatchTable = SvxUnoHatchTable_createInstance( mpDoc );
+ return mxHatchTable;
+ }
+ if( aServiceSpecifier == "com.sun.star.drawing.BitmapTable" )
+ {
+ if( !mxBitmapTable.is() )
+ mxBitmapTable = SvxUnoBitmapTable_createInstance( mpDoc );
+ return mxBitmapTable;
+ }
+ if( aServiceSpecifier == "com.sun.star.drawing.TransparencyGradientTable" )
+ {
+ if( !mxTransGradientTable.is() )
+ mxTransGradientTable = SvxUnoTransGradientTable_createInstance( mpDoc );
+ return mxTransGradientTable;
+ }
+ if( aServiceSpecifier == "com.sun.star.drawing.MarkerTable" )
+ {
+ if( !mxMarkerTable.is() )
+ mxMarkerTable = SvxUnoMarkerTable_createInstance( mpDoc );
+ return mxMarkerTable;
+ }
+ if( aServiceSpecifier == "com.sun.star.text.NumberingRules" )
+ {
+ return uno::Reference< uno::XInterface >( SvxCreateNumRule( mpDoc ), uno::UNO_QUERY );
+ }
+
+ if ( aServiceSpecifier == "com.sun.star.image.ImageMapRectangleObject" )
+ {
+ return SvUnoImageMapRectangleObject_createInstance( ImplGetSupportedMacroItems() );
+ }
+
+ if ( aServiceSpecifier == "com.sun.star.image.ImageMapCircleObject" )
+ {
+ return SvUnoImageMapCircleObject_createInstance( ImplGetSupportedMacroItems() );
+ }
+
+ if ( aServiceSpecifier == "com.sun.star.image.ImageMapPolygonObject" )
+ {
+ return SvUnoImageMapPolygonObject_createInstance( ImplGetSupportedMacroItems() );
+ }
+
+ if( aServiceSpecifier == "com.sun.star.text.TextField.DateTime" )
+ {
+ return static_cast<cppu::OWeakObject *>(new SvxUnoTextField(text::textfield::Type::DATE));
+ }
+
+ uno::Reference< uno::XInterface > xRet;
+
+ static const OUStringLiteral aPackagePrefix( u"com.sun.star.presentation." );
+ if( aServiceSpecifier.startsWith( aPackagePrefix ) )
+ {
+ SvxShape* pShape = nullptr;
+
+ SdrObjKind nType = SdrObjKind::Text;
+ std::u16string_view aTypeName = aServiceSpecifier.subView( aPackagePrefix.getLength() );
+ // create a shape wrapper
+ if( o3tl::starts_with(aTypeName, u"TitleTextShape") )
+ {
+ nType = SdrObjKind::Text;
+ }
+ else if( o3tl::starts_with(aTypeName, u"OutlinerShape" ) )
+ {
+ nType = SdrObjKind::Text;
+ }
+ else if( o3tl::starts_with(aTypeName, u"SubtitleShape" ) )
+ {
+ nType = SdrObjKind::Text;
+ }
+ else if( o3tl::starts_with(aTypeName, u"GraphicObjectShape" ) )
+ {
+ nType = SdrObjKind::Graphic;
+ }
+ else if( o3tl::starts_with(aTypeName, u"PageShape" ) )
+ {
+ nType = SdrObjKind::Page;
+ }
+ else if( o3tl::starts_with(aTypeName, u"OLE2Shape" ) )
+ {
+ nType = SdrObjKind::OLE2;
+ }
+ else if( o3tl::starts_with(aTypeName, u"ChartShape" ) )
+ {
+ nType = SdrObjKind::OLE2;
+ }
+ else if( o3tl::starts_with(aTypeName, u"OrgChartShape" ) )
+ {
+ nType = SdrObjKind::OLE2;
+ }
+ else if( o3tl::starts_with(aTypeName, u"NotesShape" ) )
+ {
+ nType = SdrObjKind::Text;
+ }
+ else if( o3tl::starts_with(aTypeName, u"HandoutShape" ) )
+ {
+ nType = SdrObjKind::Page;
+ }
+ else if( o3tl::starts_with(aTypeName, u"FooterShape" ) )
+ {
+ nType = SdrObjKind::Text;
+ }
+ else if( o3tl::starts_with(aTypeName, u"HeaderShape" ) )
+ {
+ nType = SdrObjKind::Text;
+ }
+ else if( o3tl::starts_with(aTypeName, u"SlideNumberShape" ) )
+ {
+ nType = SdrObjKind::Text;
+ }
+ else if( o3tl::starts_with(aTypeName, u"DateTimeShape" ) )
+ {
+ nType = SdrObjKind::Text;
+ }
+ else if( o3tl::starts_with(aTypeName, u"TableShape" ) )
+ {
+ nType = SdrObjKind::Table;
+ }
+ else
+ {
+ throw lang::ServiceNotRegisteredException();
+ }
+
+ // create the API wrapper
+ rtl::Reference<SvxShape> xNewShape = CreateSvxShapeByTypeAndInventor( nType, SdrInventor::Default, "" );
+ pShape = xNewShape.get();
+
+ // set shape type
+ if( pShape )
+ pShape->SetShapeType(aServiceSpecifier);
+
+ xRet = static_cast<uno::XWeak*>(pShape);
+ }
+ else
+ {
+ xRet = SvxFmMSFactory::createInstance( aServiceSpecifier );
+ }
+
+ return xRet;
+}
+
+uno::Sequence< OUString > SAL_CALL SvxUnoDrawingModel::getAvailableServiceNames()
+{
+ const uno::Sequence< OUString > aSNS_ORG( SvxFmMSFactory::getAvailableServiceNames() );
+
+ uno::Sequence< OUString > aSNS{
+ "com.sun.star.drawing.DashTable",
+ "com.sun.star.drawing.GradientTable",
+ "com.sun.star.drawing.HatchTable",
+ "com.sun.star.drawing.BitmapTable",
+ "com.sun.star.drawing.TransparencyGradientTable",
+ "com.sun.star.drawing.MarkerTable",
+ "com.sun.star.text.NumberingRules",
+ "com.sun.star.image.ImageMapRectangleObject",
+ "com.sun.star.image.ImageMapCircleObject",
+ "com.sun.star.image.ImageMapPolygonObject",
+
+ "com.sun.star.presentation.TitleTextShape",
+ "com.sun.star.presentation.OutlinerShape",
+ "com.sun.star.presentation.SubtitleShape",
+ "com.sun.star.presentation.GraphicObjectShape",
+ "com.sun.star.presentation.ChartShape",
+ "com.sun.star.presentation.PageShape",
+ "com.sun.star.presentation.OLE2Shape",
+ "com.sun.star.presentation.TableShape",
+ "com.sun.star.presentation.OrgChartShape",
+ "com.sun.star.presentation.NotesShape",
+ "com.sun.star.presentation.HandoutShape"
+ };
+
+ return comphelper::concatSequences( aSNS_ORG, aSNS );
+}
+
+// lang::XServiceInfo
+OUString SAL_CALL SvxUnoDrawingModel::getImplementationName()
+{
+ return "SvxUnoDrawingModel";
+}
+
+sal_Bool SAL_CALL SvxUnoDrawingModel::supportsService( const OUString& ServiceName )
+{
+ return cppu::supportsService( this, ServiceName );
+}
+
+uno::Sequence< OUString > SAL_CALL SvxUnoDrawingModel::getSupportedServiceNames()
+{
+ return { "com.sun.star.drawing.DrawingDocument" };
+}
+
+// XAnyCompareFactory
+uno::Reference< css::ucb::XAnyCompare > SAL_CALL SvxUnoDrawingModel::createAnyCompareByName( const OUString& )
+{
+ return SvxCreateNumRuleCompare();
+}
+
+SvxUnoDrawPagesAccess::SvxUnoDrawPagesAccess( SvxUnoDrawingModel& rMyModel ) noexcept
+: mrModel(rMyModel)
+{
+}
+
+// XIndexAccess
+sal_Int32 SAL_CALL SvxUnoDrawPagesAccess::getCount()
+{
+ ::SolarMutexGuard aGuard;
+
+ sal_Int32 nCount = 0;
+
+ if( mrModel.mpDoc )
+ nCount = mrModel.mpDoc->GetPageCount();
+
+ return nCount;
+}
+
+uno::Any SAL_CALL SvxUnoDrawPagesAccess::getByIndex( sal_Int32 Index )
+{
+ ::SolarMutexGuard aGuard;
+
+ uno::Any aAny;
+
+ if( mrModel.mpDoc )
+ {
+ if( (Index < 0) || (Index >= mrModel.mpDoc->GetPageCount() ) )
+ throw lang::IndexOutOfBoundsException();
+
+ SdrPage* pPage = mrModel.mpDoc->GetPage( static_cast<sal_uInt16>(Index) );
+ if( pPage )
+ {
+ uno::Reference< uno::XInterface > xPage( pPage->mxUnoPage );
+
+ if( !xPage.is() )
+ {
+ if( dynamic_cast<FmFormModel*>( mrModel.mpDoc ) )
+ xPage = static_cast<drawing::XDrawPage*>(new SvxFmDrawPage( pPage ));
+ else
+ xPage = static_cast<drawing::XDrawPage*>(new SvxDrawPage( pPage ));
+
+ pPage->mxUnoPage = xPage;
+ }
+
+ aAny <<= xPage;
+ }
+ }
+ return aAny;
+}
+
+// XElementAccess
+uno::Type SAL_CALL SvxUnoDrawPagesAccess::getElementType()
+{
+ return cppu::UnoType<drawing::XDrawPage>::get();
+}
+
+sal_Bool SAL_CALL SvxUnoDrawPagesAccess::hasElements()
+{
+ return getCount() > 0;
+}
+
+// XDrawPages
+
+// create a new page with model at given position
+// and return corresponding SdDrawPage
+uno::Reference< drawing::XDrawPage > SAL_CALL SvxUnoDrawPagesAccess::insertNewByIndex( sal_Int32 nIndex )
+{
+ ::SolarMutexGuard aGuard;
+
+ uno::Reference< drawing::XDrawPage > xDrawPage;
+
+ if( mrModel.mpDoc )
+ {
+ rtl::Reference<SdrPage> pPage;
+
+ if( auto pFormModel = dynamic_cast<FmFormModel*>( mrModel.mpDoc ) )
+ pPage = new FmFormPage(*pFormModel);
+ else
+ pPage = new SdrPage(*mrModel.mpDoc);
+
+ mrModel.mpDoc->InsertPage( pPage.get(), static_cast<sal_uInt16>(nIndex) );
+ xDrawPage.set( pPage->getUnoPage(), uno::UNO_QUERY );
+ }
+
+ return xDrawPage;
+}
+
+void SAL_CALL SvxUnoDrawPagesAccess::remove( const uno::Reference< drawing::XDrawPage >& xPage )
+{
+ ::SolarMutexGuard aGuard;
+
+ sal_uInt16 nPageCount = mrModel.mpDoc->GetPageCount();
+ if( nPageCount <= 1 )
+ return;
+
+ // get pPage from xPage and get Id (nPos)
+ SvxDrawPage* pSvxPage = comphelper::getFromUnoTunnel<SvxDrawPage>( xPage );
+ if( pSvxPage )
+ {
+ SdrPage* pPage = pSvxPage->GetSdrPage();
+ if(pPage)
+ {
+ sal_uInt16 nPage = pPage->GetPageNum();
+ mrModel.mpDoc->DeletePage( nPage );
+ }
+ }
+}
+
+// XServiceInfo
+
+OUString SAL_CALL SvxUnoDrawPagesAccess::getImplementationName( )
+{
+ return "SvxUnoDrawPagesAccess";
+}
+
+sal_Bool SAL_CALL SvxUnoDrawPagesAccess::supportsService( const OUString& ServiceName )
+{
+ return cppu::supportsService(this, ServiceName);
+}
+
+uno::Sequence< OUString > SAL_CALL SvxUnoDrawPagesAccess::getSupportedServiceNames( )
+{
+ return { "com.sun.star.drawing.DrawPages" };
+}
+
+css::uno::Reference< css::container::XIndexReplace > SvxCreateNumRule(SdrModel* pModel)
+{
+ const SvxNumRule* pDefaultRule = nullptr;
+ if( pModel )
+ {
+ const SvxNumBulletItem* pItem = pModel->GetItemPool().GetSecondaryPool()->GetPoolDefaultItem(EE_PARA_NUMBULLET);
+ if( pItem )
+ {
+ pDefaultRule = &pItem->GetNumRule();
+ }
+ }
+
+ if( pDefaultRule )
+ {
+ return SvxCreateNumRule( *pDefaultRule );
+ }
+ else
+ {
+ SvxNumRule aTempRule( SvxNumRuleFlags::NONE, 10, false );
+ return SvxCreateNumRule( aTempRule );
+ }
+}
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/unodraw/unomtabl.cxx b/svx/source/unodraw/unomtabl.cxx
new file mode 100644
index 000000000..60b858f38
--- /dev/null
+++ b/svx/source/unodraw/unomtabl.cxx
@@ -0,0 +1,423 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * 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 <set>
+#include <com/sun/star/lang/XServiceInfo.hpp>
+#include <com/sun/star/container/XNameContainer.hpp>
+#include <com/sun/star/drawing/PointSequence.hpp>
+#include <com/sun/star/util/XCancellable.hpp>
+
+#include <comphelper/sequence.hxx>
+#include <cppuhelper/implbase.hxx>
+#include <cppuhelper/supportsservice.hxx>
+#include <svl/itempool.hxx>
+#include <svl/itemset.hxx>
+#include <svl/lstner.hxx>
+#include <svx/xlnedit.hxx>
+#include <svx/xlnstit.hxx>
+#include <svx/svdmodel.hxx>
+#include <svx/xdef.hxx>
+
+#include <vector>
+#include <vcl/svapp.hxx>
+
+
+#include <svx/unofill.hxx>
+
+#include <svx/unoapi.hxx>
+
+using namespace ::com::sun::star;
+using namespace ::cppu;
+
+typedef std::vector<std::unique_ptr<SfxItemSet>> ItemPoolVector;
+
+namespace {
+
+class SvxUnoMarkerTable
+ : public WeakImplHelper<
+ util::XCancellable,
+ container::XNameContainer,
+ lang::XServiceInfo>
+ , public SfxListener
+{
+private:
+ SdrModel* mpModel;
+ SfxItemPool* mpModelPool;
+
+ ItemPoolVector maItemSetVector;
+
+public:
+ explicit SvxUnoMarkerTable( SdrModel* pModel ) noexcept;
+ virtual ~SvxUnoMarkerTable() noexcept override;
+
+ void dispose();
+
+ // SfxListener
+ virtual void Notify( SfxBroadcaster& rBC, const SfxHint& rHint ) noexcept override;
+
+ void ImplInsertByName( const OUString& aName, const uno::Any& aElement );
+
+ // 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;
+
+ // XCancellable
+ virtual void SAL_CALL cancel() override;
+
+ // XNameContainer
+ virtual void SAL_CALL insertByName( const OUString& aName, const uno::Any& aElement ) override;
+ virtual void SAL_CALL removeByName( const OUString& Name ) override;
+
+ // XNameReplace
+ virtual void SAL_CALL replaceByName( const OUString& aName, const uno::Any& aElement ) override;
+
+ // XNameAccess
+ virtual uno::Any SAL_CALL getByName( const OUString& aName ) override;
+ virtual uno::Sequence< OUString > SAL_CALL getElementNames( ) override;
+ virtual sal_Bool SAL_CALL hasByName( const OUString& aName ) override;
+
+ // XElementAccess
+ virtual uno::Type SAL_CALL getElementType( ) override;
+ virtual sal_Bool SAL_CALL hasElements( ) override;
+};
+
+}
+
+SvxUnoMarkerTable::SvxUnoMarkerTable( SdrModel* pModel ) noexcept
+: mpModel( pModel ),
+ mpModelPool( pModel ? &pModel->GetItemPool() : nullptr )
+{
+ if( pModel )
+ StartListening( *pModel );
+}
+
+SvxUnoMarkerTable::~SvxUnoMarkerTable() noexcept
+{
+ SolarMutexGuard aGuard;
+
+ if( mpModel )
+ EndListening( *mpModel );
+ dispose();
+}
+
+void SvxUnoMarkerTable::dispose()
+{
+ maItemSetVector.clear();
+}
+
+// SfxListener
+void SvxUnoMarkerTable::Notify( SfxBroadcaster&, const SfxHint& rHint ) noexcept
+{
+ if (rHint.GetId() == SfxHintId::ThisIsAnSdrHint)
+ {
+ const SdrHint* pSdrHint = static_cast<const SdrHint*>(&rHint);
+ if( SdrHintKind::ModelCleared == pSdrHint->GetKind() )
+ dispose();
+ }
+}
+
+sal_Bool SAL_CALL SvxUnoMarkerTable::supportsService( const OUString& ServiceName )
+{
+ return cppu::supportsService(this, ServiceName);
+}
+
+OUString SAL_CALL SvxUnoMarkerTable::getImplementationName()
+{
+ return "SvxUnoMarkerTable";
+}
+
+uno::Sequence< OUString > SAL_CALL SvxUnoMarkerTable::getSupportedServiceNames( )
+{
+ uno::Sequence<OUString> aSNS { "com.sun.star.drawing.MarkerTable" };
+ return aSNS;
+}
+
+void SvxUnoMarkerTable::ImplInsertByName( const OUString& aName, const uno::Any& aElement )
+{
+ maItemSetVector.push_back(
+ std::make_unique<SfxItemSetFixed<XATTR_LINESTART, XATTR_LINEEND>>( *mpModelPool ));
+ auto pInSet = maItemSetVector.back().get();
+
+ XLineEndItem aEndMarker(XATTR_LINEEND);
+ aEndMarker.SetName( aName );
+ aEndMarker.PutValue( aElement, 0 );
+
+ pInSet->Put( aEndMarker );
+
+ XLineStartItem aStartMarker(XATTR_LINESTART);
+ aStartMarker.SetName( aName );
+ aStartMarker.PutValue( aElement, 0 );
+
+ pInSet->Put( aStartMarker );
+}
+
+// XNameContainer
+void SAL_CALL SvxUnoMarkerTable::insertByName( const OUString& aApiName, const uno::Any& aElement )
+{
+ SolarMutexGuard aGuard;
+
+ if( hasByName( aApiName ) )
+ throw container::ElementExistException();
+
+ OUString aName = SvxUnogetInternalNameForItem(XATTR_LINEEND, aApiName);
+
+ ImplInsertByName( aName, aElement );
+}
+
+void SAL_CALL SvxUnoMarkerTable::cancel()
+{
+ SolarMutexGuard aGuard;
+ // drop all items that are owned by this service and not the document
+ // (i.e. they are unused)
+ dispose();
+}
+
+void SAL_CALL SvxUnoMarkerTable::removeByName( const OUString& aApiName )
+{
+ SolarMutexGuard aGuard;
+
+ OUString aName = SvxUnogetInternalNameForItem(XATTR_LINEEND, aApiName);
+
+ auto aIter = std::find_if(maItemSetVector.begin(), maItemSetVector.end(),
+ [&aName](const std::unique_ptr<SfxItemSet>& rpItem) {
+ const NameOrIndex *pItem = &(rpItem->Get( XATTR_LINEEND ) );
+ return pItem->GetName() == aName;
+ });
+ if (aIter != maItemSetVector.end())
+ {
+ maItemSetVector.erase( aIter );
+ return;
+ }
+
+ if( !hasByName( aName ) )
+ throw container::NoSuchElementException();
+}
+
+// XNameReplace
+void SAL_CALL SvxUnoMarkerTable::replaceByName( const OUString& aApiName, const uno::Any& aElement )
+{
+ SolarMutexGuard aGuard;
+
+ const OUString aName = SvxUnogetInternalNameForItem(XATTR_LINEEND, aApiName);
+
+ auto aIter = std::find_if(maItemSetVector.begin(), maItemSetVector.end(),
+ [&aName](const std::unique_ptr<SfxItemSet>& rpItem) {
+ const NameOrIndex *pItem = &(rpItem->Get( XATTR_LINEEND ) );
+ return pItem->GetName() == aName;
+ });
+ if (aIter != maItemSetVector.end())
+ {
+ XLineEndItem aEndMarker(XATTR_LINEEND);
+ aEndMarker.SetName( aName );
+ if( !aEndMarker.PutValue( aElement, 0 ) )
+ throw lang::IllegalArgumentException();
+
+ (*aIter)->Put( aEndMarker );
+
+ XLineStartItem aStartMarker(XATTR_LINESTART);
+ aStartMarker.SetName( aName );
+ aStartMarker.PutValue( aElement, 0 );
+
+ (*aIter)->Put( aStartMarker );
+ return;
+ }
+
+ // if it is not in our own sets, modify the pool!
+ bool bFound = false;
+
+ if (mpModelPool)
+ for (const SfxPoolItem* p : mpModelPool->GetItemSurrogates(XATTR_LINESTART))
+ {
+ NameOrIndex *pItem = const_cast<NameOrIndex*>(static_cast<const NameOrIndex*>(p));
+ if( pItem && pItem->GetName() == aName )
+ {
+ pItem->PutValue( aElement, 0 );
+ bFound = true;
+ break;
+ }
+ }
+
+ if (mpModelPool)
+ for (const SfxPoolItem* p : mpModelPool->GetItemSurrogates(XATTR_LINEEND))
+ {
+ NameOrIndex *pItem = const_cast<NameOrIndex*>(static_cast<const NameOrIndex*>(p));
+ if( pItem && pItem->GetName() == aName )
+ {
+ pItem->PutValue( aElement, 0 );
+ bFound = true;
+ break;
+ }
+ }
+
+ if( !bFound )
+ throw container::NoSuchElementException();
+
+ ImplInsertByName( aName, aElement );
+}
+
+static bool getByNameFromPool( std::u16string_view rSearchName, SfxItemPool const * pPool, sal_uInt16 nWhich, uno::Any& rAny )
+{
+ if (pPool)
+ for (const SfxPoolItem* p : pPool->GetItemSurrogates(nWhich))
+ {
+ const NameOrIndex *pItem = static_cast<const NameOrIndex*>(p);
+
+ if( pItem && pItem->GetName() == rSearchName )
+ {
+ pItem->QueryValue( rAny );
+ return true;
+ }
+ }
+
+ return false;
+}
+
+// XNameAccess
+uno::Any SAL_CALL SvxUnoMarkerTable::getByName( const OUString& aApiName )
+{
+ SolarMutexGuard aGuard;
+
+ OUString aName = SvxUnogetInternalNameForItem(XATTR_LINEEND, aApiName);
+
+ uno::Any aAny;
+
+ if (mpModelPool && !aName.isEmpty())
+ {
+ do
+ {
+ if (getByNameFromPool(aName, mpModelPool, XATTR_LINESTART, aAny))
+ break;
+
+ if (getByNameFromPool(aName, mpModelPool, XATTR_LINEEND, aAny))
+ break;
+
+ throw container::NoSuchElementException();
+ }
+ while(false);
+ }
+
+ return aAny;
+}
+
+static void createNamesForPool( SfxItemPool const * pPool, sal_uInt16 nWhich, std::set< OUString >& rNameSet )
+{
+ for (const SfxPoolItem* p : pPool->GetItemSurrogates(nWhich))
+ {
+ const NameOrIndex* pItem = static_cast<const NameOrIndex*>(p);
+
+ if( pItem == nullptr || pItem->GetName().isEmpty() )
+ continue;
+
+ OUString aName = SvxUnogetApiNameForItem(XATTR_LINEEND, pItem->GetName());
+ rNameSet.insert( aName );
+ }
+}
+
+uno::Sequence< OUString > SAL_CALL SvxUnoMarkerTable::getElementNames()
+{
+ SolarMutexGuard aGuard;
+
+ std::set< OUString > aNameSet;
+
+ // search model pool for line starts
+ createNamesForPool( mpModelPool, XATTR_LINESTART, aNameSet );
+
+ // search model pool for line ends
+ createNamesForPool( mpModelPool, XATTR_LINEEND, aNameSet );
+
+ return comphelper::containerToSequence(aNameSet);
+}
+
+sal_Bool SAL_CALL SvxUnoMarkerTable::hasByName( const OUString& aName )
+{
+ SolarMutexGuard aGuard;
+
+ if( aName.isEmpty() )
+ return false;
+
+ OUString aSearchName;
+
+ const NameOrIndex *pItem;
+
+ aSearchName = SvxUnogetInternalNameForItem(XATTR_LINESTART, aName);
+ if (mpModelPool)
+ for (const SfxPoolItem* p : mpModelPool->GetItemSurrogates(XATTR_LINESTART))
+ {
+ pItem = static_cast<const NameOrIndex*>(p);
+ if( pItem && pItem->GetName() == aSearchName )
+ return true;
+ }
+
+ aSearchName = SvxUnogetInternalNameForItem(XATTR_LINEEND, aName);
+ if (mpModelPool)
+ for (const SfxPoolItem* p : mpModelPool->GetItemSurrogates(XATTR_LINEEND))
+ {
+ pItem = static_cast<const NameOrIndex*>(p);
+ if( pItem && pItem->GetName() == aSearchName )
+ return true;
+ }
+
+ return false;
+}
+
+// XElementAccess
+uno::Type SAL_CALL SvxUnoMarkerTable::getElementType( )
+{
+ return cppu::UnoType<drawing::PointSequence>::get();
+}
+
+sal_Bool SAL_CALL SvxUnoMarkerTable::hasElements( )
+{
+ SolarMutexGuard aGuard;
+
+ const NameOrIndex *pItem;
+
+ if (mpModelPool)
+ for (const SfxPoolItem* p : mpModelPool->GetItemSurrogates(XATTR_LINESTART))
+ {
+ pItem = static_cast<const NameOrIndex*>(p);
+ if( pItem && !pItem->GetName().isEmpty() )
+ return true;
+ }
+
+ if (mpModelPool)
+ for (const SfxPoolItem* p : mpModelPool->GetItemSurrogates(XATTR_LINEEND))
+ {
+ pItem = static_cast<const NameOrIndex*>(p);
+ if( pItem && !pItem->GetName().isEmpty() )
+ return true;
+ }
+
+ return false;
+}
+
+/**
+ * Create a hatchtable
+ */
+uno::Reference< uno::XInterface > SvxUnoMarkerTable_createInstance( SdrModel* pModel )
+{
+ return *new SvxUnoMarkerTable(pModel);
+}
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/unodraw/unopage.cxx b/svx/source/unodraw/unopage.cxx
new file mode 100644
index 000000000..ee91c36c9
--- /dev/null
+++ b/svx/source/unodraw/unopage.cxx
@@ -0,0 +1,898 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * 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/document/EventObject.hpp>
+#include <com/sun/star/embed/XEmbeddedObject.hpp>
+#include <com/sun/star/lang/DisposedException.hpp>
+#include <com/sun/star/lang/IndexOutOfBoundsException.hpp>
+#include <o3tl/safeint.hxx>
+#include <osl/mutex.hxx>
+#include <comphelper/classids.hxx>
+#include <comphelper/embeddedobjectcontainer.hxx>
+#include <comphelper/sequence.hxx>
+#include <cppuhelper/supportsservice.hxx>
+
+#include <svx/svdpool.hxx>
+#include <svx/svdobj.hxx>
+#include <svx/svdoole2.hxx>
+#include <svx/svdpage.hxx>
+#include <svx/svdmodel.hxx>
+#include <svx/strings.hrc>
+#include <svx/svdview.hxx>
+#include <svx/svdpagv.hxx>
+#include <svx/svdundo.hxx>
+#include <svx/unopage.hxx>
+#include "shapeimpl.hxx"
+#include <svx/unodraw/SvxTableShape.hxx>
+#include <svx/dialmgr.hxx>
+#include <svx/svdobjkind.hxx>
+#include <svx/unoprov.hxx>
+#include <svx/unoapi.hxx>
+#include <extrud3d.hxx>
+#include <svx/lathe3d.hxx>
+#include <svx/scene3d.hxx>
+#include <vcl/svapp.hxx>
+#include <tools/diagnose_ex.h>
+#include <tools/globname.hxx>
+#include <sal/log.hxx>
+
+using namespace ::cppu;
+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::drawing;
+
+UNO3_GETIMPLEMENTATION_IMPL( SvxDrawPage );
+
+SvxDrawPage::SvxDrawPage(SdrPage* pInPage) // TTTT should be reference
+: mrBHelper(m_aMutex)
+ ,mpPage(pInPage)
+ ,mpModel(&pInPage->getSdrModelFromSdrPage()) // register at broadcaster
+ ,mpView(new SdrView(pInPage->getSdrModelFromSdrPage())) // create (hidden) view
+{
+ mpView->SetDesignMode();
+}
+
+SvxDrawPage::~SvxDrawPage() noexcept
+{
+ if( !mrBHelper.bDisposed )
+ {
+ assert(!"SvxDrawPage must be disposed!");
+ acquire();
+ dispose();
+ }
+}
+
+// XInterface
+void SvxDrawPage::release() noexcept
+{
+ OWeakAggObject::release();
+}
+
+// XComponent
+void SvxDrawPage::disposing() noexcept
+{
+ if( mpModel )
+ {
+ mpModel = nullptr;
+ }
+
+ mpView.reset();
+ mpPage = nullptr;
+}
+
+// XComponent
+void SvxDrawPage::dispose()
+{
+ SolarMutexGuard aSolarGuard;
+
+ // An frequently programming error is to release the last
+ // reference to this object in the disposing message.
+ // Make it robust, hold a self Reference.
+ uno::Reference< lang::XComponent > xSelf( this );
+
+ // Guard dispose against multiple threading
+ // Remark: It is an error to call dispose more than once
+ bool bDoDispose = false;
+ {
+ osl::MutexGuard aGuard( mrBHelper.rMutex );
+ if( !mrBHelper.bDisposed && !mrBHelper.bInDispose )
+ {
+ // only one call go into this section
+ mrBHelper.bInDispose = true;
+ bDoDispose = true;
+ }
+ }
+
+ // Do not hold the mutex because we are broadcasting
+ if( !bDoDispose )
+ return;
+
+ // Create an event with this as sender
+ try
+ {
+ uno::Reference< uno::XInterface > xSource( uno::Reference< uno::XInterface >::query( static_cast<lang::XComponent *>(this) ) );
+ css::document::EventObject aEvt;
+ aEvt.Source = xSource;
+ // inform all listeners to release this object
+ // The listener container are automatically cleared
+ mrBHelper.aLC.disposeAndClear( aEvt );
+ // notify subclasses to do their dispose
+ disposing();
+ }
+ catch(const css::uno::Exception&)
+ {
+ // catch exception and throw again but signal that
+ // the object was disposed. Dispose should be called
+ // only once.
+ osl::MutexGuard aGuard( mrBHelper.rMutex );
+ mrBHelper.bDisposed = true;
+ mrBHelper.bInDispose = false;
+ throw;
+ }
+
+ osl::MutexGuard aGuard( mrBHelper.rMutex );
+ mrBHelper.bDisposed = true;
+ mrBHelper.bInDispose = false;
+
+}
+
+void SAL_CALL SvxDrawPage::addEventListener( const css::uno::Reference< css::lang::XEventListener >& aListener )
+{
+ SolarMutexGuard aGuard;
+
+ if( mpModel == nullptr )
+ throw lang::DisposedException();
+
+ mrBHelper.addListener( cppu::UnoType<decltype(aListener)>::get() , aListener );
+}
+
+void SAL_CALL SvxDrawPage::removeEventListener( const css::uno::Reference< css::lang::XEventListener >& aListener )
+{
+ SolarMutexGuard aGuard;
+
+ if( mpModel == nullptr )
+ throw lang::DisposedException();
+
+ mrBHelper.removeListener( cppu::UnoType<decltype(aListener)>::get() , aListener );
+}
+
+void SAL_CALL SvxDrawPage::add( const uno::Reference< drawing::XShape >& xShape )
+{
+ SolarMutexGuard aGuard;
+
+ if ( ( mpModel == nullptr ) || ( mpPage == nullptr ) )
+ throw lang::DisposedException();
+
+ SvxShape* pShape = comphelper::getFromUnoTunnel<SvxShape>( xShape );
+
+ if( nullptr == pShape )
+ return;
+
+ SdrObject *pObj = pShape->GetSdrObject();
+ bool bNeededToClone(false);
+
+ if(nullptr != pObj && &pObj->getSdrModelFromSdrObject() != &mpPage->getSdrModelFromSdrPage())
+ {
+ // TTTT UNO API tries to add an existing SvxShape to this SvxDrawPage,
+ // but these use different SdrModels. It was possible before to completely
+ // 'change' a SdrObject to another SdrModel (including dangerous MigrateItemPool
+ // stuff), but is no longer. We need to Clone the SdrObject to the target model
+ // and ::Create a new SvxShape (set SdrObject there, take obver values, ...)
+ SdrObject* pClonedSdrShape(pObj->CloneSdrObject(mpPage->getSdrModelFromSdrPage()));
+ pObj->setUnoShape(nullptr);
+ pClonedSdrShape->setUnoShape(xShape);
+ // pShape->InvalidateSdrObject();
+ // pShape->Create(pClonedSdrShape, this);
+ SdrObject::Free(pObj);
+ pObj = pClonedSdrShape;
+ bNeededToClone = true;
+ }
+
+ if(!pObj)
+ {
+ pObj = CreateSdrObject( xShape );
+ ENSURE_OR_RETURN_VOID( pObj != nullptr, "SvxDrawPage::add: no SdrObject was created!" );
+ }
+ else if ( !pObj->IsInserted() )
+ {
+ mpPage->InsertObject( pObj );
+
+ if(bNeededToClone)
+ {
+ // TTTT Unfortunately in SdrObject::SetPage (see there) the
+ // xShape/UnoShape at the newly cloned SDrObject is *removed* again,
+ // so re-set it here, the caller *may need it* (e.g. Writer)
+ uno::Reference< uno::XInterface > xShapeCheck(pObj->getWeakUnoShape());
+
+ if( !xShapeCheck.is() )
+ {
+ pObj->setUnoShape(xShape);
+ }
+ }
+ }
+
+ pShape->Create( pObj, this );
+ OSL_ENSURE( pShape->GetSdrObject() == pObj, "SvxDrawPage::add: shape does not know about its newly created SdrObject!" );
+
+ if ( !pObj->IsInserted() )
+ {
+ mpPage->InsertObject( pObj );
+ }
+
+ mpModel->SetChanged();
+}
+
+void SAL_CALL SvxDrawPage::addTop( const uno::Reference< drawing::XShape >& xShape )
+{
+ add(xShape);
+}
+
+void SAL_CALL SvxDrawPage::addBottom( const uno::Reference< drawing::XShape >& xShape )
+{
+ SolarMutexGuard aGuard;
+
+ if ( ( mpModel == nullptr ) || ( mpPage == nullptr ) )
+ throw lang::DisposedException();
+
+ SvxShape* pShape = comphelper::getFromUnoTunnel<SvxShape>( xShape );
+
+ if( nullptr == pShape )
+ return;
+
+ SdrObject *pObj = pShape->GetSdrObject();
+
+ if(!pObj)
+ {
+ pObj = CreateSdrObject( xShape, true );
+ ENSURE_OR_RETURN_VOID( pObj != nullptr, "SvxDrawPage::add: no SdrObject was created!" );
+ }
+ else if ( !pObj->IsInserted() )
+ {
+ mpPage->InsertObject( pObj, 0 );
+ }
+
+ pShape->Create( pObj, this );
+ OSL_ENSURE( pShape->GetSdrObject() == pObj, "SvxDrawPage::add: shape does not know about its newly created SdrObject!" );
+
+ if ( !pObj->IsInserted() )
+ {
+ mpPage->InsertObject( pObj, 0 );
+ }
+
+ mpModel->SetChanged();
+}
+
+void SAL_CALL SvxDrawPage::remove( const Reference< drawing::XShape >& xShape )
+{
+ SolarMutexGuard aGuard;
+
+ if( (mpModel == nullptr) || (mpPage == nullptr) )
+ throw lang::DisposedException();
+
+ SdrObject* pObj = SdrObject::getSdrObjectFromXShape( xShape );
+ if (!pObj)
+ return;
+
+ // remove SdrObject from page
+ const size_t nCount = mpPage->GetObjCount();
+ for( size_t nNum = 0; nNum < nCount; ++nNum )
+ {
+ if(mpPage->GetObj(nNum) == pObj)
+ {
+ const bool bUndoEnabled = mpModel->IsUndoEnabled();
+
+ if (bUndoEnabled)
+ {
+ mpModel->BegUndo(SvxResId(STR_EditDelete),
+ pObj->TakeObjNameSingul(), SdrRepeatFunc::Delete);
+
+ mpModel->AddUndo(mpModel->GetSdrUndoFactory().CreateUndoDeleteObject(*pObj));
+ }
+
+ OSL_VERIFY( mpPage->RemoveObject( nNum ) == pObj );
+
+ if (!bUndoEnabled)
+ SdrObject::Free(pObj);
+
+ if (bUndoEnabled)
+ mpModel->EndUndo();
+
+ break;
+ }
+ }
+
+ mpModel->SetChanged();
+}
+
+void SvxDrawPage::sort( const css::uno::Sequence< sal_Int32 >& sortOrder )
+{
+ SolarMutexGuard aGuard;
+
+ if ((mpModel == nullptr) || (mpPage == nullptr))
+ throw lang::DisposedException();
+
+ auto newOrder = comphelper::sequenceToContainer<std::vector<sal_Int32>>(sortOrder);
+ mpPage->sort(newOrder);
+}
+
+// css::container::XIndexAccess
+sal_Int32 SAL_CALL SvxDrawPage::getCount()
+{
+ SolarMutexGuard aGuard;
+
+ if( (mpModel == nullptr) || (mpPage == nullptr) )
+ throw lang::DisposedException();
+
+ return static_cast<sal_Int32>( mpPage->GetObjCount() );
+}
+
+uno::Any SAL_CALL SvxDrawPage::getByIndex( sal_Int32 Index )
+{
+ SolarMutexGuard aGuard;
+
+ if( (mpModel == nullptr) || (mpPage == nullptr) )
+ throw lang::DisposedException("Model or Page was already disposed!");
+
+ if ( Index < 0 || o3tl::make_unsigned(Index) >= mpPage->GetObjCount() )
+ throw lang::IndexOutOfBoundsException("Index (" + OUString::number(Index)
+ + ") needs to be a positive integer smaller than the shape count ("
+ + OUString::number(mpPage->GetObjCount()) + ")!");
+
+ SdrObject* pObj = mpPage->GetObj( Index );
+ if( pObj == nullptr )
+ throw uno::RuntimeException("Runtime exception thrown while getting a ref to the SdrObject at index: "
+ + OUString::number(Index));
+
+
+ return Any(Reference< drawing::XShape >( pObj->getUnoShape(), uno::UNO_QUERY ));
+}
+
+// css::container::XElementAccess
+uno::Type SAL_CALL SvxDrawPage::getElementType()
+{
+ return cppu::UnoType<drawing::XShape>::get();
+}
+
+sal_Bool SAL_CALL SvxDrawPage::hasElements()
+{
+ SolarMutexGuard aGuard;
+
+ if( (mpModel == nullptr) || (mpPage == nullptr) )
+ throw lang::DisposedException();
+
+ return mpPage && mpPage->GetObjCount()>0;
+}
+
+namespace
+{
+ void lcl_markSdrObjectOfShape( const Reference< drawing::XShape >& _rxShape, SdrView& _rView, SdrPageView& _rPageView )
+ {
+ SdrObject* pObj = SdrObject::getSdrObjectFromXShape( _rxShape );
+ if ( !pObj )
+ return;
+
+ _rView.MarkObj( pObj, &_rPageView );
+ }
+}
+
+// ATTENTION: SelectObjectsInView selects the css::drawing::Shapes
+// only in the given SdrPageView. It hasn't to be the visible SdrPageView.
+void SvxDrawPage::SelectObjectsInView( const Reference< drawing::XShapes > & aShapes, SdrPageView* pPageView ) noexcept
+{
+ SAL_WARN_IF(!pPageView, "svx", "SdrPageView is NULL!");
+ SAL_WARN_IF(!mpView, "svx", "SdrView is NULL!");
+
+ if(pPageView==nullptr || mpView==nullptr)
+ return;
+
+ mpView->UnmarkAllObj( pPageView );
+
+ tools::Long nCount = aShapes->getCount();
+ for( tools::Long i = 0; i < nCount; i++ )
+ {
+ uno::Any aAny( aShapes->getByIndex(i) );
+ Reference< drawing::XShape > xShape;
+ if( aAny >>= xShape )
+ lcl_markSdrObjectOfShape( xShape, *mpView, *pPageView );
+ }
+}
+
+// ATTENTION: SelectObjectInView selects the shape only in the given SdrPageView.
+// It hasn't to be the visible SdrPageView.
+void SvxDrawPage::SelectObjectInView( const Reference< drawing::XShape > & xShape, SdrPageView* pPageView ) noexcept
+{
+ SAL_WARN_IF(!pPageView, "svx", "SdrPageView is NULL!");
+ SAL_WARN_IF(!mpView, "svx", "SdrView is NULL!");
+
+ if(pPageView!=nullptr && mpView != nullptr)
+ {
+ mpView->UnmarkAllObj( pPageView );
+ lcl_markSdrObjectOfShape( xShape, *mpView, *pPageView );
+ }
+}
+
+Reference< drawing::XShapeGroup > SAL_CALL SvxDrawPage::group( const Reference< drawing::XShapes >& xShapes )
+{
+ SolarMutexGuard aGuard;
+
+ if( (mpModel == nullptr) || (mpPage == nullptr) )
+ throw lang::DisposedException();
+
+ SAL_WARN_IF(!mpPage , "svx", "SdrPage is NULL!");
+ SAL_WARN_IF(!mpView, "svx", "SdrView is NULL!");
+
+ Reference< css::drawing::XShapeGroup > xShapeGroup;
+ if(mpPage==nullptr||mpView==nullptr||!xShapes.is())
+ return xShapeGroup;
+
+ SdrPageView* pPageView = mpView->ShowSdrPage( mpPage );
+
+ SelectObjectsInView( xShapes, pPageView );
+
+ mpView->GroupMarked();
+
+ mpView->AdjustMarkHdl();
+ const SdrMarkList& rMarkList = mpView->GetMarkedObjectList();
+ if( rMarkList.GetMarkCount() == 1 )
+ {
+ SdrObject* pObj = rMarkList.GetMark(0)->GetMarkedSdrObj();
+ if( pObj )
+ xShapeGroup.set( pObj->getUnoShape(), UNO_QUERY );
+ }
+
+ mpView->HideSdrPage();
+
+ if( mpModel )
+ mpModel->SetChanged();
+
+ return xShapeGroup;
+}
+
+void SAL_CALL SvxDrawPage::ungroup( const Reference< drawing::XShapeGroup >& aGroup )
+{
+ SolarMutexGuard aGuard;
+
+ if( (mpModel == nullptr) || (mpPage == nullptr) )
+ throw lang::DisposedException();
+
+ SAL_WARN_IF(!mpPage, "svx", "SdrPage is NULL!");
+ SAL_WARN_IF(!mpView, "svx", "SdrView is NULL!");
+
+ if(mpPage==nullptr||mpView==nullptr||!aGroup.is())
+ return;
+
+ SdrPageView* pPageView = mpView->ShowSdrPage( mpPage );
+
+ SelectObjectInView( aGroup, pPageView );
+ mpView->UnGroupMarked();
+
+ mpView->HideSdrPage();
+
+ if( mpModel )
+ mpModel->SetChanged();
+}
+
+SdrObject* SvxDrawPage::CreateSdrObject_(const Reference< drawing::XShape > & xShape)
+{
+ SdrObjKind nType = SdrObjKind::NONE;
+ SdrInventor nInventor;
+
+ GetTypeAndInventor( nType, nInventor, xShape->getShapeType() );
+ if (nType == SdrObjKind::NONE)
+ return nullptr;
+
+ awt::Size aSize = xShape->getSize();
+ aSize.Width += 1;
+ aSize.Height += 1;
+ awt::Point aPos = xShape->getPosition();
+ tools::Rectangle aRect( Point( aPos.X, aPos.Y ), Size( aSize.Width, aSize.Height ) );
+
+ SdrObject* pNewObj = SdrObjFactory::MakeNewObject(
+ *mpModel,
+ nInventor,
+ nType,
+ &aRect);
+
+ if (!pNewObj)
+ return nullptr;
+
+ if( auto pScene = dynamic_cast<E3dScene* >(pNewObj) )
+ {
+ // initialise scene
+
+ double fW = static_cast<double>(aSize.Width);
+ double fH = static_cast<double>(aSize.Height);
+
+ Camera3D aCam(pScene->GetCamera());
+ aCam.SetAutoAdjustProjection(false);
+ aCam.SetViewWindow(- fW / 2, - fH / 2, fW, fH);
+ basegfx::B3DPoint aLookAt;
+ basegfx::B3DPoint aCamPos(0.0, 0.0, 10000.0);
+ aCam.SetPosAndLookAt(aCamPos, aLookAt);
+ aCam.SetFocalLength(100.0);
+ pScene->SetCamera(aCam);
+
+ pScene->SetBoundAndSnapRectsDirty();
+ }
+ else if(auto pObj = dynamic_cast<E3dExtrudeObj* >(pNewObj))
+ {
+ basegfx::B2DPolygon aNewPolygon;
+ aNewPolygon.append(basegfx::B2DPoint(0.0, 0.0));
+ aNewPolygon.append(basegfx::B2DPoint(0.0, 1.0));
+ aNewPolygon.append(basegfx::B2DPoint(1.0, 0.0));
+ aNewPolygon.setClosed(true);
+ pObj->SetExtrudePolygon(basegfx::B2DPolyPolygon(aNewPolygon));
+
+ // #107245# pObj->SetExtrudeCharacterMode(sal_True);
+ pObj->SetMergedItem(Svx3DCharacterModeItem(true));
+ }
+ else if(auto pLatheObj = dynamic_cast<E3dLatheObj* >(pNewObj))
+ {
+ basegfx::B2DPolygon aNewPolygon;
+ aNewPolygon.append(basegfx::B2DPoint(0.0, 0.0));
+ aNewPolygon.append(basegfx::B2DPoint(0.0, 1.0));
+ aNewPolygon.append(basegfx::B2DPoint(1.0, 0.0));
+ aNewPolygon.setClosed(true);
+ pLatheObj->SetPolyPoly2D(basegfx::B2DPolyPolygon(aNewPolygon));
+
+ // #107245# pObj->SetLatheCharacterMode(sal_True);
+ pLatheObj->SetMergedItem(Svx3DCharacterModeItem(true));
+ }
+
+ return pNewObj;
+}
+
+void SvxDrawPage::GetTypeAndInventor( SdrObjKind& rType, SdrInventor& rInventor, const OUString& aName ) noexcept
+{
+ std::optional<SdrObjKind> nTempType = UHashMap::getId( aName );
+
+ if( !nTempType )
+ {
+ if( aName == "com.sun.star.drawing.TableShape" ||
+ aName == "com.sun.star.presentation.TableShape" )
+ {
+ rInventor = SdrInventor::Default;
+ rType = SdrObjKind::Table;
+ }
+#if HAVE_FEATURE_AVMEDIA
+ else if ( aName == "com.sun.star.presentation.MediaShape" )
+ {
+ rInventor = SdrInventor::Default;
+ rType = SdrObjKind::Media;
+ }
+#endif
+ }
+ else if( IsInventorE3D(*nTempType) )
+ {
+ rInventor = SdrInventor::E3d;
+ rType = *nTempType;
+ }
+ else
+ {
+ rInventor = SdrInventor::Default;
+ rType = *nTempType;
+
+ switch( rType )
+ {
+ case SdrObjKind::OLEPluginFrame:
+ case SdrObjKind::OLE2Plugin:
+ case SdrObjKind::OLE2Applet:
+ rType = SdrObjKind::OLE2;
+ break;
+ default:
+ break;
+ }
+ }
+}
+
+rtl::Reference<SvxShape> SvxDrawPage::CreateShapeByTypeAndInventor( SdrObjKind nType, SdrInventor nInventor, SdrObject *pObj, SvxDrawPage *mpPage, OUString const & referer )
+{
+ rtl::Reference<SvxShape> pRet;
+
+ switch( nInventor )
+ {
+ case SdrInventor::E3d:
+ {
+ switch( nType )
+ {
+ case SdrObjKind::E3D_Scene :
+ pRet = new Svx3DSceneObject( pObj, mpPage );
+ break;
+ case SdrObjKind::E3D_Cube :
+ pRet = new Svx3DCubeObject( pObj );
+ break;
+ case SdrObjKind::E3D_Sphere :
+ pRet = new Svx3DSphereObject( pObj );
+ break;
+ case SdrObjKind::E3D_Lathe :
+ pRet = new Svx3DLatheObject( pObj );
+ break;
+ case SdrObjKind::E3D_Extrusion :
+ pRet = new Svx3DExtrudeObject( pObj );
+ break;
+ case SdrObjKind::E3D_Polygon :
+ pRet = new Svx3DPolygonObject( pObj );
+ break;
+ default: // unknown 3D-object on page
+ assert(false && "the IsInventor3D function must be wrong");
+ pRet = new SvxShape( pObj );
+ break;
+ }
+ break;
+ }
+ case SdrInventor::Default:
+ {
+ switch( nType )
+ {
+ case SdrObjKind::Group:
+ pRet = new SvxShapeGroup( pObj, mpPage );
+ break;
+ case SdrObjKind::Line:
+ pRet = new SvxShapePolyPolygon( pObj );
+ break;
+ case SdrObjKind::Rectangle:
+ pRet = new SvxShapeRect( pObj );
+ break;
+ case SdrObjKind::CircleOrEllipse:
+ case SdrObjKind::CircleSection:
+ case SdrObjKind::CircleArc:
+ case SdrObjKind::CircleCut:
+ pRet = new SvxShapeCircle( pObj );
+ break;
+ case SdrObjKind::Polygon:
+ pRet = new SvxShapePolyPolygon( pObj );
+ break;
+ case SdrObjKind::PolyLine:
+ pRet = new SvxShapePolyPolygon( pObj );
+ break;
+ case SdrObjKind::SplineLine:
+ case SdrObjKind::PathLine:
+ pRet = new SvxShapePolyPolygon( pObj );
+ break;
+ case SdrObjKind::SplineFill:
+ case SdrObjKind::PathFill:
+ pRet = new SvxShapePolyPolygon( pObj );
+ break;
+ case SdrObjKind::FreehandLine:
+ pRet = new SvxShapePolyPolygon( pObj );
+ break;
+ case SdrObjKind::FreehandFill:
+ pRet = new SvxShapePolyPolygon( pObj );
+ break;
+ case SdrObjKind::Caption:
+ pRet = new SvxShapeCaption( pObj );
+ break;
+ case SdrObjKind::TitleText:
+ case SdrObjKind::OutlineText:
+ case SdrObjKind::Text:
+ pRet = new SvxShapeText( pObj );
+ break;
+ case SdrObjKind::Graphic:
+ pRet = new SvxGraphicObject( pObj );
+ break;
+ case SdrObjKind::OLEPluginFrame:
+ pRet = new SvxFrameShape( pObj );
+ break;
+ case SdrObjKind::OLE2Applet:
+ pRet = new SvxAppletShape( pObj );
+ break;
+ case SdrObjKind::OLE2Plugin:
+ pRet = new SvxPluginShape( pObj );
+ break;
+ case SdrObjKind::OLE2:
+ {
+ if( pObj && !pObj->IsEmptyPresObj() && mpPage )
+ {
+ SdrPage* pSdrPage = mpPage->GetSdrPage();
+ if( pSdrPage )
+ {
+ SdrModel& rSdrModel(pSdrPage->getSdrModelFromSdrPage());
+ ::comphelper::IEmbeddedHelper *pPersist = rSdrModel.GetPersist();
+
+ if( pPersist )
+ {
+ uno::Reference < embed::XEmbeddedObject > xObject = pPersist->getEmbeddedObjectContainer().
+ GetEmbeddedObject( static_cast< SdrOle2Obj* >( pObj )->GetPersistName() );
+
+ // TODO CL->KA: Why is this not working anymore?
+ if( xObject.is() )
+ {
+ SvGlobalName aClassId( xObject->getClassID() );
+
+ const SvGlobalName aAppletClassId( SO3_APPLET_CLASSID );
+ const SvGlobalName aPluginClassId( SO3_PLUGIN_CLASSID );
+ const SvGlobalName aIFrameClassId( SO3_IFRAME_CLASSID );
+
+ if( aPluginClassId == aClassId )
+ {
+ pRet = new SvxPluginShape( pObj );
+ nType = SdrObjKind::OLE2Plugin;
+ }
+ else if( aAppletClassId == aClassId )
+ {
+ pRet = new SvxAppletShape( pObj );
+ nType = SdrObjKind::OLE2Applet;
+ }
+ else if( aIFrameClassId == aClassId )
+ {
+ pRet = new SvxFrameShape( pObj );
+ nType = SdrObjKind::OLEPluginFrame;
+ }
+ }
+ }
+ }
+ }
+ if( pRet == nullptr )
+ {
+ SvxUnoPropertyMapProvider& rSvxMapProvider = getSvxMapProvider();
+ pRet = new SvxOle2Shape( pObj, rSvxMapProvider.GetMap(SVXMAP_OLE2), rSvxMapProvider.GetPropertySet(SVXMAP_OLE2, SdrObject::GetGlobalDrawObjectItemPool()) );
+ }
+ }
+ break;
+ case SdrObjKind::Edge:
+ pRet = new SvxShapeConnector( pObj );
+ break;
+ case SdrObjKind::PathPoly:
+ pRet = new SvxShapePolyPolygon( pObj );
+ break;
+ case SdrObjKind::PathPolyLine:
+ pRet = new SvxShapePolyPolygon( pObj );
+ break;
+ case SdrObjKind::Page:
+ {
+ SvxUnoPropertyMapProvider& rSvxMapProvider = getSvxMapProvider();
+ pRet = new SvxShape( pObj, rSvxMapProvider.GetMap(SVXMAP_PAGE), rSvxMapProvider.GetPropertySet(SVXMAP_PAGE, SdrObject::GetGlobalDrawObjectItemPool()) );
+ }
+ break;
+ case SdrObjKind::Measure:
+ pRet = new SvxShapeDimensioning( pObj );
+ break;
+ case SdrObjKind::UNO:
+ pRet = new SvxShapeControl( pObj );
+ break;
+ case SdrObjKind::CustomShape:
+ pRet = new SvxCustomShape( pObj );
+ break;
+ case SdrObjKind::Media:
+ pRet = new SvxMediaShape( pObj, referer );
+ break;
+ case SdrObjKind::Table:
+ pRet = new SvxTableShape( pObj );
+ break;
+ default: // unknown 2D-object on page
+ assert(false && "Not implemented Starone-Shape created");
+ pRet = new SvxShapeText( pObj );
+ break;
+ }
+ break;
+ }
+ default: // unknown inventor
+ {
+ assert(false && "Unknown Inventor in SvxDrawPage::CreateShape()");
+ break;
+ }
+ }
+
+ if(pRet)
+ {
+ SdrObjKind nObjId = nType;
+
+ switch(nObjId)
+ {
+ case SdrObjKind::CircleCut: // segment of circle
+ case SdrObjKind::CircleArc: // arc of circle
+ case SdrObjKind::CircleSection: // sector
+ nObjId = SdrObjKind::CircleOrEllipse;
+ break;
+
+ case SdrObjKind::TitleText:
+ case SdrObjKind::OutlineText:
+ nObjId = SdrObjKind::Text;
+ break;
+ default: ;
+ }
+
+ pRet->setShapeKind(nObjId);
+ }
+
+ return pRet;
+}
+
+Reference< drawing::XShape > SvxDrawPage::CreateShape( SdrObject *pObj ) const
+{
+ Reference< drawing::XShape > xShape( CreateShapeByTypeAndInventor(pObj->GetObjIdentifier(),
+ pObj->GetObjInventor(),
+ pObj,
+ const_cast<SvxDrawPage*>(this)));
+ return xShape;
+}
+
+SdrObject *SvxDrawPage::CreateSdrObject( const Reference< drawing::XShape > & xShape, bool bBeginning ) noexcept
+{
+ SdrObject* pObj = CreateSdrObject_( xShape );
+ if( pObj)
+ {
+ if ( !pObj->IsInserted() && !pObj->IsDoNotInsertIntoPageAutomatically() )
+ {
+ if(bBeginning)
+ mpPage->InsertObject( pObj, 0 );
+ else
+ mpPage->InsertObject( pObj );
+ }
+ }
+
+ return pObj;
+}
+
+// css::lang::XServiceInfo
+OUString SAL_CALL SvxDrawPage::getImplementationName()
+{
+ return "SvxDrawPage";
+}
+
+sal_Bool SAL_CALL SvxDrawPage::supportsService( const OUString& ServiceName )
+{
+ return cppu::supportsService( this, ServiceName );
+}
+
+uno::Sequence< OUString > SAL_CALL SvxDrawPage::getSupportedServiceNames()
+{
+ uno::Sequence<OUString> aSeq { "com.sun.star.drawing.ShapeCollection" };
+ return aSeq;
+}
+
+rtl::Reference<SvxShape> CreateSvxShapeByTypeAndInventor(SdrObjKind nType, SdrInventor nInventor, OUString const & referer)
+{
+ return SvxDrawPage::CreateShapeByTypeAndInventor( nType, nInventor, nullptr, nullptr, referer );
+}
+
+/** returns a StarOffice API wrapper for the given SdrPage */
+uno::Reference< drawing::XDrawPage > GetXDrawPageForSdrPage( SdrPage* pPage ) noexcept
+{
+ if(pPage)
+ {
+ uno::Reference< drawing::XDrawPage > xDrawPage( pPage->getUnoPage(), uno::UNO_QUERY );
+
+ return xDrawPage;
+ }
+
+ return uno::Reference< drawing::XDrawPage >();
+}
+
+/** returns the SdrObject from the given StarOffice API wrapper */
+SdrPage* GetSdrPageFromXDrawPage( const uno::Reference< drawing::XDrawPage >& xDrawPage ) noexcept
+{
+ if(xDrawPage.is())
+ {
+ SvxDrawPage* pDrawPage = comphelper::getFromUnoTunnel<SvxDrawPage>( xDrawPage );
+
+ if(pDrawPage)
+ {
+ return pDrawPage->GetSdrPage();
+ }
+ }
+
+ return nullptr;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/unodraw/unopool.cxx b/svx/source/unodraw/unopool.cxx
new file mode 100644
index 000000000..59c19c702
--- /dev/null
+++ b/svx/source/unodraw/unopool.cxx
@@ -0,0 +1,375 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * 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/XServiceInfo.hpp>
+#include <com/sun/star/beans/PropertyState.hpp>
+
+#include <comphelper/propertysetinfo.hxx>
+#include <cppuhelper/supportsservice.hxx>
+#include <vcl/svapp.hxx>
+#include <svx/unopool.hxx>
+#include <svx/svdmodel.hxx>
+#include <svx/svdpool.hxx>
+#include <svx/unoprov.hxx>
+#include <svx/unoshprp.hxx>
+#include <svx/xflbstit.hxx>
+#include <svx/xflbmtit.hxx>
+#include <svx/svdetc.hxx>
+#include <editeng/editeng.hxx>
+#include <tools/debug.hxx>
+
+#include <memory>
+
+using namespace ::com::sun::star;
+using namespace ::cppu;
+
+SvxUnoDrawPool::SvxUnoDrawPool(SdrModel* pModel, sal_Int32 nServiceId)
+: PropertySetHelper( SvxPropertySetInfoPool::getOrCreate( nServiceId ) ), mpModel( pModel )
+{
+ init();
+}
+
+/* deprecated */
+SvxUnoDrawPool::SvxUnoDrawPool(SdrModel* pModel)
+: PropertySetHelper( SvxPropertySetInfoPool::getOrCreate( SVXUNO_SERVICEID_COM_SUN_STAR_DRAWING_DEFAULTS ) ), mpModel( pModel )
+{
+ init();
+}
+
+SvxUnoDrawPool::~SvxUnoDrawPool() noexcept
+{
+}
+
+void SvxUnoDrawPool::init()
+{
+ mpDefaultsPool = new SdrItemPool();
+ rtl::Reference<SfxItemPool> pOutlPool = EditEngine::CreatePool();
+ mpDefaultsPool->SetSecondaryPool(pOutlPool.get());
+
+ SdrModel::SetTextDefaults( mpDefaultsPool.get(), SdrEngineDefaults::GetFontHeight() );
+ mpDefaultsPool->SetDefaultMetric(SdrEngineDefaults::GetMapUnit());
+ mpDefaultsPool->FreezeIdRanges();
+}
+
+SfxItemPool* SvxUnoDrawPool::getModelPool( bool bReadOnly ) noexcept
+{
+ if( mpModel )
+ {
+ return &mpModel->GetItemPool();
+ }
+ else
+ {
+ if( bReadOnly )
+ return mpDefaultsPool.get();
+ else
+ return nullptr;
+ }
+}
+
+void SvxUnoDrawPool::getAny( SfxItemPool const * pPool, const comphelper::PropertyMapEntry* pEntry, uno::Any& rValue )
+{
+ switch( pEntry->mnHandle )
+ {
+ case OWN_ATTR_FILLBMP_MODE:
+ {
+ if (pPool->GetDefaultItem(XATTR_FILLBMP_TILE).GetValue())
+ {
+ rValue <<= drawing::BitmapMode_REPEAT;
+ }
+ else if (pPool->GetDefaultItem(XATTR_FILLBMP_STRETCH).GetValue())
+ {
+ rValue <<= drawing::BitmapMode_STRETCH;
+ }
+ else
+ {
+ rValue <<= drawing::BitmapMode_NO_REPEAT;
+ }
+ break;
+ }
+ default:
+ {
+ const MapUnit eMapUnit = pPool->GetMetric(static_cast<sal_uInt16>(pEntry->mnHandle));
+
+ sal_uInt8 nMemberId = pEntry->mnMemberId;
+ if( eMapUnit == MapUnit::Map100thMM )
+ nMemberId &= (~CONVERT_TWIPS);
+
+ // Assure, that ID is a Which-ID (it could be a Slot-ID.)
+ // Thus, convert handle to Which-ID.
+ pPool->GetDefaultItem( pPool->GetWhich( static_cast<sal_uInt16>(pEntry->mnHandle) ) ).QueryValue( rValue, nMemberId );
+ }
+ }
+
+
+ // check for needed metric translation
+ const MapUnit eMapUnit = pPool->GetMetric(static_cast<sal_uInt16>(pEntry->mnHandle));
+ if(pEntry->mnMoreFlags & PropertyMoreFlags::METRIC_ITEM && eMapUnit != MapUnit::Map100thMM)
+ {
+ SvxUnoConvertToMM( eMapUnit, rValue );
+ }
+ // convert int32 to correct enum type if needed
+ else if ( pEntry->maType.getTypeClass() == uno::TypeClass_ENUM && rValue.getValueType() == ::cppu::UnoType<sal_Int32>::get() )
+ {
+ sal_Int32 nEnum;
+ rValue >>= nEnum;
+
+ rValue.setValue( &nEnum, pEntry->maType );
+ }
+}
+
+void SvxUnoDrawPool::putAny( SfxItemPool* pPool, const comphelper::PropertyMapEntry* pEntry, const uno::Any& rValue )
+{
+ uno::Any aValue( rValue );
+
+ const MapUnit eMapUnit = pPool->GetMetric(static_cast<sal_uInt16>(pEntry->mnHandle));
+ if(pEntry->mnMoreFlags & PropertyMoreFlags::METRIC_ITEM && eMapUnit != MapUnit::Map100thMM)
+ {
+ SvxUnoConvertFromMM( eMapUnit, aValue );
+ }
+
+ // Assure, that ID is a Which-ID (it could be a Slot-ID.)
+ // Thus, convert handle to Which-ID.
+ const sal_uInt16 nWhich = pPool->GetWhich( static_cast<sal_uInt16>(pEntry->mnHandle) );
+ switch( nWhich )
+ {
+ case OWN_ATTR_FILLBMP_MODE:
+ do
+ {
+ drawing::BitmapMode eMode;
+ if(!(aValue >>= eMode) )
+ {
+ sal_Int32 nMode = 0;
+ if(!(aValue >>= nMode))
+ throw lang::IllegalArgumentException();
+
+ eMode = static_cast<drawing::BitmapMode>(nMode);
+ }
+
+ pPool->SetPoolDefaultItem( XFillBmpStretchItem( eMode == drawing::BitmapMode_STRETCH ) );
+ pPool->SetPoolDefaultItem( XFillBmpTileItem( eMode == drawing::BitmapMode_REPEAT ) );
+ return;
+ }
+ while(false);
+
+ default:
+ {
+ std::unique_ptr<SfxPoolItem> pNewItem( pPool->GetDefaultItem( nWhich ).Clone() );
+ sal_uInt8 nMemberId = pEntry->mnMemberId;
+ if( pPool->GetMetric(nWhich) == MapUnit::Map100thMM )
+ nMemberId &= (~CONVERT_TWIPS);
+
+ if( !pNewItem->PutValue( aValue, nMemberId ) )
+ throw lang::IllegalArgumentException();
+
+ pPool->SetPoolDefaultItem( *pNewItem );
+ }
+ }
+}
+
+void SvxUnoDrawPool::_setPropertyValues( const comphelper::PropertyMapEntry** ppEntries, const uno::Any* pValues )
+{
+ SolarMutexGuard aGuard;
+
+ SfxItemPool* pPool = getModelPool( false );
+
+ DBG_ASSERT( pPool, "I need a SfxItemPool!" );
+ if( nullptr == pPool )
+ throw beans::UnknownPropertyException( "no pool, no properties..", static_cast<cppu::OWeakObject*>(this));
+
+ while( *ppEntries )
+ putAny( pPool, *ppEntries++, *pValues++ );
+}
+
+void SvxUnoDrawPool::_getPropertyValues( const comphelper::PropertyMapEntry** ppEntries, uno::Any* pValue )
+{
+ SolarMutexGuard aGuard;
+
+ SfxItemPool* pPool = getModelPool( true );
+
+ DBG_ASSERT( pPool, "I need a SfxItemPool!" );
+ if( nullptr == pPool )
+ throw beans::UnknownPropertyException( "no pool, no properties..", static_cast<cppu::OWeakObject*>(this));
+
+ while( *ppEntries )
+ getAny( pPool, *ppEntries++, *pValue++ );
+}
+
+void SvxUnoDrawPool::_getPropertyStates( const comphelper::PropertyMapEntry** ppEntries, beans::PropertyState* pStates )
+{
+ SolarMutexGuard aGuard;
+
+ SfxItemPool* pPool = getModelPool( true );
+
+ if( pPool && pPool != mpDefaultsPool.get() )
+ {
+ while( *ppEntries )
+ {
+ //Assure, that ID is a Which-ID (it could be a Slot-ID.)
+ // Thus, convert handle to Which-ID.
+ const sal_uInt16 nWhich = pPool->GetWhich( static_cast<sal_uInt16>((*ppEntries)->mnHandle) );
+
+ switch( nWhich )
+ {
+ case OWN_ATTR_FILLBMP_MODE:
+ {
+ // use method <IsStaticDefaultItem(..)> instead of using
+ // probably incompatible item pool <mpDefaultPool>.
+ if ( IsStaticDefaultItem( &(pPool->GetDefaultItem( XATTR_FILLBMP_STRETCH )) ) ||
+ IsStaticDefaultItem( &(pPool->GetDefaultItem( XATTR_FILLBMP_TILE )) ) )
+ {
+ *pStates = beans::PropertyState_DEFAULT_VALUE;
+ }
+ else
+ {
+ *pStates = beans::PropertyState_DIRECT_VALUE;
+ }
+ }
+ break;
+ case OWN_ATTR_TEXTCOLUMNS:
+ if (IsStaticDefaultItem(&pPool->GetDefaultItem(sal_uInt16(SDRATTR_TEXTCOLUMNS_NUMBER)))
+ && IsStaticDefaultItem(&pPool->GetDefaultItem(sal_uInt16(SDRATTR_TEXTCOLUMNS_SPACING))))
+ *pStates = beans::PropertyState_DEFAULT_VALUE;
+ else
+ *pStates = beans::PropertyState_DIRECT_VALUE;
+ break;
+ default:
+ //#i18732# - correction:
+ // use method <IsStaticDefaultItem(..)> instead of using probably
+ // incompatible item pool <mpDefaultPool>.
+ const SfxPoolItem& r1 = pPool->GetDefaultItem( nWhich );
+ //const SfxPoolItem& r2 = mpDefaultPool->GetDefaultItem( nWhich );
+
+ if ( IsStaticDefaultItem( &r1 ) )
+ {
+ *pStates = beans::PropertyState_DEFAULT_VALUE;
+ }
+ else
+ {
+ *pStates = beans::PropertyState_DIRECT_VALUE;
+ }
+ }
+
+ pStates++;
+ ppEntries++;
+ }
+ }
+ else
+ {
+ // as long as we have no model, all properties are default
+ while( *ppEntries++ )
+ *pStates++ = beans::PropertyState_DEFAULT_VALUE;
+ return;
+ }
+}
+
+void SvxUnoDrawPool::_setPropertyToDefault( const comphelper::PropertyMapEntry* pEntry )
+{
+ SolarMutexGuard aGuard;
+
+ SfxItemPool* pPool = getModelPool( true );
+
+ // Assure, that ID is a Which-ID (it could be a Slot-ID.)
+ // Thus, convert handle to Which-ID.
+ const sal_uInt16 nWhich = pPool->GetWhich( static_cast<sal_uInt16>(pEntry->mnHandle) );
+ if ( pPool && pPool != mpDefaultsPool.get() )
+ {
+ // use method <ResetPoolDefaultItem(..)> instead of using probably incompatible item pool <mpDefaultsPool>.
+ pPool->ResetPoolDefaultItem( nWhich );
+ }
+}
+
+uno::Any SvxUnoDrawPool::_getPropertyDefault( const comphelper::PropertyMapEntry* pEntry )
+{
+ SolarMutexGuard aGuard;
+ //#i18732# - use method <GetPoolDefaultItem(..)> instead of
+ // using probably incompatible item pool <mpDefaultsPool>
+ uno::Any aAny;
+ SfxItemPool* pPool = getModelPool( true );
+ const sal_uInt16 nWhich = pPool->GetWhich( static_cast<sal_uInt16>(pEntry->mnHandle) );
+ const SfxPoolItem *pItem = pPool->GetPoolDefaultItem ( nWhich );
+ if (pItem)
+ {
+ pItem->QueryValue( aAny, pEntry->mnMemberId );
+ }
+
+ return aAny;
+}
+
+// XInterface
+
+uno::Any SAL_CALL SvxUnoDrawPool::queryInterface( const uno::Type & rType )
+{
+ return OWeakAggObject::queryInterface( rType );
+}
+
+uno::Any SAL_CALL SvxUnoDrawPool::queryAggregation( const uno::Type & rType )
+{
+ uno::Any aAny;
+
+ if( rType == cppu::UnoType<lang::XServiceInfo>::get())
+ aAny <<= uno::Reference< lang::XServiceInfo >(this);
+ else if( rType == cppu::UnoType<lang::XTypeProvider>::get())
+ aAny <<= uno::Reference< lang::XTypeProvider >(this);
+ else if( rType == cppu::UnoType<beans::XPropertySet>::get())
+ aAny <<= uno::Reference< beans::XPropertySet >(this);
+ else if( rType == cppu::UnoType<beans::XPropertyState>::get())
+ aAny <<= uno::Reference< beans::XPropertyState >(this);
+ else if( rType == cppu::UnoType<beans::XMultiPropertySet>::get())
+ aAny <<= uno::Reference< beans::XMultiPropertySet >(this);
+ else
+ aAny = OWeakAggObject::queryAggregation( rType );
+
+ return aAny;
+}
+
+uno::Sequence< uno::Type > SAL_CALL SvxUnoDrawPool::getTypes()
+{
+ static const uno::Sequence aTypes {
+ cppu::UnoType<uno::XAggregation>::get(),
+ cppu::UnoType<lang::XServiceInfo>::get(),
+ cppu::UnoType<lang::XTypeProvider>::get(),
+ cppu::UnoType<beans::XPropertySet>::get(),
+ cppu::UnoType<beans::XPropertyState>::get(),
+ cppu::UnoType<beans::XMultiPropertySet>::get() };
+ return aTypes;
+}
+
+uno::Sequence< sal_Int8 > SAL_CALL SvxUnoDrawPool::getImplementationId()
+{
+ return css::uno::Sequence<sal_Int8>();
+}
+
+// XServiceInfo
+sal_Bool SAL_CALL SvxUnoDrawPool::supportsService( const OUString& ServiceName )
+{
+ return cppu::supportsService(this, ServiceName);
+}
+
+OUString SAL_CALL SvxUnoDrawPool::getImplementationName()
+{
+ return "SvxUnoDrawPool";
+}
+
+uno::Sequence< OUString > SAL_CALL SvxUnoDrawPool::getSupportedServiceNames( )
+{
+ uno::Sequence<OUString> aSNS { "com.sun.star.drawing.Defaults" };
+ return aSNS;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/unodraw/unoprov.cxx b/svx/source/unodraw/unoprov.cxx
new file mode 100644
index 000000000..9936a4df9
--- /dev/null
+++ b/svx/source/unodraw/unoprov.cxx
@@ -0,0 +1,2107 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * 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 <sal/macros.h>
+#include <com/sun/star/table/XTable.hpp>
+#include <com/sun/star/container/XIndexAccess.hpp>
+#include <com/sun/star/container/XNameContainer.hpp>
+#include <com/sun/star/frame/XModel.hpp>
+#include <com/sun/star/embed/XEmbeddedObject.hpp>
+#include <com/sun/star/util/MeasureUnit.hpp>
+#include <com/sun/star/drawing/TextVerticalAdjust.hpp>
+#include <com/sun/star/media/ZoomLevel.hpp>
+#include <com/sun/star/io/XInputStream.hpp>
+#include <com/sun/star/beans/PropertyAttribute.hpp>
+#include <com/sun/star/lang/Locale.hpp>
+#include <tools/fldunit.hxx>
+#include <vcl/svapp.hxx>
+#include <comphelper/propertysetinfo.hxx>
+#include <comphelper/sequence.hxx>
+#include <svx/dialmgr.hxx>
+#include <svx/unoapi.hxx>
+#include <svx/unoshprp.hxx>
+#include <svx/svxids.hrc>
+#include <svx/svdobjkind.hxx>
+#include <svx/strings.hrc>
+#include <o3tl/string_view.hxx>
+#include <strings.hxx>
+
+#include "shapeimpl.hxx"
+#include <unordered_map>
+
+using namespace ::com::sun::star;
+using namespace ::com::sun::star::beans::PropertyAttribute;
+using ::com::sun::star::drawing::TextVerticalAdjust;
+
+static SfxItemPropertyMapEntry const * ImplGetSvxShapePropertyMap()
+{
+ static SfxItemPropertyMapEntry const aShapePropertyMap_Impl[] =
+ {
+ EDGERADIUS_PROPERTIES
+ FILL_PROPERTIES
+ LINE_PROPERTIES
+ LINE_PROPERTIES_START_END
+ SHAPE_DESCRIPTOR_PROPERTIES
+ MISC_OBJ_PROPERTIES
+ LINKTARGET_PROPERTIES
+ GLOW_PROPERTIES
+ SOFTEDGE_PROPERTIES
+ SHADOW_PROPERTIES
+ TEXT_PROPERTIES
+ // #FontWork#
+ FONTWORK_PROPERTIES
+ { u"UserDefinedAttributes", SDRATTR_XMLATTRIBUTES, cppu::UnoType<css::container::XNameContainer>::get(), 0, 0},
+ { u"ParaUserDefinedAttributes", EE_PARA_XMLATTRIBS, cppu::UnoType<css::container::XNameContainer>::get(), 0, 0},
+ { u"", 0, css::uno::Type(), 0, 0 }
+ };
+
+ return aShapePropertyMap_Impl;
+}
+
+static SfxItemPropertyMapEntry const * ImplGetSvxTextShapePropertyMap()
+{
+ static SfxItemPropertyMapEntry const aTextShapePropertyMap_Impl[] =
+ {
+ EDGERADIUS_PROPERTIES
+ FILL_PROPERTIES
+ LINE_PROPERTIES
+ LINE_PROPERTIES_START_END
+ SHAPE_DESCRIPTOR_PROPERTIES
+ MISC_OBJ_PROPERTIES_NO_SHEAR
+ LINKTARGET_PROPERTIES
+ GLOW_PROPERTIES
+ SOFTEDGE_PROPERTIES
+ SHADOW_PROPERTIES
+ TEXT_PROPERTIES
+ // #FontWork#
+ FONTWORK_PROPERTIES
+ { u"UserDefinedAttributes", SDRATTR_XMLATTRIBUTES, cppu::UnoType<css::container::XNameContainer>::get(), 0, 0},
+ { u"ParaUserDefinedAttributes", EE_PARA_XMLATTRIBS, cppu::UnoType<css::container::XNameContainer>::get(), 0, 0},
+ { u"", 0, css::uno::Type(), 0, 0 }
+ };
+
+ return aTextShapePropertyMap_Impl;
+}
+
+static SfxItemPropertyMapEntry const * ImplGetSvxConnectorPropertyMap()
+{
+ static SfxItemPropertyMapEntry const aConnectorPropertyMap_Impl[] =
+ {
+ SPECIAL_CONNECTOR_PROPERTIES
+ EDGERADIUS_PROPERTIES
+ FILL_PROPERTIES
+ LINE_PROPERTIES
+ LINE_PROPERTIES_START_END
+ SHAPE_DESCRIPTOR_PROPERTIES
+ MISC_OBJ_PROPERTIES
+ LINKTARGET_PROPERTIES
+ GLOW_PROPERTIES
+ SOFTEDGE_PROPERTIES
+ SHADOW_PROPERTIES
+ TEXT_PROPERTIES
+ // #FontWork#
+ FONTWORK_PROPERTIES
+ { u"UserDefinedAttributes", SDRATTR_XMLATTRIBUTES, cppu::UnoType<css::container::XNameContainer>::get(), 0, 0},
+ { u"ParaUserDefinedAttributes", EE_PARA_XMLATTRIBS, cppu::UnoType<css::container::XNameContainer>::get(), 0, 0},
+ { u"", 0, css::uno::Type(), 0, 0 }
+ };
+
+ return aConnectorPropertyMap_Impl;
+}
+
+static SfxItemPropertyMapEntry const * ImplGetSvxDimensioningPropertyMap()
+{
+ static SfxItemPropertyMapEntry const aDimensioningPropertyMap_Impl[] =
+ {
+ SPECIAL_DIMENSIONING_PROPERTIES
+ EDGERADIUS_PROPERTIES
+ FILL_PROPERTIES
+ LINE_PROPERTIES
+ LINE_PROPERTIES_START_END
+ SHAPE_DESCRIPTOR_PROPERTIES
+ MISC_OBJ_PROPERTIES
+ LINKTARGET_PROPERTIES
+ GLOW_PROPERTIES
+ SOFTEDGE_PROPERTIES
+ SHADOW_PROPERTIES
+ TEXT_PROPERTIES
+ // #FontWork#
+ FONTWORK_PROPERTIES
+ { u"UserDefinedAttributes", SDRATTR_XMLATTRIBUTES, cppu::UnoType<css::container::XNameContainer>::get(), 0, 0},
+ { u"ParaUserDefinedAttributes", EE_PARA_XMLATTRIBS, cppu::UnoType<css::container::XNameContainer>::get(), 0, 0},
+ { u"", 0, css::uno::Type(), 0, 0 }
+ };
+
+ return aDimensioningPropertyMap_Impl;
+}
+
+static SfxItemPropertyMapEntry const * ImplGetSvxCirclePropertyMap()
+{
+ static SfxItemPropertyMapEntry const aCirclePropertyMap_Impl[] =
+ {
+ SPECIAL_CIRCLE_PROPERTIES
+ EDGERADIUS_PROPERTIES
+ FILL_PROPERTIES
+ LINE_PROPERTIES
+ LINE_PROPERTIES_START_END
+ SHAPE_DESCRIPTOR_PROPERTIES
+ MISC_OBJ_PROPERTIES
+ LINKTARGET_PROPERTIES
+ GLOW_PROPERTIES
+ SOFTEDGE_PROPERTIES
+ SHADOW_PROPERTIES
+ TEXT_PROPERTIES
+ // #FontWork#
+ FONTWORK_PROPERTIES
+ { u"UserDefinedAttributes", SDRATTR_XMLATTRIBUTES, cppu::UnoType<css::container::XNameContainer>::get(), 0, 0},
+ { u"ParaUserDefinedAttributes", EE_PARA_XMLATTRIBS, cppu::UnoType<css::container::XNameContainer>::get(), 0, 0},
+ { u"", 0, css::uno::Type(), 0, 0 }
+ };
+
+ return aCirclePropertyMap_Impl;
+}
+
+static SfxItemPropertyMapEntry const * ImplGetSvxPolyPolygonPropertyMap()
+{
+ static SfxItemPropertyMapEntry const aPolyPolygonPropertyMap_Impl[] =
+ {
+ { u"Geometry", OWN_ATTR_BASE_GEOMETRY, cppu::UnoType<css::drawing::PointSequenceSequence>::get(), 0, 0 },
+ SPECIAL_POLYGON_PROPERTIES
+ SPECIAL_POLYPOLYGON_PROPERTIES
+ SPECIAL_POLYPOLYGONBEZIER_PROPERTIES
+ FILL_PROPERTIES
+ LINE_PROPERTIES
+ LINE_PROPERTIES_START_END
+ SHAPE_DESCRIPTOR_PROPERTIES
+ MISC_OBJ_PROPERTIES
+ LINKTARGET_PROPERTIES
+ GLOW_PROPERTIES
+ SOFTEDGE_PROPERTIES
+ SHADOW_PROPERTIES
+ TEXT_PROPERTIES
+ // #FontWork#
+ FONTWORK_PROPERTIES
+ { u"UserDefinedAttributes", SDRATTR_XMLATTRIBUTES, cppu::UnoType<css::container::XNameContainer>::get(), 0, 0},
+ { u"ParaUserDefinedAttributes", EE_PARA_XMLATTRIBS, cppu::UnoType<css::container::XNameContainer>::get(), 0, 0},
+ { u"", 0, css::uno::Type(), 0, 0 }
+ };
+
+ return aPolyPolygonPropertyMap_Impl;
+}
+
+static SfxItemPropertyMapEntry const * ImplGetSvxGraphicObjectPropertyMap()
+{
+ static SfxItemPropertyMapEntry const aGraphicObjectPropertyMap_Impl[] =
+ {
+ SPECIAL_GRAPHOBJ_PROPERTIES
+
+ // #i25616#
+ FILL_PROPERTIES
+
+ LINE_PROPERTIES
+ LINE_PROPERTIES_START_END
+ SHAPE_DESCRIPTOR_PROPERTIES
+
+ // #i118485# Full properties now, shear included
+ MISC_OBJ_PROPERTIES
+
+ LINKTARGET_PROPERTIES
+ GLOW_PROPERTIES
+ SOFTEDGE_PROPERTIES
+ SHADOW_PROPERTIES
+ TEXT_PROPERTIES
+ // #FontWork#
+ FONTWORK_PROPERTIES
+ { u"IsMirrored", OWN_ATTR_MIRRORED, cppu::UnoType<bool>::get(), 0, 0},
+ { u"UserDefinedAttributes", SDRATTR_XMLATTRIBUTES, cppu::UnoType<css::container::XNameContainer>::get(), 0, 0},
+ { u"ParaUserDefinedAttributes", EE_PARA_XMLATTRIBS, cppu::UnoType<css::container::XNameContainer>::get(), 0, 0},
+ { u"GraphicStream", OWN_ATTR_GRAPHIC_STREAM, cppu::UnoType<css::io::XInputStream>::get(), css::beans::PropertyAttribute::READONLY, 0},
+ { u"", 0, css::uno::Type(), 0, 0 }
+ };
+
+ return aGraphicObjectPropertyMap_Impl;
+}
+
+static SfxItemPropertyMapEntry const * ImplGetSvx3DSceneObjectPropertyMap()
+{
+ static SfxItemPropertyMapEntry const a3DSceneObjectPropertyMap_Impl[] =
+ {
+ SPECIAL_3DSCENEOBJECT_PROPERTIES
+ FILL_PROPERTIES
+ LINE_PROPERTIES
+ LINE_PROPERTIES_START_END
+ SHAPE_DESCRIPTOR_PROPERTIES
+ MISC_OBJ_PROPERTIES
+ LINKTARGET_PROPERTIES
+ SHADOW_PROPERTIES
+ { u"", 0, css::uno::Type(), 0, 0 }
+ };
+
+ return a3DSceneObjectPropertyMap_Impl;
+}
+
+static SfxItemPropertyMapEntry const * ImplGetSvx3DCubeObjectPropertyMap()
+{
+ static SfxItemPropertyMapEntry const a3DCubeObjectPropertyMap_Impl[] =
+ {
+ SPECIAL_3DCUBEOBJECT_PROPERTIES
+ MISC_3D_OBJ_PROPERTIES
+ FILL_PROPERTIES
+ LINE_PROPERTIES
+ LINE_PROPERTIES_START_END
+ SHAPE_DESCRIPTOR_PROPERTIES
+ MISC_OBJ_PROPERTIES
+ LINKTARGET_PROPERTIES
+ SHADOW_PROPERTIES
+ { u"UserDefinedAttributes", SDRATTR_XMLATTRIBUTES, cppu::UnoType<css::container::XNameContainer>::get(), 0, 0},
+ { u"ParaUserDefinedAttributes", EE_PARA_XMLATTRIBS, cppu::UnoType<css::container::XNameContainer>::get(), 0, 0},
+ { u"", 0, css::uno::Type(), 0, 0 }
+ };
+
+ return a3DCubeObjectPropertyMap_Impl;
+}
+
+static SfxItemPropertyMapEntry const * ImplGetSvx3DSphereObjectPropertyMap()
+{
+ static SfxItemPropertyMapEntry const a3DSphereObjectPropertyMap_Impl[] =
+ {
+ SPECIAL_3DSPHEREOBJECT_PROPERTIES
+ MISC_3D_OBJ_PROPERTIES
+ FILL_PROPERTIES
+ LINE_PROPERTIES
+ LINE_PROPERTIES_START_END
+ SHAPE_DESCRIPTOR_PROPERTIES
+ MISC_OBJ_PROPERTIES
+ LINKTARGET_PROPERTIES
+ SHADOW_PROPERTIES
+ { u"UserDefinedAttributes", SDRATTR_XMLATTRIBUTES, cppu::UnoType<css::container::XNameContainer>::get(), 0, 0},
+ { u"ParaUserDefinedAttributes", EE_PARA_XMLATTRIBS, cppu::UnoType<css::container::XNameContainer>::get(), 0, 0},
+ { u"", 0, css::uno::Type(), 0, 0 }
+ };
+ return a3DSphereObjectPropertyMap_Impl;
+}
+
+static SfxItemPropertyMapEntry const * ImplGetSvx3DLatheObjectPropertyMap()
+{
+ static SfxItemPropertyMapEntry const a3DLatheObjectPropertyMap_Impl[] =
+ {
+ SPECIAL_3DLATHEOBJECT_PROPERTIES
+
+ // #107245# New 3D properties which are possible for lathe and extrude 3d objects
+ SPECIAL_3DLATHEANDEXTRUDEOBJ_PROPERTIES
+
+ SPECIAL_3DBACKSCALE_PROPERTIES
+ MISC_3D_OBJ_PROPERTIES
+ FILL_PROPERTIES
+ LINE_PROPERTIES
+ LINE_PROPERTIES_START_END
+ SHAPE_DESCRIPTOR_PROPERTIES
+ MISC_OBJ_PROPERTIES
+ LINKTARGET_PROPERTIES
+ SHADOW_PROPERTIES
+ { u"UserDefinedAttributes", SDRATTR_XMLATTRIBUTES, cppu::UnoType<css::container::XNameContainer>::get(), 0, 0},
+ { u"ParaUserDefinedAttributes", EE_PARA_XMLATTRIBS, cppu::UnoType<css::container::XNameContainer>::get(), 0, 0},
+ { u"", 0, css::uno::Type(), 0, 0 }
+ };
+
+ return a3DLatheObjectPropertyMap_Impl;
+}
+
+static SfxItemPropertyMapEntry const * ImplGetSvx3DExtrudeObjectPropertyMap()
+{
+ static SfxItemPropertyMapEntry const a3DExtrudeObjectPropertyMap_Impl[] =
+ {
+ SPECIAL_3DEXTRUDEOBJECT_PROPERTIES
+
+ // #107245# New 3D properties which are possible for lathe and extrude 3d objects
+ SPECIAL_3DLATHEANDEXTRUDEOBJ_PROPERTIES
+
+ SPECIAL_3DBACKSCALE_PROPERTIES
+ MISC_3D_OBJ_PROPERTIES
+ FILL_PROPERTIES
+ LINE_PROPERTIES
+ LINE_PROPERTIES_START_END
+ SHAPE_DESCRIPTOR_PROPERTIES
+ MISC_OBJ_PROPERTIES
+ LINKTARGET_PROPERTIES
+ SHADOW_PROPERTIES
+ { u"UserDefinedAttributes", SDRATTR_XMLATTRIBUTES, cppu::UnoType<css::container::XNameContainer>::get(), 0, 0},
+ { u"ParaUserDefinedAttributes", EE_PARA_XMLATTRIBS, cppu::UnoType<css::container::XNameContainer>::get(), 0, 0},
+ { u"", 0, css::uno::Type(), 0, 0 }
+ };
+
+ return a3DExtrudeObjectPropertyMap_Impl;
+}
+
+static SfxItemPropertyMapEntry const * ImplGetSvx3DPolygonObjectPropertyMap()
+{
+ static SfxItemPropertyMapEntry const a3DPolygonObjectPropertyMap_Impl[] =
+ {
+ SPECIAL_3DPOLYGONOBJECT_PROPERTIES
+ MISC_3D_OBJ_PROPERTIES
+ FILL_PROPERTIES
+ LINE_PROPERTIES
+ LINE_PROPERTIES_START_END
+ SHAPE_DESCRIPTOR_PROPERTIES
+ MISC_OBJ_PROPERTIES
+ LINKTARGET_PROPERTIES
+ SHADOW_PROPERTIES
+ { u"UserDefinedAttributes", SDRATTR_XMLATTRIBUTES, cppu::UnoType<css::container::XNameContainer>::get(), 0, 0},
+ { u"ParaUserDefinedAttributes", EE_PARA_XMLATTRIBS, cppu::UnoType<css::container::XNameContainer>::get(), 0, 0},
+ { u"", 0, css::uno::Type(), 0, 0 }
+ };
+
+ return a3DPolygonObjectPropertyMap_Impl;
+}
+
+static SfxItemPropertyMapEntry const * ImplGetSvxAllPropertyMap()
+{
+ static SfxItemPropertyMapEntry const aAllPropertyMap_Impl[] =
+ {
+ GLOW_PROPERTIES
+ SOFTEDGE_PROPERTIES
+ SHADOW_PROPERTIES
+ LINE_PROPERTIES
+ LINE_PROPERTIES_START_END
+ FILL_PROPERTIES
+ EDGERADIUS_PROPERTIES
+ TEXT_PROPERTIES
+ // #FontWork#
+ FONTWORK_PROPERTIES
+ SHAPE_DESCRIPTOR_PROPERTIES
+ MISC_OBJ_PROPERTIES
+ LINKTARGET_PROPERTIES
+ SPECIAL_CONNECTOR_PROPERTIES
+ SPECIAL_DIMENSIONING_PROPERTIES
+ SPECIAL_CIRCLE_PROPERTIES
+ SPECIAL_POLYGON_PROPERTIES
+ SPECIAL_POLYPOLYGON_PROPERTIES
+ SPECIAL_POLYPOLYGONBEZIER_PROPERTIES
+ SPECIAL_GRAPHOBJ_PROPERTIES
+ SPECIAL_3DSCENEOBJECT_PROPERTIES
+ MISC_3D_OBJ_PROPERTIES
+ SPECIAL_3DCUBEOBJECT_PROPERTIES
+ SPECIAL_3DSPHEREOBJECT_PROPERTIES
+ SPECIAL_3DLATHEOBJECT_PROPERTIES
+ SPECIAL_3DEXTRUDEOBJECT_PROPERTIES
+
+ // #107245# New 3D properties which are possible for lathe and extrude 3d objects
+ SPECIAL_3DLATHEANDEXTRUDEOBJ_PROPERTIES
+
+ SPECIAL_3DBACKSCALE_PROPERTIES
+ SPECIAL_3DPOLYGONOBJECT_PROPERTIES
+ { u"UserDefinedAttributes", SDRATTR_XMLATTRIBUTES, cppu::UnoType<css::container::XNameContainer>::get(), 0, 0},
+ { u"ParaUserDefinedAttributes", EE_PARA_XMLATTRIBS, cppu::UnoType<css::container::XNameContainer>::get(), 0, 0},
+ { u"", 0, css::uno::Type(), 0, 0 }
+ };
+
+ return aAllPropertyMap_Impl;
+}
+
+static SfxItemPropertyMapEntry const * ImplGetSvxGroupPropertyMap()
+{
+ static SfxItemPropertyMapEntry const aGroupPropertyMap_Impl[] =
+ {
+ SHAPE_DESCRIPTOR_PROPERTIES
+ MISC_OBJ_PROPERTIES
+ LINKTARGET_PROPERTIES
+ { u"", 0, css::uno::Type(), 0, 0 }
+ };
+
+ return aGroupPropertyMap_Impl;
+}
+
+static SfxItemPropertyMapEntry const * ImplGetSvxOle2PropertyMap()
+{
+ static SfxItemPropertyMapEntry const aOle2PropertyMap_Impl[] =
+ {
+ // #i118485# Adding properties for line, fill, text, shadow, fontwork, rotate, shear
+ FILL_PROPERTIES
+ LINE_PROPERTIES
+ LINE_PROPERTIES_START_END
+ SHAPE_DESCRIPTOR_PROPERTIES
+ MISC_OBJ_PROPERTIES
+ LINKTARGET_PROPERTIES
+ GLOW_PROPERTIES
+ SOFTEDGE_PROPERTIES
+ SHADOW_PROPERTIES
+ TEXT_PROPERTIES
+ FONTWORK_PROPERTIES
+
+ { u"ThumbnailGraphic", OWN_ATTR_THUMBNAIL , cppu::UnoType<css::graphic::XGraphic>::get(), 0, 0 },
+ { u"Model", OWN_ATTR_OLEMODEL , cppu::UnoType<css::frame::XModel>::get(), css::beans::PropertyAttribute::READONLY, 0},
+ { u"EmbeddedObject", OWN_ATTR_OLE_EMBEDDED_OBJECT, cppu::UnoType<css::embed::XEmbeddedObject>::get(), css::beans::PropertyAttribute::READONLY, 0},
+ { u"EmbeddedObjectNoNewClient",OWN_ATTR_OLE_EMBEDDED_OBJECT_NONEWCLIENT, cppu::UnoType<css::embed::XEmbeddedObject>::get(), css::beans::PropertyAttribute::READONLY, 0},
+ { u"OriginalSize", OWN_ATTR_OLESIZE , cppu::UnoType<css::awt::Size>::get(), css::beans::PropertyAttribute::READONLY, 0},
+ { u"CLSID", OWN_ATTR_CLSID , cppu::UnoType<OUString>::get(), 0, 0 },
+ { u"IsInternal", OWN_ATTR_INTERNAL_OLE , cppu::UnoType<bool>::get() , css::beans::PropertyAttribute::READONLY, 0},
+ { u"VisibleArea", OWN_ATTR_OLE_VISAREA , cppu::UnoType<css::awt::Rectangle>::get(), 0, 0},
+ { u"Aspect", OWN_ATTR_OLE_ASPECT , cppu::UnoType<sal_Int64>::get(), 0, 0},
+ { UNO_NAME_OLE2_PERSISTNAME, OWN_ATTR_PERSISTNAME , cppu::UnoType<OUString>::get(), 0, 0 },
+ { u"LinkURL", OWN_ATTR_OLE_LINKURL , cppu::UnoType<OUString>::get(), 0, 0 },
+ { UNO_NAME_GRAPHOBJ_GRAPHIC, OWN_ATTR_VALUE_GRAPHIC , cppu::UnoType<css::graphic::XGraphic>::get(), 0, 0},
+ { u"", 0, css::uno::Type(), 0, 0 }
+ };
+
+ return aOle2PropertyMap_Impl;
+}
+
+static SfxItemPropertyMapEntry const * ImplGetSvxPluginPropertyMap()
+{
+ static SfxItemPropertyMapEntry const aPluginPropertyMap_Impl[] =
+ {
+ { u"PluginMimeType", OWN_ATTR_PLUGIN_MIMETYPE , cppu::UnoType<OUString>::get(), 0, 0},
+ { u"PluginURL", OWN_ATTR_PLUGIN_URL , cppu::UnoType<OUString>::get(), 0, 0},
+ { u"PluginCommands", OWN_ATTR_PLUGIN_COMMANDS , cppu::UnoType<css::uno::Sequence< css::beans::PropertyValue >>::get(), 0, 0},
+ { u"Transformation", OWN_ATTR_TRANSFORMATION , cppu::UnoType<css::drawing::HomogenMatrix3>::get(), 0, 0 },
+ { UNO_NAME_MISC_OBJ_ZORDER, OWN_ATTR_ZORDER , cppu::UnoType<sal_Int32>::get(), 0, 0},
+ { UNO_NAME_MISC_OBJ_LAYERID, SDRATTR_LAYERID , cppu::UnoType<sal_Int16>::get(), 0, 0},
+ { UNO_NAME_MISC_OBJ_LAYERNAME,SDRATTR_LAYERNAME , cppu::UnoType<OUString>::get(), 0, 0},
+ { UNO_NAME_LINKDISPLAYBITMAP, OWN_ATTR_LDBITMAP , cppu::UnoType<css::awt::XBitmap>::get(), css::beans::PropertyAttribute::READONLY, 0},
+ { UNO_NAME_LINKDISPLAYNAME, OWN_ATTR_LDNAME , cppu::UnoType<OUString>::get(), css::beans::PropertyAttribute::READONLY, 0},
+ { UNO_NAME_OLE2_METAFILE, OWN_ATTR_METAFILE , cppu::UnoType<css::uno::Sequence<sal_Int8>>::get(), css::beans::PropertyAttribute::READONLY, 0},
+ { u"ThumbnailGraphic", OWN_ATTR_THUMBNAIL , cppu::UnoType<css::graphic::XGraphic>::get(), 0, 0 },
+ { UNO_NAME_MISC_OBJ_MOVEPROTECT, SDRATTR_OBJMOVEPROTECT, cppu::UnoType<bool>::get(),0, 0},
+ { UNO_NAME_MISC_OBJ_SIZEPROTECT, SDRATTR_OBJSIZEPROTECT, cppu::UnoType<bool>::get(),0, 0},
+ { UNO_NAME_OLE2_PERSISTNAME, OWN_ATTR_PERSISTNAME , cppu::UnoType<OUString>::get(), 0, 0 },
+ { u"LinkURL", OWN_ATTR_OLE_LINKURL , cppu::UnoType<OUString>::get(), 0, 0 },
+ { UNO_NAME_MISC_OBJ_BOUNDRECT, OWN_ATTR_BOUNDRECT, cppu::UnoType<css::awt::Rectangle>::get(), css::beans::PropertyAttribute::READONLY, 0},
+ { u"VisibleArea", OWN_ATTR_OLE_VISAREA , cppu::UnoType<css::awt::Rectangle>::get(), 0, 0},
+ { u"UINameSingular", OWN_ATTR_UINAME_SINGULAR , ::cppu::UnoType<OUString>::get(), css::beans::PropertyAttribute::READONLY, 0},
+ // #i68101#
+ { UNO_NAME_MISC_OBJ_TITLE, OWN_ATTR_MISC_OBJ_TITLE , cppu::UnoType<OUString>::get(), 0, 0},
+ { UNO_NAME_MISC_OBJ_DESCRIPTION, OWN_ATTR_MISC_OBJ_DESCRIPTION , cppu::UnoType<OUString>::get(), 0, 0},
+ { u"", 0, css::uno::Type(), 0, 0 }
+ };
+
+ return aPluginPropertyMap_Impl;
+}
+
+static SfxItemPropertyMapEntry const * ImplGetSvxFramePropertyMap()
+{
+ //TODO/LATER: new properties for ScrollingMode and DefaultBorder
+ static SfxItemPropertyMapEntry const aFramePropertyMap_Impl[] =
+ {
+ { u"FrameURL", OWN_ATTR_FRAME_URL , cppu::UnoType<OUString>::get(), 0, 0},
+ { u"FrameName", OWN_ATTR_FRAME_NAME , cppu::UnoType<OUString>::get(), 0, 0},
+ { u"FrameIsAutoScroll", OWN_ATTR_FRAME_ISAUTOSCROLL , cppu::UnoType<bool>::get() , css::beans::PropertyAttribute::MAYBEVOID, 0},
+ { u"FrameIsBorder", OWN_ATTR_FRAME_ISBORDER , cppu::UnoType<bool>::get() , 0, 0},
+ { u"FrameMarginWidth", OWN_ATTR_FRAME_MARGIN_WIDTH , cppu::UnoType<sal_Int32>::get(), 0, 0},
+ { u"FrameMarginHeight", OWN_ATTR_FRAME_MARGIN_HEIGHT, cppu::UnoType<sal_Int32>::get(), 0, 0},
+ { u"Transformation", OWN_ATTR_TRANSFORMATION , cppu::UnoType<css::drawing::HomogenMatrix3>::get(), 0, 0 },
+ { UNO_NAME_MISC_OBJ_ZORDER, OWN_ATTR_ZORDER , cppu::UnoType<sal_Int32>::get(), 0, 0},
+ { UNO_NAME_MISC_OBJ_LAYERID, SDRATTR_LAYERID , cppu::UnoType<sal_Int16>::get(), 0, 0},
+ { UNO_NAME_MISC_OBJ_LAYERNAME,SDRATTR_LAYERNAME , cppu::UnoType<OUString>::get(), 0, 0},
+ { UNO_NAME_LINKDISPLAYBITMAP, OWN_ATTR_LDBITMAP , cppu::UnoType<css::awt::XBitmap>::get(), css::beans::PropertyAttribute::READONLY, 0},
+ { UNO_NAME_LINKDISPLAYNAME, OWN_ATTR_LDNAME , cppu::UnoType<OUString>::get(), css::beans::PropertyAttribute::READONLY, 0},
+ { UNO_NAME_OLE2_METAFILE, OWN_ATTR_METAFILE , cppu::UnoType<css::uno::Sequence<sal_Int8>>::get(), css::beans::PropertyAttribute::READONLY, 0},
+ { u"ThumbnailGraphic", OWN_ATTR_THUMBNAIL , cppu::UnoType<css::graphic::XGraphic>::get(), 0, 0 },
+ { UNO_NAME_MISC_OBJ_MOVEPROTECT, SDRATTR_OBJMOVEPROTECT, cppu::UnoType<bool>::get(),0, 0},
+ { UNO_NAME_MISC_OBJ_SIZEPROTECT, SDRATTR_OBJSIZEPROTECT, cppu::UnoType<bool>::get(),0, 0},
+ { UNO_NAME_OLE2_PERSISTNAME, OWN_ATTR_PERSISTNAME , cppu::UnoType<OUString>::get(), 0, 0 },
+ { u"LinkURL", OWN_ATTR_OLE_LINKURL , cppu::UnoType<OUString>::get(), 0, 0 },
+ { UNO_NAME_MISC_OBJ_BOUNDRECT, OWN_ATTR_BOUNDRECT, cppu::UnoType<css::awt::Rectangle>::get(), css::beans::PropertyAttribute::READONLY, 0},
+ { u"VisibleArea", OWN_ATTR_OLE_VISAREA , cppu::UnoType<css::awt::Rectangle>::get(), 0, 0},
+ { u"UINameSingular", OWN_ATTR_UINAME_SINGULAR , ::cppu::UnoType<OUString>::get(), css::beans::PropertyAttribute::READONLY, 0},
+ // #i68101#
+ { UNO_NAME_MISC_OBJ_TITLE, OWN_ATTR_MISC_OBJ_TITLE , cppu::UnoType<OUString>::get(), 0, 0},
+ { UNO_NAME_MISC_OBJ_DESCRIPTION, OWN_ATTR_MISC_OBJ_DESCRIPTION , cppu::UnoType<OUString>::get(), 0, 0},
+ { u"", 0, css::uno::Type(), 0, 0 }
+ };
+
+ return aFramePropertyMap_Impl;
+}
+
+static SfxItemPropertyMapEntry const * ImplGetSvxAppletPropertyMap()
+{
+ static SfxItemPropertyMapEntry const aAppletPropertyMap_Impl[] =
+ {
+ { u"AppletCodeBase", OWN_ATTR_APPLET_CODEBASE , cppu::UnoType<OUString>::get(), 0, 0},
+ { u"AppletName", OWN_ATTR_APPLET_NAME , cppu::UnoType<OUString>::get(), 0, 0},
+ { u"AppletCode", OWN_ATTR_APPLET_CODE , cppu::UnoType<OUString>::get(), 0, 0},
+ { u"AppletCommands", OWN_ATTR_APPLET_COMMANDS , cppu::UnoType<css::uno::Sequence< css::beans::PropertyValue >>::get(), 0, 0},
+ { u"AppletDocBase", OWN_ATTR_APPLET_DOCBASE , cppu::UnoType<OUString>::get(), 0, 0},
+ { u"AppletIsScript", OWN_ATTR_APPLET_ISSCRIPT , cppu::UnoType<bool>::get(), 0, 0 },
+ { u"Transformation", OWN_ATTR_TRANSFORMATION , cppu::UnoType<css::drawing::HomogenMatrix3>::get(), 0, 0 },
+ { UNO_NAME_MISC_OBJ_ZORDER, OWN_ATTR_ZORDER , cppu::UnoType<sal_Int32>::get(), 0, 0},
+ { UNO_NAME_MISC_OBJ_LAYERID, SDRATTR_LAYERID , cppu::UnoType<sal_Int16>::get(), 0, 0},
+ { UNO_NAME_MISC_OBJ_LAYERNAME,SDRATTR_LAYERNAME , cppu::UnoType<OUString>::get(), 0, 0},
+ { UNO_NAME_LINKDISPLAYBITMAP, OWN_ATTR_LDBITMAP , cppu::UnoType<css::awt::XBitmap>::get(), css::beans::PropertyAttribute::READONLY, 0},
+ { UNO_NAME_LINKDISPLAYNAME, OWN_ATTR_LDNAME , cppu::UnoType<OUString>::get(), css::beans::PropertyAttribute::READONLY, 0},
+ { UNO_NAME_OLE2_METAFILE, OWN_ATTR_METAFILE , cppu::UnoType<css::uno::Sequence<sal_Int8>>::get(), css::beans::PropertyAttribute::READONLY, 0},
+ { u"ThumbnailGraphic", OWN_ATTR_THUMBNAIL , cppu::UnoType<css::graphic::XGraphic>::get(), 0, 0 },
+ { UNO_NAME_MISC_OBJ_MOVEPROTECT, SDRATTR_OBJMOVEPROTECT, cppu::UnoType<bool>::get(),0, 0},
+ { UNO_NAME_MISC_OBJ_SIZEPROTECT, SDRATTR_OBJSIZEPROTECT, cppu::UnoType<bool>::get(),0, 0},
+ { UNO_NAME_OLE2_PERSISTNAME, OWN_ATTR_PERSISTNAME , cppu::UnoType<OUString>::get(), 0, 0 },
+ { u"LinkURL", OWN_ATTR_OLE_LINKURL , cppu::UnoType<OUString>::get(), 0, 0 },
+ { UNO_NAME_MISC_OBJ_BOUNDRECT, OWN_ATTR_BOUNDRECT, cppu::UnoType<css::awt::Rectangle>::get(), css::beans::PropertyAttribute::READONLY, 0},
+ { u"VisibleArea", OWN_ATTR_OLE_VISAREA , cppu::UnoType<css::awt::Rectangle>::get(), 0, 0},
+ { u"UINameSingular", OWN_ATTR_UINAME_SINGULAR , ::cppu::UnoType<OUString>::get(), css::beans::PropertyAttribute::READONLY, 0},
+ // #i68101#
+ { UNO_NAME_MISC_OBJ_TITLE, OWN_ATTR_MISC_OBJ_TITLE , cppu::UnoType<OUString>::get(), 0, 0},
+ { UNO_NAME_MISC_OBJ_DESCRIPTION, OWN_ATTR_MISC_OBJ_DESCRIPTION , cppu::UnoType<OUString>::get(), 0, 0},
+ { u"", 0, css::uno::Type(), 0, 0 }
+ };
+
+ return aAppletPropertyMap_Impl;
+}
+
+static SfxItemPropertyMapEntry const * ImplGetSvxControlShapePropertyMap()
+{
+ static SfxItemPropertyMapEntry const aControlPropertyMap_Impl[] =
+ {
+ // the following properties are mapped to the XControl Model of this shape
+ { UNO_NAME_EDIT_CHAR_FONTNAME, 0, cppu::UnoType<OUString>::get(), 0, 0 },
+ { UNO_NAME_EDIT_CHAR_FONTSTYLENAME, 0, cppu::UnoType<OUString>::get(), 0, 0 },
+ { UNO_NAME_EDIT_CHAR_FONTFAMILY, 0, cppu::UnoType<sal_Int16>::get(), 0, 0 },
+ { UNO_NAME_EDIT_CHAR_FONTCHARSET, 0, cppu::UnoType<sal_Int16>::get(), 0, 0 },
+ { UNO_NAME_EDIT_CHAR_HEIGHT, 0, cppu::UnoType<float>::get(), 0, 0 },
+ { UNO_NAME_EDIT_CHAR_FONTPITCH, 0, cppu::UnoType<sal_Int16>::get(), 0, 0 },
+ { UNO_NAME_EDIT_CHAR_POSTURE, 0, cppu::UnoType<css::awt::FontSlant>::get(),0, 0 },
+ { UNO_NAME_EDIT_CHAR_WEIGHT, 0, cppu::UnoType<float>::get(), 0, 0 },
+ { UNO_NAME_EDIT_CHAR_UNDERLINE, 0, cppu::UnoType<sal_Int16>::get(), 0, 0 },
+ { UNO_NAME_EDIT_CHAR_STRIKEOUT, 0, cppu::UnoType<sal_Int16>::get(), 0, 0},
+ { UNO_NAME_EDIT_CHAR_CASEMAP, 0, cppu::UnoType<sal_Int16>::get(), 0, 0 },
+ { UNO_NAME_EDIT_CHAR_COLOR, 0, cppu::UnoType<sal_Int32>::get(), 0, MID_COLOR_RGB },
+ { UNO_NAME_EDIT_CHAR_COLOR_THEME, 0, cppu::UnoType<sal_Int16>::get(), 0, MID_COLOR_THEME_INDEX },
+ { UNO_NAME_EDIT_CHAR_COLOR_TINT_OR_SHADE, 0, cppu::UnoType<sal_Int16>::get(), 0, MID_COLOR_TINT_OR_SHADE },
+ { u"CharBackColor", 0, cppu::UnoType<sal_Int32>::get(), 0, 0 },
+ { u"CharBackTransparent", 0, cppu::UnoType<bool>::get(), 0, 0 },
+ { u"CharRelief", 0, cppu::UnoType<sal_Int16>::get(), 0, 0 },
+ { u"CharUnderlineColor", 0, cppu::UnoType<sal_Int32>::get(), 0, 0 },
+ { u"CharKerning", 0, cppu::UnoType<sal_Int16>::get(), 0, 0 },
+ { u"CharWordMode", 0, cppu::UnoType<bool>::get(), 0, 0 },
+ { UNO_NAME_EDIT_PARA_ADJUST, 0, cppu::UnoType<sal_Int16>::get(), 0, 0 },
+ { u"TextVerticalAdjust", 0, cppu::UnoType<TextVerticalAdjust>::get(), MAYBEVOID, 0 },
+ { u"ControlBackground", 0, cppu::UnoType<sal_Int32>::get(), 0, 0 },
+ { u"ControlBorder", 0, cppu::UnoType<sal_Int16>::get(), 0, 0 },
+ { u"ControlBorderColor", 0, cppu::UnoType<sal_Int32>::get(), 0, 0 },
+ { u"ControlSymbolColor", 0, cppu::UnoType<sal_Int16>::get(), 0, 0 },
+ { u"ImageScaleMode", 0, cppu::UnoType<sal_Int16>::get(), 0, 0 },
+ { UNO_NAME_MISC_OBJ_MOVEPROTECT, SDRATTR_OBJMOVEPROTECT, cppu::UnoType<bool>::get(),0, 0},
+ { UNO_NAME_MISC_OBJ_SIZEPROTECT, SDRATTR_OBJSIZEPROTECT, cppu::UnoType<bool>::get(),0, 0},
+ { u"ControlTextEmphasis", 0, cppu::UnoType<sal_Int16>::get(), 0, 0 },
+ { u"ControlWritingMode", 0, cppu::UnoType< sal_Int16 >::get(), 0, 0},
+ // the following properties are handled by SvxShape
+ { u"Transformation", OWN_ATTR_TRANSFORMATION , cppu::UnoType<css::drawing::HomogenMatrix3>::get(), 0, 0 },
+ { UNO_NAME_MISC_OBJ_ZORDER, OWN_ATTR_ZORDER , cppu::UnoType<sal_Int32>::get(), 0, 0},
+ { UNO_NAME_MISC_OBJ_LAYERID, SDRATTR_LAYERID , cppu::UnoType<sal_Int16>::get(), 0, 0},
+ { UNO_NAME_MISC_OBJ_LAYERNAME,SDRATTR_LAYERNAME , cppu::UnoType<OUString>::get(), 0, 0},
+ { UNO_NAME_LINKDISPLAYBITMAP, OWN_ATTR_LDBITMAP , cppu::UnoType<css::awt::XBitmap>::get(), css::beans::PropertyAttribute::READONLY, 0},
+ { UNO_NAME_LINKDISPLAYNAME, OWN_ATTR_LDNAME , cppu::UnoType<OUString>::get(), css::beans::PropertyAttribute::READONLY, 0},
+ { u"UserDefinedAttributes", SDRATTR_XMLATTRIBUTES, cppu::UnoType<css::container::XNameContainer>::get(), 0, 0},
+ {u"ParaUserDefinedAttributes", EE_PARA_XMLATTRIBS, cppu::UnoType<css::container::XNameContainer>::get(), 0, 0},
+ { UNO_NAME_MISC_OBJ_BOUNDRECT, OWN_ATTR_BOUNDRECT, cppu::UnoType<css::awt::Rectangle>::get(), css::beans::PropertyAttribute::READONLY, 0},
+ { u"UINameSingular", OWN_ATTR_UINAME_SINGULAR , ::cppu::UnoType<OUString>::get(), css::beans::PropertyAttribute::READONLY, 0},
+ // #i68101#
+ { UNO_NAME_MISC_OBJ_TITLE, OWN_ATTR_MISC_OBJ_TITLE , cppu::UnoType<OUString>::get(), 0, 0},
+ { UNO_NAME_MISC_OBJ_DESCRIPTION, OWN_ATTR_MISC_OBJ_DESCRIPTION , cppu::UnoType<OUString>::get(), 0, 0},
+ // #i112587#
+ { UNO_NAME_MISC_OBJ_PRINTABLE, SDRATTR_OBJPRINTABLE , cppu::UnoType<bool>::get(), 0, 0},
+ { u"Visible", SDRATTR_OBJVISIBLE , cppu::UnoType<bool>::get(), 0, 0},
+ { UNO_NAME_MISC_OBJ_INTEROPGRABBAG, OWN_ATTR_INTEROPGRABBAG, cppu::UnoType<css::uno::Sequence< css::beans::PropertyValue >>::get(), 0, 0},
+ { u"", 0, css::uno::Type(), 0, 0 }
+ };
+
+ return aControlPropertyMap_Impl;
+}
+
+static SfxItemPropertyMapEntry const * ImplGetSvxPageShapePropertyMap()
+{
+ static SfxItemPropertyMapEntry const aPageShapePropertyMap_Impl[] =
+ {
+ { u"PageNumber", OWN_ATTR_PAGE_NUMBER , cppu::UnoType<sal_Int32>::get(), 0, 0},
+ { u"Transformation", OWN_ATTR_TRANSFORMATION , cppu::UnoType<css::drawing::HomogenMatrix3>::get(), 0, 0 },
+ { UNO_NAME_MISC_OBJ_ZORDER, OWN_ATTR_ZORDER , cppu::UnoType<sal_Int32>::get(), 0, 0},
+ { UNO_NAME_MISC_OBJ_LAYERID, SDRATTR_LAYERID , cppu::UnoType<sal_Int16>::get(), 0, 0},
+ { UNO_NAME_MISC_OBJ_LAYERNAME,SDRATTR_LAYERNAME , cppu::UnoType<OUString>::get(), 0, 0},
+ { UNO_NAME_LINKDISPLAYBITMAP, OWN_ATTR_LDBITMAP , cppu::UnoType<css::awt::XBitmap>::get(), css::beans::PropertyAttribute::READONLY, 0},
+ { UNO_NAME_LINKDISPLAYNAME, OWN_ATTR_LDNAME , cppu::UnoType<OUString>::get(), css::beans::PropertyAttribute::READONLY, 0},
+ { UNO_NAME_MISC_OBJ_MOVEPROTECT, SDRATTR_OBJMOVEPROTECT, cppu::UnoType<bool>::get(),0, 0},
+ { UNO_NAME_MISC_OBJ_SIZEPROTECT, SDRATTR_OBJSIZEPROTECT, cppu::UnoType<bool>::get(),0, 0},
+ { UNO_NAME_MISC_OBJ_BOUNDRECT, OWN_ATTR_BOUNDRECT, cppu::UnoType<css::awt::Rectangle>::get(), css::beans::PropertyAttribute::READONLY, 0},
+ // #i68101#
+ { UNO_NAME_MISC_OBJ_TITLE, OWN_ATTR_MISC_OBJ_TITLE , cppu::UnoType<OUString>::get(), 0, 0},
+ { UNO_NAME_MISC_OBJ_DESCRIPTION, OWN_ATTR_MISC_OBJ_DESCRIPTION , cppu::UnoType<OUString>::get(), 0, 0},
+ { u"", 0, css::uno::Type(), 0, 0 }
+ };
+
+ return aPageShapePropertyMap_Impl;
+}
+
+static SfxItemPropertyMapEntry const * ImplGetSvxCaptionPropertyMap()
+{
+ static SfxItemPropertyMapEntry const aCaptionPropertyMap_Impl[] =
+ {
+ { u"CaptionPoint", OWN_ATTR_CAPTION_POINT, cppu::UnoType<css::awt::Point>::get(), 0, 0 },
+ { u"CaptionType", SDRATTR_CAPTIONTYPE, cppu::UnoType<sal_Int16>::get(), 0, 0},
+ { u"CaptionIsFixedAngle", SDRATTR_CAPTIONFIXEDANGLE, cppu::UnoType<bool>::get(), 0, 0},
+ { u"CaptionAngle", SDRATTR_CAPTIONANGLE, cppu::UnoType<sal_Int32>::get(), 0, 0},
+ { u"CaptionGap", SDRATTR_CAPTIONGAP, cppu::UnoType<sal_Int32>::get(), 0, 0, PropertyMoreFlags::METRIC_ITEM},
+ { u"CaptionEscapeDirection", SDRATTR_CAPTIONESCDIR, cppu::UnoType<sal_Int32>::get(), 0, 0},
+ { u"CaptionIsEscapeRelative", SDRATTR_CAPTIONESCISREL, cppu::UnoType<bool>::get(), 0, 0},
+ { u"CaptionEscapeRelative", SDRATTR_CAPTIONESCREL, cppu::UnoType<sal_Int32>::get(), 0, 0},
+ { u"CaptionEscapeAbsolute", SDRATTR_CAPTIONESCABS, cppu::UnoType<sal_Int32>::get(), 0, 0, PropertyMoreFlags::METRIC_ITEM},
+ { u"CaptionLineLength", SDRATTR_CAPTIONLINELEN, cppu::UnoType<sal_Int32>::get(), 0, 0, PropertyMoreFlags::METRIC_ITEM},
+ { u"CaptionIsFitLineLength", SDRATTR_CAPTIONFITLINELEN, cppu::UnoType<bool>::get(), 0, 0},
+ EDGERADIUS_PROPERTIES
+ FILL_PROPERTIES
+ LINE_PROPERTIES
+ LINE_PROPERTIES_START_END
+ SHAPE_DESCRIPTOR_PROPERTIES
+ MISC_OBJ_PROPERTIES
+ LINKTARGET_PROPERTIES
+ SHADOW_PROPERTIES
+ TEXT_PROPERTIES
+ // #FontWork#
+ FONTWORK_PROPERTIES
+ { u"UserDefinedAttributes", SDRATTR_XMLATTRIBUTES, cppu::UnoType<css::container::XNameContainer>::get(), 0, 0},
+ {u"ParaUserDefinedAttributes", EE_PARA_XMLATTRIBS, cppu::UnoType<css::container::XNameContainer>::get(), 0, 0},
+ { u"", 0, css::uno::Type(), 0, 0 }
+ };
+
+ return aCaptionPropertyMap_Impl;
+}
+
+static SfxItemPropertyMapEntry const * ImplGetSvxCustomShapePropertyMap()
+{
+ static SfxItemPropertyMapEntry const aCustomShapePropertyMap_Impl[] =
+ {
+ { u"CustomShapeEngine", SDRATTR_CUSTOMSHAPE_ENGINE, cppu::UnoType<OUString>::get(), 0, 0 },
+ { u"CustomShapeData", SDRATTR_CUSTOMSHAPE_DATA, cppu::UnoType<OUString>::get(), 0, 0 },
+ { u"CustomShapeGeometry", SDRATTR_CUSTOMSHAPE_GEOMETRY,
+ cppu::UnoType<css::uno::Sequence< css::beans::PropertyValue >>::get(), 0, 0 },
+ FILL_PROPERTIES
+ LINE_PROPERTIES
+ LINE_PROPERTIES_START_END
+ SHAPE_DESCRIPTOR_PROPERTIES
+ MISC_OBJ_PROPERTIES
+ LINKTARGET_PROPERTIES
+ GLOW_PROPERTIES
+ SOFTEDGE_PROPERTIES
+ SHADOW_PROPERTIES
+ TEXT_PROPERTIES
+ {u"UserDefinedAttributes", SDRATTR_XMLATTRIBUTES, cppu::UnoType<css::container::XNameContainer>::get(), 0, 0},
+ {u"ParaUserDefinedAttributes", EE_PARA_XMLATTRIBS, cppu::UnoType<css::container::XNameContainer>::get(), 0, 0},
+ { u"", 0, css::uno::Type(), 0, 0 }
+ };
+ return aCustomShapePropertyMap_Impl;
+}
+
+static SfxItemPropertyMapEntry const * ImplGetSvxMediaShapePropertyMap()
+{
+ static SfxItemPropertyMapEntry const aMediaShapePropertyMap_Impl[] =
+ {
+ { UNO_NAME_MISC_OBJ_ZORDER, OWN_ATTR_ZORDER, cppu::UnoType<sal_Int32>::get(), 0, 0},
+ { UNO_NAME_MISC_OBJ_LAYERID, SDRATTR_LAYERID, cppu::UnoType<sal_Int16>::get(), 0, 0},
+ { UNO_NAME_MISC_OBJ_LAYERNAME, SDRATTR_LAYERNAME, cppu::UnoType<OUString>::get(), 0, 0},
+ { UNO_NAME_LINKDISPLAYBITMAP, OWN_ATTR_LDBITMAP, cppu::UnoType<css::awt::XBitmap>::get(), css::beans::PropertyAttribute::READONLY, 0},
+ { UNO_NAME_LINKDISPLAYNAME, OWN_ATTR_LDNAME, cppu::UnoType<OUString>::get(), css::beans::PropertyAttribute::READONLY, 0},
+ { u"Transformation", OWN_ATTR_TRANSFORMATION, cppu::UnoType<css::drawing::HomogenMatrix3>::get(), 0, 0 },
+ { u"MediaURL", OWN_ATTR_MEDIA_URL, cppu::UnoType<OUString>::get(), 0, 0},
+ { u"PreferredSize", OWN_ATTR_MEDIA_PREFERREDSIZE, cppu::UnoType<css::awt::Size>::get(), css::beans::PropertyAttribute::READONLY, 0},
+ { u"Loop", OWN_ATTR_MEDIA_LOOP, cppu::UnoType<sal_Bool>::get(), 0, 0},
+ { u"Mute", OWN_ATTR_MEDIA_MUTE, cppu::UnoType<sal_Bool>::get(), 0, 0},
+ { u"VolumeDB", OWN_ATTR_MEDIA_VOLUMEDB, cppu::UnoType<sal_Int16>::get(), 0, 0},
+ { u"Zoom", OWN_ATTR_MEDIA_ZOOM, cppu::UnoType<css::media::ZoomLevel>::get(), 0, 0},
+ { UNO_NAME_MISC_OBJ_MOVEPROTECT, SDRATTR_OBJMOVEPROTECT, cppu::UnoType<bool>::get(),0, 0},
+ { UNO_NAME_MISC_OBJ_SIZEPROTECT, SDRATTR_OBJSIZEPROTECT, cppu::UnoType<bool>::get(),0, 0},
+ { UNO_NAME_MISC_OBJ_BOUNDRECT, OWN_ATTR_BOUNDRECT, cppu::UnoType<css::awt::Rectangle>::get(), css::beans::PropertyAttribute::READONLY, 0},
+ { u"UINameSingular", OWN_ATTR_UINAME_SINGULAR , ::cppu::UnoType<OUString>::get(), css::beans::PropertyAttribute::READONLY, 0},
+ // #i68101#
+ { UNO_NAME_MISC_OBJ_TITLE, OWN_ATTR_MISC_OBJ_TITLE , cppu::UnoType<OUString>::get(), 0, 0},
+ { UNO_NAME_MISC_OBJ_DESCRIPTION, OWN_ATTR_MISC_OBJ_DESCRIPTION , cppu::UnoType<OUString>::get(), 0, 0},
+ {u"PrivateStream", OWN_ATTR_MEDIA_STREAM, cppu::UnoType<css::io::XInputStream>::get(), 0, 0},
+ {u"PrivateTempFileURL", OWN_ATTR_MEDIA_TEMPFILEURL, cppu::UnoType<OUString>::get(), css::beans::PropertyAttribute::READONLY, 0},
+ { u"MediaMimeType", OWN_ATTR_MEDIA_MIMETYPE, cppu::UnoType<OUString>::get(), 0, 0},
+ { u"FallbackGraphic", OWN_ATTR_FALLBACK_GRAPHIC, cppu::UnoType<css::graphic::XGraphic>::get(), css::beans::PropertyAttribute::READONLY, 0},
+ { UNO_NAME_GRAPHOBJ_GRAPHIC, OWN_ATTR_VALUE_GRAPHIC , cppu::UnoType<css::graphic::XGraphic>::get(), 0, 0},
+ { UNO_NAME_GRAPHIC_GRAPHICCROP, SDRATTR_GRAFCROP , cppu::UnoType<css::text::GraphicCrop>::get(), 0, 0},
+ { u"", 0, css::uno::Type(), 0, 0 }
+ };
+
+ return aMediaShapePropertyMap_Impl;
+}
+
+static SfxItemPropertyMapEntry const * ImplGetSvxTableShapePropertyMap()
+{
+ static SfxItemPropertyMapEntry const aTableShapePropertyMap_Impl[] =
+ {
+ SHADOW_PROPERTIES
+ { UNO_NAME_MISC_OBJ_ZORDER, OWN_ATTR_ZORDER, cppu::UnoType<sal_Int32>::get(), 0, 0},
+ { UNO_NAME_MISC_OBJ_LAYERID, SDRATTR_LAYERID, cppu::UnoType<sal_Int16>::get(), 0, 0},
+ { UNO_NAME_MISC_OBJ_LAYERNAME, SDRATTR_LAYERNAME, cppu::UnoType<OUString>::get(), 0, 0},
+ { UNO_NAME_LINKDISPLAYBITMAP, OWN_ATTR_LDBITMAP, cppu::UnoType<css::awt::XBitmap>::get(), css::beans::PropertyAttribute::READONLY, 0},
+ { UNO_NAME_LINKDISPLAYNAME, OWN_ATTR_LDNAME, cppu::UnoType<OUString>::get(), css::beans::PropertyAttribute::READONLY, 0},
+ { u"Transformation", OWN_ATTR_TRANSFORMATION, cppu::UnoType<css::drawing::HomogenMatrix3>::get(), 0, 0 },
+ { UNO_NAME_MISC_OBJ_MOVEPROTECT, SDRATTR_OBJMOVEPROTECT, cppu::UnoType<bool>::get(),0, 0},
+ { UNO_NAME_MISC_OBJ_SIZEPROTECT, SDRATTR_OBJSIZEPROTECT, cppu::UnoType<bool>::get(),0, 0},
+ { UNO_NAME_MISC_OBJ_BOUNDRECT, OWN_ATTR_BOUNDRECT, cppu::UnoType<css::awt::Rectangle>::get(), css::beans::PropertyAttribute::READONLY, 0},
+ { UNO_NAME_MISC_OBJ_NAME, SDRATTR_OBJECTNAME, cppu::UnoType<OUString>::get(), 0, 0},
+ { u"UINameSingular", OWN_ATTR_UINAME_SINGULAR , ::cppu::UnoType<OUString>::get(), css::beans::PropertyAttribute::READONLY, 0},
+ { UNO_NAME_MISC_OBJ_TITLE, OWN_ATTR_MISC_OBJ_TITLE , cppu::UnoType<OUString>::get(), 0, 0},
+ { UNO_NAME_MISC_OBJ_DESCRIPTION, OWN_ATTR_MISC_OBJ_DESCRIPTION , cppu::UnoType<OUString>::get(), 0, 0},
+ { u"Model", OWN_ATTR_OLEMODEL , cppu::UnoType<css::table::XTable>::get(), css::beans::PropertyAttribute::READONLY, 0},
+ { u"TableTemplate", OWN_ATTR_TABLETEMPLATE , cppu::UnoType<css::container::XIndexAccess>::get(), 0, 0},
+ { u"UseFirstRowStyle", OWN_ATTR_TABLETEMPLATE_FIRSTROW, cppu::UnoType<bool>::get(),0, 0},
+ { u"UseLastRowStyle", OWN_ATTR_TABLETEMPLATE_LASTROW, cppu::UnoType<bool>::get(),0, 0},
+ { u"UseFirstColumnStyle", OWN_ATTR_TABLETEMPLATE_FIRSTCOLUMN, cppu::UnoType<bool>::get(),0, 0},
+ { u"UseLastColumnStyle", OWN_ATTR_TABLETEMPLATE_LASTCOLUMN, cppu::UnoType<bool>::get(),0, 0},
+ { u"UseBandingRowStyle", OWN_ATTR_TABLETEMPLATE_BANDINGROWS, cppu::UnoType<bool>::get(),0, 0},
+ { u"UseBandingColumnStyle", OWN_ATTR_TABLETEMPLATE_BANDINGCOLUMNS, cppu::UnoType<bool>::get(),0, 0},
+ { u"ReplacementGraphic", OWN_ATTR_REPLACEMENT_GRAPHIC, cppu::UnoType<css::graphic::XGraphic>::get(), css::beans::PropertyAttribute::READONLY, 0},
+ { u"", 0, css::uno::Type(), 0, 0 }
+ };
+
+ return aTableShapePropertyMap_Impl;
+}
+
+static o3tl::span<comphelper::PropertyMapEntry const> ImplGetSvxDrawingDefaultsPropertyMap()
+{
+ static comphelper::PropertyMapEntry const aSvxDrawingDefaultsPropertyMap_Impl[] =
+ {
+ GLOW_PROPERTIES
+ SOFTEDGE_PROPERTIES
+ SHADOW_PROPERTIES
+ LINE_PROPERTIES_DEFAULTS
+ FILL_PROPERTIES_BMP
+ FILL_PROPERTIES_DEFAULTS
+ EDGERADIUS_PROPERTIES
+ TEXT_PROPERTIES_DEFAULTS
+ CONNECTOR_PROPERTIES
+ SPECIAL_DIMENSIONING_PROPERTIES_DEFAULTS
+ MISC_3D_OBJ_PROPERTIES
+ SPECIAL_3DBACKSCALE_PROPERTIES
+ };
+
+ return aSvxDrawingDefaultsPropertyMap_Impl;
+}
+
+static o3tl::span<comphelper::PropertyMapEntry const> ImplGetAdditionalWriterDrawingDefaultsPropertyMap()
+{
+ static comphelper::PropertyMapEntry const aSvxAdditionalDefaultsPropertyMap_Impl[] =
+ {
+ { "IsFollowingTextFlow", SID_SW_FOLLOW_TEXT_FLOW, cppu::UnoType<bool>::get(), 0, 0},
+ };
+
+ return aSvxAdditionalDefaultsPropertyMap_Impl;
+}
+
+typedef std::unordered_map< OUString, SdrObjKind > UHashMapImpl;
+
+namespace {
+
+const UHashMapImpl& GetUHashImpl()
+{
+ static UHashMapImpl const aImpl
+ {
+ { "com.sun.star.drawing.RectangleShape", SdrObjKind::Rectangle },
+ { "com.sun.star.drawing.EllipseShape", SdrObjKind::CircleOrEllipse },
+ { "com.sun.star.drawing.ControlShape", SdrObjKind::UNO },
+ { "com.sun.star.drawing.ConnectorShape", SdrObjKind::Edge },
+ { "com.sun.star.drawing.MeasureShape", SdrObjKind::Measure },
+ { "com.sun.star.drawing.LineShape", SdrObjKind::Line },
+ { "com.sun.star.drawing.PolyPolygonShape", SdrObjKind::Polygon },
+ { "com.sun.star.drawing.PolyLineShape", SdrObjKind::PolyLine },
+ { "com.sun.star.drawing.OpenBezierShape", SdrObjKind::PathLine },
+ { "com.sun.star.drawing.ClosedBezierShape", SdrObjKind::PathFill },
+ { "com.sun.star.drawing.OpenFreeHandShape", SdrObjKind::FreehandLine },
+ { "com.sun.star.drawing.ClosedFreeHandShape", SdrObjKind::FreehandFill },
+ { "com.sun.star.drawing.PolyPolygonPathShape", SdrObjKind::PathPoly },
+ { "com.sun.star.drawing.PolyLinePathShape", SdrObjKind::PathPolyLine },
+ { "com.sun.star.drawing.GraphicObjectShape", SdrObjKind::Graphic },
+ { "com.sun.star.drawing.GroupShape", SdrObjKind::Group },
+ { "com.sun.star.drawing.TextShape", SdrObjKind::Text },
+ { "com.sun.star.drawing.OLE2Shape", SdrObjKind::OLE2 },
+ { "com.sun.star.drawing.PageShape", SdrObjKind::Page },
+ { "com.sun.star.drawing.CaptionShape", SdrObjKind::Caption },
+ { "com.sun.star.drawing.FrameShape", SdrObjKind::OLEPluginFrame },
+ { "com.sun.star.drawing.PluginShape", SdrObjKind::OLE2Plugin },
+ { "com.sun.star.drawing.AppletShape", SdrObjKind::OLE2Applet },
+ { "com.sun.star.drawing.CustomShape", SdrObjKind::CustomShape },
+ { "com.sun.star.drawing.MediaShape", SdrObjKind::Media },
+
+ { "com.sun.star.drawing.Shape3DSceneObject", SdrObjKind::E3D_Scene },
+ { "com.sun.star.drawing.Shape3DCubeObject", SdrObjKind::E3D_Cube },
+ { "com.sun.star.drawing.Shape3DSphereObject", SdrObjKind::E3D_Sphere },
+ { "com.sun.star.drawing.Shape3DLatheObject", SdrObjKind::E3D_Lathe },
+ { "com.sun.star.drawing.Shape3DExtrudeObject", SdrObjKind::E3D_Extrusion },
+ { "com.sun.star.drawing.Shape3DPolygonObject", SdrObjKind::E3D_Polygon },
+ };
+
+ return aImpl;
+}
+
+}
+
+
+OUString UHashMap::getNameFromId(SdrObjKind nId)
+{
+ const UHashMapImpl &rMap = GetUHashImpl();
+
+ auto it = std::find_if(rMap.begin(), rMap.end(),
+ [nId](const UHashMapImpl::value_type& rEntry) { return rEntry.second == nId; });
+ if (it != rMap.end())
+ return it->first;
+ OSL_FAIL("[CL] unknown SdrObject identifier");
+ return OUString();
+}
+
+uno::Sequence< OUString > UHashMap::getServiceNames()
+{
+ return comphelper::mapKeysToSequence( GetUHashImpl() );
+}
+
+std::optional<SdrObjKind> UHashMap::getId( const OUString& rCompareString )
+{
+ const UHashMapImpl &rMap = GetUHashImpl();
+ UHashMapImpl::const_iterator it = rMap.find( rCompareString );
+ if( it == rMap.end() )
+ return {};
+ else
+ return it->second;
+}
+
+SvxUnoPropertyMapProvider& getSvxMapProvider()
+{
+ static SvxUnoPropertyMapProvider theSvxMapProvider;
+ return theSvxMapProvider;
+}
+
+
+SvxUnoPropertyMapProvider::SvxUnoPropertyMapProvider()
+{
+ for(sal_uInt16 i=0;i<SVXMAP_END; i++)
+ {
+ aSetArr[i] = nullptr;
+ aMapArr[i] = nullptr;
+ }
+}
+
+SvxUnoPropertyMapProvider::~SvxUnoPropertyMapProvider()
+{
+}
+
+
+const SfxItemPropertyMapEntry* SvxUnoPropertyMapProvider::GetMap(sal_uInt16 nPropertyId)
+{
+ assert(nPropertyId < SVXMAP_END);
+ if(!aMapArr[nPropertyId]) {
+ switch(nPropertyId) {
+ case SVXMAP_SHAPE: aMapArr[SVXMAP_SHAPE]=ImplGetSvxShapePropertyMap(); break;
+ case SVXMAP_CONNECTOR: aMapArr[SVXMAP_CONNECTOR]=ImplGetSvxConnectorPropertyMap(); break;
+ case SVXMAP_DIMENSIONING: aMapArr[SVXMAP_DIMENSIONING]=ImplGetSvxDimensioningPropertyMap(); break;
+ case SVXMAP_CIRCLE: aMapArr[SVXMAP_CIRCLE]=ImplGetSvxCirclePropertyMap(); break;
+ case SVXMAP_POLYPOLYGON: aMapArr[SVXMAP_POLYPOLYGON]=ImplGetSvxPolyPolygonPropertyMap(); break;
+ case SVXMAP_GRAPHICOBJECT: aMapArr[SVXMAP_GRAPHICOBJECT]=ImplGetSvxGraphicObjectPropertyMap(); break;
+ case SVXMAP_3DSCENEOBJECT: aMapArr[SVXMAP_3DSCENEOBJECT]=ImplGetSvx3DSceneObjectPropertyMap(); break;
+ case SVXMAP_3DCUBEOBJECT: aMapArr[SVXMAP_3DCUBEOBJECT]=ImplGetSvx3DCubeObjectPropertyMap(); break;
+ case SVXMAP_3DSPHEREOBJECT: aMapArr[SVXMAP_3DSPHEREOBJECT]=ImplGetSvx3DSphereObjectPropertyMap(); break;
+ case SVXMAP_3DLATHEOBJECT: aMapArr[SVXMAP_3DLATHEOBJECT]=ImplGetSvx3DLatheObjectPropertyMap(); break;
+ case SVXMAP_3DEXTRUDEOBJECT: aMapArr[SVXMAP_3DEXTRUDEOBJECT]=ImplGetSvx3DExtrudeObjectPropertyMap(); break;
+ case SVXMAP_3DPOLYGONOBJECT: aMapArr[SVXMAP_3DPOLYGONOBJECT]=ImplGetSvx3DPolygonObjectPropertyMap(); break;
+ case SVXMAP_ALL: aMapArr[SVXMAP_ALL]=ImplGetSvxAllPropertyMap(); break;
+ case SVXMAP_GROUP: aMapArr[SVXMAP_GROUP]=ImplGetSvxGroupPropertyMap(); break;
+ case SVXMAP_CAPTION: aMapArr[SVXMAP_CAPTION]=ImplGetSvxCaptionPropertyMap(); break;
+ case SVXMAP_OLE2: aMapArr[SVXMAP_OLE2]=ImplGetSvxOle2PropertyMap(); break;
+ case SVXMAP_PLUGIN: aMapArr[SVXMAP_PLUGIN]=ImplGetSvxPluginPropertyMap(); break;
+ case SVXMAP_FRAME: aMapArr[SVXMAP_FRAME]=ImplGetSvxFramePropertyMap(); break;
+ case SVXMAP_APPLET: aMapArr[SVXMAP_APPLET]=ImplGetSvxAppletPropertyMap(); break;
+ case SVXMAP_CONTROL: aMapArr[SVXMAP_CONTROL]=ImplGetSvxControlShapePropertyMap(); break;
+ case SVXMAP_TEXT: aMapArr[SVXMAP_TEXT]=ImplGetSvxTextShapePropertyMap(); break;
+ case SVXMAP_CUSTOMSHAPE: aMapArr[SVXMAP_CUSTOMSHAPE]=ImplGetSvxCustomShapePropertyMap(); break;
+ case SVXMAP_MEDIA: aMapArr[SVXMAP_MEDIA]=ImplGetSvxMediaShapePropertyMap(); break;
+ case SVXMAP_TABLE: aMapArr[SVXMAP_TABLE]=ImplGetSvxTableShapePropertyMap(); break;
+ case SVXMAP_PAGE: aMapArr[SVXMAP_PAGE] = ImplGetSvxPageShapePropertyMap(); break;
+
+ default:
+ OSL_FAIL( "Unknown property map for SvxUnoPropertyMapProvider!" );
+ }
+// Sort(nPropertyId);
+ }
+ return aMapArr[nPropertyId];
+}
+const SvxItemPropertySet* SvxUnoPropertyMapProvider::GetPropertySet(sal_uInt16 nPropertyId, SfxItemPool& rPool)
+{
+ if( !aSetArr[nPropertyId] )
+ aSetArr[nPropertyId].reset(new SvxItemPropertySet( GetMap( nPropertyId ), rPool ));
+ return aSetArr[nPropertyId].get();
+}
+
+/** maps the vcl MapUnit enum to an API constant MeasureUnit.
+ Returns false if conversion is not supported.
+*/
+bool SvxMapUnitToMeasureUnit( const MapUnit eVcl, short& eApi ) noexcept
+{
+ switch( eVcl )
+ {
+ case MapUnit::Map100thMM: eApi = util::MeasureUnit::MM_100TH; break;
+ case MapUnit::Map10thMM: eApi = util::MeasureUnit::MM_10TH; break;
+ case MapUnit::MapMM: eApi = util::MeasureUnit::MM; break;
+ case MapUnit::MapCM: eApi = util::MeasureUnit::CM; break;
+ case MapUnit::Map1000thInch: eApi = util::MeasureUnit::INCH_1000TH; break;
+ case MapUnit::Map100thInch: eApi = util::MeasureUnit::INCH_100TH; break;
+ case MapUnit::Map10thInch: eApi = util::MeasureUnit::INCH_10TH; break;
+ case MapUnit::MapInch: eApi = util::MeasureUnit::INCH; break;
+ case MapUnit::MapPoint: eApi = util::MeasureUnit::POINT; break;
+ case MapUnit::MapTwip: eApi = util::MeasureUnit::TWIP; break;
+ case MapUnit::MapRelative: eApi = util::MeasureUnit::PERCENT; break;
+ default:
+ return false;
+ }
+
+ return true;
+}
+
+/** maps the API constant MeasureUnit to a vcl MapUnit enum.
+ Returns false if conversion is not supported.
+*/
+
+bool SvxMeasureUnitToFieldUnit( const short eApi, FieldUnit& eVcl ) noexcept
+{
+ switch( eApi )
+ {
+ case util::MeasureUnit::MM: eVcl = FieldUnit::MM; break;
+ case util::MeasureUnit::CM: eVcl = FieldUnit::CM; break;
+ case util::MeasureUnit::M: eVcl = FieldUnit::M; break;
+ case util::MeasureUnit::KM: eVcl = FieldUnit::KM; break;
+ case util::MeasureUnit::TWIP: eVcl = FieldUnit::TWIP; break;
+ case util::MeasureUnit::POINT: eVcl = FieldUnit::POINT; break;
+ case util::MeasureUnit::PICA: eVcl = FieldUnit::PICA; break;
+ case util::MeasureUnit::INCH: eVcl = FieldUnit::INCH; break;
+ case util::MeasureUnit::FOOT: eVcl = FieldUnit::FOOT; break;
+ case util::MeasureUnit::MILE: eVcl = FieldUnit::MILE; break;
+ case util::MeasureUnit::PERCENT: eVcl = FieldUnit::PERCENT; break;
+ case util::MeasureUnit::MM_100TH: eVcl = FieldUnit::MM_100TH; break;
+ default:
+ return false;
+ }
+
+ return true;
+}
+
+/** maps the vcl MapUnit enum to an API constant MeasureUnit.
+ Returns false if conversion is not supported.
+*/
+bool SvxFieldUnitToMeasureUnit( const FieldUnit eVcl, short& eApi ) noexcept
+{
+ switch( eVcl )
+ {
+ case FieldUnit::MM: eApi = util::MeasureUnit::MM; break;
+ case FieldUnit::CM: eApi = util::MeasureUnit::CM; break;
+ case FieldUnit::M: eApi = util::MeasureUnit::M; break;
+ case FieldUnit::KM: eApi = util::MeasureUnit::KM; break;
+ case FieldUnit::TWIP: eApi = util::MeasureUnit::TWIP; break;
+ case FieldUnit::POINT: eApi = util::MeasureUnit::POINT; break;
+ case FieldUnit::PICA: eApi = util::MeasureUnit::PICA; break;
+ case FieldUnit::INCH: eApi = util::MeasureUnit::INCH; break;
+ case FieldUnit::FOOT: eApi = util::MeasureUnit::FOOT; break;
+ case FieldUnit::MILE: eApi = util::MeasureUnit::MILE; break;
+ case FieldUnit::PERCENT: eApi = util::MeasureUnit::PERCENT; break;
+ case FieldUnit::MM_100TH: eApi = util::MeasureUnit::MM_100TH; break;
+ default:
+ return false;
+ }
+
+ return true;
+}
+
+constexpr rtl::OUStringConstExpr RID_SVXSTR_BMP_DEF[] =
+{
+ RID_SVXSTR_BMP0_DEF,
+ RID_SVXSTR_BMP1_DEF,
+ RID_SVXSTR_BMP2_DEF,
+ RID_SVXSTR_BMP3_DEF,
+ RID_SVXSTR_BMP4_DEF,
+ RID_SVXSTR_BMP5_DEF,
+ RID_SVXSTR_BMP6_DEF,
+ RID_SVXSTR_BMP7_DEF,
+ RID_SVXSTR_BMP8_DEF,
+ RID_SVXSTR_BMP9_DEF,
+ RID_SVXSTR_BMP10_DEF,
+ RID_SVXSTR_BMP11_DEF,
+ RID_SVXSTR_BMP12_DEF,
+ RID_SVXSTR_BMP13_DEF,
+ RID_SVXSTR_BMP14_DEF,
+ RID_SVXSTR_BMP15_DEF,
+ RID_SVXSTR_BMP16_DEF,
+ RID_SVXSTR_BMP17_DEF,
+ RID_SVXSTR_BMP18_DEF,
+ RID_SVXSTR_BMP19_DEF,
+ RID_SVXSTR_BMP20_DEF,
+ RID_SVXSTR_BMP21_DEF,
+ RID_SVXSTR_BMP22_DEF,
+ RID_SVXSTR_BMP23_DEF,
+ RID_SVXSTR_BMP24_DEF,
+ RID_SVXSTR_BMP25_DEF,
+ RID_SVXSTR_BMP26_DEF,
+ RID_SVXSTR_BMP27_DEF,
+ RID_SVXSTR_BMP28_DEF,
+ RID_SVXSTR_BMP29_DEF,
+ RID_SVXSTR_BMP30_DEF,
+ RID_SVXSTR_BMP31_DEF,
+ RID_SVXSTR_BMP32_DEF,
+ RID_SVXSTR_BMP33_DEF,
+ RID_SVXSTR_BMP34_DEF,
+ RID_SVXSTR_BMP35_DEF,
+ RID_SVXSTR_BMP36_DEF,
+ RID_SVXSTR_BMP37_DEF,
+ RID_SVXSTR_BMP38_DEF,
+ RID_SVXSTR_BMP39_DEF,
+ RID_SVXSTR_BMP40_DEF,
+ RID_SVXSTR_BMP41_DEF,
+ RID_SVXSTR_BMP42_DEF,
+ RID_SVXSTR_BMP43_DEF,
+ RID_SVXSTR_BMP44_DEF,
+ RID_SVXSTR_BMP45_DEF,
+ RID_SVXSTR_BMP46_DEF,
+ RID_SVXSTR_BMP47_DEF,
+ RID_SVXSTR_BMP48_DEF,
+ RID_SVXSTR_BMP49_DEF,
+ RID_SVXSTR_BMP50_DEF,
+ RID_SVXSTR_BMP51_DEF,
+ RID_SVXSTR_BMP52_DEF,
+ RID_SVXSTR_BMP53_DEF,
+ RID_SVXSTR_BMP54_DEF,
+ RID_SVXSTR_BMP55_DEF,
+ RID_SVXSTR_BMP56_DEF,
+ RID_SVXSTR_BMP57_DEF,
+ RID_SVXSTR_BMP58_DEF,
+ RID_SVXSTR_BMP59_DEF,
+ RID_SVXSTR_BMP60_DEF,
+ RID_SVXSTR_BMP61_DEF,
+ RID_SVXSTR_BMP62_DEF,
+ RID_SVXSTR_BMP63_DEF,
+ RID_SVXSTR_BMP64_DEF,
+ RID_SVXSTR_BMP65_DEF,
+ RID_SVXSTR_BMP66_DEF,
+ RID_SVXSTR_BMP67_DEF,
+ RID_SVXSTR_BMP68_DEF,
+ RID_SVXSTR_BMP69_DEF,
+ RID_SVXSTR_BMP70_DEF,
+ RID_SVXSTR_BMP71_DEF,
+ RID_SVXSTR_BMP72_DEF,
+ RID_SVXSTR_BMP73_DEF,
+ RID_SVXSTR_BMP74_DEF,
+ RID_SVXSTR_BMP75_DEF,
+ RID_SVXSTR_BMP76_DEF,
+ RID_SVXSTR_BMP77_DEF,
+ RID_SVXSTR_BMP78_DEF,
+ RID_SVXSTR_BMP79_DEF,
+ RID_SVXSTR_BMP80_DEF,
+ RID_SVXSTR_BMP81_DEF,
+ RID_SVXSTR_BMP82_DEF,
+ RID_SVXSTR_BMP83_DEF,
+ RID_SVXSTR_BMP84_DEF,
+ RID_SVXSTR_BMP85_DEF,
+ RID_SVXSTR_BMP86_DEF,
+ RID_SVXSTR_BMP87_DEF,
+ RID_SVXSTR_BMP88_DEF,
+ RID_SVXSTR_BMP89_DEF,
+ RID_SVXSTR_BMP90_DEF,
+ RID_SVXSTR_BMP91_DEF,
+ RID_SVXSTR_BMP92_DEF
+};
+
+const TranslateId RID_SVXSTR_BMP[] =
+{
+ RID_SVXSTR_BMP0,
+ RID_SVXSTR_BMP1,
+ RID_SVXSTR_BMP2,
+ RID_SVXSTR_BMP3,
+ RID_SVXSTR_BMP4,
+ RID_SVXSTR_BMP5,
+ RID_SVXSTR_BMP6,
+ RID_SVXSTR_BMP7,
+ RID_SVXSTR_BMP8,
+ RID_SVXSTR_BMP9,
+ RID_SVXSTR_BMP10,
+ RID_SVXSTR_BMP11,
+ RID_SVXSTR_BMP12,
+ RID_SVXSTR_BMP13,
+ RID_SVXSTR_BMP14,
+ RID_SVXSTR_BMP15,
+ RID_SVXSTR_BMP16,
+ RID_SVXSTR_BMP17,
+ RID_SVXSTR_BMP18,
+ RID_SVXSTR_BMP19,
+ RID_SVXSTR_BMP20,
+ RID_SVXSTR_BMP21,
+ RID_SVXSTR_BMP22,
+ RID_SVXSTR_BMP23,
+ RID_SVXSTR_BMP24,
+ RID_SVXSTR_BMP25,
+ RID_SVXSTR_BMP26,
+ RID_SVXSTR_BMP27,
+ RID_SVXSTR_BMP28,
+ RID_SVXSTR_BMP29,
+ RID_SVXSTR_BMP30,
+ RID_SVXSTR_BMP31,
+ RID_SVXSTR_BMP32,
+ RID_SVXSTR_BMP33,
+ RID_SVXSTR_BMP34,
+ RID_SVXSTR_BMP35,
+ RID_SVXSTR_BMP36,
+ RID_SVXSTR_BMP37,
+ RID_SVXSTR_BMP38,
+ RID_SVXSTR_BMP39,
+ RID_SVXSTR_BMP40,
+ RID_SVXSTR_BMP41,
+ RID_SVXSTR_BMP42,
+ RID_SVXSTR_BMP43,
+ RID_SVXSTR_BMP44,
+ RID_SVXSTR_BMP45,
+ RID_SVXSTR_BMP46,
+ RID_SVXSTR_BMP47,
+ RID_SVXSTR_BMP48,
+ RID_SVXSTR_BMP49,
+ RID_SVXSTR_BMP50,
+ RID_SVXSTR_BMP51,
+ RID_SVXSTR_BMP52,
+ RID_SVXSTR_BMP53,
+ RID_SVXSTR_BMP54,
+ RID_SVXSTR_BMP55,
+ RID_SVXSTR_BMP56,
+ RID_SVXSTR_BMP57,
+ RID_SVXSTR_BMP58,
+ RID_SVXSTR_BMP59,
+ RID_SVXSTR_BMP60,
+ RID_SVXSTR_BMP61,
+ RID_SVXSTR_BMP62,
+ RID_SVXSTR_BMP63,
+ RID_SVXSTR_BMP64,
+ RID_SVXSTR_BMP65,
+ RID_SVXSTR_BMP66,
+ RID_SVXSTR_BMP67,
+ RID_SVXSTR_BMP68,
+ RID_SVXSTR_BMP69,
+ RID_SVXSTR_BMP70,
+ RID_SVXSTR_BMP71,
+ RID_SVXSTR_BMP72,
+ RID_SVXSTR_BMP73,
+ RID_SVXSTR_BMP74,
+ RID_SVXSTR_BMP75,
+ RID_SVXSTR_BMP76,
+ RID_SVXSTR_BMP77,
+ RID_SVXSTR_BMP78,
+ RID_SVXSTR_BMP79,
+ RID_SVXSTR_BMP80,
+ RID_SVXSTR_BMP81,
+ RID_SVXSTR_BMP82,
+ RID_SVXSTR_BMP83,
+ RID_SVXSTR_BMP84,
+ RID_SVXSTR_BMP85,
+ RID_SVXSTR_BMP86,
+ RID_SVXSTR_BMP87,
+ RID_SVXSTR_BMP88,
+ RID_SVXSTR_BMP89,
+ RID_SVXSTR_BMP90,
+ RID_SVXSTR_BMP91,
+ RID_SVXSTR_BMP92
+};
+
+constexpr rtl::OUStringConstExpr RID_SVXSTR_DASH_DEF[] =
+{
+ RID_SVXSTR_DASH0_DEF,
+ RID_SVXSTR_DASH1_DEF,
+ RID_SVXSTR_DASH2_DEF,
+ RID_SVXSTR_DASH3_DEF,
+ RID_SVXSTR_DASH4_DEF,
+ RID_SVXSTR_DASH5_DEF,
+ RID_SVXSTR_DASH6_DEF,
+ RID_SVXSTR_DASH7_DEF,
+ RID_SVXSTR_DASH8_DEF,
+ RID_SVXSTR_DASH9_DEF,
+ RID_SVXSTR_DASH10_DEF,
+ RID_SVXSTR_DASH11_DEF,
+ RID_SVXSTR_DASH12_DEF,
+ RID_SVXSTR_DASH13_DEF,
+ RID_SVXSTR_DASH14_DEF,
+ RID_SVXSTR_DASH15_DEF,
+ RID_SVXSTR_DASH16_DEF,
+ RID_SVXSTR_DASH17_DEF,
+ RID_SVXSTR_DASH18_DEF,
+ RID_SVXSTR_DASH19_DEF,
+ RID_SVXSTR_DASH20_DEF,
+ RID_SVXSTR_DASH21_DEF,
+ RID_SVXSTR_DASH22_DEF,
+ RID_SVXSTR_DASH23_DEF,
+ RID_SVXSTR_DASH24_DEF,
+ RID_SVXSTR_DASH25_DEF,
+ RID_SVXSTR_DASH26_DEF,
+ RID_SVXSTR_DASH27_DEF,
+ RID_SVXSTR_DASH28_DEF,
+ RID_SVXSTR_DASH29_DEF,
+ RID_SVXSTR_DASH30_DEF
+
+};
+
+const TranslateId RID_SVXSTR_DASH[] =
+{
+ RID_SVXSTR_DASH0,
+ RID_SVXSTR_DASH1,
+ RID_SVXSTR_DASH2,
+ RID_SVXSTR_DASH3,
+ RID_SVXSTR_DASH4,
+ RID_SVXSTR_DASH5,
+ RID_SVXSTR_DASH6,
+ RID_SVXSTR_DASH7,
+ RID_SVXSTR_DASH8,
+ RID_SVXSTR_DASH9,
+ RID_SVXSTR_DASH10,
+ RID_SVXSTR_DASH11,
+ RID_SVXSTR_DASH12,
+ RID_SVXSTR_DASH13,
+ RID_SVXSTR_DASH14,
+ RID_SVXSTR_DASH15,
+ RID_SVXSTR_DASH16,
+ RID_SVXSTR_DASH17,
+ RID_SVXSTR_DASH18,
+ RID_SVXSTR_DASH19,
+ RID_SVXSTR_DASH20,
+ RID_SVXSTR_DASH21,
+ RID_SVXSTR_DASH22,
+ RID_SVXSTR_DASH23,
+ RID_SVXSTR_DASH24,
+ RID_SVXSTR_DASH25,
+ RID_SVXSTR_DASH26,
+ RID_SVXSTR_DASH27,
+ RID_SVXSTR_DASH28,
+ RID_SVXSTR_DASH29,
+ RID_SVXSTR_DASH30
+};
+
+constexpr rtl::OUStringConstExpr RID_SVXSTR_LEND_DEF[] =
+{
+ RID_SVXSTR_LEND0_DEF,
+ RID_SVXSTR_LEND1_DEF,
+ RID_SVXSTR_LEND2_DEF,
+ RID_SVXSTR_LEND3_DEF,
+ RID_SVXSTR_LEND4_DEF,
+ RID_SVXSTR_LEND5_DEF,
+ RID_SVXSTR_LEND6_DEF,
+ RID_SVXSTR_LEND7_DEF,
+ RID_SVXSTR_LEND8_DEF,
+ RID_SVXSTR_LEND9_DEF,
+ RID_SVXSTR_LEND10_DEF,
+ RID_SVXSTR_LEND11_DEF,
+ RID_SVXSTR_LEND12_DEF,
+ RID_SVXSTR_LEND13_DEF,
+ RID_SVXSTR_LEND14_DEF,
+ RID_SVXSTR_LEND15_DEF,
+ RID_SVXSTR_LEND16_DEF,
+ RID_SVXSTR_LEND17_DEF,
+ RID_SVXSTR_LEND18_DEF,
+ RID_SVXSTR_LEND19_DEF,
+ RID_SVXSTR_LEND20_DEF,
+ RID_SVXSTR_LEND21_DEF,
+ RID_SVXSTR_LEND22_DEF,
+ RID_SVXSTR_LEND23_DEF,
+ RID_SVXSTR_LEND24_DEF,
+ RID_SVXSTR_LEND25_DEF,
+ RID_SVXSTR_LEND26_DEF,
+ RID_SVXSTR_LEND27_DEF,
+ RID_SVXSTR_LEND28_DEF,
+ RID_SVXSTR_LEND29_DEF,
+ RID_SVXSTR_LEND30_DEF,
+ RID_SVXSTR_LEND31_DEF
+};
+
+const TranslateId RID_SVXSTR_LEND[] =
+{
+ RID_SVXSTR_LEND0,
+ RID_SVXSTR_LEND1,
+ RID_SVXSTR_LEND2,
+ RID_SVXSTR_LEND3,
+ RID_SVXSTR_LEND4,
+ RID_SVXSTR_LEND5,
+ RID_SVXSTR_LEND6,
+ RID_SVXSTR_LEND7,
+ RID_SVXSTR_LEND8,
+ RID_SVXSTR_LEND9,
+ RID_SVXSTR_LEND10,
+ RID_SVXSTR_LEND11,
+ RID_SVXSTR_LEND12,
+ RID_SVXSTR_LEND13,
+ RID_SVXSTR_LEND14,
+ RID_SVXSTR_LEND15,
+ RID_SVXSTR_LEND16,
+ RID_SVXSTR_LEND17,
+ RID_SVXSTR_LEND18,
+ RID_SVXSTR_LEND19,
+ RID_SVXSTR_LEND20,
+ RID_SVXSTR_LEND21,
+ RID_SVXSTR_LEND22,
+ RID_SVXSTR_LEND23,
+ RID_SVXSTR_LEND24,
+ RID_SVXSTR_LEND25,
+ RID_SVXSTR_LEND26,
+ RID_SVXSTR_LEND27,
+ RID_SVXSTR_LEND28,
+ RID_SVXSTR_LEND29,
+ RID_SVXSTR_LEND30,
+ RID_SVXSTR_LEND31
+};
+
+constexpr rtl::OUStringConstExpr RID_SVXSTR_GRDT_DEF[] =
+{
+ RID_SVXSTR_GRDT0_DEF,
+ RID_SVXSTR_GRDT1_DEF,
+ RID_SVXSTR_GRDT2_DEF,
+ RID_SVXSTR_GRDT3_DEF,
+ RID_SVXSTR_GRDT4_DEF,
+ RID_SVXSTR_GRDT5_DEF,
+ RID_SVXSTR_GRDT6_DEF,
+ RID_SVXSTR_GRDT7_DEF,
+ RID_SVXSTR_GRDT8_DEF,
+ RID_SVXSTR_GRDT9_DEF,
+ RID_SVXSTR_GRDT10_DEF,
+ RID_SVXSTR_GRDT11_DEF,
+ RID_SVXSTR_GRDT12_DEF,
+ RID_SVXSTR_GRDT13_DEF,
+ RID_SVXSTR_GRDT14_DEF,
+ RID_SVXSTR_GRDT15_DEF,
+ RID_SVXSTR_GRDT16_DEF,
+ RID_SVXSTR_GRDT17_DEF,
+ RID_SVXSTR_GRDT18_DEF,
+ RID_SVXSTR_GRDT19_DEF,
+ RID_SVXSTR_GRDT20_DEF,
+ RID_SVXSTR_GRDT21_DEF,
+ RID_SVXSTR_GRDT22_DEF,
+ RID_SVXSTR_GRDT23_DEF,
+ RID_SVXSTR_GRDT24_DEF,
+ RID_SVXSTR_GRDT25_DEF,
+ RID_SVXSTR_GRDT26_DEF,
+ RID_SVXSTR_GRDT27_DEF,
+ RID_SVXSTR_GRDT28_DEF,
+ RID_SVXSTR_GRDT29_DEF,
+ RID_SVXSTR_GRDT30_DEF,
+ RID_SVXSTR_GRDT31_DEF,
+ RID_SVXSTR_GRDT32_DEF,
+ RID_SVXSTR_GRDT33_DEF,
+ RID_SVXSTR_GRDT34_DEF,
+ RID_SVXSTR_GRDT35_DEF,
+ RID_SVXSTR_GRDT36_DEF,
+ RID_SVXSTR_GRDT37_DEF,
+ RID_SVXSTR_GRDT38_DEF,
+ RID_SVXSTR_GRDT39_DEF,
+ RID_SVXSTR_GRDT40_DEF,
+ RID_SVXSTR_GRDT41_DEF,
+ RID_SVXSTR_GRDT42_DEF,
+ RID_SVXSTR_GRDT43_DEF,
+ RID_SVXSTR_GRDT44_DEF,
+ RID_SVXSTR_GRDT45_DEF,
+ RID_SVXSTR_GRDT46_DEF,
+ RID_SVXSTR_GRDT47_DEF,
+ RID_SVXSTR_GRDT48_DEF,
+ RID_SVXSTR_GRDT49_DEF,
+ RID_SVXSTR_GRDT50_DEF,
+ RID_SVXSTR_GRDT51_DEF,
+ RID_SVXSTR_GRDT52_DEF,
+ RID_SVXSTR_GRDT53_DEF,
+ RID_SVXSTR_GRDT54_DEF,
+ RID_SVXSTR_GRDT55_DEF,
+ RID_SVXSTR_GRDT56_DEF,
+ RID_SVXSTR_GRDT57_DEF,
+ RID_SVXSTR_GRDT58_DEF,
+ RID_SVXSTR_GRDT59_DEF,
+ RID_SVXSTR_GRDT60_DEF,
+ RID_SVXSTR_GRDT61_DEF,
+ RID_SVXSTR_GRDT62_DEF,
+ RID_SVXSTR_GRDT63_DEF,
+ RID_SVXSTR_GRDT64_DEF,
+ RID_SVXSTR_GRDT65_DEF,
+ RID_SVXSTR_GRDT66_DEF,
+ RID_SVXSTR_GRDT67_DEF,
+ RID_SVXSTR_GRDT68_DEF,
+ RID_SVXSTR_GRDT69_DEF,
+ RID_SVXSTR_GRDT70_DEF,
+ RID_SVXSTR_GRDT71_DEF,
+ RID_SVXSTR_GRDT72_DEF,
+ RID_SVXSTR_GRDT73_DEF,
+ RID_SVXSTR_GRDT74_DEF,
+ RID_SVXSTR_GRDT75_DEF,
+ RID_SVXSTR_GRDT76_DEF,
+ RID_SVXSTR_GRDT77_DEF,
+ RID_SVXSTR_GRDT78_DEF,
+ RID_SVXSTR_GRDT79_DEF,
+ RID_SVXSTR_GRDT80_DEF,
+ RID_SVXSTR_GRDT81_DEF,
+ RID_SVXSTR_GRDT82_DEF,
+ RID_SVXSTR_GRDT83_DEF,
+ RID_SVXSTR_GRDT84_DEF
+};
+
+const TranslateId RID_SVXSTR_GRDT[] =
+{
+ RID_SVXSTR_GRDT0,
+ RID_SVXSTR_GRDT1,
+ RID_SVXSTR_GRDT2,
+ RID_SVXSTR_GRDT3,
+ RID_SVXSTR_GRDT4,
+ RID_SVXSTR_GRDT5,
+ RID_SVXSTR_GRDT6,
+ RID_SVXSTR_GRDT7,
+ RID_SVXSTR_GRDT8,
+ RID_SVXSTR_GRDT9,
+ RID_SVXSTR_GRDT10,
+ RID_SVXSTR_GRDT11,
+ RID_SVXSTR_GRDT12,
+ RID_SVXSTR_GRDT13,
+ RID_SVXSTR_GRDT14,
+ RID_SVXSTR_GRDT15,
+ RID_SVXSTR_GRDT16,
+ RID_SVXSTR_GRDT17,
+ RID_SVXSTR_GRDT18,
+ RID_SVXSTR_GRDT19,
+ RID_SVXSTR_GRDT20,
+ RID_SVXSTR_GRDT21,
+ RID_SVXSTR_GRDT22,
+ RID_SVXSTR_GRDT23,
+ RID_SVXSTR_GRDT24,
+ RID_SVXSTR_GRDT25,
+ RID_SVXSTR_GRDT26,
+ RID_SVXSTR_GRDT27,
+ RID_SVXSTR_GRDT28,
+ RID_SVXSTR_GRDT29,
+ RID_SVXSTR_GRDT30,
+ RID_SVXSTR_GRDT31,
+ RID_SVXSTR_GRDT32,
+ RID_SVXSTR_GRDT33,
+ RID_SVXSTR_GRDT34,
+ RID_SVXSTR_GRDT35,
+ RID_SVXSTR_GRDT36,
+ RID_SVXSTR_GRDT37,
+ RID_SVXSTR_GRDT38,
+ RID_SVXSTR_GRDT39,
+ RID_SVXSTR_GRDT40,
+ RID_SVXSTR_GRDT41,
+ RID_SVXSTR_GRDT42,
+ RID_SVXSTR_GRDT43,
+ RID_SVXSTR_GRDT44,
+ RID_SVXSTR_GRDT45,
+ RID_SVXSTR_GRDT46,
+ RID_SVXSTR_GRDT47,
+ RID_SVXSTR_GRDT48,
+ RID_SVXSTR_GRDT49,
+ RID_SVXSTR_GRDT50,
+ RID_SVXSTR_GRDT51,
+ RID_SVXSTR_GRDT52,
+ RID_SVXSTR_GRDT53,
+ RID_SVXSTR_GRDT54,
+ RID_SVXSTR_GRDT55,
+ RID_SVXSTR_GRDT56,
+ RID_SVXSTR_GRDT57,
+ RID_SVXSTR_GRDT58,
+ RID_SVXSTR_GRDT59,
+ RID_SVXSTR_GRDT60,
+ RID_SVXSTR_GRDT61,
+ RID_SVXSTR_GRDT62,
+ RID_SVXSTR_GRDT63,
+ RID_SVXSTR_GRDT64,
+ RID_SVXSTR_GRDT65,
+ RID_SVXSTR_GRDT66,
+ RID_SVXSTR_GRDT67,
+ RID_SVXSTR_GRDT68,
+ RID_SVXSTR_GRDT69,
+ RID_SVXSTR_GRDT70,
+ RID_SVXSTR_GRDT71,
+ RID_SVXSTR_GRDT72,
+ RID_SVXSTR_GRDT73,
+ RID_SVXSTR_GRDT74,
+ RID_SVXSTR_GRDT75,
+ RID_SVXSTR_GRDT76,
+ RID_SVXSTR_GRDT77,
+ RID_SVXSTR_GRDT78,
+ RID_SVXSTR_GRDT79,
+ RID_SVXSTR_GRDT80,
+ RID_SVXSTR_GRDT81,
+ RID_SVXSTR_GRDT82,
+ RID_SVXSTR_GRDT83,
+ RID_SVXSTR_GRDT84
+};
+
+constexpr rtl::OUStringConstExpr RID_SVXSTR_HATCHS_DEF[] =
+{
+ RID_SVXSTR_HATCH0_DEF,
+ RID_SVXSTR_HATCH1_DEF,
+ RID_SVXSTR_HATCH2_DEF,
+ RID_SVXSTR_HATCH3_DEF,
+ RID_SVXSTR_HATCH4_DEF,
+ RID_SVXSTR_HATCH5_DEF,
+ RID_SVXSTR_HATCH6_DEF,
+ RID_SVXSTR_HATCH7_DEF,
+ RID_SVXSTR_HATCH8_DEF,
+ RID_SVXSTR_HATCH9_DEF,
+ RID_SVXSTR_HATCH10_DEF,
+ RID_SVXSTR_HATCH11_DEF,
+ RID_SVXSTR_HATCH12_DEF,
+ RID_SVXSTR_HATCH13_DEF,
+ RID_SVXSTR_HATCH14_DEF,
+ RID_SVXSTR_HATCH15_DEF
+};
+
+const TranslateId RID_SVXSTR_HATCHS[] =
+{
+ RID_SVXSTR_HATCH0,
+ RID_SVXSTR_HATCH1,
+ RID_SVXSTR_HATCH2,
+ RID_SVXSTR_HATCH3,
+ RID_SVXSTR_HATCH4,
+ RID_SVXSTR_HATCH5,
+ RID_SVXSTR_HATCH6,
+ RID_SVXSTR_HATCH7,
+ RID_SVXSTR_HATCH8,
+ RID_SVXSTR_HATCH9,
+ RID_SVXSTR_HATCH10,
+ RID_SVXSTR_HATCH11,
+ RID_SVXSTR_HATCH12,
+ RID_SVXSTR_HATCH13,
+ RID_SVXSTR_HATCH14,
+ RID_SVXSTR_HATCH15
+};
+
+constexpr rtl::OUStringConstExpr RID_SVXSTR_TRASNGR_DEF[] =
+{
+ RID_SVXSTR_TRASNGR0_DEF
+};
+
+const TranslateId RID_SVXSTR_TRASNGR[] =
+{
+ RID_SVXSTR_TRASNGR0
+};
+
+static bool SvxUnoGetResourceRanges( const sal_uInt16 nWhich, const rtl::OUStringConstExpr*& pApiResIds, const TranslateId*& pIntResIds, int& nCount ) noexcept
+{
+ switch( nWhich )
+ {
+ case XATTR_FILLBITMAP:
+ pApiResIds = RID_SVXSTR_BMP_DEF;
+ pIntResIds = RID_SVXSTR_BMP;
+ nCount = SAL_N_ELEMENTS(RID_SVXSTR_BMP_DEF);
+ break;
+ case XATTR_LINEDASH:
+ pApiResIds = RID_SVXSTR_DASH_DEF;
+ pIntResIds = RID_SVXSTR_DASH;
+ nCount = SAL_N_ELEMENTS(RID_SVXSTR_DASH_DEF);
+ break;
+
+ case XATTR_LINESTART:
+ case XATTR_LINEEND:
+ pApiResIds = RID_SVXSTR_LEND_DEF;
+ pIntResIds = RID_SVXSTR_LEND;
+ nCount = SAL_N_ELEMENTS(RID_SVXSTR_LEND_DEF);
+ break;
+
+ case XATTR_FILLGRADIENT:
+ pApiResIds = RID_SVXSTR_GRDT_DEF;
+ pIntResIds = RID_SVXSTR_GRDT;
+ nCount = SAL_N_ELEMENTS(RID_SVXSTR_GRDT_DEF);
+ break;
+
+ case XATTR_FILLHATCH:
+ pApiResIds = RID_SVXSTR_HATCHS_DEF;
+ pIntResIds = RID_SVXSTR_HATCHS;
+ nCount = SAL_N_ELEMENTS(RID_SVXSTR_HATCHS_DEF);
+ break;
+
+ case XATTR_FILLFLOATTRANSPARENCE:
+ pApiResIds = RID_SVXSTR_TRASNGR_DEF;
+ pIntResIds = RID_SVXSTR_TRASNGR;
+ nCount = SAL_N_ELEMENTS(RID_SVXSTR_TRASNGR_DEF);
+ break;
+
+ default:
+ return false;
+ }
+ return true;
+}
+
+/// @throws std::exception
+static bool SvxUnoConvertResourceStringToApi(const TranslateId* pSourceResIds, const rtl::OUStringConstExpr* pDestResIds, int nCount, OUString& rString)
+{
+ // first, calculate the search string length without an optional number after the name
+ sal_Int32 nLength = rString.getLength();
+ while( nLength > 0 )
+ {
+ const sal_Unicode nChar = rString[ nLength - 1 ];
+ if( (nChar < '0') || (nChar > '9') )
+ break;
+
+ nLength--;
+ }
+
+ // if we cut off a number, also cut of some spaces
+ if( nLength != rString.getLength() )
+ {
+ while( nLength > 0 )
+ {
+ const sal_Unicode nChar = rString[ nLength - 1 ];
+ if( nChar != ' ' )
+ break;
+
+ nLength--;
+ }
+ }
+
+ const std::u16string_view aShortString( rString.subView( 0, nLength ) );
+
+ for (int i = 0; i < nCount; ++i)
+ {
+ const OUString & aCompare = SvxResId(pSourceResIds[i]);
+ if( aShortString == aCompare )
+ {
+ rString = rString.replaceAt( 0, aShortString.size(), OUString(pDestResIds[i]) );
+ return true;
+ }
+ else if( rString == aCompare )
+ {
+ rString = pDestResIds[i];
+ return true;
+ }
+ }
+
+ return false;
+}
+
+static bool SvxUnoConvertResourceStringFromApi(const rtl::OUStringConstExpr* pSourceResIds, const TranslateId* pDestResIds, int nCount, OUString& rString)
+{
+ // first, calculate the search string length without an optional number after the name
+ sal_Int32 nLength = rString.getLength();
+ while( nLength > 0 )
+ {
+ const sal_Unicode nChar = rString[ nLength - 1 ];
+ if( (nChar < '0') || (nChar > '9') )
+ break;
+
+ nLength--;
+ }
+
+ // if we cut off a number, also cut of some spaces
+ if( nLength != rString.getLength() )
+ {
+ while( nLength > 0 )
+ {
+ const sal_Unicode nChar = rString[ nLength - 1 ];
+ if( nChar != ' ' )
+ break;
+
+ nLength--;
+ }
+ }
+
+ const std::u16string_view aShortString( rString.subView( 0, nLength ) );
+
+ for (int i = 0; i < nCount; ++i)
+ {
+ auto pCompare = pSourceResIds[i];
+ if( aShortString == pCompare.asView() )
+ {
+ rString = rString.replaceAt( 0, aShortString.size(), SvxResId(pDestResIds[i]) );
+ return true;
+ }
+ else if( rString == pCompare )
+ {
+ rString = SvxResId(pDestResIds[i]);
+ return true;
+ }
+ }
+
+ return false;
+}
+
+// #i122649# Some comments on the below arrays:
+// - They need to have the same order and count of items
+// - They are used to translate between translated and non-translated color names
+// - To make longer names be found which start with the same basic string,
+// these have to be in front of others
+
+// It would be nice to:
+// - evtl. organize these in a single array with 2-dimensional inner to eliminate
+// the possibility to define it wrong
+// - change the compare to also work when a shorter name is in front of a longer one
+
+constexpr rtl::OUStringConstExpr SvxUnoColorNameDefResId[] =
+{
+ RID_SVXSTR_COLOR_BLUEGREY_DEF,
+ RID_SVXSTR_COLOR_BLACK_DEF,
+ RID_SVXSTR_COLOR_BLUE_CLASSIC_DEF,
+ RID_SVXSTR_COLOR_BLUE_DEF,
+ RID_SVXSTR_COLOR_GREEN_DEF,
+ RID_SVXSTR_COLOR_RED_DEF,
+ RID_SVXSTR_COLOR_MAGENTA_DEF,
+ RID_SVXSTR_COLOR_GREY_DEF,
+ RID_SVXSTR_COLOR_YELLOWGREEN_DEF,
+ RID_SVXSTR_COLOR_YELLOW_DEF,
+ RID_SVXSTR_COLOR_WHITE_DEF,
+ RID_SVXSTR_COLOR_ORANGE_DEF,
+ RID_SVXSTR_COLOR_BORDEAUX_DEF,
+ RID_SVXSTR_COLOR_PALE_YELLOW_DEF,
+ RID_SVXSTR_COLOR_PALE_GREEN_DEF,
+ RID_SVXSTR_COLOR_DARKVIOLET_DEF,
+ RID_SVXSTR_COLOR_SALMON_DEF,
+ RID_SVXSTR_COLOR_SEABLUE_DEF,
+ RID_SVXSTR_COLOR_CHART_DEF,
+ RID_SVXSTR_COLOR_PURPLE_DEF,
+ RID_SVXSTR_COLOR_SKYBLUE_DEF,
+ RID_SVXSTR_COLOR_PINK_DEF,
+ RID_SVXSTR_COLOR_TURQUOISE_DEF,
+ RID_SVXSTR_COLOR_GOLD_DEF,
+ RID_SVXSTR_COLOR_BRICK_DEF,
+ RID_SVXSTR_COLOR_INDIGO_DEF,
+ RID_SVXSTR_COLOR_TEAL_DEF,
+ RID_SVXSTR_COLOR_LIME_DEF,
+ RID_SVXSTR_COLOR_LIGHTGRAY_DEF,
+ RID_SVXSTR_COLOR_LIGHTYELLOW_DEF,
+ RID_SVXSTR_COLOR_LIGHTGOLD_DEF,
+ RID_SVXSTR_COLOR_LIGHTORANGE_DEF,
+ RID_SVXSTR_COLOR_LIGHTBRICK_DEF,
+ RID_SVXSTR_COLOR_LIGHTRED_DEF,
+ RID_SVXSTR_COLOR_LIGHTMAGENTA_DEF,
+ RID_SVXSTR_COLOR_LIGHTPURPLE_DEF,
+ RID_SVXSTR_COLOR_LIGHTINDIGO_DEF,
+ RID_SVXSTR_COLOR_LIGHTBLUE_DEF,
+ RID_SVXSTR_COLOR_LIGHTTEAL_DEF,
+ RID_SVXSTR_COLOR_LIGHTGREEN_DEF,
+ RID_SVXSTR_COLOR_LIGHTLIME_DEF,
+ RID_SVXSTR_COLOR_DARKGRAY_DEF,
+ RID_SVXSTR_COLOR_DARKYELLOW_DEF,
+ RID_SVXSTR_COLOR_DARKGOLD_DEF,
+ RID_SVXSTR_COLOR_DARKORANGE_DEF,
+ RID_SVXSTR_COLOR_DARKBRICK_DEF,
+ RID_SVXSTR_COLOR_DARKRED_DEF,
+ RID_SVXSTR_COLOR_DARKMAGENTA_DEF,
+ RID_SVXSTR_COLOR_DARKPURPLE_DEF,
+ RID_SVXSTR_COLOR_DARKINDIGO_DEF,
+ RID_SVXSTR_COLOR_DARKBLUE_DEF,
+ RID_SVXSTR_COLOR_DARKTEAL_DEF,
+ RID_SVXSTR_COLOR_DARKGREEN_DEF,
+ RID_SVXSTR_COLOR_DARKLIME_DEF,
+ RID_SVXSTR_COLOR_VIOLET_DEF,
+ RID_SVXSTR_COLOR_VIOLET_OUG_DEF,
+ RID_SVXSTR_COLOR_BLUE_OUG_DEF,
+ RID_SVXSTR_COLOR_AZURE_OUG_DEF,
+ RID_SVXSTR_COLOR_SPRINGGREEN_OUG_DEF,
+ RID_SVXSTR_COLOR_GREEN_OUG_DEF,
+ RID_SVXSTR_COLOR_CHARTREUSEGREEN_OUG_DEF,
+ RID_SVXSTR_COLOR_ORANGE_OUG_DEF,
+ RID_SVXSTR_COLOR_RED_OUG_DEF,
+ RID_SVXSTR_COLOR_ROSE_OUG_DEF,
+ RID_SVXSTR_COLOR_AZURE_DEF,
+ RID_SVXSTR_COLOR_CYAN_DEF,
+ RID_SVXSTR_COLOR_SPRINGGREEN_DEF,
+ RID_SVXSTR_COLOR_CHARTREUSEGREEN_DEF,
+ RID_SVXSTR_COLOR_ROSE_DEF,
+ RID_SVXSTR_COLOR_MATERIAL_GRAY_A_DEF,
+ RID_SVXSTR_COLOR_MATERIAL_YELLOW_A_DEF,
+ RID_SVXSTR_COLOR_MATERIAL_AMBER_A_DEF,
+ RID_SVXSTR_COLOR_MATERIAL_AMBER_DEF,
+ RID_SVXSTR_COLOR_MATERIAL_ORANGE_A_DEF,
+ RID_SVXSTR_COLOR_MATERIAL_DEEP_ORANGE_A_DEF,
+ RID_SVXSTR_COLOR_MATERIAL_DEEP_ORANGE_DEF,
+ RID_SVXSTR_COLOR_MATERIAL_RED_A_DEF,
+ RID_SVXSTR_COLOR_MATERIAL_PINK_A_DEF,
+ RID_SVXSTR_COLOR_MATERIAL_PURPLE_A_DEF,
+ RID_SVXSTR_COLOR_MATERIAL_DEEP_PURPLE_A_DEF,
+ RID_SVXSTR_COLOR_MATERIAL_DEEP_PURPLE_DEF,
+ RID_SVXSTR_COLOR_MATERIAL_INDIGO_A_DEF,
+ RID_SVXSTR_COLOR_MATERIAL_BLUE_A_DEF,
+ RID_SVXSTR_COLOR_MATERIAL_LIGHT_BLUE_A_DEF,
+ RID_SVXSTR_COLOR_MATERIAL_CYAN_A_DEF,
+ RID_SVXSTR_COLOR_MATERIAL_TEAL_A_DEF,
+ RID_SVXSTR_COLOR_MATERIAL_GREEN_A_DEF,
+ RID_SVXSTR_COLOR_MATERIAL_LIGHT_GREEN_A_DEF,
+ RID_SVXSTR_COLOR_MATERIAL_LIME_A_DEF,
+ RID_SVXSTR_COLOR_MATERIAL_BROWN_A_DEF,
+ RID_SVXSTR_COLOR_MATERIAL_BROWN_DEF,
+ RID_SVXSTR_COLOR_MATERIAL_BLUE_GRAY_A_DEF,
+ RID_SVXSTR_COLOR_MATERIAL_BLUE_GRAY_DEF,
+ RID_SVXSTR_COLOR_LIBRE_GREEN_1_DEF,
+ RID_SVXSTR_COLOR_LIBRE_GREEN_ACCENT_DEF,
+ RID_SVXSTR_COLOR_LIBRE_BLUE_ACCENT_DEF,
+ RID_SVXSTR_COLOR_LIBRE_ORANGE_ACCENT_DEF,
+ RID_SVXSTR_COLOR_LIBRE_PURPLE_DEF,
+ RID_SVXSTR_COLOR_LIBRE_PURPLE_ACCENT_DEF,
+ RID_SVXSTR_COLOR_LIBRE_YELLOW_ACCENT_DEF
+};
+
+const TranslateId SvxUnoColorNameResId[] =
+{
+ RID_SVXSTR_COLOR_BLUEGREY,
+ RID_SVXSTR_COLOR_BLACK,
+ RID_SVXSTR_COLOR_BLUE_CLASSIC,
+ RID_SVXSTR_COLOR_BLUE,
+ RID_SVXSTR_COLOR_GREEN,
+ RID_SVXSTR_COLOR_RED,
+ RID_SVXSTR_COLOR_MAGENTA,
+ RID_SVXSTR_COLOR_GREY,
+ RID_SVXSTR_COLOR_YELLOWGREEN,
+ RID_SVXSTR_COLOR_YELLOW,
+ RID_SVXSTR_COLOR_WHITE,
+ RID_SVXSTR_COLOR_ORANGE,
+ RID_SVXSTR_COLOR_BORDEAUX,
+ RID_SVXSTR_COLOR_PALE_YELLOW,
+ RID_SVXSTR_COLOR_PALE_GREEN,
+ RID_SVXSTR_COLOR_DARKVIOLET,
+ RID_SVXSTR_COLOR_SALMON,
+ RID_SVXSTR_COLOR_SEABLUE,
+ RID_SVXSTR_COLOR_CHART,
+ RID_SVXSTR_COLOR_PURPLE,
+ RID_SVXSTR_COLOR_SKYBLUE,
+ RID_SVXSTR_COLOR_PINK,
+ RID_SVXSTR_COLOR_TURQUOISE,
+ RID_SVXSTR_COLOR_GOLD,
+ RID_SVXSTR_COLOR_BRICK,
+ RID_SVXSTR_COLOR_INDIGO,
+ RID_SVXSTR_COLOR_TEAL,
+ RID_SVXSTR_COLOR_LIME,
+ RID_SVXSTR_COLOR_LIGHTGRAY,
+ RID_SVXSTR_COLOR_LIGHTYELLOW,
+ RID_SVXSTR_COLOR_LIGHTGOLD,
+ RID_SVXSTR_COLOR_LIGHTORANGE,
+ RID_SVXSTR_COLOR_LIGHTBRICK,
+ RID_SVXSTR_COLOR_LIGHTRED,
+ RID_SVXSTR_COLOR_LIGHTMAGENTA,
+ RID_SVXSTR_COLOR_LIGHTPURPLE,
+ RID_SVXSTR_COLOR_LIGHTINDIGO,
+ RID_SVXSTR_COLOR_LIGHTBLUE,
+ RID_SVXSTR_COLOR_LIGHTTEAL,
+ RID_SVXSTR_COLOR_LIGHTGREEN,
+ RID_SVXSTR_COLOR_LIGHTLIME,
+ RID_SVXSTR_COLOR_DARKGRAY,
+ RID_SVXSTR_COLOR_DARKYELLOW,
+ RID_SVXSTR_COLOR_DARKGOLD,
+ RID_SVXSTR_COLOR_DARKORANGE,
+ RID_SVXSTR_COLOR_DARKBRICK,
+ RID_SVXSTR_COLOR_DARKRED,
+ RID_SVXSTR_COLOR_DARKMAGENTA,
+ RID_SVXSTR_COLOR_DARKPURPLE,
+ RID_SVXSTR_COLOR_DARKINDIGO,
+ RID_SVXSTR_COLOR_DARKBLUE,
+ RID_SVXSTR_COLOR_DARKTEAL,
+ RID_SVXSTR_COLOR_DARKGREEN,
+ RID_SVXSTR_COLOR_DARKLIME,
+ RID_SVXSTR_COLOR_VIOLET,
+ RID_SVXSTR_COLOR_VIOLET_OUG,
+ RID_SVXSTR_COLOR_BLUE_OUG,
+ RID_SVXSTR_COLOR_AZURE_OUG,
+ RID_SVXSTR_COLOR_SPRINGGREEN_OUG,
+ RID_SVXSTR_COLOR_GREEN_OUG,
+ RID_SVXSTR_COLOR_CHARTREUSEGREEN_OUG,
+ RID_SVXSTR_COLOR_ORANGE_OUG,
+ RID_SVXSTR_COLOR_RED_OUG,
+ RID_SVXSTR_COLOR_ROSE_OUG,
+ RID_SVXSTR_COLOR_AZURE,
+ RID_SVXSTR_COLOR_CYAN,
+ RID_SVXSTR_COLOR_SPRINGGREEN,
+ RID_SVXSTR_COLOR_CHARTREUSEGREEN,
+ RID_SVXSTR_COLOR_ROSE,
+ RID_SVXSTR_COLOR_MATERIAL_GRAY_A,
+ RID_SVXSTR_COLOR_MATERIAL_YELLOW_A,
+ RID_SVXSTR_COLOR_MATERIAL_AMBER_A,
+ RID_SVXSTR_COLOR_MATERIAL_AMBER,
+ RID_SVXSTR_COLOR_MATERIAL_ORANGE_A,
+ RID_SVXSTR_COLOR_MATERIAL_DEEP_ORANGE_A,
+ RID_SVXSTR_COLOR_MATERIAL_DEEP_ORANGE,
+ RID_SVXSTR_COLOR_MATERIAL_RED_A,
+ RID_SVXSTR_COLOR_MATERIAL_PINK_A,
+ RID_SVXSTR_COLOR_MATERIAL_PURPLE_A,
+ RID_SVXSTR_COLOR_MATERIAL_DEEP_PURPLE_A,
+ RID_SVXSTR_COLOR_MATERIAL_DEEP_PURPLE,
+ RID_SVXSTR_COLOR_MATERIAL_INDIGO_A,
+ RID_SVXSTR_COLOR_MATERIAL_BLUE_A,
+ RID_SVXSTR_COLOR_MATERIAL_LIGHT_BLUE_A,
+ RID_SVXSTR_COLOR_MATERIAL_CYAN_A,
+ RID_SVXSTR_COLOR_MATERIAL_TEAL_A,
+ RID_SVXSTR_COLOR_MATERIAL_GREEN_A,
+ RID_SVXSTR_COLOR_MATERIAL_LIGHT_GREEN_A,
+ RID_SVXSTR_COLOR_MATERIAL_LIME_A,
+ RID_SVXSTR_COLOR_MATERIAL_BROWN_A,
+ RID_SVXSTR_COLOR_MATERIAL_BROWN,
+ RID_SVXSTR_COLOR_MATERIAL_BLUE_GRAY_A,
+ RID_SVXSTR_COLOR_MATERIAL_BLUE_GRAY,
+ RID_SVXSTR_COLOR_LIBRE_GREEN_1,
+ RID_SVXSTR_COLOR_LIBRE_GREEN_ACCENT,
+ RID_SVXSTR_COLOR_LIBRE_BLUE_ACCENT,
+ RID_SVXSTR_COLOR_LIBRE_ORANGE_ACCENT,
+ RID_SVXSTR_COLOR_LIBRE_PURPLE,
+ RID_SVXSTR_COLOR_LIBRE_PURPLE_ACCENT,
+ RID_SVXSTR_COLOR_LIBRE_YELLOW_ACCENT
+};
+
+/// @throws std::exception
+static bool SvxUnoConvertResourceStringBuiltInToApi(const TranslateId* pSourceResIds, rtl::OUStringConstExpr const *pDestResIds, int nCount, OUString& rString)
+{
+ //We replace e.g. "Gray 10%" with the translation of Gray, but we shouldn't
+ //replace "Red Hat 1" with the translation of Red :-)
+ sal_Int32 nLength = rString.getLength();
+ while( nLength > 0 )
+ {
+ const sal_Unicode nChar = rString[nLength-1];
+ if (nChar != '%' && (nChar < '0' || nChar > '9'))
+ break;
+ nLength--;
+ }
+ std::u16string_view sStr = o3tl::trim(rString.subView(0, nLength));
+
+ for(int i = 0; i < nCount; ++i )
+ {
+ OUString aStrDefName = SvxResId(pSourceResIds[i]);
+ if( sStr == aStrDefName )
+ {
+ OUString aReplace = pDestResIds[i];
+ rString = rString.replaceAt( 0, aStrDefName.getLength(), aReplace );
+ return true;
+ }
+ }
+
+ return false;
+}
+
+static bool SvxUnoConvertResourceStringBuiltInFromApi(rtl::OUStringConstExpr const *pSourceResIds, const TranslateId* pDestResIds, int nCount, OUString& rString)
+{
+ //We replace e.g. "Gray 10%" with the translation of Gray, but we shouldn't
+ //replace "Red Hat 1" with the translation of Red :-)
+ sal_Int32 nLength = rString.getLength();
+ while( nLength > 0 )
+ {
+ const sal_Unicode nChar = rString[nLength-1];
+ if (nChar != '%' && (nChar < '0' || nChar > '9'))
+ break;
+ nLength--;
+ }
+ std::u16string_view sStr = o3tl::trim(rString.subView(0, nLength));
+
+ for(int i = 0; i < nCount; ++i )
+ {
+ if( sStr == pSourceResIds[i].asView() )
+ {
+ OUString aReplace = SvxResId(pDestResIds[i]);
+ rString = aReplace + rString.subView( pSourceResIds[i].asView().size() );
+ return true;
+ }
+ }
+
+ return false;
+}
+
+/** if the given name is a predefined name for the current language it is replaced by
+ the corresponding api name.
+*/
+OUString SvxUnogetApiNameForItem(const sal_uInt16 nWhich, const OUString& rInternalName)
+{
+ OUString aNew = rInternalName;
+
+ if( nWhich == sal_uInt16(XATTR_LINECOLOR) )
+ {
+ if (SvxUnoConvertResourceStringBuiltInToApi(SvxUnoColorNameResId, SvxUnoColorNameDefResId, SAL_N_ELEMENTS(SvxUnoColorNameResId), aNew))
+ {
+ return aNew;
+ }
+ }
+ else
+ {
+ const rtl::OUStringConstExpr* pApiResIds;
+ const TranslateId* pIntResIds;
+ int nCount;
+
+ if( SvxUnoGetResourceRanges(nWhich, pApiResIds, pIntResIds, nCount))
+ {
+ if (SvxUnoConvertResourceStringToApi(pIntResIds, pApiResIds, nCount, aNew))
+ {
+ return aNew;
+ }
+ }
+ }
+
+ // just use previous name, if nothing else was found.
+ return rInternalName;
+}
+
+/** if the given name is a predefined api name it is replaced by the predefined name
+ for the current language.
+*/
+OUString SvxUnogetInternalNameForItem(const sal_uInt16 nWhich, const OUString& rApiName)
+{
+ OUString aNew = rApiName;
+
+ if( nWhich == sal_uInt16(XATTR_LINECOLOR) )
+ {
+ if (SvxUnoConvertResourceStringBuiltInFromApi(SvxUnoColorNameDefResId, SvxUnoColorNameResId, SAL_N_ELEMENTS(SvxUnoColorNameResId), aNew))
+ {
+ return aNew;
+ }
+ }
+ else
+ {
+ const rtl::OUStringConstExpr* pApiResIds;
+ const TranslateId* pIntResIds;
+ int nCount;
+
+ if (SvxUnoGetResourceRanges(nWhich, pApiResIds, pIntResIds, nCount))
+ {
+ if (SvxUnoConvertResourceStringFromApi(pApiResIds, pIntResIds, nCount, aNew))
+ {
+ return aNew;
+ }
+ }
+ }
+
+ // just use previous name, if nothing else was found.
+ return rApiName;
+}
+
+
+rtl::Reference<comphelper::PropertySetInfo> const & SvxPropertySetInfoPool::getOrCreate( sal_Int32 nServiceId ) noexcept
+{
+ SolarMutexGuard aGuard;
+
+ assert( nServiceId <= SVXUNO_SERVICEID_LASTID );
+
+ if( !mxInfos[ nServiceId ].is() )
+ {
+ mxInfos[nServiceId] = new comphelper::PropertySetInfo();
+
+ switch( nServiceId )
+ {
+ case SVXUNO_SERVICEID_COM_SUN_STAR_DRAWING_DEFAULTS:
+ mxInfos[SVXUNO_SERVICEID_COM_SUN_STAR_DRAWING_DEFAULTS]->add( ImplGetSvxDrawingDefaultsPropertyMap() );
+ break;
+ case SVXUNO_SERVICEID_COM_SUN_STAR_DRAWING_DEFAULTS_WRITER:
+ mxInfos[SVXUNO_SERVICEID_COM_SUN_STAR_DRAWING_DEFAULTS_WRITER]->add( ImplGetSvxDrawingDefaultsPropertyMap() );
+ mxInfos[SVXUNO_SERVICEID_COM_SUN_STAR_DRAWING_DEFAULTS_WRITER]->remove( UNO_NAME_EDIT_PARA_IS_HANGING_PUNCTUATION );
+ // OD 13.10.2003 #i18732# - add property map for writer item 'IsFollowingTextFlow'
+ mxInfos[SVXUNO_SERVICEID_COM_SUN_STAR_DRAWING_DEFAULTS_WRITER]->add( ImplGetAdditionalWriterDrawingDefaultsPropertyMap() );
+ break;
+
+ default:
+ OSL_FAIL( "unknown service id!" );
+ }
+ }
+
+ return mxInfos[ nServiceId ];
+}
+
+rtl::Reference<comphelper::PropertySetInfo> SvxPropertySetInfoPool::mxInfos[SVXUNO_SERVICEID_LASTID+1] = { nullptr };
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/unodraw/unoshap2.cxx b/svx/source/unodraw/unoshap2.cxx
new file mode 100644
index 000000000..6972819c3
--- /dev/null
+++ b/svx/source/unodraw/unoshap2.cxx
@@ -0,0 +1,1798 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * 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/PolyPolygonBezierCoords.hpp>
+#include <com/sun/star/lang/DisposedException.hpp>
+#include <com/sun/star/lang/IndexOutOfBoundsException.hpp>
+#include <com/sun/star/awt/FontSlant.hpp>
+#include <com/sun/star/style/VerticalAlignment.hpp>
+#include <com/sun/star/drawing/TextVerticalAdjust.hpp>
+#include <com/sun/star/drawing/XEnhancedCustomShapeDefaulter.hpp>
+#include <com/sun/star/awt/TextAlign.hpp>
+#include <com/sun/star/style/ParagraphAdjust.hpp>
+#include <com/sun/star/drawing/PointSequenceSequence.hpp>
+#include <com/sun/star/drawing/PointSequence.hpp>
+#include <com/sun/star/drawing/PolygonKind.hpp>
+#include <com/sun/star/graphic/XGraphic.hpp>
+#include <com/sun/star/drawing/BarCode.hpp>
+#include <o3tl/any.hxx>
+#include <o3tl/safeint.hxx>
+#include <vcl/svapp.hxx>
+#include <vcl/wmf.hxx>
+#include <vcl/cvtgrf.hxx>
+#include <vcl/GraphicLoader.hxx>
+
+#include <svx/svdpool.hxx>
+
+#include <editeng/unoprnms.hxx>
+#include <svx/unoshape.hxx>
+#include <svx/unopage.hxx>
+#include <svx/svdobj.hxx>
+#include <svx/svdpage.hxx>
+#include <svx/svdmodel.hxx>
+#include <svx/svdouno.hxx>
+#include "shapeimpl.hxx"
+#include <svx/unoshprp.hxx>
+#include <svx/svdoashp.hxx>
+#include <svx/svdviter.hxx>
+#include <svx/svdview.hxx>
+#include <svx/svdopath.hxx>
+#include <basegfx/matrix/b2dhommatrix.hxx>
+#include <basegfx/polygon/b2dpolygon.hxx>
+#include <basegfx/point/b2dpoint.hxx>
+#include <basegfx/polygon/b2dpolygontools.hxx>
+#include <basegfx/polygon/b2dpolypolygontools.hxx>
+#include <com/sun/star/awt/XBitmap.hpp>
+#include <svx/svdograf.hxx>
+#include <sal/log.hxx>
+#include <cppuhelper/queryinterface.hxx>
+#include <tools/stream.hxx>
+
+
+#include <memory>
+
+using namespace ::osl;
+using namespace ::cppu;
+using namespace ::com::sun::star;
+using namespace ::com::sun::star::uno;
+using namespace ::com::sun::star::lang;
+using namespace ::com::sun::star::container;
+
+#define QUERYINT( xint ) \
+ if( rType == cppu::UnoType<xint>::get() ) \
+ aAny <<= Reference< xint >(this)
+
+SvxShapeGroup::SvxShapeGroup(SdrObject* pObj, SvxDrawPage* pDrawPage)
+ : SvxShapeGroupAnyD(pObj, getSvxMapProvider().GetMap(SVXMAP_GROUP), getSvxMapProvider().GetPropertySet(SVXMAP_GROUP, SdrObject::GetGlobalDrawObjectItemPool()))
+ , mxPage(pDrawPage)
+{
+}
+
+SvxShapeGroup::~SvxShapeGroup() noexcept
+{
+}
+
+void SvxShapeGroup::Create( SdrObject* pNewObj, SvxDrawPage* pNewPage )
+{
+ SvxShape::Create( pNewObj, pNewPage );
+ mxPage = pNewPage;
+}
+
+
+uno::Any SAL_CALL SvxShapeGroup::queryInterface( const uno::Type & rType )
+{
+ return SvxShape::queryInterface( rType );
+}
+
+uno::Any SAL_CALL SvxShapeGroup::queryAggregation( const uno::Type & rType )
+{
+ uno::Any aAny;
+
+ QUERYINT( drawing::XShapeGroup );
+ else QUERYINT( drawing::XShapes );
+ else QUERYINT( drawing::XShapes2 );
+ else QUERYINT( container::XIndexAccess );
+ else QUERYINT( container::XElementAccess );
+ else
+ return SvxShape::queryAggregation( rType );
+
+ return aAny;
+}
+
+uno::Sequence< sal_Int8 > SAL_CALL SvxShapeGroup::getImplementationId()
+{
+ return css::uno::Sequence<sal_Int8>();
+}
+
+// css::drawing::XShape
+
+
+OUString SAL_CALL SvxShapeGroup::getShapeType()
+{
+ return SvxShape::getShapeType();
+}
+
+awt::Point SAL_CALL SvxShapeGroup::getPosition()
+{
+ return SvxShape::getPosition();
+}
+
+
+void SAL_CALL SvxShapeGroup::setPosition( const awt::Point& Position )
+{
+ SvxShape::setPosition(Position);
+}
+
+
+awt::Size SAL_CALL SvxShapeGroup::getSize()
+{
+ return SvxShape::getSize();
+}
+
+
+void SAL_CALL SvxShapeGroup::setSize( const awt::Size& rSize )
+{
+ SvxShape::setSize( rSize );
+}
+
+// drawing::XShapeGroup
+
+
+void SAL_CALL SvxShapeGroup::enterGroup( )
+{
+ // Todo
+// pDrView->EnterMarkedGroup();
+}
+
+
+void SAL_CALL SvxShapeGroup::leaveGroup( )
+{
+ // Todo
+// pDrView->LeaveOneGroup();
+}
+
+void SvxShapeGroup::addUnoShape( const uno::Reference< drawing::XShape >& xShape, size_t nPos )
+{
+ SvxShape* pShape = comphelper::getFromUnoTunnel<SvxShape>( xShape );
+ if (!pShape)
+ {
+ OSL_FAIL("could not add XShape to group shape!");
+ return;
+ }
+ addShape(*pShape, nPos);
+}
+
+void SvxShapeGroup::addShape( SvxShape& rShape )
+{
+ addShape(rShape, SAL_MAX_SIZE);
+}
+
+void SvxShapeGroup::addShape( SvxShape& rShape, size_t nPos )
+{
+ if (!HasSdrObject() || !mxPage.is())
+ {
+ OSL_FAIL("could not add XShape to group shape!");
+ return;
+ }
+
+ SdrObject* pSdrShape = rShape.GetSdrObject();
+ if( pSdrShape == nullptr )
+ pSdrShape = mxPage->CreateSdrObject_( &rShape );
+
+ if( pSdrShape->IsInserted() )
+ pSdrShape->getParentSdrObjListFromSdrObject()->RemoveObject( pSdrShape->GetOrdNum() );
+
+ GetSdrObject()->GetSubList()->InsertObject(pSdrShape, nPos);
+ // TTTT Was created using mpModel in CreateSdrObject_ above
+ // TTTT may be good to add an assertion here for the future
+ // pSdrShape->SetModel(GetSdrObject()->GetModel());
+
+ // #85922# It makes no sense to set the layer asked
+ // from the group object since this is an iteration
+ // over the contained objects. In consequence, this
+ // statement erases all layer information from the draw
+ // objects. Layers need to be set at draw objects directly
+ // and have nothing to do with grouping at all.
+ // pSdrShape->SetLayer(pObject->GetLayer());
+
+ // Establish connection between new SdrObject and its wrapper before
+ // inserting the new shape into the group. There a new wrapper
+ // would be created when this connection would not already exist.
+ rShape.Create( pSdrShape, mxPage.get() );
+
+ GetSdrObject()->getSdrModelFromSdrObject().SetChanged();
+}
+
+// XShapes
+void SAL_CALL SvxShapeGroup::add( const uno::Reference< drawing::XShape >& xShape )
+{
+ ::SolarMutexGuard aGuard;
+
+ // Add to the top of the stack (i.e. bottom of the list) by default.
+ addUnoShape(xShape, SAL_MAX_SIZE);
+}
+
+
+void SAL_CALL SvxShapeGroup::remove( const uno::Reference< drawing::XShape >& xShape )
+{
+ ::SolarMutexGuard aGuard;
+
+ SdrObject* pSdrShape = SdrObject::getSdrObjectFromXShape( xShape );
+
+ if( !HasSdrObject() || pSdrShape == nullptr || pSdrShape->getParentSdrObjectFromSdrObject() != GetSdrObject() )
+ throw uno::RuntimeException();
+
+ SdrObjList& rList = *pSdrShape->getParentSdrObjListFromSdrObject();
+
+ const size_t nObjCount = rList.GetObjCount();
+ size_t nObjNum = 0;
+ while( nObjNum < nObjCount )
+ {
+ if(rList.GetObj( nObjNum ) == pSdrShape )
+ break;
+ nObjNum++;
+ }
+
+ if( nObjNum < nObjCount )
+ {
+ // #i29181#
+ // If the SdrObject which is about to be deleted is in any selection,
+ // deselect it first.
+ SdrViewIter aIter( pSdrShape );
+
+ for ( SdrView* pView = aIter.FirstView(); pView; pView = aIter.NextView() )
+ {
+ if(SAL_MAX_SIZE != pView->TryToFindMarkedObject(pSdrShape))
+ {
+ pView->MarkObj(pSdrShape, pView->GetSdrPageView(), true);
+ }
+ }
+
+ SdrObject* pObject = rList.NbcRemoveObject( nObjNum );
+ SdrObject::Free( pObject );
+ }
+ else
+ {
+ SAL_WARN( "svx", "Fatality! SdrObject is not belonging to its SdrObjList! [CL]" );
+ }
+
+ GetSdrObject()->getSdrModelFromSdrObject().SetChanged();
+}
+
+void SAL_CALL SvxShapeGroup::addTop( const uno::Reference< drawing::XShape >& xShape )
+{
+ SolarMutexGuard aGuard;
+
+ // Add to the top of the stack (i.e. bottom of the list).
+ addUnoShape(xShape, SAL_MAX_SIZE);
+}
+
+void SAL_CALL SvxShapeGroup::addBottom( const uno::Reference< drawing::XShape >& xShape )
+{
+ SolarMutexGuard aGuard;
+
+ // Add to the bottom of the stack (i.e. top of the list).
+ addUnoShape(xShape, 0);
+}
+
+// XIndexAccess
+
+
+sal_Int32 SAL_CALL SvxShapeGroup::getCount()
+{
+ ::SolarMutexGuard aGuard;
+
+ if(!HasSdrObject() || !GetSdrObject()->GetSubList())
+ throw uno::RuntimeException();
+
+ sal_Int32 nRetval = GetSdrObject()->GetSubList()->GetObjCount();
+ return nRetval;
+}
+
+
+uno::Any SAL_CALL SvxShapeGroup::getByIndex( sal_Int32 Index )
+{
+ ::SolarMutexGuard aGuard;
+
+ if( !HasSdrObject() || GetSdrObject()->GetSubList() == nullptr )
+ throw uno::RuntimeException();
+
+ if( Index<0 || GetSdrObject()->GetSubList()->GetObjCount() <= o3tl::make_unsigned(Index) )
+ throw lang::IndexOutOfBoundsException();
+
+ SdrObject* pDestObj = GetSdrObject()->GetSubList()->GetObj( Index );
+
+ if(pDestObj == nullptr)
+ throw lang::IndexOutOfBoundsException();
+
+ Reference< drawing::XShape > xShape( pDestObj->getUnoShape(), uno::UNO_QUERY );
+ return uno::Any( xShape );
+}
+
+// css::container::XElementAccess
+
+
+uno::Type SAL_CALL SvxShapeGroup::getElementType()
+{
+ return cppu::UnoType<drawing::XShape>::get();
+}
+
+
+sal_Bool SAL_CALL SvxShapeGroup::hasElements()
+{
+ ::SolarMutexGuard aGuard;
+
+ return HasSdrObject() && GetSdrObject()->GetSubList() && (GetSdrObject()->GetSubList()->GetObjCount() > 0);
+}
+
+SvxShapeConnector::SvxShapeConnector(SdrObject* pObj)
+ : SvxShapeText( pObj, getSvxMapProvider().GetMap(SVXMAP_CONNECTOR), getSvxMapProvider().GetPropertySet(SVXMAP_CONNECTOR, SdrObject::GetGlobalDrawObjectItemPool()) )
+{
+}
+
+
+SvxShapeConnector::~SvxShapeConnector() noexcept
+{
+}
+
+
+uno::Any SAL_CALL SvxShapeConnector::queryInterface( const uno::Type & rType )
+{
+ return SvxShapeText::queryInterface( rType );
+}
+
+uno::Any SAL_CALL SvxShapeConnector::queryAggregation( const uno::Type & rType )
+{
+ uno::Any aAny;
+
+ QUERYINT( drawing::XConnectorShape );
+ else
+ return SvxShapeText::queryAggregation( rType );
+
+ return aAny;
+}
+
+// XTypeProvider
+
+uno::Sequence< uno::Type > SAL_CALL SvxShapeConnector::getTypes()
+{
+ return SvxShape::getTypes();
+}
+
+uno::Sequence< sal_Int8 > SAL_CALL SvxShapeConnector::getImplementationId()
+{
+ return css::uno::Sequence<sal_Int8>();
+}
+
+// css::drawing::XShape
+
+
+OUString SAL_CALL SvxShapeConnector::getShapeType()
+{
+ return SvxShapeText::getShapeType();
+}
+
+awt::Point SAL_CALL SvxShapeConnector::getPosition()
+{
+ return SvxShapeText::getPosition();
+}
+
+
+void SAL_CALL SvxShapeConnector::setPosition( const awt::Point& Position )
+{
+ SvxShapeText::setPosition(Position);
+}
+
+
+awt::Size SAL_CALL SvxShapeConnector::getSize()
+{
+ return SvxShapeText::getSize();
+}
+
+
+void SAL_CALL SvxShapeConnector::setSize( const awt::Size& rSize )
+{
+ SvxShapeText::setSize( rSize );
+}
+
+
+// XConnectorShape
+
+void SAL_CALL SvxShapeConnector::connectStart( const uno::Reference< drawing::XConnectableShape >& xShape, drawing::ConnectionType )
+{
+ ::SolarMutexGuard aGuard;
+
+ Reference< drawing::XShape > xRef( xShape, UNO_QUERY );
+ SdrObject* pSdrObject = SdrObject::getSdrObjectFromXShape( xRef );
+
+ if( pSdrObject )
+ GetSdrObject()->ConnectToNode( true, pSdrObject );
+
+ GetSdrObject()->getSdrModelFromSdrObject().SetChanged();
+}
+
+
+void SAL_CALL SvxShapeConnector::connectEnd( const uno::Reference< drawing::XConnectableShape >& xShape, drawing::ConnectionType )
+{
+ ::SolarMutexGuard aGuard;
+
+ Reference< drawing::XShape > xRef( xShape, UNO_QUERY );
+ SdrObject* pSdrObject = SdrObject::getSdrObjectFromXShape( xRef );
+
+ if( HasSdrObject() && pSdrObject )
+ GetSdrObject()->ConnectToNode( false, pSdrObject );
+
+ GetSdrObject()->getSdrModelFromSdrObject().SetChanged();
+}
+
+
+void SAL_CALL SvxShapeConnector::disconnectBegin( const uno::Reference< drawing::XConnectableShape >& )
+{
+ ::SolarMutexGuard aGuard;
+
+ if(HasSdrObject())
+ GetSdrObject()->DisconnectFromNode( true );
+
+ GetSdrObject()->getSdrModelFromSdrObject().SetChanged();
+}
+
+
+void SAL_CALL SvxShapeConnector::disconnectEnd( const uno::Reference< drawing::XConnectableShape >& )
+{
+ ::SolarMutexGuard aGuard;
+
+ if(HasSdrObject())
+ GetSdrObject()->DisconnectFromNode( false );
+
+ GetSdrObject()->getSdrModelFromSdrObject().SetChanged();
+}
+
+SvxShapeControl::SvxShapeControl(SdrObject* pObj)
+ : SvxShapeText( pObj, getSvxMapProvider().GetMap(SVXMAP_CONTROL), getSvxMapProvider().GetPropertySet(SVXMAP_CONTROL, SdrObject::GetGlobalDrawObjectItemPool()) )
+{
+ setShapeKind( SdrObjKind::UNO );
+}
+
+
+SvxShapeControl::~SvxShapeControl() noexcept
+{
+}
+
+
+uno::Any SAL_CALL SvxShapeControl::queryInterface( const uno::Type & rType )
+{
+ return SvxShapeText::queryInterface( rType );
+}
+
+uno::Any SAL_CALL SvxShapeControl::queryAggregation( const uno::Type & rType )
+{
+ uno::Any aAny;
+
+ QUERYINT( drawing::XControlShape );
+ else
+ return SvxShapeText::queryAggregation( rType );
+
+ return aAny;
+}
+
+// XTypeProvider
+
+uno::Sequence< uno::Type > SAL_CALL SvxShapeControl::getTypes()
+{
+ return SvxShape::getTypes();
+}
+
+uno::Sequence< sal_Int8 > SAL_CALL SvxShapeControl::getImplementationId()
+{
+ return css::uno::Sequence<sal_Int8>();
+}
+
+// css::drawing::XShape
+
+
+OUString SAL_CALL SvxShapeControl::getShapeType()
+{
+ return SvxShapeText::getShapeType();
+}
+
+awt::Point SAL_CALL SvxShapeControl::getPosition()
+{
+ return SvxShapeText::getPosition();
+}
+
+
+void SAL_CALL SvxShapeControl::setPosition( const awt::Point& Position )
+{
+ SvxShapeText::setPosition(Position);
+}
+
+
+awt::Size SAL_CALL SvxShapeControl::getSize()
+{
+ return SvxShapeText::getSize();
+}
+
+
+void SAL_CALL SvxShapeControl::setSize( const awt::Size& rSize )
+{
+ SvxShapeText::setSize( rSize );
+}
+
+
+// XControlShape
+
+Reference< awt::XControlModel > SAL_CALL SvxShapeControl::getControl()
+{
+ ::SolarMutexGuard aGuard;
+
+ Reference< awt::XControlModel > xModel;
+
+ SdrUnoObj* pUnoObj = dynamic_cast< SdrUnoObj * >(GetSdrObject());
+ if( pUnoObj )
+ xModel = pUnoObj->GetUnoControlModel();
+
+ return xModel;
+}
+
+
+void SAL_CALL SvxShapeControl::setControl( const Reference< awt::XControlModel >& xControl )
+{
+ ::SolarMutexGuard aGuard;
+
+ SdrUnoObj* pUnoObj = dynamic_cast< SdrUnoObj * >(GetSdrObject());
+ if( pUnoObj )
+ pUnoObj->SetUnoControlModel( xControl );
+
+ GetSdrObject()->getSdrModelFromSdrObject().SetChanged();
+}
+
+struct
+{
+ OUString msAPIName;
+ OUString msFormName;
+}
+const SvxShapeControlPropertyMapping[] =
+{
+ // Warning: The first entry must be FontSlant because the any needs to be converted
+ { UNO_NAME_EDIT_CHAR_POSTURE, "FontSlant" }, // const sal_Int16 => css::awt::FontSlant
+ { UNO_NAME_EDIT_CHAR_FONTNAME, "FontName" },
+ { UNO_NAME_EDIT_CHAR_FONTSTYLENAME, "FontStyleName" },
+ { UNO_NAME_EDIT_CHAR_FONTFAMILY, "FontFamily" },
+ { UNO_NAME_EDIT_CHAR_FONTCHARSET, "FontCharset" },
+ { UNO_NAME_EDIT_CHAR_HEIGHT, "FontHeight" },
+ { UNO_NAME_EDIT_CHAR_FONTPITCH, "FontPitch" },
+ { UNO_NAME_EDIT_CHAR_WEIGHT, "FontWeight" },
+ { UNO_NAME_EDIT_CHAR_UNDERLINE, "FontUnderline" },
+ { UNO_NAME_EDIT_CHAR_STRIKEOUT, "FontStrikeout" },
+ { "CharKerning", "FontKerning" },
+ { "CharWordMode", "FontWordLineMode" },
+ { UNO_NAME_EDIT_CHAR_COLOR, "TextColor" },
+ { "CharBackColor", "CharBackColor" },
+ { "CharBackTransparent", "CharBackTransparent" },
+ { UNO_NAME_TEXT_CHAINNEXTNAME, UNO_NAME_TEXT_CHAINNEXTNAME },
+ { "CharRelief", "FontRelief" },
+ { "CharUnderlineColor", "TextLineColor" },
+ { UNO_NAME_EDIT_PARA_ADJUST, "Align" },
+ { "TextVerticalAdjust", "VerticalAlign" },
+ { "ControlBackground", "BackgroundColor" },
+ { "ControlSymbolColor", "SymbolColor" },
+ { "ControlBorder", "Border" },
+ { "ControlBorderColor", "BorderColor" },
+ { "ControlTextEmphasis", "FontEmphasisMark" },
+ { "ImageScaleMode", "ScaleMode" },
+ { "ControlWritingMode", "WritingMode" },
+ //added for exporting OCX control
+ { "ControlTypeinMSO", "ControlTypeinMSO" },
+ { "ObjIDinMSO", "ObjIDinMSO" },
+ { "CharCaseMap", "CharCaseMap" },
+ { "CharColorTheme", "CharColorTheme" },
+ { "CharColorTintOrShade", "CharColorTintOrShade" },
+};
+
+namespace
+{
+ bool lcl_convertPropertyName( const OUString& rApiName, OUString& rInternalName )
+ {
+ for( const auto & rEntry : SvxShapeControlPropertyMapping )
+ {
+ if( rApiName.reverseCompareTo( rEntry.msAPIName ) == 0 )
+ {
+ rInternalName = rEntry.msFormName;
+ }
+ }
+ return !rInternalName.isEmpty();
+ }
+
+ struct EnumConversionMap
+ {
+ style::ParagraphAdjust nAPIValue;
+ sal_Int16 nFormValue;
+ };
+
+ EnumConversionMap const aMapAdjustToAlign[] =
+ {
+ // note that order matters:
+ // lcl_convertTextAlignmentToParaAdjustment and lcl_convertParaAdjustmentToTextAlignment search this map from the _beginning_
+ // and use the first matching entry
+ {style::ParagraphAdjust_LEFT, sal_Int16(awt::TextAlign::LEFT)},
+ {style::ParagraphAdjust_CENTER, sal_Int16(awt::TextAlign::CENTER)},
+ {style::ParagraphAdjust_RIGHT, sal_Int16(awt::TextAlign::RIGHT)},
+ {style::ParagraphAdjust_BLOCK, sal_Int16(awt::TextAlign::RIGHT)},
+ {style::ParagraphAdjust_STRETCH, sal_Int16(awt::TextAlign::LEFT)},
+ {style::ParagraphAdjust(-1),-1}
+ };
+
+ void lcl_convertTextAlignmentToParaAdjustment( Any& _rValue )
+ {
+ sal_Int16 nValue = sal_Int16();
+ OSL_VERIFY( _rValue >>= nValue );
+
+ for ( auto const & rEntry : aMapAdjustToAlign )
+ if ( nValue == rEntry.nFormValue )
+ {
+ _rValue <<= static_cast<sal_uInt16>(rEntry.nAPIValue);
+ return;
+ }
+ }
+
+ void lcl_convertParaAdjustmentToTextAlignment( Any& _rValue )
+ {
+ sal_Int32 nValue = 0;
+ OSL_VERIFY( _rValue >>= nValue );
+
+ for ( auto const & rEntry : aMapAdjustToAlign )
+ if ( static_cast<style::ParagraphAdjust>(nValue) == rEntry.nAPIValue )
+ {
+ _rValue <<= rEntry.nFormValue;
+ return;
+ }
+ }
+
+ void convertVerticalAdjustToVerticalAlign( Any& _rValue )
+ {
+ if ( !_rValue.hasValue() )
+ return;
+
+ drawing::TextVerticalAdjust eAdjust = drawing::TextVerticalAdjust_TOP;
+ style::VerticalAlignment eAlign = style::VerticalAlignment_TOP;
+ if ( !( _rValue >>= eAdjust ) )
+ throw lang::IllegalArgumentException();
+ switch ( eAdjust )
+ {
+ case drawing::TextVerticalAdjust_TOP: eAlign = style::VerticalAlignment_TOP; break;
+ case drawing::TextVerticalAdjust_BOTTOM: eAlign = style::VerticalAlignment_BOTTOM; break;
+ default: eAlign = style::VerticalAlignment_MIDDLE; break;
+ }
+ _rValue <<= eAlign;
+ }
+
+ void convertVerticalAlignToVerticalAdjust( Any& _rValue )
+ {
+ if ( !_rValue.hasValue() )
+ return;
+ style::VerticalAlignment eAlign = style::VerticalAlignment_TOP;
+ drawing::TextVerticalAdjust eAdjust = drawing::TextVerticalAdjust_TOP;
+ OSL_VERIFY( _rValue >>= eAlign );
+ switch ( eAlign )
+ {
+ case style::VerticalAlignment_TOP: eAdjust = drawing::TextVerticalAdjust_TOP; break;
+ case style::VerticalAlignment_BOTTOM: eAdjust = drawing::TextVerticalAdjust_BOTTOM; break;
+ default: eAdjust = drawing::TextVerticalAdjust_CENTER; break;
+ }
+ _rValue <<= eAdjust;
+ }
+}
+
+void SAL_CALL SvxShapeControl::setPropertyValue( const OUString& aPropertyName, const uno::Any& aValue )
+{
+ OUString aFormsName;
+ if ( lcl_convertPropertyName( aPropertyName, aFormsName ) )
+ {
+ uno::Reference< beans::XPropertySet > xControl( getControl(), uno::UNO_QUERY );
+ if( xControl.is() )
+ {
+ uno::Reference< beans::XPropertySetInfo > xInfo( xControl->getPropertySetInfo() );
+ if( xInfo.is() && xInfo->hasPropertyByName( aFormsName ) )
+ {
+ uno::Any aConvertedValue( aValue );
+ if ( aFormsName == "FontSlant" )
+ {
+ awt::FontSlant nSlant;
+ if( !(aValue >>= nSlant ) )
+ throw lang::IllegalArgumentException();
+ aConvertedValue <<= static_cast<sal_Int16>(nSlant);
+ }
+ else if ( aFormsName == "Align" )
+ {
+ lcl_convertParaAdjustmentToTextAlignment( aConvertedValue );
+ }
+ else if ( aFormsName == "VerticalAlign" )
+ {
+ convertVerticalAdjustToVerticalAlign( aConvertedValue );
+ }
+
+ xControl->setPropertyValue( aFormsName, aConvertedValue );
+ }
+ }
+ }
+ else
+ {
+ SvxShape::setPropertyValue( aPropertyName, aValue );
+ }
+}
+
+uno::Any SAL_CALL SvxShapeControl::getPropertyValue( const OUString& aPropertyName )
+{
+ OUString aFormsName;
+ if ( lcl_convertPropertyName( aPropertyName, aFormsName ) )
+ {
+ uno::Reference< beans::XPropertySet > xControl( getControl(), uno::UNO_QUERY );
+
+ uno::Any aValue;
+ if( xControl.is() )
+ {
+ uno::Reference< beans::XPropertySetInfo > xInfo( xControl->getPropertySetInfo() );
+ if( xInfo.is() && xInfo->hasPropertyByName( aFormsName ) )
+ {
+ aValue = xControl->getPropertyValue( aFormsName );
+ if ( aFormsName == "FontSlant" )
+ {
+ awt::FontSlant eSlant = awt::FontSlant_NONE;
+ sal_Int16 nSlant = sal_Int16();
+ if ( aValue >>= nSlant )
+ {
+ eSlant = static_cast<awt::FontSlant>(nSlant);
+ }
+ else
+ {
+ OSL_VERIFY( aValue >>= eSlant );
+ }
+ aValue <<= eSlant;
+ }
+ else if ( aFormsName == "Align" )
+ {
+ lcl_convertTextAlignmentToParaAdjustment( aValue );
+ }
+ else if ( aFormsName == "VerticalAlign" )
+ {
+ convertVerticalAlignToVerticalAdjust( aValue );
+ }
+ }
+ }
+
+ return aValue;
+ }
+ else
+ {
+ return SvxShape::getPropertyValue( aPropertyName );
+ }
+
+}
+
+// XPropertyState
+beans::PropertyState SAL_CALL SvxShapeControl::getPropertyState( const OUString& PropertyName )
+{
+ OUString aFormsName;
+ if ( lcl_convertPropertyName( PropertyName, aFormsName ) )
+ {
+ uno::Reference< beans::XPropertyState > xControl( getControl(), uno::UNO_QUERY );
+ uno::Reference< beans::XPropertySet > xPropSet( getControl(), uno::UNO_QUERY );
+
+ if( xControl.is() && xPropSet.is() )
+ {
+ uno::Reference< beans::XPropertySetInfo > xInfo( xPropSet->getPropertySetInfo() );
+ if( xInfo.is() && xInfo->hasPropertyByName( aFormsName ) )
+ {
+ return xControl->getPropertyState( aFormsName );
+ }
+ }
+
+ return beans::PropertyState_DEFAULT_VALUE;
+ }
+ else
+ {
+ return SvxShape::getPropertyState( PropertyName );
+ }
+}
+
+void SAL_CALL SvxShapeControl::setPropertyToDefault( const OUString& PropertyName )
+{
+ OUString aFormsName;
+ if ( lcl_convertPropertyName( PropertyName, aFormsName ) )
+ {
+ uno::Reference< beans::XPropertyState > xControl( getControl(), uno::UNO_QUERY );
+ uno::Reference< beans::XPropertySet > xPropSet( getControl(), uno::UNO_QUERY );
+
+ if( xControl.is() && xPropSet.is() )
+ {
+ uno::Reference< beans::XPropertySetInfo > xInfo( xPropSet->getPropertySetInfo() );
+ if( xInfo.is() && xInfo->hasPropertyByName( aFormsName ) )
+ {
+ xControl->setPropertyToDefault( aFormsName );
+ }
+ }
+ }
+ else
+ {
+ SvxShape::setPropertyToDefault( PropertyName );
+ }
+}
+
+uno::Any SAL_CALL SvxShapeControl::getPropertyDefault( const OUString& aPropertyName )
+{
+ OUString aFormsName;
+ if ( lcl_convertPropertyName( aPropertyName, aFormsName ) )
+ {
+ uno::Reference< beans::XPropertyState > xControl( getControl(), uno::UNO_QUERY );
+
+ if( xControl.is() )
+ {
+ Any aDefault( xControl->getPropertyDefault( aFormsName ) );
+ if ( aFormsName == "FontSlant" )
+ {
+ sal_Int16 nSlant( 0 );
+ aDefault >>= nSlant;
+ aDefault <<= static_cast<awt::FontSlant>(nSlant);
+ }
+ else if ( aFormsName == "Align" )
+ {
+ lcl_convertTextAlignmentToParaAdjustment( aDefault );
+ }
+ else if ( aFormsName == "VerticalAlign" )
+ {
+ convertVerticalAlignToVerticalAdjust( aDefault );
+ }
+ return aDefault;
+ }
+
+ throw beans::UnknownPropertyException( aPropertyName, static_cast<cppu::OWeakObject*>(this));
+ }
+ else
+ {
+ return SvxShape::getPropertyDefault( aPropertyName );
+ }
+}
+
+SvxShapeDimensioning::SvxShapeDimensioning(SdrObject* pObj)
+ : SvxShapeText( pObj, getSvxMapProvider().GetMap(SVXMAP_DIMENSIONING), getSvxMapProvider().GetPropertySet(SVXMAP_DIMENSIONING, SdrObject::GetGlobalDrawObjectItemPool()) )
+{
+}
+
+SvxShapeDimensioning::~SvxShapeDimensioning() noexcept
+{
+}
+
+SvxShapeCircle::SvxShapeCircle(SdrObject* pObj)
+ : SvxShapeText( pObj, getSvxMapProvider().GetMap(SVXMAP_CIRCLE), getSvxMapProvider().GetPropertySet(SVXMAP_CIRCLE, SdrObject::GetGlobalDrawObjectItemPool()) )
+{
+}
+
+SvxShapeCircle::~SvxShapeCircle() noexcept
+{
+}
+
+//////////////////////////////////////////////////////////////////////////////
+
+SvxShapePolyPolygon::SvxShapePolyPolygon(
+ SdrObject* pObj)
+: SvxShapeText(
+ pObj,
+ getSvxMapProvider().GetMap(SVXMAP_POLYPOLYGON),
+ getSvxMapProvider().GetPropertySet(SVXMAP_POLYPOLYGON, SdrObject::GetGlobalDrawObjectItemPool()))
+{
+}
+
+SvxShapePolyPolygon::~SvxShapePolyPolygon() noexcept
+{
+}
+
+bool SvxShapePolyPolygon::setPropertyValueImpl( const OUString& rName, const SfxItemPropertyMapEntry* pProperty, const css::uno::Any& rValue )
+{
+ switch( pProperty->nWID )
+ {
+ case OWN_ATTR_VALUE_POLYPOLYGONBEZIER:
+ {
+ if( auto s = o3tl::tryAccess<drawing::PolyPolygonBezierCoords>(rValue) )
+ {
+ basegfx::B2DPolyPolygon aNewPolyPolygon(
+ basegfx::utils::UnoPolyPolygonBezierCoordsToB2DPolyPolygon(*s));
+
+ // tdf#117145 metric of SdrModel is app-specific, metric of UNO API is 100thmm
+ ForceMetricToItemPoolMetric(aNewPolyPolygon);
+
+ SetPolygon(aNewPolyPolygon);
+ return true;
+ }
+ break;
+ }
+ case OWN_ATTR_VALUE_POLYPOLYGON:
+ {
+ if( auto s = o3tl::tryAccess<drawing::PointSequenceSequence>(rValue) )
+ {
+ basegfx::B2DPolyPolygon aNewPolyPolygon(
+ basegfx::utils::UnoPointSequenceSequenceToB2DPolyPolygon(*s));
+
+ // tdf#117145 metric of SdrModel is app-specific, metric of UNO API is 100thmm
+ ForceMetricToItemPoolMetric(aNewPolyPolygon);
+
+ SetPolygon(aNewPolyPolygon);
+ return true;
+ }
+ break;
+ }
+ case OWN_ATTR_BASE_GEOMETRY:
+ {
+ drawing::PointSequenceSequence aPointSequenceSequence;
+ drawing::PolyPolygonBezierCoords aPolyPolygonBezierCoords;
+
+ if( rValue >>= aPointSequenceSequence)
+ {
+ if( HasSdrObject() )
+ {
+ basegfx::B2DPolyPolygon aNewPolyPolygon;
+ basegfx::B2DHomMatrix aNewHomogenMatrix;
+
+ GetSdrObject()->TRGetBaseGeometry(aNewHomogenMatrix, aNewPolyPolygon);
+ aNewPolyPolygon = basegfx::utils::UnoPointSequenceSequenceToB2DPolyPolygon(aPointSequenceSequence);
+
+ // tdf#117145 metric of SdrModel is app-specific, metric of UNO API is 100thmm
+ // Need to adapt aNewPolyPolygon from 100thmm to app-specific
+ ForceMetricToItemPoolMetric(aNewPolyPolygon);
+
+ GetSdrObject()->TRSetBaseGeometry(aNewHomogenMatrix, aNewPolyPolygon);
+ }
+ return true;
+ }
+ else if( rValue >>= aPolyPolygonBezierCoords)
+ {
+ if( HasSdrObject() )
+ {
+ basegfx::B2DPolyPolygon aNewPolyPolygon;
+ basegfx::B2DHomMatrix aNewHomogenMatrix;
+
+ GetSdrObject()->TRGetBaseGeometry(aNewHomogenMatrix, aNewPolyPolygon);
+ aNewPolyPolygon = basegfx::utils::UnoPolyPolygonBezierCoordsToB2DPolyPolygon(aPolyPolygonBezierCoords);
+
+ // tdf#117145 metric of SdrModel is app-specific, metric of UNO API is 100thmm
+ ForceMetricToItemPoolMetric(aNewPolyPolygon);
+
+ GetSdrObject()->TRSetBaseGeometry(aNewHomogenMatrix, aNewPolyPolygon);
+ }
+ return true;
+ }
+ break;
+ }
+ case OWN_ATTR_VALUE_POLYGON:
+ {
+ if( auto pSequence = o3tl::tryAccess<drawing::PointSequence>(rValue) )
+ {
+ // prepare new polygon
+ basegfx::B2DPolygon aNewPolygon;
+
+ // get pointer to arrays
+ const awt::Point* pArray = pSequence->getConstArray();
+ const awt::Point* pArrayEnd = pArray + pSequence->getLength();
+
+ for(;pArray != pArrayEnd;++pArray)
+ {
+ aNewPolygon.append(basegfx::B2DPoint(pArray->X, pArray->Y));
+ }
+
+ // check for closed state flag
+ basegfx::utils::checkClosed(aNewPolygon);
+
+ // tdf#117145 metric of SdrModel is app-specific, metric of UNO API is 100thmm
+ basegfx::B2DPolyPolygon aNewPolyPolygon(aNewPolygon);
+ ForceMetricToItemPoolMetric(aNewPolyPolygon);
+
+ // set polygon
+ SetPolygon(aNewPolyPolygon);
+ return true;
+ }
+ break;
+ }
+ default:
+ return SvxShapeText::setPropertyValueImpl( rName, pProperty, rValue );
+ }
+
+ throw lang::IllegalArgumentException();
+}
+
+bool SvxShapePolyPolygon::getPropertyValueImpl( const OUString& rName, const SfxItemPropertyMapEntry* pProperty,
+ css::uno::Any& rValue )
+{
+ switch( pProperty->nWID )
+ {
+ case OWN_ATTR_VALUE_POLYPOLYGONBEZIER:
+ {
+ // pack a tools::PolyPolygon in a struct tools::PolyPolygon
+ basegfx::B2DPolyPolygon aPolyPoly(GetPolygon());
+
+ // tdf#117145 metric of SdrModel is app-specific, metric of UNO API is 100thmm
+ ForceMetricTo100th_mm(aPolyPoly);
+
+ drawing::PolyPolygonBezierCoords aRetval;
+ basegfx::utils::B2DPolyPolygonToUnoPolyPolygonBezierCoords(aPolyPoly, aRetval);
+
+ rValue <<= aRetval;
+ break;
+ }
+ case OWN_ATTR_VALUE_POLYPOLYGON:
+ {
+ // pack a tools::PolyPolygon in a struct tools::PolyPolygon
+ basegfx::B2DPolyPolygon aPolyPoly(GetPolygon());
+
+ // tdf#117145 metric of SdrModel is app-specific, metric of UNO API is 100thmm
+ ForceMetricTo100th_mm(aPolyPoly);
+
+ drawing::PointSequenceSequence aRetval( aPolyPoly.count() );
+ basegfx::utils::B2DPolyPolygonToUnoPointSequenceSequence(aPolyPoly, aRetval);
+
+ rValue <<= aRetval;
+ break;
+ }
+ case OWN_ATTR_BASE_GEOMETRY:
+ {
+ // pack a tools::PolyPolygon in struct PolyPolygon
+ basegfx::B2DPolyPolygon aPolyPoly;
+ basegfx::B2DHomMatrix aNewHomogenMatrix;
+
+ if(HasSdrObject())
+ {
+ GetSdrObject()->TRGetBaseGeometry(aNewHomogenMatrix, aPolyPoly);
+
+ // tdf#117145 metric of SdrModel is app-specific, metric of UNO API is 100thmm
+ ForceMetricTo100th_mm(aPolyPoly);
+ }
+
+ if(aPolyPoly.areControlPointsUsed())
+ {
+ drawing::PolyPolygonBezierCoords aRetval;
+ basegfx::utils::B2DPolyPolygonToUnoPolyPolygonBezierCoords(aPolyPoly, aRetval);
+ rValue <<= aRetval;
+ }
+ else
+ {
+ drawing::PointSequenceSequence aRetval(aPolyPoly.count());
+ basegfx::utils::B2DPolyPolygonToUnoPointSequenceSequence(aPolyPoly, aRetval);
+ rValue <<= aRetval;
+ }
+ break;
+ }
+ case OWN_ATTR_VALUE_POLYGON:
+ {
+ // pack a tools::PolyPolygon in a struct tools::PolyPolygon
+ basegfx::B2DPolyPolygon aPolyPoly(GetPolygon());
+
+ // tdf#117145 metric of SdrModel is app-specific, metric of UNO API is 100thmm
+ ForceMetricTo100th_mm(aPolyPoly);
+
+ const sal_Int32 nCount(0 == aPolyPoly.count() ? 0 : aPolyPoly.getB2DPolygon(0).count());
+ drawing::PointSequence aRetval( nCount );
+
+ if( nCount > 0 )
+ {
+ // get single polygon
+ const basegfx::B2DPolygon& aPoly(aPolyPoly.getB2DPolygon(0));
+
+ // get pointer to arrays
+ awt::Point* pSequence = aRetval.getArray();
+
+ for(sal_Int32 b=0;b<nCount;b++)
+ {
+ const basegfx::B2DPoint aPoint(aPoly.getB2DPoint(b));
+ *pSequence++ = awt::Point( basegfx::fround(aPoint.getX()), basegfx::fround(aPoint.getY()) );
+ }
+ }
+
+ rValue <<= aRetval;
+ break;
+ }
+ case OWN_ATTR_VALUE_POLYGONKIND:
+ {
+ rValue <<= GetPolygonKind();
+ break;
+ }
+ default:
+ return SvxShapeText::getPropertyValueImpl( rName, pProperty, rValue );
+ }
+
+ return true;
+}
+
+drawing::PolygonKind SvxShapePolyPolygon::GetPolygonKind() const
+{
+ ::SolarMutexGuard aGuard;
+ drawing::PolygonKind aRetval(drawing::PolygonKind_LINE);
+
+ if(HasSdrObject())
+ {
+ switch(GetSdrObject()->GetObjIdentifier())
+ {
+ case SdrObjKind::Polygon: aRetval = drawing::PolygonKind_POLY; break;
+ case SdrObjKind::PolyLine: aRetval = drawing::PolygonKind_PLIN; break;
+ case SdrObjKind::SplineLine:
+ case SdrObjKind::PathLine: aRetval = drawing::PolygonKind_PATHLINE; break;
+ case SdrObjKind::SplineFill:
+ case SdrObjKind::PathFill: aRetval = drawing::PolygonKind_PATHFILL; break;
+ case SdrObjKind::FreehandLine: aRetval = drawing::PolygonKind_FREELINE; break;
+ case SdrObjKind::FreehandFill: aRetval = drawing::PolygonKind_FREEFILL; break;
+ default: break;
+ }
+ }
+
+ return aRetval;
+}
+
+void SvxShapePolyPolygon::SetPolygon(const basegfx::B2DPolyPolygon& rNew)
+{
+ ::SolarMutexGuard aGuard;
+
+ if(HasSdrObject())
+ static_cast<SdrPathObj*>(GetSdrObject())->SetPathPoly(rNew);
+}
+
+
+basegfx::B2DPolyPolygon SvxShapePolyPolygon::GetPolygon() const noexcept
+{
+ ::SolarMutexGuard aGuard;
+
+ if(HasSdrObject())
+ {
+ return static_cast<SdrPathObj*>(GetSdrObject())->GetPathPoly();
+ }
+ else
+ {
+ return basegfx::B2DPolyPolygon();
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////////
+
+SvxGraphicObject::SvxGraphicObject(SdrObject* pObj)
+ : SvxShapeText( pObj, getSvxMapProvider().GetMap(SVXMAP_GRAPHICOBJECT), getSvxMapProvider().GetPropertySet(SVXMAP_GRAPHICOBJECT, SdrObject::GetGlobalDrawObjectItemPool()) )
+{
+}
+
+SvxGraphicObject::~SvxGraphicObject() noexcept
+{
+}
+
+bool SvxGraphicObject::setPropertyValueImpl( const OUString& rName, const SfxItemPropertyMapEntry* pProperty, const css::uno::Any& rValue )
+{
+ bool bOk = false;
+ switch( pProperty->nWID )
+ {
+ case OWN_ATTR_VALUE_FILLBITMAP:
+ {
+ if( auto pSeq = o3tl::tryAccess<uno::Sequence<sal_Int8>>(rValue) )
+ {
+ SvMemoryStream aMemStm;
+ Graphic aGraphic;
+
+ aMemStm.SetBuffer( const_cast<css::uno::Sequence<sal_Int8> *>(pSeq)->getArray(), pSeq->getLength(), pSeq->getLength() );
+
+ if( GraphicConverter::Import( aMemStm, aGraphic ) == ERRCODE_NONE )
+ {
+ static_cast<SdrGrafObj*>(GetSdrObject())->SetGraphic(aGraphic);
+ bOk = true;
+ }
+ }
+ else if (rValue.getValueType() == cppu::UnoType<graphic::XGraphic>::get())
+ {
+ auto xGraphic = rValue.get<uno::Reference<graphic::XGraphic>>();
+ if (xGraphic.is())
+ {
+ static_cast<SdrGrafObj*>(GetSdrObject())->SetGraphic(Graphic(xGraphic));
+ bOk = true;
+ }
+ }
+ else if (rValue.getValueType() == cppu::UnoType<awt::XBitmap>::get())
+ {
+ auto xBitmap = rValue.get<uno::Reference<awt::XBitmap>>();
+ if (xBitmap.is())
+ {
+ uno::Reference<graphic::XGraphic> xGraphic(xBitmap, uno::UNO_QUERY);
+ Graphic aGraphic(xGraphic);
+ static_cast<SdrGrafObj*>(GetSdrObject())->SetGraphic(aGraphic);
+ bOk = true;
+ }
+ }
+ break;
+ }
+
+ case OWN_ATTR_GRAFSTREAMURL:
+ {
+ OUString aStreamURL;
+
+ if( rValue >>= aStreamURL )
+ {
+ if( !aStreamURL.startsWith( UNO_NAME_GRAPHOBJ_URLPKGPREFIX ) )
+ aStreamURL.clear();
+
+ if( HasSdrObject() )
+ {
+ static_cast<SdrGrafObj*>(GetSdrObject())->SetGrafStreamURL( aStreamURL );
+ }
+ bOk = true;
+ }
+ break;
+ }
+
+ case OWN_ATTR_GRAPHIC_URL:
+ {
+ OUString aURL;
+ uno::Reference<awt::XBitmap> xBitmap;
+ if (rValue >>= aURL)
+ {
+ Graphic aGraphic = vcl::graphic::loadFromURL(aURL);
+ if (!aGraphic.IsNone())
+ {
+ static_cast<SdrGrafObj*>(GetSdrObject())->SetGraphic(aGraphic);
+ bOk = true;
+ }
+ }
+ else if (rValue >>= xBitmap)
+ {
+ uno::Reference<graphic::XGraphic> xGraphic(xBitmap, uno::UNO_QUERY);
+ if (xGraphic.is())
+ {
+ Graphic aGraphic = xGraphic;
+ if (!aGraphic.IsNone())
+ {
+ static_cast<SdrGrafObj*>(GetSdrObject())->SetGraphic(aGraphic);
+ bOk = true;
+ }
+ }
+ }
+ break;
+ }
+
+ case OWN_ATTR_VALUE_GRAPHIC:
+ {
+ Reference< graphic::XGraphic > xGraphic( rValue, uno::UNO_QUERY );
+ if( xGraphic.is() )
+ {
+ static_cast< SdrGrafObj*>( GetSdrObject() )->SetGraphic( xGraphic );
+ bOk = true;
+ }
+ break;
+ }
+
+ case OWN_ATTR_IS_SIGNATURELINE:
+ {
+ bool bIsSignatureLine;
+ if (rValue >>= bIsSignatureLine)
+ {
+ static_cast<SdrGrafObj*>(GetSdrObject())->setIsSignatureLine(bIsSignatureLine);
+ bOk = true;
+ }
+ break;
+ }
+
+ case OWN_ATTR_SIGNATURELINE_ID:
+ {
+ OUString aSignatureLineId;
+ if (rValue >>= aSignatureLineId)
+ {
+ static_cast<SdrGrafObj*>(GetSdrObject())->setSignatureLineId(aSignatureLineId);
+ bOk = true;
+ }
+ break;
+ }
+
+ case OWN_ATTR_SIGNATURELINE_SUGGESTED_SIGNER_NAME:
+ {
+ OUString aSuggestedSignerName;
+ if (rValue >>= aSuggestedSignerName)
+ {
+ static_cast<SdrGrafObj*>(GetSdrObject())->setSignatureLineSuggestedSignerName(aSuggestedSignerName);
+ bOk = true;
+ }
+ break;
+ }
+
+ case OWN_ATTR_SIGNATURELINE_SUGGESTED_SIGNER_TITLE:
+ {
+ OUString aSuggestedSignerTitle;
+ if (rValue >>= aSuggestedSignerTitle)
+ {
+ static_cast<SdrGrafObj*>(GetSdrObject())->setSignatureLineSuggestedSignerTitle(aSuggestedSignerTitle);
+ bOk = true;
+ }
+ break;
+ }
+
+ case OWN_ATTR_SIGNATURELINE_SUGGESTED_SIGNER_EMAIL:
+ {
+ OUString aSuggestedSignerEmail;
+ if (rValue >>= aSuggestedSignerEmail)
+ {
+ static_cast<SdrGrafObj*>(GetSdrObject())->setSignatureLineSuggestedSignerEmail(aSuggestedSignerEmail);
+ bOk = true;
+ }
+ break;
+ }
+
+ case OWN_ATTR_SIGNATURELINE_SIGNING_INSTRUCTIONS:
+ {
+ OUString aSigningInstructions;
+ if (rValue >>= aSigningInstructions)
+ {
+ static_cast<SdrGrafObj*>(GetSdrObject())->setSignatureLineSigningInstructions(aSigningInstructions);
+ bOk = true;
+ }
+ break;
+ }
+
+ case OWN_ATTR_SIGNATURELINE_SHOW_SIGN_DATE:
+ {
+ bool bShowSignDate;
+ if (rValue >>= bShowSignDate)
+ {
+ static_cast<SdrGrafObj*>(GetSdrObject())->setSignatureLineShowSignDate(bShowSignDate);
+ bOk = true;
+ }
+ break;
+ }
+
+ case OWN_ATTR_SIGNATURELINE_CAN_ADD_COMMENT:
+ {
+ bool bCanAddComment;
+ if (rValue >>= bCanAddComment)
+ {
+ static_cast<SdrGrafObj*>(GetSdrObject())->setSignatureLineCanAddComment(bCanAddComment);
+ bOk = true;
+ }
+ break;
+ }
+
+ case OWN_ATTR_SIGNATURELINE_UNSIGNED_IMAGE:
+ {
+ Reference<graphic::XGraphic> xGraphic(rValue, uno::UNO_QUERY);
+ if (xGraphic.is())
+ {
+ static_cast<SdrGrafObj*>(GetSdrObject())->setSignatureLineUnsignedGraphic(xGraphic);
+ bOk = true;
+ }
+ break;
+ }
+
+ case OWN_ATTR_SIGNATURELINE_IS_SIGNED:
+ {
+ bool bIsSigned;
+ if (rValue >>= bIsSigned)
+ {
+ static_cast<SdrGrafObj*>(GetSdrObject())->setSignatureLineIsSigned(bIsSigned);
+ bOk = true;
+ }
+ break;
+ }
+
+ case OWN_ATTR_QRCODE:
+ {
+ css::drawing::BarCode aBarCode;
+ if (rValue >>= aBarCode)
+ {
+ static_cast<SdrGrafObj*>(GetSdrObject())->setQrCode(aBarCode);
+ bOk = true;
+ }
+ break;
+ }
+
+ default:
+ return SvxShapeText::setPropertyValueImpl( rName, pProperty, rValue );
+ }
+
+ if( !bOk )
+ throw lang::IllegalArgumentException();
+
+ GetSdrObject()->getSdrModelFromSdrObject().SetChanged();
+
+ return true;
+}
+
+bool SvxGraphicObject::getPropertyValueImpl( const OUString& rName, const SfxItemPropertyMapEntry* pProperty, css::uno::Any& rValue )
+{
+ switch( pProperty->nWID )
+ {
+ case OWN_ATTR_VALUE_FILLBITMAP:
+ {
+ const Graphic& rGraphic = static_cast<SdrGrafObj*>(GetSdrObject())->GetGraphic();
+
+ if (rGraphic.GetType() != GraphicType::GdiMetafile)
+ {
+ uno::Reference<awt::XBitmap> xBitmap(rGraphic.GetXGraphic(), uno::UNO_QUERY);
+ rValue <<= xBitmap;
+ }
+ else
+ {
+ SvMemoryStream aDestStrm( 65535, 65535 );
+
+ ConvertGDIMetaFileToWMF( rGraphic.GetGDIMetaFile(), aDestStrm, nullptr, false );
+ const uno::Sequence<sal_Int8> aSeq(
+ static_cast< const sal_Int8* >(aDestStrm.GetData()),
+ aDestStrm.GetEndOfData());
+ rValue <<= aSeq;
+ }
+ break;
+ }
+
+ case OWN_ATTR_REPLACEMENT_GRAPHIC:
+ {
+ const GraphicObject* pGrafObj = static_cast< SdrGrafObj* >(GetSdrObject())->GetReplacementGraphicObject();
+
+ if (pGrafObj)
+ {
+ rValue <<= pGrafObj->GetGraphic().GetXGraphic();
+ }
+
+ break;
+ }
+
+ case OWN_ATTR_GRAFSTREAMURL:
+ {
+ const OUString aStreamURL( static_cast<SdrGrafObj*>( GetSdrObject() )->GetGrafStreamURL() );
+ if( !aStreamURL.isEmpty() )
+ rValue <<= aStreamURL;
+ break;
+ }
+
+ case OWN_ATTR_GRAPHIC_URL:
+ case OWN_ATTR_VALUE_GRAPHIC:
+ {
+ if (pProperty->nWID == OWN_ATTR_GRAPHIC_URL)
+ {
+ SAL_WARN("svx", "Getting Graphic by URL is not supported, getting it by value");
+ }
+
+ Reference<graphic::XGraphic> xGraphic;
+ auto pSdrGraphicObject = static_cast<SdrGrafObj*>(GetSdrObject());
+ if (pSdrGraphicObject
+ && pSdrGraphicObject->GetGraphicObject().GetType() != GraphicType::NONE)
+ xGraphic = pSdrGraphicObject->GetGraphic().GetXGraphic();
+ rValue <<= xGraphic;
+ break;
+ }
+
+ case OWN_ATTR_GRAPHIC_STREAM:
+ {
+ rValue <<= static_cast< SdrGrafObj* >( GetSdrObject() )->getInputStream();
+ break;
+ }
+
+ case OWN_ATTR_IS_SIGNATURELINE:
+ {
+ rValue <<= static_cast<SdrGrafObj*>(GetSdrObject())->isSignatureLine();
+ break;
+ }
+
+ case OWN_ATTR_SIGNATURELINE_ID:
+ {
+ rValue <<= static_cast<SdrGrafObj*>(GetSdrObject())->getSignatureLineId();
+ break;
+ }
+
+ case OWN_ATTR_SIGNATURELINE_SUGGESTED_SIGNER_NAME:
+ {
+ rValue <<= static_cast<SdrGrafObj*>(GetSdrObject())->getSignatureLineSuggestedSignerName();
+ break;
+ }
+
+ case OWN_ATTR_SIGNATURELINE_SUGGESTED_SIGNER_TITLE:
+ {
+ rValue <<= static_cast<SdrGrafObj*>(GetSdrObject())->getSignatureLineSuggestedSignerTitle();
+ break;
+ }
+
+ case OWN_ATTR_SIGNATURELINE_SUGGESTED_SIGNER_EMAIL:
+ {
+ rValue <<= static_cast<SdrGrafObj*>(GetSdrObject())->getSignatureLineSuggestedSignerEmail();
+ break;
+ }
+
+ case OWN_ATTR_SIGNATURELINE_SIGNING_INSTRUCTIONS:
+ {
+ rValue <<= static_cast<SdrGrafObj*>(GetSdrObject())->getSignatureLineSigningInstructions();
+ break;
+ }
+
+ case OWN_ATTR_SIGNATURELINE_SHOW_SIGN_DATE:
+ {
+ rValue <<= static_cast<SdrGrafObj*>(GetSdrObject())->isSignatureLineShowSignDate();
+ break;
+ }
+
+ case OWN_ATTR_SIGNATURELINE_CAN_ADD_COMMENT:
+ {
+ rValue <<= static_cast<SdrGrafObj*>(GetSdrObject())->isSignatureLineCanAddComment();
+ break;
+ }
+
+ case OWN_ATTR_SIGNATURELINE_UNSIGNED_IMAGE:
+ {
+ Reference<graphic::XGraphic> xGraphic(
+ static_cast<SdrGrafObj*>(GetSdrObject())->getSignatureLineUnsignedGraphic());
+ rValue <<= xGraphic;
+ break;
+ }
+
+ case OWN_ATTR_SIGNATURELINE_IS_SIGNED:
+ {
+ rValue <<= static_cast<SdrGrafObj*>(GetSdrObject())->isSignatureLineSigned();
+ break;
+ }
+
+ case OWN_ATTR_QRCODE:
+ {
+ css::drawing::BarCode* ptr = static_cast<SdrGrafObj*>(GetSdrObject())->getQrCode();
+ if(ptr)
+ {
+ rValue <<= *ptr;
+ }
+ break;
+ }
+
+ default:
+ return SvxShapeText::getPropertyValueImpl(rName, pProperty,rValue);
+ }
+
+ return true;
+}
+
+
+SvxShapeCaption::SvxShapeCaption(SdrObject* pObj)
+: SvxShapeText( pObj, getSvxMapProvider().GetMap(SVXMAP_CAPTION), getSvxMapProvider().GetPropertySet(SVXMAP_CAPTION, SdrObject::GetGlobalDrawObjectItemPool()) )
+{
+}
+
+SvxShapeCaption::~SvxShapeCaption() noexcept
+{
+}
+
+SvxCustomShape::SvxCustomShape(SdrObject* pObj)
+ : SvxShapeText( pObj, getSvxMapProvider().GetMap( SVXMAP_CUSTOMSHAPE ), getSvxMapProvider().GetPropertySet(SVXMAP_CUSTOMSHAPE, SdrObject::GetGlobalDrawObjectItemPool()) )
+{
+}
+
+SvxCustomShape::~SvxCustomShape() noexcept
+{
+}
+
+uno::Any SAL_CALL SvxCustomShape::queryInterface( const uno::Type & rType )
+{
+ return SvxShapeText::queryInterface( rType );
+}
+
+uno::Any SAL_CALL SvxCustomShape::queryAggregation( const uno::Type & rType )
+{
+ css::uno::Any aReturn = SvxShapeText::queryAggregation( rType );
+ if ( !aReturn.hasValue() )
+ aReturn = ::cppu::queryInterface(rType, static_cast<drawing::XEnhancedCustomShapeDefaulter*>(this) );
+ return aReturn;
+}
+
+uno::Sequence< sal_Int8 > SAL_CALL SvxCustomShape::getImplementationId()
+{
+ return css::uno::Sequence<sal_Int8>();
+}
+
+// css::drawing::XShape
+
+
+awt::Point SAL_CALL SvxCustomShape::getPosition()
+{
+ ::SolarMutexGuard aGuard;
+ if ( HasSdrObject() )
+ {
+ SdrAShapeObjGeoData aCustomShapeGeoData;
+ static_cast<SdrObjCustomShape*>(GetSdrObject())->SaveGeoData( aCustomShapeGeoData );
+
+ bool bMirroredX = false;
+ bool bMirroredY = false;
+
+ if ( HasSdrObject() )
+ {
+ bMirroredX = static_cast<SdrObjCustomShape*>(GetSdrObject())->IsMirroredX();
+ bMirroredY = static_cast<SdrObjCustomShape*>(GetSdrObject())->IsMirroredY();
+ }
+ // get aRect, this is the unrotated snaprect
+ tools::Rectangle aRect(static_cast<SdrObjCustomShape*>(GetSdrObject())->GetLogicRect());
+ tools::Rectangle aRectangle( aRect );
+
+ if ( bMirroredX || bMirroredY )
+ { // we have to retrieve the unmirrored rect
+
+ GeoStat aNewGeo(aCustomShapeGeoData.maGeo);
+ if ( bMirroredX )
+ {
+ tools::Polygon aPol( Rect2Poly( aRect, aNewGeo ) );
+ tools::Rectangle aBoundRect( aPol.GetBoundRect() );
+
+ Point aRef1( ( aBoundRect.Left() + aBoundRect.Right() ) >> 1, aBoundRect.Top() );
+ Point aRef2( aRef1.X(), aRef1.Y() + 1000 );
+ sal_uInt16 i;
+ sal_uInt16 nPointCount=aPol.GetSize();
+ for (i=0; i<nPointCount; i++)
+ {
+ MirrorPoint(aPol[i],aRef1,aRef2);
+ }
+ // turn and move polygon
+ tools::Polygon aPol0(aPol);
+ aPol[0]=aPol0[1];
+ aPol[1]=aPol0[0];
+ aPol[2]=aPol0[3];
+ aPol[3]=aPol0[2];
+ aPol[4]=aPol0[1];
+ Poly2Rect(aPol,aRectangle,aNewGeo);
+ }
+ if ( bMirroredY )
+ {
+ tools::Polygon aPol( Rect2Poly( aRectangle, aNewGeo ) );
+ tools::Rectangle aBoundRect( aPol.GetBoundRect() );
+
+ Point aRef1( aBoundRect.Left(), ( aBoundRect.Top() + aBoundRect.Bottom() ) >> 1 );
+ Point aRef2( aRef1.X() + 1000, aRef1.Y() );
+ sal_uInt16 i;
+ sal_uInt16 nPointCount=aPol.GetSize();
+ for (i=0; i<nPointCount; i++)
+ {
+ MirrorPoint(aPol[i],aRef1,aRef2);
+ }
+ // turn and move polygon
+ tools::Polygon aPol0(aPol);
+ aPol[0]=aPol0[1];
+ aPol[1]=aPol0[0];
+ aPol[2]=aPol0[3];
+ aPol[3]=aPol0[2];
+ aPol[4]=aPol0[1];
+ Poly2Rect( aPol, aRectangle, aNewGeo );
+ }
+ }
+ Point aPt( aRectangle.TopLeft() );
+
+ if( GetSdrObject()->getSdrModelFromSdrObject().IsWriter() )
+ aPt -= GetSdrObject()->GetAnchorPos();
+
+ ForceMetricTo100th_mm(aPt);
+ return css::awt::Point( aPt.X(), aPt.Y() );
+ }
+ else
+ return SvxShape::getPosition();
+}
+
+
+void SAL_CALL SvxCustomShape::setPropertyValue( const OUString& aPropertyName, const uno::Any& aValue )
+{
+ ::SolarMutexGuard aGuard;
+
+ SdrObject* pObject = GetSdrObject();
+
+ // tdf#98163 Use a custom slot to have filter code flush the UNO
+ // API implementations of SdrObjCustomShape. Used e.g. by
+ // ~SdXMLCustomShapeContext, see there for more information
+ if("FlushCustomShapeUnoApiObjects" == aPropertyName)
+ {
+ SdrObjCustomShape* pTarget = dynamic_cast< SdrObjCustomShape* >(pObject);
+ if(pTarget)
+ {
+ // Luckily, the object causing problems in tdf#93994 is not the
+ // UNO API object, but the XCustomShapeEngine involved. This
+ // object is on-demand replaceable and can be reset here. This
+ // will free the involved EditEngine and VirtualDevice.
+ pTarget->mxCustomShapeEngine.set(nullptr);
+ }
+ // since this case is only for the application cores
+ // we should return from this function now
+ return;
+ }
+
+ bool bCustomShapeGeometry = pObject && aPropertyName == "CustomShapeGeometry";
+
+ bool bMirroredX = false;
+ bool bMirroredY = false;
+
+ if ( bCustomShapeGeometry )
+ {
+ bMirroredX = static_cast<SdrObjCustomShape*>(pObject)->IsMirroredX();
+ bMirroredY = static_cast<SdrObjCustomShape*>(pObject)->IsMirroredY();
+ }
+
+ SvxShape::setPropertyValue( aPropertyName, aValue );
+
+ if ( !bCustomShapeGeometry )
+ return;
+
+ static_cast<SdrObjCustomShape*>(pObject)->MergeDefaultAttributes();
+ tools::Rectangle aRect( pObject->GetSnapRect() );
+
+ // #i38892#
+ bool bNeedsMirrorX = static_cast<SdrObjCustomShape*>(pObject)->IsMirroredX() != bMirroredX;
+ bool bNeedsMirrorY = static_cast<SdrObjCustomShape*>(pObject)->IsMirroredY() != bMirroredY;
+
+ std::unique_ptr< SdrGluePointList > pListCopy;
+ if( bNeedsMirrorX || bNeedsMirrorY )
+ {
+ const SdrGluePointList* pList = pObject->GetGluePointList();
+ if( pList )
+ pListCopy.reset( new SdrGluePointList(*pList) );
+ }
+
+ if ( bNeedsMirrorX )
+ {
+ Point aTop( ( aRect.Left() + aRect.Right() ) >> 1, aRect.Top() );
+ Point aBottom( aTop.X(), aTop.Y() + 1000 );
+ pObject->NbcMirror( aTop, aBottom );
+ // NbcMirroring is flipping the current mirror state,
+ // so we have to set the correct state again
+ static_cast<SdrObjCustomShape*>(pObject)->SetMirroredX( !bMirroredX );
+ }
+ if ( bNeedsMirrorY )
+ {
+ Point aLeft( aRect.Left(), ( aRect.Top() + aRect.Bottom() ) >> 1 );
+ Point aRight( aLeft.X() + 1000, aLeft.Y() );
+ pObject->NbcMirror( aLeft, aRight );
+ // NbcMirroring is flipping the current mirror state,
+ // so we have to set the correct state again
+ static_cast<SdrObjCustomShape*>(pObject)->SetMirroredY( !bMirroredY );
+ }
+
+ if( pListCopy )
+ {
+ SdrGluePointList* pNewList = const_cast< SdrGluePointList* >( pObject->GetGluePointList() );
+ if(pNewList)
+ *pNewList = *pListCopy;
+ }
+}
+
+bool SvxCustomShape::getPropertyValueImpl( const OUString& rName, const SfxItemPropertyMapEntry* pProperty, css::uno::Any& rValue )
+{
+ switch( pProperty->nWID )
+ {
+ case SDRATTR_ROTATEANGLE:
+ {
+ double fAngle = static_cast<SdrObjCustomShape*>(GetSdrObject())->GetObjectRotation();
+ fAngle *= 100;
+ rValue <<= static_cast<sal_Int32>(fAngle);
+ return true;
+ }
+ default:
+ return SvxShape::getPropertyValueImpl( rName, pProperty, rValue );
+ }
+}
+
+void SvxCustomShape::createCustomShapeDefaults( const OUString& rValueType )
+{
+ if (!HasSdrObject())
+ {
+ OSL_FAIL("could not create Custom Shape Defaults!");
+ return;
+ }
+
+ static_cast<SdrObjCustomShape*>(GetSdrObject())->MergeDefaultAttributes( &rValueType );
+}
+
+SvxShapeGroupAnyD::SvxShapeGroupAnyD( SdrObject* pObject, const SfxItemPropertyMapEntry* pEntries, const SvxItemPropertySet* pPropertySet )
+ : SvxShape(pObject, pEntries, pPropertySet)
+{}
+
+SvxShapeGroupAnyD::~SvxShapeGroupAnyD() noexcept
+{}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/unodraw/unoshap3.cxx b/svx/source/unodraw/unoshap3.cxx
new file mode 100644
index 000000000..0704a4e5c
--- /dev/null
+++ b/svx/source/unodraw/unoshap3.cxx
@@ -0,0 +1,1052 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * 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 <string_view>
+
+#include <com/sun/star/drawing/HomogenMatrix.hpp>
+#include <com/sun/star/drawing/Position3D.hpp>
+#include <com/sun/star/drawing/Direction3D.hpp>
+#include <com/sun/star/drawing/DoubleSequence.hpp>
+#include <com/sun/star/drawing/CameraGeometry.hpp>
+#include <com/sun/star/lang/IndexOutOfBoundsException.hpp>
+#include <o3tl/safeint.hxx>
+#include <vcl/svapp.hxx>
+#include <comphelper/sequence.hxx>
+#include <sal/log.hxx>
+
+#include <svx/svdpool.hxx>
+#include <svx/svditer.hxx>
+#include <svx/unoshape.hxx>
+#include <svx/unopage.hxx>
+#include <svx/cube3d.hxx>
+#include <svx/sphere3d.hxx>
+#include <svx/lathe3d.hxx>
+#include <extrud3d.hxx>
+#include <polygn3d.hxx>
+#include <svx/unoshprp.hxx>
+#include <svx/svdmodel.hxx>
+#include <svx/scene3d.hxx>
+#include <basegfx/polygon/b3dpolygon.hxx>
+#include <basegfx/polygon/b3dpolygontools.hxx>
+#include <com/sun/star/drawing/PolyPolygonShape3D.hpp>
+#include <basegfx/polygon/b2dpolypolygontools.hxx>
+#include <basegfx/matrix/b3dhommatrixtools.hxx>
+#include "shapeimpl.hxx"
+
+using namespace ::cppu;
+using namespace ::com::sun::star;
+using namespace ::com::sun::star::uno;
+using namespace ::com::sun::star::lang;
+using namespace ::com::sun::star::container;
+
+#define QUERYINT( xint ) \
+ if( rType == cppu::UnoType<xint>::get() ) \
+ aAny <<= Reference< xint >(this)
+
+Svx3DSceneObject::Svx3DSceneObject(SdrObject* pObj, SvxDrawPage* pDrawPage)
+: SvxShapeGroupAnyD( pObj, getSvxMapProvider().GetMap(SVXMAP_3DSCENEOBJECT), getSvxMapProvider().GetPropertySet(SVXMAP_3DSCENEOBJECT, SdrObject::GetGlobalDrawObjectItemPool()) )
+, mxPage( pDrawPage )
+{
+}
+
+
+Svx3DSceneObject::~Svx3DSceneObject() noexcept
+{
+}
+
+
+void Svx3DSceneObject::Create( SdrObject* pNewObj, SvxDrawPage* pNewPage )
+{
+ SvxShape::Create( pNewObj, pNewPage );
+ mxPage = pNewPage;
+}
+
+
+uno::Any SAL_CALL Svx3DSceneObject::queryAggregation( const uno::Type & rType )
+{
+ uno::Any aAny;
+
+ QUERYINT( drawing::XShapes );
+ else QUERYINT( container::XIndexAccess );
+ else QUERYINT( container::XElementAccess );
+ else
+ return SvxShape::queryAggregation( rType );
+
+ return aAny;
+}
+
+uno::Any SAL_CALL Svx3DSceneObject::queryInterface( const uno::Type & rType )
+{
+ return SvxShape::queryInterface( rType );
+}
+
+// XTypeProvider
+
+uno::Sequence< sal_Int8 > SAL_CALL Svx3DSceneObject::getImplementationId()
+{
+ return css::uno::Sequence<sal_Int8>();
+}
+
+
+void SAL_CALL Svx3DSceneObject::add( const Reference< drawing::XShape >& xShape )
+{
+ SolarMutexGuard aGuard;
+
+ SvxShape* pShape = comphelper::getFromUnoTunnel<SvxShape>( xShape );
+
+ if(!HasSdrObject() || !mxPage.is() || pShape == nullptr || nullptr != pShape->GetSdrObject() )
+ throw uno::RuntimeException();
+
+ SdrObject* pSdrShape = mxPage->CreateSdrObject_( xShape );
+ if( dynamic_cast<const E3dObject* >(pSdrShape) != nullptr )
+ {
+ GetSdrObject()->GetSubList()->NbcInsertObject( pSdrShape );
+ pShape->Create(pSdrShape, mxPage.get());
+ }
+ else
+ {
+ SdrObject::Free( pSdrShape );
+ throw uno::RuntimeException();
+ }
+
+ GetSdrObject()->getSdrModelFromSdrObject().SetChanged();
+}
+
+void Svx3DSceneObject::addShape( SvxShape& rShape )
+{
+ SolarMutexGuard aGuard;
+
+ if(!HasSdrObject() || !mxPage.is() || nullptr != rShape.GetSdrObject() )
+ throw uno::RuntimeException();
+
+ SdrObject* pSdrShape = mxPage->CreateSdrObject_( &rShape );
+ if( dynamic_cast<const E3dObject* >(pSdrShape) != nullptr )
+ {
+ GetSdrObject()->GetSubList()->NbcInsertObject( pSdrShape );
+ rShape.Create(pSdrShape, mxPage.get());
+ }
+ else
+ {
+ SdrObject::Free( pSdrShape );
+ throw uno::RuntimeException();
+ }
+
+ GetSdrObject()->getSdrModelFromSdrObject().SetChanged();
+}
+
+void SAL_CALL Svx3DSceneObject::remove( const Reference< drawing::XShape >& xShape )
+{
+ SolarMutexGuard aGuard;
+
+ SdrObject* pSdrShape = SdrObject::getSdrObjectFromXShape( xShape );
+
+ if(!HasSdrObject() || !pSdrShape ||
+ pSdrShape->getParentSdrObjectFromSdrObject() != GetSdrObject())
+ throw uno::RuntimeException();
+
+ SdrObjList& rList = *pSdrShape->getParentSdrObjListFromSdrObject();
+
+ const size_t nObjCount = rList.GetObjCount();
+ size_t nObjNum = 0;
+ while( nObjNum < nObjCount )
+ {
+ if(rList.GetObj( nObjNum ) == pSdrShape )
+ break;
+ nObjNum++;
+ }
+
+ if( nObjNum < nObjCount )
+ {
+ SdrObject* pObject = rList.NbcRemoveObject( nObjNum );
+ SdrObject::Free( pObject );
+ }
+ else
+ {
+ SAL_WARN( "svx", "Fatality! SdrObject is not belonging to its SdrObjList! [CL]" );
+ }
+}
+
+
+sal_Int32 SAL_CALL Svx3DSceneObject::getCount()
+{
+ SolarMutexGuard aGuard;
+
+ sal_Int32 nRetval = 0;
+
+ if(HasSdrObject() && dynamic_cast<const E3dScene* >(GetSdrObject()) != nullptr && GetSdrObject()->GetSubList())
+ nRetval = GetSdrObject()->GetSubList()->GetObjCount();
+ return nRetval;
+}
+
+
+uno::Any SAL_CALL Svx3DSceneObject::getByIndex( sal_Int32 Index )
+{
+ SolarMutexGuard aGuard;
+
+ if( !HasSdrObject() || GetSdrObject()->GetSubList() == nullptr )
+ throw uno::RuntimeException();
+
+ if( Index<0 || GetSdrObject()->GetSubList()->GetObjCount() <= o3tl::make_unsigned(Index) )
+ throw lang::IndexOutOfBoundsException();
+
+ SdrObject* pDestObj = GetSdrObject()->GetSubList()->GetObj( Index );
+ if(pDestObj == nullptr)
+ throw lang::IndexOutOfBoundsException();
+
+ Reference< drawing::XShape > xShape( pDestObj->getUnoShape(), uno::UNO_QUERY );
+ return uno::Any(xShape);
+}
+
+
+// css::container::XElementAccess
+
+uno::Type SAL_CALL Svx3DSceneObject::getElementType()
+{
+ return cppu::UnoType<drawing::XShape>::get();
+}
+
+
+sal_Bool SAL_CALL Svx3DSceneObject::hasElements()
+{
+ SolarMutexGuard aGuard;
+
+ return HasSdrObject() && GetSdrObject()->GetSubList() && (GetSdrObject()->GetSubList()->GetObjCount() > 0);
+}
+
+
+static bool ConvertHomogenMatrixToObject( E3dObject* pObject, const Any& rValue )
+{
+ drawing::HomogenMatrix aMat;
+ if( rValue >>= aMat )
+ {
+ pObject->SetTransform(basegfx::utils::UnoHomogenMatrixToB3DHomMatrix(aMat));
+ return true;
+ }
+ return false;
+}
+
+static void ConvertObjectToHomogenMatric( E3dObject const * pObject, Any& rValue )
+{
+ drawing::HomogenMatrix aHomMat;
+ const basegfx::B3DHomMatrix& rMat = pObject->GetTransform();
+ basegfx::utils::B3DHomMatrixToUnoHomogenMatrix(rMat, aHomMat);
+ rValue <<= aHomMat;
+}
+
+namespace {
+
+struct ImpRememberTransAndRect
+{
+ basegfx::B3DHomMatrix maMat;
+ tools::Rectangle maRect;
+};
+
+}
+
+bool Svx3DSceneObject::setPropertyValueImpl( const OUString& rName, const SfxItemPropertyMapEntry* pProperty, const css::uno::Any& rValue )
+{
+ switch( pProperty->nWID )
+ {
+ case OWN_ATTR_3D_VALUE_TRANSFORM_MATRIX:
+ {
+ // patch transformation matrix to the object
+ if( ConvertHomogenMatrixToObject( static_cast< E3dObject* >( GetSdrObject() ), rValue ) )
+ return true;
+ break;
+ }
+ case OWN_ATTR_3D_VALUE_CAMERA_GEOMETRY:
+ {
+ // set CameraGeometry at scene
+ E3dScene* pScene = static_cast< E3dScene* >( GetSdrObject() );
+ drawing::CameraGeometry aCamGeo;
+
+ if(rValue >>= aCamGeo)
+ {
+ basegfx::B3DPoint aVRP(aCamGeo.vrp.PositionX, aCamGeo.vrp.PositionY, aCamGeo.vrp.PositionZ);
+ basegfx::B3DVector aVPN(aCamGeo.vpn.DirectionX, aCamGeo.vpn.DirectionY, aCamGeo.vpn.DirectionZ);
+ basegfx::B3DVector aVUP(aCamGeo.vup.DirectionX, aCamGeo.vup.DirectionY, aCamGeo.vup.DirectionZ);
+
+ // rescue scene transformation
+ ImpRememberTransAndRect aSceneTAR;
+ aSceneTAR.maMat = pScene->GetTransform();
+ aSceneTAR.maRect = pScene->GetSnapRect();
+
+ // rescue object transformations
+ SdrObjListIter aIter(pScene->GetSubList(), SdrIterMode::DeepWithGroups);
+ std::vector<basegfx::B3DHomMatrix*> aObjTrans;
+ while(aIter.IsMore())
+ {
+ E3dObject* p3DObj = static_cast<E3dObject*>(aIter.Next());
+ basegfx::B3DHomMatrix* pNew = new basegfx::B3DHomMatrix;
+ *pNew = p3DObj->GetTransform();
+ aObjTrans.push_back(pNew);
+ }
+
+ // reset object transformations
+ aIter.Reset();
+ while(aIter.IsMore())
+ {
+ E3dObject* p3DObj = static_cast<E3dObject*>(aIter.Next());
+ p3DObj->NbcSetTransform(basegfx::B3DHomMatrix());
+ }
+
+ // reset scene transformation and make a complete recalc
+ pScene->NbcSetTransform(basegfx::B3DHomMatrix());
+
+ // fill old camera from new parameters
+ Camera3D aCam(pScene->GetCamera());
+ const basegfx::B3DRange& rVolume = pScene->GetBoundVolume();
+ double fW = rVolume.getWidth();
+ double fH = rVolume.getHeight();
+
+ const SfxItemSet& rSceneSet = pScene->GetMergedItemSet();
+ double fCamPosZ =
+ static_cast<double>(rSceneSet.Get(SDRATTR_3DSCENE_DISTANCE).GetValue());
+ double fCamFocal =
+ static_cast<double>(rSceneSet.Get(SDRATTR_3DSCENE_FOCAL_LENGTH).GetValue());
+
+ aCam.SetAutoAdjustProjection(false);
+ aCam.SetViewWindow(- fW / 2, - fH / 2, fW, fH);
+ basegfx::B3DPoint aLookAt;
+ basegfx::B3DPoint aCamPos(0.0, 0.0, fCamPosZ);
+ aCam.SetPosAndLookAt(aCamPos, aLookAt);
+ aCam.SetFocalLength(fCamFocal / 100.0);
+ aCam.SetDeviceWindow(tools::Rectangle(0, 0, static_cast<tools::Long>(fW), static_cast<tools::Long>(fH)));
+
+ // set at scene
+ pScene->SetCamera(aCam);
+
+ // #91047# use imported VRP, VPN and VUP (if used)
+ bool bVRPUsed(!aVRP.equal(basegfx::B3DPoint(0.0, 0.0, 1.0)));
+ bool bVPNUsed(!aVPN.equal(basegfx::B3DVector(0.0, 0.0, 1.0)));
+ bool bVUPUsed(!aVUP.equal(basegfx::B3DVector(0.0, 1.0, 0.0)));
+
+ if(bVRPUsed || bVPNUsed || bVUPUsed)
+ {
+ pScene->GetCameraSet().SetViewportValues(aVRP, aVPN, aVUP);
+ }
+
+ // set object transformations again at objects
+ aIter.Reset();
+ sal_uInt32 nIndex(0);
+ while(aIter.IsMore())
+ {
+ E3dObject* p3DObj = static_cast<E3dObject*>(aIter.Next());
+ basegfx::B3DHomMatrix* pMat = aObjTrans[nIndex++];
+ p3DObj->NbcSetTransform(*pMat);
+ delete pMat;
+ }
+
+ // set scene transformation again at scene
+ pScene->NbcSetTransform(aSceneTAR.maMat);
+ pScene->NbcSetSnapRect(aSceneTAR.maRect);
+
+ return true;
+ }
+ break;
+ }
+ default:
+ return SvxShape::setPropertyValueImpl(rName, pProperty, rValue);
+ }
+
+ throw IllegalArgumentException();
+}
+
+
+bool Svx3DSceneObject::getPropertyValueImpl(const OUString& rName, const SfxItemPropertyMapEntry* pProperty,
+ css::uno::Any& rValue)
+{
+ switch( pProperty->nWID )
+ {
+ case OWN_ATTR_3D_VALUE_TRANSFORM_MATRIX:
+ {
+ // patch object to a homogeneous 4x4 matrix
+ ConvertObjectToHomogenMatric( static_cast< E3dObject* >( GetSdrObject() ), rValue );
+ break;
+ }
+ case OWN_ATTR_3D_VALUE_CAMERA_GEOMETRY:
+ {
+ // get CameraGeometry from scene
+ E3dScene* pScene = static_cast< E3dScene* >( GetSdrObject() );
+ drawing::CameraGeometry aCamGeo;
+
+ // fill Vectors from scene camera
+ B3dCamera& aCameraSet = pScene->GetCameraSet();
+ basegfx::B3DPoint aVRP(aCameraSet.GetVRP());
+ basegfx::B3DVector aVPN(aCameraSet.GetVPN());
+ basegfx::B3DVector aVUP(aCameraSet.GetVUV());
+
+ // transfer to structure
+ aCamGeo.vrp.PositionX = aVRP.getX();
+ aCamGeo.vrp.PositionY = aVRP.getY();
+ aCamGeo.vrp.PositionZ = aVRP.getZ();
+ aCamGeo.vpn.DirectionX = aVPN.getX();
+ aCamGeo.vpn.DirectionY = aVPN.getY();
+ aCamGeo.vpn.DirectionZ = aVPN.getZ();
+ aCamGeo.vup.DirectionX = aVUP.getX();
+ aCamGeo.vup.DirectionY = aVUP.getY();
+ aCamGeo.vup.DirectionZ = aVUP.getZ();
+
+ rValue <<= aCamGeo;
+ break;
+ }
+ default:
+ return SvxShape::getPropertyValueImpl( rName, pProperty, rValue );
+ }
+
+ return true;
+}
+
+// css::lang::XServiceInfo
+uno::Sequence< OUString > SAL_CALL Svx3DSceneObject::getSupportedServiceNames()
+{
+ return comphelper::concatSequences(
+ SvxShape::getSupportedServiceNames(),
+ std::initializer_list<std::u16string_view>{ u"com.sun.star.drawing.Shape3DScene" });
+}
+
+Svx3DCubeObject::Svx3DCubeObject(SdrObject* pObj)
+: SvxShape( pObj, getSvxMapProvider().GetMap(SVXMAP_3DCUBEOBJECT), getSvxMapProvider().GetPropertySet(SVXMAP_3DCUBEOBJECT, SdrObject::GetGlobalDrawObjectItemPool()) )
+{
+}
+
+Svx3DCubeObject::~Svx3DCubeObject() noexcept
+{
+}
+
+bool Svx3DCubeObject::setPropertyValueImpl( const OUString& rName, const SfxItemPropertyMapEntry* pProperty, const css::uno::Any& rValue )
+{
+ SolarMutexGuard aGuard;
+
+ switch( pProperty->nWID )
+ {
+ case OWN_ATTR_3D_VALUE_TRANSFORM_MATRIX:
+ {
+ // pack transformationmatrix to the object
+ if( ConvertHomogenMatrixToObject( static_cast< E3dObject* >( GetSdrObject() ), rValue ) )
+ return true;
+ break;
+ }
+ case OWN_ATTR_3D_VALUE_POSITION:
+ {
+ // pack position to the object
+ drawing::Position3D aUnoPos;
+ if( rValue >>= aUnoPos )
+ {
+ basegfx::B3DPoint aPos(aUnoPos.PositionX, aUnoPos.PositionY, aUnoPos.PositionZ);
+ static_cast< E3dCubeObj* >( GetSdrObject() )->SetCubePos(aPos);
+ return true;
+ }
+ break;
+ }
+ case OWN_ATTR_3D_VALUE_SIZE:
+ {
+ // pack size to the object
+ drawing::Direction3D aDirection;
+ if( rValue >>= aDirection )
+ {
+ basegfx::B3DVector aSize(aDirection.DirectionX, aDirection.DirectionY, aDirection.DirectionZ);
+ static_cast< E3dCubeObj* >( GetSdrObject() )->SetCubeSize(aSize);
+ return true;
+ }
+ break;
+ }
+ case OWN_ATTR_3D_VALUE_POS_IS_CENTER:
+ {
+ bool bNew = false;
+ // pack sal_Bool bPosIsCenter to the object
+ if( rValue >>= bNew )
+ {
+ static_cast< E3dCubeObj* >( GetSdrObject() )->SetPosIsCenter(bNew);
+ return true;
+ }
+ break;
+ }
+ default:
+ return SvxShape::setPropertyValueImpl( rName, pProperty, rValue );
+ }
+
+ throw IllegalArgumentException();
+}
+
+bool Svx3DCubeObject::getPropertyValueImpl( const OUString& rName, const SfxItemPropertyMapEntry* pProperty, css::uno::Any& rValue )
+{
+ switch( pProperty->nWID )
+ {
+ case OWN_ATTR_3D_VALUE_TRANSFORM_MATRIX:
+ {
+ // pack transformation to a homogeneous matrix
+ ConvertObjectToHomogenMatric( static_cast< E3dObject* >( GetSdrObject() ), rValue );
+ break;
+ }
+ case OWN_ATTR_3D_VALUE_POSITION:
+ {
+ // pack position
+ const basegfx::B3DPoint& rPos = static_cast<E3dCubeObj*>(GetSdrObject())->GetCubePos();
+ drawing::Position3D aPos;
+
+ aPos.PositionX = rPos.getX();
+ aPos.PositionY = rPos.getY();
+ aPos.PositionZ = rPos.getZ();
+
+ rValue <<= aPos;
+ break;
+ }
+ case OWN_ATTR_3D_VALUE_SIZE:
+ {
+ // pack size
+ const basegfx::B3DVector& rSize = static_cast<E3dCubeObj*>(GetSdrObject())->GetCubeSize();
+ drawing::Direction3D aDir;
+
+ aDir.DirectionX = rSize.getX();
+ aDir.DirectionY = rSize.getY();
+ aDir.DirectionZ = rSize.getZ();
+
+ rValue <<= aDir;
+ break;
+ }
+ case OWN_ATTR_3D_VALUE_POS_IS_CENTER:
+ {
+ rValue <<= static_cast<E3dCubeObj*>(GetSdrObject())->GetPosIsCenter();
+ break;
+ }
+ default:
+ return SvxShape::getPropertyValueImpl( rName, pProperty, rValue );
+ }
+
+ return true;
+}
+
+// css::lang::XServiceInfo
+uno::Sequence< OUString > SAL_CALL Svx3DCubeObject::getSupportedServiceNames()
+{
+ return comphelper::concatSequences(
+ SvxShape::getSupportedServiceNames(),
+ std::initializer_list<std::u16string_view>{ u"com.sun.star.drawing.Shape3D",
+ u"com.sun.star.drawing.Shape3DCube" });
+}
+
+Svx3DSphereObject::Svx3DSphereObject(SdrObject* pObj)
+ : SvxShape( pObj, getSvxMapProvider().GetMap(SVXMAP_3DSPHEREOBJECT), getSvxMapProvider().GetPropertySet(SVXMAP_3DSPHEREOBJECT, SdrObject::GetGlobalDrawObjectItemPool()) )
+{
+}
+
+Svx3DSphereObject::~Svx3DSphereObject() noexcept
+{
+}
+
+bool Svx3DSphereObject::setPropertyValueImpl( const OUString& rName, const SfxItemPropertyMapEntry* pProperty, const css::uno::Any& rValue )
+{
+ switch( pProperty->nWID )
+ {
+ case OWN_ATTR_3D_VALUE_TRANSFORM_MATRIX:
+ {
+ // pack transformation matrix to the object
+ if( ConvertHomogenMatrixToObject( static_cast< E3dObject* >( GetSdrObject() ), rValue ) )
+ return true;
+ break;
+ }
+
+ case OWN_ATTR_3D_VALUE_POSITION:
+ {
+ // pack position to the object
+ drawing::Position3D aUnoPos;
+ if( rValue >>= aUnoPos )
+ {
+ basegfx::B3DPoint aPos(aUnoPos.PositionX, aUnoPos.PositionY, aUnoPos.PositionZ);
+ static_cast<E3dSphereObj*>(GetSdrObject())->SetCenter(aPos);
+ return true;
+ }
+ break;
+ }
+
+ case OWN_ATTR_3D_VALUE_SIZE:
+ {
+ // pack size to the object
+ drawing::Direction3D aDir;
+ if( rValue >>= aDir )
+ {
+ basegfx::B3DVector aPos(aDir.DirectionX, aDir.DirectionY, aDir.DirectionZ);
+ static_cast<E3dSphereObj*>(GetSdrObject())->SetSize(aPos);
+ return true;
+ }
+ break;
+ }
+ default:
+ return SvxShape::setPropertyValueImpl( rName, pProperty, rValue );
+ }
+
+ throw IllegalArgumentException();
+}
+
+bool Svx3DSphereObject::getPropertyValueImpl( const OUString& rName, const SfxItemPropertyMapEntry* pProperty, css::uno::Any& rValue )
+{
+ switch( pProperty->nWID )
+ {
+ case OWN_ATTR_3D_VALUE_TRANSFORM_MATRIX:
+ {
+ // pack transformation to a homogeneous matrix
+ ConvertObjectToHomogenMatric( static_cast< E3dObject* >( GetSdrObject() ), rValue );
+ break;
+ }
+ case OWN_ATTR_3D_VALUE_POSITION:
+ {
+ // pack position
+ const basegfx::B3DPoint& rPos = static_cast<E3dSphereObj*>(GetSdrObject())->Center();
+ drawing::Position3D aPos;
+
+ aPos.PositionX = rPos.getX();
+ aPos.PositionY = rPos.getY();
+ aPos.PositionZ = rPos.getZ();
+
+ rValue <<= aPos;
+ break;
+ }
+ case OWN_ATTR_3D_VALUE_SIZE:
+ {
+ // pack size
+ const basegfx::B3DVector& rSize = static_cast<E3dSphereObj*>(GetSdrObject())->Size();
+ drawing::Direction3D aDir;
+
+ aDir.DirectionX = rSize.getX();
+ aDir.DirectionY = rSize.getY();
+ aDir.DirectionZ = rSize.getZ();
+
+ rValue <<= aDir;
+ break;
+ }
+ default:
+ return SvxShape::getPropertyValueImpl( rName, pProperty, rValue );
+ }
+
+ return true;
+}
+
+// css::lang::XServiceInfo
+uno::Sequence< OUString > SAL_CALL Svx3DSphereObject::getSupportedServiceNames()
+{
+ return comphelper::concatSequences(
+ SvxShape::getSupportedServiceNames(),
+ std::initializer_list<std::u16string_view>{ u"com.sun.star.drawing.Shape3D",
+ u"com.sun.star.drawing.Shape3DSphere" });
+}
+
+Svx3DLatheObject::Svx3DLatheObject(SdrObject* pObj)
+: SvxShape( pObj, getSvxMapProvider().GetMap(SVXMAP_3DLATHEOBJECT), getSvxMapProvider().GetPropertySet(SVXMAP_3DLATHEOBJECT, SdrObject::GetGlobalDrawObjectItemPool()) )
+{
+}
+
+Svx3DLatheObject::~Svx3DLatheObject() noexcept
+{
+}
+
+static bool PolyPolygonShape3D_to_B3dPolyPolygon(
+ const Any& rValue,
+ basegfx::B3DPolyPolygon& rResultPolygon,
+ bool bCorrectPolygon)
+{
+ drawing::PolyPolygonShape3D aSourcePolyPolygon;
+ if( !(rValue >>= aSourcePolyPolygon) )
+ return false;
+
+ sal_Int32 nOuterSequenceCount = aSourcePolyPolygon.SequenceX.getLength();
+ if(nOuterSequenceCount != aSourcePolyPolygon.SequenceY.getLength() || nOuterSequenceCount != aSourcePolyPolygon.SequenceZ.getLength())
+ return false;
+
+ const drawing::DoubleSequence* pInnerSequenceX = aSourcePolyPolygon.SequenceX.getConstArray();
+ const drawing::DoubleSequence* pInnerSequenceY = aSourcePolyPolygon.SequenceY.getConstArray();
+ const drawing::DoubleSequence* pInnerSequenceZ = aSourcePolyPolygon.SequenceZ.getConstArray();
+ for(sal_Int32 a(0);a<nOuterSequenceCount;a++)
+ {
+ sal_Int32 nInnerSequenceCount = pInnerSequenceX->getLength();
+ if(nInnerSequenceCount != pInnerSequenceY->getLength() || nInnerSequenceCount != pInnerSequenceZ->getLength())
+ {
+ return false;
+ }
+ basegfx::B3DPolygon aNewPolygon;
+ const double* pArrayX = pInnerSequenceX->getConstArray();
+ const double* pArrayY = pInnerSequenceY->getConstArray();
+ const double* pArrayZ = pInnerSequenceZ->getConstArray();
+ for(sal_Int32 b(0);b<nInnerSequenceCount;b++)
+ {
+ aNewPolygon.append(basegfx::B3DPoint(*pArrayX++,*pArrayY++,*pArrayZ++));
+ }
+ pInnerSequenceX++;
+ pInnerSequenceY++;
+ pInnerSequenceZ++;
+
+ // #i101520# correction is needed for imported polygons of old format,
+ // see callers
+ if(bCorrectPolygon)
+ {
+ basegfx::utils::checkClosed(aNewPolygon);
+ }
+
+ rResultPolygon.append(aNewPolygon);
+ }
+ return true;
+}
+
+static void B3dPolyPolygon_to_PolyPolygonShape3D( const basegfx::B3DPolyPolygon& rSourcePolyPolygon, Any& rValue )
+{
+ drawing::PolyPolygonShape3D aRetval;
+ aRetval.SequenceX.realloc(rSourcePolyPolygon.count());
+ aRetval.SequenceY.realloc(rSourcePolyPolygon.count());
+ aRetval.SequenceZ.realloc(rSourcePolyPolygon.count());
+ drawing::DoubleSequence* pOuterSequenceX = aRetval.SequenceX.getArray();
+ drawing::DoubleSequence* pOuterSequenceY = aRetval.SequenceY.getArray();
+ drawing::DoubleSequence* pOuterSequenceZ = aRetval.SequenceZ.getArray();
+ for(sal_uInt32 a(0);a<rSourcePolyPolygon.count();a++)
+ {
+ const basegfx::B3DPolygon& aPoly(rSourcePolyPolygon.getB3DPolygon(a));
+ sal_Int32 nPointCount(aPoly.count());
+ if(aPoly.isClosed()) nPointCount++;
+ pOuterSequenceX->realloc(nPointCount);
+ pOuterSequenceY->realloc(nPointCount);
+ pOuterSequenceZ->realloc(nPointCount);
+ double* pInnerSequenceX = pOuterSequenceX->getArray();
+ double* pInnerSequenceY = pOuterSequenceY->getArray();
+ double* pInnerSequenceZ = pOuterSequenceZ->getArray();
+ for(sal_uInt32 b(0);b<aPoly.count();b++)
+ {
+ const basegfx::B3DPoint aPoint(aPoly.getB3DPoint(b));
+ *pInnerSequenceX++ = aPoint.getX();
+ *pInnerSequenceY++ = aPoint.getY();
+ *pInnerSequenceZ++ = aPoint.getZ();
+ }
+ if(aPoly.isClosed())
+ {
+ const basegfx::B3DPoint aPoint(aPoly.getB3DPoint(0));
+ *pInnerSequenceX++ = aPoint.getX();
+ *pInnerSequenceY++ = aPoint.getY();
+ *pInnerSequenceZ++ = aPoint.getZ();
+ }
+ pOuterSequenceX++;
+ pOuterSequenceY++;
+ pOuterSequenceZ++;
+ }
+ rValue <<= aRetval;
+}
+
+bool Svx3DLatheObject::setPropertyValueImpl( const OUString& rName, const SfxItemPropertyMapEntry* pProperty, const css::uno::Any& rValue )
+{
+ switch( pProperty->nWID )
+ {
+ case OWN_ATTR_3D_VALUE_TRANSFORM_MATRIX:
+ {
+ // pack transformation matrix to the object
+ if( ConvertHomogenMatrixToObject( static_cast< E3dObject* >( GetSdrObject() ), rValue ) )
+ return true;
+ break;
+ }
+ case OWN_ATTR_3D_VALUE_POLYPOLYGON3D:
+ {
+ // pack polygon definition to the object
+ basegfx::B3DPolyPolygon aNewB3DPolyPolygon;
+
+ // #i101520# Probably imported
+ if( PolyPolygonShape3D_to_B3dPolyPolygon( rValue, aNewB3DPolyPolygon, true ) )
+ {
+ // #105127# SetPolyPoly3D sets the Svx3DVerticalSegmentsItem to the number
+ // of points of the polygon. Thus, value gets lost. To avoid this, rescue
+ // item here and re-set after setting the polygon.
+ const sal_uInt32 nPrevVerticalSegs(static_cast<E3dLatheObj*>(GetSdrObject())->GetVerticalSegments());
+
+ // set polygon
+ const basegfx::B3DHomMatrix aIdentity;
+ const basegfx::B2DPolyPolygon aB2DPolyPolygon(basegfx::utils::createB2DPolyPolygonFromB3DPolyPolygon(aNewB3DPolyPolygon, aIdentity));
+ static_cast<E3dLatheObj*>(GetSdrObject())->SetPolyPoly2D(aB2DPolyPolygon);
+ const sal_uInt32 nPostVerticalSegs(static_cast<E3dLatheObj*>(GetSdrObject())->GetVerticalSegments());
+
+ if(nPrevVerticalSegs != nPostVerticalSegs)
+ {
+ // restore the vertical segment count
+ static_cast<E3dLatheObj*>(GetSdrObject())->SetMergedItem(makeSvx3DVerticalSegmentsItem(nPrevVerticalSegs));
+ }
+ return true;
+ }
+ break;
+ }
+ default:
+ return SvxShape::setPropertyValueImpl( rName, pProperty, rValue );
+ }
+
+ throw IllegalArgumentException();
+}
+
+bool Svx3DLatheObject::getPropertyValueImpl( const OUString& rName, const SfxItemPropertyMapEntry* pProperty, css::uno::Any& rValue )
+{
+ switch( pProperty->nWID )
+ {
+ case OWN_ATTR_3D_VALUE_TRANSFORM_MATRIX:
+ {
+ // pack transformation to a homogeneous matrix
+ drawing::HomogenMatrix aHomMat;
+ basegfx::B3DHomMatrix aMat = static_cast<E3dObject*>(GetSdrObject())->GetTransform();
+ basegfx::utils::B3DHomMatrixToUnoHomogenMatrix(aMat, aHomMat);
+ rValue <<= aHomMat;
+ break;
+ }
+ case OWN_ATTR_3D_VALUE_POLYPOLYGON3D:
+ {
+ const basegfx::B2DPolyPolygon& rPolyPoly = static_cast<E3dLatheObj*>(GetSdrObject())->GetPolyPoly2D();
+ const basegfx::B3DPolyPolygon aB3DPolyPolygon(basegfx::utils::createB3DPolyPolygonFromB2DPolyPolygon(rPolyPoly));
+
+ B3dPolyPolygon_to_PolyPolygonShape3D(aB3DPolyPolygon, rValue);
+ break;
+ }
+ default:
+ return SvxShape::getPropertyValueImpl( rName, pProperty, rValue );
+ }
+
+ return true;
+}
+
+// css::lang::XServiceInfo
+uno::Sequence< OUString > SAL_CALL Svx3DLatheObject::getSupportedServiceNames()
+{
+ return comphelper::concatSequences(
+ SvxShape::getSupportedServiceNames(),
+ std::initializer_list<std::u16string_view>{ u"com.sun.star.drawing.Shape3D",
+ u"com.sun.star.drawing.Shape3DLathe" });
+}
+
+Svx3DExtrudeObject::Svx3DExtrudeObject(SdrObject* pObj)
+: SvxShape( pObj, getSvxMapProvider().GetMap(SVXMAP_3DEXTRUDEOBJECT), getSvxMapProvider().GetPropertySet(SVXMAP_3DEXTRUDEOBJECT, SdrObject::GetGlobalDrawObjectItemPool()) )
+{
+}
+
+Svx3DExtrudeObject::~Svx3DExtrudeObject() noexcept
+{
+}
+
+bool Svx3DExtrudeObject::setPropertyValueImpl( const OUString& rName, const SfxItemPropertyMapEntry* pProperty, const css::uno::Any& rValue )
+{
+ switch( pProperty->nWID )
+ {
+ case OWN_ATTR_3D_VALUE_TRANSFORM_MATRIX:
+ {
+ // pack transformation matrix to the object
+ if( ConvertHomogenMatrixToObject( static_cast< E3dObject* >( GetSdrObject() ), rValue ) )
+ return true;
+ break;
+ }
+
+ case OWN_ATTR_3D_VALUE_POLYPOLYGON3D:
+ {
+ // pack polygon definition to the object
+ basegfx::B3DPolyPolygon aNewB3DPolyPolygon;
+
+ // #i101520# Probably imported
+ if( PolyPolygonShape3D_to_B3dPolyPolygon( rValue, aNewB3DPolyPolygon, true ) )
+ {
+ // set polygon
+ const basegfx::B3DHomMatrix aIdentity;
+ const basegfx::B2DPolyPolygon aB2DPolyPolygon(basegfx::utils::createB2DPolyPolygonFromB3DPolyPolygon(aNewB3DPolyPolygon, aIdentity));
+ static_cast<E3dExtrudeObj*>(GetSdrObject())->SetExtrudePolygon(aB2DPolyPolygon);
+ return true;
+ }
+ break;
+ }
+ default:
+ return SvxShape::setPropertyValueImpl( rName, pProperty, rValue );
+ }
+
+ throw IllegalArgumentException();
+}
+
+bool Svx3DExtrudeObject::getPropertyValueImpl( const OUString& rName, const SfxItemPropertyMapEntry* pProperty, css::uno::Any& rValue )
+{
+ switch( pProperty->nWID )
+ {
+ case OWN_ATTR_3D_VALUE_TRANSFORM_MATRIX:
+ {
+ // pack transformation to a homogeneous matrix
+ drawing::HomogenMatrix aHomMat;
+ basegfx::B3DHomMatrix aMat = static_cast<E3dObject*>(GetSdrObject())->GetTransform();
+ basegfx::utils::B3DHomMatrixToUnoHomogenMatrix(aMat, aHomMat);
+ rValue <<= aHomMat;
+ break;
+ }
+
+ case OWN_ATTR_3D_VALUE_POLYPOLYGON3D:
+ {
+ // pack polygon definition
+ const basegfx::B2DPolyPolygon& rPolyPoly = static_cast<E3dExtrudeObj*>(GetSdrObject())->GetExtrudePolygon();
+ const basegfx::B3DPolyPolygon aB3DPolyPolygon(basegfx::utils::createB3DPolyPolygonFromB2DPolyPolygon(rPolyPoly));
+
+ B3dPolyPolygon_to_PolyPolygonShape3D(aB3DPolyPolygon, rValue);
+ break;
+ }
+ default:
+ return SvxShape::getPropertyValueImpl( rName, pProperty, rValue );
+ }
+
+ return true;
+}
+
+// css::lang::XServiceInfo
+uno::Sequence< OUString > SAL_CALL Svx3DExtrudeObject::getSupportedServiceNames()
+{
+ return comphelper::concatSequences(
+ SvxShape::getSupportedServiceNames(),
+ std::initializer_list<std::u16string_view>{ u"com.sun.star.drawing.Shape3D",
+ u"com.sun.star.drawing.Shape3DExtrude" });
+}
+
+Svx3DPolygonObject::Svx3DPolygonObject(SdrObject* pObj)
+: SvxShape( pObj, getSvxMapProvider().GetMap(SVXMAP_3DPOLYGONOBJECT), getSvxMapProvider().GetPropertySet(SVXMAP_3DPOLYGONOBJECT, SdrObject::GetGlobalDrawObjectItemPool()) )
+{
+}
+
+Svx3DPolygonObject::~Svx3DPolygonObject() noexcept
+{
+}
+
+bool Svx3DPolygonObject::setPropertyValueImpl( const OUString& rName, const SfxItemPropertyMapEntry* pProperty, const css::uno::Any& rValue )
+{
+ switch( pProperty->nWID )
+ {
+ case OWN_ATTR_3D_VALUE_TRANSFORM_MATRIX:
+ {
+ // pack transformation matrix to the object
+ if( ConvertHomogenMatrixToObject( static_cast< E3dObject* >( GetSdrObject() ), rValue ) )
+ return true;
+ break;
+ }
+
+ case OWN_ATTR_3D_VALUE_POLYPOLYGON3D:
+ {
+ // pack polygon definition to the object
+ basegfx::B3DPolyPolygon aNewB3DPolyPolygon;
+
+ // #i101520# Direct API data (e.g. from chart)
+ if( PolyPolygonShape3D_to_B3dPolyPolygon( rValue, aNewB3DPolyPolygon, false ) )
+ {
+ // set polygon
+ static_cast<E3dPolygonObj*>(GetSdrObject())->SetPolyPolygon3D(aNewB3DPolyPolygon);
+ return true;
+ }
+ break;
+ }
+ case OWN_ATTR_3D_VALUE_NORMALSPOLYGON3D:
+ {
+ // pack perpendicular definition to the object
+ basegfx::B3DPolyPolygon aNewB3DPolyPolygon;
+
+ // #i101520# Direct API data (e.g. from chart)
+ if( PolyPolygonShape3D_to_B3dPolyPolygon( rValue, aNewB3DPolyPolygon, false ) )
+ {
+ // set polygon
+ static_cast<E3dPolygonObj*>(GetSdrObject())->SetPolyNormals3D(aNewB3DPolyPolygon);
+ return true;
+ }
+ break;
+ }
+ case OWN_ATTR_3D_VALUE_TEXTUREPOLYGON3D:
+ {
+ // pack texture definition to the object
+ basegfx::B3DPolyPolygon aNewB3DPolyPolygon;
+
+ // #i101520# Direct API data (e.g. from chart)
+ if( PolyPolygonShape3D_to_B3dPolyPolygon( rValue, aNewB3DPolyPolygon, false ) )
+ {
+ // set polygon
+ const basegfx::B3DHomMatrix aIdentity;
+ const basegfx::B2DPolyPolygon aB2DPolyPolygon(basegfx::utils::createB2DPolyPolygonFromB3DPolyPolygon(aNewB3DPolyPolygon, aIdentity));
+ static_cast<E3dPolygonObj*>(GetSdrObject())->SetPolyTexture2D(aB2DPolyPolygon);
+ return true;
+ }
+ break;
+ }
+ case OWN_ATTR_3D_VALUE_LINEONLY:
+ {
+ bool bNew = false;
+ if( rValue >>= bNew )
+ {
+ static_cast<E3dPolygonObj*>(GetSdrObject())->SetLineOnly(bNew);
+ return true;
+ }
+ break;
+ }
+ default:
+ return SvxShape::setPropertyValueImpl( rName, pProperty, rValue );
+ }
+
+ throw IllegalArgumentException();
+}
+
+bool Svx3DPolygonObject::getPropertyValueImpl( const OUString& rName, const SfxItemPropertyMapEntry* pProperty, css::uno::Any& rValue )
+{
+ switch( pProperty->nWID )
+ {
+ case OWN_ATTR_3D_VALUE_TRANSFORM_MATRIX:
+ {
+ ConvertObjectToHomogenMatric( static_cast< E3dObject* >( GetSdrObject() ), rValue );
+ break;
+ }
+
+ case OWN_ATTR_3D_VALUE_POLYPOLYGON3D:
+ {
+ B3dPolyPolygon_to_PolyPolygonShape3D(static_cast<E3dPolygonObj*>(GetSdrObject())->GetPolyPolygon3D(),rValue);
+ break;
+ }
+
+ case OWN_ATTR_3D_VALUE_NORMALSPOLYGON3D:
+ {
+ B3dPolyPolygon_to_PolyPolygonShape3D(static_cast<E3dPolygonObj*>(GetSdrObject())->GetPolyNormals3D(),rValue);
+ break;
+ }
+
+ case OWN_ATTR_3D_VALUE_TEXTUREPOLYGON3D:
+ {
+ // pack texture definition
+ const basegfx::B2DPolyPolygon& rPolyPoly = static_cast<E3dPolygonObj*>(GetSdrObject())->GetPolyTexture2D();
+ const basegfx::B3DPolyPolygon aB3DPolyPolygon(basegfx::utils::createB3DPolyPolygonFromB2DPolyPolygon(rPolyPoly));
+
+ B3dPolyPolygon_to_PolyPolygonShape3D(aB3DPolyPolygon,rValue);
+ break;
+ }
+
+ case OWN_ATTR_3D_VALUE_LINEONLY:
+ {
+ rValue <<= static_cast<E3dPolygonObj*>(GetSdrObject())->GetLineOnly();
+ break;
+ }
+
+ default:
+ return SvxShape::getPropertyValueImpl( rName, pProperty, rValue );
+ }
+
+ return true;
+}
+
+// css::lang::XServiceInfo
+uno::Sequence< OUString > SAL_CALL Svx3DPolygonObject::getSupportedServiceNames()
+{
+ return comphelper::concatSequences(
+ SvxShape::getSupportedServiceNames(),
+ std::initializer_list<std::u16string_view>{ u"com.sun.star.drawing.Shape3D",
+ u"com.sun.star.drawing.Shape3DPolygon" });
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/unodraw/unoshap4.cxx b/svx/source/unodraw/unoshap4.cxx
new file mode 100644
index 000000000..18ce14f5e
--- /dev/null
+++ b/svx/source/unodraw/unoshap4.cxx
@@ -0,0 +1,1115 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * 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/XModifiable.hpp>
+#include <com/sun/star/embed/XEmbeddedObject.hpp>
+#include <com/sun/star/embed/XLinkageSupport.hpp>
+#include <com/sun/star/embed/NoVisualAreaSizeException.hpp>
+#include <com/sun/star/embed/Aspects.hpp>
+#include <com/sun/star/task/XInteractionHandler.hpp>
+#include <com/sun/star/ucb/CommandFailedException.hpp>
+#include <com/sun/star/ucb/ContentCreationException.hpp>
+
+#include <svx/svdoole2.hxx>
+#include <svx/svdomedia.hxx>
+#include <svx/svdpool.hxx>
+#include <comphelper/classids.hxx>
+#include <comphelper/embeddedobjectcontainer.hxx>
+#include <comphelper/propertysequence.hxx>
+#include <comphelper/propertyvalue.hxx>
+#include <cppuhelper/exc_hlp.hxx>
+
+#include <toolkit/helper/vclunohelper.hxx>
+
+#include <sot/exchange.hxx>
+
+#include <svx/svdmodel.hxx>
+#include "shapeimpl.hxx"
+
+#include <svx/unoshprp.hxx>
+
+#include <vcl/gdimtf.hxx>
+#include <vcl/wmf.hxx>
+#include <svtools/embedhlp.hxx>
+#include <sal/log.hxx>
+#include <tools/debug.hxx>
+#include <tools/globname.hxx>
+#include <tools/stream.hxx>
+
+#include <config_features.h>
+
+
+using namespace ::cppu;
+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::beans;
+
+
+SvxOle2Shape::SvxOle2Shape(SdrObject* pObject)
+: SvxShapeText( pObject, getSvxMapProvider().GetMap(SVXMAP_OLE2),
+ getSvxMapProvider().GetPropertySet(SVXMAP_OLE2,SdrObject::GetGlobalDrawObjectItemPool()) )
+{
+}
+
+SvxOle2Shape::SvxOle2Shape(SdrObject* pObject, const SfxItemPropertyMapEntry* pPropertyMap, const SvxItemPropertySet* pPropertySet)
+: SvxShapeText( pObject, pPropertyMap, pPropertySet )
+{
+}
+
+SvxOle2Shape::~SvxOle2Shape() noexcept
+{
+}
+
+//XPropertySet
+bool SvxOle2Shape::setPropertyValueImpl( const OUString& rName, const SfxItemPropertyMapEntry* pProperty, const css::uno::Any& rValue )
+{
+ switch( pProperty->nWID )
+ {
+ case OWN_ATTR_OLE_VISAREA:
+ {
+ // TODO/LATER: seems to make no sense for iconified object
+
+ awt::Rectangle aVisArea;
+ if( !(rValue >>= aVisArea))
+ break;
+ if( auto pOle2Obj = dynamic_cast<SdrOle2Obj* >(GetSdrObject()) )
+ {
+ Size aTmp( aVisArea.X + aVisArea.Width, aVisArea.Y + aVisArea.Height );
+ uno::Reference < embed::XEmbeddedObject > xObj = pOle2Obj->GetObjRef();
+ if( xObj.is() )
+ {
+ try
+ {
+ // the API handles with MapUnit::Map100thMM map mode
+ MapUnit aObjUnit = VCLUnoHelper::UnoEmbed2VCLMapUnit( xObj->getMapUnit( embed::Aspects::MSOLE_CONTENT ) );
+ aTmp = OutputDevice::LogicToLogic(aTmp, MapMode(MapUnit::Map100thMM), MapMode(aObjUnit));
+ xObj->setVisualAreaSize( embed::Aspects::MSOLE_CONTENT, awt::Size( aTmp.Width(), aTmp.Height() ) );
+ }
+ catch( uno::Exception& )
+ {
+ OSL_FAIL( "Couldn't set the visual area for the object!" );
+ }
+ }
+
+ return true;
+ }
+ break;
+ }
+ case OWN_ATTR_OLE_ASPECT:
+ {
+ sal_Int64 nAspect = 0;
+ if( rValue >>= nAspect )
+ {
+ static_cast<SdrOle2Obj*>(GetSdrObject())->SetAspect( nAspect );
+ return true;
+ }
+ break;
+ }
+ case OWN_ATTR_CLSID:
+ {
+ OUString aCLSID;
+ if( rValue >>= aCLSID )
+ {
+ // init an OLE object with a global name
+ SvGlobalName aClassName;
+ if( aClassName.MakeId( aCLSID ) )
+ {
+ if( createObject( aClassName ) )
+ return true;
+ }
+ }
+ break;
+ }
+ case OWN_ATTR_THUMBNAIL:
+ {
+ uno::Reference< graphic::XGraphic > xGraphic( rValue, uno::UNO_QUERY );
+ if( xGraphic.is() )
+ {
+ const Graphic aGraphic(xGraphic);
+ static_cast<SdrOle2Obj*>(GetSdrObject())->SetGraphic(aGraphic);
+ return true;
+ }
+ break;
+ }
+ case OWN_ATTR_VALUE_GRAPHIC:
+ {
+ uno::Reference< graphic::XGraphic > xGraphic( rValue, uno::UNO_QUERY );
+ if( xGraphic.is() )
+ {
+ SdrOle2Obj* pOle = dynamic_cast< SdrOle2Obj* >( GetSdrObject() );
+ if( pOle )
+ {
+ GraphicObject aGrafObj( xGraphic );
+ const Graphic& aGraphic( aGrafObj.GetGraphic() );
+ pOle->SetGraphicToObj( aGraphic );
+ }
+ return true;
+ }
+ break;
+ }
+ case OWN_ATTR_PERSISTNAME:
+ {
+ OUString aPersistName;
+ if( rValue >>= aPersistName )
+ {
+ SdrOle2Obj *pOle;
+#if OSL_DEBUG_LEVEL > 0
+ pOle = dynamic_cast<SdrOle2Obj*>(GetSdrObject());
+ assert(pOle);
+#else
+ pOle = static_cast<SdrOle2Obj*>(GetSdrObject());
+#endif
+ pOle->SetPersistName( aPersistName, this );
+ return true;
+ }
+ break;
+ }
+ case OWN_ATTR_OLE_LINKURL:
+ {
+ OUString aLinkURL;
+ if( rValue >>= aLinkURL )
+ {
+ createLink( aLinkURL );
+ return true;
+ }
+ break;
+ }
+ default:
+ return SvxShapeText::setPropertyValueImpl( rName, pProperty, rValue );
+ }
+
+ throw IllegalArgumentException();
+}
+
+bool SvxOle2Shape::getPropertyValueImpl( const OUString& rName, const SfxItemPropertyMapEntry* pProperty, css::uno::Any& rValue )
+{
+ switch( pProperty->nWID )
+ {
+ case OWN_ATTR_CLSID:
+ {
+ OUString aCLSID;
+ GetClassName_Impl(aCLSID);
+ rValue <<= aCLSID;
+ break;
+ }
+
+ case OWN_ATTR_INTERNAL_OLE:
+ {
+ OUString sCLSID;
+ rValue <<= SotExchange::IsInternal( GetClassName_Impl(sCLSID) );
+ break;
+ }
+
+ case OWN_ATTR_METAFILE:
+ {
+ SdrOle2Obj* pObj = dynamic_cast<SdrOle2Obj*>(GetSdrObject());
+ if( pObj )
+ {
+ const Graphic* pGraphic = pObj->GetGraphic();
+ if( pGraphic )
+ {
+ bool bIsWMF = false;
+ if ( pGraphic->IsGfxLink() )
+ {
+ GfxLink aLnk = pGraphic->GetGfxLink();
+ if ( aLnk.GetType() == GfxLinkType::NativeWmf )
+ {
+ bIsWMF = true;
+ uno::Sequence<sal_Int8> aSeq(reinterpret_cast<sal_Int8 const *>(aLnk.GetData()), static_cast<sal_Int32>(aLnk.GetDataSize()));
+ rValue <<= aSeq;
+ }
+ }
+ if ( !bIsWMF )
+ {
+ // #i119735# just use GetGDIMetaFile, it will create a buffered version of contained bitmap now automatically
+ GDIMetaFile aMtf(pObj->GetGraphic()->GetGDIMetaFile());
+ SvMemoryStream aDestStrm( 65535, 65535 );
+ ConvertGDIMetaFileToWMF( aMtf, aDestStrm, nullptr, false );
+ const uno::Sequence<sal_Int8> aSeq(
+ static_cast< const sal_Int8* >(aDestStrm.GetData()),
+ aDestStrm.GetEndOfData());
+ rValue <<= aSeq;
+ }
+ }
+ }
+ else
+ {
+ rValue = GetBitmap( true );
+ }
+ break;
+ }
+
+ case OWN_ATTR_OLE_VISAREA:
+ {
+ awt::Rectangle aVisArea;
+ if( dynamic_cast<const SdrOle2Obj* >(GetSdrObject()) != nullptr)
+ {
+ MapMode aMapMode( MapUnit::Map100thMM ); // the API uses this map mode
+ Size aTmp = static_cast<SdrOle2Obj*>(GetSdrObject())->GetOrigObjSize( &aMapMode ); // get the size in the requested map mode
+ aVisArea = awt::Rectangle( 0, 0, aTmp.Width(), aTmp.Height() );
+ }
+
+ rValue <<= aVisArea;
+ break;
+ }
+
+ case OWN_ATTR_OLESIZE:
+ {
+ Size aTmp( static_cast<SdrOle2Obj*>(GetSdrObject())->GetOrigObjSize() );
+ rValue <<= awt::Size( aTmp.Width(), aTmp.Height() );
+ break;
+ }
+
+ case OWN_ATTR_OLE_ASPECT:
+ {
+ rValue <<= static_cast<SdrOle2Obj*>(GetSdrObject())->GetAspect();
+ break;
+ }
+
+ case OWN_ATTR_OLEMODEL:
+ case OWN_ATTR_OLE_EMBEDDED_OBJECT:
+ case OWN_ATTR_OLE_EMBEDDED_OBJECT_NONEWCLIENT:
+ {
+ SdrOle2Obj* pObj = dynamic_cast<SdrOle2Obj*>( GetSdrObject() );
+ if( pObj )
+ {
+ uno::Reference < embed::XEmbeddedObject > xObj( pObj->GetObjRef() );
+ if ( xObj.is()
+ && ( pProperty->nWID == OWN_ATTR_OLE_EMBEDDED_OBJECT || pProperty->nWID == OWN_ATTR_OLE_EMBEDDED_OBJECT_NONEWCLIENT || svt::EmbeddedObjectRef::TryRunningState( xObj ) ) )
+ {
+ // Discussed with CL fue to the before GetPaintingPageView
+ // usage. Removed it, former fallback is used now
+ if ( pProperty->nWID == OWN_ATTR_OLEMODEL || pProperty->nWID == OWN_ATTR_OLE_EMBEDDED_OBJECT )
+ {
+ const bool bSuccess(pObj->AddOwnLightClient());
+ SAL_WARN_IF(!bSuccess, "svx.svdraw", "An object without client is provided!");
+ }
+
+ if ( pProperty->nWID == OWN_ATTR_OLEMODEL )
+ rValue <<= pObj->GetObjRef()->getComponent();
+ else
+ rValue <<= xObj;
+ }
+ }
+ break;
+ }
+
+ case OWN_ATTR_VALUE_GRAPHIC:
+ {
+ uno::Reference< graphic::XGraphic > xGraphic;
+ const Graphic* pGraphic = static_cast<SdrOle2Obj*>( GetSdrObject() )->GetGraphic();
+ if( pGraphic )
+ xGraphic = pGraphic->GetXGraphic();
+ rValue <<= xGraphic;
+ break;
+ }
+
+ case OWN_ATTR_THUMBNAIL:
+ {
+ uno::Reference< graphic::XGraphic > xGraphic;
+ const Graphic* pGraphic = static_cast<SdrOle2Obj*>( GetSdrObject() )->GetGraphic();
+ if( pGraphic )
+ xGraphic = pGraphic->GetXGraphic();
+ rValue <<= xGraphic;
+ break;
+ }
+ case OWN_ATTR_PERSISTNAME:
+ {
+ OUString aPersistName;
+ SdrOle2Obj* pOle = dynamic_cast< SdrOle2Obj* >( GetSdrObject() );
+
+ if( pOle )
+ {
+ aPersistName = pOle->GetPersistName();
+ if( !aPersistName.isEmpty() )
+ {
+ ::comphelper::IEmbeddedHelper* pPersist(GetSdrObject()->getSdrModelFromSdrObject().GetPersist());
+ if( (nullptr == pPersist) || !pPersist->getEmbeddedObjectContainer().HasEmbeddedObject( pOle->GetPersistName() ) )
+ aPersistName.clear();
+ }
+ }
+
+ rValue <<= aPersistName;
+ break;
+ }
+ case OWN_ATTR_OLE_LINKURL:
+ {
+ OUString aLinkURL;
+ SdrOle2Obj* pOle = dynamic_cast< SdrOle2Obj* >( GetSdrObject() );
+
+ if( pOle )
+ {
+ uno::Reference< embed::XLinkageSupport > xLink( pOle->GetObjRef(), uno::UNO_QUERY );
+ if ( xLink.is() && xLink->isLink() )
+ aLinkURL = xLink->getLinkURL();
+ }
+
+ rValue <<= aLinkURL;
+ break;
+ }
+ default:
+ return SvxShapeText::getPropertyValueImpl( rName, pProperty, rValue );
+ }
+
+ return true;
+}
+
+bool SvxOle2Shape::createObject( const SvGlobalName &aClassName )
+{
+ DBG_TESTSOLARMUTEX();
+
+ SdrOle2Obj* pOle2Obj = dynamic_cast< SdrOle2Obj* >( GetSdrObject() );
+ if ( !pOle2Obj || !pOle2Obj->IsEmpty() )
+ return false;
+
+ // create storage and inplace object
+ ::comphelper::IEmbeddedHelper* pPersist = GetSdrObject()->getSdrModelFromSdrObject().GetPersist();
+ OUString aPersistName;
+ OUString aTmpStr;
+ if( SvxShape::getPropertyValue( UNO_NAME_OLE2_PERSISTNAME ) >>= aTmpStr )
+ aPersistName = aTmpStr;
+
+ uno::Sequence<beans::PropertyValue> objArgs( comphelper::InitPropertySequence({
+ { "DefaultParentBaseURL", Any(pPersist->getDocumentBaseURL()) }
+ }));
+ //TODO/LATER: how to cope with creation failure?!
+ uno::Reference<embed::XEmbeddedObject> xObj(
+ pPersist->getEmbeddedObjectContainer().CreateEmbeddedObject(
+ aClassName.GetByteSequence(), objArgs, aPersistName));
+ if( xObj.is() )
+ {
+ tools::Rectangle aRect = pOle2Obj->GetLogicRect();
+ if ( aRect.GetWidth() == 101 && aRect.GetHeight() == 101 )
+ {
+ // TODO/LATER: is it possible that this method is used to create an iconified object?
+ // default size
+ try
+ {
+ awt::Size aSz = xObj->getVisualAreaSize( pOle2Obj->GetAspect() );
+ aRect.SetSize( Size( aSz.Width, aSz.Height ) );
+ }
+ catch( embed::NoVisualAreaSizeException& )
+ {}
+ pOle2Obj->SetLogicRect( aRect );
+ }
+ else
+ {
+ awt::Size aSz;
+ Size aSize = aRect.GetSize();
+ aSz.Width = aSize.Width();
+ aSz.Height = aSize.Height();
+ if (aSz.Width != 0 || aSz.Height != 0)
+ {
+ //HACK: can aSz legally be empty?
+ xObj->setVisualAreaSize( pOle2Obj->GetAspect(), aSz );
+ }
+ }
+
+ // connect the object after the visual area is set
+ aTmpStr = aPersistName;
+ SvxShape::setPropertyValue( UNO_NAME_OLE2_PERSISTNAME, Any( aTmpStr ) );
+
+ // the object is inserted during setting of PersistName property usually
+ if( pOle2Obj->IsEmpty() )
+ pOle2Obj->SetObjRef( xObj );
+ }
+
+ return xObj.is();
+}
+
+void SvxOle2Shape::createLink( const OUString& aLinkURL )
+{
+ DBG_TESTSOLARMUTEX();
+
+ SdrOle2Obj* pOle2Obj = dynamic_cast< SdrOle2Obj* >( GetSdrObject() );
+ if ( !pOle2Obj || !pOle2Obj->IsEmpty() )
+ return;
+
+ OUString aPersistName;
+
+ ::comphelper::IEmbeddedHelper* pPersist = GetSdrObject()->getSdrModelFromSdrObject().GetPersist();
+
+ uno::Sequence< beans::PropertyValue > aMediaDescr{ comphelper::makePropertyValue("URL",
+ aLinkURL) };
+
+ uno::Reference< task::XInteractionHandler > xInteraction = pPersist->getInteractionHandler();
+ if ( xInteraction.is() )
+ {
+ aMediaDescr.realloc( 2 );
+ auto pMediaDescr = aMediaDescr.getArray();
+ pMediaDescr[1].Name = "InteractionHandler";
+ pMediaDescr[1].Value <<= xInteraction;
+ }
+
+ //TODO/LATER: how to cope with creation failure?!
+ uno::Reference< embed::XEmbeddedObject > xObj =
+ pPersist->getEmbeddedObjectContainer().InsertEmbeddedLink( aMediaDescr , aPersistName );
+
+ if( !xObj.is() )
+ return;
+
+ tools::Rectangle aRect = pOle2Obj->GetLogicRect();
+ if ( aRect.GetWidth() == 101 && aRect.GetHeight() == 101 )
+ {
+ // default size
+ try
+ {
+ awt::Size aSz = xObj->getVisualAreaSize( pOle2Obj->GetAspect() );
+ aRect.SetSize( Size( aSz.Width, aSz.Height ) );
+ }
+ catch( embed::NoVisualAreaSizeException& )
+ {}
+ pOle2Obj->SetLogicRect( aRect );
+ }
+ else
+ {
+ awt::Size aSz;
+ Size aSize = pOle2Obj->GetLogicRect().GetSize();
+ aSz.Width = aSize.Width();
+ aSz.Height = aSize.Height();
+ xObj->setVisualAreaSize( pOle2Obj->GetAspect(), aSz );
+ }
+
+ // connect the object after the visual area is set
+ SvxShape::setPropertyValue( UNO_NAME_OLE2_PERSISTNAME, uno::Any( aPersistName ) );
+
+ // the object is inserted during setting of PersistName property usually
+ if ( pOle2Obj->IsEmpty() )
+ pOle2Obj->SetObjRef( xObj );
+}
+
+void SvxOle2Shape::resetModifiedState()
+{
+ SdrObject* pObject = GetSdrObject();
+ ::comphelper::IEmbeddedHelper* pPersist = pObject ? pObject->getSdrModelFromSdrObject().GetPersist() : nullptr;
+ if( pPersist && !pPersist->isEnableSetModified() )
+ {
+ SdrOle2Obj* pOle = dynamic_cast< SdrOle2Obj* >(pObject);
+ if( pOle && !pOle->IsEmpty() )
+ {
+ uno::Reference < util::XModifiable > xMod( pOle->GetObjRef(), uno::UNO_QUERY );
+ if( xMod.is() )
+ // TODO/MBA: what's this?!
+ xMod->setModified( false );
+ }
+ }
+}
+
+SvGlobalName SvxOle2Shape::GetClassName_Impl(OUString& rHexCLSID)
+{
+ DBG_TESTSOLARMUTEX();
+ SvGlobalName aClassName;
+ SdrOle2Obj* pOle2Obj = dynamic_cast< SdrOle2Obj* >( GetSdrObject() );
+
+ if( pOle2Obj )
+ {
+ rHexCLSID.clear();
+
+ if( pOle2Obj->IsEmpty() )
+ {
+ ::comphelper::IEmbeddedHelper* pPersist = GetSdrObject()->getSdrModelFromSdrObject().GetPersist();
+ if( pPersist )
+ {
+ uno::Reference < embed::XEmbeddedObject > xObj =
+ pPersist->getEmbeddedObjectContainer().GetEmbeddedObject( pOle2Obj->GetPersistName() );
+ if ( xObj.is() )
+ {
+ aClassName = SvGlobalName( xObj->getClassID() );
+ rHexCLSID = aClassName.GetHexName();
+ }
+ }
+ }
+
+ if (rHexCLSID.isEmpty())
+ {
+ const uno::Reference < embed::XEmbeddedObject >& xObj( pOle2Obj->GetObjRef() );
+ if ( xObj.is() )
+ {
+ aClassName = SvGlobalName( xObj->getClassID() );
+ rHexCLSID = aClassName.GetHexName();
+ }
+ }
+ }
+
+ return aClassName;
+}
+
+OUString SvxOle2Shape::GetAndClearInitialFrameURL()
+{
+ return OUString();
+}
+
+SvxAppletShape::SvxAppletShape(SdrObject* pObject)
+ : SvxOle2Shape( pObject, getSvxMapProvider().GetMap(SVXMAP_APPLET), getSvxMapProvider().GetPropertySet(SVXMAP_APPLET, SdrObject::GetGlobalDrawObjectItemPool()) )
+{
+ SetShapeType( "com.sun.star.drawing.AppletShape" );
+}
+
+SvxAppletShape::~SvxAppletShape() noexcept
+{
+}
+
+void SvxAppletShape::Create( SdrObject* pNewObj, SvxDrawPage* pNewPage )
+{
+ SvxShape::Create( pNewObj, pNewPage );
+ const SvGlobalName aAppletClassId( SO3_APPLET_CLASSID );
+ createObject(aAppletClassId);
+ SetShapeType( "com.sun.star.drawing.AppletShape" );
+}
+
+void SAL_CALL SvxAppletShape::setPropertyValue( const OUString& aPropertyName, const css::uno::Any& rValue )
+{
+ SvxShape::setPropertyValue( aPropertyName, rValue );
+ resetModifiedState();
+}
+
+void SAL_CALL SvxAppletShape::setPropertyValues( const css::uno::Sequence< OUString >& aPropertyNames, const css::uno::Sequence< css::uno::Any >& rValues )
+{
+ SvxShape::setPropertyValues( aPropertyNames, rValues );
+ resetModifiedState();
+}
+
+bool SvxAppletShape::setPropertyValueImpl( const OUString& rName, const SfxItemPropertyMapEntry* pProperty, const css::uno::Any& rValue )
+{
+ if( (pProperty->nWID >= OWN_ATTR_APPLET_DOCBASE) && (pProperty->nWID <= OWN_ATTR_APPLET_ISSCRIPT) )
+ {
+ if ( svt::EmbeddedObjectRef::TryRunningState( static_cast<SdrOle2Obj*>(GetSdrObject())->GetObjRef() ) )
+ {
+ uno::Reference < beans::XPropertySet > xSet( static_cast<SdrOle2Obj*>(GetSdrObject())->GetObjRef()->getComponent(), uno::UNO_QUERY );
+ if( xSet.is() )
+ {
+ // allow exceptions to pass through
+ xSet->setPropertyValue( rName, rValue );
+ }
+ }
+ return true;
+ }
+ else
+ {
+ return SvxOle2Shape::setPropertyValueImpl( rName, pProperty, rValue );
+ }
+}
+
+bool SvxAppletShape::getPropertyValueImpl( const OUString& rName, const SfxItemPropertyMapEntry* pProperty, css::uno::Any& rValue )
+{
+ if( (pProperty->nWID >= OWN_ATTR_APPLET_DOCBASE) && (pProperty->nWID <= OWN_ATTR_APPLET_ISSCRIPT) )
+ {
+ if ( svt::EmbeddedObjectRef::TryRunningState( static_cast<SdrOle2Obj*>(GetSdrObject())->GetObjRef() ) )
+ {
+ uno::Reference < beans::XPropertySet > xSet( static_cast<SdrOle2Obj*>(GetSdrObject())->GetObjRef()->getComponent(), uno::UNO_QUERY );
+ if( xSet.is() )
+ {
+ rValue = xSet->getPropertyValue( rName );
+ }
+ }
+ return true;
+ }
+ else
+ {
+ return SvxOle2Shape::getPropertyValueImpl( rName, pProperty, rValue );
+ }
+}
+
+SvxPluginShape::SvxPluginShape(SdrObject* pObject)
+ : SvxOle2Shape( pObject, getSvxMapProvider().GetMap(SVXMAP_PLUGIN), getSvxMapProvider().GetPropertySet(SVXMAP_PLUGIN, SdrObject::GetGlobalDrawObjectItemPool()) )
+{
+ SetShapeType( "com.sun.star.drawing.PluginShape" );
+}
+
+SvxPluginShape::~SvxPluginShape() noexcept
+{
+}
+
+void SvxPluginShape::Create( SdrObject* pNewObj, SvxDrawPage* pNewPage )
+{
+ SvxShape::Create( pNewObj, pNewPage );
+ const SvGlobalName aPluginClassId( SO3_PLUGIN_CLASSID );
+ createObject(aPluginClassId);
+ SetShapeType( "com.sun.star.drawing.PluginShape" );
+}
+
+void SAL_CALL SvxPluginShape::setPropertyValue( const OUString& aPropertyName, const css::uno::Any& rValue )
+{
+ SvxShape::setPropertyValue( aPropertyName, rValue );
+ resetModifiedState();
+}
+
+void SAL_CALL SvxPluginShape::setPropertyValues( const css::uno::Sequence< OUString >& aPropertyNames, const css::uno::Sequence< css::uno::Any >& rValues )
+{
+ SvxShape::setPropertyValues( aPropertyNames, rValues );
+ resetModifiedState();
+}
+
+bool SvxPluginShape::setPropertyValueImpl( const OUString& rName, const SfxItemPropertyMapEntry* pProperty, const css::uno::Any& rValue )
+{
+ if( (pProperty->nWID >= OWN_ATTR_PLUGIN_MIMETYPE) && (pProperty->nWID <= OWN_ATTR_PLUGIN_COMMANDS) )
+ {
+ if( svt::EmbeddedObjectRef::TryRunningState( static_cast<SdrOle2Obj*>(GetSdrObject())->GetObjRef() ) )
+ {
+ uno::Reference < beans::XPropertySet > xSet( static_cast<SdrOle2Obj*>(GetSdrObject())->GetObjRef()->getComponent(), uno::UNO_QUERY );
+ if( xSet.is() )
+ {
+ // allow exceptions to pass through
+ xSet->setPropertyValue( rName, rValue );
+ }
+ }
+ return true;
+ }
+ else
+ {
+ return SvxOle2Shape::setPropertyValueImpl( rName, pProperty, rValue );
+ }
+}
+
+bool SvxPluginShape::getPropertyValueImpl( const OUString& rName, const SfxItemPropertyMapEntry* pProperty, css::uno::Any& rValue )
+{
+ if( (pProperty->nWID >= OWN_ATTR_PLUGIN_MIMETYPE) && (pProperty->nWID <= OWN_ATTR_PLUGIN_COMMANDS) )
+ {
+ if( svt::EmbeddedObjectRef::TryRunningState( static_cast<SdrOle2Obj*>(GetSdrObject())->GetObjRef() ) )
+ {
+ uno::Reference < beans::XPropertySet > xSet( static_cast<SdrOle2Obj*>(GetSdrObject())->GetObjRef()->getComponent(), uno::UNO_QUERY );
+ if( xSet.is() )
+ {
+ rValue = xSet->getPropertyValue( rName );
+ }
+ }
+ return true;
+ }
+ else
+ {
+ return SvxOle2Shape::getPropertyValueImpl( rName, pProperty, rValue );
+ }
+}
+
+
+SvxFrameShape::SvxFrameShape(SdrObject* pObject)
+: SvxOle2Shape( pObject, getSvxMapProvider().GetMap(SVXMAP_FRAME), getSvxMapProvider().GetPropertySet(SVXMAP_FRAME, SdrObject::GetGlobalDrawObjectItemPool()) )
+{
+ SetShapeType( "com.sun.star.drawing.FrameShape" );
+}
+
+SvxFrameShape::~SvxFrameShape() noexcept
+{
+}
+
+OUString SvxFrameShape::GetAndClearInitialFrameURL()
+{
+ OUString sRet(m_sInitialFrameURL);
+ m_sInitialFrameURL.clear();
+ return sRet;
+}
+
+void SvxFrameShape::Create( SdrObject* pNewObj, SvxDrawPage* pNewPage )
+{
+ uno::Reference<beans::XPropertySet> xSet(static_cast<OWeakObject *>(this), uno::UNO_QUERY);
+ if (xSet)
+ xSet->getPropertyValue("FrameURL") >>= m_sInitialFrameURL;
+
+ SvxShape::Create( pNewObj, pNewPage );
+ const SvGlobalName aIFrameClassId( SO3_IFRAME_CLASSID );
+ createObject(aIFrameClassId);
+ SetShapeType( "com.sun.star.drawing.FrameShape" );
+}
+
+void SAL_CALL SvxFrameShape::setPropertyValue( const OUString& aPropertyName, const css::uno::Any& rValue )
+{
+ SvxShape::setPropertyValue( aPropertyName, rValue );
+ resetModifiedState();
+}
+
+void SAL_CALL SvxFrameShape::setPropertyValues( const css::uno::Sequence< OUString >& aPropertyNames, const css::uno::Sequence< css::uno::Any >& rValues )
+{
+ SvxShape::setPropertyValues( aPropertyNames, rValues );
+ resetModifiedState();
+}
+
+bool SvxFrameShape::setPropertyValueImpl( const OUString& rName, const SfxItemPropertyMapEntry* pProperty, const css::uno::Any& rValue )
+{
+ if( (pProperty->nWID >= OWN_ATTR_FRAME_URL) && (pProperty->nWID <= OWN_ATTR_FRAME_MARGIN_HEIGHT) )
+ {
+ if( svt::EmbeddedObjectRef::TryRunningState( static_cast<SdrOle2Obj*>(GetSdrObject())->GetObjRef() ) )
+ {
+ uno::Reference < beans::XPropertySet > xSet( static_cast<SdrOle2Obj*>(GetSdrObject())->GetObjRef()->getComponent(), uno::UNO_QUERY );
+ if( xSet.is() )
+ {
+ // allow exceptions to pass through
+ xSet->setPropertyValue( rName, rValue );
+ }
+ }
+ return true;
+ }
+ else
+ {
+ return SvxOle2Shape::setPropertyValueImpl( rName, pProperty, rValue );
+ }
+}
+
+bool SvxFrameShape::getPropertyValueImpl(const OUString& rName, const SfxItemPropertyMapEntry* pProperty,
+ css::uno::Any& rValue)
+{
+ if( (pProperty->nWID >= OWN_ATTR_FRAME_URL) && (pProperty->nWID <= OWN_ATTR_FRAME_MARGIN_HEIGHT) )
+ {
+ if( svt::EmbeddedObjectRef::TryRunningState( static_cast<SdrOle2Obj*>(GetSdrObject())->GetObjRef() ) )
+ {
+ uno::Reference < beans::XPropertySet > xSet( static_cast<SdrOle2Obj*>(GetSdrObject())->GetObjRef()->getComponent(), uno::UNO_QUERY );
+ if( xSet.is() )
+ {
+ rValue = xSet->getPropertyValue( rName );
+ }
+ }
+ return true;
+ }
+ else
+ {
+ return SvxOle2Shape::getPropertyValueImpl( rName, pProperty, rValue );
+ }
+}
+SvxMediaShape::SvxMediaShape(SdrObject* pObj, OUString const & referer)
+: SvxShape( pObj, getSvxMapProvider().GetMap(SVXMAP_MEDIA), getSvxMapProvider().GetPropertySet(SVXMAP_MEDIA, SdrObject::GetGlobalDrawObjectItemPool()) ),
+ referer_(referer)
+{
+ SetShapeType( "com.sun.star.drawing.MediaShape" );
+}
+
+
+SvxMediaShape::~SvxMediaShape() noexcept
+{
+}
+
+
+bool SvxMediaShape::setPropertyValueImpl( const OUString& rName, const SfxItemPropertyMapEntry* pProperty, const css::uno::Any& rValue )
+{
+ if( ((pProperty->nWID >= OWN_ATTR_MEDIA_URL) && (pProperty->nWID <= OWN_ATTR_MEDIA_ZOOM))
+ || (pProperty->nWID == OWN_ATTR_MEDIA_STREAM)
+ || (pProperty->nWID == OWN_ATTR_MEDIA_MIMETYPE)
+ || (pProperty->nWID == OWN_ATTR_VALUE_GRAPHIC)
+ || (pProperty->nWID == SDRATTR_GRAFCROP))
+ {
+#if HAVE_FEATURE_AVMEDIA
+ SdrMediaObj* pMedia = static_cast< SdrMediaObj* >( GetSdrObject() );
+ ::avmedia::MediaItem aItem;
+ bool bOk = false;
+#endif
+
+ switch( pProperty->nWID )
+ {
+ case OWN_ATTR_MEDIA_URL:
+#if HAVE_FEATURE_AVMEDIA
+ {
+ OUString aURL;
+ if( rValue >>= aURL )
+ {
+ bOk = true;
+ aItem.setURL( aURL, "", referer_ );
+ }
+ }
+#endif
+ break;
+
+ case OWN_ATTR_MEDIA_LOOP:
+#if HAVE_FEATURE_AVMEDIA
+ {
+ bool bLoop;
+
+ if( rValue >>= bLoop )
+ {
+ bOk = true;
+ aItem.setLoop( bLoop );
+ }
+ }
+#endif
+ break;
+
+ case OWN_ATTR_MEDIA_MUTE:
+#if HAVE_FEATURE_AVMEDIA
+ {
+ bool bMute;
+
+ if( rValue >>= bMute )
+ {
+ bOk = true;
+ aItem.setMute( bMute );
+ }
+ }
+#endif
+ break;
+
+ case OWN_ATTR_MEDIA_VOLUMEDB:
+#if HAVE_FEATURE_AVMEDIA
+ {
+ sal_Int16 nVolumeDB = sal_Int16();
+
+ if( rValue >>= nVolumeDB )
+ {
+ bOk = true;
+ aItem.setVolumeDB( nVolumeDB );
+ }
+ }
+#endif
+ break;
+
+ case OWN_ATTR_MEDIA_ZOOM:
+#if HAVE_FEATURE_AVMEDIA
+ {
+ css::media::ZoomLevel eLevel;
+
+ if( rValue >>= eLevel )
+ {
+ bOk = true;
+ aItem.setZoom( eLevel );
+ }
+ }
+#endif
+ break;
+
+ case OWN_ATTR_MEDIA_MIMETYPE:
+#if HAVE_FEATURE_AVMEDIA
+ {
+ OUString sMimeType;
+ if( rValue >>= sMimeType )
+ {
+ bOk = true;
+ aItem.setMimeType( sMimeType );
+ }
+ }
+#endif
+ break;
+
+ case OWN_ATTR_VALUE_GRAPHIC:
+#if HAVE_FEATURE_AVMEDIA
+ {
+ uno::Reference<graphic::XGraphic> xGraphic(rValue, uno::UNO_QUERY);
+ if (xGraphic.is())
+ {
+ bOk = true;
+ aItem.setGraphic(Graphic(xGraphic));
+ }
+ }
+#endif
+ break;
+
+ case SDRATTR_GRAFCROP:
+#if HAVE_FEATURE_AVMEDIA
+ {
+ text::GraphicCrop aCrop;
+ if (rValue >>= aCrop)
+ {
+ bOk = true;
+ aItem.setCrop(aCrop);
+ }
+ }
+#endif
+ break;
+
+ case OWN_ATTR_MEDIA_STREAM:
+#if HAVE_FEATURE_AVMEDIA
+ try
+ {
+ uno::Reference<io::XInputStream> xStream;
+ if (rValue >>= xStream)
+ {
+ pMedia->SetInputStream(xStream);
+ }
+ }
+ catch (const css::ucb::ContentCreationException&)
+ {
+ css::uno::Any exc = cppu::getCaughtException();
+ throw css::lang::WrappedTargetException(
+ "ContentCreationException Setting InputStream!",
+ static_cast<OWeakObject *>(this),
+ exc);
+ }
+ catch (const css::ucb::CommandFailedException&)
+ {
+ css::uno::Any anyEx = cppu::getCaughtException();
+ throw css::lang::WrappedTargetException(
+ "CommandFailedException Setting InputStream!",
+ static_cast<OWeakObject *>(this),
+ anyEx);
+ }
+#endif
+ break;
+
+ default:
+ OSL_FAIL("SvxMediaShape::setPropertyValueImpl(), unknown argument!");
+ }
+
+#if HAVE_FEATURE_AVMEDIA
+ if( bOk )
+ {
+ pMedia->setMediaProperties( aItem );
+ return true;
+ }
+#endif
+ }
+ else
+ {
+ return SvxShape::setPropertyValueImpl( rName, pProperty, rValue );
+ }
+
+ throw IllegalArgumentException();
+}
+
+
+bool SvxMediaShape::getPropertyValueImpl( const OUString& rName, const SfxItemPropertyMapEntry* pProperty, css::uno::Any& rValue )
+{
+ if ( ((pProperty->nWID >= OWN_ATTR_MEDIA_URL) &&
+ (pProperty->nWID <= OWN_ATTR_MEDIA_ZOOM))
+ || (pProperty->nWID == OWN_ATTR_MEDIA_STREAM)
+ || (pProperty->nWID == OWN_ATTR_MEDIA_TEMPFILEURL)
+ || (pProperty->nWID == OWN_ATTR_MEDIA_MIMETYPE)
+ || (pProperty->nWID == OWN_ATTR_FALLBACK_GRAPHIC)
+ || (pProperty->nWID == OWN_ATTR_VALUE_GRAPHIC)
+ || (pProperty->nWID == SDRATTR_GRAFCROP))
+ {
+ SdrMediaObj* pMedia = static_cast< SdrMediaObj* >( GetSdrObject() );
+#if HAVE_FEATURE_AVMEDIA
+ const ::avmedia::MediaItem aItem( pMedia->getMediaProperties() );
+#endif
+
+ switch( pProperty->nWID )
+ {
+ case OWN_ATTR_MEDIA_URL:
+#if HAVE_FEATURE_AVMEDIA
+ rValue <<= aItem.getURL();
+#endif
+ break;
+
+ case OWN_ATTR_MEDIA_LOOP:
+#if HAVE_FEATURE_AVMEDIA
+ rValue <<= aItem.isLoop();
+#endif
+ break;
+
+ case OWN_ATTR_MEDIA_MUTE:
+#if HAVE_FEATURE_AVMEDIA
+ rValue <<= aItem.isMute();
+#endif
+ break;
+
+ case OWN_ATTR_MEDIA_VOLUMEDB:
+#if HAVE_FEATURE_AVMEDIA
+ rValue <<= aItem.getVolumeDB();
+#endif
+ break;
+
+ case OWN_ATTR_MEDIA_ZOOM:
+#if HAVE_FEATURE_AVMEDIA
+ rValue <<= aItem.getZoom();
+#endif
+ break;
+
+ case OWN_ATTR_MEDIA_STREAM:
+ try
+ {
+ rValue <<= pMedia->GetInputStream();
+ }
+ catch (const css::ucb::ContentCreationException&)
+ {
+ css::uno::Any anyEx = cppu::getCaughtException();
+ throw css::lang::WrappedTargetException(
+ "ContentCreationException Getting InputStream!",
+ static_cast < OWeakObject * > ( this ), anyEx );
+ }
+ catch (const css::ucb::CommandFailedException&)
+ {
+ css::uno::Any anyEx = cppu::getCaughtException();
+ throw css::lang::WrappedTargetException(
+ "CommandFailedException Getting InputStream!",
+ static_cast < OWeakObject * > ( this ), anyEx );
+ }
+
+ break;
+
+ case OWN_ATTR_MEDIA_TEMPFILEURL:
+#if HAVE_FEATURE_AVMEDIA
+ rValue <<= aItem.getTempURL();
+#endif
+ break;
+
+ case OWN_ATTR_MEDIA_MIMETYPE:
+#if HAVE_FEATURE_AVMEDIA
+ rValue <<= aItem.getMimeType();
+#endif
+ break;
+
+ case OWN_ATTR_VALUE_GRAPHIC:
+#if HAVE_FEATURE_AVMEDIA
+ {
+ Graphic aGraphic = aItem.getGraphic();
+ if (!aGraphic.IsNone())
+ {
+ rValue <<= aGraphic.GetXGraphic();
+ }
+ }
+#endif
+ break;
+
+ case SDRATTR_GRAFCROP:
+#if HAVE_FEATURE_AVMEDIA
+ {
+ text::GraphicCrop aCrop = aItem.getCrop();
+ rValue <<= aCrop;
+ }
+#endif
+ break;
+
+ case OWN_ATTR_FALLBACK_GRAPHIC:
+ rValue <<= pMedia->getSnapshot();
+ break;
+
+ default:
+ OSL_FAIL("SvxMediaShape::getPropertyValueImpl(), unknown property!");
+ }
+ return true;
+ }
+ else
+ {
+ return SvxShape::getPropertyValueImpl( rName, pProperty, rValue );
+ }
+}
+
+bool SvxMediaShape::getPropertyStateImpl(const SfxItemPropertyMapEntry* pProperty,
+ css::beans::PropertyState& rState)
+{
+#if HAVE_FEATURE_AVMEDIA
+ if (pProperty->nWID == SDRATTR_GRAFCROP)
+ {
+ auto pMedia = static_cast<SdrMediaObj*>(GetSdrObject());
+ const avmedia::MediaItem& rItem = pMedia->getMediaProperties();
+ const text::GraphicCrop& rCrop = rItem.getCrop();
+ if (rCrop.Bottom > 0 || rCrop.Left > 0 || rCrop.Right > 0 || rCrop.Top > 0)
+ {
+ // The media has a crop, expose it to UNO-based export filters.
+ rState = beans::PropertyState_DIRECT_VALUE;
+ }
+ else
+ {
+ rState = beans::PropertyState_AMBIGUOUS_VALUE;
+ }
+ return true;
+ }
+#endif
+
+ return SvxShape::getPropertyStateImpl(pProperty, rState);
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/unodraw/unoshape.cxx b/svx/source/unodraw/unoshape.cxx
new file mode 100644
index 000000000..a0c81180d
--- /dev/null
+++ b/svx/source/unodraw/unoshape.cxx
@@ -0,0 +1,4042 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * 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/supportsservice.hxx>
+#include <com/sun/star/awt/XBitmap.hpp>
+#include <com/sun/star/awt/Rectangle.hpp>
+#include <com/sun/star/drawing/CircleKind.hpp>
+#include <com/sun/star/lang/NoSupportException.hpp>
+#include <vcl/svapp.hxx>
+#include <svl/itemprop.hxx>
+#include <o3tl/any.hxx>
+#include <osl/mutex.hxx>
+#include <editeng/unotext.hxx>
+#include <svx/svdobj.hxx>
+#include <svx/svdoole2.hxx>
+#include <svx/shapepropertynotifier.hxx>
+#include <comphelper/interfacecontainer3.hxx>
+#include <comphelper/scopeguard.hxx>
+#include <comphelper/servicehelper.hxx>
+#include <toolkit/helper/vclunohelper.hxx>
+#include <vcl/gfxlink.hxx>
+#include <vcl/virdev.hxx>
+#include <svx/sdangitm.hxx>
+#include <svx/svdlayer.hxx>
+#include <svx/svdopage.hxx>
+#include <svx/xflbstit.hxx>
+#include <svx/xflbmtit.hxx>
+#include <svx/xlnstit.hxx>
+#include <svx/xlnedit.hxx>
+#include <svx/svdmodel.hxx>
+#include <svx/svdobjkind.hxx>
+#include <svx/unopage.hxx>
+#include <svx/unoshape.hxx>
+#include <svx/unoshtxt.hxx>
+#include <svx/svdpage.hxx>
+#include <svx/unoshprp.hxx>
+#include <svx/svdograf.hxx>
+#include <svx/unoapi.hxx>
+#include <svx/svdomeas.hxx>
+#include <svx/svdpool.hxx>
+#include <tools/diagnose_ex.h>
+#include <tools/stream.hxx>
+#include <tools/gen.hxx>
+#include <tools/UnitConversion.hxx>
+#include <svx/svdoedge.hxx>
+#include <svx/svdocapt.hxx>
+#include <svx/obj3d.hxx>
+#include <svx/xflftrit.hxx>
+#include <svx/xtable.hxx>
+#include <svx/xbtmpit.hxx>
+#include <svx/xfillit0.hxx>
+#include <svx/xflgrit.hxx>
+#include <svx/xflhtit.hxx>
+#include <svx/xlineit0.hxx>
+#include <svx/xlndsit.hxx>
+#include <svx/unomaster.hxx>
+#include <basegfx/matrix/b2dhommatrix.hxx>
+#include <basegfx/matrix/b2dhommatrixtools.hxx>
+#include <basegfx/polygon/b2dpolypolygontools.hxx>
+#include "gluepts.hxx"
+#include "shapeimpl.hxx"
+#include <sal/log.hxx>
+
+#include <svx/lathe3d.hxx>
+#include <extrud3d.hxx>
+#include <svx/sdr/contact/viewcontact.hxx>
+#include <drawinglayer/converters.hxx>
+#include <drawinglayer/geometry/viewinformation2d.hxx>
+#include <drawinglayer/primitive2d/transformprimitive2d.hxx>
+
+#include <vcl/gdimtf.hxx>
+#include <vcl/wmf.hxx>
+#include <svx/sdtfsitm.hxx>
+#include <svx/svdopath.hxx>
+#include <svx/SvxXTextColumns.hxx>
+#include <svx/xflclit.hxx>
+
+#include <memory>
+#include <optional>
+#include <vector>
+#include <iostream>
+
+#include <bitmaps.hlst>
+
+using namespace ::osl;
+using namespace ::cppu;
+using namespace ::com::sun::star;
+using namespace ::com::sun::star::uno;
+using namespace ::com::sun::star::lang;
+using namespace ::com::sun::star::container;
+using svx::PropertyValueProvider;
+
+class GDIMetaFile;
+
+struct SvxShapeImpl
+{
+ std::optional<SfxItemSet> mxItemSet;
+ SdrObjKind mnObjId;
+ SvxShapeMaster* mpMaster;
+ bool mbHasSdrObjectOwnership;
+ bool mbDisposing;
+
+ /** CL, OD 2005-07-19 #i52126# - this is initially 0 and set when
+ * a SvxShape::Create() call is executed. It is then set to the created
+ * SdrObject so a multiple call to SvxShape::Create() with same SdrObject
+ * is prohibited.
+ */
+ ::tools::WeakReference< SdrObject > mpCreatedObj;
+
+ // for xComponent
+ ::comphelper::OInterfaceContainerHelper3<css::lang::XEventListener> maDisposeListeners;
+ svx::PropertyChangeNotifier maPropertyNotifier;
+
+ SvxShapeImpl( SvxShape& _rAntiImpl, ::osl::Mutex& _rMutex )
+ :mnObjId( SdrObjKind::NONE )
+ ,mpMaster( nullptr )
+ ,mbHasSdrObjectOwnership( false )
+ ,mbDisposing( false )
+ ,maDisposeListeners( _rMutex )
+ ,maPropertyNotifier( _rAntiImpl, _rMutex )
+ {
+ }
+};
+
+namespace {
+
+class ShapePositionProvider : public PropertyValueProvider
+{
+public:
+ explicit ShapePositionProvider( SvxShape& _shape )
+ :PropertyValueProvider( _shape, "Position" )
+ {
+ }
+
+protected:
+ virtual void getCurrentValue( Any& _out_rCurrentValue ) const override
+ {
+ _out_rCurrentValue <<= static_cast< SvxShape& >( getContext() ).getPosition();
+ }
+};
+
+
+class ShapeSizeProvider : public PropertyValueProvider
+{
+public:
+ static constexpr OUStringLiteral sSize = u"Size";
+ explicit ShapeSizeProvider( SvxShape& _shape )
+ :PropertyValueProvider( _shape, sSize )
+ {
+ }
+
+protected:
+ virtual void getCurrentValue( Any& _out_rCurrentValue ) const override
+ {
+ _out_rCurrentValue <<= static_cast< SvxShape& >( getContext() ).getSize();
+ }
+};
+
+/// Calculates what scaling factor will be used for autofit text scaling of this shape.
+sal_Int16 GetTextFitToSizeScale(SdrObject* pObject)
+{
+ SdrTextObj* pTextObj = dynamic_cast<SdrTextObj*>(pObject);
+ if (!pTextObj)
+ {
+ return 0;
+ }
+
+ const SfxItemSet& rTextObjSet = pTextObj->GetMergedItemSet();
+ if (rTextObjSet.GetItem<SdrTextFitToSizeTypeItem>(SDRATTR_TEXT_FITTOSIZE)->GetValue()
+ != drawing::TextFitToSizeType_AUTOFIT)
+ {
+ return 0;
+ }
+
+ return pTextObj->GetFontScaleY();
+}
+}
+
+SvxShape::SvxShape( SdrObject* pObject )
+: maSize(100,100)
+, mpImpl( new SvxShapeImpl( *this, m_aMutex ) )
+, mbIsMultiPropertyCall(false)
+, mpPropSet(getSvxMapProvider().GetPropertySet(SVXMAP_SHAPE, SdrObject::GetGlobalDrawObjectItemPool()))
+, maPropMapEntries(getSvxMapProvider().GetMap(SVXMAP_SHAPE))
+, mpSdrObjectWeakReference(pObject)
+, mnLockCount(0)
+{
+ impl_construct();
+}
+
+
+SvxShape::SvxShape( SdrObject* pObject, const SfxItemPropertyMapEntry* pEntries, const SvxItemPropertySet* pPropertySet )
+: maSize(100,100)
+, mpImpl( new SvxShapeImpl( *this, m_aMutex ) )
+, mbIsMultiPropertyCall(false)
+, mpPropSet(pPropertySet)
+, maPropMapEntries(pEntries)
+, mpSdrObjectWeakReference(pObject)
+, mnLockCount(0)
+{
+ impl_construct();
+}
+
+
+SvxShape::~SvxShape() noexcept
+{
+ ::SolarMutexGuard aGuard;
+
+ DBG_ASSERT( mnLockCount == 0, "Locked shape was disposed!" );
+
+ if ( mpImpl->mpMaster )
+ mpImpl->mpMaster->dispose();
+
+ if ( HasSdrObject() )
+ {
+ EndListening(GetSdrObject()->getSdrModelFromSdrObject());
+ GetSdrObject()->setUnoShape(nullptr);
+ }
+
+ if( HasSdrObjectOwnership() && HasSdrObject() )
+ {
+ mpImpl->mbHasSdrObjectOwnership = false;
+ SdrObject* pObject = GetSdrObject();
+ SdrObject::Free( pObject );
+ }
+
+ EndListeningAll(); // call explicitly within SolarMutexGuard
+}
+
+
+void SvxShape::TakeSdrObjectOwnership()
+{
+ mpImpl->mbHasSdrObjectOwnership = true;
+}
+
+
+void SvxShape::InvalidateSdrObject()
+{
+ if(HasSdrObject())
+ {
+ EndListening(GetSdrObject()->getSdrModelFromSdrObject());
+ }
+
+ if (HasSdrObjectOwnership())
+ return;
+
+ mpSdrObjectWeakReference.reset(nullptr);
+};
+
+bool SvxShape::HasSdrObjectOwnership() const
+{
+ if ( !mpImpl->mbHasSdrObjectOwnership )
+ return false;
+
+ OSL_ENSURE( HasSdrObject(), "SvxShape::HasSdrObjectOwnership: have the ownership of an object which I don't know!" );
+ return HasSdrObject();
+}
+
+
+void SvxShape::setShapeKind( SdrObjKind nKind )
+{
+ mpImpl->mnObjId = nKind;
+}
+
+
+SdrObjKind SvxShape::getShapeKind() const
+{
+ return mpImpl->mnObjId;
+}
+
+
+void SvxShape::setMaster( SvxShapeMaster* pMaster )
+{
+ mpImpl->mpMaster = pMaster;
+}
+
+
+uno::Any SAL_CALL SvxShape::queryAggregation( const uno::Type& rType )
+{
+ if( mpImpl->mpMaster )
+ {
+ uno::Any aAny;
+ if( mpImpl->mpMaster->queryAggregation( rType, aAny ) )
+ return aAny;
+ }
+
+ return SvxShape_UnoImplHelper::queryAggregation(rType);
+}
+
+const css::uno::Sequence< sal_Int8 > & SvxShape::getUnoTunnelId() noexcept
+{
+ static const comphelper::UnoIdInit theSvxShapeUnoTunnelId;
+ return theSvxShapeUnoTunnelId.getSeq();
+}
+
+sal_Int64 SAL_CALL SvxShape::getSomething( const css::uno::Sequence< sal_Int8 >& rId )
+{
+ return comphelper::getSomethingImpl(rId, this);
+}
+
+
+svx::PropertyChangeNotifier& SvxShape::getShapePropertyChangeNotifier()
+{
+ return mpImpl->maPropertyNotifier;
+}
+
+
+void SvxShape::impl_construct()
+{
+ mpImpl->maPropertyNotifier.registerProvider( svx::ShapePropertyProviderId::Position,
+ std::make_unique<ShapePositionProvider>( *this ) );
+ mpImpl->maPropertyNotifier.registerProvider( svx::ShapePropertyProviderId::Size,
+ std::make_unique<ShapeSizeProvider>( *this ) );
+
+ if ( HasSdrObject() )
+ {
+ StartListening(GetSdrObject()->getSdrModelFromSdrObject());
+ impl_initFromSdrObject();
+ }
+}
+
+
+void SvxShape::impl_initFromSdrObject()
+{
+ DBG_TESTSOLARMUTEX();
+ OSL_PRECOND( HasSdrObject(), "SvxShape::impl_initFromSdrObject: not to be called without SdrObject!" );
+ if ( !HasSdrObject() )
+ return;
+
+ osl_atomic_increment( &m_refCount );
+ {
+ GetSdrObject()->setUnoShape(this);
+ }
+ osl_atomic_decrement( &m_refCount );
+
+ // #i40944#
+ // Do not simply return when no model but do the type corrections
+ // following below.
+ const SdrInventor nInventor = GetSdrObject()->GetObjInventor();
+
+ // is it one of ours (svx) ?
+ if( !(nInventor == SdrInventor::Default || nInventor == SdrInventor::E3d || nInventor == SdrInventor::FmForm) )
+ return;
+
+ if(nInventor == SdrInventor::FmForm)
+ {
+ mpImpl->mnObjId = SdrObjKind::UNO;
+ }
+ else
+ {
+ mpImpl->mnObjId = GetSdrObject()->GetObjIdentifier();
+ }
+
+ switch(mpImpl->mnObjId)
+ {
+ case SdrObjKind::CircleCut: // segment of circle
+ case SdrObjKind::CircleArc: // arc of circle
+ case SdrObjKind::CircleSection: // sector
+ mpImpl->mnObjId = SdrObjKind::CircleOrEllipse;
+ break;
+ default: ;
+ }
+}
+
+
+void SvxShape::Create( SdrObject* pNewObj, SvxDrawPage* /*pNewPage*/ )
+{
+ DBG_TESTSOLARMUTEX();
+
+ assert( pNewObj && "SvxShape::Create: invalid new object!" );
+ if ( !pNewObj )
+ return;
+
+ SdrObject* pCreatedObj = mpImpl->mpCreatedObj.get();
+ assert( ( !pCreatedObj || ( pCreatedObj == pNewObj ) ) &&
+ "SvxShape::Create: the same shape used for two different objects?! Strange ..." );
+
+ // Correct condition (#i52126#)
+ if ( pCreatedObj == pNewObj )
+ return;
+
+ // Correct condition (#i52126#)
+ mpImpl->mpCreatedObj = pNewObj;
+
+ if( HasSdrObject() )
+ {
+ EndListening( GetSdrObject()->getSdrModelFromSdrObject() );
+ }
+
+ mpSdrObjectWeakReference.reset( pNewObj );
+
+ if( HasSdrObject() )
+ {
+ StartListening( GetSdrObject()->getSdrModelFromSdrObject() );
+ }
+
+ OSL_ENSURE( !mbIsMultiPropertyCall, "SvxShape::Create: hmm?" );
+ // this was previously set in impl_initFromSdrObject, but I think it was superfluous
+ // (it definitely was in the other context where it was called, but I strongly suppose
+ // it was also superfluous when called from here)
+ impl_initFromSdrObject();
+
+ ObtainSettingsFromPropertySet( *mpPropSet );
+
+ // save user call
+ SdrObjUserCall* pUser = GetSdrObject()->GetUserCall();
+ GetSdrObject()->SetUserCall(nullptr);
+
+ setPosition( maPosition );
+ setSize( maSize );
+
+ // restore user call after we set the initial size
+ GetSdrObject()->SetUserCall( pUser );
+
+ // if this shape was already named, use this name
+ if( !maShapeName.isEmpty() )
+ {
+ GetSdrObject()->SetName( maShapeName );
+ maShapeName.clear();
+ }
+}
+
+void SvxShape::ForceMetricToItemPoolMetric(Pair& rPoint) const noexcept
+{
+ DBG_TESTSOLARMUTEX();
+ if(!HasSdrObject())
+ return;
+
+ MapUnit eMapUnit(GetSdrObject()->getSdrModelFromSdrObject().GetItemPool().GetMetric(0));
+ if(eMapUnit == MapUnit::Map100thMM)
+ return;
+
+ if (const auto eTo = MapToO3tlLength(eMapUnit); eTo != o3tl::Length::invalid)
+ {
+ rPoint.A() = o3tl::convert(rPoint.A(), o3tl::Length::mm100, eTo);
+ rPoint.B() = o3tl::convert(rPoint.B(), o3tl::Length::mm100, eTo);
+ }
+ else
+ {
+ OSL_FAIL("AW: Missing unit translation to PoolMetric!");
+ }
+}
+
+void SvxShape::ForceMetricToItemPoolMetric(basegfx::B2DPolyPolygon& rPolyPolygon) const noexcept
+{
+ DBG_TESTSOLARMUTEX();
+ if(!HasSdrObject())
+ return;
+
+ GetSdrObject()->ForceMetricToItemPoolMetric(rPolyPolygon);
+}
+
+void SvxShape::ForceMetricToItemPoolMetric(basegfx::B2DHomMatrix& rB2DHomMatrix) const noexcept
+{
+ DBG_TESTSOLARMUTEX();
+ if(!HasSdrObject())
+ return;
+
+ MapUnit eMapUnit(GetSdrObject()->getSdrModelFromSdrObject().GetItemPool().GetMetric(0));
+ if(eMapUnit == MapUnit::Map100thMM)
+ return;
+
+ if (const auto eTo = MapToO3tlLength(eMapUnit); eTo != o3tl::Length::invalid)
+ {
+ const double fConvert(o3tl::convert(1.0, o3tl::Length::mm100, eTo));
+ const basegfx::utils::B2DHomMatrixBufferedDecompose aDecomposedTransform(rB2DHomMatrix);
+ rB2DHomMatrix = basegfx::utils::createScaleShearXRotateTranslateB2DHomMatrix(
+ aDecomposedTransform.getScale() * fConvert,
+ aDecomposedTransform.getShearX(),
+ aDecomposedTransform.getRotate(),
+ aDecomposedTransform.getTranslate() * fConvert);
+ }
+ else
+ {
+ OSL_FAIL("Missing unit translation to PoolMetric!");
+ }
+}
+
+void SvxShape::ForceMetricTo100th_mm(Pair& rPoint) const noexcept
+{
+ DBG_TESTSOLARMUTEX();
+ if(!HasSdrObject())
+ return;
+
+ MapUnit eMapUnit = GetSdrObject()->getSdrModelFromSdrObject().GetItemPool().GetMetric(0);
+ if(eMapUnit == MapUnit::Map100thMM)
+ return;
+
+ if (const auto eFrom = MapToO3tlLength(eMapUnit); eFrom != o3tl::Length::invalid)
+ {
+ rPoint.A() = o3tl::convert(rPoint.A(), eFrom, o3tl::Length::mm100);
+ rPoint.B() = o3tl::convert(rPoint.B(), eFrom, o3tl::Length::mm100);
+ }
+ else
+ {
+ OSL_FAIL("AW: Missing unit translation to 100th mm!");
+ }
+}
+
+void SvxShape::ForceMetricTo100th_mm(basegfx::B2DPolyPolygon& rPolyPolygon) const noexcept
+{
+ DBG_TESTSOLARMUTEX();
+ if(!HasSdrObject())
+ return;
+
+ MapUnit eMapUnit = GetSdrObject()->getSdrModelFromSdrObject().GetItemPool().GetMetric(0);
+ if(eMapUnit == MapUnit::Map100thMM)
+ return;
+
+ if (const auto eFrom = MapToO3tlLength(eMapUnit); eFrom != o3tl::Length::invalid)
+ {
+ const double fConvert(o3tl::convert(1.0, eFrom, o3tl::Length::mm100));
+ rPolyPolygon.transform(basegfx::utils::createScaleB2DHomMatrix(fConvert, fConvert));
+ }
+ else
+ {
+ OSL_FAIL("Missing unit translation to 100th mm!");
+ }
+}
+
+void SvxShape::ForceMetricTo100th_mm(basegfx::B2DHomMatrix& rB2DHomMatrix) const noexcept
+{
+ DBG_TESTSOLARMUTEX();
+ if(!HasSdrObject())
+ return;
+
+ MapUnit eMapUnit = GetSdrObject()->getSdrModelFromSdrObject().GetItemPool().GetMetric(0);
+ if(eMapUnit == MapUnit::Map100thMM)
+ return;
+
+ if (const auto eFrom = MapToO3tlLength(eMapUnit); eFrom != o3tl::Length::invalid)
+ {
+ const double fConvert(o3tl::convert(1.0, eFrom, o3tl::Length::mm100));
+ const basegfx::utils::B2DHomMatrixBufferedDecompose aDecomposedTransform(rB2DHomMatrix);
+ rB2DHomMatrix = basegfx::utils::createScaleShearXRotateTranslateB2DHomMatrix(
+ aDecomposedTransform.getScale() * fConvert,
+ aDecomposedTransform.getShearX(),
+ aDecomposedTransform.getRotate(),
+ aDecomposedTransform.getTranslate() * fConvert);
+ }
+ else
+ {
+ OSL_FAIL("Missing unit translation to 100th mm!");
+ }
+}
+
+static void SvxItemPropertySet_ObtainSettingsFromPropertySet(const SvxItemPropertySet& rPropSet, SvxItemPropertySetUsrAnys& rAnys,
+ SfxItemSet& rSet, const uno::Reference< beans::XPropertySet >& xSet, const SfxItemPropertyMap* pMap )
+{
+ if(!rAnys.AreThereOwnUsrAnys())
+ return;
+
+ const SfxItemPropertyMap& rSrc = rPropSet.getPropertyMap();
+
+ for(const SfxItemPropertyMapEntry* pSrcProp : rSrc.getPropertyEntries())
+ {
+ const sal_uInt16 nWID = pSrcProp->nWID;
+ if(SfxItemPool::IsWhich(nWID)
+ && (nWID < OWN_ATTR_VALUE_START || nWID > OWN_ATTR_VALUE_END)
+ && rAnys.GetUsrAnyForID(*pSrcProp))
+ rSet.Put(rSet.GetPool()->GetDefaultItem(nWID));
+ }
+
+ for(const SfxItemPropertyMapEntry* pSrcProp : rSrc.getPropertyEntries())
+ {
+ if(pSrcProp->nWID)
+ {
+ uno::Any* pUsrAny = rAnys.GetUsrAnyForID(*pSrcProp);
+ if(pUsrAny)
+ {
+ // search for equivalent entry in pDst
+ const SfxItemPropertyMapEntry* pEntry = pMap->getByName( pSrcProp->aName );
+ if(pEntry)
+ {
+ // entry found
+ if(pEntry->nWID >= OWN_ATTR_VALUE_START && pEntry->nWID <= OWN_ATTR_VALUE_END)
+ {
+ // special ID in PropertySet, can only be set
+ // directly at the object
+ xSet->setPropertyValue( pSrcProp->aName, *pUsrAny);
+ }
+ else
+ {
+ SvxItemPropertySet_setPropertyValue(pEntry, *pUsrAny, rSet);
+ }
+ }
+ }
+ }
+ }
+ rAnys.ClearAllUsrAny();
+}
+
+
+void SvxShape::ObtainSettingsFromPropertySet(const SvxItemPropertySet& rPropSet)
+{
+ DBG_TESTSOLARMUTEX();
+ if(HasSdrObject() && maUrsAnys.AreThereOwnUsrAnys())
+ {
+ SfxItemSetFixed<SDRATTR_START, SDRATTR_END> aSet( GetSdrObject()->getSdrModelFromSdrObject().GetItemPool() );
+ Reference< beans::XPropertySet > xShape(this);
+ SvxItemPropertySet_ObtainSettingsFromPropertySet(rPropSet, maUrsAnys, aSet, xShape, &mpPropSet->getPropertyMap() );
+
+ GetSdrObject()->SetMergedItemSetAndBroadcast(aSet);
+
+ GetSdrObject()->ApplyNotPersistAttr( aSet );
+ }
+}
+
+uno::Any SvxShape::GetBitmap( bool bMetaFile /* = false */ ) const
+{
+ DBG_TESTSOLARMUTEX();
+ uno::Any aAny;
+
+ if(!HasSdrObject() || nullptr == GetSdrObject()->getSdrPageFromSdrObject())
+ {
+ return aAny;
+ }
+
+ // tdf#118662 Emulate old behaviour of XclObjComment (see there)
+ const SdrCaptionObj* pSdrCaptionObj(dynamic_cast<SdrCaptionObj*>(GetSdrObject()));
+ if(nullptr != pSdrCaptionObj && pSdrCaptionObj->isSuppressGetBitmap())
+ {
+ return aAny;
+ }
+
+ // tdf#119180 If we do not ask for Metafile and we access a SdrGrafObj,
+ // and content exists and is a Bitmap, take the shortcut.
+ // Do *not* do this for Metafile - as can be seen, requested in that case
+ // is a byte-sequence of a saved WMF format file (see below)
+ if(!bMetaFile)
+ {
+ const SdrGrafObj* pSdrGrafObj(dynamic_cast<SdrGrafObj*>(GetSdrObject()));
+
+ if(nullptr != pSdrGrafObj)
+ {
+ const Graphic& rGraphic(pSdrGrafObj->GetGraphic());
+
+ if(GraphicType::Bitmap == rGraphic.GetType())
+ {
+ Reference< awt::XBitmap > xBmp( rGraphic.GetXGraphic(), UNO_QUERY );
+ aAny <<= xBmp;
+
+ return aAny;
+ }
+ }
+ }
+
+ // tdf#118662 instead of creating an E3dView instance every time to paint
+ // a single SdrObject, use the existing SdrObject::SingleObjectPainter to
+ // use less resources and runtime
+ if(bMetaFile)
+ {
+ ScopedVclPtrInstance< VirtualDevice > pVDev;
+ const tools::Rectangle aBoundRect(GetSdrObject()->GetCurrentBoundRect());
+ GDIMetaFile aMtf;
+
+ pVDev->SetMapMode(MapMode(MapUnit::Map100thMM));
+ pVDev->EnableOutput(false);
+ aMtf.Record(pVDev);
+ GetSdrObject()->SingleObjectPainter(*pVDev);
+ aMtf.Stop();
+ aMtf.WindStart();
+ aMtf.Move(-aBoundRect.Left(), -aBoundRect.Top());
+ aMtf.SetPrefMapMode(MapMode(MapUnit::Map100thMM));
+ aMtf.SetPrefSize(aBoundRect.GetSize());
+
+ SvMemoryStream aDestStrm(65535, 65535);
+
+ ConvertGDIMetaFileToWMF(
+ aMtf,
+ aDestStrm,
+ nullptr,
+ false);
+
+ const uno::Sequence<sal_Int8> aSeq(
+ static_cast< const sal_Int8* >(aDestStrm.GetData()),
+ aDestStrm.GetEndOfData());
+
+ aAny <<= aSeq;
+ }
+ else
+ {
+ drawinglayer::primitive2d::Primitive2DContainer xPrimitives;
+ GetSdrObject()->GetViewContact().getViewIndependentPrimitive2DContainer(xPrimitives);
+
+ if(!xPrimitives.empty())
+ {
+ const drawinglayer::geometry::ViewInformation2D aViewInformation2D;
+ basegfx::B2DRange aRange(
+ xPrimitives.getB2DRange(aViewInformation2D));
+
+ if(!aRange.isEmpty())
+ {
+ const MapUnit aSourceMapUnit(GetSdrObject()->getSdrModelFromSdrObject().GetScaleUnit());
+
+ if(MapUnit::Map100thMM != aSourceMapUnit)
+ {
+ // tdf#119180 This is UNO API and thus works in 100th_mm,
+ // so if the MapMode from the used SdrModel is *not* equal
+ // to Map100thMM we need to embed the primitives to an adapting
+ // homogen transformation for correct values
+ const basegfx::B2DHomMatrix aMapTransform(
+ OutputDevice::LogicToLogic(
+ MapMode(aSourceMapUnit),
+ MapMode(MapUnit::Map100thMM)));
+
+ // Embed primitives to get them in 100th mm
+ drawinglayer::primitive2d::Primitive2DReference xEmbedRef(
+ new drawinglayer::primitive2d::TransformPrimitive2D(
+ aMapTransform,
+ std::move(xPrimitives)));
+
+ xPrimitives = drawinglayer::primitive2d::Primitive2DContainer { xEmbedRef };
+
+ // Update basegfx::B2DRange aRange, too. Here we have the
+ // choice of transforming the existing value or get newly by
+ // again using 'xPrimitives.getB2DRange(aViewInformation2D)'
+ aRange.transform(aMapTransform);
+ }
+
+ const BitmapEx aBmp(
+ drawinglayer::convertPrimitive2DContainerToBitmapEx(
+ std::move(xPrimitives),
+ aRange));
+
+ Graphic aGraph(aBmp);
+
+ aGraph.SetPrefSize(aBmp.GetPrefSize());
+ aGraph.SetPrefMapMode(aBmp.GetPrefMapMode());
+
+ Reference< awt::XBitmap > xBmp( aGraph.GetXGraphic(), UNO_QUERY );
+ aAny <<= xBmp;
+ }
+ }
+ }
+
+ return aAny;
+}
+
+uno::Sequence< uno::Type > SAL_CALL SvxShape::getTypes()
+{
+ if( mpImpl->mpMaster )
+ {
+ return mpImpl->mpMaster->getTypes();
+ }
+ else
+ {
+ return _getTypes();
+ }
+}
+
+
+uno::Sequence< uno::Type > const & SvxShape::_getTypes()
+{
+ switch( mpImpl->mnObjId )
+ {
+ // shapes without text
+ case SdrObjKind::Page:
+ case SdrObjKind::OLEPluginFrame:
+ case SdrObjKind::OLE2Plugin:
+ case SdrObjKind::OLE2Applet:
+ case SdrObjKind::E3D_Cube:
+ case SdrObjKind::E3D_Sphere:
+ case SdrObjKind::E3D_Lathe:
+ case SdrObjKind::E3D_Extrusion:
+ case SdrObjKind::E3D_Polygon:
+ case SdrObjKind::Media:
+ case SdrObjKind::Table:
+ {
+ static uno::Sequence<uno::Type> aTypeSequence{
+ cppu::UnoType<drawing::XShape>::get(),
+ cppu::UnoType<lang::XComponent>::get(),
+ cppu::UnoType<beans::XPropertySet>::get(),
+ cppu::UnoType<beans::XMultiPropertySet>::get(),
+ cppu::UnoType<beans::XPropertyState>::get(),
+ cppu::UnoType<beans::XMultiPropertyStates>::get(),
+ cppu::UnoType<drawing::XGluePointsSupplier>::get(),
+ cppu::UnoType<container::XChild>::get(),
+ cppu::UnoType<lang::XServiceInfo>::get(),
+ cppu::UnoType<lang::XTypeProvider>::get(),
+ cppu::UnoType<lang::XUnoTunnel>::get(),
+ cppu::UnoType<container::XNamed>::get(),
+ };
+
+ return aTypeSequence;
+ }
+ // group shape
+ case SdrObjKind::Group:
+ {
+ static uno::Sequence<uno::Type> aTypeSequence{
+ cppu::UnoType<drawing::XShape>::get(),
+ cppu::UnoType<lang::XComponent>::get(),
+ cppu::UnoType<beans::XPropertySet>::get(),
+ cppu::UnoType<beans::XMultiPropertySet>::get(),
+ cppu::UnoType<beans::XPropertyState>::get(),
+ cppu::UnoType<beans::XMultiPropertyStates>::get(),
+ cppu::UnoType<drawing::XGluePointsSupplier>::get(),
+ cppu::UnoType<container::XChild>::get(),
+ cppu::UnoType<lang::XServiceInfo>::get(),
+ cppu::UnoType<lang::XTypeProvider>::get(),
+ cppu::UnoType<lang::XUnoTunnel>::get(),
+ cppu::UnoType<container::XNamed>::get(),
+ cppu::UnoType<drawing::XShapes>::get(),
+ cppu::UnoType<drawing::XShapeGroup>::get(),
+ };
+
+ return aTypeSequence;
+ }
+ // connector shape
+ case SdrObjKind::Edge:
+ {
+ static uno::Sequence<uno::Type> aTypeSequence{
+ cppu::UnoType<drawing::XShape>::get(),
+ cppu::UnoType<lang::XComponent>::get(),
+ cppu::UnoType<beans::XPropertySet>::get(),
+ cppu::UnoType<beans::XMultiPropertySet>::get(),
+ cppu::UnoType<beans::XPropertyState>::get(),
+ cppu::UnoType<beans::XMultiPropertyStates>::get(),
+ cppu::UnoType<drawing::XGluePointsSupplier>::get(),
+ cppu::UnoType<container::XChild>::get(),
+ cppu::UnoType<lang::XServiceInfo>::get(),
+ cppu::UnoType<lang::XTypeProvider>::get(),
+ cppu::UnoType<lang::XUnoTunnel>::get(),
+ cppu::UnoType<container::XNamed>::get(),
+ cppu::UnoType<drawing::XConnectorShape>::get(),
+ // from SvxUnoTextBase::getTypes()
+ cppu::UnoType<text::XTextAppend>::get(),
+ cppu::UnoType<text::XTextCopy>::get(),
+ cppu::UnoType<container::XEnumerationAccess>::get(),
+ cppu::UnoType<text::XTextRangeMover>::get(),
+ };
+
+ return aTypeSequence;
+ }
+ // control shape
+ case SdrObjKind::UNO:
+ {
+ static uno::Sequence<uno::Type> aTypeSequence{
+ cppu::UnoType<drawing::XShape>::get(),
+ cppu::UnoType<lang::XComponent>::get(),
+ cppu::UnoType<beans::XPropertySet>::get(),
+ cppu::UnoType<beans::XMultiPropertySet>::get(),
+ cppu::UnoType<beans::XPropertyState>::get(),
+ cppu::UnoType<beans::XMultiPropertyStates>::get(),
+ cppu::UnoType<drawing::XGluePointsSupplier>::get(),
+ cppu::UnoType<container::XChild>::get(),
+ cppu::UnoType<lang::XServiceInfo>::get(),
+ cppu::UnoType<lang::XTypeProvider>::get(),
+ cppu::UnoType<lang::XUnoTunnel>::get(),
+ cppu::UnoType<container::XNamed>::get(),
+ cppu::UnoType<drawing::XControlShape>::get(),
+ };
+
+ return aTypeSequence;
+ }
+ // 3d scene shape
+ case SdrObjKind::E3D_Scene:
+ {
+ static uno::Sequence<uno::Type> aTypeSequence{
+ cppu::UnoType<drawing::XShape>::get(),
+ cppu::UnoType<lang::XComponent>::get(),
+ cppu::UnoType<beans::XPropertySet>::get(),
+ cppu::UnoType<beans::XMultiPropertySet>::get(),
+ cppu::UnoType<beans::XPropertyState>::get(),
+ cppu::UnoType<beans::XMultiPropertyStates>::get(),
+ cppu::UnoType<drawing::XGluePointsSupplier>::get(),
+ cppu::UnoType<container::XChild>::get(),
+ cppu::UnoType<lang::XServiceInfo>::get(),
+ cppu::UnoType<lang::XTypeProvider>::get(),
+ cppu::UnoType<lang::XUnoTunnel>::get(),
+ cppu::UnoType<container::XNamed>::get(),
+ cppu::UnoType<drawing::XShapes>::get(),
+ };
+
+ return aTypeSequence;
+ }
+ case SdrObjKind::CustomShape:
+ {
+ static uno::Sequence<uno::Type> aTypeSequence{
+ cppu::UnoType<drawing::XShape>::get(),
+ cppu::UnoType<lang::XComponent>::get(),
+ cppu::UnoType<beans::XPropertySet>::get(),
+ cppu::UnoType<beans::XMultiPropertySet>::get(),
+ cppu::UnoType<beans::XPropertyState>::get(),
+ cppu::UnoType<beans::XMultiPropertyStates>::get(),
+ cppu::UnoType<drawing::XGluePointsSupplier>::get(),
+ cppu::UnoType<container::XChild>::get(),
+ cppu::UnoType<lang::XServiceInfo>::get(),
+ cppu::UnoType<lang::XTypeProvider>::get(),
+ cppu::UnoType<lang::XUnoTunnel>::get(),
+ cppu::UnoType<container::XNamed>::get(),
+ // from SvxUnoTextBase::getTypes()
+ cppu::UnoType<text::XText>::get(),
+ cppu::UnoType<container::XEnumerationAccess>::get(),
+ cppu::UnoType<text::XTextRangeMover>::get(),
+ cppu::UnoType<drawing::XEnhancedCustomShapeDefaulter>::get(),
+ };
+
+ return aTypeSequence;
+ }
+ // shapes with text
+ case SdrObjKind::Rectangle:
+ case SdrObjKind::CircleOrEllipse:
+ case SdrObjKind::Measure:
+ case SdrObjKind::Line:
+ case SdrObjKind::Polygon:
+ case SdrObjKind::PolyLine:
+ case SdrObjKind::PathLine:
+ case SdrObjKind::PathFill:
+ case SdrObjKind::FreehandLine:
+ case SdrObjKind::FreehandFill:
+ case SdrObjKind::PathPoly:
+ case SdrObjKind::PathPolyLine:
+ case SdrObjKind::Graphic:
+ case SdrObjKind::Text:
+ case SdrObjKind::Caption:
+ case SdrObjKind::OLE2: // #i118485# Moved to shapes with text
+ default:
+ {
+ static uno::Sequence<uno::Type> aTypeSequence{
+ cppu::UnoType<drawing::XShape>::get(),
+ cppu::UnoType<lang::XComponent>::get(),
+ cppu::UnoType<beans::XPropertySet>::get(),
+ cppu::UnoType<beans::XMultiPropertySet>::get(),
+ cppu::UnoType<beans::XPropertyState>::get(),
+ cppu::UnoType<beans::XMultiPropertyStates>::get(),
+ cppu::UnoType<drawing::XGluePointsSupplier>::get(),
+ cppu::UnoType<container::XChild>::get(),
+ cppu::UnoType<lang::XServiceInfo>::get(),
+ cppu::UnoType<lang::XTypeProvider>::get(),
+ cppu::UnoType<lang::XUnoTunnel>::get(),
+ cppu::UnoType<container::XNamed>::get(),
+ // from SvxUnoTextBase::getTypes()
+ cppu::UnoType<text::XTextAppend>::get(),
+ cppu::UnoType<text::XTextCopy>::get(),
+ cppu::UnoType<container::XEnumerationAccess>::get(),
+ cppu::UnoType<text::XTextRangeMover>::get(),
+ };
+
+ return aTypeSequence;
+ }
+ }
+}
+
+
+uno::Sequence< sal_Int8 > SAL_CALL SvxShape::getImplementationId()
+{
+ return css::uno::Sequence<sal_Int8>();
+}
+
+void SvxShape::Notify( SfxBroadcaster&, const SfxHint& rHint ) noexcept
+{
+ DBG_TESTSOLARMUTEX();
+
+ // do cheap checks first, this method is hot
+ if (rHint.GetId() != SfxHintId::ThisIsAnSdrHint)
+ return;
+ SdrObject* pSdrObject(mpSdrObjectWeakReference.get());
+ if( !pSdrObject )
+ return;
+ const SdrHint* pSdrHint = static_cast<const SdrHint*>(&rHint);
+ // #i55919# SdrHintKind::ObjectChange is only interesting if it's for this object
+ if ((pSdrHint->GetKind() != SdrHintKind::ModelCleared) &&
+ (pSdrHint->GetKind() != SdrHintKind::ObjectChange || pSdrHint->GetObject() != pSdrObject ))
+ return;
+
+ uno::Reference< uno::XInterface > xSelf( pSdrObject->getWeakUnoShape() );
+ if( !xSelf.is() )
+ {
+ EndListening(pSdrObject->getSdrModelFromSdrObject());
+ mpSdrObjectWeakReference.reset(nullptr);
+ return;
+ }
+
+ bool bClearMe = false;
+
+ switch( pSdrHint->GetKind() )
+ {
+ case SdrHintKind::ObjectChange:
+ {
+ updateShapeKind();
+ break;
+ }
+ case SdrHintKind::ModelCleared:
+ {
+ bClearMe = true;
+ break;
+ }
+ default:
+ break;
+ };
+
+ if( !bClearMe )
+ return;
+
+ if(!HasSdrObjectOwnership())
+ {
+ if(nullptr != pSdrObject)
+ {
+ EndListening(pSdrObject->getSdrModelFromSdrObject());
+ pSdrObject->setUnoShape(nullptr);
+ }
+
+ mpSdrObjectWeakReference.reset(nullptr);
+
+ // SdrModel *is* going down, try to Free SdrObject even
+ // when !HasSdrObjectOwnership
+ if(nullptr != pSdrObject && !pSdrObject->IsInserted())
+ {
+ SdrObject::Free(pSdrObject);
+ }
+ }
+
+ if(!mpImpl->mbDisposing)
+ {
+ dispose();
+ }
+}
+
+// XShape
+
+
+// The "*LogicRectHack" functions also existed in sch, and those
+// duplicate symbols cause Bad Things To Happen (TM) #i9462#.
+// Prefixing with 'svx' and marking static to make sure name collisions
+// do not occur.
+
+static bool svx_needLogicRectHack( SdrObject const * pObj )
+{
+ if( pObj->GetObjInventor() == SdrInventor::Default)
+ {
+ switch(pObj->GetObjIdentifier())
+ {
+ case SdrObjKind::Group:
+ case SdrObjKind::Line:
+ case SdrObjKind::Polygon:
+ case SdrObjKind::PolyLine:
+ case SdrObjKind::PathLine:
+ case SdrObjKind::PathFill:
+ case SdrObjKind::FreehandLine:
+ case SdrObjKind::FreehandFill:
+ case SdrObjKind::SplineLine:
+ case SdrObjKind::SplineFill:
+ case SdrObjKind::Edge:
+ case SdrObjKind::PathPoly:
+ case SdrObjKind::PathPolyLine:
+ case SdrObjKind::Measure:
+ return true;
+ default:
+ break;
+ }
+ }
+ return false;
+}
+
+
+static tools::Rectangle svx_getLogicRectHack( SdrObject const * pObj )
+{
+ if(svx_needLogicRectHack(pObj))
+ {
+ return pObj->GetSnapRect();
+ }
+ else
+ {
+ return pObj->GetLogicRect();
+ }
+}
+
+
+static void svx_setLogicRectHack( SdrObject* pObj, const tools::Rectangle& rRect )
+{
+ if(svx_needLogicRectHack(pObj))
+ {
+ pObj->SetSnapRect( rRect );
+ }
+ else
+ {
+ pObj->SetLogicRect( rRect );
+ }
+}
+
+
+awt::Point SAL_CALL SvxShape::getPosition()
+{
+ ::SolarMutexGuard aGuard;
+
+ if(HasSdrObject())
+ {
+ tools::Rectangle aRect( svx_getLogicRectHack(GetSdrObject()) );
+ Point aPt( aRect.Left(), aRect.Top() );
+
+ // Position is relative to anchor, so recalc to absolute position
+ if( GetSdrObject()->getSdrModelFromSdrObject().IsWriter() )
+ aPt -= GetSdrObject()->GetAnchorPos();
+
+ ForceMetricTo100th_mm(aPt);
+ return css::awt::Point( aPt.X(), aPt.Y() );
+ }
+ else
+ {
+ return maPosition;
+ }
+}
+
+
+void SAL_CALL SvxShape::setPosition( const awt::Point& Position )
+{
+ ::SolarMutexGuard aGuard;
+
+ if(HasSdrObject())
+ {
+ // do NOT move 3D objects, this would change the homogen
+ // transformation matrix
+ if(dynamic_cast<const E3dCompoundObject* >(GetSdrObject()) == nullptr)
+ {
+ tools::Rectangle aRect( svx_getLogicRectHack(GetSdrObject()) );
+ Point aLocalPos( Position.X, Position.Y );
+ ForceMetricToItemPoolMetric(aLocalPos);
+
+ // Position is absolute, so recalc to position relative to anchor
+ if( GetSdrObject()->getSdrModelFromSdrObject().IsWriter() )
+ aLocalPos += GetSdrObject()->GetAnchorPos();
+
+ tools::Long nDX = aLocalPos.X() - aRect.Left();
+ tools::Long nDY = aLocalPos.Y() - aRect.Top();
+
+ GetSdrObject()->Move( Size( nDX, nDY ) );
+ GetSdrObject()->getSdrModelFromSdrObject().SetChanged();
+ }
+ }
+
+ maPosition = Position;
+}
+
+
+awt::Size SAL_CALL SvxShape::getSize()
+{
+ ::SolarMutexGuard aGuard;
+
+ if(HasSdrObject())
+ {
+ tools::Rectangle aRect( svx_getLogicRectHack(GetSdrObject()) );
+ Size aObjSize( aRect.getWidth(), aRect.getHeight() );
+ ForceMetricTo100th_mm(aObjSize);
+ return css::awt::Size( aObjSize.getWidth(), aObjSize.getHeight() );
+ }
+ else
+ return maSize;
+}
+
+
+void SAL_CALL SvxShape::setSize( const awt::Size& rSize )
+{
+ ::SolarMutexGuard aGuard;
+
+ if(HasSdrObject())
+ {
+ tools::Rectangle aRect( svx_getLogicRectHack(GetSdrObject()) );
+ Size aLocalSize( rSize.Width, rSize.Height );
+ ForceMetricToItemPoolMetric(aLocalSize);
+
+ if(GetSdrObject()->GetObjInventor() == SdrInventor::Default && GetSdrObject()->GetObjIdentifier() == SdrObjKind::Measure )
+ {
+ Fraction aWdt(aLocalSize.Width(),aRect.Right()-aRect.Left());
+ Fraction aHgt(aLocalSize.Height(),aRect.Bottom()-aRect.Top());
+ Point aPt = GetSdrObject()->GetSnapRect().TopLeft();
+ GetSdrObject()->Resize(aPt,aWdt,aHgt);
+ }
+ else
+ {
+ //aRect.SetSize(aLocalSize); // this call subtract 1 // https://bz.apache.org/ooo/show_bug.cgi?id=83193
+ if ( !aLocalSize.Width() )
+ {
+ aRect.SetWidthEmpty();
+ }
+ else
+ aRect.setWidth(aLocalSize.Width());
+ if ( !aLocalSize.Height() )
+ {
+ aRect.SetHeightEmpty();
+ }
+ else
+ aRect.setHeight(aLocalSize.Height());
+
+ svx_setLogicRectHack( GetSdrObject(), aRect );
+ }
+
+ GetSdrObject()->getSdrModelFromSdrObject().SetChanged();
+ }
+ maSize = rSize;
+}
+
+
+// XNamed
+OUString SAL_CALL SvxShape::getName( )
+{
+ ::SolarMutexGuard aGuard;
+ if( HasSdrObject() )
+ {
+ return GetSdrObject()->GetName();
+ }
+ else
+ {
+ return maShapeName;
+ }
+}
+
+
+void SAL_CALL SvxShape::setName( const OUString& aName )
+{
+ ::SolarMutexGuard aGuard;
+ if( HasSdrObject() )
+ {
+ GetSdrObject()->SetName( aName );
+ }
+ else
+ {
+ maShapeName = aName;
+ }
+}
+
+// XShapeDescriptor
+
+
+OUString SAL_CALL SvxShape::getShapeType()
+{
+ if( !maShapeType.getLength() )
+ return UHashMap::getNameFromId( mpImpl->mnObjId );
+ else
+ return maShapeType;
+}
+
+// XComponent
+
+
+void SAL_CALL SvxShape::dispose()
+{
+ ::SolarMutexGuard aGuard;
+
+ if( mpImpl->mbDisposing )
+ return; // caught a recursion
+
+ mpImpl->mbDisposing = true;
+
+ lang::EventObject aEvt;
+ aEvt.Source = *static_cast<OWeakAggObject*>(this);
+ mpImpl->maDisposeListeners.disposeAndClear(aEvt);
+ mpImpl->maPropertyNotifier.disposing();
+
+ if ( !HasSdrObject() )
+ return;
+
+ SdrObject* pObject = GetSdrObject();
+
+ EndListening( pObject->getSdrModelFromSdrObject() );
+ bool bFreeSdrObject = false;
+
+ if ( pObject->IsInserted() && pObject->getSdrPageFromSdrObject() )
+ {
+ SdrPage* pPage = pObject->getSdrPageFromSdrObject();
+ // delete the SdrObject from the page
+ const size_t nCount = pPage->GetObjCount();
+ for ( size_t nNum = 0; nNum < nCount; ++nNum )
+ {
+ if ( pPage->GetObj( nNum ) == pObject )
+ {
+ OSL_VERIFY( pPage->RemoveObject( nNum ) == pObject );
+ if (HasSdrObjectOwnership())
+ {
+ bFreeSdrObject = true;
+ }
+ break;
+ }
+ }
+ }
+
+ pObject->setUnoShape(nullptr);
+
+ if ( bFreeSdrObject )
+ {
+ // in case we have the ownership of the SdrObject, a Free
+ // would do nothing. So ensure the ownership is reset.
+ mpImpl->mbHasSdrObjectOwnership = false;
+ SdrObject::Free( pObject );
+ }
+}
+
+
+void SAL_CALL SvxShape::addEventListener( const Reference< lang::XEventListener >& xListener )
+{
+ mpImpl->maDisposeListeners.addInterface(xListener);
+}
+
+
+void SAL_CALL SvxShape::removeEventListener( const Reference< lang::XEventListener >& aListener )
+{
+ mpImpl->maDisposeListeners.removeInterface(aListener);
+}
+
+// XPropertySet
+
+
+Reference< beans::XPropertySetInfo > SAL_CALL
+ SvxShape::getPropertySetInfo()
+{
+ if( mpImpl->mpMaster )
+ {
+ return mpImpl->mpMaster->getPropertySetInfo();
+ }
+ else
+ {
+ return _getPropertySetInfo();
+ }
+}
+
+Reference< beans::XPropertySetInfo > const &
+ SvxShape::_getPropertySetInfo()
+{
+ return mpPropSet->getPropertySetInfo();
+}
+
+
+void SAL_CALL SvxShape::addPropertyChangeListener( const OUString& _propertyName, const Reference< beans::XPropertyChangeListener >& _listener )
+{
+ ::osl::MutexGuard aGuard( m_aMutex );
+ mpImpl->maPropertyNotifier.addPropertyChangeListener( _propertyName, _listener );
+}
+
+
+void SAL_CALL SvxShape::removePropertyChangeListener( const OUString& _propertyName, const Reference< beans::XPropertyChangeListener >& _listener )
+{
+ ::osl::MutexGuard aGuard( m_aMutex );
+ mpImpl->maPropertyNotifier.removePropertyChangeListener( _propertyName, _listener );
+}
+
+
+void SAL_CALL SvxShape::addVetoableChangeListener( const OUString& , const Reference< beans::XVetoableChangeListener >& )
+{
+ OSL_FAIL( "SvxShape::addVetoableChangeListener: don't have any vetoable properties, so why ...?" );
+}
+
+
+void SAL_CALL SvxShape::removeVetoableChangeListener( const OUString& , const Reference< beans::XVetoableChangeListener >& )
+{
+ OSL_FAIL( "SvxShape::removeVetoableChangeListener: don't have any vetoable properties, so why ...?" );
+}
+
+
+bool SvxShape::SetFillAttribute( sal_uInt16 nWID, const OUString& rName )
+{
+ if(HasSdrObject())
+ {
+ SfxItemSet aSet( GetSdrObject()->getSdrModelFromSdrObject().GetItemPool(), nWID, nWID );
+
+ if( SetFillAttribute( nWID, rName, aSet, &GetSdrObject()->getSdrModelFromSdrObject() ) )
+ {
+ //GetSdrObject()->SetItemSetAndBroadcast(aSet);
+ GetSdrObject()->SetMergedItemSetAndBroadcast(aSet);
+
+ return true;
+ }
+ }
+
+ return false;
+}
+
+
+bool SvxShape::SetFillAttribute( sal_uInt16 nWID, const OUString& rName, SfxItemSet& rSet, SdrModel const * pModel )
+{
+ // check if an item with the given name and which id is inside the models
+ // pool or the stylesheet pool, if found it's put in the itemset
+ if( !SetFillAttribute( nWID, rName, rSet ) )
+ {
+ // we did not find such item in one of the pools, so we check
+ // the property lists that are loaded for the model for items
+ // that support such.
+ OUString aStrName = SvxUnogetInternalNameForItem(nWID, rName);
+
+ switch( nWID )
+ {
+ case XATTR_FILLBITMAP:
+ {
+ XBitmapListRef pBitmapList = pModel->GetBitmapList();
+
+ if( !pBitmapList.is() )
+ return false;
+
+ tools::Long nPos = pBitmapList->GetIndex(aStrName);
+ if( nPos == -1 )
+ return false;
+
+ const XBitmapEntry* pEntry = pBitmapList->GetBitmap(nPos);
+ XFillBitmapItem aBmpItem(rName, pEntry->GetGraphicObject());
+ rSet.Put(aBmpItem);
+ break;
+ }
+ case XATTR_FILLGRADIENT:
+ {
+ XGradientListRef pGradientList = pModel->GetGradientList();
+
+ if( !pGradientList.is() )
+ return false;
+
+ tools::Long nPos = pGradientList->GetIndex(aStrName);
+ if( nPos == -1 )
+ return false;
+
+ const XGradientEntry* pEntry = pGradientList->GetGradient(nPos);
+ XFillGradientItem aGrdItem(rName, pEntry->GetGradient());
+ rSet.Put( aGrdItem );
+ break;
+ }
+ case XATTR_FILLHATCH:
+ {
+ XHatchListRef pHatchList = pModel->GetHatchList();
+
+ if( !pHatchList.is() )
+ return false;
+
+ tools::Long nPos = pHatchList->GetIndex(aStrName);
+ if( nPos == -1 )
+ return false;
+
+ const XHatchEntry* pEntry = pHatchList->GetHatch( nPos );
+ XFillHatchItem aHatchItem(rName, pEntry->GetHatch());
+ rSet.Put( aHatchItem );
+ break;
+ }
+ case XATTR_LINEEND:
+ case XATTR_LINESTART:
+ {
+ XLineEndListRef pLineEndList = pModel->GetLineEndList();
+
+ if( !pLineEndList.is() )
+ return false;
+
+ tools::Long nPos = pLineEndList->GetIndex(aStrName);
+ if( nPos == -1 )
+ return false;
+
+ const XLineEndEntry* pEntry = pLineEndList->GetLineEnd(nPos);
+ if( sal_uInt16(XATTR_LINEEND) == nWID )
+ {
+ XLineEndItem aLEItem(rName, pEntry->GetLineEnd());
+ rSet.Put( aLEItem );
+ }
+ else
+ {
+ XLineStartItem aLSItem(rName, pEntry->GetLineEnd());
+ rSet.Put( aLSItem );
+ }
+
+ break;
+ }
+ case XATTR_LINEDASH:
+ {
+ XDashListRef pDashList = pModel->GetDashList();
+
+ if( !pDashList.is() )
+ return false;
+
+ tools::Long nPos = pDashList->GetIndex(aStrName);
+ if( nPos == -1 )
+ return false;
+
+ const XDashEntry* pEntry = pDashList->GetDash(nPos);
+ XLineDashItem aDashItem(rName, pEntry->GetDash());
+ rSet.Put( aDashItem );
+ break;
+ }
+ default:
+ return false;
+ }
+ }
+
+ return true;
+}
+
+
+bool SvxShape::SetFillAttribute( sal_uInt16 nWID, const OUString& rName, SfxItemSet& rSet )
+{
+ OUString aName = SvxUnogetInternalNameForItem(nWID, rName);
+
+ if (aName.isEmpty())
+ {
+ switch( nWID )
+ {
+ case XATTR_LINEEND:
+ case XATTR_LINESTART:
+ {
+ const basegfx::B2DPolyPolygon aEmptyPoly;
+ if( nWID == sal_uInt16(XATTR_LINEEND) )
+ rSet.Put( XLineEndItem( "", aEmptyPoly ) );
+ else
+ rSet.Put( XLineStartItem( "", aEmptyPoly ) );
+
+ return true;
+ }
+ case XATTR_FILLFLOATTRANSPARENCE:
+ {
+ // #85953# Set a disabled XFillFloatTransparenceItem
+ rSet.Put(XFillFloatTransparenceItem());
+
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ for (const SfxPoolItem* p : rSet.GetPool()->GetItemSurrogates(nWID))
+ {
+ const NameOrIndex* pItem = static_cast<const NameOrIndex*>(p);
+ if( pItem->GetName() == aName )
+ {
+ rSet.Put( *pItem );
+ return true;
+ }
+ }
+
+ return false;
+}
+
+
+void SAL_CALL SvxShape::setPropertyValue( const OUString& rPropertyName, const uno::Any& rVal )
+{
+ if( mpImpl->mpMaster )
+ {
+ mpImpl->mpMaster->setPropertyValue( rPropertyName, rVal );
+ }
+ else
+ {
+ _setPropertyValue( rPropertyName, rVal );
+ }
+}
+
+void SvxShape::_setPropertyValue( const OUString& rPropertyName, const uno::Any& rVal )
+{
+ ::SolarMutexGuard aGuard;
+
+ const SfxItemPropertyMapEntry* pMap = mpPropSet->getPropertyMapEntry(rPropertyName);
+
+ if (!HasSdrObject())
+ {
+ // Since we have no actual sdr object right now, remember all
+ // properties in a list. These properties will be set when the sdr
+ // object is created.
+
+ if (pMap && pMap->nWID)
+ {
+ // FIXME: We should throw a UnknownPropertyException here.
+ // But since this class is aggregated from classes that
+ // support additional properties that we don't know here we
+ // silently store *all* properties, even if they may be not
+ // supported after creation.
+ SvxItemPropertySet::setPropertyValue( pMap, rVal, maUrsAnys );
+ }
+
+ return;
+ }
+
+ if (rPropertyName == "HandlePathObjScale")
+ {
+ auto pPathObj = dynamic_cast<SdrPathObj*>(GetSdrObject());
+ if (pPathObj)
+ {
+ bool bHandleScale{};
+ if (rVal >>= bHandleScale)
+ {
+ pPathObj->SetHandleScale(bHandleScale);
+ }
+ }
+ return;
+ }
+
+ if (!pMap)
+ {
+ // reduce log noise by ignoring two properties that higher level code queries for on all objects
+ SAL_WARN_IF(rPropertyName != "FromWordArt" && rPropertyName != "GraphicColorMode",
+ "svx.uno", "Unknown Property: " << rPropertyName);
+ throw beans::UnknownPropertyException( rPropertyName, static_cast<cppu::OWeakObject*>(this));
+ }
+
+ if ((pMap->nFlags & beans::PropertyAttribute::READONLY) != 0)
+ throw beans::PropertyVetoException(
+ "Readonly property can't be set: " + rPropertyName,
+ uno::Reference<drawing::XShape>(this));
+
+ GetSdrObject()->getSdrModelFromSdrObject().SetChanged();
+
+ if (setPropertyValueImpl(rPropertyName, pMap, rVal))
+ return;
+
+ DBG_ASSERT( pMap->nWID == SDRATTR_TEXTDIRECTION || pMap->nWID < SDRATTR_NOTPERSIST_FIRST || pMap->nWID > SDRATTR_NOTPERSIST_LAST, "Not persist item not handled!" );
+ DBG_ASSERT( pMap->nWID < OWN_ATTR_VALUE_START || pMap->nWID > OWN_ATTR_VALUE_END, "Not item property not handled!" );
+
+ bool bIsNotPersist = pMap->nWID >= SDRATTR_NOTPERSIST_FIRST && pMap->nWID <= SDRATTR_NOTPERSIST_LAST && pMap->nWID != SDRATTR_TEXTDIRECTION;
+
+ if( pMap->nWID == SDRATTR_CORNER_RADIUS )
+ {
+ sal_Int32 nCornerRadius = 0;
+ if( !(rVal >>= nCornerRadius) || (nCornerRadius < 0) || (nCornerRadius > 5000000))
+ throw IllegalArgumentException();
+ }
+
+ std::optional<SfxItemSet> xLocalSet;
+ SfxItemSet* pSet;
+ if( mbIsMultiPropertyCall && !bIsNotPersist )
+ {
+ if( !mpImpl->mxItemSet )
+ {
+ mpImpl->mxItemSet.emplace( GetSdrObject()->GetProperties().CreateObjectSpecificItemSet( GetSdrObject()->getSdrModelFromSdrObject().GetItemPool() ) );
+ }
+ pSet = &*mpImpl->mxItemSet;
+ }
+ else
+ {
+ xLocalSet.emplace( GetSdrObject()->getSdrModelFromSdrObject().GetItemPool(), pMap->nWID, pMap->nWID);
+ pSet = &*xLocalSet;
+ }
+
+ if( pSet->GetItemState( pMap->nWID ) != SfxItemState::SET )
+ pSet->Put(GetSdrObject()->GetMergedItem(pMap->nWID));
+
+ if( !SvxUnoTextRangeBase::SetPropertyValueHelper( pMap, rVal, *pSet ))
+ {
+ if( pSet->GetItemState( pMap->nWID ) != SfxItemState::SET )
+ {
+ if(bIsNotPersist)
+ {
+ // not-persistent attribute, get those extra
+ GetSdrObject()->TakeNotPersistAttr(*pSet);
+ }
+ }
+
+ if( pSet->GetItemState( pMap->nWID ) != SfxItemState::SET )
+ {
+ // get default from ItemPool
+ if(SfxItemPool::IsWhich(pMap->nWID))
+ pSet->Put(GetSdrObject()->getSdrModelFromSdrObject().GetItemPool().GetDefaultItem(pMap->nWID));
+ }
+
+ if( pSet->GetItemState( pMap->nWID ) == SfxItemState::SET )
+ {
+ SvxItemPropertySet_setPropertyValue( pMap, rVal, *pSet );
+ }
+ }
+
+ if(bIsNotPersist)
+ {
+ // set not-persistent attribute extra
+ GetSdrObject()->ApplyNotPersistAttr( *pSet );
+ }
+ else
+ {
+ // if we have a XMultiProperty call then the item set
+ // will be set in setPropertyValues later
+ if( !mbIsMultiPropertyCall )
+ GetSdrObject()->SetMergedItemSetAndBroadcast( *pSet );
+ }
+}
+
+
+uno::Any SAL_CALL SvxShape::getPropertyValue( const OUString& PropertyName )
+{
+ if ( mpImpl->mpMaster )
+ return mpImpl->mpMaster->getPropertyValue( PropertyName );
+ else
+ return _getPropertyValue( PropertyName );
+}
+
+
+uno::Any SvxShape::_getPropertyValue( const OUString& PropertyName )
+{
+ ::SolarMutexGuard aGuard;
+
+ const SfxItemPropertyMapEntry* pMap = mpPropSet->getPropertyMapEntry(PropertyName);
+
+ uno::Any aAny;
+ if(HasSdrObject())
+ {
+ if(pMap == nullptr )
+ throw beans::UnknownPropertyException( PropertyName, static_cast<cppu::OWeakObject*>(this));
+
+ if( !getPropertyValueImpl( PropertyName, pMap, aAny ) )
+ {
+ DBG_ASSERT( pMap->nWID == SDRATTR_TEXTDIRECTION || (pMap->nWID < SDRATTR_NOTPERSIST_FIRST || pMap->nWID > SDRATTR_NOTPERSIST_LAST), "Not persist item not handled!" );
+ DBG_ASSERT( pMap->nWID < OWN_ATTR_VALUE_START || pMap->nWID > OWN_ATTR_VALUE_END, "Not item property not handled!" );
+
+ SfxItemSet aSet( GetSdrObject()->getSdrModelFromSdrObject().GetItemPool(), pMap->nWID, pMap->nWID );
+ aSet.Put(GetSdrObject()->GetMergedItem(pMap->nWID));
+
+ if(SvxUnoTextRangeBase::GetPropertyValueHelper( aSet, pMap, aAny ))
+ return aAny;
+
+ if(!aSet.Count())
+ {
+ if(pMap->nWID >= SDRATTR_NOTPERSIST_FIRST && pMap->nWID <= SDRATTR_NOTPERSIST_LAST)
+ {
+ // not-persistent attribute, get those extra
+ GetSdrObject()->TakeNotPersistAttr(aSet);
+ }
+ }
+
+ if(!aSet.Count())
+ {
+ // get default from ItemPool
+ if(SfxItemPool::IsWhich(pMap->nWID))
+ aSet.Put(GetSdrObject()->getSdrModelFromSdrObject().GetItemPool().GetDefaultItem(pMap->nWID));
+ }
+
+ if(aSet.Count())
+ aAny = GetAnyForItem( aSet, pMap );
+ }
+ }
+ else
+ {
+
+// Fixme: we should return default values for OWN_ATTR !
+
+ if(pMap && pMap->nWID)
+// FixMe: see setPropertyValue
+ aAny = mpPropSet->getPropertyValue( pMap, maUrsAnys );
+
+ }
+ return aAny;
+}
+
+
+// XMultiPropertySet
+void SAL_CALL SvxShape::setPropertyValues( const css::uno::Sequence< OUString >& aPropertyNames, const css::uno::Sequence< css::uno::Any >& aValues )
+{
+ ::SolarMutexGuard aSolarGuard;
+
+ const sal_Int32 nCount = aPropertyNames.getLength();
+ const OUString* pNames = aPropertyNames.getConstArray();
+
+ const uno::Any* pValues = aValues.getConstArray();
+
+ // make sure mbIsMultiPropertyCall and mpImpl->mpItemSet are
+ // reset even when an exception is thrown
+ const ::comphelper::ScopeGuard aGuard( [this] () { return this->endSetPropertyValues(); } );
+
+ mbIsMultiPropertyCall = true;
+
+ if( mpImpl->mpMaster )
+ {
+ for( sal_Int32 nIdx = 0; nIdx < nCount; nIdx++, pNames++, pValues++ )
+ {
+ try
+ {
+ setPropertyValue( *pNames, *pValues );
+ }
+ catch (beans::UnknownPropertyException&)
+ {
+ DBG_UNHANDLED_EXCEPTION("svx");
+ }
+ catch (uno::Exception&)
+ {
+ DBG_UNHANDLED_EXCEPTION("svx");
+ }
+ }
+ }
+ else
+ {
+ uno::Reference< beans::XPropertySet > xSet;
+ queryInterface( cppu::UnoType<beans::XPropertySet>::get()) >>= xSet;
+
+ for( sal_Int32 nIdx = 0; nIdx < nCount; nIdx++, pNames++, pValues++ )
+ {
+ try
+ {
+ xSet->setPropertyValue( *pNames, *pValues );
+ }
+ catch (beans::UnknownPropertyException&)
+ {
+ DBG_UNHANDLED_EXCEPTION("svx");
+ }
+ catch (uno::Exception&)
+ {
+ DBG_UNHANDLED_EXCEPTION("svx");
+ }
+ }
+ }
+
+ if( mpImpl->mxItemSet && HasSdrObject() )
+ GetSdrObject()->SetMergedItemSetAndBroadcast( *mpImpl->mxItemSet );
+}
+
+
+void SvxShape::endSetPropertyValues()
+{
+ mbIsMultiPropertyCall = false;
+ mpImpl->mxItemSet.reset();
+}
+
+
+css::uno::Sequence< css::uno::Any > SAL_CALL SvxShape::getPropertyValues( const css::uno::Sequence< OUString >& aPropertyNames )
+{
+ const sal_Int32 nCount = aPropertyNames.getLength();
+ const OUString* pNames = aPropertyNames.getConstArray();
+
+ uno::Sequence< uno::Any > aRet( nCount );
+ uno::Any* pValue = aRet.getArray();
+
+ if( mpImpl->mpMaster )
+ {
+ for( sal_Int32 nIdx = 0; nIdx < nCount; nIdx++, pValue++, pNames++ )
+ {
+ try
+ {
+ *pValue = getPropertyValue( *pNames );
+ }
+ catch( uno::Exception& )
+ {
+ OSL_FAIL( "SvxShape::getPropertyValues, unknown property asked" );
+ }
+ }
+ }
+ else
+ {
+ uno::Reference< beans::XPropertySet > xSet;
+ queryInterface( cppu::UnoType<beans::XPropertySet>::get()) >>= xSet;
+
+ for( sal_Int32 nIdx = 0; nIdx < nCount; nIdx++, pValue++, pNames++ )
+ {
+ try
+ {
+ *pValue = xSet->getPropertyValue( *pNames );
+ }
+ catch( uno::Exception& )
+ {
+ OSL_FAIL( "SvxShape::getPropertyValues, unknown property asked" );
+ }
+ }
+ }
+
+ return aRet;
+}
+
+void SAL_CALL SvxShape::addPropertiesChangeListener( const css::uno::Sequence< OUString >& , const css::uno::Reference< css::beans::XPropertiesChangeListener >& )
+{
+}
+
+void SAL_CALL SvxShape::removePropertiesChangeListener( const css::uno::Reference< css::beans::XPropertiesChangeListener >& )
+{
+}
+
+void SAL_CALL SvxShape::firePropertiesChangeEvent( const css::uno::Sequence< OUString >& , const css::uno::Reference< css::beans::XPropertiesChangeListener >& )
+{
+}
+
+
+uno::Any SvxShape::GetAnyForItem( SfxItemSet const & aSet, const SfxItemPropertyMapEntry* pMap ) const
+{
+ DBG_TESTSOLARMUTEX();
+ uno::Any aAny;
+
+ switch(pMap->nWID)
+ {
+ case SDRATTR_CIRCSTARTANGLE:
+ {
+ if(const SdrAngleItem* pPoolItem = aSet.GetItemIfSet(SDRATTR_CIRCSTARTANGLE,false))
+ {
+ Degree100 nAngle = pPoolItem->GetValue();
+ aAny <<= nAngle.get();
+ }
+ break;
+ }
+
+ case SDRATTR_CIRCENDANGLE:
+ {
+ if (const SdrAngleItem* pPoolItem = aSet.GetItemIfSet(SDRATTR_CIRCENDANGLE,false))
+ {
+ Degree100 nAngle = pPoolItem->GetValue();
+ aAny <<= nAngle.get();
+ }
+ break;
+ }
+
+ case SDRATTR_CIRCKIND:
+ {
+ if( GetSdrObject()->GetObjInventor() == SdrInventor::Default)
+ {
+ drawing::CircleKind eKind;
+ switch(GetSdrObject()->GetObjIdentifier())
+ {
+ case SdrObjKind::CircleOrEllipse: // circle, ellipse
+ eKind = drawing::CircleKind_FULL;
+ break;
+ case SdrObjKind::CircleCut: // segment of circle
+ eKind = drawing::CircleKind_CUT;
+ break;
+ case SdrObjKind::CircleArc: // arc of circle
+ eKind = drawing::CircleKind_ARC;
+ break;
+ case SdrObjKind::CircleSection: // sector
+ eKind = drawing::CircleKind_SECTION;
+ break;
+ default:
+ break;
+ }
+ aAny <<= eKind;
+ }
+ break;
+ }
+ default:
+ {
+ // get value from ItemSet
+ aAny = SvxItemPropertySet_getPropertyValue( pMap, aSet );
+
+ if( pMap->aType != aAny.getValueType() )
+ {
+ // since the sfx uint16 item now exports a sal_Int32, we may have to fix this here
+ if( ( pMap->aType == ::cppu::UnoType<sal_Int16>::get()) && aAny.getValueType() == ::cppu::UnoType<sal_Int32>::get() )
+ {
+ sal_Int32 nValue = 0;
+ aAny >>= nValue;
+ aAny <<= static_cast<sal_Int16>(nValue);
+ }
+ else
+ {
+ OSL_FAIL("SvxShape::GetAnyForItem() Returnvalue has wrong Type!" );
+ }
+ }
+
+ }
+ }
+
+ return aAny;
+}
+
+
+// XPropertyState
+beans::PropertyState SAL_CALL SvxShape::getPropertyState( const OUString& PropertyName )
+{
+ if( mpImpl->mpMaster )
+ {
+ return mpImpl->mpMaster->getPropertyState( PropertyName );
+ }
+ else
+ {
+ return _getPropertyState( PropertyName );
+ }
+}
+
+beans::PropertyState SvxShape::_getPropertyState( const OUString& PropertyName )
+{
+ ::SolarMutexGuard aGuard;
+
+ const SfxItemPropertyMapEntry* pMap = mpPropSet->getPropertyMapEntry(PropertyName);
+
+ if( !HasSdrObject() || pMap == nullptr )
+ throw beans::UnknownPropertyException( PropertyName, static_cast<cppu::OWeakObject*>(this));
+
+ beans::PropertyState eState;
+ if( !getPropertyStateImpl( pMap, eState ) )
+ {
+ const SfxItemSet& rSet = GetSdrObject()->GetMergedItemSet();
+
+ switch( rSet.GetItemState( pMap->nWID, false ) )
+ {
+ case SfxItemState::SET:
+ eState = beans::PropertyState_DIRECT_VALUE;
+ break;
+ case SfxItemState::DEFAULT:
+ eState = beans::PropertyState_DEFAULT_VALUE;
+ break;
+ default:
+ eState = beans::PropertyState_AMBIGUOUS_VALUE;
+ break;
+ }
+
+ // if an item is set, this doesn't mean we want it :)
+ if( beans::PropertyState_DIRECT_VALUE == eState )
+ {
+ switch( pMap->nWID )
+ {
+ // the following items are disabled by changing the
+ // fill style or the line style. so there is no need
+ // to export items without names which should be empty
+ case XATTR_FILLBITMAP:
+ case XATTR_FILLGRADIENT:
+ case XATTR_FILLHATCH:
+ case XATTR_LINEDASH:
+ {
+ const NameOrIndex* pItem = rSet.GetItem<NameOrIndex>(pMap->nWID);
+ if( ( pItem == nullptr ) || pItem->GetName().isEmpty() )
+ eState = beans::PropertyState_DEFAULT_VALUE;
+ }
+ break;
+
+ // #i36115#
+ // If e.g. the LineStart is on NONE and thus the string has length 0, it still
+ // may be a hard attribute covering the set LineStart of the parent (Style).
+ // #i37644#
+ // same is for fill float transparency
+ case XATTR_LINEEND:
+ case XATTR_LINESTART:
+ case XATTR_FILLFLOATTRANSPARENCE:
+ {
+ const NameOrIndex* pItem = rSet.GetItem<NameOrIndex>(pMap->nWID);
+ if ( pItem == nullptr )
+ eState = beans::PropertyState_DEFAULT_VALUE;
+ }
+ break;
+ case XATTR_FILLCOLOR:
+ if (pMap->nMemberId == MID_COLOR_THEME_INDEX)
+ {
+ const XFillColorItem* pColor = rSet.GetItem<XFillColorItem>(pMap->nWID);
+ if (pColor->GetThemeColor().GetThemeIndex() == -1)
+ {
+ eState = beans::PropertyState_DEFAULT_VALUE;
+ }
+ }
+ else if (pMap->nMemberId == MID_COLOR_LUM_MOD)
+ {
+ const XFillColorItem* pColor = rSet.GetItem<XFillColorItem>(pMap->nWID);
+ if (pColor->GetThemeColor().GetLumMod() == 10000)
+ {
+ eState = beans::PropertyState_DEFAULT_VALUE;
+ }
+ }
+ else if (pMap->nMemberId == MID_COLOR_LUM_OFF)
+ {
+ const XFillColorItem* pColor = rSet.GetItem<XFillColorItem>(pMap->nWID);
+ if (pColor->GetThemeColor().GetLumOff() == 0)
+ {
+ eState = beans::PropertyState_DEFAULT_VALUE;
+ }
+ }
+ break;
+ }
+ }
+ }
+ return eState;
+}
+
+bool SvxShape::setPropertyValueImpl( const OUString&, const SfxItemPropertyMapEntry* pProperty, const css::uno::Any& rValue )
+{
+ switch( pProperty->nWID )
+ {
+ case OWN_ATTR_CAPTION_POINT:
+ {
+ awt::Point aPnt;
+ if( rValue >>= aPnt )
+ {
+ Point aVclPoint( aPnt.X, aPnt.Y );
+
+ // tdf#117145 metric of SdrModel is app-specific, metric of UNO API is 100thmm
+ // Need to adapt aVclPoint from 100thmm to app-specific
+ ForceMetricToItemPoolMetric(aVclPoint);
+
+ // #90763# position is relative to top left, make it absolute
+ basegfx::B2DPolyPolygon aNewPolyPolygon;
+ basegfx::B2DHomMatrix aNewHomogenMatrix;
+ GetSdrObject()->TRGetBaseGeometry(aNewHomogenMatrix, aNewPolyPolygon);
+
+ aVclPoint.AdjustX(basegfx::fround(aNewHomogenMatrix.get(0, 2)) );
+ aVclPoint.AdjustY(basegfx::fround(aNewHomogenMatrix.get(1, 2)) );
+
+ // #88491# position relative to anchor
+ if( GetSdrObject()->getSdrModelFromSdrObject().IsWriter() )
+ {
+ aVclPoint += GetSdrObject()->GetAnchorPos();
+ }
+
+ static_cast<SdrCaptionObj*>(GetSdrObject())->SetTailPos(aVclPoint);
+
+ return true;
+ }
+ break;
+ }
+ case OWN_ATTR_TRANSFORMATION:
+ {
+ drawing::HomogenMatrix3 aMatrix;
+ if(rValue >>= aMatrix)
+ {
+ basegfx::B2DPolyPolygon aNewPolyPolygon;
+ basegfx::B2DHomMatrix aNewHomogenMatrix;
+
+ // tdf#117145 SdrModel data is app-specific
+ GetSdrObject()->TRGetBaseGeometry(aNewHomogenMatrix, aNewPolyPolygon);
+
+ aNewHomogenMatrix.set(0, 0, aMatrix.Line1.Column1);
+ aNewHomogenMatrix.set(0, 1, aMatrix.Line1.Column2);
+ aNewHomogenMatrix.set(0, 2, aMatrix.Line1.Column3);
+ aNewHomogenMatrix.set(1, 0, aMatrix.Line2.Column1);
+ aNewHomogenMatrix.set(1, 1, aMatrix.Line2.Column2);
+ aNewHomogenMatrix.set(1, 2, aMatrix.Line2.Column3);
+ aNewHomogenMatrix.set(2, 0, aMatrix.Line3.Column1);
+ aNewHomogenMatrix.set(2, 1, aMatrix.Line3.Column2);
+ aNewHomogenMatrix.set(2, 2, aMatrix.Line3.Column3);
+
+ // tdf#117145 metric of SdrModel is app-specific, metric of UNO API is 100thmm
+ // Need to adapt aNewHomogenMatrix from 100thmm to app-specific
+ ForceMetricToItemPoolMetric(aNewHomogenMatrix);
+
+ GetSdrObject()->TRSetBaseGeometry(aNewHomogenMatrix, aNewPolyPolygon);
+ return true;
+ }
+ break;
+ }
+
+ case OWN_ATTR_ZORDER:
+ {
+ sal_Int32 nNewOrdNum = 0;
+ if(rValue >>= nNewOrdNum)
+ {
+ SdrObjList* pObjList = GetSdrObject()->getParentSdrObjListFromSdrObject();
+ if( pObjList )
+ pObjList->SetExistingObjectOrdNum( GetSdrObject(), static_cast<size_t>(nNewOrdNum) );
+ return true;
+ }
+ break;
+ }
+ case OWN_ATTR_FRAMERECT:
+ {
+ awt::Rectangle aUnoRect;
+ if(rValue >>= aUnoRect)
+ {
+ Point aTopLeft( aUnoRect.X, aUnoRect.Y );
+ Size aObjSize( aUnoRect.Width, aUnoRect.Height );
+ ForceMetricToItemPoolMetric(aTopLeft);
+ ForceMetricToItemPoolMetric(aObjSize);
+ tools::Rectangle aRect;
+ aRect.SetPos(aTopLeft);
+ aRect.SetSize(aObjSize);
+ GetSdrObject()->SetSnapRect(aRect);
+ return true;
+ }
+ break;
+ }
+ case OWN_ATTR_MIRRORED:
+ {
+ bool bMirror;
+ if(rValue >>= bMirror )
+ {
+ SdrGrafObj* pObj = dynamic_cast< SdrGrafObj* >( GetSdrObject() );
+ if( pObj )
+ pObj->SetMirrored(bMirror);
+ return true;
+ }
+ break;
+ }
+ case OWN_ATTR_EDGE_START_OBJ:
+ case OWN_ATTR_EDGE_END_OBJ:
+ case OWN_ATTR_GLUEID_HEAD:
+ case OWN_ATTR_GLUEID_TAIL:
+ case OWN_ATTR_EDGE_START_POS:
+ case OWN_ATTR_EDGE_END_POS:
+ case OWN_ATTR_EDGE_POLYPOLYGONBEZIER:
+ {
+ SdrEdgeObj* pEdgeObj = dynamic_cast< SdrEdgeObj* >(GetSdrObject());
+ if(pEdgeObj)
+ {
+ switch(pProperty->nWID)
+ {
+ case OWN_ATTR_EDGE_START_OBJ:
+ case OWN_ATTR_EDGE_END_OBJ:
+ {
+ Reference< drawing::XShape > xShape;
+ if( rValue >>= xShape )
+ {
+ SdrObject* pNode = SdrObject::getSdrObjectFromXShape(xShape);
+ if( pNode )
+ {
+ pEdgeObj->ConnectToNode( pProperty->nWID == OWN_ATTR_EDGE_START_OBJ, pNode );
+ pEdgeObj->setGluePointIndex( pProperty->nWID == OWN_ATTR_EDGE_START_OBJ );
+ return true;
+ }
+ }
+ break;
+ }
+
+ case OWN_ATTR_EDGE_START_POS:
+ case OWN_ATTR_EDGE_END_POS:
+ {
+ awt::Point aUnoPoint;
+ if( rValue >>= aUnoPoint )
+ {
+ Point aPoint( aUnoPoint.X, aUnoPoint.Y );
+
+ // Reintroduction of fix for issue i59051 (#i108851#)
+ // perform metric change before applying anchor position,
+ // because the anchor position is in pool metric.
+ ForceMetricToItemPoolMetric( aPoint );
+ if( GetSdrObject()->getSdrModelFromSdrObject().IsWriter() )
+ aPoint += GetSdrObject()->GetAnchorPos();
+
+ pEdgeObj->SetTailPoint( pProperty->nWID == OWN_ATTR_EDGE_START_POS, aPoint );
+ return true;
+ }
+ break;
+ }
+
+ case OWN_ATTR_GLUEID_HEAD:
+ case OWN_ATTR_GLUEID_TAIL:
+ {
+ sal_Int32 nId = 0;
+ if( rValue >>= nId )
+ {
+ pEdgeObj->setGluePointIndex( pProperty->nWID == OWN_ATTR_GLUEID_HEAD, nId );
+ return true;
+ }
+ break;
+ }
+ case OWN_ATTR_EDGE_POLYPOLYGONBEZIER:
+ {
+ basegfx::B2DPolyPolygon aNewPolyPolygon;
+
+ // #123616# be a little bit more flexible regarding the data type used
+ if( auto s = o3tl::tryAccess<drawing::PointSequenceSequence>(rValue) )
+ {
+ // get polygpon data from PointSequenceSequence
+ aNewPolyPolygon = basegfx::utils::UnoPointSequenceSequenceToB2DPolyPolygon(
+ *s);
+ }
+ else if( auto cs = o3tl::tryAccess<drawing::PolyPolygonBezierCoords>(rValue) )
+ {
+ // get polygpon data from PolyPolygonBezierCoords
+ aNewPolyPolygon = basegfx::utils::UnoPolyPolygonBezierCoordsToB2DPolyPolygon(
+ *cs);
+ }
+
+ if(aNewPolyPolygon.count())
+ {
+ // Reintroduction of fix for issue i59051 (#i108851#)
+ ForceMetricToItemPoolMetric( aNewPolyPolygon );
+ if( GetSdrObject()->getSdrModelFromSdrObject().IsWriter() )
+ {
+ Point aPoint( GetSdrObject()->GetAnchorPos() );
+ aNewPolyPolygon.transform(basegfx::utils::createTranslateB2DHomMatrix(aPoint.X(), aPoint.Y()));
+ }
+ pEdgeObj->SetEdgeTrackPath( aNewPolyPolygon );
+ return true;
+ }
+ }
+ }
+ }
+ break;
+ }
+ case OWN_ATTR_MEASURE_START_POS:
+ case OWN_ATTR_MEASURE_END_POS:
+ {
+ SdrMeasureObj* pMeasureObj = dynamic_cast< SdrMeasureObj* >(GetSdrObject());
+ awt::Point aUnoPoint;
+ if(pMeasureObj && ( rValue >>= aUnoPoint ) )
+ {
+ Point aPoint( aUnoPoint.X, aUnoPoint.Y );
+
+ // Reintroduction of fix for issue #i59051# (#i108851#)
+ ForceMetricToItemPoolMetric( aPoint );
+ if( GetSdrObject()->getSdrModelFromSdrObject().IsWriter() )
+ aPoint += GetSdrObject()->GetAnchorPos();
+
+ pMeasureObj->NbcSetPoint( aPoint, pProperty->nWID == OWN_ATTR_MEASURE_START_POS ? 0 : 1 );
+ pMeasureObj->SetChanged();
+ pMeasureObj->BroadcastObjectChange();
+ return true;
+ }
+ break;
+ }
+ case OWN_ATTR_FILLBMP_MODE:
+ {
+ drawing::BitmapMode eMode;
+ if(!(rValue >>= eMode) )
+ {
+ sal_Int32 nMode = 0;
+ if(!(rValue >>= nMode))
+ break;
+
+ eMode = static_cast<drawing::BitmapMode>(nMode);
+ }
+ GetSdrObject()->SetMergedItem( XFillBmpStretchItem( eMode == drawing::BitmapMode_STRETCH ) );
+ GetSdrObject()->SetMergedItem( XFillBmpTileItem( eMode == drawing::BitmapMode_REPEAT ) );
+ return true;
+ }
+
+ case SDRATTR_LAYERID:
+ {
+ sal_Int16 nLayerId = sal_Int16();
+ if( rValue >>= nLayerId )
+ {
+ SdrLayer* pLayer = GetSdrObject()->getSdrModelFromSdrObject().GetLayerAdmin().GetLayerPerID(SdrLayerID(nLayerId));
+ if( pLayer )
+ {
+ GetSdrObject()->SetLayer(SdrLayerID(nLayerId));
+ return true;
+ }
+ }
+ break;
+ }
+
+ case SDRATTR_LAYERNAME:
+ {
+ OUString aLayerName;
+ if( rValue >>= aLayerName )
+ {
+ const SdrLayer* pLayer = GetSdrObject()->getSdrModelFromSdrObject().GetLayerAdmin().GetLayer(aLayerName);
+ if( pLayer != nullptr )
+ {
+ GetSdrObject()->SetLayer( pLayer->GetID() );
+ return true;
+ }
+ }
+ break;
+ }
+ case SDRATTR_ROTATEANGLE:
+ {
+ sal_Int32 nTmp = 0;
+ if( rValue >>= nTmp )
+ {
+ Degree100 nAngle(nTmp);
+ Point aRef1(GetSdrObject()->GetSnapRect().Center());
+ nAngle -= GetSdrObject()->GetRotateAngle();
+ if (nAngle)
+ {
+ double nSin = sin(toRadians(nAngle));
+ double nCos = cos(toRadians(nAngle));
+ GetSdrObject()->Rotate(aRef1,nAngle,nSin,nCos);
+ }
+ return true;
+ }
+
+ break;
+ }
+
+ case SDRATTR_SHEARANGLE:
+ {
+ sal_Int32 nTmp = 0;
+ if( rValue >>= nTmp )
+ {
+ Degree100 nShear(nTmp);
+ nShear -= GetSdrObject()->GetShearAngle();
+ if(nShear)
+ {
+ Point aRef1(GetSdrObject()->GetSnapRect().Center());
+ double nTan = tan(toRadians(nShear));
+ GetSdrObject()->Shear(aRef1,nShear,nTan,false);
+ return true;
+ }
+ }
+
+ break;
+ }
+
+ case OWN_ATTR_INTEROPGRABBAG:
+ {
+ GetSdrObject()->SetGrabBagItem(rValue);
+ return true;
+ }
+
+ case SDRATTR_OBJMOVEPROTECT:
+ {
+ bool bMoveProtect;
+ if( rValue >>= bMoveProtect )
+ {
+ GetSdrObject()->SetMoveProtect(bMoveProtect);
+ return true;
+ }
+ break;
+ }
+ case SDRATTR_OBJECTNAME:
+ {
+ OUString aName;
+ if( rValue >>= aName )
+ {
+ GetSdrObject()->SetName( aName );
+ return true;
+ }
+ break;
+ }
+
+ case OWN_ATTR_TEXTFITTOSIZESCALE:
+ {
+ sal_Int16 nMaxScale = 0;
+ if (rValue >>= nMaxScale)
+ {
+ SdrTextFitToSizeTypeItem aItem(GetSdrObject()->GetMergedItem(SDRATTR_TEXT_FITTOSIZE));
+ aItem.SetMaxScale(nMaxScale);
+ GetSdrObject()->SetMergedItem(aItem);
+ return true;
+ }
+ break;
+ }
+
+ // #i68101#
+ case OWN_ATTR_MISC_OBJ_TITLE:
+ {
+ OUString aTitle;
+ if( rValue >>= aTitle )
+ {
+ GetSdrObject()->SetTitle( aTitle );
+ return true;
+ }
+ break;
+ }
+ case OWN_ATTR_MISC_OBJ_DESCRIPTION:
+ {
+ OUString aDescription;
+ if( rValue >>= aDescription )
+ {
+ GetSdrObject()->SetDescription( aDescription );
+ return true;
+ }
+ break;
+ }
+
+ case SDRATTR_OBJPRINTABLE:
+ {
+ bool bPrintable;
+ if( rValue >>= bPrintable )
+ {
+ GetSdrObject()->SetPrintable(bPrintable);
+ return true;
+ }
+ break;
+ }
+ case SDRATTR_OBJVISIBLE:
+ {
+ bool bVisible;
+ if( rValue >>= bVisible )
+ {
+ GetSdrObject()->SetVisible(bVisible);
+ return true;
+ }
+ break;
+ }
+ case SDRATTR_OBJSIZEPROTECT:
+ {
+ bool bResizeProtect;
+ if( rValue >>= bResizeProtect )
+ {
+ GetSdrObject()->SetResizeProtect(bResizeProtect);
+ return true;
+ }
+ break;
+ }
+ case OWN_ATTR_PAGE_NUMBER:
+ {
+ sal_Int32 nPageNum = 0;
+ if( (rValue >>= nPageNum) && ( nPageNum >= 0 ) && ( nPageNum <= 0xffff ) )
+ {
+ SdrPageObj* pPageObj = dynamic_cast< SdrPageObj* >(GetSdrObject());
+ if( pPageObj )
+ {
+ SdrModel& rModel(pPageObj->getSdrModelFromSdrObject());
+ SdrPage* pNewPage = nullptr;
+ const sal_uInt16 nDestinationPageNum(static_cast<sal_uInt16>((nPageNum << 1) - 1));
+
+ if(nDestinationPageNum < rModel.GetPageCount())
+ {
+ pNewPage = rModel.GetPage(nDestinationPageNum);
+ }
+
+ pPageObj->SetReferencedPage(pNewPage);
+ }
+
+ return true;
+ }
+ break;
+ }
+ case XATTR_FILLBITMAP:
+ case XATTR_FILLGRADIENT:
+ case XATTR_FILLHATCH:
+ case XATTR_FILLFLOATTRANSPARENCE:
+ case XATTR_LINEEND:
+ case XATTR_LINESTART:
+ case XATTR_LINEDASH:
+ {
+ if( pProperty->nMemberId == MID_NAME )
+ {
+ OUString aApiName;
+ if( rValue >>= aApiName )
+ {
+ if( SetFillAttribute( pProperty->nWID, aApiName ) )
+ return true;
+ }
+ break;
+ }
+ else
+ {
+ return false;
+ }
+ }
+
+ case OWN_ATTR_TEXTCOLUMNS:
+ {
+ if (auto pTextObj = dynamic_cast<SdrTextObj*>(GetSdrObject()))
+ {
+ css::uno::Reference<css::text::XTextColumns> xTextColumns;
+ if (rValue >>= xTextColumns)
+ {
+ pTextObj->SetTextColumnsNumber(xTextColumns->getColumnCount());
+ if (css::uno::Reference<css::beans::XPropertySet> xPropSet{ xTextColumns,
+ css::uno::UNO_QUERY })
+ {
+ auto aVal = xPropSet->getPropertyValue("AutomaticDistance");
+ if (sal_Int32 nSpacing; aVal >>= nSpacing)
+ pTextObj->SetTextColumnsSpacing(nSpacing);
+ }
+ }
+ }
+ return true;
+ }
+
+ case OWN_ATTR_HYPERLINK:
+ {
+ OUString sHyperlink;
+ if (rValue >>= sHyperlink)
+ {
+ GetSdrObject()->setHyperlink(sHyperlink);
+ return true;
+ }
+ break;
+ }
+
+ default:
+ {
+ return false;
+ }
+ }
+
+ OUString sExceptionMessage ("IllegalArgumentException in SvxShape::setPropertyValueImpl."
+ " Property Type: " + pProperty->aType.getTypeName() + " Property nWID: " + OUString::number(pProperty->nWID)
+ + " Value Type: " + (rValue.hasValue() ? rValue.getValueTypeName() : "void (no value)"));
+
+ throw lang::IllegalArgumentException(sExceptionMessage, nullptr, 1);
+}
+
+
+bool SvxShape::getPropertyValueImpl( const OUString&, const SfxItemPropertyMapEntry* pProperty, css::uno::Any& rValue )
+{
+ switch( pProperty->nWID )
+ {
+ case OWN_ATTR_CAPTION_POINT:
+ {
+ Point aVclPoint = static_cast<SdrCaptionObj*>(GetSdrObject())->GetTailPos();
+
+ // #88491# make pos relative to anchor
+ if( GetSdrObject()->getSdrModelFromSdrObject().IsWriter() )
+ {
+ aVclPoint -= GetSdrObject()->GetAnchorPos();
+ }
+
+ // #90763# pos is absolute, make it relative to top left
+ basegfx::B2DPolyPolygon aNewPolyPolygon;
+ basegfx::B2DHomMatrix aNewHomogenMatrix;
+ GetSdrObject()->TRGetBaseGeometry(aNewHomogenMatrix, aNewPolyPolygon);
+
+ aVclPoint.AdjustX( -(basegfx::fround(aNewHomogenMatrix.get(0, 2))) );
+ aVclPoint.AdjustY( -(basegfx::fround(aNewHomogenMatrix.get(1, 2))) );
+
+ // tdf#117145 metric of SdrModel is app-specific, metric of UNO API is 100thmm
+ // Need to adapt aVclPoint from app-specific to 100thmm
+ ForceMetricTo100th_mm(aVclPoint);
+
+ awt::Point aPnt( aVclPoint.X(), aVclPoint.Y() );
+ rValue <<= aPnt;
+ break;
+ }
+
+ case OWN_ATTR_TRANSFORMATION:
+ {
+ basegfx::B2DPolyPolygon aNewPolyPolygon;
+ basegfx::B2DHomMatrix aNewHomogenMatrix;
+ GetSdrObject()->TRGetBaseGeometry(aNewHomogenMatrix, aNewPolyPolygon);
+ drawing::HomogenMatrix3 aMatrix;
+
+ // tdf#117145 metric of SdrModel is app-specific, metric of UNO API is 100thmm
+ // Need to adapt aNewHomogenMatrix from app-specific to 100thmm
+ ForceMetricTo100th_mm(aNewHomogenMatrix);
+
+ aMatrix.Line1.Column1 = aNewHomogenMatrix.get(0, 0);
+ aMatrix.Line1.Column2 = aNewHomogenMatrix.get(0, 1);
+ aMatrix.Line1.Column3 = aNewHomogenMatrix.get(0, 2);
+ aMatrix.Line2.Column1 = aNewHomogenMatrix.get(1, 0);
+ aMatrix.Line2.Column2 = aNewHomogenMatrix.get(1, 1);
+ aMatrix.Line2.Column3 = aNewHomogenMatrix.get(1, 2);
+ aMatrix.Line3.Column1 = aNewHomogenMatrix.get(2, 0);
+ aMatrix.Line3.Column2 = aNewHomogenMatrix.get(2, 1);
+ aMatrix.Line3.Column3 = aNewHomogenMatrix.get(2, 2);
+
+ rValue <<= aMatrix;
+
+ break;
+ }
+
+ case OWN_ATTR_ZORDER:
+ {
+ rValue <<= static_cast<sal_Int32>(GetSdrObject()->GetOrdNum());
+ break;
+ }
+
+ case OWN_ATTR_BITMAP:
+ {
+ rValue = GetBitmap();
+ if(!rValue.hasValue())
+ throw uno::RuntimeException();
+
+ break;
+ }
+
+ case OWN_ATTR_ISFONTWORK:
+ {
+ bool bIsFontwork = false;
+ if (const SdrTextObj* pTextObj = dynamic_cast<const SdrTextObj*>(GetSdrObject()))
+ bIsFontwork = pTextObj->IsFontwork();
+ rValue <<= bIsFontwork;
+ break;
+ }
+
+ case OWN_ATTR_FRAMERECT:
+ {
+ tools::Rectangle aRect( GetSdrObject()->GetSnapRect() );
+ Point aTopLeft( aRect.TopLeft() );
+ Size aObjSize( aRect.GetWidth(), aRect.GetHeight() );
+ ForceMetricTo100th_mm(aTopLeft);
+ ForceMetricTo100th_mm(aObjSize);
+ css::awt::Rectangle aUnoRect(
+ aTopLeft.X(), aTopLeft.Y(),
+ aObjSize.getWidth(), aObjSize.getHeight() );
+ rValue <<= aUnoRect;
+ break;
+ }
+
+ case OWN_ATTR_BOUNDRECT:
+ {
+ tools::Rectangle aRect( GetSdrObject()->GetCurrentBoundRect() );
+ Point aTopLeft( aRect.TopLeft() );
+ Size aObjSize( aRect.GetWidth(), aRect.GetHeight() );
+ ForceMetricTo100th_mm(aTopLeft);
+ ForceMetricTo100th_mm(aObjSize);
+ css::awt::Rectangle aUnoRect(
+ aTopLeft.X(), aTopLeft.Y(),
+ aObjSize.getWidth(), aObjSize.getHeight() );
+ rValue <<= aUnoRect;
+ break;
+ }
+
+ case OWN_ATTR_LDNAME:
+ {
+ OUString aName( GetSdrObject()->GetName() );
+ rValue <<= aName;
+ break;
+ }
+
+ case OWN_ATTR_LDBITMAP:
+ {
+ OUString sId;
+ if( GetSdrObject()->GetObjInventor() == SdrInventor::Default && GetSdrObject()->GetObjIdentifier() == SdrObjKind::OLE2 )
+ {
+ sId = RID_UNODRAW_OLE2;
+ }
+ else if( GetSdrObject()->GetObjInventor() == SdrInventor::Default && GetSdrObject()->GetObjIdentifier() == SdrObjKind::Graphic )
+ {
+ sId = RID_UNODRAW_GRAPHICS;
+ }
+ else
+ {
+ sId = RID_UNODRAW_OBJECTS;
+ }
+
+ BitmapEx aBmp(sId);
+ Reference<awt::XBitmap> xBmp(VCLUnoHelper::CreateBitmap(aBmp));
+
+ rValue <<= xBmp;
+ break;
+ }
+
+ case OWN_ATTR_MIRRORED:
+ {
+ bool bMirror = false;
+ if( HasSdrObject() )
+ if (auto pGrafObj = dynamic_cast<SdrGrafObj*>(GetSdrObject()) )
+ bMirror = pGrafObj->IsMirrored();
+
+ rValue <<= bMirror;
+ break;
+ }
+
+ case OWN_ATTR_EDGE_START_OBJ:
+ case OWN_ATTR_EDGE_START_POS:
+ case OWN_ATTR_EDGE_END_POS:
+ case OWN_ATTR_EDGE_END_OBJ:
+ case OWN_ATTR_GLUEID_HEAD:
+ case OWN_ATTR_GLUEID_TAIL:
+ case OWN_ATTR_EDGE_POLYPOLYGONBEZIER:
+ {
+ SdrEdgeObj* pEdgeObj = dynamic_cast<SdrEdgeObj*>(GetSdrObject());
+ if(pEdgeObj)
+ {
+ switch(pProperty->nWID)
+ {
+ case OWN_ATTR_EDGE_START_OBJ:
+ case OWN_ATTR_EDGE_END_OBJ:
+ {
+ SdrObject* pNode = pEdgeObj->GetConnectedNode(pProperty->nWID == OWN_ATTR_EDGE_START_OBJ);
+ if(pNode)
+ {
+ Reference< drawing::XShape > xShape( GetXShapeForSdrObject( pNode ) );
+ if(xShape.is())
+ rValue <<= xShape;
+
+ }
+ break;
+ }
+
+ case OWN_ATTR_EDGE_START_POS:
+ case OWN_ATTR_EDGE_END_POS:
+ {
+ Point aPoint( pEdgeObj->GetTailPoint( pProperty->nWID == OWN_ATTR_EDGE_START_POS ) );
+ if( GetSdrObject()->getSdrModelFromSdrObject().IsWriter() )
+ aPoint -= GetSdrObject()->GetAnchorPos();
+
+ ForceMetricTo100th_mm( aPoint );
+ awt::Point aUnoPoint( aPoint.X(), aPoint.Y() );
+
+ rValue <<= aUnoPoint;
+ break;
+ }
+ case OWN_ATTR_GLUEID_HEAD:
+ case OWN_ATTR_GLUEID_TAIL:
+ {
+ rValue <<= pEdgeObj->getGluePointIndex( pProperty->nWID == OWN_ATTR_GLUEID_HEAD );
+ break;
+ }
+ case OWN_ATTR_EDGE_POLYPOLYGONBEZIER:
+ {
+ basegfx::B2DPolyPolygon aPolyPoly( pEdgeObj->GetEdgeTrackPath() );
+ if( GetSdrObject()->getSdrModelFromSdrObject().IsWriter() )
+ {
+ Point aPoint( GetSdrObject()->GetAnchorPos() );
+ aPolyPoly.transform(basegfx::utils::createTranslateB2DHomMatrix(-aPoint.X(), -aPoint.Y()));
+ }
+ // Reintroduction of fix for issue #i59051# (#i108851#)
+ ForceMetricTo100th_mm( aPolyPoly );
+ drawing::PolyPolygonBezierCoords aRetval;
+ basegfx::utils::B2DPolyPolygonToUnoPolyPolygonBezierCoords( aPolyPoly, aRetval);
+ rValue <<= aRetval;
+ break;
+ }
+ }
+ }
+ break;
+ }
+
+ case OWN_ATTR_MEASURE_START_POS:
+ case OWN_ATTR_MEASURE_END_POS:
+ {
+ SdrMeasureObj* pMeasureObj = dynamic_cast<SdrMeasureObj*>(GetSdrObject());
+ if(pMeasureObj)
+ {
+ Point aPoint( pMeasureObj->GetPoint( pProperty->nWID == OWN_ATTR_MEASURE_START_POS ? 0 : 1 ) );
+ if( GetSdrObject()->getSdrModelFromSdrObject().IsWriter() )
+ aPoint -= GetSdrObject()->GetAnchorPos();
+
+ // Reintroduction of fix for issue #i59051# (#i108851#)
+ ForceMetricTo100th_mm( aPoint );
+ awt::Point aUnoPoint( aPoint.X(), aPoint.Y() );
+
+ rValue <<= aUnoPoint;
+ break;
+ }
+ break;
+ }
+
+ case OWN_ATTR_FILLBMP_MODE:
+ {
+ const SfxItemSet& rObjItemSet = GetSdrObject()->GetMergedItemSet();
+
+ if (rObjItemSet.Get(XATTR_FILLBMP_TILE).GetValue())
+ {
+ rValue <<= drawing::BitmapMode_REPEAT;
+ }
+ else if (rObjItemSet.Get(XATTR_FILLBMP_STRETCH).GetValue())
+ {
+ rValue <<= drawing::BitmapMode_STRETCH;
+ }
+ else
+ {
+ rValue <<= drawing::BitmapMode_NO_REPEAT;
+ }
+ break;
+ }
+ case SDRATTR_LAYERID:
+ rValue <<= GetSdrObject()->GetLayer().get();
+ break;
+
+ case SDRATTR_LAYERNAME:
+ {
+ SdrLayer* pLayer = GetSdrObject()->getSdrModelFromSdrObject().GetLayerAdmin().GetLayerPerID(GetSdrObject()->GetLayer());
+ if( pLayer )
+ {
+ rValue <<= pLayer->GetName();
+ }
+ break;
+ }
+
+ case SDRATTR_ROTATEANGLE:
+ rValue <<= static_cast<sal_Int32>(GetSdrObject()->GetRotateAngle());
+ break;
+
+ case SDRATTR_SHEARANGLE:
+ rValue <<= static_cast<sal_Int32>(GetSdrObject()->GetShearAngle());
+ break;
+
+ case OWN_ATTR_INTEROPGRABBAG:
+ {
+ GetSdrObject()->GetGrabBagItem(rValue);
+ break;
+ }
+
+ case SDRATTR_OBJMOVEPROTECT:
+ rValue <<= GetSdrObject()->IsMoveProtect();
+ break;
+
+ case SDRATTR_OBJECTNAME:
+ {
+ OUString aName( GetSdrObject()->GetName() );
+ rValue <<= aName;
+ break;
+ }
+
+ // #i68101#
+ case OWN_ATTR_MISC_OBJ_TITLE:
+ {
+ OUString aTitle( GetSdrObject()->GetTitle() );
+ rValue <<= aTitle;
+ break;
+ }
+
+ case OWN_ATTR_MISC_OBJ_DESCRIPTION:
+ {
+ OUString aDescription( GetSdrObject()->GetDescription() );
+ rValue <<= aDescription;
+ break;
+ }
+
+ case SDRATTR_OBJPRINTABLE:
+ rValue <<= GetSdrObject()->IsPrintable();
+ break;
+
+ case SDRATTR_OBJVISIBLE:
+ rValue <<= GetSdrObject()->IsVisible();
+ break;
+
+ case SDRATTR_OBJSIZEPROTECT:
+ rValue <<= GetSdrObject()->IsResizeProtect();
+ break;
+
+ case OWN_ATTR_PAGE_NUMBER:
+ {
+ SdrPageObj* pPageObj = dynamic_cast<SdrPageObj*>(GetSdrObject());
+ if(pPageObj)
+ {
+ SdrPage* pPage = pPageObj->GetReferencedPage();
+ sal_Int32 nPageNumber = pPage ? pPage->GetPageNum() : 0;
+ nPageNumber++;
+ nPageNumber >>= 1;
+ rValue <<= nPageNumber;
+ }
+ break;
+ }
+
+ case OWN_ATTR_UINAME_SINGULAR:
+ {
+ rValue <<= GetSdrObject()->TakeObjNameSingul();
+ break;
+ }
+
+ case OWN_ATTR_TEXTFITTOSIZESCALE:
+ {
+ rValue <<= GetTextFitToSizeScale(GetSdrObject());
+ break;
+ }
+
+ case OWN_ATTR_UINAME_PLURAL:
+ {
+ rValue <<= GetSdrObject()->TakeObjNamePlural();
+ break;
+ }
+ case OWN_ATTR_METAFILE:
+ {
+ SdrOle2Obj* pObj = dynamic_cast<SdrOle2Obj*>(GetSdrObject());
+ if( pObj )
+ {
+ const Graphic* pGraphic = pObj->GetGraphic();
+ if( pGraphic )
+ {
+ bool bIsWMF = false;
+ if ( pGraphic->IsGfxLink() )
+ {
+ GfxLink aLnk = pGraphic->GetGfxLink();
+ if ( aLnk.GetType() == GfxLinkType::NativeWmf )
+ {
+ bIsWMF = true;
+ uno::Sequence<sal_Int8> aSeq(reinterpret_cast<sal_Int8 const *>(aLnk.GetData()), static_cast<sal_Int32>(aLnk.GetDataSize()));
+ rValue <<= aSeq;
+ }
+ }
+ if ( !bIsWMF )
+ {
+ // #119735# just use GetGDIMetaFile, it will create a buffered version of contained bitmap now automatically
+ GDIMetaFile aMtf(pObj->GetGraphic()->GetGDIMetaFile());
+ SvMemoryStream aDestStrm( 65535, 65535 );
+ ConvertGDIMetaFileToWMF( aMtf, aDestStrm, nullptr, false );
+ const uno::Sequence<sal_Int8> aSeq(
+ static_cast< const sal_Int8* >(aDestStrm.GetData()),
+ aDestStrm.GetEndOfData());
+ rValue <<= aSeq;
+ }
+ }
+ }
+ else
+ {
+ rValue = GetBitmap( true );
+ }
+ break;
+ }
+
+ case OWN_ATTR_TEXTCOLUMNS:
+ {
+ if (auto pTextObj = dynamic_cast<const SdrTextObj*>(GetSdrObject()))
+ {
+ if (pTextObj->HasTextColumnsNumber() || pTextObj->HasTextColumnsSpacing())
+ {
+ auto xIf = SvxXTextColumns_createInstance();
+ css::uno::Reference<css::text::XTextColumns> xCols(xIf, css::uno::UNO_QUERY_THROW);
+ xCols->setColumnCount(pTextObj->GetTextColumnsNumber());
+ css::uno::Reference<css::beans::XPropertySet> xProp(xIf, css::uno::UNO_QUERY_THROW);
+ xProp->setPropertyValue("AutomaticDistance",
+ css::uno::Any(pTextObj->GetTextColumnsSpacing()));
+ rValue <<= xIf;
+ }
+ }
+ break;
+ }
+
+ case OWN_ATTR_HYPERLINK:
+ {
+ rValue <<= GetSdrObject()->getHyperlink();
+ break;
+ }
+
+ default:
+ return false;
+ }
+ return true;
+}
+
+
+bool SvxShape::getPropertyStateImpl( const SfxItemPropertyMapEntry* pProperty, css::beans::PropertyState& rState )
+{
+ if( pProperty->nWID == OWN_ATTR_FILLBMP_MODE )
+ {
+ const SfxItemSet& rSet = GetSdrObject()->GetMergedItemSet();
+
+ if( rSet.GetItemState( XATTR_FILLBMP_STRETCH, false ) == SfxItemState::SET ||
+ rSet.GetItemState( XATTR_FILLBMP_TILE, false ) == SfxItemState::SET )
+ {
+ rState = beans::PropertyState_DIRECT_VALUE;
+ }
+ else
+ {
+ rState = beans::PropertyState_AMBIGUOUS_VALUE;
+ }
+ }
+ else if((( pProperty->nWID >= OWN_ATTR_VALUE_START && pProperty->nWID <= OWN_ATTR_VALUE_END ) ||
+ ( pProperty->nWID >= SDRATTR_NOTPERSIST_FIRST && pProperty->nWID <= SDRATTR_NOTPERSIST_LAST )) && ( pProperty->nWID != SDRATTR_TEXTDIRECTION ) )
+ {
+ rState = beans::PropertyState_DIRECT_VALUE;
+ }
+ else
+ {
+ return false;
+ }
+
+ return true;
+}
+
+
+bool SvxShape::setPropertyToDefaultImpl( const SfxItemPropertyMapEntry* pProperty )
+{
+ if( pProperty->nWID == OWN_ATTR_FILLBMP_MODE )
+ {
+ GetSdrObject()->ClearMergedItem( XATTR_FILLBMP_STRETCH );
+ GetSdrObject()->ClearMergedItem( XATTR_FILLBMP_TILE );
+ return true;
+ }
+ else if((pProperty->nWID >= OWN_ATTR_VALUE_START && pProperty->nWID <= OWN_ATTR_VALUE_END ) ||
+ ( pProperty->nWID >= SDRATTR_NOTPERSIST_FIRST && pProperty->nWID <= SDRATTR_NOTPERSIST_LAST ))
+ {
+ return true;
+ }
+ else
+ {
+ return false;
+ }
+}
+
+
+uno::Sequence< beans::PropertyState > SAL_CALL SvxShape::getPropertyStates( const uno::Sequence< OUString >& aPropertyName )
+{
+ const sal_Int32 nCount = aPropertyName.getLength();
+ uno::Sequence< beans::PropertyState > aRet( nCount );
+
+ std::transform(aPropertyName.begin(), aPropertyName.end(), aRet.getArray(),
+ [this](const OUString& rName) -> beans::PropertyState { return getPropertyState(rName); });
+
+ return aRet;
+}
+
+
+void SAL_CALL SvxShape::setPropertyToDefault( const OUString& PropertyName )
+{
+ if( mpImpl->mpMaster )
+ {
+ mpImpl->mpMaster->setPropertyToDefault( PropertyName );
+ }
+ else
+ {
+ _setPropertyToDefault( PropertyName );
+ }
+}
+
+void SvxShape::_setPropertyToDefault( const OUString& PropertyName )
+{
+ ::SolarMutexGuard aGuard;
+
+ const SfxItemPropertyMapEntry* pProperty = mpPropSet->getPropertyMapEntry(PropertyName);
+
+ if( !HasSdrObject() || pProperty == nullptr )
+ throw beans::UnknownPropertyException( PropertyName, static_cast<cppu::OWeakObject*>(this));
+
+ if( !setPropertyToDefaultImpl( pProperty ) )
+ {
+ GetSdrObject()->ClearMergedItem( pProperty->nWID );
+ }
+
+ GetSdrObject()->getSdrModelFromSdrObject().SetChanged();
+}
+
+
+uno::Any SAL_CALL SvxShape::getPropertyDefault( const OUString& aPropertyName )
+{
+ if( mpImpl->mpMaster )
+ {
+ return mpImpl->mpMaster->getPropertyDefault( aPropertyName );
+ }
+ else
+ {
+ return _getPropertyDefault( aPropertyName );
+ }
+}
+
+uno::Any SvxShape::_getPropertyDefault( const OUString& aPropertyName )
+{
+ ::SolarMutexGuard aGuard;
+
+ const SfxItemPropertyMapEntry* pMap = mpPropSet->getPropertyMapEntry(aPropertyName);
+
+ if( !HasSdrObject() || pMap == nullptr )
+ throw beans::UnknownPropertyException( aPropertyName, static_cast<cppu::OWeakObject*>(this));
+
+ if(( pMap->nWID >= OWN_ATTR_VALUE_START && pMap->nWID <= OWN_ATTR_VALUE_END ) ||
+ ( pMap->nWID >= SDRATTR_NOTPERSIST_FIRST && pMap->nWID <= SDRATTR_NOTPERSIST_LAST ))
+ {
+ return getPropertyValue( aPropertyName );
+ }
+
+ // get default from ItemPool
+ if(!SfxItemPool::IsWhich(pMap->nWID))
+ throw beans::UnknownPropertyException( "No WhichID " + OUString::number(pMap->nWID) + " for " + aPropertyName, static_cast<cppu::OWeakObject*>(this));
+
+ SfxItemSet aSet( GetSdrObject()->getSdrModelFromSdrObject().GetItemPool(), pMap->nWID, pMap->nWID );
+ aSet.Put(GetSdrObject()->getSdrModelFromSdrObject().GetItemPool().GetDefaultItem(pMap->nWID));
+
+ return GetAnyForItem( aSet, pMap );
+}
+
+// XMultiPropertyStates
+void SvxShape::setAllPropertiesToDefault()
+{
+ ::SolarMutexGuard aGuard;
+
+ if( !HasSdrObject() )
+ throw lang::DisposedException();
+ GetSdrObject()->ClearMergedItem(); // nWhich == 0 => all
+
+ if(dynamic_cast<const SdrGrafObj*>(GetSdrObject()) != nullptr)
+ {
+ // defaults for graphic objects have changed:
+ GetSdrObject()->SetMergedItem( XFillStyleItem( drawing::FillStyle_NONE ) );
+ GetSdrObject()->SetMergedItem( XLineStyleItem( drawing::LineStyle_NONE ) );
+ }
+
+ // #i68523# special handling for Svx3DCharacterModeItem, this is not saved
+ // but needs to be sal_True in svx, pool default (false) in sch. Since sch
+ // does not load lathe or extrude objects, it is possible to set the items
+ // here.
+ // For other solution possibilities, see task description.
+ if( dynamic_cast<const E3dLatheObj* >(GetSdrObject()) != nullptr|| dynamic_cast<const E3dExtrudeObj* >(GetSdrObject()) != nullptr)
+ {
+ GetSdrObject()->SetMergedItem(Svx3DCharacterModeItem(true));
+ }
+
+ GetSdrObject()->getSdrModelFromSdrObject().SetChanged();
+}
+
+void SvxShape::setPropertiesToDefault(
+ const uno::Sequence<OUString>& aPropertyNames )
+{
+ for ( const auto& rPropertyName : aPropertyNames )
+ setPropertyToDefault( rPropertyName );
+}
+
+uno::Sequence<uno::Any> SvxShape::getPropertyDefaults(
+ const uno::Sequence<OUString>& aPropertyNames )
+{
+ ::std::vector<uno::Any> ret;
+ ret.reserve(aPropertyNames.getLength());
+ std::transform(aPropertyNames.begin(), aPropertyNames.end(), std::back_inserter(ret),
+ [this](const OUString& rName) -> uno::Any { return getPropertyDefault(rName); });
+ return uno::Sequence<uno::Any>( ret.data(), ret.size() );
+}
+
+
+// XServiceInfo
+
+OUString SAL_CALL SvxShape::getImplementationName()
+{
+ return "SvxShape";
+}
+
+constexpr OUStringLiteral sUNO_service_style_ParagraphProperties = u"com.sun.star.style.ParagraphProperties";
+constexpr OUStringLiteral sUNO_service_style_ParagraphPropertiesComplex = u"com.sun.star.style.ParagraphPropertiesComplex";
+constexpr OUStringLiteral sUNO_service_style_ParagraphPropertiesAsian = u"com.sun.star.style.ParagraphPropertiesAsian";
+constexpr OUStringLiteral sUNO_service_style_CharacterProperties = u"com.sun.star.style.CharacterProperties";
+constexpr OUStringLiteral sUNO_service_style_CharacterPropertiesComplex = u"com.sun.star.style.CharacterPropertiesComplex";
+constexpr OUStringLiteral sUNO_service_style_CharacterPropertiesAsian = u"com.sun.star.style.CharacterPropertiesAsian";
+
+constexpr OUStringLiteral sUNO_service_drawing_FillProperties = u"com.sun.star.drawing.FillProperties";
+constexpr OUStringLiteral sUNO_service_drawing_TextProperties = u"com.sun.star.drawing.TextProperties";
+constexpr OUStringLiteral sUNO_service_drawing_LineProperties = u"com.sun.star.drawing.LineProperties";
+constexpr OUStringLiteral sUNO_service_drawing_ConnectorProperties = u"com.sun.star.drawing.ConnectorProperties";
+constexpr OUStringLiteral sUNO_service_drawing_MeasureProperties = u"com.sun.star.drawing.MeasureProperties";
+constexpr OUStringLiteral sUNO_service_drawing_ShadowProperties = u"com.sun.star.drawing.ShadowProperties";
+
+constexpr OUStringLiteral sUNO_service_drawing_RotationDescriptor = u"com.sun.star.drawing.RotationDescriptor";
+
+constexpr OUStringLiteral sUNO_service_drawing_Text = u"com.sun.star.drawing.Text";
+constexpr OUStringLiteral sUNO_service_drawing_GroupShape = u"com.sun.star.drawing.GroupShape";
+
+constexpr OUStringLiteral sUNO_service_drawing_CustomShapeProperties = u"com.sun.star.drawing.CustomShapeProperties";
+constexpr OUStringLiteral sUNO_service_drawing_CustomShape = u"com.sun.star.drawing.CustomShape";
+
+constexpr OUStringLiteral sUNO_service_drawing_PolyPolygonDescriptor = u"com.sun.star.drawing.PolyPolygonDescriptor";
+constexpr OUStringLiteral sUNO_service_drawing_PolyPolygonBezierDescriptor= u"com.sun.star.drawing.PolyPolygonBezierDescriptor";
+
+constexpr OUStringLiteral sUNO_service_drawing_LineShape = u"com.sun.star.drawing.LineShape";
+constexpr OUStringLiteral sUNO_service_drawing_Shape = u"com.sun.star.drawing.Shape";
+constexpr OUStringLiteral sUNO_service_drawing_RectangleShape = u"com.sun.star.drawing.RectangleShape";
+constexpr OUStringLiteral sUNO_service_drawing_EllipseShape = u"com.sun.star.drawing.EllipseShape";
+constexpr OUStringLiteral sUNO_service_drawing_PolyPolygonShape = u"com.sun.star.drawing.PolyPolygonShape";
+constexpr OUStringLiteral sUNO_service_drawing_PolyLineShape = u"com.sun.star.drawing.PolyLineShape";
+constexpr OUStringLiteral sUNO_service_drawing_OpenBezierShape = u"com.sun.star.drawing.OpenBezierShape";
+constexpr OUStringLiteral sUNO_service_drawing_ClosedBezierShape = u"com.sun.star.drawing.ClosedBezierShape";
+constexpr OUStringLiteral sUNO_service_drawing_TextShape = u"com.sun.star.drawing.TextShape";
+constexpr OUStringLiteral sUNO_service_drawing_GraphicObjectShape = u"com.sun.star.drawing.GraphicObjectShape";
+constexpr OUStringLiteral sUNO_service_drawing_OLE2Shape = u"com.sun.star.drawing.OLE2Shape";
+constexpr OUStringLiteral sUNO_service_drawing_PageShape = u"com.sun.star.drawing.PageShape";
+constexpr OUStringLiteral sUNO_service_drawing_CaptionShape = u"com.sun.star.drawing.CaptionShape";
+constexpr OUStringLiteral sUNO_service_drawing_MeasureShape = u"com.sun.star.drawing.MeasureShape";
+constexpr OUStringLiteral sUNO_service_drawing_FrameShape = u"com.sun.star.drawing.FrameShape";
+constexpr OUStringLiteral sUNO_service_drawing_ControlShape = u"com.sun.star.drawing.ControlShape";
+constexpr OUStringLiteral sUNO_service_drawing_ConnectorShape = u"com.sun.star.drawing.ConnectorShape";
+constexpr OUStringLiteral sUNO_service_drawing_MediaShape = u"com.sun.star.drawing.MediaShape";
+
+
+uno::Sequence< OUString > SAL_CALL SvxShape::getSupportedServiceNames()
+{
+ if( mpImpl->mpMaster )
+ {
+ return mpImpl->mpMaster->getSupportedServiceNames();
+ }
+ else
+ {
+ return _getSupportedServiceNames();
+ }
+}
+
+uno::Sequence< OUString > SvxShape::_getSupportedServiceNames()
+{
+ ::SolarMutexGuard aGuard;
+
+ if( HasSdrObject() && GetSdrObject()->GetObjInventor() == SdrInventor::Default)
+ {
+ const SdrObjKind nIdent = GetSdrObject()->GetObjIdentifier();
+
+ switch(nIdent)
+ {
+ case SdrObjKind::Group:
+ {
+ static const uno::Sequence<OUString> aSvxShape_GroupServices
+ = { sUNO_service_drawing_GroupShape,
+ sUNO_service_drawing_Shape };
+ return aSvxShape_GroupServices;
+ }
+ case SdrObjKind::CustomShape:
+ {
+ static const uno::Sequence<OUString> aSvxShape_CustomShapeServices
+ = { sUNO_service_drawing_CustomShape,
+ sUNO_service_drawing_Shape,
+ sUNO_service_drawing_CustomShapeProperties,
+ sUNO_service_drawing_FillProperties,
+ sUNO_service_drawing_LineProperties,
+ sUNO_service_drawing_Text,
+ sUNO_service_drawing_TextProperties,
+ sUNO_service_style_ParagraphProperties,
+ sUNO_service_style_ParagraphPropertiesComplex,
+ sUNO_service_style_ParagraphPropertiesAsian,
+ sUNO_service_style_CharacterProperties,
+ sUNO_service_style_CharacterPropertiesComplex,
+ sUNO_service_style_CharacterPropertiesAsian,
+ sUNO_service_drawing_ShadowProperties,
+ sUNO_service_drawing_RotationDescriptor };
+ return aSvxShape_CustomShapeServices;
+ }
+ case SdrObjKind::Line:
+ {
+ static const uno::Sequence<OUString> aSvxShape_LineServices
+ = { sUNO_service_drawing_LineShape,
+
+ sUNO_service_drawing_Shape,
+ sUNO_service_drawing_LineProperties,
+
+ sUNO_service_drawing_Text,
+ sUNO_service_drawing_TextProperties,
+ sUNO_service_style_ParagraphProperties,
+ sUNO_service_style_ParagraphPropertiesComplex,
+ sUNO_service_style_ParagraphPropertiesAsian,
+ sUNO_service_style_CharacterProperties,
+ sUNO_service_style_CharacterPropertiesComplex,
+ sUNO_service_style_CharacterPropertiesAsian,
+
+ sUNO_service_drawing_PolyPolygonDescriptor,
+ sUNO_service_drawing_ShadowProperties,
+ sUNO_service_drawing_RotationDescriptor };
+ return aSvxShape_LineServices;
+ }
+
+ case SdrObjKind::Rectangle:
+ {
+ static const uno::Sequence<OUString> aSvxShape_RectServices
+ = { sUNO_service_drawing_RectangleShape,
+
+ sUNO_service_drawing_Shape,
+ sUNO_service_drawing_FillProperties,
+ sUNO_service_drawing_LineProperties,
+ sUNO_service_drawing_Text,
+ sUNO_service_drawing_TextProperties,
+ sUNO_service_style_ParagraphProperties,
+ sUNO_service_style_ParagraphPropertiesComplex,
+ sUNO_service_style_ParagraphPropertiesAsian,
+ sUNO_service_style_CharacterProperties,
+ sUNO_service_style_CharacterPropertiesComplex,
+ sUNO_service_style_CharacterPropertiesAsian,
+
+ sUNO_service_drawing_ShadowProperties,
+ sUNO_service_drawing_RotationDescriptor };
+ return aSvxShape_RectServices;
+ }
+
+ case SdrObjKind::CircleOrEllipse:
+ case SdrObjKind::CircleSection:
+ case SdrObjKind::CircleArc:
+ case SdrObjKind::CircleCut:
+ {
+ static const uno::Sequence<OUString> aSvxShape_CircServices
+ = { sUNO_service_drawing_EllipseShape,
+
+ sUNO_service_drawing_Shape,
+ sUNO_service_drawing_FillProperties,
+ sUNO_service_drawing_LineProperties,
+
+ sUNO_service_drawing_Text,
+ sUNO_service_drawing_TextProperties,
+ sUNO_service_style_ParagraphProperties,
+ sUNO_service_style_ParagraphPropertiesComplex,
+ sUNO_service_style_ParagraphPropertiesAsian,
+ sUNO_service_style_CharacterProperties,
+ sUNO_service_style_CharacterPropertiesComplex,
+ sUNO_service_style_CharacterPropertiesAsian,
+
+ sUNO_service_drawing_ShadowProperties,
+ sUNO_service_drawing_RotationDescriptor };
+ return aSvxShape_CircServices;
+ }
+
+ case SdrObjKind::PathPolyLine:
+ case SdrObjKind::PolyLine:
+ {
+ static const uno::Sequence<OUString> aSvxShape_PathServices
+ = { sUNO_service_drawing_PolyLineShape,
+
+ sUNO_service_drawing_Shape,
+ sUNO_service_drawing_LineProperties,
+
+ sUNO_service_drawing_PolyPolygonDescriptor,
+
+ sUNO_service_drawing_Text,
+ sUNO_service_drawing_TextProperties,
+ sUNO_service_style_ParagraphProperties,
+ sUNO_service_style_ParagraphPropertiesComplex,
+ sUNO_service_style_ParagraphPropertiesAsian,
+ sUNO_service_style_CharacterProperties,
+ sUNO_service_style_CharacterPropertiesComplex,
+ sUNO_service_style_CharacterPropertiesAsian,
+
+ sUNO_service_drawing_ShadowProperties,
+ sUNO_service_drawing_RotationDescriptor };
+ return aSvxShape_PathServices;
+ }
+
+ case SdrObjKind::PathPoly:
+ case SdrObjKind::Polygon:
+ {
+ static const uno::Sequence<OUString> aSvxShape_PolyServices
+ = { sUNO_service_drawing_PolyPolygonShape,
+
+ sUNO_service_drawing_Shape,
+ sUNO_service_drawing_LineProperties,
+ sUNO_service_drawing_FillProperties,
+
+ sUNO_service_drawing_PolyPolygonDescriptor,
+
+ sUNO_service_drawing_Text,
+ sUNO_service_drawing_TextProperties,
+ sUNO_service_style_ParagraphProperties,
+ sUNO_service_style_ParagraphPropertiesComplex,
+ sUNO_service_style_ParagraphPropertiesAsian,
+ sUNO_service_style_CharacterProperties,
+ sUNO_service_style_CharacterPropertiesComplex,
+ sUNO_service_style_CharacterPropertiesAsian,
+
+ sUNO_service_drawing_ShadowProperties,
+ sUNO_service_drawing_RotationDescriptor };
+ return aSvxShape_PolyServices;
+ }
+
+ case SdrObjKind::FreehandLine:
+ case SdrObjKind::PathLine:
+ {
+ static const uno::Sequence<OUString> aSvxShape_FreeLineServices
+ = { sUNO_service_drawing_OpenBezierShape,
+
+ sUNO_service_drawing_Shape,
+ sUNO_service_drawing_LineProperties,
+ sUNO_service_drawing_FillProperties,
+
+ sUNO_service_drawing_PolyPolygonBezierDescriptor,
+
+ sUNO_service_drawing_Text,
+ sUNO_service_drawing_TextProperties,
+ sUNO_service_style_ParagraphProperties,
+ sUNO_service_style_ParagraphPropertiesComplex,
+ sUNO_service_style_ParagraphPropertiesAsian,
+ sUNO_service_style_CharacterProperties,
+ sUNO_service_style_CharacterPropertiesComplex,
+ sUNO_service_style_CharacterPropertiesAsian,
+
+ sUNO_service_drawing_ShadowProperties,
+ sUNO_service_drawing_RotationDescriptor };
+ return aSvxShape_FreeLineServices;
+ }
+
+ case SdrObjKind::FreehandFill:
+ case SdrObjKind::PathFill:
+ {
+ static const uno::Sequence<OUString> aSvxShape_FreeFillServices
+ = { sUNO_service_drawing_ClosedBezierShape,
+
+ sUNO_service_drawing_Shape,
+ sUNO_service_drawing_LineProperties,
+ sUNO_service_drawing_FillProperties,
+
+ sUNO_service_drawing_PolyPolygonBezierDescriptor,
+
+ sUNO_service_drawing_Text,
+ sUNO_service_drawing_TextProperties,
+ sUNO_service_style_ParagraphProperties,
+ sUNO_service_style_ParagraphPropertiesComplex,
+ sUNO_service_style_ParagraphPropertiesAsian,
+ sUNO_service_style_CharacterProperties,
+ sUNO_service_style_CharacterPropertiesComplex,
+ sUNO_service_style_CharacterPropertiesAsian,
+
+ sUNO_service_drawing_ShadowProperties,
+ sUNO_service_drawing_RotationDescriptor };
+ return aSvxShape_FreeFillServices;
+ }
+
+ case SdrObjKind::OutlineText:
+ case SdrObjKind::TitleText:
+ case SdrObjKind::Text:
+ {
+ static const uno::Sequence<OUString> aSvxShape_TextServices
+ = { sUNO_service_drawing_TextShape,
+
+ sUNO_service_drawing_Shape,
+ sUNO_service_drawing_FillProperties,
+ sUNO_service_drawing_LineProperties,
+
+ sUNO_service_drawing_Text,
+ sUNO_service_drawing_TextProperties,
+ sUNO_service_style_ParagraphProperties,
+ sUNO_service_style_ParagraphPropertiesComplex,
+ sUNO_service_style_ParagraphPropertiesAsian,
+ sUNO_service_style_CharacterProperties,
+ sUNO_service_style_CharacterPropertiesComplex,
+ sUNO_service_style_CharacterPropertiesAsian,
+
+ sUNO_service_drawing_ShadowProperties,
+ sUNO_service_drawing_RotationDescriptor };
+ return aSvxShape_TextServices;
+ }
+
+ case SdrObjKind::Graphic:
+ {
+ static const uno::Sequence<OUString> aSvxShape_GrafServices
+ = { sUNO_service_drawing_GraphicObjectShape,
+
+ sUNO_service_drawing_Shape,
+
+ sUNO_service_drawing_Text,
+ sUNO_service_drawing_TextProperties,
+ sUNO_service_style_ParagraphProperties,
+ sUNO_service_style_ParagraphPropertiesComplex,
+ sUNO_service_style_ParagraphPropertiesAsian,
+ sUNO_service_style_CharacterProperties,
+ sUNO_service_style_CharacterPropertiesComplex,
+ sUNO_service_style_CharacterPropertiesAsian,
+
+ sUNO_service_drawing_ShadowProperties,
+ sUNO_service_drawing_RotationDescriptor};
+ return aSvxShape_GrafServices;
+ }
+
+ case SdrObjKind::OLE2:
+ {
+ static const uno::Sequence<OUString> aSvxShape_Ole2Services
+ = { sUNO_service_drawing_OLE2Shape,
+ sUNO_service_drawing_Shape,
+
+ // #i118485# Added Text, Shadow and Rotation
+ sUNO_service_drawing_Text,
+ sUNO_service_drawing_TextProperties,
+ sUNO_service_style_ParagraphProperties,
+ sUNO_service_style_ParagraphPropertiesComplex,
+ sUNO_service_style_ParagraphPropertiesAsian,
+ sUNO_service_style_CharacterProperties,
+ sUNO_service_style_CharacterPropertiesComplex,
+ sUNO_service_style_CharacterPropertiesAsian,
+
+ sUNO_service_drawing_ShadowProperties,
+ sUNO_service_drawing_RotationDescriptor };
+ return aSvxShape_Ole2Services;
+ }
+
+ case SdrObjKind::Caption:
+ {
+ static const uno::Sequence<OUString> aSvxShape_CaptionServices
+ = { sUNO_service_drawing_CaptionShape,
+
+ sUNO_service_drawing_Shape,
+ sUNO_service_drawing_FillProperties,
+ sUNO_service_drawing_LineProperties,
+
+ sUNO_service_drawing_Text,
+ sUNO_service_drawing_TextProperties,
+ sUNO_service_style_ParagraphProperties,
+ sUNO_service_style_ParagraphPropertiesComplex,
+ sUNO_service_style_ParagraphPropertiesAsian,
+ sUNO_service_style_CharacterProperties,
+ sUNO_service_style_CharacterPropertiesComplex,
+ sUNO_service_style_CharacterPropertiesAsian,
+
+ sUNO_service_drawing_ShadowProperties,
+ sUNO_service_drawing_RotationDescriptor };
+ return aSvxShape_CaptionServices;
+ }
+
+ case SdrObjKind::Page:
+ {
+ static const uno::Sequence<OUString> aSvxShape_PageServices
+ = { sUNO_service_drawing_PageShape,
+ sUNO_service_drawing_Shape };
+ return aSvxShape_PageServices;
+ }
+
+ case SdrObjKind::Measure:
+ {
+ static const uno::Sequence<OUString> aSvxShape_MeasureServices
+ = { sUNO_service_drawing_MeasureShape,
+
+ sUNO_service_drawing_MeasureProperties,
+
+ sUNO_service_drawing_Shape,
+ sUNO_service_drawing_LineProperties,
+
+ sUNO_service_drawing_Text,
+ sUNO_service_drawing_TextProperties,
+ sUNO_service_style_ParagraphProperties,
+ sUNO_service_style_ParagraphPropertiesComplex,
+ sUNO_service_style_ParagraphPropertiesAsian,
+ sUNO_service_style_CharacterProperties,
+ sUNO_service_style_CharacterPropertiesComplex,
+ sUNO_service_style_CharacterPropertiesAsian,
+
+ sUNO_service_drawing_PolyPolygonDescriptor,
+ sUNO_service_drawing_ShadowProperties,
+ sUNO_service_drawing_RotationDescriptor };
+ return aSvxShape_MeasureServices;
+ }
+
+ case SdrObjKind::OLEPluginFrame:
+ {
+ static const uno::Sequence<OUString> aSvxShape_FrameServices
+ = { sUNO_service_drawing_FrameShape,
+ sUNO_service_drawing_Shape };
+ return aSvxShape_FrameServices;
+ }
+
+ case SdrObjKind::UNO:
+ {
+ static const uno::Sequence<OUString> aSvxShape_UnoServices
+ = { sUNO_service_drawing_ControlShape,
+ sUNO_service_drawing_Shape };
+ return aSvxShape_UnoServices;
+ }
+
+ case SdrObjKind::Edge:
+ {
+ static const uno::Sequence<OUString> aSvxShape_EdgeServices
+ = { sUNO_service_drawing_ConnectorShape,
+ sUNO_service_drawing_ConnectorProperties,
+
+ sUNO_service_drawing_Shape,
+ sUNO_service_drawing_LineProperties,
+
+ sUNO_service_drawing_Text,
+ sUNO_service_drawing_TextProperties,
+ sUNO_service_style_ParagraphProperties,
+ sUNO_service_style_ParagraphPropertiesComplex,
+ sUNO_service_style_ParagraphPropertiesAsian,
+ sUNO_service_style_CharacterProperties,
+ sUNO_service_style_CharacterPropertiesComplex,
+ sUNO_service_style_CharacterPropertiesAsian,
+
+ sUNO_service_drawing_PolyPolygonDescriptor,
+ sUNO_service_drawing_ShadowProperties,
+ sUNO_service_drawing_RotationDescriptor };
+ return aSvxShape_EdgeServices;
+ }
+ case SdrObjKind::Media:
+ {
+ static const uno::Sequence<OUString> aSvxShape_MediaServices
+ = { sUNO_service_drawing_MediaShape,
+ sUNO_service_drawing_Shape };
+ return aSvxShape_MediaServices;
+ }
+ default: ;
+ }
+ }
+ else if( HasSdrObject() && GetSdrObject()->GetObjInventor() == SdrInventor::FmForm)
+ {
+#if OSL_DEBUG_LEVEL > 0
+ const SdrObjKind nIdent = GetSdrObject()->GetObjIdentifier();
+ OSL_ENSURE( nIdent == SdrObjKind::UNO, "SvxShape::_getSupportedServiceNames: SdrInventor::FmForm, but no UNO object?" );
+#endif
+ static const uno::Sequence<OUString> aSvxShape_UnoServices
+ = { sUNO_service_drawing_ControlShape,
+ sUNO_service_drawing_Shape };
+ return aSvxShape_UnoServices;
+ }
+ OSL_FAIL( "SvxShape::_getSupportedServiceNames: could not determine object type!" );
+ uno::Sequence< OUString > aSeq;
+ return aSeq;
+}
+
+sal_Bool SAL_CALL SvxShape::supportsService( const OUString& ServiceName )
+{
+ return cppu::supportsService(this, ServiceName);
+}
+
+// XGluePointsSupplier
+uno::Reference< container::XIndexContainer > SAL_CALL SvxShape::getGluePoints()
+{
+ ::SolarMutexGuard aGuard;
+ uno::Reference< container::XIndexContainer > xGluePoints( mxGluePoints );
+
+ if( HasSdrObject() && !xGluePoints.is() )
+ {
+ uno::Reference< container::XIndexContainer > xNew( SvxUnoGluePointAccess_createInstance( GetSdrObject() ), uno::UNO_QUERY );
+ mxGluePoints = xGluePoints = xNew;
+ }
+
+ return xGluePoints;
+}
+
+// XChild
+uno::Reference<uno::XInterface> SAL_CALL SvxShape::getParent()
+{
+ ::SolarMutexGuard aGuard;
+ const SdrObject* pSdrObject(GetSdrObject());
+
+ if(nullptr != pSdrObject)
+ {
+ const SdrObjList* pParentSdrObjList(GetSdrObject()->getParentSdrObjListFromSdrObject());
+
+ if(nullptr != pParentSdrObjList)
+ {
+ // SdrObject is member of a SdrObjList. That may be a SdrObject
+ // (SdrObjGroup or E3dScene) or a SdrPage.
+ // Check for SdrObject first - using getSdrPageFromSdrObjList
+ // *will* get the SdrPage even when the SdrObject is deep buried
+ // in a construct of SdrObjGroup.
+ // We want to ask for the direct parent here...
+ SdrObject* pParentSdrObject(pParentSdrObjList->getSdrObjectFromSdrObjList());
+
+ if(nullptr != pParentSdrObject)
+ {
+ // SdrObject is member of a SdrObject-based Group (SdrObjGroup or E3dScene).
+ return pParentSdrObject->getUnoShape();
+ }
+ else
+ {
+ SdrPage* pParentSdrPage(pParentSdrObjList->getSdrPageFromSdrObjList());
+
+ if(nullptr != pParentSdrPage)
+ {
+ // SdrObject is inserted to a SdrPage. Since
+ // we checked for getSdrObjectFromSdrObjList first,
+ // we can even say that it is directly member of that
+ // SdrPage.
+ return pParentSdrPage->getUnoPage();
+ }
+ }
+
+ // not member of any SdrObjList, no parent
+ OSL_FAIL( "SvxShape::getParent( ): unexpected Parent SdrObjList" );
+ }
+ }
+
+ // no SdrObject, no parent
+ return uno::Reference<uno::XInterface>();
+}
+
+void SAL_CALL SvxShape::setParent( const css::uno::Reference< css::uno::XInterface >& )
+{
+ throw lang::NoSupportException();
+}
+
+
+/** called from the XActionLockable interface methods on initial locking */
+void SvxShape::lock()
+{
+}
+
+
+/** called from the XActionLockable interface methods on final unlock */
+void SvxShape::unlock()
+{
+}
+
+
+// XActionLockable
+sal_Bool SAL_CALL SvxShape::isActionLocked( )
+{
+ ::SolarMutexGuard aGuard;
+
+ return mnLockCount != 0;
+}
+
+
+void SAL_CALL SvxShape::addActionLock( )
+{
+ ::SolarMutexGuard aGuard;
+
+ DBG_ASSERT( mnLockCount < 0xffff, "lock overflow in SvxShape!" );
+ mnLockCount++;
+
+ if( mnLockCount == 1 )
+ lock();
+}
+
+
+void SAL_CALL SvxShape::removeActionLock( )
+{
+ ::SolarMutexGuard aGuard;
+
+ DBG_ASSERT( mnLockCount > 0, "lock underflow in SvxShape!" );
+ mnLockCount--;
+
+ if( mnLockCount == 0 )
+ unlock();
+}
+
+
+void SAL_CALL SvxShape::setActionLocks( sal_Int16 nLock )
+{
+ ::SolarMutexGuard aGuard;
+
+ if( (mnLockCount == 0) && (nLock != 0) )
+ unlock();
+
+ if( (mnLockCount != 0) && (nLock == 0) )
+ lock();
+
+ mnLockCount = static_cast<sal_uInt16>(nLock);
+}
+
+
+sal_Int16 SAL_CALL SvxShape::resetActionLocks( )
+{
+ ::SolarMutexGuard aGuard;
+
+ if( mnLockCount != 0 )
+ unlock();
+
+ sal_Int16 nOldLocks = static_cast<sal_Int16>(mnLockCount);
+ mnLockCount = 0;
+
+ return nOldLocks;
+}
+
+
+/** since polygon shapes can change their kind during editing, we have
+ to recheck it here.
+ Circle shapes also change their kind, but they are all treated equal
+ so no update is necessary.
+*/
+void SvxShape::updateShapeKind()
+{
+ switch( mpImpl->mnObjId )
+ {
+ case SdrObjKind::Line:
+ case SdrObjKind::Polygon:
+ case SdrObjKind::PolyLine:
+ case SdrObjKind::PathLine:
+ case SdrObjKind::PathFill:
+ case SdrObjKind::FreehandLine:
+ case SdrObjKind::FreehandFill:
+ case SdrObjKind::PathPoly:
+ case SdrObjKind::PathPolyLine:
+ {
+ const SdrObjKind nId = GetSdrObject()->GetObjIdentifier();
+
+ if( nId != mpImpl->mnObjId )
+ {
+ mpImpl->mnObjId = nId;
+
+ }
+ break;
+ }
+ default: ;
+ }
+}
+
+SvxShapeText::SvxShapeText(SdrObject* pObject)
+: SvxShape( pObject, getSvxMapProvider().GetMap(SVXMAP_TEXT), getSvxMapProvider().GetPropertySet(SVXMAP_TEXT, SdrObject::GetGlobalDrawObjectItemPool()) ), SvxUnoTextBase( ImplGetSvxUnoOutlinerTextCursorSvxPropertySet() )
+{
+ if( pObject )
+ SetEditSource( new SvxTextEditSource( pObject, nullptr ) );
+}
+
+
+SvxShapeText::SvxShapeText(SdrObject* pObject, const SfxItemPropertyMapEntry* pPropertyMap, const SvxItemPropertySet* pPropertySet)
+: SvxShape( pObject, pPropertyMap, pPropertySet ), SvxUnoTextBase( ImplGetSvxUnoOutlinerTextCursorSvxPropertySet() )
+{
+ if( pObject )
+ SetEditSource( new SvxTextEditSource( pObject, nullptr ) );
+}
+
+
+SvxShapeText::~SvxShapeText() noexcept
+{
+ // check if only this instance is registered at the ranges
+ DBG_ASSERT( (nullptr == GetEditSource()) || (GetEditSource()->getRanges().size()==1),
+ "svx::SvxShapeText::~SvxShapeText(), text shape with living text ranges destroyed!");
+}
+
+void SvxShapeText::Create( SdrObject* pNewObj, SvxDrawPage* pNewPage )
+{
+ if( pNewObj && (nullptr == GetEditSource()))
+ SetEditSource( new SvxTextEditSource( pNewObj, nullptr ) );
+ SvxShape::Create( pNewObj, pNewPage );
+}
+
+// XInterface
+
+uno::Any SAL_CALL SvxShapeText::queryInterface( const uno::Type & rType )
+{
+ return SvxShape::queryInterface( rType );
+}
+
+
+uno::Any SAL_CALL SvxShapeText::queryAggregation( const uno::Type & rType )
+{
+ uno::Any aAny( SvxShape::queryAggregation( rType ) );
+ if( aAny.hasValue() )
+ return aAny;
+
+ return SvxUnoTextBase::queryAggregation( rType );
+}
+
+
+// XServiceInfo
+
+OUString SAL_CALL SvxShapeText::getImplementationName()
+{
+ return "SvxShapeText";
+}
+
+
+uno::Sequence< OUString > SAL_CALL SvxShapeText::getSupportedServiceNames()
+{
+ return SvxShape::getSupportedServiceNames();
+}
+
+
+sal_Bool SAL_CALL SvxShapeText::supportsService( const OUString& ServiceName )
+{
+ return cppu::supportsService(static_cast<SvxShape*>(this), ServiceName);
+}
+
+ // XTypeProvider
+
+uno::Sequence< uno::Type > SAL_CALL SvxShapeText::getTypes()
+{
+ return SvxShape::getTypes();
+}
+
+sal_Int64 SAL_CALL SvxShapeText::getSomething( const css::uno::Sequence< sal_Int8 >& rId )
+{
+ const sal_Int64 nReturn = SvxShape::getSomething( rId );
+ if( nReturn )
+ return nReturn;
+
+ return SvxUnoTextBase::getSomething( rId );
+}
+
+
+uno::Sequence< sal_Int8 > SAL_CALL SvxShapeText::getImplementationId()
+{
+ return css::uno::Sequence<sal_Int8>();
+}
+
+
+/** called from the XActionLockable interface methods on initial locking */
+void SvxShapeText::lock()
+{
+ SvxTextEditSource* pEditSource = static_cast<SvxTextEditSource*>(GetEditSource());
+ if( pEditSource )
+ pEditSource->lock();
+}
+
+
+/** called from the XActionLockable interface methods on final unlock */
+void SvxShapeText::unlock()
+{
+ SvxTextEditSource* pEditSource = static_cast<SvxTextEditSource*>(GetEditSource());
+ if( pEditSource )
+ pEditSource->unlock();
+}
+
+// css::text::XTextRange
+uno::Reference< text::XTextRange > SAL_CALL SvxShapeText::getStart()
+{
+ ::SolarMutexGuard aGuard;
+ SvxTextForwarder* pForwarder = mpEditSource ? mpEditSource->GetTextForwarder() : nullptr;
+ if( pForwarder )
+ ::GetSelection( maSelection, pForwarder );
+ return SvxUnoTextBase::getStart();
+
+}
+
+uno::Reference< text::XTextRange > SAL_CALL SvxShapeText::getEnd()
+{
+ ::SolarMutexGuard aGuard;
+ SvxTextForwarder* pForwarder = mpEditSource ? mpEditSource->GetTextForwarder() : nullptr;
+ if( pForwarder )
+ ::GetSelection( maSelection, pForwarder );
+ return SvxUnoTextBase::getEnd();
+}
+
+OUString SAL_CALL SvxShapeText::getString()
+{
+ ::SolarMutexGuard aGuard;
+ SvxTextForwarder* pForwarder = mpEditSource ? mpEditSource->GetTextForwarder() : nullptr;
+ if( pForwarder )
+ ::GetSelection( maSelection, pForwarder );
+ return SvxUnoTextBase::getString();
+}
+
+
+void SAL_CALL SvxShapeText::setString( const OUString& aString )
+{
+ ::SolarMutexGuard aGuard;
+ SvxTextForwarder* pForwarder = mpEditSource ? mpEditSource->GetTextForwarder() : nullptr;
+ if( pForwarder )
+ ::GetSelection( maSelection, pForwarder );
+ SvxUnoTextBase::setString( aString );
+}
+
+// override these for special property handling in subcasses. Return true if property is handled
+bool SvxShapeText::setPropertyValueImpl( const OUString& rName, const SfxItemPropertyMapEntry* pProperty, const css::uno::Any& rValue )
+{
+ // HACK-fix #99090#
+ // since SdrTextObj::SetVerticalWriting exchanges
+ // SDRATTR_TEXT_AUTOGROWWIDTH and SDRATTR_TEXT_AUTOGROWHEIGHT,
+ // we have to set the textdirection here
+
+ if( pProperty->nWID == SDRATTR_TEXTDIRECTION )
+ {
+ SdrTextObj* pTextObj = dynamic_cast< SdrTextObj* >( GetSdrObject() );
+ if( pTextObj )
+ {
+ css::text::WritingMode eMode;
+ if( rValue >>= eMode )
+ {
+ pTextObj->SetVerticalWriting( eMode == css::text::WritingMode_TB_RL );
+ }
+ }
+ return true;
+ }
+ return SvxShape::setPropertyValueImpl( rName, pProperty, rValue );
+}
+
+bool SvxShapeText::getPropertyValueImpl( const OUString& rName, const SfxItemPropertyMapEntry* pProperty, css::uno::Any& rValue )
+{
+ if( pProperty->nWID == SDRATTR_TEXTDIRECTION )
+ {
+ SdrTextObj* pTextObj = dynamic_cast< SdrTextObj* >( GetSdrObject() );
+ if( pTextObj && pTextObj->IsVerticalWriting() )
+ rValue <<= css::text::WritingMode_TB_RL;
+ else
+ rValue <<= css::text::WritingMode_LR_TB;
+ return true;
+ }
+
+ return SvxShape::getPropertyValueImpl( rName, pProperty, rValue );
+}
+
+bool SvxShapeText::getPropertyStateImpl( const SfxItemPropertyMapEntry* pProperty, css::beans::PropertyState& rState )
+{
+ return SvxShape::getPropertyStateImpl( pProperty, rState );
+}
+
+bool SvxShapeText::setPropertyToDefaultImpl( const SfxItemPropertyMapEntry* pProperty )
+{
+ return SvxShape::setPropertyToDefaultImpl( pProperty );
+}
+
+SvxShapeRect::SvxShapeRect(SdrObject* pObj)
+: SvxShapeText( pObj, getSvxMapProvider().GetMap(SVXMAP_SHAPE), getSvxMapProvider().GetPropertySet(SVXMAP_SHAPE, SdrObject::GetGlobalDrawObjectItemPool()))
+{
+}
+
+SvxShapeRect::~SvxShapeRect() noexcept
+{
+}
+
+uno::Any SAL_CALL SvxShapeRect::queryInterface( const uno::Type & rType )
+{
+ return SvxShapeText::queryInterface( rType );
+}
+
+uno::Any SAL_CALL SvxShapeRect::queryAggregation( const uno::Type & rType )
+{
+ return SvxShapeText::queryAggregation( rType );
+}
+
+// XServiceInfo
+
+uno::Sequence< OUString > SvxShapeRect::getSupportedServiceNames()
+{
+ return SvxShape::getSupportedServiceNames();
+}
+
+/** returns a StarOffice API wrapper for the given SdrObject */
+uno::Reference< drawing::XShape > GetXShapeForSdrObject( SdrObject* pObj ) noexcept
+{
+ uno::Reference< drawing::XShape > xShape( pObj->getUnoShape(), uno::UNO_QUERY );
+ return xShape;
+}
+
+
+SdrObject* SdrObject::getSdrObjectFromXShape( const css::uno::Reference< css::uno::XInterface >& xInt )
+{
+ SvxShape* pSvxShape = comphelper::getFromUnoTunnel<SvxShape>( xInt );
+ return pSvxShape ? pSvxShape->GetSdrObject() : nullptr;
+}
+
+uno::Any SvxItemPropertySet_getPropertyValue( const SfxItemPropertyMapEntry* pMap, const SfxItemSet& rSet )
+{
+ if(!pMap || !pMap->nWID)
+ return uno::Any();
+
+ // Check is for items that store either metric values if they are positive or percentage if they are negative.
+ bool bDontConvertNegativeValues = ( pMap->nWID == XATTR_FILLBMP_SIZEX || pMap->nWID == XATTR_FILLBMP_SIZEY );
+ return SvxItemPropertySet::getPropertyValue( pMap, rSet, (pMap->nWID != SDRATTR_XMLATTRIBUTES), bDontConvertNegativeValues );
+}
+
+void SvxItemPropertySet_setPropertyValue( const SfxItemPropertyMapEntry* pMap, const uno::Any& rVal, SfxItemSet& rSet )
+{
+ if(!pMap || !pMap->nWID)
+ return;
+
+ bool bDontConvertNegativeValues = ( pMap->nWID == XATTR_FILLBMP_SIZEX || pMap->nWID == XATTR_FILLBMP_SIZEY );
+ SvxItemPropertySet::setPropertyValue( pMap, rVal, rSet, bDontConvertNegativeValues );
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/unodraw/unoshcol.cxx b/svx/source/unodraw/unoshcol.cxx
new file mode 100644
index 000000000..105ca6523
--- /dev/null
+++ b/svx/source/unodraw/unoshcol.cxx
@@ -0,0 +1,252 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * 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/EventObject.hpp>
+#include <com/sun/star/drawing/XShapes.hpp>
+#include <com/sun/star/lang/IndexOutOfBoundsException.hpp>
+#include <com/sun/star/lang/XComponent.hpp>
+#include <com/sun/star/lang/XServiceInfo.hpp>
+#include <com/sun/star/uno/XComponentContext.hpp>
+
+#include <cppuhelper/basemutex.hxx>
+#include <cppuhelper/implbase3.hxx>
+#include <cppuhelper/interfacecontainer.hxx>
+#include <comphelper/interfacecontainer3.hxx>
+#include <cppuhelper/supportsservice.hxx>
+#include <osl/mutex.hxx>
+#include <sal/log.hxx>
+
+using namespace ::com::sun::star;
+using namespace ::com::sun::star::uno;
+
+namespace {
+
+class SvxShapeCollection :
+ public cppu::BaseMutex,
+ public cppu::WeakAggImplHelper3<drawing::XShapes, lang::XServiceInfo, lang::XComponent>
+{
+private:
+ comphelper::OInterfaceContainerHelper3<drawing::XShape> maShapeContainer;
+
+ cppu::OBroadcastHelper mrBHelper;
+
+public:
+ SvxShapeCollection() noexcept;
+
+ // XInterface
+ virtual void SAL_CALL release() noexcept override;
+
+ // XComponent
+ virtual void SAL_CALL dispose() override;
+ virtual void SAL_CALL addEventListener( const css::uno::Reference< css::lang::XEventListener >& aListener ) override;
+ virtual void SAL_CALL removeEventListener( const css::uno::Reference< css::lang::XEventListener >& aListener ) override;
+
+ // XIndexAccess
+ virtual sal_Int32 SAL_CALL getCount() override ;
+ virtual css::uno::Any SAL_CALL getByIndex( sal_Int32 Index ) override;
+
+ // XElementAccess
+ virtual css::uno::Type SAL_CALL getElementType() override;
+ virtual sal_Bool SAL_CALL hasElements() override;
+
+ // XShapes
+ virtual void SAL_CALL add( const css::uno::Reference< css::drawing::XShape >& xShape ) override;
+ virtual void SAL_CALL remove( const css::uno::Reference< css::drawing::XShape >& xShape ) 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;
+};
+
+SvxShapeCollection::SvxShapeCollection() noexcept
+: maShapeContainer( m_aMutex ), mrBHelper( m_aMutex )
+{
+}
+
+// XInterface
+void SvxShapeCollection::release() noexcept
+{
+ uno::Reference< uno::XInterface > x( xDelegator );
+ if (! x.is())
+ {
+ if (osl_atomic_decrement( &m_refCount ) == 0)
+ {
+ if (! mrBHelper.bDisposed)
+ {
+ uno::Reference< uno::XInterface > xHoldAlive( static_cast<uno::XWeak*>(this) );
+ // First dispose
+ try
+ {
+ dispose();
+ }
+ catch(css::uno::Exception&)
+ {
+ // release should not throw exceptions
+ }
+
+ // only the alive ref holds the object
+ OSL_ASSERT( m_refCount == 1 );
+ // destroy the object if xHoldAlive decrement the refcount to 0
+ return;
+ }
+ }
+ // restore the reference count
+ osl_atomic_increment( &m_refCount );
+ }
+ OWeakAggObject::release();
+}
+
+// XComponent
+void SvxShapeCollection::dispose()
+{
+ // An frequently programming error is to release the last
+ // reference to this object in the disposing message.
+ // Make it robust, hold a self Reference.
+ uno::Reference< lang::XComponent > xSelf( this );
+
+ // Guard dispose against multiple threading
+ // Remark: It is an error to call dispose more than once
+ bool bDoDispose = false;
+ {
+ osl::MutexGuard aGuard( mrBHelper.rMutex );
+ if( !mrBHelper.bDisposed && !mrBHelper.bInDispose )
+ {
+ // only one call go into this section
+ mrBHelper.bInDispose = true;
+ bDoDispose = true;
+ }
+ }
+
+ // Do not hold the mutex because we are broadcasting
+ if( bDoDispose )
+ {
+ // Create an event with this as sender
+ try
+ {
+ uno::Reference< uno::XInterface > xSource( uno::Reference< uno::XInterface >::query( static_cast<lang::XComponent *>(this) ) );
+ document::EventObject aEvt;
+ aEvt.Source = xSource;
+ // inform all listeners to release this object
+ // The listener container are automatically cleared
+ mrBHelper.aLC.disposeAndClear( aEvt );
+ maShapeContainer.clear();
+ }
+ catch(const css::uno::Exception&)
+ {
+ // catch exception and throw again but signal that
+ // the object was disposed. Dispose should be called
+ // only once.
+ mrBHelper.bDisposed = true;
+ mrBHelper.bInDispose = false;
+ throw;
+ }
+
+ // the values bDispose and bInDisposing must set in this order.
+ // No multithread call overcome the "!rBHelper.bDisposed && !rBHelper.bInDispose" guard.
+ mrBHelper.bDisposed = true;
+ mrBHelper.bInDispose = false;
+ }
+ else
+ {
+ // in a multithreaded environment, it can't be avoided, that dispose is called twice.
+ // However this condition is traced, because it MAY indicate an error.
+ SAL_INFO("svx", "dispose called twice" );
+ }
+}
+
+// XComponent
+void SAL_CALL SvxShapeCollection::addEventListener( const css::uno::Reference< css::lang::XEventListener >& aListener )
+{
+ mrBHelper.addListener( cppu::UnoType<decltype(aListener)>::get() , aListener );
+}
+
+// XComponent
+void SAL_CALL SvxShapeCollection::removeEventListener( const css::uno::Reference< css::lang::XEventListener >& aListener )
+{
+ mrBHelper.removeListener( cppu::UnoType<decltype(aListener)>::get() , aListener );
+}
+
+// XShapes
+
+void SAL_CALL SvxShapeCollection::add( const Reference< drawing::XShape >& xShape )
+{
+ maShapeContainer.addInterface( xShape );
+}
+
+
+void SAL_CALL SvxShapeCollection::remove( const uno::Reference< drawing::XShape >& xShape )
+{
+ maShapeContainer.removeInterface( xShape );
+}
+
+
+sal_Int32 SAL_CALL SvxShapeCollection::getCount()
+{
+ return maShapeContainer.getLength();
+}
+
+
+uno::Any SAL_CALL SvxShapeCollection::getByIndex( sal_Int32 Index )
+{
+ if( Index < 0 || Index >= getCount() )
+ throw lang::IndexOutOfBoundsException();
+
+ Reference<drawing::XShape> xShape = maShapeContainer.getInterface(Index);
+ return uno::Any( xShape );
+}
+
+// XElementAccess
+uno::Type SAL_CALL SvxShapeCollection::getElementType()
+{
+ return cppu::UnoType<drawing::XShape>::get();
+}
+
+sal_Bool SAL_CALL SvxShapeCollection::hasElements()
+{
+ return getCount() != 0;
+}
+
+// XServiceInfo
+OUString SAL_CALL SvxShapeCollection::getImplementationName()
+{
+ return "com.sun.star.drawing.SvxShapeCollection";
+}
+
+sal_Bool SAL_CALL SvxShapeCollection::supportsService( const OUString& ServiceName )
+{
+ return cppu::supportsService( this, ServiceName);
+}
+
+uno::Sequence< OUString > SAL_CALL SvxShapeCollection::getSupportedServiceNames()
+{
+ return { "com.sun.star.drawing.Shapes", "com.sun.star.drawing.ShapeCollection" };
+}
+
+}
+
+extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface *
+com_sun_star_drawing_SvxShapeCollection_get_implementation(
+ css::uno::XComponentContext *,
+ css::uno::Sequence<css::uno::Any> const &)
+{
+ return cppu::acquire(new SvxShapeCollection);
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/unodraw/unoshtxt.cxx b/svx/source/unodraw/unoshtxt.cxx
new file mode 100644
index 000000000..71e63997e
--- /dev/null
+++ b/svx/source/unodraw/unoshtxt.cxx
@@ -0,0 +1,1009 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * 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 <vcl/svapp.hxx>
+
+#include <svx/unoshtxt.hxx>
+#include <editeng/unoedhlp.hxx>
+#include <svl/lstner.hxx>
+#include <rtl/ref.hxx>
+#include <tools/debug.hxx>
+#include <svl/hint.hxx>
+#include <svl/style.hxx>
+#include <svx/svdmodel.hxx>
+#include <svx/svdoutl.hxx>
+#include <svx/svdobj.hxx>
+#include <svx/svdview.hxx>
+#include <editeng/outliner.hxx>
+#include <editeng/unoforou.hxx>
+#include <editeng/unoviwou.hxx>
+#include <editeng/outlobj.hxx>
+#include <svx/svdotext.hxx>
+#include <svx/svdpage.hxx>
+#include <editeng/editeng.hxx>
+
+#include <editeng/unotext.hxx>
+#include <com/sun/star/linguistic2/LinguServiceManager.hpp>
+#include <comphelper/processfactory.hxx>
+#include <svx/svdotable.hxx>
+#include <cell.hxx>
+#include <unotools/configmgr.hxx>
+
+
+// SvxTextEditSourceImpl
+
+
+/** @descr
+ <p>This class essentially provides the text and view forwarders. If
+ no SdrView is given, this class handles the UNO objects, which are
+ currently not concerned with view issues. In this case,
+ GetViewForwarder() always returns NULL and the underlying
+ EditEngine of the SvxTextForwarder is a background one (i.e. not
+ the official DrawOutliner, but one created exclusively for this
+ object, with no relation to a view).
+ </p>
+
+ <p>If a SdrView is given at construction time, the caller is
+ responsible for destroying this object when the view becomes
+ invalid (the views cannot notify). If GetViewForwarder(sal_True)
+ is called, the underlying shape is put into edit mode, the view
+ forwarder returned encapsulates the OutlinerView and the next call
+ to GetTextForwarder() yields a forwarder encapsulating the actual
+ DrawOutliner. Thus, changes on that Outliner are immediately
+ reflected on the screen. If the object leaves edit mode, the old
+ behaviour is restored.</p>
+ */
+class SvxTextEditSourceImpl : public SfxListener, public SfxBroadcaster, public sdr::ObjectUser
+{
+private:
+ oslInterlockedCount maRefCount;
+
+ SdrObject* mpObject; // TTTT could be reference (?)
+ SdrText* mpText;
+ SdrView* mpView;
+ VclPtr<const OutputDevice> mpWindow;
+ SdrModel* mpModel; // TTTT probably not needed -> use SdrModel from SdrObject (?)
+ std::unique_ptr<SdrOutliner> mpOutliner;
+ std::unique_ptr<SvxOutlinerForwarder> mpTextForwarder;
+ std::unique_ptr<SvxDrawOutlinerViewForwarder> mpViewForwarder; // if non-NULL, use GetViewModeTextForwarder text forwarder
+ css::uno::Reference< css::linguistic2::XLinguServiceManager2 > m_xLinguServiceManager;
+ Point maTextOffset;
+ bool mbDataValid;
+ bool mbIsLocked;
+ bool mbNeedsUpdate;
+ bool mbOldUndoMode;
+ bool mbForwarderIsEditMode; // have to reflect that, since ENDEDIT can happen more often
+ bool mbShapeIsEditMode; // only true, if SdrHintKind::BeginEdit was received
+ bool mbNotificationsDisabled; // prevent EditEngine/Outliner notifications (e.g. when setting up forwarder)
+ bool mbNotifyEditOutlinerSet;
+
+ SvxUnoTextRangeBaseVec mvTextRanges;
+
+ SvxTextForwarder* GetBackgroundTextForwarder();
+ SvxTextForwarder* GetEditModeTextForwarder();
+ std::unique_ptr<SvxDrawOutlinerViewForwarder> CreateViewForwarder();
+
+ void SetupOutliner();
+
+ bool HasView() const { return mpView != nullptr; }
+ bool IsEditMode() const
+ {
+ if (!mbShapeIsEditMode)
+ return false;
+ SdrTextObj* pTextObj = dynamic_cast<SdrTextObj*>( mpObject );
+ return pTextObj && pTextObj->IsTextEditActive();
+ }
+
+ void dispose();
+
+public:
+ SvxTextEditSourceImpl( SdrObject* pObject, SdrText* pText );
+ SvxTextEditSourceImpl( SdrObject& rObject, SdrText* pText, SdrView& rView, const OutputDevice& rWindow );
+ virtual ~SvxTextEditSourceImpl() override;
+
+ void acquire();
+ void release();
+
+ virtual void Notify( SfxBroadcaster& rBC, const SfxHint& rHint ) override;
+
+ SvxTextForwarder* GetTextForwarder();
+ SvxEditViewForwarder* GetEditViewForwarder( bool );
+ void UpdateData();
+
+ void addRange( SvxUnoTextRangeBase* pNewRange );
+ void removeRange( SvxUnoTextRangeBase* pOldRange );
+ const SvxUnoTextRangeBaseVec& getRanges() const { return mvTextRanges;}
+
+ void lock();
+ void unlock();
+
+ bool IsValid() const;
+
+ Point LogicToPixel( const Point&, const MapMode& rMapMode );
+ Point PixelToLogic( const Point&, const MapMode& rMapMode );
+
+ DECL_LINK( NotifyHdl, EENotify&, void );
+
+ virtual void ObjectInDestruction(const SdrObject& rObject) override;
+
+ void UpdateOutliner();
+};
+
+
+SvxTextEditSourceImpl::SvxTextEditSourceImpl( SdrObject* pObject, SdrText* pText )
+ : maRefCount ( 0 ),
+ mpObject ( pObject ),
+ mpText ( pText ),
+ mpView ( nullptr ),
+ mpWindow ( nullptr ),
+ mpModel ( pObject ? &pObject->getSdrModelFromSdrObject() : nullptr ), // TTTT should be reference
+ mbDataValid ( false ),
+ mbIsLocked ( false ),
+ mbNeedsUpdate ( false ),
+ mbOldUndoMode ( false ),
+ mbForwarderIsEditMode ( false ),
+ mbShapeIsEditMode ( false ),
+ mbNotificationsDisabled ( false ),
+ mbNotifyEditOutlinerSet ( false )
+{
+ DBG_ASSERT( mpObject, "invalid pObject!" );
+
+ if( !mpText )
+ {
+ SdrTextObj* pTextObj = dynamic_cast< SdrTextObj* >( mpObject );
+ if( pTextObj )
+ mpText = pTextObj->getText( 0 );
+ }
+
+ if( mpModel )
+ StartListening( *mpModel );
+
+ if( mpObject )
+ mpObject->AddObjectUser( *this );
+}
+
+
+SvxTextEditSourceImpl::SvxTextEditSourceImpl( SdrObject& rObject, SdrText* pText, SdrView& rView, const OutputDevice& rWindow )
+ : maRefCount ( 0 ),
+ mpObject ( &rObject ),
+ mpText ( pText ),
+ mpView ( &rView ),
+ mpWindow ( &rWindow ),
+ mpModel ( &rObject.getSdrModelFromSdrObject() ), // TTTT should be reference
+ mbDataValid ( false ),
+ mbIsLocked ( false ),
+ mbNeedsUpdate ( false ),
+ mbOldUndoMode ( false ),
+ mbForwarderIsEditMode ( false ),
+ mbShapeIsEditMode ( true ),
+ mbNotificationsDisabled ( false ),
+ mbNotifyEditOutlinerSet ( false )
+{
+ if( !mpText )
+ {
+ SdrTextObj* pTextObj = dynamic_cast< SdrTextObj* >( mpObject );
+ if( pTextObj )
+ mpText = pTextObj->getText( 0 );
+ }
+
+ StartListening( *mpModel );
+ StartListening( *mpView );
+ mpObject->AddObjectUser( *this );
+
+ // Init edit mode state from shape info (IsTextEditActive())
+ mbShapeIsEditMode = IsEditMode();
+}
+
+
+SvxTextEditSourceImpl::~SvxTextEditSourceImpl()
+{
+ DBG_ASSERT( !mbIsLocked, "text edit source was not unlocked before dispose!" );
+ if( mpObject )
+ mpObject->RemoveObjectUser( *this );
+
+ dispose();
+}
+
+
+void SvxTextEditSourceImpl::addRange( SvxUnoTextRangeBase* pNewRange )
+{
+ if( pNewRange )
+ if( std::find( mvTextRanges.begin(), mvTextRanges.end(), pNewRange ) == mvTextRanges.end() )
+ mvTextRanges.push_back( pNewRange );
+}
+
+
+void SvxTextEditSourceImpl::removeRange( SvxUnoTextRangeBase* pOldRange )
+{
+ if( pOldRange )
+ mvTextRanges.erase( std::remove(mvTextRanges.begin(), mvTextRanges.end(), pOldRange), mvTextRanges.end() );
+}
+
+
+void SvxTextEditSourceImpl::acquire()
+{
+ osl_atomic_increment( &maRefCount );
+}
+
+
+void SvxTextEditSourceImpl::release()
+{
+ if( ! osl_atomic_decrement( &maRefCount ) )
+ delete this;
+}
+
+void SvxTextEditSourceImpl::Notify(SfxBroadcaster& rBC, const SfxHint& rHint)
+{
+ // #i105988 keep reference to this object
+ rtl::Reference< SvxTextEditSourceImpl > xThis( this );
+
+ if (SfxHintId::Dying == rHint.GetId())
+ {
+ if (&rBC == mpView)
+ {
+ mpView = nullptr;
+ mpViewForwarder.reset();
+ }
+ }
+ else if (rHint.GetId() == SfxHintId::ThisIsAnSdrHint)
+ {
+ const SdrHint* pSdrHint = static_cast<const SdrHint*>(&rHint);
+ switch( pSdrHint->GetKind() )
+ {
+ case SdrHintKind::ObjectChange:
+ {
+ mbDataValid = false; // Text has to be get again
+
+ if( HasView() )
+ {
+ // Update maTextOffset, object has changed
+ // Cannot call that here, since TakeTextRect() (called from there)
+ // changes outliner content.
+ // UpdateOutliner();
+
+ // Broadcast object changes, as they might change visible attributes
+ SvxViewChangedHint aHint;
+ Broadcast( aHint );
+ }
+ break;
+ }
+
+ case SdrHintKind::BeginEdit:
+ if( mpObject == pSdrHint->GetObject() )
+ {
+ // Once SdrHintKind::BeginEdit is broadcast, each EditSource of
+ // AccessibleCell will handle it here and call below:
+ // mpView->GetTextEditOutliner()->SetNotifyHdl(), which
+ // will replace the Notifier for current editable cell. It
+ // is totally wrong. So add check here to avoid the
+ // incorrect replacement of notifier.
+
+ // Currently it only happens on the editsource of
+ // AccessibleCell
+ if (mpObject && mpText)
+ {
+ sdr::table::SdrTableObj* pTableObj = dynamic_cast< sdr::table::SdrTableObj* >( mpObject );
+ if(pTableObj)
+ {
+ const sdr::table::CellRef& xCell = pTableObj->getActiveCell();
+ if (xCell.is())
+ {
+ sdr::table::Cell* pCellObj = dynamic_cast< sdr::table::Cell* >( mpText );
+ if (pCellObj && xCell.get() != pCellObj)
+ break;
+ }
+ }
+ }
+ // invalidate old forwarder
+ if( !mbForwarderIsEditMode )
+ {
+ mpTextForwarder.reset();
+ }
+
+ // register as listener - need to broadcast state change messages
+ if( mpView && mpView->GetTextEditOutliner() )
+ {
+ mpView->GetTextEditOutliner()->SetNotifyHdl( LINK(this, SvxTextEditSourceImpl, NotifyHdl) );
+ mbNotifyEditOutlinerSet = true;
+ }
+
+ // Only now we're really in edit mode
+ mbShapeIsEditMode = true;
+
+ Broadcast( *pSdrHint );
+ }
+ break;
+
+ case SdrHintKind::EndEdit:
+ if( mpObject == pSdrHint->GetObject() )
+ {
+ Broadcast( *pSdrHint );
+
+ // We're no longer in edit mode
+ mbShapeIsEditMode = false;
+
+ // remove as listener - outliner might outlive ourselves
+ if( mpView && mpView->GetTextEditOutliner() )
+ {
+ mpView->GetTextEditOutliner()->SetNotifyHdl( Link<EENotify&,void>() );
+ mbNotifyEditOutlinerSet = false;
+ }
+
+ // destroy view forwarder, OutlinerView no longer
+ // valid (no need for UpdateData(), it's been
+ // synched on SdrEndTextEdit)
+ mpViewForwarder.reset();
+
+ // Invalidate text forwarder, we might
+ // not be called again before entering edit mode a
+ // second time! Then, the old outliner might be
+ // invalid.
+ if( mbForwarderIsEditMode )
+ {
+ mbForwarderIsEditMode = false;
+ mpTextForwarder.reset();
+ }
+ }
+ break;
+
+ case SdrHintKind::ModelCleared:
+ dispose();
+ break;
+ default:
+ break;
+ }
+ }
+ else if (const SvxViewChangedHint* pViewHint = dynamic_cast<const SvxViewChangedHint*>(&rHint))
+ {
+ Broadcast( *pViewHint );
+ }
+}
+
+/* this is a callback from the attached SdrObject when it is actually deleted */
+void SvxTextEditSourceImpl::ObjectInDestruction(const SdrObject&)
+{
+ mpObject = nullptr;
+ dispose();
+ Broadcast( SfxHint( SfxHintId::Dying ) );
+}
+
+/* unregister at all objects and set all references to 0 */
+void SvxTextEditSourceImpl::dispose()
+{
+ mpTextForwarder.reset();
+ mpViewForwarder.reset();
+
+ if( mpOutliner )
+ {
+ if( mpModel )
+ {
+ mpModel->disposeOutliner( std::move(mpOutliner) );
+ }
+ else
+ {
+ mpOutliner.reset();
+ }
+ }
+
+ if( mpModel )
+ {
+ EndListening( *mpModel );
+ mpModel = nullptr;
+ }
+
+ if( mpView )
+ {
+ // remove as listener - outliner might outlive ourselves
+ if (mbNotifyEditOutlinerSet && mpView->GetTextEditOutliner())
+ {
+ mpView->GetTextEditOutliner()->SetNotifyHdl(Link<EENotify&,void>());
+ mbNotifyEditOutlinerSet = false;
+ }
+ EndListening( *mpView );
+ mpView = nullptr;
+ }
+
+ if( mpObject )
+ {
+ mpObject->RemoveObjectUser( *this );
+ mpObject = nullptr;
+ }
+ mpWindow = nullptr;
+}
+
+
+void SvxTextEditSourceImpl::SetupOutliner()
+{
+ // only for UAA edit source: setup outliner equivalently as in
+ // SdrTextObj::Paint(), such that formatting equals screen
+ // layout
+ if( !(mpObject && mpOutliner) )
+ return;
+
+ SdrTextObj* pTextObj = dynamic_cast<SdrTextObj*>( mpObject );
+ if( pTextObj )
+ {
+ tools::Rectangle aPaintRect;
+ tools::Rectangle aBoundRect( pTextObj->GetCurrentBoundRect() );
+ pTextObj->SetupOutlinerFormatting( *mpOutliner, aPaintRect );
+
+ // calc text offset from shape anchor
+ maTextOffset = aPaintRect.TopLeft() - aBoundRect.TopLeft();
+ }
+}
+
+
+void SvxTextEditSourceImpl::UpdateOutliner()
+{
+ // only for UAA edit source: update outliner equivalently as in
+ // SdrTextObj::Paint(), such that formatting equals screen
+ // layout
+ if( !(mpObject && mpOutliner) )
+ return;
+
+ SdrTextObj* pTextObj = dynamic_cast<SdrTextObj*>( mpObject );
+ if( pTextObj )
+ {
+ tools::Rectangle aPaintRect;
+ tools::Rectangle aBoundRect( pTextObj->GetCurrentBoundRect() );
+ pTextObj->UpdateOutlinerFormatting( *mpOutliner, aPaintRect );
+
+ // calc text offset from shape anchor
+ maTextOffset = aPaintRect.TopLeft() - aBoundRect.TopLeft();
+ }
+}
+
+
+SvxTextForwarder* SvxTextEditSourceImpl::GetBackgroundTextForwarder()
+{
+ bool bCreated = false;
+
+ // prevent EE/Outliner notifications during setup
+ mbNotificationsDisabled = true;
+
+ if (!mpTextForwarder)
+ {
+ if( mpOutliner == nullptr )
+ {
+ SdrTextObj* pTextObj = dynamic_cast<SdrTextObj*>( mpObject );
+ OutlinerMode nOutlMode = OutlinerMode::TextObject;
+ if( pTextObj && pTextObj->IsTextFrame() && pTextObj->GetTextKind() == SdrObjKind::OutlineText )
+ nOutlMode = OutlinerMode::OutlineObject;
+
+ mpOutliner = mpModel->createOutliner( nOutlMode );
+
+ // Do the setup after outliner creation, would be useless otherwise
+ if( HasView() )
+ {
+ // Setup outliner _before_ filling it
+ SetupOutliner();
+ }
+
+ mpOutliner->SetTextObjNoInit( pTextObj );
+ if( mbIsLocked )
+ {
+ const_cast<EditEngine*>(&(mpOutliner->GetEditEngine()))->SetUpdateLayout( false );
+ mbOldUndoMode = mpOutliner->GetEditEngine().IsUndoEnabled();
+ const_cast<EditEngine*>(&(mpOutliner->GetEditEngine()))->EnableUndo( false );
+ }
+
+ if (!utl::ConfigManager::IsFuzzing())
+ {
+ if ( !m_xLinguServiceManager.is() )
+ {
+ css::uno::Reference< css::uno::XComponentContext > xContext( ::comphelper::getProcessComponentContext() );
+ m_xLinguServiceManager.set(css::linguistic2::LinguServiceManager::create(xContext));
+ }
+
+ css::uno::Reference< css::linguistic2::XHyphenator > xHyphenator = m_xLinguServiceManager->getHyphenator();
+ if( xHyphenator.is() )
+ mpOutliner->SetHyphenator( xHyphenator );
+ }
+ }
+
+
+ mpTextForwarder.reset(new SvxOutlinerForwarder( *mpOutliner, (mpObject->GetObjInventor() == SdrInventor::Default) && (mpObject->GetObjIdentifier() == SdrObjKind::OutlineText) ));
+ // delay listener subscription and UAA initialization until Outliner is fully setup
+ bCreated = true;
+
+ mbForwarderIsEditMode = false;
+ mbDataValid = false;
+ }
+
+ if( mpObject && mpText && !mbDataValid && mpObject->IsInserted() && mpObject->getSdrPageFromSdrObject() )
+ {
+ mpTextForwarder->flushCache();
+
+ std::optional<OutlinerParaObject> pOutlinerParaObject;
+ SdrTextObj* pTextObj = dynamic_cast<SdrTextObj*>( mpObject );
+ if( pTextObj && pTextObj->getActiveText() == mpText )
+ pOutlinerParaObject = pTextObj->CreateEditOutlinerParaObject(); // Get the OutlinerParaObject if text edit is active
+ bool bOwnParaObj(false);
+
+ if( pOutlinerParaObject )
+ bOwnParaObj = true; // text edit active
+ else if (mpText->GetOutlinerParaObject())
+ pOutlinerParaObject = *mpText->GetOutlinerParaObject();
+
+ if( pOutlinerParaObject && ( bOwnParaObj || !mpObject->IsEmptyPresObj() || mpObject->getSdrPageFromSdrObject()->IsMasterPage() ) )
+ {
+ mpOutliner->SetText( *pOutlinerParaObject );
+
+ // put text to object and set EmptyPresObj to FALSE
+ if( mpText && bOwnParaObj && mpObject->IsEmptyPresObj() && pTextObj->IsReallyEdited() )
+ {
+ mpObject->SetEmptyPresObj( false );
+ static_cast< SdrTextObj* >( mpObject)->NbcSetOutlinerParaObjectForText( pOutlinerParaObject, mpText );
+
+ // #i103982# Here, due to mpObject->NbcSetOutlinerParaObjectForText, we LOSE ownership of the
+ // OPO, so do NOT delete it when leaving this method (!)
+ bOwnParaObj = false;
+ }
+ }
+ else
+ {
+ bool bVertical = pOutlinerParaObject && pOutlinerParaObject->IsEffectivelyVertical();
+
+ // set objects style sheet on empty outliner
+ SfxStyleSheetPool* pPool = static_cast<SfxStyleSheetPool*>(mpObject->getSdrModelFromSdrObject().GetStyleSheetPool());
+ if( pPool )
+ mpOutliner->SetStyleSheetPool( pPool );
+
+ SfxStyleSheet* pStyleSheet = mpObject->getSdrPageFromSdrObject()->GetTextStyleSheetForObject( mpObject );
+ if( pStyleSheet )
+ mpOutliner->SetStyleSheet( 0, pStyleSheet );
+
+ if( bVertical )
+ {
+ mpOutliner->SetVertical( pOutlinerParaObject->GetVertical());
+ mpOutliner->SetRotation( pOutlinerParaObject->GetRotation());
+ }
+ }
+
+ // maybe we have to set the border attributes
+ if (mpOutliner->GetParagraphCount()==1)
+ {
+ // if we only have one paragraph we check if it is empty
+ OUString aStr(mpOutliner->GetText(mpOutliner->GetParagraph(0)));
+
+ if (aStr.isEmpty())
+ {
+ // its empty, so we have to force the outliner to initialise itself
+ mpOutliner->SetText( "", mpOutliner->GetParagraph( 0 ) );
+
+ if(mpObject->GetStyleSheet())
+ mpOutliner->SetStyleSheet( 0, mpObject->GetStyleSheet());
+ }
+ }
+
+ mbDataValid = true;
+ }
+
+ if( bCreated && mpOutliner && HasView() )
+ {
+ // register as listener - need to broadcast state change messages
+ // registration delayed until outliner is completely set up
+ mpOutliner->SetNotifyHdl( LINK(this, SvxTextEditSourceImpl, NotifyHdl) );
+ }
+
+ // prevent EE/Outliner notifications during setup
+ mbNotificationsDisabled = false;
+
+ return mpTextForwarder.get();
+}
+
+
+SvxTextForwarder* SvxTextEditSourceImpl::GetEditModeTextForwarder()
+{
+ if( !mpTextForwarder && HasView() )
+ {
+ SdrOutliner* pEditOutliner = mpView->GetTextEditOutliner();
+
+ if( pEditOutliner )
+ {
+ mpTextForwarder.reset(new SvxOutlinerForwarder( *pEditOutliner, (mpObject->GetObjInventor() == SdrInventor::Default) && (mpObject->GetObjIdentifier() == SdrObjKind::OutlineText) ));
+ mbForwarderIsEditMode = true;
+ }
+ }
+
+ return mpTextForwarder.get();
+}
+
+
+SvxTextForwarder* SvxTextEditSourceImpl::GetTextForwarder()
+{
+ if( mpObject == nullptr )
+ return nullptr;
+
+ if( mpModel == nullptr )
+ mpModel = &mpObject->getSdrModelFromSdrObject();
+
+ // distinguish the cases
+ // a) connected to view, maybe edit mode is active, can work directly on the EditOutliner
+ // b) background Outliner, reflect changes into ParaOutlinerObject (this is exactly the old UNO code)
+ if( HasView() )
+ {
+ if( IsEditMode() != mbForwarderIsEditMode )
+ {
+ // forwarder mismatch - create new
+ mpTextForwarder.reset();
+ }
+
+ if( IsEditMode() )
+ return GetEditModeTextForwarder();
+ else
+ return GetBackgroundTextForwarder();
+ }
+ else
+ {
+ // tdf#123470 if the text edit mode of the shape is active, then we
+ // cannot trust a previously cached TextForwarder state as the text may
+ // be out of date, so force a refetch in that case, unless locked against
+ // changes
+ if (IsEditMode() && mpTextForwarder && !mbIsLocked)
+ {
+ assert(!mbForwarderIsEditMode); // because without a view there is no other option except !mbForwarderIsEditMode
+ bool bTextEditActive = false;
+ SdrTextObj* pTextObj = dynamic_cast<SdrTextObj*>(mpObject);
+ // similar to the GetBackgroundTextForwarder check, see if the text edit is active
+ if (pTextObj && pTextObj->getActiveText() == mpText && pTextObj->CanCreateEditOutlinerParaObject())
+ bTextEditActive = true; // text edit active
+ if (bTextEditActive)
+ mbDataValid = false;
+ }
+
+ return GetBackgroundTextForwarder();
+ }
+}
+
+std::unique_ptr<SvxDrawOutlinerViewForwarder> SvxTextEditSourceImpl::CreateViewForwarder()
+{
+ if( mpView->GetTextEditOutlinerView() && mpObject )
+ {
+ // register as listener - need to broadcast state change messages
+ mpView->GetTextEditOutliner()->SetNotifyHdl( LINK(this, SvxTextEditSourceImpl, NotifyHdl) );
+ mbNotifyEditOutlinerSet = true;
+
+ SdrTextObj* pTextObj = dynamic_cast<SdrTextObj*>( mpObject );
+ if( pTextObj )
+ {
+ tools::Rectangle aBoundRect( pTextObj->GetCurrentBoundRect() );
+ OutlinerView& rOutlView = *mpView->GetTextEditOutlinerView();
+
+ return std::unique_ptr<SvxDrawOutlinerViewForwarder>(new SvxDrawOutlinerViewForwarder( rOutlView, aBoundRect.TopLeft() ));
+ }
+ }
+
+ return nullptr;
+}
+
+SvxEditViewForwarder* SvxTextEditSourceImpl::GetEditViewForwarder( bool bCreate )
+{
+ if( mpObject == nullptr )
+ return nullptr;
+
+ if( mpModel == nullptr )
+ mpModel = &mpObject->getSdrModelFromSdrObject();
+
+ // shall we delete?
+ if( mpViewForwarder )
+ {
+ if( !IsEditMode() )
+ {
+ // destroy all forwarders (no need for UpdateData(),
+ // it's been synched on SdrEndTextEdit)
+ mpViewForwarder.reset();
+ }
+ }
+ // which to create? Directly in edit mode, create new, or none?
+ else if( mpView )
+ {
+ if( IsEditMode() )
+ {
+ // create new view forwarder
+ mpViewForwarder = CreateViewForwarder();
+ }
+ else if( bCreate )
+ {
+ // dispose old text forwarder
+ UpdateData();
+
+ mpTextForwarder.reset();
+
+ // enter edit mode
+ mpView->SdrEndTextEdit();
+
+ if(mpView->SdrBeginTextEdit(mpObject))
+ {
+ SdrTextObj* pTextObj = dynamic_cast<SdrTextObj*>( mpObject );
+ if (pTextObj && pTextObj->IsTextEditActive())
+ {
+ // create new view forwarder
+ mpViewForwarder = CreateViewForwarder();
+ }
+ else
+ {
+ // failure. Somehow, SdrBeginTextEdit did not set
+ // our SdrTextObj into edit mode
+ mpView->SdrEndTextEdit();
+ }
+ }
+ }
+ }
+
+ return mpViewForwarder.get();
+}
+
+
+void SvxTextEditSourceImpl::UpdateData()
+{
+ // if we have a view and in edit mode, we're working with the
+ // DrawOutliner. Thus, all changes made on the text forwarder are
+ // reflected on the view and committed to the model on
+ // SdrEndTextEdit(). Thus, no need for explicit updates here.
+ if( HasView() && IsEditMode() )
+ return;
+
+ if( mbIsLocked )
+ {
+ mbNeedsUpdate = true;
+ }
+ else
+ {
+ if( mpOutliner && mpObject && mpText )
+ {
+ SdrTextObj* pTextObj = dynamic_cast< SdrTextObj* >( mpObject );
+ if( pTextObj )
+ {
+ if( (mpOutliner->GetParagraphCount() == 1 && mpOutliner->GetEditEngine().GetTextLen( 0 ) == 0 )
+ || (mpOutliner->GetParagraphCount() == 2 && mpOutliner->GetEditEngine().GetTextLen( 0 ) == 0
+ && mpOutliner->GetEditEngine().GetTextLen( 1 ) == 0) )
+ {
+ pTextObj->NbcSetOutlinerParaObjectForText( std::nullopt, mpText );
+ }
+ else
+ {
+ pTextObj->NbcSetOutlinerParaObjectForText( mpOutliner->CreateParaObject(), mpText );
+ }
+ }
+
+ if( mpObject->IsEmptyPresObj() )
+ mpObject->SetEmptyPresObj(false);
+ }
+ }
+}
+
+void SvxTextEditSourceImpl::lock()
+{
+ // if this assert ever fires, we will need to make this a counter instead of a boolean
+ assert(!mbIsLocked && "cannot nest these loc() calls");
+ mbIsLocked = true;
+ if( mpOutliner )
+ {
+ const_cast<EditEngine*>(&(mpOutliner->GetEditEngine()))->SetUpdateLayout( false );
+ mbOldUndoMode = mpOutliner->GetEditEngine().IsUndoEnabled();
+ const_cast<EditEngine*>(&(mpOutliner->GetEditEngine()))->EnableUndo( false );
+ }
+}
+
+void SvxTextEditSourceImpl::unlock()
+{
+ mbIsLocked = false;
+
+ if( mbNeedsUpdate )
+ {
+ UpdateData();
+ mbNeedsUpdate = false;
+ }
+
+ if( mpOutliner )
+ {
+ const_cast<EditEngine*>(&(mpOutliner->GetEditEngine()))->SetUpdateLayout( true );
+ const_cast<EditEngine*>(&(mpOutliner->GetEditEngine()))->EnableUndo( mbOldUndoMode );
+ }
+}
+
+bool SvxTextEditSourceImpl::IsValid() const
+{
+ return mpView && mpWindow;
+}
+
+Point SvxTextEditSourceImpl::LogicToPixel( const Point& rPoint, const MapMode& rMapMode )
+{
+ // The responsibilities of ViewForwarder happen to be
+ // somewhat mixed in this case. On the one hand, we need the
+ // different interface queries on the SvxEditSource interface,
+ // since we need both VisAreas. On the other hand, if an
+ // EditViewForwarder exists, maTextOffset does not remain static,
+ // but may change with every key press.
+ if( IsEditMode() )
+ {
+ SvxEditViewForwarder* pForwarder = GetEditViewForwarder(false);
+
+ if( pForwarder )
+ return pForwarder->LogicToPixel( rPoint, rMapMode );
+ }
+ else if( IsValid() && mpModel )
+ {
+ Point aPoint1( rPoint );
+ aPoint1.AdjustX(maTextOffset.X() );
+ aPoint1.AdjustY(maTextOffset.Y() );
+
+ Point aPoint2( OutputDevice::LogicToLogic( aPoint1, rMapMode,
+ MapMode(mpModel->GetScaleUnit()) ) );
+ MapMode aMapMode(mpWindow->GetMapMode());
+ aMapMode.SetOrigin(Point());
+ return mpWindow->LogicToPixel( aPoint2, aMapMode );
+ }
+
+ return Point();
+}
+
+Point SvxTextEditSourceImpl::PixelToLogic( const Point& rPoint, const MapMode& rMapMode )
+{
+ // The responsibilities of ViewForwarder happen to be
+ // somewhat mixed in this case. On the one hand, we need the
+ // different interface queries on the SvxEditSource interface,
+ // since we need both VisAreas. On the other hand, if an
+ // EditViewForwarder exists, maTextOffset does not remain static,
+ // but may change with every key press.
+ if( IsEditMode() )
+ {
+ SvxEditViewForwarder* pForwarder = GetEditViewForwarder(false);
+
+ if( pForwarder )
+ return pForwarder->PixelToLogic( rPoint, rMapMode );
+ }
+ else if( IsValid() && mpModel )
+ {
+ MapMode aMapMode(mpWindow->GetMapMode());
+ aMapMode.SetOrigin(Point());
+ Point aPoint1( mpWindow->PixelToLogic( rPoint, aMapMode ) );
+ Point aPoint2( OutputDevice::LogicToLogic( aPoint1,
+ MapMode(mpModel->GetScaleUnit()),
+ rMapMode ) );
+ aPoint2.AdjustX( -(maTextOffset.X()) );
+ aPoint2.AdjustY( -(maTextOffset.Y()) );
+
+ return aPoint2;
+ }
+
+ return Point();
+}
+
+IMPL_LINK(SvxTextEditSourceImpl, NotifyHdl, EENotify&, rNotify, void)
+{
+ if( !mbNotificationsDisabled )
+ {
+ std::unique_ptr< SfxHint > aHint( SvxEditSourceHelper::EENotification2Hint( &rNotify) );
+
+ if (aHint)
+ Broadcast(*aHint);
+ }
+}
+
+SvxTextEditSource::SvxTextEditSource( SdrObject* pObject, SdrText* pText )
+{
+ mpImpl = new SvxTextEditSourceImpl( pObject, pText );
+}
+
+
+SvxTextEditSource::SvxTextEditSource( SdrObject& rObj, SdrText* pText, SdrView& rView, const OutputDevice& rWindow )
+{
+ mpImpl = new SvxTextEditSourceImpl( rObj, pText, rView, rWindow );
+}
+
+
+SvxTextEditSource::SvxTextEditSource( SvxTextEditSourceImpl* pImpl )
+{
+ mpImpl = pImpl;
+}
+
+
+SvxTextEditSource::~SvxTextEditSource()
+{
+ ::SolarMutexGuard aGuard;
+ mpImpl.clear();
+}
+
+
+std::unique_ptr<SvxEditSource> SvxTextEditSource::Clone() const
+{
+ return std::unique_ptr<SvxEditSource>(new SvxTextEditSource( mpImpl.get() ));
+}
+
+
+SvxTextForwarder* SvxTextEditSource::GetTextForwarder()
+{
+ return mpImpl->GetTextForwarder();
+}
+
+
+SvxEditViewForwarder* SvxTextEditSource::GetEditViewForwarder( bool bCreate )
+{
+ return mpImpl->GetEditViewForwarder( bCreate );
+}
+
+
+SvxViewForwarder* SvxTextEditSource::GetViewForwarder()
+{
+ return this;
+}
+
+
+void SvxTextEditSource::UpdateData()
+{
+ mpImpl->UpdateData();
+}
+
+SfxBroadcaster& SvxTextEditSource::GetBroadcaster() const
+{
+ return *mpImpl;
+}
+
+void SvxTextEditSource::lock()
+{
+ mpImpl->lock();
+}
+
+void SvxTextEditSource::unlock()
+{
+ mpImpl->unlock();
+}
+
+bool SvxTextEditSource::IsValid() const
+{
+ return mpImpl->IsValid();
+}
+
+Point SvxTextEditSource::LogicToPixel( const Point& rPoint, const MapMode& rMapMode ) const
+{
+ return mpImpl->LogicToPixel( rPoint, rMapMode );
+}
+
+Point SvxTextEditSource::PixelToLogic( const Point& rPoint, const MapMode& rMapMode ) const
+{
+ return mpImpl->PixelToLogic( rPoint, rMapMode );
+}
+
+void SvxTextEditSource::addRange( SvxUnoTextRangeBase* pNewRange )
+{
+ mpImpl->addRange( pNewRange );
+}
+
+void SvxTextEditSource::removeRange( SvxUnoTextRangeBase* pOldRange )
+{
+ mpImpl->removeRange( pOldRange );
+}
+
+const SvxUnoTextRangeBaseVec& SvxTextEditSource::getRanges() const
+{
+ return mpImpl->getRanges();
+}
+
+void SvxTextEditSource::UpdateOutliner()
+{
+ mpImpl->UpdateOutliner();
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/unodraw/unottabl.cxx b/svx/source/unodraw/unottabl.cxx
new file mode 100644
index 000000000..6233bccb9
--- /dev/null
+++ b/svx/source/unodraw/unottabl.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 <com/sun/star/awt/Gradient.hpp>
+#include <svx/xflftrit.hxx>
+
+#include <svx/svdmodel.hxx>
+#include <svx/unofill.hxx>
+#include <svx/unomid.hxx>
+#include "UnoNameItemTable.hxx"
+
+using namespace ::com::sun::star;
+using namespace ::cppu;
+
+namespace
+{
+class SvxUnoTransGradientTable : public SvxUnoNameItemTable
+{
+public:
+ explicit SvxUnoTransGradientTable(SdrModel* pModel) noexcept;
+
+ virtual NameOrIndex* createItem() const override;
+
+ // XServiceInfo
+ virtual OUString SAL_CALL getImplementationName() override;
+ virtual uno::Sequence<OUString> SAL_CALL getSupportedServiceNames() override;
+
+ // XElementAccess
+ virtual uno::Type SAL_CALL getElementType() override;
+};
+}
+
+SvxUnoTransGradientTable::SvxUnoTransGradientTable(SdrModel* pModel) noexcept
+ : SvxUnoNameItemTable(pModel, XATTR_FILLFLOATTRANSPARENCE, MID_FILLGRADIENT)
+{
+}
+
+OUString SAL_CALL SvxUnoTransGradientTable::getImplementationName()
+{
+ return "SvxUnoTransGradientTable";
+}
+
+uno::Sequence<OUString> SAL_CALL SvxUnoTransGradientTable::getSupportedServiceNames()
+{
+ return { "com.sun.star.drawing.TransparencyGradientTable" };
+}
+
+NameOrIndex* SvxUnoTransGradientTable::createItem() const
+{
+ XFillFloatTransparenceItem* pNewItem = new XFillFloatTransparenceItem();
+ pNewItem->SetEnabled(true);
+ return pNewItem;
+}
+
+// XElementAccess
+uno::Type SAL_CALL SvxUnoTransGradientTable::getElementType()
+{
+ return cppu::UnoType<awt::Gradient>::get();
+}
+
+/**
+ * Create a hatchtable
+ */
+uno::Reference<uno::XInterface> SvxUnoTransGradientTable_createInstance(SdrModel* pModel)
+{
+ return *new SvxUnoTransGradientTable(pModel);
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/unogallery/unogalitem.cxx b/svx/source/unogallery/unogalitem.cxx
new file mode 100644
index 000000000..5addd3226
--- /dev/null
+++ b/svx/source/unogallery/unogalitem.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 "unogalitem.hxx"
+#include "unogaltheme.hxx"
+#include <svx/galtheme.hxx>
+#include <svx/galmisc.hxx>
+#include <svx/fmmodel.hxx>
+#include <vcl/svapp.hxx>
+#include <vcl/graph.hxx>
+#include <svl/itempool.hxx>
+#include <comphelper/servicehelper.hxx>
+#include <cppuhelper/supportsservice.hxx>
+#include <galobj.hxx>
+
+#include <com/sun/star/beans/PropertyAttribute.hpp>
+#include <com/sun/star/gallery/GalleryItemType.hpp>
+#include <memory>
+
+#define UNOGALLERY_GALLERYITEMTYPE 1
+#define UNOGALLERY_URL 2
+#define UNOGALLERY_TITLE 3
+#define UNOGALLERY_THUMBNAIL 4
+#define UNOGALLERY_GRAPHIC 5
+#define UNOGALLERY_DRAWING 6
+
+using namespace ::com::sun::star;
+
+namespace unogallery {
+
+
+GalleryItem::GalleryItem( ::unogallery::GalleryTheme& rTheme, const GalleryObject& rObject ) :
+ ::comphelper::PropertySetHelper( createPropertySetInfo() ),
+ mpTheme( &rTheme ),
+ mpGalleryObject( &rObject )
+{
+ mpTheme->implRegisterGalleryItem( *this );
+}
+
+
+GalleryItem::~GalleryItem()
+ noexcept
+{
+ if( mpTheme )
+ mpTheme->implDeregisterGalleryItem( *this );
+}
+
+
+bool GalleryItem::isValid() const
+{
+ return( mpTheme != nullptr );
+}
+
+
+uno::Any SAL_CALL GalleryItem::queryAggregation( const uno::Type & rType )
+{
+ uno::Any aAny;
+
+ if( rType == cppu::UnoType<lang::XServiceInfo>::get())
+ aAny <<= uno::Reference< lang::XServiceInfo >(this);
+ else if( rType == cppu::UnoType<lang::XTypeProvider>::get())
+ aAny <<= uno::Reference< lang::XTypeProvider >(this);
+ else if( rType == cppu::UnoType<gallery::XGalleryItem>::get())
+ aAny <<= uno::Reference< gallery::XGalleryItem >(this);
+ else if( rType == cppu::UnoType<beans::XPropertySet>::get())
+ aAny <<= uno::Reference< beans::XPropertySet >(this);
+ else if( rType == cppu::UnoType<beans::XPropertyState>::get())
+ aAny <<= uno::Reference< beans::XPropertyState >(this);
+ else if( rType == cppu::UnoType<beans::XMultiPropertySet>::get())
+ aAny <<= uno::Reference< beans::XMultiPropertySet >(this);
+ else
+ aAny = OWeakAggObject::queryAggregation( rType );
+
+ return aAny;
+}
+
+
+uno::Any SAL_CALL GalleryItem::queryInterface( const uno::Type & rType )
+{
+ return OWeakAggObject::queryInterface( rType );
+}
+
+
+void SAL_CALL GalleryItem::acquire()
+ noexcept
+{
+ OWeakAggObject::acquire();
+}
+
+
+void SAL_CALL GalleryItem::release()
+ noexcept
+{
+ OWeakAggObject::release();
+}
+
+
+OUString SAL_CALL GalleryItem::getImplementationName()
+{
+ return "com.sun.star.comp.gallery.GalleryItem";
+}
+
+sal_Bool SAL_CALL GalleryItem::supportsService( const OUString& ServiceName )
+{
+ return cppu::supportsService(this, ServiceName);
+}
+
+uno::Sequence< OUString > SAL_CALL GalleryItem::getSupportedServiceNames()
+{
+ return { "com.sun.star.gallery.GalleryItem" };
+}
+
+uno::Sequence< uno::Type > SAL_CALL GalleryItem::getTypes()
+{
+ static const uno::Sequence aTypes {
+ cppu::UnoType<lang::XServiceInfo>::get(),
+ cppu::UnoType<lang::XTypeProvider>::get(),
+ cppu::UnoType<gallery::XGalleryItem>::get(),
+ cppu::UnoType<beans::XPropertySet>::get(),
+ cppu::UnoType<beans::XPropertyState>::get(),
+ cppu::UnoType<beans::XMultiPropertySet>::get() };
+ return aTypes;
+}
+
+uno::Sequence< sal_Int8 > SAL_CALL GalleryItem::getImplementationId()
+{
+ return css::uno::Sequence<sal_Int8>();
+}
+
+
+sal_Int8 SAL_CALL GalleryItem::getType()
+{
+ const SolarMutexGuard aGuard;
+ sal_Int8 nRet = gallery::GalleryItemType::EMPTY;
+
+ if( isValid() )
+ {
+ switch( implGetObject()->eObjKind )
+ {
+ case SgaObjKind::Sound:
+ nRet = gallery::GalleryItemType::MEDIA;
+ break;
+
+ case SgaObjKind::SvDraw:
+ nRet = gallery::GalleryItemType::DRAWING;
+ break;
+
+ default:
+ nRet = gallery::GalleryItemType::GRAPHIC;
+ break;
+ }
+ }
+
+ return nRet;
+}
+
+
+rtl::Reference<::comphelper::PropertySetInfo> GalleryItem::createPropertySetInfo()
+{
+ static ::comphelper::PropertyMapEntry const aEntries[] =
+ {
+ { OUString("GalleryItemType"), UNOGALLERY_GALLERYITEMTYPE, cppu::UnoType<sal_Int8>::get(),
+ beans::PropertyAttribute::READONLY, 0 },
+
+ { OUString("URL"), UNOGALLERY_URL, ::cppu::UnoType<OUString>::get(),
+ beans::PropertyAttribute::READONLY, 0 },
+
+ { OUString("Title"), UNOGALLERY_TITLE, ::cppu::UnoType<OUString>::get(),
+ 0, 0 },
+
+ { OUString("Thumbnail"), UNOGALLERY_THUMBNAIL, cppu::UnoType<graphic::XGraphic>::get(),
+ beans::PropertyAttribute::READONLY, 0 },
+
+ { OUString("Graphic"), UNOGALLERY_GRAPHIC, cppu::UnoType<graphic::XGraphic>::get(),
+ beans::PropertyAttribute::READONLY, 0 },
+
+ { OUString("Drawing"), UNOGALLERY_DRAWING, cppu::UnoType<lang::XComponent>::get(),
+ beans::PropertyAttribute::READONLY, 0 },
+ };
+
+ return rtl::Reference<::comphelper::PropertySetInfo>( new ::comphelper::PropertySetInfo( aEntries ) );
+}
+
+void GalleryItem::_setPropertyValues( const comphelper::PropertyMapEntry** ppEntries, const uno::Any* pValues )
+{
+ const SolarMutexGuard aGuard;
+
+ while( *ppEntries )
+ {
+ if( UNOGALLERY_TITLE == (*ppEntries)->mnHandle )
+ {
+ OUString aNewTitle;
+
+ if( !(*pValues >>= aNewTitle) )
+ {
+ throw lang::IllegalArgumentException();
+ }
+
+ ::GalleryTheme* pGalTheme = ( isValid() ? mpTheme->implGetTheme() : nullptr );
+
+ if( pGalTheme )
+ {
+ std::unique_ptr<SgaObject> pObj(pGalTheme->getGalleryStorageEngine()->implReadSgaObject( implGetObject() ));
+
+ if( pObj )
+ {
+ if( pObj->GetTitle() != aNewTitle )
+ {
+ pObj->SetTitle( aNewTitle );
+ pGalTheme->InsertObject( *pObj );
+ }
+ }
+ }
+
+ }
+
+ ++ppEntries;
+ ++pValues;
+ }
+}
+
+void GalleryItem::_getPropertyValues( const comphelper::PropertyMapEntry** ppEntries, uno::Any* pValue )
+{
+ const SolarMutexGuard aGuard;
+
+ while( *ppEntries )
+ {
+ switch( (*ppEntries)->mnHandle )
+ {
+ case UNOGALLERY_GALLERYITEMTYPE:
+ {
+ *pValue <<= getType();
+ }
+ break;
+
+ case UNOGALLERY_URL:
+ {
+ ::GalleryTheme* pGalTheme = ( isValid() ? mpTheme->implGetTheme() : nullptr );
+
+ if( pGalTheme )
+ *pValue <<= implGetObject()->getURL().GetMainURL( INetURLObject::DecodeMechanism::NONE );
+ }
+ break;
+
+ case UNOGALLERY_TITLE:
+ {
+ ::GalleryTheme* pGalTheme = ( isValid() ? mpTheme->implGetTheme() : nullptr );
+
+ if( pGalTheme )
+ {
+ std::unique_ptr<SgaObject> pObj = pGalTheme->AcquireObject( pGalTheme->maGalleryObjectCollection.searchPosWithObject( implGetObject() ) );
+
+ if( pObj )
+ {
+ *pValue <<= pObj->GetTitle();
+ }
+ }
+ }
+ break;
+
+ case UNOGALLERY_THUMBNAIL:
+ {
+ ::GalleryTheme* pGalTheme = ( isValid() ? mpTheme->implGetTheme() : nullptr );
+
+ if( pGalTheme )
+ {
+ std::unique_ptr<SgaObject> pObj = pGalTheme->AcquireObject( pGalTheme->maGalleryObjectCollection.searchPosWithObject( implGetObject() ) );
+
+ if( pObj )
+ {
+ Graphic aThumbnail;
+
+ if( pObj->IsThumbBitmap() )
+ aThumbnail = pObj->GetThumbBmp();
+ else
+ aThumbnail = pObj->GetThumbMtf();
+
+ *pValue <<= aThumbnail.GetXGraphic();
+ }
+ }
+ }
+ break;
+
+ case UNOGALLERY_GRAPHIC:
+ {
+ ::GalleryTheme* pGalTheme = ( isValid() ? mpTheme->implGetTheme() : nullptr );
+ Graphic aGraphic;
+
+ if( pGalTheme && pGalTheme->GetGraphic( pGalTheme->maGalleryObjectCollection.searchPosWithObject( implGetObject() ), aGraphic ) )
+ *pValue <<= aGraphic.GetXGraphic();
+ }
+ break;
+
+ case UNOGALLERY_DRAWING:
+ {
+ if( gallery::GalleryItemType::DRAWING == getType() )
+ {
+ ::GalleryTheme* pGalTheme = ( isValid() ? mpTheme->implGetTheme() : nullptr );
+ FmFormModel* pModel = new FmFormModel();
+
+ pModel->GetItemPool().FreezeIdRanges();
+
+ if( pGalTheme && pGalTheme->GetModel( pGalTheme->maGalleryObjectCollection.searchPosWithObject( implGetObject() ), *pModel ) )
+ {
+ uno::Reference< lang::XComponent > xDrawing( new GalleryDrawingModel( pModel ) );
+
+ pModel->setUnoModel( uno::Reference< uno::XInterface >::query( xDrawing ) );
+ *pValue <<= xDrawing;
+ }
+ else
+ delete pModel;
+ }
+ }
+ break;
+ }
+
+ ++ppEntries;
+ ++pValue;
+ }
+}
+
+
+void GalleryItem::implSetInvalid()
+{
+ if( mpTheme )
+ {
+ mpTheme = nullptr;
+ mpGalleryObject = nullptr;
+ }
+}
+
+
+GalleryDrawingModel::GalleryDrawingModel( SdrModel* pDoc )
+ noexcept :
+ SvxUnoDrawingModel( pDoc )
+{
+}
+
+
+GalleryDrawingModel::~GalleryDrawingModel()
+ noexcept
+{
+ delete GetDoc();
+}
+
+
+UNO3_GETIMPLEMENTATION_IMPL( GalleryDrawingModel );
+
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/unogallery/unogalitem.hxx b/svx/source/unogallery/unogalitem.hxx
new file mode 100644
index 000000000..42bc0ba60
--- /dev/null
+++ b/svx/source/unogallery/unogalitem.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_SVX_SOURCE_UNOGALLERY_UNOGALITEM_HXX
+#define INCLUDED_SVX_SOURCE_UNOGALLERY_UNOGALITEM_HXX
+
+#include <svx/unomodel.hxx>
+#include <comphelper/servicehelper.hxx>
+#include <com/sun/star/lang/XServiceInfo.hpp>
+#include <com/sun/star/gallery/XGalleryItem.hpp>
+#include <comphelper/propertysethelper.hxx>
+#include <comphelper/propertysetinfo.hxx>
+#include <cppuhelper/weakagg.hxx>
+
+class GalleryTheme;
+struct GalleryObject;
+namespace unogallery { class GalleryTheme; }
+
+namespace unogallery {
+
+
+class GalleryItem final : public ::cppu::OWeakAggObject,
+ public css::lang::XServiceInfo,
+ public css::lang::XTypeProvider,
+ public css::gallery::XGalleryItem,
+ public ::comphelper::PropertySetHelper
+{
+ friend class ::unogallery::GalleryTheme;
+
+public:
+
+ GalleryItem( ::unogallery::GalleryTheme& rTheme, const GalleryObject& rObject );
+ virtual ~GalleryItem() noexcept override;
+
+ bool isValid() const;
+
+private:
+
+ // XInterface
+ virtual css::uno::Any SAL_CALL queryAggregation( const css::uno::Type & rType ) override;
+ virtual css::uno::Any SAL_CALL queryInterface( const css::uno::Type & rType ) override;
+ virtual void SAL_CALL acquire() noexcept override;
+ virtual void SAL_CALL release() noexcept override;
+
+ // XServiceInfo
+ 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;
+
+ // XGalleryItem
+ virtual ::sal_Int8 SAL_CALL getType( ) override;
+
+ // PropertySetHelper
+ virtual void _setPropertyValues( const comphelper::PropertyMapEntry** ppEntries, const css::uno::Any* pValues ) override;
+ virtual void _getPropertyValues( const comphelper::PropertyMapEntry** ppEntries, css::uno::Any* pValue ) override;
+
+ static rtl::Reference<::comphelper::PropertySetInfo> createPropertySetInfo();
+
+ const ::GalleryObject* implGetObject() const { return mpGalleryObject;}
+ void implSetInvalid();
+
+ GalleryItem( const GalleryItem& ) = delete;
+ GalleryItem& operator=( const GalleryItem& ) = delete;
+
+ ::unogallery::GalleryTheme* mpTheme;
+ const ::GalleryObject* mpGalleryObject;
+};
+
+
+class GalleryDrawingModel : public SvxUnoDrawingModel
+{
+public:
+
+ explicit GalleryDrawingModel( SdrModel* pDoc ) noexcept;
+ virtual ~GalleryDrawingModel() noexcept override;
+
+ UNO3_GETIMPLEMENTATION_DECL( GalleryDrawingModel )
+};
+
+}
+
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/unogallery/unogaltheme.cxx b/svx/source/unogallery/unogaltheme.cxx
new file mode 100644
index 000000000..b40cbc365
--- /dev/null
+++ b/svx/source/unogallery/unogaltheme.cxx
@@ -0,0 +1,362 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <algorithm>
+
+#include "unogaltheme.hxx"
+#include "unogalitem.hxx"
+#include <svx/galtheme.hxx>
+#include <svx/gallery1.hxx>
+#include <svx/galmisc.hxx>
+#include <svx/fmmodel.hxx>
+#include <svx/svdpage.hxx>
+#include <svx/unopage.hxx>
+#include <vcl/svapp.hxx>
+#include <com/sun/star/lang/IndexOutOfBoundsException.hpp>
+#include <cppuhelper/supportsservice.hxx>
+
+using namespace ::com::sun::star;
+
+namespace unogallery {
+
+
+GalleryTheme::GalleryTheme( std::u16string_view rThemeName )
+{
+ mpGallery = ::Gallery::GetGalleryInstance();
+ mpTheme = ( mpGallery ? mpGallery->AcquireTheme( rThemeName, *this ) : nullptr );
+
+ if( mpGallery )
+ StartListening( *mpGallery );
+}
+
+
+GalleryTheme::~GalleryTheme()
+{
+ const SolarMutexGuard aGuard;
+
+ DBG_ASSERT( !mpTheme || mpGallery, "Theme is living without Gallery" );
+
+ implReleaseItems( nullptr );
+
+ if( mpGallery )
+ {
+ EndListening( *mpGallery );
+
+ if( mpTheme )
+ mpGallery->ReleaseTheme( mpTheme, *this );
+ }
+}
+
+
+OUString SAL_CALL GalleryTheme::getImplementationName()
+{
+ return "com.sun.star.comp.gallery.GalleryTheme";
+}
+
+sal_Bool SAL_CALL GalleryTheme::supportsService( const OUString& ServiceName )
+{
+ return cppu::supportsService( this, ServiceName );
+}
+
+uno::Sequence< OUString > SAL_CALL GalleryTheme::getSupportedServiceNames()
+{
+ return { "com.sun.star.gallery.GalleryTheme" };
+}
+
+uno::Sequence< uno::Type > SAL_CALL GalleryTheme::getTypes()
+{
+ static const uno::Sequence aTypes {
+ cppu::UnoType<lang::XServiceInfo>::get(),
+ cppu::UnoType<lang::XTypeProvider>::get(),
+ cppu::UnoType<container::XElementAccess>::get(),
+ cppu::UnoType<container::XIndexAccess>::get(),
+ cppu::UnoType<gallery::XGalleryTheme>::get(),
+ };
+ return aTypes;
+}
+
+uno::Sequence< sal_Int8 > SAL_CALL GalleryTheme::getImplementationId()
+{
+ return css::uno::Sequence<sal_Int8>();
+}
+
+
+uno::Type SAL_CALL GalleryTheme::getElementType()
+{
+ return cppu::UnoType<gallery::XGalleryItem>::get();
+}
+
+
+sal_Bool SAL_CALL GalleryTheme::hasElements()
+{
+ const SolarMutexGuard aGuard;
+
+ return( ( mpTheme != nullptr ) && ( mpTheme->GetObjectCount() > 0 ) );
+}
+
+
+sal_Int32 SAL_CALL GalleryTheme::getCount()
+{
+ const SolarMutexGuard aGuard;
+
+ return( mpTheme ? mpTheme->GetObjectCount() : 0 );
+}
+
+
+uno::Any SAL_CALL GalleryTheme::getByIndex( ::sal_Int32 nIndex )
+{
+ const SolarMutexGuard aGuard;
+ uno::Any aRet;
+
+ if( mpTheme )
+ {
+ if( ( nIndex < 0 ) || ( nIndex >= getCount() ) )
+ {
+ throw lang::IndexOutOfBoundsException();
+ }
+ const GalleryObject* pObj = mpTheme->maGalleryObjectCollection.getForPosition( nIndex );
+
+ if( pObj )
+ aRet <<= uno::Reference< gallery::XGalleryItem >( new GalleryItem( *this, *pObj ) );
+ }
+
+ return aRet;
+}
+
+
+OUString SAL_CALL GalleryTheme::getName( )
+{
+ const SolarMutexGuard aGuard;
+ OUString aRet;
+
+ if( mpTheme )
+ aRet = mpTheme->GetName();
+
+ return aRet;
+}
+
+
+void SAL_CALL GalleryTheme::update( )
+{
+ const SolarMutexGuard aGuard;
+
+ if( mpTheme )
+ {
+ const Link<const INetURLObject&, void> aDummyLink;
+ mpTheme->Actualize( aDummyLink );
+ }
+}
+
+
+::sal_Int32 SAL_CALL GalleryTheme::insertURLByIndex(
+ const OUString& rURL, ::sal_Int32 nIndex )
+{
+ const SolarMutexGuard aGuard;
+ sal_Int32 nRet = -1;
+
+ if( mpTheme )
+ {
+ try
+ {
+ const INetURLObject aURL( rURL );
+
+ nIndex = std::clamp( nIndex, sal_Int32(0), getCount() );
+
+ if( ( aURL.GetProtocol() != INetProtocol::NotValid ) && mpTheme->InsertURL( aURL, nIndex ) )
+ {
+ const GalleryObject* pObj = mpTheme->maGalleryObjectCollection.searchObjectWithURL( aURL );
+
+ if( pObj )
+ nRet = mpTheme->maGalleryObjectCollection.searchPosWithObject( pObj );
+ }
+ }
+ catch( ... )
+ {
+ }
+ }
+
+ return nRet;
+}
+
+
+::sal_Int32 SAL_CALL GalleryTheme::insertGraphicByIndex(
+ const uno::Reference< graphic::XGraphic >& rxGraphic, sal_Int32 nIndex )
+{
+ const SolarMutexGuard aGuard;
+ sal_Int32 nRet = -1;
+
+ if( mpTheme )
+ {
+ try
+ {
+ const Graphic aGraphic( rxGraphic );
+
+ nIndex = std::clamp( nIndex, sal_Int32(0), getCount() );
+
+ if( mpTheme->InsertGraphic( aGraphic, nIndex ) )
+ nRet = nIndex;
+ }
+ catch( ... )
+ {
+ }
+ }
+
+ return nRet;
+}
+
+
+::sal_Int32 SAL_CALL GalleryTheme::insertDrawingByIndex(
+ const uno::Reference< lang::XComponent >& Drawing, sal_Int32 nIndex )
+{
+ const SolarMutexGuard aGuard;
+ sal_Int32 nRet = -1;
+
+ if( mpTheme )
+ {
+ GalleryDrawingModel* pModel = comphelper::getFromUnoTunnel<GalleryDrawingModel>( Drawing );
+
+ if( pModel && dynamic_cast<const FmFormModel*>(pModel->GetDoc()) )
+ {
+ // Here we're inserting something that's already a gallery theme drawing
+ nIndex = std::clamp( nIndex, sal_Int32(0), getCount() );
+
+ if( mpTheme->InsertModel( *static_cast< FmFormModel* >( pModel->GetDoc() ), nIndex ) )
+ nRet = nIndex;
+ }
+ else if (!pModel)
+ {
+ // #i80184# Try to do the right thing and make a Gallery drawing out
+ // of an ordinary Drawing if possible.
+ try
+ {
+ uno::Reference< drawing::XDrawPagesSupplier > xDrawPagesSupplier( Drawing, uno::UNO_QUERY_THROW );
+ uno::Reference< drawing::XDrawPages > xDrawPages( xDrawPagesSupplier->getDrawPages(), uno::UNO_SET_THROW );
+ uno::Reference< drawing::XDrawPage > xPage( xDrawPages->getByIndex( 0 ), uno::UNO_QUERY_THROW );
+ SvxDrawPage* pUnoPage = xPage.is() ? comphelper::getFromUnoTunnel<SvxDrawPage>( xPage ) : nullptr;
+ SdrModel* pOrigModel = pUnoPage ? &pUnoPage->GetSdrPage()->getSdrModelFromSdrPage() : nullptr;
+ SdrPage* pOrigPage = pUnoPage ? pUnoPage->GetSdrPage() : nullptr;
+
+ if (pOrigPage && pOrigModel)
+ {
+ FmFormModel* pTmpModel = new FmFormModel(&pOrigModel->GetItemPool());
+ // Clone to new target SdrModel
+ rtl::Reference<SdrPage> pNewPage = pOrigPage->CloneSdrPage(*pTmpModel);
+ pTmpModel->InsertPage(pNewPage.get(), 0);
+
+ uno::Reference< lang::XComponent > xDrawing( new GalleryDrawingModel( pTmpModel ) );
+ pTmpModel->setUnoModel( uno::Reference< uno::XInterface >::query( xDrawing ) );
+
+ nRet = insertDrawingByIndex( xDrawing, nIndex );
+ return nRet;
+ }
+ }
+ catch (...)
+ {
+ }
+ }
+ }
+
+ return nRet;
+}
+
+
+void SAL_CALL GalleryTheme::removeByIndex( sal_Int32 nIndex )
+{
+ const SolarMutexGuard aGuard;
+
+ if( mpTheme )
+ {
+ if( ( nIndex < 0 ) || ( nIndex >= getCount() ) )
+ throw lang::IndexOutOfBoundsException();
+ mpTheme->RemoveObject( nIndex );
+ }
+}
+
+
+void GalleryTheme::Notify( SfxBroadcaster&, const SfxHint& rHint )
+{
+ const SolarMutexGuard aGuard;
+ const GalleryHint& rGalleryHint = static_cast< const GalleryHint& >( rHint );
+
+ switch( rGalleryHint.GetType() )
+ {
+ case GalleryHintType::CLOSE_THEME:
+ {
+ DBG_ASSERT( !mpTheme || mpGallery, "Theme is living without Gallery" );
+
+ implReleaseItems( nullptr );
+
+ if( mpGallery && mpTheme )
+ {
+ mpGallery->ReleaseTheme( mpTheme, *this );
+ mpTheme = nullptr;
+ }
+ }
+ break;
+
+ case GalleryHintType::CLOSE_OBJECT:
+ {
+ GalleryObject* pObj = static_cast< GalleryObject* >( rGalleryHint.GetData1() );
+
+ if( pObj )
+ implReleaseItems( pObj );
+ }
+ break;
+
+ default:
+ break;
+ }
+}
+
+
+void GalleryTheme::implReleaseItems( GalleryObject const * pObj )
+{
+ const SolarMutexGuard aGuard;
+
+ for( GalleryItemVector::iterator aIter = maItemVector.begin(); aIter != maItemVector.end(); )
+ {
+ if( !pObj || ( (*aIter)->implGetObject() == pObj ) )
+ {
+ (*aIter)->implSetInvalid();
+ aIter = maItemVector.erase( aIter );
+ }
+ else
+ ++aIter;
+ }
+}
+
+
+void GalleryTheme::implRegisterGalleryItem( ::unogallery::GalleryItem& rItem )
+{
+ const SolarMutexGuard aGuard;
+
+ maItemVector.push_back( &rItem );
+}
+
+
+void GalleryTheme::implDeregisterGalleryItem( ::unogallery::GalleryItem& rItem )
+{
+ const SolarMutexGuard aGuard;
+
+ maItemVector.erase(std::remove(maItemVector.begin(), maItemVector.end(), &rItem), maItemVector.end());
+}
+
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/unogallery/unogaltheme.hxx b/svx/source/unogallery/unogaltheme.hxx
new file mode 100644
index 000000000..31cde7003
--- /dev/null
+++ b/svx/source/unogallery/unogaltheme.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_SVX_SOURCE_UNOGALLERY_UNOGALTHEME_HXX
+#define INCLUDED_SVX_SOURCE_UNOGALLERY_UNOGALTHEME_HXX
+
+#include <vector>
+
+#include <cppuhelper/implbase.hxx>
+#include <svl/lstner.hxx>
+#include <com/sun/star/lang/XServiceInfo.hpp>
+#include <com/sun/star/gallery/XGalleryTheme.hpp>
+
+class Gallery;
+class GalleryTheme;
+struct GalleryObject;
+namespace unogallery { class GalleryItem; }
+
+namespace unogallery {
+
+
+class GalleryTheme : public ::cppu::WeakImplHelper<
+ css::gallery::XGalleryTheme,
+ css::lang::XServiceInfo >,
+ public SfxListener
+{
+ friend class ::unogallery::GalleryItem;
+
+public:
+
+ explicit GalleryTheme( std::u16string_view rThemeName );
+ virtual ~GalleryTheme() override;
+
+protected:
+
+ // 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;
+
+ // XElementAccess
+ virtual css::uno::Type SAL_CALL getElementType() override;
+ virtual sal_Bool SAL_CALL hasElements() override;
+
+ // XIndexAccess
+ virtual ::sal_Int32 SAL_CALL getCount( ) override;
+ virtual css::uno::Any SAL_CALL getByIndex( ::sal_Int32 Index ) override;
+
+ // XGalleryThemes
+ virtual OUString SAL_CALL getName( ) override;
+ virtual void SAL_CALL update( ) override;
+ virtual ::sal_Int32 SAL_CALL insertURLByIndex( const OUString& URL, ::sal_Int32 Index ) override;
+ virtual ::sal_Int32 SAL_CALL insertGraphicByIndex( const css::uno::Reference< css::graphic::XGraphic >& Graphic, ::sal_Int32 Index ) override;
+ virtual ::sal_Int32 SAL_CALL insertDrawingByIndex( const css::uno::Reference< css::lang::XComponent >& Drawing, ::sal_Int32 Index ) override;
+ virtual void SAL_CALL removeByIndex( ::sal_Int32 Index ) override;
+
+ // SfxListener
+ virtual void Notify( SfxBroadcaster& rBC, const SfxHint& rHint ) override;
+
+private:
+
+ typedef ::std::vector< ::unogallery::GalleryItem* > GalleryItemVector;
+
+ GalleryItemVector maItemVector;
+ ::Gallery* mpGallery;
+ ::GalleryTheme* mpTheme;
+
+ ::GalleryTheme* implGetTheme() const { return mpTheme;}
+
+ void implReleaseItems( GalleryObject const * pObj );
+
+ void implRegisterGalleryItem( ::unogallery::GalleryItem& rItem );
+ void implDeregisterGalleryItem( ::unogallery::GalleryItem& rItem );
+};
+
+}
+
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/unogallery/unogalthemeprovider.cxx b/svx/source/unogallery/unogalthemeprovider.cxx
new file mode 100644
index 000000000..638406e4d
--- /dev/null
+++ b/svx/source/unogallery/unogalthemeprovider.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 "unogaltheme.hxx"
+#include <svx/gallery1.hxx>
+#include <vcl/svapp.hxx>
+#include <cppuhelper/implbase.hxx>
+#include <cppuhelper/supportsservice.hxx>
+#include <com/sun/star/container/ElementExistException.hpp>
+#include <com/sun/star/gallery/XGalleryTheme.hpp>
+#include <com/sun/star/gallery/XGalleryThemeProvider.hpp>
+#include <com/sun/star/lang/XInitialization.hpp>
+#include <com/sun/star/lang/XServiceInfo.hpp>
+#include <com/sun/star/beans/PropertyValue.hpp>
+#include <com/sun/star/uno/XComponentContext.hpp>
+
+using namespace ::com::sun::star;
+
+namespace {
+
+class GalleryThemeProvider : public ::cppu::WeakImplHelper< css::lang::XInitialization,
+ css::gallery::XGalleryThemeProvider,
+ css::lang::XServiceInfo >
+{
+public:
+
+ GalleryThemeProvider();
+
+protected:
+
+ // 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;
+
+ // XElementAccess
+ virtual css::uno::Type SAL_CALL getElementType() override;
+ virtual sal_Bool SAL_CALL hasElements() 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;
+
+ // XInitialization
+ virtual void SAL_CALL initialize( const css::uno::Sequence< css::uno::Any >& aArguments ) override;
+
+ // XGalleryThemeProvider
+ virtual css::uno::Reference< css::gallery::XGalleryTheme > SAL_CALL insertNewByName( const OUString& ThemeName ) override;
+ virtual void SAL_CALL removeByName( const OUString& ThemeName ) override;
+
+private:
+
+ Gallery* mpGallery;
+ bool mbHiddenThemes;
+};
+
+GalleryThemeProvider::GalleryThemeProvider() :
+ mbHiddenThemes( false )
+{
+ mpGallery = ::Gallery::GetGalleryInstance();
+}
+
+OUString SAL_CALL GalleryThemeProvider::getImplementationName()
+{
+ return "com.sun.star.comp.gallery.GalleryThemeProvider";
+}
+
+sal_Bool SAL_CALL GalleryThemeProvider::supportsService( const OUString& ServiceName )
+{
+ return cppu::supportsService( this, ServiceName );
+}
+
+uno::Sequence< OUString > SAL_CALL GalleryThemeProvider::getSupportedServiceNames()
+{
+ uno::Sequence<OUString> aSeq { "com.sun.star.gallery.GalleryThemeProvider" };
+ return aSeq;
+}
+
+uno::Sequence< uno::Type > SAL_CALL GalleryThemeProvider::getTypes()
+{
+ static const uno::Sequence aTypes {
+ cppu::UnoType<lang::XServiceInfo>::get(),
+ cppu::UnoType<lang::XTypeProvider>::get(),
+ cppu::UnoType<lang::XInitialization>::get(),
+ cppu::UnoType<container::XElementAccess>::get(),
+ cppu::UnoType<container::XNameAccess>::get(),
+ cppu::UnoType<gallery::XGalleryThemeProvider>::get() };
+
+ return aTypes;
+}
+
+uno::Sequence< sal_Int8 > SAL_CALL GalleryThemeProvider::getImplementationId()
+{
+ return css::uno::Sequence<sal_Int8>();
+}
+
+void SAL_CALL GalleryThemeProvider::initialize( const uno::Sequence< uno::Any >& rArguments )
+{
+ uno::Sequence< beans::PropertyValue > aParams;
+
+ for( const auto& rArgument : rArguments )
+ {
+ if( rArgument >>= aParams )
+ break;
+ }
+
+ for( const beans::PropertyValue& rProp : std::as_const(aParams) )
+ {
+ if ( rProp.Name == "ProvideHiddenThemes" )
+ rProp.Value >>= mbHiddenThemes;
+ }
+}
+
+
+uno::Type SAL_CALL GalleryThemeProvider::getElementType()
+{
+ return cppu::UnoType<gallery::XGalleryTheme>::get();
+}
+
+
+sal_Bool SAL_CALL GalleryThemeProvider::hasElements()
+{
+ const SolarMutexGuard aGuard;
+
+ return( ( mpGallery != nullptr ) && ( mpGallery->GetThemeCount() > 0 ) );
+}
+
+
+uno::Any SAL_CALL GalleryThemeProvider::getByName( const OUString& rName )
+{
+ const SolarMutexGuard aGuard;
+ uno::Any aRet;
+
+ if( !mpGallery || !mpGallery->HasTheme( rName ) )
+ {
+ throw container::NoSuchElementException();
+ }
+
+ aRet <<= uno::Reference< gallery::XGalleryTheme >( new ::unogallery::GalleryTheme( rName ) );
+
+ return aRet;
+}
+
+
+uno::Sequence< OUString > SAL_CALL GalleryThemeProvider::getElementNames()
+{
+ const SolarMutexGuard aGuard;
+ sal_uInt32 i = 0, nCount = ( mpGallery ? mpGallery->GetThemeCount() : 0 ), nRealCount = 0;
+ uno::Sequence< OUString > aSeq( nCount );
+ auto aSeqRange = asNonConstRange(aSeq);
+
+ for( ; i < nCount; ++i )
+ {
+ const GalleryThemeEntry* pEntry = mpGallery->GetThemeInfo( i );
+
+ if( mbHiddenThemes || !pEntry->IsHidden() )
+ aSeqRange[ nRealCount++ ] = pEntry->GetThemeName();
+ }
+
+ aSeq.realloc( nRealCount );
+
+ return aSeq;
+}
+
+
+sal_Bool SAL_CALL GalleryThemeProvider::hasByName( const OUString& rName )
+{
+ const SolarMutexGuard aGuard;
+
+ bool bRet = false;
+
+ if( mpGallery && mpGallery->HasTheme( rName ) )
+ bRet = ( mbHiddenThemes || !mpGallery->GetThemeInfo( rName )->IsHidden() );
+
+ return bRet;
+}
+
+
+uno::Reference< gallery::XGalleryTheme > SAL_CALL GalleryThemeProvider::insertNewByName( const OUString& rThemeName )
+{
+ const SolarMutexGuard aGuard;
+ uno::Reference< gallery::XGalleryTheme > xRet;
+
+ if( mpGallery )
+ {
+ if( mpGallery->HasTheme( rThemeName ) )
+ {
+ throw container::ElementExistException();
+ }
+ else if( mpGallery->CreateTheme( rThemeName ) )
+ {
+ xRet = new ::unogallery::GalleryTheme( rThemeName );
+ }
+ }
+
+ return xRet;
+}
+
+
+void SAL_CALL GalleryThemeProvider::removeByName( const OUString& rName )
+{
+ const SolarMutexGuard aGuard;
+
+ if( !mpGallery ||
+ !mpGallery->HasTheme( rName ) ||
+ ( !mbHiddenThemes && mpGallery->GetThemeInfo( rName )->IsHidden() ) )
+ {
+ throw container::NoSuchElementException();
+ }
+
+ mpGallery->RemoveTheme( rName );
+}
+
+}
+
+extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface *
+com_sun_star_comp_gallery_GalleryThemeProvider_get_implementation(
+ css::uno::XComponentContext *,
+ css::uno::Sequence<css::uno::Any> const &)
+{
+ return cppu::acquire(new GalleryThemeProvider);
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/xml/xmleohlp.cxx b/svx/source/xml/xmleohlp.cxx
new file mode 100644
index 000000000..7fd88e492
--- /dev/null
+++ b/svx/source/xml/xmleohlp.cxx
@@ -0,0 +1,717 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * 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/io/XStream.hpp>
+#include <com/sun/star/beans/XPropertySet.hpp>
+#include <com/sun/star/embed/XTransactedObject.hpp>
+#include <com/sun/star/embed/ElementModes.hpp>
+#include <com/sun/star/embed/XEmbeddedObject.hpp>
+#include <com/sun/star/embed/XEmbedPersist.hpp>
+#include <com/sun/star/embed/EmbedStates.hpp>
+#include <com/sun/star/embed/Aspects.hpp>
+#include <com/sun/star/lang/WrappedTargetRuntimeException.hpp>
+#include <osl/diagnose.h>
+#include <sot/storage.hxx>
+#include <tools/debug.hxx>
+#include <sal/log.hxx>
+#include <unotools/streamwrap.hxx>
+#include <unotools/tempfile.hxx>
+
+#include <svtools/embedhlp.hxx>
+#include <unotools/ucbstreamhelper.hxx>
+#include <comphelper/propertyvalue.hxx>
+#include <comphelper/storagehelper.hxx>
+#include <comphelper/embeddedobjectcontainer.hxx>
+
+#include <comphelper/fileformat.h>
+#include <cppuhelper/exc_hlp.hxx>
+#include <cppuhelper/implbase.hxx>
+#include <svx/xmleohlp.hxx>
+#include <map>
+#include <memory>
+#include <mutex>
+
+using namespace ::osl;
+using namespace ::cppu;
+using namespace ::utl;
+using namespace ::com::sun::star;
+using namespace ::com::sun::star::document;
+using namespace ::com::sun::star::uno;
+using namespace ::com::sun::star::container;
+using namespace ::com::sun::star::io;
+using namespace ::com::sun::star::lang;
+
+constexpr OUStringLiteral XML_CONTAINERSTORAGE_NAME_60 = u"Pictures";
+constexpr OUStringLiteral XML_CONTAINERSTORAGE_NAME = u"ObjectReplacements";
+constexpr OUStringLiteral XML_EMBEDDEDOBJECT_URL_BASE = u"vnd.sun.star.EmbeddedObject:";
+constexpr OUStringLiteral XML_EMBEDDEDOBJECTGRAPHIC_URL_BASE = u"vnd.sun.star.GraphicObject:";
+
+
+class OutputStorageWrapper_Impl : public ::cppu::WeakImplHelper<XOutputStream>
+{
+ std::mutex maMutex;
+ Reference < XOutputStream > xOut;
+ TempFile aTempFile;
+ bool bStreamClosed : 1;
+ SvStream* pStream;
+
+public:
+ OutputStorageWrapper_Impl();
+
+// css::io::XOutputStream
+ virtual void SAL_CALL writeBytes(const Sequence< sal_Int8 >& aData) override;
+ virtual void SAL_CALL flush() override;
+ virtual void SAL_CALL closeOutput() override;
+
+ SvStream* GetStream();
+};
+
+OutputStorageWrapper_Impl::OutputStorageWrapper_Impl()
+ : bStreamClosed( false )
+ , pStream(nullptr)
+{
+ aTempFile.EnableKillingFile();
+ pStream = aTempFile.GetStream( StreamMode::READWRITE );
+ xOut = new OOutputStreamWrapper( *pStream );
+}
+
+SvStream *OutputStorageWrapper_Impl::GetStream()
+{
+ if( bStreamClosed )
+ return pStream;
+ return nullptr;
+}
+
+void SAL_CALL OutputStorageWrapper_Impl::writeBytes(
+ const Sequence< sal_Int8 >& aData)
+{
+ std::scoped_lock aGuard( maMutex );
+ xOut->writeBytes( aData );
+}
+
+void SAL_CALL OutputStorageWrapper_Impl::flush()
+{
+ std::scoped_lock aGuard( maMutex );
+ xOut->flush();
+}
+
+void SAL_CALL OutputStorageWrapper_Impl::closeOutput()
+{
+ std::scoped_lock aGuard( maMutex );
+ xOut->closeOutput();
+ bStreamClosed = true;
+}
+
+SvXMLEmbeddedObjectHelper::SvXMLEmbeddedObjectHelper() :
+ mpDocPersist( nullptr ),
+ meCreateMode( SvXMLEmbeddedObjectHelperMode::Read )
+{
+}
+
+SvXMLEmbeddedObjectHelper::SvXMLEmbeddedObjectHelper( ::comphelper::IEmbeddedHelper& rDocPersist, SvXMLEmbeddedObjectHelperMode eCreateMode ) :
+ mpDocPersist( nullptr ),
+ meCreateMode( SvXMLEmbeddedObjectHelperMode::Read )
+{
+ Init( nullptr, rDocPersist, eCreateMode );
+}
+
+SvXMLEmbeddedObjectHelper::~SvXMLEmbeddedObjectHelper()
+{
+}
+
+void SvXMLEmbeddedObjectHelper::disposing(std::unique_lock<std::mutex>&)
+{
+ if( mxTempStorage.is() )
+ {
+ mxTempStorage->dispose();
+ mxTempStorage.clear();
+ }
+}
+
+void SvXMLEmbeddedObjectHelper::splitObjectURL(const OUString& _aURLNoPar,
+ OUString& rContainerStorageName,
+ OUString& rObjectStorageName)
+{
+ DBG_ASSERT(_aURLNoPar.isEmpty() || '#' != _aURLNoPar[0], "invalid object URL" );
+ OUString aURLNoPar = _aURLNoPar;
+
+ sal_Int32 _nPos = aURLNoPar.lastIndexOf( '/' );
+ if( -1 == _nPos )
+ {
+ rContainerStorageName.clear();
+ rObjectStorageName = aURLNoPar;
+ }
+ else
+ {
+ //eliminate 'superfluous' slashes at start and end
+ //#i103076# load objects with all allowed xlink:href syntaxes
+ {
+ //eliminate './' at start
+ sal_Int32 nStart = 0;
+ sal_Int32 nCount = aURLNoPar.getLength();
+ if( aURLNoPar.startsWith( "./" ) )
+ {
+ nStart = 2;
+ nCount -= 2;
+ }
+
+ //eliminate '/' at end
+ sal_Int32 nEnd = aURLNoPar.lastIndexOf( '/' );
+ if( nEnd == aURLNoPar.getLength()-1 && nEnd != (nStart-1) )
+ nCount--;
+
+ aURLNoPar = aURLNoPar.copy( nStart, nCount );
+ }
+
+ _nPos = aURLNoPar.lastIndexOf( '/' );
+ if( _nPos >= 0 )
+ rContainerStorageName = aURLNoPar.copy( 0, _nPos );
+ rObjectStorageName = aURLNoPar.copy( _nPos+1 );
+ }
+}
+
+bool SvXMLEmbeddedObjectHelper::ImplGetStorageNames(
+ const OUString& rURLStr,
+ OUString& rContainerStorageName,
+ OUString& rObjectStorageName,
+ bool bInternalToExternal,
+ bool *pGraphicRepl,
+ bool *pOasisFormat ) const
+{
+ // internal URL: vnd.sun.star.EmbeddedObject:<object-name>
+ // or: vnd.sun.star.EmbeddedObject:<path>/<object-name>
+ // internal replacement images:
+ // vnd.sun.star.EmbeddedObjectGraphic:<object-name>
+ // or: vnd.sun.star.EmbeddedObjectGraphic:<path>/<object-name>
+ // external URL: ./<path>/<object-name>
+ // or: <path>/<object-name>
+ // or: <object-name>
+ // currently, path may only consist of a single directory name
+ // it is also possible to have additional arguments at the end of URL: <main URL>[?<name>=<value>[,<name>=<value>]*]
+
+ if( pGraphicRepl )
+ *pGraphicRepl = false;
+
+ if( pOasisFormat )
+ *pOasisFormat = true; // the default value
+
+ if( rURLStr.isEmpty() )
+ return false;
+
+ // get rid of arguments
+ sal_Int32 nPos = rURLStr.indexOf( '?' );
+ OUString aURLNoPar;
+ if ( nPos == -1 )
+ aURLNoPar = rURLStr;
+ else
+ {
+ aURLNoPar = rURLStr.copy( 0, nPos );
+
+ // check the arguments
+ nPos++;
+ while( nPos >= 0 && nPos < rURLStr.getLength() )
+ {
+ OUString aToken = rURLStr.getToken( 0, ',', nPos );
+ if ( aToken.equalsIgnoreAsciiCase( "oasis=false" ) )
+ {
+ if ( pOasisFormat )
+ *pOasisFormat = false;
+ break;
+ }
+ else
+ {
+ SAL_WARN( "svx", "invalid arguments was found in URL!" );
+ }
+ }
+ }
+
+ if( bInternalToExternal )
+ {
+ nPos = aURLNoPar.indexOf( ':' );
+ if( -1 == nPos )
+ return false;
+ bool bObjUrl = aURLNoPar.startsWith( XML_EMBEDDEDOBJECT_URL_BASE );
+ bool bGrUrl = !bObjUrl &&
+ aURLNoPar.startsWith( XML_EMBEDDEDOBJECTGRAPHIC_URL_BASE );
+ if( !(bObjUrl || bGrUrl) )
+ return false;
+
+ sal_Int32 nPathStart = nPos + 1;
+ nPos = aURLNoPar.lastIndexOf( '/' );
+ if( -1 == nPos )
+ {
+ rContainerStorageName.clear();
+ rObjectStorageName = aURLNoPar.copy( nPathStart );
+ }
+ else if( nPos > nPathStart )
+ {
+ rContainerStorageName = aURLNoPar.copy( nPathStart, nPos-nPathStart);
+ rObjectStorageName = aURLNoPar.copy( nPos+1 );
+ }
+ else
+ return false;
+
+ if( bGrUrl )
+ {
+ bool bOASIS = mxRootStorage.is() &&
+ ( SotStorage::GetVersion( mxRootStorage ) > SOFFICE_FILEFORMAT_60 );
+ if (bOASIS)
+ rContainerStorageName = XML_CONTAINERSTORAGE_NAME;
+ else
+ rContainerStorageName = XML_CONTAINERSTORAGE_NAME_60;
+
+ if( pGraphicRepl )
+ *pGraphicRepl = true;
+ }
+
+
+ }
+ else
+ {
+ splitObjectURL(aURLNoPar, rContainerStorageName, rObjectStorageName);
+ }
+
+ if( -1 != rContainerStorageName.indexOf( '/' ) )
+ {
+ OSL_FAIL( "SvXMLEmbeddedObjectHelper: invalid path name" );
+ return false;
+ }
+
+ return true;
+}
+
+uno::Reference < embed::XStorage > const & SvXMLEmbeddedObjectHelper::ImplGetContainerStorage(
+ const OUString& rStorageName )
+{
+ DBG_ASSERT( -1 == rStorageName.indexOf( '/' ) &&
+ -1 == rStorageName.indexOf( '\\' ),
+ "nested embedded storages aren't supported" );
+ if( !mxContainerStorage.is() ||
+ ( rStorageName != maCurContainerStorageName ) )
+ {
+ if( mxContainerStorage.is() &&
+ !maCurContainerStorageName.isEmpty() &&
+ SvXMLEmbeddedObjectHelperMode::Write == meCreateMode )
+ {
+ uno::Reference < embed::XTransactedObject > xTrans( mxContainerStorage, uno::UNO_QUERY );
+ if ( xTrans.is() )
+ xTrans->commit();
+ }
+
+ if( !rStorageName.isEmpty() && mxRootStorage.is() )
+ {
+ sal_Int32 nMode = SvXMLEmbeddedObjectHelperMode::Write == meCreateMode
+ ? ::embed::ElementModes::READWRITE
+ : ::embed::ElementModes::READ;
+ mxContainerStorage = mxRootStorage->openStorageElement( rStorageName,
+ nMode );
+ }
+ else
+ {
+ mxContainerStorage = mxRootStorage;
+ }
+ maCurContainerStorageName = rStorageName;
+ }
+
+ return mxContainerStorage;
+}
+
+void SvXMLEmbeddedObjectHelper::ImplReadObject(
+ const OUString& rContainerStorageName,
+ OUString& rObjName,
+ const SvGlobalName *, // pClassId, see "TODO/LATER" below
+ SvStream* pTemp )
+{
+ uno::Reference < embed::XStorage > xDocStor( mpDocPersist->getStorage() );
+ uno::Reference < embed::XStorage > xCntnrStor( ImplGetContainerStorage( rContainerStorageName ) );
+
+ if( !xCntnrStor.is() && !pTemp )
+ return;
+
+ OUString aSrcObjName( rObjName );
+ comphelper::EmbeddedObjectContainer& rContainer = mpDocPersist->getEmbeddedObjectContainer();
+
+ // Is the object name unique?
+ // if the object is already instantiated by GetEmbeddedObject
+ // that means that the duplication is being loaded
+ bool bDuplicate = rContainer.HasInstantiatedEmbeddedObject( rObjName );
+ DBG_ASSERT( !bDuplicate, "An object in the document is referenced twice!" );
+
+ if( xDocStor != xCntnrStor || pTemp || bDuplicate )
+ {
+ // TODO/LATER: make this altogether a method in the EmbeddedObjectContainer
+
+ // create a unique name for the duplicate object
+ if( bDuplicate )
+ rObjName = rContainer.CreateUniqueObjectName();
+
+ if( pTemp )
+ {
+ try
+ {
+ pTemp->Seek( 0 );
+ uno::Reference < io::XStream > xStm = xDocStor->openStreamElement( rObjName,
+ embed::ElementModes::READWRITE | embed::ElementModes::TRUNCATE );
+ std::unique_ptr<SvStream> pStream(::utl::UcbStreamHelper::CreateStream( xStm ));
+ pTemp->ReadStream( *pStream );
+ pStream.reset();
+
+ // TODO/LATER: what to do when other types of objects are based on substream persistence?
+ // This is an ole object
+ uno::Reference< beans::XPropertySet > xProps( xStm, uno::UNO_QUERY_THROW );
+ xProps->setPropertyValue(
+ "MediaType",
+ uno::Any( OUString( "application/vnd.sun.star.oleobject" ) ) );
+
+ xStm->getOutputStream()->closeOutput();
+ }
+ catch ( uno::Exception& )
+ {
+ return;
+ }
+ }
+ else
+ {
+ try
+ {
+ xCntnrStor->copyElementTo( aSrcObjName, xDocStor, rObjName );
+ }
+ catch ( uno::Exception& )
+ {
+ return;
+ }
+ }
+ }
+
+ // make object known to the container
+ // TODO/LATER: could be done a little bit more efficient!
+ OUString aName( rObjName );
+
+ // TODO/LATER: The provided pClassId is ignored for now.
+ // The stream contains OLE storage internally and this storage already has a class id specifying the
+ // server that was used to create the object. pClassId could be used to specify the server that should
+ // be used for the next opening, but this information seems to be out of the file format responsibility
+ // area.
+ OUString const baseURL(mpDocPersist->getDocumentBaseURL());
+ rContainer.GetEmbeddedObject(aName, &baseURL);
+}
+
+OUString SvXMLEmbeddedObjectHelper::ImplInsertEmbeddedObjectURL(
+ const OUString& rURLStr )
+{
+ OUString sRetURL;
+
+ OUString aContainerStorageName, aObjectStorageName;
+ if( !ImplGetStorageNames( rURLStr, aContainerStorageName,
+ aObjectStorageName,
+ SvXMLEmbeddedObjectHelperMode::Write == meCreateMode ) )
+ return sRetURL;
+
+ if( SvXMLEmbeddedObjectHelperMode::Read == meCreateMode )
+ {
+ OutputStorageWrapper_Impl *pOut = nullptr;
+ std::map< OUString, rtl::Reference<OutputStorageWrapper_Impl> >::iterator aIter;
+
+ if( mxStreamMap )
+ {
+ aIter = mxStreamMap->find( rURLStr );
+ if( aIter != mxStreamMap->end() && aIter->second.is() )
+ pOut = aIter->second.get();
+ }
+
+ SvGlobalName aClassId, *pClassId = nullptr;
+ sal_Int32 nPos = aObjectStorageName.lastIndexOf( '!' );
+ if( -1 != nPos && aClassId.MakeId( aObjectStorageName.subView( nPos+1 ) ) )
+ {
+ aObjectStorageName = aObjectStorageName.copy( 0, nPos );
+ pClassId = &aClassId;
+ }
+
+ ImplReadObject( aContainerStorageName, aObjectStorageName, pClassId, pOut ? pOut->GetStream() : nullptr );
+ sRetURL = XML_EMBEDDEDOBJECT_URL_BASE + aObjectStorageName;
+
+ if( pOut )
+ {
+ mxStreamMap->erase( aIter );
+ }
+ }
+ else
+ {
+ // Objects are written using ::comphelper::IEmbeddedHelper::SaveAs
+ sRetURL = "./";
+ if( !aContainerStorageName.isEmpty() )
+ {
+ sRetURL += aContainerStorageName + "/";
+ }
+ sRetURL += aObjectStorageName;
+ }
+
+ return sRetURL;
+}
+
+uno::Reference< io::XInputStream > SvXMLEmbeddedObjectHelper::ImplGetReplacementImage(
+ const uno::Reference< embed::XEmbeddedObject >& xObj )
+{
+ uno::Reference< io::XInputStream > xStream;
+
+ if( xObj.is() )
+ {
+ try
+ {
+ bool bSwitchBackToLoaded = false;
+ sal_Int32 nCurState = xObj->getCurrentState();
+ if ( nCurState == embed::EmbedStates::LOADED || nCurState == embed::EmbedStates::RUNNING )
+ {
+ // means that the object is not active
+ // copy replacement image from old to new container
+ OUString aMediaType;
+ xStream = mpDocPersist->getEmbeddedObjectContainer().GetGraphicStream( xObj, &aMediaType );
+ }
+
+ if ( !xStream.is() )
+ {
+ // the image must be regenerated
+ // TODO/LATER: another aspect could be used
+ if ( nCurState == embed::EmbedStates::LOADED )
+ bSwitchBackToLoaded = true;
+
+ OUString aMediaType;
+ xStream = svt::EmbeddedObjectRef::GetGraphicReplacementStream(
+ embed::Aspects::MSOLE_CONTENT,
+ xObj,
+ &aMediaType );
+ }
+
+ if ( bSwitchBackToLoaded )
+ // switch back to loaded state; that way we have a minimum cache confusion
+ xObj->changeState( embed::EmbedStates::LOADED );
+ }
+ catch( uno::Exception& )
+ {}
+ }
+
+ return xStream;
+}
+
+void SvXMLEmbeddedObjectHelper::Init(
+ const uno::Reference < embed::XStorage >& rRootStorage,
+ ::comphelper::IEmbeddedHelper& rPersist,
+ SvXMLEmbeddedObjectHelperMode eCreateMode )
+{
+ mxRootStorage = rRootStorage;
+ mpDocPersist = &rPersist;
+ meCreateMode = eCreateMode;
+}
+
+rtl::Reference<SvXMLEmbeddedObjectHelper> SvXMLEmbeddedObjectHelper::Create(
+ const uno::Reference < embed::XStorage >& rRootStorage,
+ ::comphelper::IEmbeddedHelper& rDocPersist,
+ SvXMLEmbeddedObjectHelperMode eCreateMode )
+{
+ rtl::Reference<SvXMLEmbeddedObjectHelper> pThis(new SvXMLEmbeddedObjectHelper);
+
+ pThis->Init( rRootStorage, rDocPersist, eCreateMode );
+
+ return pThis;
+}
+
+rtl::Reference<SvXMLEmbeddedObjectHelper> SvXMLEmbeddedObjectHelper::Create(
+ ::comphelper::IEmbeddedHelper& rDocPersist,
+ SvXMLEmbeddedObjectHelperMode eCreateMode )
+{
+ rtl::Reference<SvXMLEmbeddedObjectHelper> pThis(new SvXMLEmbeddedObjectHelper);
+
+ pThis->Init( nullptr, rDocPersist, eCreateMode );
+
+ return pThis;
+}
+
+OUString SAL_CALL SvXMLEmbeddedObjectHelper::resolveEmbeddedObjectURL(const OUString& rURL)
+{
+ std::unique_lock aGuard( m_aMutex );
+
+ OUString sRet;
+ try
+ {
+ sRet = ImplInsertEmbeddedObjectURL(rURL);
+ }
+ catch (const RuntimeException&)
+ {
+ throw;
+ }
+ catch (const Exception&)
+ {
+ css::uno::Any anyEx = cppu::getCaughtException();
+ throw WrappedTargetRuntimeException(
+ "SvXMLEmbeddedObjectHelper::resolveEmbeddedObjectURL non-RuntimeException",
+ static_cast<uno::XWeak*>(this), anyEx);
+ }
+ return sRet;
+}
+
+// XNameAccess: alien objects!
+Any SAL_CALL SvXMLEmbeddedObjectHelper::getByName(
+ const OUString& rURLStr )
+{
+ std::unique_lock aGuard( m_aMutex );
+ Any aRet;
+ if( SvXMLEmbeddedObjectHelperMode::Read == meCreateMode )
+ {
+ Reference < XOutputStream > xStrm;
+ if( mxStreamMap )
+ {
+ auto aIter = mxStreamMap->find( rURLStr );
+ if( aIter != mxStreamMap->end() && aIter->second.is() )
+ xStrm = aIter->second.get();
+ }
+ if( !xStrm.is() )
+ {
+ rtl::Reference<OutputStorageWrapper_Impl> xOut = new OutputStorageWrapper_Impl;
+ if( !mxStreamMap )
+ mxStreamMap.emplace();
+ (*mxStreamMap)[rURLStr] = xOut;
+ xStrm = xOut.get();
+ }
+
+ aRet <<= xStrm;
+ }
+ else
+ {
+ bool bGraphicRepl = false;
+ bool bOasisFormat = true;
+ Reference < XInputStream > xStrm;
+ OUString aContainerStorageName, aObjectStorageName;
+ if( ImplGetStorageNames( rURLStr, aContainerStorageName,
+ aObjectStorageName,
+ true,
+ &bGraphicRepl,
+ &bOasisFormat ) )
+ {
+ try
+ {
+ comphelper::EmbeddedObjectContainer& rContainer =
+ mpDocPersist->getEmbeddedObjectContainer();
+
+ Reference < embed::XEmbeddedObject > xObj = rContainer.GetEmbeddedObject( aObjectStorageName );
+ DBG_ASSERT( xObj.is(), "Didn't get object" );
+
+ if( xObj.is() )
+ {
+ if( bGraphicRepl )
+ {
+ xStrm = ImplGetReplacementImage( xObj );
+ }
+ else
+ {
+ Reference < embed::XEmbedPersist > xPersist( xObj, UNO_QUERY );
+ if( xPersist.is() )
+ {
+ if( !mxTempStorage.is() )
+ mxTempStorage =
+ comphelper::OStorageHelper::GetTemporaryStorage();
+ Sequence < beans::PropertyValue > aDummy,
+ aEmbDescr{ comphelper::makePropertyValue("StoreVisualReplacement",
+ !bOasisFormat) };
+ if ( !bOasisFormat )
+ {
+ uno::Reference< io::XInputStream > xGrInStream = ImplGetReplacementImage( xObj );
+ if ( xGrInStream.is() )
+ {
+ aEmbDescr.realloc( 2 );
+ auto pEmbDescr = aEmbDescr.getArray();
+ pEmbDescr[1].Name = "VisualReplacement";
+ pEmbDescr[1].Value <<= xGrInStream;
+ }
+ }
+
+ xPersist->storeToEntry( mxTempStorage, aObjectStorageName,
+ aDummy, aEmbDescr );
+ Reference < io::XStream > xStream =
+ mxTempStorage->openStreamElement(
+ aObjectStorageName,
+ embed::ElementModes::READ);
+ if( xStream.is() )
+ xStrm = xStream->getInputStream();
+ }
+ }
+ }
+ }
+ catch ( uno::Exception& )
+ {
+ }
+ }
+
+ aRet <<= xStrm;
+ }
+
+ return aRet;
+}
+
+Sequence< OUString > SAL_CALL SvXMLEmbeddedObjectHelper::getElementNames()
+{
+ return {};
+}
+
+sal_Bool SAL_CALL SvXMLEmbeddedObjectHelper::hasByName( const OUString& rURLStr )
+{
+ std::unique_lock aGuard( m_aMutex );
+ if( SvXMLEmbeddedObjectHelperMode::Read == meCreateMode )
+ {
+ return true;
+ }
+ else
+ {
+ OUString aContainerStorageName, aObjectStorageName;
+ if( !ImplGetStorageNames( rURLStr, aContainerStorageName,
+ aObjectStorageName,
+ true ) )
+ return false;
+
+ comphelper::EmbeddedObjectContainer& rContainer = mpDocPersist->getEmbeddedObjectContainer();
+ return !aObjectStorageName.isEmpty() &&
+ rContainer.HasEmbeddedObject( aObjectStorageName );
+ }
+}
+
+// XNameAccess
+Type SAL_CALL SvXMLEmbeddedObjectHelper::getElementType()
+{
+ std::unique_lock aGuard( m_aMutex );
+ if( SvXMLEmbeddedObjectHelperMode::Read == meCreateMode )
+ return cppu::UnoType<XOutputStream>::get();
+ else
+ return cppu::UnoType<XInputStream>::get();
+}
+
+sal_Bool SAL_CALL SvXMLEmbeddedObjectHelper::hasElements()
+{
+ std::unique_lock aGuard( m_aMutex );
+ if( SvXMLEmbeddedObjectHelperMode::Read == meCreateMode )
+ {
+ return true;
+ }
+ else
+ {
+ comphelper::EmbeddedObjectContainer& rContainer = mpDocPersist->getEmbeddedObjectContainer();
+ return rContainer.HasEmbeddedObjects();
+ }
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/xml/xmlexport.cxx b/svx/source/xml/xmlexport.cxx
new file mode 100644
index 000000000..1900a9e0b
--- /dev/null
+++ b/svx/source/xml/xmlexport.cxx
@@ -0,0 +1,239 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * 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/diagnose_ex.h>
+#include <com/sun/star/xml/sax/InputSource.hpp>
+#include <com/sun/star/xml/sax/XDocumentHandler.hpp>
+#include <com/sun/star/xml/sax/Writer.hpp>
+#include <com/sun/star/lang/XComponent.hpp>
+#include <com/sun/star/xml/sax/XFastParser.hpp>
+#include <com/sun/star/io/XOutputStream.hpp>
+#include <com/sun/star/document/XFilter.hpp>
+#include <com/sun/star/document/XExporter.hpp>
+#include <com/sun/star/document/XImporter.hpp>
+#include <comphelper/processfactory.hxx>
+#include <svx/svdmodel.hxx>
+#include <svx/xmleohlp.hxx>
+#include <svx/xmlgrhlp.hxx>
+
+#include <svx/unomodel.hxx>
+
+using namespace ::com::sun::star;
+using namespace ::com::sun::star::uno;
+
+bool SvxDrawingLayerExport( SdrModel* pModel, const uno::Reference<io::XOutputStream>& xOut, const Reference< lang::XComponent >& xComponent )
+{
+ return SvxDrawingLayerExport( pModel, xOut, xComponent, "com.sun.star.comp.DrawingLayer.XMLExporter" );
+}
+
+bool SvxDrawingLayerExport( SdrModel* pModel, const uno::Reference<io::XOutputStream>& xOut, const Reference< lang::XComponent >& xComponent, const char* pExportService )
+{
+ bool bDocRet = xOut.is();
+
+ uno::Reference<document::XGraphicStorageHandler> xGraphicStorageHandler;
+ rtl::Reference<SvXMLGraphicHelper> xGraphicHelper;
+
+ Reference< document::XEmbeddedObjectResolver > xObjectResolver;
+ rtl::Reference<SvXMLEmbeddedObjectHelper> xObjectHelper;
+
+ Reference< lang::XComponent > xSourceDoc( xComponent );
+ try
+ {
+ if( !xSourceDoc.is() )
+ {
+ xSourceDoc = new SvxUnoDrawingModel( pModel );
+ pModel->setUnoModel( Reference< XInterface >::query( xSourceDoc ) );
+ }
+
+ uno::Reference< uno::XComponentContext> xContext( ::comphelper::getProcessComponentContext() );
+
+ if( bDocRet )
+ {
+ uno::Reference< xml::sax::XWriter > xWriter = xml::sax::Writer::create( xContext );
+
+ ::comphelper::IEmbeddedHelper *pPersist = pModel->GetPersist();
+ if( pPersist )
+ {
+ xObjectHelper = SvXMLEmbeddedObjectHelper::Create( *pPersist, SvXMLEmbeddedObjectHelperMode::Write );
+ xObjectResolver = xObjectHelper.get();
+ }
+
+ xGraphicHelper = SvXMLGraphicHelper::Create( SvXMLGraphicHelperMode::Write );
+ xGraphicStorageHandler = xGraphicHelper.get();
+
+ uno::Reference<xml::sax::XDocumentHandler> xHandler = xWriter;
+
+ // doc export
+ xWriter->setOutputStream( xOut );
+
+ uno::Sequence< uno::Any > aArgs( xObjectResolver.is() ? 3 : 2 );
+ auto pArgs = aArgs.getArray();
+ pArgs[0] <<= xHandler;
+ pArgs[1] <<= xGraphicStorageHandler;
+ if( xObjectResolver.is() )
+ pArgs[2] <<= xObjectResolver;
+
+ uno::Reference< document::XFilter > xFilter( xContext->getServiceManager()->createInstanceWithArgumentsAndContext( OUString::createFromAscii( pExportService ), aArgs, xContext ), uno::UNO_QUERY );
+ if( !xFilter.is() )
+ {
+ OSL_FAIL( "com.sun.star.comp.Draw.XMLExporter service missing" );
+ bDocRet = false;
+ }
+
+ if( bDocRet )
+ {
+ uno::Reference< document::XExporter > xExporter( xFilter, uno::UNO_QUERY );
+ if( xExporter.is() )
+ {
+ xExporter->setSourceDocument( xSourceDoc );
+
+ uno::Sequence< beans::PropertyValue > aDescriptor( 0 );
+ bDocRet = xFilter->filter( aDescriptor );
+ }
+ }
+ }
+ }
+ catch(uno::Exception const&)
+ {
+ DBG_UNHANDLED_EXCEPTION("svx");
+ bDocRet = false;
+ }
+
+ if( xGraphicHelper )
+ xGraphicHelper->dispose();
+ xGraphicHelper.clear();
+ xGraphicStorageHandler = nullptr;
+
+ if( xObjectHelper.is() )
+ xObjectHelper->dispose();
+
+ return bDocRet;
+}
+
+bool SvxDrawingLayerExport( SdrModel* pModel, const uno::Reference<io::XOutputStream>& xOut )
+{
+ Reference< lang::XComponent > xComponent;
+ return SvxDrawingLayerExport( pModel, xOut, xComponent );
+}
+
+//-
+
+bool SvxDrawingLayerImport( SdrModel* pModel, const uno::Reference<io::XInputStream>& xInputStream, const Reference< lang::XComponent >& xComponent )
+{
+ return SvxDrawingLayerImport( pModel, xInputStream, xComponent, "com.sun.star.comp.Draw.XMLOasisImporter" );
+}
+
+bool SvxDrawingLayerImport( SdrModel* pModel, const uno::Reference<io::XInputStream>& xInputStream, const Reference< lang::XComponent >& xComponent, const char* pImportService )
+{
+ bool bRet = true;
+
+ uno::Reference<document::XGraphicStorageHandler> xGraphicStorageHandler;
+ rtl::Reference<SvXMLGraphicHelper> xGraphicHelper;
+
+ Reference< document::XEmbeddedObjectResolver > xObjectResolver;
+ rtl::Reference<SvXMLEmbeddedObjectHelper> xObjectHelper;
+
+ Reference< lang::XComponent > xTargetDocument( xComponent );
+ if( !xTargetDocument.is() )
+ {
+ xTargetDocument = new SvxUnoDrawingModel( pModel );
+ pModel->setUnoModel( Reference< XInterface >::query( xTargetDocument ) );
+ }
+
+ Reference< frame::XModel > xTargetModel( xTargetDocument, UNO_QUERY );
+
+ try
+ {
+ // Get service factory
+ Reference< uno::XComponentContext > xContext = comphelper::getProcessComponentContext();
+
+ if ( xTargetModel.is() )
+ xTargetModel->lockControllers();
+
+
+ xGraphicHelper = SvXMLGraphicHelper::Create( SvXMLGraphicHelperMode::Read );
+ xGraphicStorageHandler = xGraphicHelper.get();
+
+ ::comphelper::IEmbeddedHelper *pPersist = pModel->GetPersist();
+ if( pPersist )
+ {
+ xObjectHelper = SvXMLEmbeddedObjectHelper::Create(
+ *pPersist,
+ SvXMLEmbeddedObjectHelperMode::Read );
+ xObjectResolver = xObjectHelper.get();
+ }
+
+ // parse
+ // prepare ParserInputSource
+ xml::sax::InputSource aParserInput;
+ aParserInput.aInputStream = xInputStream;
+
+ // prepare filter arguments
+ Sequence<Any> aFilterArgs( 2 );
+ Any *pArgs = aFilterArgs.getArray();
+ *pArgs++ <<= xGraphicStorageHandler;
+ *pArgs++ <<= xObjectResolver;
+
+ // get filter
+ Reference< XInterface > xFilter = xContext->getServiceManager()->createInstanceWithArgumentsAndContext( OUString::createFromAscii( pImportService ), aFilterArgs, xContext);
+ SAL_WARN_IF( !xFilter, "svx", "Can't instantiate filter component " << pImportService);
+ uno::Reference< xml::sax::XFastParser > xFastParser( xFilter, UNO_QUERY );
+ assert(xFastParser);
+
+ bRet = false;
+ if( xFastParser.is() )
+ {
+ // connect model and filter
+ uno::Reference < document::XImporter > xImporter( xFilter, UNO_QUERY );
+ xImporter->setTargetDocument( xTargetDocument );
+
+ // finally, parser the stream
+ xFastParser->parseStream( aParserInput );
+
+ bRet = true;
+ }
+ }
+ catch( uno::Exception& )
+ {
+ DBG_UNHANDLED_EXCEPTION("svx");
+ }
+
+ if( xGraphicHelper )
+ xGraphicHelper->dispose();
+ xGraphicHelper.clear();
+ xGraphicStorageHandler = nullptr;
+
+ if( xObjectHelper.is() )
+ xObjectHelper->dispose();
+ xObjectHelper.clear();
+ xObjectResolver = nullptr;
+
+ if ( xTargetModel.is() )
+ xTargetModel->unlockControllers();
+
+ return bRet;
+}
+
+bool SvxDrawingLayerImport( SdrModel* pModel, const uno::Reference<io::XInputStream>& xInputStream )
+{
+ Reference< lang::XComponent > xComponent;
+ return SvxDrawingLayerImport( pModel, xInputStream, xComponent );
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/xml/xmlgrhlp.cxx b/svx/source/xml/xmlgrhlp.cxx
new file mode 100644
index 000000000..4815c9e8a
--- /dev/null
+++ b/svx/source/xml/xmlgrhlp.cxx
@@ -0,0 +1,1190 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * 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/embed/XTransactedObject.hpp>
+#include <com/sun/star/embed/ElementModes.hpp>
+#include <com/sun/star/beans/XPropertySet.hpp>
+#include <com/sun/star/io/NotConnectedException.hpp>
+#include <com/sun/star/lang/XServiceInfo.hpp>
+#include <com/sun/star/lang/XInitialization.hpp>
+#include <com/sun/star/lang/XMultiServiceFactory.hpp>
+#include <com/sun/star/util/XCancellable.hpp>
+#include <com/sun/star/embed/XHierarchicalStorageAccess.hpp>
+#include <comphelper/fileformat.h>
+#include <comphelper/graphicmimetype.hxx>
+#include <comphelper/compbase.hxx>
+#include <cppuhelper/implbase.hxx>
+#include <cppuhelper/supportsservice.hxx>
+
+#include <rtl/ref.hxx>
+#include <unotools/ucbstreamhelper.hxx>
+#include <unotools/streamwrap.hxx>
+#include <unotools/tempfile.hxx>
+#include <unotools/saveopt.hxx>
+#include <vcl/filter/SvmWriter.hxx>
+#include <vcl/gfxlink.hxx>
+#include <vcl/metaact.hxx>
+#include <tools/zcodec.hxx>
+#include <tools/diagnose_ex.h>
+
+#include <vcl/GraphicObject.hxx>
+#include <vcl/graphicfilter.hxx>
+#include <svx/xmlgrhlp.hxx>
+#include <svx/xmleohlp.hxx>
+
+#include <algorithm>
+#include <memory>
+#include <string_view>
+#include <utility>
+
+using namespace com::sun::star;
+using namespace com::sun::star::uno;
+using namespace com::sun::star::io;
+
+namespace com::sun::star::uno { class XComponentContext; }
+
+constexpr OUStringLiteral XML_GRAPHICSTORAGE_NAME = u"Pictures";
+constexpr OUStringLiteral XML_GRAPHICOBJECT_URL_BASE = u"vnd.sun.star.GraphicObject:";
+
+namespace {
+
+const MetaCommentAction* ImplCheckForEPS( GDIMetaFile const & rMtf )
+{
+ const MetaCommentAction* pComment = nullptr;
+
+ if ( rMtf.GetActionSize() >= 2
+ && rMtf.GetAction(0)->GetType() == MetaActionType::EPS
+ && rMtf.GetAction(1)->GetType() == MetaActionType::COMMENT
+ && ( static_cast<const MetaCommentAction*>(rMtf.GetAction( 1 ))->GetComment() == "EPSReplacementGraphic" ) )
+ pComment = static_cast<const MetaCommentAction*>(rMtf.GetAction( 1 ));
+
+ return pComment;
+}
+
+class GraphicInputStream : public cppu::WeakImplHelper<XInputStream>
+{
+private:
+ virtual sal_Int32 SAL_CALL readBytes(Sequence<sal_Int8> & aData, sal_Int32 nBytesToRead) override;
+ virtual sal_Int32 SAL_CALL readSomeBytes(Sequence<sal_Int8> & aData, sal_Int32 nMaxBytesToRead) override;
+ virtual void SAL_CALL skipBytes(sal_Int32 nBytesToSkip) override;
+ virtual sal_Int32 SAL_CALL available() override;
+ virtual void SAL_CALL closeInput() override;
+
+private:
+ utl::TempFile maTempFile;
+ Reference<XInputStream> mxStreamWrapper;
+
+public:
+
+ explicit GraphicInputStream(GraphicObject const & raGraphicObject, const OUString & rMimeType);
+ GraphicInputStream(const GraphicInputStream&) = delete;
+
+ GraphicInputStream& operator=(const GraphicInputStream&) = delete;
+
+ bool exists() const
+ {
+ return mxStreamWrapper.is();
+ }
+};
+
+
+GraphicInputStream::GraphicInputStream(GraphicObject const & aGraphicObject, const OUString & rMimeType)
+{
+ maTempFile.EnableKillingFile();
+
+ if (aGraphicObject.GetType() == GraphicType::NONE)
+ return;
+
+ std::unique_ptr<SvStream> pStream = ::utl::UcbStreamHelper::CreateStream(maTempFile.GetURL(), StreamMode::WRITE | StreamMode::TRUNC);
+
+ if (!pStream)
+ return;
+
+ const Graphic& aGraphic(aGraphicObject.GetGraphic());
+ const GfxLink aGfxLink(aGraphic.GetGfxLink());
+ bool bRet = false;
+
+ if (aGfxLink.GetDataSize() && aGfxLink.GetData())
+ {
+ if (rMimeType.isEmpty())
+ {
+ pStream->WriteBytes(aGfxLink.GetData(), aGfxLink.GetDataSize());
+ bRet = (pStream->GetError() == ERRCODE_NONE);
+ }
+ else
+ {
+ GraphicFilter &rFilter = GraphicFilter::GetGraphicFilter();
+ bRet = (rFilter.ExportGraphic(aGraphic, u"", *pStream, rFilter.GetExportFormatNumberForMediaType(rMimeType)) == ERRCODE_NONE);
+ }
+ }
+ else
+ {
+ if (aGraphic.GetType() == GraphicType::Bitmap)
+ {
+ GraphicFilter & rFilter = GraphicFilter::GetGraphicFilter();
+ OUString aFormat = rMimeType;
+
+ if (aGraphic.IsAnimated())
+ aFormat = "image/gif";
+ else if (aFormat.isEmpty())
+ aFormat = "image/png";
+
+ bRet = (rFilter.ExportGraphic(aGraphic, u"", *pStream, rFilter.GetExportFormatNumberForMediaType(aFormat)) == ERRCODE_NONE);
+ }
+ else if (rMimeType.isEmpty() && aGraphic.GetType() == GraphicType::GdiMetafile)
+ {
+ pStream->SetVersion(SOFFICE_FILEFORMAT_8);
+ pStream->SetCompressMode(SvStreamCompressFlags::ZBITMAP);
+ SvmWriter aWriter(*pStream);
+ aWriter.Write(aGraphic.GetGDIMetaFile());
+ bRet = (pStream->GetError() == ERRCODE_NONE);
+ }
+ else if (!rMimeType.isEmpty())
+ {
+ GraphicFilter & rFilter = GraphicFilter::GetGraphicFilter();
+ bRet = ( rFilter.ExportGraphic( aGraphic, u"", *pStream, rFilter.GetExportFormatNumberForMediaType( rMimeType ) ) == ERRCODE_NONE );
+ }
+ }
+
+ if (bRet)
+ {
+ pStream->Seek( 0 );
+ mxStreamWrapper = new ::utl::OInputStreamWrapper(std::move(pStream));
+ }
+}
+
+sal_Int32 SAL_CALL GraphicInputStream::readBytes(Sequence<sal_Int8> & rData, sal_Int32 nBytesToRead)
+{
+ if (!mxStreamWrapper.is())
+ throw NotConnectedException();
+
+ return mxStreamWrapper->readBytes(rData, nBytesToRead);
+}
+
+sal_Int32 SAL_CALL GraphicInputStream::readSomeBytes(Sequence<sal_Int8>& rData, sal_Int32 nMaxBytesToRead )
+{
+ if (!mxStreamWrapper.is())
+ throw NotConnectedException() ;
+
+ return mxStreamWrapper->readSomeBytes(rData, nMaxBytesToRead);
+}
+
+void SAL_CALL GraphicInputStream::skipBytes(sal_Int32 nBytesToSkip)
+{
+ if (!mxStreamWrapper.is())
+ throw NotConnectedException();
+
+ mxStreamWrapper->skipBytes(nBytesToSkip);
+}
+
+sal_Int32 SAL_CALL GraphicInputStream::available()
+{
+ if (!mxStreamWrapper.is())
+ throw NotConnectedException();
+
+ return mxStreamWrapper->available();
+}
+
+void SAL_CALL GraphicInputStream::closeInput()
+{
+ if (!mxStreamWrapper.is())
+ throw NotConnectedException();
+
+ mxStreamWrapper->closeInput();
+}
+
+class SvXMLGraphicOutputStream:
+ public cppu::WeakImplHelper<XOutputStream>
+{
+private:
+
+ // XOutputStream
+ virtual void SAL_CALL writeBytes( const Sequence< sal_Int8 >& rData ) override;
+ virtual void SAL_CALL flush() override;
+ virtual void SAL_CALL closeOutput() override;
+
+private:
+
+ std::unique_ptr<::utl::TempFile> mpTmp;
+ std::unique_ptr<SvStream> mpOStm;
+ Reference< XOutputStream > mxStmWrapper;
+ std::unique_ptr<GraphicObject> mxGrfObj;
+ bool mbClosed;
+
+public:
+
+ SvXMLGraphicOutputStream();
+ virtual ~SvXMLGraphicOutputStream() override;
+ SvXMLGraphicOutputStream(const SvXMLGraphicOutputStream&) = delete;
+ SvXMLGraphicOutputStream& operator=(const SvXMLGraphicOutputStream&) = delete;
+
+ bool Exists() const { return mxStmWrapper.is(); }
+ const GraphicObject& GetGraphicObject();
+ Graphic GetGraphic();
+};
+
+SvXMLGraphicOutputStream::SvXMLGraphicOutputStream()
+ : mpTmp(new ::utl::TempFile)
+ , mxGrfObj(new GraphicObject)
+ , mbClosed(false)
+{
+ mpTmp->EnableKillingFile();
+
+ mpOStm = ::utl::UcbStreamHelper::CreateStream( mpTmp->GetURL(), StreamMode::WRITE | StreamMode::TRUNC );
+
+ if( mpOStm )
+ mxStmWrapper = new ::utl::OOutputStreamWrapper( *mpOStm );
+}
+
+SvXMLGraphicOutputStream::~SvXMLGraphicOutputStream()
+{
+ mpTmp.reset();
+ mpOStm.reset();
+}
+
+void SAL_CALL SvXMLGraphicOutputStream::writeBytes( const Sequence< sal_Int8 >& rData )
+{
+ if( !mxStmWrapper.is() )
+ throw NotConnectedException() ;
+
+ mxStmWrapper->writeBytes( rData );
+}
+
+void SAL_CALL SvXMLGraphicOutputStream::flush()
+{
+ if( !mxStmWrapper.is() )
+ throw NotConnectedException() ;
+
+ mxStmWrapper->flush();
+}
+
+void SAL_CALL SvXMLGraphicOutputStream::closeOutput()
+{
+ if( !mxStmWrapper.is() )
+ throw NotConnectedException() ;
+
+ mxStmWrapper->closeOutput();
+ mxStmWrapper.clear();
+
+ mbClosed = true;
+}
+
+Graphic SvXMLGraphicOutputStream::GetGraphic()
+{
+ Graphic aGraphic;
+
+ if (mbClosed && mxGrfObj->GetType() == GraphicType::NONE && mpOStm)
+ {
+ mpOStm->Seek( 0 );
+ sal_uInt16 nFormat = GRFILTER_FORMAT_DONTKNOW;
+ sal_uInt16 nDeterminedFormat = GRFILTER_FORMAT_DONTKNOW;
+ GraphicFilter::GetGraphicFilter().ImportGraphic( aGraphic, u"", *mpOStm ,nFormat,&nDeterminedFormat);
+
+ if (nDeterminedFormat == GRFILTER_FORMAT_DONTKNOW)
+ {
+ //Read the first two byte to check whether it is a gzipped stream, is so it may be in wmz or emz format
+ //unzip them and try again
+
+ sal_uInt8 sFirstBytes[ 2 ];
+
+ sal_uInt64 nStreamLen = mpOStm->TellEnd();
+ mpOStm->Seek( 0 );
+
+ if ( nStreamLen == 0 )
+ {
+ SvLockBytes* pLockBytes = mpOStm->GetLockBytes();
+ if ( pLockBytes )
+ pLockBytes->SetSynchronMode();
+
+ nStreamLen = mpOStm->TellEnd();
+ mpOStm->Seek( 0 );
+ }
+ if( nStreamLen >= 2 )
+ {
+ //read two byte
+ mpOStm->ReadBytes(sFirstBytes, 2);
+
+ if( sFirstBytes[0] == 0x1f && sFirstBytes[1] == 0x8b )
+ {
+ SvMemoryStream aDest;
+ ZCodec aZCodec( 0x8000, 0x8000 );
+ aZCodec.BeginCompression(ZCODEC_DEFAULT_COMPRESSION, /*gzLib*/true);
+ mpOStm->Seek( 0 );
+ aZCodec.Decompress( *mpOStm, aDest );
+
+ if (aZCodec.EndCompression())
+ {
+ sal_uInt64 nStreamLen_ = aDest.TellEnd();
+ if (nStreamLen_ > 0)
+ {
+ aDest.Seek(0);
+ GraphicFilter::GetGraphicFilter().ImportGraphic( aGraphic, u"", aDest ,nFormat,&nDeterminedFormat );
+ }
+ }
+ }
+ }
+ }
+ }
+
+ if (aGraphic.GetType() != GraphicType::NONE)
+ {
+ mpOStm.reset();
+ mpTmp.reset();
+ }
+ return aGraphic;
+}
+
+const GraphicObject& SvXMLGraphicOutputStream::GetGraphicObject()
+{
+ Graphic aGraphic(GetGraphic());
+ if (aGraphic.GetType() != GraphicType::NONE)
+ {
+ mxGrfObj.reset(new GraphicObject(aGraphic));
+ }
+ return *mxGrfObj;
+}
+
+}
+
+SvXMLGraphicHelper::SvXMLGraphicHelper(SvXMLGraphicHelperMode eCreateMode)
+{
+ Init( nullptr, eCreateMode );
+}
+
+SvXMLGraphicHelper::SvXMLGraphicHelper()
+ : meCreateMode(SvXMLGraphicHelperMode::Read)
+{
+}
+
+SvXMLGraphicHelper::~SvXMLGraphicHelper()
+{
+}
+
+bool SvXMLGraphicHelper::ImplGetStreamNames( const OUString& rURLStr,
+ OUString& rPictureStorageName,
+ OUString& rPictureStreamName )
+{
+ if (rURLStr.isEmpty())
+ return false;
+
+ const OUString aURLStr {rURLStr.copy(rURLStr.lastIndexOf(':')+1)};
+
+ if( !aURLStr.isEmpty() && aURLStr.indexOf('/')<0 ) // just one token?
+ {
+ rPictureStorageName = OUString();
+ rPictureStreamName = aURLStr;
+ }
+ else
+ SvXMLEmbeddedObjectHelper::splitObjectURL(aURLStr, rPictureStorageName, rPictureStreamName);
+
+ SAL_WARN_IF(rPictureStreamName.isEmpty(), "svx", "SvXMLGraphicHelper::ImplInsertGraphicURL: invalid scheme: " << rURLStr);
+
+ return !rPictureStreamName.isEmpty();
+}
+
+uno::Reference < embed::XStorage > SvXMLGraphicHelper::ImplGetGraphicStorage( const OUString& rStorageName )
+{
+ uno::Reference < embed::XStorage > xRetStorage;
+ if( mxRootStorage.is() )
+ {
+ try
+ {
+ maCurStorageName = rStorageName;
+ xRetStorage = mxRootStorage->openStorageElement(
+ maCurStorageName,
+ ( SvXMLGraphicHelperMode::Write == meCreateMode )
+ ? embed::ElementModes::READWRITE
+ : embed::ElementModes::READ );
+ }
+ catch ( uno::Exception& )
+ {
+ }
+ //#i43196# try again to open the storage element - this time readonly
+ if(!xRetStorage.is())
+ {
+ try
+ {
+ maCurStorageName = rStorageName;
+ xRetStorage = mxRootStorage->openStorageElement( maCurStorageName, embed::ElementModes::READ );
+ }
+ catch ( uno::Exception& )
+ {
+ }
+ }
+ }
+
+ return xRetStorage;
+}
+
+SvxGraphicHelperStream_Impl SvXMLGraphicHelper::ImplGetGraphicStream( const OUString& rPictureStorageName,
+ const OUString& rPictureStreamName )
+{
+ SvxGraphicHelperStream_Impl aRet;
+ if (!rPictureStorageName.isEmpty())
+ aRet.xStorage = ImplGetGraphicStorage(rPictureStorageName);
+ else
+ aRet.xStorage = mxRootStorage;
+
+ sal_Int32 nMode = embed::ElementModes::READ;
+ if (SvXMLGraphicHelperMode::Write == meCreateMode)
+ {
+ nMode = embed::ElementModes::READWRITE;
+ }
+
+ if (aRet.xStorage.is())
+ {
+ aRet.xStream = aRet.xStorage->openStreamElement( rPictureStreamName, nMode );
+ }
+ else if (rPictureStorageName.indexOf('/') != -1)
+ {
+ uno::Reference<embed::XHierarchicalStorageAccess> xHierRootStorage(mxRootStorage,
+ uno::UNO_QUERY);
+ if (xHierRootStorage.is())
+ {
+ try
+ {
+ aRet.xStream = xHierRootStorage->openStreamElementByHierarchicalName(
+ rPictureStorageName + "/" + rPictureStreamName, nMode);
+ aRet.xStorage = mxRootStorage;
+ }
+ catch (const uno::Exception&)
+ {
+ TOOLS_WARN_EXCEPTION("svx",
+ "SvXMLGraphicHelper::ImplGetGraphicStream: failed to open "
+ << rPictureStreamName);
+ }
+ }
+ }
+
+ if (aRet.xStream.is() && (SvXMLGraphicHelperMode::Write == meCreateMode))
+ {
+ uno::Reference<beans::XPropertySet> xProps(aRet.xStream, uno::UNO_QUERY);
+ xProps->setPropertyValue("UseCommonStoragePasswordEncryption", uno::Any(true));
+ }
+
+ return aRet;
+}
+
+OUString SvXMLGraphicHelper::ImplGetGraphicMimeType( const OUString& rFileName )
+{
+ if( ( rFileName.getLength() >= 4 ) && ( rFileName[ rFileName.getLength() - 4 ] == '.' ) )
+ {
+ const OString aExt(OUStringToOString(rFileName.subView(rFileName.getLength() - 3),
+ RTL_TEXTENCODING_ASCII_US));
+ return comphelper::GraphicMimeTypeHelper::GetMimeTypeForExtension( aExt );
+ }
+
+ return OUString();
+}
+
+Graphic SvXMLGraphicHelper::ImplReadGraphic( const OUString& rPictureStorageName,
+ const OUString& rPictureStreamName )
+{
+ Graphic aReturnGraphic;
+ SvxGraphicHelperStream_Impl aStream( ImplGetGraphicStream( rPictureStorageName, rPictureStreamName ) );
+ if (aStream.xStream.is())
+ {
+ GraphicFilter& rGraphicFilter = GraphicFilter::GetGraphicFilter();
+ std::unique_ptr<SvStream> pStream(utl::UcbStreamHelper::CreateStream(aStream.xStream));
+ Graphic aGraphic = rGraphicFilter.ImportUnloadedGraphic(*pStream);
+ if (!aGraphic.IsNone())
+ aReturnGraphic = aGraphic;
+ else
+ rGraphicFilter.ImportGraphic(aReturnGraphic, u"", *pStream);
+ }
+
+ return aReturnGraphic;
+}
+
+void SvXMLGraphicHelper::Init( const uno::Reference < embed::XStorage >& rXMLStorage,
+ SvXMLGraphicHelperMode eCreateMode,
+ const OUString& rGraphicMimeType )
+{
+ mxRootStorage = rXMLStorage;
+ meCreateMode = eCreateMode;
+ maOutputMimeType = rGraphicMimeType;
+}
+
+rtl::Reference<SvXMLGraphicHelper> SvXMLGraphicHelper::Create( const uno::Reference < embed::XStorage >& rXMLStorage,
+ SvXMLGraphicHelperMode eCreateMode )
+{
+ rtl::Reference<SvXMLGraphicHelper> pThis = new SvXMLGraphicHelper;
+ pThis->Init( rXMLStorage, eCreateMode, OUString() );
+
+ return pThis;
+}
+
+rtl::Reference<SvXMLGraphicHelper> SvXMLGraphicHelper::Create( SvXMLGraphicHelperMode eCreateMode,
+ const OUString& rGraphicMimeType )
+{
+ rtl::Reference<SvXMLGraphicHelper> pThis = new SvXMLGraphicHelper;
+
+ pThis->Init( nullptr, eCreateMode, rGraphicMimeType );
+
+ return pThis;
+}
+
+namespace
+{
+
+void splitUserDataFromURL(OUString const & rWholeURL, OUString & rJustURL, OUString & rUserData)
+{
+ sal_Int32 nUser = rWholeURL.indexOf('?');
+ if (nUser >= 0)
+ {
+ rJustURL = rWholeURL.copy(0, nUser);
+ nUser++;
+ rUserData = rWholeURL.copy(nUser);
+ }
+ else
+ {
+ rJustURL = rWholeURL;
+ }
+}
+
+} // end anonymous namespace
+
+// XGraphicObjectResolver
+OUString SAL_CALL SvXMLGraphicHelper::resolveGraphicObjectURL( const OUString& /*rURL*/ )
+{
+ throw uno::RuntimeException("XGraphicObjectResolver has been removed in LibreOffice 6.1");
+}
+
+// XGraphicStorageHandler
+uno::Reference<graphic::XGraphic> SAL_CALL SvXMLGraphicHelper::loadGraphic(OUString const & rURL)
+{
+ std::unique_lock aGuard(m_aMutex);
+
+ uno::Reference<graphic::XGraphic> xGraphic;
+
+ OUString aURLOnly;
+ OUString aUserData;
+ splitUserDataFromURL(rURL, aURLOnly, aUserData);
+
+ auto aIterator = maGraphicObjects.find(aURLOnly);
+ if (aIterator != maGraphicObjects.end())
+ {
+ return aIterator->second;
+ }
+
+ OUString aPictureStorageName, aPictureStreamName;
+
+ if (ImplGetStreamNames(aURLOnly, aPictureStorageName, aPictureStreamName))
+ {
+ const GraphicObject aGraphicObject(ImplReadGraphic(aPictureStorageName, aPictureStreamName));
+
+ if (aGraphicObject.GetType() != GraphicType::NONE)
+ {
+ xGraphic = aGraphicObject.GetGraphic().GetXGraphic();
+ maGraphicObjects[aURLOnly] = xGraphic;
+ }
+ }
+
+ return xGraphic;
+}
+
+uno::Reference<graphic::XGraphic> SAL_CALL SvXMLGraphicHelper::loadGraphicFromOutputStream(uno::Reference<io::XOutputStream> const & rxOutputStream)
+{
+ std::unique_lock aGuard(m_aMutex);
+
+ uno::Reference<graphic::XGraphic> xGraphic;
+
+ if ((SvXMLGraphicHelperMode::Read == meCreateMode) && rxOutputStream.is())
+ {
+
+ SvXMLGraphicOutputStream* pGraphicOutputStream = static_cast<SvXMLGraphicOutputStream*>(rxOutputStream.get());
+ if (pGraphicOutputStream)
+ {
+ xGraphic = pGraphicOutputStream->GetGraphic().GetXGraphic();
+ }
+ }
+ return xGraphic;
+}
+
+OUString SAL_CALL SvXMLGraphicHelper::saveGraphicByName(css::uno::Reference<css::graphic::XGraphic> const & rxGraphic,
+ OUString & rOutSavedMimeType, OUString const & rRequestName)
+{
+ return implSaveGraphic(rxGraphic, rOutSavedMimeType, rRequestName);
+}
+
+OUString SAL_CALL SvXMLGraphicHelper::saveGraphic(css::uno::Reference<css::graphic::XGraphic> const & rxGraphic)
+{
+ OUString aOutMimeType;
+ return implSaveGraphic(rxGraphic, aOutMimeType, std::u16string_view());
+}
+
+OUString SvXMLGraphicHelper::implSaveGraphic(css::uno::Reference<css::graphic::XGraphic> const & rxGraphic,
+ OUString & rOutSavedMimeType, std::u16string_view rRequestName)
+{
+ Graphic aGraphic(rxGraphic);
+
+ auto aIterator = maExportGraphics.find(aGraphic);
+ if (aIterator != maExportGraphics.end())
+ {
+ auto const & aURLAndMimePair = aIterator->second;
+ rOutSavedMimeType = aURLAndMimePair.second;
+ return aURLAndMimePair.first;
+ }
+
+ GraphicObject aGraphicObject(aGraphic);
+
+ if (aGraphicObject.GetType() != GraphicType::NONE)
+ {
+ const GfxLink aGfxLink(aGraphic.GetGfxLink());
+ OUString aExtension;
+ bool bUseGfxLink = true;
+
+ if (aGfxLink.GetDataSize())
+ {
+ switch (aGfxLink.GetType())
+ {
+ case GfxLinkType::EpsBuffer: aExtension = ".eps"; break;
+ case GfxLinkType::NativeGif: aExtension = ".gif"; break;
+ // #i15508# added BMP type for better exports (checked, works)
+ case GfxLinkType::NativeBmp: aExtension = ".bmp"; break;
+ case GfxLinkType::NativeJpg: aExtension = ".jpg"; break;
+ case GfxLinkType::NativePng: aExtension = ".png"; break;
+ case GfxLinkType::NativeTif: aExtension = ".tif"; break;
+ case GfxLinkType::NativeWmf:
+ if (aGfxLink.IsEMF())
+ aExtension = ".emf";
+ else
+ aExtension = ".wmf";
+ break;
+ case GfxLinkType::NativeMet: aExtension = ".met"; break;
+ case GfxLinkType::NativePct: aExtension = ".pct"; break;
+ case GfxLinkType::NativeSvg:
+ {
+ // backward-compat kludge: since no released OOo
+ // version to date can handle svg properly, wrap it up
+ // into an svm. slight catch22 here, since strict ODF
+ // conformance _recommends_ svg - then again, most old
+ // ODF consumers are believed to be OOo
+ auto nSaneVersion = GetODFSaneDefaultVersion();
+ if ( nSaneVersion < SvtSaveOptions::ODFSVER_012
+ || nSaneVersion == SvtSaveOptions::ODFSVER_012_EXT_COMPAT)
+ {
+ bUseGfxLink = false;
+ aExtension = ".svm";
+ }
+ else
+ {
+ aExtension = ".svg";
+ }
+ break;
+ }
+ case GfxLinkType::NativePdf: aExtension = ".pdf"; break;
+ case GfxLinkType::NativeWebp: aExtension = ".webp"; break;
+
+ default:
+ aExtension = ".grf";
+ break;
+ }
+ }
+ else
+ {
+ if (aGraphicObject.GetType() == GraphicType::Bitmap)
+ {
+ if (aGraphicObject.IsAnimated())
+ aExtension = ".gif";
+ else
+ aExtension = ".png";
+ }
+ else if (aGraphicObject.GetType() == GraphicType::GdiMetafile)
+ {
+ // SJ: first check if this metafile is just an eps file, then we will store the eps instead of svm
+ GDIMetaFile& rMetafile(const_cast<GDIMetaFile&>(aGraphic.GetGDIMetaFile()));
+
+ if (ImplCheckForEPS(rMetafile))
+ aExtension = ".eps";
+ else
+ aExtension = ".svm";
+ }
+ }
+
+ OUString rPictureStreamName;
+ if (!rRequestName.empty())
+ {
+ rPictureStreamName = rRequestName + aExtension;
+ }
+ else
+ {
+ OUString sId = OStringToOUString(aGraphicObject.GetUniqueID(), RTL_TEXTENCODING_ASCII_US);
+ rPictureStreamName = sId + aExtension;
+ }
+
+ SvxGraphicHelperStream_Impl aStream(ImplGetGraphicStream(XML_GRAPHICSTORAGE_NAME, rPictureStreamName));
+
+ if (aStream.xStream.is())
+ {
+ const OUString aMimeType(ImplGetGraphicMimeType(rPictureStreamName));
+ uno::Reference<beans::XPropertySet> xProps(aStream.xStream, uno::UNO_QUERY);
+
+ // set stream properties (MediaType/Compression)
+ if (!aMimeType.isEmpty())
+ {
+ xProps->setPropertyValue("MediaType", uno::Any(aMimeType));
+ }
+
+ // picture formats that actually _do_ benefit from zip
+ // storage compression
+ // .svm pics gets compressed via ZBITMAP old-style stream
+ // option below
+ static const char* aCompressiblePics[] =
+ {
+ "image/svg+xml",
+ "image/x-emf",
+ "image/x-wmf",
+ "image/tiff",
+ "image/x-eps",
+ "image/bmp",
+ "image/x-pict"
+ };
+
+ bool bSuccess = false;
+
+ bool bCompressed = aMimeType.isEmpty();
+ if( !bCompressed )
+ {
+ for(const char* p : aCompressiblePics)
+ {
+ if( aMimeType.equalsIgnoreAsciiCaseAscii(p) )
+ {
+ bCompressed = true;
+ break;
+ }
+ }
+ }
+
+ xProps->setPropertyValue("Compressed", Any(bCompressed));
+
+ std::unique_ptr<SvStream> pStream(utl::UcbStreamHelper::CreateStream(aStream.xStream));
+ if (bUseGfxLink && aGfxLink.GetDataSize() && aGfxLink.GetData())
+ {
+ pStream->WriteBytes(aGfxLink.GetData(), aGfxLink.GetDataSize());
+ rOutSavedMimeType = aMimeType;
+ bSuccess = (pStream->GetError() == ERRCODE_NONE);
+ }
+ else
+ {
+ if (aGraphic.GetType() == GraphicType::Bitmap)
+ {
+ GraphicFilter& rFilter = GraphicFilter::GetGraphicFilter();
+ OUString aFormat;
+
+ if (aGraphic.IsAnimated())
+ {
+ aFormat = "gif";
+ }
+ else
+ {
+ aFormat = "png";
+ }
+ rOutSavedMimeType = comphelper::GraphicMimeTypeHelper::GetMimeTypeForExtension(aFormat.toUtf8());
+
+ bSuccess = (rFilter.ExportGraphic(aGraphic, u"", *pStream, rFilter.GetExportFormatNumberForShortName(aFormat)) == ERRCODE_NONE);
+ }
+ else if (aGraphic.GetType() == GraphicType::GdiMetafile)
+ {
+ pStream->SetVersion(SOFFICE_FILEFORMAT_8);
+ pStream->SetCompressMode(SvStreamCompressFlags::ZBITMAP);
+ rOutSavedMimeType = comphelper::GraphicMimeTypeHelper::GetMimeTypeForExtension("svm");
+
+ // SJ: first check if this metafile is just an eps file, then we will store the eps instead of svm
+ GDIMetaFile& rMtf(const_cast<GDIMetaFile&>(aGraphic.GetGDIMetaFile()));
+ const MetaCommentAction* pComment = ImplCheckForEPS(rMtf);
+ if (pComment)
+ {
+ sal_uInt32 nSize = pComment->GetDataSize();
+ const sal_uInt8* pData = pComment->GetData();
+ if (nSize && pData)
+ pStream->WriteBytes(pData, nSize);
+
+ const MetaEPSAction* pAct = static_cast<const MetaEPSAction*>(rMtf.FirstAction());
+ const GfxLink& rLink = pAct->GetLink();
+
+ pStream->WriteBytes(rLink.GetData(), rLink.GetDataSize());
+ }
+ else
+ {
+ SvmWriter aWriter(*pStream);
+ aWriter.Write(rMtf);
+ }
+
+ bSuccess = (pStream->GetError() == ERRCODE_NONE);
+ }
+ }
+
+ if (!bSuccess)
+ return OUString();
+
+ uno::Reference<embed::XTransactedObject> xStorage(aStream.xStorage, uno::UNO_QUERY);
+ pStream.reset();
+ aStream.xStream->getOutputStream()->closeOutput();
+ if (xStorage.is())
+ xStorage->commit();
+
+ OUString aStoragePath = "Pictures/" + rPictureStreamName;
+
+ // put into cache
+ maExportGraphics[aGraphic] = std::make_pair(aStoragePath, rOutSavedMimeType);
+
+ return aStoragePath;
+ }
+ }
+
+ return OUString();
+}
+
+uno::Reference<io::XInputStream> SAL_CALL SvXMLGraphicHelper::createInputStream(uno::Reference<graphic::XGraphic> const & rxGraphic)
+{
+ Reference<XInputStream> xInputStream;
+
+ Graphic aGraphic(rxGraphic);
+ GraphicObject aGraphicObject(aGraphic);
+
+ if (SvXMLGraphicHelperMode::Write == meCreateMode)
+ {
+ OUString sMimeType = comphelper::GraphicMimeTypeHelper::GetMimeTypeForExtension(OUStringToOString(maOutputMimeType, RTL_TEXTENCODING_ASCII_US));
+ rtl::Reference<GraphicInputStream> pInputStream(new GraphicInputStream(aGraphicObject, sMimeType));
+
+ // We release the pointer from unique_ptr and assign it to the input stream return type.
+ // In case the stream doesn't exists, unique_ptr will delete the pointer when we go out of scope.
+ if (pInputStream->exists())
+ xInputStream = pInputStream.get();
+ }
+
+ return xInputStream;
+}
+
+// XBinaryStreamResolver
+Reference< XInputStream > SAL_CALL SvXMLGraphicHelper::getInputStream( const OUString& /*rURL*/ )
+{
+ Reference<XInputStream> xRet;
+ return xRet;
+}
+
+Reference< XOutputStream > SAL_CALL SvXMLGraphicHelper::createOutputStream()
+{
+ Reference< XOutputStream > xRet;
+
+ if( SvXMLGraphicHelperMode::Read == meCreateMode )
+ {
+ rtl::Reference<SvXMLGraphicOutputStream> pOutputStream(new SvXMLGraphicOutputStream);
+
+ if( pOutputStream->Exists() )
+ {
+ xRet = pOutputStream.get();
+ maGrfStms.push_back( xRet );
+ }
+ }
+
+ return xRet;
+}
+
+OUString SAL_CALL SvXMLGraphicHelper::resolveOutputStream( const Reference< XOutputStream >& rxBinaryStream )
+{
+ OUString aRet;
+
+ if( ( SvXMLGraphicHelperMode::Read == meCreateMode ) && rxBinaryStream.is() )
+ {
+ if( ::std::find( maGrfStms.begin(), maGrfStms.end(), rxBinaryStream ) != maGrfStms.end() )
+ {
+ SvXMLGraphicOutputStream* pOStm = static_cast< SvXMLGraphicOutputStream* >( rxBinaryStream.get() );
+
+ if( pOStm )
+ {
+ const GraphicObject& rGrfObj = pOStm->GetGraphicObject();
+ const OUString aId(OStringToOUString(
+ rGrfObj.GetUniqueID(), RTL_TEXTENCODING_ASCII_US));
+
+ if( !aId.isEmpty() )
+ {
+ aRet = XML_GRAPHICOBJECT_URL_BASE + aId;
+ }
+ }
+ }
+ }
+
+ return aRet;
+}
+
+// for instantiation via service manager
+namespace {
+
+namespace impl
+{
+typedef comphelper::WeakComponentImplHelper<lang::XInitialization,
+ document::XGraphicObjectResolver,
+ document::XGraphicStorageHandler,
+ document::XBinaryStreamResolver,
+ lang::XServiceInfo>
+ SvXMLGraphicImportExportHelper_Base;
+
+} // namespace impl
+
+class SvXMLGraphicImportExportHelper :
+ public impl::SvXMLGraphicImportExportHelper_Base
+{
+public:
+ explicit SvXMLGraphicImportExportHelper( SvXMLGraphicHelperMode eMode );
+
+protected:
+ // is called from WeakComponentImplHelper when XComponent::dispose() was
+ // called from outside
+ virtual void disposing(std::unique_lock<std::mutex>&) override;
+
+ // ____ XInitialization ____
+ // one argument is allowed, which is the XStorage
+ virtual void SAL_CALL initialize( const Sequence< Any >& aArguments ) override;
+
+ // ____ XGraphicObjectResolver ____
+ virtual OUString SAL_CALL resolveGraphicObjectURL( const OUString& aURL ) override;
+
+ // ____ XGraphicStorageHandler ____
+ virtual css::uno::Reference<css::graphic::XGraphic> SAL_CALL
+ loadGraphic(const OUString& aURL) override;
+
+ virtual css::uno::Reference<css::graphic::XGraphic> SAL_CALL
+ loadGraphicFromOutputStream(css::uno::Reference<css::io::XOutputStream> const & rxOutputStream) override;
+
+ virtual OUString SAL_CALL
+ saveGraphic(css::uno::Reference<css::graphic::XGraphic> const & rxGraphic) override;
+
+ virtual OUString SAL_CALL
+ saveGraphicByName(css::uno::Reference<css::graphic::XGraphic> const & rxGraphic, OUString & rOutSavedMimeType, OUString const & rRequestName) override;
+
+ virtual css::uno::Reference<css::io::XInputStream> SAL_CALL
+ createInputStream(css::uno::Reference<css::graphic::XGraphic> const & rxGraphic) override;
+
+ // ____ XBinaryStreamResolver ____
+ virtual Reference< io::XInputStream > SAL_CALL getInputStream( const OUString& aURL ) override;
+ virtual Reference< io::XOutputStream > SAL_CALL createOutputStream() override;
+ virtual OUString SAL_CALL resolveOutputStream( const Reference< io::XOutputStream >& aBinaryStream ) 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;
+
+private:
+ SvXMLGraphicHelperMode m_eGraphicHelperMode;
+ rtl::Reference<SvXMLGraphicHelper> m_xXMLGraphicHelper;
+};
+
+SvXMLGraphicImportExportHelper::SvXMLGraphicImportExportHelper( SvXMLGraphicHelperMode eMode ) :
+ m_eGraphicHelperMode( eMode )
+{}
+
+void SvXMLGraphicImportExportHelper::disposing(std::unique_lock<std::mutex>&)
+{
+ if (m_xXMLGraphicHelper)
+ {
+ m_xXMLGraphicHelper->dispose();
+ m_xXMLGraphicHelper.clear();
+ }
+}
+
+// ____ XInitialization ____
+void SAL_CALL SvXMLGraphicImportExportHelper::initialize(
+ const Sequence< Any >& aArguments )
+{
+ Reference< embed::XStorage > xStorage;
+ if( aArguments.hasElements() )
+ aArguments[0] >>= xStorage;
+
+ m_xXMLGraphicHelper = SvXMLGraphicHelper::Create( xStorage, m_eGraphicHelperMode );
+}
+
+// ____ XGraphicObjectResolver ____
+OUString SAL_CALL SvXMLGraphicImportExportHelper::resolveGraphicObjectURL( const OUString& aURL )
+{
+ return m_xXMLGraphicHelper->resolveGraphicObjectURL( aURL );
+}
+
+// ____ XGraphicStorageHandler ____
+uno::Reference<graphic::XGraphic> SAL_CALL SvXMLGraphicImportExportHelper::loadGraphic(OUString const & rURL)
+{
+ return m_xXMLGraphicHelper->loadGraphic(rURL);
+}
+
+uno::Reference<graphic::XGraphic> SAL_CALL SvXMLGraphicImportExportHelper::loadGraphicFromOutputStream(uno::Reference<io::XOutputStream> const & rxOutputStream)
+{
+ return m_xXMLGraphicHelper->loadGraphicFromOutputStream(rxOutputStream);
+}
+
+OUString SAL_CALL SvXMLGraphicImportExportHelper::saveGraphic(css::uno::Reference<css::graphic::XGraphic> const & rxGraphic)
+{
+ return m_xXMLGraphicHelper->saveGraphic(rxGraphic);
+}
+
+OUString SAL_CALL SvXMLGraphicImportExportHelper::saveGraphicByName(css::uno::Reference<css::graphic::XGraphic> const & rxGraphic,
+ OUString & rOutSavedMimeType, OUString const & rRequestName)
+{
+ return m_xXMLGraphicHelper->saveGraphicByName(rxGraphic, rOutSavedMimeType, rRequestName);
+}
+
+uno::Reference<io::XInputStream> SAL_CALL SvXMLGraphicImportExportHelper::createInputStream(uno::Reference<graphic::XGraphic> const & rxGraphic)
+{
+ return m_xXMLGraphicHelper->createInputStream(rxGraphic);
+}
+
+// ____ XBinaryStreamResolver ____
+Reference< io::XInputStream > SAL_CALL SvXMLGraphicImportExportHelper::getInputStream( const OUString& aURL )
+{
+ return m_xXMLGraphicHelper->getInputStream( aURL );
+}
+Reference< io::XOutputStream > SAL_CALL SvXMLGraphicImportExportHelper::createOutputStream()
+{
+ return m_xXMLGraphicHelper->createOutputStream();
+}
+OUString SAL_CALL SvXMLGraphicImportExportHelper::resolveOutputStream( const Reference< io::XOutputStream >& aBinaryStream )
+{
+ return m_xXMLGraphicHelper->resolveOutputStream( aBinaryStream );
+}
+
+// ____ XServiceInfo ____
+OUString SAL_CALL SvXMLGraphicImportExportHelper::getImplementationName()
+{
+ if( m_eGraphicHelperMode == SvXMLGraphicHelperMode::Read )
+ return "com.sun.star.comp.Svx.GraphicImportHelper";
+ return "com.sun.star.comp.Svx.GraphicExportHelper";
+}
+
+sal_Bool SAL_CALL SvXMLGraphicImportExportHelper::supportsService( const OUString& ServiceName )
+{
+ return cppu::supportsService(this, ServiceName);
+}
+
+Sequence< OUString > SAL_CALL SvXMLGraphicImportExportHelper::getSupportedServiceNames()
+{
+ return { "com.sun.star.document.GraphicObjectResolver",
+ "com.sun.star.document.GraphicStorageHandler",
+ "com.sun.star.document.BinaryStreamResolver" };
+}
+
+}
+
+/** Create this with createInstanceWithArguments. service name
+ "com.sun.star.comp.Svx.GraphicImportHelper", one argument which is the
+ XStorage. Without arguments no helper class is created. With an empty
+ argument the helper class is created and initialized like in the CTOR to
+ SvXMLGraphicHelper that only gets the create mode.
+
+ You should call dispose after you no longer need this component.
+
+ uses eCreateMode == SvXMLGraphicHelperMode::Read, bDirect == sal_True in
+ SvXMLGraphicHelper
+ */
+extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface *
+com_sun_star_comp_Svx_GraphicImportHelper_get_implementation(
+ css::uno::XComponentContext *,
+ css::uno::Sequence<css::uno::Any> const &)
+{
+ return cppu::acquire(new SvXMLGraphicImportExportHelper(SvXMLGraphicHelperMode::Read));
+}
+
+/** Create this with createInstanceWithArguments. service name
+ "com.sun.star.comp.Svx.GraphicExportHelper", one argument which is the
+ XStorage. Without arguments no helper class is created. With an empty
+ argument the helper class is created and initialized like in the CTOR to
+ SvXMLGraphicHelper that only gets the create mode
+
+ To write the Pictures stream, you have to call dispose at this component.
+ Make sure you call dispose before you commit the parent storage.
+
+ uses eCreateMode == SvXMLGraphicHelperMode::Write, bDirect == sal_True in
+ SvXMLGraphicHelper
+ */
+extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface *
+com_sun_star_comp_Svx_GraphicExportHelper_get_implementation(
+ css::uno::XComponentContext *,
+ css::uno::Sequence<css::uno::Any> const &)
+{
+ return cppu::acquire(new SvXMLGraphicImportExportHelper(SvXMLGraphicHelperMode::Write));
+}
+
+namespace svx {
+
+ void DropUnusedNamedItems(css::uno::Reference<css::uno::XInterface> const& xModel)
+ {
+ uno::Reference<lang::XMultiServiceFactory> const xModelFactory(xModel, uno::UNO_QUERY);
+ assert(xModelFactory.is());
+ try
+ {
+ uno::Reference<util::XCancellable> const xGradient(
+ xModelFactory->createInstance("com.sun.star.drawing.GradientTable"),
+ uno::UNO_QUERY );
+ if (xGradient.is())
+ {
+ xGradient->cancel();
+ }
+
+ uno::Reference<util::XCancellable> const xHatch(
+ xModelFactory->createInstance("com.sun.star.drawing.HatchTable"),
+ uno::UNO_QUERY );
+ if (xHatch.is())
+ {
+ xHatch->cancel();
+ }
+
+ uno::Reference<util::XCancellable> const xBitmap(
+ xModelFactory->createInstance("com.sun.star.drawing.BitmapTable"),
+ uno::UNO_QUERY );
+ if (xBitmap.is())
+ {
+ xBitmap->cancel();
+ }
+
+ uno::Reference<util::XCancellable> const xTransGradient(
+ xModelFactory->createInstance("com.sun.star.drawing.TransparencyGradientTable"),
+ uno::UNO_QUERY );
+ if (xTransGradient.is())
+ {
+ xTransGradient->cancel();
+ }
+
+ uno::Reference<util::XCancellable> const xMarker(
+ xModelFactory->createInstance("com.sun.star.drawing.MarkerTable"),
+ uno::UNO_QUERY );
+ if (xMarker.is())
+ {
+ xMarker->cancel();
+ }
+
+ uno::Reference<util::XCancellable> const xDashes(
+ xModelFactory->createInstance("com.sun.star.drawing.DashTable"),
+ uno::UNO_QUERY );
+ if (xDashes.is())
+ {
+ xDashes->cancel();
+ }
+ }
+ catch (const Exception&)
+ {
+ TOOLS_WARN_EXCEPTION("svx", "dropUnusedNamedItems(): exception during clearing of unused named items");
+ }
+ }
+
+} // namespace svx
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/xml/xmlxtexp.cxx b/svx/source/xml/xmlxtexp.cxx
new file mode 100644
index 000000000..103752b05
--- /dev/null
+++ b/svx/source/xml/xmlxtexp.cxx
@@ -0,0 +1,485 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <sal/config.h>
+
+#include <tools/diagnose_ex.h>
+#include <tools/urlobj.hxx>
+#include <com/sun/star/beans/XPropertySet.hpp>
+#include <com/sun/star/container/XNameContainer.hpp>
+#include <com/sun/star/xml/sax/Writer.hpp>
+#include <com/sun/star/uno/Sequence.hxx>
+#include <com/sun/star/drawing/PolyPolygonBezierCoords.hpp>
+#include <com/sun/star/drawing/LineDash.hpp>
+#include <com/sun/star/awt/Gradient.hpp>
+#include <com/sun/star/awt/XBitmap.hpp>
+#include <com/sun/star/drawing/Hatch.hpp>
+#include <com/sun/star/embed/ElementModes.hpp>
+
+#include <sax/tools/converter.hxx>
+#include <sfx2/docfile.hxx>
+#include <rtl/ustrbuf.hxx>
+#include <xmloff/xmlnamespace.hxx>
+#include <xmloff/namespacemap.hxx>
+
+#include <xmloff/xmltoken.hxx>
+#include <xmloff/xmlmetae.hxx>
+#include <xmloff/DashStyle.hxx>
+#include <xmloff/GradientStyle.hxx>
+#include <xmloff/HatchStyle.hxx>
+#include <xmloff/ImageStyle.hxx>
+#include <xmloff/MarkerStyle.hxx>
+#include <com/sun/star/embed/XTransactedObject.hpp>
+#include <comphelper/processfactory.hxx>
+#include <unotools/streamwrap.hxx>
+#include <svx/xmlgrhlp.hxx>
+
+#include <xmlxtexp.hxx>
+
+#include <comphelper/storagehelper.hxx>
+#include <memory>
+
+using namespace com::sun::star;
+using namespace com::sun::star::container;
+using namespace com::sun::star::document;
+using namespace com::sun::star::uno;
+using namespace com::sun::star::awt;
+using namespace com::sun::star::lang;
+using namespace com::sun::star::xml::sax;
+using namespace ::xmloff::token;
+using namespace cppu;
+
+using com::sun::star::embed::XTransactedObject;
+
+namespace {
+
+class SvxXMLTableEntryExporter
+{
+public:
+ explicit SvxXMLTableEntryExporter( SvXMLExport& rExport ) : mrExport( rExport ) {}
+ virtual ~SvxXMLTableEntryExporter();
+
+ virtual void exportEntry( const OUString& rStrName, const Any& rValue ) = 0;
+
+protected:
+ SvXMLExport& mrExport;
+};
+
+class SvxXMLColorEntryExporter : public SvxXMLTableEntryExporter
+{
+public:
+ explicit SvxXMLColorEntryExporter( SvXMLExport& rExport );
+
+ virtual void exportEntry( const OUString& rStrName, const Any& rValue ) override;
+};
+
+class SvxXMLLineEndEntryExporter : public SvxXMLTableEntryExporter
+{
+public:
+ explicit SvxXMLLineEndEntryExporter( SvXMLExport& rExport );
+
+ virtual void exportEntry( const OUString& rStrName, const Any& rValue ) override;
+private:
+ XMLMarkerStyleExport maMarkerStyle;
+};
+
+class SvxXMLDashEntryExporter : public SvxXMLTableEntryExporter
+{
+public:
+ explicit SvxXMLDashEntryExporter( SvXMLExport& rExport );
+
+ virtual void exportEntry( const OUString& rStrName, const Any& rValue ) override;
+
+private:
+ XMLDashStyleExport maDashStyle;
+};
+
+class SvxXMLHatchEntryExporter : public SvxXMLTableEntryExporter
+{
+public:
+ explicit SvxXMLHatchEntryExporter( SvXMLExport& rExport );
+
+ virtual void exportEntry( const OUString& rStrName, const Any& rValue ) override;
+private:
+ XMLHatchStyleExport maHatchStyle;
+};
+
+class SvxXMLGradientEntryExporter : public SvxXMLTableEntryExporter
+{
+public:
+ explicit SvxXMLGradientEntryExporter( SvXMLExport& rExport );
+
+ virtual void exportEntry( const OUString& rStrName, const Any& rValue ) override;
+private:
+ XMLGradientStyleExport maGradientStyle;
+};
+
+class SvxXMLBitmapEntryExporter : public SvxXMLTableEntryExporter
+{
+public:
+ explicit SvxXMLBitmapEntryExporter( SvXMLExport& rExport );
+
+ virtual void exportEntry( const OUString& rStrName, const Any& rValue ) override;
+};
+
+}
+
+SvxXMLXTableExportComponent::SvxXMLXTableExportComponent(
+ const css::uno::Reference< css::uno::XComponentContext >& rContext,
+ const uno::Reference<xml::sax::XDocumentHandler> & rHandler,
+ const uno::Reference<container::XNameContainer >& xTable,
+ uno::Reference<document::XGraphicStorageHandler> const & xGraphicStorageHandler)
+: SvXMLExport(rContext, "", /*rFileName*/"", rHandler, nullptr, FieldUnit::MM_100TH, SvXMLExportFlags::NONE),
+ mxTable( xTable )
+{
+
+ GetNamespaceMap_().Add( GetXMLToken(XML_NP_OOO), GetXMLToken(XML_N_OOO), XML_NAMESPACE_OOO );
+ GetNamespaceMap_().Add( GetXMLToken(XML_NP_OFFICE), GetXMLToken(XML_N_OFFICE), XML_NAMESPACE_OFFICE );
+ GetNamespaceMap_().Add( GetXMLToken(XML_NP_DRAW), GetXMLToken(XML_N_DRAW), XML_NAMESPACE_DRAW );
+ GetNamespaceMap_().Add( GetXMLToken(XML_NP_XLINK), GetXMLToken(XML_N_XLINK), XML_NAMESPACE_XLINK );
+ GetNamespaceMap_().Add( GetXMLToken(XML_NP_SVG), GetXMLToken(XML_N_SVG), XML_NAMESPACE_SVG );
+ SetGraphicStorageHandler(xGraphicStorageHandler);
+}
+
+SvxXMLXTableExportComponent::~SvxXMLXTableExportComponent()
+{
+}
+
+static void initializeStreamMetadata( const uno::Reference< uno::XInterface > &xOut )
+{
+ uno::Reference< beans::XPropertySet > xProps( xOut, uno::UNO_QUERY );
+ if( !xProps.is() )
+ {
+ OSL_FAIL( "Missing stream metadata interface" );
+ return;
+ }
+
+ try
+ {
+ xProps->setPropertyValue("MediaType", uno::Any( OUString( "text/xml" ) ) );
+
+ // use stock encryption
+ xProps->setPropertyValue("UseCommonStoragePasswordEncryption", uno::Any( true ) );
+ } catch ( const uno::Exception & )
+ {
+ TOOLS_WARN_EXCEPTION("svx", "exception setting stream metadata");
+ }
+}
+
+static void createStorageStream( uno::Reference < io::XOutputStream > *xOut,
+ rtl::Reference<SvXMLGraphicHelper>& rxGraphicHelper,
+ const uno::Reference < embed::XStorage >& xSubStorage )
+{
+ uno::Reference < io::XStream > xStream = xSubStorage->openStreamElement(
+ "Content.xml",
+ embed::ElementModes::WRITE );
+ rxGraphicHelper = SvXMLGraphicHelper::Create( xSubStorage, SvXMLGraphicHelperMode::Write );
+ initializeStreamMetadata( xStream );
+ *xOut = xStream->getOutputStream();
+}
+
+bool SvxXMLXTableExportComponent::save(
+ const OUString& rURL,
+ const uno::Reference<container::XNameContainer >& xTable,
+ const uno::Reference<embed::XStorage >& xStorage,
+ OUString *pOptName )
+{
+ bool bRet = false;
+ std::unique_ptr<SfxMedium> pMedium;
+ rtl::Reference<SvXMLGraphicHelper> xGraphicHelper;
+
+ INetURLObject aURLObj( rURL );
+ bool bToStorage = aURLObj.GetProtocol() == INetProtocol::NotValid; // a relative path
+ bool bSaveAsStorage = xTable->getElementType() == cppu::UnoType<awt::XBitmap>::get();
+
+ if( pOptName )
+ *pOptName = rURL;
+
+ try
+ {
+ uno::Reference< uno::XComponentContext> xContext( ::comphelper::getProcessComponentContext() );
+
+ uno::Reference< xml::sax::XWriter > xWriter = xml::sax::Writer::create( xContext );
+
+ uno::Reference < io::XStream > xStream;
+ uno::Reference < io::XOutputStream > xOut;
+ uno::Reference<embed::XStorage > xSubStorage;
+ uno::Reference<XGraphicStorageHandler> xGraphicStorageHandler;
+ const sal_Int32 eCreate = embed::ElementModes::WRITE | embed::ElementModes::TRUNCATE;
+
+ if( !bToStorage || !xStorage.is() )
+ { // local URL -> SfxMedium route
+ if( bSaveAsStorage )
+ xSubStorage = ::comphelper::OStorageHelper::GetStorageFromURL( rURL, eCreate );
+ else
+ {
+ pMedium.reset(new SfxMedium( rURL, StreamMode::WRITE | StreamMode::TRUNC ));
+
+ SvStream* pStream = pMedium->GetOutStream();
+ if( !pStream )
+ {
+ OSL_FAIL( "no output stream!" );
+ return false;
+ }
+
+ xOut = new utl::OOutputStreamWrapper( *pStream );
+ }
+ }
+ else // save into the xSubStorage
+ {
+ OUString aPath = rURL;
+
+ if( bSaveAsStorage )
+ {
+ try {
+ xSubStorage = xStorage->openStorageElement( aPath, eCreate );
+ } catch (uno::Exception &) {
+ OSL_FAIL( "no output storage!" );
+ return false;
+ }
+ }
+ else
+ {
+ aPath += ".xml";
+ try {
+ xStream = xStorage->openStreamElement( aPath, eCreate );
+ if( !xStream.is() )
+ return false;
+ initializeStreamMetadata( xStream );
+ xOut = xStream->getOutputStream();
+ } catch (uno::Exception &) {
+ OSL_FAIL( "no output stream!" );
+ return false;
+ }
+ if( pOptName )
+ *pOptName = aPath;
+ }
+ }
+
+ if( !xOut.is() && xSubStorage.is() )
+ createStorageStream( &xOut, xGraphicHelper, xSubStorage );
+ if( !xOut.is() )
+ return false;
+
+ xWriter->setOutputStream( xOut );
+ if( xGraphicHelper.is() )
+ xGraphicStorageHandler = xGraphicHelper.get();
+
+ // Finally do the export
+ rtl::Reference< SvxXMLXTableExportComponent > xExporter( new SvxXMLXTableExportComponent( xContext, xWriter, xTable, xGraphicStorageHandler ) );
+ bRet = xExporter->exportTable();
+
+ if( xGraphicHelper )
+ xGraphicHelper->dispose();
+ xGraphicHelper.clear();
+
+ if( xSubStorage.is() )
+ {
+ uno::Reference< XTransactedObject > xTrans( xSubStorage, UNO_QUERY );
+ if( xTrans.is() )
+ xTrans->commit();
+
+ xSubStorage->dispose();
+ }
+ }
+ catch( uno::Exception& )
+ {
+ bRet = false;
+ }
+
+ if( pMedium )
+ pMedium->Commit();
+
+ return bRet;
+}
+
+bool SvxXMLXTableExportComponent::exportTable() noexcept
+{
+ bool bRet = false;
+
+ try
+ {
+ GetDocHandler()->startDocument();
+
+ addChaffWhenEncryptedStorage();
+
+ // export namespaces
+ sal_uInt16 nPos = GetNamespaceMap().GetFirstKey();
+ while( USHRT_MAX != nPos )
+ {
+ GetAttrList().AddAttribute( GetNamespaceMap().GetAttrNameByKey( nPos ), GetNamespaceMap().GetNameByKey( nPos ) );
+ nPos = GetNamespaceMap().GetNextKey( nPos );
+ }
+
+ do
+ {
+ if( !mxTable.is() )
+ break;
+
+ char const* pEleName;
+ Type aExportType = mxTable->getElementType();
+ std::unique_ptr<SvxXMLTableEntryExporter> pExporter;
+
+ if( aExportType == cppu::UnoType<sal_Int32>::get() )
+ {
+ pExporter.reset(new SvxXMLColorEntryExporter(*this));
+ pEleName = "color-table";
+ }
+ else if( aExportType == cppu::UnoType< drawing::PolyPolygonBezierCoords >::get() )
+ {
+ pExporter.reset(new SvxXMLLineEndEntryExporter(*this));
+ pEleName = "marker-table";
+ }
+ else if( aExportType == cppu::UnoType< drawing::LineDash >::get() )
+ {
+ pExporter.reset(new SvxXMLDashEntryExporter(*this));
+ pEleName = "dash-table";
+ }
+ else if( aExportType == cppu::UnoType< drawing::Hatch >::get() )
+ {
+ pExporter.reset(new SvxXMLHatchEntryExporter(*this));
+ pEleName = "hatch-table";
+ }
+ else if( aExportType == cppu::UnoType< awt::Gradient >::get() )
+ {
+ pExporter.reset(new SvxXMLGradientEntryExporter(*this));
+ pEleName = "gradient-table";
+ }
+ else if( aExportType == cppu::UnoType<awt::XBitmap>::get())
+ {
+ pExporter.reset(new SvxXMLBitmapEntryExporter(*this));
+ pEleName = "bitmap-table";
+ }
+ else
+ {
+ OSL_FAIL( "unknown type for export");
+ break;
+ }
+
+ SvXMLElementExport aElem( *this, XML_NAMESPACE_OOO, pEleName, true, true );
+
+ const Sequence< OUString > aNames = mxTable->getElementNames();
+ Any aAny;
+
+ for( const OUString& rName : aNames )
+ {
+ aAny = mxTable->getByName( rName );
+ pExporter->exportEntry( rName, aAny );
+ }
+
+ bRet = true;
+ }
+ while(false);
+
+ GetDocHandler()->endDocument();
+ }
+ catch( Exception const& )
+ {
+ bRet = false;
+ }
+
+ return bRet;
+}
+
+// methods without content:
+void SvxXMLXTableExportComponent::ExportAutoStyles_() {}
+void SvxXMLXTableExportComponent::ExportMasterStyles_() {}
+void SvxXMLXTableExportComponent::ExportContent_() {}
+
+
+SvxXMLTableEntryExporter::~SvxXMLTableEntryExporter()
+{
+}
+
+
+SvxXMLColorEntryExporter::SvxXMLColorEntryExporter( SvXMLExport& rExport )
+: SvxXMLTableEntryExporter( rExport )
+{
+}
+
+void SvxXMLColorEntryExporter::exportEntry( const OUString& rStrName, const Any& rValue )
+{
+ mrExport.AddAttribute( XML_NAMESPACE_DRAW, XML_NAME, rStrName );
+
+ sal_Int32 nColor = 0;
+ rValue >>= nColor;
+
+ OUStringBuffer aOut;
+ ::sax::Converter::convertColor( aOut, nColor );
+ mrExport.AddAttribute( XML_NAMESPACE_DRAW, XML_COLOR, aOut.makeStringAndClear() );
+
+ SvXMLElementExport aElem( mrExport, XML_NAMESPACE_DRAW, XML_COLOR, true, true );
+}
+
+
+SvxXMLLineEndEntryExporter::SvxXMLLineEndEntryExporter( SvXMLExport& rExport )
+: SvxXMLTableEntryExporter( rExport ), maMarkerStyle( rExport )
+{
+}
+
+void SvxXMLLineEndEntryExporter::exportEntry( const OUString& rStrName, const Any& rValue )
+{
+ maMarkerStyle.exportXML( rStrName, rValue );
+}
+
+
+SvxXMLDashEntryExporter::SvxXMLDashEntryExporter( SvXMLExport& rExport )
+: SvxXMLTableEntryExporter( rExport ), maDashStyle( rExport )
+{
+}
+
+void SvxXMLDashEntryExporter::exportEntry( const OUString& rStrName, const Any& rValue )
+{
+ maDashStyle.exportXML( rStrName, rValue );
+}
+
+
+SvxXMLHatchEntryExporter::SvxXMLHatchEntryExporter( SvXMLExport& rExport )
+: SvxXMLTableEntryExporter( rExport ), maHatchStyle( rExport )
+{
+}
+
+void SvxXMLHatchEntryExporter::exportEntry( const OUString& rStrName, const Any& rValue )
+{
+ maHatchStyle.exportXML( rStrName, rValue );
+}
+
+
+SvxXMLGradientEntryExporter::SvxXMLGradientEntryExporter( SvXMLExport& rExport )
+: SvxXMLTableEntryExporter( rExport ), maGradientStyle( rExport )
+{
+}
+
+void SvxXMLGradientEntryExporter::exportEntry( const OUString& rStrName, const Any& rValue )
+{
+ maGradientStyle.exportXML( rStrName, rValue );
+}
+
+
+SvxXMLBitmapEntryExporter::SvxXMLBitmapEntryExporter( SvXMLExport& rExport )
+: SvxXMLTableEntryExporter( rExport )
+{
+}
+
+void SvxXMLBitmapEntryExporter::exportEntry( const OUString& rStrName, const Any& rValue )
+{
+ XMLImageStyle::exportXML(rStrName, rValue, mrExport);
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/xml/xmlxtimp.cxx b/svx/source/xml/xmlxtimp.cxx
new file mode 100644
index 000000000..9fd5481d5
--- /dev/null
+++ b/svx/source/xml/xmlxtimp.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 <tools/urlobj.hxx>
+#include <com/sun/star/document/XGraphicStorageHandler.hpp>
+#include <com/sun/star/embed/ElementModes.hpp>
+#include <com/sun/star/container/XNameContainer.hpp>
+#include <com/sun/star/xml/sax/XDocumentHandler.hpp>
+#include <com/sun/star/drawing/PolyPolygonBezierCoords.hpp>
+#include <com/sun/star/drawing/LineDash.hpp>
+#include <com/sun/star/awt/Gradient.hpp>
+#include <com/sun/star/awt/XBitmap.hpp>
+#include <com/sun/star/drawing/Hatch.hpp>
+#include <com/sun/star/io/XSeekable.hpp>
+#include <comphelper/processfactory.hxx>
+#include <comphelper/storagehelper.hxx>
+#include <sax/tools/converter.hxx>
+#include <sfx2/docfile.hxx>
+#include <xmloff/xmlnamespace.hxx>
+#include <xmloff/namespacemap.hxx>
+
+#include <xmloff/xmltoken.hxx>
+#include <xmloff/DashStyle.hxx>
+#include <xmloff/GradientStyle.hxx>
+#include <xmloff/HatchStyle.hxx>
+#include <xmloff/ImageStyle.hxx>
+#include <xmloff/MarkerStyle.hxx>
+#include <xmloff/xmlictxt.hxx>
+#include <svx/xmlgrhlp.hxx>
+
+#include <xmlxtimp.hxx>
+#include <tools/diagnose_ex.h>
+
+using namespace com::sun::star;
+using namespace com::sun::star::container;
+using namespace com::sun::star::document;
+using namespace com::sun::star::uno;
+using namespace com::sun::star::awt;
+using namespace com::sun::star::lang;
+using namespace com::sun::star::xml::sax;
+using namespace ::xmloff::token;
+using namespace cppu;
+
+namespace {
+
+enum class SvxXMLTableImportContextEnum { Color, Marker, Dash, Hatch, Gradient, Bitmap };
+
+class SvxXMLTableImportContext : public SvXMLImportContext
+{
+public:
+ SvxXMLTableImportContext( SvXMLImport& rImport, SvxXMLTableImportContextEnum eContext, const uno::Reference< XNameContainer >& xTable,
+ bool bOOoFormat );
+
+ virtual css::uno::Reference< css::xml::sax::XFastContextHandler > SAL_CALL
+ createFastChildContext(sal_Int32 Element,
+ const css::uno::Reference< css::xml::sax::XFastAttributeList > & Attribs) override;
+
+protected:
+ static void importColor( const uno::Reference< XFastAttributeList >& xAttrList, Any& rAny, OUString& rName );
+ void importMarker( const uno::Reference< XFastAttributeList >& xAttrList, Any& rAny, OUString& rName );
+ void importDash( const uno::Reference< XFastAttributeList >& xAttrList, Any& rAny, OUString& rName );
+ void importHatch( const uno::Reference< XFastAttributeList >& xAttrList, Any& rAny, OUString& rName );
+ void importGradient( const uno::Reference< XFastAttributeList >& xAttrList, Any& rAny, OUString& rName );
+ void importBitmap( const uno::Reference< XFastAttributeList >& xAttrList, Any& rAny, OUString& rName );
+
+private:
+ uno::Reference< XNameContainer > mxTable;
+ SvxXMLTableImportContextEnum meContext;
+ bool mbOOoFormat;
+};
+
+}
+
+SvxXMLTableImportContext::SvxXMLTableImportContext( SvXMLImport& rImport, SvxXMLTableImportContextEnum eContext, const uno::Reference< XNameContainer >& xTable, bool bOOoFormat )
+: SvXMLImportContext( rImport ), mxTable( xTable ), meContext( eContext ),
+ mbOOoFormat( bOOoFormat )
+{
+}
+
+css::uno::Reference< css::xml::sax::XFastContextHandler >
+ SvxXMLTableImportContext::createFastChildContext(sal_Int32 nElement,
+ const css::uno::Reference< css::xml::sax::XFastAttributeList > & rAttrList)
+{
+ if( !(IsTokenInNamespace(nElement, XML_NAMESPACE_DRAW) ||
+ IsTokenInNamespace(nElement, XML_NAMESPACE_DRAW_OOO) ))
+ return nullptr;
+
+ std::vector<std::pair<sal_Int32, OString>> aTmpAttrList;
+ for (auto& aIter : sax_fastparser::castToFastAttributeList( rAttrList ))
+ aTmpAttrList.push_back({aIter.getToken(), OString(aIter.toCString())});
+ if( mbOOoFormat &&
+ (SvxXMLTableImportContextEnum::Dash == meContext || SvxXMLTableImportContextEnum::Hatch == meContext ||
+ SvxXMLTableImportContextEnum::Bitmap == meContext) )
+ {
+ for( auto & aIter : aTmpAttrList )
+ {
+ sal_Int32 aLocalAttr = aIter.first & TOKEN_MASK;
+ if( aIter.first == XML_ELEMENT(XLINK, XML_HREF) &&
+ SvxXMLTableImportContextEnum::Bitmap == meContext )
+ {
+ OString& rValue = aIter.second;
+ if( !rValue.isEmpty() && '#' == rValue[0] )
+ rValue = rValue.copy( 1 );
+ }
+ else if( (IsTokenInNamespace(aIter.first, XML_NAMESPACE_DRAW) || IsTokenInNamespace(aIter.first, XML_NAMESPACE_DRAW_OOO)) &&
+ ( ( SvxXMLTableImportContextEnum::Dash == meContext &&
+ ( aLocalAttr == XML_DOTS1_LENGTH ||
+ aLocalAttr == XML_DOTS2_LENGTH ||
+ aLocalAttr == XML_DISTANCE ) ) ||
+ ( SvxXMLTableImportContextEnum::Hatch == meContext &&
+ ( aLocalAttr == XML_DISTANCE ) ) ) )
+ {
+ OString& rValue = aIter.second;
+ sal_Int32 nPos = rValue.getLength();
+ while( nPos && rValue[nPos-1] <= ' ' )
+ --nPos;
+ if( nPos > 2 &&
+ ('c'==rValue[nPos-2] || 'C'==rValue[nPos-2]) &&
+ ('h'==rValue[nPos-1] || 'H'==rValue[nPos-1]) )
+ {
+ rValue = rValue.copy( 0, nPos-2 );
+ }
+ }
+ }
+ }
+ try
+ {
+ rtl::Reference<sax_fastparser::FastAttributeList> xFastList = new sax_fastparser::FastAttributeList(nullptr);
+ for (const auto& aIter : aTmpAttrList)
+ xFastList->add(aIter.first, aIter.second);
+
+ Any aAny;
+ OUString aName;
+
+ switch( meContext )
+ {
+ case SvxXMLTableImportContextEnum::Color:
+ importColor( xFastList, aAny, aName );
+ break;
+ case SvxXMLTableImportContextEnum::Marker:
+ importMarker( xFastList, aAny, aName );
+ break;
+ case SvxXMLTableImportContextEnum::Dash:
+ importDash( xFastList, aAny, aName );
+ break;
+ case SvxXMLTableImportContextEnum::Hatch:
+ importHatch( xFastList, aAny, aName );
+ break;
+ case SvxXMLTableImportContextEnum::Gradient:
+ importGradient( xFastList, aAny, aName );
+ break;
+ case SvxXMLTableImportContextEnum::Bitmap:
+ importBitmap( xFastList, aAny, aName );
+ break;
+ }
+
+ if( !aName.isEmpty() && aAny.hasValue() )
+ {
+ if( mxTable->hasByName( aName ) )
+ {
+ mxTable->replaceByName( aName, aAny );
+ }
+ else
+ {
+ mxTable->insertByName( aName, aAny );
+ }
+ }
+ }
+ catch (const uno::Exception&)
+ {
+ DBG_UNHANDLED_EXCEPTION("svx");
+ }
+ return new SvXMLImportContext( GetImport() );
+}
+
+void SvxXMLTableImportContext::importColor( const uno::Reference< XFastAttributeList >& xAttrList, Any& rAny, OUString& rName )
+{
+ for (auto& aIter : sax_fastparser::castToFastAttributeList( xAttrList ))
+ {
+ switch (aIter.getToken())
+ {
+ case XML_ELEMENT(DRAW, XML_NAME):
+ case XML_ELEMENT(DRAW_OOO, XML_NAME):
+ rName = aIter.toString();
+ break;
+ case XML_ELEMENT(DRAW, XML_COLOR):
+ case XML_ELEMENT(DRAW_OOO, XML_COLOR):
+ {
+ sal_Int32 nColor(0);
+ ::sax::Converter::convertColor(nColor, aIter.toView());
+ rAny <<= nColor;
+ break;
+ }
+ default:
+ XMLOFF_WARN_UNKNOWN("xmloff", aIter);
+ }
+ }
+}
+
+void SvxXMLTableImportContext::importMarker( const uno::Reference< XFastAttributeList >& xAttrList, Any& rAny, OUString& rName )
+{
+ try
+ {
+ XMLMarkerStyleImport aMarkerStyle( GetImport() );
+ aMarkerStyle.importXML( xAttrList, rAny, rName );
+ }
+ catch (const Exception&)
+ {
+ TOOLS_WARN_EXCEPTION("svx", "");
+ }
+}
+
+void SvxXMLTableImportContext::importDash( const uno::Reference< XFastAttributeList >& xAttrList, Any& rAny, OUString& rName )
+{
+ try
+ {
+ XMLDashStyleImport aDashStyle( GetImport() );
+ aDashStyle.importXML( xAttrList, rAny, rName );
+ }
+ catch (const Exception&)
+ {
+ TOOLS_WARN_EXCEPTION("svx", "");
+ }
+}
+
+void SvxXMLTableImportContext::importHatch( const uno::Reference< XFastAttributeList >& xAttrList, Any& rAny, OUString& rName )
+{
+ try
+ {
+ XMLHatchStyleImport aHatchStyle( GetImport() );
+ aHatchStyle.importXML( xAttrList, rAny, rName );
+ }
+ catch (const Exception&)
+ {
+ TOOLS_WARN_EXCEPTION("svx", "");
+ }
+}
+
+void SvxXMLTableImportContext::importGradient( const uno::Reference< XFastAttributeList >& xAttrList, Any& rAny, OUString& rName )
+{
+ try
+ {
+ XMLGradientStyleImport aGradientStyle( GetImport() );
+ aGradientStyle.importXML( xAttrList, rAny, rName );
+ }
+ catch (const Exception&)
+ {
+ TOOLS_WARN_EXCEPTION("svx", "");
+ }
+}
+
+void SvxXMLTableImportContext::importBitmap( const uno::Reference< XFastAttributeList >& xAttrList, Any& rAny, OUString& rName )
+{
+ try
+ {
+ uno::Any aGraphicAny;
+ XMLImageStyle::importXML(xAttrList, aGraphicAny, rName, GetImport());
+ if (aGraphicAny.has<uno::Reference<graphic::XGraphic>>())
+ {
+ auto xGraphic = aGraphicAny.get<uno::Reference<graphic::XGraphic>>();
+ uno::Reference<awt::XBitmap> xBitmap(xGraphic, uno::UNO_QUERY);
+ if (xBitmap.is())
+ rAny <<= xBitmap;
+ }
+ }
+ catch (const Exception&)
+ {
+ TOOLS_WARN_EXCEPTION("svx", "");
+ }
+}
+
+
+SvxXMLXTableImport::SvxXMLXTableImport(
+ const css::uno::Reference< css::uno::XComponentContext >& rContext,
+ const uno::Reference< XNameContainer > & rTable,
+ uno::Reference<XGraphicStorageHandler> const & xGraphicStorageHandler)
+: SvXMLImport(rContext, "", SvXMLImportFlags::NONE),
+ mrTable( rTable )
+{
+ SetGraphicStorageHandler(xGraphicStorageHandler);
+
+ GetNamespaceMap().Add( GetXMLToken(XML_NP_OOO), GetXMLToken(XML_N_OOO), XML_NAMESPACE_OOO);
+ GetNamespaceMap().Add( GetXMLToken(XML_NP_OFFICE), GetXMLToken(XML_N_OFFICE), XML_NAMESPACE_OFFICE);
+ GetNamespaceMap().Add( GetXMLToken(XML_NP_DRAW), GetXMLToken(XML_N_DRAW), XML_NAMESPACE_DRAW);
+ GetNamespaceMap().Add( GetXMLToken(XML_NP_XLINK), GetXMLToken(XML_N_XLINK), XML_NAMESPACE_XLINK);
+
+ GetNamespaceMap().Add( "__ooo", GetXMLToken(XML_N_OOO), XML_NAMESPACE_OOO );
+ GetNamespaceMap().Add( "__xlink", GetXMLToken(XML_N_XLINK), XML_NAMESPACE_XLINK );
+
+ // OOo namespaces for reading OOo 1.1 files
+ GetNamespaceMap().Add( "___office",
+ GetXMLToken(XML_N_OFFICE_OOO),
+ XML_NAMESPACE_OFFICE );
+ GetNamespaceMap().Add( "___draw",
+ GetXMLToken(XML_N_DRAW_OOO),
+ XML_NAMESPACE_DRAW );
+}
+
+SvxXMLXTableImport::~SvxXMLXTableImport() noexcept
+{
+}
+
+static void openStorageStream( xml::sax::InputSource *pParserInput,
+ rtl::Reference<SvXMLGraphicHelper>& rxGraphicHelper,
+ const uno::Reference < embed::XStorage >& xStorage )
+{
+ uno::Reference < io::XStream > xIStm( xStorage->openStreamElement( "Content.xml", embed::ElementModes::READ ), uno::UNO_SET_THROW );
+ pParserInput->aInputStream = xIStm->getInputStream();
+ rxGraphicHelper = SvXMLGraphicHelper::Create( xStorage, SvXMLGraphicHelperMode::Read );
+}
+
+bool SvxXMLXTableImport::load( const OUString &rPath, const OUString &rReferer,
+ const uno::Reference < embed::XStorage > &xStorage,
+ const uno::Reference< XNameContainer >& xTable,
+ bool *bOptLoadedFromStorage ) noexcept
+{
+ bool bRet = true;
+ rtl::Reference<SvXMLGraphicHelper> xGraphicHelper;
+
+ INetURLObject aURLObj( rPath );
+ bool bUseStorage = aURLObj.GetProtocol() == INetProtocol::NotValid; // a relative path
+
+ try
+ {
+ uno::Reference<uno::XComponentContext> xContext( ::comphelper::getProcessComponentContext() );
+
+ xml::sax::InputSource aParserInput;
+ comphelper::LifecycleProxy aNasty;
+
+ if( !bUseStorage || !xStorage.is() )
+ {
+ SfxMedium aMedium( rPath, rReferer, StreamMode::READ | StreamMode::NOCREATE );
+ aParserInput.sSystemId = aMedium.GetName();
+
+ if( aMedium.IsStorage() )
+ {
+ uno::Reference < embed::XStorage > xMediumStorage( aMedium.GetStorage( false ), uno::UNO_SET_THROW );
+ openStorageStream( &aParserInput, xGraphicHelper, xMediumStorage );
+ }
+ else
+ aParserInput.aInputStream = aMedium.GetInputStream();
+ }
+ else // relative URL into a storage
+ {
+ uno::Reference< embed::XStorage > xSubStorage;
+ try
+ {
+ xSubStorage = comphelper::OStorageHelper::GetStorageAtPath(
+ xStorage, rPath, embed::ElementModes::READ, aNasty );
+ }
+ catch (const uno::Exception&)
+ {
+ }
+ if( xSubStorage.is() )
+ openStorageStream( &aParserInput, xGraphicHelper, xSubStorage );
+ else
+ {
+ css::uno::Reference< css::io::XStream > xStream = comphelper::OStorageHelper::GetStreamAtPath(
+ xStorage, rPath, embed::ElementModes::READ, aNasty );
+ if( !xStream.is() )
+ return false;
+ aParserInput.aInputStream = xStream->getInputStream();
+ }
+ if( bOptLoadedFromStorage )
+ *bOptLoadedFromStorage = true;
+ }
+
+ uno::Reference<XGraphicStorageHandler> xGraphicStorageHandler;
+ if (xGraphicHelper.is())
+ xGraphicStorageHandler = xGraphicHelper.get();
+
+ try
+ {
+ uno::Reference< io::XSeekable > xSeek( aParserInput.aInputStream, uno::UNO_QUERY_THROW );
+ xSeek->seek( 0 );
+ }
+ catch (const uno::Exception&)
+ {
+ }
+
+ rtl::Reference<SvxXMLXTableImport> xImport(new SvxXMLXTableImport(xContext, xTable, xGraphicStorageHandler));
+ xImport->parseStream( aParserInput );
+
+ if( xGraphicHelper )
+ xGraphicHelper->dispose();
+ }
+ catch (...)
+ {
+// thrown each time you load a document with property tables that are not
+// on the current machine. FIXME: would be better to check a file exists
+// before importing ...
+ bRet = false;
+ }
+
+ return bRet;
+}
+
+SvXMLImportContext *SvxXMLXTableImport::CreateFastContext( sal_Int32 nElement,
+ const ::css::uno::Reference< ::css::xml::sax::XFastAttributeList >& /*xAttrList*/ )
+{
+ if( IsTokenInNamespace(nElement, XML_NAMESPACE_OOO) ||
+ IsTokenInNamespace(nElement, XML_NAMESPACE_OFFICE) ||
+ IsTokenInNamespace(nElement, XML_NAMESPACE_OFFICE_OOO) )
+ {
+ bool bOOoFormat = IsTokenInNamespace(nElement, XML_NAMESPACE_OFFICE) ||
+ IsTokenInNamespace(nElement, XML_NAMESPACE_OFFICE_OOO);
+ Type aType = mrTable->getElementType();
+ sal_Int32 nToken = nElement & TOKEN_MASK;
+
+ if ( nToken == XML_COLOR_TABLE )
+ {
+ if( aType == ::cppu::UnoType<sal_Int32>::get() )
+ return new SvxXMLTableImportContext( *this, SvxXMLTableImportContextEnum::Color, mrTable, bOOoFormat );
+ }
+ else if ( nToken == XML_MARKER_TABLE )
+ {
+ if( aType == cppu::UnoType<drawing::PolyPolygonBezierCoords>::get())
+ return new SvxXMLTableImportContext( *this, SvxXMLTableImportContextEnum::Marker, mrTable, bOOoFormat );
+ }
+ else if ( nToken == XML_DASH_TABLE )
+ {
+ if( aType == cppu::UnoType<drawing::LineDash>::get())
+ return new SvxXMLTableImportContext( *this, SvxXMLTableImportContextEnum::Dash, mrTable, bOOoFormat );
+ }
+ else if ( nToken == XML_HATCH_TABLE )
+ {
+ if( aType == cppu::UnoType<drawing::Hatch>::get())
+ return new SvxXMLTableImportContext( *this, SvxXMLTableImportContextEnum::Hatch, mrTable, bOOoFormat );
+ }
+ else if ( nToken == XML_GRADIENT_TABLE )
+ {
+ if( aType == cppu::UnoType<awt::Gradient>::get())
+ return new SvxXMLTableImportContext( *this, SvxXMLTableImportContextEnum::Gradient, mrTable, bOOoFormat );
+ }
+ else if ( nToken == XML_BITMAP_TABLE )
+ {
+ if( aType == ::cppu::UnoType<awt::XBitmap>::get())
+ return new SvxXMLTableImportContext( *this, SvxXMLTableImportContextEnum::Bitmap, mrTable, bOOoFormat );
+ }
+ }
+
+ return nullptr;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/xoutdev/XPropertyEntry.cxx b/svx/source/xoutdev/XPropertyEntry.cxx
new file mode 100644
index 000000000..0c3d140e8
--- /dev/null
+++ b/svx/source/xoutdev/XPropertyEntry.cxx
@@ -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/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * 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/XPropertyEntry.hxx>
+
+XPropertyEntry::XPropertyEntry(const OUString& rPropEntryName)
+ : maPropEntryName(rPropEntryName)
+{
+}
+
+XPropertyEntry::~XPropertyEntry() {}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/xoutdev/_xoutbmp.cxx b/svx/source/xoutdev/_xoutbmp.cxx
new file mode 100644
index 000000000..cb690df32
--- /dev/null
+++ b/svx/source/xoutdev/_xoutbmp.cxx
@@ -0,0 +1,389 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <sal/config.h>
+#include <sal/log.hxx>
+
+#include <comphelper/base64.hxx>
+#include <comphelper/graphicmimetype.hxx>
+#include <tools/debug.hxx>
+#include <vcl/virdev.hxx>
+#include <sfx2/docfile.hxx>
+#include <svx/xoutbmp.hxx>
+#include <vcl/graphicfilter.hxx>
+#include <vcl/cvtgrf.hxx>
+#include <memory>
+
+constexpr OUStringLiteral FORMAT_BMP = u"bmp";
+constexpr OUStringLiteral FORMAT_GIF = u"gif";
+constexpr OUStringLiteral FORMAT_JPG = u"jpg";
+constexpr OUStringLiteral FORMAT_PNG = u"png";
+constexpr OUStringLiteral FORMAT_WEBP = u"webp";
+
+using namespace com::sun::star;
+
+Animation XOutBitmap::MirrorAnimation( const Animation& rAnimation, bool bHMirr, bool bVMirr )
+{
+ Animation aNewAnim( rAnimation );
+
+ if( bHMirr || bVMirr )
+ {
+ const Size& rGlobalSize = aNewAnim.GetDisplaySizePixel();
+ BmpMirrorFlags nMirrorFlags = BmpMirrorFlags::NONE;
+
+ if( bHMirr )
+ nMirrorFlags |= BmpMirrorFlags::Horizontal;
+
+ if( bVMirr )
+ nMirrorFlags |= BmpMirrorFlags::Vertical;
+
+ for( sal_uInt16 i = 0, nCount = aNewAnim.Count(); i < nCount; i++ )
+ {
+ AnimationBitmap aAnimationBitmap( aNewAnim.Get( i ) );
+
+ // mirror the BitmapEx
+ aAnimationBitmap.maBitmapEx.Mirror( nMirrorFlags );
+
+ // Adjust the positions inside the whole bitmap
+ if( bHMirr )
+ aAnimationBitmap.maPositionPixel.setX(rGlobalSize.Width() - aAnimationBitmap.maPositionPixel.X() -
+ aAnimationBitmap.maSizePixel.Width());
+
+ if( bVMirr )
+ aAnimationBitmap.maPositionPixel.setY(rGlobalSize.Height() - aAnimationBitmap.maPositionPixel.Y() -
+ aAnimationBitmap.maSizePixel.Height());
+
+ aNewAnim.Replace(aAnimationBitmap, i);
+ }
+ }
+
+ return aNewAnim;
+}
+
+Graphic XOutBitmap::MirrorGraphic( const Graphic& rGraphic, const BmpMirrorFlags nMirrorFlags )
+{
+ Graphic aRetGraphic;
+
+ if( nMirrorFlags != BmpMirrorFlags::NONE )
+ {
+ if( rGraphic.IsAnimated() )
+ {
+ aRetGraphic = MirrorAnimation( rGraphic.GetAnimation(),
+ bool( nMirrorFlags & BmpMirrorFlags::Horizontal ),
+ bool( nMirrorFlags & BmpMirrorFlags::Vertical ) );
+ }
+ else
+ {
+ BitmapEx aBmp( rGraphic.GetBitmapEx() );
+ aBmp.Mirror( nMirrorFlags );
+ aRetGraphic = aBmp;
+ }
+ }
+ else
+ aRetGraphic = rGraphic;
+
+ return aRetGraphic;
+}
+
+ErrCode XOutBitmap::WriteGraphic( const Graphic& rGraphic, OUString& rFileName,
+ const OUString& rFilterName, const XOutFlags nFlags,
+ const Size* pMtfSize_100TH_MM,
+ const css::uno::Sequence< css::beans::PropertyValue >* pFilterData )
+{
+ if( rGraphic.GetType() == GraphicType::NONE )
+ return ERRCODE_NONE;
+
+ INetURLObject aURL( rFileName );
+ Graphic aGraphic;
+ OUString aExt;
+ GraphicFilter& rFilter = GraphicFilter::GetGraphicFilter();
+ ErrCode nErr = ERRCODE_GRFILTER_FILTERERROR;
+ bool bTransparent = rGraphic.IsTransparent(), bAnimated = rGraphic.IsAnimated();
+
+ DBG_ASSERT( aURL.GetProtocol() != INetProtocol::NotValid, "XOutBitmap::WriteGraphic(...): invalid URL" );
+
+ // calculate correct file name
+ if( !( nFlags & XOutFlags::DontExpandFilename ) )
+ {
+ OUString aStr( OUString::number( rGraphic.GetChecksum(), 16 ) );
+ if ( aStr[0] == '-' )
+ aStr = OUString::Concat("m") + aStr.subView(1);
+ OUString aName = aURL.getBase() + "_" + aURL.getExtension() + "_" + aStr;
+ aURL.setBase( aName );
+ }
+
+ // #i121128# use shortcut to write Vector Graphic Data data in original form (if possible)
+ auto const & rVectorGraphicDataPtr(rGraphic.getVectorGraphicData());
+
+ if (rVectorGraphicDataPtr && rVectorGraphicDataPtr->getBinaryDataContainer().getSize())
+ {
+ // Does the filter name match the original format?
+ const bool bIsSvg(rFilterName.equalsIgnoreAsciiCase("svg") && VectorGraphicDataType::Svg == rVectorGraphicDataPtr->getType());
+ const bool bIsWmf(rFilterName.equalsIgnoreAsciiCase("wmf") && VectorGraphicDataType::Wmf == rVectorGraphicDataPtr->getType());
+ bool bIsEmf(rFilterName.equalsIgnoreAsciiCase("emf") && VectorGraphicDataType::Emf == rVectorGraphicDataPtr->getType());
+ if (!bIsEmf)
+ {
+ bIsEmf = rFilterName.equalsIgnoreAsciiCase("emf") && rGraphic.GetGfxLink().IsEMF();
+ }
+ const bool bIsPdf(rFilterName.equalsIgnoreAsciiCase("pdf") && VectorGraphicDataType::Pdf == rVectorGraphicDataPtr->getType());
+
+ if (bIsSvg || bIsWmf || bIsEmf || bIsPdf)
+ {
+ if (!(nFlags & XOutFlags::DontAddExtension))
+ {
+ aURL.setExtension(rFilterName);
+ }
+
+ rFileName = aURL.GetMainURL(INetURLObject::DecodeMechanism::NONE);
+ SfxMedium aMedium(aURL.GetMainURL(INetURLObject::DecodeMechanism::NONE), StreamMode::WRITE | StreamMode::SHARE_DENYNONE | StreamMode::TRUNC);
+ SvStream* pOStm = aMedium.GetOutStream();
+
+ if (pOStm)
+ {
+ auto & rDataContainer = rVectorGraphicDataPtr->getBinaryDataContainer();
+ pOStm->WriteBytes(rDataContainer.getData(), rDataContainer.getSize());
+ aMedium.Commit();
+
+ if (!aMedium.GetError())
+ return ERRCODE_NONE;
+ }
+ }
+ }
+
+ if( ( nFlags & XOutFlags::UseNativeIfPossible ) &&
+ !( nFlags & XOutFlags::MirrorHorz ) &&
+ !( nFlags & XOutFlags::MirrorVert ) &&
+ ( rGraphic.GetType() != GraphicType::GdiMetafile ) && rGraphic.IsGfxLink() )
+ {
+ // try to write native link
+ const GfxLink aGfxLink( rGraphic.GetGfxLink() );
+
+ switch( aGfxLink.GetType() )
+ {
+ case GfxLinkType::NativeGif: aExt = FORMAT_GIF; break;
+
+ // #i15508# added BMP type for better exports (no call/trigger found, prob used in HTML export)
+ case GfxLinkType::NativeBmp: aExt = FORMAT_BMP; break;
+
+ case GfxLinkType::NativeJpg: aExt = FORMAT_JPG; break;
+ case GfxLinkType::NativePng: aExt = FORMAT_PNG; break;
+ case GfxLinkType::NativeWebp: aExt = FORMAT_WEBP; break;
+
+ default:
+ break;
+ }
+
+ // tdf#60684: use native format if possible but it must correspond to filter name
+ // or no specific format has been required
+ // without this, you may save for example file with png extension but jpg content
+ if( !aExt.isEmpty() && (aExt == rFilterName || rFilterName.isEmpty()) )
+ {
+ if( !(nFlags & XOutFlags::DontAddExtension) )
+ aURL.setExtension( aExt );
+ rFileName = aURL.GetMainURL( INetURLObject::DecodeMechanism::NONE );
+
+ SfxMedium aMedium(aURL.GetMainURL(INetURLObject::DecodeMechanism::NONE), StreamMode::WRITE | StreamMode::SHARE_DENYNONE | StreamMode::TRUNC);
+ SvStream* pOStm = aMedium.GetOutStream();
+
+ if( pOStm && aGfxLink.GetDataSize() && aGfxLink.GetData() )
+ {
+ pOStm->WriteBytes(aGfxLink.GetData(), aGfxLink.GetDataSize());
+ aMedium.Commit();
+
+ if( !aMedium.GetError() )
+ return ERRCODE_NONE;
+ }
+ }
+ }
+
+ OUString aFilter( rFilterName );
+ bool bWriteTransGrf = ( aFilter.equalsIgnoreAsciiCase( "transgrf" ) ) ||
+ ( aFilter.equalsIgnoreAsciiCase( "gif" ) ) ||
+ ( nFlags & XOutFlags::UseGifIfPossible ) ||
+ ( ( nFlags & XOutFlags::UseGifIfSensible ) && ( bAnimated || bTransparent ) );
+
+ // get filter and extension
+ if( bWriteTransGrf )
+ aFilter = FORMAT_GIF;
+
+ sal_uInt16 nFilter = rFilter.GetExportFormatNumberForShortName( aFilter );
+
+ if( GRFILTER_FORMAT_NOTFOUND == nFilter )
+ {
+ nFilter = rFilter.GetExportFormatNumberForShortName( FORMAT_PNG );
+
+ if( GRFILTER_FORMAT_NOTFOUND == nFilter )
+ nFilter = rFilter.GetExportFormatNumberForShortName( FORMAT_BMP );
+ }
+
+ if( GRFILTER_FORMAT_NOTFOUND != nFilter )
+ {
+ aExt = rFilter.GetExportFormatShortName( nFilter ).toAsciiLowerCase();
+
+ if( bWriteTransGrf )
+ {
+ if( bAnimated )
+ aGraphic = rGraphic;
+ else
+ {
+ if( pMtfSize_100TH_MM && ( rGraphic.GetType() != GraphicType::Bitmap ) )
+ {
+ ScopedVclPtrInstance< VirtualDevice > pVDev;
+ const Size aSize(pVDev->LogicToPixel(*pMtfSize_100TH_MM, MapMode(MapUnit::Map100thMM)));
+
+ if( pVDev->SetOutputSizePixel( aSize ) )
+ {
+ const Wallpaper aWallpaper( pVDev->GetBackground() );
+ const Point aPt;
+
+ pVDev->SetBackground( Wallpaper( COL_BLACK ) );
+ pVDev->Erase();
+ rGraphic.Draw(*pVDev, aPt, aSize);
+
+ const Bitmap aBitmap( pVDev->GetBitmap( aPt, aSize ) );
+
+ pVDev->SetBackground( aWallpaper );
+ pVDev->Erase();
+ rGraphic.Draw(*pVDev, aPt, aSize);
+
+ pVDev->SetRasterOp( RasterOp::Xor );
+ pVDev->DrawBitmap( aPt, aSize, aBitmap );
+ aGraphic = BitmapEx( aBitmap, pVDev->GetBitmap( aPt, aSize ) );
+ }
+ else
+ aGraphic = rGraphic.GetBitmapEx();
+ }
+ else
+ aGraphic = rGraphic.GetBitmapEx();
+ }
+ }
+ else
+ {
+ if( pMtfSize_100TH_MM && ( rGraphic.GetType() != GraphicType::Bitmap ) )
+ {
+ ScopedVclPtrInstance< VirtualDevice > pVDev;
+ const Size aSize(pVDev->LogicToPixel(*pMtfSize_100TH_MM, MapMode(MapUnit::Map100thMM)));
+
+ if( pVDev->SetOutputSizePixel( aSize ) )
+ {
+ rGraphic.Draw(*pVDev, Point(), aSize);
+ aGraphic = BitmapEx(pVDev->GetBitmap(Point(), aSize));
+ }
+ else
+ aGraphic = rGraphic.GetBitmapEx();
+ }
+ else
+ aGraphic = rGraphic.GetBitmapEx();
+ }
+
+ // mirror?
+ if( ( nFlags & XOutFlags::MirrorHorz ) || ( nFlags & XOutFlags::MirrorVert ) )
+ {
+ BmpMirrorFlags nBmpMirrorFlags = BmpMirrorFlags::NONE;
+ if( nFlags & XOutFlags::MirrorHorz )
+ nBmpMirrorFlags |= BmpMirrorFlags::Horizontal;
+ if( nFlags & XOutFlags::MirrorVert )
+ nBmpMirrorFlags |= BmpMirrorFlags::Vertical;
+ aGraphic = MirrorGraphic( aGraphic, nBmpMirrorFlags );
+ }
+
+ if (aGraphic.GetType() != GraphicType::NONE)
+ {
+ if( !(nFlags & XOutFlags::DontAddExtension) )
+ aURL.setExtension( aExt );
+ rFileName = aURL.GetMainURL( INetURLObject::DecodeMechanism::NONE );
+ nErr = ExportGraphic( aGraphic, aURL, rFilter, nFilter, pFilterData );
+ }
+ }
+
+ return nErr;
+}
+
+bool XOutBitmap::GraphicToBase64(const Graphic& rGraphic, OUString& rOUString, bool bAddPrefix,
+ ConvertDataFormat aTargetFormat)
+{
+ SvMemoryStream aOStm;
+ GfxLink aLink = rGraphic.GetGfxLink();
+
+ if (aTargetFormat == ConvertDataFormat::Unknown)
+ {
+ switch (aLink.GetType())
+ {
+ case GfxLinkType::NativeJpg:
+ aTargetFormat = ConvertDataFormat::JPG;
+ break;
+ case GfxLinkType::NativePng:
+ aTargetFormat = ConvertDataFormat::PNG;
+ break;
+ case GfxLinkType::NativeSvg:
+ aTargetFormat = ConvertDataFormat::SVG;
+ break;
+ default:
+ // save everything else (including gif) into png
+ aTargetFormat = ConvertDataFormat::PNG;
+ break;
+ }
+ }
+
+ ErrCode nErr = GraphicConverter::Export(aOStm,rGraphic,aTargetFormat);
+ if ( nErr )
+ {
+ SAL_WARN("svx", "XOutBitmap::GraphicToBase64() invalid Graphic? error: " << nErr );
+ return false;
+ }
+ css::uno::Sequence<sal_Int8> aOStmSeq( static_cast<sal_Int8 const *>(aOStm.GetData()),aOStm.TellEnd() );
+ OUStringBuffer aStrBuffer;
+ ::comphelper::Base64::encode(aStrBuffer,aOStmSeq);
+ rOUString = aStrBuffer.makeStringAndClear();
+
+ if (bAddPrefix)
+ {
+ OUString aMimeType
+ = comphelper::GraphicMimeTypeHelper::GetMimeTypeForConvertDataFormat(aTargetFormat);
+ rOUString = aMimeType + ";base64," + rOUString;
+ }
+
+ return true;
+}
+
+ErrCode XOutBitmap::ExportGraphic( const Graphic& rGraphic, const INetURLObject& rURL,
+ GraphicFilter& rFilter, const sal_uInt16 nFormat,
+ const css::uno::Sequence< css::beans::PropertyValue >* pFilterData )
+{
+ DBG_ASSERT( rURL.GetProtocol() != INetProtocol::NotValid, "XOutBitmap::ExportGraphic(...): invalid URL" );
+
+ SfxMedium aMedium( rURL.GetMainURL( INetURLObject::DecodeMechanism::NONE ), StreamMode::WRITE | StreamMode::SHARE_DENYNONE | StreamMode::TRUNC );
+ SvStream* pOStm = aMedium.GetOutStream();
+ ErrCode nRet = ERRCODE_GRFILTER_IOERROR;
+
+ if( pOStm )
+ {
+ nRet = rFilter.ExportGraphic( rGraphic, rURL.GetMainURL( INetURLObject::DecodeMechanism::NONE ), *pOStm, nFormat, pFilterData );
+
+ aMedium.Commit();
+
+ if( aMedium.GetError() && ( ERRCODE_NONE == nRet ) )
+ nRet = ERRCODE_GRFILTER_IOERROR;
+ }
+
+ return nRet;
+}
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/xoutdev/_xpoly.cxx b/svx/source/xoutdev/_xpoly.cxx
new file mode 100644
index 000000000..4d556b1f9
--- /dev/null
+++ b/svx/source/xoutdev/_xpoly.cxx
@@ -0,0 +1,947 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * 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 <tools/debug.hxx>
+#include <tools/poly.hxx>
+#include <tools/helpers.hxx>
+#include <tools/gen.hxx>
+
+#include <svx/xpoly.hxx>
+#include <xpolyimp.hxx>
+#include <basegfx/polygon/b2dpolygon.hxx>
+#include <basegfx/polygon/b2dpolygontools.hxx>
+#include <basegfx/range/b2drange.hxx>
+
+
+ImpXPolygon::ImpXPolygon(sal_uInt16 nInitSize, sal_uInt16 _nResize)
+ : pOldPointAry(nullptr)
+ , bDeleteOldPoints(false)
+ , nSize(0)
+ , nResize(_nResize)
+ , nPoints(0)
+{
+ Resize(nInitSize);
+}
+
+ImpXPolygon::ImpXPolygon( const ImpXPolygon& rImpXPoly )
+ : pOldPointAry(nullptr)
+ , bDeleteOldPoints(false)
+ , nSize(0)
+ , nResize(rImpXPoly.nResize)
+ , nPoints(0)
+{
+ rImpXPoly.CheckPointDelete();
+
+ Resize( rImpXPoly.nSize );
+
+ // copy
+ nPoints = rImpXPoly.nPoints;
+ memcpy( pPointAry.get(), rImpXPoly.pPointAry.get(), nSize*sizeof( Point ) );
+ memcpy( pFlagAry.get(), rImpXPoly.pFlagAry.get(), nSize );
+}
+
+ImpXPolygon::~ImpXPolygon()
+{
+ pPointAry.reset();
+ if ( bDeleteOldPoints )
+ {
+ delete[] pOldPointAry;
+ pOldPointAry = nullptr;
+ }
+}
+
+bool ImpXPolygon::operator==(const ImpXPolygon& rImpXPoly) const
+{
+ return nPoints==rImpXPoly.nPoints &&
+ (nPoints==0 ||
+ (memcmp(pPointAry.get(), rImpXPoly.pPointAry.get(), nPoints*sizeof(Point))==0 &&
+ memcmp(pFlagAry.get(), rImpXPoly.pFlagAry.get(), nPoints)==0));
+}
+
+/** Change polygon size
+ *
+ * @param nNewSize the new size of the polygon
+ * @param bDeletePoints if FALSE, do not delete the point array directly but
+ * wait for the next call before doing so. This prevents
+ * errors with XPoly[n] = XPoly[0] where a resize might
+ * destroy the right side point array too early.
+ */
+void ImpXPolygon::Resize( sal_uInt16 nNewSize, bool bDeletePoints )
+{
+ if( nNewSize == nSize )
+ return;
+
+ PolyFlags* pOldFlagAry = pFlagAry.release();
+ sal_uInt16 nOldSize = nSize;
+
+ CheckPointDelete();
+ pOldPointAry = pPointAry.release();
+
+ // Round the new size to a multiple of nResize, if
+ // the object was not newly created (nSize != 0)
+ if ( nSize != 0 && nNewSize > nSize )
+ {
+ DBG_ASSERT(nResize, "Trying to resize but nResize = 0 !");
+ nNewSize = nSize + ((nNewSize-nSize-1) / nResize + 1) * nResize;
+ }
+ // create point array
+ nSize = nNewSize;
+ pPointAry.reset( new Point[ nSize ] );
+
+ // create flag array
+ pFlagAry.reset( new PolyFlags[ nSize ] );
+ memset( pFlagAry.get(), 0, nSize );
+
+ // copy if needed
+ if (nOldSize)
+ {
+ if( nOldSize < nSize )
+ {
+ memcpy( pPointAry.get(), pOldPointAry, nOldSize*sizeof( Point ) );
+ memcpy( pFlagAry.get(), pOldFlagAry, nOldSize );
+ }
+ else
+ {
+ memcpy( pPointAry.get(), pOldPointAry, nSize*sizeof( Point ) );
+ memcpy( pFlagAry.get(), pOldFlagAry, nSize );
+
+ // adjust number of valid points
+ if( nPoints > nSize )
+ nPoints = nSize;
+ }
+ }
+ if ( bDeletePoints )
+ {
+ delete[] pOldPointAry;
+ pOldPointAry = nullptr;
+ }
+ else
+ bDeleteOldPoints = true;
+ delete[] pOldFlagAry;
+}
+
+void ImpXPolygon::InsertSpace( sal_uInt16 nPos, sal_uInt16 nCount )
+{
+ CheckPointDelete();
+
+ if ( nPos > nPoints )
+ nPos = nPoints;
+
+ // if the polygon is too small then enlarge it
+ if( (nPoints + nCount) > nSize )
+ Resize( nPoints + nCount );
+
+ // If the insert is not at the last position, move everything after backwards
+ if( nPos < nPoints )
+ {
+ sal_uInt16 nMove = nPoints - nPos;
+ memmove( &pPointAry[nPos+nCount], &pPointAry[nPos],
+ nMove * sizeof(Point) );
+ memmove( &pFlagAry[nPos+nCount], &pFlagAry[nPos], nMove );
+ }
+ std::fill(pPointAry.get() + nPos, pPointAry.get() + nPos + nCount, Point());
+ memset( &pFlagAry [nPos], 0, nCount );
+
+ nPoints = nPoints + nCount;
+}
+
+void ImpXPolygon::Remove( sal_uInt16 nPos, sal_uInt16 nCount )
+{
+ CheckPointDelete();
+
+ if( (nPos + nCount) > nPoints )
+ return;
+
+ sal_uInt16 nMove = nPoints - nPos - nCount;
+
+ if( nMove )
+ {
+ memmove( &pPointAry[nPos], &pPointAry[nPos+nCount],
+ nMove * sizeof(Point) );
+ memmove( &pFlagAry[nPos], &pFlagAry[nPos+nCount], nMove );
+ }
+ std::fill(pPointAry.get() + (nPoints - nCount), pPointAry.get() + nPoints, Point());
+ memset( &pFlagAry [nPoints - nCount], 0, nCount );
+ nPoints = nPoints - nCount;
+}
+
+void ImpXPolygon::CheckPointDelete() const
+{
+ if ( bDeleteOldPoints )
+ {
+ delete[] pOldPointAry;
+ const_cast< ImpXPolygon* >(this)->pOldPointAry = nullptr;
+ const_cast< ImpXPolygon* >(this)->bDeleteOldPoints = false;
+ }
+}
+
+XPolygon::XPolygon( sal_uInt16 nSize )
+ : pImpXPolygon( ImpXPolygon( nSize, 16 ) )
+{
+}
+
+XPolygon::XPolygon( const XPolygon& ) = default;
+
+XPolygon::XPolygon( XPolygon&& ) = default;
+
+/// create a XPolygon out of a standard polygon
+XPolygon::XPolygon( const tools::Polygon& rPoly )
+ : pImpXPolygon( rPoly.GetSize() )
+{
+ sal_uInt16 nSize = rPoly.GetSize();
+ pImpXPolygon->nPoints = nSize;
+
+ for( sal_uInt16 i = 0; i < nSize; i++ )
+ {
+ pImpXPolygon->pPointAry[i] = rPoly[i];
+ pImpXPolygon->pFlagAry[i] = rPoly.GetFlags( i );
+ }
+}
+
+/// create a rectangle (also with rounded corners) as a Bézier polygon
+XPolygon::XPolygon(const tools::Rectangle& rRect, tools::Long nRx, tools::Long nRy)
+ : pImpXPolygon( 17 )
+{
+ tools::Long nWh = (rRect.GetWidth() - 1) / 2;
+ tools::Long nHh = (rRect.GetHeight() - 1) / 2;
+
+ if ( nRx > nWh ) nRx = nWh;
+ if ( nRy > nHh ) nRy = nHh;
+
+ // negate Rx => circle clockwise
+ nRx = -nRx;
+
+ // factor for control points of the Bézier curve: 8/3 * (sin(45g) - 0.5)
+ tools::Long nXHdl = static_cast<tools::Long>(0.552284749 * nRx);
+ tools::Long nYHdl = static_cast<tools::Long>(0.552284749 * nRy);
+ sal_uInt16 nPos = 0;
+
+ if ( nRx && nRy )
+ {
+ Point aCenter;
+
+ for (sal_uInt16 nQuad = 0; nQuad < 4; nQuad++)
+ {
+ switch ( nQuad )
+ {
+ case 0: aCenter = rRect.TopLeft();
+ aCenter.AdjustX( -nRx );
+ aCenter.AdjustY(nRy );
+ break;
+ case 1: aCenter = rRect.TopRight();
+ aCenter.AdjustX(nRx );
+ aCenter.AdjustY(nRy );
+ break;
+ case 2: aCenter = rRect.BottomRight();
+ aCenter.AdjustX(nRx );
+ aCenter.AdjustY( -nRy );
+ break;
+ case 3: aCenter = rRect.BottomLeft();
+ aCenter.AdjustX( -nRx );
+ aCenter.AdjustY( -nRy );
+ break;
+ }
+ GenBezArc(aCenter, nRx, nRy, nXHdl, nYHdl, 0_deg100, 9000_deg100, nQuad, nPos);
+ pImpXPolygon->pFlagAry[nPos ] = PolyFlags::Smooth;
+ pImpXPolygon->pFlagAry[nPos+3] = PolyFlags::Smooth;
+ nPos += 4;
+ }
+ }
+ else
+ {
+ pImpXPolygon->pPointAry[nPos++] = rRect.TopLeft();
+ pImpXPolygon->pPointAry[nPos++] = rRect.TopRight();
+ pImpXPolygon->pPointAry[nPos++] = rRect.BottomRight();
+ pImpXPolygon->pPointAry[nPos++] = rRect.BottomLeft();
+ }
+ pImpXPolygon->pPointAry[nPos] = pImpXPolygon->pPointAry[0];
+ pImpXPolygon->nPoints = nPos + 1;
+}
+
+/// create an ellipse (curve) as Bézier polygon
+XPolygon::XPolygon(const Point& rCenter, tools::Long nRx, tools::Long nRy,
+ Degree100 nStartAngle, Degree100 nEndAngle, bool bClose)
+ : pImpXPolygon( 17 )
+{
+ nStartAngle %= 36000_deg100;
+ if ( nEndAngle > 36000_deg100 ) nEndAngle %= 36000_deg100;
+ bool bFull = (nStartAngle == 0_deg100 && nEndAngle == 36000_deg100);
+
+ // factor for control points of the Bézier curve: 8/3 * (sin(45g) - 0.5)
+ tools::Long nXHdl = static_cast<tools::Long>(0.552284749 * nRx);
+ tools::Long nYHdl = static_cast<tools::Long>(0.552284749 * nRy);
+ sal_uInt16 nPos = 0;
+ bool bLoopEnd = false;
+
+ do
+ {
+ Degree100 nA1, nA2;
+ sal_uInt16 nQuad = nStartAngle.get() / 9000;
+ if ( nQuad == 4 ) nQuad = 0;
+ bLoopEnd = CheckAngles(nStartAngle, nEndAngle, nA1, nA2);
+ GenBezArc(rCenter, nRx, nRy, nXHdl, nYHdl, nA1, nA2, nQuad, nPos);
+ nPos += 3;
+ if ( !bLoopEnd )
+ pImpXPolygon->pFlagAry[nPos] = PolyFlags::Smooth;
+
+ } while ( !bLoopEnd );
+
+ // if not a full circle then connect edges with center point if necessary
+ if ( !bFull && bClose )
+ pImpXPolygon->pPointAry[++nPos] = rCenter;
+
+ if ( bFull )
+ {
+ pImpXPolygon->pFlagAry[0 ] = PolyFlags::Smooth;
+ pImpXPolygon->pFlagAry[nPos] = PolyFlags::Smooth;
+ }
+ pImpXPolygon->nPoints = nPos + 1;
+}
+
+XPolygon::~XPolygon() = default;
+
+void XPolygon::SetPointCount( sal_uInt16 nPoints )
+{
+ std::as_const(pImpXPolygon)->CheckPointDelete();
+
+ if( pImpXPolygon->nSize < nPoints )
+ pImpXPolygon->Resize( nPoints );
+
+ if ( nPoints < pImpXPolygon->nPoints )
+ {
+ sal_uInt16 nSize = pImpXPolygon->nPoints - nPoints;
+ std::fill(
+ pImpXPolygon->pPointAry.get() + nPoints, pImpXPolygon->pPointAry.get() + nPoints + nSize, Point());
+ memset( &pImpXPolygon->pFlagAry [nPoints], 0, nSize );
+ }
+ pImpXPolygon->nPoints = nPoints;
+}
+
+sal_uInt16 XPolygon::GetSize() const
+{
+ pImpXPolygon->CheckPointDelete();
+ return pImpXPolygon->nSize;
+}
+
+sal_uInt16 XPolygon::GetPointCount() const
+{
+ pImpXPolygon->CheckPointDelete();
+ return pImpXPolygon->nPoints;
+}
+
+void XPolygon::Insert( sal_uInt16 nPos, const Point& rPt, PolyFlags eFlags )
+{
+ if (nPos>pImpXPolygon->nPoints) nPos=pImpXPolygon->nPoints;
+ pImpXPolygon->InsertSpace( nPos, 1 );
+ pImpXPolygon->pPointAry[nPos] = rPt;
+ pImpXPolygon->pFlagAry[nPos] = eFlags;
+}
+
+void XPolygon::Insert( sal_uInt16 nPos, const XPolygon& rXPoly )
+{
+ if (nPos>pImpXPolygon->nPoints) nPos=pImpXPolygon->nPoints;
+
+ sal_uInt16 nPoints = rXPoly.GetPointCount();
+
+ pImpXPolygon->InsertSpace( nPos, nPoints );
+
+ memcpy( &(pImpXPolygon->pPointAry[nPos]),
+ rXPoly.pImpXPolygon->pPointAry.get(),
+ nPoints*sizeof( Point ) );
+ memcpy( &(pImpXPolygon->pFlagAry[nPos]),
+ rXPoly.pImpXPolygon->pFlagAry.get(),
+ nPoints );
+}
+
+void XPolygon::Remove( sal_uInt16 nPos, sal_uInt16 nCount )
+{
+ pImpXPolygon->Remove( nPos, nCount );
+}
+
+void XPolygon::Move( tools::Long nHorzMove, tools::Long nVertMove )
+{
+ if ( !nHorzMove && !nVertMove )
+ return;
+
+ // move points
+ sal_uInt16 nCount = pImpXPolygon->nPoints;
+ for ( sal_uInt16 i = 0; i < nCount; i++ )
+ {
+ Point* pPt = &(pImpXPolygon->pPointAry[i]);
+ pPt->AdjustX( nHorzMove );
+ pPt->AdjustY( nVertMove );
+ }
+}
+
+tools::Rectangle XPolygon::GetBoundRect() const
+{
+ pImpXPolygon->CheckPointDelete();
+ tools::Rectangle aRetval;
+
+ if(pImpXPolygon->nPoints)
+ {
+ // #i37709#
+ // For historical reasons the control points are not part of the
+ // BoundRect. This makes it necessary to subdivide the polygon to
+ // get a relatively correct BoundRect. Numerically, this is not
+ // correct and never was.
+
+ const basegfx::B2DRange aPolygonRange(basegfx::utils::getRange(getB2DPolygon()));
+ aRetval = tools::Rectangle(
+ FRound(aPolygonRange.getMinX()), FRound(aPolygonRange.getMinY()),
+ FRound(aPolygonRange.getMaxX()), FRound(aPolygonRange.getMaxY()));
+ }
+
+ return aRetval;
+}
+
+const Point& XPolygon::operator[]( sal_uInt16 nPos ) const
+{
+ DBG_ASSERT(nPos < pImpXPolygon->nPoints, "Invalid index at const array access to XPolygon");
+
+ pImpXPolygon->CheckPointDelete();
+ return pImpXPolygon->pPointAry[nPos];
+}
+
+Point& XPolygon::operator[]( sal_uInt16 nPos )
+{
+ std::as_const(pImpXPolygon)->CheckPointDelete();
+
+ if( nPos >= pImpXPolygon->nSize )
+ {
+ DBG_ASSERT(pImpXPolygon->nResize, "Invalid index at array access to XPolygon");
+ pImpXPolygon->Resize(nPos + 1, false);
+ }
+ if( nPos >= pImpXPolygon->nPoints )
+ pImpXPolygon->nPoints = nPos + 1;
+
+ return pImpXPolygon->pPointAry[nPos];
+}
+
+XPolygon& XPolygon::operator=( const XPolygon& ) = default;
+
+XPolygon& XPolygon::operator=( XPolygon&& ) = default;
+
+bool XPolygon::operator==( const XPolygon& rXPoly ) const
+{
+ pImpXPolygon->CheckPointDelete();
+ return rXPoly.pImpXPolygon == pImpXPolygon;
+}
+
+/// get the flags for the point at the given position
+PolyFlags XPolygon::GetFlags( sal_uInt16 nPos ) const
+{
+ pImpXPolygon->CheckPointDelete();
+ return pImpXPolygon->pFlagAry[nPos];
+}
+
+/// set the flags for the point at the given position
+void XPolygon::SetFlags( sal_uInt16 nPos, PolyFlags eFlags )
+{
+ std::as_const(pImpXPolygon)->CheckPointDelete();
+ pImpXPolygon->pFlagAry[nPos] = eFlags;
+}
+
+/// short path to read the CONTROL flag directly (TODO: better explain what the sense behind this flag is!)
+bool XPolygon::IsControl(sal_uInt16 nPos) const
+{
+ return pImpXPolygon->pFlagAry[nPos] == PolyFlags::Control;
+}
+
+/// short path to read the SMOOTH and SYMMTR flag directly (TODO: better explain what the sense behind these flags is!)
+bool XPolygon::IsSmooth(sal_uInt16 nPos) const
+{
+ PolyFlags eFlag = pImpXPolygon->pFlagAry[nPos];
+ return ( eFlag == PolyFlags::Smooth || eFlag == PolyFlags::Symmetric );
+}
+
+/** calculate the euclidean distance between two points
+ *
+ * @param nP1 The first point
+ * @param nP2 The second point
+ */
+double XPolygon::CalcDistance(sal_uInt16 nP1, sal_uInt16 nP2)
+{
+ const Point& rP1 = pImpXPolygon->pPointAry[nP1];
+ const Point& rP2 = pImpXPolygon->pPointAry[nP2];
+ double fDx = rP2.X() - rP1.X();
+ double fDy = rP2.Y() - rP1.Y();
+ return std::hypot(fDx, fDy);
+}
+
+void XPolygon::SubdivideBezier(sal_uInt16 nPos, bool bCalcFirst, double fT)
+{
+ Point* pPoints = pImpXPolygon->pPointAry.get();
+ double fT2 = fT * fT;
+ double fT3 = fT * fT2;
+ double fU = 1.0 - fT;
+ double fU2 = fU * fU;
+ double fU3 = fU * fU2;
+ sal_uInt16 nIdx = nPos;
+ short nPosInc, nIdxInc;
+
+ if ( bCalcFirst )
+ {
+ nPos += 3;
+ nPosInc = -1;
+ nIdxInc = 0;
+ }
+ else
+ {
+ nPosInc = 1;
+ nIdxInc = 1;
+ }
+ pPoints[nPos].setX( static_cast<tools::Long>(fU3 * pPoints[nIdx ].X() +
+ fT * fU2 * pPoints[nIdx+1].X() * 3 +
+ fT2 * fU * pPoints[nIdx+2].X() * 3 +
+ fT3 * pPoints[nIdx+3].X()) );
+ pPoints[nPos].setY( static_cast<tools::Long>(fU3 * pPoints[nIdx ].Y() +
+ fT * fU2 * pPoints[nIdx+1].Y() * 3 +
+ fT2 * fU * pPoints[nIdx+2].Y() * 3 +
+ fT3 * pPoints[nIdx+3].Y()) );
+ nPos = nPos + nPosInc;
+ nIdx = nIdx + nIdxInc;
+ pPoints[nPos].setX( static_cast<tools::Long>(fU2 * pPoints[nIdx ].X() +
+ fT * fU * pPoints[nIdx+1].X() * 2 +
+ fT2 * pPoints[nIdx+2].X()) );
+ pPoints[nPos].setY( static_cast<tools::Long>(fU2 * pPoints[nIdx ].Y() +
+ fT * fU * pPoints[nIdx+1].Y() * 2 +
+ fT2 * pPoints[nIdx+2].Y()) );
+ nPos = nPos + nPosInc;
+ nIdx = nIdx + nIdxInc;
+ pPoints[nPos].setX( static_cast<tools::Long>(fU * pPoints[nIdx ].X() +
+ fT * pPoints[nIdx+1].X()) );
+ pPoints[nPos].setY( static_cast<tools::Long>(fU * pPoints[nIdx ].Y() +
+ fT * pPoints[nIdx+1].Y()) );
+}
+
+/// Generate a Bézier arc
+void XPolygon::GenBezArc(const Point& rCenter, tools::Long nRx, tools::Long nRy,
+ tools::Long nXHdl, tools::Long nYHdl, Degree100 nStart, Degree100 nEnd,
+ sal_uInt16 nQuad, sal_uInt16 nFirst)
+{
+ Point* pPoints = pImpXPolygon->pPointAry.get();
+ pPoints[nFirst ] = rCenter;
+ pPoints[nFirst+3] = rCenter;
+
+ if ( nQuad == 1 || nQuad == 2 )
+ {
+ nRx = -nRx; nXHdl = -nXHdl;
+ }
+ if ( nQuad == 0 || nQuad == 1 )
+ {
+ nRy = -nRy; nYHdl = -nYHdl;
+ }
+
+ if ( nQuad == 0 || nQuad == 2 )
+ {
+ pPoints[nFirst].AdjustX( nRx );
+ pPoints[nFirst+3].AdjustY( nRy );
+ }
+ else
+ {
+ pPoints[nFirst].AdjustY( nRy );
+ pPoints[nFirst+3].AdjustX( nRx );
+ }
+ pPoints[nFirst+1] = pPoints[nFirst];
+ pPoints[nFirst+2] = pPoints[nFirst+3];
+
+ if ( nQuad == 0 || nQuad == 2 )
+ {
+ pPoints[nFirst+1].AdjustY( nYHdl );
+ pPoints[nFirst+2].AdjustX( nXHdl );
+ }
+ else
+ {
+ pPoints[nFirst+1].AdjustX( nXHdl );
+ pPoints[nFirst+2].AdjustY( nYHdl );
+ }
+ if ( nStart > 0_deg100 )
+ SubdivideBezier(nFirst, false, static_cast<double>(nStart.get()) / 9000);
+ if ( nEnd < 9000_deg100 )
+ SubdivideBezier(nFirst, true, static_cast<double>((nEnd-nStart).get()) / (9000_deg100-nStart).get());
+ SetFlags(nFirst+1, PolyFlags::Control);
+ SetFlags(nFirst+2, PolyFlags::Control);
+}
+
+bool XPolygon::CheckAngles(Degree100& nStart, Degree100 nEnd, Degree100& nA1, Degree100& nA2)
+{
+ if ( nStart == 36000_deg100 ) nStart = 0_deg100;
+ if ( nEnd == 0_deg100 ) nEnd = 36000_deg100;
+ Degree100 nStPrev = nStart;
+ Degree100 nMax((nStart.get() / 9000 + 1) * 9000);
+ Degree100 nMin = nMax - 9000_deg100;
+
+ if ( nEnd >= nMax || nEnd <= nStart ) nA2 = 9000_deg100;
+ else nA2 = nEnd - nMin;
+ nA1 = nStart - nMin;
+ nStart = nMax;
+
+ // returns true when the last segment was calculated
+ return (nStPrev < nEnd && nStart >= nEnd);
+}
+
+/** Calculate a smooth transition to connect two Bézier curves
+ *
+ * This is done by projecting the corresponding point onto a line between
+ * two other points.
+ *
+ * @param nCenter The point at the end or beginning of the curve.
+ * If nCenter is at the end of the polygon the point is moved
+ * to the opposite side.
+ * @param nDrag The moved point that specifies the relocation.
+ * @param nPnt The point to modify.
+ */
+void XPolygon::CalcSmoothJoin(sal_uInt16 nCenter, sal_uInt16 nDrag, sal_uInt16 nPnt)
+{
+ // If nPoint is no control point, i.e. cannot be moved, then
+ // move nDrag instead on the line between nCenter and nPnt
+ if ( !IsControl(nPnt) )
+ {
+ sal_uInt16 nTmp = nDrag;
+ nDrag = nPnt;
+ nPnt = nTmp;
+ }
+ Point* pPoints = pImpXPolygon->pPointAry.get();
+ Point aDiff = pPoints[nDrag] - pPoints[nCenter];
+ double fDiv = CalcDistance(nCenter, nDrag);
+
+ if ( fDiv )
+ {
+ double fRatio = CalcDistance(nCenter, nPnt) / fDiv;
+ // keep the length if SMOOTH
+ if ( GetFlags(nCenter) == PolyFlags::Smooth || !IsControl(nDrag) )
+ {
+ aDiff.setX( static_cast<tools::Long>(fRatio * aDiff.X()) );
+ aDiff.setY( static_cast<tools::Long>(fRatio * aDiff.Y()) );
+ }
+ pPoints[nPnt] = pPoints[nCenter] - aDiff;
+ }
+}
+
+/** Calculate tangent between two Bézier curves
+ *
+ * @param nCenter start or end point of the curves
+ * @param nPrev previous reference point
+ * @param nNext next reference point
+ */
+void XPolygon::CalcTangent(sal_uInt16 nCenter, sal_uInt16 nPrev, sal_uInt16 nNext)
+{
+ double fAbsLen = CalcDistance(nNext, nPrev);
+
+ if ( !fAbsLen )
+ return;
+
+ const Point& rCenter = pImpXPolygon->pPointAry[nCenter];
+ Point& rNext = pImpXPolygon->pPointAry[nNext];
+ Point& rPrev = pImpXPolygon->pPointAry[nPrev];
+ Point aDiff = rNext - rPrev;
+ double fNextLen = CalcDistance(nCenter, nNext) / fAbsLen;
+ double fPrevLen = CalcDistance(nCenter, nPrev) / fAbsLen;
+
+ // same length for both sides if SYMMTR
+ if ( GetFlags(nCenter) == PolyFlags::Symmetric )
+ {
+ fPrevLen = (fNextLen + fPrevLen) / 2;
+ fNextLen = fPrevLen;
+ }
+ rNext.setX( rCenter.X() + static_cast<tools::Long>(fNextLen * aDiff.X()) );
+ rNext.setY( rCenter.Y() + static_cast<tools::Long>(fNextLen * aDiff.Y()) );
+ rPrev.setX( rCenter.X() - static_cast<tools::Long>(fPrevLen * aDiff.X()) );
+ rPrev.setY( rCenter.Y() - static_cast<tools::Long>(fPrevLen * aDiff.Y()) );
+}
+
+/// convert four polygon points into a Bézier curve
+void XPolygon::PointsToBezier(sal_uInt16 nFirst)
+{
+ double nFullLength, nPart1Length, nPart2Length;
+ double fX0, fY0, fX1, fY1, fX2, fY2, fX3, fY3;
+ double fTx1, fTx2, fTy1, fTy2;
+ double fT1, fU1, fT2, fU2, fV;
+ Point* pPoints = pImpXPolygon->pPointAry.get();
+
+ if ( nFirst > pImpXPolygon->nPoints - 4 || IsControl(nFirst) ||
+ IsControl(nFirst+1) || IsControl(nFirst+2) || IsControl(nFirst+3) )
+ return;
+
+ fTx1 = pPoints[nFirst+1].X();
+ fTy1 = pPoints[nFirst+1].Y();
+ fTx2 = pPoints[nFirst+2].X();
+ fTy2 = pPoints[nFirst+2].Y();
+ fX0 = pPoints[nFirst ].X();
+ fY0 = pPoints[nFirst ].Y();
+ fX3 = pPoints[nFirst+3].X();
+ fY3 = pPoints[nFirst+3].Y();
+
+ nPart1Length = CalcDistance(nFirst, nFirst+1);
+ nPart2Length = nPart1Length + CalcDistance(nFirst+1, nFirst+2);
+ nFullLength = nPart2Length + CalcDistance(nFirst+2, nFirst+3);
+ if ( nFullLength < 20 )
+ return;
+
+ if ( nPart2Length == nFullLength )
+ nPart2Length -= 1;
+ if ( nPart1Length == nFullLength )
+ nPart1Length = nPart2Length - 1;
+ if ( nPart1Length <= 0 )
+ nPart1Length = 1;
+ if ( nPart2Length <= 0 || nPart2Length == nPart1Length )
+ nPart2Length = nPart1Length + 1;
+
+ fT1 = nPart1Length / nFullLength;
+ fU1 = 1.0 - fT1;
+ fT2 = nPart2Length / nFullLength;
+ fU2 = 1.0 - fT2;
+ fV = 3 * (1.0 - (fT1 * fU2) / (fT2 * fU1));
+
+ fX1 = fTx1 / (fT1 * fU1 * fU1) - fTx2 * fT1 / (fT2 * fT2 * fU1 * fU2);
+ fX1 /= fV;
+ fX1 -= fX0 * ( fU1 / fT1 + fU2 / fT2) / 3;
+ fX1 += fX3 * ( fT1 * fT2 / (fU1 * fU2)) / 3;
+
+ fY1 = fTy1 / (fT1 * fU1 * fU1) - fTy2 * fT1 / (fT2 * fT2 * fU1 * fU2);
+ fY1 /= fV;
+ fY1 -= fY0 * ( fU1 / fT1 + fU2 / fT2) / 3;
+ fY1 += fY3 * ( fT1 * fT2 / (fU1 * fU2)) / 3;
+
+ fX2 = fTx2 / (fT2 * fT2 * fU2 * 3) - fX0 * fU2 * fU2 / ( fT2 * fT2 * 3);
+ fX2 -= fX1 * fU2 / fT2;
+ fX2 -= fX3 * fT2 / (fU2 * 3);
+
+ fY2 = fTy2 / (fT2 * fT2 * fU2 * 3) - fY0 * fU2 * fU2 / ( fT2 * fT2 * 3);
+ fY2 -= fY1 * fU2 / fT2;
+ fY2 -= fY3 * fT2 / (fU2 * 3);
+
+ pPoints[nFirst+1] = Point(static_cast<tools::Long>(fX1), static_cast<tools::Long>(fY1));
+ pPoints[nFirst+2] = Point(static_cast<tools::Long>(fX2), static_cast<tools::Long>(fY2));
+ SetFlags(nFirst+1, PolyFlags::Control);
+ SetFlags(nFirst+2, PolyFlags::Control);
+}
+
+/// scale in X- and/or Y-direction
+void XPolygon::Scale(double fSx, double fSy)
+{
+ std::as_const(pImpXPolygon)->CheckPointDelete();
+
+ sal_uInt16 nPntCnt = pImpXPolygon->nPoints;
+
+ for (sal_uInt16 i = 0; i < nPntCnt; i++)
+ {
+ Point& rPnt = pImpXPolygon->pPointAry[i];
+ rPnt.setX( static_cast<tools::Long>(fSx * rPnt.X()) );
+ rPnt.setY( static_cast<tools::Long>(fSy * rPnt.Y()) );
+ }
+}
+
+/**
+ * Distort a polygon by scaling its coordinates relative to a reference
+ * rectangle into an arbitrary rectangle.
+ *
+ * Mapping between polygon corners and reference rectangle:
+ * 0: top left 0----1
+ * 1: top right | |
+ * 2: bottom right 3----2
+ * 3: bottom left
+ */
+void XPolygon::Distort(const tools::Rectangle& rRefRect,
+ const XPolygon& rDistortedRect)
+{
+ std::as_const(pImpXPolygon)->CheckPointDelete();
+
+ tools::Long Xr, Wr;
+ tools::Long Yr, Hr;
+
+ Xr = rRefRect.Left();
+ Yr = rRefRect.Top();
+ Wr = rRefRect.GetWidth();
+ Hr = rRefRect.GetHeight();
+
+ if ( !Wr || !Hr )
+ return;
+
+ tools::Long X1, X2, X3, X4;
+ tools::Long Y1, Y2, Y3, Y4;
+ DBG_ASSERT(rDistortedRect.pImpXPolygon->nPoints >= 4,
+ "Distort: rectangle too small");
+
+ X1 = rDistortedRect[0].X();
+ Y1 = rDistortedRect[0].Y();
+ X2 = rDistortedRect[1].X();
+ Y2 = rDistortedRect[1].Y();
+ X3 = rDistortedRect[3].X();
+ Y3 = rDistortedRect[3].Y();
+ X4 = rDistortedRect[2].X();
+ Y4 = rDistortedRect[2].Y();
+
+ sal_uInt16 nPntCnt = pImpXPolygon->nPoints;
+
+ for (sal_uInt16 i = 0; i < nPntCnt; i++)
+ {
+ double fTx, fTy, fUx, fUy;
+ Point& rPnt = pImpXPolygon->pPointAry[i];
+
+ fTx = static_cast<double>(rPnt.X() - Xr) / Wr;
+ fTy = static_cast<double>(rPnt.Y() - Yr) / Hr;
+ fUx = 1.0 - fTx;
+ fUy = 1.0 - fTy;
+
+ rPnt.setX( static_cast<tools::Long>( fUy * (fUx * X1 + fTx * X2) +
+ fTy * (fUx * X3 + fTx * X4) ) );
+ rPnt.setY( static_cast<tools::Long>( fUx * (fUy * Y1 + fTy * Y3) +
+ fTx * (fUy * Y2 + fTy * Y4) ) );
+ }
+}
+
+basegfx::B2DPolygon XPolygon::getB2DPolygon() const
+{
+ // #i74631# use tools Polygon class for conversion to not have the code doubled
+ // here. This needs one more conversion but avoids different converters in
+ // the long run
+ const tools::Polygon aSource(GetPointCount(), pImpXPolygon->pPointAry.get(), pImpXPolygon->pFlagAry.get());
+
+ return aSource.getB2DPolygon();
+}
+
+XPolygon::XPolygon(const basegfx::B2DPolygon& rPolygon)
+ : pImpXPolygon( tools::Polygon( rPolygon ).GetSize() )
+{
+ // #i74631# use tools Polygon class for conversion to not have the code doubled
+ // here. This needs one more conversion but avoids different converters in
+ // the long run
+
+ const tools::Polygon aSource(rPolygon);
+ sal_uInt16 nSize = aSource.GetSize();
+ pImpXPolygon->nPoints = nSize;
+
+ for( sal_uInt16 i = 0; i < nSize; i++ )
+ {
+ pImpXPolygon->pPointAry[i] = aSource[i];
+ pImpXPolygon->pFlagAry[i] = aSource.GetFlags( i );
+ }
+}
+
+// XPolyPolygon
+XPolyPolygon::XPolyPolygon() = default;
+
+XPolyPolygon::XPolyPolygon( const XPolyPolygon& ) = default;
+
+XPolyPolygon::XPolyPolygon( XPolyPolygon&& ) = default;
+
+XPolyPolygon::XPolyPolygon(const basegfx::B2DPolyPolygon& rPolyPolygon)
+{
+ for(auto const& rCandidate : rPolyPolygon)
+ {
+ Insert(XPolygon(rCandidate));
+ }
+}
+
+XPolyPolygon::~XPolyPolygon() = default;
+
+void XPolyPolygon::Insert( XPolygon&& rXPoly )
+{
+ pImpXPolyPolygon->aXPolyList.emplace_back( std::move(rXPoly) );
+}
+
+/// insert all XPolygons of a XPolyPolygon
+void XPolyPolygon::Insert( const XPolyPolygon& rXPolyPoly )
+{
+ for ( size_t i = 0; i < rXPolyPoly.Count(); i++)
+ {
+ pImpXPolyPolygon->aXPolyList.emplace_back( rXPolyPoly[i] );
+ }
+}
+
+void XPolyPolygon::Remove( sal_uInt16 nPos )
+{
+ pImpXPolyPolygon->aXPolyList.erase( pImpXPolyPolygon->aXPolyList.begin() + nPos );
+}
+
+const XPolygon& XPolyPolygon::GetObject( sal_uInt16 nPos ) const
+{
+ return pImpXPolyPolygon->aXPolyList[ nPos ];
+}
+
+void XPolyPolygon::Clear()
+{
+ pImpXPolyPolygon->aXPolyList.clear();
+}
+
+sal_uInt16 XPolyPolygon::Count() const
+{
+ return static_cast<sal_uInt16>(pImpXPolyPolygon->aXPolyList.size());
+}
+
+tools::Rectangle XPolyPolygon::GetBoundRect() const
+{
+ size_t nXPoly = pImpXPolyPolygon->aXPolyList.size();
+ tools::Rectangle aRect;
+
+ for ( size_t n = 0; n < nXPoly; n++ )
+ {
+ XPolygon const & rXPoly = pImpXPolyPolygon->aXPolyList[ n ];
+ aRect.Union( rXPoly.GetBoundRect() );
+ }
+
+ return aRect;
+}
+
+XPolygon& XPolyPolygon::operator[]( sal_uInt16 nPos )
+{
+ return pImpXPolyPolygon->aXPolyList[ nPos ];
+}
+
+XPolyPolygon& XPolyPolygon::operator=( const XPolyPolygon& ) = default;
+
+XPolyPolygon& XPolyPolygon::operator=( XPolyPolygon&& ) = default;
+
+/**
+ * Distort a polygon by scaling its coordinates relative to a reference
+ * rectangle into an arbitrary rectangle.
+ *
+ * Mapping between polygon corners and reference rectangle:
+ * 0: top left 0----1
+ * 1: top right | |
+ * 2: bottom right 3----2
+ * 3: bottom left
+ */
+void XPolyPolygon::Distort(const tools::Rectangle& rRefRect,
+ const XPolygon& rDistortedRect)
+{
+ for (size_t i = 0; i < Count(); i++)
+ pImpXPolyPolygon->aXPolyList[ i ].Distort(rRefRect, rDistortedRect);
+}
+
+basegfx::B2DPolyPolygon XPolyPolygon::getB2DPolyPolygon() const
+{
+ basegfx::B2DPolyPolygon aRetval;
+
+ for(sal_uInt16 a(0); a < Count(); a++)
+ {
+ const XPolygon& rPoly = (*this)[a];
+ aRetval.append(rPoly.getB2DPolygon());
+ }
+
+ return aRetval;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/xoutdev/xattr.cxx b/svx/source/xoutdev/xattr.cxx
new file mode 100644
index 000000000..aceba42bb
--- /dev/null
+++ b/svx/source/xoutdev/xattr.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 <sal/config.h>
+
+#include <utility>
+
+#include <com/sun/star/drawing/PolyPolygonBezierCoords.hpp>
+#include <com/sun/star/drawing/Hatch.hpp>
+#include <com/sun/star/drawing/LineStyle.hpp>
+#include <com/sun/star/drawing/LineDash.hpp>
+#include <com/sun/star/drawing/DashStyle.hpp>
+#include <com/sun/star/drawing/FillStyle.hpp>
+#include <com/sun/star/awt/Gradient.hpp>
+#include <com/sun/star/uno/Sequence.hxx>
+#include <com/sun/star/beans/PropertyValue.hpp>
+
+#include <comphelper/propertyvalue.hxx>
+#include <o3tl/string_view.hxx>
+#include <o3tl/any.hxx>
+#include <svl/itempool.hxx>
+#include <editeng/memberids.h>
+#include <tools/mapunit.hxx>
+#include <tools/UnitConversion.hxx>
+#include <osl/diagnose.h>
+
+#include <svx/unoapi.hxx>
+#include <svl/style.hxx>
+
+#include <tools/bigint.hxx>
+#include <svl/itemset.hxx>
+#include <svx/strings.hrc>
+#include <svx/xfillit0.hxx>
+#include <svx/xflasit.hxx>
+#include <svx/xlineit0.hxx>
+#include <svx/xlnasit.hxx>
+#include <svx/xtextit0.hxx>
+#include <svx/xtable.hxx>
+#include <svx/dialmgr.hxx>
+#include <svx/xflclit.hxx>
+#include <svx/xflgrit.hxx>
+#include <svx/xflftrit.hxx>
+#include <svx/xsflclit.hxx>
+#include <svx/xflhtit.hxx>
+#include <svx/xbtmpit.hxx>
+#include <svx/xlndsit.hxx>
+#include <svx/xlnwtit.hxx>
+#include <svx/xlnclit.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 <editeng/itemtype.hxx>
+#include <editeng/eerdll.hxx>
+#include <svx/xdef.hxx>
+#include <svx/unomid.hxx>
+#include <svx/svdmodel.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/xftadit.hxx>
+#include <svx/svddef.hxx>
+#include <basegfx/polygon/b2dpolypolygontools.hxx>
+#include <unotools/intlwrapper.hxx>
+#include <unotools/syslocale.hxx>
+#include <string>
+
+#include <boost/property_tree/json_parser.hpp>
+#include <libxml/xmlwriter.h>
+
+using namespace ::com::sun::star;
+
+typedef std::map<OUString, OUString> StringMap;
+
+NameOrIndex::NameOrIndex(TypedWhichId<NameOrIndex> _nWhich, sal_Int32 nIndex) :
+ SfxStringItem(_nWhich, OUString()),
+ nPalIndex(nIndex)
+{
+}
+
+NameOrIndex::NameOrIndex(TypedWhichId<NameOrIndex> _nWhich, const OUString& rName) :
+ SfxStringItem(_nWhich, rName),
+ nPalIndex(-1)
+{
+}
+
+NameOrIndex::NameOrIndex(const NameOrIndex& rNameOrIndex) :
+ SfxStringItem(rNameOrIndex),
+ nPalIndex(rNameOrIndex.nPalIndex)
+{
+}
+
+bool NameOrIndex::operator==(const SfxPoolItem& rItem) const
+{
+ return ( SfxStringItem::operator==(rItem) &&
+ static_cast<const NameOrIndex&>(rItem).nPalIndex == nPalIndex );
+}
+
+NameOrIndex* NameOrIndex::Clone(SfxItemPool* /*pPool*/) const
+{
+ return new NameOrIndex(*this);
+}
+
+/** this static checks if the given NameOrIndex item has a unique name for its value.
+ The returned String is a unique name for an item with this value in both given pools.
+ Argument pPool2 can be null.
+ If returned string equals NameOrIndex->GetName(), the name was already unique.
+*/
+OUString NameOrIndex::CheckNamedItem( const NameOrIndex* pCheckItem, const sal_uInt16 nWhich, const SfxItemPool* pPool1, SvxCompareValueFunc pCompareValueFunc, TranslateId pPrefixResId, const XPropertyListRef &pDefaults )
+{
+ bool bForceNew = false;
+
+ OUString aUniqueName = SvxUnogetInternalNameForItem(nWhich, pCheckItem->GetName());
+
+ // 2. if we have a name check if there is already an item with the
+ // same name in the documents pool with a different line end or start
+
+ if (!aUniqueName.isEmpty() && pPool1)
+ {
+ for (const SfxPoolItem* pItem : pPool1->GetItemSurrogates(nWhich))
+ {
+ const NameOrIndex *pNameOrIndex = static_cast<const NameOrIndex*>(pItem);
+
+ if( pNameOrIndex->GetName() == pCheckItem->GetName() )
+ {
+ // if there is already an item with the same name and the same
+ // value it's ok to set it
+ if( !pCompareValueFunc( pNameOrIndex, pCheckItem ) )
+ {
+ // same name but different value, we need a new name for this item
+ aUniqueName.clear();
+ bForceNew = true;
+ }
+ break;
+ }
+ }
+ }
+
+ // if we have no name yet, find existing item with same content or
+ // create a unique name
+ if (aUniqueName.isEmpty())
+ {
+ sal_Int32 nUserIndex = 1;
+ const OUString aUser(SvxResId(pPrefixResId) + " ");
+
+ if( pDefaults )
+ {
+ const int nCount = pDefaults->Count();
+ int nIndex;
+ for( nIndex = 0; nIndex < nCount; nIndex++ )
+ {
+ const XPropertyEntry* pEntry = pDefaults->Get(nIndex);
+ if( pEntry )
+ {
+ bool bFound = false;
+
+ switch( nWhich )
+ {
+ case XATTR_FILLBITMAP:
+ {
+ const GraphicObject& rGraphicObjectA(static_cast<const XFillBitmapItem*>(pCheckItem)->GetGraphicObject());
+ const GraphicObject& rGraphicObjectB(static_cast<const XBitmapEntry*>(pEntry)->GetGraphicObject());
+
+ bFound = (rGraphicObjectA == rGraphicObjectB);
+ break;
+ }
+ case XATTR_LINEDASH:
+ bFound = static_cast<const XLineDashItem*>(pCheckItem)->GetDashValue() == static_cast<const XDashEntry*>(pEntry)->GetDash();
+ break;
+ case XATTR_LINESTART:
+ bFound = static_cast<const XLineStartItem*>(pCheckItem)->GetLineStartValue() == static_cast<const XLineEndEntry*>(pEntry)->GetLineEnd();
+ break;
+ case XATTR_LINEEND:
+ bFound = static_cast<const XLineEndItem*>(pCheckItem)->GetLineEndValue() == static_cast<const XLineEndEntry*>(pEntry)->GetLineEnd();
+ break;
+ case XATTR_FILLGRADIENT:
+ bFound = static_cast<const XFillGradientItem*>(pCheckItem)->GetGradientValue() == static_cast<const XGradientEntry*>(pEntry)->GetGradient();
+ break;
+ case XATTR_FILLHATCH:
+ bFound = static_cast<const XFillHatchItem*>(pCheckItem)->GetHatchValue() == static_cast<const XHatchEntry*>(pEntry)->GetHatch();
+ break;
+ }
+
+ if( bFound )
+ {
+ aUniqueName = pEntry->GetName();
+ break;
+ }
+ else
+ {
+ const OUString& aEntryName = pEntry->GetName();
+ if(aEntryName.getLength() >= aUser.getLength())
+ {
+ sal_Int32 nThisIndex = o3tl::toInt32(aEntryName.subView( aUser.getLength() ));
+ if( nThisIndex >= nUserIndex )
+ nUserIndex = nThisIndex + 1;
+ }
+ }
+ }
+ }
+ }
+
+ if (aUniqueName.isEmpty() && pPool1)
+ {
+ for (const SfxPoolItem* pItem : pPool1->GetItemSurrogates(nWhich))
+ {
+ const NameOrIndex *pNameOrIndex = static_cast<const NameOrIndex*>(pItem);
+
+ if( !pNameOrIndex->GetName().isEmpty() )
+ {
+ if( !bForceNew && pCompareValueFunc( pNameOrIndex, pCheckItem ) )
+ return pNameOrIndex->GetName();
+
+ if( pNameOrIndex->GetName().startsWith( aUser ) )
+ {
+ sal_Int32 nThisIndex = o3tl::toInt32(pNameOrIndex->GetName().subView( aUser.getLength() ));
+ if( nThisIndex >= nUserIndex )
+ nUserIndex = nThisIndex + 1;
+ }
+ }
+ }
+ aUniqueName = aUser + OUString::number( nUserIndex );
+ }
+ }
+
+ return aUniqueName;
+}
+
+void NameOrIndex::dumpAsXml(xmlTextWriterPtr pWriter) const
+{
+ (void)xmlTextWriterStartElement(pWriter, BAD_CAST("NameOrIndex"));
+ (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("whichId"), BAD_CAST(OString::number(Which()).getStr()));
+ (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("isIndex"), BAD_CAST(OString::boolean(IsIndex()).getStr()));
+ (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("name"), BAD_CAST(GetName().toUtf8().getStr()));
+ (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("index"), BAD_CAST(OString::number(nPalIndex).getStr()));
+ (void)xmlTextWriterEndElement(pWriter);
+}
+
+SfxPoolItem* XColorItem::CreateDefault() { return new XColorItem; }
+
+XColorItem::XColorItem(TypedWhichId<XColorItem> _nWhich, sal_Int32 nIndex, const Color& rTheColor) :
+ NameOrIndex(_nWhich, nIndex),
+ aColor(rTheColor)
+{
+}
+
+XColorItem::XColorItem(TypedWhichId<XColorItem> _nWhich, const OUString& rName, const Color& rTheColor) :
+ NameOrIndex(_nWhich, rName),
+ aColor(rTheColor)
+{
+}
+
+XColorItem::XColorItem(TypedWhichId<XColorItem> _nWhich, const Color& rTheColor)
+ : NameOrIndex(_nWhich, OUString())
+ , aColor(rTheColor)
+{
+}
+
+XColorItem::XColorItem(const XColorItem& rItem) :
+ NameOrIndex(rItem),
+ aColor(rItem.aColor),
+ maThemeColor(rItem.maThemeColor)
+{
+}
+
+XColorItem* XColorItem::Clone(SfxItemPool* /*pPool*/) const
+{
+ return new XColorItem(*this);
+}
+
+bool XColorItem::operator==(const SfxPoolItem& rItem) const
+{
+ return ( NameOrIndex::operator==(rItem) &&
+ static_cast<const XColorItem&>(rItem).aColor == aColor ) &&
+ static_cast<const XColorItem&>(rItem).maThemeColor == maThemeColor;
+}
+
+const Color& XColorItem::GetColorValue() const
+{
+ assert(!IsIndex());
+ return aColor;
+
+}
+
+bool XColorItem::QueryValue( css::uno::Any& rVal, sal_uInt8 /*nMemberId*/) const
+{
+ rVal <<= GetColorValue().GetRGBColor();
+ return true;
+}
+
+bool XColorItem::PutValue( const css::uno::Any& rVal, sal_uInt8 /*nMemberId*/)
+{
+ Color nValue;
+ rVal >>= nValue;
+ SetColorValue( nValue );
+
+ return true;
+}
+
+void XColorItem::dumpAsXml(xmlTextWriterPtr pWriter) const
+{
+ (void)xmlTextWriterStartElement(pWriter, BAD_CAST("XColorItem"));
+ if (Which() == SDRATTR_SHADOWCOLOR)
+ {
+ (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("whichId"), BAD_CAST("SDRATTR_SHADOWCOLOR"));
+ }
+ else if (Which() == XATTR_FILLCOLOR)
+ {
+ (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("whichId"), BAD_CAST("XATTR_FILLCOLOR"));
+ }
+ (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("aColor"),
+ BAD_CAST(aColor.AsRGBHexString().toUtf8().getStr()));
+
+ NameOrIndex::dumpAsXml(pWriter);
+
+ maThemeColor.dumpAsXml(pWriter);
+
+ (void)xmlTextWriterEndElement(pWriter);
+}
+
+// --- line attributes ---
+
+
+SfxPoolItem* XLineStyleItem::CreateDefault() { return new XLineStyleItem; }
+
+XLineStyleItem::XLineStyleItem(css::drawing::LineStyle eTheLineStyle) :
+ SfxEnumItem(XATTR_LINESTYLE, eTheLineStyle)
+{
+}
+
+XLineStyleItem* XLineStyleItem::Clone(SfxItemPool* /*pPool*/) const
+{
+ return new XLineStyleItem( *this );
+}
+
+bool XLineStyleItem::GetPresentation
+(
+ SfxItemPresentation /*ePres*/,
+ MapUnit /*eCoreUnit*/,
+ MapUnit /*ePresUnit*/,
+ OUString& rText, const IntlWrapper&
+) const
+{
+ rText.clear();
+
+ TranslateId pId;
+
+ switch( GetValue() )
+ {
+ case css::drawing::LineStyle_NONE:
+ pId = RID_SVXSTR_INVISIBLE;
+ break;
+ case css::drawing::LineStyle_SOLID:
+ pId = RID_SVXSTR_SOLID;
+ break;
+ default: break;
+ }
+
+ if (pId)
+ rText = SvxResId(pId);
+ return true;
+}
+
+bool XLineStyleItem::QueryValue( css::uno::Any& rVal, sal_uInt8 /*nMemberId*/) const
+{
+ css::drawing::LineStyle eLS = GetValue();
+ rVal <<= eLS;
+ return true;
+}
+
+bool XLineStyleItem::PutValue( const css::uno::Any& rVal, sal_uInt8 /*nMemberId*/)
+{
+ css::drawing::LineStyle eLS;
+ if(!(rVal >>= eLS ))
+ {
+ // also try an int (for Basic)
+ sal_Int32 nLS = 0;
+ if(!(rVal >>= nLS))
+ return false;
+ eLS = static_cast<css::drawing::LineStyle>(nLS);
+ }
+
+ SetValue( eLS );
+ return true;
+}
+
+sal_uInt16 XLineStyleItem::GetValueCount() const
+{
+ return 3;
+}
+
+XDash::XDash(css::drawing::DashStyle eTheDash, sal_uInt16 nTheDots, double nTheDotLen,
+ sal_uInt16 nTheDashes, double nTheDashLen, double nTheDistance) :
+ eDash(eTheDash),
+ nDots(nTheDots),
+ nDashes(nTheDashes),
+ nDotLen(nTheDotLen),
+ nDashLen(nTheDashLen),
+ nDistance(nTheDistance)
+{
+}
+
+bool XDash::operator==(const XDash& rDash) const
+{
+ return ( eDash == rDash.eDash &&
+ nDots == rDash.nDots &&
+ nDotLen == rDash.nDotLen &&
+ nDashes == rDash.nDashes &&
+ nDashLen == rDash.nDashLen &&
+ nDistance == rDash.nDistance );
+}
+
+// XDash is translated into an array of doubles which describe the lengths of the
+// dashes, dots and empty passages. It returns the complete length of the full DashDot
+// sequence and fills the given vetor of doubles accordingly (also resizing, so deleting it).
+const double SMALLEST_DASH_WIDTH(26.95);
+
+double XDash::CreateDotDashArray(::std::vector< double >& rDotDashArray, double fLineWidth) const
+{
+ double fFullDotDashLen(0.0);
+ const sal_uInt16 nNumDotDashArray = (GetDots() + GetDashes()) * 2;
+ rDotDashArray.resize( nNumDotDashArray, 0.0 );
+ sal_uInt16 a;
+ sal_uInt16 nIns(0);
+ double fDashDotDistance = GetDistance();
+ double fSingleDashLen = GetDashLen();
+ double fSingleDotLen = GetDotLen();
+
+ if (fLineWidth == 0.0)
+ fLineWidth = SMALLEST_DASH_WIDTH;
+
+ if(GetDashStyle() == css::drawing::DashStyle_RECTRELATIVE || GetDashStyle() == css::drawing::DashStyle_ROUNDRELATIVE)
+ {
+ double fFactor = fLineWidth / 100.0;
+
+ if(GetDashes())
+ {
+ if(GetDashLen())
+ {
+ // is a dash
+ fSingleDashLen *= fFactor;
+ }
+ else
+ {
+ // is a dot
+ fSingleDashLen = fLineWidth;
+ }
+ }
+
+ if(GetDots())
+ {
+ if(GetDotLen())
+ {
+ // is a dash
+ fSingleDotLen *= fFactor;
+ }
+ else
+ {
+ // is a dot
+ fSingleDotLen = fLineWidth;
+ }
+ }
+
+ if(GetDashes() || GetDots())
+ {
+ if(GetDistance())
+ {
+ // dash as distance
+ fDashDotDistance *= fFactor;
+ }
+ else
+ {
+ // dot as distance
+ fDashDotDistance = fLineWidth;
+ }
+ }
+ }
+ else
+ {
+ // absolute values
+ if(GetDashes())
+ {
+ if(GetDashLen())
+ {
+ // is a dash
+ if(fSingleDashLen < SMALLEST_DASH_WIDTH)
+ {
+ fSingleDashLen = SMALLEST_DASH_WIDTH;
+ }
+ }
+ else
+ {
+ // is a dot
+ if(fSingleDashLen < fLineWidth)
+ {
+ fSingleDashLen = fLineWidth;
+ }
+ }
+ }
+
+ if(GetDots())
+ {
+ if(GetDotLen())
+ {
+ // is a dash
+ if(fSingleDotLen < SMALLEST_DASH_WIDTH)
+ {
+ fSingleDotLen = SMALLEST_DASH_WIDTH;
+ }
+ }
+ else
+ {
+ // is a dot
+ if(fSingleDotLen < fLineWidth)
+ {
+ fSingleDotLen = fLineWidth;
+ }
+ }
+ }
+
+ if(GetDashes() || GetDots())
+ {
+ if(GetDistance())
+ {
+ // dash as distance
+ if(fDashDotDistance < SMALLEST_DASH_WIDTH)
+ {
+ fDashDotDistance = SMALLEST_DASH_WIDTH;
+ }
+ }
+ else
+ {
+ // dot as distance
+ if(fDashDotDistance < fLineWidth)
+ {
+ fDashDotDistance = fLineWidth;
+ }
+ }
+ }
+ }
+
+ for(a=0;a<GetDots();a++)
+ {
+ rDotDashArray[nIns++] = fSingleDotLen;
+ fFullDotDashLen += fSingleDotLen;
+ rDotDashArray[nIns++] = fDashDotDistance;
+ fFullDotDashLen += fDashDotDistance;
+ }
+
+ for(a=0;a<GetDashes();a++)
+ {
+ rDotDashArray[nIns++] = fSingleDashLen;
+ fFullDotDashLen += fSingleDashLen;
+ rDotDashArray[nIns++] = fDashDotDistance;
+ fFullDotDashLen += fDashDotDistance;
+ }
+
+ return fFullDotDashLen;
+}
+
+SfxPoolItem* XLineDashItem::CreateDefault() {return new XLineDashItem;}
+
+XLineDashItem::XLineDashItem(const OUString& rName, const XDash& rTheDash) :
+ NameOrIndex(XATTR_LINEDASH, rName),
+ aDash(rTheDash)
+{
+}
+
+XLineDashItem::XLineDashItem(const XLineDashItem& rItem) :
+ NameOrIndex(rItem),
+ aDash(rItem.aDash)
+{
+}
+
+XLineDashItem::XLineDashItem(const XDash& rTheDash)
+: NameOrIndex( XATTR_LINEDASH, -1 ),
+ aDash(rTheDash)
+{
+}
+
+XLineDashItem* XLineDashItem::Clone(SfxItemPool* /*pPool*/) const
+{
+ return new XLineDashItem(*this);
+}
+
+bool XLineDashItem::operator==(const SfxPoolItem& rItem) const
+{
+ return ( NameOrIndex::operator==(rItem) &&
+ aDash == static_cast<const XLineDashItem&>(rItem).aDash );
+}
+
+bool XLineDashItem::GetPresentation
+(
+ SfxItemPresentation /*ePres*/,
+ MapUnit /*eCoreUnit*/,
+ MapUnit /*ePresUnit*/,
+ OUString& rText, const IntlWrapper&
+) const
+{
+ rText = GetName();
+ return true;
+}
+
+bool XLineDashItem::HasMetrics() const
+{
+ return true;
+}
+
+void XLineDashItem::ScaleMetrics(tools::Long nMul, tools::Long nDiv)
+{
+ aDash.SetDotLen( BigInt::Scale( aDash.GetDotLen(), nMul, nDiv ) );
+ aDash.SetDashLen( BigInt::Scale( aDash.GetDashLen(), nMul, nDiv ) );
+ aDash.SetDistance( BigInt::Scale( aDash.GetDistance(), nMul, nDiv ) );
+}
+
+bool XLineDashItem::QueryValue( css::uno::Any& rVal, sal_uInt8 nMemberId ) const
+{
+ nMemberId &= ~CONVERT_TWIPS;
+
+ switch ( nMemberId )
+ {
+ case 0:
+ {
+ css::drawing::LineDash aLineDash;
+
+ const XDash& rXD = GetDashValue();
+ aLineDash.Style = static_cast<css::drawing::DashStyle>(static_cast<sal_uInt16>(rXD.GetDashStyle()));
+ aLineDash.Dots = rXD.GetDots();
+ aLineDash.DotLen = rXD.GetDotLen();
+ aLineDash.Dashes = rXD.GetDashes();
+ aLineDash.DashLen = rXD.GetDashLen();
+ aLineDash.Distance = rXD.GetDistance();
+
+ uno::Sequence< beans::PropertyValue > aPropSeq{
+ comphelper::makePropertyValue("Name", SvxUnogetApiNameForItem(Which(), GetName())),
+ comphelper::makePropertyValue("LineDash", aLineDash)
+ };
+ rVal <<= aPropSeq;
+ break;
+ }
+
+ case MID_NAME:
+ {
+ rVal <<= SvxUnogetApiNameForItem(Which(), GetName());
+ break;
+ }
+
+ case MID_LINEDASH:
+ {
+ const XDash& rXD = GetDashValue();
+
+ css::drawing::LineDash aLineDash;
+
+ aLineDash.Style = static_cast<css::drawing::DashStyle>(static_cast<sal_uInt16>(rXD.GetDashStyle()));
+ aLineDash.Dots = rXD.GetDots();
+ aLineDash.DotLen = rXD.GetDotLen();
+ aLineDash.Dashes = rXD.GetDashes();
+ aLineDash.DashLen = rXD.GetDashLen();
+ aLineDash.Distance = rXD.GetDistance();
+
+ rVal <<= aLineDash;
+ break;
+ }
+
+ case MID_LINEDASH_STYLE:
+ {
+ const XDash& rXD = GetDashValue();
+ rVal <<= static_cast<css::drawing::DashStyle>(static_cast<sal_Int16>(rXD.GetDashStyle()));
+ break;
+ }
+
+ case MID_LINEDASH_DOTS:
+ {
+ const XDash& rXD = GetDashValue();
+ rVal <<= rXD.GetDots();
+ break;
+ }
+
+ case MID_LINEDASH_DOTLEN:
+ {
+ const XDash& rXD = GetDashValue();
+ rVal <<= rXD.GetDotLen();
+ break;
+ }
+
+ case MID_LINEDASH_DASHES:
+ {
+ const XDash& rXD = GetDashValue();
+ rVal <<= rXD.GetDashes();
+ break;
+ }
+
+ case MID_LINEDASH_DASHLEN:
+ {
+ const XDash& rXD = GetDashValue();
+ rVal <<= rXD.GetDashLen();
+ break;
+ }
+
+ case MID_LINEDASH_DISTANCE:
+ {
+ const XDash& rXD = GetDashValue();
+ rVal <<= rXD.GetDistance();
+ break;
+ }
+
+ default: OSL_FAIL("Wrong MemberId!"); return false;
+ }
+
+ return true;
+}
+
+bool XLineDashItem::PutValue( const css::uno::Any& rVal, sal_uInt8 nMemberId )
+{
+ nMemberId &= ~CONVERT_TWIPS;
+
+ switch ( nMemberId )
+ {
+ case 0:
+ {
+ uno::Sequence< beans::PropertyValue > aPropSeq;
+
+ if ( rVal >>= aPropSeq )
+ {
+ css::drawing::LineDash aLineDash;
+ OUString aName;
+ bool bLineDash( false );
+ for ( const auto& rProp : std::as_const(aPropSeq) )
+ {
+ if ( rProp.Name == "Name" )
+ rProp.Value >>= aName;
+ else if ( rProp.Name == "LineDash" )
+ {
+ if ( rProp.Value >>= aLineDash )
+ bLineDash = true;
+ }
+ }
+
+ SetName( aName );
+ if ( bLineDash )
+ {
+ XDash aXDash;
+
+ aXDash.SetDashStyle(static_cast<css::drawing::DashStyle>(static_cast<sal_uInt16>(aLineDash.Style)));
+ aXDash.SetDots(aLineDash.Dots);
+ aXDash.SetDotLen(aLineDash.DotLen);
+ aXDash.SetDashes(aLineDash.Dashes);
+ aXDash.SetDashLen(aLineDash.DashLen);
+ aXDash.SetDistance(aLineDash.Distance);
+
+ if((0 == aXDash.GetDots()) && (0 == aXDash.GetDashes()))
+ aXDash.SetDots(1);
+
+ SetDashValue( aXDash );
+ }
+
+ return true;
+ }
+
+ return false;
+ }
+
+ case MID_NAME:
+ {
+ OUString aName;
+ if (!(rVal >>= aName))
+ return false;
+ SetName( aName );
+ break;
+ }
+
+ case MID_LINEDASH:
+ {
+ css::drawing::LineDash aLineDash;
+ if(!(rVal >>= aLineDash))
+ return false;
+
+ XDash aXDash;
+
+ aXDash.SetDashStyle(static_cast<css::drawing::DashStyle>(static_cast<sal_uInt16>(aLineDash.Style)));
+ aXDash.SetDots(aLineDash.Dots);
+ aXDash.SetDotLen(aLineDash.DotLen);
+ aXDash.SetDashes(aLineDash.Dashes);
+ aXDash.SetDashLen(aLineDash.DashLen);
+ aXDash.SetDistance(aLineDash.Distance);
+
+ if((0 == aXDash.GetDots()) && (0 == aXDash.GetDashes()))
+ aXDash.SetDots(1);
+
+ SetDashValue( aXDash );
+ break;
+ }
+
+ case MID_LINEDASH_STYLE:
+ {
+ sal_Int16 nVal = sal_Int16();
+ if(!(rVal >>= nVal))
+ return false;
+
+ XDash aXDash = GetDashValue();
+ aXDash.SetDashStyle(static_cast<css::drawing::DashStyle>(static_cast<sal_uInt16>(nVal)));
+
+ if((0 == aXDash.GetDots()) && (0 == aXDash.GetDashes()))
+ aXDash.SetDots(1);
+
+ SetDashValue( aXDash );
+
+ break;
+ }
+
+ case MID_LINEDASH_DOTS:
+ case MID_LINEDASH_DASHES:
+ {
+ sal_Int16 nVal = sal_Int16();
+ if(!(rVal >>= nVal))
+ return false;
+
+ XDash aXDash = GetDashValue();
+ if ( nMemberId == MID_LINEDASH_DOTS )
+ aXDash.SetDots( nVal );
+ else
+ aXDash.SetDashes( nVal );
+
+ if((0 == aXDash.GetDots()) && (0 == aXDash.GetDashes()))
+ aXDash.SetDots(1);
+
+ SetDashValue( aXDash );
+ break;
+ }
+
+ case MID_LINEDASH_DOTLEN:
+ case MID_LINEDASH_DASHLEN:
+ case MID_LINEDASH_DISTANCE:
+ {
+ sal_uInt32 nVal = 0;
+ if(!(rVal >>= nVal))
+ return false;
+
+ XDash aXDash = GetDashValue();
+ if ( nMemberId == MID_LINEDASH_DOTLEN )
+ aXDash.SetDotLen( nVal );
+ else if ( nMemberId == MID_LINEDASH_DASHLEN )
+ aXDash.SetDashLen( nVal );
+ else
+ aXDash.SetDistance( nVal );
+
+ if((0 == aXDash.GetDots()) && (0 == aXDash.GetDashes()))
+ aXDash.SetDots(1);
+
+ SetDashValue( aXDash );
+ break;
+ }
+ }
+
+ return true;
+}
+
+bool XLineDashItem::CompareValueFunc( const NameOrIndex* p1, const NameOrIndex* p2 )
+{
+ return static_cast<const XLineDashItem*>(p1)->GetDashValue() == static_cast<const XLineDashItem*>(p2)->GetDashValue();
+}
+
+std::unique_ptr<XLineDashItem> XLineDashItem::checkForUniqueItem( SdrModel* pModel ) const
+{
+ if( pModel )
+ {
+ const OUString aUniqueName = NameOrIndex::CheckNamedItem(
+ this, XATTR_LINEDASH, &pModel->GetItemPool(),
+ XLineDashItem::CompareValueFunc, RID_SVXSTR_DASH20,
+ pModel->GetPropertyList( XPropertyListType::Dash ) );
+
+ // if the given name is not valid, replace it!
+ if( aUniqueName != GetName() )
+ return std::make_unique<XLineDashItem>( aUniqueName, aDash );
+ }
+
+ return nullptr;
+}
+
+SfxPoolItem* XLineWidthItem::CreateDefault() {return new XLineWidthItem;}
+
+XLineWidthItem::XLineWidthItem(tools::Long nWidth) :
+ SfxMetricItem(XATTR_LINEWIDTH, nWidth)
+{
+}
+
+XLineWidthItem* XLineWidthItem::Clone(SfxItemPool* /*pPool*/) const
+{
+ return new XLineWidthItem(*this);
+}
+
+bool XLineWidthItem::GetPresentation
+(
+ SfxItemPresentation /*ePres*/,
+ MapUnit eCoreUnit,
+ MapUnit ePresUnit,
+ OUString& rText, const IntlWrapper& rIntl
+) const
+{
+ rText = GetMetricText( static_cast<tools::Long>(GetValue()),
+ eCoreUnit, ePresUnit, &rIntl) +
+ " " + EditResId( GetMetricId( ePresUnit) );
+ return true;
+}
+
+bool XLineWidthItem::QueryValue( css::uno::Any& rVal, sal_uInt8 nMemberId ) const
+{
+ sal_Int32 nValue = GetValue();
+ if( 0 != (nMemberId&CONVERT_TWIPS) )
+ nValue = convertTwipToMm100(nValue);
+
+ rVal <<= nValue;
+ return true;
+}
+
+bool XLineWidthItem::PutValue( const css::uno::Any& rVal, sal_uInt8 nMemberId )
+{
+ sal_Int32 nValue = 0;
+ rVal >>= nValue;
+ if( 0 != (nMemberId&CONVERT_TWIPS) )
+ nValue = o3tl::toTwips(nValue, o3tl::Length::mm100);
+
+ SetValue( nValue );
+ return true;
+}
+
+SfxPoolItem* XLineColorItem::CreateDefault() { return new XLineColorItem; }
+
+XLineColorItem::XLineColorItem(sal_Int32 nIndex, const Color& rTheColor) :
+ XColorItem(XATTR_LINECOLOR, nIndex, rTheColor)
+{
+}
+
+XLineColorItem::XLineColorItem(const OUString& rName, const Color& rTheColor) :
+ XColorItem(XATTR_LINECOLOR, rName, rTheColor)
+{
+}
+
+XLineColorItem* XLineColorItem::Clone(SfxItemPool* /*pPool*/) const
+{
+ return new XLineColorItem(*this);
+}
+
+bool XLineColorItem::GetPresentation
+(
+ SfxItemPresentation /*ePres*/,
+ MapUnit /*eCoreUnit*/,
+ MapUnit /*ePresUnit*/,
+ OUString& rText, const IntlWrapper&
+) const
+{
+ rText = GetName();
+ return true;
+}
+
+bool XLineColorItem::QueryValue( css::uno::Any& rVal, sal_uInt8 /*nMemberId*/) const
+{
+ rVal <<= GetColorValue().GetRGBColor();
+ return true;
+}
+
+bool XLineColorItem::PutValue( const css::uno::Any& rVal, sal_uInt8 /*nMemberId*/)
+{
+ sal_Int32 nValue = 0;
+ if(!(rVal >>= nValue))
+ return false;
+
+ SetColorValue( Color(ColorTransparency, nValue) );
+ return true;
+}
+
+
+SfxPoolItem* XLineStartItem::CreateDefault() {return new XLineStartItem;}
+
+XLineStartItem::XLineStartItem(sal_Int32 nIndex)
+: NameOrIndex(XATTR_LINESTART, nIndex)
+{
+}
+
+XLineStartItem::XLineStartItem(const OUString& rName, const basegfx::B2DPolyPolygon& rPolyPolygon)
+: NameOrIndex(XATTR_LINESTART, rName),
+ maPolyPolygon(rPolyPolygon)
+{
+}
+
+XLineStartItem::XLineStartItem(const XLineStartItem& rItem)
+: NameOrIndex(rItem),
+ maPolyPolygon(rItem.maPolyPolygon)
+{
+}
+
+XLineStartItem::XLineStartItem(const basegfx::B2DPolyPolygon& rPolyPolygon)
+: NameOrIndex( XATTR_LINESTART, -1 ),
+ maPolyPolygon(rPolyPolygon)
+{
+}
+
+XLineStartItem* XLineStartItem::Clone(SfxItemPool* /*pPool*/) const
+{
+ return new XLineStartItem(*this);
+}
+
+bool XLineStartItem::operator==(const SfxPoolItem& rItem) const
+{
+ return ( NameOrIndex::operator==(rItem) && static_cast<const XLineStartItem&>(rItem).maPolyPolygon == maPolyPolygon );
+}
+
+bool XLineStartItem::GetPresentation
+(
+ SfxItemPresentation /*ePres*/,
+ MapUnit /*eCoreUnit*/,
+ MapUnit /*ePresUnit*/,
+ OUString& rText, const IntlWrapper&
+) const
+{
+ rText = GetName();
+ return true;
+}
+
+bool XLineStartItem::QueryValue( css::uno::Any& rVal, sal_uInt8 nMemberId ) const
+{
+ nMemberId &= ~CONVERT_TWIPS;
+ if( nMemberId == MID_NAME )
+ {
+ rVal <<= SvxUnogetApiNameForItem(Which(), GetName());
+ }
+ else
+ {
+ css::drawing::PolyPolygonBezierCoords aBezier;
+ basegfx::utils::B2DPolyPolygonToUnoPolyPolygonBezierCoords( maPolyPolygon, aBezier );
+ rVal <<= aBezier;
+ }
+
+ return true;
+}
+
+bool XLineStartItem::PutValue( const css::uno::Any& rVal, sal_uInt8 nMemberId )
+{
+ nMemberId &= ~CONVERT_TWIPS;
+ if( nMemberId == MID_NAME )
+ {
+ return false;
+ }
+ else
+ {
+ maPolyPolygon.clear();
+
+ if( rVal.hasValue() )
+ {
+ auto pCoords = o3tl::tryAccess<css::drawing::PolyPolygonBezierCoords>(
+ rVal);
+ if( !pCoords )
+ return false;
+
+ if( pCoords->Coordinates.getLength() > 0 )
+ {
+ maPolyPolygon = basegfx::utils::UnoPolyPolygonBezierCoordsToB2DPolyPolygon( *pCoords );
+ // #i72807# close line start/end polygons hard
+ // maPolyPolygon.setClosed(true);
+ }
+ }
+ }
+
+ return true;
+}
+
+/** this function searches in both the models pool and the styles pool for XLineStartItem
+ and XLineEndItem with the same value or name and returns an item with the value of
+ this item and a unique name for an item with this value. */
+std::unique_ptr<XLineStartItem> XLineStartItem::checkForUniqueItem( SdrModel* pModel ) const
+{
+ if( pModel )
+ {
+ std::unique_ptr<XLineStartItem> pTempItem;
+ const XLineStartItem* pLineStartItem = this;
+
+ OUString aUniqueName( GetName() );
+
+ if( !maPolyPolygon.count() )
+ {
+ // if the polygon is empty, check if the name is empty
+ if( aUniqueName.isEmpty() )
+ return nullptr;
+
+ // force empty name for empty polygons
+ return std::make_unique<XLineStartItem>( "", maPolyPolygon );
+ }
+
+ if( maPolyPolygon.count() > 1 )
+ {
+ // check if the polygon is closed
+ if(!maPolyPolygon.isClosed())
+ {
+ // force a closed polygon
+ basegfx::B2DPolyPolygon aNew(maPolyPolygon);
+ aNew.setClosed(true);
+ pTempItem.reset(new XLineStartItem( aUniqueName, aNew ));
+ pLineStartItem = pTempItem.get();
+ }
+ }
+
+ bool bForceNew = false;
+
+ // 2. if we have a name check if there is already an item with the
+ // same name in the documents pool with a different line end or start
+
+ const SfxItemPool& rPool1 = pModel->GetItemPool();
+ if (!aUniqueName.isEmpty())
+ {
+ for (const SfxPoolItem* p : rPool1.GetItemSurrogates(XATTR_LINESTART))
+ {
+ auto pItem = dynamic_cast<const XLineStartItem*>(p);
+
+ if( pItem && ( pItem->GetName() == pLineStartItem->GetName() ) )
+ {
+ // if there is already an item with the same name and the same
+ // value it's ok to set it
+ if( pItem->GetLineStartValue() != pLineStartItem->GetLineStartValue() )
+ {
+ // same name but different value, we need a new name for this item
+ aUniqueName.clear();
+ bForceNew = true;
+ }
+ break;
+ }
+ }
+
+ if( !bForceNew )
+ {
+ for (const SfxPoolItem* p : rPool1.GetItemSurrogates(XATTR_LINEEND))
+ {
+ auto pItem = dynamic_cast<const XLineEndItem*>(p);
+
+ if( pItem && ( pItem->GetName() == pLineStartItem->GetName() ) )
+ {
+ // if there is already an item with the same name and the same
+ // value it's ok to set it
+ if( pItem->GetLineEndValue() != pLineStartItem->GetLineStartValue() )
+ {
+ // same name but different value, we need a new name for this item
+ aUniqueName.clear();
+ bForceNew = true;
+ }
+ break;
+ }
+ }
+ }
+ }
+
+ const SfxItemPool* pPool2 = pModel->GetStyleSheetPool() ? &pModel->GetStyleSheetPool()->GetPool() : nullptr;
+ if( !aUniqueName.isEmpty() && pPool2)
+ {
+ for (const SfxPoolItem* p : pPool2->GetItemSurrogates(XATTR_LINESTART))
+ {
+ auto pItem = dynamic_cast<const XLineStartItem*>(p);
+
+ if( pItem && ( pItem->GetName() == pLineStartItem->GetName() ) )
+ {
+ // if there is already an item with the same name and the same
+ // value it's ok to set it
+ if( pItem->GetLineStartValue() != pLineStartItem->GetLineStartValue() )
+ {
+ // same name but different value, we need a new name for this item
+ aUniqueName.clear();
+ bForceNew = true;
+ }
+ break;
+ }
+ }
+
+ if( !bForceNew )
+ {
+ for (const SfxPoolItem* p : pPool2->GetItemSurrogates(XATTR_LINEEND))
+ {
+ auto pItem = dynamic_cast<const XLineEndItem*>(p);
+
+ if( pItem && ( pItem->GetName() == pLineStartItem->GetName() ) )
+ {
+ // if there is already an item with the same name and the same
+ // value it's ok to set it
+ if( pItem->GetLineEndValue() != pLineStartItem->GetLineStartValue() )
+ {
+ // same name but different value, we need a new name for this item
+ aUniqueName.clear();
+ bForceNew = true;
+ }
+ break;
+ }
+ }
+ }
+ }
+
+ // if we have no name yet, find existing item with same content or
+ // create a unique name
+ if( aUniqueName.isEmpty() )
+ {
+ bool bFoundExisting = false;
+
+ sal_Int32 nUserIndex = 1;
+ const OUString aUser(SvxResId(RID_SVXSTR_LINEEND));
+
+ for (const SfxPoolItem* p : rPool1.GetItemSurrogates(XATTR_LINESTART))
+ {
+ auto pItem = dynamic_cast<const XLineStartItem*>(p);
+
+ if (pItem && !pItem->GetName().isEmpty())
+ {
+ if (!bForceNew && pItem->GetLineStartValue() == pLineStartItem->GetLineStartValue())
+ {
+ aUniqueName = pItem->GetName();
+ bFoundExisting = true;
+ break;
+ }
+
+ if (pItem->GetName().startsWith(aUser))
+ {
+ sal_Int32 nThisIndex = o3tl::toInt32(pItem->GetName().subView(aUser.getLength()));
+ if (nThisIndex >= nUserIndex)
+ nUserIndex = nThisIndex + 1;
+ }
+ }
+ }
+
+ for (const SfxPoolItem* p : rPool1.GetItemSurrogates(XATTR_LINEEND))
+ {
+ auto pItem = dynamic_cast<const XLineEndItem*>(p);
+
+ if (pItem && !pItem->GetName().isEmpty())
+ {
+ if (!bForceNew && pItem->GetLineEndValue() == pLineStartItem->GetLineStartValue())
+ {
+ aUniqueName = pItem->GetName();
+ bFoundExisting = true;
+ break;
+ }
+
+ if (pItem->GetName().startsWith(aUser))
+ {
+ sal_Int32 nThisIndex = o3tl::toInt32(pItem->GetName().subView(aUser.getLength()));
+ if (nThisIndex >= nUserIndex)
+ nUserIndex = nThisIndex + 1;
+ }
+ }
+ }
+
+ if( !bFoundExisting )
+ {
+ aUniqueName = aUser + " " + OUString::number( nUserIndex );
+ }
+ }
+
+ // if the given name is not valid, replace it!
+ if( aUniqueName != GetName() || pTempItem )
+ {
+ if( pTempItem )
+ {
+ pTempItem->SetName( aUniqueName );
+ return pTempItem;
+ }
+ else
+ {
+ return std::make_unique<XLineStartItem>( aUniqueName, maPolyPolygon );
+ }
+ }
+ }
+
+ return nullptr;
+}
+
+SfxPoolItem* XLineEndItem::CreateDefault() {return new XLineEndItem;}
+
+XLineEndItem::XLineEndItem(sal_Int32 nIndex)
+: NameOrIndex(XATTR_LINEEND, nIndex)
+{
+}
+
+XLineEndItem::XLineEndItem(const OUString& rName, const basegfx::B2DPolyPolygon& rPolyPolygon)
+: NameOrIndex(XATTR_LINEEND, rName),
+ maPolyPolygon(rPolyPolygon)
+{
+}
+
+XLineEndItem::XLineEndItem(const XLineEndItem& rItem)
+: NameOrIndex(rItem),
+ maPolyPolygon(rItem.maPolyPolygon)
+{
+}
+
+XLineEndItem::XLineEndItem(const basegfx::B2DPolyPolygon& rPolyPolygon)
+: NameOrIndex( XATTR_LINEEND, -1 ),
+ maPolyPolygon(rPolyPolygon)
+{
+}
+
+XLineEndItem* XLineEndItem::Clone(SfxItemPool* /*pPool*/) const
+{
+ return new XLineEndItem(*this);
+}
+
+bool XLineEndItem::operator==(const SfxPoolItem& rItem) const
+{
+ return ( NameOrIndex::operator==(rItem) && static_cast<const XLineEndItem&>(rItem).maPolyPolygon == maPolyPolygon );
+}
+
+
+/** this function searches in both the models pool and the styles pool for XLineStartItem
+ and XLineEndItem with the same value or name and returns an item with the value of
+ this item and a unique name for an item with this value. */
+std::unique_ptr<XLineEndItem> XLineEndItem::checkForUniqueItem( SdrModel* pModel ) const
+{
+ if( pModel )
+ {
+ std::unique_ptr<XLineEndItem> pTempItem;
+ const XLineEndItem* pLineEndItem = this;
+
+ OUString aUniqueName( GetName() );
+
+ if( !maPolyPolygon.count() )
+ {
+ // if the polygon is empty, check if the name is empty
+ if( aUniqueName.isEmpty() )
+ return nullptr;
+
+ // force empty name for empty polygons
+ return std::make_unique<XLineEndItem>( "", maPolyPolygon );
+ }
+
+ if( maPolyPolygon.count() > 1 )
+ {
+ // check if the polygon is closed
+ if(!maPolyPolygon.isClosed())
+ {
+ // force a closed polygon
+ basegfx::B2DPolyPolygon aNew(maPolyPolygon);
+ aNew.setClosed(true);
+ pTempItem.reset(new XLineEndItem( aUniqueName, aNew ));
+ pLineEndItem = pTempItem.get();
+ }
+ }
+
+ bool bForceNew = false;
+
+ // 2. if we have a name check if there is already an item with the
+ // same name in the documents pool with a different line end or start
+
+ const SfxItemPool& rPool1 = pModel->GetItemPool();
+ if (!aUniqueName.isEmpty())
+ {
+ for (const SfxPoolItem* p : rPool1.GetItemSurrogates(XATTR_LINESTART))
+ {
+ auto pItem = dynamic_cast<const XLineStartItem*>(p);
+
+ if( pItem && ( pItem->GetName() == pLineEndItem->GetName() ) )
+ {
+ // if there is already an item with the same name and the same
+ // value it's ok to set it
+ if( pItem->GetLineStartValue() != pLineEndItem->GetLineEndValue() )
+ {
+ // same name but different value, we need a new name for this item
+ aUniqueName.clear();
+ bForceNew = true;
+ }
+ break;
+ }
+ }
+
+ if( !bForceNew )
+ {
+ for (const SfxPoolItem* p : rPool1.GetItemSurrogates(XATTR_LINEEND))
+ {
+ auto pItem = dynamic_cast<const XLineEndItem*>(p);
+
+ if( pItem && ( pItem->GetName() == pLineEndItem->GetName() ) )
+ {
+ // if there is already an item with the same name and the same
+ // value it's ok to set it
+ if( pItem->GetLineEndValue() != pLineEndItem->GetLineEndValue() )
+ {
+ // same name but different value, we need a new name for this item
+ aUniqueName.clear();
+ bForceNew = true;
+ }
+ break;
+ }
+ }
+ }
+ }
+
+ const SfxItemPool* pPool2 = pModel->GetStyleSheetPool() ? &pModel->GetStyleSheetPool()->GetPool() : nullptr;
+ if( !aUniqueName.isEmpty() && pPool2)
+ {
+ for (const SfxPoolItem* p : pPool2->GetItemSurrogates(XATTR_LINESTART))
+ {
+ auto pItem = dynamic_cast<const XLineStartItem*>(p);
+
+ if( pItem && ( pItem->GetName() == pLineEndItem->GetName() ) )
+ {
+ // if there is already an item with the same name and the same
+ // value it's ok to set it
+ if( pItem->GetLineStartValue() != pLineEndItem->GetLineEndValue() )
+ {
+ // same name but different value, we need a new name for this item
+ aUniqueName.clear();
+ bForceNew = true;
+ }
+ break;
+ }
+ }
+
+ if( !bForceNew )
+ {
+ for (const SfxPoolItem* p : pPool2->GetItemSurrogates(XATTR_LINEEND))
+ {
+ auto pItem = dynamic_cast<const XLineEndItem*>(p);
+
+ if( pItem && ( pItem->GetName() == pLineEndItem->GetName() ) )
+ {
+ // if there is already an item with the same name and the same
+ // value it's ok to set it
+ if( pItem->GetLineEndValue() != pLineEndItem->GetLineEndValue() )
+ {
+ // same name but different value, we need a new name for this item
+ aUniqueName.clear();
+ bForceNew = true;
+ }
+ break;
+ }
+ }
+ }
+ }
+
+ // if we have no name yet, find existing item with same content or
+ // create a unique name
+ if( aUniqueName.isEmpty() )
+ {
+ bool bFoundExisting = false;
+
+ sal_Int32 nUserIndex = 1;
+ const OUString aUser(SvxResId(RID_SVXSTR_LINEEND));
+
+ for (const SfxPoolItem* p : rPool1.GetItemSurrogates(XATTR_LINESTART))
+ {
+ auto pItem = dynamic_cast<const XLineStartItem*>(p);
+
+ if (pItem && !pItem->GetName().isEmpty())
+ {
+ if (!bForceNew && pItem->GetLineStartValue() == pLineEndItem->GetLineEndValue())
+ {
+ aUniqueName = pItem->GetName();
+ bFoundExisting = true;
+ break;
+ }
+
+ if (pItem->GetName().startsWith(aUser))
+ {
+ sal_Int32 nThisIndex = o3tl::toInt32(pItem->GetName().subView(aUser.getLength()));
+ if (nThisIndex >= nUserIndex)
+ nUserIndex = nThisIndex + 1;
+ }
+ }
+ }
+
+ for (const SfxPoolItem* p : rPool1.GetItemSurrogates(XATTR_LINEEND))
+ {
+ auto pItem = dynamic_cast<const XLineEndItem*>(p);
+
+ if (pItem && !pItem->GetName().isEmpty())
+ {
+ if (!bForceNew && pItem->GetLineEndValue() == pLineEndItem->GetLineEndValue())
+ {
+ aUniqueName = pItem->GetName();
+ bFoundExisting = true;
+ break;
+ }
+
+ if (pItem->GetName().startsWith(aUser))
+ {
+ sal_Int32 nThisIndex = o3tl::toInt32(pItem->GetName().subView(aUser.getLength()));
+ if (nThisIndex >= nUserIndex)
+ nUserIndex = nThisIndex + 1;
+ }
+ }
+ }
+
+ if( !bFoundExisting )
+ {
+ aUniqueName = aUser + " " + OUString::number( nUserIndex );
+ }
+ }
+
+ // if the given name is not valid, replace it!
+ if( aUniqueName != GetName() || pTempItem )
+ {
+ if( pTempItem )
+ {
+ pTempItem->SetName( aUniqueName );
+ return pTempItem;
+ }
+ else
+ {
+ return std::make_unique<XLineEndItem>( aUniqueName, maPolyPolygon );
+ }
+ }
+ }
+
+ return nullptr;
+}
+
+bool XLineEndItem::GetPresentation
+(
+ SfxItemPresentation /*ePres*/,
+ MapUnit /*eCoreUnit*/,
+ MapUnit /*ePresUnit*/,
+ OUString& rText, const IntlWrapper&
+) const
+{
+ rText = GetName();
+ return true;
+}
+
+bool XLineEndItem::QueryValue( css::uno::Any& rVal, sal_uInt8 nMemberId ) const
+{
+ nMemberId &= ~CONVERT_TWIPS;
+ if( nMemberId == MID_NAME )
+ {
+ rVal <<= SvxUnogetApiNameForItem(Which(), GetName());
+ }
+ else
+ {
+ css::drawing::PolyPolygonBezierCoords aBezier;
+ basegfx::utils::B2DPolyPolygonToUnoPolyPolygonBezierCoords( maPolyPolygon, aBezier );
+ rVal <<= aBezier;
+ }
+ return true;
+}
+
+bool XLineEndItem::PutValue( const css::uno::Any& rVal, sal_uInt8 nMemberId )
+{
+ nMemberId &= ~CONVERT_TWIPS;
+ if( nMemberId == MID_NAME )
+ {
+ return false;
+ }
+ else
+ {
+ maPolyPolygon.clear();
+
+ if( rVal.hasValue() )
+ {
+ auto pCoords = o3tl::tryAccess<css::drawing::PolyPolygonBezierCoords>(
+ rVal);
+ if( !pCoords )
+ return false;
+
+ if( pCoords->Coordinates.getLength() > 0 )
+ {
+ maPolyPolygon = basegfx::utils::UnoPolyPolygonBezierCoordsToB2DPolyPolygon( *pCoords );
+ // #i72807# close line start/end polygons hard
+ // maPolyPolygon.setClosed(true);
+ }
+ }
+ }
+
+ return true;
+}
+
+XLineStartWidthItem::XLineStartWidthItem(tools::Long nWidth) :
+ SfxMetricItem(XATTR_LINESTARTWIDTH, nWidth)
+{
+}
+
+XLineStartWidthItem* XLineStartWidthItem::Clone(SfxItemPool* /*pPool*/) const
+{
+ return new XLineStartWidthItem(*this);
+}
+
+bool XLineStartWidthItem::GetPresentation
+(
+ SfxItemPresentation /*ePres*/,
+ MapUnit eCoreUnit,
+ MapUnit ePresUnit,
+ OUString& rText, const IntlWrapper& rIntl
+) const
+{
+ rText = GetMetricText( static_cast<tools::Long>(GetValue()),
+ eCoreUnit, ePresUnit, &rIntl) +
+ " " + EditResId( GetMetricId( ePresUnit) );
+ return true;
+}
+
+bool XLineStartWidthItem::QueryValue( css::uno::Any& rVal, sal_uInt8 /*nMemberId*/) const
+{
+ rVal <<= GetValue();
+ return true;
+}
+
+bool XLineStartWidthItem::PutValue( const css::uno::Any& rVal, sal_uInt8 /*nMemberId*/)
+{
+ sal_Int32 nValue = 0;
+ rVal >>= nValue;
+ SetValue( nValue );
+ return true;
+}
+
+XLineEndWidthItem::XLineEndWidthItem(tools::Long nWidth) :
+ SfxMetricItem(XATTR_LINEENDWIDTH, nWidth)
+{
+}
+
+XLineEndWidthItem* XLineEndWidthItem::Clone(SfxItemPool* /*pPool*/) const
+{
+ return new XLineEndWidthItem(*this);
+}
+
+bool XLineEndWidthItem::GetPresentation
+(
+ SfxItemPresentation /*ePres*/,
+ MapUnit eCoreUnit,
+ MapUnit ePresUnit,
+ OUString& rText, const IntlWrapper& rIntl
+) const
+{
+ rText = GetMetricText( static_cast<tools::Long>(GetValue()),
+ eCoreUnit, ePresUnit, &rIntl) +
+ " " + EditResId( GetMetricId( ePresUnit) );
+ return true;
+}
+
+bool XLineEndWidthItem::QueryValue( css::uno::Any& rVal, sal_uInt8 /*nMemberId*/) const
+{
+ rVal <<= GetValue();
+ return true;
+}
+
+bool XLineEndWidthItem::PutValue( const css::uno::Any& rVal, sal_uInt8 /*nMemberId*/)
+{
+ sal_Int32 nValue = 0;
+ rVal >>= nValue;
+ SetValue( nValue );
+ return true;
+}
+
+XLineStartCenterItem::XLineStartCenterItem(bool bStartCenter) :
+ SfxBoolItem(XATTR_LINESTARTCENTER, bStartCenter)
+{
+}
+
+XLineStartCenterItem* XLineStartCenterItem::Clone(SfxItemPool* /*pPool*/) const
+{
+ return new XLineStartCenterItem(*this);
+}
+
+bool XLineStartCenterItem::GetPresentation
+(
+ SfxItemPresentation /*ePres*/,
+ MapUnit /*eCoreUnit*/,
+ MapUnit /*ePresUnit*/,
+ OUString& rText, const IntlWrapper&
+) const
+{
+ rText = SvxResId(GetValue() ? RID_SVXSTR_CENTERED : RID_SVXSTR_NOTCENTERED);
+ return true;
+}
+
+bool XLineStartCenterItem::QueryValue( css::uno::Any& rVal, sal_uInt8 /*nMemberId*/) const
+{
+ bool bValue = GetValue();
+ rVal <<= bValue;
+ return true;
+}
+
+bool XLineStartCenterItem::PutValue( const css::uno::Any& rVal, sal_uInt8 /*nMemberId*/)
+{
+ auto b = o3tl::tryAccess<bool>(rVal);
+ if( !b )
+ return false;
+
+ SetValue( *b );
+ return true;
+}
+
+XLineEndCenterItem::XLineEndCenterItem(bool bEndCenter) :
+ SfxBoolItem(XATTR_LINEENDCENTER, bEndCenter)
+{
+}
+
+XLineEndCenterItem* XLineEndCenterItem::Clone(SfxItemPool* /*pPool*/) const
+{
+ return new XLineEndCenterItem(*this);
+}
+
+bool XLineEndCenterItem::GetPresentation
+(
+ SfxItemPresentation /*ePres*/,
+ MapUnit /*eCoreUnit*/,
+ MapUnit /*ePresUnit*/,
+ OUString& rText, const IntlWrapper&
+) const
+{
+ rText = SvxResId(GetValue() ? RID_SVXSTR_CENTERED : RID_SVXSTR_NOTCENTERED);
+ return true;
+}
+
+bool XLineEndCenterItem::QueryValue( css::uno::Any& rVal, sal_uInt8 /*nMemberId*/) const
+{
+ bool bValue = GetValue();
+ rVal <<= bValue;
+ return true;
+}
+
+bool XLineEndCenterItem::PutValue( const css::uno::Any& rVal, sal_uInt8 /*nMemberId*/)
+{
+ auto b = o3tl::tryAccess<bool>(rVal);
+ if( !b )
+ return false;
+
+ SetValue( *b );
+ return true;
+}
+
+// --- fill attributes ---
+
+
+SfxPoolItem* XFillStyleItem::CreateDefault() { return new XFillStyleItem; }
+
+XFillStyleItem::XFillStyleItem(drawing::FillStyle eFillStyle) :
+ SfxEnumItem(XATTR_FILLSTYLE, eFillStyle)
+{
+}
+
+XFillStyleItem* XFillStyleItem::Clone(SfxItemPool* /*pPool*/) const
+{
+ return new XFillStyleItem( *this );
+}
+
+bool XFillStyleItem::GetPresentation
+(
+ SfxItemPresentation /*ePres*/,
+ MapUnit /*eCoreUnit*/,
+ MapUnit /*ePresUnit*/,
+ OUString& rText, const IntlWrapper&
+) const
+{
+ rText.clear();
+
+ TranslateId pId;
+
+ switch( GetValue() )
+ {
+ case drawing::FillStyle_NONE:
+ pId = RID_SVXSTR_INVISIBLE;
+ break;
+ case drawing::FillStyle_SOLID:
+ pId = RID_SVXSTR_SOLID;
+ break;
+ case drawing::FillStyle_GRADIENT:
+ pId = RID_SVXSTR_GRADIENT;
+ break;
+ case drawing::FillStyle_HATCH:
+ pId = RID_SVXSTR_HATCH;
+ break;
+ case drawing::FillStyle_BITMAP:
+ pId = RID_SVXSTR_BITMAP;
+ break;
+ default: break;
+ }
+
+ if (pId)
+ rText = SvxResId(pId);
+ return true;
+}
+
+sal_uInt16 XFillStyleItem::GetValueCount() const
+{
+ return 5;
+}
+
+bool XFillStyleItem::QueryValue( css::uno::Any& rVal, sal_uInt8 /*nMemberId*/) const
+{
+ css::drawing::FillStyle eFS = GetValue();
+
+ rVal <<= eFS;
+
+ return true;
+}
+
+bool XFillStyleItem::PutValue( const css::uno::Any& rVal, sal_uInt8 /*nMemberId*/)
+{
+ css::drawing::FillStyle eFS;
+ if(!(rVal >>= eFS))
+ {
+ // also try an int (for Basic)
+ sal_Int32 nFS = 0;
+ if(!(rVal >>= nFS))
+ return false;
+ eFS = static_cast<css::drawing::FillStyle>(nFS);
+ }
+
+ SetValue( eFS );
+
+ return true;
+}
+
+void XFillStyleItem::dumpAsXml(xmlTextWriterPtr pWriter) const
+{
+ (void)xmlTextWriterStartElement(pWriter, BAD_CAST("XFillStyleItem"));
+ (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("whichId"), BAD_CAST(OString::number(Which()).getStr()));
+ (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("value"), BAD_CAST(OString::number(static_cast<sal_Int16>(GetValue())).getStr()));
+
+ OUString aPresentation;
+ IntlWrapper aIntlWrapper(SvtSysLocale().GetUILanguageTag());
+ GetPresentation(SfxItemPresentation::Nameless, MapUnit::Map100thMM, MapUnit::Map100thMM, aPresentation, aIntlWrapper);
+ (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("presentation"), BAD_CAST(aPresentation.toUtf8().getStr()));
+
+ (void)xmlTextWriterEndElement(pWriter);
+}
+
+boost::property_tree::ptree XFillStyleItem::dumpAsJSON() const
+{
+ boost::property_tree::ptree aTree = SfxPoolItem::dumpAsJSON();
+
+ if (Which() == XATTR_FILLSTYLE)
+ aTree.put("commandName", ".uno:FillStyle");
+
+ OUString sValue;
+
+ switch( GetValue() )
+ {
+ case drawing::FillStyle_NONE:
+ sValue = "NONE";
+ break;
+ case drawing::FillStyle_SOLID:
+ sValue = "SOLID";
+ break;
+ case drawing::FillStyle_GRADIENT:
+ sValue = "GRADIENT";
+ break;
+ case drawing::FillStyle_HATCH:
+ sValue = "HATCH";
+ break;
+ case drawing::FillStyle_BITMAP:
+ sValue = "BITMAP";
+ break;
+ default: break;
+ }
+
+ aTree.put("state", sValue);
+
+ return aTree;
+}
+
+
+SfxPoolItem* XFillColorItem::CreateDefault() { return new XFillColorItem; }
+
+XFillColorItem::XFillColorItem(sal_Int32 nIndex, const Color& rTheColor) :
+ XColorItem(XATTR_FILLCOLOR, nIndex, rTheColor)
+{
+}
+
+XFillColorItem::XFillColorItem(const OUString& rName, const Color& rTheColor) :
+ XColorItem(XATTR_FILLCOLOR, rName, rTheColor)
+{
+}
+
+XFillColorItem* XFillColorItem::Clone(SfxItemPool* /*pPool*/) const
+{
+ return new XFillColorItem(*this);
+}
+
+bool XFillColorItem::GetPresentation
+(
+ SfxItemPresentation /*ePres*/,
+ MapUnit /*eCoreUnit*/,
+ MapUnit /*ePresUnit*/,
+ OUString& rText, const IntlWrapper&
+) const
+{
+ rText = GetName();
+ return true;
+}
+
+bool XFillColorItem::QueryValue( css::uno::Any& rVal, sal_uInt8 nMemberId ) const
+{
+ nMemberId &= ~CONVERT_TWIPS;
+ switch (nMemberId)
+ {
+ case MID_COLOR_THEME_INDEX:
+ {
+ rVal <<= GetThemeColor().GetThemeIndex();
+ break;
+ }
+ case MID_COLOR_LUM_MOD:
+ {
+ rVal <<= GetThemeColor().GetLumMod();
+ break;
+ }
+ case MID_COLOR_LUM_OFF:
+ {
+ rVal <<= GetThemeColor().GetLumOff();
+ break;
+ }
+ default:
+ {
+ rVal <<= GetColorValue().GetRGBColor();
+ break;
+ }
+ }
+
+ return true;
+}
+
+bool XFillColorItem::PutValue( const css::uno::Any& rVal, sal_uInt8 nMemberId )
+{
+ nMemberId &= ~CONVERT_TWIPS;
+ switch(nMemberId)
+ {
+ case MID_COLOR_THEME_INDEX:
+ {
+ sal_Int16 nIndex = -1;
+ if (!(rVal >>= nIndex))
+ return false;
+ GetThemeColor().SetThemeIndex(nIndex);
+ break;
+ }
+ case MID_COLOR_LUM_MOD:
+ {
+ sal_Int16 nLumMod = -1;
+ if (!(rVal >>= nLumMod))
+ return false;
+ GetThemeColor().SetLumMod(nLumMod);
+ }
+ break;
+ case MID_COLOR_LUM_OFF:
+ {
+ sal_Int16 nLumOff = -1;
+ if (!(rVal >>= nLumOff))
+ return false;
+ GetThemeColor().SetLumOff(nLumOff);
+ }
+ break;
+ default:
+ {
+ Color nValue;
+ if(!(rVal >>= nValue ))
+ return false;
+
+ SetColorValue( nValue );
+ break;
+ }
+ }
+ return true;
+}
+
+void XFillColorItem::dumpAsXml(xmlTextWriterPtr pWriter) const
+{
+ (void)xmlTextWriterStartElement(pWriter, BAD_CAST("XFillColorItem"));
+ (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("whichId"), BAD_CAST(OString::number(Which()).getStr()));
+
+ XColorItem::dumpAsXml(pWriter);
+
+ (void)xmlTextWriterEndElement(pWriter);
+}
+
+boost::property_tree::ptree XFillColorItem::dumpAsJSON() const
+{
+ boost::property_tree::ptree aTree = SfxPoolItem::dumpAsJSON();
+
+ if (Which() == XATTR_FILLCOLOR)
+ aTree.put("commandName", ".uno:FillPageColor");
+
+ aTree.put("state", GetColorValue().AsRGBHexString());
+
+ return aTree;
+}
+
+XSecondaryFillColorItem::XSecondaryFillColorItem(const OUString& rName, const Color& rTheColor) :
+ XColorItem(XATTR_SECONDARYFILLCOLOR, rName, rTheColor)
+{
+}
+
+XSecondaryFillColorItem* XSecondaryFillColorItem::Clone(SfxItemPool* /*pPool*/) const
+{
+ return new XSecondaryFillColorItem(*this);
+}
+
+bool XSecondaryFillColorItem::GetPresentation
+(
+ SfxItemPresentation /*ePres*/,
+ MapUnit /*eCoreUnit*/,
+ MapUnit /*ePresUnit*/,
+ OUString& rText, const IntlWrapper&
+) const
+{
+ rText = GetName();
+ return true;
+}
+
+std::string XGradient::GradientStyleToString(css::awt::GradientStyle eStyle)
+{
+ switch (eStyle)
+ {
+ case css::awt::GradientStyle::GradientStyle_LINEAR:
+ return "LINEAR";
+
+ case css::awt::GradientStyle::GradientStyle_AXIAL:
+ return "AXIAL";
+
+ case css::awt::GradientStyle::GradientStyle_RADIAL:
+ return "RADIAL";
+
+ case css::awt::GradientStyle::GradientStyle_ELLIPTICAL:
+ return "ELLIPTICAL";
+
+ case css::awt::GradientStyle::GradientStyle_SQUARE:
+ return "SQUARE";
+
+ case css::awt::GradientStyle::GradientStyle_RECT:
+ return "RECT";
+
+ case css::awt::GradientStyle::GradientStyle_MAKE_FIXED_SIZE:
+ return "MAKE_FIXED_SIZE";
+ }
+
+ return "";
+}
+
+namespace
+{
+ css::awt::GradientStyle lcl_getStyleFromString(std::u16string_view rStyle)
+ {
+ if (rStyle == u"LINEAR")
+ return css::awt::GradientStyle_LINEAR;
+ else if (rStyle == u"AXIAL")
+ return css::awt::GradientStyle_AXIAL;
+ else if (rStyle == u"RADIAL")
+ return css::awt::GradientStyle_RADIAL;
+ else if (rStyle == u"ELLIPTICAL")
+ return css::awt::GradientStyle_ELLIPTICAL;
+ else if (rStyle == u"SQUARE")
+ return css::awt::GradientStyle_SQUARE;
+ else if (rStyle == u"RECT")
+ return css::awt::GradientStyle_RECT;
+
+ return css::awt::GradientStyle_LINEAR;
+ }
+
+ StringMap lcl_jsonToStringMap(const OUString& rJSON)
+ {
+ StringMap aArgs;
+ if (rJSON.getLength() && rJSON[0] != '\0')
+ {
+ std::stringstream aStream(OUStringToOString(rJSON, RTL_TEXTENCODING_ASCII_US).getStr());
+ boost::property_tree::ptree aTree;
+ boost::property_tree::read_json(aStream, aTree);
+
+ for (const auto& rPair : aTree)
+ {
+ aArgs[OUString::fromUtf8(rPair.first.c_str())] = OUString::fromUtf8(rPair.second.get_value<std::string>(".").c_str());
+ }
+ }
+ return aArgs;
+ }
+
+ XGradient lcl_buildGradientFromStringMap(StringMap& rMap)
+ {
+ XGradient aGradient;
+
+ aGradient.SetStartColor(Color(ColorTransparency, rMap["startcolor"].toInt32(16)));
+ aGradient.SetEndColor(Color(ColorTransparency, rMap["endcolor"].toInt32(16)));
+ aGradient.SetGradientStyle(lcl_getStyleFromString(rMap["style"]));
+ aGradient.SetAngle(Degree10(rMap["angle"].toInt32()));
+
+ return aGradient;
+ }
+}
+
+XGradient XGradient::fromJSON(const OUString& rJSON)
+{
+ StringMap aMap(lcl_jsonToStringMap(rJSON));
+ return lcl_buildGradientFromStringMap(aMap);
+}
+
+css::awt::Gradient XGradient::toGradientUNO() const
+{
+ css::awt::Gradient aGradient;
+
+ aGradient.Style = this->GetGradientStyle();
+ aGradient.StartColor = static_cast<sal_Int32>(this->GetStartColor());
+ aGradient.EndColor = static_cast<sal_Int32>(this->GetEndColor());
+ aGradient.Angle = static_cast<short>(this->GetAngle());
+ aGradient.Border = this->GetBorder();
+ aGradient.XOffset = this->GetXOffset();
+ aGradient.YOffset = this->GetYOffset();
+ aGradient.StartIntensity = this->GetStartIntens();
+ aGradient.EndIntensity = this->GetEndIntens();
+ aGradient.StepCount = this->GetSteps();
+
+ return aGradient;
+}
+
+XGradient::XGradient() :
+ eStyle( css::awt::GradientStyle_LINEAR ),
+ aStartColor( COL_BLACK ),
+ aEndColor( COL_WHITE ),
+ nAngle( 0 ),
+ nBorder( 0 ),
+ nOfsX( 50 ),
+ nOfsY( 50 ),
+ nIntensStart( 100 ),
+ nIntensEnd( 100 ),
+ nStepCount( 0 )
+{
+}
+
+XGradient::XGradient(const Color& rStart, const Color& rEnd,
+ css::awt::GradientStyle eTheStyle, Degree10 nTheAngle, sal_uInt16 nXOfs,
+ sal_uInt16 nYOfs, sal_uInt16 nTheBorder,
+ sal_uInt16 nStartIntens, sal_uInt16 nEndIntens,
+ sal_uInt16 nSteps) :
+ eStyle(eTheStyle),
+ aStartColor(rStart),
+ aEndColor(rEnd),
+ nAngle(nTheAngle),
+ nBorder(nTheBorder),
+ nOfsX(nXOfs),
+ nOfsY(nYOfs),
+ nIntensStart(nStartIntens),
+ nIntensEnd(nEndIntens),
+ nStepCount(nSteps)
+{
+}
+
+bool XGradient::operator==(const XGradient& rGradient) const
+{
+ return ( eStyle == rGradient.eStyle &&
+ aStartColor == rGradient.aStartColor &&
+ aEndColor == rGradient.aEndColor &&
+ nAngle == rGradient.nAngle &&
+ nBorder == rGradient.nBorder &&
+ nOfsX == rGradient.nOfsX &&
+ nOfsY == rGradient.nOfsY &&
+ nIntensStart == rGradient.nIntensStart &&
+ nIntensEnd == rGradient.nIntensEnd &&
+ nStepCount == rGradient.nStepCount );
+}
+
+boost::property_tree::ptree XGradient::dumpAsJSON() const
+{
+ boost::property_tree::ptree aTree;
+
+ aTree.put("style", XGradient::GradientStyleToString(eStyle));
+ aTree.put("startcolor",aStartColor.AsRGBHexString());
+ aTree.put("endcolor", aEndColor.AsRGBHexString());
+ aTree.put("angle", std::to_string(nAngle.get()));
+ aTree.put("border", std::to_string(nBorder));
+ aTree.put("x", std::to_string(nOfsX));
+ aTree.put("y", std::to_string(nOfsY));
+ aTree.put("intensstart", std::to_string(nIntensStart));
+ aTree.put("intensend", std::to_string(nIntensEnd));
+ aTree.put("stepcount", std::to_string(nStepCount));
+
+ return aTree;
+}
+
+SfxPoolItem* XFillGradientItem::CreateDefault() { return new XFillGradientItem; }
+
+XFillGradientItem::XFillGradientItem(sal_Int32 nIndex,
+ const XGradient& rTheGradient) :
+ NameOrIndex(XATTR_FILLGRADIENT, nIndex),
+ aGradient(rTheGradient)
+{
+}
+
+XFillGradientItem::XFillGradientItem(const OUString& rName,
+ const XGradient& rTheGradient, TypedWhichId<XFillGradientItem> nWhich)
+ : NameOrIndex(nWhich, rName)
+ , aGradient(rTheGradient)
+{
+}
+
+XFillGradientItem::XFillGradientItem(const XFillGradientItem& rItem) :
+ NameOrIndex(rItem),
+ aGradient(rItem.aGradient)
+{
+}
+
+XFillGradientItem::XFillGradientItem( const XGradient& rTheGradient )
+: NameOrIndex( XATTR_FILLGRADIENT, -1 ),
+ aGradient(rTheGradient)
+{
+}
+
+XFillGradientItem* XFillGradientItem::Clone(SfxItemPool* /*pPool*/) const
+{
+ return new XFillGradientItem(*this);
+}
+
+bool XFillGradientItem::operator==(const SfxPoolItem& rItem) const
+{
+ return ( NameOrIndex::operator==(rItem) &&
+ aGradient == static_cast<const XFillGradientItem&>(rItem).aGradient );
+}
+
+const XGradient& XFillGradientItem::GetGradientValue() const // GetValue -> GetGradientValue
+{
+ if (!IsIndex())
+ return aGradient;
+ // ToDo: This should fail. We never called this code with a table so this should always
+ // have failed. Thus, I'm thinking that XFillGradientItem can't be an Index.
+ return aGradient;
+}
+
+bool XFillGradientItem::GetPresentation
+(
+ SfxItemPresentation /*ePres*/,
+ MapUnit /*eCoreUnit*/,
+ MapUnit /*ePresUnit*/,
+ OUString& rText, const IntlWrapper&
+) const
+{
+ rText = GetName();
+ return true;
+}
+
+bool XFillGradientItem::QueryValue( css::uno::Any& rVal, sal_uInt8 nMemberId ) const
+{
+ nMemberId &= ~CONVERT_TWIPS;
+ switch ( nMemberId )
+ {
+ case 0:
+ {
+ css::awt::Gradient aGradient2;
+
+ const XGradient& aXGradient = GetGradientValue();
+ aGradient2.Style = aXGradient.GetGradientStyle();
+ aGradient2.StartColor = static_cast<sal_Int32>(aXGradient.GetStartColor());
+ aGradient2.EndColor = static_cast<sal_Int32>(aXGradient.GetEndColor());
+ aGradient2.Angle = static_cast<short>(aXGradient.GetAngle());
+ aGradient2.Border = aXGradient.GetBorder();
+ aGradient2.XOffset = aXGradient.GetXOffset();
+ aGradient2.YOffset = aXGradient.GetYOffset();
+ aGradient2.StartIntensity = aXGradient.GetStartIntens();
+ aGradient2.EndIntensity = aXGradient.GetEndIntens();
+ aGradient2.StepCount = aXGradient.GetSteps();
+
+ uno::Sequence< beans::PropertyValue > aPropSeq{
+ comphelper::makePropertyValue("Name", SvxUnogetApiNameForItem(Which(), GetName())),
+ comphelper::makePropertyValue("FillGradient", aGradient2)
+ };
+ rVal <<= aPropSeq;
+ break;
+ }
+
+ case MID_FILLGRADIENT:
+ {
+ const XGradient& aXGradient = GetGradientValue();
+ css::awt::Gradient aGradient2;
+
+ aGradient2.Style = aXGradient.GetGradientStyle();
+ aGradient2.StartColor = static_cast<sal_Int32>(aXGradient.GetStartColor());
+ aGradient2.EndColor = static_cast<sal_Int32>(aXGradient.GetEndColor());
+ aGradient2.Angle = static_cast<short>(aXGradient.GetAngle());
+ aGradient2.Border = aXGradient.GetBorder();
+ aGradient2.XOffset = aXGradient.GetXOffset();
+ aGradient2.YOffset = aXGradient.GetYOffset();
+ aGradient2.StartIntensity = aXGradient.GetStartIntens();
+ aGradient2.EndIntensity = aXGradient.GetEndIntens();
+ aGradient2.StepCount = aXGradient.GetSteps();
+
+ rVal <<= aGradient2;
+ break;
+ }
+
+ case MID_NAME:
+ {
+ rVal <<= SvxUnogetApiNameForItem(Which(), GetName());
+ break;
+ }
+
+ case MID_GRADIENT_STYLE: rVal <<= static_cast<sal_Int16>(GetGradientValue().GetGradientStyle()); break;
+ case MID_GRADIENT_STARTCOLOR: rVal <<= GetGradientValue().GetStartColor(); break;
+ case MID_GRADIENT_ENDCOLOR: rVal <<= GetGradientValue().GetEndColor(); break;
+ case MID_GRADIENT_ANGLE: rVal <<= static_cast<sal_Int16>(GetGradientValue().GetAngle()); break;
+ case MID_GRADIENT_BORDER: rVal <<= GetGradientValue().GetBorder(); break;
+ case MID_GRADIENT_XOFFSET: rVal <<= GetGradientValue().GetXOffset(); break;
+ case MID_GRADIENT_YOFFSET: rVal <<= GetGradientValue().GetYOffset(); break;
+ case MID_GRADIENT_STARTINTENSITY: rVal <<= GetGradientValue().GetStartIntens(); break;
+ case MID_GRADIENT_ENDINTENSITY: rVal <<= GetGradientValue().GetEndIntens(); break;
+ case MID_GRADIENT_STEPCOUNT: rVal <<= GetGradientValue().GetSteps(); break;
+
+ default: OSL_FAIL("Wrong MemberId!"); return false;
+ }
+
+ return true;
+}
+
+bool XFillGradientItem::PutValue( const css::uno::Any& rVal, sal_uInt8 nMemberId )
+{
+ nMemberId &= ~CONVERT_TWIPS;
+
+ switch ( nMemberId )
+ {
+ case 0:
+ {
+ uno::Sequence< beans::PropertyValue > aPropSeq;
+
+ if ( rVal >>= aPropSeq )
+ {
+ css::awt::Gradient aGradient2;
+ OUString aName;
+ bool bGradient( false );
+ for ( const auto& rProp : std::as_const(aPropSeq) )
+ {
+ if ( rProp.Name == "Name" )
+ rProp.Value >>= aName;
+ else if ( rProp.Name == "FillGradient" )
+ {
+ if ( rProp.Value >>= aGradient2 )
+ bGradient = true;
+ }
+ }
+
+ SetName( aName );
+ if ( bGradient )
+ {
+ XGradient aXGradient;
+
+ aXGradient.SetGradientStyle( aGradient2.Style );
+ aXGradient.SetStartColor( Color(ColorTransparency, aGradient2.StartColor) );
+ aXGradient.SetEndColor( Color(ColorTransparency, aGradient2.EndColor) );
+ aXGradient.SetAngle( Degree10(aGradient2.Angle) );
+ aXGradient.SetBorder( aGradient2.Border );
+ aXGradient.SetXOffset( aGradient2.XOffset );
+ aXGradient.SetYOffset( aGradient2.YOffset );
+ aXGradient.SetStartIntens( aGradient2.StartIntensity );
+ aXGradient.SetEndIntens( aGradient2.EndIntensity );
+ aXGradient.SetSteps( aGradient2.StepCount );
+
+ SetGradientValue( aXGradient );
+ }
+
+ return true;
+ }
+
+ return false;
+ }
+
+ case MID_NAME:
+ {
+ OUString aName;
+ if (!(rVal >>= aName ))
+ return false;
+ SetName( aName );
+ break;
+ }
+
+ case MID_FILLGRADIENT:
+ {
+ css::awt::Gradient aGradient2;
+ if(!(rVal >>= aGradient2))
+ return false;
+
+ XGradient aXGradient;
+
+ aXGradient.SetGradientStyle( aGradient2.Style );
+ aXGradient.SetStartColor( Color(ColorTransparency, aGradient2.StartColor) );
+ aXGradient.SetEndColor( Color(ColorTransparency, aGradient2.EndColor) );
+ aXGradient.SetAngle( Degree10(aGradient2.Angle) );
+ aXGradient.SetBorder( aGradient2.Border );
+ aXGradient.SetXOffset( aGradient2.XOffset );
+ aXGradient.SetYOffset( aGradient2.YOffset );
+ aXGradient.SetStartIntens( aGradient2.StartIntensity );
+ aXGradient.SetEndIntens( aGradient2.EndIntensity );
+ aXGradient.SetSteps( aGradient2.StepCount );
+
+ SetGradientValue( aXGradient );
+ break;
+ }
+
+ case MID_GRADIENT_STARTCOLOR:
+ case MID_GRADIENT_ENDCOLOR:
+ {
+ Color nVal;
+ if(!(rVal >>= nVal ))
+ return false;
+
+ XGradient aXGradient = GetGradientValue();
+
+ if ( nMemberId == MID_GRADIENT_STARTCOLOR )
+ aXGradient.SetStartColor( nVal );
+ else
+ aXGradient.SetEndColor( nVal );
+ SetGradientValue( aXGradient );
+ break;
+ }
+
+ case MID_GRADIENT_STYLE:
+ case MID_GRADIENT_ANGLE:
+ case MID_GRADIENT_BORDER:
+ case MID_GRADIENT_STARTINTENSITY:
+ case MID_GRADIENT_ENDINTENSITY:
+ case MID_GRADIENT_STEPCOUNT:
+ case MID_GRADIENT_XOFFSET:
+ case MID_GRADIENT_YOFFSET:
+ {
+ sal_Int16 nVal = sal_Int16();
+ if(!(rVal >>= nVal ))
+ return false;
+
+ XGradient aXGradient = GetGradientValue();
+
+ switch ( nMemberId )
+ {
+ case MID_GRADIENT_STYLE:
+ aXGradient.SetGradientStyle( static_cast<css::awt::GradientStyle>(nVal) ); break;
+ case MID_GRADIENT_ANGLE:
+ aXGradient.SetAngle( Degree10(nVal) ); break;
+ case MID_GRADIENT_BORDER:
+ aXGradient.SetBorder( nVal ); break;
+ case MID_GRADIENT_STARTINTENSITY:
+ aXGradient.SetStartIntens( nVal ); break;
+ case MID_GRADIENT_ENDINTENSITY:
+ aXGradient.SetEndIntens( nVal ); break;
+ case MID_GRADIENT_STEPCOUNT:
+ aXGradient.SetSteps( nVal ); break;
+ case MID_GRADIENT_XOFFSET:
+ aXGradient.SetXOffset( nVal ); break;
+ case MID_GRADIENT_YOFFSET:
+ aXGradient.SetYOffset( nVal ); break;
+ }
+
+ SetGradientValue( aXGradient );
+ break;
+ }
+ }
+
+ return true;
+}
+
+bool XFillGradientItem::CompareValueFunc( const NameOrIndex* p1, const NameOrIndex* p2 )
+{
+ return static_cast<const XFillGradientItem*>(p1)->GetGradientValue() == static_cast<const XFillGradientItem*>(p2)->GetGradientValue();
+}
+
+std::unique_ptr<XFillGradientItem> XFillGradientItem::checkForUniqueItem( SdrModel* pModel ) const
+{
+ if( pModel )
+ {
+ const OUString aUniqueName = NameOrIndex::CheckNamedItem(
+ this, Which(), &pModel->GetItemPool(),
+ XFillGradientItem::CompareValueFunc, RID_SVXSTR_GRADIENT,
+ pModel->GetPropertyList( XPropertyListType::Gradient ) );
+
+ // if the given name is not valid, replace it!
+ if( aUniqueName != GetName() )
+ return std::make_unique<XFillGradientItem>( aUniqueName, aGradient, TypedWhichId<XFillGradientItem>(Which()) );
+ }
+
+ return nullptr;
+}
+
+boost::property_tree::ptree XFillGradientItem::dumpAsJSON() const
+{
+ boost::property_tree::ptree aTree = SfxPoolItem::dumpAsJSON();
+
+ if (Which() == XATTR_FILLGRADIENT)
+ aTree.put("commandName", ".uno:FillGradient");
+
+ aTree.push_back(std::make_pair("state", GetGradientValue().dumpAsJSON()));
+
+ return aTree;
+}
+
+
+SfxPoolItem* XFillFloatTransparenceItem::CreateDefault() { return new XFillFloatTransparenceItem; }
+
+XFillFloatTransparenceItem::XFillFloatTransparenceItem() :
+ bEnabled( false )
+{
+ SetWhich( XATTR_FILLFLOATTRANSPARENCE );
+}
+
+XFillFloatTransparenceItem::XFillFloatTransparenceItem(const OUString& rName, const XGradient& rGradient, bool bEnable ) :
+ XFillGradientItem ( rName, rGradient ),
+ bEnabled ( bEnable )
+{
+ SetWhich( XATTR_FILLFLOATTRANSPARENCE );
+}
+
+XFillFloatTransparenceItem::XFillFloatTransparenceItem( const XFillFloatTransparenceItem& rItem ) :
+ XFillGradientItem ( rItem ),
+ bEnabled ( rItem.bEnabled )
+{
+ SetWhich( XATTR_FILLFLOATTRANSPARENCE );
+}
+
+XFillFloatTransparenceItem::XFillFloatTransparenceItem(const XGradient& rTheGradient, bool bEnable )
+: XFillGradientItem ( -1, rTheGradient ),
+ bEnabled ( bEnable )
+{
+ SetWhich( XATTR_FILLFLOATTRANSPARENCE );
+}
+
+bool XFillFloatTransparenceItem::operator==( const SfxPoolItem& rItem ) const
+{
+ return ( NameOrIndex::operator==(rItem) ) &&
+ ( GetGradientValue() == static_cast<const XFillGradientItem&>(rItem).GetGradientValue() ) &&
+ ( bEnabled == static_cast<const XFillFloatTransparenceItem&>(rItem).bEnabled );
+}
+
+XFillFloatTransparenceItem* XFillFloatTransparenceItem::Clone( SfxItemPool* /*pPool*/) const
+{
+ return new XFillFloatTransparenceItem( *this );
+}
+
+bool XFillFloatTransparenceItem::QueryValue( css::uno::Any& rVal, sal_uInt8 nMemberId ) const
+{
+ return XFillGradientItem::QueryValue( rVal, nMemberId );
+}
+
+bool XFillFloatTransparenceItem::PutValue( const css::uno::Any& rVal, sal_uInt8 nMemberId )
+{
+ return XFillGradientItem::PutValue( rVal, nMemberId );
+}
+
+bool XFillFloatTransparenceItem::GetPresentation( SfxItemPresentation ePres,
+ MapUnit eCoreUnit, MapUnit ePresUnit,
+ OUString& rText,
+ const IntlWrapper& rIntlWrapper ) const
+{
+ return XFillGradientItem::GetPresentation( ePres, eCoreUnit, ePresUnit, rText, rIntlWrapper );
+}
+
+bool XFillFloatTransparenceItem::CompareValueFunc( const NameOrIndex* p1, const NameOrIndex* p2 )
+{
+ return static_cast<const XFillFloatTransparenceItem*>(p1)->IsEnabled() == static_cast<const XFillFloatTransparenceItem*>(p2)->IsEnabled() &&
+ static_cast<const XFillFloatTransparenceItem*>(p1)->GetGradientValue() == static_cast<const XFillFloatTransparenceItem*>(p2)->GetGradientValue();
+}
+
+std::unique_ptr<XFillFloatTransparenceItem> XFillFloatTransparenceItem::checkForUniqueItem( SdrModel* pModel ) const
+{
+ // #85953# unique name only necessary when enabled
+ if(IsEnabled())
+ {
+ if( pModel )
+ {
+ const OUString aUniqueName = NameOrIndex::CheckNamedItem( this,
+ XATTR_FILLFLOATTRANSPARENCE,
+ &pModel->GetItemPool(),
+ XFillFloatTransparenceItem::CompareValueFunc,
+ RID_SVXSTR_TRASNGR0,
+ XPropertyListRef() );
+
+ // if the given name is not valid, replace it!
+ if( aUniqueName != GetName() )
+ {
+ return std::make_unique<XFillFloatTransparenceItem>( aUniqueName, GetGradientValue(), true );
+ }
+ }
+ }
+ else
+ {
+ // #85953# if disabled, force name to empty string
+ if( !GetName().isEmpty() )
+ {
+ return std::make_unique<XFillFloatTransparenceItem>(OUString(), GetGradientValue(), false);
+ }
+ }
+
+ return nullptr;
+}
+
+boost::property_tree::ptree XFillFloatTransparenceItem::dumpAsJSON() const
+{
+ boost::property_tree::ptree aTree = XFillGradientItem::dumpAsJSON();
+ aTree.put("commandName", ".uno:FillFloatTransparence");
+
+ if (!bEnabled)
+ {
+ boost::property_tree::ptree& rState = aTree.get_child("state");
+ // When gradient fill is disabled, the intensity fields contain the
+ // constant encoded percent-transparency. However we use that here to just
+ // distinguish between 'None' and 'Solid' types and correct the 'style'
+ // property appropriately.
+ if (GetGradientValue().GetStartIntens() == 100)
+ rState.put("style", "NONE");
+ else
+ rState.put("style", "SOLID");
+ }
+
+ return aTree;
+}
+
+XHatch::XHatch(const Color& rCol, css::drawing::HatchStyle eTheStyle, tools::Long nTheDistance,
+ Degree10 nTheAngle) :
+ eStyle(eTheStyle),
+ aColor(rCol),
+ nDistance(nTheDistance),
+ nAngle(nTheAngle)
+{
+}
+
+bool XHatch::operator==(const XHatch& rHatch) const
+{
+ return ( eStyle == rHatch.eStyle &&
+ aColor == rHatch.aColor &&
+ nDistance == rHatch.nDistance &&
+ nAngle == rHatch.nAngle );
+}
+
+
+SfxPoolItem* XFillHatchItem::CreateDefault() { return new XFillHatchItem; }
+
+XFillHatchItem::XFillHatchItem(const OUString& rName,
+ const XHatch& rTheHatch) :
+ NameOrIndex(XATTR_FILLHATCH, rName),
+ aHatch(rTheHatch)
+{
+}
+
+XFillHatchItem::XFillHatchItem(const XFillHatchItem& rItem) :
+ NameOrIndex(rItem),
+ aHatch(rItem.aHatch)
+{
+}
+
+XFillHatchItem::XFillHatchItem(const XHatch& rTheHatch)
+: NameOrIndex( XATTR_FILLHATCH, -1 ),
+ aHatch(rTheHatch)
+{
+}
+
+XFillHatchItem* XFillHatchItem::Clone(SfxItemPool* /*pPool*/) const
+{
+ return new XFillHatchItem(*this);
+}
+
+bool XFillHatchItem::operator==(const SfxPoolItem& rItem) const
+{
+ return ( NameOrIndex::operator==(rItem) &&
+ aHatch == static_cast<const XFillHatchItem&>(rItem).aHatch );
+}
+
+bool XFillHatchItem::GetPresentation
+(
+ SfxItemPresentation /*ePres*/,
+ MapUnit /*eCoreUnit*/,
+ MapUnit /*ePresUnit*/,
+ OUString& rText, const IntlWrapper&
+) const
+{
+ rText = GetName();
+ return true;
+}
+
+bool XFillHatchItem::HasMetrics() const
+{
+ return true;
+}
+
+void XFillHatchItem::ScaleMetrics(tools::Long nMul, tools::Long nDiv)
+{
+ aHatch.SetDistance( BigInt::Scale( aHatch.GetDistance(), nMul, nDiv ) );
+}
+
+bool XFillHatchItem::QueryValue( css::uno::Any& rVal, sal_uInt8 nMemberId ) const
+{
+ nMemberId &= ~CONVERT_TWIPS;
+
+ switch ( nMemberId )
+ {
+ case 0:
+ {
+ css::drawing::Hatch aUnoHatch;
+
+ aUnoHatch.Style = aHatch.GetHatchStyle();
+ aUnoHatch.Color = sal_Int32(aHatch.GetColor());
+ aUnoHatch.Distance = aHatch.GetDistance();
+ aUnoHatch.Angle = aHatch.GetAngle().get();
+
+ uno::Sequence< beans::PropertyValue > aPropSeq{
+ comphelper::makePropertyValue("Name", SvxUnogetApiNameForItem(Which(), GetName())),
+ comphelper::makePropertyValue("FillHatch", aUnoHatch)
+ };
+ rVal <<= aPropSeq;
+ break;
+ }
+
+ case MID_FILLHATCH:
+ {
+ css::drawing::Hatch aUnoHatch;
+
+ aUnoHatch.Style = aHatch.GetHatchStyle();
+ aUnoHatch.Color = sal_Int32(aHatch.GetColor());
+ aUnoHatch.Distance = aHatch.GetDistance();
+ aUnoHatch.Angle = aHatch.GetAngle().get();
+ rVal <<= aUnoHatch;
+ break;
+ }
+
+ case MID_NAME:
+ {
+ rVal <<= SvxUnogetApiNameForItem(Which(), GetName());
+ break;
+ }
+
+ case MID_HATCH_STYLE:
+ rVal <<= aHatch.GetHatchStyle(); break;
+ case MID_HATCH_COLOR:
+ rVal <<= aHatch.GetColor(); break;
+ case MID_HATCH_DISTANCE:
+ rVal <<= aHatch.GetDistance(); break;
+ case MID_HATCH_ANGLE:
+ rVal <<= aHatch.GetAngle().get(); break;
+
+ default: OSL_FAIL("Wrong MemberId!"); return false;
+ }
+
+ return true;
+}
+
+bool XFillHatchItem::PutValue( const css::uno::Any& rVal, sal_uInt8 nMemberId )
+{
+ nMemberId &= ~CONVERT_TWIPS;
+
+ switch ( nMemberId )
+ {
+ case 0:
+ {
+ uno::Sequence< beans::PropertyValue > aPropSeq;
+ if ( rVal >>= aPropSeq )
+ {
+ css::drawing::Hatch aUnoHatch;
+ OUString aName;
+ bool bHatch( false );
+ for ( const auto& rProp : std::as_const(aPropSeq) )
+ {
+ if ( rProp.Name == "Name" )
+ rProp.Value >>= aName;
+ else if ( rProp.Name == "FillHatch" )
+ {
+ if ( rProp.Value >>= aUnoHatch )
+ bHatch = true;
+ }
+ }
+
+ SetName( aName );
+ if ( bHatch )
+ {
+ aHatch.SetHatchStyle( aUnoHatch.Style );
+ aHatch.SetColor( Color(ColorTransparency, aUnoHatch.Color) );
+ aHatch.SetDistance( aUnoHatch.Distance );
+ aHatch.SetAngle( Degree10(aUnoHatch.Angle) );
+ }
+
+ return true;
+ }
+
+ return false;
+ }
+
+ case MID_FILLHATCH:
+ {
+ css::drawing::Hatch aUnoHatch;
+ if(!(rVal >>= aUnoHatch))
+ return false;
+
+ aHatch.SetHatchStyle( aUnoHatch.Style );
+ aHatch.SetColor( Color(ColorTransparency, aUnoHatch.Color) );
+ aHatch.SetDistance( aUnoHatch.Distance );
+ aHatch.SetAngle( Degree10(aUnoHatch.Angle) );
+ break;
+ }
+
+ case MID_NAME:
+ {
+ OUString aName;
+ if (!(rVal >>= aName ))
+ return false;
+ SetName( aName );
+ break;
+ }
+
+ case MID_HATCH_STYLE:
+ {
+ sal_Int16 nVal = sal_Int16();
+ if (!(rVal >>= nVal ))
+ return false;
+ aHatch.SetHatchStyle( static_cast<css::drawing::HatchStyle>(nVal) );
+ break;
+ }
+
+ case MID_HATCH_COLOR:
+ case MID_HATCH_DISTANCE:
+ case MID_HATCH_ANGLE:
+ {
+ sal_Int32 nVal = 0;
+ if (!(rVal >>= nVal ))
+ return false;
+
+ if ( nMemberId == MID_HATCH_COLOR )
+ aHatch.SetColor( Color(ColorTransparency, nVal) );
+ else if ( nMemberId == MID_HATCH_DISTANCE )
+ aHatch.SetDistance( nVal );
+ else
+ aHatch.SetAngle( Degree10(nVal) );
+ break;
+ }
+
+ default: OSL_FAIL("Wrong MemberId!"); return false;
+ }
+
+ return true;
+}
+
+bool XFillHatchItem::CompareValueFunc( const NameOrIndex* p1, const NameOrIndex* p2 )
+{
+ return static_cast<const XFillHatchItem*>(p1)->GetHatchValue() == static_cast<const XFillHatchItem*>(p2)->GetHatchValue();
+}
+
+std::unique_ptr<XFillHatchItem> XFillHatchItem::checkForUniqueItem( SdrModel* pModel ) const
+{
+ if( pModel )
+ {
+ const OUString aUniqueName = NameOrIndex::CheckNamedItem(
+ this, XATTR_FILLHATCH, &pModel->GetItemPool(),
+ XFillHatchItem::CompareValueFunc, RID_SVXSTR_HATCH10,
+ pModel->GetPropertyList( XPropertyListType::Hatch ) );
+
+ // if the given name is not valid, replace it!
+ if( aUniqueName != GetName() )
+ return std::make_unique<XFillHatchItem>( aUniqueName, aHatch );
+ }
+
+ return nullptr;
+}
+
+// --- form text attributes ---
+
+
+SfxPoolItem* XFormTextStyleItem::CreateDefault() { return new XFormTextStyleItem; }
+
+XFormTextStyleItem::XFormTextStyleItem(XFormTextStyle eTheStyle) :
+ SfxEnumItem(XATTR_FORMTXTSTYLE, eTheStyle)
+{
+}
+
+XFormTextStyleItem* XFormTextStyleItem::Clone(SfxItemPool* /*pPool*/) const
+{
+ return new XFormTextStyleItem( *this );
+}
+
+sal_uInt16 XFormTextStyleItem::GetValueCount() const
+{
+ return 5;
+}
+
+bool XFormTextStyleItem::QueryValue( uno::Any& rVal, sal_uInt8 /*nMemberId*/) const
+{
+ rVal <<= static_cast<sal_Int32>(GetValue());
+ return true;
+}
+
+bool XFormTextStyleItem::PutValue( const uno::Any& rVal, sal_uInt8 /*nMemberId*/)
+{
+ sal_Int32 nValue = 0;
+ rVal >>= nValue;
+ SetValue(static_cast<XFormTextStyle>(nValue));
+
+ return true;
+}
+
+
+SfxPoolItem* XFormTextAdjustItem::CreateDefault() { return new XFormTextAdjustItem; }
+
+XFormTextAdjustItem::XFormTextAdjustItem(XFormTextAdjust eTheAdjust) :
+ SfxEnumItem(XATTR_FORMTXTADJUST, eTheAdjust)
+{
+}
+
+XFormTextAdjustItem* XFormTextAdjustItem::Clone(SfxItemPool* /*pPool*/) const
+{
+ return new XFormTextAdjustItem( *this );
+}
+
+sal_uInt16 XFormTextAdjustItem::GetValueCount() const
+{
+ return 4;
+}
+
+bool XFormTextAdjustItem::QueryValue( uno::Any& rVal, sal_uInt8 /*nMemberId*/) const
+{
+ rVal <<= static_cast<sal_Int32>(GetValue());
+ return true;
+}
+
+bool XFormTextAdjustItem::PutValue( const uno::Any& rVal, sal_uInt8 /*nMemberId*/)
+{
+ sal_Int32 nValue = 0;
+ rVal >>= nValue;
+ SetValue(static_cast<XFormTextAdjust>(nValue));
+
+ return true;
+}
+
+
+SfxPoolItem* XFormTextDistanceItem::CreateDefault() { return new XFormTextDistanceItem; }
+
+XFormTextDistanceItem::XFormTextDistanceItem(tools::Long nDist) :
+ SfxMetricItem(XATTR_FORMTXTDISTANCE, nDist)
+{
+}
+
+XFormTextDistanceItem* XFormTextDistanceItem::Clone(SfxItemPool* /*pPool*/) const
+{
+ return new XFormTextDistanceItem(*this);
+}
+
+SfxPoolItem* XFormTextStartItem::CreateDefault() { return new XFormTextStartItem; }
+
+XFormTextStartItem::XFormTextStartItem(tools::Long nStart) :
+ SfxMetricItem(XATTR_FORMTXTSTART, nStart)
+{
+}
+
+XFormTextStartItem* XFormTextStartItem::Clone(SfxItemPool* /*pPool*/) const
+{
+ return new XFormTextStartItem(*this);
+}
+
+SfxPoolItem* XFormTextMirrorItem::CreateDefault() { return new XFormTextMirrorItem; }
+
+XFormTextMirrorItem::XFormTextMirrorItem(bool bMirror) :
+ SfxBoolItem(XATTR_FORMTXTMIRROR, bMirror)
+{
+}
+
+XFormTextMirrorItem* XFormTextMirrorItem::Clone(SfxItemPool* /*pPool*/) const
+{
+ return new XFormTextMirrorItem(*this);
+}
+
+SfxPoolItem* XFormTextOutlineItem::CreateDefault() { return new XFormTextOutlineItem; }
+
+XFormTextOutlineItem::XFormTextOutlineItem(bool bOutline) :
+ SfxBoolItem(XATTR_FORMTXTOUTLINE, bOutline)
+{
+}
+
+XFormTextOutlineItem* XFormTextOutlineItem::Clone(SfxItemPool* /*pPool*/) const
+{
+ return new XFormTextOutlineItem(*this);
+}
+
+SfxPoolItem* XFormTextShadowItem::CreateDefault() { return new XFormTextShadowItem; }
+
+XFormTextShadowItem::XFormTextShadowItem(XFormTextShadow eFormTextShadow) :
+ SfxEnumItem(XATTR_FORMTXTSHADOW, eFormTextShadow)
+{
+}
+
+XFormTextShadowItem* XFormTextShadowItem::Clone(SfxItemPool* /*pPool*/) const
+{
+ return new XFormTextShadowItem( *this );
+}
+
+sal_uInt16 XFormTextShadowItem::GetValueCount() const
+{
+ return 3;
+}
+
+bool XFormTextShadowItem::QueryValue( uno::Any& rVal, sal_uInt8 /*nMemberId*/) const
+{
+ rVal <<= static_cast<sal_Int32>(GetValue());
+ return true;
+}
+
+bool XFormTextShadowItem::PutValue( const uno::Any& rVal, sal_uInt8 /*nMemberId*/)
+{
+ sal_Int32 nValue = 0;
+ rVal >>= nValue;
+ SetValue(static_cast<XFormTextShadow>(nValue));
+
+ return true;
+}
+
+
+SfxPoolItem* XFormTextShadowColorItem::CreateDefault() { return new XFormTextShadowColorItem; }
+
+XFormTextShadowColorItem::XFormTextShadowColorItem(const OUString& rName,
+ const Color& rTheColor) :
+ XColorItem(XATTR_FORMTXTSHDWCOLOR, rName, rTheColor)
+{
+}
+
+XFormTextShadowColorItem* XFormTextShadowColorItem::Clone(SfxItemPool* /*pPool*/) const
+{
+ return new XFormTextShadowColorItem(*this);
+}
+
+SfxPoolItem* XFormTextShadowXValItem::CreateDefault() { return new XFormTextShadowXValItem; }
+
+XFormTextShadowXValItem::XFormTextShadowXValItem(tools::Long nVal) :
+ SfxMetricItem(XATTR_FORMTXTSHDWXVAL, nVal)
+{
+}
+
+XFormTextShadowXValItem* XFormTextShadowXValItem::Clone(SfxItemPool* /*pPool*/) const
+{
+ return new XFormTextShadowXValItem(*this);
+}
+
+SfxPoolItem* XFormTextShadowYValItem::CreateDefault() { return new XFormTextShadowYValItem; }
+
+XFormTextShadowYValItem::XFormTextShadowYValItem(tools::Long nVal) :
+ SfxMetricItem(XATTR_FORMTXTSHDWYVAL, nVal)
+{
+}
+
+XFormTextShadowYValItem* XFormTextShadowYValItem::Clone(SfxItemPool* /*pPool*/) const
+{
+ return new XFormTextShadowYValItem(*this);
+}
+
+SfxPoolItem* XFormTextHideFormItem::CreateDefault() { return new XFormTextHideFormItem; }
+
+XFormTextHideFormItem::XFormTextHideFormItem(bool bHide) :
+ SfxBoolItem(XATTR_FORMTXTHIDEFORM, bHide)
+{
+}
+
+XFormTextHideFormItem* XFormTextHideFormItem::Clone(SfxItemPool* /*pPool*/) const
+{
+ return new XFormTextHideFormItem(*this);
+}
+
+// --- SetItems ---
+
+
+/// a line attribute set item
+XLineAttrSetItem::XLineAttrSetItem( SfxItemSet&& pItemSet ) :
+ SfxSetItem( XATTRSET_LINE, std::move(pItemSet))
+{
+}
+
+XLineAttrSetItem::XLineAttrSetItem( SfxItemPool* pItemPool ) :
+ SfxSetItem( XATTRSET_LINE,
+ SfxItemSetFixed<XATTR_LINE_FIRST, XATTR_LINE_LAST>( *pItemPool ))
+{
+}
+
+XLineAttrSetItem::XLineAttrSetItem( const XLineAttrSetItem& rLineAttr ) :
+ SfxSetItem( rLineAttr )
+{
+}
+
+XLineAttrSetItem::XLineAttrSetItem( const XLineAttrSetItem& rLineAttr,
+ SfxItemPool* pItemPool) :
+ SfxSetItem( rLineAttr, pItemPool )
+{
+}
+
+XLineAttrSetItem* XLineAttrSetItem::Clone( SfxItemPool* pPool ) const
+{
+ return new XLineAttrSetItem( *this, pPool );
+}
+
+/// fill attribute set item
+XFillAttrSetItem::XFillAttrSetItem( SfxItemSet&& pItemSet ) :
+ SfxSetItem( XATTRSET_FILL, std::move(pItemSet))
+{
+}
+
+XFillAttrSetItem::XFillAttrSetItem( SfxItemPool* pItemPool ) :
+ SfxSetItem( XATTRSET_FILL,
+ SfxItemSetFixed<XATTR_FILL_FIRST, XATTR_FILL_LAST>( *pItemPool ))
+{
+}
+
+XFillAttrSetItem::XFillAttrSetItem( const XFillAttrSetItem& rFillAttr ) :
+ SfxSetItem( rFillAttr )
+{
+}
+
+XFillAttrSetItem::XFillAttrSetItem( const XFillAttrSetItem& rFillAttr,
+ SfxItemPool* pItemPool ) :
+ SfxSetItem( rFillAttr, pItemPool )
+{
+}
+
+XFillAttrSetItem* XFillAttrSetItem::Clone( SfxItemPool* pPool ) const
+{
+ return new XFillAttrSetItem( *this, pPool );
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/xoutdev/xattr2.cxx b/svx/source/xoutdev/xattr2.cxx
new file mode 100644
index 000000000..ad1b3b295
--- /dev/null
+++ b/svx/source/xoutdev/xattr2.cxx
@@ -0,0 +1,731 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * 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/LineJoint.hpp>
+#include <com/sun/star/drawing/LineCap.hpp>
+#include <com/sun/star/uno/Any.hxx>
+
+#include <osl/diagnose.h>
+#include <i18nutil/unicode.hxx>
+#include <svx/strings.hrc>
+#include <svx/svxids.hrc>
+#include <svx/xlinjoit.hxx>
+#include <svx/xlncapit.hxx>
+#include <svx/xlntrit.hxx>
+#include <svx/xfltrit.hxx>
+#include <xftshtit.hxx>
+#include <svx/xgrscit.hxx>
+#include <svx/xflbmtit.hxx>
+#include <svx/xflbmpit.hxx>
+#include <svx/xflbmsxy.hxx>
+#include <svx/xflbmsli.hxx>
+#include <svx/xflbtoxy.hxx>
+#include <svx/xflbstit.hxx>
+#include <svx/xflboxy.hxx>
+#include <svx/xflbckit.hxx>
+#include <svx/xfilluseslidebackgrounditem.hxx>
+#include <svx/dialmgr.hxx>
+#include <svx/xdef.hxx>
+#include <AffineMatrixItem.hxx>
+#include <vcl/svapp.hxx>
+#include <vcl/settings.hxx>
+
+#include <comphelper/lok.hxx>
+
+#include <libxml/xmlwriter.h>
+
+XLineTransparenceItem::XLineTransparenceItem(sal_uInt16 nLineTransparence) :
+ SfxUInt16Item(XATTR_LINETRANSPARENCE, nLineTransparence)
+{
+}
+
+XLineTransparenceItem* XLineTransparenceItem::Clone(SfxItemPool* /*pPool*/) const
+{
+ return new XLineTransparenceItem(*this);
+}
+
+bool XLineTransparenceItem::GetPresentation
+(
+ SfxItemPresentation ePres,
+ MapUnit /*eCoreUnit*/,
+ MapUnit /*ePresUnit*/,
+ OUString& rText, const IntlWrapper&
+) const
+{
+ rText.clear();
+
+ switch ( ePres )
+ {
+ case SfxItemPresentation::Complete:
+ rText = SvxResId(RID_SVXSTR_TRANSPARENCE) + ": ";
+ [[fallthrough]];
+ case SfxItemPresentation::Nameless:
+ rText += unicode::formatPercent(GetValue(),
+ Application::GetSettings().GetUILanguageTag());
+ return true;
+ default:
+ return false;
+ }
+}
+
+
+SfxPoolItem* XLineJointItem::CreateDefault() { return new XLineJointItem; }
+
+XLineJointItem::XLineJointItem( css::drawing::LineJoint eLineJoint ) :
+ SfxEnumItem(XATTR_LINEJOINT, eLineJoint)
+{
+}
+
+XLineJointItem* XLineJointItem::Clone(SfxItemPool* /*pPool*/) const
+{
+ return new XLineJointItem( *this );
+}
+
+bool XLineJointItem::GetPresentation( SfxItemPresentation /*ePres*/, MapUnit /*eCoreUnit*/,
+ MapUnit /*ePresUnit*/, OUString& rText, const IntlWrapper&) const
+{
+ rText.clear();
+
+ TranslateId pId;
+
+ switch( GetValue() )
+ {
+ case css::drawing::LineJoint::LineJoint_MAKE_FIXED_SIZE:
+ case css::drawing::LineJoint_NONE:
+ pId = comphelper::LibreOfficeKit::isActive() ? RID_SVXSTR_INVISIBLE : RID_SVXSTR_NONE;
+ break;
+
+ case css::drawing::LineJoint_MIDDLE:
+ pId = RID_SVXSTR_LINEJOINT_MIDDLE;
+ break;
+
+
+ case css::drawing::LineJoint_BEVEL:
+ pId = RID_SVXSTR_LINEJOINT_BEVEL;
+ break;
+
+
+ case css::drawing::LineJoint_MITER:
+ pId = RID_SVXSTR_LINEJOINT_MITER;
+ break;
+
+
+ case css::drawing::LineJoint_ROUND:
+ pId = RID_SVXSTR_LINEJOINT_ROUND;
+ break;
+ }
+
+ if (pId)
+ rText = SvxResId(pId);
+
+ return true;
+}
+
+bool XLineJointItem::QueryValue( css::uno::Any& rVal, sal_uInt8 /*nMemberId*/) const
+{
+ const css::drawing::LineJoint eJoint = GetValue();
+ rVal <<= eJoint;
+ return true;
+}
+
+bool XLineJointItem::PutValue( const css::uno::Any& rVal, sal_uInt8 /*nMemberId*/)
+{
+ css::drawing::LineJoint eUnoJoint;
+
+ if(!(rVal >>= eUnoJoint))
+ {
+ // also try an int (for Basic)
+ sal_Int32 nLJ = 0;
+ if(!(rVal >>= nLJ))
+ return false;
+ eUnoJoint = static_cast<css::drawing::LineJoint>(nLJ);
+ }
+
+ SetValue( eUnoJoint );
+
+ return true;
+}
+
+sal_uInt16 XLineJointItem::GetValueCount() const
+{
+ // don't forget to update the api interface also
+ return 5;
+}
+
+
+AffineMatrixItem::AffineMatrixItem(const css::geometry::AffineMatrix2D* pMatrix)
+: SfxPoolItem(SID_ATTR_TRANSFORM_MATRIX)
+{
+ if(pMatrix)
+ {
+ maMatrix = *pMatrix;
+ }
+ else
+ {
+ maMatrix.m00 = 1.0;
+ maMatrix.m01 = 0.0;
+ maMatrix.m02 = 0.0;
+ maMatrix.m10 = 0.0;
+ maMatrix.m11 = 1.0;
+ maMatrix.m12 = 0.0;
+ }
+}
+
+AffineMatrixItem::AffineMatrixItem(const AffineMatrixItem& rRef)
+: SfxPoolItem(rRef)
+{
+ maMatrix = rRef.maMatrix;
+}
+
+AffineMatrixItem::~AffineMatrixItem()
+{
+}
+
+bool AffineMatrixItem::operator==(const SfxPoolItem& rRef) const
+{
+ if(!SfxPoolItem::operator==(rRef))
+ {
+ return false;
+ }
+
+ const AffineMatrixItem* pRef = static_cast< const AffineMatrixItem* >(&rRef);
+
+ if(!pRef)
+ {
+ return false;
+ }
+
+ return (maMatrix.m00 == pRef->maMatrix.m00
+ && maMatrix.m01 == pRef->maMatrix.m01
+ && maMatrix.m02 == pRef->maMatrix.m02
+ && maMatrix.m10 == pRef->maMatrix.m10
+ && maMatrix.m11 == pRef->maMatrix.m11
+ && maMatrix.m12 == pRef->maMatrix.m12);
+}
+
+AffineMatrixItem* AffineMatrixItem::Clone( SfxItemPool* /*pPool*/ ) const
+{
+ return new AffineMatrixItem(*this);
+}
+
+bool AffineMatrixItem::QueryValue( css::uno::Any& rVal, sal_uInt8 /*nMemberId*/ ) const
+{
+ rVal <<= maMatrix;
+ return true;
+}
+
+bool AffineMatrixItem::PutValue( const css::uno::Any& rVal, sal_uInt8 /*nMemberId*/ )
+{
+ if (rVal >>= maMatrix)
+ {
+ return true;
+ }
+
+ OSL_ENSURE(false, "AffineMatrixItem::PutValue - Wrong type!");
+ return false;
+}
+
+
+SfxPoolItem* XLineCapItem::CreateDefault() { return new XLineCapItem; }
+
+XLineCapItem::XLineCapItem(css::drawing::LineCap eLineCap)
+: SfxEnumItem(XATTR_LINECAP, eLineCap)
+{
+}
+
+XLineCapItem* XLineCapItem::Clone(SfxItemPool* /*pPool*/) const
+{
+ return new XLineCapItem( *this );
+}
+
+bool XLineCapItem::GetPresentation( SfxItemPresentation /*ePres*/, MapUnit /*eCoreUnit*/,
+ MapUnit /*ePresUnit*/, OUString& rText, const IntlWrapper&) const
+{
+ TranslateId pId;
+
+ switch( GetValue() )
+ {
+ default: /*css::drawing::LineCap_BUTT*/
+ pId = RID_SVXSTR_LINECAP_BUTT;
+ break;
+
+ case css::drawing::LineCap_ROUND:
+ pId = RID_SVXSTR_LINECAP_ROUND;
+ break;
+
+ case css::drawing::LineCap_SQUARE:
+ pId = RID_SVXSTR_LINECAP_SQUARE;
+ break;
+ }
+
+ rText = SvxResId(pId);
+
+ return true;
+}
+
+bool XLineCapItem::QueryValue( css::uno::Any& rVal, sal_uInt8 /*nMemberId*/) const
+{
+ const css::drawing::LineCap eCap(GetValue());
+ rVal <<= eCap;
+ return true;
+}
+
+bool XLineCapItem::PutValue( const css::uno::Any& rVal, sal_uInt8 /*nMemberId*/)
+{
+ css::drawing::LineCap eUnoCap;
+
+ if(!(rVal >>= eUnoCap))
+ {
+ // also try an int (for Basic)
+ sal_Int32 nLJ(0);
+
+ if(!(rVal >>= nLJ))
+ {
+ return false;
+ }
+
+ eUnoCap = static_cast<css::drawing::LineCap>(nLJ);
+ }
+
+ OSL_ENSURE(css::drawing::LineCap_BUTT == eUnoCap
+ || css::drawing::LineCap_ROUND == eUnoCap
+ || css::drawing::LineCap_SQUARE == eUnoCap, "Unknown enum value in XATTR_LINECAP (!)");
+
+ SetValue(eUnoCap);
+
+ return true;
+}
+
+sal_uInt16 XLineCapItem::GetValueCount() const
+{
+ // don't forget to update the api interface also
+ return 3;
+}
+
+css::drawing::LineCap XLineCapItem::GetValue() const
+{
+ const css::drawing::LineCap eRetval(SfxEnumItem::GetValue());
+ OSL_ENSURE(css::drawing::LineCap_BUTT == eRetval
+ || css::drawing::LineCap_ROUND == eRetval
+ || css::drawing::LineCap_SQUARE == eRetval, "Unknown enum value in XATTR_LINECAP (!)");
+
+ return eRetval;
+}
+
+XFillTransparenceItem::XFillTransparenceItem(sal_uInt16 nFillTransparence) :
+ SfxUInt16Item(XATTR_FILLTRANSPARENCE, nFillTransparence)
+{
+}
+
+XFillTransparenceItem* XFillTransparenceItem::Clone(SfxItemPool* /*pPool*/) const
+{
+ return new XFillTransparenceItem(*this);
+}
+
+bool XFillTransparenceItem::GetPresentation
+(
+ SfxItemPresentation ePres,
+ MapUnit /*eCoreUnit*/,
+ MapUnit /*ePresUnit*/,
+ OUString& rText, const IntlWrapper&
+) const
+{
+ rText.clear();
+
+ switch ( ePres )
+ {
+ case SfxItemPresentation::Complete:
+ rText = SvxResId(RID_SVXSTR_TRANSPARENCE) + ": ";
+ [[fallthrough]];
+ case SfxItemPresentation::Nameless:
+ rText += unicode::formatPercent(GetValue(),
+ Application::GetSettings().GetUILanguageTag());
+ return true;
+ default:
+ return false;
+ }
+}
+
+void XFillTransparenceItem::dumpAsXml(xmlTextWriterPtr pWriter) const
+{
+ (void)xmlTextWriterStartElement(pWriter, BAD_CAST("XFillTransparenceItem"));
+ (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("whichId"), BAD_CAST(OString::number(Which()).getStr()));
+ (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("value"), BAD_CAST(OString::number(GetValue()).getStr()));
+ (void)xmlTextWriterEndElement(pWriter);
+}
+
+
+XFormTextShadowTranspItem::XFormTextShadowTranspItem(sal_uInt16 nShdwTransparence) :
+ SfxUInt16Item(XATTR_FORMTXTSHDWTRANSP, nShdwTransparence)
+{
+}
+
+XFormTextShadowTranspItem* XFormTextShadowTranspItem::Clone(SfxItemPool* /*pPool*/) const
+{
+ return new XFormTextShadowTranspItem(*this);
+}
+
+
+XGradientStepCountItem::XGradientStepCountItem( sal_uInt16 nStepCount ) :
+ SfxUInt16Item( XATTR_GRADIENTSTEPCOUNT, nStepCount )
+{
+}
+
+XGradientStepCountItem* XGradientStepCountItem::Clone( SfxItemPool* /*pPool*/) const
+{
+ return new XGradientStepCountItem( *this );
+}
+
+bool XGradientStepCountItem::GetPresentation
+(
+ SfxItemPresentation /*ePres*/,
+ MapUnit /*eCoreUnit*/,
+ MapUnit /*ePresUnit*/,
+ OUString& rText, const IntlWrapper&
+) const
+{
+ rText.clear();
+
+ rText += OUString::number(GetValue());
+ return true;
+}
+
+
+XFillBmpTileItem::XFillBmpTileItem( bool bTile ) :
+ SfxBoolItem( XATTR_FILLBMP_TILE, bTile )
+{
+}
+
+XFillBmpTileItem* XFillBmpTileItem::Clone( SfxItemPool* /*pPool*/) const
+{
+ return new XFillBmpTileItem( *this );
+}
+
+bool XFillBmpTileItem::GetPresentation
+(
+ SfxItemPresentation /*ePres*/,
+ MapUnit /*eCoreUnit*/,
+ MapUnit /*ePresUnit*/,
+ OUString& rText, const IntlWrapper&
+) const
+{
+ rText.clear();
+ return true;
+}
+
+void XFillBmpTileItem::dumpAsXml(xmlTextWriterPtr pWriter) const
+{
+ (void)xmlTextWriterStartElement(pWriter, BAD_CAST("XFillBmpTileItem"));
+ (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("whichId"), BAD_CAST(OString::number(Which()).getStr()));
+ (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("value"), BAD_CAST(OString::boolean(GetValue()).getStr()));
+ (void)xmlTextWriterEndElement(pWriter);
+}
+
+
+
+XFillBmpPosItem::XFillBmpPosItem( RectPoint eRP ) :
+ SfxEnumItem( XATTR_FILLBMP_POS, eRP )
+{
+}
+
+XFillBmpPosItem* XFillBmpPosItem::Clone( SfxItemPool* /*pPool*/) const
+{
+ return new XFillBmpPosItem( *this );
+}
+
+bool XFillBmpPosItem::GetPresentation
+(
+ SfxItemPresentation /*ePres*/,
+ MapUnit /*eCoreUnit*/,
+ MapUnit /*ePresUnit*/,
+ OUString& rText, const IntlWrapper&
+) const
+{
+ rText.clear();
+ return true;
+}
+
+sal_uInt16 XFillBmpPosItem::GetValueCount() const
+{
+ return 9;
+}
+
+void XFillBmpPosItem::dumpAsXml(xmlTextWriterPtr pWriter) const
+{
+ (void)xmlTextWriterStartElement(pWriter, BAD_CAST("XFillBmpPosItem"));
+ (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("whichId"), BAD_CAST(OString::number(Which()).getStr()));
+ (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("value"), BAD_CAST(OString::number(static_cast<int>(GetValue())).getStr()));
+ (void)xmlTextWriterEndElement(pWriter);
+}
+
+
+XFillBmpSizeXItem::XFillBmpSizeXItem( tools::Long nSizeX ) :
+ SfxMetricItem( XATTR_FILLBMP_SIZEX, nSizeX )
+{
+}
+
+XFillBmpSizeXItem* XFillBmpSizeXItem::Clone( SfxItemPool* /*pPool*/) const
+{
+ return new XFillBmpSizeXItem( *this );
+}
+
+bool XFillBmpSizeXItem::GetPresentation
+(
+ SfxItemPresentation /*ePres*/,
+ MapUnit /*eCoreUnit*/,
+ MapUnit /*ePresUnit*/,
+ OUString& rText, const IntlWrapper&
+) const
+{
+ rText.clear();
+ return true;
+}
+
+bool XFillBmpSizeXItem::HasMetrics() const
+{
+ return GetValue() > 0;
+}
+
+
+
+XFillBmpSizeYItem::XFillBmpSizeYItem( tools::Long nSizeY ) :
+ SfxMetricItem( XATTR_FILLBMP_SIZEY, nSizeY )
+{
+}
+
+XFillBmpSizeYItem* XFillBmpSizeYItem::Clone( SfxItemPool* /*pPool*/) const
+{
+ return new XFillBmpSizeYItem( *this );
+}
+
+bool XFillBmpSizeYItem::GetPresentation
+(
+ SfxItemPresentation /*ePres*/,
+ MapUnit /*eCoreUnit*/,
+ MapUnit /*ePresUnit*/,
+ OUString& rText, const IntlWrapper&
+) const
+{
+ rText.clear();
+ return true;
+}
+
+bool XFillBmpSizeYItem::HasMetrics() const
+{
+ return GetValue() > 0;
+}
+
+
+XFillBmpSizeLogItem::XFillBmpSizeLogItem( bool bLog ) :
+ SfxBoolItem( XATTR_FILLBMP_SIZELOG, bLog )
+{
+}
+
+XFillBmpSizeLogItem* XFillBmpSizeLogItem::Clone( SfxItemPool* /*pPool*/) const
+{
+ return new XFillBmpSizeLogItem( *this );
+}
+
+bool XFillBmpSizeLogItem::GetPresentation
+(
+ SfxItemPresentation /*ePres*/,
+ MapUnit /*eCoreUnit*/,
+ MapUnit /*ePresUnit*/,
+ OUString& rText, const IntlWrapper&
+) const
+{
+ rText.clear();
+ return true;
+}
+
+
+
+XFillBmpTileOffsetXItem::XFillBmpTileOffsetXItem( sal_uInt16 nOffX ) :
+ SfxUInt16Item( XATTR_FILLBMP_TILEOFFSETX, nOffX )
+{
+}
+
+XFillBmpTileOffsetXItem* XFillBmpTileOffsetXItem::Clone( SfxItemPool* /*pPool*/) const
+{
+ return new XFillBmpTileOffsetXItem( *this );
+}
+
+bool XFillBmpTileOffsetXItem::GetPresentation
+(
+ SfxItemPresentation /*ePres*/,
+ MapUnit /*eCoreUnit*/,
+ MapUnit /*ePresUnit*/,
+ OUString& rText, const IntlWrapper&
+) const
+{
+ rText.clear();
+ return true;
+}
+
+
+XFillBmpTileOffsetYItem::XFillBmpTileOffsetYItem( sal_uInt16 nOffY ) :
+ SfxUInt16Item( XATTR_FILLBMP_TILEOFFSETY, nOffY )
+{
+}
+
+XFillBmpTileOffsetYItem* XFillBmpTileOffsetYItem::Clone( SfxItemPool* /*pPool*/) const
+{
+ return new XFillBmpTileOffsetYItem( *this );
+}
+
+bool XFillBmpTileOffsetYItem::GetPresentation
+(
+ SfxItemPresentation /*ePres*/,
+ MapUnit /*eCoreUnit*/,
+ MapUnit /*ePresUnit*/,
+ OUString& rText, const IntlWrapper&
+) const
+{
+ rText.clear();
+ return true;
+}
+
+XFillBmpStretchItem::XFillBmpStretchItem( bool bStretch ) :
+ SfxBoolItem( XATTR_FILLBMP_STRETCH, bStretch )
+{
+}
+
+XFillBmpStretchItem* XFillBmpStretchItem::Clone( SfxItemPool* /*pPool*/) const
+{
+ return new XFillBmpStretchItem( *this );
+}
+
+bool XFillBmpStretchItem::GetPresentation
+(
+ SfxItemPresentation /*ePres*/,
+ MapUnit /*eCoreUnit*/,
+ MapUnit /*ePresUnit*/,
+ OUString& rText, const IntlWrapper&
+) const
+{
+ rText.clear();
+ return true;
+}
+
+void XFillBmpStretchItem::dumpAsXml(xmlTextWriterPtr pWriter) const
+{
+ (void)xmlTextWriterStartElement(pWriter, BAD_CAST("XFillBmpStretchItem"));
+ (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("whichId"), BAD_CAST(OString::number(Which()).getStr()));
+ (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("value"), BAD_CAST(OString::boolean(GetValue()).getStr()));
+ (void)xmlTextWriterEndElement(pWriter);
+}
+
+
+XFillBmpPosOffsetXItem::XFillBmpPosOffsetXItem( sal_uInt16 nOffPosX ) :
+ SfxUInt16Item( XATTR_FILLBMP_POSOFFSETX, nOffPosX )
+{
+}
+
+XFillBmpPosOffsetXItem* XFillBmpPosOffsetXItem::Clone( SfxItemPool* /*pPool*/) const
+{
+ return new XFillBmpPosOffsetXItem( *this );
+}
+
+bool XFillBmpPosOffsetXItem::GetPresentation
+(
+ SfxItemPresentation /*ePres*/,
+ MapUnit /*eCoreUnit*/,
+ MapUnit /*ePresUnit*/,
+ OUString& rText, const IntlWrapper&
+) const
+{
+ rText.clear();
+ return true;
+}
+
+
+XFillBmpPosOffsetYItem::XFillBmpPosOffsetYItem( sal_uInt16 nOffPosY ) :
+ SfxUInt16Item( XATTR_FILLBMP_POSOFFSETY, nOffPosY )
+{
+}
+
+XFillBmpPosOffsetYItem* XFillBmpPosOffsetYItem::Clone( SfxItemPool* /*pPool*/) const
+{
+ return new XFillBmpPosOffsetYItem( *this );
+}
+
+bool XFillBmpPosOffsetYItem::GetPresentation
+(
+ SfxItemPresentation /*ePres*/,
+ MapUnit /*eCoreUnit*/,
+ MapUnit /*ePresUnit*/,
+ OUString& rText, const IntlWrapper&
+) const
+{
+ rText.clear();
+ return true;
+}
+
+XFillBackgroundItem::XFillBackgroundItem( bool bFill ) :
+ SfxBoolItem( XATTR_FILLBACKGROUND, bFill )
+{
+}
+
+XFillBackgroundItem* XFillBackgroundItem::Clone( SfxItemPool* /*pPool*/) const
+{
+ return new XFillBackgroundItem( *this );
+}
+
+bool XFillBackgroundItem::GetPresentation( SfxItemPresentation /*ePres*/, MapUnit /*eCoreUnit*/,
+ MapUnit /*ePresUnit*/, OUString& rText, const IntlWrapper&) const
+{
+ rText.clear();
+ return true;
+}
+
+void XFillBackgroundItem::dumpAsXml(xmlTextWriterPtr pWriter) const
+{
+ (void)xmlTextWriterStartElement(pWriter, BAD_CAST("XFillBackgroundItem"));
+ (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("whichId"), BAD_CAST(OString::number(Which()).getStr()));
+ (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("value"), BAD_CAST(OString::boolean(GetValue()).getStr()));
+ (void)xmlTextWriterEndElement(pWriter);
+}
+
+XFillUseSlideBackgroundItem::XFillUseSlideBackgroundItem( bool bFill ) :
+ SfxBoolItem( XATTR_FILLUSESLIDEBACKGROUND, bFill )
+{
+}
+
+XFillUseSlideBackgroundItem* XFillUseSlideBackgroundItem::Clone( SfxItemPool* /*pPool*/) const
+{
+ return new XFillUseSlideBackgroundItem( *this );
+}
+
+bool XFillUseSlideBackgroundItem::GetPresentation( SfxItemPresentation /*ePres*/, MapUnit /*eCoreUnit*/,
+ MapUnit /*ePresUnit*/, OUString& rText, const IntlWrapper&) const
+{
+ rText.clear();
+ return true;
+}
+
+void XFillUseSlideBackgroundItem::dumpAsXml(xmlTextWriterPtr pWriter) const
+{
+ (void)xmlTextWriterStartElement(pWriter, BAD_CAST("XFillUseSlideBackgroundItem"));
+ (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("whichId"), BAD_CAST(OString::number(Which()).getStr()));
+ (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("value"), BAD_CAST(OString::boolean(GetValue()).getStr()));
+ (void)xmlTextWriterEndElement(pWriter);
+}
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/xoutdev/xattrbmp.cxx b/svx/source/xoutdev/xattrbmp.cxx
new file mode 100644
index 000000000..2401bb361
--- /dev/null
+++ b/svx/source/xoutdev/xattrbmp.cxx
@@ -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/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * 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/graphic/XGraphic.hpp>
+
+#include <comphelper/propertyvalue.hxx>
+#include <tools/debug.hxx>
+#include <vcl/virdev.hxx>
+#include <vcl/bitmapex.hxx>
+#include <svl/style.hxx>
+#include <editeng/memberids.h>
+#include <svx/strings.hrc>
+#include <svx/xtable.hxx>
+#include <svx/xdef.hxx>
+#include <svx/unomid.hxx>
+#include <svx/unoapi.hxx>
+#include <svx/svdmodel.hxx>
+#include <svx/xbitmap.hxx>
+#include <svx/xbtmpit.hxx>
+#include <com/sun/star/beans/PropertyValue.hpp>
+#include <vcl/BitmapTools.hxx>
+#include <vcl/GraphicLoader.hxx>
+
+#include <libxml/xmlwriter.h>
+
+using namespace ::com::sun::star;
+
+XOBitmap::XOBitmap( const BitmapEx& rBmp ) :
+ xGraphicObject (new GraphicObject(rBmp)),
+ bGraphicDirty ( false )
+{
+}
+
+XOBitmap::~XOBitmap()
+{
+}
+
+BitmapEx XOBitmap::GetBitmap() const
+{
+ return GetGraphicObject().GetGraphic().GetBitmapEx();
+}
+
+const GraphicObject& XOBitmap::GetGraphicObject() const
+{
+ if( bGraphicDirty )
+ const_cast<XOBitmap*>(this)->Array2Bitmap();
+
+ return *xGraphicObject;
+}
+
+void XOBitmap::Bitmap2Array()
+{
+ ScopedVclPtrInstance< VirtualDevice > pVDev;
+ bool bPixelColor = false;
+ const BitmapEx aBitmap( GetBitmap() );
+ const sal_Int32 nLines = 8; // type dependent
+
+ if( !pPixelArray )
+ pPixelArray.reset( new sal_uInt16[ nLines * nLines ] );
+
+ pVDev->SetOutputSizePixel( aBitmap.GetSizePixel() );
+ pVDev->DrawBitmapEx( Point(), aBitmap );
+ aPixelColor = aBckgrColor = pVDev->GetPixel( Point() );
+
+ // create array and determine foreground and background color
+ for (sal_Int32 i = 0; i < nLines; ++i)
+ {
+ for (sal_Int32 j = 0; j < nLines; ++j)
+ {
+ if ( pVDev->GetPixel( Point( j, i ) ) == aBckgrColor )
+ pPixelArray[ j + i * nLines ] = 0;
+ else
+ {
+ pPixelArray[ j + i * nLines ] = 1;
+ if( !bPixelColor )
+ {
+ aPixelColor = pVDev->GetPixel( Point( j, i ) );
+ bPixelColor = true;
+ }
+ }
+ }
+ }
+}
+
+/// convert array, fore- and background color into a bitmap
+void XOBitmap::Array2Bitmap()
+{
+ if (!pPixelArray)
+ return;
+
+ ScopedVclPtrInstance< VirtualDevice > pVDev;
+ const sal_Int32 nLines = 8; // type dependent
+
+ pVDev->SetOutputSizePixel( Size( nLines, nLines ) );
+
+ // create bitmap
+ for (sal_Int32 i = 0; i < nLines; ++i)
+ {
+ for (sal_Int32 j = 0; j < nLines; ++j)
+ {
+ if( pPixelArray[ j + i * nLines ] == 0 )
+ pVDev->DrawPixel( Point( j, i ), aBckgrColor );
+ else
+ pVDev->DrawPixel( Point( j, i ), aPixelColor );
+ }
+ }
+
+ xGraphicObject.reset(new GraphicObject(pVDev->GetBitmapEx(Point(), Size(nLines, nLines))));
+ bGraphicDirty = false;
+}
+
+
+SfxPoolItem* XFillBitmapItem::CreateDefault() { return new XFillBitmapItem; }
+
+XFillBitmapItem::XFillBitmapItem(const OUString& rName, const GraphicObject& rGraphicObject)
+: NameOrIndex(XATTR_FILLBITMAP, rName),
+ maGraphicObject(rGraphicObject)
+{
+}
+
+XFillBitmapItem::XFillBitmapItem(const XFillBitmapItem& rItem)
+: NameOrIndex(rItem),
+ maGraphicObject(rItem.maGraphicObject)
+{
+}
+
+XFillBitmapItem::XFillBitmapItem(const GraphicObject& rGraphicObject)
+ : NameOrIndex(XATTR_FILLBITMAP, -1)
+ , maGraphicObject(rGraphicObject)
+{
+}
+
+XFillBitmapItem* XFillBitmapItem::Clone(SfxItemPool* /*pPool*/) const
+{
+ return new XFillBitmapItem(*this);
+}
+
+bool XFillBitmapItem::operator==(const SfxPoolItem& rItem) const
+{
+ return (NameOrIndex::operator==(rItem)
+ && maGraphicObject == static_cast<const XFillBitmapItem&>(rItem).maGraphicObject);
+}
+
+
+bool XFillBitmapItem::isPattern() const
+{
+ Color aBack, aFront;
+ return vcl::bitmap::isHistorical8x8(GetGraphicObject().GetGraphic().GetBitmapEx(), aBack, aFront);
+}
+
+bool XFillBitmapItem::GetPresentation(
+ SfxItemPresentation /*ePres*/,
+ MapUnit /*eCoreUnit*/,
+ MapUnit /*ePresUnit*/,
+ OUString& rText,
+ const IntlWrapper&) const
+{
+ rText += GetName();
+ return true;
+}
+
+bool XFillBitmapItem::QueryValue(css::uno::Any& rVal, sal_uInt8 nMemberId) const
+{
+ nMemberId &= ~CONVERT_TWIPS;
+
+ // needed for MID_NAME
+ OUString aApiName;
+ // needed for complete item (MID 0)
+ OUString aInternalName;
+
+ css::uno::Reference< css::awt::XBitmap > xBmp;
+
+ if( nMemberId == MID_NAME )
+ {
+ aApiName = SvxUnogetApiNameForItem(Which(), GetName());
+ }
+ else if( nMemberId == 0 )
+ {
+ aInternalName = GetName();
+ }
+
+ if (nMemberId == MID_BITMAP ||
+ nMemberId == 0)
+ {
+ xBmp.set(GetGraphicObject().GetGraphic().GetXGraphic(), uno::UNO_QUERY);
+ }
+
+ if( nMemberId == MID_NAME )
+ rVal <<= aApiName;
+ else if( nMemberId == MID_BITMAP )
+ rVal <<= xBmp;
+ else
+ {
+ // member-id 0 => complete item (e.g. for toolbars)
+ DBG_ASSERT( nMemberId == 0, "invalid member-id" );
+ uno::Sequence< beans::PropertyValue > aPropSeq{
+ comphelper::makePropertyValue("Name", aInternalName),
+ comphelper::makePropertyValue("Bitmap", xBmp)
+ };
+
+ rVal <<= aPropSeq;
+ }
+
+ return true;
+}
+
+bool XFillBitmapItem::PutValue( const css::uno::Any& rVal, sal_uInt8 nMemberId )
+{
+ nMemberId &= ~CONVERT_TWIPS;
+
+ OUString aName;
+ OUString aURL;
+ css::uno::Reference< css::awt::XBitmap > xBmp;
+ css::uno::Reference< css::graphic::XGraphic > xGraphic;
+
+ bool bSetURL = false;
+ bool bSetName = false;
+ bool bSetBitmap = false;
+
+ if( nMemberId == MID_NAME )
+ bSetName = (rVal >>= aName);
+ else if( nMemberId == MID_BITMAP )
+ {
+ if (rVal.has<OUString>())
+ {
+ bSetURL = true;
+ aURL = rVal.get<OUString>();
+ }
+ else if (rVal.has<uno::Reference<awt::XBitmap>>())
+ {
+ bSetBitmap = true;
+ xBmp = rVal.get<uno::Reference<awt::XBitmap>>();
+ }
+ else if (rVal.has<uno::Reference<graphic::XGraphic>>())
+ {
+ bSetBitmap = true;
+ xGraphic = rVal.get<uno::Reference<graphic::XGraphic>>();
+ }
+ }
+ else
+ {
+ DBG_ASSERT( nMemberId == 0, "invalid member-id" );
+ uno::Sequence< beans::PropertyValue > aPropSeq;
+ if( rVal >>= aPropSeq )
+ {
+ for ( const auto& rProp : std::as_const(aPropSeq) )
+ {
+ if ( rProp.Name == "Name" )
+ bSetName = (rProp.Value >>= aName);
+ else if ( rProp.Name == "Bitmap" )
+ bSetBitmap = (rProp.Value >>= xBmp);
+ else if ( rProp.Name == "FillBitmapURL" )
+ bSetURL = (rProp.Value >>= aURL);
+ }
+ }
+ }
+
+ if( bSetName )
+ {
+ SetName( aName );
+ }
+ if (bSetURL && !aURL.isEmpty())
+ {
+ Graphic aGraphic = vcl::graphic::loadFromURL(aURL);
+ if (!aGraphic.IsNone())
+ {
+ maGraphicObject.SetGraphic(aGraphic.GetXGraphic());
+ }
+ }
+ else if( bSetBitmap )
+ {
+ if (xBmp.is())
+ {
+ xGraphic.set(xBmp, uno::UNO_QUERY);
+ }
+ if (xGraphic.is())
+ {
+ maGraphicObject.SetGraphic(xGraphic);
+ }
+ }
+
+ return (bSetURL || bSetName || bSetBitmap);
+}
+
+bool XFillBitmapItem::CompareValueFunc( const NameOrIndex* p1, const NameOrIndex* p2 )
+{
+ const GraphicObject& aGraphicObjectA(static_cast<const XFillBitmapItem*>(p1)->GetGraphicObject());
+ const GraphicObject& aGraphicObjectB(static_cast<const XFillBitmapItem*>(p2)->GetGraphicObject());
+
+ return aGraphicObjectA == aGraphicObjectB;
+}
+
+std::unique_ptr<XFillBitmapItem> XFillBitmapItem::checkForUniqueItem( SdrModel* pModel ) const
+{
+ if( pModel )
+ {
+ XPropertyListType aListType = XPropertyListType::Bitmap;
+ if(isPattern())
+ aListType = XPropertyListType::Pattern;
+ const OUString aUniqueName = NameOrIndex::CheckNamedItem(
+ this, XATTR_FILLBITMAP, &pModel->GetItemPool(),
+ XFillBitmapItem::CompareValueFunc, RID_SVXSTR_BMP21,
+ pModel->GetPropertyList( aListType ) );
+
+ // if the given name is not valid, replace it!
+ if( aUniqueName != GetName() )
+ {
+ return std::make_unique<XFillBitmapItem>(aUniqueName, maGraphicObject);
+ }
+ }
+
+ return nullptr;
+}
+
+void XFillBitmapItem::dumpAsXml(xmlTextWriterPtr pWriter) const
+{
+ (void)xmlTextWriterStartElement(pWriter, BAD_CAST("XFillBitmapItem"));
+ (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("whichId"), BAD_CAST(OString::number(Which()).getStr()));
+
+ NameOrIndex::dumpAsXml(pWriter);
+
+ (void)xmlTextWriterEndElement(pWriter);
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/xoutdev/xpool.cxx b/svx/source/xoutdev/xpool.cxx
new file mode 100644
index 000000000..b3e044e83
--- /dev/null
+++ b/svx/source/xoutdev/xpool.cxx
@@ -0,0 +1,219 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * 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 <svx/xflbckit.hxx>
+#include <xftshtit.hxx>
+#include <svx/xflboxy.hxx>
+#include <svx/xflbstit.hxx>
+#include <svx/xflclit.hxx>
+#include <svx/xflgrit.hxx>
+#include <svx/xflhtit.hxx>
+#include <svx/xbtmpit.hxx>
+#include <svx/xflftrit.hxx>
+#include <svx/xsflclit.hxx>
+#include <svx/xlntrit.hxx>
+#include <svx/xfltrit.hxx>
+#include <svx/xgrscit.hxx>
+#include <svx/xflasit.hxx>
+#include <svx/xflbmtit.hxx>
+#include <svx/xflbmpit.hxx>
+#include <svx/xflbmsxy.hxx>
+#include <svx/xflbmsli.hxx>
+#include <svx/xflbtoxy.hxx>
+#include <svx/xlineit0.hxx>
+#include <svx/xlinjoit.hxx>
+#include <svx/xlncapit.hxx>
+#include <svx/xfillit0.hxx>
+#include <svx/xfilluseslidebackgrounditem.hxx>
+#include <svx/xtextit0.hxx>
+#include <svx/xlnasit.hxx>
+#include <svx/xlndsit.hxx>
+#include <svx/xlnwtit.hxx>
+#include <svx/xlnclit.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/xpool.hxx>
+#include <svx/svddef.hxx>
+#include <svx/svxids.hrc>
+#include <svl/itemset.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>
+
+XOutdevItemPool::XOutdevItemPool(SfxItemPool* _pMaster)
+ : SfxItemPool("XOutdevItemPool", SDRATTR_START, SDRATTR_END, nullptr, nullptr)
+ , mpLocalPoolDefaults(new std::vector<SfxPoolItem*>(SDRATTR_END - SDRATTR_START + 1))
+ , mpLocalItemInfos(new SfxItemInfo[SDRATTR_END - SDRATTR_START + 1])
+{
+ // prepare some defaults
+ const OUString aNullStr;
+ const Graphic aNullGraphic;
+ const basegfx::B2DPolyPolygon aNullPol;
+ const Color aNullLineCol(COL_DEFAULT_SHAPE_STROKE); // #i121448# Use defined default color
+ const Color aNullFillCol(COL_DEFAULT_SHAPE_FILLING); // #i121448# Use defined default color
+ const Color aNullShadowCol(COL_LIGHTGRAY);
+ const XDash aNullDash;
+ const XHatch aNullHatch(aNullLineCol);
+
+ // get master pointer, evtl. add myself to the end of the pools
+ if(!_pMaster)
+ {
+ _pMaster = this;
+ }
+ else
+ {
+ _pMaster->GetLastPoolInChain()->SetSecondaryPool(this);
+ }
+
+ // prepare PoolDefaults
+ std::vector<SfxPoolItem*>& rPoolDefaults = *mpLocalPoolDefaults;
+ rPoolDefaults[XATTR_LINESTYLE -XATTR_START] = new XLineStyleItem;
+ rPoolDefaults[XATTR_LINEDASH -XATTR_START] = new XLineDashItem(aNullDash);
+ rPoolDefaults[XATTR_LINEWIDTH -XATTR_START] = new XLineWidthItem;
+ rPoolDefaults[XATTR_LINECOLOR -XATTR_START] = new XLineColorItem(aNullStr,aNullLineCol);
+ rPoolDefaults[XATTR_LINESTART -XATTR_START] = new XLineStartItem(aNullPol);
+ rPoolDefaults[XATTR_LINEEND -XATTR_START] = new XLineEndItem (aNullPol);
+ rPoolDefaults[XATTR_LINESTARTWIDTH -XATTR_START] = new XLineStartWidthItem;
+ rPoolDefaults[XATTR_LINEENDWIDTH -XATTR_START] = new XLineEndWidthItem;
+ rPoolDefaults[XATTR_LINESTARTCENTER -XATTR_START] = new XLineStartCenterItem;
+ rPoolDefaults[XATTR_LINEENDCENTER -XATTR_START] = new XLineEndCenterItem;
+ rPoolDefaults[XATTR_LINETRANSPARENCE -XATTR_START] = new XLineTransparenceItem;
+ rPoolDefaults[XATTR_LINEJOINT -XATTR_START] = new XLineJointItem;
+ rPoolDefaults[XATTR_LINECAP -XATTR_START] = new XLineCapItem;
+ rPoolDefaults[XATTR_FILLSTYLE -XATTR_START] = new XFillStyleItem;
+ rPoolDefaults[XATTR_FILLCOLOR -XATTR_START] = new XFillColorItem (aNullStr,aNullFillCol);
+ rPoolDefaults[XATTR_FILLGRADIENT -XATTR_START] = new XFillGradientItem(XGradient(COL_BLACK, COL_WHITE));
+ rPoolDefaults[XATTR_FILLHATCH -XATTR_START] = new XFillHatchItem (aNullHatch);
+ rPoolDefaults[XATTR_FILLBITMAP -XATTR_START] = new XFillBitmapItem (aNullGraphic);
+ rPoolDefaults[XATTR_FILLTRANSPARENCE -XATTR_START] = new XFillTransparenceItem;
+ rPoolDefaults[XATTR_GRADIENTSTEPCOUNT -XATTR_START] = new XGradientStepCountItem;
+ rPoolDefaults[XATTR_FILLBMP_TILE -XATTR_START] = new XFillBmpTileItem;
+ rPoolDefaults[XATTR_FILLBMP_POS -XATTR_START] = new XFillBmpPosItem;
+ rPoolDefaults[XATTR_FILLBMP_SIZEX -XATTR_START] = new XFillBmpSizeXItem;
+ rPoolDefaults[XATTR_FILLBMP_SIZEY -XATTR_START] = new XFillBmpSizeYItem;
+ rPoolDefaults[XATTR_FILLBMP_SIZELOG -XATTR_START] = new XFillBmpSizeLogItem;
+ rPoolDefaults[XATTR_FILLBMP_TILEOFFSETX -XATTR_START] = new XFillBmpTileOffsetXItem;
+ rPoolDefaults[XATTR_FILLBMP_TILEOFFSETY -XATTR_START] = new XFillBmpTileOffsetYItem;
+ rPoolDefaults[XATTR_FILLBMP_STRETCH -XATTR_START] = new XFillBmpStretchItem;
+ rPoolDefaults[XATTR_FILLBMP_POSOFFSETX -XATTR_START] = new XFillBmpPosOffsetXItem;
+ rPoolDefaults[XATTR_FILLBMP_POSOFFSETY -XATTR_START] = new XFillBmpPosOffsetYItem;
+ rPoolDefaults[XATTR_FILLFLOATTRANSPARENCE -XATTR_START] = new XFillFloatTransparenceItem( XGradient(COL_BLACK, COL_BLACK), false );
+ rPoolDefaults[XATTR_SECONDARYFILLCOLOR -XATTR_START] = new XSecondaryFillColorItem(aNullStr, aNullFillCol);
+ rPoolDefaults[XATTR_FILLBACKGROUND -XATTR_START] = new XFillBackgroundItem;
+ rPoolDefaults[XATTR_FILLUSESLIDEBACKGROUND -XATTR_START] = new XFillUseSlideBackgroundItem;
+ rPoolDefaults[XATTR_FORMTXTSTYLE -XATTR_START] = new XFormTextStyleItem;
+ rPoolDefaults[XATTR_FORMTXTADJUST -XATTR_START] = new XFormTextAdjustItem;
+ rPoolDefaults[XATTR_FORMTXTDISTANCE -XATTR_START] = new XFormTextDistanceItem;
+ rPoolDefaults[XATTR_FORMTXTSTART -XATTR_START] = new XFormTextStartItem;
+ rPoolDefaults[XATTR_FORMTXTMIRROR -XATTR_START] = new XFormTextMirrorItem;
+ rPoolDefaults[XATTR_FORMTXTOUTLINE -XATTR_START] = new XFormTextOutlineItem;
+ rPoolDefaults[XATTR_FORMTXTSHADOW -XATTR_START] = new XFormTextShadowItem;
+ rPoolDefaults[XATTR_FORMTXTSHDWCOLOR -XATTR_START] = new XFormTextShadowColorItem(aNullStr,aNullShadowCol);
+ rPoolDefaults[XATTR_FORMTXTSHDWXVAL -XATTR_START] = new XFormTextShadowXValItem;
+ rPoolDefaults[XATTR_FORMTXTSHDWYVAL -XATTR_START] = new XFormTextShadowYValItem;
+ rPoolDefaults[XATTR_FORMTXTHIDEFORM -XATTR_START] = new XFormTextHideFormItem;
+ rPoolDefaults[XATTR_FORMTXTSHDWTRANSP -XATTR_START] = new XFormTextShadowTranspItem;
+
+ // create SetItems
+ rPoolDefaults[XATTRSET_LINE - XATTR_START] = new XLineAttrSetItem(
+ SfxItemSetFixed<XATTR_LINE_FIRST, XATTR_LINE_LAST>( *_pMaster ) );
+ rPoolDefaults[XATTRSET_FILL - XATTR_START] = new XFillAttrSetItem(
+ SfxItemSetFixed<XATTR_FILL_FIRST, XATTR_FILL_LAST>( *_pMaster ) );
+
+ // create ItemInfos
+ for(sal_uInt16 i(GetFirstWhich()); i <= GetLastWhich(); i++)
+ {
+ mpLocalItemInfos[i - XATTR_START]._nSID = 0;
+ mpLocalItemInfos[i - XATTR_START]._bPoolable = true;
+ }
+
+ mpLocalItemInfos[XATTR_LINESTYLE -XATTR_START]._nSID = SID_ATTR_LINE_STYLE;
+ mpLocalItemInfos[XATTR_LINEDASH -XATTR_START]._nSID = SID_ATTR_LINE_DASH;
+ mpLocalItemInfos[XATTR_LINEWIDTH -XATTR_START]._nSID = SID_ATTR_LINE_WIDTH;
+ mpLocalItemInfos[XATTR_LINECOLOR -XATTR_START]._nSID = SID_ATTR_LINE_COLOR;
+ mpLocalItemInfos[XATTR_LINESTART -XATTR_START]._nSID = SID_ATTR_LINE_START;
+ mpLocalItemInfos[XATTR_LINEEND -XATTR_START]._nSID = SID_ATTR_LINE_END;
+ mpLocalItemInfos[XATTR_LINESTARTWIDTH -XATTR_START]._nSID = SID_ATTR_LINE_STARTWIDTH;
+ mpLocalItemInfos[XATTR_LINEENDWIDTH -XATTR_START]._nSID = SID_ATTR_LINE_ENDWIDTH;
+ mpLocalItemInfos[XATTR_LINESTARTCENTER -XATTR_START]._nSID = SID_ATTR_LINE_STARTCENTER;
+ mpLocalItemInfos[XATTR_LINEENDCENTER -XATTR_START]._nSID = SID_ATTR_LINE_ENDCENTER;
+ mpLocalItemInfos[XATTR_FILLSTYLE -XATTR_START]._nSID = SID_ATTR_FILL_STYLE;
+ mpLocalItemInfos[XATTR_FILLCOLOR -XATTR_START]._nSID = SID_ATTR_FILL_COLOR;
+ mpLocalItemInfos[XATTR_FILLGRADIENT -XATTR_START]._nSID = SID_ATTR_FILL_GRADIENT;
+ mpLocalItemInfos[XATTR_FILLHATCH -XATTR_START]._nSID = SID_ATTR_FILL_HATCH;
+ mpLocalItemInfos[XATTR_FILLBITMAP -XATTR_START]._nSID = SID_ATTR_FILL_BITMAP;
+ mpLocalItemInfos[XATTR_FORMTXTSTYLE -XATTR_START]._nSID = SID_FORMTEXT_STYLE;
+ mpLocalItemInfos[XATTR_FORMTXTADJUST -XATTR_START]._nSID = SID_FORMTEXT_ADJUST;
+ mpLocalItemInfos[XATTR_FORMTXTDISTANCE -XATTR_START]._nSID = SID_FORMTEXT_DISTANCE;
+ mpLocalItemInfos[XATTR_FORMTXTSTART -XATTR_START]._nSID = SID_FORMTEXT_START;
+ mpLocalItemInfos[XATTR_FORMTXTMIRROR -XATTR_START]._nSID = SID_FORMTEXT_MIRROR;
+ mpLocalItemInfos[XATTR_FORMTXTOUTLINE -XATTR_START]._nSID = SID_FORMTEXT_OUTLINE;
+ mpLocalItemInfos[XATTR_FORMTXTSHADOW -XATTR_START]._nSID = SID_FORMTEXT_SHADOW;
+ mpLocalItemInfos[XATTR_FORMTXTSHDWCOLOR -XATTR_START]._nSID = SID_FORMTEXT_SHDWCOLOR;
+ mpLocalItemInfos[XATTR_FORMTXTSHDWXVAL -XATTR_START]._nSID = SID_FORMTEXT_SHDWXVAL;
+ mpLocalItemInfos[XATTR_FORMTXTSHDWYVAL -XATTR_START]._nSID = SID_FORMTEXT_SHDWYVAL;
+ mpLocalItemInfos[XATTR_FORMTXTHIDEFORM -XATTR_START]._nSID = SID_FORMTEXT_HIDEFORM;
+
+ // associate new slots for panels with known items
+ mpLocalItemInfos[XATTR_FILLUSESLIDEBACKGROUND - XATTR_START]._nSID = SID_ATTR_FILL_USE_SLIDE_BACKGROUND;
+ mpLocalItemInfos[XATTR_FILLTRANSPARENCE - XATTR_START]._nSID = SID_ATTR_FILL_TRANSPARENCE;
+ mpLocalItemInfos[XATTR_FILLFLOATTRANSPARENCE - XATTR_START]._nSID = SID_ATTR_FILL_FLOATTRANSPARENCE;
+ mpLocalItemInfos[XATTR_LINETRANSPARENCE - XATTR_START]._nSID = SID_ATTR_LINE_TRANSPARENCE;
+ mpLocalItemInfos[XATTR_LINEJOINT - XATTR_START]._nSID = SID_ATTR_LINE_JOINT;
+ mpLocalItemInfos[XATTR_LINECAP - XATTR_START]._nSID = SID_ATTR_LINE_CAP;
+
+ // if it's my own creation level, set Defaults and ItemInfos
+ if(XATTR_START == GetFirstWhich() && XATTR_END == GetLastWhich())
+ {
+ SetDefaults(mpLocalPoolDefaults);
+ SetItemInfos(mpLocalItemInfos.get());
+ }
+}
+
+// copy ctor, clones all static defaults
+XOutdevItemPool::XOutdevItemPool(const XOutdevItemPool& rPool)
+: SfxItemPool(rPool, true),
+ mpLocalPoolDefaults(nullptr)
+{
+}
+
+rtl::Reference<SfxItemPool> XOutdevItemPool::Clone() const
+{
+ return new XOutdevItemPool(*this);
+}
+
+XOutdevItemPool::~XOutdevItemPool()
+{
+ Delete();
+ // release and delete static pool default items
+ ReleaseDefaults(true);
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/xoutdev/xtabbtmp.cxx b/svx/source/xoutdev/xtabbtmp.cxx
new file mode 100644
index 000000000..178ba8ab2
--- /dev/null
+++ b/svx/source/xoutdev/xtabbtmp.cxx
@@ -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 .
+ */
+
+#include <XPropertyTable.hxx>
+
+#include <osl/diagnose.h>
+#include <vcl/virdev.hxx>
+#include <svx/xtable.hxx>
+#include <vcl/bitmapex.hxx>
+#include <vcl/settings.hxx>
+#include <vcl/svapp.hxx>
+
+using namespace com::sun::star;
+
+XBitmapEntry* XBitmapList::GetBitmap(tools::Long nIndex) const
+{
+ return static_cast<XBitmapEntry*>( XPropertyList::Get(nIndex) );
+}
+
+uno::Reference< container::XNameContainer > XBitmapList::createInstance()
+{
+ return SvxUnoXBitmapTable_createInstance( *this );
+}
+
+bool XBitmapList::Create()
+{
+ return true;
+}
+
+BitmapEx XBitmapList::CreateBitmap( tools::Long nIndex, const Size& rSize ) const
+{
+ OSL_ENSURE( nIndex < Count(), "Access out of range" );
+
+ if(nIndex < Count())
+ {
+ BitmapEx rBitmapEx = GetBitmap( nIndex )->GetGraphicObject().GetGraphic().GetBitmapEx();
+ ScopedVclPtrInstance< VirtualDevice > pVirtualDevice;
+ pVirtualDevice->SetOutputSizePixel(rSize);
+
+ if(rBitmapEx.IsAlpha())
+ {
+ const StyleSettings& rStyleSettings = Application::GetSettings().GetStyleSettings();
+
+ if(rStyleSettings.GetPreviewUsesCheckeredBackground())
+ {
+ const Point aNull(0, 0);
+ static const sal_uInt32 nLen(8);
+ static const Color aW(COL_WHITE);
+ static const Color aG(0xef, 0xef, 0xef);
+
+ pVirtualDevice->DrawCheckered(aNull, rSize, nLen, aW, aG);
+ }
+ else
+ {
+ pVirtualDevice->SetBackground(rStyleSettings.GetFieldColor());
+ pVirtualDevice->Erase();
+ }
+ }
+
+ if(rBitmapEx.GetSizePixel().Width() >= rSize.Width() && rBitmapEx.GetSizePixel().Height() >= rSize.Height())
+ {
+ rBitmapEx.Scale(rSize);
+ pVirtualDevice->DrawBitmapEx(Point(0, 0), rBitmapEx);
+ }
+ else
+ {
+ const Size aBitmapSize(rBitmapEx.GetSizePixel());
+
+ for(tools::Long y(0); y < rSize.Height(); y += aBitmapSize.Height())
+ {
+ for(tools::Long x(0); x < rSize.Width(); x += aBitmapSize.Width())
+ {
+ pVirtualDevice->DrawBitmapEx(
+ Point(x, y),
+ rBitmapEx);
+ }
+ }
+ }
+ rBitmapEx = pVirtualDevice->GetBitmapEx(Point(0, 0), rSize);
+ return rBitmapEx;
+ }
+ else
+ return BitmapEx();
+}
+
+BitmapEx XBitmapList::CreateBitmapForUI( tools::Long nIndex )
+{
+ const StyleSettings& rStyleSettings = Application::GetSettings().GetStyleSettings();
+ const Size& rSize = rStyleSettings.GetListBoxPreviewDefaultPixelSize();
+ return CreateBitmap(nIndex, rSize);
+}
+
+BitmapEx XBitmapList::GetBitmapForPreview( tools::Long nIndex, const Size& rSize )
+{
+ return CreateBitmap(nIndex, rSize);
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/xoutdev/xtabcolr.cxx b/svx/source/xoutdev/xtabcolr.cxx
new file mode 100644
index 000000000..e952d6f8a
--- /dev/null
+++ b/svx/source/xoutdev/xtabcolr.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 <memory>
+#include <XPropertyTable.hxx>
+#include <unotools/configmgr.hxx>
+#include <unotools/pathoptions.hxx>
+
+#include <svx/strings.hrc>
+#include <svx/dialmgr.hxx>
+#include <svx/xtable.hxx>
+
+using namespace com::sun::star;
+
+XColorListRef XColorList::CreateStdColorList()
+{
+ return XPropertyList::AsColorList(
+ XPropertyList::CreatePropertyList(
+ XPropertyListType::Color, !utl::ConfigManager::IsFuzzing() ?
+ SvtPathOptions().GetPalettePath() :
+ "", ""));
+}
+
+XColorListRef XColorList::GetStdColorList()
+{
+ XColorListRef aTable( CreateStdColorList() );
+ return aTable;
+}
+
+void XColorList::Replace(tools::Long nIndex, std::unique_ptr<XColorEntry> pEntry)
+{
+ XPropertyList::Replace(std::move(pEntry), nIndex);
+}
+XColorEntry* XColorList::GetColor(tools::Long nIndex) const
+{
+ return static_cast<XColorEntry*>( XPropertyList::Get(nIndex) );
+}
+
+uno::Reference< container::XNameContainer > XColorList::createInstance()
+{
+ return SvxUnoXColorTable_createInstance( *this );
+}
+
+bool XColorList::Create()
+{
+ sal_uInt32 a(0);
+ sal_uInt32 b(0);
+
+ // <!-- Gray palette from white to black -->
+ const OUString aStrGrey( SvxResId( RID_SVXSTR_COLOR_GREY ) );
+
+ Insert( std::make_unique<XColorEntry>( Color( 0xff, 0xff, 0xff ), SvxResId( RID_SVXSTR_COLOR_WHITE ) ) );
+ Insert( std::make_unique<XColorEntry>( Color( 0xee, 0xee, 0xee ), aStrGrey + " 1" ) );
+ Insert( std::make_unique<XColorEntry>( Color( 0xdd, 0xdd, 0xdd ), aStrGrey + " 2" ) );
+ Insert( std::make_unique<XColorEntry>( Color( 0xcc, 0xcc, 0xcc ), aStrGrey + " 3" ) );
+ Insert( std::make_unique<XColorEntry>( Color( 0xb2, 0xb2, 0xb2 ), aStrGrey + " 4" ) );
+ Insert( std::make_unique<XColorEntry>( Color( 0x99, 0x99, 0x99 ), aStrGrey + " 5" ) );
+ Insert( std::make_unique<XColorEntry>( Color( 0x80, 0x80, 0x80 ), aStrGrey + " 6" ) );
+ Insert( std::make_unique<XColorEntry>( Color( 0x66, 0x66, 0x66 ), aStrGrey + " 7" ) );
+ Insert( std::make_unique<XColorEntry>( Color( 0x33, 0x33, 0x33 ), aStrGrey + " 8" ) );
+ Insert( std::make_unique<XColorEntry>( Color( 0x1c, 0x1c, 0x1c ), aStrGrey + " 9" ) );
+ Insert( std::make_unique<XColorEntry>( Color( 0x11, 0x11, 0x11 ), aStrGrey + " 10") );
+ Insert( std::make_unique<XColorEntry>( Color( 0x00, 0x00, 0x00 ), SvxResId( RID_SVXSTR_COLOR_BLACK ) ) );
+
+ // <!-- Base colors step 0 to 10 -->
+ const sal_uInt32 nNumColorsInGroup(12);
+ const sal_uInt32 nNumGroups(11);
+ const OUString aStrCol[nNumColorsInGroup] = {
+ SvxResId(RID_SVXSTR_COLOR_YELLOW),
+ SvxResId(RID_SVXSTR_COLOR_ORANGE),
+ SvxResId(RID_SVXSTR_COLOR_RED),
+ SvxResId(RID_SVXSTR_COLOR_PINK),
+ SvxResId(RID_SVXSTR_COLOR_MAGENTA),
+ SvxResId(RID_SVXSTR_COLOR_PURPLE),
+ SvxResId(RID_SVXSTR_COLOR_BLUE),
+ SvxResId(RID_SVXSTR_COLOR_SKYBLUE),
+ SvxResId(RID_SVXSTR_COLOR_CYAN),
+ SvxResId(RID_SVXSTR_COLOR_TURQUOISE),
+ SvxResId(RID_SVXSTR_COLOR_GREEN),
+ SvxResId(RID_SVXSTR_COLOR_YELLOWGREEN) };
+ static const sal_uInt32 aStdCol[nNumColorsInGroup * nNumGroups] = {
+ 0xffff99, 0xff6600, 0xff3333, 0xff00cc, 0xff33ff, 0x9900ff, 0x6666ff, 0x00ccff, 0x66ffff, 0x33ff99, 0x99ff66, 0xccff00,
+ 0xffff66, 0xffcc00, 0xff9999, 0xff66cc, 0xff99ff, 0xcc66ff, 0x9999ff, 0x9999ff, 0x99ffff, 0x66ff99, 0x99ff99, 0xccff66,
+ 0xffff00, 0xff9900, 0xff6666, 0xff3399, 0xff66ff, 0x9933ff, 0x3333ff, 0x3399ff, 0x00ffff, 0x00ff66, 0x66ff66, 0x99ff33,
+ 0xcc9900, 0xff3300, 0xff0000, 0xff0066, 0xff00ff, 0x6600ff, 0x0000ff, 0x0066ff, 0x00cccc, 0x00cc33, 0x00cc00, 0x66ff00,
+ 0x996600, 0xcc3300, 0xcc0000, 0xcc0066, 0xcc00cc, 0x6600cc, 0x0000cc, 0x0066cc, 0x009999, 0x009933, 0x009900, 0x66cc00,
+ 0x663300, 0x801900, 0x990000, 0x990066, 0x990099, 0x330099, 0x000099, 0x006699, 0x006666, 0x007826, 0x006600, 0x669900,
+ 0x333300, 0x461900, 0x330000, 0x330033, 0x660066, 0x000033, 0x000066, 0x000080, 0x003333, 0x00331a, 0x003300, 0x193300,
+ 0x666633, 0x661900, 0x663333, 0x660033, 0x663366, 0x330066, 0x333366, 0x003366, 0x336666, 0x006633, 0x336633, 0x336600,
+ 0x999966, 0x996633, 0x996666, 0x993366, 0x996699, 0x663399, 0x666699, 0x336699, 0x669999, 0x339966, 0x669966, 0x669933,
+ 0xcccc99, 0xcc9966, 0xcc9999, 0xcc6699, 0xcc99cc, 0x9966cc, 0x9999cc, 0x6699cc, 0x99cccc, 0x66cc99, 0x99cc99, 0x99cc66,
+ 0xffffcc, 0xffcc99, 0xffcccc, 0xff99cc, 0xffccff, 0xcc99ff, 0xccccff, 0x99ccff, 0xccffff, 0x99ffcc, 0xccffcc, 0xccff99 };
+
+ for(a = 0; a < nNumGroups; a++)
+ {
+ OUString aSuffix;
+ if (a > 0) aSuffix = OUString::number(a);
+
+ const sal_uInt32 nOffset(a * nNumColorsInGroup);
+
+ for(b = 0; b < nNumColorsInGroup; b++)
+ {
+ Insert( std::make_unique<XColorEntry>( Color(ColorTransparency, aStdCol[nOffset + b]), aStrCol[b] + aSuffix ) );
+ }
+ }
+
+ // <!-- use some 'nice' colors from original palette -->
+ Insert( std::make_unique<XColorEntry>( Color( 0xe6, 0xe6, 0xff ), SvxResId( RID_SVXSTR_COLOR_BLUEGREY) ) );
+ Insert( std::make_unique<XColorEntry>( Color( 0xCF, 0xE7, 0xF5 ), SvxResId( RID_SVXSTR_COLOR_BLUE_CLASSIC ) ) );
+
+ // <!-- add 'pale' colors from original palette -->
+ Insert( std::make_unique<XColorEntry>( Color( 0x99, 0x99, 0xff ), SvxResId( RID_SVXSTR_COLOR_VIOLET ) ) );
+ Insert( std::make_unique<XColorEntry>( Color( 0x99, 0x33, 0x66 ), SvxResId( RID_SVXSTR_COLOR_BORDEAUX ) ) );
+ Insert( std::make_unique<XColorEntry>( Color( 0xff, 0xff, 0xcc ), SvxResId( RID_SVXSTR_COLOR_PALE_YELLOW ) ) );
+ Insert( std::make_unique<XColorEntry>( Color( 0xcc, 0xff, 0xff ), SvxResId( RID_SVXSTR_COLOR_PALE_GREEN ) ) );
+ Insert( std::make_unique<XColorEntry>( Color( 0x66, 0x00, 0x66 ), SvxResId( RID_SVXSTR_COLOR_DARKVIOLET ) ) );
+ Insert( std::make_unique<XColorEntry>( Color( 0xff, 0x80, 0x80 ), SvxResId( RID_SVXSTR_COLOR_SALMON ) ) );
+ Insert( std::make_unique<XColorEntry>( Color( 0x00, 0x66, 0xcc ), SvxResId( RID_SVXSTR_COLOR_SEABLUE ) ) );
+
+ // <!-- add Chart colors from original palette (also 12, coincidence?) -->
+ const OUString aStrChart( SvxResId( RID_SVXSTR_COLOR_CHART ) );
+ Insert( std::make_unique<XColorEntry>( Color( 0x00, 0x45, 0x86 ), aStrChart + " 1" ) );
+ Insert( std::make_unique<XColorEntry>( Color( 0xff, 0x42, 0x0e ), aStrChart + " 2" ) );
+ Insert( std::make_unique<XColorEntry>( Color( 0xff, 0xd3, 0x20 ), aStrChart + " 3" ) );
+ Insert( std::make_unique<XColorEntry>( Color( 0x57, 0x9d, 0x1c ), aStrChart + " 4" ) );
+ Insert( std::make_unique<XColorEntry>( Color( 0x7e, 0x00, 0x21 ), aStrChart + " 5" ) );
+ Insert( std::make_unique<XColorEntry>( Color( 0x83, 0xca, 0xff ), aStrChart + " 6" ) );
+ Insert( std::make_unique<XColorEntry>( Color( 0x31, 0x40, 0x04 ), aStrChart + " 7" ) );
+ Insert( std::make_unique<XColorEntry>( Color( 0xae, 0xcf, 0x00 ), aStrChart + " 8" ) );
+ Insert( std::make_unique<XColorEntry>( Color( 0x4b, 0x1f, 0x6f ), aStrChart + " 9" ) );
+ Insert( std::make_unique<XColorEntry>( Color( 0xff, 0x95, 0x0e ), aStrChart + " 10" ) );
+ Insert( std::make_unique<XColorEntry>( Color( 0xc5, 0x00, 0x0b ), aStrChart + " 11" ) );
+ Insert( std::make_unique<XColorEntry>( Color( 0x00, 0x84, 0xd1 ), aStrChart + " 12" ) );
+
+ return(165 == Count());
+}
+
+BitmapEx XColorList::CreateBitmapForUI( tools::Long /*nIndex*/ )
+{
+ return BitmapEx();
+}
+
+tools::Long XColorList::GetIndexOfColor( const Color& rColor ) const
+{
+ for( tools::Long i = 0, n = maList.size(); i < n; ++i )
+ {
+ const Color aColor = static_cast<XColorEntry*>( maList[i].get() )->GetColor();
+
+ if (aColor == rColor )
+ return i;
+ }
+
+ return -1;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/xoutdev/xtabdash.cxx b/svx/source/xoutdev/xtabdash.cxx
new file mode 100644
index 000000000..da5ec20a3
--- /dev/null
+++ b/svx/source/xoutdev/xtabdash.cxx
@@ -0,0 +1,220 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <XPropertyTable.hxx>
+
+#include <vcl/svapp.hxx>
+#include <vcl/settings.hxx>
+
+#include <vcl/virdev.hxx>
+#include <svx/strings.hrc>
+#include <svx/dialmgr.hxx>
+#include <svx/xtable.hxx>
+
+#include <comphelper/lok.hxx>
+
+#include <basegfx/matrix/b2dhommatrix.hxx>
+#include <drawinglayer/attribute/lineattribute.hxx>
+#include <drawinglayer/attribute/strokeattribute.hxx>
+#include <drawinglayer/primitive2d/PolygonStrokePrimitive2D.hxx>
+#include <drawinglayer/processor2d/baseprocessor2d.hxx>
+#include <drawinglayer/processor2d/processor2dtools.hxx>
+#include <memory>
+
+using namespace com::sun::star;
+
+XDashList::XDashList(const OUString& rPath, const OUString& rReferer)
+ : XPropertyList(XPropertyListType::Dash, rPath, rReferer)
+{
+}
+
+XDashList::~XDashList()
+{
+}
+
+void XDashList::Replace(std::unique_ptr<XDashEntry> pEntry, tools::Long nIndex)
+{
+ XPropertyList::Replace(std::move(pEntry), nIndex);
+}
+
+XDashEntry* XDashList::GetDash(tools::Long nIndex) const
+{
+ return static_cast<XDashEntry*>( XPropertyList::Get(nIndex) );
+}
+
+uno::Reference< container::XNameContainer > XDashList::createInstance()
+{
+ return SvxUnoXDashTable_createInstance( *this );
+}
+
+bool XDashList::Create()
+{
+ const OUString aStr(SvxResId(RID_SVXSTR_LINESTYLE));
+
+ Insert(std::make_unique<XDashEntry>(XDash(css::drawing::DashStyle_RECT,1, 50,1, 50, 50),aStr + " 1"));
+ Insert(std::make_unique<XDashEntry>(XDash(css::drawing::DashStyle_RECT,1,500,1,500,500),aStr + " 2"));
+ Insert(std::make_unique<XDashEntry>(XDash(css::drawing::DashStyle_RECT,2, 50,3,250,120),aStr + " 3"));
+
+ return true;
+}
+
+BitmapEx XDashList::ImpCreateBitmapForXDash(const XDash* pDash)
+{
+ const StyleSettings& rStyleSettings = Application::GetSettings().GetStyleSettings();
+ const Size& rSize = rStyleSettings.GetListBoxPreviewDefaultPixelSize();
+ const sal_uInt32 nFactor(2);
+ const Size aSize((rSize.Width() * 5 * 2) / 2, rSize.Height() * nFactor);
+
+ // prepare polygon geometry for line
+ basegfx::B2DPolygon aLine;
+
+ aLine.append(basegfx::B2DPoint(0.0, aSize.Height() / 2.0));
+ aLine.append(basegfx::B2DPoint(aSize.Width(), aSize.Height() / 2.0));
+
+ // prepare LineAttribute
+ const basegfx::BColor aLineColor(rStyleSettings.GetFieldTextColor().getBColor());
+ const double fLineWidth(StyleSettings::GetListBoxPreviewDefaultLineWidth() * (nFactor * 1.1));
+ const drawinglayer::attribute::LineAttribute aLineAttribute(
+ aLineColor,
+ fLineWidth);
+
+ // prepare StrokeAttribute
+ ::std::vector< double > aDotDashArray;
+ double fFullDotDashLen(0.0);
+
+ if(pDash && (pDash->GetDots() || pDash->GetDashes()))
+ {
+ const basegfx::B2DHomMatrix aScaleMatrix(OutputDevice::LogicToLogic(MapMode(MapUnit::Map100thMM), MapMode(MapUnit::MapPixel)));
+ const basegfx::B2DVector aScaleVector(aScaleMatrix * basegfx::B2DVector(1.0, 0.0));
+ const double fScaleValue(aScaleVector.getLength() * (nFactor * (1.4 / 2.0)));
+ const double fLineWidthInUnits(fLineWidth / fScaleValue);
+
+ fFullDotDashLen = pDash->CreateDotDashArray(aDotDashArray, fLineWidthInUnits);
+
+ if(!aDotDashArray.empty())
+ {
+ for(double & a : aDotDashArray)
+ {
+ a *= fScaleValue;
+ // ~zero length dash is a dot-like dot (with line width size round cap), so show it
+ if (a < 0.1)
+ a += 1.0;
+ }
+
+ fFullDotDashLen *= fScaleValue;
+ }
+ }
+
+ const drawinglayer::attribute::StrokeAttribute aStrokeAttribute(
+ std::move(aDotDashArray),
+ fFullDotDashLen);
+
+ // create LinePrimitive
+ const drawinglayer::primitive2d::Primitive2DReference aLinePrimitive(
+ new drawinglayer::primitive2d::PolygonStrokePrimitive2D(
+ aLine,
+ aLineAttribute,
+ aStrokeAttribute));
+
+ // prepare VirtualDevice
+ ScopedVclPtrInstance< VirtualDevice > pVirtualDevice;
+ const drawinglayer::geometry::ViewInformation2D aNewViewInformation2D;
+
+ pVirtualDevice->SetOutputSizePixel(aSize);
+ pVirtualDevice->SetDrawMode(rStyleSettings.GetHighContrastMode()
+ ? DrawModeFlags::SettingsLine | DrawModeFlags::SettingsFill | DrawModeFlags::SettingsText | DrawModeFlags::SettingsGradient
+ : DrawModeFlags::Default);
+
+ if(rStyleSettings.GetPreviewUsesCheckeredBackground())
+ {
+ const Point aNull(0, 0);
+ static const sal_uInt32 nLen(8 * nFactor);
+ static const Color aW(COL_WHITE);
+ static const Color aG(0xef, 0xef, 0xef);
+
+ pVirtualDevice->DrawCheckered(aNull, aSize, nLen, aW, aG);
+ }
+ else
+ {
+ pVirtualDevice->SetBackground(rStyleSettings.GetFieldColor());
+ pVirtualDevice->Erase();
+ }
+
+ // create processor and draw primitives
+ std::unique_ptr<drawinglayer::processor2d::BaseProcessor2D> pProcessor2D(drawinglayer::processor2d::createPixelProcessor2DFromOutputDevice(
+ *pVirtualDevice,
+ aNewViewInformation2D));
+
+ const drawinglayer::primitive2d::Primitive2DContainer aSequence { aLinePrimitive };
+
+ pProcessor2D->process(aSequence);
+ pProcessor2D.reset();
+
+ // get result bitmap and scale
+ BitmapEx aRetval(pVirtualDevice->GetBitmapEx(Point(0, 0), pVirtualDevice->GetOutputSizePixel()));
+
+ if(1 != nFactor)
+ {
+ aRetval.Scale(Size((rSize.Width() * 5) / 2, rSize.Height()));
+ }
+
+ return aRetval;
+}
+
+BitmapEx XDashList::CreateBitmapForUI( tools::Long nIndex )
+{
+ const XDash& rDash = GetDash(nIndex)->GetDash();
+
+ return ImpCreateBitmapForXDash(&rDash);
+}
+
+BitmapEx const & XDashList::GetBitmapForUISolidLine() const
+{
+ if(maBitmapSolidLine.IsEmpty())
+ {
+ const_cast< XDashList* >(this)->maBitmapSolidLine = XDashList::ImpCreateBitmapForXDash(nullptr);
+ }
+
+ return maBitmapSolidLine;
+}
+
+OUString const & XDashList::GetStringForUiSolidLine() const
+{
+ if(maStringSolidLine.isEmpty())
+ {
+ const_cast< XDashList* >(this)->maStringSolidLine = SvxResId(RID_SVXSTR_SOLID);
+ }
+
+ return maStringSolidLine;
+}
+
+OUString const & XDashList::GetStringForUiNoLine() const
+{
+ if(maStringNoLine.isEmpty())
+ {
+ // formerly was RID_SVXSTR_INVISIBLE, but to make equal
+ // everywhere, use RID_SVXSTR_NONE
+ const_cast< XDashList* >(this)->maStringNoLine = comphelper::LibreOfficeKit::isActive() ? SvxResId(RID_SVXSTR_INVISIBLE) :
+ SvxResId(RID_SVXSTR_NONE);
+ }
+
+ return maStringNoLine;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/xoutdev/xtabgrdt.cxx b/svx/source/xoutdev/xtabgrdt.cxx
new file mode 100644
index 000000000..3d2acb574
--- /dev/null
+++ b/svx/source/xoutdev/xtabgrdt.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 <XPropertyTable.hxx>
+
+#include <vcl/virdev.hxx>
+#include <svx/strings.hrc>
+#include <svx/dialmgr.hxx>
+#include <svx/xtable.hxx>
+
+#include <rtl/ustrbuf.hxx>
+#include <vcl/svapp.hxx>
+#include <vcl/settings.hxx>
+#include <osl/diagnose.h>
+
+#include <drawinglayer/attribute/fillgradientattribute.hxx>
+#include <drawinglayer/primitive2d/PolygonHairlinePrimitive2D.hxx>
+#include <drawinglayer/primitive2d/PolyPolygonGradientPrimitive2D.hxx>
+#include <drawinglayer/processor2d/baseprocessor2d.hxx>
+#include <drawinglayer/processor2d/processor2dtools.hxx>
+#include <basegfx/polygon/b2dpolygontools.hxx>
+#include <memory>
+
+using namespace com::sun::star;
+
+XGradientList::XGradientList( const OUString& rPath, const OUString& rReferer )
+: XPropertyList( XPropertyListType::Gradient, rPath, rReferer )
+{
+}
+
+XGradientList::~XGradientList()
+{
+}
+
+void XGradientList::Replace(std::unique_ptr<XGradientEntry> pEntry, tools::Long nIndex)
+{
+ XPropertyList::Replace(std::move(pEntry), nIndex);
+}
+
+XGradientEntry* XGradientList::GetGradient(tools::Long nIndex) const
+{
+ return static_cast<XGradientEntry*>( XPropertyList::Get( nIndex ) );
+}
+
+uno::Reference< container::XNameContainer > XGradientList::createInstance()
+{
+ return SvxUnoXGradientTable_createInstance( *this );
+}
+
+bool XGradientList::Create()
+{
+ OUStringBuffer aStr(SvxResId(RID_SVXSTR_GRADIENT));
+ aStr.append(" 1");
+ sal_Int32 nLen = aStr.getLength() - 1;
+ Insert(std::make_unique<XGradientEntry>(XGradient(COL_BLACK, COL_WHITE, css::awt::GradientStyle_LINEAR , 0_deg10,10,10, 0,100,100),aStr.toString()));
+ aStr[nLen] = '2';
+ Insert(std::make_unique<XGradientEntry>(XGradient(COL_BLUE, COL_RED, css::awt::GradientStyle_AXIAL , 300_deg10,20,20,10,100,100),aStr.toString()));
+ aStr[nLen] = '3';
+ Insert(std::make_unique<XGradientEntry>(XGradient(COL_RED, COL_YELLOW,css::awt::GradientStyle_RADIAL , 600_deg10,30,30,20,100,100),aStr.toString()));
+ aStr[nLen] = '4';
+ Insert(std::make_unique<XGradientEntry>(XGradient(COL_YELLOW , COL_GREEN, css::awt::GradientStyle_ELLIPTICAL, 900_deg10,40,40,30,100,100),aStr.toString()));
+ aStr[nLen] = '5';
+ Insert(std::make_unique<XGradientEntry>(XGradient(COL_GREEN , COL_MAGENTA,css::awt::GradientStyle_SQUARE , 1200_deg10,50,50,40,100,100),aStr.toString()));
+ aStr[nLen] = '6';
+ Insert(std::make_unique<XGradientEntry>(XGradient(COL_MAGENTA, COL_YELLOW ,css::awt::GradientStyle_RECT , 1900_deg10,60,60,50,100,100),aStr.toString()));
+
+ return true;
+}
+
+BitmapEx XGradientList::CreateBitmap( tools::Long nIndex, const Size& rSize ) const
+{
+ BitmapEx aRetval;
+
+ OSL_ENSURE(nIndex < Count(), "OOps, access out of range (!)");
+
+ if(nIndex < Count())
+ {
+ const StyleSettings& rStyleSettings = Application::GetSettings().GetStyleSettings();
+ // prepare polygon geometry for rectangle
+ const basegfx::B2DPolygon aRectangle(
+ basegfx::utils::createPolygonFromRect(
+ basegfx::B2DRange(0.0, 0.0, rSize.Width(), rSize.Height())));
+
+ const XGradient& rGradient = GetGradient(nIndex)->GetGradient();
+ const sal_uInt16 nStartIntens(rGradient.GetStartIntens());
+ basegfx::BColor aStart(rGradient.GetStartColor().getBColor());
+
+ if(nStartIntens != 100)
+ {
+ const basegfx::BColor aBlack;
+ aStart = interpolate(aBlack, aStart, static_cast<double>(nStartIntens) * 0.01);
+ }
+
+ const sal_uInt16 nEndIntens(rGradient.GetEndIntens());
+ basegfx::BColor aEnd(rGradient.GetEndColor().getBColor());
+
+ if(nEndIntens != 100)
+ {
+ const basegfx::BColor aBlack;
+ aEnd = interpolate(aBlack, aEnd, static_cast<double>(nEndIntens) * 0.01);
+ }
+
+ drawinglayer::attribute::GradientStyle aGradientStyle(drawinglayer::attribute::GradientStyle::Rect);
+
+ switch(rGradient.GetGradientStyle())
+ {
+ case css::awt::GradientStyle_LINEAR :
+ {
+ aGradientStyle = drawinglayer::attribute::GradientStyle::Linear;
+ break;
+ }
+ case css::awt::GradientStyle_AXIAL :
+ {
+ aGradientStyle = drawinglayer::attribute::GradientStyle::Axial;
+ break;
+ }
+ case css::awt::GradientStyle_RADIAL :
+ {
+ aGradientStyle = drawinglayer::attribute::GradientStyle::Radial;
+ break;
+ }
+ case css::awt::GradientStyle_ELLIPTICAL :
+ {
+ aGradientStyle = drawinglayer::attribute::GradientStyle::Elliptical;
+ break;
+ }
+ case css::awt::GradientStyle_SQUARE :
+ {
+ aGradientStyle = drawinglayer::attribute::GradientStyle::Square;
+ break;
+ }
+ default :
+ {
+ aGradientStyle = drawinglayer::attribute::GradientStyle::Rect; // css::awt::GradientStyle_RECT
+ break;
+ }
+ }
+
+ const drawinglayer::attribute::FillGradientAttribute aFillGradient(
+ aGradientStyle,
+ static_cast<double>(rGradient.GetBorder()) * 0.01,
+ static_cast<double>(rGradient.GetXOffset()) * 0.01,
+ static_cast<double>(rGradient.GetYOffset()) * 0.01,
+ toRadians(rGradient.GetAngle()),
+ aStart,
+ aEnd);
+
+ const drawinglayer::primitive2d::Primitive2DReference aGradientPrimitive(
+ new drawinglayer::primitive2d::PolyPolygonGradientPrimitive2D(
+ basegfx::B2DPolyPolygon(aRectangle),
+ aFillGradient));
+
+ const basegfx::BColor aBlack(0.0, 0.0, 0.0);
+ const drawinglayer::primitive2d::Primitive2DReference aBlackRectanglePrimitive(
+ new drawinglayer::primitive2d::PolygonHairlinePrimitive2D(
+ aRectangle,
+ aBlack));
+
+ // prepare VirtualDevice
+ ScopedVclPtrInstance< VirtualDevice > pVirtualDevice;
+ const drawinglayer::geometry::ViewInformation2D aNewViewInformation2D;
+
+ pVirtualDevice->SetOutputSizePixel(rSize);
+ pVirtualDevice->SetDrawMode(rStyleSettings.GetHighContrastMode()
+ ? DrawModeFlags::SettingsLine | DrawModeFlags::SettingsFill | DrawModeFlags::SettingsText | DrawModeFlags::SettingsGradient
+ : DrawModeFlags::Default);
+
+ // create processor and draw primitives
+ std::unique_ptr<drawinglayer::processor2d::BaseProcessor2D> pProcessor2D(drawinglayer::processor2d::createPixelProcessor2DFromOutputDevice(
+ *pVirtualDevice,
+ aNewViewInformation2D));
+
+ drawinglayer::primitive2d::Primitive2DContainer aSequence(2);
+
+ aSequence[0] = aGradientPrimitive;
+ aSequence[1] = aBlackRectanglePrimitive;
+
+ pProcessor2D->process(aSequence);
+ pProcessor2D.reset();
+
+ // get result bitmap and scale
+ aRetval = pVirtualDevice->GetBitmapEx(Point(0, 0), pVirtualDevice->GetOutputSizePixel());
+ }
+
+ return aRetval;
+}
+
+BitmapEx XGradientList::CreateBitmapForUI(tools::Long nIndex)
+{
+ const StyleSettings& rStyleSettings = Application::GetSettings().GetStyleSettings();
+ const Size& rSize = rStyleSettings.GetListBoxPreviewDefaultPixelSize();
+ return CreateBitmap(nIndex, rSize);
+}
+
+BitmapEx XGradientList::GetBitmapForPreview(tools::Long nIndex, const Size& rSize)
+{
+ return CreateBitmap(nIndex, rSize);
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/xoutdev/xtabhtch.cxx b/svx/source/xoutdev/xtabhtch.cxx
new file mode 100644
index 000000000..4fc97727b
--- /dev/null
+++ b/svx/source/xoutdev/xtabhtch.cxx
@@ -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 .
+ */
+
+#include <XPropertyTable.hxx>
+#include <vcl/svapp.hxx>
+
+#include <vcl/virdev.hxx>
+#include <vcl/settings.hxx>
+#include <svx/strings.hrc>
+#include <svx/dialmgr.hxx>
+
+#include <drawinglayer/attribute/fillhatchattribute.hxx>
+#include <drawinglayer/primitive2d/PolyPolygonHatchPrimitive2D.hxx>
+#include <drawinglayer/primitive2d/PolygonHairlinePrimitive2D.hxx>
+#include <drawinglayer/processor2d/baseprocessor2d.hxx>
+#include <drawinglayer/processor2d/processor2dtools.hxx>
+#include <basegfx/matrix/b2dhommatrix.hxx>
+#include <basegfx/polygon/b2dpolygontools.hxx>
+#include <osl/diagnose.h>
+#include <rtl/ustrbuf.hxx>
+#include <memory>
+
+using namespace ::com::sun::star;
+
+XHatchList::XHatchList(const OUString& rPath, const OUString& rReferer)
+ : XPropertyList( XPropertyListType::Hatch, rPath, rReferer )
+{
+}
+
+XHatchList::~XHatchList()
+{
+}
+
+void XHatchList::Replace(std::unique_ptr<XHatchEntry> pEntry, tools::Long nIndex)
+{
+ XPropertyList::Replace(std::move(pEntry), nIndex);
+}
+
+XHatchEntry* XHatchList::GetHatch(tools::Long nIndex) const
+{
+ return static_cast<XHatchEntry*>( XPropertyList::Get(nIndex) );
+}
+
+uno::Reference< container::XNameContainer > XHatchList::createInstance()
+{
+ return SvxUnoXHatchTable_createInstance( *this );
+}
+
+bool XHatchList::Create()
+{
+ OUStringBuffer aStr(SvxResId(RID_SVXSTR_HATCH));
+ aStr.append(" 1");
+
+ sal_Int32 nLen = aStr.getLength() - 1;
+ Insert(std::make_unique<XHatchEntry>(XHatch(COL_BLACK,css::drawing::HatchStyle_SINGLE,100, 0_deg10),aStr.toString()));
+ aStr[nLen] = '2';
+ Insert(std::make_unique<XHatchEntry>(XHatch(COL_RED ,css::drawing::HatchStyle_DOUBLE, 80,450_deg10),aStr.toString()));
+ aStr[nLen] = '3';
+ Insert(std::make_unique<XHatchEntry>(XHatch(COL_BLUE ,css::drawing::HatchStyle_TRIPLE,120, 0_deg10),aStr.toString()));
+
+ return true;
+}
+
+BitmapEx XHatchList::CreateBitmap( tools::Long nIndex, const Size& rSize) const
+{
+ BitmapEx aRetval;
+ OSL_ENSURE(nIndex < Count(), "OOps, access out of range (!)");
+
+ if(nIndex < Count())
+ {
+ const StyleSettings& rStyleSettings = Application::GetSettings().GetStyleSettings();
+
+ // prepare polygon geometry for rectangle
+ const basegfx::B2DPolygon aRectangle(
+ basegfx::utils::createPolygonFromRect(
+ basegfx::B2DRange(0.0, 0.0, rSize.Width(), rSize.Height())));
+
+ const XHatch& rHatch = GetHatch(nIndex)->GetHatch();
+ drawinglayer::attribute::HatchStyle aHatchStyle(drawinglayer::attribute::HatchStyle::Triple);
+
+ switch(rHatch.GetHatchStyle())
+ {
+ case css::drawing::HatchStyle_SINGLE :
+ {
+ aHatchStyle = drawinglayer::attribute::HatchStyle::Single;
+ break;
+ }
+ case css::drawing::HatchStyle_DOUBLE :
+ {
+ aHatchStyle = drawinglayer::attribute::HatchStyle::Double;
+ break;
+ }
+ default :
+ {
+ aHatchStyle = drawinglayer::attribute::HatchStyle::Triple; // css::drawing::HatchStyle_TRIPLE
+ break;
+ }
+ }
+
+ const basegfx::B2DHomMatrix aScaleMatrix(OutputDevice::LogicToLogic(MapMode(MapUnit::Map100thMM), MapMode(MapUnit::MapPixel)));
+ const basegfx::B2DVector aScaleVector(aScaleMatrix * basegfx::B2DVector(1.0, 0.0));
+ const double fScaleValue(aScaleVector.getLength());
+
+ const drawinglayer::attribute::FillHatchAttribute aFillHatch(
+ aHatchStyle,
+ static_cast<double>(rHatch.GetDistance()) * fScaleValue,
+ toRadians(rHatch.GetAngle()),
+ rHatch.GetColor().getBColor(),
+ 3, // same default as VCL, a minimum of three discrete units (pixels) offset
+ false);
+
+ const basegfx::BColor aBlack(0.0, 0.0, 0.0);
+ const drawinglayer::primitive2d::Primitive2DReference aHatchPrimitive(
+ new drawinglayer::primitive2d::PolyPolygonHatchPrimitive2D(
+ basegfx::B2DPolyPolygon(aRectangle),
+ aBlack,
+ aFillHatch));
+
+ const drawinglayer::primitive2d::Primitive2DReference aBlackRectanglePrimitive(
+ new drawinglayer::primitive2d::PolygonHairlinePrimitive2D(
+ aRectangle,
+ aBlack));
+
+ // prepare VirtualDevice
+ ScopedVclPtrInstance< VirtualDevice > pVirtualDevice;
+ const drawinglayer::geometry::ViewInformation2D aNewViewInformation2D;
+
+ pVirtualDevice->SetOutputSizePixel(rSize);
+ pVirtualDevice->SetDrawMode(rStyleSettings.GetHighContrastMode()
+ ? DrawModeFlags::SettingsLine | DrawModeFlags::SettingsFill | DrawModeFlags::SettingsText | DrawModeFlags::SettingsGradient
+ : DrawModeFlags::Default);
+
+ if(rStyleSettings.GetPreviewUsesCheckeredBackground())
+ {
+ const Point aNull(0, 0);
+ static const sal_uInt32 nLen(8);
+ static const Color aW(COL_WHITE);
+ static const Color aG(0xef, 0xef, 0xef);
+ pVirtualDevice->DrawCheckered(aNull, rSize, nLen, aW, aG);
+ }
+ else
+ {
+ pVirtualDevice->SetBackground(rStyleSettings.GetFieldColor());
+ pVirtualDevice->Erase();
+ }
+
+ // create processor and draw primitives
+ std::unique_ptr<drawinglayer::processor2d::BaseProcessor2D> pProcessor2D(drawinglayer::processor2d::createPixelProcessor2DFromOutputDevice(
+ *pVirtualDevice,
+ aNewViewInformation2D));
+
+ drawinglayer::primitive2d::Primitive2DContainer aSequence(2);
+
+ aSequence[0] = aHatchPrimitive;
+ aSequence[1] = aBlackRectanglePrimitive;
+ pProcessor2D->process(aSequence);
+ pProcessor2D.reset();
+
+ // get result bitmap and scale
+ aRetval = pVirtualDevice->GetBitmapEx(Point(0, 0), pVirtualDevice->GetOutputSizePixel());
+ }
+
+ return aRetval;
+}
+
+BitmapEx XHatchList::CreateBitmapForUI(tools::Long nIndex)
+{
+ const StyleSettings& rStyleSettings = Application::GetSettings().GetStyleSettings();
+ const Size& rSize = rStyleSettings.GetListBoxPreviewDefaultPixelSize();
+ BitmapEx aRetVal = CreateBitmap(nIndex, rSize);
+ return aRetVal;
+}
+
+BitmapEx XHatchList::GetBitmapForPreview(tools::Long nIndex, const Size& rSize)
+{
+ return CreateBitmap(nIndex, rSize);
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/xoutdev/xtable.cxx b/svx/source/xoutdev/xtable.cxx
new file mode 100644
index 000000000..016dd261b
--- /dev/null
+++ b/svx/source/xoutdev/xtable.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 <memory>
+#include <xmlxtexp.hxx>
+#include <xmlxtimp.hxx>
+#include <o3tl/safeint.hxx>
+#include <osl/diagnose.h>
+#include <tools/urlobj.hxx>
+#include <svx/xtable.hxx>
+#include <tools/debug.hxx>
+#include <stack>
+
+using namespace com::sun::star;
+
+XColorEntry::XColorEntry(const Color& rColor, const OUString& rName)
+: XPropertyEntry(rName),
+ aColor(rColor)
+{
+}
+
+XLineEndEntry::XLineEndEntry(const basegfx::B2DPolyPolygon& rB2DPolyPolygon, const OUString& rName)
+: XPropertyEntry(rName),
+ aB2DPolyPolygon(rB2DPolyPolygon)
+{
+}
+
+XLineEndEntry::XLineEndEntry(const XLineEndEntry& rOther)
+: XPropertyEntry(rOther),
+ aB2DPolyPolygon(rOther.aB2DPolyPolygon)
+{
+}
+
+XDashEntry::XDashEntry(const XDash& rDash, const OUString& rName)
+: XPropertyEntry(rName),
+ aDash(rDash)
+{
+}
+
+XDashEntry::XDashEntry(const XDashEntry& rOther)
+: XPropertyEntry(rOther),
+aDash(rOther.aDash)
+{
+}
+
+XHatchEntry::XHatchEntry(const XHatch& rHatch, const OUString& rName)
+: XPropertyEntry(rName),
+ aHatch(rHatch)
+{
+}
+
+XHatchEntry::XHatchEntry(const XHatchEntry& rOther)
+: XPropertyEntry(rOther),
+ aHatch(rOther.aHatch)
+{
+}
+
+XGradientEntry::XGradientEntry(const XGradient& rGradient, const OUString& rName)
+: XPropertyEntry(rName),
+ aGradient(rGradient)
+{
+}
+
+XGradientEntry::XGradientEntry(const XGradientEntry& rOther)
+: XPropertyEntry(rOther),
+ aGradient(rOther.aGradient)
+{
+}
+
+XBitmapEntry::XBitmapEntry(const GraphicObject& rGraphicObject, const OUString& rName)
+: XPropertyEntry(rName),
+ maGraphicObject(rGraphicObject)
+{
+}
+
+XBitmapEntry::XBitmapEntry(const XBitmapEntry& rOther)
+: XPropertyEntry(rOther),
+ maGraphicObject(rOther.maGraphicObject)
+{
+}
+
+XPropertyList::XPropertyList(
+ XPropertyListType type,
+ const OUString& rPath, const OUString& rReferer
+) : meType ( type ),
+ maName ( "standard" ),
+ maPath ( rPath ),
+ maReferer ( rReferer ),
+ mbListDirty ( true ),
+ mbEmbedInDocument( false )
+{
+// fprintf (stderr, "Create type %d count %d\n", (int)meType, count++);
+}
+
+bool XPropertyList::isValidIdx(tools::Long nIndex) const
+{
+ return (nIndex >= 0 && o3tl::make_unsigned(nIndex) < maList.size());
+}
+
+
+XPropertyList::~XPropertyList()
+{
+}
+
+tools::Long XPropertyList::Count() const
+{
+ if( mbListDirty )
+ {
+ if( !const_cast<XPropertyList*>(this)->Load() )
+ const_cast<XPropertyList*>(this)->Create();
+ }
+ return maList.size();
+}
+
+XPropertyEntry* XPropertyList::Get( tools::Long nIndex ) const
+{
+ if( mbListDirty )
+ {
+ if( !const_cast<XPropertyList*>(this)->Load() )
+ const_cast<XPropertyList*>(this)->Create();
+ }
+ if (!isValidIdx(nIndex))
+ return nullptr;
+
+ return maList[nIndex].get();
+}
+
+tools::Long XPropertyList::GetIndex(std::u16string_view rName) const
+{
+ if( mbListDirty )
+ {
+ if( !const_cast<XPropertyList*>(this)->Load() )
+ const_cast<XPropertyList*>(this)->Create();
+ }
+
+ for( tools::Long i = 0, n = maList.size(); i < n; ++i ) {
+ if (rName == maList[ i ]->GetName()) {
+ return i;
+ }
+ }
+ return -1;
+}
+
+BitmapEx XPropertyList::GetUiBitmap( tools::Long nIndex ) const
+{
+ BitmapEx aRetval;
+ if (!isValidIdx(nIndex))
+ return aRetval;
+
+ XPropertyEntry* pEntry = maList[nIndex].get();
+ aRetval = pEntry->GetUiBitmap();
+
+ if(aRetval.IsEmpty())
+ {
+ aRetval = const_cast< XPropertyList* >(this)->CreateBitmapForUI(nIndex);
+ pEntry->SetUiBitmap(aRetval);
+ }
+ return aRetval;
+}
+
+void XPropertyList::Insert(std::unique_ptr<XPropertyEntry> pEntry, tools::Long nIndex)
+{
+ if (!pEntry)
+ {
+ assert(!"empty XPropertyEntry not allowed in XPropertyList");
+ return;
+ }
+
+ if (isValidIdx(nIndex)) {
+ maList.insert( maList.begin()+nIndex, std::move(pEntry) );
+ } else {
+ maList.push_back( std::move(pEntry) );
+ }
+}
+
+void XPropertyList::Replace(std::unique_ptr<XPropertyEntry> pEntry, tools::Long nIndex)
+{
+ if (!pEntry)
+ {
+ assert(!"empty XPropertyEntry not allowed in XPropertyList");
+ return;
+ }
+ if (!isValidIdx(nIndex))
+ {
+ assert(!"trying to replace invalid entry in XPropertyList");
+ return;
+ }
+
+ maList[nIndex] = std::move(pEntry);
+}
+
+void XPropertyList::Remove(tools::Long nIndex)
+{
+ if (!isValidIdx(nIndex))
+ {
+ assert(!"trying to remove invalid entry in XPropertyList");
+ return;
+ }
+
+ maList.erase(maList.begin() + nIndex);
+}
+
+void XPropertyList::SetName( const OUString& rString )
+{
+ if(!rString.isEmpty())
+ {
+ maName = rString;
+ }
+}
+
+bool XPropertyList::Load()
+{
+ if( mbListDirty )
+ {
+ mbListDirty = false;
+ std::stack<OUString> aDirs;
+
+ sal_Int32 nIndex = 0;
+ do
+ {
+ aDirs.push(maPath.getToken(0, ';', nIndex));
+ }
+ while (nIndex >= 0);
+
+ //try all entries palette path list working back to front until one
+ //succeeds
+ while (!aDirs.empty())
+ {
+ OUString aPath(aDirs.top());
+ aDirs.pop();
+
+ INetURLObject aURL(aPath);
+
+ if( INetProtocol::NotValid == aURL.GetProtocol() )
+ {
+ DBG_ASSERT( aPath.isEmpty(), "invalid URL" );
+ return false;
+ }
+
+ aURL.Append( maName );
+
+ if( aURL.getExtension().isEmpty() )
+ aURL.setExtension( GetDefaultExt() );
+
+ bool bRet = SvxXMLXTableImport::load(aURL.GetMainURL(INetURLObject::DecodeMechanism::NONE),
+ maReferer, uno::Reference < embed::XStorage >(),
+ createInstance(), nullptr );
+ if (bRet)
+ return bRet;
+ }
+ }
+ return false;
+}
+
+bool XPropertyList::LoadFrom( const uno::Reference < embed::XStorage > &xStorage,
+ const OUString &rURL, const OUString &rReferer )
+{
+ if( !mbListDirty )
+ return false;
+ mbListDirty = false;
+ return SvxXMLXTableImport::load( rURL, rReferer, xStorage, createInstance(), &mbEmbedInDocument );
+}
+
+bool XPropertyList::Save()
+{
+ //save to the last path in the palette path list
+ OUString aLastDir;
+ sal_Int32 nIndex = 0;
+ do
+ {
+ aLastDir = maPath.getToken(0, ';', nIndex);
+ }
+ while (nIndex >= 0);
+
+ INetURLObject aURL(aLastDir);
+
+ if( INetProtocol::NotValid == aURL.GetProtocol() )
+ {
+ DBG_ASSERT( aLastDir.isEmpty(), "invalid URL" );
+ return false;
+ }
+
+ aURL.Append( maName );
+
+ if( aURL.getExtension().isEmpty() )
+ aURL.setExtension( GetDefaultExt() );
+
+ return SvxXMLXTableExportComponent::save( aURL.GetMainURL( INetURLObject::DecodeMechanism::NONE ),
+ createInstance(),
+ uno::Reference< embed::XStorage >(), nullptr );
+}
+
+bool XPropertyList::SaveTo( const uno::Reference< embed::XStorage > &xStorage,
+ const OUString &rURL, OUString *pOptName )
+{
+ return SvxXMLXTableExportComponent::save( rURL, createInstance(), xStorage, pOptName );
+}
+
+XPropertyListRef XPropertyList::CreatePropertyList( XPropertyListType aType,
+ const OUString& rPath,
+ const OUString& rReferer )
+{
+ XPropertyListRef pRet;
+
+ switch (aType) {
+ case XPropertyListType::Color:
+ pRet = XPropertyListRef(new XColorList(rPath, rReferer));
+ break;
+ case XPropertyListType::LineEnd:
+ pRet = XPropertyListRef(new XLineEndList(rPath, rReferer));
+ break;
+ case XPropertyListType::Dash:
+ pRet = XPropertyListRef(new XDashList(rPath, rReferer));
+ break;
+ case XPropertyListType::Hatch:
+ pRet = XPropertyListRef(new XHatchList(rPath, rReferer));
+ break;
+ case XPropertyListType::Gradient:
+ pRet = XPropertyListRef(new XGradientList(rPath, rReferer));
+ break;
+ case XPropertyListType::Bitmap:
+ pRet = XPropertyListRef(new XBitmapList(rPath, rReferer));
+ break;
+ case XPropertyListType::Pattern:
+ pRet = XPropertyListRef(new XPatternList(rPath, rReferer));
+ break;
+ default:
+ OSL_FAIL("unknown xproperty type");
+ break;
+ }
+ OSL_ASSERT( !pRet.is() || pRet->meType == aType );
+
+ return pRet;
+}
+
+XPropertyListRef
+XPropertyList::CreatePropertyListFromURL( XPropertyListType t,
+ std::u16string_view rURLStr )
+{
+ INetURLObject aURL( rURLStr );
+ INetURLObject aPathURL( aURL );
+
+ aPathURL.removeSegment();
+ aPathURL.removeFinalSlash();
+
+ XPropertyListRef pList = XPropertyList::CreatePropertyList(
+ t, aPathURL.GetMainURL( INetURLObject::DecodeMechanism::NONE ), "" );
+ pList->SetName( aURL.getName() );
+
+ return pList;
+}
+
+struct {
+ XPropertyListType t;
+ const char *pExt;
+} const pExtnMap[] = {
+ { XPropertyListType::Color, "soc" },
+ { XPropertyListType::LineEnd, "soe" },
+ { XPropertyListType::Dash, "sod" },
+ { XPropertyListType::Hatch, "soh" },
+ { XPropertyListType::Gradient, "sog" },
+ { XPropertyListType::Bitmap, "sob" },
+ { XPropertyListType::Pattern, "sop"}
+};
+
+OUString XPropertyList::GetDefaultExt( XPropertyListType t )
+{
+ for (const auto & i : pExtnMap)
+ {
+ if( i.t == t )
+ return OUString::createFromAscii( i.pExt );
+ }
+ return OUString();
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/xoutdev/xtablend.cxx b/svx/source/xoutdev/xtablend.cxx
new file mode 100644
index 000000000..fcca05960
--- /dev/null
+++ b/svx/source/xoutdev/xtablend.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 <XPropertyTable.hxx>
+#include <vcl/virdev.hxx>
+
+#include <vcl/svapp.hxx>
+#include <vcl/settings.hxx>
+
+#include <svx/strings.hrc>
+#include <svx/dialmgr.hxx>
+#include <osl/diagnose.h>
+
+#include <svx/xtable.hxx>
+#include <drawinglayer/attribute/linestartendattribute.hxx>
+#include <drawinglayer/primitive2d/PolygonStrokeArrowPrimitive2D.hxx>
+#include <drawinglayer/processor2d/baseprocessor2d.hxx>
+#include <drawinglayer/processor2d/processor2dtools.hxx>
+#include <basegfx/polygon/b2dpolygontools.hxx>
+#include <memory>
+
+using namespace com::sun::star;
+
+XLineEndList::XLineEndList( const OUString& rPath, const OUString& rReferer )
+ : XPropertyList( XPropertyListType::LineEnd, rPath, rReferer )
+{
+}
+
+XLineEndList::~XLineEndList()
+{
+}
+
+XLineEndEntry* XLineEndList::GetLineEnd(tools::Long nIndex) const
+{
+ return static_cast<XLineEndEntry*>( XPropertyList::Get(nIndex) );
+}
+
+uno::Reference< container::XNameContainer > XLineEndList::createInstance()
+{
+ return SvxUnoXLineEndTable_createInstance( *this );
+}
+
+bool XLineEndList::Create()
+{
+ basegfx::B2DPolygon aTriangle;
+ aTriangle.append(basegfx::B2DPoint(10.0, 0.0));
+ aTriangle.append(basegfx::B2DPoint(0.0, 30.0));
+ aTriangle.append(basegfx::B2DPoint(20.0, 30.0));
+ aTriangle.setClosed(true);
+ Insert( std::make_unique<XLineEndEntry>( basegfx::B2DPolyPolygon(aTriangle), SvxResId( RID_SVXSTR_ARROW ) ) );
+
+ basegfx::B2DPolygon aSquare;
+ aSquare.append(basegfx::B2DPoint(0.0, 0.0));
+ aSquare.append(basegfx::B2DPoint(10.0, 0.0));
+ aSquare.append(basegfx::B2DPoint(10.0, 10.0));
+ aSquare.append(basegfx::B2DPoint(0.0, 10.0));
+ aSquare.setClosed(true);
+ Insert( std::make_unique<XLineEndEntry>( basegfx::B2DPolyPolygon(aSquare), SvxResId( RID_SVXSTR_SQUARE ) ) );
+
+ basegfx::B2DPolygon aCircle(basegfx::utils::createPolygonFromCircle(basegfx::B2DPoint(0.0, 0.0), 100.0));
+ Insert( std::make_unique<XLineEndEntry>( basegfx::B2DPolyPolygon(aCircle), SvxResId( RID_SVXSTR_CIRCLE ) ) );
+
+ return true;
+}
+
+BitmapEx XLineEndList::CreateBitmapForUI( tools::Long nIndex )
+{
+ BitmapEx aRetval;
+ OSL_ENSURE(nIndex < Count(), "OOps, access out of range (!)");
+
+ if(nIndex < Count())
+ {
+ const StyleSettings& rStyleSettings = Application::GetSettings().GetStyleSettings();
+ const Size& rSize = rStyleSettings.GetListBoxPreviewDefaultPixelSize();
+
+ const Size aSize(rSize.Width() * 2, rSize.Height());
+
+ // prepare line geometry
+ basegfx::B2DPolygon aLine;
+ const double fBorderDistance(aSize.Height() * 0.1);
+
+ aLine.append(basegfx::B2DPoint(fBorderDistance, aSize.Height() / 2));
+ aLine.append(basegfx::B2DPoint(aSize.Width() - fBorderDistance, aSize.Height() / 2));
+
+ // prepare LineAttribute
+ const basegfx::BColor aLineColor(rStyleSettings.GetFieldTextColor().getBColor());
+ const double fLineWidth(StyleSettings::GetListBoxPreviewDefaultLineWidth() * 1.1);
+ const drawinglayer::attribute::LineAttribute aLineAttribute(
+ aLineColor,
+ fLineWidth);
+
+ const basegfx::B2DPolyPolygon aLineEnd(GetLineEnd(nIndex)->GetLineEnd());
+ const double fArrowHeight(aSize.Height() - (2.0 * fBorderDistance));
+ const drawinglayer::attribute::LineStartEndAttribute aLineStartEndAttribute(
+ fArrowHeight,
+ aLineEnd,
+ false);
+
+ // prepare line primitive
+ const drawinglayer::primitive2d::Primitive2DReference aLineStartEndPrimitive(
+ new drawinglayer::primitive2d::PolygonStrokeArrowPrimitive2D(
+ aLine,
+ aLineAttribute,
+ aLineStartEndAttribute,
+ aLineStartEndAttribute));
+
+ // prepare VirtualDevice
+ ScopedVclPtrInstance< VirtualDevice > pVirtualDevice;
+ const drawinglayer::geometry::ViewInformation2D aNewViewInformation2D;
+
+ pVirtualDevice->SetOutputSizePixel(aSize);
+ pVirtualDevice->SetDrawMode(rStyleSettings.GetHighContrastMode()
+ ? DrawModeFlags::SettingsLine | DrawModeFlags::SettingsFill | DrawModeFlags::SettingsText | DrawModeFlags::SettingsGradient
+ : DrawModeFlags::Default);
+
+ if(rStyleSettings.GetPreviewUsesCheckeredBackground())
+ {
+ const Point aNull(0, 0);
+ static const sal_uInt32 nLen(8);
+ static const Color aW(COL_WHITE);
+ static const Color aG(0xef, 0xef, 0xef);
+ pVirtualDevice->DrawCheckered(aNull, aSize, nLen, aW, aG);
+ }
+ else
+ {
+ pVirtualDevice->SetBackground(rStyleSettings.GetFieldColor());
+ pVirtualDevice->Erase();
+ }
+
+ // create processor and draw primitives
+ std::unique_ptr<drawinglayer::processor2d::BaseProcessor2D> pProcessor2D(drawinglayer::processor2d::createPixelProcessor2DFromOutputDevice(
+ *pVirtualDevice,
+ aNewViewInformation2D));
+
+ const drawinglayer::primitive2d::Primitive2DContainer aSequence { aLineStartEndPrimitive };
+
+ pProcessor2D->process(aSequence);
+ pProcessor2D.reset();
+
+ // get result bitmap and scale
+ aRetval = pVirtualDevice->GetBitmapEx(Point(0, 0), pVirtualDevice->GetOutputSizePixel());
+ }
+
+ return aRetval;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/svx/source/xoutdev/xtabptrn.cxx b/svx/source/xoutdev/xtabptrn.cxx
new file mode 100644
index 000000000..29697880f
--- /dev/null
+++ b/svx/source/xoutdev/xtabptrn.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 <XPropertyTable.hxx>
+
+#include <vcl/virdev.hxx>
+#include <svx/strings.hrc>
+#include <svx/dialmgr.hxx>
+#include <svx/xtable.hxx>
+#include <rtl/ustrbuf.hxx>
+#include <vcl/settings.hxx>
+#include <vcl/svapp.hxx>
+#include <vcl/BitmapTools.hxx>
+
+using namespace com::sun::star;
+
+XBitmapEntry* XPatternList::GetBitmap(tools::Long nIndex) const
+{
+ return static_cast<XBitmapEntry*>( XPropertyList::Get(nIndex) );
+}
+
+uno::Reference< container::XNameContainer > XPatternList::createInstance()
+{
+ return SvxUnoXBitmapTable_createInstance( *this );
+}
+
+bool XPatternList::Create()
+{
+ OUStringBuffer aStr(SvxResId(RID_SVXSTR_PATTERN));
+ std::array<sal_uInt8,64> aArray;
+ BitmapEx aBitmap;
+ const sal_Int32 nLen(aStr.getLength() - 1);
+
+ aArray.fill(0);
+
+ // white/white bitmap
+ aStr.append(" 1");
+ aBitmap = vcl::bitmap::createHistorical8x8FromArray(aArray, COL_WHITE, COL_WHITE);
+ Insert(std::make_unique<XBitmapEntry>(Graphic(aBitmap), aStr.toString()));
+
+ // black/white bitmap
+ aArray[ 0] = 1; aArray[ 9] = 1; aArray[18] = 1; aArray[27] = 1;
+ aArray[36] = 1; aArray[45] = 1; aArray[54] = 1; aArray[63] = 1;
+ aStr[nLen] = '2';
+ aBitmap = vcl::bitmap::createHistorical8x8FromArray(aArray, COL_BLACK, COL_WHITE);
+ Insert(std::make_unique<XBitmapEntry>(Graphic(aBitmap), aStr.toString()));
+
+ // lightred/white bitmap
+ aArray[ 7] = 1; aArray[14] = 1; aArray[21] = 1; aArray[28] = 1;
+ aArray[35] = 1; aArray[42] = 1; aArray[49] = 1; aArray[56] = 1;
+ aStr[nLen] = '3';
+ aBitmap = vcl::bitmap::createHistorical8x8FromArray(aArray, COL_LIGHTRED, COL_WHITE);
+ Insert(std::make_unique<XBitmapEntry>(Graphic(aBitmap), aStr.toString()));
+
+ // lightblue/white bitmap
+ aArray[24] = 1; aArray[25] = 1; aArray[26] = 1;
+ aArray[29] = 1; aArray[30] = 1; aArray[31] = 1;
+ aStr[nLen] = '4';
+ aBitmap = vcl::bitmap::createHistorical8x8FromArray(aArray, COL_LIGHTBLUE, COL_WHITE);
+ Insert(std::make_unique<XBitmapEntry>(Graphic(aBitmap), aStr.toString()));
+
+ return true;
+}
+
+BitmapEx XPatternList::CreateBitmap( tools::Long nIndex, const Size& rSize ) const
+{
+ assert( nIndex < Count() );
+
+ if(nIndex < Count())
+ {
+ BitmapEx rBitmapEx = GetBitmap( nIndex )->GetGraphicObject().GetGraphic().GetBitmapEx();
+ ScopedVclPtrInstance< VirtualDevice > pVirtualDevice;
+ pVirtualDevice->SetOutputSizePixel(rSize);
+
+ if(rBitmapEx.IsAlpha())
+ {
+ const StyleSettings& rStyleSettings = Application::GetSettings().GetStyleSettings();
+
+ if(rStyleSettings.GetPreviewUsesCheckeredBackground())
+ {
+ const Point aNull(0, 0);
+ static const sal_uInt32 nLen(8);
+ static const Color aW(COL_WHITE);
+ static const Color aG(0xef, 0xef, 0xef);
+
+ pVirtualDevice->DrawCheckered(aNull, rSize, nLen, aW, aG);
+ }
+ else
+ {
+ pVirtualDevice->SetBackground(rStyleSettings.GetFieldColor());
+ pVirtualDevice->Erase();
+ }
+ }
+
+ if(rBitmapEx.GetSizePixel().Width() >= rSize.Width() && rBitmapEx.GetSizePixel().Height() >= rSize.Height())
+ {
+ rBitmapEx.Scale(rSize);
+ pVirtualDevice->DrawBitmapEx(Point(0, 0), rBitmapEx);
+ }
+ else
+ {
+ const Size aBitmapSize(rBitmapEx.GetSizePixel());
+
+ for(tools::Long y(0); y < rSize.Height(); y += aBitmapSize.Height())
+ {
+ for(tools::Long x(0); x < rSize.Width(); x += aBitmapSize.Width())
+ {
+ pVirtualDevice->DrawBitmapEx(
+ Point(x, y),
+ rBitmapEx);
+ }
+ }
+ }
+ rBitmapEx = pVirtualDevice->GetBitmapEx(Point(0, 0), rSize);
+ return rBitmapEx;
+ }
+ else
+ return BitmapEx();
+}
+
+BitmapEx XPatternList::CreateBitmapForUI( tools::Long nIndex )
+{
+ const StyleSettings& rStyleSettings = Application::GetSettings().GetStyleSettings();
+ const Size& rSize = rStyleSettings.GetListBoxPreviewDefaultPixelSize();
+ return CreateBitmap(nIndex, rSize);
+}
+
+BitmapEx XPatternList::GetBitmapForPreview( tools::Long nIndex, const Size& rSize )
+{
+ return CreateBitmap(nIndex, rSize);
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */